From 1896d7ce1ff6a124569a4725eadb9fd72ea0737e Mon Sep 17 00:00:00 2001 From: TEHEK Firefox Date: Fri, 27 Jan 2012 13:35:56 -0800 Subject: [PATCH 0001/1748] Fix for [GH-165] - createClient to properly assign parser_module --- index.js | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/index.js b/index.js index 5c7799a8f83..02f8affa7ca 100644 --- a/index.js +++ b/index.js @@ -224,7 +224,7 @@ RedisClient.prototype.init_parser = function () { if (this.options.parser) { if (! parsers.some(function (parser) { if (parser.name === self.options.parser) { - this.parser_module = parser; + self.parser_module = parser; if (exports.debug_mode) { console.log("Using parser module: " + self.parser_module.name); } From 29a416b7a55fc6a90d02aad6475c5215f4be8daf Mon Sep 17 00:00:00 2001 From: "Isaac Z. Schlueter" Date: Thu, 2 Feb 2012 17:42:55 -0800 Subject: [PATCH 0002/1748] List hiredis as an optional dependency. This will cause npm to attempt to install hiredis when installing redis, but if the hiredis installation fails, it won't cause the redis install to abort. The optionalDependencies feature was added pretty much explicitly for the redis->hiredis use case. :) --- package.json | 3 +++ 1 file changed, 3 insertions(+) diff --git a/package.json b/package.json index cf1248cd18e..ac9f0c522b7 100644 --- a/package.json +++ b/package.json @@ -22,6 +22,9 @@ "devDependencies": { "metrics": ">=0.1.5" }, + "optionalDependencies": { + "hiredis": "*" + } "repository": { "type": "git", "url": "git://github.com/mranney/node_redis.git" From 5afa763c89d01f37bd7894a2aaf1f8d411710ce5 Mon Sep 17 00:00:00 2001 From: David Trejo Date: Mon, 5 Mar 2012 15:31:43 -0500 Subject: [PATCH 0003/1748] readme code formatting --- README.md | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/README.md b/README.md index 10b90cfced8..40d60a81c9f 100644 --- a/README.md +++ b/README.md @@ -186,7 +186,7 @@ be loading the database from disk. While loading, the server not respond to any indicates whether the server is ready for more commands. When ready, `node_redis` emits a `ready` event. Setting `no_ready_check` to `true` will inhibit this check. - +```js var redis = require("redis"), client = redis.createClient(null, null, {detect_buffers: true}); @@ -202,6 +202,7 @@ Setting `no_ready_check` to `true` will inhibit this check. console.log(reply.toString()); // Will print `` }); client.end(); +``` `createClient()` returns a `RedisClient` object that is named `client` in all of the examples here. From 234ae6be9a80924552062ea2160e8ad2be62dea7 Mon Sep 17 00:00:00 2001 From: David Trejo Date: Mon, 5 Mar 2012 17:25:32 -0500 Subject: [PATCH 0004/1748] Emit Error objects not strings --- index.js | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/index.js b/index.js index 5c7799a8f83..eb27628b97f 100644 --- a/index.js +++ b/index.js @@ -160,11 +160,11 @@ RedisClient.prototype.do_auth = function () { }, 2000); // TODO - magic number alert return; } else { - return self.emit("error", "Auth error: " + err); + return self.emit("error", new Error("Auth error: " + err.message)); } } if (res.toString() !== "OK") { - return self.emit("error", "Auth failed: " + res.toString()); + return self.emit("error", new Error("Auth failed: " + res.toString())); } if (exports.debug_mode) { console.log("Auth succeeded " + self.host + ":" + self.port + " id " + self.connection_id); @@ -290,7 +290,7 @@ RedisClient.prototype.on_info_cmd = function (err, res) { var self = this, obj = {}, lines, retry_time; if (err) { - return self.emit("error", "Ready check failed: " + err); + return self.emit("error", new Error("Ready check failed: " + err.message)); } lines = res.toString().split("\r\n"); From cd5db44f665ff50d2fcd7346de2f561fffa80ef1 Mon Sep 17 00:00:00 2001 From: David Trejo Date: Mon, 5 Mar 2012 17:31:47 -0500 Subject: [PATCH 0005/1748] readme: how to correctly auth to server, what error looks like if done wrong --- README.md | 3 +++ 1 file changed, 3 insertions(+) diff --git a/README.md b/README.md index 40d60a81c9f..c0d23bd915c 100644 --- a/README.md +++ b/README.md @@ -213,6 +213,9 @@ first command after connecting. This can be tricky to coordinate with reconnect etc. To make this easier, `client.auth()` stashes `password` and will send it after each connection, including reconnections. `callback` is invoked only once, after the response to the very first `AUTH` command sent. +NOTE: Your call to `client.auth()` should not be inside the ready handler. If +you are doing this wrong, `client` will emit an error that looks +something like this `Error: Ready check failed: ERR operation not permitted`. ## client.end() From 5a5af9a4f9a05897d816c74dae45c64da8150f43 Mon Sep 17 00:00:00 2001 From: Brian Noguchi Date: Mon, 12 Mar 2012 22:17:13 -0700 Subject: [PATCH 0006/1748] Add comma to package.json --- package.json | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/package.json b/package.json index ac9f0c522b7..01ee9296717 100644 --- a/package.json +++ b/package.json @@ -24,7 +24,7 @@ }, "optionalDependencies": { "hiredis": "*" - } + }, "repository": { "type": "git", "url": "git://github.com/mranney/node_redis.git" From 2afa0e5acca0051749beb4b4eb19b1d70418b5ed Mon Sep 17 00:00:00 2001 From: Brian Noguchi Date: Mon, 12 Mar 2012 22:59:38 -0700 Subject: [PATCH 0007/1748] Add failing test. The test demonstrates failure for the following scenario. A single-subscription client calls unsubscribe immediately followed by a subscribe. It will fail when it tries to receive the next pmessage/message because the client will be in false pub_sub_mode. Here is why it is false: First, the 2nd subscribe sets pub_sub_mode to true during send_command. Next, the unsubscribe's return_reply sets pub_sub_mode to false. The 2nd subscribe's return_reply does not re-set pub_sub_mode back to true. So the result is a client with false pub_sub_mode that fails upon receipt of the next message or pmessage. --- tests/re_sub_test.js | 14 ++++++++++++++ 1 file changed, 14 insertions(+) create mode 100644 tests/re_sub_test.js diff --git a/tests/re_sub_test.js b/tests/re_sub_test.js new file mode 100644 index 00000000000..64b8f31287f --- /dev/null +++ b/tests/re_sub_test.js @@ -0,0 +1,14 @@ +var client = require('../index').createClient() + , client2 = require('../index').createClient() + , assert = require('assert'); + +client.once('subscribe', function (channel, count) { + client.unsubscribe('x'); + client.subscribe('x', function () { + client.quit(); + client2.quit(); + }); + client2.publish('x', 'hi'); +}); + +client.subscribe('x'); From 64a0e689278d0d2621445420d814ad13f39f925f Mon Sep 17 00:00:00 2001 From: Brian Noguchi Date: Mon, 12 Mar 2012 23:03:46 -0700 Subject: [PATCH 0008/1748] Add fix for last test (re-establish pub_sub_mode = true) --- index.js | 2 ++ 1 file changed, 2 insertions(+) diff --git a/index.js b/index.js index eb27628b97f..1ace00ed0c2 100644 --- a/index.js +++ b/index.js @@ -570,6 +570,8 @@ RedisClient.prototype.return_reply = function (reply) { if (this.debug_mode) { console.log("All subscriptions removed, exiting pub/sub mode"); } + } else { + this.pub_sub_mode = true; } // subscribe commands take an optional callback and also emit an event, but only the first response is included in the callback // TODO - document this or fix it so it works in a more obvious way From 4f172ce7139a2f59b8de1204c2d3812386193ba0 Mon Sep 17 00:00:00 2001 From: Philip Tellis Date: Mon, 23 Apr 2012 17:11:02 -0300 Subject: [PATCH 0009/1748] remove duplicate "take either" --- README.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/README.md b/README.md index c0d23bd915c..a00c52a41d9 100644 --- a/README.md +++ b/README.md @@ -84,7 +84,7 @@ The performance of `node_redis` improves dramatically with pipelining, which hap ### Sending Commands Each Redis command is exposed as a function on the `client` object. -All functions take either take either an `args` Array plus optional `callback` Function or +All functions take either an `args` Array plus optional `callback` Function or a variable number of individual arguments followed by an optional callback. Here is an example of passing an array of arguments and a callback: From b1ccbd71f9086b883dfea90f55374eccc7566aed Mon Sep 17 00:00:00 2001 From: Marcus Westin Date: Tue, 10 Apr 2012 09:22:50 -0700 Subject: [PATCH 0010/1748] Fix package.json format Signed-off-by: David Trejo --- package.json | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/package.json b/package.json index ac9f0c522b7..01ee9296717 100644 --- a/package.json +++ b/package.json @@ -24,7 +24,7 @@ }, "optionalDependencies": { "hiredis": "*" - } + }, "repository": { "type": "git", "url": "git://github.com/mranney/node_redis.git" From 69cf7dcd1b0cc3b82cb3c5a65ea7f45b8ecb6f9a Mon Sep 17 00:00:00 2001 From: Marcus Westin Date: Tue, 10 Apr 2012 09:22:42 -0700 Subject: [PATCH 0011/1748] git ignore node_modules Signed-off-by: David Trejo --- .gitignore | 1 + 1 file changed, 1 insertion(+) create mode 100644 .gitignore diff --git a/.gitignore b/.gitignore new file mode 100644 index 00000000000..3c3629e647f --- /dev/null +++ b/.gitignore @@ -0,0 +1 @@ +node_modules From e7e5a07594839fe96e01a4a4fc460d186c9ab44b Mon Sep 17 00:00:00 2001 From: David Trejo Date: Mon, 23 Apr 2012 19:20:29 -0400 Subject: [PATCH 0012/1748] readme: add a few missing contributors to the list --- README.md | 3 +++ 1 file changed, 3 insertions(+) diff --git a/README.md b/README.md index a00c52a41d9..5b0ef34c299 100644 --- a/README.md +++ b/README.md @@ -570,6 +570,9 @@ In order of first contribution, they are: * [Pieter Noordhuis](https://github.com/pietern) * [Vladimir Dronnikov](https://github.com/dvv) * [Dave Hoover](https://github.com/redsquirrel) +* [David Trejo](https://github.com/DTrejo) +* [Philip Tellis](https://github.com/bluesmoon) +* [Marcus Westin](https://github.com/marcuswestin) Thanks. From 72476be3e170d51bdaf3fe32063add8b239c3b50 Mon Sep 17 00:00:00 2001 From: David Trejo Date: Mon, 23 Apr 2012 19:58:44 -0400 Subject: [PATCH 0013/1748] package.json: add a few missing contributors --- package.json | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) diff --git a/package.json b/package.json index 01ee9296717..325d9f750a9 100644 --- a/package.json +++ b/package.json @@ -13,7 +13,10 @@ "Pieter Noordhuis", "Andy Ray", "Vladimir Dronnikov", - "Dave Hoover" + "Dave Hoover", + "David Trejo", + "Philip Tellis", + "Marcus Westin" ], "main": "./index.js", "scripts": { From 625d5134e2ebcc18e395d14d3c4c6b19c0b70cd8 Mon Sep 17 00:00:00 2001 From: David Trejo Date: Fri, 27 Apr 2012 13:29:27 -0400 Subject: [PATCH 0014/1748] readme: add more contributors --- README.md | 43 ++++++++++++++++++++++++++++--------------- 1 file changed, 28 insertions(+), 15 deletions(-) diff --git a/README.md b/README.md index 5b0ef34c299..6b748dc7b9d 100644 --- a/README.md +++ b/README.md @@ -558,21 +558,34 @@ I think there are more performance improvements left in there for smaller values Some people have have added features and fixed bugs in `node_redis` other than me. -In order of first contribution, they are: - -* [Tim Smart](https://github.com/Tim-Smart) -* [TJ Holowaychuk](https://github.com/visionmedia) -* [Rick Olson](https://github.com/technoweenie) -* [Orion Henry](https://github.com/orionz) -* [Hank Sims](https://github.com/hanksims) -* [Aivo Paas](https://github.com/aivopaas) -* [Paul Carey](https://github.com/paulcarey) -* [Pieter Noordhuis](https://github.com/pietern) -* [Vladimir Dronnikov](https://github.com/dvv) -* [Dave Hoover](https://github.com/redsquirrel) -* [David Trejo](https://github.com/DTrejo) -* [Philip Tellis](https://github.com/bluesmoon) -* [Marcus Westin](https://github.com/marcuswestin) +In alphabetical order, they are: + +* [Aivo Paas](https://github.com/aivopaas) +* [Andy Ray](https://github.com/DelvarWorld) +* Daniele +* [Dave Hoover](https://github.com/redsquirrel) +* [David Trejo](https://github.com/DTrejo) +* Dayananda Nanjundappa +* [Felix Geisendörfer](https://github.com/felixge) +* [Hank Sims](https://github.com/hanksims) +* [Ian Babrou](https://github.com/bobrik) +* [Isaac Z. Schlueter](https://github.com/isaacs) +* [Louis-Philippe Perron](https://github.com/lp) +* [Maksim Lin](https://github.com/maks) +* [Marcus Westin](https://github.com/marcuswestin) +* [Mark Dawson](https://github.com/markdaws) +* [Nithesh Chandra Gupta Mittapally](https://github.com/nithesh) +* [Orion Henry](https://github.com/orionz) +* [Owen Smith](https://github.com/orls) +* [Paul Carey](https://github.com/paulcarey) +* [Philip Tellis](https://github.com/bluesmoon) +* [Pieter Noordhuis](https://github.com/pietern) +* [Rick Olson](https://github.com/technoweenie) +* [Tim Smart](https://github.com/Tim-Smart) +* [TJ Holowaychuk](https://github.com/visionmedia) +* [Umair Siddique](https://github.com/umairsiddique) +* [Vladimir Dronnikov](https://github.com/dvv) +* [Zachary Scott](https://github.com/zzak) Thanks. From 3b645c893d2371bee465ef89d0b2665ba5a9539f Mon Sep 17 00:00:00 2001 From: David Trejo Date: Fri, 27 Apr 2012 13:35:14 -0400 Subject: [PATCH 0015/1748] index.js: fix missing .EXEC alias --- index.js | 1 + 1 file changed, 1 insertion(+) diff --git a/index.js b/index.js index eb27628b97f..5fd97c06ed7 100644 --- a/index.js +++ b/index.js @@ -995,6 +995,7 @@ Multi.prototype.exec = function (callback) { } }); }; +Multi.prototype.EXEC = Multi.prototype.exec; RedisClient.prototype.multi = function (args) { return new Multi(this, args); From 02b62e56bdb693f7a11c1b887a71a26ea59cd685 Mon Sep 17 00:00:00 2001 From: TEHEK Firefox Date: Fri, 27 Jan 2012 13:35:56 -0800 Subject: [PATCH 0016/1748] Fix for [GH-165] - createClient to properly assign parser_module Signed-off-by: David Trejo --- index.js | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/index.js b/index.js index 5fd97c06ed7..585ca73e811 100644 --- a/index.js +++ b/index.js @@ -224,7 +224,7 @@ RedisClient.prototype.init_parser = function () { if (this.options.parser) { if (! parsers.some(function (parser) { if (parser.name === self.options.parser) { - this.parser_module = parser; + self.parser_module = parser; if (exports.debug_mode) { console.log("Using parser module: " + self.parser_module.name); } From f2e123c42dd6f014a8bb91f96155ab4b44e410b8 Mon Sep 17 00:00:00 2001 From: David Trejo Date: Fri, 27 Apr 2012 15:25:09 -0400 Subject: [PATCH 0017/1748] package.json: add dtrejo as maintainer --- package.json | 3 +++ 1 file changed, 3 insertions(+) diff --git a/package.json b/package.json index 325d9f750a9..d78cbaa5ffe 100644 --- a/package.json +++ b/package.json @@ -2,6 +2,9 @@ "version" : "0.7.1", "description" : "Redis client library", "author": "Matt Ranney ", + "maintainers": [ + "David Trejo (http://dtrejo.com/)" + ], "contributors": [ "Rick Olson", "Tim-Smart", From 192784905b495fdd6d920c7180fd3ba6aa10e6a9 Mon Sep 17 00:00:00 2001 From: Jed Schmidt Date: Fri, 16 Mar 2012 22:28:04 +0900 Subject: [PATCH 0018/1748] Removed reference to individual `args` argument When the second argument of `send_command` is not an array, the following error is thrown: Error: send_command: second argument must be an array Signed-off-by: David Trejo --- README.md | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/README.md b/README.md index 6b748dc7b9d..44d98e26e84 100644 --- a/README.md +++ b/README.md @@ -515,8 +515,7 @@ Used internally to send commands to Redis. For convenience, nearly all commands Wiki have been added to the `client` object. However, if I missed any, or if new commands are introduced before this library is updated, you can use `send_command()` to send arbitrary commands to Redis. -All commands are sent as multi-bulk commands. `args` can either be an Array of arguments, or individual arguments, -or omitted completely. +All commands are sent as multi-bulk commands. `args` can either be an Array of arguments, or omitted. ## client.connected From 710a705be268a9a0d7e9f24be8fa23e82a9e4a93 Mon Sep 17 00:00:00 2001 From: David Trejo Date: Fri, 27 Apr 2012 21:07:17 -0400 Subject: [PATCH 0019/1748] readme: highlight js examples --- README.md | 19 ++++++++++++++++++- 1 file changed, 18 insertions(+), 1 deletion(-) diff --git a/README.md b/README.md index 44d98e26e84..afc6227c3d3 100644 --- a/README.md +++ b/README.md @@ -23,6 +23,7 @@ happen between node and native code modules after a node upgrade. Simple example, included as `examples/simple.js`: +```js var redis = require("redis"), client = redis.createClient(); @@ -40,6 +41,7 @@ Simple example, included as `examples/simple.js`: }); client.quit(); }); +``` This will display: @@ -225,6 +227,7 @@ If you want to exit cleanly, call `client.quit()` to send the `QUIT` command aft This example closes the connection to the Redis server before the replies have been read. You probably don't want to do this: +```js var redis = require("redis"), client = redis.createClient(); @@ -233,6 +236,7 @@ want to do this: console.log(reply.toString()); }); client.end(); +``` `client.end()` is useful for timeout cases where something is stuck or taking too long and you want to start over. @@ -282,6 +286,7 @@ Here is a simple example of the API for publish / subscribe. This program opens client connections, subscribes to a channel on one of them, and publishes to that channel on the other: +```js var redis = require("redis"), client1 = redis.createClient(), client2 = redis.createClient(), msg_count = 0; @@ -304,6 +309,7 @@ channel on the other: client1.incr("did a thing"); client1.subscribe("a nice channel"); +``` When a client issues a `SUBSCRIBE` or `PSUBSCRIBE`, that connection is put into "pub/sub" mode. At that point, only commands that modify the subscription set are valid. When the subscription @@ -353,6 +359,7 @@ channel name as `channel` and the new count of subscriptions for this client as `MULTI` commands are queued up until an `EXEC` is issued, and then all commands are run atomically by Redis. The interface in `node_redis` is to return an individual `Multi` object by calling `client.multi()`. +```js var redis = require("./index"), client = redis.createClient(), set_size = 20; @@ -378,6 +385,7 @@ Redis. The interface in `node_redis` is to return an individual `Multi` object console.log("Reply " + index + ": " + reply.toString()); }); }); +``` `client.multi()` is a constructor that returns a `Multi` object. `Multi` objects share all of the same command methods as `client` objects do. Commands are queued up inside the `Multi` object @@ -386,6 +394,7 @@ until `Multi.exec()` is invoked. You can either chain together `MULTI` commands as in the above example, or you can queue individual commands while still sending regular client command as in this example: +```js var redis = require("redis"), client = redis.createClient(), multi; @@ -407,10 +416,12 @@ commands while still sending regular client command as in this example: console.log(replies); // 102, 3 client.quit(); }); +``` In addition to adding commands to the `MULTI` queue individually, you can also pass an array of commands and arguments to the constructor: +```js var redis = require("redis"), client = redis.createClient(), multi; @@ -421,6 +432,7 @@ of commands and arguments to the constructor: ]).exec(function (err, replies) { console.log(replies); }); +``` ## Monitor mode @@ -434,6 +446,7 @@ will emit a `monitor` event for every new monitor message that comes across. Th Here is a simple example: +```js var client = require("redis").createClient(), util = require("util"); @@ -444,7 +457,7 @@ Here is a simple example: client.on("monitor", function (time, args) { console.log(time + ": " + util.inspect(args)); }); - +``` # Extras @@ -466,6 +479,7 @@ The `versions` key contains an array of the elements of the version string for e A handy callback function for displaying return values when testing. Example: +```js var redis = require("redis"), client = redis.createClient(); @@ -473,6 +487,7 @@ A handy callback function for displaying return values when testing. Example: client.set("foo_rand000000000000", "some fantastic value", redis.print); client.get("foo_rand000000000000", redis.print); }); +``` This will print: @@ -485,6 +500,7 @@ Note that this program will not exit cleanly because the client is still connect Boolean to enable debug mode and protocol tracing. +```js var redis = require("redis"), client = redis.createClient(); @@ -493,6 +509,7 @@ Boolean to enable debug mode and protocol tracing. client.on("connect", function () { client.set("foo_rand000000000000", "some fantastic value"); }); +``` This will display: From 0a732b8c0ec234a3d2fd1bb05231c47a0c7e396b Mon Sep 17 00:00:00 2001 From: David Trejo Date: Fri, 27 Apr 2012 21:07:59 -0400 Subject: [PATCH 0020/1748] readme: warn that code in callbacks to commands in a multi are not atomic --- README.md | 2 ++ 1 file changed, 2 insertions(+) diff --git a/README.md b/README.md index afc6227c3d3..11e20178390 100644 --- a/README.md +++ b/README.md @@ -376,6 +376,8 @@ Redis. The interface in `node_redis` is to return an individual `Multi` object .scard("bigset") .smembers("bigset") .keys("*", function (err, replies) { + // NOTE: code in this callback is NOT atomic + // this only happens after the the .exec call finishes. client.mget(replies, redis.print); }) .dbsize() From 38e38c5e7cad361f44d10c71e186491ec7f494bc Mon Sep 17 00:00:00 2001 From: David Trejo Date: Fri, 27 Apr 2012 21:17:14 -0400 Subject: [PATCH 0021/1748] readme: info on how to select a different db --- README.md | 3 +++ 1 file changed, 3 insertions(+) diff --git a/README.md b/README.md index 11e20178390..a6ee761d8cc 100644 --- a/README.md +++ b/README.md @@ -27,6 +27,9 @@ Simple example, included as `examples/simple.js`: var redis = require("redis"), client = redis.createClient(); + // if you'd like to select database 3, instead of 0 (default), call + // client.select(3, function() { /* ... */ }); + client.on("error", function (err) { console.log("Error " + err); }); From 1d22f1f8744f14ea1e90aed22897df55666ec538 Mon Sep 17 00:00:00 2001 From: Matt Ranney Date: Sun, 29 Apr 2012 15:18:30 -1000 Subject: [PATCH 0022/1748] Many contributed fixes. Thank you, contributors. * [GH-190] - pub/sub mode fix (Brian Noguchi) * [GH-165] - parser selection fix (TEHEK) * numerous documentation and examples updates * auth errors emit Errors instead of Strings (David Trejo) --- changelog.md | 9 +++++++++ package.json | 16 ---------------- 2 files changed, 9 insertions(+), 16 deletions(-) diff --git a/changelog.md b/changelog.md index 63a29bc4b76..4248288ce73 100644 --- a/changelog.md +++ b/changelog.md @@ -1,6 +1,15 @@ Changelog ========= +## v0.7.2 - April 29, 2012 + +Many contributed fixes. Thank you, contributors. + +* [GH-190] - pub/sub mode fix (Brian Noguchi) +* [GH-165] - parser selection fix (TEHEK) +* numerous documentation and examples updates +* auth errors emit Errors instead of Strings (David Trejo) + ## v0.7.1 - November 15, 2011 Fix regression in reconnect logic. diff --git a/package.json b/package.json index d78cbaa5ffe..2153627a177 100644 --- a/package.json +++ b/package.json @@ -5,22 +5,6 @@ "maintainers": [ "David Trejo (http://dtrejo.com/)" ], - "contributors": [ - "Rick Olson", - "Tim-Smart", - "TJ Holowaychuk", - "Orion Henry", - "Hank Sims", - "Aivo Paas", - "Paul Carey", - "Pieter Noordhuis", - "Andy Ray", - "Vladimir Dronnikov", - "Dave Hoover", - "David Trejo", - "Philip Tellis", - "Marcus Westin" - ], "main": "./index.js", "scripts": { "test": "node ./test.js" From 172fc8251b4b9225cad53e9c4482c932450cc2f8 Mon Sep 17 00:00:00 2001 From: Matt Ranney Date: Sun, 29 Apr 2012 15:19:51 -1000 Subject: [PATCH 0023/1748] Bump version. --- package.json | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/package.json b/package.json index 2153627a177..9234d9156c0 100644 --- a/package.json +++ b/package.json @@ -1,5 +1,5 @@ { "name" : "redis", - "version" : "0.7.1", + "version" : "0.7.2", "description" : "Redis client library", "author": "Matt Ranney ", "maintainers": [ From 874a893c2cdeb5372782cce33e2dbdaeacb6541c Mon Sep 17 00:00:00 2001 From: Dave Peticolas Date: Wed, 8 Feb 2012 07:56:55 -0800 Subject: [PATCH 0024/1748] test.js: Switch to pubsub mode when the number of channels is > 0. Tests for a bug where the client unsubscribes and then subscribes to a single channel. If the subscription is sent before the response to the unsubscribe is received, then the client would leave pubsub mode when it received the unsubscribe response and then fail to enter when the subsequent subscription is processed. This is another test for #190: https://github.com/mranney/node_redis/pull/190 Signed-off-by: David Trejo --- test.js | 15 +++++++++++++++ 1 file changed, 15 insertions(+) diff --git a/test.js b/test.js index cc7ba888786..a84bdb9c72b 100644 --- a/test.js +++ b/test.js @@ -582,6 +582,21 @@ tests.SUBSCRIBE = function () { }); }; +tests.SUB_UNSUB_SUB = function () { + var name = "SUB_UNSUB_SUB"; + client3.subscribe('chan3'); + client3.unsubscribe('chan3'); + client3.subscribe('chan3', function (err, results) { + assert.strictEqual(null, err, "unexpected error: " + err); + client2.publish('chan3', 'foo'); + }); + client3.on('message', function (channel, message) { + assert.strictEqual(channel, 'chan3'); + assert.strictEqual(message, 'foo'); + next(name); + }); +}; + tests.SUBSCRIBE_QUIT = function () { var name = "SUBSCRIBE_QUIT"; client3.on("end", function () { From 65142cf8c02aaf1d77433e916ebf8aefd91862c8 Mon Sep 17 00:00:00 2001 From: DTrejo Date: Mon, 4 Jun 2012 10:26:12 -0700 Subject: [PATCH 0025/1748] [doc] missing key, fix #223 --- README.md | 7 +++++++ 1 file changed, 7 insertions(+) diff --git a/README.md b/README.md index a6ee761d8cc..6ffc0555757 100644 --- a/README.md +++ b/README.md @@ -104,6 +104,13 @@ Note that in either form the `callback` is optional: client.set("some key", "some val"); client.set(["some other key", "some val"]); +If the key is missing, reply will be null (probably): + + client.get("missingkey", function(err, reply) { + // reply is null when the key is missing + console.log(reply); + }); + For a list of Redis commands, see [Redis Command Reference](http://redis.io/commands) The commands can be specified in uppercase or lowercase for convenience. `client.get()` is the same as `client.GET()`. From 7734fb63b408ad426168074c2e1b612b7c3efbcd Mon Sep 17 00:00:00 2001 From: DTrejo Date: Mon, 4 Jun 2012 13:43:10 -0700 Subject: [PATCH 0026/1748] fix #222 hmset object expansion by @kartikrao --- index.js | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/index.js b/index.js index 77cebdfa7d8..b39ab4ef824 100644 --- a/index.js +++ b/index.js @@ -943,7 +943,7 @@ Multi.prototype.exec = function (callback) { if (args.length === 1 && Array.isArray(args[0])) { args = args[0]; } - if (command === 'hmset' && typeof args[1] === 'object') { + if (command.toLowerCase() === 'hmset' && typeof args[1] === 'object') { obj = args.pop(); Object.keys(obj).forEach(function (key) { args.push(key); From b60e001fa08403240a6ecbf0c36d1c08ed8ccd9d Mon Sep 17 00:00:00 2001 From: DTrejo Date: Mon, 4 Jun 2012 16:13:56 -0700 Subject: [PATCH 0027/1748] hmset throws/errors out on non-string values. fixes #218 --- README.md | 2 +- index.js | 5 +++++ test.js | 18 ++++++++++++++++++ 3 files changed, 24 insertions(+), 1 deletion(-) diff --git a/README.md b/README.md index 6ffc0555757..5cd5ec9965f 100644 --- a/README.md +++ b/README.md @@ -277,7 +277,7 @@ Output: Multiple values in a hash can be set by supplying an object: client.HMSET(key2, { - "0123456789": "abcdefghij", + "0123456789": "abcdefghij", // NOTE: the key and value must both be strings "some manner of key": "a type of value" }); diff --git a/index.js b/index.js index b39ab4ef824..45b4c7ed021 100644 --- a/index.js +++ b/index.js @@ -898,6 +898,11 @@ RedisClient.prototype.hmset = function (args, callback) { for (i = 0, il = tmp_keys.length; i < il ; i++) { key = tmp_keys[i]; tmp_args.push(key); + if (typeof args[1][key] !== "string") { + var err = new Error("hmset expected value to be a string", key, ":", args[1][key]); + if (callback) return callback(err); + else throw err; + } tmp_args.push(args[1][key]); } args = tmp_args; diff --git a/test.js b/test.js index a84bdb9c72b..037512669fd 100644 --- a/test.js +++ b/test.js @@ -1348,6 +1348,24 @@ tests.OPTIONAL_CALLBACK_UNDEFINED = function () { client.get("op_cb2", last(name, require_string("y", name))); }; +tests.HMSET_THROWS_ON_NON_STRINGS = function () { + var name = "HMSET_THROWS_ON_NON_STRINGS"; + var hash = name; + var data = { "a": [ "this is not a string" ] }; + + client.hmset(hash, data, cb); + function cb(e, r) { + assert(e); // should be an error! + } + + // alternative way it throws + function thrower() { + client.hmset(hash, data); + } + assert.throws(thrower); + next(name); +}; + // TODO - need a better way to test auth, maybe auto-config a local Redis server or something. // Yes, this is the real password. Please be nice, thanks. tests.auth = function () { From 2224767c4cf725ddcd2546984565e97537824c06 Mon Sep 17 00:00:00 2001 From: Trae Robrock Date: Thu, 28 Jun 2012 17:13:40 -0700 Subject: [PATCH 0028/1748] Moving some logic that should fix the idle event Signed-off-by: DTrejo --- index.js | 7 +++---- 1 file changed, 3 insertions(+), 4 deletions(-) diff --git a/index.js b/index.js index 45b4c7ed021..76fb2640f6c 100644 --- a/index.js +++ b/index.js @@ -525,8 +525,9 @@ function reply_to_strings(reply) { RedisClient.prototype.return_reply = function (reply) { var command_obj, obj, i, len, type, timestamp, argindex, args, queue_len; - - queue_len = this.command_queue.getLength(); + + command_obj = this.command_queue.shift() + queue_len = this.command_queue.getLength(); if (this.pub_sub_mode === false && queue_len === 0) { this.emit("idle"); @@ -537,8 +538,6 @@ RedisClient.prototype.return_reply = function (reply) { this.should_buffer = false; } - command_obj = this.command_queue.shift(); - if (command_obj && !command_obj.sub_command) { if (typeof command_obj.callback === "function") { if (this.options.detect_buffers && command_obj.buffer_args === false) { From db4f1648d48b7b3535fe0afce163d3cefd1888e1 Mon Sep 17 00:00:00 2001 From: Trae Robrock Date: Thu, 28 Jun 2012 21:36:00 -0700 Subject: [PATCH 0029/1748] Adding tests for idle event Signed-off-by: DTrejo --- test.js | 11 +++++++++++ 1 file changed, 11 insertions(+) diff --git a/test.js b/test.js index 037512669fd..0fa15e9a794 100644 --- a/test.js +++ b/test.js @@ -470,6 +470,17 @@ tests.reconnect = function () { }); }; +tests.idle = function () { + var name = "idle"; + + client.on("idle", function on_idle() { + client.removeListener("idle", on_idle); + next(name); + }); + + client.set("idle", "test"); +}; + tests.HSET = function () { var key = "test hash", field1 = new Buffer("0123456789"), From eb005b10dfb5fe40cd10ff30ac3c4ec20e9e90c1 Mon Sep 17 00:00:00 2001 From: Trae Robrock Date: Thu, 28 Jun 2012 22:42:44 -0700 Subject: [PATCH 0030/1748] Forgot a comma Signed-off-by: DTrejo --- index.js | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/index.js b/index.js index 76fb2640f6c..22e3b553a4a 100644 --- a/index.js +++ b/index.js @@ -526,7 +526,7 @@ function reply_to_strings(reply) { RedisClient.prototype.return_reply = function (reply) { var command_obj, obj, i, len, type, timestamp, argindex, args, queue_len; - command_obj = this.command_queue.shift() + command_obj = this.command_queue.shift(), queue_len = this.command_queue.getLength(); if (this.pub_sub_mode === false && queue_len === 0) { From 83dc4c999b7b00c529d254f0acd0881d48052b7d Mon Sep 17 00:00:00 2001 From: Shankar Karuppiah Date: Tue, 8 May 2012 13:00:12 +0300 Subject: [PATCH 0031/1748] Added option to disable offline queue Signed-off-by: DTrejo --- index.js | 23 ++++++++++++----- test.js | 78 +++++++++++++++++++++++++++++++++++++++++++------------- 2 files changed, 76 insertions(+), 25 deletions(-) diff --git a/index.js b/index.js index 22e3b553a4a..b3f0beda573 100644 --- a/index.js +++ b/index.js @@ -50,6 +50,8 @@ function RedisClient(stream, options) { if (options.connect_timeout && !isNaN(options.connect_timeout) && options.connect_timeout > 0) { this.connect_timeout = +options.connect_timeout; } + this.enable_offline_queue = this.options.enable_offline_queue || true; + this.initialize_retry_vars(); this.pub_sub_mode = false; this.subscription_set = {}; @@ -59,7 +61,7 @@ function RedisClient(stream, options) { this.auth_pass = null; this.parser_module = null; this.selected_db = null; // save the selected db here, used when reconnecting - + var self = this; this.stream.on("connect", function () { @@ -661,11 +663,18 @@ RedisClient.prototype.send_command = function (command, args, callback) { if (!stream.writable) { console.log("send command: stream is not writeable."); } - - console.log("Queueing " + command + " for next server connection."); } - this.offline_queue.push(command_obj); - this.should_buffer = true; + + if (this.enable_offline_queue) { + if (exports.debug_mode) { + console.log("Queueing " + command + " for next server connection."); + } + this.offline_queue.push(command_obj); + this.should_buffer = true; + } else { + command_obj.callback(new Error('send command: stream is not writeable.')); + } + return false; } @@ -745,7 +754,7 @@ RedisClient.prototype.send_command = function (command, args, callback) { RedisClient.prototype.pub_sub_command = function (command_obj) { var i, key, command, args; - + if (this.pub_sub_mode === false && exports.debug_mode) { console.log("Entering pub/sub mode from " + command_obj.command); } @@ -795,7 +804,7 @@ exports.Multi = Multi; // take 2 arrays and return the union of their elements function set_union(seta, setb) { var obj = {}; - + seta.forEach(function (val) { obj[val] = true; }); diff --git a/test.js b/test.js index 0fa15e9a794..222abad37d1 100644 --- a/test.js +++ b/test.js @@ -645,7 +645,7 @@ tests.TYPE = function () { client.sadd(["set key", "should be a set"], require_number_any(name)); client.zadd(["zset key", "10.0", "should be a zset"], require_number_any(name)); client.hset(["hash key", "hashtest", "should be a hash"], require_number_any(0, name)); - + client.TYPE(["string key"], require_string("string", name)); client.TYPE(["list key"], require_string("list", name)); client.TYPE(["set key"], require_string("set", name)); @@ -825,7 +825,7 @@ tests.UTF8 = function () { tests.SADD = function () { var name = "SADD"; - + client.del('set0'); client.sadd('set0', 'member0', require_number(1, name)); client.sadd('set0', 'member0', last(name, require_number(0, name))); @@ -833,7 +833,7 @@ tests.SADD = function () { tests.SADD2 = function () { var name = "SADD2"; - + client.del("set0"); client.sadd("set0", ["member0", "member1", "member2"], require_number(3, name)); client.smembers("set0", function (err, res) { @@ -847,7 +847,7 @@ tests.SADD2 = function () { tests.SISMEMBER = function () { var name = "SISMEMBER"; - + client.del('set0'); client.sadd('set0', 'member0', require_number(1, name)); client.sismember('set0', 'member0', require_number(1, name)); @@ -856,7 +856,7 @@ tests.SISMEMBER = function () { tests.SCARD = function () { var name = "SCARD"; - + client.del('set0'); client.sadd('set0', 'member0', require_number(1, name)); client.scard('set0', require_number(1, name)); @@ -876,7 +876,7 @@ tests.SREM = function () { tests.SPOP = function () { var name = "SPOP"; - + client.del('zzz'); client.sadd('zzz', 'member0', require_number(1, name)); client.scard('zzz', require_number(1, name)); @@ -893,7 +893,7 @@ tests.SPOP = function () { tests.SDIFF = function () { var name = "SDIFF"; - + client.del('foo'); client.sadd('foo', 'x', require_number(1, name)); client.sadd('foo', 'a', require_number(1, name)); @@ -919,7 +919,7 @@ tests.SDIFF = function () { tests.SDIFFSTORE = function () { var name = "SDIFFSTORE"; - + client.del('foo'); client.del('bar'); client.del('baz'); @@ -952,7 +952,7 @@ tests.SDIFFSTORE = function () { tests.SMEMBERS = function () { var name = "SMEMBERS"; - + client.del('foo'); client.sadd('foo', 'x', require_number(1, name)); @@ -996,7 +996,7 @@ tests.SINTER = function () { client.del('sa'); client.del('sb'); client.del('sc'); - + client.sadd('sa', 'a', require_number(1, name)); client.sadd('sa', 'b', require_number(1, name)); client.sadd('sa', 'c', require_number(1, name)); @@ -1078,11 +1078,11 @@ tests.SINTERSTORE = function () { tests.SUNION = function () { var name = "SUNION"; - + client.del('sa'); client.del('sb'); client.del('sc'); - + client.sadd('sa', 'a', require_number(1, name)); client.sadd('sa', 'b', require_number(1, name)); client.sadd('sa', 'c', require_number(1, name)); @@ -1106,12 +1106,12 @@ tests.SUNION = function () { tests.SUNIONSTORE = function () { var name = "SUNIONSTORE"; - + client.del('sa'); client.del('sb'); client.del('sc'); client.del('foo'); - + client.sadd('sa', 'a', require_number(1, name)); client.sadd('sa', 'b', require_number(1, name)); client.sadd('sa', 'c', require_number(1, name)); @@ -1148,7 +1148,7 @@ tests.SORT = function () { client.del('y'); client.del('x'); - + client.rpush('y', 'd', require_number(1, name)); client.rpush('y', 'b', require_number(2, name)); client.rpush('y', 'a', require_number(3, name)); @@ -1257,7 +1257,7 @@ tests.SORT = function () { assert.deepEqual(buffers_to_strings(values), ['foo', 'bux', 'bar', 'tux', 'baz', 'lux', 'buz', 'qux'], name); next(name); }); - + // TODO - sort by hash value }; @@ -1302,7 +1302,7 @@ tests.BLPOP = function () { client2.BLPOP("blocking list", 0, function (err, res) { assert.strictEqual("blocking list", res[0].toString()); assert.strictEqual("initial value", res[1].toString()); - + client.rpush("blocking list", "wait for this value"); }); client2.BLPOP("blocking list", 0, function (err, res) { @@ -1315,7 +1315,7 @@ tests.BLPOP = function () { tests.BLPOP_TIMEOUT = function () { var name = "BLPOP_TIMEOUT"; - + // try to BLPOP the list again, which should be empty. This should timeout and return null. client2.BLPOP("blocking list", 1, function (err, res) { if (err) { @@ -1377,6 +1377,48 @@ tests.HMSET_THROWS_ON_NON_STRINGS = function () { next(name); }; +tests.ENABLE_OFFLINE_QUEUE_TRUE = function () { + var name = "ENABLE_OFFLINE_QUEUE_TRUE"; + var cli = redis.createClient(9999, null, { + max_attempts: 1 + // default :) + // enable_offline_queue: true + }); + cli.on('error', function(e) { + // ignore, b/c expecting a "can't connect" error + }); + return setTimeout(function() { + cli.set(name, name, function(err, result) { + assert.ifError(err); + }); + + return setTimeout(function(){ + assert.strictEqual(cli.offline_queue.length, 1); + return next(name); + }, 25); + }, 50); +}; + +tests.ENABLE_OFFLINE_QUEUE_FALSE = function () { + var name = "ENABLE_OFFLINE_QUEUE_FALSE"; + var cli = redis.createClient(9999, null, { + max_attempts: 1, + enable_offline_queue: false + }); + cli.on('error', function() { + // ignore, see above + }); + assert.throws(function () { + cli.set(name, name) + }) + assert.doesNotThrow(function () { + cli.set(name, name, function (err) { + // should callback with an error + assert.ok(err); + }); + }); +}; + // TODO - need a better way to test auth, maybe auto-config a local Redis server or something. // Yes, this is the real password. Please be nice, thanks. tests.auth = function () { From 71a52638de38da328bd2d9e773657405ffafe40b Mon Sep 17 00:00:00 2001 From: Shankar Karuppiah Date: Tue, 8 May 2012 15:50:31 +0300 Subject: [PATCH 0032/1748] Added checking for callback before attempting to execute the callback Signed-off-by: DTrejo --- index.js | 7 ++++++- 1 file changed, 6 insertions(+), 1 deletion(-) diff --git a/index.js b/index.js index b3f0beda573..dacc113f414 100644 --- a/index.js +++ b/index.js @@ -672,7 +672,12 @@ RedisClient.prototype.send_command = function (command, args, callback) { this.offline_queue.push(command_obj); this.should_buffer = true; } else { - command_obj.callback(new Error('send command: stream is not writeable.')); + var not_writeable_error = new Error('send_command: stream not writeable. enable_offline_queue is false'); + if (command_obj.callback) { + command_obj.callback(not_writeable_error); + } else { + throw not_writeable_error; + } } return false; From c913c06f46dff837359e5ad6591617235734d867 Mon Sep 17 00:00:00 2001 From: Shankar Karuppiah Date: Fri, 8 Jun 2012 00:27:48 +0300 Subject: [PATCH 0033/1748] Added documentation for disable_offline_queue option Signed-off-by: DTrejo --- README.md | 63 ++++++++++++++++++++++++++++++------------------------- 1 file changed, 34 insertions(+), 29 deletions(-) diff --git a/README.md b/README.md index 5cd5ec9965f..23f5aba0582 100644 --- a/README.md +++ b/README.md @@ -8,7 +8,7 @@ experimental Redis server branches. Install with: npm install redis - + Pieter Noordhuis has provided a binding to the official `hiredis` C library, which is non-blocking and fast. To use `hiredis`, do: npm install hiredis redis @@ -55,7 +55,7 @@ This will display: 2 replies: 0: hashtest 1 1: hashtest 2 - mjr:~/work/node_redis (master)$ + mjr:~/work/node_redis (master)$ ## Performance @@ -98,7 +98,7 @@ Here is an example of passing an array of arguments and a callback: Here is that same call in the second style: client.mset("test keys 1", "test val 1", "test keys 2", "test val 2", function (err, res) {}); - + Note that in either form the `callback` is optional: client.set("some key", "some val"); @@ -115,8 +115,8 @@ For a list of Redis commands, see [Redis Command Reference](http://redis.io/comm The commands can be specified in uppercase or lowercase for convenience. `client.get()` is the same as `client.GET()`. -Minimal parsing is done on the replies. Commands that return a single line reply return JavaScript Strings, -integer replies return JavaScript Numbers, "bulk" replies return node Buffers, and "multi bulk" replies return a +Minimal parsing is done on the replies. Commands that return a single line reply return JavaScript Strings, +integer replies return JavaScript Numbers, "bulk" replies return node Buffers, and "multi bulk" replies return a JavaScript Array of node Buffers. `HGETALL` returns an Object with Buffers keyed by the hash keys. # API @@ -127,8 +127,8 @@ JavaScript Array of node Buffers. `HGETALL` returns an Object with Buffers keye ### "ready" -`client` will emit `ready` a connection is established to the Redis server and the server reports -that it is ready to receive commands. Commands issued before the `ready` event are queued, +`client` will emit `ready` a connection is established to the Redis server and the server reports +that it is ready to receive commands. Commands issued before the `ready` event are queued, then replayed just before this event is emitted. ### "connect" @@ -141,11 +141,11 @@ you are free to try to send commands. `client` will emit `error` when encountering an error connecting to the Redis server. -Note that "error" is a special event type in node. If there are no listeners for an -"error" event, node will exit. This is usually what you want, but it can lead to some +Note that "error" is a special event type in node. If there are no listeners for an +"error" event, node will exit. This is usually what you want, but it can lead to some cryptic error messages like this: - mjr:~/work/node_redis (master)$ node example.js + mjr:~/work/node_redis (master)$ node example.js node.js:50 throw e; @@ -168,7 +168,7 @@ It would be nice to distinguish these two cases. `client` will emit `drain` when the TCP connection to the Redis server has been buffering, but is now writable. This event can be used to stream commands in to Redis and adapt to backpressure. Right now, -you need to check `client.command_queue.length` to decide when to reduce your send rate. Then you can +you need to check `client.command_queue.length` to decide when to reduce your send rate. Then you can resume sending when you get `drain`. ### "idle" @@ -190,13 +190,18 @@ if any of the input arguments to the original command were Buffer objects. This option lets you switch between Buffers and Strings on a per-command basis, whereas `return_buffers` applies to every command on a client. * `socket_nodelay`: defaults to `true`. Whether to call setNoDelay() on the TCP stream, which disables the -Nagle algorithm on the underlying socket. Setting this option to `false` can result in additional throughput at the +Nagle algorithm on the underlying socket. Setting this option to `false` can result in additional throughput at the cost of more latency. Most applications will want this set to `true`. * `no_ready_check`: defaults to `false`. When a connection is established to the Redis server, the server might still -be loading the database from disk. While loading, the server not respond to any commands. To work around this, +be loading the database from disk. While loading, the server not respond to any commands. To work around this, `node_redis` has a "ready check" which sends the `INFO` command to the server. The response from the `INFO` command indicates whether the server is ready for more commands. When ready, `node_redis` emits a `ready` event. Setting `no_ready_check` to `true` will inhibit this check. +* `enable_offline_queue`: defaults to `true`. By default, if there is no active +connection to the redis server, commands are added to a queue and are executed +once the connection has been established. Setting `enable_offline_queue` to +`false` will disable this feature and the callback will be execute immediately +with an error, or an error will be thrown if no callback is specified. ```js var redis = require("redis"), @@ -234,7 +239,7 @@ something like this `Error: Ready check failed: ERR operation not permitted`. Forcibly close the connection to the Redis server. Note that this does not wait until all replies have been parsed. If you want to exit cleanly, call `client.quit()` to send the `QUIT` command after you have handled all replies. -This example closes the connection to the Redis server before the replies have been read. You probably don't +This example closes the connection to the Redis server before the replies have been read. You probably don't want to do this: ```js @@ -248,7 +253,7 @@ want to do this: client.end(); ``` -`client.end()` is useful for timeout cases where something is stuck or taking too long and you want +`client.end()` is useful for timeout cases where something is stuck or taking too long and you want to start over. ## Friendlier hash commands @@ -258,7 +263,7 @@ When dealing with hash values, there are a couple of useful exceptions to this. ### client.hgetall(hash) -The reply from an HGETALL command will be converted into a JavaScript Object by `node_redis`. That way you can interact +The reply from an HGETALL command will be converted into a JavaScript Object by `node_redis`. That way you can interact with the responses using JavaScript syntax. Example: @@ -293,7 +298,7 @@ Multiple values may also be set by supplying a list: ## Publish / Subscribe Here is a simple example of the API for publish / subscribe. This program opens two -client connections, subscribes to a channel on one of them, and publishes to that +client connections, subscribes to a channel on one of them, and publishes to that channel on the other: ```js @@ -322,7 +327,7 @@ channel on the other: ``` When a client issues a `SUBSCRIBE` or `PSUBSCRIBE`, that connection is put into "pub/sub" mode. -At that point, only commands that modify the subscription set are valid. When the subscription +At that point, only commands that modify the subscription set are valid. When the subscription set is empty, the connection is put back into regular mode. If you need to send regular commands to Redis while in pub/sub mode, just open another connection. @@ -344,23 +349,23 @@ name as `channel`, and the message Buffer as `message`. ### "subscribe" (channel, count) -Client will emit `subscribe` in response to a `SUBSCRIBE` command. Listeners are passed the +Client will emit `subscribe` in response to a `SUBSCRIBE` command. Listeners are passed the channel name as `channel` and the new count of subscriptions for this client as `count`. -### "psubscribe" (pattern, count) +### "psubscribe" (pattern, count) Client will emit `psubscribe` in response to a `PSUBSCRIBE` command. Listeners are passed the original pattern as `pattern`, and the new count of subscriptions for this client as `count`. ### "unsubscribe" (channel, count) -Client will emit `unsubscribe` in response to a `UNSUBSCRIBE` command. Listeners are passed the +Client will emit `unsubscribe` in response to a `UNSUBSCRIBE` command. Listeners are passed the channel name as `channel` and the new count of subscriptions for this client as `count`. When `count` is 0, this client has left pub/sub mode and no more pub/sub events will be emitted. ### "punsubscribe" (pattern, count) -Client will emit `punsubscribe` in response to a `PUNSUBSCRIBE` command. Listeners are passed the +Client will emit `punsubscribe` in response to a `PUNSUBSCRIBE` command. Listeners are passed the channel name as `channel` and the new count of subscriptions for this client as `count`. When `count` is 0, this client has left pub/sub mode and no more pub/sub events will be emitted. @@ -410,7 +415,7 @@ commands while still sending regular client command as in this example: var redis = require("redis"), client = redis.createClient(), multi; - // start a separate multi command queue + // start a separate multi command queue multi = client.multi(); multi.incr("incr thing", redis.print); multi.incr("incr other thing", redis.print); @@ -430,7 +435,7 @@ commands while still sending regular client command as in this example: }); ``` -In addition to adding commands to the `MULTI` queue individually, you can also pass an array +In addition to adding commands to the `MULTI` queue individually, you can also pass an array of commands and arguments to the constructor: ```js @@ -453,7 +458,7 @@ Redis supports the `MONITOR` command, which lets you see all commands received b across all client connections, including from other client libraries and other computers. After you send the `MONITOR` command, no other commands are valid on that connection. `node_redis` -will emit a `monitor` event for every new monitor message that comes across. The callback for the +will emit a `monitor` event for every new monitor message that comes across. The callback for the `monitor` event takes a timestamp from the Redis server and an array of command arguments. Here is a simple example: @@ -477,7 +482,7 @@ Some other things you might like to know about. ## client.server_info -After the ready probe completes, the results from the INFO command are saved in the `client.server_info` +After the ready probe completes, the results from the INFO command are saved in the `client.server_info` object. The `versions` key contains an array of the elements of the version string for easy comparison. @@ -525,7 +530,7 @@ Boolean to enable debug mode and protocol tracing. This will display: - mjr:~/work/node_redis (master)$ node ~/example.js + mjr:~/work/node_redis (master)$ node ~/example.js send command: *3 $3 SET @@ -540,7 +545,7 @@ This will display: ## client.send_command(command_name, args, callback) -Used internally to send commands to Redis. For convenience, nearly all commands that are published on the Redis +Used internally to send commands to Redis. For convenience, nearly all commands that are published on the Redis Wiki have been added to the `client` object. However, if I missed any, or if new commands are introduced before this library is updated, you can use `send_command()` to send arbitrary commands to Redis. @@ -552,7 +557,7 @@ Boolean tracking the state of the connection to the Redis server. ## client.command_queue.length -The number of commands that have been sent to the Redis server but not yet replied to. You can use this to +The number of commands that have been sent to the Redis server but not yet replied to. You can use this to enforce some kind of maximum queue depth for commands while connected. Don't mess with `client.command_queue` though unless you really know what you are doing. From a532e65ccfd9f1fb7299d057d1a512db238cfc9c Mon Sep 17 00:00:00 2001 From: DTrejo Date: Thu, 5 Jul 2012 17:50:56 -0700 Subject: [PATCH 0034/1748] readme: how to contribute --- README.md | 7 +++++++ 1 file changed, 7 insertions(+) diff --git a/README.md b/README.md index 23f5aba0582..cc2fee1e140 100644 --- a/README.md +++ b/README.md @@ -587,6 +587,13 @@ Performance can be better for very large values. I think there are more performance improvements left in there for smaller values, especially for large lists of small values. +## How to Contribute +- make your changes on a branch +- your code should be the same style as the rest of the code +- add your tests to `test.js` +- add your documentation to `README.md` (if needed). +- open a pull request! + ## Contributors Some people have have added features and fixed bugs in `node_redis` other than me. From 1b0b2dc87dc5c92525af24b90f19ea5e63ee41b9 Mon Sep 17 00:00:00 2001 From: DTrejo Date: Thu, 5 Jul 2012 19:18:26 -0700 Subject: [PATCH 0035/1748] index.js: fix enable_offline_queue default. tests pass --- index.js | 6 +++++- test.js | 3 +++ 2 files changed, 8 insertions(+), 1 deletion(-) diff --git a/index.js b/index.js index dacc113f414..bd8c16a2ef8 100644 --- a/index.js +++ b/index.js @@ -50,7 +50,11 @@ function RedisClient(stream, options) { if (options.connect_timeout && !isNaN(options.connect_timeout) && options.connect_timeout > 0) { this.connect_timeout = +options.connect_timeout; } - this.enable_offline_queue = this.options.enable_offline_queue || true; + + this.enable_offline_queue = true; + if (typeof this.options.enable_offline_queue === "boolean") { + this.enable_offline_queue = this.options.enable_offline_queue; + } this.initialize_retry_vars(); this.pub_sub_mode = false; diff --git a/test.js b/test.js index 222abad37d1..f6f7f0e3be0 100644 --- a/test.js +++ b/test.js @@ -1415,6 +1415,9 @@ tests.ENABLE_OFFLINE_QUEUE_FALSE = function () { cli.set(name, name, function (err) { // should callback with an error assert.ok(err); + setTimeout(function () { + next(name); + }, 50); }); }); }; From 64702f18c105bfdf94b94aae8ac57457060d4437 Mon Sep 17 00:00:00 2001 From: DTrejo Date: Thu, 5 Jul 2012 19:20:56 -0700 Subject: [PATCH 0036/1748] rename tests to benches for clarity --- {tests => benches}/buffer_bench.js | 0 {tests => benches}/hiredis_parser.js | 0 {tests => benches}/re_sub_test.js | 0 {tests => benches}/reconnect_test.js | 0 {tests => benches}/stress/codec.js | 0 {tests => benches}/stress/pubsub/pub.js | 0 {tests => benches}/stress/pubsub/run | 0 {tests => benches}/stress/pubsub/server.js | 0 {tests => benches}/stress/rpushblpop/pub.js | 0 {tests => benches}/stress/rpushblpop/run | 0 {tests => benches}/stress/rpushblpop/server.js | 0 {tests => benches}/stress/speed/00 | 0 {tests => benches}/stress/speed/plot | 0 {tests => benches}/stress/speed/size-rate.png | Bin {tests => benches}/stress/speed/speed.js | 0 {tests => benches}/sub_quit_test.js | 0 16 files changed, 0 insertions(+), 0 deletions(-) rename {tests => benches}/buffer_bench.js (100%) rename {tests => benches}/hiredis_parser.js (100%) rename {tests => benches}/re_sub_test.js (100%) rename {tests => benches}/reconnect_test.js (100%) rename {tests => benches}/stress/codec.js (100%) rename {tests => benches}/stress/pubsub/pub.js (100%) rename {tests => benches}/stress/pubsub/run (100%) rename {tests => benches}/stress/pubsub/server.js (100%) rename {tests => benches}/stress/rpushblpop/pub.js (100%) rename {tests => benches}/stress/rpushblpop/run (100%) rename {tests => benches}/stress/rpushblpop/server.js (100%) rename {tests => benches}/stress/speed/00 (100%) rename {tests => benches}/stress/speed/plot (100%) rename {tests => benches}/stress/speed/size-rate.png (100%) rename {tests => benches}/stress/speed/speed.js (100%) rename {tests => benches}/sub_quit_test.js (100%) diff --git a/tests/buffer_bench.js b/benches/buffer_bench.js similarity index 100% rename from tests/buffer_bench.js rename to benches/buffer_bench.js diff --git a/tests/hiredis_parser.js b/benches/hiredis_parser.js similarity index 100% rename from tests/hiredis_parser.js rename to benches/hiredis_parser.js diff --git a/tests/re_sub_test.js b/benches/re_sub_test.js similarity index 100% rename from tests/re_sub_test.js rename to benches/re_sub_test.js diff --git a/tests/reconnect_test.js b/benches/reconnect_test.js similarity index 100% rename from tests/reconnect_test.js rename to benches/reconnect_test.js diff --git a/tests/stress/codec.js b/benches/stress/codec.js similarity index 100% rename from tests/stress/codec.js rename to benches/stress/codec.js diff --git a/tests/stress/pubsub/pub.js b/benches/stress/pubsub/pub.js similarity index 100% rename from tests/stress/pubsub/pub.js rename to benches/stress/pubsub/pub.js diff --git a/tests/stress/pubsub/run b/benches/stress/pubsub/run similarity index 100% rename from tests/stress/pubsub/run rename to benches/stress/pubsub/run diff --git a/tests/stress/pubsub/server.js b/benches/stress/pubsub/server.js similarity index 100% rename from tests/stress/pubsub/server.js rename to benches/stress/pubsub/server.js diff --git a/tests/stress/rpushblpop/pub.js b/benches/stress/rpushblpop/pub.js similarity index 100% rename from tests/stress/rpushblpop/pub.js rename to benches/stress/rpushblpop/pub.js diff --git a/tests/stress/rpushblpop/run b/benches/stress/rpushblpop/run similarity index 100% rename from tests/stress/rpushblpop/run rename to benches/stress/rpushblpop/run diff --git a/tests/stress/rpushblpop/server.js b/benches/stress/rpushblpop/server.js similarity index 100% rename from tests/stress/rpushblpop/server.js rename to benches/stress/rpushblpop/server.js diff --git a/tests/stress/speed/00 b/benches/stress/speed/00 similarity index 100% rename from tests/stress/speed/00 rename to benches/stress/speed/00 diff --git a/tests/stress/speed/plot b/benches/stress/speed/plot similarity index 100% rename from tests/stress/speed/plot rename to benches/stress/speed/plot diff --git a/tests/stress/speed/size-rate.png b/benches/stress/speed/size-rate.png similarity index 100% rename from tests/stress/speed/size-rate.png rename to benches/stress/speed/size-rate.png diff --git a/tests/stress/speed/speed.js b/benches/stress/speed/speed.js similarity index 100% rename from tests/stress/speed/speed.js rename to benches/stress/speed/speed.js diff --git a/tests/sub_quit_test.js b/benches/sub_quit_test.js similarity index 100% rename from tests/sub_quit_test.js rename to benches/sub_quit_test.js From a798cc0e574f4e5d6fb697aa4d2597dc33c27bc7 Mon Sep 17 00:00:00 2001 From: DTrejo Date: Thu, 5 Jul 2012 19:27:20 -0700 Subject: [PATCH 0037/1748] readme.md: typo --- README.md | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/README.md b/README.md index cc2fee1e140..43a742b26a6 100644 --- a/README.md +++ b/README.md @@ -588,9 +588,9 @@ Performance can be better for very large values. I think there are more performance improvements left in there for smaller values, especially for large lists of small values. ## How to Contribute -- make your changes on a branch +- make your changes on a fork - your code should be the same style as the rest of the code -- add your tests to `test.js` +- add your tests to `./test.js` - add your documentation to `README.md` (if needed). - open a pull request! From 7ec1815188fafa9764e8b1494692f28ff3cfd549 Mon Sep 17 00:00:00 2001 From: Jerry Sievert Date: Sat, 7 Jul 2012 02:44:51 -0700 Subject: [PATCH 0038/1748] generally faster javascript parser --- lib/parser/javascript.js | 474 ++++++++++++++++----------------------- 1 file changed, 193 insertions(+), 281 deletions(-) diff --git a/lib/parser/javascript.js b/lib/parser/javascript.js index b8f5bc68298..bf67573e9f8 100644 --- a/lib/parser/javascript.js +++ b/lib/parser/javascript.js @@ -1,317 +1,229 @@ -/*global Buffer require exports console setTimeout */ - -// TODO - incorporate these V8 pro tips: -// pre-allocate Arrays if length is known in advance -// do not use delete -// use numbers for parser state - var events = require("events"), - util = require("../util"); + util = require('../util'); + +function Packet (type, size) { + this.type = type; + this.size = +size; +} +exports.name = 'faster'; exports.debug_mode = false; -exports.name = "javascript"; -function RedisReplyParser(options) { +function FasterReplyParser (options) { this.name = exports.name; - this.options = options || {}; - this.reset(); - events.EventEmitter.call(this); + this.options = options || { }; + + this._buffer = null; + this._offset = 0; + this._encoding = 'utf-8'; + this._debug_mode = options.debug_mode; + this._reply_type = null; } -util.inherits(RedisReplyParser, events.EventEmitter); +util.inherits(FasterReplyParser, events.EventEmitter); -exports.Parser = RedisReplyParser; +exports.Parser = FasterReplyParser; -// Buffer.toString() is quite slow for small strings -function small_toString(buf, len) { - var tmp = "", i; +FasterReplyParser.prototype._parseResult = function (type) { + var start, end, offset, packetHeader; + + if (type === 43 || type === 45) { // + + end = this._packetEndOffset() - 1; + start = this._offset; - for (i = 0; i < len; i += 1) { - tmp += String.fromCharCode(buf[i]); - } + this._offset = end + 2; - return tmp; -} + if (this.options.return_buffers) { + return this._buffer.slice(start, end); + } else { + return this._buffer.slice(start, end).toString(this._encoding); + } + } else if (type === 58) { // : + end = this._packetEndOffset() - 1; + start = this._offset; -// Reset parser to it's original state. -RedisReplyParser.prototype.reset = function () { - this.return_buffer = new Buffer(16384); // for holding replies, might grow - this.return_string = ""; - this.tmp_string = ""; // for holding size fields - - this.multi_bulk_length = 0; - this.multi_bulk_replies = null; - this.multi_bulk_pos = 0; - this.multi_bulk_nested_length = 0; - this.multi_bulk_nested_replies = null; - - this.states = { - TYPE: 1, - SINGLE_LINE: 2, - MULTI_BULK_COUNT: 3, - INTEGER_LINE: 4, - BULK_LENGTH: 5, - ERROR_LINE: 6, - BULK_DATA: 7, - UNKNOWN_TYPE: 8, - FINAL_CR: 9, - FINAL_LF: 10, - MULTI_BULK_COUNT_LF: 11, - BULK_LF: 12 - }; - - this.state = this.states.TYPE; -}; + this._offset = end + 2; -RedisReplyParser.prototype.parser_error = function (message) { - this.emit("error", message); - this.reset(); + return +this._buffer.toString(this._encoding, start, end); + } else if (type === 36) { // $ + offset = this._offset - 1; + + packetHeader = new Packet(type, this.parseHeader()); + + if (packetHeader.size === null) { + this._offset++; + + return null; + } + + if (packetHeader.size === -1) { + return null; + } + + end = this._offset + packetHeader.size; + start = this._offset; + + this._offset = end + 2; + + if (end > this._buffer.length) { + this._offset = offset; + return null; + } + + if (this.options.return_buffers) { + return this._buffer.slice(start, end); + } else { + return this._buffer.slice(start, end).toString(this._encoding); + } + } else if (type === 42) { // * + offset = this._offset; + packetHeader = new Packet(type, this.parseHeader()); + + if (packetHeader.size > this._bytesRemaining()) { + this._offset = offset - 1; + return -1; + } + + if (packetHeader.size < 0) { + this._offset += 2; + return null; + } + + var reply = [ ]; + offset = this._offset - 1; + + for (var i = 0; i < packetHeader.size; i++) { + var ntype = this._buffer[this._offset++]; + + if (this._offset === this._buffer.length) { + throw new Error('too far'); + } + reply.push(this._parseResult(ntype)); + } + + return reply; + } }; -RedisReplyParser.prototype.execute = function (incoming_buf) { - var pos = 0, bd_tmp, bd_str, i, il, states = this.states; - //, state_times = {}, start_execute = new Date(), start_switch, end_switch, old_state; - //start_switch = new Date(); - - while (pos < incoming_buf.length) { - // old_state = this.state; - // console.log("execute: " + this.state + ", " + pos + "/" + incoming_buf.length + ", " + String.fromCharCode(incoming_buf[pos])); - - switch (this.state) { - case 1: // states.TYPE - this.type = incoming_buf[pos]; - pos += 1; - - switch (this.type) { - case 43: // + - this.state = states.SINGLE_LINE; - this.return_buffer.end = 0; - this.return_string = ""; - break; - case 42: // * - this.state = states.MULTI_BULK_COUNT; - this.tmp_string = ""; - break; - case 58: // : - this.state = states.INTEGER_LINE; - this.return_buffer.end = 0; - this.return_string = ""; - break; - case 36: // $ - this.state = states.BULK_LENGTH; - this.tmp_string = ""; - break; - case 45: // - - this.state = states.ERROR_LINE; - this.return_buffer.end = 0; - this.return_string = ""; +FasterReplyParser.prototype.execute = function (buffer) { + this.append(buffer); + + while (true) { + var offset = this._offset; + try { + var ret; + + // at least 4 bytes: *1\r\n + if (this._bytesRemaining() < 4) { break; - default: - this.state = states.UNKNOWN_TYPE; } - break; - case 4: // states.INTEGER_LINE - if (incoming_buf[pos] === 13) { - this.send_reply(+small_toString(this.return_buffer, this.return_buffer.end)); - this.state = states.FINAL_LF; - } else { - this.return_buffer[this.return_buffer.end] = incoming_buf[pos]; - this.return_buffer.end += 1; - } - pos += 1; - break; - case 6: // states.ERROR_LINE - if (incoming_buf[pos] === 13) { - this.send_error(this.return_buffer.toString("ascii", 0, this.return_buffer.end)); - this.state = states.FINAL_LF; - } else { - this.return_buffer[this.return_buffer.end] = incoming_buf[pos]; - this.return_buffer.end += 1; - } - pos += 1; - break; - case 2: // states.SINGLE_LINE - if (incoming_buf[pos] === 13) { - this.send_reply(this.return_string); - this.state = states.FINAL_LF; - } else { - this.return_string += String.fromCharCode(incoming_buf[pos]); - } - pos += 1; - break; - case 3: // states.MULTI_BULK_COUNT - if (incoming_buf[pos] === 13) { // \r - this.state = states.MULTI_BULK_COUNT_LF; - } else { - this.tmp_string += String.fromCharCode(incoming_buf[pos]); - } - pos += 1; - break; - case 11: // states.MULTI_BULK_COUNT_LF - if (incoming_buf[pos] === 10) { // \n - if (this.multi_bulk_length) { // nested multi-bulk - this.multi_bulk_nested_length = this.multi_bulk_length; - this.multi_bulk_nested_replies = this.multi_bulk_replies; - this.multi_bulk_nested_pos = this.multi_bulk_pos; - } - this.multi_bulk_length = +this.tmp_string; - this.multi_bulk_pos = 0; - this.state = states.TYPE; - if (this.multi_bulk_length < 0) { - this.send_reply(null); - this.multi_bulk_length = 0; - } else if (this.multi_bulk_length === 0) { - this.multi_bulk_pos = 0; - this.multi_bulk_replies = null; - this.send_reply([]); - } else { - this.multi_bulk_replies = new Array(this.multi_bulk_length); + + var type = this._buffer[this._offset++]; + + if (type === 43) { // + + ret = this._parseResult(type); + this.send_reply(ret); + } else if (type === 45) { + ret = this._parseResult(type); + this.send_error(ret); + } else if (type === 58) { // : + ret = this._parseResult(type); + this.send_reply(+ret); + } else if (type === 36) { // $ + ret = this._parseResult(type); + + if (ret === null) { + break; } - } else { - this.parser_error(new Error("didn't see LF after NL reading multi bulk count")); - return; - } - pos += 1; - break; - case 5: // states.BULK_LENGTH - if (incoming_buf[pos] === 13) { // \r - this.state = states.BULK_LF; - } else { - this.tmp_string += String.fromCharCode(incoming_buf[pos]); - } - pos += 1; - break; - case 12: // states.BULK_LF - if (incoming_buf[pos] === 10) { // \n - this.bulk_length = +this.tmp_string; - if (this.bulk_length === -1) { - this.send_reply(null); - this.state = states.TYPE; - } else if (this.bulk_length === 0) { - this.send_reply(new Buffer("")); - this.state = states.FINAL_CR; - } else { - this.state = states.BULK_DATA; - if (this.bulk_length > this.return_buffer.length) { - if (exports.debug_mode) { - console.log("Growing return_buffer from " + this.return_buffer.length + " to " + this.bulk_length); - } - this.return_buffer = new Buffer(this.bulk_length); - } - this.return_buffer.end = 0; + this.send_reply(ret); + } else if (type === 42) { // * + offset = this._offset - 1; + ret = this._parseResult(type); + if (ret === -1) { + this._offset = offset; + break; } - } else { - this.parser_error(new Error("didn't see LF after NL while reading bulk length")); - return; - } - pos += 1; - break; - case 7: // states.BULK_DATA - this.return_buffer[this.return_buffer.end] = incoming_buf[pos]; - this.return_buffer.end += 1; - pos += 1; - if (this.return_buffer.end === this.bulk_length) { - bd_tmp = new Buffer(this.bulk_length); - // When the response is small, Buffer.copy() is a lot slower. - if (this.bulk_length > 10) { - this.return_buffer.copy(bd_tmp, 0, 0, this.bulk_length); - } else { - for (i = 0, il = this.bulk_length; i < il; i += 1) { - bd_tmp[i] = this.return_buffer[i]; - } - } - this.send_reply(bd_tmp); - this.state = states.FINAL_CR; - } - break; - case 9: // states.FINAL_CR - if (incoming_buf[pos] === 13) { // \r - this.state = states.FINAL_LF; - pos += 1; - } else { - this.parser_error(new Error("saw " + incoming_buf[pos] + " when expecting final CR")); - return; - } - break; - case 10: // states.FINAL_LF - if (incoming_buf[pos] === 10) { // \n - this.state = states.TYPE; - pos += 1; - } else { - this.parser_error(new Error("saw " + incoming_buf[pos] + " when expecting final LF")); - return; + + this.send_reply(ret); } + } catch(err) { + this._offset = offset; break; - default: - this.parser_error(new Error("invalid state " + this.state)); } - // end_switch = new Date(); - // if (state_times[old_state] === undefined) { - // state_times[old_state] = 0; - // } - // state_times[old_state] += (end_switch - start_switch); - // start_switch = end_switch; } - // console.log("execute ran for " + (Date.now() - start_execute) + " ms, on " + incoming_buf.length + " Bytes. "); - // Object.keys(state_times).forEach(function (state) { - // console.log(" " + state + ": " + state_times[state]); - // }); }; -RedisReplyParser.prototype.send_error = function (reply) { - if (this.multi_bulk_length > 0 || this.multi_bulk_nested_length > 0) { - // TODO - can this happen? Seems like maybe not. - this.add_multi_bulk_reply(reply); - } else { - this.emit("reply error", reply); +FasterReplyParser.prototype.append = function(newBuffer) { + if (!newBuffer) { + return; } -}; -RedisReplyParser.prototype.send_reply = function (reply) { - if (this.multi_bulk_length > 0 || this.multi_bulk_nested_length > 0) { - if (!this.options.return_buffers && Buffer.isBuffer(reply)) { - this.add_multi_bulk_reply(reply.toString("utf8")); - } else { - this.add_multi_bulk_reply(reply); - } - } else { - if (!this.options.return_buffers && Buffer.isBuffer(reply)) { - this.emit("reply", reply.toString("utf8")); - } else { - this.emit("reply", reply); - } + var oldBuffer = this._buffer; + if (!oldBuffer) { + this._buffer = newBuffer; + + return; } -}; -RedisReplyParser.prototype.add_multi_bulk_reply = function (reply) { - if (this.multi_bulk_replies) { - this.multi_bulk_replies[this.multi_bulk_pos] = reply; - this.multi_bulk_pos += 1; - if (this.multi_bulk_pos < this.multi_bulk_length) { - return; - } - } else { - this.multi_bulk_replies = reply; + var bytesRemaining = this._bytesRemaining(); + + var newLength = bytesRemaining + newBuffer.length; + + if (bytesRemaining === 0) { + this._buffer = newBuffer; + this._offset = 0; + + return; } - if (this.multi_bulk_nested_length > 0) { - this.multi_bulk_nested_replies[this.multi_bulk_nested_pos] = this.multi_bulk_replies; - this.multi_bulk_nested_pos += 1; + this._buffer = Buffer.concat([this._buffer.slice(this._offset), newBuffer]); + + this._offset = 0; +}; + +FasterReplyParser.prototype.parseHeader = function () { + var end = this._packetEndOffset(), + value = this._buffer.toString(this._encoding, this._offset, end - 1); + + this._offset = end + 1; + + return value; +}; + +FasterReplyParser.prototype.parseBuffer = function(length) { + var buffer = this._buffer.slice(this._offset, this._offset + length); - this.multi_bulk_length = 0; - this.multi_bulk_replies = null; - this.multi_bulk_pos = 0; + this._offset += length; + return buffer; +}; + +FasterReplyParser.prototype._packetEndOffset = function () { + var offset = this._offset; - if (this.multi_bulk_nested_length === this.multi_bulk_nested_pos) { - this.emit("reply", this.multi_bulk_nested_replies); - this.multi_bulk_nested_length = 0; - this.multi_bulk_nested_pos = 0; - this.multi_bulk_nested_replies = null; + while (this._buffer[offset] !== 0x0d && this._buffer[offset + 1] !== 0x0a) { + offset++; + + if (offset >= this._buffer.length) { + throw new Error("didn't see LF after NL reading multi bulk count (" + offset + " => " + this._buffer.length + ", " + this._offset + ")"); } - } else { - this.emit("reply", this.multi_bulk_replies); - this.multi_bulk_length = 0; - this.multi_bulk_replies = null; - this.multi_bulk_pos = 0; } + + offset++; + return offset; }; + +FasterReplyParser.prototype._bytesRemaining = function() { + return (this._buffer.length - this._offset) < 0 ? 0 : (this._buffer.length - this._offset); +}; + +FasterReplyParser.prototype.parser_error = function (message) { + this.emit("error", message); +}; + +FasterReplyParser.prototype.send_error = function (reply) { + this.emit("reply error", reply); +}; + +FasterReplyParser.prototype.send_reply = function (reply) { + this.emit("reply", reply); +}; \ No newline at end of file From a5ea716ccb7bba4a44eab532c8b248840419c287 Mon Sep 17 00:00:00 2001 From: DTrejo Date: Sat, 7 Jul 2012 16:32:09 -0700 Subject: [PATCH 0039/1748] readme: add more contributors --- README.md | 65 ++++++++++++++++++++++++++++++------------------------- 1 file changed, 36 insertions(+), 29 deletions(-) diff --git a/README.md b/README.md index 43a742b26a6..3cf5a9390f1 100644 --- a/README.md +++ b/README.md @@ -595,37 +595,44 @@ I think there are more performance improvements left in there for smaller values - open a pull request! ## Contributors - Some people have have added features and fixed bugs in `node_redis` other than me. -In alphabetical order, they are: - -* [Aivo Paas](https://github.com/aivopaas) -* [Andy Ray](https://github.com/DelvarWorld) -* Daniele -* [Dave Hoover](https://github.com/redsquirrel) -* [David Trejo](https://github.com/DTrejo) -* Dayananda Nanjundappa -* [Felix Geisendörfer](https://github.com/felixge) -* [Hank Sims](https://github.com/hanksims) -* [Ian Babrou](https://github.com/bobrik) -* [Isaac Z. Schlueter](https://github.com/isaacs) -* [Louis-Philippe Perron](https://github.com/lp) -* [Maksim Lin](https://github.com/maks) -* [Marcus Westin](https://github.com/marcuswestin) -* [Mark Dawson](https://github.com/markdaws) -* [Nithesh Chandra Gupta Mittapally](https://github.com/nithesh) -* [Orion Henry](https://github.com/orionz) -* [Owen Smith](https://github.com/orls) -* [Paul Carey](https://github.com/paulcarey) -* [Philip Tellis](https://github.com/bluesmoon) -* [Pieter Noordhuis](https://github.com/pietern) -* [Rick Olson](https://github.com/technoweenie) -* [Tim Smart](https://github.com/Tim-Smart) -* [TJ Holowaychuk](https://github.com/visionmedia) -* [Umair Siddique](https://github.com/umairsiddique) -* [Vladimir Dronnikov](https://github.com/dvv) -* [Zachary Scott](https://github.com/zzak) +Ordered by date of first contribution. +[Auto-generated](http://github.com/dtrejo/node-authors) on Sat Jul 07 2012 16:11:04 GMT-0700 (PDT). + +- [Matt Ranney aka `mranney`](https://github.com/mranney) +- [Tim-Smart aka `tim-smart`](https://github.com/tim-smart) +- [Tj Holowaychuk aka `visionmedia`](https://github.com/visionmedia) +- [rick aka `technoweenie`](https://github.com/technoweenie) +- [Orion Henry aka `orionz`](https://github.com/orionz) +- [Aivo Paas aka `aivopaas`](https://github.com/aivopaas) +- [Hank Sims aka `hanksims`](https://github.com/hanksims) +- [Paul Carey aka `paulcarey`](https://github.com/paulcarey) +- [Pieter Noordhuis aka `pietern`](https://github.com/pietern) +- [nithesh aka `nithesh`](https://github.com/nithesh) +- [Andy Ray aka `andy2ray`](https://github.com/andy2ray) +- [unknown aka `unknowdna`](https://github.com/unknowdna) +- [Dave Hoover aka `redsquirrel`](https://github.com/redsquirrel) +- [Vladimir Dronnikov aka `dvv`](https://github.com/dvv) +- [Umair Siddique aka `umairsiddique`](https://github.com/umairsiddique) +- [Louis-Philippe Perron aka `lp`](https://github.com/lp) +- [Mark Dawson aka `markdaws`](https://github.com/markdaws) +- [Ian Babrou aka `bobrik`](https://github.com/bobrik) +- [Felix Geisendörfer aka `felixge`](https://github.com/felixge) +- [Jean-Hugues Pinson aka `undefined`](https://github.com/undefined) +- [Maksim Lin aka `maks`](https://github.com/maks) +- [Owen Smith aka `orls`](https://github.com/orls) +- [Zachary Scott aka `zzak`](https://github.com/zzak) +- [TEHEK Firefox aka `TEHEK`](https://github.com/TEHEK) +- [Isaac Z. Schlueter aka `isaacs`](https://github.com/isaacs) +- [David Trejo aka `DTrejo`](https://github.com/DTrejo) +- [Brian Noguchi aka `bnoguchi`](https://github.com/bnoguchi) +- [Philip Tellis aka `bluesmoon`](https://github.com/bluesmoon) +- [Marcus Westin aka `marcuswestin2`](https://github.com/marcuswestin2) +- [Jed Schmidt aka `jed`](https://github.com/jed) +- [Dave Peticolas aka `jdavisp3`](https://github.com/jdavisp3) +- [Trae Robrock aka `trobrock`](https://github.com/trobrock) +- [Shankar Karuppiah aka `shankar0306`](https://github.com/shankar0306) Thanks. From 4048349115ec94745d734f84eb79fb5dc3e5c53d Mon Sep 17 00:00:00 2001 From: Jerry Sievert Date: Sun, 8 Jul 2012 05:16:36 -0700 Subject: [PATCH 0040/1748] some cleanup and comments --- lib/parser/javascript.js | 53 ++++++++++++++++++---------------------- 1 file changed, 24 insertions(+), 29 deletions(-) diff --git a/lib/parser/javascript.js b/lib/parser/javascript.js index bf67573e9f8..01c66fe3398 100644 --- a/lib/parser/javascript.js +++ b/lib/parser/javascript.js @@ -27,42 +27,46 @@ exports.Parser = FasterReplyParser; FasterReplyParser.prototype._parseResult = function (type) { var start, end, offset, packetHeader; - if (type === 43 || type === 45) { // + + if (type === 43 || type === 45) { // + or - + // up to the delimiter end = this._packetEndOffset() - 1; start = this._offset; + // include the delimiter this._offset = end + 2; if (this.options.return_buffers) { return this._buffer.slice(start, end); } else { - return this._buffer.slice(start, end).toString(this._encoding); + return this._buffer.toString(this._encoding, start, end); } } else if (type === 58) { // : + // up to the delimiter end = this._packetEndOffset() - 1; start = this._offset; + // include the delimiter this._offset = end + 2; + // return the coerced numeric value return +this._buffer.toString(this._encoding, start, end); } else if (type === 36) { // $ + // set a rewind point, as the packet could be larger than the + // buffer in memory offset = this._offset - 1; packetHeader = new Packet(type, this.parseHeader()); - if (packetHeader.size === null) { - this._offset++; - - return null; - } - + // packets with a size of -1 are considered null if (packetHeader.size === -1) { return null; } + end = this._offset + packetHeader.size; start = this._offset; + // set the offset to after the delimiter this._offset = end + 2; if (end > this._buffer.length) { @@ -73,7 +77,7 @@ FasterReplyParser.prototype._parseResult = function (type) { if (this.options.return_buffers) { return this._buffer.slice(start, end); } else { - return this._buffer.slice(start, end).toString(this._encoding); + return this._buffer.toString(this._encoding, start, end); } } else if (type === 42) { // * offset = this._offset; @@ -113,7 +117,7 @@ FasterReplyParser.prototype.execute = function (buffer) { try { var ret; - // at least 4 bytes: *1\r\n + // at least 4 bytes: :1\r\n if (this._bytesRemaining() < 4) { break; } @@ -123,7 +127,7 @@ FasterReplyParser.prototype.execute = function (buffer) { if (type === 43) { // + ret = this._parseResult(type); this.send_reply(ret); - } else if (type === 45) { + } else if (type === 45) { // - ret = this._parseResult(type); this.send_error(ret); } else if (type === 58) { // : @@ -132,11 +136,10 @@ FasterReplyParser.prototype.execute = function (buffer) { } else if (type === 36) { // $ ret = this._parseResult(type); - if (ret === null) { - break; - } this.send_reply(ret); } else if (type === 42) { // * + // set a rewind point. if a failure occurs, + // wait for the next execute()/append() and try again offset = this._offset - 1; ret = this._parseResult(type); if (ret === -1) { @@ -147,6 +150,8 @@ FasterReplyParser.prototype.execute = function (buffer) { this.send_reply(ret); } } catch(err) { + // catch the error (not enough data), rewind, and wait + // for the next packet to appear this._offset = offset; break; } @@ -158,26 +163,23 @@ FasterReplyParser.prototype.append = function(newBuffer) { return; } - var oldBuffer = this._buffer; - if (!oldBuffer) { + // first run + if (this._buffer === null) { this._buffer = newBuffer; return; } - var bytesRemaining = this._bytesRemaining(); - - var newLength = bytesRemaining + newBuffer.length; - - if (bytesRemaining === 0) { + // out of data + if (this._offset >= this._buffer.length) { this._buffer = newBuffer; this._offset = 0; return; } + // very large packet this._buffer = Buffer.concat([this._buffer.slice(this._offset), newBuffer]); - this._offset = 0; }; @@ -190,13 +192,6 @@ FasterReplyParser.prototype.parseHeader = function () { return value; }; -FasterReplyParser.prototype.parseBuffer = function(length) { - var buffer = this._buffer.slice(this._offset, this._offset + length); - - this._offset += length; - return buffer; -}; - FasterReplyParser.prototype._packetEndOffset = function () { var offset = this._offset; From 8d0f2e72393d112093ec2284b20456485e2016b7 Mon Sep 17 00:00:00 2001 From: Jerry Sievert Date: Sun, 8 Jul 2012 08:32:33 -0700 Subject: [PATCH 0041/1748] some microbenchmark updates, slight speed improvement --- lib/parser/javascript.js | 23 +++++++++++++++++++---- 1 file changed, 19 insertions(+), 4 deletions(-) diff --git a/lib/parser/javascript.js b/lib/parser/javascript.js index 01c66fe3398..2fe310b8da4 100644 --- a/lib/parser/javascript.js +++ b/lib/parser/javascript.js @@ -24,6 +24,17 @@ util.inherits(FasterReplyParser, events.EventEmitter); exports.Parser = FasterReplyParser; +// Buffer.toString() is quite slow for small strings +function small_toString(buf, start, end) { + var tmp = "", i; + + for (i = start; i < end; i++) { + tmp += String.fromCharCode(buf[i]); + } + + return tmp; +} + FasterReplyParser.prototype._parseResult = function (type) { var start, end, offset, packetHeader; @@ -38,7 +49,11 @@ FasterReplyParser.prototype._parseResult = function (type) { if (this.options.return_buffers) { return this._buffer.slice(start, end); } else { - return this._buffer.toString(this._encoding, start, end); + if (end - start < 65536) { // completely arbitrary + return small_toString(this._buffer, start, end); + } else { + return this._buffer.toString(this._encoding, start, end); + } } } else if (type === 58) { // : // up to the delimiter @@ -49,7 +64,7 @@ FasterReplyParser.prototype._parseResult = function (type) { this._offset = end + 2; // return the coerced numeric value - return +this._buffer.toString(this._encoding, start, end); + return +small_toString(this._buffer, start, end); } else if (type === 36) { // $ // set a rewind point, as the packet could be larger than the // buffer in memory @@ -185,8 +200,8 @@ FasterReplyParser.prototype.append = function(newBuffer) { FasterReplyParser.prototype.parseHeader = function () { var end = this._packetEndOffset(), - value = this._buffer.toString(this._encoding, this._offset, end - 1); - + value = small_toString(this._buffer, this._offset, end - 1); + this._offset = end + 1; return value; From 34568a4bb39ec6d566f46796f1852d78987eb2a8 Mon Sep 17 00:00:00 2001 From: Jerry Sievert Date: Sun, 8 Jul 2012 14:54:48 -0700 Subject: [PATCH 0042/1748] added fallback if missing Buffer.concat() --- lib/parser/javascript.js | 14 +++++++++++++- 1 file changed, 13 insertions(+), 1 deletion(-) diff --git a/lib/parser/javascript.js b/lib/parser/javascript.js index 2fe310b8da4..1c1c41064c7 100644 --- a/lib/parser/javascript.js +++ b/lib/parser/javascript.js @@ -194,7 +194,19 @@ FasterReplyParser.prototype.append = function(newBuffer) { } // very large packet - this._buffer = Buffer.concat([this._buffer.slice(this._offset), newBuffer]); + // check for concat, if we have it, use it + if (Buffer.concat !== undefined) { + this._buffer = Buffer.concat([this._buffer.slice(this._offset), newBuffer]); + } else { + var remaining = this._bytesRemaining(); + var newLength = remaining + newBuffer.length; + var tmpBuffer = new Buffer(newLength); + + this._buffer.copy(tmpBuffer, 0, this._offset); + newBuffer.copy(tmpBuffer, remaining, 0); + + this._buffer = tmpBuffer; + } this._offset = 0; }; From 50914baa7f3a3fcc7203e30bfb5b201bc82ffbfa Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Ignacio=20Burgue=C3=B1o?= Date: Fri, 6 Jul 2012 16:19:25 -0300 Subject: [PATCH 0043/1748] Save and clear client state. Restore it after the connection is ready. This change stores the connection state regarding subscriptions, selected db and monitoring. When the connection to Redis drops, the state is reestablished after a succesful reconnect. Fixes #241. Fixes #210. Signed-off-by: DTrejo --- index.js | 33 ++++++++++++++++++++++++++++++- test.js | 60 ++++++++++++++++++++++++++++++++++++++++++++++++++++++++ 2 files changed, 92 insertions(+), 1 deletion(-) diff --git a/index.js b/index.js index bd8c16a2ef8..77bda9779d2 100644 --- a/index.js +++ b/index.js @@ -66,6 +66,8 @@ function RedisClient(stream, options) { this.parser_module = null; this.selected_db = null; // save the selected db here, used when reconnecting + this.old_state = null; + var self = this; this.stream.on("connect", function () { @@ -272,18 +274,35 @@ RedisClient.prototype.on_ready = function () { this.ready = true; + if (this.old_state !== null) { + this.monitoring = this.old_state.monitoring; + this.pub_sub_mode = this.old_state.pub_sub_mode; + this.selected_db = this.old_state.selected_db; + this.old_state = null; + } + // magically restore any modal commands from a previous connection if (this.selected_db !== null) { this.send_command('select', [this.selected_db]); } if (this.pub_sub_mode === true) { + // only emit "ready" when all subscriptions were made again + var callback_count = 0; + var callback = function() { + callback_count--; + if (callback_count == 0) { + self.emit("ready"); + } + } Object.keys(this.subscription_set).forEach(function (key) { var parts = key.split(" "); if (exports.debug_mode) { console.warn("sending pub/sub on_ready " + parts[0] + ", " + parts[1]); } - self.send_command(parts[0], [parts[1]]); + callback_count++; + self.send_command(parts[0] + "scribe", [parts[1]], callback); }); + return; } else if (this.monitoring) { this.send_command("monitor"); } else { @@ -382,6 +401,18 @@ RedisClient.prototype.connection_gone = function (why) { this.connected = false; this.ready = false; + if (this.old_state === null) { + var state = { + monitoring: this.monitoring, + pub_sub_mode: this.pub_sub_mode, + selected_db: this.selected_db + }; + this.old_state = state; + this.monitoring = false; + this.pub_sub_mode = false; + this.selected_db = null; + } + // since we are collapsing end and close, users don't expect to be called twice if (! this.emitted_end) { this.emit("end"); diff --git a/test.js b/test.js index f6f7f0e3be0..f85050cb6bb 100644 --- a/test.js +++ b/test.js @@ -619,6 +619,66 @@ tests.SUBSCRIBE_QUIT = function () { client3.subscribe("chan3"); }; +tests.SUBSCRIBE_CLOSE_RESUBSCRIBE = function () { + var name = "SUBSCRIBE_CLOSE_RESUBSCRIBE"; + var c1 = redis.createClient(); + var c2 = redis.createClient(); + var count = 0; + + /* Create two clients. c1 subscribes to two channels, c2 will publish to them. + c2 publishes the first message. + c1 gets the message and drops its connection. It must resubscribe itself. + When it resubscribes, c2 publishes the second message, on the same channel + c1 gets the message and drops its connection. It must resubscribe itself, again. + When it resubscribes, c2 publishes the third message, on the second channel + c1 gets the message and drops its connection. When it reconnects, the test ends. + */ + + c1.on("message", function(channel, message) { + if (channel === "chan1") { + assert.strictEqual(message, "hi on channel 1"); + c1.stream.end(); + + } else if (channel === "chan2") { + assert.strictEqual(message, "hi on channel 2"); + c1.stream.end(); + + } else { + c1.quit(); + c2.quit(); + assert.fail("test failed"); + } + }) + + c1.subscribe("chan1", "chan2"); + + c2.once("ready", function() { + console.log("c2 is ready"); + c1.on("ready", function(err, results) { + console.log("c1 is ready", count); + + count++; + if (count == 1) { + c2.publish("chan1", "hi on channel 1"); + return; + + } else if (count == 2) { + c2.publish("chan2", "hi on channel 2"); + + } else { + c1.quit(function() { + c2.quit(function() { + next(name); + }); + }); + } + }); + + c2.publish("chan1", "hi on channel 1"); + + }); +}; + tests.EXISTS = function () { var name = "EXISTS"; client.del("foo", "foo2", require_number_any(name)); From f120cbd3029d6335fd8f76ed6a37bf4f6827c9e3 Mon Sep 17 00:00:00 2001 From: DTrejo Date: Thu, 12 Jul 2012 17:37:48 -0700 Subject: [PATCH 0044/1748] diff_multi_bench_output.js for easy benchmark comparison --- diff_multi_bench_output.js | 87 ++++++++++++++++++++++++++++++++++++++ 1 file changed, 87 insertions(+) create mode 100755 diff_multi_bench_output.js diff --git a/diff_multi_bench_output.js b/diff_multi_bench_output.js new file mode 100755 index 00000000000..99fdf4df6e3 --- /dev/null +++ b/diff_multi_bench_output.js @@ -0,0 +1,87 @@ +#!/usr/bin/env node + +var colors = require('colors'), + fs = require('fs'), + _ = require('underscore'), + metrics = require('metrics'), + + // `node diff_multi_bench_output.js before.txt after.txt` + before = process.argv[2], + after = process.argv[3]; + +if (!before || !after) { + console.log('Please supply two file arguments:'); + var n = __filename; + n = n.substring(n.lastIndexOf('/', n.length)); + console.log(' ./' + n + ' multiBenchBefore.txt multiBenchAfter.txt'); + console.log('To generate multiBenchBefore.txt, run'); + console.log(' node multi_bench.js > multiBenchBefore.txt'); + console.log('Thank you for benchmarking responsibly.'); + return; +} + +var before_lines = fs.readFileSync(before, 'utf8').split('\n'), + after_lines = fs.readFileSync(after, 'utf8').split('\n'); + +console.log('Comparing before,', before.green, '(', before_lines.length, + 'lines)', 'to after,', after.green, '(', after_lines.length, 'lines)'); + +var total_ops = new metrics.Histogram.createUniformHistogram(); + +before_lines.forEach(function(b, i) { + var a = after_lines[i]; + if (!a || !b || !b.trim() || !a.trim()) { + // console.log('#ignored#', '>'+a+'<', '>'+b+'<'); + return; + } + + b_words = b.split(' ').filter(is_whitespace); + a_words = a.split(' ').filter(is_whitespace); + + var ops = + [b_words, a_words] + .map(function(words) { + // console.log(words); + return parseInt10(words.slice(-2, -1)); + }).filter(function(num) { + var isNaN = !num && num !== 0; + return !isNaN; + }); + if (ops.length != 2) return + + var delta = ops[1] - ops[0]; + + total_ops.update(delta); + + delta = humanize_diff(delta); + console.log( + // name of test + command_name(a_words) == command_name(b_words) + ? command_name(a_words) + ':' + : '404:', + // results of test + ops.join(' -> '), 'ops/sec (∆', delta, ')'); +}); + +console.log('Mean difference in ops/sec:', humanize_diff(total_ops.mean())); + +function is_whitespace(s) { + return !!s.trim(); +} + +function parseInt10(s) { + return parseInt(s, 10); +} + +// green if greater than 0, red otherwise +function humanize_diff(num) { + if (num > 0) { + return ('+' + num).green; + } + return ('' + num).red; +} + +function command_name(words) { + var line = words.join(' '); + return line.substr(0, line.indexOf(',')); +} From 49c73bac304c89044e539bffae2a256627e8d0c6 Mon Sep 17 00:00:00 2001 From: DTrejo Date: Thu, 12 Jul 2012 17:56:43 -0700 Subject: [PATCH 0045/1748] readme: please include bench results on iffy pull requests --- README.md | 10 ++++++++++ 1 file changed, 10 insertions(+) diff --git a/README.md b/README.md index 3cf5a9390f1..6a06df78587 100644 --- a/README.md +++ b/README.md @@ -592,6 +592,16 @@ I think there are more performance improvements left in there for smaller values - your code should be the same style as the rest of the code - add your tests to `./test.js` - add your documentation to `README.md` (if needed). +- if you think your change will make node_redis slower, run this: + + git checkout master + node multi_bench.js > before.txt + git checkout branch-with-your-change + node multi_bench.js > after.txt + ./diff_multi_bench_output.js before.txt after.txt + + and please attach a screenshot of the output (if it's not faster I recommend + trying to make it faster!). - open a pull request! ## Contributors From 92264ad5fadf30eedec82584c770d9b2e5a10cc5 Mon Sep 17 00:00:00 2001 From: DTrejo Date: Wed, 25 Jul 2012 18:33:49 -0700 Subject: [PATCH 0046/1748] readme.md: how to ZREVRANGEBYSCORE with LIMIT. fixes #163 --- README.md | 28 ++++++++++++++++++++++++++++ 1 file changed, 28 insertions(+) diff --git a/README.md b/README.md index 6a06df78587..3488979f70e 100644 --- a/README.md +++ b/README.md @@ -119,6 +119,34 @@ Minimal parsing is done on the replies. Commands that return a single line repl integer replies return JavaScript Numbers, "bulk" replies return node Buffers, and "multi bulk" replies return a JavaScript Array of node Buffers. `HGETALL` returns an Object with Buffers keyed by the hash keys. +### Commands with Optional and Keyword arguments + +This applies to anything that uses an optional `[WITHSCORES]` or `[LIMIT offset count]` in the [redis.io/commands](http://redis.io/commands) documentation. + +Example: +```js +var args = [ 'myzset', 1, 'one', 2, 'two', 3, 'three', 99, 'ninety-nine' ]; +client.zadd(args, function (err, response) { + if (err) throw err; + console.log('added '+response+' items.'); + + // -Infinity and +Infinity also work + var args1 = [ 'myzset', '+inf', '-inf' ]; + client.zrevrangebyscore(args1, function (err, response) { + if (err) throw err; + console.log('example1', response); + // write your code here + }); + + var max = 3, min = 1, offset = 1, count = 2; + var args2 = [ 'myzset', max, min, 'WITHSCORES', 'LIMIT', offset, count ]; + client.zrevrangebyscore(args2, function (err, response) { + if (err) throw err; + console.log('example2', response); + // write your code here + }); +}); + # API ## Connection Events From 949ecdff311c4b3e6e7ba7b6635a2d3970286f15 Mon Sep 17 00:00:00 2001 From: DTrejo Date: Wed, 25 Jul 2012 19:16:07 -0700 Subject: [PATCH 0047/1748] readme.md: add ignacio as contrib --- README.md | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/README.md b/README.md index 3488979f70e..62783c7c759 100644 --- a/README.md +++ b/README.md @@ -636,7 +636,7 @@ I think there are more performance improvements left in there for smaller values Some people have have added features and fixed bugs in `node_redis` other than me. Ordered by date of first contribution. -[Auto-generated](http://github.com/dtrejo/node-authors) on Sat Jul 07 2012 16:11:04 GMT-0700 (PDT). +[Auto-generated](http://github.com/dtrejo/node-authors) on Wed Jul 25 2012 19:14:59 GMT-0700 (PDT). - [Matt Ranney aka `mranney`](https://github.com/mranney) - [Tim-Smart aka `tim-smart`](https://github.com/tim-smart) @@ -671,6 +671,7 @@ Ordered by date of first contribution. - [Dave Peticolas aka `jdavisp3`](https://github.com/jdavisp3) - [Trae Robrock aka `trobrock`](https://github.com/trobrock) - [Shankar Karuppiah aka `shankar0306`](https://github.com/shankar0306) +- [Ignacio Burgueño aka `ignacio`](https://github.com/ignacio) Thanks. From a042258794b70993039a36b4c7ef9c61cb874bf8 Mon Sep 17 00:00:00 2001 From: DTrejo Date: Thu, 26 Jul 2012 10:52:03 -0700 Subject: [PATCH 0048/1748] readme.md: reorder --- README.md | 56 +++++++++++++++++++++++++++---------------------------- 1 file changed, 28 insertions(+), 28 deletions(-) diff --git a/README.md b/README.md index 62783c7c759..eac0a88d46f 100644 --- a/README.md +++ b/README.md @@ -119,34 +119,6 @@ Minimal parsing is done on the replies. Commands that return a single line repl integer replies return JavaScript Numbers, "bulk" replies return node Buffers, and "multi bulk" replies return a JavaScript Array of node Buffers. `HGETALL` returns an Object with Buffers keyed by the hash keys. -### Commands with Optional and Keyword arguments - -This applies to anything that uses an optional `[WITHSCORES]` or `[LIMIT offset count]` in the [redis.io/commands](http://redis.io/commands) documentation. - -Example: -```js -var args = [ 'myzset', 1, 'one', 2, 'two', 3, 'three', 99, 'ninety-nine' ]; -client.zadd(args, function (err, response) { - if (err) throw err; - console.log('added '+response+' items.'); - - // -Infinity and +Infinity also work - var args1 = [ 'myzset', '+inf', '-inf' ]; - client.zrevrangebyscore(args1, function (err, response) { - if (err) throw err; - console.log('example1', response); - // write your code here - }); - - var max = 3, min = 1, offset = 1, count = 2; - var args2 = [ 'myzset', max, min, 'WITHSCORES', 'LIMIT', offset, count ]; - client.zrevrangebyscore(args2, function (err, response) { - if (err) throw err; - console.log('example2', response); - // write your code here - }); -}); - # API ## Connection Events @@ -604,6 +576,34 @@ Current delay in milliseconds before a connection retry will be attempted. This Multiplier for future retry timeouts. This should be larger than 1 to add more time between retries. Defaults to 1.7. The default initial connection retry is 250, so the second retry will be 425, followed by 723.5, etc. +### Commands with Optional and Keyword arguments + +This applies to anything that uses an optional `[WITHSCORES]` or `[LIMIT offset count]` in the [redis.io/commands](http://redis.io/commands) documentation. + +Example: +```js +var args = [ 'myzset', 1, 'one', 2, 'two', 3, 'three', 99, 'ninety-nine' ]; +client.zadd(args, function (err, response) { + if (err) throw err; + console.log('added '+response+' items.'); + + // -Infinity and +Infinity also work + var args1 = [ 'myzset', '+inf', '-inf' ]; + client.zrevrangebyscore(args1, function (err, response) { + if (err) throw err; + console.log('example1', response); + // write your code here + }); + + var max = 3, min = 1, offset = 1, count = 2; + var args2 = [ 'myzset', max, min, 'WITHSCORES', 'LIMIT', offset, count ]; + client.zrevrangebyscore(args2, function (err, response) { + if (err) throw err; + console.log('example2', response); + // write your code here + }); +}); +``` ## TODO From 75c38dd8b3a759c9fc2a712fc2c5d7643673797f Mon Sep 17 00:00:00 2001 From: DTrejo Date: Mon, 30 Jul 2012 15:10:41 -0700 Subject: [PATCH 0049/1748] README.md: more indignation --- README.md | 18 +++--------------- 1 file changed, 3 insertions(+), 15 deletions(-) diff --git a/README.md b/README.md index eac0a88d46f..46e7018c4b3 100644 --- a/README.md +++ b/README.md @@ -616,21 +616,9 @@ Performance can be better for very large values. I think there are more performance improvements left in there for smaller values, especially for large lists of small values. ## How to Contribute -- make your changes on a fork -- your code should be the same style as the rest of the code -- add your tests to `./test.js` -- add your documentation to `README.md` (if needed). -- if you think your change will make node_redis slower, run this: - - git checkout master - node multi_bench.js > before.txt - git checkout branch-with-your-change - node multi_bench.js > after.txt - ./diff_multi_bench_output.js before.txt after.txt - - and please attach a screenshot of the output (if it's not faster I recommend - trying to make it faster!). -- open a pull request! +- open a pull request and then wait for feedback (if + [DTrejo](http://github.com/dtrejo) does not get back to you within 2 days, + comment again with indignation!) ## Contributors Some people have have added features and fixed bugs in `node_redis` other than me. From f3ee8eabd890244a6c4d39a84753bafd8ea31133 Mon Sep 17 00:00:00 2001 From: DTrejo Date: Mon, 6 Aug 2012 15:05:25 -0700 Subject: [PATCH 0050/1748] commands.js regenerated --- lib/commands.js | 23 ++++++++++++++++++++++- 1 file changed, 22 insertions(+), 1 deletion(-) diff --git a/lib/commands.js b/lib/commands.js index 0293ae8d37b..f57cca964a5 100644 --- a/lib/commands.js +++ b/lib/commands.js @@ -1,12 +1,16 @@ -// This file was generated by ./generate_commands.js on Tue Jun 28 2011 22:37:02 GMT-0700 (PDT) +// This file was generated by ./generate_commands.js on Mon Aug 06 2012 15:04:06 GMT-0700 (PDT) module.exports = [ "append", "auth", "bgrewriteaof", "bgsave", + "bitcount", + "bitop", "blpop", "brpop", "brpoplpush", + "client kill", + "client list", "config get", "config set", "config resetstat", @@ -17,7 +21,10 @@ module.exports = [ "decrby", "del", "discard", + "dump", "echo", + "eval", + "evalsha", "exec", "exists", "expire", @@ -33,6 +40,7 @@ module.exports = [ "hget", "hgetall", "hincrby", + "hincrbyfloat", "hkeys", "hlen", "hmget", @@ -42,6 +50,7 @@ module.exports = [ "hvals", "incr", "incrby", + "incrbyfloat", "info", "keys", "lastsave", @@ -56,6 +65,7 @@ module.exports = [ "lset", "ltrim", "mget", + "migrate", "monitor", "move", "mset", @@ -63,14 +73,19 @@ module.exports = [ "multi", "object", "persist", + "pexpire", + "pexpireat", "ping", + "psetex", "psubscribe", + "pttl", "publish", "punsubscribe", "quit", "randomkey", "rename", "renamenx", + "restore", "rpop", "rpoplpush", "rpush", @@ -78,6 +93,10 @@ module.exports = [ "sadd", "save", "scard", + "script exists", + "script flush", + "script kill", + "script load", "sdiff", "sdiffstore", "select", @@ -91,6 +110,7 @@ module.exports = [ "sinterstore", "sismember", "slaveof", + "slowlog", "smembers", "smove", "sort", @@ -102,6 +122,7 @@ module.exports = [ "sunion", "sunionstore", "sync", + "time", "ttl", "type", "unsubscribe", From 58804e6e27a0f1d9cf85a1b426c8c0c5e924222b Mon Sep 17 00:00:00 2001 From: DTrejo Date: Mon, 6 Aug 2012 18:19:39 -0700 Subject: [PATCH 0051/1748] test.js: HLEN --- test.js | 18 ++++++++++++++++++ 1 file changed, 18 insertions(+) diff --git a/test.js b/test.js index f85050cb6bb..4e9289762fd 100644 --- a/test.js +++ b/test.js @@ -501,6 +501,24 @@ tests.HSET = function () { client.HSET(key, field2, value2, last(name, require_number(0, name))); }; +tests.HLEN = function () { + var key = "test hash", + field1 = new Buffer("0123456789"), + value1 = new Buffer("abcdefghij"), + field2 = new Buffer(0), + value2 = new Buffer(0), + name = "HSET", + timeout = 1000; + + client.HSET(key, field1, value1, function (err, results) { + client.HLEN(key, function (err, len) { + console.log(results+"sgdshhsshs") + assert.ok(2 === +len); + next(name); + }); + }); +} + tests.HMSET_BUFFER_AND_ARRAY = function () { // Saving a buffer and an array to the same key should not error var key = "test hash", From c8103928b43d37b25c46af939759098968d016f0 Mon Sep 17 00:00:00 2001 From: Kevin Ingersoll Date: Tue, 14 Aug 2012 20:17:41 -0700 Subject: [PATCH 0052/1748] Attempt evalsha before eval Fix #253 Signed-off-by: DTrejo --- index.js | 31 +++++++++++++++++++++++++++++++ 1 file changed, 31 insertions(+) diff --git a/index.js b/index.js index 77bda9779d2..888ee055e3d 100644 --- a/index.js +++ b/index.js @@ -5,6 +5,7 @@ var net = require("net"), Queue = require("./lib/queue"), to_array = require("./lib/to_array"), events = require("events"), + crypto = require("crypto"), parsers = [], commands, connection_id = 0, default_port = 6379, @@ -1059,6 +1060,36 @@ RedisClient.prototype.MULTI = function (args) { return new Multi(this, args); }; + +// stash original eval method +var eval = RedisClient.prototype.eval; +// hook eval with an attempt to evalsha for cached scripts +RedisClient.prototype.eval = +RedisClient.prototype.EVAL = function () { + var self = this, + args = to_array(arguments), + callback; + + if (typeof args[args.length - 1] === "function") { + callback = args.pop(); + } + + // replace script source with sha value + var source = args[0]; + args[0] = crypto.createHash("sha1").update(source).digest("hex"); + + self.evalsha(args, function (err, reply) { + if (err && /NOSCRIPT/.test(err.message)) { + args[0] = source; + eval.call(self, args, callback); + + } else if (callback) { + callback(err, reply); + } + }); +}; + + exports.createClient = function (port_arg, host_arg, options) { var port = port_arg || default_port, host = host_arg || default_host, From 124ea082b92b603f27f265d903dce0e745a0c9b0 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Mart=C3=ADn=20Ciparelli?= Date: Fri, 17 Aug 2012 15:23:25 -0300 Subject: [PATCH 0053/1748] fixes #218 by expanding last argument array only for sadd command Also adds a test that uses SADD in caps. Nicely enough, this makes multi_bench.js run just a tiny bit faster :) Signed-off-by: DTrejo --- index.js | 11 +++++------ test.js | 9 ++++++++- 2 files changed, 13 insertions(+), 7 deletions(-) diff --git a/index.js b/index.js index 888ee055e3d..61cb4e91c5f 100644 --- a/index.js +++ b/index.js @@ -676,12 +676,11 @@ RedisClient.prototype.send_command = function (command, args, callback) { throw new Error("send_command: second argument must be an array"); } - // if the last argument is an array, expand it out. This allows commands like this: - // client.command(arg1, [arg2, arg3, arg4], cb); - // and converts to: - // client.command(arg1, arg2, arg3, arg4, cb); - // which is convenient for some things like sadd - if (args.length > 0 && Array.isArray(args[args.length - 1])) { + // if the last argument is an array and command is sadd, expand it out: + // client.sadd(arg1, [arg2, arg3, arg4], cb); + // converts to: + // client.sadd(arg1, arg2, arg3, arg4, cb); + if ((command === 'sadd' || command === 'SADD') && args.length > 0 && Array.isArray(args[args.length - 1])) { args = args.slice(0, -1).concat(args[args.length - 1]); } diff --git a/test.js b/test.js index 4e9289762fd..91846800376 100644 --- a/test.js +++ b/test.js @@ -905,7 +905,7 @@ tests.SADD = function () { var name = "SADD"; client.del('set0'); - client.sadd('set0', 'member0', require_number(1, name)); + client.SADD('set0', 'member0', require_number(1, name)); client.sadd('set0', 'member0', last(name, require_number(0, name))); }; @@ -919,6 +919,13 @@ tests.SADD2 = function () { assert.strictEqual(res[0], "member0"); assert.strictEqual(res[1], "member1"); assert.strictEqual(res[2], "member2"); + }); + client.SADD("set1", ["member0", "member1", "member2"], require_number(3, name)); + client.smembers("set1", function (err, res) { + assert.strictEqual(res.length, 3); + assert.strictEqual(res[0], "member0"); + assert.strictEqual(res[1], "member1"); + assert.strictEqual(res[2], "member2"); next(name); }); }; From 8be55fb3044b1160d0f7d854f127f5c3f0da285b Mon Sep 17 00:00:00 2001 From: Jerry Sievert Date: Wed, 22 Aug 2012 21:53:18 -0700 Subject: [PATCH 0054/1748] fix all but lrange 20k --- lib/parser/javascript.js | 24 +++++++++++++++++++----- 1 file changed, 19 insertions(+), 5 deletions(-) diff --git a/lib/parser/javascript.js b/lib/parser/javascript.js index 1c1c41064c7..b35fd84ddea 100644 --- a/lib/parser/javascript.js +++ b/lib/parser/javascript.js @@ -126,7 +126,7 @@ FasterReplyParser.prototype._parseResult = function (type) { FasterReplyParser.prototype.execute = function (buffer) { this.append(buffer); - + var type; while (true) { var offset = this._offset; try { @@ -137,20 +137,33 @@ FasterReplyParser.prototype.execute = function (buffer) { break; } - var type = this._buffer[this._offset++]; + type = this._buffer[this._offset++]; + if (type === 43) { // + ret = this._parseResult(type); + if (ret === null) { + break; + } this.send_reply(ret); } else if (type === 45) { // - ret = this._parseResult(type); + if (ret === null) { + break; + } this.send_error(ret); } else if (type === 58) { // : ret = this._parseResult(type); + if (ret === null) { + break; + } this.send_reply(+ret); } else if (type === 36) { // $ ret = this._parseResult(type); + if (ret === null) { + break; + } this.send_reply(ret); } else if (type === 42) { // * // set a rewind point. if a failure occurs, @@ -167,8 +180,8 @@ FasterReplyParser.prototype.execute = function (buffer) { } catch(err) { // catch the error (not enough data), rewind, and wait // for the next packet to appear - this._offset = offset; - break; + this._offset = offset; + break; } } }; @@ -207,6 +220,7 @@ FasterReplyParser.prototype.append = function(newBuffer) { this._buffer = tmpBuffer; } + this._offset = 0; }; @@ -245,7 +259,7 @@ FasterReplyParser.prototype.parser_error = function (message) { FasterReplyParser.prototype.send_error = function (reply) { this.emit("reply error", reply); }; - +var count = 0; FasterReplyParser.prototype.send_reply = function (reply) { this.emit("reply", reply); }; \ No newline at end of file From 3d27cb23ee2e7c02abde4aff332074b2765f803b Mon Sep 17 00:00:00 2001 From: Jerry Sievert Date: Thu, 23 Aug 2012 13:09:05 -0700 Subject: [PATCH 0055/1748] update for style changes --- lib/parser/javascript.js | 35 +++++++++++++++++++++++------------ 1 file changed, 23 insertions(+), 12 deletions(-) diff --git a/lib/parser/javascript.js b/lib/parser/javascript.js index b35fd84ddea..07a0962e7ff 100644 --- a/lib/parser/javascript.js +++ b/lib/parser/javascript.js @@ -109,10 +109,12 @@ FasterReplyParser.prototype._parseResult = function (type) { } var reply = [ ]; + var ntype, i; + offset = this._offset - 1; - for (var i = 0; i < packetHeader.size; i++) { - var ntype = this._buffer[this._offset++]; + for (i = 0; i < packetHeader.size; i++) { + ntype = this._buffer[this._offset++]; if (this._offset === this._buffer.length) { throw new Error('too far'); @@ -126,12 +128,12 @@ FasterReplyParser.prototype._parseResult = function (type) { FasterReplyParser.prototype.execute = function (buffer) { this.append(buffer); - var type; + + var type, ret, offset; + while (true) { - var offset = this._offset; + offset = this._offset; try { - var ret; - // at least 4 bytes: :1\r\n if (this._bytesRemaining() < 4) { break; @@ -142,21 +144,27 @@ FasterReplyParser.prototype.execute = function (buffer) { if (type === 43) { // + ret = this._parseResult(type); + if (ret === null) { break; } + this.send_reply(ret); } else if (type === 45) { // - ret = this._parseResult(type); + if (ret === null) { break; } + this.send_error(ret); } else if (type === 58) { // : ret = this._parseResult(type); + if (ret === null) { break; } + this.send_reply(+ret); } else if (type === 36) { // $ ret = this._parseResult(type); @@ -164,12 +172,15 @@ FasterReplyParser.prototype.execute = function (buffer) { if (ret === null) { break; } + this.send_reply(ret); } else if (type === 42) { // * // set a rewind point. if a failure occurs, // wait for the next execute()/append() and try again offset = this._offset - 1; + ret = this._parseResult(type); + if (ret === -1) { this._offset = offset; break; @@ -178,8 +189,8 @@ FasterReplyParser.prototype.execute = function (buffer) { this.send_reply(ret); } } catch(err) { - // catch the error (not enough data), rewind, and wait - // for the next packet to appear + // catch the error (not enough data), rewind, and wait + // for the next packet to appear this._offset = offset; break; } @@ -211,9 +222,9 @@ FasterReplyParser.prototype.append = function(newBuffer) { if (Buffer.concat !== undefined) { this._buffer = Buffer.concat([this._buffer.slice(this._offset), newBuffer]); } else { - var remaining = this._bytesRemaining(); - var newLength = remaining + newBuffer.length; - var tmpBuffer = new Buffer(newLength); + var remaining = this._bytesRemaining(), + newLength = remaining + newBuffer.length, + tmpBuffer = new Buffer(newLength); this._buffer.copy(tmpBuffer, 0, this._offset); newBuffer.copy(tmpBuffer, remaining, 0); @@ -259,7 +270,7 @@ FasterReplyParser.prototype.parser_error = function (message) { FasterReplyParser.prototype.send_error = function (reply) { this.emit("reply error", reply); }; -var count = 0; + FasterReplyParser.prototype.send_reply = function (reply) { this.emit("reply", reply); }; \ No newline at end of file From 790c783e8d44ca62b5fbe95a29bc1d2218e9d7bf Mon Sep 17 00:00:00 2001 From: Kevin Ingersoll Date: Sun, 19 Aug 2012 14:04:59 -0700 Subject: [PATCH 0056/1748] Fix tests w/ support for array as last arg and no errors Signed-off-by: DTrejo --- test.js | 152 +++++++++++++++++++++++++++++++++----------------------- 1 file changed, 90 insertions(+), 62 deletions(-) diff --git a/test.js b/test.js index 91846800376..0a03375e55c 100644 --- a/test.js +++ b/test.js @@ -4,6 +4,7 @@ var redis = require("./index"), client2 = redis.createClient(), client3 = redis.createClient(), assert = require("assert"), + crypto = require("crypto"), util = require("./lib/util"), test_db_num = 15, // this DB will be flushed and used for testing tests = {}, @@ -22,7 +23,7 @@ function buffers_to_strings(arr) { function require_number(expected, label) { return function (err, results) { - assert.strictEqual(null, err, "result sent back unexpected error: " + err); + assert.strictEqual(null, err, label + " expected " + expected + ", got error: " + err); assert.strictEqual(expected, results, label + " " + expected + " !== " + results); assert.strictEqual(typeof results, "number", label); return true; @@ -31,7 +32,7 @@ function require_number(expected, label) { function require_number_any(label) { return function (err, results) { - assert.strictEqual(null, err, "result sent back unexpected error: " + err); + assert.strictEqual(null, err, label + " expected any number, got error: " + err); assert.strictEqual(typeof results, "number", label + " " + results + " is not a number"); return true; }; @@ -39,7 +40,7 @@ function require_number_any(label) { function require_number_pos(label) { return function (err, results) { - assert.strictEqual(null, err, "result sent back unexpected error: " + err); + assert.strictEqual(null, err, label + " expected positive number, got error: " + err); assert.strictEqual(true, (results > 0), label + " " + results + " is not a positive number"); return true; }; @@ -47,7 +48,7 @@ function require_number_pos(label) { function require_string(str, label) { return function (err, results) { - assert.strictEqual(null, err, "result sent back unexpected error: " + err); + assert.strictEqual(null, err, label + " expected string '" + str + "', got error: " + err); assert.equal(str, results, label + " " + str + " does not match " + results); return true; }; @@ -55,7 +56,7 @@ function require_string(str, label) { function require_null(label) { return function (err, results) { - assert.strictEqual(null, err, "result sent back unexpected error: " + err); + assert.strictEqual(null, err, label + " expected null, got error: " + err); assert.strictEqual(null, results, label + ": " + results + " is not null"); return true; }; @@ -229,7 +230,7 @@ tests.MULTI_6 = function () { tests.EVAL_1 = function () { var name = "EVAL_1"; - if (client.server_info.versions[0] >= 2 && client.server_info.versions[1] >= 9) { + if (client.server_info.versions[0] >= 2 && client.server_info.versions[1] >= 5) { // test {EVAL - Lua integer -> Redis protocol type conversion} client.eval("return 100.5", 0, require_number(100, name)); // test {EVAL - Lua string -> Redis protocol type conversion} @@ -261,62 +262,89 @@ tests.EVAL_1 = function () { assert.strictEqual("c", res[2], name); assert.strictEqual("d", res[3], name); }); - // test {EVAL - is Lua able to call Redis API?} - client.set("mykey", "myval"); - client.eval("return redis.call('get','mykey')", 0, require_string("myval", name)); - // test {EVALSHA - Can we call a SHA1 if already defined?} - client.evalsha("9bd632c7d33e571e9f24556ebed26c3479a87129", 0, require_string("myval", name)); - // test {EVALSHA - Do we get an error on non defined SHA1?} - client.evalsha("ffffffffffffffffffffffffffffffffffffffff", 0, require_error(name)); + + // prepare sha sum for evalsha cache test + var source = "return redis.call('get', 'sha test')", + sha = crypto.createHash('sha1').update(source).digest('hex'); + + client.set("sha test", "eval get sha test", function (err, res) { + if (err) throw err; + // test {EVAL - is Lua able to call Redis API?} + client.eval(source, 0, function (err, res) { + require_string("eval get sha test", name)(err, res); + // test {EVALSHA - Can we call a SHA1 if already defined?} + client.evalsha(sha, 0, require_string("eval get sha test", name)); + // test {EVALSHA - Do we get an error on non defined SHA1?} + client.evalsha("ffffffffffffffffffffffffffffffffffffffff", 0, require_error(name)); + }); + }); + // test {EVAL - Redis integer -> Lua type conversion} - client.set("x", 0); - client.eval("local foo = redis.call('incr','x')\n" + "return {type(foo),foo}", 0, function (err, res) { - assert.strictEqual(2, res.length, name); - assert.strictEqual("number", res[0], name); - assert.strictEqual(1, res[1], name); + client.set("incr key", 0, function (err, reply) { + if (err) throw err; + client.eval("local foo = redis.call('incr','incr key')\n" + "return {type(foo),foo}", 0, function (err, res) { + if (err) throw err; + assert.strictEqual(2, res.length, name); + assert.strictEqual("number", res[0], name); + assert.strictEqual(1, res[1], name); + }); }); - // test {EVAL - Redis bulk -> Lua type conversion} - client.eval("local foo = redis.call('get','mykey'); return {type(foo),foo}", 0, function (err, res) { - assert.strictEqual(2, res.length, name); - assert.strictEqual("string", res[0], name); - assert.strictEqual("myval", res[1], name); + + client.set("bulk reply key", "bulk reply value", function (err, res) { + // test {EVAL - Redis bulk -> Lua type conversion} + client.eval("local foo = redis.call('get','bulk reply key'); return {type(foo),foo}", 0, function (err, res) { + if (err) throw err; + assert.strictEqual(2, res.length, name); + assert.strictEqual("string", res[0], name); + assert.strictEqual("bulk reply value", res[1], name); + }); }); + // test {EVAL - Redis multi bulk -> Lua type conversion} - client.del("mylist"); - client.rpush("mylist", "a"); - client.rpush("mylist", "b"); - client.rpush("mylist", "c"); - client.eval("local foo = redis.call('lrange','mylist',0,-1)\n" + "return {type(foo),foo[1],foo[2],foo[3],# foo}", 0, function (err, res) { - assert.strictEqual(5, res.length, name); - assert.strictEqual("table", res[0], name); - assert.strictEqual("a", res[1], name); - assert.strictEqual("b", res[2], name); - assert.strictEqual("c", res[3], name); - assert.strictEqual(3, res[4], name); - }); + client.multi() + .del("mylist") + .rpush("mylist", "a") + .rpush("mylist", "b") + .rpush("mylist", "c") + .exec(function (err, replies) { + if (err) throw err; + client.eval("local foo = redis.call('lrange','mylist',0,-1); return {type(foo),foo[1],foo[2],foo[3],# foo}", 0, function (err, res) { + assert.strictEqual(5, res.length, name); + assert.strictEqual("table", res[0], name); + assert.strictEqual("a", res[1], name); + assert.strictEqual("b", res[2], name); + assert.strictEqual("c", res[3], name); + assert.strictEqual(3, res[4], name); + }); + }); // test {EVAL - Redis status reply -> Lua type conversion} client.eval("local foo = redis.call('set','mykey','myval'); return {type(foo),foo['ok']}", 0, function (err, res) { + if (err) throw err; assert.strictEqual(2, res.length, name); assert.strictEqual("table", res[0], name); assert.strictEqual("OK", res[1], name); }); // test {EVAL - Redis error reply -> Lua type conversion} - client.set("mykey", "myval"); - client.eval("local foo = redis.call('incr','mykey'); return {type(foo),foo['err']}", 0, function (err, res) { - assert.strictEqual(2, res.length, name); - assert.strictEqual("table", res[0], name); - assert.strictEqual("ERR value is not an integer or out of range", res[1], name); + client.set("error reply key", "error reply value", function (err, res) { + if (err) throw err; + client.eval("local foo = redis.pcall('incr','error reply key'); return {type(foo),foo['err']}", 0, function (err, res) { + if (err) throw err; + assert.strictEqual(2, res.length, name); + assert.strictEqual("table", res[0], name); + assert.strictEqual("ERR value is not an integer or out of range", res[1], name); + }); }); // test {EVAL - Redis nil bulk reply -> Lua type conversion} - client.del("mykey"); - client.eval("local foo = redis.call('get','mykey'); return {type(foo),foo == false}", 0, function (err, res) { - assert.strictEqual(2, res.length, name); - assert.strictEqual("boolean", res[0], name); - assert.strictEqual(1, res[1], name); + client.del("nil reply key", function (err, res) { + if (err) throw err; + client.eval("local foo = redis.call('get','nil reply key'); return {type(foo),foo == false}", 0, function (err, res) { + if (err) throw err; + assert.strictEqual(2, res.length, name); + assert.strictEqual("boolean", res[0], name); + assert.strictEqual(1, res[1], name); + next(name); + }); }); - // test {EVAL - Script can't run more than configured time limit} { - client.config("set", "lua-time-limit", 1); - client.eval("local i = 0; while true do i=i+1 end", 0, last("name", require_error(name))); } else { console.log("Skipping " + name + " because server version isn't new enough."); next(name); @@ -512,7 +540,6 @@ tests.HLEN = function () { client.HSET(key, field1, value1, function (err, results) { client.HLEN(key, function (err, len) { - console.log(results+"sgdshhsshs") assert.ok(2 === +len); next(name); }); @@ -1359,20 +1386,21 @@ tests.MONITOR = function () { })); }); monitor_client.on("monitor", function (time, args) { + // skip monitor command for Redis <= 2.4.16 + if (args[0] === "monitor") return; + responses.push(args); - if (responses.length === 3) { - assert.strictEqual(1, responses[0].length); - assert.strictEqual("monitor", responses[0][0]); - assert.strictEqual(5, responses[1].length); - assert.strictEqual("mget", responses[1][0]); - assert.strictEqual("some", responses[1][1]); - assert.strictEqual("keys", responses[1][2]); - assert.strictEqual("foo", responses[1][3]); - assert.strictEqual("bar", responses[1][4]); - assert.strictEqual(3, responses[2].length); - assert.strictEqual("set", responses[2][0]); - assert.strictEqual("json", responses[2][1]); - assert.strictEqual('{"foo":"123","bar":"sdflkdfsjk","another":false}', responses[2][2]); + if (responses.length === 2) { + assert.strictEqual(5, responses[0].length); + assert.strictEqual("mget", responses[0][0]); + assert.strictEqual("some", responses[0][1]); + assert.strictEqual("keys", responses[0][2]); + assert.strictEqual("foo", responses[0][3]); + assert.strictEqual("bar", responses[0][4]); + assert.strictEqual(3, responses[1].length); + assert.strictEqual("set", responses[1][0]); + assert.strictEqual("json", responses[1][1]); + assert.strictEqual('{"foo":"123","bar":"sdflkdfsjk","another":false}', responses[1][2]); monitor_client.quit(function (err, res) { next(name); }); From bb6dc24f928e3cf1e4a5bb09970a51c000ea226e Mon Sep 17 00:00:00 2001 From: DTrejo Date: Mon, 10 Sep 2012 15:55:57 -0400 Subject: [PATCH 0057/1748] package.json: remove optional hiredis dep Fix #258. Also adds keywords hash and more info to the description hash. --- package.json | 9 ++------- 1 file changed, 2 insertions(+), 7 deletions(-) diff --git a/package.json b/package.json index 9234d9156c0..54a92d04c58 100644 --- a/package.json +++ b/package.json @@ -1,10 +1,8 @@ { "name" : "redis", "version" : "0.7.2", - "description" : "Redis client library", + "description" : "Redis client library, battle-tested by Voxer.com", + "keywords" : [ "redis", "pub", "sub", "voxer", "database" ], "author": "Matt Ranney ", - "maintainers": [ - "David Trejo (http://dtrejo.com/)" - ], "main": "./index.js", "scripts": { "test": "node ./test.js" @@ -12,9 +10,6 @@ "devDependencies": { "metrics": ">=0.1.5" }, - "optionalDependencies": { - "hiredis": "*" - }, "repository": { "type": "git", "url": "git://github.com/mranney/node_redis.git" From 670c256e35fb8db89b47f775404eab3bd53ae1f6 Mon Sep 17 00:00:00 2001 From: Matt Ranney Date: Mon, 10 Sep 2012 22:50:54 -0700 Subject: [PATCH 0058/1748] Whitespace and other JSHint changes. --- index.js | 32 ++++++++++----------- lib/parser/hiredis.js | 12 ++++---- lib/parser/javascript.js | 62 +++++++++++++++++++--------------------- lib/queue.js | 8 ++---- multi_bench.js | 5 +--- package.json | 4 +-- 6 files changed, 58 insertions(+), 65 deletions(-) diff --git a/index.js b/index.js index 61cb4e91c5f..8ef3f9ecde8 100644 --- a/index.js +++ b/index.js @@ -130,8 +130,7 @@ RedisClient.prototype.flush_and_error = function (message) { }; RedisClient.prototype.on_error = function (msg) { - var message = "Redis connection to " + this.host + ":" + this.port + " failed - " + msg, - self = this, command_obj; + var message = "Redis connection to " + this.host + ":" + this.port + " failed - " + msg; if (this.closing) { return; @@ -198,7 +197,6 @@ RedisClient.prototype.on_connect = function () { if (exports.debug_mode) { console.log("Stream connected " + this.host + ":" + this.port + " id " + this.connection_id); } - var self = this; this.connected = true; this.ready = false; @@ -289,12 +287,12 @@ RedisClient.prototype.on_ready = function () { if (this.pub_sub_mode === true) { // only emit "ready" when all subscriptions were made again var callback_count = 0; - var callback = function() { + var callback = function () { callback_count--; - if (callback_count == 0) { + if (callback_count === 0) { self.emit("ready"); } - } + }; Object.keys(this.subscription_set).forEach(function (key) { var parts = key.split(" "); if (exports.debug_mode) { @@ -389,7 +387,7 @@ RedisClient.prototype.send_offline_queue = function () { }; RedisClient.prototype.connection_gone = function (why) { - var self = this, message; + var self = this; // If a retry is already in progress, just let that happen if (this.retry_timer) { @@ -562,7 +560,7 @@ function reply_to_strings(reply) { } RedisClient.prototype.return_reply = function (reply) { - var command_obj, obj, i, len, type, timestamp, argindex, args, queue_len; + var command_obj, len, type, timestamp, argindex, args, queue_len; command_obj = this.command_queue.shift(), queue_len = this.command_queue.getLength(); @@ -646,7 +644,7 @@ function Command(command, args, sub_command, buffer_args, callback) { } RedisClient.prototype.send_command = function (command, args, callback) { - var arg, this_args, command_obj, i, il, elem_count, buffer_args, stream = this.stream, command_str = "", buffered_writes = 0, last_arg_type; + var arg, command_obj, i, il, elem_count, buffer_args, stream = this.stream, command_str = "", buffered_writes = 0, last_arg_type; if (typeof command !== "string") { throw new Error("First argument to send_command must be the command name string, not " + typeof command); @@ -948,8 +946,11 @@ RedisClient.prototype.hmset = function (args, callback) { tmp_args.push(key); if (typeof args[1][key] !== "string") { var err = new Error("hmset expected value to be a string", key, ":", args[1][key]); - if (callback) return callback(err); - else throw err; + if (callback) { + return callback(err); + } else { + throw err; + } } tmp_args.push(args[1][key]); } @@ -1027,7 +1028,7 @@ Multi.prototype.exec = function (callback) { } } - var i, il, j, jl, reply, args; + var i, il, reply, args; if (replies) { for (i = 1, il = self.queue.length; i < il; i += 1) { @@ -1061,10 +1062,9 @@ RedisClient.prototype.MULTI = function (args) { // stash original eval method -var eval = RedisClient.prototype.eval; +var eval_orig = RedisClient.prototype.eval; // hook eval with an attempt to evalsha for cached scripts -RedisClient.prototype.eval = -RedisClient.prototype.EVAL = function () { +RedisClient.prototype.eval = RedisClient.prototype.EVAL = function () { var self = this, args = to_array(arguments), callback; @@ -1080,7 +1080,7 @@ RedisClient.prototype.EVAL = function () { self.evalsha(args, function (err, reply) { if (err && /NOSCRIPT/.test(err.message)) { args[0] = source; - eval.call(self, args, callback); + eval_orig.call(self, args, callback); } else if (callback) { callback(err, reply); diff --git a/lib/parser/hiredis.js b/lib/parser/hiredis.js index cbb15ba387e..940bfeeb767 100644 --- a/lib/parser/hiredis.js +++ b/lib/parser/hiredis.js @@ -1,5 +1,3 @@ -/*global Buffer require exports console setTimeout */ - var events = require("events"), util = require("../util"), hiredis = require("hiredis"); @@ -29,13 +27,15 @@ HiredisReplyParser.prototype.execute = function (data) { this.reader.feed(data); while (true) { try { - reply = this.reader.get(); + reply = this.reader.get(); } catch (err) { - this.emit("error", err); - break; + this.emit("error", err); + break; } - if (reply === undefined) break; + if (reply === undefined) { + break; + } if (reply && reply.constructor === Error) { this.emit("reply error", reply); diff --git a/lib/parser/javascript.js b/lib/parser/javascript.js index 07a0962e7ff..3a69a574e5b 100644 --- a/lib/parser/javascript.js +++ b/lib/parser/javascript.js @@ -1,28 +1,28 @@ var events = require("events"), - util = require('../util'); + util = require("../util"); -function Packet (type, size) { +function Packet(type, size) { this.type = type; this.size = +size; } -exports.name = 'faster'; +exports.name = "faster"; exports.debug_mode = false; -function FasterReplyParser (options) { +function ReplyParser(options) { this.name = exports.name; this.options = options || { }; this._buffer = null; this._offset = 0; - this._encoding = 'utf-8'; + this._encoding = "utf-8"; this._debug_mode = options.debug_mode; this._reply_type = null; } -util.inherits(FasterReplyParser, events.EventEmitter); +util.inherits(ReplyParser, events.EventEmitter); -exports.Parser = FasterReplyParser; +exports.Parser = ReplyParser; // Buffer.toString() is quite slow for small strings function small_toString(buf, start, end) { @@ -35,7 +35,7 @@ function small_toString(buf, start, end) { return tmp; } -FasterReplyParser.prototype._parseResult = function (type) { +ReplyParser.prototype._parseResult = function (type) { var start, end, offset, packetHeader; if (type === 43 || type === 45) { // + or - @@ -77,7 +77,6 @@ FasterReplyParser.prototype._parseResult = function (type) { return null; } - end = this._offset + packetHeader.size; start = this._offset; @@ -117,7 +116,7 @@ FasterReplyParser.prototype._parseResult = function (type) { ntype = this._buffer[this._offset++]; if (this._offset === this._buffer.length) { - throw new Error('too far'); + throw new Error("too far"); } reply.push(this._parseResult(ntype)); } @@ -126,7 +125,7 @@ FasterReplyParser.prototype._parseResult = function (type) { } }; -FasterReplyParser.prototype.execute = function (buffer) { +ReplyParser.prototype.execute = function (buffer) { this.append(buffer); var type, ret, offset; @@ -140,13 +139,12 @@ FasterReplyParser.prototype.execute = function (buffer) { } type = this._buffer[this._offset++]; - - + if (type === 43) { // + ret = this._parseResult(type); if (ret === null) { - break; + break; } this.send_reply(ret); @@ -154,7 +152,7 @@ FasterReplyParser.prototype.execute = function (buffer) { ret = this._parseResult(type); if (ret === null) { - break; + break; } this.send_error(ret); @@ -162,7 +160,7 @@ FasterReplyParser.prototype.execute = function (buffer) { ret = this._parseResult(type); if (ret === null) { - break; + break; } this.send_reply(+ret); @@ -170,7 +168,7 @@ FasterReplyParser.prototype.execute = function (buffer) { ret = this._parseResult(type); if (ret === null) { - break; + break; } this.send_reply(ret); @@ -188,16 +186,16 @@ FasterReplyParser.prototype.execute = function (buffer) { this.send_reply(ret); } - } catch(err) { - // catch the error (not enough data), rewind, and wait - // for the next packet to appear - this._offset = offset; - break; + } catch (err) { + // catch the error (not enough data), rewind, and wait + // for the next packet to appear + this._offset = offset; + break; } } }; -FasterReplyParser.prototype.append = function(newBuffer) { +ReplyParser.prototype.append = function (newBuffer) { if (!newBuffer) { return; } @@ -235,42 +233,42 @@ FasterReplyParser.prototype.append = function(newBuffer) { this._offset = 0; }; -FasterReplyParser.prototype.parseHeader = function () { +ReplyParser.prototype.parseHeader = function () { var end = this._packetEndOffset(), - value = small_toString(this._buffer, this._offset, end - 1); + value = small_toString(this._buffer, this._offset, end - 1); this._offset = end + 1; return value; }; -FasterReplyParser.prototype._packetEndOffset = function () { +ReplyParser.prototype._packetEndOffset = function () { var offset = this._offset; while (this._buffer[offset] !== 0x0d && this._buffer[offset + 1] !== 0x0a) { offset++; - + if (offset >= this._buffer.length) { throw new Error("didn't see LF after NL reading multi bulk count (" + offset + " => " + this._buffer.length + ", " + this._offset + ")"); } } - + offset++; return offset; }; -FasterReplyParser.prototype._bytesRemaining = function() { +ReplyParser.prototype._bytesRemaining = function () { return (this._buffer.length - this._offset) < 0 ? 0 : (this._buffer.length - this._offset); }; -FasterReplyParser.prototype.parser_error = function (message) { +ReplyParser.prototype.parser_error = function (message) { this.emit("error", message); }; -FasterReplyParser.prototype.send_error = function (reply) { +ReplyParser.prototype.send_error = function (reply) { this.emit("reply error", reply); }; -FasterReplyParser.prototype.send_reply = function (reply) { +ReplyParser.prototype.send_reply = function (reply) { this.emit("reply", reply); }; \ No newline at end of file diff --git a/lib/queue.js b/lib/queue.js index 56254e1ca40..3fc87ab1028 100644 --- a/lib/queue.js +++ b/lib/queue.js @@ -1,5 +1,3 @@ -var to_array = require("./to_array"); - // Queue class adapted from Tim Caswell's pattern library // http://github.com/creationix/pattern/blob/master/lib/pattern/queue.js @@ -49,13 +47,13 @@ Queue.prototype.getLength = function () { return this.head.length - this.offset + this.tail.length; }; -Object.defineProperty(Queue.prototype, 'length', { +Object.defineProperty(Queue.prototype, "length", { get: function () { return this.getLength(); } }); -if(typeof module !== 'undefined' && module.exports) { - module.exports = Queue; +if (typeof module !== "undefined" && module.exports) { + module.exports = Queue; } diff --git a/multi_bench.js b/multi_bench.js index 5be2e564fda..3a0d92da141 100644 --- a/multi_bench.js +++ b/multi_bench.js @@ -28,8 +28,6 @@ metrics.Histogram.prototype.print_line = function () { }; function Test(args) { - var self = this; - this.args = args; this.callback = null; @@ -46,7 +44,7 @@ function Test(args) { } Test.prototype.run = function (callback) { - var self = this, i; + var i; this.callback = callback; @@ -120,7 +118,6 @@ Test.prototype.stop_clients = function () { Test.prototype.send_next = function () { var self = this, cur_client = this.commands_sent % this.clients.length, - command_num = this.commands_sent, start = Date.now(); this.clients[cur_client][this.args.command](this.args.args, function (err, res) { diff --git a/package.json b/package.json index 54a92d04c58..5fdec601a27 100644 --- a/package.json +++ b/package.json @@ -1,6 +1,6 @@ { "name" : "redis", - "version" : "0.7.2", - "description" : "Redis client library, battle-tested by Voxer.com", + "version" : "0.8.0", + "description" : "Redis client library, battle-tested by Voxer", "keywords" : [ "redis", "pub", "sub", "voxer", "database" ], "author": "Matt Ranney ", "main": "./index.js", From cbcb8ca5b9cc1263ad63a079c68d49841071f7e4 Mon Sep 17 00:00:00 2001 From: Matt Ranney Date: Mon, 10 Sep 2012 23:00:13 -0700 Subject: [PATCH 0059/1748] Many contributed features and fixes, including: MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit * Pure JavaScript reply parser that is usually faster than hiredis (Jerry Sievert) * Remove hiredis as optionalDependency from package.json. It still works if you want it. * Restore client state on reconnect, including select, subscribe, and monitor. (Ignacio Burgueño) * Fix idle event (Trae Robrock) * Many documentation improvements and bug fixes (David Trejo) --- changelog.md | 10 ++++++++++ 1 file changed, 10 insertions(+) diff --git a/changelog.md b/changelog.md index 4248288ce73..8d09c7bf06c 100644 --- a/changelog.md +++ b/changelog.md @@ -1,6 +1,16 @@ Changelog ========= +## v0.8.0 - September 10, 2012 + +Many contributed features and fixes, including: + +* Pure JavaScript reply parser that is usually faster than hiredis (Jerry Sievert) +* Remove hiredis as optionalDependency from package.json. It still works if you want it. +* Restore client state on reconnect, including select, subscribe, and monitor. (Ignacio Burgueño) +* Fix idle event (Trae Robrock) +* Many documentation improvements and bug fixes (David Trejo) + ## v0.7.2 - April 29, 2012 Many contributed fixes. Thank you, contributors. From 668dfdead6436a0ea9e91fa6d401ea5cd57f5cc1 Mon Sep 17 00:00:00 2001 From: Jerry Sievert Date: Tue, 11 Sep 2012 11:43:18 -0700 Subject: [PATCH 0060/1748] fix null key response: issue #267 --- lib/parser/javascript.js | 16 +++++++++++++--- 1 file changed, 13 insertions(+), 3 deletions(-) diff --git a/lib/parser/javascript.js b/lib/parser/javascript.js index 3a69a574e5b..b2d6cb2387d 100644 --- a/lib/parser/javascript.js +++ b/lib/parser/javascript.js @@ -74,7 +74,7 @@ ReplyParser.prototype._parseResult = function (type) { // packets with a size of -1 are considered null if (packetHeader.size === -1) { - return null; + return undefined; } end = this._offset + packetHeader.size; @@ -108,7 +108,7 @@ ReplyParser.prototype._parseResult = function (type) { } var reply = [ ]; - var ntype, i; + var ntype, i, res; offset = this._offset - 1; @@ -118,7 +118,11 @@ ReplyParser.prototype._parseResult = function (type) { if (this._offset === this._buffer.length) { throw new Error("too far"); } - reply.push(this._parseResult(ntype)); + res = this._parseResult(ntype); + if (res === undefined) { + res = null; + } + reply.push(res); } return reply; @@ -171,6 +175,12 @@ ReplyParser.prototype.execute = function (buffer) { break; } + // check the state for what is the result of + // a -1, set it back up for a null reply + if (ret === undefined) { + ret = null; + } + this.send_reply(ret); } else if (type === 42) { // * // set a rewind point. if a failure occurs, From 587d4a361be97f3014c84f6ae1ad4a906ca5d4fb Mon Sep 17 00:00:00 2001 From: Chakrit Wichian Date: Wed, 12 Sep 2012 01:26:23 +0700 Subject: [PATCH 0061/1748] Add test for `GET non-existent-key` case. (issue #267) --- test.js | 11 +++++++++-- 1 file changed, 9 insertions(+), 2 deletions(-) diff --git a/test.js b/test.js index 0a03375e55c..9b3c4526488 100644 --- a/test.js +++ b/test.js @@ -817,12 +817,19 @@ tests.DBSIZE = function () { client.DBSIZE([], last(name, require_number_pos("DBSIZE"))); }; -tests.GET = function () { - var name = "GET"; +tests.GET_1 = function () { + var name = "GET_1"; client.set(["get key", "get val"], require_string("OK", name)); client.GET(["get key"], last(name, require_string("get val", name))); }; +tests.GET_2 = function() { + var name = "GET_2"; + + // tests handling of non-existent keys + client.GET('this_key_shouldnt_exist', last(name, require_null(name))); +}; + tests.SET = function () { var name = "SET"; client.SET(["set key", "set val"], require_string("OK", name)); From 0391b95490dfb9cf365fe74306b070fa5b7a9f48 Mon Sep 17 00:00:00 2001 From: Matt Ranney Date: Tue, 11 Sep 2012 12:56:36 -0700 Subject: [PATCH 0062/1748] Important bug fix for null responses (Jerry Sievert) --- changelog.md | 4 ++++ package.json | 2 +- 2 files changed, 5 insertions(+), 1 deletion(-) diff --git a/changelog.md b/changelog.md index 8d09c7bf06c..f6e0f3e0a88 100644 --- a/changelog.md +++ b/changelog.md @@ -1,6 +1,10 @@ Changelog ========= +## v0.8.1 - September 11, 2012 + +Important bug fix for null responses (Jerry Sievert) + ## v0.8.0 - September 10, 2012 Many contributed features and fixes, including: diff --git a/package.json b/package.json index 5fdec601a27..2308a0f5750 100644 --- a/package.json +++ b/package.json @@ -1,5 +1,5 @@ { "name" : "redis", - "version" : "0.8.0", + "version" : "0.8.1", "description" : "Redis client library, battle-tested by Voxer", "keywords" : [ "redis", "pub", "sub", "voxer", "database" ], "author": "Matt Ranney ", From 1dbe587fcabe4cf2ee99a6774484279790608c2f Mon Sep 17 00:00:00 2001 From: Jerry Sievert Date: Fri, 14 Sep 2012 13:51:49 -0700 Subject: [PATCH 0063/1748] remove null for rewind and use throw mechanism, should help with #273 Fix #275 Signed-off-by: DTrejo --- lib/parser/javascript.js | 12 +++++++++++- 1 file changed, 11 insertions(+), 1 deletion(-) diff --git a/lib/parser/javascript.js b/lib/parser/javascript.js index b2d6cb2387d..dd70cf0987b 100644 --- a/lib/parser/javascript.js +++ b/lib/parser/javascript.js @@ -46,6 +46,11 @@ ReplyParser.prototype._parseResult = function (type) { // include the delimiter this._offset = end + 2; + if (end > this._buffer.length) { + this._offset = start; + throw new Error("too far"); + } + if (this.options.return_buffers) { return this._buffer.slice(start, end); } else { @@ -63,6 +68,11 @@ ReplyParser.prototype._parseResult = function (type) { // include the delimiter this._offset = end + 2; + if (end > this._buffer.length) { + this._offset = start; + throw new Error("too far"); + } + // return the coerced numeric value return +small_toString(this._buffer, start, end); } else if (type === 36) { // $ @@ -85,7 +95,7 @@ ReplyParser.prototype._parseResult = function (type) { if (end > this._buffer.length) { this._offset = offset; - return null; + throw new Error("too far"); } if (this.options.return_buffers) { From f03e673338d54b8e76b2c62c817b68f62d5f166b Mon Sep 17 00:00:00 2001 From: Luigi Pinca Date: Sat, 15 Sep 2012 22:29:47 +0200 Subject: [PATCH 0064/1748] Fix cases where the offset is out of range (javascript parser) The exception for the "too far" error was not thrown if the offset was bigger than the buffer length, screwing up the parser. Fix #277 Signed-off-by: DTrejo --- lib/parser/javascript.js | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/lib/parser/javascript.js b/lib/parser/javascript.js index dd70cf0987b..9acfa1f188d 100644 --- a/lib/parser/javascript.js +++ b/lib/parser/javascript.js @@ -125,12 +125,12 @@ ReplyParser.prototype._parseResult = function (type) { for (i = 0; i < packetHeader.size; i++) { ntype = this._buffer[this._offset++]; - if (this._offset === this._buffer.length) { + if (this._offset > this._buffer.length) { throw new Error("too far"); } res = this._parseResult(ntype); if (res === undefined) { - res = null; + res = null; } reply.push(res); } From 252a77e92a1fc0a67a910e183e97c3816b3cab67 Mon Sep 17 00:00:00 2001 From: DTrejo Date: Fri, 21 Sep 2012 22:27:33 -0400 Subject: [PATCH 0065/1748] multi_bulk reply test for fix #274 --- test.js | 26 ++++++++++++++++++++++++++ 1 file changed, 26 insertions(+) diff --git a/test.js b/test.js index 9b3c4526488..db6e9bbf763 100644 --- a/test.js +++ b/test.js @@ -771,6 +771,32 @@ tests.KEYS = function () { }); }; +tests.MULTIBULK = function() { + var name = "MULTIBULK", + keys_values = []; + + for (var i = 0; i < 200; i++) { + var key_value = [ + "multibulk:" + crypto.randomBytes(256).toString("hex"), // use long strings as keys to ensure generation of large packet + "test val " + i + ]; + keys_values.push(key_value); + } + + client.mset(keys_values.reduce(function(a, b) { + return a.concat(b); + }), require_string("OK", name)); + + client.KEYS("multibulk:*", function(err, results) { + assert.strictEqual(null, err, "result sent back unexpected error: " + err); + assert.deepEqual(keys_values.map(function(val) { + return val[0]; + }).sort(), results.sort(), name); + }); + + next(name); +}; + tests.MULTIBULK_ZERO_LENGTH = function () { var name = "MULTIBULK_ZERO_LENGTH"; client.KEYS(['users:*'], function (err, results) { From a9626578df235953dd01839cdc1d3e63a044e911 Mon Sep 17 00:00:00 2001 From: Richard Mok Date: Thu, 11 Oct 2012 16:07:36 +0800 Subject: [PATCH 0066/1748] RedisClient.prototype.end function bug fixed. An uncaught exception will be raised when the retry timer tries to reconnect and encounter an error, for all event listeners of the stream were removed in line 826. It should set closing varable to be true. --- index.js | 1 + 1 file changed, 1 insertion(+) diff --git a/index.js b/index.js index 8ef3f9ecde8..6832b4bcebf 100644 --- a/index.js +++ b/index.js @@ -826,6 +826,7 @@ RedisClient.prototype.end = function () { this.stream._events = {}; this.connected = false; this.ready = false; + this.closing = true; return this.stream.end(); }; From 71070d84fbda9211fb808706a155dd69458c769b Mon Sep 17 00:00:00 2001 From: Jan Matousek Date: Thu, 18 Oct 2012 19:37:37 +0200 Subject: [PATCH 0067/1748] Remove unused variable reference --- index.js | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/index.js b/index.js index 8ef3f9ecde8..786407a8ab3 100644 --- a/index.js +++ b/index.js @@ -432,7 +432,7 @@ RedisClient.prototype.connection_gone = function (why) { this.retry_delay = Math.floor(this.retry_delay * this.retry_backoff); if (exports.debug_mode) { - console.log("Retry connection in " + this.current_retry_delay + " ms"); + console.log("Retry connection in " + this.retry_delay + " ms"); } if (this.max_attempts && this.attempts >= this.max_attempts) { @@ -453,7 +453,7 @@ RedisClient.prototype.connection_gone = function (why) { console.log("Retrying connection..."); } - self.retry_totaltime += self.current_retry_delay; + self.retry_totaltime += self.retry_delay; if (self.connect_timeout && self.retry_totaltime >= self.connect_timeout) { self.retry_timer = null; From cc3a1da543684a3c778c27a2a9fc11ae6b49c371 Mon Sep 17 00:00:00 2001 From: Jan Matousek Date: Thu, 18 Oct 2012 19:38:39 +0200 Subject: [PATCH 0068/1748] fix whitespace usage --- index.js | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/index.js b/index.js index 786407a8ab3..dd27dbdd78c 100644 --- a/index.js +++ b/index.js @@ -883,9 +883,9 @@ commands.forEach(function (command) { // store db in this.select_db to restore it on reconnect RedisClient.prototype.select = function (db, callback) { - var self = this; + var self = this; - this.send_command('select', [db], function (err, res) { + this.send_command('select', [db], function (err, res) { if (err === null) { self.selected_db = db; } From 7f3f11f9b1ff51e6d098ae1e8a75c65fc532a8ef Mon Sep 17 00:00:00 2001 From: Michael Jackson Date: Fri, 26 Oct 2012 20:19:49 -0700 Subject: [PATCH 0069/1748] Make return_buffers work with ints --- lib/parser/javascript.js | 10 +++++++--- test.js | 32 ++++++++++++++++++++++++++++---- 2 files changed, 35 insertions(+), 7 deletions(-) diff --git a/lib/parser/javascript.js b/lib/parser/javascript.js index 9acfa1f188d..c9ef7ab94dc 100644 --- a/lib/parser/javascript.js +++ b/lib/parser/javascript.js @@ -37,7 +37,7 @@ function small_toString(buf, start, end) { ReplyParser.prototype._parseResult = function (type) { var start, end, offset, packetHeader; - + if (type === 43 || type === 45) { // + or - // up to the delimiter end = this._packetEndOffset() - 1; @@ -73,6 +73,10 @@ ReplyParser.prototype._parseResult = function (type) { throw new Error("too far"); } + if (this.options.return_buffers) { + return this._buffer.slice(start, end); + } + // return the coerced numeric value return +small_toString(this._buffer, start, end); } else if (type === 36) { // $ @@ -177,7 +181,7 @@ ReplyParser.prototype.execute = function (buffer) { break; } - this.send_reply(+ret); + this.send_reply(ret); } else if (type === 36) { // $ ret = this._parseResult(type); @@ -246,7 +250,7 @@ ReplyParser.prototype.append = function (newBuffer) { this._buffer.copy(tmpBuffer, 0, this._offset); newBuffer.copy(tmpBuffer, remaining, 0); - + this._buffer = tmpBuffer; } diff --git a/test.js b/test.js index db6e9bbf763..94bd19d6824 100644 --- a/test.js +++ b/test.js @@ -1,8 +1,12 @@ /*global require console setTimeout process Buffer */ +var PORT = 6379; +var HOST = '127.0.0.1'; + var redis = require("./index"), - client = redis.createClient(), - client2 = redis.createClient(), - client3 = redis.createClient(), + client = redis.createClient(PORT, HOST), + client2 = redis.createClient(PORT, HOST), + client3 = redis.createClient(PORT, HOST), + bclient = redis.createClient(PORT, HOST, { return_buffers: true }), assert = require("assert"), crypto = require("crypto"), util = require("./lib/util"), @@ -85,7 +89,7 @@ next = function next(name) { run_next_test(); }; -// Tests are run in the order they are defined. So FLUSHDB should be stay first. +// Tests are run in the order they are defined, so FLUSHDB should always be first. tests.FLUSHDB = function () { var name = "FLUSHDB"; @@ -97,6 +101,20 @@ tests.FLUSHDB = function () { client.dbsize(last(name, require_number(0, name))); }; +tests.INCR = function () { + var name = "INCR"; + + // Test incr with the maximum JavaScript number value. Since we are + // returning buffers we should get back one more as a Buffer. + bclient.set("seq", "9007199254740992", function (err, result) { + assert.strictEqual(result.toString(), "OK"); + bclient.incr("seq", function (err, result) { + assert.strictEqual("9007199254740993", result.toString()); + next(name); + }); + }); +}; + tests.MULTI_1 = function () { var name = "MULTI_1", multi1, multi2; @@ -1607,6 +1625,7 @@ run_next_test = function run_next_test() { console.log('\n completed \x1b[32m%d\x1b[0m tests in \x1b[33m%d\x1b[0m ms\n', test_count, new Date() - all_start); client.quit(); client2.quit(); + bclient.quit(); } }; @@ -1636,6 +1655,11 @@ client3.on("error", function (err) { console.error("client3: " + err.stack); process.exit(); }); +bclient.on("error", function (err) { + console.error("bclient: " + err.stack); + process.exit(); +}); + client.on("reconnecting", function (params) { console.log("reconnecting: " + util.inspect(params)); }); From f367b8a03ca0c5a1ce0abda17679e3af55298399 Mon Sep 17 00:00:00 2001 From: Matt Ranney Date: Fri, 2 Nov 2012 14:17:53 -0700 Subject: [PATCH 0070/1748] Another version bump because 0.8.1 didn't get applied properly for some mysterious reason. Sorry about that. Changed name of "faster" parser to "javascript". --- changelog.md | 7 +++++++ lib/parser/javascript.js | 4 ++-- package.json | 6 +++--- 3 files changed, 12 insertions(+), 5 deletions(-) diff --git a/changelog.md b/changelog.md index f6e0f3e0a88..e4e3277b541 100644 --- a/changelog.md +++ b/changelog.md @@ -1,6 +1,13 @@ Changelog ========= +## v0.8.2 - November 11, 2012 + +Another version bump because 0.8.1 didn't get applied properly for some mysterious reason. +Sorry about that. + +Changed name of "faster" parser to "javascript". + ## v0.8.1 - September 11, 2012 Important bug fix for null responses (Jerry Sievert) diff --git a/lib/parser/javascript.js b/lib/parser/javascript.js index c9ef7ab94dc..256c3ea9ad3 100644 --- a/lib/parser/javascript.js +++ b/lib/parser/javascript.js @@ -6,7 +6,7 @@ function Packet(type, size) { this.size = +size; } -exports.name = "faster"; +exports.name = "javascript"; exports.debug_mode = false; function ReplyParser(options) { @@ -192,7 +192,7 @@ ReplyParser.prototype.execute = function (buffer) { // check the state for what is the result of // a -1, set it back up for a null reply if (ret === undefined) { - ret = null; + ret = null; } this.send_reply(ret); diff --git a/package.json b/package.json index 2308a0f5750..7873a543c65 100644 --- a/package.json +++ b/package.json @@ -1,7 +1,7 @@ { "name" : "redis", - "version" : "0.8.1", - "description" : "Redis client library, battle-tested by Voxer", - "keywords" : [ "redis", "pub", "sub", "voxer", "database" ], + "version" : "0.8.2", + "description" : "Redis client library", + "keywords" : [ "redis", "database" ], "author": "Matt Ranney ", "main": "./index.js", "scripts": { From 11db346e733db5b7de8d8a44f1b45bfaf1cd75e8 Mon Sep 17 00:00:00 2001 From: roam Date: Wed, 19 Dec 2012 15:04:07 +0800 Subject: [PATCH 0071/1748] Fixed an unexpected exception bug when reconnecting in pub/sub mode if selected_db is not null RedisClient might enter pub/sub mode after at least one SELECT command issued. When reconnecting, the SELECT command is issued after restoring pub_sub_command to true, which causes an exception. --- index.js | 5 +++++ 1 file changed, 5 insertions(+) diff --git a/index.js b/index.js index 2dc86a96593..2c2ae23c7f4 100644 --- a/index.js +++ b/index.js @@ -282,7 +282,12 @@ RedisClient.prototype.on_ready = function () { // magically restore any modal commands from a previous connection if (this.selected_db !== null) { + // this trick works if and only if the following send_command + // never goes into the offline queue + var pub_sub_mode = this.pub_sub_mode; + this.pub_sub_mode = false; this.send_command('select', [this.selected_db]); + this.pub_sub_mode = pub_sub_mode; } if (this.pub_sub_mode === true) { // only emit "ready" when all subscriptions were made again From bd1e004e99593724962cc73622580330fd113418 Mon Sep 17 00:00:00 2001 From: DTrejo Date: Sun, 17 Feb 2013 19:06:13 -0500 Subject: [PATCH 0072/1748] regenerate lib/commands.js --- lib/commands.js | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/lib/commands.js b/lib/commands.js index f57cca964a5..4abfe68668a 100644 --- a/lib/commands.js +++ b/lib/commands.js @@ -1,4 +1,4 @@ -// This file was generated by ./generate_commands.js on Mon Aug 06 2012 15:04:06 GMT-0700 (PDT) +// This file was generated by ./generate_commands.js on Sun Feb 17 2013 19:04:47 GMT-0500 (EST) module.exports = [ "append", "auth", @@ -11,6 +11,8 @@ module.exports = [ "brpoplpush", "client kill", "client list", + "client getname", + "client setname", "config get", "config set", "config resetstat", From 6a09b2d1a316505b1bf60e762732eea8aaa65324 Mon Sep 17 00:00:00 2001 From: DTrejo Date: Sun, 17 Feb 2013 19:06:36 -0500 Subject: [PATCH 0073/1748] fix require in examples/eval.js --- examples/eval.js | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/examples/eval.js b/examples/eval.js index c1fbf8a5568..f7ab0b0b7b3 100644 --- a/examples/eval.js +++ b/examples/eval.js @@ -1,4 +1,4 @@ -var redis = require("./index"), +var redis = require("../index"), client = redis.createClient(); redis.debug_mode = true; From b5a57b40f7b7eee699670d6e1db1716f1bdeed4a Mon Sep 17 00:00:00 2001 From: Garrett Johnson Date: Sat, 3 Nov 2012 17:12:11 -0400 Subject: [PATCH 0074/1748] test.js: smembers reply should not assume order, b/c sets do not ensure order Closes #326. Signed-off-by: DTrejo --- test.js | 12 ++++++------ 1 file changed, 6 insertions(+), 6 deletions(-) diff --git a/test.js b/test.js index 94bd19d6824..2e802b6b635 100644 --- a/test.js +++ b/test.js @@ -994,16 +994,16 @@ tests.SADD2 = function () { client.sadd("set0", ["member0", "member1", "member2"], require_number(3, name)); client.smembers("set0", function (err, res) { assert.strictEqual(res.length, 3); - assert.strictEqual(res[0], "member0"); - assert.strictEqual(res[1], "member1"); - assert.strictEqual(res[2], "member2"); + assert.ok(~res.indexOf("member0")); + assert.ok(~res.indexOf("member1")); + assert.ok(~res.indexOf("member2")); }); client.SADD("set1", ["member0", "member1", "member2"], require_number(3, name)); client.smembers("set1", function (err, res) { assert.strictEqual(res.length, 3); - assert.strictEqual(res[0], "member0"); - assert.strictEqual(res[1], "member1"); - assert.strictEqual(res[2], "member2"); + assert.ok(~res.indexOf("member0")); + assert.ok(~res.indexOf("member1")); + assert.ok(~res.indexOf("member2")); next(name); }); }; From 0f5b43a68cf80401c576776acdcefab84b2b9305 Mon Sep 17 00:00:00 2001 From: Nitesh Date: Thu, 27 Sep 2012 23:35:38 +0530 Subject: [PATCH 0075/1748] test.js: slowlog. Closes #295 Signed-off-by: DTrejo --- test.js | 15 +++++++++++++++ 1 file changed, 15 insertions(+) diff --git a/test.js b/test.js index 2e802b6b635..77789b3847f 100644 --- a/test.js +++ b/test.js @@ -1586,6 +1586,21 @@ tests.ENABLE_OFFLINE_QUEUE_FALSE = function () { }); }; +tests.SLOWLOG = function () { + var name = "SLOWLOG"; + client.slowlog('reset',require_string("OK", name)); + client.config('set', 'slowlog-log-slower-than', 0,require_string("OK", name)); + client.set('foo','bar',require_string("OK", name)); + client.get('foo',require_string("bar", name)); + client.slowlog('get',function(err,res){ + assert.equal(res.length, 4, name); + assert.equal(res[0][3].length, 2, name); + assert.deepEqual(res[1][3], ['set', 'foo', 'bar'], name); + assert.deepEqual(res[3][3], ['slowlog', 'reset'], name); + next(name); + }); +} + // TODO - need a better way to test auth, maybe auto-config a local Redis server or something. // Yes, this is the real password. Please be nice, thanks. tests.auth = function () { From 92ac62541dc10fd31b31dc9318b34c9a7b44370f Mon Sep 17 00:00:00 2001 From: DTrejo Date: Sat, 23 Feb 2013 21:06:36 -0500 Subject: [PATCH 0076/1748] Fix #381. slowlog test failure on first run. --- test.js | 23 ++++++++++++----------- 1 file changed, 12 insertions(+), 11 deletions(-) diff --git a/test.js b/test.js index 77789b3847f..0217ca24f03 100644 --- a/test.js +++ b/test.js @@ -1588,17 +1588,18 @@ tests.ENABLE_OFFLINE_QUEUE_FALSE = function () { tests.SLOWLOG = function () { var name = "SLOWLOG"; - client.slowlog('reset',require_string("OK", name)); - client.config('set', 'slowlog-log-slower-than', 0,require_string("OK", name)); - client.set('foo','bar',require_string("OK", name)); - client.get('foo',require_string("bar", name)); - client.slowlog('get',function(err,res){ - assert.equal(res.length, 4, name); - assert.equal(res[0][3].length, 2, name); - assert.deepEqual(res[1][3], ['set', 'foo', 'bar'], name); - assert.deepEqual(res[3][3], ['slowlog', 'reset'], name); - next(name); - }); + client.config("set", "slowlog-log-slower-than", 0, require_string("OK", name)); + client.slowlog("reset", require_string("OK", name)); + client.set("foo", "bar", require_string("OK", name)); + client.get("foo", require_string("bar", name)); + client.slowlog("get", function (err, res) { + assert.equal(res.length, 3, name); + assert.equal(res[0][3].length, 2, name); + assert.deepEqual(res[1][3], ["set", "foo", "bar"], name); + assert.deepEqual(res[2][3], ["slowlog", "reset"], name); + client.config("set", "slowlog-log-slower-than", 10000, require_string("OK", name)); + next(name); + }); } // TODO - need a better way to test auth, maybe auto-config a local Redis server or something. From 2a12ca39aab525740bc2e4a2bb5165e86e356472 Mon Sep 17 00:00:00 2001 From: Bryce Baril Date: Tue, 19 Feb 2013 13:51:08 -0800 Subject: [PATCH 0077/1748] Fixing tests for Redis version 2.6.5+ MULTI/EXEC transaction failure compatibility. Signed-off-by: DTrejo --- test.js | 66 ++++++++++++++++++++++++++++++++++++++++++--------------- 1 file changed, 49 insertions(+), 17 deletions(-) diff --git a/test.js b/test.js index 0217ca24f03..29ca258f4b0 100644 --- a/test.js +++ b/test.js @@ -19,6 +19,16 @@ var redis = require("./index"), // Set this to truthy to see the wire protocol and other debugging info redis.debug_mode = process.argv[2]; +function server_version_at_least(connection, desired_version) { + // Return true if the server version >= desired_version + var version = connection.server_info.versions; + for (var i = 0; i < 3; i++) { + if (version[i] > desired_version[i]) return true; + if (version[i] < desired_version[i]) return false; + } + return true; +} + function buffers_to_strings(arr) { return arr.map(function (val) { return val.toString(); @@ -124,16 +134,27 @@ tests.MULTI_1 = function () { multi1.set("foo2", require_error(name)); multi1.incr("multifoo", require_number(11, name)); multi1.incr("multibar", require_number(21, name)); - multi1.exec(); - - // Confirm that the previous command, while containing an error, still worked. - multi2 = client.multi(); - multi2.incr("multibar", require_number(22, name)); - multi2.incr("multifoo", require_number(12, name)); - multi2.exec(function (err, replies) { - assert.strictEqual(22, replies[0]); - assert.strictEqual(12, replies[1]); - next(name); + multi1.exec(function () { + require_error(name); + + // Redis 2.6.5+ will abort transactions with errors + // see: http://redis.io/topics/transactions + var multibar_expected = 22; + var multifoo_expected = 12; + if (server_version_at_least(client, [2, 6, 5])) { + multibar_expected = 1; + multifoo_expected = 1; + } + + // Confirm that the previous command, while containing an error, still worked. + multi2 = client.multi(); + multi2.incr("multibar", require_number(multibar_expected, name)); + multi2.incr("multifoo", require_number(multifoo_expected, name)); + multi2.exec(function (err, replies) { + assert.strictEqual(multibar_expected, replies[0]); + assert.strictEqual(multifoo_expected, replies[1]); + next(name); + }); }); }; @@ -151,12 +172,18 @@ tests.MULTI_2 = function () { ["incr", "multifoo", require_number(13, name)], ["incr", "multibar", require_number(23, name)] ]).exec(function (err, replies) { - assert.strictEqual(2, replies[0].length, name); - assert.strictEqual("12", replies[0][0].toString(), name); - assert.strictEqual("22", replies[0][1].toString(), name); + if (server_version_at_least(client, [2, 6, 5])) { + assert.notEqual(err, null, name); + assert.equal(replies, undefined, name); + } + else { + assert.strictEqual(2, replies[0].length, name); + assert.strictEqual("12", replies[0][0].toString(), name); + assert.strictEqual("22", replies[0][1].toString(), name); - assert.strictEqual("13", replies[1].toString()); - assert.strictEqual("23", replies[2].toString()); + assert.strictEqual("13", replies[1].toString()); + assert.strictEqual("23", replies[2].toString()); + } next(name); }); }; @@ -248,7 +275,7 @@ tests.MULTI_6 = function () { tests.EVAL_1 = function () { var name = "EVAL_1"; - if (client.server_info.versions[0] >= 2 && client.server_info.versions[1] >= 5) { + if (server_version_at_least(client, [2, 5, 0])) { // test {EVAL - Lua integer -> Redis protocol type conversion} client.eval("return 100.5", 0, require_number(100, name)); // test {EVAL - Lua string -> Redis protocol type conversion} @@ -372,7 +399,7 @@ tests.EVAL_1 = function () { tests.WATCH_MULTI = function () { var name = 'WATCH_MULTI', multi; - if (client.server_info.versions[0] >= 2 && client.server_info.versions[1] >= 1) { + if (server_version_at_least(client, [2, 1, 0])) { client.watch(name); client.incr(name); multi = client.multi(); @@ -1427,6 +1454,11 @@ tests.SORT = function () { tests.MONITOR = function () { var name = "MONITOR", responses = [], monitor_client; + if (!server_version_at_least(client, [2, 6, 0])) { + console.log("Skipping monitor for old Redis server version <= 2.6.x"); + return next(name); + } + monitor_client = redis.createClient(); monitor_client.monitor(function (err, res) { client.mget("some", "keys", "foo", "bar"); From f3c298d0888bef44880f58a9d47b5f529687d089 Mon Sep 17 00:00:00 2001 From: Bryce Baril Date: Tue, 19 Feb 2013 13:52:10 -0800 Subject: [PATCH 0078/1748] Removing order requirement for KEYS test Close #378. Signed-off-by: DTrejo --- test.js | 9 +++++---- 1 file changed, 5 insertions(+), 4 deletions(-) diff --git a/test.js b/test.js index 29ca258f4b0..4a47dcfa479 100644 --- a/test.js +++ b/test.js @@ -171,12 +171,13 @@ tests.MULTI_2 = function () { ["set", "foo2", require_error(name)], ["incr", "multifoo", require_number(13, name)], ["incr", "multibar", require_number(23, name)] + ]).exec(function (err, replies) { + if (server_version_at_least(client, [2, 6, 5])) { assert.notEqual(err, null, name); assert.equal(replies, undefined, name); - } - else { + } else { assert.strictEqual(2, replies[0].length, name); assert.strictEqual("12", replies[0][0].toString(), name); assert.strictEqual("22", replies[0][1].toString(), name); @@ -810,8 +811,8 @@ tests.KEYS = function () { client.KEYS(["test keys*"], function (err, results) { assert.strictEqual(null, err, "result sent back unexpected error: " + err); assert.strictEqual(2, results.length, name); - assert.strictEqual("test keys 1", results[0].toString(), name); - assert.strictEqual("test keys 2", results[1].toString(), name); + assert.ok(~results.indexOf("test keys 1")); + assert.ok(~results.indexOf("test keys 2")); next(name); }); }; From 938c0526a08d7e39ace5e8c72822096a299d1832 Mon Sep 17 00:00:00 2001 From: DTrejo Date: Sat, 23 Feb 2013 22:04:46 -0500 Subject: [PATCH 0079/1748] EVAL: allow parameters as an array. Close #368. Signed-off-by: DTrejo --- examples/eval.js | 5 +++++ index.js | 4 ++++ test.js | 9 +++++++++ 3 files changed, 18 insertions(+) diff --git a/examples/eval.js b/examples/eval.js index f7ab0b0b7b3..a3ff6b0793d 100644 --- a/examples/eval.js +++ b/examples/eval.js @@ -7,3 +7,8 @@ client.eval("return 100.5", 0, function (err, res) { console.dir(err); console.dir(res); }); + +client.eval([ "return 100.5", 0 ], function (err, res) { + console.dir(err); + console.dir(res); +}); diff --git a/index.js b/index.js index 2dc86a96593..4f920bc4277 100644 --- a/index.js +++ b/index.js @@ -1074,6 +1074,10 @@ RedisClient.prototype.eval = RedisClient.prototype.EVAL = function () { callback = args.pop(); } + if (Array.isArray(args[0])) { + args = args[0]; + } + // replace script source with sha value var source = args[0]; args[0] = crypto.createHash("sha1").update(source).digest("hex"); diff --git a/test.js b/test.js index 4a47dcfa479..033bef03a26 100644 --- a/test.js +++ b/test.js @@ -309,6 +309,15 @@ tests.EVAL_1 = function () { assert.strictEqual("d", res[3], name); }); + // test {EVAL - parameters in array format gives same result} + client.eval(["return {KEYS[1],KEYS[2],ARGV[1],ARGV[2]}", 2, "a", "b", "c", "d"], function (err, res) { + assert.strictEqual(4, res.length, name); + assert.strictEqual("a", res[0], name); + assert.strictEqual("b", res[1], name); + assert.strictEqual("c", res[2], name); + assert.strictEqual("d", res[3], name); + }); + // prepare sha sum for evalsha cache test var source = "return redis.call('get', 'sha test')", sha = crypto.createHash('sha1').update(source).digest('hex'); From f0ae6642f91c2375a0becb7596060aab1ff27cf5 Mon Sep 17 00:00:00 2001 From: Jonas Dohse Date: Thu, 10 Jan 2013 15:35:15 +0000 Subject: [PATCH 0080/1748] Use first word of multi word commands Close #363. Signed-off-by: DTrejo --- README.md | 9 +++++++++ index.js | 4 +++- test.js | 16 ++++++++++++++++ 3 files changed, 28 insertions(+), 1 deletion(-) diff --git a/README.md b/README.md index 46e7018c4b3..aab10658a23 100644 --- a/README.md +++ b/README.md @@ -543,6 +543,15 @@ This will display: `send command` is data sent into Redis and `on_data` is data received from Redis. +## Multi-word commands + +To execute redis multi-word commands like `SCRIPT LOAD` or `CLIENT LIST` pass +the second word as first parameter: + + client.script('load', 'return 1'); + client.multi().script('load', 'return 1').exec(...); + client.multi([['script', 'load', 'return 1']]).exec(...); + ## client.send_command(command_name, args, callback) Used internally to send commands to Redis. For convenience, nearly all commands that are published on the Redis diff --git a/index.js b/index.js index 4f920bc4277..b6a01819524 100644 --- a/index.js +++ b/index.js @@ -865,7 +865,9 @@ commands = set_union(["get", "set", "setnx", "setex", "append", "strlen", "del", "persist", "slaveof", "debug", "config", "subscribe", "unsubscribe", "psubscribe", "punsubscribe", "publish", "watch", "unwatch", "cluster", "restore", "migrate", "dump", "object", "client", "eval", "evalsha"], require("./lib/commands")); -commands.forEach(function (command) { +commands.forEach(function (fullCommand) { + var command = fullCommand.split(' ')[0]; + RedisClient.prototype[command] = function (args, callback) { if (Array.isArray(args) && typeof callback === "function") { return this.send_command(command, args, callback); diff --git a/test.js b/test.js index 033bef03a26..ba801b56c70 100644 --- a/test.js +++ b/test.js @@ -406,6 +406,22 @@ tests.EVAL_1 = function () { } }; +tests.SCRIPT_LOAD = function() { + if (server_version_at_least(bclient, [2, 5, 0])) { + var name = "SCRIPT_LOAD", + command = "return 1", + commandSha = crypto.createHash('sha1').update(command).digest('hex'); + + bclient.script("load", command, function(err, result) { + assert.strictEqual(result.toString(), commandSha); + next(name); + }); + } else { + console.log("Skipping " + name + " because server version isn't new enough."); + next(name); + } +}; + tests.WATCH_MULTI = function () { var name = 'WATCH_MULTI', multi; From 837cec36b642cf6ca5eace93bb59378d1c3e477e Mon Sep 17 00:00:00 2001 From: Tom Leach Date: Mon, 31 Dec 2012 14:43:48 -0500 Subject: [PATCH 0081/1748] Detect is an incoming "reply" is in fact a pubsub message. If so, do not pop the command queue. This fixes an issue where the command queue gets popped prematurely by pubsub messages, leading to callbacks for those commands not being invoked. Close #360. Signed-off-by: DTrejo --- index.js | 14 ++++++++++++-- test.js | 38 +++++++++++++++++++++++++++++++++++++- 2 files changed, 49 insertions(+), 3 deletions(-) diff --git a/index.js b/index.js index b6a01819524..bb224a5d31e 100644 --- a/index.js +++ b/index.js @@ -562,8 +562,18 @@ function reply_to_strings(reply) { RedisClient.prototype.return_reply = function (reply) { var command_obj, len, type, timestamp, argindex, args, queue_len; - command_obj = this.command_queue.shift(), - queue_len = this.command_queue.getLength(); + // If the "reply" here is actually a message received asynchronously due to a + // pubsub subscription, don't pop the command queue as we'll only be consuming + // the head command prematurely. + if (Array.isArray(reply) && reply.length > 0 && reply[0]) { + type = reply[0].toString(); + } + + if (type !== 'message' && type !== 'pmessage') { + command_obj = this.command_queue.shift(); + } + + queue_len = this.command_queue.getLength(); if (this.pub_sub_mode === false && queue_len === 0) { this.emit("idle"); diff --git a/test.js b/test.js index ba801b56c70..5967ed329a3 100644 --- a/test.js +++ b/test.js @@ -16,6 +16,7 @@ var redis = require("./index"), ended = false, next, cur_start, run_next_test, all_tests, all_start, test_count; + // Set this to truthy to see the wire protocol and other debugging info redis.debug_mode = process.argv[2]; @@ -94,6 +95,18 @@ function last(name, fn) { }; } +// Wraps the given callback in a timeout. If the returned function +// is not called within the timeout period, we fail the named test. +function with_timeout(name, cb, millis) { + var timeoutId = setTimeout(function() { + assert.fail("Callback timed out!", name); + }, millis); + return function() { + clearTimeout(timeoutId); + cb.apply(this, arguments); + }; +} + next = function next(name) { console.log(" \x1b[33m" + (Date.now() - cur_start) + "\x1b[0m ms"); run_next_test(); @@ -720,10 +733,33 @@ tests.SUB_UNSUB_SUB = function () { client3.on('message', function (channel, message) { assert.strictEqual(channel, 'chan3'); assert.strictEqual(message, 'foo'); + client3.removeAllListeners(); next(name); }); }; +tests.SUB_UNSUB_MSG_SUB = function () { + var name = "SUB_UNSUB_MSG_SUB"; + client3.subscribe('chan8'); + client3.subscribe('chan9'); + client3.unsubscribe('chan9'); + client2.publish('chan8', 'something'); + client3.subscribe('chan9', with_timeout(name, function (err, results) { + next(name); + }, 2000)); +}; + +tests.PSUB_UNSUB_PMSG_SUB = function () { + var name = "PSUB_UNSUB_PMSG_SUB"; + client3.psubscribe('abc*'); + client3.subscribe('xyz'); + client3.unsubscribe('xyz'); + client2.publish('abcd', 'something'); + client3.subscribe('xyz', with_timeout(name, function (err, results) { + next(name); + }, 2000)); +}; + tests.SUBSCRIBE_QUIT = function () { var name = "SUBSCRIBE_QUIT"; client3.on("end", function () { @@ -764,7 +800,7 @@ tests.SUBSCRIBE_CLOSE_RESUBSCRIBE = function () { c2.quit(); assert.fail("test failed"); } - }) + }); c1.subscribe("chan1", "chan2"); From a02b0f57e4b1ad90312cc8c2cae0413760c4da8f Mon Sep 17 00:00:00 2001 From: DTrejo Date: Sat, 23 Feb 2013 22:41:53 -0500 Subject: [PATCH 0082/1748] test.js: early return if command not supported --- test.js | 265 ++++++++++++++++++++++++++++---------------------------- 1 file changed, 132 insertions(+), 133 deletions(-) diff --git a/test.js b/test.js index 5967ed329a3..1bdc4502e8a 100644 --- a/test.js +++ b/test.js @@ -289,165 +289,164 @@ tests.MULTI_6 = function () { tests.EVAL_1 = function () { var name = "EVAL_1"; - if (server_version_at_least(client, [2, 5, 0])) { - // test {EVAL - Lua integer -> Redis protocol type conversion} - client.eval("return 100.5", 0, require_number(100, name)); - // test {EVAL - Lua string -> Redis protocol type conversion} - client.eval("return 'hello world'", 0, require_string("hello world", name)); - // test {EVAL - Lua true boolean -> Redis protocol type conversion} - client.eval("return true", 0, require_number(1, name)); - // test {EVAL - Lua false boolean -> Redis protocol type conversion} - client.eval("return false", 0, require_null(name)); - // test {EVAL - Lua status code reply -> Redis protocol type conversion} - client.eval("return {ok='fine'}", 0, require_string("fine", name)); - // test {EVAL - Lua error reply -> Redis protocol type conversion} - client.eval("return {err='this is an error'}", 0, require_error(name)); - // test {EVAL - Lua table -> Redis protocol type conversion} - client.eval("return {1,2,3,'ciao',{1,2}}", 0, function (err, res) { - assert.strictEqual(5, res.length, name); - assert.strictEqual(1, res[0], name); - assert.strictEqual(2, res[1], name); - assert.strictEqual(3, res[2], name); - assert.strictEqual("ciao", res[3], name); - assert.strictEqual(2, res[4].length, name); - assert.strictEqual(1, res[4][0], name); - assert.strictEqual(2, res[4][1], name); - }); - // test {EVAL - Are the KEYS and ARGS arrays populated correctly?} - client.eval("return {KEYS[1],KEYS[2],ARGV[1],ARGV[2]}", 2, "a", "b", "c", "d", function (err, res) { - assert.strictEqual(4, res.length, name); - assert.strictEqual("a", res[0], name); - assert.strictEqual("b", res[1], name); - assert.strictEqual("c", res[2], name); - assert.strictEqual("d", res[3], name); - }); + if (!server_version_at_least(client, [2, 5, 0])) { + console.log("Skipping " + name + " for old Redis server version < 2.5.x"); + return next(name); + } - // test {EVAL - parameters in array format gives same result} - client.eval(["return {KEYS[1],KEYS[2],ARGV[1],ARGV[2]}", 2, "a", "b", "c", "d"], function (err, res) { - assert.strictEqual(4, res.length, name); - assert.strictEqual("a", res[0], name); - assert.strictEqual("b", res[1], name); - assert.strictEqual("c", res[2], name); - assert.strictEqual("d", res[3], name); + // test {EVAL - Lua integer -> Redis protocol type conversion} + client.eval("return 100.5", 0, require_number(100, name)); + // test {EVAL - Lua string -> Redis protocol type conversion} + client.eval("return 'hello world'", 0, require_string("hello world", name)); + // test {EVAL - Lua true boolean -> Redis protocol type conversion} + client.eval("return true", 0, require_number(1, name)); + // test {EVAL - Lua false boolean -> Redis protocol type conversion} + client.eval("return false", 0, require_null(name)); + // test {EVAL - Lua status code reply -> Redis protocol type conversion} + client.eval("return {ok='fine'}", 0, require_string("fine", name)); + // test {EVAL - Lua error reply -> Redis protocol type conversion} + client.eval("return {err='this is an error'}", 0, require_error(name)); + // test {EVAL - Lua table -> Redis protocol type conversion} + client.eval("return {1,2,3,'ciao',{1,2}}", 0, function (err, res) { + assert.strictEqual(5, res.length, name); + assert.strictEqual(1, res[0], name); + assert.strictEqual(2, res[1], name); + assert.strictEqual(3, res[2], name); + assert.strictEqual("ciao", res[3], name); + assert.strictEqual(2, res[4].length, name); + assert.strictEqual(1, res[4][0], name); + assert.strictEqual(2, res[4][1], name); + }); + // test {EVAL - Are the KEYS and ARGS arrays populated correctly?} + client.eval("return {KEYS[1],KEYS[2],ARGV[1],ARGV[2]}", 2, "a", "b", "c", "d", function (err, res) { + assert.strictEqual(4, res.length, name); + assert.strictEqual("a", res[0], name); + assert.strictEqual("b", res[1], name); + assert.strictEqual("c", res[2], name); + assert.strictEqual("d", res[3], name); + }); + + // test {EVAL - parameters in array format gives same result} + client.eval(["return {KEYS[1],KEYS[2],ARGV[1],ARGV[2]}", 2, "a", "b", "c", "d"], function (err, res) { + assert.strictEqual(4, res.length, name); + assert.strictEqual("a", res[0], name); + assert.strictEqual("b", res[1], name); + assert.strictEqual("c", res[2], name); + assert.strictEqual("d", res[3], name); + }); + + // prepare sha sum for evalsha cache test + var source = "return redis.call('get', 'sha test')", + sha = crypto.createHash('sha1').update(source).digest('hex'); + + client.set("sha test", "eval get sha test", function (err, res) { + if (err) throw err; + // test {EVAL - is Lua able to call Redis API?} + client.eval(source, 0, function (err, res) { + require_string("eval get sha test", name)(err, res); + // test {EVALSHA - Can we call a SHA1 if already defined?} + client.evalsha(sha, 0, require_string("eval get sha test", name)); + // test {EVALSHA - Do we get an error on non defined SHA1?} + client.evalsha("ffffffffffffffffffffffffffffffffffffffff", 0, require_error(name)); }); + }); - // prepare sha sum for evalsha cache test - var source = "return redis.call('get', 'sha test')", - sha = crypto.createHash('sha1').update(source).digest('hex'); - - client.set("sha test", "eval get sha test", function (err, res) { + // test {EVAL - Redis integer -> Lua type conversion} + client.set("incr key", 0, function (err, reply) { + if (err) throw err; + client.eval("local foo = redis.call('incr','incr key')\n" + "return {type(foo),foo}", 0, function (err, res) { if (err) throw err; - // test {EVAL - is Lua able to call Redis API?} - client.eval(source, 0, function (err, res) { - require_string("eval get sha test", name)(err, res); - // test {EVALSHA - Can we call a SHA1 if already defined?} - client.evalsha(sha, 0, require_string("eval get sha test", name)); - // test {EVALSHA - Do we get an error on non defined SHA1?} - client.evalsha("ffffffffffffffffffffffffffffffffffffffff", 0, require_error(name)); - }); + assert.strictEqual(2, res.length, name); + assert.strictEqual("number", res[0], name); + assert.strictEqual(1, res[1], name); }); + }); - // test {EVAL - Redis integer -> Lua type conversion} - client.set("incr key", 0, function (err, reply) { + client.set("bulk reply key", "bulk reply value", function (err, res) { + // test {EVAL - Redis bulk -> Lua type conversion} + client.eval("local foo = redis.call('get','bulk reply key'); return {type(foo),foo}", 0, function (err, res) { if (err) throw err; - client.eval("local foo = redis.call('incr','incr key')\n" + "return {type(foo),foo}", 0, function (err, res) { - if (err) throw err; - assert.strictEqual(2, res.length, name); - assert.strictEqual("number", res[0], name); - assert.strictEqual(1, res[1], name); - }); + assert.strictEqual(2, res.length, name); + assert.strictEqual("string", res[0], name); + assert.strictEqual("bulk reply value", res[1], name); }); + }); - client.set("bulk reply key", "bulk reply value", function (err, res) { - // test {EVAL - Redis bulk -> Lua type conversion} - client.eval("local foo = redis.call('get','bulk reply key'); return {type(foo),foo}", 0, function (err, res) { - if (err) throw err; - assert.strictEqual(2, res.length, name); - assert.strictEqual("string", res[0], name); - assert.strictEqual("bulk reply value", res[1], name); + // test {EVAL - Redis multi bulk -> Lua type conversion} + client.multi() + .del("mylist") + .rpush("mylist", "a") + .rpush("mylist", "b") + .rpush("mylist", "c") + .exec(function (err, replies) { + if (err) throw err; + client.eval("local foo = redis.call('lrange','mylist',0,-1); return {type(foo),foo[1],foo[2],foo[3],# foo}", 0, function (err, res) { + assert.strictEqual(5, res.length, name); + assert.strictEqual("table", res[0], name); + assert.strictEqual("a", res[1], name); + assert.strictEqual("b", res[2], name); + assert.strictEqual("c", res[3], name); + assert.strictEqual(3, res[4], name); }); }); - - // test {EVAL - Redis multi bulk -> Lua type conversion} - client.multi() - .del("mylist") - .rpush("mylist", "a") - .rpush("mylist", "b") - .rpush("mylist", "c") - .exec(function (err, replies) { - if (err) throw err; - client.eval("local foo = redis.call('lrange','mylist',0,-1); return {type(foo),foo[1],foo[2],foo[3],# foo}", 0, function (err, res) { - assert.strictEqual(5, res.length, name); - assert.strictEqual("table", res[0], name); - assert.strictEqual("a", res[1], name); - assert.strictEqual("b", res[2], name); - assert.strictEqual("c", res[3], name); - assert.strictEqual(3, res[4], name); - }); - }); - // test {EVAL - Redis status reply -> Lua type conversion} - client.eval("local foo = redis.call('set','mykey','myval'); return {type(foo),foo['ok']}", 0, function (err, res) { + // test {EVAL - Redis status reply -> Lua type conversion} + client.eval("local foo = redis.call('set','mykey','myval'); return {type(foo),foo['ok']}", 0, function (err, res) { + if (err) throw err; + assert.strictEqual(2, res.length, name); + assert.strictEqual("table", res[0], name); + assert.strictEqual("OK", res[1], name); + }); + // test {EVAL - Redis error reply -> Lua type conversion} + client.set("error reply key", "error reply value", function (err, res) { + if (err) throw err; + client.eval("local foo = redis.pcall('incr','error reply key'); return {type(foo),foo['err']}", 0, function (err, res) { if (err) throw err; assert.strictEqual(2, res.length, name); assert.strictEqual("table", res[0], name); - assert.strictEqual("OK", res[1], name); - }); - // test {EVAL - Redis error reply -> Lua type conversion} - client.set("error reply key", "error reply value", function (err, res) { - if (err) throw err; - client.eval("local foo = redis.pcall('incr','error reply key'); return {type(foo),foo['err']}", 0, function (err, res) { - if (err) throw err; - assert.strictEqual(2, res.length, name); - assert.strictEqual("table", res[0], name); - assert.strictEqual("ERR value is not an integer or out of range", res[1], name); - }); + assert.strictEqual("ERR value is not an integer or out of range", res[1], name); }); - // test {EVAL - Redis nil bulk reply -> Lua type conversion} - client.del("nil reply key", function (err, res) { + }); + // test {EVAL - Redis nil bulk reply -> Lua type conversion} + client.del("nil reply key", function (err, res) { + if (err) throw err; + client.eval("local foo = redis.call('get','nil reply key'); return {type(foo),foo == false}", 0, function (err, res) { if (err) throw err; - client.eval("local foo = redis.call('get','nil reply key'); return {type(foo),foo == false}", 0, function (err, res) { - if (err) throw err; - assert.strictEqual(2, res.length, name); - assert.strictEqual("boolean", res[0], name); - assert.strictEqual(1, res[1], name); - next(name); - }); + assert.strictEqual(2, res.length, name); + assert.strictEqual("boolean", res[0], name); + assert.strictEqual(1, res[1], name); + next(name); }); - } else { - console.log("Skipping " + name + " because server version isn't new enough."); - next(name); - } + }); }; tests.SCRIPT_LOAD = function() { - if (server_version_at_least(bclient, [2, 5, 0])) { - var name = "SCRIPT_LOAD", - command = "return 1", - commandSha = crypto.createHash('sha1').update(command).digest('hex'); + var name = "SCRIPT_LOAD", + command = "return 1", + commandSha = crypto.createHash('sha1').update(command).digest('hex'); - bclient.script("load", command, function(err, result) { - assert.strictEqual(result.toString(), commandSha); - next(name); - }); - } else { - console.log("Skipping " + name + " because server version isn't new enough."); - next(name); + if (!server_version_at_least(client, [2, 6, 0])) { + console.log("Skipping " + name + " for old Redis server version < 2.6.x"); + return next(name); } + + bclient.script("load", command, function(err, result) { + assert.strictEqual(result.toString(), commandSha); + next(name); + }); }; tests.WATCH_MULTI = function () { var name = 'WATCH_MULTI', multi; - - if (server_version_at_least(client, [2, 1, 0])) { - client.watch(name); - client.incr(name); - multi = client.multi(); - multi.incr(name); - multi.exec(last(name, require_null(name))); - } else { - console.log("Skipping " + name + " because server version isn't new enough."); - next(name); + if (!server_version_at_least(client, [2, 2, 0])) { + console.log("Skipping " + name + " for old Redis server version < 2.2.x"); + return next(name); } + + client.watch(name); + client.incr(name); + multi = client.multi(); + multi.incr(name); + multi.exec(last(name, require_null(name))); }; tests.detect_buffers = function () { @@ -1517,7 +1516,7 @@ tests.MONITOR = function () { var name = "MONITOR", responses = [], monitor_client; if (!server_version_at_least(client, [2, 6, 0])) { - console.log("Skipping monitor for old Redis server version <= 2.6.x"); + console.log("Skipping " + name + " for old Redis server version < 2.6.x"); return next(name); } From 405011b64031639c19913d14b690b92ba03d209c Mon Sep 17 00:00:00 2001 From: DTrejo Date: Sat, 23 Feb 2013 22:56:08 -0500 Subject: [PATCH 0083/1748] Revert "hmset throws/errors out on non-string values. fixes #218" Reverting because this was a documentation problem, not a problem with the code. Performance-wise, this is faster than the approach in #345, though it may cause users more trouble. This is okay, if someone opens an issue we can point them to the docs. This reverts commit b60e001fa08403240a6ecbf0c36d1c08ed8ccd9d. Conflicts: index.js test.js --- README.md | 2 +- index.js | 8 -------- test.js | 18 ------------------ 3 files changed, 1 insertion(+), 27 deletions(-) diff --git a/README.md b/README.md index aab10658a23..82fed0b080d 100644 --- a/README.md +++ b/README.md @@ -282,7 +282,7 @@ Output: Multiple values in a hash can be set by supplying an object: client.HMSET(key2, { - "0123456789": "abcdefghij", // NOTE: the key and value must both be strings + "0123456789": "abcdefghij", // NOTE: key and value will be coerced to strings "some manner of key": "a type of value" }); diff --git a/index.js b/index.js index bb224a5d31e..4135dc1ec90 100644 --- a/index.js +++ b/index.js @@ -957,14 +957,6 @@ RedisClient.prototype.hmset = function (args, callback) { for (i = 0, il = tmp_keys.length; i < il ; i++) { key = tmp_keys[i]; tmp_args.push(key); - if (typeof args[1][key] !== "string") { - var err = new Error("hmset expected value to be a string", key, ":", args[1][key]); - if (callback) { - return callback(err); - } else { - throw err; - } - } tmp_args.push(args[1][key]); } args = tmp_args; diff --git a/test.js b/test.js index 1bdc4502e8a..05a09ef6c1d 100644 --- a/test.js +++ b/test.js @@ -1616,24 +1616,6 @@ tests.OPTIONAL_CALLBACK_UNDEFINED = function () { client.get("op_cb2", last(name, require_string("y", name))); }; -tests.HMSET_THROWS_ON_NON_STRINGS = function () { - var name = "HMSET_THROWS_ON_NON_STRINGS"; - var hash = name; - var data = { "a": [ "this is not a string" ] }; - - client.hmset(hash, data, cb); - function cb(e, r) { - assert(e); // should be an error! - } - - // alternative way it throws - function thrower() { - client.hmset(hash, data); - } - assert.throws(thrower); - next(name); -}; - tests.ENABLE_OFFLINE_QUEUE_TRUE = function () { var name = "ENABLE_OFFLINE_QUEUE_TRUE"; var cli = redis.createClient(9999, null, { From 87132e2b0351d0ac1342a41d1a3762ef59d4fb99 Mon Sep 17 00:00:00 2001 From: Bryce Baril Date: Sat, 23 Feb 2013 19:17:27 -0800 Subject: [PATCH 0084/1748] Add hiredis guard to INCR test Signed-off-by: DTrejo --- test.js | 5 +++++ 1 file changed, 5 insertions(+) diff --git a/test.js b/test.js index 05a09ef6c1d..5d0d4a692fc 100644 --- a/test.js +++ b/test.js @@ -127,6 +127,11 @@ tests.FLUSHDB = function () { tests.INCR = function () { var name = "INCR"; + if (bclient.reply_parser.name == "hiredis") { + console.log("Skipping INCR buffer test with hiredis"); + return next(name); + } + // Test incr with the maximum JavaScript number value. Since we are // returning buffers we should get back one more as a Buffer. bclient.set("seq", "9007199254740992", function (err, result) { From 0c172f425cc43ef9c8cb3f6a91eb0eec0461b80e Mon Sep 17 00:00:00 2001 From: Bryce Baril Date: Sat, 23 Feb 2013 19:20:30 -0800 Subject: [PATCH 0085/1748] Fix parser incorrect buffer skip for MULTI/EXEC transaction errors with WATCH. Signed-off-by: DTrejo --- lib/parser/javascript.js | 3 +-- test.js | 35 +++++++++++++++++++++++++++++++++++ 2 files changed, 36 insertions(+), 2 deletions(-) diff --git a/lib/parser/javascript.js b/lib/parser/javascript.js index 256c3ea9ad3..7bfdb57edf7 100644 --- a/lib/parser/javascript.js +++ b/lib/parser/javascript.js @@ -117,7 +117,6 @@ ReplyParser.prototype._parseResult = function (type) { } if (packetHeader.size < 0) { - this._offset += 2; return null; } @@ -295,4 +294,4 @@ ReplyParser.prototype.send_error = function (reply) { ReplyParser.prototype.send_reply = function (reply) { this.emit("reply", reply); -}; \ No newline at end of file +}; diff --git a/test.js b/test.js index 5d0d4a692fc..c8a98436546 100644 --- a/test.js +++ b/test.js @@ -454,6 +454,41 @@ tests.WATCH_MULTI = function () { multi.exec(last(name, require_null(name))); }; +tests.WATCH_TRANSACTION = function () { + var name = "WATCH_TRANSACTION"; + + if (!server_version_at_least(client, [2, 1, 0])) { + console.log("Skipping " + name + " because server version isn't new enough."); + return next(name); + } + + // Test WATCH command aborting transactions, look for parser offset errors. + + client.set("unwatched", 200); + + client.set(name, 0); + client.watch(name); + client.incr(name); + var multi = client.multi() + .incr(name) + .exec(function (err, replies) { + // Failure expected because of pre-multi incr + assert.strictEqual(replies, null, "Aborted transaction multi-bulk reply should be null."); + + client.get("unwatched", function (err, reply) { + assert.equal(err, null, name); + assert.equal(reply, 200, "Expected 200, got " + reply); + next(name); + }); + }); + + client.set("unrelated", 100, function (err, reply) { + assert.equal(err, null, name); + assert.equal(reply, "OK", "Expected 'OK', got " + reply); + }); +}; + + tests.detect_buffers = function () { var name = "detect_buffers", detect_client = redis.createClient(null, null, {detect_buffers: true}); From 9127f34393989718bac537e1a7361a5bdba72c7d Mon Sep 17 00:00:00 2001 From: Bryce Baril Date: Sat, 23 Feb 2013 21:13:20 -0800 Subject: [PATCH 0086/1748] Parser should only catch parser errors and bubble the rest to the caller. Signed-off-by: DTrejo --- lib/parser/javascript.js | 24 ++++++++++++++---------- test.js | 28 ++++++++++++++++++++++++++++ 2 files changed, 42 insertions(+), 10 deletions(-) diff --git a/lib/parser/javascript.js b/lib/parser/javascript.js index 7bfdb57edf7..de8e4602efb 100644 --- a/lib/parser/javascript.js +++ b/lib/parser/javascript.js @@ -24,6 +24,12 @@ util.inherits(ReplyParser, events.EventEmitter); exports.Parser = ReplyParser; +function IncompleteReadBuffer(message) { + this.name = "IncompleteReadBuffer"; + this.message = message; +} +util.inherits(IncompleteReadBuffer, Error); + // Buffer.toString() is quite slow for small strings function small_toString(buf, start, end) { var tmp = "", i; @@ -48,7 +54,7 @@ ReplyParser.prototype._parseResult = function (type) { if (end > this._buffer.length) { this._offset = start; - throw new Error("too far"); + throw new IncompleteReadBuffer("Wait for more data."); } if (this.options.return_buffers) { @@ -70,7 +76,7 @@ ReplyParser.prototype._parseResult = function (type) { if (end > this._buffer.length) { this._offset = start; - throw new Error("too far"); + throw new IncompleteReadBuffer("Wait for more data."); } if (this.options.return_buffers) { @@ -99,7 +105,7 @@ ReplyParser.prototype._parseResult = function (type) { if (end > this._buffer.length) { this._offset = offset; - throw new Error("too far"); + throw new IncompleteReadBuffer("Wait for more data."); } if (this.options.return_buffers) { @@ -129,7 +135,7 @@ ReplyParser.prototype._parseResult = function (type) { ntype = this._buffer[this._offset++]; if (this._offset > this._buffer.length) { - throw new Error("too far"); + throw new IncompleteReadBuffer("Wait for more data."); } res = this._parseResult(ntype); if (res === undefined) { @@ -202,16 +208,14 @@ ReplyParser.prototype.execute = function (buffer) { ret = this._parseResult(type); - if (ret === -1) { - this._offset = offset; - break; - } - this.send_reply(ret); } } catch (err) { // catch the error (not enough data), rewind, and wait // for the next packet to appear + if (! (err instanceof IncompleteReadBuffer)) { + throw err; + } this._offset = offset; break; } @@ -272,7 +276,7 @@ ReplyParser.prototype._packetEndOffset = function () { offset++; if (offset >= this._buffer.length) { - throw new Error("didn't see LF after NL reading multi bulk count (" + offset + " => " + this._buffer.length + ", " + this._offset + ")"); + throw new IncompleteReadBuffer("didn't see LF after NL reading multi bulk count (" + offset + " => " + this._buffer.length + ", " + this._offset + ")"); } } diff --git a/test.js b/test.js index c8a98436546..bb0ca604bfa 100644 --- a/test.js +++ b/test.js @@ -291,6 +291,34 @@ tests.MULTI_6 = function () { }); }; +tests.FWD_ERRORS_1 = function () { + var name = "FWD_ERRORS_1"; + + var toThrow = new Error("Forced exception"); + var recordedError = null; + + var originalHandler = client3.listeners("error").pop(); + client3.once("error", function (err) { + recordedError = err; + }); + + client3.on("message", function (channel, data) { + console.log("incoming"); + if (channel == name) { + assert.equal(data, "Some message"); + throw toThrow; + } + }); + client3.subscribe(name); + + client.publish(name, "Some message"); + setTimeout(function () { + client3.listeners("error").push(originalHandler); + assert.equal(recordedError, toThrow, "Should have caught our forced exception"); + next(name); + }, 150); +}; + tests.EVAL_1 = function () { var name = "EVAL_1"; From 92ed0befc192409d705a807feb025dbffbebf380 Mon Sep 17 00:00:00 2001 From: Bryce Baril Date: Sat, 23 Feb 2013 21:15:11 -0800 Subject: [PATCH 0087/1748] In nested MULTIBULK buffers, correctly recurse on an incomplete read buffer. Signed-off-by: DTrejo --- lib/parser/javascript.js | 10 +++++----- test.js | 29 +++++++++++++++++++++++++++++ 2 files changed, 34 insertions(+), 5 deletions(-) diff --git a/lib/parser/javascript.js b/lib/parser/javascript.js index de8e4602efb..0990cc098da 100644 --- a/lib/parser/javascript.js +++ b/lib/parser/javascript.js @@ -117,15 +117,15 @@ ReplyParser.prototype._parseResult = function (type) { offset = this._offset; packetHeader = new Packet(type, this.parseHeader()); - if (packetHeader.size > this._bytesRemaining()) { - this._offset = offset - 1; - return -1; - } - if (packetHeader.size < 0) { return null; } + if (packetHeader.size > this._bytesRemaining()) { + this._offset = offset - 1; + throw new IncompleteReadBuffer("Wait for more data."); + } + var reply = [ ]; var ntype, i, res; diff --git a/test.js b/test.js index bb0ca604bfa..b790f5f64d3 100644 --- a/test.js +++ b/test.js @@ -291,6 +291,35 @@ tests.MULTI_6 = function () { }); }; +tests.MULTI_7 = function () { + var name = "MULTI_7"; + + if (bclient.reply_parser.name != "javascript") { + console.log("Skipping wire-protocol test for 3rd-party parser"); + return next(name); + } + + var p = require("./lib/parser/javascript"); + var parser = new p.Parser(false); + var reply_count = 0; + function check_reply(reply) { + assert.deepEqual(reply, [['a']], "Expecting multi-bulk reply of [['a']]"); + reply_count++; + assert.notEqual(reply_count, 4, "Should only parse 3 replies"); + } + parser.on("reply", check_reply); + + parser.execute(new Buffer('*1\r\n*1\r\n$1\r\na\r\n')); + + parser.execute(new Buffer('*1\r\n*1\r')); + parser.execute(new Buffer('\n$1\r\na\r\n')); + + parser.execute(new Buffer('*1\r\n*1\r\n')); + parser.execute(new Buffer('$1\r\na\r\n')); + + next(name); +}; + tests.FWD_ERRORS_1 = function () { var name = "FWD_ERRORS_1"; From 38dbacac9f0604d8754e3e98f2c452e3c1494252 Mon Sep 17 00:00:00 2001 From: Tomasz Durka Date: Tue, 12 Mar 2013 12:31:19 +0100 Subject: [PATCH 0088/1748] Add retry_max_delay option - add option - add test --- index.js | 11 +++++++++-- test.js | 22 ++++++++++++++++++++++ 2 files changed, 31 insertions(+), 2 deletions(-) diff --git a/index.js b/index.js index 4135dc1ec90..3b5ee8f0428 100644 --- a/index.js +++ b/index.js @@ -51,11 +51,14 @@ function RedisClient(stream, options) { if (options.connect_timeout && !isNaN(options.connect_timeout) && options.connect_timeout > 0) { this.connect_timeout = +options.connect_timeout; } - this.enable_offline_queue = true; if (typeof this.options.enable_offline_queue === "boolean") { this.enable_offline_queue = this.options.enable_offline_queue; } + this.retry_max_delay = null; + if (options.retry_max_delay !== undefined && !isNaN(options.retry_max_delay) && options.retry_max_delay > 0) { + this.retry_max_delay = options.retry_max_delay; + } this.initialize_retry_vars(); this.pub_sub_mode = false; @@ -429,7 +432,11 @@ RedisClient.prototype.connection_gone = function (why) { return; } - this.retry_delay = Math.floor(this.retry_delay * this.retry_backoff); + if (this.retry_max_delay !== null && this.retry_delay > this.retry_max_delay) { + this.retry_delay = this.retry_max_delay; + } else { + this.retry_delay = Math.floor(this.retry_delay * this.retry_backoff); + } if (exports.debug_mode) { console.log("Retry connection in " + this.retry_delay + " ms"); diff --git a/test.js b/test.js index b790f5f64d3..12947e25cce 100644 --- a/test.js +++ b/test.js @@ -1798,6 +1798,28 @@ tests.auth = function () { }); }; +tests.reconnectRetryMaxDelay = function() { + var time = new Date().getTime(), + name = 'reconnectRetryMaxDelay', + reconnecting = false; + var client = redis.createClient(PORT, HOST, { + retry_max_delay: 1 + }); + client.on('ready', function() { + if (!reconnecting) { + reconnecting = true; + client.retry_delay = 1000; + client.retry_backoff = 1; + client.stream.end(); + } else { + client.end(); + var lasted = new Date().getTime() - time; + assert.ok(lasted < 1000); + next(name); + } + }); +}; + all_tests = Object.keys(tests); all_start = new Date(); test_count = 0; From ccd4a2b8a3acc5b0324ec41525d83c1b2909707d Mon Sep 17 00:00:00 2001 From: Bryce Baril Date: Tue, 12 Mar 2013 21:00:37 -0700 Subject: [PATCH 0089/1748] Adding percentage outputs to diff_multi_bench_output.js Adding missing dev dependencies to package.json --- diff_multi_bench_output.js | 13 ++++++++----- package.json | 38 ++++++++++++++++++++++---------------- 2 files changed, 30 insertions(+), 21 deletions(-) diff --git a/diff_multi_bench_output.js b/diff_multi_bench_output.js index 99fdf4df6e3..73964720198 100755 --- a/diff_multi_bench_output.js +++ b/diff_multi_bench_output.js @@ -50,20 +50,22 @@ before_lines.forEach(function(b, i) { if (ops.length != 2) return var delta = ops[1] - ops[0]; + var pct = ((delta / ops[0]) * 100).toPrecision(3); total_ops.update(delta); delta = humanize_diff(delta); + pct = humanize_diff(pct, '%'); console.log( // name of test command_name(a_words) == command_name(b_words) ? command_name(a_words) + ':' : '404:', // results of test - ops.join(' -> '), 'ops/sec (∆', delta, ')'); + ops.join(' -> '), 'ops/sec (∆', delta, pct, ')'); }); -console.log('Mean difference in ops/sec:', humanize_diff(total_ops.mean())); +console.log('Mean difference in ops/sec:', humanize_diff(total_ops.mean().toPrecision(6))); function is_whitespace(s) { return !!s.trim(); @@ -74,11 +76,12 @@ function parseInt10(s) { } // green if greater than 0, red otherwise -function humanize_diff(num) { +function humanize_diff(num, unit) { + unit = unit || ""; if (num > 0) { - return ('+' + num).green; + return ('+' + num + unit).green; } - return ('' + num).red; + return ('' + num + unit).red; } function command_name(words) { diff --git a/package.json b/package.json index 7873a543c65..57439708c6d 100644 --- a/package.json +++ b/package.json @@ -1,17 +1,23 @@ -{ "name" : "redis", - "version" : "0.8.2", - "description" : "Redis client library", - "keywords" : [ "redis", "database" ], - "author": "Matt Ranney ", - "main": "./index.js", - "scripts": { - "test": "node ./test.js" - }, - "devDependencies": { - "metrics": ">=0.1.5" - }, - "repository": { - "type": "git", - "url": "git://github.com/mranney/node_redis.git" - } +{ + "name": "redis", + "version": "0.8.2", + "description": "Redis client library", + "keywords": [ + "redis", + "database" + ], + "author": "Matt Ranney ", + "main": "./index.js", + "scripts": { + "test": "node ./test.js" + }, + "devDependencies": { + "metrics": ">=0.1.5", + "colors": "~0.6.0-1", + "underscore": "~1.4.4" + }, + "repository": { + "type": "git", + "url": "git://github.com/mranney/node_redis.git" + } } From de22a94edd4fb53733ced171fc1e919f63000175 Mon Sep 17 00:00:00 2001 From: Jonas Dohse Date: Thu, 14 Mar 2013 12:38:44 +0100 Subject: [PATCH 0090/1748] Amend SCRIPT LOAD test cases --- test.js | 8 +++++++- 1 file changed, 7 insertions(+), 1 deletion(-) diff --git a/test.js b/test.js index b790f5f64d3..543158196cb 100644 --- a/test.js +++ b/test.js @@ -493,7 +493,13 @@ tests.SCRIPT_LOAD = function() { bclient.script("load", command, function(err, result) { assert.strictEqual(result.toString(), commandSha); - next(name); + client.multi().script("load", command).exec(function(err, result) { + assert.strictEqual(result[0].toString(), commandSha); + client.multi([['script', 'load', command]]).exec(function(err, result) { + assert.strictEqual(result[0].toString(), commandSha); + next(name); + }); + }); }); }; From 67e908ad55fe0dacde37312da350be8371859b90 Mon Sep 17 00:00:00 2001 From: Jonas Dohse Date: Thu, 14 Mar 2013 15:40:32 +0100 Subject: [PATCH 0091/1748] Avoid collision between command and internal field --- index.js | 6 +++--- test.js | 35 +++++++++++++++++++++++++++++++++-- 2 files changed, 36 insertions(+), 5 deletions(-) diff --git a/index.js b/index.js index 4135dc1ec90..834b43c48b5 100644 --- a/index.js +++ b/index.js @@ -841,7 +841,7 @@ RedisClient.prototype.end = function () { }; function Multi(client, args) { - this.client = client; + this._client = client; this.queue = [["MULTI"]]; if (Array.isArray(args)) { this.queue = this.queue.concat(args); @@ -1009,7 +1009,7 @@ Multi.prototype.exec = function (callback) { args.push(obj[key]); }); } - this.client.send_command(command, args, function (err, reply) { + this._client.send_command(command, args, function (err, reply) { if (err) { var cur = self.queue[index]; if (typeof cur[cur.length - 1] === "function") { @@ -1023,7 +1023,7 @@ Multi.prototype.exec = function (callback) { }, this); // TODO - make this callback part of Multi.prototype instead of creating it each time - return this.client.send_command("EXEC", [], function (err, replies) { + return this._client.send_command("EXEC", [], function (err, replies) { if (err) { if (callback) { callback(new Error(err)); diff --git a/test.js b/test.js index 543158196cb..3596d94f188 100644 --- a/test.js +++ b/test.js @@ -496,8 +496,39 @@ tests.SCRIPT_LOAD = function() { client.multi().script("load", command).exec(function(err, result) { assert.strictEqual(result[0].toString(), commandSha); client.multi([['script', 'load', command]]).exec(function(err, result) { - assert.strictEqual(result[0].toString(), commandSha); - next(name); + assert.strictEqual(result[0].toString(), commandSha); + next(name); + }); + }); + }); +}; + +tests.CLIENT_LIST = function() { + var name = "CLIENT_LIST"; + + if (!server_version_at_least(client, [2, 4, 0])) { + console.log("Skipping " + name + " for old Redis server version < 2.4.x"); + return next(name); + } + + function checkResult(result) { + var lines = result.toString().split('\n').slice(0, -1); + assert.strictEqual(lines.length, 4); + assert(lines.every(function(line) { + return line.match(/^addr=/); + })); + } + + bclient.client("list", function(err, result) { + console.log(result.toString()); + checkResult(result); + client.multi().client("list").exec(function(err, result) { + console.log(result.toString()); + checkResult(result); + client.multi([['client', 'list']]).exec(function(err, result) { + console.log(result.toString()); + checkResult(result); + next(name); }); }); }); From 229be2068cf0db44ce7ec3139b3f8c2e3f3fc587 Mon Sep 17 00:00:00 2001 From: Joffrey F Date: Wed, 12 Dec 2012 00:22:41 -0800 Subject: [PATCH 0092/1748] Support null values in arrays in reply_to_strings() --- index.js | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/index.js b/index.js index 834b43c48b5..98386405e47 100644 --- a/index.js +++ b/index.js @@ -551,7 +551,7 @@ function reply_to_strings(reply) { if (Array.isArray(reply)) { for (i = 0; i < reply.length; i++) { - reply[i] = reply[i].toString(); + reply[i] = reply[i] ? reply[i].toString() : reply[i]; } return reply; } From 98a190830b182f47f5f2bbaf326f7e30f320d370 Mon Sep 17 00:00:00 2001 From: Joffrey F Date: Thu, 17 Jan 2013 13:51:42 -0800 Subject: [PATCH 0093/1748] Support null values in arrays in reply_to_strings() --- index.js | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/index.js b/index.js index 98386405e47..2283cd81783 100644 --- a/index.js +++ b/index.js @@ -551,7 +551,9 @@ function reply_to_strings(reply) { if (Array.isArray(reply)) { for (i = 0; i < reply.length; i++) { - reply[i] = reply[i] ? reply[i].toString() : reply[i]; + if (reply[i] !== null && reply[i] !== undefined) { + reply[i] = reply[i].toString(); + } } return reply; } From ed2652f04cfcdf9478bd7e1707818c96839004dc Mon Sep 17 00:00:00 2001 From: Joffrey F Date: Sun, 17 Feb 2013 17:37:52 -0800 Subject: [PATCH 0094/1748] Added reproduction test for #344 in test.js --- test.js | 9 +++++++++ 1 file changed, 9 insertions(+) diff --git a/test.js b/test.js index 3596d94f188..526d005c44d 100644 --- a/test.js +++ b/test.js @@ -614,6 +614,15 @@ tests.detect_buffers = function () { assert.strictEqual("", reply[0].inspect(), name); assert.strictEqual("", reply[1].inspect(), name); }); + + // array of strings with undefined values (repro #344) + detect_client.hmget("hash key 2", "key 3", "key 4", function(err, reply) { + assert.strictEqual(null, err, name); + assert.strictEqual(true, Array.isArray(reply), name); + assert.strictEqual(2, reply.length, name); + assert.equal(null, reply[0], name); + assert.equal(null, reply[1], name); + }); // Object of Buffers or Strings detect_client.hgetall("hash key 2", function (err, reply) { From 1ba5864a20216b3e51fc71e718c513d865deb52c Mon Sep 17 00:00:00 2001 From: Joffrey F Date: Sun, 17 Feb 2013 17:38:12 -0800 Subject: [PATCH 0095/1748] removed extra space --- index.js | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/index.js b/index.js index 2283cd81783..22051bf5e96 100644 --- a/index.js +++ b/index.js @@ -552,7 +552,7 @@ function reply_to_strings(reply) { if (Array.isArray(reply)) { for (i = 0; i < reply.length; i++) { if (reply[i] !== null && reply[i] !== undefined) { - reply[i] = reply[i].toString(); + reply[i] = reply[i].toString(); } } return reply; From 78d8f9ef9c1852c0801394bf5a38ab7264c5ea5f Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=E7=BB=A7=E9=A3=8E?= Date: Sun, 17 Mar 2013 18:05:39 +0800 Subject: [PATCH 0096/1748] need not send message to server when set undefined value --- index.js | 8 ++++++++ test.js | 2 ++ 2 files changed, 10 insertions(+) diff --git a/index.js b/index.js index 22051bf5e96..6231c8deb06 100644 --- a/index.js +++ b/index.js @@ -694,6 +694,14 @@ RedisClient.prototype.send_command = function (command, args, callback) { args = args.slice(0, -1).concat(args[args.length - 1]); } + // if the value is undefined or null and command is set or setx, need not to send message to redis + if (command === 'set' || command === 'setex') { + if(args[args.length - 1] === undefined || args[args.length - 1] === null) { + var err = new Error('send_command: ' + command + ' value must not be undefined or null'); + return callback(err); + } + } + buffer_args = false; for (i = 0, il = args.length, arg; i < il; i += 1) { if (Buffer.isBuffer(args[i])) { diff --git a/test.js b/test.js index 526d005c44d..a00d7fe88b9 100644 --- a/test.js +++ b/test.js @@ -1109,6 +1109,7 @@ tests.SET = function () { var name = "SET"; client.SET(["set key", "set val"], require_string("OK", name)); client.get(["set key"], last(name, require_string("set val", name))); + client.SET(["set key", undefined], require_error(name)); }; tests.GETSET = function () { @@ -1161,6 +1162,7 @@ tests.SETEX = function () { client.SETEX(["setex key", "100", "setex val"], require_string("OK", name)); client.exists(["setex key"], require_number(1, name)); client.ttl(["setex key"], last(name, require_number_pos(name))); + client.SETEX(["setex key", "100", undefined], require_error(name)); }; tests.MSETNX = function () { From 0698a5e627a2396ba768650665e115443e46f921 Mon Sep 17 00:00:00 2001 From: Tomasz Durka Date: Sun, 17 Mar 2013 23:45:51 +0100 Subject: [PATCH 0097/1748] Formatting --- index.js | 18 +++++++++--------- test.js | 38 +++++++++++++++++++------------------- 2 files changed, 28 insertions(+), 28 deletions(-) diff --git a/index.js b/index.js index 3b5ee8f0428..2ef865987ed 100644 --- a/index.js +++ b/index.js @@ -55,10 +55,10 @@ function RedisClient(stream, options) { if (typeof this.options.enable_offline_queue === "boolean") { this.enable_offline_queue = this.options.enable_offline_queue; } - this.retry_max_delay = null; - if (options.retry_max_delay !== undefined && !isNaN(options.retry_max_delay) && options.retry_max_delay > 0) { - this.retry_max_delay = options.retry_max_delay; - } + this.retry_max_delay = null; + if (options.retry_max_delay !== undefined && !isNaN(options.retry_max_delay) && options.retry_max_delay > 0) { + this.retry_max_delay = options.retry_max_delay; + } this.initialize_retry_vars(); this.pub_sub_mode = false; @@ -432,11 +432,11 @@ RedisClient.prototype.connection_gone = function (why) { return; } - if (this.retry_max_delay !== null && this.retry_delay > this.retry_max_delay) { - this.retry_delay = this.retry_max_delay; - } else { - this.retry_delay = Math.floor(this.retry_delay * this.retry_backoff); - } + if (this.retry_max_delay !== null && this.retry_delay > this.retry_max_delay) { + this.retry_delay = this.retry_max_delay; + } else { + this.retry_delay = Math.floor(this.retry_delay * this.retry_backoff); + } if (exports.debug_mode) { console.log("Retry connection in " + this.retry_delay + " ms"); diff --git a/test.js b/test.js index 12947e25cce..48d352e7b0c 100644 --- a/test.js +++ b/test.js @@ -1799,25 +1799,25 @@ tests.auth = function () { }; tests.reconnectRetryMaxDelay = function() { - var time = new Date().getTime(), - name = 'reconnectRetryMaxDelay', - reconnecting = false; - var client = redis.createClient(PORT, HOST, { - retry_max_delay: 1 - }); - client.on('ready', function() { - if (!reconnecting) { - reconnecting = true; - client.retry_delay = 1000; - client.retry_backoff = 1; - client.stream.end(); - } else { - client.end(); - var lasted = new Date().getTime() - time; - assert.ok(lasted < 1000); - next(name); - } - }); + var time = new Date().getTime(), + name = 'reconnectRetryMaxDelay', + reconnecting = false; + var client = redis.createClient(PORT, HOST, { + retry_max_delay: 1 + }); + client.on('ready', function() { + if (!reconnecting) { + reconnecting = true; + client.retry_delay = 1000; + client.retry_backoff = 1; + client.stream.end(); + } else { + client.end(); + var lasted = new Date().getTime() - time; + assert.ok(lasted < 1000); + next(name); + } + }); }; all_tests = Object.keys(tests); From 375929103431a053f6d3ac2abf13ddaec3b57ef6 Mon Sep 17 00:00:00 2001 From: Tomasz Durka Date: Sun, 17 Mar 2013 23:52:17 +0100 Subject: [PATCH 0098/1748] Docu --- README.md | 3 +++ 1 file changed, 3 insertions(+) diff --git a/README.md b/README.md index 82fed0b080d..bd1cc3b5033 100644 --- a/README.md +++ b/README.md @@ -202,6 +202,9 @@ connection to the redis server, commands are added to a queue and are executed once the connection has been established. Setting `enable_offline_queue` to `false` will disable this feature and the callback will be execute immediately with an error, or an error will be thrown if no callback is specified. +* `retry_max_delay`: defaults to `null`. By default every time the client tries to connect and fails time before +reconnection (delay) almost doubles. This delay normally grows infinitely, but setting `retry_max_delay` limits delay +to maximum value, provided in miliseconds. ```js var redis = require("redis"), From c456315c1c16ccab5f85ad207a7919f3f9bfb1a4 Mon Sep 17 00:00:00 2001 From: Tomasz Durka Date: Mon, 18 Mar 2013 00:16:58 +0100 Subject: [PATCH 0099/1748] Add docu for options: max_attemps connect_timeout --- README.md | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/README.md b/README.md index bd1cc3b5033..6c75e30ec3a 100644 --- a/README.md +++ b/README.md @@ -205,6 +205,10 @@ with an error, or an error will be thrown if no callback is specified. * `retry_max_delay`: defaults to `null`. By default every time the client tries to connect and fails time before reconnection (delay) almost doubles. This delay normally grows infinitely, but setting `retry_max_delay` limits delay to maximum value, provided in miliseconds. +* `connect_timeout` defaults to `false`. By default client will try reconnecting until connected. Setting `connect_timeout` +limits total time for client to reconnect. Value is provided in miliseconds and is counted once the disconnect occured. +* `max_attempts` defaults to `null`. By default client will try reconnecting until connected. Setting `max_attempts` +limits total amount of reconnects. ```js var redis = require("redis"), From 3aab43e55aae505267bf0eb86217d271ac143a04 Mon Sep 17 00:00:00 2001 From: Bryce Baril Date: Sun, 17 Mar 2013 16:31:05 -0700 Subject: [PATCH 0100/1748] Spelling fix in the docs. --- README.md | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/README.md b/README.md index 6c75e30ec3a..e0cda3d8165 100644 --- a/README.md +++ b/README.md @@ -204,9 +204,9 @@ once the connection has been established. Setting `enable_offline_queue` to with an error, or an error will be thrown if no callback is specified. * `retry_max_delay`: defaults to `null`. By default every time the client tries to connect and fails time before reconnection (delay) almost doubles. This delay normally grows infinitely, but setting `retry_max_delay` limits delay -to maximum value, provided in miliseconds. +to maximum value, provided in milliseconds. * `connect_timeout` defaults to `false`. By default client will try reconnecting until connected. Setting `connect_timeout` -limits total time for client to reconnect. Value is provided in miliseconds and is counted once the disconnect occured. +limits total time for client to reconnect. Value is provided in milliseconds and is counted once the disconnect occured. * `max_attempts` defaults to `null`. By default client will try reconnecting until connected. Setting `max_attempts` limits total amount of reconnects. From 6fb7204ca5bb0b101bb70cdcc5dadae566ff2808 Mon Sep 17 00:00:00 2001 From: roam Date: Mon, 18 Mar 2013 16:08:58 +0800 Subject: [PATCH 0101/1748] Add a test case for reconnect_select_db_after_pubsub --- test.js | 21 +++++++++++++++++++++ 1 file changed, 21 insertions(+) diff --git a/test.js b/test.js index 94bd19d6824..13a53d7c6cc 100644 --- a/test.js +++ b/test.js @@ -516,6 +516,27 @@ tests.reconnect = function () { }); }; +tests.reconnect_select_db_after_pubsub = function() { + var name = "reconnect_select_db_after_pubsub"; + + client.select(test_db_num); + client.set(name, "one"); + client.subscribe('ChannelV', function (err, res) { + client.stream.destroy(); + }); + + client.on("reconnecting", function on_recon(params) { + client.on("ready", function on_connect() { + client.unsubscribe('ChannelV', function (err, res) { + client.get(name, require_string("one", name)); + client.removeListener("connect", on_connect); + client.removeListener("reconnecting", on_recon); + next(name); + }); + }); + }); +}; + tests.idle = function () { var name = "idle"; From 290de97e18d8a0717f8d223e57d342a9c8244970 Mon Sep 17 00:00:00 2001 From: roam Date: Mon, 18 Mar 2013 16:27:55 +0800 Subject: [PATCH 0102/1748] Fixed a subtle error where commands issued within the idle event handler --- index.js | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/index.js b/index.js index 2c2ae23c7f4..028b38866bd 100644 --- a/index.js +++ b/index.js @@ -492,8 +492,8 @@ RedisClient.prototype.return_error = function (err) { var command_obj = this.command_queue.shift(), queue_len = this.command_queue.getLength(); if (this.pub_sub_mode === false && queue_len === 0) { - this.emit("idle"); this.command_queue = new Queue(); + this.emit("idle"); } if (this.should_buffer && queue_len <= this.command_queue_low_water) { this.emit("drain"); @@ -571,8 +571,8 @@ RedisClient.prototype.return_reply = function (reply) { queue_len = this.command_queue.getLength(); if (this.pub_sub_mode === false && queue_len === 0) { - this.emit("idle"); this.command_queue = new Queue(); // explicitly reclaim storage from old Queue + this.emit("idle"); } if (this.should_buffer && queue_len <= this.command_queue_low_water) { this.emit("drain"); From 92b7b6dd6d2e0952b42b6bc0949960b37263d279 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Mart=C3=ADn=20Ciparelli?= Date: Thu, 21 Mar 2013 15:57:22 -0300 Subject: [PATCH 0103/1748] fixes #404 --- index.js | 8 +++++--- 1 file changed, 5 insertions(+), 3 deletions(-) diff --git a/index.js b/index.js index e82d2e5f49b..df4fe56a6f8 100644 --- a/index.js +++ b/index.js @@ -627,6 +627,7 @@ RedisClient.prototype.return_reply = function (reply) { } else { this.pub_sub_mode = true; } + console.log(command_obj); // subscribe commands take an optional callback and also emit an event, but only the first response is included in the callback // TODO - document this or fix it so it works in a more obvious way if (command_obj && typeof command_obj.callback === "function") { @@ -663,7 +664,7 @@ function Command(command, args, sub_command, buffer_args, callback) { } RedisClient.prototype.send_command = function (command, args, callback) { - var arg, command_obj, i, il, elem_count, buffer_args, stream = this.stream, command_str = "", buffered_writes = 0, last_arg_type; + var arg, command_obj, i, il, elem_count, buffer_args, stream = this.stream, command_str = "", buffered_writes = 0, last_arg_type, lcaseCommand; if (typeof command !== "string") { throw new Error("First argument to send_command must be the command name string, not " + typeof command); @@ -693,11 +694,12 @@ RedisClient.prototype.send_command = function (command, args, callback) { throw new Error("send_command: second argument must be an array"); } - // if the last argument is an array and command is sadd, expand it out: + // if the last argument is an array and command is sadd or srem, expand it out: // client.sadd(arg1, [arg2, arg3, arg4], cb); // converts to: // client.sadd(arg1, arg2, arg3, arg4, cb); - if ((command === 'sadd' || command === 'SADD') && args.length > 0 && Array.isArray(args[args.length - 1])) { + lcaseCommand = command.toLowerCase(); + if ((lcaseCommand === 'sadd' || lcaseCommand === 'srem') && args.length > 0 && Array.isArray(args[args.length - 1])) { args = args.slice(0, -1).concat(args[args.length - 1]); } From 859d2b1171d860a83044b9c3d7d668bdb44ce478 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Mart=C3=ADn=20Ciparelli?= Date: Thu, 21 Mar 2013 17:14:41 -0300 Subject: [PATCH 0104/1748] removed console.log line --- index.js | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/index.js b/index.js index df4fe56a6f8..3608bcc87b7 100644 --- a/index.js +++ b/index.js @@ -627,7 +627,6 @@ RedisClient.prototype.return_reply = function (reply) { } else { this.pub_sub_mode = true; } - console.log(command_obj); // subscribe commands take an optional callback and also emit an event, but only the first response is included in the callback // TODO - document this or fix it so it works in a more obvious way if (command_obj && typeof command_obj.callback === "function") { @@ -710,7 +709,7 @@ RedisClient.prototype.send_command = function (command, args, callback) { return callback(err); } } - + buffer_args = false; for (i = 0, il = args.length, arg; i < il; i += 1) { if (Buffer.isBuffer(args[i])) { From 0c143a7299c5a875e599afb5e9bf4f0fe971806f Mon Sep 17 00:00:00 2001 From: Jeff Barczewski Date: Tue, 26 Mar 2013 10:26:12 -0500 Subject: [PATCH 0105/1748] failing tests for empty unsub and punsub When unsubscribe or punsubscribe is called and there is nothing to unsubscribe from, the reply[1] argument is a null which causes a TypeError Cannot call method 'toString' of null ``` TypeError: Cannot call method 'toString' of null at RedisClient.return_reply (/Users/barczewskij/projects/node_redis/index.js:633:65) at ReplyParser.RedisClient.init_parser (/Users/barczewskij/projects/node_redis/index.js:266:14) at ReplyParser.EventEmitter.emit (events.js:96:17) at ReplyParser.send_reply (/Users/barczewskij/projects/node_redis/lib/parser/javascript.js:300:10) at ReplyParser.execute (/Users/barczewskij/projects/node_redis/lib/parser/javascript.js:211:22) at RedisClient.on_data (/Users/barczewskij/projects/node_redis/index.js:483:27) at Socket. (/Users/barczewskij/projects/node_redis/index.js:82:14) at Socket.EventEmitter.emit (events.js:96:17) at TCP.onread (net.js:396:14) ``` --- test.js | 40 +++++++++++++++++++++++++++++++++++++++- 1 file changed, 39 insertions(+), 1 deletion(-) diff --git a/test.js b/test.js index 4b26dca9c96..08fea7f5f62 100644 --- a/test.js +++ b/test.js @@ -614,7 +614,7 @@ tests.detect_buffers = function () { assert.strictEqual("", reply[0].inspect(), name); assert.strictEqual("", reply[1].inspect(), name); }); - + // array of strings with undefined values (repro #344) detect_client.hmget("hash key 2", "key 3", "key 4", function(err, reply) { assert.strictEqual(null, err, name); @@ -864,6 +864,44 @@ tests.SUBSCRIBE = function () { }); }; +tests.UNSUB_EMPTY = function () { + // test situation where unsubscribe reply[1] is null + var name = "UNSUB_EMPTY"; + client3.unsubscribe(); // unsubscribe from all so can test null + client3.unsubscribe(); // reply[1] will be null + next(name); +}; + +tests.PUNSUB_EMPTY = function () { + // test situation where punsubscribe reply[1] is null + var name = "PUNSUB_EMPTY"; + client3.punsubscribe(); // punsubscribe from all so can test null + client3.punsubscribe(); // reply[1] will be null + next(name); +}; + +tests.UNSUB_EMPTY_CB = function () { + // test situation where unsubscribe reply[1] is null + var name = "UNSUB_EMPTY_CB"; + client3.unsubscribe(); // unsubscribe from all so can test null + client3.unsubscribe(function (err, results) { + // reply[1] will be null + assert.strictEqual(null, err, "unexpected error: " + err); + next(name); + }); +}; + +tests.PUNSUB_EMPTY_CB = function () { + // test situation where punsubscribe reply[1] is null + var name = "PUNSUB_EMPTY_CB"; + client3.punsubscribe(); // punsubscribe from all so can test null + client3.punsubscribe(function (err, results) { + // reply[1] will be null + assert.strictEqual(null, err, "unexpected error: " + err); + next(name); + }); +}; + tests.SUB_UNSUB_SUB = function () { var name = "SUB_UNSUB_SUB"; client3.subscribe('chan3'); From 655681f79001b471b93b601ea2716957b54ee2d3 Mon Sep 17 00:00:00 2001 From: Jeff Barczewski Date: Tue, 26 Mar 2013 10:49:13 -0500 Subject: [PATCH 0106/1748] fix empty unsub/punsub TypeError When unsubscribe or punsubscribe is called and it has no subscriptions, the reply[1] is a null which causes `TypeError: Cannot call method 'toString' of null` Check if reply[1] is null before calling toString otherwise just pass null. --- index.js | 8 +++++--- 1 file changed, 5 insertions(+), 3 deletions(-) diff --git a/index.js b/index.js index e82d2e5f49b..6dab1a73c86 100644 --- a/index.js +++ b/index.js @@ -629,10 +629,12 @@ RedisClient.prototype.return_reply = function (reply) { } // subscribe commands take an optional callback and also emit an event, but only the first response is included in the callback // TODO - document this or fix it so it works in a more obvious way + // reply[1] can be null + var reply1String = (reply[1] === null) ? null : reply[1].toString(); if (command_obj && typeof command_obj.callback === "function") { - try_callback(command_obj.callback, reply[1].toString()); + try_callback(command_obj.callback, reply1String); } - this.emit(type, reply[1].toString(), reply[2]); // channel, count + this.emit(type, reply1String, reply[2]); // channel, count } else { throw new Error("subscriptions are active but got unknown reply type " + type); } @@ -708,7 +710,7 @@ RedisClient.prototype.send_command = function (command, args, callback) { return callback(err); } } - + buffer_args = false; for (i = 0, il = args.length, arg; i < il; i += 1) { if (Buffer.isBuffer(args[i])) { From 44526402dc71166d05c839b8044f91c47292c13d Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=E7=BB=A7=E9=A3=8E?= Date: Wed, 27 Mar 2013 15:58:50 +0800 Subject: [PATCH 0107/1748] fix bug when callback is undefined --- index.js | 2 +- test.js | 2 ++ 2 files changed, 3 insertions(+), 1 deletion(-) diff --git a/index.js b/index.js index 283c743fb19..bdfb9a51300 100644 --- a/index.js +++ b/index.js @@ -710,7 +710,7 @@ RedisClient.prototype.send_command = function (command, args, callback) { if (command === 'set' || command === 'setex') { if(args[args.length - 1] === undefined || args[args.length - 1] === null) { var err = new Error('send_command: ' + command + ' value must not be undefined or null'); - return callback(err); + return callback && callback(err); } } diff --git a/test.js b/test.js index 29fd5678805..65561e34a40 100644 --- a/test.js +++ b/test.js @@ -1780,6 +1780,8 @@ tests.OPTIONAL_CALLBACK_UNDEFINED = function () { client.del("op_cb2"); client.set("op_cb2", "y", undefined); client.get("op_cb2", last(name, require_string("y", name))); + + client.set("op_cb_undefined", undefined, undefined); }; tests.ENABLE_OFFLINE_QUEUE_TRUE = function () { From 383bafd2cf57e7b3ba54dc97504e5e26321ff2f5 Mon Sep 17 00:00:00 2001 From: Jeff Barczewski Date: Wed, 27 Mar 2013 13:39:43 -0500 Subject: [PATCH 0108/1748] limit cbtests to 2.6.11 and above Test hangs on older versions of Redis --- test.js | 10 ++++++++-- 1 file changed, 8 insertions(+), 2 deletions(-) diff --git a/test.js b/test.js index 08fea7f5f62..cde711482a2 100644 --- a/test.js +++ b/test.js @@ -881,8 +881,11 @@ tests.PUNSUB_EMPTY = function () { }; tests.UNSUB_EMPTY_CB = function () { - // test situation where unsubscribe reply[1] is null var name = "UNSUB_EMPTY_CB"; + // test hangs on older versions of redis, so skip + if (!server_version_at_least(client, [2, 6, 11])) return next(name); + + // test situation where unsubscribe reply[1] is null client3.unsubscribe(); // unsubscribe from all so can test null client3.unsubscribe(function (err, results) { // reply[1] will be null @@ -892,8 +895,11 @@ tests.UNSUB_EMPTY_CB = function () { }; tests.PUNSUB_EMPTY_CB = function () { - // test situation where punsubscribe reply[1] is null var name = "PUNSUB_EMPTY_CB"; + // test hangs on older versions of redis, so skip + if (!server_version_at_least(client, [2, 6, 11])) return next(name); + + // test situation where punsubscribe reply[1] is null client3.punsubscribe(); // punsubscribe from all so can test null client3.punsubscribe(function (err, results) { // reply[1] will be null From 3e0762e6ecff4d1b0c6414eeac8bfc99cdc22686 Mon Sep 17 00:00:00 2001 From: englandpost Date: Fri, 5 Apr 2013 00:44:06 +0400 Subject: [PATCH 0109/1748] fix fwd errors test for node >= 0.9.1 it seems that listeners are no longer mutable * events: Make emitter.listeners() side-effect free (isaacs, Joe Andaverde) https://raw.github.com/joyent/node/v0.9.1/ChangeLog --- test.js | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/test.js b/test.js index 29fd5678805..1d8d6343af2 100644 --- a/test.js +++ b/test.js @@ -326,7 +326,8 @@ tests.FWD_ERRORS_1 = function () { var toThrow = new Error("Forced exception"); var recordedError = null; - var originalHandler = client3.listeners("error").pop(); + var originalHandlers = client3.listeners("error"); + client3.removeAllListeners("error"); client3.once("error", function (err) { recordedError = err; }); @@ -342,7 +343,7 @@ tests.FWD_ERRORS_1 = function () { client.publish(name, "Some message"); setTimeout(function () { - client3.listeners("error").push(originalHandler); + client3.listeners("error").push(originalHandlers); assert.equal(recordedError, toThrow, "Should have caught our forced exception"); next(name); }, 150); From 2e4c178382c7818892379a145f36db630dcd26a7 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Mart=C3=ADn=20Ciparelli?= Date: Tue, 9 Apr 2013 16:13:25 -0300 Subject: [PATCH 0110/1748] added tests for #404 --- test.js | 21 +++++++++++++++++++++ 1 file changed, 21 insertions(+) diff --git a/test.js b/test.js index 4b26dca9c96..8f19277c59f 100644 --- a/test.js +++ b/test.js @@ -1270,6 +1270,27 @@ tests.SREM = function () { client.scard('set0', last(name, require_number(0, name))); }; + +tests.SREM2 = function () { + var name = "SREM2"; + client.del("set0"); + client.sadd("set0", ["member0", "member1", "member2"], require_number(3, name)); + client.SREM("set0", ["member1", "member2"], require_number(2, name)); + client.smembers("set0", function (err, res) { + assert.strictEqual(res.length, 1); + assert.ok(~res.indexOf("member0")); + }); + client.sadd("set0", ["member3", "member4", "member5"], require_number(3, name)); + client.srem("set0", ["member0", "member6"], require_number(1, name)); + client.smembers("set0", function (err, res) { + assert.strictEqual(res.length, 3); + assert.ok(~res.indexOf("member3")); + assert.ok(~res.indexOf("member4")); + assert.ok(~res.indexOf("member5")); + next(name); + }); +}; + tests.SPOP = function () { var name = "SPOP"; From 8feaf8d904dfb331f7913afb1abd1d98301aad5b Mon Sep 17 00:00:00 2001 From: Bryce Baril Date: Tue, 9 Apr 2013 16:09:41 -0700 Subject: [PATCH 0111/1748] 0.8.3 --- package.json | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/package.json b/package.json index 57439708c6d..31913b08570 100644 --- a/package.json +++ b/package.json @@ -1,6 +1,6 @@ { "name": "redis", - "version": "0.8.2", + "version": "0.8.3", "description": "Redis client library", "keywords": [ "redis", From f558cae04904cc48933578e987d65dd1b82cf12d Mon Sep 17 00:00:00 2001 From: Luke Plaster Date: Sun, 14 Apr 2013 16:32:35 +0100 Subject: [PATCH 0112/1748] Amended README to refer to 'pub/sub' mode as 'subscriber' mode; fixes mranney/node_redis#420 --- README.md | 10 +++++----- 1 file changed, 5 insertions(+), 5 deletions(-) diff --git a/README.md b/README.md index e0cda3d8165..b8237f0043a 100644 --- a/README.md +++ b/README.md @@ -333,13 +333,13 @@ channel on the other: client1.subscribe("a nice channel"); ``` -When a client issues a `SUBSCRIBE` or `PSUBSCRIBE`, that connection is put into "pub/sub" mode. +When a client issues a `SUBSCRIBE` or `PSUBSCRIBE`, that connection is put into a "subscriber" mode. At that point, only commands that modify the subscription set are valid. When the subscription set is empty, the connection is put back into regular mode. -If you need to send regular commands to Redis while in pub/sub mode, just open another connection. +If you need to send regular commands to Redis while in subscriber mode, just open another connection. -## Pub / Sub Events +## Subscriber Events If a client has subscriptions active, it may emit these events: @@ -368,13 +368,13 @@ original pattern as `pattern`, and the new count of subscriptions for this clien Client will emit `unsubscribe` in response to a `UNSUBSCRIBE` command. Listeners are passed the channel name as `channel` and the new count of subscriptions for this client as `count`. When -`count` is 0, this client has left pub/sub mode and no more pub/sub events will be emitted. +`count` is 0, this client has left subscriber mode and no more subscriber events will be emitted. ### "punsubscribe" (pattern, count) Client will emit `punsubscribe` in response to a `PUNSUBSCRIBE` command. Listeners are passed the channel name as `channel` and the new count of subscriptions for this client as `count`. When -`count` is 0, this client has left pub/sub mode and no more pub/sub events will be emitted. +`count` is 0, this client has left subscriber mode and no more subscriber events will be emitted. ## client.multi([commands]) From e63947b0b642aa06e57a2be43df30516ba3b039b Mon Sep 17 00:00:00 2001 From: Luke Plaster Date: Sun, 14 Apr 2013 16:45:25 +0100 Subject: [PATCH 0113/1748] Amended subscriber mode error message --- index.js | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/index.js b/index.js index 283c743fb19..6bc76d54a12 100644 --- a/index.js +++ b/index.js @@ -755,7 +755,7 @@ RedisClient.prototype.send_command = function (command, args, callback) { } else if (command === "quit") { this.closing = true; } else if (this.pub_sub_mode === true) { - throw new Error("Connection in pub/sub mode, only pub/sub commands may be used"); + throw new Error("Connection in subscriber mode, only subscriber commands may be used"); } this.command_queue.push(command_obj); this.commands_sent += 1; From c7633bf738ec6565f59076066bc73df3bcb5df4a Mon Sep 17 00:00:00 2001 From: Thanasis Polychronakis Date: Thu, 18 Apr 2013 21:48:35 +0300 Subject: [PATCH 0114/1748] fix unescaped exception in Multi.exec --- index.js | 9 +++++++-- 1 file changed, 7 insertions(+), 2 deletions(-) diff --git a/index.js b/index.js index 6bc76d54a12..fcca3feba1c 100644 --- a/index.js +++ b/index.js @@ -713,7 +713,7 @@ RedisClient.prototype.send_command = function (command, args, callback) { return callback(err); } } - + buffer_args = false; for (i = 0, il = args.length, arg; i < il; i += 1) { if (Buffer.isBuffer(args[i])) { @@ -1037,7 +1037,12 @@ Multi.prototype.exec = function (callback) { if (typeof cur[cur.length - 1] === "function") { cur[cur.length - 1](err); } else { - throw new Error(err); + if (callback) { + callback(new Error(err)); + return; + } else { + throw new Error(err); + } } self.queue.splice(index, 1); } From ed57dcd9d5c594b8efc74bb20453dcbc7cdddb4d Mon Sep 17 00:00:00 2001 From: Thanasis Polychronakis Date: Thu, 18 Apr 2013 22:25:58 +0300 Subject: [PATCH 0115/1748] handling of errors on multi, now returns 'err' array type --- index.js | 12 ++++-------- test.js | 11 ++++++++++- 2 files changed, 14 insertions(+), 9 deletions(-) diff --git a/index.js b/index.js index fcca3feba1c..5f468881107 100644 --- a/index.js +++ b/index.js @@ -1011,7 +1011,7 @@ Multi.prototype.HMSET = Multi.prototype.hmset; Multi.prototype.exec = function (callback) { var self = this; - + var errors = []; // drain queue, callback will catch "QUEUED" or error // TODO - get rid of all of these anonymous functions which are elegant but slow this.queue.forEach(function (args, index) { @@ -1037,12 +1037,7 @@ Multi.prototype.exec = function (callback) { if (typeof cur[cur.length - 1] === "function") { cur[cur.length - 1](err); } else { - if (callback) { - callback(new Error(err)); - return; - } else { - throw new Error(err); - } + errors.push(new Error(err)); } self.queue.splice(index, 1); } @@ -1053,7 +1048,8 @@ Multi.prototype.exec = function (callback) { return this._client.send_command("EXEC", [], function (err, replies) { if (err) { if (callback) { - callback(new Error(err)); + errors.push(new Error(err)); + callback(errors); return; } else { throw new Error(err); diff --git a/test.js b/test.js index 1d8d6343af2..07ddcf97cb6 100644 --- a/test.js +++ b/test.js @@ -320,6 +320,15 @@ tests.MULTI_7 = function () { next(name); }; + +tests.MULTI_EXCEPTION_1 = function() { + client.multi().set("foo").exec(function (err, reply) { + /* ... */ + console.log('CB:', arguments); + }); + // [Error: Error: ERR wrong number of arguments for 'set' command] +}; + tests.FWD_ERRORS_1 = function () { var name = "FWD_ERRORS_1"; @@ -615,7 +624,7 @@ tests.detect_buffers = function () { assert.strictEqual("", reply[0].inspect(), name); assert.strictEqual("", reply[1].inspect(), name); }); - + // array of strings with undefined values (repro #344) detect_client.hmget("hash key 2", "key 3", "key 4", function(err, reply) { assert.strictEqual(null, err, name); From a86720097e42e3dc4208d1ee6e6f769a4a58a872 Mon Sep 17 00:00:00 2001 From: Thanasis Polychronakis Date: Fri, 19 Apr 2013 15:40:53 +0300 Subject: [PATCH 0116/1748] added test for MULTI exception --- test.js | 10 +++++++--- 1 file changed, 7 insertions(+), 3 deletions(-) diff --git a/test.js b/test.js index 07ddcf97cb6..832de4062b5 100644 --- a/test.js +++ b/test.js @@ -322,11 +322,15 @@ tests.MULTI_7 = function () { tests.MULTI_EXCEPTION_1 = function() { + var name = "MULTI_EXCEPTION_1"; + client.multi().set("foo").exec(function (err, reply) { - /* ... */ - console.log('CB:', arguments); + assert(Array.isArray(err), "err should be an array"); + assert.equal(2, err.length, "err should have 2 items"); + assert(err[0].message.match(/ERR/), "First error message should contain ERR"); + assert(err[1].message.match(/EXECABORT/), "First error message should contain EXECABORT"); + next(name); }); - // [Error: Error: ERR wrong number of arguments for 'set' command] }; tests.FWD_ERRORS_1 = function () { From 802539b07e44e577a488601df2eb8d7c6200f35a Mon Sep 17 00:00:00 2001 From: Thanasis Polychronakis Date: Fri, 19 Apr 2013 15:49:32 +0300 Subject: [PATCH 0117/1748] update docs for MULTI exception --- README.md | 7 +++++++ 1 file changed, 7 insertions(+) diff --git a/README.md b/README.md index b8237f0043a..545ffe99114 100644 --- a/README.md +++ b/README.md @@ -411,10 +411,17 @@ Redis. The interface in `node_redis` is to return an individual `Multi` object }); ``` +### Multi.exec( callback ) + `client.multi()` is a constructor that returns a `Multi` object. `Multi` objects share all of the same command methods as `client` objects do. Commands are queued up inside the `Multi` object until `Multi.exec()` is invoked. +The `callback` of `.exec()` will get invoked with two arguments: + +* `err` **type:** `null | Array` err is either null or an array of Error Objects corresponding the the sequence the commands where chained. The last item of the array will always be an `EXECABORT` type of error originating from the `.exec()` itself. +* `results` **type:** `null | Array` results is an array of responses corresponding the the sequence the commands where chained. + You can either chain together `MULTI` commands as in the above example, or you can queue individual commands while still sending regular client command as in this example: From b30efac4768b70d561adc94a70dfbf59dcf9dc14 Mon Sep 17 00:00:00 2001 From: Amos Barreto Date: Wed, 24 Apr 2013 13:59:43 -0700 Subject: [PATCH 0118/1748] Protect connection retries from application exceptions --- index.js | 16 ++++++++++++++-- 1 file changed, 14 insertions(+), 2 deletions(-) diff --git a/index.js b/index.js index 012a7d0ca10..6d8758d359d 100644 --- a/index.js +++ b/index.js @@ -118,7 +118,13 @@ RedisClient.prototype.flush_and_error = function (message) { while (this.offline_queue.length > 0) { command_obj = this.offline_queue.shift(); if (typeof command_obj.callback === "function") { - command_obj.callback(message); + try { + command_obj.callback(message); + } catch (callback_err) { + process.nextTick(function () { + throw callback_err; + }); + } } } this.offline_queue = new Queue(); @@ -126,7 +132,13 @@ RedisClient.prototype.flush_and_error = function (message) { while (this.command_queue.length > 0) { command_obj = this.command_queue.shift(); if (typeof command_obj.callback === "function") { - command_obj.callback(message); + try { + command_obj.callback(message); + } catch (callback_err) { + process.nextTick(function () { + throw callback_err; + }); + } } } this.command_queue = new Queue(); From 9d14970782421871a29d55b156e3e7217ad2dc58 Mon Sep 17 00:00:00 2001 From: Bryce Baril Date: Sat, 27 Apr 2013 08:02:52 -0700 Subject: [PATCH 0119/1748] Adding missing 0.8.3 changelog --- changelog.md | 24 ++++++++++++++++++++++++ 1 file changed, 24 insertions(+) diff --git a/changelog.md b/changelog.md index e4e3277b541..93fdd5b3e28 100644 --- a/changelog.md +++ b/changelog.md @@ -1,6 +1,30 @@ Changelog ========= +## v0.8.3 - April 09, 2013 + +Many contributed features and fixes, including: +* Fix some tests for Node.js version 0.9.x+ changes (Roman Ivanilov) +* Fix error when commands submitted after idle event handler (roamm) +* Bypass Redis for no-op SET/SETEX commands (jifeng) +* Fix HMGET + detect_buffers (Joffrey F) +* Fix CLIENT LOAD functionality (Jonas Dohse) +* Add percentage outputs to diff_multi_bench_output.js (Bryce Baril) +* Add retry_max_delay option (Tomasz Durka) +* Fix parser off-by-one errors with nested multi-bulk replies (Bryce Baril) +* Prevent parser from sinking application-side exceptions (Bryce Baril) +* Fix parser incorrect buffer skip when parsing multi-bulk errors (Bryce Baril) +* Reverted previous change with throwing on non-string values with HMSET (David Trejo) +* Fix command queue sync issue when using pubsub (Tom Leach) +* Fix compatibility with two-word Redis commands (Jonas Dohse) +* Add EVAL with array syntax (dmoena) +* Fix tests due to Redis reply order changes in 2.6.5+ (Bryce Baril) +* Added a test for the SLOWLOG command (Nitesh Sinha) +* Fix SMEMBERS order dependency in test broken by Redis changes (Garrett Johnson) +* Update commands for new Redis commands (David Trejo) +* Prevent exception from SELECT on subscriber reconnection (roamm) + + ## v0.8.2 - November 11, 2012 Another version bump because 0.8.1 didn't get applied properly for some mysterious reason. From a8ee9cdb13d6358767184b810d42a7a011ea7e47 Mon Sep 17 00:00:00 2001 From: Bryce Baril Date: Sat, 27 Apr 2013 08:10:37 -0700 Subject: [PATCH 0120/1748] Deprecate end() by having it call quit() instead. Marked for eventual removal. Fixes #419 --- index.js | 12 +++++++----- 1 file changed, 7 insertions(+), 5 deletions(-) diff --git a/index.js b/index.js index 012a7d0ca10..3455fb8bdc3 100644 --- a/index.js +++ b/index.js @@ -855,12 +855,14 @@ RedisClient.prototype.pub_sub_command = function (command_obj) { } }; +// Warning! end() has been deprecated and will go away +// in later versions of this client. Please consider switching to +// quit() everywhere you use end. RedisClient.prototype.end = function () { - this.stream._events = {}; - this.connected = false; - this.ready = false; - this.closing = true; - return this.stream.end(); + if (exports.debug_mode) { + console.log("Using deprecated .end() method!"); + } + return this.quit(); }; function Multi(client, args) { From 19c918db51a76ceebdae5f58c2d89d25809737bb Mon Sep 17 00:00:00 2001 From: Bryce Baril Date: Sun, 5 May 2013 09:33:47 -0700 Subject: [PATCH 0121/1748] Revert "Deprecate end() by having it call quit() instead. Marked for eventual removal. Fixes #419" A hard client-side quit is useful for test purposes, and some other rare use cases. This reverts commit a8ee9cdb13d6358767184b810d42a7a011ea7e47. --- index.js | 12 +++++------- 1 file changed, 5 insertions(+), 7 deletions(-) diff --git a/index.js b/index.js index 4bb2891b804..4ae92fe6c83 100644 --- a/index.js +++ b/index.js @@ -857,14 +857,12 @@ RedisClient.prototype.pub_sub_command = function (command_obj) { } }; -// Warning! end() has been deprecated and will go away -// in later versions of this client. Please consider switching to -// quit() everywhere you use end. RedisClient.prototype.end = function () { - if (exports.debug_mode) { - console.log("Using deprecated .end() method!"); - } - return this.quit(); + this.stream._events = {}; + this.connected = false; + this.ready = false; + this.closing = true; + return this.stream.end(); }; function Multi(client, args) { From 807aaf91b70f047201d4b63fe668d138b6c89cc2 Mon Sep 17 00:00:00 2001 From: Bryce Baril Date: Sun, 5 May 2013 10:01:38 -0700 Subject: [PATCH 0122/1748] Add guards to some newer tests for older Redis server differences. Fixes #430 --- test.js | 30 ++++++++++++++++++++++-------- 1 file changed, 22 insertions(+), 8 deletions(-) diff --git a/test.js b/test.js index 13bf141d881..1d447bc2eab 100644 --- a/test.js +++ b/test.js @@ -322,15 +322,20 @@ tests.MULTI_7 = function () { tests.MULTI_EXCEPTION_1 = function() { - var name = "MULTI_EXCEPTION_1"; + var name = "MULTI_EXCEPTION_1"; - client.multi().set("foo").exec(function (err, reply) { - assert(Array.isArray(err), "err should be an array"); - assert.equal(2, err.length, "err should have 2 items"); - assert(err[0].message.match(/ERR/), "First error message should contain ERR"); - assert(err[1].message.match(/EXECABORT/), "First error message should contain EXECABORT"); - next(name); - }); + if (!server_version_at_least(client, [2, 6, 5])) { + console.log("Skipping " + name + " for old Redis server version < 2.6.5"); + return next(name); + } + + client.multi().set("foo").exec(function (err, reply) { + assert(Array.isArray(err), "err should be an array"); + assert.equal(2, err.length, "err should have 2 items"); + assert(err[0].message.match(/ERR/), "First error message should contain ERR"); + assert(err[1].message.match(/EXECABORT/), "First error message should contain EXECABORT"); + next(name); + }); }; tests.FWD_ERRORS_1 = function () { @@ -945,6 +950,9 @@ tests.PUNSUB_EMPTY_CB = function () { tests.SUB_UNSUB_SUB = function () { var name = "SUB_UNSUB_SUB"; + // test hangs on older versions of redis, so skip + if (!server_version_at_least(client, [2, 6, 11])) return next(name); + client3.subscribe('chan3'); client3.unsubscribe('chan3'); client3.subscribe('chan3', function (err, results) { @@ -961,6 +969,9 @@ tests.SUB_UNSUB_SUB = function () { tests.SUB_UNSUB_MSG_SUB = function () { var name = "SUB_UNSUB_MSG_SUB"; + // test hangs on older versions of redis, so skip + if (!server_version_at_least(client, [2, 6, 11])) return next(name); + client3.subscribe('chan8'); client3.subscribe('chan9'); client3.unsubscribe('chan9'); @@ -972,6 +983,9 @@ tests.SUB_UNSUB_MSG_SUB = function () { tests.PSUB_UNSUB_PMSG_SUB = function () { var name = "PSUB_UNSUB_PMSG_SUB"; + // test hangs on older versions of redis, so skip + if (!server_version_at_least(client, [2, 6, 11])) return next(name); + client3.psubscribe('abc*'); client3.subscribe('xyz'); client3.unsubscribe('xyz'); From 515e975539d26902df94b04b69bed39a4be92633 Mon Sep 17 00:00:00 2001 From: Bryce Baril Date: Sun, 5 May 2013 10:43:17 -0700 Subject: [PATCH 0123/1748] Switching RedisClient.prototype.end to call .destroySoon() vs .end() on the stream to have compatible behavior between 0.8 and 0.10. Fixes #419 --- index.js | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/index.js b/index.js index ae6d753d9f9..9d0e7dd70ce 100644 --- a/index.js +++ b/index.js @@ -874,7 +874,7 @@ RedisClient.prototype.end = function () { this.connected = false; this.ready = false; this.closing = true; - return this.stream.end(); + return this.stream.destroySoon(); }; function Multi(client, args) { From 35001fec763ed0a23d833172df7c4419a31124ed Mon Sep 17 00:00:00 2001 From: Jonathan Bergknoff Date: Thu, 9 May 2013 10:46:26 -0700 Subject: [PATCH 0124/1748] If password is supplied but redis server does not require it, continue without throwing an error --- index.js | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/index.js b/index.js index 9d0e7dd70ce..6ec5ab3b7af 100644 --- a/index.js +++ b/index.js @@ -182,6 +182,10 @@ RedisClient.prototype.do_auth = function () { self.do_auth(); }, 2000); // TODO - magic number alert return; + } else if (err.toString().match("no password is set")) { + console.log("Warning: Redis server does not require a password, but a password was supplied.") + err = null; + res = "OK"; } else { return self.emit("error", new Error("Auth error: " + err.message)); } From 4dcfa0f92a640f79d7a9ee3d6db8c616285b4661 Mon Sep 17 00:00:00 2001 From: Henrik Peinar Date: Fri, 31 May 2013 10:54:11 -0700 Subject: [PATCH 0125/1748] Auth_pass should be part of options --- index.js | 3 +++ 1 file changed, 3 insertions(+) diff --git a/index.js b/index.js index 6ec5ab3b7af..5c40333bbfd 100644 --- a/index.js +++ b/index.js @@ -67,6 +67,9 @@ function RedisClient(stream, options) { this.closing = false; this.server_info = {}; this.auth_pass = null; + if(options.auth_pass !== undefined) { + this.auth_pass = options.auth_pass; + } this.parser_module = null; this.selected_db = null; // save the selected db here, used when reconnecting From 660b89bb42f96c752aa8393c0968f8212b0d33c1 Mon Sep 17 00:00:00 2001 From: Henrik Peinar Date: Fri, 31 May 2013 11:11:37 -0700 Subject: [PATCH 0126/1748] Added auth_pass to readme too --- README.md | 1 + 1 file changed, 1 insertion(+) diff --git a/README.md b/README.md index 545ffe99114..b9285af50ee 100644 --- a/README.md +++ b/README.md @@ -209,6 +209,7 @@ to maximum value, provided in milliseconds. limits total time for client to reconnect. Value is provided in milliseconds and is counted once the disconnect occured. * `max_attempts` defaults to `null`. By default client will try reconnecting until connected. Setting `max_attempts` limits total amount of reconnects. +* `auth_pass` defaults to `null`. By default client will try connecting without auth. If set, client will run redis auth command on connect. ```js var redis = require("redis"), From 46cd9329b181ce71210621a93c32c00cbc1d2438 Mon Sep 17 00:00:00 2001 From: Kwangsu Kim Date: Wed, 26 Jun 2013 02:48:15 +0900 Subject: [PATCH 0127/1748] Fix TypeError when handling multi exception When errors occurs using multi/exec, avoid "TypeError: Cannot read property 'length' of undefined" --- index.js | 1 - 1 file changed, 1 deletion(-) diff --git a/index.js b/index.js index 6ec5ab3b7af..b9cc8eea026 100644 --- a/index.js +++ b/index.js @@ -1058,7 +1058,6 @@ Multi.prototype.exec = function (callback) { } else { errors.push(new Error(err)); } - self.queue.splice(index, 1); } }); }, this); From 9fd7341b2d2888e5c90ef32da76eb659da29b2b7 Mon Sep 17 00:00:00 2001 From: Bryce Baril Date: Wed, 26 Jun 2013 18:17:21 -0700 Subject: [PATCH 0128/1748] Changelog for 0.8.4 --- changelog.md | 11 +++++++++++ 1 file changed, 11 insertions(+) diff --git a/changelog.md b/changelog.md index 93fdd5b3e28..5f825a76a8a 100644 --- a/changelog.md +++ b/changelog.md @@ -1,6 +1,17 @@ Changelog ========= +## v0.8.4 - June 24, 2013 +Many contributed features and fixes, including: +* Ignore password set if not needed. (jbergknoff) +* Improved compatibility with 0.10.X for tests and client.end() (Bryce Baril) +* Protect connection retries from application exceptions. (Amos Barreto) +* Better exception handling for Multi/Exec (Thanasis Polychronakis) +* Renamed pubsub mode to subscriber mode (Luke Plaster) +* Treat SREM like SADD when passed an array (Martin Ciparelli) +* Fix empty unsub/punsub TypeError (Jeff Barczewski) +* Only attempt to run a callback if it one was provided (jifeng) + ## v0.8.3 - April 09, 2013 Many contributed features and fixes, including: From 278137c39968b26c57cf9ccfa63c10e2404c0d75 Mon Sep 17 00:00:00 2001 From: Bryce Baril Date: Wed, 26 Jun 2013 18:17:26 -0700 Subject: [PATCH 0129/1748] 0.8.4 --- package.json | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/package.json b/package.json index 31913b08570..24a570aae91 100644 --- a/package.json +++ b/package.json @@ -1,6 +1,6 @@ { "name": "redis", - "version": "0.8.3", + "version": "0.8.4", "description": "Redis client library", "keywords": [ "redis", From 8f7100300918fe35c882e253bd22ba6e040a61d2 Mon Sep 17 00:00:00 2001 From: Bryce Baril Date: Wed, 24 Jul 2013 08:21:28 -0700 Subject: [PATCH 0130/1748] Add test for auth_pass option submitted by hpeinar --- changelog.md | 3 +++ index.js | 2 +- test.js | 18 ++++++++++++++++++ 3 files changed, 22 insertions(+), 1 deletion(-) diff --git a/changelog.md b/changelog.md index 5f825a76a8a..2857c96bca2 100644 --- a/changelog.md +++ b/changelog.md @@ -1,6 +1,9 @@ Changelog ========= +## v0.8.5 - (pending release) +* Add `auth_pass` option to connect and immediately authenticate (Henrik Peinar) + ## v0.8.4 - June 24, 2013 Many contributed features and fixes, including: * Ignore password set if not needed. (jbergknoff) diff --git a/index.js b/index.js index 5c40333bbfd..d16a15ff633 100644 --- a/index.js +++ b/index.js @@ -67,7 +67,7 @@ function RedisClient(stream, options) { this.closing = false; this.server_info = {}; this.auth_pass = null; - if(options.auth_pass !== undefined) { + if (options.auth_pass !== undefined) { this.auth_pass = options.auth_pass; } this.parser_module = null; diff --git a/test.js b/test.js index 1d447bc2eab..79907a01600 100644 --- a/test.js +++ b/test.js @@ -1962,6 +1962,24 @@ tests.auth = function () { }); }; +tests.auth2 = function () { + var name = "AUTH2", client4, ready_count = 0; + + client4 = redis.createClient(9006, "filefish.redistogo.com", {auth_pass: "664b1b6aaf134e1ec281945a8de702a9"}); + + // test auth, then kill the connection so it'll auto-reconnect and auto-re-auth + client4.on("ready", function () { + ready_count++; + if (ready_count === 1) { + client4.stream.destroy(); + } else { + client4.quit(function (err, res) { + next(name); + }); + } + }); +}; + tests.reconnectRetryMaxDelay = function() { var time = new Date().getTime(), name = 'reconnectRetryMaxDelay', From 5f3537e93cac7e82ad1b68234f60ffe8d14c3041 Mon Sep 17 00:00:00 2001 From: Bryce Baril Date: Thu, 26 Sep 2013 08:02:55 -0700 Subject: [PATCH 0131/1748] Prep for release --- changelog.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/changelog.md b/changelog.md index 2857c96bca2..b4b24a18a2f 100644 --- a/changelog.md +++ b/changelog.md @@ -1,7 +1,7 @@ Changelog ========= -## v0.8.5 - (pending release) +## v0.8.5 - September 26, 2013 * Add `auth_pass` option to connect and immediately authenticate (Henrik Peinar) ## v0.8.4 - June 24, 2013 From 520ed070d6451d9106b462b3875826d1381bf4a3 Mon Sep 17 00:00:00 2001 From: Bryce Baril Date: Thu, 26 Sep 2013 08:02:58 -0700 Subject: [PATCH 0132/1748] 0.8.5 --- package.json | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/package.json b/package.json index 24a570aae91..2668642320c 100644 --- a/package.json +++ b/package.json @@ -1,6 +1,6 @@ { "name": "redis", - "version": "0.8.4", + "version": "0.8.5", "description": "Redis client library", "keywords": [ "redis", From 5f125b51d48eecd1171dd3e36400c609d7d8d5fc Mon Sep 17 00:00:00 2001 From: Bryce Baril Date: Wed, 2 Oct 2013 14:04:28 -0700 Subject: [PATCH 0133/1748] Issue #439 (and others): Stop assuming all "message" or "pmessage" replies are pubsub replies. Check pub_sub_mode as well. --- .gitignore | 1 + index.js | 11 ++++++++++- test.js | 11 +++++++++++ 3 files changed, 22 insertions(+), 1 deletion(-) diff --git a/.gitignore b/.gitignore index 3c3629e647f..6a59b3d64c3 100644 --- a/.gitignore +++ b/.gitignore @@ -1 +1,2 @@ node_modules +.tern-port diff --git a/index.js b/index.js index d16a15ff633..38606be7694 100644 --- a/index.js +++ b/index.js @@ -14,6 +14,12 @@ var net = require("net"), // can set this to true to enable for all connections exports.debug_mode = false; +var arraySlice = Array.prototype.slice +function trace() { + if (!exports.debug_mode) return; + console.log.apply(null, arraySlice.call(arguments)) +} + // hiredis might not be installed try { require("./lib/parser/hiredis"); @@ -602,7 +608,10 @@ RedisClient.prototype.return_reply = function (reply) { type = reply[0].toString(); } - if (type !== 'message' && type !== 'pmessage') { + if (this.pub_sub_mode && (type == 'message' || type == 'pmessage')) { + trace("received pubsub message"); + } + else { command_obj = this.command_queue.shift(); } diff --git a/test.js b/test.js index 79907a01600..5e204bc2c71 100644 --- a/test.js +++ b/test.js @@ -1281,6 +1281,17 @@ tests.HGETALL = function () { }); }; +tests.HGETALL_MESSAGE = function () { + var name = "HGETALL_MESSAGE"; + client.hmset("msg_test", {message: "hello"}, require_string("OK", name)); + client.hgetall("msg_test", function (err, obj) { + assert.strictEqual(null, err, name + " result sent back unexpected error: " + err); + assert.strictEqual(1, Object.keys(obj).length, name); + assert.strictEqual(obj.message, "hello") + next(name); + }); +}; + tests.HGETALL_NULL = function () { var name = "HGETALL_NULL"; From b12c49a20785d9b608158655e79638fac5d2fa2e Mon Sep 17 00:00:00 2001 From: Bryce Baril Date: Wed, 2 Oct 2013 16:10:26 -0700 Subject: [PATCH 0134/1748] Add unref() method to RedisClient that will call unref() on the socket connection, allowing it to close when unused. This is experimental because it does not yet understand the Redis protocol, so subscriptions or blocking operations will not work correctly. --- README.md | 20 ++++++++++++++++++++ index.js | 14 ++++++++++++++ test-unref.js | 12 ++++++++++++ test.js | 18 ++++++++++++++++++ 4 files changed, 64 insertions(+) create mode 100644 test-unref.js diff --git a/README.md b/README.md index b9285af50ee..babf5bf4bea 100644 --- a/README.md +++ b/README.md @@ -264,6 +264,26 @@ want to do this: `client.end()` is useful for timeout cases where something is stuck or taking too long and you want to start over. +## client.unref() + +Call `unref()` on the underlying socket connection to the Redis server, allowing the program to exit once no more commands are pending. + +This is an **experimental** feature, and only supports a subset of the Redis protocol. Any commands where client state is saved on the Redis server, e.g. `*SUBSCRIBE` or the blocking `BL*` commands will *NOT* work with `.unref()`. + +```js +var redis = require("redis") +var client = redis.createClient() + +/* + Calling unref() will allow this program to exit immediately after the get command finishes. Otherwise the client would hang as long as the client-server connection is alive. +*/ +client.unref() +client.get("foo", function (err, value){ + if (err) throw(err) + console.log(value) +}) +``` + ## Friendlier hash commands Most Redis commands take a single String or an Array of Strings as arguments, and replies are sent back as a single String or an Array of Strings. diff --git a/index.js b/index.js index 38606be7694..7a93a58dc8b 100644 --- a/index.js +++ b/index.js @@ -121,6 +121,20 @@ RedisClient.prototype.initialize_retry_vars = function () { this.attempts = 1; }; +RedisClient.prototype.unref = function () { + trace("User requesting to unref the connection"); + if (this.connected) { + trace("unref'ing the socket connection"); + this.stream.unref(); + } + else { + trace("Not connected yet, will unref later"); + this.once("connect", function () { + this.unref(); + }) + } +}; + // flush offline_queue and command_queue, erroring any items with a callback first RedisClient.prototype.flush_and_error = function (message) { var command_obj; diff --git a/test-unref.js b/test-unref.js new file mode 100644 index 00000000000..4a3cc3622d2 --- /dev/null +++ b/test-unref.js @@ -0,0 +1,12 @@ +var redis = require("./") +//redis.debug_mode = true +var PORT = process.argv[2] || 6379; +var HOST = process.argv[3] || '127.0.0.1'; + +var c = redis.createClient(PORT, HOST) +c.unref() +c.info(function (err, reply) { + if (err) process.exit(-1) + if (!reply.length) process.exit(-1) + process.stdout.write(reply.length.toString()) +}) \ No newline at end of file diff --git a/test.js b/test.js index 5e204bc2c71..89f67e1b347 100644 --- a/test.js +++ b/test.js @@ -10,6 +10,7 @@ var redis = require("./index"), assert = require("assert"), crypto = require("crypto"), util = require("./lib/util"), + fork = require("child_process").fork, test_db_num = 15, // this DB will be flushed and used for testing tests = {}, connected = false, @@ -2013,6 +2014,23 @@ tests.reconnectRetryMaxDelay = function() { }); }; +tests.unref = function () { + var name = "unref"; + var external = fork("./test-unref.js"); + var done = false; + external.on("close", function (code) { + assert(code == 0, "test-unref.js failed"); + done = true; + }) + setTimeout(function () { + if (!done) { + external.kill(); + } + assert(done, "test-unref.js didn't finish in time."); + next(name); + }, 100); +}; + all_tests = Object.keys(tests); all_start = new Date(); test_count = 0; From 31c22be6b93b4bd4f4ada7c9382e0ecc19def456 Mon Sep 17 00:00:00 2001 From: bobrik Date: Mon, 15 Jul 2013 23:10:54 +0400 Subject: [PATCH 0135/1748] String is not an error --- index.js | 9 ++++++--- 1 file changed, 6 insertions(+), 3 deletions(-) diff --git a/index.js b/index.js index 7a93a58dc8b..395c23f01d1 100644 --- a/index.js +++ b/index.js @@ -137,12 +137,15 @@ RedisClient.prototype.unref = function () { // flush offline_queue and command_queue, erroring any items with a callback first RedisClient.prototype.flush_and_error = function (message) { - var command_obj; + var command_obj, error; + + error = new Error(message); + while (this.offline_queue.length > 0) { command_obj = this.offline_queue.shift(); if (typeof command_obj.callback === "function") { try { - command_obj.callback(message); + command_obj.callback(error); } catch (callback_err) { process.nextTick(function () { throw callback_err; @@ -156,7 +159,7 @@ RedisClient.prototype.flush_and_error = function (message) { command_obj = this.command_queue.shift(); if (typeof command_obj.callback === "function") { try { - command_obj.callback(message); + command_obj.callback(error); } catch (callback_err) { process.nextTick(function () { throw callback_err; From a4f266684edb3d63b68b982f17172b41a3a4838b Mon Sep 17 00:00:00 2001 From: David Barshow Date: Wed, 31 Jul 2013 13:00:23 -0700 Subject: [PATCH 0136/1748] Make retry_max_delay option work correctly when retry_backoff becomes greater than one --- index.js | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/index.js b/index.js index 395c23f01d1..c458458ef1e 100644 --- a/index.js +++ b/index.js @@ -478,8 +478,8 @@ RedisClient.prototype.connection_gone = function (why) { } return; } - - if (this.retry_max_delay !== null && this.retry_delay > this.retry_max_delay) { + + if (this.retry_max_delay !== null && (this.retry_delay * this.retry_backoff) > this.retry_max_delay) { this.retry_delay = this.retry_max_delay; } else { this.retry_delay = Math.floor(this.retry_delay * this.retry_backoff); From d5c7a2ce2e44e0e50fb5d25abd79ca1a204dd7ca Mon Sep 17 00:00:00 2001 From: Bryce Baril Date: Wed, 2 Oct 2013 22:12:56 -0700 Subject: [PATCH 0137/1748] Formatting --- index.js | 7 ++++--- 1 file changed, 4 insertions(+), 3 deletions(-) diff --git a/index.js b/index.js index c458458ef1e..aa73e38772a 100644 --- a/index.js +++ b/index.js @@ -478,11 +478,12 @@ RedisClient.prototype.connection_gone = function (why) { } return; } - - if (this.retry_max_delay !== null && (this.retry_delay * this.retry_backoff) > this.retry_max_delay) { + + var nextDelay = Math.floor(this.retry_delay * this.retry_backoff); + if (this.retry_max_delay !== null && nextDelay > this.retry_max_delay) { this.retry_delay = this.retry_max_delay; } else { - this.retry_delay = Math.floor(this.retry_delay * this.retry_backoff); + this.retry_delay = nextDelay; } if (exports.debug_mode) { From 75cf487d739cf5cff534cb5499270b0b24814c7e Mon Sep 17 00:00:00 2001 From: Mathieu M-Gosselin Date: Fri, 30 Aug 2013 12:58:28 -0400 Subject: [PATCH 0138/1748] When handling the "error reply" event from the parser, we now bubble up the Error object if it's one instead of wrapping it in another Error. --- index.js | 6 +++++- 1 file changed, 5 insertions(+), 1 deletion(-) diff --git a/index.js b/index.js index aa73e38772a..b83492f916a 100644 --- a/index.js +++ b/index.js @@ -302,7 +302,11 @@ RedisClient.prototype.init_parser = function () { // "reply error" is an error sent back by Redis this.reply_parser.on("reply error", function (reply) { - self.return_error(new Error(reply)); + if (reply instanceof Error) { + self.return_error(reply); + } else { + self.return_error(new Error(reply)); + } }); this.reply_parser.on("reply", function (reply) { self.return_reply(reply); From 1d0d3aaf8f8e6d75dcd55f5c189931e5343d2fcc Mon Sep 17 00:00:00 2001 From: Bryce Baril Date: Wed, 2 Oct 2013 22:26:43 -0700 Subject: [PATCH 0139/1748] Give more time for unref test for slower systems --- test.js | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/test.js b/test.js index 89f67e1b347..e79832b4746 100644 --- a/test.js +++ b/test.js @@ -2028,7 +2028,7 @@ tests.unref = function () { } assert(done, "test-unref.js didn't finish in time."); next(name); - }, 100); + }, 500); }; all_tests = Object.keys(tests); From 5d999ab4404b79036d62871e3adf8308f2121594 Mon Sep 17 00:00:00 2001 From: Bryce Baril Date: Wed, 2 Oct 2013 22:29:01 -0700 Subject: [PATCH 0140/1748] Update changelog --- changelog.md | 10 ++++++++++ 1 file changed, 10 insertions(+) diff --git a/changelog.md b/changelog.md index b4b24a18a2f..673249c2dc9 100644 --- a/changelog.md +++ b/changelog.md @@ -1,10 +1,20 @@ Changelog ========= +## v0.8.6 - October 2, 2013 + +* If error is alredy an Error, don't wrap it in another Error. (Mathieu M-Gosselin) +* Fix retry delay logic (Ian Babrou) +* Return Errors instead of strings where Errors are expected (Ian Babrou) +* Add experimental `.unref()` method to RedisClient (Bryce Baril / Olivier Lalonde) +* Strengthen checking of reply to prevent conflating "message" or "pmessage" fields with pub_sub replies. (Bryce Baril) + ## v0.8.5 - September 26, 2013 + * Add `auth_pass` option to connect and immediately authenticate (Henrik Peinar) ## v0.8.4 - June 24, 2013 + Many contributed features and fixes, including: * Ignore password set if not needed. (jbergknoff) * Improved compatibility with 0.10.X for tests and client.end() (Bryce Baril) From ebf463556c20574d7e2fe2f50d5fdce77a9604e9 Mon Sep 17 00:00:00 2001 From: Bryce Baril Date: Wed, 2 Oct 2013 22:30:16 -0700 Subject: [PATCH 0141/1748] 0.8.6 --- package.json | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/package.json b/package.json index 2668642320c..ad3a1965894 100644 --- a/package.json +++ b/package.json @@ -1,6 +1,6 @@ { "name": "redis", - "version": "0.8.5", + "version": "0.8.6", "description": "Redis client library", "keywords": [ "redis", From 8c394c11016f43e27ae3cb6eb603c4c25c164bbf Mon Sep 17 00:00:00 2001 From: Chakrit Wichian Date: Fri, 11 Oct 2013 16:00:04 +0700 Subject: [PATCH 0142/1748] README for unix domain socket connection. --- README.md | 19 +++++++++++++++++++ 1 file changed, 19 insertions(+) diff --git a/README.md b/README.md index babf5bf4bea..d8bd75ae321 100644 --- a/README.md +++ b/README.md @@ -231,6 +231,25 @@ limits total amount of reconnects. `createClient()` returns a `RedisClient` object that is named `client` in all of the examples here. +### Unix Domain Socket + +You can also create a connection to Redis server via the unix domain socket if the server +has it enabled: + +```js +var redis = require("redis"); +var client = redis.createClient("/tmp/redis.sock"); +``` + +Sample `redis.conf` configuration to enable unix domain socket listening: + +```conf +unixsocket /tmp/redis.sock +unixsocketperm 755 +``` + +See [issue #204](https://github.com/mranney/node_redis/issues/204) for more information. + ## client.auth(password, callback) When connecting to Redis servers that require authentication, the `AUTH` command must be sent as the From 1d495d304ebd171551355f2496f42475bc00b948 Mon Sep 17 00:00:00 2001 From: Paul Verest Date: Mon, 14 Oct 2013 10:47:00 +0800 Subject: [PATCH 0143/1748] typo already --- changelog.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/changelog.md b/changelog.md index 673249c2dc9..7a77c3561f1 100644 --- a/changelog.md +++ b/changelog.md @@ -3,7 +3,7 @@ Changelog ## v0.8.6 - October 2, 2013 -* If error is alredy an Error, don't wrap it in another Error. (Mathieu M-Gosselin) +* If error is already an Error, don't wrap it in another Error. (Mathieu M-Gosselin) * Fix retry delay logic (Ian Babrou) * Return Errors instead of strings where Errors are expected (Ian Babrou) * Add experimental `.unref()` method to RedisClient (Bryce Baril / Olivier Lalonde) From 0b6870be5cab57315594fadd1fd205647b267932 Mon Sep 17 00:00:00 2001 From: Forrest L Norvell Date: Mon, 15 Oct 2012 00:21:55 -0700 Subject: [PATCH 0144/1748] Added direct support for domains. There are three pieces to this support, all of them small, and none of them with large overhead: 1. When sending a command, ensure that any callback is bound to the current domain, if one is present. 2. Also add the RedisClient to the current domain so that error events bubble properly. 3. In try_callback, when a domain is in play, instead of throwing on the next tick, emit the error on the domain. The parser can still finish processing the response and the error ends up in the correct place. --- index.js | 17 ++++++++++++++--- test.js | 24 ++++++++++++++++++++++++ 2 files changed, 38 insertions(+), 3 deletions(-) diff --git a/index.js b/index.js index b83492f916a..cfe321ce8af 100644 --- a/index.js +++ b/index.js @@ -573,14 +573,19 @@ RedisClient.prototype.return_error = function (err) { }; // if a callback throws an exception, re-throw it on a new stack so the parser can keep going. +// if a domain is active, emit the error on the domain, which will serve the same function. // put this try/catch in its own function because V8 doesn't optimize this well yet. function try_callback(callback, reply) { try { callback(null, reply); } catch (err) { - process.nextTick(function () { - throw err; - }); + if (process.domain) { + process.domain.emit('error', err); + } else { + process.nextTick(function () { + throw err; + }); + } } } @@ -750,6 +755,12 @@ RedisClient.prototype.send_command = function (command, args, callback) { throw new Error("send_command: second argument must be an array"); } + if (callback && process.domain) { + callback = process.domain.bind(callback); + // ensure that any unhandled error events emitted by the client are scoped to the correct domain + process.domain.add(this); + } + // if the last argument is an array and command is sadd or srem, expand it out: // client.sadd(arg1, [arg2, arg3, arg4], cb); // converts to: diff --git a/test.js b/test.js index e79832b4746..ad2e79c8007 100644 --- a/test.js +++ b/test.js @@ -1950,6 +1950,30 @@ tests.SLOWLOG = function () { }); } +tests.DOMAIN = function () { + var name = "DOMAIN"; + + var domain; + try { + domain = require('domain').create(); + } catch (err) { + console.log("Skipping " + name + " because this version of node doesn't have domains."); + next(name); + } + + if (domain) { + domain.run(function () { + client.set('domain', 'value', function (err, res) { + assert.ok(process.domain); + var notFound = res.not.existing.thing; // ohhh nooooo + }); + }); + + // this is the expected and desired behavior + domain.on('error', function (err) { next(name); }); + } +}; + // TODO - need a better way to test auth, maybe auto-config a local Redis server or something. // Yes, this is the real password. Please be nice, thanks. tests.auth = function () { From e7099717aa12ed624df79299f576566acf717ea3 Mon Sep 17 00:00:00 2001 From: Forrest L Norvell Date: Mon, 15 Oct 2012 11:02:04 -0700 Subject: [PATCH 0145/1748] Don't bind the RedisClient to the domain; callback should suffice. --- index.js | 6 +----- 1 file changed, 1 insertion(+), 5 deletions(-) diff --git a/index.js b/index.js index cfe321ce8af..3867f795557 100644 --- a/index.js +++ b/index.js @@ -755,11 +755,7 @@ RedisClient.prototype.send_command = function (command, args, callback) { throw new Error("send_command: second argument must be an array"); } - if (callback && process.domain) { - callback = process.domain.bind(callback); - // ensure that any unhandled error events emitted by the client are scoped to the correct domain - process.domain.add(this); - } + if (callback && process.domain) callback = process.domain.bind(callback); // if the last argument is an array and command is sadd or srem, expand it out: // client.sadd(arg1, [arg2, arg3, arg4], cb); From 2e8c6dae8531b308af9102af47d98b74a14209dc Mon Sep 17 00:00:00 2001 From: Forrest L Norvell Date: Thu, 17 Oct 2013 11:46:34 -0700 Subject: [PATCH 0146/1748] exit the domain after emitting. Thanks, cxreg! --- index.js | 1 + 1 file changed, 1 insertion(+) diff --git a/index.js b/index.js index 3867f795557..f915b460c48 100644 --- a/index.js +++ b/index.js @@ -581,6 +581,7 @@ function try_callback(callback, reply) { } catch (err) { if (process.domain) { process.domain.emit('error', err); + process.domain.exit(); } else { process.nextTick(function () { throw err; From dcad0a2da25519cca64a502a3fe1ddc49958d48a Mon Sep 17 00:00:00 2001 From: Bryce Baril Date: Thu, 17 Oct 2013 11:54:14 -0700 Subject: [PATCH 0147/1748] Update changelog for version 0.9.0 --- changelog.md | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/changelog.md b/changelog.md index 7a77c3561f1..cc52850f045 100644 --- a/changelog.md +++ b/changelog.md @@ -1,6 +1,10 @@ Changelog ========= +## v0.9.0 - October 17, 2013 + +* Domains support. (Forrest L Norvell) + ## v0.8.6 - October 2, 2013 * If error is already an Error, don't wrap it in another Error. (Mathieu M-Gosselin) From b93aee5fabe34318bf010e6c3328bf4409a59ded Mon Sep 17 00:00:00 2001 From: Bryce Baril Date: Thu, 17 Oct 2013 11:54:17 -0700 Subject: [PATCH 0148/1748] 0.9.0 --- package.json | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/package.json b/package.json index ad3a1965894..51f4d963616 100644 --- a/package.json +++ b/package.json @@ -1,6 +1,6 @@ { "name": "redis", - "version": "0.8.6", + "version": "0.9.0", "description": "Redis client library", "keywords": [ "redis", From a2695f3d171ac9f64893c87d6e562b6176e17b64 Mon Sep 17 00:00:00 2001 From: Alex Stokes Date: Wed, 20 Nov 2013 16:53:47 -0600 Subject: [PATCH 0149/1748] updated RedisClient.prototype.hmset to accept string or numeric keys when calling client.hmset(key, {key1: val1, key2: val2}) --- index.js | 11 +++++++++-- 1 file changed, 9 insertions(+), 2 deletions(-) diff --git a/index.js b/index.js index f915b460c48..39728bd0f20 100644 --- a/index.js +++ b/index.js @@ -1033,8 +1033,15 @@ RedisClient.prototype.hmset = function (args, callback) { callback = null; } - if (args.length === 2 && typeof args[0] === "string" && typeof args[1] === "object") { + if (args.length === 2 && (typeof args[0] === "string" || typeof args[0] === "number") && typeof args[1] === "object") { // User does: client.hmset(key, {key1: val1, key2: val2}) + // assuming key is a string, i.e. email address + + // if key is a number, i.e. timestamp, convert to string + if (typeof args[0] === "number") { + args[0] = args[0].toString(); + } + tmp_args = [ args[0] ]; tmp_keys = Object.keys(args[1]); for (i = 0, il = tmp_keys.length; i < il ; i++) { @@ -1044,7 +1051,7 @@ RedisClient.prototype.hmset = function (args, callback) { } args = tmp_args; } - + return this.send_command("hmset", args, callback); }; RedisClient.prototype.HMSET = RedisClient.prototype.hmset; From 2fbbe26127f9216a4a4a4623c747cb81b0536972 Mon Sep 17 00:00:00 2001 From: Alex Stokes Date: Wed, 20 Nov 2013 23:10:14 -0600 Subject: [PATCH 0150/1748] removed trailing whitespace --- index.js | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/index.js b/index.js index 39728bd0f20..d03b3755900 100644 --- a/index.js +++ b/index.js @@ -1051,7 +1051,7 @@ RedisClient.prototype.hmset = function (args, callback) { } args = tmp_args; } - + return this.send_command("hmset", args, callback); }; RedisClient.prototype.HMSET = RedisClient.prototype.hmset; From 6634e5eeaff67221c7c80a2aac2ab6af8fcd9468 Mon Sep 17 00:00:00 2001 From: Alex Stokes Date: Thu, 21 Nov 2013 11:44:33 -0600 Subject: [PATCH 0151/1748] added tests for HMSET using numeric key --- test.js | 16 +++++++++++++--- 1 file changed, 13 insertions(+), 3 deletions(-) diff --git a/test.js b/test.js index ad2e79c8007..7aa404aff31 100644 --- a/test.js +++ b/test.js @@ -811,7 +811,7 @@ tests.HLEN = function () { next(name); }); }); -} +}; tests.HMSET_BUFFER_AND_ARRAY = function () { // Saving a buffer and an array to the same key should not error @@ -828,8 +828,7 @@ tests.HMSET_BUFFER_AND_ARRAY = function () { // TODO - add test for HMSET with optional callbacks tests.HMGET = function () { - var key1 = "test hash 1", key2 = "test hash 2", name = "HMGET"; - + var key1 = "test hash 1", key2 = "test hash 2", key3 = 123456789, name = "HMGET"; // redis-like hmset syntax client.HMSET(key1, "0123456789", "abcdefghij", "some manner of key", "a type of value", require_string("OK", name)); @@ -839,6 +838,12 @@ tests.HMGET = function () { "some manner of key": "a type of value" }, require_string("OK", name)); + // test for numeric key + client.HMSET(key3, { + "0123456789": "abcdefghij", + "some manner of key": "a type of value" + }, require_string("OK", name)); + client.HMGET(key1, "0123456789", "some manner of key", function (err, reply) { assert.strictEqual("abcdefghij", reply[0].toString(), name); assert.strictEqual("a type of value", reply[1].toString(), name); @@ -849,6 +854,11 @@ tests.HMGET = function () { assert.strictEqual("a type of value", reply[1].toString(), name); }); + client.HMGET(key3, "0123456789", "some manner of key", function (err, reply) { + assert.strictEqual("abcdefghij", reply[0].toString(), name); + assert.strictEqual("a type of value", reply[1].toString(), name); + }); + client.HMGET(key1, ["0123456789"], function (err, reply) { assert.strictEqual("abcdefghij", reply[0], name); }); From 67e018cdb38ee6e8fcfa38a4378ad05c94e218de Mon Sep 17 00:00:00 2001 From: Alex Stokes Date: Thu, 21 Nov 2013 11:46:30 -0600 Subject: [PATCH 0152/1748] restored white space --- test.js | 1 + 1 file changed, 1 insertion(+) diff --git a/test.js b/test.js index 7aa404aff31..e72408fe4f3 100644 --- a/test.js +++ b/test.js @@ -829,6 +829,7 @@ tests.HMSET_BUFFER_AND_ARRAY = function () { tests.HMGET = function () { var key1 = "test hash 1", key2 = "test hash 2", key3 = 123456789, name = "HMGET"; + // redis-like hmset syntax client.HMSET(key1, "0123456789", "abcdefghij", "some manner of key", "a type of value", require_string("OK", name)); From 58385daf616d63f9aea1a10bd49c6be458e922d7 Mon Sep 17 00:00:00 2001 From: Alex Stokes Date: Thu, 21 Nov 2013 11:47:46 -0600 Subject: [PATCH 0153/1748] fixed spacing issue --- test.js | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/test.js b/test.js index e72408fe4f3..a92f65cde60 100644 --- a/test.js +++ b/test.js @@ -829,7 +829,7 @@ tests.HMSET_BUFFER_AND_ARRAY = function () { tests.HMGET = function () { var key1 = "test hash 1", key2 = "test hash 2", key3 = 123456789, name = "HMGET"; - + // redis-like hmset syntax client.HMSET(key1, "0123456789", "abcdefghij", "some manner of key", "a type of value", require_string("OK", name)); From c1926cd27c217a47326482ea3147673f92235992 Mon Sep 17 00:00:00 2001 From: Bryce Baril Date: Sat, 23 Nov 2013 11:44:29 -0800 Subject: [PATCH 0154/1748] Adding test for #457 --- test.js | 34 ++++++++++++++++++++++++++++++++++ 1 file changed, 34 insertions(+) diff --git a/test.js b/test.js index a92f65cde60..1147dc8901b 100644 --- a/test.js +++ b/test.js @@ -339,6 +339,40 @@ tests.MULTI_EXCEPTION_1 = function() { }); }; +tests.MULTI_8 = function () { + var name = "MULTI_8", multi1, multi2; + + // Provoke an error at queue time + multi1 = client.multi(); + multi1.mset("multifoo_8", "10", "multibar_8", "20", require_string("OK", name)); + multi1.set("foo2", require_error(name)); + multi1.set("foo3", require_error(name)); + multi1.incr("multifoo_8", require_number(11, name)); + multi1.incr("multibar_8", require_number(21, name)); + multi1.exec(function () { + require_error(name); + + // Redis 2.6.5+ will abort transactions with errors + // see: http://redis.io/topics/transactions + var multibar_expected = 22; + var multifoo_expected = 12; + if (server_version_at_least(client, [2, 6, 5])) { + multibar_expected = 1; + multifoo_expected = 1; + } + + // Confirm that the previous command, while containing an error, still worked. + multi2 = client.multi(); + multi2.incr("multibar_8", require_number(multibar_expected, name)); + multi2.incr("multifoo_8", require_number(multifoo_expected, name)); + multi2.exec(function (err, replies) { + assert.strictEqual(multibar_expected, replies[0]); + assert.strictEqual(multifoo_expected, replies[1]); + next(name); + }); + }); +}; + tests.FWD_ERRORS_1 = function () { var name = "FWD_ERRORS_1"; From 0d2f3b37df8337b66423a6842456a474791222bf Mon Sep 17 00:00:00 2001 From: Bryce Baril Date: Sat, 23 Nov 2013 11:45:52 -0800 Subject: [PATCH 0155/1748] 0.9.1 --- package.json | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/package.json b/package.json index 51f4d963616..f4f215f9d12 100644 --- a/package.json +++ b/package.json @@ -1,6 +1,6 @@ { "name": "redis", - "version": "0.9.0", + "version": "0.9.1", "description": "Redis client library", "keywords": [ "redis", From e9cd46a22352a54c3a52776ac7776885a58f778c Mon Sep 17 00:00:00 2001 From: Bryce Baril Date: Sat, 23 Nov 2013 11:48:28 -0800 Subject: [PATCH 0156/1748] Updating changelog.md for 0.9.1 --- changelog.md | 5 +++++ 1 file changed, 5 insertions(+) diff --git a/changelog.md b/changelog.md index cc52850f045..85f49b62c2d 100644 --- a/changelog.md +++ b/changelog.md @@ -1,6 +1,11 @@ Changelog ========= +## v0.9.1 - November 23, 2013 + +* Allow hmset to accept numeric keys. (Alex Stokes) +* Fix TypeError for multiple MULTI/EXEC errors. (Kwangsu Kim) + ## v0.9.0 - October 17, 2013 * Domains support. (Forrest L Norvell) From 355bea1e3ba3e5f198a56fbc5208fa1bd95573e9 Mon Sep 17 00:00:00 2001 From: William Hockey Date: Thu, 12 Dec 2013 12:04:34 -0800 Subject: [PATCH 0157/1748] Removing attempts reset - will be set to 1 in initialize_retry_vars --- index.js | 1 - 1 file changed, 1 deletion(-) diff --git a/index.js b/index.js index f2f88900fdd..aed785618cc 100644 --- a/index.js +++ b/index.js @@ -245,7 +245,6 @@ RedisClient.prototype.on_connect = function () { this.connected = true; this.ready = false; - this.attempts = 0; this.connections += 1; this.command_queue = new Queue(); this.emitted_end = false; From 313fd7f65be2866c01e967310d6ddb3bad15dfa2 Mon Sep 17 00:00:00 2001 From: William Hockey Date: Thu, 12 Dec 2013 12:05:38 -0800 Subject: [PATCH 0158/1748] only re-initializing retry variables if the connection is really connected --- index.js | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/index.js b/index.js index aed785618cc..0ea23123779 100644 --- a/index.js +++ b/index.js @@ -229,6 +229,8 @@ RedisClient.prototype.do_auth = function () { // now we are really connected self.emit("connect"); + self.initialize_retry_vars(); + if (self.options.no_ready_check) { self.on_ready(); } else { @@ -248,7 +250,6 @@ RedisClient.prototype.on_connect = function () { this.connections += 1; this.command_queue = new Queue(); this.emitted_end = false; - this.initialize_retry_vars(); if (this.options.socket_nodelay) { this.stream.setNoDelay(); } @@ -260,6 +261,7 @@ RedisClient.prototype.on_connect = function () { this.do_auth(); } else { this.emit("connect"); + this.initialize_retry_vars(); if (this.options.no_ready_check) { this.on_ready(); From 656ecbbbf4b2d78b98bed19faa96aeb86d9b1828 Mon Sep 17 00:00:00 2001 From: Marek Date: Sun, 15 Dec 2013 16:01:33 +0000 Subject: [PATCH 0159/1748] Regenerate commands.js to match redis 2.8.0. Adds support for 'config rewrite', 'pubsub', 'scan', 'sscan', 'hscan' and 'zscan' --- lib/commands.js | 10 ++++++++-- 1 file changed, 8 insertions(+), 2 deletions(-) diff --git a/lib/commands.js b/lib/commands.js index 4abfe68668a..669afe41618 100644 --- a/lib/commands.js +++ b/lib/commands.js @@ -1,4 +1,4 @@ -// This file was generated by ./generate_commands.js on Sun Feb 17 2013 19:04:47 GMT-0500 (EST) +// This file was generated by ./generate_commands.js on Sun Dec 15 2013 15:58:20 GMT+0000 (GMT) module.exports = [ "append", "auth", @@ -14,6 +14,7 @@ module.exports = [ "client getname", "client setname", "config get", + "config rewrite", "config set", "config resetstat", "dbsize", @@ -80,6 +81,7 @@ module.exports = [ "ping", "psetex", "psubscribe", + "pubsub", "pttl", "publish", "punsubscribe", @@ -145,5 +147,9 @@ module.exports = [ "zrevrangebyscore", "zrevrank", "zscore", - "zunionstore" + "zunionstore", + "scan", + "sscan", + "hscan", + "zscan" ]; From 36384e850eb4d3692f0c81511410edf1376d343c Mon Sep 17 00:00:00 2001 From: Bryce Baril Date: Sun, 15 Dec 2013 14:54:23 -0800 Subject: [PATCH 0160/1748] Update changelog for 0.9.2 --- changelog.md | 5 +++++ 1 file changed, 5 insertions(+) diff --git a/changelog.md b/changelog.md index 85f49b62c2d..033652571b3 100644 --- a/changelog.md +++ b/changelog.md @@ -1,6 +1,11 @@ Changelog ========= +## v0.9.2 - December 15, 2013 + +* Regenerate commands for new 2.8.x Redis commands. (Marek Ventur) +* Correctly time reconnect counts when using 'auth'. (William Hockey) + ## v0.9.1 - November 23, 2013 * Allow hmset to accept numeric keys. (Alex Stokes) From 34a2375bec86bf6684f228fc90f78ac4cc27ff27 Mon Sep 17 00:00:00 2001 From: Bryce Baril Date: Sun, 15 Dec 2013 14:54:28 -0800 Subject: [PATCH 0161/1748] 0.9.2 --- package.json | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/package.json b/package.json index f4f215f9d12..8a2f7158010 100644 --- a/package.json +++ b/package.json @@ -1,6 +1,6 @@ { "name": "redis", - "version": "0.9.1", + "version": "0.9.2", "description": "Redis client library", "keywords": [ "redis", From 6a3ccf64f4b49804e0f3514ce3aab4baafd67b00 Mon Sep 17 00:00:00 2001 From: Bryce Baril Date: Sat, 21 Dec 2013 11:08:00 -0800 Subject: [PATCH 0162/1748] Client to emit errors now instead of throwing them asynchronously where they're uncatchable. --- index.js | 28 +++++++----------------- test.js | 66 ++++++++++++++++++++++++++++++++++++++++++++++++++++++++ 2 files changed, 74 insertions(+), 20 deletions(-) diff --git a/index.js b/index.js index 0ea23123779..7f0fd588b9b 100644 --- a/index.js +++ b/index.js @@ -147,9 +147,7 @@ RedisClient.prototype.flush_and_error = function (message) { try { command_obj.callback(error); } catch (callback_err) { - process.nextTick(function () { - throw callback_err; - }); + this.emit("error", callback_err); } } } @@ -161,9 +159,7 @@ RedisClient.prototype.flush_and_error = function (message) { try { command_obj.callback(error); } catch (callback_err) { - process.nextTick(function () { - throw callback_err; - }); + this.emit("error", callback_err); } } } @@ -559,24 +555,18 @@ RedisClient.prototype.return_error = function (err) { try { command_obj.callback(err); } catch (callback_err) { - // if a callback throws an exception, re-throw it on a new stack so the parser can keep going - process.nextTick(function () { - throw callback_err; - }); + this.emit("error", callback_err); } } else { console.log("node_redis: no callback to send error: " + err.message); - // this will probably not make it anywhere useful, but we might as well throw - process.nextTick(function () { - throw err; - }); + this.emit("error", err); } }; // if a callback throws an exception, re-throw it on a new stack so the parser can keep going. // if a domain is active, emit the error on the domain, which will serve the same function. // put this try/catch in its own function because V8 doesn't optimize this well yet. -function try_callback(callback, reply) { +function try_callback(client, callback, reply) { try { callback(null, reply); } catch (err) { @@ -584,9 +574,7 @@ function try_callback(callback, reply) { process.domain.emit('error', err); process.domain.exit(); } else { - process.nextTick(function () { - throw err; - }); + client.emit("error", err); } } } @@ -668,7 +656,7 @@ RedisClient.prototype.return_reply = function (reply) { reply = reply_to_object(reply); } - try_callback(command_obj.callback, reply); + try_callback(this, command_obj.callback, reply); } else if (exports.debug_mode) { console.log("no callback for reply: " + (reply && reply.toString && reply.toString())); } @@ -694,7 +682,7 @@ RedisClient.prototype.return_reply = function (reply) { // reply[1] can be null var reply1String = (reply[1] === null) ? null : reply[1].toString(); if (command_obj && typeof command_obj.callback === "function") { - try_callback(command_obj.callback, reply1String); + try_callback(this, command_obj.callback, reply1String); } this.emit(type, reply1String, reply[2]); // channel, count } else { diff --git a/test.js b/test.js index 1147dc8901b..b6850440cbe 100644 --- a/test.js +++ b/test.js @@ -402,6 +402,72 @@ tests.FWD_ERRORS_1 = function () { }, 150); }; +tests.FWD_ERRORS_2 = function () { + var name = "FWD_ERRORS_2"; + + var toThrow = new Error("Forced exception"); + var recordedError = null; + + var originalHandler = client.listeners("error").pop(); + client.removeAllListeners("error"); + client.once("error", function (err) { + recordedError = err; + }); + + client.get("no_such_key", function (err, reply) { + throw toThrow; + }); + + setTimeout(function () { + client.listeners("error").push(originalHandler); + assert.equal(recordedError, toThrow, "Should have caught our forced exception"); + next(name); + }, 150); +}; + +tests.FWD_ERRORS_3 = function () { + var name = "FWD_ERRORS_3"; + + var recordedError = null; + + var originalHandler = client.listeners("error").pop(); + client.removeAllListeners("error"); + client.once("error", function (err) { + recordedError = err; + }); + + client.send_command("no_such_command", []); + + setTimeout(function () { + client.listeners("error").push(originalHandler); + assert.ok(recordedError instanceof Error); + next(name); + }, 150); +}; + +tests.FWD_ERRORS_4 = function () { + var name = "FWD_ERRORS_4"; + + var toThrow = new Error("Forced exception"); + var recordedError = null; + + var originalHandler = client.listeners("error").pop(); + client.removeAllListeners("error"); + client.once("error", function (err) { + recordedError = err; + }); + + client.send_command("no_such_command", [], function () { + throw toThrow; + }); + + setTimeout(function () { + client.listeners("error").push(originalHandler); + assert.equal(recordedError, toThrow, "Should have caught our forced exception"); + next(name); + }, 150); +}; + tests.EVAL_1 = function () { var name = "EVAL_1"; From f1b11603fdf5f779445f2a0fc8ff39c2373ae434 Mon Sep 17 00:00:00 2001 From: Bryce Baril Date: Sat, 21 Dec 2013 13:25:56 -0800 Subject: [PATCH 0163/1748] 0.10.0 --- package.json | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/package.json b/package.json index 8a2f7158010..643befba16b 100644 --- a/package.json +++ b/package.json @@ -1,6 +1,6 @@ { "name": "redis", - "version": "0.9.2", + "version": "0.10.0", "description": "Redis client library", "keywords": [ "redis", From f6019658e6b05be2e295b266315aa5d12d6a6021 Mon Sep 17 00:00:00 2001 From: Bryce Baril Date: Sat, 21 Dec 2013 13:53:51 -0800 Subject: [PATCH 0164/1748] update changelog for 0.10.0 --- changelog.md | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/changelog.md b/changelog.md index 033652571b3..b59b1cf78fb 100644 --- a/changelog.md +++ b/changelog.md @@ -1,6 +1,10 @@ Changelog ========= +## v0.10.0 - December 21, 2013 + +* Instead of throwing errors asynchronously, emit errors on client. (Bryce Baril) + ## v0.9.2 - December 15, 2013 * Regenerate commands for new 2.8.x Redis commands. (Marek Ventur) From 0367a64bff369bafed1c16d362967758f0dacd8b Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Robert=20Sk=C3=B6ld?= Date: Mon, 17 Feb 2014 13:26:16 +0100 Subject: [PATCH 0165/1748] Handle undefined redis_version Apparently some servers don't send the redis_version with the INFO command (*cough* redis cloud *cough*). This causes the app to crash violently (yay node!). --- index.js | 8 +++++--- 1 file changed, 5 insertions(+), 3 deletions(-) diff --git a/index.js b/index.js index 7f0fd588b9b..6e87bb123d2 100644 --- a/index.js +++ b/index.js @@ -378,9 +378,11 @@ RedisClient.prototype.on_info_cmd = function (err, res) { }); obj.versions = []; - obj.redis_version.split('.').forEach(function (num) { - obj.versions.push(+num); - }); + if( obj.redis_version ){ + obj.redis_version.split('.').forEach(function (num) { + obj.versions.push(+num); + }); + } // expose info key/vals to users this.server_info = obj; From 15294e785625530c244b8992a2707c44d3b958aa Mon Sep 17 00:00:00 2001 From: Bryce Baril Date: Mon, 17 Feb 2014 17:53:31 -0800 Subject: [PATCH 0166/1748] Update changelog for 0.10.1 --- changelog.md | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/changelog.md b/changelog.md index b59b1cf78fb..cc46a5d6211 100644 --- a/changelog.md +++ b/changelog.md @@ -1,6 +1,10 @@ Changelog ========= +## v0.10.1 - February 17, 2014 + +* Skip plucking redis version from the INFO stream if INFO results weren't provided. (Robert Sköld) + ## v0.10.0 - December 21, 2013 * Instead of throwing errors asynchronously, emit errors on client. (Bryce Baril) From 2ff2a74ef6cfe8289752e8fbcc7d2f60610088db Mon Sep 17 00:00:00 2001 From: Bryce Baril Date: Mon, 17 Feb 2014 17:53:33 -0800 Subject: [PATCH 0167/1748] 0.10.1 --- package.json | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/package.json b/package.json index 643befba16b..14ea02f188e 100644 --- a/package.json +++ b/package.json @@ -1,6 +1,6 @@ { "name": "redis", - "version": "0.10.0", + "version": "0.10.1", "description": "Redis client library", "keywords": [ "redis", From 4672479b917e955ca1a9b17a4172acac9bd9a06b Mon Sep 17 00:00:00 2001 From: Bryan English Date: Tue, 18 Feb 2014 22:02:33 -0800 Subject: [PATCH 0168/1748] If there's an error in SELECT and there's no callback, emit the error. --- index.js | 2 ++ test.js | 10 ++++++++++ 2 files changed, 12 insertions(+) diff --git a/index.js b/index.js index 6e87bb123d2..0291f17be31 100644 --- a/index.js +++ b/index.js @@ -978,6 +978,8 @@ RedisClient.prototype.select = function (db, callback) { } if (typeof(callback) === 'function') { callback(err, res); + } else if (err) { + self.emit('error', err); } }); }; diff --git a/test.js b/test.js index b6850440cbe..e0bcff42aa9 100644 --- a/test.js +++ b/test.js @@ -865,6 +865,16 @@ tests.reconnect_select_db_after_pubsub = function() { }); }; +tests.select_error_emits_if_no_callback = function () { + var name = "select_error_emits_if_no_callback"; + + client.on('error', with_timeout(name, function (err) { + require_error(name)(err); + next(name); + }, 500)); + client.select(9999); +}; + tests.idle = function () { var name = "idle"; From d313aa5dab5b8ab25392cd42588e22ca73dc33c2 Mon Sep 17 00:00:00 2001 From: Mohit Gupta Date: Tue, 25 Mar 2014 16:33:43 -0700 Subject: [PATCH 0169/1748] param socket_keepalive to set keep-alive on socket --- README.md | 1 + index.js | 4 ++++ 2 files changed, 5 insertions(+) diff --git a/README.md b/README.md index d8bd75ae321..5e9a94eae04 100644 --- a/README.md +++ b/README.md @@ -192,6 +192,7 @@ every command on a client. * `socket_nodelay`: defaults to `true`. Whether to call setNoDelay() on the TCP stream, which disables the Nagle algorithm on the underlying socket. Setting this option to `false` can result in additional throughput at the cost of more latency. Most applications will want this set to `true`. +* `socket_keepalive` defaults to `false`. Whether the keep-alive functionality is enabled on the underlying socket. * `no_ready_check`: defaults to `false`. When a connection is established to the Redis server, the server might still be loading the database from disk. While loading, the server not respond to any commands. To work around this, `node_redis` has a "ready check" which sends the `INFO` command to the server. The response from the `INFO` command diff --git a/index.js b/index.js index 0291f17be31..0ef65ff8961 100644 --- a/index.js +++ b/index.js @@ -43,6 +43,9 @@ function RedisClient(stream, options) { if (this.options.socket_nodelay === undefined) { this.options.socket_nodelay = true; } + if (this.options.socket_keepalive === undefined) { + this.options.socket_keepalive = false; + } this.should_buffer = false; this.command_queue_high_water = this.options.command_queue_high_water || 1000; this.command_queue_low_water = this.options.command_queue_low_water || 0; @@ -249,6 +252,7 @@ RedisClient.prototype.on_connect = function () { if (this.options.socket_nodelay) { this.stream.setNoDelay(); } + this.stream.setKeepAlive(this.options.socket_keepalive); this.stream.setTimeout(0); this.init_parser(); From 7d61feb411c130e53acf4aa092d18a6cd016e0f2 Mon Sep 17 00:00:00 2001 From: "Raynos (Jake Verbaten)" Date: Fri, 18 Apr 2014 13:54:16 -0700 Subject: [PATCH 0170/1748] Check that `process.domain` still exists --- index.js | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/index.js b/index.js index 0291f17be31..8bdd8ce85a9 100644 --- a/index.js +++ b/index.js @@ -574,7 +574,9 @@ function try_callback(client, callback, reply) { } catch (err) { if (process.domain) { process.domain.emit('error', err); - process.domain.exit(); + if (process.domain) { + process.domain.exit(); + } } else { client.emit("error", err); } From d34308ea4c53f16c6a402d8d318644abe52d9118 Mon Sep 17 00:00:00 2001 From: Raynos Date: Fri, 18 Apr 2014 13:57:35 -0700 Subject: [PATCH 0171/1748] check process.domain is STILL currDomain --- index.js | 7 ++++--- 1 file changed, 4 insertions(+), 3 deletions(-) diff --git a/index.js b/index.js index 8bdd8ce85a9..e571de0dab1 100644 --- a/index.js +++ b/index.js @@ -573,9 +573,10 @@ function try_callback(client, callback, reply) { callback(null, reply); } catch (err) { if (process.domain) { - process.domain.emit('error', err); - if (process.domain) { - process.domain.exit(); + var currDomain = process.domain; + currDomain.emit('error', err); + if (process.domain === currDomain) { + currDomain.exit(); } } else { client.emit("error", err); From ef1a90642d509ee2724d2b2dd449e041da0210f3 Mon Sep 17 00:00:00 2001 From: Charles Feng Date: Wed, 23 Apr 2014 14:52:17 -0700 Subject: [PATCH 0172/1748] Regenerate commands.js for redis 2.8.9. --- lib/commands.js | 10 +++++++++- 1 file changed, 9 insertions(+), 1 deletion(-) diff --git a/lib/commands.js b/lib/commands.js index 669afe41618..b0365350cda 100644 --- a/lib/commands.js +++ b/lib/commands.js @@ -1,4 +1,4 @@ -// This file was generated by ./generate_commands.js on Sun Dec 15 2013 15:58:20 GMT+0000 (GMT) +// This file was generated by ./generate_commands.js on Wed Apr 23 2014 14:51:21 GMT-0700 (PDT) module.exports = [ "append", "auth", @@ -6,12 +6,14 @@ module.exports = [ "bgsave", "bitcount", "bitop", + "bitpos", "blpop", "brpop", "brpoplpush", "client kill", "client list", "client getname", + "client pause", "client setname", "config get", "config rewrite", @@ -78,6 +80,9 @@ module.exports = [ "persist", "pexpire", "pexpireat", + "pfadd", + "pfcount", + "pfmerge", "ping", "psetex", "psubscribe", @@ -137,10 +142,13 @@ module.exports = [ "zcount", "zincrby", "zinterstore", + "zlexcount", "zrange", + "zrangebylex", "zrangebyscore", "zrank", "zrem", + "zremrangebylex", "zremrangebyrank", "zremrangebyscore", "zrevrange", From b1d8ff9ae8e57a5cac335baa9ac29dbcaecd5a88 Mon Sep 17 00:00:00 2001 From: Nick Apperson Date: Wed, 7 May 2014 14:14:11 -0500 Subject: [PATCH 0173/1748] reply_to_object function is broken for binary keys There is currently no way to opt out of the way hgetall works with the node_redis library and if any of the keys have binary, they are incorrectly converted to the charactor 0xFEFF instead. This makes it impossible to fully use HGETALL. --- index.js | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/index.js b/index.js index 0291f17be31..13964983e0e 100644 --- a/index.js +++ b/index.js @@ -590,7 +590,7 @@ function reply_to_object(reply) { } for (j = 0, jl = reply.length; j < jl; j += 2) { - key = reply[j].toString(); + key = reply[j].toString('binary'); val = reply[j + 1]; obj[key] = val; } From f9916ed45c0727391899618820f770312593feb6 Mon Sep 17 00:00:00 2001 From: CrypticSwarm Date: Mon, 12 May 2014 21:23:04 -0500 Subject: [PATCH 0174/1748] cleanup after faulty test --- test.js | 7 ++++--- 1 file changed, 4 insertions(+), 3 deletions(-) diff --git a/test.js b/test.js index e0bcff42aa9..96e7bb5c53d 100644 --- a/test.js +++ b/test.js @@ -867,11 +867,12 @@ tests.reconnect_select_db_after_pubsub = function() { tests.select_error_emits_if_no_callback = function () { var name = "select_error_emits_if_no_callback"; - - client.on('error', with_timeout(name, function (err) { + var handler = with_timeout(name, function (err) { require_error(name)(err); + client.removeListener('error', handler); next(name); - }, 500)); + }, 500); + client.on('error', handler); client.select(9999); }; From 6f00ff8ffe08bd7440bdb39c9a0c4604af59e9e1 Mon Sep 17 00:00:00 2001 From: Nick Apperson Date: Mon, 12 May 2014 21:25:46 -0500 Subject: [PATCH 0175/1748] Add a test for binary client hgetall binary keys --- test.js | 15 +++++++++++++++ 1 file changed, 15 insertions(+) diff --git a/test.js b/test.js index e0bcff42aa9..540f327d947 100644 --- a/test.js +++ b/test.js @@ -1403,6 +1403,21 @@ tests.HGETALL = function () { }); }; +tests.HGETALL_2 = function () { + var name = "HGETALL (Binary client)"; + bclient.hmset(["bhosts", "mjr", "1", "another", "23", "home", "1234", new Buffer([0xAA, 0xBB, 0x00, 0xF0]), new Buffer([0xCC, 0xDD, 0x00, 0xF0])], require_string("OK", name)); + bclient.HGETALL(["bhosts"], function (err, obj) { + assert.strictEqual(null, err, name + " result sent back unexpected error: " + err); + assert.strictEqual(4, Object.keys(obj).length, name); + assert.strictEqual("1", obj.mjr.toString(), name); + assert.strictEqual("23", obj.another.toString(), name); + assert.strictEqual("1234", obj.home.toString(), name); + assert.strictEqual((new Buffer([0xAA, 0xBB, 0x00, 0xF0])).toString('binary'), Object.keys(obj)[3], name); + assert.strictEqual((new Buffer([0xCC, 0xDD, 0x00, 0xF0])).toString('binary'), obj[(new Buffer([0xAA, 0xBB, 0x00, 0xF0])).toString('binary')].toString('binary'), name); + next(name); + }); +}; + tests.HGETALL_MESSAGE = function () { var name = "HGETALL_MESSAGE"; client.hmset("msg_test", {message: "hello"}, require_string("OK", name)); From ead286f549b9875a000202747e61dc7b1ee2f907 Mon Sep 17 00:00:00 2001 From: Bryce Baril Date: Sun, 18 May 2014 15:30:59 -0700 Subject: [PATCH 0176/1748] update changelog --- changelog.md | 5 +++++ 1 file changed, 5 insertions(+) diff --git a/changelog.md b/changelog.md index cc46a5d6211..80bed2b6884 100644 --- a/changelog.md +++ b/changelog.md @@ -1,6 +1,11 @@ Changelog ========= +## v0.10.2 - May 18, 2014 + +* Fix test not resetting `error` handler. (CrypticSwarm) +* Fix SELECT error semantics. (Bryan English) + ## v0.10.1 - February 17, 2014 * Skip plucking redis version from the INFO stream if INFO results weren't provided. (Robert Sköld) From 1d1fde6b33225d96472bab8e43aadd9b65f05e5d Mon Sep 17 00:00:00 2001 From: Bryce Baril Date: Sun, 18 May 2014 15:33:15 -0700 Subject: [PATCH 0177/1748] Update changelog --- changelog.md | 1 + 1 file changed, 1 insertion(+) diff --git a/changelog.md b/changelog.md index 80bed2b6884..76d6bb8f83f 100644 --- a/changelog.md +++ b/changelog.md @@ -3,6 +3,7 @@ Changelog ## v0.10.2 - May 18, 2014 +* Better binay key handlign for HGETALL. (Nick Apperson) * Fix test not resetting `error` handler. (CrypticSwarm) * Fix SELECT error semantics. (Bryan English) From 4f9c6bd21912b1404089777d4e409d7a55ce1250 Mon Sep 17 00:00:00 2001 From: Bryce Baril Date: Sun, 18 May 2014 15:33:21 -0700 Subject: [PATCH 0178/1748] 0.10.2 --- package.json | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/package.json b/package.json index 14ea02f188e..00607830bf3 100644 --- a/package.json +++ b/package.json @@ -1,6 +1,6 @@ { "name": "redis", - "version": "0.10.1", + "version": "0.10.2", "description": "Redis client library", "keywords": [ "redis", From fb213183eeee588ce410167c2b43becbf81ff954 Mon Sep 17 00:00:00 2001 From: Bryce Baril Date: Thu, 22 May 2014 08:56:56 -0700 Subject: [PATCH 0179/1748] Update changelog --- changelog.md | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/changelog.md b/changelog.md index 76d6bb8f83f..e0ae48c92a1 100644 --- a/changelog.md +++ b/changelog.md @@ -1,6 +1,10 @@ Changelog ========= +## v0.10.3 - May 22, 2014 + +* Update command list to match Redis 2.8.9 (Charles Feng) + ## v0.10.2 - May 18, 2014 * Better binay key handlign for HGETALL. (Nick Apperson) From c68b3f7bd38b3814a13519211dfbd1084099a6f0 Mon Sep 17 00:00:00 2001 From: Bryce Baril Date: Thu, 22 May 2014 08:57:20 -0700 Subject: [PATCH 0180/1748] 0.10.3 --- package.json | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/package.json b/package.json index 00607830bf3..1e408a2b12e 100644 --- a/package.json +++ b/package.json @@ -1,6 +1,6 @@ { "name": "redis", - "version": "0.10.2", + "version": "0.10.3", "description": "Redis client library", "keywords": [ "redis", From 1c983a11717625471d3671677a639c10b353732c Mon Sep 17 00:00:00 2001 From: linkangzhen Date: Tue, 27 May 2014 09:01:28 +0800 Subject: [PATCH 0181/1748] fixed redis quit,we call client.end(),remove "error" event listener,the retry_timer try to connect;when there is a "error" exception,the process will exit --- index.js | 8 ++++++++ 1 file changed, 8 insertions(+) diff --git a/index.js b/index.js index 13964983e0e..12e9c7aa2e6 100644 --- a/index.js +++ b/index.js @@ -908,6 +908,14 @@ RedisClient.prototype.pub_sub_command = function (command_obj) { RedisClient.prototype.end = function () { this.stream._events = {}; + + if(this.retry_timer){ + clearTimeout(this.retry_timer); + this.retry_timer=null; + this.stream.on("error", function(){ + }); + } + this.connected = false; this.ready = false; this.closing = true; From c7dbfec85ccb011353e7320109203bb936386ba9 Mon Sep 17 00:00:00 2001 From: linkangzhen Date: Tue, 27 May 2014 09:14:01 +0800 Subject: [PATCH 0182/1748] prototype.end move on("error", to outsite --- index.js | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/index.js b/index.js index 12e9c7aa2e6..8c4d51d59b3 100644 --- a/index.js +++ b/index.js @@ -909,12 +909,12 @@ RedisClient.prototype.pub_sub_command = function (command_obj) { RedisClient.prototype.end = function () { this.stream._events = {}; + //clear retry_timer if(this.retry_timer){ clearTimeout(this.retry_timer); this.retry_timer=null; - this.stream.on("error", function(){ - }); } + this.stream.on("error", function(){}); this.connected = false; this.ready = false; From 9c68a286b1b0be913bbbca112ea4d241eda96d91 Mon Sep 17 00:00:00 2001 From: migounette Date: Mon, 23 Jun 2014 16:38:37 +0200 Subject: [PATCH 0183/1748] Add full IPv6 redis connection capability in a full IPv6 network or in a mix network (IPv4 and IPv6) --- README.md | 1 + changelog.md | 7 +++++++ index.js | 17 +++++++++++------ 3 files changed, 19 insertions(+), 6 deletions(-) diff --git a/README.md b/README.md index d8bd75ae321..b837bb99cbf 100644 --- a/README.md +++ b/README.md @@ -210,6 +210,7 @@ limits total time for client to reconnect. Value is provided in milliseconds and * `max_attempts` defaults to `null`. By default client will try reconnecting until connected. Setting `max_attempts` limits total amount of reconnects. * `auth_pass` defaults to `null`. By default client will try connecting without auth. If set, client will run redis auth command on connect. +* `family` defaults to `IPv4`. By default client will try connecting with a IPv4 DNS resolution when a FQDN host is set. You can also specify and IPv6 for forcing a IPv6 FQDN resolution. ```js var redis = require("redis"), diff --git a/changelog.md b/changelog.md index e0ae48c92a1..74aa941e098 100644 --- a/changelog.md +++ b/changelog.md @@ -1,6 +1,13 @@ Changelog ========= +## v0.10.4 - Jun 23, 2014 + +* Add redis-cli createClient(port, host, options) full IPv6 Network capability + Specify the family type IPv4 or IPv6 in the options object. + + eg: { 'family' : 'IPv6' } + ## v0.10.3 - May 22, 2014 * Update command list to match Redis 2.8.9 (Charles Feng) diff --git a/index.js b/index.js index 13964983e0e..f4e0095c0e4 100644 --- a/index.js +++ b/index.js @@ -1182,16 +1182,21 @@ RedisClient.prototype.eval = RedisClient.prototype.EVAL = function () { exports.createClient = function (port_arg, host_arg, options) { - var port = port_arg || default_port, - host = host_arg || default_host, - redis_client, net_client; - net_client = net.createConnection(port, host); + var cnxOptions = { + 'port' : port_arg || default_port, + 'host' : host_arg || default_host, + 'family' : options.family || 'IPv4' + }; + + var redis_client, net_client; + + net_client = net.createConnection(cnxOptions); redis_client = new RedisClient(net_client, options); - redis_client.port = port; - redis_client.host = host; + redis_client.port = cnxOptions.port; + redis_client.host = cnxOptions.host; return redis_client; }; From 890f60ac572d2675db5ab014619a31e91a7cca2a Mon Sep 17 00:00:00 2001 From: migounette Date: Mon, 23 Jun 2014 16:40:40 +0200 Subject: [PATCH 0184/1748] change the version --- package.json | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/package.json b/package.json index 1e408a2b12e..bb9e78962c7 100644 --- a/package.json +++ b/package.json @@ -1,6 +1,6 @@ { "name": "redis", - "version": "0.10.3", + "version": "0.10.4", "description": "Redis client library", "keywords": [ "redis", From c687d2e4fd83eaf414439f83f3c9414c309eeb3a Mon Sep 17 00:00:00 2001 From: Iain Proctor Date: Tue, 8 Jul 2014 21:36:23 +0000 Subject: [PATCH 0185/1748] Make a new stream on reconnect --- index.js | 17 +++++++++++------ 1 file changed, 11 insertions(+), 6 deletions(-) diff --git a/index.js b/index.js index 13964983e0e..1420179b12a 100644 --- a/index.js +++ b/index.js @@ -81,6 +81,14 @@ function RedisClient(stream, options) { this.old_state = null; + this.install_stream_listeners(); + + events.EventEmitter.call(this); +} +util.inherits(RedisClient, events.EventEmitter); +exports.RedisClient = RedisClient; + +RedisClient.prototype.install_stream_listeners = function() { var self = this; this.stream.on("connect", function () { @@ -107,11 +115,7 @@ function RedisClient(stream, options) { self.should_buffer = false; self.emit("drain"); }); - - events.EventEmitter.call(this); -} -util.inherits(RedisClient, events.EventEmitter); -exports.RedisClient = RedisClient; +}; RedisClient.prototype.initialize_retry_vars = function () { this.retry_timer = null; @@ -520,7 +524,8 @@ RedisClient.prototype.connection_gone = function (why) { return; } - self.stream.connect(self.port, self.host); + self.stream = net.createConnection(self.port, self.host); + self.install_stream_listeners(); self.retry_timer = null; }, this.retry_delay); }; From 40f85aa42ac2600b90bc99c517934a8e198313b2 Mon Sep 17 00:00:00 2001 From: migounette Date: Wed, 9 Jul 2014 10:39:08 +0200 Subject: [PATCH 0186/1748] Add IPv6 and IPv4 tests --- index.js | 8 +++++++- test.js | 45 +++++++++++++++++++++++++++++++++++++++++++++ 2 files changed, 52 insertions(+), 1 deletion(-) diff --git a/index.js b/index.js index f4e0095c0e4..3b16d08e321 100644 --- a/index.js +++ b/index.js @@ -1183,10 +1183,16 @@ RedisClient.prototype.eval = RedisClient.prototype.EVAL = function () { exports.createClient = function (port_arg, host_arg, options) { + var cnxFamily; + + if (options && options.family) { + cnxFamily = (options.family == 'IPv6' ? 6 : 4); + } + var cnxOptions = { 'port' : port_arg || default_port, 'host' : host_arg || default_host, - 'family' : options.family || 'IPv4' + 'family' : cnxFamily || '4' }; var redis_client, net_client; diff --git a/test.js b/test.js index ffbc54d1a83..13c039bdcbd 100644 --- a/test.js +++ b/test.js @@ -115,6 +115,51 @@ next = function next(name) { // Tests are run in the order they are defined, so FLUSHDB should always be first. +tests.IPV4 = function () { + var ipv4Client = redis.createClient( PORT, "127.0.0.1", { "family" : "IPv4" } ); + + ipv4Client.once("ready", function start_tests() { + console.log("Connected to " + ipv4Client.host + ":" + ipv4Client.port + ", Redis server version " + ipv4Client.server_info.redis_version + "\n"); + console.log("Using reply parser " + ipv4Client.reply_parser.name); + + ipv4Client.quit(); + run_next_test(); + }); + + ipv4Client.on('end', function () { + + }); + + // Exit immediately on connection failure, which triggers "exit", below, which fails the test + ipv4Client.on("error", function (err) { + console.error("client: " + err.stack); + process.exit(); + }); +} + +tests.IPV6 = function () { + var ipv6Client = redis.createClient( PORT, "::1", { "family" : "IPv6" } ); + + ipv6Client.once("ready", function start_tests() { + console.log("Connected to " + ipv6Client.host + ":" + ipv6Client.port + ", Redis server version " + ipv6Client.server_info.redis_version + "\n"); + console.log("Using reply parser " + ipv6Client.reply_parser.name); + + ipv6Client.quit(); + run_next_test(); + }); + + ipv6Client.on('end', function () { + + }); + + // Exit immediately on connection failure, which triggers "exit", below, which fails the test + ipv6Client.on("error", function (err) { + console.error("client: " + err.stack); + process.exit(); + }); +} + + tests.FLUSHDB = function () { var name = "FLUSHDB"; client.select(test_db_num, require_string("OK", name)); From 8e0dcc0a5599827d631e4cefc5c951843027ff75 Mon Sep 17 00:00:00 2001 From: migounette Date: Fri, 11 Jul 2014 04:49:50 +0200 Subject: [PATCH 0187/1748] Rollback package.json version and changelog.md Adjust the README.md section --- README.md | 3 ++- changelog.md | 7 ------- package.json | 2 +- 3 files changed, 3 insertions(+), 9 deletions(-) diff --git a/README.md b/README.md index b837bb99cbf..d01cfc34d04 100644 --- a/README.md +++ b/README.md @@ -210,7 +210,8 @@ limits total time for client to reconnect. Value is provided in milliseconds and * `max_attempts` defaults to `null`. By default client will try reconnecting until connected. Setting `max_attempts` limits total amount of reconnects. * `auth_pass` defaults to `null`. By default client will try connecting without auth. If set, client will run redis auth command on connect. -* `family` defaults to `IPv4`. By default client will try connecting with a IPv4 DNS resolution when a FQDN host is set. You can also specify and IPv6 for forcing a IPv6 FQDN resolution. +* `family` defaults to `IPv4`. The client connects in IPv4 if not specified or if the DNS resolution returns an IPv4 address. +You can force an IPv6 if you set the family to 'IPv6'. See nodejs net or dns modules how to use the family type. ```js var redis = require("redis"), diff --git a/changelog.md b/changelog.md index 74aa941e098..e0ae48c92a1 100644 --- a/changelog.md +++ b/changelog.md @@ -1,13 +1,6 @@ Changelog ========= -## v0.10.4 - Jun 23, 2014 - -* Add redis-cli createClient(port, host, options) full IPv6 Network capability - Specify the family type IPv4 or IPv6 in the options object. - - eg: { 'family' : 'IPv6' } - ## v0.10.3 - May 22, 2014 * Update command list to match Redis 2.8.9 (Charles Feng) diff --git a/package.json b/package.json index bb9e78962c7..1e408a2b12e 100644 --- a/package.json +++ b/package.json @@ -1,6 +1,6 @@ { "name": "redis", - "version": "0.10.4", + "version": "0.10.3", "description": "Redis client library", "keywords": [ "redis", From 3203830479976520b2b424c8f258df21a6ed94fc Mon Sep 17 00:00:00 2001 From: Bryce Baril Date: Thu, 10 Jul 2014 20:52:48 -0700 Subject: [PATCH 0188/1748] Add connection_breaker test tool for #622 --- connection_breaker.js | 80 +++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 80 insertions(+) create mode 100644 connection_breaker.js diff --git a/connection_breaker.js b/connection_breaker.js new file mode 100644 index 00000000000..489f5d56450 --- /dev/null +++ b/connection_breaker.js @@ -0,0 +1,80 @@ +var net = require('net'); + +var proxyPort = 6379; +var counter = 0; + +function breaker(conn) { + conn.end(); + conn.destroy(); +} + +var server = net.createServer(function(conn) { + counter++; + var proxyConn = net.createConnection({ + port: proxyPort + }); + conn.pipe(proxyConn); + proxyConn.pipe(conn); + proxyConn.on('end', function() { + conn.end(); + }); + conn.on('end', function() { + proxyConn.end(); + }); + conn.on('close', function() { + proxyConn.end(); + }); + proxyConn.on('close', function() { + conn.end(); + }); + proxyConn.on('error', function() { + conn.end(); + }); + conn.on('error', function() { + proxyConn.end(); + }); + + setTimeout(breaker.bind(null, conn), Math.floor(Math.random() * 2000)); +}); +server.listen(6479); + +var redis = require('./'); + +var port = 6479; + +var client = redis.createClient(6479, 'localhost'); + +function iter() { + var k = "k" + Math.floor(Math.random() * 10); + var coinflip = Math.random() > 0.5; + if (coinflip) { + client.set(k, k, function(err, resp) { + if (!err && resp !== "OK") { + console.log("Unexpected set response " + resp); + } + }); + } else { + client.get(k, function(err, resp) { + if (!err) { + if (k !== resp) { + console.log("Key response mismatch: " + k + " " + resp); + } + } + }); + } +} + +function iters() { + for (var i = 0; i < 100; ++i) { + iter(); + } + setTimeout(iters, 10); +} + +client.on("connect", function () { + iters(); +}); + +client.on("error", function (err) { + console.log("Client error " + err); +}); From 3e5ad05cda80bcaa6ebea4f193fde36158f76959 Mon Sep 17 00:00:00 2001 From: Bryce Baril Date: Thu, 10 Jul 2014 21:09:14 -0700 Subject: [PATCH 0189/1748] Update changelog --- changelog.md | 2 ++ 1 file changed, 2 insertions(+) diff --git a/changelog.md b/changelog.md index e0ae48c92a1..6b41dad5dc7 100644 --- a/changelog.md +++ b/changelog.md @@ -1,6 +1,8 @@ Changelog ========= +* Fix reconnection socket logic to prevent misqueued entries. (Iain Proctor) + ## v0.10.3 - May 22, 2014 * Update command list to match Redis 2.8.9 (Charles Feng) From 8e9c5d482233aa9cd8ac3362688e2e12c33ff768 Mon Sep 17 00:00:00 2001 From: Bryce Baril Date: Thu, 10 Jul 2014 21:09:24 -0700 Subject: [PATCH 0190/1748] add npmignore --- .npmignore | 8 ++++++++ 1 file changed, 8 insertions(+) create mode 100644 .npmignore diff --git a/.npmignore b/.npmignore new file mode 100644 index 00000000000..61755a69fc4 --- /dev/null +++ b/.npmignore @@ -0,0 +1,8 @@ +examples/ +benches/ +test.js +diff_multi_bench_output.js +generate_commands.js +multi_bench.js +test-unref.js +changelog.md From 2e099a06cfc5143a1a1d414a3f9295e7d7ec9178 Mon Sep 17 00:00:00 2001 From: Bryce Baril Date: Thu, 10 Jul 2014 21:12:05 -0700 Subject: [PATCH 0191/1748] update changelog --- changelog.md | 1 + 1 file changed, 1 insertion(+) diff --git a/changelog.md b/changelog.md index 6b41dad5dc7..6b468feebb8 100644 --- a/changelog.md +++ b/changelog.md @@ -1,6 +1,7 @@ Changelog ========= +* Domains protection from bad user exit. (Jake Verbaten) * Fix reconnection socket logic to prevent misqueued entries. (Iain Proctor) ## v0.10.3 - May 22, 2014 From f7134a6f2cfb528136f7c3250fcdcd4f1bb20dd6 Mon Sep 17 00:00:00 2001 From: Bryce Baril Date: Thu, 10 Jul 2014 21:15:47 -0700 Subject: [PATCH 0192/1748] update changelog --- changelog.md | 1 + 1 file changed, 1 insertion(+) diff --git a/changelog.md b/changelog.md index 6b468feebb8..d381ec741e2 100644 --- a/changelog.md +++ b/changelog.md @@ -1,6 +1,7 @@ Changelog ========= +* Correctly reset retry timer. (ouotuo) * Domains protection from bad user exit. (Jake Verbaten) * Fix reconnection socket logic to prevent misqueued entries. (Iain Proctor) From 8b5d55fecd70d8985f8de237718f0eb70680f0b2 Mon Sep 17 00:00:00 2001 From: Bryce Baril Date: Thu, 10 Jul 2014 21:18:47 -0700 Subject: [PATCH 0193/1748] Set socket_keepalive to default to `true` --- README.md | 2 +- index.js | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/README.md b/README.md index 5e9a94eae04..2d5d1238a08 100644 --- a/README.md +++ b/README.md @@ -192,7 +192,7 @@ every command on a client. * `socket_nodelay`: defaults to `true`. Whether to call setNoDelay() on the TCP stream, which disables the Nagle algorithm on the underlying socket. Setting this option to `false` can result in additional throughput at the cost of more latency. Most applications will want this set to `true`. -* `socket_keepalive` defaults to `false`. Whether the keep-alive functionality is enabled on the underlying socket. +* `socket_keepalive` defaults to `true`. Whether the keep-alive functionality is enabled on the underlying socket. * `no_ready_check`: defaults to `false`. When a connection is established to the Redis server, the server might still be loading the database from disk. While loading, the server not respond to any commands. To work around this, `node_redis` has a "ready check" which sends the `INFO` command to the server. The response from the `INFO` command diff --git a/index.js b/index.js index f7209c106d4..38a6084e26e 100644 --- a/index.js +++ b/index.js @@ -44,7 +44,7 @@ function RedisClient(stream, options) { this.options.socket_nodelay = true; } if (this.options.socket_keepalive === undefined) { - this.options.socket_keepalive = false; + this.options.socket_keepalive = true; } this.should_buffer = false; this.command_queue_high_water = this.options.command_queue_high_water || 1000; From a8403a011005108971c5bde9c0bf68cedd64e276 Mon Sep 17 00:00:00 2001 From: Bryce Baril Date: Thu, 10 Jul 2014 21:23:44 -0700 Subject: [PATCH 0194/1748] update changelog --- changelog.md | 1 + 1 file changed, 1 insertion(+) diff --git a/changelog.md b/changelog.md index d381ec741e2..599363cd368 100644 --- a/changelog.md +++ b/changelog.md @@ -1,6 +1,7 @@ Changelog ========= +* Set socket_keepalive to prevent long-lived client timeouts. (mohit) * Correctly reset retry timer. (ouotuo) * Domains protection from bad user exit. (Jake Verbaten) * Fix reconnection socket logic to prevent misqueued entries. (Iain Proctor) From 08a8eed111b0214b47bc8c3ca9ef7ca77e830b89 Mon Sep 17 00:00:00 2001 From: Bryce Baril Date: Thu, 10 Jul 2014 21:28:48 -0700 Subject: [PATCH 0195/1748] Revert "Client to emit errors now instead of throwing them asynchronously where they're uncatchable." This reverts commit 6a3ccf64f4b49804e0f3514ce3aab4baafd67b00. --- index.js | 28 +++++++++++++++++------- test.js | 66 -------------------------------------------------------- 2 files changed, 20 insertions(+), 74 deletions(-) diff --git a/index.js b/index.js index 38a6084e26e..67cbb5ff37a 100644 --- a/index.js +++ b/index.js @@ -154,7 +154,9 @@ RedisClient.prototype.flush_and_error = function (message) { try { command_obj.callback(error); } catch (callback_err) { - this.emit("error", callback_err); + process.nextTick(function () { + throw callback_err; + }); } } } @@ -166,7 +168,9 @@ RedisClient.prototype.flush_and_error = function (message) { try { command_obj.callback(error); } catch (callback_err) { - this.emit("error", callback_err); + process.nextTick(function () { + throw callback_err; + }); } } } @@ -566,18 +570,24 @@ RedisClient.prototype.return_error = function (err) { try { command_obj.callback(err); } catch (callback_err) { - this.emit("error", callback_err); + // if a callback throws an exception, re-throw it on a new stack so the parser can keep going + process.nextTick(function () { + throw callback_err; + }); } } else { console.log("node_redis: no callback to send error: " + err.message); - this.emit("error", err); + // this will probably not make it anywhere useful, but we might as well throw + process.nextTick(function () { + throw err; + }); } }; // if a callback throws an exception, re-throw it on a new stack so the parser can keep going. // if a domain is active, emit the error on the domain, which will serve the same function. // put this try/catch in its own function because V8 doesn't optimize this well yet. -function try_callback(client, callback, reply) { +function try_callback(callback, reply) { try { callback(null, reply); } catch (err) { @@ -588,7 +598,9 @@ function try_callback(client, callback, reply) { currDomain.exit(); } } else { - client.emit("error", err); + process.nextTick(function () { + throw err; + }); } } } @@ -670,7 +682,7 @@ RedisClient.prototype.return_reply = function (reply) { reply = reply_to_object(reply); } - try_callback(this, command_obj.callback, reply); + try_callback(command_obj.callback, reply); } else if (exports.debug_mode) { console.log("no callback for reply: " + (reply && reply.toString && reply.toString())); } @@ -696,7 +708,7 @@ RedisClient.prototype.return_reply = function (reply) { // reply[1] can be null var reply1String = (reply[1] === null) ? null : reply[1].toString(); if (command_obj && typeof command_obj.callback === "function") { - try_callback(this, command_obj.callback, reply1String); + try_callback(command_obj.callback, reply1String); } this.emit(type, reply1String, reply[2]); // channel, count } else { diff --git a/test.js b/test.js index ffbc54d1a83..96c77429bbf 100644 --- a/test.js +++ b/test.js @@ -402,72 +402,6 @@ tests.FWD_ERRORS_1 = function () { }, 150); }; -tests.FWD_ERRORS_2 = function () { - var name = "FWD_ERRORS_2"; - - var toThrow = new Error("Forced exception"); - var recordedError = null; - - var originalHandler = client.listeners("error").pop(); - client.removeAllListeners("error"); - client.once("error", function (err) { - recordedError = err; - }); - - client.get("no_such_key", function (err, reply) { - throw toThrow; - }); - - setTimeout(function () { - client.listeners("error").push(originalHandler); - assert.equal(recordedError, toThrow, "Should have caught our forced exception"); - next(name); - }, 150); -}; - -tests.FWD_ERRORS_3 = function () { - var name = "FWD_ERRORS_3"; - - var recordedError = null; - - var originalHandler = client.listeners("error").pop(); - client.removeAllListeners("error"); - client.once("error", function (err) { - recordedError = err; - }); - - client.send_command("no_such_command", []); - - setTimeout(function () { - client.listeners("error").push(originalHandler); - assert.ok(recordedError instanceof Error); - next(name); - }, 150); -}; - -tests.FWD_ERRORS_4 = function () { - var name = "FWD_ERRORS_4"; - - var toThrow = new Error("Forced exception"); - var recordedError = null; - - var originalHandler = client.listeners("error").pop(); - client.removeAllListeners("error"); - client.once("error", function (err) { - recordedError = err; - }); - - client.send_command("no_such_command", [], function () { - throw toThrow; - }); - - setTimeout(function () { - client.listeners("error").push(originalHandler); - assert.equal(recordedError, toThrow, "Should have caught our forced exception"); - next(name); - }, 150); -}; - tests.EVAL_1 = function () { var name = "EVAL_1"; From 4148a9b0dfb9f3812bb8f3f974b675730748d93b Mon Sep 17 00:00:00 2001 From: Bryce Baril Date: Thu, 10 Jul 2014 21:59:43 -0700 Subject: [PATCH 0196/1748] repair test --- test.js | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) diff --git a/test.js b/test.js index 96c77429bbf..44e97cde54b 100644 --- a/test.js +++ b/test.js @@ -800,10 +800,13 @@ tests.reconnect_select_db_after_pubsub = function() { }; tests.select_error_emits_if_no_callback = function () { + var prev = client.listeners("error")[0]; + client.removeListener("error", prev); var name = "select_error_emits_if_no_callback"; var handler = with_timeout(name, function (err) { require_error(name)(err); client.removeListener('error', handler); + client.on("error", prev); next(name); }, 500); client.on('error', handler); @@ -888,7 +891,7 @@ tests.HMGET = function () { client.HMSET(key3, { "0123456789": "abcdefghij", "some manner of key": "a type of value" - }, require_string("OK", name)); + }, require_string("OK", name)); client.HMGET(key1, "0123456789", "some manner of key", function (err, reply) { assert.strictEqual("abcdefghij", reply[0].toString(), name); From e00fbb4a452e4550763d3a4058ee60a99b63e88a Mon Sep 17 00:00:00 2001 From: Bryce Baril Date: Thu, 10 Jul 2014 22:03:28 -0700 Subject: [PATCH 0197/1748] update changelog --- changelog.md | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/changelog.md b/changelog.md index 599363cd368..6b5ed3cc3d5 100644 --- a/changelog.md +++ b/changelog.md @@ -1,6 +1,10 @@ Changelog ========= +## v0.11.0 - Jul 10, 2014 + +* IPv6 Support. (Yann Stephan) +* Revert error emitting and go back to throwing errors. (Bryce Baril) * Set socket_keepalive to prevent long-lived client timeouts. (mohit) * Correctly reset retry timer. (ouotuo) * Domains protection from bad user exit. (Jake Verbaten) From d25d12eee7fa14b249d7ce82fee5c47cce5eea75 Mon Sep 17 00:00:00 2001 From: Bryce Baril Date: Thu, 10 Jul 2014 22:03:38 -0700 Subject: [PATCH 0198/1748] 0.11.0 --- package.json | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/package.json b/package.json index 1e408a2b12e..6854145d4e7 100644 --- a/package.json +++ b/package.json @@ -1,6 +1,6 @@ { "name": "redis", - "version": "0.10.3", + "version": "0.11.0", "description": "Redis client library", "keywords": [ "redis", From 064260d1c5f2729f413e4266c3e1e0047cfd19ad Mon Sep 17 00:00:00 2001 From: Jack Tang Date: Thu, 7 Aug 2014 17:19:37 +0800 Subject: [PATCH 0199/1748] improve the interface to better support unix socket. delete client.host delete client.port add client.address add client.connectionOptions add tests.UNIX_SOCKET update all error message to use client.address update retry connection --- index.js | 80 ++++++++++++++++++++++++++++++++++++++++---------------- test.js | 46 ++++++++++++++++++++++++++------ 2 files changed, 96 insertions(+), 30 deletions(-) diff --git a/index.js b/index.js index 9674e7a5282..ed570030097 100644 --- a/index.js +++ b/index.js @@ -178,7 +178,7 @@ RedisClient.prototype.flush_and_error = function (message) { }; RedisClient.prototype.on_error = function (msg) { - var message = "Redis connection to " + this.host + ":" + this.port + " failed - " + msg; + var message = "Redis connection to " + this.address + " failed - " + msg; if (this.closing) { return; @@ -203,7 +203,7 @@ RedisClient.prototype.do_auth = function () { var self = this; if (exports.debug_mode) { - console.log("Sending auth to " + self.host + ":" + self.port + " id " + self.connection_id); + console.log("Sending auth to " + self.address + " id " + self.connection_id); } self.send_anyway = true; self.send_command("auth", [this.auth_pass], function (err, res) { @@ -227,7 +227,7 @@ RedisClient.prototype.do_auth = function () { return self.emit("error", new Error("Auth failed: " + res.toString())); } if (exports.debug_mode) { - console.log("Auth succeeded " + self.host + ":" + self.port + " id " + self.connection_id); + console.log("Auth succeeded " + self.address + " id " + self.connection_id); } if (self.auth_callback) { self.auth_callback(err, res); @@ -249,7 +249,7 @@ RedisClient.prototype.do_auth = function () { RedisClient.prototype.on_connect = function () { if (exports.debug_mode) { - console.log("Stream connected " + this.host + ":" + this.port + " id " + this.connection_id); + console.log("Stream connected " + this.address + " id " + this.connection_id); } this.connected = true; @@ -532,7 +532,7 @@ RedisClient.prototype.connection_gone = function (why) { return; } - self.stream = net.createConnection(self.port, self.host); + self.stream = net.createConnection(self.connectionOption); self.install_stream_listeners(); self.retry_timer = null; }, this.retry_delay); @@ -540,7 +540,7 @@ RedisClient.prototype.connection_gone = function (why) { RedisClient.prototype.on_data = function (data) { if (exports.debug_mode) { - console.log("net read " + this.host + ":" + this.port + " id " + this.connection_id + ": " + data.toString()); + console.log("net read " + this.address + " id " + this.connection_id + ": " + data.toString()); } try { @@ -852,7 +852,7 @@ RedisClient.prototype.send_command = function (command, args, callback) { command_str += "$" + Buffer.byteLength(arg) + "\r\n" + arg + "\r\n"; } if (exports.debug_mode) { - console.log("send " + this.host + ":" + this.port + " id " + this.connection_id + ": " + command_str); + console.log("send " + this.address + " id " + this.connection_id + ": " + command_str); } buffered_writes += !stream.write(command_str); } else { @@ -1213,28 +1213,64 @@ RedisClient.prototype.eval = RedisClient.prototype.EVAL = function () { }; -exports.createClient = function (port_arg, host_arg, options) { +exports.createClient = function(arg0, arg1, arg2){ + if( arguments.length === 0 ){ - var cnxFamily; - - if (options && options.family) { - cnxFamily = (options.family == 'IPv6' ? 6 : 4); - } - + // createClient() + return createClient_tcp(default_port, default_host, {}); + + } else if( typeof arg0 === 'number' || + typeof arg0 === 'string' && arg0.match(/^\d+$/) ){ + + // createClient( 3000, host, options) + // createClient('3000', host, options) + return createClient_tcp(arg0, arg1, arg2); + + } else if( typeof arg0 === 'string' ){ + + // createClient( '/tmp/redis.sock', options) + return createClient_unix(arg0,arg1); + + } else if( arg0 !== null && typeof arg0 === 'object' ){ + + // createClient(options) + return createClient_tcp(default_port, default_host, arg0 ); + + } else if( arg0 === null && arg1 === null ){ + + // for backward compatibility + // createClient(null,null,options) + return createClient_tcp(default_port, default_host, arg2); + + } else { + throw new Error('unknown type of connection in createClient()'); + } +} + +var createClient_unix = function(path, options){ var cnxOptions = { - 'port' : port_arg || default_port, - 'host' : host_arg || default_host, - 'family' : cnxFamily || '4' + path: path }; + var net_client = net.createConnection(cnxOptions); + var redis_client = new RedisClient(net_client, options || {}); - var redis_client, net_client; + redis_client.connectionOption = cnxOptions; + redis_client.address = path; - net_client = net.createConnection(cnxOptions); + return redis_client; +} - redis_client = new RedisClient(net_client, options); +var createClient_tcp = function (port_arg, host_arg, options) { + var cnxOptions = { + 'port' : port_arg || default_port, + 'host' : host_arg || default_host, + 'family' : (options && options.family === 'IPv6') ? 'IPv6' : 'IPv4' + }; + var net_client = net.createConnection(cnxOptions); + var redis_client = new RedisClient(net_client, options || {}); - redis_client.port = cnxOptions.port; - redis_client.host = cnxOptions.host; + redis_client.connectionOption = cnxOptions; + redis_client.address = cnxOptions.host + ':' + cnxOptions.port; return redis_client; }; diff --git a/test.js b/test.js index b9d915534b2..be29abd8f45 100644 --- a/test.js +++ b/test.js @@ -119,7 +119,7 @@ tests.IPV4 = function () { var ipv4Client = redis.createClient( PORT, "127.0.0.1", { "family" : "IPv4" } ); ipv4Client.once("ready", function start_tests() { - console.log("Connected to " + ipv4Client.host + ":" + ipv4Client.port + ", Redis server version " + ipv4Client.server_info.redis_version + "\n"); + console.log("Connected to " + ipv4Client.address + ", Redis server version " + ipv4Client.server_info.redis_version + "\n"); console.log("Using reply parser " + ipv4Client.reply_parser.name); ipv4Client.quit(); @@ -141,7 +141,7 @@ tests.IPV6 = function () { var ipv6Client = redis.createClient( PORT, "::1", { "family" : "IPv6" } ); ipv6Client.once("ready", function start_tests() { - console.log("Connected to " + ipv6Client.host + ":" + ipv6Client.port + ", Redis server version " + ipv6Client.server_info.redis_version + "\n"); + console.log("Connected to " + ipv6Client.address + ", Redis server version " + ipv6Client.server_info.redis_version + "\n"); console.log("Using reply parser " + ipv6Client.reply_parser.name); ipv6Client.quit(); @@ -159,6 +159,31 @@ tests.IPV6 = function () { }); } +tests.UNIX_SOCKET = function () { + var unixClient = redis.createClient('/tmp/redis.sock'); + + // if this fails, check the permission of unix socket. + // unixsocket /tmp/redis.sock + // unixsocketperm 777 + + unixClient.once('ready', function start_tests(){ + console.log("Connected to " + unixClient.address + ", Redis server version " + unixClient.server_info.redis_version + "\n"); + console.log("Using reply parser " + unixClient.reply_parser.name); + + unixClient.quit(); + run_next_test(); + }); + + unixClient.on( 'end', function(){ + + }); + + // Exit immediately on connection failure, which triggers "exit", below, which fails the test + unixClient.on("error", function (err) { + console.error("client: " + err.stack); + process.exit(); + }); +} tests.FLUSHDB = function () { var name = "FLUSHDB"; @@ -610,11 +635,16 @@ tests.CLIENT_LIST = function() { return next(name); } + var pattern = /^add=/; + if ( server_version_at_least(client, [2, 8, 12])) { + pattern = /^id=\d+ addr=/; + } + function checkResult(result) { var lines = result.toString().split('\n').slice(0, -1); assert.strictEqual(lines.length, 4); assert(lines.every(function(line) { - return line.match(/^addr=/); + return line.match(pattern); })); } @@ -683,7 +713,7 @@ tests.WATCH_TRANSACTION = function () { tests.detect_buffers = function () { - var name = "detect_buffers", detect_client = redis.createClient(null, null, {detect_buffers: true}); + var name = "detect_buffers", detect_client = redis.createClient({detect_buffers: true}); detect_client.on("ready", function () { // single Buffer or String @@ -750,9 +780,9 @@ tests.detect_buffers = function () { tests.socket_nodelay = function () { var name = "socket_nodelay", c1, c2, c3, ready_count = 0, quit_count = 0; - c1 = redis.createClient(null, null, {socket_nodelay: true}); - c2 = redis.createClient(null, null, {socket_nodelay: false}); - c3 = redis.createClient(null, null); + c1 = redis.createClient({socket_nodelay: true}); + c2 = redis.createClient({socket_nodelay: false}); + c3 = redis.createClient(); function quit_check() { quit_count++; @@ -2194,7 +2224,7 @@ run_next_test = function run_next_test() { }; client.once("ready", function start_tests() { - console.log("Connected to " + client.host + ":" + client.port + ", Redis server version " + client.server_info.redis_version + "\n"); + console.log("Connected to " + client.address + ", Redis server version " + client.server_info.redis_version + "\n"); console.log("Using reply parser " + client.reply_parser.name); run_next_test(); From 2a768f36b633dd69424d79f145647f851a9748d4 Mon Sep 17 00:00:00 2001 From: Jack Tang Date: Thu, 7 Aug 2014 17:35:01 +0800 Subject: [PATCH 0200/1748] updated readme of the createClient() part --- README.md | 29 ++++++++--------------------- 1 file changed, 8 insertions(+), 21 deletions(-) diff --git a/README.md b/README.md index d1ae32e26bf..94a6c67ad8a 100644 --- a/README.md +++ b/README.md @@ -175,10 +175,15 @@ resume sending when you get `drain`. `client` will emit `idle` when there are no outstanding commands that are awaiting a response. -## redis.createClient(port, host, options) +## redis.createClient() -Create a new client connection. `port` defaults to `6379` and `host` defaults -to `127.0.0.1`. If you have `redis-server` running on the same computer as node, then the defaults for +### overloading +* redis.createClient() = redis.createClient(6379, '127.0.0.1', {}) +* redis.createClient(options) = redis.createClient(6379, '127.0.0.1', options) +* redis.createClient(unix_socket, options) +* redis.createClient(port, host, options) + +If you have `redis-server` running on the same computer as node, then the defaults for port and host are probably fine. `options` in an object with the following possible properties: * `parser`: which Redis protocol reply parser to use. Defaults to `hiredis` if that module is installed. @@ -234,24 +239,6 @@ You can force an IPv6 if you set the family to 'IPv6'. See nodejs net or dns mod `createClient()` returns a `RedisClient` object that is named `client` in all of the examples here. -### Unix Domain Socket - -You can also create a connection to Redis server via the unix domain socket if the server -has it enabled: - -```js -var redis = require("redis"); -var client = redis.createClient("/tmp/redis.sock"); -``` - -Sample `redis.conf` configuration to enable unix domain socket listening: - -```conf -unixsocket /tmp/redis.sock -unixsocketperm 755 -``` - -See [issue #204](https://github.com/mranney/node_redis/issues/204) for more information. ## client.auth(password, callback) From 5e1cebee5e78792b35da454fc5408c9900bea565 Mon Sep 17 00:00:00 2001 From: Jack Tang Date: Thu, 7 Aug 2014 17:39:04 +0800 Subject: [PATCH 0201/1748] fix the example in createClient --- README.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/README.md b/README.md index 94a6c67ad8a..830cb57fa20 100644 --- a/README.md +++ b/README.md @@ -221,7 +221,7 @@ You can force an IPv6 if you set the family to 'IPv6'. See nodejs net or dns mod ```js var redis = require("redis"), - client = redis.createClient(null, null, {detect_buffers: true}); + client = redis.createClient({detect_buffers: true}); client.set("foo_rand000000000000", "OK"); From ba234edfef3e80bb74f0f6a09a290363106c8dd9 Mon Sep 17 00:00:00 2001 From: Bryce Baril Date: Sat, 9 Aug 2014 14:50:14 -0700 Subject: [PATCH 0202/1748] Fix tests for older versions of Redis. --- test.js | 82 ++++++++++++++++++++++++++++++--------------------------- 1 file changed, 43 insertions(+), 39 deletions(-) diff --git a/test.js b/test.js index be29abd8f45..b50c05d5bc1 100644 --- a/test.js +++ b/test.js @@ -116,47 +116,51 @@ next = function next(name) { // Tests are run in the order they are defined, so FLUSHDB should always be first. tests.IPV4 = function () { - var ipv4Client = redis.createClient( PORT, "127.0.0.1", { "family" : "IPv4" } ); - - ipv4Client.once("ready", function start_tests() { - console.log("Connected to " + ipv4Client.address + ", Redis server version " + ipv4Client.server_info.redis_version + "\n"); - console.log("Using reply parser " + ipv4Client.reply_parser.name); - - ipv4Client.quit(); - run_next_test(); - }); - - ipv4Client.on('end', function () { - - }); - - // Exit immediately on connection failure, which triggers "exit", below, which fails the test - ipv4Client.on("error", function (err) { - console.error("client: " + err.stack); - process.exit(); - }); + var ipv4Client = redis.createClient( PORT, "127.0.0.1", { "family" : "IPv4" } ); + + ipv4Client.once("ready", function start_tests() { + console.log("Connected to " + ipv4Client.address + ", Redis server version " + ipv4Client.server_info.redis_version + "\n"); + console.log("Using reply parser " + ipv4Client.reply_parser.name); + + ipv4Client.quit(); + run_next_test(); + }); + + ipv4Client.on('end', function () { + + }); + + // Exit immediately on connection failure, which triggers "exit", below, which fails the test + ipv4Client.on("error", function (err) { + console.error("client: " + err.stack); + process.exit(); + }); } tests.IPV6 = function () { - var ipv6Client = redis.createClient( PORT, "::1", { "family" : "IPv6" } ); - - ipv6Client.once("ready", function start_tests() { - console.log("Connected to " + ipv6Client.address + ", Redis server version " + ipv6Client.server_info.redis_version + "\n"); - console.log("Using reply parser " + ipv6Client.reply_parser.name); - - ipv6Client.quit(); - run_next_test(); - }); - - ipv6Client.on('end', function () { - - }); - - // Exit immediately on connection failure, which triggers "exit", below, which fails the test - ipv6Client.on("error", function (err) { - console.error("client: " + err.stack); - process.exit(); - }); + if (!server_version_at_least(client, [2, 8, 0])) { + console.log("Skipping IPV6 for old Redis server version < 2.8.0"); + return run_next_test(); + } + var ipv6Client = redis.createClient( PORT, "::1", { "family" : "IPv6" } ); + + ipv6Client.once("ready", function start_tests() { + console.log("Connected to " + ipv6Client.address + ", Redis server version " + ipv6Client.server_info.redis_version + "\n"); + console.log("Using reply parser " + ipv6Client.reply_parser.name); + + ipv6Client.quit(); + run_next_test(); + }); + + ipv6Client.on('end', function () { + + }); + + // Exit immediately on connection failure, which triggers "exit", below, which fails the test + ipv6Client.on("error", function (err) { + console.error("client: " + err.stack); + process.exit(); + }); } tests.UNIX_SOCKET = function () { @@ -635,7 +639,7 @@ tests.CLIENT_LIST = function() { return next(name); } - var pattern = /^add=/; + var pattern = /^addr=/; if ( server_version_at_least(client, [2, 8, 12])) { pattern = /^id=\d+ addr=/; } From 58959b89aca7893c5e5f0e154417d3c12a5b3a3b Mon Sep 17 00:00:00 2001 From: Bryce Baril Date: Sat, 9 Aug 2014 14:53:03 -0700 Subject: [PATCH 0203/1748] Update changelog --- changelog.md | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/changelog.md b/changelog.md index 6b5ed3cc3d5..f22f6edbdd6 100644 --- a/changelog.md +++ b/changelog.md @@ -1,6 +1,10 @@ Changelog ========= +## v0.12.0 - Aug 9, 2014 +* Fix unix socket support (Jack Tang) +* Improve createClient argument handling (Jack Tang) + ## v0.11.0 - Jul 10, 2014 * IPv6 Support. (Yann Stephan) From 84cfcb40b249a84cbbe1dbb7595e983430683007 Mon Sep 17 00:00:00 2001 From: Bryce Baril Date: Sat, 9 Aug 2014 14:53:08 -0700 Subject: [PATCH 0204/1748] 0.12.0 --- package.json | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/package.json b/package.json index 6854145d4e7..9545a750f21 100644 --- a/package.json +++ b/package.json @@ -1,6 +1,6 @@ { "name": "redis", - "version": "0.11.0", + "version": "0.12.0", "description": "Redis client library", "keywords": [ "redis", From 75b0bc97ea3c9b140866fb8fa9888c1aec471943 Mon Sep 17 00:00:00 2001 From: Bryce Baril Date: Mon, 11 Aug 2014 10:33:33 -0700 Subject: [PATCH 0205/1748] Fix #645 --- index.js | 10 +++++----- 1 file changed, 5 insertions(+), 5 deletions(-) diff --git a/index.js b/index.js index ed570030097..7ec74775c02 100644 --- a/index.js +++ b/index.js @@ -1216,10 +1216,10 @@ RedisClient.prototype.eval = RedisClient.prototype.EVAL = function () { exports.createClient = function(arg0, arg1, arg2){ if( arguments.length === 0 ){ - // createClient() + // createClient() return createClient_tcp(default_port, default_host, {}); - } else if( typeof arg0 === 'number' || + } else if( typeof arg0 === 'number' || typeof arg0 === 'string' && arg0.match(/^\d+$/) ){ // createClient( 3000, host, options) @@ -1238,10 +1238,10 @@ exports.createClient = function(arg0, arg1, arg2){ } else if( arg0 === null && arg1 === null ){ - // for backward compatibility + // for backward compatibility // createClient(null,null,options) return createClient_tcp(default_port, default_host, arg2); - + } else { throw new Error('unknown type of connection in createClient()'); } @@ -1264,7 +1264,7 @@ var createClient_tcp = function (port_arg, host_arg, options) { var cnxOptions = { 'port' : port_arg || default_port, 'host' : host_arg || default_host, - 'family' : (options && options.family === 'IPv6') ? 'IPv6' : 'IPv4' + 'family' : (options && options.family === 'IPv6') ? 6 : 4 }; var net_client = net.createConnection(cnxOptions); var redis_client = new RedisClient(net_client, options || {}); From 38ae7a7f507a71224e24a02d703a959f1b0fc657 Mon Sep 17 00:00:00 2001 From: Bryce Baril Date: Mon, 11 Aug 2014 10:34:26 -0700 Subject: [PATCH 0206/1748] 0.12.1 --- package.json | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/package.json b/package.json index 9545a750f21..dc1c39abd27 100644 --- a/package.json +++ b/package.json @@ -1,6 +1,6 @@ { "name": "redis", - "version": "0.12.0", + "version": "0.12.1", "description": "Redis client library", "keywords": [ "redis", From de6692774fa563f6fbdd8b6d68d627c5f919bc39 Mon Sep 17 00:00:00 2001 From: Bryce Baril Date: Mon, 11 Aug 2014 10:35:40 -0700 Subject: [PATCH 0207/1748] Update changelog for 0.12.1 --- changelog.md | 3 +++ 1 file changed, 3 insertions(+) diff --git a/changelog.md b/changelog.md index f22f6edbdd6..64afb6e95d2 100644 --- a/changelog.md +++ b/changelog.md @@ -1,6 +1,9 @@ Changelog ========= +## v0.12.1 - Aug 10, 2014 +* Fix IPv6/IPv4 family selection in node 0.11+ (Various) + ## v0.12.0 - Aug 9, 2014 * Fix unix socket support (Jack Tang) * Improve createClient argument handling (Jack Tang) From a67d3acdd6b6f2e04ba642b43a75d66825ec668b Mon Sep 17 00:00:00 2001 From: Hiroshi Kuwabara Date: Fri, 12 Sep 2014 12:37:32 +0900 Subject: [PATCH 0208/1748] added URL support to createClient --- index.js | 15 +++++++++++++-- test.js | 19 +++++++++++++++++++ 2 files changed, 32 insertions(+), 2 deletions(-) diff --git a/index.js b/index.js index 7ec74775c02..f2d5ed43cc9 100644 --- a/index.js +++ b/index.js @@ -1,6 +1,7 @@ /*global Buffer require exports console setTimeout */ var net = require("net"), + URL = require("url"), util = require("./lib/util"), Queue = require("./lib/queue"), to_array = require("./lib/to_array"), @@ -1227,9 +1228,19 @@ exports.createClient = function(arg0, arg1, arg2){ return createClient_tcp(arg0, arg1, arg2); } else if( typeof arg0 === 'string' ){ + var parsed = URL.parse(arg0, true, true), + options = (arg1 || {}); - // createClient( '/tmp/redis.sock', options) - return createClient_unix(arg0,arg1); + if (parsed.hostname) { + if (parsed.auth) { + options.auth_pass = parsed.auth.split(':')[1]; + } + // createClient(3000, host, options) + return createClient_tcp((parsed.port || default_port), parsed.hostname, options); + } else { + // createClient( '/tmp/redis.sock', options) + return createClient_unix(arg0,options); + } } else if( arg0 !== null && typeof arg0 === 'object' ){ diff --git a/test.js b/test.js index b50c05d5bc1..734e5e9c190 100644 --- a/test.js +++ b/test.js @@ -2169,6 +2169,25 @@ tests.auth2 = function () { }); }; +// auth password specified by URL string. +tests.auth3 = function () { + var name = "AUTH3", client4, ready_count = 0; + + client4 = redis.createClient('redis://redistogo:664b1b6aaf134e1ec281945a8de702a9@filefish.redistogo.com:9006/'); + + // test auth, then kill the connection so it'll auto-reconnect and auto-re-auth + client4.on("ready", function () { + ready_count++; + if (ready_count === 1) { + client4.stream.destroy(); + } else { + client4.quit(function (err, res) { + next(name); + }); + } + }); +}; + tests.reconnectRetryMaxDelay = function() { var time = new Date().getTime(), name = 'reconnectRetryMaxDelay', From b63863ed35a96a4c74cdea175fd65fee637ed019 Mon Sep 17 00:00:00 2001 From: Jason Kim Date: Tue, 13 Jan 2015 10:46:30 -0800 Subject: [PATCH 0209/1748] Removed unnecessary indentations and added some js syntax highlighting --- README.md | 315 ++++++++++++++++++++++++++++-------------------------- 1 file changed, 165 insertions(+), 150 deletions(-) diff --git a/README.md b/README.md index 830cb57fa20..1e6f10ea477 100644 --- a/README.md +++ b/README.md @@ -24,26 +24,26 @@ happen between node and native code modules after a node upgrade. Simple example, included as `examples/simple.js`: ```js - var redis = require("redis"), - client = redis.createClient(); +var redis = require("redis"), + client = redis.createClient(); - // if you'd like to select database 3, instead of 0 (default), call - // client.select(3, function() { /* ... */ }); +// if you'd like to select database 3, instead of 0 (default), call +// client.select(3, function() { /* ... */ }); - client.on("error", function (err) { - console.log("Error " + err); - }); +client.on("error", function (err) { + console.log("Error " + err); +}); - client.set("string key", "string val", redis.print); - client.hset("hash key", "hashtest 1", "some value", redis.print); - client.hset(["hash key", "hashtest 2", "some other value"], redis.print); - client.hkeys("hash key", function (err, replies) { - console.log(replies.length + " replies:"); - replies.forEach(function (reply, i) { - console.log(" " + i + ": " + reply); - }); - client.quit(); +client.set("string key", "string val", redis.print); +client.hset("hash key", "hashtest 1", "some value", redis.print); +client.hset(["hash key", "hashtest 2", "some other value"], redis.print); +client.hkeys("hash key", function (err, replies) { + console.log(replies.length + " replies:"); + replies.forEach(function (reply, i) { + console.log(" " + i + ": " + reply); }); + client.quit(); +}); ``` This will display: @@ -93,23 +93,31 @@ All functions take either an `args` Array plus optional `callback` Function or a variable number of individual arguments followed by an optional callback. Here is an example of passing an array of arguments and a callback: - client.mset(["test keys 1", "test val 1", "test keys 2", "test val 2"], function (err, res) {}); +```js +client.mset(["test keys 1", "test val 1", "test keys 2", "test val 2"], function (err, res) {}); +``` Here is that same call in the second style: - client.mset("test keys 1", "test val 1", "test keys 2", "test val 2", function (err, res) {}); +```js +client.mset("test keys 1", "test val 1", "test keys 2", "test val 2", function (err, res) {}); +``` Note that in either form the `callback` is optional: - client.set("some key", "some val"); - client.set(["some other key", "some val"]); +```js +client.set("some key", "some val"); +client.set(["some other key", "some val"]); +``` If the key is missing, reply will be null (probably): - client.get("missingkey", function(err, reply) { - // reply is null when the key is missing - console.log(reply); - }); +```js +client.get("missingkey", function(err, reply) { + // reply is null when the key is missing + console.log(reply); +}); +``` For a list of Redis commands, see [Redis Command Reference](http://redis.io/commands) @@ -197,7 +205,7 @@ every command on a client. * `socket_nodelay`: defaults to `true`. Whether to call setNoDelay() on the TCP stream, which disables the Nagle algorithm on the underlying socket. Setting this option to `false` can result in additional throughput at the cost of more latency. Most applications will want this set to `true`. -* `socket_keepalive` defaults to `true`. Whether the keep-alive functionality is enabled on the underlying socket. +* `socket_keepalive` defaults to `true`. Whether the keep-alive functionality is enabled on the underlying socket. * `no_ready_check`: defaults to `false`. When a connection is established to the Redis server, the server might still be loading the database from disk. While loading, the server not respond to any commands. To work around this, `node_redis` has a "ready check" which sends the `INFO` command to the server. The response from the `INFO` command @@ -216,25 +224,25 @@ limits total time for client to reconnect. Value is provided in milliseconds and * `max_attempts` defaults to `null`. By default client will try reconnecting until connected. Setting `max_attempts` limits total amount of reconnects. * `auth_pass` defaults to `null`. By default client will try connecting without auth. If set, client will run redis auth command on connect. -* `family` defaults to `IPv4`. The client connects in IPv4 if not specified or if the DNS resolution returns an IPv4 address. -You can force an IPv6 if you set the family to 'IPv6'. See nodejs net or dns modules how to use the family type. +* `family` defaults to `IPv4`. The client connects in IPv4 if not specified or if the DNS resolution returns an IPv4 address. +You can force an IPv6 if you set the family to 'IPv6'. See nodejs net or dns modules how to use the family type. ```js - var redis = require("redis"), - client = redis.createClient({detect_buffers: true}); +var redis = require("redis"), + client = redis.createClient({detect_buffers: true}); - client.set("foo_rand000000000000", "OK"); +client.set("foo_rand000000000000", "OK"); - // This will return a JavaScript String - client.get("foo_rand000000000000", function (err, reply) { - console.log(reply.toString()); // Will print `OK` - }); +// This will return a JavaScript String +client.get("foo_rand000000000000", function (err, reply) { + console.log(reply.toString()); // Will print `OK` +}); - // This will return a Buffer since original key is specified as a Buffer - client.get(new Buffer("foo_rand000000000000"), function (err, reply) { - console.log(reply.toString()); // Will print `` - }); - client.end(); +// This will return a Buffer since original key is specified as a Buffer +client.get(new Buffer("foo_rand000000000000"), function (err, reply) { + console.log(reply.toString()); // Will print `` +}); +client.end(); ``` `createClient()` returns a `RedisClient` object that is named `client` in all of the examples here. @@ -260,14 +268,14 @@ This example closes the connection to the Redis server before the replies have b want to do this: ```js - var redis = require("redis"), - client = redis.createClient(); +var redis = require("redis"), + client = redis.createClient(); - client.set("foo_rand000000000000", "some fantastic value"); - client.get("foo_rand000000000000", function (err, reply) { - console.log(reply.toString()); - }); - client.end(); +client.set("foo_rand000000000000", "some fantastic value"); +client.get("foo_rand000000000000", function (err, reply) { + console.log(reply.toString()); +}); +client.end(); ``` `client.end()` is useful for timeout cases where something is stuck or taking too long and you want @@ -305,23 +313,29 @@ with the responses using JavaScript syntax. Example: - client.hmset("hosts", "mjr", "1", "another", "23", "home", "1234"); - client.hgetall("hosts", function (err, obj) { - console.dir(obj); - }); +```js +client.hmset("hosts", "mjr", "1", "another", "23", "home", "1234"); +client.hgetall("hosts", function (err, obj) { + console.dir(obj); +}); +``` Output: - { mjr: '1', another: '23', home: '1234' } +```js +{ mjr: '1', another: '23', home: '1234' } +``` ### client.hmset(hash, obj, [callback]) Multiple values in a hash can be set by supplying an object: - client.HMSET(key2, { - "0123456789": "abcdefghij", // NOTE: key and value will be coerced to strings - "some manner of key": "a type of value" - }); +```js +client.HMSET(key2, { + "0123456789": "abcdefghij", // NOTE: key and value will be coerced to strings + "some manner of key": "a type of value" +}); +``` The properties and values of this Object will be set as keys and values in the Redis hash. @@ -329,8 +343,9 @@ The properties and values of this Object will be set as keys and values in the R Multiple values may also be set by supplying a list: - client.HMSET(key1, "0123456789", "abcdefghij", "some manner of key", "a type of value"); - +```js +client.HMSET(key1, "0123456789", "abcdefghij", "some manner of key", "a type of value"); +``` ## Publish / Subscribe @@ -339,28 +354,28 @@ client connections, subscribes to a channel on one of them, and publishes to tha channel on the other: ```js - var redis = require("redis"), - client1 = redis.createClient(), client2 = redis.createClient(), - msg_count = 0; - - client1.on("subscribe", function (channel, count) { - client2.publish("a nice channel", "I am sending a message."); - client2.publish("a nice channel", "I am sending a second message."); - client2.publish("a nice channel", "I am sending my last message."); - }); +var redis = require("redis"), + client1 = redis.createClient(), client2 = redis.createClient(), + msg_count = 0; + +client1.on("subscribe", function (channel, count) { + client2.publish("a nice channel", "I am sending a message."); + client2.publish("a nice channel", "I am sending a second message."); + client2.publish("a nice channel", "I am sending my last message."); +}); - client1.on("message", function (channel, message) { - console.log("client1 channel " + channel + ": " + message); - msg_count += 1; - if (msg_count === 3) { - client1.unsubscribe(); - client1.end(); - client2.end(); - } - }); +client1.on("message", function (channel, message) { + console.log("client1 channel " + channel + ": " + message); + msg_count += 1; + if (msg_count === 3) { + client1.unsubscribe(); + client1.end(); + client2.end(); + } +}); - client1.incr("did a thing"); - client1.subscribe("a nice channel"); +client1.incr("did a thing"); +client1.subscribe("a nice channel"); ``` When a client issues a `SUBSCRIBE` or `PSUBSCRIBE`, that connection is put into a "subscriber" mode. @@ -412,33 +427,33 @@ channel name as `channel` and the new count of subscriptions for this client as Redis. The interface in `node_redis` is to return an individual `Multi` object by calling `client.multi()`. ```js - var redis = require("./index"), - client = redis.createClient(), set_size = 20; - - client.sadd("bigset", "a member"); - client.sadd("bigset", "another member"); - - while (set_size > 0) { - client.sadd("bigset", "member " + set_size); - set_size -= 1; - } - - // multi chain with an individual callback - client.multi() - .scard("bigset") - .smembers("bigset") - .keys("*", function (err, replies) { - // NOTE: code in this callback is NOT atomic - // this only happens after the the .exec call finishes. - client.mget(replies, redis.print); - }) - .dbsize() - .exec(function (err, replies) { - console.log("MULTI got " + replies.length + " replies"); - replies.forEach(function (reply, index) { - console.log("Reply " + index + ": " + reply.toString()); - }); +var redis = require("./index"), + client = redis.createClient(), set_size = 20; + +client.sadd("bigset", "a member"); +client.sadd("bigset", "another member"); + +while (set_size > 0) { + client.sadd("bigset", "member " + set_size); + set_size -= 1; +} + +// multi chain with an individual callback +client.multi() + .scard("bigset") + .smembers("bigset") + .keys("*", function (err, replies) { + // NOTE: code in this callback is NOT atomic + // this only happens after the the .exec call finishes. + client.mget(replies, redis.print); + }) + .dbsize() + .exec(function (err, replies) { + console.log("MULTI got " + replies.length + " replies"); + replies.forEach(function (reply, index) { + console.log("Reply " + index + ": " + reply.toString()); }); + }); ``` ### Multi.exec( callback ) @@ -456,43 +471,43 @@ You can either chain together `MULTI` commands as in the above example, or you c commands while still sending regular client command as in this example: ```js - var redis = require("redis"), - client = redis.createClient(), multi; +var redis = require("redis"), + client = redis.createClient(), multi; - // start a separate multi command queue - multi = client.multi(); - multi.incr("incr thing", redis.print); - multi.incr("incr other thing", redis.print); +// start a separate multi command queue +multi = client.multi(); +multi.incr("incr thing", redis.print); +multi.incr("incr other thing", redis.print); - // runs immediately - client.mset("incr thing", 100, "incr other thing", 1, redis.print); +// runs immediately +client.mset("incr thing", 100, "incr other thing", 1, redis.print); - // drains multi queue and runs atomically - multi.exec(function (err, replies) { - console.log(replies); // 101, 2 - }); +// drains multi queue and runs atomically +multi.exec(function (err, replies) { + console.log(replies); // 101, 2 +}); - // you can re-run the same transaction if you like - multi.exec(function (err, replies) { - console.log(replies); // 102, 3 - client.quit(); - }); +// you can re-run the same transaction if you like +multi.exec(function (err, replies) { + console.log(replies); // 102, 3 + client.quit(); +}); ``` In addition to adding commands to the `MULTI` queue individually, you can also pass an array of commands and arguments to the constructor: ```js - var redis = require("redis"), - client = redis.createClient(), multi; - - client.multi([ - ["mget", "multifoo", "multibar", redis.print], - ["incr", "multifoo"], - ["incr", "multibar"] - ]).exec(function (err, replies) { - console.log(replies); - }); +var redis = require("redis"), + client = redis.createClient(), multi; + +client.multi([ + ["mget", "multifoo", "multibar", redis.print], + ["incr", "multifoo"], + ["incr", "multibar"] +]).exec(function (err, replies) { + console.log(replies); +}); ``` @@ -508,16 +523,16 @@ will emit a `monitor` event for every new monitor message that comes across. Th Here is a simple example: ```js - var client = require("redis").createClient(), - util = require("util"); +var client = require("redis").createClient(), + util = require("util"); - client.monitor(function (err, res) { - console.log("Entering monitoring mode."); - }); +client.monitor(function (err, res) { + console.log("Entering monitoring mode."); +}); - client.on("monitor", function (time, args) { - console.log(time + ": " + util.inspect(args)); - }); +client.on("monitor", function (time, args) { + console.log(time + ": " + util.inspect(args)); +}); ``` # Extras @@ -541,13 +556,13 @@ The `versions` key contains an array of the elements of the version string for e A handy callback function for displaying return values when testing. Example: ```js - var redis = require("redis"), - client = redis.createClient(); +var redis = require("redis"), + client = redis.createClient(); - client.on("connect", function () { - client.set("foo_rand000000000000", "some fantastic value", redis.print); - client.get("foo_rand000000000000", redis.print); - }); +client.on("connect", function () { + client.set("foo_rand000000000000", "some fantastic value", redis.print); + client.get("foo_rand000000000000", redis.print); +}); ``` This will print: @@ -562,14 +577,14 @@ Note that this program will not exit cleanly because the client is still connect Boolean to enable debug mode and protocol tracing. ```js - var redis = require("redis"), - client = redis.createClient(); +var redis = require("redis"), + client = redis.createClient(); - redis.debug_mode = true; +redis.debug_mode = true; - client.on("connect", function () { - client.set("foo_rand000000000000", "some fantastic value"); - }); +client.on("connect", function () { + client.set("foo_rand000000000000", "some fantastic value"); +}); ``` This will display: From 67be3db6cefbb507222b4aafcc25c7786af5fe24 Mon Sep 17 00:00:00 2001 From: Dave Mun Date: Tue, 24 Feb 2015 09:00:55 -0800 Subject: [PATCH 0210/1748] Update README.md Fixed missing word. --- README.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/README.md b/README.md index 830cb57fa20..be0daaca527 100644 --- a/README.md +++ b/README.md @@ -127,7 +127,7 @@ JavaScript Array of node Buffers. `HGETALL` returns an Object with Buffers keye ### "ready" -`client` will emit `ready` a connection is established to the Redis server and the server reports +`client` will emit `ready` once a connection is established to the Redis server and the server reports that it is ready to receive commands. Commands issued before the `ready` event are queued, then replayed just before this event is emitted. From e0860a28ed636859394e10bbad29101281587a28 Mon Sep 17 00:00:00 2001 From: Mikael Kohlmyr Date: Sat, 28 Feb 2015 21:54:20 +0000 Subject: [PATCH 0211/1748] Removed potential side effects of FWD_ERRORS_1 (e.g. printing of indocming on pub/sub re-use of client3) --- test.js | 15 +++++++++++++-- 1 file changed, 13 insertions(+), 2 deletions(-) diff --git a/test.js b/test.js index b50c05d5bc1..b6df1b51719 100644 --- a/test.js +++ b/test.js @@ -453,8 +453,12 @@ tests.FWD_ERRORS_1 = function () { var toThrow = new Error("Forced exception"); var recordedError = null; - var originalHandlers = client3.listeners("error"); + var originalHandlers = { + "error": client3.listeners("error"), + "message": client3.listeners("message") + }; client3.removeAllListeners("error"); + client3.removeAllListeners("message"); client3.once("error", function (err) { recordedError = err; }); @@ -470,8 +474,15 @@ tests.FWD_ERRORS_1 = function () { client.publish(name, "Some message"); setTimeout(function () { - client3.listeners("error").push(originalHandlers); assert.equal(recordedError, toThrow, "Should have caught our forced exception"); + client3.unsubscribe(name); + client3.removeAllListeners("message"); + originalHandlers.error.forEach(function (fn) { + client3.on("error", fn); + }); + originalHandlers.message.forEach(function (fn) { + client3.on("message", fn); + }); next(name); }, 150); }; From a2ebe4f24894df375f05e004b481cf135c4aef22 Mon Sep 17 00:00:00 2001 From: Bryce Baril Date: Sun, 1 Mar 2015 00:05:33 -0800 Subject: [PATCH 0212/1748] Update changelog --- changelog.md | 2 ++ 1 file changed, 2 insertions(+) diff --git a/changelog.md b/changelog.md index 64afb6e95d2..817031da7a4 100644 --- a/changelog.md +++ b/changelog.md @@ -1,6 +1,8 @@ Changelog ========= +* Fix extraneous error output due to pubsub tests (Mikael Kohlmyr) + ## v0.12.1 - Aug 10, 2014 * Fix IPv6/IPv4 family selection in node 0.11+ (Various) From 9c0462f17761c58d49f2ca95382ab21f99a489f6 Mon Sep 17 00:00:00 2001 From: Almog Melamed Date: Sat, 11 Apr 2015 19:29:34 +0300 Subject: [PATCH 0213/1748] Update changelog.md typo handlign -> handling --- changelog.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/changelog.md b/changelog.md index 817031da7a4..7cdd7fd2636 100644 --- a/changelog.md +++ b/changelog.md @@ -25,7 +25,7 @@ Changelog ## v0.10.2 - May 18, 2014 -* Better binay key handlign for HGETALL. (Nick Apperson) +* Better binay key handling for HGETALL. (Nick Apperson) * Fix test not resetting `error` handler. (CrypticSwarm) * Fix SELECT error semantics. (Bryan English) From 13162e554bc7174178a59499f6dc48ac42667c0f Mon Sep 17 00:00:00 2001 From: Kyle Mitchell Date: Tue, 5 May 2015 00:03:36 +0000 Subject: [PATCH 0214/1748] use a valid SPDX license identifier --- package.json | 1 + 1 file changed, 1 insertion(+) diff --git a/package.json b/package.json index dc1c39abd27..8aba1e91e3b 100644 --- a/package.json +++ b/package.json @@ -7,6 +7,7 @@ "database" ], "author": "Matt Ranney ", + "license": "MIT", "main": "./index.js", "scripts": { "test": "node ./test.js" From 3b6ab9e820a8a237873412c6886dbe2618537df0 Mon Sep 17 00:00:00 2001 From: Jonas Dohse Date: Sun, 17 May 2015 18:57:42 +0000 Subject: [PATCH 0215/1748] Allow garbage collection of processed queue items Set processed queue items to `null` to allow garbage collection of these items. The command queue contains callbacks. They contain the stack of the current fiber, which can be large. Allow earlier garbage collection of these stacks by removing the reference to the queue items. --- lib/queue.js | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) diff --git a/lib/queue.js b/lib/queue.js index 3fc87ab1028..ece2e6bc78d 100644 --- a/lib/queue.js +++ b/lib/queue.js @@ -18,7 +18,10 @@ Queue.prototype.shift = function () { return; } } - return this.head[this.offset++]; // sorry, JSLint + var item = this.head[this.offset]; + this.head[this.offset] = null; + this.offset++; + return item; }; Queue.prototype.push = function (item) { From 06b294c630d9695eba250aba1c57d2e51ab81e54 Mon Sep 17 00:00:00 2001 From: Benjamin Coe Date: Mon, 25 May 2015 16:25:47 -0700 Subject: [PATCH 0216/1748] add coverage reporting --- .gitignore | 2 ++ package.json | 6 ++++-- 2 files changed, 6 insertions(+), 2 deletions(-) diff --git a/.gitignore b/.gitignore index 6a59b3d64c3..f351fac36f6 100644 --- a/.gitignore +++ b/.gitignore @@ -1,2 +1,4 @@ node_modules .tern-port +.nyc_output +coverage diff --git a/package.json b/package.json index dc1c39abd27..b02f7d17363 100644 --- a/package.json +++ b/package.json @@ -9,11 +9,13 @@ "author": "Matt Ranney ", "main": "./index.js", "scripts": { - "test": "node ./test.js" + "test": "node ./test.js", + "coverage": "nyc npm test && nyc report" }, "devDependencies": { - "metrics": ">=0.1.5", "colors": "~0.6.0-1", + "metrics": ">=0.1.5", + "nyc": "^2.2.0", "underscore": "~1.4.4" }, "repository": { From 11735099d60a073dff1c3511f2cdbc5814a019f8 Mon Sep 17 00:00:00 2001 From: vitaliylag Date: Thu, 28 May 2015 14:50:46 +0300 Subject: [PATCH 0217/1748] Update index.js MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit It was almost bug: imagine args is array but callback is not defined - in this case all args (that is consists of one array) are packing to new array. That mean we get this: this.send_command(command, [[...]]). It doesn't make any sense. After I fix it we get this: this.send_command(command, [...], undefined). It's really ok because if we call for example client.hget("test", "aaa") we actually do the same: this.send_command("hget", ["test", "aaa"], undefined). No different from this.send_command(command, [...], undefined). By the way, «this.send_command(command, [[...]])» could be a bug. Try to call client.eval(["return 1", 0]) and you should get throw because "eval" required 2 param, but program thinks it's only one param: ["return 1", 0] (at the beginning it was [["return 1", 0]]). There's only one reason why you don't get throw - RedisClient.prototype.eval is overridden for some optimizations, lucky. But that doesn't mean everything is ok. --- index.js | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/index.js b/index.js index 7ec74775c02..c5b85b410d6 100644 --- a/index.js +++ b/index.js @@ -985,7 +985,7 @@ commands.forEach(function (fullCommand) { var command = fullCommand.split(' ')[0]; RedisClient.prototype[command] = function (args, callback) { - if (Array.isArray(args) && typeof callback === "function") { + if (Array.isArray(args)) { return this.send_command(command, args, callback); } else { return this.send_command(command, to_array(arguments)); From 6af443639e1f8be3c21ce4ca5f1e32d34de80d7c Mon Sep 17 00:00:00 2001 From: David Vas Date: Thu, 11 Jun 2015 16:19:47 +0200 Subject: [PATCH 0218/1748] A little optimization in a function if the client is closing. --- index.js | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/index.js b/index.js index 7ec74775c02..44322e692bf 100644 --- a/index.js +++ b/index.js @@ -178,12 +178,12 @@ RedisClient.prototype.flush_and_error = function (message) { }; RedisClient.prototype.on_error = function (msg) { - var message = "Redis connection to " + this.address + " failed - " + msg; - if (this.closing) { return; } + var message = "Redis connection to " + this.address + " failed - " + msg; + if (exports.debug_mode) { console.warn(message); } From eb03bb943c9d0fed40d0c35965ac0c9624176908 Mon Sep 17 00:00:00 2001 From: Matt Ranney Date: Fri, 10 Jul 2015 17:25:17 -0400 Subject: [PATCH 0219/1748] Update README and multi_bench --- README.md | 150 ++++++++++++++++++++++++++++++++++--------------- multi_bench.js | 98 ++++++++++++++------------------ 2 files changed, 148 insertions(+), 100 deletions(-) diff --git a/README.md b/README.md index be0daaca527..c3ffd1c898c 100644 --- a/README.md +++ b/README.md @@ -1,24 +1,14 @@ redis - a node.js redis client =========================== -This is a complete Redis client for node.js. It supports all Redis commands, including many recently added commands like EVAL from -experimental Redis server branches. - +This is a complete Redis client for node.js. It supports all Redis commands, +including many recently added commands like EVAL from experimental Redis server +branches. Install with: npm install redis -Pieter Noordhuis has provided a binding to the official `hiredis` C library, which is non-blocking and fast. To use `hiredis`, do: - - npm install hiredis redis - -If `hiredis` is installed, `node_redis` will use it by default. Otherwise, a pure JavaScript parser will be used. - -If you use `hiredis`, be sure to rebuild it whenever you upgrade your version of node. There are mysterious failures that can -happen between node and native code modules after a node upgrade. - - ## Usage Simple example, included as `examples/simple.js`: @@ -57,34 +47,9 @@ This will display: 1: hashtest 2 mjr:~/work/node_redis (master)$ - -## Performance - -Here are typical results of `multi_bench.js` which is similar to `redis-benchmark` from the Redis distribution. -It uses 50 concurrent connections with no pipelining. - -JavaScript parser: - - PING: 20000 ops 42283.30 ops/sec 0/5/1.182 - SET: 20000 ops 32948.93 ops/sec 1/7/1.515 - GET: 20000 ops 28694.40 ops/sec 0/9/1.740 - INCR: 20000 ops 39370.08 ops/sec 0/8/1.269 - LPUSH: 20000 ops 36429.87 ops/sec 0/8/1.370 - LRANGE (10 elements): 20000 ops 9891.20 ops/sec 1/9/5.048 - LRANGE (100 elements): 20000 ops 1384.56 ops/sec 10/91/36.072 - -hiredis parser: - - PING: 20000 ops 46189.38 ops/sec 1/4/1.082 - SET: 20000 ops 41237.11 ops/sec 0/6/1.210 - GET: 20000 ops 39682.54 ops/sec 1/7/1.257 - INCR: 20000 ops 40080.16 ops/sec 0/8/1.242 - LPUSH: 20000 ops 41152.26 ops/sec 0/3/1.212 - LRANGE (10 elements): 20000 ops 36563.07 ops/sec 1/8/1.363 - LRANGE (100 elements): 20000 ops 21834.06 ops/sec 0/9/2.287 - -The performance of `node_redis` improves dramatically with pipelining, which happens automatically in most normal programs. - +Note that the API is entire asynchronous. To get data back from the server, +you'll need to use a callback. The return value from most of the API is a +backpressure indicator. ### Sending Commands @@ -658,6 +623,105 @@ client.zadd(args, function (err, response) { }); ``` +## Performance + +Much effort has been spent to make `node_redis` as fast as possible for common +operations. As pipelining happens naturally from shared connections, overall +efficiency goes up. + +Here are typical results of `multi_bench.js` which is similar to +`redis-benchmark` from the Redis distribution. It uses 5 concurrent connections +and shows the difference between pipelines of 1 and 50. + +JavaScript parser: + + Client count: 5, node version: 0.10.32, server version: 2.8.18, parser: javascript + PING, 1/5 min/max/avg/p95: 0/ 3/ 0.05/ 1.00 1103ms total, 18132.37 ops/sec + PING, 50/5 min/max/avg/p95: 0/ 4/ 0.81/ 2.00 327ms total, 61162.08 ops/sec + SET 4B str, 1/5 min/max/avg/p95: 0/ 2/ 0.05/ 0.00 1104ms total, 18115.94 ops/sec + SET 4B str, 50/5 min/max/avg/p95: 0/ 3/ 0.83/ 2.00 333ms total, 60060.06 ops/sec + SET 4B buf, 1/5 min/max/avg/p95: 0/ 2/ 0.09/ 1.00 1876ms total, 10660.98 ops/sec + SET 4B buf, 50/5 min/max/avg/p95: 0/ 11/ 2.55/ 4.00 1025ms total, 19512.20 ops/sec + GET 4B str, 1/5 min/max/avg/p95: 0/ 1/ 0.05/ 1.00 1117ms total, 17905.10 ops/sec + GET 4B str, 50/5 min/max/avg/p95: 0/ 3/ 0.87/ 2.00 347ms total, 57636.89 ops/sec + GET 4B buf, 1/5 min/max/avg/p95: 0/ 1/ 0.05/ 1.00 1110ms total, 18018.02 ops/sec + GET 4B buf, 50/5 min/max/avg/p95: 0/ 2/ 0.85/ 2.00 342ms total, 58479.53 ops/sec + SET 4KiB str, 1/5 min/max/avg/p95: 0/ 1/ 0.05/ 1.00 1119ms total, 17873.10 ops/sec + SET 4KiB str, 50/5 min/max/avg/p95: 0/ 3/ 0.89/ 2.00 358ms total, 55865.92 ops/sec + SET 4KiB buf, 1/5 min/max/avg/p95: 0/ 1/ 0.09/ 1.00 1894ms total, 10559.66 ops/sec + SET 4KiB buf, 50/5 min/max/avg/p95: 0/ 7/ 2.57/ 4.00 1031ms total, 19398.64 ops/sec + GET 4KiB str, 1/5 min/max/avg/p95: 0/ 6/ 0.06/ 1.00 1248ms total, 16025.64 ops/sec + GET 4KiB str, 50/5 min/max/avg/p95: 0/ 3/ 1.03/ 2.00 415ms total, 48192.77 ops/sec + GET 4KiB buf, 1/5 min/max/avg/p95: 0/ 1/ 0.06/ 1.00 1177ms total, 16992.35 ops/sec + GET 4KiB buf, 50/5 min/max/avg/p95: 0/ 10/ 1.02/ 2.00 409ms total, 48899.76 ops/sec + INCR, 1/5 min/max/avg/p95: 0/ 2/ 0.05/ 0.55 1137ms total, 17590.15 ops/sec + INCR, 50/5 min/max/avg/p95: 0/ 2/ 0.85/ 2.00 343ms total, 58309.04 ops/sec + LPUSH, 1/5 min/max/avg/p95: 0/ 1/ 0.06/ 1.00 1143ms total, 17497.81 ops/sec + LPUSH, 50/5 min/max/avg/p95: 0/ 3/ 0.87/ 2.00 350ms total, 57142.86 ops/sec + LRANGE 10, 1/5 min/max/avg/p95: 0/ 2/ 0.06/ 1.00 1283ms total, 15588.46 ops/sec + LRANGE 10, 50/5 min/max/avg/p95: 0/ 3/ 1.12/ 2.00 449ms total, 44543.43 ops/sec + LRANGE 100, 1/5 min/max/avg/p95: 0/ 1/ 0.09/ 1.00 1932ms total, 10351.97 ops/sec + LRANGE 100, 50/5 min/max/avg/p95: 0/ 5/ 2.46/ 4.00 985ms total, 20304.57 ops/sec + SET 4MiB buf, 1/5 min/max/avg/p95: 1/ 4/ 1.37/ 2.00 691ms total, 723.59 ops/sec + SET 4MiB buf, 50/5 min/max/avg/p95: 3/ 166/ 57.66/ 116.00 601ms total, 831.95 ops/sec + GET 4MiB str, 1/5 min/max/avg/p95: 84/ 110/ 93.18/ 106.95 9320ms total, 10.73 ops/sec + GET 4MiB str, 50/5 min/max/avg/p95: 156/7375/3400.10/6840.40 8928ms total, 11.20 ops/sec + GET 4MiB buf, 1/5 min/max/avg/p95: 84/ 105/ 91.21/ 99.00 9129ms total, 10.95 ops/sec + GET 4MiB buf, 50/5 min/max/avg/p95: 424/5704/3518.94/5626.65 9145ms total, 10.93 ops/sec + +If you use very large responses in your application, the JavaScript parser +performs badly. Until the JS parser is fixed, you can use the C-based `hiredis` +parser bound to the official `hiredis` C library. To use `hiredis`, do: + + npm install hiredis redis + +If the `hiredis` npm module is installed, `node_redis` will use it by default. +Otherwise, the pure JavaScript parser will be used. + +If you use `hiredis`, be sure to rebuild it whenever you upgrade your version of +node. There are mysterious failures that can happen between node and native +code modules after a node upgrade. + +Most users find that the JS parser is faster than the `hiredis` parser. Because +of the pain associated with upgrading native code modules, you should only use +`hiredis` if your application needs it. + +hiredis parser: + + Client count: 5, node version: 0.10.32, server version: 2.8.18, parser: hiredis + PING, 1/5 min/max/avg/p95: 0/ 3/ 0.05/ 1.00 1092ms total, 18315.02 ops/sec + PING, 50/5 min/max/avg/p95: 0/ 5/ 0.87/ 2.00 347ms total, 57636.89 ops/sec + SET 4B str, 1/5 min/max/avg/p95: 0/ 2/ 0.06/ 1.00 1151ms total, 17376.19 ops/sec + SET 4B str, 50/5 min/max/avg/p95: 0/ 3/ 0.83/ 2.00 334ms total, 59880.24 ops/sec + SET 4B buf, 1/5 min/max/avg/p95: 0/ 3/ 0.09/ 1.00 1932ms total, 10351.97 ops/sec + SET 4B buf, 50/5 min/max/avg/p95: 0/ 9/ 2.64/ 4.00 1059ms total, 18885.74 ops/sec + GET 4B str, 1/5 min/max/avg/p95: 0/ 1/ 0.06/ 0.00 1185ms total, 16877.64 ops/sec + GET 4B str, 50/5 min/max/avg/p95: 0/ 3/ 0.85/ 2.00 341ms total, 58651.03 ops/sec + GET 4B buf, 1/5 min/max/avg/p95: 0/ 1/ 0.06/ 0.00 1179ms total, 16963.53 ops/sec + GET 4B buf, 50/5 min/max/avg/p95: 0/ 3/ 0.85/ 2.00 340ms total, 58823.53 ops/sec + SET 4KiB str, 1/5 min/max/avg/p95: 0/ 1/ 0.06/ 1.00 1210ms total, 16528.93 ops/sec + SET 4KiB str, 50/5 min/max/avg/p95: 0/ 3/ 0.93/ 2.00 372ms total, 53763.44 ops/sec + SET 4KiB buf, 1/5 min/max/avg/p95: 0/ 1/ 0.10/ 1.00 1967ms total, 10167.77 ops/sec + SET 4KiB buf, 50/5 min/max/avg/p95: 0/ 6/ 2.63/ 4.00 1053ms total, 18993.35 ops/sec + GET 4KiB str, 1/5 min/max/avg/p95: 0/ 6/ 0.06/ 1.00 1176ms total, 17006.80 ops/sec + GET 4KiB str, 50/5 min/max/avg/p95: 0/ 4/ 1.00/ 2.00 399ms total, 50125.31 ops/sec + GET 4KiB buf, 1/5 min/max/avg/p95: 0/ 1/ 0.06/ 1.00 1158ms total, 17271.16 ops/sec + GET 4KiB buf, 50/5 min/max/avg/p95: 0/ 3/ 0.99/ 2.00 398ms total, 50251.26 ops/sec + INCR, 1/5 min/max/avg/p95: 0/ 1/ 0.05/ 0.00 1112ms total, 17985.61 ops/sec + INCR, 50/5 min/max/avg/p95: 0/ 3/ 0.84/ 2.00 339ms total, 58997.05 ops/sec + LPUSH, 1/5 min/max/avg/p95: 0/ 1/ 0.05/ 1.00 1131ms total, 17683.47 ops/sec + LPUSH, 50/5 min/max/avg/p95: 0/ 3/ 0.86/ 2.00 345ms total, 57971.01 ops/sec + LRANGE 10, 1/5 min/max/avg/p95: 0/ 1/ 0.06/ 1.00 1228ms total, 16286.64 ops/sec + LRANGE 10, 50/5 min/max/avg/p95: 0/ 3/ 0.95/ 2.00 382ms total, 52356.02 ops/sec + LRANGE 100, 1/5 min/max/avg/p95: 0/ 1/ 0.08/ 1.00 1567ms total, 12763.24 ops/sec + LRANGE 100, 50/5 min/max/avg/p95: 0/ 6/ 1.68/ 3.00 675ms total, 29629.63 ops/sec + SET 4MiB buf, 1/5 min/max/avg/p95: 1/ 4/ 1.37/ 2.00 692ms total, 722.54 ops/sec + SET 4MiB buf, 50/5 min/max/avg/p95: 3/ 183/ 57.79/ 125.00 605ms total, 826.45 ops/sec + GET 4MiB str, 1/5 min/max/avg/p95: 5/ 16/ 8.14/ 12.95 816ms total, 122.55 ops/sec + GET 4MiB str, 50/5 min/max/avg/p95: 24/ 323/ 202.98/ 309.00 519ms total, 192.68 ops/sec + GET 4MiB buf, 1/5 min/max/avg/p95: 6/ 13/ 8.01/ 11.95 802ms total, 124.69 ops/sec + GET 4MiB buf, 50/5 min/max/avg/p95: 16/ 480/ 203.85/ 435.70 531ms total, 188.32 ops/sec + ## TODO Better tests for auth, disconnect/reconnect, and all combinations thereof. @@ -674,7 +738,7 @@ I think there are more performance improvements left in there for smaller values comment again with indignation!) ## Contributors -Some people have have added features and fixed bugs in `node_redis` other than me. +Many people have have added features and fixed bugs in `node_redis`. Ordered by date of first contribution. [Auto-generated](http://github.com/dtrejo/node-authors) on Wed Jul 25 2012 19:14:59 GMT-0700 (PDT). @@ -740,5 +804,3 @@ HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. - -![spacer](http://ranney.com/1px.gif) diff --git a/multi_bench.js b/multi_bench.js index 3a0d92da141..dbe04e515a2 100644 --- a/multi_bench.js +++ b/multi_bench.js @@ -7,7 +7,7 @@ var redis = require("./index"), client_options = { return_buffers: false }, - small_str, large_str, small_buf, large_buf; + small_str, large_str, small_buf, large_buf, very_large_str, very_large_buf; redis.debug_mode = false; @@ -37,7 +37,8 @@ function Test(args) { this.commands_completed = 0; this.max_pipeline = this.args.pipeline || num_requests; this.client_options = args.client_options || client_options; - + this.num_requests = args.reqs || num_requests; + this.connect_latency = new metrics.Histogram(); this.ready_latency = new metrics.Histogram(); this.command_latency = new metrics.Histogram(); @@ -89,13 +90,13 @@ Test.prototype.on_clients_ready = function () { Test.prototype.fill_pipeline = function () { var pipeline = this.commands_sent - this.commands_completed; - while (this.commands_sent < num_requests && pipeline < this.max_pipeline) { + while (this.commands_sent < this.num_requests && pipeline < this.max_pipeline) { this.commands_sent++; pipeline++; this.send_next(); } - if (this.commands_completed === num_requests) { + if (this.commands_completed === this.num_requests) { this.print_stats(); this.stop_clients(); } @@ -134,78 +135,63 @@ Test.prototype.print_stats = function () { var duration = Date.now() - this.test_start; console.log("min/max/avg/p95: " + this.command_latency.print_line() + " " + lpad(duration, 6) + "ms total, " + - lpad((num_requests / (duration / 1000)).toFixed(2), 8) + " ops/sec"); + lpad((this.num_requests / (duration / 1000)).toFixed(2), 8) + " ops/sec"); }; small_str = "1234"; small_buf = new Buffer(small_str); -large_str = (new Array(4097).join("-")); +large_str = (new Array(4096 + 1).join("-")); large_buf = new Buffer(large_str); +very_large_str = (new Array((4 * 1024 * 1024) + 1).join("-")); +very_large_buf = new Buffer(very_large_str); tests.push(new Test({descr: "PING", command: "ping", args: [], pipeline: 1})); tests.push(new Test({descr: "PING", command: "ping", args: [], pipeline: 50})); -tests.push(new Test({descr: "PING", command: "ping", args: [], pipeline: 200})); -tests.push(new Test({descr: "PING", command: "ping", args: [], pipeline: 20000})); - -tests.push(new Test({descr: "SET small str", command: "set", args: ["foo_rand000000000000", small_str], pipeline: 1})); -tests.push(new Test({descr: "SET small str", command: "set", args: ["foo_rand000000000000", small_str], pipeline: 50})); -tests.push(new Test({descr: "SET small str", command: "set", args: ["foo_rand000000000000", small_str], pipeline: 200})); -tests.push(new Test({descr: "SET small str", command: "set", args: ["foo_rand000000000000", small_str], pipeline: 20000})); - -tests.push(new Test({descr: "SET small buf", command: "set", args: ["foo_rand000000000000", small_buf], pipeline: 1})); -tests.push(new Test({descr: "SET small buf", command: "set", args: ["foo_rand000000000000", small_buf], pipeline: 50})); -tests.push(new Test({descr: "SET small buf", command: "set", args: ["foo_rand000000000000", small_buf], pipeline: 200})); -tests.push(new Test({descr: "SET small buf", command: "set", args: ["foo_rand000000000000", small_buf], pipeline: 20000})); - -tests.push(new Test({descr: "GET small str", command: "get", args: ["foo_rand000000000000"], pipeline: 1})); -tests.push(new Test({descr: "GET small str", command: "get", args: ["foo_rand000000000000"], pipeline: 50})); -tests.push(new Test({descr: "GET small str", command: "get", args: ["foo_rand000000000000"], pipeline: 200})); -tests.push(new Test({descr: "GET small str", command: "get", args: ["foo_rand000000000000"], pipeline: 20000})); - -tests.push(new Test({descr: "GET small buf", command: "get", args: ["foo_rand000000000000"], pipeline: 1, client_opts: { return_buffers: true} })); -tests.push(new Test({descr: "GET small buf", command: "get", args: ["foo_rand000000000000"], pipeline: 50, client_opts: { return_buffers: true} })); -tests.push(new Test({descr: "GET small buf", command: "get", args: ["foo_rand000000000000"], pipeline: 200, client_opts: { return_buffers: true} })); -tests.push(new Test({descr: "GET small buf", command: "get", args: ["foo_rand000000000000"], pipeline: 20000, client_opts: { return_buffers: true} })); - -tests.push(new Test({descr: "SET large str", command: "set", args: ["foo_rand000000000001", large_str], pipeline: 1})); -tests.push(new Test({descr: "SET large str", command: "set", args: ["foo_rand000000000001", large_str], pipeline: 50})); -tests.push(new Test({descr: "SET large str", command: "set", args: ["foo_rand000000000001", large_str], pipeline: 200})); -tests.push(new Test({descr: "SET large str", command: "set", args: ["foo_rand000000000001", large_str], pipeline: 20000})); - -tests.push(new Test({descr: "SET large buf", command: "set", args: ["foo_rand000000000001", large_buf], pipeline: 1})); -tests.push(new Test({descr: "SET large buf", command: "set", args: ["foo_rand000000000001", large_buf], pipeline: 50})); -tests.push(new Test({descr: "SET large buf", command: "set", args: ["foo_rand000000000001", large_buf], pipeline: 200})); -tests.push(new Test({descr: "SET large buf", command: "set", args: ["foo_rand000000000001", large_buf], pipeline: 20000})); - -tests.push(new Test({descr: "GET large str", command: "get", args: ["foo_rand000000000001"], pipeline: 1})); -tests.push(new Test({descr: "GET large str", command: "get", args: ["foo_rand000000000001"], pipeline: 50})); -tests.push(new Test({descr: "GET large str", command: "get", args: ["foo_rand000000000001"], pipeline: 200})); -tests.push(new Test({descr: "GET large str", command: "get", args: ["foo_rand000000000001"], pipeline: 20000})); - -tests.push(new Test({descr: "GET large buf", command: "get", args: ["foo_rand000000000001"], pipeline: 1, client_opts: { return_buffers: true} })); -tests.push(new Test({descr: "GET large buf", command: "get", args: ["foo_rand000000000001"], pipeline: 50, client_opts: { return_buffers: true} })); -tests.push(new Test({descr: "GET large buf", command: "get", args: ["foo_rand000000000001"], pipeline: 200, client_opts: { return_buffers: true} })); -tests.push(new Test({descr: "GET large buf", command: "get", args: ["foo_rand000000000001"], pipeline: 20000, client_opts: { return_buffers: true} })); + +tests.push(new Test({descr: "SET 4B str", command: "set", args: ["foo_rand000000000000", small_str], pipeline: 1})); +tests.push(new Test({descr: "SET 4B str", command: "set", args: ["foo_rand000000000000", small_str], pipeline: 50})); + +tests.push(new Test({descr: "SET 4B buf", command: "set", args: ["foo_rand000000000000", small_buf], pipeline: 1})); +tests.push(new Test({descr: "SET 4B buf", command: "set", args: ["foo_rand000000000000", small_buf], pipeline: 50})); + +tests.push(new Test({descr: "GET 4B str", command: "get", args: ["foo_rand000000000000"], pipeline: 1})); +tests.push(new Test({descr: "GET 4B str", command: "get", args: ["foo_rand000000000000"], pipeline: 50})); + +tests.push(new Test({descr: "GET 4B buf", command: "get", args: ["foo_rand000000000000"], pipeline: 1, client_opts: { return_buffers: true} })); +tests.push(new Test({descr: "GET 4B buf", command: "get", args: ["foo_rand000000000000"], pipeline: 50, client_opts: { return_buffers: true} })); + +tests.push(new Test({descr: "SET 4KiB str", command: "set", args: ["foo_rand000000000001", large_str], pipeline: 1})); +tests.push(new Test({descr: "SET 4KiB str", command: "set", args: ["foo_rand000000000001", large_str], pipeline: 50})); + +tests.push(new Test({descr: "SET 4KiB buf", command: "set", args: ["foo_rand000000000001", large_buf], pipeline: 1})); +tests.push(new Test({descr: "SET 4KiB buf", command: "set", args: ["foo_rand000000000001", large_buf], pipeline: 50})); + +tests.push(new Test({descr: "GET 4KiB str", command: "get", args: ["foo_rand000000000001"], pipeline: 1})); +tests.push(new Test({descr: "GET 4KiB str", command: "get", args: ["foo_rand000000000001"], pipeline: 50})); + +tests.push(new Test({descr: "GET 4KiB buf", command: "get", args: ["foo_rand000000000001"], pipeline: 1, client_opts: { return_buffers: true} })); +tests.push(new Test({descr: "GET 4KiB buf", command: "get", args: ["foo_rand000000000001"], pipeline: 50, client_opts: { return_buffers: true} })); tests.push(new Test({descr: "INCR", command: "incr", args: ["counter_rand000000000000"], pipeline: 1})); tests.push(new Test({descr: "INCR", command: "incr", args: ["counter_rand000000000000"], pipeline: 50})); -tests.push(new Test({descr: "INCR", command: "incr", args: ["counter_rand000000000000"], pipeline: 200})); -tests.push(new Test({descr: "INCR", command: "incr", args: ["counter_rand000000000000"], pipeline: 20000})); tests.push(new Test({descr: "LPUSH", command: "lpush", args: ["mylist", small_str], pipeline: 1})); tests.push(new Test({descr: "LPUSH", command: "lpush", args: ["mylist", small_str], pipeline: 50})); -tests.push(new Test({descr: "LPUSH", command: "lpush", args: ["mylist", small_str], pipeline: 200})); -tests.push(new Test({descr: "LPUSH", command: "lpush", args: ["mylist", small_str], pipeline: 20000})); tests.push(new Test({descr: "LRANGE 10", command: "lrange", args: ["mylist", "0", "9"], pipeline: 1})); tests.push(new Test({descr: "LRANGE 10", command: "lrange", args: ["mylist", "0", "9"], pipeline: 50})); -tests.push(new Test({descr: "LRANGE 10", command: "lrange", args: ["mylist", "0", "9"], pipeline: 200})); -tests.push(new Test({descr: "LRANGE 10", command: "lrange", args: ["mylist", "0", "9"], pipeline: 20000})); tests.push(new Test({descr: "LRANGE 100", command: "lrange", args: ["mylist", "0", "99"], pipeline: 1})); tests.push(new Test({descr: "LRANGE 100", command: "lrange", args: ["mylist", "0", "99"], pipeline: 50})); -tests.push(new Test({descr: "LRANGE 100", command: "lrange", args: ["mylist", "0", "99"], pipeline: 200})); -tests.push(new Test({descr: "LRANGE 100", command: "lrange", args: ["mylist", "0", "99"], pipeline: 20000})); + +tests.push(new Test({descr: "SET 4MiB buf", command: "set", args: ["foo_rand000000000002", very_large_buf], pipeline: 1, reqs: 500})); +tests.push(new Test({descr: "SET 4MiB buf", command: "set", args: ["foo_rand000000000002", very_large_buf], pipeline: 50, reqs: 500})); + +tests.push(new Test({descr: "GET 4MiB str", command: "get", args: ["foo_rand000000000002"], pipeline: 1, reqs: 100})); +tests.push(new Test({descr: "GET 4MiB str", command: "get", args: ["foo_rand000000000002"], pipeline: 50, reqs: 100})); + +tests.push(new Test({descr: "GET 4MiB buf", command: "get", args: ["foo_rand000000000002"], pipeline: 1, reqs: 100, client_opts: { return_buffers: true} })); +tests.push(new Test({descr: "GET 4MiB buf", command: "get", args: ["foo_rand000000000002"], pipeline: 50, reqs: 100, client_opts: { return_buffers: true} })); function next() { var test = tests.shift(); From 010a2acf3369382db8ccb3b3e67da98c4af9a1ce Mon Sep 17 00:00:00 2001 From: Jeremiah Lee Cohick Date: Fri, 10 Jul 2015 16:10:00 -0700 Subject: [PATCH 0220/1748] Added example of a Redis SCAN command --- examples/scan.js | 34 ++++++++++++++++++++++++++++++++++ 1 file changed, 34 insertions(+) create mode 100644 examples/scan.js diff --git a/examples/scan.js b/examples/scan.js new file mode 100644 index 00000000000..36f6872f005 --- /dev/null +++ b/examples/scan.js @@ -0,0 +1,34 @@ +var redis = require("redis"), + client = redis.createClient(); + +var cursor = 0; + +function scan() { + client.scan( + cursor, + "MATCH", "q:job:*", + "COUNT", "10", + function(err, res) { + if (err) throw err; + + // Update the cursor position for the next scan + cursor = res[0]; + + // From : + // An iteration starts when the cursor is set to 0, + // and terminates when the cursor returned by the server is 0. + if (cursor === 0) { + return console.log('Iteration complete'); + } else { + // Remember, more keys than COUNT or no keys may be returned + // See http://redis.io/commands/scan#the-count-option + if (res[1].length > 0) { + return console.log('Array of matching keys', res[1]); + } else { + // No keys were returned in this scan, but more keys exist. + return scan(); + } + } + } + ); +} From 6f28c6acf5ae626b53fa93441ad8eb89360938ba Mon Sep 17 00:00:00 2001 From: Jeremiah Lee Cohick Date: Fri, 10 Jul 2015 16:25:52 -0700 Subject: [PATCH 0221/1748] Cursor should be a string --- examples/scan.js | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/examples/scan.js b/examples/scan.js index 36f6872f005..35238b6e835 100644 --- a/examples/scan.js +++ b/examples/scan.js @@ -1,7 +1,7 @@ var redis = require("redis"), client = redis.createClient(); -var cursor = 0; +var cursor = '0'; function scan() { client.scan( @@ -17,7 +17,7 @@ function scan() { // From : // An iteration starts when the cursor is set to 0, // and terminates when the cursor returned by the server is 0. - if (cursor === 0) { + if (cursor === '0') { return console.log('Iteration complete'); } else { // Remember, more keys than COUNT or no keys may be returned From ff020d37f34ab503e9cfeac7490c6773eb3f93ee Mon Sep 17 00:00:00 2001 From: Jeremiah Lee Cohick Date: Fri, 10 Jul 2015 18:04:38 -0700 Subject: [PATCH 0222/1748] Clarified COUNT's behavior. scan() now will run until cursor reaches 0. --- examples/scan.js | 16 +++++++++------- 1 file changed, 9 insertions(+), 7 deletions(-) diff --git a/examples/scan.js b/examples/scan.js index 35238b6e835..0302843a768 100644 --- a/examples/scan.js +++ b/examples/scan.js @@ -15,19 +15,21 @@ function scan() { cursor = res[0]; // From : - // An iteration starts when the cursor is set to 0, - // and terminates when the cursor returned by the server is 0. + // "An iteration starts when the cursor is set to 0, + // and terminates when the cursor returned by the server is 0." if (cursor === '0') { return console.log('Iteration complete'); } else { - // Remember, more keys than COUNT or no keys may be returned + // Remember: more or less than COUNT or no keys may be returned // See http://redis.io/commands/scan#the-count-option + // Also, SCAN may return the same key multiple times + // See http://redis.io/commands/scan#scan-guarantees + if (res[1].length > 0) { - return console.log('Array of matching keys', res[1]); - } else { - // No keys were returned in this scan, but more keys exist. - return scan(); + console.log('Array of matching keys', res[1]); } + + return scan(); } } ); From d30e80abbe918e0919e55f42fdddc0334f65b861 Mon Sep 17 00:00:00 2001 From: Benjamin Coe Date: Sat, 11 Jul 2015 16:14:42 -0700 Subject: [PATCH 0223/1748] making an attempt to improve the test suite --- .npmignore | 2 +- .travis.yml | 7 ++++ lib/queue.js | 2 +- package.json | 12 +++--- test/queue-test.js | 35 +++++++++++++++++ test/run.sh | 4 ++ test-unref.js => test/test-unref.js | 4 +- test.js => test/test.js | 61 +++++++++++++++++------------ 8 files changed, 92 insertions(+), 35 deletions(-) create mode 100644 .travis.yml create mode 100644 test/queue-test.js create mode 100755 test/run.sh rename test-unref.js => test/test-unref.js (90%) rename test.js => test/test.js (97%) diff --git a/.npmignore b/.npmignore index 61755a69fc4..4df6949973f 100644 --- a/.npmignore +++ b/.npmignore @@ -1,6 +1,6 @@ examples/ benches/ -test.js +test/ diff_multi_bench_output.js generate_commands.js multi_bench.js diff --git a/.travis.yml b/.travis.yml new file mode 100644 index 00000000000..d75f4b5bb81 --- /dev/null +++ b/.travis.yml @@ -0,0 +1,7 @@ +language: node_js +sudo: false +node_js: + - "0.10" + - "0.12" + - "iojs" +after_success: npm run coverage diff --git a/lib/queue.js b/lib/queue.js index 3fc87ab1028..0d258cdfcbb 100644 --- a/lib/queue.js +++ b/lib/queue.js @@ -46,7 +46,7 @@ Queue.prototype.forEach = function (fn, thisv) { Queue.prototype.getLength = function () { return this.head.length - this.offset + this.tail.length; }; - + Object.defineProperty(Queue.prototype, "length", { get: function () { return this.getLength(); diff --git a/package.json b/package.json index 21f563ffe1a..495091bbea8 100644 --- a/package.json +++ b/package.json @@ -3,20 +3,22 @@ "version": "0.12.1", "description": "Redis client library", "keywords": [ - "redis", - "database" + "database", + "redis" ], "author": "Matt Ranney ", "license": "MIT", "main": "./index.js", "scripts": { - "test": "node ./test.js", - "coverage": "nyc npm test && nyc report" + "coverage": "nyc report --reporter=text-lcov | coveralls", + "test": "nyc ./test/run.sh" }, "devDependencies": { "colors": "~0.6.0-1", + "coveralls": "^2.11.2", + "hiredis": "^0.4.0", "metrics": ">=0.1.5", - "nyc": "^2.2.0", + "nyc": "^3.0.0", "underscore": "~1.4.4" }, "repository": { diff --git a/test/queue-test.js b/test/queue-test.js new file mode 100644 index 00000000000..017ea60785e --- /dev/null +++ b/test/queue-test.js @@ -0,0 +1,35 @@ +var assert = require("assert"); +var Queue = require('../lib/queue'); + +module.exports = function (tests, next) { + var q = new Queue(); + + tests.push = function () { + q.push('a'); + q.push(3); + assert.equal(q.length, 2); + return next(); + } + + tests.shift = function () { + assert.equal(q.shift(), 'a'); + return next(); + } + + tests.forEach = function () { + q.forEach(function (v) { + assert.equal(v, 3); + }); + + return next(); + } + + tests.forEachWithScope = function () { + q.forEach(function (v) { + assert.equal(this.foo, 'bar'); + assert.equal(v, 3); + }, {foo: 'bar'}); + + return next(); + } +} diff --git a/test/run.sh b/test/run.sh new file mode 100755 index 00000000000..ab030ba48fa --- /dev/null +++ b/test/run.sh @@ -0,0 +1,4 @@ +#!/usr/bin/env bash + +node ./test/test.js false hiredis +node ./test/test.js false javascript diff --git a/test-unref.js b/test/test-unref.js similarity index 90% rename from test-unref.js rename to test/test-unref.js index 4a3cc3622d2..d66d77c4338 100644 --- a/test-unref.js +++ b/test/test-unref.js @@ -1,4 +1,4 @@ -var redis = require("./") +var redis = require("../") //redis.debug_mode = true var PORT = process.argv[2] || 6379; var HOST = process.argv[3] || '127.0.0.1'; @@ -9,4 +9,4 @@ c.info(function (err, reply) { if (err) process.exit(-1) if (!reply.length) process.exit(-1) process.stdout.write(reply.length.toString()) -}) \ No newline at end of file +}) diff --git a/test.js b/test/test.js similarity index 97% rename from test.js rename to test/test.js index 0bba678fe79..3328673840f 100644 --- a/test.js +++ b/test/test.js @@ -1,15 +1,16 @@ /*global require console setTimeout process Buffer */ var PORT = 6379; var HOST = '127.0.0.1'; +var parser = process.argv[3]; -var redis = require("./index"), - client = redis.createClient(PORT, HOST), - client2 = redis.createClient(PORT, HOST), - client3 = redis.createClient(PORT, HOST), - bclient = redis.createClient(PORT, HOST, { return_buffers: true }), +var redis = require("../index"), + client = redis.createClient(PORT, HOST, { parser: parser }), + client2 = redis.createClient(PORT, HOST, { parser: parser }), + client3 = redis.createClient(PORT, HOST, { parser: parser }), + bclient = redis.createClient(PORT, HOST, { return_buffers: true, parser: parser }), assert = require("assert"), crypto = require("crypto"), - util = require("./lib/util"), + util = require("../lib/util"), fork = require("child_process").fork, test_db_num = 15, // this DB will be flushed and used for testing tests = {}, @@ -17,9 +18,8 @@ var redis = require("./index"), ended = false, next, cur_start, run_next_test, all_tests, all_start, test_count; - // Set this to truthy to see the wire protocol and other debugging info -redis.debug_mode = process.argv[2]; +redis.debug_mode = process.argv[2] ? JSON.parse(process.argv[2]) : false; function server_version_at_least(connection, desired_version) { // Return true if the server version >= desired_version @@ -116,7 +116,7 @@ next = function next(name) { // Tests are run in the order they are defined, so FLUSHDB should always be first. tests.IPV4 = function () { - var ipv4Client = redis.createClient( PORT, "127.0.0.1", { "family" : "IPv4" } ); + var ipv4Client = redis.createClient( PORT, "127.0.0.1", { family : "IPv4", parser: parser } ); ipv4Client.once("ready", function start_tests() { console.log("Connected to " + ipv4Client.address + ", Redis server version " + ipv4Client.server_info.redis_version + "\n"); @@ -142,7 +142,7 @@ tests.IPV6 = function () { console.log("Skipping IPV6 for old Redis server version < 2.8.0"); return run_next_test(); } - var ipv6Client = redis.createClient( PORT, "::1", { "family" : "IPv6" } ); + var ipv6Client = redis.createClient( PORT, "::1", { family: "IPv6", parser: parser } ); ipv6Client.once("ready", function start_tests() { console.log("Connected to " + ipv6Client.address + ", Redis server version " + ipv6Client.server_info.redis_version + "\n"); @@ -164,7 +164,7 @@ tests.IPV6 = function () { } tests.UNIX_SOCKET = function () { - var unixClient = redis.createClient('/tmp/redis.sock'); + var unixClient = redis.createClient('/tmp/redis.sock', { parser: parser }); // if this fails, check the permission of unix socket. // unixsocket /tmp/redis.sock @@ -374,7 +374,7 @@ tests.MULTI_7 = function () { return next(name); } - var p = require("./lib/parser/javascript"); + var p = require("../lib/parser/javascript"); var parser = new p.Parser(false); var reply_count = 0; function check_reply(reply) { @@ -728,7 +728,7 @@ tests.WATCH_TRANSACTION = function () { tests.detect_buffers = function () { - var name = "detect_buffers", detect_client = redis.createClient({detect_buffers: true}); + var name = "detect_buffers", detect_client = redis.createClient({ detect_buffers: true, parser: parser }); detect_client.on("ready", function () { // single Buffer or String @@ -795,9 +795,9 @@ tests.detect_buffers = function () { tests.socket_nodelay = function () { var name = "socket_nodelay", c1, c2, c3, ready_count = 0, quit_count = 0; - c1 = redis.createClient({socket_nodelay: true}); - c2 = redis.createClient({socket_nodelay: false}); - c3 = redis.createClient(); + c1 = redis.createClient({ socket_nodelay: true, parser: parser }); + c2 = redis.createClient({ socket_nodelay: false, parser: parser }); + c3 = redis.createClient({ parser: parser }); function quit_check() { quit_count++; @@ -1158,8 +1158,8 @@ tests.SUBSCRIBE_QUIT = function () { tests.SUBSCRIBE_CLOSE_RESUBSCRIBE = function () { var name = "SUBSCRIBE_CLOSE_RESUBSCRIBE"; - var c1 = redis.createClient(); - var c2 = redis.createClient(); + var c1 = redis.createClient({ parser: parser }); + var c2 = redis.createClient({ parser: parser }); var count = 0; /* Create two clients. c1 subscribes to two channels, c2 will publish to them. @@ -1955,7 +1955,7 @@ tests.MONITOR = function () { return next(name); } - monitor_client = redis.createClient(); + monitor_client = redis.createClient({ parser: parser }); monitor_client.monitor(function (err, res) { client.mget("some", "keys", "foo", "bar"); client.set("json", JSON.stringify({ @@ -2056,7 +2056,8 @@ tests.OPTIONAL_CALLBACK_UNDEFINED = function () { tests.ENABLE_OFFLINE_QUEUE_TRUE = function () { var name = "ENABLE_OFFLINE_QUEUE_TRUE"; var cli = redis.createClient(9999, null, { - max_attempts: 1 + max_attempts: 1, + parser: parser // default :) // enable_offline_queue: true }); @@ -2078,6 +2079,7 @@ tests.ENABLE_OFFLINE_QUEUE_TRUE = function () { tests.ENABLE_OFFLINE_QUEUE_FALSE = function () { var name = "ENABLE_OFFLINE_QUEUE_FALSE"; var cli = redis.createClient(9999, null, { + parser: parser, max_attempts: 1, enable_offline_queue: false }); @@ -2134,7 +2136,10 @@ tests.DOMAIN = function () { }); // this is the expected and desired behavior - domain.on('error', function (err) { next(name); }); + domain.on('error', function (err) { + domain.exit(); + next(name); + }); } }; @@ -2143,7 +2148,7 @@ tests.DOMAIN = function () { tests.auth = function () { var name = "AUTH", client4, ready_count = 0; - client4 = redis.createClient(9006, "filefish.redistogo.com"); + client4 = redis.createClient(9006, "filefish.redistogo.com", { parser: parser }); client4.auth("664b1b6aaf134e1ec281945a8de702a9", function (err, res) { assert.strictEqual(null, err, name); assert.strictEqual("OK", res.toString(), name); @@ -2165,7 +2170,7 @@ tests.auth = function () { tests.auth2 = function () { var name = "AUTH2", client4, ready_count = 0; - client4 = redis.createClient(9006, "filefish.redistogo.com", {auth_pass: "664b1b6aaf134e1ec281945a8de702a9"}); + client4 = redis.createClient(9006, "filefish.redistogo.com", { auth_pass: "664b1b6aaf134e1ec281945a8de702a9", parser: parser }); // test auth, then kill the connection so it'll auto-reconnect and auto-re-auth client4.on("ready", function () { @@ -2204,7 +2209,8 @@ tests.reconnectRetryMaxDelay = function() { name = 'reconnectRetryMaxDelay', reconnecting = false; var client = redis.createClient(PORT, HOST, { - retry_max_delay: 1 + retry_max_delay: 1, + parser: parser }); client.on('ready', function() { if (!reconnecting) { @@ -2223,7 +2229,7 @@ tests.reconnectRetryMaxDelay = function() { tests.unref = function () { var name = "unref"; - var external = fork("./test-unref.js"); + var external = fork("./test/test-unref.js"); var done = false; external.on("close", function (code) { assert(code == 0, "test-unref.js failed"); @@ -2235,9 +2241,12 @@ tests.unref = function () { } assert(done, "test-unref.js didn't finish in time."); next(name); - }, 500); + }, 1500); }; +// starting to split tests into multiple files. +require('./queue-test')(tests, next) + all_tests = Object.keys(tests); all_start = new Date(); test_count = 0; From 116a27a120932dede365466f8a867dd30eb6057a Mon Sep 17 00:00:00 2001 From: Benjamin Coe Date: Sat, 11 Jul 2015 18:48:37 -0700 Subject: [PATCH 0224/1748] update changelog, force travis build --- changelog.md | 1 + 1 file changed, 1 insertion(+) diff --git a/changelog.md b/changelog.md index 817031da7a4..89c1692a79f 100644 --- a/changelog.md +++ b/changelog.md @@ -1,6 +1,7 @@ Changelog ========= +* Refactor tests, and improve test coverage (Ben Coe) * Fix extraneous error output due to pubsub tests (Mikael Kohlmyr) ## v0.12.1 - Aug 10, 2014 From f4e1e2ce6dee4a6e0b535f37f13fdaf106784142 Mon Sep 17 00:00:00 2001 From: Benjamin Coe Date: Sat, 11 Jul 2015 19:07:43 -0700 Subject: [PATCH 0225/1748] getting redis configured --- .travis.yml | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/.travis.yml b/.travis.yml index d75f4b5bb81..9797bad1b44 100644 --- a/.travis.yml +++ b/.travis.yml @@ -4,4 +4,8 @@ node_js: - "0.10" - "0.12" - "iojs" +before_install: + - 'printf ''bind ::1 127.0.0.1\nunixsocket /tmp/redis.sock'' >> /tmp/redis.conf' +before_script: + - sudo redis-server /tmp/redis.conf after_success: npm run coverage From e17228a3652bfc77db073ff9fb12d54386832ac6 Mon Sep 17 00:00:00 2001 From: Benjamin Coe Date: Sat, 11 Jul 2015 19:09:19 -0700 Subject: [PATCH 0226/1748] enable sudo --- .travis.yml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.travis.yml b/.travis.yml index 9797bad1b44..2df8d3f3992 100644 --- a/.travis.yml +++ b/.travis.yml @@ -1,5 +1,5 @@ language: node_js -sudo: false +sudo: true node_js: - "0.10" - "0.12" From 7657fdbfb62df9e31048fcc92c89a2bd8228c46e Mon Sep 17 00:00:00 2001 From: Benjamin Coe Date: Sat, 11 Jul 2015 19:14:21 -0700 Subject: [PATCH 0227/1748] daemonize sure --- .travis.yml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.travis.yml b/.travis.yml index 2df8d3f3992..657281993e3 100644 --- a/.travis.yml +++ b/.travis.yml @@ -5,7 +5,7 @@ node_js: - "0.12" - "iojs" before_install: - - 'printf ''bind ::1 127.0.0.1\nunixsocket /tmp/redis.sock'' >> /tmp/redis.conf' + - 'printf ''bind ::1 127.0.0.1\nunixsocket /tmp/redis.sock\ndaemonize yes'' >> /tmp/redis.conf' before_script: - sudo redis-server /tmp/redis.conf after_success: npm run coverage From 8ca45e3f1faf4a1fb40306e52e8337bb363ddfd8 Mon Sep 17 00:00:00 2001 From: Benjamin Coe Date: Sat, 11 Jul 2015 19:24:44 -0700 Subject: [PATCH 0228/1748] loose socket perms --- .travis.yml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.travis.yml b/.travis.yml index 657281993e3..e7ed30fd796 100644 --- a/.travis.yml +++ b/.travis.yml @@ -5,7 +5,7 @@ node_js: - "0.12" - "iojs" before_install: - - 'printf ''bind ::1 127.0.0.1\nunixsocket /tmp/redis.sock\ndaemonize yes'' >> /tmp/redis.conf' + - 'printf ''bind ::1 127.0.0.1\nunixsocket /tmp/redis.sock\ndaemonize yes\nunixsocketperm 777'' >> /tmp/redis.conf' before_script: - sudo redis-server /tmp/redis.conf after_success: npm run coverage From 96da40719d8aa01d94321cf69d4dc2e64e2caef8 Mon Sep 17 00:00:00 2001 From: Benjamin Coe Date: Sat, 11 Jul 2015 19:31:19 -0700 Subject: [PATCH 0229/1748] badges \o/ --- README.md | 9 ++++++--- 1 file changed, 6 insertions(+), 3 deletions(-) diff --git a/README.md b/README.md index c3ffd1c898c..bbd06405e1c 100644 --- a/README.md +++ b/README.md @@ -1,6 +1,9 @@ redis - a node.js redis client =========================== +[![Build Status](https://travis-ci.org/NodeRedis/node_redis.png)](https://travis-ci.org/NodeRedis/node_redis) +[![Coverage Status](https://coveralls.io/reposNodeRedis/node_redisbadge.svg?branch=)](https://coveralls.io/r/NodeRedis/node_redis?branch=) + This is a complete Redis client for node.js. It supports all Redis commands, including many recently added commands like EVAL from experimental Redis server branches. @@ -162,7 +165,7 @@ every command on a client. * `socket_nodelay`: defaults to `true`. Whether to call setNoDelay() on the TCP stream, which disables the Nagle algorithm on the underlying socket. Setting this option to `false` can result in additional throughput at the cost of more latency. Most applications will want this set to `true`. -* `socket_keepalive` defaults to `true`. Whether the keep-alive functionality is enabled on the underlying socket. +* `socket_keepalive` defaults to `true`. Whether the keep-alive functionality is enabled on the underlying socket. * `no_ready_check`: defaults to `false`. When a connection is established to the Redis server, the server might still be loading the database from disk. While loading, the server not respond to any commands. To work around this, `node_redis` has a "ready check" which sends the `INFO` command to the server. The response from the `INFO` command @@ -181,8 +184,8 @@ limits total time for client to reconnect. Value is provided in milliseconds and * `max_attempts` defaults to `null`. By default client will try reconnecting until connected. Setting `max_attempts` limits total amount of reconnects. * `auth_pass` defaults to `null`. By default client will try connecting without auth. If set, client will run redis auth command on connect. -* `family` defaults to `IPv4`. The client connects in IPv4 if not specified or if the DNS resolution returns an IPv4 address. -You can force an IPv6 if you set the family to 'IPv6'. See nodejs net or dns modules how to use the family type. +* `family` defaults to `IPv4`. The client connects in IPv4 if not specified or if the DNS resolution returns an IPv4 address. +You can force an IPv6 if you set the family to 'IPv6'. See nodejs net or dns modules how to use the family type. ```js var redis = require("redis"), From 06d051ea1d71921c74f41972e1feff80922ddfd6 Mon Sep 17 00:00:00 2001 From: Benjamin Coe Date: Sat, 11 Jul 2015 19:45:05 -0700 Subject: [PATCH 0230/1748] had a bad URL to test coverage --- README.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/README.md b/README.md index bbd06405e1c..a7dbb80e287 100644 --- a/README.md +++ b/README.md @@ -2,7 +2,7 @@ redis - a node.js redis client =========================== [![Build Status](https://travis-ci.org/NodeRedis/node_redis.png)](https://travis-ci.org/NodeRedis/node_redis) -[![Coverage Status](https://coveralls.io/reposNodeRedis/node_redisbadge.svg?branch=)](https://coveralls.io/r/NodeRedis/node_redis?branch=) +[![Coverage Status](https://coveralls.io/repos/NodeRedis/node_redis/badge.svg?branch=)](https://coveralls.io/r/NodeRedis/node_redis?branch=) This is a complete Redis client for node.js. It supports all Redis commands, including many recently added commands like EVAL from experimental Redis server From 6f1acb0678f7288973e05456bd8a4facf889635c Mon Sep 17 00:00:00 2001 From: Raymond Myers Date: Tue, 31 Mar 2015 23:09:57 -0700 Subject: [PATCH 0231/1748] Fixed bug where buffer hgetall's in a multi would throw exceptions --- index.js | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/index.js b/index.js index f2d5ed43cc9..abee2973b7c 100644 --- a/index.js +++ b/index.js @@ -610,7 +610,7 @@ function try_callback(callback, reply) { function reply_to_object(reply) { var obj = {}, j, jl, key, val; - if (reply.length === 0) { + if (reply.length === 0 || !Array.isArray(reply)) { return null; } From f384d1c774d39090b9634502b230d7c29819135b Mon Sep 17 00:00:00 2001 From: Raymond Myers Date: Wed, 1 Apr 2015 00:10:23 -0700 Subject: [PATCH 0232/1748] Fixed the pub/sub logic accidentally stringing the first value of a multi/exec response --- index.js | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/index.js b/index.js index abee2973b7c..399ebf2259b 100644 --- a/index.js +++ b/index.js @@ -648,7 +648,7 @@ RedisClient.prototype.return_reply = function (reply) { // If the "reply" here is actually a message received asynchronously due to a // pubsub subscription, don't pop the command queue as we'll only be consuming // the head command prematurely. - if (Array.isArray(reply) && reply.length > 0 && reply[0]) { + if (this.pub_sub_mode && Array.isArray(reply) && reply.length > 0 && reply[0]) { type = reply[0].toString(); } From aea03d60beb499e547ca34a6cbdfad634f36c63c Mon Sep 17 00:00:00 2001 From: Raymond Myers Date: Wed, 1 Apr 2015 00:38:21 -0700 Subject: [PATCH 0233/1748] Fixed exec result arrays being stringed in detect_buffers mode --- index.js | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/index.js b/index.js index 399ebf2259b..5b4c6d56b52 100644 --- a/index.js +++ b/index.js @@ -672,7 +672,7 @@ RedisClient.prototype.return_reply = function (reply) { if (command_obj && !command_obj.sub_command) { if (typeof command_obj.callback === "function") { - if (this.options.detect_buffers && command_obj.buffer_args === false) { + if (this.options.detect_buffers && command_obj.buffer_args === false && 'exec' !== command_obj.command.toLowerCase()) { // If detect_buffers option was specified, then the reply from the parser will be Buffers. // If this command did not use Buffer arguments, then convert the reply to Strings here. reply = reply_to_strings(reply); From ded75c8ea2cbf1194c3426a2534cffe7167b45dc Mon Sep 17 00:00:00 2001 From: Raymond Myers Date: Wed, 1 Apr 2015 01:03:10 -0700 Subject: [PATCH 0234/1748] Fixed detect_buffers keeping all multi/exec results as buffers --- index.js | 19 +++++++++++++++++-- 1 file changed, 17 insertions(+), 2 deletions(-) diff --git a/index.js b/index.js index 5b4c6d56b52..70479aeb18d 100644 --- a/index.js +++ b/index.js @@ -1106,15 +1106,24 @@ Multi.prototype.HMSET = Multi.prototype.hmset; Multi.prototype.exec = function (callback) { var self = this; var errors = []; + var wants_buffers = []; // drain queue, callback will catch "QUEUED" or error // TODO - get rid of all of these anonymous functions which are elegant but slow this.queue.forEach(function (args, index) { - var command = args[0], obj; + var command = args[0], obj, i, il, buffer_args; if (typeof args[args.length - 1] === "function") { args = args.slice(1, -1); } else { args = args.slice(1); } + // Keep track of who wants buffer responses: + buffer_args = false; + for (i = 0, il = args.length; i < il; i += 1) { + if (Buffer.isBuffer(args[i])) { + buffer_args = true; + } + } + wants_buffers.push(buffer_args); if (args.length === 1 && Array.isArray(args[0])) { args = args[0]; } @@ -1149,12 +1158,18 @@ Multi.prototype.exec = function (callback) { } } - var i, il, reply, args; + var i, il, reply, to_buffer, args; if (replies) { for (i = 1, il = self.queue.length; i < il; i += 1) { reply = replies[i - 1]; args = self.queue[i]; + to_buffer = wants_buffers[i]; + + // If we asked for strings, even in detect_buffers mode, then return strings: + if (self._client.options.detect_buffers && to_buffer === false) { + replies[i - 1] = reply = reply_to_strings(reply); + } // TODO - confusing and error-prone that hgetall is special cased in two places if (reply && args[0].toLowerCase() === "hgetall") { From 19c55712293b2bea82e64a22a7ea63c530c81d52 Mon Sep 17 00:00:00 2001 From: Raymond Myers Date: Wed, 1 Apr 2015 01:17:13 -0700 Subject: [PATCH 0235/1748] Added a test case for detect_buffer behavior in a multi/exec --- test/test.js | 89 ++++++++++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 89 insertions(+) diff --git a/test/test.js b/test/test.js index 3328673840f..e7db7639142 100644 --- a/test/test.js +++ b/test/test.js @@ -792,6 +792,95 @@ tests.detect_buffers = function () { }); }; +tests.detect_buffers_multi = function () { + var name = "detect_buffers_multi", detect_client = redis.createClient({detect_buffers: true}); + + detect_client.on("ready", function () { + // single Buffer or String + detect_client.set("string key 1", "string value"); + detect_client.multi().get("string key 1").exec(require_string("string value", name)); + detect_client.multi().get(new Buffer("string key 1")).exec(function (err, reply) { + assert.strictEqual(null, err, name); + assert.strictEqual(1, reply.length, name); + assert.strictEqual(true, Buffer.isBuffer(reply[0]), name); + assert.strictEqual("", reply[0].inspect(), name); + }); + + detect_client.hmset("hash key 2", "key 1", "val 1", "key 2", "val 2"); + // array of Buffers or Strings + detect_client.multi().hmget("hash key 2", "key 1", "key 2").exec(function (err, reply) { + assert.strictEqual(null, err, name); + assert.strictEqual(true, Array.isArray(reply), name); + assert.strictEqual(1, reply.length, name); + assert.strictEqual(2, reply[0].length, name); + assert.strictEqual("val 1", reply[0][0], name); + assert.strictEqual("val 2", reply[0][1], name); + }); + detect_client.multi().hmget(new Buffer("hash key 2"), "key 1", "key 2").exec(function (err, reply) { + assert.strictEqual(null, err, name); + assert.strictEqual(true, Array.isArray(reply)); + assert.strictEqual(1, reply.length, name); + assert.strictEqual(2, reply[0].length, name); + assert.strictEqual(true, Buffer.isBuffer(reply[0][0])); + assert.strictEqual(true, Buffer.isBuffer(reply[0][1])); + assert.strictEqual("", reply[0][0].inspect(), name); + assert.strictEqual("", reply[0][1].inspect(), name); + }); + + // array of strings with undefined values (repro #344) + detect_client.multi().hmget("hash key 2", "key 3", "key 4").exec(function(err, reply) { + assert.strictEqual(null, err, name); + assert.strictEqual(true, Array.isArray(reply), name); + assert.strictEqual(1, reply.length, name); + assert.strictEqual(2, reply[0].length, name); + assert.equal(null, reply[0][0], name); + assert.equal(null, reply[0][1], name); + }); + + // Object of Buffers or Strings + detect_client.multi().hgetall("hash key 2").exec(function (err, reply) { + assert.strictEqual(null, err, name); + assert.strictEqual(1, reply.length, name); + assert.strictEqual("object", typeof reply[0], name); + assert.strictEqual(2, Object.keys(reply[0]).length, name); + assert.strictEqual("val 1", reply[0]["key 1"], name); + assert.strictEqual("val 2", reply[0]["key 2"], name); + }); + detect_client.multi().hgetall(new Buffer("hash key 2")).exec(function (err, reply) { + assert.strictEqual(null, err, name); + assert.strictEqual(1, reply.length, name); + assert.strictEqual("object", typeof reply, name); + assert.strictEqual(2, Object.keys(reply[0]).length, name); + assert.strictEqual(true, Buffer.isBuffer(reply[0]["key 1"])); + assert.strictEqual(true, Buffer.isBuffer(reply[0]["key 2"])); + assert.strictEqual("", reply[0]["key 1"].inspect(), name); + assert.strictEqual("", reply[0]["key 2"].inspect(), name); + }); + + // Can interleave string and buffer results: + detect_client.multi() + .hget("hash key 2", "key 1") + .hget(new Buffer("hash key 2"), "key 1") + .hget("hash key 2", new Buffer("key 2")) + .hget("hash key 2", "key 2") + .exec(function (err, reply) { + assert.strictEqual(null, err, name); + assert.strictEqual(true, Array.isArray(reply)); + assert.strictEqual(4, reply.length, name); + assert.strictEqual("val 1", reply[0], name); + assert.strictEqual(true, Buffer.isBuffer(reply[1])); + assert.strictEqual("", reply[1].inspect(), name); + assert.strictEqual(true, Buffer.isBuffer(reply[2])); + assert.strictEqual("", reply[2].inspect(), name); + assert.strictEqual("val 2", reply[3], name); + }); + + detect_client.quit(function (err, res) { + next(name); + }); + }); +}; + tests.socket_nodelay = function () { var name = "socket_nodelay", c1, c2, c3, ready_count = 0, quit_count = 0; From dc461f08d4571fb076f8b577270539236ddafffc Mon Sep 17 00:00:00 2001 From: Kyle Date: Thu, 16 Jul 2015 11:01:29 -0500 Subject: [PATCH 0236/1748] clarifies createClient in README as per #781 --- README.md | 15 ++++++++------- 1 file changed, 8 insertions(+), 7 deletions(-) diff --git a/README.md b/README.md index a7dbb80e287..9a97aaf65d6 100644 --- a/README.md +++ b/README.md @@ -144,15 +144,17 @@ resume sending when you get `drain`. `client` will emit `idle` when there are no outstanding commands that are awaiting a response. ## redis.createClient() +If you have `redis-server` running on the same computer as node, then the defaults for +port and host are probably fine and you don't need to supply any arguments. `createClient()` returns a `RedisClient`. ### overloading -* redis.createClient() = redis.createClient(6379, '127.0.0.1', {}) -* redis.createClient(options) = redis.createClient(6379, '127.0.0.1', options) -* redis.createClient(unix_socket, options) -* redis.createClient(port, host, options) +* `redis.createClient(port,host,options)` +* `redis.createClient()` is equivalent to `redis.createClient(6379, '127.0.0.1', {})` +* `redis.createClient(options)` is equivalent to `redis.createClient(6379, '127.0.0.1', options)` +* `redis.createClient(unix_socket, options)` +* `redis.createClient(port, host, options)` -If you have `redis-server` running on the same computer as node, then the defaults for -port and host are probably fine. `options` in an object with the following possible properties: + `options` in an object with the following possible properties: * `parser`: which Redis protocol reply parser to use. Defaults to `hiredis` if that module is installed. This may also be set to `javascript`. @@ -205,7 +207,6 @@ You can force an IPv6 if you set the family to 'IPv6'. See nodejs net or dns mod client.end(); ``` -`createClient()` returns a `RedisClient` object that is named `client` in all of the examples here. ## client.auth(password, callback) From bc48c2b069552d9de9f8d02e1a353f81ba23a42f Mon Sep 17 00:00:00 2001 From: Kyle Date: Thu, 16 Jul 2015 11:02:25 -0500 Subject: [PATCH 0237/1748] fix typo --- README.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/README.md b/README.md index 9a97aaf65d6..836bf9d1fa5 100644 --- a/README.md +++ b/README.md @@ -145,7 +145,7 @@ resume sending when you get `drain`. ## redis.createClient() If you have `redis-server` running on the same computer as node, then the defaults for -port and host are probably fine and you don't need to supply any arguments. `createClient()` returns a `RedisClient`. +port and host are probably fine and you don't need to supply any arguments. `createClient()` returns a `RedisClient` object. ### overloading * `redis.createClient(port,host,options)` From defec9c1e1a97dbe0531c17b22fd00afd3e27021 Mon Sep 17 00:00:00 2001 From: Kyle Date: Thu, 16 Jul 2015 11:30:32 -0500 Subject: [PATCH 0238/1748] fixed duplicate and typo --- README.md | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/README.md b/README.md index 836bf9d1fa5..c9900bbbef3 100644 --- a/README.md +++ b/README.md @@ -152,9 +152,8 @@ port and host are probably fine and you don't need to supply any arguments. `cre * `redis.createClient()` is equivalent to `redis.createClient(6379, '127.0.0.1', {})` * `redis.createClient(options)` is equivalent to `redis.createClient(6379, '127.0.0.1', options)` * `redis.createClient(unix_socket, options)` -* `redis.createClient(port, host, options)` - `options` in an object with the following possible properties: + `options` is an object with the following possible properties: * `parser`: which Redis protocol reply parser to use. Defaults to `hiredis` if that module is installed. This may also be set to `javascript`. From 208b7874bf62344da9a0b39cb85189058830e718 Mon Sep 17 00:00:00 2001 From: Ruben Bridgewater Date: Wed, 22 Jul 2015 17:11:38 +0200 Subject: [PATCH 0239/1748] Rewrite createClient. Fixes #651 --- index.js | 49 ++++++++++++++----------------------------------- 1 file changed, 14 insertions(+), 35 deletions(-) diff --git a/index.js b/index.js index 70479aeb18d..770987beb1e 100644 --- a/index.js +++ b/index.js @@ -1228,50 +1228,29 @@ RedisClient.prototype.eval = RedisClient.prototype.EVAL = function () { }); }; +exports.createClient = function(arg0, arg1, options) { + if (typeof arg0 === 'object' || arg0 === undefined) { + options = arg0 || options; + return createClient_tcp(default_port, default_host, options); + } + if (typeof arg0 === 'number' || typeof arg0 === 'string' && arg0.match(/^\d+$/)){ + return createClient_tcp(arg0, arg1, options); + } + if (typeof arg0 === 'string') { + options = arg1 || {}; -exports.createClient = function(arg0, arg1, arg2){ - if( arguments.length === 0 ){ - - // createClient() - return createClient_tcp(default_port, default_host, {}); - - } else if( typeof arg0 === 'number' || - typeof arg0 === 'string' && arg0.match(/^\d+$/) ){ - - // createClient( 3000, host, options) - // createClient('3000', host, options) - return createClient_tcp(arg0, arg1, arg2); - - } else if( typeof arg0 === 'string' ){ - var parsed = URL.parse(arg0, true, true), - options = (arg1 || {}); - + var parsed = URL.parse(arg0, true, true); if (parsed.hostname) { if (parsed.auth) { options.auth_pass = parsed.auth.split(':')[1]; } - // createClient(3000, host, options) return createClient_tcp((parsed.port || default_port), parsed.hostname, options); - } else { - // createClient( '/tmp/redis.sock', options) - return createClient_unix(arg0,options); } - } else if( arg0 !== null && typeof arg0 === 'object' ){ - - // createClient(options) - return createClient_tcp(default_port, default_host, arg0 ); - - } else if( arg0 === null && arg1 === null ){ - - // for backward compatibility - // createClient(null,null,options) - return createClient_tcp(default_port, default_host, arg2); - - } else { - throw new Error('unknown type of connection in createClient()'); + return createClient_unix(arg0, options); } -} + throw new Error('unknown type of connection in createClient()'); +}; var createClient_unix = function(path, options){ var cnxOptions = { From 1f9e536ca0cae200b621145a096ef15e3161c4c4 Mon Sep 17 00:00:00 2001 From: Ruben Bridgewater Date: Wed, 22 Jul 2015 16:17:02 +0200 Subject: [PATCH 0240/1748] Add use strict statements This is going to improve the performance minimal and improves the safety of the code --- benches/buffer_bench.js | 2 ++ benches/hiredis_parser.js | 2 ++ benches/re_sub_test.js | 2 ++ benches/reconnect_test.js | 2 ++ benches/stress/codec.js | 2 ++ benches/stress/speed/speed.js | 2 ++ benches/sub_quit_test.js | 2 ++ connection_breaker.js | 2 ++ diff_multi_bench_output.js | 2 ++ examples/auth.js | 2 ++ examples/backpressure_drain.js | 2 ++ examples/eval.js | 2 ++ examples/extend.js | 2 ++ examples/file.js | 2 ++ examples/mget.js | 2 ++ examples/monitor.js | 2 ++ examples/multi.js | 2 ++ examples/multi2.js | 2 ++ examples/psubscribe.js | 2 ++ examples/pub_sub.js | 2 ++ examples/scan.js | 2 ++ examples/simple.js | 2 ++ examples/sort.js | 2 ++ examples/subqueries.js | 2 ++ examples/subquery.js | 2 ++ examples/web_server.js | 6 ++++-- generate_commands.js | 2 ++ index.js | 2 ++ lib/commands.js | 2 ++ lib/parser/hiredis.js | 2 ++ lib/parser/javascript.js | 2 ++ lib/queue.js | 2 ++ lib/to_array.js | 2 ++ lib/util.js | 2 ++ multi_bench.js | 2 ++ test/queue-test.js | 2 ++ test/test.js | 2 ++ 37 files changed, 76 insertions(+), 2 deletions(-) diff --git a/benches/buffer_bench.js b/benches/buffer_bench.js index a504fbc0876..df16d90bf4a 100644 --- a/benches/buffer_bench.js +++ b/benches/buffer_bench.js @@ -1,3 +1,5 @@ +'use strict'; + var source = new Buffer(100), dest = new Buffer(100), i, j, k, tmp, count = 1000000, bytes = 100; diff --git a/benches/hiredis_parser.js b/benches/hiredis_parser.js index f1515b110b6..489a4912e2c 100644 --- a/benches/hiredis_parser.js +++ b/benches/hiredis_parser.js @@ -1,3 +1,5 @@ +'use strict'; + var Parser = require('../lib/parser/hiredis').Parser; var assert = require('assert'); diff --git a/benches/re_sub_test.js b/benches/re_sub_test.js index 64b8f31287f..256d0d0d1d6 100644 --- a/benches/re_sub_test.js +++ b/benches/re_sub_test.js @@ -1,3 +1,5 @@ +'use strict'; + var client = require('../index').createClient() , client2 = require('../index').createClient() , assert = require('assert'); diff --git a/benches/reconnect_test.js b/benches/reconnect_test.js index 7abdd516651..7556ea430a4 100644 --- a/benches/reconnect_test.js +++ b/benches/reconnect_test.js @@ -1,3 +1,5 @@ +'use strict'; + var redis = require("../index").createClient(null, null, { // max_attempts: 4 }); diff --git a/benches/stress/codec.js b/benches/stress/codec.js index 7d764f60728..c679a6ee016 100644 --- a/benches/stress/codec.js +++ b/benches/stress/codec.js @@ -1,3 +1,5 @@ +'use strict'; + var json = { encode: JSON.stringify, decode: JSON.parse diff --git a/benches/stress/speed/speed.js b/benches/stress/speed/speed.js index 8e43cbc03b9..93d54f0c133 100644 --- a/benches/stress/speed/speed.js +++ b/benches/stress/speed/speed.js @@ -1,3 +1,5 @@ +'use strict'; + var msgpack = require('node-msgpack'); var bison = require('bison'); var codec = { diff --git a/benches/sub_quit_test.js b/benches/sub_quit_test.js index ad1f413228a..ae3725a999c 100644 --- a/benches/sub_quit_test.js +++ b/benches/sub_quit_test.js @@ -1,3 +1,5 @@ +'use strict'; + var client = require("redis").createClient(), client2 = require("redis").createClient(); diff --git a/connection_breaker.js b/connection_breaker.js index 489f5d56450..f2db0eb1b69 100644 --- a/connection_breaker.js +++ b/connection_breaker.js @@ -1,3 +1,5 @@ +'use strict'; + var net = require('net'); var proxyPort = 6379; diff --git a/diff_multi_bench_output.js b/diff_multi_bench_output.js index 73964720198..23cb3ada215 100755 --- a/diff_multi_bench_output.js +++ b/diff_multi_bench_output.js @@ -1,5 +1,7 @@ #!/usr/bin/env node +'use strict'; + var colors = require('colors'), fs = require('fs'), _ = require('underscore'), diff --git a/examples/auth.js b/examples/auth.js index 6c0a563cd8b..275c40961ea 100644 --- a/examples/auth.js +++ b/examples/auth.js @@ -1,3 +1,5 @@ +'use strict'; + var redis = require("redis"), client = redis.createClient(); diff --git a/examples/backpressure_drain.js b/examples/backpressure_drain.js index 3488ef4d3f7..74107bb6a08 100644 --- a/examples/backpressure_drain.js +++ b/examples/backpressure_drain.js @@ -1,3 +1,5 @@ +'use strict'; + var redis = require("../index"), client = redis.createClient(null, null, { command_queue_high_water: 5, diff --git a/examples/eval.js b/examples/eval.js index a3ff6b0793d..0ec1c215e9a 100644 --- a/examples/eval.js +++ b/examples/eval.js @@ -1,3 +1,5 @@ +'use strict'; + var redis = require("../index"), client = redis.createClient(); diff --git a/examples/extend.js b/examples/extend.js index 488b8c2dc5d..b3a10fa7161 100644 --- a/examples/extend.js +++ b/examples/extend.js @@ -1,3 +1,5 @@ +'use strict'; + var redis = require("redis"), client = redis.createClient(); diff --git a/examples/file.js b/examples/file.js index 4d2b5d1c987..91970198585 100644 --- a/examples/file.js +++ b/examples/file.js @@ -1,3 +1,5 @@ +'use strict'; + // Read a file from disk, store it in Redis, then read it back from Redis. var redis = require("redis"), diff --git a/examples/mget.js b/examples/mget.js index 936740d32f3..30f2bce90a4 100644 --- a/examples/mget.js +++ b/examples/mget.js @@ -1,3 +1,5 @@ +'use strict'; + var client = require("redis").createClient(); client.mget(["sessions started", "sessions started", "foo"], function (err, res) { diff --git a/examples/monitor.js b/examples/monitor.js index 2cb6a4e1ecb..c67f596294e 100644 --- a/examples/monitor.js +++ b/examples/monitor.js @@ -1,3 +1,5 @@ +'use strict'; + var client = require("../index").createClient(), util = require("util"); diff --git a/examples/multi.js b/examples/multi.js index 35c08e18403..31bc14ffb00 100644 --- a/examples/multi.js +++ b/examples/multi.js @@ -1,3 +1,5 @@ +'use strict'; + var redis = require("redis"), client = redis.createClient(), set_size = 20; diff --git a/examples/multi2.js b/examples/multi2.js index 8be4d7313cc..da722b23bbd 100644 --- a/examples/multi2.js +++ b/examples/multi2.js @@ -1,3 +1,5 @@ +'use strict'; + var redis = require("redis"), client = redis.createClient(), multi; diff --git a/examples/psubscribe.js b/examples/psubscribe.js index c57117b8a63..f46d235bed4 100644 --- a/examples/psubscribe.js +++ b/examples/psubscribe.js @@ -1,3 +1,5 @@ +'use strict'; + var redis = require("redis"), client1 = redis.createClient(), client2 = redis.createClient(), diff --git a/examples/pub_sub.js b/examples/pub_sub.js index aa508d6c9d2..3446a552d16 100644 --- a/examples/pub_sub.js +++ b/examples/pub_sub.js @@ -1,3 +1,5 @@ +'use strict'; + var redis = require("redis"), client1 = redis.createClient(), msg_count = 0, client2 = redis.createClient(); diff --git a/examples/scan.js b/examples/scan.js index 0302843a768..f4aac2ef4d3 100644 --- a/examples/scan.js +++ b/examples/scan.js @@ -1,3 +1,5 @@ +'use strict'; + var redis = require("redis"), client = redis.createClient(); diff --git a/examples/simple.js b/examples/simple.js index f1f2e3209b8..e8327498958 100644 --- a/examples/simple.js +++ b/examples/simple.js @@ -1,3 +1,5 @@ +'use strict'; + var redis = require("redis"), client = redis.createClient(); diff --git a/examples/sort.js b/examples/sort.js index e7c6249e405..4c804bee1db 100644 --- a/examples/sort.js +++ b/examples/sort.js @@ -1,3 +1,5 @@ +'use strict'; + var redis = require("redis"), client = redis.createClient(); diff --git a/examples/subqueries.js b/examples/subqueries.js index 560db2404e1..ebae6d48553 100644 --- a/examples/subqueries.js +++ b/examples/subqueries.js @@ -1,3 +1,5 @@ +'use strict'; + // Sending commands in response to other commands. // This example runs "type" against every key in the database // diff --git a/examples/subquery.js b/examples/subquery.js index 861657e1f3a..4f1edb0ffde 100644 --- a/examples/subquery.js +++ b/examples/subquery.js @@ -1,3 +1,5 @@ +'use strict'; + var client = require("redis").createClient(); function print_results(obj) { diff --git a/examples/web_server.js b/examples/web_server.js index 9fd85923de1..dfd420bd1a3 100644 --- a/examples/web_server.js +++ b/examples/web_server.js @@ -1,3 +1,5 @@ +'use strict'; + // A simple web server that generates dyanmic content based on responses from Redis var http = require("http"), server, @@ -7,9 +9,9 @@ server = http.createServer(function (request, response) { response.writeHead(200, { "Content-Type": "text/plain" }); - + var redis_info, total_requests; - + redis_client.info(function (err, reply) { redis_info = reply; // stash response in outer scope }); diff --git a/generate_commands.js b/generate_commands.js index e6949d3a141..3ad420edc35 100644 --- a/generate_commands.js +++ b/generate_commands.js @@ -1,3 +1,5 @@ +'use strict'; + var http = require("http"), fs = require("fs"); diff --git a/index.js b/index.js index 770987beb1e..6c70a251297 100644 --- a/index.js +++ b/index.js @@ -1,3 +1,5 @@ +'use strict'; + /*global Buffer require exports console setTimeout */ var net = require("net"), diff --git a/lib/commands.js b/lib/commands.js index b0365350cda..3b01543d99d 100644 --- a/lib/commands.js +++ b/lib/commands.js @@ -1,3 +1,5 @@ +'use strict'; + // This file was generated by ./generate_commands.js on Wed Apr 23 2014 14:51:21 GMT-0700 (PDT) module.exports = [ "append", diff --git a/lib/parser/hiredis.js b/lib/parser/hiredis.js index 940bfeeb767..29dbfd59a95 100644 --- a/lib/parser/hiredis.js +++ b/lib/parser/hiredis.js @@ -1,3 +1,5 @@ +'use strict'; + var events = require("events"), util = require("../util"), hiredis = require("hiredis"); diff --git a/lib/parser/javascript.js b/lib/parser/javascript.js index 0990cc098da..5ad04362159 100644 --- a/lib/parser/javascript.js +++ b/lib/parser/javascript.js @@ -1,3 +1,5 @@ +'use strict'; + var events = require("events"), util = require("../util"); diff --git a/lib/queue.js b/lib/queue.js index 0d258cdfcbb..d2eb17e56cd 100644 --- a/lib/queue.js +++ b/lib/queue.js @@ -1,3 +1,5 @@ +'use strict'; + // Queue class adapted from Tim Caswell's pattern library // http://github.com/creationix/pattern/blob/master/lib/pattern/queue.js diff --git a/lib/to_array.js b/lib/to_array.js index 88a57e18a42..87804f8c3b2 100644 --- a/lib/to_array.js +++ b/lib/to_array.js @@ -1,3 +1,5 @@ +'use strict'; + function to_array(args) { var len = args.length, arr = new Array(len), i; diff --git a/lib/util.js b/lib/util.js index fc255ae9536..359cd7e9481 100644 --- a/lib/util.js +++ b/lib/util.js @@ -1,3 +1,5 @@ +'use strict'; + // Support for very old versions of node where the module was called "sys". At some point, we should abandon this. var util; diff --git a/multi_bench.js b/multi_bench.js index dbe04e515a2..4e99376787e 100644 --- a/multi_bench.js +++ b/multi_bench.js @@ -1,3 +1,5 @@ +'use strict'; + var redis = require("./index"), metrics = require("metrics"), num_clients = parseInt(process.argv[2], 10) || 5, diff --git a/test/queue-test.js b/test/queue-test.js index 017ea60785e..4811eca90ae 100644 --- a/test/queue-test.js +++ b/test/queue-test.js @@ -1,3 +1,5 @@ +'use strict'; + var assert = require("assert"); var Queue = require('../lib/queue'); diff --git a/test/test.js b/test/test.js index e7db7639142..325ea136587 100644 --- a/test/test.js +++ b/test/test.js @@ -1,3 +1,5 @@ +'use strict'; + /*global require console setTimeout process Buffer */ var PORT = 6379; var HOST = '127.0.0.1'; From a2bc59721293bbc9504db06c0ee18a51429c54c6 Mon Sep 17 00:00:00 2001 From: Ruben Bridgewater Date: Wed, 22 Jul 2015 16:19:02 +0200 Subject: [PATCH 0241/1748] Add simicolons This is just a style change --- diff_multi_bench_output.js | 2 +- examples/extend.js | 2 +- examples/file.js | 6 +++--- examples/unix_socket.js | 4 +++- index.js | 10 +++++----- test/queue-test.js | 10 +++++----- test/test-unref.js | 16 +++++++++------- test/test.js | 20 ++++++++++---------- 8 files changed, 37 insertions(+), 33 deletions(-) diff --git a/diff_multi_bench_output.js b/diff_multi_bench_output.js index 23cb3ada215..0aff103198c 100755 --- a/diff_multi_bench_output.js +++ b/diff_multi_bench_output.js @@ -49,7 +49,7 @@ before_lines.forEach(function(b, i) { var isNaN = !num && num !== 0; return !isNaN; }); - if (ops.length != 2) return + if (ops.length != 2) return; var delta = ops[1] - ops[0]; var pct = ((delta / ops[0]) * 100).toPrecision(3); diff --git a/examples/extend.js b/examples/extend.js index b3a10fa7161..22566082d46 100644 --- a/examples/extend.js +++ b/examples/extend.js @@ -16,7 +16,7 @@ redis.RedisClient.prototype.parse_info = function (callback) { obj[parts[0]] = parts[1]; } }); - callback(obj) + callback(obj); }); }; diff --git a/examples/file.js b/examples/file.js index 91970198585..620b0a10550 100644 --- a/examples/file.js +++ b/examples/file.js @@ -13,9 +13,9 @@ var redis = require("redis"), // Read a file from fs, store it in Redis, get it back from Redis, write it back to fs. fs.readFile(filename, function (err, data) { - if (err) throw err + if (err) throw err; console.log("Read " + data.length + " bytes from filesystem."); - + client.set(filename, data, redis.print); // set entire file client.get(filename, function (err, reply) { // get entire file if (err) { @@ -23,7 +23,7 @@ fs.readFile(filename, function (err, data) { } else { fs.writeFile("duplicate_" + filename, reply, function (err) { if (err) { - console.log("Error on write: " + err) + console.log("Error on write: " + err); } else { console.log("File written."); } diff --git a/examples/unix_socket.js b/examples/unix_socket.js index 4a5e0bb0e84..460b78e857c 100644 --- a/examples/unix_socket.js +++ b/examples/unix_socket.js @@ -1,9 +1,11 @@ +'use strict'; + var redis = require("redis"), client = redis.createClient("/tmp/redis.sock"), profiler = require("v8-profiler"); client.on("connect", function () { - console.log("Got Unix socket connection.") + console.log("Got Unix socket connection."); }); client.on("error", function (err) { diff --git a/index.js b/index.js index 6c70a251297..bdc2889b9a5 100644 --- a/index.js +++ b/index.js @@ -17,10 +17,10 @@ var net = require("net"), // can set this to true to enable for all connections exports.debug_mode = false; -var arraySlice = Array.prototype.slice +var arraySlice = Array.prototype.slice; function trace() { if (!exports.debug_mode) return; - console.log.apply(null, arraySlice.call(arguments)) + console.log.apply(null, arraySlice.call(arguments)); } // hiredis might not be installed @@ -141,7 +141,7 @@ RedisClient.prototype.unref = function () { trace("Not connected yet, will unref later"); this.once("connect", function () { this.unref(); - }) + }); } }; @@ -219,7 +219,7 @@ RedisClient.prototype.do_auth = function () { }, 2000); // TODO - magic number alert return; } else if (err.toString().match("no password is set")) { - console.log("Warning: Redis server does not require a password, but a password was supplied.") + console.log("Warning: Redis server does not require a password, but a password was supplied."); err = null; res = "OK"; } else { @@ -1265,7 +1265,7 @@ var createClient_unix = function(path, options){ redis_client.address = path; return redis_client; -} +}; var createClient_tcp = function (port_arg, host_arg, options) { var cnxOptions = { diff --git a/test/queue-test.js b/test/queue-test.js index 4811eca90ae..dbc9771a0aa 100644 --- a/test/queue-test.js +++ b/test/queue-test.js @@ -11,12 +11,12 @@ module.exports = function (tests, next) { q.push(3); assert.equal(q.length, 2); return next(); - } + }; tests.shift = function () { assert.equal(q.shift(), 'a'); return next(); - } + }; tests.forEach = function () { q.forEach(function (v) { @@ -24,7 +24,7 @@ module.exports = function (tests, next) { }); return next(); - } + }; tests.forEachWithScope = function () { q.forEach(function (v) { @@ -33,5 +33,5 @@ module.exports = function (tests, next) { }, {foo: 'bar'}); return next(); - } -} + }; +}; diff --git a/test/test-unref.js b/test/test-unref.js index d66d77c4338..c7dc9300047 100644 --- a/test/test-unref.js +++ b/test/test-unref.js @@ -1,12 +1,14 @@ -var redis = require("../") +'use strict'; + +var redis = require("../"); //redis.debug_mode = true var PORT = process.argv[2] || 6379; var HOST = process.argv[3] || '127.0.0.1'; -var c = redis.createClient(PORT, HOST) -c.unref() +var c = redis.createClient(PORT, HOST); +c.unref(); c.info(function (err, reply) { - if (err) process.exit(-1) - if (!reply.length) process.exit(-1) - process.stdout.write(reply.length.toString()) -}) + if (err) process.exit(-1); + if (!reply.length) process.exit(-1); + process.stdout.write(reply.length.toString()); +}); diff --git a/test/test.js b/test/test.js index 325ea136587..7bb2966ff8f 100644 --- a/test/test.js +++ b/test/test.js @@ -137,7 +137,7 @@ tests.IPV4 = function () { console.error("client: " + err.stack); process.exit(); }); -} +}; tests.IPV6 = function () { if (!server_version_at_least(client, [2, 8, 0])) { @@ -163,7 +163,7 @@ tests.IPV6 = function () { console.error("client: " + err.stack); process.exit(); }); -} +}; tests.UNIX_SOCKET = function () { var unixClient = redis.createClient('/tmp/redis.sock', { parser: parser }); @@ -189,7 +189,7 @@ tests.UNIX_SOCKET = function () { console.error("client: " + err.stack); process.exit(); }); -} +}; tests.FLUSHDB = function () { var name = "FLUSHDB"; @@ -1543,7 +1543,7 @@ tests.HGETALL_MESSAGE = function () { client.hgetall("msg_test", function (err, obj) { assert.strictEqual(null, err, name + " result sent back unexpected error: " + err); assert.strictEqual(1, Object.keys(obj).length, name); - assert.strictEqual(obj.message, "hello") + assert.strictEqual(obj.message, "hello"); next(name); }); }; @@ -2178,8 +2178,8 @@ tests.ENABLE_OFFLINE_QUEUE_FALSE = function () { // ignore, see above }); assert.throws(function () { - cli.set(name, name) - }) + cli.set(name, name); + }); assert.doesNotThrow(function () { cli.set(name, name, function (err) { // should callback with an error @@ -2205,7 +2205,7 @@ tests.SLOWLOG = function () { client.config("set", "slowlog-log-slower-than", 10000, require_string("OK", name)); next(name); }); -} +}; tests.DOMAIN = function () { var name = "DOMAIN"; @@ -2323,9 +2323,9 @@ tests.unref = function () { var external = fork("./test/test-unref.js"); var done = false; external.on("close", function (code) { - assert(code == 0, "test-unref.js failed"); + assert(code === 0, "test-unref.js failed"); done = true; - }) + }); setTimeout(function () { if (!done) { external.kill(); @@ -2336,7 +2336,7 @@ tests.unref = function () { }; // starting to split tests into multiple files. -require('./queue-test')(tests, next) +require('./queue-test')(tests, next); all_tests = Object.keys(tests); all_start = new Date(); From 6866ff9b1ef4bc9e677f79a46bd1996d51b52448 Mon Sep 17 00:00:00 2001 From: Ruben Bridgewater Date: Wed, 22 Jul 2015 16:19:30 +0200 Subject: [PATCH 0242/1748] Remove trailing whitespace --- benches/buffer_bench.js | 2 +- examples/subquery.js | 2 +- multi_bench.js | 12 ++++++------ 3 files changed, 8 insertions(+), 8 deletions(-) diff --git a/benches/buffer_bench.js b/benches/buffer_bench.js index df16d90bf4a..64347e583f0 100644 --- a/benches/buffer_bench.js +++ b/benches/buffer_bench.js @@ -2,7 +2,7 @@ var source = new Buffer(100), dest = new Buffer(100), i, j, k, tmp, count = 1000000, bytes = 100; - + for (i = 99 ; i >= 0 ; i--) { source[i] = 120; } diff --git a/examples/subquery.js b/examples/subquery.js index 4f1edb0ffde..ffb7eca382e 100644 --- a/examples/subquery.js +++ b/examples/subquery.js @@ -9,7 +9,7 @@ function print_results(obj) { // build a map of all keys and their types client.keys("*", function (err, all_keys) { var key_types = {}; - + all_keys.forEach(function (key, pos) { // use second arg of forEach to get pos client.type(key, function (err, type) { key_types[key] = type; diff --git a/multi_bench.js b/multi_bench.js index 4e99376787e..9f210a76d4e 100644 --- a/multi_bench.js +++ b/multi_bench.js @@ -25,13 +25,13 @@ function lpad(input, len, chr) { metrics.Histogram.prototype.print_line = function () { var obj = this.printObj(); - + return lpad(obj.min, 4) + "/" + lpad(obj.max, 4) + "/" + lpad(obj.mean.toFixed(2), 7) + "/" + lpad(obj.p95.toFixed(2), 7); }; function Test(args) { this.args = args; - + this.callback = null; this.clients = []; this.clients_ready = 0; @@ -58,7 +58,7 @@ Test.prototype.run = function (callback) { Test.prototype.new_client = function (id) { var self = this, new_client; - + new_client = redis.createClient(6379, "127.0.0.1", this.client_options); new_client.create_time = Date.now(); @@ -97,7 +97,7 @@ Test.prototype.fill_pipeline = function () { pipeline++; this.send_next(); } - + if (this.commands_completed === this.num_requests) { this.print_stats(); this.stop_clients(); @@ -106,7 +106,7 @@ Test.prototype.fill_pipeline = function () { Test.prototype.stop_clients = function () { var self = this; - + this.clients.forEach(function (client, pos) { if (pos === self.clients.length - 1) { client.quit(function (err, res) { @@ -135,7 +135,7 @@ Test.prototype.send_next = function () { Test.prototype.print_stats = function () { var duration = Date.now() - this.test_start; - + console.log("min/max/avg/p95: " + this.command_latency.print_line() + " " + lpad(duration, 6) + "ms total, " + lpad((this.num_requests / (duration / 1000)).toFixed(2), 8) + " ops/sec"); }; From 745fccebfb9e8ac264ad5084944166b493576a91 Mon Sep 17 00:00:00 2001 From: Ruben Bridgewater Date: Wed, 22 Jul 2015 16:19:46 +0200 Subject: [PATCH 0243/1748] Add var statements to undeclared variables and remove unused variables --- benches/stress/codec.js | 10 ---------- diff_multi_bench_output.js | 4 ++-- 2 files changed, 2 insertions(+), 12 deletions(-) diff --git a/benches/stress/codec.js b/benches/stress/codec.js index c679a6ee016..5a3cc6daa4b 100644 --- a/benches/stress/codec.js +++ b/benches/stress/codec.js @@ -5,14 +5,4 @@ var json = { decode: JSON.parse }; -var MsgPack = require('node-msgpack'); -msgpack = { - encode: MsgPack.pack, - decode: function(str) { return MsgPack.unpack(new Buffer(str)); } -}; - -bison = require('bison'); - module.exports = json; -//module.exports = msgpack; -//module.exports = bison; diff --git a/diff_multi_bench_output.js b/diff_multi_bench_output.js index 0aff103198c..8eab0a2c690 100755 --- a/diff_multi_bench_output.js +++ b/diff_multi_bench_output.js @@ -37,8 +37,8 @@ before_lines.forEach(function(b, i) { return; } - b_words = b.split(' ').filter(is_whitespace); - a_words = a.split(' ').filter(is_whitespace); + var b_words = b.split(' ').filter(is_whitespace); + var a_words = a.split(' ').filter(is_whitespace); var ops = [b_words, a_words] From 0908e9a46bb1c681410d63fa00da3c9a6b703f43 Mon Sep 17 00:00:00 2001 From: Ruben Bridgewater Date: Wed, 22 Jul 2015 16:27:05 +0200 Subject: [PATCH 0244/1748] Use console.log instead of deprecated util.print util.print has been deprecated in node --- test/test.js | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/test/test.js b/test/test.js index 7bb2966ff8f..5bcc32fd516 100644 --- a/test/test.js +++ b/test/test.js @@ -2345,7 +2345,7 @@ test_count = 0; run_next_test = function run_next_test() { var test_name = all_tests.shift(); if (typeof tests[test_name] === "function") { - util.print('- \x1b[1m' + test_name.toLowerCase() + '\x1b[0m:'); + console.log('- \x1b[1m' + test_name.toLowerCase() + '\x1b[0m:'); cur_start = new Date(); test_count += 1; tests[test_name](); From 01a377383bc97522665059ec85b71985d4e2f76e Mon Sep 17 00:00:00 2001 From: Ruben Bridgewater Date: Wed, 22 Jul 2015 16:43:01 +0200 Subject: [PATCH 0245/1748] Use type safe comparison --- diff_multi_bench_output.js | 4 ++-- test/test.js | 10 +++++----- 2 files changed, 7 insertions(+), 7 deletions(-) diff --git a/diff_multi_bench_output.js b/diff_multi_bench_output.js index 8eab0a2c690..89b6be479c9 100755 --- a/diff_multi_bench_output.js +++ b/diff_multi_bench_output.js @@ -49,7 +49,7 @@ before_lines.forEach(function(b, i) { var isNaN = !num && num !== 0; return !isNaN; }); - if (ops.length != 2) return; + if (ops.length !== 2) return; var delta = ops[1] - ops[0]; var pct = ((delta / ops[0]) * 100).toPrecision(3); @@ -60,7 +60,7 @@ before_lines.forEach(function(b, i) { pct = humanize_diff(pct, '%'); console.log( // name of test - command_name(a_words) == command_name(b_words) + command_name(a_words) === command_name(b_words) ? command_name(a_words) + ':' : '404:', // results of test diff --git a/test/test.js b/test/test.js index 5bcc32fd516..ffb097bda47 100644 --- a/test/test.js +++ b/test/test.js @@ -204,7 +204,7 @@ tests.FLUSHDB = function () { tests.INCR = function () { var name = "INCR"; - if (bclient.reply_parser.name == "hiredis") { + if (bclient.reply_parser.name === "hiredis") { console.log("Skipping INCR buffer test with hiredis"); return next(name); } @@ -371,7 +371,7 @@ tests.MULTI_6 = function () { tests.MULTI_7 = function () { var name = "MULTI_7"; - if (bclient.reply_parser.name != "javascript") { + if (bclient.reply_parser.name !== "javascript") { console.log("Skipping wire-protocol test for 3rd-party parser"); return next(name); } @@ -467,7 +467,7 @@ tests.FWD_ERRORS_1 = function () { client3.on("message", function (channel, data) { console.log("incoming"); - if (channel == name) { + if (channel === name) { assert.equal(data, "Some message"); throw toThrow; } @@ -1286,11 +1286,11 @@ tests.SUBSCRIBE_CLOSE_RESUBSCRIBE = function () { console.log("c1 is ready", count); count++; - if (count == 1) { + if (count === 1) { c2.publish("chan1", "hi on channel 1"); return; - } else if (count == 2) { + } else if (count === 2) { c2.publish("chan2", "hi on channel 2"); } else { From b77a48739a609366e831235621311c3253cd2227 Mon Sep 17 00:00:00 2001 From: Almog Melamed Date: Thu, 23 Jul 2015 09:35:42 +0300 Subject: [PATCH 0246/1748] Update changelog.md: Binary not Binay --- changelog.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/changelog.md b/changelog.md index 7cdd7fd2636..484b58b8567 100644 --- a/changelog.md +++ b/changelog.md @@ -25,7 +25,7 @@ Changelog ## v0.10.2 - May 18, 2014 -* Better binay key handling for HGETALL. (Nick Apperson) +* Better binary key handling for HGETALL. (Nick Apperson) * Fix test not resetting `error` handler. (CrypticSwarm) * Fix SELECT error semantics. (Bryan English) From d34dcc8b07021748bfaa4fa295601bd14c506129 Mon Sep 17 00:00:00 2001 From: Hans Kristian Flaatten Date: Thu, 23 Jul 2015 22:14:51 +0200 Subject: [PATCH 0247/1748] Check for REDIS HOST and PORT environment varibles --- test/test.js | 10 ++++++---- 1 file changed, 6 insertions(+), 4 deletions(-) diff --git a/test/test.js b/test/test.js index e7db7639142..638747303fa 100644 --- a/test/test.js +++ b/test/test.js @@ -1,6 +1,6 @@ /*global require console setTimeout process Buffer */ -var PORT = 6379; -var HOST = '127.0.0.1'; +var PORT = process.env.REDIS_1_PORT_6379_TCP_PORT || 6379; +var HOST = process.env.REDIS_1_PORT_6379_TCP_ADDR || '127.0.0.1'; var parser = process.argv[3]; var redis = require("../index"), @@ -116,7 +116,8 @@ next = function next(name) { // Tests are run in the order they are defined, so FLUSHDB should always be first. tests.IPV4 = function () { - var ipv4Client = redis.createClient( PORT, "127.0.0.1", { family : "IPv4", parser: parser } ); + var ipv4addr = process.env.REDIS_1_PORT_6379_TCP_ADDR || "127.0.0.1"; + var ipv4Client = redis.createClient( PORT, ipv4addr, { family : "IPv4", parser: parser } ); ipv4Client.once("ready", function start_tests() { console.log("Connected to " + ipv4Client.address + ", Redis server version " + ipv4Client.server_info.redis_version + "\n"); @@ -142,7 +143,8 @@ tests.IPV6 = function () { console.log("Skipping IPV6 for old Redis server version < 2.8.0"); return run_next_test(); } - var ipv6Client = redis.createClient( PORT, "::1", { family: "IPv6", parser: parser } ); + var ipv6addr = process.env.REDIS_1_PORT_6379_TCP_ADDR || "::1"; + var ipv6Client = redis.createClient( PORT, ipv6addr, { family: "IPv6", parser: parser } ); ipv6Client.once("ready", function start_tests() { console.log("Connected to " + ipv6Client.address + ", Redis server version " + ipv6Client.server_info.redis_version + "\n"); From e646b4eb3970df11572163a74728c2852452bfea Mon Sep 17 00:00:00 2001 From: Hans Kristian Flaatten Date: Thu, 23 Jul 2015 22:16:07 +0200 Subject: [PATCH 0248/1748] Skip SOCKET test if Redis is not listening on a socket --- test/test.js | 9 +++++++++ 1 file changed, 9 insertions(+) diff --git a/test/test.js b/test/test.js index 638747303fa..8344d14b5f9 100644 --- a/test/test.js +++ b/test/test.js @@ -166,6 +166,15 @@ tests.IPV6 = function () { } tests.UNIX_SOCKET = function () { + try { + var stat = require('fs').accessSync('/tmp/redis.sock'); + } catch(err) { + if (err.code === 'ENOENT') { + console.log("Skipping SOCKET since none exists"); + return run_next_test(); + } + } + var unixClient = redis.createClient('/tmp/redis.sock', { parser: parser }); // if this fails, check the permission of unix socket. From 8b5ce1088a555174d2202a8852a7ecae6f969a3c Mon Sep 17 00:00:00 2001 From: Hans Kristian Flaatten Date: Thu, 23 Jul 2015 22:16:48 +0200 Subject: [PATCH 0249/1748] Allways pass HOST and PORT when creating a new client --- test/test.js | 18 +++++++++--------- 1 file changed, 9 insertions(+), 9 deletions(-) diff --git a/test/test.js b/test/test.js index 8344d14b5f9..955af604b47 100644 --- a/test/test.js +++ b/test/test.js @@ -739,7 +739,7 @@ tests.WATCH_TRANSACTION = function () { tests.detect_buffers = function () { - var name = "detect_buffers", detect_client = redis.createClient({ detect_buffers: true, parser: parser }); + var name = "detect_buffers", detect_client = redis.createClient(PORT, HOST, { detect_buffers: true, parser: parser }); detect_client.on("ready", function () { // single Buffer or String @@ -804,7 +804,7 @@ tests.detect_buffers = function () { }; tests.detect_buffers_multi = function () { - var name = "detect_buffers_multi", detect_client = redis.createClient({detect_buffers: true}); + var name = "detect_buffers_multi", detect_client = redis.createClient(PORT, HOST, {detect_buffers: true}); detect_client.on("ready", function () { // single Buffer or String @@ -895,9 +895,9 @@ tests.detect_buffers_multi = function () { tests.socket_nodelay = function () { var name = "socket_nodelay", c1, c2, c3, ready_count = 0, quit_count = 0; - c1 = redis.createClient({ socket_nodelay: true, parser: parser }); - c2 = redis.createClient({ socket_nodelay: false, parser: parser }); - c3 = redis.createClient({ parser: parser }); + c1 = redis.createClient(PORT, HOST, { socket_nodelay: true, parser: parser }); + c2 = redis.createClient(PORT, HOST, { socket_nodelay: false, parser: parser }); + c3 = redis.createClient(PORT, HOST, { parser: parser }); function quit_check() { quit_count++; @@ -1258,8 +1258,8 @@ tests.SUBSCRIBE_QUIT = function () { tests.SUBSCRIBE_CLOSE_RESUBSCRIBE = function () { var name = "SUBSCRIBE_CLOSE_RESUBSCRIBE"; - var c1 = redis.createClient({ parser: parser }); - var c2 = redis.createClient({ parser: parser }); + var c1 = redis.createClient(PORT, HOST, { parser: parser }); + var c2 = redis.createClient(PORT, HOST, { parser: parser }); var count = 0; /* Create two clients. c1 subscribes to two channels, c2 will publish to them. @@ -2055,7 +2055,7 @@ tests.MONITOR = function () { return next(name); } - monitor_client = redis.createClient({ parser: parser }); + monitor_client = redis.createClient(PORT, HOST, { parser: parser }); monitor_client.monitor(function (err, res) { client.mget("some", "keys", "foo", "bar"); client.set("json", JSON.stringify({ @@ -2329,7 +2329,7 @@ tests.reconnectRetryMaxDelay = function() { tests.unref = function () { var name = "unref"; - var external = fork("./test/test-unref.js"); + var external = fork("./test/test-unref.js", [PORT, HOST]); var done = false; external.on("close", function (code) { assert(code == 0, "test-unref.js failed"); From da535a697768a8f8617544d3a37adb754c3d5f8b Mon Sep 17 00:00:00 2001 From: Hans Kristian Flaatten Date: Fri, 24 Jul 2015 08:07:33 +0200 Subject: [PATCH 0250/1748] Normalize linked REDIS environment variables --- test/test.js | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/test/test.js b/test/test.js index 955af604b47..a4c79e4f3b3 100644 --- a/test/test.js +++ b/test/test.js @@ -1,6 +1,6 @@ /*global require console setTimeout process Buffer */ -var PORT = process.env.REDIS_1_PORT_6379_TCP_PORT || 6379; -var HOST = process.env.REDIS_1_PORT_6379_TCP_ADDR || '127.0.0.1'; +var PORT = process.env.REDIS_PORT_6379_TCP_PORT || 6379; +var HOST = process.env.REDIS_PORT_6379_TCP_ADDR || '127.0.0.1'; var parser = process.argv[3]; var redis = require("../index"), @@ -116,7 +116,7 @@ next = function next(name) { // Tests are run in the order they are defined, so FLUSHDB should always be first. tests.IPV4 = function () { - var ipv4addr = process.env.REDIS_1_PORT_6379_TCP_ADDR || "127.0.0.1"; + var ipv4addr = process.env.REDIS_PORT_6379_TCP_ADDR || "127.0.0.1"; var ipv4Client = redis.createClient( PORT, ipv4addr, { family : "IPv4", parser: parser } ); ipv4Client.once("ready", function start_tests() { @@ -143,7 +143,7 @@ tests.IPV6 = function () { console.log("Skipping IPV6 for old Redis server version < 2.8.0"); return run_next_test(); } - var ipv6addr = process.env.REDIS_1_PORT_6379_TCP_ADDR || "::1"; + var ipv6addr = process.env.REDIS_PORT_6379_TCP_ADDR || "::1"; var ipv6Client = redis.createClient( PORT, ipv6addr, { family: "IPv6", parser: parser } ); ipv6Client.once("ready", function start_tests() { From b1ae21d221fd96d34e0a347630df31c0afd380a6 Mon Sep 17 00:00:00 2001 From: Hans Kristian Flaatten Date: Fri, 24 Jul 2015 15:33:37 +0200 Subject: [PATCH 0251/1748] Revert "Skip SOCKET test if Redis is not listening on a socket" This reverts commit e646b4eb3970df11572163a74728c2852452bfea. --- test/test.js | 9 --------- 1 file changed, 9 deletions(-) diff --git a/test/test.js b/test/test.js index a4c79e4f3b3..48dd86a56be 100644 --- a/test/test.js +++ b/test/test.js @@ -166,15 +166,6 @@ tests.IPV6 = function () { } tests.UNIX_SOCKET = function () { - try { - var stat = require('fs').accessSync('/tmp/redis.sock'); - } catch(err) { - if (err.code === 'ENOENT') { - console.log("Skipping SOCKET since none exists"); - return run_next_test(); - } - } - var unixClient = redis.createClient('/tmp/redis.sock', { parser: parser }); // if this fails, check the permission of unix socket. From 2b442450568744b7585d87483a449a09c29048af Mon Sep 17 00:00:00 2001 From: Erin Spiceland Date: Sun, 12 Jul 2015 13:31:13 -0500 Subject: [PATCH 0252/1748] Add example test with grunt and mocha. Add test for reconnect. Run each test for both parsers and both IP versions. Don't save a reference to this nodified assertion function. Add DEBUG env var which enables extra debug logging in node_redis. Remove Grunt, run Redis on 6378 for non-interference. Remove the tests already ported to Mocha. Port reconnect after pubsub test; add subscribed after reconnect test. Reconnet after pubsub test confused me. I don't think it tested anything, and it didn't pass for me after I ported it. I've disabled it and added a note. In its place, I've added a test to make sure we are still subscribed and can receive pubsub messages after a reconnect. Move test suite config-like stuff into a library. Move test suite createClient args generation into the config library. WIP. Some select tests, most disabled and still WIP. --- package.json | 2 + run-bootstrapped-mocha.js | 28 ++++++ test/conf/redis.conf | 5 + test/lib/config.js | 30 ++++++ test/lib/nodeify-assertions.js | 60 ++++++++++++ test/lib/redis-process.js | 23 +++++ test/mocha/connecting.spec.js | 165 +++++++++++++++++++++++++++++++++ test/mocha/select.spec.js | 130 ++++++++++++++++++++++++++ test/test.js | 46 +-------- 9 files changed, 444 insertions(+), 45 deletions(-) create mode 100644 run-bootstrapped-mocha.js create mode 100644 test/conf/redis.conf create mode 100644 test/lib/config.js create mode 100644 test/lib/nodeify-assertions.js create mode 100644 test/lib/redis-process.js create mode 100644 test/mocha/connecting.spec.js create mode 100644 test/mocha/select.spec.js diff --git a/package.json b/package.json index 495091bbea8..b5118d85de9 100644 --- a/package.json +++ b/package.json @@ -14,10 +14,12 @@ "test": "nyc ./test/run.sh" }, "devDependencies": { + "async": "^1.3.0", "colors": "~0.6.0-1", "coveralls": "^2.11.2", "hiredis": "^0.4.0", "metrics": ">=0.1.5", + "mocha": "^2.2.5", "nyc": "^3.0.0", "underscore": "~1.4.4" }, diff --git a/run-bootstrapped-mocha.js b/run-bootstrapped-mocha.js new file mode 100644 index 00000000000..716b7c0708f --- /dev/null +++ b/run-bootstrapped-mocha.js @@ -0,0 +1,28 @@ +var pm = require('./test/lib/redis-process'); +var cp = require('child_process'); +var testSets = 'test/mocha/**/*.spec.js'; +var async = require('async'); +var redis; + +process.on("exit", function () { + if (redis) { + redis.stop(); + } +}); + +async.series([function startRedis(next) { + redis = pm.start(function (err) { + next(err); + }); +}, function runMocha(next) { + var mocha = cp.spawn('mocha', [testSets], { + stdio: "inherit" + }); + mocha.on("exit", function (code) { + next(); + }); +}, function stopRedis(next) { + redis.stop(next); +}], function (err) { + // done; +}); diff --git a/test/conf/redis.conf b/test/conf/redis.conf new file mode 100644 index 00000000000..1e900d9787b --- /dev/null +++ b/test/conf/redis.conf @@ -0,0 +1,5 @@ +daemonize yes +port 6378 +bind ::1 +unixsocket /tmp/redis.sock +unixsocketperm 755 diff --git a/test/lib/config.js b/test/lib/config.js new file mode 100644 index 00000000000..475f2733ceb --- /dev/null +++ b/test/lib/config.js @@ -0,0 +1,30 @@ +module.exports = (function () { + var redis = require('../../index'); + redis.debug_mode = process.env.DEBUG ? JSON.parse(process.env.DEBUG) : false; + + var config = { + redis: redis, + PORT: 6378, + HOST: { + IPv4: "127.0.0.1", + IPv6: "::1" + } + }; + + config.configureClient = function (parser, ip, isSocket) { + var args = []; + + if (!isSocket) { + args.push(config.PORT); + args.push(config.HOST[ip]); + args.push({ family: ip, parser: parser }); + } else { + args.push(ip); + args.push({ parser: parser }); + } + + return args; + }; + + return config; +})(); diff --git a/test/lib/nodeify-assertions.js b/test/lib/nodeify-assertions.js new file mode 100644 index 00000000000..c80cccb8e5b --- /dev/null +++ b/test/lib/nodeify-assertions.js @@ -0,0 +1,60 @@ +var assert = require('assert'); + +module.exports = { + isNumber: function (expected) { + return function (err, results) { + assert.strictEqual(null, err, "expected " + expected + ", got error: " + err); + assert.strictEqual(expected, results, expected + " !== " + results); + assert.strictEqual(typeof results, "number", "expected a number, got " + typeof results); + return true; + }; + }, + + isString: function (str) { + return function (err, results) { + assert.strictEqual(null, err, "expected string '" + str + "', got error: " + err); + assert.equal(str, results, str + " does not match " + results); + return true; + }; + }, + + isNull: function () { + return function (err, results) { + assert.strictEqual(null, err, "expected null, got error: " + err); + assert.strictEqual(null, results, results + " is not null"); + return true; + }; + }, + + isError: function () { + return function (err, results) { + assert.notEqual(err, null, "err is null, but an error is expected here."); + return true; + }; + }, + + isNotError: function () { + return function (err, results) { + assert.strictEqual(err, null, "expected success, got an error: " + err); + return true; + }; + }, + + isType: { + number: function () { + return function (err, results) { + assert.strictEqual(null, err, "expected any number, got error: " + err); + assert.strictEqual(typeof results, "number", results + " is not a number"); + return true; + }; + }, + + positiveNumber: function () { + return function (err, results) { + assert.strictEqual(null, err, "expected positive number, got error: " + err); + assert.strictEqual(true, (results > 0), results + " is not a positive number"); + return true; + }; + } + } +}; diff --git a/test/lib/redis-process.js b/test/lib/redis-process.js new file mode 100644 index 00000000000..a77d20bcc84 --- /dev/null +++ b/test/lib/redis-process.js @@ -0,0 +1,23 @@ +var cp = require('child_process'); + +module.exports = { + start: function (done, isSocket) { + var confFile = isSocket ? "test/conf/redis-socket.conf" : "test/conf/redis.conf"; + var redis = cp.spawn("redis-server", [confFile]); + + redis.once('err', done); + setTimeout(function (data) { + redis.removeListener('err', done); + done(); + }, 1000); + + return { + stop: function (done) { + redis.once("exit", function () { + done(); + }); + redis.kill("SIGINT"); + } + }; + } +}; diff --git a/test/mocha/connecting.spec.js b/test/mocha/connecting.spec.js new file mode 100644 index 00000000000..dd46f56f59d --- /dev/null +++ b/test/mocha/connecting.spec.js @@ -0,0 +1,165 @@ +var nodeAssert = require("../lib/nodeify-assertions"); +var config = require("../lib/config"); +var redis = config.redis; +var async = require("async"); + +describe("A node_redis client", function () { + function allTests(parser, ip, isSocket) { + var args = config.configureClient(parser, ip, isSocket); + + describe("using " + parser + " and " + ip, function () { + var client; + + after(function () { + client.end(); + }); + + it("connects correctly", function (done) { + client = redis.createClient.apply(redis.createClient, args); + client.on("error", done); + + client.once("ready", function () { + client.removeListener("error", done); + client.get("recon 1", function (err, res) { + done(err); + }); + }); + }); + }); + + describe("when connected", function () { + var client; + + beforeEach(function (done) { + client = redis.createClient.apply(redis.createClient, args); + client.once("error", function onError(err) { + done(err); + }); + client.once("ready", function onReady() { + done(); + }); + }); + + afterEach(function () { + client.end(); + }); + + describe("when redis closes unexpectedly", function () { + it("reconnects and can retrieve the pre-existing data", function (done) { + client.on("reconnecting", function on_recon(params) { + client.on("connect", function on_connect() { + async.parallel([function (cb) { + client.get("recon 1", function (err, res) { + nodeAssert.isString("one")(err, res); + cb(); + }); + }, function (cb) { + client.get("recon 1", function (err, res) { + nodeAssert.isString("one")(err, res); + cb(); + }); + }, function (cb) { + client.get("recon 2", function (err, res) { + nodeAssert.isString("two")(err, res); + cb(); + }); + }, function (cb) { + client.get("recon 2", function (err, res) { + nodeAssert.isString("two")(err, res); + cb(); + }); + }], function (err, results) { + client.removeListener("connect", on_connect); + client.removeListener("reconnecting", on_recon); + done(err); + }); + }); + }); + + client.set("recon 1", "one"); + client.set("recon 2", "two", function (err, res) { + // Do not do this in normal programs. This is to simulate the server closing on us. + // For orderly shutdown in normal programs, do client.quit() + client.stream.destroy(); + }); + }); + + describe("and it's subscribed to a channel", function () { + // reconnect_select_db_after_pubsub + // Does not pass. + // "Connection in subscriber mode, only subscriber commands may be used" + xit("reconnects, unsubscribes, and can retrieve the pre-existing data", function (done) { + client.on("reconnecting", function on_recon(params) { + client.on("ready", function on_connect() { + async.parallel([function (cb) { + client.unsubscribe("recon channel", function (err, res) { + nodeAssert.isNotError()(err, res); + cb(); + }); + }, function (cb) { + client.get("recon 1", function (err, res) { + nodeAssert.isString("one")(err, res); + cb(); + }); + }], function (err, results) { + client.removeListener("connect", on_connect); + client.removeListener("reconnecting", on_recon); + done(err); + }); + }); + }); + + client.set("recon 1", "one"); + client.subscribe("recon channel", function (err, res) { + // Do not do this in normal programs. This is to simulate the server closing on us. + // For orderly shutdown in normal programs, do client.quit() + client.stream.destroy(); + }); + }); + + it("remains subscribed", function () { + var client2 = redis.createClient.apply(redis.createClient, args); + + client.on("reconnecting", function on_recon(params) { + client.on("ready", function on_connect() { + async.parallel([function (cb) { + client.on("message", function (channel, message) { + try { + nodeAssert.isString("recon channel")(null, channel); + nodeAssert.isString("a test message")(null, message); + } catch (err) { + cb(err); + } + }); + + client2.subscribe("recon channel", function (err, res) { + if (err) { + cb(err); + return; + } + client2.publish("recon channel", "a test message"); + }); + }], function (err, results) { + done(err); + }); + }); + }); + + client.subscribe("recon channel", function (err, res) { + // Do not do this in normal programs. This is to simulate the server closing on us. + // For orderly shutdown in normal programs, do client.quit() + client.stream.destroy(); + }); + }); + }); + }); + }); + } + + ['javascript', 'hiredis'].forEach(function (parser) { + allTests(parser, "/tmp/redis.sock", true); + ['IPv4', 'IPv6'].forEach(function (ip) { + allTests(parser, ip); + }) + }); +}); diff --git a/test/mocha/select.spec.js b/test/mocha/select.spec.js new file mode 100644 index 00000000000..418ca4439f6 --- /dev/null +++ b/test/mocha/select.spec.js @@ -0,0 +1,130 @@ +var nodeAssert = require("../lib/nodeify-assertions"); +var config = require("../lib/config"); +var redis = config.redis; +var async = require("async"); +var assert = require("assert"); + +describe("The 'select' method", function () { + function allTests(parser, ip, isSocket) { + var args = config.configureClient(parser, ip, isSocket); + + describe("using " + parser + " and " + ip, function () { + describe("when not connected", function () { + var client; + + beforeEach(function (done) { + client = redis.createClient.apply(redis.createClient, args); + client.once("error", done); + + client.once("connect", function () { + client.set("doot", "good calsum", function (err, res) { + client.end(); + done(); + }); + }); + }); + + it("doesn't even throw an error or call the callback at all WTF", function (done) { + this.timeout(50); + + client.select(1, function (err, res) { + nodeAssert.isNotError()(err, res); + done(); + }); + + setTimeout(function () { + done(); + }, 45); + }); + }); + + describe("when connected", function () { + var client; + + beforeEach(function (done) { + client = redis.createClient.apply(redis.createClient, args); + client.once("error", done); + + client.once("connect", function () { + done(); + }); + }); + + afterEach(function () { + client.end(); + }); + + it("changes the database and calls the callback", function (done) { + // default value of null means database 0 will be used. + assert.strictEqual(client.selected_db, null, "default db should be null"); + client.select(1, function (err, res) { + nodeAssert.isNotError()(err, res); + assert.strictEqual(client.selected_db, 1, "db should be 1 after select"); + done(); + }); + }); + + describe("and no callback is specified", function () { + // select_error_emits_if_no_callback + // this is another test that was testing the wrong thing. The old test did indeed emit an error, + // but not because of the lacking callback, but because 9999 was an invalid db index. + describe("with a valid db index", function () { + it("works just fine and does not actually emit an error like the old tests assert WTF", function (done) { + assert.strictEqual(client.selected_db, null, "default db should be null"); + this.timeout(50); + client.on("error", function (err) { + nodeAssert.isNotError()(err); + assert.strictEqual(client.selected_db, 1, "db should be 1 after select"); + done(new Error("the old tests were crap")); + }); + client.select(1); + + setTimeout(function () { + done(); + }, 45); + }); + }); + + // Can't seem to catch the errors thrown here. + xdescribe("with an invalid db index", function () { + it("emits an error", function (done) { + this.timeout(50); + + assert.strictEqual(client.selected_db, null, "default db should be null"); + client.on("error", function (err) { + console.log('got an error', err); + done(); + }); + + try { + client.select(9999); + } catch (err) {} + + setTimeout(function () { + done(new Error("It was supposed to emit an error.")); + }, 45); + }); + + it("throws an error bc a callback is not", function (done) { + assert.strictEqual(client.selected_db, null, "default db should be null"); + try { + client.select(9999); + done(new Error("Was supposed to throw an invalid db index error.")); + } catch (err) { + done(); + } + }); + }); + }); + }); + }); + } + + ['javascript', 'hiredis'].forEach(function (parser) { + //allTests(parser, "/tmp/redis.sock", true); + //['IPv4', 'IPv6'].forEach(function (ip) { + ['IPv4'].forEach(function (ip) { + allTests(parser, ip); + }) + }); +}); diff --git a/test/test.js b/test/test.js index fec760923d1..9142a144cab 100644 --- a/test/test.js +++ b/test/test.js @@ -117,6 +117,7 @@ next = function next(name) { // Tests are run in the order they are defined, so FLUSHDB should always be first. +<<<<<<< HEAD tests.IPV4 = function () { var ipv4addr = process.env.REDIS_PORT_6379_TCP_ADDR || "127.0.0.1"; var ipv4Client = redis.createClient( PORT, ipv4addr, { family : "IPv4", parser: parser } ); @@ -937,51 +938,6 @@ tests.socket_nodelay = function () { c3.on("ready", ready_check); }; -tests.reconnect = function () { - var name = "reconnect"; - - client.set("recon 1", "one"); - client.set("recon 2", "two", function (err, res) { - // Do not do this in normal programs. This is to simulate the server closing on us. - // For orderly shutdown in normal programs, do client.quit() - client.stream.destroy(); - }); - - client.on("reconnecting", function on_recon(params) { - client.on("connect", function on_connect() { - client.select(test_db_num, require_string("OK", name)); - client.get("recon 1", require_string("one", name)); - client.get("recon 1", require_string("one", name)); - client.get("recon 2", require_string("two", name)); - client.get("recon 2", require_string("two", name)); - client.removeListener("connect", on_connect); - client.removeListener("reconnecting", on_recon); - next(name); - }); - }); -}; - -tests.reconnect_select_db_after_pubsub = function() { - var name = "reconnect_select_db_after_pubsub"; - - client.select(test_db_num); - client.set(name, "one"); - client.subscribe('ChannelV', function (err, res) { - client.stream.destroy(); - }); - - client.on("reconnecting", function on_recon(params) { - client.on("ready", function on_connect() { - client.unsubscribe('ChannelV', function (err, res) { - client.get(name, require_string("one", name)); - client.removeListener("connect", on_connect); - client.removeListener("reconnecting", on_recon); - next(name); - }); - }); - }); -}; - tests.select_error_emits_if_no_callback = function () { var prev = client.listeners("error")[0]; client.removeListener("error", prev); From 5da083322df84d5078467d32b0c055639ee74621 Mon Sep 17 00:00:00 2001 From: Benjamin Coe Date: Mon, 13 Jul 2015 23:00:37 -0700 Subject: [PATCH 0253/1748] fixed up some tests, changed how redis spawns --- .travis.yml | 5 +- package.json | 4 +- run-bootstrapped-mocha.js | 28 --------- test/conf/redis.conf | 3 +- test/lib/redis-process.js | 49 ++++++++++----- test/mocha/connecting.spec.js | 24 ++++++-- test/mocha/select.spec.js | 112 ++++++++++++++++------------------ 7 files changed, 109 insertions(+), 116 deletions(-) delete mode 100644 run-bootstrapped-mocha.js diff --git a/.travis.yml b/.travis.yml index e7ed30fd796..4884f204e13 100644 --- a/.travis.yml +++ b/.travis.yml @@ -4,8 +4,5 @@ node_js: - "0.10" - "0.12" - "iojs" -before_install: - - 'printf ''bind ::1 127.0.0.1\nunixsocket /tmp/redis.sock\ndaemonize yes\nunixsocketperm 777'' >> /tmp/redis.conf' -before_script: - - sudo redis-server /tmp/redis.conf +script: npm run mocha after_success: npm run coverage diff --git a/package.json b/package.json index b5118d85de9..7f9b320b785 100644 --- a/package.json +++ b/package.json @@ -11,7 +11,8 @@ "main": "./index.js", "scripts": { "coverage": "nyc report --reporter=text-lcov | coveralls", - "test": "nyc ./test/run.sh" + "test": "nyc ./test/run.sh", + "mocha": "nyc mocha ./test/mocha/*.js" }, "devDependencies": { "async": "^1.3.0", @@ -21,6 +22,7 @@ "metrics": ">=0.1.5", "mocha": "^2.2.5", "nyc": "^3.0.0", + "tcp-port-used": "^0.1.2", "underscore": "~1.4.4" }, "repository": { diff --git a/run-bootstrapped-mocha.js b/run-bootstrapped-mocha.js deleted file mode 100644 index 716b7c0708f..00000000000 --- a/run-bootstrapped-mocha.js +++ /dev/null @@ -1,28 +0,0 @@ -var pm = require('./test/lib/redis-process'); -var cp = require('child_process'); -var testSets = 'test/mocha/**/*.spec.js'; -var async = require('async'); -var redis; - -process.on("exit", function () { - if (redis) { - redis.stop(); - } -}); - -async.series([function startRedis(next) { - redis = pm.start(function (err) { - next(err); - }); -}, function runMocha(next) { - var mocha = cp.spawn('mocha', [testSets], { - stdio: "inherit" - }); - mocha.on("exit", function (code) { - next(); - }); -}, function stopRedis(next) { - redis.stop(next); -}], function (err) { - // done; -}); diff --git a/test/conf/redis.conf b/test/conf/redis.conf index 1e900d9787b..dd9a4b6572c 100644 --- a/test/conf/redis.conf +++ b/test/conf/redis.conf @@ -1,5 +1,4 @@ -daemonize yes port 6378 -bind ::1 +bind ::1 127.0.0.1 unixsocket /tmp/redis.sock unixsocketperm 755 diff --git a/test/lib/redis-process.js b/test/lib/redis-process.js index a77d20bcc84..b1141f7f108 100644 --- a/test/lib/redis-process.js +++ b/test/lib/redis-process.js @@ -1,23 +1,40 @@ var cp = require('child_process'); +var config = require('./config'); +var path = require('path'); +var tcpPortUsed = require('tcp-port-used'); module.exports = { - start: function (done, isSocket) { - var confFile = isSocket ? "test/conf/redis-socket.conf" : "test/conf/redis.conf"; - var redis = cp.spawn("redis-server", [confFile]); + start: function (done) { + // spawn redis with our testing configuration. + var confFile = path.resolve(__dirname, '../conf/redis.conf'); + var rp = cp.spawn("redis-server", [confFile], {}); - redis.once('err', done); - setTimeout(function (data) { - redis.removeListener('err', done); - done(); - }, 1000); + // wait for redis to become available, by + // checking the port we bind on. + var id = setInterval(function () { + tcpPortUsed.check(config.PORT, '127.0.0.1') + .then(function (inUse) { + if (inUse) { + clearInterval(id); - return { - stop: function (done) { - redis.once("exit", function () { - done(); - }); - redis.kill("SIGINT"); - } - }; + // return an object that can be used in + // an after() block to shutdown redis. + return done(null, { + stop: function (done) { + rp.once("exit", function (code) { + var error = null; + if (code !== 0) error = Error('failed to shutdown redis'); + return done(error); + }); + rp.kill("SIGINT"); + } + }); + } + }) + .catch(function (err) { + clearInterval(id); + return done(err); + }) + }, 100); } }; diff --git a/test/mocha/connecting.spec.js b/test/mocha/connecting.spec.js index dd46f56f59d..94ce9246792 100644 --- a/test/mocha/connecting.spec.js +++ b/test/mocha/connecting.spec.js @@ -1,11 +1,21 @@ -var nodeAssert = require("../lib/nodeify-assertions"); +var async = require("async"); var config = require("../lib/config"); +var nodeAssert = require("../lib/nodeify-assertions"); var redis = config.redis; -var async = require("async"); +var RedisProcess = require("../lib/redis-process"); describe("A node_redis client", function () { - function allTests(parser, ip, isSocket) { - var args = config.configureClient(parser, ip, isSocket); + + var rp; + before(function (done) { + RedisProcess.start(function (err, _rp) { + rp = _rp; + return done(err); + }); + }) + + function allTests(parser, ip) { + var args = config.configureClient(parser, ip); describe("using " + parser + " and " + ip, function () { var client; @@ -157,9 +167,13 @@ describe("A node_redis client", function () { } ['javascript', 'hiredis'].forEach(function (parser) { - allTests(parser, "/tmp/redis.sock", true); + allTests(parser, "/tmp/redis.sock"); ['IPv4', 'IPv6'].forEach(function (ip) { allTests(parser, ip); }) }); + + after(function (done) { + if (rp) rp.stop(done); + }) }); diff --git a/test/mocha/select.spec.js b/test/mocha/select.spec.js index 418ca4439f6..18fb34c6420 100644 --- a/test/mocha/select.spec.js +++ b/test/mocha/select.spec.js @@ -1,12 +1,28 @@ -var nodeAssert = require("../lib/nodeify-assertions"); +var async = require('async'); +var assert = require('assert'); var config = require("../lib/config"); +var nodeAssert = require('../lib/nodeify-assertions'); var redis = config.redis; -var async = require("async"); -var assert = require("assert"); +var RedisProcess = require("../lib/redis-process"); describe("The 'select' method", function () { - function allTests(parser, ip, isSocket) { - var args = config.configureClient(parser, ip, isSocket); + + var rp; + before(function (done) { + RedisProcess.start(function (err, _rp) { + rp = _rp; + return done(err); + }); + }) + + function removeMochaListener () { + var mochaListener = process.listeners('uncaughtException').pop(); + process.removeListener('uncaughtException', mochaListener); + return mochaListener; + } + + function allTests(parser, ip) { + var args = config.configureClient(parser, ip); describe("using " + parser + " and " + ip, function () { describe("when not connected", function () { @@ -15,26 +31,19 @@ describe("The 'select' method", function () { beforeEach(function (done) { client = redis.createClient.apply(redis.createClient, args); client.once("error", done); - client.once("connect", function () { - client.set("doot", "good calsum", function (err, res) { - client.end(); - done(); - }); + client.quit(); + }); + client.on('end', function () { + return done(); }); }); - it("doesn't even throw an error or call the callback at all WTF", function (done) { - this.timeout(50); - + it("throws an error if redis is not connected", function (done) { client.select(1, function (err, res) { - nodeAssert.isNotError()(err, res); + assert.equal(err.message, 'Redis connection gone from end event.'); done(); }); - - setTimeout(function () { - done(); - }, 45); }); }); @@ -44,10 +53,7 @@ describe("The 'select' method", function () { beforeEach(function (done) { client = redis.createClient.apply(redis.createClient, args); client.once("error", done); - - client.once("connect", function () { - done(); - }); + client.once("connect", function () { done(); }); }); afterEach(function () { @@ -65,54 +71,37 @@ describe("The 'select' method", function () { }); describe("and no callback is specified", function () { - // select_error_emits_if_no_callback - // this is another test that was testing the wrong thing. The old test did indeed emit an error, - // but not because of the lacking callback, but because 9999 was an invalid db index. describe("with a valid db index", function () { - it("works just fine and does not actually emit an error like the old tests assert WTF", function (done) { + it("selects the appropriate database", function (done) { assert.strictEqual(client.selected_db, null, "default db should be null"); - this.timeout(50); - client.on("error", function (err) { - nodeAssert.isNotError()(err); - assert.strictEqual(client.selected_db, 1, "db should be 1 after select"); - done(new Error("the old tests were crap")); - }); client.select(1); - setTimeout(function () { - done(); - }, 45); + assert.equal(client.selected_db, 1, "we should have selected the new valid DB"); + return done(); + }, 100); }); }); - // Can't seem to catch the errors thrown here. - xdescribe("with an invalid db index", function () { + describe("with an invalid db index", function () { it("emits an error", function (done) { - this.timeout(50); - assert.strictEqual(client.selected_db, null, "default db should be null"); - client.on("error", function (err) { - console.log('got an error', err); - done(); + client.select(9999, function (err) { + assert.equal(err.message, 'ERR invalid DB index') + return done(); }); - - try { - client.select(9999); - } catch (err) {} - - setTimeout(function () { - done(new Error("It was supposed to emit an error.")); - }, 45); }); - it("throws an error bc a callback is not", function (done) { + it("throws an error when callback not provided", function (done) { + var mochaListener = removeMochaListener(); assert.strictEqual(client.selected_db, null, "default db should be null"); - try { - client.select(9999); - done(new Error("Was supposed to throw an invalid db index error.")); - } catch (err) { - done(); - } + + process.once('uncaughtException', function (err) { + process.on('uncaughtException', mochaListener); + assert.equal(err.message, 'ERR invalid DB index'); + return done(); + }); + + client.select(9999); }); }); }); @@ -121,10 +110,13 @@ describe("The 'select' method", function () { } ['javascript', 'hiredis'].forEach(function (parser) { - //allTests(parser, "/tmp/redis.sock", true); - //['IPv4', 'IPv6'].forEach(function (ip) { - ['IPv4'].forEach(function (ip) { + allTests(parser, "/tmp/redis.sock"); + ['IPv4', 'IPv6'].forEach(function (ip) { allTests(parser, ip); }) }); + + after(function (done) { + if (rp) rp.stop(done); + }); }); From 3aaef4775608a9213fdc83df612511bbbe345d59 Mon Sep 17 00:00:00 2001 From: Erin Spiceland Date: Tue, 14 Jul 2015 09:45:48 -0500 Subject: [PATCH 0254/1748] Fix bug in mocha tests Redis shutdown which expected exit code to eq 0. Move a miscategorized select test into the correct describe. --- test/lib/redis-process.js | 4 +++- test/mocha/select.spec.js | 46 +++++++++++++++++++++++++-------------- test/test.js | 13 ----------- 3 files changed, 33 insertions(+), 30 deletions(-) diff --git a/test/lib/redis-process.js b/test/lib/redis-process.js index b1141f7f108..24d7f04f60a 100644 --- a/test/lib/redis-process.js +++ b/test/lib/redis-process.js @@ -23,7 +23,9 @@ module.exports = { stop: function (done) { rp.once("exit", function (code) { var error = null; - if (code !== 0) error = Error('failed to shutdown redis'); + if (code !== null && code !== 0) { + error = Error('Redis shutdown failed with code ' + code); + } return done(error); }); rp.kill("SIGINT"); diff --git a/test/mocha/select.spec.js b/test/mocha/select.spec.js index 18fb34c6420..bde75857f67 100644 --- a/test/mocha/select.spec.js +++ b/test/mocha/select.spec.js @@ -9,16 +9,16 @@ describe("The 'select' method", function () { var rp; before(function (done) { - RedisProcess.start(function (err, _rp) { - rp = _rp; - return done(err); - }); + RedisProcess.start(function (err, _rp) { + rp = _rp; + return done(err); + }); }) function removeMochaListener () { - var mochaListener = process.listeners('uncaughtException').pop(); - process.removeListener('uncaughtException', mochaListener); - return mochaListener; + var mochaListener = process.listeners('uncaughtException').pop(); + process.removeListener('uncaughtException', mochaListener); + return mochaListener; } function allTests(parser, ip) { @@ -32,10 +32,10 @@ describe("The 'select' method", function () { client = redis.createClient.apply(redis.createClient, args); client.once("error", done); client.once("connect", function () { - client.quit(); + client.quit(); }); client.on('end', function () { - return done(); + return done(); }); }); @@ -70,15 +70,14 @@ describe("The 'select' method", function () { }); }); - describe("and no callback is specified", function () { + describe("and a callback is specified", function () { describe("with a valid db index", function () { it("selects the appropriate database", function (done) { assert.strictEqual(client.selected_db, null, "default db should be null"); - client.select(1); - setTimeout(function () { + client.select(1, function () { assert.equal(client.selected_db, 1, "we should have selected the new valid DB"); return done(); - }, 100); + }); }); }); @@ -90,15 +89,30 @@ describe("The 'select' method", function () { return done(); }); }); + }); + }); + + describe("and no callback is specified", function () { + describe("with a valid db index", function () { + it("selects the appropriate database", function (done) { + assert.strictEqual(client.selected_db, null, "default db should be null"); + client.select(1); + setTimeout(function () { + assert.equal(client.selected_db, 1, "we should have selected the new valid DB"); + return done(); + }, 100); + }); + }); + describe("with an invalid db index", function () { it("throws an error when callback not provided", function (done) { var mochaListener = removeMochaListener(); assert.strictEqual(client.selected_db, null, "default db should be null"); process.once('uncaughtException', function (err) { - process.on('uncaughtException', mochaListener); - assert.equal(err.message, 'ERR invalid DB index'); - return done(); + process.on('uncaughtException', mochaListener); + assert.equal(err.message, 'ERR invalid DB index'); + return done(); }); client.select(9999); diff --git a/test/test.js b/test/test.js index 9142a144cab..6021fcdfa9a 100644 --- a/test/test.js +++ b/test/test.js @@ -938,19 +938,6 @@ tests.socket_nodelay = function () { c3.on("ready", ready_check); }; -tests.select_error_emits_if_no_callback = function () { - var prev = client.listeners("error")[0]; - client.removeListener("error", prev); - var name = "select_error_emits_if_no_callback"; - var handler = with_timeout(name, function (err) { - require_error(name)(err); - client.removeListener('error', handler); - client.on("error", prev); - next(name); - }, 500); - client.on('error', handler); - client.select(9999); -}; tests.idle = function () { var name = "idle"; From 1e9613fa8f692e9f46385cb7b8b67245570ed478 Mon Sep 17 00:00:00 2001 From: Benjamin Coe Date: Tue, 14 Jul 2015 08:39:33 -0700 Subject: [PATCH 0255/1748] slight tweak to test command --- package.json | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/package.json b/package.json index 7f9b320b785..145f167c2a9 100644 --- a/package.json +++ b/package.json @@ -12,7 +12,7 @@ "scripts": { "coverage": "nyc report --reporter=text-lcov | coveralls", "test": "nyc ./test/run.sh", - "mocha": "nyc mocha ./test/mocha/*.js" + "mocha": "nyc ./node_modules/.bin/_mocha ./test/mocha/*.js" }, "devDependencies": { "async": "^1.3.0", From eaca486ab1aecd1329f7452ad2f2255b1263606f Mon Sep 17 00:00:00 2001 From: Erin Spiceland Date: Tue, 14 Jul 2015 11:00:41 -0500 Subject: [PATCH 0256/1748] Add Mocha tests for the "set" method. Fix error in organization of connection Mocha tests. Clarify some test descriptions in 'set' Mocha tests. Add some tests for mset. Remove old 'set' tests. Add some Mocha tests for 'get'. Add tests for 'getset'. Add tests for 'dbsize'. Add 'flushdb' tests. Add tests for 'incr'. --- package.json | 3 + test/mocha/connecting.spec.js | 199 +++++++++++++++++----------------- test/mocha/dbsize.spec.js | 125 +++++++++++++++++++++ test/mocha/flushdb.spec.js | 134 +++++++++++++++++++++++ test/mocha/get.spec.js | 111 +++++++++++++++++++ test/mocha/getset.spec.js | 115 ++++++++++++++++++++ test/mocha/incr.spec.js | 139 ++++++++++++++++++++++++ test/mocha/mset.spec.js | 181 +++++++++++++++++++++++++++++++ test/mocha/set.spec.js | 178 ++++++++++++++++++++++++++++++ test/test.js | 34 +----- 10 files changed, 1089 insertions(+), 130 deletions(-) create mode 100644 test/mocha/dbsize.spec.js create mode 100644 test/mocha/flushdb.spec.js create mode 100644 test/mocha/get.spec.js create mode 100644 test/mocha/getset.spec.js create mode 100644 test/mocha/incr.spec.js create mode 100644 test/mocha/mset.spec.js create mode 100644 test/mocha/set.spec.js diff --git a/package.json b/package.json index 145f167c2a9..05637945013 100644 --- a/package.json +++ b/package.json @@ -28,5 +28,8 @@ "repository": { "type": "git", "url": "git://github.com/mranney/node_redis.git" + }, + "dependencies": { + "uuid": "^2.0.1" } } diff --git a/test/mocha/connecting.spec.js b/test/mocha/connecting.spec.js index 94ce9246792..2319c2173da 100644 --- a/test/mocha/connecting.spec.js +++ b/test/mocha/connecting.spec.js @@ -3,6 +3,7 @@ var config = require("../lib/config"); var nodeAssert = require("../lib/nodeify-assertions"); var redis = config.redis; var RedisProcess = require("../lib/redis-process"); +var uuid = require("uuid"); describe("A node_redis client", function () { @@ -20,90 +21,48 @@ describe("A node_redis client", function () { describe("using " + parser + " and " + ip, function () { var client; - after(function () { - client.end(); - }); - - it("connects correctly", function (done) { - client = redis.createClient.apply(redis.createClient, args); - client.on("error", done); - - client.once("ready", function () { - client.removeListener("error", done); - client.get("recon 1", function (err, res) { - done(err); - }); + describe("when not connected", function () { + afterEach(function () { + client.end(); }); - }); - }); - describe("when connected", function () { - var client; + it("connects correctly", function (done) { + client = redis.createClient.apply(redis.createClient, args); + client.on("error", done); - beforeEach(function (done) { - client = redis.createClient.apply(redis.createClient, args); - client.once("error", function onError(err) { - done(err); - }); - client.once("ready", function onReady() { - done(); + client.once("ready", function () { + client.removeListener("error", done); + client.get("recon 1", function (err, res) { + done(err); + }); + }); }); }); - afterEach(function () { - client.end(); - }); + describe("when connected", function () { + var client; - describe("when redis closes unexpectedly", function () { - it("reconnects and can retrieve the pre-existing data", function (done) { - client.on("reconnecting", function on_recon(params) { - client.on("connect", function on_connect() { - async.parallel([function (cb) { - client.get("recon 1", function (err, res) { - nodeAssert.isString("one")(err, res); - cb(); - }); - }, function (cb) { - client.get("recon 1", function (err, res) { - nodeAssert.isString("one")(err, res); - cb(); - }); - }, function (cb) { - client.get("recon 2", function (err, res) { - nodeAssert.isString("two")(err, res); - cb(); - }); - }, function (cb) { - client.get("recon 2", function (err, res) { - nodeAssert.isString("two")(err, res); - cb(); - }); - }], function (err, results) { - client.removeListener("connect", on_connect); - client.removeListener("reconnecting", on_recon); - done(err); - }); - }); + beforeEach(function (done) { + client = redis.createClient.apply(redis.createClient, args); + client.once("error", function onError(err) { + done(err); }); - - client.set("recon 1", "one"); - client.set("recon 2", "two", function (err, res) { - // Do not do this in normal programs. This is to simulate the server closing on us. - // For orderly shutdown in normal programs, do client.quit() - client.stream.destroy(); + client.once("ready", function onReady() { + done(); }); }); - describe("and it's subscribed to a channel", function () { - // reconnect_select_db_after_pubsub - // Does not pass. - // "Connection in subscriber mode, only subscriber commands may be used" - xit("reconnects, unsubscribes, and can retrieve the pre-existing data", function (done) { + afterEach(function () { + client.end(); + }); + + describe("when redis closes unexpectedly", function () { + it("reconnects and can retrieve the pre-existing data", function (done) { client.on("reconnecting", function on_recon(params) { - client.on("ready", function on_connect() { + client.on("connect", function on_connect() { async.parallel([function (cb) { - client.unsubscribe("recon channel", function (err, res) { - nodeAssert.isNotError()(err, res); + client.get("recon 1", function (err, res) { + nodeAssert.isString("one")(err, res); cb(); }); }, function (cb) { @@ -111,6 +70,16 @@ describe("A node_redis client", function () { nodeAssert.isString("one")(err, res); cb(); }); + }, function (cb) { + client.get("recon 2", function (err, res) { + nodeAssert.isString("two")(err, res); + cb(); + }); + }, function (cb) { + client.get("recon 2", function (err, res) { + nodeAssert.isString("two")(err, res); + cb(); + }); }], function (err, results) { client.removeListener("connect", on_connect); client.removeListener("reconnecting", on_recon); @@ -120,45 +89,79 @@ describe("A node_redis client", function () { }); client.set("recon 1", "one"); - client.subscribe("recon channel", function (err, res) { + client.set("recon 2", "two", function (err, res) { // Do not do this in normal programs. This is to simulate the server closing on us. // For orderly shutdown in normal programs, do client.quit() client.stream.destroy(); }); }); - it("remains subscribed", function () { - var client2 = redis.createClient.apply(redis.createClient, args); - - client.on("reconnecting", function on_recon(params) { - client.on("ready", function on_connect() { - async.parallel([function (cb) { - client.on("message", function (channel, message) { - try { - nodeAssert.isString("recon channel")(null, channel); - nodeAssert.isString("a test message")(null, message); - } catch (err) { - cb(err); - } + describe("and it's subscribed to a channel", function () { + // reconnect_select_db_after_pubsub + // Does not pass. + // "Connection in subscriber mode, only subscriber commands may be used" + xit("reconnects, unsubscribes, and can retrieve the pre-existing data", function (done) { + client.on("reconnecting", function on_recon(params) { + client.on("ready", function on_connect() { + async.parallel([function (cb) { + client.unsubscribe("recon channel", function (err, res) { + nodeAssert.isNotError()(err, res); + cb(); + }); + }, function (cb) { + client.get("recon 1", function (err, res) { + nodeAssert.isString("one")(err, res); + cb(); + }); + }], function (err, results) { + client.removeListener("connect", on_connect); + client.removeListener("reconnecting", on_recon); + done(err); }); + }); + }); + + client.set("recon 1", "one"); + client.subscribe("recon channel", function (err, res) { + // Do not do this in normal programs. This is to simulate the server closing on us. + // For orderly shutdown in normal programs, do client.quit() + client.stream.destroy(); + }); + }); - client2.subscribe("recon channel", function (err, res) { - if (err) { - cb(err); - return; - } - client2.publish("recon channel", "a test message"); + it("remains subscribed", function () { + var client2 = redis.createClient.apply(redis.createClient, args); + + client.on("reconnecting", function on_recon(params) { + client.on("ready", function on_connect() { + async.parallel([function (cb) { + client.on("message", function (channel, message) { + try { + nodeAssert.isString("recon channel")(null, channel); + nodeAssert.isString("a test message")(null, message); + } catch (err) { + cb(err); + } + }); + + client2.subscribe("recon channel", function (err, res) { + if (err) { + cb(err); + return; + } + client2.publish("recon channel", "a test message"); + }); + }], function (err, results) { + done(err); }); - }], function (err, results) { - done(err); }); }); - }); - client.subscribe("recon channel", function (err, res) { - // Do not do this in normal programs. This is to simulate the server closing on us. - // For orderly shutdown in normal programs, do client.quit() - client.stream.destroy(); + client.subscribe("recon channel", function (err, res) { + // Do not do this in normal programs. This is to simulate the server closing on us. + // For orderly shutdown in normal programs, do client.quit() + client.stream.destroy(); + }); }); }); }); diff --git a/test/mocha/dbsize.spec.js b/test/mocha/dbsize.spec.js new file mode 100644 index 00000000000..aa019c57cb6 --- /dev/null +++ b/test/mocha/dbsize.spec.js @@ -0,0 +1,125 @@ +var async = require('async'); +var assert = require('assert'); +var config = require("../lib/config"); +var nodeAssert = require('../lib/nodeify-assertions'); +var redis = config.redis; +var RedisProcess = require("../lib/redis-process"); +var uuid = require('uuid'); + +describe("The 'dbsize' method", function () { + + var rp; + before(function (done) { + RedisProcess.start(function (err, _rp) { + rp = _rp; + return done(err); + }); + }) + + function removeMochaListener () { + var mochaListener = process.listeners('uncaughtException').pop(); + process.removeListener('uncaughtException', mochaListener); + return mochaListener; + } + + function allTests(parser, ip) { + var args = config.configureClient(parser, ip); + + describe("using " + parser + " and " + ip, function () { + var key, value; + + beforeEach(function () { + key = uuid.v4(); + value = uuid.v4(); + }); + + describe("when not connected", function () { + var client; + + beforeEach(function (done) { + client = redis.createClient.apply(redis.createClient, args); + client.once("error", done); + client.once("connect", function () { + client.quit(); + }); + client.on('end', function () { + return done(); + }); + }); + + it("reports an error", function (done) { + client.dbsize([], function (err, res) { + assert.equal(err.message, 'Redis connection gone from end event.'); + done(); + }); + }); + }); + + describe("when connected", function () { + var client; + + beforeEach(function (done) { + client = redis.createClient.apply(redis.createClient, args); + client.once("error", done); + client.once("connect", function () { + client.flushdb(function (err, res) { + nodeAssert.isString("OK")(err, res); + done(); + }); + }); + }); + + afterEach(function () { + client.end(); + }); + + it("returns a zero db size", function (done) { + client.dbsize([], function (err, res) { + nodeAssert.isNotError()(err, res); + nodeAssert.isType.number()(err, res); + assert.strictEqual(res, 0, "Initial db size should be 0"); + done(); + }); + }); + + describe("when more data is added to Redis", function () { + var oldSize; + + beforeEach(function (done) { + client.dbsize([], function (err, res) { + nodeAssert.isType.number()(err, res); + assert.strictEqual(res, 0, "Initial db size should be 0"); + + oldSize = res; + + client.set(key, value, function (err, res) { + nodeAssert.isNotError()(err, res); + done(); + }); + }); + }); + + it("returns a larger db size", function (done) { + client.dbsize([], function (err, res) { + nodeAssert.isNotError()(err, res); + nodeAssert.isType.positiveNumber()(err, res); + assert.strictEqual(true, (oldSize < res), "Adding data should increase db size."); + done(); + }); + }); + }); + }); + }); + } + + ['javascript', 'hiredis'].forEach(function (parser) { + allTests(parser, "/tmp/redis.sock"); + ['IPv4', 'IPv6'].forEach(function (ip) { + allTests(parser, ip); + }) + }); + + after(function (done) { + if (rp) rp.stop(done); + }); +}); diff --git a/test/mocha/flushdb.spec.js b/test/mocha/flushdb.spec.js new file mode 100644 index 00000000000..693ff69ab54 --- /dev/null +++ b/test/mocha/flushdb.spec.js @@ -0,0 +1,134 @@ +var async = require('async'); +var assert = require('assert'); +var config = require("../lib/config"); +var nodeAssert = require('../lib/nodeify-assertions'); +var redis = config.redis; +var RedisProcess = require("../lib/redis-process"); +var uuid = require('uuid'); + +describe("The 'flushdb' method", function () { + + var rp; + before(function (done) { + RedisProcess.start(function (err, _rp) { + rp = _rp; + return done(err); + }); + }) + + function removeMochaListener () { + var mochaListener = process.listeners('uncaughtException').pop(); + process.removeListener('uncaughtException', mochaListener); + return mochaListener; + } + + function allTests(parser, ip) { + var args = config.configureClient(parser, ip); + + describe("using " + parser + " and " + ip, function () { + var key, key2; + + beforeEach(function () { + key = uuid.v4(); + key2 = uuid.v4(); + }); + + describe("when not connected", function () { + var client; + + beforeEach(function (done) { + client = redis.createClient.apply(redis.createClient, args); + client.once("error", done); + client.once("connect", function () { + client.quit(); + }); + client.on('end', function () { + return done(); + }); + }); + + it("reports an error", function (done) { + client.flushdb(function (err, res) { + assert.equal(err.message, 'Redis connection gone from end event.'); + done(); + }); + }); + }); + + describe("when connected", function () { + var client; + + beforeEach(function (done) { + client = redis.createClient.apply(redis.createClient, args); + client.once("error", done); + client.once("connect", function () { + done(); + }); + }); + + afterEach(function () { + client.end(); + }); + + describe("when there is data in Redis", function () { + var oldSize; + + beforeEach(function (done) { + async.parallel([function (next) { + client.mset(key, uuid.v4(), key2, uuid.v4(), function (err, res) { + nodeAssert.isNotError()(err, res); + next(err); + }); + }, function (next) { + client.dbsize([], function (err, res) { + nodeAssert.isType.positiveNumber()(err, res); + oldSize = res; + next(err); + }); + }], function (err) { + if (err) { + return done(err); + } + + client.flushdb(function (err, res) { + nodeAssert.isString("OK")(err, res); + done(err); + }); + }); + }); + + it("deletes all the keys", function (done) { + client.mget(key, key2, function (err, res) { + assert.strictEqual(null, err, "Unexpected error returned"); + assert.strictEqual(true, Array.isArray(res), "Results object should be an array."); + assert.strictEqual(2, res.length, "Results array should have length 2."); + assert.strictEqual(null, res[0], "Redis key should have been flushed."); + assert.strictEqual(null, res[1], "Redis key should have been flushed."); + done(err); + }); + }); + + it("results in a db size of zero", function (done) { + client.dbsize([], function (err, res) { + nodeAssert.isNotError()(err, res); + nodeAssert.isType.number()(err, res); + assert.strictEqual(0, res, "Flushing db should result in db size 0"); + done(); + }); + }); + }); + }); + }); + } + + ['javascript', 'hiredis'].forEach(function (parser) { + allTests(parser, "/tmp/redis.sock"); + ['IPv4', 'IPv6'].forEach(function (ip) { + allTests(parser, ip); + }) + }); + + after(function (done) { + if (rp) rp.stop(done); + }); +}); diff --git a/test/mocha/get.spec.js b/test/mocha/get.spec.js new file mode 100644 index 00000000000..6c533f6a81e --- /dev/null +++ b/test/mocha/get.spec.js @@ -0,0 +1,111 @@ +var async = require('async'); +var assert = require('assert'); +var config = require("../lib/config"); +var nodeAssert = require('../lib/nodeify-assertions'); +var redis = config.redis; +var RedisProcess = require("../lib/redis-process"); +var uuid = require('uuid'); + +describe("The 'get' method", function () { + + var rp; + before(function (done) { + RedisProcess.start(function (err, _rp) { + rp = _rp; + return done(err); + }); + }) + + function removeMochaListener () { + var mochaListener = process.listeners('uncaughtException').pop(); + process.removeListener('uncaughtException', mochaListener); + return mochaListener; + } + + function allTests(parser, ip) { + var args = config.configureClient(parser, ip); + + describe("using " + parser + " and " + ip, function () { + var key, value; + + beforeEach(function () { + key = uuid.v4(); + value = uuid.v4(); + }); + + describe("when not connected", function () { + var client; + + beforeEach(function (done) { + client = redis.createClient.apply(redis.createClient, args); + client.once("error", done); + client.once("connect", function () { + client.quit(); + }); + client.on('end', function () { + return done(); + }); + }); + + it("reports an error", function (done) { + client.get(key, function (err, res) { + assert.equal(err.message, 'Redis connection gone from end event.'); + done(); + }); + }); + }); + + describe("when connected", function () { + var client; + + beforeEach(function (done) { + client = redis.createClient.apply(redis.createClient, args); + client.once("error", done); + client.once("connect", function () { + done(); + }); + }); + + afterEach(function () { + client.end(); + }); + + describe("when the key exists in Redis", function () { + beforeEach(function (done) { + client.set(key, value, function (err, res) { + nodeAssert.isNotError()(err, res); + done(); + }); + }); + + it("gets the value correctly", function (done) { + client.get(key, function (err, res) { + nodeAssert.isString(value)(err, res); + done(err); + }); + }); + }); + + describe("when the key does not exist in Redis", function () { + it("gets a null value", function (done) { + client.get(key, function (err, res) { + nodeAssert.isNull()(err, res); + done(err); + }); + }); + }); + }); + }); + } + + ['javascript', 'hiredis'].forEach(function (parser) { + allTests(parser, "/tmp/redis.sock"); + ['IPv4', 'IPv6'].forEach(function (ip) { + allTests(parser, ip); + }) + }); + + after(function (done) { + if (rp) rp.stop(done); + }); +}); diff --git a/test/mocha/getset.spec.js b/test/mocha/getset.spec.js new file mode 100644 index 00000000000..847aa1e4626 --- /dev/null +++ b/test/mocha/getset.spec.js @@ -0,0 +1,115 @@ +var async = require('async'); +var assert = require('assert'); +var config = require("../lib/config"); +var nodeAssert = require('../lib/nodeify-assertions'); +var redis = config.redis; +var RedisProcess = require("../lib/redis-process"); +var uuid = require('uuid'); + +describe("The 'getset' method", function () { + + var rp; + before(function (done) { + RedisProcess.start(function (err, _rp) { + rp = _rp; + return done(err); + }); + }) + + function removeMochaListener () { + var mochaListener = process.listeners('uncaughtException').pop(); + process.removeListener('uncaughtException', mochaListener); + return mochaListener; + } + + function allTests(parser, ip) { + var args = config.configureClient(parser, ip); + + describe("using " + parser + " and " + ip, function () { + var key, value, value2; + + beforeEach(function () { + key = uuid.v4(); + value = uuid.v4(); + value2 = uuid.v4(); + }); + + describe("when not connected", function () { + var client; + + beforeEach(function (done) { + client = redis.createClient.apply(redis.createClient, args); + client.once("error", done); + client.once("connect", function () { + client.quit(); + }); + client.on('end', function () { + return done(); + }); + }); + + it("reports an error", function (done) { + client.get(key, function (err, res) { + assert.equal(err.message, 'Redis connection gone from end event.'); + done(); + }); + }); + }); + + describe("when connected", function () { + var client; + + beforeEach(function (done) { + client = redis.createClient.apply(redis.createClient, args); + client.once("error", done); + client.once("connect", function () { + done(); + }); + }); + + afterEach(function () { + client.end(); + }); + + describe("when the key exists in Redis", function () { + beforeEach(function (done) { + client.set(key, value, function (err, res) { + nodeAssert.isNotError()(err, res); + done(); + }); + }); + + it("gets the value correctly", function (done) { + client.getset(key, value2, function (err, res) { + nodeAssert.isString(value)(err, res); + client.get(key, function (err, res) { + nodeAssert.isString(value2)(err, res); + done(err); + }); + }); + }); + }); + + describe("when the key does not exist in Redis", function () { + it("gets a null value", function (done) { + client.getset(key, value, function (err, res) { + nodeAssert.isNull()(err, res); + done(err); + }); + }); + }); + }); + }); + } + + ['javascript', 'hiredis'].forEach(function (parser) { + allTests(parser, "/tmp/redis.sock"); + ['IPv4', 'IPv6'].forEach(function (ip) { + allTests(parser, ip); + }) + }); + + after(function (done) { + if (rp) rp.stop(done); + }); +}); diff --git a/test/mocha/incr.spec.js b/test/mocha/incr.spec.js new file mode 100644 index 00000000000..34bacec70ea --- /dev/null +++ b/test/mocha/incr.spec.js @@ -0,0 +1,139 @@ +var async = require('async'); +var assert = require('assert'); +var config = require("../lib/config"); +var nodeAssert = require('../lib/nodeify-assertions'); +var redis = config.redis; +var RedisProcess = require("../lib/redis-process"); +var uuid = require('uuid'); + +describe("The 'incr' method", function () { + + var rp; + before(function (done) { + RedisProcess.start(function (err, _rp) { + rp = _rp; + return done(err); + }); + }) + + function removeMochaListener () { + var mochaListener = process.listeners('uncaughtException').pop(); + process.removeListener('uncaughtException', mochaListener); + return mochaListener; + } + + function allTests(parser, ip) { + var args = config.configureClient(parser, ip); + + describe("using " + parser + " and " + ip, function () { + var key = "sequence"; + + describe("when not connected", function () { + var client; + + beforeEach(function (done) { + client = redis.createClient.apply(redis.createClient, args); + client.once("error", done); + client.once("connect", function () { + client.set(key, "9007199254740992", function (err, res) { + nodeAssert.isNotError()(err, res); + client.quit(); + }); + }); + client.on('end', function () { + return done(); + }); + }); + + it("reports an error", function (done) { + client.incr(function (err, res) { + assert.equal(err.message, 'Redis connection gone from end event.'); + done(); + }); + }); + }); + + describe("when connected and a value in Redis", function () { + var client; + + // Also, why tf were these disabled for hiredis? They work just fine. + before(function (done) { + /* + 9007199254740992 -> 9007199254740992 + 9007199254740993 -> 9007199254740992 + 9007199254740994 -> 9007199254740994 + 9007199254740995 -> 9007199254740996 + 9007199254740996 -> 9007199254740996 + 9007199254740997 -> 9007199254740996 + */ + client = redis.createClient.apply(redis.createClient, args); + client.once("error", done); + client.once("connect", function () { + client.set(key, "9007199254740992", function (err, res) { + nodeAssert.isNotError()(err, res); + done(); + }); + }); + }); + + after(function () { + client.end(); + }); + + it("changes the last digit from 2 to 3", function (done) { + client.incr(key, function (err, res) { + nodeAssert.isString("9007199254740993")(err, res); + done(err); + }); + }); + + describe("and we call it again", function () { + it("changes the last digit from 3 to 4", function (done) { + client.incr(key, function (err, res) { + nodeAssert.isString("9007199254740994")(err, res); + done(err); + }); + }); + + describe("and again", function () { + it("changes the last digit from 4 to 5", function (done) { + client.incr(key, function (err, res) { + nodeAssert.isString("9007199254740995")(err, res); + done(err); + }); + }); + + describe("and again", function () { + it("changes the last digit from 5 to 6", function (done) { + client.incr(key, function (err, res) { + nodeAssert.isString("9007199254740996")(err, res); + done(err); + }); + }); + + describe("and again", function () { + it("changes the last digit from 6 to 7", function (done) { + client.incr(key, function (err, res) { + nodeAssert.isString("9007199254740997")(err, res); + done(err); + }); + }); + }); + }); + }); + }); + }); + }); + } + + ['javascript', 'hiredis'].forEach(function (parser) { + allTests(parser, "/tmp/redis.sock"); + ['IPv4', 'IPv6'].forEach(function (ip) { + allTests(parser, ip); + }) + }); + + after(function (done) { + if (rp) rp.stop(done); + }); +}); diff --git a/test/mocha/mset.spec.js b/test/mocha/mset.spec.js new file mode 100644 index 00000000000..90ffb32a74d --- /dev/null +++ b/test/mocha/mset.spec.js @@ -0,0 +1,181 @@ +var async = require('async'); +var assert = require('assert'); +var config = require("../lib/config"); +var nodeAssert = require('../lib/nodeify-assertions'); +var redis = config.redis; +var RedisProcess = require("../lib/redis-process"); +var uuid = require('uuid'); + +describe("The 'mset' method", function () { + + var rp; + before(function (done) { + RedisProcess.start(function (err, _rp) { + rp = _rp; + return done(err); + }); + }) + + function removeMochaListener () { + var mochaListener = process.listeners('uncaughtException').pop(); + process.removeListener('uncaughtException', mochaListener); + return mochaListener; + } + + function allTests(parser, ip) { + var args = config.configureClient(parser, ip); + + describe("using " + parser + " and " + ip, function () { + var key, value, key2, value2; + + beforeEach(function () { + key = uuid.v4(); + value = uuid.v4(); + key2 = uuid.v4(); + value2 = uuid.v4(); + }); + + describe("when not connected", function () { + var client; + + beforeEach(function (done) { + client = redis.createClient.apply(redis.createClient, args); + client.once("error", done); + client.once("connect", function () { + client.quit(); + }); + client.on('end', function () { + return done(); + }); + }); + + it("reports an error", function (done) { + client.mset(key, value, key2, value2, function (err, res) { + assert.equal(err.message, 'Redis connection gone from end event.'); + done(); + }); + }); + }); + + describe("when connected", function () { + var client; + + beforeEach(function (done) { + client = redis.createClient.apply(redis.createClient, args); + client.once("error", done); + client.once("connect", function () { + done(); + }); + }); + + afterEach(function () { + client.end(); + }); + + describe("and a callback is specified", function () { + describe("with valid parameters", function () { + it("sets the value correctly", function (done) { + client.mset(key, value, key2, value2, function (err, res) { + nodeAssert.isNotError()(err, res); + async.parallel([function (next) { + client.get(key, function (err, res) { + nodeAssert.isString(value)(err, res); + next(); + }); + }, function (next) { + client.get(key2, function (err, res) { + nodeAssert.isString(value2)(err, res); + next(); + }); + }], function (err) { + done(err); + }); + }); + }); + }); + + describe("with undefined 'key' parameter and missing 'value' parameter", function () { + it("reports an error", function (done) { + client.mset(undefined, function (err, res) { + nodeAssert.isError()(err, null); + done(); + }); + }); + }); + + describe("with undefined 'key' and defined 'value' parameters", function () { + it("reports an error", function () { + client.mset(undefined, value, undefined, value2, function (err, res) { + nodeAssert.isError()(err, null); + done(); + }); + }); + }); + }); + + describe("and no callback is specified", function () { + describe("with valid parameters", function () { + it("sets the value correctly", function (done) { + client.mset(key, value, key2, value2); + + setTimeout(function () { + async.parallel([function (next) { + client.get(key, function (err, res) { + nodeAssert.isString(value)(err, res); + next(); + }); + }, function (next) { + client.get(key2, function (err, res) { + nodeAssert.isString(value2)(err, res); + next(); + }); + }], function (err) { + done(err); + }); + }, 100); + }); + }); + + describe("with undefined 'key' and missing 'value' parameter", function () { + // this behavior is different from the 'set' behavior. + it("throws an error", function (done) { + var mochaListener = removeMochaListener(); + + process.once('uncaughtException', function (err) { + process.on('uncaughtException', mochaListener); + nodeAssert.isError()(err, null); + return done(); + }); + + client.mset(); + }); + }); + + describe("with undefined 'key' and defined 'value' parameters", function () { + it("throws an error", function () { + var mochaListener = removeMochaListener(); + + process.once('uncaughtException', function (err) { + process.on('uncaughtException', mochaListener); + nodeAssert.isError()(err, null); + }); + + client.mset(undefined, value, undefined, value2); + }); + }); + }); + }); + }); + } + + ['javascript', 'hiredis'].forEach(function (parser) { + allTests(parser, "/tmp/redis.sock"); + ['IPv4', 'IPv6'].forEach(function (ip) { + allTests(parser, ip); + }) + }); + + after(function (done) { + if (rp) rp.stop(done); + }); +}); diff --git a/test/mocha/set.spec.js b/test/mocha/set.spec.js new file mode 100644 index 00000000000..ed5026710ef --- /dev/null +++ b/test/mocha/set.spec.js @@ -0,0 +1,178 @@ +var async = require('async'); +var assert = require('assert'); +var config = require("../lib/config"); +var nodeAssert = require('../lib/nodeify-assertions'); +var redis = config.redis; +var RedisProcess = require("../lib/redis-process"); +var uuid = require('uuid'); + +describe("The 'set' method", function () { + + var rp; + before(function (done) { + RedisProcess.start(function (err, _rp) { + rp = _rp; + return done(err); + }); + }) + + function removeMochaListener () { + var mochaListener = process.listeners('uncaughtException').pop(); + process.removeListener('uncaughtException', mochaListener); + return mochaListener; + } + + function allTests(parser, ip) { + var args = config.configureClient(parser, ip); + + describe("using " + parser + " and " + ip, function () { + var key, value; + + beforeEach(function () { + key = uuid.v4(); + value = uuid.v4(); + }); + + describe("when not connected", function () { + var client; + + beforeEach(function (done) { + client = redis.createClient.apply(redis.createClient, args); + client.once("error", done); + client.once("connect", function () { + client.quit(); + }); + client.on('end', function () { + return done(); + }); + }); + + it("reports an error", function (done) { + client.set(key, value, function (err, res) { + assert.equal(err.message, 'Redis connection gone from end event.'); + done(); + }); + }); + }); + + describe("when connected", function () { + var client; + + beforeEach(function (done) { + client = redis.createClient.apply(redis.createClient, args); + client.once("error", done); + client.once("connect", function () { + done(); + }); + }); + + afterEach(function () { + client.end(); + }); + + describe("and a callback is specified", function () { + describe("with valid parameters", function () { + it("sets the value correctly", function (done) { + client.set(key, value, function (err, res) { + nodeAssert.isNotError()(err, res); + client.get(key, function (err, res) { + nodeAssert.isString(value)(err, res); + done(); + }); + }); + }); + }); + + describe("with undefined 'key' and missing 'value' parameter", function () { + it("reports an error", function (done) { + client.set(undefined, function (err, res) { + nodeAssert.isError()(err, null); + done(); + }); + }); + }); + + describe("with undefined 'key' and defined 'value' parameters", function () { + it("reports an error", function () { + client.set(undefined, value, function (err, res) { + nodeAssert.isError()(err, null); + done(); + }); + }); + }); + }); + + describe("and no callback is specified", function () { + describe("with valid parameters", function () { + it("sets the value correctly", function (done) { + client.set(key, value); + setTimeout(function () { + client.get(key, function (err, res) { + nodeAssert.isString(value)(err, res); + done(); + }); + }, 100); + }); + }); + + describe("with undefined 'key' and missing 'value' parameter", function () { + it("does not emit an error", function (done) { + this.timeout(50); + + client.once("error", function (err) { + nodeAssert.isError()(err, null); + return done(err); + }); + + client.set(); + + setTimeout(function () { + done(); + }, 45); + }); + + it("does not throw an error", function (done) { + this.timeout(50); + var mochaListener = removeMochaListener(); + + process.once('uncaughtException', function (err) { + process.on('uncaughtException', mochaListener); + return done(err); + }); + + client.set(); + + setTimeout(function () { + done(); + }, 45); + }); + }); + + describe("with undefined 'key' and defined 'value' parameters", function () { + it("throws an error", function () { + var mochaListener = removeMochaListener(); + + process.once('uncaughtException', function (err) { + process.on('uncaughtException', mochaListener); + nodeAssert.isError()(err, null); + }); + + client.set(undefined, value); + }); + }); + }); + }); + }); + } + + ['javascript', 'hiredis'].forEach(function (parser) { + allTests(parser, "/tmp/redis.sock"); + ['IPv4', 'IPv6'].forEach(function (ip) { + allTests(parser, ip); + }) + }); + + after(function (done) { + if (rp) rp.stop(done); + }); +}); diff --git a/test/test.js b/test/test.js index 6021fcdfa9a..530a75b19a7 100644 --- a/test/test.js +++ b/test/test.js @@ -117,6 +117,8 @@ next = function next(name) { // Tests are run in the order they are defined, so FLUSHDB should always be first. +<<<<<<< HEAD +<<<<<<< HEAD <<<<<<< HEAD tests.IPV4 = function () { var ipv4addr = process.env.REDIS_PORT_6379_TCP_ADDR || "127.0.0.1"; @@ -1365,38 +1367,6 @@ tests.RENAMENX = function () { client.exists(["foo2"], last(name, require_number(1, name))); }; -tests.DBSIZE = function () { - var name = "DBSIZE"; - client.set(['foo', 'bar'], require_string("OK", name)); - client.DBSIZE([], last(name, require_number_pos("DBSIZE"))); -}; - -tests.GET_1 = function () { - var name = "GET_1"; - client.set(["get key", "get val"], require_string("OK", name)); - client.GET(["get key"], last(name, require_string("get val", name))); -}; - -tests.GET_2 = function() { - var name = "GET_2"; - - // tests handling of non-existent keys - client.GET('this_key_shouldnt_exist', last(name, require_null(name))); -}; - -tests.SET = function () { - var name = "SET"; - client.SET(["set key", "set val"], require_string("OK", name)); - client.get(["set key"], last(name, require_string("set val", name))); - client.SET(["set key", undefined], require_error(name)); -}; - -tests.GETSET = function () { - var name = "GETSET"; - client.set(["getset key", "getset val"], require_string("OK", name)); - client.GETSET(["getset key", "new getset val"], require_string("getset val", name)); - client.get(["getset key"], last(name, require_string("new getset val", name))); -}; tests.MGET = function () { var name = "MGET"; From 071b3ff27c7580d6c4440e9d487434432b64fd00 Mon Sep 17 00:00:00 2001 From: Benjamin Coe Date: Thu, 16 Jul 2015 00:00:59 -0700 Subject: [PATCH 0257/1748] moved tests for specific commands into the /commands sub-folder, put a ton of tests around the multi command --- package.json | 2 +- test/lib/nodeify-assertions.js | 10 + test/mocha/{ => commands}/dbsize.spec.js | 6 +- test/mocha/{ => commands}/flushdb.spec.js | 6 +- test/mocha/{ => commands}/get.spec.js | 6 +- test/mocha/{ => commands}/getset.spec.js | 6 +- test/mocha/{ => commands}/incr.spec.js | 6 +- test/mocha/{ => commands}/mset.spec.js | 6 +- test/mocha/commands/multi.spec.js | 269 ++++++++++++++++++ test/mocha/{ => commands}/select.spec.js | 6 +- test/mocha/{ => commands}/set.spec.js | 14 +- ...{connecting.spec.js => node_redis.spec.js} | 0 test/test.js | 55 +--- 13 files changed, 311 insertions(+), 81 deletions(-) rename test/mocha/{ => commands}/dbsize.spec.js (96%) rename test/mocha/{ => commands}/flushdb.spec.js (96%) rename test/mocha/{ => commands}/get.spec.js (95%) rename test/mocha/{ => commands}/getset.spec.js (96%) rename test/mocha/{ => commands}/incr.spec.js (97%) rename test/mocha/{ => commands}/mset.spec.js (97%) create mode 100644 test/mocha/commands/multi.spec.js rename test/mocha/{ => commands}/select.spec.js (97%) rename test/mocha/{ => commands}/set.spec.js (95%) rename test/mocha/{connecting.spec.js => node_redis.spec.js} (100%) diff --git a/package.json b/package.json index 05637945013..ce67d02b836 100644 --- a/package.json +++ b/package.json @@ -12,7 +12,7 @@ "scripts": { "coverage": "nyc report --reporter=text-lcov | coveralls", "test": "nyc ./test/run.sh", - "mocha": "nyc ./node_modules/.bin/_mocha ./test/mocha/*.js" + "mocha": "nyc ./node_modules/.bin/_mocha ./test/mocha/*.js ./test/mocha/commands/*.js" }, "devDependencies": { "async": "^1.3.0", diff --git a/test/lib/nodeify-assertions.js b/test/lib/nodeify-assertions.js index c80cccb8e5b..73000e67d07 100644 --- a/test/lib/nodeify-assertions.js +++ b/test/lib/nodeify-assertions.js @@ -56,5 +56,15 @@ module.exports = { return true; }; } + }, + + serverVersionAtLeast: function (connection, desired_version) { + // Return true if the server version >= desired_version + var version = connection.server_info.versions; + for (var i = 0; i < 3; i++) { + if (version[i] > desired_version[i]) return true; + if (version[i] < desired_version[i]) return false; + } + return true; } }; diff --git a/test/mocha/dbsize.spec.js b/test/mocha/commands/dbsize.spec.js similarity index 96% rename from test/mocha/dbsize.spec.js rename to test/mocha/commands/dbsize.spec.js index aa019c57cb6..df1b1431bd3 100644 --- a/test/mocha/dbsize.spec.js +++ b/test/mocha/commands/dbsize.spec.js @@ -1,9 +1,9 @@ var async = require('async'); var assert = require('assert'); -var config = require("../lib/config"); -var nodeAssert = require('../lib/nodeify-assertions'); +var config = require("../../lib/config"); +var nodeAssert = require('../../lib/nodeify-assertions'); var redis = config.redis; -var RedisProcess = require("../lib/redis-process"); +var RedisProcess = require("../../lib/redis-process"); var uuid = require('uuid'); describe("The 'dbsize' method", function () { diff --git a/test/mocha/flushdb.spec.js b/test/mocha/commands/flushdb.spec.js similarity index 96% rename from test/mocha/flushdb.spec.js rename to test/mocha/commands/flushdb.spec.js index 693ff69ab54..de325a800d5 100644 --- a/test/mocha/flushdb.spec.js +++ b/test/mocha/commands/flushdb.spec.js @@ -1,9 +1,9 @@ var async = require('async'); var assert = require('assert'); -var config = require("../lib/config"); -var nodeAssert = require('../lib/nodeify-assertions'); +var config = require("../../lib/config"); +var nodeAssert = require('../../lib/nodeify-assertions'); var redis = config.redis; -var RedisProcess = require("../lib/redis-process"); +var RedisProcess = require("../../lib/redis-process"); var uuid = require('uuid'); describe("The 'flushdb' method", function () { diff --git a/test/mocha/get.spec.js b/test/mocha/commands/get.spec.js similarity index 95% rename from test/mocha/get.spec.js rename to test/mocha/commands/get.spec.js index 6c533f6a81e..d232a6ecb12 100644 --- a/test/mocha/get.spec.js +++ b/test/mocha/commands/get.spec.js @@ -1,9 +1,9 @@ var async = require('async'); var assert = require('assert'); -var config = require("../lib/config"); -var nodeAssert = require('../lib/nodeify-assertions'); +var config = require("../../lib/config"); +var nodeAssert = require('../../lib/nodeify-assertions'); var redis = config.redis; -var RedisProcess = require("../lib/redis-process"); +var RedisProcess = require("../../lib/redis-process"); var uuid = require('uuid'); describe("The 'get' method", function () { diff --git a/test/mocha/getset.spec.js b/test/mocha/commands/getset.spec.js similarity index 96% rename from test/mocha/getset.spec.js rename to test/mocha/commands/getset.spec.js index 847aa1e4626..ba76384f2c0 100644 --- a/test/mocha/getset.spec.js +++ b/test/mocha/commands/getset.spec.js @@ -1,9 +1,9 @@ var async = require('async'); var assert = require('assert'); -var config = require("../lib/config"); -var nodeAssert = require('../lib/nodeify-assertions'); +var config = require("../../lib/config"); +var nodeAssert = require('../../lib/nodeify-assertions'); var redis = config.redis; -var RedisProcess = require("../lib/redis-process"); +var RedisProcess = require("../../lib/redis-process"); var uuid = require('uuid'); describe("The 'getset' method", function () { diff --git a/test/mocha/incr.spec.js b/test/mocha/commands/incr.spec.js similarity index 97% rename from test/mocha/incr.spec.js rename to test/mocha/commands/incr.spec.js index 34bacec70ea..ae4534dd304 100644 --- a/test/mocha/incr.spec.js +++ b/test/mocha/commands/incr.spec.js @@ -1,9 +1,9 @@ var async = require('async'); var assert = require('assert'); -var config = require("../lib/config"); -var nodeAssert = require('../lib/nodeify-assertions'); +var config = require("../../lib/config"); +var nodeAssert = require('../../lib/nodeify-assertions'); var redis = config.redis; -var RedisProcess = require("../lib/redis-process"); +var RedisProcess = require("../../lib/redis-process"); var uuid = require('uuid'); describe("The 'incr' method", function () { diff --git a/test/mocha/mset.spec.js b/test/mocha/commands/mset.spec.js similarity index 97% rename from test/mocha/mset.spec.js rename to test/mocha/commands/mset.spec.js index 90ffb32a74d..b1f4f367b04 100644 --- a/test/mocha/mset.spec.js +++ b/test/mocha/commands/mset.spec.js @@ -1,9 +1,9 @@ var async = require('async'); var assert = require('assert'); -var config = require("../lib/config"); -var nodeAssert = require('../lib/nodeify-assertions'); +var config = require("../../lib/config"); +var nodeAssert = require('../../lib/nodeify-assertions'); var redis = config.redis; -var RedisProcess = require("../lib/redis-process"); +var RedisProcess = require("../../lib/redis-process"); var uuid = require('uuid'); describe("The 'mset' method", function () { diff --git a/test/mocha/commands/multi.spec.js b/test/mocha/commands/multi.spec.js new file mode 100644 index 00000000000..ba8be4fcbf7 --- /dev/null +++ b/test/mocha/commands/multi.spec.js @@ -0,0 +1,269 @@ +var async = require('async'); +var assert = require('assert'); +var config = require("../../lib/config"); +var nodeAssert = require('../../lib/nodeify-assertions'); +var redis = config.redis; +var RedisProcess = require("../../lib/redis-process"); +var uuid = require('uuid'); + +describe("The 'multi' method", function () { + + var rp; + before(function (done) { + RedisProcess.start(function (err, _rp) { + rp = _rp; + return done(err); + }); + }) + + function removeMochaListener () { + var mochaListener = process.listeners('uncaughtException').pop(); + process.removeListener('uncaughtException', mochaListener); + return mochaListener; + } + + function allTests(parser, ip) { + var args = config.configureClient(parser, ip); + + describe("using " + parser + " and " + ip, function () { + var key, value; + + beforeEach(function () { + key = uuid.v4(); + value = uuid.v4(); + }); + + describe("when not connected", function () { + var client; + + beforeEach(function (done) { + client = redis.createClient.apply(redis.createClient, args); + client.once("error", done); + client.once("connect", function () { + client.quit(); + }); + client.on('end', function () { + return done(); + }); + }); + + it("reports an error", function (done) { + client.multi(); + client.exec(function (err, res) { + assert.equal(err.message, 'Redis connection gone from end event.'); + done(); + }); + }); + }); + + describe("when connected", function () { + var client; + + beforeEach(function (done) { + client = redis.createClient.apply(redis.createClient, args); + client.once("error", done); + client.once("connect", function () { + client.del('multifoo', 'multibar', 'multifoo_8', 'multibar_8', function (err) { + return done(err); + }) + }); + }); + + afterEach(function () { + client.end(); + }); + + it('roles back a transaction when one command in a sequence of commands fails', function (done) { + var name = "MULTI_1", multi1, multi2; + + // Provoke an error at queue time + multi1 = client.multi(); + multi1.mset("multifoo", "10", "multibar", "20", nodeAssert.isString("OK")); + multi1.set("foo2", nodeAssert.isError()); + multi1.incr("multifoo", nodeAssert.isNumber(11)); + multi1.incr("multibar", nodeAssert.isNumber(21)); + multi1.exec(function () { + // Redis 2.6.5+ will abort transactions with errors + // see: http://redis.io/topics/transactions + var multibar_expected = 22; + var multifoo_expected = 12; + if (nodeAssert.serverVersionAtLeast(client, [2, 6, 5])) { + multibar_expected = 1; + multifoo_expected = 1; + } + + // Confirm that the previous command, while containing an error, still worked. + multi2 = client.multi(); + multi2.incr("multibar", nodeAssert.isNumber(multibar_expected)); + multi2.incr("multifoo", nodeAssert.isNumber(multifoo_expected)); + multi2.exec(function (err, replies) { + assert.strictEqual(multibar_expected, replies[0]); + assert.strictEqual(multifoo_expected, replies[1]); + return done(); + }); + }); + }); + + // I'm unclear as to the difference between this test in the test above, + // perhaps @mranney can clarify? + it('roles back a transaction when an error was provoked at queue time', function (done) { + multi1 = client.multi(); + multi1.mset("multifoo_8", "10", "multibar_8", "20", nodeAssert.isString("OK")); + multi1.set("foo2", nodeAssert.isError()); + multi1.set("foo3", nodeAssert.isError()); + multi1.incr("multifoo_8", nodeAssert.isNumber(11)); + multi1.incr("multibar_8", nodeAssert.isNumber(21)); + multi1.exec(function () { + // Redis 2.6.5+ will abort transactions with errors + // see: http://redis.io/topics/transactions + var multibar_expected = 22; + var multifoo_expected = 12; + if (nodeAssert.serverVersionAtLeast(client, [2, 6, 5])) { + multibar_expected = 1; + multifoo_expected = 1; + } + + // Confirm that the previous command, while containing an error, still worked. + multi2 = client.multi(); + multi2.incr("multibar_8", nodeAssert.isNumber(multibar_expected)); + multi2.incr("multifoo_8", nodeAssert.isNumber(multifoo_expected)); + multi2.exec(function (err, replies) { + assert.strictEqual(multibar_expected, replies[0]); + assert.strictEqual(multifoo_expected, replies[1]); + return done(); + }); + }); + }); + + it('roles back a transaction when one command in an array of commands fails', function (done) { + // test nested multi-bulk replies + client.multi([ + ["mget", "multifoo", "multibar", function (err, res) { + assert.strictEqual(2, res.length); + assert.strictEqual("0", res[0].toString()); + assert.strictEqual("0", res[1].toString()); + }], + ["set", "foo2", nodeAssert.isError()], + ["incr", "multifoo", nodeAssert.isNumber(1)], + ["incr", "multibar", nodeAssert.isNumber(1)] + ]).exec(function (err, replies) { + if (nodeAssert.serverVersionAtLeast(client, [2, 6, 5])) { + assert.notEqual(err, null); + assert.equal(replies, undefined); + } else { + assert.strictEqual(2, replies[0].length); + assert.strictEqual("0", replies[0][0].toString()); + assert.strictEqual("0", replies[0][1].toString()); + + assert.strictEqual("1", replies[1].toString()); + assert.strictEqual("1", replies[2].toString()); + } + + return done(); + }); + }); + + it('handles multiple operations being applied to a set', function (done) { + client.sadd("some set", "mem 1"); + client.sadd("some set", "mem 2"); + client.sadd("some set", "mem 3"); + client.sadd("some set", "mem 4"); + + // make sure empty mb reply works + client.del("some missing set"); + client.smembers("some missing set", function (err, reply) { + // make sure empty mb reply works + assert.strictEqual(0, reply.length); + }); + + // test nested multi-bulk replies with empty mb elements. + client.multi([ + ["smembers", "some set"], + ["del", "some set"], + ["smembers", "some set"] + ]) + .scard("some set") + .exec(function (err, replies) { + assert.strictEqual(4, replies[0].length); + assert.strictEqual(0, replies[2].length); + return done(); + }); + }); + + it('allows multiple operations to be performed using a chaining API', function (done) { + client.multi() + .mset('some', '10', 'keys', '20') + .incr('some') + .incr('keys') + .mget('some', 'keys') + .exec(function (err, replies) { + assert.strictEqual(null, err); + assert.equal('OK', replies[0]); + assert.equal(11, replies[1]); + assert.equal(21, replies[2]); + assert.equal(11, replies[3][0].toString()); + assert.equal(21, replies[3][1].toString()); + return done(); + }); + }); + + it('allows an array to be provided indicating multiple operations to perform', function (done) { + // test nested multi-bulk replies with nulls. + client.multi([ + ["mget", ["multifoo", "some", "random value", "keys"]], + ["incr", "multifoo"] + ]) + .exec(function (err, replies) { + assert.strictEqual(replies.length, 2); + assert.strictEqual(replies[0].length, 4); + return done(); + }); + }); + + it('allows multiple operations to be performed on a hash', function (done) { + client.multi() + .hmset("multihash", "a", "foo", "b", 1) + .hmset("multihash", { + extra: "fancy", + things: "here" + }) + .hgetall("multihash") + .exec(function (err, replies) { + assert.strictEqual(null, err); + assert.equal("OK", replies[0]); + assert.equal(Object.keys(replies[2]).length, 4); + assert.equal("foo", replies[2].a); + assert.equal("1", replies[2].b); + assert.equal("fancy", replies[2].extra); + assert.equal("here", replies[2].things); + return done(); + }); + }); + + it('reports multiple exceptions when they occur', function (done) { + if (!nodeAssert.serverVersionAtLeast(client, [2, 6, 5])) return done(); + + client.multi().set("foo").exec(function (err, reply) { + assert(Array.isArray(err), "err should be an array"); + assert.equal(2, err.length, "err should have 2 items"); + assert(err[0].message.match(/ERR/), "First error message should contain ERR"); + assert(err[1].message.match(/EXECABORT/), "First error message should contain EXECABORT"); + return done(); + }); + }); + + }); + }); + } + + ['javascript', 'hiredis'].forEach(function (parser) { + allTests(parser, "/tmp/redis.sock"); + ['IPv4', 'IPv6'].forEach(function (ip) { + allTests(parser, ip); + }) + }); + + after(function (done) { + if (rp) rp.stop(done); + }); +}); diff --git a/test/mocha/select.spec.js b/test/mocha/commands/select.spec.js similarity index 97% rename from test/mocha/select.spec.js rename to test/mocha/commands/select.spec.js index bde75857f67..24c1d159b8f 100644 --- a/test/mocha/select.spec.js +++ b/test/mocha/commands/select.spec.js @@ -1,9 +1,9 @@ var async = require('async'); var assert = require('assert'); -var config = require("../lib/config"); -var nodeAssert = require('../lib/nodeify-assertions'); +var config = require("../../lib/config"); +var nodeAssert = require('../../lib/nodeify-assertions'); var redis = config.redis; -var RedisProcess = require("../lib/redis-process"); +var RedisProcess = require("../../lib/redis-process"); describe("The 'select' method", function () { diff --git a/test/mocha/set.spec.js b/test/mocha/commands/set.spec.js similarity index 95% rename from test/mocha/set.spec.js rename to test/mocha/commands/set.spec.js index ed5026710ef..72df4467fbd 100644 --- a/test/mocha/set.spec.js +++ b/test/mocha/commands/set.spec.js @@ -1,9 +1,9 @@ var async = require('async'); var assert = require('assert'); -var config = require("../lib/config"); -var nodeAssert = require('../lib/nodeify-assertions'); +var config = require("../../lib/config"); +var nodeAssert = require('../../lib/nodeify-assertions'); var redis = config.redis; -var RedisProcess = require("../lib/redis-process"); +var RedisProcess = require("../../lib/redis-process"); var uuid = require('uuid'); describe("The 'set' method", function () { @@ -117,7 +117,7 @@ describe("The 'set' method", function () { describe("with undefined 'key' and missing 'value' parameter", function () { it("does not emit an error", function (done) { - this.timeout(50); + this.timeout(200); client.once("error", function (err) { nodeAssert.isError()(err, null); @@ -128,11 +128,11 @@ describe("The 'set' method", function () { setTimeout(function () { done(); - }, 45); + }, 100); }); it("does not throw an error", function (done) { - this.timeout(50); + this.timeout(200); var mochaListener = removeMochaListener(); process.once('uncaughtException', function (err) { @@ -144,7 +144,7 @@ describe("The 'set' method", function () { setTimeout(function () { done(); - }, 45); + }, 100); }); }); diff --git a/test/mocha/connecting.spec.js b/test/mocha/node_redis.spec.js similarity index 100% rename from test/mocha/connecting.spec.js rename to test/mocha/node_redis.spec.js diff --git a/test/test.js b/test/test.js index 530a75b19a7..7b3cb1cc721 100644 --- a/test/test.js +++ b/test/test.js @@ -120,6 +120,7 @@ next = function next(name) { <<<<<<< HEAD <<<<<<< HEAD <<<<<<< HEAD +<<<<<<< HEAD tests.IPV4 = function () { var ipv4addr = process.env.REDIS_PORT_6379_TCP_ADDR || "127.0.0.1"; var ipv4Client = redis.createClient( PORT, ipv4addr, { family : "IPv4", parser: parser } ); @@ -373,6 +374,8 @@ tests.MULTI_6 = function () { }); }; +// THIS TEST SHOULD BE MOVED IN TO A PARSER +// SPECIFIC TESTING FILE. tests.MULTI_7 = function () { var name = "MULTI_7"; @@ -402,58 +405,6 @@ tests.MULTI_7 = function () { next(name); }; - -tests.MULTI_EXCEPTION_1 = function() { - var name = "MULTI_EXCEPTION_1"; - - if (!server_version_at_least(client, [2, 6, 5])) { - console.log("Skipping " + name + " for old Redis server version < 2.6.5"); - return next(name); - } - - client.multi().set("foo").exec(function (err, reply) { - assert(Array.isArray(err), "err should be an array"); - assert.equal(2, err.length, "err should have 2 items"); - assert(err[0].message.match(/ERR/), "First error message should contain ERR"); - assert(err[1].message.match(/EXECABORT/), "First error message should contain EXECABORT"); - next(name); - }); -}; - -tests.MULTI_8 = function () { - var name = "MULTI_8", multi1, multi2; - - // Provoke an error at queue time - multi1 = client.multi(); - multi1.mset("multifoo_8", "10", "multibar_8", "20", require_string("OK", name)); - multi1.set("foo2", require_error(name)); - multi1.set("foo3", require_error(name)); - multi1.incr("multifoo_8", require_number(11, name)); - multi1.incr("multibar_8", require_number(21, name)); - multi1.exec(function () { - require_error(name); - - // Redis 2.6.5+ will abort transactions with errors - // see: http://redis.io/topics/transactions - var multibar_expected = 22; - var multifoo_expected = 12; - if (server_version_at_least(client, [2, 6, 5])) { - multibar_expected = 1; - multifoo_expected = 1; - } - - // Confirm that the previous command, while containing an error, still worked. - multi2 = client.multi(); - multi2.incr("multibar_8", require_number(multibar_expected, name)); - multi2.incr("multifoo_8", require_number(multifoo_expected, name)); - multi2.exec(function (err, replies) { - assert.strictEqual(multibar_expected, replies[0]); - assert.strictEqual(multifoo_expected, replies[1]); - next(name); - }); - }); -}; - tests.FWD_ERRORS_1 = function () { var name = "FWD_ERRORS_1"; From 04fe11b0f502b92b62695c1bc75c7fbca91cf7eb Mon Sep 17 00:00:00 2001 From: Erin Spiceland Date: Wed, 22 Jul 2015 13:43:25 -0500 Subject: [PATCH 0258/1748] Add simple tests for generated commands. --- package.json | 1 + .../mocha/commands/generated-commands.spec.js | 119 ++++++++++++++++++ 2 files changed, 120 insertions(+) create mode 100644 test/mocha/commands/generated-commands.spec.js diff --git a/package.json b/package.json index ce67d02b836..a25f7962357 100644 --- a/package.json +++ b/package.json @@ -30,6 +30,7 @@ "url": "git://github.com/mranney/node_redis.git" }, "dependencies": { + "sinon": "^1.15.4", "uuid": "^2.0.1" } } diff --git a/test/mocha/commands/generated-commands.spec.js b/test/mocha/commands/generated-commands.spec.js new file mode 100644 index 00000000000..bff15f4839e --- /dev/null +++ b/test/mocha/commands/generated-commands.spec.js @@ -0,0 +1,119 @@ +var async = require('async'); +var assert = require('assert'); +var config = require("../../lib/config"); +var nodeAssert = require('../../lib/nodeify-assertions'); +var redis = config.redis; +var RedisProcess = require("../../lib/redis-process"); +var uuid = require('uuid'); +var commands = require("../../../lib/commands"); +var sinon = require('sinon').sandbox.create(); + +var overwritten = ['eval', 'hmset', 'multi', 'select']; + +describe("The auto-generated methods", function () { + + var rp; + before(function (done) { + RedisProcess.start(function (err, _rp) { + rp = _rp; + return done(err); + }); + }) + + function removeMochaListener () { + var mochaListener = process.listeners('uncaughtException').pop(); + process.removeListener('uncaughtException', mochaListener); + return mochaListener; + } + + function allTests(parser, ip) { + var args = config.configureClient(parser, ip); + + describe("using " + parser + " and " + ip, function () { + var key, value; + var client; + + beforeEach(function (done) { + client = redis.createClient.apply(redis.createClient, args); + client.once("error", function onError(err) { + done(err); + }); + client.once("ready", function onReady() { + done(); + }); + }); + + afterEach(function () { + client.end(); + }); + + commands.forEach(function (method) { + if (overwritten.indexOf(method) > -1) { + // these are in the list of generated commands but are later overwritten with + // different behavior by node_redis + return; + } + + describe("the " + method + " method", function () { + var methodArgs; + var noop = function () { }; + + it("calls sendCommand with whatever arguments it receives", function () { + key = uuid.v4(); + value = uuid.v4(); + + var parts = method.split(' '); + var argNum = 0; + + client.send_command = sinon.spy(); + methodArgs = [key, value, noop]; + + client[parts[0]].apply(client, methodArgs); + + assert.strictEqual(client.send_command.called, true, + "Client.send_command should have been called."); + assert.strictEqual(parts[0], client.send_command.args[0][argNum], + "Command name '" + parts[0] + "' should be passed as arg " + + argNum + " to send_command"); + argNum++; + /* + * Um, except this doesn't work? The second part of the command is never sent???? + if (parts[1]) { + assert.strictEqual(parts[1], client.send_command.args[0][argNum], + "Second command '" + parts[1] + "' should be passed as arg " + + argNum + " to send_command"); + argNum++; + } + */ + assert.strictEqual(methodArgs.length, client.send_command.args[0][argNum].length, + "The rest of the args to " + method + " should be passed as arg an array to send_command"); + assert.strictEqual(methodArgs[0], client.send_command.args[0][argNum][0], + "Arg " + argNum + " to " + method + " should be passed as arg " + + argNum + " to send_command"); + assert.strictEqual(methodArgs[1], client.send_command.args[0][argNum][1], + "Arg " + argNum + " to " + method + " should be passed as arg " + + argNum + " to send_command"); + assert.strictEqual(methodArgs[2], client.send_command.args[0][argNum][2], + "Arg " + argNum + " to " + method + " should be passed as arg " + + argNum + " to send_command"); + }); + }); + }); + }); + } + + ['javascript', 'hiredis'].forEach(function (parser) { + allTests(parser, "/tmp/redis.sock"); + ['IPv4', 'IPv6'].forEach(function (ip) { + allTests(parser, ip); + }) + }); + + afterEach(function () { + sinon.restore(); + }); + + after(function (done) { + if (rp) rp.stop(done); + }); +}); From a0832c3744578402a69c9b1fc44c0883ef51f007 Mon Sep 17 00:00:00 2001 From: Benjamin Coe Date: Wed, 22 Jul 2015 20:59:08 -0700 Subject: [PATCH 0259/1748] slight refactor from code review smoke test large list of commands ported more tests to mocha, some slight cleanup in tests move sinon and uuid to dev dependencies finished porting eval tests over to mocha rebased mocha testing branch with master ported client and script tests ported watch tests ported detect_buffers tests ported unref tests ported auth tests over to mocha ported idle and no_delay tests ported hlen, hset continuing marching forward ported hincrby, sinter, sort, pubsub tests. improved logic in redis-process, I was still occasionally having issues where redis failed to exit. switch back to default test command ported del, exists, hlen, keys, randomkey, type cleanup based on what I've learned so far from refactor. we now start and stop redis less often. moved tests to their final resting place finished porting node_redis client tests ported hgetall, mget, msetnx, rename, renamenx, setex, setnx ported hgetall, mget, msetnx, rename, renamenx, setex, setnx ported queue tests to mocha amalgamated some of the helper logic ported sadd, scard, sismember, srem, utf-8 --- .travis.yml | 3 +- package.json | 10 +- test/auth.spec.js | 90 + test/commands/client.spec.js | 61 + test/{mocha => }/commands/dbsize.spec.js | 37 +- test/commands/del.spec.js | 51 + test/commands/eval.spec.js | 199 ++ test/commands/exits.spec.js | 43 + test/{mocha => }/commands/flushdb.spec.js | 33 +- test/{mocha => }/commands/get.spec.js | 29 +- test/{mocha => }/commands/getset.spec.js | 31 +- test/commands/hgetall.spec.js | 91 + test/commands/hincrby.spec.js | 48 + test/commands/hlen.spec.js | 46 + test/commands/hmget.spec.js | 68 + test/commands/hmset.spec.js | 61 + test/commands/hset.spec.js | 70 + test/{mocha => }/commands/incr.spec.js | 41 +- test/commands/keys.spec.js | 76 + test/commands/mget.spec.js | 66 + test/{mocha => }/commands/mset.spec.js | 35 +- test/commands/msetnx.spec.js | 46 + test/{mocha => }/commands/multi.spec.js | 65 +- test/commands/randomkey.test.js | 42 + test/commands/rename.spec.js | 46 + test/commands/renamenx.spec.js | 49 + test/commands/sadd.spec.js | 58 + test/commands/scard.spec.js | 39 + test/commands/script.spec.js | 68 + test/{mocha => }/commands/select.spec.js | 19 +- test/{mocha => }/commands/set.spec.js | 31 +- test/commands/setex.spec.js | 47 + test/commands/setnx.spec.js | 46 + test/commands/sinter.spec.js | 70 + test/commands/sismember.spec.js | 43 + test/commands/sort.spec.js | 128 ++ test/commands/srem.spec.js | 66 + test/commands/type.spec.js | 63 + test/commands/watch.spec.js | 68 + test/conf/password.conf | 5 + test/{lib/nodeify-assertions.js => helper.js} | 76 +- test/lib/config.js | 17 +- test/lib/redis-process.js | 71 +- test/lib/unref.js | 18 + .../mocha/commands/generated-commands.spec.js | 119 - test/mocha/node_redis.spec.js | 182 -- test/node_redis.spec.js | 640 ++++++ test/parser/javascript.spec.js | 27 + test/pubsub.spec.js | 240 ++ test/queue-test.js | 37 - test/queue.spec.js | 37 + test/run.sh | 4 - test/test-unref.js | 14 - test/test.js | 1936 +---------------- 54 files changed, 3013 insertions(+), 2593 deletions(-) create mode 100644 test/auth.spec.js create mode 100644 test/commands/client.spec.js rename test/{mocha => }/commands/dbsize.spec.js (75%) create mode 100644 test/commands/del.spec.js create mode 100644 test/commands/eval.spec.js create mode 100644 test/commands/exits.spec.js rename test/{mocha => }/commands/flushdb.spec.js (81%) rename test/{mocha => }/commands/get.spec.js (78%) rename test/{mocha => }/commands/getset.spec.js (77%) create mode 100644 test/commands/hgetall.spec.js create mode 100644 test/commands/hincrby.spec.js create mode 100644 test/commands/hlen.spec.js create mode 100644 test/commands/hmget.spec.js create mode 100644 test/commands/hmset.spec.js create mode 100644 test/commands/hset.spec.js rename test/{mocha => }/commands/incr.spec.js (79%) create mode 100644 test/commands/keys.spec.js create mode 100644 test/commands/mget.spec.js rename test/{mocha => }/commands/mset.spec.js (85%) create mode 100644 test/commands/msetnx.spec.js rename test/{mocha => }/commands/multi.spec.js (82%) create mode 100644 test/commands/randomkey.test.js create mode 100644 test/commands/rename.spec.js create mode 100644 test/commands/renamenx.spec.js create mode 100644 test/commands/sadd.spec.js create mode 100644 test/commands/scard.spec.js create mode 100644 test/commands/script.spec.js rename test/{mocha => }/commands/select.spec.js (92%) rename test/{mocha => }/commands/set.spec.js (87%) create mode 100644 test/commands/setex.spec.js create mode 100644 test/commands/setnx.spec.js create mode 100644 test/commands/sinter.spec.js create mode 100644 test/commands/sismember.spec.js create mode 100644 test/commands/sort.spec.js create mode 100644 test/commands/srem.spec.js create mode 100644 test/commands/type.spec.js create mode 100644 test/commands/watch.spec.js create mode 100644 test/conf/password.conf rename test/{lib/nodeify-assertions.js => helper.js} (55%) create mode 100644 test/lib/unref.js delete mode 100644 test/mocha/commands/generated-commands.spec.js delete mode 100644 test/mocha/node_redis.spec.js create mode 100644 test/node_redis.spec.js create mode 100644 test/parser/javascript.spec.js create mode 100644 test/pubsub.spec.js delete mode 100644 test/queue-test.js create mode 100644 test/queue.spec.js delete mode 100755 test/run.sh delete mode 100644 test/test-unref.js diff --git a/.travis.yml b/.travis.yml index 4884f204e13..f64f437830b 100644 --- a/.travis.yml +++ b/.travis.yml @@ -3,6 +3,5 @@ sudo: true node_js: - "0.10" - "0.12" - - "iojs" -script: npm run mocha + - "iojs-v2" after_success: npm run coverage diff --git a/package.json b/package.json index a25f7962357..2e7d9ea1812 100644 --- a/package.json +++ b/package.json @@ -11,8 +11,7 @@ "main": "./index.js", "scripts": { "coverage": "nyc report --reporter=text-lcov | coveralls", - "test": "nyc ./test/run.sh", - "mocha": "nyc ./node_modules/.bin/_mocha ./test/mocha/*.js ./test/mocha/commands/*.js" + "test": "nyc ./node_modules/.bin/_mocha ./test/*.js ./test/commands/*.js ./test/parser/*.js --timeout=8000" }, "devDependencies": { "async": "^1.3.0", @@ -23,14 +22,11 @@ "mocha": "^2.2.5", "nyc": "^3.0.0", "tcp-port-used": "^0.1.2", - "underscore": "~1.4.4" + "underscore": "~1.4.4", + "uuid": "^2.0.1" }, "repository": { "type": "git", "url": "git://github.com/mranney/node_redis.git" - }, - "dependencies": { - "sinon": "^1.15.4", - "uuid": "^2.0.1" } } diff --git a/test/auth.spec.js b/test/auth.spec.js new file mode 100644 index 00000000000..a149f4b9cfd --- /dev/null +++ b/test/auth.spec.js @@ -0,0 +1,90 @@ +var assert = require("assert"); +var config = require("./lib/config"); +var helper = require('./helper') +var path = require('path'); +var redis = config.redis; + +describe("client authentication", function () { + before(function (done) { + helper.stopRedis(function () { + helper.startRedis('./conf/password.conf', done); + }); + }); + + function allTests(parser, ip) { + describe("using " + parser + " and " + ip, function () { + var args = config.configureClient(parser, ip); + var auth = 'porkchopsandwiches'; + var client = null; + + afterEach(function () { + client.end(); + }); + + it("allows auth to be provided with 'auth' method", function (done) { + client = redis.createClient.apply(redis.createClient, args); + client.auth(auth, function (err, res) { + assert.strictEqual(null, err); + assert.strictEqual("OK", res.toString()); + return done(err); + }); + }); + + it("raises error when auth is bad", function (done) { + client = redis.createClient.apply(redis.createClient, args); + + client.once('error', function (error) { + assert.ok(/ERR invalid password/.test(error)) + return done(); + }); + + client.auth(auth + 'bad'); + }); + + if (ip === 'IPv4') + it('allows auth to be provided as config option for client', function (done) { + client = redis.createClient('redis://foo:' + auth + '@' + config.HOST[ip] + ':' + config.PORT); + client.on("ready", function () { + return done(); + }); + }); + + it('allows auth to be provided as part of redis url', function (done) { + var args = config.configureClient(parser, ip, { + auth_pass: auth + }); + client = redis.createClient.apply(redis.createClient, args); + client.on("ready", function () { + return done(); + }); + }); + + it('reconnects with appropriate authentication', function (done) { + var readyCount = 0; + client = redis.createClient.apply(redis.createClient, args); + client.auth(auth); + client.on("ready", function () { + readyCount++; + if (readyCount === 1) { + client.stream.destroy(); + } else { + return done(); + } + }); + }); + }); + } + + ['javascript', 'hiredis'].forEach(function (parser) { + allTests(parser, "/tmp/redis.sock"); + ['IPv4', 'IPv6'].forEach(function (ip) { + allTests(parser, ip); + }) + }); + + after(function (done) { + helper.stopRedis(function () { + helper.startRedis('./conf/redis.conf', done); + }); + }); +}); diff --git a/test/commands/client.spec.js b/test/commands/client.spec.js new file mode 100644 index 00000000000..3710bdc9edd --- /dev/null +++ b/test/commands/client.spec.js @@ -0,0 +1,61 @@ +var assert = require("assert"); +var config = require("../lib/config"); +var helper = require("../helper"); +var redis = config.redis; + +describe("The 'client' method", function () { + + function allTests(parser, ip) { + var args = config.configureClient(parser, ip); + var pattern = /addr=/; + + describe("using " + parser + " and " + ip, function () { + var client; + + beforeEach(function (done) { + client = redis.createClient.apply(redis.createClient, args); + client.once("error", done); + client.once("connect", function () { + client.flushdb(function (err) { + if (!helper.serverVersionAtLeast(client, [2, 4, 0])) { + err = Error('script not supported in redis <= 2.4.0') + } + return done(err); + + }) + }); + }); + + afterEach(function () { + client.end(); + }); + + describe('list', function () { + it('lists connected clients', function (done) { + client.client("list", helper.match(pattern, done)); + }); + + it("lists connected clients when invoked with multi's chaining syntax", function (done) { + client.multi().client("list").exec(function(err, results) { + assert(pattern.test(results[0]), "expected string '" + results + "' to match " + pattern.toString()); + return done() + }) + }); + + it("lists connected clients when invoked with multi's array syntax", function (done) { + client.multi().client("list").exec(function(err, results) { + assert(pattern.test(results[0]), "expected string '" + results + "' to match " + pattern.toString()); + return done() + }) + }); + }); + }); + } + + ['javascript', 'hiredis'].forEach(function (parser) { + allTests(parser, "/tmp/redis.sock"); + ['IPv4', 'IPv6'].forEach(function (ip) { + allTests(parser, ip); + }) + }); +}); diff --git a/test/mocha/commands/dbsize.spec.js b/test/commands/dbsize.spec.js similarity index 75% rename from test/mocha/commands/dbsize.spec.js rename to test/commands/dbsize.spec.js index df1b1431bd3..70c7eb178c1 100644 --- a/test/mocha/commands/dbsize.spec.js +++ b/test/commands/dbsize.spec.js @@ -1,27 +1,12 @@ var async = require('async'); var assert = require('assert'); -var config = require("../../lib/config"); -var nodeAssert = require('../../lib/nodeify-assertions'); +var config = require("../lib/config"); +var helper = require('../helper'); var redis = config.redis; -var RedisProcess = require("../../lib/redis-process"); var uuid = require('uuid'); describe("The 'dbsize' method", function () { - var rp; - before(function (done) { - RedisProcess.start(function (err, _rp) { - rp = _rp; - return done(err); - }); - }) - - function removeMochaListener () { - var mochaListener = process.listeners('uncaughtException').pop(); - process.removeListener('uncaughtException', mochaListener); - return mochaListener; - } - function allTests(parser, ip) { var args = config.configureClient(parser, ip); @@ -63,7 +48,7 @@ describe("The 'dbsize' method", function () { client.once("error", done); client.once("connect", function () { client.flushdb(function (err, res) { - nodeAssert.isString("OK")(err, res); + helper.isString("OK")(err, res); done(); }); }); @@ -75,8 +60,8 @@ describe("The 'dbsize' method", function () { it("returns a zero db size", function (done) { client.dbsize([], function (err, res) { - nodeAssert.isNotError()(err, res); - nodeAssert.isType.number()(err, res); + helper.isNotError()(err, res); + helper.isType.number()(err, res); assert.strictEqual(res, 0, "Initial db size should be 0"); done(); }); @@ -87,13 +72,13 @@ describe("The 'dbsize' method", function () { beforeEach(function (done) { client.dbsize([], function (err, res) { - nodeAssert.isType.number()(err, res); + helper.isType.number()(err, res); assert.strictEqual(res, 0, "Initial db size should be 0"); oldSize = res; client.set(key, value, function (err, res) { - nodeAssert.isNotError()(err, res); + helper.isNotError()(err, res); done(); }); }); @@ -101,8 +86,8 @@ describe("The 'dbsize' method", function () { it("returns a larger db size", function (done) { client.dbsize([], function (err, res) { - nodeAssert.isNotError()(err, res); - nodeAssert.isType.positiveNumber()(err, res); + helper.isNotError()(err, res); + helper.isType.positiveNumber()(err, res); assert.strictEqual(true, (oldSize < res), "Adding data should increase db size."); done(); }); @@ -118,8 +103,4 @@ describe("The 'dbsize' method", function () { allTests(parser, ip); }) }); - - after(function (done) { - if (rp) rp.stop(done); - }); }); diff --git a/test/commands/del.spec.js b/test/commands/del.spec.js new file mode 100644 index 00000000000..b2b2995c484 --- /dev/null +++ b/test/commands/del.spec.js @@ -0,0 +1,51 @@ +var assert = require("assert"); +var config = require("../lib/config"); +var helper = require("../helper"); +var redis = config.redis; + +describe("The 'del' method", function () { + + function allTests(parser, ip) { + var args = config.configureClient(parser, ip); + + describe("using " + parser + " and " + ip, function () { + var client; + + beforeEach(function (done) { + client = redis.createClient.apply(redis.createClient, args); + client.once("error", done); + client.once("connect", function () { + client.flushdb(done); + }); + }); + + it('allows a single key to be deleted', function (done) { + client.set('foo', 'bar'); + client.del('foo', helper.isNumber(1)); + client.get('foo', helper.isNull(done)); + }); + + it('allows del to be called on a key that does not exist', function (done) { + client.del('foo', helper.isNumber(0, done)); + }); + + it('allows multiple keys to be deleted', function (done) { + client.mset('foo', 'bar', 'apple', 'banana'); + client.del('foo', 'apple', helper.isNumber(2)); + client.get('foo', helper.isNull()); + client.get('apple', helper.isNull(done)); + }); + + afterEach(function () { + client.end(); + }); + }); + } + + ['javascript', 'hiredis'].forEach(function (parser) { + allTests(parser, "/tmp/redis.sock"); + ['IPv4', 'IPv6'].forEach(function (ip) { + allTests(parser, ip); + }) + }); +}); diff --git a/test/commands/eval.spec.js b/test/commands/eval.spec.js new file mode 100644 index 00000000000..a98efa032e6 --- /dev/null +++ b/test/commands/eval.spec.js @@ -0,0 +1,199 @@ +var assert = require("assert"); +var config = require("../lib/config"); +var crypto = require("crypto"); +var helper = require("../helper"); +var redis = config.redis; + +describe("The 'eval' method", function () { + function allTests(parser, ip) { + var args = config.configureClient(parser, ip); + + describe("using " + parser + " and " + ip, function () { + var client; + + beforeEach(function (done) { + client = redis.createClient.apply(redis.createClient, args); + client.once("error", done); + client.once("connect", function () { + client.flushdb(function (err) { + if (!helper.serverVersionAtLeast(client, [2, 5, 0])) { + err = Error('exec not supported in redis <= 2.5.0') + } + return done(err); + }) + }); + }); + + afterEach(function () { + client.end(); + }); + + it('converts a float to an integer when evaluated', function (done) { + client.eval("return 100.5", 0, helper.isNumber(100, done)); + }); + + it('returns a string', function (done) { + client.eval("return 'hello world'", 0, helper.isString('hello world', done)); + }); + + it('converts boolean true to integer 1', function (done) { + client.eval("return true", 0, helper.isNumber(1, done)); + }); + + it('converts boolean false to null', function (done) { + client.eval("return false", 0, helper.isNull(done)); + }); + + it('converts lua status code to string representation', function (done) { + client.eval("return {ok='fine'}", 0, helper.isString('fine', done)); + }); + + it('converts lua error to an error response', function (done) { + client.eval("return {err='this is an error'}", 0, helper.isError(done)); + }); + + it('represents a lua table appropritely', function (done) { + client.eval("return {1,2,3,'ciao',{1,2}}", 0, function (err, res) { + assert.strictEqual(5, res.length); + assert.strictEqual(1, res[0]); + assert.strictEqual(2, res[1]); + assert.strictEqual(3, res[2]); + assert.strictEqual("ciao", res[3]); + assert.strictEqual(2, res[4].length); + assert.strictEqual(1, res[4][0]); + assert.strictEqual(2, res[4][1]); + return done(); + }); + }); + + it('populates keys and argv correctly', function (done) { + client.eval("return {KEYS[1],KEYS[2],ARGV[1],ARGV[2]}", 2, "a", "b", "c", "d", function (err, res) { + assert.strictEqual(4, res.length); + assert.strictEqual("a", res[0]); + assert.strictEqual("b", res[1]); + assert.strictEqual("c", res[2]); + assert.strictEqual("d", res[3]); + return done(); + }); + }); + + it('allows arguments to be provided in array rather than as multiple parameters', function (done) { + client.eval(["return {KEYS[1],KEYS[2],ARGV[1],ARGV[2]}", 2, "a", "b", "c", "d"], function (err, res) { + assert.strictEqual(4, res.length); + assert.strictEqual("a", res[0]); + assert.strictEqual("b", res[1]); + assert.strictEqual("c", res[2]); + assert.strictEqual("d", res[3]); + return done(); + }); + }); + + describe('evalsha', function () { + var source = "return redis.call('get', 'sha test')"; + var sha = crypto.createHash('sha1').update(source).digest('hex'); + + beforeEach(function (done) { + client.set("sha test", "eval get sha test", function (err, res) { + return done(err); + }); + }); + + it('allows a script to be executed that accesses the redis API', function (done) { + client.eval(source, 0, helper.isString('eval get sha test', done)); + }); + + it('can execute a script if the SHA exists', function (done) { + client.evalsha(sha, 0, helper.isString('eval get sha test', done)); + }); + + it('throws an error if SHA does not exist', function (done) { + client.evalsha('ffffffffffffffffffffffffffffffffffffffff', 0, helper.isError(done)); + }); + }); + + it('allows a key to be incremented, and performs appropriate conversion from LUA type', function (done) { + client.set("incr key", 0, function (err, reply) { + if (err) return done(err); + client.eval("local foo = redis.call('incr','incr key')\n" + "return {type(foo),foo}", 0, function (err, res) { + assert.strictEqual(2, res.length); + assert.strictEqual("number", res[0]); + assert.strictEqual(1, res[1]); + return done(err); + }); + }); + }); + + it('allows a bulk operation to be performed, and performs appropriate conversion from LUA type', function (done) { + client.set("bulk reply key", "bulk reply value", function (err, res) { + client.eval("local foo = redis.call('get','bulk reply key'); return {type(foo),foo}", 0, function (err, res) { + assert.strictEqual(2, res.length); + assert.strictEqual("string", res[0]); + assert.strictEqual("bulk reply value", res[1]); + return done(err); + }); + }); + }); + + it('allows a multi mulk operation to be performed, with the appropriate type conversion', function (done) { + client.multi() + .del("mylist") + .rpush("mylist", "a") + .rpush("mylist", "b") + .rpush("mylist", "c") + .exec(function (err, replies) { + if (err) return done(err); + client.eval("local foo = redis.call('lrange','mylist',0,-1); return {type(foo),foo[1],foo[2],foo[3],# foo}", 0, function (err, res) { + assert.strictEqual(5, res.length); + assert.strictEqual("table", res[0]); + assert.strictEqual("a", res[1]); + assert.strictEqual("b", res[2]); + assert.strictEqual("c", res[3]); + assert.strictEqual(3, res[4]); + return done(err); + }); + }); + }); + + it('returns an appropriate representation of Lua status reply', function (done) { + client.eval("local foo = redis.call('set','mykey','myval'); return {type(foo),foo['ok']}", 0, function (err, res) { + assert.strictEqual(2, res.length); + assert.strictEqual("table", res[0]); + assert.strictEqual("OK", res[1]); + return done(err); + }); + }); + + it('returns an appropriate representation of a Lua error reply', function (done) { + client.set("error reply key", "error reply value", function (err, res) { + if (err) return done(err); + client.eval("local foo = redis.pcall('incr','error reply key'); return {type(foo),foo['err']}", 0, function (err, res) { + assert.strictEqual(2, res.length); + assert.strictEqual("table", res[0]); + assert.strictEqual("ERR value is not an integer or out of range", res[1]); + return done(err); + }); + }); + }); + + it('returns an appropriate representation of a Lua nil reply', function (done) { + client.del("nil reply key", function (err, res) { + if (err) return done(err); + client.eval("local foo = redis.call('get','nil reply key'); return {type(foo),foo == false}", 0, function (err, res) { + if (err) throw err; + assert.strictEqual(2, res.length); + assert.strictEqual("boolean", res[0]); + assert.strictEqual(1, res[1]); + return done(err); + }); + }); + }); + }); + } + + ['javascript', 'hiredis'].forEach(function (parser) { + allTests(parser, "/tmp/redis.sock"); + ['IPv4', 'IPv6'].forEach(function (ip) { + allTests(parser, ip); + }) + }); +}); diff --git a/test/commands/exits.spec.js b/test/commands/exits.spec.js new file mode 100644 index 00000000000..b3f04badcc7 --- /dev/null +++ b/test/commands/exits.spec.js @@ -0,0 +1,43 @@ +var assert = require("assert"); +var config = require("../lib/config"); +var helper = require("../helper"); +var redis = config.redis; + +describe("The 'exits' method", function () { + + function allTests(parser, ip) { + var args = config.configureClient(parser, ip); + + describe("using " + parser + " and " + ip, function () { + var client; + + beforeEach(function (done) { + client = redis.createClient.apply(redis.createClient, args); + client.once("error", done); + client.once("connect", function () { + client.flushdb(done); + }); + }); + + it('returns 1 if the key exists', function (done) { + client.set('foo', 'bar'); + client.exists('foo', helper.isNumber(1, done)); + }); + + it('returns 0 if the key does not exist', function (done) { + client.exists('bar', helper.isNumber(0, done)); + }); + + afterEach(function () { + client.end(); + }); + }); + } + + ['javascript', 'hiredis'].forEach(function (parser) { + allTests(parser, "/tmp/redis.sock"); + ['IPv4', 'IPv6'].forEach(function (ip) { + allTests(parser, ip); + }) + }); +}); diff --git a/test/mocha/commands/flushdb.spec.js b/test/commands/flushdb.spec.js similarity index 81% rename from test/mocha/commands/flushdb.spec.js rename to test/commands/flushdb.spec.js index de325a800d5..8df39f4752a 100644 --- a/test/mocha/commands/flushdb.spec.js +++ b/test/commands/flushdb.spec.js @@ -1,27 +1,12 @@ var async = require('async'); var assert = require('assert'); -var config = require("../../lib/config"); -var nodeAssert = require('../../lib/nodeify-assertions'); +var config = require("../lib/config"); +var helper = require('../helper'); var redis = config.redis; -var RedisProcess = require("../../lib/redis-process"); var uuid = require('uuid'); describe("The 'flushdb' method", function () { - var rp; - before(function (done) { - RedisProcess.start(function (err, _rp) { - rp = _rp; - return done(err); - }); - }) - - function removeMochaListener () { - var mochaListener = process.listeners('uncaughtException').pop(); - process.removeListener('uncaughtException', mochaListener); - return mochaListener; - } - function allTests(parser, ip) { var args = config.configureClient(parser, ip); @@ -76,12 +61,12 @@ describe("The 'flushdb' method", function () { beforeEach(function (done) { async.parallel([function (next) { client.mset(key, uuid.v4(), key2, uuid.v4(), function (err, res) { - nodeAssert.isNotError()(err, res); + helper.isNotError()(err, res); next(err); }); }, function (next) { client.dbsize([], function (err, res) { - nodeAssert.isType.positiveNumber()(err, res); + helper.isType.positiveNumber()(err, res); oldSize = res; next(err); }); @@ -91,7 +76,7 @@ describe("The 'flushdb' method", function () { } client.flushdb(function (err, res) { - nodeAssert.isString("OK")(err, res); + helper.isString("OK")(err, res); done(err); }); }); @@ -110,8 +95,8 @@ describe("The 'flushdb' method", function () { it("results in a db size of zero", function (done) { client.dbsize([], function (err, res) { - nodeAssert.isNotError()(err, res); - nodeAssert.isType.number()(err, res); + helper.isNotError()(err, res); + helper.isType.number()(err, res); assert.strictEqual(0, res, "Flushing db should result in db size 0"); done(); }); @@ -127,8 +112,4 @@ describe("The 'flushdb' method", function () { allTests(parser, ip); }) }); - - after(function (done) { - if (rp) rp.stop(done); - }); }); diff --git a/test/mocha/commands/get.spec.js b/test/commands/get.spec.js similarity index 78% rename from test/mocha/commands/get.spec.js rename to test/commands/get.spec.js index d232a6ecb12..31502c2c4b6 100644 --- a/test/mocha/commands/get.spec.js +++ b/test/commands/get.spec.js @@ -1,27 +1,12 @@ var async = require('async'); var assert = require('assert'); -var config = require("../../lib/config"); -var nodeAssert = require('../../lib/nodeify-assertions'); +var config = require("../lib/config"); +var helper = require('../helper'); var redis = config.redis; -var RedisProcess = require("../../lib/redis-process"); var uuid = require('uuid'); describe("The 'get' method", function () { - var rp; - before(function (done) { - RedisProcess.start(function (err, _rp) { - rp = _rp; - return done(err); - }); - }) - - function removeMochaListener () { - var mochaListener = process.listeners('uncaughtException').pop(); - process.removeListener('uncaughtException', mochaListener); - return mochaListener; - } - function allTests(parser, ip) { var args = config.configureClient(parser, ip); @@ -73,14 +58,14 @@ describe("The 'get' method", function () { describe("when the key exists in Redis", function () { beforeEach(function (done) { client.set(key, value, function (err, res) { - nodeAssert.isNotError()(err, res); + helper.isNotError()(err, res); done(); }); }); it("gets the value correctly", function (done) { client.get(key, function (err, res) { - nodeAssert.isString(value)(err, res); + helper.isString(value)(err, res); done(err); }); }); @@ -89,7 +74,7 @@ describe("The 'get' method", function () { describe("when the key does not exist in Redis", function () { it("gets a null value", function (done) { client.get(key, function (err, res) { - nodeAssert.isNull()(err, res); + helper.isNull()(err, res); done(err); }); }); @@ -104,8 +89,4 @@ describe("The 'get' method", function () { allTests(parser, ip); }) }); - - after(function (done) { - if (rp) rp.stop(done); - }); }); diff --git a/test/mocha/commands/getset.spec.js b/test/commands/getset.spec.js similarity index 77% rename from test/mocha/commands/getset.spec.js rename to test/commands/getset.spec.js index ba76384f2c0..a8e90ecfa2f 100644 --- a/test/mocha/commands/getset.spec.js +++ b/test/commands/getset.spec.js @@ -1,27 +1,12 @@ var async = require('async'); var assert = require('assert'); -var config = require("../../lib/config"); -var nodeAssert = require('../../lib/nodeify-assertions'); +var config = require("../lib/config"); +var helper = require('../helper'); var redis = config.redis; -var RedisProcess = require("../../lib/redis-process"); var uuid = require('uuid'); describe("The 'getset' method", function () { - var rp; - before(function (done) { - RedisProcess.start(function (err, _rp) { - rp = _rp; - return done(err); - }); - }) - - function removeMochaListener () { - var mochaListener = process.listeners('uncaughtException').pop(); - process.removeListener('uncaughtException', mochaListener); - return mochaListener; - } - function allTests(parser, ip) { var args = config.configureClient(parser, ip); @@ -74,16 +59,16 @@ describe("The 'getset' method", function () { describe("when the key exists in Redis", function () { beforeEach(function (done) { client.set(key, value, function (err, res) { - nodeAssert.isNotError()(err, res); + helper.isNotError()(err, res); done(); }); }); it("gets the value correctly", function (done) { client.getset(key, value2, function (err, res) { - nodeAssert.isString(value)(err, res); + helper.isString(value)(err, res); client.get(key, function (err, res) { - nodeAssert.isString(value2)(err, res); + helper.isString(value2)(err, res); done(err); }); }); @@ -93,7 +78,7 @@ describe("The 'getset' method", function () { describe("when the key does not exist in Redis", function () { it("gets a null value", function (done) { client.getset(key, value, function (err, res) { - nodeAssert.isNull()(err, res); + helper.isNull()(err, res); done(err); }); }); @@ -108,8 +93,4 @@ describe("The 'getset' method", function () { allTests(parser, ip); }) }); - - after(function (done) { - if (rp) rp.stop(done); - }); }); diff --git a/test/commands/hgetall.spec.js b/test/commands/hgetall.spec.js new file mode 100644 index 00000000000..c49988157ee --- /dev/null +++ b/test/commands/hgetall.spec.js @@ -0,0 +1,91 @@ +var assert = require("assert"); +var config = require("../lib/config"); +var helper = require("../helper"); +var redis = config.redis; + +describe("The 'hgetall' method", function () { + + function allTests(parser, ip) { + describe("using " + parser + " and " + ip, function () { + var client; + + describe('regular client', function () { + var args = config.configureClient(parser, ip); + + beforeEach(function (done) { + client = redis.createClient.apply(redis.createClient, args); + client.once("error", done); + client.once("connect", function () { + client.flushdb(done); + }); + }); + + it('handles simple keys and values', function (done) { + client.hmset(["hosts", "mjr", "1", "another", "23", "home", "1234"], helper.isString("OK")); + client.HGETALL(["hosts"], function (err, obj) { + assert.strictEqual(3, Object.keys(obj).length); + assert.strictEqual("1", obj.mjr.toString()); + assert.strictEqual("23", obj.another.toString()); + assert.strictEqual("1234", obj.home.toString()); + return done(err); + }); + }); + + it('handles fetching keys set using an object', function (done) { + client.hmset("msg_test", {message: "hello"}, helper.isString("OK")); + client.hgetall("msg_test", function (err, obj) { + assert.strictEqual(1, Object.keys(obj).length); + assert.strictEqual(obj.message, "hello"); + return done(err); + }); + }); + + it('handles fetching a messing key', function (done) { + client.hgetall("missing", function (err, obj) { + assert.strictEqual(null, obj); + return done(err); + }); + }); + }); + + describe('binary client', function () { + var client; + var args = config.configureClient(parser, ip, { + return_buffers: true + }); + + beforeEach(function (done) { + client = redis.createClient.apply(redis.createClient, args); + client.once("error", done); + client.once("connect", function () { + client.flushdb(done); + }); + }); + + it('returns binary results', function (done) { + client.hmset(["bhosts", "mjr", "1", "another", "23", "home", "1234", new Buffer([0xAA, 0xBB, 0x00, 0xF0]), new Buffer([0xCC, 0xDD, 0x00, 0xF0])], helper.isString("OK")); + client.HGETALL(["bhosts"], function (err, obj) { + assert.strictEqual(4, Object.keys(obj).length); + assert.strictEqual("1", obj.mjr.toString()); + assert.strictEqual("23", obj.another.toString()); + assert.strictEqual("1234", obj.home.toString()); + assert.strictEqual((new Buffer([0xAA, 0xBB, 0x00, 0xF0])).toString('binary'), Object.keys(obj)[3]); + assert.strictEqual((new Buffer([0xCC, 0xDD, 0x00, 0xF0])).toString('binary'), obj[(new Buffer([0xAA, 0xBB, 0x00, 0xF0])).toString('binary')].toString('binary')); + return done(err); + }); + }); + }); + + afterEach(function () { + client.end(); + }); + }); + } + + ['javascript', 'hiredis'].forEach(function (parser) { + allTests(parser, "/tmp/redis.sock"); + ['IPv4', 'IPv6'].forEach(function (ip) { + allTests(parser, ip); + }) + }); +}); diff --git a/test/commands/hincrby.spec.js b/test/commands/hincrby.spec.js new file mode 100644 index 00000000000..f137405f782 --- /dev/null +++ b/test/commands/hincrby.spec.js @@ -0,0 +1,48 @@ +var assert = require("assert"); +var config = require("../lib/config"); +var helper = require("../helper"); +var redis = config.redis; + +describe("The 'hincrby' method", function () { + + function allTests(parser, ip) { + var args = config.configureClient(parser, ip); + + describe("using " + parser + " and " + ip, function () { + var client; + var hash = "test hash"; + + beforeEach(function (done) { + client = redis.createClient.apply(redis.createClient, args); + client.once("error", done); + client.once("connect", function () { + client.flushdb(done); + }); + }); + + it('increments a key that has already been set', function (done) { + var field = "field 1"; + + client.HSET(hash, field, 33); + client.HINCRBY(hash, field, 10, helper.isNumber(43, done)); + }); + + it('increments a key that has not been set', function (done) { + var field = "field 2"; + + client.HINCRBY(hash, field, 10, helper.isNumber(10, done)); + }); + + afterEach(function () { + client.end(); + }); + }); + } + + ['javascript', 'hiredis'].forEach(function (parser) { + allTests(parser, "/tmp/redis.sock"); + ['IPv4', 'IPv6'].forEach(function (ip) { + allTests(parser, ip); + }) + }); +}); diff --git a/test/commands/hlen.spec.js b/test/commands/hlen.spec.js new file mode 100644 index 00000000000..11905809c1b --- /dev/null +++ b/test/commands/hlen.spec.js @@ -0,0 +1,46 @@ +var assert = require("assert"); +var config = require("../lib/config"); +var helper = require("../helper"); +var redis = config.redis; + +describe("The 'hlen' method", function () { + + function allTests(parser, ip) { + var args = config.configureClient(parser, ip); + + describe("using " + parser + " and " + ip, function () { + var client; + + beforeEach(function (done) { + client = redis.createClient.apply(redis.createClient, args); + client.once("error", done); + client.once("connect", function () { + client.flushdb(done); + }); + }); + + it('reports the count of keys', function (done) { + var hash = "test hash"; + var field1 = new Buffer("0123456789"); + var value1 = new Buffer("abcdefghij"); + var field2 = new Buffer(0); + var value2 = new Buffer(0); + + client.HSET(hash, field1, value1, helper.isNumber(1)); + client.HSET(hash, field2, value2, helper.isNumber(1)); + client.HLEN(hash, helper.isNumber(2, done)); + }); + + afterEach(function () { + client.end(); + }); + }); + } + + ['javascript', 'hiredis'].forEach(function (parser) { + allTests(parser, "/tmp/redis.sock"); + ['IPv4', 'IPv6'].forEach(function (ip) { + allTests(parser, ip); + }) + }); +}); diff --git a/test/commands/hmget.spec.js b/test/commands/hmget.spec.js new file mode 100644 index 00000000000..fbced263bbb --- /dev/null +++ b/test/commands/hmget.spec.js @@ -0,0 +1,68 @@ +var assert = require("assert"); +var config = require("../lib/config"); +var helper = require("../helper"); +var redis = config.redis; + +describe("The 'hmget' method", function () { + + function allTests(parser, ip) { + var args = config.configureClient(parser, ip); + + describe("using " + parser + " and " + ip, function () { + var client; + var hash = 'test hash'; + + beforeEach(function (done) { + client = redis.createClient.apply(redis.createClient, args); + client.once("error", done); + client.once("connect", function () { + client.flushdb(); + client.HMSET(hash, {"0123456789": "abcdefghij", "some manner of key": "a type of value"}, helper.isString('OK')); + return done(); + }); + }); + + it('allows keys to be specified using multiple arguments', function (done) { + client.HMGET(hash, "0123456789", "some manner of key", function (err, reply) { + assert.strictEqual("abcdefghij", reply[0].toString()); + assert.strictEqual("a type of value", reply[1].toString()); + return done(err); + }); + }); + + it('allows keys to be specified by passing an array', function (done) { + client.HMGET(hash, ["0123456789", "some manner of key"], function (err, reply) { + assert.strictEqual("abcdefghij", reply[0].toString()); + assert.strictEqual("a type of value", reply[1].toString()); + return done(err); + }); + }); + + it('allows a single key to be specified in an array', function (done) { + client.HMGET(hash, ["0123456789"], function (err, reply) { + assert.strictEqual("abcdefghij", reply[0].toString()); + return done(err); + }); + }); + + it('allows keys to be specified that have not yet been set', function (done) { + client.HMGET(hash, "missing thing", "another missing thing", function (err, reply) { + assert.strictEqual(null, reply[0]); + assert.strictEqual(null, reply[1]); + return done(err); + }); + }); + + afterEach(function () { + client.end(); + }); + }); + } + + ['javascript', 'hiredis'].forEach(function (parser) { + allTests(parser, "/tmp/redis.sock"); + ['IPv4', 'IPv6'].forEach(function (ip) { + allTests(parser, ip); + }) + }); +}); diff --git a/test/commands/hmset.spec.js b/test/commands/hmset.spec.js new file mode 100644 index 00000000000..d0f23eb066c --- /dev/null +++ b/test/commands/hmset.spec.js @@ -0,0 +1,61 @@ +var assert = require("assert"); +var config = require("../lib/config"); +var helper = require("../helper"); +var redis = config.redis; + +describe("The 'hmset' method", function () { + + function allTests(parser, ip) { + var args = config.configureClient(parser, ip); + + describe("using " + parser + " and " + ip, function () { + var client; + var hash = 'test hash'; + + beforeEach(function (done) { + client = redis.createClient.apply(redis.createClient, args); + client.once("error", done); + client.once("connect", function () { + client.flushdb(done); + }); + }); + + it('handles redis-style syntax', function (done) { + client.HMSET(hash, "0123456789", "abcdefghij", "some manner of key", "a type of value", helper.isString('OK')); + client.HGETALL(hash, function (err, obj) { + assert.equal(obj['0123456789'], 'abcdefghij'); + assert.equal(obj['some manner of key'], 'a type of value'); + return done(err); + }) + }); + + it('handles object-style syntax', function (done) { + client.HMSET(hash, {"0123456789": "abcdefghij", "some manner of key": "a type of value"}, helper.isString('OK')); + client.HGETALL(hash, function (err, obj) { + assert.equal(obj['0123456789'], 'abcdefghij'); + assert.equal(obj['some manner of key'], 'a type of value'); + return done(err); + }) + }); + + it('allows a numeric key', function (done) { + client.HMSET(hash, 99, 'banana', helper.isString('OK')); + client.HGETALL(hash, function (err, obj) { + assert.equal(obj['99'], 'banana'); + return done(err); + }); + }); + + afterEach(function () { + client.end(); + }); + }); + } + + ['javascript', 'hiredis'].forEach(function (parser) { + allTests(parser, "/tmp/redis.sock"); + ['IPv4', 'IPv6'].forEach(function (ip) { + allTests(parser, ip); + }) + }); +}); diff --git a/test/commands/hset.spec.js b/test/commands/hset.spec.js new file mode 100644 index 00000000000..0ce2432ea9d --- /dev/null +++ b/test/commands/hset.spec.js @@ -0,0 +1,70 @@ +var assert = require("assert"); +var config = require("../lib/config"); +var helper = require("../helper"); +var redis = config.redis; + +describe("The 'hset' method", function () { + + function allTests(parser, ip) { + var args = config.configureClient(parser, ip); + + describe("using " + parser + " and " + ip, function () { + var client; + var hash = 'test hash'; + + beforeEach(function (done) { + client = redis.createClient.apply(redis.createClient, args); + client.once("error", done); + client.once("connect", function () { + client.flushdb(done); + }); + }); + + it('allows a value to be set in a hash', function (done) { + var field = new Buffer("0123456789"); + var value = new Buffer("abcdefghij"); + + client.HSET(hash, field, value, helper.isNumber(1)); + client.HGET(hash, field, helper.isString(value.toString(), done)); + }); + + it('handles an empty value', function (done) { + var field = new Buffer("0123456789"); + var value = new Buffer(0); + + client.HSET(hash, field, value, helper.isNumber(1)); + client.HGET([hash, field], helper.isString("", done)); + }); + + it('handles empty key and value', function (done) { + var field = new Buffer(0); + var value = new Buffer(0); + client.HSET([hash, field, value], function (err, res) { + assert.strictEqual(res, 1); + client.HSET(hash, field, value, helper.isNumber(0, done)); + }); + }); + + it('does not error when a buffer and array are set as fields on the same hash', function (done) { + var hash = "test hash" + var field1 = "buffer" + var value1 = new Buffer("abcdefghij") + var field2 = "array" + var value2 = ["array contents"] + + client.HMSET(hash, field1, value1, field2, value2, helper.isString("OK", done)); + }); + + afterEach(function () { + client.end(); + }); + }); + } + + ['javascript', 'hiredis'].forEach(function (parser) { + allTests(parser, "/tmp/redis.sock"); + ['IPv4', 'IPv6'].forEach(function (ip) { + allTests(parser, ip); + }) + }); +}); diff --git a/test/mocha/commands/incr.spec.js b/test/commands/incr.spec.js similarity index 79% rename from test/mocha/commands/incr.spec.js rename to test/commands/incr.spec.js index ae4534dd304..bdd22bacf16 100644 --- a/test/mocha/commands/incr.spec.js +++ b/test/commands/incr.spec.js @@ -1,27 +1,12 @@ var async = require('async'); var assert = require('assert'); -var config = require("../../lib/config"); -var nodeAssert = require('../../lib/nodeify-assertions'); +var config = require("../lib/config"); +var helper = require('../helper'); var redis = config.redis; -var RedisProcess = require("../../lib/redis-process"); var uuid = require('uuid'); describe("The 'incr' method", function () { - var rp; - before(function (done) { - RedisProcess.start(function (err, _rp) { - rp = _rp; - return done(err); - }); - }) - - function removeMochaListener () { - var mochaListener = process.listeners('uncaughtException').pop(); - process.removeListener('uncaughtException', mochaListener); - return mochaListener; - } - function allTests(parser, ip) { var args = config.configureClient(parser, ip); @@ -36,7 +21,7 @@ describe("The 'incr' method", function () { client.once("error", done); client.once("connect", function () { client.set(key, "9007199254740992", function (err, res) { - nodeAssert.isNotError()(err, res); + helper.isNotError()(err, res); client.quit(); }); }); @@ -45,6 +30,10 @@ describe("The 'incr' method", function () { }); }); + afterEach(function () { + client.end(); + }); + it("reports an error", function (done) { client.incr(function (err, res) { assert.equal(err.message, 'Redis connection gone from end event.'); @@ -70,7 +59,7 @@ describe("The 'incr' method", function () { client.once("error", done); client.once("connect", function () { client.set(key, "9007199254740992", function (err, res) { - nodeAssert.isNotError()(err, res); + helper.isNotError()(err, res); done(); }); }); @@ -82,7 +71,7 @@ describe("The 'incr' method", function () { it("changes the last digit from 2 to 3", function (done) { client.incr(key, function (err, res) { - nodeAssert.isString("9007199254740993")(err, res); + helper.isString("9007199254740993")(err, res); done(err); }); }); @@ -90,7 +79,7 @@ describe("The 'incr' method", function () { describe("and we call it again", function () { it("changes the last digit from 3 to 4", function (done) { client.incr(key, function (err, res) { - nodeAssert.isString("9007199254740994")(err, res); + helper.isString("9007199254740994")(err, res); done(err); }); }); @@ -98,7 +87,7 @@ describe("The 'incr' method", function () { describe("and again", function () { it("changes the last digit from 4 to 5", function (done) { client.incr(key, function (err, res) { - nodeAssert.isString("9007199254740995")(err, res); + helper.isString("9007199254740995")(err, res); done(err); }); }); @@ -106,7 +95,7 @@ describe("The 'incr' method", function () { describe("and again", function () { it("changes the last digit from 5 to 6", function (done) { client.incr(key, function (err, res) { - nodeAssert.isString("9007199254740996")(err, res); + helper.isString("9007199254740996")(err, res); done(err); }); }); @@ -114,7 +103,7 @@ describe("The 'incr' method", function () { describe("and again", function () { it("changes the last digit from 6 to 7", function (done) { client.incr(key, function (err, res) { - nodeAssert.isString("9007199254740997")(err, res); + helper.isString("9007199254740997")(err, res); done(err); }); }); @@ -132,8 +121,4 @@ describe("The 'incr' method", function () { allTests(parser, ip); }) }); - - after(function (done) { - if (rp) rp.stop(done); - }); }); diff --git a/test/commands/keys.spec.js b/test/commands/keys.spec.js new file mode 100644 index 00000000000..b1f5f9cb51a --- /dev/null +++ b/test/commands/keys.spec.js @@ -0,0 +1,76 @@ +var assert = require("assert"); +var config = require("../lib/config"); +var crypto = require("crypto"); +var helper = require("../helper"); +var redis = config.redis; + +describe("The 'keys' method", function () { + + function allTests(parser, ip) { + var args = config.configureClient(parser, ip); + + describe("using " + parser + " and " + ip, function () { + var client; + + beforeEach(function (done) { + client = redis.createClient.apply(redis.createClient, args); + client.once("error", done); + client.once("connect", function () { + client.flushdb(done); + }); + }); + + it('returns matching keys', function (done) { + client.mset(["test keys 1", "test val 1", "test keys 2", "test val 2"], helper.isString("OK")); + client.KEYS(["test keys*"], function (err, results) { + assert.strictEqual(2, results.length); + assert.ok(~results.indexOf("test keys 1")); + assert.ok(~results.indexOf("test keys 2")); + return done(err); + }); + }); + + it('handles a large packet size', function (done) { + var keys_values = []; + + for (var i = 0; i < 200; i++) { + var key_value = [ + "multibulk:" + crypto.randomBytes(256).toString("hex"), // use long strings as keys to ensure generation of large packet + "test val " + i + ]; + keys_values.push(key_value); + } + + client.mset(keys_values.reduce(function(a, b) { + return a.concat(b); + }), helper.isString("OK")); + + client.KEYS("multibulk:*", function(err, results) { + assert.deepEqual(keys_values.map(function(val) { + return val[0]; + }).sort(), results.sort()); + return done(err); + }); + }); + + it('handles an empty response', function (done) { + client.KEYS(['users:*'], function (err, results) { + assert.strictEqual(results.length, 0); + assert.ok(Array.isArray(results)); + return done(err); + }); + }); + + afterEach(function () { + client.end(); + }); + }); + } + + ['javascript', 'hiredis'].forEach(function (parser) { + allTests(parser, "/tmp/redis.sock"); + ['IPv4', 'IPv6'].forEach(function (ip) { + allTests(parser, ip); + }) + }); +}); diff --git a/test/commands/mget.spec.js b/test/commands/mget.spec.js new file mode 100644 index 00000000000..1169b5949fc --- /dev/null +++ b/test/commands/mget.spec.js @@ -0,0 +1,66 @@ +var assert = require("assert"); +var config = require("../lib/config"); +var helper = require("../helper"); +var redis = config.redis; + +describe("The 'mget' method", function () { + + function allTests(parser, ip) { + var args = config.configureClient(parser, ip); + + describe("using " + parser + " and " + ip, function () { + var client; + + beforeEach(function (done) { + client = redis.createClient.apply(redis.createClient, args); + client.once("error", done); + client.once("connect", function () { + client.flushdb(); + client.mset(["mget keys 1", "mget val 1", "mget keys 2", "mget val 2", "mget keys 3", "mget val 3"], done); + }); + }); + + it('handles fetching multiple keys in argument form', function (done) { + client.mset(["mget keys 1", "mget val 1", "mget keys 2", "mget val 2", "mget keys 3", "mget val 3"], helper.isString("OK")); + client.MGET("mget keys 1", "mget keys 2", "mget keys 3", function (err, results) { + assert.strictEqual(3, results.length); + assert.strictEqual("mget val 1", results[0].toString()); + assert.strictEqual("mget val 2", results[1].toString()); + assert.strictEqual("mget val 3", results[2].toString()); + return done(err); + }); + }); + + it('handles fetching multiple keys via an array', function (done) { + client.MGET(["mget keys 1", "mget keys 2", "mget keys 3"], function (err, results) { + assert.strictEqual("mget val 1", results[0].toString()); + assert.strictEqual("mget val 2", results[1].toString()); + assert.strictEqual("mget val 3", results[2].toString()); + return done(err); + }); + }); + + it('handles fetching multiple keys, when some keys do not exist', function (done) { + client.MGET(["mget keys 1", "some random shit", "mget keys 2", "mget keys 3"], function (err, results) { + assert.strictEqual(4, results.length); + assert.strictEqual("mget val 1", results[0].toString()); + assert.strictEqual(null, results[1]); + assert.strictEqual("mget val 2", results[2].toString()); + assert.strictEqual("mget val 3", results[3].toString()); + return done(err); + }); + }); + + afterEach(function () { + client.end(); + }); + }); + } + + ['javascript', 'hiredis'].forEach(function (parser) { + allTests(parser, "/tmp/redis.sock"); + ['IPv4', 'IPv6'].forEach(function (ip) { + allTests(parser, ip); + }) + }); +}); diff --git a/test/mocha/commands/mset.spec.js b/test/commands/mset.spec.js similarity index 85% rename from test/mocha/commands/mset.spec.js rename to test/commands/mset.spec.js index b1f4f367b04..1523797a3c3 100644 --- a/test/mocha/commands/mset.spec.js +++ b/test/commands/mset.spec.js @@ -1,21 +1,12 @@ var async = require('async'); var assert = require('assert'); -var config = require("../../lib/config"); -var nodeAssert = require('../../lib/nodeify-assertions'); +var config = require("../lib/config"); +var helper = require('../helper'); var redis = config.redis; -var RedisProcess = require("../../lib/redis-process"); var uuid = require('uuid'); describe("The 'mset' method", function () { - var rp; - before(function (done) { - RedisProcess.start(function (err, _rp) { - rp = _rp; - return done(err); - }); - }) - function removeMochaListener () { var mochaListener = process.listeners('uncaughtException').pop(); process.removeListener('uncaughtException', mochaListener); @@ -76,15 +67,15 @@ describe("The 'mset' method", function () { describe("with valid parameters", function () { it("sets the value correctly", function (done) { client.mset(key, value, key2, value2, function (err, res) { - nodeAssert.isNotError()(err, res); + helper.isNotError()(err, res); async.parallel([function (next) { client.get(key, function (err, res) { - nodeAssert.isString(value)(err, res); + helper.isString(value)(err, res); next(); }); }, function (next) { client.get(key2, function (err, res) { - nodeAssert.isString(value2)(err, res); + helper.isString(value2)(err, res); next(); }); }], function (err) { @@ -97,7 +88,7 @@ describe("The 'mset' method", function () { describe("with undefined 'key' parameter and missing 'value' parameter", function () { it("reports an error", function (done) { client.mset(undefined, function (err, res) { - nodeAssert.isError()(err, null); + helper.isError()(err, null); done(); }); }); @@ -106,7 +97,7 @@ describe("The 'mset' method", function () { describe("with undefined 'key' and defined 'value' parameters", function () { it("reports an error", function () { client.mset(undefined, value, undefined, value2, function (err, res) { - nodeAssert.isError()(err, null); + helper.isError()(err, null); done(); }); }); @@ -121,12 +112,12 @@ describe("The 'mset' method", function () { setTimeout(function () { async.parallel([function (next) { client.get(key, function (err, res) { - nodeAssert.isString(value)(err, res); + helper.isString(value)(err, res); next(); }); }, function (next) { client.get(key2, function (err, res) { - nodeAssert.isString(value2)(err, res); + helper.isString(value2)(err, res); next(); }); }], function (err) { @@ -143,7 +134,7 @@ describe("The 'mset' method", function () { process.once('uncaughtException', function (err) { process.on('uncaughtException', mochaListener); - nodeAssert.isError()(err, null); + helper.isError()(err, null); return done(); }); @@ -157,7 +148,7 @@ describe("The 'mset' method", function () { process.once('uncaughtException', function (err) { process.on('uncaughtException', mochaListener); - nodeAssert.isError()(err, null); + helper.isError()(err, null); }); client.mset(undefined, value, undefined, value2); @@ -174,8 +165,4 @@ describe("The 'mset' method", function () { allTests(parser, ip); }) }); - - after(function (done) { - if (rp) rp.stop(done); - }); }); diff --git a/test/commands/msetnx.spec.js b/test/commands/msetnx.spec.js new file mode 100644 index 00000000000..ddbb893c1ae --- /dev/null +++ b/test/commands/msetnx.spec.js @@ -0,0 +1,46 @@ +var assert = require("assert"); +var config = require("../lib/config"); +var helper = require("../helper"); +var redis = config.redis; + +describe("The 'msetnx' method", function () { + + function allTests(parser, ip) { + var args = config.configureClient(parser, ip); + + describe("using " + parser + " and " + ip, function () { + var client; + + beforeEach(function (done) { + client = redis.createClient.apply(redis.createClient, args); + client.once("error", done); + client.once("connect", function () { + client.flushdb(done); + }); + }); + + it('if any keys exist entire operation fails', function (done) { + client.mset(["mset1", "val1", "mset2", "val2", "mset3", "val3"], helper.isString("OK")); + client.MSETNX(["mset3", "val3", "mset4", "val4"], helper.isNumber(0)); + client.exists(["mset4"], helper.isNumber(0, done)); + }); + + it('sets multiple keys if all keys are not set', function (done) { + client.MSETNX(["mset3", "val3", "mset4", "val4"], helper.isNumber(1)); + client.exists(["mset3"], helper.isNumber(1)); + client.exists(["mset3"], helper.isNumber(1, done)); + }); + + afterEach(function () { + client.end(); + }); + }); + } + + ['javascript', 'hiredis'].forEach(function (parser) { + allTests(parser, "/tmp/redis.sock"); + ['IPv4', 'IPv6'].forEach(function (ip) { + allTests(parser, ip); + }) + }); +}); diff --git a/test/mocha/commands/multi.spec.js b/test/commands/multi.spec.js similarity index 82% rename from test/mocha/commands/multi.spec.js rename to test/commands/multi.spec.js index ba8be4fcbf7..21526334a1c 100644 --- a/test/mocha/commands/multi.spec.js +++ b/test/commands/multi.spec.js @@ -1,27 +1,12 @@ var async = require('async'); var assert = require('assert'); -var config = require("../../lib/config"); -var nodeAssert = require('../../lib/nodeify-assertions'); +var config = require("../lib/config"); +var helper = require('../helper'); var redis = config.redis; -var RedisProcess = require("../../lib/redis-process"); var uuid = require('uuid'); describe("The 'multi' method", function () { - var rp; - before(function (done) { - RedisProcess.start(function (err, _rp) { - rp = _rp; - return done(err); - }); - }) - - function removeMochaListener () { - var mochaListener = process.listeners('uncaughtException').pop(); - process.removeListener('uncaughtException', mochaListener); - return mochaListener; - } - function allTests(parser, ip) { var args = config.configureClient(parser, ip); @@ -63,7 +48,7 @@ describe("The 'multi' method", function () { client = redis.createClient.apply(redis.createClient, args); client.once("error", done); client.once("connect", function () { - client.del('multifoo', 'multibar', 'multifoo_8', 'multibar_8', function (err) { + client.flushdb(function (err) { return done(err); }) }); @@ -78,24 +63,24 @@ describe("The 'multi' method", function () { // Provoke an error at queue time multi1 = client.multi(); - multi1.mset("multifoo", "10", "multibar", "20", nodeAssert.isString("OK")); - multi1.set("foo2", nodeAssert.isError()); - multi1.incr("multifoo", nodeAssert.isNumber(11)); - multi1.incr("multibar", nodeAssert.isNumber(21)); + multi1.mset("multifoo", "10", "multibar", "20", helper.isString("OK")); + multi1.set("foo2", helper.isError()); + multi1.incr("multifoo", helper.isNumber(11)); + multi1.incr("multibar", helper.isNumber(21)); multi1.exec(function () { // Redis 2.6.5+ will abort transactions with errors // see: http://redis.io/topics/transactions var multibar_expected = 22; var multifoo_expected = 12; - if (nodeAssert.serverVersionAtLeast(client, [2, 6, 5])) { + if (helper.serverVersionAtLeast(client, [2, 6, 5])) { multibar_expected = 1; multifoo_expected = 1; } // Confirm that the previous command, while containing an error, still worked. multi2 = client.multi(); - multi2.incr("multibar", nodeAssert.isNumber(multibar_expected)); - multi2.incr("multifoo", nodeAssert.isNumber(multifoo_expected)); + multi2.incr("multibar", helper.isNumber(multibar_expected)); + multi2.incr("multifoo", helper.isNumber(multifoo_expected)); multi2.exec(function (err, replies) { assert.strictEqual(multibar_expected, replies[0]); assert.strictEqual(multifoo_expected, replies[1]); @@ -108,25 +93,25 @@ describe("The 'multi' method", function () { // perhaps @mranney can clarify? it('roles back a transaction when an error was provoked at queue time', function (done) { multi1 = client.multi(); - multi1.mset("multifoo_8", "10", "multibar_8", "20", nodeAssert.isString("OK")); - multi1.set("foo2", nodeAssert.isError()); - multi1.set("foo3", nodeAssert.isError()); - multi1.incr("multifoo_8", nodeAssert.isNumber(11)); - multi1.incr("multibar_8", nodeAssert.isNumber(21)); + multi1.mset("multifoo_8", "10", "multibar_8", "20", helper.isString("OK")); + multi1.set("foo2", helper.isError()); + multi1.set("foo3", helper.isError()); + multi1.incr("multifoo_8", helper.isNumber(11)); + multi1.incr("multibar_8", helper.isNumber(21)); multi1.exec(function () { // Redis 2.6.5+ will abort transactions with errors // see: http://redis.io/topics/transactions var multibar_expected = 22; var multifoo_expected = 12; - if (nodeAssert.serverVersionAtLeast(client, [2, 6, 5])) { + if (helper.serverVersionAtLeast(client, [2, 6, 5])) { multibar_expected = 1; multifoo_expected = 1; } // Confirm that the previous command, while containing an error, still worked. multi2 = client.multi(); - multi2.incr("multibar_8", nodeAssert.isNumber(multibar_expected)); - multi2.incr("multifoo_8", nodeAssert.isNumber(multifoo_expected)); + multi2.incr("multibar_8", helper.isNumber(multibar_expected)); + multi2.incr("multifoo_8", helper.isNumber(multifoo_expected)); multi2.exec(function (err, replies) { assert.strictEqual(multibar_expected, replies[0]); assert.strictEqual(multifoo_expected, replies[1]); @@ -143,11 +128,11 @@ describe("The 'multi' method", function () { assert.strictEqual("0", res[0].toString()); assert.strictEqual("0", res[1].toString()); }], - ["set", "foo2", nodeAssert.isError()], - ["incr", "multifoo", nodeAssert.isNumber(1)], - ["incr", "multibar", nodeAssert.isNumber(1)] + ["set", "foo2", helper.isError()], + ["incr", "multifoo", helper.isNumber(1)], + ["incr", "multibar", helper.isNumber(1)] ]).exec(function (err, replies) { - if (nodeAssert.serverVersionAtLeast(client, [2, 6, 5])) { + if (helper.serverVersionAtLeast(client, [2, 6, 5])) { assert.notEqual(err, null); assert.equal(replies, undefined); } else { @@ -241,7 +226,7 @@ describe("The 'multi' method", function () { }); it('reports multiple exceptions when they occur', function (done) { - if (!nodeAssert.serverVersionAtLeast(client, [2, 6, 5])) return done(); + if (!helper.serverVersionAtLeast(client, [2, 6, 5])) return done(); client.multi().set("foo").exec(function (err, reply) { assert(Array.isArray(err), "err should be an array"); @@ -262,8 +247,4 @@ describe("The 'multi' method", function () { allTests(parser, ip); }) }); - - after(function (done) { - if (rp) rp.stop(done); - }); }); diff --git a/test/commands/randomkey.test.js b/test/commands/randomkey.test.js new file mode 100644 index 00000000000..75498003942 --- /dev/null +++ b/test/commands/randomkey.test.js @@ -0,0 +1,42 @@ +var assert = require("assert"); +var config = require("../lib/config"); +var helper = require("../helper"); +var redis = config.redis; + +describe("The 'randomkey' method", function () { + + function allTests(parser, ip) { + var args = config.configureClient(parser, ip); + + describe("using " + parser + " and " + ip, function () { + var client; + + beforeEach(function (done) { + client = redis.createClient.apply(redis.createClient, args); + client.once("error", done); + client.once("connect", function () { + client.flushdb(done); + }); + }); + + it('returns a random key', function (done) { + client.mset(["test keys 1", "test val 1", "test keys 2", "test val 2"], helper.isString('OK')); + client.RANDOMKEY([], function (err, results) { + assert.strictEqual(true, /test keys.+/.test(results)); + return done(err); + }); + }); + + afterEach(function () { + client.end(); + }); + }); + } + + ['javascript', 'hiredis'].forEach(function (parser) { + allTests(parser, "/tmp/redis.sock"); + ['IPv4', 'IPv6'].forEach(function (ip) { + allTests(parser, ip); + }) + }); +}); diff --git a/test/commands/rename.spec.js b/test/commands/rename.spec.js new file mode 100644 index 00000000000..be2145923d3 --- /dev/null +++ b/test/commands/rename.spec.js @@ -0,0 +1,46 @@ +var assert = require("assert"); +var config = require("../lib/config"); +var helper = require("../helper"); +var redis = config.redis; + +describe("The 'rename' method", function () { + + function allTests(parser, ip) { + var args = config.configureClient(parser, ip); + + describe("using " + parser + " and " + ip, function () { + var client; + + beforeEach(function (done) { + client = redis.createClient.apply(redis.createClient, args); + client.once("error", done); + client.once("connect", function () { + client.flushdb(done); + }); + }); + + it('populates the new key', function (done) { + client.set(['foo', 'bar'], helper.isString("OK")); + client.RENAME(["foo", "new foo"], helper.isString("OK")); + client.exists(["new foo"], helper.isNumber(1, done)); + }); + + it('removes the old key', function (done) { + client.set(['foo', 'bar'], helper.isString("OK")); + client.RENAME(["foo", "new foo"], helper.isString("OK")); + client.exists(["foo"], helper.isNumber(0, done)); + }); + + afterEach(function () { + client.end(); + }); + }); + } + + ['javascript', 'hiredis'].forEach(function (parser) { + allTests(parser, "/tmp/redis.sock"); + ['IPv4', 'IPv6'].forEach(function (ip) { + allTests(parser, ip); + }) + }); +}); diff --git a/test/commands/renamenx.spec.js b/test/commands/renamenx.spec.js new file mode 100644 index 00000000000..6dd1a1b0d6b --- /dev/null +++ b/test/commands/renamenx.spec.js @@ -0,0 +1,49 @@ +var assert = require("assert"); +var config = require("../lib/config"); +var helper = require("../helper"); +var redis = config.redis; + +describe("The 'renamenx' method", function () { + + function allTests(parser, ip) { + var args = config.configureClient(parser, ip); + + describe("using " + parser + " and " + ip, function () { + var client; + + beforeEach(function (done) { + client = redis.createClient.apply(redis.createClient, args); + client.once("error", done); + client.once("connect", function () { + client.flushdb(done); + }); + }); + + it('renames the key if target does not yet exist', function (done) { + client.set('foo', 'bar', helper.isString('OK')); + client.renamenx('foo', 'foo2', helper.isNumber(1)); + client.exists('foo', helper.isNumber(0)); + client.exists(['foo2'], helper.isNumber(1, done)); + }); + + it('does not rename the key if the target exists', function (done) { + client.set('foo', 'bar', helper.isString('OK')); + client.set('foo2', 'apple', helper.isString('OK')); + client.renamenx('foo', 'foo2', helper.isNumber(0)); + client.exists('foo', helper.isNumber(1)); + client.exists(['foo2'], helper.isNumber(1, done)); + }) + + afterEach(function () { + client.end(); + }); + }); + } + + ['javascript', 'hiredis'].forEach(function (parser) { + allTests(parser, "/tmp/redis.sock"); + ['IPv4', 'IPv6'].forEach(function (ip) { + allTests(parser, ip); + }) + }); +}); diff --git a/test/commands/sadd.spec.js b/test/commands/sadd.spec.js new file mode 100644 index 00000000000..ea63a637ead --- /dev/null +++ b/test/commands/sadd.spec.js @@ -0,0 +1,58 @@ +var assert = require("assert"); +var config = require("../lib/config"); +var helper = require("../helper"); +var redis = config.redis; + +describe("The 'sadd' method", function () { + + function allTests(parser, ip) { + var args = config.configureClient(parser, ip); + + describe("using " + parser + " and " + ip, function () { + var client; + + beforeEach(function (done) { + client = redis.createClient.apply(redis.createClient, args); + client.once("error", done); + client.once("connect", function () { + client.flushdb(done); + }); + }); + + it('allows a single value to be added to the set', function (done) { + client.SADD('set0', 'member0', helper.isNumber(1)); + client.smembers('set0', function (err, res) { + assert.ok(~res.indexOf('member0')); + return done(err); + }); + }); + + it('does not add the same value to the set twice', function (done) { + client.sadd('set0', 'member0', helper.isNumber(1)); + client.SADD('set0', 'member0', helper.isNumber(0, done)); + }); + + it('allows multiple values to be added to the set', function (done) { + client.sadd("set0", ["member0", "member1", "member2"], helper.isNumber(3)); + client.smembers("set0", function (err, res) { + assert.strictEqual(res.length, 3); + assert.ok(~res.indexOf("member0")); + assert.ok(~res.indexOf("member1")); + assert.ok(~res.indexOf("member2")); + return done(err); + }); + }); + + afterEach(function () { + client.end(); + }); + }); + } + + ['javascript', 'hiredis'].forEach(function (parser) { + allTests(parser, "/tmp/redis.sock"); + ['IPv4', 'IPv6'].forEach(function (ip) { + allTests(parser, ip); + }) + }); +}); diff --git a/test/commands/scard.spec.js b/test/commands/scard.spec.js new file mode 100644 index 00000000000..4030e27b958 --- /dev/null +++ b/test/commands/scard.spec.js @@ -0,0 +1,39 @@ +var assert = require("assert"); +var config = require("../lib/config"); +var helper = require("../helper"); +var redis = config.redis; + +describe("The 'scard' method", function () { + + function allTests(parser, ip) { + var args = config.configureClient(parser, ip); + + describe("using " + parser + " and " + ip, function () { + var client; + + beforeEach(function (done) { + client = redis.createClient.apply(redis.createClient, args); + client.once("error", done); + client.once("connect", function () { + client.flushdb(done); + }); + }); + + it('returns the number of values in a set', function (done) { + client.sadd('foo', [1, 2, 3], helper.isNumber(3)); + client.scard('foo', helper.isNumber(3, done)); + }); + + afterEach(function () { + client.end(); + }); + }); + } + + ['javascript', 'hiredis'].forEach(function (parser) { + allTests(parser, "/tmp/redis.sock"); + ['IPv4', 'IPv6'].forEach(function (ip) { + allTests(parser, ip); + }) + }); +}); diff --git a/test/commands/script.spec.js b/test/commands/script.spec.js new file mode 100644 index 00000000000..df3d8aa4865 --- /dev/null +++ b/test/commands/script.spec.js @@ -0,0 +1,68 @@ +var assert = require("assert"); +var config = require("../lib/config"); +var crypto = require("crypto"); +var helper = require("../helper"); +var redis = config.redis; + +describe("The 'script' method", function () { + + function allTests(parser, ip) { + var args = config.configureClient(parser, ip); + var command = "return 99"; + var commandSha = crypto.createHash('sha1').update(command).digest('hex'); + + describe("using " + parser + " and " + ip, function () { + var client; + + beforeEach(function (done) { + client = redis.createClient.apply(redis.createClient, args); + client.once("error", done); + client.once("connect", function () { + client.flushdb(function (err) { + if (!helper.serverVersionAtLeast(client, [2, 6, 0])) { + err = Error('script not supported in redis <= 2.6.0') + } + return done(err); + + }) + }); + }); + + afterEach(function () { + client.end(); + }); + + it("loads script with client.script('load')", function (done) { + client.script("load", command, function(err, result) { + assert.strictEqual(result, commandSha); + return done(); + }); + }); + + it('allows a loaded script to be evaluated', function (done) { + client.evalsha(commandSha, 0, helper.isString('99', done)); + }) + + it('allows a script to be loaded as part of a chained transaction', function (done) { + client.multi().script("load", command).exec(function(err, result) { + assert.strictEqual(result[0], commandSha); + return done() + }) + }) + + it("allows a script to be loaded using a transaction's array syntax", function (done) { + client.multi([['script', 'load', command]]).exec(function(err, result) { + assert.strictEqual(result[0], commandSha); + return done() + }) + }) + }); + } + + ['javascript', 'hiredis'].forEach(function (parser) { + allTests(parser, "/tmp/redis.sock"); + ['IPv4', 'IPv6'].forEach(function (ip) { + allTests(parser, ip); + }) + }); +}); diff --git a/test/mocha/commands/select.spec.js b/test/commands/select.spec.js similarity index 92% rename from test/mocha/commands/select.spec.js rename to test/commands/select.spec.js index 24c1d159b8f..1be845dc002 100644 --- a/test/mocha/commands/select.spec.js +++ b/test/commands/select.spec.js @@ -1,20 +1,11 @@ var async = require('async'); var assert = require('assert'); -var config = require("../../lib/config"); -var nodeAssert = require('../../lib/nodeify-assertions'); +var config = require("../lib/config"); +var helper = require('../helper'); var redis = config.redis; -var RedisProcess = require("../../lib/redis-process"); describe("The 'select' method", function () { - var rp; - before(function (done) { - RedisProcess.start(function (err, _rp) { - rp = _rp; - return done(err); - }); - }) - function removeMochaListener () { var mochaListener = process.listeners('uncaughtException').pop(); process.removeListener('uncaughtException', mochaListener); @@ -64,7 +55,7 @@ describe("The 'select' method", function () { // default value of null means database 0 will be used. assert.strictEqual(client.selected_db, null, "default db should be null"); client.select(1, function (err, res) { - nodeAssert.isNotError()(err, res); + helper.isNotError()(err, res); assert.strictEqual(client.selected_db, 1, "db should be 1 after select"); done(); }); @@ -129,8 +120,4 @@ describe("The 'select' method", function () { allTests(parser, ip); }) }); - - after(function (done) { - if (rp) rp.stop(done); - }); }); diff --git a/test/mocha/commands/set.spec.js b/test/commands/set.spec.js similarity index 87% rename from test/mocha/commands/set.spec.js rename to test/commands/set.spec.js index 72df4467fbd..b9bbe79c858 100644 --- a/test/mocha/commands/set.spec.js +++ b/test/commands/set.spec.js @@ -1,21 +1,12 @@ var async = require('async'); var assert = require('assert'); -var config = require("../../lib/config"); -var nodeAssert = require('../../lib/nodeify-assertions'); +var config = require("../lib/config"); +var helper = require('../helper'); var redis = config.redis; -var RedisProcess = require("../../lib/redis-process"); var uuid = require('uuid'); describe("The 'set' method", function () { - var rp; - before(function (done) { - RedisProcess.start(function (err, _rp) { - rp = _rp; - return done(err); - }); - }) - function removeMochaListener () { var mochaListener = process.listeners('uncaughtException').pop(); process.removeListener('uncaughtException', mochaListener); @@ -74,9 +65,9 @@ describe("The 'set' method", function () { describe("with valid parameters", function () { it("sets the value correctly", function (done) { client.set(key, value, function (err, res) { - nodeAssert.isNotError()(err, res); + helper.isNotError()(err, res); client.get(key, function (err, res) { - nodeAssert.isString(value)(err, res); + helper.isString(value)(err, res); done(); }); }); @@ -86,7 +77,7 @@ describe("The 'set' method", function () { describe("with undefined 'key' and missing 'value' parameter", function () { it("reports an error", function (done) { client.set(undefined, function (err, res) { - nodeAssert.isError()(err, null); + helper.isError()(err, null); done(); }); }); @@ -95,7 +86,7 @@ describe("The 'set' method", function () { describe("with undefined 'key' and defined 'value' parameters", function () { it("reports an error", function () { client.set(undefined, value, function (err, res) { - nodeAssert.isError()(err, null); + helper.isError()(err, null); done(); }); }); @@ -108,7 +99,7 @@ describe("The 'set' method", function () { client.set(key, value); setTimeout(function () { client.get(key, function (err, res) { - nodeAssert.isString(value)(err, res); + helper.isString(value)(err, res); done(); }); }, 100); @@ -120,7 +111,7 @@ describe("The 'set' method", function () { this.timeout(200); client.once("error", function (err) { - nodeAssert.isError()(err, null); + helper.isError()(err, null); return done(err); }); @@ -154,7 +145,7 @@ describe("The 'set' method", function () { process.once('uncaughtException', function (err) { process.on('uncaughtException', mochaListener); - nodeAssert.isError()(err, null); + helper.isError()(err, null); }); client.set(undefined, value); @@ -171,8 +162,4 @@ describe("The 'set' method", function () { allTests(parser, ip); }) }); - - after(function (done) { - if (rp) rp.stop(done); - }); }); diff --git a/test/commands/setex.spec.js b/test/commands/setex.spec.js new file mode 100644 index 00000000000..d66366785ed --- /dev/null +++ b/test/commands/setex.spec.js @@ -0,0 +1,47 @@ +var assert = require("assert"); +var config = require("../lib/config"); +var helper = require("../helper"); +var redis = config.redis; + +describe("The 'setex' method", function () { + + function allTests(parser, ip) { + var args = config.configureClient(parser, ip); + + describe("using " + parser + " and " + ip, function () { + var client; + + beforeEach(function (done) { + client = redis.createClient.apply(redis.createClient, args); + client.once("error", done); + client.once("connect", function () { + client.flushdb(done); + }); + }); + + it('sets a key with an expiry', function (done) { + client.SETEX(["setex key", "100", "setex val"], helper.isString("OK")); + client.exists(["setex key"], helper.isNumber(1)); + client.ttl(['setex key'], function (err, ttl) { + assert.ok(ttl > 0); + return done(); + }); + }); + + it('returns an error if no value is provided', function (done) { + client.SETEX(["setex key", "100", undefined], helper.isError(done)); + }); + + afterEach(function () { + client.end(); + }); + }); + } + + ['javascript', 'hiredis'].forEach(function (parser) { + allTests(parser, "/tmp/redis.sock"); + ['IPv4', 'IPv6'].forEach(function (ip) { + allTests(parser, ip); + }) + }); +}); diff --git a/test/commands/setnx.spec.js b/test/commands/setnx.spec.js new file mode 100644 index 00000000000..92f892516a2 --- /dev/null +++ b/test/commands/setnx.spec.js @@ -0,0 +1,46 @@ +var assert = require("assert"); +var config = require("../lib/config"); +var helper = require("../helper"); +var redis = config.redis; + +describe("The 'setnx' method", function () { + + function allTests(parser, ip) { + var args = config.configureClient(parser, ip); + + describe("using " + parser + " and " + ip, function () { + var client; + + beforeEach(function (done) { + client = redis.createClient.apply(redis.createClient, args); + client.once("error", done); + client.once("connect", function () { + client.flushdb(done); + }); + }); + + it('sets key if it does not have a value', function (done) { + client.setnx('foo', 'banana', helper.isNumber(1)); + client.get('foo', helper.isString('banana', done)); + }); + + it('does not set key if it already has a value', function (done) { + client.set('foo', 'bar', helper.isString('OK')); + client.setnx('foo', 'banana', helper.isNumber(0)); + client.get('foo', helper.isString('bar', done)); + return done(); + }); + + afterEach(function () { + client.end(); + }); + }); + } + + ['javascript', 'hiredis'].forEach(function (parser) { + allTests(parser, "/tmp/redis.sock"); + ['IPv4', 'IPv6'].forEach(function (ip) { + allTests(parser, ip); + }) + }); +}); diff --git a/test/commands/sinter.spec.js b/test/commands/sinter.spec.js new file mode 100644 index 00000000000..c1b89051cf3 --- /dev/null +++ b/test/commands/sinter.spec.js @@ -0,0 +1,70 @@ +var assert = require("assert"); +var config = require("../lib/config"); +var helper = require("../helper"); +var redis = config.redis; + +describe("The 'sinter' method", function () { + + function allTests(parser, ip) { + var args = config.configureClient(parser, ip); + + describe("using " + parser + " and " + ip, function () { + var client; + + beforeEach(function (done) { + client = redis.createClient.apply(redis.createClient, args); + client.once("error", done); + client.once("connect", function () { + client.flushdb(done); + }); + }); + + it('handles two sets being intersected', function (done) { + client.sadd('sa', 'a', helper.isNumber(1)); + client.sadd('sa', 'b', helper.isNumber(1)); + client.sadd('sa', 'c', helper.isNumber(1)); + + client.sadd('sb', 'b', helper.isNumber(1)); + client.sadd('sb', 'c', helper.isNumber(1)); + client.sadd('sb', 'd', helper.isNumber(1)); + + client.sinter('sa', 'sb', function (err, intersection) { + assert.equal(intersection.length, 2); + assert.deepEqual(intersection.sort(), [ 'b', 'c' ]); + return done(err); + }); + }); + + it('handles three sets being intersected', function (done) { + client.sadd('sa', 'a', helper.isNumber(1)); + client.sadd('sa', 'b', helper.isNumber(1)); + client.sadd('sa', 'c', helper.isNumber(1)); + + client.sadd('sb', 'b', helper.isNumber(1)); + client.sadd('sb', 'c', helper.isNumber(1)); + client.sadd('sb', 'd', helper.isNumber(1)); + + client.sadd('sc', 'c', helper.isNumber(1)); + client.sadd('sc', 'd', helper.isNumber(1)); + client.sadd('sc', 'e', helper.isNumber(1)); + + client.sinter('sa', 'sb', 'sc', function (err, intersection) { + assert.equal(intersection.length, 1); + assert.equal(intersection[0], 'c'); + return done(err); + }); + }); + + afterEach(function () { + client.end(); + }); + }); + } + + ['javascript', 'hiredis'].forEach(function (parser) { + allTests(parser, "/tmp/redis.sock"); + ['IPv4', 'IPv6'].forEach(function (ip) { + allTests(parser, ip); + }) + }); +}); diff --git a/test/commands/sismember.spec.js b/test/commands/sismember.spec.js new file mode 100644 index 00000000000..7277692c460 --- /dev/null +++ b/test/commands/sismember.spec.js @@ -0,0 +1,43 @@ +var assert = require("assert"); +var config = require("../lib/config"); +var helper = require("../helper"); +var redis = config.redis; + +describe("The 'sismember' method", function () { + + function allTests(parser, ip) { + var args = config.configureClient(parser, ip); + + describe("using " + parser + " and " + ip, function () { + var client; + + beforeEach(function (done) { + client = redis.createClient.apply(redis.createClient, args); + client.once("error", done); + client.once("connect", function () { + client.flushdb(done); + }); + }); + + it('returns 0 if the value is not in the set', function (done) { + client.sismember('foo', 'banana', helper.isNumber(0, done)); + }); + + it('returns 1 if the value is in the set', function (done) { + client.sadd('foo', 'banana', helper.isNumber(1)); + client.sismember('foo', 'banana', helper.isNumber(1, done)); + }); + + afterEach(function () { + client.end(); + }); + }); + } + + ['javascript', 'hiredis'].forEach(function (parser) { + allTests(parser, "/tmp/redis.sock"); + ['IPv4', 'IPv6'].forEach(function (ip) { + allTests(parser, ip); + }) + }); +}); diff --git a/test/commands/sort.spec.js b/test/commands/sort.spec.js new file mode 100644 index 00000000000..b40b63cf324 --- /dev/null +++ b/test/commands/sort.spec.js @@ -0,0 +1,128 @@ +var assert = require("assert"); +var config = require("../lib/config"); +var helper = require("../helper"); +var redis = config.redis; + +describe("The 'sort' method", function () { + + function allTests(parser, ip) { + var args = config.configureClient(parser, ip); + + describe("using " + parser + " and " + ip, function () { + var client; + + beforeEach(function (done) { + client = redis.createClient.apply(redis.createClient, args); + client.once("error", done); + client.once("connect", function () { + client.flushdb(); + setupData(client, done) + }); + }); + + describe('alphabetical', function () { + it('sorts in ascending alphabetical order', function (done) { + client.sort('y', 'asc', 'alpha', function (err, sorted) { + assert.deepEqual(sorted, ['a', 'b', 'c', 'd']); + return done(err); + }); + }); + + it('sorts in descending alphabetical order', function (done) { + client.sort('y', 'desc', 'alpha', function (err, sorted) { + assert.deepEqual(sorted, ['d', 'c', 'b', 'a']); + return done(err); + }); + }); + }); + + describe('numeric', function () { + it('sorts in ascending numeric order', function (done) { + client.sort('x', 'asc', function (err, sorted) { + assert.deepEqual(sorted, [2, 3, 4, 9]); + return done(err); + }); + }); + + it('sorts in descending numeric order', function (done) { + client.sort('x', 'desc', function (err, sorted) { + assert.deepEqual(sorted, [9, 4, 3, 2]); + return done(err); + }); + }); + }); + + describe('pattern', function () { + it('handles sorting with a pattern', function (done) { + client.sort('x', 'by', 'w*', 'asc', function (err, sorted) { + assert.deepEqual(sorted, [3, 9, 4, 2]); + return done(err); + }); + }); + + it("handles sorting with a 'by' pattern and 1 'get' pattern", function (done) { + client.sort('x', 'by', 'w*', 'asc', 'get', 'o*', function (err, sorted) { + assert.deepEqual(sorted, ['foo', 'bar', 'baz', 'buz']); + return done(err); + }); + }); + + it("handles sorting with a 'by' pattern and 2 'get' patterns", function (done) { + client.sort('x', 'by', 'w*', 'asc', 'get', 'o*', 'get', 'p*', function (err, sorted) { + assert.deepEqual(sorted, ['foo', 'bux', 'bar', 'tux', 'baz', 'lux', 'buz', 'qux']); + return done(err); + }); + }); + + it("sorting with a 'by' pattern and 2 'get' patterns and stores results", function (done) { + client.sort('x', 'by', 'w*', 'asc', 'get', 'o*', 'get', 'p*', 'store', 'bacon', function (err) { + if (err) return done(err); + }); + + client.lrange('bacon', 0, -1, function (err, values) { + assert.deepEqual(values, ['foo', 'bux', 'bar', 'tux', 'baz', 'lux', 'buz', 'qux']); + return done(err); + }); + }); + }); + + afterEach(function () { + client.end(); + }); + }); + } + + function setupData(client, done) { + client.rpush('y', 'd'); + client.rpush('y', 'b'); + client.rpush('y', 'a'); + client.rpush('y', 'c'); + + client.rpush('x', '3'); + client.rpush('x', '9'); + client.rpush('x', '2'); + client.rpush('x', '4'); + + client.set('w3', '4'); + client.set('w9', '5'); + client.set('w2', '12'); + client.set('w4', '6'); + + client.set('o2', 'buz'); + client.set('o3', 'foo'); + client.set('o4', 'baz'); + client.set('o9', 'bar'); + + client.set('p2', 'qux'); + client.set('p3', 'bux'); + client.set('p4', 'lux'); + client.set('p9', 'tux', done); + } + + ['javascript', 'hiredis'].forEach(function (parser) { + allTests(parser, "/tmp/redis.sock"); + ['IPv4', 'IPv6'].forEach(function (ip) { + allTests(parser, ip); + }) + }); +}); diff --git a/test/commands/srem.spec.js b/test/commands/srem.spec.js new file mode 100644 index 00000000000..f0a140e39ac --- /dev/null +++ b/test/commands/srem.spec.js @@ -0,0 +1,66 @@ +var assert = require("assert"); +var config = require("../lib/config"); +var helper = require("../helper"); +var redis = config.redis; + +describe("The 'srem' method", function () { + + function allTests(parser, ip) { + var args = config.configureClient(parser, ip); + + describe("using " + parser + " and " + ip, function () { + var client; + + beforeEach(function (done) { + client = redis.createClient.apply(redis.createClient, args); + client.once("error", done); + client.once("connect", function () { + client.flushdb(done); + }); + }); + + it('removes a value', function (done) { + client.sadd('set0', 'member0', helper.isNumber(1)); + client.srem('set0', 'member0', helper.isNumber(1)); + client.scard('set0', helper.isNumber(0, done)); + }); + + it('handles attempting to remove a missing value', function (done) { + client.srem('set0', 'member0', helper.isNumber(0, done)); + }); + + it('allows multiple values to be removed', function (done) { + client.sadd("set0", ["member0", "member1", "member2"], helper.isNumber(3)); + client.SREM("set0", ["member1", "member2"], helper.isNumber(2)); + client.smembers("set0", function (err, res) { + assert.strictEqual(res.length, 1); + assert.ok(~res.indexOf("member0")); + return done(err); + }); + }); + + it('handles a value missing from the set of values being removed', function (done) { + client.sadd("set0", ["member0", "member1", "member2"], helper.isNumber(3)); + client.SREM("set0", ["member3", "member4"], helper.isNumber(0)); + client.smembers("set0", function (err, res) { + assert.strictEqual(res.length, 3); + assert.ok(~res.indexOf("member0")); + assert.ok(~res.indexOf("member1")); + assert.ok(~res.indexOf("member2")); + return done(err); + }); + }); + + afterEach(function () { + client.end(); + }); + }); + } + + ['javascript', 'hiredis'].forEach(function (parser) { + allTests(parser, "/tmp/redis.sock"); + ['IPv4', 'IPv6'].forEach(function (ip) { + allTests(parser, ip); + }) + }); +}); diff --git a/test/commands/type.spec.js b/test/commands/type.spec.js new file mode 100644 index 00000000000..57aa745da11 --- /dev/null +++ b/test/commands/type.spec.js @@ -0,0 +1,63 @@ +var assert = require("assert"); +var config = require("../lib/config"); +var helper = require("../helper"); +var redis = config.redis; + +describe("The 'type' method", function () { + + function allTests(parser, ip) { + var args = config.configureClient(parser, ip); + + describe("using " + parser + " and " + ip, function () { + var client; + + beforeEach(function (done) { + client = redis.createClient.apply(redis.createClient, args); + client.once("error", done); + client.once("connect", function () { + client.flushdb(done); + }); + }); + + it('reports string type', function (done) { + client.set(["string key", "should be a string"], helper.isString("OK")); + client.TYPE(["string key"], helper.isString("string", done)); + }); + + it('reports list type', function (done) { + client.rpush(["list key", "should be a list"], helper.isNumber(1)); + client.TYPE(["list key"], helper.isString("list", done)); + }); + + it('reports set type', function (done) { + client.sadd(["set key", "should be a set"], helper.isNumber(1)); + client.TYPE(["set key"], helper.isString("set", done)); + }); + + it('reports zset type', function (done) { + client.zadd(["zset key", "10.0", "should be a zset"], helper.isNumber(1)); + client.TYPE(["zset key"], helper.isString("zset", done)); + }); + + it('reports hash type', function (done) { + client.hset(["hash key", "hashtest", "should be a hash"], helper.isNumber(1)); + client.TYPE(["hash key"], helper.isString("hash", done)); + }); + + it('reports none for null key', function (done) { + client.TYPE("not here yet", helper.isString("none", done)); + }); + + afterEach(function () { + client.end(); + }); + }); + } + + ['javascript', 'hiredis'].forEach(function (parser) { + allTests(parser, "/tmp/redis.sock"); + ['IPv4', 'IPv6'].forEach(function (ip) { + allTests(parser, ip); + }) + }); +}); diff --git a/test/commands/watch.spec.js b/test/commands/watch.spec.js new file mode 100644 index 00000000000..8494d35dd58 --- /dev/null +++ b/test/commands/watch.spec.js @@ -0,0 +1,68 @@ +var assert = require("assert"); +var config = require("../lib/config"); +var helper = require("../helper"); +var redis = config.redis; + +describe("The 'watch' method", function () { + + function allTests(parser, ip) { + var args = config.configureClient(parser, ip); + var watched = 'foobar' + + describe("using " + parser + " and " + ip, function () { + var client; + + beforeEach(function (done) { + client = redis.createClient.apply(redis.createClient, args); + client.once("error", done); + client.once("connect", function () { + client.flushdb(function (err) { + if (!helper.serverVersionAtLeast(client, [2, 2, 0])) { + err = Error('some watch commands not supported in redis <= 2.2.0') + } + return done(err); + + }) + }); + }); + + afterEach(function () { + client.end(); + }); + + it('does not execute transaction if watched key was modified prior to execution', function (done) { + client.watch(watched); + client.incr(watched); + multi = client.multi(); + multi.incr(watched); + multi.exec(helper.isNull(done)); + }) + + it('successfully modifies other keys independently of transaction', function (done) { + client.set("unwatched", 200); + + client.set(watched, 0); + client.watch(watched); + client.incr(watched); + + var multi = client.multi() + .incr(watched) + .exec(function (err, replies) { + assert.strictEqual(replies, null, "Aborted transaction multi-bulk reply should be null."); + + client.get("unwatched", function (err, reply) { + assert.equal(reply, 200, "Expected 200, got " + reply); + return done(err) + }); + }); + }) + }); + } + + ['javascript', 'hiredis'].forEach(function (parser) { + allTests(parser, "/tmp/redis.sock"); + ['IPv4', 'IPv6'].forEach(function (ip) { + allTests(parser, ip); + }) + }); +}); diff --git a/test/conf/password.conf b/test/conf/password.conf new file mode 100644 index 00000000000..8351cb455f4 --- /dev/null +++ b/test/conf/password.conf @@ -0,0 +1,5 @@ +requirepass porkchopsandwiches +port 6378 +bind ::1 127.0.0.1 +unixsocket /tmp/redis.sock +unixsocketperm 755 diff --git a/test/lib/nodeify-assertions.js b/test/helper.js similarity index 55% rename from test/lib/nodeify-assertions.js rename to test/helper.js index 73000e67d07..8aaa4d68d4a 100644 --- a/test/lib/nodeify-assertions.js +++ b/test/helper.js @@ -1,63 +1,86 @@ -var assert = require('assert'); +var assert = require("assert"); +var path = require('path'); +var RedisProcess = require("./lib/redis-process"); +var rp; + +// don't start redis every time we +// include this helper file! +if (!process.env.REDIS_TESTS_STARTED) { + process.env.REDIS_TESTS_STARTED = true; + + before(function (done) { + startRedis('./conf/redis.conf', done); + }) + + after(function (done) { + if (rp) rp.stop(done); + }); +} module.exports = { - isNumber: function (expected) { + stopRedis: function (done) { + rp.stop(done); + }, + startRedis: function (conf, done) { + startRedis(conf, done); + }, + isNumber: function (expected, done) { return function (err, results) { assert.strictEqual(null, err, "expected " + expected + ", got error: " + err); assert.strictEqual(expected, results, expected + " !== " + results); assert.strictEqual(typeof results, "number", "expected a number, got " + typeof results); - return true; + if (done) return done(); }; }, - - isString: function (str) { + isString: function (str, done) { return function (err, results) { assert.strictEqual(null, err, "expected string '" + str + "', got error: " + err); assert.equal(str, results, str + " does not match " + results); - return true; + if (done) return done(); }; }, - - isNull: function () { + isNull: function (done) { return function (err, results) { assert.strictEqual(null, err, "expected null, got error: " + err); assert.strictEqual(null, results, results + " is not null"); - return true; + if (done) return done(); }; }, - - isError: function () { + isError: function (done) { return function (err, results) { assert.notEqual(err, null, "err is null, but an error is expected here."); - return true; + if (done) return done(); }; }, - - isNotError: function () { + isNotError: function (done) { return function (err, results) { assert.strictEqual(err, null, "expected success, got an error: " + err); - return true; + if (done) return done(); }; }, - isType: { - number: function () { + number: function (done) { return function (err, results) { assert.strictEqual(null, err, "expected any number, got error: " + err); assert.strictEqual(typeof results, "number", results + " is not a number"); - return true; + if (done) return done(); }; }, - - positiveNumber: function () { + positiveNumber: function (done) { return function (err, results) { assert.strictEqual(null, err, "expected positive number, got error: " + err); assert.strictEqual(true, (results > 0), results + " is not a positive number"); - return true; + if (done) return done(); }; } }, - + match: function (pattern, done) { + return function (err, results) { + assert.strictEqual(null, err, "expected " + pattern.toString() + ", got error: " + err); + assert(pattern.test(results), "expected string '" + results + "' to match " + pattern.toString()); + if (done) return done(); + }; + }, serverVersionAtLeast: function (connection, desired_version) { // Return true if the server version >= desired_version var version = connection.server_info.versions; @@ -67,4 +90,11 @@ module.exports = { } return true; } -}; +} + +function startRedis (conf, done) { + RedisProcess.start(function (err, _rp) { + rp = _rp; + return done(err); + }, path.resolve(__dirname, conf)); +} diff --git a/test/lib/config.js b/test/lib/config.js index 475f2733ceb..0ddef514473 100644 --- a/test/lib/config.js +++ b/test/lib/config.js @@ -1,3 +1,5 @@ +// helpers for configuring a redis client in +// its various modes, ipV6, ipV4, socket. module.exports = (function () { var redis = require('../../index'); redis.debug_mode = process.env.DEBUG ? JSON.parse(process.env.DEBUG) : false; @@ -11,18 +13,21 @@ module.exports = (function () { } }; - config.configureClient = function (parser, ip, isSocket) { + config.configureClient = function (parser, ip, opts) { var args = []; + opts = opts || {}; - if (!isSocket) { + if (ip.match(/\.sock/)) { + args.push(ip) + } else { args.push(config.PORT); args.push(config.HOST[ip]); - args.push({ family: ip, parser: parser }); - } else { - args.push(ip); - args.push({ parser: parser }); + opts.family = ip; } + opts.parser = parser; + args.push(opts); + return args; }; diff --git a/test/lib/redis-process.js b/test/lib/redis-process.js index 24d7f04f60a..212cf359c06 100644 --- a/test/lib/redis-process.js +++ b/test/lib/redis-process.js @@ -1,42 +1,55 @@ +// helper to start and stop the redis process. var cp = require('child_process'); var config = require('./config'); +var fs = require('fs'); var path = require('path'); var tcpPortUsed = require('tcp-port-used'); module.exports = { - start: function (done) { + start: function (done, conf) { // spawn redis with our testing configuration. - var confFile = path.resolve(__dirname, '../conf/redis.conf'); + var confFile = conf || path.resolve(__dirname, '../conf/redis.conf'); var rp = cp.spawn("redis-server", [confFile], {}); // wait for redis to become available, by // checking the port we bind on. - var id = setInterval(function () { - tcpPortUsed.check(config.PORT, '127.0.0.1') - .then(function (inUse) { - if (inUse) { - clearInterval(id); - - // return an object that can be used in - // an after() block to shutdown redis. - return done(null, { - stop: function (done) { - rp.once("exit", function (code) { - var error = null; - if (code !== null && code !== 0) { - error = Error('Redis shutdown failed with code ' + code); - } - return done(error); - }); - rp.kill("SIGINT"); - } - }); - } - }) - .catch(function (err) { - clearInterval(id); - return done(err); - }) - }, 100); + waitForRedis(true, function () { + // return an object that can be used in + // an after() block to shutdown redis. + return done(null, { + stop: function (done) { + rp.once("exit", function (code) { + var error = null; + if (code !== null && code !== 0) { + error = Error('Redis shutdown failed with code ' + code); + } + waitForRedis(false, function () { + return done(error); + }) + }); + rp.kill("SIGTERM"); + } + }); + }); } }; + +// wait for redis to be listening in +// all three modes (ipv4, ipv6, socket). +function waitForRedis (available, cb) { + var ipV4 = false; + var id = setInterval(function () { + tcpPortUsed.check(config.PORT, '127.0.0.1') + .then(function (_ipV4) { + ipV4 = _ipV4; + return tcpPortUsed.check(config.PORT, '::1'); + }) + .then(function (ipV6) { + if (ipV6 === available && ipV4 === available && + fs.existsSync('/tmp/redis.sock') === available) { + clearInterval(id); + return cb(); + } + }); + }, 100); +} diff --git a/test/lib/unref.js b/test/lib/unref.js new file mode 100644 index 00000000000..4ccf51f203c --- /dev/null +++ b/test/lib/unref.js @@ -0,0 +1,18 @@ +// spawned by the unref tests in node_redis.spec.js. +// when configured, unref causes the client to exit +// as soon as there are no outstanding commands. +'use strict'; + +var redis = require("../../"); +//redis.debug_mode = true +var HOST = process.argv[2] || '127.0.0.1'; +var PORT = process.argv[3] +var args = PORT ? [PORT, HOST] : [HOST] + +var c = redis.createClient.apply(redis, args); +c.unref(); +c.info(function (err, reply) { + if (err) process.exit(-1); + if (!reply.length) process.exit(-1); + process.stdout.write(reply.length.toString()); +}); diff --git a/test/mocha/commands/generated-commands.spec.js b/test/mocha/commands/generated-commands.spec.js deleted file mode 100644 index bff15f4839e..00000000000 --- a/test/mocha/commands/generated-commands.spec.js +++ /dev/null @@ -1,119 +0,0 @@ -var async = require('async'); -var assert = require('assert'); -var config = require("../../lib/config"); -var nodeAssert = require('../../lib/nodeify-assertions'); -var redis = config.redis; -var RedisProcess = require("../../lib/redis-process"); -var uuid = require('uuid'); -var commands = require("../../../lib/commands"); -var sinon = require('sinon').sandbox.create(); - -var overwritten = ['eval', 'hmset', 'multi', 'select']; - -describe("The auto-generated methods", function () { - - var rp; - before(function (done) { - RedisProcess.start(function (err, _rp) { - rp = _rp; - return done(err); - }); - }) - - function removeMochaListener () { - var mochaListener = process.listeners('uncaughtException').pop(); - process.removeListener('uncaughtException', mochaListener); - return mochaListener; - } - - function allTests(parser, ip) { - var args = config.configureClient(parser, ip); - - describe("using " + parser + " and " + ip, function () { - var key, value; - var client; - - beforeEach(function (done) { - client = redis.createClient.apply(redis.createClient, args); - client.once("error", function onError(err) { - done(err); - }); - client.once("ready", function onReady() { - done(); - }); - }); - - afterEach(function () { - client.end(); - }); - - commands.forEach(function (method) { - if (overwritten.indexOf(method) > -1) { - // these are in the list of generated commands but are later overwritten with - // different behavior by node_redis - return; - } - - describe("the " + method + " method", function () { - var methodArgs; - var noop = function () { }; - - it("calls sendCommand with whatever arguments it receives", function () { - key = uuid.v4(); - value = uuid.v4(); - - var parts = method.split(' '); - var argNum = 0; - - client.send_command = sinon.spy(); - methodArgs = [key, value, noop]; - - client[parts[0]].apply(client, methodArgs); - - assert.strictEqual(client.send_command.called, true, - "Client.send_command should have been called."); - assert.strictEqual(parts[0], client.send_command.args[0][argNum], - "Command name '" + parts[0] + "' should be passed as arg " + - argNum + " to send_command"); - argNum++; - /* - * Um, except this doesn't work? The second part of the command is never sent???? - if (parts[1]) { - assert.strictEqual(parts[1], client.send_command.args[0][argNum], - "Second command '" + parts[1] + "' should be passed as arg " + - argNum + " to send_command"); - argNum++; - } - */ - assert.strictEqual(methodArgs.length, client.send_command.args[0][argNum].length, - "The rest of the args to " + method + " should be passed as arg an array to send_command"); - assert.strictEqual(methodArgs[0], client.send_command.args[0][argNum][0], - "Arg " + argNum + " to " + method + " should be passed as arg " + - argNum + " to send_command"); - assert.strictEqual(methodArgs[1], client.send_command.args[0][argNum][1], - "Arg " + argNum + " to " + method + " should be passed as arg " + - argNum + " to send_command"); - assert.strictEqual(methodArgs[2], client.send_command.args[0][argNum][2], - "Arg " + argNum + " to " + method + " should be passed as arg " + - argNum + " to send_command"); - }); - }); - }); - }); - } - - ['javascript', 'hiredis'].forEach(function (parser) { - allTests(parser, "/tmp/redis.sock"); - ['IPv4', 'IPv6'].forEach(function (ip) { - allTests(parser, ip); - }) - }); - - afterEach(function () { - sinon.restore(); - }); - - after(function (done) { - if (rp) rp.stop(done); - }); -}); diff --git a/test/mocha/node_redis.spec.js b/test/mocha/node_redis.spec.js deleted file mode 100644 index 2319c2173da..00000000000 --- a/test/mocha/node_redis.spec.js +++ /dev/null @@ -1,182 +0,0 @@ -var async = require("async"); -var config = require("../lib/config"); -var nodeAssert = require("../lib/nodeify-assertions"); -var redis = config.redis; -var RedisProcess = require("../lib/redis-process"); -var uuid = require("uuid"); - -describe("A node_redis client", function () { - - var rp; - before(function (done) { - RedisProcess.start(function (err, _rp) { - rp = _rp; - return done(err); - }); - }) - - function allTests(parser, ip) { - var args = config.configureClient(parser, ip); - - describe("using " + parser + " and " + ip, function () { - var client; - - describe("when not connected", function () { - afterEach(function () { - client.end(); - }); - - it("connects correctly", function (done) { - client = redis.createClient.apply(redis.createClient, args); - client.on("error", done); - - client.once("ready", function () { - client.removeListener("error", done); - client.get("recon 1", function (err, res) { - done(err); - }); - }); - }); - }); - - describe("when connected", function () { - var client; - - beforeEach(function (done) { - client = redis.createClient.apply(redis.createClient, args); - client.once("error", function onError(err) { - done(err); - }); - client.once("ready", function onReady() { - done(); - }); - }); - - afterEach(function () { - client.end(); - }); - - describe("when redis closes unexpectedly", function () { - it("reconnects and can retrieve the pre-existing data", function (done) { - client.on("reconnecting", function on_recon(params) { - client.on("connect", function on_connect() { - async.parallel([function (cb) { - client.get("recon 1", function (err, res) { - nodeAssert.isString("one")(err, res); - cb(); - }); - }, function (cb) { - client.get("recon 1", function (err, res) { - nodeAssert.isString("one")(err, res); - cb(); - }); - }, function (cb) { - client.get("recon 2", function (err, res) { - nodeAssert.isString("two")(err, res); - cb(); - }); - }, function (cb) { - client.get("recon 2", function (err, res) { - nodeAssert.isString("two")(err, res); - cb(); - }); - }], function (err, results) { - client.removeListener("connect", on_connect); - client.removeListener("reconnecting", on_recon); - done(err); - }); - }); - }); - - client.set("recon 1", "one"); - client.set("recon 2", "two", function (err, res) { - // Do not do this in normal programs. This is to simulate the server closing on us. - // For orderly shutdown in normal programs, do client.quit() - client.stream.destroy(); - }); - }); - - describe("and it's subscribed to a channel", function () { - // reconnect_select_db_after_pubsub - // Does not pass. - // "Connection in subscriber mode, only subscriber commands may be used" - xit("reconnects, unsubscribes, and can retrieve the pre-existing data", function (done) { - client.on("reconnecting", function on_recon(params) { - client.on("ready", function on_connect() { - async.parallel([function (cb) { - client.unsubscribe("recon channel", function (err, res) { - nodeAssert.isNotError()(err, res); - cb(); - }); - }, function (cb) { - client.get("recon 1", function (err, res) { - nodeAssert.isString("one")(err, res); - cb(); - }); - }], function (err, results) { - client.removeListener("connect", on_connect); - client.removeListener("reconnecting", on_recon); - done(err); - }); - }); - }); - - client.set("recon 1", "one"); - client.subscribe("recon channel", function (err, res) { - // Do not do this in normal programs. This is to simulate the server closing on us. - // For orderly shutdown in normal programs, do client.quit() - client.stream.destroy(); - }); - }); - - it("remains subscribed", function () { - var client2 = redis.createClient.apply(redis.createClient, args); - - client.on("reconnecting", function on_recon(params) { - client.on("ready", function on_connect() { - async.parallel([function (cb) { - client.on("message", function (channel, message) { - try { - nodeAssert.isString("recon channel")(null, channel); - nodeAssert.isString("a test message")(null, message); - } catch (err) { - cb(err); - } - }); - - client2.subscribe("recon channel", function (err, res) { - if (err) { - cb(err); - return; - } - client2.publish("recon channel", "a test message"); - }); - }], function (err, results) { - done(err); - }); - }); - }); - - client.subscribe("recon channel", function (err, res) { - // Do not do this in normal programs. This is to simulate the server closing on us. - // For orderly shutdown in normal programs, do client.quit() - client.stream.destroy(); - }); - }); - }); - }); - }); - }); - } - - ['javascript', 'hiredis'].forEach(function (parser) { - allTests(parser, "/tmp/redis.sock"); - ['IPv4', 'IPv6'].forEach(function (ip) { - allTests(parser, ip); - }) - }); - - after(function (done) { - if (rp) rp.stop(done); - }) -}); diff --git a/test/node_redis.spec.js b/test/node_redis.spec.js new file mode 100644 index 00000000000..cd3d86da554 --- /dev/null +++ b/test/node_redis.spec.js @@ -0,0 +1,640 @@ +var async = require("async"); +var assert = require("assert"); +var config = require("./lib/config"); +var helper = require('./helper') +var fork = require("child_process").fork; +var redis = config.redis; + +describe("a node_redis client", function () { + + function allTests(parser, ip) { + var args = config.configureClient(parser, ip); + + describe("using " + parser + " and " + ip, function () { + var client; + + describe("when not connected", function () { + afterEach(function () { + client.end(); + }); + + it("connects correctly", function (done) { + client = redis.createClient.apply(redis.createClient, args); + client.on("error", done); + + client.once("ready", function () { + client.removeListener("error", done); + client.get("recon 1", function (err, res) { + done(err); + }); + }); + }); + }); + + describe("when connected", function () { + beforeEach(function (done) { + client = redis.createClient.apply(redis.createClient, args); + client.once("error", done); + client.once("connect", function () { + client.flushdb(done) + }); + }); + + afterEach(function () { + client.end(); + }); + + describe("when redis closes unexpectedly", function () { + it("reconnects and can retrieve the pre-existing data", function (done) { + client.on("reconnecting", function on_recon(params) { + client.on("connect", function on_connect() { + async.parallel([function (cb) { + client.get("recon 1", function (err, res) { + helper.isString("one")(err, res); + cb(); + }); + }, function (cb) { + client.get("recon 1", function (err, res) { + helper.isString("one")(err, res); + cb(); + }); + }, function (cb) { + client.get("recon 2", function (err, res) { + helper.isString("two")(err, res); + cb(); + }); + }, function (cb) { + client.get("recon 2", function (err, res) { + helper.isString("two")(err, res); + cb(); + }); + }], function (err, results) { + client.removeListener("connect", on_connect); + client.removeListener("reconnecting", on_recon); + done(err); + }); + }); + }); + + client.set("recon 1", "one"); + client.set("recon 2", "two", function (err, res) { + // Do not do this in normal programs. This is to simulate the server closing on us. + // For orderly shutdown in normal programs, do client.quit() + client.stream.destroy(); + }); + }); + + describe("and it's subscribed to a channel", function () { + // reconnect_select_db_after_pubsub + // Does not pass. + // "Connection in subscriber mode, only subscriber commands may be used" + xit("reconnects, unsubscribes, and can retrieve the pre-existing data", function (done) { + client.on("reconnecting", function on_recon(params) { + client.on("ready", function on_connect() { + async.parallel([function (cb) { + client.unsubscribe("recon channel", function (err, res) { + helper.isNotError()(err, res); + cb(); + }); + }, function (cb) { + client.get("recon 1", function (err, res) { + helper.isString("one")(err, res); + cb(); + }); + }], function (err, results) { + client.removeListener("connect", on_connect); + client.removeListener("reconnecting", on_recon); + done(err); + }); + }); + }); + + client.set("recon 1", "one"); + client.subscribe("recon channel", function (err, res) { + // Do not do this in normal programs. This is to simulate the server closing on us. + // For orderly shutdown in normal programs, do client.quit() + client.stream.destroy(); + }); + }); + + it("remains subscribed", function () { + var client2 = redis.createClient.apply(redis.createClient, args); + + client.on("reconnecting", function on_recon(params) { + client.on("ready", function on_connect() { + async.parallel([function (cb) { + client.on("message", function (channel, message) { + try { + helper.isString("recon channel")(null, channel); + helper.isString("a test message")(null, message); + } catch (err) { + cb(err); + } + }); + + client2.subscribe("recon channel", function (err, res) { + if (err) { + cb(err); + return; + } + client2.publish("recon channel", "a test message"); + }); + }], function (err, results) { + done(err); + }); + }); + }); + + client.subscribe("recon channel", function (err, res) { + // Do not do this in normal programs. This is to simulate the server closing on us. + // For orderly shutdown in normal programs, do client.quit() + client.stream.destroy(); + }); + }); + }); + + describe('domain', function () { + it('allows client to be executed from within domain', function (done) { + var domain; + + try { + domain = require('domain').create(); + } catch (err) { + console.log("Skipping " + name + " because this version of node doesn't have domains."); + return done(); + } + + if (domain) { + domain.run(function () { + client.set('domain', 'value', function (err, res) { + assert.ok(process.domain); + var notFound = res.not.existing.thing; // ohhh nooooo + }); + }); + + // this is the expected and desired behavior + domain.on('error', function (err) { + domain.exit(); + return done() + }); + } + }) + }) + + }); + + it('emits errors thrown from within an on("message") handler', function (done) { + var client2 = redis.createClient.apply(redis.createClient, args); + var name = 'channel'; + + client2.subscribe(name, function () { + client.publish(name, "some message"); + }); + + client2.on("message", function (channel, data) { + if (channel == name) { + assert.equal(data, "some message"); + throw Error('forced exception'); + } + return done(); + }); + + client2.once("error", function (err) { + client2.end(); + assert.equal(err.message, 'forced exception'); + return done(); + }); + }); + + describe('idle', function () { + it('emits idle as soon as there are no outstanding commands', function (done) { + client.on('idle', function onIdle () { + client.removeListener("idle", onIdle); + client.get('foo', helper.isString('bar', done)); + }); + client.set('foo', 'bar'); + }); + }); + + describe('utf8', function () { + it('handles utf-8 keys', function (done) { + var utf8_sample = "ಠ_ಠ"; + client.set(["utf8test", utf8_sample], helper.isString("OK")); + client.get(["utf8test"], function (err, obj) { + assert.strictEqual(utf8_sample, obj); + return done(err); + }); + }); + }); + }); + + describe('detect_buffers', function () { + var client; + var args = config.configureClient(parser, ip, { + detect_buffers: true + }); + + beforeEach(function (done) { + client = redis.createClient.apply(redis.createClient, args); + client.once("error", done); + client.once("connect", function () { + client.flushdb(function (err) { + client.hmset("hash key 2", "key 1", "val 1", "key 2", "val 2"); + client.set("string key 1", "string value"); + return done(err); + }); + }); + }); + + describe('get', function () { + describe('first argument is a string', function () { + it('returns a string', function (done) { + client.get("string key 1", helper.isString("string value", done)); + }); + + it('returns a string when executed as part of transaction', function (done) { + client.multi().get("string key 1").exec(helper.isString("string value", done)); + }); + }); + + describe('first argument is a buffer', function () { + it('returns a buffer', function (done) { + client.get(new Buffer("string key 1"), function (err, reply) { + assert.strictEqual(true, Buffer.isBuffer(reply)); + assert.strictEqual("", reply.inspect()); + return done(err); + }); + }); + + it('returns a bufffer when executed as part of transaction', function (done) { + client.multi().get(new Buffer("string key 1")).exec(function (err, reply) { + assert.strictEqual(1, reply.length); + assert.strictEqual(true, Buffer.isBuffer(reply[0])); + assert.strictEqual("", reply[0].inspect()); + return done(err); + }); + }); + }); + }); + + describe('multi.hget', function () { + it('can interleave string and buffer results', function (done) { + client.multi() + .hget("hash key 2", "key 1") + .hget(new Buffer("hash key 2"), "key 1") + .hget("hash key 2", new Buffer("key 2")) + .hget("hash key 2", "key 2") + .exec(function (err, reply) { + assert.strictEqual(true, Array.isArray(reply)); + assert.strictEqual(4, reply.length); + assert.strictEqual("val 1", reply[0]); + assert.strictEqual(true, Buffer.isBuffer(reply[1])); + assert.strictEqual("", reply[1].inspect()); + assert.strictEqual(true, Buffer.isBuffer(reply[2])); + assert.strictEqual("", reply[2].inspect()); + assert.strictEqual("val 2", reply[3]); + return done(err); + }); + }); + }); + + describe('hmget', function () { + describe('first argument is a string', function () { + it('returns strings for keys requested', function (done) { + client.hmget("hash key 2", "key 1", "key 2", function (err, reply) { + assert.strictEqual(true, Array.isArray(reply)); + assert.strictEqual(2, reply.length); + assert.strictEqual("val 1", reply[0]); + assert.strictEqual("val 2", reply[1]); + return done(err); + }); + }); + + it('returns strings for keys requested in transaction', function (done) { + client.multi().hmget("hash key 2", "key 1", "key 2").exec(function (err, reply) { + assert.strictEqual(true, Array.isArray(reply)); + assert.strictEqual(1, reply.length); + assert.strictEqual(2, reply[0].length); + assert.strictEqual("val 1", reply[0][0]); + assert.strictEqual("val 2", reply[0][1]); + return done(err); + }); + }); + + it('handles array of strings with undefined values (repro #344)', function (done) { + client.hmget("hash key 2", "key 3", "key 4", function(err, reply) { + assert.strictEqual(true, Array.isArray(reply)); + assert.strictEqual(2, reply.length); + assert.equal(null, reply[0]); + assert.equal(null, reply[1]); + return done(err); + }); + }); + + it('handles array of strings with undefined values in transaction (repro #344)', function (done) { + client.multi().hmget("hash key 2", "key 3", "key 4").exec(function(err, reply) { + assert.strictEqual(true, Array.isArray(reply)); + assert.strictEqual(1, reply.length); + assert.strictEqual(2, reply[0].length); + assert.equal(null, reply[0][0]); + assert.equal(null, reply[0][1]); + return done(err); + }); + }); + }); + + describe('first argument is a buffer', function () { + it('returns buffers for keys requested', function (done) { + client.hmget(new Buffer("hash key 2"), "key 1", "key 2", function (err, reply) { + assert.strictEqual(true, Array.isArray(reply)); + assert.strictEqual(2, reply.length); + assert.strictEqual(true, Buffer.isBuffer(reply[0])); + assert.strictEqual(true, Buffer.isBuffer(reply[1])); + assert.strictEqual("", reply[0].inspect()); + assert.strictEqual("", reply[1].inspect()); + return done(err); + }); + }); + + it("returns buffers for keys requested in transaction", function (done) { + client.multi().hmget(new Buffer("hash key 2"), "key 1", "key 2").exec(function (err, reply) { + assert.strictEqual(true, Array.isArray(reply)); + assert.strictEqual(1, reply.length); + assert.strictEqual(2, reply[0].length); + assert.strictEqual(true, Buffer.isBuffer(reply[0][0])); + assert.strictEqual(true, Buffer.isBuffer(reply[0][1])); + assert.strictEqual("", reply[0][0].inspect()); + assert.strictEqual("", reply[0][1].inspect()); + return done(err); + }); + }); + }); + }); + + describe('hgetall', function (done) { + describe('first argument is a string', function () { + it('returns string values', function (done) { + client.hgetall("hash key 2", function (err, reply) { + assert.strictEqual("object", typeof reply); + assert.strictEqual(2, Object.keys(reply).length); + assert.strictEqual("val 1", reply["key 1"]); + assert.strictEqual("val 2", reply["key 2"]); + return done(err); + }); + }); + + it('returns string values when executed in transaction', function (done) { + client.multi().hgetall("hash key 2").exec(function (err, reply) { + assert.strictEqual(1, reply.length); + assert.strictEqual("object", typeof reply[0]); + assert.strictEqual(2, Object.keys(reply[0]).length); + assert.strictEqual("val 1", reply[0]["key 1"]); + assert.strictEqual("val 2", reply[0]["key 2"]); + return done(err); + }); + }); + }); + + describe('first argument is a buffer', function () { + it('returns buffer values', function (done) { + client.hgetall(new Buffer("hash key 2"), function (err, reply) { + assert.strictEqual(null, err); + assert.strictEqual("object", typeof reply); + assert.strictEqual(2, Object.keys(reply).length); + assert.strictEqual(true, Buffer.isBuffer(reply["key 1"])); + assert.strictEqual(true, Buffer.isBuffer(reply["key 2"])); + assert.strictEqual("", reply["key 1"].inspect()); + assert.strictEqual("", reply["key 2"].inspect()); + return done(err); + }); + }); + + it('returns buffer values when executed in transaction', function (done) { + client.multi().hgetall(new Buffer("hash key 2")).exec(function (err, reply) { + assert.strictEqual(1, reply.length); + assert.strictEqual("object", typeof reply); + assert.strictEqual(2, Object.keys(reply[0]).length); + assert.strictEqual(true, Buffer.isBuffer(reply[0]["key 1"])); + assert.strictEqual(true, Buffer.isBuffer(reply[0]["key 2"])); + assert.strictEqual("", reply[0]["key 1"].inspect()); + assert.strictEqual("", reply[0]["key 2"].inspect()); + return done(err); + }); + }); + }); + }); + }); + + describe('unref', function () { + it('exits subprocess as soon as final command is processed', function (done) { + var args = config.HOST[ip] ? [config.HOST[ip], config.PORT] : [ip]; + var external = fork("./test/lib/unref.js", args); + var id = setTimeout(function () { + external.kill(); + return done(Error('unref subprocess timed out')); + }, 5000); + + external.on("close", function (code) { + clearTimeout(id); + assert.strictEqual(code, 0); + return done(); + }); + }); + }); + + describe('socket_nodelay', function () { + describe('true', function () { + var client; + var args = config.configureClient(parser, ip, { + socket_nodelay: true + }); + + it("fires client.on('ready')", function (done) { + client = redis.createClient.apply(redis.createClient, args); + client.on("ready", function () { + assert.strictEqual(true, client.options.socket_nodelay); + client.quit(); + + client.once('end', function () { + return done(); + }); + }); + }); + + it('client is functional', function (done) { + client = redis.createClient.apply(redis.createClient, args); + client.on("ready", function () { + assert.strictEqual(true, client.options.socket_nodelay); + client.set(["set key 1", "set val"], helper.isString("OK")); + client.set(["set key 2", "set val"], helper.isString("OK")); + client.get(["set key 1"], helper.isString("set val")); + client.get(["set key 2"], helper.isString("set val")); + client.quit(); + + client.once('end', function () { + return done(); + }); + }); + }); + }); + + describe('false', function () { + var client; + var args = config.configureClient(parser, ip, { + socket_nodelay: false + }); + + it("fires client.on('ready')", function (done) { + client = redis.createClient.apply(redis.createClient, args); + client.on("ready", function () { + assert.strictEqual(false, client.options.socket_nodelay); + client.quit(); + + client.once('end', function () { + return done(); + }); + }); + }); + + it('client is functional', function (done) { + client = redis.createClient.apply(redis.createClient, args); + client.on("ready", function () { + assert.strictEqual(false, client.options.socket_nodelay); + client.set(["set key 1", "set val"], helper.isString("OK")); + client.set(["set key 2", "set val"], helper.isString("OK")); + client.get(["set key 1"], helper.isString("set val")); + client.get(["set key 2"], helper.isString("set val")); + client.quit(); + + client.once('end', function () { + return done(); + }); + }); + }); + }); + + describe('defaults to true', function () { + var client; + var args = config.configureClient(parser, ip); + + it("fires client.on('ready')", function (done) { + client = redis.createClient.apply(redis.createClient, args); + client.on("ready", function () { + assert.strictEqual(true, client.options.socket_nodelay); + client.quit(); + + client.once('end', function () { + return done(); + }); + }); + }); + + it('client is functional', function (done) { + client = redis.createClient.apply(redis.createClient, args); + client.on("ready", function () { + assert.strictEqual(true, client.options.socket_nodelay); + client.set(["set key 1", "set val"], helper.isString("OK")); + client.set(["set key 2", "set val"], helper.isString("OK")); + client.get(["set key 1"], helper.isString("set val")); + client.get(["set key 2"], helper.isString("set val")); + client.quit(); + + client.once('end', function () { + return done(); + }); + }); + }); + }); + }); + + describe('retry_max_delay', function () { + var client; + var args = config.configureClient(parser, ip, { + retry_max_delay: 1 + }); + + it("sets upper bound on how long client waits before reconnecting", function (done) { + var time = new Date().getTime() + var reconnecting = false; + + client = redis.createClient.apply(redis.createClient, args); + client.on('ready', function() { + if (!reconnecting) { + reconnecting = true; + client.retry_delay = 1000; + client.retry_backoff = 1; + client.stream.end(); + } else { + client.end(); + var lasted = new Date().getTime() - time; + assert.ok(lasted < 1000); + return done(); + } + }); + }); + }); + + describe('enable_offline_queue', function () { + describe('true', function () { + it("does not throw an error and enqueues operation", function (done) { + var client = redis.createClient(9999, null, { + max_attempts: 1, + parser: parser + }); + + client.on('error', function(e) { + // ignore, b/c expecting a "can't connect" error + }); + + return setTimeout(function() { + client.set('foo', 'bar', function(err, result) { + if (err) return done(err); + }); + + return setTimeout(function(){ + assert.strictEqual(client.offline_queue.length, 1); + return done(); + }, 25); + }, 50); + }); + }); + + describe('false', function () { + it("does not throw an error and enqueues operation", function (done) { + var client = redis.createClient(9999, null, { + parser: parser, + max_attempts: 1, + enable_offline_queue: false + }); + + client.on('error', function() { + // ignore, b/c expecting a "can't connect" error + }); + + assert.throws(function () { + cli.set('foo', 'bar'); + }); + + assert.doesNotThrow(function () { + client.set('foo', 'bar', function (err) { + // should callback with an error + assert.ok(err); + setTimeout(function () { + return done(); + }, 50); + }); + }); + }); + }); + }); + + }); + } + + ['javascript', 'hiredis'].forEach(function (parser) { + allTests(parser, "/tmp/redis.sock"); + ['IPv4', 'IPv6'].forEach(function (ip) { + allTests(parser, ip); + }); + }); +}); diff --git a/test/parser/javascript.spec.js b/test/parser/javascript.spec.js new file mode 100644 index 00000000000..445964371b2 --- /dev/null +++ b/test/parser/javascript.spec.js @@ -0,0 +1,27 @@ +/* global describe, it */ + +var assert = require('assert'); +var Parser = require("../../lib/parser/javascript").Parser; + +describe('javascript parser', function () { + it('handles multi-bulk reply', function (done) { + var parser = new Parser(false); + var reply_count = 0; + function check_reply(reply) { + assert.deepEqual(reply, [['a']], "Expecting multi-bulk reply of [['a']]"); + reply_count++; + } + parser.on("reply", check_reply); + + parser.execute(new Buffer('*1\r\n*1\r\n$1\r\na\r\n')); + + parser.execute(new Buffer('*1\r\n*1\r')); + parser.execute(new Buffer('\n$1\r\na\r\n')); + + parser.execute(new Buffer('*1\r\n*1\r\n')); + parser.execute(new Buffer('$1\r\na\r\n')); + + assert.equal(reply_count, 3, "check reply should have been called three times"); + return done(); + }); +}); diff --git a/test/pubsub.spec.js b/test/pubsub.spec.js new file mode 100644 index 00000000000..dc502461511 --- /dev/null +++ b/test/pubsub.spec.js @@ -0,0 +1,240 @@ +var assert = require("assert"); +var config = require("./lib/config"); +var helper = require("./helper"); +var redis = config.redis; + +describe("publish/subscribe", function () { + + function allTests(parser, ip) { + var args = config.configureClient(parser, ip); + + describe("using " + parser + " and " + ip, function () { + var pub = null; + var sub = null; + var channel = "test channel" + var channel2 = "test channel 2" + var message = "test message" + var hash = "test hash"; + + beforeEach(function (done) { + var pubConnected; + var subConnected; + + pub = redis.createClient.apply(redis.createClient, args); + sub = redis.createClient.apply(redis.createClient, args); + pub.once("error", done); + pub.once("connect", function () { + pub.flushdb(function () { + pubConnected = true; + }); + }); + + sub.once("error", done); + sub.once("connect", function () { + subConnected = true; + }); + + var id = setInterval(function () { + if (pubConnected && subConnected) { + clearInterval(id); + return done(); + } + }, 50); + }); + + describe('subscribe', function () { + it('fires a subscribe event for each channel subscribed to', function (done) { + sub.on("subscribe", function (chnl, count) { + if (chnl === channel2) { + assert.equal(2, count) + return done(); + } + }); + + sub.subscribe(channel, channel2); + }); + + it('receives messages on subscribed channel', function (done) { + sub.on("subscribe", function (chnl, count) { + pub.publish(channel, message, helper.isNumber(1)); + }); + + sub.on("message", function (chnl, msg) { + assert.equal(chnl, channel); + assert.equal(msg, message); + return done(); + }); + + sub.subscribe(channel); + }); + + it('receives messages if subscribe is called after unsubscribe', function (done) { + if (!helper.serverVersionAtLeast(pub, [2, 6, 11])) return done(); + + sub.once("subscribe", function (chnl, count) { + pub.publish(channel, message, helper.isNumber(1)); + }); + + sub.on("message", function (chnl, msg) { + assert.equal(chnl, channel); + assert.equal(msg, message); + return done(); + }); + + sub.subscribe(channel); + sub.unsubscribe(channel); + sub.subscribe(channel); + }); + + it('handles SUB_UNSUB_MSG_SUB', function (done) { + if (!helper.serverVersionAtLeast(pub, [2, 6, 11])) return done(); + + sub.subscribe('chan8'); + sub.subscribe('chan9'); + sub.unsubscribe('chan9'); + pub.publish('chan8', 'something'); + sub.subscribe('chan9', function () { + return done(); + }); + }); + + it('handles SUB_UNSUB_MSG_SUB', function (done) { + if (!helper.serverVersionAtLeast(pub, [2, 6, 11])) return done(); + + sub.psubscribe('abc*'); + sub.subscribe('xyz'); + sub.unsubscribe('xyz'); + pub.publish('abcd', 'something'); + sub.subscribe('xyz', function () { + return done(); + }); + }); + + it('emits end event if quit is called from within subscribe', function (done) { + sub.on("end", function () { + return done(); + }); + sub.on("subscribe", function (chnl, count) { + sub.quit(); + }); + sub.subscribe(channel); + }); + + it('handles SUBSCRIBE_CLOSE_RESUBSCRIBE', function (done) { + var count = 0; + /* Create two clients. c1 subscribes to two channels, c2 will publish to them. + c2 publishes the first message. + c1 gets the message and drops its connection. It must resubscribe itself. + When it resubscribes, c2 publishes the second message, on the same channel + c1 gets the message and drops its connection. It must resubscribe itself, again. + When it resubscribes, c2 publishes the third message, on the second channel + c1 gets the message and drops its connection. When it reconnects, the test ends. + */ + sub.on("message", function(channel, message) { + if (channel === "chan1") { + assert.strictEqual(message, "hi on channel 1"); + sub.stream.end(); + } else if (channel === "chan2") { + assert.strictEqual(message, "hi on channel 2"); + sub.stream.end(); + } else { + sub.quit(); + pub.quit(); + assert.fail("test failed"); + } + }); + + sub.subscribe("chan1", "chan2"); + + sub.on("ready", function(err, results) { + count++; + if (count === 1) { + pub.publish("chan1", "hi on channel 1"); + return; + } else if (count === 2) { + pub.publish("chan2", "hi on channel 2"); + } else { + sub.quit(function() { + pub.quit(function() { + return done(); + }); + }); + } + }); + + pub.publish("chan1", "hi on channel 1"); + }); + }); + + describe('unsubscribe', function () { + it('fires an unsubscribe event', function () { + sub.on("subscribe", function (chnl, count) { + sub.unsubscribe(channel) + }); + + sub.subscribe(channel); + + sub.on("unsubscribe", function (chnl, count) { + assert.equal(chnl, channel); + assert.strictEqual(count, 0); + return done(); + }); + }); + + it('puts client back into write mode', function (done) { + sub.on("subscribe", function (chnl, count) { + sub.unsubscribe(channel) + }); + + sub.subscribe(channel); + + sub.on("unsubscribe", function (chnl, count) { + pub.incr("foo", helper.isNumber(1, done)); + }); + }) + + it('does not complain when unsubscribe is called and there are no subscriptions', function () { + sub.unsubscribe() + }); + + it('executes callback when unsubscribe is called and there are no subscriptions', function (done) { + // test hangs on older versions of redis, so skip + if (!helper.serverVersionAtLeast(pub, [2, 6, 11])) return done(); + + pub.unsubscribe(function (err, results) { + assert.strictEqual(null, results); + return done(err); + }); + }); + }); + + describe('punsubscribe', function () { + it('does not complain when punsubscribe is called and there are no subscriptions', function () { + sub.punsubscribe() + }) + + it('executes callback when punsubscribe is called and there are no subscriptions', function (done) { + // test hangs on older versions of redis, so skip + if (!helper.serverVersionAtLeast(pub, [2, 6, 11])) return done(); + + pub.punsubscribe(function (err, results) { + assert.strictEqual(null, results); + return done(err); + }); + }); + }); + + afterEach(function () { + sub.end(); + pub.end(); + }); + }); + } + + ['javascript', 'hiredis'].forEach(function (parser) { + allTests(parser, "/tmp/redis.sock"); + ['IPv4', 'IPv6'].forEach(function (ip) { + allTests(parser, ip); + }) + }); +}); diff --git a/test/queue-test.js b/test/queue-test.js deleted file mode 100644 index dbc9771a0aa..00000000000 --- a/test/queue-test.js +++ /dev/null @@ -1,37 +0,0 @@ -'use strict'; - -var assert = require("assert"); -var Queue = require('../lib/queue'); - -module.exports = function (tests, next) { - var q = new Queue(); - - tests.push = function () { - q.push('a'); - q.push(3); - assert.equal(q.length, 2); - return next(); - }; - - tests.shift = function () { - assert.equal(q.shift(), 'a'); - return next(); - }; - - tests.forEach = function () { - q.forEach(function (v) { - assert.equal(v, 3); - }); - - return next(); - }; - - tests.forEachWithScope = function () { - q.forEach(function (v) { - assert.equal(this.foo, 'bar'); - assert.equal(v, 3); - }, {foo: 'bar'}); - - return next(); - }; -}; diff --git a/test/queue.spec.js b/test/queue.spec.js new file mode 100644 index 00000000000..f0b3c316489 --- /dev/null +++ b/test/queue.spec.js @@ -0,0 +1,37 @@ +var assert = require("assert"); +var Queue = require('../lib/queue'); + +describe('queue', function () { + var q = new Queue(); + + describe('push', function () { + it('places values on end of queue', function () { + q.push('a'); + q.push(3); + assert.equal(q.length, 2); + }); + }); + + describe('shift', function () { + it('removes values from front of queue', function () { + assert.equal(q.shift(), 'a'); + }); + }); + + describe('forEach', function () { + it('iterates over values in queue', function () { + q.forEach(function (v) { + assert.equal(v, 3); + }); + }); + }); + + describe('forEachWithScope', function () { + it('provides a scope to the iteration function', function () { + q.forEach(function (v) { + assert.equal(this.foo, 'bar'); + assert.equal(v, 3); + }, {foo: 'bar'}); + }); + }); +}); diff --git a/test/run.sh b/test/run.sh deleted file mode 100755 index ab030ba48fa..00000000000 --- a/test/run.sh +++ /dev/null @@ -1,4 +0,0 @@ -#!/usr/bin/env bash - -node ./test/test.js false hiredis -node ./test/test.js false javascript diff --git a/test/test-unref.js b/test/test-unref.js deleted file mode 100644 index c7dc9300047..00000000000 --- a/test/test-unref.js +++ /dev/null @@ -1,14 +0,0 @@ -'use strict'; - -var redis = require("../"); -//redis.debug_mode = true -var PORT = process.argv[2] || 6379; -var HOST = process.argv[3] || '127.0.0.1'; - -var c = redis.createClient(PORT, HOST); -c.unref(); -c.info(function (err, reply) { - if (err) process.exit(-1); - if (!reply.length) process.exit(-1); - process.stdout.write(reply.length.toString()); -}); diff --git a/test/test.js b/test/test.js index 7b3cb1cc721..53a2b663bb8 100644 --- a/test/test.js +++ b/test/test.js @@ -1,1521 +1,4 @@ -'use strict'; - -/*global require console setTimeout process Buffer */ -var PORT = process.env.REDIS_PORT_6379_TCP_PORT || 6379; -var HOST = process.env.REDIS_PORT_6379_TCP_ADDR || '127.0.0.1'; -var parser = process.argv[3]; - -var redis = require("../index"), - client = redis.createClient(PORT, HOST, { parser: parser }), - client2 = redis.createClient(PORT, HOST, { parser: parser }), - client3 = redis.createClient(PORT, HOST, { parser: parser }), - bclient = redis.createClient(PORT, HOST, { return_buffers: true, parser: parser }), - assert = require("assert"), - crypto = require("crypto"), - util = require("../lib/util"), - fork = require("child_process").fork, - test_db_num = 15, // this DB will be flushed and used for testing - tests = {}, - connected = false, - ended = false, - next, cur_start, run_next_test, all_tests, all_start, test_count; - -// Set this to truthy to see the wire protocol and other debugging info -redis.debug_mode = process.argv[2] ? JSON.parse(process.argv[2]) : false; - -function server_version_at_least(connection, desired_version) { - // Return true if the server version >= desired_version - var version = connection.server_info.versions; - for (var i = 0; i < 3; i++) { - if (version[i] > desired_version[i]) return true; - if (version[i] < desired_version[i]) return false; - } - return true; -} - -function buffers_to_strings(arr) { - return arr.map(function (val) { - return val.toString(); - }); -} - -function require_number(expected, label) { - return function (err, results) { - assert.strictEqual(null, err, label + " expected " + expected + ", got error: " + err); - assert.strictEqual(expected, results, label + " " + expected + " !== " + results); - assert.strictEqual(typeof results, "number", label); - return true; - }; -} - -function require_number_any(label) { - return function (err, results) { - assert.strictEqual(null, err, label + " expected any number, got error: " + err); - assert.strictEqual(typeof results, "number", label + " " + results + " is not a number"); - return true; - }; -} - -function require_number_pos(label) { - return function (err, results) { - assert.strictEqual(null, err, label + " expected positive number, got error: " + err); - assert.strictEqual(true, (results > 0), label + " " + results + " is not a positive number"); - return true; - }; -} - -function require_string(str, label) { - return function (err, results) { - assert.strictEqual(null, err, label + " expected string '" + str + "', got error: " + err); - assert.equal(str, results, label + " " + str + " does not match " + results); - return true; - }; -} - -function require_null(label) { - return function (err, results) { - assert.strictEqual(null, err, label + " expected null, got error: " + err); - assert.strictEqual(null, results, label + ": " + results + " is not null"); - return true; - }; -} - -function require_error(label) { - return function (err, results) { - assert.notEqual(err, null, label + " err is null, but an error is expected here."); - return true; - }; -} - -function is_empty_array(obj) { - return Array.isArray(obj) && obj.length === 0; -} - -function last(name, fn) { - return function (err, results) { - fn(err, results); - next(name); - }; -} - -// Wraps the given callback in a timeout. If the returned function -// is not called within the timeout period, we fail the named test. -function with_timeout(name, cb, millis) { - var timeoutId = setTimeout(function() { - assert.fail("Callback timed out!", name); - }, millis); - return function() { - clearTimeout(timeoutId); - cb.apply(this, arguments); - }; -} - -next = function next(name) { - console.log(" \x1b[33m" + (Date.now() - cur_start) + "\x1b[0m ms"); - run_next_test(); -}; - -// Tests are run in the order they are defined, so FLUSHDB should always be first. - -<<<<<<< HEAD -<<<<<<< HEAD -<<<<<<< HEAD -<<<<<<< HEAD -tests.IPV4 = function () { - var ipv4addr = process.env.REDIS_PORT_6379_TCP_ADDR || "127.0.0.1"; - var ipv4Client = redis.createClient( PORT, ipv4addr, { family : "IPv4", parser: parser } ); - - ipv4Client.once("ready", function start_tests() { - console.log("Connected to " + ipv4Client.address + ", Redis server version " + ipv4Client.server_info.redis_version + "\n"); - console.log("Using reply parser " + ipv4Client.reply_parser.name); - - ipv4Client.quit(); - run_next_test(); - }); - - ipv4Client.on('end', function () { - - }); - - // Exit immediately on connection failure, which triggers "exit", below, which fails the test - ipv4Client.on("error", function (err) { - console.error("client: " + err.stack); - process.exit(); - }); -}; - -tests.IPV6 = function () { - if (!server_version_at_least(client, [2, 8, 0])) { - console.log("Skipping IPV6 for old Redis server version < 2.8.0"); - return run_next_test(); - } - var ipv6addr = process.env.REDIS_PORT_6379_TCP_ADDR || "::1"; - var ipv6Client = redis.createClient( PORT, ipv6addr, { family: "IPv6", parser: parser } ); - - ipv6Client.once("ready", function start_tests() { - console.log("Connected to " + ipv6Client.address + ", Redis server version " + ipv6Client.server_info.redis_version + "\n"); - console.log("Using reply parser " + ipv6Client.reply_parser.name); - - ipv6Client.quit(); - run_next_test(); - }); - - ipv6Client.on('end', function () { - - }); - - // Exit immediately on connection failure, which triggers "exit", below, which fails the test - ipv6Client.on("error", function (err) { - console.error("client: " + err.stack); - process.exit(); - }); -}; - -tests.UNIX_SOCKET = function () { - var unixClient = redis.createClient('/tmp/redis.sock', { parser: parser }); - - // if this fails, check the permission of unix socket. - // unixsocket /tmp/redis.sock - // unixsocketperm 777 - - unixClient.once('ready', function start_tests(){ - console.log("Connected to " + unixClient.address + ", Redis server version " + unixClient.server_info.redis_version + "\n"); - console.log("Using reply parser " + unixClient.reply_parser.name); - - unixClient.quit(); - run_next_test(); - }); - - unixClient.on( 'end', function(){ - - }); - - // Exit immediately on connection failure, which triggers "exit", below, which fails the test - unixClient.on("error", function (err) { - console.error("client: " + err.stack); - process.exit(); - }); -}; - -tests.FLUSHDB = function () { - var name = "FLUSHDB"; - client.select(test_db_num, require_string("OK", name)); - client2.select(test_db_num, require_string("OK", name)); - client3.select(test_db_num, require_string("OK", name)); - client.mset("flush keys 1", "flush val 1", "flush keys 2", "flush val 2", require_string("OK", name)); - client.FLUSHDB(require_string("OK", name)); - client.dbsize(last(name, require_number(0, name))); -}; - -tests.INCR = function () { - var name = "INCR"; - - if (bclient.reply_parser.name === "hiredis") { - console.log("Skipping INCR buffer test with hiredis"); - return next(name); - } - - // Test incr with the maximum JavaScript number value. Since we are - // returning buffers we should get back one more as a Buffer. - bclient.set("seq", "9007199254740992", function (err, result) { - assert.strictEqual(result.toString(), "OK"); - bclient.incr("seq", function (err, result) { - assert.strictEqual("9007199254740993", result.toString()); - next(name); - }); - }); -}; - -tests.MULTI_1 = function () { - var name = "MULTI_1", multi1, multi2; - - // Provoke an error at queue time - multi1 = client.multi(); - multi1.mset("multifoo", "10", "multibar", "20", require_string("OK", name)); - multi1.set("foo2", require_error(name)); - multi1.incr("multifoo", require_number(11, name)); - multi1.incr("multibar", require_number(21, name)); - multi1.exec(function () { - require_error(name); - - // Redis 2.6.5+ will abort transactions with errors - // see: http://redis.io/topics/transactions - var multibar_expected = 22; - var multifoo_expected = 12; - if (server_version_at_least(client, [2, 6, 5])) { - multibar_expected = 1; - multifoo_expected = 1; - } - - // Confirm that the previous command, while containing an error, still worked. - multi2 = client.multi(); - multi2.incr("multibar", require_number(multibar_expected, name)); - multi2.incr("multifoo", require_number(multifoo_expected, name)); - multi2.exec(function (err, replies) { - assert.strictEqual(multibar_expected, replies[0]); - assert.strictEqual(multifoo_expected, replies[1]); - next(name); - }); - }); -}; - -tests.MULTI_2 = function () { - var name = "MULTI_2"; - - // test nested multi-bulk replies - client.multi([ - ["mget", "multifoo", "multibar", function (err, res) { - assert.strictEqual(2, res.length, name); - assert.strictEqual("12", res[0].toString(), name); - assert.strictEqual("22", res[1].toString(), name); - }], - ["set", "foo2", require_error(name)], - ["incr", "multifoo", require_number(13, name)], - ["incr", "multibar", require_number(23, name)] - - ]).exec(function (err, replies) { - - if (server_version_at_least(client, [2, 6, 5])) { - assert.notEqual(err, null, name); - assert.equal(replies, undefined, name); - } else { - assert.strictEqual(2, replies[0].length, name); - assert.strictEqual("12", replies[0][0].toString(), name); - assert.strictEqual("22", replies[0][1].toString(), name); - - assert.strictEqual("13", replies[1].toString()); - assert.strictEqual("23", replies[2].toString()); - } - next(name); - }); -}; - -tests.MULTI_3 = function () { - var name = "MULTI_3"; - - client.sadd("some set", "mem 1"); - client.sadd("some set", "mem 2"); - client.sadd("some set", "mem 3"); - client.sadd("some set", "mem 4"); - - // make sure empty mb reply works - client.del("some missing set"); - client.smembers("some missing set", function (err, reply) { - // make sure empty mb reply works - assert.strictEqual(true, is_empty_array(reply), name); - }); - - // test nested multi-bulk replies with empty mb elements. - client.multi([ - ["smembers", "some set"], - ["del", "some set"], - ["smembers", "some set"] - ]) - .scard("some set") - .exec(function (err, replies) { - assert.strictEqual(true, is_empty_array(replies[2]), name); - next(name); - }); -}; - -tests.MULTI_4 = function () { - var name = "MULTI_4"; - - client.multi() - .mset('some', '10', 'keys', '20') - .incr('some') - .incr('keys') - .mget('some', 'keys') - .exec(function (err, replies) { - assert.strictEqual(null, err); - assert.equal('OK', replies[0]); - assert.equal(11, replies[1]); - assert.equal(21, replies[2]); - assert.equal(11, replies[3][0].toString()); - assert.equal(21, replies[3][1].toString()); - next(name); - }); -}; - -tests.MULTI_5 = function () { - var name = "MULTI_5"; - - // test nested multi-bulk replies with nulls. - client.multi([ - ["mget", ["multifoo", "some", "random value", "keys"]], - ["incr", "multifoo"] - ]) - .exec(function (err, replies) { - assert.strictEqual(replies.length, 2, name); - assert.strictEqual(replies[0].length, 4, name); - next(name); - }); -}; - -tests.MULTI_6 = function () { - var name = "MULTI_6"; - - client.multi() - .hmset("multihash", "a", "foo", "b", 1) - .hmset("multihash", { - extra: "fancy", - things: "here" - }) - .hgetall("multihash") - .exec(function (err, replies) { - assert.strictEqual(null, err); - assert.equal("OK", replies[0]); - assert.equal(Object.keys(replies[2]).length, 4); - assert.equal("foo", replies[2].a); - assert.equal("1", replies[2].b); - assert.equal("fancy", replies[2].extra); - assert.equal("here", replies[2].things); - next(name); - }); -}; - -// THIS TEST SHOULD BE MOVED IN TO A PARSER -// SPECIFIC TESTING FILE. -tests.MULTI_7 = function () { - var name = "MULTI_7"; - - if (bclient.reply_parser.name !== "javascript") { - console.log("Skipping wire-protocol test for 3rd-party parser"); - return next(name); - } - - var p = require("../lib/parser/javascript"); - var parser = new p.Parser(false); - var reply_count = 0; - function check_reply(reply) { - assert.deepEqual(reply, [['a']], "Expecting multi-bulk reply of [['a']]"); - reply_count++; - assert.notEqual(reply_count, 4, "Should only parse 3 replies"); - } - parser.on("reply", check_reply); - - parser.execute(new Buffer('*1\r\n*1\r\n$1\r\na\r\n')); - - parser.execute(new Buffer('*1\r\n*1\r')); - parser.execute(new Buffer('\n$1\r\na\r\n')); - - parser.execute(new Buffer('*1\r\n*1\r\n')); - parser.execute(new Buffer('$1\r\na\r\n')); - - next(name); -}; - -tests.FWD_ERRORS_1 = function () { - var name = "FWD_ERRORS_1"; - - var toThrow = new Error("Forced exception"); - var recordedError = null; - - var originalHandlers = { - "error": client3.listeners("error"), - "message": client3.listeners("message") - }; - client3.removeAllListeners("error"); - client3.removeAllListeners("message"); - client3.once("error", function (err) { - recordedError = err; - }); - - client3.on("message", function (channel, data) { - console.log("incoming"); - if (channel === name) { - assert.equal(data, "Some message"); - throw toThrow; - } - }); - client3.subscribe(name); - - client.publish(name, "Some message"); - setTimeout(function () { - assert.equal(recordedError, toThrow, "Should have caught our forced exception"); - client3.unsubscribe(name); - client3.removeAllListeners("message"); - originalHandlers.error.forEach(function (fn) { - client3.on("error", fn); - }); - originalHandlers.message.forEach(function (fn) { - client3.on("message", fn); - }); - next(name); - }, 150); -}; - -tests.EVAL_1 = function () { - var name = "EVAL_1"; - - if (!server_version_at_least(client, [2, 5, 0])) { - console.log("Skipping " + name + " for old Redis server version < 2.5.x"); - return next(name); - } - - // test {EVAL - Lua integer -> Redis protocol type conversion} - client.eval("return 100.5", 0, require_number(100, name)); - // test {EVAL - Lua string -> Redis protocol type conversion} - client.eval("return 'hello world'", 0, require_string("hello world", name)); - // test {EVAL - Lua true boolean -> Redis protocol type conversion} - client.eval("return true", 0, require_number(1, name)); - // test {EVAL - Lua false boolean -> Redis protocol type conversion} - client.eval("return false", 0, require_null(name)); - // test {EVAL - Lua status code reply -> Redis protocol type conversion} - client.eval("return {ok='fine'}", 0, require_string("fine", name)); - // test {EVAL - Lua error reply -> Redis protocol type conversion} - client.eval("return {err='this is an error'}", 0, require_error(name)); - // test {EVAL - Lua table -> Redis protocol type conversion} - client.eval("return {1,2,3,'ciao',{1,2}}", 0, function (err, res) { - assert.strictEqual(5, res.length, name); - assert.strictEqual(1, res[0], name); - assert.strictEqual(2, res[1], name); - assert.strictEqual(3, res[2], name); - assert.strictEqual("ciao", res[3], name); - assert.strictEqual(2, res[4].length, name); - assert.strictEqual(1, res[4][0], name); - assert.strictEqual(2, res[4][1], name); - }); - // test {EVAL - Are the KEYS and ARGS arrays populated correctly?} - client.eval("return {KEYS[1],KEYS[2],ARGV[1],ARGV[2]}", 2, "a", "b", "c", "d", function (err, res) { - assert.strictEqual(4, res.length, name); - assert.strictEqual("a", res[0], name); - assert.strictEqual("b", res[1], name); - assert.strictEqual("c", res[2], name); - assert.strictEqual("d", res[3], name); - }); - - // test {EVAL - parameters in array format gives same result} - client.eval(["return {KEYS[1],KEYS[2],ARGV[1],ARGV[2]}", 2, "a", "b", "c", "d"], function (err, res) { - assert.strictEqual(4, res.length, name); - assert.strictEqual("a", res[0], name); - assert.strictEqual("b", res[1], name); - assert.strictEqual("c", res[2], name); - assert.strictEqual("d", res[3], name); - }); - - // prepare sha sum for evalsha cache test - var source = "return redis.call('get', 'sha test')", - sha = crypto.createHash('sha1').update(source).digest('hex'); - - client.set("sha test", "eval get sha test", function (err, res) { - if (err) throw err; - // test {EVAL - is Lua able to call Redis API?} - client.eval(source, 0, function (err, res) { - require_string("eval get sha test", name)(err, res); - // test {EVALSHA - Can we call a SHA1 if already defined?} - client.evalsha(sha, 0, require_string("eval get sha test", name)); - // test {EVALSHA - Do we get an error on non defined SHA1?} - client.evalsha("ffffffffffffffffffffffffffffffffffffffff", 0, require_error(name)); - }); - }); - - // test {EVAL - Redis integer -> Lua type conversion} - client.set("incr key", 0, function (err, reply) { - if (err) throw err; - client.eval("local foo = redis.call('incr','incr key')\n" + "return {type(foo),foo}", 0, function (err, res) { - if (err) throw err; - assert.strictEqual(2, res.length, name); - assert.strictEqual("number", res[0], name); - assert.strictEqual(1, res[1], name); - }); - }); - - client.set("bulk reply key", "bulk reply value", function (err, res) { - // test {EVAL - Redis bulk -> Lua type conversion} - client.eval("local foo = redis.call('get','bulk reply key'); return {type(foo),foo}", 0, function (err, res) { - if (err) throw err; - assert.strictEqual(2, res.length, name); - assert.strictEqual("string", res[0], name); - assert.strictEqual("bulk reply value", res[1], name); - }); - }); - - // test {EVAL - Redis multi bulk -> Lua type conversion} - client.multi() - .del("mylist") - .rpush("mylist", "a") - .rpush("mylist", "b") - .rpush("mylist", "c") - .exec(function (err, replies) { - if (err) throw err; - client.eval("local foo = redis.call('lrange','mylist',0,-1); return {type(foo),foo[1],foo[2],foo[3],# foo}", 0, function (err, res) { - assert.strictEqual(5, res.length, name); - assert.strictEqual("table", res[0], name); - assert.strictEqual("a", res[1], name); - assert.strictEqual("b", res[2], name); - assert.strictEqual("c", res[3], name); - assert.strictEqual(3, res[4], name); - }); - }); - // test {EVAL - Redis status reply -> Lua type conversion} - client.eval("local foo = redis.call('set','mykey','myval'); return {type(foo),foo['ok']}", 0, function (err, res) { - if (err) throw err; - assert.strictEqual(2, res.length, name); - assert.strictEqual("table", res[0], name); - assert.strictEqual("OK", res[1], name); - }); - // test {EVAL - Redis error reply -> Lua type conversion} - client.set("error reply key", "error reply value", function (err, res) { - if (err) throw err; - client.eval("local foo = redis.pcall('incr','error reply key'); return {type(foo),foo['err']}", 0, function (err, res) { - if (err) throw err; - assert.strictEqual(2, res.length, name); - assert.strictEqual("table", res[0], name); - assert.strictEqual("ERR value is not an integer or out of range", res[1], name); - }); - }); - // test {EVAL - Redis nil bulk reply -> Lua type conversion} - client.del("nil reply key", function (err, res) { - if (err) throw err; - client.eval("local foo = redis.call('get','nil reply key'); return {type(foo),foo == false}", 0, function (err, res) { - if (err) throw err; - assert.strictEqual(2, res.length, name); - assert.strictEqual("boolean", res[0], name); - assert.strictEqual(1, res[1], name); - next(name); - }); - }); -}; - -tests.SCRIPT_LOAD = function() { - var name = "SCRIPT_LOAD", - command = "return 1", - commandSha = crypto.createHash('sha1').update(command).digest('hex'); - - if (!server_version_at_least(client, [2, 6, 0])) { - console.log("Skipping " + name + " for old Redis server version < 2.6.x"); - return next(name); - } - - bclient.script("load", command, function(err, result) { - assert.strictEqual(result.toString(), commandSha); - client.multi().script("load", command).exec(function(err, result) { - assert.strictEqual(result[0].toString(), commandSha); - client.multi([['script', 'load', command]]).exec(function(err, result) { - assert.strictEqual(result[0].toString(), commandSha); - next(name); - }); - }); - }); -}; - -tests.CLIENT_LIST = function() { - var name = "CLIENT_LIST"; - - if (!server_version_at_least(client, [2, 4, 0])) { - console.log("Skipping " + name + " for old Redis server version < 2.4.x"); - return next(name); - } - - var pattern = /^addr=/; - if ( server_version_at_least(client, [2, 8, 12])) { - pattern = /^id=\d+ addr=/; - } - - function checkResult(result) { - var lines = result.toString().split('\n').slice(0, -1); - assert.strictEqual(lines.length, 4); - assert(lines.every(function(line) { - return line.match(pattern); - })); - } - - bclient.client("list", function(err, result) { - console.log(result.toString()); - checkResult(result); - client.multi().client("list").exec(function(err, result) { - console.log(result.toString()); - checkResult(result); - client.multi([['client', 'list']]).exec(function(err, result) { - console.log(result.toString()); - checkResult(result); - next(name); - }); - }); - }); -}; - -tests.WATCH_MULTI = function () { - var name = 'WATCH_MULTI', multi; - if (!server_version_at_least(client, [2, 2, 0])) { - console.log("Skipping " + name + " for old Redis server version < 2.2.x"); - return next(name); - } - - client.watch(name); - client.incr(name); - multi = client.multi(); - multi.incr(name); - multi.exec(last(name, require_null(name))); -}; - -tests.WATCH_TRANSACTION = function () { - var name = "WATCH_TRANSACTION"; - - if (!server_version_at_least(client, [2, 1, 0])) { - console.log("Skipping " + name + " because server version isn't new enough."); - return next(name); - } - - // Test WATCH command aborting transactions, look for parser offset errors. - - client.set("unwatched", 200); - - client.set(name, 0); - client.watch(name); - client.incr(name); - var multi = client.multi() - .incr(name) - .exec(function (err, replies) { - // Failure expected because of pre-multi incr - assert.strictEqual(replies, null, "Aborted transaction multi-bulk reply should be null."); - - client.get("unwatched", function (err, reply) { - assert.equal(err, null, name); - assert.equal(reply, 200, "Expected 200, got " + reply); - next(name); - }); - }); - - client.set("unrelated", 100, function (err, reply) { - assert.equal(err, null, name); - assert.equal(reply, "OK", "Expected 'OK', got " + reply); - }); -}; - - -tests.detect_buffers = function () { - var name = "detect_buffers", detect_client = redis.createClient(PORT, HOST, { detect_buffers: true, parser: parser }); - - detect_client.on("ready", function () { - // single Buffer or String - detect_client.set("string key 1", "string value"); - detect_client.get("string key 1", require_string("string value", name)); - detect_client.get(new Buffer("string key 1"), function (err, reply) { - assert.strictEqual(null, err, name); - assert.strictEqual(true, Buffer.isBuffer(reply), name); - assert.strictEqual("", reply.inspect(), name); - }); - - detect_client.hmset("hash key 2", "key 1", "val 1", "key 2", "val 2"); - // array of Buffers or Strings - detect_client.hmget("hash key 2", "key 1", "key 2", function (err, reply) { - assert.strictEqual(null, err, name); - assert.strictEqual(true, Array.isArray(reply), name); - assert.strictEqual(2, reply.length, name); - assert.strictEqual("val 1", reply[0], name); - assert.strictEqual("val 2", reply[1], name); - }); - detect_client.hmget(new Buffer("hash key 2"), "key 1", "key 2", function (err, reply) { - assert.strictEqual(null, err, name); - assert.strictEqual(true, Array.isArray(reply)); - assert.strictEqual(2, reply.length, name); - assert.strictEqual(true, Buffer.isBuffer(reply[0])); - assert.strictEqual(true, Buffer.isBuffer(reply[1])); - assert.strictEqual("", reply[0].inspect(), name); - assert.strictEqual("", reply[1].inspect(), name); - }); - - // array of strings with undefined values (repro #344) - detect_client.hmget("hash key 2", "key 3", "key 4", function(err, reply) { - assert.strictEqual(null, err, name); - assert.strictEqual(true, Array.isArray(reply), name); - assert.strictEqual(2, reply.length, name); - assert.equal(null, reply[0], name); - assert.equal(null, reply[1], name); - }); - - // Object of Buffers or Strings - detect_client.hgetall("hash key 2", function (err, reply) { - assert.strictEqual(null, err, name); - assert.strictEqual("object", typeof reply, name); - assert.strictEqual(2, Object.keys(reply).length, name); - assert.strictEqual("val 1", reply["key 1"], name); - assert.strictEqual("val 2", reply["key 2"], name); - }); - detect_client.hgetall(new Buffer("hash key 2"), function (err, reply) { - assert.strictEqual(null, err, name); - assert.strictEqual("object", typeof reply, name); - assert.strictEqual(2, Object.keys(reply).length, name); - assert.strictEqual(true, Buffer.isBuffer(reply["key 1"])); - assert.strictEqual(true, Buffer.isBuffer(reply["key 2"])); - assert.strictEqual("", reply["key 1"].inspect(), name); - assert.strictEqual("", reply["key 2"].inspect(), name); - }); - - detect_client.quit(function (err, res) { - next(name); - }); - }); -}; - -tests.detect_buffers_multi = function () { - var name = "detect_buffers_multi", detect_client = redis.createClient(PORT, HOST, {detect_buffers: true}); - - detect_client.on("ready", function () { - // single Buffer or String - detect_client.set("string key 1", "string value"); - detect_client.multi().get("string key 1").exec(require_string("string value", name)); - detect_client.multi().get(new Buffer("string key 1")).exec(function (err, reply) { - assert.strictEqual(null, err, name); - assert.strictEqual(1, reply.length, name); - assert.strictEqual(true, Buffer.isBuffer(reply[0]), name); - assert.strictEqual("", reply[0].inspect(), name); - }); - - detect_client.hmset("hash key 2", "key 1", "val 1", "key 2", "val 2"); - // array of Buffers or Strings - detect_client.multi().hmget("hash key 2", "key 1", "key 2").exec(function (err, reply) { - assert.strictEqual(null, err, name); - assert.strictEqual(true, Array.isArray(reply), name); - assert.strictEqual(1, reply.length, name); - assert.strictEqual(2, reply[0].length, name); - assert.strictEqual("val 1", reply[0][0], name); - assert.strictEqual("val 2", reply[0][1], name); - }); - detect_client.multi().hmget(new Buffer("hash key 2"), "key 1", "key 2").exec(function (err, reply) { - assert.strictEqual(null, err, name); - assert.strictEqual(true, Array.isArray(reply)); - assert.strictEqual(1, reply.length, name); - assert.strictEqual(2, reply[0].length, name); - assert.strictEqual(true, Buffer.isBuffer(reply[0][0])); - assert.strictEqual(true, Buffer.isBuffer(reply[0][1])); - assert.strictEqual("", reply[0][0].inspect(), name); - assert.strictEqual("", reply[0][1].inspect(), name); - }); - - // array of strings with undefined values (repro #344) - detect_client.multi().hmget("hash key 2", "key 3", "key 4").exec(function(err, reply) { - assert.strictEqual(null, err, name); - assert.strictEqual(true, Array.isArray(reply), name); - assert.strictEqual(1, reply.length, name); - assert.strictEqual(2, reply[0].length, name); - assert.equal(null, reply[0][0], name); - assert.equal(null, reply[0][1], name); - }); - - // Object of Buffers or Strings - detect_client.multi().hgetall("hash key 2").exec(function (err, reply) { - assert.strictEqual(null, err, name); - assert.strictEqual(1, reply.length, name); - assert.strictEqual("object", typeof reply[0], name); - assert.strictEqual(2, Object.keys(reply[0]).length, name); - assert.strictEqual("val 1", reply[0]["key 1"], name); - assert.strictEqual("val 2", reply[0]["key 2"], name); - }); - detect_client.multi().hgetall(new Buffer("hash key 2")).exec(function (err, reply) { - assert.strictEqual(null, err, name); - assert.strictEqual(1, reply.length, name); - assert.strictEqual("object", typeof reply, name); - assert.strictEqual(2, Object.keys(reply[0]).length, name); - assert.strictEqual(true, Buffer.isBuffer(reply[0]["key 1"])); - assert.strictEqual(true, Buffer.isBuffer(reply[0]["key 2"])); - assert.strictEqual("", reply[0]["key 1"].inspect(), name); - assert.strictEqual("", reply[0]["key 2"].inspect(), name); - }); - - // Can interleave string and buffer results: - detect_client.multi() - .hget("hash key 2", "key 1") - .hget(new Buffer("hash key 2"), "key 1") - .hget("hash key 2", new Buffer("key 2")) - .hget("hash key 2", "key 2") - .exec(function (err, reply) { - assert.strictEqual(null, err, name); - assert.strictEqual(true, Array.isArray(reply)); - assert.strictEqual(4, reply.length, name); - assert.strictEqual("val 1", reply[0], name); - assert.strictEqual(true, Buffer.isBuffer(reply[1])); - assert.strictEqual("", reply[1].inspect(), name); - assert.strictEqual(true, Buffer.isBuffer(reply[2])); - assert.strictEqual("", reply[2].inspect(), name); - assert.strictEqual("val 2", reply[3], name); - }); - - detect_client.quit(function (err, res) { - next(name); - }); - }); -}; - -tests.socket_nodelay = function () { - var name = "socket_nodelay", c1, c2, c3, ready_count = 0, quit_count = 0; - - c1 = redis.createClient(PORT, HOST, { socket_nodelay: true, parser: parser }); - c2 = redis.createClient(PORT, HOST, { socket_nodelay: false, parser: parser }); - c3 = redis.createClient(PORT, HOST, { parser: parser }); - - function quit_check() { - quit_count++; - - if (quit_count === 3) { - next(name); - } - } - - function run() { - assert.strictEqual(true, c1.options.socket_nodelay, name); - assert.strictEqual(false, c2.options.socket_nodelay, name); - assert.strictEqual(true, c3.options.socket_nodelay, name); - - c1.set(["set key 1", "set val"], require_string("OK", name)); - c1.set(["set key 2", "set val"], require_string("OK", name)); - c1.get(["set key 1"], require_string("set val", name)); - c1.get(["set key 2"], require_string("set val", name)); - - c2.set(["set key 3", "set val"], require_string("OK", name)); - c2.set(["set key 4", "set val"], require_string("OK", name)); - c2.get(["set key 3"], require_string("set val", name)); - c2.get(["set key 4"], require_string("set val", name)); - - c3.set(["set key 5", "set val"], require_string("OK", name)); - c3.set(["set key 6", "set val"], require_string("OK", name)); - c3.get(["set key 5"], require_string("set val", name)); - c3.get(["set key 6"], require_string("set val", name)); - - c1.quit(quit_check); - c2.quit(quit_check); - c3.quit(quit_check); - } - - function ready_check() { - ready_count++; - if (ready_count === 3) { - run(); - } - } - - c1.on("ready", ready_check); - c2.on("ready", ready_check); - c3.on("ready", ready_check); -}; - - -tests.idle = function () { - var name = "idle"; - - client.on("idle", function on_idle() { - client.removeListener("idle", on_idle); - next(name); - }); - - client.set("idle", "test"); -}; - -tests.HSET = function () { - var key = "test hash", - field1 = new Buffer("0123456789"), - value1 = new Buffer("abcdefghij"), - field2 = new Buffer(0), - value2 = new Buffer(0), - name = "HSET"; - - client.HSET(key, field1, value1, require_number(1, name)); - client.HGET(key, field1, require_string(value1.toString(), name)); - - // Empty value - client.HSET(key, field1, value2, require_number(0, name)); - client.HGET([key, field1], require_string("", name)); - - // Empty key, empty value - client.HSET([key, field2, value1], require_number(1, name)); - client.HSET(key, field2, value2, last(name, require_number(0, name))); -}; - -tests.HLEN = function () { - var key = "test hash", - field1 = new Buffer("0123456789"), - value1 = new Buffer("abcdefghij"), - field2 = new Buffer(0), - value2 = new Buffer(0), - name = "HSET", - timeout = 1000; - - client.HSET(key, field1, value1, function (err, results) { - client.HLEN(key, function (err, len) { - assert.ok(2 === +len); - next(name); - }); - }); -}; - -tests.HMSET_BUFFER_AND_ARRAY = function () { - // Saving a buffer and an array to the same key should not error - var key = "test hash", - field1 = "buffer", - value1 = new Buffer("abcdefghij"), - field2 = "array", - value2 = ["array contents"], - name = "HSET"; - - client.HMSET(key, field1, value1, field2, value2, last(name, require_string("OK", name))); -}; - -// TODO - add test for HMSET with optional callbacks - -tests.HMGET = function () { - var key1 = "test hash 1", key2 = "test hash 2", key3 = 123456789, name = "HMGET"; - - // redis-like hmset syntax - client.HMSET(key1, "0123456789", "abcdefghij", "some manner of key", "a type of value", require_string("OK", name)); - - // fancy hmset syntax - client.HMSET(key2, { - "0123456789": "abcdefghij", - "some manner of key": "a type of value" - }, require_string("OK", name)); - - // test for numeric key - client.HMSET(key3, { - "0123456789": "abcdefghij", - "some manner of key": "a type of value" - }, require_string("OK", name)); - - client.HMGET(key1, "0123456789", "some manner of key", function (err, reply) { - assert.strictEqual("abcdefghij", reply[0].toString(), name); - assert.strictEqual("a type of value", reply[1].toString(), name); - }); - - client.HMGET(key2, "0123456789", "some manner of key", function (err, reply) { - assert.strictEqual("abcdefghij", reply[0].toString(), name); - assert.strictEqual("a type of value", reply[1].toString(), name); - }); - - client.HMGET(key3, "0123456789", "some manner of key", function (err, reply) { - assert.strictEqual("abcdefghij", reply[0].toString(), name); - assert.strictEqual("a type of value", reply[1].toString(), name); - }); - - client.HMGET(key1, ["0123456789"], function (err, reply) { - assert.strictEqual("abcdefghij", reply[0], name); - }); - - client.HMGET(key1, ["0123456789", "some manner of key"], function (err, reply) { - assert.strictEqual("abcdefghij", reply[0], name); - assert.strictEqual("a type of value", reply[1], name); - }); - - client.HMGET(key1, "missing thing", "another missing thing", function (err, reply) { - assert.strictEqual(null, reply[0], name); - assert.strictEqual(null, reply[1], name); - next(name); - }); -}; - -tests.HINCRBY = function () { - var name = "HINCRBY"; - client.hset("hash incr", "value", 10, require_number(1, name)); - client.HINCRBY("hash incr", "value", 1, require_number(11, name)); - client.HINCRBY("hash incr", "value 2", 1, last(name, require_number(1, name))); -}; - -tests.SUBSCRIBE = function () { - var client1 = client, msg_count = 0, name = "SUBSCRIBE"; - - client1.on("subscribe", function (channel, count) { - if (channel === "chan1") { - client2.publish("chan1", "message 1", require_number(1, name)); - client2.publish("chan2", "message 2", require_number(1, name)); - client2.publish("chan1", "message 3", require_number(1, name)); - } - }); - - client1.on("unsubscribe", function (channel, count) { - if (count === 0) { - // make sure this connection can go into and out of pub/sub mode - client1.incr("did a thing", last(name, require_number(2, name))); - } - }); - - client1.on("message", function (channel, message) { - msg_count += 1; - assert.strictEqual("message " + msg_count, message.toString()); - if (msg_count === 3) { - client1.unsubscribe("chan1", "chan2"); - } - }); - - client1.set("did a thing", 1, require_string("OK", name)); - client1.subscribe("chan1", "chan2", function (err, results) { - assert.strictEqual(null, err, "result sent back unexpected error: " + err); - assert.strictEqual("chan1", results.toString(), name); - }); -}; - -tests.UNSUB_EMPTY = function () { - // test situation where unsubscribe reply[1] is null - var name = "UNSUB_EMPTY"; - client3.unsubscribe(); // unsubscribe from all so can test null - client3.unsubscribe(); // reply[1] will be null - next(name); -}; - -tests.PUNSUB_EMPTY = function () { - // test situation where punsubscribe reply[1] is null - var name = "PUNSUB_EMPTY"; - client3.punsubscribe(); // punsubscribe from all so can test null - client3.punsubscribe(); // reply[1] will be null - next(name); -}; - -tests.UNSUB_EMPTY_CB = function () { - var name = "UNSUB_EMPTY_CB"; - // test hangs on older versions of redis, so skip - if (!server_version_at_least(client, [2, 6, 11])) return next(name); - - // test situation where unsubscribe reply[1] is null - client3.unsubscribe(); // unsubscribe from all so can test null - client3.unsubscribe(function (err, results) { - // reply[1] will be null - assert.strictEqual(null, err, "unexpected error: " + err); - next(name); - }); -}; - -tests.PUNSUB_EMPTY_CB = function () { - var name = "PUNSUB_EMPTY_CB"; - // test hangs on older versions of redis, so skip - if (!server_version_at_least(client, [2, 6, 11])) return next(name); - - // test situation where punsubscribe reply[1] is null - client3.punsubscribe(); // punsubscribe from all so can test null - client3.punsubscribe(function (err, results) { - // reply[1] will be null - assert.strictEqual(null, err, "unexpected error: " + err); - next(name); - }); -}; - -tests.SUB_UNSUB_SUB = function () { - var name = "SUB_UNSUB_SUB"; - // test hangs on older versions of redis, so skip - if (!server_version_at_least(client, [2, 6, 11])) return next(name); - - client3.subscribe('chan3'); - client3.unsubscribe('chan3'); - client3.subscribe('chan3', function (err, results) { - assert.strictEqual(null, err, "unexpected error: " + err); - client2.publish('chan3', 'foo'); - }); - client3.on('message', function (channel, message) { - assert.strictEqual(channel, 'chan3'); - assert.strictEqual(message, 'foo'); - client3.removeAllListeners(); - next(name); - }); -}; - -tests.SUB_UNSUB_MSG_SUB = function () { - var name = "SUB_UNSUB_MSG_SUB"; - // test hangs on older versions of redis, so skip - if (!server_version_at_least(client, [2, 6, 11])) return next(name); - - client3.subscribe('chan8'); - client3.subscribe('chan9'); - client3.unsubscribe('chan9'); - client2.publish('chan8', 'something'); - client3.subscribe('chan9', with_timeout(name, function (err, results) { - next(name); - }, 2000)); -}; - -tests.PSUB_UNSUB_PMSG_SUB = function () { - var name = "PSUB_UNSUB_PMSG_SUB"; - // test hangs on older versions of redis, so skip - if (!server_version_at_least(client, [2, 6, 11])) return next(name); - - client3.psubscribe('abc*'); - client3.subscribe('xyz'); - client3.unsubscribe('xyz'); - client2.publish('abcd', 'something'); - client3.subscribe('xyz', with_timeout(name, function (err, results) { - next(name); - }, 2000)); -}; - -tests.SUBSCRIBE_QUIT = function () { - var name = "SUBSCRIBE_QUIT"; - client3.on("end", function () { - next(name); - }); - client3.on("subscribe", function (channel, count) { - client3.quit(); - }); - client3.subscribe("chan3"); -}; - -tests.SUBSCRIBE_CLOSE_RESUBSCRIBE = function () { - var name = "SUBSCRIBE_CLOSE_RESUBSCRIBE"; - var c1 = redis.createClient(PORT, HOST, { parser: parser }); - var c2 = redis.createClient(PORT, HOST, { parser: parser }); - var count = 0; - - /* Create two clients. c1 subscribes to two channels, c2 will publish to them. - c2 publishes the first message. - c1 gets the message and drops its connection. It must resubscribe itself. - When it resubscribes, c2 publishes the second message, on the same channel - c1 gets the message and drops its connection. It must resubscribe itself, again. - When it resubscribes, c2 publishes the third message, on the second channel - c1 gets the message and drops its connection. When it reconnects, the test ends. - */ - - c1.on("message", function(channel, message) { - if (channel === "chan1") { - assert.strictEqual(message, "hi on channel 1"); - c1.stream.end(); - - } else if (channel === "chan2") { - assert.strictEqual(message, "hi on channel 2"); - c1.stream.end(); - - } else { - c1.quit(); - c2.quit(); - assert.fail("test failed"); - } - }); - - c1.subscribe("chan1", "chan2"); - - c2.once("ready", function() { - console.log("c2 is ready"); - c1.on("ready", function(err, results) { - console.log("c1 is ready", count); - - count++; - if (count === 1) { - c2.publish("chan1", "hi on channel 1"); - return; - - } else if (count === 2) { - c2.publish("chan2", "hi on channel 2"); - - } else { - c1.quit(function() { - c2.quit(function() { - next(name); - }); - }); - } - }); - - c2.publish("chan1", "hi on channel 1"); - - }); -}; - -tests.EXISTS = function () { - var name = "EXISTS"; - client.del("foo", "foo2", require_number_any(name)); - client.set("foo", "bar", require_string("OK", name)); - client.EXISTS("foo", require_number(1, name)); - client.EXISTS("foo2", last(name, require_number(0, name))); -}; - -tests.DEL = function () { - var name = "DEL"; - client.DEL("delkey", require_number_any(name)); - client.set("delkey", "delvalue", require_string("OK", name)); - client.DEL("delkey", require_number(1, name)); - client.exists("delkey", require_number(0, name)); - client.DEL("delkey", require_number(0, name)); - client.mset("delkey", "delvalue", "delkey2", "delvalue2", require_string("OK", name)); - client.DEL("delkey", "delkey2", last(name, require_number(2, name))); -}; - -tests.TYPE = function () { - var name = "TYPE"; - client.set(["string key", "should be a string"], require_string("OK", name)); - client.rpush(["list key", "should be a list"], require_number_pos(name)); - client.sadd(["set key", "should be a set"], require_number_any(name)); - client.zadd(["zset key", "10.0", "should be a zset"], require_number_any(name)); - client.hset(["hash key", "hashtest", "should be a hash"], require_number_any(0, name)); - - client.TYPE(["string key"], require_string("string", name)); - client.TYPE(["list key"], require_string("list", name)); - client.TYPE(["set key"], require_string("set", name)); - client.TYPE(["zset key"], require_string("zset", name)); - client.TYPE("not here yet", require_string("none", name)); - client.TYPE(["hash key"], last(name, require_string("hash", name))); -}; - -tests.KEYS = function () { - var name = "KEYS"; - client.mset(["test keys 1", "test val 1", "test keys 2", "test val 2"], require_string("OK", name)); - client.KEYS(["test keys*"], function (err, results) { - assert.strictEqual(null, err, "result sent back unexpected error: " + err); - assert.strictEqual(2, results.length, name); - assert.ok(~results.indexOf("test keys 1")); - assert.ok(~results.indexOf("test keys 2")); - next(name); - }); -}; - -tests.MULTIBULK = function() { - var name = "MULTIBULK", - keys_values = []; - - for (var i = 0; i < 200; i++) { - var key_value = [ - "multibulk:" + crypto.randomBytes(256).toString("hex"), // use long strings as keys to ensure generation of large packet - "test val " + i - ]; - keys_values.push(key_value); - } - - client.mset(keys_values.reduce(function(a, b) { - return a.concat(b); - }), require_string("OK", name)); - - client.KEYS("multibulk:*", function(err, results) { - assert.strictEqual(null, err, "result sent back unexpected error: " + err); - assert.deepEqual(keys_values.map(function(val) { - return val[0]; - }).sort(), results.sort(), name); - }); - - next(name); -}; - -tests.MULTIBULK_ZERO_LENGTH = function () { - var name = "MULTIBULK_ZERO_LENGTH"; - client.KEYS(['users:*'], function (err, results) { - assert.strictEqual(null, err, 'error on empty multibulk reply'); - assert.strictEqual(true, is_empty_array(results), "not an empty array"); - next(name); - }); -}; - -tests.RANDOMKEY = function () { - var name = "RANDOMKEY"; - client.mset(["test keys 1", "test val 1", "test keys 2", "test val 2"], require_string("OK", name)); - client.RANDOMKEY([], function (err, results) { - assert.strictEqual(null, err, name + " result sent back unexpected error: " + err); - assert.strictEqual(true, /\w+/.test(results), name); - next(name); - }); -}; - -tests.RENAME = function () { - var name = "RENAME"; - client.set(['foo', 'bar'], require_string("OK", name)); - client.RENAME(["foo", "new foo"], require_string("OK", name)); - client.exists(["foo"], require_number(0, name)); - client.exists(["new foo"], last(name, require_number(1, name))); -}; - -tests.RENAMENX = function () { - var name = "RENAMENX"; - client.set(['foo', 'bar'], require_string("OK", name)); - client.set(['foo2', 'bar2'], require_string("OK", name)); - client.RENAMENX(["foo", "foo2"], require_number(0, name)); - client.exists(["foo"], require_number(1, name)); - client.exists(["foo2"], require_number(1, name)); - client.del(["foo2"], require_number(1, name)); - client.RENAMENX(["foo", "foo2"], require_number(1, name)); - client.exists(["foo"], require_number(0, name)); - client.exists(["foo2"], last(name, require_number(1, name))); -}; - - -tests.MGET = function () { - var name = "MGET"; - client.mset(["mget keys 1", "mget val 1", "mget keys 2", "mget val 2", "mget keys 3", "mget val 3"], require_string("OK", name)); - client.MGET("mget keys 1", "mget keys 2", "mget keys 3", function (err, results) { - assert.strictEqual(null, err, "result sent back unexpected error: " + err); - assert.strictEqual(3, results.length, name); - assert.strictEqual("mget val 1", results[0].toString(), name); - assert.strictEqual("mget val 2", results[1].toString(), name); - assert.strictEqual("mget val 3", results[2].toString(), name); - }); - client.MGET(["mget keys 1", "mget keys 2", "mget keys 3"], function (err, results) { - assert.strictEqual(null, err, "result sent back unexpected error: " + err); - assert.strictEqual(3, results.length, name); - assert.strictEqual("mget val 1", results[0].toString(), name); - assert.strictEqual("mget val 2", results[1].toString(), name); - assert.strictEqual("mget val 3", results[2].toString(), name); - }); - client.MGET(["mget keys 1", "some random shit", "mget keys 2", "mget keys 3"], function (err, results) { - assert.strictEqual(null, err, "result sent back unexpected error: " + err); - assert.strictEqual(4, results.length, name); - assert.strictEqual("mget val 1", results[0].toString(), name); - assert.strictEqual(null, results[1], name); - assert.strictEqual("mget val 2", results[2].toString(), name); - assert.strictEqual("mget val 3", results[3].toString(), name); - next(name); - }); -}; - -tests.SETNX = function () { - var name = "SETNX"; - client.set(["setnx key", "setnx value"], require_string("OK", name)); - client.SETNX(["setnx key", "new setnx value"], require_number(0, name)); - client.del(["setnx key"], require_number(1, name)); - client.exists(["setnx key"], require_number(0, name)); - client.SETNX(["setnx key", "new setnx value"], require_number(1, name)); - client.exists(["setnx key"], last(name, require_number(1, name))); -}; - -tests.SETEX = function () { - var name = "SETEX"; - client.SETEX(["setex key", "100", "setex val"], require_string("OK", name)); - client.exists(["setex key"], require_number(1, name)); - client.ttl(["setex key"], last(name, require_number_pos(name))); - client.SETEX(["setex key", "100", undefined], require_error(name)); -}; - -tests.MSETNX = function () { - var name = "MSETNX"; - client.mset(["mset1", "val1", "mset2", "val2", "mset3", "val3"], require_string("OK", name)); - client.MSETNX(["mset3", "val3", "mset4", "val4"], require_number(0, name)); - client.del(["mset3"], require_number(1, name)); - client.MSETNX(["mset3", "val3", "mset4", "val4"], require_number(1, name)); - client.exists(["mset3"], require_number(1, name)); - client.exists(["mset4"], last(name, require_number(1, name))); -}; - -tests.HGETALL = function () { - var name = "HGETALL"; - client.hmset(["hosts", "mjr", "1", "another", "23", "home", "1234"], require_string("OK", name)); - client.HGETALL(["hosts"], function (err, obj) { - assert.strictEqual(null, err, name + " result sent back unexpected error: " + err); - assert.strictEqual(3, Object.keys(obj).length, name); - assert.strictEqual("1", obj.mjr.toString(), name); - assert.strictEqual("23", obj.another.toString(), name); - assert.strictEqual("1234", obj.home.toString(), name); - next(name); - }); -}; - -tests.HGETALL_2 = function () { - var name = "HGETALL (Binary client)"; - bclient.hmset(["bhosts", "mjr", "1", "another", "23", "home", "1234", new Buffer([0xAA, 0xBB, 0x00, 0xF0]), new Buffer([0xCC, 0xDD, 0x00, 0xF0])], require_string("OK", name)); - bclient.HGETALL(["bhosts"], function (err, obj) { - assert.strictEqual(null, err, name + " result sent back unexpected error: " + err); - assert.strictEqual(4, Object.keys(obj).length, name); - assert.strictEqual("1", obj.mjr.toString(), name); - assert.strictEqual("23", obj.another.toString(), name); - assert.strictEqual("1234", obj.home.toString(), name); - assert.strictEqual((new Buffer([0xAA, 0xBB, 0x00, 0xF0])).toString('binary'), Object.keys(obj)[3], name); - assert.strictEqual((new Buffer([0xCC, 0xDD, 0x00, 0xF0])).toString('binary'), obj[(new Buffer([0xAA, 0xBB, 0x00, 0xF0])).toString('binary')].toString('binary'), name); - next(name); - }); -}; - -tests.HGETALL_MESSAGE = function () { - var name = "HGETALL_MESSAGE"; - client.hmset("msg_test", {message: "hello"}, require_string("OK", name)); - client.hgetall("msg_test", function (err, obj) { - assert.strictEqual(null, err, name + " result sent back unexpected error: " + err); - assert.strictEqual(1, Object.keys(obj).length, name); - assert.strictEqual(obj.message, "hello"); - next(name); - }); -}; - -tests.HGETALL_NULL = function () { - var name = "HGETALL_NULL"; - - client.hgetall("missing", function (err, obj) { - assert.strictEqual(null, err); - assert.strictEqual(null, obj); - next(name); - }); -}; - -tests.UTF8 = function () { - var name = "UTF8", - utf8_sample = "ಠ_ಠ"; - - client.set(["utf8test", utf8_sample], require_string("OK", name)); - client.get(["utf8test"], function (err, obj) { - assert.strictEqual(null, err); - assert.strictEqual(utf8_sample, obj); - next(name); - }); -}; - -// Set tests were adapted from Brian Hammond's redis-node-client.js, which has a comprehensive test suite - -tests.SADD = function () { - var name = "SADD"; - - client.del('set0'); - client.SADD('set0', 'member0', require_number(1, name)); - client.sadd('set0', 'member0', last(name, require_number(0, name))); -}; - -tests.SADD2 = function () { - var name = "SADD2"; - - client.del("set0"); - client.sadd("set0", ["member0", "member1", "member2"], require_number(3, name)); - client.smembers("set0", function (err, res) { - assert.strictEqual(res.length, 3); - assert.ok(~res.indexOf("member0")); - assert.ok(~res.indexOf("member1")); - assert.ok(~res.indexOf("member2")); - }); - client.SADD("set1", ["member0", "member1", "member2"], require_number(3, name)); - client.smembers("set1", function (err, res) { - assert.strictEqual(res.length, 3); - assert.ok(~res.indexOf("member0")); - assert.ok(~res.indexOf("member1")); - assert.ok(~res.indexOf("member2")); - next(name); - }); -}; - -tests.SISMEMBER = function () { - var name = "SISMEMBER"; - - client.del('set0'); - client.sadd('set0', 'member0', require_number(1, name)); - client.sismember('set0', 'member0', require_number(1, name)); - client.sismember('set0', 'member1', last(name, require_number(0, name))); -}; - -tests.SCARD = function () { - var name = "SCARD"; - - client.del('set0'); - client.sadd('set0', 'member0', require_number(1, name)); - client.scard('set0', require_number(1, name)); - client.sadd('set0', 'member1', require_number(1, name)); - client.scard('set0', last(name, require_number(2, name))); -}; - -tests.SREM = function () { - var name = "SREM"; - - client.del('set0'); - client.sadd('set0', 'member0', require_number(1, name)); - client.srem('set0', 'foobar', require_number(0, name)); - client.srem('set0', 'member0', require_number(1, name)); - client.scard('set0', last(name, require_number(0, name))); -}; - - -tests.SREM2 = function () { - var name = "SREM2"; - client.del("set0"); - client.sadd("set0", ["member0", "member1", "member2"], require_number(3, name)); - client.SREM("set0", ["member1", "member2"], require_number(2, name)); - client.smembers("set0", function (err, res) { - assert.strictEqual(res.length, 1); - assert.ok(~res.indexOf("member0")); - }); - client.sadd("set0", ["member3", "member4", "member5"], require_number(3, name)); - client.srem("set0", ["member0", "member6"], require_number(1, name)); - client.smembers("set0", function (err, res) { - assert.strictEqual(res.length, 3); - assert.ok(~res.indexOf("member3")); - assert.ok(~res.indexOf("member4")); - assert.ok(~res.indexOf("member5")); - next(name); - }); -}; +return; tests.SPOP = function () { var name = "SPOP"; @@ -1633,61 +116,6 @@ tests.SMOVE = function () { client.smove('foo', 'bar', 'x', last(name, require_number(0, name))); }; -tests.SINTER = function () { - var name = "SINTER"; - - client.del('sa'); - client.del('sb'); - client.del('sc'); - - client.sadd('sa', 'a', require_number(1, name)); - client.sadd('sa', 'b', require_number(1, name)); - client.sadd('sa', 'c', require_number(1, name)); - - client.sadd('sb', 'b', require_number(1, name)); - client.sadd('sb', 'c', require_number(1, name)); - client.sadd('sb', 'd', require_number(1, name)); - - client.sadd('sc', 'c', require_number(1, name)); - client.sadd('sc', 'd', require_number(1, name)); - client.sadd('sc', 'e', require_number(1, name)); - - client.sinter('sa', 'sb', function (err, intersection) { - if (err) { - assert.fail(err, name); - } - assert.equal(intersection.length, 2, name); - assert.deepEqual(buffers_to_strings(intersection).sort(), [ 'b', 'c' ], name); - }); - - client.sinter('sb', 'sc', function (err, intersection) { - if (err) { - assert.fail(err, name); - } - assert.equal(intersection.length, 2, name); - assert.deepEqual(buffers_to_strings(intersection).sort(), [ 'c', 'd' ], name); - }); - - client.sinter('sa', 'sc', function (err, intersection) { - if (err) { - assert.fail(err, name); - } - assert.equal(intersection.length, 1, name); - assert.equal(intersection[0], 'c', name); - }); - - // 3-way - - client.sinter('sa', 'sb', 'sc', function (err, intersection) { - if (err) { - assert.fail(err, name); - } - assert.equal(intersection.length, 1, name); - assert.equal(intersection[0], 'c', name); - next(name); - }); -}; - tests.SINTERSTORE = function () { var name = "SINTERSTORE"; @@ -1784,126 +212,6 @@ tests.SUNIONSTORE = function () { }); }; -// SORT test adapted from Brian Hammond's redis-node-client.js, which has a comprehensive test suite - -tests.SORT = function () { - var name = "SORT"; - - client.del('y'); - client.del('x'); - - client.rpush('y', 'd', require_number(1, name)); - client.rpush('y', 'b', require_number(2, name)); - client.rpush('y', 'a', require_number(3, name)); - client.rpush('y', 'c', require_number(4, name)); - - client.rpush('x', '3', require_number(1, name)); - client.rpush('x', '9', require_number(2, name)); - client.rpush('x', '2', require_number(3, name)); - client.rpush('x', '4', require_number(4, name)); - - client.set('w3', '4', require_string("OK", name)); - client.set('w9', '5', require_string("OK", name)); - client.set('w2', '12', require_string("OK", name)); - client.set('w4', '6', require_string("OK", name)); - - client.set('o2', 'buz', require_string("OK", name)); - client.set('o3', 'foo', require_string("OK", name)); - client.set('o4', 'baz', require_string("OK", name)); - client.set('o9', 'bar', require_string("OK", name)); - - client.set('p2', 'qux', require_string("OK", name)); - client.set('p3', 'bux', require_string("OK", name)); - client.set('p4', 'lux', require_string("OK", name)); - client.set('p9', 'tux', require_string("OK", name)); - - // Now the data has been setup, we can test. - - // But first, test basic sorting. - - // y = [ d b a c ] - // sort y ascending = [ a b c d ] - // sort y descending = [ d c b a ] - - client.sort('y', 'asc', 'alpha', function (err, sorted) { - if (err) { - assert.fail(err, name); - } - assert.deepEqual(buffers_to_strings(sorted), ['a', 'b', 'c', 'd'], name); - }); - - client.sort('y', 'desc', 'alpha', function (err, sorted) { - if (err) { - assert.fail(err, name); - } - assert.deepEqual(buffers_to_strings(sorted), ['d', 'c', 'b', 'a'], name); - }); - - // Now try sorting numbers in a list. - // x = [ 3, 9, 2, 4 ] - - client.sort('x', 'asc', function (err, sorted) { - if (err) { - assert.fail(err, name); - } - assert.deepEqual(buffers_to_strings(sorted), [2, 3, 4, 9], name); - }); - - client.sort('x', 'desc', function (err, sorted) { - if (err) { - assert.fail(err, name); - } - assert.deepEqual(buffers_to_strings(sorted), [9, 4, 3, 2], name); - }); - - // Try sorting with a 'by' pattern. - - client.sort('x', 'by', 'w*', 'asc', function (err, sorted) { - if (err) { - assert.fail(err, name); - } - assert.deepEqual(buffers_to_strings(sorted), [3, 9, 4, 2], name); - }); - - // Try sorting with a 'by' pattern and 1 'get' pattern. - - client.sort('x', 'by', 'w*', 'asc', 'get', 'o*', function (err, sorted) { - if (err) { - assert.fail(err, name); - } - assert.deepEqual(buffers_to_strings(sorted), ['foo', 'bar', 'baz', 'buz'], name); - }); - - // Try sorting with a 'by' pattern and 2 'get' patterns. - - client.sort('x', 'by', 'w*', 'asc', 'get', 'o*', 'get', 'p*', function (err, sorted) { - if (err) { - assert.fail(err, name); - } - assert.deepEqual(buffers_to_strings(sorted), ['foo', 'bux', 'bar', 'tux', 'baz', 'lux', 'buz', 'qux'], name); - }); - - // Try sorting with a 'by' pattern and 2 'get' patterns. - // Instead of getting back the sorted set/list, store the values to a list. - // Then check that the values are there in the expected order. - - client.sort('x', 'by', 'w*', 'asc', 'get', 'o*', 'get', 'p*', 'store', 'bacon', function (err) { - if (err) { - assert.fail(err, name); - } - }); - - client.lrange('bacon', 0, -1, function (err, values) { - if (err) { - assert.fail(err, name); - } - assert.deepEqual(buffers_to_strings(values), ['foo', 'bux', 'bar', 'tux', 'baz', 'lux', 'buz', 'qux'], name); - next(name); - }); - - // TODO - sort by hash value -}; - tests.MONITOR = function () { var name = "MONITOR", responses = [], monitor_client; @@ -2010,53 +318,6 @@ tests.OPTIONAL_CALLBACK_UNDEFINED = function () { client.set("op_cb_undefined", undefined, undefined); }; -tests.ENABLE_OFFLINE_QUEUE_TRUE = function () { - var name = "ENABLE_OFFLINE_QUEUE_TRUE"; - var cli = redis.createClient(9999, null, { - max_attempts: 1, - parser: parser - // default :) - // enable_offline_queue: true - }); - cli.on('error', function(e) { - // ignore, b/c expecting a "can't connect" error - }); - return setTimeout(function() { - cli.set(name, name, function(err, result) { - assert.ifError(err); - }); - - return setTimeout(function(){ - assert.strictEqual(cli.offline_queue.length, 1); - return next(name); - }, 25); - }, 50); -}; - -tests.ENABLE_OFFLINE_QUEUE_FALSE = function () { - var name = "ENABLE_OFFLINE_QUEUE_FALSE"; - var cli = redis.createClient(9999, null, { - parser: parser, - max_attempts: 1, - enable_offline_queue: false - }); - cli.on('error', function() { - // ignore, see above - }); - assert.throws(function () { - cli.set(name, name); - }); - assert.doesNotThrow(function () { - cli.set(name, name, function (err) { - // should callback with an error - assert.ok(err); - setTimeout(function () { - next(name); - }, 50); - }); - }); -}; - tests.SLOWLOG = function () { var name = "SLOWLOG"; client.config("set", "slowlog-log-slower-than", 0, require_string("OK", name)); @@ -2072,198 +333,3 @@ tests.SLOWLOG = function () { next(name); }); }; - -tests.DOMAIN = function () { - var name = "DOMAIN"; - - var domain; - try { - domain = require('domain').create(); - } catch (err) { - console.log("Skipping " + name + " because this version of node doesn't have domains."); - next(name); - } - - if (domain) { - domain.run(function () { - client.set('domain', 'value', function (err, res) { - assert.ok(process.domain); - var notFound = res.not.existing.thing; // ohhh nooooo - }); - }); - - // this is the expected and desired behavior - domain.on('error', function (err) { - domain.exit(); - next(name); - }); - } -}; - -// TODO - need a better way to test auth, maybe auto-config a local Redis server or something. -// Yes, this is the real password. Please be nice, thanks. -tests.auth = function () { - var name = "AUTH", client4, ready_count = 0; - - client4 = redis.createClient(9006, "filefish.redistogo.com", { parser: parser }); - client4.auth("664b1b6aaf134e1ec281945a8de702a9", function (err, res) { - assert.strictEqual(null, err, name); - assert.strictEqual("OK", res.toString(), name); - }); - - // test auth, then kill the connection so it'll auto-reconnect and auto-re-auth - client4.on("ready", function () { - ready_count++; - if (ready_count === 1) { - client4.stream.destroy(); - } else { - client4.quit(function (err, res) { - next(name); - }); - } - }); -}; - -tests.auth2 = function () { - var name = "AUTH2", client4, ready_count = 0; - - client4 = redis.createClient(9006, "filefish.redistogo.com", { auth_pass: "664b1b6aaf134e1ec281945a8de702a9", parser: parser }); - - // test auth, then kill the connection so it'll auto-reconnect and auto-re-auth - client4.on("ready", function () { - ready_count++; - if (ready_count === 1) { - client4.stream.destroy(); - } else { - client4.quit(function (err, res) { - next(name); - }); - } - }); -}; - -// auth password specified by URL string. -tests.auth3 = function () { - var name = "AUTH3", client4, ready_count = 0; - - client4 = redis.createClient('redis://redistogo:664b1b6aaf134e1ec281945a8de702a9@filefish.redistogo.com:9006/'); - - // test auth, then kill the connection so it'll auto-reconnect and auto-re-auth - client4.on("ready", function () { - ready_count++; - if (ready_count === 1) { - client4.stream.destroy(); - } else { - client4.quit(function (err, res) { - next(name); - }); - } - }); -}; - -tests.reconnectRetryMaxDelay = function() { - var time = new Date().getTime(), - name = 'reconnectRetryMaxDelay', - reconnecting = false; - var client = redis.createClient(PORT, HOST, { - retry_max_delay: 1, - parser: parser - }); - client.on('ready', function() { - if (!reconnecting) { - reconnecting = true; - client.retry_delay = 1000; - client.retry_backoff = 1; - client.stream.end(); - } else { - client.end(); - var lasted = new Date().getTime() - time; - assert.ok(lasted < 1000); - next(name); - } - }); -}; - -tests.unref = function () { - var name = "unref"; - var external = fork("./test/test-unref.js", [PORT, HOST]); - var done = false; - external.on("close", function (code) { - assert(code === 0, "test-unref.js failed"); - done = true; - }); - setTimeout(function () { - if (!done) { - external.kill(); - } - assert(done, "test-unref.js didn't finish in time."); - next(name); - }, 1500); -}; - -// starting to split tests into multiple files. -require('./queue-test')(tests, next); - -all_tests = Object.keys(tests); -all_start = new Date(); -test_count = 0; - -run_next_test = function run_next_test() { - var test_name = all_tests.shift(); - if (typeof tests[test_name] === "function") { - console.log('- \x1b[1m' + test_name.toLowerCase() + '\x1b[0m:'); - cur_start = new Date(); - test_count += 1; - tests[test_name](); - } else { - console.log('\n completed \x1b[32m%d\x1b[0m tests in \x1b[33m%d\x1b[0m ms\n', test_count, new Date() - all_start); - client.quit(); - client2.quit(); - bclient.quit(); - } -}; - -client.once("ready", function start_tests() { - console.log("Connected to " + client.address + ", Redis server version " + client.server_info.redis_version + "\n"); - console.log("Using reply parser " + client.reply_parser.name); - - run_next_test(); - - connected = true; -}); - -client.on('end', function () { - ended = true; -}); - -// Exit immediately on connection failure, which triggers "exit", below, which fails the test -client.on("error", function (err) { - console.error("client: " + err.stack); - process.exit(); -}); -client2.on("error", function (err) { - console.error("client2: " + err.stack); - process.exit(); -}); -client3.on("error", function (err) { - console.error("client3: " + err.stack); - process.exit(); -}); -bclient.on("error", function (err) { - console.error("bclient: " + err.stack); - process.exit(); -}); - -client.on("reconnecting", function (params) { - console.log("reconnecting: " + util.inspect(params)); -}); - -process.on('uncaughtException', function (err) { - console.error("Uncaught exception: " + err.stack); - process.exit(1); -}); - -process.on('exit', function (code) { - assert.equal(true, connected); - assert.equal(true, ended); -}); From ce7f21fc34cfedaa07825426a1cdfba2c57dcd78 Mon Sep 17 00:00:00 2001 From: Benjamin Coe Date: Fri, 14 Aug 2015 22:30:51 -0700 Subject: [PATCH 0260/1748] ported sdiff, sdiffstore, sinterstore, smembers, smove, spop, sunion, client.monitor --- test/node_redis.spec.js | 41 +++++++- test/test.js | 215 ---------------------------------------- 2 files changed, 38 insertions(+), 218 deletions(-) diff --git a/test/node_redis.spec.js b/test/node_redis.spec.js index cd3d86da554..6f106034674 100644 --- a/test/node_redis.spec.js +++ b/test/node_redis.spec.js @@ -5,7 +5,7 @@ var helper = require('./helper') var fork = require("child_process").fork; var redis = config.redis; -describe("a node_redis client", function () { +describe("The node_redis client", function () { function allTests(parser, ip) { var args = config.configureClient(parser, ip); @@ -178,8 +178,43 @@ describe("a node_redis client", function () { return done() }); } - }) - }) + }); + }); + + describe('monitor', function () { + it('monitors commands on all other redis clients', function (done) { + if (!helper.serverVersionAtLeast(client, [2, 6, 0])) return done(); + + var monitorClient = redis.createClient.apply(redis.createClient, args); + var responses = []; + + monitorClient.monitor(function (err, res) { + client.mget("some", "keys", "foo", "bar"); + client.set("json", JSON.stringify({ + foo: "123", + bar: "sdflkdfsjk", + another: false + })); + }); + + monitorClient.on("monitor", function (time, args) { + responses.push(args); + if (responses.length === 2) { + assert.strictEqual(5, responses[0].length); + assert.strictEqual("mget", responses[0][0]); + assert.strictEqual("some", responses[0][1]); + assert.strictEqual("keys", responses[0][2]); + assert.strictEqual("foo", responses[0][3]); + assert.strictEqual("bar", responses[0][4]); + assert.strictEqual(3, responses[1].length); + assert.strictEqual("set", responses[1][0]); + assert.strictEqual("json", responses[1][1]); + assert.strictEqual('{"foo":"123","bar":"sdflkdfsjk","another":false}', responses[1][2]); + monitorClient.quit(done); + } + }); + }); + }); }); diff --git a/test/test.js b/test/test.js index 53a2b663bb8..bd165438c09 100644 --- a/test/test.js +++ b/test/test.js @@ -1,180 +1,5 @@ return; -tests.SPOP = function () { - var name = "SPOP"; - - client.del('zzz'); - client.sadd('zzz', 'member0', require_number(1, name)); - client.scard('zzz', require_number(1, name)); - - client.spop('zzz', function (err, value) { - if (err) { - assert.fail(err); - } - assert.equal(value, 'member0', name); - }); - - client.scard('zzz', last(name, require_number(0, name))); -}; - -tests.SDIFF = function () { - var name = "SDIFF"; - - client.del('foo'); - client.sadd('foo', 'x', require_number(1, name)); - client.sadd('foo', 'a', require_number(1, name)); - client.sadd('foo', 'b', require_number(1, name)); - client.sadd('foo', 'c', require_number(1, name)); - - client.sadd('bar', 'c', require_number(1, name)); - - client.sadd('baz', 'a', require_number(1, name)); - client.sadd('baz', 'd', require_number(1, name)); - - client.sdiff('foo', 'bar', 'baz', function (err, values) { - if (err) { - assert.fail(err, name); - } - values.sort(); - assert.equal(values.length, 2, name); - assert.equal(values[0], 'b', name); - assert.equal(values[1], 'x', name); - next(name); - }); -}; - -tests.SDIFFSTORE = function () { - var name = "SDIFFSTORE"; - - client.del('foo'); - client.del('bar'); - client.del('baz'); - client.del('quux'); - - client.sadd('foo', 'x', require_number(1, name)); - client.sadd('foo', 'a', require_number(1, name)); - client.sadd('foo', 'b', require_number(1, name)); - client.sadd('foo', 'c', require_number(1, name)); - - client.sadd('bar', 'c', require_number(1, name)); - - client.sadd('baz', 'a', require_number(1, name)); - client.sadd('baz', 'd', require_number(1, name)); - - // NB: SDIFFSTORE returns the number of elements in the dstkey - - client.sdiffstore('quux', 'foo', 'bar', 'baz', require_number(2, name)); - - client.smembers('quux', function (err, values) { - if (err) { - assert.fail(err, name); - } - var members = buffers_to_strings(values).sort(); - - assert.deepEqual(members, [ 'b', 'x' ], name); - next(name); - }); -}; - -tests.SMEMBERS = function () { - var name = "SMEMBERS"; - - client.del('foo'); - client.sadd('foo', 'x', require_number(1, name)); - - client.smembers('foo', function (err, members) { - if (err) { - assert.fail(err, name); - } - assert.deepEqual(buffers_to_strings(members), [ 'x' ], name); - }); - - client.sadd('foo', 'y', require_number(1, name)); - - client.smembers('foo', function (err, values) { - if (err) { - assert.fail(err, name); - } - assert.equal(values.length, 2, name); - var members = buffers_to_strings(values).sort(); - - assert.deepEqual(members, [ 'x', 'y' ], name); - next(name); - }); -}; - -tests.SMOVE = function () { - var name = "SMOVE"; - - client.del('foo'); - client.del('bar'); - - client.sadd('foo', 'x', require_number(1, name)); - client.smove('foo', 'bar', 'x', require_number(1, name)); - client.sismember('foo', 'x', require_number(0, name)); - client.sismember('bar', 'x', require_number(1, name)); - client.smove('foo', 'bar', 'x', last(name, require_number(0, name))); -}; - -tests.SINTERSTORE = function () { - var name = "SINTERSTORE"; - - client.del('sa'); - client.del('sb'); - client.del('sc'); - client.del('foo'); - - client.sadd('sa', 'a', require_number(1, name)); - client.sadd('sa', 'b', require_number(1, name)); - client.sadd('sa', 'c', require_number(1, name)); - - client.sadd('sb', 'b', require_number(1, name)); - client.sadd('sb', 'c', require_number(1, name)); - client.sadd('sb', 'd', require_number(1, name)); - - client.sadd('sc', 'c', require_number(1, name)); - client.sadd('sc', 'd', require_number(1, name)); - client.sadd('sc', 'e', require_number(1, name)); - - client.sinterstore('foo', 'sa', 'sb', 'sc', require_number(1, name)); - - client.smembers('foo', function (err, members) { - if (err) { - assert.fail(err, name); - } - assert.deepEqual(buffers_to_strings(members), [ 'c' ], name); - next(name); - }); -}; - -tests.SUNION = function () { - var name = "SUNION"; - - client.del('sa'); - client.del('sb'); - client.del('sc'); - - client.sadd('sa', 'a', require_number(1, name)); - client.sadd('sa', 'b', require_number(1, name)); - client.sadd('sa', 'c', require_number(1, name)); - - client.sadd('sb', 'b', require_number(1, name)); - client.sadd('sb', 'c', require_number(1, name)); - client.sadd('sb', 'd', require_number(1, name)); - - client.sadd('sc', 'c', require_number(1, name)); - client.sadd('sc', 'd', require_number(1, name)); - client.sadd('sc', 'e', require_number(1, name)); - - client.sunion('sa', 'sb', 'sc', function (err, union) { - if (err) { - assert.fail(err, name); - } - assert.deepEqual(buffers_to_strings(union).sort(), ['a', 'b', 'c', 'd', 'e'], name); - next(name); - }); -}; - tests.SUNIONSTORE = function () { var name = "SUNIONSTORE"; @@ -212,46 +37,6 @@ tests.SUNIONSTORE = function () { }); }; -tests.MONITOR = function () { - var name = "MONITOR", responses = [], monitor_client; - - if (!server_version_at_least(client, [2, 6, 0])) { - console.log("Skipping " + name + " for old Redis server version < 2.6.x"); - return next(name); - } - - monitor_client = redis.createClient(PORT, HOST, { parser: parser }); - monitor_client.monitor(function (err, res) { - client.mget("some", "keys", "foo", "bar"); - client.set("json", JSON.stringify({ - foo: "123", - bar: "sdflkdfsjk", - another: false - })); - }); - monitor_client.on("monitor", function (time, args) { - // skip monitor command for Redis <= 2.4.16 - if (args[0] === "monitor") return; - - responses.push(args); - if (responses.length === 2) { - assert.strictEqual(5, responses[0].length); - assert.strictEqual("mget", responses[0][0]); - assert.strictEqual("some", responses[0][1]); - assert.strictEqual("keys", responses[0][2]); - assert.strictEqual("foo", responses[0][3]); - assert.strictEqual("bar", responses[0][4]); - assert.strictEqual(3, responses[1].length); - assert.strictEqual("set", responses[1][0]); - assert.strictEqual("json", responses[1][1]); - assert.strictEqual('{"foo":"123","bar":"sdflkdfsjk","another":false}', responses[1][2]); - monitor_client.quit(function (err, res) { - next(name); - }); - } - }); -}; - tests.BLPOP = function () { var name = "BLPOP"; From 2fd3b46835d4ae4e679a8a309f360127578177c0 Mon Sep 17 00:00:00 2001 From: Benjamin Coe Date: Fri, 14 Aug 2015 22:30:58 -0700 Subject: [PATCH 0261/1748] ported sdiff, sdiffstore, sinterstore, smembers, smove, spop, sunion, client.monitor --- test/commands/sdiff.spec.js | 54 ++++++++++++++++++++++++++++++ test/commands/sdiffstore.spec.js | 54 ++++++++++++++++++++++++++++++ test/commands/sinterstore.spec.js | 55 +++++++++++++++++++++++++++++++ test/commands/smembers.spec.js | 45 +++++++++++++++++++++++++ test/commands/smove.spec.js | 48 +++++++++++++++++++++++++++ test/commands/spop.spec.js | 45 +++++++++++++++++++++++++ test/commands/sunion.spec.js | 53 +++++++++++++++++++++++++++++ 7 files changed, 354 insertions(+) create mode 100644 test/commands/sdiff.spec.js create mode 100644 test/commands/sdiffstore.spec.js create mode 100644 test/commands/sinterstore.spec.js create mode 100644 test/commands/smembers.spec.js create mode 100644 test/commands/smove.spec.js create mode 100644 test/commands/spop.spec.js create mode 100644 test/commands/sunion.spec.js diff --git a/test/commands/sdiff.spec.js b/test/commands/sdiff.spec.js new file mode 100644 index 00000000000..deb0628326e --- /dev/null +++ b/test/commands/sdiff.spec.js @@ -0,0 +1,54 @@ +var assert = require("assert"); +var config = require("../lib/config"); +var helper = require("../helper"); +var redis = config.redis; + +describe("The 'sdiff' method", function () { + + function allTests(parser, ip) { + var args = config.configureClient(parser, ip); + + describe("using " + parser + " and " + ip, function () { + var client; + + beforeEach(function (done) { + client = redis.createClient.apply(redis.createClient, args); + client.once("error", done); + client.once("connect", function () { + client.flushdb(done); + }); + }); + + it('returns set difference', function (done) { + client.sadd('foo', 'x', helper.isNumber(1)); + client.sadd('foo', 'a', helper.isNumber(1)); + client.sadd('foo', 'b', helper.isNumber(1)); + client.sadd('foo', 'c', helper.isNumber(1)); + + client.sadd('bar', 'c', helper.isNumber(1)); + + client.sadd('baz', 'a', helper.isNumber(1)); + client.sadd('baz', 'd', helper.isNumber(1)); + + client.sdiff('foo', 'bar', 'baz', function (err, values) { + values.sort(); + assert.equal(values.length, 2); + assert.equal(values[0], 'b'); + assert.equal(values[1], 'x'); + return done(err); + }); + }); + + afterEach(function () { + client.end(); + }); + }); + } + + ['javascript', 'hiredis'].forEach(function (parser) { + allTests(parser, "/tmp/redis.sock"); + ['IPv4', 'IPv6'].forEach(function (ip) { + allTests(parser, ip); + }) + }); +}); diff --git a/test/commands/sdiffstore.spec.js b/test/commands/sdiffstore.spec.js new file mode 100644 index 00000000000..2aac531b4f5 --- /dev/null +++ b/test/commands/sdiffstore.spec.js @@ -0,0 +1,54 @@ +var assert = require("assert"); +var config = require("../lib/config"); +var helper = require("../helper"); +var redis = config.redis; + +describe("The 'sdiffstore' method", function () { + + function allTests(parser, ip) { + var args = config.configureClient(parser, ip); + + describe("using " + parser + " and " + ip, function () { + var client; + + beforeEach(function (done) { + client = redis.createClient.apply(redis.createClient, args); + client.once("error", done); + client.once("connect", function () { + client.flushdb(done); + }); + }); + + it('calculates set difference ands stores it in a key', function (done) { + client.sadd('foo', 'x', helper.isNumber(1)); + client.sadd('foo', 'a', helper.isNumber(1)); + client.sadd('foo', 'b', helper.isNumber(1)); + client.sadd('foo', 'c', helper.isNumber(1)); + + client.sadd('bar', 'c', helper.isNumber(1)); + + client.sadd('baz', 'a', helper.isNumber(1)); + client.sadd('baz', 'd', helper.isNumber(1)); + + client.sdiffstore('quux', 'foo', 'bar', 'baz', helper.isNumber(2)); + + client.smembers('quux', function (err, values) { + var members = values.sort(); + assert.deepEqual(members, [ 'b', 'x' ]); + return done(err); + }); + }); + + afterEach(function () { + client.end(); + }); + }); + } + + ['javascript', 'hiredis'].forEach(function (parser) { + allTests(parser, "/tmp/redis.sock"); + ['IPv4', 'IPv6'].forEach(function (ip) { + allTests(parser, ip); + }) + }); +}); diff --git a/test/commands/sinterstore.spec.js b/test/commands/sinterstore.spec.js new file mode 100644 index 00000000000..f27d47eea6a --- /dev/null +++ b/test/commands/sinterstore.spec.js @@ -0,0 +1,55 @@ +var assert = require("assert"); +var config = require("../lib/config"); +var helper = require("../helper"); +var redis = config.redis; + +describe("The 'sinterstore' method", function () { + + function allTests(parser, ip) { + var args = config.configureClient(parser, ip); + + describe("using " + parser + " and " + ip, function () { + var client; + + beforeEach(function (done) { + client = redis.createClient.apply(redis.createClient, args); + client.once("error", done); + client.once("connect", function () { + client.flushdb(done); + }); + }); + + it('calculates set intersection and stores it in a key', function (done) { + client.sadd('sa', 'a', helper.isNumber(1)); + client.sadd('sa', 'b', helper.isNumber(1)); + client.sadd('sa', 'c', helper.isNumber(1)); + + client.sadd('sb', 'b', helper.isNumber(1)); + client.sadd('sb', 'c', helper.isNumber(1)); + client.sadd('sb', 'd', helper.isNumber(1)); + + client.sadd('sc', 'c', helper.isNumber(1)); + client.sadd('sc', 'd', helper.isNumber(1)); + client.sadd('sc', 'e', helper.isNumber(1)); + + client.sinterstore('foo', 'sa', 'sb', 'sc', helper.isNumber(1)); + + client.smembers('foo', function (err, members) { + assert.deepEqual(members, [ 'c' ]); + return done(err); + }); + }); + + afterEach(function () { + client.end(); + }); + }); + } + + ['javascript', 'hiredis'].forEach(function (parser) { + allTests(parser, "/tmp/redis.sock"); + ['IPv4', 'IPv6'].forEach(function (ip) { + allTests(parser, ip); + }) + }); +}); diff --git a/test/commands/smembers.spec.js b/test/commands/smembers.spec.js new file mode 100644 index 00000000000..0832742ec06 --- /dev/null +++ b/test/commands/smembers.spec.js @@ -0,0 +1,45 @@ +var assert = require("assert"); +var config = require("../lib/config"); +var helper = require("../helper"); +var redis = config.redis; + +describe("The 'smembers' method", function () { + + function allTests(parser, ip) { + var args = config.configureClient(parser, ip); + + describe("using " + parser + " and " + ip, function () { + var client; + + beforeEach(function (done) { + client = redis.createClient.apply(redis.createClient, args); + client.once("error", done); + client.once("connect", function () { + client.flushdb(done); + }); + }); + + it('returns all values in a set', function (done) { + client.sadd('foo', 'x', helper.isNumber(1)); + client.sadd('foo', 'y', helper.isNumber(1)); + client.smembers('foo', function (err, values) { + assert.equal(values.length, 2); + var members = values.sort(); + assert.deepEqual(members, [ 'x', 'y' ]); + return done(err); + }); + }); + + afterEach(function () { + client.end(); + }); + }); + } + + ['javascript', 'hiredis'].forEach(function (parser) { + allTests(parser, "/tmp/redis.sock"); + ['IPv4', 'IPv6'].forEach(function (ip) { + allTests(parser, ip); + }) + }); +}); diff --git a/test/commands/smove.spec.js b/test/commands/smove.spec.js new file mode 100644 index 00000000000..598be288609 --- /dev/null +++ b/test/commands/smove.spec.js @@ -0,0 +1,48 @@ +var assert = require("assert"); +var config = require("../lib/config"); +var helper = require("../helper"); +var redis = config.redis; + +describe("The 'smove' method", function () { + + function allTests(parser, ip) { + var args = config.configureClient(parser, ip); + + describe("using " + parser + " and " + ip, function () { + var client; + + beforeEach(function (done) { + client = redis.createClient.apply(redis.createClient, args); + client.once("error", done); + client.once("connect", function () { + client.flushdb(done); + }); + }); + + it('moves a value to a set that does not yet exist', function (done) { + client.sadd('foo', 'x', helper.isNumber(1)); + client.smove('foo', 'bar', 'x', helper.isNumber(1)); + client.sismember('foo', 'x', helper.isNumber(0)); + client.sismember('bar', 'x', helper.isNumber(1, done)); + }); + + it("does not move a value if it does not exist in the first set", function (done) { + client.sadd('foo', 'x', helper.isNumber(1)); + client.smove('foo', 'bar', 'y', helper.isNumber(0)); + client.sismember('foo', 'y', helper.isNumber(0)); + client.sismember('bar', 'y', helper.isNumber(0, done)); + }); + + afterEach(function () { + client.end(); + }); + }); + } + + ['javascript', 'hiredis'].forEach(function (parser) { + allTests(parser, "/tmp/redis.sock"); + ['IPv4', 'IPv6'].forEach(function (ip) { + allTests(parser, ip); + }) + }); +}); diff --git a/test/commands/spop.spec.js b/test/commands/spop.spec.js new file mode 100644 index 00000000000..43b31442e26 --- /dev/null +++ b/test/commands/spop.spec.js @@ -0,0 +1,45 @@ +var assert = require("assert"); +var config = require("../lib/config"); +var helper = require("../helper"); +var redis = config.redis; + +describe("The 'spop' method", function () { + + function allTests(parser, ip) { + var args = config.configureClient(parser, ip); + + describe("using " + parser + " and " + ip, function () { + var client; + + beforeEach(function (done) { + client = redis.createClient.apply(redis.createClient, args); + client.once("error", done); + client.once("connect", function () { + client.flushdb(done); + }); + }); + + it('returns a random element from the set', function (done) { + client.sadd('zzz', 'member0', helper.isNumber(1)); + client.scard('zzz', helper.isNumber(1)); + + client.spop('zzz', function (err, value) { + if (err) return done(err); + assert.equal(value, 'member0'); + client.scard('zzz', helper.isNumber(0, done)); + }); + }); + + afterEach(function () { + client.end(); + }); + }); + } + + ['javascript', 'hiredis'].forEach(function (parser) { + allTests(parser, "/tmp/redis.sock"); + ['IPv4', 'IPv6'].forEach(function (ip) { + allTests(parser, ip); + }) + }); +}); diff --git a/test/commands/sunion.spec.js b/test/commands/sunion.spec.js new file mode 100644 index 00000000000..92cd81d08f6 --- /dev/null +++ b/test/commands/sunion.spec.js @@ -0,0 +1,53 @@ +var assert = require("assert"); +var config = require("../lib/config"); +var helper = require("../helper"); +var redis = config.redis; + +describe("The 'sunion' method", function () { + + function allTests(parser, ip) { + var args = config.configureClient(parser, ip); + + describe("using " + parser + " and " + ip, function () { + var client; + + beforeEach(function (done) { + client = redis.createClient.apply(redis.createClient, args); + client.once("error", done); + client.once("connect", function () { + client.flushdb(done); + }); + }); + + it('returns the union of a group of sets', function (done) { + client.sadd('sa', 'a', helper.isNumber(1)); + client.sadd('sa', 'b', helper.isNumber(1)); + client.sadd('sa', 'c', helper.isNumber(1)); + + client.sadd('sb', 'b', helper.isNumber(1)); + client.sadd('sb', 'c', helper.isNumber(1)); + client.sadd('sb', 'd', helper.isNumber(1)); + + client.sadd('sc', 'c', helper.isNumber(1)); + client.sadd('sc', 'd', helper.isNumber(1)); + client.sadd('sc', 'e', helper.isNumber(1)); + + client.sunion('sa', 'sb', 'sc', function (err, union) { + assert.deepEqual(union.sort(), ['a', 'b', 'c', 'd', 'e']); + return done(err); + }); + }); + + afterEach(function () { + client.end(); + }); + }); + } + + ['javascript', 'hiredis'].forEach(function (parser) { + allTests(parser, "/tmp/redis.sock"); + ['IPv4', 'IPv6'].forEach(function (ip) { + allTests(parser, ip); + }) + }); +}); From 65db5dbefe3c86040439a2dc2c95fc57c327399b Mon Sep 17 00:00:00 2001 From: Erin Spiceland Date: Sat, 15 Aug 2015 11:51:37 -0500 Subject: [PATCH 0262/1748] Clarify this one test is only applicable for IPv4. --- test/auth.spec.js | 13 +++++++------ 1 file changed, 7 insertions(+), 6 deletions(-) diff --git a/test/auth.spec.js b/test/auth.spec.js index a149f4b9cfd..1854a847446 100644 --- a/test/auth.spec.js +++ b/test/auth.spec.js @@ -41,13 +41,14 @@ describe("client authentication", function () { client.auth(auth + 'bad'); }); - if (ip === 'IPv4') - it('allows auth to be provided as config option for client', function (done) { - client = redis.createClient('redis://foo:' + auth + '@' + config.HOST[ip] + ':' + config.PORT); - client.on("ready", function () { - return done(); + if (ip === 'IPv4') { + it('allows auth to be provided as config option for client', function (done) { + client = redis.createClient('redis://foo:' + auth + '@' + config.HOST[ip] + ':' + config.PORT); + client.on("ready", function () { + return done(); + }); }); - }); + } it('allows auth to be provided as part of redis url', function (done) { var args = config.configureClient(parser, ip, { From 51b1ba2bef7814fc4afaf76e90950340709295df Mon Sep 17 00:00:00 2001 From: Benjamin Coe Date: Sat, 15 Aug 2015 13:00:05 -0700 Subject: [PATCH 0263/1748] finished porting blpop, expire, mset, slowlog, sunionstore, ttl --- test/commands/blpop.spec.js | 64 ++++++++++++++++ test/commands/expire.spec.js | 42 +++++++++++ test/commands/mset.spec.js | 37 ++------- test/commands/slowlog.spec.js | 48 ++++++++++++ test/commands/sunionstore.spec.js | 56 ++++++++++++++ test/commands/ttl.spec.js | 45 +++++++++++ test/test.js | 120 ------------------------------ 7 files changed, 260 insertions(+), 152 deletions(-) create mode 100644 test/commands/blpop.spec.js create mode 100644 test/commands/expire.spec.js create mode 100644 test/commands/slowlog.spec.js create mode 100644 test/commands/sunionstore.spec.js create mode 100644 test/commands/ttl.spec.js delete mode 100644 test/test.js diff --git a/test/commands/blpop.spec.js b/test/commands/blpop.spec.js new file mode 100644 index 00000000000..1e344e023bf --- /dev/null +++ b/test/commands/blpop.spec.js @@ -0,0 +1,64 @@ +var assert = require("assert"); +var config = require("../lib/config"); +var helper = require("../helper"); +var redis = config.redis; + +describe("The 'blpop' method", function () { + + function allTests(parser, ip) { + var args = config.configureClient(parser, ip); + + describe("using " + parser + " and " + ip, function () { + var client; + var bclient; + + beforeEach(function (done) { + client = redis.createClient.apply(redis.createClient, args); + client.once("error", done); + client.once("connect", function () { + client.flushdb(done); + }); + }); + + it('pops value immediately if list contains values', function (done) { + bclient = redis.createClient.apply(redis.createClient, args); + client.rpush("blocking list", "initial value", helper.isNumber(1)); + bclient.blpop("blocking list", 0, function (err, value) { + assert.strictEqual(value[0], "blocking list"); + assert.strictEqual(value[1], "initial value"); + return done(err); + }); + }); + + it('waits for value if list is not yet populated', function (done) { + bclient = redis.createClient.apply(redis.createClient, args); + bclient.blpop("blocking list 2", 5, function (err, value) { + assert.strictEqual(value[0], "blocking list 2"); + assert.strictEqual(value[1], "initial value"); + return done(err); + }); + client.rpush("blocking list 2", "initial value", helper.isNumber(1)); + }); + + it('times out after specified time', function (done) { + bclient = redis.createClient.apply(redis.createClient, args); + bclient.BLPOP("blocking list", 1, function (err, res) { + assert.strictEqual(res, null); + return done(err); + }); + }); + + afterEach(function () { + client.end(); + bclient.end(); + }); + }); + } + + ['javascript', 'hiredis'].forEach(function (parser) { + allTests(parser, "/tmp/redis.sock"); + ['IPv4', 'IPv6'].forEach(function (ip) { + allTests(parser, ip); + }) + }); +}); diff --git a/test/commands/expire.spec.js b/test/commands/expire.spec.js new file mode 100644 index 00000000000..eab08468e9b --- /dev/null +++ b/test/commands/expire.spec.js @@ -0,0 +1,42 @@ +var assert = require("assert"); +var config = require("../lib/config"); +var helper = require("../helper"); +var redis = config.redis; + +describe("The 'expire' method", function () { + + function allTests(parser, ip) { + var args = config.configureClient(parser, ip); + + describe("using " + parser + " and " + ip, function () { + var client; + + beforeEach(function (done) { + client = redis.createClient.apply(redis.createClient, args); + client.once("error", done); + client.once("connect", function () { + client.flushdb(done); + }); + }); + + it('expires key after timeout', function (done) { + client.set(['expiry key', 'bar'], helper.isString("OK")); + client.EXPIRE(["expiry key", "1"], helper.isNumber(1)); + setTimeout(function () { + client.exists(["expiry key"], helper.isNumber(0, done)); + }, 1500); + }); + + afterEach(function () { + client.end(); + }); + }); + } + + ['javascript', 'hiredis'].forEach(function (parser) { + allTests(parser, "/tmp/redis.sock"); + ['IPv4', 'IPv6'].forEach(function (ip) { + allTests(parser, ip); + }) + }); +}); diff --git a/test/commands/mset.spec.js b/test/commands/mset.spec.js index 1523797a3c3..d15c70c1a48 100644 --- a/test/commands/mset.spec.js +++ b/test/commands/mset.spec.js @@ -66,22 +66,9 @@ describe("The 'mset' method", function () { describe("and a callback is specified", function () { describe("with valid parameters", function () { it("sets the value correctly", function (done) { - client.mset(key, value, key2, value2, function (err, res) { - helper.isNotError()(err, res); - async.parallel([function (next) { - client.get(key, function (err, res) { - helper.isString(value)(err, res); - next(); - }); - }, function (next) { - client.get(key2, function (err, res) { - helper.isString(value2)(err, res); - next(); - }); - }], function (err) { - done(err); - }); - }); + client.mset(key, value, key2, value2); + client.get(key, helper.isString(value)); + client.get(key2, helper.isString(value2, done)); }); }); @@ -108,22 +95,8 @@ describe("The 'mset' method", function () { describe("with valid parameters", function () { it("sets the value correctly", function (done) { client.mset(key, value, key2, value2); - - setTimeout(function () { - async.parallel([function (next) { - client.get(key, function (err, res) { - helper.isString(value)(err, res); - next(); - }); - }, function (next) { - client.get(key2, function (err, res) { - helper.isString(value2)(err, res); - next(); - }); - }], function (err) { - done(err); - }); - }, 100); + client.get(key, helper.isString(value)); + client.get(key2, helper.isString(value2, done)); }); }); diff --git a/test/commands/slowlog.spec.js b/test/commands/slowlog.spec.js new file mode 100644 index 00000000000..d09442d5291 --- /dev/null +++ b/test/commands/slowlog.spec.js @@ -0,0 +1,48 @@ +var assert = require("assert"); +var config = require("../lib/config"); +var helper = require("../helper"); +var redis = config.redis; + +describe("The 'slowlog' method", function () { + + function allTests(parser, ip) { + var args = config.configureClient(parser, ip); + + describe("using " + parser + " and " + ip, function () { + var client; + + beforeEach(function (done) { + client = redis.createClient.apply(redis.createClient, args); + client.once("error", done); + client.once("connect", function () { + client.flushdb(done); + }); + }); + + it('logs operations in slowlog', function (done) { + client.config("set", "slowlog-log-slower-than", 0, helper.isString("OK")); + client.slowlog("reset", helper.isString("OK")); + client.set("foo", "bar", helper.isString("OK")); + client.get("foo", helper.isString("bar")); + client.slowlog("get", function (err, res) { + assert.equal(res.length, 3); + assert.equal(res[0][3].length, 2); + assert.deepEqual(res[1][3], ["set", "foo", "bar"]); + assert.deepEqual(res[2][3], ["slowlog", "reset"]); + return done(err); + }); + }); + + afterEach(function () { + client.end(); + }); + }); + } + + ['javascript', 'hiredis'].forEach(function (parser) { + allTests(parser, "/tmp/redis.sock"); + ['IPv4', 'IPv6'].forEach(function (ip) { + allTests(parser, ip); + }) + }); +}); diff --git a/test/commands/sunionstore.spec.js b/test/commands/sunionstore.spec.js new file mode 100644 index 00000000000..702ad131c13 --- /dev/null +++ b/test/commands/sunionstore.spec.js @@ -0,0 +1,56 @@ +var assert = require("assert"); +var config = require("../lib/config"); +var helper = require("../helper"); +var redis = config.redis; + +describe("The 'sunionstore' method", function () { + + function allTests(parser, ip) { + var args = config.configureClient(parser, ip); + + describe("using " + parser + " and " + ip, function () { + var client; + + beforeEach(function (done) { + client = redis.createClient.apply(redis.createClient, args); + client.once("error", done); + client.once("connect", function () { + client.flushdb(done); + }); + }); + + it('stores the result of a union', function (done) { + client.sadd('sa', 'a', helper.isNumber(1)); + client.sadd('sa', 'b', helper.isNumber(1)); + client.sadd('sa', 'c', helper.isNumber(1)); + + client.sadd('sb', 'b', helper.isNumber(1)); + client.sadd('sb', 'c', helper.isNumber(1)); + client.sadd('sb', 'd', helper.isNumber(1)); + + client.sadd('sc', 'c', helper.isNumber(1)); + client.sadd('sc', 'd', helper.isNumber(1)); + client.sadd('sc', 'e', helper.isNumber(1)); + + client.sunionstore('foo', 'sa', 'sb', 'sc', helper.isNumber(5)); + + client.smembers('foo', function (err, members) { + assert.equal(members.length, 5); + assert.deepEqual(members.sort(), ['a', 'b', 'c', 'd', 'e']); + return done(err); + }); + }); + + afterEach(function () { + client.end(); + }); + }); + } + + ['javascript', 'hiredis'].forEach(function (parser) { + allTests(parser, "/tmp/redis.sock"); + ['IPv4', 'IPv6'].forEach(function (ip) { + allTests(parser, ip); + }) + }); +}); diff --git a/test/commands/ttl.spec.js b/test/commands/ttl.spec.js new file mode 100644 index 00000000000..cc93d231e12 --- /dev/null +++ b/test/commands/ttl.spec.js @@ -0,0 +1,45 @@ +var assert = require("assert"); +var config = require("../lib/config"); +var helper = require("../helper"); +var redis = config.redis; + +describe("The 'ttl' method", function () { + + function allTests(parser, ip) { + var args = config.configureClient(parser, ip); + + describe("using " + parser + " and " + ip, function () { + var client; + + beforeEach(function (done) { + client = redis.createClient.apply(redis.createClient, args); + client.once("error", done); + client.once("connect", function () { + client.flushdb(done); + }); + }); + + it('returns the current ttl on a key', function (done) { + client.set(["ttl key", "ttl val"], helper.isString("OK")); + client.expire(["ttl key", "100"], helper.isNumber(1)); + setTimeout(function () { + client.TTL(["ttl key"], function (err, ttl) { + assert.ok(ttl > 50 && ttl <= 100); + return done(err); + }); + }, 500); + }); + + afterEach(function () { + client.end(); + }); + }); + } + + ['javascript', 'hiredis'].forEach(function (parser) { + allTests(parser, "/tmp/redis.sock"); + ['IPv4', 'IPv6'].forEach(function (ip) { + allTests(parser, ip); + }) + }); +}); diff --git a/test/test.js b/test/test.js deleted file mode 100644 index bd165438c09..00000000000 --- a/test/test.js +++ /dev/null @@ -1,120 +0,0 @@ -return; - -tests.SUNIONSTORE = function () { - var name = "SUNIONSTORE"; - - client.del('sa'); - client.del('sb'); - client.del('sc'); - client.del('foo'); - - client.sadd('sa', 'a', require_number(1, name)); - client.sadd('sa', 'b', require_number(1, name)); - client.sadd('sa', 'c', require_number(1, name)); - - client.sadd('sb', 'b', require_number(1, name)); - client.sadd('sb', 'c', require_number(1, name)); - client.sadd('sb', 'd', require_number(1, name)); - - client.sadd('sc', 'c', require_number(1, name)); - client.sadd('sc', 'd', require_number(1, name)); - client.sadd('sc', 'e', require_number(1, name)); - - client.sunionstore('foo', 'sa', 'sb', 'sc', function (err, cardinality) { - if (err) { - assert.fail(err, name); - } - assert.equal(cardinality, 5, name); - }); - - client.smembers('foo', function (err, members) { - if (err) { - assert.fail(err, name); - } - assert.equal(members.length, 5, name); - assert.deepEqual(buffers_to_strings(members).sort(), ['a', 'b', 'c', 'd', 'e'], name); - next(name); - }); -}; - -tests.BLPOP = function () { - var name = "BLPOP"; - - client.rpush("blocking list", "initial value", function (err, res) { - client2.BLPOP("blocking list", 0, function (err, res) { - assert.strictEqual("blocking list", res[0].toString()); - assert.strictEqual("initial value", res[1].toString()); - - client.rpush("blocking list", "wait for this value"); - }); - client2.BLPOP("blocking list", 0, function (err, res) { - assert.strictEqual("blocking list", res[0].toString()); - assert.strictEqual("wait for this value", res[1].toString()); - next(name); - }); - }); -}; - -tests.BLPOP_TIMEOUT = function () { - var name = "BLPOP_TIMEOUT"; - - // try to BLPOP the list again, which should be empty. This should timeout and return null. - client2.BLPOP("blocking list", 1, function (err, res) { - if (err) { - throw err; - } - - assert.strictEqual(res, null); - next(name); - }); -}; - -tests.EXPIRE = function () { - var name = "EXPIRE"; - client.set(['expiry key', 'bar'], require_string("OK", name)); - client.EXPIRE(["expiry key", "1"], require_number_pos(name)); - setTimeout(function () { - client.exists(["expiry key"], last(name, require_number(0, name))); - }, 2000); -}; - -tests.TTL = function () { - var name = "TTL"; - client.set(["ttl key", "ttl val"], require_string("OK", name)); - client.expire(["ttl key", "100"], require_number_pos(name)); - setTimeout(function () { - client.TTL(["ttl key"], last(name, require_number_pos(0, name))); - }, 500); -}; - -tests.OPTIONAL_CALLBACK = function () { - var name = "OPTIONAL_CALLBACK"; - client.del("op_cb1"); - client.set("op_cb1", "x"); - client.get("op_cb1", last(name, require_string("x", name))); -}; - -tests.OPTIONAL_CALLBACK_UNDEFINED = function () { - var name = "OPTIONAL_CALLBACK_UNDEFINED"; - client.del("op_cb2"); - client.set("op_cb2", "y", undefined); - client.get("op_cb2", last(name, require_string("y", name))); - - client.set("op_cb_undefined", undefined, undefined); -}; - -tests.SLOWLOG = function () { - var name = "SLOWLOG"; - client.config("set", "slowlog-log-slower-than", 0, require_string("OK", name)); - client.slowlog("reset", require_string("OK", name)); - client.set("foo", "bar", require_string("OK", name)); - client.get("foo", require_string("bar", name)); - client.slowlog("get", function (err, res) { - assert.equal(res.length, 3, name); - assert.equal(res[0][3].length, 2, name); - assert.deepEqual(res[1][3], ["set", "foo", "bar"], name); - assert.deepEqual(res[2][3], ["slowlog", "reset"], name); - client.config("set", "slowlog-log-slower-than", 10000, require_string("OK", name)); - next(name); - }); -}; From 5d83e64d008f3defe30c75e0b1a2a2d648d1746f Mon Sep 17 00:00:00 2001 From: Benjamin Coe Date: Sat, 15 Aug 2015 13:25:03 -0700 Subject: [PATCH 0264/1748] fixed up our one commented out test, based on @erinspice's code review --- test/node_redis.spec.js | 25 ++++++++++--------------- 1 file changed, 10 insertions(+), 15 deletions(-) diff --git a/test/node_redis.spec.js b/test/node_redis.spec.js index 6f106034674..41e3a47dcff 100644 --- a/test/node_redis.spec.js +++ b/test/node_redis.spec.js @@ -84,27 +84,22 @@ describe("The node_redis client", function () { }); }); + // TODO: we should only have a single subscription in this this + // test but unsubscribing from the single channel indicates + // that one subscriber still exists, let's dig into this. describe("and it's subscribed to a channel", function () { // reconnect_select_db_after_pubsub // Does not pass. // "Connection in subscriber mode, only subscriber commands may be used" - xit("reconnects, unsubscribes, and can retrieve the pre-existing data", function (done) { + it("reconnects, unsubscribes, and can retrieve the pre-existing data", function (done) { client.on("reconnecting", function on_recon(params) { client.on("ready", function on_connect() { - async.parallel([function (cb) { - client.unsubscribe("recon channel", function (err, res) { - helper.isNotError()(err, res); - cb(); - }); - }, function (cb) { - client.get("recon 1", function (err, res) { - helper.isString("one")(err, res); - cb(); - }); - }], function (err, results) { - client.removeListener("connect", on_connect); - client.removeListener("reconnecting", on_recon); - done(err); + client.unsubscribe(helper.isNotError()); + + client.on('unsubscribe', function (channel, count) { + // we should now be out of subscriber mode. + client.set('foo', 'bar', helper.isNumber(1)); + return done(); }); }); }); From d1558eddc2ed5bec5c09312903fbaba79bb09f8d Mon Sep 17 00:00:00 2001 From: Benjamin Coe Date: Sun, 16 Aug 2015 18:02:26 -0700 Subject: [PATCH 0265/1748] add test demonstrating psubscribe, and pmessage --- test/pubsub.spec.js | 14 ++++++++++++++ 1 file changed, 14 insertions(+) diff --git a/test/pubsub.spec.js b/test/pubsub.spec.js index dc502461511..7678fd45bf8 100644 --- a/test/pubsub.spec.js +++ b/test/pubsub.spec.js @@ -208,6 +208,20 @@ describe("publish/subscribe", function () { }); }); + describe('psubscribe', function () { + // test motivated by issue #753 + it('allows all channels to be subscribed to using a * pattern', function (done) { + sub.psubscribe('*'); + sub.on("pmessage", function(pattern, channel, message) { + assert.strictEqual(pattern, '*'); + assert.strictEqual(channel, '/foo'); + assert.strictEqual(message, 'hello world'); + return done(); + }) + pub.publish('/foo', 'hello world'); + }); + }); + describe('punsubscribe', function () { it('does not complain when punsubscribe is called and there are no subscriptions', function () { sub.punsubscribe() From 575ade907cdc2fd904c844fbb750c7abdd810986 Mon Sep 17 00:00:00 2001 From: Daniel Price Date: Fri, 21 Aug 2015 14:57:48 -0700 Subject: [PATCH 0266/1748] Issue #512 send_command("monitoring") is doomed to fail --- index.js | 2 +- test/node_redis.spec.js | 22 ++++++++++++++++++++++ 2 files changed, 23 insertions(+), 1 deletion(-) diff --git a/index.js b/index.js index bdc2889b9a5..e0fc74c2bdb 100644 --- a/index.js +++ b/index.js @@ -369,7 +369,7 @@ RedisClient.prototype.on_ready = function () { }); return; } else if (this.monitoring) { - this.send_command("monitor"); + this.send_command("monitor", []); } else { this.send_offline_queue(); } diff --git a/test/node_redis.spec.js b/test/node_redis.spec.js index 41e3a47dcff..7e88886a307 100644 --- a/test/node_redis.spec.js +++ b/test/node_redis.spec.js @@ -84,6 +84,28 @@ describe("The node_redis client", function () { }); }); + it("reconnects properly when monitoring", function (done) { + client.on("reconnecting", function on_recon(params) { + client.on("ready", function on_ready() { + assert.strictEqual(client.monitoring, true, "monitoring after reconnect"); + client.removeListener("ready", on_ready); + client.removeListener("reconnecting", on_recon); + done(); + }); + }); + + assert.strictEqual(client.monitoring, false, "monitoring off at start"); + client.set("recon 1", "one"); + client.monitor(function (err, res) { + assert.strictEqual(client.monitoring, true, "monitoring on after monitor()"); + client.set("recon 2", "two", function (err, res) { + // Do not do this in normal programs. This is to simulate the server closing on us. + // For orderly shutdown in normal programs, do client.quit() + client.stream.destroy(); + }); + }); + }); + // TODO: we should only have a single subscription in this this // test but unsubscribing from the single channel indicates // that one subscriber still exists, let's dig into this. From 010a72710f5610a0fd9d6184073c26b99c976b13 Mon Sep 17 00:00:00 2001 From: Benjamin Coe Date: Sat, 29 Aug 2015 14:31:50 -0700 Subject: [PATCH 0267/1748] new hiredis works with iojs-v3+ --- .travis.yml | 2 +- package.json | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/.travis.yml b/.travis.yml index f64f437830b..a42dfecc1cb 100644 --- a/.travis.yml +++ b/.travis.yml @@ -3,5 +3,5 @@ sudo: true node_js: - "0.10" - "0.12" - - "iojs-v2" + - "iojs" after_success: npm run coverage diff --git a/package.json b/package.json index 2e7d9ea1812..01562c5392b 100644 --- a/package.json +++ b/package.json @@ -17,7 +17,7 @@ "async": "^1.3.0", "colors": "~0.6.0-1", "coveralls": "^2.11.2", - "hiredis": "^0.4.0", + "hiredis": "^0.4.1", "metrics": ">=0.1.5", "mocha": "^2.2.5", "nyc": "^3.0.0", From 1e6ced4d5c6fbf89c04c65b55e71427f46662ac6 Mon Sep 17 00:00:00 2001 From: Benjamin Coe Date: Sat, 29 Aug 2015 14:42:35 -0700 Subject: [PATCH 0268/1748] cargo-culting node-hiredis toolchain settings --- .travis.yml | 8 ++++++++ 1 file changed, 8 insertions(+) diff --git a/.travis.yml b/.travis.yml index a42dfecc1cb..903127a0265 100644 --- a/.travis.yml +++ b/.travis.yml @@ -1,5 +1,13 @@ language: node_js sudo: true +env: + - CXX=g++-4.8 +addons: + apt: + sources: + - ubuntu-toolchain-r-test + packages: + - g++-4.8 node_js: - "0.10" - "0.12" From 417c8c124771fc15dad501163fa9326aaa8500a9 Mon Sep 17 00:00:00 2001 From: Benjamin Coe Date: Sat, 29 Aug 2015 14:55:01 -0700 Subject: [PATCH 0269/1748] had descriptions of tests swapped around --- test/auth.spec.js | 7 +++---- 1 file changed, 3 insertions(+), 4 deletions(-) diff --git a/test/auth.spec.js b/test/auth.spec.js index 1854a847446..000bb81a669 100644 --- a/test/auth.spec.js +++ b/test/auth.spec.js @@ -42,15 +42,14 @@ describe("client authentication", function () { }); if (ip === 'IPv4') { - it('allows auth to be provided as config option for client', function (done) { + it('allows auth to be provided as part of redis url', function (done) { client = redis.createClient('redis://foo:' + auth + '@' + config.HOST[ip] + ':' + config.PORT); client.on("ready", function () { - return done(); - }); + return done() }); }); } - it('allows auth to be provided as part of redis url', function (done) { + it('allows auth to be provided as config option for client', function (done) { var args = config.configureClient(parser, ip, { auth_pass: auth }); From 338b3cd9d84a8c5b9a473a6a18b9ad48a6fb3b75 Mon Sep 17 00:00:00 2001 From: jomo Date: Sun, 30 Aug 2015 02:37:26 +0200 Subject: [PATCH 0270/1748] Remove redundant check If the first operand of the disjunction (`!obj.loading`) is false, `obj.loading` is truthy. Thus there is no need to test it again in second operand. --- index.js | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/index.js b/index.js index 5abaf00871e..730372bb543 100644 --- a/index.js +++ b/index.js @@ -402,7 +402,7 @@ RedisClient.prototype.on_info_cmd = function (err, res) { // expose info key/vals to users this.server_info = obj; - if (!obj.loading || (obj.loading && obj.loading === "0")) { + if (!obj.loading || obj.loading === "0") { if (exports.debug_mode) { console.log("Redis server ready."); } From aa14d27d4a94270b1c8d46e0c4edbffb3a63a2d3 Mon Sep 17 00:00:00 2001 From: Benjamin Coe Date: Sun, 30 Aug 2015 12:24:33 -0700 Subject: [PATCH 0271/1748] added changelog for v1.0.0 release --- changelog.md | 8 +++++++- 1 file changed, 7 insertions(+), 1 deletion(-) diff --git a/changelog.md b/changelog.md index ca2bc0dc22a..c3ac3450c8e 100644 --- a/changelog.md +++ b/changelog.md @@ -1,7 +1,13 @@ Changelog ========= -* Refactor tests, and improve test coverage (Ben Coe) +## v1.0.0 - Aug 30, 2015 + +* [#658](https://github.com/NodeRedis/node_redis/pull/658) Client now parses URL-format connection strings + redis://foo:pass@127.0.0.1:8080 (@kuwabarahiroshi) +* [#749](https://github.com/NodeRedis/node_redis/pull/749) Fix reconnection bug when client is in monitoring mode (@danielbprice) +* [#786](https://github.com/NodeRedis/node_redis/pull/786) Refactor createClient fixes #651 (@BridgeAR) +* [#793](https://github.com/NodeRedis/node_redis/pull/793) Refactor tests, and improve test coverage (@erinspice, @bcoe) * Fix extraneous error output due to pubsub tests (Mikael Kohlmyr) ## v0.12.1 - Aug 10, 2014 From c0ab99e4ca65fadb8d972ee7cf8f2f55e5c61042 Mon Sep 17 00:00:00 2001 From: Benjamin Coe Date: Sun, 30 Aug 2015 12:26:19 -0700 Subject: [PATCH 0272/1748] give Blain some credit --- changelog.md | 1 + 1 file changed, 1 insertion(+) diff --git a/changelog.md b/changelog.md index c3ac3450c8e..5bfc92e0897 100644 --- a/changelog.md +++ b/changelog.md @@ -3,6 +3,7 @@ Changelog ## v1.0.0 - Aug 30, 2015 +* Huge issue and pull-request cleanup thanks Blain! (@blainsmith) * [#658](https://github.com/NodeRedis/node_redis/pull/658) Client now parses URL-format connection strings redis://foo:pass@127.0.0.1:8080 (@kuwabarahiroshi) * [#749](https://github.com/NodeRedis/node_redis/pull/749) Fix reconnection bug when client is in monitoring mode (@danielbprice) From 7cb998352af450863a0a49f5f7cddc2540ca6bbc Mon Sep 17 00:00:00 2001 From: Benjamin Coe Date: Sun, 30 Aug 2015 12:34:25 -0700 Subject: [PATCH 0273/1748] slight grammar nits --- changelog.md | 9 ++++----- 1 file changed, 4 insertions(+), 5 deletions(-) diff --git a/changelog.md b/changelog.md index 5bfc92e0897..974398dc7f9 100644 --- a/changelog.md +++ b/changelog.md @@ -3,12 +3,11 @@ Changelog ## v1.0.0 - Aug 30, 2015 -* Huge issue and pull-request cleanup thanks Blain! (@blainsmith) -* [#658](https://github.com/NodeRedis/node_redis/pull/658) Client now parses URL-format connection strings - redis://foo:pass@127.0.0.1:8080 (@kuwabarahiroshi) +* Huge issue and pull-request cleanup. Thanks Blain! (@blainsmith) +* [#658](https://github.com/NodeRedis/node_redis/pull/658) Client now parses URL-format connection strings (e.g., redis://foo:pass@127.0.0.1:8080) (@kuwabarahiroshi) * [#749](https://github.com/NodeRedis/node_redis/pull/749) Fix reconnection bug when client is in monitoring mode (@danielbprice) -* [#786](https://github.com/NodeRedis/node_redis/pull/786) Refactor createClient fixes #651 (@BridgeAR) -* [#793](https://github.com/NodeRedis/node_redis/pull/793) Refactor tests, and improve test coverage (@erinspice, @bcoe) +* [#786](https://github.com/NodeRedis/node_redis/pull/786) Refactor createClient. Fixes #651 (@BridgeAR) +* [#793](https://github.com/NodeRedis/node_redis/pull/793) Refactor tests and improve test coverage (@erinspice, @bcoe) * Fix extraneous error output due to pubsub tests (Mikael Kohlmyr) ## v0.12.1 - Aug 10, 2014 From 25b4ddafaac17fa7ab7b7fd5522d564edd60a0cd Mon Sep 17 00:00:00 2001 From: Benjamin Coe Date: Sun, 30 Aug 2015 12:50:55 -0700 Subject: [PATCH 0274/1748] URL connection strings. new release process \o/ see changelog.md for more details: https://github.com/NodeRedis/node_redis/pull/804 --- package.json | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/package.json b/package.json index 01562c5392b..b4452bb1d35 100644 --- a/package.json +++ b/package.json @@ -1,6 +1,6 @@ { "name": "redis", - "version": "0.12.1", + "version": "1.0.0", "description": "Redis client library", "keywords": [ "database", From 41d97526e735196488ac5d61b9aef02881d5f5ea Mon Sep 17 00:00:00 2001 From: Benjamin Coe Date: Sun, 30 Aug 2015 18:09:44 -0700 Subject: [PATCH 0275/1748] make sure we give @raydog credit in the changelog.md --- changelog.md | 1 + 1 file changed, 1 insertion(+) diff --git a/changelog.md b/changelog.md index 974398dc7f9..929e6451567 100644 --- a/changelog.md +++ b/changelog.md @@ -8,6 +8,7 @@ Changelog * [#749](https://github.com/NodeRedis/node_redis/pull/749) Fix reconnection bug when client is in monitoring mode (@danielbprice) * [#786](https://github.com/NodeRedis/node_redis/pull/786) Refactor createClient. Fixes #651 (@BridgeAR) * [#793](https://github.com/NodeRedis/node_redis/pull/793) Refactor tests and improve test coverage (@erinspice, @bcoe) +* [#733](https://github.com/NodeRedis/node_redis/pull/733) Fixes detect_buffers functionality in the context of exec. Fixes #732, #263 (@raydog) * Fix extraneous error output due to pubsub tests (Mikael Kohlmyr) ## v0.12.1 - Aug 10, 2014 From a5938f3ade56cd35f9ea6ffc4319ce518ae49cdb Mon Sep 17 00:00:00 2001 From: Ruben Bridgewater Date: Tue, 1 Sep 2015 23:24:28 +0200 Subject: [PATCH 0276/1748] Refactor the test suite and remove duplicated code --- test/auth.spec.js | 10 ++-------- test/commands/blpop.spec.js | 10 +--------- test/commands/client.spec.js | 10 +--------- test/commands/dbsize.spec.js | 10 +--------- test/commands/del.spec.js | 10 +--------- test/commands/eval.spec.js | 11 ++--------- test/commands/exits.spec.js | 10 +--------- test/commands/expire.spec.js | 10 +--------- test/commands/flushdb.spec.js | 10 +--------- test/commands/get.spec.js | 10 +--------- test/commands/getset.spec.js | 10 +--------- test/commands/hgetall.spec.js | 10 ++-------- test/commands/hincrby.spec.js | 10 +--------- test/commands/hlen.spec.js | 10 +--------- test/commands/hmget.spec.js | 10 +--------- test/commands/hmset.spec.js | 10 +--------- test/commands/hset.spec.js | 10 +--------- test/commands/incr.spec.js | 10 +--------- test/commands/keys.spec.js | 10 +--------- test/commands/mget.spec.js | 10 +--------- test/commands/mset.spec.js | 10 +--------- test/commands/msetnx.spec.js | 10 +--------- test/commands/multi.spec.js | 10 +--------- test/commands/randomkey.test.js | 10 +--------- test/commands/rename.spec.js | 10 +--------- test/commands/renamenx.spec.js | 10 +--------- test/commands/sadd.spec.js | 10 +--------- test/commands/scard.spec.js | 10 +--------- test/commands/script.spec.js | 10 +--------- test/commands/sdiff.spec.js | 10 +--------- test/commands/sdiffstore.spec.js | 10 +--------- test/commands/select.spec.js | 10 +--------- test/commands/set.spec.js | 10 +--------- test/commands/setex.spec.js | 12 ++---------- test/commands/setnx.spec.js | 10 +--------- test/commands/sinter.spec.js | 10 +--------- test/commands/sinterstore.spec.js | 10 +--------- test/commands/sismember.spec.js | 10 +--------- test/commands/slowlog.spec.js | 10 +--------- test/commands/smembers.spec.js | 10 +--------- test/commands/smove.spec.js | 10 +--------- test/commands/sort.spec.js | 11 ++--------- test/commands/spop.spec.js | 10 +--------- test/commands/srem.spec.js | 10 +--------- test/commands/sunion.spec.js | 10 +--------- test/commands/sunionstore.spec.js | 10 +--------- test/commands/ttl.spec.js | 10 +--------- test/commands/type.spec.js | 11 ++--------- test/commands/watch.spec.js | 13 +++---------- test/helper.js | 9 +++++++++ test/node_redis.spec.js | 10 +--------- test/pubsub.spec.js | 10 +--------- 52 files changed, 68 insertions(+), 459 deletions(-) diff --git a/test/auth.spec.js b/test/auth.spec.js index 000bb81a669..63ce1cf9ffc 100644 --- a/test/auth.spec.js +++ b/test/auth.spec.js @@ -11,7 +11,8 @@ describe("client authentication", function () { }); }); - function allTests(parser, ip) { + helper.allTests(function(parser, ip, args) { + describe("using " + parser + " and " + ip, function () { var args = config.configureClient(parser, ip); var auth = 'porkchopsandwiches'; @@ -73,13 +74,6 @@ describe("client authentication", function () { }); }); }); - } - - ['javascript', 'hiredis'].forEach(function (parser) { - allTests(parser, "/tmp/redis.sock"); - ['IPv4', 'IPv6'].forEach(function (ip) { - allTests(parser, ip); - }) }); after(function (done) { diff --git a/test/commands/blpop.spec.js b/test/commands/blpop.spec.js index 1e344e023bf..e5474154386 100644 --- a/test/commands/blpop.spec.js +++ b/test/commands/blpop.spec.js @@ -5,8 +5,7 @@ var redis = config.redis; describe("The 'blpop' method", function () { - function allTests(parser, ip) { - var args = config.configureClient(parser, ip); + helper.allTests(function(parser, ip, args) { describe("using " + parser + " and " + ip, function () { var client; @@ -53,12 +52,5 @@ describe("The 'blpop' method", function () { bclient.end(); }); }); - } - - ['javascript', 'hiredis'].forEach(function (parser) { - allTests(parser, "/tmp/redis.sock"); - ['IPv4', 'IPv6'].forEach(function (ip) { - allTests(parser, ip); - }) }); }); diff --git a/test/commands/client.spec.js b/test/commands/client.spec.js index 3710bdc9edd..d0ae072f9fc 100644 --- a/test/commands/client.spec.js +++ b/test/commands/client.spec.js @@ -5,8 +5,7 @@ var redis = config.redis; describe("The 'client' method", function () { - function allTests(parser, ip) { - var args = config.configureClient(parser, ip); + helper.allTests(function(parser, ip, args) { var pattern = /addr=/; describe("using " + parser + " and " + ip, function () { @@ -50,12 +49,5 @@ describe("The 'client' method", function () { }); }); }); - } - - ['javascript', 'hiredis'].forEach(function (parser) { - allTests(parser, "/tmp/redis.sock"); - ['IPv4', 'IPv6'].forEach(function (ip) { - allTests(parser, ip); - }) }); }); diff --git a/test/commands/dbsize.spec.js b/test/commands/dbsize.spec.js index 70c7eb178c1..d65e9469b25 100644 --- a/test/commands/dbsize.spec.js +++ b/test/commands/dbsize.spec.js @@ -7,8 +7,7 @@ var uuid = require('uuid'); describe("The 'dbsize' method", function () { - function allTests(parser, ip) { - var args = config.configureClient(parser, ip); + helper.allTests(function(parser, ip, args) { describe("using " + parser + " and " + ip, function () { var key, value; @@ -95,12 +94,5 @@ describe("The 'dbsize' method", function () { }); }); }); - } - - ['javascript', 'hiredis'].forEach(function (parser) { - allTests(parser, "/tmp/redis.sock"); - ['IPv4', 'IPv6'].forEach(function (ip) { - allTests(parser, ip); - }) }); }); diff --git a/test/commands/del.spec.js b/test/commands/del.spec.js index b2b2995c484..6845ee363f7 100644 --- a/test/commands/del.spec.js +++ b/test/commands/del.spec.js @@ -5,8 +5,7 @@ var redis = config.redis; describe("The 'del' method", function () { - function allTests(parser, ip) { - var args = config.configureClient(parser, ip); + helper.allTests(function(parser, ip, args) { describe("using " + parser + " and " + ip, function () { var client; @@ -40,12 +39,5 @@ describe("The 'del' method", function () { client.end(); }); }); - } - - ['javascript', 'hiredis'].forEach(function (parser) { - allTests(parser, "/tmp/redis.sock"); - ['IPv4', 'IPv6'].forEach(function (ip) { - allTests(parser, ip); - }) }); }); diff --git a/test/commands/eval.spec.js b/test/commands/eval.spec.js index a98efa032e6..d132e7c2a92 100644 --- a/test/commands/eval.spec.js +++ b/test/commands/eval.spec.js @@ -5,8 +5,8 @@ var helper = require("../helper"); var redis = config.redis; describe("The 'eval' method", function () { - function allTests(parser, ip) { - var args = config.configureClient(parser, ip); + + helper.allTests(function(parser, ip, args) { describe("using " + parser + " and " + ip, function () { var client; @@ -188,12 +188,5 @@ describe("The 'eval' method", function () { }); }); }); - } - - ['javascript', 'hiredis'].forEach(function (parser) { - allTests(parser, "/tmp/redis.sock"); - ['IPv4', 'IPv6'].forEach(function (ip) { - allTests(parser, ip); - }) }); }); diff --git a/test/commands/exits.spec.js b/test/commands/exits.spec.js index b3f04badcc7..c36e9bcfedd 100644 --- a/test/commands/exits.spec.js +++ b/test/commands/exits.spec.js @@ -5,8 +5,7 @@ var redis = config.redis; describe("The 'exits' method", function () { - function allTests(parser, ip) { - var args = config.configureClient(parser, ip); + helper.allTests(function(parser, ip, args) { describe("using " + parser + " and " + ip, function () { var client; @@ -32,12 +31,5 @@ describe("The 'exits' method", function () { client.end(); }); }); - } - - ['javascript', 'hiredis'].forEach(function (parser) { - allTests(parser, "/tmp/redis.sock"); - ['IPv4', 'IPv6'].forEach(function (ip) { - allTests(parser, ip); - }) }); }); diff --git a/test/commands/expire.spec.js b/test/commands/expire.spec.js index eab08468e9b..8e7d3a188e4 100644 --- a/test/commands/expire.spec.js +++ b/test/commands/expire.spec.js @@ -5,8 +5,7 @@ var redis = config.redis; describe("The 'expire' method", function () { - function allTests(parser, ip) { - var args = config.configureClient(parser, ip); + helper.allTests(function(parser, ip, args) { describe("using " + parser + " and " + ip, function () { var client; @@ -31,12 +30,5 @@ describe("The 'expire' method", function () { client.end(); }); }); - } - - ['javascript', 'hiredis'].forEach(function (parser) { - allTests(parser, "/tmp/redis.sock"); - ['IPv4', 'IPv6'].forEach(function (ip) { - allTests(parser, ip); - }) }); }); diff --git a/test/commands/flushdb.spec.js b/test/commands/flushdb.spec.js index 8df39f4752a..80533d34620 100644 --- a/test/commands/flushdb.spec.js +++ b/test/commands/flushdb.spec.js @@ -7,8 +7,7 @@ var uuid = require('uuid'); describe("The 'flushdb' method", function () { - function allTests(parser, ip) { - var args = config.configureClient(parser, ip); + helper.allTests(function(parser, ip, args) { describe("using " + parser + " and " + ip, function () { var key, key2; @@ -104,12 +103,5 @@ describe("The 'flushdb' method", function () { }); }); }); - } - - ['javascript', 'hiredis'].forEach(function (parser) { - allTests(parser, "/tmp/redis.sock"); - ['IPv4', 'IPv6'].forEach(function (ip) { - allTests(parser, ip); - }) }); }); diff --git a/test/commands/get.spec.js b/test/commands/get.spec.js index 31502c2c4b6..4ba2621f0fe 100644 --- a/test/commands/get.spec.js +++ b/test/commands/get.spec.js @@ -7,8 +7,7 @@ var uuid = require('uuid'); describe("The 'get' method", function () { - function allTests(parser, ip) { - var args = config.configureClient(parser, ip); + helper.allTests(function(parser, ip, args) { describe("using " + parser + " and " + ip, function () { var key, value; @@ -81,12 +80,5 @@ describe("The 'get' method", function () { }); }); }); - } - - ['javascript', 'hiredis'].forEach(function (parser) { - allTests(parser, "/tmp/redis.sock"); - ['IPv4', 'IPv6'].forEach(function (ip) { - allTests(parser, ip); - }) }); }); diff --git a/test/commands/getset.spec.js b/test/commands/getset.spec.js index a8e90ecfa2f..f3c294a6691 100644 --- a/test/commands/getset.spec.js +++ b/test/commands/getset.spec.js @@ -7,8 +7,7 @@ var uuid = require('uuid'); describe("The 'getset' method", function () { - function allTests(parser, ip) { - var args = config.configureClient(parser, ip); + helper.allTests(function(parser, ip, args) { describe("using " + parser + " and " + ip, function () { var key, value, value2; @@ -85,12 +84,5 @@ describe("The 'getset' method", function () { }); }); }); - } - - ['javascript', 'hiredis'].forEach(function (parser) { - allTests(parser, "/tmp/redis.sock"); - ['IPv4', 'IPv6'].forEach(function (ip) { - allTests(parser, ip); - }) }); }); diff --git a/test/commands/hgetall.spec.js b/test/commands/hgetall.spec.js index c49988157ee..aa3e520cebc 100644 --- a/test/commands/hgetall.spec.js +++ b/test/commands/hgetall.spec.js @@ -5,7 +5,8 @@ var redis = config.redis; describe("The 'hgetall' method", function () { - function allTests(parser, ip) { + helper.allTests(function(parser, ip, args) { + describe("using " + parser + " and " + ip, function () { var client; @@ -80,12 +81,5 @@ describe("The 'hgetall' method", function () { client.end(); }); }); - } - - ['javascript', 'hiredis'].forEach(function (parser) { - allTests(parser, "/tmp/redis.sock"); - ['IPv4', 'IPv6'].forEach(function (ip) { - allTests(parser, ip); - }) }); }); diff --git a/test/commands/hincrby.spec.js b/test/commands/hincrby.spec.js index f137405f782..b1da736027e 100644 --- a/test/commands/hincrby.spec.js +++ b/test/commands/hincrby.spec.js @@ -5,8 +5,7 @@ var redis = config.redis; describe("The 'hincrby' method", function () { - function allTests(parser, ip) { - var args = config.configureClient(parser, ip); + helper.allTests(function(parser, ip, args) { describe("using " + parser + " and " + ip, function () { var client; @@ -37,12 +36,5 @@ describe("The 'hincrby' method", function () { client.end(); }); }); - } - - ['javascript', 'hiredis'].forEach(function (parser) { - allTests(parser, "/tmp/redis.sock"); - ['IPv4', 'IPv6'].forEach(function (ip) { - allTests(parser, ip); - }) }); }); diff --git a/test/commands/hlen.spec.js b/test/commands/hlen.spec.js index 11905809c1b..7756e3366bf 100644 --- a/test/commands/hlen.spec.js +++ b/test/commands/hlen.spec.js @@ -5,8 +5,7 @@ var redis = config.redis; describe("The 'hlen' method", function () { - function allTests(parser, ip) { - var args = config.configureClient(parser, ip); + helper.allTests(function(parser, ip, args) { describe("using " + parser + " and " + ip, function () { var client; @@ -35,12 +34,5 @@ describe("The 'hlen' method", function () { client.end(); }); }); - } - - ['javascript', 'hiredis'].forEach(function (parser) { - allTests(parser, "/tmp/redis.sock"); - ['IPv4', 'IPv6'].forEach(function (ip) { - allTests(parser, ip); - }) }); }); diff --git a/test/commands/hmget.spec.js b/test/commands/hmget.spec.js index fbced263bbb..3ad8fa9128c 100644 --- a/test/commands/hmget.spec.js +++ b/test/commands/hmget.spec.js @@ -5,8 +5,7 @@ var redis = config.redis; describe("The 'hmget' method", function () { - function allTests(parser, ip) { - var args = config.configureClient(parser, ip); + helper.allTests(function(parser, ip, args) { describe("using " + parser + " and " + ip, function () { var client; @@ -57,12 +56,5 @@ describe("The 'hmget' method", function () { client.end(); }); }); - } - - ['javascript', 'hiredis'].forEach(function (parser) { - allTests(parser, "/tmp/redis.sock"); - ['IPv4', 'IPv6'].forEach(function (ip) { - allTests(parser, ip); - }) }); }); diff --git a/test/commands/hmset.spec.js b/test/commands/hmset.spec.js index d0f23eb066c..e30669d00e5 100644 --- a/test/commands/hmset.spec.js +++ b/test/commands/hmset.spec.js @@ -5,8 +5,7 @@ var redis = config.redis; describe("The 'hmset' method", function () { - function allTests(parser, ip) { - var args = config.configureClient(parser, ip); + helper.allTests(function(parser, ip, args) { describe("using " + parser + " and " + ip, function () { var client; @@ -50,12 +49,5 @@ describe("The 'hmset' method", function () { client.end(); }); }); - } - - ['javascript', 'hiredis'].forEach(function (parser) { - allTests(parser, "/tmp/redis.sock"); - ['IPv4', 'IPv6'].forEach(function (ip) { - allTests(parser, ip); - }) }); }); diff --git a/test/commands/hset.spec.js b/test/commands/hset.spec.js index 0ce2432ea9d..668838b60de 100644 --- a/test/commands/hset.spec.js +++ b/test/commands/hset.spec.js @@ -5,8 +5,7 @@ var redis = config.redis; describe("The 'hset' method", function () { - function allTests(parser, ip) { - var args = config.configureClient(parser, ip); + helper.allTests(function(parser, ip, args) { describe("using " + parser + " and " + ip, function () { var client; @@ -59,12 +58,5 @@ describe("The 'hset' method", function () { client.end(); }); }); - } - - ['javascript', 'hiredis'].forEach(function (parser) { - allTests(parser, "/tmp/redis.sock"); - ['IPv4', 'IPv6'].forEach(function (ip) { - allTests(parser, ip); - }) }); }); diff --git a/test/commands/incr.spec.js b/test/commands/incr.spec.js index bdd22bacf16..4e064279331 100644 --- a/test/commands/incr.spec.js +++ b/test/commands/incr.spec.js @@ -7,8 +7,7 @@ var uuid = require('uuid'); describe("The 'incr' method", function () { - function allTests(parser, ip) { - var args = config.configureClient(parser, ip); + helper.allTests(function(parser, ip, args) { describe("using " + parser + " and " + ip, function () { var key = "sequence"; @@ -113,12 +112,5 @@ describe("The 'incr' method", function () { }); }); }); - } - - ['javascript', 'hiredis'].forEach(function (parser) { - allTests(parser, "/tmp/redis.sock"); - ['IPv4', 'IPv6'].forEach(function (ip) { - allTests(parser, ip); - }) }); }); diff --git a/test/commands/keys.spec.js b/test/commands/keys.spec.js index b1f5f9cb51a..07add246507 100644 --- a/test/commands/keys.spec.js +++ b/test/commands/keys.spec.js @@ -6,8 +6,7 @@ var redis = config.redis; describe("The 'keys' method", function () { - function allTests(parser, ip) { - var args = config.configureClient(parser, ip); + helper.allTests(function(parser, ip, args) { describe("using " + parser + " and " + ip, function () { var client; @@ -65,12 +64,5 @@ describe("The 'keys' method", function () { client.end(); }); }); - } - - ['javascript', 'hiredis'].forEach(function (parser) { - allTests(parser, "/tmp/redis.sock"); - ['IPv4', 'IPv6'].forEach(function (ip) { - allTests(parser, ip); - }) }); }); diff --git a/test/commands/mget.spec.js b/test/commands/mget.spec.js index 1169b5949fc..c4052a17fdd 100644 --- a/test/commands/mget.spec.js +++ b/test/commands/mget.spec.js @@ -5,8 +5,7 @@ var redis = config.redis; describe("The 'mget' method", function () { - function allTests(parser, ip) { - var args = config.configureClient(parser, ip); + helper.allTests(function(parser, ip, args) { describe("using " + parser + " and " + ip, function () { var client; @@ -55,12 +54,5 @@ describe("The 'mget' method", function () { client.end(); }); }); - } - - ['javascript', 'hiredis'].forEach(function (parser) { - allTests(parser, "/tmp/redis.sock"); - ['IPv4', 'IPv6'].forEach(function (ip) { - allTests(parser, ip); - }) }); }); diff --git a/test/commands/mset.spec.js b/test/commands/mset.spec.js index d15c70c1a48..aacb969c696 100644 --- a/test/commands/mset.spec.js +++ b/test/commands/mset.spec.js @@ -13,8 +13,7 @@ describe("The 'mset' method", function () { return mochaListener; } - function allTests(parser, ip) { - var args = config.configureClient(parser, ip); + helper.allTests(function(parser, ip, args) { describe("using " + parser + " and " + ip, function () { var key, value, key2, value2; @@ -130,12 +129,5 @@ describe("The 'mset' method", function () { }); }); }); - } - - ['javascript', 'hiredis'].forEach(function (parser) { - allTests(parser, "/tmp/redis.sock"); - ['IPv4', 'IPv6'].forEach(function (ip) { - allTests(parser, ip); - }) }); }); diff --git a/test/commands/msetnx.spec.js b/test/commands/msetnx.spec.js index ddbb893c1ae..ef7cf63e9f7 100644 --- a/test/commands/msetnx.spec.js +++ b/test/commands/msetnx.spec.js @@ -5,8 +5,7 @@ var redis = config.redis; describe("The 'msetnx' method", function () { - function allTests(parser, ip) { - var args = config.configureClient(parser, ip); + helper.allTests(function(parser, ip, args) { describe("using " + parser + " and " + ip, function () { var client; @@ -35,12 +34,5 @@ describe("The 'msetnx' method", function () { client.end(); }); }); - } - - ['javascript', 'hiredis'].forEach(function (parser) { - allTests(parser, "/tmp/redis.sock"); - ['IPv4', 'IPv6'].forEach(function (ip) { - allTests(parser, ip); - }) }); }); diff --git a/test/commands/multi.spec.js b/test/commands/multi.spec.js index 21526334a1c..f99e85211f4 100644 --- a/test/commands/multi.spec.js +++ b/test/commands/multi.spec.js @@ -7,8 +7,7 @@ var uuid = require('uuid'); describe("The 'multi' method", function () { - function allTests(parser, ip) { - var args = config.configureClient(parser, ip); + helper.allTests(function(parser, ip, args) { describe("using " + parser + " and " + ip, function () { var key, value; @@ -239,12 +238,5 @@ describe("The 'multi' method", function () { }); }); - } - - ['javascript', 'hiredis'].forEach(function (parser) { - allTests(parser, "/tmp/redis.sock"); - ['IPv4', 'IPv6'].forEach(function (ip) { - allTests(parser, ip); - }) }); }); diff --git a/test/commands/randomkey.test.js b/test/commands/randomkey.test.js index 75498003942..ef11253adb3 100644 --- a/test/commands/randomkey.test.js +++ b/test/commands/randomkey.test.js @@ -5,8 +5,7 @@ var redis = config.redis; describe("The 'randomkey' method", function () { - function allTests(parser, ip) { - var args = config.configureClient(parser, ip); + helper.allTests(function(parser, ip, args) { describe("using " + parser + " and " + ip, function () { var client; @@ -31,12 +30,5 @@ describe("The 'randomkey' method", function () { client.end(); }); }); - } - - ['javascript', 'hiredis'].forEach(function (parser) { - allTests(parser, "/tmp/redis.sock"); - ['IPv4', 'IPv6'].forEach(function (ip) { - allTests(parser, ip); - }) }); }); diff --git a/test/commands/rename.spec.js b/test/commands/rename.spec.js index be2145923d3..3389fa59da2 100644 --- a/test/commands/rename.spec.js +++ b/test/commands/rename.spec.js @@ -5,8 +5,7 @@ var redis = config.redis; describe("The 'rename' method", function () { - function allTests(parser, ip) { - var args = config.configureClient(parser, ip); + helper.allTests(function(parser, ip, args) { describe("using " + parser + " and " + ip, function () { var client; @@ -35,12 +34,5 @@ describe("The 'rename' method", function () { client.end(); }); }); - } - - ['javascript', 'hiredis'].forEach(function (parser) { - allTests(parser, "/tmp/redis.sock"); - ['IPv4', 'IPv6'].forEach(function (ip) { - allTests(parser, ip); - }) }); }); diff --git a/test/commands/renamenx.spec.js b/test/commands/renamenx.spec.js index 6dd1a1b0d6b..233d1aaba5f 100644 --- a/test/commands/renamenx.spec.js +++ b/test/commands/renamenx.spec.js @@ -5,8 +5,7 @@ var redis = config.redis; describe("The 'renamenx' method", function () { - function allTests(parser, ip) { - var args = config.configureClient(parser, ip); + helper.allTests(function(parser, ip, args) { describe("using " + parser + " and " + ip, function () { var client; @@ -38,12 +37,5 @@ describe("The 'renamenx' method", function () { client.end(); }); }); - } - - ['javascript', 'hiredis'].forEach(function (parser) { - allTests(parser, "/tmp/redis.sock"); - ['IPv4', 'IPv6'].forEach(function (ip) { - allTests(parser, ip); - }) }); }); diff --git a/test/commands/sadd.spec.js b/test/commands/sadd.spec.js index ea63a637ead..a6a8ac43300 100644 --- a/test/commands/sadd.spec.js +++ b/test/commands/sadd.spec.js @@ -5,8 +5,7 @@ var redis = config.redis; describe("The 'sadd' method", function () { - function allTests(parser, ip) { - var args = config.configureClient(parser, ip); + helper.allTests(function(parser, ip, args) { describe("using " + parser + " and " + ip, function () { var client; @@ -47,12 +46,5 @@ describe("The 'sadd' method", function () { client.end(); }); }); - } - - ['javascript', 'hiredis'].forEach(function (parser) { - allTests(parser, "/tmp/redis.sock"); - ['IPv4', 'IPv6'].forEach(function (ip) { - allTests(parser, ip); - }) }); }); diff --git a/test/commands/scard.spec.js b/test/commands/scard.spec.js index 4030e27b958..180da0a98ef 100644 --- a/test/commands/scard.spec.js +++ b/test/commands/scard.spec.js @@ -5,8 +5,7 @@ var redis = config.redis; describe("The 'scard' method", function () { - function allTests(parser, ip) { - var args = config.configureClient(parser, ip); + helper.allTests(function(parser, ip, args) { describe("using " + parser + " and " + ip, function () { var client; @@ -28,12 +27,5 @@ describe("The 'scard' method", function () { client.end(); }); }); - } - - ['javascript', 'hiredis'].forEach(function (parser) { - allTests(parser, "/tmp/redis.sock"); - ['IPv4', 'IPv6'].forEach(function (ip) { - allTests(parser, ip); - }) }); }); diff --git a/test/commands/script.spec.js b/test/commands/script.spec.js index df3d8aa4865..80cbf1abc0b 100644 --- a/test/commands/script.spec.js +++ b/test/commands/script.spec.js @@ -6,8 +6,7 @@ var redis = config.redis; describe("The 'script' method", function () { - function allTests(parser, ip) { - var args = config.configureClient(parser, ip); + helper.allTests(function(parser, ip, args) { var command = "return 99"; var commandSha = crypto.createHash('sha1').update(command).digest('hex'); @@ -57,12 +56,5 @@ describe("The 'script' method", function () { }) }) }); - } - - ['javascript', 'hiredis'].forEach(function (parser) { - allTests(parser, "/tmp/redis.sock"); - ['IPv4', 'IPv6'].forEach(function (ip) { - allTests(parser, ip); - }) }); }); diff --git a/test/commands/sdiff.spec.js b/test/commands/sdiff.spec.js index deb0628326e..0e233f26ff7 100644 --- a/test/commands/sdiff.spec.js +++ b/test/commands/sdiff.spec.js @@ -5,8 +5,7 @@ var redis = config.redis; describe("The 'sdiff' method", function () { - function allTests(parser, ip) { - var args = config.configureClient(parser, ip); + helper.allTests(function(parser, ip, args) { describe("using " + parser + " and " + ip, function () { var client; @@ -43,12 +42,5 @@ describe("The 'sdiff' method", function () { client.end(); }); }); - } - - ['javascript', 'hiredis'].forEach(function (parser) { - allTests(parser, "/tmp/redis.sock"); - ['IPv4', 'IPv6'].forEach(function (ip) { - allTests(parser, ip); - }) }); }); diff --git a/test/commands/sdiffstore.spec.js b/test/commands/sdiffstore.spec.js index 2aac531b4f5..c497cf47e2f 100644 --- a/test/commands/sdiffstore.spec.js +++ b/test/commands/sdiffstore.spec.js @@ -5,8 +5,7 @@ var redis = config.redis; describe("The 'sdiffstore' method", function () { - function allTests(parser, ip) { - var args = config.configureClient(parser, ip); + helper.allTests(function(parser, ip, args) { describe("using " + parser + " and " + ip, function () { var client; @@ -43,12 +42,5 @@ describe("The 'sdiffstore' method", function () { client.end(); }); }); - } - - ['javascript', 'hiredis'].forEach(function (parser) { - allTests(parser, "/tmp/redis.sock"); - ['IPv4', 'IPv6'].forEach(function (ip) { - allTests(parser, ip); - }) }); }); diff --git a/test/commands/select.spec.js b/test/commands/select.spec.js index 1be845dc002..dac3c8b34c7 100644 --- a/test/commands/select.spec.js +++ b/test/commands/select.spec.js @@ -12,8 +12,7 @@ describe("The 'select' method", function () { return mochaListener; } - function allTests(parser, ip) { - var args = config.configureClient(parser, ip); + helper.allTests(function(parser, ip, args) { describe("using " + parser + " and " + ip, function () { describe("when not connected", function () { @@ -112,12 +111,5 @@ describe("The 'select' method", function () { }); }); }); - } - - ['javascript', 'hiredis'].forEach(function (parser) { - allTests(parser, "/tmp/redis.sock"); - ['IPv4', 'IPv6'].forEach(function (ip) { - allTests(parser, ip); - }) }); }); diff --git a/test/commands/set.spec.js b/test/commands/set.spec.js index b9bbe79c858..28455c9ebe9 100644 --- a/test/commands/set.spec.js +++ b/test/commands/set.spec.js @@ -13,8 +13,7 @@ describe("The 'set' method", function () { return mochaListener; } - function allTests(parser, ip) { - var args = config.configureClient(parser, ip); + helper.allTests(function(parser, ip, args) { describe("using " + parser + " and " + ip, function () { var key, value; @@ -154,12 +153,5 @@ describe("The 'set' method", function () { }); }); }); - } - - ['javascript', 'hiredis'].forEach(function (parser) { - allTests(parser, "/tmp/redis.sock"); - ['IPv4', 'IPv6'].forEach(function (ip) { - allTests(parser, ip); - }) }); }); diff --git a/test/commands/setex.spec.js b/test/commands/setex.spec.js index d66366785ed..b4e6189168b 100644 --- a/test/commands/setex.spec.js +++ b/test/commands/setex.spec.js @@ -5,8 +5,7 @@ var redis = config.redis; describe("The 'setex' method", function () { - function allTests(parser, ip) { - var args = config.configureClient(parser, ip); + helper.allTests(function(parser, ip, args) { describe("using " + parser + " and " + ip, function () { var client; @@ -31,17 +30,10 @@ describe("The 'setex' method", function () { it('returns an error if no value is provided', function (done) { client.SETEX(["setex key", "100", undefined], helper.isError(done)); }); - + afterEach(function () { client.end(); }); }); - } - - ['javascript', 'hiredis'].forEach(function (parser) { - allTests(parser, "/tmp/redis.sock"); - ['IPv4', 'IPv6'].forEach(function (ip) { - allTests(parser, ip); - }) }); }); diff --git a/test/commands/setnx.spec.js b/test/commands/setnx.spec.js index 92f892516a2..9b363a32fd8 100644 --- a/test/commands/setnx.spec.js +++ b/test/commands/setnx.spec.js @@ -5,8 +5,7 @@ var redis = config.redis; describe("The 'setnx' method", function () { - function allTests(parser, ip) { - var args = config.configureClient(parser, ip); + helper.allTests(function(parser, ip, args) { describe("using " + parser + " and " + ip, function () { var client; @@ -35,12 +34,5 @@ describe("The 'setnx' method", function () { client.end(); }); }); - } - - ['javascript', 'hiredis'].forEach(function (parser) { - allTests(parser, "/tmp/redis.sock"); - ['IPv4', 'IPv6'].forEach(function (ip) { - allTests(parser, ip); - }) }); }); diff --git a/test/commands/sinter.spec.js b/test/commands/sinter.spec.js index c1b89051cf3..6bc87390bb4 100644 --- a/test/commands/sinter.spec.js +++ b/test/commands/sinter.spec.js @@ -5,8 +5,7 @@ var redis = config.redis; describe("The 'sinter' method", function () { - function allTests(parser, ip) { - var args = config.configureClient(parser, ip); + helper.allTests(function(parser, ip, args) { describe("using " + parser + " and " + ip, function () { var client; @@ -59,12 +58,5 @@ describe("The 'sinter' method", function () { client.end(); }); }); - } - - ['javascript', 'hiredis'].forEach(function (parser) { - allTests(parser, "/tmp/redis.sock"); - ['IPv4', 'IPv6'].forEach(function (ip) { - allTests(parser, ip); - }) }); }); diff --git a/test/commands/sinterstore.spec.js b/test/commands/sinterstore.spec.js index f27d47eea6a..7cf783bf25e 100644 --- a/test/commands/sinterstore.spec.js +++ b/test/commands/sinterstore.spec.js @@ -5,8 +5,7 @@ var redis = config.redis; describe("The 'sinterstore' method", function () { - function allTests(parser, ip) { - var args = config.configureClient(parser, ip); + helper.allTests(function(parser, ip, args) { describe("using " + parser + " and " + ip, function () { var client; @@ -44,12 +43,5 @@ describe("The 'sinterstore' method", function () { client.end(); }); }); - } - - ['javascript', 'hiredis'].forEach(function (parser) { - allTests(parser, "/tmp/redis.sock"); - ['IPv4', 'IPv6'].forEach(function (ip) { - allTests(parser, ip); - }) }); }); diff --git a/test/commands/sismember.spec.js b/test/commands/sismember.spec.js index 7277692c460..fdc00da1dad 100644 --- a/test/commands/sismember.spec.js +++ b/test/commands/sismember.spec.js @@ -5,8 +5,7 @@ var redis = config.redis; describe("The 'sismember' method", function () { - function allTests(parser, ip) { - var args = config.configureClient(parser, ip); + helper.allTests(function(parser, ip, args) { describe("using " + parser + " and " + ip, function () { var client; @@ -32,12 +31,5 @@ describe("The 'sismember' method", function () { client.end(); }); }); - } - - ['javascript', 'hiredis'].forEach(function (parser) { - allTests(parser, "/tmp/redis.sock"); - ['IPv4', 'IPv6'].forEach(function (ip) { - allTests(parser, ip); - }) }); }); diff --git a/test/commands/slowlog.spec.js b/test/commands/slowlog.spec.js index d09442d5291..b2d304911ba 100644 --- a/test/commands/slowlog.spec.js +++ b/test/commands/slowlog.spec.js @@ -5,8 +5,7 @@ var redis = config.redis; describe("The 'slowlog' method", function () { - function allTests(parser, ip) { - var args = config.configureClient(parser, ip); + helper.allTests(function(parser, ip, args) { describe("using " + parser + " and " + ip, function () { var client; @@ -37,12 +36,5 @@ describe("The 'slowlog' method", function () { client.end(); }); }); - } - - ['javascript', 'hiredis'].forEach(function (parser) { - allTests(parser, "/tmp/redis.sock"); - ['IPv4', 'IPv6'].forEach(function (ip) { - allTests(parser, ip); - }) }); }); diff --git a/test/commands/smembers.spec.js b/test/commands/smembers.spec.js index 0832742ec06..39bca826bc7 100644 --- a/test/commands/smembers.spec.js +++ b/test/commands/smembers.spec.js @@ -5,8 +5,7 @@ var redis = config.redis; describe("The 'smembers' method", function () { - function allTests(parser, ip) { - var args = config.configureClient(parser, ip); + helper.allTests(function(parser, ip, args) { describe("using " + parser + " and " + ip, function () { var client; @@ -34,12 +33,5 @@ describe("The 'smembers' method", function () { client.end(); }); }); - } - - ['javascript', 'hiredis'].forEach(function (parser) { - allTests(parser, "/tmp/redis.sock"); - ['IPv4', 'IPv6'].forEach(function (ip) { - allTests(parser, ip); - }) }); }); diff --git a/test/commands/smove.spec.js b/test/commands/smove.spec.js index 598be288609..d9ad3c40218 100644 --- a/test/commands/smove.spec.js +++ b/test/commands/smove.spec.js @@ -5,8 +5,7 @@ var redis = config.redis; describe("The 'smove' method", function () { - function allTests(parser, ip) { - var args = config.configureClient(parser, ip); + helper.allTests(function(parser, ip, args) { describe("using " + parser + " and " + ip, function () { var client; @@ -37,12 +36,5 @@ describe("The 'smove' method", function () { client.end(); }); }); - } - - ['javascript', 'hiredis'].forEach(function (parser) { - allTests(parser, "/tmp/redis.sock"); - ['IPv4', 'IPv6'].forEach(function (ip) { - allTests(parser, ip); - }) }); }); diff --git a/test/commands/sort.spec.js b/test/commands/sort.spec.js index b40b63cf324..c38dfd46850 100644 --- a/test/commands/sort.spec.js +++ b/test/commands/sort.spec.js @@ -5,8 +5,7 @@ var redis = config.redis; describe("The 'sort' method", function () { - function allTests(parser, ip) { - var args = config.configureClient(parser, ip); + helper.allTests(function(parser, ip, args) { describe("using " + parser + " and " + ip, function () { var client; @@ -90,7 +89,7 @@ describe("The 'sort' method", function () { client.end(); }); }); - } + }); function setupData(client, done) { client.rpush('y', 'd'); @@ -119,10 +118,4 @@ describe("The 'sort' method", function () { client.set('p9', 'tux', done); } - ['javascript', 'hiredis'].forEach(function (parser) { - allTests(parser, "/tmp/redis.sock"); - ['IPv4', 'IPv6'].forEach(function (ip) { - allTests(parser, ip); - }) - }); }); diff --git a/test/commands/spop.spec.js b/test/commands/spop.spec.js index 43b31442e26..75470d55620 100644 --- a/test/commands/spop.spec.js +++ b/test/commands/spop.spec.js @@ -5,8 +5,7 @@ var redis = config.redis; describe("The 'spop' method", function () { - function allTests(parser, ip) { - var args = config.configureClient(parser, ip); + helper.allTests(function(parser, ip, args) { describe("using " + parser + " and " + ip, function () { var client; @@ -34,12 +33,5 @@ describe("The 'spop' method", function () { client.end(); }); }); - } - - ['javascript', 'hiredis'].forEach(function (parser) { - allTests(parser, "/tmp/redis.sock"); - ['IPv4', 'IPv6'].forEach(function (ip) { - allTests(parser, ip); - }) }); }); diff --git a/test/commands/srem.spec.js b/test/commands/srem.spec.js index f0a140e39ac..af456abf103 100644 --- a/test/commands/srem.spec.js +++ b/test/commands/srem.spec.js @@ -5,8 +5,7 @@ var redis = config.redis; describe("The 'srem' method", function () { - function allTests(parser, ip) { - var args = config.configureClient(parser, ip); + helper.allTests(function(parser, ip, args) { describe("using " + parser + " and " + ip, function () { var client; @@ -55,12 +54,5 @@ describe("The 'srem' method", function () { client.end(); }); }); - } - - ['javascript', 'hiredis'].forEach(function (parser) { - allTests(parser, "/tmp/redis.sock"); - ['IPv4', 'IPv6'].forEach(function (ip) { - allTests(parser, ip); - }) }); }); diff --git a/test/commands/sunion.spec.js b/test/commands/sunion.spec.js index 92cd81d08f6..bf61c5e6804 100644 --- a/test/commands/sunion.spec.js +++ b/test/commands/sunion.spec.js @@ -5,8 +5,7 @@ var redis = config.redis; describe("The 'sunion' method", function () { - function allTests(parser, ip) { - var args = config.configureClient(parser, ip); + helper.allTests(function(parser, ip, args) { describe("using " + parser + " and " + ip, function () { var client; @@ -42,12 +41,5 @@ describe("The 'sunion' method", function () { client.end(); }); }); - } - - ['javascript', 'hiredis'].forEach(function (parser) { - allTests(parser, "/tmp/redis.sock"); - ['IPv4', 'IPv6'].forEach(function (ip) { - allTests(parser, ip); - }) }); }); diff --git a/test/commands/sunionstore.spec.js b/test/commands/sunionstore.spec.js index 702ad131c13..644bb8744b4 100644 --- a/test/commands/sunionstore.spec.js +++ b/test/commands/sunionstore.spec.js @@ -5,8 +5,7 @@ var redis = config.redis; describe("The 'sunionstore' method", function () { - function allTests(parser, ip) { - var args = config.configureClient(parser, ip); + helper.allTests(function(parser, ip, args) { describe("using " + parser + " and " + ip, function () { var client; @@ -45,12 +44,5 @@ describe("The 'sunionstore' method", function () { client.end(); }); }); - } - - ['javascript', 'hiredis'].forEach(function (parser) { - allTests(parser, "/tmp/redis.sock"); - ['IPv4', 'IPv6'].forEach(function (ip) { - allTests(parser, ip); - }) }); }); diff --git a/test/commands/ttl.spec.js b/test/commands/ttl.spec.js index cc93d231e12..c8988ab05b5 100644 --- a/test/commands/ttl.spec.js +++ b/test/commands/ttl.spec.js @@ -5,8 +5,7 @@ var redis = config.redis; describe("The 'ttl' method", function () { - function allTests(parser, ip) { - var args = config.configureClient(parser, ip); + helper.allTests(function(parser, ip, args) { describe("using " + parser + " and " + ip, function () { var client; @@ -34,12 +33,5 @@ describe("The 'ttl' method", function () { client.end(); }); }); - } - - ['javascript', 'hiredis'].forEach(function (parser) { - allTests(parser, "/tmp/redis.sock"); - ['IPv4', 'IPv6'].forEach(function (ip) { - allTests(parser, ip); - }) }); }); diff --git a/test/commands/type.spec.js b/test/commands/type.spec.js index 57aa745da11..5c438bbd17c 100644 --- a/test/commands/type.spec.js +++ b/test/commands/type.spec.js @@ -5,8 +5,7 @@ var redis = config.redis; describe("The 'type' method", function () { - function allTests(parser, ip) { - var args = config.configureClient(parser, ip); + helper.allTests(function(parser, ip, args) { describe("using " + parser + " and " + ip, function () { var client; @@ -52,12 +51,6 @@ describe("The 'type' method", function () { client.end(); }); }); - } - - ['javascript', 'hiredis'].forEach(function (parser) { - allTests(parser, "/tmp/redis.sock"); - ['IPv4', 'IPv6'].forEach(function (ip) { - allTests(parser, ip); - }) }); + }); diff --git a/test/commands/watch.spec.js b/test/commands/watch.spec.js index 8494d35dd58..a4abdce8777 100644 --- a/test/commands/watch.spec.js +++ b/test/commands/watch.spec.js @@ -5,9 +5,9 @@ var redis = config.redis; describe("The 'watch' method", function () { - function allTests(parser, ip) { - var args = config.configureClient(parser, ip); - var watched = 'foobar' + helper.allTests(function(parser, ip, args) { + + var watched = 'foobar'; describe("using " + parser + " and " + ip, function () { var client; @@ -57,12 +57,5 @@ describe("The 'watch' method", function () { }); }) }); - } - - ['javascript', 'hiredis'].forEach(function (parser) { - allTests(parser, "/tmp/redis.sock"); - ['IPv4', 'IPv6'].forEach(function (ip) { - allTests(parser, ip); - }) }); }); diff --git a/test/helper.js b/test/helper.js index 8aaa4d68d4a..74404b2630b 100644 --- a/test/helper.js +++ b/test/helper.js @@ -1,5 +1,6 @@ var assert = require("assert"); var path = require('path'); +var config = require("./lib/config"); var RedisProcess = require("./lib/redis-process"); var rp; @@ -89,6 +90,14 @@ module.exports = { if (version[i] < desired_version[i]) return false; } return true; + }, + allTests: function (cb) { + ['javascript', 'hiredis'].forEach(function (parser) { + cb(parser, "/tmp/redis.sock", config.configureClient(parser, "/tmp/redis.sock")); + ['IPv4', 'IPv6'].forEach(function (ip) { + cb(parser, ip, config.configureClient(parser, ip)); + }); + }); } } diff --git a/test/node_redis.spec.js b/test/node_redis.spec.js index 7e88886a307..580e3e127e1 100644 --- a/test/node_redis.spec.js +++ b/test/node_redis.spec.js @@ -7,8 +7,7 @@ var redis = config.redis; describe("The node_redis client", function () { - function allTests(parser, ip) { - var args = config.configureClient(parser, ip); + helper.allTests(function(parser, ip, args) { describe("using " + parser + " and " + ip, function () { var client; @@ -681,12 +680,5 @@ describe("The node_redis client", function () { }); }); - } - - ['javascript', 'hiredis'].forEach(function (parser) { - allTests(parser, "/tmp/redis.sock"); - ['IPv4', 'IPv6'].forEach(function (ip) { - allTests(parser, ip); - }); }); }); diff --git a/test/pubsub.spec.js b/test/pubsub.spec.js index 7678fd45bf8..3e6ad9ba687 100644 --- a/test/pubsub.spec.js +++ b/test/pubsub.spec.js @@ -5,8 +5,7 @@ var redis = config.redis; describe("publish/subscribe", function () { - function allTests(parser, ip) { - var args = config.configureClient(parser, ip); + helper.allTests(function(parser, ip, args) { describe("using " + parser + " and " + ip, function () { var pub = null; @@ -243,12 +242,5 @@ describe("publish/subscribe", function () { pub.end(); }); }); - } - - ['javascript', 'hiredis'].forEach(function (parser) { - allTests(parser, "/tmp/redis.sock"); - ['IPv4', 'IPv6'].forEach(function (ip) { - allTests(parser, ip); - }) }); }); From 37866b0445c16eee04615b8a4667ec4bf709dfa1 Mon Sep 17 00:00:00 2001 From: Ruben Bridgewater Date: Wed, 2 Sep 2015 18:06:51 +0200 Subject: [PATCH 0277/1748] Update readme --- README.md | 156 +++++++++++++++++++----------------------------------- 1 file changed, 55 insertions(+), 101 deletions(-) diff --git a/README.md b/README.md index c9900bbbef3..f3cc2745f52 100644 --- a/README.md +++ b/README.md @@ -4,9 +4,8 @@ redis - a node.js redis client [![Build Status](https://travis-ci.org/NodeRedis/node_redis.png)](https://travis-ci.org/NodeRedis/node_redis) [![Coverage Status](https://coveralls.io/repos/NodeRedis/node_redis/badge.svg?branch=)](https://coveralls.io/r/NodeRedis/node_redis?branch=) -This is a complete Redis client for node.js. It supports all Redis commands, -including many recently added commands like EVAL from experimental Redis server -branches. +This is a complete Redis client for node.js. It supports all Redis commands, +including many recently added commands. Install with: @@ -81,11 +80,11 @@ If the key is missing, reply will be null (probably): For a list of Redis commands, see [Redis Command Reference](http://redis.io/commands) -The commands can be specified in uppercase or lowercase for convenience. `client.get()` is the same as `client.GET()`. +The commands can be specified in uppercase or lowercase for convenience. `client.get()` is the same as `client.GET()`. -Minimal parsing is done on the replies. Commands that return a single line reply return JavaScript Strings, +Minimal parsing is done on the replies. Commands that return a single line reply return JavaScript Strings, integer replies return JavaScript Numbers, "bulk" replies return node Buffers, and "multi bulk" replies return a -JavaScript Array of node Buffers. `HGETALL` returns an Object with Buffers keyed by the hash keys. +JavaScript Array of node Buffers. `HGETALL` returns an Object with Buffers keyed by the hash keys. # API @@ -96,21 +95,21 @@ JavaScript Array of node Buffers. `HGETALL` returns an Object with Buffers keye ### "ready" `client` will emit `ready` once a connection is established to the Redis server and the server reports -that it is ready to receive commands. Commands issued before the `ready` event are queued, +that it is ready to receive commands. Commands issued before the `ready` event are queued, then replayed just before this event is emitted. ### "connect" `client` will emit `connect` at the same time as it emits `ready` unless `client.options.no_ready_check` -is set. If this options is set, `connect` will be emitted when the stream is connected, and then +is set. If this options is set, `connect` will be emitted when the stream is connected, and then you are free to try to send commands. ### "error" `client` will emit `error` when encountering an error connecting to the Redis server. -Note that "error" is a special event type in node. If there are no listeners for an -"error" event, node will exit. This is usually what you want, but it can lead to some +Note that "error" is a special event type in node. If there are no listeners for an +"error" event, node will exit. This is usually what you want, but it can lead to some cryptic error messages like this: mjr:~/work/node_redis (master)$ node example.js @@ -135,8 +134,8 @@ It would be nice to distinguish these two cases. ### "drain" `client` will emit `drain` when the TCP connection to the Redis server has been buffering, but is now -writable. This event can be used to stream commands in to Redis and adapt to backpressure. Right now, -you need to check `client.command_queue.length` to decide when to reduce your send rate. Then you can +writable. This event can be used to stream commands in to Redis and adapt to backpressure. Right now, +you need to check `client.command_queue.length` to decide when to reduce your send rate. Then you can resume sending when you get `drain`. ### "idle" @@ -155,22 +154,22 @@ port and host are probably fine and you don't need to supply any arguments. `cre `options` is an object with the following possible properties: -* `parser`: which Redis protocol reply parser to use. Defaults to `hiredis` if that module is installed. +* `parser`: which Redis protocol reply parser to use. Defaults to `hiredis` if that module is installed. This may also be set to `javascript`. -* `return_buffers`: defaults to `false`. If set to `true`, then all replies will be sent to callbacks as node Buffer +* `return_buffers`: defaults to `false`. If set to `true`, then all replies will be sent to callbacks as node Buffer objects instead of JavaScript Strings. * `detect_buffers`: default to `false`. If set to `true`, then replies will be sent to callbacks as node Buffer objects if any of the input arguments to the original command were Buffer objects. This option lets you switch between Buffers and Strings on a per-command basis, whereas `return_buffers` applies to every command on a client. * `socket_nodelay`: defaults to `true`. Whether to call setNoDelay() on the TCP stream, which disables the -Nagle algorithm on the underlying socket. Setting this option to `false` can result in additional throughput at the -cost of more latency. Most applications will want this set to `true`. +Nagle algorithm on the underlying socket. Setting this option to `false` can result in additional throughput at the +cost of more latency. Most applications will want this set to `true`. * `socket_keepalive` defaults to `true`. Whether the keep-alive functionality is enabled on the underlying socket. * `no_ready_check`: defaults to `false`. When a connection is established to the Redis server, the server might still -be loading the database from disk. While loading, the server not respond to any commands. To work around this, -`node_redis` has a "ready check" which sends the `INFO` command to the server. The response from the `INFO` command -indicates whether the server is ready for more commands. When ready, `node_redis` emits a `ready` event. +be loading the database from disk. While loading, the server not respond to any commands. To work around this, +`node_redis` has a "ready check" which sends the `INFO` command to the server. The response from the `INFO` command +indicates whether the server is ready for more commands. When ready, `node_redis` emits a `ready` event. Setting `no_ready_check` to `true` will inhibit this check. * `enable_offline_queue`: defaults to `true`. By default, if there is no active connection to the redis server, commands are added to a queue and are executed @@ -211,9 +210,9 @@ You can force an IPv6 if you set the family to 'IPv6'. See nodejs net or dns mod ## client.auth(password, callback) When connecting to Redis servers that require authentication, the `AUTH` command must be sent as the -first command after connecting. This can be tricky to coordinate with reconnections, the ready check, -etc. To make this easier, `client.auth()` stashes `password` and will send it after each connection, -including reconnections. `callback` is invoked only once, after the response to the very first +first command after connecting. This can be tricky to coordinate with reconnections, the ready check, +etc. To make this easier, `client.auth()` stashes `password` and will send it after each connection, +including reconnections. `callback` is invoked only once, after the response to the very first `AUTH` command sent. NOTE: Your call to `client.auth()` should not be inside the ready handler. If you are doing this wrong, `client` will emit an error that looks @@ -221,10 +220,10 @@ something like this `Error: Ready check failed: ERR operation not permitted`. ## client.end() -Forcibly close the connection to the Redis server. Note that this does not wait until all replies have been parsed. +Forcibly close the connection to the Redis server. Note that this does not wait until all replies have been parsed. If you want to exit cleanly, call `client.quit()` to send the `QUIT` command after you have handled all replies. -This example closes the connection to the Redis server before the replies have been read. You probably don't +This example closes the connection to the Redis server before the replies have been read. You probably don't want to do this: ```js @@ -268,7 +267,7 @@ When dealing with hash values, there are a couple of useful exceptions to this. ### client.hgetall(hash) -The reply from an HGETALL command will be converted into a JavaScript Object by `node_redis`. That way you can interact +The reply from an HGETALL command will be converted into a JavaScript Object by `node_redis`. That way you can interact with the responses using JavaScript syntax. Example: @@ -302,7 +301,7 @@ Multiple values may also be set by supplying a list: ## Publish / Subscribe -Here is a simple example of the API for publish / subscribe. This program opens two +Here is a simple example of the API for publish / subscribe. This program opens two client connections, subscribes to a channel on one of them, and publishes to that channel on the other: @@ -332,7 +331,7 @@ channel on the other: ``` When a client issues a `SUBSCRIBE` or `PSUBSCRIBE`, that connection is put into a "subscriber" mode. -At that point, only commands that modify the subscription set are valid. When the subscription +At that point, only commands that modify the subscription set are valid. When the subscription set is empty, the connection is put back into regular mode. If you need to send regular commands to Redis while in subscriber mode, just open another connection. @@ -354,30 +353,30 @@ name as `channel`, and the message Buffer as `message`. ### "subscribe" (channel, count) -Client will emit `subscribe` in response to a `SUBSCRIBE` command. Listeners are passed the +Client will emit `subscribe` in response to a `SUBSCRIBE` command. Listeners are passed the channel name as `channel` and the new count of subscriptions for this client as `count`. ### "psubscribe" (pattern, count) -Client will emit `psubscribe` in response to a `PSUBSCRIBE` command. Listeners are passed the +Client will emit `psubscribe` in response to a `PSUBSCRIBE` command. Listeners are passed the original pattern as `pattern`, and the new count of subscriptions for this client as `count`. ### "unsubscribe" (channel, count) -Client will emit `unsubscribe` in response to a `UNSUBSCRIBE` command. Listeners are passed the -channel name as `channel` and the new count of subscriptions for this client as `count`. When +Client will emit `unsubscribe` in response to a `UNSUBSCRIBE` command. Listeners are passed the +channel name as `channel` and the new count of subscriptions for this client as `count`. When `count` is 0, this client has left subscriber mode and no more subscriber events will be emitted. ### "punsubscribe" (pattern, count) -Client will emit `punsubscribe` in response to a `PUNSUBSCRIBE` command. Listeners are passed the -channel name as `channel` and the new count of subscriptions for this client as `count`. When +Client will emit `punsubscribe` in response to a `PUNSUBSCRIBE` command. Listeners are passed the +channel name as `channel` and the new count of subscriptions for this client as `count`. When `count` is 0, this client has left subscriber mode and no more subscriber events will be emitted. ## client.multi([commands]) `MULTI` commands are queued up until an `EXEC` is issued, and then all commands are run atomically by -Redis. The interface in `node_redis` is to return an individual `Multi` object by calling `client.multi()`. +Redis. The interface in `node_redis` is to return an individual `Multi` object by calling `client.multi()`. ```js var redis = require("./index"), @@ -411,8 +410,8 @@ Redis. The interface in `node_redis` is to return an individual `Multi` object ### Multi.exec( callback ) -`client.multi()` is a constructor that returns a `Multi` object. `Multi` objects share all of the -same command methods as `client` objects do. Commands are queued up inside the `Multi` object +`client.multi()` is a constructor that returns a `Multi` object. `Multi` objects share all of the +same command methods as `client` objects do. Commands are queued up inside the `Multi` object until `Multi.exec()` is invoked. The `callback` of `.exec()` will get invoked with two arguments: @@ -469,8 +468,8 @@ of commands and arguments to the constructor: Redis supports the `MONITOR` command, which lets you see all commands received by the Redis server across all client connections, including from other client libraries and other computers. -After you send the `MONITOR` command, no other commands are valid on that connection. `node_redis` -will emit a `monitor` event for every new monitor message that comes across. The callback for the +After you send the `MONITOR` command, no other commands are valid on that connection. `node_redis` +will emit a `monitor` event for every new monitor message that comes across. The callback for the `monitor` event takes a timestamp from the Redis server and an array of command arguments. Here is a simple example: @@ -506,7 +505,7 @@ The `versions` key contains an array of the elements of the version string for e ## redis.print() -A handy callback function for displaying return values when testing. Example: +A handy callback function for displaying return values when testing. Example: ```js var redis = require("redis"), @@ -566,11 +565,11 @@ the second word as first parameter: ## client.send_command(command_name, args, callback) -Used internally to send commands to Redis. For convenience, nearly all commands that are published on the Redis -Wiki have been added to the `client` object. However, if I missed any, or if new commands are introduced before +Used internally to send commands to Redis. For convenience, nearly all commands that are published on the Redis +Wiki have been added to the `client` object. However, if I missed any, or if new commands are introduced before this library is updated, you can use `send_command()` to send arbitrary commands to Redis. -All commands are sent as multi-bulk commands. `args` can either be an Array of arguments, or omitted. +All commands are sent as multi-bulk commands. `args` can either be an Array of arguments, or omitted. ## client.connected @@ -578,24 +577,24 @@ Boolean tracking the state of the connection to the Redis server. ## client.command_queue.length -The number of commands that have been sent to the Redis server but not yet replied to. You can use this to +The number of commands that have been sent to the Redis server but not yet replied to. You can use this to enforce some kind of maximum queue depth for commands while connected. Don't mess with `client.command_queue` though unless you really know what you are doing. ## client.offline_queue.length -The number of commands that have been queued up for a future connection. You can use this to enforce +The number of commands that have been queued up for a future connection. You can use this to enforce some kind of maximum queue depth for pre-connection commands. ## client.retry_delay -Current delay in milliseconds before a connection retry will be attempted. This starts at `250`. +Current delay in milliseconds before a connection retry will be attempted. This starts at `250`. ## client.retry_backoff -Multiplier for future retry timeouts. This should be larger than 1 to add more time between retries. -Defaults to 1.7. The default initial connection retry is 250, so the second retry will be 425, followed by 723.5, etc. +Multiplier for future retry timeouts. This should be larger than 1 to add more time between retries. +Defaults to 1.7. The default initial connection retry is 250, so the second retry will be 425, followed by 723.5, etc. ### Commands with Optional and Keyword arguments @@ -633,7 +632,7 @@ operations. As pipelining happens naturally from shared connections, overall efficiency goes up. Here are typical results of `multi_bench.js` which is similar to -`redis-benchmark` from the Redis distribution. It uses 5 concurrent connections +`redis-benchmark` from the Redis distribution. It uses 5 concurrent connections and shows the difference between pipelines of 1 and 50. JavaScript parser: @@ -682,7 +681,7 @@ If the `hiredis` npm module is installed, `node_redis` will use it by default. Otherwise, the pure JavaScript parser will be used. If you use `hiredis`, be sure to rebuild it whenever you upgrade your version of -node. There are mysterious failures that can happen between node and native +node. There are mysterious failures that can happen between node and native code modules after a node upgrade. Most users find that the JS parser is faster than the `hiredis` parser. Because @@ -727,65 +726,20 @@ hiredis parser: ## TODO -Better tests for auth, disconnect/reconnect, and all combinations thereof. - -Stream large set/get values into and out of Redis. Otherwise the entire value must be in node's memory. - -Performance can be better for very large values. - -I think there are more performance improvements left in there for smaller values, especially for large lists of small values. +1. 100% coverage +2. More performance improvements +3. Stream large set/get values into and out of Redis. Otherwise the entire value must be in node's memory. +4. Performance can be better for very large values in the js parser. ## How to Contribute -- open a pull request and then wait for feedback (if - [DTrejo](http://github.com/dtrejo) does not get back to you within 2 days, - comment again with indignation!) +- open a pull request and then wait for feedback ## Contributors -Many people have have added features and fixed bugs in `node_redis`. - -Ordered by date of first contribution. -[Auto-generated](http://github.com/dtrejo/node-authors) on Wed Jul 25 2012 19:14:59 GMT-0700 (PDT). - -- [Matt Ranney aka `mranney`](https://github.com/mranney) -- [Tim-Smart aka `tim-smart`](https://github.com/tim-smart) -- [Tj Holowaychuk aka `visionmedia`](https://github.com/visionmedia) -- [rick aka `technoweenie`](https://github.com/technoweenie) -- [Orion Henry aka `orionz`](https://github.com/orionz) -- [Aivo Paas aka `aivopaas`](https://github.com/aivopaas) -- [Hank Sims aka `hanksims`](https://github.com/hanksims) -- [Paul Carey aka `paulcarey`](https://github.com/paulcarey) -- [Pieter Noordhuis aka `pietern`](https://github.com/pietern) -- [nithesh aka `nithesh`](https://github.com/nithesh) -- [Andy Ray aka `andy2ray`](https://github.com/andy2ray) -- [unknown aka `unknowdna`](https://github.com/unknowdna) -- [Dave Hoover aka `redsquirrel`](https://github.com/redsquirrel) -- [Vladimir Dronnikov aka `dvv`](https://github.com/dvv) -- [Umair Siddique aka `umairsiddique`](https://github.com/umairsiddique) -- [Louis-Philippe Perron aka `lp`](https://github.com/lp) -- [Mark Dawson aka `markdaws`](https://github.com/markdaws) -- [Ian Babrou aka `bobrik`](https://github.com/bobrik) -- [Felix Geisendörfer aka `felixge`](https://github.com/felixge) -- [Jean-Hugues Pinson aka `undefined`](https://github.com/undefined) -- [Maksim Lin aka `maks`](https://github.com/maks) -- [Owen Smith aka `orls`](https://github.com/orls) -- [Zachary Scott aka `zzak`](https://github.com/zzak) -- [TEHEK Firefox aka `TEHEK`](https://github.com/TEHEK) -- [Isaac Z. Schlueter aka `isaacs`](https://github.com/isaacs) -- [David Trejo aka `DTrejo`](https://github.com/DTrejo) -- [Brian Noguchi aka `bnoguchi`](https://github.com/bnoguchi) -- [Philip Tellis aka `bluesmoon`](https://github.com/bluesmoon) -- [Marcus Westin aka `marcuswestin2`](https://github.com/marcuswestin2) -- [Jed Schmidt aka `jed`](https://github.com/jed) -- [Dave Peticolas aka `jdavisp3`](https://github.com/jdavisp3) -- [Trae Robrock aka `trobrock`](https://github.com/trobrock) -- [Shankar Karuppiah aka `shankar0306`](https://github.com/shankar0306) -- [Ignacio Burgueño aka `ignacio`](https://github.com/ignacio) - -Thanks. +Many people have have added features and fixed bugs in `node_redis`. Thanks to all of them! ## LICENSE - "MIT License" -Copyright (c) 2010 Matthew Ranney, http://ranney.com/ +Copyright (c) 2015 Matthew Ranney, http://ranney.com/ Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation From 4aeaf0e6b9e45fd73acf64c3637b8dc0294fcee3 Mon Sep 17 00:00:00 2001 From: Ruben Bridgewater Date: Wed, 2 Sep 2015 18:18:56 +0200 Subject: [PATCH 0278/1748] Remove dead code --- lib/parser/javascript.js | 4 ---- lib/queue.js | 5 +---- 2 files changed, 1 insertion(+), 8 deletions(-) diff --git a/lib/parser/javascript.js b/lib/parser/javascript.js index 5ad04362159..2060cbab8f5 100644 --- a/lib/parser/javascript.js +++ b/lib/parser/javascript.js @@ -290,10 +290,6 @@ ReplyParser.prototype._bytesRemaining = function () { return (this._buffer.length - this._offset) < 0 ? 0 : (this._buffer.length - this._offset); }; -ReplyParser.prototype.parser_error = function (message) { - this.emit("error", message); -}; - ReplyParser.prototype.send_error = function (reply) { this.emit("reply error", reply); }; diff --git a/lib/queue.js b/lib/queue.js index d2eb17e56cd..a6ac3b25845 100644 --- a/lib/queue.js +++ b/lib/queue.js @@ -55,7 +55,4 @@ Object.defineProperty(Queue.prototype, "length", { } }); - -if (typeof module !== "undefined" && module.exports) { - module.exports = Queue; -} +module.exports = Queue; From 1eb30add6672ae7773f9c8ddd0f3f2d239f4e615 Mon Sep 17 00:00:00 2001 From: Ruben Bridgewater Date: Wed, 2 Sep 2015 18:11:19 +0200 Subject: [PATCH 0279/1748] Use util.debuglog instead of using different indidivudal styles for debugging --- README.md | 30 -------- examples/eval.js | 2 - examples/psubscribe.js | 2 - examples/pub_sub.js | 2 - index.js | 143 +++++++++++---------------------------- lib/parser/hiredis.js | 1 - lib/parser/javascript.js | 2 - multi_bench.js | 2 - test/lib/config.js | 28 ++++---- test/lib/unref.js | 1 - 10 files changed, 52 insertions(+), 161 deletions(-) diff --git a/README.md b/README.md index f3ac542007b..98749228246 100644 --- a/README.md +++ b/README.md @@ -539,36 +539,6 @@ This will print: Note that this program will not exit cleanly because the client is still connected. -## redis.debug_mode - -Boolean to enable debug mode and protocol tracing. - -```js -var redis = require("redis"), - client = redis.createClient(); - -redis.debug_mode = true; - -client.on("connect", function () { - client.set("foo_rand000000000000", "some fantastic value"); -}); -``` - -This will display: - - mjr:~/work/node_redis (master)$ node ~/example.js - send command: *3 - $3 - SET - $20 - foo_rand000000000000 - $20 - some fantastic value - - on_data: +OK - -`send command` is data sent into Redis and `on_data` is data received from Redis. - ## Multi-word commands To execute redis multi-word commands like `SCRIPT LOAD` or `CLIENT LIST` pass diff --git a/examples/eval.js b/examples/eval.js index 0ec1c215e9a..fcdc99383d1 100644 --- a/examples/eval.js +++ b/examples/eval.js @@ -3,8 +3,6 @@ var redis = require("../index"), client = redis.createClient(); -redis.debug_mode = true; - client.eval("return 100.5", 0, function (err, res) { console.dir(err); console.dir(res); diff --git a/examples/psubscribe.js b/examples/psubscribe.js index f46d235bed4..4adb23c4df0 100644 --- a/examples/psubscribe.js +++ b/examples/psubscribe.js @@ -7,8 +7,6 @@ var redis = require("redis"), client4 = redis.createClient(), msg_count = 0; -redis.debug_mode = false; - client1.on("psubscribe", function (pattern, count) { console.log("client1 psubscribed to " + pattern + ", " + count + " total subscriptions"); client2.publish("channeltwo", "Me!"); diff --git a/examples/pub_sub.js b/examples/pub_sub.js index 3446a552d16..499b37e1bea 100644 --- a/examples/pub_sub.js +++ b/examples/pub_sub.js @@ -4,8 +4,6 @@ var redis = require("redis"), client1 = redis.createClient(), msg_count = 0, client2 = redis.createClient(); -redis.debug_mode = false; - // Most clients probably don't do much on "subscribe". This example uses it to coordinate things within one program. client1.on("subscribe", function (channel, count) { console.log("client1 subscribed to " + channel + ", " + count + " total subscriptions"); diff --git a/index.js b/index.js index 730372bb543..99f8c8aae3b 100644 --- a/index.js +++ b/index.js @@ -12,25 +12,15 @@ var net = require("net"), parsers = [], commands, connection_id = 0, default_port = 6379, - default_host = "127.0.0.1"; - -// can set this to true to enable for all connections -exports.debug_mode = false; - -var arraySlice = Array.prototype.slice; -function trace() { - if (!exports.debug_mode) return; - console.log.apply(null, arraySlice.call(arguments)); -} + default_host = "127.0.0.1", + debug = util.debuglog('redis'); // hiredis might not be installed try { require("./lib/parser/hiredis"); parsers.push(require("./lib/parser/hiredis")); } catch (err) { - if (exports.debug_mode) { - console.warn("hiredis parser not installed."); - } + debug("hiredis parser not installed."); } parsers.push(require("./lib/parser/javascript")); @@ -132,13 +122,13 @@ RedisClient.prototype.initialize_retry_vars = function () { }; RedisClient.prototype.unref = function () { - trace("User requesting to unref the connection"); + debug("User requesting to unref the connection"); if (this.connected) { - trace("unref'ing the socket connection"); + debug("unref'ing the socket connection"); this.stream.unref(); } else { - trace("Not connected yet, will unref later"); + debug("Not connected yet, will unref later"); this.once("connect", function () { this.unref(); }); @@ -187,9 +177,7 @@ RedisClient.prototype.on_error = function (msg) { var message = "Redis connection to " + this.address + " failed - " + msg; - if (exports.debug_mode) { - console.warn(message); - } + debug(message); this.flush_and_error(message); @@ -205,9 +193,8 @@ RedisClient.prototype.on_error = function (msg) { RedisClient.prototype.do_auth = function () { var self = this; - if (exports.debug_mode) { - console.log("Sending auth to " + self.address + " id " + self.connection_id); - } + debug("Sending auth to " + self.address + " id " + self.connection_id); + self.send_anyway = true; self.send_command("auth", [this.auth_pass], function (err, res) { if (err) { @@ -229,9 +216,9 @@ RedisClient.prototype.do_auth = function () { if (res.toString() !== "OK") { return self.emit("error", new Error("Auth failed: " + res.toString())); } - if (exports.debug_mode) { - console.log("Auth succeeded " + self.address + " id " + self.connection_id); - } + + debug("Auth succeeded " + self.address + " id " + self.connection_id); + if (self.auth_callback) { self.auth_callback(err, res); self.auth_callback = null; @@ -251,9 +238,7 @@ RedisClient.prototype.do_auth = function () { }; RedisClient.prototype.on_connect = function () { - if (exports.debug_mode) { - console.log("Stream connected " + this.address + " id " + this.connection_id); - } + debug("Stream connected " + this.address + " id " + this.connection_id); this.connected = true; this.ready = false; @@ -289,23 +274,17 @@ RedisClient.prototype.init_parser = function () { if (! parsers.some(function (parser) { if (parser.name === self.options.parser) { self.parser_module = parser; - if (exports.debug_mode) { - console.log("Using parser module: " + self.parser_module.name); - } + debug("Using parser module: " + self.parser_module.name); return true; } })) { throw new Error("Couldn't find named parser " + self.options.parser + " on this system"); } } else { - if (exports.debug_mode) { - console.log("Using default parser module: " + parsers[0].name); - } + debug("Using default parser module: " + parsers[0].name); this.parser_module = parsers[0]; } - this.parser_module.debug_mode = exports.debug_mode; - // return_buffers sends back Buffers from parser to callback. detect_buffers sends back Buffers from parser, but // converts to Strings if the input arguments are not Buffers. this.reply_parser = new this.parser_module.Parser({ @@ -361,9 +340,7 @@ RedisClient.prototype.on_ready = function () { }; Object.keys(this.subscription_set).forEach(function (key) { var parts = key.split(" "); - if (exports.debug_mode) { - console.warn("sending pub/sub on_ready " + parts[0] + ", " + parts[1]); - } + debug("sending pub/sub on_ready " + parts[0] + ", " + parts[1]); callback_count++; self.send_command(parts[0] + "scribe", [parts[1]], callback); }); @@ -403,18 +380,14 @@ RedisClient.prototype.on_info_cmd = function (err, res) { this.server_info = obj; if (!obj.loading || obj.loading === "0") { - if (exports.debug_mode) { - console.log("Redis server ready."); - } + debug("Redis server ready."); this.on_ready(); } else { retry_time = obj.loading_eta_seconds * 1000; if (retry_time > 1000) { retry_time = 1000; } - if (exports.debug_mode) { - console.log("Redis server still loading, trying again in " + retry_time); - } + debug("Redis server still loading, trying again in " + retry_time); setTimeout(function () { self.ready_check(); }, retry_time); @@ -424,9 +397,7 @@ RedisClient.prototype.on_info_cmd = function (err, res) { RedisClient.prototype.ready_check = function () { var self = this; - if (exports.debug_mode) { - console.log("checking server ready state..."); - } + debug("checking server ready state..."); this.send_anyway = true; // secret flag to send_command to send something even if not "ready" this.info(function (err, res) { @@ -440,9 +411,7 @@ RedisClient.prototype.send_offline_queue = function () { while (this.offline_queue.length > 0) { command_obj = this.offline_queue.shift(); - if (exports.debug_mode) { - console.log("Sending offline command: " + command_obj.command); - } + debug("Sending offline command: " + command_obj.command); buffered_writes += !this.send_command(command_obj.command, command_obj.args, command_obj.callback); } this.offline_queue = new Queue(); @@ -462,9 +431,7 @@ RedisClient.prototype.connection_gone = function (why) { return; } - if (exports.debug_mode) { - console.warn("Redis connection is gone from " + why + " event."); - } + debug("Redis connection is gone from " + why + " event."); this.connected = false; this.ready = false; @@ -491,9 +458,7 @@ RedisClient.prototype.connection_gone = function (why) { // If this is a requested shutdown, then don't retry if (this.closing) { this.retry_timer = null; - if (exports.debug_mode) { - console.warn("connection ended from quit command, not retrying."); - } + debug("connection ended from quit command, not retrying."); return; } @@ -504,9 +469,7 @@ RedisClient.prototype.connection_gone = function (why) { this.retry_delay = nextDelay; } - if (exports.debug_mode) { - console.log("Retry connection in " + this.retry_delay + " ms"); - } + debug("Retry connection in " + this.retry_delay + " ms"); if (this.max_attempts && this.attempts >= this.max_attempts) { this.retry_timer = null; @@ -522,9 +485,7 @@ RedisClient.prototype.connection_gone = function (why) { attempt: self.attempts }); this.retry_timer = setTimeout(function () { - if (exports.debug_mode) { - console.log("Retrying connection..."); - } + debug("Retrying connection..."); self.retry_totaltime += self.retry_delay; @@ -542,9 +503,7 @@ RedisClient.prototype.connection_gone = function (why) { }; RedisClient.prototype.on_data = function (data) { - if (exports.debug_mode) { - console.log("net read " + this.address + " id " + this.connection_id + ": " + data.toString()); - } + debug("net read " + this.address + " id " + this.connection_id + ": " + data.toString()); try { this.reply_parser.execute(data); @@ -655,7 +614,7 @@ RedisClient.prototype.return_reply = function (reply) { } if (this.pub_sub_mode && (type == 'message' || type == 'pmessage')) { - trace("received pubsub message"); + debug("received pubsub message"); } else { command_obj = this.command_queue.shift(); @@ -686,9 +645,7 @@ RedisClient.prototype.return_reply = function (reply) { } try_callback(command_obj.callback, reply); - } else if (exports.debug_mode) { - console.log("no callback for reply: " + (reply && reply.toString && reply.toString())); - } + } else debug("no callback for reply: " + (reply && reply.toString && reply.toString())); } else if (this.pub_sub_mode || (command_obj && command_obj.sub_command)) { if (Array.isArray(reply)) { type = reply[0].toString(); @@ -700,9 +657,7 @@ RedisClient.prototype.return_reply = function (reply) { } else if (type === "subscribe" || type === "unsubscribe" || type === "psubscribe" || type === "punsubscribe") { if (reply[2] === 0) { this.pub_sub_mode = false; - if (this.debug_mode) { - console.log("All subscriptions removed, exiting pub/sub mode"); - } + debug("All subscriptions removed, exiting pub/sub mode"); } else { this.pub_sub_mode = true; } @@ -803,16 +758,12 @@ RedisClient.prototype.send_command = function (command, args, callback) { command_obj = new Command(command, args, false, buffer_args, callback); if ((!this.ready && !this.send_anyway) || !stream.writable) { - if (exports.debug_mode) { - if (!stream.writable) { - console.log("send command: stream is not writeable."); - } + if (!stream.writable) { + debug("send command: stream is not writeable."); } if (this.enable_offline_queue) { - if (exports.debug_mode) { - console.log("Queueing " + command + " for next server connection."); - } + debug("Queueing " + command + " for next server connection."); this.offline_queue.push(command_obj); this.should_buffer = true; } else { @@ -854,14 +805,10 @@ RedisClient.prototype.send_command = function (command, args, callback) { } command_str += "$" + Buffer.byteLength(arg) + "\r\n" + arg + "\r\n"; } - if (exports.debug_mode) { - console.log("send " + this.address + " id " + this.connection_id + ": " + command_str); - } + debug("send " + this.address + " id " + this.connection_id + ": " + command_str); buffered_writes += !stream.write(command_str); } else { - if (exports.debug_mode) { - console.log("send command (" + command_str + ") has Buffer arguments"); - } + debug("send command (" + command_str + ") has Buffer arguments"); buffered_writes += !stream.write(command_str); for (i = 0, il = args.length, arg; i < il; i += 1) { @@ -872,29 +819,21 @@ RedisClient.prototype.send_command = function (command, args, callback) { if (Buffer.isBuffer(arg)) { if (arg.length === 0) { - if (exports.debug_mode) { - console.log("send_command: using empty string for 0 length buffer"); - } + debug("send_command: using empty string for 0 length buffer"); buffered_writes += !stream.write("$0\r\n\r\n"); } else { buffered_writes += !stream.write("$" + arg.length + "\r\n"); buffered_writes += !stream.write(arg); buffered_writes += !stream.write("\r\n"); - if (exports.debug_mode) { - console.log("send_command: buffer send " + arg.length + " bytes"); - } + debug("send_command: buffer send " + arg.length + " bytes"); } } else { - if (exports.debug_mode) { - console.log("send_command: string send " + Buffer.byteLength(arg) + " bytes: " + arg); - } + debug("send_command: string send " + Buffer.byteLength(arg) + " bytes: " + arg); buffered_writes += !stream.write("$" + Buffer.byteLength(arg) + "\r\n" + arg + "\r\n"); } } } - if (exports.debug_mode) { - console.log("send_command buffered_writes: " + buffered_writes, " should_buffer: " + this.should_buffer); - } + debug("send_command buffered_writes: " + buffered_writes, " should_buffer: " + this.should_buffer); if (buffered_writes || this.command_queue.getLength() >= this.command_queue_high_water) { this.should_buffer = true; } @@ -904,8 +843,8 @@ RedisClient.prototype.send_command = function (command, args, callback) { RedisClient.prototype.pub_sub_command = function (command_obj) { var i, key, command, args; - if (this.pub_sub_mode === false && exports.debug_mode) { - console.log("Entering pub/sub mode from " + command_obj.command); + if (this.pub_sub_mode === false) { + debug("Entering pub/sub mode from " + command_obj.command); } this.pub_sub_mode = true; command_obj.sub_command = true; @@ -1025,9 +964,7 @@ RedisClient.prototype.auth = function () { var args = to_array(arguments); this.auth_pass = args[0]; this.auth_callback = args[1]; - if (exports.debug_mode) { - console.log("Saving auth as " + this.auth_pass); - } + debug("Saving auth as " + this.auth_pass); if (this.connected) { this.send_command("auth", args); diff --git a/lib/parser/hiredis.js b/lib/parser/hiredis.js index 29dbfd59a95..f588bcc1850 100644 --- a/lib/parser/hiredis.js +++ b/lib/parser/hiredis.js @@ -4,7 +4,6 @@ var events = require("events"), util = require("../util"), hiredis = require("hiredis"); -exports.debug_mode = false; exports.name = "hiredis"; function HiredisReplyParser(options) { diff --git a/lib/parser/javascript.js b/lib/parser/javascript.js index 2060cbab8f5..15f83909004 100644 --- a/lib/parser/javascript.js +++ b/lib/parser/javascript.js @@ -9,7 +9,6 @@ function Packet(type, size) { } exports.name = "javascript"; -exports.debug_mode = false; function ReplyParser(options) { this.name = exports.name; @@ -18,7 +17,6 @@ function ReplyParser(options) { this._buffer = null; this._offset = 0; this._encoding = "utf-8"; - this._debug_mode = options.debug_mode; this._reply_type = null; } diff --git a/multi_bench.js b/multi_bench.js index 9f210a76d4e..dd2e99fe1b8 100644 --- a/multi_bench.js +++ b/multi_bench.js @@ -11,8 +11,6 @@ var redis = require("./index"), }, small_str, large_str, small_buf, large_buf, very_large_str, very_large_buf; -redis.debug_mode = false; - function lpad(input, len, chr) { var str = input.toString(); chr = chr || " "; diff --git a/test/lib/config.js b/test/lib/config.js index 0ddef514473..54c6ed27bb6 100644 --- a/test/lib/config.js +++ b/test/lib/config.js @@ -1,19 +1,15 @@ // helpers for configuring a redis client in // its various modes, ipV6, ipV4, socket. -module.exports = (function () { - var redis = require('../../index'); - redis.debug_mode = process.env.DEBUG ? JSON.parse(process.env.DEBUG) : false; +var redis = require('../../index'); - var config = { - redis: redis, - PORT: 6378, - HOST: { - IPv4: "127.0.0.1", - IPv6: "::1" - } - }; - - config.configureClient = function (parser, ip, opts) { +var config = { + redis: redis, + PORT: 6378, + HOST: { + IPv4: "127.0.0.1", + IPv6: "::1" + }, + configureClient: function (parser, ip, opts) { var args = []; opts = opts || {}; @@ -29,7 +25,7 @@ module.exports = (function () { args.push(opts); return args; - }; + } +}; - return config; -})(); +module.exports = config; diff --git a/test/lib/unref.js b/test/lib/unref.js index 4ccf51f203c..46b03643a38 100644 --- a/test/lib/unref.js +++ b/test/lib/unref.js @@ -4,7 +4,6 @@ 'use strict'; var redis = require("../../"); -//redis.debug_mode = true var HOST = process.argv[2] || '127.0.0.1'; var PORT = process.argv[3] var args = PORT ? [PORT, HOST] : [HOST] From e0d0649c60e9363fc65787b11ada174e17b870c4 Mon Sep 17 00:00:00 2001 From: Ruben Bridgewater Date: Wed, 2 Sep 2015 21:19:38 +0200 Subject: [PATCH 0280/1748] Add fallback for node 0.10 Improve the fallback mode --- index.js | 9 +++++++++ 1 file changed, 9 insertions(+) diff --git a/index.js b/index.js index 99f8c8aae3b..b87850b9eaa 100644 --- a/index.js +++ b/index.js @@ -13,7 +13,16 @@ var net = require("net"), connection_id = 0, default_port = 6379, default_host = "127.0.0.1", + debug; + +/* istanbul ignore next */ +if (util.debuglog) { debug = util.debuglog('redis'); +} else if (/\bredis\b/i.test(process.env.NODE_DEBUG)) { + debug = console.error; +} else { + debug = function() {}; +} // hiredis might not be installed try { From 52db91c7530cbca333fb47a9f8d2019856f15b63 Mon Sep 17 00:00:00 2001 From: Ruben Bridgewater Date: Wed, 2 Sep 2015 00:43:08 +0200 Subject: [PATCH 0281/1748] Fix js parser sending non-Errors --- index.js | 10 ++++------ lib/parser/javascript.js | 3 +-- 2 files changed, 5 insertions(+), 8 deletions(-) diff --git a/index.js b/index.js index b87850b9eaa..dffdbbb9e2b 100644 --- a/index.js +++ b/index.js @@ -302,11 +302,7 @@ RedisClient.prototype.init_parser = function () { // "reply error" is an error sent back by Redis this.reply_parser.on("reply error", function (reply) { - if (reply instanceof Error) { - self.return_error(reply); - } else { - self.return_error(new Error(reply)); - } + self.return_error(reply); }); this.reply_parser.on("reply", function (reply) { self.return_reply(reply); @@ -654,7 +650,9 @@ RedisClient.prototype.return_reply = function (reply) { } try_callback(command_obj.callback, reply); - } else debug("no callback for reply: " + (reply && reply.toString && reply.toString())); + } else { + debug("no callback for reply: " + (reply && reply.toString && reply.toString())); + } } else if (this.pub_sub_mode || (command_obj && command_obj.sub_command)) { if (Array.isArray(reply)) { type = reply[0].toString(); diff --git a/lib/parser/javascript.js b/lib/parser/javascript.js index 15f83909004..bea4b68d9b3 100644 --- a/lib/parser/javascript.js +++ b/lib/parser/javascript.js @@ -177,8 +177,7 @@ ReplyParser.prototype.execute = function (buffer) { if (ret === null) { break; } - - this.send_error(ret); + this.send_error(new Error(ret)); } else if (type === 58) { // : ret = this._parseResult(type); From 19db6d1dadca0539cf2d2b1cd3038b313ddce46d Mon Sep 17 00:00:00 2001 From: Ruben Bridgewater Date: Wed, 2 Sep 2015 00:43:58 +0200 Subject: [PATCH 0282/1748] Remove lib/util.js --- index.js | 2 +- lib/parser/hiredis.js | 2 +- lib/parser/javascript.js | 2 +- lib/util.js | 13 ------------- 4 files changed, 3 insertions(+), 16 deletions(-) delete mode 100644 lib/util.js diff --git a/index.js b/index.js index dffdbbb9e2b..11e6a77cb2a 100644 --- a/index.js +++ b/index.js @@ -4,7 +4,7 @@ var net = require("net"), URL = require("url"), - util = require("./lib/util"), + util = require("util"), Queue = require("./lib/queue"), to_array = require("./lib/to_array"), events = require("events"), diff --git a/lib/parser/hiredis.js b/lib/parser/hiredis.js index f588bcc1850..d1fd0b7a9a5 100644 --- a/lib/parser/hiredis.js +++ b/lib/parser/hiredis.js @@ -1,7 +1,7 @@ 'use strict'; var events = require("events"), - util = require("../util"), + util = require("util"), hiredis = require("hiredis"); exports.name = "hiredis"; diff --git a/lib/parser/javascript.js b/lib/parser/javascript.js index bea4b68d9b3..be5248d1155 100644 --- a/lib/parser/javascript.js +++ b/lib/parser/javascript.js @@ -1,7 +1,7 @@ 'use strict'; var events = require("events"), - util = require("../util"); + util = require("util"); function Packet(type, size) { this.type = type; diff --git a/lib/util.js b/lib/util.js deleted file mode 100644 index 359cd7e9481..00000000000 --- a/lib/util.js +++ /dev/null @@ -1,13 +0,0 @@ -'use strict'; - -// Support for very old versions of node where the module was called "sys". At some point, we should abandon this. - -var util; - -try { - util = require("util"); -} catch (err) { - util = require("sys"); -} - -module.exports = util; From 62041c519f33b3d0edfa7655ddb9f3b6190a0f31 Mon Sep 17 00:00:00 2001 From: Ruben Bridgewater Date: Wed, 2 Sep 2015 00:44:17 +0200 Subject: [PATCH 0283/1748] Test for statements instead of matching them if test is enough precompile regex --- index.js | 7 +++++-- 1 file changed, 5 insertions(+), 2 deletions(-) diff --git a/index.js b/index.js index 11e6a77cb2a..50da18c91a2 100644 --- a/index.js +++ b/index.js @@ -199,6 +199,9 @@ RedisClient.prototype.on_error = function (msg) { this.connection_gone("error"); }; +var noPasswordIsSet = /no password is set/; +var loading = /LOADING/; + RedisClient.prototype.do_auth = function () { var self = this; @@ -207,14 +210,14 @@ RedisClient.prototype.do_auth = function () { self.send_anyway = true; self.send_command("auth", [this.auth_pass], function (err, res) { if (err) { - if (err.toString().match("LOADING")) { + if (loading.test(err.message)) { // if redis is still loading the db, it will not authenticate and everything else will fail console.log("Redis still loading, trying to authenticate later"); setTimeout(function () { self.do_auth(); }, 2000); // TODO - magic number alert return; - } else if (err.toString().match("no password is set")) { + } else if (noPasswordIsSet.test(err.message)) { console.log("Warning: Redis server does not require a password, but a password was supplied."); err = null; res = "OK"; From b1e51c8c7bd541d7ad7b2231cac67d0d7c7e599a Mon Sep 17 00:00:00 2001 From: Bryce Baril Date: Thu, 3 Sep 2015 10:23:45 -0700 Subject: [PATCH 0284/1748] Update repository location to new GitHub org --- package.json | 10 +++++++++- 1 file changed, 9 insertions(+), 1 deletion(-) diff --git a/package.json b/package.json index b4452bb1d35..6e322a046ab 100644 --- a/package.json +++ b/package.json @@ -27,6 +27,14 @@ }, "repository": { "type": "git", - "url": "git://github.com/mranney/node_redis.git" + "url": "git://github.com/NodeRedis/node_redis.git" + }, + "bugs": { + "url": "https://github.com/NodeRedis/node_redis/issues" + }, + "homepage": "https://github.com/NodeRedis/node_redis", + "directories": { + "example": "examples", + "test": "test" } } From 89e1f6f06709bfee8e59493cdd026ceb78df47b6 Mon Sep 17 00:00:00 2001 From: Ruben Bridgewater Date: Thu, 3 Sep 2015 21:54:57 +0200 Subject: [PATCH 0285/1748] Improve code coverage by adding tests and removing unnecessary code --- index.js | 107 +++++++++++++++++------------------ lib/parser/hiredis.js | 9 +-- lib/parser/javascript.js | 2 +- test/commands/hmget.spec.js | 8 +++ test/commands/hmset.spec.js | 13 ++++- test/commands/mset.spec.js | 10 +--- test/commands/select.spec.js | 8 +-- test/commands/set.spec.js | 10 +--- test/helper.js | 17 ++++-- test/node_redis.spec.js | 77 ++++++++++++++++++++++++- 10 files changed, 166 insertions(+), 95 deletions(-) diff --git a/index.js b/index.js index 50da18c91a2..d8f1bfaa059 100644 --- a/index.js +++ b/index.js @@ -29,14 +29,17 @@ try { require("./lib/parser/hiredis"); parsers.push(require("./lib/parser/hiredis")); } catch (err) { + /* istanbul ignore next: won't be reached with tests */ debug("hiredis parser not installed."); } parsers.push(require("./lib/parser/javascript")); function RedisClient(stream, options) { + options = options || {}; + this.stream = stream; - this.options = options = options || {}; + this.options = options; this.connection_id = ++connection_id; this.connected = false; @@ -51,26 +54,23 @@ function RedisClient(stream, options) { this.should_buffer = false; this.command_queue_high_water = this.options.command_queue_high_water || 1000; this.command_queue_low_water = this.options.command_queue_low_water || 0; - this.max_attempts = null; - if (options.max_attempts && !isNaN(options.max_attempts) && options.max_attempts > 0) { + if (options.max_attempts && options.max_attempts > 0) { this.max_attempts = +options.max_attempts; } this.command_queue = new Queue(); // holds sent commands to de-pipeline them this.offline_queue = new Queue(); // holds commands issued but not able to be sent this.commands_sent = 0; - this.connect_timeout = false; - if (options.connect_timeout && !isNaN(options.connect_timeout) && options.connect_timeout > 0) { + if (options.connect_timeout && options.connect_timeout > 0) { this.connect_timeout = +options.connect_timeout; } this.enable_offline_queue = true; - if (typeof this.options.enable_offline_queue === "boolean") { - this.enable_offline_queue = this.options.enable_offline_queue; + if (this.options.enable_offline_queue === false) { + this.enable_offline_queue = false; } this.retry_max_delay = null; - if (options.retry_max_delay !== undefined && !isNaN(options.retry_max_delay) && options.retry_max_delay > 0) { - this.retry_max_delay = options.retry_max_delay; + if (options.retry_max_delay && options.retry_max_delay > 0) { + this.retry_max_delay = +options.retry_max_delay; } - this.initialize_retry_vars(); this.pub_sub_mode = false; this.subscription_set = {}; @@ -131,12 +131,10 @@ RedisClient.prototype.initialize_retry_vars = function () { }; RedisClient.prototype.unref = function () { - debug("User requesting to unref the connection"); if (this.connected) { debug("unref'ing the socket connection"); this.stream.unref(); - } - else { + } else { debug("Not connected yet, will unref later"); this.once("connect", function () { this.unref(); @@ -210,23 +208,26 @@ RedisClient.prototype.do_auth = function () { self.send_anyway = true; self.send_command("auth", [this.auth_pass], function (err, res) { if (err) { + /* istanbul ignore if: this is almost impossible to test */ if (loading.test(err.message)) { // if redis is still loading the db, it will not authenticate and everything else will fail - console.log("Redis still loading, trying to authenticate later"); + debug("Redis still loading, trying to authenticate later"); setTimeout(function () { self.do_auth(); }, 2000); // TODO - magic number alert return; } else if (noPasswordIsSet.test(err.message)) { - console.log("Warning: Redis server does not require a password, but a password was supplied."); + debug("Warning: Redis server does not require a password, but a password was supplied."); err = null; res = "OK"; } else { return self.emit("error", new Error("Auth error: " + err.message)); } } - if (res.toString() !== "OK") { - return self.emit("error", new Error("Auth failed: " + res.toString())); + + res = res.toString(); + if (res !== "OK") { + return self.emit("error", new Error("Auth failed: " + res)); } debug("Auth succeeded " + self.address + " id " + self.connection_id); @@ -283,7 +284,7 @@ RedisClient.prototype.init_parser = function () { var self = this; if (this.options.parser) { - if (! parsers.some(function (parser) { + if (!parsers.some(function (parser) { if (parser.name === self.options.parser) { self.parser_module = parser; debug("Using parser module: " + self.parser_module.name); @@ -353,7 +354,9 @@ RedisClient.prototype.on_ready = function () { self.send_command(parts[0] + "scribe", [parts[1]], callback); }); return; - } else if (this.monitoring) { + } + + if (this.monitoring) { this.send_command("monitor", []); } else { this.send_offline_queue(); @@ -378,7 +381,7 @@ RedisClient.prototype.on_info_cmd = function (err, res) { }); obj.versions = []; - if( obj.redis_version ){ + if (obj.redis_version) { obj.redis_version.split('.').forEach(function (num) { obj.versions.push(+num); }); @@ -483,7 +486,7 @@ RedisClient.prototype.connection_gone = function (why) { this.retry_timer = null; // TODO - some people need a "Redis is Broken mode" for future commands that errors immediately, and others // want the program to exit. Right now, we just log, which doesn't really help in either case. - console.error("node_redis: Couldn't get Redis connection after " + this.max_attempts + " attempts."); + debug("node_redis: Couldn't get Redis connection after " + this.max_attempts + " attempts."); return; } @@ -500,7 +503,7 @@ RedisClient.prototype.connection_gone = function (why) { if (self.connect_timeout && self.retry_totaltime >= self.connect_timeout) { self.retry_timer = null; // TODO - engage Redis is Broken mode for future commands, or whatever - console.error("node_redis: Couldn't get Redis connection after " + self.retry_totaltime + "ms."); + debug("node_redis: Couldn't get Redis connection after " + self.retry_totaltime + "ms."); return; } @@ -525,7 +528,7 @@ RedisClient.prototype.on_data = function (data) { }; RedisClient.prototype.return_error = function (err) { - var command_obj = this.command_queue.shift(), queue_len = this.command_queue.getLength(); + var command_obj = this.command_queue.shift(), queue_len = this.command_queue.length; if (this.pub_sub_mode === false && queue_len === 0) { this.command_queue = new Queue(); @@ -536,20 +539,12 @@ RedisClient.prototype.return_error = function (err) { this.should_buffer = false; } - if (command_obj && typeof command_obj.callback === "function") { - try { - command_obj.callback(err); - } catch (callback_err) { - // if a callback throws an exception, re-throw it on a new stack so the parser can keep going - process.nextTick(function () { - throw callback_err; - }); - } - } else { - console.log("node_redis: no callback to send error: " + err.message); - // this will probably not make it anywhere useful, but we might as well throw + try { + command_obj.callback(err); + } catch (callback_err) { + // if a callback throws an exception, re-throw it on a new stack so the parser can keep going process.nextTick(function () { - throw err; + throw callback_err; }); } }; @@ -628,7 +623,7 @@ RedisClient.prototype.return_reply = function (reply) { command_obj = this.command_queue.shift(); } - queue_len = this.command_queue.getLength(); + queue_len = this.command_queue.length; if (this.pub_sub_mode === false && queue_len === 0) { this.command_queue = new Queue(); // explicitly reclaim storage from old Queue @@ -720,7 +715,7 @@ RedisClient.prototype.send_command = function (command, args, callback) { // probably the fastest way: // client.command([arg1, arg2], cb); (straight passthrough) // send_command(command, [arg1, arg2], cb); - } else if (! callback) { + } else if (!callback) { // most people find this variable argument length form more convenient, but it uses arguments, which is slower // client.command(arg1, arg2, cb); (wraps up arguments into an array) // send_command(command, [arg1, arg2, cb]); @@ -739,7 +734,9 @@ RedisClient.prototype.send_command = function (command, args, callback) { throw new Error("send_command: second argument must be an array"); } - if (callback && process.domain) callback = process.domain.bind(callback); + if (callback && process.domain) { + callback = process.domain.bind(callback); + } // if the last argument is an array and command is sadd or srem, expand it out: // client.sadd(arg1, [arg2, arg3, arg4], cb); @@ -844,7 +841,7 @@ RedisClient.prototype.send_command = function (command, args, callback) { } } debug("send_command buffered_writes: " + buffered_writes, " should_buffer: " + this.should_buffer); - if (buffered_writes || this.command_queue.getLength() >= this.command_queue_high_water) { + if (buffered_writes || this.command_queue.length >= this.command_queue_high_water) { this.should_buffer = true; } return !this.should_buffer; @@ -1141,9 +1138,7 @@ Multi.prototype.EXEC = Multi.prototype.exec; RedisClient.prototype.multi = function (args) { return new Multi(this, args); }; -RedisClient.prototype.MULTI = function (args) { - return new Multi(this, args); -}; +RedisClient.prototype.MULTI = RedisClient.prototype.multi; // stash original eval method @@ -1177,26 +1172,26 @@ RedisClient.prototype.eval = RedisClient.prototype.EVAL = function () { }); }; -exports.createClient = function(arg0, arg1, options) { - if (typeof arg0 === 'object' || arg0 === undefined) { - options = arg0 || options; +exports.createClient = function(port_arg, host_arg, options) { + if (typeof port_arg === 'object' || port_arg === undefined) { + options = port_arg || options; return createClient_tcp(default_port, default_host, options); } - if (typeof arg0 === 'number' || typeof arg0 === 'string' && arg0.match(/^\d+$/)){ - return createClient_tcp(arg0, arg1, options); + if (typeof port_arg === 'number' || typeof port_arg === 'string' && /^\d+$/.test(port_arg)) { + return createClient_tcp(port_arg, host_arg, options); } - if (typeof arg0 === 'string') { - options = arg1 || {}; + if (typeof port_arg === 'string') { + options = host_arg || {}; - var parsed = URL.parse(arg0, true, true); + var parsed = URL.parse(port_arg, true, true); if (parsed.hostname) { if (parsed.auth) { options.auth_pass = parsed.auth.split(':')[1]; } - return createClient_tcp((parsed.port || default_port), parsed.hostname, options); + return createClient_tcp(parsed.port, parsed.hostname, options); } - return createClient_unix(arg0, options); + return createClient_unix(port_arg, options); } throw new Error('unknown type of connection in createClient()'); }; @@ -1206,7 +1201,7 @@ var createClient_unix = function(path, options){ path: path }; var net_client = net.createConnection(cnxOptions); - var redis_client = new RedisClient(net_client, options || {}); + var redis_client = new RedisClient(net_client, options); redis_client.connectionOption = cnxOptions; redis_client.address = path; @@ -1218,10 +1213,10 @@ var createClient_tcp = function (port_arg, host_arg, options) { var cnxOptions = { 'port' : port_arg || default_port, 'host' : host_arg || default_host, - 'family' : (options && options.family === 'IPv6') ? 6 : 4 + 'family' : options && options.family === 'IPv6' ? 6 : 4 }; var net_client = net.createConnection(cnxOptions); - var redis_client = new RedisClient(net_client, options || {}); + var redis_client = new RedisClient(net_client, options); redis_client.connectionOption = cnxOptions; redis_client.address = cnxOptions.host + ':' + cnxOptions.port; diff --git a/lib/parser/hiredis.js b/lib/parser/hiredis.js index d1fd0b7a9a5..97ece3b08e5 100644 --- a/lib/parser/hiredis.js +++ b/lib/parser/hiredis.js @@ -8,7 +8,7 @@ exports.name = "hiredis"; function HiredisReplyParser(options) { this.name = exports.name; - this.options = options || {}; + this.options = options; this.reset(); events.EventEmitter.call(this); } @@ -27,12 +27,7 @@ HiredisReplyParser.prototype.execute = function (data) { var reply; this.reader.feed(data); while (true) { - try { - reply = this.reader.get(); - } catch (err) { - this.emit("error", err); - break; - } + reply = this.reader.get(); if (reply === undefined) { break; diff --git a/lib/parser/javascript.js b/lib/parser/javascript.js index be5248d1155..5772ad79900 100644 --- a/lib/parser/javascript.js +++ b/lib/parser/javascript.js @@ -12,7 +12,7 @@ exports.name = "javascript"; function ReplyParser(options) { this.name = exports.name; - this.options = options || { }; + this.options = options; this._buffer = null; this._offset = 0; diff --git a/test/commands/hmget.spec.js b/test/commands/hmget.spec.js index 3ad8fa9128c..6ab175a9145 100644 --- a/test/commands/hmget.spec.js +++ b/test/commands/hmget.spec.js @@ -37,6 +37,14 @@ describe("The 'hmget' method", function () { }); }); + it('allows keys to be specified by passing an array as first argument', function (done) { + client.HMGET([hash, "0123456789", "some manner of key"], function (err, reply) { + assert.strictEqual("abcdefghij", reply[0].toString()); + assert.strictEqual("a type of value", reply[1].toString()); + return done(err); + }); + }); + it('allows a single key to be specified in an array', function (done) { client.HMGET(hash, ["0123456789"], function (err, reply) { assert.strictEqual("abcdefghij", reply[0].toString()); diff --git a/test/commands/hmset.spec.js b/test/commands/hmset.spec.js index e30669d00e5..94070c0dc0d 100644 --- a/test/commands/hmset.spec.js +++ b/test/commands/hmset.spec.js @@ -20,7 +20,7 @@ describe("The 'hmset' method", function () { }); it('handles redis-style syntax', function (done) { - client.HMSET(hash, "0123456789", "abcdefghij", "some manner of key", "a type of value", helper.isString('OK')); + client.HMSET(hash, "0123456789", "abcdefghij", "some manner of key", "a type of value", "otherTypes", 555, helper.isString('OK')); client.HGETALL(hash, function (err, obj) { assert.equal(obj['0123456789'], 'abcdefghij'); assert.equal(obj['some manner of key'], 'a type of value'); @@ -29,7 +29,7 @@ describe("The 'hmset' method", function () { }); it('handles object-style syntax', function (done) { - client.HMSET(hash, {"0123456789": "abcdefghij", "some manner of key": "a type of value"}, helper.isString('OK')); + client.HMSET(hash, {"0123456789": "abcdefghij", "some manner of key": "a type of value", "otherTypes": 555}, helper.isString('OK')); client.HGETALL(hash, function (err, obj) { assert.equal(obj['0123456789'], 'abcdefghij'); assert.equal(obj['some manner of key'], 'a type of value'); @@ -37,6 +37,15 @@ describe("The 'hmset' method", function () { }) }); + it('handles object-style syntax and the key being a number', function (done) { + client.HMSET(231232, {"0123456789": "abcdefghij", "some manner of key": "a type of value", "otherTypes": 555}, helper.isString('OK')); + client.HGETALL(231232, function (err, obj) { + assert.equal(obj['0123456789'], 'abcdefghij'); + assert.equal(obj['some manner of key'], 'a type of value'); + return done(err); + }); + }); + it('allows a numeric key', function (done) { client.HMSET(hash, 99, 'banana', helper.isString('OK')); client.HGETALL(hash, function (err, obj) { diff --git a/test/commands/mset.spec.js b/test/commands/mset.spec.js index aacb969c696..45dab25462e 100644 --- a/test/commands/mset.spec.js +++ b/test/commands/mset.spec.js @@ -7,12 +7,6 @@ var uuid = require('uuid'); describe("The 'mset' method", function () { - function removeMochaListener () { - var mochaListener = process.listeners('uncaughtException').pop(); - process.removeListener('uncaughtException', mochaListener); - return mochaListener; - } - helper.allTests(function(parser, ip, args) { describe("using " + parser + " and " + ip, function () { @@ -102,7 +96,7 @@ describe("The 'mset' method", function () { describe("with undefined 'key' and missing 'value' parameter", function () { // this behavior is different from the 'set' behavior. it("throws an error", function (done) { - var mochaListener = removeMochaListener(); + var mochaListener = helper.removeMochaListener(); process.once('uncaughtException', function (err) { process.on('uncaughtException', mochaListener); @@ -116,7 +110,7 @@ describe("The 'mset' method", function () { describe("with undefined 'key' and defined 'value' parameters", function () { it("throws an error", function () { - var mochaListener = removeMochaListener(); + var mochaListener = helper.removeMochaListener(); process.once('uncaughtException', function (err) { process.on('uncaughtException', mochaListener); diff --git a/test/commands/select.spec.js b/test/commands/select.spec.js index dac3c8b34c7..f12eb07d976 100644 --- a/test/commands/select.spec.js +++ b/test/commands/select.spec.js @@ -6,12 +6,6 @@ var redis = config.redis; describe("The 'select' method", function () { - function removeMochaListener () { - var mochaListener = process.listeners('uncaughtException').pop(); - process.removeListener('uncaughtException', mochaListener); - return mochaListener; - } - helper.allTests(function(parser, ip, args) { describe("using " + parser + " and " + ip, function () { @@ -96,7 +90,7 @@ describe("The 'select' method", function () { describe("with an invalid db index", function () { it("throws an error when callback not provided", function (done) { - var mochaListener = removeMochaListener(); + var mochaListener = helper.removeMochaListener(); assert.strictEqual(client.selected_db, null, "default db should be null"); process.once('uncaughtException', function (err) { diff --git a/test/commands/set.spec.js b/test/commands/set.spec.js index 28455c9ebe9..072342db4b7 100644 --- a/test/commands/set.spec.js +++ b/test/commands/set.spec.js @@ -7,12 +7,6 @@ var uuid = require('uuid'); describe("The 'set' method", function () { - function removeMochaListener () { - var mochaListener = process.listeners('uncaughtException').pop(); - process.removeListener('uncaughtException', mochaListener); - return mochaListener; - } - helper.allTests(function(parser, ip, args) { describe("using " + parser + " and " + ip, function () { @@ -123,7 +117,7 @@ describe("The 'set' method", function () { it("does not throw an error", function (done) { this.timeout(200); - var mochaListener = removeMochaListener(); + var mochaListener = helper.removeMochaListener(); process.once('uncaughtException', function (err) { process.on('uncaughtException', mochaListener); @@ -140,7 +134,7 @@ describe("The 'set' method", function () { describe("with undefined 'key' and defined 'value' parameters", function () { it("throws an error", function () { - var mochaListener = removeMochaListener(); + var mochaListener = helper.removeMochaListener(); process.once('uncaughtException', function (err) { process.on('uncaughtException', mochaListener); diff --git a/test/helper.js b/test/helper.js index 74404b2630b..756ed1d139a 100644 --- a/test/helper.js +++ b/test/helper.js @@ -92,12 +92,21 @@ module.exports = { return true; }, allTests: function (cb) { - ['javascript', 'hiredis'].forEach(function (parser) { - cb(parser, "/tmp/redis.sock", config.configureClient(parser, "/tmp/redis.sock")); - ['IPv4', 'IPv6'].forEach(function (ip) { - cb(parser, ip, config.configureClient(parser, ip)); + [undefined].forEach(function (options) { // add buffer option at some point + describe(options && options.return_buffers ? "returning buffers" : "returning strings", function () { + ['hiredis', 'javascript'].forEach(function (parser) { + cb(parser, "/tmp/redis.sock", config.configureClient(parser, "/tmp/redis.sock", options)); + ['IPv4', 'IPv6'].forEach(function (ip) { + cb(parser, ip, config.configureClient(parser, ip, options)); + }); + }); }); }); + }, + removeMochaListener: function () { + var mochaListener = process.listeners('uncaughtException').pop(); + process.removeListener('uncaughtException', mochaListener); + return mochaListener; } } diff --git a/test/node_redis.spec.js b/test/node_redis.spec.js index 580e3e127e1..c20b879f0bf 100644 --- a/test/node_redis.spec.js +++ b/test/node_redis.spec.js @@ -9,15 +9,36 @@ describe("The node_redis client", function () { helper.allTests(function(parser, ip, args) { + if (args[2]) { // skip if options are undefined + describe("testing parser existence", function () { + it('throws on non-existence', function (done) { + var mochaListener = helper.removeMochaListener(); + + process.once('uncaughtException', function (err) { + process.on('uncaughtException', mochaListener); + assert.equal(err.message, 'Couldn\'t find named parser nonExistingParser on this system'); + return done(); + }); + + // Don't pollute the args for the other connections + var tmp = JSON.parse(JSON.stringify(args)); + tmp[2].parser = 'nonExistingParser'; + redis.createClient.apply(redis.createClient, tmp); + }); + }); + } + describe("using " + parser + " and " + ip, function () { var client; describe("when not connected", function () { afterEach(function () { - client.end(); + if (client) { + client.end(); + } }); - it("connects correctly", function (done) { + it("connects correctly with args", function (done) { client = redis.createClient.apply(redis.createClient, args); client.on("error", done); @@ -28,6 +49,58 @@ describe("The node_redis client", function () { }); }); }); + + it("connects correctly with defaults values", function (done) { + client = redis.createClient(); + client.on("error", done); + + client.once("ready", function () { + client.removeListener("error", done); + client.get("recon 1", function (err, res) { + done(err); + }); + }); + }); + + it("connects correctly to localhost", function (done) { + client = redis.createClient(null, null); + client.on("error", done); + + client.once("ready", function () { + client.removeListener("error", done); + client.get("recon 1", function (err, res) { + done(err); + }); + }); + }); + + it("throws on strange connection infos", function () { + try { + redis.createClient(true); + throw new Error('failed'); + } catch (err) { + assert.equal(err.message, 'unknown type of connection in createClient()'); + } + }); + + if (ip === 'IPv4') { + it('allows connecting with the redis url and the default port', function (done) { + client = redis.createClient('redis://foo:porkchopsandwiches@' + config.HOST[ip]); + client.on("ready", function () { + return done(); + }); + }); + + it('allows connecting with the redis url and no auth', function (done) { + client = redis.createClient('redis://' + config.HOST[ip] + ':' + config.PORT, { + detect_buffers: false + }); + client.on("ready", function () { + return done(); + }); + }); + } + }); describe("when connected", function () { From eb61ce32aa2092b9230c6fc44988fab2ca27e1a4 Mon Sep 17 00:00:00 2001 From: Ruben Bridgewater Date: Thu, 3 Sep 2015 22:39:39 +0200 Subject: [PATCH 0286/1748] Test --- .travis.yml | 2 ++ 1 file changed, 2 insertions(+) diff --git a/.travis.yml b/.travis.yml index 903127a0265..5e0cbcaf43b 100644 --- a/.travis.yml +++ b/.travis.yml @@ -8,6 +8,8 @@ addons: - ubuntu-toolchain-r-test packages: - g++-4.8 +services: + - redis-server node_js: - "0.10" - "0.12" From d976bbcb2dc1689dc4f88f739d91fadf512f29f7 Mon Sep 17 00:00:00 2001 From: Benjamin Coe Date: Thu, 3 Sep 2015 22:51:25 -0700 Subject: [PATCH 0287/1748] some small tweaks to #815, so that we don't use two differnt approaches for spawning redis --- .travis.yml | 2 -- test/conf/password.conf | 2 +- test/conf/redis.conf | 2 +- test/lib/config.js | 2 +- test/lib/redis-process.js | 11 +++++++++++ test/node_redis.spec.js | 4 ++-- 6 files changed, 16 insertions(+), 7 deletions(-) diff --git a/.travis.yml b/.travis.yml index 5e0cbcaf43b..903127a0265 100644 --- a/.travis.yml +++ b/.travis.yml @@ -8,8 +8,6 @@ addons: - ubuntu-toolchain-r-test packages: - g++-4.8 -services: - - redis-server node_js: - "0.10" - "0.12" diff --git a/test/conf/password.conf b/test/conf/password.conf index 8351cb455f4..fae73a9415d 100644 --- a/test/conf/password.conf +++ b/test/conf/password.conf @@ -1,5 +1,5 @@ requirepass porkchopsandwiches -port 6378 +port 6379 bind ::1 127.0.0.1 unixsocket /tmp/redis.sock unixsocketperm 755 diff --git a/test/conf/redis.conf b/test/conf/redis.conf index dd9a4b6572c..16f1434200b 100644 --- a/test/conf/redis.conf +++ b/test/conf/redis.conf @@ -1,4 +1,4 @@ -port 6378 +port 6379 bind ::1 127.0.0.1 unixsocket /tmp/redis.sock unixsocketperm 755 diff --git a/test/lib/config.js b/test/lib/config.js index 54c6ed27bb6..296fdc0bf89 100644 --- a/test/lib/config.js +++ b/test/lib/config.js @@ -4,7 +4,7 @@ var redis = require('../../index'); var config = { redis: redis, - PORT: 6378, + PORT: 6379, HOST: { IPv4: "127.0.0.1", IPv6: "::1" diff --git a/test/lib/redis-process.js b/test/lib/redis-process.js index 212cf359c06..32f2045e416 100644 --- a/test/lib/redis-process.js +++ b/test/lib/redis-process.js @@ -11,6 +11,17 @@ module.exports = { var confFile = conf || path.resolve(__dirname, '../conf/redis.conf'); var rp = cp.spawn("redis-server", [confFile], {}); + // capture a failure booting redis, and give + // the user running the test some directions. + rp.once("exit", function (code) { + if (code !== 0) { + console.error('failed to starting redis with exit code "' + code + '" ' + + 'stop any other redis processes currently running (' + + 'hint: lsof -i :6379)'); + process.exit(code) + } + }) + // wait for redis to become available, by // checking the port we bind on. waitForRedis(true, function () { diff --git a/test/node_redis.spec.js b/test/node_redis.spec.js index c20b879f0bf..93b5531473c 100644 --- a/test/node_redis.spec.js +++ b/test/node_redis.spec.js @@ -50,7 +50,7 @@ describe("The node_redis client", function () { }); }); - it("connects correctly with defaults values", function (done) { + it("connects correctly with default values", function (done) { client = redis.createClient(); client.on("error", done); @@ -74,7 +74,7 @@ describe("The node_redis client", function () { }); }); - it("throws on strange connection infos", function () { + it("throws on strange connection info", function () { try { redis.createClient(true); throw new Error('failed'); From 837ea10a0c8a484088dc31b35d6e11a1925bead9 Mon Sep 17 00:00:00 2001 From: Benjamin Coe Date: Thu, 3 Sep 2015 23:56:17 -0700 Subject: [PATCH 0288/1748] show off redis:// format overloading --- README.md | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/README.md b/README.md index 98749228246..85ea259e8d5 100644 --- a/README.md +++ b/README.md @@ -155,10 +155,11 @@ If you have `redis-server` running on the same computer as node, then the defaul port and host are probably fine and you don't need to supply any arguments. `createClient()` returns a `RedisClient` object. ### overloading -* `redis.createClient(port,host,options)` +* `redis.createClient(port, host, options)` * `redis.createClient()` is equivalent to `redis.createClient(6379, '127.0.0.1', {})` * `redis.createClient(options)` is equivalent to `redis.createClient(6379, '127.0.0.1', options)` * `redis.createClient(unix_socket, options)` +* `redis.createClient('redis://user:pass@host:port', options)` `options` is an object with the following possible properties: From 1f986055c48c70a8d6a856dae24d5086247164da Mon Sep 17 00:00:00 2001 From: Benjamin Coe Date: Thu, 3 Sep 2015 23:58:12 -0700 Subject: [PATCH 0289/1748] add a note about how to turn on the debugger --- README.md | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/README.md b/README.md index 85ea259e8d5..caeefe5a743 100644 --- a/README.md +++ b/README.md @@ -710,6 +710,10 @@ hiredis parser: GET 4MiB buf, 1/5 min/max/avg/p95: 6/ 13/ 8.01/ 11.95 802ms total, 124.69 ops/sec GET 4MiB buf, 50/5 min/max/avg/p95: 16/ 480/ 203.85/ 435.70 531ms total, 188.32 ops/sec +## Debugging + +To get debug output run your `node_redis` application with `NODE_DEBUG=redis`. + ## TODO 1. 100% coverage From 9b1d262cdf0a214cb1628341edfb3692f87a8a86 Mon Sep 17 00:00:00 2001 From: Ruben Bridgewater Date: Fri, 4 Sep 2015 15:21:59 +0200 Subject: [PATCH 0290/1748] Accept hmset being used without a callback. Closes #694 --- index.js | 2 +- test/commands/hmset.spec.js | 36 ++++++++++++++++++++++++++++++++++++ 2 files changed, 37 insertions(+), 1 deletion(-) diff --git a/index.js b/index.js index 12a407abd23..151b9982201 100644 --- a/index.js +++ b/index.js @@ -996,7 +996,7 @@ RedisClient.prototype.HMGET = RedisClient.prototype.hmget; RedisClient.prototype.hmset = function (args, callback) { var tmp_args, tmp_keys, i, il, key; - if (Array.isArray(args) && typeof callback === "function") { + if (Array.isArray(args)) { return this.send_command("hmset", args, callback); } diff --git a/test/commands/hmset.spec.js b/test/commands/hmset.spec.js index e30669d00e5..a4d77dfc015 100644 --- a/test/commands/hmset.spec.js +++ b/test/commands/hmset.spec.js @@ -45,6 +45,42 @@ describe("The 'hmset' method", function () { }); }); + it('allows a numeric key without callback', function (done) { + client.HMSET(hash, 99, 'banana', 'test', 25); + client.HGETALL(hash, function (err, obj) { + assert.equal(obj['99'], 'banana'); + assert.equal(obj['test'], '25'); + return done(err); + }); + }); + + it('allows an array without callback', function (done) { + client.HMSET([hash, 99, 'banana', 'test', 25]); + client.HGETALL(hash, function (err, obj) { + assert.equal(obj['99'], 'banana'); + assert.equal(obj['test'], '25'); + return done(err); + }); + }); + + it('allows an array and a callback', function (done) { + client.HMSET([hash, 99, 'banana', 'test', 25], helper.isString('OK')); + client.HGETALL(hash, function (err, obj) { + assert.equal(obj['99'], 'banana'); + assert.equal(obj['test'], '25'); + return done(err); + }); + }); + + it('handles object-style syntax without callback', function (done) { + client.HMSET(hash, {"0123456789": "abcdefghij", "some manner of key": "a type of value"}); + client.HGETALL(hash, function (err, obj) { + assert.equal(obj['0123456789'], 'abcdefghij'); + assert.equal(obj['some manner of key'], 'a type of value'); + return done(err); + }) + }); + afterEach(function () { client.end(); }); From 159bc806532a14d669c7fd5744fffa53b431656b Mon Sep 17 00:00:00 2001 From: Ruben Bridgewater Date: Fri, 4 Sep 2015 15:37:47 +0200 Subject: [PATCH 0291/1748] Revert to old behavior --- index.js | 17 +++++++---------- 1 file changed, 7 insertions(+), 10 deletions(-) diff --git a/index.js b/index.js index 12a407abd23..519beb5e853 100644 --- a/index.js +++ b/index.js @@ -13,16 +13,13 @@ var net = require("net"), connection_id = 0, default_port = 6379, default_host = "127.0.0.1", - debug; - -/* istanbul ignore next */ -if (util.debuglog) { - debug = util.debuglog('redis'); -} else if (/\bredis\b/i.test(process.env.NODE_DEBUG)) { - debug = console.error; -} else { - debug = function() {}; -} + debug = function(msg) { + if (exports.debug_mode) { + console.error(msg); + } + }; + +exports.debug_mode = /\bredis\b/i.test(process.env.NODE_DEBUG); // hiredis might not be installed try { From 0170145f744b0784065349b95a1e69b1493fdc71 Mon Sep 17 00:00:00 2001 From: Ruben Bridgewater Date: Sat, 5 Sep 2015 17:10:27 +0200 Subject: [PATCH 0292/1748] Remove event emitters from the parser as they are overhead that is not needed --- index.js | 14 ++------------ lib/parser/hiredis.js | 18 ++++-------------- lib/parser/javascript.js | 13 +------------ test/parser/javascript.spec.js | 2 +- 4 files changed, 8 insertions(+), 39 deletions(-) diff --git a/index.js b/index.js index 151b9982201..abb8d00a285 100644 --- a/index.js +++ b/index.js @@ -302,18 +302,8 @@ RedisClient.prototype.init_parser = function () { this.reply_parser = new this.parser_module.Parser({ return_buffers: self.options.return_buffers || self.options.detect_buffers || false }); - - // "reply error" is an error sent back by Redis - this.reply_parser.on("reply error", function (reply) { - self.return_error(reply); - }); - this.reply_parser.on("reply", function (reply) { - self.return_reply(reply); - }); - // "error" is bad. Somehow the parser got confused. It'll try to reset and continue. - this.reply_parser.on("error", function (err) { - self.emit("error", new Error("Redis reply parser error: " + err.stack)); - }); + this.reply_parser.send_error = this.return_error.bind(self); + this.reply_parser.send_reply = this.return_reply.bind(self); }; RedisClient.prototype.on_ready = function () { diff --git a/lib/parser/hiredis.js b/lib/parser/hiredis.js index d1fd0b7a9a5..aafaa1950c6 100644 --- a/lib/parser/hiredis.js +++ b/lib/parser/hiredis.js @@ -1,8 +1,6 @@ 'use strict'; -var events = require("events"), - util = require("util"), - hiredis = require("hiredis"); +var hiredis = require("hiredis"); exports.name = "hiredis"; @@ -10,11 +8,8 @@ function HiredisReplyParser(options) { this.name = exports.name; this.options = options || {}; this.reset(); - events.EventEmitter.call(this); } -util.inherits(HiredisReplyParser, events.EventEmitter); - exports.Parser = HiredisReplyParser; HiredisReplyParser.prototype.reset = function () { @@ -27,21 +22,16 @@ HiredisReplyParser.prototype.execute = function (data) { var reply; this.reader.feed(data); while (true) { - try { - reply = this.reader.get(); - } catch (err) { - this.emit("error", err); - break; - } + reply = this.reader.get(); if (reply === undefined) { break; } if (reply && reply.constructor === Error) { - this.emit("reply error", reply); + this.send_error(reply); } else { - this.emit("reply", reply); + this.send_reply(reply); } } }; diff --git a/lib/parser/javascript.js b/lib/parser/javascript.js index be5248d1155..e6280b21bad 100644 --- a/lib/parser/javascript.js +++ b/lib/parser/javascript.js @@ -1,7 +1,6 @@ 'use strict'; -var events = require("events"), - util = require("util"); +var util = require("util"); function Packet(type, size) { this.type = type; @@ -20,8 +19,6 @@ function ReplyParser(options) { this._reply_type = null; } -util.inherits(ReplyParser, events.EventEmitter); - exports.Parser = ReplyParser; function IncompleteReadBuffer(message) { @@ -286,11 +283,3 @@ ReplyParser.prototype._packetEndOffset = function () { ReplyParser.prototype._bytesRemaining = function () { return (this._buffer.length - this._offset) < 0 ? 0 : (this._buffer.length - this._offset); }; - -ReplyParser.prototype.send_error = function (reply) { - this.emit("reply error", reply); -}; - -ReplyParser.prototype.send_reply = function (reply) { - this.emit("reply", reply); -}; diff --git a/test/parser/javascript.spec.js b/test/parser/javascript.spec.js index 445964371b2..2a40342612d 100644 --- a/test/parser/javascript.spec.js +++ b/test/parser/javascript.spec.js @@ -11,7 +11,7 @@ describe('javascript parser', function () { assert.deepEqual(reply, [['a']], "Expecting multi-bulk reply of [['a']]"); reply_count++; } - parser.on("reply", check_reply); + parser.send_reply = check_reply; parser.execute(new Buffer('*1\r\n*1\r\n$1\r\na\r\n')); From 55dd838d770fcf20e69082f9082d55367d171f3e Mon Sep 17 00:00:00 2001 From: Benjamin Coe Date: Fri, 11 Sep 2015 21:53:49 -0700 Subject: [PATCH 0293/1748] make sure we track changes in changelog --- changelog.md | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/changelog.md b/changelog.md index 929e6451567..085cdae2bf7 100644 --- a/changelog.md +++ b/changelog.md @@ -1,6 +1,10 @@ Changelog ========= +## Upcoming + +* [#815](https://github.com/NodeRedis/node_redis/pull/815) Consistently use new debug functionality (@BridgeAR) + ## v1.0.0 - Aug 30, 2015 * Huge issue and pull-request cleanup. Thanks Blain! (@blainsmith) From 81c187ac8d06cadbec036ae7071357fa64cb01ab Mon Sep 17 00:00:00 2001 From: David Banham Date: Sat, 12 Sep 2015 20:57:12 +1000 Subject: [PATCH 0294/1748] Test against node 4.0.x on Travis --- .travis.yml | 1 + 1 file changed, 1 insertion(+) diff --git a/.travis.yml b/.travis.yml index 903127a0265..7240cf7d48c 100644 --- a/.travis.yml +++ b/.travis.yml @@ -11,5 +11,6 @@ addons: node_js: - "0.10" - "0.12" + - "4.0" - "iojs" after_success: npm run coverage From 06c5f1922b338258172359f0d54b5d3b0c6ae0ae Mon Sep 17 00:00:00 2001 From: Ruben Bridgewater Date: Tue, 1 Sep 2015 21:37:24 +0200 Subject: [PATCH 0295/1748] Add jshint and fix errors accordingly (including broken tests) --- .gitignore | 1 + .jshintrc | 24 ++++++++++ benches/re_sub_test.js | 5 +-- benches/stress/pubsub/pub.js | 2 +- benches/stress/pubsub/server.js | 3 +- benches/stress/rpushblpop/pub.js | 2 + benches/stress/rpushblpop/server.js | 3 +- benches/stress/speed/speed.js | 68 ++++++++++++++--------------- connection_breaker.js | 2 - diff_multi_bench_output.js | 56 ++++++++++++------------ examples/unix_socket.js | 2 +- index.js | 52 +++++++++++----------- package.json | 4 +- test/auth.spec.js | 5 ++- test/commands/blpop.spec.js | 2 + test/commands/client.spec.js | 2 + test/commands/dbsize.spec.js | 3 +- test/commands/del.spec.js | 3 +- test/commands/eval.spec.js | 2 + test/commands/exits.spec.js | 3 +- test/commands/expire.spec.js | 3 +- test/commands/flushdb.spec.js | 2 + test/commands/get.spec.js | 3 +- test/commands/getset.spec.js | 3 +- test/commands/hgetall.spec.js | 2 + test/commands/hincrby.spec.js | 3 +- test/commands/hlen.spec.js | 3 +- test/commands/hmget.spec.js | 2 + test/commands/hmset.spec.js | 10 +++-- test/commands/hset.spec.js | 2 + test/commands/incr.spec.js | 4 +- test/commands/keys.spec.js | 2 + test/commands/mget.spec.js | 2 + test/commands/mset.spec.js | 3 +- test/commands/msetnx.spec.js | 3 +- test/commands/multi.spec.js | 9 ++-- test/commands/randomkey.test.js | 2 + test/commands/rename.spec.js | 3 +- test/commands/renamenx.spec.js | 3 +- test/commands/sadd.spec.js | 2 + test/commands/scard.spec.js | 3 +- test/commands/script.spec.js | 2 + test/commands/sdiff.spec.js | 2 + test/commands/sdiffstore.spec.js | 2 + test/commands/select.spec.js | 3 +- test/commands/set.spec.js | 3 +- test/commands/setex.spec.js | 2 + test/commands/setnx.spec.js | 3 +- test/commands/sinter.spec.js | 2 + test/commands/sinterstore.spec.js | 2 + test/commands/sismember.spec.js | 3 +- test/commands/slowlog.spec.js | 2 + test/commands/smembers.spec.js | 2 + test/commands/smove.spec.js | 3 +- test/commands/sort.spec.js | 2 + test/commands/spop.spec.js | 2 + test/commands/srem.spec.js | 2 + test/commands/sunion.spec.js | 2 + test/commands/sunionstore.spec.js | 2 + test/commands/ttl.spec.js | 2 + test/commands/type.spec.js | 3 +- test/commands/watch.spec.js | 22 +++++----- test/helper.js | 18 ++++---- test/lib/config.js | 2 + test/lib/redis-process.js | 46 +++++++++---------- test/node_redis.spec.js | 10 +++-- test/parser/javascript.spec.js | 2 +- test/pubsub.spec.js | 11 ++--- test/queue.spec.js | 2 + 69 files changed, 290 insertions(+), 182 deletions(-) create mode 100644 .jshintrc diff --git a/.gitignore b/.gitignore index f351fac36f6..d267c944c04 100644 --- a/.gitignore +++ b/.gitignore @@ -2,3 +2,4 @@ node_modules .tern-port .nyc_output coverage +npm-debug.log diff --git a/.jshintrc b/.jshintrc new file mode 100644 index 00000000000..814239e345c --- /dev/null +++ b/.jshintrc @@ -0,0 +1,24 @@ +{ + "eqeqeq": true, // Prohibits the use of == and != in favor of === and !== + "noarg": true, // Prohibit use of `arguments.caller` and `arguments.callee` + "undef": true, // Require all non-global variables be declared before they are used. + "unused": "vars", // Warn unused variables, but not unused params + "strict": true, // Require `use strict` pragma in every file. + "nonbsp": true, // don't allow non utf-8 pages to break + "forin": true, // don't allow not filtert for in loops + "freeze": true, // prohibit overwriting prototypes of native objects + "nonew": true, // prohibit use of constructors with new when not assigning to a variable + "maxdepth": 6, + "latedef": true, + "maxparams": 5, + + // Environment options + "node": true, // Enable globals available when code is running inside of the NodeJS runtime environment. + "mocha": true, + + "overrides": { + "examples/*.js": { + "unused": false + } + } +} diff --git a/benches/re_sub_test.js b/benches/re_sub_test.js index 256d0d0d1d6..62c9302f901 100644 --- a/benches/re_sub_test.js +++ b/benches/re_sub_test.js @@ -1,8 +1,7 @@ 'use strict'; -var client = require('../index').createClient() - , client2 = require('../index').createClient() - , assert = require('assert'); +var client = require('../index').createClient(); +var client2 = require('../index').createClient(); client.once('subscribe', function (channel, count) { client.unsubscribe('x'); diff --git a/benches/stress/pubsub/pub.js b/benches/stress/pubsub/pub.js index 0acde7a6eba..51c6555f83a 100644 --- a/benches/stress/pubsub/pub.js +++ b/benches/stress/pubsub/pub.js @@ -3,8 +3,8 @@ var freemem = require('os').freemem; var profiler = require('v8-profiler'); var codec = require('../codec'); - var sent = 0; +var exec; var pub = require('redis').createClient(null, null, { //command_queue_high_water: 5, diff --git a/benches/stress/pubsub/server.js b/benches/stress/pubsub/server.js index 035e6b74406..0679561b1d8 100644 --- a/benches/stress/pubsub/server.js +++ b/benches/stress/pubsub/server.js @@ -6,12 +6,11 @@ var codec = require('../codec'); var id = Math.random(); var recv = 0; -var sub = require('redis').createClient() +require('redis').createClient() .on('ready', function() { this.subscribe('timeline'); }) .on('message', function(channel, message) { - var self = this; if (message) { message = codec.decode(message); ++recv; diff --git a/benches/stress/rpushblpop/pub.js b/benches/stress/rpushblpop/pub.js index 9caf1d0b823..009407857d2 100644 --- a/benches/stress/rpushblpop/pub.js +++ b/benches/stress/rpushblpop/pub.js @@ -6,6 +6,8 @@ var codec = require('../codec'); var sent = 0; +var exec; + var pub = require('redis').createClient(null, null, { //command_queue_high_water: 5, //command_queue_low_water: 1 diff --git a/benches/stress/rpushblpop/server.js b/benches/stress/rpushblpop/server.js index 9cbcdd9ed75..7a18a9b3f51 100644 --- a/benches/stress/rpushblpop/server.js +++ b/benches/stress/rpushblpop/server.js @@ -7,7 +7,8 @@ var id = Math.random(); var recv = 0; var cmd = require('redis').createClient(); -var sub = require('redis').createClient() + +require('redis').createClient() .on('ready', function() { this.emit('timeline'); }) diff --git a/benches/stress/speed/speed.js b/benches/stress/speed/speed.js index 93d54f0c133..7b76db6f4cd 100644 --- a/benches/stress/speed/speed.js +++ b/benches/stress/speed/speed.js @@ -16,6 +16,40 @@ var codec = { var obj, l; +function run(obj, codec) { + var t1 = Date.now(); + var n = 10000; + for (var i = 0; i < n; ++i) { + codec.decode(l = codec.encode(obj)); + } + var t2 = Date.now(); + //console.log('DONE', n*1000/(t2-t1), 'codecs/sec, length=', l.length); + return [n*1000/(t2-t1), l.length]; +} + +function series(obj, cname, n) { + var rate = 0; + var len = 0; + for (var i = 0; i < n; ++i) { + var r = run(obj, codec[cname]); + rate += r[0]; + len += r[1]; + } + rate /= n; + len /= n; + console.log(cname + ' ' + rate + ' ' + len); + return [rate, len]; +} + +function forObj(obj) { + var r = { + JSON: series(obj, 'JSON', 20), + msgpack: series(obj, 'msgpack', 20), + bison: series(obj, 'bison', 20) + }; + return r; +} + var s = '0'; for (var i = 0; i < 12; ++i) s += s; @@ -50,37 +84,3 @@ obj = { rand: [] }; forObj(obj); - -function run(obj, codec) { - var t1 = Date.now(); - var n = 10000; - for (var i = 0; i < n; ++i) { - codec.decode(l = codec.encode(obj)); - } - var t2 = Date.now(); - //console.log('DONE', n*1000/(t2-t1), 'codecs/sec, length=', l.length); - return [n*1000/(t2-t1), l.length]; -} - -function series(obj, cname, n) { - var rate = 0; - var len = 0; - for (var i = 0; i < n; ++i) { - var r = run(obj, codec[cname]); - rate += r[0]; - len += r[1]; - } - rate /= n; - len /= n; - console.log(cname + ' ' + rate + ' ' + len); - return [rate, len]; -} - -function forObj(obj) { - var r = { - JSON: series(obj, 'JSON', 20), - msgpack: series(obj, 'msgpack', 20), - bison: series(obj, 'bison', 20) - }; - return r; -} diff --git a/connection_breaker.js b/connection_breaker.js index f2db0eb1b69..e036a2b1730 100644 --- a/connection_breaker.js +++ b/connection_breaker.js @@ -42,8 +42,6 @@ server.listen(6479); var redis = require('./'); -var port = 6479; - var client = redis.createClient(6479, 'localhost'); function iter() { diff --git a/diff_multi_bench_output.js b/diff_multi_bench_output.js index 89b6be479c9..86a9414d045 100755 --- a/diff_multi_bench_output.js +++ b/diff_multi_bench_output.js @@ -2,9 +2,9 @@ 'use strict'; -var colors = require('colors'), - fs = require('fs'), - _ = require('underscore'), +/* jshint -W079: Ignore redefinitions (before & after) */ + +var fs = require('fs'), metrics = require('metrics'), // `node diff_multi_bench_output.js before.txt after.txt` @@ -30,6 +30,28 @@ console.log('Comparing before,', before.green, '(', before_lines.length, var total_ops = new metrics.Histogram.createUniformHistogram(); +function is_whitespace(s) { + return !!s.trim(); +} + +function parseInt10(s) { + return parseInt(s, 10); +} + +// green if greater than 0, red otherwise +function humanize_diff(num, unit) { + unit = unit || ""; + if (num > 0) { + return ('+' + num + unit).green; + } + return ('' + num + unit).red; +} + +function command_name(words) { + var line = words.join(' '); + return line.substr(0, line.indexOf(',')); +} + before_lines.forEach(function(b, i) { var a = after_lines[i]; if (!a || !b || !b.trim() || !a.trim()) { @@ -60,33 +82,11 @@ before_lines.forEach(function(b, i) { pct = humanize_diff(pct, '%'); console.log( // name of test - command_name(a_words) === command_name(b_words) - ? command_name(a_words) + ':' - : '404:', + command_name(a_words) === command_name(b_words) ? + command_name(a_words) + ':' : + '404:', // results of test ops.join(' -> '), 'ops/sec (∆', delta, pct, ')'); }); console.log('Mean difference in ops/sec:', humanize_diff(total_ops.mean().toPrecision(6))); - -function is_whitespace(s) { - return !!s.trim(); -} - -function parseInt10(s) { - return parseInt(s, 10); -} - -// green if greater than 0, red otherwise -function humanize_diff(num, unit) { - unit = unit || ""; - if (num > 0) { - return ('+' + num + unit).green; - } - return ('' + num + unit).red; -} - -function command_name(words) { - var line = words.join(' '); - return line.substr(0, line.indexOf(',')); -} diff --git a/examples/unix_socket.js b/examples/unix_socket.js index 460b78e857c..a6c74cbc3e7 100644 --- a/examples/unix_socket.js +++ b/examples/unix_socket.js @@ -27,5 +27,5 @@ function done() { setTimeout(function () { console.log("Taking snapshot."); - var snap = profiler.takeSnapshot(); + profiler.takeSnapshot(); }, 5000); diff --git a/index.js b/index.js index ef53de50ce2..2466130d8f2 100644 --- a/index.js +++ b/index.js @@ -1,7 +1,5 @@ 'use strict'; -/*global Buffer require exports console setTimeout */ - var net = require("net"), URL = require("url"), util = require("util"), @@ -616,7 +614,7 @@ RedisClient.prototype.return_reply = function (reply) { type = reply[0].toString(); } - if (this.pub_sub_mode && (type == 'message' || type == 'pmessage')) { + if (this.pub_sub_mode && (type === 'message' || type === 'pmessage')) { debug("received pubsub message"); } else { @@ -1172,30 +1170,6 @@ RedisClient.prototype.eval = RedisClient.prototype.EVAL = function () { }); }; -exports.createClient = function(port_arg, host_arg, options) { - if (typeof port_arg === 'object' || port_arg === undefined) { - options = port_arg || options; - return createClient_tcp(default_port, default_host, options); - } - if (typeof port_arg === 'number' || typeof port_arg === 'string' && /^\d+$/.test(port_arg)) { - return createClient_tcp(port_arg, host_arg, options); - } - if (typeof port_arg === 'string') { - options = host_arg || {}; - - var parsed = URL.parse(port_arg, true, true); - if (parsed.hostname) { - if (parsed.auth) { - options.auth_pass = parsed.auth.split(':')[1]; - } - return createClient_tcp(parsed.port, parsed.hostname, options); - } - - return createClient_unix(port_arg, options); - } - throw new Error('unknown type of connection in createClient()'); -}; - var createClient_unix = function(path, options){ var cnxOptions = { path: path @@ -1224,6 +1198,30 @@ var createClient_tcp = function (port_arg, host_arg, options) { return redis_client; }; +exports.createClient = function(port_arg, host_arg, options) { + if (typeof port_arg === 'object' || port_arg === undefined) { + options = port_arg || options; + return createClient_tcp(default_port, default_host, options); + } + if (typeof port_arg === 'number' || typeof port_arg === 'string' && /^\d+$/.test(port_arg)) { + return createClient_tcp(port_arg, host_arg, options); + } + if (typeof port_arg === 'string') { + options = host_arg || {}; + + var parsed = URL.parse(port_arg, true, true); + if (parsed.hostname) { + if (parsed.auth) { + options.auth_pass = parsed.auth.split(':')[1]; + } + return createClient_tcp(parsed.port, parsed.hostname, options); + } + + return createClient_unix(port_arg, options); + } + throw new Error('unknown type of connection in createClient()'); +}; + exports.print = function (err, reply) { if (err) { console.log("Error: " + err); diff --git a/package.json b/package.json index 6e322a046ab..f6bbc41e732 100644 --- a/package.json +++ b/package.json @@ -11,13 +11,15 @@ "main": "./index.js", "scripts": { "coverage": "nyc report --reporter=text-lcov | coveralls", - "test": "nyc ./node_modules/.bin/_mocha ./test/*.js ./test/commands/*.js ./test/parser/*.js --timeout=8000" + "test": "nyc ./node_modules/.bin/_mocha ./test/*.js ./test/commands/*.js ./test/parser/*.js --timeout=8000", + "jshint": "./node_modules/.bin/jshint *.js **/*.js **/**/*.js --exclude=node_modules/**/*" }, "devDependencies": { "async": "^1.3.0", "colors": "~0.6.0-1", "coveralls": "^2.11.2", "hiredis": "^0.4.1", + "jshint": "^2.8.0", "metrics": ">=0.1.5", "mocha": "^2.2.5", "nyc": "^3.0.0", diff --git a/test/auth.spec.js b/test/auth.spec.js index 63ce1cf9ffc..5dee3d83a32 100644 --- a/test/auth.spec.js +++ b/test/auth.spec.js @@ -1,7 +1,8 @@ +'use strict'; + var assert = require("assert"); var config = require("./lib/config"); -var helper = require('./helper') -var path = require('path'); +var helper = require('./helper'); var redis = config.redis; describe("client authentication", function () { diff --git a/test/commands/blpop.spec.js b/test/commands/blpop.spec.js index e5474154386..843b351dc8b 100644 --- a/test/commands/blpop.spec.js +++ b/test/commands/blpop.spec.js @@ -1,3 +1,5 @@ +'use strict'; + var assert = require("assert"); var config = require("../lib/config"); var helper = require("../helper"); diff --git a/test/commands/client.spec.js b/test/commands/client.spec.js index d0ae072f9fc..a9188cdac99 100644 --- a/test/commands/client.spec.js +++ b/test/commands/client.spec.js @@ -1,3 +1,5 @@ +'use strict'; + var assert = require("assert"); var config = require("../lib/config"); var helper = require("../helper"); diff --git a/test/commands/dbsize.spec.js b/test/commands/dbsize.spec.js index d65e9469b25..5eb6958e9c2 100644 --- a/test/commands/dbsize.spec.js +++ b/test/commands/dbsize.spec.js @@ -1,4 +1,5 @@ -var async = require('async'); +'use strict'; + var assert = require('assert'); var config = require("../lib/config"); var helper = require('../helper'); diff --git a/test/commands/del.spec.js b/test/commands/del.spec.js index 6845ee363f7..8a110485c61 100644 --- a/test/commands/del.spec.js +++ b/test/commands/del.spec.js @@ -1,4 +1,5 @@ -var assert = require("assert"); +'use strict'; + var config = require("../lib/config"); var helper = require("../helper"); var redis = config.redis; diff --git a/test/commands/eval.spec.js b/test/commands/eval.spec.js index d132e7c2a92..229e9c51b04 100644 --- a/test/commands/eval.spec.js +++ b/test/commands/eval.spec.js @@ -1,3 +1,5 @@ +'use strict'; + var assert = require("assert"); var config = require("../lib/config"); var crypto = require("crypto"); diff --git a/test/commands/exits.spec.js b/test/commands/exits.spec.js index c36e9bcfedd..2fd2ac5fcea 100644 --- a/test/commands/exits.spec.js +++ b/test/commands/exits.spec.js @@ -1,4 +1,5 @@ -var assert = require("assert"); +'use strict'; + var config = require("../lib/config"); var helper = require("../helper"); var redis = config.redis; diff --git a/test/commands/expire.spec.js b/test/commands/expire.spec.js index 8e7d3a188e4..ce3af95dc26 100644 --- a/test/commands/expire.spec.js +++ b/test/commands/expire.spec.js @@ -1,4 +1,5 @@ -var assert = require("assert"); +'use strict'; + var config = require("../lib/config"); var helper = require("../helper"); var redis = config.redis; diff --git a/test/commands/flushdb.spec.js b/test/commands/flushdb.spec.js index 80533d34620..c8e70bf58fb 100644 --- a/test/commands/flushdb.spec.js +++ b/test/commands/flushdb.spec.js @@ -1,3 +1,5 @@ +'use strict'; + var async = require('async'); var assert = require('assert'); var config = require("../lib/config"); diff --git a/test/commands/get.spec.js b/test/commands/get.spec.js index 4ba2621f0fe..3d3fd1e2483 100644 --- a/test/commands/get.spec.js +++ b/test/commands/get.spec.js @@ -1,4 +1,5 @@ -var async = require('async'); +'use strict'; + var assert = require('assert'); var config = require("../lib/config"); var helper = require('../helper'); diff --git a/test/commands/getset.spec.js b/test/commands/getset.spec.js index f3c294a6691..a47cd2d0b57 100644 --- a/test/commands/getset.spec.js +++ b/test/commands/getset.spec.js @@ -1,4 +1,5 @@ -var async = require('async'); +'use strict'; + var assert = require('assert'); var config = require("../lib/config"); var helper = require('../helper'); diff --git a/test/commands/hgetall.spec.js b/test/commands/hgetall.spec.js index aa3e520cebc..0735bf31de4 100644 --- a/test/commands/hgetall.spec.js +++ b/test/commands/hgetall.spec.js @@ -1,3 +1,5 @@ +'use strict'; + var assert = require("assert"); var config = require("../lib/config"); var helper = require("../helper"); diff --git a/test/commands/hincrby.spec.js b/test/commands/hincrby.spec.js index b1da736027e..c8a7cfc93ed 100644 --- a/test/commands/hincrby.spec.js +++ b/test/commands/hincrby.spec.js @@ -1,4 +1,5 @@ -var assert = require("assert"); +'use strict'; + var config = require("../lib/config"); var helper = require("../helper"); var redis = config.redis; diff --git a/test/commands/hlen.spec.js b/test/commands/hlen.spec.js index 7756e3366bf..564f248ed5d 100644 --- a/test/commands/hlen.spec.js +++ b/test/commands/hlen.spec.js @@ -1,4 +1,5 @@ -var assert = require("assert"); +'use strict'; + var config = require("../lib/config"); var helper = require("../helper"); var redis = config.redis; diff --git a/test/commands/hmget.spec.js b/test/commands/hmget.spec.js index 6ab175a9145..1b5387dc32b 100644 --- a/test/commands/hmget.spec.js +++ b/test/commands/hmget.spec.js @@ -1,3 +1,5 @@ +'use strict'; + var assert = require("assert"); var config = require("../lib/config"); var helper = require("../helper"); diff --git a/test/commands/hmset.spec.js b/test/commands/hmset.spec.js index 88b98272863..6b6f980aa99 100644 --- a/test/commands/hmset.spec.js +++ b/test/commands/hmset.spec.js @@ -1,3 +1,5 @@ +'use strict'; + var assert = require("assert"); var config = require("../lib/config"); var helper = require("../helper"); @@ -58,7 +60,7 @@ describe("The 'hmset' method", function () { client.HMSET(hash, 99, 'banana', 'test', 25); client.HGETALL(hash, function (err, obj) { assert.equal(obj['99'], 'banana'); - assert.equal(obj['test'], '25'); + assert.equal(obj.test, '25'); return done(err); }); }); @@ -67,7 +69,7 @@ describe("The 'hmset' method", function () { client.HMSET([hash, 99, 'banana', 'test', 25]); client.HGETALL(hash, function (err, obj) { assert.equal(obj['99'], 'banana'); - assert.equal(obj['test'], '25'); + assert.equal(obj.test, '25'); return done(err); }); }); @@ -76,7 +78,7 @@ describe("The 'hmset' method", function () { client.HMSET([hash, 99, 'banana', 'test', 25], helper.isString('OK')); client.HGETALL(hash, function (err, obj) { assert.equal(obj['99'], 'banana'); - assert.equal(obj['test'], '25'); + assert.equal(obj.test, '25'); return done(err); }); }); @@ -87,7 +89,7 @@ describe("The 'hmset' method", function () { assert.equal(obj['0123456789'], 'abcdefghij'); assert.equal(obj['some manner of key'], 'a type of value'); return done(err); - }) + }); }); afterEach(function () { diff --git a/test/commands/hset.spec.js b/test/commands/hset.spec.js index 668838b60de..9f55606f35f 100644 --- a/test/commands/hset.spec.js +++ b/test/commands/hset.spec.js @@ -1,3 +1,5 @@ +'use strict'; + var assert = require("assert"); var config = require("../lib/config"); var helper = require("../helper"); diff --git a/test/commands/incr.spec.js b/test/commands/incr.spec.js index 4e064279331..bc303da651d 100644 --- a/test/commands/incr.spec.js +++ b/test/commands/incr.spec.js @@ -1,9 +1,9 @@ -var async = require('async'); +'use strict'; + var assert = require('assert'); var config = require("../lib/config"); var helper = require('../helper'); var redis = config.redis; -var uuid = require('uuid'); describe("The 'incr' method", function () { diff --git a/test/commands/keys.spec.js b/test/commands/keys.spec.js index 07add246507..aaf6ddadf4a 100644 --- a/test/commands/keys.spec.js +++ b/test/commands/keys.spec.js @@ -1,3 +1,5 @@ +'use strict'; + var assert = require("assert"); var config = require("../lib/config"); var crypto = require("crypto"); diff --git a/test/commands/mget.spec.js b/test/commands/mget.spec.js index c4052a17fdd..270ff378549 100644 --- a/test/commands/mget.spec.js +++ b/test/commands/mget.spec.js @@ -1,3 +1,5 @@ +'use strict'; + var assert = require("assert"); var config = require("../lib/config"); var helper = require("../helper"); diff --git a/test/commands/mset.spec.js b/test/commands/mset.spec.js index 45dab25462e..950e6870a6e 100644 --- a/test/commands/mset.spec.js +++ b/test/commands/mset.spec.js @@ -1,4 +1,5 @@ -var async = require('async'); +'use strict'; + var assert = require('assert'); var config = require("../lib/config"); var helper = require('../helper'); diff --git a/test/commands/msetnx.spec.js b/test/commands/msetnx.spec.js index ef7cf63e9f7..80846778399 100644 --- a/test/commands/msetnx.spec.js +++ b/test/commands/msetnx.spec.js @@ -1,4 +1,5 @@ -var assert = require("assert"); +'use strict'; + var config = require("../lib/config"); var helper = require("../helper"); var redis = config.redis; diff --git a/test/commands/multi.spec.js b/test/commands/multi.spec.js index f99e85211f4..651389e6ce1 100644 --- a/test/commands/multi.spec.js +++ b/test/commands/multi.spec.js @@ -1,4 +1,5 @@ -var async = require('async'); +'use strict'; + var assert = require('assert'); var config = require("../lib/config"); var helper = require('../helper'); @@ -58,7 +59,7 @@ describe("The 'multi' method", function () { }); it('roles back a transaction when one command in a sequence of commands fails', function (done) { - var name = "MULTI_1", multi1, multi2; + var multi1, multi2; // Provoke an error at queue time multi1 = client.multi(); @@ -91,7 +92,7 @@ describe("The 'multi' method", function () { // I'm unclear as to the difference between this test in the test above, // perhaps @mranney can clarify? it('roles back a transaction when an error was provoked at queue time', function (done) { - multi1 = client.multi(); + var multi1 = client.multi(); multi1.mset("multifoo_8", "10", "multibar_8", "20", helper.isString("OK")); multi1.set("foo2", helper.isError()); multi1.set("foo3", helper.isError()); @@ -108,7 +109,7 @@ describe("The 'multi' method", function () { } // Confirm that the previous command, while containing an error, still worked. - multi2 = client.multi(); + var multi2 = client.multi(); multi2.incr("multibar_8", helper.isNumber(multibar_expected)); multi2.incr("multifoo_8", helper.isNumber(multifoo_expected)); multi2.exec(function (err, replies) { diff --git a/test/commands/randomkey.test.js b/test/commands/randomkey.test.js index ef11253adb3..4c81ed2edb9 100644 --- a/test/commands/randomkey.test.js +++ b/test/commands/randomkey.test.js @@ -1,3 +1,5 @@ +'use strict'; + var assert = require("assert"); var config = require("../lib/config"); var helper = require("../helper"); diff --git a/test/commands/rename.spec.js b/test/commands/rename.spec.js index 3389fa59da2..6196994c585 100644 --- a/test/commands/rename.spec.js +++ b/test/commands/rename.spec.js @@ -1,4 +1,5 @@ -var assert = require("assert"); +'use strict'; + var config = require("../lib/config"); var helper = require("../helper"); var redis = config.redis; diff --git a/test/commands/renamenx.spec.js b/test/commands/renamenx.spec.js index 233d1aaba5f..57eb6dab074 100644 --- a/test/commands/renamenx.spec.js +++ b/test/commands/renamenx.spec.js @@ -1,4 +1,5 @@ -var assert = require("assert"); +'use strict'; + var config = require("../lib/config"); var helper = require("../helper"); var redis = config.redis; diff --git a/test/commands/sadd.spec.js b/test/commands/sadd.spec.js index a6a8ac43300..1761cd6cf3e 100644 --- a/test/commands/sadd.spec.js +++ b/test/commands/sadd.spec.js @@ -1,3 +1,5 @@ +'use strict'; + var assert = require("assert"); var config = require("../lib/config"); var helper = require("../helper"); diff --git a/test/commands/scard.spec.js b/test/commands/scard.spec.js index 180da0a98ef..423cba98057 100644 --- a/test/commands/scard.spec.js +++ b/test/commands/scard.spec.js @@ -1,4 +1,5 @@ -var assert = require("assert"); +'use strict'; + var config = require("../lib/config"); var helper = require("../helper"); var redis = config.redis; diff --git a/test/commands/script.spec.js b/test/commands/script.spec.js index 80cbf1abc0b..507c0befb38 100644 --- a/test/commands/script.spec.js +++ b/test/commands/script.spec.js @@ -1,3 +1,5 @@ +'use strict'; + var assert = require("assert"); var config = require("../lib/config"); var crypto = require("crypto"); diff --git a/test/commands/sdiff.spec.js b/test/commands/sdiff.spec.js index 0e233f26ff7..8057bc15af4 100644 --- a/test/commands/sdiff.spec.js +++ b/test/commands/sdiff.spec.js @@ -1,3 +1,5 @@ +'use strict'; + var assert = require("assert"); var config = require("../lib/config"); var helper = require("../helper"); diff --git a/test/commands/sdiffstore.spec.js b/test/commands/sdiffstore.spec.js index c497cf47e2f..7546c446ce5 100644 --- a/test/commands/sdiffstore.spec.js +++ b/test/commands/sdiffstore.spec.js @@ -1,3 +1,5 @@ +'use strict'; + var assert = require("assert"); var config = require("../lib/config"); var helper = require("../helper"); diff --git a/test/commands/select.spec.js b/test/commands/select.spec.js index f12eb07d976..25f45f5d53d 100644 --- a/test/commands/select.spec.js +++ b/test/commands/select.spec.js @@ -1,4 +1,5 @@ -var async = require('async'); +'use strict'; + var assert = require('assert'); var config = require("../lib/config"); var helper = require('../helper'); diff --git a/test/commands/set.spec.js b/test/commands/set.spec.js index 072342db4b7..a6e49d0ab3d 100644 --- a/test/commands/set.spec.js +++ b/test/commands/set.spec.js @@ -1,4 +1,5 @@ -var async = require('async'); +'use strict'; + var assert = require('assert'); var config = require("../lib/config"); var helper = require('../helper'); diff --git a/test/commands/setex.spec.js b/test/commands/setex.spec.js index b4e6189168b..71e55b20e73 100644 --- a/test/commands/setex.spec.js +++ b/test/commands/setex.spec.js @@ -1,3 +1,5 @@ +'use strict'; + var assert = require("assert"); var config = require("../lib/config"); var helper = require("../helper"); diff --git a/test/commands/setnx.spec.js b/test/commands/setnx.spec.js index 9b363a32fd8..20a57593051 100644 --- a/test/commands/setnx.spec.js +++ b/test/commands/setnx.spec.js @@ -1,4 +1,5 @@ -var assert = require("assert"); +'use strict'; + var config = require("../lib/config"); var helper = require("../helper"); var redis = config.redis; diff --git a/test/commands/sinter.spec.js b/test/commands/sinter.spec.js index 6bc87390bb4..59b751fb5fb 100644 --- a/test/commands/sinter.spec.js +++ b/test/commands/sinter.spec.js @@ -1,3 +1,5 @@ +'use strict'; + var assert = require("assert"); var config = require("../lib/config"); var helper = require("../helper"); diff --git a/test/commands/sinterstore.spec.js b/test/commands/sinterstore.spec.js index 7cf783bf25e..38a9ffc39b5 100644 --- a/test/commands/sinterstore.spec.js +++ b/test/commands/sinterstore.spec.js @@ -1,3 +1,5 @@ +'use strict'; + var assert = require("assert"); var config = require("../lib/config"); var helper = require("../helper"); diff --git a/test/commands/sismember.spec.js b/test/commands/sismember.spec.js index fdc00da1dad..1e1d29e5afd 100644 --- a/test/commands/sismember.spec.js +++ b/test/commands/sismember.spec.js @@ -1,4 +1,5 @@ -var assert = require("assert"); +'use strict'; + var config = require("../lib/config"); var helper = require("../helper"); var redis = config.redis; diff --git a/test/commands/slowlog.spec.js b/test/commands/slowlog.spec.js index b2d304911ba..c64b0ebf589 100644 --- a/test/commands/slowlog.spec.js +++ b/test/commands/slowlog.spec.js @@ -1,3 +1,5 @@ +'use strict'; + var assert = require("assert"); var config = require("../lib/config"); var helper = require("../helper"); diff --git a/test/commands/smembers.spec.js b/test/commands/smembers.spec.js index 39bca826bc7..acca8f899de 100644 --- a/test/commands/smembers.spec.js +++ b/test/commands/smembers.spec.js @@ -1,3 +1,5 @@ +'use strict'; + var assert = require("assert"); var config = require("../lib/config"); var helper = require("../helper"); diff --git a/test/commands/smove.spec.js b/test/commands/smove.spec.js index d9ad3c40218..abdc2e476af 100644 --- a/test/commands/smove.spec.js +++ b/test/commands/smove.spec.js @@ -1,4 +1,5 @@ -var assert = require("assert"); +'use strict'; + var config = require("../lib/config"); var helper = require("../helper"); var redis = config.redis; diff --git a/test/commands/sort.spec.js b/test/commands/sort.spec.js index c38dfd46850..868d26a7f27 100644 --- a/test/commands/sort.spec.js +++ b/test/commands/sort.spec.js @@ -1,3 +1,5 @@ +'use strict'; + var assert = require("assert"); var config = require("../lib/config"); var helper = require("../helper"); diff --git a/test/commands/spop.spec.js b/test/commands/spop.spec.js index 75470d55620..0beb4869a1d 100644 --- a/test/commands/spop.spec.js +++ b/test/commands/spop.spec.js @@ -1,3 +1,5 @@ +'use strict'; + var assert = require("assert"); var config = require("../lib/config"); var helper = require("../helper"); diff --git a/test/commands/srem.spec.js b/test/commands/srem.spec.js index af456abf103..a62dd6bc834 100644 --- a/test/commands/srem.spec.js +++ b/test/commands/srem.spec.js @@ -1,3 +1,5 @@ +'use strict'; + var assert = require("assert"); var config = require("../lib/config"); var helper = require("../helper"); diff --git a/test/commands/sunion.spec.js b/test/commands/sunion.spec.js index bf61c5e6804..2477526d0ba 100644 --- a/test/commands/sunion.spec.js +++ b/test/commands/sunion.spec.js @@ -1,3 +1,5 @@ +'use strict'; + var assert = require("assert"); var config = require("../lib/config"); var helper = require("../helper"); diff --git a/test/commands/sunionstore.spec.js b/test/commands/sunionstore.spec.js index 644bb8744b4..a4086de6a35 100644 --- a/test/commands/sunionstore.spec.js +++ b/test/commands/sunionstore.spec.js @@ -1,3 +1,5 @@ +'use strict'; + var assert = require("assert"); var config = require("../lib/config"); var helper = require("../helper"); diff --git a/test/commands/ttl.spec.js b/test/commands/ttl.spec.js index c8988ab05b5..35beaa6bafe 100644 --- a/test/commands/ttl.spec.js +++ b/test/commands/ttl.spec.js @@ -1,3 +1,5 @@ +'use strict'; + var assert = require("assert"); var config = require("../lib/config"); var helper = require("../helper"); diff --git a/test/commands/type.spec.js b/test/commands/type.spec.js index 5c438bbd17c..1410b325dec 100644 --- a/test/commands/type.spec.js +++ b/test/commands/type.spec.js @@ -1,4 +1,5 @@ -var assert = require("assert"); +'use strict'; + var config = require("../lib/config"); var helper = require("../helper"); var redis = config.redis; diff --git a/test/commands/watch.spec.js b/test/commands/watch.spec.js index a4abdce8777..355f31b9b9e 100644 --- a/test/commands/watch.spec.js +++ b/test/commands/watch.spec.js @@ -1,3 +1,5 @@ +'use strict'; + var assert = require("assert"); var config = require("../lib/config"); var helper = require("../helper"); @@ -33,7 +35,7 @@ describe("The 'watch' method", function () { it('does not execute transaction if watched key was modified prior to execution', function (done) { client.watch(watched); client.incr(watched); - multi = client.multi(); + var multi = client.multi(); multi.incr(watched); multi.exec(helper.isNull(done)); }) @@ -45,17 +47,15 @@ describe("The 'watch' method", function () { client.watch(watched); client.incr(watched); - var multi = client.multi() - .incr(watched) - .exec(function (err, replies) { - assert.strictEqual(replies, null, "Aborted transaction multi-bulk reply should be null."); + client.multi().incr(watched).exec(function (err, replies) { + assert.strictEqual(replies, null, "Aborted transaction multi-bulk reply should be null."); - client.get("unwatched", function (err, reply) { - assert.equal(reply, 200, "Expected 200, got " + reply); - return done(err) - }); - }); - }) + client.get("unwatched", function (err, reply) { + assert.equal(reply, 200, "Expected 200, got " + reply); + return done(err); + }); + }); + }); }); }); }); diff --git a/test/helper.js b/test/helper.js index 756ed1d139a..30fc7b24e86 100644 --- a/test/helper.js +++ b/test/helper.js @@ -1,9 +1,18 @@ +'use strict'; + var assert = require("assert"); var path = require('path'); var config = require("./lib/config"); var RedisProcess = require("./lib/redis-process"); var rp; +function startRedis (conf, done) { + RedisProcess.start(function (err, _rp) { + rp = _rp; + return done(err); + }, path.resolve(__dirname, conf)); +} + // don't start redis every time we // include this helper file! if (!process.env.REDIS_TESTS_STARTED) { @@ -108,11 +117,4 @@ module.exports = { process.removeListener('uncaughtException', mochaListener); return mochaListener; } -} - -function startRedis (conf, done) { - RedisProcess.start(function (err, _rp) { - rp = _rp; - return done(err); - }, path.resolve(__dirname, conf)); -} +}; diff --git a/test/lib/config.js b/test/lib/config.js index 296fdc0bf89..c80a8f94204 100644 --- a/test/lib/config.js +++ b/test/lib/config.js @@ -1,3 +1,5 @@ +'use strict'; + // helpers for configuring a redis client in // its various modes, ipV6, ipV4, socket. var redis = require('../../index'); diff --git a/test/lib/redis-process.js b/test/lib/redis-process.js index 32f2045e416..fbf176d5922 100644 --- a/test/lib/redis-process.js +++ b/test/lib/redis-process.js @@ -1,3 +1,5 @@ +'use strict'; + // helper to start and stop the redis process. var cp = require('child_process'); var config = require('./config'); @@ -5,6 +7,26 @@ var fs = require('fs'); var path = require('path'); var tcpPortUsed = require('tcp-port-used'); +// wait for redis to be listening in +// all three modes (ipv4, ipv6, socket). +function waitForRedis (available, cb) { + var ipV4 = false; + var id = setInterval(function () { + tcpPortUsed.check(config.PORT, '127.0.0.1') + .then(function (_ipV4) { + ipV4 = _ipV4; + return tcpPortUsed.check(config.PORT, '::1'); + }) + .then(function (ipV6) { + if (ipV6 === available && ipV4 === available && + fs.existsSync('/tmp/redis.sock') === available) { + clearInterval(id); + return cb(); + } + }); + }, 100); +} + module.exports = { start: function (done, conf) { // spawn redis with our testing configuration. @@ -18,9 +40,9 @@ module.exports = { console.error('failed to starting redis with exit code "' + code + '" ' + 'stop any other redis processes currently running (' + 'hint: lsof -i :6379)'); - process.exit(code) + process.exit(code); } - }) + }); // wait for redis to become available, by // checking the port we bind on. @@ -44,23 +66,3 @@ module.exports = { }); } }; - -// wait for redis to be listening in -// all three modes (ipv4, ipv6, socket). -function waitForRedis (available, cb) { - var ipV4 = false; - var id = setInterval(function () { - tcpPortUsed.check(config.PORT, '127.0.0.1') - .then(function (_ipV4) { - ipV4 = _ipV4; - return tcpPortUsed.check(config.PORT, '::1'); - }) - .then(function (ipV6) { - if (ipV6 === available && ipV4 === available && - fs.existsSync('/tmp/redis.sock') === available) { - clearInterval(id); - return cb(); - } - }); - }, 100); -} diff --git a/test/node_redis.spec.js b/test/node_redis.spec.js index 93b5531473c..999b11f4047 100644 --- a/test/node_redis.spec.js +++ b/test/node_redis.spec.js @@ -1,3 +1,5 @@ +'use strict'; + var async = require("async"); var assert = require("assert"); var config = require("./lib/config"); @@ -249,7 +251,7 @@ describe("The node_redis client", function () { try { domain = require('domain').create(); } catch (err) { - console.log("Skipping " + name + " because this version of node doesn't have domains."); + console.log("Skipping test because this version of node doesn't have domains."); return done(); } @@ -257,7 +259,7 @@ describe("The node_redis client", function () { domain.run(function () { client.set('domain', 'value', function (err, res) { assert.ok(process.domain); - var notFound = res.not.existing.thing; // ohhh nooooo + throw new Error('ohhhh noooo'); }); }); @@ -316,7 +318,7 @@ describe("The node_redis client", function () { }); client2.on("message", function (channel, data) { - if (channel == name) { + if (channel === name) { assert.equal(data, "some message"); throw Error('forced exception'); } @@ -736,7 +738,7 @@ describe("The node_redis client", function () { }); assert.throws(function () { - cli.set('foo', 'bar'); + client.set('foo', 'bar'); }); assert.doesNotThrow(function () { diff --git a/test/parser/javascript.spec.js b/test/parser/javascript.spec.js index 445964371b2..dc519f8468c 100644 --- a/test/parser/javascript.spec.js +++ b/test/parser/javascript.spec.js @@ -1,4 +1,4 @@ -/* global describe, it */ +'use strict'; var assert = require('assert'); var Parser = require("../../lib/parser/javascript").Parser; diff --git a/test/pubsub.spec.js b/test/pubsub.spec.js index 3e6ad9ba687..3a3718b95b1 100644 --- a/test/pubsub.spec.js +++ b/test/pubsub.spec.js @@ -1,3 +1,5 @@ +'use strict'; + var assert = require("assert"); var config = require("./lib/config"); var helper = require("./helper"); @@ -10,10 +12,9 @@ describe("publish/subscribe", function () { describe("using " + parser + " and " + ip, function () { var pub = null; var sub = null; - var channel = "test channel" - var channel2 = "test channel 2" - var message = "test message" - var hash = "test hash"; + var channel = "test channel"; + var channel2 = "test channel 2"; + var message = "test message"; beforeEach(function (done) { var pubConnected; @@ -166,7 +167,7 @@ describe("publish/subscribe", function () { }); describe('unsubscribe', function () { - it('fires an unsubscribe event', function () { + it('fires an unsubscribe event', function (done) { sub.on("subscribe", function (chnl, count) { sub.unsubscribe(channel) }); diff --git a/test/queue.spec.js b/test/queue.spec.js index f0b3c316489..51a6bd7bb25 100644 --- a/test/queue.spec.js +++ b/test/queue.spec.js @@ -1,3 +1,5 @@ +'use strict'; + var assert = require("assert"); var Queue = require('../lib/queue'); From 9acbd6c8607829284b5774b7eb469b662dd9fa6a Mon Sep 17 00:00:00 2001 From: Ruben Bridgewater Date: Tue, 1 Sep 2015 21:39:44 +0200 Subject: [PATCH 0296/1748] Add simicolons and remove trailing whitespace --- test/auth.spec.js | 5 +++-- test/commands/client.spec.js | 13 ++++++------- test/commands/eval.spec.js | 4 ++-- test/commands/hmset.spec.js | 4 ++-- test/commands/hset.spec.js | 10 +++++----- test/commands/multi.spec.js | 2 +- test/commands/renamenx.spec.js | 2 +- test/commands/script.spec.js | 19 +++++++++---------- test/commands/select.spec.js | 2 +- test/commands/sort.spec.js | 2 +- test/commands/watch.spec.js | 7 +++---- test/helper.js | 2 +- test/lib/config.js | 2 +- test/lib/redis-process.js | 2 +- test/lib/unref.js | 4 ++-- test/node_redis.spec.js | 20 ++++++++------------ test/pubsub.spec.js | 16 ++++++++-------- 17 files changed, 55 insertions(+), 61 deletions(-) diff --git a/test/auth.spec.js b/test/auth.spec.js index 5dee3d83a32..c049df5d30a 100644 --- a/test/auth.spec.js +++ b/test/auth.spec.js @@ -36,7 +36,7 @@ describe("client authentication", function () { client = redis.createClient.apply(redis.createClient, args); client.once('error', function (error) { - assert.ok(/ERR invalid password/.test(error)) + assert.ok(/ERR invalid password/.test(error)); return done(); }); @@ -47,7 +47,8 @@ describe("client authentication", function () { it('allows auth to be provided as part of redis url', function (done) { client = redis.createClient('redis://foo:' + auth + '@' + config.HOST[ip] + ':' + config.PORT); client.on("ready", function () { - return done() }); + return done(); + }); }); } diff --git a/test/commands/client.spec.js b/test/commands/client.spec.js index a9188cdac99..50d0f4899ff 100644 --- a/test/commands/client.spec.js +++ b/test/commands/client.spec.js @@ -19,11 +19,10 @@ describe("The 'client' method", function () { client.once("connect", function () { client.flushdb(function (err) { if (!helper.serverVersionAtLeast(client, [2, 4, 0])) { - err = Error('script not supported in redis <= 2.4.0') + err = Error('script not supported in redis <= 2.4.0'); } return done(err); - - }) + }); }); }); @@ -39,15 +38,15 @@ describe("The 'client' method", function () { it("lists connected clients when invoked with multi's chaining syntax", function (done) { client.multi().client("list").exec(function(err, results) { assert(pattern.test(results[0]), "expected string '" + results + "' to match " + pattern.toString()); - return done() - }) + return done(); + }); }); it("lists connected clients when invoked with multi's array syntax", function (done) { client.multi().client("list").exec(function(err, results) { assert(pattern.test(results[0]), "expected string '" + results + "' to match " + pattern.toString()); - return done() - }) + return done(); + }); }); }); }); diff --git a/test/commands/eval.spec.js b/test/commands/eval.spec.js index 229e9c51b04..0c3edb08fa2 100644 --- a/test/commands/eval.spec.js +++ b/test/commands/eval.spec.js @@ -19,10 +19,10 @@ describe("The 'eval' method", function () { client.once("connect", function () { client.flushdb(function (err) { if (!helper.serverVersionAtLeast(client, [2, 5, 0])) { - err = Error('exec not supported in redis <= 2.5.0') + err = Error('exec not supported in redis <= 2.5.0'); } return done(err); - }) + }); }); }); diff --git a/test/commands/hmset.spec.js b/test/commands/hmset.spec.js index 6b6f980aa99..859f0c26abc 100644 --- a/test/commands/hmset.spec.js +++ b/test/commands/hmset.spec.js @@ -27,7 +27,7 @@ describe("The 'hmset' method", function () { assert.equal(obj['0123456789'], 'abcdefghij'); assert.equal(obj['some manner of key'], 'a type of value'); return done(err); - }) + }); }); it('handles object-style syntax', function (done) { @@ -36,7 +36,7 @@ describe("The 'hmset' method", function () { assert.equal(obj['0123456789'], 'abcdefghij'); assert.equal(obj['some manner of key'], 'a type of value'); return done(err); - }) + }); }); it('handles object-style syntax and the key being a number', function (done) { diff --git a/test/commands/hset.spec.js b/test/commands/hset.spec.js index 9f55606f35f..7155e3297dd 100644 --- a/test/commands/hset.spec.js +++ b/test/commands/hset.spec.js @@ -47,11 +47,11 @@ describe("The 'hset' method", function () { }); it('does not error when a buffer and array are set as fields on the same hash', function (done) { - var hash = "test hash" - var field1 = "buffer" - var value1 = new Buffer("abcdefghij") - var field2 = "array" - var value2 = ["array contents"] + var hash = "test hash"; + var field1 = "buffer"; + var value1 = new Buffer("abcdefghij"); + var field2 = "array"; + var value2 = ["array contents"]; client.HMSET(hash, field1, value1, field2, value2, helper.isString("OK", done)); }); diff --git a/test/commands/multi.spec.js b/test/commands/multi.spec.js index 651389e6ce1..41ae51f075b 100644 --- a/test/commands/multi.spec.js +++ b/test/commands/multi.spec.js @@ -50,7 +50,7 @@ describe("The 'multi' method", function () { client.once("connect", function () { client.flushdb(function (err) { return done(err); - }) + }); }); }); diff --git a/test/commands/renamenx.spec.js b/test/commands/renamenx.spec.js index 57eb6dab074..454d046727c 100644 --- a/test/commands/renamenx.spec.js +++ b/test/commands/renamenx.spec.js @@ -32,7 +32,7 @@ describe("The 'renamenx' method", function () { client.renamenx('foo', 'foo2', helper.isNumber(0)); client.exists('foo', helper.isNumber(1)); client.exists(['foo2'], helper.isNumber(1, done)); - }) + }); afterEach(function () { client.end(); diff --git a/test/commands/script.spec.js b/test/commands/script.spec.js index 507c0befb38..254ab6045f5 100644 --- a/test/commands/script.spec.js +++ b/test/commands/script.spec.js @@ -21,11 +21,10 @@ describe("The 'script' method", function () { client.once("connect", function () { client.flushdb(function (err) { if (!helper.serverVersionAtLeast(client, [2, 6, 0])) { - err = Error('script not supported in redis <= 2.6.0') + err = Error('script not supported in redis <= 2.6.0'); } return done(err); - - }) + }); }); }); @@ -42,21 +41,21 @@ describe("The 'script' method", function () { it('allows a loaded script to be evaluated', function (done) { client.evalsha(commandSha, 0, helper.isString('99', done)); - }) + }); it('allows a script to be loaded as part of a chained transaction', function (done) { client.multi().script("load", command).exec(function(err, result) { assert.strictEqual(result[0], commandSha); - return done() - }) - }) + return done(); + }); + }); it("allows a script to be loaded using a transaction's array syntax", function (done) { client.multi([['script', 'load', command]]).exec(function(err, result) { assert.strictEqual(result[0], commandSha); - return done() - }) - }) + return done(); + }); + }); }); }); }); diff --git a/test/commands/select.spec.js b/test/commands/select.spec.js index 25f45f5d53d..bdc87668d18 100644 --- a/test/commands/select.spec.js +++ b/test/commands/select.spec.js @@ -70,7 +70,7 @@ describe("The 'select' method", function () { it("emits an error", function (done) { assert.strictEqual(client.selected_db, null, "default db should be null"); client.select(9999, function (err) { - assert.equal(err.message, 'ERR invalid DB index') + assert.equal(err.message, 'ERR invalid DB index'); return done(); }); }); diff --git a/test/commands/sort.spec.js b/test/commands/sort.spec.js index 868d26a7f27..d50f176b648 100644 --- a/test/commands/sort.spec.js +++ b/test/commands/sort.spec.js @@ -17,7 +17,7 @@ describe("The 'sort' method", function () { client.once("error", done); client.once("connect", function () { client.flushdb(); - setupData(client, done) + setupData(client, done); }); }); diff --git a/test/commands/watch.spec.js b/test/commands/watch.spec.js index 355f31b9b9e..6cbc63e2f49 100644 --- a/test/commands/watch.spec.js +++ b/test/commands/watch.spec.js @@ -20,11 +20,10 @@ describe("The 'watch' method", function () { client.once("connect", function () { client.flushdb(function (err) { if (!helper.serverVersionAtLeast(client, [2, 2, 0])) { - err = Error('some watch commands not supported in redis <= 2.2.0') + err = Error('some watch commands not supported in redis <= 2.2.0'); } return done(err); - - }) + }); }); }); @@ -38,7 +37,7 @@ describe("The 'watch' method", function () { var multi = client.multi(); multi.incr(watched); multi.exec(helper.isNull(done)); - }) + }); it('successfully modifies other keys independently of transaction', function (done) { client.set("unwatched", 200); diff --git a/test/helper.js b/test/helper.js index 30fc7b24e86..36878f65166 100644 --- a/test/helper.js +++ b/test/helper.js @@ -20,7 +20,7 @@ if (!process.env.REDIS_TESTS_STARTED) { before(function (done) { startRedis('./conf/redis.conf', done); - }) + }); after(function (done) { if (rp) rp.stop(done); diff --git a/test/lib/config.js b/test/lib/config.js index c80a8f94204..d42dc21c9a7 100644 --- a/test/lib/config.js +++ b/test/lib/config.js @@ -16,7 +16,7 @@ var config = { opts = opts || {}; if (ip.match(/\.sock/)) { - args.push(ip) + args.push(ip); } else { args.push(config.PORT); args.push(config.HOST[ip]); diff --git a/test/lib/redis-process.js b/test/lib/redis-process.js index fbf176d5922..176aa2e052d 100644 --- a/test/lib/redis-process.js +++ b/test/lib/redis-process.js @@ -58,7 +58,7 @@ module.exports = { } waitForRedis(false, function () { return done(error); - }) + }); }); rp.kill("SIGTERM"); } diff --git a/test/lib/unref.js b/test/lib/unref.js index 46b03643a38..45877a493ee 100644 --- a/test/lib/unref.js +++ b/test/lib/unref.js @@ -5,8 +5,8 @@ var redis = require("../../"); var HOST = process.argv[2] || '127.0.0.1'; -var PORT = process.argv[3] -var args = PORT ? [PORT, HOST] : [HOST] +var PORT = process.argv[3]; +var args = PORT ? [PORT, HOST] : [HOST]; var c = redis.createClient.apply(redis, args); c.unref(); diff --git a/test/node_redis.spec.js b/test/node_redis.spec.js index 999b11f4047..e5058783142 100644 --- a/test/node_redis.spec.js +++ b/test/node_redis.spec.js @@ -3,7 +3,7 @@ var async = require("async"); var assert = require("assert"); var config = require("./lib/config"); -var helper = require('./helper') +var helper = require('./helper'); var fork = require("child_process").fork; var redis = config.redis; @@ -110,7 +110,7 @@ describe("The node_redis client", function () { client = redis.createClient.apply(redis.createClient, args); client.once("error", done); client.once("connect", function () { - client.flushdb(done) + client.flushdb(done); }); }); @@ -124,23 +124,19 @@ describe("The node_redis client", function () { client.on("connect", function on_connect() { async.parallel([function (cb) { client.get("recon 1", function (err, res) { - helper.isString("one")(err, res); - cb(); + helper.isString("one", cb)(err, res); }); }, function (cb) { client.get("recon 1", function (err, res) { - helper.isString("one")(err, res); - cb(); + helper.isString("one", cb)(err, res); }); }, function (cb) { client.get("recon 2", function (err, res) { - helper.isString("two")(err, res); - cb(); + helper.isString("two", cb)(err, res); }); }, function (cb) { client.get("recon 2", function (err, res) { - helper.isString("two")(err, res); - cb(); + helper.isString("two", cb)(err, res); }); }], function (err, results) { client.removeListener("connect", on_connect); @@ -266,7 +262,7 @@ describe("The node_redis client", function () { // this is the expected and desired behavior domain.on('error', function (err) { domain.exit(); - return done() + return done(); }); } }); @@ -680,7 +676,7 @@ describe("The node_redis client", function () { }); it("sets upper bound on how long client waits before reconnecting", function (done) { - var time = new Date().getTime() + var time = new Date().getTime(); var reconnecting = false; client = redis.createClient.apply(redis.createClient, args); diff --git a/test/pubsub.spec.js b/test/pubsub.spec.js index 3a3718b95b1..99c75987c7a 100644 --- a/test/pubsub.spec.js +++ b/test/pubsub.spec.js @@ -46,7 +46,7 @@ describe("publish/subscribe", function () { it('fires a subscribe event for each channel subscribed to', function (done) { sub.on("subscribe", function (chnl, count) { if (chnl === channel2) { - assert.equal(2, count) + assert.equal(2, count); return done(); } }); @@ -169,7 +169,7 @@ describe("publish/subscribe", function () { describe('unsubscribe', function () { it('fires an unsubscribe event', function (done) { sub.on("subscribe", function (chnl, count) { - sub.unsubscribe(channel) + sub.unsubscribe(channel); }); sub.subscribe(channel); @@ -183,7 +183,7 @@ describe("publish/subscribe", function () { it('puts client back into write mode', function (done) { sub.on("subscribe", function (chnl, count) { - sub.unsubscribe(channel) + sub.unsubscribe(channel); }); sub.subscribe(channel); @@ -191,10 +191,10 @@ describe("publish/subscribe", function () { sub.on("unsubscribe", function (chnl, count) { pub.incr("foo", helper.isNumber(1, done)); }); - }) + }); it('does not complain when unsubscribe is called and there are no subscriptions', function () { - sub.unsubscribe() + sub.unsubscribe(); }); it('executes callback when unsubscribe is called and there are no subscriptions', function (done) { @@ -217,15 +217,15 @@ describe("publish/subscribe", function () { assert.strictEqual(channel, '/foo'); assert.strictEqual(message, 'hello world'); return done(); - }) + }); pub.publish('/foo', 'hello world'); }); }); describe('punsubscribe', function () { it('does not complain when punsubscribe is called and there are no subscriptions', function () { - sub.punsubscribe() - }) + sub.punsubscribe(); + }); it('executes callback when punsubscribe is called and there are no subscriptions', function (done) { // test hangs on older versions of redis, so skip From ba779ac361d9128d8ab98e3b7792a51eb285e593 Mon Sep 17 00:00:00 2001 From: Ruben Bridgewater Date: Tue, 1 Sep 2015 21:45:54 +0200 Subject: [PATCH 0297/1748] Some small code style changes Revert some done stuff --- index.js | 2 +- test/commands/mset.spec.js | 6 +++--- test/node_redis.spec.js | 12 ++++++++---- 3 files changed, 12 insertions(+), 8 deletions(-) diff --git a/index.js b/index.js index 2466130d8f2..910b5bcf912 100644 --- a/index.js +++ b/index.js @@ -955,7 +955,7 @@ RedisClient.prototype.select = function (db, callback) { if (err === null) { self.selected_db = db; } - if (typeof(callback) === 'function') { + if (typeof callback === 'function') { callback(err, res); } else if (err) { self.emit('error', err); diff --git a/test/commands/mset.spec.js b/test/commands/mset.spec.js index 950e6870a6e..e8348713ca8 100644 --- a/test/commands/mset.spec.js +++ b/test/commands/mset.spec.js @@ -88,9 +88,9 @@ describe("The 'mset' method", function () { describe("and no callback is specified", function () { describe("with valid parameters", function () { it("sets the value correctly", function (done) { - client.mset(key, value, key2, value2); - client.get(key, helper.isString(value)); - client.get(key2, helper.isString(value2, done)); + client.mset(key, value2, key2, value); + client.get(key, helper.isString(value2)); + client.get(key2, helper.isString(value, done)); }); }); diff --git a/test/node_redis.spec.js b/test/node_redis.spec.js index e5058783142..ea792b946a0 100644 --- a/test/node_redis.spec.js +++ b/test/node_redis.spec.js @@ -124,19 +124,23 @@ describe("The node_redis client", function () { client.on("connect", function on_connect() { async.parallel([function (cb) { client.get("recon 1", function (err, res) { - helper.isString("one", cb)(err, res); + helper.isString("one")(err, res); + cb(); }); }, function (cb) { client.get("recon 1", function (err, res) { - helper.isString("one", cb)(err, res); + helper.isString("one")(err, res); + cb(); }); }, function (cb) { client.get("recon 2", function (err, res) { - helper.isString("two", cb)(err, res); + helper.isString("two")(err, res); + cb(); }); }, function (cb) { client.get("recon 2", function (err, res) { - helper.isString("two", cb)(err, res); + helper.isString("two")(err, res); + cb(); }); }], function (err, results) { client.removeListener("connect", on_connect); From 1ae280dcec16cfc8ebb52a162468059224fec9f3 Mon Sep 17 00:00:00 2001 From: Ruben Bridgewater Date: Tue, 1 Sep 2015 21:47:15 +0200 Subject: [PATCH 0298/1748] Fix some tests and deactivate broken ones --- test/commands/hmget.spec.js | 3 +-- test/commands/mset.spec.js | 31 ++++++----------------- test/commands/set.spec.js | 22 ---------------- test/commands/setnx.spec.js | 1 - test/helper.js | 2 +- test/node_redis.spec.js | 50 +++++-------------------------------- 6 files changed, 15 insertions(+), 94 deletions(-) diff --git a/test/commands/hmget.spec.js b/test/commands/hmget.spec.js index 1b5387dc32b..af673bfac6b 100644 --- a/test/commands/hmget.spec.js +++ b/test/commands/hmget.spec.js @@ -18,8 +18,7 @@ describe("The 'hmget' method", function () { client.once("error", done); client.once("connect", function () { client.flushdb(); - client.HMSET(hash, {"0123456789": "abcdefghij", "some manner of key": "a type of value"}, helper.isString('OK')); - return done(); + client.HMSET(hash, {"0123456789": "abcdefghij", "some manner of key": "a type of value"}, helper.isString('OK', done)); }); }); diff --git a/test/commands/mset.spec.js b/test/commands/mset.spec.js index e8348713ca8..2ef9726dcde 100644 --- a/test/commands/mset.spec.js +++ b/test/commands/mset.spec.js @@ -60,9 +60,13 @@ describe("The 'mset' method", function () { describe("and a callback is specified", function () { describe("with valid parameters", function () { it("sets the value correctly", function (done) { - client.mset(key, value, key2, value2); - client.get(key, helper.isString(value)); - client.get(key2, helper.isString(value2, done)); + client.mset(key, value, key2, value2, function(err) { + if (err) { + return done(err); + } + client.get(key, helper.isString(value)); + client.get(key2, helper.isString(value2, done)); + }); }); }); @@ -75,14 +79,6 @@ describe("The 'mset' method", function () { }); }); - describe("with undefined 'key' and defined 'value' parameters", function () { - it("reports an error", function () { - client.mset(undefined, value, undefined, value2, function (err, res) { - helper.isError()(err, null); - done(); - }); - }); - }); }); describe("and no callback is specified", function () { @@ -108,19 +104,6 @@ describe("The 'mset' method", function () { client.mset(); }); }); - - describe("with undefined 'key' and defined 'value' parameters", function () { - it("throws an error", function () { - var mochaListener = helper.removeMochaListener(); - - process.once('uncaughtException', function (err) { - process.on('uncaughtException', mochaListener); - helper.isError()(err, null); - }); - - client.mset(undefined, value, undefined, value2); - }); - }); }); }); }); diff --git a/test/commands/set.spec.js b/test/commands/set.spec.js index a6e49d0ab3d..1674051c8f0 100644 --- a/test/commands/set.spec.js +++ b/test/commands/set.spec.js @@ -76,15 +76,6 @@ describe("The 'set' method", function () { }); }); }); - - describe("with undefined 'key' and defined 'value' parameters", function () { - it("reports an error", function () { - client.set(undefined, value, function (err, res) { - helper.isError()(err, null); - done(); - }); - }); - }); }); describe("and no callback is specified", function () { @@ -132,19 +123,6 @@ describe("The 'set' method", function () { }, 100); }); }); - - describe("with undefined 'key' and defined 'value' parameters", function () { - it("throws an error", function () { - var mochaListener = helper.removeMochaListener(); - - process.once('uncaughtException', function (err) { - process.on('uncaughtException', mochaListener); - helper.isError()(err, null); - }); - - client.set(undefined, value); - }); - }); }); }); }); diff --git a/test/commands/setnx.spec.js b/test/commands/setnx.spec.js index 20a57593051..8e426d6c017 100644 --- a/test/commands/setnx.spec.js +++ b/test/commands/setnx.spec.js @@ -28,7 +28,6 @@ describe("The 'setnx' method", function () { client.set('foo', 'bar', helper.isString('OK')); client.setnx('foo', 'banana', helper.isNumber(0)); client.get('foo', helper.isString('bar', done)); - return done(); }); afterEach(function () { diff --git a/test/helper.js b/test/helper.js index 36878f65166..aec927ce993 100644 --- a/test/helper.js +++ b/test/helper.js @@ -58,7 +58,7 @@ module.exports = { }, isError: function (done) { return function (err, results) { - assert.notEqual(err, null, "err is null, but an error is expected here."); + assert(err instanceof Error, "err is not instance of 'Error', but an error is expected here."); if (done) return done(); }; }, diff --git a/test/node_redis.spec.js b/test/node_redis.spec.js index ea792b946a0..7b90fd38223 100644 --- a/test/node_redis.spec.js +++ b/test/node_redis.spec.js @@ -188,54 +188,16 @@ describe("The node_redis client", function () { // Does not pass. // "Connection in subscriber mode, only subscriber commands may be used" it("reconnects, unsubscribes, and can retrieve the pre-existing data", function (done) { - client.on("reconnecting", function on_recon(params) { - client.on("ready", function on_connect() { - client.unsubscribe(helper.isNotError()); - - client.on('unsubscribe', function (channel, count) { - // we should now be out of subscriber mode. - client.set('foo', 'bar', helper.isNumber(1)); - return done(); - }); - }); - }); + client.on("ready", function on_connect() { + client.unsubscribe(helper.isNotError()); - client.set("recon 1", "one"); - client.subscribe("recon channel", function (err, res) { - // Do not do this in normal programs. This is to simulate the server closing on us. - // For orderly shutdown in normal programs, do client.quit() - client.stream.destroy(); - }); - }); - - it("remains subscribed", function () { - var client2 = redis.createClient.apply(redis.createClient, args); - - client.on("reconnecting", function on_recon(params) { - client.on("ready", function on_connect() { - async.parallel([function (cb) { - client.on("message", function (channel, message) { - try { - helper.isString("recon channel")(null, channel); - helper.isString("a test message")(null, message); - } catch (err) { - cb(err); - } - }); - - client2.subscribe("recon channel", function (err, res) { - if (err) { - cb(err); - return; - } - client2.publish("recon channel", "a test message"); - }); - }], function (err, results) { - done(err); - }); + client.on('unsubscribe', function (channel, count) { + // we should now be out of subscriber mode. + client.set('foo', 'bar', helper.isString('OK', done)); }); }); + client.set("recon 1", "one"); client.subscribe("recon channel", function (err, res) { // Do not do this in normal programs. This is to simulate the server closing on us. // For orderly shutdown in normal programs, do client.quit() From 5cbd5b8ffc38fce393f49ee6e323818c26798a47 Mon Sep 17 00:00:00 2001 From: Ruben Bridgewater Date: Wed, 2 Sep 2015 13:00:49 +0200 Subject: [PATCH 0299/1748] Run jshint before starting the tests --- package.json | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/package.json b/package.json index f6bbc41e732..250bd399f7d 100644 --- a/package.json +++ b/package.json @@ -11,7 +11,7 @@ "main": "./index.js", "scripts": { "coverage": "nyc report --reporter=text-lcov | coveralls", - "test": "nyc ./node_modules/.bin/_mocha ./test/*.js ./test/commands/*.js ./test/parser/*.js --timeout=8000", + "test": "./node_modules/.bin/jshint *.js **/*.js **/**/*.js --exclude=node_modules/**/* && nyc ./node_modules/.bin/_mocha ./test/*.js ./test/commands/*.js ./test/parser/*.js --timeout=8000", "jshint": "./node_modules/.bin/jshint *.js **/*.js **/**/*.js --exclude=node_modules/**/*" }, "devDependencies": { From fef3891184f4b22f83e39873f3ad5c0d7ef0dbcc Mon Sep 17 00:00:00 2001 From: Ruben Bridgewater Date: Wed, 2 Sep 2015 16:09:07 +0200 Subject: [PATCH 0300/1748] Coverage task and jshintignore file added --- .jshintignore | 4 ++++ .travis.yml | 2 +- package.json | 7 ++++--- 3 files changed, 9 insertions(+), 4 deletions(-) create mode 100644 .jshintignore diff --git a/.jshintignore b/.jshintignore new file mode 100644 index 00000000000..ed454a5bac6 --- /dev/null +++ b/.jshintignore @@ -0,0 +1,4 @@ +node_modules/** +coverage/** +**.md +**.log \ No newline at end of file diff --git a/.travis.yml b/.travis.yml index 7240cf7d48c..495b45b6ffd 100644 --- a/.travis.yml +++ b/.travis.yml @@ -13,4 +13,4 @@ node_js: - "0.12" - "4.0" - "iojs" -after_success: npm run coverage +after_success: npm run coveralls diff --git a/package.json b/package.json index 250bd399f7d..3d5e9bab643 100644 --- a/package.json +++ b/package.json @@ -10,9 +10,10 @@ "license": "MIT", "main": "./index.js", "scripts": { - "coverage": "nyc report --reporter=text-lcov | coveralls", - "test": "./node_modules/.bin/jshint *.js **/*.js **/**/*.js --exclude=node_modules/**/* && nyc ./node_modules/.bin/_mocha ./test/*.js ./test/commands/*.js ./test/parser/*.js --timeout=8000", - "jshint": "./node_modules/.bin/jshint *.js **/*.js **/**/*.js --exclude=node_modules/**/*" + "coveralls": "nyc report --reporter=text-lcov | coveralls", + "coverage": "nyc report --reporter=html", + "test": "./node_modules/.bin/jshint * && nyc ./node_modules/.bin/_mocha ./test/*.js ./test/commands/*.js ./test/parser/*.js --timeout=8000", + "jshint": "./node_modules/.bin/jshint *" }, "devDependencies": { "async": "^1.3.0", From 463c7fd0621469f24a0295712dbab54085edb7c8 Mon Sep 17 00:00:00 2001 From: Ruben Bridgewater Date: Wed, 2 Sep 2015 18:17:26 +0200 Subject: [PATCH 0301/1748] Remove unused dependencies --- package.json | 2 -- 1 file changed, 2 deletions(-) diff --git a/package.json b/package.json index 3d5e9bab643..9f3e1e86ac4 100644 --- a/package.json +++ b/package.json @@ -17,7 +17,6 @@ }, "devDependencies": { "async": "^1.3.0", - "colors": "~0.6.0-1", "coveralls": "^2.11.2", "hiredis": "^0.4.1", "jshint": "^2.8.0", @@ -25,7 +24,6 @@ "mocha": "^2.2.5", "nyc": "^3.0.0", "tcp-port-used": "^0.1.2", - "underscore": "~1.4.4", "uuid": "^2.0.1" }, "repository": { From da8c2603c4fcc13506dc88b29d9032d19c949e8f Mon Sep 17 00:00:00 2001 From: Ruben Bridgewater Date: Fri, 4 Sep 2015 17:23:29 +0200 Subject: [PATCH 0302/1748] Reduce timeouts --- test/commands/expire.spec.js | 2 +- test/commands/ttl.spec.js | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/test/commands/expire.spec.js b/test/commands/expire.spec.js index ce3af95dc26..4173a3a4bdf 100644 --- a/test/commands/expire.spec.js +++ b/test/commands/expire.spec.js @@ -24,7 +24,7 @@ describe("The 'expire' method", function () { client.EXPIRE(["expiry key", "1"], helper.isNumber(1)); setTimeout(function () { client.exists(["expiry key"], helper.isNumber(0, done)); - }, 1500); + }, 1100); }); afterEach(function () { diff --git a/test/commands/ttl.spec.js b/test/commands/ttl.spec.js index 35beaa6bafe..5e1f56150f7 100644 --- a/test/commands/ttl.spec.js +++ b/test/commands/ttl.spec.js @@ -28,7 +28,7 @@ describe("The 'ttl' method", function () { assert.ok(ttl > 50 && ttl <= 100); return done(err); }); - }, 500); + }, 200); }); afterEach(function () { From 4c6b84315eee277cf1bcbc704540ac9656ca0785 Mon Sep 17 00:00:00 2001 From: Ruben Bridgewater Date: Thu, 3 Sep 2015 23:42:00 +0200 Subject: [PATCH 0303/1748] Tiny speedup by removing command.toLowerCase() This is not necessary as the command itself is only used from inside the code and as they are (now) all lower case it is safe to remove the toLowerCase --- index.js | 15 +++++++-------- test/commands/client.spec.js | 2 +- test/commands/dbsize.spec.js | 2 +- test/commands/del.spec.js | 2 +- test/commands/eval.spec.js | 2 +- test/commands/exits.spec.js | 2 +- test/commands/flushdb.spec.js | 2 +- test/commands/get.spec.js | 2 +- test/commands/getset.spec.js | 2 +- test/commands/hgetall.spec.js | 2 +- test/commands/hincrby.spec.js | 2 +- test/commands/hmget.spec.js | 2 +- test/commands/hmset.spec.js | 2 +- test/commands/hset.spec.js | 2 +- test/commands/incr.spec.js | 2 +- test/commands/keys.spec.js | 2 +- test/commands/mget.spec.js | 2 +- test/commands/msetnx.spec.js | 2 +- test/commands/multi.spec.js | 2 +- test/commands/rename.spec.js | 2 +- test/commands/renamenx.spec.js | 2 +- test/commands/script.spec.js | 2 +- test/commands/select.spec.js | 2 +- test/commands/set.spec.js | 2 +- test/commands/setex.spec.js | 2 +- test/commands/setnx.spec.js | 2 +- test/commands/sinter.spec.js | 2 +- test/commands/sismember.spec.js | 2 +- test/commands/slowlog.spec.js | 2 +- test/commands/smove.spec.js | 2 +- test/commands/sort.spec.js | 2 +- test/commands/srem.spec.js | 2 +- test/commands/type.spec.js | 2 +- test/commands/watch.spec.js | 2 +- 34 files changed, 40 insertions(+), 41 deletions(-) diff --git a/index.js b/index.js index 5397e94e0d6..a01073df98a 100644 --- a/index.js +++ b/index.js @@ -624,14 +624,14 @@ RedisClient.prototype.return_reply = function (reply) { if (command_obj && !command_obj.sub_command) { if (typeof command_obj.callback === "function") { - if (this.options.detect_buffers && command_obj.buffer_args === false && 'exec' !== command_obj.command.toLowerCase()) { + if (this.options.detect_buffers && command_obj.buffer_args === false && 'exec' !== command_obj.command) { // If detect_buffers option was specified, then the reply from the parser will be Buffers. // If this command did not use Buffer arguments, then convert the reply to Strings here. reply = reply_to_strings(reply); } // TODO - confusing and error-prone that hgetall is special cased in two places - if (reply && 'hgetall' === command_obj.command.toLowerCase()) { + if (reply && 'hgetall' === command_obj.command) { reply = reply_to_object(reply); } @@ -730,8 +730,7 @@ RedisClient.prototype.send_command = function (command, args, callback) { // client.sadd(arg1, [arg2, arg3, arg4], cb); // converts to: // client.sadd(arg1, arg2, arg3, arg4, cb); - lcaseCommand = command.toLowerCase(); - if ((lcaseCommand === 'sadd' || lcaseCommand === 'srem') && args.length > 0 && Array.isArray(args[args.length - 1])) { + if ((command === 'sadd' || command === 'srem') && args.length > 0 && Array.isArray(args[args.length - 1])) { args = args.slice(0, -1).concat(args[args.length - 1]); } @@ -885,7 +884,7 @@ RedisClient.prototype.end = function () { function Multi(client, args) { this._client = client; - this.queue = [["MULTI"]]; + this.queue = [["multi"]]; if (Array.isArray(args)) { this.queue = this.queue.concat(args); } @@ -1061,7 +1060,7 @@ Multi.prototype.exec = function (callback) { if (args.length === 1 && Array.isArray(args[0])) { args = args[0]; } - if (command.toLowerCase() === 'hmset' && typeof args[1] === 'object') { + if (command === 'hmset' && typeof args[1] === 'object') { obj = args.pop(); Object.keys(obj).forEach(function (key) { args.push(key); @@ -1081,7 +1080,7 @@ Multi.prototype.exec = function (callback) { }, this); // TODO - make this callback part of Multi.prototype instead of creating it each time - return this._client.send_command("EXEC", [], function (err, replies) { + return this._client.send_command("exec", [], function (err, replies) { if (err) { if (callback) { errors.push(new Error(err)); @@ -1106,7 +1105,7 @@ Multi.prototype.exec = function (callback) { } // TODO - confusing and error-prone that hgetall is special cased in two places - if (reply && args[0].toLowerCase() === "hgetall") { + if (reply && args[0] === "hgetall") { replies[i - 1] = reply = reply_to_object(reply); } diff --git a/test/commands/client.spec.js b/test/commands/client.spec.js index 50d0f4899ff..31bb5dc5e71 100644 --- a/test/commands/client.spec.js +++ b/test/commands/client.spec.js @@ -32,7 +32,7 @@ describe("The 'client' method", function () { describe('list', function () { it('lists connected clients', function (done) { - client.client("list", helper.match(pattern, done)); + client.client("LIST", helper.match(pattern, done)); }); it("lists connected clients when invoked with multi's chaining syntax", function (done) { diff --git a/test/commands/dbsize.spec.js b/test/commands/dbsize.spec.js index 5eb6958e9c2..118ba0b15cf 100644 --- a/test/commands/dbsize.spec.js +++ b/test/commands/dbsize.spec.js @@ -59,7 +59,7 @@ describe("The 'dbsize' method", function () { }); it("returns a zero db size", function (done) { - client.dbsize([], function (err, res) { + client.DBSIZE([], function (err, res) { helper.isNotError()(err, res); helper.isType.number()(err, res); assert.strictEqual(res, 0, "Initial db size should be 0"); diff --git a/test/commands/del.spec.js b/test/commands/del.spec.js index 8a110485c61..152e9c1e455 100644 --- a/test/commands/del.spec.js +++ b/test/commands/del.spec.js @@ -21,7 +21,7 @@ describe("The 'del' method", function () { it('allows a single key to be deleted', function (done) { client.set('foo', 'bar'); - client.del('foo', helper.isNumber(1)); + client.DEL('foo', helper.isNumber(1)); client.get('foo', helper.isNull(done)); }); diff --git a/test/commands/eval.spec.js b/test/commands/eval.spec.js index 0c3edb08fa2..f5b46942a88 100644 --- a/test/commands/eval.spec.js +++ b/test/commands/eval.spec.js @@ -35,7 +35,7 @@ describe("The 'eval' method", function () { }); it('returns a string', function (done) { - client.eval("return 'hello world'", 0, helper.isString('hello world', done)); + client.EVAL("return 'hello world'", 0, helper.isString('hello world', done)); }); it('converts boolean true to integer 1', function (done) { diff --git a/test/commands/exits.spec.js b/test/commands/exits.spec.js index 2fd2ac5fcea..3e7074268fb 100644 --- a/test/commands/exits.spec.js +++ b/test/commands/exits.spec.js @@ -21,7 +21,7 @@ describe("The 'exits' method", function () { it('returns 1 if the key exists', function (done) { client.set('foo', 'bar'); - client.exists('foo', helper.isNumber(1, done)); + client.EXISTS('foo', helper.isNumber(1, done)); }); it('returns 0 if the key does not exist', function (done) { diff --git a/test/commands/flushdb.spec.js b/test/commands/flushdb.spec.js index c8e70bf58fb..0bc2336a42c 100644 --- a/test/commands/flushdb.spec.js +++ b/test/commands/flushdb.spec.js @@ -76,7 +76,7 @@ describe("The 'flushdb' method", function () { return done(err); } - client.flushdb(function (err, res) { + client.FLUSHDB(function (err, res) { helper.isString("OK")(err, res); done(err); }); diff --git a/test/commands/get.spec.js b/test/commands/get.spec.js index 3d3fd1e2483..6defc2cf6fe 100644 --- a/test/commands/get.spec.js +++ b/test/commands/get.spec.js @@ -64,7 +64,7 @@ describe("The 'get' method", function () { }); it("gets the value correctly", function (done) { - client.get(key, function (err, res) { + client.GET(key, function (err, res) { helper.isString(value)(err, res); done(err); }); diff --git a/test/commands/getset.spec.js b/test/commands/getset.spec.js index a47cd2d0b57..32b178beca1 100644 --- a/test/commands/getset.spec.js +++ b/test/commands/getset.spec.js @@ -65,7 +65,7 @@ describe("The 'getset' method", function () { }); it("gets the value correctly", function (done) { - client.getset(key, value2, function (err, res) { + client.GETSET(key, value2, function (err, res) { helper.isString(value)(err, res); client.get(key, function (err, res) { helper.isString(value2)(err, res); diff --git a/test/commands/hgetall.spec.js b/test/commands/hgetall.spec.js index 0735bf31de4..74b506e6f0e 100644 --- a/test/commands/hgetall.spec.js +++ b/test/commands/hgetall.spec.js @@ -35,7 +35,7 @@ describe("The 'hgetall' method", function () { }); it('handles fetching keys set using an object', function (done) { - client.hmset("msg_test", {message: "hello"}, helper.isString("OK")); + client.HMSET("msg_test", {message: "hello"}, helper.isString("OK")); client.hgetall("msg_test", function (err, obj) { assert.strictEqual(1, Object.keys(obj).length); assert.strictEqual(obj.message, "hello"); diff --git a/test/commands/hincrby.spec.js b/test/commands/hincrby.spec.js index c8a7cfc93ed..7d55f11c06d 100644 --- a/test/commands/hincrby.spec.js +++ b/test/commands/hincrby.spec.js @@ -24,7 +24,7 @@ describe("The 'hincrby' method", function () { var field = "field 1"; client.HSET(hash, field, 33); - client.HINCRBY(hash, field, 10, helper.isNumber(43, done)); + client.hincrby(hash, field, 10, helper.isNumber(43, done)); }); it('increments a key that has not been set', function (done) { diff --git a/test/commands/hmget.spec.js b/test/commands/hmget.spec.js index af673bfac6b..5f7bc325f2b 100644 --- a/test/commands/hmget.spec.js +++ b/test/commands/hmget.spec.js @@ -23,7 +23,7 @@ describe("The 'hmget' method", function () { }); it('allows keys to be specified using multiple arguments', function (done) { - client.HMGET(hash, "0123456789", "some manner of key", function (err, reply) { + client.hmget(hash, "0123456789", "some manner of key", function (err, reply) { assert.strictEqual("abcdefghij", reply[0].toString()); assert.strictEqual("a type of value", reply[1].toString()); return done(err); diff --git a/test/commands/hmset.spec.js b/test/commands/hmset.spec.js index 859f0c26abc..d5cc11e3b85 100644 --- a/test/commands/hmset.spec.js +++ b/test/commands/hmset.spec.js @@ -31,7 +31,7 @@ describe("The 'hmset' method", function () { }); it('handles object-style syntax', function (done) { - client.HMSET(hash, {"0123456789": "abcdefghij", "some manner of key": "a type of value", "otherTypes": 555}, helper.isString('OK')); + client.hmset(hash, {"0123456789": "abcdefghij", "some manner of key": "a type of value", "otherTypes": 555}, helper.isString('OK')); client.HGETALL(hash, function (err, obj) { assert.equal(obj['0123456789'], 'abcdefghij'); assert.equal(obj['some manner of key'], 'a type of value'); diff --git a/test/commands/hset.spec.js b/test/commands/hset.spec.js index 7155e3297dd..0424a97fd50 100644 --- a/test/commands/hset.spec.js +++ b/test/commands/hset.spec.js @@ -25,7 +25,7 @@ describe("The 'hset' method", function () { var field = new Buffer("0123456789"); var value = new Buffer("abcdefghij"); - client.HSET(hash, field, value, helper.isNumber(1)); + client.hset(hash, field, value, helper.isNumber(1)); client.HGET(hash, field, helper.isString(value.toString(), done)); }); diff --git a/test/commands/incr.spec.js b/test/commands/incr.spec.js index bc303da651d..f4d6b8ea47e 100644 --- a/test/commands/incr.spec.js +++ b/test/commands/incr.spec.js @@ -69,7 +69,7 @@ describe("The 'incr' method", function () { }); it("changes the last digit from 2 to 3", function (done) { - client.incr(key, function (err, res) { + client.INCR(key, function (err, res) { helper.isString("9007199254740993")(err, res); done(err); }); diff --git a/test/commands/keys.spec.js b/test/commands/keys.spec.js index aaf6ddadf4a..664ec4e4f37 100644 --- a/test/commands/keys.spec.js +++ b/test/commands/keys.spec.js @@ -46,7 +46,7 @@ describe("The 'keys' method", function () { return a.concat(b); }), helper.isString("OK")); - client.KEYS("multibulk:*", function(err, results) { + client.keys("multibulk:*", function(err, results) { assert.deepEqual(keys_values.map(function(val) { return val[0]; }).sort(), results.sort()); diff --git a/test/commands/mget.spec.js b/test/commands/mget.spec.js index 270ff378549..3d506c6d10a 100644 --- a/test/commands/mget.spec.js +++ b/test/commands/mget.spec.js @@ -33,7 +33,7 @@ describe("The 'mget' method", function () { }); it('handles fetching multiple keys via an array', function (done) { - client.MGET(["mget keys 1", "mget keys 2", "mget keys 3"], function (err, results) { + client.mget(["mget keys 1", "mget keys 2", "mget keys 3"], function (err, results) { assert.strictEqual("mget val 1", results[0].toString()); assert.strictEqual("mget val 2", results[1].toString()); assert.strictEqual("mget val 3", results[2].toString()); diff --git a/test/commands/msetnx.spec.js b/test/commands/msetnx.spec.js index 80846778399..1ac77e18945 100644 --- a/test/commands/msetnx.spec.js +++ b/test/commands/msetnx.spec.js @@ -26,7 +26,7 @@ describe("The 'msetnx' method", function () { }); it('sets multiple keys if all keys are not set', function (done) { - client.MSETNX(["mset3", "val3", "mset4", "val4"], helper.isNumber(1)); + client.msetnx(["mset3", "val3", "mset4", "val4"], helper.isNumber(1)); client.exists(["mset3"], helper.isNumber(1)); client.exists(["mset3"], helper.isNumber(1, done)); }); diff --git a/test/commands/multi.spec.js b/test/commands/multi.spec.js index 41ae51f075b..11aae4f8b04 100644 --- a/test/commands/multi.spec.js +++ b/test/commands/multi.spec.js @@ -62,7 +62,7 @@ describe("The 'multi' method", function () { var multi1, multi2; // Provoke an error at queue time - multi1 = client.multi(); + multi1 = client.MULTI(); multi1.mset("multifoo", "10", "multibar", "20", helper.isString("OK")); multi1.set("foo2", helper.isError()); multi1.incr("multifoo", helper.isNumber(11)); diff --git a/test/commands/rename.spec.js b/test/commands/rename.spec.js index 6196994c585..d506bec46cc 100644 --- a/test/commands/rename.spec.js +++ b/test/commands/rename.spec.js @@ -21,7 +21,7 @@ describe("The 'rename' method", function () { it('populates the new key', function (done) { client.set(['foo', 'bar'], helper.isString("OK")); - client.RENAME(["foo", "new foo"], helper.isString("OK")); + client.rename(["foo", "new foo"], helper.isString("OK")); client.exists(["new foo"], helper.isNumber(1, done)); }); diff --git a/test/commands/renamenx.spec.js b/test/commands/renamenx.spec.js index 454d046727c..dc2224ee58c 100644 --- a/test/commands/renamenx.spec.js +++ b/test/commands/renamenx.spec.js @@ -21,7 +21,7 @@ describe("The 'renamenx' method", function () { it('renames the key if target does not yet exist', function (done) { client.set('foo', 'bar', helper.isString('OK')); - client.renamenx('foo', 'foo2', helper.isNumber(1)); + client.RENAMENX('foo', 'foo2', helper.isNumber(1)); client.exists('foo', helper.isNumber(0)); client.exists(['foo2'], helper.isNumber(1, done)); }); diff --git a/test/commands/script.spec.js b/test/commands/script.spec.js index 254ab6045f5..663bbc5d9de 100644 --- a/test/commands/script.spec.js +++ b/test/commands/script.spec.js @@ -33,7 +33,7 @@ describe("The 'script' method", function () { }); it("loads script with client.script('load')", function (done) { - client.script("load", command, function(err, result) { + client.SCRIPT("load", command, function(err, result) { assert.strictEqual(result, commandSha); return done(); }); diff --git a/test/commands/select.spec.js b/test/commands/select.spec.js index bdc87668d18..1f0c38581f7 100644 --- a/test/commands/select.spec.js +++ b/test/commands/select.spec.js @@ -48,7 +48,7 @@ describe("The 'select' method", function () { it("changes the database and calls the callback", function (done) { // default value of null means database 0 will be used. assert.strictEqual(client.selected_db, null, "default db should be null"); - client.select(1, function (err, res) { + client.SELECT(1, function (err, res) { helper.isNotError()(err, res); assert.strictEqual(client.selected_db, 1, "db should be 1 after select"); done(); diff --git a/test/commands/set.spec.js b/test/commands/set.spec.js index 1674051c8f0..1ef70fc7dbd 100644 --- a/test/commands/set.spec.js +++ b/test/commands/set.spec.js @@ -58,7 +58,7 @@ describe("The 'set' method", function () { describe("and a callback is specified", function () { describe("with valid parameters", function () { it("sets the value correctly", function (done) { - client.set(key, value, function (err, res) { + client.SET(key, value, function (err, res) { helper.isNotError()(err, res); client.get(key, function (err, res) { helper.isString(value)(err, res); diff --git a/test/commands/setex.spec.js b/test/commands/setex.spec.js index 71e55b20e73..334f92cdbf0 100644 --- a/test/commands/setex.spec.js +++ b/test/commands/setex.spec.js @@ -21,7 +21,7 @@ describe("The 'setex' method", function () { }); it('sets a key with an expiry', function (done) { - client.SETEX(["setex key", "100", "setex val"], helper.isString("OK")); + client.setex(["setex key", "100", "setex val"], helper.isString("OK")); client.exists(["setex key"], helper.isNumber(1)); client.ttl(['setex key'], function (err, ttl) { assert.ok(ttl > 0); diff --git a/test/commands/setnx.spec.js b/test/commands/setnx.spec.js index 8e426d6c017..0092a33fac2 100644 --- a/test/commands/setnx.spec.js +++ b/test/commands/setnx.spec.js @@ -20,7 +20,7 @@ describe("The 'setnx' method", function () { }); it('sets key if it does not have a value', function (done) { - client.setnx('foo', 'banana', helper.isNumber(1)); + client.SETNX('foo', 'banana', helper.isNumber(1)); client.get('foo', helper.isString('banana', done)); }); diff --git a/test/commands/sinter.spec.js b/test/commands/sinter.spec.js index 59b751fb5fb..e03973755c9 100644 --- a/test/commands/sinter.spec.js +++ b/test/commands/sinter.spec.js @@ -29,7 +29,7 @@ describe("The 'sinter' method", function () { client.sadd('sb', 'c', helper.isNumber(1)); client.sadd('sb', 'd', helper.isNumber(1)); - client.sinter('sa', 'sb', function (err, intersection) { + client.SINTER('sa', 'sb', function (err, intersection) { assert.equal(intersection.length, 2); assert.deepEqual(intersection.sort(), [ 'b', 'c' ]); return done(err); diff --git a/test/commands/sismember.spec.js b/test/commands/sismember.spec.js index 1e1d29e5afd..d5e4a554f8e 100644 --- a/test/commands/sismember.spec.js +++ b/test/commands/sismember.spec.js @@ -25,7 +25,7 @@ describe("The 'sismember' method", function () { it('returns 1 if the value is in the set', function (done) { client.sadd('foo', 'banana', helper.isNumber(1)); - client.sismember('foo', 'banana', helper.isNumber(1, done)); + client.SISMEMBER('foo', 'banana', helper.isNumber(1, done)); }); afterEach(function () { diff --git a/test/commands/slowlog.spec.js b/test/commands/slowlog.spec.js index c64b0ebf589..846694d54d6 100644 --- a/test/commands/slowlog.spec.js +++ b/test/commands/slowlog.spec.js @@ -25,7 +25,7 @@ describe("The 'slowlog' method", function () { client.slowlog("reset", helper.isString("OK")); client.set("foo", "bar", helper.isString("OK")); client.get("foo", helper.isString("bar")); - client.slowlog("get", function (err, res) { + client.SLOWLOG("get", function (err, res) { assert.equal(res.length, 3); assert.equal(res[0][3].length, 2); assert.deepEqual(res[1][3], ["set", "foo", "bar"]); diff --git a/test/commands/smove.spec.js b/test/commands/smove.spec.js index abdc2e476af..4b65b150cd5 100644 --- a/test/commands/smove.spec.js +++ b/test/commands/smove.spec.js @@ -28,7 +28,7 @@ describe("The 'smove' method", function () { it("does not move a value if it does not exist in the first set", function (done) { client.sadd('foo', 'x', helper.isNumber(1)); - client.smove('foo', 'bar', 'y', helper.isNumber(0)); + client.SMOVE('foo', 'bar', 'y', helper.isNumber(0)); client.sismember('foo', 'y', helper.isNumber(0)); client.sismember('bar', 'y', helper.isNumber(0, done)); }); diff --git a/test/commands/sort.spec.js b/test/commands/sort.spec.js index d50f176b648..e5985137def 100644 --- a/test/commands/sort.spec.js +++ b/test/commands/sort.spec.js @@ -30,7 +30,7 @@ describe("The 'sort' method", function () { }); it('sorts in descending alphabetical order', function (done) { - client.sort('y', 'desc', 'alpha', function (err, sorted) { + client.SORT('y', 'desc', 'alpha', function (err, sorted) { assert.deepEqual(sorted, ['d', 'c', 'b', 'a']); return done(err); }); diff --git a/test/commands/srem.spec.js b/test/commands/srem.spec.js index a62dd6bc834..8afd79afeda 100644 --- a/test/commands/srem.spec.js +++ b/test/commands/srem.spec.js @@ -27,7 +27,7 @@ describe("The 'srem' method", function () { }); it('handles attempting to remove a missing value', function (done) { - client.srem('set0', 'member0', helper.isNumber(0, done)); + client.SREM('set0', 'member0', helper.isNumber(0, done)); }); it('allows multiple values to be removed', function (done) { diff --git a/test/commands/type.spec.js b/test/commands/type.spec.js index 1410b325dec..e4ad527d91a 100644 --- a/test/commands/type.spec.js +++ b/test/commands/type.spec.js @@ -26,7 +26,7 @@ describe("The 'type' method", function () { it('reports list type', function (done) { client.rpush(["list key", "should be a list"], helper.isNumber(1)); - client.TYPE(["list key"], helper.isString("list", done)); + client.type(["list key"], helper.isString("list", done)); }); it('reports set type', function (done) { diff --git a/test/commands/watch.spec.js b/test/commands/watch.spec.js index 6cbc63e2f49..11cd4470f0b 100644 --- a/test/commands/watch.spec.js +++ b/test/commands/watch.spec.js @@ -32,7 +32,7 @@ describe("The 'watch' method", function () { }); it('does not execute transaction if watched key was modified prior to execution', function (done) { - client.watch(watched); + client.WATCH(watched); client.incr(watched); var multi = client.multi(); multi.incr(watched); From 1c1c4ea446ecdd4dd4a04bd76f992b8224c16608 Mon Sep 17 00:00:00 2001 From: Ruben Bridgewater Date: Fri, 4 Sep 2015 18:55:10 +0200 Subject: [PATCH 0304/1748] Update readme about lower case commands --- README.md | 6 +++--- index.js | 2 +- 2 files changed, 4 insertions(+), 4 deletions(-) diff --git a/README.md b/README.md index caeefe5a743..488d64c8623 100644 --- a/README.md +++ b/README.md @@ -551,9 +551,9 @@ the second word as first parameter: ## client.send_command(command_name, args, callback) -Used internally to send commands to Redis. For convenience, nearly all commands that are published on the Redis -Wiki have been added to the `client` object. However, if I missed any, or if new commands are introduced before -this library is updated, you can use `send_command()` to send arbitrary commands to Redis. +Used internally to send commands to Redis. Nearly all Redis commands have been added to the `client` object. +However, if new commands are introduced before this library is updated, you can use `send_command()` to send arbitrary commands to Redis. +The command has to be lower case. All commands are sent as multi-bulk commands. `args` can either be an Array of arguments, or omitted. diff --git a/index.js b/index.js index a01073df98a..7b1ea0345a9 100644 --- a/index.js +++ b/index.js @@ -692,7 +692,7 @@ function Command(command, args, sub_command, buffer_args, callback) { } RedisClient.prototype.send_command = function (command, args, callback) { - var arg, command_obj, i, il, elem_count, buffer_args, stream = this.stream, command_str = "", buffered_writes = 0, last_arg_type, lcaseCommand; + var arg, command_obj, i, il, elem_count, buffer_args, stream = this.stream, command_str = "", buffered_writes = 0, last_arg_type; if (typeof command !== "string") { throw new Error("First argument to send_command must be the command name string, not " + typeof command); From 43e25e73c957f0b4c3800224727fbe712d3f852a Mon Sep 17 00:00:00 2001 From: Ruben Bridgewater Date: Fri, 4 Sep 2015 17:05:38 +0200 Subject: [PATCH 0305/1748] Remove async dependency --- package.json | 1 - test/commands/flushdb.spec.js | 30 ++++++++---------------------- test/helper.js | 9 +++++++++ test/node_redis.spec.js | 31 +++++++------------------------ 4 files changed, 24 insertions(+), 47 deletions(-) diff --git a/package.json b/package.json index 9f3e1e86ac4..5eee7c60305 100644 --- a/package.json +++ b/package.json @@ -16,7 +16,6 @@ "jshint": "./node_modules/.bin/jshint *" }, "devDependencies": { - "async": "^1.3.0", "coveralls": "^2.11.2", "hiredis": "^0.4.1", "jshint": "^2.8.0", diff --git a/test/commands/flushdb.spec.js b/test/commands/flushdb.spec.js index 0bc2336a42c..409f81b0912 100644 --- a/test/commands/flushdb.spec.js +++ b/test/commands/flushdb.spec.js @@ -1,6 +1,5 @@ 'use strict'; -var async = require('async'); var assert = require('assert'); var config = require("../lib/config"); var helper = require('../helper'); @@ -57,29 +56,16 @@ describe("The 'flushdb' method", function () { }); describe("when there is data in Redis", function () { - var oldSize; beforeEach(function (done) { - async.parallel([function (next) { - client.mset(key, uuid.v4(), key2, uuid.v4(), function (err, res) { - helper.isNotError()(err, res); - next(err); - }); - }, function (next) { - client.dbsize([], function (err, res) { - helper.isType.positiveNumber()(err, res); - oldSize = res; - next(err); - }); - }], function (err) { - if (err) { - return done(err); - } - - client.FLUSHDB(function (err, res) { - helper.isString("OK")(err, res); - done(err); - }); + var end = helper.callFuncAfter(function () { + client.flushdb(helper.isString("OK", done)); + }, 2); + client.mset(key, uuid.v4(), key2, uuid.v4(), helper.isNotError(end)); + client.dbsize([], function (err, res) { + helper.isType.positiveNumber()(err, res); + assert.equal(res, 2, 'Two keys should have been inserted'); + end(); }); }); diff --git a/test/helper.js b/test/helper.js index aec927ce993..0286bc47c3c 100644 --- a/test/helper.js +++ b/test/helper.js @@ -116,5 +116,14 @@ module.exports = { var mochaListener = process.listeners('uncaughtException').pop(); process.removeListener('uncaughtException', mochaListener); return mochaListener; + }, + callFuncAfter: function (func, max) { + var i = 0; + return function () { + i++; + if (i === max) { + func(); + } + }; } }; diff --git a/test/node_redis.spec.js b/test/node_redis.spec.js index 7b90fd38223..b579a61a49f 100644 --- a/test/node_redis.spec.js +++ b/test/node_redis.spec.js @@ -1,6 +1,5 @@ 'use strict'; -var async = require("async"); var assert = require("assert"); var config = require("./lib/config"); var helper = require('./helper'); @@ -122,31 +121,15 @@ describe("The node_redis client", function () { it("reconnects and can retrieve the pre-existing data", function (done) { client.on("reconnecting", function on_recon(params) { client.on("connect", function on_connect() { - async.parallel([function (cb) { - client.get("recon 1", function (err, res) { - helper.isString("one")(err, res); - cb(); - }); - }, function (cb) { - client.get("recon 1", function (err, res) { - helper.isString("one")(err, res); - cb(); - }); - }, function (cb) { - client.get("recon 2", function (err, res) { - helper.isString("two")(err, res); - cb(); - }); - }, function (cb) { - client.get("recon 2", function (err, res) { - helper.isString("two")(err, res); - cb(); - }); - }], function (err, results) { + var end = helper.callFuncAfter(function () { client.removeListener("connect", on_connect); client.removeListener("reconnecting", on_recon); - done(err); - }); + done(); + }, 4); + client.get("recon 1", helper.isString("one", end)); + client.get("recon 1", helper.isString("one", end)); + client.get("recon 2", helper.isString("two", end)); + client.get("recon 2", helper.isString("two", end)); }); }); From b06985a219a64869841788630c073f21e8905030 Mon Sep 17 00:00:00 2001 From: Ruben Bridgewater Date: Thu, 3 Sep 2015 21:50:29 +0200 Subject: [PATCH 0306/1748] Update commands list and remove unecessary code Add use strict Add changelog entry --- changelog.md | 1 + generate_commands.js | 2 +- index.js | 38 ++++++++++---------------------------- lib/commands.js | 34 +++++++++++++++++++++++++++++++++- 4 files changed, 45 insertions(+), 30 deletions(-) diff --git a/changelog.md b/changelog.md index 085cdae2bf7..15588a4eae9 100644 --- a/changelog.md +++ b/changelog.md @@ -4,6 +4,7 @@ Changelog ## Upcoming * [#815](https://github.com/NodeRedis/node_redis/pull/815) Consistently use new debug functionality (@BridgeAR) +* [#814](https://github.com/NodeRedis/node_redis/pull/814) Support new commands and drop support for deprecated 'substr' (@BridgeAR) ## v1.0.0 - Aug 30, 2015 diff --git a/generate_commands.js b/generate_commands.js index 3ad420edc35..a9862321f6e 100644 --- a/generate_commands.js +++ b/generate_commands.js @@ -13,7 +13,7 @@ function write_file(commands, path) { console.log("Writing " + Object.keys(commands).length + " commands to " + path); - file_contents = "// This file was generated by ./generate_commands.js on " + prettyCurrentTime() + "\n"; + file_contents = "'use strict';\n\n// This file was generated by ./generate_commands.js on " + prettyCurrentTime() + "\n"; out_commands = Object.keys(commands).map(function (key) { return key.toLowerCase(); diff --git a/index.js b/index.js index 7b1ea0345a9..b2b8aa29325 100644 --- a/index.js +++ b/index.js @@ -7,7 +7,10 @@ var net = require("net"), to_array = require("./lib/to_array"), events = require("events"), crypto = require("crypto"), - parsers = [], commands, + parsers = [], + // This static list of commands is updated from time to time. + // ./lib/commands.js can be updated with generate_commands.js + commands = require("./lib/commands"), connection_id = 0, default_port = 6379, default_host = "127.0.0.1", @@ -892,40 +895,19 @@ function Multi(client, args) { exports.Multi = Multi; -// take 2 arrays and return the union of their elements -function set_union(seta, setb) { - var obj = {}; - - seta.forEach(function (val) { - obj[val] = true; - }); - setb.forEach(function (val) { - obj[val] = true; - }); - return Object.keys(obj); -} - -// This static list of commands is updated from time to time. ./lib/commands.js can be updated with generate_commands.js -commands = set_union(["get", "set", "setnx", "setex", "append", "strlen", "del", "exists", "setbit", "getbit", "setrange", "getrange", "substr", - "incr", "decr", "mget", "rpush", "lpush", "rpushx", "lpushx", "linsert", "rpop", "lpop", "brpop", "brpoplpush", "blpop", "llen", "lindex", - "lset", "lrange", "ltrim", "lrem", "rpoplpush", "sadd", "srem", "smove", "sismember", "scard", "spop", "srandmember", "sinter", "sinterstore", - "sunion", "sunionstore", "sdiff", "sdiffstore", "smembers", "zadd", "zincrby", "zrem", "zremrangebyscore", "zremrangebyrank", "zunionstore", - "zinterstore", "zrange", "zrangebyscore", "zrevrangebyscore", "zcount", "zrevrange", "zcard", "zscore", "zrank", "zrevrank", "hset", "hsetnx", - "hget", "hmset", "hmget", "hincrby", "hdel", "hlen", "hkeys", "hvals", "hgetall", "hexists", "incrby", "decrby", "getset", "mset", "msetnx", - "randomkey", "select", "move", "rename", "renamenx", "expire", "expireat", "keys", "dbsize", "auth", "ping", "echo", "save", "bgsave", - "bgrewriteaof", "shutdown", "lastsave", "type", "multi", "exec", "discard", "sync", "flushdb", "flushall", "sort", "info", "monitor", "ttl", - "persist", "slaveof", "debug", "config", "subscribe", "unsubscribe", "psubscribe", "punsubscribe", "publish", "watch", "unwatch", "cluster", - "restore", "migrate", "dump", "object", "client", "eval", "evalsha"], require("./lib/commands")); - commands.forEach(function (fullCommand) { var command = fullCommand.split(' ')[0]; + // Skip all full commands that have already been added instead of overwriting them over and over again + if (RedisClient.prototype[command]) { + return; + } + RedisClient.prototype[command] = function (args, callback) { if (Array.isArray(args)) { return this.send_command(command, args, callback); - } else { - return this.send_command(command, to_array(arguments)); } + return this.send_command(command, to_array(arguments)); }; RedisClient.prototype[command.toUpperCase()] = RedisClient.prototype[command]; diff --git a/lib/commands.js b/lib/commands.js index 3b01543d99d..8082841dc83 100644 --- a/lib/commands.js +++ b/lib/commands.js @@ -1,6 +1,6 @@ 'use strict'; -// This file was generated by ./generate_commands.js on Wed Apr 23 2014 14:51:21 GMT-0700 (PDT) +// This file was generated by ./generate_commands.js on Thu Sep 03 2015 02:40:54 GMT+0200 (CEST) module.exports = [ "append", "auth", @@ -17,6 +17,28 @@ module.exports = [ "client getname", "client pause", "client setname", + "cluster addslots", + "cluster count-failure-reports", + "cluster countkeysinslot", + "cluster delslots", + "cluster failover", + "cluster forget", + "cluster getkeysinslot", + "cluster info", + "cluster keyslot", + "cluster meet", + "cluster nodes", + "cluster replicate", + "cluster reset", + "cluster saveconfig", + "cluster set-config-epoch", + "cluster setslot", + "cluster slaves", + "cluster slots", + "command", + "command count", + "command getkeys", + "command info", "config get", "config rewrite", "config set", @@ -38,6 +60,12 @@ module.exports = [ "expireat", "flushall", "flushdb", + "geoadd", + "geohash", + "geopos", + "geodist", + "georadius", + "georadiusbymember", "get", "getbit", "getrange", @@ -54,6 +82,7 @@ module.exports = [ "hmset", "hset", "hsetnx", + "hstrlen", "hvals", "incr", "incrby", @@ -97,6 +126,7 @@ module.exports = [ "rename", "renamenx", "restore", + "role", "rpop", "rpoplpush", "rpush", @@ -138,6 +168,7 @@ module.exports = [ "type", "unsubscribe", "unwatch", + "wait", "watch", "zadd", "zcard", @@ -147,6 +178,7 @@ module.exports = [ "zlexcount", "zrange", "zrangebylex", + "zrevrangebylex", "zrangebyscore", "zrank", "zrem", From 0925885a88d896e0d83aa323a771a1cda256b751 Mon Sep 17 00:00:00 2001 From: Ruben Bridgewater Date: Mon, 7 Sep 2015 14:55:57 +0200 Subject: [PATCH 0307/1748] Do not wrap errors into other errors. The trace is going to be manipulated that way. --- index.js | 4 ++-- test/commands/multi.spec.js | 4 ++-- 2 files changed, 4 insertions(+), 4 deletions(-) diff --git a/index.js b/index.js index b2b8aa29325..f724d560605 100644 --- a/index.js +++ b/index.js @@ -1055,7 +1055,7 @@ Multi.prototype.exec = function (callback) { if (typeof cur[cur.length - 1] === "function") { cur[cur.length - 1](err); } else { - errors.push(new Error(err)); + errors.push(err); } } }); @@ -1065,7 +1065,7 @@ Multi.prototype.exec = function (callback) { return this._client.send_command("exec", [], function (err, replies) { if (err) { if (callback) { - errors.push(new Error(err)); + errors.push(err); callback(errors); return; } else { diff --git a/test/commands/multi.spec.js b/test/commands/multi.spec.js index 11aae4f8b04..10b50f7c50b 100644 --- a/test/commands/multi.spec.js +++ b/test/commands/multi.spec.js @@ -231,8 +231,8 @@ describe("The 'multi' method", function () { client.multi().set("foo").exec(function (err, reply) { assert(Array.isArray(err), "err should be an array"); assert.equal(2, err.length, "err should have 2 items"); - assert(err[0].message.match(/ERR/), "First error message should contain ERR"); - assert(err[1].message.match(/EXECABORT/), "First error message should contain EXECABORT"); + assert(err[0].message.match(/^ERR/), "First error message should begin with ERR"); + assert(err[1].message.match(/^EXECABORT/), "First error message should begin with EXECABORT"); return done(); }); }); From 06121a65c476a16e18e59b139319f07dc91bcdad Mon Sep 17 00:00:00 2001 From: Ruben Bridgewater Date: Sun, 13 Sep 2015 02:21:21 +0200 Subject: [PATCH 0308/1748] Increase the coverage --- test/commands/blpop.spec.js | 2 ++ test/commands/get.spec.js | 1 + test/commands/getset.spec.js | 1 + 3 files changed, 4 insertions(+) diff --git a/test/commands/blpop.spec.js b/test/commands/blpop.spec.js index 843b351dc8b..66a85dd3b49 100644 --- a/test/commands/blpop.spec.js +++ b/test/commands/blpop.spec.js @@ -23,7 +23,9 @@ describe("The 'blpop' method", function () { it('pops value immediately if list contains values', function (done) { bclient = redis.createClient.apply(redis.createClient, args); + redis.debug_mode = true; client.rpush("blocking list", "initial value", helper.isNumber(1)); + redis.debug_mode = false; bclient.blpop("blocking list", 0, function (err, value) { assert.strictEqual(value[0], "blocking list"); assert.strictEqual(value[1], "initial value"); diff --git a/test/commands/get.spec.js b/test/commands/get.spec.js index 6defc2cf6fe..9aac49f14f3 100644 --- a/test/commands/get.spec.js +++ b/test/commands/get.spec.js @@ -64,6 +64,7 @@ describe("The 'get' method", function () { }); it("gets the value correctly", function (done) { + client.GET(key, redis.print); // Use the utility function to print the result client.GET(key, function (err, res) { helper.isString(value)(err, res); done(err); diff --git a/test/commands/getset.spec.js b/test/commands/getset.spec.js index 32b178beca1..2cc938aa6c0 100644 --- a/test/commands/getset.spec.js +++ b/test/commands/getset.spec.js @@ -34,6 +34,7 @@ describe("The 'getset' method", function () { }); it("reports an error", function (done) { + client.GET(key, redis.print); // Use the utility function to print the error client.get(key, function (err, res) { assert.equal(err.message, 'Redis connection gone from end event.'); done(); From b4da9757855e71cf4dda20a4e5c4ec2ab5163da6 Mon Sep 17 00:00:00 2001 From: Benjamin Coe Date: Fri, 11 Sep 2015 23:23:34 -0700 Subject: [PATCH 0309/1748] prep for getting tests to work on appveyor --- package.json | 2 +- test/auth.spec.js | 19 +++++++++++++++++++ test/helper.js | 3 +++ test/lib/redis-process.js | 14 +++++++------- 4 files changed, 30 insertions(+), 8 deletions(-) diff --git a/package.json b/package.json index 5eee7c60305..0c757198c84 100644 --- a/package.json +++ b/package.json @@ -21,7 +21,7 @@ "jshint": "^2.8.0", "metrics": ">=0.1.5", "mocha": "^2.2.5", - "nyc": "^3.0.0", + "nyc": "^3.2.2", "tcp-port-used": "^0.1.2", "uuid": "^2.0.1" }, diff --git a/test/auth.spec.js b/test/auth.spec.js index c049df5d30a..b4a710b8463 100644 --- a/test/auth.spec.js +++ b/test/auth.spec.js @@ -24,6 +24,8 @@ describe("client authentication", function () { }); it("allows auth to be provided with 'auth' method", function (done) { + abortOnSpawnFailure(done); + client = redis.createClient.apply(redis.createClient, args); client.auth(auth, function (err, res) { assert.strictEqual(null, err); @@ -33,6 +35,8 @@ describe("client authentication", function () { }); it("raises error when auth is bad", function (done) { + abortOnSpawnFailure(done); + client = redis.createClient.apply(redis.createClient, args); client.once('error', function (error) { @@ -45,6 +49,8 @@ describe("client authentication", function () { if (ip === 'IPv4') { it('allows auth to be provided as part of redis url', function (done) { + abortOnSpawnFailure(done); + client = redis.createClient('redis://foo:' + auth + '@' + config.HOST[ip] + ':' + config.PORT); client.on("ready", function () { return done(); @@ -53,6 +59,8 @@ describe("client authentication", function () { } it('allows auth to be provided as config option for client', function (done) { + abortOnSpawnFailure(done); + var args = config.configureClient(parser, ip, { auth_pass: auth }); @@ -63,6 +71,8 @@ describe("client authentication", function () { }); it('reconnects with appropriate authentication', function (done) { + abortOnSpawnFailure(done); + var readyCount = 0; client = redis.createClient.apply(redis.createClient, args); client.auth(auth); @@ -83,4 +93,13 @@ describe("client authentication", function () { helper.startRedis('./conf/redis.conf', done); }); }); + + // if we fail to spawn Redis (spawning Redis directly is + // not possible in some CI environments) skip the auth tests. + function abortOnSpawnFailure (done) { + if (helper.redisProcess().spawnFailed()) { + console.warn('skipped authentication test') + return done(); + } + } }); diff --git a/test/helper.js b/test/helper.js index 0286bc47c3c..0c5bf8c5bff 100644 --- a/test/helper.js +++ b/test/helper.js @@ -28,6 +28,9 @@ if (!process.env.REDIS_TESTS_STARTED) { } module.exports = { + redisProcess: function () { + return rp; + }, stopRedis: function (done) { rp.stop(done); }, diff --git a/test/lib/redis-process.js b/test/lib/redis-process.js index 176aa2e052d..298558ff452 100644 --- a/test/lib/redis-process.js +++ b/test/lib/redis-process.js @@ -5,6 +5,7 @@ var cp = require('child_process'); var config = require('./config'); var fs = require('fs'); var path = require('path'); +var spawnFailed = false; var tcpPortUsed = require('tcp-port-used'); // wait for redis to be listening in @@ -36,13 +37,8 @@ module.exports = { // capture a failure booting redis, and give // the user running the test some directions. rp.once("exit", function (code) { - if (code !== 0) { - console.error('failed to starting redis with exit code "' + code + '" ' + - 'stop any other redis processes currently running (' + - 'hint: lsof -i :6379)'); - process.exit(code); - } - }); + if (code !== 0) spawnFailed = true; + }) // wait for redis to become available, by // checking the port we bind on. @@ -50,7 +46,11 @@ module.exports = { // return an object that can be used in // an after() block to shutdown redis. return done(null, { + spawnFailed: function () { + return spawnFailed; + }, stop: function (done) { + if (spawnFailed) return done(); rp.once("exit", function (code) { var error = null; if (code !== null && code !== 0) { From a0bf9e2314a4f8c5783074566d491a02d1960ba4 Mon Sep 17 00:00:00 2001 From: Benjamin Coe Date: Sat, 12 Sep 2015 17:53:49 -0700 Subject: [PATCH 0310/1748] tweaks based on running test-suite on Windows on an older Redis --- package.json | 9 +++--- test/auth.spec.js | 19 +++--------- test/commands/client.spec.js | 7 +---- test/commands/eval.spec.js | 59 ++++++++++++++++++++++-------------- test/commands/expire.spec.js | 2 +- test/commands/multi.spec.js | 13 ++++---- test/commands/script.spec.js | 13 ++++---- test/commands/watch.spec.js | 7 +---- test/helper.js | 19 +++++++++--- test/node_redis.spec.js | 2 +- test/pubsub.spec.js | 12 +++----- 11 files changed, 82 insertions(+), 80 deletions(-) diff --git a/package.json b/package.json index 0c757198c84..a784a875017 100644 --- a/package.json +++ b/package.json @@ -10,10 +10,10 @@ "license": "MIT", "main": "./index.js", "scripts": { - "coveralls": "nyc report --reporter=text-lcov | coveralls", - "coverage": "nyc report --reporter=html", - "test": "./node_modules/.bin/jshint * && nyc ./node_modules/.bin/_mocha ./test/*.js ./test/commands/*.js ./test/parser/*.js --timeout=8000", - "jshint": "./node_modules/.bin/jshint *" + "coverage": "nyc report --reporter=text-lcov | coveralls", + "test": "nyc ./node_modules/.bin/_mocha ./test/*.js ./test/commands/*.js ./test/parser/*.js --timeout=8000", + "pretest": "optional-dev-dependency hiredis", + "posttest": "jshint *" }, "devDependencies": { "coveralls": "^2.11.2", @@ -22,6 +22,7 @@ "metrics": ">=0.1.5", "mocha": "^2.2.5", "nyc": "^3.2.2", + "optional-dev-dependency": "^1.0.1", "tcp-port-used": "^0.1.2", "uuid": "^2.0.1" }, diff --git a/test/auth.spec.js b/test/auth.spec.js index b4a710b8463..b1a9cd86cc5 100644 --- a/test/auth.spec.js +++ b/test/auth.spec.js @@ -24,7 +24,7 @@ describe("client authentication", function () { }); it("allows auth to be provided with 'auth' method", function (done) { - abortOnSpawnFailure(done); + if (helper.redisProcess().spawnFailed()) this.skip(); client = redis.createClient.apply(redis.createClient, args); client.auth(auth, function (err, res) { @@ -35,7 +35,7 @@ describe("client authentication", function () { }); it("raises error when auth is bad", function (done) { - abortOnSpawnFailure(done); + if (helper.redisProcess().spawnFailed()) this.skip(); client = redis.createClient.apply(redis.createClient, args); @@ -49,7 +49,7 @@ describe("client authentication", function () { if (ip === 'IPv4') { it('allows auth to be provided as part of redis url', function (done) { - abortOnSpawnFailure(done); + if (helper.redisProcess().spawnFailed()) this.skip(); client = redis.createClient('redis://foo:' + auth + '@' + config.HOST[ip] + ':' + config.PORT); client.on("ready", function () { @@ -59,7 +59,7 @@ describe("client authentication", function () { } it('allows auth to be provided as config option for client', function (done) { - abortOnSpawnFailure(done); + if (helper.redisProcess().spawnFailed()) this.skip(); var args = config.configureClient(parser, ip, { auth_pass: auth @@ -71,7 +71,7 @@ describe("client authentication", function () { }); it('reconnects with appropriate authentication', function (done) { - abortOnSpawnFailure(done); + if (helper.redisProcess().spawnFailed()) this.skip(); var readyCount = 0; client = redis.createClient.apply(redis.createClient, args); @@ -93,13 +93,4 @@ describe("client authentication", function () { helper.startRedis('./conf/redis.conf', done); }); }); - - // if we fail to spawn Redis (spawning Redis directly is - // not possible in some CI environments) skip the auth tests. - function abortOnSpawnFailure (done) { - if (helper.redisProcess().spawnFailed()) { - console.warn('skipped authentication test') - return done(); - } - } }); diff --git a/test/commands/client.spec.js b/test/commands/client.spec.js index 31bb5dc5e71..d6df117feaf 100644 --- a/test/commands/client.spec.js +++ b/test/commands/client.spec.js @@ -17,12 +17,7 @@ describe("The 'client' method", function () { client = redis.createClient.apply(redis.createClient, args); client.once("error", done); client.once("connect", function () { - client.flushdb(function (err) { - if (!helper.serverVersionAtLeast(client, [2, 4, 0])) { - err = Error('script not supported in redis <= 2.4.0'); - } - return done(err); - }); + client.flushdb(done) }); }); diff --git a/test/commands/eval.spec.js b/test/commands/eval.spec.js index f5b46942a88..184759a92bc 100644 --- a/test/commands/eval.spec.js +++ b/test/commands/eval.spec.js @@ -17,12 +17,7 @@ describe("The 'eval' method", function () { client = redis.createClient.apply(redis.createClient, args); client.once("error", done); client.once("connect", function () { - client.flushdb(function (err) { - if (!helper.serverVersionAtLeast(client, [2, 5, 0])) { - err = Error('exec not supported in redis <= 2.5.0'); - } - return done(err); - }); + client.flushdb(done) }); }); @@ -31,30 +26,37 @@ describe("The 'eval' method", function () { }); it('converts a float to an integer when evaluated', function (done) { + helper.serverVersionAtLeast.bind(this)(client, [2, 5, 0]); client.eval("return 100.5", 0, helper.isNumber(100, done)); }); it('returns a string', function (done) { - client.EVAL("return 'hello world'", 0, helper.isString('hello world', done)); + helper.serverVersionAtLeast.bind(this)(client, [2, 5, 0]); + client.eval("return 'hello world'", 0, helper.isString('hello world', done)); }); it('converts boolean true to integer 1', function (done) { + helper.serverVersionAtLeast.bind(this)(client, [2, 5, 0]); client.eval("return true", 0, helper.isNumber(1, done)); }); it('converts boolean false to null', function (done) { + helper.serverVersionAtLeast.bind(this)(client, [2, 5, 0]); client.eval("return false", 0, helper.isNull(done)); }); it('converts lua status code to string representation', function (done) { + helper.serverVersionAtLeast.bind(this)(client, [2, 5, 0]); client.eval("return {ok='fine'}", 0, helper.isString('fine', done)); }); it('converts lua error to an error response', function (done) { + helper.serverVersionAtLeast.bind(this)(client, [2, 5, 0]); client.eval("return {err='this is an error'}", 0, helper.isError(done)); }); it('represents a lua table appropritely', function (done) { + helper.serverVersionAtLeast.bind(this)(client, [2, 5, 0]); client.eval("return {1,2,3,'ciao',{1,2}}", 0, function (err, res) { assert.strictEqual(5, res.length); assert.strictEqual(1, res[0]); @@ -69,25 +71,27 @@ describe("The 'eval' method", function () { }); it('populates keys and argv correctly', function (done) { - client.eval("return {KEYS[1],KEYS[2],ARGV[1],ARGV[2]}", 2, "a", "b", "c", "d", function (err, res) { - assert.strictEqual(4, res.length); - assert.strictEqual("a", res[0]); - assert.strictEqual("b", res[1]); - assert.strictEqual("c", res[2]); - assert.strictEqual("d", res[3]); - return done(); - }); + helper.serverVersionAtLeast.bind(this)(client, [2, 5, 0]); + client.eval("return {KEYS[1],KEYS[2],ARGV[1],ARGV[2]}", 2, "a", "b", "c", "d", function (err, res) { + assert.strictEqual(4, res.length); + assert.strictEqual("a", res[0]); + assert.strictEqual("b", res[1]); + assert.strictEqual("c", res[2]); + assert.strictEqual("d", res[3]); + return done(); + }); }); it('allows arguments to be provided in array rather than as multiple parameters', function (done) { - client.eval(["return {KEYS[1],KEYS[2],ARGV[1],ARGV[2]}", 2, "a", "b", "c", "d"], function (err, res) { - assert.strictEqual(4, res.length); - assert.strictEqual("a", res[0]); - assert.strictEqual("b", res[1]); - assert.strictEqual("c", res[2]); - assert.strictEqual("d", res[3]); - return done(); - }); + helper.serverVersionAtLeast.bind(this)(client, [2, 5, 0]); + client.eval(["return {KEYS[1],KEYS[2],ARGV[1],ARGV[2]}", 2, "a", "b", "c", "d"], function (err, res) { + assert.strictEqual(4, res.length); + assert.strictEqual("a", res[0]); + assert.strictEqual("b", res[1]); + assert.strictEqual("c", res[2]); + assert.strictEqual("d", res[3]); + return done(); + }); }); describe('evalsha', function () { @@ -101,19 +105,23 @@ describe("The 'eval' method", function () { }); it('allows a script to be executed that accesses the redis API', function (done) { + helper.serverVersionAtLeast.bind(this)(client, [2, 5, 0]); client.eval(source, 0, helper.isString('eval get sha test', done)); }); it('can execute a script if the SHA exists', function (done) { + helper.serverVersionAtLeast.bind(this)(client, [2, 5, 0]); client.evalsha(sha, 0, helper.isString('eval get sha test', done)); }); it('throws an error if SHA does not exist', function (done) { + helper.serverVersionAtLeast.bind(this)(client, [2, 5, 0]); client.evalsha('ffffffffffffffffffffffffffffffffffffffff', 0, helper.isError(done)); }); }); it('allows a key to be incremented, and performs appropriate conversion from LUA type', function (done) { + helper.serverVersionAtLeast.bind(this)(client, [2, 5, 0]); client.set("incr key", 0, function (err, reply) { if (err) return done(err); client.eval("local foo = redis.call('incr','incr key')\n" + "return {type(foo),foo}", 0, function (err, res) { @@ -126,6 +134,7 @@ describe("The 'eval' method", function () { }); it('allows a bulk operation to be performed, and performs appropriate conversion from LUA type', function (done) { + helper.serverVersionAtLeast.bind(this)(client, [2, 5, 0]); client.set("bulk reply key", "bulk reply value", function (err, res) { client.eval("local foo = redis.call('get','bulk reply key'); return {type(foo),foo}", 0, function (err, res) { assert.strictEqual(2, res.length); @@ -137,6 +146,7 @@ describe("The 'eval' method", function () { }); it('allows a multi mulk operation to be performed, with the appropriate type conversion', function (done) { + helper.serverVersionAtLeast.bind(this)(client, [2, 5, 0]); client.multi() .del("mylist") .rpush("mylist", "a") @@ -157,6 +167,7 @@ describe("The 'eval' method", function () { }); it('returns an appropriate representation of Lua status reply', function (done) { + helper.serverVersionAtLeast.bind(this)(client, [2, 5, 0]); client.eval("local foo = redis.call('set','mykey','myval'); return {type(foo),foo['ok']}", 0, function (err, res) { assert.strictEqual(2, res.length); assert.strictEqual("table", res[0]); @@ -166,6 +177,7 @@ describe("The 'eval' method", function () { }); it('returns an appropriate representation of a Lua error reply', function (done) { + helper.serverVersionAtLeast.bind(this)(client, [2, 5, 0]); client.set("error reply key", "error reply value", function (err, res) { if (err) return done(err); client.eval("local foo = redis.pcall('incr','error reply key'); return {type(foo),foo['err']}", 0, function (err, res) { @@ -178,6 +190,7 @@ describe("The 'eval' method", function () { }); it('returns an appropriate representation of a Lua nil reply', function (done) { + helper.serverVersionAtLeast.bind(this)(client, [2, 5, 0]); client.del("nil reply key", function (err, res) { if (err) return done(err); client.eval("local foo = redis.call('get','nil reply key'); return {type(foo),foo == false}", 0, function (err, res) { diff --git a/test/commands/expire.spec.js b/test/commands/expire.spec.js index 4173a3a4bdf..5d5ca2d2dc9 100644 --- a/test/commands/expire.spec.js +++ b/test/commands/expire.spec.js @@ -24,7 +24,7 @@ describe("The 'expire' method", function () { client.EXPIRE(["expiry key", "1"], helper.isNumber(1)); setTimeout(function () { client.exists(["expiry key"], helper.isNumber(0, done)); - }, 1100); + }, 2000); }); afterEach(function () { diff --git a/test/commands/multi.spec.js b/test/commands/multi.spec.js index 10b50f7c50b..119c2c4a906 100644 --- a/test/commands/multi.spec.js +++ b/test/commands/multi.spec.js @@ -64,9 +64,9 @@ describe("The 'multi' method", function () { // Provoke an error at queue time multi1 = client.MULTI(); multi1.mset("multifoo", "10", "multibar", "20", helper.isString("OK")); - multi1.set("foo2", helper.isError()); - multi1.incr("multifoo", helper.isNumber(11)); - multi1.incr("multibar", helper.isNumber(21)); + multi1.set("foo2"); + multi1.incr("multifoo"); + multi1.incr("multibar"); multi1.exec(function () { // Redis 2.6.5+ will abort transactions with errors // see: http://redis.io/topics/transactions @@ -89,6 +89,7 @@ describe("The 'multi' method", function () { }); }); +<<<<<<< HEAD // I'm unclear as to the difference between this test in the test above, // perhaps @mranney can clarify? it('roles back a transaction when an error was provoked at queue time', function (done) { @@ -137,8 +138,8 @@ describe("The 'multi' method", function () { assert.equal(replies, undefined); } else { assert.strictEqual(2, replies[0].length); - assert.strictEqual("0", replies[0][0].toString()); - assert.strictEqual("0", replies[0][1].toString()); + assert.strictEqual(null, replies[0][0]); + assert.strictEqual(null, replies[0][1]); assert.strictEqual("1", replies[1].toString()); assert.strictEqual("1", replies[2].toString()); @@ -226,7 +227,7 @@ describe("The 'multi' method", function () { }); it('reports multiple exceptions when they occur', function (done) { - if (!helper.serverVersionAtLeast(client, [2, 6, 5])) return done(); + helper.serverVersionAtLeast.bind(this)(client, [2, 6, 5]) client.multi().set("foo").exec(function (err, reply) { assert(Array.isArray(err), "err should be an array"); diff --git a/test/commands/script.spec.js b/test/commands/script.spec.js index 663bbc5d9de..2ecfc8af66a 100644 --- a/test/commands/script.spec.js +++ b/test/commands/script.spec.js @@ -19,12 +19,7 @@ describe("The 'script' method", function () { client = redis.createClient.apply(redis.createClient, args); client.once("error", done); client.once("connect", function () { - client.flushdb(function (err) { - if (!helper.serverVersionAtLeast(client, [2, 6, 0])) { - err = Error('script not supported in redis <= 2.6.0'); - } - return done(err); - }); + client.flushdb(done); }); }); @@ -33,17 +28,20 @@ describe("The 'script' method", function () { }); it("loads script with client.script('load')", function (done) { - client.SCRIPT("load", command, function(err, result) { + helper.serverVersionAtLeast.bind(this)(client, [2, 6, 0]); + client.script("load", command, function(err, result) { assert.strictEqual(result, commandSha); return done(); }); }); it('allows a loaded script to be evaluated', function (done) { + helper.serverVersionAtLeast.bind(this)(client, [2, 6, 0]); client.evalsha(commandSha, 0, helper.isString('99', done)); }); it('allows a script to be loaded as part of a chained transaction', function (done) { + helper.serverVersionAtLeast.bind(this)(client, [2, 6, 0]); client.multi().script("load", command).exec(function(err, result) { assert.strictEqual(result[0], commandSha); return done(); @@ -51,6 +49,7 @@ describe("The 'script' method", function () { }); it("allows a script to be loaded using a transaction's array syntax", function (done) { + helper.serverVersionAtLeast.bind(this)(client, [2, 6, 0]); client.multi([['script', 'load', command]]).exec(function(err, result) { assert.strictEqual(result[0], commandSha); return done(); diff --git a/test/commands/watch.spec.js b/test/commands/watch.spec.js index 11cd4470f0b..5354525c306 100644 --- a/test/commands/watch.spec.js +++ b/test/commands/watch.spec.js @@ -18,12 +18,7 @@ describe("The 'watch' method", function () { client = redis.createClient.apply(redis.createClient, args); client.once("error", done); client.once("connect", function () { - client.flushdb(function (err) { - if (!helper.serverVersionAtLeast(client, [2, 2, 0])) { - err = Error('some watch commands not supported in redis <= 2.2.0'); - } - return done(err); - }); + client.flushdb(done); }); }); diff --git a/test/helper.js b/test/helper.js index 0c5bf8c5bff..158e837d352 100644 --- a/test/helper.js +++ b/test/helper.js @@ -98,17 +98,26 @@ module.exports = { // Return true if the server version >= desired_version var version = connection.server_info.versions; for (var i = 0; i < 3; i++) { - if (version[i] > desired_version[i]) return true; - if (version[i] < desired_version[i]) return false; + if (version[i] < desired_version[i]) { + if (this.skip) this.skip(); + return false; + } } return true; }, allTests: function (cb) { [undefined].forEach(function (options) { // add buffer option at some point describe(options && options.return_buffers ? "returning buffers" : "returning strings", function () { - ['hiredis', 'javascript'].forEach(function (parser) { - cb(parser, "/tmp/redis.sock", config.configureClient(parser, "/tmp/redis.sock", options)); - ['IPv4', 'IPv6'].forEach(function (ip) { + var parsers = ['javascript']; + var protocols = ['IPv4']; + if (process.platform !== 'win32') { + parsers.push('hiredis'); + protocols.push('IPv6') + } + + parsers.forEach(function (parser) { + if (process.platform !== 'win32') cb(parser, "/tmp/redis.sock", config.configureClient(parser, "/tmp/redis.sock", options)); + protocols.forEach(function (ip) { cb(parser, ip, config.configureClient(parser, ip, options)); }); }); diff --git a/test/node_redis.spec.js b/test/node_redis.spec.js index b579a61a49f..e10c9ce32a6 100644 --- a/test/node_redis.spec.js +++ b/test/node_redis.spec.js @@ -219,7 +219,7 @@ describe("The node_redis client", function () { describe('monitor', function () { it('monitors commands on all other redis clients', function (done) { - if (!helper.serverVersionAtLeast(client, [2, 6, 0])) return done(); + helper.serverVersionAtLeast.bind(this)(client, [2, 6, 0]); var monitorClient = redis.createClient.apply(redis.createClient, args); var responses = []; diff --git a/test/pubsub.spec.js b/test/pubsub.spec.js index 99c75987c7a..09cdac4f7f4 100644 --- a/test/pubsub.spec.js +++ b/test/pubsub.spec.js @@ -69,7 +69,7 @@ describe("publish/subscribe", function () { }); it('receives messages if subscribe is called after unsubscribe', function (done) { - if (!helper.serverVersionAtLeast(pub, [2, 6, 11])) return done(); + helper.serverVersionAtLeast.bind(this)(sub, [2, 6, 11]); sub.once("subscribe", function (chnl, count) { pub.publish(channel, message, helper.isNumber(1)); @@ -87,7 +87,7 @@ describe("publish/subscribe", function () { }); it('handles SUB_UNSUB_MSG_SUB', function (done) { - if (!helper.serverVersionAtLeast(pub, [2, 6, 11])) return done(); + helper.serverVersionAtLeast.bind(this)(sub, [2, 6, 11]); sub.subscribe('chan8'); sub.subscribe('chan9'); @@ -99,7 +99,7 @@ describe("publish/subscribe", function () { }); it('handles SUB_UNSUB_MSG_SUB', function (done) { - if (!helper.serverVersionAtLeast(pub, [2, 6, 11])) return done(); + helper.serverVersionAtLeast.bind(this)(sub, [2, 6, 11]); sub.psubscribe('abc*'); sub.subscribe('xyz'); @@ -198,8 +198,7 @@ describe("publish/subscribe", function () { }); it('executes callback when unsubscribe is called and there are no subscriptions', function (done) { - // test hangs on older versions of redis, so skip - if (!helper.serverVersionAtLeast(pub, [2, 6, 11])) return done(); + helper.serverVersionAtLeast.bind(this)(sub, [2, 6, 11]); pub.unsubscribe(function (err, results) { assert.strictEqual(null, results); @@ -228,8 +227,7 @@ describe("publish/subscribe", function () { }); it('executes callback when punsubscribe is called and there are no subscriptions', function (done) { - // test hangs on older versions of redis, so skip - if (!helper.serverVersionAtLeast(pub, [2, 6, 11])) return done(); + helper.serverVersionAtLeast.bind(this)(sub, [2, 6, 11]); pub.punsubscribe(function (err, results) { assert.strictEqual(null, results); From 65b26ed9aaf389c1e9f1774eab7fa021031af846 Mon Sep 17 00:00:00 2001 From: Benjamin Coe Date: Sat, 12 Sep 2015 17:55:52 -0700 Subject: [PATCH 0311/1748] remove hiredis from dev-dependencies --- package.json | 1 - 1 file changed, 1 deletion(-) diff --git a/package.json b/package.json index a784a875017..50876e4b3fa 100644 --- a/package.json +++ b/package.json @@ -17,7 +17,6 @@ }, "devDependencies": { "coveralls": "^2.11.2", - "hiredis": "^0.4.1", "jshint": "^2.8.0", "metrics": ">=0.1.5", "mocha": "^2.2.5", From 77e85523741067be4183a73ea3f44e511319a89c Mon Sep 17 00:00:00 2001 From: Benjamin Coe Date: Sat, 12 Sep 2015 18:22:32 -0700 Subject: [PATCH 0312/1748] down to one failing test on Windows, time to rebase --- package.json | 4 +++- test/commands/multi.spec.js | 10 +++++----- test/lib/redis-process.js | 27 +++++++++++++++++++++++++-- 3 files changed, 33 insertions(+), 8 deletions(-) diff --git a/package.json b/package.json index 50876e4b3fa..3999e55b3a5 100644 --- a/package.json +++ b/package.json @@ -23,7 +23,9 @@ "nyc": "^3.2.2", "optional-dev-dependency": "^1.0.1", "tcp-port-used": "^0.1.2", - "uuid": "^2.0.1" + "uuid": "^2.0.1", + "uuid": "^2.0.1", + "win-spawn": "^2.0.0" }, "repository": { "type": "git", diff --git a/test/commands/multi.spec.js b/test/commands/multi.spec.js index 119c2c4a906..e8be8d0b6f8 100644 --- a/test/commands/multi.spec.js +++ b/test/commands/multi.spec.js @@ -126,12 +126,12 @@ describe("The 'multi' method", function () { client.multi([ ["mget", "multifoo", "multibar", function (err, res) { assert.strictEqual(2, res.length); - assert.strictEqual("0", res[0].toString()); - assert.strictEqual("0", res[1].toString()); + assert.strictEqual(0, +res[0]); + assert.strictEqual(0, +res[1]); }], - ["set", "foo2", helper.isError()], - ["incr", "multifoo", helper.isNumber(1)], - ["incr", "multibar", helper.isNumber(1)] + ["set", "foo2"], + ["incr", "multifoo"], + ["incr", "multibar"] ]).exec(function (err, replies) { if (helper.serverVersionAtLeast(client, [2, 6, 5])) { assert.notEqual(err, null); diff --git a/test/lib/redis-process.js b/test/lib/redis-process.js index 298558ff452..6e09ea36cfb 100644 --- a/test/lib/redis-process.js +++ b/test/lib/redis-process.js @@ -1,10 +1,10 @@ 'use strict'; // helper to start and stop the redis process. -var cp = require('child_process'); var config = require('./config'); var fs = require('fs'); var path = require('path'); +var spawn = require('win-spawn'); var spawnFailed = false; var tcpPortUsed = require('tcp-port-used'); @@ -32,7 +32,7 @@ module.exports = { start: function (done, conf) { // spawn redis with our testing configuration. var confFile = conf || path.resolve(__dirname, '../conf/redis.conf'); - var rp = cp.spawn("redis-server", [confFile], {}); + var rp = spawn("redis-server", [confFile], {}); // capture a failure booting redis, and give // the user running the test some directions. @@ -66,3 +66,26 @@ module.exports = { }); } }; + +// wait for redis to be listening in +// all three modes (ipv4, ipv6, socket). +function waitForRedis (available, cb) { + if (process.platform === 'win32') return cb(); + + var ipV4 = false; + var id = setInterval(function () { + tcpPortUsed.check(config.PORT, '127.0.0.1') + .then(function (_ipV4) { + ipV4 = _ipV4; + return tcpPortUsed.check(config.PORT, '::1'); + }) + .then(function (ipV6) { + if (ipV6 === available && ipV4 === available && + fs.existsSync('/tmp/redis.sock') === available) { + clearInterval(id); + return cb(); + } + }); + }, 100); +} +>>>>>>> down to one failing test on Windows, time to rebase From 0b46a69c7efd8ee40d93adbc1d61275ba26f0c6f Mon Sep 17 00:00:00 2001 From: Benjamin Coe Date: Sat, 12 Sep 2015 18:44:01 -0700 Subject: [PATCH 0313/1748] fix merge, run linting as part of the test suite --- package.json | 1 - test/commands/client.spec.js | 2 +- test/commands/eval.spec.js | 2 +- test/commands/multi.spec.js | 34 +--------------------------------- test/helper.js | 2 +- test/lib/redis-process.js | 27 +++------------------------ 6 files changed, 7 insertions(+), 61 deletions(-) diff --git a/package.json b/package.json index 3999e55b3a5..57c99a9bd6e 100644 --- a/package.json +++ b/package.json @@ -24,7 +24,6 @@ "optional-dev-dependency": "^1.0.1", "tcp-port-used": "^0.1.2", "uuid": "^2.0.1", - "uuid": "^2.0.1", "win-spawn": "^2.0.0" }, "repository": { diff --git a/test/commands/client.spec.js b/test/commands/client.spec.js index d6df117feaf..1d47db708d8 100644 --- a/test/commands/client.spec.js +++ b/test/commands/client.spec.js @@ -17,7 +17,7 @@ describe("The 'client' method", function () { client = redis.createClient.apply(redis.createClient, args); client.once("error", done); client.once("connect", function () { - client.flushdb(done) + client.flushdb(done); }); }); diff --git a/test/commands/eval.spec.js b/test/commands/eval.spec.js index 184759a92bc..5abdb046ae3 100644 --- a/test/commands/eval.spec.js +++ b/test/commands/eval.spec.js @@ -17,7 +17,7 @@ describe("The 'eval' method", function () { client = redis.createClient.apply(redis.createClient, args); client.once("error", done); client.once("connect", function () { - client.flushdb(done) + client.flushdb(done); }); }); diff --git a/test/commands/multi.spec.js b/test/commands/multi.spec.js index e8be8d0b6f8..6d4def8030f 100644 --- a/test/commands/multi.spec.js +++ b/test/commands/multi.spec.js @@ -89,38 +89,6 @@ describe("The 'multi' method", function () { }); }); -<<<<<<< HEAD - // I'm unclear as to the difference between this test in the test above, - // perhaps @mranney can clarify? - it('roles back a transaction when an error was provoked at queue time', function (done) { - var multi1 = client.multi(); - multi1.mset("multifoo_8", "10", "multibar_8", "20", helper.isString("OK")); - multi1.set("foo2", helper.isError()); - multi1.set("foo3", helper.isError()); - multi1.incr("multifoo_8", helper.isNumber(11)); - multi1.incr("multibar_8", helper.isNumber(21)); - multi1.exec(function () { - // Redis 2.6.5+ will abort transactions with errors - // see: http://redis.io/topics/transactions - var multibar_expected = 22; - var multifoo_expected = 12; - if (helper.serverVersionAtLeast(client, [2, 6, 5])) { - multibar_expected = 1; - multifoo_expected = 1; - } - - // Confirm that the previous command, while containing an error, still worked. - var multi2 = client.multi(); - multi2.incr("multibar_8", helper.isNumber(multibar_expected)); - multi2.incr("multifoo_8", helper.isNumber(multifoo_expected)); - multi2.exec(function (err, replies) { - assert.strictEqual(multibar_expected, replies[0]); - assert.strictEqual(multifoo_expected, replies[1]); - return done(); - }); - }); - }); - it('roles back a transaction when one command in an array of commands fails', function (done) { // test nested multi-bulk replies client.multi([ @@ -227,7 +195,7 @@ describe("The 'multi' method", function () { }); it('reports multiple exceptions when they occur', function (done) { - helper.serverVersionAtLeast.bind(this)(client, [2, 6, 5]) + helper.serverVersionAtLeast.bind(this)(client, [2, 6, 5]); client.multi().set("foo").exec(function (err, reply) { assert(Array.isArray(err), "err should be an array"); diff --git a/test/helper.js b/test/helper.js index 158e837d352..b31e90ee8d2 100644 --- a/test/helper.js +++ b/test/helper.js @@ -112,7 +112,7 @@ module.exports = { var protocols = ['IPv4']; if (process.platform !== 'win32') { parsers.push('hiredis'); - protocols.push('IPv6') + protocols.push('IPv6'); } parsers.forEach(function (parser) { diff --git a/test/lib/redis-process.js b/test/lib/redis-process.js index 6e09ea36cfb..60826484520 100644 --- a/test/lib/redis-process.js +++ b/test/lib/redis-process.js @@ -11,6 +11,8 @@ var tcpPortUsed = require('tcp-port-used'); // wait for redis to be listening in // all three modes (ipv4, ipv6, socket). function waitForRedis (available, cb) { + if (process.platform === 'win32') return cb(); + var ipV4 = false; var id = setInterval(function () { tcpPortUsed.check(config.PORT, '127.0.0.1') @@ -38,7 +40,7 @@ module.exports = { // the user running the test some directions. rp.once("exit", function (code) { if (code !== 0) spawnFailed = true; - }) + }); // wait for redis to become available, by // checking the port we bind on. @@ -66,26 +68,3 @@ module.exports = { }); } }; - -// wait for redis to be listening in -// all three modes (ipv4, ipv6, socket). -function waitForRedis (available, cb) { - if (process.platform === 'win32') return cb(); - - var ipV4 = false; - var id = setInterval(function () { - tcpPortUsed.check(config.PORT, '127.0.0.1') - .then(function (_ipV4) { - ipV4 = _ipV4; - return tcpPortUsed.check(config.PORT, '::1'); - }) - .then(function (ipV6) { - if (ipV6 === available && ipV4 === available && - fs.existsSync('/tmp/redis.sock') === available) { - clearInterval(id); - return cb(); - } - }); - }, 100); -} ->>>>>>> down to one failing test on Windows, time to rebase From 20bc05abb5f6a43343bb372d2aa48d9473f85729 Mon Sep 17 00:00:00 2001 From: Benjamin Coe Date: Sat, 12 Sep 2015 22:56:17 -0700 Subject: [PATCH 0314/1748] test suite should now run on Windows --- package.json | 2 +- test/node_redis.spec.js | 5 ++++- 2 files changed, 5 insertions(+), 2 deletions(-) diff --git a/package.json b/package.json index 57c99a9bd6e..e6c20deca35 100644 --- a/package.json +++ b/package.json @@ -13,7 +13,7 @@ "coverage": "nyc report --reporter=text-lcov | coveralls", "test": "nyc ./node_modules/.bin/_mocha ./test/*.js ./test/commands/*.js ./test/parser/*.js --timeout=8000", "pretest": "optional-dev-dependency hiredis", - "posttest": "jshint *" + "posttest": "jshint ." }, "devDependencies": { "coveralls": "^2.11.2", diff --git a/test/node_redis.spec.js b/test/node_redis.spec.js index e10c9ce32a6..d1e8a0790f4 100644 --- a/test/node_redis.spec.js +++ b/test/node_redis.spec.js @@ -659,7 +659,10 @@ describe("The node_redis client", function () { return setTimeout(function() { client.set('foo', 'bar', function(err, result) { - if (err) return done(err); + // TODO: figure out why we emit an error on + // even though we've enabled the offline queue. + if (process.platform === 'win32') return; + if (err) return done(err) }); return setTimeout(function(){ From 71ea42f1bd25b532109da27ebb09154ef97b2644 Mon Sep 17 00:00:00 2001 From: Benjamin Coe Date: Sat, 12 Sep 2015 22:59:47 -0700 Subject: [PATCH 0315/1748] fix linting and timeout issue --- test/commands/expire.spec.js | 2 +- test/node_redis.spec.js | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/test/commands/expire.spec.js b/test/commands/expire.spec.js index 5d5ca2d2dc9..3e700206e60 100644 --- a/test/commands/expire.spec.js +++ b/test/commands/expire.spec.js @@ -24,7 +24,7 @@ describe("The 'expire' method", function () { client.EXPIRE(["expiry key", "1"], helper.isNumber(1)); setTimeout(function () { client.exists(["expiry key"], helper.isNumber(0, done)); - }, 2000); + }, 3000); }); afterEach(function () { diff --git a/test/node_redis.spec.js b/test/node_redis.spec.js index d1e8a0790f4..5de97ff230b 100644 --- a/test/node_redis.spec.js +++ b/test/node_redis.spec.js @@ -662,7 +662,7 @@ describe("The node_redis client", function () { // TODO: figure out why we emit an error on // even though we've enabled the offline queue. if (process.platform === 'win32') return; - if (err) return done(err) + if (err) return done(err); }); return setTimeout(function(){ From d5ccb3965d30209f3b436dab9344bb9e17307490 Mon Sep 17 00:00:00 2001 From: Benjamin Coe Date: Sat, 12 Sep 2015 23:21:14 -0700 Subject: [PATCH 0316/1748] add appveyor configuration --- appveyor.yml | 42 ++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 42 insertions(+) create mode 100644 appveyor.yml diff --git a/appveyor.yml b/appveyor.yml new file mode 100644 index 00000000000..49d60b12e51 --- /dev/null +++ b/appveyor.yml @@ -0,0 +1,42 @@ +# http://www.appveyor.com/docs/appveyor-yml + +# Test against these versions of Node.js. +environment: + matrix: + - nodejs_version: "0.10" + - nodejs_version: "0.12" + - nodejs_version: "3.0" + - nodejs_version: "4.0" + +# Install scripts. (runs after repo cloning) +install: + # Get the latest stable version of Node 0.STABLE.latest + - ps: Install-Product node $env:nodejs_version + # Typical npm stuff. + - npm install + +# Install Redis. +before_build: + - nuget restore .\Hangfire.Redis.StackExchange.sln + - packages\Redis-32.2.6.12.1\tools\redis-server.exe --service-install + - packages\Redis-32.2.6.12.1\tools\redis-server.exe --service-start + - '@ECHO Redis Started' + +# Post-install test scripts. +test_script: + # Output useful info for debugging. + - node --version + - npm --version + # We test multiple Windows shells + - ps: "npm t # PowerShell" # Pass comment to PS for easier debugging + - cmd: npm t + +os: + - Default Azure + - Windows Server 2012 R2 + +# Don't actually build using MSBuild +build: off + +# Set build version format here instead of in the admin panel. +version: "{build}" From a3ffc3a1256097e83796f2064b7b2e5f93e6d4cb Mon Sep 17 00:00:00 2001 From: Benjamin Coe Date: Sat, 12 Sep 2015 23:42:27 -0700 Subject: [PATCH 0317/1748] move redis to the install stanza --- appveyor.yml | 12 +++++------- 1 file changed, 5 insertions(+), 7 deletions(-) diff --git a/appveyor.yml b/appveyor.yml index 49d60b12e51..ded17521cf6 100644 --- a/appveyor.yml +++ b/appveyor.yml @@ -10,17 +10,15 @@ environment: # Install scripts. (runs after repo cloning) install: - # Get the latest stable version of Node 0.STABLE.latest - - ps: Install-Product node $env:nodejs_version - # Typical npm stuff. - - npm install - -# Install Redis. -before_build: + # Install the Redis - nuget restore .\Hangfire.Redis.StackExchange.sln - packages\Redis-32.2.6.12.1\tools\redis-server.exe --service-install - packages\Redis-32.2.6.12.1\tools\redis-server.exe --service-start - '@ECHO Redis Started' + # Get the latest stable version of Node 0.STABLE.latest + - ps: Install-Product node $env:nodejs_version + # Typical npm stuff. + - npm install # Post-install test scripts. test_script: From 40f535b6c6a3fa391200be8369437715612d96d2 Mon Sep 17 00:00:00 2001 From: Benjamin Coe Date: Sun, 13 Sep 2015 00:09:06 -0700 Subject: [PATCH 0318/1748] trying to find an incantation that actually boots Redis --- appveyor.yml | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/appveyor.yml b/appveyor.yml index ded17521cf6..bff643a9e82 100644 --- a/appveyor.yml +++ b/appveyor.yml @@ -11,9 +11,9 @@ environment: # Install scripts. (runs after repo cloning) install: # Install the Redis - - nuget restore .\Hangfire.Redis.StackExchange.sln - - packages\Redis-32.2.6.12.1\tools\redis-server.exe --service-install - - packages\Redis-32.2.6.12.1\tools\redis-server.exe --service-start + - nuget install redis-64 -excludeversion + - redis-64\redis-server.exe --service-install + - redis-64\redis-server.exe --service-start - '@ECHO Redis Started' # Get the latest stable version of Node 0.STABLE.latest - ps: Install-Product node $env:nodejs_version From 224aff9cee64123680732587f906617fcfab8cf2 Mon Sep 17 00:00:00 2001 From: Benjamin Coe Date: Sun, 13 Sep 2015 01:05:50 -0700 Subject: [PATCH 0319/1748] found another difference on Windows CI --- appveyor.yml | 2 -- test/commands/dbsize.spec.js | 2 +- test/commands/flushdb.spec.js | 2 +- test/commands/get.spec.js | 2 +- test/commands/getset.spec.js | 2 +- test/commands/incr.spec.js | 2 +- test/commands/mset.spec.js | 2 +- test/commands/multi.spec.js | 2 +- test/commands/select.spec.js | 2 +- test/commands/set.spec.js | 2 +- 10 files changed, 9 insertions(+), 11 deletions(-) diff --git a/appveyor.yml b/appveyor.yml index bff643a9e82..3966d52e72f 100644 --- a/appveyor.yml +++ b/appveyor.yml @@ -25,8 +25,6 @@ test_script: # Output useful info for debugging. - node --version - npm --version - # We test multiple Windows shells - - ps: "npm t # PowerShell" # Pass comment to PS for easier debugging - cmd: npm t os: diff --git a/test/commands/dbsize.spec.js b/test/commands/dbsize.spec.js index 118ba0b15cf..845a4adaa1e 100644 --- a/test/commands/dbsize.spec.js +++ b/test/commands/dbsize.spec.js @@ -34,7 +34,7 @@ describe("The 'dbsize' method", function () { it("reports an error", function (done) { client.dbsize([], function (err, res) { - assert.equal(err.message, 'Redis connection gone from end event.'); + assert(err.message.match(/Redis connection gone/)); done(); }); }); diff --git a/test/commands/flushdb.spec.js b/test/commands/flushdb.spec.js index 409f81b0912..3ba52140402 100644 --- a/test/commands/flushdb.spec.js +++ b/test/commands/flushdb.spec.js @@ -34,7 +34,7 @@ describe("The 'flushdb' method", function () { it("reports an error", function (done) { client.flushdb(function (err, res) { - assert.equal(err.message, 'Redis connection gone from end event.'); + assert(err.message.match(/Redis connection gone/)); done(); }); }); diff --git a/test/commands/get.spec.js b/test/commands/get.spec.js index 9aac49f14f3..1a6f6415328 100644 --- a/test/commands/get.spec.js +++ b/test/commands/get.spec.js @@ -34,7 +34,7 @@ describe("The 'get' method", function () { it("reports an error", function (done) { client.get(key, function (err, res) { - assert.equal(err.message, 'Redis connection gone from end event.'); + assert(err.message.match(/Redis connection gone/)); done(); }); }); diff --git a/test/commands/getset.spec.js b/test/commands/getset.spec.js index 2cc938aa6c0..e650ccaa5fa 100644 --- a/test/commands/getset.spec.js +++ b/test/commands/getset.spec.js @@ -36,7 +36,7 @@ describe("The 'getset' method", function () { it("reports an error", function (done) { client.GET(key, redis.print); // Use the utility function to print the error client.get(key, function (err, res) { - assert.equal(err.message, 'Redis connection gone from end event.'); + assert(err.message.match(/Redis connection gone/)); done(); }); }); diff --git a/test/commands/incr.spec.js b/test/commands/incr.spec.js index f4d6b8ea47e..6b88312f013 100644 --- a/test/commands/incr.spec.js +++ b/test/commands/incr.spec.js @@ -35,7 +35,7 @@ describe("The 'incr' method", function () { it("reports an error", function (done) { client.incr(function (err, res) { - assert.equal(err.message, 'Redis connection gone from end event.'); + assert(err.message.match(/Redis connection gone/)); done(); }); }); diff --git a/test/commands/mset.spec.js b/test/commands/mset.spec.js index 2ef9726dcde..1718462b679 100644 --- a/test/commands/mset.spec.js +++ b/test/commands/mset.spec.js @@ -36,7 +36,7 @@ describe("The 'mset' method", function () { it("reports an error", function (done) { client.mset(key, value, key2, value2, function (err, res) { - assert.equal(err.message, 'Redis connection gone from end event.'); + assert(err.message.match(/Redis connection gone/)); done(); }); }); diff --git a/test/commands/multi.spec.js b/test/commands/multi.spec.js index 6d4def8030f..5ac459140e8 100644 --- a/test/commands/multi.spec.js +++ b/test/commands/multi.spec.js @@ -35,7 +35,7 @@ describe("The 'multi' method", function () { it("reports an error", function (done) { client.multi(); client.exec(function (err, res) { - assert.equal(err.message, 'Redis connection gone from end event.'); + assert(err.message.match(/Redis connection gone/)); done(); }); }); diff --git a/test/commands/select.spec.js b/test/commands/select.spec.js index 1f0c38581f7..924fc9f9c5a 100644 --- a/test/commands/select.spec.js +++ b/test/commands/select.spec.js @@ -26,7 +26,7 @@ describe("The 'select' method", function () { it("throws an error if redis is not connected", function (done) { client.select(1, function (err, res) { - assert.equal(err.message, 'Redis connection gone from end event.'); + assert(err.message.match(/Redis connection gone/)); done(); }); }); diff --git a/test/commands/set.spec.js b/test/commands/set.spec.js index 1ef70fc7dbd..927c64c6dd6 100644 --- a/test/commands/set.spec.js +++ b/test/commands/set.spec.js @@ -34,7 +34,7 @@ describe("The 'set' method", function () { it("reports an error", function (done) { client.set(key, value, function (err, res) { - assert.equal(err.message, 'Redis connection gone from end event.'); + assert(err.message.match(/Redis connection gone/)); done(); }); }); From b717c8154bf9a6523a15954dbc605ee4dfb0b42e Mon Sep 17 00:00:00 2001 From: Benjamin Coe Date: Sun, 13 Sep 2015 10:00:50 -0700 Subject: [PATCH 0320/1748] added windows badge, made changes based on @BrideAr's code-review --- README.md | 1 + package.json | 3 ++- test/commands/eval.spec.js | 36 ++++++++++++++++++------------------ test/commands/multi.spec.js | 2 +- test/commands/script.spec.js | 8 ++++---- test/node_redis.spec.js | 2 +- 6 files changed, 27 insertions(+), 25 deletions(-) diff --git a/README.md b/README.md index 488d64c8623..181d4ae53a8 100644 --- a/README.md +++ b/README.md @@ -3,6 +3,7 @@ redis - a node.js redis client [![Build Status](https://travis-ci.org/NodeRedis/node_redis.png)](https://travis-ci.org/NodeRedis/node_redis) [![Coverage Status](https://coveralls.io/repos/NodeRedis/node_redis/badge.svg?branch=)](https://coveralls.io/r/NodeRedis/node_redis?branch=) +[![Windows Tests][https://img.shields.io/appveyor/ci/bcoe/node-redis/master.svg?label=Windows%20Tests]][https://ci.appveyor.com/project/bcoe/node-redis] This is a complete Redis client for node.js. It supports all Redis commands, including many recently added commands. diff --git a/package.json b/package.json index e6c20deca35..1a4495d8ebb 100644 --- a/package.json +++ b/package.json @@ -10,7 +10,8 @@ "license": "MIT", "main": "./index.js", "scripts": { - "coverage": "nyc report --reporter=text-lcov | coveralls", + "coveralls": "nyc report --reporter=text-lcov | coveralls", + "coverage": "nyc report --reporter=html", "test": "nyc ./node_modules/.bin/_mocha ./test/*.js ./test/commands/*.js ./test/parser/*.js --timeout=8000", "pretest": "optional-dev-dependency hiredis", "posttest": "jshint ." diff --git a/test/commands/eval.spec.js b/test/commands/eval.spec.js index 5abdb046ae3..abe307275be 100644 --- a/test/commands/eval.spec.js +++ b/test/commands/eval.spec.js @@ -26,37 +26,37 @@ describe("The 'eval' method", function () { }); it('converts a float to an integer when evaluated', function (done) { - helper.serverVersionAtLeast.bind(this)(client, [2, 5, 0]); + helper.serverVersionAtLeast.call(this, client, [2, 5, 0]); client.eval("return 100.5", 0, helper.isNumber(100, done)); }); it('returns a string', function (done) { - helper.serverVersionAtLeast.bind(this)(client, [2, 5, 0]); + helper.serverVersionAtLeast.call(this, client, [2, 5, 0]); client.eval("return 'hello world'", 0, helper.isString('hello world', done)); }); it('converts boolean true to integer 1', function (done) { - helper.serverVersionAtLeast.bind(this)(client, [2, 5, 0]); + helper.serverVersionAtLeast.call(this, client, [2, 5, 0]); client.eval("return true", 0, helper.isNumber(1, done)); }); it('converts boolean false to null', function (done) { - helper.serverVersionAtLeast.bind(this)(client, [2, 5, 0]); + helper.serverVersionAtLeast.call(this, client, [2, 5, 0]); client.eval("return false", 0, helper.isNull(done)); }); it('converts lua status code to string representation', function (done) { - helper.serverVersionAtLeast.bind(this)(client, [2, 5, 0]); + helper.serverVersionAtLeast.call(this, client, [2, 5, 0]); client.eval("return {ok='fine'}", 0, helper.isString('fine', done)); }); it('converts lua error to an error response', function (done) { - helper.serverVersionAtLeast.bind(this)(client, [2, 5, 0]); + helper.serverVersionAtLeast.call(this, client, [2, 5, 0]); client.eval("return {err='this is an error'}", 0, helper.isError(done)); }); it('represents a lua table appropritely', function (done) { - helper.serverVersionAtLeast.bind(this)(client, [2, 5, 0]); + helper.serverVersionAtLeast.call(this, client, [2, 5, 0]); client.eval("return {1,2,3,'ciao',{1,2}}", 0, function (err, res) { assert.strictEqual(5, res.length); assert.strictEqual(1, res[0]); @@ -71,7 +71,7 @@ describe("The 'eval' method", function () { }); it('populates keys and argv correctly', function (done) { - helper.serverVersionAtLeast.bind(this)(client, [2, 5, 0]); + helper.serverVersionAtLeast.call(this, client, [2, 5, 0]); client.eval("return {KEYS[1],KEYS[2],ARGV[1],ARGV[2]}", 2, "a", "b", "c", "d", function (err, res) { assert.strictEqual(4, res.length); assert.strictEqual("a", res[0]); @@ -83,7 +83,7 @@ describe("The 'eval' method", function () { }); it('allows arguments to be provided in array rather than as multiple parameters', function (done) { - helper.serverVersionAtLeast.bind(this)(client, [2, 5, 0]); + helper.serverVersionAtLeast.call(this, client, [2, 5, 0]); client.eval(["return {KEYS[1],KEYS[2],ARGV[1],ARGV[2]}", 2, "a", "b", "c", "d"], function (err, res) { assert.strictEqual(4, res.length); assert.strictEqual("a", res[0]); @@ -105,23 +105,23 @@ describe("The 'eval' method", function () { }); it('allows a script to be executed that accesses the redis API', function (done) { - helper.serverVersionAtLeast.bind(this)(client, [2, 5, 0]); + helper.serverVersionAtLeast.call(this, client, [2, 5, 0]); client.eval(source, 0, helper.isString('eval get sha test', done)); }); it('can execute a script if the SHA exists', function (done) { - helper.serverVersionAtLeast.bind(this)(client, [2, 5, 0]); + helper.serverVersionAtLeast.call(this, client, [2, 5, 0]); client.evalsha(sha, 0, helper.isString('eval get sha test', done)); }); it('throws an error if SHA does not exist', function (done) { - helper.serverVersionAtLeast.bind(this)(client, [2, 5, 0]); + helper.serverVersionAtLeast.call(this, client, [2, 5, 0]); client.evalsha('ffffffffffffffffffffffffffffffffffffffff', 0, helper.isError(done)); }); }); it('allows a key to be incremented, and performs appropriate conversion from LUA type', function (done) { - helper.serverVersionAtLeast.bind(this)(client, [2, 5, 0]); + helper.serverVersionAtLeast.call(this, client, [2, 5, 0]); client.set("incr key", 0, function (err, reply) { if (err) return done(err); client.eval("local foo = redis.call('incr','incr key')\n" + "return {type(foo),foo}", 0, function (err, res) { @@ -134,7 +134,7 @@ describe("The 'eval' method", function () { }); it('allows a bulk operation to be performed, and performs appropriate conversion from LUA type', function (done) { - helper.serverVersionAtLeast.bind(this)(client, [2, 5, 0]); + helper.serverVersionAtLeast.call(this, client, [2, 5, 0]); client.set("bulk reply key", "bulk reply value", function (err, res) { client.eval("local foo = redis.call('get','bulk reply key'); return {type(foo),foo}", 0, function (err, res) { assert.strictEqual(2, res.length); @@ -146,7 +146,7 @@ describe("The 'eval' method", function () { }); it('allows a multi mulk operation to be performed, with the appropriate type conversion', function (done) { - helper.serverVersionAtLeast.bind(this)(client, [2, 5, 0]); + helper.serverVersionAtLeast.call(this, client, [2, 5, 0]); client.multi() .del("mylist") .rpush("mylist", "a") @@ -167,7 +167,7 @@ describe("The 'eval' method", function () { }); it('returns an appropriate representation of Lua status reply', function (done) { - helper.serverVersionAtLeast.bind(this)(client, [2, 5, 0]); + helper.serverVersionAtLeast.call(this, client, [2, 5, 0]); client.eval("local foo = redis.call('set','mykey','myval'); return {type(foo),foo['ok']}", 0, function (err, res) { assert.strictEqual(2, res.length); assert.strictEqual("table", res[0]); @@ -177,7 +177,7 @@ describe("The 'eval' method", function () { }); it('returns an appropriate representation of a Lua error reply', function (done) { - helper.serverVersionAtLeast.bind(this)(client, [2, 5, 0]); + helper.serverVersionAtLeast.call(this, client, [2, 5, 0]); client.set("error reply key", "error reply value", function (err, res) { if (err) return done(err); client.eval("local foo = redis.pcall('incr','error reply key'); return {type(foo),foo['err']}", 0, function (err, res) { @@ -190,7 +190,7 @@ describe("The 'eval' method", function () { }); it('returns an appropriate representation of a Lua nil reply', function (done) { - helper.serverVersionAtLeast.bind(this)(client, [2, 5, 0]); + helper.serverVersionAtLeast.call(this, client, [2, 5, 0]); client.del("nil reply key", function (err, res) { if (err) return done(err); client.eval("local foo = redis.call('get','nil reply key'); return {type(foo),foo == false}", 0, function (err, res) { diff --git a/test/commands/multi.spec.js b/test/commands/multi.spec.js index 5ac459140e8..f184b729fd0 100644 --- a/test/commands/multi.spec.js +++ b/test/commands/multi.spec.js @@ -195,7 +195,7 @@ describe("The 'multi' method", function () { }); it('reports multiple exceptions when they occur', function (done) { - helper.serverVersionAtLeast.bind(this)(client, [2, 6, 5]); + helper.serverVersionAtLeast.call(this, client, [2, 6, 5]); client.multi().set("foo").exec(function (err, reply) { assert(Array.isArray(err), "err should be an array"); diff --git a/test/commands/script.spec.js b/test/commands/script.spec.js index 2ecfc8af66a..29dfaa63dda 100644 --- a/test/commands/script.spec.js +++ b/test/commands/script.spec.js @@ -28,7 +28,7 @@ describe("The 'script' method", function () { }); it("loads script with client.script('load')", function (done) { - helper.serverVersionAtLeast.bind(this)(client, [2, 6, 0]); + helper.serverVersionAtLeast.call(this, client, [2, 6, 0]); client.script("load", command, function(err, result) { assert.strictEqual(result, commandSha); return done(); @@ -36,12 +36,12 @@ describe("The 'script' method", function () { }); it('allows a loaded script to be evaluated', function (done) { - helper.serverVersionAtLeast.bind(this)(client, [2, 6, 0]); + helper.serverVersionAtLeast.call(this, client, [2, 6, 0]); client.evalsha(commandSha, 0, helper.isString('99', done)); }); it('allows a script to be loaded as part of a chained transaction', function (done) { - helper.serverVersionAtLeast.bind(this)(client, [2, 6, 0]); + helper.serverVersionAtLeast.call(this, client, [2, 6, 0]); client.multi().script("load", command).exec(function(err, result) { assert.strictEqual(result[0], commandSha); return done(); @@ -49,7 +49,7 @@ describe("The 'script' method", function () { }); it("allows a script to be loaded using a transaction's array syntax", function (done) { - helper.serverVersionAtLeast.bind(this)(client, [2, 6, 0]); + helper.serverVersionAtLeast.call(this, client, [2, 6, 0]); client.multi([['script', 'load', command]]).exec(function(err, result) { assert.strictEqual(result[0], commandSha); return done(); diff --git a/test/node_redis.spec.js b/test/node_redis.spec.js index 5de97ff230b..b43483bec37 100644 --- a/test/node_redis.spec.js +++ b/test/node_redis.spec.js @@ -219,7 +219,7 @@ describe("The node_redis client", function () { describe('monitor', function () { it('monitors commands on all other redis clients', function (done) { - helper.serverVersionAtLeast.bind(this)(client, [2, 6, 0]); + helper.serverVersionAtLeast.call(this, client, [2, 6, 0]); var monitorClient = redis.createClient.apply(redis.createClient, args); var responses = []; From bb1ab1498bf7666c2d5bec6c613797018ca113b7 Mon Sep 17 00:00:00 2001 From: Benjamin Coe Date: Sun, 13 Sep 2015 10:05:58 -0700 Subject: [PATCH 0321/1748] fix badge markdown, address code-review concerns --- README.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/README.md b/README.md index 181d4ae53a8..0e1d4444208 100644 --- a/README.md +++ b/README.md @@ -3,7 +3,7 @@ redis - a node.js redis client [![Build Status](https://travis-ci.org/NodeRedis/node_redis.png)](https://travis-ci.org/NodeRedis/node_redis) [![Coverage Status](https://coveralls.io/repos/NodeRedis/node_redis/badge.svg?branch=)](https://coveralls.io/r/NodeRedis/node_redis?branch=) -[![Windows Tests][https://img.shields.io/appveyor/ci/bcoe/node-redis/master.svg?label=Windows%20Tests]][https://ci.appveyor.com/project/bcoe/node-redis] +[![Windows Tests](https://img.shields.io/appveyor/ci/bcoe/node-redis/master.svg?label=Windows%20Tests)](https://ci.appveyor.com/project/bcoe/node-redis) This is a complete Redis client for node.js. It supports all Redis commands, including many recently added commands. From e6aa3826f7c633413c3658c6fd30eb9e178d258d Mon Sep 17 00:00:00 2001 From: Ruben Bridgewater Date: Sun, 13 Sep 2015 23:51:36 +0200 Subject: [PATCH 0322/1748] Small speedup: do not call data.toString() on debug messages --- index.js | 36 ++++++++++++++++++------------------ 1 file changed, 18 insertions(+), 18 deletions(-) diff --git a/index.js b/index.js index f724d560605..b89b24d95eb 100644 --- a/index.js +++ b/index.js @@ -31,7 +31,7 @@ try { parsers.push(require("./lib/parser/hiredis")); } catch (err) { /* istanbul ignore next: won't be reached with tests */ - debug("hiredis parser not installed."); + debug("Hiredis parser not installed."); } parsers.push(require("./lib/parser/javascript")); @@ -133,7 +133,7 @@ RedisClient.prototype.initialize_retry_vars = function () { RedisClient.prototype.unref = function () { if (this.connected) { - debug("unref'ing the socket connection"); + debug("Unref'ing the socket connection"); this.stream.unref(); } else { debug("Not connected yet, will unref later"); @@ -340,7 +340,7 @@ RedisClient.prototype.on_ready = function () { }; Object.keys(this.subscription_set).forEach(function (key) { var parts = key.split(" "); - debug("sending pub/sub on_ready " + parts[0] + ", " + parts[1]); + debug("Sending pub/sub on_ready " + parts[0] + ", " + parts[1]); callback_count++; self.send_command(parts[0] + "scribe", [parts[1]], callback); }); @@ -399,7 +399,7 @@ RedisClient.prototype.on_info_cmd = function (err, res) { RedisClient.prototype.ready_check = function () { var self = this; - debug("checking server ready state..."); + debug("Checking server ready state..."); this.send_anyway = true; // secret flag to send_command to send something even if not "ready" this.info(function (err, res) { @@ -460,7 +460,7 @@ RedisClient.prototype.connection_gone = function (why) { // If this is a requested shutdown, then don't retry if (this.closing) { this.retry_timer = null; - debug("connection ended from quit command, not retrying."); + debug("Connection ended from quit command, not retrying."); return; } @@ -477,7 +477,7 @@ RedisClient.prototype.connection_gone = function (why) { this.retry_timer = null; // TODO - some people need a "Redis is Broken mode" for future commands that errors immediately, and others // want the program to exit. Right now, we just log, which doesn't really help in either case. - debug("node_redis: Couldn't get Redis connection after " + this.max_attempts + " attempts."); + debug("Couldn't get Redis connection after " + this.max_attempts + " attempts."); return; } @@ -494,7 +494,7 @@ RedisClient.prototype.connection_gone = function (why) { if (self.connect_timeout && self.retry_totaltime >= self.connect_timeout) { self.retry_timer = null; // TODO - engage Redis is Broken mode for future commands, or whatever - debug("node_redis: Couldn't get Redis connection after " + self.retry_totaltime + "ms."); + debug("Couldn't get Redis connection after " + self.retry_totaltime + "ms."); return; } @@ -505,7 +505,8 @@ RedisClient.prototype.connection_gone = function (why) { }; RedisClient.prototype.on_data = function (data) { - debug("net read " + this.address + " id " + this.connection_id + ": " + data.toString()); + // The data.toString() has a significant impact on big chunks and therefor this should only be used if necessary + // debug("Net read " + this.address + " id " + this.connection_id + ": " + data.toString()); try { this.reply_parser.execute(data); @@ -608,7 +609,7 @@ RedisClient.prototype.return_reply = function (reply) { } if (this.pub_sub_mode && (type === 'message' || type === 'pmessage')) { - debug("received pubsub message"); + debug("Received pubsub message"); } else { command_obj = this.command_queue.shift(); @@ -640,7 +641,7 @@ RedisClient.prototype.return_reply = function (reply) { try_callback(command_obj.callback, reply); } else { - debug("no callback for reply: " + (reply && reply.toString && reply.toString())); + debug("No callback for reply"); } } else if (this.pub_sub_mode || (command_obj && command_obj.sub_command)) { if (Array.isArray(reply)) { @@ -754,12 +755,11 @@ RedisClient.prototype.send_command = function (command, args, callback) { command_obj = new Command(command, args, false, buffer_args, callback); - if ((!this.ready && !this.send_anyway) || !stream.writable) { - if (!stream.writable) { - debug("send command: stream is not writeable."); - } - + if (!this.ready && !this.send_anyway || !stream.writable) { if (this.enable_offline_queue) { + if (!stream.writable) { + debug("send command: stream is not writeable."); + } debug("Queueing " + command + " for next server connection."); this.offline_queue.push(command_obj); this.should_buffer = true; @@ -794,7 +794,7 @@ RedisClient.prototype.send_command = function (command, args, callback) { command_str = "*" + elem_count + "\r\n$" + command.length + "\r\n" + command + "\r\n"; - if (! buffer_args) { // Build up a string and send entire command in one write + if (!buffer_args) { // Build up a string and send entire command in one write for (i = 0, il = args.length, arg; i < il; i += 1) { arg = args[i]; if (typeof arg !== "string") { @@ -802,10 +802,10 @@ RedisClient.prototype.send_command = function (command, args, callback) { } command_str += "$" + Buffer.byteLength(arg) + "\r\n" + arg + "\r\n"; } - debug("send " + this.address + " id " + this.connection_id + ": " + command_str); + debug("Send " + this.address + " id " + this.connection_id + ": " + command_str); buffered_writes += !stream.write(command_str); } else { - debug("send command (" + command_str + ") has Buffer arguments"); + debug("Send command (" + command_str + ") has Buffer arguments"); buffered_writes += !stream.write(command_str); for (i = 0, il = args.length, arg; i < il; i += 1) { From 68936c5eb2394ba9fd2f9c003ff204a783e9482f Mon Sep 17 00:00:00 2001 From: Benjamin Coe Date: Sun, 13 Sep 2015 19:17:52 -0700 Subject: [PATCH 0323/1748] based on code-review added back check for error --- test/commands/multi.spec.js | 56 ++++++++++++++++++++----------------- 1 file changed, 30 insertions(+), 26 deletions(-) diff --git a/test/commands/multi.spec.js b/test/commands/multi.spec.js index f184b729fd0..45c3ab3f1af 100644 --- a/test/commands/multi.spec.js +++ b/test/commands/multi.spec.js @@ -60,11 +60,13 @@ describe("The 'multi' method", function () { it('roles back a transaction when one command in a sequence of commands fails', function (done) { var multi1, multi2; + var expected = helper.serverVersionAtLeast(client, [2, 6, 5]) ? helper.isError() : function () {}; // Provoke an error at queue time multi1 = client.MULTI(); multi1.mset("multifoo", "10", "multibar", "20", helper.isString("OK")); - multi1.set("foo2"); + + multi1.set("foo2", expected); multi1.incr("multifoo"); multi1.incr("multibar"); multi1.exec(function () { @@ -90,31 +92,33 @@ describe("The 'multi' method", function () { }); it('roles back a transaction when one command in an array of commands fails', function (done) { - // test nested multi-bulk replies - client.multi([ - ["mget", "multifoo", "multibar", function (err, res) { - assert.strictEqual(2, res.length); - assert.strictEqual(0, +res[0]); - assert.strictEqual(0, +res[1]); - }], - ["set", "foo2"], - ["incr", "multifoo"], - ["incr", "multibar"] - ]).exec(function (err, replies) { - if (helper.serverVersionAtLeast(client, [2, 6, 5])) { - assert.notEqual(err, null); - assert.equal(replies, undefined); - } else { - assert.strictEqual(2, replies[0].length); - assert.strictEqual(null, replies[0][0]); - assert.strictEqual(null, replies[0][1]); - - assert.strictEqual("1", replies[1].toString()); - assert.strictEqual("1", replies[2].toString()); - } - - return done(); - }); + var expected = helper.serverVersionAtLeast(client, [2, 6, 5]) ? helper.isError() : function () {}; + + // test nested multi-bulk replies + client.multi([ + ["mget", "multifoo", "multibar", function (err, res) { + assert.strictEqual(2, res.length); + assert.strictEqual(0, +res[0]); + assert.strictEqual(0, +res[1]); + }], + ["set", "foo2", expected], + ["incr", "multifoo"], + ["incr", "multibar"] + ]).exec(function (err, replies) { + if (helper.serverVersionAtLeast(client, [2, 6, 5])) { + assert.notEqual(err, null); + assert.equal(replies, undefined); + } else { + assert.strictEqual(2, replies[0].length); + assert.strictEqual(null, replies[0][0]); + assert.strictEqual(null, replies[0][1]); + + assert.strictEqual("1", replies[1].toString()); + assert.strictEqual("1", replies[2].toString()); + } + + return done(); + }); }); it('handles multiple operations being applied to a set', function (done) { From be708906fa06beb410c68b07bb2e8d0aa861e0f3 Mon Sep 17 00:00:00 2001 From: Benjamin Coe Date: Sun, 13 Sep 2015 22:21:03 -0700 Subject: [PATCH 0324/1748] add test for providing auth after client is created --- test/auth.spec.js | 11 +++++++++++ 1 file changed, 11 insertions(+) diff --git a/test/auth.spec.js b/test/auth.spec.js index b1a9cd86cc5..57c88f707d8 100644 --- a/test/auth.spec.js +++ b/test/auth.spec.js @@ -70,6 +70,17 @@ describe("client authentication", function () { }); }); + it('allows auth to be provided post-hoc with auth method', function (done) { + if (helper.redisProcess().spawnFailed()) this.skip(); + + var args = config.configureClient(parser, ip); + client = redis.createClient.apply(redis.createClient, args); + client.auth(auth); + client.on("ready", function () { + return done(); + }); + }); + it('reconnects with appropriate authentication', function (done) { if (helper.redisProcess().spawnFailed()) this.skip(); From bb221adec43ede66d38f4e37bb74ffb74591e398 Mon Sep 17 00:00:00 2001 From: Benjamin Coe Date: Mon, 14 Sep 2015 00:09:38 -0700 Subject: [PATCH 0325/1748] added test for multi.hmset's array handling --- test/commands/multi.spec.js | 13 +++++++++++++ 1 file changed, 13 insertions(+) diff --git a/test/commands/multi.spec.js b/test/commands/multi.spec.js index 45c3ab3f1af..f7204ce14df 100644 --- a/test/commands/multi.spec.js +++ b/test/commands/multi.spec.js @@ -210,6 +210,19 @@ describe("The 'multi' method", function () { }); }); + it('allows an array to be provided to hmset', function (done) { + client.multi() + .hmset("arrayhash", ['a', 'b', 'c']) + .hgetall("arrayhash") + .exec(function (err, replies) { + assert.strictEqual(null, err); + assert.equal("OK", replies[0]); + assert.equal(Object.keys(replies[1]).length, 3); + assert.equal("b", replies[1]['1']); + return done(); + }); + }); + }); }); }); From 656706a2630670060033c5fd43ae0a7dbd988a49 Mon Sep 17 00:00:00 2001 From: Ruben Bridgewater Date: Mon, 14 Sep 2015 15:45:28 +0200 Subject: [PATCH 0326/1748] Reduce the reauthenticate timeout to 333ms It should not take all that long to load everything into redis --- index.js | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/index.js b/index.js index f724d560605..3dd3b2e29f9 100644 --- a/index.js +++ b/index.js @@ -193,7 +193,7 @@ RedisClient.prototype.on_error = function (msg) { this.ready = false; this.emit("error", new Error(message)); - // "error" events get turned into exceptions if they aren't listened for. If the user handled this error + // "error" events get turned into exceptions if they aren't listened for. If the user handled this error // then we should try to reconnect. this.connection_gone("error"); }; @@ -211,11 +211,11 @@ RedisClient.prototype.do_auth = function () { if (err) { /* istanbul ignore if: this is almost impossible to test */ if (loading.test(err.message)) { - // if redis is still loading the db, it will not authenticate and everything else will fail + // If redis is still loading the db, it will not authenticate and everything else will fail debug("Redis still loading, trying to authenticate later"); setTimeout(function () { self.do_auth(); - }, 2000); // TODO - magic number alert + }, 333); return; } else if (noPasswordIsSet.test(err.message)) { debug("Warning: Redis server does not require a password, but a password was supplied."); @@ -238,7 +238,7 @@ RedisClient.prototype.do_auth = function () { self.auth_callback = null; } - // now we are really connected + // Now we are really connected self.emit("connect"); self.initialize_retry_vars(); From 1b261fcaad8028d4c3c0a3f8a62a68dd8a80e21a Mon Sep 17 00:00:00 2001 From: Ruben Bridgewater Date: Mon, 14 Sep 2015 21:23:47 +0200 Subject: [PATCH 0327/1748] Revert "added test for multi.hmset's array handling" This reverts commit bb221adec43ede66d38f4e37bb74ffb74591e398. The test is broken and only passes because there's a bug with multi hmset using arrays. See #686 and #838 for more details --- test/commands/multi.spec.js | 13 ------------- 1 file changed, 13 deletions(-) diff --git a/test/commands/multi.spec.js b/test/commands/multi.spec.js index f7204ce14df..45c3ab3f1af 100644 --- a/test/commands/multi.spec.js +++ b/test/commands/multi.spec.js @@ -210,19 +210,6 @@ describe("The 'multi' method", function () { }); }); - it('allows an array to be provided to hmset', function (done) { - client.multi() - .hmset("arrayhash", ['a', 'b', 'c']) - .hgetall("arrayhash") - .exec(function (err, replies) { - assert.strictEqual(null, err); - assert.equal("OK", replies[0]); - assert.equal(Object.keys(replies[1]).length, 3); - assert.equal("b", replies[1]['1']); - return done(); - }); - }); - }); }); }); From c6ae7832a3e1be541656209e33c2898e441112b7 Mon Sep 17 00:00:00 2001 From: Ruben Bridgewater Date: Thu, 3 Sep 2015 21:57:13 +0200 Subject: [PATCH 0328/1748] Remove try callbacks and emit an error in case of no callback has been provided --- index.js | 51 ++++++-------------------------------- test/commands/eval.spec.js | 8 ++++++ 2 files changed, 15 insertions(+), 44 deletions(-) diff --git a/index.js b/index.js index f724d560605..6e8be469f40 100644 --- a/index.js +++ b/index.js @@ -152,13 +152,7 @@ RedisClient.prototype.flush_and_error = function (message) { while (this.offline_queue.length > 0) { command_obj = this.offline_queue.shift(); if (typeof command_obj.callback === "function") { - try { - command_obj.callback(error); - } catch (callback_err) { - process.nextTick(function () { - throw callback_err; - }); - } + command_obj.callback(error); } } this.offline_queue = new Queue(); @@ -166,13 +160,7 @@ RedisClient.prototype.flush_and_error = function (message) { while (this.command_queue.length > 0) { command_obj = this.command_queue.shift(); if (typeof command_obj.callback === "function") { - try { - command_obj.callback(error); - } catch (callback_err) { - process.nextTick(function () { - throw callback_err; - }); - } + command_obj.callback(error); } } this.command_queue = new Queue(); @@ -529,38 +517,13 @@ RedisClient.prototype.return_error = function (err) { this.emit("drain"); this.should_buffer = false; } - - try { + if (command_obj.callback) { command_obj.callback(err); - } catch (callback_err) { - // if a callback throws an exception, re-throw it on a new stack so the parser can keep going - process.nextTick(function () { - throw callback_err; - }); + } else { + this.emit('error', err); } }; -// if a callback throws an exception, re-throw it on a new stack so the parser can keep going. -// if a domain is active, emit the error on the domain, which will serve the same function. -// put this try/catch in its own function because V8 doesn't optimize this well yet. -function try_callback(callback, reply) { - try { - callback(null, reply); - } catch (err) { - if (process.domain) { - var currDomain = process.domain; - currDomain.emit('error', err); - if (process.domain === currDomain) { - currDomain.exit(); - } - } else { - process.nextTick(function () { - throw err; - }); - } - } -} - // hgetall converts its replies to an Object. If the reply is empty, null is returned. function reply_to_object(reply) { var obj = {}, j, jl, key, val; @@ -638,7 +601,7 @@ RedisClient.prototype.return_reply = function (reply) { reply = reply_to_object(reply); } - try_callback(command_obj.callback, reply); + command_obj.callback(null, reply); } else { debug("no callback for reply: " + (reply && reply.toString && reply.toString())); } @@ -662,7 +625,7 @@ RedisClient.prototype.return_reply = function (reply) { // reply[1] can be null var reply1String = (reply[1] === null) ? null : reply[1].toString(); if (command_obj && typeof command_obj.callback === "function") { - try_callback(command_obj.callback, reply1String); + command_obj.callback(null, reply1String); } this.emit(type, reply1String, reply[2]); // channel, count } else { diff --git a/test/commands/eval.spec.js b/test/commands/eval.spec.js index abe307275be..b4a1cd0a491 100644 --- a/test/commands/eval.spec.js +++ b/test/commands/eval.spec.js @@ -118,6 +118,14 @@ describe("The 'eval' method", function () { helper.serverVersionAtLeast.call(this, client, [2, 5, 0]); client.evalsha('ffffffffffffffffffffffffffffffffffffffff', 0, helper.isError(done)); }); + + it('emits an error if SHA does not exist and no callback has been provided', function (done) { + client.on('error', function (err) { + assert.equal(err.message, 'NOSCRIPT No matching script. Please use EVAL.'); + done(); + }); + client.evalsha('ffffffffffffffffffffffffffffffffffffffff', 0); + }); }); it('allows a key to be incremented, and performs appropriate conversion from LUA type', function (done) { From 4f0443cdd4fb3f203d1c654a7eb893f663522a62 Mon Sep 17 00:00:00 2001 From: Ruben Bridgewater Date: Mon, 7 Sep 2015 14:54:08 +0200 Subject: [PATCH 0329/1748] Emit errors instead of throwing them Thrown errors might kill the users app. By emitting the errors the user is able to catch all errors in one place without the app going down --- index.js | 31 ++++++++++++++++++++++--------- test/commands/eval.spec.js | 2 +- test/commands/mset.spec.js | 11 ++++------- test/commands/select.spec.js | 12 +++++------- test/commands/set.spec.js | 20 ++++++-------------- test/node_redis.spec.js | 12 +++++------- 6 files changed, 43 insertions(+), 45 deletions(-) diff --git a/index.js b/index.js index 6e8be469f40..d56b66e7d25 100644 --- a/index.js +++ b/index.js @@ -280,6 +280,8 @@ RedisClient.prototype.init_parser = function () { return true; } })) { + // Do not emit this error + // This should take down the app if anyone made such a huge mistake or should somehow be handled in user code throw new Error("Couldn't find named parser " + self.options.parser + " on this system"); } } else { @@ -629,10 +631,12 @@ RedisClient.prototype.return_reply = function (reply) { } this.emit(type, reply1String, reply[2]); // channel, count } else { - throw new Error("subscriptions are active but got unknown reply type " + type); + this.emit("error", new Error("subscriptions are active but got unknown reply type " + type)); + return; } - } else if (! this.closing) { - throw new Error("subscriptions are active but got an invalid reply: " + reply); + } else if (!this.closing) { + this.emit("error", new Error("subscriptions are active but got an invalid reply: " + reply)); + return; } } else if (this.monitoring) { len = reply.indexOf(" "); @@ -643,7 +647,7 @@ RedisClient.prototype.return_reply = function (reply) { }); this.emit("monitor", timestamp, args); } else { - throw new Error("node_redis command queue state error. If you can reproduce this, please report it."); + this.emit("error", new Error("node_redis command queue state error. If you can reproduce this, please report it.")); } }; @@ -702,9 +706,16 @@ RedisClient.prototype.send_command = function (command, args, callback) { // if the value is undefined or null and command is set or setx, need not to send message to redis if (command === 'set' || command === 'setex') { - if(args[args.length - 1] === undefined || args[args.length - 1] === null) { + if (args.length === 0) { + return; + } + if (args[args.length - 1] === undefined || args[args.length - 1] === null) { var err = new Error('send_command: ' + command + ' value must not be undefined or null'); - return callback && callback(err); + if (callback) { + return callback && callback(err); + } + this.emit("error", err); + return; } } @@ -731,7 +742,8 @@ RedisClient.prototype.send_command = function (command, args, callback) { if (command_obj.callback) { command_obj.callback(not_writeable_error); } else { - throw not_writeable_error; + this.emit("error", not_writeable_error); + return; } } @@ -745,7 +757,8 @@ RedisClient.prototype.send_command = function (command, args, callback) { } else if (command === "quit") { this.closing = true; } else if (this.pub_sub_mode === true) { - throw new Error("Connection in subscriber mode, only subscriber commands may be used"); + this.emit("error", new Error("Connection in subscriber mode, only subscriber commands may be used")); + return; } this.command_queue.push(command_obj); this.commands_sent += 1; @@ -1032,7 +1045,7 @@ Multi.prototype.exec = function (callback) { callback(errors); return; } else { - throw new Error(err); + self._client.emit('error', err); } } diff --git a/test/commands/eval.spec.js b/test/commands/eval.spec.js index b4a1cd0a491..df075f3d292 100644 --- a/test/commands/eval.spec.js +++ b/test/commands/eval.spec.js @@ -114,7 +114,7 @@ describe("The 'eval' method", function () { client.evalsha(sha, 0, helper.isString('eval get sha test', done)); }); - it('throws an error if SHA does not exist', function (done) { + it('returns an error if SHA does not exist', function (done) { helper.serverVersionAtLeast.call(this, client, [2, 5, 0]); client.evalsha('ffffffffffffffffffffffffffffffffffffffff', 0, helper.isError(done)); }); diff --git a/test/commands/mset.spec.js b/test/commands/mset.spec.js index 1718462b679..a79d6365b1d 100644 --- a/test/commands/mset.spec.js +++ b/test/commands/mset.spec.js @@ -92,13 +92,10 @@ describe("The 'mset' method", function () { describe("with undefined 'key' and missing 'value' parameter", function () { // this behavior is different from the 'set' behavior. - it("throws an error", function (done) { - var mochaListener = helper.removeMochaListener(); - - process.once('uncaughtException', function (err) { - process.on('uncaughtException', mochaListener); - helper.isError()(err, null); - return done(); + it("emits an error", function (done) { + client.on('error', function (err) { + assert.equal(err.message, "ERR wrong number of arguments for 'mset' command"); + done(); }); client.mset(); diff --git a/test/commands/select.spec.js b/test/commands/select.spec.js index 924fc9f9c5a..cb3e101c2f4 100644 --- a/test/commands/select.spec.js +++ b/test/commands/select.spec.js @@ -24,7 +24,7 @@ describe("The 'select' method", function () { }); }); - it("throws an error if redis is not connected", function (done) { + it("returns an error if redis is not connected", function (done) { client.select(1, function (err, res) { assert(err.message.match(/Redis connection gone/)); done(); @@ -67,7 +67,7 @@ describe("The 'select' method", function () { }); describe("with an invalid db index", function () { - it("emits an error", function (done) { + it("returns an error", function (done) { assert.strictEqual(client.selected_db, null, "default db should be null"); client.select(9999, function (err) { assert.equal(err.message, 'ERR invalid DB index'); @@ -90,14 +90,12 @@ describe("The 'select' method", function () { }); describe("with an invalid db index", function () { - it("throws an error when callback not provided", function (done) { - var mochaListener = helper.removeMochaListener(); + it("emits an error when callback not provided", function (done) { assert.strictEqual(client.selected_db, null, "default db should be null"); - process.once('uncaughtException', function (err) { - process.on('uncaughtException', mochaListener); + client.on('error', function (err) { assert.equal(err.message, 'ERR invalid DB index'); - return done(); + done(); }); client.select(9999); diff --git a/test/commands/set.spec.js b/test/commands/set.spec.js index 927c64c6dd6..15e0346ed7e 100644 --- a/test/commands/set.spec.js +++ b/test/commands/set.spec.js @@ -96,8 +96,7 @@ describe("The 'set' method", function () { this.timeout(200); client.once("error", function (err) { - helper.isError()(err, null); - return done(err); + done(err); }); client.set(); @@ -107,20 +106,13 @@ describe("The 'set' method", function () { }, 100); }); - it("does not throw an error", function (done) { - this.timeout(200); - var mochaListener = helper.removeMochaListener(); - - process.once('uncaughtException', function (err) { - process.on('uncaughtException', mochaListener); - return done(err); + it("does emit an error", function (done) { + client.on('error', function (err) { + assert.equal(err.message, "ERR wrong number of arguments for 'set' command"); + done(); }); - client.set(); - - setTimeout(function () { - done(); - }, 100); + client.set('foo'); }); }); }); diff --git a/test/node_redis.spec.js b/test/node_redis.spec.js index b43483bec37..e9eb0c33594 100644 --- a/test/node_redis.spec.js +++ b/test/node_redis.spec.js @@ -647,7 +647,7 @@ describe("The node_redis client", function () { describe('enable_offline_queue', function () { describe('true', function () { - it("does not throw an error and enqueues operation", function (done) { + it("does not return an error and enqueues operation", function (done) { var client = redis.createClient(9999, null, { max_attempts: 1, parser: parser @@ -674,20 +674,18 @@ describe("The node_redis client", function () { }); describe('false', function () { - it("does not throw an error and enqueues operation", function (done) { + it("does not emit an error and enqueues operation", function (done) { var client = redis.createClient(9999, null, { parser: parser, max_attempts: 1, enable_offline_queue: false }); - client.on('error', function() { - // ignore, b/c expecting a "can't connect" error + client.on('error', function(err) { + assert(/send_command: stream not writeable|ECONNREFUSED/.test(err.message)); }); - assert.throws(function () { - client.set('foo', 'bar'); - }); + client.set('foo', 'bar'); assert.doesNotThrow(function () { client.set('foo', 'bar', function (err) { From 4bdcf8a598d81cb7773ba86f6cc26edf851e6020 Mon Sep 17 00:00:00 2001 From: Ruben Bridgewater Date: Mon, 14 Sep 2015 23:53:52 +0200 Subject: [PATCH 0330/1748] Fix regression: version detection in tests did not work properly anymore --- test/helper.js | 3 +++ 1 file changed, 3 insertions(+) diff --git a/test/helper.js b/test/helper.js index b31e90ee8d2..5f4d7b46241 100644 --- a/test/helper.js +++ b/test/helper.js @@ -98,6 +98,9 @@ module.exports = { // Return true if the server version >= desired_version var version = connection.server_info.versions; for (var i = 0; i < 3; i++) { + if (version[i] > desired_version[i]) { + return true; + } if (version[i] < desired_version[i]) { if (this.skip) this.skip(); return false; From 6ea70271d6b24bd395e0352967513c6a350bf903 Mon Sep 17 00:00:00 2001 From: Ruben Bridgewater Date: Mon, 14 Sep 2015 15:24:24 +0200 Subject: [PATCH 0331/1748] v8 is going to inline a couple of things that were necessary a few years ago --- .gitignore | 1 + index.js | 45 ++++++++++++++++++--------------------------- 2 files changed, 19 insertions(+), 27 deletions(-) diff --git a/.gitignore b/.gitignore index d267c944c04..2cf7258aa69 100644 --- a/.gitignore +++ b/.gitignore @@ -3,3 +3,4 @@ node_modules .nyc_output coverage npm-debug.log +*.rdb diff --git a/index.js b/index.js index a8b467d6931..1057f5ff17c 100644 --- a/index.js +++ b/index.js @@ -359,6 +359,7 @@ RedisClient.prototype.on_info_cmd = function (err, res) { }); obj.versions = []; + /* istanbul ignore else: some redis servers do not send the version */ if (obj.redis_version) { obj.redis_version.split('.').forEach(function (num) { obj.versions.push(+num); @@ -572,8 +573,7 @@ RedisClient.prototype.return_reply = function (reply) { if (this.pub_sub_mode && (type === 'message' || type === 'pmessage')) { debug("Received pubsub message"); - } - else { + } else { command_obj = this.command_queue.shift(); } @@ -660,7 +660,7 @@ function Command(command, args, sub_command, buffer_args, callback) { } RedisClient.prototype.send_command = function (command, args, callback) { - var arg, command_obj, i, il, elem_count, buffer_args, stream = this.stream, command_str = "", buffered_writes = 0, last_arg_type; + var arg, command_obj, i, elem_count, buffer_args, stream = this.stream, command_str = "", buffered_writes = 0, last_arg_type; if (typeof command !== "string") { throw new Error("First argument to send_command must be the command name string, not " + typeof command); @@ -718,9 +718,10 @@ RedisClient.prototype.send_command = function (command, args, callback) { } buffer_args = false; - for (i = 0, il = args.length, arg; i < il; i += 1) { + for (i = 0; i < args.length; i += 1) { if (Buffer.isBuffer(args[i])) { buffer_args = true; + break; } } @@ -768,7 +769,7 @@ RedisClient.prototype.send_command = function (command, args, callback) { command_str = "*" + elem_count + "\r\n$" + command.length + "\r\n" + command + "\r\n"; if (!buffer_args) { // Build up a string and send entire command in one write - for (i = 0, il = args.length, arg; i < il; i += 1) { + for (i = 0; i < args.length; i += 1) { arg = args[i]; if (typeof arg !== "string") { arg = String(arg); @@ -781,7 +782,7 @@ RedisClient.prototype.send_command = function (command, args, callback) { debug("Send command (" + command_str + ") has Buffer arguments"); buffered_writes += !stream.write(command_str); - for (i = 0, il = args.length, arg; i < il; i += 1) { + for (i = 0; i < args.length; i += 1) { arg = args[i]; if (!(Buffer.isBuffer(arg) || arg instanceof String)) { arg = String(arg); @@ -892,7 +893,7 @@ commands.forEach(function (fullCommand) { }); // store db in this.select_db to restore it on reconnect -RedisClient.prototype.select = function (db, callback) { +RedisClient.prototype.select = RedisClient.prototype.SELECT = function (db, callback) { var self = this; this.send_command('select', [db], function (err, res) { @@ -906,22 +907,18 @@ RedisClient.prototype.select = function (db, callback) { } }); }; -RedisClient.prototype.SELECT = RedisClient.prototype.select; -// Stash auth for connect and reconnect. Send immediately if already connected. -RedisClient.prototype.auth = function () { - var args = to_array(arguments); - this.auth_pass = args[0]; - this.auth_callback = args[1]; +// Stash auth for connect and reconnect. Send immediately if already connected. +RedisClient.prototype.auth = RedisClient.prototype.AUTH = function (pass, callback) { + this.auth_pass = pass; + this.auth_callback = callback; debug("Saving auth as " + this.auth_pass); - if (this.connected) { - this.send_command("auth", args); + this.send_command("auth", pass, callback); } }; -RedisClient.prototype.AUTH = RedisClient.prototype.auth; -RedisClient.prototype.hmget = function (arg1, arg2, arg3) { +RedisClient.prototype.hmget = RedisClient.prototype.HMGET = function (arg1, arg2, arg3) { if (Array.isArray(arg2) && typeof arg3 === "function") { return this.send_command("hmget", [arg1].concat(arg2), arg3); } else if (Array.isArray(arg1) && typeof arg2 === "function") { @@ -930,9 +927,8 @@ RedisClient.prototype.hmget = function (arg1, arg2, arg3) { return this.send_command("hmget", to_array(arguments)); } }; -RedisClient.prototype.HMGET = RedisClient.prototype.hmget; -RedisClient.prototype.hmset = function (args, callback) { +RedisClient.prototype.hmset = RedisClient.prototype.HMSET = function (args, callback) { var tmp_args, tmp_keys, i, il, key; if (Array.isArray(args)) { @@ -968,9 +964,8 @@ RedisClient.prototype.hmset = function (args, callback) { return this.send_command("hmset", args, callback); }; -RedisClient.prototype.HMSET = RedisClient.prototype.hmset; -Multi.prototype.hmset = function () { +Multi.prototype.hmset = Multi.prototype.HMSET = function () { var args = to_array(arguments), tmp_args; if (args.length >= 2 && typeof args[0] === "string" && typeof args[1] === "object") { tmp_args = [ "hmset", args[0] ]; @@ -989,9 +984,8 @@ Multi.prototype.hmset = function () { this.queue.push(args); return this; }; -Multi.prototype.HMSET = Multi.prototype.hmset; -Multi.prototype.exec = function (callback) { +Multi.prototype.exec = Multi.prototype.EXEC = function (callback) { var self = this; var errors = []; var wants_buffers = []; @@ -1075,13 +1069,10 @@ Multi.prototype.exec = function (callback) { } }); }; -Multi.prototype.EXEC = Multi.prototype.exec; -RedisClient.prototype.multi = function (args) { +RedisClient.prototype.multi = RedisClient.prototype.MULTI = function (args) { return new Multi(this, args); }; -RedisClient.prototype.MULTI = RedisClient.prototype.multi; - // stash original eval method var eval_orig = RedisClient.prototype.eval; From 1a06cfb6eccff2e50fc05281563e7e44d7ce3200 Mon Sep 17 00:00:00 2001 From: Ruben Bridgewater Date: Mon, 14 Sep 2015 00:15:33 +0200 Subject: [PATCH 0332/1748] Add .command_used to errors thrown by the parser --- index.js | 1 + test/auth.spec.js | 5 +++-- test/commands/select.spec.js | 1 + 3 files changed, 5 insertions(+), 2 deletions(-) diff --git a/index.js b/index.js index 1057f5ff17c..bae89112e42 100644 --- a/index.js +++ b/index.js @@ -509,6 +509,7 @@ RedisClient.prototype.on_data = function (data) { RedisClient.prototype.return_error = function (err) { var command_obj = this.command_queue.shift(), queue_len = this.command_queue.length; + err.command_used = command_obj.command.toUpperCase(); if (this.pub_sub_mode === false && queue_len === 0) { this.command_queue = new Queue(); diff --git a/test/auth.spec.js b/test/auth.spec.js index 57c88f707d8..88b68c97ebc 100644 --- a/test/auth.spec.js +++ b/test/auth.spec.js @@ -39,8 +39,9 @@ describe("client authentication", function () { client = redis.createClient.apply(redis.createClient, args); - client.once('error', function (error) { - assert.ok(/ERR invalid password/.test(error)); + client.once('error', function (err) { + assert.strictEqual(err.command_used, 'AUTH'); + assert.ok(/ERR invalid password/.test(err.message)); return done(); }); diff --git a/test/commands/select.spec.js b/test/commands/select.spec.js index cb3e101c2f4..32e5b0a27e1 100644 --- a/test/commands/select.spec.js +++ b/test/commands/select.spec.js @@ -94,6 +94,7 @@ describe("The 'select' method", function () { assert.strictEqual(client.selected_db, null, "default db should be null"); client.on('error', function (err) { + assert.strictEqual(err.command_used, 'SELECT'); assert.equal(err.message, 'ERR invalid DB index'); done(); }); From ebbb0146b98b112e2be57f62220111f890abfeb2 Mon Sep 17 00:00:00 2001 From: Ruben Bridgewater Date: Mon, 14 Sep 2015 22:13:43 +0200 Subject: [PATCH 0333/1748] Fix auth emitting the error even though a callback is present Fix auth manipulating the returned error And this is also removing some dead code --- index.js | 12 ++++++------ test/auth.spec.js | 27 ++++++++++++++++++++++++++- 2 files changed, 32 insertions(+), 7 deletions(-) diff --git a/index.js b/index.js index bae89112e42..efca5866448 100644 --- a/index.js +++ b/index.js @@ -206,20 +206,20 @@ RedisClient.prototype.do_auth = function () { debug("Warning: Redis server does not require a password, but a password was supplied."); err = null; res = "OK"; + } else if (self.auth_callback) { + self.auth_callback(err); + self.auth_callback = null; } else { - return self.emit("error", new Error("Auth error: " + err.message)); + self.emit("error", err); + return; } } res = res.toString(); - if (res !== "OK") { - return self.emit("error", new Error("Auth failed: " + res)); - } - debug("Auth succeeded " + self.address + " id " + self.connection_id); if (self.auth_callback) { - self.auth_callback(err, res); + self.auth_callback(null, res); self.auth_callback = null; } diff --git a/test/auth.spec.js b/test/auth.spec.js index 88b68c97ebc..3a1c8812ca5 100644 --- a/test/auth.spec.js +++ b/test/auth.spec.js @@ -34,7 +34,7 @@ describe("client authentication", function () { }); }); - it("raises error when auth is bad", function (done) { + it("emits error when auth is bad without callback", function (done) { if (helper.redisProcess().spawnFailed()) this.skip(); client = redis.createClient.apply(redis.createClient, args); @@ -48,6 +48,18 @@ describe("client authentication", function () { client.auth(auth + 'bad'); }); + it("returns an error when auth is bad with a callback", function (done) { + if (helper.redisProcess().spawnFailed()) this.skip(); + + client = redis.createClient.apply(redis.createClient, args); + + client.auth(auth + 'bad', function (err, res) { + assert.strictEqual(err.command_used, 'AUTH'); + assert.ok(/ERR invalid password/.test(err.message)); + done(); + }); + }); + if (ip === 'IPv4') { it('allows auth to be provided as part of redis url', function (done) { if (helper.redisProcess().spawnFailed()) this.skip(); @@ -71,6 +83,19 @@ describe("client authentication", function () { }); }); + it('allows auth and no_ready_check to be provided as config option for client', function (done) { + if (helper.redisProcess().spawnFailed()) this.skip(); + + var args = config.configureClient(parser, ip, { + auth_pass: auth, + no_ready_check: true + }); + client = redis.createClient.apply(redis.createClient, args); + client.on("ready", function () { + done(); + }); + }); + it('allows auth to be provided post-hoc with auth method', function (done) { if (helper.redisProcess().spawnFailed()) this.skip(); From c522ca126443ac715d162694870c723869aad10f Mon Sep 17 00:00:00 2001 From: Ruben Bridgewater Date: Mon, 14 Sep 2015 15:07:32 +0200 Subject: [PATCH 0334/1748] Fix inconsistent command argument handling Earlier multi.command and client.command diverged a lot in the way they accepted arguments. This is now consistent This will also fix some bugs like using multi.hmset with arrays --- .jshintrc | 3 + index.js | 127 +++++++++++++++++++--------------- test/commands/blpop.spec.js | 10 +++ test/commands/client.spec.js | 11 ++- test/commands/dbsize.spec.js | 2 +- test/commands/del.spec.js | 14 ++++ test/commands/exits.spec.js | 5 ++ test/commands/expire.spec.js | 8 +++ test/commands/flushdb.spec.js | 48 ++++++++----- test/commands/get.spec.js | 15 ++++ test/commands/getset.spec.js | 20 ++++++ test/commands/hgetall.spec.js | 4 +- test/commands/multi.spec.js | 17 +++++ 13 files changed, 208 insertions(+), 76 deletions(-) diff --git a/.jshintrc b/.jshintrc index 814239e345c..c5caf777dd1 100644 --- a/.jshintrc +++ b/.jshintrc @@ -16,6 +16,9 @@ "node": true, // Enable globals available when code is running inside of the NodeJS runtime environment. "mocha": true, + // Relaxing options + "boss": true, // Accept things like `while (command = keys.shift()) { ... }` + "overrides": { "examples/*.js": { "unused": false diff --git a/index.js b/index.js index efca5866448..60ec1f4e047 100644 --- a/index.js +++ b/index.js @@ -863,8 +863,16 @@ RedisClient.prototype.end = function () { function Multi(client, args) { this._client = client; this.queue = [["multi"]]; + var command, tmp_args; if (Array.isArray(args)) { - this.queue = this.queue.concat(args); + while (tmp_args = args.shift()) { + command = tmp_args.shift(); + if (Array.isArray(command)) { + this[command[0]].apply(this, command.slice(1).concat(tmp_args)); + } else { + this[command].apply(this, tmp_args); + } + } } } @@ -878,16 +886,32 @@ commands.forEach(function (fullCommand) { return; } - RedisClient.prototype[command] = function (args, callback) { - if (Array.isArray(args)) { - return this.send_command(command, args, callback); + RedisClient.prototype[command] = function (key, arg, callback) { + if (Array.isArray(key)) { + return this.send_command(command, key, arg); + } + if (Array.isArray(arg)) { + arg.unshift(key); + return this.send_command(command, arg, callback); } return this.send_command(command, to_array(arguments)); }; RedisClient.prototype[command.toUpperCase()] = RedisClient.prototype[command]; - Multi.prototype[command] = function () { - this.queue.push([command].concat(to_array(arguments))); + Multi.prototype[command] = function (key, arg, callback) { + if (Array.isArray(key)) { + if (arg) { + key.push(arg); + } + this.queue.push([command].concat(key)); + } else if (Array.isArray(arg)) { + if (callback) { + arg.push(callback); + } + this.queue.push([command, key].concat(arg)); + } else { + this.queue.push([command].concat(to_array(arguments))); + } return this; }; Multi.prototype[command.toUpperCase()] = Multi.prototype[command]; @@ -919,70 +943,63 @@ RedisClient.prototype.auth = RedisClient.prototype.AUTH = function (pass, callba } }; -RedisClient.prototype.hmget = RedisClient.prototype.HMGET = function (arg1, arg2, arg3) { - if (Array.isArray(arg2) && typeof arg3 === "function") { - return this.send_command("hmget", [arg1].concat(arg2), arg3); - } else if (Array.isArray(arg1) && typeof arg2 === "function") { - return this.send_command("hmget", arg1, arg2); - } else { - return this.send_command("hmget", to_array(arguments)); +RedisClient.prototype.hmset = RedisClient.prototype.HMSET = function (key, args, callback) { + var field, tmp_args; + if (Array.isArray(key)) { + return this.send_command("hmset", key, args); } -}; - -RedisClient.prototype.hmset = RedisClient.prototype.HMSET = function (args, callback) { - var tmp_args, tmp_keys, i, il, key; - if (Array.isArray(args)) { - return this.send_command("hmset", args, callback); - } - - args = to_array(arguments); - if (typeof args[args.length - 1] === "function") { - callback = args[args.length - 1]; - args.length -= 1; - } else { - callback = null; + return this.send_command("hmset", [key].concat(args), callback); } - - if (args.length === 2 && (typeof args[0] === "string" || typeof args[0] === "number") && typeof args[1] === "object") { + if (typeof args === "object") { // User does: client.hmset(key, {key1: val1, key2: val2}) // assuming key is a string, i.e. email address // if key is a number, i.e. timestamp, convert to string - if (typeof args[0] === "number") { - args[0] = args[0].toString(); + // TODO: This seems random and no other command get's the key converted => either all or none should behave like this + if (typeof key !== "string") { + key = key.toString(); } - - tmp_args = [ args[0] ]; - tmp_keys = Object.keys(args[1]); - for (i = 0, il = tmp_keys.length; i < il ; i++) { - key = tmp_keys[i]; - tmp_args.push(key); - tmp_args.push(args[1][key]); + tmp_args = [key]; + var fields = Object.keys(args); + while (field = fields.shift()) { + tmp_args.push(field, args[field]); } - args = tmp_args; + return this.send_command("hmset", tmp_args, callback); } - - return this.send_command("hmset", args, callback); + return this.send_command("hmset", to_array(arguments)); }; -Multi.prototype.hmset = Multi.prototype.HMSET = function () { - var args = to_array(arguments), tmp_args; - if (args.length >= 2 && typeof args[0] === "string" && typeof args[1] === "object") { - tmp_args = [ "hmset", args[0] ]; - Object.keys(args[1]).map(function (key) { - tmp_args.push(key); - tmp_args.push(args[1][key]); - }); - if (args[2]) { - tmp_args.push(args[2]); +Multi.prototype.hmset = Multi.prototype.HMSET = function (key, args, callback) { + var tmp_args, field; + if (Array.isArray(key)) { + if (args) { + key.push(args); + } + tmp_args = ['hmset'].concat(key); + } else if (Array.isArray(args)) { + if (callback) { + args.push(callback); + } + tmp_args = ['hmset', key].concat(args); + } else if (typeof args === "object") { + tmp_args = ["hmset", key]; + if (typeof key !== "string") { + key = key.toString(); + } + var fields = Object.keys(args); + while (field = fields.shift()) { + tmp_args.push(field); + tmp_args.push(args[field]); + } + if (callback) { + tmp_args.push(callback); } - args = tmp_args; } else { - args.unshift("hmset"); + tmp_args = to_array(arguments); + tmp_args.unshift("hmset"); } - - this.queue.push(args); + this.queue.push(tmp_args); return this; }; diff --git a/test/commands/blpop.spec.js b/test/commands/blpop.spec.js index 66a85dd3b49..7f1aeb19471 100644 --- a/test/commands/blpop.spec.js +++ b/test/commands/blpop.spec.js @@ -33,6 +33,16 @@ describe("The 'blpop' method", function () { }); }); + it('pops value immediately if list contains values using array notation', function (done) { + bclient = redis.createClient.apply(redis.createClient, args); + client.rpush(["blocking list", "initial value"], helper.isNumber(1)); + bclient.blpop(["blocking list", 0], function (err, value) { + assert.strictEqual(value[0], "blocking list"); + assert.strictEqual(value[1], "initial value"); + return done(err); + }); + }); + it('waits for value if list is not yet populated', function (done) { bclient = redis.createClient.apply(redis.createClient, args); bclient.blpop("blocking list 2", 5, function (err, value) { diff --git a/test/commands/client.spec.js b/test/commands/client.spec.js index 1d47db708d8..5636df976c3 100644 --- a/test/commands/client.spec.js +++ b/test/commands/client.spec.js @@ -37,8 +37,17 @@ describe("The 'client' method", function () { }); }); + it("lists connected clients when invoked with array syntax on client", function (done) { + client.multi().client(["list"]).exec(function(err, results) { + assert(pattern.test(results[0]), "expected string '" + results + "' to match " + pattern.toString()); + return done(); + }); + }); + it("lists connected clients when invoked with multi's array syntax", function (done) { - client.multi().client("list").exec(function(err, results) { + client.multi([ + ['client', 'list'] + ]).exec(function(err, results) { assert(pattern.test(results[0]), "expected string '" + results + "' to match " + pattern.toString()); return done(); }); diff --git a/test/commands/dbsize.spec.js b/test/commands/dbsize.spec.js index 845a4adaa1e..d3e8c07dafd 100644 --- a/test/commands/dbsize.spec.js +++ b/test/commands/dbsize.spec.js @@ -71,7 +71,7 @@ describe("The 'dbsize' method", function () { var oldSize; beforeEach(function (done) { - client.dbsize([], function (err, res) { + client.dbsize(function (err, res) { helper.isType.number()(err, res); assert.strictEqual(res, 0, "Initial db size should be 0"); diff --git a/test/commands/del.spec.js b/test/commands/del.spec.js index 152e9c1e455..69f3930c29c 100644 --- a/test/commands/del.spec.js +++ b/test/commands/del.spec.js @@ -36,6 +36,20 @@ describe("The 'del' method", function () { client.get('apple', helper.isNull(done)); }); + it('allows multiple keys to be deleted with the array syntax', function (done) { + client.mset('foo', 'bar', 'apple', 'banana'); + client.del(['foo', 'apple'], helper.isNumber(2)); + client.get('foo', helper.isNull()); + client.get('apple', helper.isNull(done)); + }); + + it('allows multiple keys to be deleted with the array syntax and no callback', function (done) { + client.mset('foo', 'bar', 'apple', 'banana'); + client.del(['foo', 'apple']); + client.get('foo', helper.isNull()); + client.get('apple', helper.isNull(done)); + }); + afterEach(function () { client.end(); }); diff --git a/test/commands/exits.spec.js b/test/commands/exits.spec.js index 3e7074268fb..1d6d778b397 100644 --- a/test/commands/exits.spec.js +++ b/test/commands/exits.spec.js @@ -24,6 +24,11 @@ describe("The 'exits' method", function () { client.EXISTS('foo', helper.isNumber(1, done)); }); + it('returns 1 if the key exists with array syntax', function (done) { + client.set('foo', 'bar'); + client.EXISTS(['foo'], helper.isNumber(1, done)); + }); + it('returns 0 if the key does not exist', function (done) { client.exists('bar', helper.isNumber(0, done)); }); diff --git a/test/commands/expire.spec.js b/test/commands/expire.spec.js index 3e700206e60..b0480b6c1d8 100644 --- a/test/commands/expire.spec.js +++ b/test/commands/expire.spec.js @@ -20,6 +20,14 @@ describe("The 'expire' method", function () { }); it('expires key after timeout', function (done) { + client.set(['expiry key', 'bar'], helper.isString("OK")); + client.EXPIRE("expiry key", "1", helper.isNumber(1)); + setTimeout(function () { + client.exists(["expiry key"], helper.isNumber(0, done)); + }, 1100); + }); + + it('expires key after timeout with array syntax', function (done) { client.set(['expiry key', 'bar'], helper.isString("OK")); client.EXPIRE(["expiry key", "1"], helper.isNumber(1)); setTimeout(function () { diff --git a/test/commands/flushdb.spec.js b/test/commands/flushdb.spec.js index 3ba52140402..c6f3c83e19e 100644 --- a/test/commands/flushdb.spec.js +++ b/test/commands/flushdb.spec.js @@ -58,36 +58,50 @@ describe("The 'flushdb' method", function () { describe("when there is data in Redis", function () { beforeEach(function (done) { - var end = helper.callFuncAfter(function () { - client.flushdb(helper.isString("OK", done)); - }, 2); - client.mset(key, uuid.v4(), key2, uuid.v4(), helper.isNotError(end)); + client.mset(key, uuid.v4(), key2, uuid.v4(), helper.isNotError()); client.dbsize([], function (err, res) { helper.isType.positiveNumber()(err, res); assert.equal(res, 2, 'Two keys should have been inserted'); - end(); + done(); }); }); it("deletes all the keys", function (done) { - client.mget(key, key2, function (err, res) { - assert.strictEqual(null, err, "Unexpected error returned"); - assert.strictEqual(true, Array.isArray(res), "Results object should be an array."); - assert.strictEqual(2, res.length, "Results array should have length 2."); - assert.strictEqual(null, res[0], "Redis key should have been flushed."); - assert.strictEqual(null, res[1], "Redis key should have been flushed."); - done(err); + client.flushdb(function(err, res) { + assert.equal(res, 'OK'); + client.mget(key, key2, function (err, res) { + assert.strictEqual(null, err, "Unexpected error returned"); + assert.strictEqual(true, Array.isArray(res), "Results object should be an array."); + assert.strictEqual(2, res.length, "Results array should have length 2."); + assert.strictEqual(null, res[0], "Redis key should have been flushed."); + assert.strictEqual(null, res[1], "Redis key should have been flushed."); + done(err); + }); }); }); it("results in a db size of zero", function (done) { - client.dbsize([], function (err, res) { - helper.isNotError()(err, res); - helper.isType.number()(err, res); - assert.strictEqual(0, res, "Flushing db should result in db size 0"); - done(); + client.flushdb(function(err, res) { + client.dbsize([], function (err, res) { + helper.isNotError()(err, res); + helper.isType.number()(err, res); + assert.strictEqual(0, res, "Flushing db should result in db size 0"); + done(); + }); }); }); + + it("results in a db size of zero without a callback", function (done) { + client.flushdb(); + setTimeout(function(err, res) { + client.dbsize([], function (err, res) { + helper.isNotError()(err, res); + helper.isType.number()(err, res); + assert.strictEqual(0, res, "Flushing db should result in db size 0"); + done(); + }); + }, 25); + }); }); }); }); diff --git a/test/commands/get.spec.js b/test/commands/get.spec.js index 1a6f6415328..014df57446e 100644 --- a/test/commands/get.spec.js +++ b/test/commands/get.spec.js @@ -70,6 +70,21 @@ describe("The 'get' method", function () { done(err); }); }); + + it("gets the value correctly with array syntax and the callback being in the array", function (done) { + client.GET([key, function (err, res) { + helper.isString(value)(err, res); + done(err); + }]); + }); + + it("should not throw on a get without callback (even if it's not useful", function (done) { + client.GET(key); + client.on('error', function(err) { + throw err; + }); + setTimeout(done, 50); + }); }); describe("when the key does not exist in Redis", function () { diff --git a/test/commands/getset.spec.js b/test/commands/getset.spec.js index e650ccaa5fa..41c2dc3a8ee 100644 --- a/test/commands/getset.spec.js +++ b/test/commands/getset.spec.js @@ -74,6 +74,26 @@ describe("The 'getset' method", function () { }); }); }); + + it("gets the value correctly with array syntax", function (done) { + client.GETSET([key, value2], function (err, res) { + helper.isString(value)(err, res); + client.get(key, function (err, res) { + helper.isString(value2)(err, res); + done(err); + }); + }); + }); + + it("gets the value correctly with array syntax style 2", function (done) { + client.GETSET(key, [value2], function (err, res) { + helper.isString(value)(err, res); + client.get(key, function (err, res) { + helper.isString(value2)(err, res); + done(err); + }); + }); + }); }); describe("when the key does not exist in Redis", function () { diff --git a/test/commands/hgetall.spec.js b/test/commands/hgetall.spec.js index 74b506e6f0e..e45995aa35a 100644 --- a/test/commands/hgetall.spec.js +++ b/test/commands/hgetall.spec.js @@ -67,7 +67,7 @@ describe("The 'hgetall' method", function () { it('returns binary results', function (done) { client.hmset(["bhosts", "mjr", "1", "another", "23", "home", "1234", new Buffer([0xAA, 0xBB, 0x00, 0xF0]), new Buffer([0xCC, 0xDD, 0x00, 0xF0])], helper.isString("OK")); - client.HGETALL(["bhosts"], function (err, obj) { + client.HGETALL(["bhosts", function (err, obj) { assert.strictEqual(4, Object.keys(obj).length); assert.strictEqual("1", obj.mjr.toString()); assert.strictEqual("23", obj.another.toString()); @@ -75,7 +75,7 @@ describe("The 'hgetall' method", function () { assert.strictEqual((new Buffer([0xAA, 0xBB, 0x00, 0xF0])).toString('binary'), Object.keys(obj)[3]); assert.strictEqual((new Buffer([0xCC, 0xDD, 0x00, 0xF0])).toString('binary'), obj[(new Buffer([0xAA, 0xBB, 0x00, 0xF0])).toString('binary')].toString('binary')); return done(err); - }); + }]); }); }); diff --git a/test/commands/multi.spec.js b/test/commands/multi.spec.js index 45c3ab3f1af..4f78abf8ec1 100644 --- a/test/commands/multi.spec.js +++ b/test/commands/multi.spec.js @@ -165,6 +165,23 @@ describe("The 'multi' method", function () { }); }); + it('allows multiple commands to work the same as normal to be performed using a chaining API', function (done) { + client.multi() + .mset(['some', '10', 'keys', '20']) + .incr('some') + .incr('keys') + .mget('some', 'keys') + .exec(function (err, replies) { + assert.strictEqual(null, err); + assert.equal('OK', replies[0]); + assert.equal(11, replies[1]); + assert.equal(21, replies[2]); + assert.equal(11, replies[3][0].toString()); + assert.equal(21, replies[3][1].toString()); + return done(); + }); + }); + it('allows an array to be provided indicating multiple operations to perform', function (done) { // test nested multi-bulk replies with nulls. client.multi([ From a0c92b075679e2a2ed2d18591135bbe14f459294 Mon Sep 17 00:00:00 2001 From: Ruben Bridgewater Date: Tue, 15 Sep 2015 00:57:31 +0200 Subject: [PATCH 0335/1748] Add a lot more tests to indicate that the everything is consistent Add a test for #390 More special cases --- test/commands/hmset.spec.js | 18 ++++++++++++++++ test/commands/keys.spec.js | 2 +- test/commands/mget.spec.js | 2 +- test/commands/mset.spec.js | 6 ++++++ test/commands/multi.spec.js | 42 +++++++++++++++++++++++++++++++++---- test/commands/rpush.spec.js | 37 ++++++++++++++++++++++++++++++++ test/commands/sadd.spec.js | 11 ++++++++++ test/commands/sdiff.spec.js | 6 +++--- test/commands/sort.spec.js | 7 +++++++ test/commands/srem.spec.js | 4 ++-- test/commands/type.spec.js | 4 ++-- 11 files changed, 126 insertions(+), 13 deletions(-) create mode 100644 test/commands/rpush.spec.js diff --git a/test/commands/hmset.spec.js b/test/commands/hmset.spec.js index d5cc11e3b85..8846b9e9d73 100644 --- a/test/commands/hmset.spec.js +++ b/test/commands/hmset.spec.js @@ -83,6 +83,24 @@ describe("The 'hmset' method", function () { }); }); + it('allows a key plus array without callback', function (done) { + client.HMSET(hash, [99, 'banana', 'test', 25]); + client.HGETALL(hash, function (err, obj) { + assert.equal(obj['99'], 'banana'); + assert.equal(obj.test, '25'); + return done(err); + }); + }); + + it('allows a key plus array and a callback', function (done) { + client.HMSET(hash, [99, 'banana', 'test', 25], helper.isString('OK')); + client.HGETALL(hash, function (err, obj) { + assert.equal(obj['99'], 'banana'); + assert.equal(obj.test, '25'); + return done(err); + }); + }); + it('handles object-style syntax without callback', function (done) { client.HMSET(hash, {"0123456789": "abcdefghij", "some manner of key": "a type of value"}); client.HGETALL(hash, function (err, obj) { diff --git a/test/commands/keys.spec.js b/test/commands/keys.spec.js index 664ec4e4f37..b7a3660f47f 100644 --- a/test/commands/keys.spec.js +++ b/test/commands/keys.spec.js @@ -23,7 +23,7 @@ describe("The 'keys' method", function () { it('returns matching keys', function (done) { client.mset(["test keys 1", "test val 1", "test keys 2", "test val 2"], helper.isString("OK")); - client.KEYS(["test keys*"], function (err, results) { + client.KEYS("test keys*", function (err, results) { assert.strictEqual(2, results.length); assert.ok(~results.indexOf("test keys 1")); assert.ok(~results.indexOf("test keys 2")); diff --git a/test/commands/mget.spec.js b/test/commands/mget.spec.js index 3d506c6d10a..082721e3864 100644 --- a/test/commands/mget.spec.js +++ b/test/commands/mget.spec.js @@ -42,7 +42,7 @@ describe("The 'mget' method", function () { }); it('handles fetching multiple keys, when some keys do not exist', function (done) { - client.MGET(["mget keys 1", "some random shit", "mget keys 2", "mget keys 3"], function (err, results) { + client.MGET("mget keys 1", ["some random shit", "mget keys 2", "mget keys 3"], function (err, results) { assert.strictEqual(4, results.length); assert.strictEqual("mget val 1", results[0].toString()); assert.strictEqual(null, results[1]); diff --git a/test/commands/mset.spec.js b/test/commands/mset.spec.js index a79d6365b1d..476cab5e557 100644 --- a/test/commands/mset.spec.js +++ b/test/commands/mset.spec.js @@ -88,6 +88,12 @@ describe("The 'mset' method", function () { client.get(key, helper.isString(value2)); client.get(key2, helper.isString(value, done)); }); + + it("sets the value correctly with array syntax", function (done) { + client.mset([key, value2, key2, value]); + client.get([key, helper.isString(value2)]); + client.get(key2, helper.isString(value, done)); + }); }); describe("with undefined 'key' and missing 'value' parameter", function () { diff --git a/test/commands/multi.spec.js b/test/commands/multi.spec.js index 4f78abf8ec1..403ca02b77c 100644 --- a/test/commands/multi.spec.js +++ b/test/commands/multi.spec.js @@ -123,7 +123,7 @@ describe("The 'multi' method", function () { it('handles multiple operations being applied to a set', function (done) { client.sadd("some set", "mem 1"); - client.sadd("some set", "mem 2"); + client.sadd(["some set", "mem 2"]); client.sadd("some set", "mem 3"); client.sadd("some set", "mem 4"); @@ -136,7 +136,7 @@ describe("The 'multi' method", function () { // test nested multi-bulk replies with empty mb elements. client.multi([ - ["smembers", "some set"], + ["smembers", ["some set"]], ["del", "some set"], ["smembers", "some set"] ]) @@ -148,6 +148,40 @@ describe("The 'multi' method", function () { }); }); + it('allows multiple operations to be performed using constructor with all kinds of syntax', function (done) { + var now = Date.now(); + client.multi([ + ["mset", [578, "multibar"], helper.isString('OK')], + [["mset", "multifoo2", "multibar2", "multifoo3", "multibar3"], helper.isString('OK')], + ["hmset", ["multihmset", "multibar", "multibaz"]], + [["hmset", "multihmset2", "multibar2", "multifoo3", "multibar3", "test", helper.isString('OK')]], + ["hmset", ["multihmset", "multibar", "multifoo", helper.isString('OK')]], + ["hmset", [5768, "multibarx", "multifoox"], helper.isString('OK')], + ['hmset', now, {123456789: "abcdefghij", "some manner of key": "a type of value", "otherTypes": 555}], + ['hmset', 'key2', {"0123456789": "abcdefghij", "some manner of key": "a type of value", "otherTypes": 999}, helper.isString('OK')], + ["hmset", "multihmset", ["multibar", "multibaz"]], + ["hmset", "multihmset", ["multibar", "multibaz"], helper.isString('OK')], + ]) + .hmget(now, 123456789, 'otherTypes') + .hmget('key2', ['some manner of key', 'otherTypes']) + .hmget(['multihmset2', 'some manner of key', 'multibar3']) + .mget('multifoo2', ['multifoo3', 'multifoo'], function(err, res) { + assert(res[0], 'multifoo3'); + assert(res[1], 'multifoo'); + }) + .exec(function (err, replies) { + assert.strictEqual(null, err); + assert.equal(replies[10][1], '555'); + assert.equal(replies[11][0], 'a type of value'); + assert.strictEqual(replies[12][0], null); + assert.equal(replies[12][1], 'test'); + assert.equal(replies[13][0], 'multibar2'); + assert.equal(replies[13].length, 3); + assert.equal(replies.length, 14); + return done(); + }); + }); + it('allows multiple operations to be performed using a chaining API', function (done) { client.multi() .mset('some', '10', 'keys', '20') @@ -168,8 +202,8 @@ describe("The 'multi' method", function () { it('allows multiple commands to work the same as normal to be performed using a chaining API', function (done) { client.multi() .mset(['some', '10', 'keys', '20']) - .incr('some') - .incr('keys') + .incr(['some', helper.isNumber(11)]) + .incr(['keys'], helper.isNumber(21)) .mget('some', 'keys') .exec(function (err, replies) { assert.strictEqual(null, err); diff --git a/test/commands/rpush.spec.js b/test/commands/rpush.spec.js new file mode 100644 index 00000000000..5c46c7dc797 --- /dev/null +++ b/test/commands/rpush.spec.js @@ -0,0 +1,37 @@ +'use strict'; + +var config = require("../lib/config"); +var helper = require("../helper"); +var redis = config.redis; +var assert = require('assert'); + +describe("The 'rpush' command", function () { + + helper.allTests(function(parser, ip, args) { + + describe("using " + parser + " and " + ip, function () { + var client; + + beforeEach(function (done) { + client = redis.createClient.apply(redis.createClient, args); + client.once("error", done); + client.once("connect", function () { + client.flushdb(done); + }); + }); + + it('inserts multiple values at a time into a list', function (done) { + client.rpush('test', ['list key', 'should be a list']); + client.lrange('test', 0, -1, function(err, res) { + assert.equal(res[0], 'list key'); + assert.equal(res[1], 'should be a list'); + done(err); + }); + }); + + afterEach(function () { + client.end(); + }); + }); + }); +}); diff --git a/test/commands/sadd.spec.js b/test/commands/sadd.spec.js index 1761cd6cf3e..6c92dfe2010 100644 --- a/test/commands/sadd.spec.js +++ b/test/commands/sadd.spec.js @@ -44,6 +44,17 @@ describe("The 'sadd' method", function () { }); }); + it('allows multiple values to be added to the set with a different syntax', function (done) { + client.sadd(["set0", "member0", "member1", "member2"], helper.isNumber(3)); + client.smembers("set0", function (err, res) { + assert.strictEqual(res.length, 3); + assert.ok(~res.indexOf("member0")); + assert.ok(~res.indexOf("member1")); + assert.ok(~res.indexOf("member2")); + return done(err); + }); + }); + afterEach(function () { client.end(); }); diff --git a/test/commands/sdiff.spec.js b/test/commands/sdiff.spec.js index 8057bc15af4..cb2a97f14ba 100644 --- a/test/commands/sdiff.spec.js +++ b/test/commands/sdiff.spec.js @@ -22,11 +22,11 @@ describe("The 'sdiff' method", function () { it('returns set difference', function (done) { client.sadd('foo', 'x', helper.isNumber(1)); - client.sadd('foo', 'a', helper.isNumber(1)); + client.sadd('foo', ['a'], helper.isNumber(1)); client.sadd('foo', 'b', helper.isNumber(1)); - client.sadd('foo', 'c', helper.isNumber(1)); + client.sadd(['foo', 'c'], helper.isNumber(1)); - client.sadd('bar', 'c', helper.isNumber(1)); + client.sadd(['bar', 'c', helper.isNumber(1)]); client.sadd('baz', 'a', helper.isNumber(1)); client.sadd('baz', 'd', helper.isNumber(1)); diff --git a/test/commands/sort.spec.js b/test/commands/sort.spec.js index e5985137def..5d8e5aff8a6 100644 --- a/test/commands/sort.spec.js +++ b/test/commands/sort.spec.js @@ -75,6 +75,13 @@ describe("The 'sort' method", function () { }); }); + it("handles sorting with a 'by' pattern and 2 'get' patterns with the array syntax", function (done) { + client.sort(['x', 'by', 'w*', 'asc', 'get', 'o*', 'get', 'p*'], function (err, sorted) { + assert.deepEqual(sorted, ['foo', 'bux', 'bar', 'tux', 'baz', 'lux', 'buz', 'qux']); + return done(err); + }); + }); + it("sorting with a 'by' pattern and 2 'get' patterns and stores results", function (done) { client.sort('x', 'by', 'w*', 'asc', 'get', 'o*', 'get', 'p*', 'store', 'bacon', function (err) { if (err) return done(err); diff --git a/test/commands/srem.spec.js b/test/commands/srem.spec.js index 8afd79afeda..d6a64a006ea 100644 --- a/test/commands/srem.spec.js +++ b/test/commands/srem.spec.js @@ -41,8 +41,8 @@ describe("The 'srem' method", function () { }); it('handles a value missing from the set of values being removed', function (done) { - client.sadd("set0", ["member0", "member1", "member2"], helper.isNumber(3)); - client.SREM("set0", ["member3", "member4"], helper.isNumber(0)); + client.sadd(["set0", "member0", "member1", "member2"], helper.isNumber(3)); + client.SREM(["set0", "member3", "member4"], helper.isNumber(0)); client.smembers("set0", function (err, res) { assert.strictEqual(res.length, 3); assert.ok(~res.indexOf("member0")); diff --git a/test/commands/type.spec.js b/test/commands/type.spec.js index e4ad527d91a..e9ec7b95f37 100644 --- a/test/commands/type.spec.js +++ b/test/commands/type.spec.js @@ -35,12 +35,12 @@ describe("The 'type' method", function () { }); it('reports zset type', function (done) { - client.zadd(["zset key", "10.0", "should be a zset"], helper.isNumber(1)); + client.zadd("zset key", ["10.0", "should be a zset"], helper.isNumber(1)); client.TYPE(["zset key"], helper.isString("zset", done)); }); it('reports hash type', function (done) { - client.hset(["hash key", "hashtest", "should be a hash"], helper.isNumber(1)); + client.hset("hash key", "hashtest", "should be a hash", helper.isNumber(1)); client.TYPE(["hash key"], helper.isString("hash", done)); }); From c269b7539c53b66f4ce732a42e7d8c210e6a28db Mon Sep 17 00:00:00 2001 From: Ruben Bridgewater Date: Tue, 15 Sep 2015 22:33:46 +0200 Subject: [PATCH 0336/1748] Check that the password is from type string --- index.js | 12 +++++++++++- test/auth.spec.js | 28 ++++++++++++++++++++++++++-- 2 files changed, 37 insertions(+), 3 deletions(-) diff --git a/index.js b/index.js index efca5866448..dfed39313cb 100644 --- a/index.js +++ b/index.js @@ -252,7 +252,7 @@ RedisClient.prototype.on_connect = function () { this.init_parser(); - if (this.auth_pass) { + if (typeof this.auth_pass === 'string') { this.do_auth(); } else { this.emit("connect"); @@ -911,6 +911,16 @@ RedisClient.prototype.select = RedisClient.prototype.SELECT = function (db, call // Stash auth for connect and reconnect. Send immediately if already connected. RedisClient.prototype.auth = RedisClient.prototype.AUTH = function (pass, callback) { + if (typeof pass !== 'string') { + var err = new Error('The password has to be of type "string"'); + err.command_used = 'AUTH'; + if (callback) { + callback(err); + } else { + this.emit('error', err); + } + return; + } this.auth_pass = pass; this.auth_callback = callback; debug("Saving auth as " + this.auth_pass); diff --git a/test/auth.spec.js b/test/auth.spec.js index 3a1c8812ca5..6eb96e0f87e 100644 --- a/test/auth.spec.js +++ b/test/auth.spec.js @@ -48,12 +48,12 @@ describe("client authentication", function () { client.auth(auth + 'bad'); }); - it("returns an error when auth is bad with a callback", function (done) { + it("returns an error when auth is bad (empty string) with a callback", function (done) { if (helper.redisProcess().spawnFailed()) this.skip(); client = redis.createClient.apply(redis.createClient, args); - client.auth(auth + 'bad', function (err, res) { + client.auth('', function (err, res) { assert.strictEqual(err.command_used, 'AUTH'); assert.ok(/ERR invalid password/.test(err.message)); done(); @@ -122,6 +122,30 @@ describe("client authentication", function () { } }); }); + + it('should return an error if the password is not of type string and a callback has been provided', function (done) { + if (helper.redisProcess().spawnFailed()) this.skip(); + + client = redis.createClient.apply(redis.createClient, args); + client.auth(undefined, function(err, res) { + assert.strictEqual(err.message, 'The password has to be of type "string"'); + assert.strictEqual(err.command_used, 'AUTH'); + assert.strictEqual(res, undefined); + done(); + }); + }); + + it('should emit an error if the password is not of type string and no callback has been provided', function (done) { + if (helper.redisProcess().spawnFailed()) this.skip(); + + client = redis.createClient.apply(redis.createClient, args); + client.on('error', function (err) { + assert.strictEqual(err.message, 'The password has to be of type "string"'); + assert.strictEqual(err.command_used, 'AUTH'); + done(); + }); + client.auth(234567); + }); }); }); From 013831c00f9dbf02197ebae839081331866182f9 Mon Sep 17 00:00:00 2001 From: Ruben Bridgewater Date: Tue, 15 Sep 2015 22:37:04 +0200 Subject: [PATCH 0337/1748] Improve js parser --- lib/parser/javascript.js | 61 ++++++++++------------------------------ 1 file changed, 15 insertions(+), 46 deletions(-) diff --git a/lib/parser/javascript.js b/lib/parser/javascript.js index 991761b1ec2..bdb75788414 100644 --- a/lib/parser/javascript.js +++ b/lib/parser/javascript.js @@ -54,6 +54,11 @@ ReplyParser.prototype._parseResult = function (type) { throw new IncompleteReadBuffer("Wait for more data."); } + if (type === 45) { + var result = this._buffer.toString(this._encoding, start, end); + return new Error(result); + } + if (this.options.return_buffers) { return this._buffer.slice(start, end); } else { @@ -76,12 +81,8 @@ ReplyParser.prototype._parseResult = function (type) { throw new IncompleteReadBuffer("Wait for more data."); } - if (this.options.return_buffers) { - return this._buffer.slice(start, end); - } - // return the coerced numeric value - return +small_toString(this._buffer, start, end); + return +this._buffer.toString('ascii', start, end); } else if (type === 36) { // $ // set a rewind point, as the packet could be larger than the // buffer in memory @@ -123,7 +124,7 @@ ReplyParser.prototype._parseResult = function (type) { throw new IncompleteReadBuffer("Wait for more data."); } - var reply = [ ]; + var reply = []; var ntype, i, res; offset = this._offset - 1; @@ -152,36 +153,25 @@ ReplyParser.prototype.execute = function (buffer) { while (true) { offset = this._offset; - try { - // at least 4 bytes: :1\r\n - if (this._bytesRemaining() < 4) { - break; - } + // at least 4 bytes: :1\r\n + if (this._bytesRemaining() < 4) { + break; + } + try { type = this._buffer[this._offset++]; if (type === 43) { // + ret = this._parseResult(type); - if (ret === null) { - break; - } - this.send_reply(ret); } else if (type === 45) { // - ret = this._parseResult(type); - if (ret === null) { - break; - } - this.send_error(new Error(ret)); + this.send_error(ret); } else if (type === 58) { // : ret = this._parseResult(type); - if (ret === null) { - break; - } - this.send_reply(ret); } else if (type === 36) { // $ ret = this._parseResult(type); @@ -219,9 +209,6 @@ ReplyParser.prototype.execute = function (buffer) { }; ReplyParser.prototype.append = function (newBuffer) { - if (!newBuffer) { - return; - } // first run if (this._buffer === null) { @@ -238,27 +225,13 @@ ReplyParser.prototype.append = function (newBuffer) { return; } - // very large packet - // check for concat, if we have it, use it - if (Buffer.concat !== undefined) { - this._buffer = Buffer.concat([this._buffer.slice(this._offset), newBuffer]); - } else { - var remaining = this._bytesRemaining(), - newLength = remaining + newBuffer.length, - tmpBuffer = new Buffer(newLength); - - this._buffer.copy(tmpBuffer, 0, this._offset); - newBuffer.copy(tmpBuffer, remaining, 0); - - this._buffer = tmpBuffer; - } - + this._buffer = Buffer.concat([this._buffer.slice(this._offset), newBuffer]); this._offset = 0; }; ReplyParser.prototype.parseHeader = function () { var end = this._packetEndOffset(), - value = small_toString(this._buffer, this._offset, end - 1); + value = this._buffer.toString('ascii', this._offset, end - 1); this._offset = end + 1; @@ -270,10 +243,6 @@ ReplyParser.prototype._packetEndOffset = function () { while (this._buffer[offset] !== 0x0d && this._buffer[offset + 1] !== 0x0a) { offset++; - - if (offset >= this._buffer.length) { - throw new IncompleteReadBuffer("didn't see LF after NL reading multi bulk count (" + offset + " => " + this._buffer.length + ", " + this._offset + ")"); - } } offset++; From 005e869d838635ed1a4e640231f3181b12c8b2f2 Mon Sep 17 00:00:00 2001 From: Ruben Bridgewater Date: Fri, 4 Sep 2015 15:14:57 +0200 Subject: [PATCH 0338/1748] Remove send_command safety checks This checks are only important for users who use send_command directly instead of using the convience method. As the readme clearly stats how send_command should work and any user would have run into errors if misused, these checks can be removed. If any user might misuse the function anyway, it is very likely that another error will be thrown because of that Fix #629 and insert tests --- index.js | 47 ++++++++++++++++------------------------- test/node_redis.spec.js | 38 +++++++++++++++++++++++++++++++++ 2 files changed, 56 insertions(+), 29 deletions(-) diff --git a/index.js b/index.js index efca5866448..ec8e22dd824 100644 --- a/index.js +++ b/index.js @@ -661,37 +661,26 @@ function Command(command, args, sub_command, buffer_args, callback) { } RedisClient.prototype.send_command = function (command, args, callback) { - var arg, command_obj, i, elem_count, buffer_args, stream = this.stream, command_str = "", buffered_writes = 0, last_arg_type; - - if (typeof command !== "string") { - throw new Error("First argument to send_command must be the command name string, not " + typeof command); - } - - if (Array.isArray(args)) { - if (typeof callback === "function") { - // probably the fastest way: - // client.command([arg1, arg2], cb); (straight passthrough) - // send_command(command, [arg1, arg2], cb); - } else if (!callback) { - // most people find this variable argument length form more convenient, but it uses arguments, which is slower - // client.command(arg1, arg2, cb); (wraps up arguments into an array) - // send_command(command, [arg1, arg2, cb]); - // client.command(arg1, arg2); (callback is optional) - // send_command(command, [arg1, arg2]); - // client.command(arg1, arg2, undefined); (callback is undefined) - // send_command(command, [arg1, arg2, undefined]); - last_arg_type = typeof args[args.length - 1]; - if (last_arg_type === "function" || last_arg_type === "undefined") { - callback = args.pop(); - } - } else { - throw new Error("send_command: last argument must be a callback or undefined"); - } - } else { - throw new Error("send_command: second argument must be an array"); + var arg, command_obj, i, elem_count, buffer_args, stream = this.stream, command_str = "", buffered_writes = 0; + + // if (typeof callback === "function") {} + // probably the fastest way: + // client.command([arg1, arg2], cb); (straight passthrough) + // send_command(command, [arg1, arg2], cb); + if (args === undefined) { + args = []; + } else if (!callback && typeof args[args.length - 1] === "function") { + // most people find this variable argument length form more convenient, but it uses arguments, which is slower + // client.command(arg1, arg2, cb); (wraps up arguments into an array) + // send_command(command, [arg1, arg2, cb]); + // client.command(arg1, arg2); (callback is optional) + // send_command(command, [arg1, arg2]); + // client.command(arg1, arg2, undefined); (callback is undefined) + // send_command(command, [arg1, arg2, undefined]); + callback = args.pop(); } - if (callback && process.domain) { + if (process.domain && callback) { callback = process.domain.bind(callback); } diff --git a/test/node_redis.spec.js b/test/node_redis.spec.js index e9eb0c33594..bad74396873 100644 --- a/test/node_redis.spec.js +++ b/test/node_redis.spec.js @@ -117,6 +117,44 @@ describe("The node_redis client", function () { client.end(); }); + describe("send_command", function () { + + it("omitting args should be fine in some cases", function (done) { + client.send_command("info", undefined, function(err, res) { + assert(/redis_version/.test(res)); + done(); + }); + }); + + it("using another type as cb should just work as if there were no callback parameter", function (done) { + client.send_command('set', ['test', 'bla'], [true]); + client.get('test', function(err, res) { + assert.equal(res, 'bla'); + done(); + }); + }); + + it("misusing the function should eventually throw (no command)", function (done) { + var mochaListener = helper.removeMochaListener(); + + process.once('uncaughtException', function (err) { + process.on('uncaughtException', mochaListener); + assert(/is not a function|toUpperCase/.test(err)); + done(); + }); + + client.send_command(true, 'info'); + }); + + it("misusing the function should eventually throw (wrong args)", function (done) { + client.send_command('info', false, function(err, res) { + assert.equal(err.message, 'ERR Protocol error: invalid multibulk length'); + done(); + }); + }); + + }); + describe("when redis closes unexpectedly", function () { it("reconnects and can retrieve the pre-existing data", function (done) { client.on("reconnecting", function on_recon(params) { From cd5cfb4a8eea1b8ad5a3efb8de0187a1929774ed Mon Sep 17 00:00:00 2001 From: Ruben Bridgewater Date: Thu, 10 Sep 2015 12:32:07 +0200 Subject: [PATCH 0339/1748] Emit an error when connection permanently goes down Closes #724 and #615 --- README.md | 5 +++++ index.js | 7 ++----- 2 files changed, 7 insertions(+), 5 deletions(-) diff --git a/README.md b/README.md index 0e1d4444208..b44d52227fd 100644 --- a/README.md +++ b/README.md @@ -113,6 +113,11 @@ then replayed just before this event is emitted. is set. If this options is set, `connect` will be emitted when the stream is connected, and then you are free to try to send commands. +### "reconnecting" + +`client` will emit `reconnecting` when trying to reconnect to the Redis server after losing the connection. Listeners +are passed an object containing `delay` (in ms) and `attempt` (the attempt #) attributes. + ### "error" `client` will emit `error` when encountering an error connecting to the Redis server. diff --git a/index.js b/index.js index 0011ef289cd..28c88a53abf 100644 --- a/index.js +++ b/index.js @@ -463,9 +463,7 @@ RedisClient.prototype.connection_gone = function (why) { if (this.max_attempts && this.attempts >= this.max_attempts) { this.retry_timer = null; - // TODO - some people need a "Redis is Broken mode" for future commands that errors immediately, and others - // want the program to exit. Right now, we just log, which doesn't really help in either case. - debug("Couldn't get Redis connection after " + this.max_attempts + " attempts."); + this.emit('error', new Error("Redis connection in broken state: maximum connection attempts exceeded.")); return; } @@ -481,8 +479,7 @@ RedisClient.prototype.connection_gone = function (why) { if (self.connect_timeout && self.retry_totaltime >= self.connect_timeout) { self.retry_timer = null; - // TODO - engage Redis is Broken mode for future commands, or whatever - debug("Couldn't get Redis connection after " + self.retry_totaltime + "ms."); + this.emit('error', new Error("Redis connection in broken state: connection timeout exceeded.")); return; } From 04c986a4cd6e8a2968bd9319a5394bcd9ee9a8bd Mon Sep 17 00:00:00 2001 From: Ruben Bridgewater Date: Thu, 10 Sep 2015 16:17:09 +0200 Subject: [PATCH 0340/1748] Lowering the first retry_delay and begin from that value Earlier the first value was never used, as it was first multiplied by 1.7 --- README.md | 4 ++-- index.js | 2 +- 2 files changed, 3 insertions(+), 3 deletions(-) diff --git a/README.md b/README.md index b44d52227fd..4e51156cae8 100644 --- a/README.md +++ b/README.md @@ -581,12 +581,12 @@ some kind of maximum queue depth for pre-connection commands. ## client.retry_delay -Current delay in milliseconds before a connection retry will be attempted. This starts at `250`. +Current delay in milliseconds before a connection retry will be attempted. This starts at `200`. ## client.retry_backoff Multiplier for future retry timeouts. This should be larger than 1 to add more time between retries. -Defaults to 1.7. The default initial connection retry is 250, so the second retry will be 425, followed by 723.5, etc. +Defaults to 1.7. The default initial connection retry is 200, so the second retry will be 340, followed by 578, etc. ### Commands with Optional and Keyword arguments diff --git a/index.js b/index.js index 28c88a53abf..cf6a862a21e 100644 --- a/index.js +++ b/index.js @@ -123,7 +123,7 @@ RedisClient.prototype.install_stream_listeners = function() { RedisClient.prototype.initialize_retry_vars = function () { this.retry_timer = null; this.retry_totaltime = 0; - this.retry_delay = 150; + this.retry_delay = 200; this.retry_backoff = 1.7; this.attempts = 1; }; From f2ee8dbc9ebe9690061dc93357ede0d85364fa39 Mon Sep 17 00:00:00 2001 From: Ruben Bridgewater Date: Thu, 10 Sep 2015 17:59:25 +0200 Subject: [PATCH 0341/1748] Add a connection timeout of 24h as new default for maximum reconnecting --- README.md | 5 +++-- index.js | 4 +--- 2 files changed, 4 insertions(+), 5 deletions(-) diff --git a/README.md b/README.md index 4e51156cae8..51523b21f52 100644 --- a/README.md +++ b/README.md @@ -194,8 +194,9 @@ with an error, or an error will be thrown if no callback is specified. * `retry_max_delay`: defaults to `null`. By default every time the client tries to connect and fails time before reconnection (delay) almost doubles. This delay normally grows infinitely, but setting `retry_max_delay` limits delay to maximum value, provided in milliseconds. -* `connect_timeout` defaults to `false`. By default client will try reconnecting until connected. Setting `connect_timeout` -limits total time for client to reconnect. Value is provided in milliseconds and is counted once the disconnect occured. +* `connect_timeout` defaults to `86400000`. Setting `connect_timeout` limits total time for client to reconnect. +Value is provided in milliseconds and is counted once the disconnect occured. The last retry is going to happen once after the connect_timeout value is exceeded. +That way the default is to try reconnecting until at least 24h passed. * `max_attempts` defaults to `null`. By default client will try reconnecting until connected. Setting `max_attempts` limits total amount of reconnects. * `auth_pass` defaults to `null`. By default client will try connecting without auth. If set, client will run redis auth command on connect. diff --git a/index.js b/index.js index cf6a862a21e..7b4170ad793 100644 --- a/index.js +++ b/index.js @@ -58,9 +58,7 @@ function RedisClient(stream, options) { this.command_queue = new Queue(); // holds sent commands to de-pipeline them this.offline_queue = new Queue(); // holds commands issued but not able to be sent this.commands_sent = 0; - if (options.connect_timeout && options.connect_timeout > 0) { - this.connect_timeout = +options.connect_timeout; - } + this.connect_timeout = +options.connect_timeout || 86400000; // 24 * 60 * 60 * 1000 ms this.enable_offline_queue = true; if (this.options.enable_offline_queue === false) { this.enable_offline_queue = false; From 1e0421ac3b3af3944e45c0f1a7286057e3891e24 Mon Sep 17 00:00:00 2001 From: Ruben Bridgewater Date: Thu, 10 Sep 2015 18:00:57 +0200 Subject: [PATCH 0342/1748] Emit errors if the connection timeout / maximum retry attempts have been exceeded Accept setting max_attempts to zero. The reconnection event is now emitted when trying to reconnect instead of earlier. The connection timeout is now going to trigger once after exceeding the maximum timeout instead of stopping earlier. --- README.md | 2 +- index.js | 39 ++++++++++++++++++++------------------- test/node_redis.spec.js | 4 ++-- 3 files changed, 23 insertions(+), 22 deletions(-) diff --git a/README.md b/README.md index 51523b21f52..7e75d5e6304 100644 --- a/README.md +++ b/README.md @@ -198,7 +198,7 @@ to maximum value, provided in milliseconds. Value is provided in milliseconds and is counted once the disconnect occured. The last retry is going to happen once after the connect_timeout value is exceeded. That way the default is to try reconnecting until at least 24h passed. * `max_attempts` defaults to `null`. By default client will try reconnecting until connected. Setting `max_attempts` -limits total amount of reconnects. +limits total amount of reconnects. Setting this to 0 will prevent any reconnect tries. * `auth_pass` defaults to `null`. By default client will try connecting without auth. If set, client will run redis auth command on connect. * `family` defaults to `IPv4`. The client connects in IPv4 if not specified or if the DNS resolution returns an IPv4 address. You can force an IPv6 if you set the family to 'IPv6'. See nodejs net or dns modules how to use the family type. diff --git a/index.js b/index.js index 7b4170ad793..3ad63fa1c5f 100644 --- a/index.js +++ b/index.js @@ -52,7 +52,8 @@ function RedisClient(stream, options) { this.should_buffer = false; this.command_queue_high_water = this.options.command_queue_high_water || 1000; this.command_queue_low_water = this.options.command_queue_low_water || 0; - if (options.max_attempts && options.max_attempts > 0) { + this.max_attempts = null; + if (options.max_attempts || options.max_attempts === 0 || options.max_attempts === '0') { this.max_attempts = +options.max_attempts; } this.command_queue = new Queue(); // holds sent commands to de-pipeline them @@ -419,6 +420,16 @@ RedisClient.prototype.connection_gone = function (why) { return; } + if (this.max_attempts !== null && this.attempts > this.max_attempts) { + this.emit('error', new Error("Redis connection in broken state: maximum connection attempts exceeded.")); + return; + } + + if (this.retry_totaltime > this.connect_timeout) { + this.emit('error', new Error("Redis connection in broken state: connection timeout exceeded.")); + return; + } + debug("Redis connection is gone from " + why + " event."); this.connected = false; this.ready = false; @@ -436,7 +447,7 @@ RedisClient.prototype.connection_gone = function (why) { } // since we are collapsing end and close, users don't expect to be called twice - if (! this.emitted_end) { + if (!this.emitted_end) { this.emit("end"); this.emitted_end = true; } @@ -459,30 +470,20 @@ RedisClient.prototype.connection_gone = function (why) { debug("Retry connection in " + this.retry_delay + " ms"); - if (this.max_attempts && this.attempts >= this.max_attempts) { - this.retry_timer = null; - this.emit('error', new Error("Redis connection in broken state: maximum connection attempts exceeded.")); - return; - } - - this.attempts += 1; - this.emit("reconnecting", { - delay: self.retry_delay, - attempt: self.attempts - }); this.retry_timer = setTimeout(function () { debug("Retrying connection..."); - self.retry_totaltime += self.retry_delay; + self.emit("reconnecting", { + delay: self.retry_delay, + attempt: self.attempts + }); - if (self.connect_timeout && self.retry_totaltime >= self.connect_timeout) { - self.retry_timer = null; - this.emit('error', new Error("Redis connection in broken state: connection timeout exceeded.")); - return; - } + self.retry_totaltime += self.retry_delay; + self.attempts += 1; self.stream = net.createConnection(self.connectionOption); self.install_stream_listeners(); + self.retry_timer = null; }, this.retry_delay); }; diff --git a/test/node_redis.spec.js b/test/node_redis.spec.js index bad74396873..38a65f8ecd5 100644 --- a/test/node_redis.spec.js +++ b/test/node_redis.spec.js @@ -687,7 +687,7 @@ describe("The node_redis client", function () { describe('true', function () { it("does not return an error and enqueues operation", function (done) { var client = redis.createClient(9999, null, { - max_attempts: 1, + max_attempts: 0, parser: parser }); @@ -715,7 +715,7 @@ describe("The node_redis client", function () { it("does not emit an error and enqueues operation", function (done) { var client = redis.createClient(9999, null, { parser: parser, - max_attempts: 1, + max_attempts: 0, enable_offline_queue: false }); From 0b8705abe9e3a5c4c7bc0e3e020246de2d3a013b Mon Sep 17 00:00:00 2001 From: Ruben Bridgewater Date: Thu, 10 Sep 2015 18:06:50 +0200 Subject: [PATCH 0343/1748] Do not run all tests with every single connection (if one connection works, the others are going to be fine too) --- test/auth.spec.js | 5 +++-- test/commands/hgetall.spec.js | 1 - test/helper.js | 35 ++++++++++++++++++++--------------- test/node_redis.spec.js | 7 ++++--- 4 files changed, 27 insertions(+), 21 deletions(-) diff --git a/test/auth.spec.js b/test/auth.spec.js index 6eb96e0f87e..d29b605f270 100644 --- a/test/auth.spec.js +++ b/test/auth.spec.js @@ -12,10 +12,11 @@ describe("client authentication", function () { }); }); - helper.allTests(function(parser, ip, args) { + helper.allTests({ + allConnections: true + }, function(parser, ip, args) { describe("using " + parser + " and " + ip, function () { - var args = config.configureClient(parser, ip); var auth = 'porkchopsandwiches'; var client = null; diff --git a/test/commands/hgetall.spec.js b/test/commands/hgetall.spec.js index e45995aa35a..958e717c8c2 100644 --- a/test/commands/hgetall.spec.js +++ b/test/commands/hgetall.spec.js @@ -13,7 +13,6 @@ describe("The 'hgetall' method", function () { var client; describe('regular client', function () { - var args = config.configureClient(parser, ip); beforeEach(function (done) { client = redis.createClient.apply(redis.createClient, args); diff --git a/test/helper.js b/test/helper.js index 5f4d7b46241..7e9fb5ff2a1 100644 --- a/test/helper.js +++ b/test/helper.js @@ -108,22 +108,27 @@ module.exports = { } return true; }, - allTests: function (cb) { - [undefined].forEach(function (options) { // add buffer option at some point - describe(options && options.return_buffers ? "returning buffers" : "returning strings", function () { - var parsers = ['javascript']; - var protocols = ['IPv4']; - if (process.platform !== 'win32') { - parsers.push('hiredis'); - protocols.push('IPv6'); + allTests: function (options, cb) { + if (!cb) { + cb = options; + options = {}; + } + // TODO: Test all different option cases at some point (e.g. buffers) + // [undefined, { return_buffers: true }].forEach(function (config_options) { + // describe(config_options && config_options.return_buffers ? "returning buffers" : "returning strings", function () { + // }); + // }); + var parsers = ['javascript']; + var protocols = ['IPv4']; + if (process.platform !== 'win32') { + parsers.push('hiredis'); + protocols.push('IPv6', '/tmp/redis.sock'); + } + parsers.forEach(function (parser) { + protocols.forEach(function (ip, i) { + if (i === 0 || options.allConnections) { + cb(parser, ip, config.configureClient(parser, ip)); } - - parsers.forEach(function (parser) { - if (process.platform !== 'win32') cb(parser, "/tmp/redis.sock", config.configureClient(parser, "/tmp/redis.sock", options)); - protocols.forEach(function (ip) { - cb(parser, ip, config.configureClient(parser, ip, options)); - }); - }); }); }); }, diff --git a/test/node_redis.spec.js b/test/node_redis.spec.js index 38a65f8ecd5..9725cce67b3 100644 --- a/test/node_redis.spec.js +++ b/test/node_redis.spec.js @@ -8,7 +8,9 @@ var redis = config.redis; describe("The node_redis client", function () { - helper.allTests(function(parser, ip, args) { + helper.allTests({ + allConnections: true + }, function(parser, ip, args) { if (args[2]) { // skip if options are undefined describe("testing parser existence", function () { @@ -624,7 +626,6 @@ describe("The node_redis client", function () { describe('defaults to true', function () { var client; - var args = config.configureClient(parser, ip); it("fires client.on('ready')", function (done) { client = redis.createClient.apply(redis.createClient, args); @@ -703,7 +704,7 @@ describe("The node_redis client", function () { if (err) return done(err); }); - return setTimeout(function(){ + return setTimeout(function() { assert.strictEqual(client.offline_queue.length, 1); return done(); }, 25); From 03e8c035034327c3e8ddd71dccd9d34f8921cc5f Mon Sep 17 00:00:00 2001 From: Ruben Bridgewater Date: Thu, 10 Sep 2015 18:08:07 +0200 Subject: [PATCH 0344/1748] Add connection timeout and max attempts tests --- test/connection.spec.js | 76 +++++++++++++++++++++++++++++++++++++++++ 1 file changed, 76 insertions(+) create mode 100644 test/connection.spec.js diff --git a/test/connection.spec.js b/test/connection.spec.js new file mode 100644 index 00000000000..af48d2f9d61 --- /dev/null +++ b/test/connection.spec.js @@ -0,0 +1,76 @@ +'use strict'; + +var assert = require("assert"); +var config = require("./lib/config"); +var helper = require('./helper'); +var redis = config.redis; + +describe("on lost connection", function () { + helper.allTests(function(parser, ip, args) { + + describe("using " + parser + " and " + ip, function () { + + it("emit an error after max retry attempts and do not try to reconnect afterwards", function (done) { + var max_attempts = 3; + var client = redis.createClient({ + parser: parser, + max_attempts: max_attempts + }); + var calls = 0; + + client.once('ready', function() { + // Pretend that redis can't reconnect + client.on_connect = client.on_error; + client.stream.destroy(); + }); + + client.on("reconnecting", function (params) { + calls++; + }); + + client.on('error', function(err) { + if (/Redis connection in broken state: maximum connection attempts.*?exceeded./.test(err.message)) { + setTimeout(function () { + assert.strictEqual(calls, max_attempts); + done(); + }, 1500); + } + }); + }); + + it("emit an error after max retry timeout and do not try to reconnect afterwards", function (done) { + var connect_timeout = 1000; // in ms + var client = redis.createClient({ + parser: parser, + connect_timeout: connect_timeout + }); + var time = 0; + var multiplier = 0; + + client.once('ready', function() { + // Pretend that redis can't reconnect + client.on_connect = client.on_error; + client.stream.destroy(); + }); + + client.on("reconnecting", function (params) { + if (time > 0 && multiplier === 0) { + multiplier = params.delay / time; + } + time += params.delay; + }); + + client.on('error', function(err) { + if (/Redis connection in broken state: connection timeout.*?exceeded./.test(err.message)) { + setTimeout(function () { + assert(time > connect_timeout); + assert(time / multiplier < connect_timeout); + done(); + }, 1500); + } + }); + }); + + }); + }); +}); From a9e7663affb7677e2761db10d4d147abed2aac2f Mon Sep 17 00:00:00 2001 From: Chris Hamant Date: Tue, 17 Dec 2013 15:31:53 -0500 Subject: [PATCH 0345/1748] removing flush_and_error from on_error handler --- index.js | 2 -- 1 file changed, 2 deletions(-) diff --git a/index.js b/index.js index 3ad63fa1c5f..db6ca5154a1 100644 --- a/index.js +++ b/index.js @@ -171,8 +171,6 @@ RedisClient.prototype.on_error = function (msg) { debug(message); - this.flush_and_error(message); - this.connected = false; this.ready = false; From 3c2ba8c3736fd61fe947dfff365f7150dd639f45 Mon Sep 17 00:00:00 2001 From: Ruben Bridgewater Date: Thu, 10 Sep 2015 18:40:43 +0200 Subject: [PATCH 0346/1748] Try exactly until the connection timeout has been reached Fixes #587 --- README.md | 4 ++-- index.js | 11 ++++++----- test/connection.spec.js | 7 +------ 3 files changed, 9 insertions(+), 13 deletions(-) diff --git a/README.md b/README.md index 7e75d5e6304..528a0b894c2 100644 --- a/README.md +++ b/README.md @@ -195,8 +195,8 @@ with an error, or an error will be thrown if no callback is specified. reconnection (delay) almost doubles. This delay normally grows infinitely, but setting `retry_max_delay` limits delay to maximum value, provided in milliseconds. * `connect_timeout` defaults to `86400000`. Setting `connect_timeout` limits total time for client to reconnect. -Value is provided in milliseconds and is counted once the disconnect occured. The last retry is going to happen once after the connect_timeout value is exceeded. -That way the default is to try reconnecting until at least 24h passed. +Value is provided in milliseconds and is counted once the disconnect occured. The last retry is going to happen exactly at the timeout time. +That way the default is to try reconnecting until 24h passed. * `max_attempts` defaults to `null`. By default client will try reconnecting until connected. Setting `max_attempts` limits total amount of reconnects. Setting this to 0 will prevent any reconnect tries. * `auth_pass` defaults to `null`. By default client will try connecting without auth. If set, client will run redis auth command on connect. diff --git a/index.js b/index.js index db6ca5154a1..e20577a684a 100644 --- a/index.js +++ b/index.js @@ -423,7 +423,7 @@ RedisClient.prototype.connection_gone = function (why) { return; } - if (this.retry_totaltime > this.connect_timeout) { + if (this.retry_totaltime >= this.connect_timeout) { this.emit('error', new Error("Redis connection in broken state: connection timeout exceeded.")); return; } @@ -459,11 +459,12 @@ RedisClient.prototype.connection_gone = function (why) { return; } - var nextDelay = Math.floor(this.retry_delay * this.retry_backoff); - if (this.retry_max_delay !== null && nextDelay > this.retry_max_delay) { + this.retry_delay = Math.floor(this.retry_delay * this.retry_backoff); + if (this.retry_max_delay !== null && this.retry_delay > this.retry_max_delay) { this.retry_delay = this.retry_max_delay; - } else { - this.retry_delay = nextDelay; + } else if (this.retry_totaltime + this.retry_delay > this.connect_timeout) { + // Do not exceed the maximum + this.retry_delay = this.connect_timeout - this.retry_totaltime; } debug("Retry connection in " + this.retry_delay + " ms"); diff --git a/test/connection.spec.js b/test/connection.spec.js index af48d2f9d61..d76a0599416 100644 --- a/test/connection.spec.js +++ b/test/connection.spec.js @@ -45,7 +45,6 @@ describe("on lost connection", function () { connect_timeout: connect_timeout }); var time = 0; - var multiplier = 0; client.once('ready', function() { // Pretend that redis can't reconnect @@ -54,17 +53,13 @@ describe("on lost connection", function () { }); client.on("reconnecting", function (params) { - if (time > 0 && multiplier === 0) { - multiplier = params.delay / time; - } time += params.delay; }); client.on('error', function(err) { if (/Redis connection in broken state: connection timeout.*?exceeded./.test(err.message)) { setTimeout(function () { - assert(time > connect_timeout); - assert(time / multiplier < connect_timeout); + assert(time === connect_timeout); done(); }, 1500); } From 55d0036eaeb9ccc0b4c71c81249bafc847dd520e Mon Sep 17 00:00:00 2001 From: Ruben Bridgewater Date: Thu, 10 Sep 2015 19:19:09 +0200 Subject: [PATCH 0347/1748] Add test and fix keeping the offline queue Use a new delay after reconnecting --- README.md | 4 ++-- index.js | 35 +++++++++++++++-------------------- test/connection.spec.js | 6 +++--- test/node_redis.spec.js | 30 +++++++++++++++++++++++++++++- 4 files changed, 49 insertions(+), 26 deletions(-) diff --git a/README.md b/README.md index 528a0b894c2..a29c6ae1342 100644 --- a/README.md +++ b/README.md @@ -197,8 +197,8 @@ to maximum value, provided in milliseconds. * `connect_timeout` defaults to `86400000`. Setting `connect_timeout` limits total time for client to reconnect. Value is provided in milliseconds and is counted once the disconnect occured. The last retry is going to happen exactly at the timeout time. That way the default is to try reconnecting until 24h passed. -* `max_attempts` defaults to `null`. By default client will try reconnecting until connected. Setting `max_attempts` -limits total amount of reconnects. Setting this to 0 will prevent any reconnect tries. +* `max_attempts` defaults to `0`. By default client will try reconnecting until connected. Setting `max_attempts` +limits total amount of connection tries. Setting this to 1 will prevent any reconnect tries. * `auth_pass` defaults to `null`. By default client will try connecting without auth. If set, client will run redis auth command on connect. * `family` defaults to `IPv4`. The client connects in IPv4 if not specified or if the DNS resolution returns an IPv4 address. You can force an IPv6 if you set the family to 'IPv6'. See nodejs net or dns modules how to use the family type. diff --git a/index.js b/index.js index e20577a684a..25ebb5c0157 100644 --- a/index.js +++ b/index.js @@ -52,10 +52,7 @@ function RedisClient(stream, options) { this.should_buffer = false; this.command_queue_high_water = this.options.command_queue_high_water || 1000; this.command_queue_low_water = this.options.command_queue_low_water || 0; - this.max_attempts = null; - if (options.max_attempts || options.max_attempts === 0 || options.max_attempts === '0') { - this.max_attempts = +options.max_attempts; - } + this.max_attempts = +options.max_attempts || 0; this.command_queue = new Queue(); // holds sent commands to de-pipeline them this.offline_queue = new Queue(); // holds commands issued but not able to be sent this.commands_sent = 0; @@ -418,16 +415,6 @@ RedisClient.prototype.connection_gone = function (why) { return; } - if (this.max_attempts !== null && this.attempts > this.max_attempts) { - this.emit('error', new Error("Redis connection in broken state: maximum connection attempts exceeded.")); - return; - } - - if (this.retry_totaltime >= this.connect_timeout) { - this.emit('error', new Error("Redis connection in broken state: connection timeout exceeded.")); - return; - } - debug("Redis connection is gone from " + why + " event."); this.connected = false; this.ready = false; @@ -450,16 +437,23 @@ RedisClient.prototype.connection_gone = function (why) { this.emitted_end = true; } - this.flush_and_error("Redis connection gone from " + why + " event."); - // If this is a requested shutdown, then don't retry if (this.closing) { - this.retry_timer = null; - debug("Connection ended from quit command, not retrying."); + debug("connection ended from quit command, not retrying."); + this.flush_and_error("Redis connection gone from " + why + " event."); + return; + } + + if (this.max_attempts !== 0 && this.attempts >= this.max_attempts || this.retry_totaltime >= this.connect_timeout) { + this.flush_and_error("Redis connection gone from " + why + " event."); + this.end(); + var message = this.retry_totaltime >= this.connect_timeout ? + 'connection timeout exceeded.' : + 'maximum connection attempts exceeded.'; + this.emit('error', new Error("Redis connection in broken state: " + message)); return; } - this.retry_delay = Math.floor(this.retry_delay * this.retry_backoff); if (this.retry_max_delay !== null && this.retry_delay > this.retry_max_delay) { this.retry_delay = this.retry_max_delay; } else if (this.retry_totaltime + this.retry_delay > this.connect_timeout) { @@ -479,6 +473,7 @@ RedisClient.prototype.connection_gone = function (why) { self.retry_totaltime += self.retry_delay; self.attempts += 1; + self.retry_delay = Math.round(self.retry_delay * self.retry_backoff); self.stream = net.createConnection(self.connectionOption); self.install_stream_listeners(); @@ -834,7 +829,7 @@ RedisClient.prototype.end = function () { //clear retry_timer if(this.retry_timer){ clearTimeout(this.retry_timer); - this.retry_timer=null; + this.retry_timer = null; } this.stream.on("error", function(){}); diff --git a/test/connection.spec.js b/test/connection.spec.js index d76a0599416..d29214ce1a1 100644 --- a/test/connection.spec.js +++ b/test/connection.spec.js @@ -11,7 +11,7 @@ describe("on lost connection", function () { describe("using " + parser + " and " + ip, function () { it("emit an error after max retry attempts and do not try to reconnect afterwards", function (done) { - var max_attempts = 3; + var max_attempts = 4; var client = redis.createClient({ parser: parser, max_attempts: max_attempts @@ -31,7 +31,7 @@ describe("on lost connection", function () { client.on('error', function(err) { if (/Redis connection in broken state: maximum connection attempts.*?exceeded./.test(err.message)) { setTimeout(function () { - assert.strictEqual(calls, max_attempts); + assert.strictEqual(calls, max_attempts - 1); done(); }, 1500); } @@ -40,7 +40,7 @@ describe("on lost connection", function () { it("emit an error after max retry timeout and do not try to reconnect afterwards", function (done) { var connect_timeout = 1000; // in ms - var client = redis.createClient({ + client = redis.createClient({ parser: parser, connect_timeout: connect_timeout }); diff --git a/test/node_redis.spec.js b/test/node_redis.spec.js index 9725cce67b3..deb79c3af8c 100644 --- a/test/node_redis.spec.js +++ b/test/node_redis.spec.js @@ -688,7 +688,7 @@ describe("The node_redis client", function () { describe('true', function () { it("does not return an error and enqueues operation", function (done) { var client = redis.createClient(9999, null, { - max_attempts: 0, + max_attempts: 1, parser: parser }); @@ -710,6 +710,34 @@ describe("The node_redis client", function () { }, 25); }, 50); }); + + it("enqueues operation and keep the queue while trying to reconnect", function (done) { + var client = redis.createClient(9999, null, { + max_attempts: 4, + parser: parser + }); + var i = 0; + + client.on('error', function(err) { + if (err.message === 'Redis connection in broken state: maximum connection attempts exceeded.') { + assert(i, 3); + assert.strictEqual(client.offline_queue.length, 0); + done(); + } + }); + + client.on('reconnecting', function(params) { + i++; + assert.equal(params.attempt, i); + assert.strictEqual(client.offline_queue.length, 1); + }); + + client.set('foo', 'bar', function(err, result) { + assert(i, 3); + assert('Redis connection gone from error event', err.message); + assert.strictEqual(client.offline_queue.length, 0); + }); + }); }); describe('false', function () { From 30ec1cd6a2095fc09893e2eaf154d83e8274abee Mon Sep 17 00:00:00 2001 From: Ruben Bridgewater Date: Sun, 13 Sep 2015 00:26:21 +0200 Subject: [PATCH 0348/1748] shift in the while loop --- .jshintrc | 2 +- index.js | 16 +++++++--------- 2 files changed, 8 insertions(+), 10 deletions(-) diff --git a/.jshintrc b/.jshintrc index c5caf777dd1..92441eb976c 100644 --- a/.jshintrc +++ b/.jshintrc @@ -17,7 +17,7 @@ "mocha": true, // Relaxing options - "boss": true, // Accept things like `while (command = keys.shift()) { ... }` + "boss": true, // Accept statements like `while (key = keys.pop()) {}` "overrides": { "examples/*.js": { diff --git a/index.js b/index.js index 25ebb5c0157..113c2141b2f 100644 --- a/index.js +++ b/index.js @@ -142,16 +142,14 @@ RedisClient.prototype.flush_and_error = function (message) { error = new Error(message); - while (this.offline_queue.length > 0) { - command_obj = this.offline_queue.shift(); + while (command_obj = this.offline_queue.shift()) { if (typeof command_obj.callback === "function") { command_obj.callback(error); } } this.offline_queue = new Queue(); - while (this.command_queue.length > 0) { - command_obj = this.command_queue.shift(); + while (command_obj = this.command_queue.shift()) { if (typeof command_obj.callback === "function") { command_obj.callback(error); } @@ -393,8 +391,8 @@ RedisClient.prototype.ready_check = function () { RedisClient.prototype.send_offline_queue = function () { var command_obj, buffered_writes = 0; - while (this.offline_queue.length > 0) { - command_obj = this.offline_queue.shift(); + // TODO: Implement queue.pop() as it should be faster than shift and evaluate petka antonovs queue + while (command_obj = this.offline_queue.shift()) { debug("Sending offline command: " + command_obj.command); buffered_writes += !this.send_command(command_obj.command, command_obj.args, command_obj.callback); } @@ -826,12 +824,12 @@ RedisClient.prototype.pub_sub_command = function (command_obj) { RedisClient.prototype.end = function () { this.stream._events = {}; - //clear retry_timer - if(this.retry_timer){ + // Clear retry_timer + if (this.retry_timer){ clearTimeout(this.retry_timer); this.retry_timer = null; } - this.stream.on("error", function(){}); + this.stream.on("error", function noop(){}); this.connected = false; this.ready = false; From 89c8dd056bdb548f6d667303f4f47f9777be7125 Mon Sep 17 00:00:00 2001 From: Ruben Bridgewater Date: Sun, 13 Sep 2015 00:27:50 +0200 Subject: [PATCH 0349/1748] Do not emit the broken mode twice if exec is called Add more tests --- index.js | 21 ++++++++++++--------- test/connection.spec.js | 27 ++++++++++++++++++++------- test/helper.js | 10 ++++++++++ test/node_redis.spec.js | 38 +++++++++++++++++++++++++++++++++++++- 4 files changed, 79 insertions(+), 17 deletions(-) diff --git a/index.js b/index.js index 113c2141b2f..400ac952859 100644 --- a/index.js +++ b/index.js @@ -137,10 +137,8 @@ RedisClient.prototype.unref = function () { }; // flush offline_queue and command_queue, erroring any items with a callback first -RedisClient.prototype.flush_and_error = function (message) { - var command_obj, error; - - error = new Error(message); +RedisClient.prototype.flush_and_error = function (error) { + var command_obj; while (command_obj = this.offline_queue.shift()) { if (typeof command_obj.callback === "function") { @@ -438,17 +436,19 @@ RedisClient.prototype.connection_gone = function (why) { // If this is a requested shutdown, then don't retry if (this.closing) { debug("connection ended from quit command, not retrying."); - this.flush_and_error("Redis connection gone from " + why + " event."); + this.flush_and_error(new Error("Redis connection gone from " + why + " event.")); return; } if (this.max_attempts !== 0 && this.attempts >= this.max_attempts || this.retry_totaltime >= this.connect_timeout) { - this.flush_and_error("Redis connection gone from " + why + " event."); - this.end(); var message = this.retry_totaltime >= this.connect_timeout ? 'connection timeout exceeded.' : 'maximum connection attempts exceeded.'; - this.emit('error', new Error("Redis connection in broken state: " + message)); + var error = new Error("Redis connection in broken state: " + message); + error.code = 'CONNECTION_BROKEN'; + this.flush_and_error(error); + this.emit('error', error); + this.end(); return; } @@ -1035,7 +1035,7 @@ Multi.prototype.exec = Multi.prototype.EXEC = function (callback) { // TODO - make this callback part of Multi.prototype instead of creating it each time return this._client.send_command("exec", [], function (err, replies) { - if (err) { + if (err && !err.code) { if (callback) { errors.push(err); callback(errors); @@ -1071,6 +1071,9 @@ Multi.prototype.exec = Multi.prototype.EXEC = function (callback) { if (callback) { callback(null, replies); + } else if (err && err.code !== 'CONNECTION_BROKEN') { + // Exclude CONNECTION_BROKEN so that error won't be emitted twice + self._client.emit('error', err); } }); }; diff --git a/test/connection.spec.js b/test/connection.spec.js index d29214ce1a1..a59ff8deaf5 100644 --- a/test/connection.spec.js +++ b/test/connection.spec.js @@ -19,9 +19,7 @@ describe("on lost connection", function () { var calls = 0; client.once('ready', function() { - // Pretend that redis can't reconnect - client.on_connect = client.on_error; - client.stream.destroy(); + helper.killConnection(client); }); client.on("reconnecting", function (params) { @@ -40,16 +38,14 @@ describe("on lost connection", function () { it("emit an error after max retry timeout and do not try to reconnect afterwards", function (done) { var connect_timeout = 1000; // in ms - client = redis.createClient({ + var client = redis.createClient({ parser: parser, connect_timeout: connect_timeout }); var time = 0; client.once('ready', function() { - // Pretend that redis can't reconnect - client.on_connect = client.on_error; - client.stream.destroy(); + helper.killConnection(client); }); client.on("reconnecting", function (params) { @@ -66,6 +62,23 @@ describe("on lost connection", function () { }); }); + it("end connection while retry is still ongoing", function (done) { + var connect_timeout = 1000; // in ms + var client = redis.createClient({ + parser: parser, + connect_timeout: connect_timeout + }); + + client.once('ready', function() { + helper.killConnection(client); + }); + + client.on("reconnecting", function (params) { + client.end(); + setTimeout(done, 100); + }); + }); + }); }); }); diff --git a/test/helper.js b/test/helper.js index 7e9fb5ff2a1..b57f31b3a12 100644 --- a/test/helper.js +++ b/test/helper.js @@ -145,5 +145,15 @@ module.exports = { func(); } }; + }, + killConnection: function (client) { + // Change the connection option to a non existing one and destroy the stream + client.connectionOption = { + port: 6370, + host: '127.0.0.2', + family: 4 + }; + client.address = '127.0.0.2:6370'; + client.stream.destroy(); } }; diff --git a/test/node_redis.spec.js b/test/node_redis.spec.js index deb79c3af8c..14d9913b170 100644 --- a/test/node_redis.spec.js +++ b/test/node_redis.spec.js @@ -729,9 +729,11 @@ describe("The node_redis client", function () { client.on('reconnecting', function(params) { i++; assert.equal(params.attempt, i); - assert.strictEqual(client.offline_queue.length, 1); + assert.strictEqual(client.offline_queue.length, 2); }); + // Should work with either a callback or without + client.set('baz', 13); client.set('foo', 'bar', function(err, result) { assert(i, 3); assert('Redis connection gone from error event', err.message); @@ -764,6 +766,40 @@ describe("The node_redis client", function () { }); }); }); + + it("flushes the command queue connection if in broken connection mode", function (done) { + var client = redis.createClient({ + parser: parser, + max_attempts: 2, + enable_offline_queue: false + }); + + client.once('ready', function() { + var multi = client.multi(); + multi.config("bar"); + var cb = function(err, reply) { + assert.equal(err.code, 'CONNECTION_BROKEN'); + }; + for (var i = 0; i < 10; i += 2) { + multi.set("foo" + i, "bar" + i); + multi.set("foo" + (i + 1), "bar" + (i + 1), cb); + } + multi.exec(); + assert.equal(client.command_queue.length, 13); + helper.killConnection(client); + }); + + client.on("reconnecting", function (params) { + assert.equal(client.command_queue.length, 13); + }); + + client.on('error', function(err) { + if (/Redis connection in broken state:/.test(err.message)) { + assert.equal(client.command_queue.length, 0); + done(); + } + }); + }); }); }); From a0c906256c80295a515ce7d1968b3d25c95d61e8 Mon Sep 17 00:00:00 2001 From: Ruben Bridgewater Date: Wed, 16 Sep 2015 01:17:18 +0200 Subject: [PATCH 0350/1748] Fix commands not being rejected after calling .quit as reported in #791 --- index.js | 33 +++++++++++--------- test/commands/dbsize.spec.js | 2 +- test/commands/flushdb.spec.js | 2 +- test/commands/get.spec.js | 2 +- test/commands/getset.spec.js | 2 +- test/commands/incr.spec.js | 2 +- test/commands/mset.spec.js | 2 +- test/commands/multi.spec.js | 2 +- test/commands/select.spec.js | 2 +- test/commands/set.spec.js | 2 +- test/node_redis.spec.js | 59 ++++++++++++++++++++++++++++++++--- 11 files changed, 81 insertions(+), 29 deletions(-) diff --git a/index.js b/index.js index 0011ef289cd..cf6385c0774 100644 --- a/index.js +++ b/index.js @@ -661,7 +661,7 @@ function Command(command, args, sub_command, buffer_args, callback) { } RedisClient.prototype.send_command = function (command, args, callback) { - var arg, command_obj, i, elem_count, buffer_args, stream = this.stream, command_str = "", buffered_writes = 0; + var arg, command_obj, i, elem_count, buffer_args, stream = this.stream, command_str = "", buffered_writes = 0, err; // if (typeof callback === "function") {} // probably the fastest way: @@ -698,7 +698,9 @@ RedisClient.prototype.send_command = function (command, args, callback) { return; } if (args[args.length - 1] === undefined || args[args.length - 1] === null) { - var err = new Error('send_command: ' + command + ' value must not be undefined or null'); + command = command.toUpperCase(); + err = new Error('send_command: ' + command + ' value must not be undefined or null'); + err.command_used = command; if (callback) { return callback && callback(err); } @@ -718,24 +720,25 @@ RedisClient.prototype.send_command = function (command, args, callback) { command_obj = new Command(command, args, false, buffer_args, callback); if (!this.ready && !this.send_anyway || !stream.writable) { - if (this.enable_offline_queue) { - if (!stream.writable) { - debug("send command: stream is not writeable."); + if (this.closing || !this.enable_offline_queue) { + command = command.toUpperCase(); + if (!this.closing) { + err = new Error(command + ' can\'t be processed. Stream not writeable and enable_offline_queue is deactivated.'); + } else { + err = new Error(command + ' can\'t be processed. The connection has already been closed.'); + } + err.command_used = command; + if (callback) { + callback(err); + } else { + this.emit('error', err); } + } else { debug("Queueing " + command + " for next server connection."); this.offline_queue.push(command_obj); this.should_buffer = true; - } else { - var not_writeable_error = new Error('send_command: stream not writeable. enable_offline_queue is false'); - if (command_obj.callback) { - command_obj.callback(not_writeable_error); - } else { - this.emit("error", not_writeable_error); - return; - } } - - return false; + return; } if (command === "subscribe" || command === "psubscribe" || command === "unsubscribe" || command === "punsubscribe") { diff --git a/test/commands/dbsize.spec.js b/test/commands/dbsize.spec.js index d3e8c07dafd..a3f4872f142 100644 --- a/test/commands/dbsize.spec.js +++ b/test/commands/dbsize.spec.js @@ -34,7 +34,7 @@ describe("The 'dbsize' method", function () { it("reports an error", function (done) { client.dbsize([], function (err, res) { - assert(err.message.match(/Redis connection gone/)); + assert(err.message.match(/The connection has already been closed/)); done(); }); }); diff --git a/test/commands/flushdb.spec.js b/test/commands/flushdb.spec.js index c6f3c83e19e..aeee2638f3f 100644 --- a/test/commands/flushdb.spec.js +++ b/test/commands/flushdb.spec.js @@ -34,7 +34,7 @@ describe("The 'flushdb' method", function () { it("reports an error", function (done) { client.flushdb(function (err, res) { - assert(err.message.match(/Redis connection gone/)); + assert(err.message.match(/The connection has already been closed/)); done(); }); }); diff --git a/test/commands/get.spec.js b/test/commands/get.spec.js index 014df57446e..780b224ff0b 100644 --- a/test/commands/get.spec.js +++ b/test/commands/get.spec.js @@ -34,7 +34,7 @@ describe("The 'get' method", function () { it("reports an error", function (done) { client.get(key, function (err, res) { - assert(err.message.match(/Redis connection gone/)); + assert(err.message.match(/The connection has already been closed/)); done(); }); }); diff --git a/test/commands/getset.spec.js b/test/commands/getset.spec.js index 41c2dc3a8ee..2f4c391f497 100644 --- a/test/commands/getset.spec.js +++ b/test/commands/getset.spec.js @@ -36,7 +36,7 @@ describe("The 'getset' method", function () { it("reports an error", function (done) { client.GET(key, redis.print); // Use the utility function to print the error client.get(key, function (err, res) { - assert(err.message.match(/Redis connection gone/)); + assert(err.message.match(/The connection has already been closed/)); done(); }); }); diff --git a/test/commands/incr.spec.js b/test/commands/incr.spec.js index 6b88312f013..eca68ced156 100644 --- a/test/commands/incr.spec.js +++ b/test/commands/incr.spec.js @@ -35,7 +35,7 @@ describe("The 'incr' method", function () { it("reports an error", function (done) { client.incr(function (err, res) { - assert(err.message.match(/Redis connection gone/)); + assert(err.message.match(/The connection has already been closed/)); done(); }); }); diff --git a/test/commands/mset.spec.js b/test/commands/mset.spec.js index 476cab5e557..27070dc8038 100644 --- a/test/commands/mset.spec.js +++ b/test/commands/mset.spec.js @@ -36,7 +36,7 @@ describe("The 'mset' method", function () { it("reports an error", function (done) { client.mset(key, value, key2, value2, function (err, res) { - assert(err.message.match(/Redis connection gone/)); + assert(err.message.match(/The connection has already been closed/)); done(); }); }); diff --git a/test/commands/multi.spec.js b/test/commands/multi.spec.js index 403ca02b77c..be1ff042992 100644 --- a/test/commands/multi.spec.js +++ b/test/commands/multi.spec.js @@ -35,7 +35,7 @@ describe("The 'multi' method", function () { it("reports an error", function (done) { client.multi(); client.exec(function (err, res) { - assert(err.message.match(/Redis connection gone/)); + assert(err.message.match(/The connection has already been closed/)); done(); }); }); diff --git a/test/commands/select.spec.js b/test/commands/select.spec.js index 32e5b0a27e1..4df653f26c9 100644 --- a/test/commands/select.spec.js +++ b/test/commands/select.spec.js @@ -26,7 +26,7 @@ describe("The 'select' method", function () { it("returns an error if redis is not connected", function (done) { client.select(1, function (err, res) { - assert(err.message.match(/Redis connection gone/)); + assert(err.message.match(/The connection has already been closed/)); done(); }); }); diff --git a/test/commands/set.spec.js b/test/commands/set.spec.js index 15e0346ed7e..ed0f6307a50 100644 --- a/test/commands/set.spec.js +++ b/test/commands/set.spec.js @@ -34,7 +34,7 @@ describe("The 'set' method", function () { it("reports an error", function (done) { client.set(key, value, function (err, res) { - assert(err.message.match(/Redis connection gone/)); + assert(err.message.match(/The connection has already been closed/)); done(); }); }); diff --git a/test/node_redis.spec.js b/test/node_redis.spec.js index bad74396873..b73cf4ed8cf 100644 --- a/test/node_redis.spec.js +++ b/test/node_redis.spec.js @@ -155,6 +155,54 @@ describe("The node_redis client", function () { }); + describe("commands after using .quit should fail", function () { + + it("return an error in the callback", function (done) { + if (helper.redisProcess().spawnFailed()) this.skip(); + + var client = redis.createClient(); + client.quit(function() { + client.get("foo", function(err, res) { + assert.strictEqual(err.message, 'Redis connection gone from end event.'); + assert.strictEqual(client.offline_queue.length, 0); + done(); + }); + }); + }); + + it("return an error in the callback version two", function (done) { + if (helper.redisProcess().spawnFailed()) this.skip(); + + var client = redis.createClient(); + client.quit(); + setTimeout(function() { + client.get("foo", function(err, res) { + assert.strictEqual(err.message, 'GET can\'t be processed. The connection has already been closed.'); + assert.strictEqual(err.command_used, 'GET'); + assert.strictEqual(client.offline_queue.length, 0); + done(); + }); + }, 100); + }); + + it("emit an error", function (done) { + if (helper.redisProcess().spawnFailed()) this.skip(); + + var client = redis.createClient(); + client.quit(); + client.on('error', function(err) { + assert.strictEqual(err.message, 'SET can\'t be processed. The connection has already been closed.'); + assert.strictEqual(err.command_used, 'SET'); + assert.strictEqual(client.offline_queue.length, 0); + done(); + }); + setTimeout(function() { + client.set('foo', 'bar'); + }, 50); + }); + + }); + describe("when redis closes unexpectedly", function () { it("reconnects and can retrieve the pre-existing data", function (done) { client.on("reconnecting", function on_recon(params) { @@ -712,15 +760,18 @@ describe("The node_redis client", function () { }); describe('false', function () { - it("does not emit an error and enqueues operation", function (done) { + it("does emit an error and does not enqueues operation", function (done) { var client = redis.createClient(9999, null, { parser: parser, max_attempts: 1, enable_offline_queue: false }); + var end = helper.callFuncAfter(done, 3); client.on('error', function(err) { - assert(/send_command: stream not writeable|ECONNREFUSED/.test(err.message)); + assert(/Stream not writeable|ECONNREFUSED/.test(err.message)); + assert.equal(client.command_queue.length, 0); + end(); }); client.set('foo', 'bar'); @@ -729,9 +780,7 @@ describe("The node_redis client", function () { client.set('foo', 'bar', function (err) { // should callback with an error assert.ok(err); - setTimeout(function () { - return done(); - }, 50); + setTimeout(end, 50); }); }); }); From 95a2c373a227e6e3528bfb59facf35d39f603a5c Mon Sep 17 00:00:00 2001 From: Ruben Bridgewater Date: Wed, 16 Sep 2015 05:58:29 +0200 Subject: [PATCH 0351/1748] Fix broken build. The merge had a small conflict --- test/node_redis.spec.js | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/test/node_redis.spec.js b/test/node_redis.spec.js index cd9e5dba425..e650dbac8f1 100644 --- a/test/node_redis.spec.js +++ b/test/node_redis.spec.js @@ -736,7 +736,7 @@ describe("The node_redis client", function () { describe('true', function () { it("does not return an error and enqueues operation", function (done) { var client = redis.createClient(9999, null, { - max_attempts: 1, + max_attempts: 0, parser: parser }); From 9e2c665d5cceb306321f7ade028393037273df2d Mon Sep 17 00:00:00 2001 From: Ruben Bridgewater Date: Mon, 14 Sep 2015 15:21:51 +0200 Subject: [PATCH 0352/1748] Refactor exec --- index.js | 146 +++++++++++++++++++++++++++---------------------------- 1 file changed, 72 insertions(+), 74 deletions(-) diff --git a/index.js b/index.js index 93e0fe52f3d..010e4a8c592 100644 --- a/index.js +++ b/index.js @@ -579,15 +579,17 @@ RedisClient.prototype.return_reply = function (reply) { if (command_obj && !command_obj.sub_command) { if (typeof command_obj.callback === "function") { - if (this.options.detect_buffers && command_obj.buffer_args === false && 'exec' !== command_obj.command) { - // If detect_buffers option was specified, then the reply from the parser will be Buffers. - // If this command did not use Buffer arguments, then convert the reply to Strings here. - reply = reply_to_strings(reply); - } + if ('exec' !== command_obj.command) { + if (this.options.detect_buffers && command_obj.buffer_args === false) { + // If detect_buffers option was specified, then the reply from the parser will be Buffers. + // If this command did not use Buffer arguments, then convert the reply to Strings here. + reply = reply_to_strings(reply); + } - // TODO - confusing and error-prone that hgetall is special cased in two places - if (reply && 'hgetall' === command_obj.command) { - reply = reply_to_object(reply); + // TODO - confusing and error-prone that hgetall is special cased in two places + if (reply && 'hgetall' === command_obj.command) { + reply = reply_to_object(reply); + } } command_obj.callback(null, reply); @@ -993,92 +995,88 @@ Multi.prototype.hmset = Multi.prototype.HMSET = function (key, args, callback) { return this; }; +Multi.prototype.send_command = function (command, args, cb) { + var self = this; + this._client.send_command(command, args, function (err, reply) { + if (err) { + if (cb) { + cb(err); + } else { + self.errors.push(err); + } + } + }); +}; + Multi.prototype.exec = Multi.prototype.EXEC = function (callback) { var self = this; - var errors = []; - var wants_buffers = []; + this.errors = []; + this.callback = callback; + this.wants_buffers = new Array(this.queue.length); // drain queue, callback will catch "QUEUED" or error - // TODO - get rid of all of these anonymous functions which are elegant but slow - this.queue.forEach(function (args, index) { - var command = args[0], obj, i, il, buffer_args; + for (var index = 0; index < this.queue.length; index++) { + var args = this.queue[index].slice(); + var command = args.shift(); + var cb; if (typeof args[args.length - 1] === "function") { - args = args.slice(1, -1); - } else { - args = args.slice(1); + cb = args.pop(); } // Keep track of who wants buffer responses: - buffer_args = false; - for (i = 0, il = args.length; i < il; i += 1) { + this.wants_buffers[index] = false; + for (var i = 0; i < args.length; i += 1) { if (Buffer.isBuffer(args[i])) { - buffer_args = true; + this.wants_buffers[index] = true; + break; } } - wants_buffers.push(buffer_args); - if (args.length === 1 && Array.isArray(args[0])) { - args = args[0]; - } - if (command === 'hmset' && typeof args[1] === 'object') { - obj = args.pop(); - Object.keys(obj).forEach(function (key) { - args.push(key); - args.push(obj[key]); - }); - } - this._client.send_command(command, args, function (err, reply) { - if (err) { - var cur = self.queue[index]; - if (typeof cur[cur.length - 1] === "function") { - cur[cur.length - 1](err); - } else { - errors.push(err); - } - } - }); - }, this); + this.send_command(command, args, cb); + } - // TODO - make this callback part of Multi.prototype instead of creating it each time - return this._client.send_command("exec", [], function (err, replies) { - if (err && !err.code) { - if (callback) { - errors.push(err); - callback(errors); - return; + this._client.send_command('exec', [], function(err, replies) { + self.execute_callback(err, replies); + }); +}; + +Multi.prototype.execute_callback = function (err, replies) { + var i, reply, args; + + if (err) { + if (err.code !== 'CONNECTION_BROKEN') { + if (this.callback) { + this.errors.push(err); + this.callback(this.errors); } else { - self._client.emit('error', err); + // Exclude CONNECTION_BROKEN so that error won't be emitted twice + this._client.emit('error', err); } } + return; + } - var i, il, reply, to_buffer, args; - - if (replies) { - for (i = 1, il = self.queue.length; i < il; i += 1) { - reply = replies[i - 1]; - args = self.queue[i]; - to_buffer = wants_buffers[i]; + if (replies) { + for (i = 1; i < this.queue.length; i += 1) { + reply = replies[i - 1]; + args = this.queue[i]; - // If we asked for strings, even in detect_buffers mode, then return strings: - if (self._client.options.detect_buffers && to_buffer === false) { - replies[i - 1] = reply = reply_to_strings(reply); - } + // If we asked for strings, even in detect_buffers mode, then return strings: + if (this._client.options.detect_buffers && this.wants_buffers[i] === false) { + replies[i - 1] = reply = reply_to_strings(reply); + } - // TODO - confusing and error-prone that hgetall is special cased in two places - if (reply && args[0] === "hgetall") { - replies[i - 1] = reply = reply_to_object(reply); - } + // TODO - confusing and error-prone that hgetall is special cased in two places + if (reply && args[0] === "hgetall") { + replies[i - 1] = reply = reply_to_object(reply); + } - if (typeof args[args.length - 1] === "function") { - args[args.length - 1](null, reply); - } + if (typeof args[args.length - 1] === "function") { + args[args.length - 1](null, reply); } } + } - if (callback) { - callback(null, replies); - } else if (err && err.code !== 'CONNECTION_BROKEN') { - // Exclude CONNECTION_BROKEN so that error won't be emitted twice - self._client.emit('error', err); - } - }); + if (this.callback) { + this.callback(null, replies); + } }; RedisClient.prototype.multi = RedisClient.prototype.MULTI = function (args) { From 21d8bdbbcb1c3c86e511e31918b4c8382675b395 Mon Sep 17 00:00:00 2001 From: Ruben Bridgewater Date: Tue, 8 Sep 2015 01:36:25 +0200 Subject: [PATCH 0353/1748] Refactor multi to have a consistent error handling Ignore *.log files --- .npmignore | 1 + index.js | 35 ++++++++++++++----------- test/commands/multi.spec.js | 51 ++++++++++++++++++++++++++++++++----- test/node_redis.spec.js | 15 +++++++++++ 4 files changed, 80 insertions(+), 22 deletions(-) diff --git a/.npmignore b/.npmignore index 4df6949973f..886d9eab914 100644 --- a/.npmignore +++ b/.npmignore @@ -6,3 +6,4 @@ generate_commands.js multi_bench.js test-unref.js changelog.md +*.log \ No newline at end of file diff --git a/index.js b/index.js index 010e4a8c592..6d21170edef 100644 --- a/index.js +++ b/index.js @@ -389,7 +389,6 @@ RedisClient.prototype.ready_check = function () { RedisClient.prototype.send_offline_queue = function () { var command_obj, buffered_writes = 0; - // TODO: Implement queue.pop() as it should be faster than shift and evaluate petka antonovs queue while (command_obj = this.offline_queue.shift()) { debug("Sending offline command: " + command_obj.command); buffered_writes += !this.send_command(command_obj.command, command_obj.args, command_obj.callback); @@ -995,15 +994,15 @@ Multi.prototype.hmset = Multi.prototype.HMSET = function (key, args, callback) { return this; }; -Multi.prototype.send_command = function (command, args, cb) { +Multi.prototype.send_command = function (command, args, index, cb) { var self = this; this._client.send_command(command, args, function (err, reply) { if (err) { if (cb) { cb(err); - } else { - self.errors.push(err); } + err.position = index - 1; + self.errors.push(err); } }); }; @@ -1029,7 +1028,7 @@ Multi.prototype.exec = Multi.prototype.EXEC = function (callback) { break; } } - this.send_command(command, args, cb); + this.send_command(command, args, index, cb); } this._client.send_command('exec', [], function(err, replies) { @@ -1042,9 +1041,10 @@ Multi.prototype.execute_callback = function (err, replies) { if (err) { if (err.code !== 'CONNECTION_BROKEN') { + err.code = 'EXECABORT'; + err.errors = this.errors; if (this.callback) { - this.errors.push(err); - this.callback(this.errors); + this.callback(err); } else { // Exclude CONNECTION_BROKEN so that error won't be emitted twice this._client.emit('error', err); @@ -1059,17 +1059,22 @@ Multi.prototype.execute_callback = function (err, replies) { args = this.queue[i]; // If we asked for strings, even in detect_buffers mode, then return strings: - if (this._client.options.detect_buffers && this.wants_buffers[i] === false) { - replies[i - 1] = reply = reply_to_strings(reply); - } - - // TODO - confusing and error-prone that hgetall is special cased in two places - if (reply && args[0] === "hgetall") { - replies[i - 1] = reply = reply_to_object(reply); + if (reply) { + if (this._client.options.detect_buffers && this.wants_buffers[i] === false) { + replies[i - 1] = reply = reply_to_strings(reply); + } + if (args[0] === "hgetall") { + // TODO - confusing and error-prone that hgetall is special cased in two places + replies[i - 1] = reply = reply_to_object(reply); + } } if (typeof args[args.length - 1] === "function") { - args[args.length - 1](null, reply); + if (reply instanceof Error) { + args[args.length - 1](reply); + } else { + args[args.length - 1](null, reply); + } } } } diff --git a/test/commands/multi.spec.js b/test/commands/multi.spec.js index be1ff042992..d2cfd097a3f 100644 --- a/test/commands/multi.spec.js +++ b/test/commands/multi.spec.js @@ -249,18 +249,55 @@ describe("The 'multi' method", function () { }); }); - it('reports multiple exceptions when they occur', function (done) { - helper.serverVersionAtLeast.call(this, client, [2, 6, 5]); + it('reports EXECABORT exceptions when they occur (while queueing)', function (done) { + client.multi().config("bar").set("foo").exec(function (err, reply) { + assert.equal(err.code, "EXECABORT"); + assert.equal(reply, undefined, "The reply should have been discarded"); + assert(err.message.match(/^EXECABORT/), "Error message should begin with EXECABORT"); + assert.equal(err.errors.length, 1, "err.errors should have 1 items"); + assert.strictEqual(err.errors[0].command_used, 'SET'); + assert.strictEqual(err.errors[0].position, 1); + assert(/^ERR/.test(err.errors[0].message), "Actuall error message should begin with ERR"); + return done(); + }); + }); - client.multi().set("foo").exec(function (err, reply) { - assert(Array.isArray(err), "err should be an array"); - assert.equal(2, err.length, "err should have 2 items"); - assert(err[0].message.match(/^ERR/), "First error message should begin with ERR"); - assert(err[1].message.match(/^EXECABORT/), "First error message should begin with EXECABORT"); + it('reports multiple exceptions when they occur (while EXEC is running)', function (done) { + client.multi().config("bar").debug("foo").exec(function (err, reply) { + assert.strictEqual(reply.length, 2); + assert(/^ERR/.test(reply[0].message), "Error message should begin with ERR"); + assert(/^ERR/.test(reply[1].message), "Error message should begin with ERR"); return done(); }); }); + it('reports multiple exceptions when they occur (while EXEC is running) and calls cb', function (done) { + var multi = client.multi(); + multi.config("bar", helper.isError()); + multi.set('foo', 'bar', helper.isString('OK')); + multi.debug("foo").exec(function (err, reply) { + assert.strictEqual(reply.length, 3); + assert(/^ERR/.test(reply[0].message), "Error message should begin with ERR"); + assert(/^ERR/.test(reply[2].message), "Error message should begin with ERR"); + assert.strictEqual(reply[1], "OK"); + client.get('foo', helper.isString('bar', done)); + }); + }); + + it("emits an error if no callback has been provided and execabort error occured", function (done) { + helper.serverVersionAtLeast.call(this, client, [2, 6, 5]); + + var multi = client.multi(); + multi.config("bar"); + multi.set("foo"); + multi.exec(); + + client.on('error', function(err) { + assert.equal(err.code, "EXECABORT"); + done(); + }); + }); + }); }); }); diff --git a/test/node_redis.spec.js b/test/node_redis.spec.js index e650dbac8f1..821dc170b6d 100644 --- a/test/node_redis.spec.js +++ b/test/node_redis.spec.js @@ -77,6 +77,21 @@ describe("The node_redis client", function () { }); }); + it("connects correctly to localhost and no ready check", function (done) { + client = redis.createClient(undefined, undefined, { + no_ready_check: true + }); + client.on("error", done); + + client.once("ready", function () { + client.set('foo', 'bar'); + client.get('foo', function(err, res) { + assert.strictEqual(res, 'bar'); + done(err); + }); + }); + }); + it("throws on strange connection info", function () { try { redis.createClient(true); From 8b7d4a84496feaab49f9896de57e6373f0727a73 Mon Sep 17 00:00:00 2001 From: Ruben Bridgewater Date: Thu, 17 Sep 2015 22:51:42 +0200 Subject: [PATCH 0354/1748] Remove bad .eval implementation The implementation is not really good as mentioned in #722 and we pipline our commands. That way we can't just replace the eval function as it was. This could result in violating the order of execution! If we want to include a function like this we should not break the order of execution and also not recalculate the sha1 hash each time. --- index.js | 32 -------------------------------- test/commands/eval.spec.js | 29 ++++++++++++++++++++--------- 2 files changed, 20 insertions(+), 41 deletions(-) diff --git a/index.js b/index.js index 010e4a8c592..be91dc3fc3f 100644 --- a/index.js +++ b/index.js @@ -6,7 +6,6 @@ var net = require("net"), Queue = require("./lib/queue"), to_array = require("./lib/to_array"), events = require("events"), - crypto = require("crypto"), parsers = [], // This static list of commands is updated from time to time. // ./lib/commands.js can be updated with generate_commands.js @@ -1083,37 +1082,6 @@ RedisClient.prototype.multi = RedisClient.prototype.MULTI = function (args) { return new Multi(this, args); }; -// stash original eval method -var eval_orig = RedisClient.prototype.eval; -// hook eval with an attempt to evalsha for cached scripts -RedisClient.prototype.eval = RedisClient.prototype.EVAL = function () { - var self = this, - args = to_array(arguments), - callback; - - if (typeof args[args.length - 1] === "function") { - callback = args.pop(); - } - - if (Array.isArray(args[0])) { - args = args[0]; - } - - // replace script source with sha value - var source = args[0]; - args[0] = crypto.createHash("sha1").update(source).digest("hex"); - - self.evalsha(args, function (err, reply) { - if (err && /NOSCRIPT/.test(err.message)) { - args[0] = source; - eval_orig.call(self, args, callback); - - } else if (callback) { - callback(err, reply); - } - }); -}; - var createClient_unix = function(path, options){ var cnxOptions = { path: path diff --git a/test/commands/eval.spec.js b/test/commands/eval.spec.js index df075f3d292..d11fc509113 100644 --- a/test/commands/eval.spec.js +++ b/test/commands/eval.spec.js @@ -12,6 +12,7 @@ describe("The 'eval' method", function () { describe("using " + parser + " and " + ip, function () { var client; + var source = "return redis.call('set', 'sha', 'test')"; beforeEach(function (done) { client = redis.createClient.apply(redis.createClient, args); @@ -94,24 +95,25 @@ describe("The 'eval' method", function () { }); }); + it('allows a script to be executed that accesses the redis API without callback', function (done) { + helper.serverVersionAtLeast.call(this, client, [2, 5, 0]); + client.eval(source, 0); + client.get('sha', helper.isString('test', done)); + }); + describe('evalsha', function () { - var source = "return redis.call('get', 'sha test')"; var sha = crypto.createHash('sha1').update(source).digest('hex'); - beforeEach(function (done) { - client.set("sha test", "eval get sha test", function (err, res) { - return done(err); - }); - }); - it('allows a script to be executed that accesses the redis API', function (done) { helper.serverVersionAtLeast.call(this, client, [2, 5, 0]); - client.eval(source, 0, helper.isString('eval get sha test', done)); + client.eval(source, 0, helper.isString('OK')); + client.get('sha', helper.isString('test', done)); }); it('can execute a script if the SHA exists', function (done) { helper.serverVersionAtLeast.call(this, client, [2, 5, 0]); - client.evalsha(sha, 0, helper.isString('eval get sha test', done)); + client.evalsha(sha, 0, helper.isString('OK')); + client.get('sha', helper.isString('test', done)); }); it('returns an error if SHA does not exist', function (done) { @@ -119,6 +121,15 @@ describe("The 'eval' method", function () { client.evalsha('ffffffffffffffffffffffffffffffffffffffff', 0, helper.isError(done)); }); + it('emit an error if SHA does not exist without any callback', function (done) { + helper.serverVersionAtLeast.call(this, client, [2, 5, 0]); + client.evalsha('ffffffffffffffffffffffffffffffffffffffff', 0); + client.on('error', function(err) { + assert(/NOSCRIPT No matching script. Please use EVAL./.test(err.message)); + done(); + }); + }); + it('emits an error if SHA does not exist and no callback has been provided', function (done) { client.on('error', function (err) { assert.equal(err.message, 'NOSCRIPT No matching script. Please use EVAL.'); From d61d97e24ed40da38604e4f5b8a83726ba884cf8 Mon Sep 17 00:00:00 2001 From: Ruben Bridgewater Date: Thu, 17 Sep 2015 23:32:28 +0200 Subject: [PATCH 0355/1748] Fix .auth not working properly The arguments parameter was faulty andthe callback could have been triggered twice --- index.js | 27 +++++++++++---------------- test/auth.spec.js | 12 ++++++++++++ 2 files changed, 23 insertions(+), 16 deletions(-) diff --git a/index.js b/index.js index 010e4a8c592..73a2be997cf 100644 --- a/index.js +++ b/index.js @@ -50,38 +50,30 @@ function RedisClient(stream, options) { this.options.socket_keepalive = true; } this.should_buffer = false; - this.command_queue_high_water = this.options.command_queue_high_water || 1000; - this.command_queue_low_water = this.options.command_queue_low_water || 0; + this.command_queue_high_water = options.command_queue_high_water || 1000; + this.command_queue_low_water = options.command_queue_low_water || 0; this.max_attempts = +options.max_attempts || 0; this.command_queue = new Queue(); // holds sent commands to de-pipeline them this.offline_queue = new Queue(); // holds commands issued but not able to be sent this.commands_sent = 0; this.connect_timeout = +options.connect_timeout || 86400000; // 24 * 60 * 60 * 1000 ms this.enable_offline_queue = true; - if (this.options.enable_offline_queue === false) { + if (options.enable_offline_queue === false) { this.enable_offline_queue = false; } - this.retry_max_delay = null; - if (options.retry_max_delay && options.retry_max_delay > 0) { - this.retry_max_delay = +options.retry_max_delay; - } + this.retry_max_delay = +options.retry_max_delay || null; this.initialize_retry_vars(); this.pub_sub_mode = false; this.subscription_set = {}; this.monitoring = false; this.closing = false; this.server_info = {}; - this.auth_pass = null; - if (options.auth_pass !== undefined) { - this.auth_pass = options.auth_pass; - } + this.auth_pass = options.auth_pass; this.parser_module = null; - this.selected_db = null; // save the selected db here, used when reconnecting - + this.selected_db = null; // save the selected db here, used when reconnecting this.old_state = null; this.install_stream_listeners(); - events.EventEmitter.call(this); } util.inherits(RedisClient, events.EventEmitter); @@ -199,6 +191,7 @@ RedisClient.prototype.do_auth = function () { } else if (self.auth_callback) { self.auth_callback(err); self.auth_callback = null; + return; } else { self.emit("error", err); return; @@ -928,10 +921,12 @@ RedisClient.prototype.auth = RedisClient.prototype.AUTH = function (pass, callba return; } this.auth_pass = pass; - this.auth_callback = callback; debug("Saving auth as " + this.auth_pass); + // Only run the callback once. So do not safe it if already connected if (this.connected) { - this.send_command("auth", pass, callback); + this.send_command("auth", [this.auth_pass], callback); + } else { + this.auth_callback = callback; } }; diff --git a/test/auth.spec.js b/test/auth.spec.js index d29b605f270..f1270059266 100644 --- a/test/auth.spec.js +++ b/test/auth.spec.js @@ -147,6 +147,18 @@ describe("client authentication", function () { }); client.auth(234567); }); + + it('allows auth to be provided post-hoc with auth method again', function (done) { + if (helper.redisProcess().spawnFailed()) this.skip(); + + var args = config.configureClient(parser, ip, { + auth_pass: auth + }); + client = redis.createClient.apply(redis.createClient, args); + client.on("ready", function () { + client.auth(auth, helper.isString('OK', done)); + }); + }); }); }); From 46e2dc2de52b8f90555845a8b5d146822aa5feef Mon Sep 17 00:00:00 2001 From: Ruben Bridgewater Date: Fri, 18 Sep 2015 00:56:17 +0200 Subject: [PATCH 0356/1748] Fix memory leak. See #723 and thx to @rahar --- index.js | 38 ++++++++++++++++++------------------- test/commands/multi.spec.js | 4 ++-- 2 files changed, 21 insertions(+), 21 deletions(-) diff --git a/index.js b/index.js index c8a8b1a9819..e94745ed485 100644 --- a/index.js +++ b/index.js @@ -394,9 +394,25 @@ RedisClient.prototype.send_offline_queue = function () { } }; -RedisClient.prototype.connection_gone = function (why) { - var self = this; +var retry_connection = function (self) { + debug("Retrying connection..."); + + self.emit("reconnecting", { + delay: self.retry_delay, + attempt: self.attempts + }); + + self.retry_totaltime += self.retry_delay; + self.attempts += 1; + self.retry_delay = Math.round(self.retry_delay * self.retry_backoff); + + self.stream = net.createConnection(self.connectionOption); + self.install_stream_listeners(); + + self.retry_timer = null; +}; +RedisClient.prototype.connection_gone = function (why) { // If a retry is already in progress, just let that happen if (this.retry_timer) { return; @@ -452,23 +468,7 @@ RedisClient.prototype.connection_gone = function (why) { debug("Retry connection in " + this.retry_delay + " ms"); - this.retry_timer = setTimeout(function () { - debug("Retrying connection..."); - - self.emit("reconnecting", { - delay: self.retry_delay, - attempt: self.attempts - }); - - self.retry_totaltime += self.retry_delay; - self.attempts += 1; - self.retry_delay = Math.round(self.retry_delay * self.retry_backoff); - - self.stream = net.createConnection(self.connectionOption); - self.install_stream_listeners(); - - self.retry_timer = null; - }, this.retry_delay); + this.retry_timer = setTimeout(retry_connection, this.retry_delay, this); }; RedisClient.prototype.on_data = function (data) { diff --git a/test/commands/multi.spec.js b/test/commands/multi.spec.js index d2cfd097a3f..a18478b3dec 100644 --- a/test/commands/multi.spec.js +++ b/test/commands/multi.spec.js @@ -250,11 +250,11 @@ describe("The 'multi' method", function () { }); it('reports EXECABORT exceptions when they occur (while queueing)', function (done) { - client.multi().config("bar").set("foo").exec(function (err, reply) { + client.multi().config("bar").set("foo").set("bar").exec(function (err, reply) { assert.equal(err.code, "EXECABORT"); assert.equal(reply, undefined, "The reply should have been discarded"); assert(err.message.match(/^EXECABORT/), "Error message should begin with EXECABORT"); - assert.equal(err.errors.length, 1, "err.errors should have 1 items"); + assert.equal(err.errors.length, 2, "err.errors should have 2 items"); assert.strictEqual(err.errors[0].command_used, 'SET'); assert.strictEqual(err.errors[0].position, 1); assert(/^ERR/.test(err.errors[0].message), "Actuall error message should begin with ERR"); From 28f31f134c8b82640e197b40fdf360f97946c4bd Mon Sep 17 00:00:00 2001 From: Ruben Bridgewater Date: Fri, 18 Sep 2015 01:50:26 +0200 Subject: [PATCH 0357/1748] Improve server keyspace info in .server_info --- index.js | 28 ++++++++++++++++++++++------ test/node_redis.spec.js | 2 ++ 2 files changed, 24 insertions(+), 6 deletions(-) diff --git a/index.js b/index.js index e94745ed485..2661c19292c 100644 --- a/index.js +++ b/index.js @@ -325,20 +325,23 @@ RedisClient.prototype.on_ready = function () { }; RedisClient.prototype.on_info_cmd = function (err, res) { - var self = this, obj = {}, lines, retry_time; + var self = this; + var obj = {}; + var lines = res.toString().split("\r\n"); + var i = 0; + var key = 'db' + i; + var line, retry_time, parts, sub_parts; if (err) { return self.emit("error", new Error("Ready check failed: " + err.message)); } - lines = res.toString().split("\r\n"); - - lines.forEach(function (line) { - var parts = line.split(':'); + for (i = 0; i < lines.length; i++) { + parts = lines[i].split(':'); if (parts[1]) { obj[parts[0]] = parts[1]; } - }); + } obj.versions = []; /* istanbul ignore else: some redis servers do not send the version */ @@ -348,6 +351,19 @@ RedisClient.prototype.on_info_cmd = function (err, res) { }); } + while (obj[key]) { + parts = obj[key].split(','); + obj[key] = {}; + while (line = parts.pop()) { + sub_parts = line.split('='); + if (sub_parts[1]) { + obj[key][sub_parts[0]] = +sub_parts[1]; + } + } + i++; + key = 'db' + i; + } + // expose info key/vals to users this.server_info = obj; diff --git a/test/node_redis.spec.js b/test/node_redis.spec.js index 821dc170b6d..35e8ed5292d 100644 --- a/test/node_redis.spec.js +++ b/test/node_redis.spec.js @@ -227,6 +227,8 @@ describe("The node_redis client", function () { var end = helper.callFuncAfter(function () { client.removeListener("connect", on_connect); client.removeListener("reconnecting", on_recon); + assert.strictEqual(client.server_info.db0.keys, 2); + assert.strictEqual(Object.keys(client.server_info.db0).length, 3); done(); }, 4); client.get("recon 1", helper.isString("one", end)); From 97db227a8d2098f82472b08e107e02532e73616d Mon Sep 17 00:00:00 2001 From: pbihler Date: Mon, 8 Dec 2014 13:02:09 +0100 Subject: [PATCH 0358/1748] Fix for channel names with spaces. Fixes #691 Channel names with spaces were not properly resubscribed after a reconnection. Conflicts: index.js --- index.js | 3 ++- test/pubsub.spec.js | 12 ++++++++++-- 2 files changed, 12 insertions(+), 3 deletions(-) diff --git a/index.js b/index.js index 2661c19292c..45b20823ab4 100644 --- a/index.js +++ b/index.js @@ -308,7 +308,8 @@ RedisClient.prototype.on_ready = function () { } }; Object.keys(this.subscription_set).forEach(function (key) { - var parts = key.split(" "); + var space_index = key.indexOf(" "); + var parts = [key.slice(0, space_index), key.slice(space_index + 1)]; debug("Sending pub/sub on_ready " + parts[0] + ", " + parts[1]); callback_count++; self.send_command(parts[0] + "scribe", [parts[1]], callback); diff --git a/test/pubsub.spec.js b/test/pubsub.spec.js index 09cdac4f7f4..4cf801758dd 100644 --- a/test/pubsub.spec.js +++ b/test/pubsub.spec.js @@ -43,14 +43,22 @@ describe("publish/subscribe", function () { }); describe('subscribe', function () { - it('fires a subscribe event for each channel subscribed to', function (done) { + it('fires a subscribe event for each channel subscribed to even after reconnecting', function (done) { + var a = false; sub.on("subscribe", function (chnl, count) { if (chnl === channel2) { assert.equal(2, count); - return done(); + if (a) { + return done(); + } + sub.stream.destroy(); } }); + sub.on('reconnecting', function() { + a = true; + }); + sub.subscribe(channel, channel2); }); From 2c67ce528ec23957616a23ac9c9fc75764010240 Mon Sep 17 00:00:00 2001 From: Ben Vibhagool Date: Thu, 17 Sep 2015 20:48:42 -0700 Subject: [PATCH 0359/1748] Fixed typo in README.md --- README.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/README.md b/README.md index a29c6ae1342..3b40c4cf9d2 100644 --- a/README.md +++ b/README.md @@ -195,7 +195,7 @@ with an error, or an error will be thrown if no callback is specified. reconnection (delay) almost doubles. This delay normally grows infinitely, but setting `retry_max_delay` limits delay to maximum value, provided in milliseconds. * `connect_timeout` defaults to `86400000`. Setting `connect_timeout` limits total time for client to reconnect. -Value is provided in milliseconds and is counted once the disconnect occured. The last retry is going to happen exactly at the timeout time. +Value is provided in milliseconds and is counted once the disconnect occurred. The last retry is going to happen exactly at the timeout time. That way the default is to try reconnecting until 24h passed. * `max_attempts` defaults to `0`. By default client will try reconnecting until connected. Setting `max_attempts` limits total amount of connection tries. Setting this to 1 will prevent any reconnect tries. From a159412b02f74e6364484b79317b534303c0fb2a Mon Sep 17 00:00:00 2001 From: Corey Farwell Date: Fri, 18 Sep 2015 11:02:13 -0400 Subject: [PATCH 0360/1748] Remove unnecessary line from example This line doesn't seem necessary nor relevant to this example --- README.md | 1 - 1 file changed, 1 deletion(-) diff --git a/README.md b/README.md index 3b40c4cf9d2..29db8b0a24d 100644 --- a/README.md +++ b/README.md @@ -349,7 +349,6 @@ client1.on("message", function (channel, message) { } }); -client1.incr("did a thing"); client1.subscribe("a nice channel"); ``` From 621c51122935b9e4d3bfcdfe8c7ba7b421adb426 Mon Sep 17 00:00:00 2001 From: Ruben Bridgewater Date: Thu, 17 Sep 2015 20:26:07 +0200 Subject: [PATCH 0361/1748] Return parser data async --- index.js | 39 ++++++++++++++++++++------------------- 1 file changed, 20 insertions(+), 19 deletions(-) diff --git a/index.js b/index.js index 45b20823ab4..4e88b40228c 100644 --- a/index.js +++ b/index.js @@ -86,7 +86,9 @@ RedisClient.prototype.install_stream_listeners = function() { }); this.stream.on("data", function (buffer_from_socket) { - self.on_data(buffer_from_socket); + // The data.toString() has a significant impact on big chunks and therefor this should only be used if necessary + // debug("Net read " + this.address + " id " + this.connection_id + ": " + data.toString()); + self.reply_parser.execute(buffer_from_socket); }); this.stream.on("error", function (msg) { @@ -273,8 +275,18 @@ RedisClient.prototype.init_parser = function () { this.reply_parser = new this.parser_module.Parser({ return_buffers: self.options.return_buffers || self.options.detect_buffers || false }); - this.reply_parser.send_error = this.return_error.bind(self); - this.reply_parser.send_reply = this.return_reply.bind(self); + // Important: Only send results / errors async. + // That way the result / error won't stay in a try catch block and catch user things + this.reply_parser.send_error = function (data) { + process.nextTick(function() { + this.return_error(data); + }.bind(this)); + }.bind(this); + this.reply_parser.send_reply = function (data) { + process.nextTick(function() { + this.return_reply(data); + }.bind(this)); + }.bind(this); }; RedisClient.prototype.on_ready = function () { @@ -488,33 +500,22 @@ RedisClient.prototype.connection_gone = function (why) { this.retry_timer = setTimeout(retry_connection, this.retry_delay, this); }; -RedisClient.prototype.on_data = function (data) { - // The data.toString() has a significant impact on big chunks and therefor this should only be used if necessary - // debug("Net read " + this.address + " id " + this.connection_id + ": " + data.toString()); - - try { - this.reply_parser.execute(data); - } catch (err) { - // This is an unexpected parser problem, an exception that came from the parser code itself. - // Parser should emit "error" events if it notices things are out of whack. - // Callbacks that throw exceptions will land in return_reply(), below. - // TODO - it might be nice to have a different "error" event for different types of errors - this.emit("error", err); - } -}; - RedisClient.prototype.return_error = function (err) { var command_obj = this.command_queue.shift(), queue_len = this.command_queue.length; - err.command_used = command_obj.command.toUpperCase(); + if (command_obj.command && command_obj.command.toUpperCase) { + err.command_used = command_obj.command.toUpperCase(); + } if (this.pub_sub_mode === false && queue_len === 0) { this.command_queue = new Queue(); this.emit("idle"); } + if (this.should_buffer && queue_len <= this.command_queue_low_water) { this.emit("drain"); this.should_buffer = false; } + if (command_obj.callback) { command_obj.callback(err); } else { From f543d45d1f0da0c12bfebae56b8df4a2f605d06c Mon Sep 17 00:00:00 2001 From: Ruben Bridgewater Date: Thu, 17 Sep 2015 05:16:36 +0200 Subject: [PATCH 0362/1748] Fix and add more tests --- test/commands/blpop.spec.js | 1 - test/commands/client.spec.js | 1 - test/commands/dbsize.spec.js | 2 -- test/commands/del.spec.js | 1 - test/commands/eval.spec.js | 1 - test/commands/exits.spec.js | 1 - test/commands/expire.spec.js | 1 - test/commands/flushdb.spec.js | 2 -- test/commands/get.spec.js | 2 -- test/commands/getset.spec.js | 2 -- test/commands/hgetall.spec.js | 2 -- test/commands/hincrby.spec.js | 1 - test/commands/hlen.spec.js | 1 - test/commands/hmset.spec.js | 1 - test/commands/hset.spec.js | 1 - test/commands/incr.spec.js | 1 - test/commands/keys.spec.js | 1 - test/commands/mset.spec.js | 2 -- test/commands/msetnx.spec.js | 1 - test/commands/multi.spec.js | 13 +++++++++++-- test/commands/randomkey.test.js | 1 - test/commands/rename.spec.js | 1 - test/commands/renamenx.spec.js | 1 - test/commands/rpush.spec.js | 1 - test/commands/sadd.spec.js | 1 - test/commands/scard.spec.js | 1 - test/commands/script.spec.js | 1 - test/commands/sdiff.spec.js | 1 - test/commands/sdiffstore.spec.js | 1 - test/commands/select.spec.js | 2 -- test/commands/set.spec.js | 2 -- test/commands/setex.spec.js | 1 - test/commands/setnx.spec.js | 1 - test/commands/sinter.spec.js | 1 - test/commands/sinterstore.spec.js | 1 - test/commands/sismember.spec.js | 1 - test/commands/slowlog.spec.js | 1 - test/commands/smembers.spec.js | 1 - test/commands/smove.spec.js | 1 - test/commands/spop.spec.js | 1 - test/commands/srem.spec.js | 1 - test/commands/sunion.spec.js | 1 - test/commands/sunionstore.spec.js | 1 - test/commands/ttl.spec.js | 1 - test/commands/type.spec.js | 1 - test/commands/watch.spec.js | 1 - test/node_redis.spec.js | 5 +++-- 47 files changed, 14 insertions(+), 57 deletions(-) diff --git a/test/commands/blpop.spec.js b/test/commands/blpop.spec.js index 7f1aeb19471..ea9dfdf21cd 100644 --- a/test/commands/blpop.spec.js +++ b/test/commands/blpop.spec.js @@ -15,7 +15,6 @@ describe("The 'blpop' method", function () { beforeEach(function (done) { client = redis.createClient.apply(redis.createClient, args); - client.once("error", done); client.once("connect", function () { client.flushdb(done); }); diff --git a/test/commands/client.spec.js b/test/commands/client.spec.js index 5636df976c3..5707d1ab895 100644 --- a/test/commands/client.spec.js +++ b/test/commands/client.spec.js @@ -15,7 +15,6 @@ describe("The 'client' method", function () { beforeEach(function (done) { client = redis.createClient.apply(redis.createClient, args); - client.once("error", done); client.once("connect", function () { client.flushdb(done); }); diff --git a/test/commands/dbsize.spec.js b/test/commands/dbsize.spec.js index a3f4872f142..7bc03219b97 100644 --- a/test/commands/dbsize.spec.js +++ b/test/commands/dbsize.spec.js @@ -23,7 +23,6 @@ describe("The 'dbsize' method", function () { beforeEach(function (done) { client = redis.createClient.apply(redis.createClient, args); - client.once("error", done); client.once("connect", function () { client.quit(); }); @@ -45,7 +44,6 @@ describe("The 'dbsize' method", function () { beforeEach(function (done) { client = redis.createClient.apply(redis.createClient, args); - client.once("error", done); client.once("connect", function () { client.flushdb(function (err, res) { helper.isString("OK")(err, res); diff --git a/test/commands/del.spec.js b/test/commands/del.spec.js index 69f3930c29c..0adb6327d8d 100644 --- a/test/commands/del.spec.js +++ b/test/commands/del.spec.js @@ -13,7 +13,6 @@ describe("The 'del' method", function () { beforeEach(function (done) { client = redis.createClient.apply(redis.createClient, args); - client.once("error", done); client.once("connect", function () { client.flushdb(done); }); diff --git a/test/commands/eval.spec.js b/test/commands/eval.spec.js index d11fc509113..a22098e3338 100644 --- a/test/commands/eval.spec.js +++ b/test/commands/eval.spec.js @@ -16,7 +16,6 @@ describe("The 'eval' method", function () { beforeEach(function (done) { client = redis.createClient.apply(redis.createClient, args); - client.once("error", done); client.once("connect", function () { client.flushdb(done); }); diff --git a/test/commands/exits.spec.js b/test/commands/exits.spec.js index 1d6d778b397..061e3671335 100644 --- a/test/commands/exits.spec.js +++ b/test/commands/exits.spec.js @@ -13,7 +13,6 @@ describe("The 'exits' method", function () { beforeEach(function (done) { client = redis.createClient.apply(redis.createClient, args); - client.once("error", done); client.once("connect", function () { client.flushdb(done); }); diff --git a/test/commands/expire.spec.js b/test/commands/expire.spec.js index b0480b6c1d8..1e3d9073e92 100644 --- a/test/commands/expire.spec.js +++ b/test/commands/expire.spec.js @@ -13,7 +13,6 @@ describe("The 'expire' method", function () { beforeEach(function (done) { client = redis.createClient.apply(redis.createClient, args); - client.once("error", done); client.once("connect", function () { client.flushdb(done); }); diff --git a/test/commands/flushdb.spec.js b/test/commands/flushdb.spec.js index aeee2638f3f..12f10de8e8f 100644 --- a/test/commands/flushdb.spec.js +++ b/test/commands/flushdb.spec.js @@ -23,7 +23,6 @@ describe("The 'flushdb' method", function () { beforeEach(function (done) { client = redis.createClient.apply(redis.createClient, args); - client.once("error", done); client.once("connect", function () { client.quit(); }); @@ -45,7 +44,6 @@ describe("The 'flushdb' method", function () { beforeEach(function (done) { client = redis.createClient.apply(redis.createClient, args); - client.once("error", done); client.once("connect", function () { done(); }); diff --git a/test/commands/get.spec.js b/test/commands/get.spec.js index 780b224ff0b..973629945da 100644 --- a/test/commands/get.spec.js +++ b/test/commands/get.spec.js @@ -23,7 +23,6 @@ describe("The 'get' method", function () { beforeEach(function (done) { client = redis.createClient.apply(redis.createClient, args); - client.once("error", done); client.once("connect", function () { client.quit(); }); @@ -45,7 +44,6 @@ describe("The 'get' method", function () { beforeEach(function (done) { client = redis.createClient.apply(redis.createClient, args); - client.once("error", done); client.once("connect", function () { done(); }); diff --git a/test/commands/getset.spec.js b/test/commands/getset.spec.js index 2f4c391f497..e35ec7e6167 100644 --- a/test/commands/getset.spec.js +++ b/test/commands/getset.spec.js @@ -24,7 +24,6 @@ describe("The 'getset' method", function () { beforeEach(function (done) { client = redis.createClient.apply(redis.createClient, args); - client.once("error", done); client.once("connect", function () { client.quit(); }); @@ -47,7 +46,6 @@ describe("The 'getset' method", function () { beforeEach(function (done) { client = redis.createClient.apply(redis.createClient, args); - client.once("error", done); client.once("connect", function () { done(); }); diff --git a/test/commands/hgetall.spec.js b/test/commands/hgetall.spec.js index 958e717c8c2..faac08d08ef 100644 --- a/test/commands/hgetall.spec.js +++ b/test/commands/hgetall.spec.js @@ -16,7 +16,6 @@ describe("The 'hgetall' method", function () { beforeEach(function (done) { client = redis.createClient.apply(redis.createClient, args); - client.once("error", done); client.once("connect", function () { client.flushdb(done); }); @@ -58,7 +57,6 @@ describe("The 'hgetall' method", function () { beforeEach(function (done) { client = redis.createClient.apply(redis.createClient, args); - client.once("error", done); client.once("connect", function () { client.flushdb(done); }); diff --git a/test/commands/hincrby.spec.js b/test/commands/hincrby.spec.js index 7d55f11c06d..43a73826e35 100644 --- a/test/commands/hincrby.spec.js +++ b/test/commands/hincrby.spec.js @@ -14,7 +14,6 @@ describe("The 'hincrby' method", function () { beforeEach(function (done) { client = redis.createClient.apply(redis.createClient, args); - client.once("error", done); client.once("connect", function () { client.flushdb(done); }); diff --git a/test/commands/hlen.spec.js b/test/commands/hlen.spec.js index 564f248ed5d..3d6d675b965 100644 --- a/test/commands/hlen.spec.js +++ b/test/commands/hlen.spec.js @@ -13,7 +13,6 @@ describe("The 'hlen' method", function () { beforeEach(function (done) { client = redis.createClient.apply(redis.createClient, args); - client.once("error", done); client.once("connect", function () { client.flushdb(done); }); diff --git a/test/commands/hmset.spec.js b/test/commands/hmset.spec.js index 8846b9e9d73..6060677e0f6 100644 --- a/test/commands/hmset.spec.js +++ b/test/commands/hmset.spec.js @@ -15,7 +15,6 @@ describe("The 'hmset' method", function () { beforeEach(function (done) { client = redis.createClient.apply(redis.createClient, args); - client.once("error", done); client.once("connect", function () { client.flushdb(done); }); diff --git a/test/commands/hset.spec.js b/test/commands/hset.spec.js index 0424a97fd50..66ff79086ce 100644 --- a/test/commands/hset.spec.js +++ b/test/commands/hset.spec.js @@ -15,7 +15,6 @@ describe("The 'hset' method", function () { beforeEach(function (done) { client = redis.createClient.apply(redis.createClient, args); - client.once("error", done); client.once("connect", function () { client.flushdb(done); }); diff --git a/test/commands/incr.spec.js b/test/commands/incr.spec.js index eca68ced156..cdf5fecac31 100644 --- a/test/commands/incr.spec.js +++ b/test/commands/incr.spec.js @@ -17,7 +17,6 @@ describe("The 'incr' method", function () { beforeEach(function (done) { client = redis.createClient.apply(redis.createClient, args); - client.once("error", done); client.once("connect", function () { client.set(key, "9007199254740992", function (err, res) { helper.isNotError()(err, res); diff --git a/test/commands/keys.spec.js b/test/commands/keys.spec.js index b7a3660f47f..60c1e84404c 100644 --- a/test/commands/keys.spec.js +++ b/test/commands/keys.spec.js @@ -15,7 +15,6 @@ describe("The 'keys' method", function () { beforeEach(function (done) { client = redis.createClient.apply(redis.createClient, args); - client.once("error", done); client.once("connect", function () { client.flushdb(done); }); diff --git a/test/commands/mset.spec.js b/test/commands/mset.spec.js index 27070dc8038..be2e3a4baf0 100644 --- a/test/commands/mset.spec.js +++ b/test/commands/mset.spec.js @@ -25,7 +25,6 @@ describe("The 'mset' method", function () { beforeEach(function (done) { client = redis.createClient.apply(redis.createClient, args); - client.once("error", done); client.once("connect", function () { client.quit(); }); @@ -47,7 +46,6 @@ describe("The 'mset' method", function () { beforeEach(function (done) { client = redis.createClient.apply(redis.createClient, args); - client.once("error", done); client.once("connect", function () { done(); }); diff --git a/test/commands/msetnx.spec.js b/test/commands/msetnx.spec.js index 1ac77e18945..7af7fbc27a6 100644 --- a/test/commands/msetnx.spec.js +++ b/test/commands/msetnx.spec.js @@ -13,7 +13,6 @@ describe("The 'msetnx' method", function () { beforeEach(function (done) { client = redis.createClient.apply(redis.createClient, args); - client.once("error", done); client.once("connect", function () { client.flushdb(done); }); diff --git a/test/commands/multi.spec.js b/test/commands/multi.spec.js index a18478b3dec..60ac2e07fa7 100644 --- a/test/commands/multi.spec.js +++ b/test/commands/multi.spec.js @@ -23,7 +23,6 @@ describe("The 'multi' method", function () { beforeEach(function (done) { client = redis.createClient.apply(redis.createClient, args); - client.once("error", done); client.once("connect", function () { client.quit(); }); @@ -46,7 +45,6 @@ describe("The 'multi' method", function () { beforeEach(function (done) { client = redis.createClient.apply(redis.createClient, args); - client.once("error", done); client.once("connect", function () { client.flushdb(function (err) { return done(err); @@ -298,6 +296,17 @@ describe("The 'multi' method", function () { }); }); + it("should work without any callback", function (done) { + helper.serverVersionAtLeast.call(this, client, [2, 6, 5]); + + var multi = client.multi(); + multi.set("baz", "binary"); + multi.set("foo", "bar"); + multi.exec(); + + client.get('foo', helper.isString('bar', done)); + }); + }); }); }); diff --git a/test/commands/randomkey.test.js b/test/commands/randomkey.test.js index 4c81ed2edb9..85e61ef5976 100644 --- a/test/commands/randomkey.test.js +++ b/test/commands/randomkey.test.js @@ -14,7 +14,6 @@ describe("The 'randomkey' method", function () { beforeEach(function (done) { client = redis.createClient.apply(redis.createClient, args); - client.once("error", done); client.once("connect", function () { client.flushdb(done); }); diff --git a/test/commands/rename.spec.js b/test/commands/rename.spec.js index d506bec46cc..02434cc9234 100644 --- a/test/commands/rename.spec.js +++ b/test/commands/rename.spec.js @@ -13,7 +13,6 @@ describe("The 'rename' method", function () { beforeEach(function (done) { client = redis.createClient.apply(redis.createClient, args); - client.once("error", done); client.once("connect", function () { client.flushdb(done); }); diff --git a/test/commands/renamenx.spec.js b/test/commands/renamenx.spec.js index dc2224ee58c..e2fbdcfd034 100644 --- a/test/commands/renamenx.spec.js +++ b/test/commands/renamenx.spec.js @@ -13,7 +13,6 @@ describe("The 'renamenx' method", function () { beforeEach(function (done) { client = redis.createClient.apply(redis.createClient, args); - client.once("error", done); client.once("connect", function () { client.flushdb(done); }); diff --git a/test/commands/rpush.spec.js b/test/commands/rpush.spec.js index 5c46c7dc797..ba3cb56415b 100644 --- a/test/commands/rpush.spec.js +++ b/test/commands/rpush.spec.js @@ -14,7 +14,6 @@ describe("The 'rpush' command", function () { beforeEach(function (done) { client = redis.createClient.apply(redis.createClient, args); - client.once("error", done); client.once("connect", function () { client.flushdb(done); }); diff --git a/test/commands/sadd.spec.js b/test/commands/sadd.spec.js index 6c92dfe2010..dcc5e1cd420 100644 --- a/test/commands/sadd.spec.js +++ b/test/commands/sadd.spec.js @@ -14,7 +14,6 @@ describe("The 'sadd' method", function () { beforeEach(function (done) { client = redis.createClient.apply(redis.createClient, args); - client.once("error", done); client.once("connect", function () { client.flushdb(done); }); diff --git a/test/commands/scard.spec.js b/test/commands/scard.spec.js index 423cba98057..8c366e73835 100644 --- a/test/commands/scard.spec.js +++ b/test/commands/scard.spec.js @@ -13,7 +13,6 @@ describe("The 'scard' method", function () { beforeEach(function (done) { client = redis.createClient.apply(redis.createClient, args); - client.once("error", done); client.once("connect", function () { client.flushdb(done); }); diff --git a/test/commands/script.spec.js b/test/commands/script.spec.js index 29dfaa63dda..19d77084b89 100644 --- a/test/commands/script.spec.js +++ b/test/commands/script.spec.js @@ -17,7 +17,6 @@ describe("The 'script' method", function () { beforeEach(function (done) { client = redis.createClient.apply(redis.createClient, args); - client.once("error", done); client.once("connect", function () { client.flushdb(done); }); diff --git a/test/commands/sdiff.spec.js b/test/commands/sdiff.spec.js index cb2a97f14ba..43c6d1434ee 100644 --- a/test/commands/sdiff.spec.js +++ b/test/commands/sdiff.spec.js @@ -14,7 +14,6 @@ describe("The 'sdiff' method", function () { beforeEach(function (done) { client = redis.createClient.apply(redis.createClient, args); - client.once("error", done); client.once("connect", function () { client.flushdb(done); }); diff --git a/test/commands/sdiffstore.spec.js b/test/commands/sdiffstore.spec.js index 7546c446ce5..52abb487e4d 100644 --- a/test/commands/sdiffstore.spec.js +++ b/test/commands/sdiffstore.spec.js @@ -14,7 +14,6 @@ describe("The 'sdiffstore' method", function () { beforeEach(function (done) { client = redis.createClient.apply(redis.createClient, args); - client.once("error", done); client.once("connect", function () { client.flushdb(done); }); diff --git a/test/commands/select.spec.js b/test/commands/select.spec.js index 4df653f26c9..1ad7de94c1d 100644 --- a/test/commands/select.spec.js +++ b/test/commands/select.spec.js @@ -15,7 +15,6 @@ describe("The 'select' method", function () { beforeEach(function (done) { client = redis.createClient.apply(redis.createClient, args); - client.once("error", done); client.once("connect", function () { client.quit(); }); @@ -37,7 +36,6 @@ describe("The 'select' method", function () { beforeEach(function (done) { client = redis.createClient.apply(redis.createClient, args); - client.once("error", done); client.once("connect", function () { done(); }); }); diff --git a/test/commands/set.spec.js b/test/commands/set.spec.js index ed0f6307a50..048450d5b48 100644 --- a/test/commands/set.spec.js +++ b/test/commands/set.spec.js @@ -23,7 +23,6 @@ describe("The 'set' method", function () { beforeEach(function (done) { client = redis.createClient.apply(redis.createClient, args); - client.once("error", done); client.once("connect", function () { client.quit(); }); @@ -45,7 +44,6 @@ describe("The 'set' method", function () { beforeEach(function (done) { client = redis.createClient.apply(redis.createClient, args); - client.once("error", done); client.once("connect", function () { done(); }); diff --git a/test/commands/setex.spec.js b/test/commands/setex.spec.js index 334f92cdbf0..5ad1fd1b41d 100644 --- a/test/commands/setex.spec.js +++ b/test/commands/setex.spec.js @@ -14,7 +14,6 @@ describe("The 'setex' method", function () { beforeEach(function (done) { client = redis.createClient.apply(redis.createClient, args); - client.once("error", done); client.once("connect", function () { client.flushdb(done); }); diff --git a/test/commands/setnx.spec.js b/test/commands/setnx.spec.js index 0092a33fac2..5d9f358dc7f 100644 --- a/test/commands/setnx.spec.js +++ b/test/commands/setnx.spec.js @@ -13,7 +13,6 @@ describe("The 'setnx' method", function () { beforeEach(function (done) { client = redis.createClient.apply(redis.createClient, args); - client.once("error", done); client.once("connect", function () { client.flushdb(done); }); diff --git a/test/commands/sinter.spec.js b/test/commands/sinter.spec.js index e03973755c9..b7df78c9951 100644 --- a/test/commands/sinter.spec.js +++ b/test/commands/sinter.spec.js @@ -14,7 +14,6 @@ describe("The 'sinter' method", function () { beforeEach(function (done) { client = redis.createClient.apply(redis.createClient, args); - client.once("error", done); client.once("connect", function () { client.flushdb(done); }); diff --git a/test/commands/sinterstore.spec.js b/test/commands/sinterstore.spec.js index 38a9ffc39b5..f789c42dfb8 100644 --- a/test/commands/sinterstore.spec.js +++ b/test/commands/sinterstore.spec.js @@ -14,7 +14,6 @@ describe("The 'sinterstore' method", function () { beforeEach(function (done) { client = redis.createClient.apply(redis.createClient, args); - client.once("error", done); client.once("connect", function () { client.flushdb(done); }); diff --git a/test/commands/sismember.spec.js b/test/commands/sismember.spec.js index d5e4a554f8e..a7ee9ad0e00 100644 --- a/test/commands/sismember.spec.js +++ b/test/commands/sismember.spec.js @@ -13,7 +13,6 @@ describe("The 'sismember' method", function () { beforeEach(function (done) { client = redis.createClient.apply(redis.createClient, args); - client.once("error", done); client.once("connect", function () { client.flushdb(done); }); diff --git a/test/commands/slowlog.spec.js b/test/commands/slowlog.spec.js index 846694d54d6..eee3fa1d870 100644 --- a/test/commands/slowlog.spec.js +++ b/test/commands/slowlog.spec.js @@ -14,7 +14,6 @@ describe("The 'slowlog' method", function () { beforeEach(function (done) { client = redis.createClient.apply(redis.createClient, args); - client.once("error", done); client.once("connect", function () { client.flushdb(done); }); diff --git a/test/commands/smembers.spec.js b/test/commands/smembers.spec.js index acca8f899de..32078ef7bdc 100644 --- a/test/commands/smembers.spec.js +++ b/test/commands/smembers.spec.js @@ -14,7 +14,6 @@ describe("The 'smembers' method", function () { beforeEach(function (done) { client = redis.createClient.apply(redis.createClient, args); - client.once("error", done); client.once("connect", function () { client.flushdb(done); }); diff --git a/test/commands/smove.spec.js b/test/commands/smove.spec.js index 4b65b150cd5..e16e6191190 100644 --- a/test/commands/smove.spec.js +++ b/test/commands/smove.spec.js @@ -13,7 +13,6 @@ describe("The 'smove' method", function () { beforeEach(function (done) { client = redis.createClient.apply(redis.createClient, args); - client.once("error", done); client.once("connect", function () { client.flushdb(done); }); diff --git a/test/commands/spop.spec.js b/test/commands/spop.spec.js index 0beb4869a1d..2df75dd6535 100644 --- a/test/commands/spop.spec.js +++ b/test/commands/spop.spec.js @@ -14,7 +14,6 @@ describe("The 'spop' method", function () { beforeEach(function (done) { client = redis.createClient.apply(redis.createClient, args); - client.once("error", done); client.once("connect", function () { client.flushdb(done); }); diff --git a/test/commands/srem.spec.js b/test/commands/srem.spec.js index d6a64a006ea..c075a987aa9 100644 --- a/test/commands/srem.spec.js +++ b/test/commands/srem.spec.js @@ -14,7 +14,6 @@ describe("The 'srem' method", function () { beforeEach(function (done) { client = redis.createClient.apply(redis.createClient, args); - client.once("error", done); client.once("connect", function () { client.flushdb(done); }); diff --git a/test/commands/sunion.spec.js b/test/commands/sunion.spec.js index 2477526d0ba..6fb1bb03322 100644 --- a/test/commands/sunion.spec.js +++ b/test/commands/sunion.spec.js @@ -14,7 +14,6 @@ describe("The 'sunion' method", function () { beforeEach(function (done) { client = redis.createClient.apply(redis.createClient, args); - client.once("error", done); client.once("connect", function () { client.flushdb(done); }); diff --git a/test/commands/sunionstore.spec.js b/test/commands/sunionstore.spec.js index a4086de6a35..c6e9cdeb607 100644 --- a/test/commands/sunionstore.spec.js +++ b/test/commands/sunionstore.spec.js @@ -14,7 +14,6 @@ describe("The 'sunionstore' method", function () { beforeEach(function (done) { client = redis.createClient.apply(redis.createClient, args); - client.once("error", done); client.once("connect", function () { client.flushdb(done); }); diff --git a/test/commands/ttl.spec.js b/test/commands/ttl.spec.js index 5e1f56150f7..edc2db5c106 100644 --- a/test/commands/ttl.spec.js +++ b/test/commands/ttl.spec.js @@ -14,7 +14,6 @@ describe("The 'ttl' method", function () { beforeEach(function (done) { client = redis.createClient.apply(redis.createClient, args); - client.once("error", done); client.once("connect", function () { client.flushdb(done); }); diff --git a/test/commands/type.spec.js b/test/commands/type.spec.js index e9ec7b95f37..9c99da0fbd3 100644 --- a/test/commands/type.spec.js +++ b/test/commands/type.spec.js @@ -13,7 +13,6 @@ describe("The 'type' method", function () { beforeEach(function (done) { client = redis.createClient.apply(redis.createClient, args); - client.once("error", done); client.once("connect", function () { client.flushdb(done); }); diff --git a/test/commands/watch.spec.js b/test/commands/watch.spec.js index 5354525c306..133fc770cad 100644 --- a/test/commands/watch.spec.js +++ b/test/commands/watch.spec.js @@ -16,7 +16,6 @@ describe("The 'watch' method", function () { beforeEach(function (done) { client = redis.createClient.apply(redis.createClient, args); - client.once("error", done); client.once("connect", function () { client.flushdb(done); }); diff --git a/test/node_redis.spec.js b/test/node_redis.spec.js index 35e8ed5292d..1883837a72e 100644 --- a/test/node_redis.spec.js +++ b/test/node_redis.spec.js @@ -156,7 +156,7 @@ describe("The node_redis client", function () { process.once('uncaughtException', function (err) { process.on('uncaughtException', mochaListener); - assert(/is not a function|toUpperCase/.test(err)); + assert(/ERR Protocol error/.test(err)); done(); }); @@ -359,7 +359,8 @@ describe("The node_redis client", function () { }); - it('emits errors thrown from within an on("message") handler', function (done) { + // This seems to be a broken test. The exception should be handled by the user, not by node_redis + it.skip('emits errors thrown from within an on("message") handler', function (done) { var client2 = redis.createClient.apply(redis.createClient, args); var name = 'channel'; From 5a2b54fd2f83825f3f4db59b5dc89989173d04a8 Mon Sep 17 00:00:00 2001 From: Ruben Bridgewater Date: Tue, 8 Sep 2015 02:06:28 +0200 Subject: [PATCH 0363/1748] Remove dead code from js parser --- lib/parser/javascript.js | 41 +++++++++------------------------------- 1 file changed, 9 insertions(+), 32 deletions(-) diff --git a/lib/parser/javascript.js b/lib/parser/javascript.js index bdb75788414..a6ad8892fad 100644 --- a/lib/parser/javascript.js +++ b/lib/parser/javascript.js @@ -55,19 +55,13 @@ ReplyParser.prototype._parseResult = function (type) { } if (type === 45) { - var result = this._buffer.toString(this._encoding, start, end); - return new Error(result); - } - - if (this.options.return_buffers) { + return new Error(this._buffer.toString(this._encoding, start, end)); + } else if (this.options.return_buffers) { return this._buffer.slice(start, end); - } else { - if (end - start < 65536) { // completely arbitrary - return small_toString(this._buffer, start, end); - } else { - return this._buffer.toString(this._encoding, start, end); - } + } else if (end - start < 65536) { // completely arbitrary + return small_toString(this._buffer, start, end); } + return this._buffer.toString(this._encoding, start, end); } else if (type === 58) { // : // up to the delimiter end = this._packetEndOffset() - 1; @@ -92,7 +86,7 @@ ReplyParser.prototype._parseResult = function (type) { // packets with a size of -1 are considered null if (packetHeader.size === -1) { - return undefined; + return null; } end = this._offset + packetHeader.size; @@ -108,10 +102,9 @@ ReplyParser.prototype._parseResult = function (type) { if (this.options.return_buffers) { return this._buffer.slice(start, end); - } else { - return this._buffer.toString(this._encoding, start, end); } - } else if (type === 42) { // * + return this._buffer.toString(this._encoding, start, end); + } else { // * offset = this._offset; packetHeader = new Packet(type, this.parseHeader()); @@ -136,9 +129,6 @@ ReplyParser.prototype._parseResult = function (type) { throw new IncompleteReadBuffer("Wait for more data."); } res = this._parseResult(ntype); - if (res === undefined) { - res = null; - } reply.push(res); } @@ -176,18 +166,8 @@ ReplyParser.prototype.execute = function (buffer) { } else if (type === 36) { // $ ret = this._parseResult(type); - if (ret === null) { - break; - } - - // check the state for what is the result of - // a -1, set it back up for a null reply - if (ret === undefined) { - ret = null; - } - this.send_reply(ret); - } else if (type === 42) { // * + } else if (type === 42) { // 42 * // set a rewind point. if a failure occurs, // wait for the next execute()/append() and try again offset = this._offset - 1; @@ -199,9 +179,6 @@ ReplyParser.prototype.execute = function (buffer) { } catch (err) { // catch the error (not enough data), rewind, and wait // for the next packet to appear - if (! (err instanceof IncompleteReadBuffer)) { - throw err; - } this._offset = offset; break; } From 26e57642147234b13021e9d6aa6d8d5048a329eb Mon Sep 17 00:00:00 2001 From: Ruben Bridgewater Date: Thu, 17 Sep 2015 22:48:12 +0200 Subject: [PATCH 0364/1748] Remove broken test --- test/node_redis.spec.js | 24 ------------------------ 1 file changed, 24 deletions(-) diff --git a/test/node_redis.spec.js b/test/node_redis.spec.js index 1883837a72e..cfdb8f6b25e 100644 --- a/test/node_redis.spec.js +++ b/test/node_redis.spec.js @@ -359,30 +359,6 @@ describe("The node_redis client", function () { }); - // This seems to be a broken test. The exception should be handled by the user, not by node_redis - it.skip('emits errors thrown from within an on("message") handler', function (done) { - var client2 = redis.createClient.apply(redis.createClient, args); - var name = 'channel'; - - client2.subscribe(name, function () { - client.publish(name, "some message"); - }); - - client2.on("message", function (channel, data) { - if (channel === name) { - assert.equal(data, "some message"); - throw Error('forced exception'); - } - return done(); - }); - - client2.once("error", function (err) { - client2.end(); - assert.equal(err.message, 'forced exception'); - return done(); - }); - }); - describe('idle', function () { it('emits idle as soon as there are no outstanding commands', function (done) { client.on('idle', function onIdle () { From 083e446d23f10ac7064d4bb222c47ce87ade36cc Mon Sep 17 00:00:00 2001 From: Ruben Bridgewater Date: Sat, 19 Sep 2015 17:40:09 +0200 Subject: [PATCH 0365/1748] Fix parser regression. Out of memory resulted in an endless loop --- lib/parser/javascript.js | 5 +++++ test/parser/javascript.spec.js | 34 ++++++++++++++++++++++++++++++++++ 2 files changed, 39 insertions(+) diff --git a/lib/parser/javascript.js b/lib/parser/javascript.js index a6ad8892fad..d979ec3cbef 100644 --- a/lib/parser/javascript.js +++ b/lib/parser/javascript.js @@ -220,6 +220,11 @@ ReplyParser.prototype._packetEndOffset = function () { while (this._buffer[offset] !== 0x0d && this._buffer[offset + 1] !== 0x0a) { offset++; + + /* istanbul ignore if: activate the js parser out of memory test to test this */ + if (offset >= this._buffer.length) { + throw new IncompleteReadBuffer("didn't see LF after NL reading multi bulk count (" + offset + " => " + this._buffer.length + ", " + this._offset + ")"); + } } offset++; diff --git a/test/parser/javascript.spec.js b/test/parser/javascript.spec.js index 693ad45f524..19df991675b 100644 --- a/test/parser/javascript.spec.js +++ b/test/parser/javascript.spec.js @@ -2,6 +2,8 @@ var assert = require('assert'); var Parser = require("../../lib/parser/javascript").Parser; +var config = require("../lib/config"); +var redis = config.redis; describe('javascript parser', function () { it('handles multi-bulk reply', function (done) { @@ -24,4 +26,36 @@ describe('javascript parser', function () { assert.equal(reply_count, 3, "check reply should have been called three times"); return done(); }); + + // Activate this if you want to fry your cpu / memory + describe.skip("test out of memory", function () { + var args = config.configureClient('javascript', '127.0.0.1'); + var clients = new Array(300).join(" ").split(" "); + var client; + beforeEach(function (done) { + client = redis.createClient.apply(redis.createClient, args); + client.once("connect", function () { + client.flushdb(done); + }); + }); + + it('reach limit and wait for further data', function (done) { + setTimeout(done, 5000); + clients.forEach(function(entry, a) { + var max = 0; + var client = redis.createClient.apply(redis.createClient, args); + client.on('ready', function() { + while (++max < 50) { + var item = []; + for (var i = 0; i < 100; ++i) { + item.push('aaa' + (Math.random() * 1000000 | 0)); + } + client.del('foo' + a); + client.lpush('foo' + a, item); + client.lrange('foo' + a, 0, 99); + } + }); + }); + }); + }); }); From 40c037eaf44084fbdf586ad3ca545c564a877e19 Mon Sep 17 00:00:00 2001 From: Ruben Bridgewater Date: Fri, 18 Sep 2015 06:28:14 +0200 Subject: [PATCH 0366/1748] Add redis error codes to the errors --- index.js | 7 +++++++ test/commands/eval.spec.js | 6 +++++- test/commands/keys.spec.js | 3 +++ 3 files changed, 15 insertions(+), 1 deletion(-) diff --git a/index.js b/index.js index 4e88b40228c..bd71db18666 100644 --- a/index.js +++ b/index.js @@ -500,12 +500,19 @@ RedisClient.prototype.connection_gone = function (why) { this.retry_timer = setTimeout(retry_connection, this.retry_delay, this); }; +var err_code = /^([A-Z]+)\s+(.+)$/; RedisClient.prototype.return_error = function (err) { var command_obj = this.command_queue.shift(), queue_len = this.command_queue.length; if (command_obj.command && command_obj.command.toUpperCase) { err.command_used = command_obj.command.toUpperCase(); } + var match = err.message.match(err_code); + // LUA script could return user errors that don't behave like all other errors! + if (match) { + err.code = match[1]; + } + if (this.pub_sub_mode === false && queue_len === 0) { this.command_queue = new Queue(); this.emit("idle"); diff --git a/test/commands/eval.spec.js b/test/commands/eval.spec.js index a22098e3338..234547cd48a 100644 --- a/test/commands/eval.spec.js +++ b/test/commands/eval.spec.js @@ -52,7 +52,11 @@ describe("The 'eval' method", function () { it('converts lua error to an error response', function (done) { helper.serverVersionAtLeast.call(this, client, [2, 5, 0]); - client.eval("return {err='this is an error'}", 0, helper.isError(done)); + client.eval("return {err='this is an error'}", 0, function(err) { + assert(err.code === undefined); + helper.isError()(err); + done(); + }); }); it('represents a lua table appropritely', function (done) { diff --git a/test/commands/keys.spec.js b/test/commands/keys.spec.js index 60c1e84404c..58159072f6c 100644 --- a/test/commands/keys.spec.js +++ b/test/commands/keys.spec.js @@ -14,6 +14,9 @@ describe("The 'keys' method", function () { var client; beforeEach(function (done) { + args = args || {}; + // This is going to test if the high water is also respected + args.command_queue_high_water = 100; client = redis.createClient.apply(redis.createClient, args); client.once("connect", function () { client.flushdb(done); From 2293f7ff85dbcd6714b2d6bafebfc787b67eac71 Mon Sep 17 00:00:00 2001 From: Ruben Bridgewater Date: Sat, 19 Sep 2015 18:01:22 +0200 Subject: [PATCH 0367/1748] Add some more tests --- index.js | 1 - test/commands/eval.spec.js | 1 + test/commands/multi.spec.js | 1 + test/commands/select.spec.js | 4 +++- 4 files changed, 5 insertions(+), 2 deletions(-) diff --git a/index.js b/index.js index bd71db18666..9e93ef3c5e6 100644 --- a/index.js +++ b/index.js @@ -1060,7 +1060,6 @@ Multi.prototype.execute_callback = function (err, replies) { if (err) { if (err.code !== 'CONNECTION_BROKEN') { - err.code = 'EXECABORT'; err.errors = this.errors; if (this.callback) { this.callback(err); diff --git a/test/commands/eval.spec.js b/test/commands/eval.spec.js index 234547cd48a..cfbaefa82be 100644 --- a/test/commands/eval.spec.js +++ b/test/commands/eval.spec.js @@ -128,6 +128,7 @@ describe("The 'eval' method", function () { helper.serverVersionAtLeast.call(this, client, [2, 5, 0]); client.evalsha('ffffffffffffffffffffffffffffffffffffffff', 0); client.on('error', function(err) { + assert.equal(err.code, 'NOSCRIPT'); assert(/NOSCRIPT No matching script. Please use EVAL./.test(err.message)); done(); }); diff --git a/test/commands/multi.spec.js b/test/commands/multi.spec.js index 60ac2e07fa7..e6c72d01dc4 100644 --- a/test/commands/multi.spec.js +++ b/test/commands/multi.spec.js @@ -263,6 +263,7 @@ describe("The 'multi' method", function () { it('reports multiple exceptions when they occur (while EXEC is running)', function (done) { client.multi().config("bar").debug("foo").exec(function (err, reply) { assert.strictEqual(reply.length, 2); + assert.equal(reply[0].code, 'ERR'); assert(/^ERR/.test(reply[0].message), "Error message should begin with ERR"); assert(/^ERR/.test(reply[1].message), "Error message should begin with ERR"); return done(); diff --git a/test/commands/select.spec.js b/test/commands/select.spec.js index 1ad7de94c1d..c02b4de5432 100644 --- a/test/commands/select.spec.js +++ b/test/commands/select.spec.js @@ -57,7 +57,8 @@ describe("The 'select' method", function () { describe("with a valid db index", function () { it("selects the appropriate database", function (done) { assert.strictEqual(client.selected_db, null, "default db should be null"); - client.select(1, function () { + client.select(1, function (err) { + assert.equal(err, null); assert.equal(client.selected_db, 1, "we should have selected the new valid DB"); return done(); }); @@ -68,6 +69,7 @@ describe("The 'select' method", function () { it("returns an error", function (done) { assert.strictEqual(client.selected_db, null, "default db should be null"); client.select(9999, function (err) { + assert.equal(err.code, 'ERR'); assert.equal(err.message, 'ERR invalid DB index'); return done(); }); From 959b0ee093952626be68d1539f9905c2db4c4b47 Mon Sep 17 00:00:00 2001 From: Ruben Bridgewater Date: Sat, 19 Sep 2015 18:15:23 +0200 Subject: [PATCH 0368/1748] Fix error codes for multi.exec and add more tests --- index.js | 8 +++++++- test/commands/multi.spec.js | 8 ++++++-- 2 files changed, 13 insertions(+), 3 deletions(-) diff --git a/index.js b/index.js index 9e93ef3c5e6..98b2267c156 100644 --- a/index.js +++ b/index.js @@ -1077,7 +1077,13 @@ Multi.prototype.execute_callback = function (err, replies) { args = this.queue[i]; // If we asked for strings, even in detect_buffers mode, then return strings: - if (reply) { + if (reply instanceof Error) { + var match = reply.message.match(err_code); + // LUA script could return user errors that don't behave like all other errors! + if (match) { + reply.code = match[1]; + } + } else if (reply) { if (this._client.options.detect_buffers && this.wants_buffers[i] === false) { replies[i - 1] = reply = reply_to_strings(reply); } diff --git a/test/commands/multi.spec.js b/test/commands/multi.spec.js index e6c72d01dc4..840eb9687c5 100644 --- a/test/commands/multi.spec.js +++ b/test/commands/multi.spec.js @@ -254,6 +254,7 @@ describe("The 'multi' method", function () { assert(err.message.match(/^EXECABORT/), "Error message should begin with EXECABORT"); assert.equal(err.errors.length, 2, "err.errors should have 2 items"); assert.strictEqual(err.errors[0].command_used, 'SET'); + assert.strictEqual(err.errors[0].code, 'ERR'); assert.strictEqual(err.errors[0].position, 1); assert(/^ERR/.test(err.errors[0].message), "Actuall error message should begin with ERR"); return done(); @@ -261,9 +262,11 @@ describe("The 'multi' method", function () { }); it('reports multiple exceptions when they occur (while EXEC is running)', function (done) { - client.multi().config("bar").debug("foo").exec(function (err, reply) { - assert.strictEqual(reply.length, 2); + client.multi().config("bar").debug("foo").eval("return {err='this is an error'}", 0).exec(function (err, reply) { + assert.strictEqual(reply.length, 3); assert.equal(reply[0].code, 'ERR'); + assert.equal(reply[2].code, undefined); + assert(/^this is an error/.test(reply[2].message)); assert(/^ERR/.test(reply[0].message), "Error message should begin with ERR"); assert(/^ERR/.test(reply[1].message), "Error message should begin with ERR"); return done(); @@ -276,6 +279,7 @@ describe("The 'multi' method", function () { multi.set('foo', 'bar', helper.isString('OK')); multi.debug("foo").exec(function (err, reply) { assert.strictEqual(reply.length, 3); + assert.strictEqual(reply[0].code, 'ERR'); assert(/^ERR/.test(reply[0].message), "Error message should begin with ERR"); assert(/^ERR/.test(reply[2].message), "Error message should begin with ERR"); assert.strictEqual(reply[1], "OK"); From 4fa9169f471b91f611531476ce556fca5d9eec0e Mon Sep 17 00:00:00 2001 From: Ruben Bridgewater Date: Sat, 19 Sep 2015 17:37:31 +0200 Subject: [PATCH 0369/1748] Remove very old benchmarks that do not really work anymore are obsolet and rename "benches" to "benchmarks" This is also going move the multi_bench benchmark into the benchmarks folder --- benches/hiredis_parser.js | 40 -------- benches/re_sub_test.js | 15 --- benches/reconnect_test.js | 31 ------- benches/stress/codec.js | 8 -- benches/stress/pubsub/pub.js | 38 -------- benches/stress/pubsub/run | 10 -- benches/stress/pubsub/server.js | 22 ----- benches/stress/rpushblpop/pub.js | 51 ----------- benches/stress/rpushblpop/run | 6 -- benches/stress/rpushblpop/server.js | 31 ------- benches/stress/speed/00 | 13 --- benches/stress/speed/plot | 13 --- benches/stress/speed/size-rate.png | Bin 6672 -> 0 bytes benches/stress/speed/speed.js | 86 ------------------ benches/sub_quit_test.js | 20 ---- {benches => benchmarks}/buffer_bench.js | 0 .../diff_multi_bench_output.js | 0 multi_bench.js => benchmarks/multi_bench.js | 0 18 files changed, 384 deletions(-) delete mode 100644 benches/hiredis_parser.js delete mode 100644 benches/re_sub_test.js delete mode 100644 benches/reconnect_test.js delete mode 100644 benches/stress/codec.js delete mode 100644 benches/stress/pubsub/pub.js delete mode 100755 benches/stress/pubsub/run delete mode 100644 benches/stress/pubsub/server.js delete mode 100644 benches/stress/rpushblpop/pub.js delete mode 100755 benches/stress/rpushblpop/run delete mode 100644 benches/stress/rpushblpop/server.js delete mode 100644 benches/stress/speed/00 delete mode 100755 benches/stress/speed/plot delete mode 100644 benches/stress/speed/size-rate.png delete mode 100644 benches/stress/speed/speed.js delete mode 100644 benches/sub_quit_test.js rename {benches => benchmarks}/buffer_bench.js (100%) rename diff_multi_bench_output.js => benchmarks/diff_multi_bench_output.js (100%) rename multi_bench.js => benchmarks/multi_bench.js (100%) diff --git a/benches/hiredis_parser.js b/benches/hiredis_parser.js deleted file mode 100644 index 489a4912e2c..00000000000 --- a/benches/hiredis_parser.js +++ /dev/null @@ -1,40 +0,0 @@ -'use strict'; - -var Parser = require('../lib/parser/hiredis').Parser; -var assert = require('assert'); - -/* -This test makes sure that exceptions thrown inside of "reply" event handlers -are not trapped and mistakenly emitted as parse errors. -*/ -(function testExecuteDoesNotCatchReplyCallbackExceptions() { - var parser = new Parser(); - var replies = [{}]; - - parser.reader = { - feed: function() {}, - get: function() { - return replies.shift(); - } - }; - - var emittedError = false; - var caughtException = false; - - parser - .on('error', function() { - emittedError = true; - }) - .on('reply', function() { - throw new Error('bad'); - }); - - try { - parser.execute(); - } catch (err) { - caughtException = true; - } - - assert.equal(caughtException, true); - assert.equal(emittedError, false); -})(); diff --git a/benches/re_sub_test.js b/benches/re_sub_test.js deleted file mode 100644 index 62c9302f901..00000000000 --- a/benches/re_sub_test.js +++ /dev/null @@ -1,15 +0,0 @@ -'use strict'; - -var client = require('../index').createClient(); -var client2 = require('../index').createClient(); - -client.once('subscribe', function (channel, count) { - client.unsubscribe('x'); - client.subscribe('x', function () { - client.quit(); - client2.quit(); - }); - client2.publish('x', 'hi'); -}); - -client.subscribe('x'); diff --git a/benches/reconnect_test.js b/benches/reconnect_test.js deleted file mode 100644 index 7556ea430a4..00000000000 --- a/benches/reconnect_test.js +++ /dev/null @@ -1,31 +0,0 @@ -'use strict'; - -var redis = require("../index").createClient(null, null, { -// max_attempts: 4 -}); - -redis.on("error", function (err) { - console.log("Redis says: " + err); -}); - -redis.on("ready", function () { - console.log("Redis ready."); -}); - -redis.on("reconnecting", function (arg) { - console.log("Redis reconnecting: " + JSON.stringify(arg)); -}); -redis.on("connect", function () { - console.log("Redis connected."); -}); - -setInterval(function () { - var now = Date.now(); - redis.set("now", now, function (err, res) { - if (err) { - console.log(now + " Redis reply error: " + err); - } else { - console.log(now + " Redis reply: " + res); - } - }); -}, 100); diff --git a/benches/stress/codec.js b/benches/stress/codec.js deleted file mode 100644 index 5a3cc6daa4b..00000000000 --- a/benches/stress/codec.js +++ /dev/null @@ -1,8 +0,0 @@ -'use strict'; - -var json = { - encode: JSON.stringify, - decode: JSON.parse -}; - -module.exports = json; diff --git a/benches/stress/pubsub/pub.js b/benches/stress/pubsub/pub.js deleted file mode 100644 index 51c6555f83a..00000000000 --- a/benches/stress/pubsub/pub.js +++ /dev/null @@ -1,38 +0,0 @@ -'use strict'; - -var freemem = require('os').freemem; -var profiler = require('v8-profiler'); -var codec = require('../codec'); -var sent = 0; -var exec; - -var pub = require('redis').createClient(null, null, { - //command_queue_high_water: 5, - //command_queue_low_water: 1 -}) -.on('ready', function() { - this.emit('drain'); -}) -.on('drain', function() { - process.nextTick(exec); -}); - -var payload = '1'; for (var i = 0; i < 12; ++i) payload += payload; -console.log('Message payload length', payload.length); - -function exec() { - pub.publish('timeline', codec.encode({ foo: payload })); - ++sent; - if (!pub.should_buffer) { - process.nextTick(exec); - } -} - -profiler.takeSnapshot('s_0'); - -exec(); - -setInterval(function() { - profiler.takeSnapshot('s_' + sent); - console.error('sent', sent, 'free', freemem(), 'cmdqlen', pub.command_queue.length, 'offqlen', pub.offline_queue.length); -}, 2000); diff --git a/benches/stress/pubsub/run b/benches/stress/pubsub/run deleted file mode 100755 index bd9ac392539..00000000000 --- a/benches/stress/pubsub/run +++ /dev/null @@ -1,10 +0,0 @@ -#!/bin/sh -node server.js & -node server.js & -node server.js & -node server.js & -node server.js & -node server.js & -node server.js & -node server.js & -node --debug pub.js diff --git a/benches/stress/pubsub/server.js b/benches/stress/pubsub/server.js deleted file mode 100644 index 0679561b1d8..00000000000 --- a/benches/stress/pubsub/server.js +++ /dev/null @@ -1,22 +0,0 @@ -'use strict'; - -var freemem = require('os').freemem; -var codec = require('../codec'); - -var id = Math.random(); -var recv = 0; - -require('redis').createClient() - .on('ready', function() { - this.subscribe('timeline'); - }) - .on('message', function(channel, message) { - if (message) { - message = codec.decode(message); - ++recv; - } - }); - -setInterval(function() { - console.error('id', id, 'received', recv, 'free', freemem()); -}, 2000); diff --git a/benches/stress/rpushblpop/pub.js b/benches/stress/rpushblpop/pub.js deleted file mode 100644 index 009407857d2..00000000000 --- a/benches/stress/rpushblpop/pub.js +++ /dev/null @@ -1,51 +0,0 @@ -'use strict'; - -var freemem = require('os').freemem; -//var profiler = require('v8-profiler'); -var codec = require('../codec'); - -var sent = 0; - -var exec; - -var pub = require('redis').createClient(null, null, { - //command_queue_high_water: 5, - //command_queue_low_water: 1 -}) -.on('ready', function() { - this.del('timeline'); - this.emit('drain'); -}) -.on('drain', function() { - process.nextTick(exec); -}); - -var payload = '1'; for (var i = 0; i < 12; ++i) payload += payload; -console.log('Message payload length', payload.length); - -function exec() { - pub.rpush('timeline', codec.encode({ foo: payload })); - ++sent; - if (!pub.should_buffer) { - process.nextTick(exec); - } -} - -//profiler.takeSnapshot('s_0'); - -exec(); - -setInterval(function() { - //var ss = profiler.takeSnapshot('s_' + sent); - //console.error(ss.stringify()); - pub.llen('timeline', function(err, result) { - console.error('sent', sent, 'free', freemem(), - 'cmdqlen', pub.command_queue.length, 'offqlen', pub.offline_queue.length, - 'llen', result - ); - }); -}, 2000); - -/*setTimeout(function() { - process.exit(); -}, 30000);*/ diff --git a/benches/stress/rpushblpop/run b/benches/stress/rpushblpop/run deleted file mode 100755 index 8045ae80457..00000000000 --- a/benches/stress/rpushblpop/run +++ /dev/null @@ -1,6 +0,0 @@ -#!/bin/sh -node server.js & -#node server.js & -#node server.js & -#node server.js & -node --debug pub.js diff --git a/benches/stress/rpushblpop/server.js b/benches/stress/rpushblpop/server.js deleted file mode 100644 index 7a18a9b3f51..00000000000 --- a/benches/stress/rpushblpop/server.js +++ /dev/null @@ -1,31 +0,0 @@ -'use strict'; - -var freemem = require('os').freemem; -var codec = require('../codec'); - -var id = Math.random(); -var recv = 0; - -var cmd = require('redis').createClient(); - -require('redis').createClient() - .on('ready', function() { - this.emit('timeline'); - }) - .on('timeline', function() { - var self = this; - this.blpop('timeline', 0, function(err, result) { - var message = result[1]; - if (message) { - message = codec.decode(message); - ++recv; - } - self.emit('timeline'); - }); - }); - -setInterval(function() { - cmd.llen('timeline', function(err, result) { - console.error('id', id, 'received', recv, 'free', freemem(), 'llen', result); - }); -}, 2000); diff --git a/benches/stress/speed/00 b/benches/stress/speed/00 deleted file mode 100644 index 29d7bf7c5dd..00000000000 --- a/benches/stress/speed/00 +++ /dev/null @@ -1,13 +0,0 @@ -# size JSON msgpack bison -26602 2151.0170848180414 -25542 ? 2842.589272665782 -24835 ? ? 7280.4538397469805 -6104 6985.234528557929 -5045 ? 7217.461392841478 -4341 ? ? 14261.406335354604 -4180 15864.633685636572 -4143 ? 12954.806235781925 -4141 ? ? 44650.70733912719 -75 114227.07313350472 -40 ? 30162.440062810834 -39 ? ? 119815.66013519121 diff --git a/benches/stress/speed/plot b/benches/stress/speed/plot deleted file mode 100755 index 2563797cf54..00000000000 --- a/benches/stress/speed/plot +++ /dev/null @@ -1,13 +0,0 @@ -#!/bin/sh - -gnuplot >size-rate.jpg << _EOF_ - -set terminal png nocrop enhanced font verdana 12 size 640,480 -set logscale x -set logscale y -set grid -set xlabel 'Serialized object size, octets' -set ylabel 'decode(encode(obj)) rate, 1/sec' -plot '00' using 1:2 title 'json' smooth bezier, '00' using 1:3 title 'msgpack' smooth bezier, '00' using 1:4 title 'bison' smooth bezier - -_EOF_ diff --git a/benches/stress/speed/size-rate.png b/benches/stress/speed/size-rate.png deleted file mode 100644 index c9c2bee6b076040ccf7e6bc337c307732c400463..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 6672 zcmb_gcT^KkyG}v^p$MTvXrUJokPcD;(wj5^r5FTh(xga{5<-_^Kn-960g+w=L3$7= z3Me3;NLN5QD1!9djsEWUefOOE&pqev*|R%4JM+HJJp0bfK5w$Qnf@snBn=1zI%Q~} zV+jI5upkh47ES@wKo}&)fCpJ~6Kh=(i3HT-<>irYL=Xr|B7#VujSV6i)Y}Wv@|+_P zQ6Mauh$YEjuqhxA8Ve#}NztT{XA%-XSM=;4&<}}3%E`%@nwq+~xkX1u=j7zn*47e< z#Qy&Ng@pyc#g?oregj|ed2#FF)+~v%F@+=5cT{a`kUoI`Zy;CT8ZD3_AeCbK=Q)Lw z(;t8W0u3a-Uj>mNl0ff=NGx|r(dJuNEJ+u1fDKqCgl{dTwl-XUl|Z|*Gnt1)k)DAz za2xkPAE)xB@aE`?*QK#6AS??D77d(OEQW~1kg#O@Xi^?(EE* zk7)_z5_GydHtiN8^X5S(R-w4)Bkdp5^J_1CUpo5a1bugab382tSo&X%)A=P;Q6ujz z*B?A;tK?!F5GE$s)a!%2MlU{I`_--$-PzZWypEhD4py5&9KJMrg+I5Lc^SLWcI9}R zWdlr^8espWd4Wefft|msbaPmlGF0YW#iw=0_AvLb`;J$B3&dZ?QrsQXc{b5p&+BOm z9y`x0mWM_p8F~m#ge-o#9Ndwwq)GV-p^N>wmtNe>ZO&(1#8ow^s=mBov@_cAqdg(^ z+V+%HUtdPSwI}Vr7)(?!?rjD79bUV-eDJV?8rx4%p|y2()kn{wF+8x8&Cegw<)pOl zq;!R%!UK^ab@obya%oq-9Cl?nwcTG_Pd6L;){=^Z@fBS~&`K3CpA3fx1+DU(@1uSy zL7XipFh&t2o=7rv@AKySf;SOq@)ix)kR-ha#rEFCJ+dn5s*h+5=Vg6=c_#~S#1-mJ z75eo|{H}N)YW3QxVXb}f%xOM?7zAAO%;|ideMe=k?}Km#Iw;(X{0BH!BHXaBnM3h#BMu6FMIW-tAIKOgBgw(SM}n4?E;w_Zv$ z#_fMdu}Me*1D1KqKKEEu7pp1=C4jW#y&Q#PHBO)XK%tFAA%C2l=m?sQoRNaVWgdIZ zV>@#-rvtxO!;%6d*)Y5ap#+b@gbM3@^SsIwok#)Q$d@i@|`hJ51JiM*Tx-%2gj2QT%>7ga!ME84X_Q$gDO{D9BR0V za4v|F{3X6($haJq{=lQ~EF;gonx-hpnwk64Wy1G&eDJY_ z<3UQ&J;S$Zj)GL}N+}{&tMVrxMK|-j>mh2=t+^>bgPC8#&Bv&~C*%GLH0nu!+np0# zN!W+$w8ezYliOZ1kfXyl(G4@r2U1_0=>5tpwtm@VA|-Ey#PcFB=Jk+C!t{l0?6ES#=VkTswjEzy|LHYi z+38=X6`Qw9zBj{;WRYZ6D>5z4)2ox{z&5T^5}d)`Di~fI^_|fXOgqP~MtqDHL}08@ zXgo`PhR6!r7X6R`b8uOx*SG6xDR0p9*$I2mA@D&cL~`=$X<#U7+dj&U5q#HjBFN2c zex%03zeRn$L3k^Q^vw5V{os&Zb#l^t`54nT&zvS7K$P!a*jw4;bI*x@55_fsp4%Sp zjLPnl#{E4rxOJu^p47a&yFH`%T6P}*x+@Yqm%6Ywu#xi$-#33ZP0pg>w!2i%Cl$=S zYd@3qkSZ8t{Wccxl?XDmqm|J>pbTipXlX+e{}mp+;6@PmK{X3T^&|ASwV&0T5;^1!4In#b~(~;>rN+g*6+LU9>N|F*jC?#yV7| z1l0|&WKaZjNDW&qNqY^PkZVmQbekR%*8Dn^YjIM_QjKtbbBwyrQx3KlcUHkwcrt6` zd5=^|gqQZ=jwZfD9!x+66G?O)K4+R8y2hpxD<$ z^S?)H#ef=TM<6$|lcf3rv>gJ_ts(AqD6~{jg?>ly85a{Bss>QKt02+tKWKAdU#_&( zEm083r>%X2IK++cQ%E+U?mq(sz6I|H)e+S&H(Z5?HkYH{f#G=CF-QOHE;)W8AK^mR z+smHPM>+r#QV1U}vKM}|`85Gzo2RafCLQqnS*Pi6S2h#Q4;1xOGvQ<^_=od-%o*$m z3u*zxY(#LGT>vv`BMx){jYWwAAEu96hDR}@(iXQb_mIYC7D<5g3sQL#+%t^WP{uzI zdHC0JR%Uy$TQk__#xW>6fXb}-UOnt>(n&1cvUXk7_U70#xMufn$v|RIHDlgahuGX! z=p`DNJKS69`8kyn@4_AQ<_5I<8%k3Wwp{9QYhotY3h$oPn=d|+oOqxi*B5nS@fZo5 zP7~wD;GH*V$fgz*_{La*%Xv7gh~K)fXVu6e?V)OmO48gIn~T4i5{~RCj&=6u!6GWb zo~mAgTe#N}o3ntGzUkGgi+!LQ=WW%|4=qk;tg2j#r%sH=7+erP`&T2iyH{z{dqk&sj%EoLhNqOf zQS%vDhjHb)v5(7pR2G$}-us3nI__voLFQ{O^nQ;;J`nq?+cbSdOB3VB5-b5fmC&X5 zzGau1Q(y3he3DkL;N&iK4&|E+a#?N-D1UdQ*-WWr#7M#$Uz*UeRd(on)(w9WC9XoH z_Ts&X3ucYy(89mV4e{bds|u5EIAtuJDqLkihHo*g^{zj1(Sfr2xDkbPRfgB31CQQ&%`r)>GG|w4o(Z*bX7?OQb=*?IE77vRLTySeTFYfa`z)%XCW~E~ zFU?(gM$kNU74b8P<$!rn&ijO4f|!yYd4X*9hNRwX4u#pHtTMN0*6#vnae|GEWE?DX zXP!x<V z+dCW?i@$aAjzDs?LJ24B*0B>q(Lc7GzDjnu0;`;>d+(K>;ZGxFnM`7R9-XC$>N{-Z zePulO)q%GFbEP$-6=>f`Re&*`_6wCWLICEnQ*r87Yrb24O;@G`cgGdmkJ%aGugQ8a zE{Bgp3@yCb*Ek=heOERbR2YuQN)#8M;trdEpL`S3$~QIDf<#Db`KY8pYu|z=G~UUh zMoH|IcI%*r+PFq|^~GBwNtM!J&kJSnSr~~&ye3M&m408i0jZlzetZWyMOY*TcF?Oy z_`Bz%K-LA+*O@-uZ_&WK3R)`Rnzm2>0$+S_p%;HleV-zqqvKSWukzwZ5L+yMKr-m9 zqrZ81w*YIR1D?F2EPxIC8HNn9l;Jsz*0b>oFNBxsoP)PKiTSY5ak$fEdg%_y0HRSA zv{cJr_A2qVcjp@IM3nMgp@|gao_4f&c>Aof-RbI)C=TYKlTIdgE%VwY@&Z7tE?ER-HPO6s(Y1p7z5Zfjepseg0afb!hOE zy}i^^#=2JK;ZkA7HdB-z+iQjXI|3#I`bcicLraX5N*?lPtmD%{vG((#X!%>Ni8V>m zutfo4__nmzSxYONgo!41db$;@ow)*?!l@Tr^v(0w_=}w!XPld+4eW2OB+3!;KFaaK zq|AOb%*Q9gvfcDVvbbfCC6fXf>>?bgK)e}F8C7)zbO<$Y!g1lK}d!an1Ac@gCMzo3gKAP_XOXWndj#xw`rbTM`Z>ZgV;W< zD|ulFAlkN(agVM_zjp6~NgyEdKXT~D4Y%SK%tICf-h!WB6Mwd~*~^0Xcl=VAz3`x?3QqfwsZf=H;|tn5;&!zdEFU#0;VJdhzn0S=<=mSRCNlJc^>xS-Z}e{xw4Zli%H8Ak-d6DN~!z zm3OR(K6@;Z-`Q!3y}w;7^xM#Lb+iVu?@Sb?nw%S>bJJj{@dk62Gh_!PJ7q--CPILp zg#xXS3Nbt5!Om0^)B`_7zVt-(1GjD=pe> zu!hjGf+Zy7_a?$if#BB#no?~l>=}z>r-1q zOR}S%Nh`&`uOW$6ojwL69cDr~#nfTVRF@nidT2v;MR%fos?uEkZj8-Xftw#a_ta0i zDQ^Ec!&nss9O0u(nnZ8rz{FzJ{dJE#E|#*BDV|r&3-{@ucIv@hV>H%9AH`(63%Sm6 z4rcm1oQMteW$mbd`mh$i%q`?f53(}7?d4#(xQHsX7xov`dhVng4~@-o89n^;FqUuf z>pN4~r6S_qvHw;;`5H7^pZ=Atu)XSqJ!a*&fOGDv8GD{|?$VvR14>jIzWYM8TD|XbppSj~(sVLWLq(L3ppI5X zH3c5s7;9LzZ$tkhl|I-N@hykLIWDa-@?ksZPoTKfk6y&TN%wJ@@30{6JuT!n&lu`9`PC&_ZSQ&6iWREb}x6ArJ0}5!0X_M4=y# zhphwArQ7HCUl?u8Cze1xc)qh0%tka?VPyYE^c*=Q+&vZp`Qcu0r;H&UPu{l97>mb8 z>+hnmPnB&kQn7gP!|TRU24@~y0V^$v5h1hK$-S%%UpYwO1V z?}Ag)yoOD6Y7u!QL~oINU?-`-nNMw%5*;Y{hXCDTLB}LCNK17k|I;bB3?&N-M?|ud zk_|q-dG2neRuS$%w5|Xw zU*JpJGtm9z3z^iM);h6X!);Bc*&SnwlPGC(p;I!W!r`&@Aon+$34f%YK#&r*74DDp zsw@f)v(bTRWAnavMq6X#shP;h4m%k_aW@ck26tNx2VzQ0G!r0iyE#=bMgkIeZ`$nl z^2T6fkvmKekAlKW3wFOJF1!Y&KCsJoCF;gJ6$ZEmk{EA(68$k`djZ7x@;l{J7lD-s z5QFYT-^36Q=xF?If!NuoP@K^3ma@} zyw}*H_tzug&Nw{0S(Yq#Q_emm#|lRrUT`p7;14HlXL_| z5HjWris3iy5Pmc>0TSW!r5Pxhk9*8C{B1iMYBjI_ClJwd7ym+%)nJ8_g>>FC&pR}6 zAgyavkIi}we1DI0*W0) z_N+rsUe~Y2fBN^H$PjvAKz`vFD1!nJ^kslz1+?{lB*DmH`2Q0C;U`S&!CTAI*Tb?hkO6eHBY9*d>TfQjkuh)_X^ zN)Iiz8dT1KP(lTu_$NEe?o=SC%lam;2-m*vA?K(6eV~+khL3gJ1V1-KnD7@}+T5*3 z?M^#RcUg`SsAXK6&?rI-m4_VK=zZ&s`cM<3nK2IE_2IKh@)&E5JY8_ZC7LKUVdScq zeyk(xb;s@e=V(9qxM14hdwou0eIjR(pAQPg5xX;7X}pK(2o!W_rU~}Xtrwg@anRt~ z;a0~|Yd#~x)pK%xh&aYKkWfvU_V}78ucu$YzQX19?Q^^AzpR+<ffXJpQWv*itUYIncYVgkvp-rxs)wS2ThfShdV~R@&V#RxdSD$){~fkoK$T7r-Y62H7q9^z-^>ez6agpH_NnHg;JjpL5#t%`ohD-#3Pd(I*GK zY?DX6>UE{xV!}$z@0gn~#3#=M7#)9ScTy%D{i;0iZA0{C>@enBTPO-2bC|iU6*r(k zwbll7r8K1T;)))o--JiTXwhr$tn4g~+*E$Kt~5}PyB6$WCO3#65rlO^uudtYyd{5sT3vAGc@{Jx{k3Tfrf6neyAQVVJi{G4SqaV%!HL~B|w>&ppSwHn1X-hb#E(w>gH!|nuq=@9gLe?e`> z@s5T6`=HiG8*z1=9cmN>S4K=T^&(a;VXYQNE1tJKq;j~xYIH6@(cy#2?A!P(wt;M_ zM;e;Bz#C~OHs)5l*kFE4F5?EooBk~dR6mkj?+X4`XmOrUeXS8zTs@s^My@l|HPflo H#^C-7XAtZ( diff --git a/benches/stress/speed/speed.js b/benches/stress/speed/speed.js deleted file mode 100644 index 7b76db6f4cd..00000000000 --- a/benches/stress/speed/speed.js +++ /dev/null @@ -1,86 +0,0 @@ -'use strict'; - -var msgpack = require('node-msgpack'); -var bison = require('bison'); -var codec = { - JSON: { - encode: JSON.stringify, - decode: JSON.parse - }, - msgpack: { - encode: msgpack.pack, - decode: msgpack.unpack - }, - bison: bison -}; - -var obj, l; - -function run(obj, codec) { - var t1 = Date.now(); - var n = 10000; - for (var i = 0; i < n; ++i) { - codec.decode(l = codec.encode(obj)); - } - var t2 = Date.now(); - //console.log('DONE', n*1000/(t2-t1), 'codecs/sec, length=', l.length); - return [n*1000/(t2-t1), l.length]; -} - -function series(obj, cname, n) { - var rate = 0; - var len = 0; - for (var i = 0; i < n; ++i) { - var r = run(obj, codec[cname]); - rate += r[0]; - len += r[1]; - } - rate /= n; - len /= n; - console.log(cname + ' ' + rate + ' ' + len); - return [rate, len]; -} - -function forObj(obj) { - var r = { - JSON: series(obj, 'JSON', 20), - msgpack: series(obj, 'msgpack', 20), - bison: series(obj, 'bison', 20) - }; - return r; -} - -var s = '0'; -for (var i = 0; i < 12; ++i) s += s; - -obj = { - foo: s, - arrrrrr: [{a:1,b:false,c:null,d:1.0}, 1111, 2222, 33333333], - rand: [], - a: s, - ccc: s, - b: s + s + s -}; -for (i = 0; i < 100; ++i) obj.rand.push(Math.random()); -forObj(obj); - -obj = { - foo: s, - arrrrrr: [{a:1,b:false,c:null,d:1.0}, 1111, 2222, 33333333], - rand: [] -}; -for (i = 0; i < 100; ++i) obj.rand.push(Math.random()); -forObj(obj); - -obj = { - foo: s, - arrrrrr: [{a:1,b:false,c:null,d:1.0}, 1111, 2222, 33333333], - rand: [] -}; -forObj(obj); - -obj = { - arrrrrr: [{a:1,b:false,c:null,d:1.0}, 1111, 2222, 33333333], - rand: [] -}; -forObj(obj); diff --git a/benches/sub_quit_test.js b/benches/sub_quit_test.js deleted file mode 100644 index ae3725a999c..00000000000 --- a/benches/sub_quit_test.js +++ /dev/null @@ -1,20 +0,0 @@ -'use strict'; - -var client = require("redis").createClient(), - client2 = require("redis").createClient(); - -client.subscribe("something"); -client.on("subscribe", function (channel, count) { - console.log("Got sub: " + channel); - client.unsubscribe("something"); -}); - -client.on("unsubscribe", function (channel, count) { - console.log("Got unsub: " + channel + ", quitting"); - client.quit(); -}); - -// exercise unsub before sub -client2.unsubscribe("something"); -client2.subscribe("another thing"); -client2.quit(); diff --git a/benches/buffer_bench.js b/benchmarks/buffer_bench.js similarity index 100% rename from benches/buffer_bench.js rename to benchmarks/buffer_bench.js diff --git a/diff_multi_bench_output.js b/benchmarks/diff_multi_bench_output.js similarity index 100% rename from diff_multi_bench_output.js rename to benchmarks/diff_multi_bench_output.js diff --git a/multi_bench.js b/benchmarks/multi_bench.js similarity index 100% rename from multi_bench.js rename to benchmarks/multi_bench.js From 91955af38974c0175df4375e0b25047d0eb58a48 Mon Sep 17 00:00:00 2001 From: Ruben Bridgewater Date: Sun, 20 Sep 2015 18:44:00 +0200 Subject: [PATCH 0370/1748] Update dev dependencies --- package.json | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/package.json b/package.json index 1a4495d8ebb..ca2e5fc4784 100644 --- a/package.json +++ b/package.json @@ -20,9 +20,9 @@ "coveralls": "^2.11.2", "jshint": "^2.8.0", "metrics": ">=0.1.5", - "mocha": "^2.2.5", + "mocha": "^2.3.2", "nyc": "^3.2.2", - "optional-dev-dependency": "^1.0.1", + "optional-dev-dependency": "^1.1.0", "tcp-port-used": "^0.1.2", "uuid": "^2.0.1", "win-spawn": "^2.0.0" From 1f121fa6e2c3ab4b3ccafa9868aaefa419d3854a Mon Sep 17 00:00:00 2001 From: Ruben Bridgewater Date: Sun, 20 Sep 2015 18:53:57 +0200 Subject: [PATCH 0371/1748] Fix error messages being manipulated. Fixes #695 --- index.js | 16 ++++++++-------- test/node_redis.spec.js | 14 +++++++++++++- 2 files changed, 21 insertions(+), 9 deletions(-) diff --git a/index.js b/index.js index 98b2267c156..c4ffba1923a 100644 --- a/index.js +++ b/index.js @@ -91,8 +91,8 @@ RedisClient.prototype.install_stream_listeners = function() { self.reply_parser.execute(buffer_from_socket); }); - this.stream.on("error", function (msg) { - self.on_error(msg.message); + this.stream.on("error", function (err) { + self.on_error(err); }); this.stream.on("close", function () { @@ -148,19 +148,18 @@ RedisClient.prototype.flush_and_error = function (error) { this.command_queue = new Queue(); }; -RedisClient.prototype.on_error = function (msg) { +RedisClient.prototype.on_error = function (err) { if (this.closing) { return; } - var message = "Redis connection to " + this.address + " failed - " + msg; + err.message = "Redis connection to " + this.address + " failed - " + err.message; - debug(message); + debug(err.message); this.connected = false; this.ready = false; - - this.emit("error", new Error(message)); + this.emit("error", err); // "error" events get turned into exceptions if they aren't listened for. If the user handled this error // then we should try to reconnect. this.connection_gone("error"); @@ -346,7 +345,8 @@ RedisClient.prototype.on_info_cmd = function (err, res) { var line, retry_time, parts, sub_parts; if (err) { - return self.emit("error", new Error("Ready check failed: " + err.message)); + err.message = "Ready check failed: " + err.message; + return self.emit("error", err); } for (i = 0; i < lines.length; i++) { diff --git a/test/node_redis.spec.js b/test/node_redis.spec.js index cfdb8f6b25e..3a90a944193 100644 --- a/test/node_redis.spec.js +++ b/test/node_redis.spec.js @@ -765,6 +765,12 @@ describe("The node_redis client", function () { assert(i, 3); assert.strictEqual(client.offline_queue.length, 0); done(); + } else { + assert.equal(err.code, 'ECONNREFUSED'); + assert.equal(err.errno, 'ECONNREFUSED'); + assert.equal(err.syscall, 'connect'); + assert.equal(err.address, '127.0.0.1'); + assert.equal(err.port, 9999); } }); @@ -785,7 +791,7 @@ describe("The node_redis client", function () { }); describe('false', function () { - it("does emit an error and does not enqueues operation", function (done) { + it("emit an error and does not enqueues operation", function (done) { var client = redis.createClient(9999, null, { parser: parser, max_attempts: 0, @@ -840,6 +846,12 @@ describe("The node_redis client", function () { if (/Redis connection in broken state:/.test(err.message)) { assert.equal(client.command_queue.length, 0); done(); + } else { + assert.equal(err.code, 'ECONNREFUSED'); + assert.equal(err.errno, 'ECONNREFUSED'); + assert.equal(err.syscall, 'connect'); + assert.equal(err.address, '127.0.0.2'); + assert.equal(err.port, 6370); } }); }); From c60a3b65fec2b3614741ecae54de8f0c1e42a711 Mon Sep 17 00:00:00 2001 From: Ruben Bridgewater Date: Sun, 20 Sep 2015 18:56:21 +0200 Subject: [PATCH 0372/1748] Rename .command_used to .command and add the used command to more errors --- index.js | 47 +++++++++++++++++++++--------------- test/auth.spec.js | 8 +++--- test/commands/multi.spec.js | 4 ++- test/commands/select.spec.js | 2 +- test/node_redis.spec.js | 8 +++--- 5 files changed, 40 insertions(+), 29 deletions(-) diff --git a/index.js b/index.js index c4ffba1923a..98685f41a34 100644 --- a/index.js +++ b/index.js @@ -503,8 +503,11 @@ RedisClient.prototype.connection_gone = function (why) { var err_code = /^([A-Z]+)\s+(.+)$/; RedisClient.prototype.return_error = function (err) { var command_obj = this.command_queue.shift(), queue_len = this.command_queue.length; + // send_command might have been used wrong => catch those cases too if (command_obj.command && command_obj.command.toUpperCase) { - err.command_used = command_obj.command.toUpperCase(); + err.command = command_obj.command.toUpperCase(); + } else { + err.command = command_obj.command; } var match = err.message.match(err_code); @@ -652,7 +655,9 @@ RedisClient.prototype.return_reply = function (reply) { }); this.emit("monitor", timestamp, args); } else { - this.emit("error", new Error("node_redis command queue state error. If you can reproduce this, please report it.")); + var err = new Error("node_redis command queue state error. If you can reproduce this, please report it."); + err.command = command_obj.command.toUpperCase(); + this.emit("error", err); } }; @@ -706,7 +711,7 @@ RedisClient.prototype.send_command = function (command, args, callback) { if (args[args.length - 1] === undefined || args[args.length - 1] === null) { command = command.toUpperCase(); err = new Error('send_command: ' + command + ' value must not be undefined or null'); - err.command_used = command; + err.command = command; if (callback) { return callback && callback(err); } @@ -733,7 +738,7 @@ RedisClient.prototype.send_command = function (command, args, callback) { } else { err = new Error(command + ' can\'t be processed. The connection has already been closed.'); } - err.command_used = command; + err.command = command; if (callback) { callback(err); } else { @@ -754,7 +759,9 @@ RedisClient.prototype.send_command = function (command, args, callback) { } else if (command === "quit") { this.closing = true; } else if (this.pub_sub_mode === true) { - this.emit("error", new Error("Connection in subscriber mode, only subscriber commands may be used")); + err = new Error("Connection in subscriber mode, only subscriber commands may be used"); + err.command = command.toUpperCase(); + this.emit("error", err); return; } this.command_queue.push(command_obj); @@ -935,7 +942,7 @@ RedisClient.prototype.select = RedisClient.prototype.SELECT = function (db, call RedisClient.prototype.auth = RedisClient.prototype.AUTH = function (pass, callback) { if (typeof pass !== 'string') { var err = new Error('The password has to be of type "string"'); - err.command_used = 'AUTH'; + err.command = 'AUTH'; if (callback) { callback(err); } else { @@ -1056,7 +1063,7 @@ Multi.prototype.exec = Multi.prototype.EXEC = function (callback) { }; Multi.prototype.execute_callback = function (err, replies) { - var i, reply, args; + var i, args; if (err) { if (err.code !== 'CONNECTION_BROKEN') { @@ -1072,32 +1079,32 @@ Multi.prototype.execute_callback = function (err, replies) { } if (replies) { - for (i = 1; i < this.queue.length; i += 1) { - reply = replies[i - 1]; - args = this.queue[i]; + for (i = 0; i < this.queue.length - 1; i += 1) { + args = this.queue[i + 1]; // If we asked for strings, even in detect_buffers mode, then return strings: - if (reply instanceof Error) { - var match = reply.message.match(err_code); + if (replies[i] instanceof Error) { + var match = replies[i].message.match(err_code); // LUA script could return user errors that don't behave like all other errors! if (match) { - reply.code = match[1]; + replies[i].code = match[1]; } - } else if (reply) { - if (this._client.options.detect_buffers && this.wants_buffers[i] === false) { - replies[i - 1] = reply = reply_to_strings(reply); + replies[i].command = args[0].toUpperCase(); + } else if (replies[i]) { + if (this._client.options.detect_buffers && this.wants_buffers[i + 1] === false) { + replies[i] = reply_to_strings(replies[i]); } if (args[0] === "hgetall") { // TODO - confusing and error-prone that hgetall is special cased in two places - replies[i - 1] = reply = reply_to_object(reply); + replies[i] = reply_to_object(replies[i]); } } if (typeof args[args.length - 1] === "function") { - if (reply instanceof Error) { - args[args.length - 1](reply); + if (replies[i] instanceof Error) { + args[args.length - 1](replies[i]); } else { - args[args.length - 1](null, reply); + args[args.length - 1](null, replies[i]); } } } diff --git a/test/auth.spec.js b/test/auth.spec.js index f1270059266..15ec18fe222 100644 --- a/test/auth.spec.js +++ b/test/auth.spec.js @@ -41,7 +41,7 @@ describe("client authentication", function () { client = redis.createClient.apply(redis.createClient, args); client.once('error', function (err) { - assert.strictEqual(err.command_used, 'AUTH'); + assert.strictEqual(err.command, 'AUTH'); assert.ok(/ERR invalid password/.test(err.message)); return done(); }); @@ -55,7 +55,7 @@ describe("client authentication", function () { client = redis.createClient.apply(redis.createClient, args); client.auth('', function (err, res) { - assert.strictEqual(err.command_used, 'AUTH'); + assert.strictEqual(err.command, 'AUTH'); assert.ok(/ERR invalid password/.test(err.message)); done(); }); @@ -130,7 +130,7 @@ describe("client authentication", function () { client = redis.createClient.apply(redis.createClient, args); client.auth(undefined, function(err, res) { assert.strictEqual(err.message, 'The password has to be of type "string"'); - assert.strictEqual(err.command_used, 'AUTH'); + assert.strictEqual(err.command, 'AUTH'); assert.strictEqual(res, undefined); done(); }); @@ -142,7 +142,7 @@ describe("client authentication", function () { client = redis.createClient.apply(redis.createClient, args); client.on('error', function (err) { assert.strictEqual(err.message, 'The password has to be of type "string"'); - assert.strictEqual(err.command_used, 'AUTH'); + assert.strictEqual(err.command, 'AUTH'); done(); }); client.auth(234567); diff --git a/test/commands/multi.spec.js b/test/commands/multi.spec.js index 840eb9687c5..af73235891f 100644 --- a/test/commands/multi.spec.js +++ b/test/commands/multi.spec.js @@ -253,7 +253,7 @@ describe("The 'multi' method", function () { assert.equal(reply, undefined, "The reply should have been discarded"); assert(err.message.match(/^EXECABORT/), "Error message should begin with EXECABORT"); assert.equal(err.errors.length, 2, "err.errors should have 2 items"); - assert.strictEqual(err.errors[0].command_used, 'SET'); + assert.strictEqual(err.errors[0].command, 'SET'); assert.strictEqual(err.errors[0].code, 'ERR'); assert.strictEqual(err.errors[0].position, 1); assert(/^ERR/.test(err.errors[0].message), "Actuall error message should begin with ERR"); @@ -265,7 +265,9 @@ describe("The 'multi' method", function () { client.multi().config("bar").debug("foo").eval("return {err='this is an error'}", 0).exec(function (err, reply) { assert.strictEqual(reply.length, 3); assert.equal(reply[0].code, 'ERR'); + assert.equal(reply[0].command, 'CONFIG'); assert.equal(reply[2].code, undefined); + assert.equal(reply[2].command, 'EVAL'); assert(/^this is an error/.test(reply[2].message)); assert(/^ERR/.test(reply[0].message), "Error message should begin with ERR"); assert(/^ERR/.test(reply[1].message), "Error message should begin with ERR"); diff --git a/test/commands/select.spec.js b/test/commands/select.spec.js index c02b4de5432..832d5ffac1a 100644 --- a/test/commands/select.spec.js +++ b/test/commands/select.spec.js @@ -94,7 +94,7 @@ describe("The 'select' method", function () { assert.strictEqual(client.selected_db, null, "default db should be null"); client.on('error', function (err) { - assert.strictEqual(err.command_used, 'SELECT'); + assert.strictEqual(err.command, 'SELECT'); assert.equal(err.message, 'ERR invalid DB index'); done(); }); diff --git a/test/node_redis.spec.js b/test/node_redis.spec.js index 3a90a944193..2178cf16a1a 100644 --- a/test/node_redis.spec.js +++ b/test/node_redis.spec.js @@ -156,7 +156,9 @@ describe("The node_redis client", function () { process.once('uncaughtException', function (err) { process.on('uncaughtException', mochaListener); - assert(/ERR Protocol error/.test(err)); + assert(/ERR Protocol error/.test(err.message)); + assert.equal(err.command, true); + assert.equal(err.code, 'ERR'); done(); }); @@ -195,7 +197,7 @@ describe("The node_redis client", function () { setTimeout(function() { client.get("foo", function(err, res) { assert.strictEqual(err.message, 'GET can\'t be processed. The connection has already been closed.'); - assert.strictEqual(err.command_used, 'GET'); + assert.strictEqual(err.command, 'GET'); assert.strictEqual(client.offline_queue.length, 0); done(); }); @@ -209,7 +211,7 @@ describe("The node_redis client", function () { client.quit(); client.on('error', function(err) { assert.strictEqual(err.message, 'SET can\'t be processed. The connection has already been closed.'); - assert.strictEqual(err.command_used, 'SET'); + assert.strictEqual(err.command, 'SET'); assert.strictEqual(client.offline_queue.length, 0); done(); }); From 1cbf19ddf57655fb30b836b3e60cf180ff34429f Mon Sep 17 00:00:00 2001 From: Ruben Bridgewater Date: Sun, 20 Sep 2015 21:19:29 +0200 Subject: [PATCH 0373/1748] Hotfix for older node versions --- test/node_redis.spec.js | 4 ---- 1 file changed, 4 deletions(-) diff --git a/test/node_redis.spec.js b/test/node_redis.spec.js index 2178cf16a1a..60a1b7a9aaa 100644 --- a/test/node_redis.spec.js +++ b/test/node_redis.spec.js @@ -771,8 +771,6 @@ describe("The node_redis client", function () { assert.equal(err.code, 'ECONNREFUSED'); assert.equal(err.errno, 'ECONNREFUSED'); assert.equal(err.syscall, 'connect'); - assert.equal(err.address, '127.0.0.1'); - assert.equal(err.port, 9999); } }); @@ -852,8 +850,6 @@ describe("The node_redis client", function () { assert.equal(err.code, 'ECONNREFUSED'); assert.equal(err.errno, 'ECONNREFUSED'); assert.equal(err.syscall, 'connect'); - assert.equal(err.address, '127.0.0.2'); - assert.equal(err.port, 6370); } }); }); From 21d40717abf722980e7b0d969d1b1498d675b01d Mon Sep 17 00:00:00 2001 From: Ruben Bridgewater Date: Sun, 20 Sep 2015 21:44:29 +0200 Subject: [PATCH 0374/1748] Run travis without sudo --- .travis.yml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.travis.yml b/.travis.yml index 495b45b6ffd..7d65abb80bc 100644 --- a/.travis.yml +++ b/.travis.yml @@ -1,5 +1,5 @@ language: node_js -sudo: true +sudo: false env: - CXX=g++-4.8 addons: From 6975b0723d2353a11870d90997cfc7c7f774f81b Mon Sep 17 00:00:00 2001 From: Ruben Bridgewater Date: Sun, 20 Sep 2015 21:51:53 +0200 Subject: [PATCH 0375/1748] Add benchmark task to npm --- benchmarks/multi_bench.js | 2 +- package.json | 1 + 2 files changed, 2 insertions(+), 1 deletion(-) diff --git a/benchmarks/multi_bench.js b/benchmarks/multi_bench.js index dd2e99fe1b8..ea3da38d8d2 100644 --- a/benchmarks/multi_bench.js +++ b/benchmarks/multi_bench.js @@ -1,6 +1,6 @@ 'use strict'; -var redis = require("./index"), +var redis = require("../index"), metrics = require("metrics"), num_clients = parseInt(process.argv[2], 10) || 5, num_requests = 20000, diff --git a/package.json b/package.json index ca2e5fc4784..4287fac0521 100644 --- a/package.json +++ b/package.json @@ -12,6 +12,7 @@ "scripts": { "coveralls": "nyc report --reporter=text-lcov | coveralls", "coverage": "nyc report --reporter=html", + "benchmark": "node benchmarks/multi_bench.js", "test": "nyc ./node_modules/.bin/_mocha ./test/*.js ./test/commands/*.js ./test/parser/*.js --timeout=8000", "pretest": "optional-dev-dependency hiredis", "posttest": "jshint ." From 6958c1854b6803962152bb53fe93d81ac34c66b6 Mon Sep 17 00:00:00 2001 From: Ruben Bridgewater Date: Mon, 21 Sep 2015 02:38:27 +0200 Subject: [PATCH 0376/1748] Increase the coverage by adding tests and fix a failing ready check --- index.js | 32 ++++++++++-------------------- test/auth.spec.js | 28 ++++++++++++++++++++++++++ test/commands/set.spec.js | 40 +++++++++++++++++++++++--------------- test/commands/srem.spec.js | 10 ++++++++++ test/node_redis.spec.js | 9 +++++---- 5 files changed, 77 insertions(+), 42 deletions(-) diff --git a/index.js b/index.js index 98685f41a34..ac9a1759e01 100644 --- a/index.js +++ b/index.js @@ -337,6 +337,12 @@ RedisClient.prototype.on_ready = function () { }; RedisClient.prototype.on_info_cmd = function (err, res) { + if (err) { + err.message = "Ready check failed: " + err.message; + this.emit("error", err); + return; + } + var self = this; var obj = {}; var lines = res.toString().split("\r\n"); @@ -344,11 +350,6 @@ RedisClient.prototype.on_info_cmd = function (err, res) { var key = 'db' + i; var line, retry_time, parts, sub_parts; - if (err) { - err.message = "Ready check failed: " + err.message; - return self.emit("error", err); - } - for (i = 0; i < lines.length; i++) { parts = lines[i].split(':'); if (parts[1]) { @@ -369,9 +370,7 @@ RedisClient.prototype.on_info_cmd = function (err, res) { obj[key] = {}; while (line = parts.pop()) { sub_parts = line.split('='); - if (sub_parts[1]) { - obj[key][sub_parts[0]] = +sub_parts[1]; - } + obj[key][sub_parts[0]] = +sub_parts[1]; } i++; key = 'db' + i; @@ -471,7 +470,7 @@ RedisClient.prototype.connection_gone = function (why) { // If this is a requested shutdown, then don't retry if (this.closing) { - debug("connection ended from quit command, not retrying."); + debug("Connection ended from quit command, not retrying."); this.flush_and_error(new Error("Redis connection gone from " + why + " event.")); return; } @@ -656,7 +655,7 @@ RedisClient.prototype.return_reply = function (reply) { this.emit("monitor", timestamp, args); } else { var err = new Error("node_redis command queue state error. If you can reproduce this, please report it."); - err.command = command_obj.command.toUpperCase(); + err.command_obj = command_obj; this.emit("error", err); } }; @@ -695,19 +694,8 @@ RedisClient.prototype.send_command = function (command, args, callback) { callback = process.domain.bind(callback); } - // if the last argument is an array and command is sadd or srem, expand it out: - // client.sadd(arg1, [arg2, arg3, arg4], cb); - // converts to: - // client.sadd(arg1, arg2, arg3, arg4, cb); - if ((command === 'sadd' || command === 'srem') && args.length > 0 && Array.isArray(args[args.length - 1])) { - args = args.slice(0, -1).concat(args[args.length - 1]); - } - - // if the value is undefined or null and command is set or setx, need not to send message to redis if (command === 'set' || command === 'setex') { - if (args.length === 0) { - return; - } + // if the value is undefined or null and command is set or setx, need not to send message to redis if (args[args.length - 1] === undefined || args[args.length - 1] === null) { command = command.toUpperCase(); err = new Error('send_command: ' + command + ' value must not be undefined or null'); diff --git a/test/auth.spec.js b/test/auth.spec.js index 15ec18fe222..b4646c1fdc6 100644 --- a/test/auth.spec.js +++ b/test/auth.spec.js @@ -159,6 +159,34 @@ describe("client authentication", function () { client.auth(auth, helper.isString('OK', done)); }); }); + + it('does not allow any commands to be processed if not authenticated using no_ready_check true', function (done) { + if (helper.redisProcess().spawnFailed()) this.skip(); + + var args = config.configureClient(parser, ip, { + no_ready_check: true + }); + client = redis.createClient.apply(redis.createClient, args); + client.on("ready", function () { + client.set('foo', 'bar', function (err, res) { + assert.equal(err.message, 'NOAUTH Authentication required.'); + assert.equal(err.code, 'NOAUTH'); + assert.equal(err.command, 'SET'); + done(); + }); + }); + }); + + it('does not allow auth to be provided post-hoc with auth method if not authenticated before', function (done) { + if (helper.redisProcess().spawnFailed()) this.skip(); + client = redis.createClient.apply(redis.createClient, args); + client.on("error", function (err) { + assert.equal(err.code, 'NOAUTH'); + assert.equal(err.message, 'Ready check failed: NOAUTH Authentication required.'); + assert.equal(err.command, 'INFO'); + done(); + }); + }); }); }); diff --git a/test/commands/set.spec.js b/test/commands/set.spec.js index 048450d5b48..c295af1d3da 100644 --- a/test/commands/set.spec.js +++ b/test/commands/set.spec.js @@ -70,6 +70,7 @@ describe("The 'set' method", function () { it("reports an error", function (done) { client.set(undefined, function (err, res) { helper.isError()(err, null); + assert.equal(err.command, 'SET'); done(); }); }); @@ -90,28 +91,35 @@ describe("The 'set' method", function () { }); describe("with undefined 'key' and missing 'value' parameter", function () { - it("does not emit an error", function (done) { - this.timeout(200); - - client.once("error", function (err) { - done(err); + it("emits an error without callback", function (done) { + client.on('error', function (err) { + assert.equal(err.message, 'send_command: SET value must not be undefined or null'); + assert.equal(err.command, 'SET'); + done(); }); + client.set(undefined); + }); + }); - client.set(); - - setTimeout(function () { - done(); - }, 100); + it("emit an error with only the key set", function (done) { + client.on('error', function (err) { + assert.equal(err.message, "ERR wrong number of arguments for 'set' command"); + done(); }); - it("does emit an error", function (done) { - client.on('error', function (err) { - assert.equal(err.message, "ERR wrong number of arguments for 'set' command"); - done(); - }); + client.set('foo'); + }); - client.set('foo'); + it("emit an error without any parameters", function (done) { + client.once("error", function (err) { + assert.equal(err.message, 'send_command: SET value must not be undefined or null'); + assert.equal(err.command, 'SET'); + done(); }); + + // This was not supported not to throw earlier and was added by the test refactoring + // https://github.com/NodeRedis/node_redis/commit/eaca486ab1aecd1329f7452ad2f2255b1263606f + client.set(); }); }); }); diff --git a/test/commands/srem.spec.js b/test/commands/srem.spec.js index c075a987aa9..1ce496b9618 100644 --- a/test/commands/srem.spec.js +++ b/test/commands/srem.spec.js @@ -39,6 +39,16 @@ describe("The 'srem' method", function () { }); }); + it('allows multiple values to be removed with send_command', function (done) { + client.send_command('sadd', ['set0', 'member0', 'member1', 'member2'], helper.isNumber(3)); + client.send_command('srem', ["set0", "member1", "member2"], helper.isNumber(2)); + client.smembers("set0", function (err, res) { + assert.strictEqual(res.length, 1); + assert.ok(~res.indexOf("member0")); + return done(err); + }); + }); + it('handles a value missing from the set of values being removed', function (done) { client.sadd(["set0", "member0", "member1", "member2"], helper.isNumber(3)); client.SREM(["set0", "member3", "member4"], helper.isNumber(0)); diff --git a/test/node_redis.spec.js b/test/node_redis.spec.js index 60a1b7a9aaa..44dfabc8a1c 100644 --- a/test/node_redis.spec.js +++ b/test/node_redis.spec.js @@ -829,17 +829,18 @@ describe("The node_redis client", function () { var cb = function(err, reply) { assert.equal(err.code, 'CONNECTION_BROKEN'); }; - for (var i = 0; i < 10; i += 2) { - multi.set("foo" + i, "bar" + i); + for (var i = 0; i < 12; i += 3) { + client.set("foo" + i, "bar" + i); multi.set("foo" + (i + 1), "bar" + (i + 1), cb); + multi.set("foo" + (i + 2), "bar" + (i + 2)); } multi.exec(); - assert.equal(client.command_queue.length, 13); + assert.equal(client.command_queue.length, 15); helper.killConnection(client); }); client.on("reconnecting", function (params) { - assert.equal(client.command_queue.length, 13); + assert.equal(client.command_queue.length, 15); }); client.on('error', function(err) { From b900bd697fcf27a3564e57628dcbdbfb25c55ed8 Mon Sep 17 00:00:00 2001 From: Ruben Bridgewater Date: Mon, 21 Sep 2015 02:40:07 +0200 Subject: [PATCH 0377/1748] Some small parser changes The small_to_string was actually quite slow --- index.js | 4 +--- lib/parser/hiredis.js | 13 ++++++------- lib/parser/javascript.js | 29 +++++++---------------------- 3 files changed, 14 insertions(+), 32 deletions(-) diff --git a/index.js b/index.js index ac9a1759e01..cd3ccf6846a 100644 --- a/index.js +++ b/index.js @@ -271,9 +271,7 @@ RedisClient.prototype.init_parser = function () { // return_buffers sends back Buffers from parser to callback. detect_buffers sends back Buffers from parser, but // converts to Strings if the input arguments are not Buffers. - this.reply_parser = new this.parser_module.Parser({ - return_buffers: self.options.return_buffers || self.options.detect_buffers || false - }); + this.reply_parser = new this.parser_module.Parser(self.options.return_buffers || self.options.detect_buffers || false); // Important: Only send results / errors async. // That way the result / error won't stay in a try catch block and catch user things this.reply_parser.send_error = function (data) { diff --git a/lib/parser/hiredis.js b/lib/parser/hiredis.js index 57a1cfb4aba..765304bec53 100644 --- a/lib/parser/hiredis.js +++ b/lib/parser/hiredis.js @@ -2,19 +2,15 @@ var hiredis = require("hiredis"); -exports.name = "hiredis"; - -function HiredisReplyParser(options) { +function HiredisReplyParser(return_buffers) { this.name = exports.name; - this.options = options; + this.return_buffers = return_buffers; this.reset(); } -exports.Parser = HiredisReplyParser; - HiredisReplyParser.prototype.reset = function () { this.reader = new hiredis.Reader({ - return_buffers: this.options.return_buffers || false + return_buffers: this.return_buffers || false }); }; @@ -35,3 +31,6 @@ HiredisReplyParser.prototype.execute = function (data) { } } }; + +exports.Parser = HiredisReplyParser; +exports.name = "hiredis"; diff --git a/lib/parser/javascript.js b/lib/parser/javascript.js index d979ec3cbef..4ce4168c43f 100644 --- a/lib/parser/javascript.js +++ b/lib/parser/javascript.js @@ -7,37 +7,21 @@ function Packet(type, size) { this.size = +size; } -exports.name = "javascript"; - -function ReplyParser(options) { +function ReplyParser(return_buffers) { this.name = exports.name; - this.options = options; + this.return_buffers = return_buffers; this._buffer = null; this._offset = 0; this._encoding = "utf-8"; - this._reply_type = null; } -exports.Parser = ReplyParser; - function IncompleteReadBuffer(message) { this.name = "IncompleteReadBuffer"; this.message = message; } util.inherits(IncompleteReadBuffer, Error); -// Buffer.toString() is quite slow for small strings -function small_toString(buf, start, end) { - var tmp = "", i; - - for (i = start; i < end; i++) { - tmp += String.fromCharCode(buf[i]); - } - - return tmp; -} - ReplyParser.prototype._parseResult = function (type) { var start, end, offset, packetHeader; @@ -56,10 +40,8 @@ ReplyParser.prototype._parseResult = function (type) { if (type === 45) { return new Error(this._buffer.toString(this._encoding, start, end)); - } else if (this.options.return_buffers) { + } else if (this.return_buffers) { return this._buffer.slice(start, end); - } else if (end - start < 65536) { // completely arbitrary - return small_toString(this._buffer, start, end); } return this._buffer.toString(this._encoding, start, end); } else if (type === 58) { // : @@ -100,7 +82,7 @@ ReplyParser.prototype._parseResult = function (type) { throw new IncompleteReadBuffer("Wait for more data."); } - if (this.options.return_buffers) { + if (this.return_buffers) { return this._buffer.slice(start, end); } return this._buffer.toString(this._encoding, start, end); @@ -234,3 +216,6 @@ ReplyParser.prototype._packetEndOffset = function () { ReplyParser.prototype._bytesRemaining = function () { return (this._buffer.length - this._offset) < 0 ? 0 : (this._buffer.length - this._offset); }; + +exports.Parser = ReplyParser; +exports.name = "javascript"; From 55e4a9b847b15f535d1538241c98554de7c14af8 Mon Sep 17 00:00:00 2001 From: Ruben Bridgewater Date: Mon, 21 Sep 2015 02:41:24 +0200 Subject: [PATCH 0378/1748] Fix issues with returning buffers Fixes #818 and #354 --- index.js | 25 ++++++++++++++----------- test/helper.js | 36 ++++++++++++++++++++++++------------ 2 files changed, 38 insertions(+), 23 deletions(-) diff --git a/index.js b/index.js index cd3ccf6846a..64f3294059a 100644 --- a/index.js +++ b/index.js @@ -556,9 +556,8 @@ function reply_to_strings(reply) { if (Array.isArray(reply)) { for (i = 0; i < reply.length; i++) { - if (reply[i] !== null && reply[i] !== undefined) { - reply[i] = reply[i].toString(); - } + // Recusivly call the function as slowlog returns deep nested replies + reply[i] = reply_to_strings(reply[i]); } return reply; } @@ -612,14 +611,17 @@ RedisClient.prototype.return_reply = function (reply) { } else { debug("No callback for reply"); } - } else if (this.pub_sub_mode || (command_obj && command_obj.sub_command)) { + } else if (this.pub_sub_mode || command_obj && command_obj.sub_command) { if (Array.isArray(reply)) { + if (!this.options.return_buffers && (!command_obj || this.options.detect_buffers && command_obj.buffer_args === false)) { + reply = reply_to_strings(reply); + } type = reply[0].toString(); if (type === "message") { - this.emit("message", reply[1].toString(), reply[2]); // channel, message + this.emit("message", reply[1], reply[2]); // channel, message } else if (type === "pmessage") { - this.emit("pmessage", reply[1].toString(), reply[2].toString(), reply[3]); // pattern, channel, message + this.emit("pmessage", reply[1], reply[2], reply[3]); // pattern, channel, message } else if (type === "subscribe" || type === "unsubscribe" || type === "psubscribe" || type === "punsubscribe") { if (reply[2] === 0) { this.pub_sub_mode = false; @@ -629,12 +631,10 @@ RedisClient.prototype.return_reply = function (reply) { } // subscribe commands take an optional callback and also emit an event, but only the first response is included in the callback // TODO - document this or fix it so it works in a more obvious way - // reply[1] can be null - var reply1String = (reply[1] === null) ? null : reply[1].toString(); if (command_obj && typeof command_obj.callback === "function") { - command_obj.callback(null, reply1String); + command_obj.callback(null, reply[1]); } - this.emit(type, reply1String, reply[2]); // channel, count + this.emit(type, reply[1], reply[2]); // channel, count } else { this.emit("error", new Error("subscriptions are active but got unknown reply type " + type)); return; @@ -644,6 +644,9 @@ RedisClient.prototype.return_reply = function (reply) { return; } } else if (this.monitoring) { + if (Buffer.isBuffer(reply)) { + reply = reply.toString(); + } len = reply.indexOf(" "); timestamp = reply.slice(0, len); argindex = reply.indexOf('"'); @@ -776,7 +779,7 @@ RedisClient.prototype.send_command = function (command, args, callback) { for (i = 0; i < args.length; i += 1) { arg = args[i]; - if (!(Buffer.isBuffer(arg) || arg instanceof String)) { + if (!(Buffer.isBuffer(arg) || typeof arg === 'string')) { arg = String(arg); } diff --git a/test/helper.js b/test/helper.js index b57f31b3a12..776c2ca3ee6 100644 --- a/test/helper.js +++ b/test/helper.js @@ -108,27 +108,39 @@ module.exports = { } return true; }, - allTests: function (options, cb) { + allTests: function (opts, cb) { if (!cb) { - cb = options; - options = {}; + cb = opts; + opts = {}; } - // TODO: Test all different option cases at some point (e.g. buffers) - // [undefined, { return_buffers: true }].forEach(function (config_options) { - // describe(config_options && config_options.return_buffers ? "returning buffers" : "returning strings", function () { - // }); - // }); var parsers = ['javascript']; var protocols = ['IPv4']; if (process.platform !== 'win32') { parsers.push('hiredis'); protocols.push('IPv6', '/tmp/redis.sock'); } - parsers.forEach(function (parser) { - protocols.forEach(function (ip, i) { - if (i === 0 || options.allConnections) { - cb(parser, ip, config.configureClient(parser, ip)); + var options = [{ + detect_buffers: true + // Somehow we need a undefined here - otherwise the parsers return_buffers value is always true + // Investigate this further + }, undefined]; + options.forEach(function (options) { + var strOptions = ''; + var key; + for (key in options) { + if (options.hasOwnProperty(key)) { + strOptions += key + ': ' + options[key] + '; '; } + } + describe('using options: ' + strOptions, function() { + parsers.forEach(function (parser) { + protocols.forEach(function (ip, i) { + if (i !== 0 && !opts.allConnections) { + return; + } + cb(parser, ip, config.configureClient(parser, ip, options)); + }); + }); }); }); }, From 715c09dbd212f986ad6c58ec7b07c43ffd6dd103 Mon Sep 17 00:00:00 2001 From: Ruben Bridgewater Date: Mon, 14 Sep 2015 16:18:24 +0200 Subject: [PATCH 0379/1748] v.2.0.0 --- README.md | 130 ++++++++++++++++++++------------------------------- changelog.md | 83 ++++++++++++++++++++++++++++++-- index.js | 2 +- 3 files changed, 131 insertions(+), 84 deletions(-) diff --git a/README.md b/README.md index 29db8b0a24d..911771a3540 100644 --- a/README.md +++ b/README.md @@ -5,8 +5,7 @@ redis - a node.js redis client [![Coverage Status](https://coveralls.io/repos/NodeRedis/node_redis/badge.svg?branch=)](https://coveralls.io/r/NodeRedis/node_redis?branch=) [![Windows Tests](https://img.shields.io/appveyor/ci/bcoe/node-redis/master.svg?label=Windows%20Tests)](https://ci.appveyor.com/project/bcoe/node-redis) -This is a complete Redis client for node.js. It supports all Redis commands, -including many recently added commands. +This is a complete Redis client for node.js. It supports all Redis commands and focuses on performance. Install with: @@ -54,21 +53,26 @@ Note that the API is entire asynchronous. To get data back from the server, you'll need to use a callback. The return value from most of the API is a backpressure indicator. +You can also use node_redis with promises by promisifying node_redis with [bluebird](https://github.com/petkaantonov/bluebird) as in: + +```js +var redis = require('redis'); +bluebird.promisifyAll(redis.RedisClient.prototype); +``` + ### Sending Commands Each Redis command is exposed as a function on the `client` object. All functions take either an `args` Array plus optional `callback` Function or a variable number of individual arguments followed by an optional callback. -Here is an example of passing an array of arguments and a callback: - -```js -client.mset(["test keys 1", "test val 1", "test keys 2", "test val 2"], function (err, res) {}); -``` - -Here is that same call in the second style: +Here are examples how to use the api: ```js -client.mset("test keys 1", "test val 1", "test keys 2", "test val 2", function (err, res) {}); +client.hmset(["key", "test keys 1", "test val 1", "test keys 2", "test val 2"], function (err, res) {}); +// Works the same as +client.hmset("key", ["test keys 1", "test val 1", "test keys 2", "test val 2"], function (err, res) {}); +// Or +client.hmset("key", "test keys 1", "test val 1", "test keys 2", "test val 2", function (err, res) {}); ``` Note that in either form the `callback` is optional: @@ -78,7 +82,7 @@ client.set("some key", "some val"); client.set(["some other key", "some val"]); ``` -If the key is missing, reply will be null (probably): +If the key is missing, reply will be null. Only if the [Redis Command Reference](http://redis.io/commands) states something else it will not be null. ```js client.get("missingkey", function(err, reply) { @@ -103,15 +107,13 @@ JavaScript Array of node Buffers. `HGETALL` returns an Object with Buffers keyed ### "ready" -`client` will emit `ready` once a connection is established to the Redis server and the server reports -that it is ready to receive commands. Commands issued before the `ready` event are queued, +`client` will emit `ready` once a connection is established. Commands issued before the `ready` event are queued, then replayed just before this event is emitted. ### "connect" `client` will emit `connect` at the same time as it emits `ready` unless `client.options.no_ready_check` -is set. If this options is set, `connect` will be emitted when the stream is connected, and then -you are free to try to send commands. +is set. If this options is set, `connect` will be emitted when the stream is connected. ### "reconnecting" @@ -120,26 +122,9 @@ are passed an object containing `delay` (in ms) and `attempt` (the attempt #) at ### "error" -`client` will emit `error` when encountering an error connecting to the Redis server. - -Note that "error" is a special event type in node. If there are no listeners for an -"error" event, node will exit. This is usually what you want, but it can lead to some -cryptic error messages like this: - - mjr:~/work/node_redis (master)$ node example.js - - node.js:50 - throw e; - ^ - Error: ECONNREFUSED, Connection refused - at IOWatcher.callback (net:870:22) - at node.js:607:9 - -Not very useful in diagnosing the problem, but if your program isn't ready to handle this, -it is probably the right thing to just exit. +`client` will emit `error` when encountering an error connecting to the Redis server or when any other in node_redis occurs. -`client` will also emit `error` if an exception is thrown inside of `node_redis` for whatever reason. -It would be nice to distinguish these two cases. +So please attach the error listener to node_redis. ### "end" @@ -149,8 +134,7 @@ It would be nice to distinguish these two cases. `client` will emit `drain` when the TCP connection to the Redis server has been buffering, but is now writable. This event can be used to stream commands in to Redis and adapt to backpressure. Right now, -you need to check `client.command_queue.length` to decide when to reduce your send rate. Then you can -resume sending when you get `drain`. +you need to check `client.command_queue.length` to decide when to reduce your send rate and resume sending commands when you get `drain`. ### "idle" @@ -169,39 +153,35 @@ port and host are probably fine and you don't need to supply any arguments. `cre `options` is an object with the following possible properties: -* `parser`: which Redis protocol reply parser to use. Defaults to `hiredis` if that module is installed. -This may also be set to `javascript`. -* `return_buffers`: defaults to `false`. If set to `true`, then all replies will be sent to callbacks as node Buffer -objects instead of JavaScript Strings. -* `detect_buffers`: default to `false`. If set to `true`, then replies will be sent to callbacks as node Buffer objects -if any of the input arguments to the original command were Buffer objects. +* `parser`: *hiredis*; Which Redis protocol reply parser to use. If `hiredis` is not installed it will fallback to `javascript`. +* `return_buffers`: *false*; If set to `true`, then all replies will be sent to callbacks as Buffers instead of Strings. +* `detect_buffers`: *false*; If set to `true`, then replies will be sent to callbacks as Buffers +if any of the input arguments to the original command were Buffers. This option lets you switch between Buffers and Strings on a per-command basis, whereas `return_buffers` applies to every command on a client. -* `socket_nodelay`: defaults to `true`. Whether to call setNoDelay() on the TCP stream, which disables the -Nagle algorithm on the underlying socket. Setting this option to `false` can result in additional throughput at the -cost of more latency. Most applications will want this set to `true`. -* `socket_keepalive` defaults to `true`. Whether the keep-alive functionality is enabled on the underlying socket. -* `no_ready_check`: defaults to `false`. When a connection is established to the Redis server, the server might still -be loading the database from disk. While loading, the server not respond to any commands. To work around this, +* `socket_nodelay`: *true*; Disables the [Nagle algorithm](https://en.wikipedia.org/wiki/Nagle%27s_algorithm). +Setting this option to `false` can result in additional throughput at the cost of more latency. +Most applications will want this set to `true`. +* `socket_keepalive` *true*; Whether the keep-alive functionality is enabled on the underlying socket. +* `no_ready_check`: *false*; When a connection is established to the Redis server, the server might still +be loading the database from disk. While loading the server will not respond to any commands. To work around this, `node_redis` has a "ready check" which sends the `INFO` command to the server. The response from the `INFO` command indicates whether the server is ready for more commands. When ready, `node_redis` emits a `ready` event. Setting `no_ready_check` to `true` will inhibit this check. -* `enable_offline_queue`: defaults to `true`. By default, if there is no active +* `enable_offline_queue`: *true*; By default, if there is no active connection to the redis server, commands are added to a queue and are executed once the connection has been established. Setting `enable_offline_queue` to -`false` will disable this feature and the callback will be execute immediately -with an error, or an error will be thrown if no callback is specified. -* `retry_max_delay`: defaults to `null`. By default every time the client tries to connect and fails time before -reconnection (delay) almost doubles. This delay normally grows infinitely, but setting `retry_max_delay` limits delay -to maximum value, provided in milliseconds. -* `connect_timeout` defaults to `86400000`. Setting `connect_timeout` limits total time for client to reconnect. -Value is provided in milliseconds and is counted once the disconnect occurred. The last retry is going to happen exactly at the timeout time. +`false` will disable this feature and the callback will be executed immediately +with an error, or an error will be emitted if no callback is specified. +* `retry_max_delay`: *null*; By default every time the client tries to connect and fails the reconnection delay almost doubles. +This delay normally grows infinitely, but setting `retry_max_delay` limits it to the maximum value, provided in milliseconds. +* `connect_timeout` *86400000*; Setting `connect_timeout` limits total time for client to reconnect. +The value is provided in milliseconds and is counted once the disconnect occurred. The last retry is going to happen exactly at the timeout time. That way the default is to try reconnecting until 24h passed. -* `max_attempts` defaults to `0`. By default client will try reconnecting until connected. Setting `max_attempts` +* `max_attempts` *0*; By default client will try reconnecting until connected. Setting `max_attempts` limits total amount of connection tries. Setting this to 1 will prevent any reconnect tries. -* `auth_pass` defaults to `null`. By default client will try connecting without auth. If set, client will run redis auth command on connect. -* `family` defaults to `IPv4`. The client connects in IPv4 if not specified or if the DNS resolution returns an IPv4 address. -You can force an IPv6 if you set the family to 'IPv6'. See nodejs net or dns modules how to use the family type. +* `auth_pass` *null*; If set, client will run redis auth command on connect. +* `family` *IPv4*; You can force using IPv6 if you set the family to 'IPv6'. See Node.js [net](https://nodejs.org/api/net.html) or [dns](https://nodejs.org/api/dns.html) modules how to use the family type. ```js var redis = require("redis"), @@ -221,11 +201,9 @@ client.get(new Buffer("foo_rand000000000000"), function (err, reply) { client.end(); ``` - - ## client.auth(password, callback) -When connecting to Redis servers that require authentication, the `AUTH` command must be sent as the +When connecting to a Redis server that requires authentication, the `AUTH` command must be sent as the first command after connecting. This can be tricky to coordinate with reconnections, the ready check, etc. To make this easier, `client.auth()` stashes `password` and will send it after each connection, including reconnections. `callback` is invoked only once, after the response to the very first @@ -247,10 +225,11 @@ var redis = require("redis"), client = redis.createClient(); client.set("foo_rand000000000000", "some fantastic value"); +client.end(); // No further commands will be processed client.get("foo_rand000000000000", function (err, reply) { - console.log(reply.toString()); + // This won't be called anymore + console.log(err); }); -client.end(); ``` `client.end()` is useful for timeout cases where something is stuck or taking too long and you want @@ -436,10 +415,8 @@ client.multi() same command methods as `client` objects do. Commands are queued up inside the `Multi` object until `Multi.exec()` is invoked. -The `callback` of `.exec()` will get invoked with two arguments: - -* `err` **type:** `null | Array` err is either null or an array of Error Objects corresponding the the sequence the commands where chained. The last item of the array will always be an `EXECABORT` type of error originating from the `.exec()` itself. -* `results` **type:** `null | Array` results is an array of responses corresponding the the sequence the commands where chained. +If your code contains an syntax error an EXECABORT error is going to be thrown and all commands are going to be aborted. That error contains a `.errors` property that contains the concret errors. +If all commands were queued successfully and an error is thrown by redis while processing the commands that error is going to be returned in the result array! No other command is going to be aborted though than the onces failing. You can either chain together `MULTI` commands as in the above example, or you can queue individual commands while still sending regular client command as in this example: @@ -561,7 +538,7 @@ Used internally to send commands to Redis. Nearly all Redis commands have been a However, if new commands are introduced before this library is updated, you can use `send_command()` to send arbitrary commands to Redis. The command has to be lower case. -All commands are sent as multi-bulk commands. `args` can either be an Array of arguments, or omitted. +All commands are sent as multi-bulk commands. `args` can either be an Array of arguments, or omitted / set to undefined. ## client.connected @@ -720,22 +697,15 @@ hiredis parser: To get debug output run your `node_redis` application with `NODE_DEBUG=redis`. -## TODO - -1. 100% coverage -2. More performance improvements -3. Stream large set/get values into and out of Redis. Otherwise the entire value must be in node's memory. -4. Performance can be better for very large values in the js parser. - ## How to Contribute - open a pull request and then wait for feedback ## Contributors -Many people have have added features and fixed bugs in `node_redis`. Thanks to all of them! +Many [people](https://github.com/NodeRedis/node_redis/graphs/contributors) have have added features and fixed bugs in `node_redis`. Thanks to all of them! ## LICENSE - "MIT License" -Copyright (c) 2015 Matthew Ranney, http://ranney.com/ +Copyright (c) by NodeRedis Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation @@ -757,3 +727,5 @@ HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. + +Originally developed by Matthew Ranney, http://ranney.com/ \ No newline at end of file diff --git a/changelog.md b/changelog.md index 15588a4eae9..75c30eaaa0d 100644 --- a/changelog.md +++ b/changelog.md @@ -1,10 +1,84 @@ Changelog ========= -## Upcoming - -* [#815](https://github.com/NodeRedis/node_redis/pull/815) Consistently use new debug functionality (@BridgeAR) -* [#814](https://github.com/NodeRedis/node_redis/pull/814) Support new commands and drop support for deprecated 'substr' (@BridgeAR) +## v2.0.0 - Sep 21, 2015 + +This is the biggest release that node_redis had since it was released in 2010. A long list of outstanding bugs has been fixed, so we are very happy to present you redis 2.0 and we highly recommend updating as soon as possible. + +#What's new in 2.0 + +- Implemented a "connection is broken" mode if no connection could be established +- node_redis no longer throws under any circumstances, preventing it from terminating applications. +- Multi error handling is now working properly +- Consistent command behavior including multi +- Windows support +- Improved performance +- A lot of code cleanup +- Many bug fixes +- Better user support! + +## Features: + +- Added a "redis connection is broken" mode after reaching max connection attempts / exceeding connection timeout. (@BridgeAR) +- Added NODE_DEBUG=redis env to activate the debug_mode (@BridgeAR) +- Added a default connection timeout of 24h instead of never timing out as a default (@BridgeAR) +- Added: Network errors and other stream errors will from now on include the error code as `err.code` property (@BridgeAR) +- Added: Errors thrown by redis will now include the redis error code as `err.code` property. (@skeggse & @BridgeAR) +- Added: Errors thrown by node_redis will now include a `err.command` property for the command used (@BridgeAR) +- Added new commands and drop support for deprecated *substr* (@BridgeAR) +- Added new possibilities how to provide the command arguments (@BridgeAR) +- The entries in the keyspace of the server_info is now an object instead of a string. (@SinisterLight & @BridgeAR) +- Small speedup here and there (e.g. by not using .toLowerCase() anymore) (@BridgeAR) +- Full windows support (@bcoe) +- Increased coverage by 10% and add a lot of tests to make sure everything works as it should. We now reached 97% :-) (@BridgeAR) +- Remove dead code, clean up and refactor very old chunks (@BridgeAR) +- Don't flush the offline queue if reconnecting (@BridgeAR) +- Emit all errors insteaf of throwing sometimes and sometimes emitting them (@BridgeAR) +- *auth_pass* passwords are now checked to be a valid password (@jcppman & @BridgeAR) + +## Bug fixes: + +- Don't kill the app anymore by randomly throwing errors sync instead of emitting them (@BridgeAR) +- Don't catch user errors anymore occuring in callbacks (no try callback anymore & more fixes for the parser) (@BridgeAR) +- Early garbage collection of queued items (@dohse) +- Fix js parser returning errors as strings (@BridgeAR) +- Do not wrap errors into other errors (@BridgeAR) +- Authentication failures are now returned in the callback instead of being emitted (@BridgeAR) +- Fix a memory leak on reconnect (@rahar) +- Using `send_command` directly may no also be called without the args as stated in the [README.md](./README.md) (@BridgeAR) +- Fix the multi.exec error handling (@BridgeAR) +- Fix commands being inconsistent and behaving wrong (@BridgeAR) +- Channel names with spaces are now properly resubscribed after a reconnection (@pbihler) +- Do not try to reconnect after the connection timeout has been exceeded (@BridgeAR) +- Removed bad .eval implementation (@BridgeAR) +- Fix commands not being rejected after calling .quit (@BridgeAR) +- Fix .auth calling the callback twice if already connected (@BridgeAR) +- Fix detect_buffers not working in pub sub mode and while monitoring (@BridgeAR) +- Fix channel names always being strings instead of buffers while return_buffers is true (@BridgeAR) +- Don't print any debug statements if not asked for (@BridgeAR) +- Fix a couple small other bugs + +## Breaking changes: + +1. redis.send_command commands have to be lower case from now on. This does only apply if you use `.send_command` directly instead of the convenient methods like `redis.command`. +2. Error messages have changed quite a bit. If you depend on a specific wording please check your application carfully. +3. Errors are from now on always either returned if a callback is present or emitted. They won't be thrown (neither sync, nor async). +4. The Multi error handling has changed a lot! + - All errors are from now on errors instead of strings (this only applied to the js parser). + - If an error occurs while queueing the commands an EXECABORT error will be returned including the failed commands as `.errors` property instead of an array with errors. + - If an error occurs while executing the commands and that command has a callback it'll return the error as first parameter (`err, undefined` instead of `null, undefined`). + - All the errors occuring while executing the commands will stay in the result value as error instance (if you used the js parser before they would have been strings). Be aware that the transaction won't be aborted if those error occurr! + - If `multi.exec` does not have a callback and an EXECABORT error occurrs, it'll emit that error instead. +5. If redis can't connect to your redis server it'll give up after a certain point of failures (either max connection attempts or connection timeout exceeded). If that is the case it'll emit an CONNECTION_BROKEN error. You'll have to initiate a new client to try again afterwards. +6. The offline queue is not flushed anymore on a reconnect. It'll stay until node_redis gives up trying to reach the server or until you close the connection. +7. Before this release node_redis catched user errors and threw them async back. This is not the case anymore! No user behavior of what so ever will be tracked or catched. +8. The keyspace of `redis.server_info` (db0...) is from now on an object instead of an string. + +NodeRedis also thanks @qdb, @tobek, @cvibhagool, @frewsxcv, @davidbanham, @serv, @vitaliylag, @chrishamant, @GamingCoder and all other contributors that I may have missed for their contributions! + +From now on we'll push new releases more frequently out and fix further long outstanding things and implement new features. + +
## v1.0.0 - Aug 30, 2015 @@ -14,6 +88,7 @@ Changelog * [#786](https://github.com/NodeRedis/node_redis/pull/786) Refactor createClient. Fixes #651 (@BridgeAR) * [#793](https://github.com/NodeRedis/node_redis/pull/793) Refactor tests and improve test coverage (@erinspice, @bcoe) * [#733](https://github.com/NodeRedis/node_redis/pull/733) Fixes detect_buffers functionality in the context of exec. Fixes #732, #263 (@raydog) +* [#785](https://github.com/NodeRedis/node_redis/pull/785) Tiny speedup by using 'use strict' (@BridgeAR) * Fix extraneous error output due to pubsub tests (Mikael Kohlmyr) ## v0.12.1 - Aug 10, 2014 diff --git a/index.js b/index.js index 64f3294059a..597d6972021 100644 --- a/index.js +++ b/index.js @@ -1157,7 +1157,7 @@ exports.createClient = function(port_arg, host_arg, options) { return createClient_unix(port_arg, options); } - throw new Error('unknown type of connection in createClient()'); + throw new Error('Unknown type of connection in createClient()'); }; exports.print = function (err, reply) { From 604ab4e586ceff0e4e17d99f05edc0f2c142a9cb Mon Sep 17 00:00:00 2001 From: Ruben Bridgewater Date: Sat, 19 Sep 2015 20:51:02 +0200 Subject: [PATCH 0380/1748] Replace old multi_bench results --- README.md | 131 +++++++++++++++++------------------------------------- 1 file changed, 40 insertions(+), 91 deletions(-) diff --git a/README.md b/README.md index 911771a3540..7166567119b 100644 --- a/README.md +++ b/README.md @@ -461,7 +461,6 @@ client.multi([ }); ``` - ## Monitor mode Redis supports the `MONITOR` command, which lets you see all commands received by the Redis server @@ -600,99 +599,49 @@ Much effort has been spent to make `node_redis` as fast as possible for common operations. As pipelining happens naturally from shared connections, overall efficiency goes up. -Here are typical results of `multi_bench.js` which is similar to -`redis-benchmark` from the Redis distribution. It uses 5 concurrent connections -and shows the difference between pipelines of 1 and 50. - -JavaScript parser: - - Client count: 5, node version: 0.10.32, server version: 2.8.18, parser: javascript - PING, 1/5 min/max/avg/p95: 0/ 3/ 0.05/ 1.00 1103ms total, 18132.37 ops/sec - PING, 50/5 min/max/avg/p95: 0/ 4/ 0.81/ 2.00 327ms total, 61162.08 ops/sec - SET 4B str, 1/5 min/max/avg/p95: 0/ 2/ 0.05/ 0.00 1104ms total, 18115.94 ops/sec - SET 4B str, 50/5 min/max/avg/p95: 0/ 3/ 0.83/ 2.00 333ms total, 60060.06 ops/sec - SET 4B buf, 1/5 min/max/avg/p95: 0/ 2/ 0.09/ 1.00 1876ms total, 10660.98 ops/sec - SET 4B buf, 50/5 min/max/avg/p95: 0/ 11/ 2.55/ 4.00 1025ms total, 19512.20 ops/sec - GET 4B str, 1/5 min/max/avg/p95: 0/ 1/ 0.05/ 1.00 1117ms total, 17905.10 ops/sec - GET 4B str, 50/5 min/max/avg/p95: 0/ 3/ 0.87/ 2.00 347ms total, 57636.89 ops/sec - GET 4B buf, 1/5 min/max/avg/p95: 0/ 1/ 0.05/ 1.00 1110ms total, 18018.02 ops/sec - GET 4B buf, 50/5 min/max/avg/p95: 0/ 2/ 0.85/ 2.00 342ms total, 58479.53 ops/sec - SET 4KiB str, 1/5 min/max/avg/p95: 0/ 1/ 0.05/ 1.00 1119ms total, 17873.10 ops/sec - SET 4KiB str, 50/5 min/max/avg/p95: 0/ 3/ 0.89/ 2.00 358ms total, 55865.92 ops/sec - SET 4KiB buf, 1/5 min/max/avg/p95: 0/ 1/ 0.09/ 1.00 1894ms total, 10559.66 ops/sec - SET 4KiB buf, 50/5 min/max/avg/p95: 0/ 7/ 2.57/ 4.00 1031ms total, 19398.64 ops/sec - GET 4KiB str, 1/5 min/max/avg/p95: 0/ 6/ 0.06/ 1.00 1248ms total, 16025.64 ops/sec - GET 4KiB str, 50/5 min/max/avg/p95: 0/ 3/ 1.03/ 2.00 415ms total, 48192.77 ops/sec - GET 4KiB buf, 1/5 min/max/avg/p95: 0/ 1/ 0.06/ 1.00 1177ms total, 16992.35 ops/sec - GET 4KiB buf, 50/5 min/max/avg/p95: 0/ 10/ 1.02/ 2.00 409ms total, 48899.76 ops/sec - INCR, 1/5 min/max/avg/p95: 0/ 2/ 0.05/ 0.55 1137ms total, 17590.15 ops/sec - INCR, 50/5 min/max/avg/p95: 0/ 2/ 0.85/ 2.00 343ms total, 58309.04 ops/sec - LPUSH, 1/5 min/max/avg/p95: 0/ 1/ 0.06/ 1.00 1143ms total, 17497.81 ops/sec - LPUSH, 50/5 min/max/avg/p95: 0/ 3/ 0.87/ 2.00 350ms total, 57142.86 ops/sec - LRANGE 10, 1/5 min/max/avg/p95: 0/ 2/ 0.06/ 1.00 1283ms total, 15588.46 ops/sec - LRANGE 10, 50/5 min/max/avg/p95: 0/ 3/ 1.12/ 2.00 449ms total, 44543.43 ops/sec - LRANGE 100, 1/5 min/max/avg/p95: 0/ 1/ 0.09/ 1.00 1932ms total, 10351.97 ops/sec - LRANGE 100, 50/5 min/max/avg/p95: 0/ 5/ 2.46/ 4.00 985ms total, 20304.57 ops/sec - SET 4MiB buf, 1/5 min/max/avg/p95: 1/ 4/ 1.37/ 2.00 691ms total, 723.59 ops/sec - SET 4MiB buf, 50/5 min/max/avg/p95: 3/ 166/ 57.66/ 116.00 601ms total, 831.95 ops/sec - GET 4MiB str, 1/5 min/max/avg/p95: 84/ 110/ 93.18/ 106.95 9320ms total, 10.73 ops/sec - GET 4MiB str, 50/5 min/max/avg/p95: 156/7375/3400.10/6840.40 8928ms total, 11.20 ops/sec - GET 4MiB buf, 1/5 min/max/avg/p95: 84/ 105/ 91.21/ 99.00 9129ms total, 10.95 ops/sec - GET 4MiB buf, 50/5 min/max/avg/p95: 424/5704/3518.94/5626.65 9145ms total, 10.93 ops/sec - -If you use very large responses in your application, the JavaScript parser -performs badly. Until the JS parser is fixed, you can use the C-based `hiredis` -parser bound to the official `hiredis` C library. To use `hiredis`, do: +Here are results of `multi_bench.js` which is similar to `redis-benchmark` from the Redis distribution. + +hiredis parser (Lenovo T450s i7-5600U): + + Client count: 5, node version: 2.5.0, server version: 3.0.3, parser: hiredis + PING, 1/5 min/max/avg/p95: 0/ 3/ 0.03/ 0.00 540ms total, 37037.04 ops/sec + PING, 50/5 min/max/avg/p95: 0/ 4/ 0.65/ 1.00 260ms total, 76923.08 ops/sec + SET 4B str, 1/5 min/max/avg/p95: 0/ 20/ 0.04/ 0.00 816ms total, 24509.80 ops/sec + SET 4B str, 50/5 min/max/avg/p95: 0/ 13/ 1.00/ 2.00 401ms total, 49875.31 ops/sec + SET 4B buf, 1/5 min/max/avg/p95: 0/ 4/ 0.06/ 1.00 1293ms total, 15467.90 ops/sec + SET 4B buf, 50/5 min/max/avg/p95: 0/ 5/ 2.58/ 4.00 1033ms total, 19361.08 ops/sec + GET 4B str, 1/5 min/max/avg/p95: 0/ 14/ 0.03/ 0.00 717ms total, 27894.00 ops/sec + GET 4B str, 50/5 min/max/avg/p95: 0/ 3/ 0.62/ 1.00 249ms total, 80321.29 ops/sec + GET 4B buf, 1/5 min/max/avg/p95: 0/ 6/ 0.03/ 0.00 561ms total, 35650.62 ops/sec + GET 4B buf, 50/5 min/max/avg/p95: 0/ 3/ 0.64/ 1.00 259ms total, 77220.08 ops/sec + SET 4KiB str, 1/5 min/max/avg/p95: 0/ 4/ 0.03/ 0.00 678ms total, 29498.53 ops/sec + SET 4KiB str, 50/5 min/max/avg/p95: 0/ 3/ 0.91/ 2.00 364ms total, 54945.05 ops/sec + SET 4KiB buf, 1/5 min/max/avg/p95: 0/ 20/ 0.07/ 1.00 1354ms total, 14771.05 ops/sec + SET 4KiB buf, 50/5 min/max/avg/p95: 0/ 5/ 1.86/ 3.00 744ms total, 26881.72 ops/sec + GET 4KiB str, 1/5 min/max/avg/p95: 0/ 3/ 0.03/ 0.00 575ms total, 34782.61 ops/sec + GET 4KiB str, 50/5 min/max/avg/p95: 0/ 5/ 0.82/ 2.00 327ms total, 61162.08 ops/sec + GET 4KiB buf, 1/5 min/max/avg/p95: 0/ 25/ 0.04/ 0.00 808ms total, 24752.48 ops/sec + GET 4KiB buf, 50/5 min/max/avg/p95: 0/ 4/ 0.92/ 2.00 371ms total, 53908.36 ops/sec + INCR, 1/5 min/max/avg/p95: 0/ 28/ 0.03/ 0.00 556ms total, 35971.22 ops/sec + INCR, 50/5 min/max/avg/p95: 0/ 4/ 0.67/ 1.00 269ms total, 74349.44 ops/sec + LPUSH, 1/5 min/max/avg/p95: 0/ 2/ 0.03/ 0.00 534ms total, 37453.18 ops/sec + LPUSH, 50/5 min/max/avg/p95: 0/ 2/ 0.89/ 2.00 357ms total, 56022.41 ops/sec + LRANGE 10, 1/5 min/max/avg/p95: 0/ 12/ 0.04/ 0.00 829ms total, 24125.45 ops/sec + LRANGE 10, 50/5 min/max/avg/p95: 0/ 3/ 1.04/ 2.00 415ms total, 48192.77 ops/sec + LRANGE 100, 1/5 min/max/avg/p95: 0/ 16/ 0.06/ 1.00 1212ms total, 16501.65 ops/sec + LRANGE 100, 50/5 min/max/avg/p95: 0/ 5/ 1.76/ 3.00 707ms total, 28288.54 ops/sec + SET 4MiB buf, 1/5 min/max/avg/p95: 1/ 22/ 2.66/ 4.00 1335ms total, 374.53 ops/sec + SET 4MiB buf, 50/5 min/max/avg/p95: 13/ 122/ 101.33/ 114.00 1062ms total, 470.81 ops/sec + GET 4MiB str, 1/5 min/max/avg/p95: 3/ 14/ 6.07/ 12.00 607ms total, 164.74 ops/sec + GET 4MiB str, 50/5 min/max/avg/p95: 17/ 431/ 286.75/ 418.00 686ms total, 145.77 ops/sec + GET 4MiB buf, 1/5 min/max/avg/p95: 3/ 38/ 6.83/ 12.95 684ms total, 146.20 ops/sec + GET 4MiB buf, 50/5 min/max/avg/p95: 10/ 273/ 194.07/ 253.90 495ms total, 202.02 ops/sec + +The hiredis and js parser should most of the time be on the same level. The js parser lacks speed for large responses though. +Therefor the hiredis parser is the default used in node_redis. To use `hiredis`, do: npm install hiredis redis -If the `hiredis` npm module is installed, `node_redis` will use it by default. -Otherwise, the pure JavaScript parser will be used. - -If you use `hiredis`, be sure to rebuild it whenever you upgrade your version of -node. There are mysterious failures that can happen between node and native -code modules after a node upgrade. - -Most users find that the JS parser is faster than the `hiredis` parser. Because -of the pain associated with upgrading native code modules, you should only use -`hiredis` if your application needs it. - -hiredis parser: - - Client count: 5, node version: 0.10.32, server version: 2.8.18, parser: hiredis - PING, 1/5 min/max/avg/p95: 0/ 3/ 0.05/ 1.00 1092ms total, 18315.02 ops/sec - PING, 50/5 min/max/avg/p95: 0/ 5/ 0.87/ 2.00 347ms total, 57636.89 ops/sec - SET 4B str, 1/5 min/max/avg/p95: 0/ 2/ 0.06/ 1.00 1151ms total, 17376.19 ops/sec - SET 4B str, 50/5 min/max/avg/p95: 0/ 3/ 0.83/ 2.00 334ms total, 59880.24 ops/sec - SET 4B buf, 1/5 min/max/avg/p95: 0/ 3/ 0.09/ 1.00 1932ms total, 10351.97 ops/sec - SET 4B buf, 50/5 min/max/avg/p95: 0/ 9/ 2.64/ 4.00 1059ms total, 18885.74 ops/sec - GET 4B str, 1/5 min/max/avg/p95: 0/ 1/ 0.06/ 0.00 1185ms total, 16877.64 ops/sec - GET 4B str, 50/5 min/max/avg/p95: 0/ 3/ 0.85/ 2.00 341ms total, 58651.03 ops/sec - GET 4B buf, 1/5 min/max/avg/p95: 0/ 1/ 0.06/ 0.00 1179ms total, 16963.53 ops/sec - GET 4B buf, 50/5 min/max/avg/p95: 0/ 3/ 0.85/ 2.00 340ms total, 58823.53 ops/sec - SET 4KiB str, 1/5 min/max/avg/p95: 0/ 1/ 0.06/ 1.00 1210ms total, 16528.93 ops/sec - SET 4KiB str, 50/5 min/max/avg/p95: 0/ 3/ 0.93/ 2.00 372ms total, 53763.44 ops/sec - SET 4KiB buf, 1/5 min/max/avg/p95: 0/ 1/ 0.10/ 1.00 1967ms total, 10167.77 ops/sec - SET 4KiB buf, 50/5 min/max/avg/p95: 0/ 6/ 2.63/ 4.00 1053ms total, 18993.35 ops/sec - GET 4KiB str, 1/5 min/max/avg/p95: 0/ 6/ 0.06/ 1.00 1176ms total, 17006.80 ops/sec - GET 4KiB str, 50/5 min/max/avg/p95: 0/ 4/ 1.00/ 2.00 399ms total, 50125.31 ops/sec - GET 4KiB buf, 1/5 min/max/avg/p95: 0/ 1/ 0.06/ 1.00 1158ms total, 17271.16 ops/sec - GET 4KiB buf, 50/5 min/max/avg/p95: 0/ 3/ 0.99/ 2.00 398ms total, 50251.26 ops/sec - INCR, 1/5 min/max/avg/p95: 0/ 1/ 0.05/ 0.00 1112ms total, 17985.61 ops/sec - INCR, 50/5 min/max/avg/p95: 0/ 3/ 0.84/ 2.00 339ms total, 58997.05 ops/sec - LPUSH, 1/5 min/max/avg/p95: 0/ 1/ 0.05/ 1.00 1131ms total, 17683.47 ops/sec - LPUSH, 50/5 min/max/avg/p95: 0/ 3/ 0.86/ 2.00 345ms total, 57971.01 ops/sec - LRANGE 10, 1/5 min/max/avg/p95: 0/ 1/ 0.06/ 1.00 1228ms total, 16286.64 ops/sec - LRANGE 10, 50/5 min/max/avg/p95: 0/ 3/ 0.95/ 2.00 382ms total, 52356.02 ops/sec - LRANGE 100, 1/5 min/max/avg/p95: 0/ 1/ 0.08/ 1.00 1567ms total, 12763.24 ops/sec - LRANGE 100, 50/5 min/max/avg/p95: 0/ 6/ 1.68/ 3.00 675ms total, 29629.63 ops/sec - SET 4MiB buf, 1/5 min/max/avg/p95: 1/ 4/ 1.37/ 2.00 692ms total, 722.54 ops/sec - SET 4MiB buf, 50/5 min/max/avg/p95: 3/ 183/ 57.79/ 125.00 605ms total, 826.45 ops/sec - GET 4MiB str, 1/5 min/max/avg/p95: 5/ 16/ 8.14/ 12.95 816ms total, 122.55 ops/sec - GET 4MiB str, 50/5 min/max/avg/p95: 24/ 323/ 202.98/ 309.00 519ms total, 192.68 ops/sec - GET 4MiB buf, 1/5 min/max/avg/p95: 6/ 13/ 8.01/ 11.95 802ms total, 124.69 ops/sec - GET 4MiB buf, 50/5 min/max/avg/p95: 16/ 480/ 203.85/ 435.70 531ms total, 188.32 ops/sec - ## Debugging To get debug output run your `node_redis` application with `NODE_DEBUG=redis`. From 3aa16dd16ae70ae416523710ffdf7d6c94e71a40 Mon Sep 17 00:00:00 2001 From: Ruben Bridgewater Date: Mon, 21 Sep 2015 21:59:23 +0200 Subject: [PATCH 0381/1748] Help wanted --- README.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/README.md b/README.md index 7166567119b..98fd47e8c09 100644 --- a/README.md +++ b/README.md @@ -647,7 +647,7 @@ Therefor the hiredis parser is the default used in node_redis. To use `hiredis`, To get debug output run your `node_redis` application with `NODE_DEBUG=redis`. ## How to Contribute -- open a pull request and then wait for feedback +- Open a pull request or an issue about what you want to implement / change. We're glad for any help! ## Contributors Many [people](https://github.com/NodeRedis/node_redis/graphs/contributors) have have added features and fixed bugs in `node_redis`. Thanks to all of them! From 505b3eb9199d00d69af01d437a95058379f36499 Mon Sep 17 00:00:00 2001 From: Ruben Bridgewater Date: Mon, 21 Sep 2015 22:17:24 +0200 Subject: [PATCH 0382/1748] v.2.0.0 --- package.json | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/package.json b/package.json index 4287fac0521..0ba2852af85 100644 --- a/package.json +++ b/package.json @@ -1,6 +1,6 @@ { "name": "redis", - "version": "1.0.0", + "version": "2.0.0", "description": "Redis client library", "keywords": [ "database", From 07154fce25d8c42b146c2a91aec84053318c1b1d Mon Sep 17 00:00:00 2001 From: Ruben Bridgewater Date: Mon, 21 Sep 2015 22:30:35 +0200 Subject: [PATCH 0383/1748] Fix typo --- test/node_redis.spec.js | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/test/node_redis.spec.js b/test/node_redis.spec.js index 44dfabc8a1c..9325df0c879 100644 --- a/test/node_redis.spec.js +++ b/test/node_redis.spec.js @@ -97,7 +97,7 @@ describe("The node_redis client", function () { redis.createClient(true); throw new Error('failed'); } catch (err) { - assert.equal(err.message, 'unknown type of connection in createClient()'); + assert.equal(err.message, 'Unknown type of connection in createClient()'); } }); From f29193a7e05f94eacb250f5529440878e24f9c29 Mon Sep 17 00:00:00 2001 From: Ruben Bridgewater Date: Mon, 21 Sep 2015 22:37:56 +0200 Subject: [PATCH 0384/1748] Investigate failure --- test/node_redis.spec.js | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/test/node_redis.spec.js b/test/node_redis.spec.js index 9325df0c879..d3aa2b91c91 100644 --- a/test/node_redis.spec.js +++ b/test/node_redis.spec.js @@ -151,13 +151,13 @@ describe("The node_redis client", function () { }); }); - it("misusing the function should eventually throw (no command)", function (done) { + it.skip("misusing the function should eventually throw (no command)", function (done) { var mochaListener = helper.removeMochaListener(); process.once('uncaughtException', function (err) { process.on('uncaughtException', mochaListener); - assert(/ERR Protocol error/.test(err.message)); - assert.equal(err.command, true); + // assert(/ERR Protocol error/.test(err.message)); + // assert.equal(err.command, true); assert.equal(err.code, 'ERR'); done(); }); From 7be7128b2b86c1d79ea1026fe04d93f6ae25c26c Mon Sep 17 00:00:00 2001 From: Ruben Bridgewater Date: Thu, 24 Sep 2015 00:31:55 +0200 Subject: [PATCH 0385/1748] Arguments passed as arrays should not be mutated. Fixes #866 --- changelog.md | 6 ++++++ index.js | 12 ++++++------ test/commands/hmget.spec.js | 6 ++++-- test/commands/multi.spec.js | 12 +++++++++--- 4 files changed, 25 insertions(+), 11 deletions(-) diff --git a/changelog.md b/changelog.md index 75c30eaaa0d..1f71c40190f 100644 --- a/changelog.md +++ b/changelog.md @@ -1,6 +1,12 @@ Changelog ========= +## v2.0.1 - Sep 24, 2015 + +Bugfixes: + +- Fix argument mutation while using the array notation in combination with keys / callbacks ([#866]). (@BridgeAR) + ## v2.0.0 - Sep 21, 2015 This is the biggest release that node_redis had since it was released in 2010. A long list of outstanding bugs has been fixed, so we are very happy to present you redis 2.0 and we highly recommend updating as soon as possible. diff --git a/index.js b/index.js index 597d6972021..ad753c9a4cf 100644 --- a/index.js +++ b/index.js @@ -885,7 +885,7 @@ commands.forEach(function (fullCommand) { return this.send_command(command, key, arg); } if (Array.isArray(arg)) { - arg.unshift(key); + arg = [key].concat(arg); return this.send_command(command, arg, callback); } return this.send_command(command, to_array(arguments)); @@ -895,12 +895,12 @@ commands.forEach(function (fullCommand) { Multi.prototype[command] = function (key, arg, callback) { if (Array.isArray(key)) { if (arg) { - key.push(arg); + key = key.concat([arg]); } this.queue.push([command].concat(key)); } else if (Array.isArray(arg)) { if (callback) { - arg.push(callback); + arg = arg.concat([callback]); } this.queue.push([command, key].concat(arg)); } else { @@ -980,12 +980,12 @@ Multi.prototype.hmset = Multi.prototype.HMSET = function (key, args, callback) { var tmp_args, field; if (Array.isArray(key)) { if (args) { - key.push(args); + key = key.concat([args]); } tmp_args = ['hmset'].concat(key); } else if (Array.isArray(args)) { if (callback) { - args.push(callback); + args = args.concat([callback]); } tmp_args = ['hmset', key].concat(args); } else if (typeof args === "object") { @@ -1029,7 +1029,7 @@ Multi.prototype.exec = Multi.prototype.EXEC = function (callback) { this.wants_buffers = new Array(this.queue.length); // drain queue, callback will catch "QUEUED" or error for (var index = 0; index < this.queue.length; index++) { - var args = this.queue[index].slice(); + var args = this.queue[index].slice(0); var command = args.shift(); var cb; if (typeof args[args.length - 1] === "function") { diff --git a/test/commands/hmget.spec.js b/test/commands/hmget.spec.js index 5f7bc325f2b..5d4e1df3a7e 100644 --- a/test/commands/hmget.spec.js +++ b/test/commands/hmget.spec.js @@ -30,8 +30,10 @@ describe("The 'hmget' method", function () { }); }); - it('allows keys to be specified by passing an array', function (done) { - client.HMGET(hash, ["0123456789", "some manner of key"], function (err, reply) { + it('allows keys to be specified by passing an array without manipulating the array', function (done) { + var data = ["0123456789", "some manner of key"]; + client.HMGET(hash, data, function (err, reply) { + assert.strictEqual(data.length, 2); assert.strictEqual("abcdefghij", reply[0].toString()); assert.strictEqual("a type of value", reply[1].toString()); return done(err); diff --git a/test/commands/multi.spec.js b/test/commands/multi.spec.js index af73235891f..58c9eee0f24 100644 --- a/test/commands/multi.spec.js +++ b/test/commands/multi.spec.js @@ -148,26 +148,32 @@ describe("The 'multi' method", function () { it('allows multiple operations to be performed using constructor with all kinds of syntax', function (done) { var now = Date.now(); + var arr = ["multihmset", "multibar", "multibaz"]; + var arr2 = ['some manner of key', 'otherTypes']; + var arr3 = [5768, "multibarx", "multifoox"]; client.multi([ ["mset", [578, "multibar"], helper.isString('OK')], [["mset", "multifoo2", "multibar2", "multifoo3", "multibar3"], helper.isString('OK')], - ["hmset", ["multihmset", "multibar", "multibaz"]], + ["hmset", arr], [["hmset", "multihmset2", "multibar2", "multifoo3", "multibar3", "test", helper.isString('OK')]], ["hmset", ["multihmset", "multibar", "multifoo", helper.isString('OK')]], - ["hmset", [5768, "multibarx", "multifoox"], helper.isString('OK')], + ["hmset", arr3, helper.isString('OK')], ['hmset', now, {123456789: "abcdefghij", "some manner of key": "a type of value", "otherTypes": 555}], ['hmset', 'key2', {"0123456789": "abcdefghij", "some manner of key": "a type of value", "otherTypes": 999}, helper.isString('OK')], ["hmset", "multihmset", ["multibar", "multibaz"]], ["hmset", "multihmset", ["multibar", "multibaz"], helper.isString('OK')], ]) .hmget(now, 123456789, 'otherTypes') - .hmget('key2', ['some manner of key', 'otherTypes']) + .hmget('key2', arr2, function noop() {}) .hmget(['multihmset2', 'some manner of key', 'multibar3']) .mget('multifoo2', ['multifoo3', 'multifoo'], function(err, res) { assert(res[0], 'multifoo3'); assert(res[1], 'multifoo'); }) .exec(function (err, replies) { + assert.equal(arr.length, 3); + assert.equal(arr2.length, 2); + assert.equal(arr3.length, 3); assert.strictEqual(null, err); assert.equal(replies[10][1], '555'); assert.equal(replies[11][0], 'a type of value'); From f26b64d0204fd12e27161e7ddd7fa48abd133561 Mon Sep 17 00:00:00 2001 From: Ruben Bridgewater Date: Thu, 24 Sep 2015 00:54:54 +0200 Subject: [PATCH 0386/1748] v.2.0.1 --- package.json | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/package.json b/package.json index 0ba2852af85..eb8438cc6e3 100644 --- a/package.json +++ b/package.json @@ -1,6 +1,6 @@ { "name": "redis", - "version": "2.0.0", + "version": "2.0.1", "description": "Redis client library", "keywords": [ "database", From 38281c20b2bab5feef09c48262c70c0e4d34c20e Mon Sep 17 00:00:00 2001 From: Ruben Bridgewater Date: Thu, 24 Sep 2015 12:08:25 +0200 Subject: [PATCH 0387/1748] Fix small issues with hmset & multi constructor --- changelog.md | 9 ++++++++- index.js | 5 +++-- test/commands/multi.spec.js | 14 ++++++++++++-- 3 files changed, 23 insertions(+), 5 deletions(-) diff --git a/changelog.md b/changelog.md index 1f71c40190f..cf2e928969b 100644 --- a/changelog.md +++ b/changelog.md @@ -1,11 +1,18 @@ Changelog ========= +## v2.x.x - xx, 2015 + +Bugfixes: + +- Fix argument mutation while using the array notation with the multi constructor (@BridgeAR) +- Fix multi.hmset key not being type converted if used with an object and key not being a string (@BridgeAR) + ## v2.0.1 - Sep 24, 2015 Bugfixes: -- Fix argument mutation while using the array notation in combination with keys / callbacks ([#866]). (@BridgeAR) +- Fix argument mutation while using the array notation in combination with keys / callbacks ([#866](.)). (@BridgeAR) ## v2.0.0 - Sep 21, 2015 diff --git a/index.js b/index.js index ad753c9a4cf..271b6aa5dba 100644 --- a/index.js +++ b/index.js @@ -860,7 +860,8 @@ function Multi(client, args) { var command, tmp_args; if (Array.isArray(args)) { while (tmp_args = args.shift()) { - command = tmp_args.shift(); + command = tmp_args[0]; + tmp_args = tmp_args.slice(1); if (Array.isArray(command)) { this[command[0]].apply(this, command.slice(1).concat(tmp_args)); } else { @@ -989,10 +990,10 @@ Multi.prototype.hmset = Multi.prototype.HMSET = function (key, args, callback) { } tmp_args = ['hmset', key].concat(args); } else if (typeof args === "object") { - tmp_args = ["hmset", key]; if (typeof key !== "string") { key = key.toString(); } + tmp_args = ["hmset", key]; var fields = Object.keys(args); while (field = fields.shift()) { tmp_args.push(field); diff --git a/test/commands/multi.spec.js b/test/commands/multi.spec.js index 58c9eee0f24..f9661393c58 100644 --- a/test/commands/multi.spec.js +++ b/test/commands/multi.spec.js @@ -151,8 +151,9 @@ describe("The 'multi' method", function () { var arr = ["multihmset", "multibar", "multibaz"]; var arr2 = ['some manner of key', 'otherTypes']; var arr3 = [5768, "multibarx", "multifoox"]; + var arr4 = ["mset", [578, "multibar"], helper.isString('OK')]; client.multi([ - ["mset", [578, "multibar"], helper.isString('OK')], + arr4, [["mset", "multifoo2", "multibar2", "multifoo3", "multibar3"], helper.isString('OK')], ["hmset", arr], [["hmset", "multihmset2", "multibar2", "multifoo3", "multibar3", "test", helper.isString('OK')]], @@ -160,7 +161,7 @@ describe("The 'multi' method", function () { ["hmset", arr3, helper.isString('OK')], ['hmset', now, {123456789: "abcdefghij", "some manner of key": "a type of value", "otherTypes": 555}], ['hmset', 'key2', {"0123456789": "abcdefghij", "some manner of key": "a type of value", "otherTypes": 999}, helper.isString('OK')], - ["hmset", "multihmset", ["multibar", "multibaz"]], + ["HMSET", "multihmset", ["multibar", "multibaz"]], ["hmset", "multihmset", ["multibar", "multibaz"], helper.isString('OK')], ]) .hmget(now, 123456789, 'otherTypes') @@ -174,6 +175,7 @@ describe("The 'multi' method", function () { assert.equal(arr.length, 3); assert.equal(arr2.length, 2); assert.equal(arr3.length, 3); + assert.equal(arr4.length, 3); assert.strictEqual(null, err); assert.equal(replies[10][1], '555'); assert.equal(replies[11][0], 'a type of value'); @@ -186,6 +188,14 @@ describe("The 'multi' method", function () { }); }); + it('converts a non string key to a string', function(done) { + // TODO: Converting the key might change soon again. + client.multi().hmset(true, { + test: 123, + bar: 'baz' + }).exec(done); + }); + it('allows multiple operations to be performed using a chaining API', function (done) { client.multi() .mset('some', '10', 'keys', '20') From 4b100b8b64fd142d40db7c0c0662966980f9dbdb Mon Sep 17 00:00:00 2001 From: Chris Breneman Date: Tue, 15 Sep 2015 13:22:35 -0400 Subject: [PATCH 0388/1748] Flush queue when .end() is called --- index.js | 3 +++ 1 file changed, 3 insertions(+) diff --git a/index.js b/index.js index 271b6aa5dba..9a8cd2c0f72 100644 --- a/index.js +++ b/index.js @@ -848,6 +848,9 @@ RedisClient.prototype.end = function () { } this.stream.on("error", function noop(){}); + // Flush queue + this.flush_and_error("Redis connection ended."); + this.connected = false; this.ready = false; this.closing = true; From bd4fca130dc7ed7ed9809c3dab42aed9d87c0fc9 Mon Sep 17 00:00:00 2001 From: Ruben Bridgewater Date: Thu, 24 Sep 2015 13:27:32 +0200 Subject: [PATCH 0389/1748] Make .end flush optional and add some tests --- README.md | 6 ++++-- changelog.md | 6 +++++- index.js | 8 +++++--- test/commands/multi.spec.js | 8 ++++++++ test/node_redis.spec.js | 26 ++++++++++++++++++++++++++ 5 files changed, 48 insertions(+), 6 deletions(-) diff --git a/README.md b/README.md index 98fd47e8c09..06e789c76bd 100644 --- a/README.md +++ b/README.md @@ -212,11 +212,13 @@ NOTE: Your call to `client.auth()` should not be inside the ready handler. If you are doing this wrong, `client` will emit an error that looks something like this `Error: Ready check failed: ERR operation not permitted`. -## client.end() +## client.end([flush]) Forcibly close the connection to the Redis server. Note that this does not wait until all replies have been parsed. If you want to exit cleanly, call `client.quit()` to send the `QUIT` command after you have handled all replies. +If flush is set to true, all commands will be rejected instead of ignored after using `.end`. + This example closes the connection to the Redis server before the replies have been read. You probably don't want to do this: @@ -227,7 +229,7 @@ var redis = require("redis"), client.set("foo_rand000000000000", "some fantastic value"); client.end(); // No further commands will be processed client.get("foo_rand000000000000", function (err, reply) { - // This won't be called anymore + // This won't be called anymore, since flush has not been set to true! console.log(err); }); ``` diff --git a/changelog.md b/changelog.md index cf2e928969b..5262d7bebca 100644 --- a/changelog.md +++ b/changelog.md @@ -1,7 +1,11 @@ Changelog ========= -## v2.x.x - xx, 2015 +## v2.1.0 - xx, 2015 + +Features: + +- Add optional flush parameter to `.end`. If set to true, commands fired after using .end are going to be rejected instead of being ignored. (@crispy1989) Bugfixes: diff --git a/index.js b/index.js index 9a8cd2c0f72..ab7d11e3353 100644 --- a/index.js +++ b/index.js @@ -838,7 +838,7 @@ RedisClient.prototype.pub_sub_command = function (command_obj) { } }; -RedisClient.prototype.end = function () { +RedisClient.prototype.end = function (flush) { this.stream._events = {}; // Clear retry_timer @@ -848,8 +848,10 @@ RedisClient.prototype.end = function () { } this.stream.on("error", function noop(){}); - // Flush queue - this.flush_and_error("Redis connection ended."); + // Flush queue if wanted + if (flush) { + this.flush_and_error("Redis connection ended."); + } this.connected = false; this.ready = false; diff --git a/test/commands/multi.spec.js b/test/commands/multi.spec.js index f9661393c58..bd3d10979db 100644 --- a/test/commands/multi.spec.js +++ b/test/commands/multi.spec.js @@ -196,6 +196,14 @@ describe("The 'multi' method", function () { }).exec(done); }); + it('runs a multi without any further commands', function(done) { + client.multi().exec(function(err, res) { + assert.strictEqual(err, null); + assert.strictEqual(res.length, 0); + done(); + }); + }); + it('allows multiple operations to be performed using a chaining API', function (done) { client.multi() .mset('some', '10', 'keys', '20') diff --git a/test/node_redis.spec.js b/test/node_redis.spec.js index d3aa2b91c91..2dabc68c8dd 100644 --- a/test/node_redis.spec.js +++ b/test/node_redis.spec.js @@ -174,6 +174,32 @@ describe("The node_redis client", function () { }); + describe(".end", function () { + + it('used without flush', function(done) { + var err = null; + client.set('foo', 'bar'); + client.end(); + client.get('foo', function(err, res) { + err = new Error('failed'); + }); + setTimeout(function() { + done(err); + }, 200); + }); + + it('used with flush set to true', function(done) { + client.set('foo', 'bar'); + client.end(); + client.get('foo', function(err, res) { + assert.strictEqual(err.command, 'GET'); + assert.strictEqual(err.message, "GET can't be processed. The connection has already been closed."); + done(); + }); + }); + + }); + describe("commands after using .quit should fail", function () { it("return an error in the callback", function (done) { From ff47dc3ce848538bfdba2db5b28540cf91ab8027 Mon Sep 17 00:00:00 2001 From: Ruben Bridgewater Date: Fri, 25 Sep 2015 00:51:38 +0200 Subject: [PATCH 0390/1748] Improve multi bench It will now print the total time elapsed and start a redis server if none is running and closes it afterwards again --- benchmarks/multi_bench.js | 55 ++++++++++++++++++++++++++++++--------- 1 file changed, 43 insertions(+), 12 deletions(-) diff --git a/benchmarks/multi_bench.js b/benchmarks/multi_bench.js index ea3da38d8d2..a1d470611a7 100644 --- a/benchmarks/multi_bench.js +++ b/benchmarks/multi_bench.js @@ -1,15 +1,21 @@ 'use strict'; -var redis = require("../index"), - metrics = require("metrics"), - num_clients = parseInt(process.argv[2], 10) || 5, - num_requests = 20000, - tests = [], - versions_logged = false, - client_options = { - return_buffers: false - }, - small_str, large_str, small_buf, large_buf, very_large_str, very_large_buf; +var path = require('path'); +var RedisProcess = require("../test/lib/redis-process"); +var rp; +var redis = require("../index"); +var totalTime = 0; +var metrics = require("metrics"); +var num_clients = parseInt(process.argv[2], 10) || 5; +var num_requests = 20000; +var tests = []; +var versions_logged = false; +var client_options = { + return_buffers: false, + max_attempts: 4, + parser: process.argv.indexOf('parser=javascript') === -1 ? 'hiredis' : 'javascript' + }; +var small_str, large_str, small_buf, large_buf, very_large_str, very_large_buf; function lpad(input, len, chr) { var str = input.toString(); @@ -57,7 +63,7 @@ Test.prototype.run = function (callback) { Test.prototype.new_client = function (id) { var self = this, new_client; - new_client = redis.createClient(6379, "127.0.0.1", this.client_options); + new_client = redis.createClient(this.client_options); new_client.create_time = Date.now(); new_client.on("connect", function () { @@ -77,6 +83,24 @@ Test.prototype.new_client = function (id) { } }); + // If no redis server is running, start one + new_client.on("error", function(err) { + if (err.code === 'CONNECTION_BROKEN') { + throw err; + } + if (rp) { + return; + } + rp = true; + var conf = '../test/conf/redis.conf'; + RedisProcess.start(function (err, _rp) { + if (err) { + throw err; + } + rp = _rp; + }, path.resolve(__dirname, conf)); + }); + self.clients[id] = new_client; }; @@ -133,6 +157,7 @@ Test.prototype.send_next = function () { Test.prototype.print_stats = function () { var duration = Date.now() - this.test_start; + totalTime += duration; console.log("min/max/avg/p95: " + this.command_latency.print_line() + " " + lpad(duration, 6) + "ms total, " + lpad((this.num_requests / (duration / 1000)).toFixed(2), 8) + " ops/sec"); @@ -199,8 +224,14 @@ function next() { test.run(function () { next(); }); + } else if (rp) { + // Stop the redis process if started by the benchmark + rp.stop(function() { + rp = undefined; + next(); + }); } else { - console.log("End of tests."); + console.log("End of tests. Total time elapsed:", totalTime, 'ms'); process.exit(0); } } From 5f261c5823d9062c43ebb9845c86d783d067ca68 Mon Sep 17 00:00:00 2001 From: Ruben Bridgewater Date: Fri, 25 Sep 2015 00:54:16 +0200 Subject: [PATCH 0391/1748] Minor changes Move utility functions in lib/utils.js Improve the js parser in cases the buffer is incomplete Rename lib/parser to lib/parsers Fix smaller issues with test suite and fix parser errors not being catched Fixed wrong test for the new .end flush parameter Fixed test suite options being partly mutated Add some more tests --- index.js | 64 +++++++-------------------- lib/{parser => parsers}/hiredis.js | 8 +++- lib/{parser => parsers}/javascript.js | 26 ++++++----- lib/to_array.js | 14 ------ lib/utils.js | 53 ++++++++++++++++++++++ package.json | 2 +- test/commands/geoadd.spec.js.future | 34 ++++++++++++++ test/commands/set.spec.js | 10 +++++ test/commands/sync.spec.js | 43 ++++++++++++++++++ test/helper.js | 6 +-- test/lib/config.js | 3 +- test/node_redis.spec.js | 49 ++++++++++++-------- test/parser/javascript.spec.js | 2 +- 13 files changed, 212 insertions(+), 102 deletions(-) rename lib/{parser => parsers}/hiredis.js (80%) rename lib/{parser => parsers}/javascript.js (96%) delete mode 100644 lib/to_array.js create mode 100644 lib/utils.js create mode 100644 test/commands/geoadd.spec.js.future create mode 100644 test/commands/sync.spec.js diff --git a/index.js b/index.js index ab7d11e3353..dcdbaf96bb1 100644 --- a/index.js +++ b/index.js @@ -3,8 +3,8 @@ var net = require("net"), URL = require("url"), util = require("util"), + utils = require("./lib/utils"), Queue = require("./lib/queue"), - to_array = require("./lib/to_array"), events = require("events"), parsers = [], // This static list of commands is updated from time to time. @@ -23,14 +23,13 @@ exports.debug_mode = /\bredis\b/i.test(process.env.NODE_DEBUG); // hiredis might not be installed try { - require("./lib/parser/hiredis"); - parsers.push(require("./lib/parser/hiredis")); + parsers.push(require("./lib/parsers/hiredis")); } catch (err) { /* istanbul ignore next: won't be reached with tests */ debug("Hiredis parser not installed."); } -parsers.push(require("./lib/parser/javascript")); +parsers.push(require("./lib/parsers/javascript")); function RedisClient(stream, options) { options = options || {}; @@ -135,6 +134,7 @@ RedisClient.prototype.flush_and_error = function (error) { while (command_obj = this.offline_queue.shift()) { if (typeof command_obj.callback === "function") { + error.command = command_obj.command.toUpperCase(); command_obj.callback(error); } } @@ -142,6 +142,7 @@ RedisClient.prototype.flush_and_error = function (error) { while (command_obj = this.command_queue.shift()) { if (typeof command_obj.callback === "function") { + error.command = command_obj.command.toUpperCase(); command_obj.callback(error); } } @@ -530,41 +531,6 @@ RedisClient.prototype.return_error = function (err) { } }; -// hgetall converts its replies to an Object. If the reply is empty, null is returned. -function reply_to_object(reply) { - var obj = {}, j, jl, key, val; - - if (reply.length === 0 || !Array.isArray(reply)) { - return null; - } - - for (j = 0, jl = reply.length; j < jl; j += 2) { - key = reply[j].toString('binary'); - val = reply[j + 1]; - obj[key] = val; - } - - return obj; -} - -function reply_to_strings(reply) { - var i; - - if (Buffer.isBuffer(reply)) { - return reply.toString(); - } - - if (Array.isArray(reply)) { - for (i = 0; i < reply.length; i++) { - // Recusivly call the function as slowlog returns deep nested replies - reply[i] = reply_to_strings(reply[i]); - } - return reply; - } - - return reply; -} - RedisClient.prototype.return_reply = function (reply) { var command_obj, len, type, timestamp, argindex, args, queue_len; @@ -598,12 +564,12 @@ RedisClient.prototype.return_reply = function (reply) { if (this.options.detect_buffers && command_obj.buffer_args === false) { // If detect_buffers option was specified, then the reply from the parser will be Buffers. // If this command did not use Buffer arguments, then convert the reply to Strings here. - reply = reply_to_strings(reply); + reply = utils.reply_to_strings(reply); } // TODO - confusing and error-prone that hgetall is special cased in two places if (reply && 'hgetall' === command_obj.command) { - reply = reply_to_object(reply); + reply = utils.reply_to_object(reply); } } @@ -614,7 +580,7 @@ RedisClient.prototype.return_reply = function (reply) { } else if (this.pub_sub_mode || command_obj && command_obj.sub_command) { if (Array.isArray(reply)) { if (!this.options.return_buffers && (!command_obj || this.options.detect_buffers && command_obj.buffer_args === false)) { - reply = reply_to_strings(reply); + reply = utils.reply_to_strings(reply); } type = reply[0].toString(); @@ -850,7 +816,7 @@ RedisClient.prototype.end = function (flush) { // Flush queue if wanted if (flush) { - this.flush_and_error("Redis connection ended."); + this.flush_and_error(new Error("The command can't be processed. The connection has already been closed.")); } this.connected = false; @@ -894,7 +860,7 @@ commands.forEach(function (fullCommand) { arg = [key].concat(arg); return this.send_command(command, arg, callback); } - return this.send_command(command, to_array(arguments)); + return this.send_command(command, utils.to_array(arguments)); }; RedisClient.prototype[command.toUpperCase()] = RedisClient.prototype[command]; @@ -910,7 +876,7 @@ commands.forEach(function (fullCommand) { } this.queue.push([command, key].concat(arg)); } else { - this.queue.push([command].concat(to_array(arguments))); + this.queue.push([command].concat(utils.to_array(arguments))); } return this; }; @@ -979,7 +945,7 @@ RedisClient.prototype.hmset = RedisClient.prototype.HMSET = function (key, args, } return this.send_command("hmset", tmp_args, callback); } - return this.send_command("hmset", to_array(arguments)); + return this.send_command("hmset", utils.to_array(arguments)); }; Multi.prototype.hmset = Multi.prototype.HMSET = function (key, args, callback) { @@ -1008,7 +974,7 @@ Multi.prototype.hmset = Multi.prototype.HMSET = function (key, args, callback) { tmp_args.push(callback); } } else { - tmp_args = to_array(arguments); + tmp_args = utils.to_array(arguments); tmp_args.unshift("hmset"); } this.queue.push(tmp_args); @@ -1087,11 +1053,11 @@ Multi.prototype.execute_callback = function (err, replies) { replies[i].command = args[0].toUpperCase(); } else if (replies[i]) { if (this._client.options.detect_buffers && this.wants_buffers[i + 1] === false) { - replies[i] = reply_to_strings(replies[i]); + replies[i] = utils.reply_to_strings(replies[i]); } if (args[0] === "hgetall") { // TODO - confusing and error-prone that hgetall is special cased in two places - replies[i] = reply_to_object(replies[i]); + replies[i] = utils.reply_to_object(replies[i]); } } diff --git a/lib/parser/hiredis.js b/lib/parsers/hiredis.js similarity index 80% rename from lib/parser/hiredis.js rename to lib/parsers/hiredis.js index 765304bec53..5be6bf2ac9e 100644 --- a/lib/parser/hiredis.js +++ b/lib/parsers/hiredis.js @@ -18,7 +18,13 @@ HiredisReplyParser.prototype.execute = function (data) { var reply; this.reader.feed(data); while (true) { - reply = this.reader.get(); + try { + reply = this.reader.get(); + } catch (err) { + // Protocol errors land here + this.send_error(err); + break; + } if (reply === undefined) { break; diff --git a/lib/parser/javascript.js b/lib/parsers/javascript.js similarity index 96% rename from lib/parser/javascript.js rename to lib/parsers/javascript.js index 4ce4168c43f..dbbe658b6b2 100644 --- a/lib/parser/javascript.js +++ b/lib/parsers/javascript.js @@ -30,14 +30,13 @@ ReplyParser.prototype._parseResult = function (type) { end = this._packetEndOffset() - 1; start = this._offset; - // include the delimiter - this._offset = end + 2; - if (end > this._buffer.length) { - this._offset = start; throw new IncompleteReadBuffer("Wait for more data."); } + // include the delimiter + this._offset = end + 2; + if (type === 45) { return new Error(this._buffer.toString(this._encoding, start, end)); } else if (this.return_buffers) { @@ -49,14 +48,13 @@ ReplyParser.prototype._parseResult = function (type) { end = this._packetEndOffset() - 1; start = this._offset; - // include the delimiter - this._offset = end + 2; - if (end > this._buffer.length) { - this._offset = start; throw new IncompleteReadBuffer("Wait for more data."); } + // include the delimiter + this._offset = end + 2; + // return the coerced numeric value return +this._buffer.toString('ascii', start, end); } else if (type === 36) { // $ @@ -74,14 +72,13 @@ ReplyParser.prototype._parseResult = function (type) { end = this._offset + packetHeader.size; start = this._offset; - // set the offset to after the delimiter - this._offset = end + 2; - if (end > this._buffer.length) { - this._offset = offset; throw new IncompleteReadBuffer("Wait for more data."); } + // set the offset to after the delimiter + this._offset = end + 2; + if (this.return_buffers) { return this._buffer.slice(start, end); } @@ -157,6 +154,11 @@ ReplyParser.prototype.execute = function (buffer) { ret = this._parseResult(type); this.send_reply(ret); + } else if (type === 10 || type === 13) { + break; + } else { + var err = new Error('Protocol error, got "' + String.fromCharCode(type) + '" as reply type byte'); + this.send_error(err); } } catch (err) { // catch the error (not enough data), rewind, and wait diff --git a/lib/to_array.js b/lib/to_array.js deleted file mode 100644 index 87804f8c3b2..00000000000 --- a/lib/to_array.js +++ /dev/null @@ -1,14 +0,0 @@ -'use strict'; - -function to_array(args) { - var len = args.length, - arr = new Array(len), i; - - for (i = 0; i < len; i += 1) { - arr[i] = args[i]; - } - - return arr; -} - -module.exports = to_array; diff --git a/lib/utils.js b/lib/utils.js new file mode 100644 index 00000000000..42ae211c3a2 --- /dev/null +++ b/lib/utils.js @@ -0,0 +1,53 @@ +'use strict'; + +// hgetall converts its replies to an Object. If the reply is empty, null is returned. +function replyToObject(reply) { + var obj = {}, j, jl, key, val; + + if (reply.length === 0 || !Array.isArray(reply)) { + return null; + } + + for (j = 0, jl = reply.length; j < jl; j += 2) { + key = reply[j].toString('binary'); + val = reply[j + 1]; + obj[key] = val; + } + + return obj; +} + +function replyToStrings(reply) { + var i; + + if (Buffer.isBuffer(reply)) { + return reply.toString(); + } + + if (Array.isArray(reply)) { + for (i = 0; i < reply.length; i++) { + // Recusivly call the function as slowlog returns deep nested replies + reply[i] = replyToStrings(reply[i]); + } + return reply; + } + + return reply; +} + +function toArray(args) { + var len = args.length, + arr = new Array(len), i; + + for (i = 0; i < len; i += 1) { + arr[i] = args[i]; + } + + return arr; +} + +module.exports = { + reply_to_strings: replyToStrings, + reply_to_object: replyToObject, + to_array: toArray +}; diff --git a/package.json b/package.json index eb8438cc6e3..208bdbbbb2c 100644 --- a/package.json +++ b/package.json @@ -20,7 +20,7 @@ "devDependencies": { "coveralls": "^2.11.2", "jshint": "^2.8.0", - "metrics": ">=0.1.5", + "metrics": "^0.1.9", "mocha": "^2.3.2", "nyc": "^3.2.2", "optional-dev-dependency": "^1.1.0", diff --git a/test/commands/geoadd.spec.js.future b/test/commands/geoadd.spec.js.future new file mode 100644 index 00000000000..710b528f809 --- /dev/null +++ b/test/commands/geoadd.spec.js.future @@ -0,0 +1,34 @@ +'use strict'; + +var config = require("../lib/config"); +var helper = require("../helper"); +var redis = config.redis; + +describe("The 'getoadd' method", function () { + + helper.allTests(function(parser, ip, args) { + + describe("using " + parser + " and " + ip, function () { + var client; + + beforeEach(function (done) { + client = redis.createClient.apply(redis.createClient, args); + client.once("connect", function () { + client.flushdb(done); + }); + }); + + it('returns 1 if the key exists', function (done) { + client.geoadd("mycity:21:0:location", "13.361389","38.115556","COR", function(err, res) { + console.log(err, res); + // geoadd is still in the unstable branch. As soon as it reaches the stable one, activate this test + done(); + }); + }); + + afterEach(function () { + client.end(); + }); + }); + }); +}); diff --git a/test/commands/set.spec.js b/test/commands/set.spec.js index c295af1d3da..dbdd3103e9b 100644 --- a/test/commands/set.spec.js +++ b/test/commands/set.spec.js @@ -88,6 +88,16 @@ describe("The 'set' method", function () { }); }, 100); }); + + it("sets the value correctly with the array syntax", function (done) { + client.set([key, value]); + setTimeout(function () { + client.get(key, function (err, res) { + helper.isString(value)(err, res); + done(); + }); + }, 100); + }); }); describe("with undefined 'key' and missing 'value' parameter", function () { diff --git a/test/commands/sync.spec.js b/test/commands/sync.spec.js new file mode 100644 index 00000000000..7e7702c8b56 --- /dev/null +++ b/test/commands/sync.spec.js @@ -0,0 +1,43 @@ +'use strict'; + +var assert = require('assert'); +var config = require("../lib/config"); +var helper = require("../helper"); +var redis = config.redis; + +describe("The 'sync' method", function () { + + helper.allTests(function(parser, ip, args) { + + describe("using " + parser + " and " + ip, function () { + var client; + + beforeEach(function (done) { + client = redis.createClient.apply(redis.createClient, args); + client.once("connect", function () { + client.flushdb(done); + }); + }); + + // This produces a parser error + // Protocol error, got "K" as reply type byte + // I'm uncertain if this is correct behavior or not + it('try to sync with the server and fail other commands', function (done) { + client.on('error', function(err) { + assert.equal(err.message, 'Protocol error, got "K" as reply type byte'); + assert.equal(err.command, 'SET'); + done(); + }); + client.sync(function(err, res) { + assert.equal(err, null); + assert(!!res); + }); + client.set('foo', 'bar'); + }); + + afterEach(function () { + client.end(); + }); + }); + }); +}); diff --git a/test/helper.js b/test/helper.js index 776c2ca3ee6..a9f66d4a40c 100644 --- a/test/helper.js +++ b/test/helper.js @@ -31,12 +31,10 @@ module.exports = { redisProcess: function () { return rp; }, - stopRedis: function (done) { + stopRedis: function(done) { rp.stop(done); }, - startRedis: function (conf, done) { - startRedis(conf, done); - }, + startRedis: startRedis, isNumber: function (expected, done) { return function (err, results) { assert.strictEqual(null, err, "expected " + expected + ", got error: " + err); diff --git a/test/lib/config.js b/test/lib/config.js index d42dc21c9a7..c6f1b25dae3 100644 --- a/test/lib/config.js +++ b/test/lib/config.js @@ -13,7 +13,8 @@ var config = { }, configureClient: function (parser, ip, opts) { var args = []; - opts = opts || {}; + // Do not manipulate the opts => copy them each time + opts = opts ? JSON.parse(JSON.stringify(opts)) : {}; if (ip.match(/\.sock/)) { args.push(ip); diff --git a/test/node_redis.spec.js b/test/node_redis.spec.js index 2dabc68c8dd..34ba79e3dd1 100644 --- a/test/node_redis.spec.js +++ b/test/node_redis.spec.js @@ -156,8 +156,8 @@ describe("The node_redis client", function () { process.once('uncaughtException', function (err) { process.on('uncaughtException', mochaListener); - // assert(/ERR Protocol error/.test(err.message)); - // assert.equal(err.command, true); + assert(/ERR Protocol error/.test(err.message)); + assert.equal(err.command, true); assert.equal(err.code, 'ERR'); done(); }); @@ -177,25 +177,36 @@ describe("The node_redis client", function () { describe(".end", function () { it('used without flush', function(done) { - var err = null; - client.set('foo', 'bar'); - client.end(); - client.get('foo', function(err, res) { - err = new Error('failed'); - }); - setTimeout(function() { - done(err); - }, 200); + var end = helper.callFuncAfter(function() { + done(new Error('failed')); + }, 20); + var cb = function(err, res) { + assert.equal(err.message, "SET can't be processed. The connection has already been closed."); + end(); + }; + for (var i = 0; i < 20; i++) { + if (i === 10) { + client.end(); + } + client.set('foo', 'bar', cb); + } + setTimeout(done, 250); }); it('used with flush set to true', function(done) { - client.set('foo', 'bar'); - client.end(); - client.get('foo', function(err, res) { - assert.strictEqual(err.command, 'GET'); - assert.strictEqual(err.message, "GET can't be processed. The connection has already been closed."); + var end = helper.callFuncAfter(function() { done(); - }); + }, 20); + var cb = function(err, res) { + assert(/The connection has already been closed./.test(err.message)); + end(); + }; + for (var i = 0; i < 20; i++) { + if (i === 10) { + client.end(true); + } + client.set('foo', 'bar', cb); + } }); }); @@ -730,7 +741,7 @@ describe("The node_redis client", function () { describe('retry_max_delay', function () { var client; var args = config.configureClient(parser, ip, { - retry_max_delay: 1 + retry_max_delay: 1 // ms }); it("sets upper bound on how long client waits before reconnecting", function (done) { @@ -747,7 +758,7 @@ describe("The node_redis client", function () { } else { client.end(); var lasted = new Date().getTime() - time; - assert.ok(lasted < 1000); + assert.ok(lasted < 50); return done(); } }); diff --git a/test/parser/javascript.spec.js b/test/parser/javascript.spec.js index 19df991675b..491a292ab1f 100644 --- a/test/parser/javascript.spec.js +++ b/test/parser/javascript.spec.js @@ -1,7 +1,7 @@ 'use strict'; var assert = require('assert'); -var Parser = require("../../lib/parser/javascript").Parser; +var Parser = require("../../lib/parsers/javascript").Parser; var config = require("../lib/config"); var redis = config.redis; From db8c6e3bc2511bfb3fb20637ef4c768a4c78fa99 Mon Sep 17 00:00:00 2001 From: Ruben Bridgewater Date: Fri, 25 Sep 2015 02:23:19 +0200 Subject: [PATCH 0392/1748] Skip test until the issue is fixed --- test/commands/sync.spec.js | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/test/commands/sync.spec.js b/test/commands/sync.spec.js index 7e7702c8b56..c8aa5df40ed 100644 --- a/test/commands/sync.spec.js +++ b/test/commands/sync.spec.js @@ -20,9 +20,10 @@ describe("The 'sync' method", function () { }); // This produces a parser error - // Protocol error, got "K" as reply type byte + // "Protocol error, got "K" as reply type byte" // I'm uncertain if this is correct behavior or not - it('try to sync with the server and fail other commands', function (done) { + // TODO: Fix the command queue state error occuring + it.skip('try to sync with the server and fail other commands', function (done) { client.on('error', function(err) { assert.equal(err.message, 'Protocol error, got "K" as reply type byte'); assert.equal(err.command, 'SET'); From 29b31f749a82ed016c9b6ff7fce521038738e69d Mon Sep 17 00:00:00 2001 From: Ruben Bridgewater Date: Wed, 30 Sep 2015 01:57:58 +0200 Subject: [PATCH 0393/1748] Add a better promise documentation and add some tests --- README.md | 19 +++++++++++++++++++ package.json | 3 ++- test/commands/mget.spec.js | 10 ++++++++++ test/commands/multi.spec.js | 35 +++++++++++++++++++++++++++++++++++ test/lib/config.js | 5 +++++ test/lib/redis-process.js | 21 +++++++++------------ 6 files changed, 80 insertions(+), 13 deletions(-) diff --git a/README.md b/README.md index 06e789c76bd..a03106eabb1 100644 --- a/README.md +++ b/README.md @@ -53,11 +53,30 @@ Note that the API is entire asynchronous. To get data back from the server, you'll need to use a callback. The return value from most of the API is a backpressure indicator. +### Promises + You can also use node_redis with promises by promisifying node_redis with [bluebird](https://github.com/petkaantonov/bluebird) as in: ```js var redis = require('redis'); bluebird.promisifyAll(redis.RedisClient.prototype); +bluebird.promisifyAll(redis.Multi.prototype); +``` + +It'll add a *Async* to all node_redis functions (e.g. return client.getAsync().then()) + +```js +// We expect a value 'foo': 'bar' to be present +// So instead of writing client.get('foo', cb); you have to write: +return client.getAsync('foo').then(function(res) { + console.log(res); // => 'bar' +}); + +// Using multi with promises looks like: + +return client.multi().get('foo').execAsync().then(function(res) { + console.log(res); // => 'bar' +}); ``` ### Sending Commands diff --git a/package.json b/package.json index 208bdbbbb2c..15953e49a74 100644 --- a/package.json +++ b/package.json @@ -26,7 +26,8 @@ "optional-dev-dependency": "^1.1.0", "tcp-port-used": "^0.1.2", "uuid": "^2.0.1", - "win-spawn": "^2.0.0" + "win-spawn": "^2.0.0", + "bluebird": "^2.10.0" }, "repository": { "type": "git", diff --git a/test/commands/mget.spec.js b/test/commands/mget.spec.js index 082721e3864..54f48a7cffa 100644 --- a/test/commands/mget.spec.js +++ b/test/commands/mget.spec.js @@ -52,6 +52,16 @@ describe("The 'mget' method", function () { }); }); + it('handles fetching multiple keys, when some keys do not exist promisified', function () { + return client.MGETAsync("mget keys 1", ["some random shit", "mget keys 2", "mget keys 3"]).then(function (results) { + assert.strictEqual(4, results.length); + assert.strictEqual("mget val 1", results[0].toString()); + assert.strictEqual(null, results[1]); + assert.strictEqual("mget val 2", results[2].toString()); + assert.strictEqual("mget val 3", results[3].toString()); + }); + }); + afterEach(function () { client.end(); }); diff --git a/test/commands/multi.spec.js b/test/commands/multi.spec.js index bd3d10979db..5ce3fa82b0e 100644 --- a/test/commands/multi.spec.js +++ b/test/commands/multi.spec.js @@ -38,6 +38,12 @@ describe("The 'multi' method", function () { done(); }); }); + + it("reports an error if promisified", function () { + return client.multi().execAsync().catch(function(err) { + assert(err.message.match(/The connection has already been closed/)); + }); + }); }); describe("when connected", function () { @@ -238,6 +244,22 @@ describe("The 'multi' method", function () { }); }); + it('allows multiple commands to work the same as normal to be performed using a chaining API promisified', function () { + return client.multi() + .mset(['some', '10', 'keys', '20']) + .incr(['some', helper.isNumber(11)]) + .incr(['keys'], helper.isNumber(21)) + .mget('some', 'keys') + .execAsync() + .then(function (replies) { + assert.equal('OK', replies[0]); + assert.equal(11, replies[1]); + assert.equal(21, replies[2]); + assert.equal(11, replies[3][0].toString()); + assert.equal(21, replies[3][1].toString()); + }); + }); + it('allows an array to be provided indicating multiple operations to perform', function (done) { // test nested multi-bulk replies with nulls. client.multi([ @@ -299,6 +321,19 @@ describe("The 'multi' method", function () { }); }); + it('reports multiple exceptions when they occur (while EXEC is running) promisified', function () { + return client.multi().config("bar").debug("foo").eval("return {err='this is an error'}", 0).execAsync().then(function (reply) { + assert.strictEqual(reply.length, 3); + assert.equal(reply[0].code, 'ERR'); + assert.equal(reply[0].command, 'CONFIG'); + assert.equal(reply[2].code, undefined); + assert.equal(reply[2].command, 'EVAL'); + assert(/^this is an error/.test(reply[2].message)); + assert(/^ERR/.test(reply[0].message), "Error message should begin with ERR"); + assert(/^ERR/.test(reply[1].message), "Error message should begin with ERR"); + }); + }); + it('reports multiple exceptions when they occur (while EXEC is running) and calls cb', function (done) { var multi = client.multi(); multi.config("bar", helper.isError()); diff --git a/test/lib/config.js b/test/lib/config.js index c6f1b25dae3..ed7ef640c5d 100644 --- a/test/lib/config.js +++ b/test/lib/config.js @@ -3,6 +3,11 @@ // helpers for configuring a redis client in // its various modes, ipV6, ipV4, socket. var redis = require('../../index'); +var bluebird = require('bluebird'); + +// Promisify everything +bluebird.promisifyAll(redis.RedisClient.prototype); +bluebird.promisifyAll(redis.Multi.prototype); var config = { redis: redis, diff --git a/test/lib/redis-process.js b/test/lib/redis-process.js index 60826484520..465afdbf2ef 100644 --- a/test/lib/redis-process.js +++ b/test/lib/redis-process.js @@ -15,18 +15,15 @@ function waitForRedis (available, cb) { var ipV4 = false; var id = setInterval(function () { - tcpPortUsed.check(config.PORT, '127.0.0.1') - .then(function (_ipV4) { - ipV4 = _ipV4; - return tcpPortUsed.check(config.PORT, '::1'); - }) - .then(function (ipV6) { - if (ipV6 === available && ipV4 === available && - fs.existsSync('/tmp/redis.sock') === available) { - clearInterval(id); - return cb(); - } - }); + tcpPortUsed.check(config.PORT, '127.0.0.1').then(function (_ipV4) { + ipV4 = _ipV4; + return tcpPortUsed.check(config.PORT, '::1'); + }).then(function (ipV6) { + if (ipV6 === available && ipV4 === available && fs.existsSync('/tmp/redis.sock') === available) { + clearInterval(id); + return cb(); + } + }); }, 100); } From fba050802b643661a89605320fcbb1986397ca68 Mon Sep 17 00:00:00 2001 From: Ruben Bridgewater Date: Wed, 30 Sep 2015 02:03:37 +0200 Subject: [PATCH 0394/1748] Move command out of the index.js --- index.js | 42 ++++++++++++-------------------------- lib/command.js | 13 ++++++++++++ lib/parsers/javascript.js | 10 ++++----- lib/utils.js | 14 ++++++++++++- test/commands/sync.spec.js | 4 ++-- test/helper.js | 14 ++++++------- 6 files changed, 53 insertions(+), 44 deletions(-) create mode 100644 lib/command.js diff --git a/index.js b/index.js index dcdbaf96bb1..8bc2b72430a 100644 --- a/index.js +++ b/index.js @@ -5,6 +5,7 @@ var net = require("net"), util = require("util"), utils = require("./lib/utils"), Queue = require("./lib/queue"), + Command = require("./lib/command"), events = require("events"), parsers = [], // This static list of commands is updated from time to time. @@ -277,14 +278,14 @@ RedisClient.prototype.init_parser = function () { // That way the result / error won't stay in a try catch block and catch user things this.reply_parser.send_error = function (data) { process.nextTick(function() { - this.return_error(data); - }.bind(this)); - }.bind(this); + self.return_error(data); + }); + }; this.reply_parser.send_reply = function (data) { process.nextTick(function() { - this.return_reply(data); - }.bind(this)); - }.bind(this); + self.return_reply(data); + }); + }; }; RedisClient.prototype.on_ready = function () { @@ -498,7 +499,6 @@ RedisClient.prototype.connection_gone = function (why) { this.retry_timer = setTimeout(retry_connection, this.retry_delay, this); }; -var err_code = /^([A-Z]+)\s+(.+)$/; RedisClient.prototype.return_error = function (err) { var command_obj = this.command_queue.shift(), queue_len = this.command_queue.length; // send_command might have been used wrong => catch those cases too @@ -508,7 +508,7 @@ RedisClient.prototype.return_error = function (err) { err.command = command_obj.command; } - var match = err.message.match(err_code); + var match = err.message.match(utils.errCode); // LUA script could return user errors that don't behave like all other errors! if (match) { err.code = match[1]; @@ -537,7 +537,7 @@ RedisClient.prototype.return_reply = function (reply) { // If the "reply" here is actually a message received asynchronously due to a // pubsub subscription, don't pop the command queue as we'll only be consuming // the head command prematurely. - if (this.pub_sub_mode && Array.isArray(reply) && reply.length > 0 && reply[0]) { + if (this.pub_sub_mode && Array.isArray(reply) && reply[0]) { type = reply[0].toString(); } @@ -613,6 +613,7 @@ RedisClient.prototype.return_reply = function (reply) { if (Buffer.isBuffer(reply)) { reply = reply.toString(); } + // If in monitoring mode only two commands are valid ones: AUTH and MONITOR wich reply with OK len = reply.indexOf(" "); timestamp = reply.slice(0, len); argindex = reply.indexOf('"'); @@ -627,16 +628,6 @@ RedisClient.prototype.return_reply = function (reply) { } }; -// This Command constructor is ever so slightly faster than using an object literal, but more importantly, using -// a named constructor helps it show up meaningfully in the V8 CPU profiler and in heap snapshots. -function Command(command, args, sub_command, buffer_args, callback) { - this.command = command; - this.args = args; - this.sub_command = sub_command; - this.buffer_args = buffer_args; - this.callback = callback; -} - RedisClient.prototype.send_command = function (command, args, callback) { var arg, command_obj, i, elem_count, buffer_args, stream = this.stream, command_str = "", buffered_writes = 0, err; @@ -842,8 +833,6 @@ function Multi(client, args) { } } -exports.Multi = Multi; - commands.forEach(function (fullCommand) { var command = fullCommand.split(' ')[0]; @@ -1045,7 +1034,7 @@ Multi.prototype.execute_callback = function (err, replies) { // If we asked for strings, even in detect_buffers mode, then return strings: if (replies[i] instanceof Error) { - var match = replies[i].message.match(err_code); + var match = replies[i].message.match(utils.errCode); // LUA script could return user errors that don't behave like all other errors! if (match) { replies[i].code = match[1]; @@ -1132,10 +1121,5 @@ exports.createClient = function(port_arg, host_arg, options) { throw new Error('Unknown type of connection in createClient()'); }; -exports.print = function (err, reply) { - if (err) { - console.log("Error: " + err); - } else { - console.log("Reply: " + reply); - } -}; +exports.print = utils.print; +exports.Multi = Multi; diff --git a/lib/command.js b/lib/command.js new file mode 100644 index 00000000000..4aa795b696b --- /dev/null +++ b/lib/command.js @@ -0,0 +1,13 @@ +'use strict'; + +// This Command constructor is ever so slightly faster than using an object literal, but more importantly, using +// a named constructor helps it show up meaningfully in the V8 CPU profiler and in heap snapshots. +function Command(command, args, sub_command, buffer_args, callback) { + this.command = command; + this.args = args; + this.sub_command = sub_command; + this.buffer_args = buffer_args; + this.callback = callback; +} + +module.exports = Command; diff --git a/lib/parsers/javascript.js b/lib/parsers/javascript.js index dbbe658b6b2..029080ec291 100644 --- a/lib/parsers/javascript.js +++ b/lib/parsers/javascript.js @@ -130,23 +130,23 @@ ReplyParser.prototype.execute = function (buffer) { try { type = this._buffer[this._offset++]; - if (type === 43) { // + + if (type === 43) { // Strings + ret = this._parseResult(type); this.send_reply(ret); - } else if (type === 45) { // - + } else if (type === 45) { // Errors - ret = this._parseResult(type); this.send_error(ret); - } else if (type === 58) { // : + } else if (type === 58) { // Integers : ret = this._parseResult(type); this.send_reply(ret); - } else if (type === 36) { // $ + } else if (type === 36) { // Bulk strings $ ret = this._parseResult(type); this.send_reply(ret); - } else if (type === 42) { // 42 * + } else if (type === 42) { // Arrays * // set a rewind point. if a failure occurs, // wait for the next execute()/append() and try again offset = this._offset - 1; diff --git a/lib/utils.js b/lib/utils.js index 42ae211c3a2..c749380858b 100644 --- a/lib/utils.js +++ b/lib/utils.js @@ -46,8 +46,20 @@ function toArray(args) { return arr; } +function print (err, reply) { + if (err) { + console.log("Error: " + err); + } else { + console.log("Reply: " + reply); + } +} + +var redisErrCode = /^([A-Z]+)\s+(.+)$/; + module.exports = { reply_to_strings: replyToStrings, reply_to_object: replyToObject, - to_array: toArray + to_array: toArray, + print: print, + errCode: redisErrCode }; diff --git a/test/commands/sync.spec.js b/test/commands/sync.spec.js index c8aa5df40ed..a2d9009abe5 100644 --- a/test/commands/sync.spec.js +++ b/test/commands/sync.spec.js @@ -5,7 +5,7 @@ var config = require("../lib/config"); var helper = require("../helper"); var redis = config.redis; -describe("The 'sync' method", function () { +describe.skip("The 'sync' method", function () { helper.allTests(function(parser, ip, args) { @@ -23,7 +23,7 @@ describe("The 'sync' method", function () { // "Protocol error, got "K" as reply type byte" // I'm uncertain if this is correct behavior or not // TODO: Fix the command queue state error occuring - it.skip('try to sync with the server and fail other commands', function (done) { + it('try to sync with the server and fail other commands', function (done) { client.on('error', function(err) { assert.equal(err.message, 'Protocol error, got "K" as reply type byte'); assert.equal(err.command, 'SET'); diff --git a/test/helper.js b/test/helper.js index a9f66d4a40c..5f4f791b954 100644 --- a/test/helper.js +++ b/test/helper.js @@ -16,15 +16,15 @@ function startRedis (conf, done) { // don't start redis every time we // include this helper file! if (!process.env.REDIS_TESTS_STARTED) { - process.env.REDIS_TESTS_STARTED = true; + process.env.REDIS_TESTS_STARTED = true; - before(function (done) { - startRedis('./conf/redis.conf', done); - }); + before(function (done) { + startRedis('./conf/redis.conf', done); + }); - after(function (done) { - if (rp) rp.stop(done); - }); + after(function (done) { + if (rp) rp.stop(done); + }); } module.exports = { From afcd760b182a12031d8c1b8c986687046feaec1a Mon Sep 17 00:00:00 2001 From: Ruben Bridgewater Date: Wed, 30 Sep 2015 02:04:56 +0200 Subject: [PATCH 0395/1748] Fix a test and add some more --- test/commands/client.spec.js | 31 +++++++++++++++++++++++-- test/commands/zadd.spec.js | 44 ++++++++++++++++++++++++++++++++++++ test/commands/zscore.spec.js | 35 ++++++++++++++++++++++++++++ test/node_redis.spec.js | 9 ++------ 4 files changed, 110 insertions(+), 9 deletions(-) create mode 100644 test/commands/zadd.spec.js create mode 100644 test/commands/zscore.spec.js diff --git a/test/commands/client.spec.js b/test/commands/client.spec.js index 5707d1ab895..9014af862ac 100644 --- a/test/commands/client.spec.js +++ b/test/commands/client.spec.js @@ -11,17 +11,25 @@ describe("The 'client' method", function () { var pattern = /addr=/; describe("using " + parser + " and " + ip, function () { - var client; + var client, client2; beforeEach(function (done) { client = redis.createClient.apply(redis.createClient, args); - client.once("connect", function () { + client.once("ready", function () { client.flushdb(done); }); }); + beforeEach(function (done) { + client2 = redis.createClient.apply(redis.createClient, args); + client2.once("ready", function () { + done(); + }); + }); + afterEach(function () { client.end(); + client2.end(); }); describe('list', function () { @@ -52,6 +60,25 @@ describe("The 'client' method", function () { }); }); }); + + describe('setname / getname', function () { + + it('sets the name', function (done) { + helper.serverVersionAtLeast.call(this, client, [2, 6, 9]); + + client.client("setname", "RUTH", helper.isString('OK')); + client2.client("setname", "RENEE", helper.isString('OK')); + client2.client("setname", "MARTIN", helper.isString('OK')); + client2.client("getname", function(err, res) { + assert.equal(res, 'MARTIN'); + }); + client.client("getname", function(err, res) { + assert.equal(res, 'RUTH'); + done(); + }); + }); + + }); }); }); }); diff --git a/test/commands/zadd.spec.js b/test/commands/zadd.spec.js new file mode 100644 index 00000000000..52f45ef378e --- /dev/null +++ b/test/commands/zadd.spec.js @@ -0,0 +1,44 @@ +'use strict'; + +var config = require("../lib/config"); +var helper = require("../helper"); +var assert = require('assert'); +var redis = config.redis; + +describe("The 'zadd' method", function () { + + helper.allTests(function(parser, ip, args) { + + describe.only("using " + parser + " and " + ip, function () { + var client; + + beforeEach(function (done) { + client = redis.createClient.apply(redis.createClient, args); + client.once("connect", function () { + client.flushdb(done); + }); + }); + + it('reports an error', function (done) { + client.zadd('infinity', [+'5t', "should not be possible"], helper.isError(done)); + }); + + it('return inf / -inf', function (done) { + client.zadd('infinity', [+Infinity, "should be inf"], helper.isNumber(1)); + client.zadd('infinity', [-Infinity, "should be negative inf"], helper.isNumber(1)); + client.zadd('infinity', [99999999999999999999999, "should not be inf"], helper.isNumber(1)); + client.zrange('infinity', 0, -1, 'WITHSCORES', function (err, res) { + assert.equal(res[5], 'inf'); + assert.equal(res[1], '-inf'); + assert.equal(res[3], '9.9999999999999992e+22'); + done(); + }); + }); + + afterEach(function () { + client.end(); + }); + }); + }); + +}); diff --git a/test/commands/zscore.spec.js b/test/commands/zscore.spec.js new file mode 100644 index 00000000000..fc5d6f37a09 --- /dev/null +++ b/test/commands/zscore.spec.js @@ -0,0 +1,35 @@ +'use strict'; + +var config = require("../lib/config"); +var helper = require("../helper"); +var assert = require('assert'); +var redis = config.redis; + +describe("The 'zscore' method", function () { + + helper.allTests(function(parser, ip, args) { + + describe("using " + parser + " and " + ip, function () { + var client; + + beforeEach(function (done) { + client = redis.createClient.apply(redis.createClient, args); + client.once("connect", function () { + client.flushdb(done); + }); + }); + + it('should return the score of member in the sorted set at key', function (done) { + client.zadd('myzset', 1, 'one'); + client.zscore('myzset', 'one', function (err, res) { + assert.equal(res, 1); + done(); + }); + }); + + afterEach(function () { + client.end(); + }); + }); + }); +}); diff --git a/test/node_redis.spec.js b/test/node_redis.spec.js index 34ba79e3dd1..d8d6b0d05d3 100644 --- a/test/node_redis.spec.js +++ b/test/node_redis.spec.js @@ -151,18 +151,13 @@ describe("The node_redis client", function () { }); }); - it.skip("misusing the function should eventually throw (no command)", function (done) { - var mochaListener = helper.removeMochaListener(); - - process.once('uncaughtException', function (err) { - process.on('uncaughtException', mochaListener); + it("misusing the function should eventually throw (no command)", function (done) { + client.send_command(true, 'info', function (err, res) { assert(/ERR Protocol error/.test(err.message)); assert.equal(err.command, true); assert.equal(err.code, 'ERR'); done(); }); - - client.send_command(true, 'info'); }); it("misusing the function should eventually throw (wrong args)", function (done) { From 025c65c614929a26ab90033c4c7a5ecd312487c4 Mon Sep 17 00:00:00 2001 From: Ruben Bridgewater Date: Wed, 30 Sep 2015 02:09:01 +0200 Subject: [PATCH 0396/1748] Fix handling of missing info return value. Fixes #541 --- changelog.md | 2 ++ index.js | 8 ++++++++ 2 files changed, 10 insertions(+) diff --git a/changelog.md b/changelog.md index 5262d7bebca..782afc24377 100644 --- a/changelog.md +++ b/changelog.md @@ -11,6 +11,8 @@ Bugfixes: - Fix argument mutation while using the array notation with the multi constructor (@BridgeAR) - Fix multi.hmset key not being type converted if used with an object and key not being a string (@BridgeAR) +- Fix parser errors not being catched properly (@BridgeAR) +- Fix a crash that could occur if a redis server does return the info command as usual #541 (@BridgeAR) ## v2.0.1 - Sep 24, 2015 diff --git a/index.js b/index.js index 8bc2b72430a..dc41a42c7ad 100644 --- a/index.js +++ b/index.js @@ -343,6 +343,14 @@ RedisClient.prototype.on_info_cmd = function (err, res) { return; } + /* istanbul ignore if: some servers might not respond with any info data. This is just a safety check that is difficult to test */ + if (!res) { + debug('The info command returned without any data.'); + this.server_info = {}; + this.on_ready(); + return; + } + var self = this; var obj = {}; var lines = res.toString().split("\r\n"); From 3c39a8bdfccca3e421d7762c59b14f1b3483185d Mon Sep 17 00:00:00 2001 From: Ruben Bridgewater Date: Wed, 30 Sep 2015 02:12:28 +0200 Subject: [PATCH 0397/1748] Remove .only --- test/commands/zadd.spec.js | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/test/commands/zadd.spec.js b/test/commands/zadd.spec.js index 52f45ef378e..8a7dd2a7dff 100644 --- a/test/commands/zadd.spec.js +++ b/test/commands/zadd.spec.js @@ -9,7 +9,7 @@ describe("The 'zadd' method", function () { helper.allTests(function(parser, ip, args) { - describe.only("using " + parser + " and " + ip, function () { + describe("using " + parser + " and " + ip, function () { var client; beforeEach(function (done) { From 977d4dba2b97811eea374e0dd9b21e24c2f2441f Mon Sep 17 00:00:00 2001 From: Ruben Bridgewater Date: Wed, 30 Sep 2015 02:35:11 +0200 Subject: [PATCH 0398/1748] Add host and port to options object --- changelog.md | 3 ++- index.js | 6 ++++-- test/connection.spec.js | 28 ++++++++++++++++++++++++++++ 3 files changed, 34 insertions(+), 3 deletions(-) diff --git a/changelog.md b/changelog.md index 782afc24377..3a34697586a 100644 --- a/changelog.md +++ b/changelog.md @@ -5,7 +5,8 @@ Changelog Features: -- Add optional flush parameter to `.end`. If set to true, commands fired after using .end are going to be rejected instead of being ignored. (@crispy1989) +- Addded optional flush parameter to `.end`. If set to true, commands fired after using .end are going to be rejected instead of being ignored. (@crispy1989) +- Addded: host and port can now be provided in a single options object. E.g. redis.createClient({ host: 'localhost', port: 1337, max_attempts: 5 }); (@BridgeAR) Bugfixes: diff --git a/index.js b/index.js index dc41a42c7ad..80e1c23c551 100644 --- a/index.js +++ b/index.js @@ -1107,8 +1107,10 @@ var createClient_tcp = function (port_arg, host_arg, options) { exports.createClient = function(port_arg, host_arg, options) { if (typeof port_arg === 'object' || port_arg === undefined) { - options = port_arg || options; - return createClient_tcp(default_port, default_host, options); + options = port_arg || options || {}; + var host = options.host || default_host; + var port = +options.port || default_port; + return createClient_tcp(port, host, options); } if (typeof port_arg === 'number' || typeof port_arg === 'string' && /^\d+$/.test(port_arg)) { return createClient_tcp(port_arg, host_arg, options); diff --git a/test/connection.spec.js b/test/connection.spec.js index a59ff8deaf5..042eabb7eeb 100644 --- a/test/connection.spec.js +++ b/test/connection.spec.js @@ -79,6 +79,34 @@ describe("on lost connection", function () { }); }); + it("can not connect with wrong host / port in the options object", function (done) { + var client = redis.createClient({ + host: 'somewhere', + port: 6379, + max_attempts: 1 + }); + var end = helper.callFuncAfter(done, 2); + + client.on('error', function (err) { + assert(/CONNECTION_BROKEN|ENOTFOUND/.test(err.code)); + end(); + }); + + }); + + it("connect with host and port provided in the options object", function (done) { + var client = redis.createClient({ + host: 'localhost', + port: '6379', + parser: parser, + connect_timeout: 1000 + }); + + client.once('ready', function() { + done(); + }); + }); + }); }); }); From 2ca42417bfd1b0221e98289459bf6799ad6913f4 Mon Sep 17 00:00:00 2001 From: Ruben Bridgewater Date: Fri, 2 Oct 2015 20:20:56 +0200 Subject: [PATCH 0399/1748] Fix explicitly passing undefined as callback --- changelog.md | 1 + index.js | 20 +- .../{geoadd.spec.js.future => geoadd.spec.js} | 3 +- test/commands/set.spec.js | 10 + test/commands/zadd.spec.js | 6 +- test/detect_buffers.spec.js | 209 ++++++++++++++++++ test/node_redis.spec.js | 209 +----------------- 7 files changed, 240 insertions(+), 218 deletions(-) rename test/commands/{geoadd.spec.js.future => geoadd.spec.js} (89%) create mode 100644 test/detect_buffers.spec.js diff --git a/changelog.md b/changelog.md index 3a34697586a..ab0147265d3 100644 --- a/changelog.md +++ b/changelog.md @@ -14,6 +14,7 @@ Bugfixes: - Fix multi.hmset key not being type converted if used with an object and key not being a string (@BridgeAR) - Fix parser errors not being catched properly (@BridgeAR) - Fix a crash that could occur if a redis server does return the info command as usual #541 (@BridgeAR) +- Explicitly passing undefined as a callback statement will work again. E.g. client.publish('channel', 'message', undefined); (@BridgeAR) ## v2.0.1 - Sep 24, 2015 diff --git a/index.js b/index.js index 80e1c23c551..4458b68da26 100644 --- a/index.js +++ b/index.js @@ -629,6 +629,7 @@ RedisClient.prototype.return_reply = function (reply) { return elem.replace(/\\"/g, '"'); }); this.emit("monitor", timestamp, args); + /* istanbul ignore else: this is a safety check that we should not be able to trigger */ } else { var err = new Error("node_redis command queue state error. If you can reproduce this, please report it."); err.command_obj = command_obj; @@ -639,21 +640,14 @@ RedisClient.prototype.return_reply = function (reply) { RedisClient.prototype.send_command = function (command, args, callback) { var arg, command_obj, i, elem_count, buffer_args, stream = this.stream, command_str = "", buffered_writes = 0, err; - // if (typeof callback === "function") {} - // probably the fastest way: - // client.command([arg1, arg2], cb); (straight passthrough) - // send_command(command, [arg1, arg2], cb); if (args === undefined) { args = []; - } else if (!callback && typeof args[args.length - 1] === "function") { - // most people find this variable argument length form more convenient, but it uses arguments, which is slower - // client.command(arg1, arg2, cb); (wraps up arguments into an array) - // send_command(command, [arg1, arg2, cb]); - // client.command(arg1, arg2); (callback is optional) - // send_command(command, [arg1, arg2]); - // client.command(arg1, arg2, undefined); (callback is undefined) - // send_command(command, [arg1, arg2, undefined]); - callback = args.pop(); + } else if (!callback) { + if (typeof args[args.length - 1] === "function") { + callback = args.pop(); + } else if (typeof args[args.length - 1] === "undefined") { + args.pop(); + } } if (process.domain && callback) { diff --git a/test/commands/geoadd.spec.js.future b/test/commands/geoadd.spec.js similarity index 89% rename from test/commands/geoadd.spec.js.future rename to test/commands/geoadd.spec.js index 710b528f809..c98992471bc 100644 --- a/test/commands/geoadd.spec.js.future +++ b/test/commands/geoadd.spec.js @@ -4,7 +4,7 @@ var config = require("../lib/config"); var helper = require("../helper"); var redis = config.redis; -describe("The 'getoadd' method", function () { +describe("The 'geoadd' method", function () { helper.allTests(function(parser, ip, args) { @@ -19,6 +19,7 @@ describe("The 'getoadd' method", function () { }); it('returns 1 if the key exists', function (done) { + helper.serverVersionAtLeast.call(this, client, [3, 2, 0]); client.geoadd("mycity:21:0:location", "13.361389","38.115556","COR", function(err, res) { console.log(err, res); // geoadd is still in the unstable branch. As soon as it reaches the stable one, activate this test diff --git a/test/commands/set.spec.js b/test/commands/set.spec.js index dbdd3103e9b..6354f97f619 100644 --- a/test/commands/set.spec.js +++ b/test/commands/set.spec.js @@ -89,6 +89,16 @@ describe("The 'set' method", function () { }, 100); }); + it("sets the value correctly even if the callback is explicitly set to undefined", function (done) { + client.set(key, value, undefined); + setTimeout(function () { + client.get(key, function (err, res) { + helper.isString(value)(err, res); + done(); + }); + }, 100); + }); + it("sets the value correctly with the array syntax", function (done) { client.set([key, value]); setTimeout(function () { diff --git a/test/commands/zadd.spec.js b/test/commands/zadd.spec.js index 8a7dd2a7dff..973660dcd08 100644 --- a/test/commands/zadd.spec.js +++ b/test/commands/zadd.spec.js @@ -20,12 +20,16 @@ describe("The 'zadd' method", function () { }); it('reports an error', function (done) { + if (helper.redisProcess().spawnFailed()) this.skip(); client.zadd('infinity', [+'5t', "should not be possible"], helper.isError(done)); }); it('return inf / -inf', function (done) { + if (helper.redisProcess().spawnFailed()) this.skip(); + helper.serverVersionAtLeast.call(this, client, [3, 0, 2]); client.zadd('infinity', [+Infinity, "should be inf"], helper.isNumber(1)); - client.zadd('infinity', [-Infinity, "should be negative inf"], helper.isNumber(1)); + client.zadd('infinity', ['inf', 'should be also be inf'], helper.isNumber(1)); + client.zadd('infinity', -Infinity, "should be negative inf", helper.isNumber(1)); client.zadd('infinity', [99999999999999999999999, "should not be inf"], helper.isNumber(1)); client.zrange('infinity', 0, -1, 'WITHSCORES', function (err, res) { assert.equal(res[5], 'inf'); diff --git a/test/detect_buffers.spec.js b/test/detect_buffers.spec.js new file mode 100644 index 00000000000..fc9cd1aa314 --- /dev/null +++ b/test/detect_buffers.spec.js @@ -0,0 +1,209 @@ +'use strict'; + +var assert = require("assert"); +var config = require("./lib/config"); +var helper = require('./helper'); +var redis = config.redis; + +describe("detect_buffers", function () { + + helper.allTests(function(parser, ip, args) { + + describe("using " + parser + " and " + ip, function () { + var client; + var args = config.configureClient(parser, ip, { + detect_buffers: true + }); + + beforeEach(function (done) { + client = redis.createClient.apply(redis.createClient, args); + client.once("error", done); + client.once("connect", function () { + client.flushdb(function (err) { + client.hmset("hash key 2", "key 1", "val 1", "key 2", "val 2"); + client.set("string key 1", "string value"); + return done(err); + }); + }); + }); + + describe('get', function () { + describe('first argument is a string', function () { + it('returns a string', function (done) { + client.get("string key 1", helper.isString("string value", done)); + }); + + it('returns a string when executed as part of transaction', function (done) { + client.multi().get("string key 1").exec(helper.isString("string value", done)); + }); + }); + + describe('first argument is a buffer', function () { + it('returns a buffer', function (done) { + client.get(new Buffer("string key 1"), function (err, reply) { + assert.strictEqual(true, Buffer.isBuffer(reply)); + assert.strictEqual("", reply.inspect()); + return done(err); + }); + }); + + it('returns a bufffer when executed as part of transaction', function (done) { + client.multi().get(new Buffer("string key 1")).exec(function (err, reply) { + assert.strictEqual(1, reply.length); + assert.strictEqual(true, Buffer.isBuffer(reply[0])); + assert.strictEqual("", reply[0].inspect()); + return done(err); + }); + }); + }); + }); + + describe('multi.hget', function () { + it('can interleave string and buffer results', function (done) { + client.multi() + .hget("hash key 2", "key 1") + .hget(new Buffer("hash key 2"), "key 1") + .hget("hash key 2", new Buffer("key 2")) + .hget("hash key 2", "key 2") + .exec(function (err, reply) { + assert.strictEqual(true, Array.isArray(reply)); + assert.strictEqual(4, reply.length); + assert.strictEqual("val 1", reply[0]); + assert.strictEqual(true, Buffer.isBuffer(reply[1])); + assert.strictEqual("", reply[1].inspect()); + assert.strictEqual(true, Buffer.isBuffer(reply[2])); + assert.strictEqual("", reply[2].inspect()); + assert.strictEqual("val 2", reply[3]); + return done(err); + }); + }); + }); + + describe('hmget', function () { + describe('first argument is a string', function () { + it('returns strings for keys requested', function (done) { + client.hmget("hash key 2", "key 1", "key 2", function (err, reply) { + assert.strictEqual(true, Array.isArray(reply)); + assert.strictEqual(2, reply.length); + assert.strictEqual("val 1", reply[0]); + assert.strictEqual("val 2", reply[1]); + return done(err); + }); + }); + + it('returns strings for keys requested in transaction', function (done) { + client.multi().hmget("hash key 2", "key 1", "key 2").exec(function (err, reply) { + assert.strictEqual(true, Array.isArray(reply)); + assert.strictEqual(1, reply.length); + assert.strictEqual(2, reply[0].length); + assert.strictEqual("val 1", reply[0][0]); + assert.strictEqual("val 2", reply[0][1]); + return done(err); + }); + }); + + it('handles array of strings with undefined values (repro #344)', function (done) { + client.hmget("hash key 2", "key 3", "key 4", function(err, reply) { + assert.strictEqual(true, Array.isArray(reply)); + assert.strictEqual(2, reply.length); + assert.equal(null, reply[0]); + assert.equal(null, reply[1]); + return done(err); + }); + }); + + it('handles array of strings with undefined values in transaction (repro #344)', function (done) { + client.multi().hmget("hash key 2", "key 3", "key 4").exec(function(err, reply) { + assert.strictEqual(true, Array.isArray(reply)); + assert.strictEqual(1, reply.length); + assert.strictEqual(2, reply[0].length); + assert.equal(null, reply[0][0]); + assert.equal(null, reply[0][1]); + return done(err); + }); + }); + }); + + describe('first argument is a buffer', function () { + it('returns buffers for keys requested', function (done) { + client.hmget(new Buffer("hash key 2"), "key 1", "key 2", function (err, reply) { + assert.strictEqual(true, Array.isArray(reply)); + assert.strictEqual(2, reply.length); + assert.strictEqual(true, Buffer.isBuffer(reply[0])); + assert.strictEqual(true, Buffer.isBuffer(reply[1])); + assert.strictEqual("", reply[0].inspect()); + assert.strictEqual("", reply[1].inspect()); + return done(err); + }); + }); + + it("returns buffers for keys requested in transaction", function (done) { + client.multi().hmget(new Buffer("hash key 2"), "key 1", "key 2").exec(function (err, reply) { + assert.strictEqual(true, Array.isArray(reply)); + assert.strictEqual(1, reply.length); + assert.strictEqual(2, reply[0].length); + assert.strictEqual(true, Buffer.isBuffer(reply[0][0])); + assert.strictEqual(true, Buffer.isBuffer(reply[0][1])); + assert.strictEqual("", reply[0][0].inspect()); + assert.strictEqual("", reply[0][1].inspect()); + return done(err); + }); + }); + }); + }); + + describe('hgetall', function (done) { + describe('first argument is a string', function () { + it('returns string values', function (done) { + client.hgetall("hash key 2", function (err, reply) { + assert.strictEqual("object", typeof reply); + assert.strictEqual(2, Object.keys(reply).length); + assert.strictEqual("val 1", reply["key 1"]); + assert.strictEqual("val 2", reply["key 2"]); + return done(err); + }); + }); + + it('returns string values when executed in transaction', function (done) { + client.multi().hgetall("hash key 2").exec(function (err, reply) { + assert.strictEqual(1, reply.length); + assert.strictEqual("object", typeof reply[0]); + assert.strictEqual(2, Object.keys(reply[0]).length); + assert.strictEqual("val 1", reply[0]["key 1"]); + assert.strictEqual("val 2", reply[0]["key 2"]); + return done(err); + }); + }); + }); + + describe('first argument is a buffer', function () { + it('returns buffer values', function (done) { + client.hgetall(new Buffer("hash key 2"), function (err, reply) { + assert.strictEqual(null, err); + assert.strictEqual("object", typeof reply); + assert.strictEqual(2, Object.keys(reply).length); + assert.strictEqual(true, Buffer.isBuffer(reply["key 1"])); + assert.strictEqual(true, Buffer.isBuffer(reply["key 2"])); + assert.strictEqual("", reply["key 1"].inspect()); + assert.strictEqual("", reply["key 2"].inspect()); + return done(err); + }); + }); + + it('returns buffer values when executed in transaction', function (done) { + client.multi().hgetall(new Buffer("hash key 2")).exec(function (err, reply) { + assert.strictEqual(1, reply.length); + assert.strictEqual("object", typeof reply); + assert.strictEqual(2, Object.keys(reply[0]).length); + assert.strictEqual(true, Buffer.isBuffer(reply[0]["key 1"])); + assert.strictEqual(true, Buffer.isBuffer(reply[0]["key 2"])); + assert.strictEqual("", reply[0]["key 1"].inspect()); + assert.strictEqual("", reply[0]["key 2"].inspect()); + return done(err); + }); + }); + }); + }); + }); + }); +}); \ No newline at end of file diff --git a/test/node_redis.spec.js b/test/node_redis.spec.js index d8d6b0d05d3..f45480eb79b 100644 --- a/test/node_redis.spec.js +++ b/test/node_redis.spec.js @@ -293,12 +293,12 @@ describe("The node_redis client", function () { assert.strictEqual(client.monitoring, false, "monitoring off at start"); client.set("recon 1", "one"); client.monitor(function (err, res) { - assert.strictEqual(client.monitoring, true, "monitoring on after monitor()"); - client.set("recon 2", "two", function (err, res) { - // Do not do this in normal programs. This is to simulate the server closing on us. - // For orderly shutdown in normal programs, do client.quit() - client.stream.destroy(); - }); + assert.strictEqual(client.monitoring, true, "monitoring on after monitor()"); + client.set("recon 2", "two", function (err, res) { + // Do not do this in normal programs. This is to simulate the server closing on us. + // For orderly shutdown in normal programs, do client.quit() + client.stream.destroy(); + }); }); }); @@ -415,203 +415,6 @@ describe("The node_redis client", function () { }); }); - describe('detect_buffers', function () { - var client; - var args = config.configureClient(parser, ip, { - detect_buffers: true - }); - - beforeEach(function (done) { - client = redis.createClient.apply(redis.createClient, args); - client.once("error", done); - client.once("connect", function () { - client.flushdb(function (err) { - client.hmset("hash key 2", "key 1", "val 1", "key 2", "val 2"); - client.set("string key 1", "string value"); - return done(err); - }); - }); - }); - - describe('get', function () { - describe('first argument is a string', function () { - it('returns a string', function (done) { - client.get("string key 1", helper.isString("string value", done)); - }); - - it('returns a string when executed as part of transaction', function (done) { - client.multi().get("string key 1").exec(helper.isString("string value", done)); - }); - }); - - describe('first argument is a buffer', function () { - it('returns a buffer', function (done) { - client.get(new Buffer("string key 1"), function (err, reply) { - assert.strictEqual(true, Buffer.isBuffer(reply)); - assert.strictEqual("", reply.inspect()); - return done(err); - }); - }); - - it('returns a bufffer when executed as part of transaction', function (done) { - client.multi().get(new Buffer("string key 1")).exec(function (err, reply) { - assert.strictEqual(1, reply.length); - assert.strictEqual(true, Buffer.isBuffer(reply[0])); - assert.strictEqual("", reply[0].inspect()); - return done(err); - }); - }); - }); - }); - - describe('multi.hget', function () { - it('can interleave string and buffer results', function (done) { - client.multi() - .hget("hash key 2", "key 1") - .hget(new Buffer("hash key 2"), "key 1") - .hget("hash key 2", new Buffer("key 2")) - .hget("hash key 2", "key 2") - .exec(function (err, reply) { - assert.strictEqual(true, Array.isArray(reply)); - assert.strictEqual(4, reply.length); - assert.strictEqual("val 1", reply[0]); - assert.strictEqual(true, Buffer.isBuffer(reply[1])); - assert.strictEqual("", reply[1].inspect()); - assert.strictEqual(true, Buffer.isBuffer(reply[2])); - assert.strictEqual("", reply[2].inspect()); - assert.strictEqual("val 2", reply[3]); - return done(err); - }); - }); - }); - - describe('hmget', function () { - describe('first argument is a string', function () { - it('returns strings for keys requested', function (done) { - client.hmget("hash key 2", "key 1", "key 2", function (err, reply) { - assert.strictEqual(true, Array.isArray(reply)); - assert.strictEqual(2, reply.length); - assert.strictEqual("val 1", reply[0]); - assert.strictEqual("val 2", reply[1]); - return done(err); - }); - }); - - it('returns strings for keys requested in transaction', function (done) { - client.multi().hmget("hash key 2", "key 1", "key 2").exec(function (err, reply) { - assert.strictEqual(true, Array.isArray(reply)); - assert.strictEqual(1, reply.length); - assert.strictEqual(2, reply[0].length); - assert.strictEqual("val 1", reply[0][0]); - assert.strictEqual("val 2", reply[0][1]); - return done(err); - }); - }); - - it('handles array of strings with undefined values (repro #344)', function (done) { - client.hmget("hash key 2", "key 3", "key 4", function(err, reply) { - assert.strictEqual(true, Array.isArray(reply)); - assert.strictEqual(2, reply.length); - assert.equal(null, reply[0]); - assert.equal(null, reply[1]); - return done(err); - }); - }); - - it('handles array of strings with undefined values in transaction (repro #344)', function (done) { - client.multi().hmget("hash key 2", "key 3", "key 4").exec(function(err, reply) { - assert.strictEqual(true, Array.isArray(reply)); - assert.strictEqual(1, reply.length); - assert.strictEqual(2, reply[0].length); - assert.equal(null, reply[0][0]); - assert.equal(null, reply[0][1]); - return done(err); - }); - }); - }); - - describe('first argument is a buffer', function () { - it('returns buffers for keys requested', function (done) { - client.hmget(new Buffer("hash key 2"), "key 1", "key 2", function (err, reply) { - assert.strictEqual(true, Array.isArray(reply)); - assert.strictEqual(2, reply.length); - assert.strictEqual(true, Buffer.isBuffer(reply[0])); - assert.strictEqual(true, Buffer.isBuffer(reply[1])); - assert.strictEqual("", reply[0].inspect()); - assert.strictEqual("", reply[1].inspect()); - return done(err); - }); - }); - - it("returns buffers for keys requested in transaction", function (done) { - client.multi().hmget(new Buffer("hash key 2"), "key 1", "key 2").exec(function (err, reply) { - assert.strictEqual(true, Array.isArray(reply)); - assert.strictEqual(1, reply.length); - assert.strictEqual(2, reply[0].length); - assert.strictEqual(true, Buffer.isBuffer(reply[0][0])); - assert.strictEqual(true, Buffer.isBuffer(reply[0][1])); - assert.strictEqual("", reply[0][0].inspect()); - assert.strictEqual("", reply[0][1].inspect()); - return done(err); - }); - }); - }); - }); - - describe('hgetall', function (done) { - describe('first argument is a string', function () { - it('returns string values', function (done) { - client.hgetall("hash key 2", function (err, reply) { - assert.strictEqual("object", typeof reply); - assert.strictEqual(2, Object.keys(reply).length); - assert.strictEqual("val 1", reply["key 1"]); - assert.strictEqual("val 2", reply["key 2"]); - return done(err); - }); - }); - - it('returns string values when executed in transaction', function (done) { - client.multi().hgetall("hash key 2").exec(function (err, reply) { - assert.strictEqual(1, reply.length); - assert.strictEqual("object", typeof reply[0]); - assert.strictEqual(2, Object.keys(reply[0]).length); - assert.strictEqual("val 1", reply[0]["key 1"]); - assert.strictEqual("val 2", reply[0]["key 2"]); - return done(err); - }); - }); - }); - - describe('first argument is a buffer', function () { - it('returns buffer values', function (done) { - client.hgetall(new Buffer("hash key 2"), function (err, reply) { - assert.strictEqual(null, err); - assert.strictEqual("object", typeof reply); - assert.strictEqual(2, Object.keys(reply).length); - assert.strictEqual(true, Buffer.isBuffer(reply["key 1"])); - assert.strictEqual(true, Buffer.isBuffer(reply["key 2"])); - assert.strictEqual("", reply["key 1"].inspect()); - assert.strictEqual("", reply["key 2"].inspect()); - return done(err); - }); - }); - - it('returns buffer values when executed in transaction', function (done) { - client.multi().hgetall(new Buffer("hash key 2")).exec(function (err, reply) { - assert.strictEqual(1, reply.length); - assert.strictEqual("object", typeof reply); - assert.strictEqual(2, Object.keys(reply[0]).length); - assert.strictEqual(true, Buffer.isBuffer(reply[0]["key 1"])); - assert.strictEqual(true, Buffer.isBuffer(reply[0]["key 2"])); - assert.strictEqual("", reply[0]["key 1"].inspect()); - assert.strictEqual("", reply[0]["key 2"].inspect()); - return done(err); - }); - }); - }); - }); - }); - describe('unref', function () { it('exits subprocess as soon as final command is processed', function (done) { var args = config.HOST[ip] ? [config.HOST[ip], config.PORT] : [ip]; From 2744fe865027bc599fa184b07166872692b5e0a2 Mon Sep 17 00:00:00 2001 From: Ruben Bridgewater Date: Fri, 2 Oct 2015 20:21:13 +0200 Subject: [PATCH 0400/1748] Optimize statements and speed up the common case --- .gitignore | 2 +- benchmarks/multi_bench.js | 2 +- index.js | 22 ++++++++++++++++++---- lib/parsers/javascript.js | 10 +--------- 4 files changed, 21 insertions(+), 15 deletions(-) diff --git a/.gitignore b/.gitignore index 2cf7258aa69..3730c724445 100644 --- a/.gitignore +++ b/.gitignore @@ -2,5 +2,5 @@ node_modules .tern-port .nyc_output coverage -npm-debug.log +*.log *.rdb diff --git a/benchmarks/multi_bench.js b/benchmarks/multi_bench.js index a1d470611a7..05fcd2dc043 100644 --- a/benchmarks/multi_bench.js +++ b/benchmarks/multi_bench.js @@ -7,7 +7,7 @@ var redis = require("../index"); var totalTime = 0; var metrics = require("metrics"); var num_clients = parseInt(process.argv[2], 10) || 5; -var num_requests = 20000; +var num_requests = 50000; var tests = []; var versions_logged = false; var client_options = { diff --git a/index.js b/index.js index 4458b68da26..2f9a8885cb7 100644 --- a/index.js +++ b/index.js @@ -851,6 +851,14 @@ commands.forEach(function (fullCommand) { arg = [key].concat(arg); return this.send_command(command, arg, callback); } + // Speed up the common case + var len = arguments.length; + if (len === 2) { + return this.send_command(command, [key, arg]); + } + if (len === 3) { + return this.send_command(command, [key, arg, callback]); + } return this.send_command(command, utils.to_array(arguments)); }; RedisClient.prototype[command.toUpperCase()] = RedisClient.prototype[command]; @@ -867,7 +875,15 @@ commands.forEach(function (fullCommand) { } this.queue.push([command, key].concat(arg)); } else { - this.queue.push([command].concat(utils.to_array(arguments))); + // Speed up the common case + var len = arguments.length; + if (len === 2) { + this.queue.push([command, key, arg]); + } else if (len === 3) { + this.queue.push([command, key, arg, callback]); + } else { + this.queue.push([command].concat(utils.to_array(arguments))); + } } return this; }; @@ -1102,9 +1118,7 @@ var createClient_tcp = function (port_arg, host_arg, options) { exports.createClient = function(port_arg, host_arg, options) { if (typeof port_arg === 'object' || port_arg === undefined) { options = port_arg || options || {}; - var host = options.host || default_host; - var port = +options.port || default_port; - return createClient_tcp(port, host, options); + return createClient_tcp(+options.port, options.host, options); } if (typeof port_arg === 'number' || typeof port_arg === 'string' && /^\d+$/.test(port_arg)) { return createClient_tcp(port_arg, host_arg, options); diff --git a/lib/parsers/javascript.js b/lib/parsers/javascript.js index 029080ec291..4beda4841fa 100644 --- a/lib/parsers/javascript.js +++ b/lib/parsers/javascript.js @@ -11,7 +11,7 @@ function ReplyParser(return_buffers) { this.name = exports.name; this.return_buffers = return_buffers; - this._buffer = null; + this._buffer = new Buffer(0); this._offset = 0; this._encoding = "utf-8"; } @@ -171,18 +171,10 @@ ReplyParser.prototype.execute = function (buffer) { ReplyParser.prototype.append = function (newBuffer) { - // first run - if (this._buffer === null) { - this._buffer = newBuffer; - - return; - } - // out of data if (this._offset >= this._buffer.length) { this._buffer = newBuffer; this._offset = 0; - return; } From adee2396455fe4671fb8095be4147241ff5b050d Mon Sep 17 00:00:00 2001 From: Ruben Bridgewater Date: Fri, 2 Oct 2015 20:25:49 +0200 Subject: [PATCH 0401/1748] Fix istanbul statement --- index.js | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/index.js b/index.js index 2f9a8885cb7..a0e17e6098d 100644 --- a/index.js +++ b/index.js @@ -617,7 +617,9 @@ RedisClient.prototype.return_reply = function (reply) { this.emit("error", new Error("subscriptions are active but got an invalid reply: " + reply)); return; } - } else if (this.monitoring) { + } + /* istanbul ignore else: this is a safety check that we should not be able to trigger */ + else if (this.monitoring) { if (Buffer.isBuffer(reply)) { reply = reply.toString(); } @@ -629,7 +631,6 @@ RedisClient.prototype.return_reply = function (reply) { return elem.replace(/\\"/g, '"'); }); this.emit("monitor", timestamp, args); - /* istanbul ignore else: this is a safety check that we should not be able to trigger */ } else { var err = new Error("node_redis command queue state error. If you can reproduce this, please report it."); err.command_obj = command_obj; From 044db8ca0620f97143fa34f8bced0b3b02657578 Mon Sep 17 00:00:00 2001 From: Ruben Bridgewater Date: Fri, 2 Oct 2015 21:10:08 +0200 Subject: [PATCH 0402/1748] Update the readme + changelog --- README.md | 8 +++++--- changelog.md | 1 + 2 files changed, 6 insertions(+), 3 deletions(-) diff --git a/README.md b/README.md index a03106eabb1..582fc809281 100644 --- a/README.md +++ b/README.md @@ -164,14 +164,16 @@ If you have `redis-server` running on the same computer as node, then the defaul port and host are probably fine and you don't need to supply any arguments. `createClient()` returns a `RedisClient` object. ### overloading -* `redis.createClient(port, host, options)` -* `redis.createClient()` is equivalent to `redis.createClient(6379, '127.0.0.1', {})` -* `redis.createClient(options)` is equivalent to `redis.createClient(6379, '127.0.0.1', options)` +* `redis.createClient()` +* `redis.createClient(options)` * `redis.createClient(unix_socket, options)` * `redis.createClient('redis://user:pass@host:port', options)` +* `redis.createClient(port, host, options)` `options` is an object with the following possible properties: +* `host`: *127.0.0.1*; The host to connect to +* `port`: *6370*; The port to connect to * `parser`: *hiredis*; Which Redis protocol reply parser to use. If `hiredis` is not installed it will fallback to `javascript`. * `return_buffers`: *false*; If set to `true`, then all replies will be sent to callbacks as Buffers instead of Strings. * `detect_buffers`: *false*; If set to `true`, then replies will be sent to callbacks as Buffers diff --git a/changelog.md b/changelog.md index ab0147265d3..b573948e280 100644 --- a/changelog.md +++ b/changelog.md @@ -7,6 +7,7 @@ Features: - Addded optional flush parameter to `.end`. If set to true, commands fired after using .end are going to be rejected instead of being ignored. (@crispy1989) - Addded: host and port can now be provided in a single options object. E.g. redis.createClient({ host: 'localhost', port: 1337, max_attempts: 5 }); (@BridgeAR) +- Speedup common cases (@BridgeAR) Bugfixes: From 6bc61f7f301b606687b157d8bff2f5c2cf478d4a Mon Sep 17 00:00:00 2001 From: Ruben Bridgewater Date: Fri, 2 Oct 2015 21:10:39 +0200 Subject: [PATCH 0403/1748] v.2.1.0 --- package.json | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/package.json b/package.json index 15953e49a74..4b87b9ee193 100644 --- a/package.json +++ b/package.json @@ -1,6 +1,6 @@ { "name": "redis", - "version": "2.0.1", + "version": "2.1.0", "description": "Redis client library", "keywords": [ "database", From 088b3f699686cbe9d5d1e4238f399d0db347b815 Mon Sep 17 00:00:00 2001 From: Ruben Bridgewater Date: Fri, 2 Oct 2015 21:12:02 +0200 Subject: [PATCH 0404/1748] Add release date --- changelog.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/changelog.md b/changelog.md index b573948e280..762f35f489e 100644 --- a/changelog.md +++ b/changelog.md @@ -1,7 +1,7 @@ Changelog ========= -## v2.1.0 - xx, 2015 +## v2.1.0 - Oct 02, 2015 Features: From e8d9858e299485b9db8f8ffcab02b7a06447aeea Mon Sep 17 00:00:00 2001 From: Ruben Bridgewater Date: Fri, 2 Oct 2015 23:28:59 +0200 Subject: [PATCH 0405/1748] Add disable_resubscribingg option. Fixes #472 --- README.md | 1 + changelog.md | 14 +++++++++-- index.js | 3 +++ test/pubsub.spec.js | 59 ++++++++++++++++++++++++++++++++++++++++----- 4 files changed, 69 insertions(+), 8 deletions(-) diff --git a/README.md b/README.md index 582fc809281..90a34d1c606 100644 --- a/README.md +++ b/README.md @@ -203,6 +203,7 @@ That way the default is to try reconnecting until 24h passed. limits total amount of connection tries. Setting this to 1 will prevent any reconnect tries. * `auth_pass` *null*; If set, client will run redis auth command on connect. * `family` *IPv4*; You can force using IPv6 if you set the family to 'IPv6'. See Node.js [net](https://nodejs.org/api/net.html) or [dns](https://nodejs.org/api/dns.html) modules how to use the family type. +* `disable_resubscribing`: *false*; If set to `true`, a client won't resubscribe after disconnecting ```js var redis = require("redis"), diff --git a/changelog.md b/changelog.md index 762f35f489e..1bf4e91bc7d 100644 --- a/changelog.md +++ b/changelog.md @@ -1,6 +1,16 @@ Changelog ========= +## v.2.x.x - xx, 2015 + +Features + +- Added disable_resubscribing option to prevent a client from resubscribing after reconnecting (@BridgeAR) + +Bugfixes + +- + ## v2.1.0 - Oct 02, 2015 Features: @@ -14,7 +24,7 @@ Bugfixes: - Fix argument mutation while using the array notation with the multi constructor (@BridgeAR) - Fix multi.hmset key not being type converted if used with an object and key not being a string (@BridgeAR) - Fix parser errors not being catched properly (@BridgeAR) -- Fix a crash that could occur if a redis server does return the info command as usual #541 (@BridgeAR) +- Fix a crash that could occur if a redis server does not return the info command as usual #541 (@BridgeAR) - Explicitly passing undefined as a callback statement will work again. E.g. client.publish('channel', 'message', undefined); (@BridgeAR) ## v2.0.1 - Sep 24, 2015 @@ -27,7 +37,7 @@ Bugfixes: This is the biggest release that node_redis had since it was released in 2010. A long list of outstanding bugs has been fixed, so we are very happy to present you redis 2.0 and we highly recommend updating as soon as possible. -#What's new in 2.0 +# What's new in 2.0 - Implemented a "connection is broken" mode if no connection could be established - node_redis no longer throws under any circumstances, preventing it from terminating applications. diff --git a/index.js b/index.js index a0e17e6098d..01b135907b3 100644 --- a/index.js +++ b/index.js @@ -318,6 +318,9 @@ RedisClient.prototype.on_ready = function () { self.emit("ready"); } }; + if (this.options.disable_resubscribing) { + return; + } Object.keys(this.subscription_set).forEach(function (key) { var space_index = key.indexOf(" "); var parts = [key.slice(0, space_index), key.slice(space_index + 1)]; diff --git a/test/pubsub.spec.js b/test/pubsub.spec.js index 4cf801758dd..d8c93ae9f61 100644 --- a/test/pubsub.spec.js +++ b/test/pubsub.spec.js @@ -26,20 +26,67 @@ describe("publish/subscribe", function () { pub.once("connect", function () { pub.flushdb(function () { pubConnected = true; + if (subConnected) { + done(); + } }); }); sub.once("error", done); sub.once("connect", function () { subConnected = true; + if (pubConnected) { + done(); + } }); + }); - var id = setInterval(function () { - if (pubConnected && subConnected) { - clearInterval(id); - return done(); - } - }, 50); + describe('disable resubscribe', function () { + beforeEach(function (done) { + var pubConnected; + var subConnected; + + pub = redis.createClient(); + sub = redis.createClient({ + disable_resubscribing: false + }); + pub.once("error", done); + pub.once("connect", function () { + pubConnected = true; + if (subConnected) { + done(); + } + }); + + sub.once("error", done); + sub.once("connect", function () { + subConnected = true; + if (pubConnected) { + done(); + } + }); + }); + + it('does not fire subscribe events after reconnecting', function (done) { + var a = false; + sub.on("subscribe", function (chnl, count) { + if (chnl === channel2) { + if (a) { + return done(new Error('Test failed')); + } + assert.equal(2, count); + sub.stream.destroy(); + } + }); + + sub.on('reconnecting', function() { + a = true; + }); + + sub.subscribe(channel, channel2); + + setTimeout(done, 250); + }); }); describe('subscribe', function () { From 25113e67591b0d451cb29c357a78dbd503387797 Mon Sep 17 00:00:00 2001 From: Ruben Bridgewater Date: Sat, 3 Oct 2015 04:13:04 +0200 Subject: [PATCH 0406/1748] Fix test --- test/pubsub.spec.js | 8 +------- 1 file changed, 1 insertion(+), 7 deletions(-) diff --git a/test/pubsub.spec.js b/test/pubsub.spec.js index d8c93ae9f61..b39b532cbde 100644 --- a/test/pubsub.spec.js +++ b/test/pubsub.spec.js @@ -22,7 +22,6 @@ describe("publish/subscribe", function () { pub = redis.createClient.apply(redis.createClient, args); sub = redis.createClient.apply(redis.createClient, args); - pub.once("error", done); pub.once("connect", function () { pub.flushdb(function () { pubConnected = true; @@ -31,8 +30,6 @@ describe("publish/subscribe", function () { } }); }); - - sub.once("error", done); sub.once("connect", function () { subConnected = true; if (pubConnected) { @@ -48,17 +45,14 @@ describe("publish/subscribe", function () { pub = redis.createClient(); sub = redis.createClient({ - disable_resubscribing: false + disable_resubscribing: true }); - pub.once("error", done); pub.once("connect", function () { pubConnected = true; if (subConnected) { done(); } }); - - sub.once("error", done); sub.once("connect", function () { subConnected = true; if (pubConnected) { From 0db1152492d995416cafca1cb01f538522761430 Mon Sep 17 00:00:00 2001 From: bcoe Date: Sat, 3 Oct 2015 21:08:00 -0700 Subject: [PATCH 0407/1748] tests were failing on my old mac (node 0.10.40, osx 10.9.5, 2.8.7) --- test/helper.js | 6 +++--- test/node_redis.spec.js | 4 ++-- 2 files changed, 5 insertions(+), 5 deletions(-) diff --git a/test/helper.js b/test/helper.js index 5f4f791b954..b92f3d5e1b5 100644 --- a/test/helper.js +++ b/test/helper.js @@ -159,11 +159,11 @@ module.exports = { killConnection: function (client) { // Change the connection option to a non existing one and destroy the stream client.connectionOption = { - port: 6370, - host: '127.0.0.2', + port: 999999, + host: '127.0.0.1', family: 4 }; - client.address = '127.0.0.2:6370'; + client.address = '127.0.0.1:999999'; client.stream.destroy(); } }; diff --git a/test/node_redis.spec.js b/test/node_redis.spec.js index f45480eb79b..f3df657fa5f 100644 --- a/test/node_redis.spec.js +++ b/test/node_redis.spec.js @@ -214,7 +214,7 @@ describe("The node_redis client", function () { var client = redis.createClient(); client.quit(function() { client.get("foo", function(err, res) { - assert.strictEqual(err.message, 'Redis connection gone from end event.'); + assert(err.message.indexOf('Redis connection gone') !== -1); assert.strictEqual(client.offline_queue.length, 0); done(); }); @@ -619,7 +619,7 @@ describe("The node_redis client", function () { client.set('baz', 13); client.set('foo', 'bar', function(err, result) { assert(i, 3); - assert('Redis connection gone from error event', err.message); + assert(err); assert.strictEqual(client.offline_queue.length, 0); }); }); From aad5045c8e545386e8a54fa371345f41cf399f6b Mon Sep 17 00:00:00 2001 From: bcoe Date: Sat, 3 Oct 2015 21:13:18 -0700 Subject: [PATCH 0408/1748] use the max port # in newer versions of node --- test/helper.js | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/test/helper.js b/test/helper.js index b92f3d5e1b5..d007676ea62 100644 --- a/test/helper.js +++ b/test/helper.js @@ -159,11 +159,11 @@ module.exports = { killConnection: function (client) { // Change the connection option to a non existing one and destroy the stream client.connectionOption = { - port: 999999, + port: 65535, host: '127.0.0.1', family: 4 }; - client.address = '127.0.0.1:999999'; + client.address = '127.0.0.1:65535'; client.stream.destroy(); } }; From b7731199671ed463f5660fcc4ba7bec78cd51d41 Mon Sep 17 00:00:00 2001 From: bcoe Date: Sat, 3 Oct 2015 21:28:14 -0700 Subject: [PATCH 0409/1748] the monitor test should not be within the block of tests related to redis closing unexpectedly --- test/node_redis.spec.js | 65 ++++++++++++++++++++--------------------- 1 file changed, 32 insertions(+), 33 deletions(-) diff --git a/test/node_redis.spec.js b/test/node_redis.spec.js index f3df657fa5f..cd6659686a3 100644 --- a/test/node_redis.spec.js +++ b/test/node_redis.spec.js @@ -355,42 +355,41 @@ describe("The node_redis client", function () { } }); }); + }); - describe('monitor', function () { - it('monitors commands on all other redis clients', function (done) { - helper.serverVersionAtLeast.call(this, client, [2, 6, 0]); - - var monitorClient = redis.createClient.apply(redis.createClient, args); - var responses = []; - - monitorClient.monitor(function (err, res) { - client.mget("some", "keys", "foo", "bar"); - client.set("json", JSON.stringify({ - foo: "123", - bar: "sdflkdfsjk", - another: false - })); - }); - - monitorClient.on("monitor", function (time, args) { - responses.push(args); - if (responses.length === 2) { - assert.strictEqual(5, responses[0].length); - assert.strictEqual("mget", responses[0][0]); - assert.strictEqual("some", responses[0][1]); - assert.strictEqual("keys", responses[0][2]); - assert.strictEqual("foo", responses[0][3]); - assert.strictEqual("bar", responses[0][4]); - assert.strictEqual(3, responses[1].length); - assert.strictEqual("set", responses[1][0]); - assert.strictEqual("json", responses[1][1]); - assert.strictEqual('{"foo":"123","bar":"sdflkdfsjk","another":false}', responses[1][2]); - monitorClient.quit(done); - } - }); + describe('monitor', function () { + it('monitors commands on all other redis clients', function (done) { + helper.serverVersionAtLeast.call(this, client, [2, 6, 0]); + + var monitorClient = redis.createClient.apply(redis.createClient, args); + var responses = []; + + monitorClient.monitor(function (err, res) { + client.mget("some", "keys", "foo", "bar"); + client.set("json", JSON.stringify({ + foo: "123", + bar: "sdflkdfsjk", + another: false + })); + }); + + monitorClient.on("monitor", function (time, args) { + responses.push(args); + if (responses.length === 2) { + assert.strictEqual(5, responses[0].length); + assert.strictEqual("mget", responses[0][0]); + assert.strictEqual("some", responses[0][1]); + assert.strictEqual("keys", responses[0][2]); + assert.strictEqual("foo", responses[0][3]); + assert.strictEqual("bar", responses[0][4]); + assert.strictEqual(3, responses[1].length); + assert.strictEqual("set", responses[1][0]); + assert.strictEqual("json", responses[1][1]); + assert.strictEqual('{"foo":"123","bar":"sdflkdfsjk","another":false}', responses[1][2]); + monitorClient.quit(done); + } }); }); - }); describe('idle', function () { From 7922d4eb8552b287b30a18a744c9ab27674c12e5 Mon Sep 17 00:00:00 2001 From: Ruben Bridgewater Date: Sun, 4 Oct 2015 21:44:52 +0200 Subject: [PATCH 0410/1748] Small style changes --- README.md | 8 +- benchmarks/buffer_bench.js | 26 +-- benchmarks/diff_multi_bench_output.js | 2 +- benchmarks/multi_bench.js | 98 ++++---- changelog.md | 2 +- examples/auth.js | 6 +- examples/backpressure_drain.js | 14 +- examples/eval.js | 6 +- examples/extend.js | 6 +- examples/file.js | 16 +- examples/mget.js | 4 +- examples/monitor.js | 10 +- examples/multi.js | 26 +-- examples/multi2.js | 14 +- examples/psubscribe.js | 22 +- examples/pub_sub.js | 30 +-- examples/scan.js | 29 ++- examples/simple.js | 22 +- examples/sort.js | 22 +- examples/subqueries.js | 8 +- examples/subquery.js | 10 +- examples/unix_socket.js | 18 +- examples/web_server.js | 22 +- index.js | 323 +++++++++++++------------- lib/parsers/hiredis.js | 4 +- lib/parsers/javascript.js | 20 +- lib/queue.js | 2 +- lib/utils.js | 4 +- 28 files changed, 385 insertions(+), 389 deletions(-) diff --git a/README.md b/README.md index 90a34d1c606..4692728f31a 100644 --- a/README.md +++ b/README.md @@ -196,13 +196,13 @@ once the connection has been established. Setting `enable_offline_queue` to with an error, or an error will be emitted if no callback is specified. * `retry_max_delay`: *null*; By default every time the client tries to connect and fails the reconnection delay almost doubles. This delay normally grows infinitely, but setting `retry_max_delay` limits it to the maximum value, provided in milliseconds. -* `connect_timeout` *86400000*; Setting `connect_timeout` limits total time for client to reconnect. +* `connect_timeout`: *86400000*; Setting `connect_timeout` limits total time for client to reconnect. The value is provided in milliseconds and is counted once the disconnect occurred. The last retry is going to happen exactly at the timeout time. That way the default is to try reconnecting until 24h passed. -* `max_attempts` *0*; By default client will try reconnecting until connected. Setting `max_attempts` +* `max_attempts`: *0*; By default client will try reconnecting until connected. Setting `max_attempts` limits total amount of connection tries. Setting this to 1 will prevent any reconnect tries. -* `auth_pass` *null*; If set, client will run redis auth command on connect. -* `family` *IPv4*; You can force using IPv6 if you set the family to 'IPv6'. See Node.js [net](https://nodejs.org/api/net.html) or [dns](https://nodejs.org/api/dns.html) modules how to use the family type. +* `auth_pass`: *null*; If set, client will run redis auth command on connect. +* `family`: *IPv4*; You can force using IPv6 if you set the family to 'IPv6'. See Node.js [net](https://nodejs.org/api/net.html) or [dns](https://nodejs.org/api/dns.html) modules how to use the family type. * `disable_resubscribing`: *false*; If set to `true`, a client won't resubscribe after disconnecting ```js diff --git a/benchmarks/buffer_bench.js b/benchmarks/buffer_bench.js index 64347e583f0..63dae1e606b 100644 --- a/benchmarks/buffer_bench.js +++ b/benchmarks/buffer_bench.js @@ -7,56 +7,56 @@ for (i = 99 ; i >= 0 ; i--) { source[i] = 120; } -var str = "This is a nice String.", - buf = new Buffer("This is a lovely Buffer."); +var str = 'This is a nice String.', + buf = new Buffer('This is a lovely Buffer.'); var start = new Date(); for (i = count * 100; i > 0 ; i--) { if (Buffer.isBuffer(str)) {} } var end = new Date(); -console.log("Buffer.isBuffer(str) " + (end - start) + " ms"); +console.log('Buffer.isBuffer(str) ' + (end - start) + ' ms'); var start = new Date(); for (i = count * 100; i > 0 ; i--) { if (Buffer.isBuffer(buf)) {} } var end = new Date(); -console.log("Buffer.isBuffer(buf) " + (end - start) + " ms"); +console.log('Buffer.isBuffer(buf) ' + (end - start) + ' ms'); var start = new Date(); for (i = count * 100; i > 0 ; i--) { if (str instanceof Buffer) {} } var end = new Date(); -console.log("str instanceof Buffer " + (end - start) + " ms"); +console.log('str instanceof Buffer ' + (end - start) + ' ms'); var start = new Date(); for (i = count * 100; i > 0 ; i--) { if (buf instanceof Buffer) {} } var end = new Date(); -console.log("buf instanceof Buffer " + (end - start) + " ms"); +console.log('buf instanceof Buffer ' + (end - start) + ' ms'); for (i = bytes ; i > 0 ; i --) { var start = new Date(); for (j = count ; j > 0; j--) { - tmp = source.toString("ascii", 0, bytes); + tmp = source.toString('ascii', 0, bytes); } var end = new Date(); - console.log("toString() " + i + " bytes " + (end - start) + " ms"); + console.log('toString() ' + i + ' bytes ' + (end - start) + ' ms'); } for (i = bytes ; i > 0 ; i --) { var start = new Date(); for (j = count ; j > 0; j--) { - tmp = ""; + tmp = ''; for (k = 0; k <= i ; k++) { tmp += String.fromCharCode(source[k]); } } var end = new Date(); - console.log("manual string " + i + " bytes " + (end - start) + " ms"); + console.log('manual string ' + i + ' bytes ' + (end - start) + ' ms'); } for (i = bytes ; i > 0 ; i--) { @@ -67,7 +67,7 @@ for (i = bytes ; i > 0 ; i--) { } } var end = new Date(); - console.log("Manual copy " + i + " bytes " + (end - start) + " ms"); + console.log('Manual copy ' + i + ' bytes ' + (end - start) + ' ms'); } for (i = bytes ; i > 0 ; i--) { @@ -78,7 +78,7 @@ for (i = bytes ; i > 0 ; i--) { } } var end = new Date(); - console.log("Direct assignment " + i + " bytes " + (end - start) + " ms"); + console.log('Direct assignment ' + i + ' bytes ' + (end - start) + ' ms'); } for (i = bytes ; i > 0 ; i--) { @@ -87,5 +87,5 @@ for (i = bytes ; i > 0 ; i--) { source.copy(dest, 0, 0, i); } var end = new Date(); - console.log("Buffer.copy() " + i + " bytes " + (end - start) + " ms"); + console.log('Buffer.copy() ' + i + ' bytes ' + (end - start) + ' ms'); } diff --git a/benchmarks/diff_multi_bench_output.js b/benchmarks/diff_multi_bench_output.js index 86a9414d045..fb2a4a66ce7 100755 --- a/benchmarks/diff_multi_bench_output.js +++ b/benchmarks/diff_multi_bench_output.js @@ -40,7 +40,7 @@ function parseInt10(s) { // green if greater than 0, red otherwise function humanize_diff(num, unit) { - unit = unit || ""; + unit = unit || ''; if (num > 0) { return ('+' + num + unit).green; } diff --git a/benchmarks/multi_bench.js b/benchmarks/multi_bench.js index 05fcd2dc043..6101b41dced 100644 --- a/benchmarks/multi_bench.js +++ b/benchmarks/multi_bench.js @@ -1,11 +1,11 @@ 'use strict'; var path = require('path'); -var RedisProcess = require("../test/lib/redis-process"); +var RedisProcess = require('../test/lib/redis-process'); var rp; -var redis = require("../index"); +var redis = require('../index'); var totalTime = 0; -var metrics = require("metrics"); +var metrics = require('metrics'); var num_clients = parseInt(process.argv[2], 10) || 5; var num_requests = 50000; var tests = []; @@ -19,7 +19,7 @@ var small_str, large_str, small_buf, large_buf, very_large_str, very_large_buf; function lpad(input, len, chr) { var str = input.toString(); - chr = chr || " "; + chr = chr || ' '; while (str.length < len) { str = chr + str; @@ -30,7 +30,7 @@ function lpad(input, len, chr) { metrics.Histogram.prototype.print_line = function () { var obj = this.printObj(); - return lpad(obj.min, 4) + "/" + lpad(obj.max, 4) + "/" + lpad(obj.mean.toFixed(2), 7) + "/" + lpad(obj.p95.toFixed(2), 7); + return lpad(obj.min, 4) + '/' + lpad(obj.max, 4) + '/' + lpad(obj.mean.toFixed(2), 7) + '/' + lpad(obj.p95.toFixed(2), 7); }; function Test(args) { @@ -66,14 +66,14 @@ Test.prototype.new_client = function (id) { new_client = redis.createClient(this.client_options); new_client.create_time = Date.now(); - new_client.on("connect", function () { + new_client.on('connect', function () { self.connect_latency.update(Date.now() - new_client.create_time); }); - new_client.on("ready", function () { + new_client.on('ready', function () { if (! versions_logged) { - console.log("Client count: " + num_clients + ", node version: " + process.versions.node + ", server version: " + - new_client.server_info.redis_version + ", parser: " + new_client.reply_parser.name); + console.log('Client count: ' + num_clients + ', node version: ' + process.versions.node + ', server version: ' + + new_client.server_info.redis_version + ', parser: ' + new_client.reply_parser.name); versions_logged = true; } self.ready_latency.update(Date.now() - new_client.create_time); @@ -84,7 +84,7 @@ Test.prototype.new_client = function (id) { }); // If no redis server is running, start one - new_client.on("error", function(err) { + new_client.on('error', function(err) { if (err.code === 'CONNECTION_BROKEN') { throw err; } @@ -105,7 +105,7 @@ Test.prototype.new_client = function (id) { }; Test.prototype.on_clients_ready = function () { - process.stdout.write(lpad(this.args.descr, 13) + ", " + lpad(this.args.pipeline, 5) + "/" + this.clients_ready + " "); + process.stdout.write(lpad(this.args.descr, 13) + ', ' + lpad(this.args.pipeline, 5) + '/' + this.clients_ready + ' '); this.test_start = Date.now(); this.fill_pipeline(); @@ -159,64 +159,64 @@ Test.prototype.print_stats = function () { var duration = Date.now() - this.test_start; totalTime += duration; - console.log("min/max/avg/p95: " + this.command_latency.print_line() + " " + lpad(duration, 6) + "ms total, " + - lpad((this.num_requests / (duration / 1000)).toFixed(2), 8) + " ops/sec"); + console.log('min/max/avg/p95: ' + this.command_latency.print_line() + ' ' + lpad(duration, 6) + 'ms total, ' + + lpad((this.num_requests / (duration / 1000)).toFixed(2), 8) + ' ops/sec'); }; -small_str = "1234"; +small_str = '1234'; small_buf = new Buffer(small_str); -large_str = (new Array(4096 + 1).join("-")); +large_str = (new Array(4096 + 1).join('-')); large_buf = new Buffer(large_str); -very_large_str = (new Array((4 * 1024 * 1024) + 1).join("-")); +very_large_str = (new Array((4 * 1024 * 1024) + 1).join('-')); very_large_buf = new Buffer(very_large_str); -tests.push(new Test({descr: "PING", command: "ping", args: [], pipeline: 1})); -tests.push(new Test({descr: "PING", command: "ping", args: [], pipeline: 50})); +tests.push(new Test({descr: 'PING', command: 'ping', args: [], pipeline: 1})); +tests.push(new Test({descr: 'PING', command: 'ping', args: [], pipeline: 50})); -tests.push(new Test({descr: "SET 4B str", command: "set", args: ["foo_rand000000000000", small_str], pipeline: 1})); -tests.push(new Test({descr: "SET 4B str", command: "set", args: ["foo_rand000000000000", small_str], pipeline: 50})); +tests.push(new Test({descr: 'SET 4B str', command: 'set', args: ['foo_rand000000000000', small_str], pipeline: 1})); +tests.push(new Test({descr: 'SET 4B str', command: 'set', args: ['foo_rand000000000000', small_str], pipeline: 50})); -tests.push(new Test({descr: "SET 4B buf", command: "set", args: ["foo_rand000000000000", small_buf], pipeline: 1})); -tests.push(new Test({descr: "SET 4B buf", command: "set", args: ["foo_rand000000000000", small_buf], pipeline: 50})); +tests.push(new Test({descr: 'SET 4B buf', command: 'set', args: ['foo_rand000000000000', small_buf], pipeline: 1})); +tests.push(new Test({descr: 'SET 4B buf', command: 'set', args: ['foo_rand000000000000', small_buf], pipeline: 50})); -tests.push(new Test({descr: "GET 4B str", command: "get", args: ["foo_rand000000000000"], pipeline: 1})); -tests.push(new Test({descr: "GET 4B str", command: "get", args: ["foo_rand000000000000"], pipeline: 50})); +tests.push(new Test({descr: 'GET 4B str', command: 'get', args: ['foo_rand000000000000'], pipeline: 1})); +tests.push(new Test({descr: 'GET 4B str', command: 'get', args: ['foo_rand000000000000'], pipeline: 50})); -tests.push(new Test({descr: "GET 4B buf", command: "get", args: ["foo_rand000000000000"], pipeline: 1, client_opts: { return_buffers: true} })); -tests.push(new Test({descr: "GET 4B buf", command: "get", args: ["foo_rand000000000000"], pipeline: 50, client_opts: { return_buffers: true} })); +tests.push(new Test({descr: 'GET 4B buf', command: 'get', args: ['foo_rand000000000000'], pipeline: 1, client_opts: { return_buffers: true} })); +tests.push(new Test({descr: 'GET 4B buf', command: 'get', args: ['foo_rand000000000000'], pipeline: 50, client_opts: { return_buffers: true} })); -tests.push(new Test({descr: "SET 4KiB str", command: "set", args: ["foo_rand000000000001", large_str], pipeline: 1})); -tests.push(new Test({descr: "SET 4KiB str", command: "set", args: ["foo_rand000000000001", large_str], pipeline: 50})); +tests.push(new Test({descr: 'SET 4KiB str', command: 'set', args: ['foo_rand000000000001', large_str], pipeline: 1})); +tests.push(new Test({descr: 'SET 4KiB str', command: 'set', args: ['foo_rand000000000001', large_str], pipeline: 50})); -tests.push(new Test({descr: "SET 4KiB buf", command: "set", args: ["foo_rand000000000001", large_buf], pipeline: 1})); -tests.push(new Test({descr: "SET 4KiB buf", command: "set", args: ["foo_rand000000000001", large_buf], pipeline: 50})); +tests.push(new Test({descr: 'SET 4KiB buf', command: 'set', args: ['foo_rand000000000001', large_buf], pipeline: 1})); +tests.push(new Test({descr: 'SET 4KiB buf', command: 'set', args: ['foo_rand000000000001', large_buf], pipeline: 50})); -tests.push(new Test({descr: "GET 4KiB str", command: "get", args: ["foo_rand000000000001"], pipeline: 1})); -tests.push(new Test({descr: "GET 4KiB str", command: "get", args: ["foo_rand000000000001"], pipeline: 50})); +tests.push(new Test({descr: 'GET 4KiB str', command: 'get', args: ['foo_rand000000000001'], pipeline: 1})); +tests.push(new Test({descr: 'GET 4KiB str', command: 'get', args: ['foo_rand000000000001'], pipeline: 50})); -tests.push(new Test({descr: "GET 4KiB buf", command: "get", args: ["foo_rand000000000001"], pipeline: 1, client_opts: { return_buffers: true} })); -tests.push(new Test({descr: "GET 4KiB buf", command: "get", args: ["foo_rand000000000001"], pipeline: 50, client_opts: { return_buffers: true} })); +tests.push(new Test({descr: 'GET 4KiB buf', command: 'get', args: ['foo_rand000000000001'], pipeline: 1, client_opts: { return_buffers: true} })); +tests.push(new Test({descr: 'GET 4KiB buf', command: 'get', args: ['foo_rand000000000001'], pipeline: 50, client_opts: { return_buffers: true} })); -tests.push(new Test({descr: "INCR", command: "incr", args: ["counter_rand000000000000"], pipeline: 1})); -tests.push(new Test({descr: "INCR", command: "incr", args: ["counter_rand000000000000"], pipeline: 50})); +tests.push(new Test({descr: 'INCR', command: 'incr', args: ['counter_rand000000000000'], pipeline: 1})); +tests.push(new Test({descr: 'INCR', command: 'incr', args: ['counter_rand000000000000'], pipeline: 50})); -tests.push(new Test({descr: "LPUSH", command: "lpush", args: ["mylist", small_str], pipeline: 1})); -tests.push(new Test({descr: "LPUSH", command: "lpush", args: ["mylist", small_str], pipeline: 50})); +tests.push(new Test({descr: 'LPUSH', command: 'lpush', args: ['mylist', small_str], pipeline: 1})); +tests.push(new Test({descr: 'LPUSH', command: 'lpush', args: ['mylist', small_str], pipeline: 50})); -tests.push(new Test({descr: "LRANGE 10", command: "lrange", args: ["mylist", "0", "9"], pipeline: 1})); -tests.push(new Test({descr: "LRANGE 10", command: "lrange", args: ["mylist", "0", "9"], pipeline: 50})); +tests.push(new Test({descr: 'LRANGE 10', command: 'lrange', args: ['mylist', '0', '9'], pipeline: 1})); +tests.push(new Test({descr: 'LRANGE 10', command: 'lrange', args: ['mylist', '0', '9'], pipeline: 50})); -tests.push(new Test({descr: "LRANGE 100", command: "lrange", args: ["mylist", "0", "99"], pipeline: 1})); -tests.push(new Test({descr: "LRANGE 100", command: "lrange", args: ["mylist", "0", "99"], pipeline: 50})); +tests.push(new Test({descr: 'LRANGE 100', command: 'lrange', args: ['mylist', '0', '99'], pipeline: 1})); +tests.push(new Test({descr: 'LRANGE 100', command: 'lrange', args: ['mylist', '0', '99'], pipeline: 50})); -tests.push(new Test({descr: "SET 4MiB buf", command: "set", args: ["foo_rand000000000002", very_large_buf], pipeline: 1, reqs: 500})); -tests.push(new Test({descr: "SET 4MiB buf", command: "set", args: ["foo_rand000000000002", very_large_buf], pipeline: 50, reqs: 500})); +tests.push(new Test({descr: 'SET 4MiB buf', command: 'set', args: ['foo_rand000000000002', very_large_buf], pipeline: 1, reqs: 500})); +tests.push(new Test({descr: 'SET 4MiB buf', command: 'set', args: ['foo_rand000000000002', very_large_buf], pipeline: 50, reqs: 500})); -tests.push(new Test({descr: "GET 4MiB str", command: "get", args: ["foo_rand000000000002"], pipeline: 1, reqs: 100})); -tests.push(new Test({descr: "GET 4MiB str", command: "get", args: ["foo_rand000000000002"], pipeline: 50, reqs: 100})); +tests.push(new Test({descr: 'GET 4MiB str', command: 'get', args: ['foo_rand000000000002'], pipeline: 1, reqs: 100})); +tests.push(new Test({descr: 'GET 4MiB str', command: 'get', args: ['foo_rand000000000002'], pipeline: 50, reqs: 100})); -tests.push(new Test({descr: "GET 4MiB buf", command: "get", args: ["foo_rand000000000002"], pipeline: 1, reqs: 100, client_opts: { return_buffers: true} })); -tests.push(new Test({descr: "GET 4MiB buf", command: "get", args: ["foo_rand000000000002"], pipeline: 50, reqs: 100, client_opts: { return_buffers: true} })); +tests.push(new Test({descr: 'GET 4MiB buf', command: 'get', args: ['foo_rand000000000002'], pipeline: 1, reqs: 100, client_opts: { return_buffers: true} })); +tests.push(new Test({descr: 'GET 4MiB buf', command: 'get', args: ['foo_rand000000000002'], pipeline: 50, reqs: 100, client_opts: { return_buffers: true} })); function next() { var test = tests.shift(); @@ -231,7 +231,7 @@ function next() { next(); }); } else { - console.log("End of tests. Total time elapsed:", totalTime, 'ms'); + console.log('End of tests. Total time elapsed:', totalTime, 'ms'); process.exit(0); } } diff --git a/changelog.md b/changelog.md index 1bf4e91bc7d..94b79b273d6 100644 --- a/changelog.md +++ b/changelog.md @@ -82,7 +82,7 @@ This is the biggest release that node_redis had since it was released in 2010. A - Fix commands being inconsistent and behaving wrong (@BridgeAR) - Channel names with spaces are now properly resubscribed after a reconnection (@pbihler) - Do not try to reconnect after the connection timeout has been exceeded (@BridgeAR) -- Removed bad .eval implementation (@BridgeAR) +- Ensure the execution order is observed if using .eval (@BridgeAR) - Fix commands not being rejected after calling .quit (@BridgeAR) - Fix .auth calling the callback twice if already connected (@BridgeAR) - Fix detect_buffers not working in pub sub mode and while monitoring (@BridgeAR) diff --git a/examples/auth.js b/examples/auth.js index 275c40961ea..c954a4047a4 100644 --- a/examples/auth.js +++ b/examples/auth.js @@ -1,7 +1,7 @@ 'use strict'; -var redis = require("redis"), +var redis = require('redis'), client = redis.createClient(); -// This command is magical. Client stashes the password and will issue on every connect. -client.auth("somepass"); +// The client stashes the password and will reauthenticate on every connect. +client.auth('somepass'); diff --git a/examples/backpressure_drain.js b/examples/backpressure_drain.js index 74107bb6a08..12cd24107a5 100644 --- a/examples/backpressure_drain.js +++ b/examples/backpressure_drain.js @@ -1,6 +1,6 @@ 'use strict'; -var redis = require("../index"), +var redis = require('../index'), client = redis.createClient(null, null, { command_queue_high_water: 5, command_queue_low_water: 1 @@ -9,26 +9,26 @@ var redis = require("../index"), function op() { if (remaining_ops <= 0) { - console.error("Finished."); + console.error('Finished.'); process.exit(0); } remaining_ops--; - if (client.hset("test hash", "val " + remaining_ops, remaining_ops) === false) { - console.log("Pausing at " + remaining_ops); + if (client.hset('test hash', 'val ' + remaining_ops, remaining_ops) === false) { + console.log('Pausing at ' + remaining_ops); paused = true; } else { process.nextTick(op); } } -client.on("drain", function () { +client.on('drain', function () { if (paused) { - console.log("Resuming at " + remaining_ops); + console.log('Resuming at ' + remaining_ops); paused = false; process.nextTick(op); } else { - console.log("Got drain while not paused at " + remaining_ops); + console.log('Got drain while not paused at ' + remaining_ops); } }); diff --git a/examples/eval.js b/examples/eval.js index fcdc99383d1..2fbcc427863 100644 --- a/examples/eval.js +++ b/examples/eval.js @@ -1,14 +1,14 @@ 'use strict'; -var redis = require("../index"), +var redis = require('../index'), client = redis.createClient(); -client.eval("return 100.5", 0, function (err, res) { +client.eval('return 100.5', 0, function (err, res) { console.dir(err); console.dir(res); }); -client.eval([ "return 100.5", 0 ], function (err, res) { +client.eval([ 'return 100.5', 0 ], function (err, res) { console.dir(err); console.dir(res); }); diff --git a/examples/extend.js b/examples/extend.js index 22566082d46..c132cebd589 100644 --- a/examples/extend.js +++ b/examples/extend.js @@ -1,14 +1,14 @@ 'use strict'; -var redis = require("redis"), +var redis = require('redis'), client = redis.createClient(); // Extend the RedisClient prototype to add a custom method -// This one converts the results from "INFO" into a JavaScript Object +// This one converts the results from 'INFO' into a JavaScript Object redis.RedisClient.prototype.parse_info = function (callback) { this.info(function (err, res) { - var lines = res.toString().split("\r\n").sort(); + var lines = res.toString().split('\r\n').sort(); var obj = {}; lines.forEach(function (line) { var parts = line.split(':'); diff --git a/examples/file.js b/examples/file.js index 620b0a10550..c68ec27d4d5 100644 --- a/examples/file.js +++ b/examples/file.js @@ -2,10 +2,10 @@ // Read a file from disk, store it in Redis, then read it back from Redis. -var redis = require("redis"), +var redis = require('redis'), client = redis.createClient(), - fs = require("fs"), - filename = "kids_in_cart.jpg"; + fs = require('fs'), + filename = 'kids_in_cart.jpg'; // Get the file I use for testing like this: // curl http://ranney.com/kids_in_cart.jpg -o kids_in_cart.jpg @@ -14,18 +14,18 @@ var redis = require("redis"), // Read a file from fs, store it in Redis, get it back from Redis, write it back to fs. fs.readFile(filename, function (err, data) { if (err) throw err; - console.log("Read " + data.length + " bytes from filesystem."); + console.log('Read ' + data.length + ' bytes from filesystem.'); client.set(filename, data, redis.print); // set entire file client.get(filename, function (err, reply) { // get entire file if (err) { - console.log("Get error: " + err); + console.log('Get error: ' + err); } else { - fs.writeFile("duplicate_" + filename, reply, function (err) { + fs.writeFile('duplicate_' + filename, reply, function (err) { if (err) { - console.log("Error on write: " + err); + console.log('Error on write: ' + err); } else { - console.log("File written."); + console.log('File written.'); } client.end(); }); diff --git a/examples/mget.js b/examples/mget.js index 30f2bce90a4..64dd3daa54c 100644 --- a/examples/mget.js +++ b/examples/mget.js @@ -1,7 +1,7 @@ 'use strict'; -var client = require("redis").createClient(); +var client = require('redis').createClient(); -client.mget(["sessions started", "sessions started", "foo"], function (err, res) { +client.mget(['sessions started', 'sessions started', 'foo'], function (err, res) { console.dir(res); }); \ No newline at end of file diff --git a/examples/monitor.js b/examples/monitor.js index c67f596294e..01a21228234 100644 --- a/examples/monitor.js +++ b/examples/monitor.js @@ -1,12 +1,12 @@ 'use strict'; -var client = require("../index").createClient(), - util = require("util"); +var client = require('../index').createClient(), + util = require('util'); client.monitor(function (err, res) { - console.log("Entering monitoring mode."); + console.log('Entering monitoring mode.'); }); -client.on("monitor", function (time, args) { - console.log(time + ": " + util.inspect(args)); +client.on('monitor', function (time, args) { + console.log(time + ': ' + util.inspect(args)); }); diff --git a/examples/multi.js b/examples/multi.js index 31bc14ffb00..ac7f01b831b 100644 --- a/examples/multi.js +++ b/examples/multi.js @@ -1,40 +1,40 @@ 'use strict'; -var redis = require("redis"), +var redis = require('redis'), client = redis.createClient(), set_size = 20; -client.sadd("bigset", "a member"); -client.sadd("bigset", "another member"); +client.sadd('bigset', 'a member'); +client.sadd('bigset', 'another member'); while (set_size > 0) { - client.sadd("bigset", "member " + set_size); + client.sadd('bigset', 'member ' + set_size); set_size -= 1; } // multi chain with an individual callback client.multi() - .scard("bigset") - .smembers("bigset") - .keys("*", function (err, replies) { + .scard('bigset') + .smembers('bigset') + .keys('*', function (err, replies) { client.mget(replies, redis.print); }) .dbsize() .exec(function (err, replies) { - console.log("MULTI got " + replies.length + " replies"); + console.log('MULTI got ' + replies.length + ' replies'); replies.forEach(function (reply, index) { - console.log("Reply " + index + ": " + reply.toString()); + console.log('Reply ' + index + ': ' + reply.toString()); }); }); -client.mset("incr thing", 100, "incr other thing", 1, redis.print); +client.mset('incr thing', 100, 'incr other thing', 1, redis.print); // start a separate multi command queue var multi = client.multi(); -multi.incr("incr thing", redis.print); -multi.incr("incr other thing", redis.print); +multi.incr('incr thing', redis.print); +multi.incr('incr other thing', redis.print); // runs immediately -client.get("incr thing", redis.print); // 100 +client.get('incr thing', redis.print); // 100 // drains multi queue and runs atomically multi.exec(function (err, replies) { diff --git a/examples/multi2.js b/examples/multi2.js index da722b23bbd..2071895dffb 100644 --- a/examples/multi2.js +++ b/examples/multi2.js @@ -1,15 +1,15 @@ 'use strict'; -var redis = require("redis"), +var redis = require('redis'), client = redis.createClient(), multi; // start a separate command queue for multi multi = client.multi(); -multi.incr("incr thing", redis.print); -multi.incr("incr other thing", redis.print); +multi.incr('incr thing', redis.print); +multi.incr('incr other thing', redis.print); // runs immediately -client.mset("incr thing", 100, "incr other thing", 1, redis.print); +client.mset('incr thing', 100, 'incr other thing', 1, redis.print); // drains multi queue and runs atomically multi.exec(function (err, replies) { @@ -23,9 +23,9 @@ multi.exec(function (err, replies) { }); client.multi([ - ["mget", "multifoo", "multibar", redis.print], - ["incr", "multifoo"], - ["incr", "multibar"] + ['mget', 'multifoo', 'multibar', redis.print], + ['incr', 'multifoo'], + ['incr', 'multibar'] ]).exec(function (err, replies) { console.log(replies.toString()); }); diff --git a/examples/psubscribe.js b/examples/psubscribe.js index 4adb23c4df0..65b113cd907 100644 --- a/examples/psubscribe.js +++ b/examples/psubscribe.js @@ -1,33 +1,33 @@ 'use strict'; -var redis = require("redis"), +var redis = require('redis'), client1 = redis.createClient(), client2 = redis.createClient(), client3 = redis.createClient(), client4 = redis.createClient(), msg_count = 0; -client1.on("psubscribe", function (pattern, count) { - console.log("client1 psubscribed to " + pattern + ", " + count + " total subscriptions"); - client2.publish("channeltwo", "Me!"); - client3.publish("channelthree", "Me too!"); - client4.publish("channelfour", "And me too!"); +client1.on('psubscribe', function (pattern, count) { + console.log('client1 psubscribed to ' + pattern + ', ' + count + ' total subscriptions'); + client2.publish('channeltwo', 'Me!'); + client3.publish('channelthree', 'Me too!'); + client4.publish('channelfour', 'And me too!'); }); -client1.on("punsubscribe", function (pattern, count) { - console.log("client1 punsubscribed from " + pattern + ", " + count + " total subscriptions"); +client1.on('punsubscribe', function (pattern, count) { + console.log('client1 punsubscribed from ' + pattern + ', ' + count + ' total subscriptions'); client4.end(); client3.end(); client2.end(); client1.end(); }); -client1.on("pmessage", function (pattern, channel, message) { - console.log("("+ pattern +")" + " client1 received message on " + channel + ": " + message); +client1.on('pmessage', function (pattern, channel, message) { + console.log('('+ pattern +')' + ' client1 received message on ' + channel + ': ' + message); msg_count += 1; if (msg_count === 3) { client1.punsubscribe(); } }); -client1.psubscribe("channel*"); +client1.psubscribe('channel*'); diff --git a/examples/pub_sub.js b/examples/pub_sub.js index 499b37e1bea..006c730839a 100644 --- a/examples/pub_sub.js +++ b/examples/pub_sub.js @@ -1,41 +1,41 @@ 'use strict'; -var redis = require("redis"), +var redis = require('redis'), client1 = redis.createClient(), msg_count = 0, client2 = redis.createClient(); -// Most clients probably don't do much on "subscribe". This example uses it to coordinate things within one program. -client1.on("subscribe", function (channel, count) { - console.log("client1 subscribed to " + channel + ", " + count + " total subscriptions"); +// Most clients probably don't do much on 'subscribe'. This example uses it to coordinate things within one program. +client1.on('subscribe', function (channel, count) { + console.log('client1 subscribed to ' + channel + ', ' + count + ' total subscriptions'); if (count === 2) { - client2.publish("a nice channel", "I am sending a message."); - client2.publish("another one", "I am sending a second message."); - client2.publish("a nice channel", "I am sending my last message."); + client2.publish('a nice channel', 'I am sending a message.'); + client2.publish('another one', 'I am sending a second message.'); + client2.publish('a nice channel', 'I am sending my last message.'); } }); -client1.on("unsubscribe", function (channel, count) { - console.log("client1 unsubscribed from " + channel + ", " + count + " total subscriptions"); +client1.on('unsubscribe', function (channel, count) { + console.log('client1 unsubscribed from ' + channel + ', ' + count + ' total subscriptions'); if (count === 0) { client2.end(); client1.end(); } }); -client1.on("message", function (channel, message) { - console.log("client1 channel " + channel + ": " + message); +client1.on('message', function (channel, message) { + console.log('client1 channel ' + channel + ': ' + message); msg_count += 1; if (msg_count === 3) { client1.unsubscribe(); } }); -client1.on("ready", function () { +client1.on('ready', function () { // if you need auth, do it here - client1.incr("did a thing"); - client1.subscribe("a nice channel", "another one"); + client1.incr('did a thing'); + client1.subscribe('a nice channel', 'another one'); }); -client2.on("ready", function () { +client2.on('ready', function () { // if you need auth, do it here }); diff --git a/examples/scan.js b/examples/scan.js index f4aac2ef4d3..e3152537cc4 100644 --- a/examples/scan.js +++ b/examples/scan.js @@ -1,6 +1,6 @@ 'use strict'; -var redis = require("redis"), +var redis = require('redis'), client = redis.createClient(); var cursor = '0'; @@ -8,8 +8,8 @@ var cursor = '0'; function scan() { client.scan( cursor, - "MATCH", "q:job:*", - "COUNT", "10", + 'MATCH', 'q:job:*', + 'COUNT', '10', function(err, res) { if (err) throw err; @@ -17,22 +17,21 @@ function scan() { cursor = res[0]; // From : - // "An iteration starts when the cursor is set to 0, - // and terminates when the cursor returned by the server is 0." + // 'An iteration starts when the cursor is set to 0, + // and terminates when the cursor returned by the server is 0.' if (cursor === '0') { return console.log('Iteration complete'); - } else { - // Remember: more or less than COUNT or no keys may be returned - // See http://redis.io/commands/scan#the-count-option - // Also, SCAN may return the same key multiple times - // See http://redis.io/commands/scan#scan-guarantees - - if (res[1].length > 0) { - console.log('Array of matching keys', res[1]); - } + } + // Remember: more or less than COUNT or no keys may be returned + // See http://redis.io/commands/scan#the-count-option + // Also, SCAN may return the same key multiple times + // See http://redis.io/commands/scan#scan-guarantees - return scan(); + if (res[1].length > 0) { + console.log('Array of matching keys', res[1]); } + + return scan(); } ); } diff --git a/examples/simple.js b/examples/simple.js index e8327498958..3e67242ddca 100644 --- a/examples/simple.js +++ b/examples/simple.js @@ -1,26 +1,26 @@ 'use strict'; -var redis = require("redis"), +var redis = require('redis'), client = redis.createClient(); -client.on("error", function (err) { - console.log("error event - " + client.host + ":" + client.port + " - " + err); +client.on('error', function (err) { + console.log('error event - ' + client.host + ':' + client.port + ' - ' + err); }); -client.set("string key", "string val", redis.print); -client.hset("hash key", "hashtest 1", "some value", redis.print); -client.hset(["hash key", "hashtest 2", "some other value"], redis.print); -client.hkeys("hash key", function (err, replies) { +client.set('string key', 'string val', redis.print); +client.hset('hash key', 'hashtest 1', 'some value', redis.print); +client.hset(['hash key', 'hashtest 2', 'some other value'], redis.print); +client.hkeys('hash key', function (err, replies) { if (err) { - return console.error("error response - " + err); + return console.error('error response - ' + err); } - console.log(replies.length + " replies:"); + console.log(replies.length + ' replies:'); replies.forEach(function (reply, i) { - console.log(" " + i + ": " + reply); + console.log(' ' + i + ': ' + reply); }); }); client.quit(function (err, res) { - console.log("Exiting from quit command."); + console.log('Exiting from quit command.'); }); diff --git a/examples/sort.js b/examples/sort.js index 4c804bee1db..2181cf30321 100644 --- a/examples/sort.js +++ b/examples/sort.js @@ -1,19 +1,19 @@ 'use strict'; -var redis = require("redis"), +var redis = require('redis'), client = redis.createClient(); -client.sadd("mylist", 1); -client.sadd("mylist", 2); -client.sadd("mylist", 3); +client.sadd('mylist', 1); +client.sadd('mylist', 2); +client.sadd('mylist', 3); -client.set("weight_1", 5); -client.set("weight_2", 500); -client.set("weight_3", 1); +client.set('weight_1', 5); +client.set('weight_2', 500); +client.set('weight_3', 1); -client.set("object_1", "foo"); -client.set("object_2", "bar"); -client.set("object_3", "qux"); +client.set('object_1', 'foo'); +client.set('object_2', 'bar'); +client.set('object_3', 'qux'); -client.sort("mylist", "by", "weight_*", "get", "object_*", redis.print); +client.sort('mylist', 'by', 'weight_*', 'get', 'object_*', redis.print); // Prints Reply: qux,foo,bar \ No newline at end of file diff --git a/examples/subqueries.js b/examples/subqueries.js index ebae6d48553..5677c129195 100644 --- a/examples/subqueries.js +++ b/examples/subqueries.js @@ -1,14 +1,14 @@ 'use strict'; // Sending commands in response to other commands. -// This example runs "type" against every key in the database +// This example runs 'type' against every key in the database // -var client = require("redis").createClient(); +var client = require('redis').createClient(); -client.keys("*", function (err, keys) { +client.keys('*', function (err, keys) { keys.forEach(function (key, pos) { client.type(key, function (err, keytype) { - console.log(key + " is " + keytype); + console.log(key + ' is ' + keytype); if (pos === (keys.length - 1)) { client.quit(); } diff --git a/examples/subquery.js b/examples/subquery.js index ffb7eca382e..355dd94abca 100644 --- a/examples/subquery.js +++ b/examples/subquery.js @@ -1,20 +1,16 @@ 'use strict'; -var client = require("redis").createClient(); - -function print_results(obj) { - console.dir(obj); -} +var client = require('redis').createClient(); // build a map of all keys and their types -client.keys("*", function (err, all_keys) { +client.keys('*', function (err, all_keys) { var key_types = {}; all_keys.forEach(function (key, pos) { // use second arg of forEach to get pos client.type(key, function (err, type) { key_types[key] = type; if (pos === all_keys.length - 1) { // callbacks all run in order - print_results(key_types); + console.dir(key_types); } }); }); diff --git a/examples/unix_socket.js b/examples/unix_socket.js index a6c74cbc3e7..28e5eb0c515 100644 --- a/examples/unix_socket.js +++ b/examples/unix_socket.js @@ -1,21 +1,21 @@ 'use strict'; -var redis = require("redis"), - client = redis.createClient("/tmp/redis.sock"), - profiler = require("v8-profiler"); +var redis = require('redis'), + client = redis.createClient('/tmp/redis.sock'), + profiler = require('v8-profiler'); -client.on("connect", function () { - console.log("Got Unix socket connection."); +client.on('connect', function () { + console.log('Got Unix socket connection.'); }); -client.on("error", function (err) { +client.on('error', function (err) { console.log(err.message); }); -client.set("space chars", "space value"); +client.set('space chars', 'space value'); setInterval(function () { - client.get("space chars"); + client.get('space chars'); }, 100); function done() { @@ -26,6 +26,6 @@ function done() { } setTimeout(function () { - console.log("Taking snapshot."); + console.log('Taking snapshot.'); profiler.takeSnapshot(); }, 5000); diff --git a/examples/web_server.js b/examples/web_server.js index dfd420bd1a3..6afd0c96f22 100644 --- a/examples/web_server.js +++ b/examples/web_server.js @@ -2,12 +2,12 @@ // A simple web server that generates dyanmic content based on responses from Redis -var http = require("http"), server, - redis_client = require("redis").createClient(); +var http = require('http'), server, + redis_client = require('redis').createClient(); server = http.createServer(function (request, response) { response.writeHead(200, { - "Content-Type": "text/plain" + 'Content-Type': 'text/plain' }); var redis_info, total_requests; @@ -15,18 +15,18 @@ server = http.createServer(function (request, response) { redis_client.info(function (err, reply) { redis_info = reply; // stash response in outer scope }); - redis_client.incr("requests", function (err, reply) { + redis_client.incr('requests', function (err, reply) { total_requests = reply; // stash response in outer scope }); - redis_client.hincrby("ip", request.connection.remoteAddress, 1); - redis_client.hgetall("ip", function (err, reply) { + redis_client.hincrby('ip', request.connection.remoteAddress, 1); + redis_client.hgetall('ip', function (err, reply) { // This is the last reply, so all of the previous replies must have completed already - response.write("This page was generated after talking to redis.\n\n" + - "Redis info:\n" + redis_info + "\n" + - "Total requests: " + total_requests + "\n\n" + - "IP count: \n"); + response.write('This page was generated after talking to redis.\n\n' + + 'Redis info:\n' + redis_info + '\n' + + 'Total requests: ' + total_requests + '\n\n' + + 'IP count: \n'); Object.keys(reply).forEach(function (ip) { - response.write(" " + ip + ": " + reply[ip] + "\n"); + response.write(' ' + ip + ': ' + reply[ip] + '\n'); }); response.end(); }); diff --git a/index.js b/index.js index 01b135907b3..f28f96a39f8 100644 --- a/index.js +++ b/index.js @@ -1,36 +1,36 @@ 'use strict'; -var net = require("net"), - URL = require("url"), - util = require("util"), - utils = require("./lib/utils"), - Queue = require("./lib/queue"), - Command = require("./lib/command"), - events = require("events"), - parsers = [], - // This static list of commands is updated from time to time. - // ./lib/commands.js can be updated with generate_commands.js - commands = require("./lib/commands"), - connection_id = 0, - default_port = 6379, - default_host = "127.0.0.1", - debug = function(msg) { - if (exports.debug_mode) { - console.error(msg); - } - }; +var net = require('net'); +var URL = require('url'); +var util = require('util'); +var utils = require('./lib/utils'); +var Queue = require('./lib/queue'); +var Command = require('./lib/command'); +var events = require('events'); +var parsers = []; +// This static list of commands is updated from time to time. +// ./lib/commands.js can be updated with generate_commands.js +var commands = require('./lib/commands'); +var connection_id = 0; +var default_port = 6379; +var default_host = '127.0.0.1'; +var debug = function(msg) { + if (exports.debug_mode) { + console.error(msg); + } +}; exports.debug_mode = /\bredis\b/i.test(process.env.NODE_DEBUG); // hiredis might not be installed try { - parsers.push(require("./lib/parsers/hiredis")); + parsers.push(require('./lib/parsers/hiredis')); } catch (err) { /* istanbul ignore next: won't be reached with tests */ - debug("Hiredis parser not installed."); + debug('Hiredis parser not installed.'); } -parsers.push(require("./lib/parsers/javascript")); +parsers.push(require('./lib/parsers/javascript')); function RedisClient(stream, options) { options = options || {}; @@ -76,36 +76,35 @@ function RedisClient(stream, options) { events.EventEmitter.call(this); } util.inherits(RedisClient, events.EventEmitter); -exports.RedisClient = RedisClient; RedisClient.prototype.install_stream_listeners = function() { var self = this; - this.stream.on("connect", function () { + this.stream.on('connect', function () { self.on_connect(); }); - this.stream.on("data", function (buffer_from_socket) { + this.stream.on('data', function (buffer_from_socket) { // The data.toString() has a significant impact on big chunks and therefor this should only be used if necessary - // debug("Net read " + this.address + " id " + this.connection_id + ": " + data.toString()); + // debug('Net read ' + this.address + ' id ' + this.connection_id + ': ' + data.toString()); self.reply_parser.execute(buffer_from_socket); }); - this.stream.on("error", function (err) { + this.stream.on('error', function (err) { self.on_error(err); }); - this.stream.on("close", function () { - self.connection_gone("close"); + this.stream.on('close', function () { + self.connection_gone('close'); }); - this.stream.on("end", function () { - self.connection_gone("end"); + this.stream.on('end', function () { + self.connection_gone('end'); }); - this.stream.on("drain", function () { + this.stream.on('drain', function () { self.should_buffer = false; - self.emit("drain"); + self.emit('drain'); }); }; @@ -122,8 +121,8 @@ RedisClient.prototype.unref = function () { debug("Unref'ing the socket connection"); this.stream.unref(); } else { - debug("Not connected yet, will unref later"); - this.once("connect", function () { + debug('Not connected yet, will unref later'); + this.once('connect', function () { this.unref(); }); } @@ -134,7 +133,7 @@ RedisClient.prototype.flush_and_error = function (error) { var command_obj; while (command_obj = this.offline_queue.shift()) { - if (typeof command_obj.callback === "function") { + if (typeof command_obj.callback === 'function') { error.command = command_obj.command.toUpperCase(); command_obj.callback(error); } @@ -142,7 +141,7 @@ RedisClient.prototype.flush_and_error = function (error) { this.offline_queue = new Queue(); while (command_obj = this.command_queue.shift()) { - if (typeof command_obj.callback === "function") { + if (typeof command_obj.callback === 'function') { error.command = command_obj.command.toUpperCase(); command_obj.callback(error); } @@ -155,16 +154,16 @@ RedisClient.prototype.on_error = function (err) { return; } - err.message = "Redis connection to " + this.address + " failed - " + err.message; + err.message = 'Redis connection to ' + this.address + ' failed - ' + err.message; debug(err.message); this.connected = false; this.ready = false; - this.emit("error", err); - // "error" events get turned into exceptions if they aren't listened for. If the user handled this error + this.emit('error', err); + // 'error' events get turned into exceptions if they aren't listened for. If the user handled this error // then we should try to reconnect. - this.connection_gone("error"); + this.connection_gone('error'); }; var noPasswordIsSet = /no password is set/; @@ -173,35 +172,35 @@ var loading = /LOADING/; RedisClient.prototype.do_auth = function () { var self = this; - debug("Sending auth to " + self.address + " id " + self.connection_id); + debug('Sending auth to ' + self.address + ' id ' + self.connection_id); self.send_anyway = true; - self.send_command("auth", [this.auth_pass], function (err, res) { + self.send_command('auth', [this.auth_pass], function (err, res) { if (err) { /* istanbul ignore if: this is almost impossible to test */ if (loading.test(err.message)) { // If redis is still loading the db, it will not authenticate and everything else will fail - debug("Redis still loading, trying to authenticate later"); + debug('Redis still loading, trying to authenticate later'); setTimeout(function () { self.do_auth(); }, 333); return; } else if (noPasswordIsSet.test(err.message)) { - debug("Warning: Redis server does not require a password, but a password was supplied."); + debug('Warning: Redis server does not require a password, but a password was supplied.'); err = null; - res = "OK"; + res = 'OK'; } else if (self.auth_callback) { self.auth_callback(err); self.auth_callback = null; return; } else { - self.emit("error", err); + self.emit('error', err); return; } } res = res.toString(); - debug("Auth succeeded " + self.address + " id " + self.connection_id); + debug('Auth succeeded ' + self.address + ' id ' + self.connection_id); if (self.auth_callback) { self.auth_callback(null, res); @@ -209,7 +208,7 @@ RedisClient.prototype.do_auth = function () { } // Now we are really connected - self.emit("connect"); + self.emit('connect'); self.initialize_retry_vars(); if (self.options.no_ready_check) { @@ -222,7 +221,7 @@ RedisClient.prototype.do_auth = function () { }; RedisClient.prototype.on_connect = function () { - debug("Stream connected " + this.address + " id " + this.connection_id); + debug('Stream connected ' + this.address + ' id ' + this.connection_id); this.connected = true; this.ready = false; @@ -240,7 +239,7 @@ RedisClient.prototype.on_connect = function () { if (typeof this.auth_pass === 'string') { this.do_auth(); } else { - this.emit("connect"); + this.emit('connect'); this.initialize_retry_vars(); if (this.options.no_ready_check) { @@ -258,7 +257,7 @@ RedisClient.prototype.init_parser = function () { if (!parsers.some(function (parser) { if (parser.name === self.options.parser) { self.parser_module = parser; - debug("Using parser module: " + self.parser_module.name); + debug('Using parser module: ' + self.parser_module.name); return true; } })) { @@ -267,7 +266,7 @@ RedisClient.prototype.init_parser = function () { throw new Error("Couldn't find named parser " + self.options.parser + " on this system"); } } else { - debug("Using default parser module: " + parsers[0].name); + debug('Using default parser module: ' + parsers[0].name); this.parser_module = parsers[0]; } @@ -310,39 +309,39 @@ RedisClient.prototype.on_ready = function () { this.pub_sub_mode = pub_sub_mode; } if (this.pub_sub_mode === true) { - // only emit "ready" when all subscriptions were made again + // only emit 'ready' when all subscriptions were made again var callback_count = 0; var callback = function () { callback_count--; if (callback_count === 0) { - self.emit("ready"); + self.emit('ready'); } }; if (this.options.disable_resubscribing) { return; } Object.keys(this.subscription_set).forEach(function (key) { - var space_index = key.indexOf(" "); + var space_index = key.indexOf(' '); var parts = [key.slice(0, space_index), key.slice(space_index + 1)]; - debug("Sending pub/sub on_ready " + parts[0] + ", " + parts[1]); + debug('Sending pub/sub on_ready ' + parts[0] + ', ' + parts[1]); callback_count++; - self.send_command(parts[0] + "scribe", [parts[1]], callback); + self.send_command(parts[0] + 'scribe', [parts[1]], callback); }); return; } if (this.monitoring) { - this.send_command("monitor", []); + this.send_command('monitor', []); } else { this.send_offline_queue(); } - this.emit("ready"); + this.emit('ready'); }; RedisClient.prototype.on_info_cmd = function (err, res) { if (err) { - err.message = "Ready check failed: " + err.message; - this.emit("error", err); + err.message = 'Ready check failed: ' + err.message; + this.emit('error', err); return; } @@ -356,7 +355,7 @@ RedisClient.prototype.on_info_cmd = function (err, res) { var self = this; var obj = {}; - var lines = res.toString().split("\r\n"); + var lines = res.toString().split('\r\n'); var i = 0; var key = 'db' + i; var line, retry_time, parts, sub_parts; @@ -390,15 +389,15 @@ RedisClient.prototype.on_info_cmd = function (err, res) { // expose info key/vals to users this.server_info = obj; - if (!obj.loading || obj.loading === "0") { - debug("Redis server ready."); + if (!obj.loading || obj.loading === '0') { + debug('Redis server ready.'); this.on_ready(); } else { retry_time = obj.loading_eta_seconds * 1000; if (retry_time > 1000) { retry_time = 1000; } - debug("Redis server still loading, trying again in " + retry_time); + debug('Redis server still loading, trying again in ' + retry_time); setTimeout(function () { self.ready_check(); }, retry_time); @@ -408,9 +407,9 @@ RedisClient.prototype.on_info_cmd = function (err, res) { RedisClient.prototype.ready_check = function () { var self = this; - debug("Checking server ready state..."); + debug('Checking server ready state...'); - this.send_anyway = true; // secret flag to send_command to send something even if not "ready" + this.send_anyway = true; // secret flag to send_command to send something even if not 'ready' this.info(function (err, res) { self.on_info_cmd(err, res); }); @@ -421,7 +420,7 @@ RedisClient.prototype.send_offline_queue = function () { var command_obj, buffered_writes = 0; while (command_obj = this.offline_queue.shift()) { - debug("Sending offline command: " + command_obj.command); + debug('Sending offline command: ' + command_obj.command); buffered_writes += !this.send_command(command_obj.command, command_obj.args, command_obj.callback); } this.offline_queue = new Queue(); @@ -429,14 +428,14 @@ RedisClient.prototype.send_offline_queue = function () { if (!buffered_writes) { this.should_buffer = false; - this.emit("drain"); + this.emit('drain'); } }; var retry_connection = function (self) { - debug("Retrying connection..."); + debug('Retrying connection...'); - self.emit("reconnecting", { + self.emit('reconnecting', { delay: self.retry_delay, attempt: self.attempts }); @@ -457,7 +456,7 @@ RedisClient.prototype.connection_gone = function (why) { return; } - debug("Redis connection is gone from " + why + " event."); + debug('Redis connection is gone from ' + why + ' event.'); this.connected = false; this.ready = false; @@ -475,14 +474,14 @@ RedisClient.prototype.connection_gone = function (why) { // since we are collapsing end and close, users don't expect to be called twice if (!this.emitted_end) { - this.emit("end"); + this.emit('end'); this.emitted_end = true; } // If this is a requested shutdown, then don't retry if (this.closing) { - debug("Connection ended from quit command, not retrying."); - this.flush_and_error(new Error("Redis connection gone from " + why + " event.")); + debug('Connection ended from quit command, not retrying.'); + this.flush_and_error(new Error('Redis connection gone from ' + why + ' event.')); return; } @@ -490,7 +489,7 @@ RedisClient.prototype.connection_gone = function (why) { var message = this.retry_totaltime >= this.connect_timeout ? 'connection timeout exceeded.' : 'maximum connection attempts exceeded.'; - var error = new Error("Redis connection in broken state: " + message); + var error = new Error('Redis connection in broken state: ' + message); error.code = 'CONNECTION_BROKEN'; this.flush_and_error(error); this.emit('error', error); @@ -527,11 +526,11 @@ RedisClient.prototype.return_error = function (err) { if (this.pub_sub_mode === false && queue_len === 0) { this.command_queue = new Queue(); - this.emit("idle"); + this.emit('idle'); } if (this.should_buffer && queue_len <= this.command_queue_low_water) { - this.emit("drain"); + this.emit('drain'); this.should_buffer = false; } @@ -545,7 +544,7 @@ RedisClient.prototype.return_error = function (err) { RedisClient.prototype.return_reply = function (reply) { var command_obj, len, type, timestamp, argindex, args, queue_len; - // If the "reply" here is actually a message received asynchronously due to a + // If the 'reply' here is actually a message received asynchronously due to a // pubsub subscription, don't pop the command queue as we'll only be consuming // the head command prematurely. if (this.pub_sub_mode && Array.isArray(reply) && reply[0]) { @@ -553,7 +552,7 @@ RedisClient.prototype.return_reply = function (reply) { } if (this.pub_sub_mode && (type === 'message' || type === 'pmessage')) { - debug("Received pubsub message"); + debug('Received pubsub message'); } else { command_obj = this.command_queue.shift(); } @@ -562,15 +561,15 @@ RedisClient.prototype.return_reply = function (reply) { if (this.pub_sub_mode === false && queue_len === 0) { this.command_queue = new Queue(); // explicitly reclaim storage from old Queue - this.emit("idle"); + this.emit('idle'); } if (this.should_buffer && queue_len <= this.command_queue_low_water) { - this.emit("drain"); + this.emit('drain'); this.should_buffer = false; } if (command_obj && !command_obj.sub_command) { - if (typeof command_obj.callback === "function") { + if (typeof command_obj.callback === 'function') { if ('exec' !== command_obj.command) { if (this.options.detect_buffers && command_obj.buffer_args === false) { // If detect_buffers option was specified, then the reply from the parser will be Buffers. @@ -586,7 +585,7 @@ RedisClient.prototype.return_reply = function (reply) { command_obj.callback(null, reply); } else { - debug("No callback for reply"); + debug('No callback for reply'); } } else if (this.pub_sub_mode || command_obj && command_obj.sub_command) { if (Array.isArray(reply)) { @@ -595,29 +594,29 @@ RedisClient.prototype.return_reply = function (reply) { } type = reply[0].toString(); - if (type === "message") { - this.emit("message", reply[1], reply[2]); // channel, message - } else if (type === "pmessage") { - this.emit("pmessage", reply[1], reply[2], reply[3]); // pattern, channel, message - } else if (type === "subscribe" || type === "unsubscribe" || type === "psubscribe" || type === "punsubscribe") { + if (type === 'message') { + this.emit('message', reply[1], reply[2]); // channel, message + } else if (type === 'pmessage') { + this.emit('pmessage', reply[1], reply[2], reply[3]); // pattern, channel, message + } else if (type === 'subscribe' || type === 'unsubscribe' || type === 'psubscribe' || type === 'punsubscribe') { if (reply[2] === 0) { this.pub_sub_mode = false; - debug("All subscriptions removed, exiting pub/sub mode"); + debug('All subscriptions removed, exiting pub/sub mode'); } else { this.pub_sub_mode = true; } // subscribe commands take an optional callback and also emit an event, but only the first response is included in the callback // TODO - document this or fix it so it works in a more obvious way - if (command_obj && typeof command_obj.callback === "function") { + if (command_obj && typeof command_obj.callback === 'function') { command_obj.callback(null, reply[1]); } this.emit(type, reply[1], reply[2]); // channel, count } else { - this.emit("error", new Error("subscriptions are active but got unknown reply type " + type)); + this.emit('error', new Error('subscriptions are active but got unknown reply type ' + type)); return; } } else if (!this.closing) { - this.emit("error", new Error("subscriptions are active but got an invalid reply: " + reply)); + this.emit('error', new Error('subscriptions are active but got an invalid reply: ' + reply)); return; } } @@ -627,29 +626,29 @@ RedisClient.prototype.return_reply = function (reply) { reply = reply.toString(); } // If in monitoring mode only two commands are valid ones: AUTH and MONITOR wich reply with OK - len = reply.indexOf(" "); + len = reply.indexOf(' '); timestamp = reply.slice(0, len); argindex = reply.indexOf('"'); args = reply.slice(argindex + 1, -1).split('" "').map(function (elem) { return elem.replace(/\\"/g, '"'); }); - this.emit("monitor", timestamp, args); + this.emit('monitor', timestamp, args); } else { - var err = new Error("node_redis command queue state error. If you can reproduce this, please report it."); + var err = new Error('node_redis command queue state error. If you can reproduce this, please report it.'); err.command_obj = command_obj; - this.emit("error", err); + this.emit('error', err); } }; RedisClient.prototype.send_command = function (command, args, callback) { - var arg, command_obj, i, elem_count, buffer_args, stream = this.stream, command_str = "", buffered_writes = 0, err; + var arg, command_obj, i, elem_count, buffer_args, stream = this.stream, command_str = '', buffered_writes = 0, err; if (args === undefined) { args = []; } else if (!callback) { - if (typeof args[args.length - 1] === "function") { + if (typeof args[args.length - 1] === 'function') { callback = args.pop(); - } else if (typeof args[args.length - 1] === "undefined") { + } else if (typeof args[args.length - 1] === 'undefined') { args.pop(); } } @@ -667,7 +666,7 @@ RedisClient.prototype.send_command = function (command, args, callback) { if (callback) { return callback && callback(err); } - this.emit("error", err); + this.emit('error', err); return; } } @@ -686,9 +685,9 @@ RedisClient.prototype.send_command = function (command, args, callback) { if (this.closing || !this.enable_offline_queue) { command = command.toUpperCase(); if (!this.closing) { - err = new Error(command + ' can\'t be processed. Stream not writeable and enable_offline_queue is deactivated.'); + err = new Error(command + " can't be processed. Stream not writeable and enable_offline_queue is deactivated."); } else { - err = new Error(command + ' can\'t be processed. The connection has already been closed.'); + err = new Error(command + " can't be processed. The connection has already been closed."); } err.command = command; if (callback) { @@ -697,23 +696,23 @@ RedisClient.prototype.send_command = function (command, args, callback) { this.emit('error', err); } } else { - debug("Queueing " + command + " for next server connection."); + debug('Queueing ' + command + ' for next server connection.'); this.offline_queue.push(command_obj); this.should_buffer = true; } return; } - if (command === "subscribe" || command === "psubscribe" || command === "unsubscribe" || command === "punsubscribe") { + if (command === 'subscribe' || command === 'psubscribe' || command === 'unsubscribe' || command === 'punsubscribe') { this.pub_sub_command(command_obj); - } else if (command === "monitor") { + } else if (command === 'monitor') { this.monitoring = true; - } else if (command === "quit") { + } else if (command === 'quit') { this.closing = true; } else if (this.pub_sub_mode === true) { - err = new Error("Connection in subscriber mode, only subscriber commands may be used"); + err = new Error('Connection in subscriber mode, only subscriber commands may be used'); err.command = command.toUpperCase(); - this.emit("error", err); + this.emit('error', err); return; } this.command_queue.push(command_obj); @@ -721,23 +720,23 @@ RedisClient.prototype.send_command = function (command, args, callback) { elem_count = args.length + 1; - // Always use "Multi bulk commands", but if passed any Buffer args, then do multiple writes, one for each arg. + // Always use 'Multi bulk commands', but if passed any Buffer args, then do multiple writes, one for each arg. // This means that using Buffers in commands is going to be slower, so use Strings if you don't already have a Buffer. - command_str = "*" + elem_count + "\r\n$" + command.length + "\r\n" + command + "\r\n"; + command_str = '*' + elem_count + '\r\n$' + command.length + '\r\n' + command + '\r\n'; if (!buffer_args) { // Build up a string and send entire command in one write for (i = 0; i < args.length; i += 1) { arg = args[i]; - if (typeof arg !== "string") { + if (typeof arg !== 'string') { arg = String(arg); } - command_str += "$" + Buffer.byteLength(arg) + "\r\n" + arg + "\r\n"; + command_str += '$' + Buffer.byteLength(arg) + '\r\n' + arg + '\r\n'; } - debug("Send " + this.address + " id " + this.connection_id + ": " + command_str); + debug('Send ' + this.address + ' id ' + this.connection_id + ': ' + command_str); buffered_writes += !stream.write(command_str); } else { - debug("Send command (" + command_str + ") has Buffer arguments"); + debug('Send command (' + command_str + ') has Buffer arguments'); buffered_writes += !stream.write(command_str); for (i = 0; i < args.length; i += 1) { @@ -748,21 +747,21 @@ RedisClient.prototype.send_command = function (command, args, callback) { if (Buffer.isBuffer(arg)) { if (arg.length === 0) { - debug("send_command: using empty string for 0 length buffer"); - buffered_writes += !stream.write("$0\r\n\r\n"); + debug('send_command: using empty string for 0 length buffer'); + buffered_writes += !stream.write('$0\r\n\r\n'); } else { - buffered_writes += !stream.write("$" + arg.length + "\r\n"); + buffered_writes += !stream.write('$' + arg.length + '\r\n'); buffered_writes += !stream.write(arg); - buffered_writes += !stream.write("\r\n"); - debug("send_command: buffer send " + arg.length + " bytes"); + buffered_writes += !stream.write('\r\n'); + debug('send_command: buffer send ' + arg.length + ' bytes'); } } else { - debug("send_command: string send " + Buffer.byteLength(arg) + " bytes: " + arg); - buffered_writes += !stream.write("$" + Buffer.byteLength(arg) + "\r\n" + arg + "\r\n"); + debug('send_command: string send ' + Buffer.byteLength(arg) + ' bytes: ' + arg); + buffered_writes += !stream.write('$' + Buffer.byteLength(arg) + '\r\n' + arg + '\r\n'); } } } - debug("send_command buffered_writes: " + buffered_writes, " should_buffer: " + this.should_buffer); + debug('send_command buffered_writes: ' + buffered_writes, ' should_buffer: ' + this.should_buffer); if (buffered_writes || this.command_queue.length >= this.command_queue_high_water) { this.should_buffer = true; } @@ -773,30 +772,30 @@ RedisClient.prototype.pub_sub_command = function (command_obj) { var i, key, command, args; if (this.pub_sub_mode === false) { - debug("Entering pub/sub mode from " + command_obj.command); + debug('Entering pub/sub mode from ' + command_obj.command); } this.pub_sub_mode = true; command_obj.sub_command = true; command = command_obj.command; args = command_obj.args; - if (command === "subscribe" || command === "psubscribe") { - if (command === "subscribe") { - key = "sub"; + if (command === 'subscribe' || command === 'psubscribe') { + if (command === 'subscribe') { + key = 'sub'; } else { - key = "psub"; + key = 'psub'; } for (i = 0; i < args.length; i++) { - this.subscription_set[key + " " + args[i]] = true; + this.subscription_set[key + ' ' + args[i]] = true; } } else { - if (command === "unsubscribe") { - key = "sub"; + if (command === 'unsubscribe') { + key = 'sub'; } else { - key = "psub"; + key = 'psub'; } for (i = 0; i < args.length; i++) { - delete this.subscription_set[key + " " + args[i]]; + delete this.subscription_set[key + ' ' + args[i]]; } } }; @@ -809,7 +808,7 @@ RedisClient.prototype.end = function (flush) { clearTimeout(this.retry_timer); this.retry_timer = null; } - this.stream.on("error", function noop(){}); + this.stream.on('error', function noop(){}); // Flush queue if wanted if (flush) { @@ -824,7 +823,7 @@ RedisClient.prototype.end = function (flush) { function Multi(client, args) { this._client = client; - this.queue = [["multi"]]; + this.queue = [['multi']]; var command, tmp_args; if (Array.isArray(args)) { while (tmp_args = args.shift()) { @@ -839,6 +838,10 @@ function Multi(client, args) { } } +RedisClient.prototype.multi = RedisClient.prototype.MULTI = function (args) { + return new Multi(this, args); +}; + commands.forEach(function (fullCommand) { var command = fullCommand.split(' ')[0]; @@ -923,10 +926,10 @@ RedisClient.prototype.auth = RedisClient.prototype.AUTH = function (pass, callba return; } this.auth_pass = pass; - debug("Saving auth as " + this.auth_pass); + debug('Saving auth as ' + this.auth_pass); // Only run the callback once. So do not safe it if already connected if (this.connected) { - this.send_command("auth", [this.auth_pass], callback); + this.send_command('auth', [this.auth_pass], callback); } else { this.auth_callback = callback; } @@ -935,18 +938,18 @@ RedisClient.prototype.auth = RedisClient.prototype.AUTH = function (pass, callba RedisClient.prototype.hmset = RedisClient.prototype.HMSET = function (key, args, callback) { var field, tmp_args; if (Array.isArray(key)) { - return this.send_command("hmset", key, args); + return this.send_command('hmset', key, args); } if (Array.isArray(args)) { - return this.send_command("hmset", [key].concat(args), callback); + return this.send_command('hmset', [key].concat(args), callback); } - if (typeof args === "object") { + if (typeof args === 'object') { // User does: client.hmset(key, {key1: val1, key2: val2}) // assuming key is a string, i.e. email address // if key is a number, i.e. timestamp, convert to string // TODO: This seems random and no other command get's the key converted => either all or none should behave like this - if (typeof key !== "string") { + if (typeof key !== 'string') { key = key.toString(); } tmp_args = [key]; @@ -954,9 +957,9 @@ RedisClient.prototype.hmset = RedisClient.prototype.HMSET = function (key, args, while (field = fields.shift()) { tmp_args.push(field, args[field]); } - return this.send_command("hmset", tmp_args, callback); + return this.send_command('hmset', tmp_args, callback); } - return this.send_command("hmset", utils.to_array(arguments)); + return this.send_command('hmset', utils.to_array(arguments)); }; Multi.prototype.hmset = Multi.prototype.HMSET = function (key, args, callback) { @@ -971,11 +974,11 @@ Multi.prototype.hmset = Multi.prototype.HMSET = function (key, args, callback) { args = args.concat([callback]); } tmp_args = ['hmset', key].concat(args); - } else if (typeof args === "object") { - if (typeof key !== "string") { + } else if (typeof args === 'object') { + if (typeof key !== 'string') { key = key.toString(); } - tmp_args = ["hmset", key]; + tmp_args = ['hmset', key]; var fields = Object.keys(args); while (field = fields.shift()) { tmp_args.push(field); @@ -986,7 +989,7 @@ Multi.prototype.hmset = Multi.prototype.HMSET = function (key, args, callback) { } } else { tmp_args = utils.to_array(arguments); - tmp_args.unshift("hmset"); + tmp_args.unshift('hmset'); } this.queue.push(tmp_args); return this; @@ -1010,12 +1013,12 @@ Multi.prototype.exec = Multi.prototype.EXEC = function (callback) { this.errors = []; this.callback = callback; this.wants_buffers = new Array(this.queue.length); - // drain queue, callback will catch "QUEUED" or error + // drain queue, callback will catch 'QUEUED' or error for (var index = 0; index < this.queue.length; index++) { var args = this.queue[index].slice(0); var command = args.shift(); var cb; - if (typeof args[args.length - 1] === "function") { + if (typeof args[args.length - 1] === 'function') { cb = args.pop(); } // Keep track of who wants buffer responses: @@ -1066,13 +1069,13 @@ Multi.prototype.execute_callback = function (err, replies) { if (this._client.options.detect_buffers && this.wants_buffers[i + 1] === false) { replies[i] = utils.reply_to_strings(replies[i]); } - if (args[0] === "hgetall") { + if (args[0] === 'hgetall') { // TODO - confusing and error-prone that hgetall is special cased in two places replies[i] = utils.reply_to_object(replies[i]); } } - if (typeof args[args.length - 1] === "function") { + if (typeof args[args.length - 1] === 'function') { if (replies[i] instanceof Error) { args[args.length - 1](replies[i]); } else { @@ -1087,11 +1090,7 @@ Multi.prototype.execute_callback = function (err, replies) { } }; -RedisClient.prototype.multi = RedisClient.prototype.MULTI = function (args) { - return new Multi(this, args); -}; - -var createClient_unix = function(path, options){ +var createClient_unix = function (path, options){ var cnxOptions = { path: path }; @@ -1106,9 +1105,9 @@ var createClient_unix = function(path, options){ var createClient_tcp = function (port_arg, host_arg, options) { var cnxOptions = { - 'port' : port_arg || default_port, - 'host' : host_arg || default_host, - 'family' : options && options.family === 'IPv6' ? 6 : 4 + port : port_arg || default_port, + host : host_arg || default_host, + family : options && options.family === 'IPv6' ? 6 : 4 }; var net_client = net.createConnection(cnxOptions); var redis_client = new RedisClient(net_client, options); @@ -1119,7 +1118,7 @@ var createClient_tcp = function (port_arg, host_arg, options) { return redis_client; }; -exports.createClient = function(port_arg, host_arg, options) { +var createClient = function (port_arg, host_arg, options) { if (typeof port_arg === 'object' || port_arg === undefined) { options = port_arg || options || {}; return createClient_tcp(+options.port, options.host, options); @@ -1143,5 +1142,7 @@ exports.createClient = function(port_arg, host_arg, options) { throw new Error('Unknown type of connection in createClient()'); }; +exports.createClient = createClient; +exports.RedisClient = RedisClient; exports.print = utils.print; exports.Multi = Multi; diff --git a/lib/parsers/hiredis.js b/lib/parsers/hiredis.js index 5be6bf2ac9e..e16304e8e6d 100644 --- a/lib/parsers/hiredis.js +++ b/lib/parsers/hiredis.js @@ -1,6 +1,6 @@ 'use strict'; -var hiredis = require("hiredis"); +var hiredis = require('hiredis'); function HiredisReplyParser(return_buffers) { this.name = exports.name; @@ -39,4 +39,4 @@ HiredisReplyParser.prototype.execute = function (data) { }; exports.Parser = HiredisReplyParser; -exports.name = "hiredis"; +exports.name = 'hiredis'; diff --git a/lib/parsers/javascript.js b/lib/parsers/javascript.js index 4beda4841fa..d8c19032aed 100644 --- a/lib/parsers/javascript.js +++ b/lib/parsers/javascript.js @@ -1,6 +1,6 @@ 'use strict'; -var util = require("util"); +var util = require('util'); function Packet(type, size) { this.type = type; @@ -13,11 +13,11 @@ function ReplyParser(return_buffers) { this._buffer = new Buffer(0); this._offset = 0; - this._encoding = "utf-8"; + this._encoding = 'utf-8'; } function IncompleteReadBuffer(message) { - this.name = "IncompleteReadBuffer"; + this.name = 'IncompleteReadBuffer'; this.message = message; } util.inherits(IncompleteReadBuffer, Error); @@ -31,7 +31,7 @@ ReplyParser.prototype._parseResult = function (type) { start = this._offset; if (end > this._buffer.length) { - throw new IncompleteReadBuffer("Wait for more data."); + throw new IncompleteReadBuffer('Wait for more data.'); } // include the delimiter @@ -49,7 +49,7 @@ ReplyParser.prototype._parseResult = function (type) { start = this._offset; if (end > this._buffer.length) { - throw new IncompleteReadBuffer("Wait for more data."); + throw new IncompleteReadBuffer('Wait for more data.'); } // include the delimiter @@ -73,7 +73,7 @@ ReplyParser.prototype._parseResult = function (type) { start = this._offset; if (end > this._buffer.length) { - throw new IncompleteReadBuffer("Wait for more data."); + throw new IncompleteReadBuffer('Wait for more data.'); } // set the offset to after the delimiter @@ -93,7 +93,7 @@ ReplyParser.prototype._parseResult = function (type) { if (packetHeader.size > this._bytesRemaining()) { this._offset = offset - 1; - throw new IncompleteReadBuffer("Wait for more data."); + throw new IncompleteReadBuffer('Wait for more data.'); } var reply = []; @@ -105,7 +105,7 @@ ReplyParser.prototype._parseResult = function (type) { ntype = this._buffer[this._offset++]; if (this._offset > this._buffer.length) { - throw new IncompleteReadBuffer("Wait for more data."); + throw new IncompleteReadBuffer('Wait for more data.'); } res = this._parseResult(ntype); reply.push(res); @@ -199,7 +199,7 @@ ReplyParser.prototype._packetEndOffset = function () { /* istanbul ignore if: activate the js parser out of memory test to test this */ if (offset >= this._buffer.length) { - throw new IncompleteReadBuffer("didn't see LF after NL reading multi bulk count (" + offset + " => " + this._buffer.length + ", " + this._offset + ")"); + throw new IncompleteReadBuffer('Did not see LF after NL reading multi bulk count (' + offset + ' => ' + this._buffer.length + ', ' + this._offset + ')'); } } @@ -212,4 +212,4 @@ ReplyParser.prototype._bytesRemaining = function () { }; exports.Parser = ReplyParser; -exports.name = "javascript"; +exports.name = 'javascript'; diff --git a/lib/queue.js b/lib/queue.js index ab4a0354b00..5c9c9917e87 100644 --- a/lib/queue.js +++ b/lib/queue.js @@ -52,7 +52,7 @@ Queue.prototype.getLength = function () { return this.head.length - this.offset + this.tail.length; }; -Object.defineProperty(Queue.prototype, "length", { +Object.defineProperty(Queue.prototype, 'length', { get: function () { return this.getLength(); } diff --git a/lib/utils.js b/lib/utils.js index c749380858b..6e5cbad3563 100644 --- a/lib/utils.js +++ b/lib/utils.js @@ -48,9 +48,9 @@ function toArray(args) { function print (err, reply) { if (err) { - console.log("Error: " + err); + console.log('Error: ' + err); } else { - console.log("Reply: " + reply); + console.log('Reply: ' + reply); } } From 331ea59ca773ce0700c73445a41189056b429e4c Mon Sep 17 00:00:00 2001 From: Ruben Bridgewater Date: Sat, 10 Oct 2015 01:51:28 +0200 Subject: [PATCH 0411/1748] Improve the parser --- lib/parsers/javascript.js | 26 ++++++++++++-------------- 1 file changed, 12 insertions(+), 14 deletions(-) diff --git a/lib/parsers/javascript.js b/lib/parsers/javascript.js index d8c19032aed..a0a594f6808 100644 --- a/lib/parsers/javascript.js +++ b/lib/parsers/javascript.js @@ -2,11 +2,6 @@ var util = require('util'); -function Packet(type, size) { - this.type = type; - this.size = +size; -} - function ReplyParser(return_buffers) { this.name = exports.name; this.return_buffers = return_buffers; @@ -23,7 +18,10 @@ function IncompleteReadBuffer(message) { util.inherits(IncompleteReadBuffer, Error); ReplyParser.prototype._parseResult = function (type) { - var start, end, offset, packetHeader; + var start = 0, + end = 0, + offset = 0, + packetHeader = 0; if (type === 43 || type === 45) { // + or - // up to the delimiter @@ -62,14 +60,14 @@ ReplyParser.prototype._parseResult = function (type) { // buffer in memory offset = this._offset - 1; - packetHeader = new Packet(type, this.parseHeader()); + packetHeader = this.parseHeader(); // packets with a size of -1 are considered null - if (packetHeader.size === -1) { + if (packetHeader === -1) { return null; } - end = this._offset + packetHeader.size; + end = this._offset + packetHeader; start = this._offset; if (end > this._buffer.length) { @@ -85,13 +83,13 @@ ReplyParser.prototype._parseResult = function (type) { return this._buffer.toString(this._encoding, start, end); } else { // * offset = this._offset; - packetHeader = new Packet(type, this.parseHeader()); + packetHeader = this.parseHeader(); - if (packetHeader.size < 0) { + if (packetHeader < 0) { return null; } - if (packetHeader.size > this._bytesRemaining()) { + if (packetHeader > this._bytesRemaining()) { this._offset = offset - 1; throw new IncompleteReadBuffer('Wait for more data.'); } @@ -101,7 +99,7 @@ ReplyParser.prototype._parseResult = function (type) { offset = this._offset - 1; - for (i = 0; i < packetHeader.size; i++) { + for (i = 0; i < packetHeader; i++) { ntype = this._buffer[this._offset++]; if (this._offset > this._buffer.length) { @@ -184,7 +182,7 @@ ReplyParser.prototype.append = function (newBuffer) { ReplyParser.prototype.parseHeader = function () { var end = this._packetEndOffset(), - value = this._buffer.toString('ascii', this._offset, end - 1); + value = this._buffer.toString('ascii', this._offset, end - 1) | 0; this._offset = end + 1; From e951bfb177e65e9e03cb8dd6d1c1171c9d42b7a1 Mon Sep 17 00:00:00 2001 From: Ruben Bridgewater Date: Sat, 10 Oct 2015 01:51:37 +0200 Subject: [PATCH 0412/1748] Fix parser regression --- lib/parsers/javascript.js | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/lib/parsers/javascript.js b/lib/parsers/javascript.js index a0a594f6808..1230401e4d8 100644 --- a/lib/parsers/javascript.js +++ b/lib/parsers/javascript.js @@ -81,7 +81,7 @@ ReplyParser.prototype._parseResult = function (type) { return this._buffer.slice(start, end); } return this._buffer.toString(this._encoding, start, end); - } else { // * + } else if (type === 42) { // * offset = this._offset; packetHeader = this.parseHeader(); From 0f43aa5294e711ea6e066b7377eba3a877e6c135 Mon Sep 17 00:00:00 2001 From: Ruben Bridgewater Date: Sat, 10 Oct 2015 01:59:04 +0200 Subject: [PATCH 0413/1748] Use msvs 2013 for appveyor to get the hiredis parser to work --- appveyor.yml | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/appveyor.yml b/appveyor.yml index 3966d52e72f..f7d70bb1894 100644 --- a/appveyor.yml +++ b/appveyor.yml @@ -17,8 +17,8 @@ install: - '@ECHO Redis Started' # Get the latest stable version of Node 0.STABLE.latest - ps: Install-Product node $env:nodejs_version - # Typical npm stuff. - - npm install + # Typical npm stuff. Use msvs 2013 for the hiredis parser + - npm install --msvs_version=2013 # Post-install test scripts. test_script: From 987e4f8a7ca425a4d392b7c04c5139010748249d Mon Sep 17 00:00:00 2001 From: Ruben Bridgewater Date: Sat, 10 Oct 2015 02:17:31 +0200 Subject: [PATCH 0414/1748] Add hiredis to the tests if it's present --- test/helper.js | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) diff --git a/test/helper.js b/test/helper.js index d007676ea62..8402b9cfc02 100644 --- a/test/helper.js +++ b/test/helper.js @@ -113,8 +113,11 @@ module.exports = { } var parsers = ['javascript']; var protocols = ['IPv4']; - if (process.platform !== 'win32') { + try { + require('hiredis'); parsers.push('hiredis'); + } catch (e) {} + if (process.platform !== 'win32') { protocols.push('IPv6', '/tmp/redis.sock'); } var options = [{ From 6d8daef59954235acefdcdcb96ef62162a58d07d Mon Sep 17 00:00:00 2001 From: Ruben Bridgewater Date: Sat, 10 Oct 2015 03:40:26 +0200 Subject: [PATCH 0415/1748] Add changelog entry --- changelog.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/changelog.md b/changelog.md index 94b79b273d6..40553fae6c1 100644 --- a/changelog.md +++ b/changelog.md @@ -9,7 +9,7 @@ Features Bugfixes -- +- Fix a javascript parser regression introduced in 2.0 that could result in timeouts on high load. (@BridgeAR) ## v2.1.0 - Oct 02, 2015 From 972d1cdeb4918fd01bbeecbc72fadaefe5af4c1f Mon Sep 17 00:00:00 2001 From: Ruben Bridgewater Date: Sat, 10 Oct 2015 05:39:55 +0200 Subject: [PATCH 0416/1748] Add rename_commands option --- README.md | 3 +- changelog.md | 1 + index.js | 19 ++++++-- test/conf/rename.conf | 7 +++ test/rename.spec.js | 110 ++++++++++++++++++++++++++++++++++++++++++ 5 files changed, 135 insertions(+), 5 deletions(-) create mode 100644 test/conf/rename.conf create mode 100644 test/rename.spec.js diff --git a/README.md b/README.md index 4692728f31a..c862017d0a5 100644 --- a/README.md +++ b/README.md @@ -5,7 +5,7 @@ redis - a node.js redis client [![Coverage Status](https://coveralls.io/repos/NodeRedis/node_redis/badge.svg?branch=)](https://coveralls.io/r/NodeRedis/node_redis?branch=) [![Windows Tests](https://img.shields.io/appveyor/ci/bcoe/node-redis/master.svg?label=Windows%20Tests)](https://ci.appveyor.com/project/bcoe/node-redis) -This is a complete Redis client for node.js. It supports all Redis commands and focuses on performance. +This is a complete and feature rich Redis client for node.js. It supports all Redis commands and focuses on performance. Install with: @@ -204,6 +204,7 @@ limits total amount of connection tries. Setting this to 1 will prevent any reco * `auth_pass`: *null*; If set, client will run redis auth command on connect. * `family`: *IPv4*; You can force using IPv6 if you set the family to 'IPv6'. See Node.js [net](https://nodejs.org/api/net.html) or [dns](https://nodejs.org/api/dns.html) modules how to use the family type. * `disable_resubscribing`: *false*; If set to `true`, a client won't resubscribe after disconnecting +* `rename_commands`: *null*; pass a object with renamed commands to use those instead of the original functions. See the [redis security topics](http://redis.io/topics/security) for more info. ```js var redis = require("redis"), diff --git a/changelog.md b/changelog.md index 40553fae6c1..deef540ea02 100644 --- a/changelog.md +++ b/changelog.md @@ -6,6 +6,7 @@ Changelog Features - Added disable_resubscribing option to prevent a client from resubscribing after reconnecting (@BridgeAR) +- Added rename_commands options to handle renamed commands from the redis config (@digmxl & @BridgeAR) Bugfixes diff --git a/index.js b/index.js index f28f96a39f8..c1477dc06a1 100644 --- a/index.js +++ b/index.js @@ -42,11 +42,18 @@ function RedisClient(stream, options) { this.connected = false; this.ready = false; this.connections = 0; - if (this.options.socket_nodelay === undefined) { - this.options.socket_nodelay = true; + if (options.socket_nodelay === undefined) { + options.socket_nodelay = true; } - if (this.options.socket_keepalive === undefined) { - this.options.socket_keepalive = true; + if (options.socket_keepalive === undefined) { + options.socket_keepalive = true; + } + if (options.rename_commands) { + for (var command in options.rename_commands) { + if (options.rename_commands.hasOwnProperty(command)) { + options.rename_commands[command.toLowerCase()] = options.rename_commands[command]; + } + } } this.should_buffer = false; this.command_queue_high_water = options.command_queue_high_water || 1000; @@ -720,6 +727,10 @@ RedisClient.prototype.send_command = function (command, args, callback) { elem_count = args.length + 1; + if (typeof this.options.rename_commands !== 'undefined' && this.options.rename_commands[command]) { + command = this.options.rename_commands[command]; + } + // Always use 'Multi bulk commands', but if passed any Buffer args, then do multiple writes, one for each arg. // This means that using Buffers in commands is going to be slower, so use Strings if you don't already have a Buffer. diff --git a/test/conf/rename.conf b/test/conf/rename.conf new file mode 100644 index 00000000000..4da225053fd --- /dev/null +++ b/test/conf/rename.conf @@ -0,0 +1,7 @@ +port 6379 +bind ::1 127.0.0.1 +unixsocket /tmp/redis.sock +unixsocketperm 755 +rename-command SET 807081f5afa96845a02816a28b7258c3 +rename-command GET f397808a43ceca3963e22b4e13deb672 +rename-command GETRANGE 9e3102b15cf231c4e9e940f284744fe0 diff --git a/test/rename.spec.js b/test/rename.spec.js new file mode 100644 index 00000000000..52ee7a4fe45 --- /dev/null +++ b/test/rename.spec.js @@ -0,0 +1,110 @@ +'use strict'; + +var assert = require("assert"); +var config = require("./lib/config"); +var helper = require('./helper'); +var redis = config.redis; + +describe("rename commands", function () { + before(function (done) { + helper.stopRedis(function () { + helper.startRedis('./conf/rename.conf', done); + }); + }); + + helper.allTests(function(parser, ip, args) { + + describe("using " + parser + " and " + ip, function () { + var client = null; + + afterEach(function () { + client.end(); + }); + + it("allows to use renamed functions", function (done) { + if (helper.redisProcess().spawnFailed()) this.skip(); + + client = redis.createClient({ + rename_commands: { + set: '807081f5afa96845a02816a28b7258c3', + GETRANGE: '9e3102b15cf231c4e9e940f284744fe0' + } + }); + + client.set('key', 'value', function(err, reply) { + assert.strictEqual(reply, 'OK'); + }); + + client.get('key', function(err, reply) { + assert.strictEqual(err.message, "ERR unknown command 'get'"); + assert.strictEqual(err.command, 'GET'); + assert.strictEqual(reply, undefined); + }); + + client.getrange('key', 1, -1, function(err, reply) { + assert.strictEqual(reply, 'alue'); + assert.strictEqual(err, null); + done(); + }); + }); + + it("should also work with multi", function (done) { + if (helper.redisProcess().spawnFailed()) this.skip(); + + client = redis.createClient({ + rename_commands: { + SET: '807081f5afa96845a02816a28b7258c3', + getrange: '9e3102b15cf231c4e9e940f284744fe0' + } + }); + + client.multi([['set', 'key', 'value']]).exec(function (err, res) { + assert.strictEqual(res[0], 'OK'); + }); + + var multi = client.multi(); + multi.getrange('key', 1, -1); + multi.exec(function (err, res) { + assert(!err); + assert.strictEqual(res.length, 1); + assert.strictEqual(res[0], 'alue'); + done(); + }); + }); + + it("should also work with multi and abort transaction", function (done) { + if (helper.redisProcess().spawnFailed()) this.skip(); + + client = redis.createClient({ + rename_commands: { + SET: '807081f5afa96845a02816a28b7258c3', + getrange: '9e3102b15cf231c4e9e940f284744fe0' + } + }); + + var multi = client.multi(); + multi.get('key'); + multi.getrange('key', 1, -1, function(err, reply) { + assert.strictEqual(reply, 'alue'); + assert.strictEqual(err, null); + }); + multi.exec(function (err, res) { + assert(err); + assert.strictEqual(err.message, 'EXECABORT Transaction discarded because of previous errors.'); + assert.strictEqual(err.errors[0].message, "ERR unknown command 'get'"); + assert.strictEqual(err.errors[0].command, 'GET'); + assert.strictEqual(err.code, 'EXECABORT'); + assert.strictEqual(err.errors[0].code, 'ERR'); + done(); + }); + }); + + }); + }); + + after(function (done) { + helper.stopRedis(function () { + helper.startRedis('./conf/redis.conf', done); + }); + }); +}); From 90033bdd0047137f5f67634c8d7d9555361b761a Mon Sep 17 00:00:00 2001 From: Ruben Bridgewater Date: Sat, 10 Oct 2015 19:37:42 +0200 Subject: [PATCH 0417/1748] Fix some tests --- test/lib/unref.js | 4 ++-- test/node_redis.spec.js | 6 ++++-- test/pubsub.spec.js | 2 +- 3 files changed, 7 insertions(+), 5 deletions(-) diff --git a/test/lib/unref.js b/test/lib/unref.js index 45877a493ee..2fb24bd7485 100644 --- a/test/lib/unref.js +++ b/test/lib/unref.js @@ -3,15 +3,15 @@ // as soon as there are no outstanding commands. 'use strict'; -var redis = require("../../"); +var redis = require("../../index"); var HOST = process.argv[2] || '127.0.0.1'; var PORT = process.argv[3]; var args = PORT ? [PORT, HOST] : [HOST]; var c = redis.createClient.apply(redis, args); -c.unref(); c.info(function (err, reply) { if (err) process.exit(-1); if (!reply.length) process.exit(-1); process.stdout.write(reply.length.toString()); }); +c.unref(); diff --git a/test/node_redis.spec.js b/test/node_redis.spec.js index cd6659686a3..64817c2c981 100644 --- a/test/node_redis.spec.js +++ b/test/node_redis.spec.js @@ -416,12 +416,14 @@ describe("The node_redis client", function () { describe('unref', function () { it('exits subprocess as soon as final command is processed', function (done) { + this.timeout(12000); var args = config.HOST[ip] ? [config.HOST[ip], config.PORT] : [ip]; var external = fork("./test/lib/unref.js", args); + var id = setTimeout(function () { external.kill(); return done(Error('unref subprocess timed out')); - }, 5000); + }, 8000); external.on("close", function (code) { clearTimeout(id); @@ -555,7 +557,7 @@ describe("The node_redis client", function () { } else { client.end(); var lasted = new Date().getTime() - time; - assert.ok(lasted < 50); + assert.ok(lasted < 100); return done(); } }); diff --git a/test/pubsub.spec.js b/test/pubsub.spec.js index b39b532cbde..9ffd831a110 100644 --- a/test/pubsub.spec.js +++ b/test/pubsub.spec.js @@ -147,7 +147,7 @@ describe("publish/subscribe", function () { }); }); - it('handles SUB_UNSUB_MSG_SUB', function (done) { + it('handles SUB_UNSUB_MSG_SUB 2', function (done) { helper.serverVersionAtLeast.bind(this)(sub, [2, 6, 11]); sub.psubscribe('abc*'); From e0b9f0de7986090c6d54c61837f53f0d96dab076 Mon Sep 17 00:00:00 2001 From: Ruben Bridgewater Date: Sun, 11 Oct 2015 03:31:09 +0200 Subject: [PATCH 0418/1748] Fix race condition in tests --- test/commands/blpop.spec.js | 2 +- test/commands/dbsize.spec.js | 4 ++-- test/commands/del.spec.js | 2 +- test/commands/eval.spec.js | 2 +- test/commands/exits.spec.js | 2 +- test/commands/expire.spec.js | 2 +- test/commands/flushdb.spec.js | 4 ++-- test/commands/geoadd.spec.js | 2 +- test/commands/get.spec.js | 4 ++-- test/commands/getset.spec.js | 4 ++-- test/commands/hgetall.spec.js | 4 ++-- test/commands/hincrby.spec.js | 2 +- test/commands/hlen.spec.js | 2 +- test/commands/hmget.spec.js | 2 +- test/commands/hmset.spec.js | 2 +- test/commands/hset.spec.js | 2 +- test/commands/incr.spec.js | 4 ++-- test/commands/keys.spec.js | 2 +- test/commands/mget.spec.js | 2 +- test/commands/mset.spec.js | 4 ++-- test/commands/msetnx.spec.js | 2 +- test/commands/multi.spec.js | 2 +- test/commands/randomkey.test.js | 2 +- test/commands/rename.spec.js | 2 +- test/commands/renamenx.spec.js | 2 +- test/commands/rpush.spec.js | 2 +- test/commands/sadd.spec.js | 2 +- test/commands/scard.spec.js | 2 +- test/commands/script.spec.js | 2 +- test/commands/sdiff.spec.js | 2 +- test/commands/sdiffstore.spec.js | 2 +- test/commands/select.spec.js | 2 +- test/commands/set.spec.js | 4 ++-- test/commands/setex.spec.js | 2 +- test/commands/setnx.spec.js | 2 +- test/commands/sinter.spec.js | 2 +- test/commands/sinterstore.spec.js | 2 +- test/commands/sismember.spec.js | 2 +- test/commands/slowlog.spec.js | 2 +- test/commands/smembers.spec.js | 2 +- test/commands/smove.spec.js | 2 +- test/commands/spop.spec.js | 2 +- test/commands/srem.spec.js | 2 +- test/commands/sunion.spec.js | 2 +- test/commands/sunionstore.spec.js | 2 +- test/commands/sync.spec.js | 2 +- test/commands/ttl.spec.js | 2 +- test/commands/type.spec.js | 2 +- test/commands/watch.spec.js | 2 +- test/commands/zadd.spec.js | 2 +- test/commands/zscore.spec.js | 2 +- test/connection.spec.js | 2 +- 52 files changed, 60 insertions(+), 60 deletions(-) diff --git a/test/commands/blpop.spec.js b/test/commands/blpop.spec.js index ea9dfdf21cd..e8042d6053e 100644 --- a/test/commands/blpop.spec.js +++ b/test/commands/blpop.spec.js @@ -15,7 +15,7 @@ describe("The 'blpop' method", function () { beforeEach(function (done) { client = redis.createClient.apply(redis.createClient, args); - client.once("connect", function () { + client.once("ready", function () { client.flushdb(done); }); }); diff --git a/test/commands/dbsize.spec.js b/test/commands/dbsize.spec.js index 7bc03219b97..6e362087a5b 100644 --- a/test/commands/dbsize.spec.js +++ b/test/commands/dbsize.spec.js @@ -23,7 +23,7 @@ describe("The 'dbsize' method", function () { beforeEach(function (done) { client = redis.createClient.apply(redis.createClient, args); - client.once("connect", function () { + client.once("ready", function () { client.quit(); }); client.on('end', function () { @@ -44,7 +44,7 @@ describe("The 'dbsize' method", function () { beforeEach(function (done) { client = redis.createClient.apply(redis.createClient, args); - client.once("connect", function () { + client.once("ready", function () { client.flushdb(function (err, res) { helper.isString("OK")(err, res); done(); diff --git a/test/commands/del.spec.js b/test/commands/del.spec.js index 0adb6327d8d..5502e91b6cf 100644 --- a/test/commands/del.spec.js +++ b/test/commands/del.spec.js @@ -13,7 +13,7 @@ describe("The 'del' method", function () { beforeEach(function (done) { client = redis.createClient.apply(redis.createClient, args); - client.once("connect", function () { + client.once("ready", function () { client.flushdb(done); }); }); diff --git a/test/commands/eval.spec.js b/test/commands/eval.spec.js index cfbaefa82be..bdb0d48061b 100644 --- a/test/commands/eval.spec.js +++ b/test/commands/eval.spec.js @@ -16,7 +16,7 @@ describe("The 'eval' method", function () { beforeEach(function (done) { client = redis.createClient.apply(redis.createClient, args); - client.once("connect", function () { + client.once("ready", function () { client.flushdb(done); }); }); diff --git a/test/commands/exits.spec.js b/test/commands/exits.spec.js index 061e3671335..030e68c7f5c 100644 --- a/test/commands/exits.spec.js +++ b/test/commands/exits.spec.js @@ -13,7 +13,7 @@ describe("The 'exits' method", function () { beforeEach(function (done) { client = redis.createClient.apply(redis.createClient, args); - client.once("connect", function () { + client.once("ready", function () { client.flushdb(done); }); }); diff --git a/test/commands/expire.spec.js b/test/commands/expire.spec.js index 1e3d9073e92..42e01b2e409 100644 --- a/test/commands/expire.spec.js +++ b/test/commands/expire.spec.js @@ -13,7 +13,7 @@ describe("The 'expire' method", function () { beforeEach(function (done) { client = redis.createClient.apply(redis.createClient, args); - client.once("connect", function () { + client.once("ready", function () { client.flushdb(done); }); }); diff --git a/test/commands/flushdb.spec.js b/test/commands/flushdb.spec.js index 12f10de8e8f..f80e5475bee 100644 --- a/test/commands/flushdb.spec.js +++ b/test/commands/flushdb.spec.js @@ -23,7 +23,7 @@ describe("The 'flushdb' method", function () { beforeEach(function (done) { client = redis.createClient.apply(redis.createClient, args); - client.once("connect", function () { + client.once("ready", function () { client.quit(); }); client.on('end', function () { @@ -44,7 +44,7 @@ describe("The 'flushdb' method", function () { beforeEach(function (done) { client = redis.createClient.apply(redis.createClient, args); - client.once("connect", function () { + client.once("ready", function () { done(); }); }); diff --git a/test/commands/geoadd.spec.js b/test/commands/geoadd.spec.js index c98992471bc..690df55de68 100644 --- a/test/commands/geoadd.spec.js +++ b/test/commands/geoadd.spec.js @@ -13,7 +13,7 @@ describe("The 'geoadd' method", function () { beforeEach(function (done) { client = redis.createClient.apply(redis.createClient, args); - client.once("connect", function () { + client.once("ready", function () { client.flushdb(done); }); }); diff --git a/test/commands/get.spec.js b/test/commands/get.spec.js index 973629945da..8bb985964d1 100644 --- a/test/commands/get.spec.js +++ b/test/commands/get.spec.js @@ -23,7 +23,7 @@ describe("The 'get' method", function () { beforeEach(function (done) { client = redis.createClient.apply(redis.createClient, args); - client.once("connect", function () { + client.once("ready", function () { client.quit(); }); client.on('end', function () { @@ -44,7 +44,7 @@ describe("The 'get' method", function () { beforeEach(function (done) { client = redis.createClient.apply(redis.createClient, args); - client.once("connect", function () { + client.once("ready", function () { done(); }); }); diff --git a/test/commands/getset.spec.js b/test/commands/getset.spec.js index e35ec7e6167..289257573a0 100644 --- a/test/commands/getset.spec.js +++ b/test/commands/getset.spec.js @@ -24,7 +24,7 @@ describe("The 'getset' method", function () { beforeEach(function (done) { client = redis.createClient.apply(redis.createClient, args); - client.once("connect", function () { + client.once("ready", function () { client.quit(); }); client.on('end', function () { @@ -46,7 +46,7 @@ describe("The 'getset' method", function () { beforeEach(function (done) { client = redis.createClient.apply(redis.createClient, args); - client.once("connect", function () { + client.once("ready", function () { done(); }); }); diff --git a/test/commands/hgetall.spec.js b/test/commands/hgetall.spec.js index faac08d08ef..2a734052b5f 100644 --- a/test/commands/hgetall.spec.js +++ b/test/commands/hgetall.spec.js @@ -16,7 +16,7 @@ describe("The 'hgetall' method", function () { beforeEach(function (done) { client = redis.createClient.apply(redis.createClient, args); - client.once("connect", function () { + client.once("ready", function () { client.flushdb(done); }); }); @@ -57,7 +57,7 @@ describe("The 'hgetall' method", function () { beforeEach(function (done) { client = redis.createClient.apply(redis.createClient, args); - client.once("connect", function () { + client.once("ready", function () { client.flushdb(done); }); }); diff --git a/test/commands/hincrby.spec.js b/test/commands/hincrby.spec.js index 43a73826e35..cfa1f132972 100644 --- a/test/commands/hincrby.spec.js +++ b/test/commands/hincrby.spec.js @@ -14,7 +14,7 @@ describe("The 'hincrby' method", function () { beforeEach(function (done) { client = redis.createClient.apply(redis.createClient, args); - client.once("connect", function () { + client.once("ready", function () { client.flushdb(done); }); }); diff --git a/test/commands/hlen.spec.js b/test/commands/hlen.spec.js index 3d6d675b965..d5d6b149928 100644 --- a/test/commands/hlen.spec.js +++ b/test/commands/hlen.spec.js @@ -13,7 +13,7 @@ describe("The 'hlen' method", function () { beforeEach(function (done) { client = redis.createClient.apply(redis.createClient, args); - client.once("connect", function () { + client.once("ready", function () { client.flushdb(done); }); }); diff --git a/test/commands/hmget.spec.js b/test/commands/hmget.spec.js index 5d4e1df3a7e..a6f566ec693 100644 --- a/test/commands/hmget.spec.js +++ b/test/commands/hmget.spec.js @@ -16,7 +16,7 @@ describe("The 'hmget' method", function () { beforeEach(function (done) { client = redis.createClient.apply(redis.createClient, args); client.once("error", done); - client.once("connect", function () { + client.once("ready", function () { client.flushdb(); client.HMSET(hash, {"0123456789": "abcdefghij", "some manner of key": "a type of value"}, helper.isString('OK', done)); }); diff --git a/test/commands/hmset.spec.js b/test/commands/hmset.spec.js index 6060677e0f6..1f8f15096db 100644 --- a/test/commands/hmset.spec.js +++ b/test/commands/hmset.spec.js @@ -15,7 +15,7 @@ describe("The 'hmset' method", function () { beforeEach(function (done) { client = redis.createClient.apply(redis.createClient, args); - client.once("connect", function () { + client.once("ready", function () { client.flushdb(done); }); }); diff --git a/test/commands/hset.spec.js b/test/commands/hset.spec.js index 66ff79086ce..8ce8996d860 100644 --- a/test/commands/hset.spec.js +++ b/test/commands/hset.spec.js @@ -15,7 +15,7 @@ describe("The 'hset' method", function () { beforeEach(function (done) { client = redis.createClient.apply(redis.createClient, args); - client.once("connect", function () { + client.once("ready", function () { client.flushdb(done); }); }); diff --git a/test/commands/incr.spec.js b/test/commands/incr.spec.js index cdf5fecac31..3803585ff48 100644 --- a/test/commands/incr.spec.js +++ b/test/commands/incr.spec.js @@ -17,7 +17,7 @@ describe("The 'incr' method", function () { beforeEach(function (done) { client = redis.createClient.apply(redis.createClient, args); - client.once("connect", function () { + client.once("ready", function () { client.set(key, "9007199254740992", function (err, res) { helper.isNotError()(err, res); client.quit(); @@ -55,7 +55,7 @@ describe("The 'incr' method", function () { */ client = redis.createClient.apply(redis.createClient, args); client.once("error", done); - client.once("connect", function () { + client.once("ready", function () { client.set(key, "9007199254740992", function (err, res) { helper.isNotError()(err, res); done(); diff --git a/test/commands/keys.spec.js b/test/commands/keys.spec.js index 58159072f6c..6437aeb0a8a 100644 --- a/test/commands/keys.spec.js +++ b/test/commands/keys.spec.js @@ -18,7 +18,7 @@ describe("The 'keys' method", function () { // This is going to test if the high water is also respected args.command_queue_high_water = 100; client = redis.createClient.apply(redis.createClient, args); - client.once("connect", function () { + client.once("ready", function () { client.flushdb(done); }); }); diff --git a/test/commands/mget.spec.js b/test/commands/mget.spec.js index 54f48a7cffa..447fac04900 100644 --- a/test/commands/mget.spec.js +++ b/test/commands/mget.spec.js @@ -15,7 +15,7 @@ describe("The 'mget' method", function () { beforeEach(function (done) { client = redis.createClient.apply(redis.createClient, args); client.once("error", done); - client.once("connect", function () { + client.once("ready", function () { client.flushdb(); client.mset(["mget keys 1", "mget val 1", "mget keys 2", "mget val 2", "mget keys 3", "mget val 3"], done); }); diff --git a/test/commands/mset.spec.js b/test/commands/mset.spec.js index be2e3a4baf0..366f52c8899 100644 --- a/test/commands/mset.spec.js +++ b/test/commands/mset.spec.js @@ -25,7 +25,7 @@ describe("The 'mset' method", function () { beforeEach(function (done) { client = redis.createClient.apply(redis.createClient, args); - client.once("connect", function () { + client.once("ready", function () { client.quit(); }); client.on('end', function () { @@ -46,7 +46,7 @@ describe("The 'mset' method", function () { beforeEach(function (done) { client = redis.createClient.apply(redis.createClient, args); - client.once("connect", function () { + client.once("ready", function () { done(); }); }); diff --git a/test/commands/msetnx.spec.js b/test/commands/msetnx.spec.js index 7af7fbc27a6..e89780ac756 100644 --- a/test/commands/msetnx.spec.js +++ b/test/commands/msetnx.spec.js @@ -13,7 +13,7 @@ describe("The 'msetnx' method", function () { beforeEach(function (done) { client = redis.createClient.apply(redis.createClient, args); - client.once("connect", function () { + client.once("ready", function () { client.flushdb(done); }); }); diff --git a/test/commands/multi.spec.js b/test/commands/multi.spec.js index 5ce3fa82b0e..fa099a28569 100644 --- a/test/commands/multi.spec.js +++ b/test/commands/multi.spec.js @@ -23,7 +23,7 @@ describe("The 'multi' method", function () { beforeEach(function (done) { client = redis.createClient.apply(redis.createClient, args); - client.once("connect", function () { + client.once("ready", function () { client.quit(); }); client.on('end', function () { diff --git a/test/commands/randomkey.test.js b/test/commands/randomkey.test.js index 85e61ef5976..870726daef5 100644 --- a/test/commands/randomkey.test.js +++ b/test/commands/randomkey.test.js @@ -14,7 +14,7 @@ describe("The 'randomkey' method", function () { beforeEach(function (done) { client = redis.createClient.apply(redis.createClient, args); - client.once("connect", function () { + client.once("ready", function () { client.flushdb(done); }); }); diff --git a/test/commands/rename.spec.js b/test/commands/rename.spec.js index 02434cc9234..65dad08e7f3 100644 --- a/test/commands/rename.spec.js +++ b/test/commands/rename.spec.js @@ -13,7 +13,7 @@ describe("The 'rename' method", function () { beforeEach(function (done) { client = redis.createClient.apply(redis.createClient, args); - client.once("connect", function () { + client.once("ready", function () { client.flushdb(done); }); }); diff --git a/test/commands/renamenx.spec.js b/test/commands/renamenx.spec.js index e2fbdcfd034..e5020155ba3 100644 --- a/test/commands/renamenx.spec.js +++ b/test/commands/renamenx.spec.js @@ -13,7 +13,7 @@ describe("The 'renamenx' method", function () { beforeEach(function (done) { client = redis.createClient.apply(redis.createClient, args); - client.once("connect", function () { + client.once("ready", function () { client.flushdb(done); }); }); diff --git a/test/commands/rpush.spec.js b/test/commands/rpush.spec.js index ba3cb56415b..2a0d72fd222 100644 --- a/test/commands/rpush.spec.js +++ b/test/commands/rpush.spec.js @@ -14,7 +14,7 @@ describe("The 'rpush' command", function () { beforeEach(function (done) { client = redis.createClient.apply(redis.createClient, args); - client.once("connect", function () { + client.once("ready", function () { client.flushdb(done); }); }); diff --git a/test/commands/sadd.spec.js b/test/commands/sadd.spec.js index dcc5e1cd420..9aa246487dc 100644 --- a/test/commands/sadd.spec.js +++ b/test/commands/sadd.spec.js @@ -14,7 +14,7 @@ describe("The 'sadd' method", function () { beforeEach(function (done) { client = redis.createClient.apply(redis.createClient, args); - client.once("connect", function () { + client.once("ready", function () { client.flushdb(done); }); }); diff --git a/test/commands/scard.spec.js b/test/commands/scard.spec.js index 8c366e73835..52467823093 100644 --- a/test/commands/scard.spec.js +++ b/test/commands/scard.spec.js @@ -13,7 +13,7 @@ describe("The 'scard' method", function () { beforeEach(function (done) { client = redis.createClient.apply(redis.createClient, args); - client.once("connect", function () { + client.once("ready", function () { client.flushdb(done); }); }); diff --git a/test/commands/script.spec.js b/test/commands/script.spec.js index 19d77084b89..c3e13407028 100644 --- a/test/commands/script.spec.js +++ b/test/commands/script.spec.js @@ -17,7 +17,7 @@ describe("The 'script' method", function () { beforeEach(function (done) { client = redis.createClient.apply(redis.createClient, args); - client.once("connect", function () { + client.once("ready", function () { client.flushdb(done); }); }); diff --git a/test/commands/sdiff.spec.js b/test/commands/sdiff.spec.js index 43c6d1434ee..435ddf63e95 100644 --- a/test/commands/sdiff.spec.js +++ b/test/commands/sdiff.spec.js @@ -14,7 +14,7 @@ describe("The 'sdiff' method", function () { beforeEach(function (done) { client = redis.createClient.apply(redis.createClient, args); - client.once("connect", function () { + client.once("ready", function () { client.flushdb(done); }); }); diff --git a/test/commands/sdiffstore.spec.js b/test/commands/sdiffstore.spec.js index 52abb487e4d..00bc2c8e12c 100644 --- a/test/commands/sdiffstore.spec.js +++ b/test/commands/sdiffstore.spec.js @@ -14,7 +14,7 @@ describe("The 'sdiffstore' method", function () { beforeEach(function (done) { client = redis.createClient.apply(redis.createClient, args); - client.once("connect", function () { + client.once("ready", function () { client.flushdb(done); }); }); diff --git a/test/commands/select.spec.js b/test/commands/select.spec.js index 832d5ffac1a..dc121b781ac 100644 --- a/test/commands/select.spec.js +++ b/test/commands/select.spec.js @@ -15,7 +15,7 @@ describe("The 'select' method", function () { beforeEach(function (done) { client = redis.createClient.apply(redis.createClient, args); - client.once("connect", function () { + client.once("ready", function () { client.quit(); }); client.on('end', function () { diff --git a/test/commands/set.spec.js b/test/commands/set.spec.js index 6354f97f619..e76b0c88db1 100644 --- a/test/commands/set.spec.js +++ b/test/commands/set.spec.js @@ -23,7 +23,7 @@ describe("The 'set' method", function () { beforeEach(function (done) { client = redis.createClient.apply(redis.createClient, args); - client.once("connect", function () { + client.once("ready", function () { client.quit(); }); client.on('end', function () { @@ -44,7 +44,7 @@ describe("The 'set' method", function () { beforeEach(function (done) { client = redis.createClient.apply(redis.createClient, args); - client.once("connect", function () { + client.once("ready", function () { done(); }); }); diff --git a/test/commands/setex.spec.js b/test/commands/setex.spec.js index 5ad1fd1b41d..e8de49bd574 100644 --- a/test/commands/setex.spec.js +++ b/test/commands/setex.spec.js @@ -14,7 +14,7 @@ describe("The 'setex' method", function () { beforeEach(function (done) { client = redis.createClient.apply(redis.createClient, args); - client.once("connect", function () { + client.once("ready", function () { client.flushdb(done); }); }); diff --git a/test/commands/setnx.spec.js b/test/commands/setnx.spec.js index 5d9f358dc7f..229eb2b58bf 100644 --- a/test/commands/setnx.spec.js +++ b/test/commands/setnx.spec.js @@ -13,7 +13,7 @@ describe("The 'setnx' method", function () { beforeEach(function (done) { client = redis.createClient.apply(redis.createClient, args); - client.once("connect", function () { + client.once("ready", function () { client.flushdb(done); }); }); diff --git a/test/commands/sinter.spec.js b/test/commands/sinter.spec.js index b7df78c9951..a655b860689 100644 --- a/test/commands/sinter.spec.js +++ b/test/commands/sinter.spec.js @@ -14,7 +14,7 @@ describe("The 'sinter' method", function () { beforeEach(function (done) { client = redis.createClient.apply(redis.createClient, args); - client.once("connect", function () { + client.once("ready", function () { client.flushdb(done); }); }); diff --git a/test/commands/sinterstore.spec.js b/test/commands/sinterstore.spec.js index f789c42dfb8..38b7dcfb5bb 100644 --- a/test/commands/sinterstore.spec.js +++ b/test/commands/sinterstore.spec.js @@ -14,7 +14,7 @@ describe("The 'sinterstore' method", function () { beforeEach(function (done) { client = redis.createClient.apply(redis.createClient, args); - client.once("connect", function () { + client.once("ready", function () { client.flushdb(done); }); }); diff --git a/test/commands/sismember.spec.js b/test/commands/sismember.spec.js index a7ee9ad0e00..eefd37e4089 100644 --- a/test/commands/sismember.spec.js +++ b/test/commands/sismember.spec.js @@ -13,7 +13,7 @@ describe("The 'sismember' method", function () { beforeEach(function (done) { client = redis.createClient.apply(redis.createClient, args); - client.once("connect", function () { + client.once("ready", function () { client.flushdb(done); }); }); diff --git a/test/commands/slowlog.spec.js b/test/commands/slowlog.spec.js index eee3fa1d870..b2fbebdfe9c 100644 --- a/test/commands/slowlog.spec.js +++ b/test/commands/slowlog.spec.js @@ -14,7 +14,7 @@ describe("The 'slowlog' method", function () { beforeEach(function (done) { client = redis.createClient.apply(redis.createClient, args); - client.once("connect", function () { + client.once("ready", function () { client.flushdb(done); }); }); diff --git a/test/commands/smembers.spec.js b/test/commands/smembers.spec.js index 32078ef7bdc..e92a658d501 100644 --- a/test/commands/smembers.spec.js +++ b/test/commands/smembers.spec.js @@ -14,7 +14,7 @@ describe("The 'smembers' method", function () { beforeEach(function (done) { client = redis.createClient.apply(redis.createClient, args); - client.once("connect", function () { + client.once("ready", function () { client.flushdb(done); }); }); diff --git a/test/commands/smove.spec.js b/test/commands/smove.spec.js index e16e6191190..123ff163a7d 100644 --- a/test/commands/smove.spec.js +++ b/test/commands/smove.spec.js @@ -13,7 +13,7 @@ describe("The 'smove' method", function () { beforeEach(function (done) { client = redis.createClient.apply(redis.createClient, args); - client.once("connect", function () { + client.once("ready", function () { client.flushdb(done); }); }); diff --git a/test/commands/spop.spec.js b/test/commands/spop.spec.js index 2df75dd6535..f8d4d46ede7 100644 --- a/test/commands/spop.spec.js +++ b/test/commands/spop.spec.js @@ -14,7 +14,7 @@ describe("The 'spop' method", function () { beforeEach(function (done) { client = redis.createClient.apply(redis.createClient, args); - client.once("connect", function () { + client.once("ready", function () { client.flushdb(done); }); }); diff --git a/test/commands/srem.spec.js b/test/commands/srem.spec.js index 1ce496b9618..e4e552ec5dd 100644 --- a/test/commands/srem.spec.js +++ b/test/commands/srem.spec.js @@ -14,7 +14,7 @@ describe("The 'srem' method", function () { beforeEach(function (done) { client = redis.createClient.apply(redis.createClient, args); - client.once("connect", function () { + client.once("ready", function () { client.flushdb(done); }); }); diff --git a/test/commands/sunion.spec.js b/test/commands/sunion.spec.js index 6fb1bb03322..638cb116a6a 100644 --- a/test/commands/sunion.spec.js +++ b/test/commands/sunion.spec.js @@ -14,7 +14,7 @@ describe("The 'sunion' method", function () { beforeEach(function (done) { client = redis.createClient.apply(redis.createClient, args); - client.once("connect", function () { + client.once("ready", function () { client.flushdb(done); }); }); diff --git a/test/commands/sunionstore.spec.js b/test/commands/sunionstore.spec.js index c6e9cdeb607..3ebb10b085d 100644 --- a/test/commands/sunionstore.spec.js +++ b/test/commands/sunionstore.spec.js @@ -14,7 +14,7 @@ describe("The 'sunionstore' method", function () { beforeEach(function (done) { client = redis.createClient.apply(redis.createClient, args); - client.once("connect", function () { + client.once("ready", function () { client.flushdb(done); }); }); diff --git a/test/commands/sync.spec.js b/test/commands/sync.spec.js index a2d9009abe5..6a9813112be 100644 --- a/test/commands/sync.spec.js +++ b/test/commands/sync.spec.js @@ -14,7 +14,7 @@ describe.skip("The 'sync' method", function () { beforeEach(function (done) { client = redis.createClient.apply(redis.createClient, args); - client.once("connect", function () { + client.once("ready", function () { client.flushdb(done); }); }); diff --git a/test/commands/ttl.spec.js b/test/commands/ttl.spec.js index edc2db5c106..deec21720a7 100644 --- a/test/commands/ttl.spec.js +++ b/test/commands/ttl.spec.js @@ -14,7 +14,7 @@ describe("The 'ttl' method", function () { beforeEach(function (done) { client = redis.createClient.apply(redis.createClient, args); - client.once("connect", function () { + client.once("ready", function () { client.flushdb(done); }); }); diff --git a/test/commands/type.spec.js b/test/commands/type.spec.js index 9c99da0fbd3..c652e76d960 100644 --- a/test/commands/type.spec.js +++ b/test/commands/type.spec.js @@ -13,7 +13,7 @@ describe("The 'type' method", function () { beforeEach(function (done) { client = redis.createClient.apply(redis.createClient, args); - client.once("connect", function () { + client.once("ready", function () { client.flushdb(done); }); }); diff --git a/test/commands/watch.spec.js b/test/commands/watch.spec.js index 133fc770cad..87bcc81d222 100644 --- a/test/commands/watch.spec.js +++ b/test/commands/watch.spec.js @@ -16,7 +16,7 @@ describe("The 'watch' method", function () { beforeEach(function (done) { client = redis.createClient.apply(redis.createClient, args); - client.once("connect", function () { + client.once("ready", function () { client.flushdb(done); }); }); diff --git a/test/commands/zadd.spec.js b/test/commands/zadd.spec.js index 973660dcd08..514c51b3398 100644 --- a/test/commands/zadd.spec.js +++ b/test/commands/zadd.spec.js @@ -14,7 +14,7 @@ describe("The 'zadd' method", function () { beforeEach(function (done) { client = redis.createClient.apply(redis.createClient, args); - client.once("connect", function () { + client.once("ready", function () { client.flushdb(done); }); }); diff --git a/test/commands/zscore.spec.js b/test/commands/zscore.spec.js index fc5d6f37a09..43964a32b3c 100644 --- a/test/commands/zscore.spec.js +++ b/test/commands/zscore.spec.js @@ -14,7 +14,7 @@ describe("The 'zscore' method", function () { beforeEach(function (done) { client = redis.createClient.apply(redis.createClient, args); - client.once("connect", function () { + client.once("ready", function () { client.flushdb(done); }); }); diff --git a/test/connection.spec.js b/test/connection.spec.js index 042eabb7eeb..3def5eb15fa 100644 --- a/test/connection.spec.js +++ b/test/connection.spec.js @@ -88,7 +88,7 @@ describe("on lost connection", function () { var end = helper.callFuncAfter(done, 2); client.on('error', function (err) { - assert(/CONNECTION_BROKEN|ENOTFOUND/.test(err.code)); + assert(/CONNECTION_BROKEN|ENOTFOUND|EAI_AGAIN/.test(err.code)); end(); }); From 2232a8948e28f2f5e8bce81829d0de67f755225b Mon Sep 17 00:00:00 2001 From: Ruben Bridgewater Date: Sun, 4 Oct 2015 22:35:17 +0200 Subject: [PATCH 0419/1748] Exchange queue with a better one --- changelog.md | 1 + index.js | 4 +-- lib/queue.js | 61 ---------------------------------------------- lib/utils.js | 5 ++-- package.json | 3 +++ test/queue.spec.js | 39 ----------------------------- 6 files changed, 9 insertions(+), 104 deletions(-) delete mode 100644 lib/queue.js delete mode 100644 test/queue.spec.js diff --git a/changelog.md b/changelog.md index deef540ea02..03b2ab796e0 100644 --- a/changelog.md +++ b/changelog.md @@ -7,6 +7,7 @@ Features - Added disable_resubscribing option to prevent a client from resubscribing after reconnecting (@BridgeAR) - Added rename_commands options to handle renamed commands from the redis config (@digmxl & @BridgeAR) +- Increase performance by exchanging built in queue with [Petka Antonov's](@petkaantonov) [double-ended queue](https://github.com/petkaantonov/deque) and prevent polymorphism (@BridgeAR) Bugfixes diff --git a/index.js b/index.js index c1477dc06a1..3188cd42ad9 100644 --- a/index.js +++ b/index.js @@ -4,7 +4,7 @@ var net = require('net'); var URL = require('url'); var util = require('util'); var utils = require('./lib/utils'); -var Queue = require('./lib/queue'); +var Queue = require('double-ended-queue'); var Command = require('./lib/command'); var events = require('events'); var parsers = []; @@ -511,7 +511,7 @@ RedisClient.prototype.connection_gone = function (why) { this.retry_delay = this.connect_timeout - this.retry_totaltime; } - debug("Retry connection in " + this.retry_delay + " ms"); + debug('Retry connection in ' + this.retry_delay + ' ms'); this.retry_timer = setTimeout(retry_connection, this.retry_delay, this); }; diff --git a/lib/queue.js b/lib/queue.js deleted file mode 100644 index 5c9c9917e87..00000000000 --- a/lib/queue.js +++ /dev/null @@ -1,61 +0,0 @@ -'use strict'; - -// Queue class adapted from Tim Caswell's pattern library -// http://github.com/creationix/pattern/blob/master/lib/pattern/queue.js - -function Queue() { - this.tail = []; - this.head = []; - this.offset = 0; -} - -Queue.prototype.shift = function () { - if (this.offset === this.head.length) { - var tmp = this.head; - tmp.length = 0; - this.head = this.tail; - this.tail = tmp; - this.offset = 0; - if (this.head.length === 0) { - return; - } - } - var item = this.head[this.offset]; - this.head[this.offset] = null; - this.offset++; - return item; -}; - -Queue.prototype.push = function (item) { - return this.tail.push(item); -}; - -Queue.prototype.forEach = function (fn, thisv) { - var array = this.head.slice(this.offset), i, il; - - array.push.apply(array, this.tail); - - if (thisv) { - for (i = 0, il = array.length; i < il; i += 1) { - fn.call(thisv, array[i], i, array); - } - } else { - for (i = 0, il = array.length; i < il; i += 1) { - fn(array[i], i, array); - } - } - - return array; -}; - -Queue.prototype.getLength = function () { - return this.head.length - this.offset + this.tail.length; -}; - -Object.defineProperty(Queue.prototype, 'length', { - get: function () { - return this.getLength(); - } -}); - -module.exports = Queue; diff --git a/lib/utils.js b/lib/utils.js index 6e5cbad3563..6f1661f1d33 100644 --- a/lib/utils.js +++ b/lib/utils.js @@ -25,11 +25,12 @@ function replyToStrings(reply) { } if (Array.isArray(reply)) { + var res = new Array(reply.length); for (i = 0; i < reply.length; i++) { // Recusivly call the function as slowlog returns deep nested replies - reply[i] = replyToStrings(reply[i]); + res[i] = replyToStrings(reply[i]); } - return reply; + return res; } return reply; diff --git a/package.json b/package.json index 4b87b9ee193..f26d99e7482 100644 --- a/package.json +++ b/package.json @@ -17,6 +17,9 @@ "pretest": "optional-dev-dependency hiredis", "posttest": "jshint ." }, + "dependencies": { + "double-ended-queue": "^2.1.0-0" + }, "devDependencies": { "coveralls": "^2.11.2", "jshint": "^2.8.0", diff --git a/test/queue.spec.js b/test/queue.spec.js deleted file mode 100644 index 51a6bd7bb25..00000000000 --- a/test/queue.spec.js +++ /dev/null @@ -1,39 +0,0 @@ -'use strict'; - -var assert = require("assert"); -var Queue = require('../lib/queue'); - -describe('queue', function () { - var q = new Queue(); - - describe('push', function () { - it('places values on end of queue', function () { - q.push('a'); - q.push(3); - assert.equal(q.length, 2); - }); - }); - - describe('shift', function () { - it('removes values from front of queue', function () { - assert.equal(q.shift(), 'a'); - }); - }); - - describe('forEach', function () { - it('iterates over values in queue', function () { - q.forEach(function (v) { - assert.equal(v, 3); - }); - }); - }); - - describe('forEachWithScope', function () { - it('provides a scope to the iteration function', function () { - q.forEach(function (v) { - assert.equal(this.foo, 'bar'); - assert.equal(v, 3); - }, {foo: 'bar'}); - }); - }); -}); From e47ba4a5833ddbcea64add92b03b4d0cea1553f3 Mon Sep 17 00:00:00 2001 From: Ruben Bridgewater Date: Mon, 5 Oct 2015 10:57:57 +0200 Subject: [PATCH 0420/1748] Improve performance further --- changelog.md | 7 +++- index.js | 77 ++++++++++++++++++---------------- lib/parsers/javascript.js | 14 ++----- lib/utils.js | 2 +- test/parser/javascript.spec.js | 4 +- 5 files changed, 54 insertions(+), 50 deletions(-) diff --git a/changelog.md b/changelog.md index 03b2ab796e0..9cdca6501e6 100644 --- a/changelog.md +++ b/changelog.md @@ -1,13 +1,16 @@ Changelog ========= -## v.2.x.x - xx, 2015 +## v.2.2.0 - xx, 2015 Features - Added disable_resubscribing option to prevent a client from resubscribing after reconnecting (@BridgeAR) - Added rename_commands options to handle renamed commands from the redis config (@digmxl & @BridgeAR) -- Increase performance by exchanging built in queue with [Petka Antonov's](@petkaantonov) [double-ended queue](https://github.com/petkaantonov/deque) and prevent polymorphism (@BridgeAR) +- Increased performance (@BridgeAR) + - exchanging built in queue with [Petka Antonov's](@petkaantonov) [double-ended queue](https://github.com/petkaantonov/deque) + - prevent polymorphism + - optimize statements Bugfixes diff --git a/index.js b/index.js index 3188cd42ad9..b8300577de4 100644 --- a/index.js +++ b/index.js @@ -55,6 +55,13 @@ function RedisClient(stream, options) { } } } + this.options.return_buffers = !!this.options.return_buffers; + this.options.detect_buffers = !!this.options.detect_buffers; + // Override the detect_buffers setting if return_buffers is active and print a warning + if (this.options.return_buffers && this.options.detect_buffers) { + console.warn('>> WARNING: You activated return_buffers and detect_buffers at the same time. The return value is always going to be a buffer.'); + this.options.detect_buffers = false; + } this.should_buffer = false; this.command_queue_high_water = options.command_queue_high_water || 1000; this.command_queue_low_water = options.command_queue_low_water || 0; @@ -433,7 +440,7 @@ RedisClient.prototype.send_offline_queue = function () { this.offline_queue = new Queue(); // Even though items were shifted off, Queue backing store still uses memory until next add, so just get a new Queue - if (!buffered_writes) { + if (buffered_writes === 0) { this.should_buffer = false; this.emit('drain'); } @@ -531,8 +538,18 @@ RedisClient.prototype.return_error = function (err) { err.code = match[1]; } + this.emit_drain_idle(queue_len); + + if (command_obj.callback) { + command_obj.callback(err); + } else { + this.emit('error', err); + } +}; + +RedisClient.prototype.emit_drain_idle = function (queue_len) { if (this.pub_sub_mode === false && queue_len === 0) { - this.command_queue = new Queue(); + this.command_queue.clear(); this.emit('idle'); } @@ -540,12 +557,6 @@ RedisClient.prototype.return_error = function (err) { this.emit('drain'); this.should_buffer = false; } - - if (command_obj.callback) { - command_obj.callback(err); - } else { - this.emit('error', err); - } }; RedisClient.prototype.return_reply = function (reply) { @@ -566,37 +577,29 @@ RedisClient.prototype.return_reply = function (reply) { queue_len = this.command_queue.length; - if (this.pub_sub_mode === false && queue_len === 0) { - this.command_queue = new Queue(); // explicitly reclaim storage from old Queue - this.emit('idle'); - } - if (this.should_buffer && queue_len <= this.command_queue_low_water) { - this.emit('drain'); - this.should_buffer = false; - } + this.emit_drain_idle(queue_len); if (command_obj && !command_obj.sub_command) { if (typeof command_obj.callback === 'function') { if ('exec' !== command_obj.command) { - if (this.options.detect_buffers && command_obj.buffer_args === false) { + if (command_obj.buffer_args === false) { // If detect_buffers option was specified, then the reply from the parser will be Buffers. // If this command did not use Buffer arguments, then convert the reply to Strings here. reply = utils.reply_to_strings(reply); } // TODO - confusing and error-prone that hgetall is special cased in two places - if (reply && 'hgetall' === command_obj.command) { + if ('hgetall' === command_obj.command) { reply = utils.reply_to_object(reply); } } - command_obj.callback(null, reply); } else { debug('No callback for reply'); } } else if (this.pub_sub_mode || command_obj && command_obj.sub_command) { if (Array.isArray(reply)) { - if (!this.options.return_buffers && (!command_obj || this.options.detect_buffers && command_obj.buffer_args === false)) { + if (!command_obj || command_obj.buffer_args === false) { reply = utils.reply_to_strings(reply); } type = reply[0].toString(); @@ -620,11 +623,9 @@ RedisClient.prototype.return_reply = function (reply) { this.emit(type, reply[1], reply[2]); // channel, count } else { this.emit('error', new Error('subscriptions are active but got unknown reply type ' + type)); - return; } } else if (!this.closing) { this.emit('error', new Error('subscriptions are active but got an invalid reply: ' + reply)); - return; } } /* istanbul ignore else: this is a safety check that we should not be able to trigger */ @@ -648,7 +649,12 @@ RedisClient.prototype.return_reply = function (reply) { }; RedisClient.prototype.send_command = function (command, args, callback) { - var arg, command_obj, i, elem_count, buffer_args, stream = this.stream, command_str = '', buffered_writes = 0, err; + var arg, command_obj, i, err, + stream = this.stream, + command_str = '', + buffered_writes = 0, + buffer_args = false, + buffer = this.options.return_buffers; if (args === undefined) { args = []; @@ -660,7 +666,7 @@ RedisClient.prototype.send_command = function (command, args, callback) { } } - if (process.domain && callback) { + if (callback && process.domain) { callback = process.domain.bind(callback); } @@ -678,15 +684,17 @@ RedisClient.prototype.send_command = function (command, args, callback) { } } - buffer_args = false; for (i = 0; i < args.length; i += 1) { if (Buffer.isBuffer(args[i])) { buffer_args = true; break; } } + if (this.options.detect_buffers) { + buffer = buffer_args; + } - command_obj = new Command(command, args, false, buffer_args, callback); + command_obj = new Command(command, args, false, buffer, callback); if (!this.ready && !this.send_anyway || !stream.writable) { if (this.closing || !this.enable_offline_queue) { @@ -725,8 +733,6 @@ RedisClient.prototype.send_command = function (command, args, callback) { this.command_queue.push(command_obj); this.commands_sent += 1; - elem_count = args.length + 1; - if (typeof this.options.rename_commands !== 'undefined' && this.options.rename_commands[command]) { command = this.options.rename_commands[command]; } @@ -734,7 +740,7 @@ RedisClient.prototype.send_command = function (command, args, callback) { // Always use 'Multi bulk commands', but if passed any Buffer args, then do multiple writes, one for each arg. // This means that using Buffers in commands is going to be slower, so use Strings if you don't already have a Buffer. - command_str = '*' + elem_count + '\r\n$' + command.length + '\r\n' + command + '\r\n'; + command_str = '*' + (args.length + 1) + '\r\n$' + command.length + '\r\n' + command + '\r\n'; if (!buffer_args) { // Build up a string and send entire command in one write for (i = 0; i < args.length; i += 1) { @@ -752,10 +758,6 @@ RedisClient.prototype.send_command = function (command, args, callback) { for (i = 0; i < args.length; i += 1) { arg = args[i]; - if (!(Buffer.isBuffer(arg) || typeof arg === 'string')) { - arg = String(arg); - } - if (Buffer.isBuffer(arg)) { if (arg.length === 0) { debug('send_command: using empty string for 0 length buffer'); @@ -767,13 +769,16 @@ RedisClient.prototype.send_command = function (command, args, callback) { debug('send_command: buffer send ' + arg.length + ' bytes'); } } else { + if (typeof arg !== 'string') { + arg = String(arg); + } debug('send_command: string send ' + Buffer.byteLength(arg) + ' bytes: ' + arg); buffered_writes += !stream.write('$' + Buffer.byteLength(arg) + '\r\n' + arg + '\r\n'); } } } - debug('send_command buffered_writes: ' + buffered_writes, ' should_buffer: ' + this.should_buffer); - if (buffered_writes || this.command_queue.length >= this.command_queue_high_water) { + if (buffered_writes !== 0 || this.command_queue.length >= this.command_queue_high_water) { + debug('send_command buffered_writes: ' + buffered_writes, ' should_buffer: ' + this.should_buffer); this.should_buffer = true; } return !this.should_buffer; @@ -1077,7 +1082,7 @@ Multi.prototype.execute_callback = function (err, replies) { } replies[i].command = args[0].toUpperCase(); } else if (replies[i]) { - if (this._client.options.detect_buffers && this.wants_buffers[i + 1] === false) { + if (this.wants_buffers[i + 1] === false) { replies[i] = utils.reply_to_strings(replies[i]); } if (args[0] === 'hgetall') { diff --git a/lib/parsers/javascript.js b/lib/parsers/javascript.js index 1230401e4d8..cfd89f20b6b 100644 --- a/lib/parsers/javascript.js +++ b/lib/parsers/javascript.js @@ -2,9 +2,8 @@ var util = require('util'); -function ReplyParser(return_buffers) { +function ReplyParser() { this.name = exports.name; - this.return_buffers = return_buffers; this._buffer = new Buffer(0); this._offset = 0; @@ -37,10 +36,8 @@ ReplyParser.prototype._parseResult = function (type) { if (type === 45) { return new Error(this._buffer.toString(this._encoding, start, end)); - } else if (this.return_buffers) { - return this._buffer.slice(start, end); } - return this._buffer.toString(this._encoding, start, end); + return this._buffer.slice(start, end); } else if (type === 58) { // : // up to the delimiter end = this._packetEndOffset() - 1; @@ -77,15 +74,12 @@ ReplyParser.prototype._parseResult = function (type) { // set the offset to after the delimiter this._offset = end + 2; - if (this.return_buffers) { - return this._buffer.slice(start, end); - } - return this._buffer.toString(this._encoding, start, end); + return this._buffer.slice(start, end); } else if (type === 42) { // * offset = this._offset; packetHeader = this.parseHeader(); - if (packetHeader < 0) { + if (packetHeader === -1) { return null; } diff --git a/lib/utils.js b/lib/utils.js index 6f1661f1d33..3b282bc5ea4 100644 --- a/lib/utils.js +++ b/lib/utils.js @@ -1,6 +1,6 @@ 'use strict'; -// hgetall converts its replies to an Object. If the reply is empty, null is returned. +// hgetall converts its replies to an Object. If the reply is empty, null is returned. function replyToObject(reply) { var obj = {}, j, jl, key, val; diff --git a/test/parser/javascript.spec.js b/test/parser/javascript.spec.js index 491a292ab1f..499cfb11ca7 100644 --- a/test/parser/javascript.spec.js +++ b/test/parser/javascript.spec.js @@ -3,13 +3,15 @@ var assert = require('assert'); var Parser = require("../../lib/parsers/javascript").Parser; var config = require("../lib/config"); +var utils = require("../../lib/utils"); var redis = config.redis; describe('javascript parser', function () { it('handles multi-bulk reply', function (done) { - var parser = new Parser(false); + var parser = new Parser(); var reply_count = 0; function check_reply(reply) { + reply = utils.reply_to_strings(reply); assert.deepEqual(reply, [['a']], "Expecting multi-bulk reply of [['a']]"); reply_count++; } From 146d88154c6f080a45265f2663ab3d61cdc79d83 Mon Sep 17 00:00:00 2001 From: Ruben Bridgewater Date: Mon, 5 Oct 2015 11:16:24 +0200 Subject: [PATCH 0421/1748] Fix send_command always returning should_buffer boolean Fix .auth, .select and .exec to return the should_buffer boolean --- changelog.md | 3 ++- index.js | 25 +++++++++++++------------ test/commands/multi.spec.js | 3 ++- test/commands/select.spec.js | 8 +++++--- test/commands/setex.spec.js | 3 ++- 5 files changed, 24 insertions(+), 18 deletions(-) diff --git a/changelog.md b/changelog.md index 9cdca6501e6..be6f7a2b982 100644 --- a/changelog.md +++ b/changelog.md @@ -14,7 +14,8 @@ Features Bugfixes -- Fix a javascript parser regression introduced in 2.0 that could result in timeouts on high load. (@BridgeAR) +- Fix a javascript parser regression introduced in 2.0 that could result in timeouts on high load. (@BridgeAR) +- Fixed should_buffer boolean for .exec, .select and .auth commands not being returned (@BridgeAR) ## v2.1.0 - Oct 02, 2015 diff --git a/index.js b/index.js index b8300577de4..23fd75fc1e6 100644 --- a/index.js +++ b/index.js @@ -677,10 +677,11 @@ RedisClient.prototype.send_command = function (command, args, callback) { err = new Error('send_command: ' + command + ' value must not be undefined or null'); err.command = command; if (callback) { - return callback && callback(err); + callback(err); + } else { + this.emit('error', err); } - this.emit('error', err); - return; + return false; } } @@ -715,7 +716,8 @@ RedisClient.prototype.send_command = function (command, args, callback) { this.offline_queue.push(command_obj); this.should_buffer = true; } - return; + // Return false to signal no buffering + return false; } if (command === 'subscribe' || command === 'psubscribe' || command === 'unsubscribe' || command === 'punsubscribe') { @@ -728,7 +730,7 @@ RedisClient.prototype.send_command = function (command, args, callback) { err = new Error('Connection in subscriber mode, only subscriber commands may be used'); err.command = command.toUpperCase(); this.emit('error', err); - return; + return false; } this.command_queue.push(command_obj); this.commands_sent += 1; @@ -916,8 +918,7 @@ commands.forEach(function (fullCommand) { // store db in this.select_db to restore it on reconnect RedisClient.prototype.select = RedisClient.prototype.SELECT = function (db, callback) { var self = this; - - this.send_command('select', [db], function (err, res) { + return this.send_command('select', [db], function (err, res) { if (err === null) { self.selected_db = db; } @@ -939,16 +940,16 @@ RedisClient.prototype.auth = RedisClient.prototype.AUTH = function (pass, callba } else { this.emit('error', err); } - return; + return false; } this.auth_pass = pass; debug('Saving auth as ' + this.auth_pass); // Only run the callback once. So do not safe it if already connected if (this.connected) { - this.send_command('auth', [this.auth_pass], callback); - } else { - this.auth_callback = callback; + return this.send_command('auth', [this.auth_pass], callback); } + this.auth_callback = callback; + return false; }; RedisClient.prototype.hmset = RedisClient.prototype.HMSET = function (key, args, callback) { @@ -1048,7 +1049,7 @@ Multi.prototype.exec = Multi.prototype.EXEC = function (callback) { this.send_command(command, args, index, cb); } - this._client.send_command('exec', [], function(err, replies) { + return this._client.send_command('exec', [], function(err, replies) { self.execute_callback(err, replies); }); }; diff --git a/test/commands/multi.spec.js b/test/commands/multi.spec.js index fa099a28569..89868133b04 100644 --- a/test/commands/multi.spec.js +++ b/test/commands/multi.spec.js @@ -203,11 +203,12 @@ describe("The 'multi' method", function () { }); it('runs a multi without any further commands', function(done) { - client.multi().exec(function(err, res) { + var buffering = client.multi().exec(function(err, res) { assert.strictEqual(err, null); assert.strictEqual(res.length, 0); done(); }); + assert(typeof buffering === 'boolean'); }); it('allows multiple operations to be performed using a chaining API', function (done) { diff --git a/test/commands/select.spec.js b/test/commands/select.spec.js index dc121b781ac..3a90703332b 100644 --- a/test/commands/select.spec.js +++ b/test/commands/select.spec.js @@ -24,10 +24,11 @@ describe("The 'select' method", function () { }); it("returns an error if redis is not connected", function (done) { - client.select(1, function (err, res) { + var buffering = client.select(1, function (err, res) { assert(err.message.match(/The connection has already been closed/)); done(); }); + assert(typeof buffering === 'boolean'); }); }); @@ -36,7 +37,7 @@ describe("The 'select' method", function () { beforeEach(function (done) { client = redis.createClient.apply(redis.createClient, args); - client.once("connect", function () { done(); }); + client.once("ready", function () { done(); }); }); afterEach(function () { @@ -46,11 +47,12 @@ describe("The 'select' method", function () { it("changes the database and calls the callback", function (done) { // default value of null means database 0 will be used. assert.strictEqual(client.selected_db, null, "default db should be null"); - client.SELECT(1, function (err, res) { + var buffering = client.SELECT(1, function (err, res) { helper.isNotError()(err, res); assert.strictEqual(client.selected_db, 1, "db should be 1 after select"); done(); }); + assert(typeof buffering === 'boolean'); }); describe("and a callback is specified", function () { diff --git a/test/commands/setex.spec.js b/test/commands/setex.spec.js index e8de49bd574..7d0b152ae0c 100644 --- a/test/commands/setex.spec.js +++ b/test/commands/setex.spec.js @@ -29,7 +29,8 @@ describe("The 'setex' method", function () { }); it('returns an error if no value is provided', function (done) { - client.SETEX(["setex key", "100", undefined], helper.isError(done)); + var buffering = client.SETEX(["setex key", "100", undefined], helper.isError(done)); + assert(typeof buffering === 'boolean'); }); afterEach(function () { From f8c245e04f1658d35f8676ff5e1a43baf2748550 Mon Sep 17 00:00:00 2001 From: Ruben Bridgewater Date: Mon, 5 Oct 2015 23:30:21 +0200 Subject: [PATCH 0422/1748] Add .batch with better pipeline implementation --- README.md | 7 ++ changelog.md | 9 ++- index.js | 132 ++++++++++++++++++++++++++++++------ test/commands/multi.spec.js | 2 +- test/detect_buffers.spec.js | 60 +++++++++++++++- 5 files changed, 188 insertions(+), 22 deletions(-) diff --git a/README.md b/README.md index c862017d0a5..3f97f7df9bc 100644 --- a/README.md +++ b/README.md @@ -403,6 +403,7 @@ channel name as `channel` and the new count of subscriptions for this client as `MULTI` commands are queued up until an `EXEC` is issued, and then all commands are run atomically by Redis. The interface in `node_redis` is to return an individual `Multi` object by calling `client.multi()`. +If any command fails to queue, all commands are rolled back and none is going to be executed (For further information look at [transactions](http://redis.io/topics/transactions)). ```js var redis = require("./index"), @@ -485,6 +486,12 @@ client.multi([ console.log(replies); }); ``` +## client.batch([commands]) + +`BATCH` commands are queued up until an `EXEC` is issued, and then all commands are run atomically by +Redis. The interface in `node_redis` is to return an individual `Batch` object by calling `client.batch()`. +The only difference between .batch and .multi is that no transaction is going to be used. +Be aware that the errors are - just like in multi statements - in the result. Otherwise both, errors and results could be returned at the same time. ## Monitor mode diff --git a/changelog.md b/changelog.md index be6f7a2b982..23efe9e2763 100644 --- a/changelog.md +++ b/changelog.md @@ -1,7 +1,7 @@ Changelog ========= -## v.2.2.0 - xx, 2015 +## v.2.2.0 - 07, 2015 - The peregrino falcon Features @@ -11,6 +11,13 @@ Features - exchanging built in queue with [Petka Antonov's](@petkaantonov) [double-ended queue](https://github.com/petkaantonov/deque) - prevent polymorphism - optimize statements +- Added .batch command, similar to multi but without transaction (@BridgeAR) +- Improved pipelining to minimize the [RTT](http://redis.io/topics/pipelining) further (@BridgeAR) + +This release is mainly focusing on further speed improvements and we can proudly say that node_redis is very likely outperforming any other node redis client. + +If you do not rely on transactions but want to reduze the RTT you can use .batch from now on. It'll behave just the same as .multi but it does not have any transaction and therefor won't roll back any failed commands. +Both .multi and .batch are from now on going to fire the commands in bulk without doing any other operation in between. Bugfixes diff --git a/index.js b/index.js index 23fd75fc1e6..1e1691071a9 100644 --- a/index.js +++ b/index.js @@ -85,6 +85,8 @@ function RedisClient(stream, options) { this.parser_module = null; this.selected_db = null; // save the selected db here, used when reconnecting this.old_state = null; + this.pipeline = 0; + this.pipeline_queue = new Queue(); this.install_stream_listeners(); events.EventEmitter.call(this); @@ -648,6 +650,26 @@ RedisClient.prototype.return_reply = function (reply) { } }; +RedisClient.prototype.writeStream = function (data) { + var stream = this.stream; + var nr = 0; + + // Do not use a pipeline + if (this.pipeline === 0) { + return !stream.write(data); + } + this.pipeline--; + this.pipeline_queue.push(data); + if (this.pipeline === 0) { + var len = this.pipeline_queue.length; + while (len--) { + nr += !stream.write(this.pipeline_queue.shift()); + } + return !nr; + } + return true; +}; + RedisClient.prototype.send_command = function (command, args, callback) { var arg, command_obj, i, err, stream = this.stream, @@ -753,21 +775,21 @@ RedisClient.prototype.send_command = function (command, args, callback) { command_str += '$' + Buffer.byteLength(arg) + '\r\n' + arg + '\r\n'; } debug('Send ' + this.address + ' id ' + this.connection_id + ': ' + command_str); - buffered_writes += !stream.write(command_str); + buffered_writes += !this.writeStream(command_str); } else { debug('Send command (' + command_str + ') has Buffer arguments'); - buffered_writes += !stream.write(command_str); + buffered_writes += !this.writeStream(command_str); for (i = 0; i < args.length; i += 1) { arg = args[i]; if (Buffer.isBuffer(arg)) { if (arg.length === 0) { debug('send_command: using empty string for 0 length buffer'); - buffered_writes += !stream.write('$0\r\n\r\n'); + buffered_writes += !this.writeStream('$0\r\n\r\n'); } else { - buffered_writes += !stream.write('$' + arg.length + '\r\n'); - buffered_writes += !stream.write(arg); - buffered_writes += !stream.write('\r\n'); + buffered_writes += !this.writeStream('$' + arg.length + '\r\n'); + buffered_writes += !this.writeStream(arg); + buffered_writes += !this.writeStream('\r\n'); debug('send_command: buffer send ' + arg.length + ' bytes'); } } else { @@ -775,7 +797,7 @@ RedisClient.prototype.send_command = function (command, args, callback) { arg = String(arg); } debug('send_command: string send ' + Buffer.byteLength(arg) + ' bytes: ' + arg); - buffered_writes += !stream.write('$' + Buffer.byteLength(arg) + '\r\n' + arg + '\r\n'); + buffered_writes += !this.writeStream('$' + Buffer.byteLength(arg) + '\r\n' + arg + '\r\n'); } } } @@ -839,9 +861,15 @@ RedisClient.prototype.end = function (flush) { return this.stream.destroySoon(); }; -function Multi(client, args) { +function Multi(client, args, transaction) { this._client = client; - this.queue = [['multi']]; + this.queue = []; + if (transaction) { + this.exec = this.exec_transaction; + this.EXEC = this.exec_transaction; + this.queue.push(['multi']); + } + this._client.pipeline_queue.clear(); var command, tmp_args; if (Array.isArray(args)) { while (tmp_args = args.shift()) { @@ -857,7 +885,11 @@ function Multi(client, args) { } RedisClient.prototype.multi = RedisClient.prototype.MULTI = function (args) { - return new Multi(this, args); + return new Multi(this, args, true); +}; + +RedisClient.prototype.batch = RedisClient.prototype.BATCH = function (args) { + return new Multi(this, args, false); }; commands.forEach(function (fullCommand) { @@ -1025,25 +1057,35 @@ Multi.prototype.send_command = function (command, args, index, cb) { }); }; -Multi.prototype.exec = Multi.prototype.EXEC = function (callback) { +Multi.prototype.exec_transaction = function (callback) { var self = this; + var len = this.queue.length; + var cb; this.errors = []; this.callback = callback; - this.wants_buffers = new Array(this.queue.length); + this._client.pipeline = len; + this.wants_buffers = new Array(len); // drain queue, callback will catch 'QUEUED' or error - for (var index = 0; index < this.queue.length; index++) { + for (var index = 0; index < len; index++) { var args = this.queue[index].slice(0); var command = args.shift(); - var cb; if (typeof args[args.length - 1] === 'function') { cb = args.pop(); + } else { + cb = undefined; } // Keep track of who wants buffer responses: - this.wants_buffers[index] = false; - for (var i = 0; i < args.length; i += 1) { - if (Buffer.isBuffer(args[i])) { - this.wants_buffers[index] = true; - break; + if (this._client.options.return_buffers) { + this.wants_buffers[index] = true; + } else if (!this._client.options.detect_buffers) { + this.wants_buffers[index] = false; + } else { + this.wants_buffers[index] = false; + for (var i = 0; i < args.length; i += 1) { + if (Buffer.isBuffer(args[i])) { + this.wants_buffers[index] = true; + break; + } } } this.send_command(command, args, index, cb); @@ -1107,6 +1149,58 @@ Multi.prototype.execute_callback = function (err, replies) { } }; +Multi.prototype.callback = function (cb, command, i) { + var self = this; + return function (err, res) { + if (err) { + self.results[i] = err; + } else { + self.results[i] = res; + } + if (cb) { + cb(err, res); + } + // Do not emit an error here. Otherwise each error would result in one emit. + // The errors will be returned in the result anyway + }; +}; + +Multi.prototype.exec = Multi.prototype.EXEC = function (callback) { + var len = this.queue.length; + var self = this; + var index = 0; + var args; + if (len === 0) { + if (callback) { + callback(null, []); + } + return false; + } + this.results = new Array(len); + this._client.pipeline = len; + var lastCallback = function (cb) { + return function (err, res) { + cb(err, res); + callback(null, self.results); + }; + }; + while (args = this.queue.shift()) { + var command = args.shift(); + var cb; + if (typeof args[args.length - 1] === 'function') { + cb = this.callback(args.pop(), command, index); + } else { + cb = this.callback(undefined, command, index); + } + if (callback && index === len - 1) { + cb = lastCallback(cb); + } + this._client.send_command(command, args, cb); + index++; + } + return this._client.should_buffer; +}; + var createClient_unix = function (path, options){ var cnxOptions = { path: path diff --git a/test/commands/multi.spec.js b/test/commands/multi.spec.js index 89868133b04..99e43d472f1 100644 --- a/test/commands/multi.spec.js +++ b/test/commands/multi.spec.js @@ -216,7 +216,7 @@ describe("The 'multi' method", function () { .mset('some', '10', 'keys', '20') .incr('some') .incr('keys') - .mget('some', 'keys') + .mget('some', ['keys']) .exec(function (err, replies) { assert.strictEqual(null, err); assert.equal('OK', replies[0]); diff --git a/test/detect_buffers.spec.js b/test/detect_buffers.spec.js index fc9cd1aa314..747253918c5 100644 --- a/test/detect_buffers.spec.js +++ b/test/detect_buffers.spec.js @@ -79,6 +79,27 @@ describe("detect_buffers", function () { }); }); + describe('batch.hget', function () { + it('can interleave string and buffer results', function (done) { + client.batch() + .hget("hash key 2", "key 1") + .hget(new Buffer("hash key 2"), "key 1") + .hget("hash key 2", new Buffer("key 2")) + .hget("hash key 2", "key 2") + .exec(function (err, reply) { + assert.strictEqual(true, Array.isArray(reply)); + assert.strictEqual(4, reply.length); + assert.strictEqual("val 1", reply[0]); + assert.strictEqual(true, Buffer.isBuffer(reply[1])); + assert.strictEqual("", reply[1].inspect()); + assert.strictEqual(true, Buffer.isBuffer(reply[2])); + assert.strictEqual("", reply[2].inspect()); + assert.strictEqual("val 2", reply[3]); + return done(err); + }); + }); + }); + describe('hmget', function () { describe('first argument is a string', function () { it('returns strings for keys requested', function (done) { @@ -149,6 +170,19 @@ describe("detect_buffers", function () { return done(err); }); }); + + it("returns buffers for keys requested in .batch", function (done) { + client.batch().hmget(new Buffer("hash key 2"), "key 1", "key 2").exec(function (err, reply) { + assert.strictEqual(true, Array.isArray(reply)); + assert.strictEqual(1, reply.length); + assert.strictEqual(2, reply[0].length); + assert.strictEqual(true, Buffer.isBuffer(reply[0][0])); + assert.strictEqual(true, Buffer.isBuffer(reply[0][1])); + assert.strictEqual("", reply[0][0].inspect()); + assert.strictEqual("", reply[0][1].inspect()); + return done(err); + }); + }); }); }); @@ -174,6 +208,17 @@ describe("detect_buffers", function () { return done(err); }); }); + + it('returns string values when executed in .batch', function (done) { + client.batch().hgetall("hash key 2").exec(function (err, reply) { + assert.strictEqual(1, reply.length); + assert.strictEqual("object", typeof reply[0]); + assert.strictEqual(2, Object.keys(reply[0]).length); + assert.strictEqual("val 1", reply[0]["key 1"]); + assert.strictEqual("val 2", reply[0]["key 2"]); + return done(err); + }); + }); }); describe('first argument is a buffer', function () { @@ -193,7 +238,20 @@ describe("detect_buffers", function () { it('returns buffer values when executed in transaction', function (done) { client.multi().hgetall(new Buffer("hash key 2")).exec(function (err, reply) { assert.strictEqual(1, reply.length); - assert.strictEqual("object", typeof reply); + assert.strictEqual("object", typeof reply[0]); + assert.strictEqual(2, Object.keys(reply[0]).length); + assert.strictEqual(true, Buffer.isBuffer(reply[0]["key 1"])); + assert.strictEqual(true, Buffer.isBuffer(reply[0]["key 2"])); + assert.strictEqual("", reply[0]["key 1"].inspect()); + assert.strictEqual("", reply[0]["key 2"].inspect()); + return done(err); + }); + }); + + it('returns buffer values when executed in .batch', function (done) { + client.batch().hgetall(new Buffer("hash key 2")).exec(function (err, reply) { + assert.strictEqual(1, reply.length); + assert.strictEqual("object", typeof reply[0]); assert.strictEqual(2, Object.keys(reply[0]).length); assert.strictEqual(true, Buffer.isBuffer(reply[0]["key 1"])); assert.strictEqual(true, Buffer.isBuffer(reply[0]["key 2"])); From d59d6cf1142777013ab7b91e19f9a9218e773162 Mon Sep 17 00:00:00 2001 From: Ruben Bridgewater Date: Mon, 5 Oct 2015 23:30:54 +0200 Subject: [PATCH 0423/1748] Add .batch tests --- test/commands/batch.spec.js | 311 ++++++++++++++++++++++++++++++++++++ 1 file changed, 311 insertions(+) create mode 100644 test/commands/batch.spec.js diff --git a/test/commands/batch.spec.js b/test/commands/batch.spec.js new file mode 100644 index 00000000000..0e009c5b21c --- /dev/null +++ b/test/commands/batch.spec.js @@ -0,0 +1,311 @@ +'use strict'; + +var assert = require('assert'); +var config = require("../lib/config"); +var helper = require('../helper'); +var redis = config.redis; +var uuid = require('uuid'); + +describe("The 'batch' method", function () { + + helper.allTests(function(parser, ip, args) { + + describe("using " + parser + " and " + ip, function () { + var key, value; + + beforeEach(function () { + key = uuid.v4(); + value = uuid.v4(); + }); + + describe("when not connected", function () { + var client; + + beforeEach(function (done) { + client = redis.createClient.apply(redis.createClient, args); + client.once("connect", function () { + client.quit(); + }); + client.on('end', function () { + return done(); + }); + }); + + it("returns an empty array", function (done) { + var batch = client.batch(); + batch.exec(function (err, res) { + assert.strictEqual(err, null); + assert.strictEqual(res.length, 0); + done(); + }); + }); + + it("returns an empty array if promisified", function () { + return client.batch().execAsync().then(function(res) { + assert.strictEqual(res.length, 0); + }); + }); + }); + + describe("when connected", function () { + var client; + + beforeEach(function (done) { + client = redis.createClient.apply(redis.createClient, args); + client.once("connect", function () { + client.flushdb(function (err) { + return done(err); + }); + }); + }); + + afterEach(function () { + client.end(); + }); + + it('fail individually when one command fails using chaining notation', function (done) { + var batch1, batch2; + batch1 = client.batch(); + batch1.mset("batchfoo", "10", "batchbar", "20", helper.isString("OK")); + + // Provoke an error at queue time + batch1.set("foo2", helper.isError()); + batch1.incr("batchfoo"); + batch1.incr("batchbar"); + batch1.exec(function () { + // Confirm that the previous command, while containing an error, still worked. + batch2 = client.batch(); + batch2.get('foo2', helper.isNull()); + batch2.incr("batchbar", helper.isNumber(22)); + batch2.incr("batchfoo", helper.isNumber(12)); + batch2.exec(function (err, replies) { + assert.strictEqual(null, replies[0]); + assert.strictEqual(22, replies[1]); + assert.strictEqual(12, replies[2]); + return done(); + }); + }); + }); + + it('fail individually when one command fails and emit the error if no callback has been provided', function (done) { + var batch1; + client.on('error', function (err) { + done(err); + }); + batch1 = client.batch(); + batch1.mset("batchfoo", "10", "batchbar", "20", helper.isString("OK")); + + // Provoke an error at queue time + batch1.set("foo2"); + batch1.incr("batchfoo"); + batch1.incr("batchbar"); + batch1.exec(function (err, res) { + assert.strictEqual(res[1].command, 'SET'); + assert.strictEqual(res[1].code, 'ERR'); + done(); + }); + }); + + it('fail individually when one command in an array of commands fails', function (done) { + // test nested batch-bulk replies + client.batch([ + ["mget", "batchfoo", "batchbar", function (err, res) { + assert.strictEqual(2, res.length); + assert.strictEqual(0, +res[0]); + assert.strictEqual(0, +res[1]); + }], + ["set", "foo2", helper.isError()], + ["incr", "batchfoo"], + ["incr", "batchbar"] + ]).exec(function (err, replies) { + assert.strictEqual(2, replies[0].length); + assert.strictEqual(null, replies[0][0]); + assert.strictEqual(null, replies[0][1]); + assert.strictEqual('SET', replies[1].command); + assert.strictEqual("1", replies[2].toString()); + assert.strictEqual("1", replies[3].toString()); + return done(); + }); + }); + + it('handles batchple operations being applied to a set', function (done) { + client.sadd("some set", "mem 1"); + client.sadd(["some set", "mem 2"]); + client.sadd("some set", "mem 3"); + client.sadd("some set", "mem 4"); + + // make sure empty mb reply works + client.del("some missing set"); + client.smembers("some missing set", function (err, reply) { + // make sure empty mb reply works + assert.strictEqual(0, reply.length); + }); + + // test nested batch-bulk replies with empty mb elements. + client.BATCH([ + ["smembers", ["some set"]], + ["del", "some set"], + ["smembers", "some set"] + ]) + .scard("some set") + .exec(function (err, replies) { + assert.strictEqual(4, replies[0].length); + assert.strictEqual(0, replies[2].length); + return done(); + }); + }); + + it('allows batchple operations to be performed using constructor with all kinds of syntax', function (done) { + var now = Date.now(); + var arr = ["batchhmset", "batchbar", "batchbaz"]; + var arr2 = ['some manner of key', 'otherTypes']; + var arr3 = [5768, "batchbarx", "batchfoox"]; + var arr4 = ["mset", [578, "batchbar"], helper.isString('OK')]; + client.batch([ + arr4, + [["mset", "batchfoo2", "batchbar2", "batchfoo3", "batchbar3"], helper.isString('OK')], + ["hmset", arr], + [["hmset", "batchhmset2", "batchbar2", "batchfoo3", "batchbar3", "test", helper.isString('OK')]], + ["hmset", ["batchhmset", "batchbar", "batchfoo", helper.isString('OK')]], + ["hmset", arr3, helper.isString('OK')], + ['hmset', now, {123456789: "abcdefghij", "some manner of key": "a type of value", "otherTypes": 555}], + ['hmset', 'key2', {"0123456789": "abcdefghij", "some manner of key": "a type of value", "otherTypes": 999}, helper.isString('OK')], + ["HMSET", "batchhmset", ["batchbar", "batchbaz"]], + ["hmset", "batchhmset", ["batchbar", "batchbaz"], helper.isString('OK')], + ]) + .hmget(now, 123456789, 'otherTypes') + .hmget('key2', arr2, function noop() {}) + .hmget(['batchhmset2', 'some manner of key', 'batchbar3']) + .mget('batchfoo2', ['batchfoo3', 'batchfoo'], function(err, res) { + assert(res[0], 'batchfoo3'); + assert(res[1], 'batchfoo'); + }) + .exec(function (err, replies) { + assert.equal(arr.length, 3); + assert.equal(arr2.length, 2); + assert.equal(arr3.length, 3); + assert.equal(arr4.length, 3); + assert.strictEqual(null, err); + assert.equal(replies[10][1], '555'); + assert.equal(replies[11][0], 'a type of value'); + assert.strictEqual(replies[12][0], null); + assert.equal(replies[12][1], 'test'); + assert.equal(replies[13][0], 'batchbar2'); + assert.equal(replies[13].length, 3); + assert.equal(replies.length, 14); + return done(); + }); + }); + + it('converts a non string key to a string', function(done) { + // TODO: Converting the key might change soon again. + client.batch().hmset(true, { + test: 123, + bar: 'baz' + }).exec(done); + }); + + it('runs a batch without any further commands', function(done) { + var buffering = client.batch().exec(function(err, res) { + assert.strictEqual(err, null); + assert.strictEqual(res.length, 0); + done(); + }); + assert(typeof buffering === 'boolean'); + }); + + it('runs a batch without any further commands and without callback', function() { + var buffering = client.batch().exec(); + assert(typeof buffering === 'boolean'); + assert(!buffering); + }); + + it('allows batchple operations to be performed using a chaining API', function (done) { + client.batch() + .mset('some', '10', 'keys', '20') + .incr('some') + .incr('keys') + .mget('some', 'keys') + .exec(function (err, replies) { + assert.strictEqual(null, err); + assert.equal('OK', replies[0]); + assert.equal(11, replies[1]); + assert.equal(21, replies[2]); + assert.equal(11, replies[3][0].toString()); + assert.equal(21, replies[3][1].toString()); + return done(); + }); + }); + + it('allows batchple commands to work the same as normal to be performed using a chaining API', function (done) { + client.batch() + .mset(['some', '10', 'keys', '20']) + .incr(['some', helper.isNumber(11)]) + .incr(['keys'], helper.isNumber(21)) + .mget('some', 'keys') + .exec(function (err, replies) { + assert.strictEqual(null, err); + assert.equal('OK', replies[0]); + assert.equal(11, replies[1]); + assert.equal(21, replies[2]); + assert.equal(11, replies[3][0].toString()); + assert.equal(21, replies[3][1].toString()); + return done(); + }); + }); + + it('allows batchple commands to work the same as normal to be performed using a chaining API promisified', function () { + return client.batch() + .mset(['some', '10', 'keys', '20']) + .incr(['some', helper.isNumber(11)]) + .incr(['keys'], helper.isNumber(21)) + .mget('some', 'keys') + .execAsync() + .then(function (replies) { + assert.equal('OK', replies[0]); + assert.equal(11, replies[1]); + assert.equal(21, replies[2]); + assert.equal(11, replies[3][0].toString()); + assert.equal(21, replies[3][1].toString()); + }); + }); + + it('allows an array to be provided indicating batchple operations to perform', function (done) { + // test nested batch-bulk replies with nulls. + client.batch([ + ["mget", ["batchfoo", "some", "random value", "keys"]], + ["incr", "batchfoo"] + ]) + .exec(function (err, replies) { + assert.strictEqual(replies.length, 2); + assert.strictEqual(replies[0].length, 4); + return done(); + }); + }); + + it('allows multiple operations to be performed on a hash', function (done) { + client.batch() + .hmset("batchhash", "a", "foo", "b", 1) + .hmset("batchhash", { + extra: "fancy", + things: "here" + }) + .hgetall("batchhash") + .exec(done); + }); + + it("should work without any callback", function (done) { + helper.serverVersionAtLeast.call(this, client, [2, 6, 5]); + + var batch = client.batch(); + batch.set("baz", "binary"); + batch.set("foo", "bar"); + batch.exec(); + + client.get('foo', helper.isString('bar', done)); + }); + + }); + }); + }); +}); From 76a2e31c9e5ab8bbff01740ca3270d9aa8d901d3 Mon Sep 17 00:00:00 2001 From: Ruben Bridgewater Date: Mon, 5 Oct 2015 23:31:05 +0200 Subject: [PATCH 0424/1748] Add return_buffers tests --- test/return_buffers.spec.js | 232 ++++++++++++++++++++++++++++++++++++ 1 file changed, 232 insertions(+) create mode 100644 test/return_buffers.spec.js diff --git a/test/return_buffers.spec.js b/test/return_buffers.spec.js new file mode 100644 index 00000000000..f0f282f9d8f --- /dev/null +++ b/test/return_buffers.spec.js @@ -0,0 +1,232 @@ +'use strict'; + +var assert = require("assert"); +var config = require("./lib/config"); +var helper = require('./helper'); +var redis = config.redis; + +describe("return_buffers", function () { + + helper.allTests(function(parser, ip) { + + describe("using " + parser + " and " + ip, function () { + var client; + var args = config.configureClient(parser, ip, { + return_buffers: true, + detect_buffers: true + }); + + beforeEach(function (done) { + client = redis.createClient.apply(redis.createClient, args); + if (args[2].detect_buffers) { + args[2].detect_buffers = false; + } + client.once("error", done); + client.once("connect", function () { + client.flushdb(function (err) { + client.hmset("hash key 2", "key 1", "val 1", "key 2", "val 2"); + client.set("string key 1", "string value"); + return done(err); + }); + }); + }); + + describe('get', function () { + describe('first argument is a string', function () { + it('returns a buffer', function (done) { + client.get("string key 1", function (err, reply) { + assert.strictEqual(true, Buffer.isBuffer(reply)); + assert.strictEqual("", reply.inspect()); + return done(err); + }); + }); + + it('returns a bufffer when executed as part of transaction', function (done) { + client.multi().get("string key 1").exec(function (err, reply) { + assert.strictEqual(1, reply.length); + assert.strictEqual(true, Buffer.isBuffer(reply[0])); + assert.strictEqual("", reply[0].inspect()); + return done(err); + }); + }); + }); + }); + + describe('multi.hget', function () { + it('returns buffers', function (done) { + client.multi() + .hget("hash key 2", "key 1") + .hget(new Buffer("hash key 2"), "key 1") + .hget("hash key 2", new Buffer("key 2")) + .hget("hash key 2", "key 2") + .exec(function (err, reply) { + assert.strictEqual(true, Array.isArray(reply)); + assert.strictEqual(4, reply.length); + assert.strictEqual("", reply[0].inspect()); + assert.strictEqual(true, Buffer.isBuffer(reply[1])); + assert.strictEqual("", reply[1].inspect()); + assert.strictEqual(true, Buffer.isBuffer(reply[2])); + assert.strictEqual("", reply[2].inspect()); + assert.strictEqual(true, Buffer.isBuffer(reply[3])); + assert.strictEqual("", reply[3].inspect()); + return done(err); + }); + }); + }); + + describe('batch.hget', function () { + it('returns buffers', function (done) { + client.batch() + .hget("hash key 2", "key 1") + .hget(new Buffer("hash key 2"), "key 1") + .hget("hash key 2", new Buffer("key 2")) + .hget("hash key 2", "key 2") + .exec(function (err, reply) { + assert.strictEqual(true, Array.isArray(reply)); + assert.strictEqual(4, reply.length); + assert.strictEqual("", reply[0].inspect()); + assert.strictEqual(true, Buffer.isBuffer(reply[1])); + assert.strictEqual("", reply[1].inspect()); + assert.strictEqual(true, Buffer.isBuffer(reply[2])); + assert.strictEqual("", reply[2].inspect()); + assert.strictEqual(true, Buffer.isBuffer(reply[3])); + assert.strictEqual("", reply[3].inspect()); + return done(err); + }); + }); + }); + + describe('hmget', function () { + describe('first argument is a string', function () { + it('handles array of strings with undefined values in transaction (repro #344)', function (done) { + client.multi().hmget("hash key 2", "key 3", "key 4").exec(function(err, reply) { + assert.strictEqual(true, Array.isArray(reply)); + assert.strictEqual(1, reply.length); + assert.strictEqual(2, reply[0].length); + assert.equal(null, reply[0][0]); + assert.equal(null, reply[0][1]); + return done(err); + }); + }); + }); + + describe('first argument is a buffer', function () { + it('returns buffers for keys requested', function (done) { + client.hmget(new Buffer("hash key 2"), "key 1", "key 2", function (err, reply) { + assert.strictEqual(true, Array.isArray(reply)); + assert.strictEqual(2, reply.length); + assert.strictEqual(true, Buffer.isBuffer(reply[0])); + assert.strictEqual(true, Buffer.isBuffer(reply[1])); + assert.strictEqual("", reply[0].inspect()); + assert.strictEqual("", reply[1].inspect()); + return done(err); + }); + }); + + it("returns buffers for keys requested in transaction", function (done) { + client.multi().hmget(new Buffer("hash key 2"), "key 1", "key 2").exec(function (err, reply) { + assert.strictEqual(true, Array.isArray(reply)); + assert.strictEqual(1, reply.length); + assert.strictEqual(2, reply[0].length); + assert.strictEqual(true, Buffer.isBuffer(reply[0][0])); + assert.strictEqual(true, Buffer.isBuffer(reply[0][1])); + assert.strictEqual("", reply[0][0].inspect()); + assert.strictEqual("", reply[0][1].inspect()); + return done(err); + }); + }); + + it("returns buffers for keys requested in .batch", function (done) { + client.batch().hmget(new Buffer("hash key 2"), "key 1", "key 2").exec(function (err, reply) { + assert.strictEqual(true, Array.isArray(reply)); + assert.strictEqual(1, reply.length); + assert.strictEqual(2, reply[0].length); + assert.strictEqual(true, Buffer.isBuffer(reply[0][0])); + assert.strictEqual(true, Buffer.isBuffer(reply[0][1])); + assert.strictEqual("", reply[0][0].inspect()); + assert.strictEqual("", reply[0][1].inspect()); + return done(err); + }); + }); + }); + }); + + describe('hgetall', function (done) { + describe('first argument is a string', function () { + it('returns buffer values', function (done) { + client.hgetall("hash key 2", function (err, reply) { + assert.strictEqual("object", typeof reply); + assert.strictEqual(2, Object.keys(reply).length); + assert.strictEqual("", reply["key 1"].inspect()); + assert.strictEqual("", reply["key 2"].inspect()); + return done(err); + }); + }); + + it('returns buffer values when executed in transaction', function (done) { + client.multi().hgetall("hash key 2").exec(function (err, reply) { + assert.strictEqual(1, reply.length); + assert.strictEqual("object", typeof reply[0]); + assert.strictEqual(2, Object.keys(reply[0]).length); + assert.strictEqual("", reply[0]["key 1"].inspect()); + assert.strictEqual("", reply[0]["key 2"].inspect()); + return done(err); + }); + }); + + it('returns buffer values when executed in .batch', function (done) { + client.batch().hgetall("hash key 2").exec(function (err, reply) { + assert.strictEqual(1, reply.length); + assert.strictEqual("object", typeof reply[0]); + assert.strictEqual(2, Object.keys(reply[0]).length); + assert.strictEqual("", reply[0]["key 1"].inspect()); + assert.strictEqual("", reply[0]["key 2"].inspect()); + return done(err); + }); + }); + }); + + describe('first argument is a buffer', function () { + it('returns buffer values', function (done) { + client.hgetall(new Buffer("hash key 2"), function (err, reply) { + assert.strictEqual(null, err); + assert.strictEqual("object", typeof reply); + assert.strictEqual(2, Object.keys(reply).length); + assert.strictEqual(true, Buffer.isBuffer(reply["key 1"])); + assert.strictEqual(true, Buffer.isBuffer(reply["key 2"])); + assert.strictEqual("", reply["key 1"].inspect()); + assert.strictEqual("", reply["key 2"].inspect()); + return done(err); + }); + }); + + it('returns buffer values when executed in transaction', function (done) { + client.multi().hgetall(new Buffer("hash key 2")).exec(function (err, reply) { + assert.strictEqual(1, reply.length); + assert.strictEqual("object", typeof reply[0]); + assert.strictEqual(2, Object.keys(reply[0]).length); + assert.strictEqual(true, Buffer.isBuffer(reply[0]["key 1"])); + assert.strictEqual(true, Buffer.isBuffer(reply[0]["key 2"])); + assert.strictEqual("", reply[0]["key 1"].inspect()); + assert.strictEqual("", reply[0]["key 2"].inspect()); + return done(err); + }); + }); + + it('returns buffer values when executed in .batch', function (done) { + client.batch().hgetall(new Buffer("hash key 2")).exec(function (err, reply) { + assert.strictEqual(1, reply.length); + assert.strictEqual("object", typeof reply[0]); + assert.strictEqual(2, Object.keys(reply[0]).length); + assert.strictEqual(true, Buffer.isBuffer(reply[0]["key 1"])); + assert.strictEqual(true, Buffer.isBuffer(reply[0]["key 2"])); + assert.strictEqual("", reply[0]["key 1"].inspect()); + assert.strictEqual("", reply[0]["key 2"].inspect()); + return done(err); + }); + }); + }); + }); + }); + }); +}); \ No newline at end of file From 9ee1e3c764a42764cc0d8a3bb6b8357a72a562b6 Mon Sep 17 00:00:00 2001 From: Ruben Bridgewater Date: Wed, 7 Oct 2015 15:01:16 +0200 Subject: [PATCH 0425/1748] Add batch benchmarks --- README.md | 84 ++++++++++++++++++++++++--------------- benchmarks/multi_bench.js | 56 +++++++++++++++++++++++--- 2 files changed, 101 insertions(+), 39 deletions(-) diff --git a/README.md b/README.md index 3f97f7df9bc..16124210923 100644 --- a/README.md +++ b/README.md @@ -493,6 +493,8 @@ Redis. The interface in `node_redis` is to return an individual `Batch` object b The only difference between .batch and .multi is that no transaction is going to be used. Be aware that the errors are - just like in multi statements - in the result. Otherwise both, errors and results could be returned at the same time. +If you fire many commands at once this is going to boost the execution speed significantly (see the benchmark section). Please remember that all commands are kept in memory until they are fired. + ## Monitor mode Redis supports the `MONITOR` command, which lets you see all commands received by the Redis server @@ -635,39 +637,55 @@ Here are results of `multi_bench.js` which is similar to `redis-benchmark` from hiredis parser (Lenovo T450s i7-5600U): - Client count: 5, node version: 2.5.0, server version: 3.0.3, parser: hiredis - PING, 1/5 min/max/avg/p95: 0/ 3/ 0.03/ 0.00 540ms total, 37037.04 ops/sec - PING, 50/5 min/max/avg/p95: 0/ 4/ 0.65/ 1.00 260ms total, 76923.08 ops/sec - SET 4B str, 1/5 min/max/avg/p95: 0/ 20/ 0.04/ 0.00 816ms total, 24509.80 ops/sec - SET 4B str, 50/5 min/max/avg/p95: 0/ 13/ 1.00/ 2.00 401ms total, 49875.31 ops/sec - SET 4B buf, 1/5 min/max/avg/p95: 0/ 4/ 0.06/ 1.00 1293ms total, 15467.90 ops/sec - SET 4B buf, 50/5 min/max/avg/p95: 0/ 5/ 2.58/ 4.00 1033ms total, 19361.08 ops/sec - GET 4B str, 1/5 min/max/avg/p95: 0/ 14/ 0.03/ 0.00 717ms total, 27894.00 ops/sec - GET 4B str, 50/5 min/max/avg/p95: 0/ 3/ 0.62/ 1.00 249ms total, 80321.29 ops/sec - GET 4B buf, 1/5 min/max/avg/p95: 0/ 6/ 0.03/ 0.00 561ms total, 35650.62 ops/sec - GET 4B buf, 50/5 min/max/avg/p95: 0/ 3/ 0.64/ 1.00 259ms total, 77220.08 ops/sec - SET 4KiB str, 1/5 min/max/avg/p95: 0/ 4/ 0.03/ 0.00 678ms total, 29498.53 ops/sec - SET 4KiB str, 50/5 min/max/avg/p95: 0/ 3/ 0.91/ 2.00 364ms total, 54945.05 ops/sec - SET 4KiB buf, 1/5 min/max/avg/p95: 0/ 20/ 0.07/ 1.00 1354ms total, 14771.05 ops/sec - SET 4KiB buf, 50/5 min/max/avg/p95: 0/ 5/ 1.86/ 3.00 744ms total, 26881.72 ops/sec - GET 4KiB str, 1/5 min/max/avg/p95: 0/ 3/ 0.03/ 0.00 575ms total, 34782.61 ops/sec - GET 4KiB str, 50/5 min/max/avg/p95: 0/ 5/ 0.82/ 2.00 327ms total, 61162.08 ops/sec - GET 4KiB buf, 1/5 min/max/avg/p95: 0/ 25/ 0.04/ 0.00 808ms total, 24752.48 ops/sec - GET 4KiB buf, 50/5 min/max/avg/p95: 0/ 4/ 0.92/ 2.00 371ms total, 53908.36 ops/sec - INCR, 1/5 min/max/avg/p95: 0/ 28/ 0.03/ 0.00 556ms total, 35971.22 ops/sec - INCR, 50/5 min/max/avg/p95: 0/ 4/ 0.67/ 1.00 269ms total, 74349.44 ops/sec - LPUSH, 1/5 min/max/avg/p95: 0/ 2/ 0.03/ 0.00 534ms total, 37453.18 ops/sec - LPUSH, 50/5 min/max/avg/p95: 0/ 2/ 0.89/ 2.00 357ms total, 56022.41 ops/sec - LRANGE 10, 1/5 min/max/avg/p95: 0/ 12/ 0.04/ 0.00 829ms total, 24125.45 ops/sec - LRANGE 10, 50/5 min/max/avg/p95: 0/ 3/ 1.04/ 2.00 415ms total, 48192.77 ops/sec - LRANGE 100, 1/5 min/max/avg/p95: 0/ 16/ 0.06/ 1.00 1212ms total, 16501.65 ops/sec - LRANGE 100, 50/5 min/max/avg/p95: 0/ 5/ 1.76/ 3.00 707ms total, 28288.54 ops/sec - SET 4MiB buf, 1/5 min/max/avg/p95: 1/ 22/ 2.66/ 4.00 1335ms total, 374.53 ops/sec - SET 4MiB buf, 50/5 min/max/avg/p95: 13/ 122/ 101.33/ 114.00 1062ms total, 470.81 ops/sec - GET 4MiB str, 1/5 min/max/avg/p95: 3/ 14/ 6.07/ 12.00 607ms total, 164.74 ops/sec - GET 4MiB str, 50/5 min/max/avg/p95: 17/ 431/ 286.75/ 418.00 686ms total, 145.77 ops/sec - GET 4MiB buf, 1/5 min/max/avg/p95: 3/ 38/ 6.83/ 12.95 684ms total, 146.20 ops/sec - GET 4MiB buf, 50/5 min/max/avg/p95: 10/ 273/ 194.07/ 253.90 495ms total, 202.02 ops/sec +Client count: 5, node version: 4.1.1, server version: 3.0.3, parser: hiredis + PING, 1/5 min/max/avg/p95: 0/ 11/ 0.03/ 0.00 1412ms total, 35410.76 ops/sec + PING, 50/5 min/max/avg/p95: 0/ 9/ 0.54/ 1.00 539ms total, 92764.38 ops/sec + PING, batch 50/5 min/max/avg/p95: 0/ 3/ 0.32/ 1.00 327ms total, 152905.20 ops/sec + SET 4B str, 1/5 min/max/avg/p95: 0/ 4/ 0.03/ 0.00 1450ms total, 34482.76 ops/sec + SET 4B str, 50/5 min/max/avg/p95: 0/ 2/ 0.55/ 1.00 548ms total, 91240.88 ops/sec + SET 4B str, batch 50/5 min/max/avg/p95: 0/ 10/ 0.36/ 1.00 362ms total, 138121.55 ops/sec + SET 4B buf, 1/5 min/max/avg/p95: 0/ 5/ 0.06/ 0.55 2838ms total, 17618.04 ops/sec + SET 4B buf, 50/5 min/max/avg/p95: 0/ 9/ 1.70/ 3.00 1699ms total, 29429.08 ops/sec + SET 4B buf, batch 50/5 min/max/avg/p95: 1/ 11/ 1.69/ 3.00 1694ms total, 29515.94 ops/sec + GET 4B str, 1/5 min/max/avg/p95: 0/ 4/ 0.03/ 0.00 1350ms total, 37037.04 ops/sec + GET 4B str, 50/5 min/max/avg/p95: 0/ 7/ 0.54/ 1.00 539ms total, 92764.38 ops/sec + GET 4B str, batch 50/5 min/max/avg/p95: 0/ 2/ 0.48/ 1.00 483ms total, 103519.67 ops/sec + GET 4B buf, 1/5 min/max/avg/p95: 0/ 9/ 0.03/ 0.00 1373ms total, 36416.61 ops/sec + GET 4B buf, 50/5 min/max/avg/p95: 0/ 2/ 0.53/ 1.00 534ms total, 93632.96 ops/sec + GET 4B buf, batch 50/5 min/max/avg/p95: 0/ 10/ 0.60/ 1.00 605ms total, 82644.63 ops/sec + SET 4KiB str, 1/5 min/max/avg/p95: 0/ 5/ 0.03/ 0.00 1790ms total, 27932.96 ops/sec + SET 4KiB str, 50/5 min/max/avg/p95: 0/ 7/ 0.80/ 2.00 798ms total, 62656.64 ops/sec + SET 4KiB str, batch 50/5 min/max/avg/p95: 0/ 10/ 0.92/ 1.00 924ms total, 54112.55 ops/sec + SET 4KiB buf, 1/5 min/max/avg/p95: 0/ 16/ 0.05/ 1.00 2687ms total, 18608.11 ops/sec + SET 4KiB buf, 50/5 min/max/avg/p95: 0/ 16/ 1.88/ 3.00 1885ms total, 26525.20 ops/sec + SET 4KiB buf, batch 50/5 min/max/avg/p95: 1/ 6/ 1.83/ 3.00 1832ms total, 27292.58 ops/sec + GET 4KiB str, 1/5 min/max/avg/p95: 0/ 7/ 0.04/ 0.00 1909ms total, 26191.72 ops/sec + GET 4KiB str, 50/5 min/max/avg/p95: 0/ 8/ 0.88/ 2.00 887ms total, 56369.79 ops/sec + GET 4KiB str, batch 50/5 min/max/avg/p95: 0/ 4/ 0.57/ 1.00 570ms total, 87719.30 ops/sec + GET 4KiB buf, 1/5 min/max/avg/p95: 0/ 7/ 0.03/ 0.00 1754ms total, 28506.27 ops/sec + GET 4KiB buf, 50/5 min/max/avg/p95: 0/ 6/ 0.72/ 1.00 717ms total, 69735.01 ops/sec + GET 4KiB buf, batch 50/5 min/max/avg/p95: 0/ 1/ 0.47/ 1.00 472ms total, 105932.20 ops/sec + INCR, 1/5 min/max/avg/p95: 0/ 8/ 0.03/ 0.00 1531ms total, 32658.39 ops/sec + INCR, 50/5 min/max/avg/p95: 0/ 5/ 0.64/ 1.00 638ms total, 78369.91 ops/sec + INCR, batch 50/5 min/max/avg/p95: 0/ 13/ 0.45/ 1.00 452ms total, 110619.47 ops/sec + LPUSH, 1/5 min/max/avg/p95: 0/ 4/ 0.03/ 0.00 1445ms total, 34602.08 ops/sec + LPUSH, 50/5 min/max/avg/p95: 0/ 9/ 0.67/ 1.00 670ms total, 74626.87 ops/sec + LPUSH, batch 50/5 min/max/avg/p95: 0/ 2/ 0.34/ 1.00 339ms total, 147492.63 ops/sec + LRANGE 10, 1/5 min/max/avg/p95: 0/ 9/ 0.03/ 0.00 1739ms total, 28752.16 ops/sec + LRANGE 10, 50/5 min/max/avg/p95: 0/ 11/ 0.76/ 2.00 759ms total, 65876.15 ops/sec + LRANGE 10, batch 50/5 min/max/avg/p95: 0/ 4/ 0.49/ 1.00 497ms total, 100603.62 ops/sec + LRANGE 100, 1/5 min/max/avg/p95: 0/ 7/ 0.06/ 1.00 3252ms total, 15375.15 ops/sec + LRANGE 100, 50/5 min/max/avg/p95: 0/ 9/ 1.90/ 3.00 1905ms total, 26246.72 ops/sec + LRANGE 100, batch 50/5 min/max/avg/p95: 1/ 5/ 1.81/ 2.00 1816ms total, 27533.04 ops/sec + SET 4MiB buf, 1/5 min/max/avg/p95: 2/ 5/ 2.32/ 3.00 1160ms total, 431.03 ops/sec + SET 4MiB buf, 50/5 min/max/avg/p95: 19/ 134/ 102.27/ 118.00 1071ms total, 466.85 ops/sec + SET 4MiB buf, batch 50/5 min/max/avg/p95: 97/ 129/ 104.90/ 129.00 1049ms total, 476.64 ops/sec + GET 4MiB str, 1/5 min/max/avg/p95: 4/ 19/ 6.59/ 11.00 660ms total, 151.52 ops/sec + GET 4MiB str, 50/5 min/max/avg/p95: 19/ 278/ 200.11/ 258.85 503ms total, 198.81 ops/sec + GET 4MiB str, batch 50/5 min/max/avg/p95: 229/ 235/ 232.00/ 235.00 465ms total, 215.05 ops/sec + GET 4MiB buf, 1/5 min/max/avg/p95: 4/ 27/ 7.11/ 13.95 713ms total, 140.25 ops/sec + GET 4MiB buf, 50/5 min/max/avg/p95: 7/ 293/ 204.74/ 269.00 518ms total, 193.05 ops/sec + GET 4MiB buf, batch 50/5 min/max/avg/p95: 219/ 261/ 240.00/ 261.00 480ms total, 208.33 ops/sec The hiredis and js parser should most of the time be on the same level. The js parser lacks speed for large responses though. Therefor the hiredis parser is the default used in node_redis. To use `hiredis`, do: diff --git a/benchmarks/multi_bench.js b/benchmarks/multi_bench.js index 6101b41dced..c00bcc3d8c3 100644 --- a/benchmarks/multi_bench.js +++ b/benchmarks/multi_bench.js @@ -3,6 +3,7 @@ var path = require('path'); var RedisProcess = require('../test/lib/redis-process'); var rp; +var client_nr = 0; var redis = require('../index'); var totalTime = 0; var metrics = require('metrics'); @@ -42,6 +43,7 @@ function Test(args) { this.commands_sent = 0; this.commands_completed = 0; this.max_pipeline = this.args.pipeline || num_requests; + this.batch_pipeline = this.args.batch || 0; this.client_options = args.client_options || client_options; this.num_requests = args.reqs || num_requests; @@ -105,7 +107,7 @@ Test.prototype.new_client = function (id) { }; Test.prototype.on_clients_ready = function () { - process.stdout.write(lpad(this.args.descr, 13) + ', ' + lpad(this.args.pipeline, 5) + '/' + this.clients_ready + ' '); + process.stdout.write(lpad(this.args.descr, 13) + ', ' + (this.args.batch ? lpad('batch ' + this.args.batch, 9) : lpad(this.args.pipeline, 9)) + '/' + this.clients_ready + ' '); this.test_start = Date.now(); this.fill_pipeline(); @@ -114,10 +116,14 @@ Test.prototype.on_clients_ready = function () { Test.prototype.fill_pipeline = function () { var pipeline = this.commands_sent - this.commands_completed; - while (this.commands_sent < this.num_requests && pipeline < this.max_pipeline) { - this.commands_sent++; - pipeline++; - this.send_next(); + if (this.batch_pipeline && this.commands_sent < this.num_requests) { + this.batch(); + } else { + while (this.commands_sent < this.num_requests && pipeline < this.max_pipeline) { + this.commands_sent++; + pipeline++; + this.send_next(); + } } if (this.commands_completed === this.num_requests) { @@ -126,6 +132,28 @@ Test.prototype.fill_pipeline = function () { } }; +Test.prototype.batch = function () { + var self = this, + cur_client = client_nr++ % this.clients.length, + start = Date.now(), + i = 0, + batch = this.clients[cur_client].batch(); + + while (i++ < this.batch_pipeline) { + this.commands_sent++; + batch[this.args.command](this.args.args); + } + + batch.exec(function (err, res) { + if (err) { + throw err; + } + self.commands_completed += res.length; + self.command_latency.update(Date.now() - start); + self.fill_pipeline(); + }); +}; + Test.prototype.stop_clients = function () { var self = this; @@ -160,7 +188,7 @@ Test.prototype.print_stats = function () { totalTime += duration; console.log('min/max/avg/p95: ' + this.command_latency.print_line() + ' ' + lpad(duration, 6) + 'ms total, ' + - lpad((this.num_requests / (duration / 1000)).toFixed(2), 8) + ' ops/sec'); + lpad((this.num_requests / (duration / 1000)).toFixed(2), 9) + ' ops/sec'); }; small_str = '1234'; @@ -172,51 +200,67 @@ very_large_buf = new Buffer(very_large_str); tests.push(new Test({descr: 'PING', command: 'ping', args: [], pipeline: 1})); tests.push(new Test({descr: 'PING', command: 'ping', args: [], pipeline: 50})); +tests.push(new Test({descr: 'PING', command: 'ping', args: [], batch: 50})); tests.push(new Test({descr: 'SET 4B str', command: 'set', args: ['foo_rand000000000000', small_str], pipeline: 1})); tests.push(new Test({descr: 'SET 4B str', command: 'set', args: ['foo_rand000000000000', small_str], pipeline: 50})); +tests.push(new Test({descr: 'SET 4B str', command: 'set', args: ['foo_rand000000000000', small_str], batch: 50})); tests.push(new Test({descr: 'SET 4B buf', command: 'set', args: ['foo_rand000000000000', small_buf], pipeline: 1})); tests.push(new Test({descr: 'SET 4B buf', command: 'set', args: ['foo_rand000000000000', small_buf], pipeline: 50})); +tests.push(new Test({descr: 'SET 4B buf', command: 'set', args: ['foo_rand000000000000', small_buf], batch: 50})); tests.push(new Test({descr: 'GET 4B str', command: 'get', args: ['foo_rand000000000000'], pipeline: 1})); tests.push(new Test({descr: 'GET 4B str', command: 'get', args: ['foo_rand000000000000'], pipeline: 50})); +tests.push(new Test({descr: 'GET 4B str', command: 'get', args: ['foo_rand000000000000'], batch: 50})); tests.push(new Test({descr: 'GET 4B buf', command: 'get', args: ['foo_rand000000000000'], pipeline: 1, client_opts: { return_buffers: true} })); tests.push(new Test({descr: 'GET 4B buf', command: 'get', args: ['foo_rand000000000000'], pipeline: 50, client_opts: { return_buffers: true} })); +tests.push(new Test({descr: 'GET 4B buf', command: 'get', args: ['foo_rand000000000000'], batch: 50, client_opts: { return_buffers: true} })); tests.push(new Test({descr: 'SET 4KiB str', command: 'set', args: ['foo_rand000000000001', large_str], pipeline: 1})); tests.push(new Test({descr: 'SET 4KiB str', command: 'set', args: ['foo_rand000000000001', large_str], pipeline: 50})); +tests.push(new Test({descr: 'SET 4KiB str', command: 'set', args: ['foo_rand000000000001', large_str], batch: 50})); tests.push(new Test({descr: 'SET 4KiB buf', command: 'set', args: ['foo_rand000000000001', large_buf], pipeline: 1})); tests.push(new Test({descr: 'SET 4KiB buf', command: 'set', args: ['foo_rand000000000001', large_buf], pipeline: 50})); +tests.push(new Test({descr: 'SET 4KiB buf', command: 'set', args: ['foo_rand000000000001', large_buf], batch: 50})); tests.push(new Test({descr: 'GET 4KiB str', command: 'get', args: ['foo_rand000000000001'], pipeline: 1})); tests.push(new Test({descr: 'GET 4KiB str', command: 'get', args: ['foo_rand000000000001'], pipeline: 50})); +tests.push(new Test({descr: 'GET 4KiB str', command: 'get', args: ['foo_rand000000000001'], batch: 50})); tests.push(new Test({descr: 'GET 4KiB buf', command: 'get', args: ['foo_rand000000000001'], pipeline: 1, client_opts: { return_buffers: true} })); tests.push(new Test({descr: 'GET 4KiB buf', command: 'get', args: ['foo_rand000000000001'], pipeline: 50, client_opts: { return_buffers: true} })); +tests.push(new Test({descr: 'GET 4KiB buf', command: 'get', args: ['foo_rand000000000001'], batch: 50, client_opts: { return_buffers: true} })); tests.push(new Test({descr: 'INCR', command: 'incr', args: ['counter_rand000000000000'], pipeline: 1})); tests.push(new Test({descr: 'INCR', command: 'incr', args: ['counter_rand000000000000'], pipeline: 50})); +tests.push(new Test({descr: 'INCR', command: 'incr', args: ['counter_rand000000000000'], batch: 50})); tests.push(new Test({descr: 'LPUSH', command: 'lpush', args: ['mylist', small_str], pipeline: 1})); tests.push(new Test({descr: 'LPUSH', command: 'lpush', args: ['mylist', small_str], pipeline: 50})); +tests.push(new Test({descr: 'LPUSH', command: 'lpush', args: ['mylist', small_str], batch: 50})); tests.push(new Test({descr: 'LRANGE 10', command: 'lrange', args: ['mylist', '0', '9'], pipeline: 1})); tests.push(new Test({descr: 'LRANGE 10', command: 'lrange', args: ['mylist', '0', '9'], pipeline: 50})); +tests.push(new Test({descr: 'LRANGE 10', command: 'lrange', args: ['mylist', '0', '9'], batch: 50})); tests.push(new Test({descr: 'LRANGE 100', command: 'lrange', args: ['mylist', '0', '99'], pipeline: 1})); tests.push(new Test({descr: 'LRANGE 100', command: 'lrange', args: ['mylist', '0', '99'], pipeline: 50})); +tests.push(new Test({descr: 'LRANGE 100', command: 'lrange', args: ['mylist', '0', '99'], batch: 50})); tests.push(new Test({descr: 'SET 4MiB buf', command: 'set', args: ['foo_rand000000000002', very_large_buf], pipeline: 1, reqs: 500})); tests.push(new Test({descr: 'SET 4MiB buf', command: 'set', args: ['foo_rand000000000002', very_large_buf], pipeline: 50, reqs: 500})); +tests.push(new Test({descr: 'SET 4MiB buf', command: 'set', args: ['foo_rand000000000002', very_large_buf], batch: 50, reqs: 500})); tests.push(new Test({descr: 'GET 4MiB str', command: 'get', args: ['foo_rand000000000002'], pipeline: 1, reqs: 100})); tests.push(new Test({descr: 'GET 4MiB str', command: 'get', args: ['foo_rand000000000002'], pipeline: 50, reqs: 100})); +tests.push(new Test({descr: 'GET 4MiB str', command: 'get', args: ['foo_rand000000000002'], batch: 50, reqs: 100})); tests.push(new Test({descr: 'GET 4MiB buf', command: 'get', args: ['foo_rand000000000002'], pipeline: 1, reqs: 100, client_opts: { return_buffers: true} })); tests.push(new Test({descr: 'GET 4MiB buf', command: 'get', args: ['foo_rand000000000002'], pipeline: 50, reqs: 100, client_opts: { return_buffers: true} })); +tests.push(new Test({descr: 'GET 4MiB buf', command: 'get', args: ['foo_rand000000000002'], batch: 50, reqs: 100, client_opts: { return_buffers: true} })); function next() { var test = tests.shift(); From 7d2bb8edec94daf1f51ec315c71d77fb7a3a8ff2 Mon Sep 17 00:00:00 2001 From: Ruben Bridgewater Date: Thu, 8 Oct 2015 02:29:07 +0200 Subject: [PATCH 0426/1748] Better pipelining Add fallback mode --- README.md | 101 ++++++++++++++++++++++++++------------------------- changelog.md | 30 ++++++++------- index.js | 63 ++++++++++++++++++-------------- 3 files changed, 104 insertions(+), 90 deletions(-) diff --git a/README.md b/README.md index 16124210923..26ebe099422 100644 --- a/README.md +++ b/README.md @@ -493,7 +493,7 @@ Redis. The interface in `node_redis` is to return an individual `Batch` object b The only difference between .batch and .multi is that no transaction is going to be used. Be aware that the errors are - just like in multi statements - in the result. Otherwise both, errors and results could be returned at the same time. -If you fire many commands at once this is going to boost the execution speed significantly (see the benchmark section). Please remember that all commands are kept in memory until they are fired. +If you fire many commands at once this is going to **boost the execution speed by up to 400%** [sic!] compared to fireing the same commands in a loop without waiting for the result! See the benchmarks for further comparison. Please remember that all commands are kept in memory until they are fired. ## Monitor mode @@ -637,55 +637,56 @@ Here are results of `multi_bench.js` which is similar to `redis-benchmark` from hiredis parser (Lenovo T450s i7-5600U): -Client count: 5, node version: 4.1.1, server version: 3.0.3, parser: hiredis - PING, 1/5 min/max/avg/p95: 0/ 11/ 0.03/ 0.00 1412ms total, 35410.76 ops/sec - PING, 50/5 min/max/avg/p95: 0/ 9/ 0.54/ 1.00 539ms total, 92764.38 ops/sec - PING, batch 50/5 min/max/avg/p95: 0/ 3/ 0.32/ 1.00 327ms total, 152905.20 ops/sec - SET 4B str, 1/5 min/max/avg/p95: 0/ 4/ 0.03/ 0.00 1450ms total, 34482.76 ops/sec - SET 4B str, 50/5 min/max/avg/p95: 0/ 2/ 0.55/ 1.00 548ms total, 91240.88 ops/sec - SET 4B str, batch 50/5 min/max/avg/p95: 0/ 10/ 0.36/ 1.00 362ms total, 138121.55 ops/sec - SET 4B buf, 1/5 min/max/avg/p95: 0/ 5/ 0.06/ 0.55 2838ms total, 17618.04 ops/sec - SET 4B buf, 50/5 min/max/avg/p95: 0/ 9/ 1.70/ 3.00 1699ms total, 29429.08 ops/sec - SET 4B buf, batch 50/5 min/max/avg/p95: 1/ 11/ 1.69/ 3.00 1694ms total, 29515.94 ops/sec - GET 4B str, 1/5 min/max/avg/p95: 0/ 4/ 0.03/ 0.00 1350ms total, 37037.04 ops/sec - GET 4B str, 50/5 min/max/avg/p95: 0/ 7/ 0.54/ 1.00 539ms total, 92764.38 ops/sec - GET 4B str, batch 50/5 min/max/avg/p95: 0/ 2/ 0.48/ 1.00 483ms total, 103519.67 ops/sec - GET 4B buf, 1/5 min/max/avg/p95: 0/ 9/ 0.03/ 0.00 1373ms total, 36416.61 ops/sec - GET 4B buf, 50/5 min/max/avg/p95: 0/ 2/ 0.53/ 1.00 534ms total, 93632.96 ops/sec - GET 4B buf, batch 50/5 min/max/avg/p95: 0/ 10/ 0.60/ 1.00 605ms total, 82644.63 ops/sec - SET 4KiB str, 1/5 min/max/avg/p95: 0/ 5/ 0.03/ 0.00 1790ms total, 27932.96 ops/sec - SET 4KiB str, 50/5 min/max/avg/p95: 0/ 7/ 0.80/ 2.00 798ms total, 62656.64 ops/sec - SET 4KiB str, batch 50/5 min/max/avg/p95: 0/ 10/ 0.92/ 1.00 924ms total, 54112.55 ops/sec - SET 4KiB buf, 1/5 min/max/avg/p95: 0/ 16/ 0.05/ 1.00 2687ms total, 18608.11 ops/sec - SET 4KiB buf, 50/5 min/max/avg/p95: 0/ 16/ 1.88/ 3.00 1885ms total, 26525.20 ops/sec - SET 4KiB buf, batch 50/5 min/max/avg/p95: 1/ 6/ 1.83/ 3.00 1832ms total, 27292.58 ops/sec - GET 4KiB str, 1/5 min/max/avg/p95: 0/ 7/ 0.04/ 0.00 1909ms total, 26191.72 ops/sec - GET 4KiB str, 50/5 min/max/avg/p95: 0/ 8/ 0.88/ 2.00 887ms total, 56369.79 ops/sec - GET 4KiB str, batch 50/5 min/max/avg/p95: 0/ 4/ 0.57/ 1.00 570ms total, 87719.30 ops/sec - GET 4KiB buf, 1/5 min/max/avg/p95: 0/ 7/ 0.03/ 0.00 1754ms total, 28506.27 ops/sec - GET 4KiB buf, 50/5 min/max/avg/p95: 0/ 6/ 0.72/ 1.00 717ms total, 69735.01 ops/sec - GET 4KiB buf, batch 50/5 min/max/avg/p95: 0/ 1/ 0.47/ 1.00 472ms total, 105932.20 ops/sec - INCR, 1/5 min/max/avg/p95: 0/ 8/ 0.03/ 0.00 1531ms total, 32658.39 ops/sec - INCR, 50/5 min/max/avg/p95: 0/ 5/ 0.64/ 1.00 638ms total, 78369.91 ops/sec - INCR, batch 50/5 min/max/avg/p95: 0/ 13/ 0.45/ 1.00 452ms total, 110619.47 ops/sec - LPUSH, 1/5 min/max/avg/p95: 0/ 4/ 0.03/ 0.00 1445ms total, 34602.08 ops/sec - LPUSH, 50/5 min/max/avg/p95: 0/ 9/ 0.67/ 1.00 670ms total, 74626.87 ops/sec - LPUSH, batch 50/5 min/max/avg/p95: 0/ 2/ 0.34/ 1.00 339ms total, 147492.63 ops/sec - LRANGE 10, 1/5 min/max/avg/p95: 0/ 9/ 0.03/ 0.00 1739ms total, 28752.16 ops/sec - LRANGE 10, 50/5 min/max/avg/p95: 0/ 11/ 0.76/ 2.00 759ms total, 65876.15 ops/sec - LRANGE 10, batch 50/5 min/max/avg/p95: 0/ 4/ 0.49/ 1.00 497ms total, 100603.62 ops/sec - LRANGE 100, 1/5 min/max/avg/p95: 0/ 7/ 0.06/ 1.00 3252ms total, 15375.15 ops/sec - LRANGE 100, 50/5 min/max/avg/p95: 0/ 9/ 1.90/ 3.00 1905ms total, 26246.72 ops/sec - LRANGE 100, batch 50/5 min/max/avg/p95: 1/ 5/ 1.81/ 2.00 1816ms total, 27533.04 ops/sec - SET 4MiB buf, 1/5 min/max/avg/p95: 2/ 5/ 2.32/ 3.00 1160ms total, 431.03 ops/sec - SET 4MiB buf, 50/5 min/max/avg/p95: 19/ 134/ 102.27/ 118.00 1071ms total, 466.85 ops/sec - SET 4MiB buf, batch 50/5 min/max/avg/p95: 97/ 129/ 104.90/ 129.00 1049ms total, 476.64 ops/sec - GET 4MiB str, 1/5 min/max/avg/p95: 4/ 19/ 6.59/ 11.00 660ms total, 151.52 ops/sec - GET 4MiB str, 50/5 min/max/avg/p95: 19/ 278/ 200.11/ 258.85 503ms total, 198.81 ops/sec - GET 4MiB str, batch 50/5 min/max/avg/p95: 229/ 235/ 232.00/ 235.00 465ms total, 215.05 ops/sec - GET 4MiB buf, 1/5 min/max/avg/p95: 4/ 27/ 7.11/ 13.95 713ms total, 140.25 ops/sec - GET 4MiB buf, 50/5 min/max/avg/p95: 7/ 293/ 204.74/ 269.00 518ms total, 193.05 ops/sec - GET 4MiB buf, batch 50/5 min/max/avg/p95: 219/ 261/ 240.00/ 261.00 480ms total, 208.33 ops/sec +Client count: 5, node version: 4.1.2, server version: 3.0.3, parser: hiredis + PING, 1/5 min/max/avg/p95: 0/ 5/ 0.03/ 0.00 1537ms total, 32530.90 ops/sec + PING, 50/5 min/max/avg/p95: 0/ 4/ 0.49/ 1.00 491ms total, 101832.99 ops/sec + PING, batch 50/5 min/max/avg/p95: 0/ 2/ 0.17/ 1.00 178ms total, 280898.88 ops/sec + SET 4B str, 1/5 min/max/avg/p95: 0/ 2/ 0.03/ 0.00 1400ms total, 35714.29 ops/sec + SET 4B str, 50/5 min/max/avg/p95: 0/ 3/ 0.61/ 1.00 610ms total, 81967.21 ops/sec + SET 4B str, batch 50/5 min/max/avg/p95: 0/ 1/ 0.19/ 1.00 198ms total, 252525.25 ops/sec + SET 4B buf, 1/5 min/max/avg/p95: 0/ 3/ 0.05/ 0.00 2349ms total, 21285.65 ops/sec + SET 4B buf, 50/5 min/max/avg/p95: 0/ 5/ 1.63/ 3.00 1632ms total, 30637.25 ops/sec + SET 4B buf, batch 50/5 min/max/avg/p95: 0/ 1/ 0.37/ 1.00 366ms total, 136612.02 ops/sec + GET 4B str, 1/5 min/max/avg/p95: 0/ 3/ 0.03/ 0.00 1348ms total, 37091.99 ops/sec + GET 4B str, 50/5 min/max/avg/p95: 0/ 3/ 0.51/ 1.00 513ms total, 97465.89 ops/sec + GET 4B str, batch 50/5 min/max/avg/p95: 0/ 1/ 0.18/ 1.00 177ms total, 282485.88 ops/sec + GET 4B buf, 1/5 min/max/avg/p95: 0/ 3/ 0.03/ 0.00 1336ms total, 37425.15 ops/sec + GET 4B buf, 50/5 min/max/avg/p95: 0/ 4/ 0.52/ 1.00 525ms total, 95238.10 ops/sec + GET 4B buf, batch 50/5 min/max/avg/p95: 0/ 1/ 0.18/ 1.00 177ms total, 282485.88 ops/sec + SET 4KiB str, 1/5 min/max/avg/p95: 0/ 2/ 0.03/ 0.00 1674ms total, 29868.58 ops/sec + SET 4KiB str, 50/5 min/max/avg/p95: 0/ 3/ 0.77/ 1.00 775ms total, 64516.13 ops/sec + SET 4KiB str, batch 50/5 min/max/avg/p95: 0/ 3/ 0.50/ 1.00 500ms total, 100000.00 ops/sec + SET 4KiB buf, 1/5 min/max/avg/p95: 0/ 2/ 0.05/ 0.00 2410ms total, 20746.89 ops/sec + SET 4KiB buf, 50/5 min/max/avg/p95: 0/ 5/ 1.64/ 3.00 1643ms total, 30432.14 ops/sec + SET 4KiB buf, batch 50/5 min/max/avg/p95: 0/ 1/ 0.41/ 1.00 409ms total, 122249.39 ops/sec + GET 4KiB str, 1/5 min/max/avg/p95: 0/ 2/ 0.03/ 0.00 1422ms total, 35161.74 ops/sec + GET 4KiB str, 50/5 min/max/avg/p95: 0/ 4/ 0.68/ 1.00 680ms total, 73529.41 ops/sec + GET 4KiB str, batch 50/5 min/max/avg/p95: 0/ 2/ 0.39/ 1.00 391ms total, 127877.24 ops/sec + GET 4KiB buf, 1/5 min/max/avg/p95: 0/ 1/ 0.03/ 0.00 1420ms total, 35211.27 ops/sec + GET 4KiB buf, 50/5 min/max/avg/p95: 0/ 4/ 0.68/ 1.00 681ms total, 73421.44 ops/sec + GET 4KiB buf, batch 50/5 min/max/avg/p95: 0/ 2/ 0.39/ 1.00 387ms total, 129198.97 ops/sec + INCR, 1/5 min/max/avg/p95: 0/ 2/ 0.03/ 0.00 1334ms total, 37481.26 ops/sec + INCR, 50/5 min/max/avg/p95: 0/ 4/ 0.51/ 1.00 513ms total, 97465.89 ops/sec + INCR, batch 50/5 min/max/avg/p95: 0/ 1/ 0.18/ 1.00 179ms total, 279329.61 ops/sec + LPUSH, 1/5 min/max/avg/p95: 0/ 2/ 0.03/ 0.00 1351ms total, 37009.62 ops/sec + LPUSH, 50/5 min/max/avg/p95: 0/ 3/ 0.52/ 1.00 521ms total, 95969.29 ops/sec + LPUSH, batch 50/5 min/max/avg/p95: 0/ 2/ 0.20/ 1.00 200ms total, 250000.00 ops/sec + LRANGE 10, 1/5 min/max/avg/p95: 0/ 1/ 0.03/ 0.00 1562ms total, 32010.24 ops/sec + LRANGE 10, 50/5 min/max/avg/p95: 0/ 4/ 0.69/ 1.00 690ms total, 72463.77 ops/sec + LRANGE 10, batch 50/5 min/max/avg/p95: 0/ 2/ 0.39/ 1.00 393ms total, 127226.46 ops/sec + LRANGE 100, 1/5 min/max/avg/p95: 0/ 3/ 0.06/ 1.00 3009ms total, 16616.82 ops/sec + LRANGE 100, 50/5 min/max/avg/p95: 0/ 5/ 1.85/ 3.00 1850ms total, 27027.03 ops/sec + LRANGE 100, batch 50/5 min/max/avg/p95: 2/ 4/ 2.15/ 3.00 2153ms total, 23223.41 ops/sec + SET 4MiB buf, 1/5 min/max/avg/p95: 1/ 5/ 1.91/ 3.00 957ms total, 522.47 ops/sec + SET 4MiB buf, 50/5 min/max/avg/p95: 13/ 109/ 94.20/ 102.00 987ms total, 506.59 ops/sec + SET 4MiB buf, batch 50/5 min/max/avg/p95: 90/ 107/ 93.10/ 107.00 931ms total, 537.06 ops/sec + GET 4MiB str, 1/5 min/max/avg/p95: 4/ 16/ 5.97/ 10.00 598ms total, 167.22 ops/sec + GET 4MiB str, 50/5 min/max/avg/p95: 10/ 249/ 179.47/ 231.90 454ms total, 220.26 ops/sec + GET 4MiB str, batch 50/5 min/max/avg/p95: 215/ 226/ 220.50/ 226.00 441ms total, 226.76 ops/sec + GET 4MiB buf, 1/5 min/max/avg/p95: 3/ 26/ 6.55/ 11.95 658ms total, 151.98 ops/sec + GET 4MiB buf, 50/5 min/max/avg/p95: 11/ 265/ 186.73/ 241.90 469ms total, 213.22 ops/sec + GET 4MiB buf, batch 50/5 min/max/avg/p95: 226/ 247/ 236.50/ 247.00 473ms total, 211.42 ops/sec +End of tests. Total time elapsed: 44952 ms The hiredis and js parser should most of the time be on the same level. The js parser lacks speed for large responses though. Therefor the hiredis parser is the default used in node_redis. To use `hiredis`, do: diff --git a/changelog.md b/changelog.md index 23efe9e2763..4b3ca523032 100644 --- a/changelog.md +++ b/changelog.md @@ -1,28 +1,32 @@ Changelog ========= -## v.2.2.0 - 07, 2015 - The peregrino falcon +## v.2.2.0 - 08, 2015 - The peregrino falcon + +The peregrino falcon is the fasted bird on earth and this is what this release is all about: We increased performance for heavy usage by up to **400%** [sic!] and increased overall performance for any command as well. Please check the benchmarks in the [README.md](README.md) for further details. Features -- Added disable_resubscribing option to prevent a client from resubscribing after reconnecting (@BridgeAR) -- Added rename_commands options to handle renamed commands from the redis config (@digmxl & @BridgeAR) -- Increased performance (@BridgeAR) - - exchanging built in queue with [Petka Antonov's](@petkaantonov) [double-ended queue](https://github.com/petkaantonov/deque) +- Added rename_commands options to handle renamed commands from the redis config ([@digmxl](https://github.com/digmxl) & [@BridgeAR](https://github.com/BridgeAR)) +- Added disable_resubscribing option to prevent a client from resubscribing after reconnecting ([@BridgeAR](https://github.com/BridgeAR)) +- Increased performance ([@BridgeAR](https://github.com/BridgeAR)) + - exchanging built in queue with [@petkaantonov](https://github.com/petkaantonov)'s [double-ended queue](https://github.com/petkaantonov/deque) - prevent polymorphism - optimize statements -- Added .batch command, similar to multi but without transaction (@BridgeAR) -- Improved pipelining to minimize the [RTT](http://redis.io/topics/pipelining) further (@BridgeAR) +- Added *.batch* command, similar to .multi but without transaction ([@BridgeAR](https://github.com/BridgeAR)) +- Improved pipelining to minimize the [RTT](http://redis.io/topics/pipelining) further ([@BridgeAR](https://github.com/BridgeAR)) + +Bugfixes -This release is mainly focusing on further speed improvements and we can proudly say that node_redis is very likely outperforming any other node redis client. +- Fix a javascript parser regression introduced in 2.0 that could result in timeouts on high load. ([@BridgeAR](https://github.com/BridgeAR)) +- Fixed should_buffer boolean for .exec, .select and .auth commands not being returned ([@BridgeAR](https://github.com/BridgeAR)) -If you do not rely on transactions but want to reduze the RTT you can use .batch from now on. It'll behave just the same as .multi but it does not have any transaction and therefor won't roll back any failed commands. -Both .multi and .batch are from now on going to fire the commands in bulk without doing any other operation in between. +If you do not rely on transactions but want to reduce the RTT you can use .batch from now on. It'll behave just the same as .multi but it does not have any transaction and therefor won't roll back any failed commands.
+Both .multi and .batch are from now on going to cache the commands and release them while calling .exec. -Bugfixes +Please consider using .batch instead of looping through a lot of commands one by one. This will significantly improve your performance. -- Fix a javascript parser regression introduced in 2.0 that could result in timeouts on high load. (@BridgeAR) -- Fixed should_buffer boolean for .exec, .select and .auth commands not being returned (@BridgeAR) +To conclude: we can proudly say that node_redis is very likely outperforming any other node redis client. ## v2.1.0 - Oct 02, 2015 diff --git a/index.js b/index.js index 1e1691071a9..64b31257ae3 100644 --- a/index.js +++ b/index.js @@ -35,6 +35,13 @@ parsers.push(require('./lib/parsers/javascript')); function RedisClient(stream, options) { options = options || {}; + if (!stream.cork) { + stream.cork = function noop() {}; + stream.uncork = function noop() {}; + stream.__write = stream.write; + stream.write = this.writeStream.bind(this); + } + this.stream = stream; this.options = options; @@ -650,26 +657,6 @@ RedisClient.prototype.return_reply = function (reply) { } }; -RedisClient.prototype.writeStream = function (data) { - var stream = this.stream; - var nr = 0; - - // Do not use a pipeline - if (this.pipeline === 0) { - return !stream.write(data); - } - this.pipeline--; - this.pipeline_queue.push(data); - if (this.pipeline === 0) { - var len = this.pipeline_queue.length; - while (len--) { - nr += !stream.write(this.pipeline_queue.shift()); - } - return !nr; - } - return true; -}; - RedisClient.prototype.send_command = function (command, args, callback) { var arg, command_obj, i, err, stream = this.stream, @@ -775,21 +762,21 @@ RedisClient.prototype.send_command = function (command, args, callback) { command_str += '$' + Buffer.byteLength(arg) + '\r\n' + arg + '\r\n'; } debug('Send ' + this.address + ' id ' + this.connection_id + ': ' + command_str); - buffered_writes += !this.writeStream(command_str); + buffered_writes += !stream.write(command_str); } else { debug('Send command (' + command_str + ') has Buffer arguments'); - buffered_writes += !this.writeStream(command_str); + buffered_writes += !stream.write(command_str); for (i = 0; i < args.length; i += 1) { arg = args[i]; if (Buffer.isBuffer(arg)) { if (arg.length === 0) { debug('send_command: using empty string for 0 length buffer'); - buffered_writes += !this.writeStream('$0\r\n\r\n'); + buffered_writes += !stream.write('$0\r\n\r\n'); } else { - buffered_writes += !this.writeStream('$' + arg.length + '\r\n'); - buffered_writes += !this.writeStream(arg); - buffered_writes += !this.writeStream('\r\n'); + buffered_writes += !stream.write('$' + arg.length + '\r\n'); + buffered_writes += !stream.write(arg); + buffered_writes += !stream.write('\r\n'); debug('send_command: buffer send ' + arg.length + ' bytes'); } } else { @@ -797,7 +784,7 @@ RedisClient.prototype.send_command = function (command, args, callback) { arg = String(arg); } debug('send_command: string send ' + Buffer.byteLength(arg) + ' bytes: ' + arg); - buffered_writes += !this.writeStream('$' + Buffer.byteLength(arg) + '\r\n' + arg + '\r\n'); + buffered_writes += !stream.write('$' + Buffer.byteLength(arg) + '\r\n' + arg + '\r\n'); } } } @@ -808,6 +795,25 @@ RedisClient.prototype.send_command = function (command, args, callback) { return !this.should_buffer; }; +RedisClient.prototype.writeStream = function (data) { + var nr = 0; + + // Do not use a pipeline + if (this.pipeline === 0) { + return !this.stream.__write(data); + } + this.pipeline--; + this.pipeline_queue.push(data); + if (this.pipeline === 0) { + var len = this.pipeline_queue.length; + while (len--) { + nr += !this.stream.__write(this.pipeline_queue.shift()); + } + return !nr; + } + return true; +}; + RedisClient.prototype.pub_sub_command = function (command_obj) { var i, key, command, args; @@ -862,6 +868,7 @@ RedisClient.prototype.end = function (flush) { }; function Multi(client, args, transaction) { + client.stream.cork(); this._client = client; this.queue = []; if (transaction) { @@ -1091,6 +1098,7 @@ Multi.prototype.exec_transaction = function (callback) { this.send_command(command, args, index, cb); } + this._client.stream.uncork(); return this._client.send_command('exec', [], function(err, replies) { self.execute_callback(err, replies); }); @@ -1198,6 +1206,7 @@ Multi.prototype.exec = Multi.prototype.EXEC = function (callback) { this._client.send_command(command, args, cb); index++; } + this._client.stream.uncork(); return this._client.should_buffer; }; From ed2fc954442707f58fb9857df6435392b4f3c367 Mon Sep 17 00:00:00 2001 From: Ruben Bridgewater Date: Sat, 10 Oct 2015 03:30:53 +0200 Subject: [PATCH 0427/1748] Fix should_buffer return values and empty .batch and .auth return value being sync Fix test --- README.md | 11 ++++++++--- changelog.md | 2 +- index.js | 23 ++++++++++++++--------- test/auth.spec.js | 3 +++ test/{commands => }/batch.spec.js | 22 +++++++++++++++++----- test/commands/multi.spec.js | 17 ++++++++++++++--- 6 files changed, 57 insertions(+), 21 deletions(-) rename test/{commands => }/batch.spec.js (95%) diff --git a/README.md b/README.md index 26ebe099422..9d870b05ccc 100644 --- a/README.md +++ b/README.md @@ -152,8 +152,13 @@ So please attach the error listener to node_redis. ### "drain" `client` will emit `drain` when the TCP connection to the Redis server has been buffering, but is now -writable. This event can be used to stream commands in to Redis and adapt to backpressure. Right now, -you need to check `client.command_queue.length` to decide when to reduce your send rate and resume sending commands when you get `drain`. +writable. This event can be used to stream commands in to Redis and adapt to backpressure. + +All commands return a boolean if the stream had to buffer or not. If false is returned the stream had to buffer. +That way you can decide when to reduce your send rate and resume sending commands when you get `drain`. + +You can manually control the low water and high water marks by passing ommand_queue_high_water` and `command_queue_low_water` to the client options. +Check the [Node.js streams API](https://nodejs.org/api/stream.html) for further info. ### "idle" @@ -689,7 +694,7 @@ Client count: 5, node version: 4.1.2, server version: 3.0.3, parser: hiredis End of tests. Total time elapsed: 44952 ms The hiredis and js parser should most of the time be on the same level. The js parser lacks speed for large responses though. -Therefor the hiredis parser is the default used in node_redis. To use `hiredis`, do: +Therefor the hiredis parser is the default used in node_redis and we recommend using the hiredis parser. To use `hiredis`, do: npm install hiredis redis diff --git a/changelog.md b/changelog.md index 4b3ca523032..ce8da25d4a0 100644 --- a/changelog.md +++ b/changelog.md @@ -19,7 +19,7 @@ Features Bugfixes - Fix a javascript parser regression introduced in 2.0 that could result in timeouts on high load. ([@BridgeAR](https://github.com/BridgeAR)) -- Fixed should_buffer boolean for .exec, .select and .auth commands not being returned ([@BridgeAR](https://github.com/BridgeAR)) +- Fixed should_buffer boolean for .exec, .select and .auth commands not being returned and fix a couple special conditions ([@BridgeAR](https://github.com/BridgeAR)) If you do not rely on transactions but want to reduce the RTT you can use .batch from now on. It'll behave just the same as .multi but it does not have any transaction and therefor won't roll back any failed commands.
Both .multi and .batch are from now on going to cache the commands and release them while calling .exec. diff --git a/index.js b/index.js index 64b31257ae3..f661167f07c 100644 --- a/index.js +++ b/index.js @@ -690,7 +690,8 @@ RedisClient.prototype.send_command = function (command, args, callback) { } else { this.emit('error', err); } - return false; + // Singal no buffering + return true; } } @@ -725,7 +726,7 @@ RedisClient.prototype.send_command = function (command, args, callback) { this.offline_queue.push(command_obj); this.should_buffer = true; } - // Return false to signal no buffering + // Return false to signal buffering return false; } @@ -739,7 +740,7 @@ RedisClient.prototype.send_command = function (command, args, callback) { err = new Error('Connection in subscriber mode, only subscriber commands may be used'); err.command = command.toUpperCase(); this.emit('error', err); - return false; + return true; } this.command_queue.push(command_obj); this.commands_sent += 1; @@ -800,7 +801,7 @@ RedisClient.prototype.writeStream = function (data) { // Do not use a pipeline if (this.pipeline === 0) { - return !this.stream.__write(data); + return this.stream.__write(data); } this.pipeline--; this.pipeline_queue.push(data); @@ -975,11 +976,13 @@ RedisClient.prototype.auth = RedisClient.prototype.AUTH = function (pass, callba var err = new Error('The password has to be of type "string"'); err.command = 'AUTH'; if (callback) { - callback(err); + setImmediate(function () { + callback(err); + }); } else { this.emit('error', err); } - return false; + return true; } this.auth_pass = pass; debug('Saving auth as ' + this.auth_pass); @@ -988,7 +991,7 @@ RedisClient.prototype.auth = RedisClient.prototype.AUTH = function (pass, callba return this.send_command('auth', [this.auth_pass], callback); } this.auth_callback = callback; - return false; + return true; }; RedisClient.prototype.hmset = RedisClient.prototype.HMSET = function (key, args, callback) { @@ -1180,9 +1183,11 @@ Multi.prototype.exec = Multi.prototype.EXEC = function (callback) { var args; if (len === 0) { if (callback) { - callback(null, []); + setImmediate(function () { + callback(null, []); + }); } - return false; + return true; } this.results = new Array(len); this._client.pipeline = len; diff --git a/test/auth.spec.js b/test/auth.spec.js index b4646c1fdc6..ef34b82ccbc 100644 --- a/test/auth.spec.js +++ b/test/auth.spec.js @@ -128,12 +128,15 @@ describe("client authentication", function () { if (helper.redisProcess().spawnFailed()) this.skip(); client = redis.createClient.apply(redis.createClient, args); + var async = true; client.auth(undefined, function(err, res) { assert.strictEqual(err.message, 'The password has to be of type "string"'); assert.strictEqual(err.command, 'AUTH'); assert.strictEqual(res, undefined); + async = false; done(); }); + assert(async); }); it('should emit an error if the password is not of type string and no callback has been provided', function (done) { diff --git a/test/commands/batch.spec.js b/test/batch.spec.js similarity index 95% rename from test/commands/batch.spec.js rename to test/batch.spec.js index 0e009c5b21c..5164d897e9b 100644 --- a/test/commands/batch.spec.js +++ b/test/batch.spec.js @@ -1,8 +1,8 @@ 'use strict'; var assert = require('assert'); -var config = require("../lib/config"); -var helper = require('../helper'); +var config = require("./lib/config"); +var helper = require('./helper'); var redis = config.redis; var uuid = require('uuid'); @@ -52,7 +52,7 @@ describe("The 'batch' method", function () { beforeEach(function (done) { client = redis.createClient.apply(redis.createClient, args); - client.once("connect", function () { + client.once("ready", function () { client.flushdb(function (err) { return done(err); }); @@ -63,6 +63,19 @@ describe("The 'batch' method", function () { client.end(); }); + it("returns an empty result array", function (done) { + var batch = client.batch(); + var async = true; + var notBuffering = batch.exec(function (err, res) { + assert.strictEqual(err, null); + assert.strictEqual(res.length, 0); + async = false; + done(); + }); + assert(async); + assert.strictEqual(notBuffering, true); + }); + it('fail individually when one command fails using chaining notation', function (done) { var batch1, batch2; batch1 = client.batch(); @@ -216,8 +229,7 @@ describe("The 'batch' method", function () { it('runs a batch without any further commands and without callback', function() { var buffering = client.batch().exec(); - assert(typeof buffering === 'boolean'); - assert(!buffering); + assert.strictEqual(buffering, true); }); it('allows batchple operations to be performed using a chaining API', function (done) { diff --git a/test/commands/multi.spec.js b/test/commands/multi.spec.js index 99e43d472f1..73aaa46bf43 100644 --- a/test/commands/multi.spec.js +++ b/test/commands/multi.spec.js @@ -32,11 +32,12 @@ describe("The 'multi' method", function () { }); it("reports an error", function (done) { - client.multi(); - client.exec(function (err, res) { + var multi = client.multi(); + var notBuffering = multi.exec(function (err, res) { assert(err.message.match(/The connection has already been closed/)); done(); }); + assert.strictEqual(notBuffering, false); }); it("reports an error if promisified", function () { @@ -51,7 +52,7 @@ describe("The 'multi' method", function () { beforeEach(function (done) { client = redis.createClient.apply(redis.createClient, args); - client.once("connect", function () { + client.once("ready", function () { client.flushdb(function (err) { return done(err); }); @@ -62,6 +63,16 @@ describe("The 'multi' method", function () { client.end(); }); + it("returns an empty result array", function (done) { + var multi = client.multi(); + var notBuffering = multi.exec(function (err, res) { + assert.strictEqual(err, null); + assert.strictEqual(res.length, 0); + done(); + }); + assert.strictEqual(notBuffering, true); + }); + it('roles back a transaction when one command in a sequence of commands fails', function (done) { var multi1, multi2; var expected = helper.serverVersionAtLeast(client, [2, 6, 5]) ? helper.isError() : function () {}; From f0e28bf0f77070feb38e3c6e9326c6ee721cd378 Mon Sep 17 00:00:00 2001 From: Ruben Bridgewater Date: Sat, 10 Oct 2015 22:58:58 +0200 Subject: [PATCH 0428/1748] Minor improvement for .batch and .multi for small values Improve the speed by round about 5% for small values Add Multi.exec_atomic --- README.md | 105 +++++++++++++++++++----------------- benchmarks/multi_bench.js | 34 ++++++------ changelog.md | 28 +++++++++- index.js | 100 +++++++++++++++++----------------- test/commands/multi.spec.js | 35 ++++++++++++ test/rename.spec.js | 49 ++++++++++------- 6 files changed, 216 insertions(+), 135 deletions(-) diff --git a/README.md b/README.md index 9d870b05ccc..69446407e8d 100644 --- a/README.md +++ b/README.md @@ -491,8 +491,15 @@ client.multi([ console.log(replies); }); ``` + +### Multi.exec_atomic( callback ) + +Identical to Multi.exec but with the difference that executing a single command will not use transactions. + ## client.batch([commands]) +Identical to .multi without transactions. This is recommended if you want to execute many commands at once but don't have to rely on transactions. + `BATCH` commands are queued up until an `EXEC` is issued, and then all commands are run atomically by Redis. The interface in `node_redis` is to return an individual `Batch` object by calling `client.batch()`. The only difference between .batch and .multi is that no transaction is going to be used. @@ -643,55 +650,55 @@ Here are results of `multi_bench.js` which is similar to `redis-benchmark` from hiredis parser (Lenovo T450s i7-5600U): Client count: 5, node version: 4.1.2, server version: 3.0.3, parser: hiredis - PING, 1/5 min/max/avg/p95: 0/ 5/ 0.03/ 0.00 1537ms total, 32530.90 ops/sec - PING, 50/5 min/max/avg/p95: 0/ 4/ 0.49/ 1.00 491ms total, 101832.99 ops/sec - PING, batch 50/5 min/max/avg/p95: 0/ 2/ 0.17/ 1.00 178ms total, 280898.88 ops/sec - SET 4B str, 1/5 min/max/avg/p95: 0/ 2/ 0.03/ 0.00 1400ms total, 35714.29 ops/sec - SET 4B str, 50/5 min/max/avg/p95: 0/ 3/ 0.61/ 1.00 610ms total, 81967.21 ops/sec - SET 4B str, batch 50/5 min/max/avg/p95: 0/ 1/ 0.19/ 1.00 198ms total, 252525.25 ops/sec - SET 4B buf, 1/5 min/max/avg/p95: 0/ 3/ 0.05/ 0.00 2349ms total, 21285.65 ops/sec - SET 4B buf, 50/5 min/max/avg/p95: 0/ 5/ 1.63/ 3.00 1632ms total, 30637.25 ops/sec - SET 4B buf, batch 50/5 min/max/avg/p95: 0/ 1/ 0.37/ 1.00 366ms total, 136612.02 ops/sec - GET 4B str, 1/5 min/max/avg/p95: 0/ 3/ 0.03/ 0.00 1348ms total, 37091.99 ops/sec - GET 4B str, 50/5 min/max/avg/p95: 0/ 3/ 0.51/ 1.00 513ms total, 97465.89 ops/sec - GET 4B str, batch 50/5 min/max/avg/p95: 0/ 1/ 0.18/ 1.00 177ms total, 282485.88 ops/sec - GET 4B buf, 1/5 min/max/avg/p95: 0/ 3/ 0.03/ 0.00 1336ms total, 37425.15 ops/sec - GET 4B buf, 50/5 min/max/avg/p95: 0/ 4/ 0.52/ 1.00 525ms total, 95238.10 ops/sec - GET 4B buf, batch 50/5 min/max/avg/p95: 0/ 1/ 0.18/ 1.00 177ms total, 282485.88 ops/sec - SET 4KiB str, 1/5 min/max/avg/p95: 0/ 2/ 0.03/ 0.00 1674ms total, 29868.58 ops/sec - SET 4KiB str, 50/5 min/max/avg/p95: 0/ 3/ 0.77/ 1.00 775ms total, 64516.13 ops/sec - SET 4KiB str, batch 50/5 min/max/avg/p95: 0/ 3/ 0.50/ 1.00 500ms total, 100000.00 ops/sec - SET 4KiB buf, 1/5 min/max/avg/p95: 0/ 2/ 0.05/ 0.00 2410ms total, 20746.89 ops/sec - SET 4KiB buf, 50/5 min/max/avg/p95: 0/ 5/ 1.64/ 3.00 1643ms total, 30432.14 ops/sec - SET 4KiB buf, batch 50/5 min/max/avg/p95: 0/ 1/ 0.41/ 1.00 409ms total, 122249.39 ops/sec - GET 4KiB str, 1/5 min/max/avg/p95: 0/ 2/ 0.03/ 0.00 1422ms total, 35161.74 ops/sec - GET 4KiB str, 50/5 min/max/avg/p95: 0/ 4/ 0.68/ 1.00 680ms total, 73529.41 ops/sec - GET 4KiB str, batch 50/5 min/max/avg/p95: 0/ 2/ 0.39/ 1.00 391ms total, 127877.24 ops/sec - GET 4KiB buf, 1/5 min/max/avg/p95: 0/ 1/ 0.03/ 0.00 1420ms total, 35211.27 ops/sec - GET 4KiB buf, 50/5 min/max/avg/p95: 0/ 4/ 0.68/ 1.00 681ms total, 73421.44 ops/sec - GET 4KiB buf, batch 50/5 min/max/avg/p95: 0/ 2/ 0.39/ 1.00 387ms total, 129198.97 ops/sec - INCR, 1/5 min/max/avg/p95: 0/ 2/ 0.03/ 0.00 1334ms total, 37481.26 ops/sec - INCR, 50/5 min/max/avg/p95: 0/ 4/ 0.51/ 1.00 513ms total, 97465.89 ops/sec - INCR, batch 50/5 min/max/avg/p95: 0/ 1/ 0.18/ 1.00 179ms total, 279329.61 ops/sec - LPUSH, 1/5 min/max/avg/p95: 0/ 2/ 0.03/ 0.00 1351ms total, 37009.62 ops/sec - LPUSH, 50/5 min/max/avg/p95: 0/ 3/ 0.52/ 1.00 521ms total, 95969.29 ops/sec - LPUSH, batch 50/5 min/max/avg/p95: 0/ 2/ 0.20/ 1.00 200ms total, 250000.00 ops/sec - LRANGE 10, 1/5 min/max/avg/p95: 0/ 1/ 0.03/ 0.00 1562ms total, 32010.24 ops/sec - LRANGE 10, 50/5 min/max/avg/p95: 0/ 4/ 0.69/ 1.00 690ms total, 72463.77 ops/sec - LRANGE 10, batch 50/5 min/max/avg/p95: 0/ 2/ 0.39/ 1.00 393ms total, 127226.46 ops/sec - LRANGE 100, 1/5 min/max/avg/p95: 0/ 3/ 0.06/ 1.00 3009ms total, 16616.82 ops/sec - LRANGE 100, 50/5 min/max/avg/p95: 0/ 5/ 1.85/ 3.00 1850ms total, 27027.03 ops/sec - LRANGE 100, batch 50/5 min/max/avg/p95: 2/ 4/ 2.15/ 3.00 2153ms total, 23223.41 ops/sec - SET 4MiB buf, 1/5 min/max/avg/p95: 1/ 5/ 1.91/ 3.00 957ms total, 522.47 ops/sec - SET 4MiB buf, 50/5 min/max/avg/p95: 13/ 109/ 94.20/ 102.00 987ms total, 506.59 ops/sec - SET 4MiB buf, batch 50/5 min/max/avg/p95: 90/ 107/ 93.10/ 107.00 931ms total, 537.06 ops/sec - GET 4MiB str, 1/5 min/max/avg/p95: 4/ 16/ 5.97/ 10.00 598ms total, 167.22 ops/sec - GET 4MiB str, 50/5 min/max/avg/p95: 10/ 249/ 179.47/ 231.90 454ms total, 220.26 ops/sec - GET 4MiB str, batch 50/5 min/max/avg/p95: 215/ 226/ 220.50/ 226.00 441ms total, 226.76 ops/sec - GET 4MiB buf, 1/5 min/max/avg/p95: 3/ 26/ 6.55/ 11.95 658ms total, 151.98 ops/sec - GET 4MiB buf, 50/5 min/max/avg/p95: 11/ 265/ 186.73/ 241.90 469ms total, 213.22 ops/sec - GET 4MiB buf, batch 50/5 min/max/avg/p95: 226/ 247/ 236.50/ 247.00 473ms total, 211.42 ops/sec -End of tests. Total time elapsed: 44952 ms + PING, 1/5 min/max/avg/p95: 0/ 4/ 0.02/ 0.00 1223ms total, 40883.07 ops/sec + PING, 50/5 min/max/avg/p95: 0/ 3/ 0.50/ 1.00 497ms total, 100603.62 ops/sec + PING, batch 50/5 min/max/avg/p95: 0/ 1/ 0.15/ 1.00 308ms total, 324675.32 ops/sec + SET 4B str, 1/5 min/max/avg/p95: 0/ 2/ 0.03/ 0.00 1402ms total, 35663.34 ops/sec + SET 4B str, 50/5 min/max/avg/p95: 0/ 2/ 0.53/ 1.00 534ms total, 93632.96 ops/sec + SET 4B str, batch 50/5 min/max/avg/p95: 0/ 1/ 0.19/ 1.00 392ms total, 255102.04 ops/sec + SET 4B buf, 1/5 min/max/avg/p95: 0/ 2/ 0.05/ 1.00 2433ms total, 20550.76 ops/sec + SET 4B buf, 50/5 min/max/avg/p95: 0/ 5/ 1.65/ 3.00 1652ms total, 30266.34 ops/sec + SET 4B buf, batch 50/5 min/max/avg/p95: 0/ 3/ 0.36/ 1.00 726ms total, 137741.05 ops/sec + GET 4B str, 1/5 min/max/avg/p95: 0/ 1/ 0.03/ 0.00 1314ms total, 38051.75 ops/sec + GET 4B str, 50/5 min/max/avg/p95: 0/ 3/ 0.53/ 1.00 529ms total, 94517.96 ops/sec + GET 4B str, batch 50/5 min/max/avg/p95: 0/ 1/ 0.16/ 1.00 328ms total, 304878.05 ops/sec + GET 4B buf, 1/5 min/max/avg/p95: 0/ 2/ 0.03/ 0.00 1389ms total, 35997.12 ops/sec + GET 4B buf, 50/5 min/max/avg/p95: 0/ 2/ 0.52/ 1.00 519ms total, 96339.11 ops/sec + GET 4B buf, batch 50/5 min/max/avg/p95: 0/ 1/ 0.16/ 1.00 168ms total, 297619.05 ops/sec + SET 4KiB str, 1/5 min/max/avg/p95: 0/ 3/ 0.03/ 0.00 1670ms total, 29940.12 ops/sec + SET 4KiB str, 50/5 min/max/avg/p95: 0/ 5/ 0.94/ 2.00 941ms total, 53134.96 ops/sec + SET 4KiB str, batch 50/5 min/max/avg/p95: 0/ 2/ 0.49/ 1.00 984ms total, 101626.02 ops/sec + SET 4KiB buf, 1/5 min/max/avg/p95: 0/ 1/ 0.05/ 0.00 2423ms total, 20635.58 ops/sec + SET 4KiB buf, 50/5 min/max/avg/p95: 0/ 5/ 1.60/ 3.00 1598ms total, 31289.11 ops/sec + SET 4KiB buf, batch 50/5 min/max/avg/p95: 0/ 1/ 0.41/ 1.00 825ms total, 121212.12 ops/sec + GET 4KiB str, 1/5 min/max/avg/p95: 0/ 1/ 0.03/ 0.00 1483ms total, 33715.44 ops/sec + GET 4KiB str, 50/5 min/max/avg/p95: 0/ 3/ 0.69/ 1.00 691ms total, 72358.90 ops/sec + GET 4KiB str, batch 50/5 min/max/avg/p95: 0/ 2/ 0.38/ 1.00 759ms total, 131752.31 ops/sec + GET 4KiB buf, 1/5 min/max/avg/p95: 0/ 3/ 0.03/ 0.00 1485ms total, 33670.03 ops/sec + GET 4KiB buf, 50/5 min/max/avg/p95: 0/ 3/ 0.80/ 2.00 797ms total, 62735.26 ops/sec + GET 4KiB buf, batch 50/5 min/max/avg/p95: 0/ 2/ 0.39/ 1.00 396ms total, 126262.63 ops/sec + INCR, 1/5 min/max/avg/p95: 0/ 2/ 0.03/ 0.00 1376ms total, 36337.21 ops/sec + INCR, 50/5 min/max/avg/p95: 0/ 3/ 0.53/ 1.00 529ms total, 94517.96 ops/sec + INCR, batch 50/5 min/max/avg/p95: 0/ 1/ 0.17/ 1.00 339ms total, 294985.25 ops/sec + LPUSH, 1/5 min/max/avg/p95: 0/ 3/ 0.03/ 0.00 1394ms total, 35868.01 ops/sec + LPUSH, 50/5 min/max/avg/p95: 0/ 3/ 0.58/ 1.00 584ms total, 85616.44 ops/sec + LPUSH, batch 50/5 min/max/avg/p95: 0/ 1/ 0.19/ 1.00 383ms total, 261096.61 ops/sec + LRANGE 10, 1/5 min/max/avg/p95: 0/ 4/ 0.03/ 0.00 1706ms total, 29308.32 ops/sec + LRANGE 10, 50/5 min/max/avg/p95: 0/ 3/ 0.71/ 1.00 712ms total, 70224.72 ops/sec + LRANGE 10, batch 50/5 min/max/avg/p95: 0/ 1/ 0.38/ 1.00 772ms total, 129533.68 ops/sec + LRANGE 100, 1/5 min/max/avg/p95: 0/ 1/ 0.06/ 1.00 3026ms total, 16523.46 ops/sec + LRANGE 100, 50/5 min/max/avg/p95: 0/ 5/ 1.88/ 3.00 1882ms total, 26567.48 ops/sec + LRANGE 100, batch 50/5 min/max/avg/p95: 2/ 4/ 2.09/ 3.00 4189ms total, 23872.05 ops/sec + SET 4MiB buf, 1/5 min/max/avg/p95: 1/ 7/ 2.08/ 3.00 1044ms total, 478.93 ops/sec + SET 4MiB buf, 20/5 min/max/avg/p95: 17/ 50/ 40.02/ 46.00 1022ms total, 489.24 ops/sec + SET 4MiB buf, batch 20/5 min/max/avg/p95: 37/ 45/ 39.00/ 44.40 975ms total, 512.82 ops/sec + GET 4MiB str, 1/5 min/max/avg/p95: 4/ 15/ 6.31/ 10.00 634ms total, 157.73 ops/sec + GET 4MiB str, 20/5 min/max/avg/p95: 7/ 124/ 88.27/ 110.80 476ms total, 210.08 ops/sec + GET 4MiB str, batch 20/5 min/max/avg/p95: 76/ 99/ 89.00/ 99.00 446ms total, 224.22 ops/sec + GET 4MiB buf, 1/5 min/max/avg/p95: 4/ 12/ 5.67/ 10.00 568ms total, 176.06 ops/sec + GET 4MiB buf, 20/5 min/max/avg/p95: 14/ 133/ 85.34/ 107.95 458ms total, 218.34 ops/sec + GET 4MiB buf, batch 20/5 min/max/avg/p95: 78/ 96/ 88.00/ 96.00 440ms total, 227.27 ops/sec +End of tests. Total time elapsed: 50421 ms The hiredis and js parser should most of the time be on the same level. The js parser lacks speed for large responses though. Therefor the hiredis parser is the default used in node_redis and we recommend using the hiredis parser. To use `hiredis`, do: diff --git a/benchmarks/multi_bench.js b/benchmarks/multi_bench.js index c00bcc3d8c3..2185a1b0252 100644 --- a/benchmarks/multi_bench.js +++ b/benchmarks/multi_bench.js @@ -200,19 +200,19 @@ very_large_buf = new Buffer(very_large_str); tests.push(new Test({descr: 'PING', command: 'ping', args: [], pipeline: 1})); tests.push(new Test({descr: 'PING', command: 'ping', args: [], pipeline: 50})); -tests.push(new Test({descr: 'PING', command: 'ping', args: [], batch: 50})); +tests.push(new Test({descr: 'PING', command: 'ping', args: [], batch: 50, reqs: num_requests * 2})); tests.push(new Test({descr: 'SET 4B str', command: 'set', args: ['foo_rand000000000000', small_str], pipeline: 1})); tests.push(new Test({descr: 'SET 4B str', command: 'set', args: ['foo_rand000000000000', small_str], pipeline: 50})); -tests.push(new Test({descr: 'SET 4B str', command: 'set', args: ['foo_rand000000000000', small_str], batch: 50})); +tests.push(new Test({descr: 'SET 4B str', command: 'set', args: ['foo_rand000000000000', small_str], batch: 50, reqs: num_requests * 2})); tests.push(new Test({descr: 'SET 4B buf', command: 'set', args: ['foo_rand000000000000', small_buf], pipeline: 1})); tests.push(new Test({descr: 'SET 4B buf', command: 'set', args: ['foo_rand000000000000', small_buf], pipeline: 50})); -tests.push(new Test({descr: 'SET 4B buf', command: 'set', args: ['foo_rand000000000000', small_buf], batch: 50})); +tests.push(new Test({descr: 'SET 4B buf', command: 'set', args: ['foo_rand000000000000', small_buf], batch: 50, reqs: num_requests * 2})); tests.push(new Test({descr: 'GET 4B str', command: 'get', args: ['foo_rand000000000000'], pipeline: 1})); tests.push(new Test({descr: 'GET 4B str', command: 'get', args: ['foo_rand000000000000'], pipeline: 50})); -tests.push(new Test({descr: 'GET 4B str', command: 'get', args: ['foo_rand000000000000'], batch: 50})); +tests.push(new Test({descr: 'GET 4B str', command: 'get', args: ['foo_rand000000000000'], batch: 50, reqs: num_requests * 2})); tests.push(new Test({descr: 'GET 4B buf', command: 'get', args: ['foo_rand000000000000'], pipeline: 1, client_opts: { return_buffers: true} })); tests.push(new Test({descr: 'GET 4B buf', command: 'get', args: ['foo_rand000000000000'], pipeline: 50, client_opts: { return_buffers: true} })); @@ -220,15 +220,15 @@ tests.push(new Test({descr: 'GET 4B buf', command: 'get', args: ['foo_rand000000 tests.push(new Test({descr: 'SET 4KiB str', command: 'set', args: ['foo_rand000000000001', large_str], pipeline: 1})); tests.push(new Test({descr: 'SET 4KiB str', command: 'set', args: ['foo_rand000000000001', large_str], pipeline: 50})); -tests.push(new Test({descr: 'SET 4KiB str', command: 'set', args: ['foo_rand000000000001', large_str], batch: 50})); +tests.push(new Test({descr: 'SET 4KiB str', command: 'set', args: ['foo_rand000000000001', large_str], batch: 50, reqs: num_requests * 2})); tests.push(new Test({descr: 'SET 4KiB buf', command: 'set', args: ['foo_rand000000000001', large_buf], pipeline: 1})); tests.push(new Test({descr: 'SET 4KiB buf', command: 'set', args: ['foo_rand000000000001', large_buf], pipeline: 50})); -tests.push(new Test({descr: 'SET 4KiB buf', command: 'set', args: ['foo_rand000000000001', large_buf], batch: 50})); +tests.push(new Test({descr: 'SET 4KiB buf', command: 'set', args: ['foo_rand000000000001', large_buf], batch: 50, reqs: num_requests * 2})); tests.push(new Test({descr: 'GET 4KiB str', command: 'get', args: ['foo_rand000000000001'], pipeline: 1})); tests.push(new Test({descr: 'GET 4KiB str', command: 'get', args: ['foo_rand000000000001'], pipeline: 50})); -tests.push(new Test({descr: 'GET 4KiB str', command: 'get', args: ['foo_rand000000000001'], batch: 50})); +tests.push(new Test({descr: 'GET 4KiB str', command: 'get', args: ['foo_rand000000000001'], batch: 50, reqs: num_requests * 2})); tests.push(new Test({descr: 'GET 4KiB buf', command: 'get', args: ['foo_rand000000000001'], pipeline: 1, client_opts: { return_buffers: true} })); tests.push(new Test({descr: 'GET 4KiB buf', command: 'get', args: ['foo_rand000000000001'], pipeline: 50, client_opts: { return_buffers: true} })); @@ -236,31 +236,31 @@ tests.push(new Test({descr: 'GET 4KiB buf', command: 'get', args: ['foo_rand0000 tests.push(new Test({descr: 'INCR', command: 'incr', args: ['counter_rand000000000000'], pipeline: 1})); tests.push(new Test({descr: 'INCR', command: 'incr', args: ['counter_rand000000000000'], pipeline: 50})); -tests.push(new Test({descr: 'INCR', command: 'incr', args: ['counter_rand000000000000'], batch: 50})); +tests.push(new Test({descr: 'INCR', command: 'incr', args: ['counter_rand000000000000'], batch: 50, reqs: num_requests * 2})); tests.push(new Test({descr: 'LPUSH', command: 'lpush', args: ['mylist', small_str], pipeline: 1})); tests.push(new Test({descr: 'LPUSH', command: 'lpush', args: ['mylist', small_str], pipeline: 50})); -tests.push(new Test({descr: 'LPUSH', command: 'lpush', args: ['mylist', small_str], batch: 50})); +tests.push(new Test({descr: 'LPUSH', command: 'lpush', args: ['mylist', small_str], batch: 50, reqs: num_requests * 2})); tests.push(new Test({descr: 'LRANGE 10', command: 'lrange', args: ['mylist', '0', '9'], pipeline: 1})); tests.push(new Test({descr: 'LRANGE 10', command: 'lrange', args: ['mylist', '0', '9'], pipeline: 50})); -tests.push(new Test({descr: 'LRANGE 10', command: 'lrange', args: ['mylist', '0', '9'], batch: 50})); +tests.push(new Test({descr: 'LRANGE 10', command: 'lrange', args: ['mylist', '0', '9'], batch: 50, reqs: num_requests * 2})); tests.push(new Test({descr: 'LRANGE 100', command: 'lrange', args: ['mylist', '0', '99'], pipeline: 1})); tests.push(new Test({descr: 'LRANGE 100', command: 'lrange', args: ['mylist', '0', '99'], pipeline: 50})); -tests.push(new Test({descr: 'LRANGE 100', command: 'lrange', args: ['mylist', '0', '99'], batch: 50})); +tests.push(new Test({descr: 'LRANGE 100', command: 'lrange', args: ['mylist', '0', '99'], batch: 50, reqs: num_requests * 2})); tests.push(new Test({descr: 'SET 4MiB buf', command: 'set', args: ['foo_rand000000000002', very_large_buf], pipeline: 1, reqs: 500})); -tests.push(new Test({descr: 'SET 4MiB buf', command: 'set', args: ['foo_rand000000000002', very_large_buf], pipeline: 50, reqs: 500})); -tests.push(new Test({descr: 'SET 4MiB buf', command: 'set', args: ['foo_rand000000000002', very_large_buf], batch: 50, reqs: 500})); +tests.push(new Test({descr: 'SET 4MiB buf', command: 'set', args: ['foo_rand000000000002', very_large_buf], pipeline: 20, reqs: 500})); +tests.push(new Test({descr: 'SET 4MiB buf', command: 'set', args: ['foo_rand000000000002', very_large_buf], batch: 20, reqs: 500})); tests.push(new Test({descr: 'GET 4MiB str', command: 'get', args: ['foo_rand000000000002'], pipeline: 1, reqs: 100})); -tests.push(new Test({descr: 'GET 4MiB str', command: 'get', args: ['foo_rand000000000002'], pipeline: 50, reqs: 100})); -tests.push(new Test({descr: 'GET 4MiB str', command: 'get', args: ['foo_rand000000000002'], batch: 50, reqs: 100})); +tests.push(new Test({descr: 'GET 4MiB str', command: 'get', args: ['foo_rand000000000002'], pipeline: 20, reqs: 100})); +tests.push(new Test({descr: 'GET 4MiB str', command: 'get', args: ['foo_rand000000000002'], batch: 20, reqs: 100})); tests.push(new Test({descr: 'GET 4MiB buf', command: 'get', args: ['foo_rand000000000002'], pipeline: 1, reqs: 100, client_opts: { return_buffers: true} })); -tests.push(new Test({descr: 'GET 4MiB buf', command: 'get', args: ['foo_rand000000000002'], pipeline: 50, reqs: 100, client_opts: { return_buffers: true} })); -tests.push(new Test({descr: 'GET 4MiB buf', command: 'get', args: ['foo_rand000000000002'], batch: 50, reqs: 100, client_opts: { return_buffers: true} })); +tests.push(new Test({descr: 'GET 4MiB buf', command: 'get', args: ['foo_rand000000000002'], pipeline: 20, reqs: 100, client_opts: { return_buffers: true} })); +tests.push(new Test({descr: 'GET 4MiB buf', command: 'get', args: ['foo_rand000000000002'], batch: 20, reqs: 100, client_opts: { return_buffers: true} })); function next() { var test = tests.shift(); diff --git a/changelog.md b/changelog.md index ce8da25d4a0..0e9b1c768d3 100644 --- a/changelog.md +++ b/changelog.md @@ -1,7 +1,7 @@ Changelog ========= -## v.2.2.0 - 08, 2015 - The peregrino falcon +## v.2.2.0 - xx Oct, 2015 - The peregrino falcon The peregrino falcon is the fasted bird on earth and this is what this release is all about: We increased performance for heavy usage by up to **400%** [sic!] and increased overall performance for any command as well. Please check the benchmarks in the [README.md](README.md) for further details. @@ -26,6 +26,32 @@ Both .multi and .batch are from now on going to cache the commands and release t Please consider using .batch instead of looping through a lot of commands one by one. This will significantly improve your performance. +Here are some stats compared to ioredis 1.9.1: + + simple set + 82,496 op/s » ioredis + 112,617 op/s » node_redis + + simple get + 82,015 op/s » ioredis + 105,701 op/s » node_redis + + simple get with pipeline + 10,233 op/s » ioredis + 26,541 op/s » node_redis + + lrange 100 + 7,321 op/s » ioredis + 26,155 op/s » node_redis + + publish + 90,524 op/s » ioredis + 112,823 op/s » node_redis + + subscribe + 43,783 op/s » ioredis + 61,889 op/s » node_redis + To conclude: we can proudly say that node_redis is very likely outperforming any other node redis client. ## v2.1.0 - Oct 02, 2015 diff --git a/index.js b/index.js index f661167f07c..c846fa5b603 100644 --- a/index.js +++ b/index.js @@ -34,17 +34,19 @@ parsers.push(require('./lib/parsers/javascript')); function RedisClient(stream, options) { options = options || {}; + var self = this; + this.pipeline = 0; if (!stream.cork) { - stream.cork = function noop() {}; + stream.cork = function noop() { + self.pipeline_queue = new Queue(); + }; stream.uncork = function noop() {}; - stream.__write = stream.write; - stream.write = this.writeStream.bind(this); + this.write = this.writeStream; } this.stream = stream; this.options = options; - this.connection_id = ++connection_id; this.connected = false; this.ready = false; @@ -73,14 +75,11 @@ function RedisClient(stream, options) { this.command_queue_high_water = options.command_queue_high_water || 1000; this.command_queue_low_water = options.command_queue_low_water || 0; this.max_attempts = +options.max_attempts || 0; - this.command_queue = new Queue(); // holds sent commands to de-pipeline them - this.offline_queue = new Queue(); // holds commands issued but not able to be sent + this.command_queue = new Queue(); // Holds sent commands to de-pipeline them + this.offline_queue = new Queue(); // Holds commands issued but not able to be sent this.commands_sent = 0; this.connect_timeout = +options.connect_timeout || 86400000; // 24 * 60 * 60 * 1000 ms - this.enable_offline_queue = true; - if (options.enable_offline_queue === false) { - this.enable_offline_queue = false; - } + this.enable_offline_queue = options.enable_offline_queue === false ? false : true; this.retry_max_delay = +options.retry_max_delay || null; this.initialize_retry_vars(); this.pub_sub_mode = false; @@ -90,10 +89,8 @@ function RedisClient(stream, options) { this.server_info = {}; this.auth_pass = options.auth_pass; this.parser_module = null; - this.selected_db = null; // save the selected db here, used when reconnecting + this.selected_db = null; // Save the selected db here, used when reconnecting this.old_state = null; - this.pipeline = 0; - this.pipeline_queue = new Queue(); this.install_stream_listeners(); events.EventEmitter.call(this); @@ -154,21 +151,19 @@ RedisClient.prototype.unref = function () { // flush offline_queue and command_queue, erroring any items with a callback first RedisClient.prototype.flush_and_error = function (error) { var command_obj; - while (command_obj = this.offline_queue.shift()) { if (typeof command_obj.callback === 'function') { error.command = command_obj.command.toUpperCase(); command_obj.callback(error); } } - this.offline_queue = new Queue(); - while (command_obj = this.command_queue.shift()) { if (typeof command_obj.callback === 'function') { error.command = command_obj.command.toUpperCase(); command_obj.callback(error); } } + this.offline_queue = new Queue(); this.command_queue = new Queue(); }; @@ -249,7 +244,6 @@ RedisClient.prototype.on_connect = function () { this.connected = true; this.ready = false; this.connections += 1; - this.command_queue = new Queue(); this.emitted_end = false; if (this.options.socket_nodelay) { this.stream.setNoDelay(); @@ -446,8 +440,8 @@ RedisClient.prototype.send_offline_queue = function () { debug('Sending offline command: ' + command_obj.command); buffered_writes += !this.send_command(command_obj.command, command_obj.args, command_obj.callback); } - this.offline_queue = new Queue(); // Even though items were shifted off, Queue backing store still uses memory until next add, so just get a new Queue + this.offline_queue = new Queue(); if (buffered_writes === 0) { this.should_buffer = false; @@ -558,7 +552,8 @@ RedisClient.prototype.return_error = function (err) { RedisClient.prototype.emit_drain_idle = function (queue_len) { if (this.pub_sub_mode === false && queue_len === 0) { - this.command_queue.clear(); + // Free the queue capacity memory by using a new queue + this.command_queue = new Queue(); this.emit('idle'); } @@ -763,21 +758,21 @@ RedisClient.prototype.send_command = function (command, args, callback) { command_str += '$' + Buffer.byteLength(arg) + '\r\n' + arg + '\r\n'; } debug('Send ' + this.address + ' id ' + this.connection_id + ': ' + command_str); - buffered_writes += !stream.write(command_str); + buffered_writes += !this.write(command_str); } else { debug('Send command (' + command_str + ') has Buffer arguments'); - buffered_writes += !stream.write(command_str); + buffered_writes += !this.write(command_str); for (i = 0; i < args.length; i += 1) { arg = args[i]; if (Buffer.isBuffer(arg)) { if (arg.length === 0) { debug('send_command: using empty string for 0 length buffer'); - buffered_writes += !stream.write('$0\r\n\r\n'); + buffered_writes += !this.write('$0\r\n\r\n'); } else { - buffered_writes += !stream.write('$' + arg.length + '\r\n'); - buffered_writes += !stream.write(arg); - buffered_writes += !stream.write('\r\n'); + buffered_writes += !this.write('$' + arg.length + '\r\n'); + buffered_writes += !this.write(arg); + buffered_writes += !this.write('\r\n'); debug('send_command: buffer send ' + arg.length + ' bytes'); } } else { @@ -785,7 +780,7 @@ RedisClient.prototype.send_command = function (command, args, callback) { arg = String(arg); } debug('send_command: string send ' + Buffer.byteLength(arg) + ' bytes: ' + arg); - buffered_writes += !stream.write('$' + Buffer.byteLength(arg) + '\r\n' + arg + '\r\n'); + buffered_writes += !this.write('$' + Buffer.byteLength(arg) + '\r\n' + arg + '\r\n'); } } } @@ -796,22 +791,28 @@ RedisClient.prototype.send_command = function (command, args, callback) { return !this.should_buffer; }; +RedisClient.prototype.write = function (data) { + return this.stream.write(data); +}; + RedisClient.prototype.writeStream = function (data) { var nr = 0; - // Do not use a pipeline if (this.pipeline === 0) { - return this.stream.__write(data); + return this.stream.write(data); } + this.pipeline--; - this.pipeline_queue.push(data); if (this.pipeline === 0) { - var len = this.pipeline_queue.length; - while (len--) { - nr += !this.stream.__write(this.pipeline_queue.shift()); + var command; + while (command = this.pipeline_queue.shift()) { + nr += !this.stream.write(command); } + nr += !this.stream.write(data); return !nr; } + + this.pipeline_queue.push(data); return true; }; @@ -871,13 +872,11 @@ RedisClient.prototype.end = function (flush) { function Multi(client, args, transaction) { client.stream.cork(); this._client = client; - this.queue = []; + this.queue = new Queue(); if (transaction) { this.exec = this.exec_transaction; this.EXEC = this.exec_transaction; - this.queue.push(['multi']); } - this._client.pipeline_queue.clear(); var command, tmp_args; if (Array.isArray(args)) { while (tmp_args = args.shift()) { @@ -908,7 +907,7 @@ commands.forEach(function (fullCommand) { return; } - RedisClient.prototype[command] = function (key, arg, callback) { + RedisClient.prototype[command.toUpperCase()] = RedisClient.prototype[command] = function (key, arg, callback) { if (Array.isArray(key)) { return this.send_command(command, key, arg); } @@ -926,9 +925,8 @@ commands.forEach(function (fullCommand) { } return this.send_command(command, utils.to_array(arguments)); }; - RedisClient.prototype[command.toUpperCase()] = RedisClient.prototype[command]; - Multi.prototype[command] = function (key, arg, callback) { + Multi.prototype[command.toUpperCase()] = Multi.prototype[command] = function (key, arg, callback) { if (Array.isArray(key)) { if (arg) { key = key.concat([arg]); @@ -952,7 +950,6 @@ commands.forEach(function (fullCommand) { } return this; }; - Multi.prototype[command.toUpperCase()] = Multi.prototype[command]; }); // store db in this.select_db to restore it on reconnect @@ -1061,23 +1058,31 @@ Multi.prototype.send_command = function (command, args, index, cb) { if (cb) { cb(err); } - err.position = index - 1; + err.position = index; self.errors.push(err); } }); }; +Multi.prototype.exec_atomic = function (callback) { + if (this.queue.length < 2) { + return this.exec_batch(callback); + } + return this.exec(callback); +}; + Multi.prototype.exec_transaction = function (callback) { var self = this; var len = this.queue.length; var cb; this.errors = []; this.callback = callback; - this._client.pipeline = len; + this._client.pipeline = len + 2; this.wants_buffers = new Array(len); + this.send_command('multi', []); // drain queue, callback will catch 'QUEUED' or error for (var index = 0; index < len; index++) { - var args = this.queue[index].slice(0); + var args = this.queue.get(index).slice(0); var command = args.shift(); if (typeof args[args.length - 1] === 'function') { cb = args.pop(); @@ -1108,7 +1113,7 @@ Multi.prototype.exec_transaction = function (callback) { }; Multi.prototype.execute_callback = function (err, replies) { - var i, args; + var i = 0, args; if (err) { if (err.code !== 'CONNECTION_BROKEN') { @@ -1124,9 +1129,7 @@ Multi.prototype.execute_callback = function (err, replies) { } if (replies) { - for (i = 0; i < this.queue.length - 1; i += 1) { - args = this.queue[i + 1]; - + while (args = this.queue.shift()) { // If we asked for strings, even in detect_buffers mode, then return strings: if (replies[i] instanceof Error) { var match = replies[i].message.match(utils.errCode); @@ -1136,7 +1139,7 @@ Multi.prototype.execute_callback = function (err, replies) { } replies[i].command = args[0].toUpperCase(); } else if (replies[i]) { - if (this.wants_buffers[i + 1] === false) { + if (this.wants_buffers[i] === false) { replies[i] = utils.reply_to_strings(replies[i]); } if (args[0] === 'hgetall') { @@ -1152,6 +1155,7 @@ Multi.prototype.execute_callback = function (err, replies) { args[args.length - 1](null, replies[i]); } } + i++; } } @@ -1176,7 +1180,7 @@ Multi.prototype.callback = function (cb, command, i) { }; }; -Multi.prototype.exec = Multi.prototype.EXEC = function (callback) { +Multi.prototype.exec = Multi.prototype.EXEC = Multi.prototype.exec_batch = function (callback) { var len = this.queue.length; var self = this; var index = 0; diff --git a/test/commands/multi.spec.js b/test/commands/multi.spec.js index 73aaa46bf43..44ac2c9c58c 100644 --- a/test/commands/multi.spec.js +++ b/test/commands/multi.spec.js @@ -385,6 +385,41 @@ describe("The 'multi' method", function () { client.get('foo', helper.isString('bar', done)); }); + it("should not use a transaction with exec_atomic if only no command is used", function () { + var multi = client.multi(); + var test = false; + multi.exec_batch = function () { + test = true; + }; + multi.exec_atomic(); + assert(test); + }); + + it("should not use a transaction with exec_atomic if only one command is used", function () { + var multi = client.multi(); + var test = false; + multi.exec_batch = function () { + test = true; + }; + multi.set("baz", "binary"); + multi.exec_atomic(); + assert(test); + }); + + it("should use transaction with exec_atomic and more than one command used", function (done) { + helper.serverVersionAtLeast.call(this, client, [2, 6, 5]); + + var multi = client.multi(); + var test = false; + multi.exec_batch = function () { + test = true; + }; + multi.set("baz", "binary"); + multi.get('baz'); + multi.exec_atomic(done); + assert(!test); + }); + }); }); }); diff --git a/test/rename.spec.js b/test/rename.spec.js index 52ee7a4fe45..fe4b3b50997 100644 --- a/test/rename.spec.js +++ b/test/rename.spec.js @@ -17,13 +17,7 @@ describe("rename commands", function () { describe("using " + parser + " and " + ip, function () { var client = null; - afterEach(function () { - client.end(); - }); - - it("allows to use renamed functions", function (done) { - if (helper.redisProcess().spawnFailed()) this.skip(); - + beforeEach(function(done) { client = redis.createClient({ rename_commands: { set: '807081f5afa96845a02816a28b7258c3', @@ -31,6 +25,18 @@ describe("rename commands", function () { } }); + client.on('ready', function () { + done(); + }); + }); + + afterEach(function () { + client.end(); + }); + + it("allows to use renamed functions", function (done) { + if (helper.redisProcess().spawnFailed()) this.skip(); + client.set('key', 'value', function(err, reply) { assert.strictEqual(reply, 'OK'); }); @@ -48,16 +54,26 @@ describe("rename commands", function () { }); }); - it("should also work with multi", function (done) { + it("should also work with batch", function (done) { if (helper.redisProcess().spawnFailed()) this.skip(); - client = redis.createClient({ - rename_commands: { - SET: '807081f5afa96845a02816a28b7258c3', - getrange: '9e3102b15cf231c4e9e940f284744fe0' - } + client.batch([['set', 'key', 'value']]).exec(function (err, res) { + assert.strictEqual(res[0], 'OK'); }); + var batch = client.batch(); + batch.getrange('key', 1, -1); + batch.exec(function (err, res) { + assert(!err); + assert.strictEqual(res.length, 1); + assert.strictEqual(res[0], 'alue'); + done(); + }); + }); + + it("should also work with multi", function (done) { + if (helper.redisProcess().spawnFailed()) this.skip(); + client.multi([['set', 'key', 'value']]).exec(function (err, res) { assert.strictEqual(res[0], 'OK'); }); @@ -75,13 +91,6 @@ describe("rename commands", function () { it("should also work with multi and abort transaction", function (done) { if (helper.redisProcess().spawnFailed()) this.skip(); - client = redis.createClient({ - rename_commands: { - SET: '807081f5afa96845a02816a28b7258c3', - getrange: '9e3102b15cf231c4e9e940f284744fe0' - } - }); - var multi = client.multi(); multi.get('key'); multi.getrange('key', 1, -1, function(err, reply) { From 06f57fd1d9edcd4addd15622005d15f2f889bfbc Mon Sep 17 00:00:00 2001 From: Ruben Bridgewater Date: Mon, 12 Oct 2015 17:21:58 +0200 Subject: [PATCH 0429/1748] Add some more notes and tests --- changelog.md | 16 +++++++++++----- test/batch.spec.js | 15 +++++++++++++++ test/pubsub.spec.js | 45 +++++++++++++++++++++++++++++++++++++++++++++ 3 files changed, 71 insertions(+), 5 deletions(-) diff --git a/changelog.md b/changelog.md index 0e9b1c768d3..975bd683904 100644 --- a/changelog.md +++ b/changelog.md @@ -1,9 +1,9 @@ Changelog ========= -## v.2.2.0 - xx Oct, 2015 - The peregrino falcon +## v.2.2.0 - 12 Oct, 2015 - The peregrino falcon -The peregrino falcon is the fasted bird on earth and this is what this release is all about: We increased performance for heavy usage by up to **400%** [sic!] and increased overall performance for any command as well. Please check the benchmarks in the [README.md](README.md) for further details. +The peregrino falcon is the fasted bird on earth and this is what this release is all about: Increased performance for heavy usage by up to **400%** [sic!] and increased overall performance for any command as well. Please check the benchmarks in the [README.md](README.md) for further details. Features @@ -18,15 +18,17 @@ Features Bugfixes -- Fix a javascript parser regression introduced in 2.0 that could result in timeouts on high load. ([@BridgeAR](https://github.com/BridgeAR)) +- Fix a javascript parser regression introduced in 2.0 that could result in timeouts on high load. * ([@BridgeAR](https://github.com/BridgeAR)) - Fixed should_buffer boolean for .exec, .select and .auth commands not being returned and fix a couple special conditions ([@BridgeAR](https://github.com/BridgeAR)) +* I was not able to write a regression test for this, since the error seems to only occur under heavy load with special conditions. So please have a look for timeouts with the js parser, if you use it and report all issues and switch to the hiredis parser in the meanwhile. If you're able to come up with a reproducable test case, this would be even better :) + If you do not rely on transactions but want to reduce the RTT you can use .batch from now on. It'll behave just the same as .multi but it does not have any transaction and therefor won't roll back any failed commands.
Both .multi and .batch are from now on going to cache the commands and release them while calling .exec. Please consider using .batch instead of looping through a lot of commands one by one. This will significantly improve your performance. -Here are some stats compared to ioredis 1.9.1: +Here are some stats compared to ioredis 1.9.1 (Lenovo T450s i7-5600U): simple set 82,496 op/s » ioredis @@ -38,7 +40,7 @@ Here are some stats compared to ioredis 1.9.1: simple get with pipeline 10,233 op/s » ioredis - 26,541 op/s » node_redis + 26,541 op/s » node_redis (using .batch) lrange 100 7,321 op/s » ioredis @@ -54,6 +56,10 @@ Here are some stats compared to ioredis 1.9.1: To conclude: we can proudly say that node_redis is very likely outperforming any other node redis client. +Known issues + +- The pub sub system has some flaws and those will be addressed in the next minor release + ## v2.1.0 - Oct 02, 2015 Features: diff --git a/test/batch.spec.js b/test/batch.spec.js index 5164d897e9b..3fb5e392a49 100644 --- a/test/batch.spec.js +++ b/test/batch.spec.js @@ -63,6 +63,21 @@ describe("The 'batch' method", function () { client.end(); }); + it("returns an empty array", function (done) { + var batch = client.batch(); + batch.exec(function (err, res) { + assert.strictEqual(err, null); + assert.strictEqual(res.length, 0); + done(); + }); + }); + + it("returns an empty array if promisified", function () { + return client.batch().execAsync().then(function(res) { + assert.strictEqual(res.length, 0); + }); + }); + it("returns an empty result array", function (done) { var batch = client.batch(); var async = true; diff --git a/test/pubsub.spec.js b/test/pubsub.spec.js index 9ffd831a110..37d9e8bfadc 100644 --- a/test/pubsub.spec.js +++ b/test/pubsub.spec.js @@ -285,6 +285,51 @@ describe("publish/subscribe", function () { }); }); + // TODO: Fix pub sub + // And there's more than just those two issues + describe.skip('FIXME: broken pub sub', function () { + + it("should not publish a message without any publish command", function (done) { + pub.set('foo', 'message'); + pub.set('bar', 'hello'); + pub.mget('foo', 'bar'); + pub.subscribe('channel'); + pub.on('message', function (msg) { + done(new Error('This message should not have been published: ' + msg)); + }); + setTimeout(done, 500); + }); + + it("should not publish a message multiple times per command", function (done) { + var published = {}; + + function subscribe(message) { + sub.on('subscribe', function () { + pub.publish('/foo', message); + }); + sub.on('message', function (channel, message) { + if (published[message]) { + done(new Error('Message published more than once.')); + } + published[message] = true; + }); + sub.on('unsubscribe', function (channel, count) { + assert.strictEqual(count, 0); + }); + sub.subscribe('/foo'); + } + + subscribe('hello'); + + setTimeout(function () { + sub.unsubscribe(); + setTimeout(function () { + subscribe('world'); + }, 400); + }, 400); + }); + }); + afterEach(function () { sub.end(); pub.end(); From b937b012eb9ae5e22a31180c02bb7dbd8067d781 Mon Sep 17 00:00:00 2001 From: Ruben Bridgewater Date: Mon, 12 Oct 2015 17:23:10 +0200 Subject: [PATCH 0430/1748] v.2.2.0 --- package.json | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/package.json b/package.json index f26d99e7482..0f69d234d3a 100644 --- a/package.json +++ b/package.json @@ -1,6 +1,6 @@ { "name": "redis", - "version": "2.1.0", + "version": "2.2.0", "description": "Redis client library", "keywords": [ "database", From 3225b1e704dce42c89bd30e1d13613d66909f5eb Mon Sep 17 00:00:00 2001 From: Ruben Bridgewater Date: Mon, 12 Oct 2015 17:47:34 +0200 Subject: [PATCH 0431/1748] Minor changelog change --- changelog.md | 5 ++--- 1 file changed, 2 insertions(+), 3 deletions(-) diff --git a/changelog.md b/changelog.md index 975bd683904..1dfb2e1905f 100644 --- a/changelog.md +++ b/changelog.md @@ -18,11 +18,10 @@ Features Bugfixes -- Fix a javascript parser regression introduced in 2.0 that could result in timeouts on high load. * ([@BridgeAR](https://github.com/BridgeAR)) +- Fix a javascript parser regression introduced in 2.0 that could result in timeouts on high load. ([@BridgeAR](https://github.com/BridgeAR)) + - I was not able to write a regression test for this, since the error seems to only occur under heavy load with special conditions. So please have a look for timeouts with the js parser, if you use it and report all issues and switch to the hiredis parser in the meanwhile. If you're able to come up with a reproducable test case, this would be even better :) - Fixed should_buffer boolean for .exec, .select and .auth commands not being returned and fix a couple special conditions ([@BridgeAR](https://github.com/BridgeAR)) -* I was not able to write a regression test for this, since the error seems to only occur under heavy load with special conditions. So please have a look for timeouts with the js parser, if you use it and report all issues and switch to the hiredis parser in the meanwhile. If you're able to come up with a reproducable test case, this would be even better :) - If you do not rely on transactions but want to reduce the RTT you can use .batch from now on. It'll behave just the same as .multi but it does not have any transaction and therefor won't roll back any failed commands.
Both .multi and .batch are from now on going to cache the commands and release them while calling .exec. From 2d1b1e23497c4943cd2e84e2f19378652972c307 Mon Sep 17 00:00:00 2001 From: Ruben Bridgewater Date: Mon, 12 Oct 2015 17:56:56 +0200 Subject: [PATCH 0432/1748] Fix benchmark section in the readme --- README.md | 2 ++ 1 file changed, 2 insertions(+) diff --git a/README.md b/README.md index 69446407e8d..69f87d87219 100644 --- a/README.md +++ b/README.md @@ -649,6 +649,7 @@ Here are results of `multi_bench.js` which is similar to `redis-benchmark` from hiredis parser (Lenovo T450s i7-5600U): +``` Client count: 5, node version: 4.1.2, server version: 3.0.3, parser: hiredis PING, 1/5 min/max/avg/p95: 0/ 4/ 0.02/ 0.00 1223ms total, 40883.07 ops/sec PING, 50/5 min/max/avg/p95: 0/ 3/ 0.50/ 1.00 497ms total, 100603.62 ops/sec @@ -699,6 +700,7 @@ Client count: 5, node version: 4.1.2, server version: 3.0.3, parser: hiredis GET 4MiB buf, 20/5 min/max/avg/p95: 14/ 133/ 85.34/ 107.95 458ms total, 218.34 ops/sec GET 4MiB buf, batch 20/5 min/max/avg/p95: 78/ 96/ 88.00/ 96.00 440ms total, 227.27 ops/sec End of tests. Total time elapsed: 50421 ms +``` The hiredis and js parser should most of the time be on the same level. The js parser lacks speed for large responses though. Therefor the hiredis parser is the default used in node_redis and we recommend using the hiredis parser. To use `hiredis`, do: From 28d343c2ade94baac94b2dec3a68187c08d75b69 Mon Sep 17 00:00:00 2001 From: Ruben Bridgewater Date: Mon, 12 Oct 2015 17:58:41 +0200 Subject: [PATCH 0433/1748] v.2.2.1 --- package.json | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/package.json b/package.json index 0f69d234d3a..35d6bd7e92a 100644 --- a/package.json +++ b/package.json @@ -1,6 +1,6 @@ { "name": "redis", - "version": "2.2.0", + "version": "2.2.1", "description": "Redis client library", "keywords": [ "database", From 60e9d0fdd60d53021581402537b8379f2c346ea0 Mon Sep 17 00:00:00 2001 From: Ruben Bridgewater Date: Wed, 14 Oct 2015 00:58:27 +0200 Subject: [PATCH 0434/1748] Fix multi regression. Closes #889 Allow commands being executed after a Multi / Batch was initiated but not yet executed --- index.js | 15 +++++++------- test/batch.spec.js | 6 ++++++ test/commands/multi.spec.js | 41 +++++++++++++++++++++++++++++++++++++ 3 files changed, 54 insertions(+), 8 deletions(-) diff --git a/index.js b/index.js index c846fa5b603..8988aa68d9c 100644 --- a/index.js +++ b/index.js @@ -869,14 +869,9 @@ RedisClient.prototype.end = function (flush) { return this.stream.destroySoon(); }; -function Multi(client, args, transaction) { - client.stream.cork(); +function Multi(client, args) { this._client = client; this.queue = new Queue(); - if (transaction) { - this.exec = this.exec_transaction; - this.EXEC = this.exec_transaction; - } var command, tmp_args; if (Array.isArray(args)) { while (tmp_args = args.shift()) { @@ -892,11 +887,13 @@ function Multi(client, args, transaction) { } RedisClient.prototype.multi = RedisClient.prototype.MULTI = function (args) { - return new Multi(this, args, true); + var multi = new Multi(this, args); + multi.exec = multi.EXEC = multi.exec_transaction; + return multi; }; RedisClient.prototype.batch = RedisClient.prototype.BATCH = function (args) { - return new Multi(this, args, false); + return new Multi(this, args); }; commands.forEach(function (fullCommand) { @@ -1077,6 +1074,7 @@ Multi.prototype.exec_transaction = function (callback) { var cb; this.errors = []; this.callback = callback; + this._client.stream.cork(); this._client.pipeline = len + 2; this.wants_buffers = new Array(len); this.send_command('multi', []); @@ -1194,6 +1192,7 @@ Multi.prototype.exec = Multi.prototype.EXEC = Multi.prototype.exec_batch = funct return true; } this.results = new Array(len); + this._client.stream.cork(); this._client.pipeline = len; var lastCallback = function (cb) { return function (err, res) { diff --git a/test/batch.spec.js b/test/batch.spec.js index 3fb5e392a49..27b45ad3f06 100644 --- a/test/batch.spec.js +++ b/test/batch.spec.js @@ -72,6 +72,12 @@ describe("The 'batch' method", function () { }); }); + it("runs normal calls inbetween batch", function (done) { + var batch = client.batch(); + batch.set("m1", "123"); + client.set('m2', '456', done); + }); + it("returns an empty array if promisified", function () { return client.batch().execAsync().then(function(res) { assert.strictEqual(res.length, 0); diff --git a/test/commands/multi.spec.js b/test/commands/multi.spec.js index 44ac2c9c58c..7ebfc63b299 100644 --- a/test/commands/multi.spec.js +++ b/test/commands/multi.spec.js @@ -73,6 +73,47 @@ describe("The 'multi' method", function () { assert.strictEqual(notBuffering, true); }); + it("runs normal calls inbetween multis", function (done) { + var multi1 = client.multi(); + multi1.set("m1", "123"); + client.set('m2', '456', done); + }); + + it("runs simultaneous multis with the same client", function (done) { + var end = helper.callFuncAfter(done, 2); + + var multi1 = client.multi(); + multi1.set("m1", "123"); + multi1.get('m1'); + + var multi2 = client.multi(); + multi2.set("m2", "456"); + multi2.get('m2'); + + multi1.exec(end); + multi2.exec(function(err, res) { + assert.strictEqual(res[1], '456'); + end(); + }); + }); + + it("runs simultaneous multis with the same client version 2", function (done) { + var end = helper.callFuncAfter(done, 2); + var multi2 = client.multi(); + var multi1 = client.multi(); + + multi2.set("m2", "456"); + multi1.set("m1", "123"); + multi1.get('m1'); + multi2.get('m2'); + + multi1.exec(end); + multi2.exec(function(err, res) { + assert.strictEqual(res[1], '456'); + end(); + }); + }); + it('roles back a transaction when one command in a sequence of commands fails', function (done) { var multi1, multi2; var expected = helper.serverVersionAtLeast(client, [2, 6, 5]) ? helper.isError() : function () {}; From a408235a3824b1e5d5f0072ccf56d918dae458a5 Mon Sep 17 00:00:00 2001 From: Ruben Bridgewater Date: Wed, 14 Oct 2015 01:12:41 +0200 Subject: [PATCH 0435/1748] v.2.2.2 --- changelog.md | 10 ++++++++++ package.json | 2 +- 2 files changed, 11 insertions(+), 1 deletion(-) diff --git a/changelog.md b/changelog.md index 1dfb2e1905f..f271ae20bb9 100644 --- a/changelog.md +++ b/changelog.md @@ -1,6 +1,16 @@ Changelog ========= +## v.2.2.2 - 14 Oct, 2015 + +Bugfixes + +- Fix regular commands not being executed after a .multi until .exec was called ([@BridgeAR](https://github.com/BridgeAR)) + +## v.2.2.1 - 12 Oct, 2015 + +No code change + ## v.2.2.0 - 12 Oct, 2015 - The peregrino falcon The peregrino falcon is the fasted bird on earth and this is what this release is all about: Increased performance for heavy usage by up to **400%** [sic!] and increased overall performance for any command as well. Please check the benchmarks in the [README.md](README.md) for further details. diff --git a/package.json b/package.json index 35d6bd7e92a..c85d0ce937c 100644 --- a/package.json +++ b/package.json @@ -1,6 +1,6 @@ { "name": "redis", - "version": "2.2.1", + "version": "2.2.2", "description": "Redis client library", "keywords": [ "database", From 0d4d4d74166fe368d1d5231283178fe5623078bc Mon Sep 17 00:00:00 2001 From: Ruben Bridgewater Date: Wed, 14 Oct 2015 02:24:11 +0200 Subject: [PATCH 0436/1748] Fix multi not being executed on node 0.10 if not yet ready. Closes #889 --- changelog.md | 6 ++++++ index.js | 20 +++++++++++++------- test/commands/multi.spec.js | 22 +++++++++++++++++++++- 3 files changed, 40 insertions(+), 8 deletions(-) diff --git a/changelog.md b/changelog.md index f271ae20bb9..0a1c23d10d9 100644 --- a/changelog.md +++ b/changelog.md @@ -1,6 +1,12 @@ Changelog ========= +## v.2.2.3 - 14 Oct, 2015 + +Bugfixes + +- Fix multi not being executed on Node 0.10.x if node_redis not yet ready ([@BridgeAR](https://github.com/BridgeAR)) + ## v.2.2.2 - 14 Oct, 2015 Bugfixes diff --git a/index.js b/index.js index 8988aa68d9c..5a71bca39ae 100644 --- a/index.js +++ b/index.js @@ -38,9 +38,13 @@ function RedisClient(stream, options) { this.pipeline = 0; if (!stream.cork) { - stream.cork = function noop() { - self.pipeline_queue = new Queue(); - }; + this.cork = function noop (len) {}; + this.once('ready', function () { + self.cork = function (len) { + self.pipeline = len; + self.pipeline_queue = new Queue(len); + }; + }); stream.uncork = function noop() {}; this.write = this.writeStream; } @@ -128,6 +132,10 @@ RedisClient.prototype.install_stream_listeners = function() { }); }; +RedisClient.prototype.cork = function (len) { + this.stream.cork(); +}; + RedisClient.prototype.initialize_retry_vars = function () { this.retry_timer = null; this.retry_totaltime = 0; @@ -1074,8 +1082,7 @@ Multi.prototype.exec_transaction = function (callback) { var cb; this.errors = []; this.callback = callback; - this._client.stream.cork(); - this._client.pipeline = len + 2; + this._client.cork(len + 2); this.wants_buffers = new Array(len); this.send_command('multi', []); // drain queue, callback will catch 'QUEUED' or error @@ -1192,8 +1199,7 @@ Multi.prototype.exec = Multi.prototype.EXEC = Multi.prototype.exec_batch = funct return true; } this.results = new Array(len); - this._client.stream.cork(); - this._client.pipeline = len; + this._client.cork(len); var lastCallback = function (cb) { return function (err, res) { cb(err, res); diff --git a/test/commands/multi.spec.js b/test/commands/multi.spec.js index 7ebfc63b299..775bbcc150c 100644 --- a/test/commands/multi.spec.js +++ b/test/commands/multi.spec.js @@ -50,6 +50,26 @@ describe("The 'multi' method", function () { describe("when connected", function () { var client; + beforeEach(function (done) { + client = redis.createClient.apply(redis.createClient, args); + client.once("connect", done); + }); + + afterEach(function () { + client.end(); + }); + + it("executes a pipelined multi properly in combination with the offline queue", function (done) { + var multi1 = client.multi(); + multi1.set("m1", "123"); + multi1.get('m1'); + multi1.exec(done); + }); + }); + + describe("when ready", function () { + var client; + beforeEach(function (done) { client = redis.createClient.apply(redis.createClient, args); client.once("ready", function () { @@ -73,7 +93,7 @@ describe("The 'multi' method", function () { assert.strictEqual(notBuffering, true); }); - it("runs normal calls inbetween multis", function (done) { + it("runs normal calls in-between multis", function (done) { var multi1 = client.multi(); multi1.set("m1", "123"); client.set('m2', '456', done); From 587b1c95a05303405cd3e188887b79c22a0b5654 Mon Sep 17 00:00:00 2001 From: Ruben Bridgewater Date: Wed, 14 Oct 2015 02:52:32 +0200 Subject: [PATCH 0437/1748] v.2.2.3 --- index.js | 2 +- package.json | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/index.js b/index.js index 5a71bca39ae..703bfd68504 100644 --- a/index.js +++ b/index.js @@ -36,8 +36,8 @@ function RedisClient(stream, options) { options = options || {}; var self = this; - this.pipeline = 0; if (!stream.cork) { + this.pipeline = 0; this.cork = function noop (len) {}; this.once('ready', function () { self.cork = function (len) { diff --git a/package.json b/package.json index c85d0ce937c..b3477dc773a 100644 --- a/package.json +++ b/package.json @@ -1,6 +1,6 @@ { "name": "redis", - "version": "2.2.2", + "version": "2.2.3", "description": "Redis client library", "keywords": [ "database", From 2a65ee48dd9c3ea4cca090526119085bff6304e3 Mon Sep 17 00:00:00 2001 From: Ruben Bridgewater Date: Sat, 17 Oct 2015 00:49:41 +0200 Subject: [PATCH 0438/1748] Fix some minor issues and add more tests Do not mutate the options object and add some more tests --- README.md | 16 +- benchmarks/multi_bench.js | 3 + changelog.md | 19 ++- index.js | 128 +++++++-------- lib/parsers/javascript.js | 43 ++--- package.json | 9 +- test/commands/expire.spec.js | 2 +- test/commands/get.spec.js | 8 +- test/commands/multi.spec.js | 56 +++++++ test/connection.spec.js | 281 +++++++++++++++++++++++++-------- test/helper.js | 6 +- test/node_redis.spec.js | 219 ++++++++++--------------- test/parser.spec.js | 113 +++++++++++++ test/parser/javascript.spec.js | 63 -------- test/pubsub.spec.js | 42 ++++- test/return_buffers.spec.js | 2 + 16 files changed, 619 insertions(+), 391 deletions(-) create mode 100644 test/parser.spec.js delete mode 100644 test/parser/javascript.spec.js diff --git a/README.md b/README.md index 69f87d87219..ec4f9ebf4ec 100644 --- a/README.md +++ b/README.md @@ -175,8 +175,7 @@ port and host are probably fine and you don't need to supply any arguments. `cre * `redis.createClient('redis://user:pass@host:port', options)` * `redis.createClient(port, host, options)` - `options` is an object with the following possible properties: - +### `options` is an object with the following possible properties: * `host`: *127.0.0.1*; The host to connect to * `port`: *6370*; The port to connect to * `parser`: *hiredis*; Which Redis protocol reply parser to use. If `hiredis` is not installed it will fallback to `javascript`. @@ -229,7 +228,7 @@ client.get(new Buffer("foo_rand000000000000"), function (err, reply) { client.end(); ``` -## client.auth(password, callback) +## client.auth(password[, callback]) When connecting to a Redis server that requires authentication, the `AUTH` command must be sent as the first command after connecting. This can be tricky to coordinate with reconnections, the ready check, @@ -290,7 +289,7 @@ client.get("foo", function (err, value){ Most Redis commands take a single String or an Array of Strings as arguments, and replies are sent back as a single String or an Array of Strings. When dealing with hash values, there are a couple of useful exceptions to this. -### client.hgetall(hash) +### client.hgetall(hash, callback) The reply from an HGETALL command will be converted into a JavaScript Object by `node_redis`. That way you can interact with the responses using JavaScript syntax. @@ -310,7 +309,7 @@ Output: { mjr: '1', another: '23', home: '1234' } ``` -### client.hmset(hash, obj, [callback]) +### client.hmset(hash, obj[, callback]) Multiple values in a hash can be set by supplying an object: @@ -440,7 +439,7 @@ client.multi() }); ``` -### Multi.exec( callback ) +### Multi.exec([callback]) `client.multi()` is a constructor that returns a `Multi` object. `Multi` objects share all of the same command methods as `client` objects do. Commands are queued up inside the `Multi` object @@ -492,7 +491,7 @@ client.multi([ }); ``` -### Multi.exec_atomic( callback ) +### Multi.exec_atomic([callback]) Identical to Multi.exec but with the difference that executing a single command will not use transactions. @@ -577,7 +576,7 @@ the second word as first parameter: client.multi().script('load', 'return 1').exec(...); client.multi([['script', 'load', 'return 1']]).exec(...); -## client.send_command(command_name, args, callback) +## client.send_command(command_name[, [args][, callback]]) Used internally to send commands to Redis. Nearly all Redis commands have been added to the `client` object. However, if new commands are introduced before this library is updated, you can use `send_command()` to send arbitrary commands to Redis. @@ -713,6 +712,7 @@ To get debug output run your `node_redis` application with `NODE_DEBUG=redis`. ## How to Contribute - Open a pull request or an issue about what you want to implement / change. We're glad for any help! + - Please be aware that we'll only accept fully tested code. ## Contributors Many [people](https://github.com/NodeRedis/node_redis/graphs/contributors) have have added features and fixed bugs in `node_redis`. Thanks to all of them! diff --git a/benchmarks/multi_bench.js b/benchmarks/multi_bench.js index 2185a1b0252..62b79668ef9 100644 --- a/benchmarks/multi_bench.js +++ b/benchmarks/multi_bench.js @@ -10,6 +10,9 @@ var metrics = require('metrics'); var num_clients = parseInt(process.argv[2], 10) || 5; var num_requests = 50000; var tests = []; +// var bluebird = require('bluebird'); +// bluebird.promisifyAll(redis.RedisClient.prototype); +// bluebird.promisifyAll(redis.Multi.prototype); var versions_logged = false; var client_options = { return_buffers: false, diff --git a/changelog.md b/changelog.md index 0a1c23d10d9..5c757c21993 100644 --- a/changelog.md +++ b/changelog.md @@ -1,17 +1,28 @@ Changelog ========= +## v.2.2.4 - 17 Oct, 2015 + +Bugfixes + +- Fixed unspecific error message for unresolvable commands ([@BridgeAR](https://github.com/BridgeAR)) +- Fixed not allowed command error in pubsub mode not being returned in a provided callback ([@BridgeAR](https://github.com/BridgeAR)) +- Fixed to many commands forbidden in pub sub mode ([@BridgeAR](https://github.com/BridgeAR)) +- Fixed mutation of the arguments array passed to .multi / .batch constructor ([@BridgeAR](https://github.com/BridgeAR)) +- Fixed mutation of the options object passed to createClient ([@BridgeAR](https://github.com/BridgeAR)) +- Fixed error callback in .multi not called if connection in broken mode ([@BridgeAR](https://github.com/BridgeAR)) + ## v.2.2.3 - 14 Oct, 2015 Bugfixes -- Fix multi not being executed on Node 0.10.x if node_redis not yet ready ([@BridgeAR](https://github.com/BridgeAR)) +- Fixed multi not being executed on Node 0.10.x if node_redis not yet ready ([@BridgeAR](https://github.com/BridgeAR)) ## v.2.2.2 - 14 Oct, 2015 Bugfixes -- Fix regular commands not being executed after a .multi until .exec was called ([@BridgeAR](https://github.com/BridgeAR)) +- Fixed regular commands not being executed after a .multi until .exec was called ([@BridgeAR](https://github.com/BridgeAR)) ## v.2.2.1 - 12 Oct, 2015 @@ -34,7 +45,7 @@ Features Bugfixes -- Fix a javascript parser regression introduced in 2.0 that could result in timeouts on high load. ([@BridgeAR](https://github.com/BridgeAR)) +- Fixed a javascript parser regression introduced in 2.0 that could result in timeouts on high load. ([@BridgeAR](https://github.com/BridgeAR)) - I was not able to write a regression test for this, since the error seems to only occur under heavy load with special conditions. So please have a look for timeouts with the js parser, if you use it and report all issues and switch to the hiredis parser in the meanwhile. If you're able to come up with a reproducable test case, this would be even better :) - Fixed should_buffer boolean for .exec, .select and .auth commands not being returned and fix a couple special conditions ([@BridgeAR](https://github.com/BridgeAR)) @@ -141,7 +152,7 @@ This is the biggest release that node_redis had since it was released in 2010. A - Do not wrap errors into other errors (@BridgeAR) - Authentication failures are now returned in the callback instead of being emitted (@BridgeAR) - Fix a memory leak on reconnect (@rahar) -- Using `send_command` directly may no also be called without the args as stated in the [README.md](./README.md) (@BridgeAR) +- Using `send_command` directly may now also be called without the args as stated in the [README.md](./README.md) (@BridgeAR) - Fix the multi.exec error handling (@BridgeAR) - Fix commands being inconsistent and behaving wrong (@BridgeAR) - Channel names with spaces are now properly resubscribed after a reconnection (@pbihler) diff --git a/index.js b/index.js index 703bfd68504..f70e85b6d52 100644 --- a/index.js +++ b/index.js @@ -20,6 +20,8 @@ var debug = function(msg) { } }; +function noop () {} + exports.debug_mode = /\bredis\b/i.test(process.env.NODE_DEBUG); // hiredis might not be installed @@ -33,24 +35,24 @@ try { parsers.push(require('./lib/parsers/javascript')); function RedisClient(stream, options) { - options = options || {}; + // Copy the options so they are not mutated + options = JSON.parse(JSON.stringify(options)); var self = this; if (!stream.cork) { this.pipeline = 0; - this.cork = function noop (len) {}; + this.cork = noop; this.once('ready', function () { self.cork = function (len) { self.pipeline = len; self.pipeline_queue = new Queue(len); }; }); - stream.uncork = function noop() {}; + stream.uncork = noop; this.write = this.writeStream; } this.stream = stream; - this.options = options; this.connection_id = ++connection_id; this.connected = false; this.ready = false; @@ -62,26 +64,23 @@ function RedisClient(stream, options) { options.socket_keepalive = true; } if (options.rename_commands) { - for (var command in options.rename_commands) { - if (options.rename_commands.hasOwnProperty(command)) { - options.rename_commands[command.toLowerCase()] = options.rename_commands[command]; - } + for (var command in options.rename_commands) { // jshint ignore: line + options.rename_commands[command.toLowerCase()] = options.rename_commands[command]; } } - this.options.return_buffers = !!this.options.return_buffers; - this.options.detect_buffers = !!this.options.detect_buffers; + options.return_buffers = !!options.return_buffers; + options.detect_buffers = !!options.detect_buffers; // Override the detect_buffers setting if return_buffers is active and print a warning - if (this.options.return_buffers && this.options.detect_buffers) { + if (options.return_buffers && options.detect_buffers) { console.warn('>> WARNING: You activated return_buffers and detect_buffers at the same time. The return value is always going to be a buffer.'); - this.options.detect_buffers = false; + options.detect_buffers = false; } this.should_buffer = false; - this.command_queue_high_water = options.command_queue_high_water || 1000; - this.command_queue_low_water = options.command_queue_low_water || 0; - this.max_attempts = +options.max_attempts || 0; + this.command_queue_high_water = +options.command_queue_high_water || 1000; + this.command_queue_low_water = options.command_queue_low_water | 0; + this.max_attempts = options.max_attempts | 0; this.command_queue = new Queue(); // Holds sent commands to de-pipeline them this.offline_queue = new Queue(); // Holds commands issued but not able to be sent - this.commands_sent = 0; this.connect_timeout = +options.connect_timeout || 86400000; // 24 * 60 * 60 * 1000 ms this.enable_offline_queue = options.enable_offline_queue === false ? false : true; this.retry_max_delay = +options.retry_max_delay || null; @@ -95,6 +94,7 @@ function RedisClient(stream, options) { this.parser_module = null; this.selected_db = null; // Save the selected db here, used when reconnecting this.old_state = null; + this.options = options; this.install_stream_listeners(); events.EventEmitter.call(this); @@ -118,17 +118,16 @@ RedisClient.prototype.install_stream_listeners = function() { self.on_error(err); }); - this.stream.on('close', function () { + this.stream.once('close', function () { self.connection_gone('close'); }); - this.stream.on('end', function () { + this.stream.once('end', function () { self.connection_gone('end'); }); this.stream.on('drain', function () { - self.should_buffer = false; - self.emit('drain'); + self.drain(); }); }; @@ -450,11 +449,6 @@ RedisClient.prototype.send_offline_queue = function () { } // Even though items were shifted off, Queue backing store still uses memory until next add, so just get a new Queue this.offline_queue = new Queue(); - - if (buffered_writes === 0) { - this.should_buffer = false; - this.emit('drain'); - } }; var retry_connection = function (self) { @@ -558,6 +552,11 @@ RedisClient.prototype.return_error = function (err) { } }; +RedisClient.prototype.drain = function () { + this.emit('drain'); + this.should_buffer = false; +}; + RedisClient.prototype.emit_drain_idle = function (queue_len) { if (this.pub_sub_mode === false && queue_len === 0) { // Free the queue capacity memory by using a new queue @@ -566,8 +565,7 @@ RedisClient.prototype.emit_drain_idle = function (queue_len) { } if (this.should_buffer && queue_len <= this.command_queue_low_water) { - this.emit('drain'); - this.should_buffer = false; + this.drain(); } }; @@ -688,11 +686,7 @@ RedisClient.prototype.send_command = function (command, args, callback) { command = command.toUpperCase(); err = new Error('send_command: ' + command + ' value must not be undefined or null'); err.command = command; - if (callback) { - callback(err); - } else { - this.emit('error', err); - } + this.callback_emit_error(callback, err); // Singal no buffering return true; } @@ -714,16 +708,15 @@ RedisClient.prototype.send_command = function (command, args, callback) { if (this.closing || !this.enable_offline_queue) { command = command.toUpperCase(); if (!this.closing) { - err = new Error(command + " can't be processed. Stream not writeable and enable_offline_queue is deactivated."); + var msg = !stream.writable ? + 'Stream not writeable.' : + 'The connection is not yet established and the offline queue is deactivated.'; + err = new Error(command + " can't be processed. " + msg); } else { err = new Error(command + " can't be processed. The connection has already been closed."); } err.command = command; - if (callback) { - callback(err); - } else { - this.emit('error', err); - } + this.callback_emit_error(callback, err); } else { debug('Queueing ' + command + ' for next server connection.'); this.offline_queue.push(command_obj); @@ -739,14 +732,8 @@ RedisClient.prototype.send_command = function (command, args, callback) { this.monitoring = true; } else if (command === 'quit') { this.closing = true; - } else if (this.pub_sub_mode === true) { - err = new Error('Connection in subscriber mode, only subscriber commands may be used'); - err.command = command.toUpperCase(); - this.emit('error', err); - return true; } this.command_queue.push(command_obj); - this.commands_sent += 1; if (typeof this.options.rename_commands !== 'undefined' && this.options.rename_commands[command]) { command = this.options.rename_commands[command]; @@ -864,7 +851,7 @@ RedisClient.prototype.end = function (flush) { clearTimeout(this.retry_timer); this.retry_timer = null; } - this.stream.on('error', function noop(){}); + this.stream.on('error', noop); // Flush queue if wanted if (flush) { @@ -882,9 +869,9 @@ function Multi(client, args) { this.queue = new Queue(); var command, tmp_args; if (Array.isArray(args)) { - while (tmp_args = args.shift()) { - command = tmp_args[0]; - tmp_args = tmp_args.slice(1); + for (var i = 0; i < args.length; i++) { + command = args[i][0]; + tmp_args = args[i].slice(1); if (Array.isArray(command)) { this[command[0]].apply(this, command.slice(1).concat(tmp_args)); } else { @@ -972,18 +959,22 @@ RedisClient.prototype.select = RedisClient.prototype.SELECT = function (db, call }); }; +RedisClient.prototype.callback_emit_error = function (callback, err) { + if (callback) { + setImmediate(function () { + callback(err); + }); + } else { + this.emit('error', err); + } +}; + // Stash auth for connect and reconnect. Send immediately if already connected. RedisClient.prototype.auth = RedisClient.prototype.AUTH = function (pass, callback) { if (typeof pass !== 'string') { var err = new Error('The password has to be of type "string"'); err.command = 'AUTH'; - if (callback) { - setImmediate(function () { - callback(err); - }); - } else { - this.emit('error', err); - } + this.callback_emit_error(callback, err); return true; } this.auth_pass = pass; @@ -1121,14 +1112,13 @@ Multi.prototype.execute_callback = function (err, replies) { var i = 0, args; if (err) { - if (err.code !== 'CONNECTION_BROKEN') { - err.errors = this.errors; - if (this.callback) { - this.callback(err); - } else { - // Exclude CONNECTION_BROKEN so that error won't be emitted twice - this._client.emit('error', err); - } + // The errors would be circular + err.errors = err.code !== 'CONNECTION_BROKEN' ? this.errors : []; + if (this.callback) { + this.callback(err); + } else if (err.code !== 'CONNECTION_BROKEN') { + // Exclude CONNECTION_BROKEN so that error won't be emitted twice + this._client.emit('error', err); } return; } @@ -1169,7 +1159,7 @@ Multi.prototype.execute_callback = function (err, replies) { } }; -Multi.prototype.callback = function (cb, command, i) { +Multi.prototype.callback = function (cb, i) { var self = this; return function (err, res) { if (err) { @@ -1210,9 +1200,9 @@ Multi.prototype.exec = Multi.prototype.EXEC = Multi.prototype.exec_batch = funct var command = args.shift(); var cb; if (typeof args[args.length - 1] === 'function') { - cb = this.callback(args.pop(), command, index); + cb = this.callback(args.pop(), index); } else { - cb = this.callback(undefined, command, index); + cb = this.callback(undefined, index); } if (callback && index === len - 1) { cb = lastCallback(cb); @@ -1241,7 +1231,7 @@ var createClient_tcp = function (port_arg, host_arg, options) { var cnxOptions = { port : port_arg || default_port, host : host_arg || default_host, - family : options && options.family === 'IPv6' ? 6 : 4 + family : options.family === 'IPv6' ? 6 : 4 }; var net_client = net.createConnection(cnxOptions); var redis_client = new RedisClient(net_client, options); @@ -1258,10 +1248,10 @@ var createClient = function (port_arg, host_arg, options) { return createClient_tcp(+options.port, options.host, options); } if (typeof port_arg === 'number' || typeof port_arg === 'string' && /^\d+$/.test(port_arg)) { - return createClient_tcp(port_arg, host_arg, options); + return createClient_tcp(port_arg, host_arg, options || {}); } if (typeof port_arg === 'string') { - options = host_arg || {}; + options = host_arg || options || {}; var parsed = URL.parse(port_arg, true, true); if (parsed.hostname) { diff --git a/lib/parsers/javascript.js b/lib/parsers/javascript.js index cfd89f20b6b..4eb863c3801 100644 --- a/lib/parsers/javascript.js +++ b/lib/parsers/javascript.js @@ -2,12 +2,10 @@ var util = require('util'); -function ReplyParser() { +function JavascriptReplyParser() { this.name = exports.name; - this._buffer = new Buffer(0); this._offset = 0; - this._encoding = 'utf-8'; } function IncompleteReadBuffer(message) { @@ -16,13 +14,13 @@ function IncompleteReadBuffer(message) { } util.inherits(IncompleteReadBuffer, Error); -ReplyParser.prototype._parseResult = function (type) { +JavascriptReplyParser.prototype._parseResult = function (type) { var start = 0, end = 0, offset = 0, packetHeader = 0; - if (type === 43 || type === 45) { // + or - + if (type === 43 || type === 58 || type === 45) { // + or : or - // up to the delimiter end = this._packetEndOffset() - 1; start = this._offset; @@ -34,24 +32,13 @@ ReplyParser.prototype._parseResult = function (type) { // include the delimiter this._offset = end + 2; - if (type === 45) { - return new Error(this._buffer.toString(this._encoding, start, end)); + if (type === 43) { + return this._buffer.slice(start, end); + } else if (type === 58) { + // return the coerced numeric value + return +this._buffer.toString('ascii', start, end); } - return this._buffer.slice(start, end); - } else if (type === 58) { // : - // up to the delimiter - end = this._packetEndOffset() - 1; - start = this._offset; - - if (end > this._buffer.length) { - throw new IncompleteReadBuffer('Wait for more data.'); - } - - // include the delimiter - this._offset = end + 2; - - // return the coerced numeric value - return +this._buffer.toString('ascii', start, end); + return new Error(this._buffer.toString('utf-8', start, end)); } else if (type === 36) { // $ // set a rewind point, as the packet could be larger than the // buffer in memory @@ -107,7 +94,7 @@ ReplyParser.prototype._parseResult = function (type) { } }; -ReplyParser.prototype.execute = function (buffer) { +JavascriptReplyParser.prototype.execute = function (buffer) { this.append(buffer); var type, ret, offset; @@ -161,7 +148,7 @@ ReplyParser.prototype.execute = function (buffer) { } }; -ReplyParser.prototype.append = function (newBuffer) { +JavascriptReplyParser.prototype.append = function (newBuffer) { // out of data if (this._offset >= this._buffer.length) { @@ -174,7 +161,7 @@ ReplyParser.prototype.append = function (newBuffer) { this._offset = 0; }; -ReplyParser.prototype.parseHeader = function () { +JavascriptReplyParser.prototype.parseHeader = function () { var end = this._packetEndOffset(), value = this._buffer.toString('ascii', this._offset, end - 1) | 0; @@ -183,7 +170,7 @@ ReplyParser.prototype.parseHeader = function () { return value; }; -ReplyParser.prototype._packetEndOffset = function () { +JavascriptReplyParser.prototype._packetEndOffset = function () { var offset = this._offset; while (this._buffer[offset] !== 0x0d && this._buffer[offset + 1] !== 0x0a) { @@ -199,9 +186,9 @@ ReplyParser.prototype._packetEndOffset = function () { return offset; }; -ReplyParser.prototype._bytesRemaining = function () { +JavascriptReplyParser.prototype._bytesRemaining = function () { return (this._buffer.length - this._offset) < 0 ? 0 : (this._buffer.length - this._offset); }; -exports.Parser = ReplyParser; +exports.Parser = JavascriptReplyParser; exports.name = 'javascript'; diff --git a/package.json b/package.json index b3477dc773a..5de9de70c04 100644 --- a/package.json +++ b/package.json @@ -4,7 +4,9 @@ "description": "Redis client library", "keywords": [ "database", - "redis" + "redis", + "transaction", + "pipelining" ], "author": "Matt Ranney ", "license": "MIT", @@ -13,13 +15,16 @@ "coveralls": "nyc report --reporter=text-lcov | coveralls", "coverage": "nyc report --reporter=html", "benchmark": "node benchmarks/multi_bench.js", - "test": "nyc ./node_modules/.bin/_mocha ./test/*.js ./test/commands/*.js ./test/parser/*.js --timeout=8000", + "test": "nyc ./node_modules/.bin/_mocha ./test/*.js ./test/commands/*.js --timeout=8000", "pretest": "optional-dev-dependency hiredis", "posttest": "jshint ." }, "dependencies": { "double-ended-queue": "^2.1.0-0" }, + "engines": { + "node": ">=0.10.0" + }, "devDependencies": { "coveralls": "^2.11.2", "jshint": "^2.8.0", diff --git a/test/commands/expire.spec.js b/test/commands/expire.spec.js index 42e01b2e409..a77a32b2f21 100644 --- a/test/commands/expire.spec.js +++ b/test/commands/expire.spec.js @@ -31,7 +31,7 @@ describe("The 'expire' method", function () { client.EXPIRE(["expiry key", "1"], helper.isNumber(1)); setTimeout(function () { client.exists(["expiry key"], helper.isNumber(0, done)); - }, 3000); + }, 1100); }); afterEach(function () { diff --git a/test/commands/get.spec.js b/test/commands/get.spec.js index 8bb985964d1..cfefe463513 100644 --- a/test/commands/get.spec.js +++ b/test/commands/get.spec.js @@ -37,6 +37,12 @@ describe("The 'get' method", function () { done(); }); }); + + it("reports an error promisified", function () { + return client.getAsync(key).then(assert, function (err) { + assert(err.message.match(/The connection has already been closed/)); + }); + }); }); describe("when connected", function () { @@ -76,7 +82,7 @@ describe("The 'get' method", function () { }]); }); - it("should not throw on a get without callback (even if it's not useful", function (done) { + it("should not throw on a get without callback (even if it's not useful)", function (done) { client.GET(key); client.on('error', function(err) { throw err; diff --git a/test/commands/multi.spec.js b/test/commands/multi.spec.js index 775bbcc150c..624998b42da 100644 --- a/test/commands/multi.spec.js +++ b/test/commands/multi.spec.js @@ -67,6 +67,50 @@ describe("The 'multi' method", function () { }); }); + describe("when connection is broken", function () { + var client; + + afterEach(function () { + client.end(); + }); + + it("return an error even if connection is in broken mode if callback is present", function (done) { + client = redis.createClient({ + host: 'somewhere', + port: 6379, + max_attempts: 1 + }); + + client.on('error', function(err) { + if (/Redis connection in broken state/.test(err.message)) { + done(); + } + }); + + client.multi([['set', 'foo', 'bar'], ['get', 'foo']]).exec(function (err, res) { + assert(/Redis connection in broken state/.test(err.message)); + assert.strictEqual(err.errors.length, 0); + }); + }); + + it("does not emit an error twice if connection is in broken mode with no callback", function (done) { + client = redis.createClient({ + host: 'somewhere', + port: 6379, + max_attempts: 1 + }); + + client.on('error', function(err) { + // Results in multiple done calls if test fails + if (/Redis connection in broken state/.test(err.message)) { + done(); + } + }); + + client.multi([['set', 'foo', 'bar'], ['get', 'foo']]).exec(); + }); + }); + describe("when ready", function () { var client; @@ -481,6 +525,18 @@ describe("The 'multi' method", function () { assert(!test); }); + it("do not mutate arguments in the multi constructor", function (done) { + helper.serverVersionAtLeast.call(this, client, [2, 6, 5]); + + var input = [['set', 'foo', 'bar'], ['get', 'foo']]; + client.multi(input).exec(function (err, res) { + assert.strictEqual(input.length, 2); + assert.strictEqual(input[0].length, 3); + assert.strictEqual(input[1].length, 2); + done(); + }); + }); + }); }); }); diff --git a/test/connection.spec.js b/test/connection.spec.js index 3def5eb15fa..c1a749051e5 100644 --- a/test/connection.spec.js +++ b/test/connection.spec.js @@ -5,106 +5,247 @@ var config = require("./lib/config"); var helper = require('./helper'); var redis = config.redis; -describe("on lost connection", function () { +describe("connection tests", function () { helper.allTests(function(parser, ip, args) { describe("using " + parser + " and " + ip, function () { - it("emit an error after max retry attempts and do not try to reconnect afterwards", function (done) { - var max_attempts = 4; - var client = redis.createClient({ - parser: parser, - max_attempts: max_attempts + var client; + + afterEach(function () { + if (client) { + client.end(); + } + }); + + describe("on lost connection", function () { + it("emit an error after max retry attempts and do not try to reconnect afterwards", function (done) { + var max_attempts = 4; + var options = { + parser: parser, + max_attempts: max_attempts + }; + client = redis.createClient(options); + assert.strictEqual(Object.keys(options).length, 2); + var calls = 0; + + client.once('ready', function() { + helper.killConnection(client); + }); + + client.on("reconnecting", function (params) { + calls++; + }); + + client.on('error', function(err) { + if (/Redis connection in broken state: maximum connection attempts.*?exceeded./.test(err.message)) { + setTimeout(function () { + assert.strictEqual(calls, max_attempts - 1); + done(); + }, 500); + } + }); }); - var calls = 0; - client.once('ready', function() { - helper.killConnection(client); + it("emit an error after max retry timeout and do not try to reconnect afterwards", function (done) { + var connect_timeout = 500; // in ms + client = redis.createClient({ + parser: parser, + connect_timeout: connect_timeout + }); + var time = 0; + + client.once('ready', function() { + helper.killConnection(client); + }); + + client.on("reconnecting", function (params) { + time += params.delay; + }); + + client.on('error', function(err) { + if (/Redis connection in broken state: connection timeout.*?exceeded./.test(err.message)) { + setTimeout(function () { + assert(time === connect_timeout); + done(); + }, 500); + } + }); }); - client.on("reconnecting", function (params) { - calls++; + it("end connection while retry is still ongoing", function (done) { + var connect_timeout = 1000; // in ms + client = redis.createClient({ + parser: parser, + connect_timeout: connect_timeout + }); + + client.once('ready', function() { + helper.killConnection(client); + }); + + client.on("reconnecting", function (params) { + client.end(); + setTimeout(done, 100); + }); }); - client.on('error', function(err) { - if (/Redis connection in broken state: maximum connection attempts.*?exceeded./.test(err.message)) { - setTimeout(function () { - assert.strictEqual(calls, max_attempts - 1); - done(); - }, 1500); - } + it("can not connect with wrong host / port in the options object", function (done) { + var options = { + host: 'somewhere', + port: 6379, + max_attempts: 1 + }; + client = redis.createClient(options); + assert.strictEqual(Object.keys(options).length, 3); + var end = helper.callFuncAfter(done, 2); + + client.on('error', function (err) { + assert(/CONNECTION_BROKEN|ENOTFOUND|EAI_AGAIN/.test(err.code)); + end(); + }); + }); }); - it("emit an error after max retry timeout and do not try to reconnect afterwards", function (done) { - var connect_timeout = 1000; // in ms - var client = redis.createClient({ - parser: parser, - connect_timeout: connect_timeout - }); - var time = 0; + describe("when not connected", function () { - client.once('ready', function() { - helper.killConnection(client); - }); + it("connect with host and port provided in the options object", function (done) { + client = redis.createClient({ + host: 'localhost', + port: '6379', + parser: parser, + connect_timeout: 1000 + }); - client.on("reconnecting", function (params) { - time += params.delay; + client.once('ready', function() { + done(); + }); }); - client.on('error', function(err) { - if (/Redis connection in broken state: connection timeout.*?exceeded./.test(err.message)) { - setTimeout(function () { - assert(time === connect_timeout); - done(); - }, 1500); - } - }); - }); + it("connects correctly with args", function (done) { + client = redis.createClient.apply(redis.createClient, args); + client.on("error", done); - it("end connection while retry is still ongoing", function (done) { - var connect_timeout = 1000; // in ms - var client = redis.createClient({ - parser: parser, - connect_timeout: connect_timeout + client.once("ready", function () { + client.removeListener("error", done); + client.get("recon 1", function (err, res) { + done(err); + }); + }); }); - client.once('ready', function() { - helper.killConnection(client); - }); + it("connects correctly with default values", function (done) { + client = redis.createClient(); + client.on("error", done); - client.on("reconnecting", function (params) { - client.end(); - setTimeout(done, 100); + client.once("ready", function () { + client.removeListener("error", done); + client.get("recon 1", function (err, res) { + done(err); + }); + }); }); - }); - it("can not connect with wrong host / port in the options object", function (done) { - var client = redis.createClient({ - host: 'somewhere', - port: 6379, - max_attempts: 1 - }); - var end = helper.callFuncAfter(done, 2); + it("connects with a port only", function (done) { + client = redis.createClient(6379); + client.on("error", done); - client.on('error', function (err) { - assert(/CONNECTION_BROKEN|ENOTFOUND|EAI_AGAIN/.test(err.code)); - end(); + client.once("ready", function () { + client.removeListener("error", done); + client.get("recon 1", function (err, res) { + done(err); + }); + }); }); - }); + it("connects correctly to localhost", function (done) { + client = redis.createClient(null, null); + client.on("error", done); - it("connect with host and port provided in the options object", function (done) { - var client = redis.createClient({ - host: 'localhost', - port: '6379', - parser: parser, - connect_timeout: 1000 + client.once("ready", function () { + client.removeListener("error", done); + client.get("recon 1", function (err, res) { + done(err); + }); + }); }); - client.once('ready', function() { - done(); + it("connects correctly to localhost and no ready check", function (done) { + client = redis.createClient(undefined, undefined, { + no_ready_check: true + }); + client.on("error", done); + + client.once("ready", function () { + client.set('foo', 'bar'); + client.get('foo', function(err, res) { + assert.strictEqual(res, 'bar'); + done(err); + }); + }); }); + + it("buffer commands and flush them after ", function (done) { + client = redis.createClient(9999, null, { + parser: parser + }); + + client.on('error', function(e) { + // ignore, b/c expecting a "can't connect" error + }); + + return setTimeout(function() { + client.set('foo', 'bar', function(err, result) { + // This should never be called + return done(err); + }); + + return setTimeout(function() { + assert.strictEqual(client.offline_queue.length, 1); + return done(); + }, 25); + }, 50); + }); + + it("throws on strange connection info", function () { + try { + redis.createClient(true); + throw new Error('failed'); + } catch (err) { + assert.equal(err.message, 'Unknown type of connection in createClient()'); + } + }); + + if (ip === 'IPv4') { + it('allows connecting with the redis url and the default port', function (done) { + client = redis.createClient('redis://foo:porkchopsandwiches@' + config.HOST[ip]); + client.on("ready", function () { + return done(); + }); + }); + + it('allows connecting with the redis url and no auth and options as second parameter', function (done) { + var options = { + detect_buffers: false + }; + client = redis.createClient('redis://' + config.HOST[ip] + ':' + config.PORT, options); + assert.strictEqual(Object.keys(options).length, 1); + client.on("ready", function () { + return done(); + }); + }); + + it('allows connecting with the redis url and no auth and options as third parameter', function (done) { + client = redis.createClient('redis://' + config.HOST[ip] + ':' + config.PORT, null, { + detect_buffers: false + }); + client.on("ready", function () { + return done(); + }); + }); + } + }); }); diff --git a/test/helper.js b/test/helper.js index 8402b9cfc02..a6899aad7af 100644 --- a/test/helper.js +++ b/test/helper.js @@ -122,9 +122,9 @@ module.exports = { } var options = [{ detect_buffers: true - // Somehow we need a undefined here - otherwise the parsers return_buffers value is always true - // Investigate this further - }, undefined]; + }, { + detect_buffers: false + }]; options.forEach(function (options) { var strOptions = ''; var key; diff --git a/test/node_redis.spec.js b/test/node_redis.spec.js index 64817c2c981..e89cddb3d32 100644 --- a/test/node_redis.spec.js +++ b/test/node_redis.spec.js @@ -8,132 +8,41 @@ var redis = config.redis; describe("The node_redis client", function () { + describe("testing parser existence", function () { + it('throws on non-existence', function (done) { + var mochaListener = helper.removeMochaListener(); + + process.once('uncaughtException', function (err) { + process.on('uncaughtException', mochaListener); + assert.equal(err.message, 'Couldn\'t find named parser nonExistingParser on this system'); + return done(); + }); + + redis.createClient({ + parser: 'nonExistingParser' + }); + }); + }); + helper.allTests({ allConnections: true }, function(parser, ip, args) { - if (args[2]) { // skip if options are undefined - describe("testing parser existence", function () { - it('throws on non-existence', function (done) { - var mochaListener = helper.removeMochaListener(); - - process.once('uncaughtException', function (err) { - process.on('uncaughtException', mochaListener); - assert.equal(err.message, 'Couldn\'t find named parser nonExistingParser on this system'); - return done(); - }); - - // Don't pollute the args for the other connections - var tmp = JSON.parse(JSON.stringify(args)); - tmp[2].parser = 'nonExistingParser'; - redis.createClient.apply(redis.createClient, tmp); - }); - }); - } - describe("using " + parser + " and " + ip, function () { var client; - describe("when not connected", function () { - afterEach(function () { - if (client) { - client.end(); - } - }); - - it("connects correctly with args", function (done) { - client = redis.createClient.apply(redis.createClient, args); - client.on("error", done); - - client.once("ready", function () { - client.removeListener("error", done); - client.get("recon 1", function (err, res) { - done(err); - }); - }); - }); - - it("connects correctly with default values", function (done) { - client = redis.createClient(); - client.on("error", done); - - client.once("ready", function () { - client.removeListener("error", done); - client.get("recon 1", function (err, res) { - done(err); - }); - }); - }); - - it("connects correctly to localhost", function (done) { - client = redis.createClient(null, null); - client.on("error", done); - - client.once("ready", function () { - client.removeListener("error", done); - client.get("recon 1", function (err, res) { - done(err); - }); - }); - }); - - it("connects correctly to localhost and no ready check", function (done) { - client = redis.createClient(undefined, undefined, { - no_ready_check: true - }); - client.on("error", done); - - client.once("ready", function () { - client.set('foo', 'bar'); - client.get('foo', function(err, res) { - assert.strictEqual(res, 'bar'); - done(err); - }); - }); - }); - - it("throws on strange connection info", function () { - try { - redis.createClient(true); - throw new Error('failed'); - } catch (err) { - assert.equal(err.message, 'Unknown type of connection in createClient()'); - } - }); - - if (ip === 'IPv4') { - it('allows connecting with the redis url and the default port', function (done) { - client = redis.createClient('redis://foo:porkchopsandwiches@' + config.HOST[ip]); - client.on("ready", function () { - return done(); - }); - }); - - it('allows connecting with the redis url and no auth', function (done) { - client = redis.createClient('redis://' + config.HOST[ip] + ':' + config.PORT, { - detect_buffers: false - }); - client.on("ready", function () { - return done(); - }); - }); - } - + afterEach(function () { + client.end(); }); describe("when connected", function () { beforeEach(function (done) { client = redis.createClient.apply(redis.createClient, args); - client.once("error", done); client.once("connect", function () { client.flushdb(done); }); }); - afterEach(function () { - client.end(); - }); - describe("send_command", function () { it("omitting args should be fine in some cases", function (done) { @@ -211,7 +120,9 @@ describe("The node_redis client", function () { it("return an error in the callback", function (done) { if (helper.redisProcess().spawnFailed()) this.skip(); - var client = redis.createClient(); + // TODO: Investigate why this test is failing hard and killing mocha if the client is created with .apply + // Seems like something is wrong while passing a socket connection to create client! args[1] + client = redis.createClient(); client.quit(function() { client.get("foo", function(err, res) { assert(err.message.indexOf('Redis connection gone') !== -1); @@ -224,7 +135,6 @@ describe("The node_redis client", function () { it("return an error in the callback version two", function (done) { if (helper.redisProcess().spawnFailed()) this.skip(); - var client = redis.createClient(); client.quit(); setTimeout(function() { client.get("foo", function(err, res) { @@ -239,7 +149,6 @@ describe("The node_redis client", function () { it("emit an error", function (done) { if (helper.redisProcess().spawnFailed()) this.skip(); - var client = redis.createClient(); client.quit(); client.on('error', function(err) { assert.strictEqual(err.message, 'SET can\'t be processed. The connection has already been closed.'); @@ -339,20 +248,19 @@ describe("The node_redis client", function () { return done(); } - if (domain) { - domain.run(function () { - client.set('domain', 'value', function (err, res) { - assert.ok(process.domain); - throw new Error('ohhhh noooo'); - }); + domain.run(function () { + client.set('domain', 'value', function (err, res) { + assert.ok(process.domain); + throw new Error('ohhhh noooo'); }); + }); - // this is the expected and desired behavior - domain.on('error', function (err) { - domain.exit(); - return done(); - }); - } + // this is the expected and desired behavior + domain.on('error', function (err) { + assert.strictEqual(err.message, 'ohhhh noooo'); + domain.exit(); + return done(); + }); }); }); }); @@ -435,7 +343,6 @@ describe("The node_redis client", function () { describe('socket_nodelay', function () { describe('true', function () { - var client; var args = config.configureClient(parser, ip, { socket_nodelay: true }); @@ -470,7 +377,6 @@ describe("The node_redis client", function () { }); describe('false', function () { - var client; var args = config.configureClient(parser, ip, { socket_nodelay: false }); @@ -505,7 +411,6 @@ describe("The node_redis client", function () { }); describe('defaults to true', function () { - var client; it("fires client.on('ready')", function (done) { client = redis.createClient.apply(redis.createClient, args); @@ -538,7 +443,6 @@ describe("The node_redis client", function () { }); describe('retry_max_delay', function () { - var client; var args = config.configureClient(parser, ip, { retry_max_delay: 1 // ms }); @@ -566,8 +470,36 @@ describe("The node_redis client", function () { describe('enable_offline_queue', function () { describe('true', function () { + it("should emit drain after info command and nothing to buffer", function (done) { + client = redis.createClient({ + parser: parser + }); + client.set('foo', 'bar'); + client.get('foo', function () { + assert(!client.should_buffer); + setTimeout(done, 25); + }); + client.on('drain', function() { + assert(client.offline_queue.length === 2); + }); + }); + + it("should emit drain if offline queue is flushed and nothing to buffer", function (done) { + client = redis.createClient({ + parser: parser, + no_ready_check: true + }); + var end = helper.callFuncAfter(done, 2); + client.set('foo', 'bar'); + client.get('foo', end); + client.on('drain', function() { + assert(client.offline_queue.length === 0); + end(); + }); + }); + it("does not return an error and enqueues operation", function (done) { - var client = redis.createClient(9999, null, { + client = redis.createClient(9999, null, { max_attempts: 0, parser: parser }); @@ -578,10 +510,8 @@ describe("The node_redis client", function () { return setTimeout(function() { client.set('foo', 'bar', function(err, result) { - // TODO: figure out why we emit an error on - // even though we've enabled the offline queue. - if (process.platform === 'win32') return; - if (err) return done(err); + // This should never be called + return done(err); }); return setTimeout(function() { @@ -592,7 +522,7 @@ describe("The node_redis client", function () { }); it("enqueues operation and keep the queue while trying to reconnect", function (done) { - var client = redis.createClient(9999, null, { + client = redis.createClient(9999, null, { max_attempts: 4, parser: parser }); @@ -627,8 +557,23 @@ describe("The node_redis client", function () { }); describe('false', function () { + + it('stream not writable', function(done) { + client = redis.createClient({ + parser: parser, + enable_offline_queue: false + }); + client.on('ready', function () { + client.stream.writable = false; + client.set('foo', 'bar', function (err, res) { + assert.strictEqual(err.message, "SET can't be processed. Stream not writeable."); + done(); + }); + }); + }); + it("emit an error and does not enqueues operation", function (done) { - var client = redis.createClient(9999, null, { + client = redis.createClient(9999, null, { parser: parser, max_attempts: 0, enable_offline_queue: false @@ -636,7 +581,7 @@ describe("The node_redis client", function () { var end = helper.callFuncAfter(done, 3); client.on('error', function(err) { - assert(/Stream not writeable|ECONNREFUSED/.test(err.message)); + assert(/offline queue is deactivated|ECONNREFUSED/.test(err.message)); assert.equal(client.command_queue.length, 0); end(); }); @@ -653,7 +598,7 @@ describe("The node_redis client", function () { }); it("flushes the command queue connection if in broken connection mode", function (done) { - var client = redis.createClient({ + client = redis.createClient({ parser: parser, max_attempts: 2, enable_offline_queue: false diff --git a/test/parser.spec.js b/test/parser.spec.js new file mode 100644 index 00000000000..f54181ba78c --- /dev/null +++ b/test/parser.spec.js @@ -0,0 +1,113 @@ +'use strict'; + +var assert = require('assert'); +var config = require("./lib/config"); +var utils = require("../lib/utils"); +var redis = config.redis; +var parsers = [ + require("../lib/parsers/javascript").Parser +]; +try { + // Test the hiredis parser if available + parsers.push(require("../lib/parsers/hiredis").Parser); +} catch (e) {} + +describe('parsers', function () { + + parsers.forEach(function (Parser) { + + describe(Parser.name, function () { + + it('handles multi-bulk reply', function (done) { + var parser = new Parser(); + var reply_count = 0; + function check_reply(reply) { + reply = utils.reply_to_strings(reply); + assert.deepEqual(reply, [['a']], "Expecting multi-bulk reply of [['a']]"); + reply_count++; + } + parser.send_reply = check_reply; + + parser.execute(new Buffer('*1\r\n*1\r\n$1\r\na\r\n')); + + parser.execute(new Buffer('*1\r\n*1\r')); + parser.execute(new Buffer('\n$1\r\na\r\n')); + + parser.execute(new Buffer('*1\r\n*1\r\n')); + parser.execute(new Buffer('$1\r\na\r\n')); + + assert.equal(reply_count, 3, "check reply should have been called three times"); + return done(); + }); + + it('parser error', function (done) { + var parser = new Parser(); + var reply_count = 0; + function check_reply(reply) { + assert.strictEqual(reply.message, 'Protocol error, got "a" as reply type byte'); + reply_count++; + } + parser.send_error = check_reply; + + parser.execute(new Buffer('a*1\r*1\r$1`zasd\r\na')); + + assert.equal(reply_count, 1, "check reply should have been called one time"); + return done(); + }); + + it('line breaks in the beginning', function (done) { + var parser = new Parser(); + var reply_count = 0; + function check_reply(reply) { + reply = utils.reply_to_strings(reply); + assert.deepEqual(reply, [['a']], "Expecting multi-bulk reply of [['a']]"); + reply_count++; + } + parser.send_reply = check_reply; + + parser.execute(new Buffer('*1\r\n*1\r\n$1\r\na')); + + parser.execute(new Buffer('\r\n*1\r\n*1\r')); + parser.execute(new Buffer('\n$1\r\na\r\n')); + + parser.execute(new Buffer('*1\r\n*1\r\n')); + parser.execute(new Buffer('$1\r\na\r\n')); + + assert.equal(reply_count, 3, "check reply should have been called three times"); + return done(); + }); + }); + }); + + // Activate this if you want to fry your cpu / memory + describe.skip("test out of memory", function () { + var args = config.configureClient('javascript', '127.0.0.1'); + var clients = new Array(300).join(" ").split(" "); + var client; + beforeEach(function (done) { + client = redis.createClient.apply(redis.createClient, args); + client.once("connect", function () { + client.flushdb(done); + }); + }); + + it('reach limit and wait for further data', function (done) { + setTimeout(done, 5000); + clients.forEach(function(entry, a) { + var max = 0; + var client = redis.createClient.apply(redis.createClient, args); + client.on('ready', function() { + while (++max < 50) { + var item = []; + for (var i = 0; i < 100; ++i) { + item.push('aaa' + (Math.random() * 1000000 | 0)); + } + client.del('foo' + a); + client.lpush('foo' + a, item); + client.lrange('foo' + a, 0, 99); + } + }); + }); + }); + }); +}); diff --git a/test/parser/javascript.spec.js b/test/parser/javascript.spec.js deleted file mode 100644 index 499cfb11ca7..00000000000 --- a/test/parser/javascript.spec.js +++ /dev/null @@ -1,63 +0,0 @@ -'use strict'; - -var assert = require('assert'); -var Parser = require("../../lib/parsers/javascript").Parser; -var config = require("../lib/config"); -var utils = require("../../lib/utils"); -var redis = config.redis; - -describe('javascript parser', function () { - it('handles multi-bulk reply', function (done) { - var parser = new Parser(); - var reply_count = 0; - function check_reply(reply) { - reply = utils.reply_to_strings(reply); - assert.deepEqual(reply, [['a']], "Expecting multi-bulk reply of [['a']]"); - reply_count++; - } - parser.send_reply = check_reply; - - parser.execute(new Buffer('*1\r\n*1\r\n$1\r\na\r\n')); - - parser.execute(new Buffer('*1\r\n*1\r')); - parser.execute(new Buffer('\n$1\r\na\r\n')); - - parser.execute(new Buffer('*1\r\n*1\r\n')); - parser.execute(new Buffer('$1\r\na\r\n')); - - assert.equal(reply_count, 3, "check reply should have been called three times"); - return done(); - }); - - // Activate this if you want to fry your cpu / memory - describe.skip("test out of memory", function () { - var args = config.configureClient('javascript', '127.0.0.1'); - var clients = new Array(300).join(" ").split(" "); - var client; - beforeEach(function (done) { - client = redis.createClient.apply(redis.createClient, args); - client.once("connect", function () { - client.flushdb(done); - }); - }); - - it('reach limit and wait for further data', function (done) { - setTimeout(done, 5000); - clients.forEach(function(entry, a) { - var max = 0; - var client = redis.createClient.apply(redis.createClient, args); - client.on('ready', function() { - while (++max < 50) { - var item = []; - for (var i = 0; i < 100; ++i) { - item.push('aaa' + (Math.random() * 1000000 | 0)); - } - client.del('foo' + a); - client.lpush('foo' + a, item); - client.lrange('foo' + a, 0, 99); - } - }); - }); - }); - }); -}); diff --git a/test/pubsub.spec.js b/test/pubsub.spec.js index 37d9e8bfadc..f7dbd57fe43 100644 --- a/test/pubsub.spec.js +++ b/test/pubsub.spec.js @@ -242,8 +242,12 @@ describe("publish/subscribe", function () { }); }); - it('does not complain when unsubscribe is called and there are no subscriptions', function () { - sub.unsubscribe(); + it('does not complain when unsubscribe is called and there are no subscriptions', function (done) { + sub.unsubscribe(function (err, res) { + assert.strictEqual(err, null); + assert.strictEqual(res, null); + done(); + }); }); it('executes callback when unsubscribe is called and there are no subscriptions', function (done) { @@ -285,6 +289,34 @@ describe("publish/subscribe", function () { }); }); + describe('fail for other commands while in pub sub mode', function () { + it('return error if only pub sub commands are allowed', function (done) { + sub.subscribe('channel'); + // Ping is allowed even if not listed as such! + sub.ping(function (err, res) { + assert.strictEqual(err, null); + assert.strictEqual(res[0], 'pong'); + }); + // Get is forbidden + sub.get('foo', function (err, res) { + assert.strictEqual(err.message, 'ERR only (P)SUBSCRIBE / (P)UNSUBSCRIBE / QUIT allowed in this context'); + assert.strictEqual(err.command, 'GET'); + }); + // Quit is allowed + sub.quit(done); + }); + + it('emit error if only pub sub commands are allowed without callback', function (done) { + sub.subscribe('channel'); + sub.on('error', function (err) { + assert.strictEqual(err.message, 'ERR only (P)SUBSCRIBE / (P)UNSUBSCRIBE / QUIT allowed in this context'); + assert.strictEqual(err.command, 'GET'); + done(); + }); + sub.get('foo'); + }); + }); + // TODO: Fix pub sub // And there's more than just those two issues describe.skip('FIXME: broken pub sub', function () { @@ -297,7 +329,7 @@ describe("publish/subscribe", function () { pub.on('message', function (msg) { done(new Error('This message should not have been published: ' + msg)); }); - setTimeout(done, 500); + setTimeout(done, 200); }); it("should not publish a message multiple times per command", function (done) { @@ -325,8 +357,8 @@ describe("publish/subscribe", function () { sub.unsubscribe(); setTimeout(function () { subscribe('world'); - }, 400); - }, 400); + }, 40); + }, 40); }); }); diff --git a/test/return_buffers.spec.js b/test/return_buffers.spec.js index f0f282f9d8f..24dc6f30d2f 100644 --- a/test/return_buffers.spec.js +++ b/test/return_buffers.spec.js @@ -19,6 +19,8 @@ describe("return_buffers", function () { beforeEach(function (done) { client = redis.createClient.apply(redis.createClient, args); if (args[2].detect_buffers) { + // Test if detect_buffer option was deactivated + assert.strictEqual(client.options.detect_buffers, false); args[2].detect_buffers = false; } client.once("error", done); From ce4a67bb746e5ea5736c61d2524645ad9f3aaa73 Mon Sep 17 00:00:00 2001 From: Ruben Bridgewater Date: Sat, 17 Oct 2015 22:07:34 +0200 Subject: [PATCH 0439/1748] v.2.2.4 --- README.md | 2 +- package.json | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/README.md b/README.md index ec4f9ebf4ec..e2252c20c84 100644 --- a/README.md +++ b/README.md @@ -175,7 +175,7 @@ port and host are probably fine and you don't need to supply any arguments. `cre * `redis.createClient('redis://user:pass@host:port', options)` * `redis.createClient(port, host, options)` -### `options` is an object with the following possible properties: +#### `options` is an object with the following possible properties: * `host`: *127.0.0.1*; The host to connect to * `port`: *6370*; The port to connect to * `parser`: *hiredis*; Which Redis protocol reply parser to use. If `hiredis` is not installed it will fallback to `javascript`. diff --git a/package.json b/package.json index 5de9de70c04..21fd63aec9f 100644 --- a/package.json +++ b/package.json @@ -1,6 +1,6 @@ { "name": "redis", - "version": "2.2.3", + "version": "2.2.4", "description": "Redis client library", "keywords": [ "database", From 304abe431828b1931c32187d4888f460ba99f8c5 Mon Sep 17 00:00:00 2001 From: Ruben Bridgewater Date: Sun, 18 Oct 2015 16:58:34 +0200 Subject: [PATCH 0440/1748] Fix individual createClient functions passing undefined options to a new instance. Closes #893 --- changelog.md | 6 ++++++ index.js | 2 +- test/connection.spec.js | 32 +++++++++++--------------------- 3 files changed, 18 insertions(+), 22 deletions(-) diff --git a/changelog.md b/changelog.md index 5c757c21993..d0cc69f8b60 100644 --- a/changelog.md +++ b/changelog.md @@ -1,6 +1,12 @@ Changelog ========= +## v.2.2.5 - 18 Oct, 2015 + +Bugfixes + +- Fixed undefined options passed to a new instance not accepted (possible with individual .createClient functions) ([@BridgeAR](https://github.com/BridgeAR)) + ## v.2.2.4 - 17 Oct, 2015 Bugfixes diff --git a/index.js b/index.js index f70e85b6d52..ebe44d969ab 100644 --- a/index.js +++ b/index.js @@ -36,7 +36,7 @@ parsers.push(require('./lib/parsers/javascript')); function RedisClient(stream, options) { // Copy the options so they are not mutated - options = JSON.parse(JSON.stringify(options)); + options = JSON.parse(JSON.stringify(options || {})); var self = this; if (!stream.cork) { diff --git a/test/connection.spec.js b/test/connection.spec.js index c1a749051e5..0ea6e16613b 100644 --- a/test/connection.spec.js +++ b/test/connection.spec.js @@ -4,6 +4,7 @@ var assert = require("assert"); var config = require("./lib/config"); var helper = require('./helper'); var redis = config.redis; +var net = require('net'); describe("connection tests", function () { helper.allTests(function(parser, ip, args) { @@ -186,27 +187,16 @@ describe("connection tests", function () { }); }); - it("buffer commands and flush them after ", function (done) { - client = redis.createClient(9999, null, { - parser: parser - }); - - client.on('error', function(e) { - // ignore, b/c expecting a "can't connect" error - }); - - return setTimeout(function() { - client.set('foo', 'bar', function(err, result) { - // This should never be called - return done(err); - }); - - return setTimeout(function() { - assert.strictEqual(client.offline_queue.length, 1); - return done(); - }, 25); - }, 50); - }); + it("works with missing options object for new redis instances", function () { + // This is needed for libraries that have their own createClient function like fakeredis + var cnxOptions = { + port : 6379, + host : '127.0.0.1', + family : ip === 'IPv6' ? 6 : 4 + }; + var net_client = net.createConnection(cnxOptions); + client = new redis.RedisClient(net_client); + }); it("throws on strange connection info", function () { try { From 851dd8720684ed084e069f9d7659b9e736033fdc Mon Sep 17 00:00:00 2001 From: Ruben Bridgewater Date: Sun, 18 Oct 2015 16:58:48 +0200 Subject: [PATCH 0441/1748] v.2.2.5 --- package.json | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/package.json b/package.json index 21fd63aec9f..b5cbe13d525 100644 --- a/package.json +++ b/package.json @@ -1,6 +1,6 @@ { "name": "redis", - "version": "2.2.4", + "version": "2.2.5", "description": "Redis client library", "keywords": [ "database", From 9b3b0901198c509b8392150d5a5813d654b24c9f Mon Sep 17 00:00:00 2001 From: Ruben Bridgewater Date: Tue, 20 Oct 2015 10:30:41 +0200 Subject: [PATCH 0442/1748] Simplify parser and remove dead code --- lib/parsers/javascript.js | 35 +++++++++-------------------------- 1 file changed, 9 insertions(+), 26 deletions(-) diff --git a/lib/parsers/javascript.js b/lib/parsers/javascript.js index 4eb863c3801..7da5c3ed147 100644 --- a/lib/parsers/javascript.js +++ b/lib/parsers/javascript.js @@ -22,13 +22,9 @@ JavascriptReplyParser.prototype._parseResult = function (type) { if (type === 43 || type === 58 || type === 45) { // + or : or - // up to the delimiter - end = this._packetEndOffset() - 1; + end = this._packetEndOffset(); start = this._offset; - if (end > this._buffer.length) { - throw new IncompleteReadBuffer('Wait for more data.'); - } - // include the delimiter this._offset = end + 2; @@ -91,13 +87,15 @@ JavascriptReplyParser.prototype._parseResult = function (type) { } return reply; + } else { + return null; } }; JavascriptReplyParser.prototype.execute = function (buffer) { this.append(buffer); - var type, ret, offset; + var type, offset; while (true) { offset = this._offset; @@ -109,30 +107,16 @@ JavascriptReplyParser.prototype.execute = function (buffer) { try { type = this._buffer[this._offset++]; - if (type === 43) { // Strings + - ret = this._parseResult(type); - - this.send_reply(ret); + if (type === 43 || type === 58 || type === 36) { // Strings + // Integers : // Bulk strings $ + this.send_reply(this._parseResult(type)); } else if (type === 45) { // Errors - - ret = this._parseResult(type); - - this.send_error(ret); - } else if (type === 58) { // Integers : - ret = this._parseResult(type); - - this.send_reply(ret); - } else if (type === 36) { // Bulk strings $ - ret = this._parseResult(type); - - this.send_reply(ret); + this.send_error(this._parseResult(type)); } else if (type === 42) { // Arrays * // set a rewind point. if a failure occurs, // wait for the next execute()/append() and try again offset = this._offset - 1; - ret = this._parseResult(type); - - this.send_reply(ret); + this.send_reply(this._parseResult(type)); } else if (type === 10 || type === 13) { break; } else { @@ -162,7 +146,7 @@ JavascriptReplyParser.prototype.append = function (newBuffer) { }; JavascriptReplyParser.prototype.parseHeader = function () { - var end = this._packetEndOffset(), + var end = this._packetEndOffset() + 1, value = this._buffer.toString('ascii', this._offset, end - 1) | 0; this._offset = end + 1; @@ -182,7 +166,6 @@ JavascriptReplyParser.prototype._packetEndOffset = function () { } } - offset++; return offset; }; From 5d08132f7ce6dd77507afc20d8998c2d0983c59a Mon Sep 17 00:00:00 2001 From: Ruben Bridgewater Date: Tue, 27 Oct 2015 11:13:43 +0100 Subject: [PATCH 0443/1748] Fix: do not stop parsing a chunk if the first character is a line break Add changelog entry --- changelog.md | 4 ++++ lib/parsers/javascript.js | 4 +--- test/parser.spec.js | 7 ++----- 3 files changed, 7 insertions(+), 8 deletions(-) diff --git a/changelog.md b/changelog.md index d0cc69f8b60..ef585e082d8 100644 --- a/changelog.md +++ b/changelog.md @@ -1,6 +1,10 @@ Changelog ========= +Bugfixes + +- Fixed a js parser error that could result in a timeout ([@BridgeAR](https://github.com/BridgeAR)) + ## v.2.2.5 - 18 Oct, 2015 Bugfixes diff --git a/lib/parsers/javascript.js b/lib/parsers/javascript.js index 7da5c3ed147..5d0208b46bc 100644 --- a/lib/parsers/javascript.js +++ b/lib/parsers/javascript.js @@ -117,9 +117,7 @@ JavascriptReplyParser.prototype.execute = function (buffer) { offset = this._offset - 1; this.send_reply(this._parseResult(type)); - } else if (type === 10 || type === 13) { - break; - } else { + } else if (type !== 10 && type !== 13) { var err = new Error('Protocol error, got "' + String.fromCharCode(type) + '" as reply type byte'); this.send_error(err); } diff --git a/test/parser.spec.js b/test/parser.spec.js index f54181ba78c..01dd38e5391 100644 --- a/test/parser.spec.js +++ b/test/parser.spec.js @@ -55,7 +55,7 @@ describe('parsers', function () { return done(); }); - it('line breaks in the beginning', function (done) { + it('line breaks in the beginning of the last chunk', function (done) { var parser = new Parser(); var reply_count = 0; function check_reply(reply) { @@ -68,10 +68,7 @@ describe('parsers', function () { parser.execute(new Buffer('*1\r\n*1\r\n$1\r\na')); parser.execute(new Buffer('\r\n*1\r\n*1\r')); - parser.execute(new Buffer('\n$1\r\na\r\n')); - - parser.execute(new Buffer('*1\r\n*1\r\n')); - parser.execute(new Buffer('$1\r\na\r\n')); + parser.execute(new Buffer('\n$1\r\na\r\n*1\r\n*1\r\n$1\r\na\r\n')); assert.equal(reply_count, 3, "check reply should have been called three times"); return done(); From 938ed7c7533ea7b97b422a15daaf0abed26de2be Mon Sep 17 00:00:00 2001 From: Ruben Bridgewater Date: Tue, 27 Oct 2015 00:37:49 +0100 Subject: [PATCH 0444/1748] Add consolidation part to the readme --- README.md | 12 +++++++++++- 1 file changed, 11 insertions(+), 1 deletion(-) diff --git a/README.md b/README.md index e2252c20c84..5eb25dddce1 100644 --- a/README.md +++ b/README.md @@ -742,4 +742,14 @@ WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. -Originally developed by Matthew Ranney, http://ranney.com/ \ No newline at end of file +Originally developed by Matthew Ranney, http://ranney.com/ + +### Consolidation: It's time for celebration + +Right now there are two great redis clients around and both have some advantages above each other. We speak about ioredis and node_redis. So after talking to each other about how we could improve in working together we (that is @luin and @BridgeAR) decided to work towards a single library on the long run. But step by step. + +First of all, we want to split small parts of our libraries into others so that we're both able to use the same code. Those libraries are going to be maintained under the NodeRedis organization. This is going to reduce the maintance overhead, allows others to use the very same code, if they need it and it's way easyer for others to contribute to both libraries. + +We're very happy about this step towards working together as we both want to give you the best redis experience possible. + +If you want to join our cause by help maintaining something, please don't hesitate to contact either one of us. From 7befc24907edd6e2e0b98116787b71e6e05e75ed Mon Sep 17 00:00:00 2001 From: Feodor Fitsner Date: Wed, 28 Oct 2015 15:27:44 -0700 Subject: [PATCH 0445/1748] Testing in AppVeyor This is how to always test against latest io.js 3.x and Node.js 4.x --- appveyor.yml | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/appveyor.yml b/appveyor.yml index f7d70bb1894..60124f7ba1a 100644 --- a/appveyor.yml +++ b/appveyor.yml @@ -5,8 +5,8 @@ environment: matrix: - nodejs_version: "0.10" - nodejs_version: "0.12" - - nodejs_version: "3.0" - - nodejs_version: "4.0" + - nodejs_version: "3" + - nodejs_version: "4" # Install scripts. (runs after repo cloning) install: From d5628f414aa0e7c1ad3bc4c900fc1c0e53b007d7 Mon Sep 17 00:00:00 2001 From: Ruben Bridgewater Date: Mon, 26 Oct 2015 19:33:40 +0100 Subject: [PATCH 0446/1748] Move the license in the LICENSE file and update the contributors section --- LICENSE | 24 ++++++++++++++++++++++++ README.md | 38 ++++++++++---------------------------- 2 files changed, 34 insertions(+), 28 deletions(-) create mode 100644 LICENSE diff --git a/LICENSE b/LICENSE new file mode 100644 index 00000000000..777c0b82086 --- /dev/null +++ b/LICENSE @@ -0,0 +1,24 @@ +## LICENSE - "MIT License" + +Copyright (c) 2015 by NodeRedis + +Permission is hereby granted, free of charge, to any person +obtaining a copy of this software and associated documentation +files (the "Software"), to deal in the Software without +restriction, including without limitation the rights to use, +copy, modify, merge, publish, distribute, sublicense, and/or sell +copies of the Software, and to permit persons to whom the +Software is furnished to do so, subject to the following +conditions: + +The above copyright notice and this permission notice shall be +included in all copies or substantial portions of the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, +EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES +OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND +NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT +HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, +WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING +FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR +OTHER DEALINGS IN THE SOFTWARE. \ No newline at end of file diff --git a/README.md b/README.md index 5eb25dddce1..3a1590d54cf 100644 --- a/README.md +++ b/README.md @@ -715,34 +715,16 @@ To get debug output run your `node_redis` application with `NODE_DEBUG=redis`. - Please be aware that we'll only accept fully tested code. ## Contributors -Many [people](https://github.com/NodeRedis/node_redis/graphs/contributors) have have added features and fixed bugs in `node_redis`. Thanks to all of them! - -## LICENSE - "MIT License" - -Copyright (c) by NodeRedis - -Permission is hereby granted, free of charge, to any person -obtaining a copy of this software and associated documentation -files (the "Software"), to deal in the Software without -restriction, including without limitation the rights to use, -copy, modify, merge, publish, distribute, sublicense, and/or sell -copies of the Software, and to permit persons to whom the -Software is furnished to do so, subject to the following -conditions: - -The above copyright notice and this permission notice shall be -included in all copies or substantial portions of the Software. - -THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, -EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES -OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND -NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT -HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, -WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING -FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR -OTHER DEALINGS IN THE SOFTWARE. - -Originally developed by Matthew Ranney, http://ranney.com/ + +The original author of node_redis is [Matthew Ranney](https://github.com/mranney) + +The current lead maintainer is [Ruben Bridgewater](https://github.com/BridgeAR) + +Many [others](https://github.com/NodeRedis/node_redis/graphs/contributors) contributed to `node_redis` too. Thanks to all of them! + +## License + +[MIT](LICENSE) ### Consolidation: It's time for celebration From 3142af584724178c61b74217f65a65c028386a04 Mon Sep 17 00:00:00 2001 From: Ruben Bridgewater Date: Mon, 26 Oct 2015 19:34:37 +0100 Subject: [PATCH 0447/1748] Update benchmark and diff output --- benchmarks/diff_multi_bench_output.js | 101 +++++++++++++------------- benchmarks/multi_bench.js | 99 +++++++++++++++---------- 2 files changed, 111 insertions(+), 89 deletions(-) diff --git a/benchmarks/diff_multi_bench_output.js b/benchmarks/diff_multi_bench_output.js index fb2a4a66ce7..6ba8c6900af 100755 --- a/benchmarks/diff_multi_bench_output.js +++ b/benchmarks/diff_multi_bench_output.js @@ -1,50 +1,55 @@ -#!/usr/bin/env node - 'use strict'; -/* jshint -W079: Ignore redefinitions (before & after) */ - -var fs = require('fs'), - metrics = require('metrics'), +var fs = require('fs'); +var metrics = require('metrics'); + // `node diff_multi_bench_output.js beforeBench.txt afterBench.txt` +var file1 = process.argv[2]; +var file2 = process.argv[3]; - // `node diff_multi_bench_output.js before.txt after.txt` - before = process.argv[2], - after = process.argv[3]; - -if (!before || !after) { +if (!file1 || !file2) { console.log('Please supply two file arguments:'); var n = __filename; n = n.substring(n.lastIndexOf('/', n.length)); - console.log(' ./' + n + ' multiBenchBefore.txt multiBenchAfter.txt'); - console.log('To generate multiBenchBefore.txt, run'); - console.log(' node multi_bench.js > multiBenchBefore.txt'); + console.log(' node .' + n + ' benchBefore.txt benchAfter.txt\n'); + console.log('To generate the benchmark files, run'); + console.log(' npm run benchmark > benchBefore.txt\n'); console.log('Thank you for benchmarking responsibly.'); return; } -var before_lines = fs.readFileSync(before, 'utf8').split('\n'), - after_lines = fs.readFileSync(after, 'utf8').split('\n'); - -console.log('Comparing before,', before.green, '(', before_lines.length, - 'lines)', 'to after,', after.green, '(', after_lines.length, 'lines)'); - +var before_lines = fs.readFileSync(file1, 'utf8').split('\n'); +var after_lines = fs.readFileSync(file2, 'utf8').split('\n'); var total_ops = new metrics.Histogram.createUniformHistogram(); +console.log('Comparing before,', file1, '(', before_lines.length, 'lines)', 'to after,', file2, '(', after_lines.length, 'lines)'); + function is_whitespace(s) { return !!s.trim(); } -function parseInt10(s) { - return parseInt(s, 10); +function pad(input, len, chr, right) { + var str = input.toString(); + chr = chr || ' '; + + if (right) { + while (str.length < len) { + str += chr; + } + } else { + while (str.length < len) { + str = chr + str; + } + } + return str; } // green if greater than 0, red otherwise -function humanize_diff(num, unit) { +function humanize_diff(num, unit, toFixed) { unit = unit || ''; if (num > 0) { - return ('+' + num + unit).green; + return ' +' + pad(num.toFixed(toFixed || 0) + unit, 7); } - return ('' + num + unit).red; + return ' -' + pad(Math.abs(num).toFixed(toFixed || 0) + unit, 7); } function command_name(words) { @@ -58,35 +63,33 @@ before_lines.forEach(function(b, i) { // console.log('#ignored#', '>'+a+'<', '>'+b+'<'); return; } - var b_words = b.split(' ').filter(is_whitespace); var a_words = a.split(' ').filter(is_whitespace); - var ops = - [b_words, a_words] - .map(function(words) { - // console.log(words); - return parseInt10(words.slice(-2, -1)); - }).filter(function(num) { - var isNaN = !num && num !== 0; - return !isNaN; - }); - if (ops.length !== 2) return; - + var ops = [b_words, a_words].map(function(words) { + // console.log(words); + return words.slice(-2, -1) | 0; + }).filter(function(num) { + var isNaN = !num && num !== 0; + return !isNaN; + }); + if (ops.length !== 2) { + return; + } var delta = ops[1] - ops[0]; - var pct = ((delta / ops[0]) * 100).toPrecision(3); - + var pct = +((delta / ops[0]) * 100); + ops[0] = pad(ops[0], 6); + ops[1] = pad(ops[1], 6); total_ops.update(delta); - delta = humanize_diff(delta); - pct = humanize_diff(pct, '%'); - console.log( - // name of test - command_name(a_words) === command_name(b_words) ? - command_name(a_words) + ':' : - '404:', - // results of test - ops.join(' -> '), 'ops/sec (∆', delta, pct, ')'); + var small_delta = pct < 3 && pct > -3; + // Let's mark differences above 20% bold + var big_delta = pct > 20 || pct < -20 ? ';1' : ''; + pct = humanize_diff(pct, '', 2) + '%'; + var str = pad((command_name(a_words) === command_name(b_words) ? command_name(a_words) + ':' : '404:'), 14, false, true) + + (pad(ops.join(' -> '), 15) + ' ops/sec (∆' + delta + pct + ')'); + str = (small_delta ? '' : (/-[^>]/.test(str) ? '\x1b[31' : '\x1b[32') + big_delta + 'm') + str + '\x1b[0m'; + console.log(str); }); -console.log('Mean difference in ops/sec:', humanize_diff(total_ops.mean().toPrecision(6))); +console.log('Mean difference in ops/sec:', humanize_diff(total_ops.mean(), '', 1)); diff --git a/benchmarks/multi_bench.js b/benchmarks/multi_bench.js index 62b79668ef9..43831414de6 100644 --- a/benchmarks/multi_bench.js +++ b/benchmarks/multi_bench.js @@ -7,24 +7,33 @@ var client_nr = 0; var redis = require('../index'); var totalTime = 0; var metrics = require('metrics'); -var num_clients = parseInt(process.argv[2], 10) || 5; -var num_requests = 50000; var tests = []; // var bluebird = require('bluebird'); // bluebird.promisifyAll(redis.RedisClient.prototype); // bluebird.promisifyAll(redis.Multi.prototype); + +function returnArg (name, def) { + var matches = process.argv.filter(function(entry) { + return entry.indexOf(name + '=') === 0; + }); + if (matches.length) { + return matches[0].substr(name.length + 1); + } + return def; +} +var num_clients = returnArg('clients', 1); +var run_time = returnArg('time', 2500); // ms var versions_logged = false; var client_options = { return_buffers: false, max_attempts: 4, - parser: process.argv.indexOf('parser=javascript') === -1 ? 'hiredis' : 'javascript' + parser: returnArg('parser', 'hiredis') }; var small_str, large_str, small_buf, large_buf, very_large_str, very_large_buf; function lpad(input, len, chr) { var str = input.toString(); chr = chr || ' '; - while (str.length < len) { str = chr + str; } @@ -33,23 +42,19 @@ function lpad(input, len, chr) { metrics.Histogram.prototype.print_line = function () { var obj = this.printObj(); - return lpad(obj.min, 4) + '/' + lpad(obj.max, 4) + '/' + lpad(obj.mean.toFixed(2), 7) + '/' + lpad(obj.p95.toFixed(2), 7); }; function Test(args) { this.args = args; - this.callback = null; this.clients = []; this.clients_ready = 0; this.commands_sent = 0; this.commands_completed = 0; - this.max_pipeline = this.args.pipeline || num_requests; + this.max_pipeline = this.args.pipeline || 50; this.batch_pipeline = this.args.batch || 0; this.client_options = args.client_options || client_options; - this.num_requests = args.reqs || num_requests; - this.connect_latency = new metrics.Histogram(); this.ready_latency = new metrics.Histogram(); this.command_latency = new metrics.Histogram(); @@ -57,9 +62,7 @@ function Test(args) { Test.prototype.run = function (callback) { var i; - this.callback = callback; - for (i = 0; i < num_clients ; i++) { this.new_client(i); } @@ -112,27 +115,39 @@ Test.prototype.new_client = function (id) { Test.prototype.on_clients_ready = function () { process.stdout.write(lpad(this.args.descr, 13) + ', ' + (this.args.batch ? lpad('batch ' + this.args.batch, 9) : lpad(this.args.pipeline, 9)) + '/' + this.clients_ready + ' '); this.test_start = Date.now(); - this.fill_pipeline(); }; Test.prototype.fill_pipeline = function () { var pipeline = this.commands_sent - this.commands_completed; - if (this.batch_pipeline && this.commands_sent < this.num_requests) { + if (this.test_start < Date.now() - run_time) { + if (this.ended) { + return; + } + this.ended = true; + this.print_stats(); + this.stop_clients(); + return; + } + + if (this.clients[0].should_buffer) { + var self = this; + setTimeout(function() { + self.fill_pipeline(); + }, 1); + return; + } + + if (this.batch_pipeline) { this.batch(); } else { - while (this.commands_sent < this.num_requests && pipeline < this.max_pipeline) { + while (pipeline < this.max_pipeline) { this.commands_sent++; pipeline++; this.send_next(); } } - - if (this.commands_completed === this.num_requests) { - this.print_stats(); - this.stop_clients(); - } }; Test.prototype.batch = function () { @@ -191,7 +206,7 @@ Test.prototype.print_stats = function () { totalTime += duration; console.log('min/max/avg/p95: ' + this.command_latency.print_line() + ' ' + lpad(duration, 6) + 'ms total, ' + - lpad((this.num_requests / (duration / 1000)).toFixed(2), 9) + ' ops/sec'); + lpad((this.commands_completed / (duration / 1000)).toFixed(2), 9) + ' ops/sec'); }; small_str = '1234'; @@ -203,19 +218,19 @@ very_large_buf = new Buffer(very_large_str); tests.push(new Test({descr: 'PING', command: 'ping', args: [], pipeline: 1})); tests.push(new Test({descr: 'PING', command: 'ping', args: [], pipeline: 50})); -tests.push(new Test({descr: 'PING', command: 'ping', args: [], batch: 50, reqs: num_requests * 2})); +tests.push(new Test({descr: 'PING', command: 'ping', args: [], batch: 50})); tests.push(new Test({descr: 'SET 4B str', command: 'set', args: ['foo_rand000000000000', small_str], pipeline: 1})); tests.push(new Test({descr: 'SET 4B str', command: 'set', args: ['foo_rand000000000000', small_str], pipeline: 50})); -tests.push(new Test({descr: 'SET 4B str', command: 'set', args: ['foo_rand000000000000', small_str], batch: 50, reqs: num_requests * 2})); +tests.push(new Test({descr: 'SET 4B str', command: 'set', args: ['foo_rand000000000000', small_str], batch: 50})); tests.push(new Test({descr: 'SET 4B buf', command: 'set', args: ['foo_rand000000000000', small_buf], pipeline: 1})); tests.push(new Test({descr: 'SET 4B buf', command: 'set', args: ['foo_rand000000000000', small_buf], pipeline: 50})); -tests.push(new Test({descr: 'SET 4B buf', command: 'set', args: ['foo_rand000000000000', small_buf], batch: 50, reqs: num_requests * 2})); +tests.push(new Test({descr: 'SET 4B buf', command: 'set', args: ['foo_rand000000000000', small_buf], batch: 50})); tests.push(new Test({descr: 'GET 4B str', command: 'get', args: ['foo_rand000000000000'], pipeline: 1})); tests.push(new Test({descr: 'GET 4B str', command: 'get', args: ['foo_rand000000000000'], pipeline: 50})); -tests.push(new Test({descr: 'GET 4B str', command: 'get', args: ['foo_rand000000000000'], batch: 50, reqs: num_requests * 2})); +tests.push(new Test({descr: 'GET 4B str', command: 'get', args: ['foo_rand000000000000'], batch: 50})); tests.push(new Test({descr: 'GET 4B buf', command: 'get', args: ['foo_rand000000000000'], pipeline: 1, client_opts: { return_buffers: true} })); tests.push(new Test({descr: 'GET 4B buf', command: 'get', args: ['foo_rand000000000000'], pipeline: 50, client_opts: { return_buffers: true} })); @@ -223,15 +238,15 @@ tests.push(new Test({descr: 'GET 4B buf', command: 'get', args: ['foo_rand000000 tests.push(new Test({descr: 'SET 4KiB str', command: 'set', args: ['foo_rand000000000001', large_str], pipeline: 1})); tests.push(new Test({descr: 'SET 4KiB str', command: 'set', args: ['foo_rand000000000001', large_str], pipeline: 50})); -tests.push(new Test({descr: 'SET 4KiB str', command: 'set', args: ['foo_rand000000000001', large_str], batch: 50, reqs: num_requests * 2})); +tests.push(new Test({descr: 'SET 4KiB str', command: 'set', args: ['foo_rand000000000001', large_str], batch: 50})); tests.push(new Test({descr: 'SET 4KiB buf', command: 'set', args: ['foo_rand000000000001', large_buf], pipeline: 1})); tests.push(new Test({descr: 'SET 4KiB buf', command: 'set', args: ['foo_rand000000000001', large_buf], pipeline: 50})); -tests.push(new Test({descr: 'SET 4KiB buf', command: 'set', args: ['foo_rand000000000001', large_buf], batch: 50, reqs: num_requests * 2})); +tests.push(new Test({descr: 'SET 4KiB buf', command: 'set', args: ['foo_rand000000000001', large_buf], batch: 50})); tests.push(new Test({descr: 'GET 4KiB str', command: 'get', args: ['foo_rand000000000001'], pipeline: 1})); tests.push(new Test({descr: 'GET 4KiB str', command: 'get', args: ['foo_rand000000000001'], pipeline: 50})); -tests.push(new Test({descr: 'GET 4KiB str', command: 'get', args: ['foo_rand000000000001'], batch: 50, reqs: num_requests * 2})); +tests.push(new Test({descr: 'GET 4KiB str', command: 'get', args: ['foo_rand000000000001'], batch: 50})); tests.push(new Test({descr: 'GET 4KiB buf', command: 'get', args: ['foo_rand000000000001'], pipeline: 1, client_opts: { return_buffers: true} })); tests.push(new Test({descr: 'GET 4KiB buf', command: 'get', args: ['foo_rand000000000001'], pipeline: 50, client_opts: { return_buffers: true} })); @@ -239,31 +254,35 @@ tests.push(new Test({descr: 'GET 4KiB buf', command: 'get', args: ['foo_rand0000 tests.push(new Test({descr: 'INCR', command: 'incr', args: ['counter_rand000000000000'], pipeline: 1})); tests.push(new Test({descr: 'INCR', command: 'incr', args: ['counter_rand000000000000'], pipeline: 50})); -tests.push(new Test({descr: 'INCR', command: 'incr', args: ['counter_rand000000000000'], batch: 50, reqs: num_requests * 2})); +tests.push(new Test({descr: 'INCR', command: 'incr', args: ['counter_rand000000000000'], batch: 50})); tests.push(new Test({descr: 'LPUSH', command: 'lpush', args: ['mylist', small_str], pipeline: 1})); tests.push(new Test({descr: 'LPUSH', command: 'lpush', args: ['mylist', small_str], pipeline: 50})); -tests.push(new Test({descr: 'LPUSH', command: 'lpush', args: ['mylist', small_str], batch: 50, reqs: num_requests * 2})); +tests.push(new Test({descr: 'LPUSH', command: 'lpush', args: ['mylist', small_str], batch: 50})); tests.push(new Test({descr: 'LRANGE 10', command: 'lrange', args: ['mylist', '0', '9'], pipeline: 1})); tests.push(new Test({descr: 'LRANGE 10', command: 'lrange', args: ['mylist', '0', '9'], pipeline: 50})); -tests.push(new Test({descr: 'LRANGE 10', command: 'lrange', args: ['mylist', '0', '9'], batch: 50, reqs: num_requests * 2})); +tests.push(new Test({descr: 'LRANGE 10', command: 'lrange', args: ['mylist', '0', '9'], batch: 50})); tests.push(new Test({descr: 'LRANGE 100', command: 'lrange', args: ['mylist', '0', '99'], pipeline: 1})); tests.push(new Test({descr: 'LRANGE 100', command: 'lrange', args: ['mylist', '0', '99'], pipeline: 50})); -tests.push(new Test({descr: 'LRANGE 100', command: 'lrange', args: ['mylist', '0', '99'], batch: 50, reqs: num_requests * 2})); +tests.push(new Test({descr: 'LRANGE 100', command: 'lrange', args: ['mylist', '0', '99'], batch: 50})); + +tests.push(new Test({descr: 'SET 4MiB buf', command: 'set', args: ['foo_rand000000000002', very_large_buf], pipeline: 1})); +tests.push(new Test({descr: 'SET 4MiB buf', command: 'set', args: ['foo_rand000000000002', very_large_buf], pipeline: 20})); +tests.push(new Test({descr: 'SET 4MiB buf', command: 'set', args: ['foo_rand000000000002', very_large_buf], batch: 20})); -tests.push(new Test({descr: 'SET 4MiB buf', command: 'set', args: ['foo_rand000000000002', very_large_buf], pipeline: 1, reqs: 500})); -tests.push(new Test({descr: 'SET 4MiB buf', command: 'set', args: ['foo_rand000000000002', very_large_buf], pipeline: 20, reqs: 500})); -tests.push(new Test({descr: 'SET 4MiB buf', command: 'set', args: ['foo_rand000000000002', very_large_buf], batch: 20, reqs: 500})); +tests.push(new Test({descr: 'SET 4MiB str', command: 'set', args: ['foo_rand000000000002', very_large_str], pipeline: 1})); +tests.push(new Test({descr: 'SET 4MiB str', command: 'set', args: ['foo_rand000000000002', very_large_str], pipeline: 20})); +tests.push(new Test({descr: 'SET 4MiB str', command: 'set', args: ['foo_rand000000000002', very_large_str], batch: 20})); -tests.push(new Test({descr: 'GET 4MiB str', command: 'get', args: ['foo_rand000000000002'], pipeline: 1, reqs: 100})); -tests.push(new Test({descr: 'GET 4MiB str', command: 'get', args: ['foo_rand000000000002'], pipeline: 20, reqs: 100})); -tests.push(new Test({descr: 'GET 4MiB str', command: 'get', args: ['foo_rand000000000002'], batch: 20, reqs: 100})); +tests.push(new Test({descr: 'GET 4MiB str', command: 'get', args: ['foo_rand000000000002'], pipeline: 1})); +tests.push(new Test({descr: 'GET 4MiB str', command: 'get', args: ['foo_rand000000000002'], pipeline: 20})); +tests.push(new Test({descr: 'GET 4MiB str', command: 'get', args: ['foo_rand000000000002'], batch: 20})); -tests.push(new Test({descr: 'GET 4MiB buf', command: 'get', args: ['foo_rand000000000002'], pipeline: 1, reqs: 100, client_opts: { return_buffers: true} })); -tests.push(new Test({descr: 'GET 4MiB buf', command: 'get', args: ['foo_rand000000000002'], pipeline: 20, reqs: 100, client_opts: { return_buffers: true} })); -tests.push(new Test({descr: 'GET 4MiB buf', command: 'get', args: ['foo_rand000000000002'], batch: 20, reqs: 100, client_opts: { return_buffers: true} })); +tests.push(new Test({descr: 'GET 4MiB buf', command: 'get', args: ['foo_rand000000000002'], pipeline: 1, client_opts: { return_buffers: true} })); +tests.push(new Test({descr: 'GET 4MiB buf', command: 'get', args: ['foo_rand000000000002'], pipeline: 20, client_opts: { return_buffers: true} })); +tests.push(new Test({descr: 'GET 4MiB buf', command: 'get', args: ['foo_rand000000000002'], batch: 20, client_opts: { return_buffers: true} })); function next() { var test = tests.shift(); From 1cb158b5daac48791c7fa4fc4f77225ba53b2707 Mon Sep 17 00:00:00 2001 From: Ruben Bridgewater Date: Mon, 26 Oct 2015 19:44:29 +0100 Subject: [PATCH 0448/1748] Add zscan test --- test/commands/zscan.spec.js | 50 +++++++++++++++++++++++++++++++++++++ 1 file changed, 50 insertions(+) create mode 100644 test/commands/zscan.spec.js diff --git a/test/commands/zscan.spec.js b/test/commands/zscan.spec.js new file mode 100644 index 00000000000..f655bdeac0c --- /dev/null +++ b/test/commands/zscan.spec.js @@ -0,0 +1,50 @@ +'use strict'; + +var config = require('../lib/config'); +var helper = require('../helper'); +var assert = require('assert'); +var redis = config.redis; + +describe("The 'zscan' method", function () { + + helper.allTests(function(parser, ip, args) { + + describe("using " + parser + " and " + ip, function () { + var client; + + beforeEach(function (done) { + client = redis.createClient.apply(redis.createClient, args); + client.once("ready", function () { + client.flushdb(done); + }); + }); + + it('return values', function (done) { + if (helper.redisProcess().spawnFailed()) this.skip(); + helper.serverVersionAtLeast.call(this, client, [2, 8, 0]); + var hash = {}; + var set = []; + var zset = ["zset:1"]; + for (var i = 0; i < 500; i++) { + hash["key_" + i] = "value_" + i; + set.push("member_" + i); + zset.push(i, "z_member_" + i); + } + client.hmset("hash:1", hash); + client.sadd("set:1", set); + client.zadd(zset); + client.zscan('zset:1', 0, 'MATCH', '*', 'COUNT', 500, function (err, res) { + assert(!err); + assert.strictEqual(res.length, 2); + assert.strictEqual(res[1].length, 1000); + done(); + }); + }); + + afterEach(function () { + client.end(); + }); + }); + }); + +}); From 399a29a97c631a718f2ad15623bcc61ac1af6d09 Mon Sep 17 00:00:00 2001 From: Ruben Bridgewater Date: Mon, 26 Oct 2015 21:35:19 +0100 Subject: [PATCH 0449/1748] Improve pipeline logic and fix #897 --- index.js | 123 ++++++++++++++++++---------------------- test/batch.spec.js | 12 ++-- test/node_redis.spec.js | 30 +++++----- 3 files changed, 77 insertions(+), 88 deletions(-) diff --git a/index.js b/index.js index ebe44d969ab..2975d9cc586 100644 --- a/index.js +++ b/index.js @@ -39,18 +39,24 @@ function RedisClient(stream, options) { options = JSON.parse(JSON.stringify(options || {})); var self = this; + this.pipeline = 0; + var cork; if (!stream.cork) { - this.pipeline = 0; - this.cork = noop; - this.once('ready', function () { - self.cork = function (len) { - self.pipeline = len; - self.pipeline_queue = new Queue(len); - }; - }); - stream.uncork = noop; - this.write = this.writeStream; + cork = function (len) { + self.pipeline = len; + self.pipeline_queue = new Queue(len); + }; + this.uncork = noop; + } else { + cork = function (len) { + self.pipeline = len; + self.pipeline_queue = new Queue(len); + self.stream.cork(); + }; } + this.once('ready', function () { + self.cork = cork; + }); this.stream = stream; this.connection_id = ++connection_id; @@ -131,8 +137,9 @@ RedisClient.prototype.install_stream_listeners = function() { }); }; -RedisClient.prototype.cork = function (len) { - this.stream.cork(); +RedisClient.prototype.cork = noop; +RedisClient.prototype.uncork = function () { + this.stream.uncork(); }; RedisClient.prototype.initialize_retry_vars = function () { @@ -377,7 +384,6 @@ RedisClient.prototype.on_info_cmd = function (err, res) { return; } - var self = this; var obj = {}; var lines = res.toString().split('\r\n'); var i = 0; @@ -422,9 +428,9 @@ RedisClient.prototype.on_info_cmd = function (err, res) { retry_time = 1000; } debug('Redis server still loading, trying again in ' + retry_time); - setTimeout(function () { + setTimeout(function (self) { self.ready_check(); - }, retry_time); + }, retry_time, this); } }; @@ -441,12 +447,13 @@ RedisClient.prototype.ready_check = function () { }; RedisClient.prototype.send_offline_queue = function () { - var command_obj, buffered_writes = 0; + var command_obj; while (command_obj = this.offline_queue.shift()) { debug('Sending offline command: ' + command_obj.command); - buffered_writes += !this.send_command(command_obj.command, command_obj.args, command_obj.callback); + this.send_command(command_obj.command, command_obj.args, command_obj.callback); } + this.drain(); // Even though items were shifted off, Queue backing store still uses memory until next add, so just get a new Queue this.offline_queue = new Queue(); }; @@ -543,7 +550,7 @@ RedisClient.prototype.return_error = function (err) { err.code = match[1]; } - this.emit_drain_idle(queue_len); + this.emit_idle(queue_len); if (command_obj.callback) { command_obj.callback(err); @@ -557,16 +564,12 @@ RedisClient.prototype.drain = function () { this.should_buffer = false; }; -RedisClient.prototype.emit_drain_idle = function (queue_len) { +RedisClient.prototype.emit_idle = function (queue_len) { if (this.pub_sub_mode === false && queue_len === 0) { // Free the queue capacity memory by using a new queue this.command_queue = new Queue(); this.emit('idle'); } - - if (this.should_buffer && queue_len <= this.command_queue_low_water) { - this.drain(); - } }; RedisClient.prototype.return_reply = function (reply) { @@ -587,7 +590,7 @@ RedisClient.prototype.return_reply = function (reply) { queue_len = this.command_queue.length; - this.emit_drain_idle(queue_len); + this.emit_idle(queue_len); if (command_obj && !command_obj.sub_command) { if (typeof command_obj.callback === 'function') { @@ -640,7 +643,7 @@ RedisClient.prototype.return_reply = function (reply) { } /* istanbul ignore else: this is a safety check that we should not be able to trigger */ else if (this.monitoring) { - if (Buffer.isBuffer(reply)) { + if (typeof reply !== 'string') { reply = reply.toString(); } // If in monitoring mode only two commands are valid ones: AUTH and MONITOR wich reply with OK @@ -662,8 +665,8 @@ RedisClient.prototype.send_command = function (command, args, callback) { var arg, command_obj, i, err, stream = this.stream, command_str = '', - buffered_writes = 0, buffer_args = false, + big_data = false, buffer = this.options.return_buffers; if (args === undefined) { @@ -695,7 +698,12 @@ RedisClient.prototype.send_command = function (command, args, callback) { for (i = 0; i < args.length; i += 1) { if (Buffer.isBuffer(args[i])) { buffer_args = true; - break; + } else if (typeof args[i] !== 'string') { + arg = String(arg); + // 30000 seemed to be a good value to switch to buffers after testing this with and checking the pros and cons + } else if (args[i].length > 30000) { + big_data = true; + args[i] = new Buffer(args[i]); } } if (this.options.detect_buffers) { @@ -741,74 +749,53 @@ RedisClient.prototype.send_command = function (command, args, callback) { // Always use 'Multi bulk commands', but if passed any Buffer args, then do multiple writes, one for each arg. // This means that using Buffers in commands is going to be slower, so use Strings if you don't already have a Buffer. - command_str = '*' + (args.length + 1) + '\r\n$' + command.length + '\r\n' + command + '\r\n'; - if (!buffer_args) { // Build up a string and send entire command in one write + if (!buffer_args && !big_data) { // Build up a string and send entire command in one write for (i = 0; i < args.length; i += 1) { - arg = args[i]; - if (typeof arg !== 'string') { - arg = String(arg); - } + arg = String(args[i]); command_str += '$' + Buffer.byteLength(arg) + '\r\n' + arg + '\r\n'; } debug('Send ' + this.address + ' id ' + this.connection_id + ': ' + command_str); - buffered_writes += !this.write(command_str); + this.write(command_str); } else { debug('Send command (' + command_str + ') has Buffer arguments'); - buffered_writes += !this.write(command_str); + this.write(command_str); for (i = 0; i < args.length; i += 1) { arg = args[i]; - if (Buffer.isBuffer(arg)) { - if (arg.length === 0) { - debug('send_command: using empty string for 0 length buffer'); - buffered_writes += !this.write('$0\r\n\r\n'); - } else { - buffered_writes += !this.write('$' + arg.length + '\r\n'); - buffered_writes += !this.write(arg); - buffered_writes += !this.write('\r\n'); - debug('send_command: buffer send ' + arg.length + ' bytes'); - } + if (!Buffer.isBuffer(arg)) { + arg = String(arg); + this.write('$' + Buffer.byteLength(arg) + '\r\n' + arg + '\r\n'); } else { - if (typeof arg !== 'string') { - arg = String(arg); - } - debug('send_command: string send ' + Buffer.byteLength(arg) + ' bytes: ' + arg); - buffered_writes += !this.write('$' + Buffer.byteLength(arg) + '\r\n' + arg + '\r\n'); + this.write('$' + arg.length + '\r\n'); + this.write(arg); + this.write('\r\n'); } + debug('send_command: buffer send ' + arg.length + ' bytes'); } } - if (buffered_writes !== 0 || this.command_queue.length >= this.command_queue_high_water) { - debug('send_command buffered_writes: ' + buffered_writes, ' should_buffer: ' + this.should_buffer); - this.should_buffer = true; - } return !this.should_buffer; }; RedisClient.prototype.write = function (data) { - return this.stream.write(data); -}; - -RedisClient.prototype.writeStream = function (data) { - var nr = 0; - if (this.pipeline === 0) { - return this.stream.write(data); + this.should_buffer = !this.stream.write(data); + return; } this.pipeline--; if (this.pipeline === 0) { - var command; + var command, str = ''; while (command = this.pipeline_queue.shift()) { - nr += !this.stream.write(command); + str += command; } - nr += !this.stream.write(data); - return !nr; + this.should_buffer = !this.stream.write(str + data); + return; } this.pipeline_queue.push(data); - return true; + return; }; RedisClient.prototype.pub_sub_command = function (command_obj) { @@ -1102,7 +1089,7 @@ Multi.prototype.exec_transaction = function (callback) { this.send_command(command, args, index, cb); } - this._client.stream.uncork(); + this._client.uncork(); return this._client.send_command('exec', [], function(err, replies) { self.execute_callback(err, replies); }); @@ -1210,7 +1197,7 @@ Multi.prototype.exec = Multi.prototype.EXEC = Multi.prototype.exec_batch = funct this._client.send_command(command, args, cb); index++; } - this._client.stream.uncork(); + this._client.uncork(); return this._client.should_buffer; }; diff --git a/test/batch.spec.js b/test/batch.spec.js index 27b45ad3f06..9afc7973e9d 100644 --- a/test/batch.spec.js +++ b/test/batch.spec.js @@ -162,7 +162,7 @@ describe("The 'batch' method", function () { }); }); - it('handles batchple operations being applied to a set', function (done) { + it('handles multiple operations being applied to a set', function (done) { client.sadd("some set", "mem 1"); client.sadd(["some set", "mem 2"]); client.sadd("some set", "mem 3"); @@ -189,7 +189,7 @@ describe("The 'batch' method", function () { }); }); - it('allows batchple operations to be performed using constructor with all kinds of syntax', function (done) { + it('allows multiple operations to be performed using constructor with all kinds of syntax', function (done) { var now = Date.now(); var arr = ["batchhmset", "batchbar", "batchbaz"]; var arr2 = ['some manner of key', 'otherTypes']; @@ -253,7 +253,7 @@ describe("The 'batch' method", function () { assert.strictEqual(buffering, true); }); - it('allows batchple operations to be performed using a chaining API', function (done) { + it('allows multiple operations to be performed using a chaining API', function (done) { client.batch() .mset('some', '10', 'keys', '20') .incr('some') @@ -270,7 +270,7 @@ describe("The 'batch' method", function () { }); }); - it('allows batchple commands to work the same as normal to be performed using a chaining API', function (done) { + it('allows multiple commands to work the same as normal to be performed using a chaining API', function (done) { client.batch() .mset(['some', '10', 'keys', '20']) .incr(['some', helper.isNumber(11)]) @@ -287,7 +287,7 @@ describe("The 'batch' method", function () { }); }); - it('allows batchple commands to work the same as normal to be performed using a chaining API promisified', function () { + it('allows multiple commands to work the same as normal to be performed using a chaining API promisified', function () { return client.batch() .mset(['some', '10', 'keys', '20']) .incr(['some', helper.isNumber(11)]) @@ -303,7 +303,7 @@ describe("The 'batch' method", function () { }); }); - it('allows an array to be provided indicating batchple operations to perform', function (done) { + it('allows an array to be provided indicating multiple operations to perform', function (done) { // test nested batch-bulk replies with nulls. client.batch([ ["mget", ["batchfoo", "some", "random value", "keys"]], diff --git a/test/node_redis.spec.js b/test/node_redis.spec.js index e89cddb3d32..84ef29b3e92 100644 --- a/test/node_redis.spec.js +++ b/test/node_redis.spec.js @@ -43,6 +43,22 @@ describe("The node_redis client", function () { }); }); + describe('big data', function () { + + // Check if the fast mode for big strings is working correct + it('safe strings that are bigger than 30000 characters', function(done) { + var str = 'foo ಠ_ಠ bar '; + while (str.length < 111111) { + str += str; + } + client.set('foo', str); + client.get('foo', function (err, res) { + assert.strictEqual(res, str); + done(); + }); + }); + }); + describe("send_command", function () { it("omitting args should be fine in some cases", function (done) { @@ -470,20 +486,6 @@ describe("The node_redis client", function () { describe('enable_offline_queue', function () { describe('true', function () { - it("should emit drain after info command and nothing to buffer", function (done) { - client = redis.createClient({ - parser: parser - }); - client.set('foo', 'bar'); - client.get('foo', function () { - assert(!client.should_buffer); - setTimeout(done, 25); - }); - client.on('drain', function() { - assert(client.offline_queue.length === 2); - }); - }); - it("should emit drain if offline queue is flushed and nothing to buffer", function (done) { client = redis.createClient({ parser: parser, From ebea0872a90925d404b8dd481f10c644108e7553 Mon Sep 17 00:00:00 2001 From: Ruben Bridgewater Date: Tue, 27 Oct 2015 00:35:31 +0100 Subject: [PATCH 0450/1748] Add regression test --- test/commands/multi.spec.js | 16 ++++++++++++++++ 1 file changed, 16 insertions(+) diff --git a/test/commands/multi.spec.js b/test/commands/multi.spec.js index 624998b42da..062d14c32cf 100644 --- a/test/commands/multi.spec.js +++ b/test/commands/multi.spec.js @@ -537,6 +537,22 @@ describe("The 'multi' method", function () { }); }); + it("works properly after a reconnect. issue #897", function (done) { + helper.serverVersionAtLeast.call(this, client, [2, 6, 5]); + + client.stream.destroy(); + client.on('error', function (err) { + assert.strictEqual(err.code, 'ECONNREFUSED'); + }); + client.on('ready', function () { + client.multi([['set', 'foo', 'bar'], ['get', 'foo']]).exec(function (err, res) { + assert(!err); + assert.strictEqual(res[1], 'bar'); + done(); + }); + }); + }); + }); }); }); From 0ec2c43603ccbad95185475447f832f3f44866bd Mon Sep 17 00:00:00 2001 From: Ruben Bridgewater Date: Mon, 26 Oct 2015 21:37:02 +0100 Subject: [PATCH 0451/1748] Fix fired but not yet returned commands not being rejected after a connection loss --- index.js | 39 ++++++++++++++++---------- test/connection.spec.js | 57 +++++++++++++++++++++++++++++++++++++ test/node_redis.spec.js | 43 ++++++++++++++++++++++++++-- test/pubsub.spec.js | 62 ++++++++++++++++++++++------------------- 4 files changed, 154 insertions(+), 47 deletions(-) diff --git a/index.js b/index.js index 2975d9cc586..2f840ecfed7 100644 --- a/index.js +++ b/index.js @@ -162,23 +162,19 @@ RedisClient.prototype.unref = function () { } }; -// flush offline_queue and command_queue, erroring any items with a callback first -RedisClient.prototype.flush_and_error = function (error) { +// flush provided queues, erroring any items with a callback first +RedisClient.prototype.flush_and_error = function (error, queue_names) { var command_obj; - while (command_obj = this.offline_queue.shift()) { - if (typeof command_obj.callback === 'function') { - error.command = command_obj.command.toUpperCase(); - command_obj.callback(error); - } - } - while (command_obj = this.command_queue.shift()) { - if (typeof command_obj.callback === 'function') { - error.command = command_obj.command.toUpperCase(); - command_obj.callback(error); + queue_names = queue_names || ['offline_queue', 'command_queue']; + for (var i = 0; i < queue_names.length; i++) { + while (command_obj = this[queue_names[i]].shift()) { + if (typeof command_obj.callback === 'function') { + error.command = command_obj.command.toUpperCase(); + command_obj.callback(error); + } } + this[queue_names[i]] = new Queue(); } - this.offline_queue = new Queue(); - this.command_queue = new Queue(); }; RedisClient.prototype.on_error = function (err) { @@ -477,6 +473,7 @@ var retry_connection = function (self) { }; RedisClient.prototype.connection_gone = function (why) { + var error; // If a retry is already in progress, just let that happen if (this.retry_timer) { return; @@ -515,7 +512,7 @@ RedisClient.prototype.connection_gone = function (why) { var message = this.retry_totaltime >= this.connect_timeout ? 'connection timeout exceeded.' : 'maximum connection attempts exceeded.'; - var error = new Error('Redis connection in broken state: ' + message); + error = new Error('Redis connection in broken state: ' + message); error.code = 'CONNECTION_BROKEN'; this.flush_and_error(error); this.emit('error', error); @@ -523,6 +520,18 @@ RedisClient.prototype.connection_gone = function (why) { return; } + // Flush all commands that have not yet returned. We can't handle them appropriatly + if (this.command_queue.length !== 0) { + error = new Error('Redis connection lost and command aborted in uncertain state. It might have been processed.'); + error.code = 'UNCERTAIN_STATE'; + // TODO: Evaluate to add this + // if (this.options.retry_commands) { + // this.offline_queue.unshift(this.command_queue.toArray()); + // error.message = 'Command aborted in uncertain state and queued for next connection.'; + // } + this.flush_and_error(error, ['command_queue']); + } + if (this.retry_max_delay !== null && this.retry_delay > this.retry_max_delay) { this.retry_delay = this.retry_max_delay; } else if (this.retry_totaltime + this.retry_delay > this.connect_timeout) { diff --git a/test/connection.spec.js b/test/connection.spec.js index 0ea6e16613b..1fda737951c 100644 --- a/test/connection.spec.js +++ b/test/connection.spec.js @@ -236,6 +236,63 @@ describe("connection tests", function () { }); } + it("redis still loading <= 1000ms", function (done) { + client = redis.createClient.apply(redis.createClient, args); + var tmp = client.info.bind(client); + var end = helper.callFuncAfter(done, 3); + var delayed = false; + var time; + // Mock original function and pretent redis is still loading + client.info = function (cb) { + tmp(function(err, res) { + if (!delayed) { + assert(!err); + res = res.toString().replace(/loading:0/, 'loading:1\r\nloading_eta_seconds:0.5'); + delayed = true; + time = Date.now(); + } + end(); + cb(err, res); + }); + }; + client.on("ready", function () { + var rest = Date.now() - time; + // Be on the safe side and accept 100ms above the original value + assert(rest - 100 < 500 && rest >= 500); + assert(delayed); + end(); + }); + }); + + it("redis still loading > 1000ms", function (done) { + client = redis.createClient.apply(redis.createClient, args); + var tmp = client.info.bind(client); + var end = helper.callFuncAfter(done, 3); + var delayed = false; + var time; + // Mock original function and pretent redis is still loading + client.info = function (cb) { + tmp(function(err, res) { + if (!delayed) { + assert(!err); + // Try reconnecting after one second even if redis tells us the time needed is above one second + res = res.toString().replace(/loading:0/, 'loading:1\r\nloading_eta_seconds:2.5'); + delayed = true; + time = Date.now(); + } + end(); + cb(err, res); + }); + }; + client.on("ready", function () { + var rest = Date.now() - time; + // Be on the safe side and accept 100ms above the original value + assert(rest - 100 < 1000 && rest >= 1000); + assert(delayed); + end(); + }); + }); + }); }); diff --git a/test/node_redis.spec.js b/test/node_redis.spec.js index 84ef29b3e92..4650a0a3330 100644 --- a/test/node_redis.spec.js +++ b/test/node_redis.spec.js @@ -556,6 +556,43 @@ describe("The node_redis client", function () { assert.strictEqual(client.offline_queue.length, 0); }); }); + + it("flushes the command queue if connection is lost", function (done) { + client = redis.createClient({ + parser: parser + }); + + client.once('ready', function() { + var multi = client.multi(); + multi.config("bar"); + var cb = function(err, reply) { + assert.equal(err.code, 'UNCERTAIN_STATE'); + }; + for (var i = 0; i < 12; i += 3) { + client.set("foo" + i, "bar" + i); + multi.set("foo" + (i + 1), "bar" + (i + 1), cb); + multi.set("foo" + (i + 2), "bar" + (i + 2)); + } + multi.exec(); + assert.equal(client.command_queue.length, 15); + helper.killConnection(client); + }); + + client.on("reconnecting", function (params) { + assert.equal(client.command_queue.length, 15); + }); + + client.on('error', function(err) { + if (/uncertain state/.test(err.message)) { + assert.equal(client.command_queue.length, 0); + done(); + } else { + assert.equal(err.code, 'ECONNREFUSED'); + assert.equal(err.errno, 'ECONNREFUSED'); + assert.equal(err.syscall, 'connect'); + } + }); + }); }); describe('false', function () { @@ -599,7 +636,7 @@ describe("The node_redis client", function () { }); }); - it("flushes the command queue connection if in broken connection mode", function (done) { + it("flushes the command queue if connection is lost", function (done) { client = redis.createClient({ parser: parser, max_attempts: 2, @@ -610,7 +647,7 @@ describe("The node_redis client", function () { var multi = client.multi(); multi.config("bar"); var cb = function(err, reply) { - assert.equal(err.code, 'CONNECTION_BROKEN'); + assert.equal(err.code, 'UNCERTAIN_STATE'); }; for (var i = 0; i < 12; i += 3) { client.set("foo" + i, "bar" + i); @@ -627,7 +664,7 @@ describe("The node_redis client", function () { }); client.on('error', function(err) { - if (/Redis connection in broken state:/.test(err.message)) { + if (err.code === 'UNCERTAIN_STATE') { assert.equal(client.command_queue.length, 0); done(); } else { diff --git a/test/pubsub.spec.js b/test/pubsub.spec.js index f7dbd57fe43..545f85b6c87 100644 --- a/test/pubsub.spec.js +++ b/test/pubsub.spec.js @@ -317,6 +317,39 @@ describe("publish/subscribe", function () { }); }); + it("should not publish a message multiple times per command", function (done) { + var published = {}; + + function subscribe(message) { + sub.removeAllListeners('subscribe'); + sub.removeAllListeners('message'); + sub.removeAllListeners('unsubscribe'); + sub.on('subscribe', function () { + pub.publish('/foo', message); + }); + sub.on('message', function (channel, message) { + if (published[message]) { + done(new Error('Message published more than once.')); + } + published[message] = true; + }); + sub.on('unsubscribe', function (channel, count) { + assert.strictEqual(count, 0); + }); + sub.subscribe('/foo'); + } + + subscribe('hello'); + + setTimeout(function () { + sub.unsubscribe(); + setTimeout(function () { + subscribe('world'); + setTimeout(done, 50); + }, 40); + }, 40); + }); + // TODO: Fix pub sub // And there's more than just those two issues describe.skip('FIXME: broken pub sub', function () { @@ -331,35 +364,6 @@ describe("publish/subscribe", function () { }); setTimeout(done, 200); }); - - it("should not publish a message multiple times per command", function (done) { - var published = {}; - - function subscribe(message) { - sub.on('subscribe', function () { - pub.publish('/foo', message); - }); - sub.on('message', function (channel, message) { - if (published[message]) { - done(new Error('Message published more than once.')); - } - published[message] = true; - }); - sub.on('unsubscribe', function (channel, count) { - assert.strictEqual(count, 0); - }); - sub.subscribe('/foo'); - } - - subscribe('hello'); - - setTimeout(function () { - sub.unsubscribe(); - setTimeout(function () { - subscribe('world'); - }, 40); - }, 40); - }); }); afterEach(function () { From d39f6961e656e3bd626e33bf22b695f8fcbab88d Mon Sep 17 00:00:00 2001 From: Ruben Bridgewater Date: Wed, 28 Oct 2015 00:58:11 +0100 Subject: [PATCH 0452/1748] Add tests and emit UNCERTAIN_STATE errors --- index.js | 9 ++++++--- test/commands/multi.spec.js | 13 +++++++++++++ test/connection.spec.js | 14 ++++++++++++++ 3 files changed, 33 insertions(+), 3 deletions(-) diff --git a/index.js b/index.js index 2f840ecfed7..94d213e92f0 100644 --- a/index.js +++ b/index.js @@ -530,6 +530,8 @@ RedisClient.prototype.connection_gone = function (why) { // error.message = 'Command aborted in uncertain state and queued for next connection.'; // } this.flush_and_error(error, ['command_queue']); + error.message = 'Redis connection lost and commands aborted in uncertain state. They might have been processed.'; + this.emit('error', error); } if (this.retry_max_delay !== null && this.retry_delay > this.retry_max_delay) { @@ -1109,11 +1111,12 @@ Multi.prototype.execute_callback = function (err, replies) { if (err) { // The errors would be circular - err.errors = err.code !== 'CONNECTION_BROKEN' ? this.errors : []; + var connection_error = ['CONNECTION_BROKEN', 'UNCERTAIN_STATE'].indexOf(err.code) !== -1; + err.errors = connection_error ? [] : this.errors; if (this.callback) { this.callback(err); - } else if (err.code !== 'CONNECTION_BROKEN') { - // Exclude CONNECTION_BROKEN so that error won't be emitted twice + // Exclude connection errors so that those errors won't be emitted twice + } else if (!connection_error) { this._client.emit('error', err); } return; diff --git a/test/commands/multi.spec.js b/test/commands/multi.spec.js index 062d14c32cf..ea26fdfb505 100644 --- a/test/commands/multi.spec.js +++ b/test/commands/multi.spec.js @@ -553,6 +553,19 @@ describe("The 'multi' method", function () { }); }); + it("emits error once if reconnecting after multi has been executed but not yet returned without callback", function (done) { + helper.serverVersionAtLeast.call(this, client, [2, 6, 5]); + + client.on('error', function(err) { + assert.strictEqual(err.code, 'UNCERTAIN_STATE'); + done(); + }); + + client.multi().set("foo", 'bar').get('foo').exec(); + // Abort connection before the value returned + client.stream.destroy(); + }); + }); }); }); diff --git a/test/connection.spec.js b/test/connection.spec.js index 1fda737951c..62251fdef4f 100644 --- a/test/connection.spec.js +++ b/test/connection.spec.js @@ -107,6 +107,20 @@ describe("connection tests", function () { }); }); + + it("emits error once if reconnecting after command has been executed but not yet returned without callback", function (done) { + client = redis.createClient.apply(redis.createClient, args); + client.on('error', function(err) { + assert.strictEqual(err.code, 'UNCERTAIN_STATE'); + done(); + }); + + client.on('ready', function() { + client.set("foo", 'bar'); + // Abort connection before the value returned + client.stream.destroy(); + }); + }); }); describe("when not connected", function () { From 790f12909b10429cecc3386c4057352f9b1997e0 Mon Sep 17 00:00:00 2001 From: Ruben Bridgewater Date: Tue, 27 Oct 2015 00:36:03 +0100 Subject: [PATCH 0453/1748] Update changelog / readme --- README.md | 87 ++++++++++++++++----------------------- benchmarks/multi_bench.js | 40 +++++++++--------- changelog.md | 12 ++++++ 3 files changed, 68 insertions(+), 71 deletions(-) diff --git a/README.md b/README.md index 3a1590d54cf..7ecaaaaad33 100644 --- a/README.md +++ b/README.md @@ -649,57 +649,42 @@ Here are results of `multi_bench.js` which is similar to `redis-benchmark` from hiredis parser (Lenovo T450s i7-5600U): ``` -Client count: 5, node version: 4.1.2, server version: 3.0.3, parser: hiredis - PING, 1/5 min/max/avg/p95: 0/ 4/ 0.02/ 0.00 1223ms total, 40883.07 ops/sec - PING, 50/5 min/max/avg/p95: 0/ 3/ 0.50/ 1.00 497ms total, 100603.62 ops/sec - PING, batch 50/5 min/max/avg/p95: 0/ 1/ 0.15/ 1.00 308ms total, 324675.32 ops/sec - SET 4B str, 1/5 min/max/avg/p95: 0/ 2/ 0.03/ 0.00 1402ms total, 35663.34 ops/sec - SET 4B str, 50/5 min/max/avg/p95: 0/ 2/ 0.53/ 1.00 534ms total, 93632.96 ops/sec - SET 4B str, batch 50/5 min/max/avg/p95: 0/ 1/ 0.19/ 1.00 392ms total, 255102.04 ops/sec - SET 4B buf, 1/5 min/max/avg/p95: 0/ 2/ 0.05/ 1.00 2433ms total, 20550.76 ops/sec - SET 4B buf, 50/5 min/max/avg/p95: 0/ 5/ 1.65/ 3.00 1652ms total, 30266.34 ops/sec - SET 4B buf, batch 50/5 min/max/avg/p95: 0/ 3/ 0.36/ 1.00 726ms total, 137741.05 ops/sec - GET 4B str, 1/5 min/max/avg/p95: 0/ 1/ 0.03/ 0.00 1314ms total, 38051.75 ops/sec - GET 4B str, 50/5 min/max/avg/p95: 0/ 3/ 0.53/ 1.00 529ms total, 94517.96 ops/sec - GET 4B str, batch 50/5 min/max/avg/p95: 0/ 1/ 0.16/ 1.00 328ms total, 304878.05 ops/sec - GET 4B buf, 1/5 min/max/avg/p95: 0/ 2/ 0.03/ 0.00 1389ms total, 35997.12 ops/sec - GET 4B buf, 50/5 min/max/avg/p95: 0/ 2/ 0.52/ 1.00 519ms total, 96339.11 ops/sec - GET 4B buf, batch 50/5 min/max/avg/p95: 0/ 1/ 0.16/ 1.00 168ms total, 297619.05 ops/sec - SET 4KiB str, 1/5 min/max/avg/p95: 0/ 3/ 0.03/ 0.00 1670ms total, 29940.12 ops/sec - SET 4KiB str, 50/5 min/max/avg/p95: 0/ 5/ 0.94/ 2.00 941ms total, 53134.96 ops/sec - SET 4KiB str, batch 50/5 min/max/avg/p95: 0/ 2/ 0.49/ 1.00 984ms total, 101626.02 ops/sec - SET 4KiB buf, 1/5 min/max/avg/p95: 0/ 1/ 0.05/ 0.00 2423ms total, 20635.58 ops/sec - SET 4KiB buf, 50/5 min/max/avg/p95: 0/ 5/ 1.60/ 3.00 1598ms total, 31289.11 ops/sec - SET 4KiB buf, batch 50/5 min/max/avg/p95: 0/ 1/ 0.41/ 1.00 825ms total, 121212.12 ops/sec - GET 4KiB str, 1/5 min/max/avg/p95: 0/ 1/ 0.03/ 0.00 1483ms total, 33715.44 ops/sec - GET 4KiB str, 50/5 min/max/avg/p95: 0/ 3/ 0.69/ 1.00 691ms total, 72358.90 ops/sec - GET 4KiB str, batch 50/5 min/max/avg/p95: 0/ 2/ 0.38/ 1.00 759ms total, 131752.31 ops/sec - GET 4KiB buf, 1/5 min/max/avg/p95: 0/ 3/ 0.03/ 0.00 1485ms total, 33670.03 ops/sec - GET 4KiB buf, 50/5 min/max/avg/p95: 0/ 3/ 0.80/ 2.00 797ms total, 62735.26 ops/sec - GET 4KiB buf, batch 50/5 min/max/avg/p95: 0/ 2/ 0.39/ 1.00 396ms total, 126262.63 ops/sec - INCR, 1/5 min/max/avg/p95: 0/ 2/ 0.03/ 0.00 1376ms total, 36337.21 ops/sec - INCR, 50/5 min/max/avg/p95: 0/ 3/ 0.53/ 1.00 529ms total, 94517.96 ops/sec - INCR, batch 50/5 min/max/avg/p95: 0/ 1/ 0.17/ 1.00 339ms total, 294985.25 ops/sec - LPUSH, 1/5 min/max/avg/p95: 0/ 3/ 0.03/ 0.00 1394ms total, 35868.01 ops/sec - LPUSH, 50/5 min/max/avg/p95: 0/ 3/ 0.58/ 1.00 584ms total, 85616.44 ops/sec - LPUSH, batch 50/5 min/max/avg/p95: 0/ 1/ 0.19/ 1.00 383ms total, 261096.61 ops/sec - LRANGE 10, 1/5 min/max/avg/p95: 0/ 4/ 0.03/ 0.00 1706ms total, 29308.32 ops/sec - LRANGE 10, 50/5 min/max/avg/p95: 0/ 3/ 0.71/ 1.00 712ms total, 70224.72 ops/sec - LRANGE 10, batch 50/5 min/max/avg/p95: 0/ 1/ 0.38/ 1.00 772ms total, 129533.68 ops/sec - LRANGE 100, 1/5 min/max/avg/p95: 0/ 1/ 0.06/ 1.00 3026ms total, 16523.46 ops/sec - LRANGE 100, 50/5 min/max/avg/p95: 0/ 5/ 1.88/ 3.00 1882ms total, 26567.48 ops/sec - LRANGE 100, batch 50/5 min/max/avg/p95: 2/ 4/ 2.09/ 3.00 4189ms total, 23872.05 ops/sec - SET 4MiB buf, 1/5 min/max/avg/p95: 1/ 7/ 2.08/ 3.00 1044ms total, 478.93 ops/sec - SET 4MiB buf, 20/5 min/max/avg/p95: 17/ 50/ 40.02/ 46.00 1022ms total, 489.24 ops/sec - SET 4MiB buf, batch 20/5 min/max/avg/p95: 37/ 45/ 39.00/ 44.40 975ms total, 512.82 ops/sec - GET 4MiB str, 1/5 min/max/avg/p95: 4/ 15/ 6.31/ 10.00 634ms total, 157.73 ops/sec - GET 4MiB str, 20/5 min/max/avg/p95: 7/ 124/ 88.27/ 110.80 476ms total, 210.08 ops/sec - GET 4MiB str, batch 20/5 min/max/avg/p95: 76/ 99/ 89.00/ 99.00 446ms total, 224.22 ops/sec - GET 4MiB buf, 1/5 min/max/avg/p95: 4/ 12/ 5.67/ 10.00 568ms total, 176.06 ops/sec - GET 4MiB buf, 20/5 min/max/avg/p95: 14/ 133/ 85.34/ 107.95 458ms total, 218.34 ops/sec - GET 4MiB buf, batch 20/5 min/max/avg/p95: 78/ 96/ 88.00/ 96.00 440ms total, 227.27 ops/sec -End of tests. Total time elapsed: 50421 ms -``` +Client count: 1, node version: 4.2.1, server version: 3.0.3, parser: hiredis + PING, 1/1 min/max/avg/p95: 0/ 4/ 0.02/ 0.00 10001ms total, 38850.41 ops/sec + PING, batch 50/1 min/max/avg/p95: 0/ 3/ 0.10/ 1.00 10001ms total, 488376.16 ops/sec + SET 4B str, 1/1 min/max/avg/p95: 0/ 2/ 0.03/ 0.00 10001ms total, 35782.02 ops/sec + SET 4B str, batch 50/1 min/max/avg/p95: 0/ 2/ 0.14/ 1.00 10001ms total, 349740.03 ops/sec + SET 4B buf, 1/1 min/max/avg/p95: 0/ 5/ 0.04/ 0.00 10001ms total, 23497.75 ops/sec + SET 4B buf, batch 50/1 min/max/avg/p95: 0/ 3/ 0.28/ 1.00 10001ms total, 177087.29 ops/sec + GET 4B str, 1/1 min/max/avg/p95: 0/ 4/ 0.03/ 0.00 10001ms total, 37044.10 ops/sec + GET 4B str, batch 50/1 min/max/avg/p95: 0/ 4/ 0.12/ 1.00 10001ms total, 421987.80 ops/sec + GET 4B buf, 1/1 min/max/avg/p95: 0/ 4/ 0.03/ 0.00 10001ms total, 35608.24 ops/sec + GET 4B buf, batch 50/1 min/max/avg/p95: 0/ 3/ 0.12/ 1.00 10001ms total, 416593.34 ops/sec + SET 4KiB str, 1/1 min/max/avg/p95: 0/ 4/ 0.03/ 0.00 10001ms total, 30014.10 ops/sec + SET 4KiB str, batch 50/1 min/max/avg/p95: 0/ 4/ 0.34/ 1.00 10001ms total, 147705.23 ops/sec + SET 4KiB buf, 1/1 min/max/avg/p95: 0/ 4/ 0.04/ 0.00 10001ms total, 23803.52 ops/sec + SET 4KiB buf, batch 50/1 min/max/avg/p95: 0/ 4/ 0.37/ 1.00 10001ms total, 132611.74 ops/sec + GET 4KiB str, 1/1 min/max/avg/p95: 0/ 5/ 0.03/ 0.00 10001ms total, 34216.98 ops/sec + GET 4KiB str, batch 50/1 min/max/avg/p95: 0/ 4/ 0.32/ 1.00 10001ms total, 153039.70 ops/sec + GET 4KiB buf, 1/1 min/max/avg/p95: 0/ 3/ 0.03/ 0.00 10001ms total, 34169.18 ops/sec + GET 4KiB buf, batch 50/1 min/max/avg/p95: 0/ 2/ 0.32/ 1.00 10001ms total, 153264.67 ops/sec + INCR, 1/1 min/max/avg/p95: 0/ 3/ 0.03/ 0.00 10001ms total, 36307.17 ops/sec + INCR, batch 50/1 min/max/avg/p95: 0/ 4/ 0.12/ 1.00 10001ms total, 412438.76 ops/sec + LPUSH, 1/1 min/max/avg/p95: 0/ 4/ 0.03/ 0.00 10001ms total, 36073.89 ops/sec + LPUSH, batch 50/1 min/max/avg/p95: 0/ 2/ 0.14/ 1.00 10001ms total, 355954.40 ops/sec + LRANGE 10, 1/1 min/max/avg/p95: 0/ 2/ 0.03/ 0.00 10001ms total, 30395.66 ops/sec + LRANGE 10, batch 50/1 min/max/avg/p95: 0/ 3/ 0.33/ 1.00 10001ms total, 149400.06 ops/sec + LRANGE 100, 1/1 min/max/avg/p95: 0/ 2/ 0.06/ 1.00 10001ms total, 16814.62 ops/sec + LRANGE 100, batch 50/1 min/max/avg/p95: 1/ 4/ 2.01/ 2.00 10002ms total, 24790.04 ops/sec + SET 4MiB str, 1/1 min/max/avg/p95: 1/ 7/ 2.01/ 2.00 10002ms total, 496.90 ops/sec + SET 4MiB str, batch 20/1 min/max/avg/p95: 100/ 135/ 109.58/ 125.00 10085ms total, 182.45 ops/sec + SET 4MiB buf, 1/1 min/max/avg/p95: 1/ 5/ 1.87/ 2.00 10001ms total, 531.75 ops/sec + SET 4MiB buf, batch 20/1 min/max/avg/p95: 52/ 77/ 58.90/ 68.45 10016ms total, 339.46 ops/sec + GET 4MiB str, 1/1 min/max/avg/p95: 3/ 19/ 5.79/ 11.00 10005ms total, 172.51 ops/sec + GET 4MiB str, batch 20/1 min/max/avg/p95: 73/ 112/ 89.89/ 107.00 10072ms total, 222.40 ops/sec + GET 4MiB buf, 1/1 min/max/avg/p95: 3/ 13/ 5.35/ 9.00 10002ms total, 186.76 ops/sec + GET 4MiB buf, batch 20/1 min/max/avg/p95: 76/ 106/ 85.37/ 98.00 10077ms total, 234.20 ops/sec + ``` The hiredis and js parser should most of the time be on the same level. The js parser lacks speed for large responses though. Therefor the hiredis parser is the default used in node_redis and we recommend using the hiredis parser. To use `hiredis`, do: diff --git a/benchmarks/multi_bench.js b/benchmarks/multi_bench.js index 43831414de6..d4b506e78c9 100644 --- a/benchmarks/multi_bench.js +++ b/benchmarks/multi_bench.js @@ -217,71 +217,71 @@ very_large_str = (new Array((4 * 1024 * 1024) + 1).join('-')); very_large_buf = new Buffer(very_large_str); tests.push(new Test({descr: 'PING', command: 'ping', args: [], pipeline: 1})); -tests.push(new Test({descr: 'PING', command: 'ping', args: [], pipeline: 50})); +// tests.push(new Test({descr: 'PING', command: 'ping', args: [], pipeline: 50})); tests.push(new Test({descr: 'PING', command: 'ping', args: [], batch: 50})); tests.push(new Test({descr: 'SET 4B str', command: 'set', args: ['foo_rand000000000000', small_str], pipeline: 1})); -tests.push(new Test({descr: 'SET 4B str', command: 'set', args: ['foo_rand000000000000', small_str], pipeline: 50})); +// tests.push(new Test({descr: 'SET 4B str', command: 'set', args: ['foo_rand000000000000', small_str], pipeline: 50})); tests.push(new Test({descr: 'SET 4B str', command: 'set', args: ['foo_rand000000000000', small_str], batch: 50})); tests.push(new Test({descr: 'SET 4B buf', command: 'set', args: ['foo_rand000000000000', small_buf], pipeline: 1})); -tests.push(new Test({descr: 'SET 4B buf', command: 'set', args: ['foo_rand000000000000', small_buf], pipeline: 50})); +// tests.push(new Test({descr: 'SET 4B buf', command: 'set', args: ['foo_rand000000000000', small_buf], pipeline: 50})); tests.push(new Test({descr: 'SET 4B buf', command: 'set', args: ['foo_rand000000000000', small_buf], batch: 50})); tests.push(new Test({descr: 'GET 4B str', command: 'get', args: ['foo_rand000000000000'], pipeline: 1})); -tests.push(new Test({descr: 'GET 4B str', command: 'get', args: ['foo_rand000000000000'], pipeline: 50})); +// tests.push(new Test({descr: 'GET 4B str', command: 'get', args: ['foo_rand000000000000'], pipeline: 50})); tests.push(new Test({descr: 'GET 4B str', command: 'get', args: ['foo_rand000000000000'], batch: 50})); tests.push(new Test({descr: 'GET 4B buf', command: 'get', args: ['foo_rand000000000000'], pipeline: 1, client_opts: { return_buffers: true} })); -tests.push(new Test({descr: 'GET 4B buf', command: 'get', args: ['foo_rand000000000000'], pipeline: 50, client_opts: { return_buffers: true} })); +// tests.push(new Test({descr: 'GET 4B buf', command: 'get', args: ['foo_rand000000000000'], pipeline: 50, client_opts: { return_buffers: true} })); tests.push(new Test({descr: 'GET 4B buf', command: 'get', args: ['foo_rand000000000000'], batch: 50, client_opts: { return_buffers: true} })); tests.push(new Test({descr: 'SET 4KiB str', command: 'set', args: ['foo_rand000000000001', large_str], pipeline: 1})); -tests.push(new Test({descr: 'SET 4KiB str', command: 'set', args: ['foo_rand000000000001', large_str], pipeline: 50})); +// tests.push(new Test({descr: 'SET 4KiB str', command: 'set', args: ['foo_rand000000000001', large_str], pipeline: 50})); tests.push(new Test({descr: 'SET 4KiB str', command: 'set', args: ['foo_rand000000000001', large_str], batch: 50})); tests.push(new Test({descr: 'SET 4KiB buf', command: 'set', args: ['foo_rand000000000001', large_buf], pipeline: 1})); -tests.push(new Test({descr: 'SET 4KiB buf', command: 'set', args: ['foo_rand000000000001', large_buf], pipeline: 50})); +// tests.push(new Test({descr: 'SET 4KiB buf', command: 'set', args: ['foo_rand000000000001', large_buf], pipeline: 50})); tests.push(new Test({descr: 'SET 4KiB buf', command: 'set', args: ['foo_rand000000000001', large_buf], batch: 50})); tests.push(new Test({descr: 'GET 4KiB str', command: 'get', args: ['foo_rand000000000001'], pipeline: 1})); -tests.push(new Test({descr: 'GET 4KiB str', command: 'get', args: ['foo_rand000000000001'], pipeline: 50})); +// tests.push(new Test({descr: 'GET 4KiB str', command: 'get', args: ['foo_rand000000000001'], pipeline: 50})); tests.push(new Test({descr: 'GET 4KiB str', command: 'get', args: ['foo_rand000000000001'], batch: 50})); tests.push(new Test({descr: 'GET 4KiB buf', command: 'get', args: ['foo_rand000000000001'], pipeline: 1, client_opts: { return_buffers: true} })); -tests.push(new Test({descr: 'GET 4KiB buf', command: 'get', args: ['foo_rand000000000001'], pipeline: 50, client_opts: { return_buffers: true} })); +// tests.push(new Test({descr: 'GET 4KiB buf', command: 'get', args: ['foo_rand000000000001'], pipeline: 50, client_opts: { return_buffers: true} })); tests.push(new Test({descr: 'GET 4KiB buf', command: 'get', args: ['foo_rand000000000001'], batch: 50, client_opts: { return_buffers: true} })); tests.push(new Test({descr: 'INCR', command: 'incr', args: ['counter_rand000000000000'], pipeline: 1})); -tests.push(new Test({descr: 'INCR', command: 'incr', args: ['counter_rand000000000000'], pipeline: 50})); +// tests.push(new Test({descr: 'INCR', command: 'incr', args: ['counter_rand000000000000'], pipeline: 50})); tests.push(new Test({descr: 'INCR', command: 'incr', args: ['counter_rand000000000000'], batch: 50})); tests.push(new Test({descr: 'LPUSH', command: 'lpush', args: ['mylist', small_str], pipeline: 1})); -tests.push(new Test({descr: 'LPUSH', command: 'lpush', args: ['mylist', small_str], pipeline: 50})); +// tests.push(new Test({descr: 'LPUSH', command: 'lpush', args: ['mylist', small_str], pipeline: 50})); tests.push(new Test({descr: 'LPUSH', command: 'lpush', args: ['mylist', small_str], batch: 50})); tests.push(new Test({descr: 'LRANGE 10', command: 'lrange', args: ['mylist', '0', '9'], pipeline: 1})); -tests.push(new Test({descr: 'LRANGE 10', command: 'lrange', args: ['mylist', '0', '9'], pipeline: 50})); +// tests.push(new Test({descr: 'LRANGE 10', command: 'lrange', args: ['mylist', '0', '9'], pipeline: 50})); tests.push(new Test({descr: 'LRANGE 10', command: 'lrange', args: ['mylist', '0', '9'], batch: 50})); tests.push(new Test({descr: 'LRANGE 100', command: 'lrange', args: ['mylist', '0', '99'], pipeline: 1})); -tests.push(new Test({descr: 'LRANGE 100', command: 'lrange', args: ['mylist', '0', '99'], pipeline: 50})); +// tests.push(new Test({descr: 'LRANGE 100', command: 'lrange', args: ['mylist', '0', '99'], pipeline: 50})); tests.push(new Test({descr: 'LRANGE 100', command: 'lrange', args: ['mylist', '0', '99'], batch: 50})); -tests.push(new Test({descr: 'SET 4MiB buf', command: 'set', args: ['foo_rand000000000002', very_large_buf], pipeline: 1})); -tests.push(new Test({descr: 'SET 4MiB buf', command: 'set', args: ['foo_rand000000000002', very_large_buf], pipeline: 20})); -tests.push(new Test({descr: 'SET 4MiB buf', command: 'set', args: ['foo_rand000000000002', very_large_buf], batch: 20})); - tests.push(new Test({descr: 'SET 4MiB str', command: 'set', args: ['foo_rand000000000002', very_large_str], pipeline: 1})); -tests.push(new Test({descr: 'SET 4MiB str', command: 'set', args: ['foo_rand000000000002', very_large_str], pipeline: 20})); +// tests.push(new Test({descr: 'SET 4MiB str', command: 'set', args: ['foo_rand000000000002', very_large_str], pipeline: 20})); tests.push(new Test({descr: 'SET 4MiB str', command: 'set', args: ['foo_rand000000000002', very_large_str], batch: 20})); +tests.push(new Test({descr: 'SET 4MiB buf', command: 'set', args: ['foo_rand000000000002', very_large_buf], pipeline: 1})); +// tests.push(new Test({descr: 'SET 4MiB buf', command: 'set', args: ['foo_rand000000000002', very_large_buf], pipeline: 20})); +tests.push(new Test({descr: 'SET 4MiB buf', command: 'set', args: ['foo_rand000000000002', very_large_buf], batch: 20})); + tests.push(new Test({descr: 'GET 4MiB str', command: 'get', args: ['foo_rand000000000002'], pipeline: 1})); -tests.push(new Test({descr: 'GET 4MiB str', command: 'get', args: ['foo_rand000000000002'], pipeline: 20})); +// tests.push(new Test({descr: 'GET 4MiB str', command: 'get', args: ['foo_rand000000000002'], pipeline: 20})); tests.push(new Test({descr: 'GET 4MiB str', command: 'get', args: ['foo_rand000000000002'], batch: 20})); tests.push(new Test({descr: 'GET 4MiB buf', command: 'get', args: ['foo_rand000000000002'], pipeline: 1, client_opts: { return_buffers: true} })); -tests.push(new Test({descr: 'GET 4MiB buf', command: 'get', args: ['foo_rand000000000002'], pipeline: 20, client_opts: { return_buffers: true} })); +// tests.push(new Test({descr: 'GET 4MiB buf', command: 'get', args: ['foo_rand000000000002'], pipeline: 20, client_opts: { return_buffers: true} })); tests.push(new Test({descr: 'GET 4MiB buf', command: 'get', args: ['foo_rand000000000002'], batch: 20, client_opts: { return_buffers: true} })); function next() { diff --git a/changelog.md b/changelog.md index ef585e082d8..a77ba9082ca 100644 --- a/changelog.md +++ b/changelog.md @@ -1,9 +1,21 @@ Changelog ========= +## v.2.3.0 - xx XXX, 2015 + +Features + +- Improve speed further for: ([@BridgeAR](https://github.com/BridgeAR)) + - saving big strings (up to +300%) + - using .multi / .batch (up to +50% / on Node.js 0.10.x +300%) + - saving small buffers +- Increased coverage to 99% ([@BridgeAR](https://github.com/BridgeAR)) + Bugfixes - Fixed a js parser error that could result in a timeout ([@BridgeAR](https://github.com/BridgeAR)) +- Fixed .multi / .batch used with Node.js 0.10.x not working properly after a reconnect ([@BridgeAR](https://github.com/BridgeAR)) +- Fixed fired but not yet returned commands not being rejected after a connection loss ([@BridgeAR](https://github.com/BridgeAR)) ## v.2.2.5 - 18 Oct, 2015 From 4e5e4635aa7fccb47e8bc909c0a1436e157079eb Mon Sep 17 00:00:00 2001 From: Ruben Bridgewater Date: Wed, 28 Oct 2015 00:58:31 +0100 Subject: [PATCH 0454/1748] Use debug statement for incoming data --- index.js | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/index.js b/index.js index 94d213e92f0..197ad542820 100644 --- a/index.js +++ b/index.js @@ -116,7 +116,7 @@ RedisClient.prototype.install_stream_listeners = function() { this.stream.on('data', function (buffer_from_socket) { // The data.toString() has a significant impact on big chunks and therefor this should only be used if necessary - // debug('Net read ' + this.address + ' id ' + this.connection_id + ': ' + data.toString()); + debug('Net read ' + this.address + ' id ' + this.connection_id); // + ': ' + data.toString()); self.reply_parser.execute(buffer_from_socket); }); From afc4989495245e683ce70a234c55046a51e73c08 Mon Sep 17 00:00:00 2001 From: Ruben Bridgewater Date: Wed, 28 Oct 2015 23:51:00 +0100 Subject: [PATCH 0455/1748] Remove command queue high and low water marks --- README.md | 6 +++--- changelog.md | 3 +++ examples/backpressure_drain.js | 10 ++++------ examples/pub_sub.js | 2 +- index.js | 2 -- test/commands/keys.spec.js | 2 -- 6 files changed, 11 insertions(+), 14 deletions(-) diff --git a/README.md b/README.md index 7ecaaaaad33..597f817ebf8 100644 --- a/README.md +++ b/README.md @@ -154,11 +154,11 @@ So please attach the error listener to node_redis. `client` will emit `drain` when the TCP connection to the Redis server has been buffering, but is now writable. This event can be used to stream commands in to Redis and adapt to backpressure. -All commands return a boolean if the stream had to buffer or not. If false is returned the stream had to buffer. +If the stream is buffering `client.should_buffer` is set to true. Otherwise the variable is always set to false. That way you can decide when to reduce your send rate and resume sending commands when you get `drain`. -You can manually control the low water and high water marks by passing ommand_queue_high_water` and `command_queue_low_water` to the client options. -Check the [Node.js streams API](https://nodejs.org/api/stream.html) for further info. +You can also check the return value of each command as it will also return the backpressure indicator. +If false is returned the stream had to buffer. ### "idle" diff --git a/changelog.md b/changelog.md index a77ba9082ca..533088d6241 100644 --- a/changelog.md +++ b/changelog.md @@ -10,6 +10,9 @@ Features - using .multi / .batch (up to +50% / on Node.js 0.10.x +300%) - saving small buffers - Increased coverage to 99% ([@BridgeAR](https://github.com/BridgeAR)) +- Refactored manual backpressure control ([@BridgeAR](https://github.com/BridgeAR)) + - Removed the high water mark and low water mark. Such a mechanism should be implemented by a user instead + - The `drain` event is from now on only emitted if the stream really had to buffer Bugfixes diff --git a/examples/backpressure_drain.js b/examples/backpressure_drain.js index 12cd24107a5..f9ab961c332 100644 --- a/examples/backpressure_drain.js +++ b/examples/backpressure_drain.js @@ -1,10 +1,7 @@ 'use strict'; var redis = require('../index'), - client = redis.createClient(null, null, { - command_queue_high_water: 5, - command_queue_low_water: 1 - }), + client = redis.createClient(), remaining_ops = 100000, paused = false; function op() { @@ -14,11 +11,12 @@ function op() { } remaining_ops--; - if (client.hset('test hash', 'val ' + remaining_ops, remaining_ops) === false) { + client.hset('test hash', 'val ' + remaining_ops, remaining_ops); + if (client.should_buffer === true) { console.log('Pausing at ' + remaining_ops); paused = true; } else { - process.nextTick(op); + setTimeout(op, 1); } } diff --git a/examples/pub_sub.js b/examples/pub_sub.js index 006c730839a..8d691126daa 100644 --- a/examples/pub_sub.js +++ b/examples/pub_sub.js @@ -4,7 +4,7 @@ var redis = require('redis'), client1 = redis.createClient(), msg_count = 0, client2 = redis.createClient(); -// Most clients probably don't do much on 'subscribe'. This example uses it to coordinate things within one program. +// Most clients probably don't do much on 'subscribe'. This example uses it to coordinate things within one program. client1.on('subscribe', function (channel, count) { console.log('client1 subscribed to ' + channel + ', ' + count + ' total subscriptions'); if (count === 2) { diff --git a/index.js b/index.js index 197ad542820..d7bd5245f5a 100644 --- a/index.js +++ b/index.js @@ -82,8 +82,6 @@ function RedisClient(stream, options) { options.detect_buffers = false; } this.should_buffer = false; - this.command_queue_high_water = +options.command_queue_high_water || 1000; - this.command_queue_low_water = options.command_queue_low_water | 0; this.max_attempts = options.max_attempts | 0; this.command_queue = new Queue(); // Holds sent commands to de-pipeline them this.offline_queue = new Queue(); // Holds commands issued but not able to be sent diff --git a/test/commands/keys.spec.js b/test/commands/keys.spec.js index 6437aeb0a8a..5cb84228058 100644 --- a/test/commands/keys.spec.js +++ b/test/commands/keys.spec.js @@ -15,8 +15,6 @@ describe("The 'keys' method", function () { beforeEach(function (done) { args = args || {}; - // This is going to test if the high water is also respected - args.command_queue_high_water = 100; client = redis.createClient.apply(redis.createClient, args); client.once("ready", function () { client.flushdb(done); From dc6fc9c11391f3cde0bd927332160a5c89cbbe31 Mon Sep 17 00:00:00 2001 From: Ruben Bridgewater Date: Thu, 29 Oct 2015 13:49:01 +0100 Subject: [PATCH 0456/1748] Use connect_timeout also as the socket_timeout if explicitly provided Fixes #587 Fixes #393 Closes #652 Closes #394 --- README.md | 6 +++--- changelog.md | 2 ++ index.js | 9 ++++++++- test/connection.spec.js | 42 +++++++++++++++++++++++++++++++++++++++++ 4 files changed, 55 insertions(+), 4 deletions(-) diff --git a/README.md b/README.md index 597f817ebf8..99e5ed63c54 100644 --- a/README.md +++ b/README.md @@ -200,9 +200,9 @@ once the connection has been established. Setting `enable_offline_queue` to with an error, or an error will be emitted if no callback is specified. * `retry_max_delay`: *null*; By default every time the client tries to connect and fails the reconnection delay almost doubles. This delay normally grows infinitely, but setting `retry_max_delay` limits it to the maximum value, provided in milliseconds. -* `connect_timeout`: *86400000*; Setting `connect_timeout` limits total time for client to reconnect. -The value is provided in milliseconds and is counted once the disconnect occurred. The last retry is going to happen exactly at the timeout time. -That way the default is to try reconnecting until 24h passed. +* `connect_timeout`: *3600000*; Setting `connect_timeout` limits total time for client to connect and reconnect. +The value is provided in milliseconds and is counted from the moment on a new client is created / a connection is lost. The last retry is going to happen exactly at the timeout time. +Default is to try connecting until the default system socket timeout has been exceeded and to try reconnecting until 1h passed. * `max_attempts`: *0*; By default client will try reconnecting until connected. Setting `max_attempts` limits total amount of connection tries. Setting this to 1 will prevent any reconnect tries. * `auth_pass`: *null*; If set, client will run redis auth command on connect. diff --git a/changelog.md b/changelog.md index 533088d6241..8fdd6c800a8 100644 --- a/changelog.md +++ b/changelog.md @@ -13,12 +13,14 @@ Features - Refactored manual backpressure control ([@BridgeAR](https://github.com/BridgeAR)) - Removed the high water mark and low water mark. Such a mechanism should be implemented by a user instead - The `drain` event is from now on only emitted if the stream really had to buffer +- Reduced the default connect_timeout to be one hour instead of 24h ([@BridgeAR](https://github.com/BridgeAR)) Bugfixes - Fixed a js parser error that could result in a timeout ([@BridgeAR](https://github.com/BridgeAR)) - Fixed .multi / .batch used with Node.js 0.10.x not working properly after a reconnect ([@BridgeAR](https://github.com/BridgeAR)) - Fixed fired but not yet returned commands not being rejected after a connection loss ([@BridgeAR](https://github.com/BridgeAR)) +- Fixed connect_timeout not respected if no connection has ever been established ([@gagle](https://github.com/gagle) & [@benjie](https://github.com/benjie)) ## v.2.2.5 - 18 Oct, 2015 diff --git a/index.js b/index.js index d7bd5245f5a..e13d93a02fd 100644 --- a/index.js +++ b/index.js @@ -85,7 +85,7 @@ function RedisClient(stream, options) { this.max_attempts = options.max_attempts | 0; this.command_queue = new Queue(); // Holds sent commands to de-pipeline them this.offline_queue = new Queue(); // Holds commands issued but not able to be sent - this.connect_timeout = +options.connect_timeout || 86400000; // 24 * 60 * 60 * 1000 ms + this.connect_timeout = +options.connect_timeout || 3600000; // 60 * 60 * 1000 ms this.enable_offline_queue = options.enable_offline_queue === false ? false : true; this.retry_max_delay = +options.retry_max_delay || null; this.initialize_retry_vars(); @@ -108,6 +108,13 @@ util.inherits(RedisClient, events.EventEmitter); RedisClient.prototype.install_stream_listeners = function() { var self = this; + if (this.options.connect_timeout) { + this.stream.setTimeout(this.connect_timeout, function () { + self.retry_totaltime = self.connect_timeout; + self.connection_gone('timeout'); + }); + } + this.stream.on('connect', function () { self.on_connect(); }); diff --git a/test/connection.spec.js b/test/connection.spec.js index 62251fdef4f..8a04366ba11 100644 --- a/test/connection.spec.js +++ b/test/connection.spec.js @@ -125,6 +125,48 @@ describe("connection tests", function () { describe("when not connected", function () { + it("emit an error after the socket timeout exceeded the connect_timeout time", function (done) { + var connect_timeout = 1000; // in ms + var client = redis.createClient({ + parser: parser, + host: '1.2.3.4', + connect_timeout: connect_timeout + }); + assert(client.stream._events.timeout); + assert.strictEqual(client.address, '1.2.3.4:6379'); + var time = Date.now(); + + client.on("reconnecting", function (params) { + throw new Error('No reconnect, since no connection was ever established'); + }); + + client.on('error', function(err) { + assert(/Redis connection in broken state: connection timeout.*?exceeded./.test(err.message)); + assert(Date.now() - time < connect_timeout + 50); + done(); + }); + }); + + it("use the system socket timeout if the connect_timeout has not been provided", function () { + var client = redis.createClient({ + parser: parser, + host: '1.2.3.4' + }); + assert(client.stream._events.timeout === undefined); + }); + + it("clears the socket timeout after a connection has been established", function (done) { + var client = redis.createClient({ + parser: parser, + connect_timeout: 1000 + }); + assert.strictEqual(client.stream._idleTimeout, 1000); + client.on('connect', function () { + assert.strictEqual(client.stream._idleTimeout, -1); + done(); + }); + }); + it("connect with host and port provided in the options object", function (done) { client = redis.createClient({ host: 'localhost', From 7718e219e9fbf3ae18aec354ff66495cc9397c4b Mon Sep 17 00:00:00 2001 From: Ruben Bridgewater Date: Thu, 29 Oct 2015 15:04:47 +0100 Subject: [PATCH 0457/1748] Remove listener if not needed anymore and alawys end a client after a test --- index.js | 3 ++- test/auth.spec.js | 3 +++ test/connection.spec.js | 23 ++++++++++++++--------- 3 files changed, 19 insertions(+), 10 deletions(-) diff --git a/index.js b/index.js index e13d93a02fd..1fc40e8ebde 100644 --- a/index.js +++ b/index.js @@ -115,7 +115,8 @@ RedisClient.prototype.install_stream_listeners = function() { }); } - this.stream.on('connect', function () { + this.stream.once('connect', function () { + this.removeAllListeners("timeout"); self.on_connect(); }); diff --git a/test/auth.spec.js b/test/auth.spec.js index ef34b82ccbc..35481230ae4 100644 --- a/test/auth.spec.js +++ b/test/auth.spec.js @@ -20,6 +20,9 @@ describe("client authentication", function () { var auth = 'porkchopsandwiches'; var client = null; + beforeEach(function () { + client = null; + }); afterEach(function () { client.end(); }); diff --git a/test/connection.spec.js b/test/connection.spec.js index 8a04366ba11..22a64afdc43 100644 --- a/test/connection.spec.js +++ b/test/connection.spec.js @@ -13,10 +13,11 @@ describe("connection tests", function () { var client; + beforeEach(function () { + client = null; + }); afterEach(function () { - if (client) { - client.end(); - } + client.end(); }); describe("on lost connection", function () { @@ -127,13 +128,13 @@ describe("connection tests", function () { it("emit an error after the socket timeout exceeded the connect_timeout time", function (done) { var connect_timeout = 1000; // in ms - var client = redis.createClient({ + client = redis.createClient({ parser: parser, - host: '1.2.3.4', + host: '192.168.74.167', connect_timeout: connect_timeout }); assert(client.stream._events.timeout); - assert.strictEqual(client.address, '1.2.3.4:6379'); + assert.strictEqual(client.address, '192.168.74.167:6379'); var time = Date.now(); client.on("reconnecting", function (params) { @@ -148,21 +149,22 @@ describe("connection tests", function () { }); it("use the system socket timeout if the connect_timeout has not been provided", function () { - var client = redis.createClient({ + client = redis.createClient({ parser: parser, - host: '1.2.3.4' + host: '192.168.74.167' }); assert(client.stream._events.timeout === undefined); }); it("clears the socket timeout after a connection has been established", function (done) { - var client = redis.createClient({ + client = redis.createClient({ parser: parser, connect_timeout: 1000 }); assert.strictEqual(client.stream._idleTimeout, 1000); client.on('connect', function () { assert.strictEqual(client.stream._idleTimeout, -1); + assert(client.stream._events.timeout === undefined); done(); }); }); @@ -255,6 +257,9 @@ describe("connection tests", function () { }); it("throws on strange connection info", function () { + client = { + end: function() {} + }; try { redis.createClient(true); throw new Error('failed'); From 92e49437f897742f632610cb809014bc632352da Mon Sep 17 00:00:00 2001 From: Ruben Bridgewater Date: Thu, 29 Oct 2015 17:25:09 +0100 Subject: [PATCH 0458/1748] Update appyevor --- README.md | 2 +- appveyor.yml | 6 ++++++ 2 files changed, 7 insertions(+), 1 deletion(-) diff --git a/README.md b/README.md index 99e5ed63c54..6c967dcccab 100644 --- a/README.md +++ b/README.md @@ -3,7 +3,7 @@ redis - a node.js redis client [![Build Status](https://travis-ci.org/NodeRedis/node_redis.png)](https://travis-ci.org/NodeRedis/node_redis) [![Coverage Status](https://coveralls.io/repos/NodeRedis/node_redis/badge.svg?branch=)](https://coveralls.io/r/NodeRedis/node_redis?branch=) -[![Windows Tests](https://img.shields.io/appveyor/ci/bcoe/node-redis/master.svg?label=Windows%20Tests)](https://ci.appveyor.com/project/bcoe/node-redis) +[![Windows Tests](https://ci.appveyor.com/api/projects/status/koc3xraik0xq3b56/branch/master?svg=true)](https://ci.appveyor.com/project/BridgeAR/node-redis/branch/master) This is a complete and feature rich Redis client for node.js. It supports all Redis commands and focuses on performance. diff --git a/appveyor.yml b/appveyor.yml index 60124f7ba1a..51879a7964b 100644 --- a/appveyor.yml +++ b/appveyor.yml @@ -8,6 +8,12 @@ environment: - nodejs_version: "3" - nodejs_version: "4" +pull_requests: + do_not_increment_build_number: true + +platform: Any CPU +shallow_clone: true + # Install scripts. (runs after repo cloning) install: # Install the Redis From c3502c799f2c512a10edca031780c7912c299c81 Mon Sep 17 00:00:00 2001 From: Ruben Bridgewater Date: Thu, 29 Oct 2015 17:25:23 +0100 Subject: [PATCH 0459/1748] Add gitter batch --- README.md | 1 + 1 file changed, 1 insertion(+) diff --git a/README.md b/README.md index 6c967dcccab..44ac71f6b91 100644 --- a/README.md +++ b/README.md @@ -4,6 +4,7 @@ redis - a node.js redis client [![Build Status](https://travis-ci.org/NodeRedis/node_redis.png)](https://travis-ci.org/NodeRedis/node_redis) [![Coverage Status](https://coveralls.io/repos/NodeRedis/node_redis/badge.svg?branch=)](https://coveralls.io/r/NodeRedis/node_redis?branch=) [![Windows Tests](https://ci.appveyor.com/api/projects/status/koc3xraik0xq3b56/branch/master?svg=true)](https://ci.appveyor.com/project/BridgeAR/node-redis/branch/master) +[![Gitter](https://badges.gitter.im/Join%20Chat.svg)](https://gitter.im/NodeRedis/node_redis?utm_source=badge&utm_medium=badge&utm_campaign=pr-badge) This is a complete and feature rich Redis client for node.js. It supports all Redis commands and focuses on performance. From d454e4025b8deb90678df50cdc34ec579adf802f Mon Sep 17 00:00:00 2001 From: Ruben Bridgewater Date: Thu, 29 Oct 2015 23:21:08 +0100 Subject: [PATCH 0460/1748] Fix an issue with .multi after a reconnect on node 0.10 Add .path to .createClient options object for unix sockets --- README.md | 3 +- changelog.md | 1 + index.js | 128 ++++++++++++++++-------------------- test/commands/multi.spec.js | 25 +++++++ test/connection.spec.js | 41 ++++++++---- test/helper.js | 2 +- 6 files changed, 113 insertions(+), 87 deletions(-) diff --git a/README.md b/README.md index 44ac71f6b91..214e64967df 100644 --- a/README.md +++ b/README.md @@ -3,7 +3,7 @@ redis - a node.js redis client [![Build Status](https://travis-ci.org/NodeRedis/node_redis.png)](https://travis-ci.org/NodeRedis/node_redis) [![Coverage Status](https://coveralls.io/repos/NodeRedis/node_redis/badge.svg?branch=)](https://coveralls.io/r/NodeRedis/node_redis?branch=) -[![Windows Tests](https://ci.appveyor.com/api/projects/status/koc3xraik0xq3b56/branch/master?svg=true)](https://ci.appveyor.com/project/BridgeAR/node-redis/branch/master) +[![Windows Tests](https://ci.appveyor.com/api/projects/status/koc3xraik0xq3b56/branch/master?svg=true&label=Windows%20Tests)](https://ci.appveyor.com/project/BridgeAR/node-redis/branch/master) [![Gitter](https://badges.gitter.im/Join%20Chat.svg)](https://gitter.im/NodeRedis/node_redis?utm_source=badge&utm_medium=badge&utm_campaign=pr-badge) This is a complete and feature rich Redis client for node.js. It supports all Redis commands and focuses on performance. @@ -179,6 +179,7 @@ port and host are probably fine and you don't need to supply any arguments. `cre #### `options` is an object with the following possible properties: * `host`: *127.0.0.1*; The host to connect to * `port`: *6370*; The port to connect to +* `path`: *null*; The unix socket string to connect to * `parser`: *hiredis*; Which Redis protocol reply parser to use. If `hiredis` is not installed it will fallback to `javascript`. * `return_buffers`: *false*; If set to `true`, then all replies will be sent to callbacks as Buffers instead of Strings. * `detect_buffers`: *false*; If set to `true`, then replies will be sent to callbacks as Buffers diff --git a/changelog.md b/changelog.md index 8fdd6c800a8..42f9ef7ee9a 100644 --- a/changelog.md +++ b/changelog.md @@ -14,6 +14,7 @@ Features - Removed the high water mark and low water mark. Such a mechanism should be implemented by a user instead - The `drain` event is from now on only emitted if the stream really had to buffer - Reduced the default connect_timeout to be one hour instead of 24h ([@BridgeAR](https://github.com/BridgeAR)) +- Added .path to redis.createClient(options); ([@BridgeAR](https://github.com/BridgeAR)) Bugfixes diff --git a/index.js b/index.js index 1fc40e8ebde..745efebc3e1 100644 --- a/index.js +++ b/index.js @@ -21,6 +21,7 @@ var debug = function(msg) { }; function noop () {} +function clone (obj) { return JSON.parse(JSON.stringify(obj || {})); } exports.debug_mode = /\bredis\b/i.test(process.env.NODE_DEBUG); @@ -34,31 +35,22 @@ try { parsers.push(require('./lib/parsers/javascript')); -function RedisClient(stream, options) { +function RedisClient(options) { // Copy the options so they are not mutated - options = JSON.parse(JSON.stringify(options || {})); + options = clone(options); + events.EventEmitter.call(this); var self = this; - - this.pipeline = 0; - var cork; - if (!stream.cork) { - cork = function (len) { - self.pipeline = len; - self.pipeline_queue = new Queue(len); - }; - this.uncork = noop; + var cnx_options = {}; + if (options.path) { + cnx_options.path = options.path; + this.address = options.path; } else { - cork = function (len) { - self.pipeline = len; - self.pipeline_queue = new Queue(len); - self.stream.cork(); - }; + cnx_options.port = options.port || default_port; + cnx_options.host = options.host || default_host; + cnx_options.family = options.family === 'IPv6' ? 6 : 4; + this.address = cnx_options.host + ':' + cnx_options.port; } - this.once('ready', function () { - self.cork = cork; - }); - - this.stream = stream; + this.connection_option = cnx_options; this.connection_id = ++connection_id; this.connected = false; this.ready = false; @@ -69,10 +61,8 @@ function RedisClient(stream, options) { if (options.socket_keepalive === undefined) { options.socket_keepalive = true; } - if (options.rename_commands) { - for (var command in options.rename_commands) { // jshint ignore: line - options.rename_commands[command.toLowerCase()] = options.rename_commands[command]; - } + for (var command in options.rename_commands) { // jshint ignore: line + options.rename_commands[command.toLowerCase()] = options.rename_commands[command]; } options.return_buffers = !!options.return_buffers; options.detect_buffers = !!options.detect_buffers; @@ -98,14 +88,15 @@ function RedisClient(stream, options) { this.parser_module = null; this.selected_db = null; // Save the selected db here, used when reconnecting this.old_state = null; + this.pipeline = 0; this.options = options; - this.install_stream_listeners(); - events.EventEmitter.call(this); + self.stream = net.createConnection(cnx_options); + self.install_stream_listeners(); } util.inherits(RedisClient, events.EventEmitter); -RedisClient.prototype.install_stream_listeners = function() { +RedisClient.prototype.install_stream_listeners = function () { var self = this; if (this.options.connect_timeout) { @@ -144,9 +135,7 @@ RedisClient.prototype.install_stream_listeners = function() { }; RedisClient.prototype.cork = noop; -RedisClient.prototype.uncork = function () { - this.stream.uncork(); -}; +RedisClient.prototype.uncork = noop; RedisClient.prototype.initialize_retry_vars = function () { this.retry_timer = null; @@ -332,6 +321,24 @@ RedisClient.prototype.on_ready = function () { this.old_state = null; } + var cork; + if (!this.stream.cork) { + cork = function (len) { + self.pipeline = len; + self.pipeline_queue = new Queue(len); + }; + } else { + cork = function (len) { + self.pipeline = len; + self.pipeline_queue = new Queue(len); + self.stream.cork(); + }; + this.uncork = function () { + self.stream.uncork(); + }; + } + this.cork = cork; + // magically restore any modal commands from a previous connection if (this.selected_db !== null) { // this trick works if and only if the following send_command @@ -472,7 +479,7 @@ var retry_connection = function (self) { self.attempts += 1; self.retry_delay = Math.round(self.retry_delay * self.retry_backoff); - self.stream = net.createConnection(self.connectionOption); + self.stream = net.createConnection(self.connection_option); self.install_stream_listeners(); self.retry_timer = null; @@ -488,6 +495,9 @@ RedisClient.prototype.connection_gone = function (why) { debug('Redis connection is gone from ' + why + ' event.'); this.connected = false; this.ready = false; + // Deactivate cork to work with the offline queue + this.cork = noop; + this.pipeline = 0; if (this.old_state === null) { var state = { @@ -1219,56 +1229,30 @@ Multi.prototype.exec = Multi.prototype.EXEC = Multi.prototype.exec_batch = funct return this._client.should_buffer; }; -var createClient_unix = function (path, options){ - var cnxOptions = { - path: path - }; - var net_client = net.createConnection(cnxOptions); - var redis_client = new RedisClient(net_client, options); - - redis_client.connectionOption = cnxOptions; - redis_client.address = path; - - return redis_client; -}; - -var createClient_tcp = function (port_arg, host_arg, options) { - var cnxOptions = { - port : port_arg || default_port, - host : host_arg || default_host, - family : options.family === 'IPv6' ? 6 : 4 - }; - var net_client = net.createConnection(cnxOptions); - var redis_client = new RedisClient(net_client, options); - - redis_client.connectionOption = cnxOptions; - redis_client.address = cnxOptions.host + ':' + cnxOptions.port; - - return redis_client; -}; - var createClient = function (port_arg, host_arg, options) { if (typeof port_arg === 'object' || port_arg === undefined) { options = port_arg || options || {}; - return createClient_tcp(+options.port, options.host, options); - } - if (typeof port_arg === 'number' || typeof port_arg === 'string' && /^\d+$/.test(port_arg)) { - return createClient_tcp(port_arg, host_arg, options || {}); - } - if (typeof port_arg === 'string') { - options = host_arg || options || {}; - + } else if (typeof port_arg === 'number' || typeof port_arg === 'string' && /^\d+$/.test(port_arg)) { + options = clone(options); + options.host = host_arg; + options.port = port_arg; + } else if (typeof port_arg === 'string') { + options = clone(host_arg || options); var parsed = URL.parse(port_arg, true, true); if (parsed.hostname) { if (parsed.auth) { options.auth_pass = parsed.auth.split(':')[1]; } - return createClient_tcp(parsed.port, parsed.hostname, options); + options.host = parsed.hostname; + options.port = parsed.port; + } else { + options.path = port_arg; } - - return createClient_unix(port_arg, options); } - throw new Error('Unknown type of connection in createClient()'); + if (!options) { + throw new Error('Unknown type of connection in createClient()'); + } + return new RedisClient(options); }; exports.createClient = createClient; diff --git a/test/commands/multi.spec.js b/test/commands/multi.spec.js index ea26fdfb505..b21d3da0d35 100644 --- a/test/commands/multi.spec.js +++ b/test/commands/multi.spec.js @@ -65,6 +65,31 @@ describe("The 'multi' method", function () { multi1.get('m1'); multi1.exec(done); }); + + it("executes a pipelined multi properly after a reconnect in combination with the offline queue", function (done) { + client.once('ready', function () { + client.stream.destroy(); + var called = false; + var multi1 = client.multi(); + multi1.set("m1", "123"); + multi1.get('m1'); + multi1.exec(function (err, res) { + assert(!err); + called = true; + }); + client.once('ready', function () { + var multi1 = client.multi(); + multi1.set("m2", "456"); + multi1.get('m2'); + multi1.exec(function (err, res) { + assert(called); + assert(!err); + assert.strictEqual(res, '456'); + done(); + }); + }); + }); + }); }); describe("when connection is broken", function () { diff --git a/test/connection.spec.js b/test/connection.spec.js index 22a64afdc43..c3b17fae431 100644 --- a/test/connection.spec.js +++ b/test/connection.spec.js @@ -4,7 +4,6 @@ var assert = require("assert"); var config = require("./lib/config"); var helper = require('./helper'); var redis = config.redis; -var net = require('net'); describe("connection tests", function () { helper.allTests(function(parser, ip, args) { @@ -68,7 +67,7 @@ describe("connection tests", function () { client.on('error', function(err) { if (/Redis connection in broken state: connection timeout.*?exceeded./.test(err.message)) { setTimeout(function () { - assert(time === connect_timeout); + assert.strictEqual(time, connect_timeout); done(); }, 500); } @@ -133,7 +132,9 @@ describe("connection tests", function () { host: '192.168.74.167', connect_timeout: connect_timeout }); - assert(client.stream._events.timeout); + process.nextTick(function() { + assert(client.stream._events.timeout); + }); assert.strictEqual(client.address, '192.168.74.167:6379'); var time = Date.now(); @@ -153,7 +154,9 @@ describe("connection tests", function () { parser: parser, host: '192.168.74.167' }); - assert(client.stream._events.timeout === undefined); + process.nextTick(function() { + assert.strictEqual(client.stream._events.timeout, undefined); + }); }); it("clears the socket timeout after a connection has been established", function (done) { @@ -161,10 +164,12 @@ describe("connection tests", function () { parser: parser, connect_timeout: 1000 }); - assert.strictEqual(client.stream._idleTimeout, 1000); + process.nextTick(function() { + assert.strictEqual(client.stream._idleTimeout, 1000); + }); client.on('connect', function () { assert.strictEqual(client.stream._idleTimeout, -1); - assert(client.stream._events.timeout === undefined); + assert.strictEqual(client.stream._events.timeout, undefined); done(); }); }); @@ -182,6 +187,22 @@ describe("connection tests", function () { }); }); + it("connect with path provided in the options object", function (done) { + client = redis.createClient({ + path: '/tmp/redis.sock', + parser: parser, + connect_timeout: 1000 + }); + + var end = helper.callFuncAfter(done, 2); + + client.once('ready', function() { + end(); + }); + + client.set('foo', 'bar', end); + }); + it("connects correctly with args", function (done) { client = redis.createClient.apply(redis.createClient, args); client.on("error", done); @@ -247,13 +268,7 @@ describe("connection tests", function () { it("works with missing options object for new redis instances", function () { // This is needed for libraries that have their own createClient function like fakeredis - var cnxOptions = { - port : 6379, - host : '127.0.0.1', - family : ip === 'IPv6' ? 6 : 4 - }; - var net_client = net.createConnection(cnxOptions); - client = new redis.RedisClient(net_client); + client = new redis.RedisClient({ on: function () {}}); }); it("throws on strange connection info", function () { diff --git a/test/helper.js b/test/helper.js index a6899aad7af..a66f67ac4e8 100644 --- a/test/helper.js +++ b/test/helper.js @@ -161,7 +161,7 @@ module.exports = { }, killConnection: function (client) { // Change the connection option to a non existing one and destroy the stream - client.connectionOption = { + client.connection_option = { port: 65535, host: '127.0.0.1', family: 4 From 88c9e28a2717fe03fa6a3f3470081d6d82fce950 Mon Sep 17 00:00:00 2001 From: Ruben Bridgewater Date: Thu, 29 Oct 2015 23:27:52 +0100 Subject: [PATCH 0461/1748] Fix test --- test/commands/multi.spec.js | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/test/commands/multi.spec.js b/test/commands/multi.spec.js index b21d3da0d35..503f20389ab 100644 --- a/test/commands/multi.spec.js +++ b/test/commands/multi.spec.js @@ -84,7 +84,7 @@ describe("The 'multi' method", function () { multi1.exec(function (err, res) { assert(called); assert(!err); - assert.strictEqual(res, '456'); + assert.strictEqual(res[1], '456'); done(); }); }); From 62c75a66cd45aa6c7f1a691d1967a8135916265f Mon Sep 17 00:00:00 2001 From: greenkeeperio-bot Date: Thu, 29 Oct 2015 23:34:19 +0100 Subject: [PATCH 0462/1748] chore(package): update dependencies http://greenkeeper.io/ --- package.json | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/package.json b/package.json index b5cbe13d525..21104432f38 100644 --- a/package.json +++ b/package.json @@ -35,7 +35,7 @@ "tcp-port-used": "^0.1.2", "uuid": "^2.0.1", "win-spawn": "^2.0.0", - "bluebird": "^2.10.0" + "bluebird": "^3.0.2" }, "repository": { "type": "git", From b3407ff8c7ed60b66202b039d5be5fe0cfd13e9d Mon Sep 17 00:00:00 2001 From: ivanB1975 Date: Thu, 29 Oct 2015 16:32:14 +0100 Subject: [PATCH 0463/1748] ignore if info command is not available on server --- index.js | 7 +++++++ 1 file changed, 7 insertions(+) diff --git a/index.js b/index.js index 745efebc3e1..a86642cd834 100644 --- a/index.js +++ b/index.js @@ -380,9 +380,16 @@ RedisClient.prototype.on_ready = function () { RedisClient.prototype.on_info_cmd = function (err, res) { if (err) { + /* ignore if the command info is not existing */ + if (err.message === "ERR unknown command 'info'"){ + this.server_info = {}; + this.on_ready(); + return; + } else { err.message = 'Ready check failed: ' + err.message; this.emit('error', err); return; + } } /* istanbul ignore if: some servers might not respond with any info data. This is just a safety check that is difficult to test */ From 1293046bab7de7405c9ae224405235f4a0cda8aa Mon Sep 17 00:00:00 2001 From: ivanB1975 Date: Thu, 29 Oct 2015 17:03:58 +0100 Subject: [PATCH 0464/1748] changed style --- index.js | 16 ++++++++-------- 1 file changed, 8 insertions(+), 8 deletions(-) diff --git a/index.js b/index.js index a86642cd834..5bbccf0bf9c 100644 --- a/index.js +++ b/index.js @@ -380,15 +380,15 @@ RedisClient.prototype.on_ready = function () { RedisClient.prototype.on_info_cmd = function (err, res) { if (err) { - /* ignore if the command info is not existing */ - if (err.message === "ERR unknown command 'info'"){ - this.server_info = {}; - this.on_ready(); - return; + /* istanbul ignore if: the command info is not existing on some servers for security reasons */ + if (err.message === "ERR unknown command 'info'") { + this.server_info = {}; + this.on_ready(); + return; } else { - err.message = 'Ready check failed: ' + err.message; - this.emit('error', err); - return; + err.message = 'Ready check failed: ' + err.message; + this.emit('error', err); + return; } } From b7a0f6f905de3b7103d2836a9afb48d31dc839c7 Mon Sep 17 00:00:00 2001 From: Ruben Bridgewater Date: Thu, 29 Oct 2015 23:49:10 +0100 Subject: [PATCH 0465/1748] Add test. Closes #909 --- changelog.md | 1 + test/connection.spec.js | 12 ++++++++++++ 2 files changed, 13 insertions(+) diff --git a/changelog.md b/changelog.md index 42f9ef7ee9a..9ae0be8dfba 100644 --- a/changelog.md +++ b/changelog.md @@ -15,6 +15,7 @@ Features - The `drain` event is from now on only emitted if the stream really had to buffer - Reduced the default connect_timeout to be one hour instead of 24h ([@BridgeAR](https://github.com/BridgeAR)) - Added .path to redis.createClient(options); ([@BridgeAR](https://github.com/BridgeAR)) +- Ignore info command, if not available on server ([@ivanB1975](https://github.com/ivanB1975)) Bugfixes diff --git a/test/connection.spec.js b/test/connection.spec.js index c3b17fae431..f5bcacdeffa 100644 --- a/test/connection.spec.js +++ b/test/connection.spec.js @@ -266,6 +266,18 @@ describe("connection tests", function () { }); }); + it("connects correctly even if the info command is not present on the redis server", function (done) { + client = redis.createClient.apply(redis.createClient, args); + client.info = function (cb) { + // Mock the result + cb(new Error("ERR unknown command 'info'")); + }; + client.once("ready", function () { + assert.strictEqual(Object.keys(client.server_info).length, 0); + done(); + }); + }); + it("works with missing options object for new redis instances", function () { // This is needed for libraries that have their own createClient function like fakeredis client = new redis.RedisClient({ on: function () {}}); From f45a89887105cc8d02bc1c0b7669f9f36d1bc6c0 Mon Sep 17 00:00:00 2001 From: Ruben Bridgewater Date: Thu, 29 Oct 2015 23:51:02 +0100 Subject: [PATCH 0466/1748] Remove istanbul ignore statement --- index.js | 1 - 1 file changed, 1 deletion(-) diff --git a/index.js b/index.js index 5bbccf0bf9c..054d074093c 100644 --- a/index.js +++ b/index.js @@ -380,7 +380,6 @@ RedisClient.prototype.on_ready = function () { RedisClient.prototype.on_info_cmd = function (err, res) { if (err) { - /* istanbul ignore if: the command info is not existing on some servers for security reasons */ if (err.message === "ERR unknown command 'info'") { this.server_info = {}; this.on_ready(); From dd563c2cb91c6b21475ea8113270a317739d80b0 Mon Sep 17 00:00:00 2001 From: Ruben Bridgewater Date: Fri, 30 Oct 2015 00:09:16 +0100 Subject: [PATCH 0467/1748] Fix unix only test not to run on windows --- test/connection.spec.js | 28 +++++++++++++++------------- 1 file changed, 15 insertions(+), 13 deletions(-) diff --git a/test/connection.spec.js b/test/connection.spec.js index f5bcacdeffa..c6b8d3821bd 100644 --- a/test/connection.spec.js +++ b/test/connection.spec.js @@ -49,7 +49,7 @@ describe("connection tests", function () { }); it("emit an error after max retry timeout and do not try to reconnect afterwards", function (done) { - var connect_timeout = 500; // in ms + var connect_timeout = 600; // in ms client = redis.createClient({ parser: parser, connect_timeout: connect_timeout @@ -187,21 +187,23 @@ describe("connection tests", function () { }); }); - it("connect with path provided in the options object", function (done) { - client = redis.createClient({ - path: '/tmp/redis.sock', - parser: parser, - connect_timeout: 1000 - }); + if (process.platform !== 'win32') { + it("connect with path provided in the options object", function (done) { + client = redis.createClient({ + path: '/tmp/redis.sock', + parser: parser, + connect_timeout: 1000 + }); - var end = helper.callFuncAfter(done, 2); + var end = helper.callFuncAfter(done, 2); - client.once('ready', function() { - end(); - }); + client.once('ready', function() { + end(); + }); - client.set('foo', 'bar', end); - }); + client.set('foo', 'bar', end); + }); + } it("connects correctly with args", function (done) { client = redis.createClient.apply(redis.createClient, args); From cc79b2db69a9c2318fc9109be9416235673f07db Mon Sep 17 00:00:00 2001 From: Ruben Bridgewater Date: Fri, 30 Oct 2015 01:16:11 +0100 Subject: [PATCH 0468/1748] Deactivate test for windows --- test/connection.spec.js | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/test/connection.spec.js b/test/connection.spec.js index c6b8d3821bd..b3c3fbe983e 100644 --- a/test/connection.spec.js +++ b/test/connection.spec.js @@ -49,6 +49,9 @@ describe("connection tests", function () { }); it("emit an error after max retry timeout and do not try to reconnect afterwards", function (done) { + // TODO: Investigate why this test fails with windows. Reconnect is only triggered once + if (process.platform === 'win32') this.skip(); + var connect_timeout = 600; // in ms client = redis.createClient({ parser: parser, @@ -67,6 +70,7 @@ describe("connection tests", function () { client.on('error', function(err) { if (/Redis connection in broken state: connection timeout.*?exceeded./.test(err.message)) { setTimeout(function () { + assert.strictEqual(client.retry_totaltime, connect_timeout); assert.strictEqual(time, connect_timeout); done(); }, 500); From 2cc01f52b653070ddc7d2711d72e641b2ef12ce3 Mon Sep 17 00:00:00 2001 From: Ruben Bridgewater Date: Fri, 30 Oct 2015 01:33:51 +0100 Subject: [PATCH 0469/1748] Use higher time difference for slow machines in tests --- changelog.md | 2 +- test/connection.spec.js | 10 ++++++---- 2 files changed, 7 insertions(+), 5 deletions(-) diff --git a/changelog.md b/changelog.md index 9ae0be8dfba..946fa27555f 100644 --- a/changelog.md +++ b/changelog.md @@ -1,7 +1,7 @@ Changelog ========= -## v.2.3.0 - xx XXX, 2015 +## v.2.3.0 - 30 Oct, 2015 Features diff --git a/test/connection.spec.js b/test/connection.spec.js index b3c3fbe983e..8383e794540 100644 --- a/test/connection.spec.js +++ b/test/connection.spec.js @@ -351,8 +351,9 @@ describe("connection tests", function () { }; client.on("ready", function () { var rest = Date.now() - time; - // Be on the safe side and accept 100ms above the original value - assert(rest - 100 < 500 && rest >= 500); + assert(rest >= 500); + // Be on the safe side and accept 200ms above the original value + assert(rest - 200 < 500); assert(delayed); end(); }); @@ -380,8 +381,9 @@ describe("connection tests", function () { }; client.on("ready", function () { var rest = Date.now() - time; - // Be on the safe side and accept 100ms above the original value - assert(rest - 100 < 1000 && rest >= 1000); + assert(rest >= 1000); + // Be on the safe side and accept 200ms above the original value + assert(rest - 200 < 1000); assert(delayed); end(); }); From cc93e0b3a7df4fef6205a8e09a10ad4993edbe9e Mon Sep 17 00:00:00 2001 From: Anton Nesterov Date: Fri, 30 Oct 2015 16:08:28 +0300 Subject: [PATCH 0470/1748] Fix return_buffers in pub/sub mode (#911) --- index.js | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/index.js b/index.js index 054d074093c..ebb5cba87a0 100644 --- a/index.js +++ b/index.js @@ -645,7 +645,7 @@ RedisClient.prototype.return_reply = function (reply) { } } else if (this.pub_sub_mode || command_obj && command_obj.sub_command) { if (Array.isArray(reply)) { - if (!command_obj || command_obj.buffer_args === false) { + if ((!command_obj || command_obj.buffer_args === false) && !this.options.return_buffers) { reply = utils.reply_to_strings(reply); } type = reply[0].toString(); From 3e64ce7bc3385ebc8fda459109ae548a85d59341 Mon Sep 17 00:00:00 2001 From: Anton Nesterov Date: Fri, 30 Oct 2015 16:09:17 +0300 Subject: [PATCH 0471/1748] Add regression test for return_buffers in pub/sub mode (#911) --- test/return_buffers.spec.js | 56 +++++++++++++++++++++++++++++++++++-- 1 file changed, 54 insertions(+), 2 deletions(-) diff --git a/test/return_buffers.spec.js b/test/return_buffers.spec.js index 24dc6f30d2f..da36a7a2a29 100644 --- a/test/return_buffers.spec.js +++ b/test/return_buffers.spec.js @@ -7,7 +7,7 @@ var redis = config.redis; describe("return_buffers", function () { - helper.allTests(function(parser, ip) { + helper.allTests(function(parser, ip, basicArgs) { describe("using " + parser + " and " + ip, function () { var client; @@ -229,6 +229,58 @@ describe("return_buffers", function () { }); }); }); + + describe('publish/subscribe', function (done) { + var pub; + var sub; + var channel = "test channel"; + var message = new Buffer("test message"); + + var args = config.configureClient(parser, ip, { + return_buffers: true + }); + + beforeEach(function (done) { + var pubConnected; + var subConnected; + + pub = redis.createClient.apply(redis.createClient, basicArgs); + sub = redis.createClient.apply(redis.createClient, args); + pub.once("connect", function () { + pub.flushdb(function () { + pubConnected = true; + if (subConnected) { + done(); + } + }); + }); + sub.once("connect", function () { + subConnected = true; + if (pubConnected) { + done(); + } + }); + }); + + it('receives buffer messages', function (done) { + sub.on("subscribe", function (chnl, count) { + pub.publish(channel, message); + }); + + sub.on("message", function (chnl, msg) { + assert.strictEqual(true, Buffer.isBuffer(msg)); + assert.strictEqual("", msg.inspect()); + return done(); + }); + + sub.subscribe(channel); + }); + + afterEach(function () { + sub.end(); + pub.end(); + }); + }); }); }); -}); \ No newline at end of file +}); From 42e979b1af844abb815469dceff6b37e05e3e210 Mon Sep 17 00:00:00 2001 From: Ruben Bridgewater Date: Fri, 30 Oct 2015 14:56:30 +0100 Subject: [PATCH 0472/1748] Add changelog entry and add a note in the readme that detect_buffers does not work in pub sub mode --- README.md | 2 +- changelog.md | 1 + index.js | 2 +- 3 files changed, 3 insertions(+), 2 deletions(-) diff --git a/README.md b/README.md index 214e64967df..f818dc6d7a9 100644 --- a/README.md +++ b/README.md @@ -182,7 +182,7 @@ port and host are probably fine and you don't need to supply any arguments. `cre * `path`: *null*; The unix socket string to connect to * `parser`: *hiredis*; Which Redis protocol reply parser to use. If `hiredis` is not installed it will fallback to `javascript`. * `return_buffers`: *false*; If set to `true`, then all replies will be sent to callbacks as Buffers instead of Strings. -* `detect_buffers`: *false*; If set to `true`, then replies will be sent to callbacks as Buffers +* `detect_buffers`: *false*; If set to `true`, then replies will be sent to callbacks as Buffers. Please be aware that this can't work properly with the pubsub mode. A subscriber has to either always return strings or buffers. if any of the input arguments to the original command were Buffers. This option lets you switch between Buffers and Strings on a per-command basis, whereas `return_buffers` applies to every command on a client. diff --git a/changelog.md b/changelog.md index 946fa27555f..3121a92763f 100644 --- a/changelog.md +++ b/changelog.md @@ -23,6 +23,7 @@ Bugfixes - Fixed .multi / .batch used with Node.js 0.10.x not working properly after a reconnect ([@BridgeAR](https://github.com/BridgeAR)) - Fixed fired but not yet returned commands not being rejected after a connection loss ([@BridgeAR](https://github.com/BridgeAR)) - Fixed connect_timeout not respected if no connection has ever been established ([@gagle](https://github.com/gagle) & [@benjie](https://github.com/benjie)) +- Fixed return_buffers in pub sub mode ([@komachi](https://github.com/komachi)) ## v.2.2.5 - 18 Oct, 2015 diff --git a/index.js b/index.js index ebb5cba87a0..cf4f4fb8e9a 100644 --- a/index.js +++ b/index.js @@ -653,7 +653,7 @@ RedisClient.prototype.return_reply = function (reply) { if (type === 'message') { this.emit('message', reply[1], reply[2]); // channel, message } else if (type === 'pmessage') { - this.emit('pmessage', reply[1], reply[2], reply[3]); // pattern, channel, message + this.emit('pmessage', reply[1].toString(), reply[2], reply[3]); // pattern, channel, message } else if (type === 'subscribe' || type === 'unsubscribe' || type === 'psubscribe' || type === 'punsubscribe') { if (reply[2] === 0) { this.pub_sub_mode = false; From b4e8e79653cfce6cff3c544f04f060c3383bc22b Mon Sep 17 00:00:00 2001 From: Ruben Bridgewater Date: Fri, 30 Oct 2015 15:01:06 +0100 Subject: [PATCH 0473/1748] v.2.3.0 --- README.md | 2 +- package.json | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/README.md b/README.md index f818dc6d7a9..279bb0f97d8 100644 --- a/README.md +++ b/README.md @@ -3,7 +3,7 @@ redis - a node.js redis client [![Build Status](https://travis-ci.org/NodeRedis/node_redis.png)](https://travis-ci.org/NodeRedis/node_redis) [![Coverage Status](https://coveralls.io/repos/NodeRedis/node_redis/badge.svg?branch=)](https://coveralls.io/r/NodeRedis/node_redis?branch=) -[![Windows Tests](https://ci.appveyor.com/api/projects/status/koc3xraik0xq3b56/branch/master?svg=true&label=Windows%20Tests)](https://ci.appveyor.com/project/BridgeAR/node-redis/branch/master) +[![Windows Tests](https://img.shields.io/appveyor/ci/BridgeAR/node-redis/master.svg?label=Windows%20Tests)](https://ci.appveyor.com/project/BridgeAR/node-redis/branch/master) [![Gitter](https://badges.gitter.im/Join%20Chat.svg)](https://gitter.im/NodeRedis/node_redis?utm_source=badge&utm_medium=badge&utm_campaign=pr-badge) This is a complete and feature rich Redis client for node.js. It supports all Redis commands and focuses on performance. diff --git a/package.json b/package.json index 21104432f38..d5e36db134e 100644 --- a/package.json +++ b/package.json @@ -1,6 +1,6 @@ { "name": "redis", - "version": "2.2.5", + "version": "2.3.0", "description": "Redis client library", "keywords": [ "database", From ef238143e9a7a706f1fedbece5322c004654325f Mon Sep 17 00:00:00 2001 From: Ruben Bridgewater Date: Fri, 6 Nov 2015 20:13:12 +0100 Subject: [PATCH 0474/1748] Fix typo in string conversion. Closes #918 --- index.js | 5 ++--- 1 file changed, 2 insertions(+), 3 deletions(-) diff --git a/index.js b/index.js index cf4f4fb8e9a..58e42c50365 100644 --- a/index.js +++ b/index.js @@ -732,7 +732,7 @@ RedisClient.prototype.send_command = function (command, args, callback) { if (Buffer.isBuffer(args[i])) { buffer_args = true; } else if (typeof args[i] !== 'string') { - arg = String(arg); + args[i] = String(args[i]); // 30000 seemed to be a good value to switch to buffers after testing this with and checking the pros and cons } else if (args[i].length > 30000) { big_data = true; @@ -786,7 +786,7 @@ RedisClient.prototype.send_command = function (command, args, callback) { if (!buffer_args && !big_data) { // Build up a string and send entire command in one write for (i = 0; i < args.length; i += 1) { - arg = String(args[i]); + arg = args[i]; command_str += '$' + Buffer.byteLength(arg) + '\r\n' + arg + '\r\n'; } debug('Send ' + this.address + ' id ' + this.connection_id + ': ' + command_str); @@ -798,7 +798,6 @@ RedisClient.prototype.send_command = function (command, args, callback) { for (i = 0; i < args.length; i += 1) { arg = args[i]; if (!Buffer.isBuffer(arg)) { - arg = String(arg); this.write('$' + Buffer.byteLength(arg) + '\r\n' + arg + '\r\n'); } else { this.write('$' + arg.length + '\r\n'); From c08461f781cbfc1ce5b55b409082c6574ee76149 Mon Sep 17 00:00:00 2001 From: Ruben Bridgewater Date: Fri, 6 Nov 2015 20:25:46 +0100 Subject: [PATCH 0475/1748] Fix typo in debug message --- index.js | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/index.js b/index.js index 58e42c50365..919587acf9c 100644 --- a/index.js +++ b/index.js @@ -112,8 +112,8 @@ RedisClient.prototype.install_stream_listeners = function () { }); this.stream.on('data', function (buffer_from_socket) { - // The data.toString() has a significant impact on big chunks and therefor this should only be used if necessary - debug('Net read ' + this.address + ' id ' + this.connection_id); // + ': ' + data.toString()); + // The buffer_from_socket.toString() has a significant impact on big chunks and therefor this should only be used if necessary + debug('Net read ' + self.address + ' id ' + self.connection_id); // + ': ' + buffer_from_socket.toString()); self.reply_parser.execute(buffer_from_socket); }); From ff857f97aec48c74d16b13b4a07d6f00256c5b54 Mon Sep 17 00:00:00 2001 From: Ruben Bridgewater Date: Sun, 8 Nov 2015 23:05:15 +0100 Subject: [PATCH 0476/1748] Intermediate fix js parser handling big values very slow Closes #900 --- changelog.md | 7 +++++ index.js | 2 +- lib/parsers/javascript.js | 63 ++++++++++++++++++++----------------- test/return_buffers.spec.js | 2 +- 4 files changed, 44 insertions(+), 30 deletions(-) diff --git a/changelog.md b/changelog.md index 3121a92763f..df5e85f0033 100644 --- a/changelog.md +++ b/changelog.md @@ -1,6 +1,13 @@ Changelog ========= +## v.2.3.1 - xx Nov, 2015 + +Bugfixes + +- Fixed js parser handling big values very slow ([@BridgeAR](https://github.com/BridgeAR)) + - The speed is up to ~500% faster than before but still up to ~50% slower than the hiredis parser. + ## v.2.3.0 - 30 Oct, 2015 Features diff --git a/index.js b/index.js index 919587acf9c..f408e446842 100644 --- a/index.js +++ b/index.js @@ -733,7 +733,7 @@ RedisClient.prototype.send_command = function (command, args, callback) { buffer_args = true; } else if (typeof args[i] !== 'string') { args[i] = String(args[i]); - // 30000 seemed to be a good value to switch to buffers after testing this with and checking the pros and cons + // 30000 seemed to be a good value to switch to buffers after testing and checking the pros and cons } else if (args[i].length > 30000) { big_data = true; args[i] = new Buffer(args[i]); diff --git a/lib/parsers/javascript.js b/lib/parsers/javascript.js index 5d0208b46bc..e6f6ec07844 100644 --- a/lib/parsers/javascript.js +++ b/lib/parsers/javascript.js @@ -1,11 +1,12 @@ 'use strict'; -var util = require('util'); +var util = require('util'); function JavascriptReplyParser() { this.name = exports.name; - this._buffer = new Buffer(0); - this._offset = 0; + this._buffer = new Buffer(0); + this._offset = 0; + this._buffers = []; } function IncompleteReadBuffer(message) { @@ -66,7 +67,7 @@ JavascriptReplyParser.prototype._parseResult = function (type) { return null; } - if (packetHeader > this._bytesRemaining()) { + if (packetHeader > this._buffer.length - this._offset) { this._offset = offset - 1; throw new IncompleteReadBuffer('Wait for more data.'); } @@ -93,14 +94,37 @@ JavascriptReplyParser.prototype._parseResult = function (type) { }; JavascriptReplyParser.prototype.execute = function (buffer) { - this.append(buffer); + var i = buffer.length - 1; + + while (buffer[i] !== 0x0a) { + i--; + if (i < 1) { + this._buffers.push(buffer); + return; + } + } + + if (this._buffers.length !== 0) { + this._buffers.unshift(this._offset === 0 ? this._buffer : this._buffer.slice(this._offset)); + this._buffers.push(buffer); + this._buffer = Buffer.concat(this._buffers); + this._buffers = []; + } else if (this._offset >= this._buffer.length) { + this._buffer = buffer; + } else { + this._buffer = Buffer.concat([this._buffer.slice(this._offset), buffer]); + } + this._offset = 0; + this.run(); +}; - var type, offset; +JavascriptReplyParser.prototype.run = function (buffer) { + var type, offset = this._offset; while (true) { offset = this._offset; // at least 4 bytes: :1\r\n - if (this._bytesRemaining() < 4) { + if (this._buffer.length - this._offset < 4) { break; } @@ -109,7 +133,7 @@ JavascriptReplyParser.prototype.execute = function (buffer) { if (type === 43 || type === 58 || type === 36) { // Strings + // Integers : // Bulk strings $ this.send_reply(this._parseResult(type)); - } else if (type === 45) { // Errors - + } else if (type === 45) { // Errors - this.send_error(this._parseResult(type)); } else if (type === 42) { // Arrays * // set a rewind point. if a failure occurs, @@ -130,24 +154,11 @@ JavascriptReplyParser.prototype.execute = function (buffer) { } }; -JavascriptReplyParser.prototype.append = function (newBuffer) { - - // out of data - if (this._offset >= this._buffer.length) { - this._buffer = newBuffer; - this._offset = 0; - return; - } - - this._buffer = Buffer.concat([this._buffer.slice(this._offset), newBuffer]); - this._offset = 0; -}; - JavascriptReplyParser.prototype.parseHeader = function () { - var end = this._packetEndOffset() + 1, - value = this._buffer.toString('ascii', this._offset, end - 1) | 0; + var end = this._packetEndOffset(), + value = this._buffer.toString('ascii', this._offset, end) | 0; - this._offset = end + 1; + this._offset = end + 2; return value; }; @@ -167,9 +178,5 @@ JavascriptReplyParser.prototype._packetEndOffset = function () { return offset; }; -JavascriptReplyParser.prototype._bytesRemaining = function () { - return (this._buffer.length - this._offset) < 0 ? 0 : (this._buffer.length - this._offset); -}; - exports.Parser = JavascriptReplyParser; exports.name = 'javascript'; diff --git a/test/return_buffers.spec.js b/test/return_buffers.spec.js index da36a7a2a29..a89ecbe34d8 100644 --- a/test/return_buffers.spec.js +++ b/test/return_buffers.spec.js @@ -261,7 +261,7 @@ describe("return_buffers", function () { } }); }); - + it('receives buffer messages', function (done) { sub.on("subscribe", function (chnl, count) { pub.publish(channel, message); From 19ce6680ef14a9a498b056d502280f8a705a810b Mon Sep 17 00:00:00 2001 From: Ruben Bridgewater Date: Tue, 17 Nov 2015 22:08:17 +0100 Subject: [PATCH 0477/1748] Prevent jshint race condition --- test/commands/sort.spec.js | 54 +++++++++++++++++++------------------- 1 file changed, 27 insertions(+), 27 deletions(-) diff --git a/test/commands/sort.spec.js b/test/commands/sort.spec.js index 5d8e5aff8a6..97e64a686b0 100644 --- a/test/commands/sort.spec.js +++ b/test/commands/sort.spec.js @@ -5,6 +5,33 @@ var config = require("../lib/config"); var helper = require("../helper"); var redis = config.redis; +function setupData(client, done) { + client.rpush('y', 'd'); + client.rpush('y', 'b'); + client.rpush('y', 'a'); + client.rpush('y', 'c'); + + client.rpush('x', '3'); + client.rpush('x', '9'); + client.rpush('x', '2'); + client.rpush('x', '4'); + + client.set('w3', '4'); + client.set('w9', '5'); + client.set('w2', '12'); + client.set('w4', '6'); + + client.set('o2', 'buz'); + client.set('o3', 'foo'); + client.set('o4', 'baz'); + client.set('o9', 'bar'); + + client.set('p2', 'qux'); + client.set('p3', 'bux'); + client.set('p4', 'lux'); + client.set('p9', 'tux', done); +} + describe("The 'sort' method", function () { helper.allTests(function(parser, ip, args) { @@ -100,31 +127,4 @@ describe("The 'sort' method", function () { }); }); - function setupData(client, done) { - client.rpush('y', 'd'); - client.rpush('y', 'b'); - client.rpush('y', 'a'); - client.rpush('y', 'c'); - - client.rpush('x', '3'); - client.rpush('x', '9'); - client.rpush('x', '2'); - client.rpush('x', '4'); - - client.set('w3', '4'); - client.set('w9', '5'); - client.set('w2', '12'); - client.set('w4', '6'); - - client.set('o2', 'buz'); - client.set('o3', 'foo'); - client.set('o4', 'baz'); - client.set('o9', 'bar'); - - client.set('p2', 'qux'); - client.set('p3', 'bux'); - client.set('p4', 'lux'); - client.set('p9', 'tux', done); - } - }); From 241e156499a90c0ea64edc505d37fae918a704bd Mon Sep 17 00:00:00 2001 From: Ruben Bridgewater Date: Wed, 18 Nov 2015 01:45:03 +0100 Subject: [PATCH 0478/1748] Fix saving buffers with charsets other than utf-8 while using multi This will also improve pipelinening for buffers and fixes the return value of Batch.exec Fixes #913 --- changelog.md | 1 + index.js | 43 ++++++++++++---- test/detect_buffers.spec.js | 4 ++ test/helper.js | 2 + test/{commands => }/multi.spec.js | 85 +++++++++++++++++++++++-------- 5 files changed, 105 insertions(+), 30 deletions(-) rename test/{commands => }/multi.spec.js (90%) diff --git a/changelog.md b/changelog.md index df5e85f0033..01a6ee80e1b 100644 --- a/changelog.md +++ b/changelog.md @@ -5,6 +5,7 @@ Changelog Bugfixes +- Fixed saving buffers with charsets other than utf-8 while using multi ([@BridgeAR](https://github.com/BridgeAR)) - Fixed js parser handling big values very slow ([@BridgeAR](https://github.com/BridgeAR)) - The speed is up to ~500% faster than before but still up to ~50% slower than the hiredis parser. diff --git a/index.js b/index.js index f408e446842..d89ea4f6fd4 100644 --- a/index.js +++ b/index.js @@ -731,12 +731,20 @@ RedisClient.prototype.send_command = function (command, args, callback) { for (i = 0; i < args.length; i += 1) { if (Buffer.isBuffer(args[i])) { buffer_args = true; + if (this.pipeline !== 0) { + this.pipeline += 2; + this.writeDefault = this.writeBuffers; + } } else if (typeof args[i] !== 'string') { args[i] = String(args[i]); // 30000 seemed to be a good value to switch to buffers after testing and checking the pros and cons } else if (args[i].length > 30000) { big_data = true; args[i] = new Buffer(args[i]); + if (this.pipeline !== 0) { + this.pipeline += 2; + this.writeDefault = this.writeBuffers; + } } } if (this.options.detect_buffers) { @@ -764,7 +772,7 @@ RedisClient.prototype.send_command = function (command, args, callback) { this.should_buffer = true; } // Return false to signal buffering - return false; + return !this.should_buffer; } if (command === 'subscribe' || command === 'psubscribe' || command === 'unsubscribe' || command === 'punsubscribe') { @@ -797,7 +805,7 @@ RedisClient.prototype.send_command = function (command, args, callback) { for (i = 0; i < args.length; i += 1) { arg = args[i]; - if (!Buffer.isBuffer(arg)) { + if (typeof arg === 'string') { this.write('$' + Buffer.byteLength(arg) + '\r\n' + arg + '\r\n'); } else { this.write('$' + arg.length + '\r\n'); @@ -810,6 +818,22 @@ RedisClient.prototype.send_command = function (command, args, callback) { return !this.should_buffer; }; +RedisClient.prototype.writeDefault = RedisClient.prototype.writeStrings = function (data) { + var command, str = ''; + while (command = this.pipeline_queue.shift()) { + str += command; + } + this.should_buffer = !this.stream.write(str + data); +}; + +RedisClient.prototype.writeBuffers = function (data) { + var command; + while (command = this.pipeline_queue.shift()) { + this.stream.write(command); + } + this.should_buffer = !this.stream.write(data); +}; + RedisClient.prototype.write = function (data) { if (this.pipeline === 0) { this.should_buffer = !this.stream.write(data); @@ -818,11 +842,7 @@ RedisClient.prototype.write = function (data) { this.pipeline--; if (this.pipeline === 0) { - var command, str = ''; - while (command = this.pipeline_queue.shift()) { - str += command; - } - this.should_buffer = !this.stream.write(str + data); + this.writeDefault(data); return; } @@ -1121,10 +1141,12 @@ Multi.prototype.exec_transaction = function (callback) { this.send_command(command, args, index, cb); } - this._client.uncork(); - return this._client.send_command('exec', [], function(err, replies) { + this._client.send_command('exec', [], function(err, replies) { self.execute_callback(err, replies); }); + this._client.uncork(); + this._client.writeDefault = this._client.writeStrings; + return !this._client.should_buffer; }; Multi.prototype.execute_callback = function (err, replies) { @@ -1231,7 +1253,8 @@ Multi.prototype.exec = Multi.prototype.EXEC = Multi.prototype.exec_batch = funct index++; } this._client.uncork(); - return this._client.should_buffer; + this._client.writeDefault = this._client.writeStrings; + return !this._client.should_buffer; }; var createClient = function (port_arg, host_arg, options) { diff --git a/test/detect_buffers.spec.js b/test/detect_buffers.spec.js index 747253918c5..b1decc3aabe 100644 --- a/test/detect_buffers.spec.js +++ b/test/detect_buffers.spec.js @@ -27,6 +27,10 @@ describe("detect_buffers", function () { }); }); + afterEach(function () { + client.end(); + }); + describe('get', function () { describe('first argument is a string', function () { it('returns a string', function (done) { diff --git a/test/helper.js b/test/helper.js index a66f67ac4e8..a5a105137d7 100644 --- a/test/helper.js +++ b/test/helper.js @@ -156,7 +156,9 @@ module.exports = { i++; if (i === max) { func(); + return true; } + return false; }; }, killConnection: function (client) { diff --git a/test/commands/multi.spec.js b/test/multi.spec.js similarity index 90% rename from test/commands/multi.spec.js rename to test/multi.spec.js index 503f20389ab..b1cb36ca85b 100644 --- a/test/commands/multi.spec.js +++ b/test/multi.spec.js @@ -1,13 +1,74 @@ 'use strict'; var assert = require('assert'); -var config = require("../lib/config"); -var helper = require('../helper'); +var config = require("./lib/config"); +var helper = require('./helper'); var redis = config.redis; +var zlib = require('zlib'); var uuid = require('uuid'); +var client; describe("The 'multi' method", function () { + afterEach(function () { + client.end(); + }); + + describe('regression test', function () { + it('saved buffers with charsets different than utf-8 (issue #913)', function (done) { + client = redis.createClient(); + + var end = helper.callFuncAfter(done, 100); + + // Some random object created from http://beta.json-generator.com/ + var test_obj = { + "_id": "5642c4c33d4667c4a1fefd99","index": 0, "guid": "5baf1f1c-7621-41e7-ae7a-f8c6f3199b0f", "isActive": true, + "balance": "$1,028.63", "picture": "http://placehold.it/32x32", "age": 31, "eyeColor": "green", "name": {"first": "Shana", "last": "Long"}, + "company": "MANGLO", "email": "shana.long@manglo.us", "phone": "+1 (926) 405-3105", "address": "747 Dank Court, Norfolk, Ohio, 1112", + "about": "Eu pariatur in nisi occaecat enim qui consequat nostrud cupidatat id. " + + "Commodo commodo dolore esse irure minim quis deserunt anim laborum aute deserunt et est. Quis nisi laborum deserunt nisi quis.", + "registered": "Friday, April 18, 2014 9:56 AM", "latitude": "74.566613", "longitude": "-11.660432", "tags": [7, "excepteur"], + "range": [0, 1, 2, 3, 4, 5, 6, 7, 8, 9], "friends": [3, {"id": 1, "name": "Schultz Dyer"}], + "greeting": "Hello, Shana! You have 5 unread messages.", "favoriteFruit": "strawberry" + }; + + function run () { + if (end() === true) { + return; + } + // To demonstrate a big payload for hash set field values, let's create a big array + var test_arr = []; + for (var i = 0; i < 80; i++) { + var new_obj = JSON.parse(JSON.stringify(test_obj)); + test_arr.push(new_obj); + } + + var json = JSON.stringify(test_arr); + zlib.deflate(new Buffer(json), function (err, buffer) { + if (err) { + done(err); + return; + } + + var multi = client.multi(); + multi.del('SOME_KEY'); + + for (i = 0; i < 100; i++) { + multi.hset('SOME_KEY', 'SOME_FIELD' + i, buffer); + } + multi.exec(function (err, res) { + if (err) { + done(err); + return; + } + run(); + }); + }); + } + run(); + }); + }); + helper.allTests(function(parser, ip, args) { describe("using " + parser + " and " + ip, function () { @@ -19,14 +80,13 @@ describe("The 'multi' method", function () { }); describe("when not connected", function () { - var client; beforeEach(function (done) { client = redis.createClient.apply(redis.createClient, args); client.once("ready", function () { client.quit(); }); - client.on('end', function () { + client.once('end', function () { return done(); }); }); @@ -37,7 +97,7 @@ describe("The 'multi' method", function () { assert(err.message.match(/The connection has already been closed/)); done(); }); - assert.strictEqual(notBuffering, false); + assert.strictEqual(notBuffering, true); }); it("reports an error if promisified", function () { @@ -48,17 +108,12 @@ describe("The 'multi' method", function () { }); describe("when connected", function () { - var client; beforeEach(function (done) { client = redis.createClient.apply(redis.createClient, args); client.once("connect", done); }); - afterEach(function () { - client.end(); - }); - it("executes a pipelined multi properly in combination with the offline queue", function (done) { var multi1 = client.multi(); multi1.set("m1", "123"); @@ -93,11 +148,6 @@ describe("The 'multi' method", function () { }); describe("when connection is broken", function () { - var client; - - afterEach(function () { - client.end(); - }); it("return an error even if connection is in broken mode if callback is present", function (done) { client = redis.createClient({ @@ -137,7 +187,6 @@ describe("The 'multi' method", function () { }); describe("when ready", function () { - var client; beforeEach(function (done) { client = redis.createClient.apply(redis.createClient, args); @@ -148,10 +197,6 @@ describe("The 'multi' method", function () { }); }); - afterEach(function () { - client.end(); - }); - it("returns an empty result array", function (done) { var multi = client.multi(); var notBuffering = multi.exec(function (err, res) { From 4c43d56b331dd0d0c3bad47dc7e8560d86db91f7 Mon Sep 17 00:00:00 2001 From: Ruben Bridgewater Date: Wed, 18 Nov 2015 13:58:37 +0100 Subject: [PATCH 0479/1748] v.2.3.1 --- package.json | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/package.json b/package.json index d5e36db134e..91ba58dd7bb 100644 --- a/package.json +++ b/package.json @@ -1,6 +1,6 @@ { "name": "redis", - "version": "2.3.0", + "version": "2.3.1", "description": "Redis client library", "keywords": [ "database", From 6cee5df6aac45e4c1313c1205ae6aaa81dd71eb1 Mon Sep 17 00:00:00 2001 From: Ruben Bridgewater Date: Wed, 18 Nov 2015 14:02:16 +0100 Subject: [PATCH 0480/1748] Add changelog release date --- changelog.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/changelog.md b/changelog.md index 01a6ee80e1b..704b17054a7 100644 --- a/changelog.md +++ b/changelog.md @@ -1,7 +1,7 @@ Changelog ========= -## v.2.3.1 - xx Nov, 2015 +## v.2.3.1 - 18 Nov, 2015 Bugfixes From 431bed882b0c4ec494ad59ef2af4bd9fc20cf005 Mon Sep 17 00:00:00 2001 From: Ruben Bridgewater Date: Thu, 19 Nov 2015 00:29:49 +0100 Subject: [PATCH 0481/1748] Increase test timeout for appveyor --- test/multi.spec.js | 1 + 1 file changed, 1 insertion(+) diff --git a/test/multi.spec.js b/test/multi.spec.js index b1cb36ca85b..25cbfa538b6 100644 --- a/test/multi.spec.js +++ b/test/multi.spec.js @@ -16,6 +16,7 @@ describe("The 'multi' method", function () { describe('regression test', function () { it('saved buffers with charsets different than utf-8 (issue #913)', function (done) { + this.timeout(12000); // Windows tests on 0.10 are slow client = redis.createClient(); var end = helper.callFuncAfter(done, 100); From 0913a4d52115295d83570702614773432f3324eb Mon Sep 17 00:00:00 2001 From: Ruben Bridgewater Date: Sun, 22 Nov 2015 16:54:41 +0100 Subject: [PATCH 0482/1748] Test Node.js v.5 instead of iojs 3 --- .travis.yml | 2 +- appveyor.yml | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/.travis.yml b/.travis.yml index 7d65abb80bc..79274f7a1a3 100644 --- a/.travis.yml +++ b/.travis.yml @@ -12,5 +12,5 @@ node_js: - "0.10" - "0.12" - "4.0" - - "iojs" + - "5.0" after_success: npm run coveralls diff --git a/appveyor.yml b/appveyor.yml index 51879a7964b..78bb63bac2b 100644 --- a/appveyor.yml +++ b/appveyor.yml @@ -5,8 +5,8 @@ environment: matrix: - nodejs_version: "0.10" - nodejs_version: "0.12" - - nodejs_version: "3" - nodejs_version: "4" + - nodejs_version: "5" pull_requests: do_not_increment_build_number: true From 8bf794fb36f97be1025de7efa93a63decd03524b Mon Sep 17 00:00:00 2001 From: Ruben Bridgewater Date: Sun, 22 Nov 2015 16:56:54 +0100 Subject: [PATCH 0483/1748] Stricten tests by always ending redis with .end(true) if possible --- README.md | 5 ++--- test/auth.spec.js | 4 +++- test/commands/blpop.spec.js | 4 ++-- test/commands/client.spec.js | 4 ++-- test/commands/dbsize.spec.js | 2 +- test/commands/del.spec.js | 2 +- test/commands/eval.spec.js | 2 +- test/commands/exits.spec.js | 2 +- test/commands/expire.spec.js | 2 +- test/commands/flushdb.spec.js | 2 +- test/commands/geoadd.spec.js | 2 +- test/commands/get.spec.js | 2 +- test/commands/getset.spec.js | 2 +- test/commands/hgetall.spec.js | 2 +- test/commands/hincrby.spec.js | 2 +- test/commands/hlen.spec.js | 2 +- test/commands/hmget.spec.js | 2 +- test/commands/hmset.spec.js | 2 +- test/commands/hset.spec.js | 2 +- test/commands/incr.spec.js | 4 ++-- test/commands/keys.spec.js | 2 +- test/commands/mget.spec.js | 2 +- test/commands/mset.spec.js | 2 +- test/commands/msetnx.spec.js | 2 +- test/commands/randomkey.test.js | 2 +- test/commands/rename.spec.js | 2 +- test/commands/renamenx.spec.js | 2 +- test/commands/rpush.spec.js | 2 +- test/commands/sadd.spec.js | 2 +- test/commands/scard.spec.js | 2 +- test/commands/script.spec.js | 2 +- test/commands/sdiff.spec.js | 2 +- test/commands/sdiffstore.spec.js | 2 +- test/commands/select.spec.js | 2 +- test/commands/set.spec.js | 2 +- test/commands/setex.spec.js | 2 +- test/commands/setnx.spec.js | 2 +- test/commands/sinter.spec.js | 2 +- test/commands/sinterstore.spec.js | 2 +- test/commands/sismember.spec.js | 2 +- test/commands/slowlog.spec.js | 2 +- test/commands/smembers.spec.js | 2 +- test/commands/smove.spec.js | 2 +- test/commands/sort.spec.js | 2 +- test/commands/spop.spec.js | 2 +- test/commands/srem.spec.js | 2 +- test/commands/sunion.spec.js | 2 +- test/commands/sunionstore.spec.js | 2 +- test/commands/sync.spec.js | 2 +- test/commands/ttl.spec.js | 2 +- test/commands/type.spec.js | 2 +- test/commands/watch.spec.js | 2 +- test/commands/zadd.spec.js | 2 +- test/commands/zscan.spec.js | 2 +- test/commands/zscore.spec.js | 2 +- test/connection.spec.js | 4 ++-- test/node_redis.spec.js | 35 ++++++++++++++++++------------- 57 files changed, 83 insertions(+), 77 deletions(-) diff --git a/README.md b/README.md index 279bb0f97d8..bd7c8fd480c 100644 --- a/README.md +++ b/README.md @@ -246,7 +246,7 @@ something like this `Error: Ready check failed: ERR operation not permitted`. Forcibly close the connection to the Redis server. Note that this does not wait until all replies have been parsed. If you want to exit cleanly, call `client.quit()` to send the `QUIT` command after you have handled all replies. -If flush is set to true, all commands will be rejected instead of ignored after using `.end`. +If flush is set to true, all still running commands will be rejected instead of ignored after using `.end`. This example closes the connection to the Redis server before the replies have been read. You probably don't want to do this: @@ -263,8 +263,7 @@ client.get("foo_rand000000000000", function (err, reply) { }); ``` -`client.end()` is useful for timeout cases where something is stuck or taking too long and you want -to start over. +`client.end()` without the flush parameter should not be used in production! ## client.unref() diff --git a/test/auth.spec.js b/test/auth.spec.js index 35481230ae4..1e79f023e13 100644 --- a/test/auth.spec.js +++ b/test/auth.spec.js @@ -24,7 +24,9 @@ describe("client authentication", function () { client = null; }); afterEach(function () { - client.end(); + // Explicitly ignore still running commands + // The ready command could still be running + client.end(false); }); it("allows auth to be provided with 'auth' method", function (done) { diff --git a/test/commands/blpop.spec.js b/test/commands/blpop.spec.js index e8042d6053e..fc022a91855 100644 --- a/test/commands/blpop.spec.js +++ b/test/commands/blpop.spec.js @@ -61,8 +61,8 @@ describe("The 'blpop' method", function () { }); afterEach(function () { - client.end(); - bclient.end(); + client.end(true); + bclient.end(true); }); }); }); diff --git a/test/commands/client.spec.js b/test/commands/client.spec.js index 9014af862ac..49497b9973f 100644 --- a/test/commands/client.spec.js +++ b/test/commands/client.spec.js @@ -28,8 +28,8 @@ describe("The 'client' method", function () { }); afterEach(function () { - client.end(); - client2.end(); + client.end(true); + client2.end(true); }); describe('list', function () { diff --git a/test/commands/dbsize.spec.js b/test/commands/dbsize.spec.js index 6e362087a5b..aa1fb57fcff 100644 --- a/test/commands/dbsize.spec.js +++ b/test/commands/dbsize.spec.js @@ -53,7 +53,7 @@ describe("The 'dbsize' method", function () { }); afterEach(function () { - client.end(); + client.end(true); }); it("returns a zero db size", function (done) { diff --git a/test/commands/del.spec.js b/test/commands/del.spec.js index 5502e91b6cf..d5dc8fbb64d 100644 --- a/test/commands/del.spec.js +++ b/test/commands/del.spec.js @@ -50,7 +50,7 @@ describe("The 'del' method", function () { }); afterEach(function () { - client.end(); + client.end(true); }); }); }); diff --git a/test/commands/eval.spec.js b/test/commands/eval.spec.js index bdb0d48061b..53cff94e71d 100644 --- a/test/commands/eval.spec.js +++ b/test/commands/eval.spec.js @@ -22,7 +22,7 @@ describe("The 'eval' method", function () { }); afterEach(function () { - client.end(); + client.end(true); }); it('converts a float to an integer when evaluated', function (done) { diff --git a/test/commands/exits.spec.js b/test/commands/exits.spec.js index 030e68c7f5c..8d5ac41a319 100644 --- a/test/commands/exits.spec.js +++ b/test/commands/exits.spec.js @@ -33,7 +33,7 @@ describe("The 'exits' method", function () { }); afterEach(function () { - client.end(); + client.end(true); }); }); }); diff --git a/test/commands/expire.spec.js b/test/commands/expire.spec.js index a77a32b2f21..99da8e007bb 100644 --- a/test/commands/expire.spec.js +++ b/test/commands/expire.spec.js @@ -35,7 +35,7 @@ describe("The 'expire' method", function () { }); afterEach(function () { - client.end(); + client.end(true); }); }); }); diff --git a/test/commands/flushdb.spec.js b/test/commands/flushdb.spec.js index f80e5475bee..e1c073c0f27 100644 --- a/test/commands/flushdb.spec.js +++ b/test/commands/flushdb.spec.js @@ -50,7 +50,7 @@ describe("The 'flushdb' method", function () { }); afterEach(function () { - client.end(); + client.end(true); }); describe("when there is data in Redis", function () { diff --git a/test/commands/geoadd.spec.js b/test/commands/geoadd.spec.js index 690df55de68..70ba278a6b3 100644 --- a/test/commands/geoadd.spec.js +++ b/test/commands/geoadd.spec.js @@ -28,7 +28,7 @@ describe("The 'geoadd' method", function () { }); afterEach(function () { - client.end(); + client.end(true); }); }); }); diff --git a/test/commands/get.spec.js b/test/commands/get.spec.js index cfefe463513..f51201b990b 100644 --- a/test/commands/get.spec.js +++ b/test/commands/get.spec.js @@ -56,7 +56,7 @@ describe("The 'get' method", function () { }); afterEach(function () { - client.end(); + client.end(true); }); describe("when the key exists in Redis", function () { diff --git a/test/commands/getset.spec.js b/test/commands/getset.spec.js index 289257573a0..11465bf4951 100644 --- a/test/commands/getset.spec.js +++ b/test/commands/getset.spec.js @@ -52,7 +52,7 @@ describe("The 'getset' method", function () { }); afterEach(function () { - client.end(); + client.end(true); }); describe("when the key exists in Redis", function () { diff --git a/test/commands/hgetall.spec.js b/test/commands/hgetall.spec.js index 2a734052b5f..4e219326f2b 100644 --- a/test/commands/hgetall.spec.js +++ b/test/commands/hgetall.spec.js @@ -77,7 +77,7 @@ describe("The 'hgetall' method", function () { }); afterEach(function () { - client.end(); + client.end(true); }); }); }); diff --git a/test/commands/hincrby.spec.js b/test/commands/hincrby.spec.js index cfa1f132972..d2d03c95467 100644 --- a/test/commands/hincrby.spec.js +++ b/test/commands/hincrby.spec.js @@ -33,7 +33,7 @@ describe("The 'hincrby' method", function () { }); afterEach(function () { - client.end(); + client.end(true); }); }); }); diff --git a/test/commands/hlen.spec.js b/test/commands/hlen.spec.js index d5d6b149928..e366582b917 100644 --- a/test/commands/hlen.spec.js +++ b/test/commands/hlen.spec.js @@ -31,7 +31,7 @@ describe("The 'hlen' method", function () { }); afterEach(function () { - client.end(); + client.end(true); }); }); }); diff --git a/test/commands/hmget.spec.js b/test/commands/hmget.spec.js index a6f566ec693..4e90dd40a01 100644 --- a/test/commands/hmget.spec.js +++ b/test/commands/hmget.spec.js @@ -64,7 +64,7 @@ describe("The 'hmget' method", function () { }); afterEach(function () { - client.end(); + client.end(true); }); }); }); diff --git a/test/commands/hmset.spec.js b/test/commands/hmset.spec.js index 1f8f15096db..2dbc62e7fc6 100644 --- a/test/commands/hmset.spec.js +++ b/test/commands/hmset.spec.js @@ -110,7 +110,7 @@ describe("The 'hmset' method", function () { }); afterEach(function () { - client.end(); + client.end(true); }); }); }); diff --git a/test/commands/hset.spec.js b/test/commands/hset.spec.js index 8ce8996d860..c08150382e9 100644 --- a/test/commands/hset.spec.js +++ b/test/commands/hset.spec.js @@ -56,7 +56,7 @@ describe("The 'hset' method", function () { }); afterEach(function () { - client.end(); + client.end(true); }); }); }); diff --git a/test/commands/incr.spec.js b/test/commands/incr.spec.js index 3803585ff48..2f7daae0d12 100644 --- a/test/commands/incr.spec.js +++ b/test/commands/incr.spec.js @@ -29,7 +29,7 @@ describe("The 'incr' method", function () { }); afterEach(function () { - client.end(); + client.end(true); }); it("reports an error", function (done) { @@ -64,7 +64,7 @@ describe("The 'incr' method", function () { }); after(function () { - client.end(); + client.end(true); }); it("changes the last digit from 2 to 3", function (done) { diff --git a/test/commands/keys.spec.js b/test/commands/keys.spec.js index 5cb84228058..2c977f664d5 100644 --- a/test/commands/keys.spec.js +++ b/test/commands/keys.spec.js @@ -63,7 +63,7 @@ describe("The 'keys' method", function () { }); afterEach(function () { - client.end(); + client.end(true); }); }); }); diff --git a/test/commands/mget.spec.js b/test/commands/mget.spec.js index 447fac04900..0907558e56b 100644 --- a/test/commands/mget.spec.js +++ b/test/commands/mget.spec.js @@ -63,7 +63,7 @@ describe("The 'mget' method", function () { }); afterEach(function () { - client.end(); + client.end(true); }); }); }); diff --git a/test/commands/mset.spec.js b/test/commands/mset.spec.js index 366f52c8899..c61c3d726c1 100644 --- a/test/commands/mset.spec.js +++ b/test/commands/mset.spec.js @@ -52,7 +52,7 @@ describe("The 'mset' method", function () { }); afterEach(function () { - client.end(); + client.end(true); }); describe("and a callback is specified", function () { diff --git a/test/commands/msetnx.spec.js b/test/commands/msetnx.spec.js index e89780ac756..e3f1d3afaad 100644 --- a/test/commands/msetnx.spec.js +++ b/test/commands/msetnx.spec.js @@ -31,7 +31,7 @@ describe("The 'msetnx' method", function () { }); afterEach(function () { - client.end(); + client.end(true); }); }); }); diff --git a/test/commands/randomkey.test.js b/test/commands/randomkey.test.js index 870726daef5..72fd2fda881 100644 --- a/test/commands/randomkey.test.js +++ b/test/commands/randomkey.test.js @@ -28,7 +28,7 @@ describe("The 'randomkey' method", function () { }); afterEach(function () { - client.end(); + client.end(true); }); }); }); diff --git a/test/commands/rename.spec.js b/test/commands/rename.spec.js index 65dad08e7f3..dc1de1e3afb 100644 --- a/test/commands/rename.spec.js +++ b/test/commands/rename.spec.js @@ -31,7 +31,7 @@ describe("The 'rename' method", function () { }); afterEach(function () { - client.end(); + client.end(true); }); }); }); diff --git a/test/commands/renamenx.spec.js b/test/commands/renamenx.spec.js index e5020155ba3..bd4e8232350 100644 --- a/test/commands/renamenx.spec.js +++ b/test/commands/renamenx.spec.js @@ -34,7 +34,7 @@ describe("The 'renamenx' method", function () { }); afterEach(function () { - client.end(); + client.end(true); }); }); }); diff --git a/test/commands/rpush.spec.js b/test/commands/rpush.spec.js index 2a0d72fd222..b6c1562b800 100644 --- a/test/commands/rpush.spec.js +++ b/test/commands/rpush.spec.js @@ -29,7 +29,7 @@ describe("The 'rpush' command", function () { }); afterEach(function () { - client.end(); + client.end(true); }); }); }); diff --git a/test/commands/sadd.spec.js b/test/commands/sadd.spec.js index 9aa246487dc..504dc47541e 100644 --- a/test/commands/sadd.spec.js +++ b/test/commands/sadd.spec.js @@ -55,7 +55,7 @@ describe("The 'sadd' method", function () { }); afterEach(function () { - client.end(); + client.end(true); }); }); }); diff --git a/test/commands/scard.spec.js b/test/commands/scard.spec.js index 52467823093..8c8eb70d1f1 100644 --- a/test/commands/scard.spec.js +++ b/test/commands/scard.spec.js @@ -24,7 +24,7 @@ describe("The 'scard' method", function () { }); afterEach(function () { - client.end(); + client.end(true); }); }); }); diff --git a/test/commands/script.spec.js b/test/commands/script.spec.js index c3e13407028..a82ab09d19f 100644 --- a/test/commands/script.spec.js +++ b/test/commands/script.spec.js @@ -23,7 +23,7 @@ describe("The 'script' method", function () { }); afterEach(function () { - client.end(); + client.end(true); }); it("loads script with client.script('load')", function (done) { diff --git a/test/commands/sdiff.spec.js b/test/commands/sdiff.spec.js index 435ddf63e95..7121b1e95f7 100644 --- a/test/commands/sdiff.spec.js +++ b/test/commands/sdiff.spec.js @@ -40,7 +40,7 @@ describe("The 'sdiff' method", function () { }); afterEach(function () { - client.end(); + client.end(true); }); }); }); diff --git a/test/commands/sdiffstore.spec.js b/test/commands/sdiffstore.spec.js index 00bc2c8e12c..1963317a708 100644 --- a/test/commands/sdiffstore.spec.js +++ b/test/commands/sdiffstore.spec.js @@ -40,7 +40,7 @@ describe("The 'sdiffstore' method", function () { }); afterEach(function () { - client.end(); + client.end(true); }); }); }); diff --git a/test/commands/select.spec.js b/test/commands/select.spec.js index 3a90703332b..bf41fc46a08 100644 --- a/test/commands/select.spec.js +++ b/test/commands/select.spec.js @@ -41,7 +41,7 @@ describe("The 'select' method", function () { }); afterEach(function () { - client.end(); + client.end(true); }); it("changes the database and calls the callback", function (done) { diff --git a/test/commands/set.spec.js b/test/commands/set.spec.js index e76b0c88db1..7a508920a1a 100644 --- a/test/commands/set.spec.js +++ b/test/commands/set.spec.js @@ -50,7 +50,7 @@ describe("The 'set' method", function () { }); afterEach(function () { - client.end(); + client.end(true); }); describe("and a callback is specified", function () { diff --git a/test/commands/setex.spec.js b/test/commands/setex.spec.js index 7d0b152ae0c..47f616253e9 100644 --- a/test/commands/setex.spec.js +++ b/test/commands/setex.spec.js @@ -34,7 +34,7 @@ describe("The 'setex' method", function () { }); afterEach(function () { - client.end(); + client.end(true); }); }); }); diff --git a/test/commands/setnx.spec.js b/test/commands/setnx.spec.js index 229eb2b58bf..d72c36696eb 100644 --- a/test/commands/setnx.spec.js +++ b/test/commands/setnx.spec.js @@ -30,7 +30,7 @@ describe("The 'setnx' method", function () { }); afterEach(function () { - client.end(); + client.end(true); }); }); }); diff --git a/test/commands/sinter.spec.js b/test/commands/sinter.spec.js index a655b860689..8e547923dbc 100644 --- a/test/commands/sinter.spec.js +++ b/test/commands/sinter.spec.js @@ -56,7 +56,7 @@ describe("The 'sinter' method", function () { }); afterEach(function () { - client.end(); + client.end(true); }); }); }); diff --git a/test/commands/sinterstore.spec.js b/test/commands/sinterstore.spec.js index 38b7dcfb5bb..d4a0c228a49 100644 --- a/test/commands/sinterstore.spec.js +++ b/test/commands/sinterstore.spec.js @@ -41,7 +41,7 @@ describe("The 'sinterstore' method", function () { }); afterEach(function () { - client.end(); + client.end(true); }); }); }); diff --git a/test/commands/sismember.spec.js b/test/commands/sismember.spec.js index eefd37e4089..8f556e741f4 100644 --- a/test/commands/sismember.spec.js +++ b/test/commands/sismember.spec.js @@ -28,7 +28,7 @@ describe("The 'sismember' method", function () { }); afterEach(function () { - client.end(); + client.end(true); }); }); }); diff --git a/test/commands/slowlog.spec.js b/test/commands/slowlog.spec.js index b2fbebdfe9c..b297a985f71 100644 --- a/test/commands/slowlog.spec.js +++ b/test/commands/slowlog.spec.js @@ -34,7 +34,7 @@ describe("The 'slowlog' method", function () { }); afterEach(function () { - client.end(); + client.end(true); }); }); }); diff --git a/test/commands/smembers.spec.js b/test/commands/smembers.spec.js index e92a658d501..c19efc2c81d 100644 --- a/test/commands/smembers.spec.js +++ b/test/commands/smembers.spec.js @@ -31,7 +31,7 @@ describe("The 'smembers' method", function () { }); afterEach(function () { - client.end(); + client.end(true); }); }); }); diff --git a/test/commands/smove.spec.js b/test/commands/smove.spec.js index 123ff163a7d..436069be6a9 100644 --- a/test/commands/smove.spec.js +++ b/test/commands/smove.spec.js @@ -33,7 +33,7 @@ describe("The 'smove' method", function () { }); afterEach(function () { - client.end(); + client.end(true); }); }); }); diff --git a/test/commands/sort.spec.js b/test/commands/sort.spec.js index 97e64a686b0..28355d9fe87 100644 --- a/test/commands/sort.spec.js +++ b/test/commands/sort.spec.js @@ -122,7 +122,7 @@ describe("The 'sort' method", function () { }); afterEach(function () { - client.end(); + client.end(true); }); }); }); diff --git a/test/commands/spop.spec.js b/test/commands/spop.spec.js index f8d4d46ede7..b44d9b172bf 100644 --- a/test/commands/spop.spec.js +++ b/test/commands/spop.spec.js @@ -31,7 +31,7 @@ describe("The 'spop' method", function () { }); afterEach(function () { - client.end(); + client.end(true); }); }); }); diff --git a/test/commands/srem.spec.js b/test/commands/srem.spec.js index e4e552ec5dd..dc79134d634 100644 --- a/test/commands/srem.spec.js +++ b/test/commands/srem.spec.js @@ -62,7 +62,7 @@ describe("The 'srem' method", function () { }); afterEach(function () { - client.end(); + client.end(true); }); }); }); diff --git a/test/commands/sunion.spec.js b/test/commands/sunion.spec.js index 638cb116a6a..deb751b1afc 100644 --- a/test/commands/sunion.spec.js +++ b/test/commands/sunion.spec.js @@ -39,7 +39,7 @@ describe("The 'sunion' method", function () { }); afterEach(function () { - client.end(); + client.end(true); }); }); }); diff --git a/test/commands/sunionstore.spec.js b/test/commands/sunionstore.spec.js index 3ebb10b085d..54cdddf52ea 100644 --- a/test/commands/sunionstore.spec.js +++ b/test/commands/sunionstore.spec.js @@ -42,7 +42,7 @@ describe("The 'sunionstore' method", function () { }); afterEach(function () { - client.end(); + client.end(true); }); }); }); diff --git a/test/commands/sync.spec.js b/test/commands/sync.spec.js index 6a9813112be..bd793a0d388 100644 --- a/test/commands/sync.spec.js +++ b/test/commands/sync.spec.js @@ -37,7 +37,7 @@ describe.skip("The 'sync' method", function () { }); afterEach(function () { - client.end(); + client.end(true); }); }); }); diff --git a/test/commands/ttl.spec.js b/test/commands/ttl.spec.js index deec21720a7..1df47cfe311 100644 --- a/test/commands/ttl.spec.js +++ b/test/commands/ttl.spec.js @@ -31,7 +31,7 @@ describe("The 'ttl' method", function () { }); afterEach(function () { - client.end(); + client.end(true); }); }); }); diff --git a/test/commands/type.spec.js b/test/commands/type.spec.js index c652e76d960..5e592054d08 100644 --- a/test/commands/type.spec.js +++ b/test/commands/type.spec.js @@ -48,7 +48,7 @@ describe("The 'type' method", function () { }); afterEach(function () { - client.end(); + client.end(true); }); }); }); diff --git a/test/commands/watch.spec.js b/test/commands/watch.spec.js index 87bcc81d222..3cc28334f87 100644 --- a/test/commands/watch.spec.js +++ b/test/commands/watch.spec.js @@ -22,7 +22,7 @@ describe("The 'watch' method", function () { }); afterEach(function () { - client.end(); + client.end(true); }); it('does not execute transaction if watched key was modified prior to execution', function (done) { diff --git a/test/commands/zadd.spec.js b/test/commands/zadd.spec.js index 514c51b3398..9536b2688ad 100644 --- a/test/commands/zadd.spec.js +++ b/test/commands/zadd.spec.js @@ -40,7 +40,7 @@ describe("The 'zadd' method", function () { }); afterEach(function () { - client.end(); + client.end(true); }); }); }); diff --git a/test/commands/zscan.spec.js b/test/commands/zscan.spec.js index f655bdeac0c..0794b8941ae 100644 --- a/test/commands/zscan.spec.js +++ b/test/commands/zscan.spec.js @@ -42,7 +42,7 @@ describe("The 'zscan' method", function () { }); afterEach(function () { - client.end(); + client.end(true); }); }); }); diff --git a/test/commands/zscore.spec.js b/test/commands/zscore.spec.js index 43964a32b3c..6bfd2796688 100644 --- a/test/commands/zscore.spec.js +++ b/test/commands/zscore.spec.js @@ -28,7 +28,7 @@ describe("The 'zscore' method", function () { }); afterEach(function () { - client.end(); + client.end(true); }); }); }); diff --git a/test/connection.spec.js b/test/connection.spec.js index 8383e794540..2223ba70e86 100644 --- a/test/connection.spec.js +++ b/test/connection.spec.js @@ -16,7 +16,7 @@ describe("connection tests", function () { client = null; }); afterEach(function () { - client.end(); + client.end(true); }); describe("on lost connection", function () { @@ -174,7 +174,7 @@ describe("connection tests", function () { client.on('connect', function () { assert.strictEqual(client.stream._idleTimeout, -1); assert.strictEqual(client.stream._events.timeout, undefined); - done(); + client.on('ready', done); }); }); diff --git a/test/node_redis.spec.js b/test/node_redis.spec.js index 4650a0a3330..2c03de5de95 100644 --- a/test/node_redis.spec.js +++ b/test/node_redis.spec.js @@ -32,7 +32,8 @@ describe("The node_redis client", function () { var client; afterEach(function () { - client.end(); + // Explicitly ignore still running commands + client.end(true); }); describe("when connected", function () { @@ -97,11 +98,14 @@ describe("The node_redis client", function () { describe(".end", function () { it('used without flush', function(done) { + var finished = false; var end = helper.callFuncAfter(function() { - done(new Error('failed')); + if (!finished) { + done(new Error('failed')); + } }, 20); var cb = function(err, res) { - assert.equal(err.message, "SET can't be processed. The connection has already been closed."); + assert(/The connection has already been closed/.test(err.message)); end(); }; for (var i = 0; i < 20; i++) { @@ -110,7 +114,10 @@ describe("The node_redis client", function () { } client.set('foo', 'bar', cb); } - setTimeout(done, 250); + setTimeout(function () { + finished = true; + done(); + }, 250); }); it('used with flush set to true', function(done) { @@ -255,14 +262,7 @@ describe("The node_redis client", function () { describe('domain', function () { it('allows client to be executed from within domain', function (done) { - var domain; - - try { - domain = require('domain').create(); - } catch (err) { - console.log("Skipping test because this version of node doesn't have domains."); - return done(); - } + var domain = require('domain').create(); domain.run(function () { client.set('domain', 'value', function (err, res) { @@ -505,19 +505,24 @@ describe("The node_redis client", function () { max_attempts: 0, parser: parser }); - + var finished = false; client.on('error', function(e) { // ignore, b/c expecting a "can't connect" error }); return setTimeout(function() { client.set('foo', 'bar', function(err, result) { - // This should never be called - return done(err); + if (!finished) { + // This should never be called + return done(err); + } else { + assert.strictEqual(err.message, "The command can't be processed. The connection has already been closed."); + } }); return setTimeout(function() { assert.strictEqual(client.offline_queue.length, 1); + finished = true; return done(); }, 25); }, 50); From 06a1bdd7b0bf72d46c71d92e9ca20d12f32e83bb Mon Sep 17 00:00:00 2001 From: Ruben Bridgewater Date: Sun, 22 Nov 2015 16:58:51 +0100 Subject: [PATCH 0484/1748] Fix js parser handling big values not fast enough Fixes #678 --- README.md | 74 +++++++++++----------- changelog.md | 7 +++ lib/parsers/javascript.js | 25 ++++---- test/parser.spec.js | 129 +++++++++++++++++++++++++++++--------- 4 files changed, 156 insertions(+), 79 deletions(-) diff --git a/README.md b/README.md index bd7c8fd480c..69681fbfbe5 100644 --- a/README.md +++ b/README.md @@ -650,45 +650,45 @@ Here are results of `multi_bench.js` which is similar to `redis-benchmark` from hiredis parser (Lenovo T450s i7-5600U): ``` -Client count: 1, node version: 4.2.1, server version: 3.0.3, parser: hiredis - PING, 1/1 min/max/avg/p95: 0/ 4/ 0.02/ 0.00 10001ms total, 38850.41 ops/sec - PING, batch 50/1 min/max/avg/p95: 0/ 3/ 0.10/ 1.00 10001ms total, 488376.16 ops/sec - SET 4B str, 1/1 min/max/avg/p95: 0/ 2/ 0.03/ 0.00 10001ms total, 35782.02 ops/sec - SET 4B str, batch 50/1 min/max/avg/p95: 0/ 2/ 0.14/ 1.00 10001ms total, 349740.03 ops/sec - SET 4B buf, 1/1 min/max/avg/p95: 0/ 5/ 0.04/ 0.00 10001ms total, 23497.75 ops/sec - SET 4B buf, batch 50/1 min/max/avg/p95: 0/ 3/ 0.28/ 1.00 10001ms total, 177087.29 ops/sec - GET 4B str, 1/1 min/max/avg/p95: 0/ 4/ 0.03/ 0.00 10001ms total, 37044.10 ops/sec - GET 4B str, batch 50/1 min/max/avg/p95: 0/ 4/ 0.12/ 1.00 10001ms total, 421987.80 ops/sec - GET 4B buf, 1/1 min/max/avg/p95: 0/ 4/ 0.03/ 0.00 10001ms total, 35608.24 ops/sec - GET 4B buf, batch 50/1 min/max/avg/p95: 0/ 3/ 0.12/ 1.00 10001ms total, 416593.34 ops/sec - SET 4KiB str, 1/1 min/max/avg/p95: 0/ 4/ 0.03/ 0.00 10001ms total, 30014.10 ops/sec - SET 4KiB str, batch 50/1 min/max/avg/p95: 0/ 4/ 0.34/ 1.00 10001ms total, 147705.23 ops/sec - SET 4KiB buf, 1/1 min/max/avg/p95: 0/ 4/ 0.04/ 0.00 10001ms total, 23803.52 ops/sec - SET 4KiB buf, batch 50/1 min/max/avg/p95: 0/ 4/ 0.37/ 1.00 10001ms total, 132611.74 ops/sec - GET 4KiB str, 1/1 min/max/avg/p95: 0/ 5/ 0.03/ 0.00 10001ms total, 34216.98 ops/sec - GET 4KiB str, batch 50/1 min/max/avg/p95: 0/ 4/ 0.32/ 1.00 10001ms total, 153039.70 ops/sec - GET 4KiB buf, 1/1 min/max/avg/p95: 0/ 3/ 0.03/ 0.00 10001ms total, 34169.18 ops/sec - GET 4KiB buf, batch 50/1 min/max/avg/p95: 0/ 2/ 0.32/ 1.00 10001ms total, 153264.67 ops/sec - INCR, 1/1 min/max/avg/p95: 0/ 3/ 0.03/ 0.00 10001ms total, 36307.17 ops/sec - INCR, batch 50/1 min/max/avg/p95: 0/ 4/ 0.12/ 1.00 10001ms total, 412438.76 ops/sec - LPUSH, 1/1 min/max/avg/p95: 0/ 4/ 0.03/ 0.00 10001ms total, 36073.89 ops/sec - LPUSH, batch 50/1 min/max/avg/p95: 0/ 2/ 0.14/ 1.00 10001ms total, 355954.40 ops/sec - LRANGE 10, 1/1 min/max/avg/p95: 0/ 2/ 0.03/ 0.00 10001ms total, 30395.66 ops/sec - LRANGE 10, batch 50/1 min/max/avg/p95: 0/ 3/ 0.33/ 1.00 10001ms total, 149400.06 ops/sec - LRANGE 100, 1/1 min/max/avg/p95: 0/ 2/ 0.06/ 1.00 10001ms total, 16814.62 ops/sec - LRANGE 100, batch 50/1 min/max/avg/p95: 1/ 4/ 2.01/ 2.00 10002ms total, 24790.04 ops/sec - SET 4MiB str, 1/1 min/max/avg/p95: 1/ 7/ 2.01/ 2.00 10002ms total, 496.90 ops/sec - SET 4MiB str, batch 20/1 min/max/avg/p95: 100/ 135/ 109.58/ 125.00 10085ms total, 182.45 ops/sec - SET 4MiB buf, 1/1 min/max/avg/p95: 1/ 5/ 1.87/ 2.00 10001ms total, 531.75 ops/sec - SET 4MiB buf, batch 20/1 min/max/avg/p95: 52/ 77/ 58.90/ 68.45 10016ms total, 339.46 ops/sec - GET 4MiB str, 1/1 min/max/avg/p95: 3/ 19/ 5.79/ 11.00 10005ms total, 172.51 ops/sec - GET 4MiB str, batch 20/1 min/max/avg/p95: 73/ 112/ 89.89/ 107.00 10072ms total, 222.40 ops/sec - GET 4MiB buf, 1/1 min/max/avg/p95: 3/ 13/ 5.35/ 9.00 10002ms total, 186.76 ops/sec - GET 4MiB buf, batch 20/1 min/max/avg/p95: 76/ 106/ 85.37/ 98.00 10077ms total, 234.20 ops/sec +Client count: 1, node version: 4.2.2, server version: 3.0.3, parser: hiredis + PING, 1/1 min/max/avg/p95: 0/ 3/ 0.02/ 0.00 2501ms total, 39862.85 ops/sec + PING, batch 50/1 min/max/avg/p95: 0/ 2/ 0.10/ 1.00 2501ms total, 491223.51 ops/sec + SET 4B str, 1/1 min/max/avg/p95: 0/ 3/ 0.03/ 0.00 2501ms total, 36387.45 ops/sec + SET 4B str, batch 50/1 min/max/avg/p95: 0/ 3/ 0.14/ 1.00 2501ms total, 346381.45 ops/sec + SET 4B buf, 1/1 min/max/avg/p95: 0/ 2/ 0.04/ 0.00 2501ms total, 24395.84 ops/sec + SET 4B buf, batch 50/1 min/max/avg/p95: 0/ 2/ 0.32/ 1.00 2501ms total, 156457.42 ops/sec + GET 4B str, 1/1 min/max/avg/p95: 0/ 3/ 0.03/ 0.00 2501ms total, 36906.44 ops/sec + GET 4B str, batch 50/1 min/max/avg/p95: 0/ 3/ 0.12/ 1.00 2501ms total, 425729.71 ops/sec + GET 4B buf, 1/1 min/max/avg/p95: 0/ 2/ 0.03/ 0.00 2501ms total, 36221.91 ops/sec + GET 4B buf, batch 50/1 min/max/avg/p95: 0/ 2/ 0.11/ 1.00 2501ms total, 430407.84 ops/sec + SET 4KiB str, 1/1 min/max/avg/p95: 0/ 3/ 0.03/ 0.00 2501ms total, 30951.22 ops/sec + SET 4KiB str, batch 50/1 min/max/avg/p95: 0/ 2/ 0.33/ 1.00 2501ms total, 150299.88 ops/sec + SET 4KiB buf, 1/1 min/max/avg/p95: 0/ 2/ 0.04/ 1.00 2501ms total, 23919.63 ops/sec + SET 4KiB buf, batch 50/1 min/max/avg/p95: 0/ 2/ 0.36/ 1.00 2501ms total, 139204.32 ops/sec + GET 4KiB str, 1/1 min/max/avg/p95: 0/ 2/ 0.03/ 0.00 2501ms total, 32739.30 ops/sec + GET 4KiB str, batch 50/1 min/max/avg/p95: 0/ 2/ 0.32/ 1.00 2501ms total, 154158.34 ops/sec + GET 4KiB buf, 1/1 min/max/avg/p95: 0/ 2/ 0.03/ 0.00 2501ms total, 34654.94 ops/sec + GET 4KiB buf, batch 50/1 min/max/avg/p95: 0/ 2/ 0.32/ 1.00 2501ms total, 153758.50 ops/sec + INCR, 1/1 min/max/avg/p95: 0/ 2/ 0.03/ 0.00 2501ms total, 37530.19 ops/sec + INCR, batch 50/1 min/max/avg/p95: 0/ 3/ 0.12/ 1.00 2501ms total, 415993.60 ops/sec + LPUSH, 1/1 min/max/avg/p95: 0/ 1/ 0.03/ 0.00 2501ms total, 37409.04 ops/sec + LPUSH, batch 50/1 min/max/avg/p95: 0/ 2/ 0.14/ 1.00 2501ms total, 354778.09 ops/sec + LRANGE 10, 1/1 min/max/avg/p95: 0/ 3/ 0.03/ 0.00 2501ms total, 31768.49 ops/sec + LRANGE 10, batch 50/1 min/max/avg/p95: 0/ 3/ 0.33/ 1.00 2501ms total, 151379.45 ops/sec + LRANGE 100, 1/1 min/max/avg/p95: 0/ 2/ 0.06/ 1.00 2501ms total, 16801.68 ops/sec + LRANGE 100, batch 50/1 min/max/avg/p95: 2/ 4/ 2.07/ 3.00 2501ms total, 24150.34 ops/sec + SET 4MiB str, 1/1 min/max/avg/p95: 1/ 5/ 1.96/ 2.00 2501ms total, 510.20 ops/sec + SET 4MiB str, batch 20/1 min/max/avg/p95: 83/ 108/ 94.44/ 106.40 2550ms total, 211.76 ops/sec + SET 4MiB buf, 1/1 min/max/avg/p95: 1/ 7/ 2.06/ 3.00 2501ms total, 484.21 ops/sec + SET 4MiB buf, batch 20/1 min/max/avg/p95: 38/ 48/ 40.90/ 46.00 2536ms total, 488.96 ops/sec + GET 4MiB str, 1/1 min/max/avg/p95: 3/ 13/ 5.20/ 9.00 2503ms total, 192.17 ops/sec + GET 4MiB str, batch 20/1 min/max/avg/p95: 74/ 105/ 87.24/ 104.00 2530ms total, 229.25 ops/sec + GET 4MiB buf, 1/1 min/max/avg/p95: 3/ 11/ 5.01/ 9.00 2501ms total, 199.12 ops/sec + GET 4MiB buf, batch 20/1 min/max/avg/p95: 78/ 93/ 84.23/ 91.90 2528ms total, 237.34 ops/sec ``` -The hiredis and js parser should most of the time be on the same level. The js parser lacks speed for large responses though. -Therefor the hiredis parser is the default used in node_redis and we recommend using the hiredis parser. To use `hiredis`, do: +The hiredis and js parser should most of the time be on the same level. But if you use Redis for big SUNION/SINTER/LRANGE/ZRANGE hiredis is significantly faster. +Therefor the hiredis parser is the default used in node_redis. To use `hiredis`, do: npm install hiredis redis diff --git a/changelog.md b/changelog.md index 704b17054a7..502eaa8a870 100644 --- a/changelog.md +++ b/changelog.md @@ -1,6 +1,13 @@ Changelog ========= +## v.2.x.x - xx Nov, 2015 + +Bugfixes + +- Fixed js parser handling big values slow ([@BridgeAR](https://github.com/BridgeAR)) + - The speed is now on par with the hiredis parser. + ## v.2.3.1 - 18 Nov, 2015 Bugfixes diff --git a/lib/parsers/javascript.js b/lib/parsers/javascript.js index e6f6ec07844..7decd0447b2 100644 --- a/lib/parsers/javascript.js +++ b/lib/parsers/javascript.js @@ -6,6 +6,8 @@ function JavascriptReplyParser() { this.name = exports.name; this._buffer = new Buffer(0); this._offset = 0; + this._big_offset = 0; + this._chunks_size = 0; this._buffers = []; } @@ -37,10 +39,6 @@ JavascriptReplyParser.prototype._parseResult = function (type) { } return new Error(this._buffer.toString('utf-8', start, end)); } else if (type === 36) { // $ - // set a rewind point, as the packet could be larger than the - // buffer in memory - offset = this._offset - 1; - packetHeader = this.parseHeader(); // packets with a size of -1 are considered null @@ -52,6 +50,8 @@ JavascriptReplyParser.prototype._parseResult = function (type) { start = this._offset; if (end > this._buffer.length) { + this._chunks_size = this._buffer.length - this._offset - 2; + this._big_offset = packetHeader; throw new IncompleteReadBuffer('Wait for more data.'); } @@ -60,6 +60,7 @@ JavascriptReplyParser.prototype._parseResult = function (type) { return this._buffer.slice(start, end); } else if (type === 42) { // * + // set a rewind point, as the packet is larger than the buffer in memory offset = this._offset; packetHeader = this.parseHeader(); @@ -94,14 +95,11 @@ JavascriptReplyParser.prototype._parseResult = function (type) { }; JavascriptReplyParser.prototype.execute = function (buffer) { - var i = buffer.length - 1; - while (buffer[i] !== 0x0a) { - i--; - if (i < 1) { - this._buffers.push(buffer); - return; - } + if (this._chunks_size !== 0 && this._big_offset > this._chunks_size + buffer.length) { + this._buffers.push(buffer); + this._chunks_size += buffer.length; + return; } if (this._buffers.length !== 0) { @@ -109,6 +107,8 @@ JavascriptReplyParser.prototype.execute = function (buffer) { this._buffers.push(buffer); this._buffer = Buffer.concat(this._buffers); this._buffers = []; + this._big_offset = 0; + this._chunks_size = 0; } else if (this._offset >= this._buffer.length) { this._buffer = buffer; } else { @@ -142,6 +142,8 @@ JavascriptReplyParser.prototype.run = function (buffer) { this.send_reply(this._parseResult(type)); } else if (type !== 10 && type !== 13) { + // Reset the buffer so the parser can handle following commands properly + this._buffer = new Buffer(0); var err = new Error('Protocol error, got "' + String.fromCharCode(type) + '" as reply type byte'); this.send_error(err); } @@ -169,7 +171,6 @@ JavascriptReplyParser.prototype._packetEndOffset = function () { while (this._buffer[offset] !== 0x0d && this._buffer[offset + 1] !== 0x0a) { offset++; - /* istanbul ignore if: activate the js parser out of memory test to test this */ if (offset >= this._buffer.length) { throw new IncompleteReadBuffer('Did not see LF after NL reading multi bulk count (' + offset + ' => ' + this._buffer.length + ', ' + this._offset + ')'); } diff --git a/test/parser.spec.js b/test/parser.spec.js index 01dd38e5391..c1444186b79 100644 --- a/test/parser.spec.js +++ b/test/parser.spec.js @@ -1,9 +1,7 @@ 'use strict'; var assert = require('assert'); -var config = require("./lib/config"); var utils = require("../lib/utils"); -var redis = config.redis; var parsers = [ require("../lib/parsers/javascript").Parser ]; @@ -73,37 +71,108 @@ describe('parsers', function () { assert.equal(reply_count, 3, "check reply should have been called three times"); return done(); }); - }); - }); - // Activate this if you want to fry your cpu / memory - describe.skip("test out of memory", function () { - var args = config.configureClient('javascript', '127.0.0.1'); - var clients = new Array(300).join(" ").split(" "); - var client; - beforeEach(function (done) { - client = redis.createClient.apply(redis.createClient, args); - client.once("connect", function () { - client.flushdb(done); + it('multiple chunks in a bulk string', function (done) { + var parser = new Parser(); + var reply_count = 0; + function check_reply(reply) { + reply = utils.reply_to_strings(reply); + assert.strictEqual(reply, "abcdefghijabcdefghijabcdefghijabcdefghijabcdefghijabcdefghijabcdefghijabcdefghijabcdefghijabcdefghij"); + reply_count++; + } + parser.send_reply = check_reply; + + parser.execute(new Buffer('$100\r\nabcdefghij')); + parser.execute(new Buffer('abcdefghijabcdefghijabcdefghij')); + parser.execute(new Buffer('abcdefghijabcdefghijabcdefghij')); + parser.execute(new Buffer('abcdefghijabcdefghijabcdefghij')); + assert.strictEqual(reply_count, 0); + parser.execute(new Buffer('\r\n')); + assert.strictEqual(reply_count, 1); + + parser.execute(new Buffer('$100\r')); + parser.execute(new Buffer('\nabcdefghijabcdefghijabcdefghijabcdefghij')); + parser.execute(new Buffer('abcdefghijabcdefghijabcdefghij')); + parser.execute(new Buffer('abcdefghijabcdefghij')); + assert.strictEqual(reply_count, 1); + parser.execute(new Buffer( + 'abcdefghij\r\n' + + '$100\r\nabcdefghijabcdefghijabcdefghijabcdefghijabcdefghijabcdefghijabcdefghijabcdefghijabcdefghijabcdefghij\r\n' + + '$100\r\nabcdefghijabcdefghijabcdefghijabcdefghij' + )); + assert.strictEqual(reply_count, 3); + parser.execute(new Buffer('abcdefghijabcdefghijabcdefghij')); + parser.execute(new Buffer('abcdefghijabcdefghijabcdefghij\r')); + assert.strictEqual(reply_count, 3); + parser.execute(new Buffer('\n')); + + assert.equal(reply_count, 4, "check reply should have been called three times"); + return done(); }); - }); - it('reach limit and wait for further data', function (done) { - setTimeout(done, 5000); - clients.forEach(function(entry, a) { - var max = 0; - var client = redis.createClient.apply(redis.createClient, args); - client.on('ready', function() { - while (++max < 50) { - var item = []; - for (var i = 0; i < 100; ++i) { - item.push('aaa' + (Math.random() * 1000000 | 0)); - } - client.del('foo' + a); - client.lpush('foo' + a, item); - client.lrange('foo' + a, 0, 99); - } - }); + it('multiple chunks with arrays different types', function (done) { + var parser = new Parser(); + var reply_count = 0; + function check_reply(reply) { + reply = utils.reply_to_strings(reply); + assert.deepStrictEqual(reply, [ + 'abcdefghijabcdefghijabcdefghijabcdefghijabcdefghijabcdefghijabcdefghijabcdefghijabcdefghijabcdefghij', + 'test', + 100, + new Error('Error message'), + ['The force awakens'] + ]); + reply_count++; + } + parser.send_reply = check_reply; + + parser.execute(new Buffer('*5\r\n$100\r\nabcdefghij')); + parser.execute(new Buffer('abcdefghijabcdefghijabcdefghij')); + parser.execute(new Buffer('abcdefghijabcdefghijabcdefghij')); + parser.execute(new Buffer('abcdefghijabcdefghijabcdefghij\r\n')); + parser.execute(new Buffer('+test\r')); + parser.execute(new Buffer('\n:100')); + parser.execute(new Buffer('\r\n-Error message')); + parser.execute(new Buffer('\r\n*1\r\n$17\r\nThe force')); + assert.strictEqual(reply_count, 0); + parser.execute(new Buffer(' awakens\r\n$5')); + assert.strictEqual(reply_count, 1); + return done(); + }); + + it('return normal errors', function (done) { + var parser = new Parser(); + var reply_count = 0; + function check_reply(reply) { + assert.equal(reply.message, 'Error message'); + reply_count++; + } + parser.send_error = check_reply; + + parser.execute(new Buffer('-Error ')); + parser.execute(new Buffer('message\r\n*3\r\n$17\r\nThe force')); + assert.strictEqual(reply_count, 1); + parser.execute(new Buffer(' awakens\r\n$5')); + assert.strictEqual(reply_count, 1); + return done(); + }); + + it('return null for empty arrays and empty bulk strings', function (done) { + var parser = new Parser(); + var reply_count = 0; + function check_reply(reply) { + assert.equal(reply, null); + reply_count++; + } + parser.send_reply = check_reply; + + parser.execute(new Buffer('$-1\r\n*-')); + assert.strictEqual(reply_count, 1); + parser.execute(new Buffer('1')); + assert.strictEqual(reply_count, 1); + parser.execute(new Buffer('\r\n$-')); + assert.strictEqual(reply_count, 2); + return done(); }); }); }); From 0903bba205c63b59a0fb8fa43adda150162c7d74 Mon Sep 17 00:00:00 2001 From: Ruben Bridgewater Date: Sun, 22 Nov 2015 16:59:49 +0100 Subject: [PATCH 0485/1748] Remove old and not anymore needed connection breaker --- connection_breaker.js | 80 ------------------------------------------- 1 file changed, 80 deletions(-) delete mode 100644 connection_breaker.js diff --git a/connection_breaker.js b/connection_breaker.js deleted file mode 100644 index e036a2b1730..00000000000 --- a/connection_breaker.js +++ /dev/null @@ -1,80 +0,0 @@ -'use strict'; - -var net = require('net'); - -var proxyPort = 6379; -var counter = 0; - -function breaker(conn) { - conn.end(); - conn.destroy(); -} - -var server = net.createServer(function(conn) { - counter++; - var proxyConn = net.createConnection({ - port: proxyPort - }); - conn.pipe(proxyConn); - proxyConn.pipe(conn); - proxyConn.on('end', function() { - conn.end(); - }); - conn.on('end', function() { - proxyConn.end(); - }); - conn.on('close', function() { - proxyConn.end(); - }); - proxyConn.on('close', function() { - conn.end(); - }); - proxyConn.on('error', function() { - conn.end(); - }); - conn.on('error', function() { - proxyConn.end(); - }); - - setTimeout(breaker.bind(null, conn), Math.floor(Math.random() * 2000)); -}); -server.listen(6479); - -var redis = require('./'); - -var client = redis.createClient(6479, 'localhost'); - -function iter() { - var k = "k" + Math.floor(Math.random() * 10); - var coinflip = Math.random() > 0.5; - if (coinflip) { - client.set(k, k, function(err, resp) { - if (!err && resp !== "OK") { - console.log("Unexpected set response " + resp); - } - }); - } else { - client.get(k, function(err, resp) { - if (!err) { - if (k !== resp) { - console.log("Key response mismatch: " + k + " " + resp); - } - } - }); - } -} - -function iters() { - for (var i = 0; i < 100; ++i) { - iter(); - } - setTimeout(iters, 10); -} - -client.on("connect", function () { - iters(); -}); - -client.on("error", function (err) { - console.log("Client error " + err); -}); From d3352bf5501d5be9f73ec91675161465351e0924 Mon Sep 17 00:00:00 2001 From: Ruben Bridgewater Date: Sun, 22 Nov 2015 17:00:22 +0100 Subject: [PATCH 0486/1748] Auto detect ip family if a IP has been provided --- index.js | 2 +- test/connection.spec.js | 12 +++++++++--- 2 files changed, 10 insertions(+), 4 deletions(-) diff --git a/index.js b/index.js index d89ea4f6fd4..175477b2fa2 100644 --- a/index.js +++ b/index.js @@ -47,7 +47,7 @@ function RedisClient(options) { } else { cnx_options.port = options.port || default_port; cnx_options.host = options.host || default_host; - cnx_options.family = options.family === 'IPv6' ? 6 : 4; + cnx_options.family = (!options.family && net.isIP(cnx_options.host)) || (options.family === 'IPv6' ? 6 : 4); this.address = cnx_options.host + ':' + cnx_options.port; } this.connection_option = cnx_options; diff --git a/test/connection.spec.js b/test/connection.spec.js index 2223ba70e86..ee883f60b6b 100644 --- a/test/connection.spec.js +++ b/test/connection.spec.js @@ -99,10 +99,12 @@ describe("connection tests", function () { var options = { host: 'somewhere', port: 6379, + family: ip, max_attempts: 1 }; client = redis.createClient(options); - assert.strictEqual(Object.keys(options).length, 3); + assert.strictEqual(client.connection_option.family, ip === 'IPv6' ? 6 : 4); + assert.strictEqual(Object.keys(options).length, 4); var end = helper.callFuncAfter(done, 2); client.on('error', function (err) { @@ -133,13 +135,14 @@ describe("connection tests", function () { var connect_timeout = 1000; // in ms client = redis.createClient({ parser: parser, - host: '192.168.74.167', + host: '192.168.74.167', // Should be auto detected as ipv4 connect_timeout: connect_timeout }); process.nextTick(function() { assert(client.stream._events.timeout); }); assert.strictEqual(client.address, '192.168.74.167:6379'); + assert.strictEqual(client.connection_option.family, 4); var time = Date.now(); client.on("reconnecting", function (params) { @@ -156,8 +159,10 @@ describe("connection tests", function () { it("use the system socket timeout if the connect_timeout has not been provided", function () { client = redis.createClient({ parser: parser, - host: '192.168.74.167' + host: '2001:db8::ff00:42:8329' // auto detect ip v6 }); + assert.strictEqual(client.address, '2001:db8::ff00:42:8329:6379'); + assert.strictEqual(client.connection_option.family, 6); process.nextTick(function() { assert.strictEqual(client.stream._events.timeout, undefined); }); @@ -235,6 +240,7 @@ describe("connection tests", function () { it("connects with a port only", function (done) { client = redis.createClient(6379); + assert.strictEqual(client.connection_option.family, 4); client.on("error", done); client.once("ready", function () { From 634dcee859237b8d546a2033c40d3eefa15d3c9b Mon Sep 17 00:00:00 2001 From: Ruben Bridgewater Date: Sun, 22 Nov 2015 17:01:10 +0100 Subject: [PATCH 0487/1748] Only initiate the parser once per instance and throw sync if the parser can't be found --- index.js | 5 ++--- test/node_redis.spec.js | 18 ++++++++---------- 2 files changed, 10 insertions(+), 13 deletions(-) diff --git a/index.js b/index.js index 175477b2fa2..fbc6f559dfd 100644 --- a/index.js +++ b/index.js @@ -90,7 +90,8 @@ function RedisClient(options) { this.old_state = null; this.pipeline = 0; this.options = options; - + // Init parser once per instance + this.init_parser(); self.stream = net.createConnection(cnx_options); self.install_stream_listeners(); } @@ -256,8 +257,6 @@ RedisClient.prototype.on_connect = function () { this.stream.setKeepAlive(this.options.socket_keepalive); this.stream.setTimeout(0); - this.init_parser(); - if (typeof this.auth_pass === 'string') { this.do_auth(); } else { diff --git a/test/node_redis.spec.js b/test/node_redis.spec.js index 2c03de5de95..ca13878597c 100644 --- a/test/node_redis.spec.js +++ b/test/node_redis.spec.js @@ -10,17 +10,15 @@ describe("The node_redis client", function () { describe("testing parser existence", function () { it('throws on non-existence', function (done) { - var mochaListener = helper.removeMochaListener(); - - process.once('uncaughtException', function (err) { - process.on('uncaughtException', mochaListener); + try { + redis.createClient({ + parser: 'nonExistingParser' + }); + done(new Error('test failed')); + } catch (err) { assert.equal(err.message, 'Couldn\'t find named parser nonExistingParser on this system'); - return done(); - }); - - redis.createClient({ - parser: 'nonExistingParser' - }); + done(); + } }); }); From bc85c4a01d30276509bb50055b46dd8aa9becd5a Mon Sep 17 00:00:00 2001 From: Ruben Bridgewater Date: Sun, 22 Nov 2015 17:02:18 +0100 Subject: [PATCH 0488/1748] Minor hiredis handling improvement --- index.js | 2 +- lib/parsers/hiredis.js | 35 +++++++++++++++-------------------- 2 files changed, 16 insertions(+), 21 deletions(-) diff --git a/index.js b/index.js index fbc6f559dfd..a070e93b0bd 100644 --- a/index.js +++ b/index.js @@ -293,7 +293,7 @@ RedisClient.prototype.init_parser = function () { // return_buffers sends back Buffers from parser to callback. detect_buffers sends back Buffers from parser, but // converts to Strings if the input arguments are not Buffers. - this.reply_parser = new this.parser_module.Parser(self.options.return_buffers || self.options.detect_buffers || false); + this.reply_parser = new this.parser_module.Parser(self.options.return_buffers || self.options.detect_buffers); // Important: Only send results / errors async. // That way the result / error won't stay in a try catch block and catch user things this.reply_parser.send_error = function (data) { diff --git a/lib/parsers/hiredis.js b/lib/parsers/hiredis.js index e16304e8e6d..1b254191037 100644 --- a/lib/parsers/hiredis.js +++ b/lib/parsers/hiredis.js @@ -4,37 +4,32 @@ var hiredis = require('hiredis'); function HiredisReplyParser(return_buffers) { this.name = exports.name; - this.return_buffers = return_buffers; - this.reset(); -} - -HiredisReplyParser.prototype.reset = function () { this.reader = new hiredis.Reader({ - return_buffers: this.return_buffers || false + return_buffers: return_buffers }); +} + +HiredisReplyParser.prototype.return_data = function () { + try { + return this.reader.get(); + } catch (err) { + // Protocol errors land here + this.send_error(err); + return void 0; + } }; HiredisReplyParser.prototype.execute = function (data) { - var reply; this.reader.feed(data); - while (true) { - try { - reply = this.reader.get(); - } catch (err) { - // Protocol errors land here - this.send_error(err); - break; - } - - if (reply === undefined) { - break; - } + var reply = this.return_data(); - if (reply && reply.constructor === Error) { + while (reply !== undefined) { + if (reply && reply.name === 'Error') { this.send_error(reply); } else { this.send_reply(reply); } + reply = this.return_data(); } }; From 30d2184dbb6d5f9391c1a63b7eced1c6a3967598 Mon Sep 17 00:00:00 2001 From: Ruben Bridgewater Date: Sun, 22 Nov 2015 17:02:44 +0100 Subject: [PATCH 0489/1748] Throw on other protocols provided than the redis protocol --- index.js | 3 +++ test/connection.spec.js | 12 ++++++++++++ 2 files changed, 15 insertions(+) diff --git a/index.js b/index.js index a070e93b0bd..b93ddfe5a39 100644 --- a/index.js +++ b/index.js @@ -1270,6 +1270,9 @@ var createClient = function (port_arg, host_arg, options) { if (parsed.auth) { options.auth_pass = parsed.auth.split(':')[1]; } + if (parsed.protocol !== 'redis:') { + throw new Error('Connection string must use the "redis:" protocol'); + } options.host = parsed.hostname; options.port = parsed.port; } else { diff --git a/test/connection.spec.js b/test/connection.spec.js index ee883f60b6b..1c70f620219 100644 --- a/test/connection.spec.js +++ b/test/connection.spec.js @@ -307,6 +307,18 @@ describe("connection tests", function () { } }); + it("throws on protocol other than redis in the redis url", function () { + client = { + end: function() {} + }; + try { + redis.createClient(config.HOST[ip] + ':' + config.PORT); + throw new Error('failed'); + } catch (err) { + assert.equal(err.message, 'Connection string must use the "redis:" protocol'); + } + }); + if (ip === 'IPv4') { it('allows connecting with the redis url and the default port', function (done) { client = redis.createClient('redis://foo:porkchopsandwiches@' + config.HOST[ip]); From 8f9ad00de272e237bd4fb5aed70d8f147e4bc875 Mon Sep 17 00:00:00 2001 From: Ruben Bridgewater Date: Sun, 22 Nov 2015 22:48:13 +0100 Subject: [PATCH 0490/1748] Add the redis url to the options object and accept .createClient(null, host, options) --- LICENSE | 2 +- README.md | 1 + index.js | 22 ++++++++--------- test/connection.spec.js | 52 +++++++++++++++++++++++++++++++++++++++++ 4 files changed, 64 insertions(+), 13 deletions(-) diff --git a/LICENSE b/LICENSE index 777c0b82086..a515acd061a 100644 --- a/LICENSE +++ b/LICENSE @@ -1,4 +1,4 @@ -## LICENSE - "MIT License" +LICENSE - "MIT License" Copyright (c) 2015 by NodeRedis diff --git a/README.md b/README.md index 69681fbfbe5..9187bd1cfef 100644 --- a/README.md +++ b/README.md @@ -180,6 +180,7 @@ port and host are probably fine and you don't need to supply any arguments. `cre * `host`: *127.0.0.1*; The host to connect to * `port`: *6370*; The port to connect to * `path`: *null*; The unix socket string to connect to +* `url`: *null*; The redis url to connect to * `parser`: *hiredis*; Which Redis protocol reply parser to use. If `hiredis` is not installed it will fallback to `javascript`. * `return_buffers`: *false*; If set to `true`, then all replies will be sent to callbacks as Buffers instead of Strings. * `detect_buffers`: *false*; If set to `true`, then replies will be sent to callbacks as Buffers. Please be aware that this can't work properly with the pubsub mode. A subscriber has to either always return strings or buffers. diff --git a/index.js b/index.js index b93ddfe5a39..a1c7d4d5381 100644 --- a/index.js +++ b/index.js @@ -14,14 +14,10 @@ var commands = require('./lib/commands'); var connection_id = 0; var default_port = 6379; var default_host = '127.0.0.1'; -var debug = function(msg) { - if (exports.debug_mode) { - console.error(msg); - } -}; function noop () {} function clone (obj) { return JSON.parse(JSON.stringify(obj || {})); } +function debug (msg) { if (exports.debug_mode) { console.error(msg); } } exports.debug_mode = /\bredis\b/i.test(process.env.NODE_DEBUG); @@ -35,7 +31,7 @@ try { parsers.push(require('./lib/parsers/javascript')); -function RedisClient(options) { +function RedisClient (options) { // Copy the options so they are not mutated options = clone(options); events.EventEmitter.call(this); @@ -1223,6 +1219,7 @@ Multi.prototype.exec = Multi.prototype.EXEC = Multi.prototype.exec_batch = funct var args; if (len === 0) { if (callback) { + // The execution order won't be obtained in this case setImmediate(function () { callback(null, []); }); @@ -1257,15 +1254,13 @@ Multi.prototype.exec = Multi.prototype.EXEC = Multi.prototype.exec_batch = funct }; var createClient = function (port_arg, host_arg, options) { - if (typeof port_arg === 'object' || port_arg === undefined) { - options = port_arg || options || {}; - } else if (typeof port_arg === 'number' || typeof port_arg === 'string' && /^\d+$/.test(port_arg)) { + if (typeof port_arg === 'number' || typeof port_arg === 'string' && /^\d+$/.test(port_arg)) { options = clone(options); options.host = host_arg; options.port = port_arg; - } else if (typeof port_arg === 'string') { - options = clone(host_arg || options); - var parsed = URL.parse(port_arg, true, true); + } else if (typeof port_arg === 'string' || port_arg && port_arg.url) { + options = clone(port_arg.url ? port_arg : host_arg || options); + var parsed = URL.parse(port_arg.url || port_arg, true, true); if (parsed.hostname) { if (parsed.auth) { options.auth_pass = parsed.auth.split(':')[1]; @@ -1278,6 +1273,9 @@ var createClient = function (port_arg, host_arg, options) { } else { options.path = port_arg; } + } else if (typeof port_arg === 'object' || port_arg === undefined) { + options = clone(port_arg || options); + options.host = options.host || host_arg; } if (!options) { throw new Error('Unknown type of connection in createClient()'); diff --git a/test/connection.spec.js b/test/connection.spec.js index 1c70f620219..259c616a357 100644 --- a/test/connection.spec.js +++ b/test/connection.spec.js @@ -263,6 +263,20 @@ describe("connection tests", function () { }); }); + it("connects correctly to the provided host with the port set to null", function (done) { + client = redis.createClient(null, 'localhost'); + client.on("error", done); + assert.strictEqual(client.address, 'localhost:6379'); + + client.once("ready", function () { + client.set('foo', 'bar'); + client.get('foo', function(err, res) { + assert.strictEqual(res, 'bar'); + done(err); + }); + }); + }); + it("connects correctly to localhost and no ready check", function (done) { client = redis.createClient(undefined, undefined, { no_ready_check: true @@ -278,6 +292,22 @@ describe("connection tests", function () { }); }); + it("connects correctly to the provided host with the port set to undefined", function (done) { + client = redis.createClient(undefined, 'localhost', { + no_ready_check: true + }); + client.on("error", done); + assert.strictEqual(client.address, 'localhost:6379'); + + client.once("ready", function () { + client.set('foo', 'bar'); + client.get('foo', function(err, res) { + assert.strictEqual(res, 'bar'); + done(err); + }); + }); + }); + it("connects correctly even if the info command is not present on the redis server", function (done) { client = redis.createClient.apply(redis.createClient, args); client.info = function (cb) { @@ -327,6 +357,28 @@ describe("connection tests", function () { }); }); + it('allows connecting with the redis url as first parameter and the options as second parameter', function (done) { + client = redis.createClient('redis://127.0.0.1', { + connect_timeout: 1000 + }); + assert.strictEqual(client.options.connect_timeout, 1000); + client.on('ready', function () { + done(); + }); + }); + + it('allows connecting with the redis url in the options object', function (done) { + client = redis.createClient({ + url: 'redis://foo:porkchopsandwiches@' + config.HOST[ip] + }); + assert.strictEqual(client.options.auth_pass, 'porkchopsandwiches'); + assert(!client.options.port); + assert.strictEqual(client.options.host, config.HOST[ip]); + client.on("ready", function () { + return done(); + }); + }); + it('allows connecting with the redis url and no auth and options as second parameter', function (done) { var options = { detect_buffers: false From 8712e32e62871eb013a3bfbe88d295e3d3c89775 Mon Sep 17 00:00:00 2001 From: Ruben Bridgewater Date: Sun, 22 Nov 2015 22:49:22 +0100 Subject: [PATCH 0491/1748] Remove comment about reusing the same multi function. This is not supported anymore. Fixes #923 --- README.md | 6 ------ 1 file changed, 6 deletions(-) diff --git a/README.md b/README.md index 9187bd1cfef..cd728c8f023 100644 --- a/README.md +++ b/README.md @@ -469,12 +469,6 @@ client.mset("incr thing", 100, "incr other thing", 1, redis.print); multi.exec(function (err, replies) { console.log(replies); // 101, 2 }); - -// you can re-run the same transaction if you like -multi.exec(function (err, replies) { - console.log(replies); // 102, 3 - client.quit(); -}); ``` In addition to adding commands to the `MULTI` queue individually, you can also pass an array From b6a81a42975f3157d7d9064f56d4260473c0f679 Mon Sep 17 00:00:00 2001 From: Ruben Bridgewater Date: Sun, 22 Nov 2015 22:50:28 +0100 Subject: [PATCH 0492/1748] Use a .create_stream function, so other libraries can mock the stream if wanted Reference https://github.com/hdachev/fakeredis/pull/34 --- index.js | 12 +++++------- test/connection.spec.js | 19 ++++++++++++++++--- 2 files changed, 21 insertions(+), 10 deletions(-) diff --git a/index.js b/index.js index a1c7d4d5381..422bc41965e 100644 --- a/index.js +++ b/index.js @@ -88,13 +88,14 @@ function RedisClient (options) { this.options = options; // Init parser once per instance this.init_parser(); - self.stream = net.createConnection(cnx_options); - self.install_stream_listeners(); + self.create_stream(); } util.inherits(RedisClient, events.EventEmitter); -RedisClient.prototype.install_stream_listeners = function () { +// Attention: the function name "create_stream" should not be changed, as other libraries need this to mock the stream (e.g. fakeredis) +RedisClient.prototype.create_stream = function () { var self = this; + this.stream = net.createConnection(this.connection_option); if (this.options.connect_timeout) { this.stream.setTimeout(this.connect_timeout, function () { @@ -479,10 +480,7 @@ var retry_connection = function (self) { self.retry_totaltime += self.retry_delay; self.attempts += 1; self.retry_delay = Math.round(self.retry_delay * self.retry_backoff); - - self.stream = net.createConnection(self.connection_option); - self.install_stream_listeners(); - + self.create_stream(); self.retry_timer = null; }; diff --git a/test/connection.spec.js b/test/connection.spec.js index 259c616a357..38fb705aa33 100644 --- a/test/connection.spec.js +++ b/test/connection.spec.js @@ -320,9 +320,22 @@ describe("connection tests", function () { }); }); - it("works with missing options object for new redis instances", function () { - // This is needed for libraries that have their own createClient function like fakeredis - client = new redis.RedisClient({ on: function () {}}); + it("fake the stream to mock redis", function () { + // This is needed for libraries that want to mock the stream like fakeredis + var temp = redis.RedisClient.prototype.create_stream; + var create_stream_string = String(temp); + redis.RedisClient.prototype.create_stream = function () { + this.connected = true; + this.ready = true; + }; + client = new redis.RedisClient(); + assert.strictEqual(client.stream, undefined); + assert.strictEqual(client.ready, true); + assert.strictEqual(client.connected, true); + client.end = function () {}; + assert(create_stream_string !== String(redis.RedisClient.prototype.create_stream)); + redis.RedisClient.prototype.create_stream = temp; + assert(create_stream_string === String(redis.RedisClient.prototype.create_stream)); }); it("throws on strange connection info", function () { From ac9ff9a3b14497c76d49866d76c6a86058130e60 Mon Sep 17 00:00:00 2001 From: Ruben Bridgewater Date: Mon, 23 Nov 2015 11:17:05 +0100 Subject: [PATCH 0493/1748] Refactor js parser Fix tests to work with Node.js 0.10 Improve average use case speed by up to 20% Fix some small js parser issues --- README.md | 70 +++++++++++------------ index.js | 8 +-- lib/parsers/javascript.js | 116 ++++++++++++++++---------------------- test/parser.spec.js | 108 +++++++++++++++++++++++++++-------- 4 files changed, 172 insertions(+), 130 deletions(-) diff --git a/README.md b/README.md index cd728c8f023..df7519c143c 100644 --- a/README.md +++ b/README.md @@ -6,7 +6,7 @@ redis - a node.js redis client [![Windows Tests](https://img.shields.io/appveyor/ci/BridgeAR/node-redis/master.svg?label=Windows%20Tests)](https://ci.appveyor.com/project/BridgeAR/node-redis/branch/master) [![Gitter](https://badges.gitter.im/Join%20Chat.svg)](https://gitter.im/NodeRedis/node_redis?utm_source=badge&utm_medium=badge&utm_campaign=pr-badge) -This is a complete and feature rich Redis client for node.js. It supports all Redis commands and focuses on performance. +This is a complete and feature rich Redis client for node.js. It supports all Redis commands and focuses on high performance. Install with: @@ -646,40 +646,40 @@ hiredis parser (Lenovo T450s i7-5600U): ``` Client count: 1, node version: 4.2.2, server version: 3.0.3, parser: hiredis - PING, 1/1 min/max/avg/p95: 0/ 3/ 0.02/ 0.00 2501ms total, 39862.85 ops/sec - PING, batch 50/1 min/max/avg/p95: 0/ 2/ 0.10/ 1.00 2501ms total, 491223.51 ops/sec - SET 4B str, 1/1 min/max/avg/p95: 0/ 3/ 0.03/ 0.00 2501ms total, 36387.45 ops/sec - SET 4B str, batch 50/1 min/max/avg/p95: 0/ 3/ 0.14/ 1.00 2501ms total, 346381.45 ops/sec - SET 4B buf, 1/1 min/max/avg/p95: 0/ 2/ 0.04/ 0.00 2501ms total, 24395.84 ops/sec - SET 4B buf, batch 50/1 min/max/avg/p95: 0/ 2/ 0.32/ 1.00 2501ms total, 156457.42 ops/sec - GET 4B str, 1/1 min/max/avg/p95: 0/ 3/ 0.03/ 0.00 2501ms total, 36906.44 ops/sec - GET 4B str, batch 50/1 min/max/avg/p95: 0/ 3/ 0.12/ 1.00 2501ms total, 425729.71 ops/sec - GET 4B buf, 1/1 min/max/avg/p95: 0/ 2/ 0.03/ 0.00 2501ms total, 36221.91 ops/sec - GET 4B buf, batch 50/1 min/max/avg/p95: 0/ 2/ 0.11/ 1.00 2501ms total, 430407.84 ops/sec - SET 4KiB str, 1/1 min/max/avg/p95: 0/ 3/ 0.03/ 0.00 2501ms total, 30951.22 ops/sec - SET 4KiB str, batch 50/1 min/max/avg/p95: 0/ 2/ 0.33/ 1.00 2501ms total, 150299.88 ops/sec - SET 4KiB buf, 1/1 min/max/avg/p95: 0/ 2/ 0.04/ 1.00 2501ms total, 23919.63 ops/sec - SET 4KiB buf, batch 50/1 min/max/avg/p95: 0/ 2/ 0.36/ 1.00 2501ms total, 139204.32 ops/sec - GET 4KiB str, 1/1 min/max/avg/p95: 0/ 2/ 0.03/ 0.00 2501ms total, 32739.30 ops/sec - GET 4KiB str, batch 50/1 min/max/avg/p95: 0/ 2/ 0.32/ 1.00 2501ms total, 154158.34 ops/sec - GET 4KiB buf, 1/1 min/max/avg/p95: 0/ 2/ 0.03/ 0.00 2501ms total, 34654.94 ops/sec - GET 4KiB buf, batch 50/1 min/max/avg/p95: 0/ 2/ 0.32/ 1.00 2501ms total, 153758.50 ops/sec - INCR, 1/1 min/max/avg/p95: 0/ 2/ 0.03/ 0.00 2501ms total, 37530.19 ops/sec - INCR, batch 50/1 min/max/avg/p95: 0/ 3/ 0.12/ 1.00 2501ms total, 415993.60 ops/sec - LPUSH, 1/1 min/max/avg/p95: 0/ 1/ 0.03/ 0.00 2501ms total, 37409.04 ops/sec - LPUSH, batch 50/1 min/max/avg/p95: 0/ 2/ 0.14/ 1.00 2501ms total, 354778.09 ops/sec - LRANGE 10, 1/1 min/max/avg/p95: 0/ 3/ 0.03/ 0.00 2501ms total, 31768.49 ops/sec - LRANGE 10, batch 50/1 min/max/avg/p95: 0/ 3/ 0.33/ 1.00 2501ms total, 151379.45 ops/sec - LRANGE 100, 1/1 min/max/avg/p95: 0/ 2/ 0.06/ 1.00 2501ms total, 16801.68 ops/sec - LRANGE 100, batch 50/1 min/max/avg/p95: 2/ 4/ 2.07/ 3.00 2501ms total, 24150.34 ops/sec - SET 4MiB str, 1/1 min/max/avg/p95: 1/ 5/ 1.96/ 2.00 2501ms total, 510.20 ops/sec - SET 4MiB str, batch 20/1 min/max/avg/p95: 83/ 108/ 94.44/ 106.40 2550ms total, 211.76 ops/sec - SET 4MiB buf, 1/1 min/max/avg/p95: 1/ 7/ 2.06/ 3.00 2501ms total, 484.21 ops/sec - SET 4MiB buf, batch 20/1 min/max/avg/p95: 38/ 48/ 40.90/ 46.00 2536ms total, 488.96 ops/sec - GET 4MiB str, 1/1 min/max/avg/p95: 3/ 13/ 5.20/ 9.00 2503ms total, 192.17 ops/sec - GET 4MiB str, batch 20/1 min/max/avg/p95: 74/ 105/ 87.24/ 104.00 2530ms total, 229.25 ops/sec - GET 4MiB buf, 1/1 min/max/avg/p95: 3/ 11/ 5.01/ 9.00 2501ms total, 199.12 ops/sec - GET 4MiB buf, batch 20/1 min/max/avg/p95: 78/ 93/ 84.23/ 91.90 2528ms total, 237.34 ops/sec + PING, 1/1 min/max/avg/p95: 0/ 2/ 0.02/ 0.00 2501ms total, 47503.80 ops/sec + PING, batch 50/1 min/max/avg/p95: 0/ 2/ 0.09/ 1.00 2501ms total, 529668.13 ops/sec + SET 4B str, 1/1 min/max/avg/p95: 0/ 2/ 0.02/ 0.00 2501ms total, 41900.04 ops/sec + SET 4B str, batch 50/1 min/max/avg/p95: 0/ 2/ 0.14/ 1.00 2501ms total, 354658.14 ops/sec + SET 4B buf, 1/1 min/max/avg/p95: 0/ 4/ 0.04/ 0.00 2501ms total, 23499.00 ops/sec + SET 4B buf, batch 50/1 min/max/avg/p95: 0/ 2/ 0.31/ 1.00 2501ms total, 159836.07 ops/sec + GET 4B str, 1/1 min/max/avg/p95: 0/ 4/ 0.02/ 0.00 2501ms total, 43489.80 ops/sec + GET 4B str, batch 50/1 min/max/avg/p95: 0/ 2/ 0.11/ 1.00 2501ms total, 444202.32 ops/sec + GET 4B buf, 1/1 min/max/avg/p95: 0/ 3/ 0.02/ 0.00 2501ms total, 38561.38 ops/sec + GET 4B buf, batch 50/1 min/max/avg/p95: 0/ 2/ 0.11/ 1.00 2501ms total, 452139.14 ops/sec + SET 4KiB str, 1/1 min/max/avg/p95: 0/ 2/ 0.03/ 0.00 2501ms total, 32990.80 ops/sec + SET 4KiB str, batch 50/1 min/max/avg/p95: 0/ 2/ 0.34/ 1.00 2501ms total, 146161.54 ops/sec + SET 4KiB buf, 1/1 min/max/avg/p95: 0/ 1/ 0.04/ 0.00 2501ms total, 23294.28 ops/sec + SET 4KiB buf, batch 50/1 min/max/avg/p95: 0/ 2/ 0.36/ 1.00 2501ms total, 137584.97 ops/sec + GET 4KiB str, 1/1 min/max/avg/p95: 0/ 2/ 0.03/ 0.00 2501ms total, 36350.66 ops/sec + GET 4KiB str, batch 50/1 min/max/avg/p95: 0/ 2/ 0.32/ 1.00 2501ms total, 155157.94 ops/sec + GET 4KiB buf, 1/1 min/max/avg/p95: 0/ 4/ 0.02/ 0.00 2501ms total, 39776.49 ops/sec + GET 4KiB buf, batch 50/1 min/max/avg/p95: 0/ 2/ 0.32/ 1.00 2501ms total, 155457.82 ops/sec + INCR, 1/1 min/max/avg/p95: 0/ 3/ 0.02/ 0.00 2501ms total, 43972.41 ops/sec + INCR, batch 50/1 min/max/avg/p95: 0/ 1/ 0.12/ 1.00 2501ms total, 425809.68 ops/sec + LPUSH, 1/1 min/max/avg/p95: 0/ 2/ 0.02/ 0.00 2501ms total, 38998.40 ops/sec + LPUSH, batch 50/1 min/max/avg/p95: 0/ 4/ 0.14/ 1.00 2501ms total, 365013.99 ops/sec + LRANGE 10, 1/1 min/max/avg/p95: 0/ 2/ 0.03/ 0.00 2501ms total, 31879.25 ops/sec + LRANGE 10, batch 50/1 min/max/avg/p95: 0/ 1/ 0.32/ 1.00 2501ms total, 153698.52 ops/sec + LRANGE 100, 1/1 min/max/avg/p95: 0/ 4/ 0.06/ 0.00 2501ms total, 16676.13 ops/sec + LRANGE 100, batch 50/1 min/max/avg/p95: 1/ 6/ 2.03/ 2.00 2502ms total, 24520.38 ops/sec + SET 4MiB str, 1/1 min/max/avg/p95: 1/ 6/ 2.11/ 3.00 2502ms total, 472.82 ops/sec + SET 4MiB str, batch 20/1 min/max/avg/p95: 85/ 112/ 94.93/ 109.60 2563ms total, 210.69 ops/sec + SET 4MiB buf, 1/1 min/max/avg/p95: 1/ 8/ 2.02/ 3.00 2502ms total, 490.01 ops/sec + SET 4MiB buf, batch 20/1 min/max/avg/p95: 37/ 52/ 39.48/ 46.75 2528ms total, 506.33 ops/sec + GET 4MiB str, 1/1 min/max/avg/p95: 3/ 13/ 5.26/ 9.00 2504ms total, 190.10 ops/sec + GET 4MiB str, batch 20/1 min/max/avg/p95: 70/ 106/ 89.36/ 103.75 2503ms total, 223.73 ops/sec + GET 4MiB buf, 1/1 min/max/avg/p95: 3/ 11/ 5.04/ 8.15 2502ms total, 198.24 ops/sec + GET 4MiB buf, batch 20/1 min/max/avg/p95: 70/ 105/ 88.07/ 103.00 2554ms total, 227.09 ops/sec ``` The hiredis and js parser should most of the time be on the same level. But if you use Redis for big SUNION/SINTER/LRANGE/ZRANGE hiredis is significantly faster. diff --git a/index.js b/index.js index 422bc41965e..2212f1b1436 100644 --- a/index.js +++ b/index.js @@ -294,14 +294,10 @@ RedisClient.prototype.init_parser = function () { // Important: Only send results / errors async. // That way the result / error won't stay in a try catch block and catch user things this.reply_parser.send_error = function (data) { - process.nextTick(function() { - self.return_error(data); - }); + self.return_error(data); }; this.reply_parser.send_reply = function (data) { - process.nextTick(function() { - self.return_reply(data); - }); + self.return_reply(data); }; }; diff --git a/lib/parsers/javascript.js b/lib/parsers/javascript.js index 7decd0447b2..bdb4a9f8bb6 100644 --- a/lib/parsers/javascript.js +++ b/lib/parsers/javascript.js @@ -9,6 +9,8 @@ function JavascriptReplyParser() { this._big_offset = 0; this._chunks_size = 0; this._buffers = []; + this._type = 0; + this._protocol_error = false; } function IncompleteReadBuffer(message) { @@ -21,87 +23,73 @@ JavascriptReplyParser.prototype._parseResult = function (type) { var start = 0, end = 0, offset = 0, - packetHeader = 0; + packetHeader = 0, + res, + reply; if (type === 43 || type === 58 || type === 45) { // + or : or - - // up to the delimiter + // Up to the delimiter end = this._packetEndOffset(); start = this._offset; - - // include the delimiter + // Include the delimiter this._offset = end + 2; if (type === 43) { return this._buffer.slice(start, end); } else if (type === 58) { - // return the coerced numeric value + // Return the coerced numeric value return +this._buffer.toString('ascii', start, end); } return new Error(this._buffer.toString('utf-8', start, end)); } else if (type === 36) { // $ packetHeader = this.parseHeader(); - // packets with a size of -1 are considered null + // Packets with a size of -1 are considered null if (packetHeader === -1) { return null; } - end = this._offset + packetHeader; start = this._offset; - if (end > this._buffer.length) { + if (end + 2 > this._buffer.length) { this._chunks_size = this._buffer.length - this._offset - 2; this._big_offset = packetHeader; throw new IncompleteReadBuffer('Wait for more data.'); } - - // set the offset to after the delimiter + // Set the offset to after the delimiter this._offset = end + 2; return this._buffer.slice(start, end); } else if (type === 42) { // * - // set a rewind point, as the packet is larger than the buffer in memory + // Set a rewind point, as the packet is larger than the buffer in memory offset = this._offset; packetHeader = this.parseHeader(); if (packetHeader === -1) { return null; } - - if (packetHeader > this._buffer.length - this._offset) { - this._offset = offset - 1; - throw new IncompleteReadBuffer('Wait for more data.'); - } - - var reply = []; - var ntype, i, res; - + reply = []; offset = this._offset - 1; - for (i = 0; i < packetHeader; i++) { - ntype = this._buffer[this._offset++]; - - if (this._offset > this._buffer.length) { + for (var i = 0; i < packetHeader; i++) { + if (this._offset >= this._buffer.length) { throw new IncompleteReadBuffer('Wait for more data.'); } - res = this._parseResult(ntype); + res = this._parseResult(this._buffer[this._offset++]); reply.push(res); } - return reply; } else { - return null; + return void 0; } }; JavascriptReplyParser.prototype.execute = function (buffer) { - if (this._chunks_size !== 0 && this._big_offset > this._chunks_size + buffer.length) { this._buffers.push(buffer); this._chunks_size += buffer.length; return; } - if (this._buffers.length !== 0) { this._buffers.unshift(this._offset === 0 ? this._buffer : this._buffer.slice(this._offset)); this._buffers.push(buffer); @@ -115,44 +103,41 @@ JavascriptReplyParser.prototype.execute = function (buffer) { this._buffer = Buffer.concat([this._buffer.slice(this._offset), buffer]); } this._offset = 0; + this._protocol_error = true; this.run(); }; -JavascriptReplyParser.prototype.run = function (buffer) { - var type, offset = this._offset; - - while (true) { - offset = this._offset; - // at least 4 bytes: :1\r\n - if (this._buffer.length - this._offset < 4) { - break; - } +JavascriptReplyParser.prototype.try_parsing = function () { + // Set a rewind point. If a failure occurs, wait for the next execute()/append() and try again + var offset = this._offset - 1; + try { + return this._parseResult(this._type); + } catch (err) { + // Catch the error (not enough data), rewind if it's an array, + // and wait for the next packet to appear + this._offset = offset; + this._protocol_error = false; + return void 0; + } +}; - try { - type = this._buffer[this._offset++]; - - if (type === 43 || type === 58 || type === 36) { // Strings + // Integers : // Bulk strings $ - this.send_reply(this._parseResult(type)); - } else if (type === 45) { // Errors - - this.send_error(this._parseResult(type)); - } else if (type === 42) { // Arrays * - // set a rewind point. if a failure occurs, - // wait for the next execute()/append() and try again - offset = this._offset - 1; - - this.send_reply(this._parseResult(type)); - } else if (type !== 10 && type !== 13) { - // Reset the buffer so the parser can handle following commands properly - this._buffer = new Buffer(0); - var err = new Error('Protocol error, got "' + String.fromCharCode(type) + '" as reply type byte'); - this.send_error(err); - } - } catch (err) { - // catch the error (not enough data), rewind, and wait - // for the next packet to appear - this._offset = offset; - break; +JavascriptReplyParser.prototype.run = function (buffer) { + this._type = this._buffer[this._offset++]; + var reply = this.try_parsing(); + + while (reply !== undefined) { + if (this._type === 45) { // Errors - + this.send_error(reply); + } else { + this.send_reply(reply); // Strings + // Integers : // Bulk strings $ // Arrays * } + this._type = this._buffer[this._offset++]; + reply = this.try_parsing(); + } + if (this._type !== undefined && this._protocol_error === true) { + // Reset the buffer so the parser can handle following commands properly + this._buffer = new Buffer(0); + this.send_error(new Error('Protocol error, got "' + String.fromCharCode(this._type) + '" as reply type byte')); } }; @@ -161,21 +146,20 @@ JavascriptReplyParser.prototype.parseHeader = function () { value = this._buffer.toString('ascii', this._offset, end) | 0; this._offset = end + 2; - return value; }; JavascriptReplyParser.prototype._packetEndOffset = function () { - var offset = this._offset; + var offset = this._offset, + len = this._buffer.length - 1; while (this._buffer[offset] !== 0x0d && this._buffer[offset + 1] !== 0x0a) { offset++; - if (offset >= this._buffer.length) { + if (offset >= len) { throw new IncompleteReadBuffer('Did not see LF after NL reading multi bulk count (' + offset + ' => ' + this._buffer.length + ', ' + this._offset + ')'); } } - return offset; }; diff --git a/test/parser.spec.js b/test/parser.spec.js index c1444186b79..85f92f50de0 100644 --- a/test/parser.spec.js +++ b/test/parser.spec.js @@ -16,7 +16,7 @@ describe('parsers', function () { describe(Parser.name, function () { - it('handles multi-bulk reply', function (done) { + it('handles multi-bulk reply', function () { var parser = new Parser(); var reply_count = 0; function check_reply(reply) { @@ -27,33 +27,53 @@ describe('parsers', function () { parser.send_reply = check_reply; parser.execute(new Buffer('*1\r\n*1\r\n$1\r\na\r\n')); + assert.strictEqual(reply_count, 1); parser.execute(new Buffer('*1\r\n*1\r')); parser.execute(new Buffer('\n$1\r\na\r\n')); + assert.strictEqual(reply_count, 2); parser.execute(new Buffer('*1\r\n*1\r\n')); parser.execute(new Buffer('$1\r\na\r\n')); assert.equal(reply_count, 3, "check reply should have been called three times"); - return done(); }); - it('parser error', function (done) { + it('parser error', function () { var parser = new Parser(); var reply_count = 0; - function check_reply(reply) { + function check_reply (reply) { assert.strictEqual(reply.message, 'Protocol error, got "a" as reply type byte'); reply_count++; } parser.send_error = check_reply; parser.execute(new Buffer('a*1\r*1\r$1`zasd\r\na')); + assert.equal(reply_count, 1); + }); + + it('parser error v2', function () { + var parser = new Parser(); + var reply_count = 0; + var err_count = 0; + function check_reply (reply) { + reply = utils.reply_to_strings(reply); + assert.strictEqual(reply[0], 'OK'); + reply_count++; + } + function check_error (err) { + assert.strictEqual(err.message, 'Protocol error, got "b" as reply type byte'); + err_count++; + } + parser.send_error = check_error; + parser.send_reply = check_reply; - assert.equal(reply_count, 1, "check reply should have been called one time"); - return done(); + parser.execute(new Buffer('*1\r\n+OK\r\nb$1`zasd\r\na')); + assert.strictEqual(reply_count, 1); + assert.strictEqual(err_count, 1); }); - it('line breaks in the beginning of the last chunk', function (done) { + it('line breaks in the beginning of the last chunk', function () { var parser = new Parser(); var reply_count = 0; function check_reply(reply) { @@ -64,15 +84,16 @@ describe('parsers', function () { parser.send_reply = check_reply; parser.execute(new Buffer('*1\r\n*1\r\n$1\r\na')); + assert.equal(reply_count, 0); parser.execute(new Buffer('\r\n*1\r\n*1\r')); + assert.equal(reply_count, 1); parser.execute(new Buffer('\n$1\r\na\r\n*1\r\n*1\r\n$1\r\na\r\n')); assert.equal(reply_count, 3, "check reply should have been called three times"); - return done(); }); - it('multiple chunks in a bulk string', function (done) { + it('multiple chunks in a bulk string', function () { var parser = new Parser(); var reply_count = 0; function check_reply(reply) { @@ -107,21 +128,27 @@ describe('parsers', function () { parser.execute(new Buffer('\n')); assert.equal(reply_count, 4, "check reply should have been called three times"); - return done(); }); - it('multiple chunks with arrays different types', function (done) { + it('multiple chunks with arrays different types', function () { var parser = new Parser(); var reply_count = 0; + var predefined_data = [ + 'abcdefghijabcdefghijabcdefghijabcdefghijabcdefghijabcdefghijabcdefghijabcdefghijabcdefghijabcdefghij', + 'test', + 100, + new Error('Error message'), + ['The force awakens'] + ]; function check_reply(reply) { reply = utils.reply_to_strings(reply); - assert.deepStrictEqual(reply, [ - 'abcdefghijabcdefghijabcdefghijabcdefghijabcdefghijabcdefghijabcdefghijabcdefghijabcdefghijabcdefghij', - 'test', - 100, - new Error('Error message'), - ['The force awakens'] - ]); + for (var i = 0; i < reply.length; i++) { + if (i < 3) { + assert.strictEqual(reply[i], predefined_data[i]); + } else { + assert.deepEqual(reply[i], predefined_data[i]); + } + } reply_count++; } parser.send_reply = check_reply; @@ -137,10 +164,9 @@ describe('parsers', function () { assert.strictEqual(reply_count, 0); parser.execute(new Buffer(' awakens\r\n$5')); assert.strictEqual(reply_count, 1); - return done(); }); - it('return normal errors', function (done) { + it('return normal errors', function () { var parser = new Parser(); var reply_count = 0; function check_reply(reply) { @@ -154,10 +180,9 @@ describe('parsers', function () { assert.strictEqual(reply_count, 1); parser.execute(new Buffer(' awakens\r\n$5')); assert.strictEqual(reply_count, 1); - return done(); }); - it('return null for empty arrays and empty bulk strings', function (done) { + it('return null for empty arrays and empty bulk strings', function () { var parser = new Parser(); var reply_count = 0; function check_reply(reply) { @@ -172,7 +197,44 @@ describe('parsers', function () { assert.strictEqual(reply_count, 1); parser.execute(new Buffer('\r\n$-')); assert.strictEqual(reply_count, 2); - return done(); + }); + + it('return value even if all chunks are only 1 character long', function () { + var parser = new Parser(); + var reply_count = 0; + function check_reply(reply) { + assert.equal(reply, 1); + reply_count++; + } + parser.send_reply = check_reply; + + parser.execute(new Buffer(':')); + assert.strictEqual(reply_count, 0); + parser.execute(new Buffer('1')); + assert.strictEqual(reply_count, 0); + parser.execute(new Buffer('\r')); + assert.strictEqual(reply_count, 0); + parser.execute(new Buffer('\n')); + assert.strictEqual(reply_count, 1); + }); + + it('do not return before \\r\\n', function () { + var parser = new Parser(); + var reply_count = 0; + function check_reply(reply) { + assert.equal(reply, 1); + reply_count++; + } + parser.send_reply = check_reply; + + parser.execute(new Buffer(':1\r\n:')); + assert.strictEqual(reply_count, 1); + parser.execute(new Buffer('1')); + assert.strictEqual(reply_count, 1); + parser.execute(new Buffer('\r')); + assert.strictEqual(reply_count, 1); + parser.execute(new Buffer('\n')); + assert.strictEqual(reply_count, 2); }); }); }); From 6711c94d1b682e2a4664515ac6f39e68e0070aa0 Mon Sep 17 00:00:00 2001 From: Ruben Bridgewater Date: Mon, 23 Nov 2015 11:20:20 +0100 Subject: [PATCH 0494/1748] Add duplicate function to duplicate the current client instance Fixes #919 --- README.md | 4 ++++ index.js | 9 +++++++++ test/node_redis.spec.js | 39 +++++++++++++++++++++++++++++++++++++++ 3 files changed, 52 insertions(+) diff --git a/README.md b/README.md index df7519c143c..243942683bf 100644 --- a/README.md +++ b/README.md @@ -572,6 +572,10 @@ the second word as first parameter: client.multi().script('load', 'return 1').exec(...); client.multi([['script', 'load', 'return 1']]).exec(...); +## client.duplicate([options]) + +Duplicate all current options and return a new redisClient instance. All options passed to the duplicate function are going to replace the original option. + ## client.send_command(command_name[, [args][, callback]]) Used internally to send commands to Redis. Nearly all Redis commands have been added to the `client` object. diff --git a/index.js b/index.js index 2212f1b1436..c41f0c82a2b 100644 --- a/index.js +++ b/index.js @@ -135,6 +135,15 @@ RedisClient.prototype.create_stream = function () { RedisClient.prototype.cork = noop; RedisClient.prototype.uncork = noop; +RedisClient.prototype.duplicate = function (options) { + var existing_options = clone(this.options); + options = clone(options); + for (var elem in options) { // jshint ignore: line + existing_options[elem] = options[elem]; + } + return new RedisClient(existing_options); +}; + RedisClient.prototype.initialize_retry_vars = function () { this.retry_timer = null; this.retry_totaltime = 0; diff --git a/test/node_redis.spec.js b/test/node_redis.spec.js index ca13878597c..39cabc8118c 100644 --- a/test/node_redis.spec.js +++ b/test/node_redis.spec.js @@ -42,6 +42,45 @@ describe("The node_redis client", function () { }); }); + describe('duplicate', function () { + it('check if all options got copied properly', function(done) { + var client2 = client.duplicate(); + assert(client.connected); + assert(!client2.connected); + for (var elem in client.options) { + if (client.options.hasOwnProperty(elem)) { + assert.strictEqual(client2.options[elem], client.options[elem]); + } + } + client2.on('ready', function () { + client2.end(true); + done(); + }); + }); + + it('check if all new options replaced the old ones', function(done) { + var client2 = client.duplicate({ + no_ready_check: true + }); + assert(client.connected); + assert(!client2.connected); + assert.strictEqual(client.options.no_ready_check, undefined); + assert.strictEqual(client2.options.no_ready_check, true); + assert.notDeepEqual(client.options, client2.options); + for (var elem in client.options) { + if (client.options.hasOwnProperty(elem)) { + if (elem !== 'no_ready_check') { + assert.strictEqual(client2.options[elem], client.options[elem]); + } + } + } + client2.on('ready', function () { + client2.end(true); + done(); + }); + }); + }); + describe('big data', function () { // Check if the fast mode for big strings is working correct From f82fb6cf421d6266393c77332a6a6feb2db2e518 Mon Sep 17 00:00:00 2001 From: Ruben Bridgewater Date: Mon, 23 Nov 2015 13:22:07 +0100 Subject: [PATCH 0495/1748] Explicitly install hiredis on appveyor and only use start / end clients if needed --- appveyor.yml | 3 ++- test/commands/client.spec.js | 22 +++++++++++++--------- 2 files changed, 15 insertions(+), 10 deletions(-) diff --git a/appveyor.yml b/appveyor.yml index 78bb63bac2b..33b6485d539 100644 --- a/appveyor.yml +++ b/appveyor.yml @@ -24,7 +24,8 @@ install: # Get the latest stable version of Node 0.STABLE.latest - ps: Install-Product node $env:nodejs_version # Typical npm stuff. Use msvs 2013 for the hiredis parser - - npm install --msvs_version=2013 + - npm install hiredis --msvs_version=2013 + - npm install # Post-install test scripts. test_script: diff --git a/test/commands/client.spec.js b/test/commands/client.spec.js index 49497b9973f..0d7f4226739 100644 --- a/test/commands/client.spec.js +++ b/test/commands/client.spec.js @@ -11,7 +11,7 @@ describe("The 'client' method", function () { var pattern = /addr=/; describe("using " + parser + " and " + ip, function () { - var client, client2; + var client; beforeEach(function (done) { client = redis.createClient.apply(redis.createClient, args); @@ -20,16 +20,8 @@ describe("The 'client' method", function () { }); }); - beforeEach(function (done) { - client2 = redis.createClient.apply(redis.createClient, args); - client2.once("ready", function () { - done(); - }); - }); - afterEach(function () { client.end(true); - client2.end(true); }); describe('list', function () { @@ -62,6 +54,18 @@ describe("The 'client' method", function () { }); describe('setname / getname', function () { + var client2; + + beforeEach(function (done) { + client2 = redis.createClient.apply(redis.createClient, args); + client2.once("ready", function () { + done(); + }); + }); + + afterEach(function () { + client2.end(true); + }); it('sets the name', function (done) { helper.serverVersionAtLeast.call(this, client, [2, 6, 9]); From 9ca2d1ddaa31faa52c0fd8578882fd6ad9b4bb3f Mon Sep 17 00:00:00 2001 From: Ruben Bridgewater Date: Mon, 23 Nov 2015 14:26:29 +0100 Subject: [PATCH 0496/1748] Fix test on appveyor Somehow the calls do not seem to be handled sync on appveyor in this test --- appveyor.yml | 2 +- test/commands/client.spec.js | 8 ++++++-- 2 files changed, 7 insertions(+), 3 deletions(-) diff --git a/appveyor.yml b/appveyor.yml index 33b6485d539..d2b21d51a55 100644 --- a/appveyor.yml +++ b/appveyor.yml @@ -24,8 +24,8 @@ install: # Get the latest stable version of Node 0.STABLE.latest - ps: Install-Product node $env:nodejs_version # Typical npm stuff. Use msvs 2013 for the hiredis parser - - npm install hiredis --msvs_version=2013 - npm install + - npm install hiredis --msvs_version=2013 # Post-install test scripts. test_script: diff --git a/test/commands/client.spec.js b/test/commands/client.spec.js index 0d7f4226739..db7a539ccb1 100644 --- a/test/commands/client.spec.js +++ b/test/commands/client.spec.js @@ -67,18 +67,22 @@ describe("The 'client' method", function () { client2.end(true); }); - it('sets the name', function (done) { helper.serverVersionAtLeast.call(this, client, [2, 6, 9]); + // The querys are auto pipelined and the response is a response to all querys of one client + // per chunk. So the execution order is only garanteed on each client + var end = helper.callFuncAfter(done, 2); + client.client("setname", "RUTH", helper.isString('OK')); client2.client("setname", "RENEE", helper.isString('OK')); client2.client("setname", "MARTIN", helper.isString('OK')); client2.client("getname", function(err, res) { assert.equal(res, 'MARTIN'); + end(); }); client.client("getname", function(err, res) { assert.equal(res, 'RUTH'); - done(); + end(); }); }); From b5fe82255359d0705168740404014a631217cb58 Mon Sep 17 00:00:00 2001 From: Ruben Bridgewater Date: Mon, 23 Nov 2015 14:27:10 +0100 Subject: [PATCH 0497/1748] Add selected_db to the new redis instance if using client.duplicate --- index.js | 4 +++- test/node_redis.spec.js | 2 ++ 2 files changed, 5 insertions(+), 1 deletion(-) diff --git a/index.js b/index.js index c41f0c82a2b..b3c5669b46e 100644 --- a/index.js +++ b/index.js @@ -141,7 +141,9 @@ RedisClient.prototype.duplicate = function (options) { for (var elem in options) { // jshint ignore: line existing_options[elem] = options[elem]; } - return new RedisClient(existing_options); + var client = new RedisClient(existing_options); + client.selected_db = this.selected_db; + return client; }; RedisClient.prototype.initialize_retry_vars = function () { diff --git a/test/node_redis.spec.js b/test/node_redis.spec.js index 39cabc8118c..547dbc62519 100644 --- a/test/node_redis.spec.js +++ b/test/node_redis.spec.js @@ -44,7 +44,9 @@ describe("The node_redis client", function () { describe('duplicate', function () { it('check if all options got copied properly', function(done) { + client.selected_db = 2; var client2 = client.duplicate(); + assert.strictEqual(client2.selected_db, 2); assert(client.connected); assert(!client2.connected); for (var elem in client.options) { From 918882f0bf883ebae54cc4fbb4a7b4782423c327 Mon Sep 17 00:00:00 2001 From: Ruben Bridgewater Date: Mon, 23 Nov 2015 14:38:33 +0100 Subject: [PATCH 0498/1748] Hotfix --- test/commands/client.spec.js | 1 + 1 file changed, 1 insertion(+) diff --git a/test/commands/client.spec.js b/test/commands/client.spec.js index db7a539ccb1..e26df12ec47 100644 --- a/test/commands/client.spec.js +++ b/test/commands/client.spec.js @@ -67,6 +67,7 @@ describe("The 'client' method", function () { client2.end(true); }); + it('sets the name', function (done) { helper.serverVersionAtLeast.call(this, client, [2, 6, 9]); // The querys are auto pipelined and the response is a response to all querys of one client From eae5596a3cfc89633f2ac50badd8900364c9fc55 Mon Sep 17 00:00:00 2001 From: Paddy Byers Date: Mon, 19 Oct 2015 17:16:41 +0100 Subject: [PATCH 0499/1748] Add support for TLS connections --- index.js | 26 +++++++++++++++++++++++--- test/helper.js | 2 +- 2 files changed, 24 insertions(+), 4 deletions(-) diff --git a/index.js b/index.js index b3c5669b46e..0cd1cd39ccb 100644 --- a/index.js +++ b/index.js @@ -1,6 +1,7 @@ 'use strict'; var net = require('net'); +var tls = require('tls'); var URL = require('url'); var util = require('util'); var utils = require('./lib/utils'); @@ -46,7 +47,10 @@ function RedisClient (options) { cnx_options.family = (!options.family && net.isIP(cnx_options.host)) || (options.family === 'IPv6' ? 6 : 4); this.address = cnx_options.host + ':' + cnx_options.port; } - this.connection_option = cnx_options; + for (var tls_option in options.tls) { // jshint ignore: line + cnx_options[tls_option] = options.tls[tls_option]; + } + this.connection_options = cnx_options; this.connection_id = ++connection_id; this.connected = false; this.ready = false; @@ -95,7 +99,18 @@ util.inherits(RedisClient, events.EventEmitter); // Attention: the function name "create_stream" should not be changed, as other libraries need this to mock the stream (e.g. fakeredis) RedisClient.prototype.create_stream = function () { var self = this; - this.stream = net.createConnection(this.connection_option); + + // On a reconnect destroy the former stream and retry + if (this.stream) { + this.stream.removeAllListeners(); + this.stream.destroy(); + } + + if (this.options.tls) { + this.stream = tls.connect(this.connection_options); + } else { + this.stream = net.createConnection(this.connection_options); + } if (this.options.connect_timeout) { this.stream.setTimeout(this.connect_timeout, function () { @@ -104,7 +119,8 @@ RedisClient.prototype.create_stream = function () { }); } - this.stream.once('connect', function () { + var connect_event = this.options.tls ? "secureConnect" : "connect"; + this.stream.on(connect_event, function () { this.removeAllListeners("timeout"); self.on_connect(); }); @@ -119,6 +135,10 @@ RedisClient.prototype.create_stream = function () { self.on_error(err); }); + this.stream.on('clientError', function (err) { + self.on_error(err); + }); + this.stream.once('close', function () { self.connection_gone('close'); }); diff --git a/test/helper.js b/test/helper.js index a5a105137d7..ae33c88990e 100644 --- a/test/helper.js +++ b/test/helper.js @@ -163,7 +163,7 @@ module.exports = { }, killConnection: function (client) { // Change the connection option to a non existing one and destroy the stream - client.connection_option = { + client.connection_options = { port: 65535, host: '127.0.0.1', family: 4 From 1fa9f15ae493a9362339031c67665af3d2b4c915 Mon Sep 17 00:00:00 2001 From: Paddy Byers Date: Mon, 19 Oct 2015 17:17:21 +0100 Subject: [PATCH 0500/1748] Add tests for TLS connections --- test/conf/.gitignore | 4 + test/conf/redis.js.org.cert | 19 +++ test/conf/redis.js.org.key | 27 +++++ test/conf/stunnel.conf.template | 12 ++ test/helper.js | 19 +++ test/lib/stunnel-process.js | 83 +++++++++++++ test/tls.spec.js | 201 ++++++++++++++++++++++++++++++++ 7 files changed, 365 insertions(+) create mode 100644 test/conf/.gitignore create mode 100644 test/conf/redis.js.org.cert create mode 100644 test/conf/redis.js.org.key create mode 100644 test/conf/stunnel.conf.template create mode 100644 test/lib/stunnel-process.js create mode 100644 test/tls.spec.js diff --git a/test/conf/.gitignore b/test/conf/.gitignore new file mode 100644 index 00000000000..4718e642987 --- /dev/null +++ b/test/conf/.gitignore @@ -0,0 +1,4 @@ +stunnel.conf +stunnel.log +stunnel.pid + diff --git a/test/conf/redis.js.org.cert b/test/conf/redis.js.org.cert new file mode 100644 index 00000000000..6747b744ac3 --- /dev/null +++ b/test/conf/redis.js.org.cert @@ -0,0 +1,19 @@ +-----BEGIN CERTIFICATE----- +MIIDATCCAemgAwIBAgIJALkMmVkQOERnMA0GCSqGSIb3DQEBBQUAMBcxFTATBgNV +BAMMDHJlZGlzLmpzLm9yZzAeFw0xNTEwMTkxMjIzMjRaFw0yNTEwMTYxMjIzMjRa +MBcxFTATBgNVBAMMDHJlZGlzLmpzLm9yZzCCASIwDQYJKoZIhvcNAQEBBQADggEP +ADCCAQoCggEBAJ/DmMTJHf7kyspxI1A/JmOc+KI9vxEcN5qn7IiZuGN7ghE43Q3q +XB2GUkMAuW1POkmM5yi3SuT1UXDR/4Gk7KlbHKMs37AV6PgJXX6oX0zu12LTAT7V +5byNrYtehSo42l1188dGEMCGaaf0cDntc7A3aW0ZtzrJt+2pu31Uatl2SEJCMra6 ++v6O0c9aHMF1cArKeawGqR+jHw6vXFZQbUd05nW5nQlUA6wVt1JjlLPwBwYsWLsi +YQxMC8NqpgAIg5tULSCpKwx5isL/CeotVVGDNZ/G8R1nTrxuygPlc3Qskj57hmV4 +tZK4JJxQFi7/9ehvjAvHohKrEPeqV5XL87cCAwEAAaNQME4wHQYDVR0OBBYEFCn/ +5hB+XY4pVOnaqvrmZMxrLFjLMB8GA1UdIwQYMBaAFCn/5hB+XY4pVOnaqvrmZMxr +LFjLMAwGA1UdEwQFMAMBAf8wDQYJKoZIhvcNAQEFBQADggEBAEduPyTHpXkCVZRQ +v6p+Ug4iVeXpxGCVr34y7EDUMgmuDdqsz1SrmqeDd0VmjZT8htbWw7QBKDPEBsbi +wl606aAn01iM+oUrwbtXxid1xfZj/j6pIhQVkGu7e/8A7Pr4QOP4OMdHB7EmqkAo +d/OLHa9LdKv2UtJHD6U7oVQbdBHrRV62125GMmotpQuSkEfZM6edKNzHPlqV/zJc +2kGCw3lZC21mTrsSMIC/FQiobPnig4kAvfh0of2rK/XAntlwT8ie1v1aK+jERsfm +uzMihl6XXBdzheq6KdIlf+5STHBIIRcvBoRKr5Va7EhnO03tTzeJowtqDv47yPC6 +w4kLcP8= +-----END CERTIFICATE----- diff --git a/test/conf/redis.js.org.key b/test/conf/redis.js.org.key new file mode 100644 index 00000000000..9376fc1eee2 --- /dev/null +++ b/test/conf/redis.js.org.key @@ -0,0 +1,27 @@ +-----BEGIN RSA PRIVATE KEY----- +MIIEowIBAAKCAQEAn8OYxMkd/uTKynEjUD8mY5z4oj2/ERw3mqfsiJm4Y3uCETjd +DepcHYZSQwC5bU86SYznKLdK5PVRcNH/gaTsqVscoyzfsBXo+AldfqhfTO7XYtMB +PtXlvI2ti16FKjjaXXXzx0YQwIZpp/RwOe1zsDdpbRm3Osm37am7fVRq2XZIQkIy +trr6/o7Rz1ocwXVwCsp5rAapH6MfDq9cVlBtR3TmdbmdCVQDrBW3UmOUs/AHBixY +uyJhDEwLw2qmAAiDm1QtIKkrDHmKwv8J6i1VUYM1n8bxHWdOvG7KA+VzdCySPnuG +ZXi1krgknFAWLv/16G+MC8eiEqsQ96pXlcvztwIDAQABAoIBAGx4kLCLHCKDlGv+ +hMtnFNltKiJ9acxkLByFBsN4GwjwQk8PHIbmJ8Sj/hYf18WvlRN65zdtuxvYs4K2 +EZQkNcqGYdsoDHexaIt/UEs+ZfYF85bVTHMtJt3uE3Ycpq0UDK6H9wvFNnqAyBuQ +iuHJplJuTNYWL6Fqc8aZBwMA3crmwWTelgS+IXLH06E298+KIxbYrWSgrbcmV/Pj +Iwek4CPS0apoJnXxbZDDhAEYGOTxDNXGm+r7BaX/ePM2x1PPib2X9F2XqFV+A4T8 +Z91axKJwMrVuTrJkaLPDx9lNUskvvV6KgjZAtYRGpLQTN1AqXJZ09IoK9sNPE4rX +9fm4awECgYEAzMJkABL0UOoGJhdRf/R0aUOQMO7vYetX5SK9QXcEI04XYFieSaPm +71st+R/JlJ+LhrTrzGXvyU0tFAQaQZtwaGj/JhbptIpLlGrVf3mqSvxkNi/wzQnn +jBJrrf1ZkDiqtSy7AxGAefWblgK3R1ZU5+0a5jubDkmOltIlbULf0skCgYEAx76l ++5KhWOJPvrjNGB1a8oVXiFzoCpaVVZIhSdl0AtvkKollm5Ou+CKYpE3fKrejRXTD +zmr5bJFXT3VlmIa010cgXJ2btlFa1RiNzgretsOmMcHxLkpAu2/a0L4psHlCrWVK +fxbUW0BYEFVXBDe/4JhFw41YqohdPkFAyo5OUn8CgYBQZGYkzUxVVHzTicY66bym +85ryS217UY5x7WDHCjZ6shdlgYWsPgjWo0L6k+tuSfHbEr+dwcwSihWPzUiNx7yr +kcXTq51YgA/KluN6KEefJ1clG099AU2C5lyWtGjswgLsHULTopSBzdenXyuce53c +bXBpQq/PPTwZpSqCqoX8WQKBgGe+nsk+jGz1BoRBycyHmrAyD5e04ZR2R9PtFTsd +JYNCoIxzVoHqv8sDdRKJm6q9PKEbl4PDzg7UomuTxxPki1LxD17rQW/9a1cY7LYi +sTBuCAj5+YGYcWypGRaoXlDZeodC/+Fogx1uGw9Is+xt5EwL6tg5tt7D+uIV1Egg +h4+TAoGBAKYA/jn9v93bzPi+w1rlZrlPufRSr4k3mcHae165N/1PnjSguTFIF5DW ++1f5S+XioNyTcfx5gKI8f6wRn1j5zbB24GXgu8dXCzRHC2gzrwq2D9v1od4zP/o7 +xFxyiNGOMUJ7uW9d/nEL5Eg4CQKZEkZNmzHhuKNr8wDSr16DhXVK +-----END RSA PRIVATE KEY----- diff --git a/test/conf/stunnel.conf.template b/test/conf/stunnel.conf.template new file mode 100644 index 00000000000..24ed7593b09 --- /dev/null +++ b/test/conf/stunnel.conf.template @@ -0,0 +1,12 @@ +pid = __dirname/stunnel.pid +output = __dirname/stunnel.log +CAfile = __dirname/redis.js.org.cert +cert = __dirname/redis.js.org.cert +key = __dirname/redis.js.org.key +client = no +foreground = yes +libwrap = no +debug = 7 +[redis] +accept = 127.0.0.1:6380 +connect = 127.0.0.1:6379 diff --git a/test/helper.js b/test/helper.js index ae33c88990e..942546e9801 100644 --- a/test/helper.js +++ b/test/helper.js @@ -4,7 +4,9 @@ var assert = require("assert"); var path = require('path'); var config = require("./lib/config"); var RedisProcess = require("./lib/redis-process"); +var StunnelProcess = require("./lib/stunnel-process"); var rp; +var stunnel_process; function startRedis (conf, done) { RedisProcess.start(function (err, _rp) { @@ -13,6 +15,21 @@ function startRedis (conf, done) { }, path.resolve(__dirname, conf)); } +function startStunnel(done) { + StunnelProcess.start(function (err, _stunnel_process) { + stunnel_process = _stunnel_process; + return done(err); + }, path.resolve(__dirname, './conf')); +} + +function stopStunnel(done) { + if(stunnel_process) { + StunnelProcess.stop(stunnel_process, done); + } else { + done(); + } +} + // don't start redis every time we // include this helper file! if (!process.env.REDIS_TESTS_STARTED) { @@ -35,6 +52,8 @@ module.exports = { rp.stop(done); }, startRedis: startRedis, + stopStunnel: stopStunnel, + startStunnel: startStunnel, isNumber: function (expected, done) { return function (err, results) { assert.strictEqual(null, err, "expected " + expected + ", got error: " + err); diff --git a/test/lib/stunnel-process.js b/test/lib/stunnel-process.js new file mode 100644 index 00000000000..7318d3f0fa6 --- /dev/null +++ b/test/lib/stunnel-process.js @@ -0,0 +1,83 @@ +'use strict'; + +// helper to start and stop the stunnel process. +var spawn = require('child_process').spawn; +var EventEmitter = require('events').EventEmitter; +var fs = require('fs'); +var path = require('path'); +var util = require('util'); + +function once(cb) { + var called = false; + return function() { + if(called) return; + called = true; + cb.apply(this, arguments); + }; +} + +function StunnelProcess(conf_dir) { + EventEmitter.call(this); + + // set up an stunnel to redis; edit the conf file to include required absolute paths + var conf_file = path.resolve(conf_dir, 'stunnel.conf'); + var conf_text = fs.readFileSync(conf_file + '.template').toString().replace(/__dirname/g, conf_dir); + + fs.writeFileSync(conf_file, conf_text); + var stunnel = this.stunnel = spawn('stunnel', [conf_file]); + + // handle child process events, and failure to set up tunnel + var self = this; + this.timer = setTimeout(function() { + self.emit('error', new Error('Timeout waiting for stunnel to start')); + }, 8000); + + stunnel.on('error', function(err) { + self.clear(); + self.emit('error', err); + }); + + stunnel.on('exit', function(code) { + self.clear(); + if(code === 0) { + self.emit('stopped'); + } else { + self.emit('error', new Error('Stunnel exited unexpectedly; code = ' + code)); + } + }); + + // wait to stunnel to start + stunnel.stderr.on("data", function(data) { + if(data.toString().match(/Service.+redis.+bound/)) { + clearTimeout(this.timer); + self.emit('started'); + } + }); +} +util.inherits(StunnelProcess, EventEmitter); + +StunnelProcess.prototype.clear = function() { + this.stunnel = null; + clearTimeout(this.timer); +}; + +StunnelProcess.prototype.stop = function(done) { + if(this.stunnel) { + this.stunnel.kill(); + } +}; + +module.exports = { + start: function(done, conf_dir) { + done = once(done); + var stunnel = new StunnelProcess(conf_dir); + stunnel.once('error', done.bind(done)); + stunnel.once('started', done.bind(done, null, stunnel)); + }, + stop: function(stunnel, done) { + stunnel.removeAllListeners(); + stunnel.stop(); + stunnel.once('error', done.bind(done)); + stunnel.once('stopped', done.bind(done, null)); + } +}; diff --git a/test/tls.spec.js b/test/tls.spec.js new file mode 100644 index 00000000000..fc33c5a44ff --- /dev/null +++ b/test/tls.spec.js @@ -0,0 +1,201 @@ +'use strict'; + +var assert = require("assert"); +var config = require("./lib/config"); +var fs = require('fs'); +var helper = require('./helper'); +var path = require('path'); +var redis = config.redis; + +var tls_options = { + servername: "redis.js.org", + rejectUnauthorized: false, + ca: [ String(fs.readFileSync(path.resolve(__dirname, "./conf/redis.js.org.cert"))) ] +}; + +var tls_port = 6380; + +describe("TLS connection tests", function () { + before(function (done) { + helper.stopStunnel(function () { + helper.startStunnel(done); + }); + }); + + after(function (done) { + helper.stopStunnel(done); + }); + + helper.allTests(function(parser, ip, args) { + + describe("using " + parser + " and " + ip, function () { + + var client; + + afterEach(function () { + if (client) { + client.end(); + } + }); + + describe("on lost connection", function () { + it("emit an error after max retry attempts and do not try to reconnect afterwards", function (done) { + var max_attempts = 4; + var options = { + parser: parser, + max_attempts: max_attempts, + port: tls_port, + tls: tls_options + }; + client = redis.createClient(options); + var calls = 0; + + client.once('ready', function() { + helper.killConnection(client); + }); + + client.on("reconnecting", function (params) { + calls++; + }); + + client.on('error', function(err) { + if (/Redis connection in broken state: maximum connection attempts.*?exceeded./.test(err.message)) { + setTimeout(function () { + assert.strictEqual(calls, max_attempts - 1); + done(); + }, 500); + } + }); + }); + + it("emit an error after max retry timeout and do not try to reconnect afterwards", function (done) { + var connect_timeout = 500; // in ms + client = redis.createClient({ + parser: parser, + connect_timeout: connect_timeout, + port: tls_port, + tls: tls_options + }); + var time = 0; + + client.once('ready', function() { + helper.killConnection(client); + }); + + client.on("reconnecting", function (params) { + time += params.delay; + }); + + client.on('error', function(err) { + if (/Redis connection in broken state: connection timeout.*?exceeded./.test(err.message)) { + setTimeout(function () { + assert(time === connect_timeout); + done(); + }, 500); + } + }); + }); + + it("end connection while retry is still ongoing", function (done) { + var connect_timeout = 1000; // in ms + client = redis.createClient({ + parser: parser, + connect_timeout: connect_timeout, + port: tls_port, + tls: tls_options + }); + + client.once('ready', function() { + helper.killConnection(client); + }); + + client.on("reconnecting", function (params) { + client.end(); + setTimeout(done, 100); + }); + }); + + it("can not connect with wrong host / port in the options object", function (done) { + var options = { + host: 'somewhere', + max_attempts: 1, + port: tls_port, + tls: tls_options + }; + client = redis.createClient(options); + var end = helper.callFuncAfter(done, 2); + + client.on('error', function (err) { + assert(/CONNECTION_BROKEN|ENOTFOUND|EAI_AGAIN/.test(err.code)); + end(); + }); + + }); + }); + + describe("when not connected", function () { + + it("connect with host and port provided in the options object", function (done) { + client = redis.createClient({ + host: 'localhost', + parser: parser, + connect_timeout: 1000, + port: tls_port, + tls: tls_options + }); + + client.once('ready', function() { + done(); + }); + }); + + it("connects correctly with args", function (done) { + var args_host = args[1]; + var args_options = args[2] || {}; + args_options.tls = tls_options; + client = redis.createClient(tls_port, args_host, args_options); + client.on("error", done); + + client.once("ready", function () { + client.removeListener("error", done); + client.get("recon 1", function (err, res) { + done(err); + }); + }); + }); + + if (ip === 'IPv4') { + it('allows connecting with the redis url and no auth and options as second parameter', function (done) { + var options = { + detect_buffers: false, + magic: Math.random(), + port: tls_port, + tls: tls_options + }; + client = redis.createClient('redis://' + config.HOST[ip] + ':' + tls_port, options); + // verify connection is using TCP, not UNIX socket + assert.strictEqual(client.connection_options.host, config.HOST[ip]); + assert.strictEqual(client.connection_options.port, tls_port); + // verify passed options are in use + assert.strictEqual(client.options.magic, options.magic); + client.on("ready", function () { + return done(); + }); + }); + + it('allows connecting with the redis url and no auth and options as third parameter', function (done) { + client = redis.createClient('redis://' + config.HOST[ip] + ':' + tls_port, null, { + detect_buffers: false, + tls: tls_options + }); + client.on("ready", function () { + return done(); + }); + }); + } + + }); + + }); + }); +}); From c74107c9727d9a8a0bf5a11584ca14bc69764360 Mon Sep 17 00:00:00 2001 From: Paddy Byers Date: Mon, 19 Oct 2015 17:19:16 +0100 Subject: [PATCH 0501/1748] Add mention of tls option --- README.md | 2 ++ index.js | 3 +-- test/lib/stunnel-process.js | 6 +++--- 3 files changed, 6 insertions(+), 5 deletions(-) diff --git a/README.md b/README.md index 243942683bf..5e04e777e4c 100644 --- a/README.md +++ b/README.md @@ -212,6 +212,8 @@ limits total amount of connection tries. Setting this to 1 will prevent any reco * `family`: *IPv4*; You can force using IPv6 if you set the family to 'IPv6'. See Node.js [net](https://nodejs.org/api/net.html) or [dns](https://nodejs.org/api/dns.html) modules how to use the family type. * `disable_resubscribing`: *false*; If set to `true`, a client won't resubscribe after disconnecting * `rename_commands`: *null*; pass a object with renamed commands to use those instead of the original functions. See the [redis security topics](http://redis.io/topics/security) for more info. +* `tls`: an object containing options to pass to [tls.connect](http://nodejs.org/api/tls.html#tls_tls_connect_port_host_options_callback), +to set up a TLS connection to Redis (if, for example, it is set up to be accessible via a tunnel). ```js var redis = require("redis"), diff --git a/index.js b/index.js index 0cd1cd39ccb..65909eb16e7 100644 --- a/index.js +++ b/index.js @@ -36,13 +36,12 @@ function RedisClient (options) { // Copy the options so they are not mutated options = clone(options); events.EventEmitter.call(this); - var self = this; var cnx_options = {}; if (options.path) { cnx_options.path = options.path; this.address = options.path; } else { - cnx_options.port = options.port || default_port; + cnx_options.port = +options.port || default_port; cnx_options.host = options.host || default_host; cnx_options.family = (!options.family && net.isIP(cnx_options.host)) || (options.family === 'IPv6' ? 6 : 4); this.address = cnx_options.host + ':' + cnx_options.port; diff --git a/test/lib/stunnel-process.js b/test/lib/stunnel-process.js index 7318d3f0fa6..a28c7f4de57 100644 --- a/test/lib/stunnel-process.js +++ b/test/lib/stunnel-process.js @@ -62,9 +62,9 @@ StunnelProcess.prototype.clear = function() { }; StunnelProcess.prototype.stop = function(done) { - if(this.stunnel) { - this.stunnel.kill(); - } + if (this.stunnel) { + this.stunnel.kill(); + } }; module.exports = { From 0596480b034899dedeb79d4cb4c4cea1fcdb4d7a Mon Sep 17 00:00:00 2001 From: Paddy Byers Date: Fri, 23 Oct 2015 17:32:27 +0100 Subject: [PATCH 0502/1748] Fix a few style/whitespace errors --- test/helper.js | 2 +- test/lib/stunnel-process.js | 6 +++--- test/tls.spec.js | 2 -- 3 files changed, 4 insertions(+), 6 deletions(-) diff --git a/test/helper.js b/test/helper.js index 942546e9801..2cb66943f71 100644 --- a/test/helper.js +++ b/test/helper.js @@ -23,7 +23,7 @@ function startStunnel(done) { } function stopStunnel(done) { - if(stunnel_process) { + if (stunnel_process) { StunnelProcess.stop(stunnel_process, done); } else { done(); diff --git a/test/lib/stunnel-process.js b/test/lib/stunnel-process.js index a28c7f4de57..f20bc22448c 100644 --- a/test/lib/stunnel-process.js +++ b/test/lib/stunnel-process.js @@ -10,7 +10,7 @@ var util = require('util'); function once(cb) { var called = false; return function() { - if(called) return; + if (called) return; called = true; cb.apply(this, arguments); }; @@ -39,7 +39,7 @@ function StunnelProcess(conf_dir) { stunnel.on('exit', function(code) { self.clear(); - if(code === 0) { + if (code === 0) { self.emit('stopped'); } else { self.emit('error', new Error('Stunnel exited unexpectedly; code = ' + code)); @@ -48,7 +48,7 @@ function StunnelProcess(conf_dir) { // wait to stunnel to start stunnel.stderr.on("data", function(data) { - if(data.toString().match(/Service.+redis.+bound/)) { + if (data.toString().match(/Service.+redis.+bound/)) { clearTimeout(this.timer); self.emit('started'); } diff --git a/test/tls.spec.js b/test/tls.spec.js index fc33c5a44ff..5e71446182c 100644 --- a/test/tls.spec.js +++ b/test/tls.spec.js @@ -193,9 +193,7 @@ describe("TLS connection tests", function () { }); }); } - }); - }); }); }); From b91692e9282770136c5ba8db24b0d9e197624d4d Mon Sep 17 00:00:00 2001 From: Ruben Bridgewater Date: Sat, 7 Nov 2015 14:37:35 +0100 Subject: [PATCH 0503/1748] Skip tls tests on windows and stunnel This will also remove the libwrap option to work on arch --- .gitignore | 2 ++ .travis.yml | 2 ++ index.js | 8 ++++++-- test/conf/stunnel.conf.template | 1 - test/connection.spec.js | 8 ++++---- test/tls.spec.js | 28 +++++++++++++++++++++++++--- 6 files changed, 39 insertions(+), 10 deletions(-) diff --git a/.gitignore b/.gitignore index 3730c724445..6df089991f5 100644 --- a/.gitignore +++ b/.gitignore @@ -4,3 +4,5 @@ node_modules coverage *.log *.rdb +stunnel.conf +stunnel.pid \ No newline at end of file diff --git a/.travis.yml b/.travis.yml index 79274f7a1a3..e1507a7cb6e 100644 --- a/.travis.yml +++ b/.travis.yml @@ -8,6 +8,8 @@ addons: - ubuntu-toolchain-r-test packages: - g++-4.8 +env: + - TRAVIS=true node_js: - "0.10" - "0.12" diff --git a/index.js b/index.js index 65909eb16e7..9071e722217 100644 --- a/index.js +++ b/index.js @@ -46,6 +46,7 @@ function RedisClient (options) { cnx_options.family = (!options.family && net.isIP(cnx_options.host)) || (options.family === 'IPv6' ? 6 : 4); this.address = cnx_options.host + ':' + cnx_options.port; } + /* istanbul ignore next: travis does not work with stunnel atm. Therefor the tls tests are skipped on travis */ for (var tls_option in options.tls) { // jshint ignore: line cnx_options[tls_option] = options.tls[tls_option]; } @@ -91,7 +92,7 @@ function RedisClient (options) { this.options = options; // Init parser once per instance this.init_parser(); - self.create_stream(); + this.create_stream(); } util.inherits(RedisClient, events.EventEmitter); @@ -105,6 +106,7 @@ RedisClient.prototype.create_stream = function () { this.stream.destroy(); } + /* istanbul ignore if: travis does not work with stunnel atm. Therefor the tls tests are skipped on travis */ if (this.options.tls) { this.stream = tls.connect(this.connection_options); } else { @@ -118,8 +120,9 @@ RedisClient.prototype.create_stream = function () { }); } + /* istanbul ignore next: travis does not work with stunnel atm. Therefor the tls tests are skipped on travis */ var connect_event = this.options.tls ? "secureConnect" : "connect"; - this.stream.on(connect_event, function () { + this.stream.once(connect_event, function () { this.removeAllListeners("timeout"); self.on_connect(); }); @@ -134,6 +137,7 @@ RedisClient.prototype.create_stream = function () { self.on_error(err); }); + /* istanbul ignore next: travis does not work with stunnel atm. Therefor the tls tests are skipped on travis */ this.stream.on('clientError', function (err) { self.on_error(err); }); diff --git a/test/conf/stunnel.conf.template b/test/conf/stunnel.conf.template index 24ed7593b09..2f239516522 100644 --- a/test/conf/stunnel.conf.template +++ b/test/conf/stunnel.conf.template @@ -5,7 +5,6 @@ cert = __dirname/redis.js.org.cert key = __dirname/redis.js.org.key client = no foreground = yes -libwrap = no debug = 7 [redis] accept = 127.0.0.1:6380 diff --git a/test/connection.spec.js b/test/connection.spec.js index 38fb705aa33..1ce5b61698f 100644 --- a/test/connection.spec.js +++ b/test/connection.spec.js @@ -103,7 +103,7 @@ describe("connection tests", function () { max_attempts: 1 }; client = redis.createClient(options); - assert.strictEqual(client.connection_option.family, ip === 'IPv6' ? 6 : 4); + assert.strictEqual(client.connection_options.family, ip === 'IPv6' ? 6 : 4); assert.strictEqual(Object.keys(options).length, 4); var end = helper.callFuncAfter(done, 2); @@ -142,7 +142,7 @@ describe("connection tests", function () { assert(client.stream._events.timeout); }); assert.strictEqual(client.address, '192.168.74.167:6379'); - assert.strictEqual(client.connection_option.family, 4); + assert.strictEqual(client.connection_options.family, 4); var time = Date.now(); client.on("reconnecting", function (params) { @@ -162,7 +162,7 @@ describe("connection tests", function () { host: '2001:db8::ff00:42:8329' // auto detect ip v6 }); assert.strictEqual(client.address, '2001:db8::ff00:42:8329:6379'); - assert.strictEqual(client.connection_option.family, 6); + assert.strictEqual(client.connection_options.family, 6); process.nextTick(function() { assert.strictEqual(client.stream._events.timeout, undefined); }); @@ -240,7 +240,7 @@ describe("connection tests", function () { it("connects with a port only", function (done) { client = redis.createClient(6379); - assert.strictEqual(client.connection_option.family, 4); + assert.strictEqual(client.connection_options.family, 4); client.on("error", done); client.once("ready", function () { diff --git a/test/tls.spec.js b/test/tls.spec.js index 5e71446182c..8c093af1e3f 100644 --- a/test/tls.spec.js +++ b/test/tls.spec.js @@ -15,14 +15,29 @@ var tls_options = { var tls_port = 6380; +if (process.platform === 'win32') { + return; +} + +// Wait until stunnel4 is in the travis whitelist +// Check: https://github.com/travis-ci/apt-package-whitelist/issues/403 +// If this is merged, remove the travis env checks describe("TLS connection tests", function () { before(function (done) { + if (process.env.TRAVIS === 'true') { + done(); + return; + } helper.stopStunnel(function () { helper.startStunnel(done); }); }); after(function (done) { + if (process.env.TRAVIS === 'true') { + done(); + return; + } helper.stopStunnel(done); }); @@ -33,13 +48,12 @@ describe("TLS connection tests", function () { var client; afterEach(function () { - if (client) { - client.end(); - } + client.end(true); }); describe("on lost connection", function () { it("emit an error after max retry attempts and do not try to reconnect afterwards", function (done) { + if (process.env.TRAVIS === 'true') this.skip(); var max_attempts = 4; var options = { parser: parser, @@ -69,6 +83,7 @@ describe("TLS connection tests", function () { }); it("emit an error after max retry timeout and do not try to reconnect afterwards", function (done) { + if (process.env.TRAVIS === 'true') this.skip(); var connect_timeout = 500; // in ms client = redis.createClient({ parser: parser, @@ -97,6 +112,7 @@ describe("TLS connection tests", function () { }); it("end connection while retry is still ongoing", function (done) { + if (process.env.TRAVIS === 'true') this.skip(); var connect_timeout = 1000; // in ms client = redis.createClient({ parser: parser, @@ -116,6 +132,7 @@ describe("TLS connection tests", function () { }); it("can not connect with wrong host / port in the options object", function (done) { + if (process.env.TRAVIS === 'true') this.skip(); var options = { host: 'somewhere', max_attempts: 1, @@ -136,6 +153,7 @@ describe("TLS connection tests", function () { describe("when not connected", function () { it("connect with host and port provided in the options object", function (done) { + if (process.env.TRAVIS === 'true') this.skip(); client = redis.createClient({ host: 'localhost', parser: parser, @@ -150,6 +168,7 @@ describe("TLS connection tests", function () { }); it("connects correctly with args", function (done) { + if (process.env.TRAVIS === 'true') this.skip(); var args_host = args[1]; var args_options = args[2] || {}; args_options.tls = tls_options; @@ -166,6 +185,7 @@ describe("TLS connection tests", function () { if (ip === 'IPv4') { it('allows connecting with the redis url and no auth and options as second parameter', function (done) { + if (process.env.TRAVIS === 'true') this.skip(); var options = { detect_buffers: false, magic: Math.random(), @@ -176,6 +196,7 @@ describe("TLS connection tests", function () { // verify connection is using TCP, not UNIX socket assert.strictEqual(client.connection_options.host, config.HOST[ip]); assert.strictEqual(client.connection_options.port, tls_port); + assert(typeof client.stream.getCipher === 'function'); // verify passed options are in use assert.strictEqual(client.options.magic, options.magic); client.on("ready", function () { @@ -184,6 +205,7 @@ describe("TLS connection tests", function () { }); it('allows connecting with the redis url and no auth and options as third parameter', function (done) { + if (process.env.TRAVIS === 'true') this.skip(); client = redis.createClient('redis://' + config.HOST[ip] + ':' + tls_port, null, { detect_buffers: false, tls: tls_options From 32a5e1d1489228a0d1bad5e3ab93ac5fd8376a3d Mon Sep 17 00:00:00 2001 From: Ruben Bridgewater Date: Mon, 23 Nov 2015 18:38:06 +0100 Subject: [PATCH 0504/1748] Add changelog entries --- changelog.md | 9 ++++++++- test/node_redis.spec.js | 1 - 2 files changed, 8 insertions(+), 2 deletions(-) diff --git a/changelog.md b/changelog.md index 502eaa8a870..47de9acaa71 100644 --- a/changelog.md +++ b/changelog.md @@ -1,7 +1,14 @@ Changelog ========= -## v.2.x.x - xx Nov, 2015 +## v.2.4.0 - xx Nov, 2015 + +Features + +- Added `tls` option to iniate a connection to a redis server behind a TLS proxy. Thanks ([@paddybyers](https://github.com/paddybyers) +- Added a *url* option to pass the connection url with the options object ([@BridgeAR](https://github.com/BridgeAR) +- Added `client.duplicate([options])` to duplicate the current client and return a new one with the same options ([@BridgeAR](https://github.com/BridgeAR) +- Improve performance by up to 20% on almost all use cases ([@BridgeAR](https://github.com/BridgeAR) Bugfixes diff --git a/test/node_redis.spec.js b/test/node_redis.spec.js index 547dbc62519..f8d91962d86 100644 --- a/test/node_redis.spec.js +++ b/test/node_redis.spec.js @@ -30,7 +30,6 @@ describe("The node_redis client", function () { var client; afterEach(function () { - // Explicitly ignore still running commands client.end(true); }); From a8c3675218e8c7fb2cc49403296135d8ba56fe72 Mon Sep 17 00:00:00 2001 From: Ruben Bridgewater Date: Mon, 23 Nov 2015 18:38:17 +0100 Subject: [PATCH 0505/1748] Add another parser test --- test/parser.spec.js | 20 ++++++++++++++++++++ 1 file changed, 20 insertions(+) diff --git a/test/parser.spec.js b/test/parser.spec.js index 85f92f50de0..b50d37322d3 100644 --- a/test/parser.spec.js +++ b/test/parser.spec.js @@ -236,6 +236,26 @@ describe('parsers', function () { parser.execute(new Buffer('\n')); assert.strictEqual(reply_count, 2); }); + + it('return data as buffer if requested', function () { + var parser = new Parser(true); + var reply_count = 0; + function check_reply(reply) { + if (Array.isArray(reply)) { + reply = reply[0]; + } + assert(Buffer.isBuffer(reply)); + assert.strictEqual(reply.inspect(), new Buffer('test').inspect()); + reply_count++; + } + parser.send_reply = check_reply; + parser.execute(new Buffer('+test\r\n')); + assert.strictEqual(reply_count, 1); + parser.execute(new Buffer('$4\r\ntest\r\n')); + assert.strictEqual(reply_count, 2); + parser.execute(new Buffer('*1\r\n$4\r\ntest\r\n')); + assert.strictEqual(reply_count, 3); + }); }); }); }); From f877c3950c42b7ea32d7398d31b558196be2fedc Mon Sep 17 00:00:00 2001 From: Ruben Bridgewater Date: Mon, 23 Nov 2015 23:09:57 +0100 Subject: [PATCH 0506/1748] Add prefix option Fixes #323 Add key prefix tests Add changelog entry for prefix --- README.md | 1 + changelog.md | 9 +- generate_commands.js | 41 --------- index.js | 54 ++++++------ lib/commands.js | 197 ------------------------------------------- package.json | 3 +- test/prefix.spec.js | 119 ++++++++++++++++++++++++++ 7 files changed, 154 insertions(+), 270 deletions(-) delete mode 100644 generate_commands.js delete mode 100644 lib/commands.js create mode 100644 test/prefix.spec.js diff --git a/README.md b/README.md index 5e04e777e4c..4e7def98f80 100644 --- a/README.md +++ b/README.md @@ -214,6 +214,7 @@ limits total amount of connection tries. Setting this to 1 will prevent any reco * `rename_commands`: *null*; pass a object with renamed commands to use those instead of the original functions. See the [redis security topics](http://redis.io/topics/security) for more info. * `tls`: an object containing options to pass to [tls.connect](http://nodejs.org/api/tls.html#tls_tls_connect_port_host_options_callback), to set up a TLS connection to Redis (if, for example, it is set up to be accessible via a tunnel). +* `prefix`: *null*; pass a string to prefix all used keys with that string as prefix e.g. 'namespace:test' ```js var redis = require("redis"), diff --git a/changelog.md b/changelog.md index 47de9acaa71..faeaec4866f 100644 --- a/changelog.md +++ b/changelog.md @@ -5,10 +5,11 @@ Changelog Features -- Added `tls` option to iniate a connection to a redis server behind a TLS proxy. Thanks ([@paddybyers](https://github.com/paddybyers) -- Added a *url* option to pass the connection url with the options object ([@BridgeAR](https://github.com/BridgeAR) -- Added `client.duplicate([options])` to duplicate the current client and return a new one with the same options ([@BridgeAR](https://github.com/BridgeAR) -- Improve performance by up to 20% on almost all use cases ([@BridgeAR](https://github.com/BridgeAR) +- Added `tls` option to iniate a connection to a redis server behind a TLS proxy. Thanks ([@paddybyers](https://github.com/paddybyers)) +- Added `prefix` option to auto key prefix any command with the provided prefix (([@luin](https://github.com/luin) & [@BridgeAR](https://github.com/BridgeAR))) +- Added `url` option to pass the connection url with the options object ([@BridgeAR](https://github.com/BridgeAR)) +- Added `client.duplicate([options])` to duplicate the current client and return a new one with the same options ([@BridgeAR](https://github.com/BridgeAR)) +- Improve performance by up to 20% on almost all use cases ([@BridgeAR](https://github.com/BridgeAR)) Bugfixes diff --git a/generate_commands.js b/generate_commands.js deleted file mode 100644 index a9862321f6e..00000000000 --- a/generate_commands.js +++ /dev/null @@ -1,41 +0,0 @@ -'use strict'; - -var http = require("http"), - fs = require("fs"); - -function prettyCurrentTime() { - var date = new Date(); - return date.toLocaleString(); -} - -function write_file(commands, path) { - var file_contents, out_commands; - - console.log("Writing " + Object.keys(commands).length + " commands to " + path); - - file_contents = "'use strict';\n\n// This file was generated by ./generate_commands.js on " + prettyCurrentTime() + "\n"; - - out_commands = Object.keys(commands).map(function (key) { - return key.toLowerCase(); - }); - - file_contents += "module.exports = " + JSON.stringify(out_commands, null, " ") + ";\n"; - - fs.writeFile(path, file_contents); -} - -http.get({host: "redis.io", path: "/commands.json"}, function (res) { - var body = ""; - - console.log("Response from redis.io/commands.json: " + res.statusCode); - - res.on('data', function (chunk) { - body += chunk; - }); - - res.on('end', function () { - write_file(JSON.parse(body), "lib/commands.js"); - }); -}).on('error', function (e) { - console.log("Error fetching command list from redis.io: " + e.message); -}); diff --git a/index.js b/index.js index 9071e722217..c06fc9ddaa4 100644 --- a/index.js +++ b/index.js @@ -9,9 +9,7 @@ var Queue = require('double-ended-queue'); var Command = require('./lib/command'); var events = require('events'); var parsers = []; -// This static list of commands is updated from time to time. -// ./lib/commands.js can be updated with generate_commands.js -var commands = require('./lib/commands'); +var commands = require('redis-commands'); var connection_id = 0; var default_port = 6379; var default_host = '127.0.0.1'; @@ -22,7 +20,7 @@ function debug (msg) { if (exports.debug_mode) { console.error(msg); } } exports.debug_mode = /\bredis\b/i.test(process.env.NODE_DEBUG); -// hiredis might not be installed +// Hiredis might not be installed try { parsers.push(require('./lib/parsers/hiredis')); } catch (err) { @@ -189,7 +187,7 @@ RedisClient.prototype.unref = function () { } }; -// flush provided queues, erroring any items with a callback first +// Flush provided queues, erroring any items with a callback first RedisClient.prototype.flush_and_error = function (error, queue_names) { var command_obj; queue_names = queue_names || ['offline_queue', 'command_queue']; @@ -457,7 +455,7 @@ RedisClient.prototype.on_info_cmd = function (err, res) { key = 'db' + i; } - // expose info key/vals to users + // Expose info key/vals to users this.server_info = obj; if (!obj.loading || obj.loading === '0') { @@ -684,7 +682,7 @@ RedisClient.prototype.return_reply = function (reply) { } else { this.pub_sub_mode = true; } - // subscribe commands take an optional callback and also emit an event, but only the first response is included in the callback + // Subscribe commands take an optional callback and also emit an event, but only the first response is included in the callback // TODO - document this or fix it so it works in a more obvious way if (command_obj && typeof command_obj.callback === 'function') { command_obj.callback(null, reply[1]); @@ -723,6 +721,7 @@ RedisClient.prototype.send_command = function (command, args, callback) { command_str = '', buffer_args = false, big_data = false, + prefix_keys, buffer = this.options.return_buffers; if (args === undefined) { @@ -810,7 +809,14 @@ RedisClient.prototype.send_command = function (command, args, callback) { if (typeof this.options.rename_commands !== 'undefined' && this.options.rename_commands[command]) { command = this.options.rename_commands[command]; } - + if (this.options.prefix) { + prefix_keys = commands.getKeyIndexes(command, args); + i = prefix_keys.pop(); + while (i !== undefined) { + args[i] = this.options.prefix + args[i]; + i = prefix_keys.pop(); + } + } // Always use 'Multi bulk commands', but if passed any Buffer args, then do multiple writes, one for each arg. // This means that using Buffers in commands is going to be slower, so use Strings if you don't already have a Buffer. command_str = '*' + (args.length + 1) + '\r\n$' + command.length + '\r\n' + command + '\r\n'; @@ -943,23 +949,7 @@ function Multi(client, args) { } } -RedisClient.prototype.multi = RedisClient.prototype.MULTI = function (args) { - var multi = new Multi(this, args); - multi.exec = multi.EXEC = multi.exec_transaction; - return multi; -}; - -RedisClient.prototype.batch = RedisClient.prototype.BATCH = function (args) { - return new Multi(this, args); -}; - -commands.forEach(function (fullCommand) { - var command = fullCommand.split(' ')[0]; - - // Skip all full commands that have already been added instead of overwriting them over and over again - if (RedisClient.prototype[command]) { - return; - } +commands.list.forEach(function (command) { RedisClient.prototype[command.toUpperCase()] = RedisClient.prototype[command] = function (key, arg, callback) { if (Array.isArray(key)) { @@ -1006,7 +996,17 @@ commands.forEach(function (fullCommand) { }; }); -// store db in this.select_db to restore it on reconnect +RedisClient.prototype.multi = RedisClient.prototype.MULTI = function (args) { + var multi = new Multi(this, args); + multi.exec = multi.EXEC = multi.exec_transaction; + return multi; +}; + +RedisClient.prototype.batch = RedisClient.prototype.BATCH = function (args) { + return new Multi(this, args); +}; + +// Store db in this.select_db to restore it on reconnect RedisClient.prototype.select = RedisClient.prototype.SELECT = function (db, callback) { var self = this; return this.send_command('select', [db], function (err, res) { @@ -1138,7 +1138,7 @@ Multi.prototype.exec_transaction = function (callback) { this._client.cork(len + 2); this.wants_buffers = new Array(len); this.send_command('multi', []); - // drain queue, callback will catch 'QUEUED' or error + // Drain queue, callback will catch 'QUEUED' or error for (var index = 0; index < len; index++) { var args = this.queue.get(index).slice(0); var command = args.shift(); diff --git a/lib/commands.js b/lib/commands.js deleted file mode 100644 index 8082841dc83..00000000000 --- a/lib/commands.js +++ /dev/null @@ -1,197 +0,0 @@ -'use strict'; - -// This file was generated by ./generate_commands.js on Thu Sep 03 2015 02:40:54 GMT+0200 (CEST) -module.exports = [ - "append", - "auth", - "bgrewriteaof", - "bgsave", - "bitcount", - "bitop", - "bitpos", - "blpop", - "brpop", - "brpoplpush", - "client kill", - "client list", - "client getname", - "client pause", - "client setname", - "cluster addslots", - "cluster count-failure-reports", - "cluster countkeysinslot", - "cluster delslots", - "cluster failover", - "cluster forget", - "cluster getkeysinslot", - "cluster info", - "cluster keyslot", - "cluster meet", - "cluster nodes", - "cluster replicate", - "cluster reset", - "cluster saveconfig", - "cluster set-config-epoch", - "cluster setslot", - "cluster slaves", - "cluster slots", - "command", - "command count", - "command getkeys", - "command info", - "config get", - "config rewrite", - "config set", - "config resetstat", - "dbsize", - "debug object", - "debug segfault", - "decr", - "decrby", - "del", - "discard", - "dump", - "echo", - "eval", - "evalsha", - "exec", - "exists", - "expire", - "expireat", - "flushall", - "flushdb", - "geoadd", - "geohash", - "geopos", - "geodist", - "georadius", - "georadiusbymember", - "get", - "getbit", - "getrange", - "getset", - "hdel", - "hexists", - "hget", - "hgetall", - "hincrby", - "hincrbyfloat", - "hkeys", - "hlen", - "hmget", - "hmset", - "hset", - "hsetnx", - "hstrlen", - "hvals", - "incr", - "incrby", - "incrbyfloat", - "info", - "keys", - "lastsave", - "lindex", - "linsert", - "llen", - "lpop", - "lpush", - "lpushx", - "lrange", - "lrem", - "lset", - "ltrim", - "mget", - "migrate", - "monitor", - "move", - "mset", - "msetnx", - "multi", - "object", - "persist", - "pexpire", - "pexpireat", - "pfadd", - "pfcount", - "pfmerge", - "ping", - "psetex", - "psubscribe", - "pubsub", - "pttl", - "publish", - "punsubscribe", - "quit", - "randomkey", - "rename", - "renamenx", - "restore", - "role", - "rpop", - "rpoplpush", - "rpush", - "rpushx", - "sadd", - "save", - "scard", - "script exists", - "script flush", - "script kill", - "script load", - "sdiff", - "sdiffstore", - "select", - "set", - "setbit", - "setex", - "setnx", - "setrange", - "shutdown", - "sinter", - "sinterstore", - "sismember", - "slaveof", - "slowlog", - "smembers", - "smove", - "sort", - "spop", - "srandmember", - "srem", - "strlen", - "subscribe", - "sunion", - "sunionstore", - "sync", - "time", - "ttl", - "type", - "unsubscribe", - "unwatch", - "wait", - "watch", - "zadd", - "zcard", - "zcount", - "zincrby", - "zinterstore", - "zlexcount", - "zrange", - "zrangebylex", - "zrevrangebylex", - "zrangebyscore", - "zrank", - "zrem", - "zremrangebylex", - "zremrangebyrank", - "zremrangebyscore", - "zrevrange", - "zrevrangebyscore", - "zrevrank", - "zscore", - "zunionstore", - "scan", - "sscan", - "hscan", - "zscan" -]; diff --git a/package.json b/package.json index 91ba58dd7bb..97aed997ed0 100644 --- a/package.json +++ b/package.json @@ -20,7 +20,8 @@ "posttest": "jshint ." }, "dependencies": { - "double-ended-queue": "^2.1.0-0" + "double-ended-queue": "^2.1.0-0", + "redis-commands": "^1.0.1" }, "engines": { "node": ">=0.10.0" diff --git a/test/prefix.spec.js b/test/prefix.spec.js new file mode 100644 index 00000000000..4c909f1af98 --- /dev/null +++ b/test/prefix.spec.js @@ -0,0 +1,119 @@ +'use strict'; + +var assert = require("assert"); +var config = require("./lib/config"); +var helper = require('./helper'); +var redis = config.redis; + +describe("prefix key names", function () { + + helper.allTests(function(parser, ip, args) { + + describe("using " + parser + " and " + ip, function () { + var client = null; + + beforeEach(function(done) { + client = redis.createClient({ + parser: parser, + prefix: 'test:prefix:' + }); + client.on('ready', function () { + client.flushdb(function (err) { + done(err); + }); + }); + }); + + afterEach(function () { + client.end(true); + }); + + it("auto prefix set / get", function (done) { + client.set('key', 'value', function(err, reply) { + assert.strictEqual(reply, 'OK'); + }); + client.get('key', function(err, reply) { + assert.strictEqual(reply, 'value'); + }); + client.getrange('key', 1, -1, function(err, reply) { + assert.strictEqual(reply, 'alue'); + assert.strictEqual(err, null); + }); + client.exists('key', function (err, res) { + assert.strictEqual(res, 1); + }); + client.exists('test:prefix:key', function (err, res) { + // The key will be prefixed itself + assert.strictEqual(res, 0); + }); + client.mset('key2', 'value2', 'key3', 'value3'); + client.keys('*', function (err, res) { + assert.strictEqual(res.length, 3); + assert(res.indexOf('test:prefix:key') !== -1); + assert(res.indexOf('test:prefix:key2') !== -1); + assert(res.indexOf('test:prefix:key3') !== -1); + done(); + }); + }); + + it("auto prefix set / get with .batch", function (done) { + var batch = client.batch(); + batch.set('key', 'value', function(err, reply) { + assert.strictEqual(reply, 'OK'); + }); + batch.get('key', function(err, reply) { + assert.strictEqual(reply, 'value'); + }); + batch.getrange('key', 1, -1, function(err, reply) { + assert.strictEqual(reply, 'alue'); + assert.strictEqual(err, null); + }); + batch.exists('key', function (err, res) { + assert.strictEqual(res, 1); + }); + batch.exists('test:prefix:key', function (err, res) { + // The key will be prefixed itself + assert.strictEqual(res, 0); + }); + batch.mset('key2', 'value2', 'key3', 'value3'); + batch.keys('*', function (err, res) { + assert.strictEqual(res.length, 3); + assert(res.indexOf('test:prefix:key') !== -1); + assert(res.indexOf('test:prefix:key2') !== -1); + assert(res.indexOf('test:prefix:key3') !== -1); + }); + batch.exec(done); + }); + + it("auto prefix set / get with .multi", function (done) { + var multi = client.multi(); + multi.set('key', 'value', function(err, reply) { + assert.strictEqual(reply, 'OK'); + }); + multi.get('key', function(err, reply) { + assert.strictEqual(reply, 'value'); + }); + multi.getrange('key', 1, -1, function(err, reply) { + assert.strictEqual(reply, 'alue'); + assert.strictEqual(err, null); + }); + multi.exists('key', function (err, res) { + assert.strictEqual(res, 1); + }); + multi.exists('test:prefix:key', function (err, res) { + // The key will be prefixed itself + assert.strictEqual(res, 0); + }); + multi.mset('key2', 'value2', 'key3', 'value3'); + multi.keys('*', function (err, res) { + assert.strictEqual(res.length, 3); + assert(res.indexOf('test:prefix:key') !== -1); + assert(res.indexOf('test:prefix:key2') !== -1); + assert(res.indexOf('test:prefix:key3') !== -1); + }); + multi.exec(done); + }); + + }); + }); +}); From d55017a01e81923ce243525657ed9a0f2f9c0bad Mon Sep 17 00:00:00 2001 From: Ruben Bridgewater Date: Mon, 23 Nov 2015 23:11:31 +0100 Subject: [PATCH 0507/1748] Use .end(true) to stricten test cases and improve coverage --- test/batch.spec.js | 2 +- test/detect_buffers.spec.js | 2 +- test/multi.spec.js | 2 +- test/node_redis.spec.js | 28 ++++++++++++++++++++++++++-- test/pubsub.spec.js | 4 ++-- test/rename.spec.js | 5 +++-- test/return_buffers.spec.js | 4 ++-- test/tls.spec.js | 2 +- 8 files changed, 37 insertions(+), 12 deletions(-) diff --git a/test/batch.spec.js b/test/batch.spec.js index 9afc7973e9d..575544fe999 100644 --- a/test/batch.spec.js +++ b/test/batch.spec.js @@ -60,7 +60,7 @@ describe("The 'batch' method", function () { }); afterEach(function () { - client.end(); + client.end(true); }); it("returns an empty array", function (done) { diff --git a/test/detect_buffers.spec.js b/test/detect_buffers.spec.js index b1decc3aabe..c8f0f134d82 100644 --- a/test/detect_buffers.spec.js +++ b/test/detect_buffers.spec.js @@ -28,7 +28,7 @@ describe("detect_buffers", function () { }); afterEach(function () { - client.end(); + client.end(true); }); describe('get', function () { diff --git a/test/multi.spec.js b/test/multi.spec.js index 25cbfa538b6..d4f0250200b 100644 --- a/test/multi.spec.js +++ b/test/multi.spec.js @@ -11,7 +11,7 @@ var client; describe("The 'multi' method", function () { afterEach(function () { - client.end(); + client.end(true); }); describe('regression test', function () { diff --git a/test/node_redis.spec.js b/test/node_redis.spec.js index f8d91962d86..03098115d9c 100644 --- a/test/node_redis.spec.js +++ b/test/node_redis.spec.js @@ -96,6 +96,30 @@ describe("The node_redis client", function () { done(); }); }); + + it('safe strings that are bigger than 30000 characters with multi', function(done) { + var str = 'foo ಠ_ಠ bar '; + while (str.length < 111111) { + str += str; + } + var called = false; + var temp = client.writeBuffers.bind(client); + assert(String(client.writeBuffers) !== String(client.writeDefault)); + client.writeBuffers = function (data) { + called = true; + // To increase write performance for strings the value is converted to a buffer + assert(String(client.writeBuffers) === String(client.writeDefault)); + temp(data); + }; + client.multi().set('foo', str).get('foo', function (err, res) { + assert.strictEqual(res, str); + }).exec(function (err, res) { + assert(called); + assert.strictEqual(res[1], str); + done(); + }); + assert(String(client.writeBuffers) !== String(client.writeDefault)); + }); }); describe("send_command", function () { @@ -135,7 +159,7 @@ describe("The node_redis client", function () { describe(".end", function () { - it('used without flush', function(done) { + it('used without flush / flush set to false', function(done) { var finished = false; var end = helper.callFuncAfter(function() { if (!finished) { @@ -513,7 +537,7 @@ describe("The node_redis client", function () { client.retry_backoff = 1; client.stream.end(); } else { - client.end(); + client.end(true); var lasted = new Date().getTime() - time; assert.ok(lasted < 100); return done(); diff --git a/test/pubsub.spec.js b/test/pubsub.spec.js index 545f85b6c87..e67b0194621 100644 --- a/test/pubsub.spec.js +++ b/test/pubsub.spec.js @@ -367,8 +367,8 @@ describe("publish/subscribe", function () { }); afterEach(function () { - sub.end(); - pub.end(); + sub.end(true); + pub.end(true); }); }); }); diff --git a/test/rename.spec.js b/test/rename.spec.js index fe4b3b50997..5ac7435138e 100644 --- a/test/rename.spec.js +++ b/test/rename.spec.js @@ -22,7 +22,8 @@ describe("rename commands", function () { rename_commands: { set: '807081f5afa96845a02816a28b7258c3', GETRANGE: '9e3102b15cf231c4e9e940f284744fe0' - } + }, + parser: parser }); client.on('ready', function () { @@ -31,7 +32,7 @@ describe("rename commands", function () { }); afterEach(function () { - client.end(); + client.end(true); }); it("allows to use renamed functions", function (done) { diff --git a/test/return_buffers.spec.js b/test/return_buffers.spec.js index a89ecbe34d8..32582843072 100644 --- a/test/return_buffers.spec.js +++ b/test/return_buffers.spec.js @@ -277,8 +277,8 @@ describe("return_buffers", function () { }); afterEach(function () { - sub.end(); - pub.end(); + sub.end(true); + pub.end(true); }); }); }); diff --git a/test/tls.spec.js b/test/tls.spec.js index 8c093af1e3f..68d12d761c4 100644 --- a/test/tls.spec.js +++ b/test/tls.spec.js @@ -126,7 +126,7 @@ describe("TLS connection tests", function () { }); client.on("reconnecting", function (params) { - client.end(); + client.end(true); setTimeout(done, 100); }); }); From d5861c349e5ab40c07c637deec6100a9f29079c9 Mon Sep 17 00:00:00 2001 From: Ruben Bridgewater Date: Mon, 23 Nov 2015 23:14:35 +0100 Subject: [PATCH 0508/1748] Add more keywords --- package.json | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/package.json b/package.json index 97aed997ed0..aff073347aa 100644 --- a/package.json +++ b/package.json @@ -6,7 +6,9 @@ "database", "redis", "transaction", - "pipelining" + "pipelining", + "performance", + "queue" ], "author": "Matt Ranney ", "license": "MIT", From 2c4e6b05226b6ae3648a098a64c455f73300ea31 Mon Sep 17 00:00:00 2001 From: Ruben Bridgewater Date: Tue, 24 Nov 2015 00:49:17 +0100 Subject: [PATCH 0509/1748] Fix travis env settings --- .travis.yml | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/.travis.yml b/.travis.yml index e1507a7cb6e..5fa3821572a 100644 --- a/.travis.yml +++ b/.travis.yml @@ -2,14 +2,13 @@ language: node_js sudo: false env: - CXX=g++-4.8 + - TRAVIS=true addons: apt: sources: - ubuntu-toolchain-r-test packages: - g++-4.8 -env: - - TRAVIS=true node_js: - "0.10" - "0.12" From 7c6f1bd5cbf570707937e44eb4bd008797e8d9d7 Mon Sep 17 00:00:00 2001 From: Ruben Bridgewater Date: Tue, 24 Nov 2015 00:52:13 +0100 Subject: [PATCH 0510/1748] Fix travis env setting --- .travis.yml | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/.travis.yml b/.travis.yml index 5fa3821572a..6fad5fb6c30 100644 --- a/.travis.yml +++ b/.travis.yml @@ -1,8 +1,7 @@ language: node_js sudo: false env: - - CXX=g++-4.8 - - TRAVIS=true + - CXX=g++-4.8 TRAVIS=true addons: apt: sources: From dc19c759460198234a38b2f9358caf6dc3b6e55d Mon Sep 17 00:00:00 2001 From: Ruben Bridgewater Date: Tue, 24 Nov 2015 19:38:32 +0100 Subject: [PATCH 0511/1748] Update appveyor redis install location And fix double brackets in the changelog --- appveyor.yml | 4 ++-- changelog.md | 2 +- 2 files changed, 3 insertions(+), 3 deletions(-) diff --git a/appveyor.yml b/appveyor.yml index d2b21d51a55..1d5c6cdbfd1 100644 --- a/appveyor.yml +++ b/appveyor.yml @@ -18,8 +18,8 @@ shallow_clone: true install: # Install the Redis - nuget install redis-64 -excludeversion - - redis-64\redis-server.exe --service-install - - redis-64\redis-server.exe --service-start + - redis-64\tools\redis-server.exe --service-install + - redis-64\tools\redis-server.exe --service-start - '@ECHO Redis Started' # Get the latest stable version of Node 0.STABLE.latest - ps: Install-Product node $env:nodejs_version diff --git a/changelog.md b/changelog.md index faeaec4866f..4acaafab5b9 100644 --- a/changelog.md +++ b/changelog.md @@ -6,7 +6,7 @@ Changelog Features - Added `tls` option to iniate a connection to a redis server behind a TLS proxy. Thanks ([@paddybyers](https://github.com/paddybyers)) -- Added `prefix` option to auto key prefix any command with the provided prefix (([@luin](https://github.com/luin) & [@BridgeAR](https://github.com/BridgeAR))) +- Added `prefix` option to auto key prefix any command with the provided prefix ([@luin](https://github.com/luin) & [@BridgeAR](https://github.com/BridgeAR)) - Added `url` option to pass the connection url with the options object ([@BridgeAR](https://github.com/BridgeAR)) - Added `client.duplicate([options])` to duplicate the current client and return a new one with the same options ([@BridgeAR](https://github.com/BridgeAR)) - Improve performance by up to 20% on almost all use cases ([@BridgeAR](https://github.com/BridgeAR)) From 4ba72703fd97cc1ab4fc9eca3cd92fdc8d359852 Mon Sep 17 00:00:00 2001 From: Ruben Bridgewater Date: Tue, 24 Nov 2015 21:01:54 +0100 Subject: [PATCH 0512/1748] Fix test for appveyor --- test/pubsub.spec.js | 8 ++++++-- 1 file changed, 6 insertions(+), 2 deletions(-) diff --git a/test/pubsub.spec.js b/test/pubsub.spec.js index e67b0194621..939ed4558dc 100644 --- a/test/pubsub.spec.js +++ b/test/pubsub.spec.js @@ -104,14 +104,18 @@ describe("publish/subscribe", function () { }); it('receives messages on subscribed channel', function (done) { + var end = helper.callFuncAfter(done, 2); sub.on("subscribe", function (chnl, count) { - pub.publish(channel, message, helper.isNumber(1)); + pub.publish(channel, message, function (err, res) { + helper.isNumber(1)(err, res); + end(); + }); }); sub.on("message", function (chnl, msg) { assert.equal(chnl, channel); assert.equal(msg, message); - return done(); + end(); }); sub.subscribe(channel); From a0cc715206722bce3daf3e96e89cf94514d12010 Mon Sep 17 00:00:00 2001 From: Ruben Bridgewater Date: Tue, 24 Nov 2015 21:23:19 +0100 Subject: [PATCH 0513/1748] Ignore still running commands for pub sub tests after the test ended --- test/pubsub.spec.js | 13 +++++++++---- 1 file changed, 9 insertions(+), 4 deletions(-) diff --git a/test/pubsub.spec.js b/test/pubsub.spec.js index 939ed4558dc..cfe1fab8e5a 100644 --- a/test/pubsub.spec.js +++ b/test/pubsub.spec.js @@ -124,14 +124,18 @@ describe("publish/subscribe", function () { it('receives messages if subscribe is called after unsubscribe', function (done) { helper.serverVersionAtLeast.bind(this)(sub, [2, 6, 11]); + var end = helper.callFuncAfter(done, 2); sub.once("subscribe", function (chnl, count) { - pub.publish(channel, message, helper.isNumber(1)); + pub.publish(channel, message, function (err, res) { + helper.isNumber(1)(err, res); + end(); + }); }); sub.on("message", function (chnl, msg) { assert.equal(chnl, channel); assert.equal(msg, message); - return done(); + end(); }); sub.subscribe(channel); @@ -371,8 +375,9 @@ describe("publish/subscribe", function () { }); afterEach(function () { - sub.end(true); - pub.end(true); + // Explicitly ignore still running commands + pub.end(false); + sub.end(false); }); }); }); From 61bd5d0202b17932c8945ac56bfc69295338f035 Mon Sep 17 00:00:00 2001 From: Ruben Bridgewater Date: Wed, 25 Nov 2015 16:10:25 +0100 Subject: [PATCH 0514/1748] Remove obsolete nested .gitignore file --- test/conf/.gitignore | 4 ---- 1 file changed, 4 deletions(-) delete mode 100644 test/conf/.gitignore diff --git a/test/conf/.gitignore b/test/conf/.gitignore deleted file mode 100644 index 4718e642987..00000000000 --- a/test/conf/.gitignore +++ /dev/null @@ -1,4 +0,0 @@ -stunnel.conf -stunnel.log -stunnel.pid - From 6405bf8444bdfa99d1d5d0f969ea52b8ff9c5775 Mon Sep 17 00:00:00 2001 From: Ruben Bridgewater Date: Wed, 25 Nov 2015 16:22:05 +0100 Subject: [PATCH 0515/1748] v.2.4.0 --- package.json | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/package.json b/package.json index aff073347aa..5c15882f6f2 100644 --- a/package.json +++ b/package.json @@ -1,6 +1,6 @@ { "name": "redis", - "version": "2.3.1", + "version": "2.4.0", "description": "Redis client library", "keywords": [ "database", From 97e699b5d2b47cfaddc1be4496302e339d01f9e3 Mon Sep 17 00:00:00 2001 From: Ruben Bridgewater Date: Wed, 25 Nov 2015 16:23:07 +0100 Subject: [PATCH 0516/1748] Add release date --- changelog.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/changelog.md b/changelog.md index 4acaafab5b9..2ef417c64d9 100644 --- a/changelog.md +++ b/changelog.md @@ -1,7 +1,7 @@ Changelog ========= -## v.2.4.0 - xx Nov, 2015 +## v.2.4.0 - 25 Nov, 2015 Features From 3f945dbf3e35f1b8f814ea42fff8eb5e35529926 Mon Sep 17 00:00:00 2001 From: Ruben Bridgewater Date: Wed, 25 Nov 2015 20:13:57 +0100 Subject: [PATCH 0517/1748] Fix parser regression Add regression test Rename big_offset to big_str_size Fixes #924 --- changelog.md | 8 +++++++- lib/parsers/javascript.js | 18 +++++++++--------- test/parser.spec.js | 17 +++++++++++++++++ 3 files changed, 33 insertions(+), 10 deletions(-) diff --git a/changelog.md b/changelog.md index 2ef417c64d9..e3298333340 100644 --- a/changelog.md +++ b/changelog.md @@ -1,11 +1,17 @@ Changelog ========= +## v.2.4.1 - 25 Nov, 2015 + +Bugfixes + +- Fixed a js parser regression introduced in 2.4.0 ([@BridgeAR](https://github.com/BridgeAR)) + ## v.2.4.0 - 25 Nov, 2015 Features -- Added `tls` option to iniate a connection to a redis server behind a TLS proxy. Thanks ([@paddybyers](https://github.com/paddybyers)) +- Added `tls` option to initiate a connection to a redis server behind a TLS proxy. Thanks ([@paddybyers](https://github.com/paddybyers)) - Added `prefix` option to auto key prefix any command with the provided prefix ([@luin](https://github.com/luin) & [@BridgeAR](https://github.com/BridgeAR)) - Added `url` option to pass the connection url with the options object ([@BridgeAR](https://github.com/BridgeAR)) - Added `client.duplicate([options])` to duplicate the current client and return a new one with the same options ([@BridgeAR](https://github.com/BridgeAR)) diff --git a/lib/parsers/javascript.js b/lib/parsers/javascript.js index bdb4a9f8bb6..909f9b789d4 100644 --- a/lib/parsers/javascript.js +++ b/lib/parsers/javascript.js @@ -6,7 +6,7 @@ function JavascriptReplyParser() { this.name = exports.name; this._buffer = new Buffer(0); this._offset = 0; - this._big_offset = 0; + this._big_str_size = 0; this._chunks_size = 0; this._buffers = []; this._type = 0; @@ -53,7 +53,7 @@ JavascriptReplyParser.prototype._parseResult = function (type) { if (end + 2 > this._buffer.length) { this._chunks_size = this._buffer.length - this._offset - 2; - this._big_offset = packetHeader; + this._big_str_size = packetHeader; throw new IncompleteReadBuffer('Wait for more data.'); } // Set the offset to after the delimiter @@ -85,17 +85,17 @@ JavascriptReplyParser.prototype._parseResult = function (type) { }; JavascriptReplyParser.prototype.execute = function (buffer) { - if (this._chunks_size !== 0 && this._big_offset > this._chunks_size + buffer.length) { - this._buffers.push(buffer); - this._chunks_size += buffer.length; - return; - } - if (this._buffers.length !== 0) { + if (this._chunks_size !== 0) { + if (this._big_str_size > this._chunks_size + buffer.length) { + this._buffers.push(buffer); + this._chunks_size += buffer.length; + return; + } this._buffers.unshift(this._offset === 0 ? this._buffer : this._buffer.slice(this._offset)); this._buffers.push(buffer); this._buffer = Buffer.concat(this._buffers); this._buffers = []; - this._big_offset = 0; + this._big_str_size = 0; this._chunks_size = 0; } else if (this._offset >= this._buffer.length) { this._buffer = buffer; diff --git a/test/parser.spec.js b/test/parser.spec.js index b50d37322d3..9b8e8462e2c 100644 --- a/test/parser.spec.js +++ b/test/parser.spec.js @@ -256,6 +256,23 @@ describe('parsers', function () { parser.execute(new Buffer('*1\r\n$4\r\ntest\r\n')); assert.strictEqual(reply_count, 3); }); + + it('regression test v.2.4.1', function () { + var parser = new Parser(true); + var reply_count = 0; + var entries = ['test test ', 'test test test test ', '1234']; + function check_reply(reply) { + assert.strictEqual(reply.toString(), entries[reply_count]); + reply_count++; + } + parser.send_reply = check_reply; + parser.execute(new Buffer('$10\r\ntest ')); + assert.strictEqual(reply_count, 0); + parser.execute(new Buffer('test \r\n$20\r\ntest test test test \r\n:1234\r')); + assert.strictEqual(reply_count, 2); + parser.execute(new Buffer('\n')); + assert.strictEqual(reply_count, 3); + }); }); }); }); From 4c27e87daf71a3449012974d4fcd17285bdda0ce Mon Sep 17 00:00:00 2001 From: Ruben Bridgewater Date: Wed, 25 Nov 2015 23:41:48 +0100 Subject: [PATCH 0518/1748] v.2.4.1 --- package.json | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/package.json b/package.json index 5c15882f6f2..7eaaa3a61c7 100644 --- a/package.json +++ b/package.json @@ -1,6 +1,6 @@ { "name": "redis", - "version": "2.4.0", + "version": "2.4.1", "description": "Redis client library", "keywords": [ "database", From 9ee610d07231a5d5a2ebf9298f6d4ced690b0147 Mon Sep 17 00:00:00 2001 From: Massimo Galbusera Date: Thu, 26 Nov 2015 19:06:05 +0100 Subject: [PATCH 0519/1748] emit event "ready" when disable_resubscribing is true --- index.js | 1 + 1 file changed, 1 insertion(+) diff --git a/index.js b/index.js index c06fc9ddaa4..0e2e66d00a5 100644 --- a/index.js +++ b/index.js @@ -382,6 +382,7 @@ RedisClient.prototype.on_ready = function () { } }; if (this.options.disable_resubscribing) { + this.emit('ready'); return; } Object.keys(this.subscription_set).forEach(function (key) { From 56861a89d22aa0ac6c2ff2bcc52fca01be96e0a5 Mon Sep 17 00:00:00 2001 From: Ruben Bridgewater Date: Thu, 26 Nov 2015 12:57:20 +0100 Subject: [PATCH 0520/1748] Escape js parser protocol error characters --- lib/parsers/javascript.js | 2 +- test/parser.spec.js | 39 +++++++++++++++++++++++++++++++++++++++ 2 files changed, 40 insertions(+), 1 deletion(-) diff --git a/lib/parsers/javascript.js b/lib/parsers/javascript.js index 909f9b789d4..22421fdfed0 100644 --- a/lib/parsers/javascript.js +++ b/lib/parsers/javascript.js @@ -137,7 +137,7 @@ JavascriptReplyParser.prototype.run = function (buffer) { if (this._type !== undefined && this._protocol_error === true) { // Reset the buffer so the parser can handle following commands properly this._buffer = new Buffer(0); - this.send_error(new Error('Protocol error, got "' + String.fromCharCode(this._type) + '" as reply type byte')); + this.send_error(new Error('Protocol error, got ' + JSON.stringify(String.fromCharCode(this._type)) + ' as reply type byte')); } }; diff --git a/test/parser.spec.js b/test/parser.spec.js index 9b8e8462e2c..7919926168b 100644 --- a/test/parser.spec.js +++ b/test/parser.spec.js @@ -73,6 +73,45 @@ describe('parsers', function () { assert.strictEqual(err_count, 1); }); + it('parser error v3', function () { + var parser = new Parser(); + var reply_count = 0; + var err_count = 0; + function check_reply (reply) { + reply = utils.reply_to_strings(reply); + assert.strictEqual(reply[0], 'OK'); + reply_count++; + } + function check_error (err) { + assert.strictEqual(err.message, 'Protocol error, got "\\n" as reply type byte'); + err_count++; + } + parser.send_error = check_error; + parser.send_reply = check_reply; + + parser.execute(new Buffer('*1\r\n+OK\r\n\n+zasd\r\n')); + assert.strictEqual(reply_count, 1); + assert.strictEqual(err_count, 1); + }); + + it('should handle \\r and \\n characters properly', function () { + // If a string contains \r or \n characters it will always be send as a bulk string + var parser = new Parser(); + var reply_count = 0; + var entries = ['foo\r', 'foo\r\nbar', '\r\nfoo', 'foo\r\n']; + function check_reply (reply) { + reply = utils.reply_to_strings(reply); + assert.strictEqual(reply, entries[reply_count]); + reply_count++; + } + parser.send_reply = check_reply; + + parser.execute(new Buffer('$4\r\nfoo\r\r\n$8\r\nfoo\r\nbar\r\n$5\r\n\r\n')); + assert.strictEqual(reply_count, 2); + parser.execute(new Buffer('foo\r\n$5\r\nfoo\r\n\r\n')); + assert.strictEqual(reply_count, 4); + }); + it('line breaks in the beginning of the last chunk', function () { var parser = new Parser(); var reply_count = 0; From 59984136fe1ae0512b7f2c706cd94c22bf9ea4ab Mon Sep 17 00:00:00 2001 From: Ruben Bridgewater Date: Fri, 27 Nov 2015 21:21:47 +0100 Subject: [PATCH 0521/1748] Update changelog and fix a test --- changelog.md | 6 ++++++ test/pubsub.spec.js | 36 +++++++++--------------------------- 2 files changed, 15 insertions(+), 27 deletions(-) diff --git a/changelog.md b/changelog.md index e3298333340..606904f891c 100644 --- a/changelog.md +++ b/changelog.md @@ -1,6 +1,12 @@ Changelog ========= +## v.2.4.2 - 27 Nov, 2015 + +Bugfixes + +- Fixed not emitting ready after reconnect with disable_resubscribing ([@maxgalbu](https://github.com/maxgalbu)) + ## v.2.4.1 - 25 Nov, 2015 Bugfixes diff --git a/test/pubsub.spec.js b/test/pubsub.spec.js index cfe1fab8e5a..b80756f00ed 100644 --- a/test/pubsub.spec.js +++ b/test/pubsub.spec.js @@ -17,51 +17,32 @@ describe("publish/subscribe", function () { var message = "test message"; beforeEach(function (done) { - var pubConnected; - var subConnected; + var end = helper.callFuncAfter(done, 2); pub = redis.createClient.apply(redis.createClient, args); sub = redis.createClient.apply(redis.createClient, args); pub.once("connect", function () { pub.flushdb(function () { - pubConnected = true; - if (subConnected) { - done(); - } + end(); }); }); sub.once("connect", function () { - subConnected = true; - if (pubConnected) { - done(); - } + end(); }); }); describe('disable resubscribe', function () { beforeEach(function (done) { - var pubConnected; - var subConnected; - - pub = redis.createClient(); + sub.end(false); sub = redis.createClient({ disable_resubscribing: true }); - pub.once("connect", function () { - pubConnected = true; - if (subConnected) { - done(); - } - }); sub.once("connect", function () { - subConnected = true; - if (pubConnected) { - done(); - } + done(); }); }); - it('does not fire subscribe events after reconnecting', function (done) { + it.only('does not fire subscribe events after reconnecting', function (done) { var a = false; sub.on("subscribe", function (chnl, count) { if (chnl === channel2) { @@ -75,11 +56,12 @@ describe("publish/subscribe", function () { sub.on('reconnecting', function() { a = true; + sub.on('ready', function () { + setTimeout(done, 250); + }); }); sub.subscribe(channel, channel2); - - setTimeout(done, 250); }); }); From 261d064adc4535c629166b376f0f10a2d7d33665 Mon Sep 17 00:00:00 2001 From: Ruben Bridgewater Date: Fri, 27 Nov 2015 21:41:14 +0100 Subject: [PATCH 0522/1748] v.2.4.2 --- package.json | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/package.json b/package.json index 7eaaa3a61c7..6cc40427173 100644 --- a/package.json +++ b/package.json @@ -1,6 +1,6 @@ { "name": "redis", - "version": "2.4.1", + "version": "2.4.2", "description": "Redis client library", "keywords": [ "database", From 33d5981ebf90d5b1954d7ad3cb578b4bc27ce83d Mon Sep 17 00:00:00 2001 From: Ruben Bridgewater Date: Fri, 27 Nov 2015 21:42:23 +0100 Subject: [PATCH 0523/1748] Remove .only ... --- test/pubsub.spec.js | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/test/pubsub.spec.js b/test/pubsub.spec.js index b80756f00ed..906f0eb9784 100644 --- a/test/pubsub.spec.js +++ b/test/pubsub.spec.js @@ -42,7 +42,7 @@ describe("publish/subscribe", function () { }); }); - it.only('does not fire subscribe events after reconnecting', function (done) { + it('does not fire subscribe events after reconnecting', function (done) { var a = false; sub.on("subscribe", function (chnl, count) { if (chnl === channel2) { From 1ea9d8e564a55d5c7053b63857a1a65dbe4990aa Mon Sep 17 00:00:00 2001 From: greenkeeperio-bot Date: Mon, 30 Nov 2015 02:24:52 +0100 Subject: [PATCH 0524/1748] chore(package): update nyc to version 4.0.1 http://greenkeeper.io/ --- package.json | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/package.json b/package.json index 6cc40427173..5c4de2df715 100644 --- a/package.json +++ b/package.json @@ -33,7 +33,7 @@ "jshint": "^2.8.0", "metrics": "^0.1.9", "mocha": "^2.3.2", - "nyc": "^3.2.2", + "nyc": "^4.0.1", "optional-dev-dependency": "^1.1.0", "tcp-port-used": "^0.1.2", "uuid": "^2.0.1", From 0207163655985c8b9d6d2eda5261041e47e53062 Mon Sep 17 00:00:00 2001 From: Ruben Bridgewater Date: Sat, 28 Nov 2015 18:48:11 +0100 Subject: [PATCH 0525/1748] Remove support of redis 2.4 All tests require at least redis 2.6 from now on. Anyone who wants to run the tests should be able to install a newer version. --- test/batch.spec.js | 2 -- test/commands/client.spec.js | 2 -- test/commands/eval.spec.js | 20 ----------------- test/commands/script.spec.js | 4 ---- test/multi.spec.js | 43 +++++------------------------------- test/node_redis.spec.js | 2 -- test/pubsub.spec.js | 10 --------- 7 files changed, 6 insertions(+), 77 deletions(-) diff --git a/test/batch.spec.js b/test/batch.spec.js index 575544fe999..caf4e7b8efb 100644 --- a/test/batch.spec.js +++ b/test/batch.spec.js @@ -328,8 +328,6 @@ describe("The 'batch' method", function () { }); it("should work without any callback", function (done) { - helper.serverVersionAtLeast.call(this, client, [2, 6, 5]); - var batch = client.batch(); batch.set("baz", "binary"); batch.set("foo", "bar"); diff --git a/test/commands/client.spec.js b/test/commands/client.spec.js index e26df12ec47..f0f1f205adc 100644 --- a/test/commands/client.spec.js +++ b/test/commands/client.spec.js @@ -68,8 +68,6 @@ describe("The 'client' method", function () { }); it('sets the name', function (done) { - helper.serverVersionAtLeast.call(this, client, [2, 6, 9]); - // The querys are auto pipelined and the response is a response to all querys of one client // per chunk. So the execution order is only garanteed on each client var end = helper.callFuncAfter(done, 2); diff --git a/test/commands/eval.spec.js b/test/commands/eval.spec.js index 53cff94e71d..d091a4dbd73 100644 --- a/test/commands/eval.spec.js +++ b/test/commands/eval.spec.js @@ -26,32 +26,26 @@ describe("The 'eval' method", function () { }); it('converts a float to an integer when evaluated', function (done) { - helper.serverVersionAtLeast.call(this, client, [2, 5, 0]); client.eval("return 100.5", 0, helper.isNumber(100, done)); }); it('returns a string', function (done) { - helper.serverVersionAtLeast.call(this, client, [2, 5, 0]); client.eval("return 'hello world'", 0, helper.isString('hello world', done)); }); it('converts boolean true to integer 1', function (done) { - helper.serverVersionAtLeast.call(this, client, [2, 5, 0]); client.eval("return true", 0, helper.isNumber(1, done)); }); it('converts boolean false to null', function (done) { - helper.serverVersionAtLeast.call(this, client, [2, 5, 0]); client.eval("return false", 0, helper.isNull(done)); }); it('converts lua status code to string representation', function (done) { - helper.serverVersionAtLeast.call(this, client, [2, 5, 0]); client.eval("return {ok='fine'}", 0, helper.isString('fine', done)); }); it('converts lua error to an error response', function (done) { - helper.serverVersionAtLeast.call(this, client, [2, 5, 0]); client.eval("return {err='this is an error'}", 0, function(err) { assert(err.code === undefined); helper.isError()(err); @@ -60,7 +54,6 @@ describe("The 'eval' method", function () { }); it('represents a lua table appropritely', function (done) { - helper.serverVersionAtLeast.call(this, client, [2, 5, 0]); client.eval("return {1,2,3,'ciao',{1,2}}", 0, function (err, res) { assert.strictEqual(5, res.length); assert.strictEqual(1, res[0]); @@ -75,7 +68,6 @@ describe("The 'eval' method", function () { }); it('populates keys and argv correctly', function (done) { - helper.serverVersionAtLeast.call(this, client, [2, 5, 0]); client.eval("return {KEYS[1],KEYS[2],ARGV[1],ARGV[2]}", 2, "a", "b", "c", "d", function (err, res) { assert.strictEqual(4, res.length); assert.strictEqual("a", res[0]); @@ -87,7 +79,6 @@ describe("The 'eval' method", function () { }); it('allows arguments to be provided in array rather than as multiple parameters', function (done) { - helper.serverVersionAtLeast.call(this, client, [2, 5, 0]); client.eval(["return {KEYS[1],KEYS[2],ARGV[1],ARGV[2]}", 2, "a", "b", "c", "d"], function (err, res) { assert.strictEqual(4, res.length); assert.strictEqual("a", res[0]); @@ -99,7 +90,6 @@ describe("The 'eval' method", function () { }); it('allows a script to be executed that accesses the redis API without callback', function (done) { - helper.serverVersionAtLeast.call(this, client, [2, 5, 0]); client.eval(source, 0); client.get('sha', helper.isString('test', done)); }); @@ -108,24 +98,20 @@ describe("The 'eval' method", function () { var sha = crypto.createHash('sha1').update(source).digest('hex'); it('allows a script to be executed that accesses the redis API', function (done) { - helper.serverVersionAtLeast.call(this, client, [2, 5, 0]); client.eval(source, 0, helper.isString('OK')); client.get('sha', helper.isString('test', done)); }); it('can execute a script if the SHA exists', function (done) { - helper.serverVersionAtLeast.call(this, client, [2, 5, 0]); client.evalsha(sha, 0, helper.isString('OK')); client.get('sha', helper.isString('test', done)); }); it('returns an error if SHA does not exist', function (done) { - helper.serverVersionAtLeast.call(this, client, [2, 5, 0]); client.evalsha('ffffffffffffffffffffffffffffffffffffffff', 0, helper.isError(done)); }); it('emit an error if SHA does not exist without any callback', function (done) { - helper.serverVersionAtLeast.call(this, client, [2, 5, 0]); client.evalsha('ffffffffffffffffffffffffffffffffffffffff', 0); client.on('error', function(err) { assert.equal(err.code, 'NOSCRIPT'); @@ -144,7 +130,6 @@ describe("The 'eval' method", function () { }); it('allows a key to be incremented, and performs appropriate conversion from LUA type', function (done) { - helper.serverVersionAtLeast.call(this, client, [2, 5, 0]); client.set("incr key", 0, function (err, reply) { if (err) return done(err); client.eval("local foo = redis.call('incr','incr key')\n" + "return {type(foo),foo}", 0, function (err, res) { @@ -157,7 +142,6 @@ describe("The 'eval' method", function () { }); it('allows a bulk operation to be performed, and performs appropriate conversion from LUA type', function (done) { - helper.serverVersionAtLeast.call(this, client, [2, 5, 0]); client.set("bulk reply key", "bulk reply value", function (err, res) { client.eval("local foo = redis.call('get','bulk reply key'); return {type(foo),foo}", 0, function (err, res) { assert.strictEqual(2, res.length); @@ -169,7 +153,6 @@ describe("The 'eval' method", function () { }); it('allows a multi mulk operation to be performed, with the appropriate type conversion', function (done) { - helper.serverVersionAtLeast.call(this, client, [2, 5, 0]); client.multi() .del("mylist") .rpush("mylist", "a") @@ -190,7 +173,6 @@ describe("The 'eval' method", function () { }); it('returns an appropriate representation of Lua status reply', function (done) { - helper.serverVersionAtLeast.call(this, client, [2, 5, 0]); client.eval("local foo = redis.call('set','mykey','myval'); return {type(foo),foo['ok']}", 0, function (err, res) { assert.strictEqual(2, res.length); assert.strictEqual("table", res[0]); @@ -200,7 +182,6 @@ describe("The 'eval' method", function () { }); it('returns an appropriate representation of a Lua error reply', function (done) { - helper.serverVersionAtLeast.call(this, client, [2, 5, 0]); client.set("error reply key", "error reply value", function (err, res) { if (err) return done(err); client.eval("local foo = redis.pcall('incr','error reply key'); return {type(foo),foo['err']}", 0, function (err, res) { @@ -213,7 +194,6 @@ describe("The 'eval' method", function () { }); it('returns an appropriate representation of a Lua nil reply', function (done) { - helper.serverVersionAtLeast.call(this, client, [2, 5, 0]); client.del("nil reply key", function (err, res) { if (err) return done(err); client.eval("local foo = redis.call('get','nil reply key'); return {type(foo),foo == false}", 0, function (err, res) { diff --git a/test/commands/script.spec.js b/test/commands/script.spec.js index a82ab09d19f..e7eecc172b0 100644 --- a/test/commands/script.spec.js +++ b/test/commands/script.spec.js @@ -27,7 +27,6 @@ describe("The 'script' method", function () { }); it("loads script with client.script('load')", function (done) { - helper.serverVersionAtLeast.call(this, client, [2, 6, 0]); client.script("load", command, function(err, result) { assert.strictEqual(result, commandSha); return done(); @@ -35,12 +34,10 @@ describe("The 'script' method", function () { }); it('allows a loaded script to be evaluated', function (done) { - helper.serverVersionAtLeast.call(this, client, [2, 6, 0]); client.evalsha(commandSha, 0, helper.isString('99', done)); }); it('allows a script to be loaded as part of a chained transaction', function (done) { - helper.serverVersionAtLeast.call(this, client, [2, 6, 0]); client.multi().script("load", command).exec(function(err, result) { assert.strictEqual(result[0], commandSha); return done(); @@ -48,7 +45,6 @@ describe("The 'script' method", function () { }); it("allows a script to be loaded using a transaction's array syntax", function (done) { - helper.serverVersionAtLeast.call(this, client, [2, 6, 0]); client.multi([['script', 'load', command]]).exec(function(err, result) { assert.strictEqual(result[0], commandSha); return done(); diff --git a/test/multi.spec.js b/test/multi.spec.js index d4f0250200b..63541e8a505 100644 --- a/test/multi.spec.js +++ b/test/multi.spec.js @@ -251,25 +251,18 @@ describe("The 'multi' method", function () { it('roles back a transaction when one command in a sequence of commands fails', function (done) { var multi1, multi2; - var expected = helper.serverVersionAtLeast(client, [2, 6, 5]) ? helper.isError() : function () {}; - // Provoke an error at queue time multi1 = client.MULTI(); multi1.mset("multifoo", "10", "multibar", "20", helper.isString("OK")); - multi1.set("foo2", expected); + multi1.set("foo2", helper.isError()); multi1.incr("multifoo"); multi1.incr("multibar"); multi1.exec(function () { // Redis 2.6.5+ will abort transactions with errors // see: http://redis.io/topics/transactions - var multibar_expected = 22; - var multifoo_expected = 12; - if (helper.serverVersionAtLeast(client, [2, 6, 5])) { - multibar_expected = 1; - multifoo_expected = 1; - } - + var multibar_expected = 1; + var multifoo_expected = 1; // Confirm that the previous command, while containing an error, still worked. multi2 = client.multi(); multi2.incr("multibar", helper.isNumber(multibar_expected)); @@ -283,8 +276,6 @@ describe("The 'multi' method", function () { }); it('roles back a transaction when one command in an array of commands fails', function (done) { - var expected = helper.serverVersionAtLeast(client, [2, 6, 5]) ? helper.isError() : function () {}; - // test nested multi-bulk replies client.multi([ ["mget", "multifoo", "multibar", function (err, res) { @@ -292,22 +283,12 @@ describe("The 'multi' method", function () { assert.strictEqual(0, +res[0]); assert.strictEqual(0, +res[1]); }], - ["set", "foo2", expected], + ["set", "foo2", helper.isError()], ["incr", "multifoo"], ["incr", "multibar"] ]).exec(function (err, replies) { - if (helper.serverVersionAtLeast(client, [2, 6, 5])) { - assert.notEqual(err, null); - assert.equal(replies, undefined); - } else { - assert.strictEqual(2, replies[0].length); - assert.strictEqual(null, replies[0][0]); - assert.strictEqual(null, replies[0][1]); - - assert.strictEqual("1", replies[1].toString()); - assert.strictEqual("1", replies[2].toString()); - } - + assert.notEqual(err, null); + assert.equal(replies, undefined); return done(); }); }); @@ -537,8 +518,6 @@ describe("The 'multi' method", function () { }); it("emits an error if no callback has been provided and execabort error occured", function (done) { - helper.serverVersionAtLeast.call(this, client, [2, 6, 5]); - var multi = client.multi(); multi.config("bar"); multi.set("foo"); @@ -551,8 +530,6 @@ describe("The 'multi' method", function () { }); it("should work without any callback", function (done) { - helper.serverVersionAtLeast.call(this, client, [2, 6, 5]); - var multi = client.multi(); multi.set("baz", "binary"); multi.set("foo", "bar"); @@ -583,8 +560,6 @@ describe("The 'multi' method", function () { }); it("should use transaction with exec_atomic and more than one command used", function (done) { - helper.serverVersionAtLeast.call(this, client, [2, 6, 5]); - var multi = client.multi(); var test = false; multi.exec_batch = function () { @@ -597,8 +572,6 @@ describe("The 'multi' method", function () { }); it("do not mutate arguments in the multi constructor", function (done) { - helper.serverVersionAtLeast.call(this, client, [2, 6, 5]); - var input = [['set', 'foo', 'bar'], ['get', 'foo']]; client.multi(input).exec(function (err, res) { assert.strictEqual(input.length, 2); @@ -609,8 +582,6 @@ describe("The 'multi' method", function () { }); it("works properly after a reconnect. issue #897", function (done) { - helper.serverVersionAtLeast.call(this, client, [2, 6, 5]); - client.stream.destroy(); client.on('error', function (err) { assert.strictEqual(err.code, 'ECONNREFUSED'); @@ -625,8 +596,6 @@ describe("The 'multi' method", function () { }); it("emits error once if reconnecting after multi has been executed but not yet returned without callback", function (done) { - helper.serverVersionAtLeast.call(this, client, [2, 6, 5]); - client.on('error', function(err) { assert.strictEqual(err.code, 'UNCERTAIN_STATE'); done(); diff --git a/test/node_redis.spec.js b/test/node_redis.spec.js index 03098115d9c..521b9ab7602 100644 --- a/test/node_redis.spec.js +++ b/test/node_redis.spec.js @@ -345,8 +345,6 @@ describe("The node_redis client", function () { describe('monitor', function () { it('monitors commands on all other redis clients', function (done) { - helper.serverVersionAtLeast.call(this, client, [2, 6, 0]); - var monitorClient = redis.createClient.apply(redis.createClient, args); var responses = []; diff --git a/test/pubsub.spec.js b/test/pubsub.spec.js index 906f0eb9784..081399fd484 100644 --- a/test/pubsub.spec.js +++ b/test/pubsub.spec.js @@ -104,8 +104,6 @@ describe("publish/subscribe", function () { }); it('receives messages if subscribe is called after unsubscribe', function (done) { - helper.serverVersionAtLeast.bind(this)(sub, [2, 6, 11]); - var end = helper.callFuncAfter(done, 2); sub.once("subscribe", function (chnl, count) { pub.publish(channel, message, function (err, res) { @@ -126,8 +124,6 @@ describe("publish/subscribe", function () { }); it('handles SUB_UNSUB_MSG_SUB', function (done) { - helper.serverVersionAtLeast.bind(this)(sub, [2, 6, 11]); - sub.subscribe('chan8'); sub.subscribe('chan9'); sub.unsubscribe('chan9'); @@ -138,8 +134,6 @@ describe("publish/subscribe", function () { }); it('handles SUB_UNSUB_MSG_SUB 2', function (done) { - helper.serverVersionAtLeast.bind(this)(sub, [2, 6, 11]); - sub.psubscribe('abc*'); sub.subscribe('xyz'); sub.unsubscribe('xyz'); @@ -241,8 +235,6 @@ describe("publish/subscribe", function () { }); it('executes callback when unsubscribe is called and there are no subscriptions', function (done) { - helper.serverVersionAtLeast.bind(this)(sub, [2, 6, 11]); - pub.unsubscribe(function (err, results) { assert.strictEqual(null, results); return done(err); @@ -270,8 +262,6 @@ describe("publish/subscribe", function () { }); it('executes callback when punsubscribe is called and there are no subscriptions', function (done) { - helper.serverVersionAtLeast.bind(this)(sub, [2, 6, 11]); - pub.punsubscribe(function (err, results) { assert.strictEqual(null, results); return done(err); From 16a1d69c8230c247ce05af025bf41c3af47f9a12 Mon Sep 17 00:00:00 2001 From: Ruben Bridgewater Date: Mon, 30 Nov 2015 12:59:30 +0100 Subject: [PATCH 0526/1748] Move parsers into seperate module and improve js parser performance --- README.md | 70 ++++----- benchmarks/multi_bench.js | 4 +- changelog.md | 7 + index.js | 117 +++++--------- lib/parsers/hiredis.js | 37 ----- lib/parsers/javascript.js | 167 -------------------- lib/utils.js | 2 +- package.json | 6 +- test/node_redis.spec.js | 14 -- test/parser.spec.js | 317 -------------------------------------- 10 files changed, 91 insertions(+), 650 deletions(-) delete mode 100644 lib/parsers/hiredis.js delete mode 100644 lib/parsers/javascript.js delete mode 100644 test/parser.spec.js diff --git a/README.md b/README.md index 4e7def98f80..7cc110f0a17 100644 --- a/README.md +++ b/README.md @@ -653,43 +653,43 @@ hiredis parser (Lenovo T450s i7-5600U): ``` Client count: 1, node version: 4.2.2, server version: 3.0.3, parser: hiredis - PING, 1/1 min/max/avg/p95: 0/ 2/ 0.02/ 0.00 2501ms total, 47503.80 ops/sec - PING, batch 50/1 min/max/avg/p95: 0/ 2/ 0.09/ 1.00 2501ms total, 529668.13 ops/sec - SET 4B str, 1/1 min/max/avg/p95: 0/ 2/ 0.02/ 0.00 2501ms total, 41900.04 ops/sec - SET 4B str, batch 50/1 min/max/avg/p95: 0/ 2/ 0.14/ 1.00 2501ms total, 354658.14 ops/sec - SET 4B buf, 1/1 min/max/avg/p95: 0/ 4/ 0.04/ 0.00 2501ms total, 23499.00 ops/sec - SET 4B buf, batch 50/1 min/max/avg/p95: 0/ 2/ 0.31/ 1.00 2501ms total, 159836.07 ops/sec - GET 4B str, 1/1 min/max/avg/p95: 0/ 4/ 0.02/ 0.00 2501ms total, 43489.80 ops/sec - GET 4B str, batch 50/1 min/max/avg/p95: 0/ 2/ 0.11/ 1.00 2501ms total, 444202.32 ops/sec - GET 4B buf, 1/1 min/max/avg/p95: 0/ 3/ 0.02/ 0.00 2501ms total, 38561.38 ops/sec - GET 4B buf, batch 50/1 min/max/avg/p95: 0/ 2/ 0.11/ 1.00 2501ms total, 452139.14 ops/sec - SET 4KiB str, 1/1 min/max/avg/p95: 0/ 2/ 0.03/ 0.00 2501ms total, 32990.80 ops/sec - SET 4KiB str, batch 50/1 min/max/avg/p95: 0/ 2/ 0.34/ 1.00 2501ms total, 146161.54 ops/sec - SET 4KiB buf, 1/1 min/max/avg/p95: 0/ 1/ 0.04/ 0.00 2501ms total, 23294.28 ops/sec - SET 4KiB buf, batch 50/1 min/max/avg/p95: 0/ 2/ 0.36/ 1.00 2501ms total, 137584.97 ops/sec - GET 4KiB str, 1/1 min/max/avg/p95: 0/ 2/ 0.03/ 0.00 2501ms total, 36350.66 ops/sec - GET 4KiB str, batch 50/1 min/max/avg/p95: 0/ 2/ 0.32/ 1.00 2501ms total, 155157.94 ops/sec - GET 4KiB buf, 1/1 min/max/avg/p95: 0/ 4/ 0.02/ 0.00 2501ms total, 39776.49 ops/sec - GET 4KiB buf, batch 50/1 min/max/avg/p95: 0/ 2/ 0.32/ 1.00 2501ms total, 155457.82 ops/sec - INCR, 1/1 min/max/avg/p95: 0/ 3/ 0.02/ 0.00 2501ms total, 43972.41 ops/sec - INCR, batch 50/1 min/max/avg/p95: 0/ 1/ 0.12/ 1.00 2501ms total, 425809.68 ops/sec - LPUSH, 1/1 min/max/avg/p95: 0/ 2/ 0.02/ 0.00 2501ms total, 38998.40 ops/sec - LPUSH, batch 50/1 min/max/avg/p95: 0/ 4/ 0.14/ 1.00 2501ms total, 365013.99 ops/sec - LRANGE 10, 1/1 min/max/avg/p95: 0/ 2/ 0.03/ 0.00 2501ms total, 31879.25 ops/sec - LRANGE 10, batch 50/1 min/max/avg/p95: 0/ 1/ 0.32/ 1.00 2501ms total, 153698.52 ops/sec - LRANGE 100, 1/1 min/max/avg/p95: 0/ 4/ 0.06/ 0.00 2501ms total, 16676.13 ops/sec - LRANGE 100, batch 50/1 min/max/avg/p95: 1/ 6/ 2.03/ 2.00 2502ms total, 24520.38 ops/sec - SET 4MiB str, 1/1 min/max/avg/p95: 1/ 6/ 2.11/ 3.00 2502ms total, 472.82 ops/sec - SET 4MiB str, batch 20/1 min/max/avg/p95: 85/ 112/ 94.93/ 109.60 2563ms total, 210.69 ops/sec - SET 4MiB buf, 1/1 min/max/avg/p95: 1/ 8/ 2.02/ 3.00 2502ms total, 490.01 ops/sec - SET 4MiB buf, batch 20/1 min/max/avg/p95: 37/ 52/ 39.48/ 46.75 2528ms total, 506.33 ops/sec - GET 4MiB str, 1/1 min/max/avg/p95: 3/ 13/ 5.26/ 9.00 2504ms total, 190.10 ops/sec - GET 4MiB str, batch 20/1 min/max/avg/p95: 70/ 106/ 89.36/ 103.75 2503ms total, 223.73 ops/sec - GET 4MiB buf, 1/1 min/max/avg/p95: 3/ 11/ 5.04/ 8.15 2502ms total, 198.24 ops/sec - GET 4MiB buf, batch 20/1 min/max/avg/p95: 70/ 105/ 88.07/ 103.00 2554ms total, 227.09 ops/sec + PING, 1/1 min/max/avg: 0/ 2/ 0.02/ 2501ms total, 47503.80 ops/sec + PING, batch 50/1 min/max/avg: 0/ 2/ 0.09/ 2501ms total, 529668.13 ops/sec + SET 4B str, 1/1 min/max/avg: 0/ 2/ 0.02/ 2501ms total, 41900.04 ops/sec + SET 4B str, batch 50/1 min/max/avg: 0/ 2/ 0.14/ 2501ms total, 354658.14 ops/sec + SET 4B buf, 1/1 min/max/avg: 0/ 4/ 0.04/ 2501ms total, 23499.00 ops/sec + SET 4B buf, batch 50/1 min/max/avg: 0/ 2/ 0.31/ 2501ms total, 159836.07 ops/sec + GET 4B str, 1/1 min/max/avg: 0/ 4/ 0.02/ 2501ms total, 43489.80 ops/sec + GET 4B str, batch 50/1 min/max/avg: 0/ 2/ 0.11/ 2501ms total, 444202.32 ops/sec + GET 4B buf, 1/1 min/max/avg: 0/ 3/ 0.02/ 2501ms total, 38561.38 ops/sec + GET 4B buf, batch 50/1 min/max/avg: 0/ 2/ 0.11/ 2501ms total, 452139.14 ops/sec + SET 4KiB str, 1/1 min/max/avg: 0/ 2/ 0.03/ 2501ms total, 32990.80 ops/sec + SET 4KiB str, batch 50/1 min/max/avg: 0/ 2/ 0.34/ 2501ms total, 146161.54 ops/sec + SET 4KiB buf, 1/1 min/max/avg: 0/ 1/ 0.04/ 2501ms total, 23294.28 ops/sec + SET 4KiB buf, batch 50/1 min/max/avg: 0/ 2/ 0.36/ 2501ms total, 137584.97 ops/sec + GET 4KiB str, 1/1 min/max/avg: 0/ 2/ 0.03/ 2501ms total, 36350.66 ops/sec + GET 4KiB str, batch 50/1 min/max/avg: 0/ 2/ 0.32/ 2501ms total, 155157.94 ops/sec + GET 4KiB buf, 1/1 min/max/avg: 0/ 4/ 0.02/ 2501ms total, 39776.49 ops/sec + GET 4KiB buf, batch 50/1 min/max/avg: 0/ 2/ 0.32/ 2501ms total, 155457.82 ops/sec + INCR, 1/1 min/max/avg: 0/ 3/ 0.02/ 2501ms total, 43972.41 ops/sec + INCR, batch 50/1 min/max/avg: 0/ 1/ 0.12/ 2501ms total, 425809.68 ops/sec + LPUSH, 1/1 min/max/avg: 0/ 2/ 0.02/ 2501ms total, 38998.40 ops/sec + LPUSH, batch 50/1 min/max/avg: 0/ 4/ 0.14/ 2501ms total, 365013.99 ops/sec + LRANGE 10, 1/1 min/max/avg: 0/ 2/ 0.03/ 2501ms total, 31879.25 ops/sec + LRANGE 10, batch 50/1 min/max/avg: 0/ 1/ 0.32/ 2501ms total, 153698.52 ops/sec + LRANGE 100, 1/1 min/max/avg: 0/ 4/ 0.06/ 2501ms total, 16676.13 ops/sec + LRANGE 100, batch 50/1 min/max/avg: 1/ 6/ 2.03/ 2502ms total, 24520.38 ops/sec + SET 4MiB str, 1/1 min/max/avg: 1/ 6/ 2.11/ 2502ms total, 472.82 ops/sec + SET 4MiB str, batch 20/1 min/max/avg: 85/ 112/ 94.93/ 2563ms total, 210.69 ops/sec + SET 4MiB buf, 1/1 min/max/avg: 1/ 8/ 2.02/ 2502ms total, 490.01 ops/sec + SET 4MiB buf, batch 20/1 min/max/avg: 37/ 52/ 39.48/ 2528ms total, 506.33 ops/sec + GET 4MiB str, 1/1 min/max/avg: 3/ 13/ 5.26/ 2504ms total, 190.10 ops/sec + GET 4MiB str, batch 20/1 min/max/avg: 70/ 106/ 89.36/ 2503ms total, 223.73 ops/sec + GET 4MiB buf, 1/1 min/max/avg: 3/ 11/ 5.04/ 2502ms total, 198.24 ops/sec + GET 4MiB buf, batch 20/1 min/max/avg: 70/ 105/ 88.07/ 2554ms total, 227.09 ops/sec ``` -The hiredis and js parser should most of the time be on the same level. But if you use Redis for big SUNION/SINTER/LRANGE/ZRANGE hiredis is significantly faster. +The hiredis and js parser should most of the time be on the same level. But if you use Redis for big SUNION/SINTER/LRANGE/ZRANGE hiredis is faster. Therefor the hiredis parser is the default used in node_redis. To use `hiredis`, do: npm install hiredis redis diff --git a/benchmarks/multi_bench.js b/benchmarks/multi_bench.js index d4b506e78c9..6d5b0e6f22f 100644 --- a/benchmarks/multi_bench.js +++ b/benchmarks/multi_bench.js @@ -42,7 +42,7 @@ function lpad(input, len, chr) { metrics.Histogram.prototype.print_line = function () { var obj = this.printObj(); - return lpad(obj.min, 4) + '/' + lpad(obj.max, 4) + '/' + lpad(obj.mean.toFixed(2), 7) + '/' + lpad(obj.p95.toFixed(2), 7); + return lpad(obj.min, 4) + '/' + lpad(obj.max, 4) + '/' + lpad(obj.mean.toFixed(2), 7); }; function Test(args) { @@ -205,7 +205,7 @@ Test.prototype.print_stats = function () { var duration = Date.now() - this.test_start; totalTime += duration; - console.log('min/max/avg/p95: ' + this.command_latency.print_line() + ' ' + lpad(duration, 6) + 'ms total, ' + + console.log('min/max/avg: ' + this.command_latency.print_line() + ' ' + lpad(duration, 6) + 'ms total, ' + lpad((this.commands_completed / (duration / 1000)).toFixed(2), 9) + ' ops/sec'); }; diff --git a/changelog.md b/changelog.md index 606904f891c..944680781af 100644 --- a/changelog.md +++ b/changelog.md @@ -1,6 +1,13 @@ Changelog ========= +## v.2.5.0 - xx Dez, 2015 + +Features + +- The parsers moved into the [redis-parser](https://github.com/NodeRedis/node-redis-parser) module and will be maintained in there from now on ([@BridgeAR](https://github.com/BridgeAR)) + - Improve js parser speed significantly for big SUNION/SINTER/LRANGE/ZRANGE ([@BridgeAR](https://github.com/BridgeAR)) + ## v.2.4.2 - 27 Nov, 2015 Bugfixes diff --git a/index.js b/index.js index 0e2e66d00a5..3043a556271 100644 --- a/index.js +++ b/index.js @@ -8,7 +8,7 @@ var utils = require('./lib/utils'); var Queue = require('double-ended-queue'); var Command = require('./lib/command'); var events = require('events'); -var parsers = []; +var Parser = require('redis-parser'); var commands = require('redis-commands'); var connection_id = 0; var default_port = 6379; @@ -18,17 +18,20 @@ function noop () {} function clone (obj) { return JSON.parse(JSON.stringify(obj || {})); } function debug (msg) { if (exports.debug_mode) { console.error(msg); } } -exports.debug_mode = /\bredis\b/i.test(process.env.NODE_DEBUG); +function handle_detect_buffers_reply (reply, command, buffer_args) { + if (buffer_args === false) { + // If detect_buffers option was specified, then the reply from the parser will be a buffer. + // If this command did not use Buffer arguments, then convert the reply to Strings here. + reply = utils.reply_to_strings(reply); + } -// Hiredis might not be installed -try { - parsers.push(require('./lib/parsers/hiredis')); -} catch (err) { - /* istanbul ignore next: won't be reached with tests */ - debug('Hiredis parser not installed.'); + if (command === 'hgetall') { + reply = utils.reply_to_object(reply); + } + return reply; } -parsers.push(require('./lib/parsers/javascript')); +exports.debug_mode = /\bredis\b/i.test(process.env.NODE_DEBUG); function RedisClient (options) { // Copy the options so they are not mutated @@ -69,6 +72,10 @@ function RedisClient (options) { console.warn('>> WARNING: You activated return_buffers and detect_buffers at the same time. The return value is always going to be a buffer.'); options.detect_buffers = false; } + if (options.detect_buffers) { + // We only need to look at the arguments if we do not know what we have to return + this.handle_reply = handle_detect_buffers_reply; + } this.should_buffer = false; this.max_attempts = options.max_attempts | 0; this.command_queue = new Queue(); // Holds sent commands to de-pipeline them @@ -83,13 +90,22 @@ function RedisClient (options) { this.closing = false; this.server_info = {}; this.auth_pass = options.auth_pass; - this.parser_module = null; this.selected_db = null; // Save the selected db here, used when reconnecting this.old_state = null; this.pipeline = 0; this.options = options; - // Init parser once per instance - this.init_parser(); + // Init parser + var self = this; + this.reply_parser = new Parser({ + returnReply: function (data) { + self.return_reply(data); + }, + returnError: function (data) { + self.return_error(data); + }, + returnBuffers: options.return_buffers || options.detect_buffers, + name: options.parser + }); this.create_stream(); } util.inherits(RedisClient, events.EventEmitter); @@ -153,6 +169,13 @@ RedisClient.prototype.create_stream = function () { }); }; +RedisClient.prototype.handle_reply = function (reply, command) { + if (command === 'hgetall') { + reply = utils.reply_to_object(reply); + } + return reply; +}; + RedisClient.prototype.cork = noop; RedisClient.prototype.uncork = noop; @@ -300,39 +323,6 @@ RedisClient.prototype.on_connect = function () { } }; -RedisClient.prototype.init_parser = function () { - var self = this; - - if (this.options.parser) { - if (!parsers.some(function (parser) { - if (parser.name === self.options.parser) { - self.parser_module = parser; - debug('Using parser module: ' + self.parser_module.name); - return true; - } - })) { - // Do not emit this error - // This should take down the app if anyone made such a huge mistake or should somehow be handled in user code - throw new Error("Couldn't find named parser " + self.options.parser + " on this system"); - } - } else { - debug('Using default parser module: ' + parsers[0].name); - this.parser_module = parsers[0]; - } - - // return_buffers sends back Buffers from parser to callback. detect_buffers sends back Buffers from parser, but - // converts to Strings if the input arguments are not Buffers. - this.reply_parser = new this.parser_module.Parser(self.options.return_buffers || self.options.detect_buffers); - // Important: Only send results / errors async. - // That way the result / error won't stay in a try catch block and catch user things - this.reply_parser.send_error = function (data) { - self.return_error(data); - }; - this.reply_parser.send_reply = function (data) { - self.return_reply(data); - }; -}; - RedisClient.prototype.on_ready = function () { var self = this; @@ -599,7 +589,7 @@ RedisClient.prototype.return_error = function (err) { err.command = command_obj.command; } - var match = err.message.match(utils.errCode); + var match = err.message.match(utils.err_code); // LUA script could return user errors that don't behave like all other errors! if (match) { err.code = match[1]; @@ -650,16 +640,7 @@ RedisClient.prototype.return_reply = function (reply) { if (command_obj && !command_obj.sub_command) { if (typeof command_obj.callback === 'function') { if ('exec' !== command_obj.command) { - if (command_obj.buffer_args === false) { - // If detect_buffers option was specified, then the reply from the parser will be Buffers. - // If this command did not use Buffer arguments, then convert the reply to Strings here. - reply = utils.reply_to_strings(reply); - } - - // TODO - confusing and error-prone that hgetall is special cased in two places - if ('hgetall' === command_obj.command) { - reply = utils.reply_to_object(reply); - } + reply = this.handle_reply(reply, command_obj.command, command_obj.buffer_args); } command_obj.callback(null, reply); } else { @@ -722,8 +703,7 @@ RedisClient.prototype.send_command = function (command, args, callback) { command_str = '', buffer_args = false, big_data = false, - prefix_keys, - buffer = this.options.return_buffers; + prefix_keys; if (args === undefined) { args = []; @@ -770,11 +750,8 @@ RedisClient.prototype.send_command = function (command, args, callback) { } } } - if (this.options.detect_buffers) { - buffer = buffer_args; - } - command_obj = new Command(command, args, false, buffer, callback); + command_obj = new Command(command, args, false, buffer_args, callback); if (!this.ready && !this.send_anyway || !stream.writable) { if (this.closing || !this.enable_offline_queue) { @@ -1149,11 +1126,7 @@ Multi.prototype.exec_transaction = function (callback) { cb = undefined; } // Keep track of who wants buffer responses: - if (this._client.options.return_buffers) { - this.wants_buffers[index] = true; - } else if (!this._client.options.detect_buffers) { - this.wants_buffers[index] = false; - } else { + if (this._client.options.detect_buffers) { this.wants_buffers[index] = false; for (var i = 0; i < args.length; i += 1) { if (Buffer.isBuffer(args[i])) { @@ -1193,20 +1166,14 @@ Multi.prototype.execute_callback = function (err, replies) { while (args = this.queue.shift()) { // If we asked for strings, even in detect_buffers mode, then return strings: if (replies[i] instanceof Error) { - var match = replies[i].message.match(utils.errCode); + var match = replies[i].message.match(utils.err_code); // LUA script could return user errors that don't behave like all other errors! if (match) { replies[i].code = match[1]; } replies[i].command = args[0].toUpperCase(); } else if (replies[i]) { - if (this.wants_buffers[i] === false) { - replies[i] = utils.reply_to_strings(replies[i]); - } - if (args[0] === 'hgetall') { - // TODO - confusing and error-prone that hgetall is special cased in two places - replies[i] = utils.reply_to_object(replies[i]); - } + replies[i] = this._client.handle_reply(replies[i], args[0], this.wants_buffers[i]); } if (typeof args[args.length - 1] === 'function') { diff --git a/lib/parsers/hiredis.js b/lib/parsers/hiredis.js deleted file mode 100644 index 1b254191037..00000000000 --- a/lib/parsers/hiredis.js +++ /dev/null @@ -1,37 +0,0 @@ -'use strict'; - -var hiredis = require('hiredis'); - -function HiredisReplyParser(return_buffers) { - this.name = exports.name; - this.reader = new hiredis.Reader({ - return_buffers: return_buffers - }); -} - -HiredisReplyParser.prototype.return_data = function () { - try { - return this.reader.get(); - } catch (err) { - // Protocol errors land here - this.send_error(err); - return void 0; - } -}; - -HiredisReplyParser.prototype.execute = function (data) { - this.reader.feed(data); - var reply = this.return_data(); - - while (reply !== undefined) { - if (reply && reply.name === 'Error') { - this.send_error(reply); - } else { - this.send_reply(reply); - } - reply = this.return_data(); - } -}; - -exports.Parser = HiredisReplyParser; -exports.name = 'hiredis'; diff --git a/lib/parsers/javascript.js b/lib/parsers/javascript.js deleted file mode 100644 index 22421fdfed0..00000000000 --- a/lib/parsers/javascript.js +++ /dev/null @@ -1,167 +0,0 @@ -'use strict'; - -var util = require('util'); - -function JavascriptReplyParser() { - this.name = exports.name; - this._buffer = new Buffer(0); - this._offset = 0; - this._big_str_size = 0; - this._chunks_size = 0; - this._buffers = []; - this._type = 0; - this._protocol_error = false; -} - -function IncompleteReadBuffer(message) { - this.name = 'IncompleteReadBuffer'; - this.message = message; -} -util.inherits(IncompleteReadBuffer, Error); - -JavascriptReplyParser.prototype._parseResult = function (type) { - var start = 0, - end = 0, - offset = 0, - packetHeader = 0, - res, - reply; - - if (type === 43 || type === 58 || type === 45) { // + or : or - - // Up to the delimiter - end = this._packetEndOffset(); - start = this._offset; - // Include the delimiter - this._offset = end + 2; - - if (type === 43) { - return this._buffer.slice(start, end); - } else if (type === 58) { - // Return the coerced numeric value - return +this._buffer.toString('ascii', start, end); - } - return new Error(this._buffer.toString('utf-8', start, end)); - } else if (type === 36) { // $ - packetHeader = this.parseHeader(); - - // Packets with a size of -1 are considered null - if (packetHeader === -1) { - return null; - } - end = this._offset + packetHeader; - start = this._offset; - - if (end + 2 > this._buffer.length) { - this._chunks_size = this._buffer.length - this._offset - 2; - this._big_str_size = packetHeader; - throw new IncompleteReadBuffer('Wait for more data.'); - } - // Set the offset to after the delimiter - this._offset = end + 2; - - return this._buffer.slice(start, end); - } else if (type === 42) { // * - // Set a rewind point, as the packet is larger than the buffer in memory - offset = this._offset; - packetHeader = this.parseHeader(); - - if (packetHeader === -1) { - return null; - } - reply = []; - offset = this._offset - 1; - - for (var i = 0; i < packetHeader; i++) { - if (this._offset >= this._buffer.length) { - throw new IncompleteReadBuffer('Wait for more data.'); - } - res = this._parseResult(this._buffer[this._offset++]); - reply.push(res); - } - return reply; - } else { - return void 0; - } -}; - -JavascriptReplyParser.prototype.execute = function (buffer) { - if (this._chunks_size !== 0) { - if (this._big_str_size > this._chunks_size + buffer.length) { - this._buffers.push(buffer); - this._chunks_size += buffer.length; - return; - } - this._buffers.unshift(this._offset === 0 ? this._buffer : this._buffer.slice(this._offset)); - this._buffers.push(buffer); - this._buffer = Buffer.concat(this._buffers); - this._buffers = []; - this._big_str_size = 0; - this._chunks_size = 0; - } else if (this._offset >= this._buffer.length) { - this._buffer = buffer; - } else { - this._buffer = Buffer.concat([this._buffer.slice(this._offset), buffer]); - } - this._offset = 0; - this._protocol_error = true; - this.run(); -}; - -JavascriptReplyParser.prototype.try_parsing = function () { - // Set a rewind point. If a failure occurs, wait for the next execute()/append() and try again - var offset = this._offset - 1; - try { - return this._parseResult(this._type); - } catch (err) { - // Catch the error (not enough data), rewind if it's an array, - // and wait for the next packet to appear - this._offset = offset; - this._protocol_error = false; - return void 0; - } -}; - -JavascriptReplyParser.prototype.run = function (buffer) { - this._type = this._buffer[this._offset++]; - var reply = this.try_parsing(); - - while (reply !== undefined) { - if (this._type === 45) { // Errors - - this.send_error(reply); - } else { - this.send_reply(reply); // Strings + // Integers : // Bulk strings $ // Arrays * - } - this._type = this._buffer[this._offset++]; - reply = this.try_parsing(); - } - if (this._type !== undefined && this._protocol_error === true) { - // Reset the buffer so the parser can handle following commands properly - this._buffer = new Buffer(0); - this.send_error(new Error('Protocol error, got ' + JSON.stringify(String.fromCharCode(this._type)) + ' as reply type byte')); - } -}; - -JavascriptReplyParser.prototype.parseHeader = function () { - var end = this._packetEndOffset(), - value = this._buffer.toString('ascii', this._offset, end) | 0; - - this._offset = end + 2; - return value; -}; - -JavascriptReplyParser.prototype._packetEndOffset = function () { - var offset = this._offset, - len = this._buffer.length - 1; - - while (this._buffer[offset] !== 0x0d && this._buffer[offset + 1] !== 0x0a) { - offset++; - - if (offset >= len) { - throw new IncompleteReadBuffer('Did not see LF after NL reading multi bulk count (' + offset + ' => ' + this._buffer.length + ', ' + this._offset + ')'); - } - } - return offset; -}; - -exports.Parser = JavascriptReplyParser; -exports.name = 'javascript'; diff --git a/lib/utils.js b/lib/utils.js index 3b282bc5ea4..ca458030cd6 100644 --- a/lib/utils.js +++ b/lib/utils.js @@ -62,5 +62,5 @@ module.exports = { reply_to_object: replyToObject, to_array: toArray, print: print, - errCode: redisErrCode + err_code: redisErrCode }; diff --git a/package.json b/package.json index 5c4de2df715..96f9c0d3aae 100644 --- a/package.json +++ b/package.json @@ -8,7 +8,8 @@ "transaction", "pipelining", "performance", - "queue" + "queue", + "nodejs" ], "author": "Matt Ranney ", "license": "MIT", @@ -23,7 +24,8 @@ }, "dependencies": { "double-ended-queue": "^2.1.0-0", - "redis-commands": "^1.0.1" + "redis-commands": "^1.0.1", + "redis-parser": "^1.0.0" }, "engines": { "node": ">=0.10.0" diff --git a/test/node_redis.spec.js b/test/node_redis.spec.js index 521b9ab7602..7b3171f8d5b 100644 --- a/test/node_redis.spec.js +++ b/test/node_redis.spec.js @@ -8,20 +8,6 @@ var redis = config.redis; describe("The node_redis client", function () { - describe("testing parser existence", function () { - it('throws on non-existence', function (done) { - try { - redis.createClient({ - parser: 'nonExistingParser' - }); - done(new Error('test failed')); - } catch (err) { - assert.equal(err.message, 'Couldn\'t find named parser nonExistingParser on this system'); - done(); - } - }); - }); - helper.allTests({ allConnections: true }, function(parser, ip, args) { diff --git a/test/parser.spec.js b/test/parser.spec.js deleted file mode 100644 index 7919926168b..00000000000 --- a/test/parser.spec.js +++ /dev/null @@ -1,317 +0,0 @@ -'use strict'; - -var assert = require('assert'); -var utils = require("../lib/utils"); -var parsers = [ - require("../lib/parsers/javascript").Parser -]; -try { - // Test the hiredis parser if available - parsers.push(require("../lib/parsers/hiredis").Parser); -} catch (e) {} - -describe('parsers', function () { - - parsers.forEach(function (Parser) { - - describe(Parser.name, function () { - - it('handles multi-bulk reply', function () { - var parser = new Parser(); - var reply_count = 0; - function check_reply(reply) { - reply = utils.reply_to_strings(reply); - assert.deepEqual(reply, [['a']], "Expecting multi-bulk reply of [['a']]"); - reply_count++; - } - parser.send_reply = check_reply; - - parser.execute(new Buffer('*1\r\n*1\r\n$1\r\na\r\n')); - assert.strictEqual(reply_count, 1); - - parser.execute(new Buffer('*1\r\n*1\r')); - parser.execute(new Buffer('\n$1\r\na\r\n')); - assert.strictEqual(reply_count, 2); - - parser.execute(new Buffer('*1\r\n*1\r\n')); - parser.execute(new Buffer('$1\r\na\r\n')); - - assert.equal(reply_count, 3, "check reply should have been called three times"); - }); - - it('parser error', function () { - var parser = new Parser(); - var reply_count = 0; - function check_reply (reply) { - assert.strictEqual(reply.message, 'Protocol error, got "a" as reply type byte'); - reply_count++; - } - parser.send_error = check_reply; - - parser.execute(new Buffer('a*1\r*1\r$1`zasd\r\na')); - assert.equal(reply_count, 1); - }); - - it('parser error v2', function () { - var parser = new Parser(); - var reply_count = 0; - var err_count = 0; - function check_reply (reply) { - reply = utils.reply_to_strings(reply); - assert.strictEqual(reply[0], 'OK'); - reply_count++; - } - function check_error (err) { - assert.strictEqual(err.message, 'Protocol error, got "b" as reply type byte'); - err_count++; - } - parser.send_error = check_error; - parser.send_reply = check_reply; - - parser.execute(new Buffer('*1\r\n+OK\r\nb$1`zasd\r\na')); - assert.strictEqual(reply_count, 1); - assert.strictEqual(err_count, 1); - }); - - it('parser error v3', function () { - var parser = new Parser(); - var reply_count = 0; - var err_count = 0; - function check_reply (reply) { - reply = utils.reply_to_strings(reply); - assert.strictEqual(reply[0], 'OK'); - reply_count++; - } - function check_error (err) { - assert.strictEqual(err.message, 'Protocol error, got "\\n" as reply type byte'); - err_count++; - } - parser.send_error = check_error; - parser.send_reply = check_reply; - - parser.execute(new Buffer('*1\r\n+OK\r\n\n+zasd\r\n')); - assert.strictEqual(reply_count, 1); - assert.strictEqual(err_count, 1); - }); - - it('should handle \\r and \\n characters properly', function () { - // If a string contains \r or \n characters it will always be send as a bulk string - var parser = new Parser(); - var reply_count = 0; - var entries = ['foo\r', 'foo\r\nbar', '\r\nfoo', 'foo\r\n']; - function check_reply (reply) { - reply = utils.reply_to_strings(reply); - assert.strictEqual(reply, entries[reply_count]); - reply_count++; - } - parser.send_reply = check_reply; - - parser.execute(new Buffer('$4\r\nfoo\r\r\n$8\r\nfoo\r\nbar\r\n$5\r\n\r\n')); - assert.strictEqual(reply_count, 2); - parser.execute(new Buffer('foo\r\n$5\r\nfoo\r\n\r\n')); - assert.strictEqual(reply_count, 4); - }); - - it('line breaks in the beginning of the last chunk', function () { - var parser = new Parser(); - var reply_count = 0; - function check_reply(reply) { - reply = utils.reply_to_strings(reply); - assert.deepEqual(reply, [['a']], "Expecting multi-bulk reply of [['a']]"); - reply_count++; - } - parser.send_reply = check_reply; - - parser.execute(new Buffer('*1\r\n*1\r\n$1\r\na')); - assert.equal(reply_count, 0); - - parser.execute(new Buffer('\r\n*1\r\n*1\r')); - assert.equal(reply_count, 1); - parser.execute(new Buffer('\n$1\r\na\r\n*1\r\n*1\r\n$1\r\na\r\n')); - - assert.equal(reply_count, 3, "check reply should have been called three times"); - }); - - it('multiple chunks in a bulk string', function () { - var parser = new Parser(); - var reply_count = 0; - function check_reply(reply) { - reply = utils.reply_to_strings(reply); - assert.strictEqual(reply, "abcdefghijabcdefghijabcdefghijabcdefghijabcdefghijabcdefghijabcdefghijabcdefghijabcdefghijabcdefghij"); - reply_count++; - } - parser.send_reply = check_reply; - - parser.execute(new Buffer('$100\r\nabcdefghij')); - parser.execute(new Buffer('abcdefghijabcdefghijabcdefghij')); - parser.execute(new Buffer('abcdefghijabcdefghijabcdefghij')); - parser.execute(new Buffer('abcdefghijabcdefghijabcdefghij')); - assert.strictEqual(reply_count, 0); - parser.execute(new Buffer('\r\n')); - assert.strictEqual(reply_count, 1); - - parser.execute(new Buffer('$100\r')); - parser.execute(new Buffer('\nabcdefghijabcdefghijabcdefghijabcdefghij')); - parser.execute(new Buffer('abcdefghijabcdefghijabcdefghij')); - parser.execute(new Buffer('abcdefghijabcdefghij')); - assert.strictEqual(reply_count, 1); - parser.execute(new Buffer( - 'abcdefghij\r\n' + - '$100\r\nabcdefghijabcdefghijabcdefghijabcdefghijabcdefghijabcdefghijabcdefghijabcdefghijabcdefghijabcdefghij\r\n' + - '$100\r\nabcdefghijabcdefghijabcdefghijabcdefghij' - )); - assert.strictEqual(reply_count, 3); - parser.execute(new Buffer('abcdefghijabcdefghijabcdefghij')); - parser.execute(new Buffer('abcdefghijabcdefghijabcdefghij\r')); - assert.strictEqual(reply_count, 3); - parser.execute(new Buffer('\n')); - - assert.equal(reply_count, 4, "check reply should have been called three times"); - }); - - it('multiple chunks with arrays different types', function () { - var parser = new Parser(); - var reply_count = 0; - var predefined_data = [ - 'abcdefghijabcdefghijabcdefghijabcdefghijabcdefghijabcdefghijabcdefghijabcdefghijabcdefghijabcdefghij', - 'test', - 100, - new Error('Error message'), - ['The force awakens'] - ]; - function check_reply(reply) { - reply = utils.reply_to_strings(reply); - for (var i = 0; i < reply.length; i++) { - if (i < 3) { - assert.strictEqual(reply[i], predefined_data[i]); - } else { - assert.deepEqual(reply[i], predefined_data[i]); - } - } - reply_count++; - } - parser.send_reply = check_reply; - - parser.execute(new Buffer('*5\r\n$100\r\nabcdefghij')); - parser.execute(new Buffer('abcdefghijabcdefghijabcdefghij')); - parser.execute(new Buffer('abcdefghijabcdefghijabcdefghij')); - parser.execute(new Buffer('abcdefghijabcdefghijabcdefghij\r\n')); - parser.execute(new Buffer('+test\r')); - parser.execute(new Buffer('\n:100')); - parser.execute(new Buffer('\r\n-Error message')); - parser.execute(new Buffer('\r\n*1\r\n$17\r\nThe force')); - assert.strictEqual(reply_count, 0); - parser.execute(new Buffer(' awakens\r\n$5')); - assert.strictEqual(reply_count, 1); - }); - - it('return normal errors', function () { - var parser = new Parser(); - var reply_count = 0; - function check_reply(reply) { - assert.equal(reply.message, 'Error message'); - reply_count++; - } - parser.send_error = check_reply; - - parser.execute(new Buffer('-Error ')); - parser.execute(new Buffer('message\r\n*3\r\n$17\r\nThe force')); - assert.strictEqual(reply_count, 1); - parser.execute(new Buffer(' awakens\r\n$5')); - assert.strictEqual(reply_count, 1); - }); - - it('return null for empty arrays and empty bulk strings', function () { - var parser = new Parser(); - var reply_count = 0; - function check_reply(reply) { - assert.equal(reply, null); - reply_count++; - } - parser.send_reply = check_reply; - - parser.execute(new Buffer('$-1\r\n*-')); - assert.strictEqual(reply_count, 1); - parser.execute(new Buffer('1')); - assert.strictEqual(reply_count, 1); - parser.execute(new Buffer('\r\n$-')); - assert.strictEqual(reply_count, 2); - }); - - it('return value even if all chunks are only 1 character long', function () { - var parser = new Parser(); - var reply_count = 0; - function check_reply(reply) { - assert.equal(reply, 1); - reply_count++; - } - parser.send_reply = check_reply; - - parser.execute(new Buffer(':')); - assert.strictEqual(reply_count, 0); - parser.execute(new Buffer('1')); - assert.strictEqual(reply_count, 0); - parser.execute(new Buffer('\r')); - assert.strictEqual(reply_count, 0); - parser.execute(new Buffer('\n')); - assert.strictEqual(reply_count, 1); - }); - - it('do not return before \\r\\n', function () { - var parser = new Parser(); - var reply_count = 0; - function check_reply(reply) { - assert.equal(reply, 1); - reply_count++; - } - parser.send_reply = check_reply; - - parser.execute(new Buffer(':1\r\n:')); - assert.strictEqual(reply_count, 1); - parser.execute(new Buffer('1')); - assert.strictEqual(reply_count, 1); - parser.execute(new Buffer('\r')); - assert.strictEqual(reply_count, 1); - parser.execute(new Buffer('\n')); - assert.strictEqual(reply_count, 2); - }); - - it('return data as buffer if requested', function () { - var parser = new Parser(true); - var reply_count = 0; - function check_reply(reply) { - if (Array.isArray(reply)) { - reply = reply[0]; - } - assert(Buffer.isBuffer(reply)); - assert.strictEqual(reply.inspect(), new Buffer('test').inspect()); - reply_count++; - } - parser.send_reply = check_reply; - parser.execute(new Buffer('+test\r\n')); - assert.strictEqual(reply_count, 1); - parser.execute(new Buffer('$4\r\ntest\r\n')); - assert.strictEqual(reply_count, 2); - parser.execute(new Buffer('*1\r\n$4\r\ntest\r\n')); - assert.strictEqual(reply_count, 3); - }); - - it('regression test v.2.4.1', function () { - var parser = new Parser(true); - var reply_count = 0; - var entries = ['test test ', 'test test test test ', '1234']; - function check_reply(reply) { - assert.strictEqual(reply.toString(), entries[reply_count]); - reply_count++; - } - parser.send_reply = check_reply; - parser.execute(new Buffer('$10\r\ntest ')); - assert.strictEqual(reply_count, 0); - parser.execute(new Buffer('test \r\n$20\r\ntest test test test \r\n:1234\r')); - assert.strictEqual(reply_count, 2); - parser.execute(new Buffer('\n')); - assert.strictEqual(reply_count, 3); - }); - }); - }); -}); From e89bcec1c2e72d31d9123b26974e4e29f898eb2b Mon Sep 17 00:00:00 2001 From: Ruben Bridgewater Date: Sun, 6 Dec 2015 03:50:18 +0100 Subject: [PATCH 0527/1748] Deprecate and warn on null / undefined arguments --- README.md | 1 + index.js | 60 +++++++++++++++++++++---------------- lib/command.js | 3 +- test/commands/incr.spec.js | 1 - test/commands/set.spec.js | 7 ++--- test/commands/setex.spec.js | 10 ++----- test/connection.spec.js | 2 +- 7 files changed, 43 insertions(+), 41 deletions(-) diff --git a/README.md b/README.md index 7cc110f0a17..42857e52e82 100644 --- a/README.md +++ b/README.md @@ -211,6 +211,7 @@ limits total amount of connection tries. Setting this to 1 will prevent any reco * `auth_pass`: *null*; If set, client will run redis auth command on connect. * `family`: *IPv4*; You can force using IPv6 if you set the family to 'IPv6'. See Node.js [net](https://nodejs.org/api/net.html) or [dns](https://nodejs.org/api/dns.html) modules how to use the family type. * `disable_resubscribing`: *false*; If set to `true`, a client won't resubscribe after disconnecting +* `to_empty_string`: *null*; convert any undefined or null command argument to an empty string. Be careful using this * `rename_commands`: *null*; pass a object with renamed commands to use those instead of the original functions. See the [redis security topics](http://redis.io/topics/security) for more info. * `tls`: an object containing options to pass to [tls.connect](http://nodejs.org/api/tls.html#tls_tls_connect_port_host_options_callback), to set up a TLS connection to Redis (if, for example, it is set up to be accessible via a tunnel). diff --git a/index.js b/index.js index 3043a556271..179902aedfb 100644 --- a/index.js +++ b/index.js @@ -719,39 +719,49 @@ RedisClient.prototype.send_command = function (command, args, callback) { callback = process.domain.bind(callback); } - if (command === 'set' || command === 'setex') { - // if the value is undefined or null and command is set or setx, need not to send message to redis - if (args[args.length - 1] === undefined || args[args.length - 1] === null) { - command = command.toUpperCase(); - err = new Error('send_command: ' + command + ' value must not be undefined or null'); - err.command = command; - this.callback_emit_error(callback, err); - // Singal no buffering - return true; - } - } - for (i = 0; i < args.length; i += 1) { - if (Buffer.isBuffer(args[i])) { + if (typeof args[i] === 'string') { + // 30000 seemed to be a good value to switch to buffers after testing and checking the pros and cons + if (args[i].length > 30000) { + big_data = true; + args[i] = new Buffer(args[i], 'utf8'); + if (this.pipeline !== 0) { + this.pipeline += 2; + this.writeDefault = this.writeBuffers; + } + } + } else if (args[i] === null) { + if (!this.options.to_empty_string) { // > 2 // ( 2 + 3 where 3 stands for both, null and undefined and 3 for null) + console.warn( + 'node_redis: Deprecated: The %s command contains a "null" argument.\n' + + 'This is converted to a "null" string now and will return an error from v.3.0 on.\n' + + 'If you wish to convert null to an empty string instead, please use the "to_empty_string" option.', command.toUpperCase() + ); + args[i] = 'null'; // Backwards compatible :/ + } else { + args[i] = ''; + } + } else if (typeof args[i] === 'object') { // Buffer.isBuffer(args[i])) { buffer_args = true; if (this.pipeline !== 0) { this.pipeline += 2; this.writeDefault = this.writeBuffers; } - } else if (typeof args[i] !== 'string') { - args[i] = String(args[i]); - // 30000 seemed to be a good value to switch to buffers after testing and checking the pros and cons - } else if (args[i].length > 30000) { - big_data = true; - args[i] = new Buffer(args[i]); - if (this.pipeline !== 0) { - this.pipeline += 2; - this.writeDefault = this.writeBuffers; + } else if (args[i] === undefined) { + if (!this.options.to_empty_string) { + console.warn( + 'node_redis: Deprecated: The %s command contains a "undefined" argument.\n' + + 'This is converted to a "undefined" string now and will return an error from v.3.0 on.\n' + + 'If you wish to convert undefined to an empty string instead, please use the "to_empty_string" option.', command.toUpperCase() + ); + args[i] = 'undefined'; // Backwards compatible :/ + } else { + args[i] = ''; } } } - command_obj = new Command(command, args, false, buffer_args, callback); + command_obj = new Command(command, args, buffer_args, callback); if (!this.ready && !this.send_anyway || !stream.writable) { if (this.closing || !this.enable_offline_queue) { @@ -812,9 +822,9 @@ RedisClient.prototype.send_command = function (command, args, callback) { for (i = 0; i < args.length; i += 1) { arg = args[i]; - if (typeof arg === 'string') { + if (typeof arg !== 'object') { // string; number; boolean this.write('$' + Buffer.byteLength(arg) + '\r\n' + arg + '\r\n'); - } else { + } else { // buffer this.write('$' + arg.length + '\r\n'); this.write(arg); this.write('\r\n'); diff --git a/lib/command.js b/lib/command.js index 4aa795b696b..d04052ff6d3 100644 --- a/lib/command.js +++ b/lib/command.js @@ -2,10 +2,9 @@ // This Command constructor is ever so slightly faster than using an object literal, but more importantly, using // a named constructor helps it show up meaningfully in the V8 CPU profiler and in heap snapshots. -function Command(command, args, sub_command, buffer_args, callback) { +function Command(command, args, buffer_args, callback) { this.command = command; this.args = args; - this.sub_command = sub_command; this.buffer_args = buffer_args; this.callback = callback; } diff --git a/test/commands/incr.spec.js b/test/commands/incr.spec.js index 2f7daae0d12..1910bd2943f 100644 --- a/test/commands/incr.spec.js +++ b/test/commands/incr.spec.js @@ -43,7 +43,6 @@ describe("The 'incr' method", function () { describe("when connected and a value in Redis", function () { var client; - // Also, why tf were these disabled for hiredis? They work just fine. before(function (done) { /* 9007199254740992 -> 9007199254740992 diff --git a/test/commands/set.spec.js b/test/commands/set.spec.js index 7a508920a1a..4868ed06e6d 100644 --- a/test/commands/set.spec.js +++ b/test/commands/set.spec.js @@ -113,7 +113,7 @@ describe("The 'set' method", function () { describe("with undefined 'key' and missing 'value' parameter", function () { it("emits an error without callback", function (done) { client.on('error', function (err) { - assert.equal(err.message, 'send_command: SET value must not be undefined or null'); + assert.equal(err.message, "ERR wrong number of arguments for 'set' command"); assert.equal(err.command, 'SET'); done(); }); @@ -132,13 +132,10 @@ describe("The 'set' method", function () { it("emit an error without any parameters", function (done) { client.once("error", function (err) { - assert.equal(err.message, 'send_command: SET value must not be undefined or null'); + assert.equal(err.message, "ERR wrong number of arguments for 'set' command"); assert.equal(err.command, 'SET'); done(); }); - - // This was not supported not to throw earlier and was added by the test refactoring - // https://github.com/NodeRedis/node_redis/commit/eaca486ab1aecd1329f7452ad2f2255b1263606f client.set(); }); }); diff --git a/test/commands/setex.spec.js b/test/commands/setex.spec.js index 47f616253e9..575c7e854d5 100644 --- a/test/commands/setex.spec.js +++ b/test/commands/setex.spec.js @@ -21,18 +21,14 @@ describe("The 'setex' method", function () { it('sets a key with an expiry', function (done) { client.setex(["setex key", "100", "setex val"], helper.isString("OK")); - client.exists(["setex key"], helper.isNumber(1)); + var buffering = client.exists(["setex key"], helper.isNumber(1)); + assert(typeof buffering === 'boolean'); client.ttl(['setex key'], function (err, ttl) { - assert.ok(ttl > 0); + assert(ttl > 0); return done(); }); }); - it('returns an error if no value is provided', function (done) { - var buffering = client.SETEX(["setex key", "100", undefined], helper.isError(done)); - assert(typeof buffering === 'boolean'); - }); - afterEach(function () { client.end(true); }); diff --git a/test/connection.spec.js b/test/connection.spec.js index 1ce5b61698f..9e98c3732e5 100644 --- a/test/connection.spec.js +++ b/test/connection.spec.js @@ -90,7 +90,7 @@ describe("connection tests", function () { }); client.on("reconnecting", function (params) { - client.end(); + client.end(true); setTimeout(done, 100); }); }); From f6f5d91709519ec0b4eee4468d2a85fb96e45071 Mon Sep 17 00:00:00 2001 From: Ruben Bridgewater Date: Sun, 6 Dec 2015 03:51:18 +0100 Subject: [PATCH 0528/1748] Deprecate .end() by making the flush parameter mandatory and fix the docs --- README.md | 16 ++++++++++------ index.js | 18 ++++++++++++------ 2 files changed, 22 insertions(+), 12 deletions(-) diff --git a/README.md b/README.md index 42857e52e82..aa448a09bc9 100644 --- a/README.md +++ b/README.md @@ -246,12 +246,13 @@ NOTE: Your call to `client.auth()` should not be inside the ready handler. If you are doing this wrong, `client` will emit an error that looks something like this `Error: Ready check failed: ERR operation not permitted`. -## client.end([flush]) +## client.end(flush) Forcibly close the connection to the Redis server. Note that this does not wait until all replies have been parsed. If you want to exit cleanly, call `client.quit()` to send the `QUIT` command after you have handled all replies. -If flush is set to true, all still running commands will be rejected instead of ignored after using `.end`. +You should set flush to true, if you are not absolutly sure you do not care about any other commands. +If you set flush to false all still running commands will silently fail. This example closes the connection to the Redis server before the replies have been read. You probably don't want to do this: @@ -260,12 +261,15 @@ want to do this: var redis = require("redis"), client = redis.createClient(); -client.set("foo_rand000000000000", "some fantastic value"); -client.end(); // No further commands will be processed -client.get("foo_rand000000000000", function (err, reply) { - // This won't be called anymore, since flush has not been set to true! +client.set("foo_rand000000000000", "some fantastic value", function (err, reply) { + // This will either result in an error (flush parameter is set to true) + // or will silently fail and this callback will not be called at all (flush set to false) console.log(err); }); +client.end(true); // No further commands will be processed +client.get("foo_rand000000000000", function (err, reply) { + console.log(err); // => 'The connection has already been closed.' +}); ``` `client.end()` without the flush parameter should not be used in production! diff --git a/index.js b/index.js index 179902aedfb..bbc6dd439e3 100644 --- a/index.js +++ b/index.js @@ -550,7 +550,7 @@ RedisClient.prototype.connection_gone = function (why) { error.code = 'CONNECTION_BROKEN'; this.flush_and_error(error); this.emit('error', error); - this.end(); + this.end(false); return; } @@ -900,6 +900,17 @@ RedisClient.prototype.pub_sub_command = function (command_obj) { }; RedisClient.prototype.end = function (flush) { + // Flush queue if wanted + if (flush) { + this.flush_and_error(new Error("The command can't be processed. The connection has already been closed.")); + } else if (flush === undefined) { + console.warn( + 'node_redis: Using .end() without the flush parameter is deprecated. ' + + 'Please check the doku (https://github.com/NodeRedis/node_redis) and explictly use flush.\n' + + 'This will throw from v.3.0.0 on.' + ); + } + this.stream._events = {}; // Clear retry_timer @@ -909,11 +920,6 @@ RedisClient.prototype.end = function (flush) { } this.stream.on('error', noop); - // Flush queue if wanted - if (flush) { - this.flush_and_error(new Error("The command can't be processed. The connection has already been closed.")); - } - this.connected = false; this.ready = false; this.closing = true; From 835dc404b72085aa3898728ecb568867bea2b072 Mon Sep 17 00:00:00 2001 From: Ruben Bridgewater Date: Sun, 6 Dec 2015 03:52:35 +0100 Subject: [PATCH 0529/1748] Explicitly remove a undefined callback from any multi command --- .gitignore | 3 ++- index.js | 52 ++++++++++++++++++++++++++++++++++++---------------- 2 files changed, 38 insertions(+), 17 deletions(-) diff --git a/.gitignore b/.gitignore index 6df089991f5..2290a8ec51d 100644 --- a/.gitignore +++ b/.gitignore @@ -5,4 +5,5 @@ coverage *.log *.rdb stunnel.conf -stunnel.pid \ No newline at end of file +stunnel.pid +*.out diff --git a/index.js b/index.js index bbc6dd439e3..f8149aed2a5 100644 --- a/index.js +++ b/index.js @@ -658,7 +658,7 @@ RedisClient.prototype.return_reply = function (reply) { } else if (type === 'pmessage') { this.emit('pmessage', reply[1].toString(), reply[2], reply[3]); // pattern, channel, message } else if (type === 'subscribe' || type === 'unsubscribe' || type === 'psubscribe' || type === 'punsubscribe') { - if (reply[2] === 0) { + if (reply[2].toString() === '0') { this.pub_sub_mode = false; debug('All subscriptions removed, exiting pub/sub mode'); } else { @@ -1127,6 +1127,7 @@ Multi.prototype.exec_transaction = function (callback) { var self = this; var len = this.queue.length; var cb; + var args_len = 1; this.errors = []; this.callback = callback; this._client.cork(len + 2); @@ -1136,9 +1137,11 @@ Multi.prototype.exec_transaction = function (callback) { for (var index = 0; index < len; index++) { var args = this.queue.get(index).slice(0); var command = args.shift(); - if (typeof args[args.length - 1] === 'function') { + args_len = args.length - 1; + if (typeof args[args_len] === 'function' || typeof args[args_len] === 'undefined') { cb = args.pop(); } else { + // Explicitly set the callback to undefined. Otherwise we might have the callback from the command earlier cb = undefined; } // Keep track of who wants buffer responses: @@ -1213,14 +1216,12 @@ Multi.prototype.callback = function (cb, i) { return function (err, res) { if (err) { self.results[i] = err; + // Add the position to the error + self.results[i].position = i; } else { self.results[i] = res; } - if (cb) { - cb(err, res); - } - // Do not emit an error here. Otherwise each error would result in one emit. - // The errors will be returned in the result anyway + cb(err, res); }; }; @@ -1229,6 +1230,25 @@ Multi.prototype.exec = Multi.prototype.EXEC = Multi.prototype.exec_batch = funct var self = this; var index = 0; var args; + var args_len = 1; + var callbackWithoutCB = function (err, res) { + if (err) { + self.results.push(err); + // Add the position to the error + var i = self.results.length - 1; + self.results[i].position = i; + } else { + self.results.push(res); + } + // Do not emit an error here. Otherwise each error would result in one emit. + // The errors will be returned in the result anyway + }; + var lastCallback = function (cb) { + return function (err, res) { + cb(err, res); + callback(null, self.results); + }; + }; if (len === 0) { if (callback) { // The execution order won't be obtained in this case @@ -1238,21 +1258,21 @@ Multi.prototype.exec = Multi.prototype.EXEC = Multi.prototype.exec_batch = funct } return true; } - this.results = new Array(len); + this.results = []; this._client.cork(len); - var lastCallback = function (cb) { - return function (err, res) { - cb(err, res); - callback(null, self.results); - }; - }; while (args = this.queue.shift()) { var command = args.shift(); var cb; - if (typeof args[args.length - 1] === 'function') { + // Improve the callback function generation by using new Function + // check https://github.com/petkaantonov/bluebird/blob/master/src/promisify.js + args_len = args.length - 1; + if (typeof args[args_len] === 'function') { cb = this.callback(args.pop(), index); } else { - cb = this.callback(undefined, index); + cb = callbackWithoutCB; + if (typeof args[args_len] === 'undefined') { + args.pop(); + } } if (callback && index === len - 1) { cb = lastCallback(cb); From 637e59ffc987f9c0bf3c2f86a8d7f8d49713b80c Mon Sep 17 00:00:00 2001 From: Ruben Bridgewater Date: Wed, 9 Dec 2015 03:27:21 +0100 Subject: [PATCH 0530/1748] Improve arguments parsing --- index.js | 59 ++++++++++++++++++++++++++------------------------------ 1 file changed, 27 insertions(+), 32 deletions(-) diff --git a/index.js b/index.js index f8149aed2a5..a5566d745f4 100644 --- a/index.js +++ b/index.js @@ -948,20 +948,17 @@ commands.list.forEach(function (command) { RedisClient.prototype[command.toUpperCase()] = RedisClient.prototype[command] = function (key, arg, callback) { if (Array.isArray(key)) { return this.send_command(command, key, arg); - } - if (Array.isArray(arg)) { + } else if (Array.isArray(arg)) { arg = [key].concat(arg); return this.send_command(command, arg, callback); } - // Speed up the common case + // This has to be inlined, otherwise the arguments leak var len = arguments.length; - if (len === 2) { - return this.send_command(command, [key, arg]); - } - if (len === 3) { - return this.send_command(command, [key, arg, callback]); + var arr = new Array(len); + for (var i = 0; i < len; i += 1) { + arr[i] = arguments[i]; } - return this.send_command(command, utils.to_array(arguments)); + return this.send_command(command, arr); }; Multi.prototype[command.toUpperCase()] = Multi.prototype[command] = function (key, arg, callback) { @@ -976,15 +973,12 @@ commands.list.forEach(function (command) { } this.queue.push([command, key].concat(arg)); } else { - // Speed up the common case var len = arguments.length; - if (len === 2) { - this.queue.push([command, key, arg]); - } else if (len === 3) { - this.queue.push([command, key, arg, callback]); - } else { - this.queue.push([command].concat(utils.to_array(arguments))); + var arr = new Array(len); + for (var i = 0; i < len; i += 1) { + arr[i] = arguments[i]; } + this.queue.push([command].concat(arr)); } return this; }; @@ -1051,15 +1045,9 @@ RedisClient.prototype.hmset = RedisClient.prototype.HMSET = function (key, args, if (Array.isArray(args)) { return this.send_command('hmset', [key].concat(args), callback); } - if (typeof args === 'object') { + if (typeof args === 'object' && (typeof callback === 'function' || typeof callback === 'undefined')) { // User does: client.hmset(key, {key1: val1, key2: val2}) // assuming key is a string, i.e. email address - - // if key is a number, i.e. timestamp, convert to string - // TODO: This seems random and no other command get's the key converted => either all or none should behave like this - if (typeof key !== 'string') { - key = key.toString(); - } tmp_args = [key]; var fields = Object.keys(args); while (field = fields.shift()) { @@ -1067,7 +1055,12 @@ RedisClient.prototype.hmset = RedisClient.prototype.HMSET = function (key, args, } return this.send_command('hmset', tmp_args, callback); } - return this.send_command('hmset', utils.to_array(arguments)); + var len = arguments.length; + var tmp_args = new Array(len); + for (var i = 0; i < len; i += 1) { + tmp_args[i] = arguments[i]; + } + return this.send_command('hmset', tmp_args); }; Multi.prototype.hmset = Multi.prototype.HMSET = function (key, args, callback) { @@ -1096,8 +1089,12 @@ Multi.prototype.hmset = Multi.prototype.HMSET = function (key, args, callback) { tmp_args.push(callback); } } else { - tmp_args = utils.to_array(arguments); - tmp_args.unshift('hmset'); + var len = arguments.length; + var tmp_args = new Array(len); + tmp_args[0] = 'hmset'; + for (var i = 0; i < len; i += 1) { + tmp_args[i + 1] = arguments[i]; + } } this.queue.push(tmp_args); return this; @@ -1231,7 +1228,7 @@ Multi.prototype.exec = Multi.prototype.EXEC = Multi.prototype.exec_batch = funct var index = 0; var args; var args_len = 1; - var callbackWithoutCB = function (err, res) { + var callback_without_own_cb = function (err, res) { if (err) { self.results.push(err); // Add the position to the error @@ -1243,7 +1240,7 @@ Multi.prototype.exec = Multi.prototype.EXEC = Multi.prototype.exec_batch = funct // Do not emit an error here. Otherwise each error would result in one emit. // The errors will be returned in the result anyway }; - var lastCallback = function (cb) { + var last_callback = function (cb) { return function (err, res) { cb(err, res); callback(null, self.results); @@ -1263,19 +1260,17 @@ Multi.prototype.exec = Multi.prototype.EXEC = Multi.prototype.exec_batch = funct while (args = this.queue.shift()) { var command = args.shift(); var cb; - // Improve the callback function generation by using new Function - // check https://github.com/petkaantonov/bluebird/blob/master/src/promisify.js args_len = args.length - 1; if (typeof args[args_len] === 'function') { cb = this.callback(args.pop(), index); } else { - cb = callbackWithoutCB; + cb = callback_without_own_cb; if (typeof args[args_len] === 'undefined') { args.pop(); } } if (callback && index === len - 1) { - cb = lastCallback(cb); + cb = last_callback(cb); } this._client.send_command(command, args, cb); index++; From e83230fd73100554e8d93de45d3d607a023e61c4 Mon Sep 17 00:00:00 2001 From: Ruben Bridgewater Date: Wed, 16 Dec 2015 15:39:27 +0100 Subject: [PATCH 0531/1748] Test the latest nodejs 4 and 5 versions --- .travis.yml | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/.travis.yml b/.travis.yml index 6fad5fb6c30..c87df9f781b 100644 --- a/.travis.yml +++ b/.travis.yml @@ -11,6 +11,6 @@ addons: node_js: - "0.10" - "0.12" - - "4.0" - - "5.0" + - "4" + - "5" after_success: npm run coveralls From 52f987321ac8a38d92df89ca728ef3590df94c29 Mon Sep 17 00:00:00 2001 From: Ruben Bridgewater Date: Wed, 30 Dec 2015 16:08:46 +0100 Subject: [PATCH 0532/1748] Deprecate stuff --- index.js | 53 +++++++++++++++++++++++++++-------------------------- 1 file changed, 27 insertions(+), 26 deletions(-) diff --git a/index.js b/index.js index a5566d745f4..3ce06cb409d 100644 --- a/index.js +++ b/index.js @@ -55,9 +55,14 @@ function RedisClient (options) { this.connection_id = ++connection_id; this.connected = false; this.ready = false; - this.connections = 0; if (options.socket_nodelay === undefined) { options.socket_nodelay = true; + } else if (!options.socket_nodelay) { // Only warn users with this set to false + console.warn( + 'node_redis: socket_nodelay is deprecated and will be removed in v.3.0.0.\n' + + 'Setting socket_nodelay to false likely results in a reduced throughput. Please use .batch to buffer commands and use pipelining.\n' + + 'If you are sure you rely on the NAGLE-algorithm you can activate it by calling client.stream.setNoDelay(false) instead.' + ); } if (options.socket_keepalive === undefined) { options.socket_keepalive = true; @@ -730,34 +735,31 @@ RedisClient.prototype.send_command = function (command, args, callback) { this.writeDefault = this.writeBuffers; } } - } else if (args[i] === null) { - if (!this.options.to_empty_string) { // > 2 // ( 2 + 3 where 3 stands for both, null and undefined and 3 for null) + } else if (typeof args[i] === 'object') { // Checking for object instead of Buffer.isBuffer helps us finding data types that we can't handle properly + if (args[i] instanceof Date) { // Accept dates as valid input + args[i] = args[i].toString(); + // Add this to parse_arguments. + } else if (args[i] === null) { console.warn( 'node_redis: Deprecated: The %s command contains a "null" argument.\n' + 'This is converted to a "null" string now and will return an error from v.3.0 on.\n' + - 'If you wish to convert null to an empty string instead, please use the "to_empty_string" option.', command.toUpperCase() + 'Please handle this in your code to make sure everything works as you intended it to behave.', command.toUpperCase() ); args[i] = 'null'; // Backwards compatible :/ } else { - args[i] = ''; - } - } else if (typeof args[i] === 'object') { // Buffer.isBuffer(args[i])) { - buffer_args = true; - if (this.pipeline !== 0) { - this.pipeline += 2; - this.writeDefault = this.writeBuffers; - } - } else if (args[i] === undefined) { - if (!this.options.to_empty_string) { - console.warn( - 'node_redis: Deprecated: The %s command contains a "undefined" argument.\n' + - 'This is converted to a "undefined" string now and will return an error from v.3.0 on.\n' + - 'If you wish to convert undefined to an empty string instead, please use the "to_empty_string" option.', command.toUpperCase() - ); - args[i] = 'undefined'; // Backwards compatible :/ - } else { - args[i] = ''; + buffer_args = true; + if (this.pipeline !== 0) { + this.pipeline += 2; + this.writeDefault = this.writeBuffers; + } } + } else if (typeof args[i] === 'undefined') { + console.warn( + 'node_redis: Deprecated: The %s command contains a "undefined" argument.\n' + + 'This is converted to a "undefined" string now and will return an error from v.3.0 on.\n' + + 'Please handle this in your code to make sure everything works as you intended it to behave.', command.toUpperCase() + ); + args[i] = 'undefined'; // Backwards compatible :/ } } @@ -903,11 +905,10 @@ RedisClient.prototype.end = function (flush) { // Flush queue if wanted if (flush) { this.flush_and_error(new Error("The command can't be processed. The connection has already been closed.")); - } else if (flush === undefined) { + } else if (arguments.length === 0) { console.warn( - 'node_redis: Using .end() without the flush parameter is deprecated. ' + - 'Please check the doku (https://github.com/NodeRedis/node_redis) and explictly use flush.\n' + - 'This will throw from v.3.0.0 on.' + 'node_redis: Using .end() without the flush parameter is deprecated and throws from v.3.0.0 on.\n' + + 'Please check the doku (https://github.com/NodeRedis/node_redis) and explictly use flush.' ); } From 58ddd5148906e3152eb61ee449bd5c11dbc7601c Mon Sep 17 00:00:00 2001 From: Ruben Bridgewater Date: Wed, 30 Dec 2015 16:10:50 +0100 Subject: [PATCH 0533/1748] Always refresh the server_info after running info; Remove proper auth support for redis < 2.6.11 --- index.js | 206 ++++++++++++++++++++++++------------------------------- 1 file changed, 91 insertions(+), 115 deletions(-) diff --git a/index.js b/index.js index 3ce06cb409d..465ed414208 100644 --- a/index.js +++ b/index.js @@ -97,6 +97,7 @@ function RedisClient (options) { this.auth_pass = options.auth_pass; this.selected_db = null; // Save the selected db here, used when reconnecting this.old_state = null; + this.send_anyway = false; this.pipeline = 0; this.options = options; // Init parser @@ -172,6 +173,15 @@ RedisClient.prototype.create_stream = function () { this.stream.on('drain', function () { self.drain(); }); + + if (this.options.socket_nodelay) { + this.stream.setNoDelay(); + } + + // Fire the command before redis is connected to be sure it's the first fired command + if (typeof this.auth_pass === 'string') { + this.do_auth(); + } }; RedisClient.prototype.handle_reply = function (reply, command) { @@ -248,57 +258,26 @@ RedisClient.prototype.on_error = function (err) { }; var noPasswordIsSet = /no password is set/; -var loading = /LOADING/; RedisClient.prototype.do_auth = function () { var self = this; - debug('Sending auth to ' + self.address + ' id ' + self.connection_id); - self.send_anyway = true; - self.send_command('auth', [this.auth_pass], function (err, res) { + this.send_anyway = true; + this.send_command('auth', [this.auth_pass], function (err, res) { if (err) { - /* istanbul ignore if: this is almost impossible to test */ - if (loading.test(err.message)) { - // If redis is still loading the db, it will not authenticate and everything else will fail - debug('Redis still loading, trying to authenticate later'); - setTimeout(function () { - self.do_auth(); - }, 333); - return; - } else if (noPasswordIsSet.test(err.message)) { + if (noPasswordIsSet.test(err.message)) { debug('Warning: Redis server does not require a password, but a password was supplied.'); err = null; res = 'OK'; - } else if (self.auth_callback) { - self.auth_callback(err); - self.auth_callback = null; - return; } else { self.emit('error', err); - return; } - } - - res = res.toString(); - debug('Auth succeeded ' + self.address + ' id ' + self.connection_id); - - if (self.auth_callback) { - self.auth_callback(null, res); - self.auth_callback = null; - } - - // Now we are really connected - self.emit('connect'); - self.initialize_retry_vars(); - - if (self.options.no_ready_check) { - self.on_ready(); } else { - self.ready_check(); + debug('Auth succeeded ' + self.address + ' id ' + self.connection_id); } }); - self.send_anyway = false; + this.send_anyway = false; }; RedisClient.prototype.on_connect = function () { @@ -306,31 +285,24 @@ RedisClient.prototype.on_connect = function () { this.connected = true; this.ready = false; - this.connections += 1; this.emitted_end = false; - if (this.options.socket_nodelay) { - this.stream.setNoDelay(); - } this.stream.setKeepAlive(this.options.socket_keepalive); this.stream.setTimeout(0); - if (typeof this.auth_pass === 'string') { - this.do_auth(); - } else { - this.emit('connect'); - this.initialize_retry_vars(); + this.emit('connect'); + this.initialize_retry_vars(); - if (this.options.no_ready_check) { - this.on_ready(); - } else { - this.ready_check(); - } + if (this.options.no_ready_check) { + this.on_ready(); + } else { + this.ready_check(); } }; RedisClient.prototype.on_ready = function () { var self = this; + debug('on_ready called ' + this.address + ' id ' + this.connection_id); this.ready = true; if (this.old_state !== null) { @@ -358,8 +330,8 @@ RedisClient.prototype.on_ready = function () { } this.cork = cork; - // magically restore any modal commands from a previous connection - if (this.selected_db !== null) { + // restore modal commands from previous connection + if (this.selected_db !== undefined) { // this trick works if and only if the following send_command // never goes into the offline queue var pub_sub_mode = this.pub_sub_mode; @@ -401,84 +373,43 @@ RedisClient.prototype.on_ready = function () { RedisClient.prototype.on_info_cmd = function (err, res) { if (err) { if (err.message === "ERR unknown command 'info'") { - this.server_info = {}; this.on_ready(); return; - } else { - err.message = 'Ready check failed: ' + err.message; - this.emit('error', err); - return; - } + } + err.message = 'Ready check failed: ' + err.message; + this.emit('error', err); + return; } /* istanbul ignore if: some servers might not respond with any info data. This is just a safety check that is difficult to test */ if (!res) { debug('The info command returned without any data.'); - this.server_info = {}; this.on_ready(); return; } - var obj = {}; - var lines = res.toString().split('\r\n'); - var i = 0; - var key = 'db' + i; - var line, retry_time, parts, sub_parts; - - for (i = 0; i < lines.length; i++) { - parts = lines[i].split(':'); - if (parts[1]) { - obj[parts[0]] = parts[1]; - } - } - - obj.versions = []; - /* istanbul ignore else: some redis servers do not send the version */ - if (obj.redis_version) { - obj.redis_version.split('.').forEach(function (num) { - obj.versions.push(+num); - }); - } - - while (obj[key]) { - parts = obj[key].split(','); - obj[key] = {}; - while (line = parts.pop()) { - sub_parts = line.split('='); - obj[key][sub_parts[0]] = +sub_parts[1]; - } - i++; - key = 'db' + i; - } - - // Expose info key/vals to users - this.server_info = obj; - - if (!obj.loading || obj.loading === '0') { + if (!this.server_info.loading || this.server_info.loading === '0') { debug('Redis server ready.'); this.on_ready(); - } else { - retry_time = obj.loading_eta_seconds * 1000; - if (retry_time > 1000) { - retry_time = 1000; - } - debug('Redis server still loading, trying again in ' + retry_time); - setTimeout(function (self) { - self.ready_check(); - }, retry_time, this); + return; + } + + var retry_time = +this.server_info.loading_eta_seconds * 1000; + if (retry_time > 1000) { + retry_time = 1000; } + debug('Redis server still loading, trying again in ' + retry_time); + setTimeout(function (self) { + self.ready_check(); + }, retry_time, this); }; RedisClient.prototype.ready_check = function () { var self = this; - debug('Checking server ready state...'); - - this.send_anyway = true; // secret flag to send_command to send something even if not 'ready' this.info(function (err, res) { self.on_info_cmd(err, res); }); - this.send_anyway = false; }; RedisClient.prototype.send_offline_queue = function () { @@ -531,7 +462,7 @@ RedisClient.prototype.connection_gone = function (why) { this.old_state = state; this.monitoring = false; this.pub_sub_mode = false; - this.selected_db = null; + this.selected_db = undefined; } // since we are collapsing end and close, users don't expect to be called twice @@ -1010,6 +941,53 @@ RedisClient.prototype.select = RedisClient.prototype.SELECT = function (db, call }); }; +// Store db in this.select_db to restore it on reconnect +RedisClient.prototype.info = RedisClient.prototype.INFO = function (callback) { + var self = this; + this.send_anyway = true; + var tmp = this.send_command('info', [], function (err, res) { + if (res) { + var obj = {}; + var lines = res.toString().split('\r\n'); + var line, parts, sub_parts; + + for (var i = 0; i < lines.length; i++) { + parts = lines[i].split(':'); + if (parts[1]) { + if (parts[0].indexOf('db') === 0) { + sub_parts = parts[1].split(','); + obj[parts[0]] = {}; + while (line = sub_parts.pop()) { + line = line.split('='); + obj[parts[0]][line[0]] = +line[1]; + } + } else { + obj[parts[0]] = parts[1]; + } + } + } + obj.versions = []; + /* istanbul ignore else: some redis servers do not send the version */ + if (obj.redis_version) { + obj.redis_version.split('.').forEach(function (num) { + obj.versions.push(+num); + }); + } + // Expose info key/vals to users + self.server_info = obj; + } else { + self.server_info = {}; + } + if (typeof callback === 'function') { + callback(err, res); + } else if (err) { + self.emit('error', err); + } + }); + this.send_anyway = false; + return tmp; +}; + RedisClient.prototype.callback_emit_error = function (callback, err) { if (callback) { setImmediate(function () { @@ -1030,12 +1008,10 @@ RedisClient.prototype.auth = RedisClient.prototype.AUTH = function (pass, callba } this.auth_pass = pass; debug('Saving auth as ' + this.auth_pass); - // Only run the callback once. So do not safe it if already connected - if (this.connected) { - return this.send_command('auth', [this.auth_pass], callback); - } - this.auth_callback = callback; - return true; + this.send_anyway = true; + var tmp = this.send_command('auth', [pass], callback); + this.send_anyway = false; + return tmp; }; RedisClient.prototype.hmset = RedisClient.prototype.HMSET = function (key, args, callback) { From a4285c156c5b8964d92a36bd7f361a6f40e2449a Mon Sep 17 00:00:00 2001 From: Ruben Bridgewater Date: Wed, 30 Dec 2015 16:12:05 +0100 Subject: [PATCH 0534/1748] Parse redis url just like IANA --- index.js | 21 ++++++++++++++++----- 1 file changed, 16 insertions(+), 5 deletions(-) diff --git a/index.js b/index.js index 465ed414208..c8b9f42d0fa 100644 --- a/index.js +++ b/index.js @@ -94,8 +94,8 @@ function RedisClient (options) { this.monitoring = false; this.closing = false; this.server_info = {}; - this.auth_pass = options.auth_pass; - this.selected_db = null; // Save the selected db here, used when reconnecting + this.auth_pass = options.auth_pass || options.password; + this.selected_db = options.db; // Save the selected db here, used when reconnecting this.old_state = null; this.send_anyway = false; this.pipeline = 0; @@ -1265,12 +1265,23 @@ var createClient = function (port_arg, host_arg, options) { } else if (typeof port_arg === 'string' || port_arg && port_arg.url) { options = clone(port_arg.url ? port_arg : host_arg || options); var parsed = URL.parse(port_arg.url || port_arg, true, true); + // [redis:]//[user][:password@][host][:port][/db-number][?db=db-number[&password=bar[&option=value]]] if (parsed.hostname) { if (parsed.auth) { - options.auth_pass = parsed.auth.split(':')[1]; + options.password = parsed.auth.split(':')[1]; } - if (parsed.protocol !== 'redis:') { - throw new Error('Connection string must use the "redis:" protocol'); + if (!/^([a-z]+:)?\/\//i.test(parsed.href)) { + throw new Error('Connection string must use the "redis:" protocol or begin with slashes //'); + } + if (parsed.pathname && parsed.pathname !== '/') { + options.db = parsed.pathname.substr(1); + } + if (parsed.search !== '') { + var elem; + for (elem in parsed.query) { // jshint ignore: line + // If options are passed twice, only the parsed options will be used + options[elem] = parsed.query[elem]; + } } options.host = parsed.hostname; options.port = parsed.port; From d9c815dd8cfa53b201a7b9412c65f48935caad87 Mon Sep 17 00:00:00 2001 From: Ruben Bridgewater Date: Wed, 30 Dec 2015 16:12:52 +0100 Subject: [PATCH 0535/1748] Add retry_unfullfilled_commands option --- index.js | 12 +++++------- 1 file changed, 5 insertions(+), 7 deletions(-) diff --git a/index.js b/index.js index c8b9f42d0fa..05cec202b67 100644 --- a/index.js +++ b/index.js @@ -490,15 +490,13 @@ RedisClient.prototype.connection_gone = function (why) { return; } - // Flush all commands that have not yet returned. We can't handle them appropriatly - if (this.command_queue.length !== 0) { + // Retry commands after a reconnect instead of throwing an error. Use this with caution + if (this.options.retry_unfulfilled_commands) { + this.offline_queue.unshift.apply(this.offline_queue, this.command_queue.toArray()); + this.command_queue.clear(); + } else if (this.command_queue.length !== 0) { error = new Error('Redis connection lost and command aborted in uncertain state. It might have been processed.'); error.code = 'UNCERTAIN_STATE'; - // TODO: Evaluate to add this - // if (this.options.retry_commands) { - // this.offline_queue.unshift(this.command_queue.toArray()); - // error.message = 'Command aborted in uncertain state and queued for next connection.'; - // } this.flush_and_error(error, ['command_queue']); error.message = 'Redis connection lost and commands aborted in uncertain state. They might have been processed.'; this.emit('error', error); From 78b75574e846dcd7c6b9f8faca63338616b0dcee Mon Sep 17 00:00:00 2001 From: greenkeeperio-bot Date: Thu, 31 Dec 2015 00:01:07 +0100 Subject: [PATCH 0536/1748] chore(package): update nyc to version 5.1.1 http://greenkeeper.io/ --- package.json | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/package.json b/package.json index 96f9c0d3aae..43bf680b85a 100644 --- a/package.json +++ b/package.json @@ -35,7 +35,7 @@ "jshint": "^2.8.0", "metrics": "^0.1.9", "mocha": "^2.3.2", - "nyc": "^4.0.1", + "nyc": "^5.1.1", "optional-dev-dependency": "^1.1.0", "tcp-port-used": "^0.1.2", "uuid": "^2.0.1", From 2cd3818ea9ef2a88eaccbcb0f02cfe085f7a4628 Mon Sep 17 00:00:00 2001 From: Ruben Bridgewater Date: Wed, 30 Dec 2015 16:13:15 +0100 Subject: [PATCH 0537/1748] Fix some minor issues --- index.js | 34 ++++++++++++++++------------------ 1 file changed, 16 insertions(+), 18 deletions(-) diff --git a/index.js b/index.js index 05cec202b67..5b493ea621a 100644 --- a/index.js +++ b/index.js @@ -515,12 +515,11 @@ RedisClient.prototype.connection_gone = function (why) { }; RedisClient.prototype.return_error = function (err) { - var command_obj = this.command_queue.shift(), queue_len = this.command_queue.length; - // send_command might have been used wrong => catch those cases too - if (command_obj.command && command_obj.command.toUpperCase) { + var command_obj = this.command_queue.shift(), + queue_len = this.command_queue.length; + + if (command_obj && command_obj.command && command_obj.command.toUpperCase) { err.command = command_obj.command.toUpperCase(); - } else { - err.command = command_obj.command; } var match = err.message.match(utils.err_code); @@ -531,7 +530,7 @@ RedisClient.prototype.return_error = function (err) { this.emit_idle(queue_len); - if (command_obj.callback) { + if (command_obj && command_obj.callback) { command_obj.callback(err); } else { this.emit('error', err); @@ -639,9 +638,9 @@ RedisClient.prototype.send_command = function (command, args, callback) { big_data = false, prefix_keys; - if (args === undefined) { + if (typeof args === 'undefined') { args = []; - } else if (!callback) { + } else if (typeof callback === 'undefined') { if (typeof args[args.length - 1] === 'function') { callback = args.pop(); } else if (typeof args[args.length - 1] === 'undefined') { @@ -689,12 +688,15 @@ RedisClient.prototype.send_command = function (command, args, callback) { 'Please handle this in your code to make sure everything works as you intended it to behave.', command.toUpperCase() ); args[i] = 'undefined'; // Backwards compatible :/ + } else { + args[i] = String(args[i]); } } command_obj = new Command(command, args, buffer_args, callback); - if (!this.ready && !this.send_anyway || !stream.writable) { + // TODO: Replace send_anyway with `commands.hasFlag(command, 'loading') === false` as soon as pub_sub is handled in the result handler + if (this.ready === false && this.send_anyway === false || !stream.writable) { if (this.closing || !this.enable_offline_queue) { command = command.toUpperCase(); if (!this.closing) { @@ -717,7 +719,7 @@ RedisClient.prototype.send_command = function (command, args, callback) { } if (command === 'subscribe' || command === 'psubscribe' || command === 'unsubscribe' || command === 'punsubscribe') { - this.pub_sub_command(command_obj); + this.pub_sub_command(command_obj); // TODO: This has to be moved to the result handler } else if (command === 'monitor') { this.monitoring = true; } else if (command === 'quit') { @@ -740,7 +742,7 @@ RedisClient.prototype.send_command = function (command, args, callback) { // This means that using Buffers in commands is going to be slower, so use Strings if you don't already have a Buffer. command_str = '*' + (args.length + 1) + '\r\n$' + command.length + '\r\n' + command + '\r\n'; - if (!buffer_args && !big_data) { // Build up a string and send entire command in one write + if (buffer_args === false && big_data === false) { // Build up a string and send entire command in one write for (i = 0; i < args.length; i += 1) { arg = args[i]; command_str += '$' + Buffer.byteLength(arg) + '\r\n' + arg + '\r\n'; @@ -890,7 +892,6 @@ commands.list.forEach(function (command) { } return this.send_command(command, arr); }; - Multi.prototype[command.toUpperCase()] = Multi.prototype[command] = function (key, arg, callback) { if (Array.isArray(key)) { if (arg) { @@ -1031,7 +1032,7 @@ RedisClient.prototype.hmset = RedisClient.prototype.HMSET = function (key, args, return this.send_command('hmset', tmp_args, callback); } var len = arguments.length; - var tmp_args = new Array(len); + tmp_args = new Array(len); for (var i = 0; i < len; i += 1) { tmp_args[i] = arguments[i]; } @@ -1050,10 +1051,7 @@ Multi.prototype.hmset = Multi.prototype.HMSET = function (key, args, callback) { args = args.concat([callback]); } tmp_args = ['hmset', key].concat(args); - } else if (typeof args === 'object') { - if (typeof key !== 'string') { - key = key.toString(); - } + } else if (typeof args === 'object' && (typeof callback === 'function' || typeof callback === 'undefined')) { tmp_args = ['hmset', key]; var fields = Object.keys(args); while (field = fields.shift()) { @@ -1065,7 +1063,7 @@ Multi.prototype.hmset = Multi.prototype.HMSET = function (key, args, callback) { } } else { var len = arguments.length; - var tmp_args = new Array(len); + tmp_args = new Array(len); tmp_args[0] = 'hmset'; for (var i = 0; i < len; i += 1) { tmp_args[i + 1] = arguments[i]; From 5ef24a90b63f0ddde9714ce544658676bbf6d108 Mon Sep 17 00:00:00 2001 From: Ruben Bridgewater Date: Wed, 30 Dec 2015 16:14:23 +0100 Subject: [PATCH 0538/1748] Add tests and improve older tests --- index.js | 7 ++--- test/auth.spec.js | 46 +++++++++++++++++++++++++++++--- test/batch.spec.js | 2 +- test/commands/hset.spec.js | 34 +++++++++++++++++++++--- test/commands/info.spec.js | 51 ++++++++++++++++++++++++++++++++++++ test/commands/keys.spec.js | 3 +-- test/commands/select.spec.js | 29 +++++++++++++++----- test/commands/set.spec.js | 27 +++++++------------ test/commands/sync.spec.js | 44 ------------------------------- test/connection.spec.js | 20 ++++++++------ test/multi.spec.js | 2 +- test/node_redis.spec.js | 32 +++++++++++++++++----- 12 files changed, 199 insertions(+), 98 deletions(-) create mode 100644 test/commands/info.spec.js delete mode 100644 test/commands/sync.spec.js diff --git a/index.js b/index.js index 5b493ea621a..cfb123ee6cf 100644 --- a/index.js +++ b/index.js @@ -308,7 +308,6 @@ RedisClient.prototype.on_ready = function () { if (this.old_state !== null) { this.monitoring = this.old_state.monitoring; this.pub_sub_mode = this.old_state.pub_sub_mode; - this.selected_db = this.old_state.selected_db; this.old_state = null; } @@ -456,13 +455,11 @@ RedisClient.prototype.connection_gone = function (why) { if (this.old_state === null) { var state = { monitoring: this.monitoring, - pub_sub_mode: this.pub_sub_mode, - selected_db: this.selected_db + pub_sub_mode: this.pub_sub_mode }; this.old_state = state; this.monitoring = false; this.pub_sub_mode = false; - this.selected_db = undefined; } // since we are collapsing end and close, users don't expect to be called twice @@ -940,7 +937,7 @@ RedisClient.prototype.select = RedisClient.prototype.SELECT = function (db, call }); }; -// Store db in this.select_db to restore it on reconnect +// Store info in this.server_info after each call RedisClient.prototype.info = RedisClient.prototype.INFO = function (callback) { var self = this; this.send_anyway = true; diff --git a/test/auth.spec.js b/test/auth.spec.js index 1e79f023e13..9e648d63e07 100644 --- a/test/auth.spec.js +++ b/test/auth.spec.js @@ -67,12 +67,38 @@ describe("client authentication", function () { }); if (ip === 'IPv4') { - it('allows auth to be provided as part of redis url', function (done) { + it('allows auth to be provided as part of redis url and do not fire commands before auth is done', function (done) { if (helper.redisProcess().spawnFailed()) this.skip(); - client = redis.createClient('redis://foo:' + auth + '@' + config.HOST[ip] + ':' + config.PORT); + var end = helper.callFuncAfter(done, 2); + client = redis.createClient('redis://:' + auth + '@' + config.HOST[ip] + ':' + config.PORT); client.on("ready", function () { - return done(); + end(); + }); + // The info command may be used while loading but not if not yet authenticated + client.info(function (err, res) { + assert(!err); + end(); + }); + }); + + it('allows auth and database to be provided as part of redis url query parameter', function (done) { + if (helper.redisProcess().spawnFailed()) this.skip(); + + client = redis.createClient('redis://' + config.HOST[ip] + ':' + config.PORT + '?db=2&password=' + auth); + assert.strictEqual(client.options.db, '2'); + assert.strictEqual(client.options.password, auth); + assert.strictEqual(client.auth_pass, auth); + client.on("ready", function () { + // Set a key so the used database is returned in the info command + client.set('foo', 'bar'); + client.get('foo'); + assert.strictEqual(client.server_info.db2, undefined); + // Using the info command should update the server_info + client.info(function (err, res) { + assert(typeof client.server_info.db2 === 'object'); + }); + client.flushdb(done); }); }); } @@ -93,7 +119,7 @@ describe("client authentication", function () { if (helper.redisProcess().spawnFailed()) this.skip(); var args = config.configureClient(parser, ip, { - auth_pass: auth, + password: auth, no_ready_check: true }); client = redis.createClient.apply(redis.createClient, args); @@ -195,6 +221,18 @@ describe("client authentication", function () { done(); }); }); + + it('should emit an error if the provided password is faulty', function (done) { + if (helper.redisProcess().spawnFailed()) this.skip(); + client = redis.createClient({ + password: 'wrong_password', + parser: parser + }); + client.once("error", function (err) { + assert.strictEqual(err.message, 'ERR invalid password'); + done(); + }); + }); }); }); diff --git a/test/batch.spec.js b/test/batch.spec.js index caf4e7b8efb..1c8b51bcc1f 100644 --- a/test/batch.spec.js +++ b/test/batch.spec.js @@ -179,7 +179,7 @@ describe("The 'batch' method", function () { client.BATCH([ ["smembers", ["some set"]], ["del", "some set"], - ["smembers", "some set"] + ["smembers", "some set", undefined] // The explicit undefined is handled as a callback that is undefined ]) .scard("some set") .exec(function (err, replies) { diff --git a/test/commands/hset.spec.js b/test/commands/hset.spec.js index c08150382e9..2d0932476ac 100644 --- a/test/commands/hset.spec.js +++ b/test/commands/hset.spec.js @@ -45,12 +45,40 @@ describe("The 'hset' method", function () { }); }); - it('does not error when a buffer and array are set as fields on the same hash', function (done) { + it('throws a error if someone passed a array either as field or as value', function (done) { + var hash = "test hash"; + var field = "array"; + // This would be converted to "array contents" but if you use more than one entry, + // it'll result in e.g. "array contents,second content" and this is not supported and considered harmful + var value = ["array contents"]; + try { + client.HMSET(hash, field, value); + throw new Error('test failed'); + } catch (err) { + if (/invalid data/.test(err.message)) { + done(); + } else { + done(err); + } + } + }); + + it('does not error when a buffer and date are set as values on the same hash', function (done) { var hash = "test hash"; var field1 = "buffer"; var value1 = new Buffer("abcdefghij"); - var field2 = "array"; - var value2 = ["array contents"]; + var field2 = "date"; + var value2 = new Date(); + + client.HMSET(hash, field1, value1, field2, value2, helper.isString("OK", done)); + }); + + it('does not error when a buffer and date are set as fields on the same hash', function (done) { + var hash = "test hash"; + var value1 = "buffer"; + var field1 = new Buffer("abcdefghij"); + var value2 = "date"; + var field2 = new Date(); client.HMSET(hash, field1, value1, field2, value2, helper.isString("OK", done)); }); diff --git a/test/commands/info.spec.js b/test/commands/info.spec.js new file mode 100644 index 00000000000..9ce968051f0 --- /dev/null +++ b/test/commands/info.spec.js @@ -0,0 +1,51 @@ +'use strict'; + +var assert = require('assert'); +var config = require("../lib/config"); +var helper = require('../helper'); +var redis = config.redis; + +describe("The 'info' method", function () { + + helper.allTests(function(parser, ip, args) { + + describe("using " + parser + " and " + ip, function () { + var client; + + before(function (done) { + client = redis.createClient.apply(redis.createClient, args); + client.once("ready", function () { + client.flushall(done); + }); + }); + + after(function () { + client.end(true); + }); + + it("update server_info after a info command", function (done) { + client.set('foo', 'bar'); + client.info(); + client.select(2, function () { + assert.strictEqual(client.server_info.db2, undefined); + }); + client.set('foo', 'bar'); + client.info(); + setTimeout(function () { + assert.strictEqual(typeof client.server_info.db2, 'object'); + done(); + }, 150); + }); + + it("emit error after a failure", function (done) { + client.info(); + client.once('error', function (err) { + assert.strictEqual(err.code, 'UNCERTAIN_STATE'); + assert.strictEqual(err.command, 'INFO'); + done(); + }); + client.stream.destroy(); + }); + }); + }); +}); diff --git a/test/commands/keys.spec.js b/test/commands/keys.spec.js index 2c977f664d5..5522c2dfaa8 100644 --- a/test/commands/keys.spec.js +++ b/test/commands/keys.spec.js @@ -14,10 +14,9 @@ describe("The 'keys' method", function () { var client; beforeEach(function (done) { - args = args || {}; client = redis.createClient.apply(redis.createClient, args); client.once("ready", function () { - client.flushdb(done); + client.flushall(done); }); }); diff --git a/test/commands/select.spec.js b/test/commands/select.spec.js index bf41fc46a08..b3ef6d58932 100644 --- a/test/commands/select.spec.js +++ b/test/commands/select.spec.js @@ -37,7 +37,9 @@ describe("The 'select' method", function () { beforeEach(function (done) { client = redis.createClient.apply(redis.createClient, args); - client.once("ready", function () { done(); }); + client.once("ready", function () { + client.flushdb(done); + }); }); afterEach(function () { @@ -46,7 +48,7 @@ describe("The 'select' method", function () { it("changes the database and calls the callback", function (done) { // default value of null means database 0 will be used. - assert.strictEqual(client.selected_db, null, "default db should be null"); + assert.strictEqual(client.selected_db, undefined, "default db should be undefined"); var buffering = client.SELECT(1, function (err, res) { helper.isNotError()(err, res); assert.strictEqual(client.selected_db, 1, "db should be 1 after select"); @@ -58,7 +60,7 @@ describe("The 'select' method", function () { describe("and a callback is specified", function () { describe("with a valid db index", function () { it("selects the appropriate database", function (done) { - assert.strictEqual(client.selected_db, null, "default db should be null"); + assert.strictEqual(client.selected_db, undefined, "default db should be undefined"); client.select(1, function (err) { assert.equal(err, null); assert.equal(client.selected_db, 1, "we should have selected the new valid DB"); @@ -69,7 +71,7 @@ describe("The 'select' method", function () { describe("with an invalid db index", function () { it("returns an error", function (done) { - assert.strictEqual(client.selected_db, null, "default db should be null"); + assert.strictEqual(client.selected_db, undefined, "default db should be undefined"); client.select(9999, function (err) { assert.equal(err.code, 'ERR'); assert.equal(err.message, 'ERR invalid DB index'); @@ -82,7 +84,7 @@ describe("The 'select' method", function () { describe("and no callback is specified", function () { describe("with a valid db index", function () { it("selects the appropriate database", function (done) { - assert.strictEqual(client.selected_db, null, "default db should be null"); + assert.strictEqual(client.selected_db, undefined, "default db should be undefined"); client.select(1); setTimeout(function () { assert.equal(client.selected_db, 1, "we should have selected the new valid DB"); @@ -93,7 +95,7 @@ describe("The 'select' method", function () { describe("with an invalid db index", function () { it("emits an error when callback not provided", function (done) { - assert.strictEqual(client.selected_db, null, "default db should be null"); + assert.strictEqual(client.selected_db, undefined, "default db should be undefined"); client.on('error', function (err) { assert.strictEqual(err.command, 'SELECT'); @@ -105,6 +107,21 @@ describe("The 'select' method", function () { }); }); }); + + describe("reconnection occurs", function () { + it("selects the appropriate database after a reconnect", function (done) { + assert.strictEqual(client.selected_db, undefined, "default db should be undefined"); + client.select(3); + client.set('foo', 'bar', function () { + client.stream.destroy(); + }); + client.once('ready', function () { + assert.strictEqual(client.selected_db, 3); + assert(typeof client.server_info.db3 === 'object'); + done(); + }); + }); + }); }); }); }); diff --git a/test/commands/set.spec.js b/test/commands/set.spec.js index 4868ed06e6d..c4866297339 100644 --- a/test/commands/set.spec.js +++ b/test/commands/set.spec.js @@ -81,32 +81,17 @@ describe("The 'set' method", function () { describe("with valid parameters", function () { it("sets the value correctly", function (done) { client.set(key, value); - setTimeout(function () { - client.get(key, function (err, res) { - helper.isString(value)(err, res); - done(); - }); - }, 100); + client.get(key, helper.isString(value, done)); }); it("sets the value correctly even if the callback is explicitly set to undefined", function (done) { client.set(key, value, undefined); - setTimeout(function () { - client.get(key, function (err, res) { - helper.isString(value)(err, res); - done(); - }); - }, 100); + client.get(key, helper.isString(value, done)); }); it("sets the value correctly with the array syntax", function (done) { client.set([key, value]); - setTimeout(function () { - client.get(key, function (err, res) { - helper.isString(value)(err, res); - done(); - }); - }, 100); + client.get(key, helper.isString(value, done)); }); }); @@ -121,6 +106,12 @@ describe("The 'set' method", function () { }); }); + // TODO: This test has to be refactored from v.3.0 on to expect an error instead + it("converts null to 'null'", function (done) { + client.set('foo', null); + client.get('foo', helper.isString('null', done)); + }); + it("emit an error with only the key set", function (done) { client.on('error', function (err) { assert.equal(err.message, "ERR wrong number of arguments for 'set' command"); diff --git a/test/commands/sync.spec.js b/test/commands/sync.spec.js deleted file mode 100644 index bd793a0d388..00000000000 --- a/test/commands/sync.spec.js +++ /dev/null @@ -1,44 +0,0 @@ -'use strict'; - -var assert = require('assert'); -var config = require("../lib/config"); -var helper = require("../helper"); -var redis = config.redis; - -describe.skip("The 'sync' method", function () { - - helper.allTests(function(parser, ip, args) { - - describe("using " + parser + " and " + ip, function () { - var client; - - beforeEach(function (done) { - client = redis.createClient.apply(redis.createClient, args); - client.once("ready", function () { - client.flushdb(done); - }); - }); - - // This produces a parser error - // "Protocol error, got "K" as reply type byte" - // I'm uncertain if this is correct behavior or not - // TODO: Fix the command queue state error occuring - it('try to sync with the server and fail other commands', function (done) { - client.on('error', function(err) { - assert.equal(err.message, 'Protocol error, got "K" as reply type byte'); - assert.equal(err.command, 'SET'); - done(); - }); - client.sync(function(err, res) { - assert.equal(err, null); - assert(!!res); - }); - client.set('foo', 'bar'); - }); - - afterEach(function () { - client.end(true); - }); - }); - }); -}); diff --git a/test/connection.spec.js b/test/connection.spec.js index 9e98c3732e5..d731fee5fde 100644 --- a/test/connection.spec.js +++ b/test/connection.spec.js @@ -152,6 +152,7 @@ describe("connection tests", function () { client.on('error', function(err) { assert(/Redis connection in broken state: connection timeout.*?exceeded./.test(err.message)); assert(Date.now() - time < connect_timeout + 50); + assert(Date.now() - time >= connect_timeout); done(); }); }); @@ -358,20 +359,20 @@ describe("connection tests", function () { redis.createClient(config.HOST[ip] + ':' + config.PORT); throw new Error('failed'); } catch (err) { - assert.equal(err.message, 'Connection string must use the "redis:" protocol'); + assert.equal(err.message, 'Connection string must use the "redis:" protocol or begin with slashes //'); } }); if (ip === 'IPv4') { it('allows connecting with the redis url and the default port', function (done) { - client = redis.createClient('redis://foo:porkchopsandwiches@' + config.HOST[ip]); + client = redis.createClient('redis://:porkchopsandwiches@' + config.HOST[ip] + '/'); client.on("ready", function () { return done(); }); }); it('allows connecting with the redis url as first parameter and the options as second parameter', function (done) { - client = redis.createClient('redis://127.0.0.1', { + client = redis.createClient('//127.0.0.1', { connect_timeout: 1000 }); assert.strictEqual(client.options.connect_timeout, 1000); @@ -380,11 +381,12 @@ describe("connection tests", function () { }); }); - it('allows connecting with the redis url in the options object', function (done) { + it('allows connecting with the redis url in the options object and works with protocols other than the redis protocol (e.g. http)', function (done) { client = redis.createClient({ - url: 'redis://foo:porkchopsandwiches@' + config.HOST[ip] + url: 'http://foo:porkchopsandwiches@' + config.HOST[ip] + '/3' }); - assert.strictEqual(client.options.auth_pass, 'porkchopsandwiches'); + assert.strictEqual(client.auth_pass, 'porkchopsandwiches'); + assert.strictEqual(+client.selected_db, 3); assert(!client.options.port); assert.strictEqual(client.options.host, config.HOST[ip]); client.on("ready", function () { @@ -424,7 +426,8 @@ describe("connection tests", function () { tmp(function(err, res) { if (!delayed) { assert(!err); - res = res.toString().replace(/loading:0/, 'loading:1\r\nloading_eta_seconds:0.5'); + client.server_info.loading = 1; + client.server_info.loading_eta_seconds = 0.5; delayed = true; time = Date.now(); } @@ -454,7 +457,8 @@ describe("connection tests", function () { if (!delayed) { assert(!err); // Try reconnecting after one second even if redis tells us the time needed is above one second - res = res.toString().replace(/loading:0/, 'loading:1\r\nloading_eta_seconds:2.5'); + client.server_info.loading = 1; + client.server_info.loading_eta_seconds = 2.5; delayed = true; time = Date.now(); } diff --git a/test/multi.spec.js b/test/multi.spec.js index 63541e8a505..86dfe93162f 100644 --- a/test/multi.spec.js +++ b/test/multi.spec.js @@ -335,7 +335,7 @@ describe("The 'multi' method", function () { ["hmset", arr3, helper.isString('OK')], ['hmset', now, {123456789: "abcdefghij", "some manner of key": "a type of value", "otherTypes": 555}], ['hmset', 'key2', {"0123456789": "abcdefghij", "some manner of key": "a type of value", "otherTypes": 999}, helper.isString('OK')], - ["HMSET", "multihmset", ["multibar", "multibaz"]], + ["HMSET", "multihmset", ["multibar", "multibaz"], undefined], // undefined is used as a explicit not set callback variable ["hmset", "multihmset", ["multibar", "multibaz"], helper.isString('OK')], ]) .hmget(now, 123456789, 'otherTypes') diff --git a/test/node_redis.spec.js b/test/node_redis.spec.js index 7b3171f8d5b..e4ef75f00b0 100644 --- a/test/node_redis.spec.js +++ b/test/node_redis.spec.js @@ -8,9 +8,7 @@ var redis = config.redis; describe("The node_redis client", function () { - helper.allTests({ - allConnections: true - }, function(parser, ip, args) { + helper.allTests(function(parser, ip, args) { describe("using " + parser + " and " + ip, function () { var client; @@ -128,7 +126,7 @@ describe("The node_redis client", function () { it("misusing the function should eventually throw (no command)", function (done) { client.send_command(true, 'info', function (err, res) { assert(/ERR Protocol error/.test(err.message)); - assert.equal(err.command, true); + assert.equal(err.command, undefined); assert.equal(err.code, 'ERR'); done(); }); @@ -143,6 +141,28 @@ describe("The node_redis client", function () { }); + describe("retry_unfulfilled_commands", function () { + + it("should retry all commands instead of returning an error if a command did not yet return after a connection loss", function (done) { + var bclient = redis.createClient({ + parser: parser, + retry_unfulfilled_commands: true + }); + bclient.blpop("blocking list 2", 5, function (err, value) { + assert.strictEqual(value[0], "blocking list 2"); + assert.strictEqual(value[1], "initial value"); + return done(err); + }); + bclient.once('ready', function () { + setTimeout(function () { + bclient.stream.destroy(); + client.rpush("blocking list 2", "initial value", helper.isNumber(1)); + }, 100); + }); + }); + + }); + describe(".end", function () { it('used without flush / flush set to false', function(done) { @@ -191,8 +211,8 @@ describe("The node_redis client", function () { it("return an error in the callback", function (done) { if (helper.redisProcess().spawnFailed()) this.skip(); - // TODO: Investigate why this test is failing hard and killing mocha if the client is created with .apply - // Seems like something is wrong while passing a socket connection to create client! args[1] + // TODO: Investigate why this test is failing hard and killing mocha. + // Seems like something is wrong with nyc while passing a socket connection to create client! client = redis.createClient(); client.quit(function() { client.get("foo", function(err, res) { From 3514a32825b883c4cfa8f1278dee158760d2c99c Mon Sep 17 00:00:00 2001 From: Ruben Bridgewater Date: Wed, 30 Dec 2015 16:14:48 +0100 Subject: [PATCH 0539/1748] Remove unused function from utils --- lib/utils.js | 12 ------------ 1 file changed, 12 deletions(-) diff --git a/lib/utils.js b/lib/utils.js index ca458030cd6..e8328fcb039 100644 --- a/lib/utils.js +++ b/lib/utils.js @@ -36,17 +36,6 @@ function replyToStrings(reply) { return reply; } -function toArray(args) { - var len = args.length, - arr = new Array(len), i; - - for (i = 0; i < len; i += 1) { - arr[i] = args[i]; - } - - return arr; -} - function print (err, reply) { if (err) { console.log('Error: ' + err); @@ -60,7 +49,6 @@ var redisErrCode = /^([A-Z]+)\s+(.+)$/; module.exports = { reply_to_strings: replyToStrings, reply_to_object: replyToObject, - to_array: toArray, print: print, err_code: redisErrCode }; From ccf4c9950db715bb30e515b11245406d08646a76 Mon Sep 17 00:00:00 2001 From: Ruben Bridgewater Date: Sun, 6 Dec 2015 03:54:36 +0100 Subject: [PATCH 0540/1748] Update readme and changelog --- README.md | 21 ++++++++------------- changelog.md | 39 ++++++++++++++++++++++++++++++++++++--- 2 files changed, 44 insertions(+), 16 deletions(-) diff --git a/README.md b/README.md index aa448a09bc9..ae5ec5aabe9 100644 --- a/README.md +++ b/README.md @@ -50,9 +50,7 @@ This will display: 1: hashtest 2 mjr:~/work/node_redis (master)$ -Note that the API is entire asynchronous. To get data back from the server, -you'll need to use a callback. The return value from most of the API is a -backpressure indicator. +Note that the API is entire asynchronous. To get data back from the server, you'll need to use a callback. ### Promises @@ -115,9 +113,8 @@ For a list of Redis commands, see [Redis Command Reference](http://redis.io/comm The commands can be specified in uppercase or lowercase for convenience. `client.get()` is the same as `client.GET()`. -Minimal parsing is done on the replies. Commands that return a single line reply return JavaScript Strings, -integer replies return JavaScript Numbers, "bulk" replies return node Buffers, and "multi bulk" replies return a -JavaScript Array of node Buffers. `HGETALL` returns an Object with Buffers keyed by the hash keys. +Minimal parsing is done on the replies. Commands that return a integer return JavaScript Numbers, arrays return JavaScript Array. `HGETALL` returns an Object keyed by the hash keys. All strings will either be returned as string or as buffer depending on your setting. +Please be aware that sending null, undefined and Boolean values will result in the value coerced to a string! # API @@ -173,23 +170,20 @@ port and host are probably fine and you don't need to supply any arguments. `cre * `redis.createClient()` * `redis.createClient(options)` * `redis.createClient(unix_socket, options)` -* `redis.createClient('redis://user:pass@host:port', options)` +* `redis.createClient(redis_url, options)` * `redis.createClient(port, host, options)` #### `options` is an object with the following possible properties: * `host`: *127.0.0.1*; The host to connect to * `port`: *6370*; The port to connect to * `path`: *null*; The unix socket string to connect to -* `url`: *null*; The redis url to connect to +* `url`: *null*; The redis url to connect to (`[redis:]//[user][:password@][host][:port][/db-number][?db=db-number[&password=bar[&option=value]]]` For more info check [IANA](http://www.iana.org/assignments/uri-schemes/prov/redis)) * `parser`: *hiredis*; Which Redis protocol reply parser to use. If `hiredis` is not installed it will fallback to `javascript`. * `return_buffers`: *false*; If set to `true`, then all replies will be sent to callbacks as Buffers instead of Strings. * `detect_buffers`: *false*; If set to `true`, then replies will be sent to callbacks as Buffers. Please be aware that this can't work properly with the pubsub mode. A subscriber has to either always return strings or buffers. if any of the input arguments to the original command were Buffers. This option lets you switch between Buffers and Strings on a per-command basis, whereas `return_buffers` applies to every command on a client. -* `socket_nodelay`: *true*; Disables the [Nagle algorithm](https://en.wikipedia.org/wiki/Nagle%27s_algorithm). -Setting this option to `false` can result in additional throughput at the cost of more latency. -Most applications will want this set to `true`. * `socket_keepalive` *true*; Whether the keep-alive functionality is enabled on the underlying socket. * `no_ready_check`: *false*; When a connection is established to the Redis server, the server might still be loading the database from disk. While loading the server will not respond to any commands. To work around this, @@ -208,10 +202,11 @@ The value is provided in milliseconds and is counted from the moment on a new cl Default is to try connecting until the default system socket timeout has been exceeded and to try reconnecting until 1h passed. * `max_attempts`: *0*; By default client will try reconnecting until connected. Setting `max_attempts` limits total amount of connection tries. Setting this to 1 will prevent any reconnect tries. -* `auth_pass`: *null*; If set, client will run redis auth command on connect. +* `retry_unfulfilled_commands`: *false*; If set to true, all commands that were unfulfulled while the connection is lost will be retried after the connection has reestablished again. Use this with caution, if you use state altering commands (e.g. *incr*). This is especially useful if you use blocking commands. +* `password`: *null*; If set, client will run redis auth command on connect. Alias `auth_pass` +* `db`: *null*; If set, client will run redis select command on connect. This is [not recommended](https://groups.google.com/forum/#!topic/redis-db/vS5wX8X4Cjg). * `family`: *IPv4*; You can force using IPv6 if you set the family to 'IPv6'. See Node.js [net](https://nodejs.org/api/net.html) or [dns](https://nodejs.org/api/dns.html) modules how to use the family type. * `disable_resubscribing`: *false*; If set to `true`, a client won't resubscribe after disconnecting -* `to_empty_string`: *null*; convert any undefined or null command argument to an empty string. Be careful using this * `rename_commands`: *null*; pass a object with renamed commands to use those instead of the original functions. See the [redis security topics](http://redis.io/topics/security) for more info. * `tls`: an object containing options to pass to [tls.connect](http://nodejs.org/api/tls.html#tls_tls_connect_port_host_options_callback), to set up a TLS connection to Redis (if, for example, it is set up to be accessible via a tunnel). diff --git a/changelog.md b/changelog.md index 944680781af..e615e8d7045 100644 --- a/changelog.md +++ b/changelog.md @@ -1,12 +1,45 @@ Changelog ========= -## v.2.5.0 - xx Dez, 2015 +## v.2.5.0-0 - xx Dez, 2015 Features -- The parsers moved into the [redis-parser](https://github.com/NodeRedis/node-redis-parser) module and will be maintained in there from now on ([@BridgeAR](https://github.com/BridgeAR)) - - Improve js parser speed significantly for big SUNION/SINTER/LRANGE/ZRANGE ([@BridgeAR](https://github.com/BridgeAR)) +- The parsers moved into the [redis-parser](https://github.com/NodeRedis/node-redis-parser) module and will be maintained in there from now on + - Improve js parser speed significantly for big SUNION/SINTER/LRANGE/ZRANGE +- Improve redis-url parsing to also accept the database-number and options as query parameters as suggested in the [IANA](http://www.iana.org/assignments/uri-schemes/prov/redis) +- Added a `retry_unfulfilled_commands` option + - Setting this to 'true' results in retrying all commands that were not fulfilled on a connection loss after the reconnect. Use with caution +- Added a `db` option to select the database while connecting (this is [not recommended](https://groups.google.com/forum/#!topic/redis-db/vS5wX8X4Cjg)) +- Added a `password` option as alias for auth_pass +- The client.server_info is from now on updated while using the info command + +Bugfixes + +- Fixed explicit undefined as a command callback in a multi context +- Fixed hmset failing to detect the first key as buffer or date if the key is of that type +- Fixed do not run toString on an array argument and throw a "invalid data" error instead + - This is not considered as breaking change, as this is likely a error in your code and if you want to have such a behavior you should handle this beforehand + - The same applies to Map / Set and individual Object types +- Fixed redis url not accepting the protocol being omitted or protocols other than the redis protocol for convienence +- Fixed parsing the db keyspace even if the first database does not begin with a zero +- Fixed handling of errors occuring while receiving pub sub messages + +Deprecations + +- Using any command with a argument being set to null or undefined is deprecated + - From v.3.0.0 on using a command with such an argument will return an error instead + - If you want to keep the old behavior please use a precheck in your code that converts the arguments to a string. + - Using SET or SETEX with a undefined or null value will from now on also result in converting the value to "null" / "undefined" to have a consistent behavior. This is not considered as breaking change, as it returned an error earlier. +- Using .end(flush) without the flush parameter deprecated and the flush parameter should explicitly be used + - From v.3.0.0 on using .end without flush will result in an error + - Using .end without flush means that any command that did not yet return is going to silently fail. Therefor this is considered harmfull and you should explicitly silence such errors if you are sure you want this +- Depending on the return value of a command to detect the backpressure is deprecated + - From version 3.0.0 on node_redis might not return true / false as a return value anymore. Please rely on client.should_buffer instead +- The socket_nodelay option is deprecated and will be removed in v.3.0.0 + - If you want to buffer commands you should use [.batch or .multi](./README.md) instead. This is necessary to reduce the amount of different options and this is very likely reducing your throughput if set to false. + - If you are sure you want to activate the NAGLE algorithm you can still activate it by using client.stream.setNoDelay(false) +- Redis < v. 2.6.11 is not supported anymore and will not work in all cases. Please update to a newer redis version ## v.2.4.2 - 27 Nov, 2015 From 7ec50ac5cd0a34325d6ebfdaf8030271c716b7fd Mon Sep 17 00:00:00 2001 From: Benjamin Coe Date: Sat, 2 Jan 2016 17:49:38 -0800 Subject: [PATCH 0541/1748] upgrade nyc --- package.json | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/package.json b/package.json index 43bf680b85a..ac010b7f322 100644 --- a/package.json +++ b/package.json @@ -18,7 +18,7 @@ "coveralls": "nyc report --reporter=text-lcov | coveralls", "coverage": "nyc report --reporter=html", "benchmark": "node benchmarks/multi_bench.js", - "test": "nyc ./node_modules/.bin/_mocha ./test/*.js ./test/commands/*.js --timeout=8000", + "test": "nyc --cache mocha ./test/*.js ./test/commands/*.js --timeout=8000", "pretest": "optional-dev-dependency hiredis", "posttest": "jshint ." }, @@ -31,16 +31,16 @@ "node": ">=0.10.0" }, "devDependencies": { + "bluebird": "^3.0.2", "coveralls": "^2.11.2", "jshint": "^2.8.0", "metrics": "^0.1.9", "mocha": "^2.3.2", - "nyc": "^5.1.1", + "nyc": "^5.2.0", "optional-dev-dependency": "^1.1.0", "tcp-port-used": "^0.1.2", "uuid": "^2.0.1", - "win-spawn": "^2.0.0", - "bluebird": "^3.0.2" + "win-spawn": "^2.0.0" }, "repository": { "type": "git", From 89de457bba9c12e51452bac113317c2a4487bce1 Mon Sep 17 00:00:00 2001 From: Jan-Erik Rediger Date: Wed, 6 Jan 2016 18:05:46 -0800 Subject: [PATCH 0542/1748] Fix typo in README --- README.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/README.md b/README.md index ae5ec5aabe9..4a60486b48a 100644 --- a/README.md +++ b/README.md @@ -246,7 +246,7 @@ something like this `Error: Ready check failed: ERR operation not permitted`. Forcibly close the connection to the Redis server. Note that this does not wait until all replies have been parsed. If you want to exit cleanly, call `client.quit()` to send the `QUIT` command after you have handled all replies. -You should set flush to true, if you are not absolutly sure you do not care about any other commands. +You should set flush to true, if you are not absolutely sure you do not care about any other commands. If you set flush to false all still running commands will silently fail. This example closes the connection to the Redis server before the replies have been read. You probably don't From 713151467e9b8b944fd55fb25e0391aace06d96c Mon Sep 17 00:00:00 2001 From: ajk Date: Mon, 18 Jan 2016 14:33:37 -0800 Subject: [PATCH 0543/1748] Fix the port number in Readme to 6379 instead of 6370 --- README.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/README.md b/README.md index 4a60486b48a..a88332e4225 100644 --- a/README.md +++ b/README.md @@ -175,7 +175,7 @@ port and host are probably fine and you don't need to supply any arguments. `cre #### `options` is an object with the following possible properties: * `host`: *127.0.0.1*; The host to connect to -* `port`: *6370*; The port to connect to +* `port`: *6379*; The port to connect to * `path`: *null*; The unix socket string to connect to * `url`: *null*; The redis url to connect to (`[redis:]//[user][:password@][host][:port][/db-number][?db=db-number[&password=bar[&option=value]]]` For more info check [IANA](http://www.iana.org/assignments/uri-schemes/prov/redis)) * `parser`: *hiredis*; Which Redis protocol reply parser to use. If `hiredis` is not installed it will fallback to `javascript`. From 545e7231ab41e153750b65aef4b597eb9bfe0710 Mon Sep 17 00:00:00 2001 From: Ivan Tanev Date: Sun, 31 Jan 2016 12:25:35 +0200 Subject: [PATCH 0544/1748] Update SCAN example code --- examples/scan.js | 29 +++++++++++++++++++++-------- 1 file changed, 21 insertions(+), 8 deletions(-) diff --git a/examples/scan.js b/examples/scan.js index e3152537cc4..2f7a9d405ae 100644 --- a/examples/scan.js +++ b/examples/scan.js @@ -15,20 +15,33 @@ function scan() { // Update the cursor position for the next scan cursor = res[0]; + // get the SCAN result for this iteration + var keys = res[1]; - // From : - // 'An iteration starts when the cursor is set to 0, - // and terminates when the cursor returned by the server is 0.' - if (cursor === '0') { - return console.log('Iteration complete'); - } // Remember: more or less than COUNT or no keys may be returned // See http://redis.io/commands/scan#the-count-option // Also, SCAN may return the same key multiple times // See http://redis.io/commands/scan#scan-guarantees + // Additionally, you should always have the code that uses the keys + // before the code checking the cursor. + if (keys.length > 0) { + console.log('Array of matching keys', keys); + } - if (res[1].length > 0) { - console.log('Array of matching keys', res[1]); + // It's important to note that the cursor and returned keys + // vary independently. The scan is never complete until redis + // returns a non-zero cursor. However, with MATCH and large + // collections, most iterations will return an empty keys array. + + // Still, a cursor of zero DOES NOT mean that there are no keys. + // A zero cursor just means that the SCAN is complete, but there + // might be one last batch of results to process. + + // From : + // 'An iteration starts when the cursor is set to 0, + // and terminates when the cursor returned by the server is 0.' + if (cursor === '0') { + return console.log('Iteration complete'); } return scan(); From 8dcf06754df226f73fd485a542fb0bdf0d89d9f8 Mon Sep 17 00:00:00 2001 From: Ruben Bridgewater Date: Thu, 21 Jan 2016 22:28:26 +0100 Subject: [PATCH 0545/1748] Add warnings and handle protocol errors gracefuly --- README.md | 8 +++-- changelog.md | 8 +++-- index.js | 67 ++++++++++++++++++++++++------------- lib/utils.js | 18 +++------- test/connection.spec.js | 15 +++++++-- test/helper.js | 5 ++- test/node_redis.spec.js | 22 ++++++++++++ test/return_buffers.spec.js | 9 ++++- 8 files changed, 106 insertions(+), 46 deletions(-) diff --git a/README.md b/README.md index a88332e4225..1de766840dd 100644 --- a/README.md +++ b/README.md @@ -118,7 +118,7 @@ Please be aware that sending null, undefined and Boolean values will result in t # API -## Connection Events +## Connection and other Events `client` will emit some events about the state of the connection to the Redis server. @@ -155,9 +155,13 @@ writable. This event can be used to stream commands in to Redis and adapt to bac If the stream is buffering `client.should_buffer` is set to true. Otherwise the variable is always set to false. That way you can decide when to reduce your send rate and resume sending commands when you get `drain`. -You can also check the return value of each command as it will also return the backpressure indicator. +You can also check the return value of each command as it will also return the backpressure indicator (deprecated). If false is returned the stream had to buffer. +### "warning" + +`client` will emit `warning` when password was set but none is needed and if a deprecated option / function / similar is used. + ### "idle" `client` will emit `idle` when there are no outstanding commands that are awaiting a response. diff --git a/changelog.md b/changelog.md index e615e8d7045..95ce30e8b2f 100644 --- a/changelog.md +++ b/changelog.md @@ -1,18 +1,20 @@ Changelog ========= -## v.2.5.0-0 - xx Dez, 2015 +## v.2.5.0-0 - xx Jan, 2015 Features - The parsers moved into the [redis-parser](https://github.com/NodeRedis/node-redis-parser) module and will be maintained in there from now on - Improve js parser speed significantly for big SUNION/SINTER/LRANGE/ZRANGE -- Improve redis-url parsing to also accept the database-number and options as query parameters as suggested in the [IANA](http://www.iana.org/assignments/uri-schemes/prov/redis) +- Improve redis-url parsing to also accept the database-number and options as query parameters as suggested in [IANA](http://www.iana.org/assignments/uri-schemes/prov/redis) - Added a `retry_unfulfilled_commands` option - Setting this to 'true' results in retrying all commands that were not fulfilled on a connection loss after the reconnect. Use with caution - Added a `db` option to select the database while connecting (this is [not recommended](https://groups.google.com/forum/#!topic/redis-db/vS5wX8X4Cjg)) - Added a `password` option as alias for auth_pass - The client.server_info is from now on updated while using the info command +- Gracefuly handle redis protocol errors from now on +- Added a `warning` emitter that receives deprecation messages and other node_redis warnings Bugfixes @@ -31,7 +33,7 @@ Deprecations - From v.3.0.0 on using a command with such an argument will return an error instead - If you want to keep the old behavior please use a precheck in your code that converts the arguments to a string. - Using SET or SETEX with a undefined or null value will from now on also result in converting the value to "null" / "undefined" to have a consistent behavior. This is not considered as breaking change, as it returned an error earlier. -- Using .end(flush) without the flush parameter deprecated and the flush parameter should explicitly be used +- Using .end(flush) without the flush parameter is deprecated and the flush parameter should explicitly be used - From v.3.0.0 on using .end without flush will result in an error - Using .end without flush means that any command that did not yet return is going to silently fail. Therefor this is considered harmfull and you should explicitly silence such errors if you are sure you want this - Depending on the return value of a command to detect the backpressure is deprecated diff --git a/index.js b/index.js index cfb123ee6cf..83694f23bd3 100644 --- a/index.js +++ b/index.js @@ -38,6 +38,7 @@ function RedisClient (options) { options = clone(options); events.EventEmitter.call(this); var cnx_options = {}; + var self = this; if (options.path) { cnx_options.path = options.path; this.address = options.path; @@ -58,11 +59,13 @@ function RedisClient (options) { if (options.socket_nodelay === undefined) { options.socket_nodelay = true; } else if (!options.socket_nodelay) { // Only warn users with this set to false - console.warn( - 'node_redis: socket_nodelay is deprecated and will be removed in v.3.0.0.\n' + - 'Setting socket_nodelay to false likely results in a reduced throughput. Please use .batch to buffer commands and use pipelining.\n' + - 'If you are sure you rely on the NAGLE-algorithm you can activate it by calling client.stream.setNoDelay(false) instead.' - ); + process.nextTick(function () { + self.warn( + 'socket_nodelay is deprecated and will be removed in v.3.0.0.\n' + + 'Setting socket_nodelay to false likely results in a reduced throughput. Please use .batch to buffer commands and use pipelining.\n' + + 'If you are sure you rely on the NAGLE-algorithm you can activate it by calling client.stream.setNoDelay(false) instead.' + ); + }); } if (options.socket_keepalive === undefined) { options.socket_keepalive = true; @@ -74,7 +77,9 @@ function RedisClient (options) { options.detect_buffers = !!options.detect_buffers; // Override the detect_buffers setting if return_buffers is active and print a warning if (options.return_buffers && options.detect_buffers) { - console.warn('>> WARNING: You activated return_buffers and detect_buffers at the same time. The return value is always going to be a buffer.'); + process.nextTick(function () { + self.warn('WARNING: You activated return_buffers and detect_buffers at the same time. The return value is always going to be a buffer.'); + }); options.detect_buffers = false; } if (options.detect_buffers) { @@ -101,7 +106,6 @@ function RedisClient (options) { this.pipeline = 0; this.options = options; // Init parser - var self = this; this.reply_parser = new Parser({ returnReply: function (data) { self.return_reply(data); @@ -109,6 +113,12 @@ function RedisClient (options) { returnError: function (data) { self.return_error(data); }, + returnFatalError: function (err) { + // Error out all fired commands. Otherwise they might rely on faulty data. We have to reconnect to get in a working state again + self.flush_and_error(err, ['command_queue']); + self.stream.destroy(); + self.return_error(err); + }, returnBuffers: options.return_buffers || options.detect_buffers, name: options.parser }); @@ -128,10 +138,10 @@ RedisClient.prototype.create_stream = function () { /* istanbul ignore if: travis does not work with stunnel atm. Therefor the tls tests are skipped on travis */ if (this.options.tls) { - this.stream = tls.connect(this.connection_options); + this.stream = tls.connect(this.connection_options); } else { - this.stream = net.createConnection(this.connection_options); - } + this.stream = net.createConnection(this.connection_options); + } if (this.options.connect_timeout) { this.stream.setTimeout(this.connect_timeout, function () { @@ -225,6 +235,14 @@ RedisClient.prototype.unref = function () { } }; +RedisClient.prototype.warn = function (msg) { + if (this.listeners('warning').length !== 0) { + this.emit('warning', msg); + } else { + console.warn('node_redis:', msg); + } +}; + // Flush provided queues, erroring any items with a callback first RedisClient.prototype.flush_and_error = function (error, queue_names) { var command_obj; @@ -665,10 +683,10 @@ RedisClient.prototype.send_command = function (command, args, callback) { args[i] = args[i].toString(); // Add this to parse_arguments. } else if (args[i] === null) { - console.warn( - 'node_redis: Deprecated: The %s command contains a "null" argument.\n' + + this.warn( + 'Deprecated: The ' + command.toUpperCase() + ' command contains a "null" argument.\n' + 'This is converted to a "null" string now and will return an error from v.3.0 on.\n' + - 'Please handle this in your code to make sure everything works as you intended it to behave.', command.toUpperCase() + 'Please handle this in your code to make sure everything works as you intended it to.' ); args[i] = 'null'; // Backwards compatible :/ } else { @@ -679,10 +697,10 @@ RedisClient.prototype.send_command = function (command, args, callback) { } } } else if (typeof args[i] === 'undefined') { - console.warn( - 'node_redis: Deprecated: The %s command contains a "undefined" argument.\n' + + this.warn( + 'Deprecated: The ' + command.toUpperCase() + ' command contains a "undefined" argument.\n' + 'This is converted to a "undefined" string now and will return an error from v.3.0 on.\n' + - 'Please handle this in your code to make sure everything works as you intended it to behave.', command.toUpperCase() + 'Please handle this in your code to make sure everything works as you intended it to.' ); args[i] = 'undefined'; // Backwards compatible :/ } else { @@ -834,8 +852,8 @@ RedisClient.prototype.end = function (flush) { if (flush) { this.flush_and_error(new Error("The command can't be processed. The connection has already been closed.")); } else if (arguments.length === 0) { - console.warn( - 'node_redis: Using .end() without the flush parameter is deprecated and throws from v.3.0.0 on.\n' + + this.warn( + 'Using .end() without the flush parameter is deprecated and throws from v.3.0.0 on.\n' + 'Please check the doku (https://github.com/NodeRedis/node_redis) and explictly use flush.' ); } @@ -859,7 +877,7 @@ function Multi(client, args) { this._client = client; this.queue = new Queue(); var command, tmp_args; - if (Array.isArray(args)) { + if (args) { // Either undefined or an array. Fail hard if it's not an array for (var i = 0; i < args.length; i++) { command = args[i][0]; tmp_args = args[i].slice(1); @@ -1258,8 +1276,8 @@ var createClient = function (port_arg, host_arg, options) { } else if (typeof port_arg === 'string' || port_arg && port_arg.url) { options = clone(port_arg.url ? port_arg : host_arg || options); var parsed = URL.parse(port_arg.url || port_arg, true, true); - // [redis:]//[user][:password@][host][:port][/db-number][?db=db-number[&password=bar[&option=value]]] - if (parsed.hostname) { + // [redis:]//[[user][:password]@][host][:port][/db-number][?db=db-number[&password=bar[&option=value]]] + if (parsed.hostname || parsed.slashes) { // The host might be an empty string if (parsed.auth) { options.password = parsed.auth.split(':')[1]; } @@ -1269,15 +1287,18 @@ var createClient = function (port_arg, host_arg, options) { if (parsed.pathname && parsed.pathname !== '/') { options.db = parsed.pathname.substr(1); } + options.host = parsed.hostname; + options.port = parsed.port; if (parsed.search !== '') { var elem; for (elem in parsed.query) { // jshint ignore: line // If options are passed twice, only the parsed options will be used + if (options.hasOwnPropery(elem)) { + RedisClient.warn('WARNING: You passed the ' + elem + ' option twice!'); + } options[elem] = parsed.query[elem]; } } - options.host = parsed.hostname; - options.port = parsed.port; } else { options.path = port_arg; } diff --git a/lib/utils.js b/lib/utils.js index e8328fcb039..9fbb22cb60e 100644 --- a/lib/utils.js +++ b/lib/utils.js @@ -2,31 +2,23 @@ // hgetall converts its replies to an Object. If the reply is empty, null is returned. function replyToObject(reply) { - var obj = {}, j, jl, key, val; - - if (reply.length === 0 || !Array.isArray(reply)) { + if (reply.length === 0 || !Array.isArray(reply)) { // TODO: Check why the isArray check is needed and what value reply has in that case return null; } - - for (j = 0, jl = reply.length; j < jl; j += 2) { - key = reply[j].toString('binary'); - val = reply[j + 1]; - obj[key] = val; + var obj = {}; + for (var j = 0; j < reply.length; j += 2) { + obj[reply[j].toString('binary')] = reply[j + 1]; } - return obj; } function replyToStrings(reply) { - var i; - if (Buffer.isBuffer(reply)) { return reply.toString(); } - if (Array.isArray(reply)) { var res = new Array(reply.length); - for (i = 0; i < reply.length; i++) { + for (var i = 0; i < reply.length; i++) { // Recusivly call the function as slowlog returns deep nested replies res[i] = replyToStrings(reply[i]); } diff --git a/test/connection.spec.js b/test/connection.spec.js index d731fee5fde..2f05329ba1f 100644 --- a/test/connection.spec.js +++ b/test/connection.spec.js @@ -364,11 +364,20 @@ describe("connection tests", function () { }); if (ip === 'IPv4') { - it('allows connecting with the redis url and the default port', function (done) { + it('allows connecting with the redis url to the default host and port, select db 3 and warn about duplicate db option', function (done) { + client = redis.createClient('redis:///3?db=3'); + assert.strictEqual(client.selected_db, '3'); + client.on("ready", done); + }); + + it('allows connecting with the redis url and the default port and auth provided even though it is not required', function (done) { client = redis.createClient('redis://:porkchopsandwiches@' + config.HOST[ip] + '/'); - client.on("ready", function () { - return done(); + var end = helper.callFuncAfter(done, 2); + client.on('warning', function (msg) { + assert.strictEqual(msg, 'Warning: Redis server does not require a password, but a password was supplied.'); + end(); }); + client.on("ready", end); }); it('allows connecting with the redis url as first parameter and the options as second parameter', function (done) { diff --git a/test/helper.js b/test/helper.js index 2cb66943f71..72ce1d546f9 100644 --- a/test/helper.js +++ b/test/helper.js @@ -171,7 +171,10 @@ module.exports = { }, callFuncAfter: function (func, max) { var i = 0; - return function () { + return function (err) { + if (err) { + throw err; + } i++; if (i === max) { func(); diff --git a/test/node_redis.spec.js b/test/node_redis.spec.js index e4ef75f00b0..12107993a99 100644 --- a/test/node_redis.spec.js +++ b/test/node_redis.spec.js @@ -550,6 +550,28 @@ describe("The node_redis client", function () { }); }); + describe('protocol error', function () { + + it("should gracefully recover and only fail on the already send commands", function (done) { + client = redis.createClient.apply(redis.createClient, args); + client.on('error', function(err) { + assert.strictEqual(err.message, 'Protocol error, got "a" as reply type byte'); + // After the hard failure work properly again. The set should have been processed properly too + client.get('foo', function (err, res) { + assert.strictEqual(res, 'bar'); + done(); + }); + }); + client.once('ready', function () { + client.set('foo', 'bar', function (err, res) { + assert.strictEqual(err.message, 'Protocol error, got "a" as reply type byte'); + }); + // Fail the set answer. Has no corresponding command obj and will therefor land in the error handler and set + client.reply_parser.execute(new Buffer('a*1\r*1\r$1`zasd\r\na')); + }); + }); + }); + describe('enable_offline_queue', function () { describe('true', function () { it("should emit drain if offline queue is flushed and nothing to buffer", function (done) { diff --git a/test/return_buffers.spec.js b/test/return_buffers.spec.js index 32582843072..029103abb58 100644 --- a/test/return_buffers.spec.js +++ b/test/return_buffers.spec.js @@ -18,17 +18,24 @@ describe("return_buffers", function () { beforeEach(function (done) { client = redis.createClient.apply(redis.createClient, args); + var i = 1; if (args[2].detect_buffers) { // Test if detect_buffer option was deactivated assert.strictEqual(client.options.detect_buffers, false); args[2].detect_buffers = false; + i++; } + var end = helper.callFuncAfter(done, i); + client.on('warning', function (msg) { + assert.strictEqual(msg, 'WARNING: You activated return_buffers and detect_buffers at the same time. The return value is always going to be a buffer.'); + end(); + }); client.once("error", done); client.once("connect", function () { client.flushdb(function (err) { client.hmset("hash key 2", "key 1", "val 1", "key 2", "val 2"); client.set("string key 1", "string value"); - return done(err); + end(err); }); }); }); From 470ccf632da05fa03f4e53ccef8d226167df1c28 Mon Sep 17 00:00:00 2001 From: Ruben Bridgewater Date: Thu, 21 Jan 2016 22:29:40 +0100 Subject: [PATCH 0546/1748] Improve benchmark by using a higher precision and fix js parser benchmark for buffers --- benchmarks/multi_bench.js | 27 +++++++++------------------ 1 file changed, 9 insertions(+), 18 deletions(-) diff --git a/benchmarks/multi_bench.js b/benchmarks/multi_bench.js index 6d5b0e6f22f..1c3c08ae139 100644 --- a/benchmarks/multi_bench.js +++ b/benchmarks/multi_bench.js @@ -25,10 +25,8 @@ var num_clients = returnArg('clients', 1); var run_time = returnArg('time', 2500); // ms var versions_logged = false; var client_options = { - return_buffers: false, - max_attempts: 4, - parser: returnArg('parser', 'hiredis') - }; + parser: returnArg('parser', 'hiredis') +}; var small_str, large_str, small_buf, large_buf, very_large_str, very_large_buf; function lpad(input, len, chr) { @@ -42,7 +40,7 @@ function lpad(input, len, chr) { metrics.Histogram.prototype.print_line = function () { var obj = this.printObj(); - return lpad(obj.min, 4) + '/' + lpad(obj.max, 4) + '/' + lpad(obj.mean.toFixed(2), 7); + return lpad((obj.min / 1000000).toFixed(2), 6) + '/' + lpad((obj.max / 1000000).toFixed(2), 6) + '/' + lpad((obj.mean / 1000000).toFixed(2), 6); }; function Test(args) { @@ -54,7 +52,8 @@ function Test(args) { this.commands_completed = 0; this.max_pipeline = this.args.pipeline || 50; this.batch_pipeline = this.args.batch || 0; - this.client_options = args.client_options || client_options; + this.client_options = args.client_options || {}; + this.client_options.parser = client_options.parser; this.connect_latency = new metrics.Histogram(); this.ready_latency = new metrics.Histogram(); this.command_latency = new metrics.Histogram(); @@ -131,14 +130,6 @@ Test.prototype.fill_pipeline = function () { return; } - if (this.clients[0].should_buffer) { - var self = this; - setTimeout(function() { - self.fill_pipeline(); - }, 1); - return; - } - if (this.batch_pipeline) { this.batch(); } else { @@ -153,7 +144,7 @@ Test.prototype.fill_pipeline = function () { Test.prototype.batch = function () { var self = this, cur_client = client_nr++ % this.clients.length, - start = Date.now(), + start = process.hrtime(), i = 0, batch = this.clients[cur_client].batch(); @@ -167,7 +158,7 @@ Test.prototype.batch = function () { throw err; } self.commands_completed += res.length; - self.command_latency.update(Date.now() - start); + self.command_latency.update(process.hrtime(start)[1]); self.fill_pipeline(); }); }; @@ -189,14 +180,14 @@ Test.prototype.stop_clients = function () { Test.prototype.send_next = function () { var self = this, cur_client = this.commands_sent % this.clients.length, - start = Date.now(); + start = process.hrtime(); this.clients[cur_client][this.args.command](this.args.args, function (err, res) { if (err) { throw err; } self.commands_completed++; - self.command_latency.update(Date.now() - start); + self.command_latency.update(process.hrtime(start)[1]); self.fill_pipeline(); }); }; From d739ff3a762e566eba7891407e70b703b4ba2523 Mon Sep 17 00:00:00 2001 From: Ruben Bridgewater Date: Thu, 21 Jan 2016 22:31:17 +0100 Subject: [PATCH 0547/1748] Fix test race condition --- test/connection.spec.js | 45 +++++++++++------------------------------ 1 file changed, 12 insertions(+), 33 deletions(-) diff --git a/test/connection.spec.js b/test/connection.spec.js index 2f05329ba1f..5f5892dea25 100644 --- a/test/connection.spec.js +++ b/test/connection.spec.js @@ -133,6 +133,7 @@ describe("connection tests", function () { it("emit an error after the socket timeout exceeded the connect_timeout time", function (done) { var connect_timeout = 1000; // in ms + var time = Date.now(); client = redis.createClient({ parser: parser, host: '192.168.74.167', // Should be auto detected as ipv4 @@ -143,7 +144,6 @@ describe("connection tests", function () { }); assert.strictEqual(client.address, '192.168.74.167:6379'); assert.strictEqual(client.connection_options.family, 4); - var time = Date.now(); client.on("reconnecting", function (params) { throw new Error('No reconnect, since no connection was ever established'); @@ -152,7 +152,7 @@ describe("connection tests", function () { client.on('error', function(err) { assert(/Redis connection in broken state: connection timeout.*?exceeded./.test(err.message)); assert(Date.now() - time < connect_timeout + 50); - assert(Date.now() - time >= connect_timeout); + assert(Date.now() - time >= connect_timeout - 50); // Somehow this is triggered to early at times done(); }); }); @@ -192,9 +192,7 @@ describe("connection tests", function () { connect_timeout: 1000 }); - client.once('ready', function() { - done(); - }); + client.once('ready', done); }); if (process.platform !== 'win32') { @@ -207,10 +205,7 @@ describe("connection tests", function () { var end = helper.callFuncAfter(done, 2); - client.once('ready', function() { - end(); - }); - + client.once('ready', end); client.set('foo', 'bar', end); }); } @@ -221,9 +216,7 @@ describe("connection tests", function () { client.once("ready", function () { client.removeListener("error", done); - client.get("recon 1", function (err, res) { - done(err); - }); + client.get("recon 1", done); }); }); @@ -233,9 +226,7 @@ describe("connection tests", function () { client.once("ready", function () { client.removeListener("error", done); - client.get("recon 1", function (err, res) { - done(err); - }); + client.get("recon 1", done); }); }); @@ -246,9 +237,7 @@ describe("connection tests", function () { client.once("ready", function () { client.removeListener("error", done); - client.get("recon 1", function (err, res) { - done(err); - }); + client.get("recon 1", done); }); }); @@ -258,9 +247,7 @@ describe("connection tests", function () { client.once("ready", function () { client.removeListener("error", done); - client.get("recon 1", function (err, res) { - done(err); - }); + client.get("recon 1", done); }); }); @@ -385,9 +372,7 @@ describe("connection tests", function () { connect_timeout: 1000 }); assert.strictEqual(client.options.connect_timeout, 1000); - client.on('ready', function () { - done(); - }); + client.on('ready', done); }); it('allows connecting with the redis url in the options object and works with protocols other than the redis protocol (e.g. http)', function (done) { @@ -398,9 +383,7 @@ describe("connection tests", function () { assert.strictEqual(+client.selected_db, 3); assert(!client.options.port); assert.strictEqual(client.options.host, config.HOST[ip]); - client.on("ready", function () { - return done(); - }); + client.on("ready", done); }); it('allows connecting with the redis url and no auth and options as second parameter', function (done) { @@ -409,18 +392,14 @@ describe("connection tests", function () { }; client = redis.createClient('redis://' + config.HOST[ip] + ':' + config.PORT, options); assert.strictEqual(Object.keys(options).length, 1); - client.on("ready", function () { - return done(); - }); + client.on("ready", done); }); it('allows connecting with the redis url and no auth and options as third parameter', function (done) { client = redis.createClient('redis://' + config.HOST[ip] + ':' + config.PORT, null, { detect_buffers: false }); - client.on("ready", function () { - return done(); - }); + client.on("ready", done); }); } From 60eee34de1b57ca987a396cbff954fc23755634c Mon Sep 17 00:00:00 2001 From: Ruben Bridgewater Date: Thu, 21 Jan 2016 22:35:40 +0100 Subject: [PATCH 0548/1748] Unify command handling --- changelog.md | 1 + index.js | 279 ++++++++++++++++++---------------- test/batch.spec.js | 13 +- test/commands/get.spec.js | 7 - test/commands/hgetall.spec.js | 4 +- test/commands/mset.spec.js | 2 +- test/multi.spec.js | 8 +- 7 files changed, 166 insertions(+), 148 deletions(-) diff --git a/changelog.md b/changelog.md index 95ce30e8b2f..5c1a01bb677 100644 --- a/changelog.md +++ b/changelog.md @@ -42,6 +42,7 @@ Deprecations - If you want to buffer commands you should use [.batch or .multi](./README.md) instead. This is necessary to reduce the amount of different options and this is very likely reducing your throughput if set to false. - If you are sure you want to activate the NAGLE algorithm you can still activate it by using client.stream.setNoDelay(false) - Redis < v. 2.6.11 is not supported anymore and will not work in all cases. Please update to a newer redis version +- Removed non documented command syntax (adding the callback to an arguments array instead of passing it as individual argument) ## v.2.4.2 - 27 Nov, 2015 diff --git a/index.js b/index.js index 83694f23bd3..811312d9b51 100644 --- a/index.js +++ b/index.js @@ -651,45 +651,44 @@ RedisClient.prototype.send_command = function (command, args, callback) { command_str = '', buffer_args = false, big_data = false, - prefix_keys; + prefix_keys, + len, args_copy; if (typeof args === 'undefined') { args = []; - } else if (typeof callback === 'undefined') { - if (typeof args[args.length - 1] === 'function') { - callback = args.pop(); - } else if (typeof args[args.length - 1] === 'undefined') { - args.pop(); - } } - if (callback && process.domain) { callback = process.domain.bind(callback); } - for (i = 0; i < args.length; i += 1) { + len = args.length; + args_copy = new Array(len); + + for (i = 0; i < len; i += 1) { if (typeof args[i] === 'string') { // 30000 seemed to be a good value to switch to buffers after testing and checking the pros and cons if (args[i].length > 30000) { big_data = true; - args[i] = new Buffer(args[i], 'utf8'); + args_copy[i] = new Buffer(args[i], 'utf8'); if (this.pipeline !== 0) { this.pipeline += 2; this.writeDefault = this.writeBuffers; } + } else { + args_copy[i] = args[i]; } } else if (typeof args[i] === 'object') { // Checking for object instead of Buffer.isBuffer helps us finding data types that we can't handle properly if (args[i] instanceof Date) { // Accept dates as valid input - args[i] = args[i].toString(); - // Add this to parse_arguments. + args_copy[i] = args[i].toString(); } else if (args[i] === null) { this.warn( 'Deprecated: The ' + command.toUpperCase() + ' command contains a "null" argument.\n' + 'This is converted to a "null" string now and will return an error from v.3.0 on.\n' + 'Please handle this in your code to make sure everything works as you intended it to.' ); - args[i] = 'null'; // Backwards compatible :/ + args_copy[i] = 'null'; // Backwards compatible :/ } else { + args_copy[i] = args[i]; buffer_args = true; if (this.pipeline !== 0) { this.pipeline += 2; @@ -702,13 +701,13 @@ RedisClient.prototype.send_command = function (command, args, callback) { 'This is converted to a "undefined" string now and will return an error from v.3.0 on.\n' + 'Please handle this in your code to make sure everything works as you intended it to.' ); - args[i] = 'undefined'; // Backwards compatible :/ + args_copy[i] = 'undefined'; // Backwards compatible :/ } else { - args[i] = String(args[i]); + args_copy[i] = String(args[i]); } } - command_obj = new Command(command, args, buffer_args, callback); + command_obj = new Command(command, args_copy, buffer_args, callback); // TODO: Replace send_anyway with `commands.hasFlag(command, 'loading') === false` as soon as pub_sub is handled in the result handler if (this.ready === false && this.send_anyway === false || !stream.writable) { @@ -746,20 +745,20 @@ RedisClient.prototype.send_command = function (command, args, callback) { command = this.options.rename_commands[command]; } if (this.options.prefix) { - prefix_keys = commands.getKeyIndexes(command, args); + prefix_keys = commands.getKeyIndexes(command, args_copy); i = prefix_keys.pop(); while (i !== undefined) { - args[i] = this.options.prefix + args[i]; + args_copy[i] = this.options.prefix + args_copy[i]; i = prefix_keys.pop(); } } // Always use 'Multi bulk commands', but if passed any Buffer args, then do multiple writes, one for each arg. // This means that using Buffers in commands is going to be slower, so use Strings if you don't already have a Buffer. - command_str = '*' + (args.length + 1) + '\r\n$' + command.length + '\r\n' + command + '\r\n'; + command_str = '*' + (len + 1) + '\r\n$' + command.length + '\r\n' + command + '\r\n'; if (buffer_args === false && big_data === false) { // Build up a string and send entire command in one write - for (i = 0; i < args.length; i += 1) { - arg = args[i]; + for (i = 0; i < len; i += 1) { + arg = args_copy[i]; command_str += '$' + Buffer.byteLength(arg) + '\r\n' + arg + '\r\n'; } debug('Send ' + this.address + ' id ' + this.connection_id + ': ' + command_str); @@ -768,8 +767,8 @@ RedisClient.prototype.send_command = function (command, args, callback) { debug('Send command (' + command_str + ') has Buffer arguments'); this.write(command_str); - for (i = 0; i < args.length; i += 1) { - arg = args[i]; + for (i = 0; i < len; i += 1) { + arg = args_copy[i]; if (typeof arg !== 'object') { // string; number; boolean this.write('$' + Buffer.byteLength(arg) + '\r\n' + arg + '\r\n'); } else { // buffer @@ -892,40 +891,64 @@ function Multi(client, args) { commands.list.forEach(function (command) { - RedisClient.prototype[command.toUpperCase()] = RedisClient.prototype[command] = function (key, arg, callback) { - if (Array.isArray(key)) { - return this.send_command(command, key, arg); - } else if (Array.isArray(arg)) { - arg = [key].concat(arg); - return this.send_command(command, arg, callback); - } - // This has to be inlined, otherwise the arguments leak - var len = arguments.length; - var arr = new Array(len); - for (var i = 0; i < len; i += 1) { - arr[i] = arguments[i]; + RedisClient.prototype[command.toUpperCase()] = RedisClient.prototype[command] = function () { + var arr, + len = 0, + callback, + i = 0; + if (arguments[0] instanceof Array) { + arr = arguments[0]; + callback = arguments[1]; // It does not matter if it exists or not + } else if (arguments[1] instanceof Array) { + len = arguments[1].length; + arr = new Array(len + 1); + arr[0] = arguments[0]; + for (; i < len; i += 1) { + arr[i + 1] = arguments[1][i]; + } + callback = arguments[2]; + } else { + len = arguments.length; + arr = new Array(len); + for (; i < len; i += 1) { + arr[i] = arguments[i]; + } + // The later should not be the average use case + if (typeof arr[i - 1] === 'function' || typeof arr[i - 1] === 'undefined') { + callback = arr.pop(); + } } - return this.send_command(command, arr); + return this.send_command(command, arr, callback); }; - Multi.prototype[command.toUpperCase()] = Multi.prototype[command] = function (key, arg, callback) { - if (Array.isArray(key)) { - if (arg) { - key = key.concat([arg]); - } - this.queue.push([command].concat(key)); - } else if (Array.isArray(arg)) { - if (callback) { - arg = arg.concat([callback]); + + Multi.prototype[command.toUpperCase()] = Multi.prototype[command] = function () { + var arr, + len = 0, + callback, + i = 0; + if (arguments[0] instanceof Array) { + arr = arguments[0]; + callback = arguments[1]; + } else if (arguments[1] instanceof Array) { + len = arguments[1].length; + arr = new Array(len + 1); + arr[0] = arguments[0]; + for (; i < len; i += 1) { + arr[i + 1] = arguments[1][i]; } - this.queue.push([command, key].concat(arg)); + callback = arguments[2]; } else { - var len = arguments.length; - var arr = new Array(len); - for (var i = 0; i < len; i += 1) { + len = arguments.length; + arr = new Array(len); + for (; i < len; i += 1) { arr[i] = arguments[i]; } - this.queue.push([command].concat(arr)); + // The later should not be the average use case + if (typeof arr[i - 1] === 'function' || typeof arr[i - 1] === 'undefined') { + callback = arr.pop(); + } } + this.queue.push([command, arr, callback]); return this; }; }); @@ -1028,63 +1051,76 @@ RedisClient.prototype.auth = RedisClient.prototype.AUTH = function (pass, callba return tmp; }; -RedisClient.prototype.hmset = RedisClient.prototype.HMSET = function (key, args, callback) { - var field, tmp_args; - if (Array.isArray(key)) { - return this.send_command('hmset', key, args); - } - if (Array.isArray(args)) { - return this.send_command('hmset', [key].concat(args), callback); - } - if (typeof args === 'object' && (typeof callback === 'function' || typeof callback === 'undefined')) { - // User does: client.hmset(key, {key1: val1, key2: val2}) - // assuming key is a string, i.e. email address - tmp_args = [key]; - var fields = Object.keys(args); - while (field = fields.shift()) { - tmp_args.push(field, args[field]); +RedisClient.prototype.hmset = RedisClient.prototype.HMSET = function () { + var arr, + len = 0, + callback, + i = 0; + if (arguments[0] instanceof Array) { + arr = arguments[0]; + callback = arguments[1]; + } else if (arguments[1] instanceof Array) { + len = arguments[1].length; + arr = new Array(len + 1); + arr[0] = arguments[0]; + for (; i < len; i += 1) { + arr[i + 1] = arguments[1][i]; + } + callback = arguments[2]; + } else if (typeof arguments[1] === 'object' && (typeof arguments[2] === 'function' || typeof arguments[2] === 'undefined')) { + arr = [arguments[0]]; + for (var field in arguments[1]) { // jshint ignore: line + arr.push(field, arguments[1][field]); + } + callback = arguments[2]; + } else { + len = arguments.length; + arr = new Array(len); + for (; i < len; i += 1) { + arr[i] = arguments[i]; + } + // The later should not be the average use case + if (typeof arr[i - 1] === 'function' || typeof arr[i - 1] === 'undefined') { + callback = arr.pop(); } - return this.send_command('hmset', tmp_args, callback); - } - var len = arguments.length; - tmp_args = new Array(len); - for (var i = 0; i < len; i += 1) { - tmp_args[i] = arguments[i]; } - return this.send_command('hmset', tmp_args); + return this.send_command('hmset', arr, callback); }; -Multi.prototype.hmset = Multi.prototype.HMSET = function (key, args, callback) { - var tmp_args, field; - if (Array.isArray(key)) { - if (args) { - key = key.concat([args]); - } - tmp_args = ['hmset'].concat(key); - } else if (Array.isArray(args)) { - if (callback) { - args = args.concat([callback]); +Multi.prototype.hmset = Multi.prototype.HMSET = function () { + var arr, + len = 0, + callback, + i = 0; + if (arguments[0] instanceof Array) { + arr = arguments[0]; + callback = arguments[1]; + } else if (arguments[1] instanceof Array) { + len = arguments[1].length; + arr = new Array(len + 1); + arr[0] = arguments[0]; + for (; i < len; i += 1) { + arr[i + 1] = arguments[1][i]; } - tmp_args = ['hmset', key].concat(args); - } else if (typeof args === 'object' && (typeof callback === 'function' || typeof callback === 'undefined')) { - tmp_args = ['hmset', key]; - var fields = Object.keys(args); - while (field = fields.shift()) { - tmp_args.push(field); - tmp_args.push(args[field]); - } - if (callback) { - tmp_args.push(callback); + callback = arguments[2]; + } else if (typeof arguments[1] === 'object' && (typeof arguments[2] === 'function' || typeof arguments[2] === 'undefined')) { + arr = [arguments[0]]; + for (var field in arguments[1]) { // jshint ignore: line + arr.push(field, arguments[1][field]); } + callback = arguments[2]; } else { - var len = arguments.length; - tmp_args = new Array(len); - tmp_args[0] = 'hmset'; - for (var i = 0; i < len; i += 1) { - tmp_args[i + 1] = arguments[i]; + len = arguments.length; + arr = new Array(len); + for (; i < len; i += 1) { + arr[i] = arguments[i]; + } + // The later should not be the average use case + if (typeof arr[i - 1] === 'function' || typeof arr[i - 1] === 'undefined') { + callback = arr.pop(); } } - this.queue.push(tmp_args); + this.queue.push(['hmset', arr, callback]); return this; }; @@ -1111,8 +1147,6 @@ Multi.prototype.exec_atomic = function (callback) { Multi.prototype.exec_transaction = function (callback) { var self = this; var len = this.queue.length; - var cb; - var args_len = 1; this.errors = []; this.callback = callback; this._client.cork(len + 2); @@ -1120,26 +1154,20 @@ Multi.prototype.exec_transaction = function (callback) { this.send_command('multi', []); // Drain queue, callback will catch 'QUEUED' or error for (var index = 0; index < len; index++) { - var args = this.queue.get(index).slice(0); - var command = args.shift(); - args_len = args.length - 1; - if (typeof args[args_len] === 'function' || typeof args[args_len] === 'undefined') { - cb = args.pop(); - } else { - // Explicitly set the callback to undefined. Otherwise we might have the callback from the command earlier - cb = undefined; - } + var args = this.queue.get(index); + var command = args[0]; + var cb = args[2]; // Keep track of who wants buffer responses: if (this._client.options.detect_buffers) { this.wants_buffers[index] = false; - for (var i = 0; i < args.length; i += 1) { - if (Buffer.isBuffer(args[i])) { + for (var i = 0; i < args[1].length; i += 1) { + if (Buffer.isBuffer(args[1][i])) { this.wants_buffers[index] = true; break; } } } - this.send_command(command, args, index, cb); + this.send_command(command, args[1], index, cb); } this._client.send_command('exec', [], function(err, replies) { @@ -1168,7 +1196,6 @@ Multi.prototype.execute_callback = function (err, replies) { if (replies) { while (args = this.queue.shift()) { - // If we asked for strings, even in detect_buffers mode, then return strings: if (replies[i] instanceof Error) { var match = replies[i].message.match(utils.err_code); // LUA script could return user errors that don't behave like all other errors! @@ -1176,14 +1203,13 @@ Multi.prototype.execute_callback = function (err, replies) { replies[i].code = match[1]; } replies[i].command = args[0].toUpperCase(); - } else if (replies[i]) { - replies[i] = this._client.handle_reply(replies[i], args[0], this.wants_buffers[i]); - } - - if (typeof args[args.length - 1] === 'function') { - if (replies[i] instanceof Error) { + if (typeof args[args.length - 1] === 'function') { args[args.length - 1](replies[i]); - } else { + } + } else { + // If we asked for strings, even in detect_buffers mode, then return strings: + replies[i] = this._client.handle_reply(replies[i], args[0], this.wants_buffers[i]); + if (typeof args[args.length - 1] === 'function') { args[args.length - 1](null, replies[i]); } } @@ -1246,21 +1272,18 @@ Multi.prototype.exec = Multi.prototype.EXEC = Multi.prototype.exec_batch = funct this.results = []; this._client.cork(len); while (args = this.queue.shift()) { - var command = args.shift(); + var command = args[0]; var cb; - args_len = args.length - 1; - if (typeof args[args_len] === 'function') { - cb = this.callback(args.pop(), index); + args_len = args[1].length - 1; + if (typeof args[2] === 'function') { + cb = this.callback(args[2], index); } else { cb = callback_without_own_cb; - if (typeof args[args_len] === 'undefined') { - args.pop(); - } } if (callback && index === len - 1) { cb = last_callback(cb); } - this._client.send_command(command, args, cb); + this._client.send_command(command, args[1], cb); index++; } this._client.uncork(); diff --git a/test/batch.spec.js b/test/batch.spec.js index 1c8b51bcc1f..a99cb821731 100644 --- a/test/batch.spec.js +++ b/test/batch.spec.js @@ -199,8 +199,8 @@ describe("The 'batch' method", function () { arr4, [["mset", "batchfoo2", "batchbar2", "batchfoo3", "batchbar3"], helper.isString('OK')], ["hmset", arr], - [["hmset", "batchhmset2", "batchbar2", "batchfoo3", "batchbar3", "test", helper.isString('OK')]], - ["hmset", ["batchhmset", "batchbar", "batchfoo", helper.isString('OK')]], + [["hmset", "batchhmset2", "batchbar2", "batchfoo3", "batchbar3", "test"], helper.isString('OK')], + ["hmset", ["batchhmset", "batchbar", "batchfoo"], helper.isString('OK')], ["hmset", arr3, helper.isString('OK')], ['hmset', now, {123456789: "abcdefghij", "some manner of key": "a type of value", "otherTypes": 555}], ['hmset', 'key2', {"0123456789": "abcdefghij", "some manner of key": "a type of value", "otherTypes": 999}, helper.isString('OK')], @@ -211,8 +211,9 @@ describe("The 'batch' method", function () { .hmget('key2', arr2, function noop() {}) .hmget(['batchhmset2', 'some manner of key', 'batchbar3']) .mget('batchfoo2', ['batchfoo3', 'batchfoo'], function(err, res) { - assert(res[0], 'batchfoo3'); - assert(res[1], 'batchfoo'); + assert.strictEqual(res[0], 'batchbar2'); + assert.strictEqual(res[1], 'batchbar3'); + assert.strictEqual(res[2], null); }) .exec(function (err, replies) { assert.equal(arr.length, 3); @@ -273,7 +274,7 @@ describe("The 'batch' method", function () { it('allows multiple commands to work the same as normal to be performed using a chaining API', function (done) { client.batch() .mset(['some', '10', 'keys', '20']) - .incr(['some', helper.isNumber(11)]) + .incr('some', helper.isNumber(11)) .incr(['keys'], helper.isNumber(21)) .mget('some', 'keys') .exec(function (err, replies) { @@ -290,7 +291,7 @@ describe("The 'batch' method", function () { it('allows multiple commands to work the same as normal to be performed using a chaining API promisified', function () { return client.batch() .mset(['some', '10', 'keys', '20']) - .incr(['some', helper.isNumber(11)]) + .incr('some', helper.isNumber(11)) .incr(['keys'], helper.isNumber(21)) .mget('some', 'keys') .execAsync() diff --git a/test/commands/get.spec.js b/test/commands/get.spec.js index f51201b990b..522872e1dfa 100644 --- a/test/commands/get.spec.js +++ b/test/commands/get.spec.js @@ -75,13 +75,6 @@ describe("The 'get' method", function () { }); }); - it("gets the value correctly with array syntax and the callback being in the array", function (done) { - client.GET([key, function (err, res) { - helper.isString(value)(err, res); - done(err); - }]); - }); - it("should not throw on a get without callback (even if it's not useful)", function (done) { client.GET(key); client.on('error', function(err) { diff --git a/test/commands/hgetall.spec.js b/test/commands/hgetall.spec.js index 4e219326f2b..d531703eebe 100644 --- a/test/commands/hgetall.spec.js +++ b/test/commands/hgetall.spec.js @@ -64,7 +64,7 @@ describe("The 'hgetall' method", function () { it('returns binary results', function (done) { client.hmset(["bhosts", "mjr", "1", "another", "23", "home", "1234", new Buffer([0xAA, 0xBB, 0x00, 0xF0]), new Buffer([0xCC, 0xDD, 0x00, 0xF0])], helper.isString("OK")); - client.HGETALL(["bhosts", function (err, obj) { + client.HGETALL("bhosts", function (err, obj) { assert.strictEqual(4, Object.keys(obj).length); assert.strictEqual("1", obj.mjr.toString()); assert.strictEqual("23", obj.another.toString()); @@ -72,7 +72,7 @@ describe("The 'hgetall' method", function () { assert.strictEqual((new Buffer([0xAA, 0xBB, 0x00, 0xF0])).toString('binary'), Object.keys(obj)[3]); assert.strictEqual((new Buffer([0xCC, 0xDD, 0x00, 0xF0])).toString('binary'), obj[(new Buffer([0xAA, 0xBB, 0x00, 0xF0])).toString('binary')].toString('binary')); return done(err); - }]); + }); }); }); diff --git a/test/commands/mset.spec.js b/test/commands/mset.spec.js index c61c3d726c1..c5d754fc6fe 100644 --- a/test/commands/mset.spec.js +++ b/test/commands/mset.spec.js @@ -89,7 +89,7 @@ describe("The 'mset' method", function () { it("sets the value correctly with array syntax", function (done) { client.mset([key, value2, key2, value]); - client.get([key, helper.isString(value2)]); + client.get(key, helper.isString(value2)); client.get(key2, helper.isString(value, done)); }); }); diff --git a/test/multi.spec.js b/test/multi.spec.js index 86dfe93162f..4d17c927ef3 100644 --- a/test/multi.spec.js +++ b/test/multi.spec.js @@ -330,8 +330,8 @@ describe("The 'multi' method", function () { arr4, [["mset", "multifoo2", "multibar2", "multifoo3", "multibar3"], helper.isString('OK')], ["hmset", arr], - [["hmset", "multihmset2", "multibar2", "multifoo3", "multibar3", "test", helper.isString('OK')]], - ["hmset", ["multihmset", "multibar", "multifoo", helper.isString('OK')]], + [["hmset", "multihmset2", "multibar2", "multifoo3", "multibar3", "test"], helper.isString('OK')], + ["hmset", ["multihmset", "multibar", "multifoo"], helper.isString('OK')], ["hmset", arr3, helper.isString('OK')], ['hmset', now, {123456789: "abcdefghij", "some manner of key": "a type of value", "otherTypes": 555}], ['hmset', 'key2', {"0123456789": "abcdefghij", "some manner of key": "a type of value", "otherTypes": 999}, helper.isString('OK')], @@ -399,7 +399,7 @@ describe("The 'multi' method", function () { it('allows multiple commands to work the same as normal to be performed using a chaining API', function (done) { client.multi() .mset(['some', '10', 'keys', '20']) - .incr(['some', helper.isNumber(11)]) + .incr('some', helper.isNumber(11)) .incr(['keys'], helper.isNumber(21)) .mget('some', 'keys') .exec(function (err, replies) { @@ -416,7 +416,7 @@ describe("The 'multi' method", function () { it('allows multiple commands to work the same as normal to be performed using a chaining API promisified', function () { return client.multi() .mset(['some', '10', 'keys', '20']) - .incr(['some', helper.isNumber(11)]) + .incr('some', helper.isNumber(11)) .incr(['keys'], helper.isNumber(21)) .mget('some', 'keys') .execAsync() From 518e46dcc736fad7538b8ec59c47f31d41d12eb6 Mon Sep 17 00:00:00 2001 From: Ruben Bridgewater Date: Thu, 21 Jan 2016 22:36:07 +0100 Subject: [PATCH 0549/1748] Use a own clone function instead of using JSON.parse(JSON.stringify()) This will also clone functions --- index.js | 13 ++++++------- lib/utils.js | 29 ++++++++++++++++++++++++++++- 2 files changed, 34 insertions(+), 8 deletions(-) diff --git a/index.js b/index.js index 811312d9b51..f0c220bf00f 100644 --- a/index.js +++ b/index.js @@ -15,7 +15,6 @@ var default_port = 6379; var default_host = '127.0.0.1'; function noop () {} -function clone (obj) { return JSON.parse(JSON.stringify(obj || {})); } function debug (msg) { if (exports.debug_mode) { console.error(msg); } } function handle_detect_buffers_reply (reply, command, buffer_args) { @@ -35,7 +34,7 @@ exports.debug_mode = /\bredis\b/i.test(process.env.NODE_DEBUG); function RedisClient (options) { // Copy the options so they are not mutated - options = clone(options); + options = utils.clone(options); events.EventEmitter.call(this); var cnx_options = {}; var self = this; @@ -205,8 +204,8 @@ RedisClient.prototype.cork = noop; RedisClient.prototype.uncork = noop; RedisClient.prototype.duplicate = function (options) { - var existing_options = clone(this.options); - options = clone(options); + var existing_options = utils.clone(this.options); + options = utils.clone(options); for (var elem in options) { // jshint ignore: line existing_options[elem] = options[elem]; } @@ -1293,11 +1292,11 @@ Multi.prototype.exec = Multi.prototype.EXEC = Multi.prototype.exec_batch = funct var createClient = function (port_arg, host_arg, options) { if (typeof port_arg === 'number' || typeof port_arg === 'string' && /^\d+$/.test(port_arg)) { - options = clone(options); + options = utils.clone(options); options.host = host_arg; options.port = port_arg; } else if (typeof port_arg === 'string' || port_arg && port_arg.url) { - options = clone(port_arg.url ? port_arg : host_arg || options); + options = utils.clone(port_arg.url ? port_arg : host_arg || options); var parsed = URL.parse(port_arg.url || port_arg, true, true); // [redis:]//[[user][:password]@][host][:port][/db-number][?db=db-number[&password=bar[&option=value]]] if (parsed.hostname || parsed.slashes) { // The host might be an empty string @@ -1326,7 +1325,7 @@ var createClient = function (port_arg, host_arg, options) { options.path = port_arg; } } else if (typeof port_arg === 'object' || port_arg === undefined) { - options = clone(port_arg || options); + options = utils.clone(port_arg || options); options.host = options.host || host_arg; } if (!options) { diff --git a/lib/utils.js b/lib/utils.js index 9fbb22cb60e..8cdfb1f2b56 100644 --- a/lib/utils.js +++ b/lib/utils.js @@ -38,9 +38,36 @@ function print (err, reply) { var redisErrCode = /^([A-Z]+)\s+(.+)$/; +// Deep clone arbitrary objects with arrays. Can't handle cyclic structures (results in a range error) +function clone (obj) { + if (obj) { + var copy; + if (obj.constructor === Array) { + copy = new Array(obj.length); + for (var i = 0; i < obj.length; i++) { + copy[i] = clone(obj[i]); + } + return copy; + } + if (obj.constructor === Object) { + copy = {}; + for (var elem in obj) { + if (!obj.hasOwnProperty(elem)) { + // Do not add non own properties to the cloned object + continue; + } + copy[elem] = clone(obj[elem]); + } + return copy; + } + } + return obj; +} + module.exports = { reply_to_strings: replyToStrings, reply_to_object: replyToObject, print: print, - err_code: redisErrCode + err_code: redisErrCode, + clone: clone }; From fb0eaf4d41462c724b978efb4813d364fb8c640e Mon Sep 17 00:00:00 2001 From: Ruben Bridgewater Date: Thu, 21 Jan 2016 22:37:34 +0100 Subject: [PATCH 0550/1748] Unify auth handling --- index.js | 58 +++++++++++++++++++---------------------------- test/auth.spec.js | 4 ++-- 2 files changed, 25 insertions(+), 37 deletions(-) diff --git a/index.js b/index.js index f0c220bf00f..481fd1f0b1a 100644 --- a/index.js +++ b/index.js @@ -188,8 +188,8 @@ RedisClient.prototype.create_stream = function () { } // Fire the command before redis is connected to be sure it's the first fired command - if (typeof this.auth_pass === 'string') { - this.do_auth(); + if (this.auth_pass !== undefined) { + this.auth(this.auth_pass); } }; @@ -274,29 +274,6 @@ RedisClient.prototype.on_error = function (err) { this.connection_gone('error'); }; -var noPasswordIsSet = /no password is set/; - -RedisClient.prototype.do_auth = function () { - var self = this; - debug('Sending auth to ' + self.address + ' id ' + self.connection_id); - - this.send_anyway = true; - this.send_command('auth', [this.auth_pass], function (err, res) { - if (err) { - if (noPasswordIsSet.test(err.message)) { - debug('Warning: Redis server does not require a password, but a password was supplied.'); - err = null; - res = 'OK'; - } else { - self.emit('error', err); - } - } else { - debug('Auth succeeded ' + self.address + ' id ' + self.connection_id); - } - }); - this.send_anyway = false; -}; - RedisClient.prototype.on_connect = function () { debug('Stream connected ' + this.address + ' id ' + this.connection_id); @@ -1034,18 +1011,29 @@ RedisClient.prototype.callback_emit_error = function (callback, err) { } }; -// Stash auth for connect and reconnect. Send immediately if already connected. -RedisClient.prototype.auth = RedisClient.prototype.AUTH = function (pass, callback) { - if (typeof pass !== 'string') { - var err = new Error('The password has to be of type "string"'); - err.command = 'AUTH'; - this.callback_emit_error(callback, err); - return true; - } +var noPasswordIsSet = /no password is set/; + +RedisClient.prototype.auth = function (pass, callback) { + var self = this; + debug('Sending auth to ' + self.address + ' id ' + self.connection_id); + + // Stash auth for connect and reconnect. this.auth_pass = pass; - debug('Saving auth as ' + this.auth_pass); this.send_anyway = true; - var tmp = this.send_command('auth', [pass], callback); + var tmp = this.send_command('auth', [pass], function (err, res) { + if (err) { + if (noPasswordIsSet.test(err.message)) { + self.warn('Warning: Redis server does not require a password, but a password was supplied.'); + err = null; + res = 'OK'; + } else if (!callback) { + self.emit('error', err); + } + } + if (callback) { + callback(err, res); + } + }); this.send_anyway = false; return tmp; }; diff --git a/test/auth.spec.js b/test/auth.spec.js index 9e648d63e07..5f18f350661 100644 --- a/test/auth.spec.js +++ b/test/auth.spec.js @@ -161,7 +161,7 @@ describe("client authentication", function () { client = redis.createClient.apply(redis.createClient, args); var async = true; client.auth(undefined, function(err, res) { - assert.strictEqual(err.message, 'The password has to be of type "string"'); + assert.strictEqual(err.message, 'ERR invalid password'); assert.strictEqual(err.command, 'AUTH'); assert.strictEqual(res, undefined); async = false; @@ -175,7 +175,7 @@ describe("client authentication", function () { client = redis.createClient.apply(redis.createClient, args); client.on('error', function (err) { - assert.strictEqual(err.message, 'The password has to be of type "string"'); + assert.strictEqual(err.message, 'ERR invalid password'); assert.strictEqual(err.command, 'AUTH'); done(); }); From 32172cd29120811b4e3c899a3df00ff40bea5297 Mon Sep 17 00:00:00 2001 From: Ruben Bridgewater Date: Thu, 21 Jan 2016 22:38:05 +0100 Subject: [PATCH 0551/1748] Use instanceof Array instead of Array.isArray The reply is being done with a regular array and therefor will be the same array instance --- index.js | 7 +++---- 1 file changed, 3 insertions(+), 4 deletions(-) diff --git a/index.js b/index.js index 481fd1f0b1a..7b3db9198b5 100644 --- a/index.js +++ b/index.js @@ -535,8 +535,6 @@ RedisClient.prototype.drain = function () { RedisClient.prototype.emit_idle = function (queue_len) { if (this.pub_sub_mode === false && queue_len === 0) { - // Free the queue capacity memory by using a new queue - this.command_queue = new Queue(); this.emit('idle'); } }; @@ -547,7 +545,7 @@ RedisClient.prototype.return_reply = function (reply) { // If the 'reply' here is actually a message received asynchronously due to a // pubsub subscription, don't pop the command queue as we'll only be consuming // the head command prematurely. - if (this.pub_sub_mode && Array.isArray(reply) && reply[0]) { + if (this.pub_sub_mode && reply instanceof Array && reply[0]) { type = reply[0].toString(); } @@ -571,12 +569,13 @@ RedisClient.prototype.return_reply = function (reply) { debug('No callback for reply'); } } else if (this.pub_sub_mode || command_obj && command_obj.sub_command) { - if (Array.isArray(reply)) { + if (reply instanceof Array) { if ((!command_obj || command_obj.buffer_args === false) && !this.options.return_buffers) { reply = utils.reply_to_strings(reply); } type = reply[0].toString(); + // TODO: Add buffer emiters (we have to get all pubsub messages as buffers back in that case) if (type === 'message') { this.emit('message', reply[1], reply[2]); // channel, message } else if (type === 'pmessage') { From 92be1e93c53f0a27978dfa708b1f3c0b9e87b2a6 Mon Sep 17 00:00:00 2001 From: Ruben Bridgewater Date: Sun, 31 Jan 2016 14:57:07 +0100 Subject: [PATCH 0552/1748] Remove buffer bench This benchmark should not be relied on. v8 is going to inline code and the benchmark expactations won't be met anymore --- benchmarks/buffer_bench.js | 91 -------------------------------------- 1 file changed, 91 deletions(-) delete mode 100644 benchmarks/buffer_bench.js diff --git a/benchmarks/buffer_bench.js b/benchmarks/buffer_bench.js deleted file mode 100644 index 63dae1e606b..00000000000 --- a/benchmarks/buffer_bench.js +++ /dev/null @@ -1,91 +0,0 @@ -'use strict'; - -var source = new Buffer(100), - dest = new Buffer(100), i, j, k, tmp, count = 1000000, bytes = 100; - -for (i = 99 ; i >= 0 ; i--) { - source[i] = 120; -} - -var str = 'This is a nice String.', - buf = new Buffer('This is a lovely Buffer.'); - -var start = new Date(); -for (i = count * 100; i > 0 ; i--) { - if (Buffer.isBuffer(str)) {} -} -var end = new Date(); -console.log('Buffer.isBuffer(str) ' + (end - start) + ' ms'); - -var start = new Date(); -for (i = count * 100; i > 0 ; i--) { - if (Buffer.isBuffer(buf)) {} -} -var end = new Date(); -console.log('Buffer.isBuffer(buf) ' + (end - start) + ' ms'); - -var start = new Date(); -for (i = count * 100; i > 0 ; i--) { - if (str instanceof Buffer) {} -} -var end = new Date(); -console.log('str instanceof Buffer ' + (end - start) + ' ms'); - -var start = new Date(); -for (i = count * 100; i > 0 ; i--) { - if (buf instanceof Buffer) {} -} -var end = new Date(); -console.log('buf instanceof Buffer ' + (end - start) + ' ms'); - -for (i = bytes ; i > 0 ; i --) { - var start = new Date(); - for (j = count ; j > 0; j--) { - tmp = source.toString('ascii', 0, bytes); - } - var end = new Date(); - console.log('toString() ' + i + ' bytes ' + (end - start) + ' ms'); -} - -for (i = bytes ; i > 0 ; i --) { - var start = new Date(); - for (j = count ; j > 0; j--) { - tmp = ''; - for (k = 0; k <= i ; k++) { - tmp += String.fromCharCode(source[k]); - } - } - var end = new Date(); - console.log('manual string ' + i + ' bytes ' + (end - start) + ' ms'); -} - -for (i = bytes ; i > 0 ; i--) { - var start = new Date(); - for (j = count ; j > 0 ; j--) { - for (k = i ; k > 0 ; k--) { - dest[k] = source[k]; - } - } - var end = new Date(); - console.log('Manual copy ' + i + ' bytes ' + (end - start) + ' ms'); -} - -for (i = bytes ; i > 0 ; i--) { - var start = new Date(); - for (j = count ; j > 0 ; j--) { - for (k = i ; k > 0 ; k--) { - dest[k] = 120; - } - } - var end = new Date(); - console.log('Direct assignment ' + i + ' bytes ' + (end - start) + ' ms'); -} - -for (i = bytes ; i > 0 ; i--) { - var start = new Date(); - for (j = count ; j > 0 ; j--) { - source.copy(dest, 0, 0, i); - } - var end = new Date(); - console.log('Buffer.copy() ' + i + ' bytes ' + (end - start) + ' ms'); -} From a53bb3d7d1f50b9db7605d36800bf1e53d2aca45 Mon Sep 17 00:00:00 2001 From: greenkeeperio-bot Date: Fri, 4 Mar 2016 07:28:10 +0100 Subject: [PATCH 0553/1748] chore(package): update nyc to version 6.0.0 http://greenkeeper.io/ --- package.json | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/package.json b/package.json index ac010b7f322..bfd527326ce 100644 --- a/package.json +++ b/package.json @@ -36,7 +36,7 @@ "jshint": "^2.8.0", "metrics": "^0.1.9", "mocha": "^2.3.2", - "nyc": "^5.2.0", + "nyc": "^6.0.0", "optional-dev-dependency": "^1.1.0", "tcp-port-used": "^0.1.2", "uuid": "^2.0.1", From f2ca678afed5da4ae54f1dbd36ddb7c2b1a34511 Mon Sep 17 00:00:00 2001 From: Ruben Bridgewater Date: Sun, 31 Jan 2016 14:59:45 +0100 Subject: [PATCH 0554/1748] Update benchmark to also signal tcp / socket connections --- benchmarks/multi_bench.js | 39 ++++++++++++++++----------------------- 1 file changed, 16 insertions(+), 23 deletions(-) diff --git a/benchmarks/multi_bench.js b/benchmarks/multi_bench.js index 1c3c08ae139..f8b9299564b 100644 --- a/benchmarks/multi_bench.js +++ b/benchmarks/multi_bench.js @@ -25,7 +25,8 @@ var num_clients = returnArg('clients', 1); var run_time = returnArg('time', 2500); // ms var versions_logged = false; var client_options = { - parser: returnArg('parser', 'hiredis') + parser: returnArg('parser', 'hiredis'), + path: returnArg('socket') // '/tmp/redis.sock' }; var small_str, large_str, small_buf, large_buf, very_large_str, very_large_buf; @@ -40,7 +41,7 @@ function lpad(input, len, chr) { metrics.Histogram.prototype.print_line = function () { var obj = this.printObj(); - return lpad((obj.min / 1000000).toFixed(2), 6) + '/' + lpad((obj.max / 1000000).toFixed(2), 6) + '/' + lpad((obj.mean / 1000000).toFixed(2), 6); + return lpad((obj.min / 1e6).toFixed(2), 6) + '/' + lpad((obj.max / 1e6).toFixed(2), 6) + '/' + lpad((obj.mean / 1e6).toFixed(2), 6); }; function Test(args) { @@ -54,6 +55,10 @@ function Test(args) { this.batch_pipeline = this.args.batch || 0; this.client_options = args.client_options || {}; this.client_options.parser = client_options.parser; + this.client_options.connect_timeout = 1000; + if (client_options.path) { + this.client_options.path = client_options.path; + } this.connect_latency = new metrics.Histogram(); this.ready_latency = new metrics.Histogram(); this.command_latency = new metrics.Histogram(); @@ -78,9 +83,14 @@ Test.prototype.new_client = function (id) { }); new_client.on('ready', function () { - if (! versions_logged) { - console.log('Client count: ' + num_clients + ', node version: ' + process.versions.node + ', server version: ' + - new_client.server_info.redis_version + ', parser: ' + new_client.reply_parser.name); + if (!versions_logged) { + console.log( + 'clients: ' + num_clients + + ', NodeJS: ' + process.versions.node + + ', Redis: ' + new_client.server_info.redis_version + + ', parser: ' + client_options.parser + + ', connected by: ' + (client_options.path ? 'socket' : 'tcp') + ); versions_logged = true; } self.ready_latency.update(Date.now() - new_client.create_time); @@ -197,7 +207,7 @@ Test.prototype.print_stats = function () { totalTime += duration; console.log('min/max/avg: ' + this.command_latency.print_line() + ' ' + lpad(duration, 6) + 'ms total, ' + - lpad((this.commands_completed / (duration / 1000)).toFixed(2), 9) + ' ops/sec'); + lpad(Math.round(this.commands_completed / (duration / 1000)), 7) + ' ops/sec'); }; small_str = '1234'; @@ -208,71 +218,54 @@ very_large_str = (new Array((4 * 1024 * 1024) + 1).join('-')); very_large_buf = new Buffer(very_large_str); tests.push(new Test({descr: 'PING', command: 'ping', args: [], pipeline: 1})); -// tests.push(new Test({descr: 'PING', command: 'ping', args: [], pipeline: 50})); tests.push(new Test({descr: 'PING', command: 'ping', args: [], batch: 50})); tests.push(new Test({descr: 'SET 4B str', command: 'set', args: ['foo_rand000000000000', small_str], pipeline: 1})); -// tests.push(new Test({descr: 'SET 4B str', command: 'set', args: ['foo_rand000000000000', small_str], pipeline: 50})); tests.push(new Test({descr: 'SET 4B str', command: 'set', args: ['foo_rand000000000000', small_str], batch: 50})); tests.push(new Test({descr: 'SET 4B buf', command: 'set', args: ['foo_rand000000000000', small_buf], pipeline: 1})); -// tests.push(new Test({descr: 'SET 4B buf', command: 'set', args: ['foo_rand000000000000', small_buf], pipeline: 50})); tests.push(new Test({descr: 'SET 4B buf', command: 'set', args: ['foo_rand000000000000', small_buf], batch: 50})); tests.push(new Test({descr: 'GET 4B str', command: 'get', args: ['foo_rand000000000000'], pipeline: 1})); -// tests.push(new Test({descr: 'GET 4B str', command: 'get', args: ['foo_rand000000000000'], pipeline: 50})); tests.push(new Test({descr: 'GET 4B str', command: 'get', args: ['foo_rand000000000000'], batch: 50})); tests.push(new Test({descr: 'GET 4B buf', command: 'get', args: ['foo_rand000000000000'], pipeline: 1, client_opts: { return_buffers: true} })); -// tests.push(new Test({descr: 'GET 4B buf', command: 'get', args: ['foo_rand000000000000'], pipeline: 50, client_opts: { return_buffers: true} })); tests.push(new Test({descr: 'GET 4B buf', command: 'get', args: ['foo_rand000000000000'], batch: 50, client_opts: { return_buffers: true} })); tests.push(new Test({descr: 'SET 4KiB str', command: 'set', args: ['foo_rand000000000001', large_str], pipeline: 1})); -// tests.push(new Test({descr: 'SET 4KiB str', command: 'set', args: ['foo_rand000000000001', large_str], pipeline: 50})); tests.push(new Test({descr: 'SET 4KiB str', command: 'set', args: ['foo_rand000000000001', large_str], batch: 50})); tests.push(new Test({descr: 'SET 4KiB buf', command: 'set', args: ['foo_rand000000000001', large_buf], pipeline: 1})); -// tests.push(new Test({descr: 'SET 4KiB buf', command: 'set', args: ['foo_rand000000000001', large_buf], pipeline: 50})); tests.push(new Test({descr: 'SET 4KiB buf', command: 'set', args: ['foo_rand000000000001', large_buf], batch: 50})); tests.push(new Test({descr: 'GET 4KiB str', command: 'get', args: ['foo_rand000000000001'], pipeline: 1})); -// tests.push(new Test({descr: 'GET 4KiB str', command: 'get', args: ['foo_rand000000000001'], pipeline: 50})); tests.push(new Test({descr: 'GET 4KiB str', command: 'get', args: ['foo_rand000000000001'], batch: 50})); tests.push(new Test({descr: 'GET 4KiB buf', command: 'get', args: ['foo_rand000000000001'], pipeline: 1, client_opts: { return_buffers: true} })); -// tests.push(new Test({descr: 'GET 4KiB buf', command: 'get', args: ['foo_rand000000000001'], pipeline: 50, client_opts: { return_buffers: true} })); tests.push(new Test({descr: 'GET 4KiB buf', command: 'get', args: ['foo_rand000000000001'], batch: 50, client_opts: { return_buffers: true} })); tests.push(new Test({descr: 'INCR', command: 'incr', args: ['counter_rand000000000000'], pipeline: 1})); -// tests.push(new Test({descr: 'INCR', command: 'incr', args: ['counter_rand000000000000'], pipeline: 50})); tests.push(new Test({descr: 'INCR', command: 'incr', args: ['counter_rand000000000000'], batch: 50})); tests.push(new Test({descr: 'LPUSH', command: 'lpush', args: ['mylist', small_str], pipeline: 1})); -// tests.push(new Test({descr: 'LPUSH', command: 'lpush', args: ['mylist', small_str], pipeline: 50})); tests.push(new Test({descr: 'LPUSH', command: 'lpush', args: ['mylist', small_str], batch: 50})); tests.push(new Test({descr: 'LRANGE 10', command: 'lrange', args: ['mylist', '0', '9'], pipeline: 1})); -// tests.push(new Test({descr: 'LRANGE 10', command: 'lrange', args: ['mylist', '0', '9'], pipeline: 50})); tests.push(new Test({descr: 'LRANGE 10', command: 'lrange', args: ['mylist', '0', '9'], batch: 50})); tests.push(new Test({descr: 'LRANGE 100', command: 'lrange', args: ['mylist', '0', '99'], pipeline: 1})); -// tests.push(new Test({descr: 'LRANGE 100', command: 'lrange', args: ['mylist', '0', '99'], pipeline: 50})); tests.push(new Test({descr: 'LRANGE 100', command: 'lrange', args: ['mylist', '0', '99'], batch: 50})); tests.push(new Test({descr: 'SET 4MiB str', command: 'set', args: ['foo_rand000000000002', very_large_str], pipeline: 1})); -// tests.push(new Test({descr: 'SET 4MiB str', command: 'set', args: ['foo_rand000000000002', very_large_str], pipeline: 20})); tests.push(new Test({descr: 'SET 4MiB str', command: 'set', args: ['foo_rand000000000002', very_large_str], batch: 20})); tests.push(new Test({descr: 'SET 4MiB buf', command: 'set', args: ['foo_rand000000000002', very_large_buf], pipeline: 1})); -// tests.push(new Test({descr: 'SET 4MiB buf', command: 'set', args: ['foo_rand000000000002', very_large_buf], pipeline: 20})); tests.push(new Test({descr: 'SET 4MiB buf', command: 'set', args: ['foo_rand000000000002', very_large_buf], batch: 20})); tests.push(new Test({descr: 'GET 4MiB str', command: 'get', args: ['foo_rand000000000002'], pipeline: 1})); -// tests.push(new Test({descr: 'GET 4MiB str', command: 'get', args: ['foo_rand000000000002'], pipeline: 20})); tests.push(new Test({descr: 'GET 4MiB str', command: 'get', args: ['foo_rand000000000002'], batch: 20})); tests.push(new Test({descr: 'GET 4MiB buf', command: 'get', args: ['foo_rand000000000002'], pipeline: 1, client_opts: { return_buffers: true} })); -// tests.push(new Test({descr: 'GET 4MiB buf', command: 'get', args: ['foo_rand000000000002'], pipeline: 20, client_opts: { return_buffers: true} })); tests.push(new Test({descr: 'GET 4MiB buf', command: 'get', args: ['foo_rand000000000002'], batch: 20, client_opts: { return_buffers: true} })); function next() { From ce80569bfed54eee070cd36b46f0816a17c69c06 Mon Sep 17 00:00:00 2001 From: Ruben Bridgewater Date: Sun, 31 Jan 2016 15:00:27 +0100 Subject: [PATCH 0555/1748] Update npm ignore file --- .npmignore | 15 ++++++++------- 1 file changed, 8 insertions(+), 7 deletions(-) diff --git a/.npmignore b/.npmignore index 886d9eab914..b0238e058cb 100644 --- a/.npmignore +++ b/.npmignore @@ -1,9 +1,10 @@ examples/ -benches/ +benchmarks/ test/ -diff_multi_bench_output.js -generate_commands.js -multi_bench.js -test-unref.js -changelog.md -*.log \ No newline at end of file +.nyc_output/ +coverage/ +.tern-port +*.log +*.rdb +*.out +*.yml From 614e35ab57eabe55267e59ef8c625fbbdda54eca Mon Sep 17 00:00:00 2001 From: Ruben Bridgewater Date: Mon, 22 Feb 2016 19:38:07 +0100 Subject: [PATCH 0556/1748] Move multi; commands; createClient code into separate files --- lib/command.js | 4 +- lib/commands.js | 86 +++++++++++++++ lib/createClient.js | 67 ++++++++++++ lib/debug.js | 11 ++ lib/individualCommands.js | 137 +++++++++++++++++++++++ lib/multi.js | 224 ++++++++++++++++++++++++++++++++++++++ lib/utils.js | 83 ++++++++++---- 7 files changed, 586 insertions(+), 26 deletions(-) create mode 100644 lib/commands.js create mode 100644 lib/createClient.js create mode 100644 lib/debug.js create mode 100644 lib/individualCommands.js create mode 100644 lib/multi.js diff --git a/lib/command.js b/lib/command.js index d04052ff6d3..0c01976bb7c 100644 --- a/lib/command.js +++ b/lib/command.js @@ -2,10 +2,10 @@ // This Command constructor is ever so slightly faster than using an object literal, but more importantly, using // a named constructor helps it show up meaningfully in the V8 CPU profiler and in heap snapshots. -function Command(command, args, buffer_args, callback) { +function Command(command, args, callback) { this.command = command; this.args = args; - this.buffer_args = buffer_args; + this.buffer_args = false; this.callback = callback; } diff --git a/lib/commands.js b/lib/commands.js new file mode 100644 index 00000000000..734e7b7060c --- /dev/null +++ b/lib/commands.js @@ -0,0 +1,86 @@ +'use strict'; + +var commands = require('redis-commands'); +var Multi = require('./multi'); +var RedisClient = require('../').RedisClient; + +// TODO: Rewrite this including the invidual commands into a Commands class +// that provided a functionality to add new commands to the client + +commands.list.forEach(function (command) { + + // Do not override existing functions + if (!RedisClient.prototype[command]) { + RedisClient.prototype[command.toUpperCase()] = RedisClient.prototype[command] = function () { + var arr; + var len = arguments.length; + var callback; + var i = 0; + if (Array.isArray(arguments[0])) { + arr = arguments[0]; + if (len === 2) { + callback = arguments[1]; + } + } else if (len > 1 && Array.isArray(arguments[1])) { + if (len === 3) { + callback = arguments[2]; + } + len = arguments[1].length; + arr = new Array(len + 1); + arr[0] = arguments[0]; + for (; i < len; i += 1) { + arr[i + 1] = arguments[1][i]; + } + } else { + // The later should not be the average use case + if (len !== 0 && (typeof arguments[len - 1] === 'function' || typeof arguments[len - 1] === 'undefined')) { + len--; + callback = arguments[len]; + } + arr = new Array(len); + for (; i < len; i += 1) { + arr[i] = arguments[i]; + } + } + return this.send_command(command, arr, callback); + }; + } + + // Do not override existing functions + if (!Multi.prototype[command]) { + Multi.prototype[command.toUpperCase()] = Multi.prototype[command] = function () { + var arr; + var len = arguments.length; + var callback; + var i = 0; + if (Array.isArray(arguments[0])) { + arr = arguments[0]; + if (len === 2) { + callback = arguments[1]; + } + } else if (len > 1 && Array.isArray(arguments[1])) { + if (len === 3) { + callback = arguments[2]; + } + len = arguments[1].length; + arr = new Array(len + 1); + arr[0] = arguments[0]; + for (; i < len; i += 1) { + arr[i + 1] = arguments[1][i]; + } + } else { + // The later should not be the average use case + if (len !== 0 && (typeof arguments[len - 1] === 'function' || typeof arguments[len - 1] === 'undefined')) { + len--; + callback = arguments[len]; + } + arr = new Array(len); + for (; i < len; i += 1) { + arr[i] = arguments[i]; + } + } + this.queue.push([command, arr, callback]); + return this; + }; + } +}); diff --git a/lib/createClient.js b/lib/createClient.js new file mode 100644 index 00000000000..3b14ef06f39 --- /dev/null +++ b/lib/createClient.js @@ -0,0 +1,67 @@ +'use strict'; + +var utils = require('./utils'); +var URL = require('url'); + +module.exports = function createClient (port_arg, host_arg, options) { + + if (typeof port_arg === 'number' || typeof port_arg === 'string' && /^\d+$/.test(port_arg)) { + + var host; + if (typeof host_arg === 'string') { + host = host_arg; + } else { + options = options || host_arg; + } + options = utils.clone(options); + options.host = host || options.host; + options.port = port_arg; + + } else if (typeof port_arg === 'string' || port_arg && port_arg.url) { + + options = utils.clone(port_arg.url ? port_arg : host_arg || options); + var parsed = URL.parse(port_arg.url || port_arg, true, true); + + // [redis:]//[[user][:password]@][host][:port][/db-number][?db=db-number[&password=bar[&option=value]]] + if (parsed.hostname || parsed.slashes) { // The host might be an empty string + if (parsed.auth) { + options.password = parsed.auth.split(':')[1]; + } + if (!/^([a-z]+:)?\/\//i.test(parsed.href)) { + throw new Error('Connection string must use the "redis:" protocol or begin with slashes //'); + } + if (parsed.pathname && parsed.pathname !== '/') { + options.db = parsed.pathname.substr(1); + } + options.host = parsed.hostname; + options.port = parsed.port; + + if (parsed.search !== '') { + var elem; + for (elem in parsed.query) { // jshint ignore: line + // If options are passed twice, only the parsed options will be used + if (elem in options) { + if (options[elem] === parsed.query[elem]) { + console.warn('node_redis: WARNING: You passed the ' + elem + ' option twice!'); + } else { + throw new Error('The ' + elem + ' option is added twice and does not match'); + } + } + options[elem] = parsed.query[elem]; + } + } + } else { + options.path = port_arg; + } + + } else if (typeof port_arg === 'object' || port_arg === undefined) { + options = utils.clone(port_arg || options); + options.host = options.host || host_arg; + } + + if (!options) { + throw new Error('Unknown type of connection in createClient()'); + } + + return options; +}; diff --git a/lib/debug.js b/lib/debug.js new file mode 100644 index 00000000000..3f9d482bbca --- /dev/null +++ b/lib/debug.js @@ -0,0 +1,11 @@ +'use strict'; + +var index = require('../'); + +function debug (msg) { + if (index.debug_mode) { + console.error(msg); + } +} + +module.exports = debug; diff --git a/lib/individualCommands.js b/lib/individualCommands.js new file mode 100644 index 00000000000..b162238cbe9 --- /dev/null +++ b/lib/individualCommands.js @@ -0,0 +1,137 @@ +'use strict'; + +var utils = require('./utils'); +var debug = require('./debug'); +var Multi = require('./multi'); +var no_password_is_set = /no password is set/; +var RedisClient = require('../').RedisClient; + +/******************************** +Replace built-in redis functions +********************************/ + +RedisClient.prototype.multi = RedisClient.prototype.MULTI = function multi (args) { + var multi = new Multi(this, args); + multi.exec = multi.EXEC = multi.exec_transaction; + return multi; +}; + +// ATTENTION: This is not a native function but is still handled as a individual command as it behaves just the same as multi +RedisClient.prototype.batch = RedisClient.prototype.BATCH = function batch (args) { + return new Multi(this, args); +}; + +// Store db in this.select_db to restore it on reconnect +RedisClient.prototype.select = RedisClient.prototype.SELECT = function select (db, callback) { + var self = this; + return this.send_command('select', [db], function (err, res) { + if (err === null) { + self.selected_db = db; + } + utils.callback_or_emit(self, callback, err, res); + }); +}; + +// Store info in this.server_info after each call +RedisClient.prototype.info = RedisClient.prototype.INFO = function info (callback) { + var self = this; + var ready = this.ready; + this.ready = ready || this.offline_queue.length === 0; // keep the execution order intakt + var tmp = this.send_command('info', [], function (err, res) { + if (res) { + var obj = {}; + var lines = res.toString().split('\r\n'); + var line, parts, sub_parts; + + for (var i = 0; i < lines.length; i++) { + parts = lines[i].split(':'); + if (parts[1]) { + if (parts[0].indexOf('db') === 0) { + sub_parts = parts[1].split(','); + obj[parts[0]] = {}; + while (line = sub_parts.pop()) { + line = line.split('='); + obj[parts[0]][line[0]] = +line[1]; + } + } else { + obj[parts[0]] = parts[1]; + } + } + } + obj.versions = []; + /* istanbul ignore else: some redis servers do not send the version */ + if (obj.redis_version) { + obj.redis_version.split('.').forEach(function (num) { + obj.versions.push(+num); + }); + } + // Expose info key/vals to users + self.server_info = obj; + } else { + self.server_info = {}; + } + utils.callback_or_emit(self, callback, err, res); + }); + this.ready = ready; + return tmp; +}; + +RedisClient.prototype.auth = RedisClient.prototype.AUTH = function auth (pass, callback) { + var self = this; + var ready = this.ready; + debug('Sending auth to ' + self.address + ' id ' + self.connection_id); + + // Stash auth for connect and reconnect. + this.auth_pass = pass; + this.ready = this.offline_queue.length === 0; // keep the execution order intakt + var tmp = this.send_command('auth', [pass], function (err, res) { + if (err && no_password_is_set.test(err.message)) { + self.warn('Warning: Redis server does not require a password, but a password was supplied.'); + err = null; + res = 'OK'; + } + + utils.callback_or_emit(self, callback, err, res); + }); + this.ready = ready; + return tmp; +}; + +RedisClient.prototype.hmset = RedisClient.prototype.HMSET = function hmset () { + var arr, + len = arguments.length, + callback, + i = 0; + if (Array.isArray(arguments[0])) { + arr = arguments[0]; + callback = arguments[1]; + } else if (Array.isArray(arguments[1])) { + if (len === 3) { + callback = arguments[2]; + } + len = arguments[1].length; + arr = new Array(len + 1); + arr[0] = arguments[0]; + for (; i < len; i += 1) { + arr[i + 1] = arguments[1][i]; + } + } else if (typeof arguments[1] === 'object' && (arguments.length === 2 || arguments.length === 3 && typeof arguments[2] === 'function' || typeof arguments[2] === 'undefined')) { + arr = [arguments[0]]; + for (var field in arguments[1]) { // jshint ignore: line + arr.push(field, arguments[1][field]); + } + callback = arguments[2]; + } else { + len = arguments.length; + // The later should not be the average use case + if (len !== 0 && (typeof arguments[len - 1] === 'function' || typeof arguments[len - 1] === 'undefined')) { + len--; + callback = arguments[len]; + } + arr = new Array(len); + for (; i < len; i += 1) { + arr[i] = arguments[i]; + } + } + return this.send_command('hmset', arr, callback); +}; diff --git a/lib/multi.js b/lib/multi.js new file mode 100644 index 00000000000..5a06e1096ab --- /dev/null +++ b/lib/multi.js @@ -0,0 +1,224 @@ +'use strict'; + +var Queue = require('double-ended-queue'); +var utils = require('./utils'); + +function Multi(client, args) { + this._client = client; + this.queue = new Queue(); + var command, tmp_args; + if (args) { // Either undefined or an array. Fail hard if it's not an array + for (var i = 0; i < args.length; i++) { + command = args[i][0]; + tmp_args = args[i].slice(1); + if (Array.isArray(command)) { + this[command[0]].apply(this, command.slice(1).concat(tmp_args)); + } else { + this[command].apply(this, tmp_args); + } + } + } +} + +Multi.prototype.hmset = Multi.prototype.HMSET = function hmset () { + var arr, + len = 0, + callback, + i = 0; + if (Array.isArray(arguments[0])) { + arr = arguments[0]; + callback = arguments[1]; + } else if (Array.isArray(arguments[1])) { + len = arguments[1].length; + arr = new Array(len + 1); + arr[0] = arguments[0]; + for (; i < len; i += 1) { + arr[i + 1] = arguments[1][i]; + } + callback = arguments[2]; + } else if (typeof arguments[1] === 'object' && (typeof arguments[2] === 'function' || typeof arguments[2] === 'undefined')) { + arr = [arguments[0]]; + for (var field in arguments[1]) { // jshint ignore: line + arr.push(field, arguments[1][field]); + } + callback = arguments[2]; + } else { + len = arguments.length; + // The later should not be the average use case + if (len !== 0 && (typeof arguments[len - 1] === 'function' || typeof arguments[len - 1] === 'undefined')) { + len--; + callback = arguments[len]; + } + arr = new Array(len); + for (; i < len; i += 1) { + arr[i] = arguments[i]; + } + } + this.queue.push(['hmset', arr, callback]); + return this; +}; + +function pipeline_transaction_command (self, command, args, index, cb) { + self._client.send_command(command, args, function (err, reply) { + if (err) { + if (cb) { + cb(err); + } + err.position = index; + self.errors.push(err); + } + }); +} + +Multi.prototype.exec_atomic = function exec_atomic (callback) { + if (this.queue.length < 2) { + return this.exec_batch(callback); + } + return this.exec(callback); +}; + +function multi_callback (self, err, replies) { + var i = 0, args; + + if (err) { + // The errors would be circular + var connection_error = ['CONNECTION_BROKEN', 'UNCERTAIN_STATE'].indexOf(err.code) !== -1; + err.errors = connection_error ? [] : self.errors; + if (self.callback) { + self.callback(err); + // Exclude connection errors so that those errors won't be emitted twice + } else if (!connection_error) { + self._client.emit('error', err); + } + return; + } + + if (replies) { + while (args = self.queue.shift()) { + if (replies[i] instanceof Error) { + var match = replies[i].message.match(utils.err_code); + // LUA script could return user errors that don't behave like all other errors! + if (match) { + replies[i].code = match[1]; + } + replies[i].command = args[0].toUpperCase(); + if (typeof args[2] === 'function') { + args[2](replies[i]); + } + } else { + // If we asked for strings, even in detect_buffers mode, then return strings: + replies[i] = self._client.handle_reply(replies[i], args[0], self.wants_buffers[i]); + if (typeof args[2] === 'function') { + args[2](null, replies[i]); + } + } + i++; + } + } + + if (self.callback) { + self.callback(null, replies); + } +} + +Multi.prototype.exec_transaction = function exec_transaction (callback) { + var self = this; + var len = self.queue.length; + self.errors = []; + self.callback = callback; + self._client.cork(len + 2); + self.wants_buffers = new Array(len); + pipeline_transaction_command(self, 'multi', []); + // Drain queue, callback will catch 'QUEUED' or error + for (var index = 0; index < len; index++) { + var args = self.queue.get(index); + var command = args[0]; + var cb = args[2]; + // Keep track of who wants buffer responses: + if (self._client.options.detect_buffers) { + self.wants_buffers[index] = false; + for (var i = 0; i < args[1].length; i += 1) { + if (args[1][i] instanceof Buffer) { + self.wants_buffers[index] = true; + break; + } + } + } + pipeline_transaction_command(self, command, args[1], index, cb); + } + + self._client.send_command('exec', [], function(err, replies) { + multi_callback(self, err, replies); + }); + self._client.uncork(); + self._client.writeDefault = self._client.writeStrings; + return !self._client.should_buffer; +}; + +function batch_callback (self, cb, i) { + return function batch_callback (err, res) { + if (err) { + self.results[i] = err; + // Add the position to the error + self.results[i].position = i; + } else { + self.results[i] = res; + } + cb(err, res); + }; +} + +Multi.prototype.exec = Multi.prototype.EXEC = Multi.prototype.exec_batch = function exec_batch (callback) { + var self = this; + var len = self.queue.length; + var index = 0; + var args; + var args_len = 1; + var callback_without_own_cb = function (err, res) { + if (err) { + self.results.push(err); + // Add the position to the error + var i = self.results.length - 1; + self.results[i].position = i; + } else { + self.results.push(res); + } + // Do not emit an error here. Otherwise each error would result in one emit. + // The errors will be returned in the result anyway + }; + var last_callback = function (cb) { + return function (err, res) { + cb(err, res); + callback(null, self.results); + }; + }; + if (len === 0) { + if (callback) { + utils.reply_in_order(self._client, callback, null, []); + } + return true; + } + self.results = []; + self._client.cork(len); + while (args = self.queue.shift()) { + var command = args[0]; + var cb; + args_len = args[1].length - 1; + if (typeof args[2] === 'function') { + cb = batch_callback(self, args[2], index); + } else { + cb = callback_without_own_cb; + } + if (callback && index === len - 1) { + cb = last_callback(cb); + } + self._client.send_command(command, args[1], cb); + index++; + } + self.queue = new Queue(); + self._client.uncork(); + self._client.writeDefault = self._client.writeStrings; + return !self._client.should_buffer; +}; + +module.exports = Multi; diff --git a/lib/utils.js b/lib/utils.js index 8cdfb1f2b56..89d85907fe3 100644 --- a/lib/utils.js +++ b/lib/utils.js @@ -1,22 +1,24 @@ 'use strict'; // hgetall converts its replies to an Object. If the reply is empty, null is returned. +// These function are only called with internal data and have therefor always the same instanceof X function replyToObject(reply) { - if (reply.length === 0 || !Array.isArray(reply)) { // TODO: Check why the isArray check is needed and what value reply has in that case + // The reply might be a string or a buffer if this is called in a transaction (multi) + if (reply.length === 0 || !(reply instanceof Array)) { return null; } var obj = {}; - for (var j = 0; j < reply.length; j += 2) { - obj[reply[j].toString('binary')] = reply[j + 1]; + for (var i = 0; i < reply.length; i += 2) { + obj[reply[i].toString('binary')] = reply[i + 1]; } return obj; } function replyToStrings(reply) { - if (Buffer.isBuffer(reply)) { + if (reply instanceof Buffer) { return reply.toString(); } - if (Array.isArray(reply)) { + if (reply instanceof Array) { var res = new Array(reply.length); for (var i = 0; i < reply.length; i++) { // Recusivly call the function as slowlog returns deep nested replies @@ -39,35 +41,68 @@ function print (err, reply) { var redisErrCode = /^([A-Z]+)\s+(.+)$/; // Deep clone arbitrary objects with arrays. Can't handle cyclic structures (results in a range error) +// Any attribute with a non primitive value besides object and array will be passed by reference (e.g. Buffers, Maps, Functions) function clone (obj) { - if (obj) { - var copy; - if (obj.constructor === Array) { - copy = new Array(obj.length); - for (var i = 0; i < obj.length; i++) { - copy[i] = clone(obj[i]); - } - return copy; + var copy; + if (Array.isArray(obj)) { + copy = new Array(obj.length); + for (var i = 0; i < obj.length; i++) { + copy[i] = clone(obj[i]); } - if (obj.constructor === Object) { - copy = {}; - for (var elem in obj) { - if (!obj.hasOwnProperty(elem)) { - // Do not add non own properties to the cloned object - continue; - } - copy[elem] = clone(obj[elem]); - } - return copy; + return copy; + } + if (Object.prototype.toString.call(obj) === '[object Object]') { + copy = {}; + var elems = Object.keys(obj); + var elem; + while (elem = elems.pop()) { + copy[elem] = clone(obj[elem]); } + return copy; } return obj; } +function convenienceClone (obj) { + return clone(obj) || {}; +} + +function callbackOrEmit (self, callback, err, res) { + if (callback) { + callback(err, res); + } else if (err) { + self.emit('error', err); + } +} + +function replyInOrder (self, callback, err, res) { + var command_obj = self.command_queue.peekBack() || self.offline_queue.peekBack(); + if (!command_obj) { + process.nextTick(function () { + callbackOrEmit(self, callback, err, res); + }); + } else { + var tmp = command_obj.callback; + command_obj.callback = tmp ? + function (e, r) { + tmp(e, r); + callbackOrEmit(self, callback, err, res); + } : + function (e, r) { + if (e) { + self.emit('error', e); + } + callbackOrEmit(self, callback, err, res); + }; + } +} + module.exports = { reply_to_strings: replyToStrings, reply_to_object: replyToObject, print: print, err_code: redisErrCode, - clone: clone + clone: convenienceClone, + callback_or_emit: callbackOrEmit, + reply_in_order: replyInOrder }; From 4f3c4a2ef672af2a7182b72aa92dca0310084932 Mon Sep 17 00:00:00 2001 From: Ruben Bridgewater Date: Thu, 25 Feb 2016 02:37:42 +0100 Subject: [PATCH 0557/1748] Add more tests Add execution order tests Fix flaky test Add utils tests Improve other tests --- test/batch.spec.js | 10 ++- test/commands/set.spec.js | 11 +++- test/connection.spec.js | 15 ++--- test/multi.spec.js | 3 +- test/node_redis.spec.js | 35 +++++++++++ test/rename.spec.js | 19 ++++++ test/utils.spec.js | 129 ++++++++++++++++++++++++++++++++++++++ 7 files changed, 210 insertions(+), 12 deletions(-) create mode 100644 test/utils.spec.js diff --git a/test/batch.spec.js b/test/batch.spec.js index a99cb821731..ba4c9020bba 100644 --- a/test/batch.spec.js +++ b/test/batch.spec.js @@ -63,11 +63,16 @@ describe("The 'batch' method", function () { client.end(true); }); - it("returns an empty array", function (done) { + it("returns an empty array and keep the execution order in takt", function (done) { + var called = false; + client.set('foo', 'bar', function (err, res) { + called = true; + }); var batch = client.batch(); batch.exec(function (err, res) { assert.strictEqual(err, null); assert.strictEqual(res.length, 0); + assert(called); done(); }); }); @@ -328,10 +333,11 @@ describe("The 'batch' method", function () { .exec(done); }); - it("should work without any callback", function (done) { + it("should work without any callback or arguments", function (done) { var batch = client.batch(); batch.set("baz", "binary"); batch.set("foo", "bar"); + batch.ping(); batch.exec(); client.get('foo', helper.isString('bar', done)); diff --git a/test/commands/set.spec.js b/test/commands/set.spec.js index c4866297339..3235d634f64 100644 --- a/test/commands/set.spec.js +++ b/test/commands/set.spec.js @@ -66,14 +66,21 @@ describe("The 'set' method", function () { }); }); - describe("with undefined 'key' and missing 'value' parameter", function () { - it("reports an error", function (done) { + describe("reports an error with invalid parameters", function () { + it("undefined 'key' and missing 'value' parameter", function (done) { client.set(undefined, function (err, res) { helper.isError()(err, null); assert.equal(err.command, 'SET'); done(); }); }); + + it("empty array as second parameter", function (done) { + client.set('foo', [], function (err, res) { + assert.strictEqual(err.message, "ERR wrong number of arguments for 'set' command"); + done(); + }); + }); }); }); diff --git a/test/connection.spec.js b/test/connection.spec.js index 5f5892dea25..c047788847c 100644 --- a/test/connection.spec.js +++ b/test/connection.spec.js @@ -136,13 +136,14 @@ describe("connection tests", function () { var time = Date.now(); client = redis.createClient({ parser: parser, - host: '192.168.74.167', // Should be auto detected as ipv4 + // Auto detect ipv4 and use non routable ip to trigger the timeout + host: '10.255.255.1', connect_timeout: connect_timeout }); process.nextTick(function() { - assert(client.stream._events.timeout); + assert.strictEqual(client.stream.listeners('timeout').length, 1); }); - assert.strictEqual(client.address, '192.168.74.167:6379'); + assert.strictEqual(client.address, '10.255.255.1:6379'); assert.strictEqual(client.connection_options.family, 4); client.on("reconnecting", function (params) { @@ -151,8 +152,8 @@ describe("connection tests", function () { client.on('error', function(err) { assert(/Redis connection in broken state: connection timeout.*?exceeded./.test(err.message)); - assert(Date.now() - time < connect_timeout + 50); - assert(Date.now() - time >= connect_timeout - 50); // Somehow this is triggered to early at times + assert(Date.now() - time < connect_timeout + 25); + assert(Date.now() - time >= connect_timeout - 3); // Timers sometimes trigger early (e.g. 1ms to early) done(); }); }); @@ -165,7 +166,7 @@ describe("connection tests", function () { assert.strictEqual(client.address, '2001:db8::ff00:42:8329:6379'); assert.strictEqual(client.connection_options.family, 6); process.nextTick(function() { - assert.strictEqual(client.stream._events.timeout, undefined); + assert.strictEqual(client.stream.listeners('timeout').length, 0); }); }); @@ -179,7 +180,7 @@ describe("connection tests", function () { }); client.on('connect', function () { assert.strictEqual(client.stream._idleTimeout, -1); - assert.strictEqual(client.stream._events.timeout, undefined); + assert.strictEqual(client.stream.listeners('timeout').length, 0); client.on('ready', done); }); }); diff --git a/test/multi.spec.js b/test/multi.spec.js index 4d17c927ef3..766d5798751 100644 --- a/test/multi.spec.js +++ b/test/multi.spec.js @@ -241,6 +241,7 @@ describe("The 'multi' method", function () { multi1.set("m1", "123"); multi1.get('m1'); multi2.get('m2'); + multi2.ping(); multi1.exec(end); multi2.exec(function(err, res) { @@ -538,7 +539,7 @@ describe("The 'multi' method", function () { client.get('foo', helper.isString('bar', done)); }); - it("should not use a transaction with exec_atomic if only no command is used", function () { + it("should not use a transaction with exec_atomic if no command is used", function () { var multi = client.multi(); var test = false; multi.exec_batch = function () { diff --git a/test/node_redis.spec.js b/test/node_redis.spec.js index 12107993a99..ef5090c3a82 100644 --- a/test/node_redis.spec.js +++ b/test/node_redis.spec.js @@ -423,6 +423,41 @@ describe("The node_redis client", function () { }); }); + describe('execution order / fire query while loading', function () { + it('keep execution order for commands that may fire while redis is still loading', function (done) { + client = redis.createClient.apply(null, args); + var fired = false; + client.set('foo', 'bar', function (err, res) { + assert(fired === false); + done(); + }); + client.info(function (err, res) { + fired = true; + }); + }); + + it('should fire early', function (done) { + client = redis.createClient.apply(null, args); + var fired = false; + client.info(function (err, res) { + fired = true; + }); + client.set('foo', 'bar', function (err, res) { + assert(fired); + done(); + }); + assert.strictEqual(client.offline_queue.length, 1); + assert.strictEqual(client.command_queue.length, 1); + client.on('connect', function () { + assert.strictEqual(client.offline_queue.length, 1); + assert.strictEqual(client.command_queue.length, 1); + }); + client.on('ready', function () { + assert.strictEqual(client.offline_queue.length, 0); + }); + }); + }); + describe('socket_nodelay', function () { describe('true', function () { var args = config.configureClient(parser, ip, { diff --git a/test/rename.spec.js b/test/rename.spec.js index 5ac7435138e..96072105675 100644 --- a/test/rename.spec.js +++ b/test/rename.spec.js @@ -109,6 +109,25 @@ describe("rename commands", function () { }); }); + it("should also work prefixed commands", function (done) { + if (helper.redisProcess().spawnFailed()) this.skip(); + + client.end(true); + client = redis.createClient({ + rename_commands: { + set: '807081f5afa96845a02816a28b7258c3' + }, + parser: parser, + prefix: 'baz' + }); + client.set('foo', 'bar'); + client.keys('*', function(err, reply) { + assert.strictEqual(reply[0], 'bazfoo'); + assert.strictEqual(err, null); + done(); + }); + }); + }); }); diff --git a/test/utils.spec.js b/test/utils.spec.js new file mode 100644 index 00000000000..d75e035486a --- /dev/null +++ b/test/utils.spec.js @@ -0,0 +1,129 @@ +'use strict'; + +var assert = require('assert'); +var Queue = require('double-ended-queue'); +var utils = require('../lib/utils'); + +describe('utils.js', function () { + + describe('clone', function () { + it('ignore the object prototype and clone a nested array / object', function () { + var obj = { + a: [null, 'foo', ['bar'], { + "I'm special": true + }], + number: 5, + fn: function noop () {} + }; + var clone = utils.clone(obj); + assert.deepEqual(clone, obj); + assert.strictEqual(obj.fn, clone.fn); + assert(typeof clone.fn === 'function'); + }); + + it('replace faulty values with an empty object as return value', function () { + var a = utils.clone(); + var b = utils.clone(null); + assert.strictEqual(Object.keys(a).length, 0); + assert.strictEqual(Object.keys(b).length, 0); + }); + + it('throws on circular data', function () { + try { + var a = {}; + a.b = a; + utils.clone(a); + throw new Error('failed'); + } catch (e) { + assert(e.message !== 'failed'); + } + }); + }); + + describe('reply_in_order', function () { + + var err_count = 0; + var res_count = 0; + var emitted = false; + var clientMock = { + emit: function () { emitted = true; }, + offline_queue: new Queue(), + command_queue: new Queue() + }; + var create_command_obj = function () { + return { + callback: function (err, res) { + if (err) err_count++; + else res_count++; + } + }; + }; + + beforeEach(function () { + clientMock.offline_queue.clear(); + clientMock.command_queue.clear(); + err_count = 0; + res_count = 0; + emitted = false; + }); + + it('no elements in either queue. Reply in the next tick', function (done) { + var called = false; + utils.reply_in_order(clientMock, function () { + called = true; + done(); + }, null, null); + assert(!called); + }); + + it('no elements in either queue. Reply in the next tick', function (done) { + assert(!emitted); + utils.reply_in_order(clientMock, null, new Error('tada')); + assert(!emitted); + setTimeout(function () { + assert(emitted); + done(); + }, 1); + }); + + it('elements in the offline queue. Reply after the offline queue is empty and respect the command_obj callback', function (done) { + clientMock.offline_queue.push(create_command_obj(), create_command_obj()); + utils.reply_in_order(clientMock, function () { + assert.strictEqual(clientMock.offline_queue.length, 0); + assert.strictEqual(res_count, 2); + done(); + }, null, null); + while (clientMock.offline_queue.length) clientMock.offline_queue.shift().callback(null, 'foo'); + }); + + it('elements in the offline queue. Reply after the offline queue is empty and respect the command_obj error emit', function (done) { + clientMock.command_queue.push({}, create_command_obj(), {}); + utils.reply_in_order(clientMock, function () { + assert.strictEqual(clientMock.command_queue.length, 0); + assert(emitted); + assert.strictEqual(err_count, 1); + assert.strictEqual(res_count, 0); + done(); + }, null, null); + while (clientMock.command_queue.length) { + var command_obj = clientMock.command_queue.shift(); + if (command_obj.callback) { + command_obj.callback(new Error('tada')); + } + } + }); + + it('elements in the offline queue. Reply after the offline queue is empty and respect the command_obj', function (done) { + clientMock.command_queue.push(create_command_obj(), {}); + utils.reply_in_order(clientMock, function () { + assert.strictEqual(clientMock.command_queue.length, 0); + assert(!emitted); + assert.strictEqual(res_count, 1); + done(); + }, null, null); + while (clientMock.command_queue.length) { + clientMock.command_queue.shift().callback(null, 'bar'); + } + }); + }); +}); From 1a8a72ddf36d15d7850fc7a6d616e11510e16b77 Mon Sep 17 00:00:00 2001 From: Ruben Bridgewater Date: Tue, 1 Mar 2016 16:32:15 +0100 Subject: [PATCH 0558/1748] Improve tls tests Removed redundant tests. Skip the tests on windows instead of not even showing them. Add a faulty cert to check proper cert validation. Reject unauthorized certs --- test/conf/faulty.cert | 19 ++++ test/tls.spec.js | 251 +++++++++++++----------------------------- 2 files changed, 98 insertions(+), 172 deletions(-) create mode 100644 test/conf/faulty.cert diff --git a/test/conf/faulty.cert b/test/conf/faulty.cert new file mode 100644 index 00000000000..30c98790061 --- /dev/null +++ b/test/conf/faulty.cert @@ -0,0 +1,19 @@ +-----BEGIN CERTIFICATE----- +MIIDATCCAemgAwIBAgIJALkMmVkQOERnMA0GCSqGSIb3DQEBBQUAMBcxFTATBgNV +BAMMDHJlZGlzLmpzLm9yZzAeFw0xNTEwMTkxMjIzMjRaFw0yNTEwMTYxMjIzMjRa +MBcxFTATBgNVBAMMDHJlZGlzLmpzLm9yZzCCASIwDQYJKoZIhvcNAQEBBQADggEP +ADCCAQoCggEBAJ/DmMTJHf7kyspxI1A/JmOc+KI9vxEcN5qn7IiZuGN7ghE43Q3q +XB2GUkMAuW1POkmM5yi3SuT1UXDR/4Gk7KlbHKMs37AV6PgJXX6oX0zu12LTAT7V +5byNrYtehSo42l1188dGEMCGaaf0cDntc7A3aW0ZtzrJt+2pu31Uatl2SEJCMra6 ++v6O0c9aHMF1cArKeawGqR+jHw6vXFZQbUd06nW5nQlUA6wVt1JjlLPwBwYsWLsi +YQxMC8NqpgAIg5tULSCpKwx5isL/CeotVVGDNZ/G8R1nTrxuygPlc3Qskj57hmV4 +tZK4JJxQFi7/9ehvjAvHohKrEPeqV5XL87cCAwEAAaNQME4wHQYDVR0OBBYEFCn/ +5hB+XY4pVOnaqvrmZMxrLFjLMB8GA1UdIwQYMBaAFCn/5hB+XY4pVOnaqvrmZMxr +LFjLMAwGA1UdEwQFMAMBAf8wDQYJKoZIhvcNAQEFBQADggEBAEduPyTHpXkCVZRQ +v6p+Ug4iVeXpxGCVr34y7EDUMgmuDdqsz1SrmqeDd0VmjZT8htbWw7QBKDPEBsbi +wl606aAn01iM+oUrwbtXxid1xfZj/j6pIhQVkGu7e/8A7Pr4QOP4OMdHB7EmqkAo +d/OLHa9LdKv2UtJHD6U7oVQbdBHrRV62125GMmotpQuSkEfZM6edKNzHPlqV/zJc +2kGCw3lZC21mTrsSMIC/FQiobPnig4kAvfh0of2rK/XAntlwT8ie1v1aK+jERsfm +uzMihl6XXBdzheq6KdIlf+5STHBIIRcvBoRKr5Va7EhnO03tTzeJowtqDv47yPC6 +w4kLcP8= +-----END CERTIFICATE----- diff --git a/test/tls.spec.js b/test/tls.spec.js index 68d12d761c4..20cd246494e 100644 --- a/test/tls.spec.js +++ b/test/tls.spec.js @@ -6,25 +6,33 @@ var fs = require('fs'); var helper = require('./helper'); var path = require('path'); var redis = config.redis; +var utils = require('../lib/utils'); var tls_options = { servername: "redis.js.org", - rejectUnauthorized: false, + rejectUnauthorized: true, ca: [ String(fs.readFileSync(path.resolve(__dirname, "./conf/redis.js.org.cert"))) ] }; var tls_port = 6380; - -if (process.platform === 'win32') { - return; -} +// Use skip instead of returning to indicate what tests really got skipped +var skip = false; // Wait until stunnel4 is in the travis whitelist // Check: https://github.com/travis-ci/apt-package-whitelist/issues/403 // If this is merged, remove the travis env checks describe("TLS connection tests", function () { + before(function (done) { - if (process.env.TRAVIS === 'true') { + // Print the warning when the tests run instead of while starting mocha + if (process.platform === 'win32') { + skip = true; + console.warn('\nStunnel tests do not work on windows atm. If you think you can fix that, it would be warmly welcome.\n'); + } else if (process.env.TRAVIS === 'true') { + skip = true; + console.warn('\nTravis does not support stunnel right now. Skipping tests.\nCheck: https://github.com/travis-ci/apt-package-whitelist/issues/403\n'); + } + if (skip) { done(); return; } @@ -34,188 +42,87 @@ describe("TLS connection tests", function () { }); after(function (done) { - if (process.env.TRAVIS === 'true') { + if (skip) { done(); return; } helper.stopStunnel(done); }); - helper.allTests(function(parser, ip, args) { + var client; - describe("using " + parser + " and " + ip, function () { - - var client; + afterEach(function () { + client.end(true); + }); - afterEach(function () { - client.end(true); + describe("on lost connection", function () { + it("emit an error after max retry timeout and do not try to reconnect afterwards", function (done) { + if (skip) this.skip(); + var connect_timeout = 500; // in ms + client = redis.createClient({ + connect_timeout: connect_timeout, + port: tls_port, + tls: tls_options }); + var time = 0; - describe("on lost connection", function () { - it("emit an error after max retry attempts and do not try to reconnect afterwards", function (done) { - if (process.env.TRAVIS === 'true') this.skip(); - var max_attempts = 4; - var options = { - parser: parser, - max_attempts: max_attempts, - port: tls_port, - tls: tls_options - }; - client = redis.createClient(options); - var calls = 0; - - client.once('ready', function() { - helper.killConnection(client); - }); - - client.on("reconnecting", function (params) { - calls++; - }); - - client.on('error', function(err) { - if (/Redis connection in broken state: maximum connection attempts.*?exceeded./.test(err.message)) { - setTimeout(function () { - assert.strictEqual(calls, max_attempts - 1); - done(); - }, 500); - } - }); - }); - - it("emit an error after max retry timeout and do not try to reconnect afterwards", function (done) { - if (process.env.TRAVIS === 'true') this.skip(); - var connect_timeout = 500; // in ms - client = redis.createClient({ - parser: parser, - connect_timeout: connect_timeout, - port: tls_port, - tls: tls_options - }); - var time = 0; - - client.once('ready', function() { - helper.killConnection(client); - }); - - client.on("reconnecting", function (params) { - time += params.delay; - }); - - client.on('error', function(err) { - if (/Redis connection in broken state: connection timeout.*?exceeded./.test(err.message)) { - setTimeout(function () { - assert(time === connect_timeout); - done(); - }, 500); - } - }); - }); - - it("end connection while retry is still ongoing", function (done) { - if (process.env.TRAVIS === 'true') this.skip(); - var connect_timeout = 1000; // in ms - client = redis.createClient({ - parser: parser, - connect_timeout: connect_timeout, - port: tls_port, - tls: tls_options - }); - - client.once('ready', function() { - helper.killConnection(client); - }); - - client.on("reconnecting", function (params) { - client.end(true); - setTimeout(done, 100); - }); - }); - - it("can not connect with wrong host / port in the options object", function (done) { - if (process.env.TRAVIS === 'true') this.skip(); - var options = { - host: 'somewhere', - max_attempts: 1, - port: tls_port, - tls: tls_options - }; - client = redis.createClient(options); - var end = helper.callFuncAfter(done, 2); - - client.on('error', function (err) { - assert(/CONNECTION_BROKEN|ENOTFOUND|EAI_AGAIN/.test(err.code)); - end(); - }); - - }); + client.once('ready', function() { + helper.killConnection(client); }); - describe("when not connected", function () { - - it("connect with host and port provided in the options object", function (done) { - if (process.env.TRAVIS === 'true') this.skip(); - client = redis.createClient({ - host: 'localhost', - parser: parser, - connect_timeout: 1000, - port: tls_port, - tls: tls_options - }); + client.on("reconnecting", function (params) { + time += params.delay; + }); - client.once('ready', function() { + client.on('error', function(err) { + if (/Redis connection in broken state: connection timeout.*?exceeded./.test(err.message)) { + setTimeout(function () { + assert(time === connect_timeout); done(); - }); - }); - - it("connects correctly with args", function (done) { - if (process.env.TRAVIS === 'true') this.skip(); - var args_host = args[1]; - var args_options = args[2] || {}; - args_options.tls = tls_options; - client = redis.createClient(tls_port, args_host, args_options); - client.on("error", done); - - client.once("ready", function () { - client.removeListener("error", done); - client.get("recon 1", function (err, res) { - done(err); - }); - }); - }); - - if (ip === 'IPv4') { - it('allows connecting with the redis url and no auth and options as second parameter', function (done) { - if (process.env.TRAVIS === 'true') this.skip(); - var options = { - detect_buffers: false, - magic: Math.random(), - port: tls_port, - tls: tls_options - }; - client = redis.createClient('redis://' + config.HOST[ip] + ':' + tls_port, options); - // verify connection is using TCP, not UNIX socket - assert.strictEqual(client.connection_options.host, config.HOST[ip]); - assert.strictEqual(client.connection_options.port, tls_port); - assert(typeof client.stream.getCipher === 'function'); - // verify passed options are in use - assert.strictEqual(client.options.magic, options.magic); - client.on("ready", function () { - return done(); - }); - }); - - it('allows connecting with the redis url and no auth and options as third parameter', function (done) { - if (process.env.TRAVIS === 'true') this.skip(); - client = redis.createClient('redis://' + config.HOST[ip] + ':' + tls_port, null, { - detect_buffers: false, - tls: tls_options - }); - client.on("ready", function () { - return done(); - }); - }); + }, 100); } }); }); }); + + describe("when not connected", function () { + + it("connect with host and port provided in the options object", function (done) { + if (skip) this.skip(); + client = redis.createClient({ + host: 'localhost', + connect_timeout: 1000, + port: tls_port, + tls: tls_options + }); + + // verify connection is using TCP, not UNIX socket + assert.strictEqual(client.connection_options.host, 'localhost'); + assert.strictEqual(client.connection_options.port, tls_port); + assert(client.stream.encrypted); + + client.set('foo', 'bar'); + client.get('foo', helper.isString('bar', done)); + }); + + it('fails to connect because the cert is not correct', function (done) { + if (skip) this.skip(); + var faulty_cert = utils.clone(tls_options); + faulty_cert.ca = [ String(fs.readFileSync(path.resolve(__dirname, "./conf/faulty.cert"))) ]; + client = redis.createClient({ + host: 'localhost', + connect_timeout: 1000, + port: tls_port, + tls: faulty_cert + }); + client.on('error', function (err) { + assert.strictEqual(err.code, 'DEPTH_ZERO_SELF_SIGNED_CERT'); + client.end(true); + }); + client.set('foo', 'bar', function (err, res) { + done(res); + }); + }); + + }); }); From 0a636f95ccc6879dca153feeb24be4fe7f417e53 Mon Sep 17 00:00:00 2001 From: Ruben Bridgewater Date: Tue, 1 Mar 2016 16:36:48 +0100 Subject: [PATCH 0559/1748] Signal test failures due to used ports and accept individual ports to be provided --- test/lib/redis-process.js | 49 ++++++++++++++++++++++++++++----------- 1 file changed, 35 insertions(+), 14 deletions(-) diff --git a/test/lib/redis-process.js b/test/lib/redis-process.js index 465afdbf2ef..a6e978494e6 100644 --- a/test/lib/redis-process.js +++ b/test/lib/redis-process.js @@ -5,30 +5,51 @@ var config = require('./config'); var fs = require('fs'); var path = require('path'); var spawn = require('win-spawn'); -var spawnFailed = false; var tcpPortUsed = require('tcp-port-used'); +var bluebird = require('bluebird'); // wait for redis to be listening in // all three modes (ipv4, ipv6, socket). -function waitForRedis (available, cb) { +function waitForRedis (available, cb, port) { if (process.platform === 'win32') return cb(); - var ipV4 = false; + var time = Date.now(); + var running = false; + var socket = '/tmp/redis.sock'; + if (port) { + // We have to distinguishe the redis sockets if we have more than a single redis instance running + socket = '/tmp/redis' + port + '.sock'; + } + port = port || config.PORT; var id = setInterval(function () { - tcpPortUsed.check(config.PORT, '127.0.0.1').then(function (_ipV4) { - ipV4 = _ipV4; - return tcpPortUsed.check(config.PORT, '::1'); - }).then(function (ipV6) { - if (ipV6 === available && ipV4 === available && fs.existsSync('/tmp/redis.sock') === available) { - clearInterval(id); - return cb(); + if (running) return; + running = true; + bluebird.join( + tcpPortUsed.check(port, '127.0.0.1'), + tcpPortUsed.check(port, '::1'), + function (ipV4, ipV6) { + if (ipV6 === available && ipV4 === available) { + if (fs.existsSync(socket) === available) { + clearInterval(id); + return cb(); + } + // The same message applies for can't stop but we ignore that case + throw new Error('Port ' + port + ' is already in use. Tests can\'t start.\n'); } + if (Date.now() - time > 6000) { + throw new Error('Redis could not start on port ' + (port || config.PORT) + '\n'); + } + running = false; + }).catch(function (err) { + console.error('\x1b[31m' + err.stack + '\x1b[0m\n'); + process.exit(1); }); }, 100); } module.exports = { - start: function (done, conf) { + start: function (done, conf, port) { + var spawnFailed = false; // spawn redis with our testing configuration. var confFile = conf || path.resolve(__dirname, '../conf/redis.conf'); var rp = spawn("redis-server", [confFile], {}); @@ -53,15 +74,15 @@ module.exports = { rp.once("exit", function (code) { var error = null; if (code !== null && code !== 0) { - error = Error('Redis shutdown failed with code ' + code); + error = new Error('Redis shutdown failed with code ' + code); } waitForRedis(false, function () { return done(error); - }); + }, port); }); rp.kill("SIGTERM"); } }); - }); + }, port); } }; From 6013ee7f900769f70124999df28bfa3e782bd255 Mon Sep 17 00:00:00 2001 From: Ruben Bridgewater Date: Tue, 1 Mar 2016 16:37:32 +0100 Subject: [PATCH 0560/1748] callFuncAfter should fire the passed fn everytime called above the minimum Also pass individual ports to the redis process through --- test/helper.js | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/test/helper.js b/test/helper.js index 72ce1d546f9..74085772f3b 100644 --- a/test/helper.js +++ b/test/helper.js @@ -8,11 +8,11 @@ var StunnelProcess = require("./lib/stunnel-process"); var rp; var stunnel_process; -function startRedis (conf, done) { +function startRedis (conf, done, port) { RedisProcess.start(function (err, _rp) { rp = _rp; return done(err); - }, path.resolve(__dirname, conf)); + }, path.resolve(__dirname, conf), port); } function startStunnel(done) { @@ -176,7 +176,7 @@ module.exports = { throw err; } i++; - if (i === max) { + if (i >= max) { func(); return true; } From 711d51c387e729c49c376e7d603cbef54410c46f Mon Sep 17 00:00:00 2001 From: Ruben Bridgewater Date: Tue, 1 Mar 2016 16:39:52 +0100 Subject: [PATCH 0561/1748] Add unify_options / createClient tests --- test/connection.spec.js | 24 ---- test/unify_options.spec.js | 241 +++++++++++++++++++++++++++++++++++++ 2 files changed, 241 insertions(+), 24 deletions(-) create mode 100644 test/unify_options.spec.js diff --git a/test/connection.spec.js b/test/connection.spec.js index c047788847c..906ae02cd04 100644 --- a/test/connection.spec.js +++ b/test/connection.spec.js @@ -327,30 +327,6 @@ describe("connection tests", function () { assert(create_stream_string === String(redis.RedisClient.prototype.create_stream)); }); - it("throws on strange connection info", function () { - client = { - end: function() {} - }; - try { - redis.createClient(true); - throw new Error('failed'); - } catch (err) { - assert.equal(err.message, 'Unknown type of connection in createClient()'); - } - }); - - it("throws on protocol other than redis in the redis url", function () { - client = { - end: function() {} - }; - try { - redis.createClient(config.HOST[ip] + ':' + config.PORT); - throw new Error('failed'); - } catch (err) { - assert.equal(err.message, 'Connection string must use the "redis:" protocol or begin with slashes //'); - } - }); - if (ip === 'IPv4') { it('allows connecting with the redis url to the default host and port, select db 3 and warn about duplicate db option', function (done) { client = redis.createClient('redis:///3?db=3'); diff --git a/test/unify_options.spec.js b/test/unify_options.spec.js new file mode 100644 index 00000000000..975ce2b53d4 --- /dev/null +++ b/test/unify_options.spec.js @@ -0,0 +1,241 @@ +'use strict'; + +var assert = require('assert'); +var unifyOptions = require('../lib/createClient'); +var intercept = require('intercept-stdout'); + +describe('createClient options', function () { + + describe('port as first parameter', function () { + it('pass the options in the second parameter after a port', function () { + var options = unifyOptions(1234, { + option1: true, + option2: function () {} + }); + assert.strictEqual(Object.keys(options).length, 4); + assert(options.option1); + assert.strictEqual(options.port, 1234); + assert.strictEqual(options.host, undefined); + assert.strictEqual(typeof options.option2, 'function'); + }); + + it('pass the options in the third parameter after a port and host being set to null', function () { + var options = unifyOptions(1234, null, { + option1: true, + option2: function () {} + }); + assert.strictEqual(Object.keys(options).length, 4); + assert(options.option1); + assert.strictEqual(options.port, 1234); + assert.strictEqual(options.host, undefined); + assert.strictEqual(typeof options.option2, 'function'); + }); + + it('pass the options in the third parameter after a port and host being set to undefined', function () { + var options = unifyOptions(1234, undefined, { + option1: true, + option2: function () {} + }); + assert.strictEqual(Object.keys(options).length, 4); + assert(options.option1); + assert.strictEqual(options.port, 1234); + assert.strictEqual(options.host, undefined); + assert.strictEqual(typeof options.option2, 'function'); + }); + + it('pass the options in the third parameter after a port and host', function () { + var options = unifyOptions('1234', 'localhost', { + option1: true, + option2: function () {} + }); + assert.strictEqual(Object.keys(options).length, 4); + assert(options.option1); + assert.strictEqual(options.port, '1234'); + assert.strictEqual(options.host, 'localhost'); + assert.strictEqual(typeof options.option2, 'function'); + }); + + it('should throw with three parameters all set to a truthy value', function () { + try { + unifyOptions(1234, {}, {}); + throw new Error('failed'); + } catch (err) { + assert.strictEqual(err.message, 'Unknown type of connection in createClient()'); + } + }); + }); + + describe('unix socket as first parameter', function () { + it('pass the options in the second parameter after a port', function () { + var options = unifyOptions('/tmp/redis.sock', { + option1: true, + option2: function () {}, + option3: [1, 2, 3] + }); + assert.strictEqual(Object.keys(options).length, 4); + assert(options.option1); + assert.strictEqual(options.path, '/tmp/redis.sock'); + assert.strictEqual(typeof options.option2, 'function'); + assert.strictEqual(options.option3.length, 3); + }); + + it('pass the options in the third parameter after a port and host being set to null', function () { + var options = unifyOptions('/tmp/redis.sock', null, { + option1: true, + option2: function () {} + }); + assert.strictEqual(Object.keys(options).length, 3); + assert(options.option1); + assert.strictEqual(options.path, '/tmp/redis.sock'); + assert.strictEqual(typeof options.option2, 'function'); + }); + }); + + describe('redis url as first parameter', function () { + it('empty redis url including options as second parameter', function () { + var options = unifyOptions('redis://', { + option: [1, 2, 3] + }); + assert.strictEqual(Object.keys(options).length, 1); + assert.strictEqual(options.option.length, 3); + }); + + it('begin with two slashes including options as third parameter', function () { + var options = unifyOptions('//:abc@/3?port=123', { + option: [1, 2, 3] + }); + assert.strictEqual(Object.keys(options).length, 4); + assert.strictEqual(options.option.length, 3); + assert.strictEqual(options.port, '123'); + assert.strictEqual(options.db, '3'); + assert.strictEqual(options.password, 'abc'); + }); + + it('duplicated, identical query options including options obj', function () { + var text = ''; + var unhookIntercept = intercept(function(data) { + text += data; + return ''; + }); + var options = unifyOptions('//:abc@localhost:123/3?db=3&port=123&password=abc', null, { + option: [1, 2, 3] + }); + unhookIntercept(); + assert.strictEqual(text, + 'node_redis: WARNING: You passed the db option twice!\n' + + 'node_redis: WARNING: You passed the port option twice!\n' + + 'node_redis: WARNING: You passed the password option twice!\n' + ); + assert.strictEqual(Object.keys(options).length, 5); + assert.strictEqual(options.option.length, 3); + assert.strictEqual(options.host, 'localhost'); + assert.strictEqual(options.port, '123'); + assert.strictEqual(options.db, '3'); + assert.strictEqual(options.password, 'abc'); + }); + + it('should throw on duplicated, non-identical query options', function () { + try { + unifyOptions('//:abc@localhost:1234/3?port=123&password=abc'); + throw new Error('failed'); + } catch (err) { + assert.equal(err.message, 'The port option is added twice and does not match'); + } + }); + + it('should throw without protocol slashes', function () { + try { + unifyOptions('redis:abc@localhost:123/3?db=3&port=123&password=abc'); + throw new Error('failed'); + } catch (err) { + assert.equal(err.message, 'The redis url must begin with slashes "//" or contain slashes after the redis protocol'); + } + }); + + it("warns on protocol other than redis in the redis url", function () { + var text = ''; + var unhookIntercept = intercept(function (data) { + text += data; + return ''; + }); + var options = unifyOptions('http://abc'); + unhookIntercept(); + assert.strictEqual(Object.keys(options).length, 1); + assert.strictEqual(options.host, 'abc'); + assert.strictEqual(text, 'node_redis: WARNING: You passed "http" as protocol instead of the "redis" protocol!\n'); + }); + }); + + describe('no parameters or set to null / undefined', function () { + it('no parameters', function () { + var options = unifyOptions(); + assert.strictEqual(Object.keys(options).length, 1); + assert.strictEqual(options.host, undefined); + }); + + it('set to null', function () { + var options = unifyOptions(null, null); + assert.strictEqual(Object.keys(options).length, 1); + assert.strictEqual(options.host, null); + }); + + it('set to undefined', function () { + var options = unifyOptions(undefined, undefined); + assert.strictEqual(Object.keys(options).length, 1); + assert.strictEqual(options.host, undefined); + }); + }); + + describe('only an options object is passed', function () { + it('with options', function () { + var options = unifyOptions({ + option: true + }); + assert.strictEqual(Object.keys(options).length, 2); + assert.strictEqual(options.host, undefined); + assert.strictEqual(options.option, true); + }); + + it('without options', function () { + var options = unifyOptions({}); + assert.strictEqual(Object.keys(options).length, 1); + assert.strictEqual(options.host, undefined); + }); + + it('should throw with more parameters', function () { + try { + unifyOptions({ + option: true + }, undefined); + throw new Error('failed'); + } catch (err) { + assert.strictEqual(err.message, 'To many arguments passed to createClient. Please only pass the options object'); + } + }); + + it('including url as option', function () { + var options = unifyOptions({ + option: [1, 2, 3], + url: '//hm:abc@localhost:123/3' + }); + assert.strictEqual(Object.keys(options).length, 6); + assert.strictEqual(options.option.length, 3); + assert.strictEqual(options.host, 'localhost'); + assert.strictEqual(options.port, '123'); + assert.strictEqual(options.db, '3'); + assert.strictEqual(options.url, '//hm:abc@localhost:123/3'); + assert.strictEqual(options.password, 'abc'); + }); + }); + + describe('faulty data', function () { + it("throws on strange connection info", function () { + try { + unifyOptions(true); + throw new Error('failed'); + } catch (err) { + assert.equal(err.message, 'Unknown type of connection in createClient()'); + } + }); + }); +}); From 290bf1d65105ae800755c94fd689560eba3f5524 Mon Sep 17 00:00:00 2001 From: Ruben Bridgewater Date: Tue, 1 Mar 2016 16:42:15 +0100 Subject: [PATCH 0562/1748] Small utils improvements Don't write "Error:" infront of errors --- lib/utils.js | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/lib/utils.js b/lib/utils.js index 89d85907fe3..069bf306452 100644 --- a/lib/utils.js +++ b/lib/utils.js @@ -32,7 +32,8 @@ function replyToStrings(reply) { function print (err, reply) { if (err) { - console.log('Error: ' + err); + // A error always begins with Error: + console.log(err.toString()); } else { console.log('Reply: ' + reply); } From 5e8a87b4dcdb5161b938f567f6bcd37dd9869962 Mon Sep 17 00:00:00 2001 From: Ruben Bridgewater Date: Tue, 1 Mar 2016 16:43:46 +0100 Subject: [PATCH 0563/1748] Improve createClient function to detect faulty input and throw --- lib/createClient.js | 24 ++++++++++++++++++------ 1 file changed, 18 insertions(+), 6 deletions(-) diff --git a/lib/createClient.js b/lib/createClient.js index 3b14ef06f39..f97823e01d2 100644 --- a/lib/createClient.js +++ b/lib/createClient.js @@ -11,6 +11,9 @@ module.exports = function createClient (port_arg, host_arg, options) { if (typeof host_arg === 'string') { host = host_arg; } else { + if (options && host_arg) { + throw new Error('Unknown type of connection in createClient()'); + } options = options || host_arg; } options = utils.clone(options); @@ -23,19 +26,22 @@ module.exports = function createClient (port_arg, host_arg, options) { var parsed = URL.parse(port_arg.url || port_arg, true, true); // [redis:]//[[user][:password]@][host][:port][/db-number][?db=db-number[&password=bar[&option=value]]] - if (parsed.hostname || parsed.slashes) { // The host might be an empty string + if (parsed.slashes) { // We require slashes if (parsed.auth) { options.password = parsed.auth.split(':')[1]; } - if (!/^([a-z]+:)?\/\//i.test(parsed.href)) { - throw new Error('Connection string must use the "redis:" protocol or begin with slashes //'); + if (parsed.protocol && parsed.protocol !== 'redis:') { + console.warn('node_redis: WARNING: You passed "' + parsed.protocol.substring(0, parsed.protocol.length - 1) + '" as protocol instead of the "redis" protocol!'); } if (parsed.pathname && parsed.pathname !== '/') { options.db = parsed.pathname.substr(1); } - options.host = parsed.hostname; - options.port = parsed.port; - + if (parsed.hostname) { + options.host = parsed.hostname; + } + if (parsed.port) { + options.port = parsed.port; + } if (parsed.search !== '') { var elem; for (elem in parsed.query) { // jshint ignore: line @@ -50,6 +56,8 @@ module.exports = function createClient (port_arg, host_arg, options) { options[elem] = parsed.query[elem]; } } + } else if (parsed.hostname) { + throw new Error('The redis url must begin with slashes "//" or contain slashes after the redis protocol'); } else { options.path = port_arg; } @@ -57,6 +65,10 @@ module.exports = function createClient (port_arg, host_arg, options) { } else if (typeof port_arg === 'object' || port_arg === undefined) { options = utils.clone(port_arg || options); options.host = options.host || host_arg; + + if (port_arg && arguments.length !== 1) { + throw new Error('To many arguments passed to createClient. Please only pass the options object'); + } } if (!options) { From cc540dbc3c90e24098be71cf180567b8001f4ce2 Mon Sep 17 00:00:00 2001 From: Ruben Bridgewater Date: Tue, 1 Mar 2016 16:55:12 +0100 Subject: [PATCH 0564/1748] Implement retry_strategy and add more info to the reconnect event --- index.js | 54 ++++++++++++++++++++++++++++--------- test/connection.spec.js | 59 +++++++++++++++++++++++++++++++++++++++++ 2 files changed, 100 insertions(+), 13 deletions(-) diff --git a/index.js b/index.js index 7b3db9198b5..57d0c650888 100644 --- a/index.js +++ b/index.js @@ -103,6 +103,7 @@ function RedisClient (options) { this.old_state = null; this.send_anyway = false; this.pipeline = 0; + this.times_connected = 0; this.options = options; // Init parser this.reply_parser = new Parser({ @@ -145,14 +146,15 @@ RedisClient.prototype.create_stream = function () { if (this.options.connect_timeout) { this.stream.setTimeout(this.connect_timeout, function () { self.retry_totaltime = self.connect_timeout; - self.connection_gone('timeout'); + self.connection_gone('timeout', new Error('Redis connection gone from timeout event')); }); } /* istanbul ignore next: travis does not work with stunnel atm. Therefor the tls tests are skipped on travis */ - var connect_event = this.options.tls ? "secureConnect" : "connect"; + var connect_event = this.options.tls ? 'secureConnect' : 'connect'; this.stream.once(connect_event, function () { - this.removeAllListeners("timeout"); + this.removeAllListeners('timeout'); + self.times_connected++; self.on_connect(); }); @@ -166,17 +168,18 @@ RedisClient.prototype.create_stream = function () { self.on_error(err); }); - /* istanbul ignore next: travis does not work with stunnel atm. Therefor the tls tests are skipped on travis */ + /* istanbul ignore next: difficult to test and not important as long as we keep this listener */ this.stream.on('clientError', function (err) { + debug('clientError occured'); self.on_error(err); }); this.stream.once('close', function () { - self.connection_gone('close'); + self.connection_gone('close', new Error('Stream connection closed')); }); this.stream.once('end', function () { - self.connection_gone('end'); + self.connection_gone('end', new Error('Stream connection ended')); }); this.stream.on('drain', function () { @@ -268,10 +271,14 @@ RedisClient.prototype.on_error = function (err) { this.connected = false; this.ready = false; - this.emit('error', err); + + // Only emit the error if the retry_stategy option is not set + if (!this.options.retry_strategy) { + this.emit('error', err); + } // 'error' events get turned into exceptions if they aren't listened for. If the user handled this error // then we should try to reconnect. - this.connection_gone('error'); + this.connection_gone('error', err); }; RedisClient.prototype.on_connect = function () { @@ -417,12 +424,15 @@ RedisClient.prototype.send_offline_queue = function () { this.offline_queue = new Queue(); }; -var retry_connection = function (self) { +var retry_connection = function (self, error) { debug('Retrying connection...'); self.emit('reconnecting', { delay: self.retry_delay, - attempt: self.attempts + attempt: self.attempts, + error: error, + times_connected: self.times_connected, + total_retry_time: self.retry_totaltime }); self.retry_totaltime += self.retry_delay; @@ -432,8 +442,7 @@ var retry_connection = function (self) { self.retry_timer = null; }; -RedisClient.prototype.connection_gone = function (why) { - var error; +RedisClient.prototype.connection_gone = function (why, error) { // If a retry is already in progress, just let that happen if (this.retry_timer) { return; @@ -469,6 +478,25 @@ RedisClient.prototype.connection_gone = function (why) { return; } + if (typeof this.options.retry_strategy === 'function') { + this.retry_delay = this.options.retry_strategy({ + attempt: this.attempts, + error: error, + total_retry_time: this.retry_totaltime, + times_connected: this.times_connected + }); + if (typeof this.retry_delay !== 'number') { + // Pass individual error through + if (this.retry_delay instanceof Error) { + error = this.retry_delay; + } + this.flush_and_error(error); + this.emit('error', error); + this.end(false); + return; + } + } + if (this.max_attempts !== 0 && this.attempts >= this.max_attempts || this.retry_totaltime >= this.connect_timeout) { var message = this.retry_totaltime >= this.connect_timeout ? 'connection timeout exceeded.' : @@ -502,7 +530,7 @@ RedisClient.prototype.connection_gone = function (why) { debug('Retry connection in ' + this.retry_delay + ' ms'); - this.retry_timer = setTimeout(retry_connection, this.retry_delay, this); + this.retry_timer = setTimeout(retry_connection, this.retry_delay, this, error); }; RedisClient.prototype.return_error = function (err) { diff --git a/test/connection.spec.js b/test/connection.spec.js index 906ae02cd04..507089c2fdc 100644 --- a/test/connection.spec.js +++ b/test/connection.spec.js @@ -127,6 +127,65 @@ describe("connection tests", function () { client.stream.destroy(); }); }); + + it("retry_strategy used to reconnect with individual error", function (done) { + var text = ''; + var unhookIntercept = intercept(function (data) { + text += data; + return ''; + }); + var end = helper.callFuncAfter(done, 2); + client = redis.createClient({ + retry_strategy: function (options) { + if (options.total_retry_time > 150) { + client.set('foo', 'bar', function (err, res) { + assert.strictEqual(err.message, 'Connection timeout'); + end(); + }); + // Pass a individual error message to the error handler + return new Error('Connection timeout'); + } + return Math.min(options.attempt * 25, 200); + }, + max_attempts: 5, + retry_max_delay: 123, + port: 9999 + }); + + client.on('error', function(err) { + unhookIntercept(); + assert.strictEqual( + text, + 'node_redis: WARNING: You activated the retry_strategy and max_attempts at the same time. This is not possible and max_attempts will be ignored.\n' + + 'node_redis: WARNING: You activated the retry_strategy and retry_max_delay at the same time. This is not possible and retry_max_delay will be ignored.\n' + ); + assert.strictEqual(err.message, 'Connection timeout'); + assert(!err.code); + end(); + }); + }); + + it("retry_strategy used to reconnect", function (done) { + var end = helper.callFuncAfter(done, 2); + client = redis.createClient({ + retry_strategy: function (options) { + if (options.total_retry_time > 150) { + client.set('foo', 'bar', function (err, res) { + assert.strictEqual(err.code, 'ECONNREFUSED'); + end(); + }); + return false; + } + return Math.min(options.attempt * 25, 200); + }, + port: 9999 + }); + + client.on('error', function(err) { + assert.strictEqual(err.code, 'ECONNREFUSED'); + end(); + }); + }); }); describe("when not connected", function () { From 89209b8adc48cd15cc6784605c22a01b1edb3002 Mon Sep 17 00:00:00 2001 From: Ruben Bridgewater Date: Tue, 1 Mar 2016 16:57:32 +0100 Subject: [PATCH 0565/1748] Handle very big pipelines without crashing --- index.js | 11 ++++++++--- test/multi.spec.js | 22 ++++++++++++++++++++++ 2 files changed, 30 insertions(+), 3 deletions(-) diff --git a/index.js b/index.js index 57d0c650888..ba2a78e02c2 100644 --- a/index.js +++ b/index.js @@ -106,7 +106,7 @@ function RedisClient (options) { this.times_connected = 0; this.options = options; // Init parser - this.reply_parser = new Parser({ + this.reply_parser = Parser({ returnReply: function (data) { self.return_reply(data); }, @@ -786,8 +786,13 @@ RedisClient.prototype.send_command = function (command, args, callback) { }; RedisClient.prototype.writeDefault = RedisClient.prototype.writeStrings = function (data) { - var command, str = ''; - while (command = this.pipeline_queue.shift()) { + var str = ''; + for (var command = this.pipeline_queue.shift(); command; command = this.pipeline_queue.shift()) { + // Write to stream if the string is bigger than 4mb. The biggest string may be Math.pow(2, 28) - 15 chars long + if (str.length + command.length > 4 * 1024 * 1024) { + this.stream.write(str); + str = ''; + } str += command; } this.should_buffer = !this.stream.write(str + data); diff --git a/test/multi.spec.js b/test/multi.spec.js index 766d5798751..181cdf87d58 100644 --- a/test/multi.spec.js +++ b/test/multi.spec.js @@ -70,6 +70,28 @@ describe("The 'multi' method", function () { }); }); + describe('pipeline limit', function () { + + it('do not exceed maximum string size', function (done) { + this.timeout(12000); // Windows tests on 0.10 are slow + // Triggers a RangeError: Invalid string length if not handled properly + client = redis.createClient(); + var multi = client.multi(); + var i = Math.pow(2, 28); + while (i > 0) { + i -= 10230; + multi.set('foo' + i, 'bar' + new Array(1024).join('1234567890')); + } + client.on('ready', function () { + multi.exec(function (err, res) { + assert.strictEqual(res.length, 26241); + }); + client.flushdb(done); + }); + }); + + }); + helper.allTests(function(parser, ip, args) { describe("using " + parser + " and " + ip, function () { From 19ea518b36ae55cef8fc9630e74df8974566e886 Mon Sep 17 00:00:00 2001 From: Ruben Bridgewater Date: Tue, 1 Mar 2016 16:58:42 +0100 Subject: [PATCH 0566/1748] Do not emit ready if the slave is still syncing with master / master being down --- index.js | 12 +++++++++--- 1 file changed, 9 insertions(+), 3 deletions(-) diff --git a/index.js b/index.js index ba2a78e02c2..d5c0c0169d8 100644 --- a/index.js +++ b/index.js @@ -389,9 +389,15 @@ RedisClient.prototype.on_info_cmd = function (err, res) { } if (!this.server_info.loading || this.server_info.loading === '0') { - debug('Redis server ready.'); - this.on_ready(); - return; + // If the master_link_status exists but the link is not up, try again after 50 ms + if (this.server_info.master_link_status && this.server_info.master_link_status !== 'up') { + this.server_info.loading_eta_seconds = 0.05; + } else { + // Eta loading should change + debug('Redis server ready.'); + this.on_ready(); + return; + } } var retry_time = +this.server_info.loading_eta_seconds * 1000; From 575ad7357b5f51bc02209e7fecdedb26f7883024 Mon Sep 17 00:00:00 2001 From: Ruben Bridgewater Date: Tue, 1 Mar 2016 17:11:02 +0100 Subject: [PATCH 0567/1748] Insert deprecation warnings and some minor refactoring --- README.md | 19 ++++---- index.js | 91 ++++++++++++++++++++++++++---------- test/commands/blpop.spec.js | 8 ++++ test/commands/get.spec.js | 1 - test/commands/getset.spec.js | 1 - test/connection.spec.js | 2 + test/lib/stunnel-process.js | 7 ++- test/node_redis.spec.js | 32 +++++++++---- test/utils.spec.js | 25 ++++++++++ 9 files changed, 140 insertions(+), 46 deletions(-) diff --git a/README.md b/README.md index 1de766840dd..c48054b1a32 100644 --- a/README.md +++ b/README.md @@ -147,7 +147,7 @@ So please attach the error listener to node_redis. `client` will emit `end` when an established Redis server connection has closed. -### "drain" +### "drain" (deprecated) `client` will emit `drain` when the TCP connection to the Redis server has been buffering, but is now writable. This event can be used to stream commands in to Redis and adapt to backpressure. @@ -162,7 +162,7 @@ If false is returned the stream had to buffer. `client` will emit `warning` when password was set but none is needed and if a deprecated option / function / similar is used. -### "idle" +### "idle" (deprecated) `client` will emit `idle` when there are no outstanding commands that are awaiting a response. @@ -170,12 +170,13 @@ If false is returned the stream had to buffer. If you have `redis-server` running on the same computer as node, then the defaults for port and host are probably fine and you don't need to supply any arguments. `createClient()` returns a `RedisClient` object. +If the redis server runs on the same machine as the client consider using unix sockets if possible to increase throughput. + ### overloading -* `redis.createClient()` -* `redis.createClient(options)` -* `redis.createClient(unix_socket, options)` -* `redis.createClient(redis_url, options)` -* `redis.createClient(port, host, options)` +* `redis.createClient([options])` +* `redis.createClient(unix_socket[, options])` +* `redis.createClient(redis_url[, options])` +* `redis.createClient(port[, host][, options])` #### `options` is an object with the following possible properties: * `host`: *127.0.0.1*; The host to connect to @@ -204,10 +205,10 @@ This delay normally grows infinitely, but setting `retry_max_delay` limits it to * `connect_timeout`: *3600000*; Setting `connect_timeout` limits total time for client to connect and reconnect. The value is provided in milliseconds and is counted from the moment on a new client is created / a connection is lost. The last retry is going to happen exactly at the timeout time. Default is to try connecting until the default system socket timeout has been exceeded and to try reconnecting until 1h passed. -* `max_attempts`: *0*; By default client will try reconnecting until connected. Setting `max_attempts` +* `max_attempts`: *0*; (Deprecated, please use `retry_strategy` instead) By default client will try reconnecting until connected. Setting `max_attempts` limits total amount of connection tries. Setting this to 1 will prevent any reconnect tries. * `retry_unfulfilled_commands`: *false*; If set to true, all commands that were unfulfulled while the connection is lost will be retried after the connection has reestablished again. Use this with caution, if you use state altering commands (e.g. *incr*). This is especially useful if you use blocking commands. -* `password`: *null*; If set, client will run redis auth command on connect. Alias `auth_pass` +* `password`: *null*; If set, client will run redis auth command on connect. Alias `auth_pass` (node_redis < 2.5 have to use auth_pass) * `db`: *null*; If set, client will run redis select command on connect. This is [not recommended](https://groups.google.com/forum/#!topic/redis-db/vS5wX8X4Cjg). * `family`: *IPv4*; You can force using IPv6 if you set the family to 'IPv6'. See Node.js [net](https://nodejs.org/api/net.html) or [dns](https://nodejs.org/api/dns.html) modules how to use the family type. * `disable_resubscribing`: *false*; If set to `true`, a client won't resubscribe after disconnecting diff --git a/index.js b/index.js index d5c0c0169d8..8f755cc9a19 100644 --- a/index.js +++ b/index.js @@ -51,20 +51,32 @@ function RedisClient (options) { for (var tls_option in options.tls) { // jshint ignore: line cnx_options[tls_option] = options.tls[tls_option]; } + // Warn on misusing deprecated functions + if (typeof options.retry_strategy === 'function') { + if ('max_attempts' in options) { + self.warn('WARNING: You activated the retry_strategy and max_attempts at the same time. This is not possible and max_attempts will be ignored.'); + // Do not print deprecation warnings twice + delete options.max_attempts; + } + if ('retry_max_delay' in options) { + self.warn('WARNING: You activated the retry_strategy and retry_max_delay at the same time. This is not possible and retry_max_delay will be ignored.'); + // Do not print deprecation warnings twice + delete options.retry_max_delay; + } + } + this.connection_options = cnx_options; - this.connection_id = ++connection_id; + this.connection_id = RedisClient.connection_id++; this.connected = false; this.ready = false; if (options.socket_nodelay === undefined) { options.socket_nodelay = true; } else if (!options.socket_nodelay) { // Only warn users with this set to false - process.nextTick(function () { - self.warn( - 'socket_nodelay is deprecated and will be removed in v.3.0.0.\n' + - 'Setting socket_nodelay to false likely results in a reduced throughput. Please use .batch to buffer commands and use pipelining.\n' + - 'If you are sure you rely on the NAGLE-algorithm you can activate it by calling client.stream.setNoDelay(false) instead.' - ); - }); + self.warn( + 'socket_nodelay is deprecated and will be removed in v.3.0.0.\n' + + 'Setting socket_nodelay to false likely results in a reduced throughput. Please use .batch for pipelining instead.\n' + + 'If you are sure you rely on the NAGLE-algorithm you can activate it by calling client.stream.setNoDelay(false) instead.' + ); } if (options.socket_keepalive === undefined) { options.socket_keepalive = true; @@ -76,9 +88,7 @@ function RedisClient (options) { options.detect_buffers = !!options.detect_buffers; // Override the detect_buffers setting if return_buffers is active and print a warning if (options.return_buffers && options.detect_buffers) { - process.nextTick(function () { - self.warn('WARNING: You activated return_buffers and detect_buffers at the same time. The return value is always going to be a buffer.'); - }); + self.warn('WARNING: You activated return_buffers and detect_buffers at the same time. The return value is always going to be a buffer.'); options.detect_buffers = false; } if (options.detect_buffers) { @@ -87,11 +97,27 @@ function RedisClient (options) { } this.should_buffer = false; this.max_attempts = options.max_attempts | 0; + if ('max_attempts' in options) { + self.warn( + 'max_attempts is deprecated and will be removed in v.3.0.0.\n' + + 'To reduce the amount of options and the improve the reconnection handling please use the new `retry_strategy` option instead.\n' + + 'This replaces the max_attempts and retry_max_delay option.' + ); + } this.command_queue = new Queue(); // Holds sent commands to de-pipeline them this.offline_queue = new Queue(); // Holds commands issued but not able to be sent + // ATTENTION: connect_timeout should change in v.3.0 so it does not count towards ending reconnection attempts after x seconds + // This should be done by the retry_strategy. Instead it should only be the timeout for connecting to redis this.connect_timeout = +options.connect_timeout || 3600000; // 60 * 60 * 1000 ms this.enable_offline_queue = options.enable_offline_queue === false ? false : true; this.retry_max_delay = +options.retry_max_delay || null; + if ('retry_max_delay' in options) { + self.warn( + 'retry_max_delay is deprecated and will be removed in v.3.0.0.\n' + + 'To reduce the amount of options and the improve the reconnection handling please use the new `retry_strategy` option instead.\n' + + 'This replaces the max_attempts and retry_max_delay option.' + ); + } this.initialize_retry_vars(); this.pub_sub_mode = false; this.subscription_set = {}; @@ -123,8 +149,24 @@ function RedisClient (options) { name: options.parser }); this.create_stream(); + // The listeners will not be attached right away, so let's print the deprecation message while the listener is attached + this.on('newListener', function (event) { + if (event === 'idle') { + this.warn( + 'The idle event listener is deprecated and will likely be removed in v.3.0.0.\n' + + 'If you rely on this feature please open a new ticket in node_redis with your use case' + ); + } else if (event === 'drain') { + this.warn( + 'The drain event listener is deprecated and will be removed in v.3.0.0.\n' + + 'If you want to keep on listening to this event please listen to the stream drain event directly.' + ); + } + }); } -util.inherits(RedisClient, events.EventEmitter); +util.inherits(RedisClient, EventEmitter); + +RedisClient.connection_id = 0; // Attention: the function name "create_stream" should not be changed, as other libraries need this to mock the stream (e.g. fakeredis) RedisClient.prototype.create_stream = function () { @@ -238,19 +280,23 @@ RedisClient.prototype.unref = function () { }; RedisClient.prototype.warn = function (msg) { - if (this.listeners('warning').length !== 0) { - this.emit('warning', msg); - } else { - console.warn('node_redis:', msg); - } + var self = this; + // Warn on the next tick. Otherwise no event listener can be added + // for warnings that are emitted in the redis client constructor + process.nextTick(function () { + if (self.listeners('warning').length !== 0) { + self.emit('warning', msg); + } else { + console.warn('node_redis:', msg); + } + }); }; // Flush provided queues, erroring any items with a callback first RedisClient.prototype.flush_and_error = function (error, queue_names) { - var command_obj; queue_names = queue_names || ['offline_queue', 'command_queue']; for (var i = 0; i < queue_names.length; i++) { - while (command_obj = this[queue_names[i]].shift()) { + for (var command_obj = this[queue_names[i]].shift(); command_obj; command_obj = this[queue_names[i]].shift()) { if (typeof command_obj.callback === 'function') { error.command = command_obj.command.toUpperCase(); command_obj.callback(error); @@ -419,9 +465,7 @@ RedisClient.prototype.ready_check = function () { }; RedisClient.prototype.send_offline_queue = function () { - var command_obj; - - while (command_obj = this.offline_queue.shift()) { + for (var command_obj = this.offline_queue.shift(); command_obj; command_obj = this.offline_queue.shift()) { debug('Sending offline command: ' + command_obj.command); this.send_command(command_obj.command, command_obj.args, command_obj.callback); } @@ -805,8 +849,7 @@ RedisClient.prototype.writeDefault = RedisClient.prototype.writeStrings = functi }; RedisClient.prototype.writeBuffers = function (data) { - var command; - while (command = this.pipeline_queue.shift()) { + for (var command = this.pipeline_queue.shift(); command; command = this.pipeline_queue.shift()) { this.stream.write(command); } this.should_buffer = !this.stream.write(data); diff --git a/test/commands/blpop.spec.js b/test/commands/blpop.spec.js index fc022a91855..4105dcd9c5b 100644 --- a/test/commands/blpop.spec.js +++ b/test/commands/blpop.spec.js @@ -4,6 +4,7 @@ var assert = require("assert"); var config = require("../lib/config"); var helper = require("../helper"); var redis = config.redis; +var intercept = require('intercept-stdout'); describe("The 'blpop' method", function () { @@ -23,7 +24,14 @@ describe("The 'blpop' method", function () { it('pops value immediately if list contains values', function (done) { bclient = redis.createClient.apply(redis.createClient, args); redis.debug_mode = true; + var text = ''; + var unhookIntercept = intercept(function(data) { + text += data; + return ''; + }); client.rpush("blocking list", "initial value", helper.isNumber(1)); + unhookIntercept(); + assert(/^Send 127\.0\.0\.1:6379 id [0-9]+: \*3\r\n\$5\r\nrpush\r\n\$13\r\nblocking list\r\n\$13\r\ninitial value\r\n\n$/.test(text)); redis.debug_mode = false; bclient.blpop("blocking list", 0, function (err, value) { assert.strictEqual(value[0], "blocking list"); diff --git a/test/commands/get.spec.js b/test/commands/get.spec.js index 522872e1dfa..7f1f0cdf4c6 100644 --- a/test/commands/get.spec.js +++ b/test/commands/get.spec.js @@ -68,7 +68,6 @@ describe("The 'get' method", function () { }); it("gets the value correctly", function (done) { - client.GET(key, redis.print); // Use the utility function to print the result client.GET(key, function (err, res) { helper.isString(value)(err, res); done(err); diff --git a/test/commands/getset.spec.js b/test/commands/getset.spec.js index 11465bf4951..b39b028413d 100644 --- a/test/commands/getset.spec.js +++ b/test/commands/getset.spec.js @@ -33,7 +33,6 @@ describe("The 'getset' method", function () { }); it("reports an error", function (done) { - client.GET(key, redis.print); // Use the utility function to print the error client.get(key, function (err, res) { assert(err.message.match(/The connection has already been closed/)); done(); diff --git a/test/connection.spec.js b/test/connection.spec.js index 507089c2fdc..262face2ec2 100644 --- a/test/connection.spec.js +++ b/test/connection.spec.js @@ -4,6 +4,7 @@ var assert = require("assert"); var config = require("./lib/config"); var helper = require('./helper'); var redis = config.redis; +var intercept = require('intercept-stdout'); describe("connection tests", function () { helper.allTests(function(parser, ip, args) { @@ -91,6 +92,7 @@ describe("connection tests", function () { client.on("reconnecting", function (params) { client.end(true); + assert.strictEqual(params.times_connected, 1); setTimeout(done, 100); }); }); diff --git a/test/lib/stunnel-process.js b/test/lib/stunnel-process.js index f20bc22448c..f75049da179 100644 --- a/test/lib/stunnel-process.js +++ b/test/lib/stunnel-process.js @@ -2,11 +2,16 @@ // helper to start and stop the stunnel process. var spawn = require('child_process').spawn; -var EventEmitter = require('events').EventEmitter; +var EventEmitter = require('events'); var fs = require('fs'); var path = require('path'); var util = require('util'); +// Newer Node.js versions > 0.10 return the EventEmitter right away and using .EventEmitter was deprecated +if (typeof EventEmitter !== 'function') { + EventEmitter = EventEmitter.EventEmitter; +} + function once(cb) { var called = false; return function() { diff --git a/test/node_redis.spec.js b/test/node_redis.spec.js index ef5090c3a82..527633e6ba5 100644 --- a/test/node_redis.spec.js +++ b/test/node_redis.spec.js @@ -384,9 +384,18 @@ describe("The node_redis client", function () { describe('idle', function () { it('emits idle as soon as there are no outstanding commands', function (done) { + var end = helper.callFuncAfter(done, 2); + client.on('warning', function (msg) { + assert.strictEqual( + msg, + 'The idle event listener is deprecated and will likely be removed in v.3.0.0.\n' + + 'If you rely on this feature please open a new ticket in node_redis with your use case' + ); + end(); + }); client.on('idle', function onIdle () { client.removeListener("idle", onIdle); - client.get('foo', helper.isString('bar', done)); + client.get('foo', helper.isString('bar', end)); }); client.set('foo', 'bar'); }); @@ -614,9 +623,17 @@ describe("The node_redis client", function () { parser: parser, no_ready_check: true }); - var end = helper.callFuncAfter(done, 2); + var end = helper.callFuncAfter(done, 3); client.set('foo', 'bar'); client.get('foo', end); + client.on('warning', function (msg) { + assert.strictEqual( + msg, + 'The drain event listener is deprecated and will be removed in v.3.0.0.\n' + + 'If you want to keep on listening to this event please listen to the stream drain event directly.' + ); + end(); + }); client.on('drain', function() { assert(client.offline_queue.length === 0); end(); @@ -673,6 +690,9 @@ describe("The node_redis client", function () { client.on('reconnecting', function(params) { i++; assert.equal(params.attempt, i); + assert.strictEqual(params.times_connected, 0); + assert(params.error instanceof Error); + assert(typeof params.total_retry_time === 'number'); assert.strictEqual(client.offline_queue.length, 2); }); @@ -706,10 +726,6 @@ describe("The node_redis client", function () { helper.killConnection(client); }); - client.on("reconnecting", function (params) { - assert.equal(client.command_queue.length, 15); - }); - client.on('error', function(err) { if (/uncertain state/.test(err.message)) { assert.equal(client.command_queue.length, 0); @@ -787,10 +803,6 @@ describe("The node_redis client", function () { helper.killConnection(client); }); - client.on("reconnecting", function (params) { - assert.equal(client.command_queue.length, 15); - }); - client.on('error', function(err) { if (err.code === 'UNCERTAIN_STATE') { assert.equal(client.command_queue.length, 0); diff --git a/test/utils.spec.js b/test/utils.spec.js index d75e035486a..c886648d398 100644 --- a/test/utils.spec.js +++ b/test/utils.spec.js @@ -3,6 +3,7 @@ var assert = require('assert'); var Queue = require('double-ended-queue'); var utils = require('../lib/utils'); +var intercept = require('intercept-stdout'); describe('utils.js', function () { @@ -40,6 +41,30 @@ describe('utils.js', function () { }); }); + describe('print helper', function () { + it('callback with reply', function () { + var text = ''; + var unhookIntercept = intercept(function(data) { + text += data; + return ''; + }); + utils.print(null, 'abc'); + unhookIntercept(); + assert.strictEqual(text, 'Reply: abc\n'); + }); + + it('callback with error', function () { + var text = ''; + var unhookIntercept = intercept(function(data) { + text += data; + return ''; + }); + utils.print(new Error('Wonderful exception')); + unhookIntercept(); + assert.strictEqual(text, 'Error: Wonderful exception\n'); + }); + }); + describe('reply_in_order', function () { var err_count = 0; From 535db5231edb64570eafe1629a24a708b2e6af63 Mon Sep 17 00:00:00 2001 From: Ruben Bridgewater Date: Tue, 1 Mar 2016 17:13:31 +0100 Subject: [PATCH 0568/1748] Fix rename command not working together with key prefixes --- index.js | 10 ++++------ test/rename.spec.js | 2 +- 2 files changed, 5 insertions(+), 7 deletions(-) diff --git a/index.js b/index.js index 8f755cc9a19..4263786382e 100644 --- a/index.js +++ b/index.js @@ -794,17 +794,15 @@ RedisClient.prototype.send_command = function (command, args, callback) { } this.command_queue.push(command_obj); - if (typeof this.options.rename_commands !== 'undefined' && this.options.rename_commands[command]) { - command = this.options.rename_commands[command]; - } if (this.options.prefix) { prefix_keys = commands.getKeyIndexes(command, args_copy); - i = prefix_keys.pop(); - while (i !== undefined) { + for (i = prefix_keys.pop(); i !== undefined; i = prefix_keys.pop()) { args_copy[i] = this.options.prefix + args_copy[i]; - i = prefix_keys.pop(); } } + if (typeof this.options.rename_commands !== 'undefined' && this.options.rename_commands[command]) { + command = this.options.rename_commands[command]; + } // Always use 'Multi bulk commands', but if passed any Buffer args, then do multiple writes, one for each arg. // This means that using Buffers in commands is going to be slower, so use Strings if you don't already have a Buffer. command_str = '*' + (len + 1) + '\r\n$' + command.length + '\r\n' + command + '\r\n'; diff --git a/test/rename.spec.js b/test/rename.spec.js index 96072105675..e829323c82d 100644 --- a/test/rename.spec.js +++ b/test/rename.spec.js @@ -27,7 +27,7 @@ describe("rename commands", function () { }); client.on('ready', function () { - done(); + client.flushdb(done); }); }); From c2e25a7f7157431597651ebf6866bdbe3c061134 Mon Sep 17 00:00:00 2001 From: Ruben Bridgewater Date: Tue, 1 Mar 2016 17:14:57 +0100 Subject: [PATCH 0569/1748] Move lots code into separate files and split big functions party into smaller ones Also refactor small stuff here Removed the .send_anyway boolean and use .ready instead --- index.js | 735 ++++++++-------------------------------- package.json | 3 +- test/multi.spec.js | 2 +- test/node_redis.spec.js | 2 +- 4 files changed, 150 insertions(+), 592 deletions(-) diff --git a/index.js b/index.js index 4263786382e..b59e69d5e9a 100644 --- a/index.js +++ b/index.js @@ -2,20 +2,22 @@ var net = require('net'); var tls = require('tls'); -var URL = require('url'); var util = require('util'); var utils = require('./lib/utils'); var Queue = require('double-ended-queue'); var Command = require('./lib/command'); -var events = require('events'); +var EventEmitter = require('events'); var Parser = require('redis-parser'); var commands = require('redis-commands'); -var connection_id = 0; -var default_port = 6379; -var default_host = '127.0.0.1'; +var debug = require('./lib/debug'); +var unifyOptions = require('./lib/createClient'); + +// Newer Node.js versions > 0.10 return the EventEmitter right away and using .EventEmitter was deprecated +if (typeof EventEmitter !== 'function') { + EventEmitter = EventEmitter.EventEmitter; +} function noop () {} -function debug (msg) { if (exports.debug_mode) { console.error(msg); } } function handle_detect_buffers_reply (reply, command, buffer_args) { if (buffer_args === false) { @@ -35,15 +37,15 @@ exports.debug_mode = /\bredis\b/i.test(process.env.NODE_DEBUG); function RedisClient (options) { // Copy the options so they are not mutated options = utils.clone(options); - events.EventEmitter.call(this); + EventEmitter.call(this); var cnx_options = {}; var self = this; if (options.path) { cnx_options.path = options.path; this.address = options.path; } else { - cnx_options.port = +options.port || default_port; - cnx_options.host = options.host || default_host; + cnx_options.port = +options.port || 6379; + cnx_options.host = options.host || '127.0.0.1'; cnx_options.family = (!options.family && net.isIP(cnx_options.host)) || (options.family === 'IPv6' ? 6 : 4); this.address = cnx_options.host + ':' + cnx_options.port; } @@ -459,9 +461,12 @@ RedisClient.prototype.on_info_cmd = function (err, res) { RedisClient.prototype.ready_check = function () { var self = this; debug('Checking server ready state...'); + // Always fire this info command as first command even if other commands are already queued up + this.ready = true; this.info(function (err, res) { self.on_info_cmd(err, res); }); + this.ready = false; }; RedisClient.prototype.send_offline_queue = function () { @@ -599,11 +604,7 @@ RedisClient.prototype.return_error = function (err) { this.emit_idle(queue_len); - if (command_obj && command_obj.callback) { - command_obj.callback(err); - } else { - this.emit('error', err); - } + utils.callback_or_emit(this, command_obj && command_obj.callback, err); }; RedisClient.prototype.drain = function () { @@ -612,13 +613,78 @@ RedisClient.prototype.drain = function () { }; RedisClient.prototype.emit_idle = function (queue_len) { - if (this.pub_sub_mode === false && queue_len === 0) { + if (queue_len === 0 && this.pub_sub_mode === false) { this.emit('idle'); } }; +/* istanbul ignore next: this is a safety check that we should not be able to trigger */ +function queue_state_error (self, command_obj) { + var err = new Error('node_redis command queue state error. If you can reproduce this, please report it.'); + err.command_obj = command_obj; + self.emit('error', err); +} + +function monitor (self, reply) { + if (typeof reply !== 'string') { + reply = reply.toString(); + } + // If in monitoring mode only two commands are valid ones: AUTH and MONITOR wich reply with OK + var len = reply.indexOf(' '); + var timestamp = reply.slice(0, len); + var argindex = reply.indexOf('"'); + var args = reply.slice(argindex + 1, -1).split('" "').map(function (elem) { + return elem.replace(/\\"/g, '"'); + }); + self.emit('monitor', timestamp, args); +} + +function normal_reply (self, reply, command_obj) { + if (typeof command_obj.callback === 'function') { + if ('exec' !== command_obj.command) { + reply = self.handle_reply(reply, command_obj.command, command_obj.buffer_args); + } + command_obj.callback(null, reply); + } else { + debug('No callback for reply'); + } +} + +function return_pub_sub (self, reply, command_obj) { + if (reply instanceof Array) { + if ((!command_obj || command_obj.buffer_args === false) && !self.options.return_buffers) { + reply = utils.reply_to_strings(reply); + } + var type = reply[0].toString(); + + // TODO: Add buffer emiters (we have to get all pubsub messages as buffers back in that case) + if (type === 'message') { + self.emit('message', reply[1], reply[2]); // channcel, message + } else if (type === 'pmessage') { + self.emit('pmessage', reply[1], reply[2], reply[3]); // pattern, channcel, message + } else if (type === 'subscribe' || type === 'unsubscribe' || type === 'psubscribe' || type === 'punsubscribe') { + if (reply[2].toString() === '0') { + self.pub_sub_mode = false; + debug('All subscriptions removed, exiting pub/sub mode'); + } else { + self.pub_sub_mode = true; + } + // Subscribe commands take an optional callback and also emit an event, but only the first response is included in the callback + // TODO - document this or fix it so it works in a more obvious way + if (command_obj && typeof command_obj.callback === 'function') { + command_obj.callback(null, reply[1]); + } + self.emit(type, reply[1], reply[2]); // channcel, count + } else { + self.emit('error', new Error('subscriptions are active but got unknown reply type ' + type)); + } + } else if (!self.closing) { + self.emit('error', new Error('subscriptions are active but got an invalid reply: ' + reply)); + } +} + RedisClient.prototype.return_reply = function (reply) { - var command_obj, len, type, timestamp, argindex, args, queue_len; + var command_obj, type, queue_len; // If the 'reply' here is actually a message received asynchronously due to a // pubsub subscription, don't pop the command queue as we'll only be consuming @@ -638,84 +704,68 @@ RedisClient.prototype.return_reply = function (reply) { this.emit_idle(queue_len); if (command_obj && !command_obj.sub_command) { - if (typeof command_obj.callback === 'function') { - if ('exec' !== command_obj.command) { - reply = this.handle_reply(reply, command_obj.command, command_obj.buffer_args); - } - command_obj.callback(null, reply); - } else { - debug('No callback for reply'); - } + normal_reply(this, reply, command_obj); } else if (this.pub_sub_mode || command_obj && command_obj.sub_command) { - if (reply instanceof Array) { - if ((!command_obj || command_obj.buffer_args === false) && !this.options.return_buffers) { - reply = utils.reply_to_strings(reply); - } - type = reply[0].toString(); - - // TODO: Add buffer emiters (we have to get all pubsub messages as buffers back in that case) - if (type === 'message') { - this.emit('message', reply[1], reply[2]); // channel, message - } else if (type === 'pmessage') { - this.emit('pmessage', reply[1].toString(), reply[2], reply[3]); // pattern, channel, message - } else if (type === 'subscribe' || type === 'unsubscribe' || type === 'psubscribe' || type === 'punsubscribe') { - if (reply[2].toString() === '0') { - this.pub_sub_mode = false; - debug('All subscriptions removed, exiting pub/sub mode'); - } else { - this.pub_sub_mode = true; - } - // Subscribe commands take an optional callback and also emit an event, but only the first response is included in the callback - // TODO - document this or fix it so it works in a more obvious way - if (command_obj && typeof command_obj.callback === 'function') { - command_obj.callback(null, reply[1]); - } - this.emit(type, reply[1], reply[2]); // channel, count - } else { - this.emit('error', new Error('subscriptions are active but got unknown reply type ' + type)); - } - } else if (!this.closing) { - this.emit('error', new Error('subscriptions are active but got an invalid reply: ' + reply)); - } + return_pub_sub(this, reply, command_obj); } /* istanbul ignore else: this is a safety check that we should not be able to trigger */ else if (this.monitoring) { - if (typeof reply !== 'string') { - reply = reply.toString(); - } - // If in monitoring mode only two commands are valid ones: AUTH and MONITOR wich reply with OK - len = reply.indexOf(' '); - timestamp = reply.slice(0, len); - argindex = reply.indexOf('"'); - args = reply.slice(argindex + 1, -1).split('" "').map(function (elem) { - return elem.replace(/\\"/g, '"'); - }); - this.emit('monitor', timestamp, args); + monitor(this, reply); } else { - var err = new Error('node_redis command queue state error. If you can reproduce this, please report it.'); - err.command_obj = command_obj; - this.emit('error', err); + queue_state_error(this, command_obj); } }; +function handle_offline_command (self, command_obj) { + var command = command_obj.command; + var callback = command_obj.callback; + var err, msg; + if (self.closing || !self.enable_offline_queue) { + command = command.toUpperCase(); + if (!self.closing) { + if (self.stream.writable) { + msg = 'The connection is not yet established and the offline queue is deactivated.'; + } else { + msg = 'Stream not writeable.'; + } + } else { + msg = 'The connection has already been closed.'; + } + err = new Error(command + " can't be processed. " + msg); + err.command = command; + utils.reply_in_order(self, callback, err); + } else { + debug('Queueing ' + command + ' for next server connection.'); + self.offline_queue.push(command_obj); + } + self.should_buffer = true; +} + RedisClient.prototype.send_command = function (command, args, callback) { - var arg, command_obj, i, err, - stream = this.stream, - command_str = '', - buffer_args = false, - big_data = false, - prefix_keys, - len, args_copy; + var args_copy, arg, prefix_keys; + var i = 0; + var command_str = ''; + var len = 0; + var big_data = false; - if (typeof args === 'undefined') { - args = []; - } - if (callback && process.domain) { + if (process.domain && callback) { callback = process.domain.bind(callback); } - len = args.length; - args_copy = new Array(len); + var command_obj = new Command(command, args, callback); + + if (this.ready === false || this.stream.writable === false) { + // Handle offline commands right away + handle_offline_command(this, command_obj); + return false; // Indicate buffering + } + + if (typeof args === 'undefined') { + args_copy = []; + } else { + len = args.length; + args_copy = new Array(len); + } for (i = 0; i < len; i += 1) { if (typeof args[i] === 'string') { @@ -742,7 +792,8 @@ RedisClient.prototype.send_command = function (command, args, callback) { args_copy[i] = 'null'; // Backwards compatible :/ } else { args_copy[i] = args[i]; - buffer_args = true; + command_obj.buffer_args = true; + big_data = true; if (this.pipeline !== 0) { this.pipeline += 2; this.writeDefault = this.writeBuffers; @@ -756,34 +807,11 @@ RedisClient.prototype.send_command = function (command, args, callback) { ); args_copy[i] = 'undefined'; // Backwards compatible :/ } else { - args_copy[i] = String(args[i]); - } - } - - command_obj = new Command(command, args_copy, buffer_args, callback); - - // TODO: Replace send_anyway with `commands.hasFlag(command, 'loading') === false` as soon as pub_sub is handled in the result handler - if (this.ready === false && this.send_anyway === false || !stream.writable) { - if (this.closing || !this.enable_offline_queue) { - command = command.toUpperCase(); - if (!this.closing) { - var msg = !stream.writable ? - 'Stream not writeable.' : - 'The connection is not yet established and the offline queue is deactivated.'; - err = new Error(command + " can't be processed. " + msg); - } else { - err = new Error(command + " can't be processed. The connection has already been closed."); - } - err.command = command; - this.callback_emit_error(callback, err); - } else { - debug('Queueing ' + command + ' for next server connection.'); - this.offline_queue.push(command_obj); - this.should_buffer = true; + // Seems like numbers are converted fast using string concatenation + args_copy[i] = '' + args[i]; } - // Return false to signal buffering - return !this.should_buffer; } + args = null; if (command === 'subscribe' || command === 'psubscribe' || command === 'unsubscribe' || command === 'punsubscribe') { this.pub_sub_command(command_obj); // TODO: This has to be moved to the result handler @@ -807,7 +835,7 @@ RedisClient.prototype.send_command = function (command, args, callback) { // This means that using Buffers in commands is going to be slower, so use Strings if you don't already have a Buffer. command_str = '*' + (len + 1) + '\r\n$' + command.length + '\r\n' + command + '\r\n'; - if (buffer_args === false && big_data === false) { // Build up a string and send entire command in one write + if (big_data === false) { // Build up a string and send entire command in one write for (i = 0; i < len; i += 1) { arg = args_copy[i]; command_str += '$' + Buffer.byteLength(arg) + '\r\n' + arg + '\r\n'; @@ -820,7 +848,7 @@ RedisClient.prototype.send_command = function (command, args, callback) { for (i = 0; i < len; i += 1) { arg = args_copy[i]; - if (typeof arg !== 'object') { // string; number; boolean + if (typeof arg === 'string') { this.write('$' + Buffer.byteLength(arg) + '\r\n' + arg + '\r\n'); } else { // buffer this.write('$' + arg.length + '\r\n'); @@ -911,497 +939,26 @@ RedisClient.prototype.end = function (flush) { 'Please check the doku (https://github.com/NodeRedis/node_redis) and explictly use flush.' ); } - - this.stream._events = {}; - // Clear retry_timer if (this.retry_timer){ clearTimeout(this.retry_timer); this.retry_timer = null; } + this.stream.removeAllListeners(); this.stream.on('error', noop); - this.connected = false; this.ready = false; this.closing = true; return this.stream.destroySoon(); }; -function Multi(client, args) { - this._client = client; - this.queue = new Queue(); - var command, tmp_args; - if (args) { // Either undefined or an array. Fail hard if it's not an array - for (var i = 0; i < args.length; i++) { - command = args[i][0]; - tmp_args = args[i].slice(1); - if (Array.isArray(command)) { - this[command[0]].apply(this, command.slice(1).concat(tmp_args)); - } else { - this[command].apply(this, tmp_args); - } - } - } -} - -commands.list.forEach(function (command) { - - RedisClient.prototype[command.toUpperCase()] = RedisClient.prototype[command] = function () { - var arr, - len = 0, - callback, - i = 0; - if (arguments[0] instanceof Array) { - arr = arguments[0]; - callback = arguments[1]; // It does not matter if it exists or not - } else if (arguments[1] instanceof Array) { - len = arguments[1].length; - arr = new Array(len + 1); - arr[0] = arguments[0]; - for (; i < len; i += 1) { - arr[i + 1] = arguments[1][i]; - } - callback = arguments[2]; - } else { - len = arguments.length; - arr = new Array(len); - for (; i < len; i += 1) { - arr[i] = arguments[i]; - } - // The later should not be the average use case - if (typeof arr[i - 1] === 'function' || typeof arr[i - 1] === 'undefined') { - callback = arr.pop(); - } - } - return this.send_command(command, arr, callback); - }; - - Multi.prototype[command.toUpperCase()] = Multi.prototype[command] = function () { - var arr, - len = 0, - callback, - i = 0; - if (arguments[0] instanceof Array) { - arr = arguments[0]; - callback = arguments[1]; - } else if (arguments[1] instanceof Array) { - len = arguments[1].length; - arr = new Array(len + 1); - arr[0] = arguments[0]; - for (; i < len; i += 1) { - arr[i + 1] = arguments[1][i]; - } - callback = arguments[2]; - } else { - len = arguments.length; - arr = new Array(len); - for (; i < len; i += 1) { - arr[i] = arguments[i]; - } - // The later should not be the average use case - if (typeof arr[i - 1] === 'function' || typeof arr[i - 1] === 'undefined') { - callback = arr.pop(); - } - } - this.queue.push([command, arr, callback]); - return this; - }; -}); - -RedisClient.prototype.multi = RedisClient.prototype.MULTI = function (args) { - var multi = new Multi(this, args); - multi.exec = multi.EXEC = multi.exec_transaction; - return multi; -}; - -RedisClient.prototype.batch = RedisClient.prototype.BATCH = function (args) { - return new Multi(this, args); -}; - -// Store db in this.select_db to restore it on reconnect -RedisClient.prototype.select = RedisClient.prototype.SELECT = function (db, callback) { - var self = this; - return this.send_command('select', [db], function (err, res) { - if (err === null) { - self.selected_db = db; - } - if (typeof callback === 'function') { - callback(err, res); - } else if (err) { - self.emit('error', err); - } - }); +exports.createClient = function () { + return new RedisClient(unifyOptions.apply(null, arguments)); }; - -// Store info in this.server_info after each call -RedisClient.prototype.info = RedisClient.prototype.INFO = function (callback) { - var self = this; - this.send_anyway = true; - var tmp = this.send_command('info', [], function (err, res) { - if (res) { - var obj = {}; - var lines = res.toString().split('\r\n'); - var line, parts, sub_parts; - - for (var i = 0; i < lines.length; i++) { - parts = lines[i].split(':'); - if (parts[1]) { - if (parts[0].indexOf('db') === 0) { - sub_parts = parts[1].split(','); - obj[parts[0]] = {}; - while (line = sub_parts.pop()) { - line = line.split('='); - obj[parts[0]][line[0]] = +line[1]; - } - } else { - obj[parts[0]] = parts[1]; - } - } - } - obj.versions = []; - /* istanbul ignore else: some redis servers do not send the version */ - if (obj.redis_version) { - obj.redis_version.split('.').forEach(function (num) { - obj.versions.push(+num); - }); - } - // Expose info key/vals to users - self.server_info = obj; - } else { - self.server_info = {}; - } - if (typeof callback === 'function') { - callback(err, res); - } else if (err) { - self.emit('error', err); - } - }); - this.send_anyway = false; - return tmp; -}; - -RedisClient.prototype.callback_emit_error = function (callback, err) { - if (callback) { - setImmediate(function () { - callback(err); - }); - } else { - this.emit('error', err); - } -}; - -var noPasswordIsSet = /no password is set/; - -RedisClient.prototype.auth = function (pass, callback) { - var self = this; - debug('Sending auth to ' + self.address + ' id ' + self.connection_id); - - // Stash auth for connect and reconnect. - this.auth_pass = pass; - this.send_anyway = true; - var tmp = this.send_command('auth', [pass], function (err, res) { - if (err) { - if (noPasswordIsSet.test(err.message)) { - self.warn('Warning: Redis server does not require a password, but a password was supplied.'); - err = null; - res = 'OK'; - } else if (!callback) { - self.emit('error', err); - } - } - if (callback) { - callback(err, res); - } - }); - this.send_anyway = false; - return tmp; -}; - -RedisClient.prototype.hmset = RedisClient.prototype.HMSET = function () { - var arr, - len = 0, - callback, - i = 0; - if (arguments[0] instanceof Array) { - arr = arguments[0]; - callback = arguments[1]; - } else if (arguments[1] instanceof Array) { - len = arguments[1].length; - arr = new Array(len + 1); - arr[0] = arguments[0]; - for (; i < len; i += 1) { - arr[i + 1] = arguments[1][i]; - } - callback = arguments[2]; - } else if (typeof arguments[1] === 'object' && (typeof arguments[2] === 'function' || typeof arguments[2] === 'undefined')) { - arr = [arguments[0]]; - for (var field in arguments[1]) { // jshint ignore: line - arr.push(field, arguments[1][field]); - } - callback = arguments[2]; - } else { - len = arguments.length; - arr = new Array(len); - for (; i < len; i += 1) { - arr[i] = arguments[i]; - } - // The later should not be the average use case - if (typeof arr[i - 1] === 'function' || typeof arr[i - 1] === 'undefined') { - callback = arr.pop(); - } - } - return this.send_command('hmset', arr, callback); -}; - -Multi.prototype.hmset = Multi.prototype.HMSET = function () { - var arr, - len = 0, - callback, - i = 0; - if (arguments[0] instanceof Array) { - arr = arguments[0]; - callback = arguments[1]; - } else if (arguments[1] instanceof Array) { - len = arguments[1].length; - arr = new Array(len + 1); - arr[0] = arguments[0]; - for (; i < len; i += 1) { - arr[i + 1] = arguments[1][i]; - } - callback = arguments[2]; - } else if (typeof arguments[1] === 'object' && (typeof arguments[2] === 'function' || typeof arguments[2] === 'undefined')) { - arr = [arguments[0]]; - for (var field in arguments[1]) { // jshint ignore: line - arr.push(field, arguments[1][field]); - } - callback = arguments[2]; - } else { - len = arguments.length; - arr = new Array(len); - for (; i < len; i += 1) { - arr[i] = arguments[i]; - } - // The later should not be the average use case - if (typeof arr[i - 1] === 'function' || typeof arr[i - 1] === 'undefined') { - callback = arr.pop(); - } - } - this.queue.push(['hmset', arr, callback]); - return this; -}; - -Multi.prototype.send_command = function (command, args, index, cb) { - var self = this; - this._client.send_command(command, args, function (err, reply) { - if (err) { - if (cb) { - cb(err); - } - err.position = index; - self.errors.push(err); - } - }); -}; - -Multi.prototype.exec_atomic = function (callback) { - if (this.queue.length < 2) { - return this.exec_batch(callback); - } - return this.exec(callback); -}; - -Multi.prototype.exec_transaction = function (callback) { - var self = this; - var len = this.queue.length; - this.errors = []; - this.callback = callback; - this._client.cork(len + 2); - this.wants_buffers = new Array(len); - this.send_command('multi', []); - // Drain queue, callback will catch 'QUEUED' or error - for (var index = 0; index < len; index++) { - var args = this.queue.get(index); - var command = args[0]; - var cb = args[2]; - // Keep track of who wants buffer responses: - if (this._client.options.detect_buffers) { - this.wants_buffers[index] = false; - for (var i = 0; i < args[1].length; i += 1) { - if (Buffer.isBuffer(args[1][i])) { - this.wants_buffers[index] = true; - break; - } - } - } - this.send_command(command, args[1], index, cb); - } - - this._client.send_command('exec', [], function(err, replies) { - self.execute_callback(err, replies); - }); - this._client.uncork(); - this._client.writeDefault = this._client.writeStrings; - return !this._client.should_buffer; -}; - -Multi.prototype.execute_callback = function (err, replies) { - var i = 0, args; - - if (err) { - // The errors would be circular - var connection_error = ['CONNECTION_BROKEN', 'UNCERTAIN_STATE'].indexOf(err.code) !== -1; - err.errors = connection_error ? [] : this.errors; - if (this.callback) { - this.callback(err); - // Exclude connection errors so that those errors won't be emitted twice - } else if (!connection_error) { - this._client.emit('error', err); - } - return; - } - - if (replies) { - while (args = this.queue.shift()) { - if (replies[i] instanceof Error) { - var match = replies[i].message.match(utils.err_code); - // LUA script could return user errors that don't behave like all other errors! - if (match) { - replies[i].code = match[1]; - } - replies[i].command = args[0].toUpperCase(); - if (typeof args[args.length - 1] === 'function') { - args[args.length - 1](replies[i]); - } - } else { - // If we asked for strings, even in detect_buffers mode, then return strings: - replies[i] = this._client.handle_reply(replies[i], args[0], this.wants_buffers[i]); - if (typeof args[args.length - 1] === 'function') { - args[args.length - 1](null, replies[i]); - } - } - i++; - } - } - - if (this.callback) { - this.callback(null, replies); - } -}; - -Multi.prototype.callback = function (cb, i) { - var self = this; - return function (err, res) { - if (err) { - self.results[i] = err; - // Add the position to the error - self.results[i].position = i; - } else { - self.results[i] = res; - } - cb(err, res); - }; -}; - -Multi.prototype.exec = Multi.prototype.EXEC = Multi.prototype.exec_batch = function (callback) { - var len = this.queue.length; - var self = this; - var index = 0; - var args; - var args_len = 1; - var callback_without_own_cb = function (err, res) { - if (err) { - self.results.push(err); - // Add the position to the error - var i = self.results.length - 1; - self.results[i].position = i; - } else { - self.results.push(res); - } - // Do not emit an error here. Otherwise each error would result in one emit. - // The errors will be returned in the result anyway - }; - var last_callback = function (cb) { - return function (err, res) { - cb(err, res); - callback(null, self.results); - }; - }; - if (len === 0) { - if (callback) { - // The execution order won't be obtained in this case - setImmediate(function () { - callback(null, []); - }); - } - return true; - } - this.results = []; - this._client.cork(len); - while (args = this.queue.shift()) { - var command = args[0]; - var cb; - args_len = args[1].length - 1; - if (typeof args[2] === 'function') { - cb = this.callback(args[2], index); - } else { - cb = callback_without_own_cb; - } - if (callback && index === len - 1) { - cb = last_callback(cb); - } - this._client.send_command(command, args[1], cb); - index++; - } - this._client.uncork(); - this._client.writeDefault = this._client.writeStrings; - return !this._client.should_buffer; -}; - -var createClient = function (port_arg, host_arg, options) { - if (typeof port_arg === 'number' || typeof port_arg === 'string' && /^\d+$/.test(port_arg)) { - options = utils.clone(options); - options.host = host_arg; - options.port = port_arg; - } else if (typeof port_arg === 'string' || port_arg && port_arg.url) { - options = utils.clone(port_arg.url ? port_arg : host_arg || options); - var parsed = URL.parse(port_arg.url || port_arg, true, true); - // [redis:]//[[user][:password]@][host][:port][/db-number][?db=db-number[&password=bar[&option=value]]] - if (parsed.hostname || parsed.slashes) { // The host might be an empty string - if (parsed.auth) { - options.password = parsed.auth.split(':')[1]; - } - if (!/^([a-z]+:)?\/\//i.test(parsed.href)) { - throw new Error('Connection string must use the "redis:" protocol or begin with slashes //'); - } - if (parsed.pathname && parsed.pathname !== '/') { - options.db = parsed.pathname.substr(1); - } - options.host = parsed.hostname; - options.port = parsed.port; - if (parsed.search !== '') { - var elem; - for (elem in parsed.query) { // jshint ignore: line - // If options are passed twice, only the parsed options will be used - if (options.hasOwnPropery(elem)) { - RedisClient.warn('WARNING: You passed the ' + elem + ' option twice!'); - } - options[elem] = parsed.query[elem]; - } - } - } else { - options.path = port_arg; - } - } else if (typeof port_arg === 'object' || port_arg === undefined) { - options = utils.clone(port_arg || options); - options.host = options.host || host_arg; - } - if (!options) { - throw new Error('Unknown type of connection in createClient()'); - } - return new RedisClient(options); -}; - -exports.createClient = createClient; exports.RedisClient = RedisClient; exports.print = utils.print; -exports.Multi = Multi; +exports.Multi = require('./lib/multi'); + +// Add all redis commands to the client +require('./lib/individualCommands'); +require('./lib/commands'); diff --git a/package.json b/package.json index ac010b7f322..7760c7cf87d 100644 --- a/package.json +++ b/package.json @@ -25,7 +25,7 @@ "dependencies": { "double-ended-queue": "^2.1.0-0", "redis-commands": "^1.0.1", - "redis-parser": "^1.0.0" + "redis-parser": "^1.1.0" }, "engines": { "node": ">=0.10.0" @@ -33,6 +33,7 @@ "devDependencies": { "bluebird": "^3.0.2", "coveralls": "^2.11.2", + "intercept-stdout": "~0.1.2", "jshint": "^2.8.0", "metrics": "^0.1.9", "mocha": "^2.3.2", diff --git a/test/multi.spec.js b/test/multi.spec.js index 181cdf87d58..334d0ae084e 100644 --- a/test/multi.spec.js +++ b/test/multi.spec.js @@ -120,7 +120,7 @@ describe("The 'multi' method", function () { assert(err.message.match(/The connection has already been closed/)); done(); }); - assert.strictEqual(notBuffering, true); + assert.strictEqual(notBuffering, false); }); it("reports an error if promisified", function () { diff --git a/test/node_redis.spec.js b/test/node_redis.spec.js index 527633e6ba5..45e86a5773b 100644 --- a/test/node_redis.spec.js +++ b/test/node_redis.spec.js @@ -747,7 +747,7 @@ describe("The node_redis client", function () { enable_offline_queue: false }); client.on('ready', function () { - client.stream.writable = false; + client.stream.destroy(); client.set('foo', 'bar', function (err, res) { assert.strictEqual(err.message, "SET can't be processed. Stream not writeable."); done(); From 2684fdbb4d4f8cfa0c372911ca641cfc0477d847 Mon Sep 17 00:00:00 2001 From: Ruben Bridgewater Date: Tue, 1 Mar 2016 17:14:00 +0100 Subject: [PATCH 0570/1748] Update changelog --- changelog.md | 28 +++++++++++++++++++++++++--- 1 file changed, 25 insertions(+), 3 deletions(-) diff --git a/changelog.md b/changelog.md index 5c1a01bb677..85d59db65be 100644 --- a/changelog.md +++ b/changelog.md @@ -1,7 +1,13 @@ Changelog ========= -## v.2.5.0-0 - xx Jan, 2015 +## v.2.5.0-1 - 01 Mar, 2015 + +This is a big release with some substaintual underlining changes. Therefor this is released as a pre-release and I encourage anyone who's able to, to test this out. + +It took way to long to release this one and the next release cycles will be shorter again. + +This release is also going to deprecate a couple things to prepare for a future v.3 (it'll still take a while to v.3). Features @@ -14,7 +20,16 @@ Features - Added a `password` option as alias for auth_pass - The client.server_info is from now on updated while using the info command - Gracefuly handle redis protocol errors from now on -- Added a `warning` emitter that receives deprecation messages and other node_redis warnings +- Added a `warning` emitter that receives node_redis warnings like auth not required and deprecation messages +- Added a `retry_strategy` option that replaces all reconnect options +- The reconnecting event from now on also receives: + - The error message why the reconnect happend (params.error) + - The amount of times the client was connected (params.times_connected) + - The total reconnecting time since the last time connected (params.total_retry_time) +- Always respect the command execution order no matter if the reply could be returned sync or not (former exceptions: [#937](https://github.com/NodeRedis/node_redis/issues/937#issuecomment-167525939)) +- redis.createClient is now checking input values stricter and detects more faulty input +- Started refactoring internals into individual modules +- Pipelining speed improvements Bugfixes @@ -26,6 +41,9 @@ Bugfixes - Fixed redis url not accepting the protocol being omitted or protocols other than the redis protocol for convienence - Fixed parsing the db keyspace even if the first database does not begin with a zero - Fixed handling of errors occuring while receiving pub sub messages +- Fixed huge string pipelines crashing NodeJS (Pipeline size above 256mb) +- Fixed rename_commands and prefix option not working together +- Fixed ready being emitted to early in case a slave is still syncing / master down Deprecations @@ -38,9 +56,13 @@ Deprecations - Using .end without flush means that any command that did not yet return is going to silently fail. Therefor this is considered harmfull and you should explicitly silence such errors if you are sure you want this - Depending on the return value of a command to detect the backpressure is deprecated - From version 3.0.0 on node_redis might not return true / false as a return value anymore. Please rely on client.should_buffer instead -- The socket_nodelay option is deprecated and will be removed in v.3.0.0 +- The `socket_nodelay` option is deprecated and will be removed in v.3.0.0 - If you want to buffer commands you should use [.batch or .multi](./README.md) instead. This is necessary to reduce the amount of different options and this is very likely reducing your throughput if set to false. - If you are sure you want to activate the NAGLE algorithm you can still activate it by using client.stream.setNoDelay(false) +- The `max_attempts` option is deprecated and will be removed in v.3.0.0. Please use the `retry_strategy` instead +- The `retry_max_delay` option is deprecated and will be removed in v.3.0.0. Please use the `retry_strategy` instead +- The drain event is deprecated and will be removed in v.3.0.0. Please listen to the stream drain event instead +- The idle event is deprecated and will likely be removed in v.3.0.0. If you rely on this feature please open a new ticket in node_redis with your use case - Redis < v. 2.6.11 is not supported anymore and will not work in all cases. Please update to a newer redis version - Removed non documented command syntax (adding the callback to an arguments array instead of passing it as individual argument) From 31a2d843e8070be735a914cc231b113d90f9452a Mon Sep 17 00:00:00 2001 From: Ruben Bridgewater Date: Tue, 1 Mar 2016 17:56:04 +0100 Subject: [PATCH 0571/1748] Add slave ready test --- test/conect.slave.spec.js | 92 +++++++++++++++++++++++++++++++++++++++ test/conf/slave.conf | 6 +++ 2 files changed, 98 insertions(+) create mode 100644 test/conect.slave.spec.js create mode 100644 test/conf/slave.conf diff --git a/test/conect.slave.spec.js b/test/conect.slave.spec.js new file mode 100644 index 00000000000..0080b2339c5 --- /dev/null +++ b/test/conect.slave.spec.js @@ -0,0 +1,92 @@ +'use strict'; + +var assert = require("assert"); +var config = require("./lib/config"); +var helper = require('./helper'); +var RedisProcess = require("./lib/redis-process"); +var rp; +var path = require('path'); +var redis = config.redis; + +describe('master slave sync', function () { + var master = null; + var slave = null; + + before(function (done) { + helper.stopRedis(function () { + helper.startRedis('./conf/password.conf', done); + }); + }); + + before(function (done) { + master = redis.createClient({ + password: 'porkchopsandwiches' + }); + var multi = master.multi(); + var i = 0; + while (i < 1000) { + i++; + // Write some data in the redis instance, so there's something to sync + multi.set('foo' + i, 'bar' + new Array(500).join(Math.random())); + } + multi.exec(done); + }); + + it("sync process and no master should delay ready being emitted for slaves", function (done) { + if (helper.redisProcess().spawnFailed()) this.skip(); + + var port = 6381; + var firstInfo; + slave = redis.createClient({ + port: port, + retry_strategy: function (options) { + // Try to reconnect in very small intervals to catch the master_link_status down before the sync completes + return 10; + } + }); + + var tmp = slave.info.bind(slave); + var i = 0; + slave.info = function (err, res) { + i++; + tmp(err, res); + if (!firstInfo || Object.keys(firstInfo).length === 0) { + firstInfo = slave.server_info; + } + }; + + slave.on('connect', function () { + assert.strictEqual(i, 0); + }); + + var end = helper.callFuncAfter(done, 2); + + slave.on('ready', function () { + assert.strictEqual(this.server_info.master_link_status, 'up'); + assert.strictEqual(firstInfo.master_link_status, 'down'); + assert(i > 1); + this.get('foo300', function (err, res) { + assert.strictEqual(res.substr(0, 3), 'bar'); + end(err); + }); + }); + + RedisProcess.start(function (err, _rp) { + rp = _rp; + end(err); + }, path.resolve(__dirname, './conf/slave.conf'), port); + }); + + after(function (done) { + var end = helper.callFuncAfter(done, 3); + rp.stop(end); + slave.end(true); + master.flushdb(function (err) { + end(err); + master.end(true); + }); + helper.stopRedis(function () { + helper.startRedis('./conf/redis.conf', end); + }); + }); +}); diff --git a/test/conf/slave.conf b/test/conf/slave.conf new file mode 100644 index 00000000000..61ea50d9593 --- /dev/null +++ b/test/conf/slave.conf @@ -0,0 +1,6 @@ +port 6381 +bind ::1 127.0.0.1 +unixsocket /tmp/redis6381.sock +unixsocketperm 755 +slaveof localhost 6379 +masterauth porkchopsandwiches From e48e1e845f06b0e469e98544f78bd37b40968a2f Mon Sep 17 00:00:00 2001 From: Ruben Bridgewater Date: Tue, 1 Mar 2016 19:55:26 +0100 Subject: [PATCH 0572/1748] Windows fixes Skip redis process spawn on windows for now --- test/auth.spec.js | 6 ++++++ test/conect.slave.spec.js | 7 +++++++ test/connection.spec.js | 25 +++++++++++++------------ test/helper.js | 30 +++++++++++++----------------- test/multi.spec.js | 2 +- test/rename.spec.js | 8 ++++++++ test/tls.spec.js | 11 +++-------- 7 files changed, 51 insertions(+), 38 deletions(-) diff --git a/test/auth.spec.js b/test/auth.spec.js index 5f18f350661..61e185fc859 100644 --- a/test/auth.spec.js +++ b/test/auth.spec.js @@ -5,6 +5,11 @@ var config = require("./lib/config"); var helper = require('./helper'); var redis = config.redis; +if (process.platform === 'win32') { + // TODO: Fix redis process spawn on windows + return; +} + describe("client authentication", function () { before(function (done) { helper.stopRedis(function () { @@ -237,6 +242,7 @@ describe("client authentication", function () { }); after(function (done) { + if (helper.redisProcess().spawnFailed()) return done(); helper.stopRedis(function () { helper.startRedis('./conf/redis.conf', done); }); diff --git a/test/conect.slave.spec.js b/test/conect.slave.spec.js index 0080b2339c5..28f56d94594 100644 --- a/test/conect.slave.spec.js +++ b/test/conect.slave.spec.js @@ -8,6 +8,11 @@ var rp; var path = require('path'); var redis = config.redis; +if (process.platform === 'win32') { + // TODO: Fix redis process spawn on windows + return; +} + describe('master slave sync', function () { var master = null; var slave = null; @@ -19,6 +24,7 @@ describe('master slave sync', function () { }); before(function (done) { + if (helper.redisProcess().spawnFailed()) return done(); master = redis.createClient({ password: 'porkchopsandwiches' }); @@ -78,6 +84,7 @@ describe('master slave sync', function () { }); after(function (done) { + if (helper.redisProcess().spawnFailed()) return done(); var end = helper.callFuncAfter(done, 3); rp.stop(end); slave.end(true); diff --git a/test/connection.spec.js b/test/connection.spec.js index 262face2ec2..8eb38f47dc2 100644 --- a/test/connection.spec.js +++ b/test/connection.spec.js @@ -257,20 +257,21 @@ describe("connection tests", function () { client.once('ready', done); }); - if (process.platform !== 'win32') { - it("connect with path provided in the options object", function (done) { - client = redis.createClient({ - path: '/tmp/redis.sock', - parser: parser, - connect_timeout: 1000 - }); + it("connect with path provided in the options object", function (done) { + if (process.platform === 'win32') { + this.skip(); + } + client = redis.createClient({ + path: '/tmp/redis.sock', + parser: parser, + connect_timeout: 1000 + }); - var end = helper.callFuncAfter(done, 2); + var end = helper.callFuncAfter(done, 2); - client.once('ready', end); - client.set('foo', 'bar', end); - }); - } + client.once('ready', end); + client.set('foo', 'bar', end); + }); it("connects correctly with args", function (done) { client = redis.createClient.apply(redis.createClient, args); diff --git a/test/helper.js b/test/helper.js index 74085772f3b..dd30e527b0a 100644 --- a/test/helper.js +++ b/test/helper.js @@ -15,21 +15,6 @@ function startRedis (conf, done, port) { }, path.resolve(__dirname, conf), port); } -function startStunnel(done) { - StunnelProcess.start(function (err, _stunnel_process) { - stunnel_process = _stunnel_process; - return done(err); - }, path.resolve(__dirname, './conf')); -} - -function stopStunnel(done) { - if (stunnel_process) { - StunnelProcess.stop(stunnel_process, done); - } else { - done(); - } -} - // don't start redis every time we // include this helper file! if (!process.env.REDIS_TESTS_STARTED) { @@ -52,8 +37,19 @@ module.exports = { rp.stop(done); }, startRedis: startRedis, - stopStunnel: stopStunnel, - startStunnel: startStunnel, + stopStunnel: function (done) { + if (stunnel_process) { + StunnelProcess.stop(stunnel_process, done); + } else { + done(); + } + }, + startStunnel: function (done) { + StunnelProcess.start(function (err, _stunnel_process) { + stunnel_process = _stunnel_process; + return done(err); + }, path.resolve(__dirname, './conf')); + }, isNumber: function (expected, done) { return function (err, results) { assert.strictEqual(null, err, "expected " + expected + ", got error: " + err); diff --git a/test/multi.spec.js b/test/multi.spec.js index 334d0ae084e..bd6f498abe9 100644 --- a/test/multi.spec.js +++ b/test/multi.spec.js @@ -73,7 +73,7 @@ describe("The 'multi' method", function () { describe('pipeline limit', function () { it('do not exceed maximum string size', function (done) { - this.timeout(12000); // Windows tests on 0.10 are slow + this.timeout(25000); // Windows tests are horribly slow // Triggers a RangeError: Invalid string length if not handled properly client = redis.createClient(); var multi = client.multi(); diff --git a/test/rename.spec.js b/test/rename.spec.js index e829323c82d..0d3aa0cc19e 100644 --- a/test/rename.spec.js +++ b/test/rename.spec.js @@ -5,6 +5,11 @@ var config = require("./lib/config"); var helper = require('./helper'); var redis = config.redis; +if (process.platform === 'win32') { + // TODO: Fix redis process spawn on windows + return; +} + describe("rename commands", function () { before(function (done) { helper.stopRedis(function () { @@ -18,6 +23,7 @@ describe("rename commands", function () { var client = null; beforeEach(function(done) { + if (helper.redisProcess().spawnFailed()) return done(); client = redis.createClient({ rename_commands: { set: '807081f5afa96845a02816a28b7258c3', @@ -32,6 +38,7 @@ describe("rename commands", function () { }); afterEach(function () { + if (helper.redisProcess().spawnFailed()) return; client.end(true); }); @@ -132,6 +139,7 @@ describe("rename commands", function () { }); after(function (done) { + if (helper.redisProcess().spawnFailed()) return done(); helper.stopRedis(function () { helper.startRedis('./conf/redis.conf', done); }); diff --git a/test/tls.spec.js b/test/tls.spec.js index 20cd246494e..8caddf816f4 100644 --- a/test/tls.spec.js +++ b/test/tls.spec.js @@ -32,26 +32,21 @@ describe("TLS connection tests", function () { skip = true; console.warn('\nTravis does not support stunnel right now. Skipping tests.\nCheck: https://github.com/travis-ci/apt-package-whitelist/issues/403\n'); } - if (skip) { - done(); - return; - } + if (skip) return done(); helper.stopStunnel(function () { helper.startStunnel(done); }); }); after(function (done) { - if (skip) { - done(); - return; - } + if (skip) return done(); helper.stopStunnel(done); }); var client; afterEach(function () { + if (skip) return; client.end(true); }); From 7c9c5e2693060faaaaec2247664940dee54c0388 Mon Sep 17 00:00:00 2001 From: Ruben Bridgewater Date: Mon, 7 Mar 2016 02:34:30 +0100 Subject: [PATCH 0573/1748] Update readme to include more details about retry_strategy and backpressure --- README.md | 36 +++++++++++++++++++++++++++++++++--- 1 file changed, 33 insertions(+), 3 deletions(-) diff --git a/README.md b/README.md index c48054b1a32..15ef775faf8 100644 --- a/README.md +++ b/README.md @@ -216,10 +216,11 @@ limits total amount of connection tries. Setting this to 1 will prevent any reco * `tls`: an object containing options to pass to [tls.connect](http://nodejs.org/api/tls.html#tls_tls_connect_port_host_options_callback), to set up a TLS connection to Redis (if, for example, it is set up to be accessible via a tunnel). * `prefix`: *null*; pass a string to prefix all used keys with that string as prefix e.g. 'namespace:test' +* `retry_strategy`: *function*; pass a function that receives a options object as parameter including the retry `attempt`, the `total_retry_time` indicating how much time passed since the last time connected, the `error` why the connection was lost and the number of `times_connected` in total. If you return a number from this function, the retry will happen exactly after that time in milliseconds. If you return a non-number no further retry is going to happen and all offline commands are flushed with errors. Return a error to return that specific error to all offline commands. Check out the example too. ```js -var redis = require("redis"), - client = redis.createClient({detect_buffers: true}); +var redis = require("redis"); +var client = redis.createClient({detect_buffers: true}); client.set("foo_rand000000000000", "OK"); @@ -235,6 +236,28 @@ client.get(new Buffer("foo_rand000000000000"), function (err, reply) { client.end(); ``` +retry_strategy example +```js +var client = redis.createClient({ + retry_strategy: function (options) { + if (options.error.code === 'ECONNREFUSED') { + // End reconnecting on a specific error and flush all commands with a individual error + return new Error('The server refused the connection'); + } + if (options.total_retry_time > 1000 * 60 * 60) { + // End reconnecting after a specific timeout and flush all commands with a individual error + return new Error('Retry time exhausted'); + } + if (options.times_connected > 10) { + // End reconnecting with built in error + return undefined; + } + // reconnect after + return Math.max(options.attempt * 100, 3000); + } +}); +``` + ## client.auth(password[, callback]) When connecting to a Redis server that requires authentication, the `AUTH` command must be sent as the @@ -246,6 +269,13 @@ NOTE: Your call to `client.auth()` should not be inside the ready handler. If you are doing this wrong, `client` will emit an error that looks something like this `Error: Ready check failed: ERR operation not permitted`. +## backpressure + +### stream + +The client exposed the used [stream](https://nodejs.org/api/stream.html) in `client.stream` and if the stream or client had to [buffer](https://nodejs.org/api/stream.html#stream_writable_write_chunk_encoding_callback) the command in `client.should_buffer`. +In combination this can be used to implement backpressure by checking the buffer state before sending a command and listening to the stream [drain](https://nodejs.org/api/stream.html#stream_event_drain) event. + ## client.end(flush) Forcibly close the connection to the Redis server. Note that this does not wait until all replies have been parsed. @@ -272,7 +302,7 @@ client.get("foo_rand000000000000", function (err, reply) { }); ``` -`client.end()` without the flush parameter should not be used in production! +`client.end()` without the flush parameter should NOT be used in production! ## client.unref() From ae0030659f0094f534dad7c35e06be72810f7bae Mon Sep 17 00:00:00 2001 From: beaulm Date: Sun, 6 Mar 2016 20:22:43 -0600 Subject: [PATCH 0574/1748] Update README.md Changed "Note that the API is entire asynchronous" to "Note that the API is entirely asynchronous" --- README.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/README.md b/README.md index a88332e4225..5c914795bca 100644 --- a/README.md +++ b/README.md @@ -50,7 +50,7 @@ This will display: 1: hashtest 2 mjr:~/work/node_redis (master)$ -Note that the API is entire asynchronous. To get data back from the server, you'll need to use a callback. +Note that the API is entirely asynchronous. To get data back from the server, you'll need to use a callback. ### Promises From b14cbaf1d517ca040bbc1107bbd167ec92428c00 Mon Sep 17 00:00:00 2001 From: Matt Berther Date: Thu, 18 Feb 2016 15:47:03 -0700 Subject: [PATCH 0575/1748] updating the test description to match the command --- test/commands/exits.spec.js | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/test/commands/exits.spec.js b/test/commands/exits.spec.js index 8d5ac41a319..080e1d1e65c 100644 --- a/test/commands/exits.spec.js +++ b/test/commands/exits.spec.js @@ -4,7 +4,7 @@ var config = require("../lib/config"); var helper = require("../helper"); var redis = config.redis; -describe("The 'exits' method", function () { +describe("The 'exists' method", function () { helper.allTests(function(parser, ip, args) { From aa2d8c04e0232aa5e68c771feec7262fbfc8039d Mon Sep 17 00:00:00 2001 From: Ruben Bridgewater Date: Mon, 7 Mar 2016 03:29:46 +0100 Subject: [PATCH 0576/1748] 2.5.0-1 --- changelog.md | 2 +- package.json | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/changelog.md b/changelog.md index 85d59db65be..4827641a149 100644 --- a/changelog.md +++ b/changelog.md @@ -1,7 +1,7 @@ Changelog ========= -## v.2.5.0-1 - 01 Mar, 2015 +## v.2.5.0-1 - 07 Mar, 2015 This is a big release with some substaintual underlining changes. Therefor this is released as a pre-release and I encourage anyone who's able to, to test this out. diff --git a/package.json b/package.json index 7760c7cf87d..81b8df96994 100644 --- a/package.json +++ b/package.json @@ -1,6 +1,6 @@ { "name": "redis", - "version": "2.4.2", + "version": "2.5.0-1", "description": "Redis client library", "keywords": [ "database", From a09f6b964c697e703520169bb724a6ea0217daee Mon Sep 17 00:00:00 2001 From: Ruben Bridgewater Date: Mon, 7 Mar 2016 04:20:42 +0100 Subject: [PATCH 0577/1748] Update license year --- LICENSE | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/LICENSE b/LICENSE index a515acd061a..710407f442b 100644 --- a/LICENSE +++ b/LICENSE @@ -1,6 +1,6 @@ LICENSE - "MIT License" -Copyright (c) 2015 by NodeRedis +Copyright (c) 2016 by NodeRedis Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation From 49179babb284b70ce3f586fc867984ca2ddfeb72 Mon Sep 17 00:00:00 2001 From: Ruben Bridgewater Date: Mon, 7 Mar 2016 04:20:52 +0100 Subject: [PATCH 0578/1748] Add more keywords --- package.json | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/package.json b/package.json index 0a3ce4e8879..bd1d42831fd 100644 --- a/package.json +++ b/package.json @@ -9,7 +9,9 @@ "pipelining", "performance", "queue", - "nodejs" + "nodejs", + "pubsub", + "backpressure" ], "author": "Matt Ranney ", "license": "MIT", From 7ddb955517fb622347138fbe2cc7d5953138e479 Mon Sep 17 00:00:00 2001 From: Matt Berther Date: Thu, 18 Feb 2016 15:46:34 -0700 Subject: [PATCH 0579/1748] updating the test filename to match the command --- test/commands/{exits.spec.js => exists.spec.js} | 0 1 file changed, 0 insertions(+), 0 deletions(-) rename test/commands/{exits.spec.js => exists.spec.js} (100%) diff --git a/test/commands/exits.spec.js b/test/commands/exists.spec.js similarity index 100% rename from test/commands/exits.spec.js rename to test/commands/exists.spec.js From 2913eaccaf20cea9b57eff752d45e10aba4439f8 Mon Sep 17 00:00:00 2001 From: Ruben Bridgewater Date: Mon, 7 Mar 2016 11:52:48 +0100 Subject: [PATCH 0580/1748] Make tests more robust and print more details if one might still fail --- test/connection.spec.js | 15 +++++++++------ test/node_redis.spec.js | 5 +++++ 2 files changed, 14 insertions(+), 6 deletions(-) diff --git a/test/connection.spec.js b/test/connection.spec.js index 8eb38f47dc2..3fb468ba404 100644 --- a/test/connection.spec.js +++ b/test/connection.spec.js @@ -213,8 +213,11 @@ describe("connection tests", function () { client.on('error', function(err) { assert(/Redis connection in broken state: connection timeout.*?exceeded./.test(err.message)); - assert(Date.now() - time < connect_timeout + 25); - assert(Date.now() - time >= connect_timeout - 3); // Timers sometimes trigger early (e.g. 1ms to early) + // The code execution on windows is very slow at times + var now = Date.now(); + assert(now - time < connect_timeout + 50, 'The real timeout time should be below ' + (connect_timeout + 50) + 'ms but is: ' + (now - time)); + // Timers sometimes trigger early (e.g. 1ms to early) + assert(now - time >= connect_timeout - 3, 'The real timeout time should be above ' + connect_timeout + 'ms, but it is: ' + (now - time)); done(); }); }); @@ -464,9 +467,9 @@ describe("connection tests", function () { }; client.on("ready", function () { var rest = Date.now() - time; - assert(rest >= 500); + assert(rest >= 498, 'Rest should be equal or above 500 ms but is: ' + rest); // setTimeout might trigger early // Be on the safe side and accept 200ms above the original value - assert(rest - 200 < 500); + assert(rest - 200 < 500, 'Rest - 200 should be below 500 ms but is: ' + (rest - 200)); assert(delayed); end(); }); @@ -495,9 +498,9 @@ describe("connection tests", function () { }; client.on("ready", function () { var rest = Date.now() - time; - assert(rest >= 1000); + assert(rest >= 998, '`rest` should be equal or above 1000 ms but is: ' + rest); // setTimeout might trigger early // Be on the safe side and accept 200ms above the original value - assert(rest - 200 < 1000); + assert(rest - 200 < 1000, '`rest` - 200 should be below 1000 ms but is: ' + (rest - 200)); assert(delayed); end(); }); diff --git a/test/node_redis.spec.js b/test/node_redis.spec.js index 45e86a5773b..5bed67d40b1 100644 --- a/test/node_redis.spec.js +++ b/test/node_redis.spec.js @@ -591,6 +591,11 @@ describe("The node_redis client", function () { return done(); } }); + client.on('error', function (err) { + // This is rare but it might be triggered. + // So let's have a robust test + assert.strictEqual(err.code, 'ECONNRESET'); + }); }); }); From d858bd8383d51fb32d889b11a1aa91ae484844e3 Mon Sep 17 00:00:00 2001 From: Ruben Bridgewater Date: Thu, 10 Mar 2016 21:45:44 +0100 Subject: [PATCH 0581/1748] Passing a stream as second parameter in the constructor To support private streams this is implemented. This needs some monkey patching to work, so it is not officially supported and might be removed at any time! Fixes #950 Closes #951 --- index.js | 39 +++++++++++++++++++++++++++------------ test/connection.spec.js | 36 +++++++++++++++++++++++++++--------- 2 files changed, 54 insertions(+), 21 deletions(-) diff --git a/index.js b/index.js index b59e69d5e9a..5ded1abce65 100644 --- a/index.js +++ b/index.js @@ -34,13 +34,20 @@ function handle_detect_buffers_reply (reply, command, buffer_args) { exports.debug_mode = /\bredis\b/i.test(process.env.NODE_DEBUG); -function RedisClient (options) { +// Attention: The second parameter might be removed at will and is not officially supported. +// Do not rely on this +function RedisClient (options, stream) { // Copy the options so they are not mutated options = utils.clone(options); EventEmitter.call(this); var cnx_options = {}; var self = this; - if (options.path) { + if (stream) { + // The stream from the outside is used so no connection from this side is triggered but from the server this client should talk to + // Reconnect etc won't work with this. This requires monkey patching to work, so it is not officially supported + options.stream = stream; + this.address = '"Private stream"'; + } else if (options.path) { cnx_options.path = options.path; this.address = options.path; } else { @@ -174,17 +181,25 @@ RedisClient.connection_id = 0; RedisClient.prototype.create_stream = function () { var self = this; - // On a reconnect destroy the former stream and retry - if (this.stream) { - this.stream.removeAllListeners(); - this.stream.destroy(); - } - - /* istanbul ignore if: travis does not work with stunnel atm. Therefor the tls tests are skipped on travis */ - if (this.options.tls) { - this.stream = tls.connect(this.connection_options); + if (this.options.stream) { + // Only add the listeners once in case of a reconnect try (that won't work) + if (this.stream) { + return; + } + this.stream = this.options.stream; } else { - this.stream = net.createConnection(this.connection_options); + // On a reconnect destroy the former stream and retry + if (this.stream) { + this.stream.removeAllListeners(); + this.stream.destroy(); + } + + /* istanbul ignore if: travis does not work with stunnel atm. Therefor the tls tests are skipped on travis */ + if (this.options.tls) { + this.stream = tls.connect(this.connection_options); + } else { + this.stream = net.createConnection(this.connection_options); + } } if (this.options.connect_timeout) { diff --git a/test/connection.spec.js b/test/connection.spec.js index 3fb468ba404..59fc8213cf4 100644 --- a/test/connection.spec.js +++ b/test/connection.spec.js @@ -5,20 +5,38 @@ var config = require("./lib/config"); var helper = require('./helper'); var redis = config.redis; var intercept = require('intercept-stdout'); +var net = require('net'); +var client; describe("connection tests", function () { - helper.allTests(function(parser, ip, args) { - describe("using " + parser + " and " + ip, function () { + beforeEach(function () { + client = null; + }); + afterEach(function () { + client.end(true); + }); + + it('unofficially support for a private stream', function () { + // While using a private stream, reconnection and other features are not going to work properly. + // Besides that some functions also have to be monkey patched to be safe from errors in this case. + // Therefor this is not officially supported! + var socket = new net.Socket(); + client = new redis.RedisClient({ + prefix: 'test' + }, socket); + assert.strictEqual(client.stream, socket); + assert.strictEqual(client.stream.listeners('error').length, 1); + assert.strictEqual(client.address, '"Private stream"'); + // Pretent a reconnect event + client.create_stream(); + assert.strictEqual(client.stream, socket); + assert.strictEqual(client.stream.listeners('error').length, 1); + }); - var client; + helper.allTests(function(parser, ip, args) { - beforeEach(function () { - client = null; - }); - afterEach(function () { - client.end(true); - }); + describe("using " + parser + " and " + ip, function () { describe("on lost connection", function () { it("emit an error after max retry attempts and do not try to reconnect afterwards", function (done) { From f75b38a3e27add302db0de8d93b95732540fc77c Mon Sep 17 00:00:00 2001 From: Ruben Bridgewater Date: Mon, 14 Mar 2016 22:06:42 +0100 Subject: [PATCH 0582/1748] Make windows tests more robust --- test/connection.spec.js | 5 +++-- test/multi.spec.js | 2 +- test/node_redis.spec.js | 29 +++++++++++++---------------- 3 files changed, 17 insertions(+), 19 deletions(-) diff --git a/test/connection.spec.js b/test/connection.spec.js index 59fc8213cf4..65bcd0b6c85 100644 --- a/test/connection.spec.js +++ b/test/connection.spec.js @@ -211,7 +211,7 @@ describe("connection tests", function () { describe("when not connected", function () { it("emit an error after the socket timeout exceeded the connect_timeout time", function (done) { - var connect_timeout = 1000; // in ms + var connect_timeout = 500; // in ms var time = Date.now(); client = redis.createClient({ parser: parser, @@ -232,8 +232,9 @@ describe("connection tests", function () { client.on('error', function(err) { assert(/Redis connection in broken state: connection timeout.*?exceeded./.test(err.message)); // The code execution on windows is very slow at times + var add = process.platform !== 'win32' ? 25 : 125; var now = Date.now(); - assert(now - time < connect_timeout + 50, 'The real timeout time should be below ' + (connect_timeout + 50) + 'ms but is: ' + (now - time)); + assert(now - time < connect_timeout + add, 'The real timeout time should be below ' + (connect_timeout + add) + 'ms but is: ' + (now - time)); // Timers sometimes trigger early (e.g. 1ms to early) assert(now - time >= connect_timeout - 3, 'The real timeout time should be above ' + connect_timeout + 'ms, but it is: ' + (now - time)); done(); diff --git a/test/multi.spec.js b/test/multi.spec.js index bd6f498abe9..849071a7232 100644 --- a/test/multi.spec.js +++ b/test/multi.spec.js @@ -73,7 +73,7 @@ describe("The 'multi' method", function () { describe('pipeline limit', function () { it('do not exceed maximum string size', function (done) { - this.timeout(25000); // Windows tests are horribly slow + this.timeout(30000); // Windows tests are horribly slow // Triggers a RangeError: Invalid string length if not handled properly client = redis.createClient(); var multi = client.multi(); diff --git a/test/node_redis.spec.js b/test/node_redis.spec.js index 5bed67d40b1..09d64a8d025 100644 --- a/test/node_redis.spec.js +++ b/test/node_redis.spec.js @@ -569,28 +569,25 @@ describe("The node_redis client", function () { }); describe('retry_max_delay', function () { - var args = config.configureClient(parser, ip, { - retry_max_delay: 1 // ms - }); - it("sets upper bound on how long client waits before reconnecting", function (done) { - var time = new Date().getTime(); - var reconnecting = false; + var time; + var timeout = process.platform !== 'win32' ? 20 : 100; - client = redis.createClient.apply(redis.createClient, args); + client = redis.createClient.apply(null, config.configureClient(parser, ip, { + retry_max_delay: 1 // ms + })); client.on('ready', function() { - if (!reconnecting) { - reconnecting = true; - client.retry_delay = 1000; - client.retry_backoff = 1; - client.stream.end(); + if (this.times_connected === 1) { + this.stream.end(); + time = Date.now(); } else { - client.end(true); - var lasted = new Date().getTime() - time; - assert.ok(lasted < 100); - return done(); + done(); } }); + client.on('reconnecting', function () { + time = Date.now() - time; + assert(time < timeout, 'The reconnect should not have taken longer than ' + timeout + ' but it took ' + time); + }); client.on('error', function (err) { // This is rare but it might be triggered. // So let's have a robust test From ff19663d9d2dc3deb355d595aa379e8a060d97aa Mon Sep 17 00:00:00 2001 From: Ruben Bridgewater Date: Mon, 14 Mar 2016 22:31:59 +0100 Subject: [PATCH 0583/1748] Remove code overhead Add another domain test Fix test on node 0.10 --- test/auth.spec.js | 24 ++++++++++----------- test/batch.spec.js | 8 +++---- test/commands/blpop.spec.js | 10 ++++----- test/commands/client.spec.js | 2 +- test/commands/dbsize.spec.js | 8 +++---- test/commands/del.spec.js | 2 +- test/commands/eval.spec.js | 2 +- test/commands/exists.spec.js | 2 +- test/commands/expire.spec.js | 2 +- test/commands/flushdb.spec.js | 8 +++---- test/commands/geoadd.spec.js | 2 +- test/commands/get.spec.js | 8 +++---- test/commands/getset.spec.js | 8 +++---- test/commands/hgetall.spec.js | 4 ++-- test/commands/hincrby.spec.js | 2 +- test/commands/hlen.spec.js | 2 +- test/commands/hmget.spec.js | 2 +- test/commands/hmset.spec.js | 2 +- test/commands/hset.spec.js | 2 +- test/commands/incr.spec.js | 8 +++---- test/commands/info.spec.js | 2 +- test/commands/keys.spec.js | 2 +- test/commands/mget.spec.js | 2 +- test/commands/mset.spec.js | 8 +++---- test/commands/msetnx.spec.js | 2 +- test/commands/randomkey.test.js | 2 +- test/commands/rename.spec.js | 2 +- test/commands/renamenx.spec.js | 2 +- test/commands/rpush.spec.js | 2 +- test/commands/sadd.spec.js | 2 +- test/commands/scard.spec.js | 2 +- test/commands/script.spec.js | 2 +- test/commands/sdiff.spec.js | 2 +- test/commands/sdiffstore.spec.js | 2 +- test/commands/select.spec.js | 8 +++---- test/commands/set.spec.js | 8 +++---- test/commands/setex.spec.js | 2 +- test/commands/setnx.spec.js | 2 +- test/commands/sinter.spec.js | 2 +- test/commands/sinterstore.spec.js | 2 +- test/commands/sismember.spec.js | 2 +- test/commands/slowlog.spec.js | 2 +- test/commands/smembers.spec.js | 2 +- test/commands/smove.spec.js | 2 +- test/commands/sort.spec.js | 2 +- test/commands/spop.spec.js | 2 +- test/commands/srem.spec.js | 2 +- test/commands/sunion.spec.js | 2 +- test/commands/sunionstore.spec.js | 2 +- test/commands/ttl.spec.js | 2 +- test/commands/type.spec.js | 2 +- test/commands/watch.spec.js | 2 +- test/commands/zadd.spec.js | 2 +- test/commands/zscan.spec.js | 2 +- test/commands/zscore.spec.js | 2 +- test/connection.spec.js | 10 ++++----- test/detect_buffers.spec.js | 2 +- test/multi.spec.js | 10 ++++----- test/node_redis.spec.js | 36 ++++++++++++++++++++++--------- test/return_buffers.spec.js | 2 +- test/tls.spec.js | 2 +- 61 files changed, 127 insertions(+), 131 deletions(-) diff --git a/test/auth.spec.js b/test/auth.spec.js index 61e185fc859..f09cef6f98d 100644 --- a/test/auth.spec.js +++ b/test/auth.spec.js @@ -37,7 +37,7 @@ describe("client authentication", function () { it("allows auth to be provided with 'auth' method", function (done) { if (helper.redisProcess().spawnFailed()) this.skip(); - client = redis.createClient.apply(redis.createClient, args); + client = redis.createClient.apply(null, args); client.auth(auth, function (err, res) { assert.strictEqual(null, err); assert.strictEqual("OK", res.toString()); @@ -48,7 +48,7 @@ describe("client authentication", function () { it("emits error when auth is bad without callback", function (done) { if (helper.redisProcess().spawnFailed()) this.skip(); - client = redis.createClient.apply(redis.createClient, args); + client = redis.createClient.apply(null, args); client.once('error', function (err) { assert.strictEqual(err.command, 'AUTH'); @@ -62,7 +62,7 @@ describe("client authentication", function () { it("returns an error when auth is bad (empty string) with a callback", function (done) { if (helper.redisProcess().spawnFailed()) this.skip(); - client = redis.createClient.apply(redis.createClient, args); + client = redis.createClient.apply(null, args); client.auth('', function (err, res) { assert.strictEqual(err.command, 'AUTH'); @@ -114,7 +114,7 @@ describe("client authentication", function () { var args = config.configureClient(parser, ip, { auth_pass: auth }); - client = redis.createClient.apply(redis.createClient, args); + client = redis.createClient.apply(null, args); client.on("ready", function () { return done(); }); @@ -127,7 +127,7 @@ describe("client authentication", function () { password: auth, no_ready_check: true }); - client = redis.createClient.apply(redis.createClient, args); + client = redis.createClient.apply(null, args); client.on("ready", function () { done(); }); @@ -137,7 +137,7 @@ describe("client authentication", function () { if (helper.redisProcess().spawnFailed()) this.skip(); var args = config.configureClient(parser, ip); - client = redis.createClient.apply(redis.createClient, args); + client = redis.createClient.apply(null, args); client.auth(auth); client.on("ready", function () { return done(); @@ -148,7 +148,7 @@ describe("client authentication", function () { if (helper.redisProcess().spawnFailed()) this.skip(); var readyCount = 0; - client = redis.createClient.apply(redis.createClient, args); + client = redis.createClient.apply(null, args); client.auth(auth); client.on("ready", function () { readyCount++; @@ -163,7 +163,7 @@ describe("client authentication", function () { it('should return an error if the password is not of type string and a callback has been provided', function (done) { if (helper.redisProcess().spawnFailed()) this.skip(); - client = redis.createClient.apply(redis.createClient, args); + client = redis.createClient.apply(null, args); var async = true; client.auth(undefined, function(err, res) { assert.strictEqual(err.message, 'ERR invalid password'); @@ -178,7 +178,7 @@ describe("client authentication", function () { it('should emit an error if the password is not of type string and no callback has been provided', function (done) { if (helper.redisProcess().spawnFailed()) this.skip(); - client = redis.createClient.apply(redis.createClient, args); + client = redis.createClient.apply(null, args); client.on('error', function (err) { assert.strictEqual(err.message, 'ERR invalid password'); assert.strictEqual(err.command, 'AUTH'); @@ -193,7 +193,7 @@ describe("client authentication", function () { var args = config.configureClient(parser, ip, { auth_pass: auth }); - client = redis.createClient.apply(redis.createClient, args); + client = redis.createClient.apply(null, args); client.on("ready", function () { client.auth(auth, helper.isString('OK', done)); }); @@ -205,7 +205,7 @@ describe("client authentication", function () { var args = config.configureClient(parser, ip, { no_ready_check: true }); - client = redis.createClient.apply(redis.createClient, args); + client = redis.createClient.apply(null, args); client.on("ready", function () { client.set('foo', 'bar', function (err, res) { assert.equal(err.message, 'NOAUTH Authentication required.'); @@ -218,7 +218,7 @@ describe("client authentication", function () { it('does not allow auth to be provided post-hoc with auth method if not authenticated before', function (done) { if (helper.redisProcess().spawnFailed()) this.skip(); - client = redis.createClient.apply(redis.createClient, args); + client = redis.createClient.apply(null, args); client.on("error", function (err) { assert.equal(err.code, 'NOAUTH'); assert.equal(err.message, 'Ready check failed: NOAUTH Authentication required.'); diff --git a/test/batch.spec.js b/test/batch.spec.js index ba4c9020bba..5a1ace4e7da 100644 --- a/test/batch.spec.js +++ b/test/batch.spec.js @@ -22,13 +22,11 @@ describe("The 'batch' method", function () { var client; beforeEach(function (done) { - client = redis.createClient.apply(redis.createClient, args); + client = redis.createClient.apply(null, args); client.once("connect", function () { client.quit(); }); - client.on('end', function () { - return done(); - }); + client.on('end', done); }); it("returns an empty array", function (done) { @@ -51,7 +49,7 @@ describe("The 'batch' method", function () { var client; beforeEach(function (done) { - client = redis.createClient.apply(redis.createClient, args); + client = redis.createClient.apply(null, args); client.once("ready", function () { client.flushdb(function (err) { return done(err); diff --git a/test/commands/blpop.spec.js b/test/commands/blpop.spec.js index 4105dcd9c5b..bf081233b89 100644 --- a/test/commands/blpop.spec.js +++ b/test/commands/blpop.spec.js @@ -15,14 +15,14 @@ describe("The 'blpop' method", function () { var bclient; beforeEach(function (done) { - client = redis.createClient.apply(redis.createClient, args); + client = redis.createClient.apply(null, args); client.once("ready", function () { client.flushdb(done); }); }); it('pops value immediately if list contains values', function (done) { - bclient = redis.createClient.apply(redis.createClient, args); + bclient = redis.createClient.apply(null, args); redis.debug_mode = true; var text = ''; var unhookIntercept = intercept(function(data) { @@ -41,7 +41,7 @@ describe("The 'blpop' method", function () { }); it('pops value immediately if list contains values using array notation', function (done) { - bclient = redis.createClient.apply(redis.createClient, args); + bclient = redis.createClient.apply(null, args); client.rpush(["blocking list", "initial value"], helper.isNumber(1)); bclient.blpop(["blocking list", 0], function (err, value) { assert.strictEqual(value[0], "blocking list"); @@ -51,7 +51,7 @@ describe("The 'blpop' method", function () { }); it('waits for value if list is not yet populated', function (done) { - bclient = redis.createClient.apply(redis.createClient, args); + bclient = redis.createClient.apply(null, args); bclient.blpop("blocking list 2", 5, function (err, value) { assert.strictEqual(value[0], "blocking list 2"); assert.strictEqual(value[1], "initial value"); @@ -61,7 +61,7 @@ describe("The 'blpop' method", function () { }); it('times out after specified time', function (done) { - bclient = redis.createClient.apply(redis.createClient, args); + bclient = redis.createClient.apply(null, args); bclient.BLPOP("blocking list", 1, function (err, res) { assert.strictEqual(res, null); return done(err); diff --git a/test/commands/client.spec.js b/test/commands/client.spec.js index f0f1f205adc..661f70d0be2 100644 --- a/test/commands/client.spec.js +++ b/test/commands/client.spec.js @@ -14,7 +14,7 @@ describe("The 'client' method", function () { var client; beforeEach(function (done) { - client = redis.createClient.apply(redis.createClient, args); + client = redis.createClient.apply(null, args); client.once("ready", function () { client.flushdb(done); }); diff --git a/test/commands/dbsize.spec.js b/test/commands/dbsize.spec.js index aa1fb57fcff..0bc922c2f39 100644 --- a/test/commands/dbsize.spec.js +++ b/test/commands/dbsize.spec.js @@ -22,13 +22,11 @@ describe("The 'dbsize' method", function () { var client; beforeEach(function (done) { - client = redis.createClient.apply(redis.createClient, args); + client = redis.createClient.apply(null, args); client.once("ready", function () { client.quit(); }); - client.on('end', function () { - return done(); - }); + client.on('end', done); }); it("reports an error", function (done) { @@ -43,7 +41,7 @@ describe("The 'dbsize' method", function () { var client; beforeEach(function (done) { - client = redis.createClient.apply(redis.createClient, args); + client = redis.createClient.apply(null, args); client.once("ready", function () { client.flushdb(function (err, res) { helper.isString("OK")(err, res); diff --git a/test/commands/del.spec.js b/test/commands/del.spec.js index d5dc8fbb64d..99f3e8fcaea 100644 --- a/test/commands/del.spec.js +++ b/test/commands/del.spec.js @@ -12,7 +12,7 @@ describe("The 'del' method", function () { var client; beforeEach(function (done) { - client = redis.createClient.apply(redis.createClient, args); + client = redis.createClient.apply(null, args); client.once("ready", function () { client.flushdb(done); }); diff --git a/test/commands/eval.spec.js b/test/commands/eval.spec.js index d091a4dbd73..2fdd7857d5d 100644 --- a/test/commands/eval.spec.js +++ b/test/commands/eval.spec.js @@ -15,7 +15,7 @@ describe("The 'eval' method", function () { var source = "return redis.call('set', 'sha', 'test')"; beforeEach(function (done) { - client = redis.createClient.apply(redis.createClient, args); + client = redis.createClient.apply(null, args); client.once("ready", function () { client.flushdb(done); }); diff --git a/test/commands/exists.spec.js b/test/commands/exists.spec.js index 080e1d1e65c..7135c10553a 100644 --- a/test/commands/exists.spec.js +++ b/test/commands/exists.spec.js @@ -12,7 +12,7 @@ describe("The 'exists' method", function () { var client; beforeEach(function (done) { - client = redis.createClient.apply(redis.createClient, args); + client = redis.createClient.apply(null, args); client.once("ready", function () { client.flushdb(done); }); diff --git a/test/commands/expire.spec.js b/test/commands/expire.spec.js index 99da8e007bb..679b152f5a2 100644 --- a/test/commands/expire.spec.js +++ b/test/commands/expire.spec.js @@ -12,7 +12,7 @@ describe("The 'expire' method", function () { var client; beforeEach(function (done) { - client = redis.createClient.apply(redis.createClient, args); + client = redis.createClient.apply(null, args); client.once("ready", function () { client.flushdb(done); }); diff --git a/test/commands/flushdb.spec.js b/test/commands/flushdb.spec.js index e1c073c0f27..97e46e36e1a 100644 --- a/test/commands/flushdb.spec.js +++ b/test/commands/flushdb.spec.js @@ -22,13 +22,11 @@ describe("The 'flushdb' method", function () { var client; beforeEach(function (done) { - client = redis.createClient.apply(redis.createClient, args); + client = redis.createClient.apply(null, args); client.once("ready", function () { client.quit(); }); - client.on('end', function () { - return done(); - }); + client.on('end', done); }); it("reports an error", function (done) { @@ -43,7 +41,7 @@ describe("The 'flushdb' method", function () { var client; beforeEach(function (done) { - client = redis.createClient.apply(redis.createClient, args); + client = redis.createClient.apply(null, args); client.once("ready", function () { done(); }); diff --git a/test/commands/geoadd.spec.js b/test/commands/geoadd.spec.js index 70ba278a6b3..026d0fe7174 100644 --- a/test/commands/geoadd.spec.js +++ b/test/commands/geoadd.spec.js @@ -12,7 +12,7 @@ describe("The 'geoadd' method", function () { var client; beforeEach(function (done) { - client = redis.createClient.apply(redis.createClient, args); + client = redis.createClient.apply(null, args); client.once("ready", function () { client.flushdb(done); }); diff --git a/test/commands/get.spec.js b/test/commands/get.spec.js index 7f1f0cdf4c6..cc4a7386a12 100644 --- a/test/commands/get.spec.js +++ b/test/commands/get.spec.js @@ -22,13 +22,11 @@ describe("The 'get' method", function () { var client; beforeEach(function (done) { - client = redis.createClient.apply(redis.createClient, args); + client = redis.createClient.apply(null, args); client.once("ready", function () { client.quit(); }); - client.on('end', function () { - return done(); - }); + client.on('end', done); }); it("reports an error", function (done) { @@ -49,7 +47,7 @@ describe("The 'get' method", function () { var client; beforeEach(function (done) { - client = redis.createClient.apply(redis.createClient, args); + client = redis.createClient.apply(null, args); client.once("ready", function () { done(); }); diff --git a/test/commands/getset.spec.js b/test/commands/getset.spec.js index b39b028413d..0ebbb12f157 100644 --- a/test/commands/getset.spec.js +++ b/test/commands/getset.spec.js @@ -23,13 +23,11 @@ describe("The 'getset' method", function () { var client; beforeEach(function (done) { - client = redis.createClient.apply(redis.createClient, args); + client = redis.createClient.apply(null, args); client.once("ready", function () { client.quit(); }); - client.on('end', function () { - return done(); - }); + client.on('end', done); }); it("reports an error", function (done) { @@ -44,7 +42,7 @@ describe("The 'getset' method", function () { var client; beforeEach(function (done) { - client = redis.createClient.apply(redis.createClient, args); + client = redis.createClient.apply(null, args); client.once("ready", function () { done(); }); diff --git a/test/commands/hgetall.spec.js b/test/commands/hgetall.spec.js index d531703eebe..0195ba5f658 100644 --- a/test/commands/hgetall.spec.js +++ b/test/commands/hgetall.spec.js @@ -15,7 +15,7 @@ describe("The 'hgetall' method", function () { describe('regular client', function () { beforeEach(function (done) { - client = redis.createClient.apply(redis.createClient, args); + client = redis.createClient.apply(null, args); client.once("ready", function () { client.flushdb(done); }); @@ -56,7 +56,7 @@ describe("The 'hgetall' method", function () { }); beforeEach(function (done) { - client = redis.createClient.apply(redis.createClient, args); + client = redis.createClient.apply(null, args); client.once("ready", function () { client.flushdb(done); }); diff --git a/test/commands/hincrby.spec.js b/test/commands/hincrby.spec.js index d2d03c95467..5e933d23ce3 100644 --- a/test/commands/hincrby.spec.js +++ b/test/commands/hincrby.spec.js @@ -13,7 +13,7 @@ describe("The 'hincrby' method", function () { var hash = "test hash"; beforeEach(function (done) { - client = redis.createClient.apply(redis.createClient, args); + client = redis.createClient.apply(null, args); client.once("ready", function () { client.flushdb(done); }); diff --git a/test/commands/hlen.spec.js b/test/commands/hlen.spec.js index e366582b917..b8fa3e99c6e 100644 --- a/test/commands/hlen.spec.js +++ b/test/commands/hlen.spec.js @@ -12,7 +12,7 @@ describe("The 'hlen' method", function () { var client; beforeEach(function (done) { - client = redis.createClient.apply(redis.createClient, args); + client = redis.createClient.apply(null, args); client.once("ready", function () { client.flushdb(done); }); diff --git a/test/commands/hmget.spec.js b/test/commands/hmget.spec.js index 4e90dd40a01..996ed255709 100644 --- a/test/commands/hmget.spec.js +++ b/test/commands/hmget.spec.js @@ -14,7 +14,7 @@ describe("The 'hmget' method", function () { var hash = 'test hash'; beforeEach(function (done) { - client = redis.createClient.apply(redis.createClient, args); + client = redis.createClient.apply(null, args); client.once("error", done); client.once("ready", function () { client.flushdb(); diff --git a/test/commands/hmset.spec.js b/test/commands/hmset.spec.js index 2dbc62e7fc6..e6f99312364 100644 --- a/test/commands/hmset.spec.js +++ b/test/commands/hmset.spec.js @@ -14,7 +14,7 @@ describe("The 'hmset' method", function () { var hash = 'test hash'; beforeEach(function (done) { - client = redis.createClient.apply(redis.createClient, args); + client = redis.createClient.apply(null, args); client.once("ready", function () { client.flushdb(done); }); diff --git a/test/commands/hset.spec.js b/test/commands/hset.spec.js index 2d0932476ac..bd5415189c4 100644 --- a/test/commands/hset.spec.js +++ b/test/commands/hset.spec.js @@ -14,7 +14,7 @@ describe("The 'hset' method", function () { var hash = 'test hash'; beforeEach(function (done) { - client = redis.createClient.apply(redis.createClient, args); + client = redis.createClient.apply(null, args); client.once("ready", function () { client.flushdb(done); }); diff --git a/test/commands/incr.spec.js b/test/commands/incr.spec.js index 1910bd2943f..b0d605ae221 100644 --- a/test/commands/incr.spec.js +++ b/test/commands/incr.spec.js @@ -16,16 +16,14 @@ describe("The 'incr' method", function () { var client; beforeEach(function (done) { - client = redis.createClient.apply(redis.createClient, args); + client = redis.createClient.apply(null, args); client.once("ready", function () { client.set(key, "9007199254740992", function (err, res) { helper.isNotError()(err, res); client.quit(); }); }); - client.on('end', function () { - return done(); - }); + client.on('end', done); }); afterEach(function () { @@ -52,7 +50,7 @@ describe("The 'incr' method", function () { 9007199254740996 -> 9007199254740996 9007199254740997 -> 9007199254740996 */ - client = redis.createClient.apply(redis.createClient, args); + client = redis.createClient.apply(null, args); client.once("error", done); client.once("ready", function () { client.set(key, "9007199254740992", function (err, res) { diff --git a/test/commands/info.spec.js b/test/commands/info.spec.js index 9ce968051f0..64daf7acdfa 100644 --- a/test/commands/info.spec.js +++ b/test/commands/info.spec.js @@ -13,7 +13,7 @@ describe("The 'info' method", function () { var client; before(function (done) { - client = redis.createClient.apply(redis.createClient, args); + client = redis.createClient.apply(null, args); client.once("ready", function () { client.flushall(done); }); diff --git a/test/commands/keys.spec.js b/test/commands/keys.spec.js index 5522c2dfaa8..7af3ae40c97 100644 --- a/test/commands/keys.spec.js +++ b/test/commands/keys.spec.js @@ -14,7 +14,7 @@ describe("The 'keys' method", function () { var client; beforeEach(function (done) { - client = redis.createClient.apply(redis.createClient, args); + client = redis.createClient.apply(null, args); client.once("ready", function () { client.flushall(done); }); diff --git a/test/commands/mget.spec.js b/test/commands/mget.spec.js index 0907558e56b..8e395dedbe7 100644 --- a/test/commands/mget.spec.js +++ b/test/commands/mget.spec.js @@ -13,7 +13,7 @@ describe("The 'mget' method", function () { var client; beforeEach(function (done) { - client = redis.createClient.apply(redis.createClient, args); + client = redis.createClient.apply(null, args); client.once("error", done); client.once("ready", function () { client.flushdb(); diff --git a/test/commands/mset.spec.js b/test/commands/mset.spec.js index c5d754fc6fe..81ab6d3bc67 100644 --- a/test/commands/mset.spec.js +++ b/test/commands/mset.spec.js @@ -24,13 +24,11 @@ describe("The 'mset' method", function () { var client; beforeEach(function (done) { - client = redis.createClient.apply(redis.createClient, args); + client = redis.createClient.apply(null, args); client.once("ready", function () { client.quit(); }); - client.on('end', function () { - return done(); - }); + client.on('end', done); }); it("reports an error", function (done) { @@ -45,7 +43,7 @@ describe("The 'mset' method", function () { var client; beforeEach(function (done) { - client = redis.createClient.apply(redis.createClient, args); + client = redis.createClient.apply(null, args); client.once("ready", function () { done(); }); diff --git a/test/commands/msetnx.spec.js b/test/commands/msetnx.spec.js index e3f1d3afaad..59cd62187c5 100644 --- a/test/commands/msetnx.spec.js +++ b/test/commands/msetnx.spec.js @@ -12,7 +12,7 @@ describe("The 'msetnx' method", function () { var client; beforeEach(function (done) { - client = redis.createClient.apply(redis.createClient, args); + client = redis.createClient.apply(null, args); client.once("ready", function () { client.flushdb(done); }); diff --git a/test/commands/randomkey.test.js b/test/commands/randomkey.test.js index 72fd2fda881..671942effed 100644 --- a/test/commands/randomkey.test.js +++ b/test/commands/randomkey.test.js @@ -13,7 +13,7 @@ describe("The 'randomkey' method", function () { var client; beforeEach(function (done) { - client = redis.createClient.apply(redis.createClient, args); + client = redis.createClient.apply(null, args); client.once("ready", function () { client.flushdb(done); }); diff --git a/test/commands/rename.spec.js b/test/commands/rename.spec.js index dc1de1e3afb..e6d8b938a10 100644 --- a/test/commands/rename.spec.js +++ b/test/commands/rename.spec.js @@ -12,7 +12,7 @@ describe("The 'rename' method", function () { var client; beforeEach(function (done) { - client = redis.createClient.apply(redis.createClient, args); + client = redis.createClient.apply(null, args); client.once("ready", function () { client.flushdb(done); }); diff --git a/test/commands/renamenx.spec.js b/test/commands/renamenx.spec.js index bd4e8232350..ef503c96fb5 100644 --- a/test/commands/renamenx.spec.js +++ b/test/commands/renamenx.spec.js @@ -12,7 +12,7 @@ describe("The 'renamenx' method", function () { var client; beforeEach(function (done) { - client = redis.createClient.apply(redis.createClient, args); + client = redis.createClient.apply(null, args); client.once("ready", function () { client.flushdb(done); }); diff --git a/test/commands/rpush.spec.js b/test/commands/rpush.spec.js index b6c1562b800..59d776cbcc7 100644 --- a/test/commands/rpush.spec.js +++ b/test/commands/rpush.spec.js @@ -13,7 +13,7 @@ describe("The 'rpush' command", function () { var client; beforeEach(function (done) { - client = redis.createClient.apply(redis.createClient, args); + client = redis.createClient.apply(null, args); client.once("ready", function () { client.flushdb(done); }); diff --git a/test/commands/sadd.spec.js b/test/commands/sadd.spec.js index 504dc47541e..6333d940ff2 100644 --- a/test/commands/sadd.spec.js +++ b/test/commands/sadd.spec.js @@ -13,7 +13,7 @@ describe("The 'sadd' method", function () { var client; beforeEach(function (done) { - client = redis.createClient.apply(redis.createClient, args); + client = redis.createClient.apply(null, args); client.once("ready", function () { client.flushdb(done); }); diff --git a/test/commands/scard.spec.js b/test/commands/scard.spec.js index 8c8eb70d1f1..162513b4ed8 100644 --- a/test/commands/scard.spec.js +++ b/test/commands/scard.spec.js @@ -12,7 +12,7 @@ describe("The 'scard' method", function () { var client; beforeEach(function (done) { - client = redis.createClient.apply(redis.createClient, args); + client = redis.createClient.apply(null, args); client.once("ready", function () { client.flushdb(done); }); diff --git a/test/commands/script.spec.js b/test/commands/script.spec.js index e7eecc172b0..e69f25025ea 100644 --- a/test/commands/script.spec.js +++ b/test/commands/script.spec.js @@ -16,7 +16,7 @@ describe("The 'script' method", function () { var client; beforeEach(function (done) { - client = redis.createClient.apply(redis.createClient, args); + client = redis.createClient.apply(null, args); client.once("ready", function () { client.flushdb(done); }); diff --git a/test/commands/sdiff.spec.js b/test/commands/sdiff.spec.js index 7121b1e95f7..4a9e8c651b7 100644 --- a/test/commands/sdiff.spec.js +++ b/test/commands/sdiff.spec.js @@ -13,7 +13,7 @@ describe("The 'sdiff' method", function () { var client; beforeEach(function (done) { - client = redis.createClient.apply(redis.createClient, args); + client = redis.createClient.apply(null, args); client.once("ready", function () { client.flushdb(done); }); diff --git a/test/commands/sdiffstore.spec.js b/test/commands/sdiffstore.spec.js index 1963317a708..eb8fb79952a 100644 --- a/test/commands/sdiffstore.spec.js +++ b/test/commands/sdiffstore.spec.js @@ -13,7 +13,7 @@ describe("The 'sdiffstore' method", function () { var client; beforeEach(function (done) { - client = redis.createClient.apply(redis.createClient, args); + client = redis.createClient.apply(null, args); client.once("ready", function () { client.flushdb(done); }); diff --git a/test/commands/select.spec.js b/test/commands/select.spec.js index b3ef6d58932..2d0ac3a1046 100644 --- a/test/commands/select.spec.js +++ b/test/commands/select.spec.js @@ -14,13 +14,11 @@ describe("The 'select' method", function () { var client; beforeEach(function (done) { - client = redis.createClient.apply(redis.createClient, args); + client = redis.createClient.apply(null, args); client.once("ready", function () { client.quit(); }); - client.on('end', function () { - return done(); - }); + client.on('end', done); }); it("returns an error if redis is not connected", function (done) { @@ -36,7 +34,7 @@ describe("The 'select' method", function () { var client; beforeEach(function (done) { - client = redis.createClient.apply(redis.createClient, args); + client = redis.createClient.apply(null, args); client.once("ready", function () { client.flushdb(done); }); diff --git a/test/commands/set.spec.js b/test/commands/set.spec.js index 3235d634f64..8cf6fe6cd1b 100644 --- a/test/commands/set.spec.js +++ b/test/commands/set.spec.js @@ -22,13 +22,11 @@ describe("The 'set' method", function () { var client; beforeEach(function (done) { - client = redis.createClient.apply(redis.createClient, args); + client = redis.createClient.apply(null, args); client.once("ready", function () { client.quit(); }); - client.on('end', function () { - return done(); - }); + client.on('end', done); }); it("reports an error", function (done) { @@ -43,7 +41,7 @@ describe("The 'set' method", function () { var client; beforeEach(function (done) { - client = redis.createClient.apply(redis.createClient, args); + client = redis.createClient.apply(null, args); client.once("ready", function () { done(); }); diff --git a/test/commands/setex.spec.js b/test/commands/setex.spec.js index 575c7e854d5..72019ae926b 100644 --- a/test/commands/setex.spec.js +++ b/test/commands/setex.spec.js @@ -13,7 +13,7 @@ describe("The 'setex' method", function () { var client; beforeEach(function (done) { - client = redis.createClient.apply(redis.createClient, args); + client = redis.createClient.apply(null, args); client.once("ready", function () { client.flushdb(done); }); diff --git a/test/commands/setnx.spec.js b/test/commands/setnx.spec.js index d72c36696eb..5468463b0af 100644 --- a/test/commands/setnx.spec.js +++ b/test/commands/setnx.spec.js @@ -12,7 +12,7 @@ describe("The 'setnx' method", function () { var client; beforeEach(function (done) { - client = redis.createClient.apply(redis.createClient, args); + client = redis.createClient.apply(null, args); client.once("ready", function () { client.flushdb(done); }); diff --git a/test/commands/sinter.spec.js b/test/commands/sinter.spec.js index 8e547923dbc..bd7c934fdc0 100644 --- a/test/commands/sinter.spec.js +++ b/test/commands/sinter.spec.js @@ -13,7 +13,7 @@ describe("The 'sinter' method", function () { var client; beforeEach(function (done) { - client = redis.createClient.apply(redis.createClient, args); + client = redis.createClient.apply(null, args); client.once("ready", function () { client.flushdb(done); }); diff --git a/test/commands/sinterstore.spec.js b/test/commands/sinterstore.spec.js index d4a0c228a49..434bab9a600 100644 --- a/test/commands/sinterstore.spec.js +++ b/test/commands/sinterstore.spec.js @@ -13,7 +13,7 @@ describe("The 'sinterstore' method", function () { var client; beforeEach(function (done) { - client = redis.createClient.apply(redis.createClient, args); + client = redis.createClient.apply(null, args); client.once("ready", function () { client.flushdb(done); }); diff --git a/test/commands/sismember.spec.js b/test/commands/sismember.spec.js index 8f556e741f4..0ba5af64023 100644 --- a/test/commands/sismember.spec.js +++ b/test/commands/sismember.spec.js @@ -12,7 +12,7 @@ describe("The 'sismember' method", function () { var client; beforeEach(function (done) { - client = redis.createClient.apply(redis.createClient, args); + client = redis.createClient.apply(null, args); client.once("ready", function () { client.flushdb(done); }); diff --git a/test/commands/slowlog.spec.js b/test/commands/slowlog.spec.js index b297a985f71..e381ca28cf0 100644 --- a/test/commands/slowlog.spec.js +++ b/test/commands/slowlog.spec.js @@ -13,7 +13,7 @@ describe("The 'slowlog' method", function () { var client; beforeEach(function (done) { - client = redis.createClient.apply(redis.createClient, args); + client = redis.createClient.apply(null, args); client.once("ready", function () { client.flushdb(done); }); diff --git a/test/commands/smembers.spec.js b/test/commands/smembers.spec.js index c19efc2c81d..aab09895798 100644 --- a/test/commands/smembers.spec.js +++ b/test/commands/smembers.spec.js @@ -13,7 +13,7 @@ describe("The 'smembers' method", function () { var client; beforeEach(function (done) { - client = redis.createClient.apply(redis.createClient, args); + client = redis.createClient.apply(null, args); client.once("ready", function () { client.flushdb(done); }); diff --git a/test/commands/smove.spec.js b/test/commands/smove.spec.js index 436069be6a9..ca2c6ae2561 100644 --- a/test/commands/smove.spec.js +++ b/test/commands/smove.spec.js @@ -12,7 +12,7 @@ describe("The 'smove' method", function () { var client; beforeEach(function (done) { - client = redis.createClient.apply(redis.createClient, args); + client = redis.createClient.apply(null, args); client.once("ready", function () { client.flushdb(done); }); diff --git a/test/commands/sort.spec.js b/test/commands/sort.spec.js index 28355d9fe87..1a72664d9ea 100644 --- a/test/commands/sort.spec.js +++ b/test/commands/sort.spec.js @@ -40,7 +40,7 @@ describe("The 'sort' method", function () { var client; beforeEach(function (done) { - client = redis.createClient.apply(redis.createClient, args); + client = redis.createClient.apply(null, args); client.once("error", done); client.once("connect", function () { client.flushdb(); diff --git a/test/commands/spop.spec.js b/test/commands/spop.spec.js index b44d9b172bf..224869e5b66 100644 --- a/test/commands/spop.spec.js +++ b/test/commands/spop.spec.js @@ -13,7 +13,7 @@ describe("The 'spop' method", function () { var client; beforeEach(function (done) { - client = redis.createClient.apply(redis.createClient, args); + client = redis.createClient.apply(null, args); client.once("ready", function () { client.flushdb(done); }); diff --git a/test/commands/srem.spec.js b/test/commands/srem.spec.js index dc79134d634..7db8b6a8471 100644 --- a/test/commands/srem.spec.js +++ b/test/commands/srem.spec.js @@ -13,7 +13,7 @@ describe("The 'srem' method", function () { var client; beforeEach(function (done) { - client = redis.createClient.apply(redis.createClient, args); + client = redis.createClient.apply(null, args); client.once("ready", function () { client.flushdb(done); }); diff --git a/test/commands/sunion.spec.js b/test/commands/sunion.spec.js index deb751b1afc..835c258f215 100644 --- a/test/commands/sunion.spec.js +++ b/test/commands/sunion.spec.js @@ -13,7 +13,7 @@ describe("The 'sunion' method", function () { var client; beforeEach(function (done) { - client = redis.createClient.apply(redis.createClient, args); + client = redis.createClient.apply(null, args); client.once("ready", function () { client.flushdb(done); }); diff --git a/test/commands/sunionstore.spec.js b/test/commands/sunionstore.spec.js index 54cdddf52ea..cbef9084fe7 100644 --- a/test/commands/sunionstore.spec.js +++ b/test/commands/sunionstore.spec.js @@ -13,7 +13,7 @@ describe("The 'sunionstore' method", function () { var client; beforeEach(function (done) { - client = redis.createClient.apply(redis.createClient, args); + client = redis.createClient.apply(null, args); client.once("ready", function () { client.flushdb(done); }); diff --git a/test/commands/ttl.spec.js b/test/commands/ttl.spec.js index 1df47cfe311..3448fcf1357 100644 --- a/test/commands/ttl.spec.js +++ b/test/commands/ttl.spec.js @@ -13,7 +13,7 @@ describe("The 'ttl' method", function () { var client; beforeEach(function (done) { - client = redis.createClient.apply(redis.createClient, args); + client = redis.createClient.apply(null, args); client.once("ready", function () { client.flushdb(done); }); diff --git a/test/commands/type.spec.js b/test/commands/type.spec.js index 5e592054d08..b6336153efd 100644 --- a/test/commands/type.spec.js +++ b/test/commands/type.spec.js @@ -12,7 +12,7 @@ describe("The 'type' method", function () { var client; beforeEach(function (done) { - client = redis.createClient.apply(redis.createClient, args); + client = redis.createClient.apply(null, args); client.once("ready", function () { client.flushdb(done); }); diff --git a/test/commands/watch.spec.js b/test/commands/watch.spec.js index 3cc28334f87..7214c9c8f34 100644 --- a/test/commands/watch.spec.js +++ b/test/commands/watch.spec.js @@ -15,7 +15,7 @@ describe("The 'watch' method", function () { var client; beforeEach(function (done) { - client = redis.createClient.apply(redis.createClient, args); + client = redis.createClient.apply(null, args); client.once("ready", function () { client.flushdb(done); }); diff --git a/test/commands/zadd.spec.js b/test/commands/zadd.spec.js index 9536b2688ad..669d7a06413 100644 --- a/test/commands/zadd.spec.js +++ b/test/commands/zadd.spec.js @@ -13,7 +13,7 @@ describe("The 'zadd' method", function () { var client; beforeEach(function (done) { - client = redis.createClient.apply(redis.createClient, args); + client = redis.createClient.apply(null, args); client.once("ready", function () { client.flushdb(done); }); diff --git a/test/commands/zscan.spec.js b/test/commands/zscan.spec.js index 0794b8941ae..b7a5936f300 100644 --- a/test/commands/zscan.spec.js +++ b/test/commands/zscan.spec.js @@ -13,7 +13,7 @@ describe("The 'zscan' method", function () { var client; beforeEach(function (done) { - client = redis.createClient.apply(redis.createClient, args); + client = redis.createClient.apply(null, args); client.once("ready", function () { client.flushdb(done); }); diff --git a/test/commands/zscore.spec.js b/test/commands/zscore.spec.js index 6bfd2796688..8a285fba9d4 100644 --- a/test/commands/zscore.spec.js +++ b/test/commands/zscore.spec.js @@ -13,7 +13,7 @@ describe("The 'zscore' method", function () { var client; beforeEach(function (done) { - client = redis.createClient.apply(redis.createClient, args); + client = redis.createClient.apply(null, args); client.once("ready", function () { client.flushdb(done); }); diff --git a/test/connection.spec.js b/test/connection.spec.js index 65bcd0b6c85..71ecf1c9b59 100644 --- a/test/connection.spec.js +++ b/test/connection.spec.js @@ -135,7 +135,7 @@ describe("connection tests", function () { }); it("emits error once if reconnecting after command has been executed but not yet returned without callback", function (done) { - client = redis.createClient.apply(redis.createClient, args); + client = redis.createClient.apply(null, args); client.on('error', function(err) { assert.strictEqual(err.code, 'UNCERTAIN_STATE'); done(); @@ -296,7 +296,7 @@ describe("connection tests", function () { }); it("connects correctly with args", function (done) { - client = redis.createClient.apply(redis.createClient, args); + client = redis.createClient.apply(null, args); client.on("error", done); client.once("ready", function () { @@ -382,7 +382,7 @@ describe("connection tests", function () { }); it("connects correctly even if the info command is not present on the redis server", function (done) { - client = redis.createClient.apply(redis.createClient, args); + client = redis.createClient.apply(null, args); client.info = function (cb) { // Mock the result cb(new Error("ERR unknown command 'info'")); @@ -465,7 +465,7 @@ describe("connection tests", function () { } it("redis still loading <= 1000ms", function (done) { - client = redis.createClient.apply(redis.createClient, args); + client = redis.createClient.apply(null, args); var tmp = client.info.bind(client); var end = helper.callFuncAfter(done, 3); var delayed = false; @@ -495,7 +495,7 @@ describe("connection tests", function () { }); it("redis still loading > 1000ms", function (done) { - client = redis.createClient.apply(redis.createClient, args); + client = redis.createClient.apply(null, args); var tmp = client.info.bind(client); var end = helper.callFuncAfter(done, 3); var delayed = false; diff --git a/test/detect_buffers.spec.js b/test/detect_buffers.spec.js index c8f0f134d82..a7c97eac11e 100644 --- a/test/detect_buffers.spec.js +++ b/test/detect_buffers.spec.js @@ -16,7 +16,7 @@ describe("detect_buffers", function () { }); beforeEach(function (done) { - client = redis.createClient.apply(redis.createClient, args); + client = redis.createClient.apply(null, args); client.once("error", done); client.once("connect", function () { client.flushdb(function (err) { diff --git a/test/multi.spec.js b/test/multi.spec.js index 849071a7232..20be0325a76 100644 --- a/test/multi.spec.js +++ b/test/multi.spec.js @@ -105,13 +105,11 @@ describe("The 'multi' method", function () { describe("when not connected", function () { beforeEach(function (done) { - client = redis.createClient.apply(redis.createClient, args); + client = redis.createClient.apply(null, args); client.once("ready", function () { client.quit(); }); - client.once('end', function () { - return done(); - }); + client.once('end', done); }); it("reports an error", function (done) { @@ -133,7 +131,7 @@ describe("The 'multi' method", function () { describe("when connected", function () { beforeEach(function (done) { - client = redis.createClient.apply(redis.createClient, args); + client = redis.createClient.apply(null, args); client.once("connect", done); }); @@ -212,7 +210,7 @@ describe("The 'multi' method", function () { describe("when ready", function () { beforeEach(function (done) { - client = redis.createClient.apply(redis.createClient, args); + client = redis.createClient.apply(null, args); client.once("ready", function () { client.flushdb(function (err) { return done(err); diff --git a/test/node_redis.spec.js b/test/node_redis.spec.js index 09d64a8d025..44dc40f78f4 100644 --- a/test/node_redis.spec.js +++ b/test/node_redis.spec.js @@ -19,7 +19,7 @@ describe("The node_redis client", function () { describe("when connected", function () { beforeEach(function (done) { - client = redis.createClient.apply(redis.createClient, args); + client = redis.createClient.apply(null, args); client.once("connect", function () { client.flushdb(done); }); @@ -343,7 +343,23 @@ describe("The node_redis client", function () { domain.on('error', function (err) { assert.strictEqual(err.message, 'ohhhh noooo'); domain.exit(); - return done(); + done(); + }); + }); + + it('catches all errors from within the domain', function (done) { + var domain = require('domain').create(); + + domain.run(function () { + // Trigger an error within the domain + client.end(true); + client.set('domain', 'value'); + }); + + domain.on('error', function (err) { + assert.strictEqual(err.message, 'SET can\'t be processed. The connection has already been closed.'); + domain.exit(); + done(); }); }); }); @@ -351,7 +367,7 @@ describe("The node_redis client", function () { describe('monitor', function () { it('monitors commands on all other redis clients', function (done) { - var monitorClient = redis.createClient.apply(redis.createClient, args); + var monitorClient = redis.createClient.apply(null, args); var responses = []; monitorClient.monitor(function (err, res) { @@ -474,7 +490,7 @@ describe("The node_redis client", function () { }); it("fires client.on('ready')", function (done) { - client = redis.createClient.apply(redis.createClient, args); + client = redis.createClient.apply(null, args); client.on("ready", function () { assert.strictEqual(true, client.options.socket_nodelay); client.quit(); @@ -486,7 +502,7 @@ describe("The node_redis client", function () { }); it('client is functional', function (done) { - client = redis.createClient.apply(redis.createClient, args); + client = redis.createClient.apply(null, args); client.on("ready", function () { assert.strictEqual(true, client.options.socket_nodelay); client.set(["set key 1", "set val"], helper.isString("OK")); @@ -508,7 +524,7 @@ describe("The node_redis client", function () { }); it("fires client.on('ready')", function (done) { - client = redis.createClient.apply(redis.createClient, args); + client = redis.createClient.apply(null, args); client.on("ready", function () { assert.strictEqual(false, client.options.socket_nodelay); client.quit(); @@ -520,7 +536,7 @@ describe("The node_redis client", function () { }); it('client is functional', function (done) { - client = redis.createClient.apply(redis.createClient, args); + client = redis.createClient.apply(null, args); client.on("ready", function () { assert.strictEqual(false, client.options.socket_nodelay); client.set(["set key 1", "set val"], helper.isString("OK")); @@ -539,7 +555,7 @@ describe("The node_redis client", function () { describe('defaults to true', function () { it("fires client.on('ready')", function (done) { - client = redis.createClient.apply(redis.createClient, args); + client = redis.createClient.apply(null, args); client.on("ready", function () { assert.strictEqual(true, client.options.socket_nodelay); client.quit(); @@ -551,7 +567,7 @@ describe("The node_redis client", function () { }); it('client is functional', function (done) { - client = redis.createClient.apply(redis.createClient, args); + client = redis.createClient.apply(null, args); client.on("ready", function () { assert.strictEqual(true, client.options.socket_nodelay); client.set(["set key 1", "set val"], helper.isString("OK")); @@ -599,7 +615,7 @@ describe("The node_redis client", function () { describe('protocol error', function () { it("should gracefully recover and only fail on the already send commands", function (done) { - client = redis.createClient.apply(redis.createClient, args); + client = redis.createClient.apply(null, args); client.on('error', function(err) { assert.strictEqual(err.message, 'Protocol error, got "a" as reply type byte'); // After the hard failure work properly again. The set should have been processed properly too diff --git a/test/return_buffers.spec.js b/test/return_buffers.spec.js index 029103abb58..ab12668c595 100644 --- a/test/return_buffers.spec.js +++ b/test/return_buffers.spec.js @@ -17,7 +17,7 @@ describe("return_buffers", function () { }); beforeEach(function (done) { - client = redis.createClient.apply(redis.createClient, args); + client = redis.createClient.apply(null, args); var i = 1; if (args[2].detect_buffers) { // Test if detect_buffer option was deactivated diff --git a/test/tls.spec.js b/test/tls.spec.js index 8caddf816f4..0fb051cf67c 100644 --- a/test/tls.spec.js +++ b/test/tls.spec.js @@ -111,7 +111,7 @@ describe("TLS connection tests", function () { tls: faulty_cert }); client.on('error', function (err) { - assert.strictEqual(err.code, 'DEPTH_ZERO_SELF_SIGNED_CERT'); + assert(/DEPTH_ZERO_SELF_SIGNED_CERT/.test(err.code || err.message), err); client.end(true); }); client.set('foo', 'bar', function (err, res) { From 6598da536620f6e3c713cebd00fa6c28e24dd596 Mon Sep 17 00:00:00 2001 From: Ruben Bridgewater Date: Mon, 14 Mar 2016 22:55:01 +0100 Subject: [PATCH 0584/1748] Indicate transmission errors --- index.js | 4 ++-- test/auth.spec.js | 21 ++++++++------------- 2 files changed, 10 insertions(+), 15 deletions(-) diff --git a/index.js b/index.js index 5ded1abce65..f35dd1b6a57 100644 --- a/index.js +++ b/index.js @@ -233,8 +233,8 @@ RedisClient.prototype.create_stream = function () { self.on_error(err); }); - this.stream.once('close', function () { - self.connection_gone('close', new Error('Stream connection closed')); + this.stream.once('close', function (hadError) { + self.connection_gone('close', new Error('Stream connection closed' + (hadError ? ' because of a transmission error' : ''))); }); this.stream.once('end', function () { diff --git a/test/auth.spec.js b/test/auth.spec.js index f09cef6f98d..102bce91f8b 100644 --- a/test/auth.spec.js +++ b/test/auth.spec.js @@ -115,9 +115,7 @@ describe("client authentication", function () { auth_pass: auth }); client = redis.createClient.apply(null, args); - client.on("ready", function () { - return done(); - }); + client.on("ready", done); }); it('allows auth and no_ready_check to be provided as config option for client', function (done) { @@ -128,9 +126,7 @@ describe("client authentication", function () { no_ready_check: true }); client = redis.createClient.apply(null, args); - client.on("ready", function () { - done(); - }); + client.on("ready", done); }); it('allows auth to be provided post-hoc with auth method', function (done) { @@ -139,25 +135,24 @@ describe("client authentication", function () { var args = config.configureClient(parser, ip); client = redis.createClient.apply(null, args); client.auth(auth); - client.on("ready", function () { - return done(); - }); + client.on("ready", done); }); it('reconnects with appropriate authentication', function (done) { if (helper.redisProcess().spawnFailed()) this.skip(); - var readyCount = 0; client = redis.createClient.apply(null, args); client.auth(auth); client.on("ready", function () { - readyCount++; - if (readyCount === 1) { + if (this.times_connected === 1) { client.stream.destroy(); } else { - return done(); + done(); } }); + client.on('reconnecting', function (params) { + assert.strictEqual(params.error.message, 'Stream connection closed'); + }); }); it('should return an error if the password is not of type string and a callback has been provided', function (done) { From 8e970a7fef09fd709bd87972158584dbc9d8aa16 Mon Sep 17 00:00:00 2001 From: Ruben Bridgewater Date: Tue, 15 Mar 2016 03:45:38 +0100 Subject: [PATCH 0585/1748] Add Github issue / pull request templates --- .github/ISSUE_TEMPLATE.md | 15 +++++++++++++++ .github/PULL_REQUEST_TEMPLATE.md | 14 ++++++++++++++ 2 files changed, 29 insertions(+) create mode 100644 .github/ISSUE_TEMPLATE.md create mode 100644 .github/PULL_REQUEST_TEMPLATE.md diff --git a/.github/ISSUE_TEMPLATE.md b/.github/ISSUE_TEMPLATE.md new file mode 100644 index 00000000000..0b4f598a5b0 --- /dev/null +++ b/.github/ISSUE_TEMPLATE.md @@ -0,0 +1,15 @@ +_Thanks for wanting to report an issue you've found in node_redis. Please delete +this text and fill in the template below. Please note that the issue tracker is only +for bug reports or feature requests. If you have a question, please ask that on [gitter]. +If unsure about something, just do as best as you're able._ + +_Note that it will be much easier to fix the issue if a test case that reproduces +the problem is provided. It is of course not always possible to reduce your code +to a small test case, but it's highly appreciated to have as much data as possible. +Thank you!_ + +* **Version**: What node_redis version is the issue happening on? +* **Platform**: What platform / version? (For example Node.js 0.10 or Node.js 5.7.0) +* **Description**: Description of your issue, stack traces from errors and code that reproduces the issue + +[gitter]: https://gitter.im/NodeRedis/node_redis?utm_source=badge&utm_medium=badge&utm_campaign=pr-badge \ No newline at end of file diff --git a/.github/PULL_REQUEST_TEMPLATE.md b/.github/PULL_REQUEST_TEMPLATE.md new file mode 100644 index 00000000000..9706621c1b1 --- /dev/null +++ b/.github/PULL_REQUEST_TEMPLATE.md @@ -0,0 +1,14 @@ +### Pull Request check-list + +_Please make sure to review and check all of these items:_ + +- [ ] Does `npm test` pass with this change (including linting)? +- [ ] Is the new or changed code fully tested? +- [ ] Is a documentation update included (if this change modifies existing APIs, or introduces new ones)? + +_NOTE: these things are not required to open a PR and can be done +afterwards / while the PR is open._ + +### Description of change + +_Please provide a description of the change here._ \ No newline at end of file From 093f437fac70ac1faacf8553e5e399b01e33eb6f Mon Sep 17 00:00:00 2001 From: Ruben Bridgewater Date: Tue, 15 Mar 2016 05:43:10 +0100 Subject: [PATCH 0586/1748] v.2.5.0 --- changelog.md | 4 ++++ package.json | 2 +- 2 files changed, 5 insertions(+), 1 deletion(-) diff --git a/changelog.md b/changelog.md index 4827641a149..3b000822ec1 100644 --- a/changelog.md +++ b/changelog.md @@ -1,6 +1,10 @@ Changelog ========= +## v.2.5.0 - 15 Mar, 2015 + +Same changelog as the pre-release + ## v.2.5.0-1 - 07 Mar, 2015 This is a big release with some substaintual underlining changes. Therefor this is released as a pre-release and I encourage anyone who's able to, to test this out. diff --git a/package.json b/package.json index bd1d42831fd..64e674d6cdc 100644 --- a/package.json +++ b/package.json @@ -1,6 +1,6 @@ { "name": "redis", - "version": "2.5.0-1", + "version": "2.5.0", "description": "Redis client library", "keywords": [ "database", From 24e7486a5adc47b59410007d5f190e3097e01c3b Mon Sep 17 00:00:00 2001 From: Ruben Bridgewater Date: Tue, 15 Mar 2016 09:59:09 +0100 Subject: [PATCH 0587/1748] Fix forgotten optional info section Fixes #1003 --- lib/individualCommands.js | 10 ++++++++-- test/commands/info.spec.js | 16 ++++++++++++++++ 2 files changed, 24 insertions(+), 2 deletions(-) diff --git a/lib/individualCommands.js b/lib/individualCommands.js index b162238cbe9..e4c871842ab 100644 --- a/lib/individualCommands.js +++ b/lib/individualCommands.js @@ -33,11 +33,17 @@ RedisClient.prototype.select = RedisClient.prototype.SELECT = function select (d }; // Store info in this.server_info after each call -RedisClient.prototype.info = RedisClient.prototype.INFO = function info (callback) { +RedisClient.prototype.info = RedisClient.prototype.INFO = function info (section, callback) { var self = this; var ready = this.ready; + if (typeof section === 'function') { + callback = section; + section = 'default'; + } else if (section === undefined) { + section = 'default'; + } this.ready = ready || this.offline_queue.length === 0; // keep the execution order intakt - var tmp = this.send_command('info', [], function (err, res) { + var tmp = this.send_command('info', [section], function (err, res) { if (res) { var obj = {}; var lines = res.toString().split('\r\n'); diff --git a/test/commands/info.spec.js b/test/commands/info.spec.js index 64daf7acdfa..bb628a757b6 100644 --- a/test/commands/info.spec.js +++ b/test/commands/info.spec.js @@ -37,6 +37,22 @@ describe("The 'info' method", function () { }, 150); }); + it("works with optional section provided with and without callback", function (done) { + client.set('foo', 'bar'); + client.info('keyspace'); + client.select(2, function () { + assert.strictEqual(Object.keys(client.server_info).length, 3, 'Key length should be three'); + assert(typeof client.server_info.db2 === 'object', 'db2 keyspace should be an object'); + }); + client.set('foo', 'bar'); + client.info('all', function (err, res) { + assert(Object.keys(client.server_info).length > 3, 'Key length should be way above three'); + assert.strictEqual(typeof client.server_info.redis_version, 'string'); + assert.strictEqual(typeof client.server_info.db2, 'object'); + done(); + }); + }); + it("emit error after a failure", function (done) { client.info(); client.once('error', function (err) { From 08a4537263aead8da24775371e810b5f538a91a5 Mon Sep 17 00:00:00 2001 From: Ruben Bridgewater Date: Tue, 15 Mar 2016 10:03:18 +0100 Subject: [PATCH 0588/1748] v.2.5.1 --- changelog.md | 6 ++++++ package.json | 2 +- 2 files changed, 7 insertions(+), 1 deletion(-) diff --git a/changelog.md b/changelog.md index 3b000822ec1..f3b49a0960b 100644 --- a/changelog.md +++ b/changelog.md @@ -1,6 +1,12 @@ Changelog ========= +## v.2.5.1 - 15 Mar, 2015 + +Bugfixes + +- Fixed info command not working anymore with optional section argument + ## v.2.5.0 - 15 Mar, 2015 Same changelog as the pre-release diff --git a/package.json b/package.json index 64e674d6cdc..dbe60024e7f 100644 --- a/package.json +++ b/package.json @@ -1,6 +1,6 @@ { "name": "redis", - "version": "2.5.0", + "version": "2.5.1", "description": "Redis client library", "keywords": [ "database", From eb9500bb9fcfc5800ecfab5fe3145f89b443b5e3 Mon Sep 17 00:00:00 2001 From: Ruben Bridgewater Date: Wed, 16 Mar 2016 23:15:21 +0100 Subject: [PATCH 0589/1748] Fix redis 2.4 auth support --- lib/individualCommands.js | 19 ++++++++++++++----- test/auth.spec.js | 20 ++++++++++++++++++++ 2 files changed, 34 insertions(+), 5 deletions(-) diff --git a/lib/individualCommands.js b/lib/individualCommands.js index e4c871842ab..188372eea26 100644 --- a/lib/individualCommands.js +++ b/lib/individualCommands.js @@ -4,6 +4,7 @@ var utils = require('./utils'); var debug = require('./debug'); var Multi = require('./multi'); var no_password_is_set = /no password is set/; +var loading = /LOADING/; var RedisClient = require('../').RedisClient; /******************************** @@ -91,12 +92,20 @@ RedisClient.prototype.auth = RedisClient.prototype.AUTH = function auth (pass, c this.auth_pass = pass; this.ready = this.offline_queue.length === 0; // keep the execution order intakt var tmp = this.send_command('auth', [pass], function (err, res) { - if (err && no_password_is_set.test(err.message)) { - self.warn('Warning: Redis server does not require a password, but a password was supplied.'); - err = null; - res = 'OK'; + if (err) { + if (no_password_is_set.test(err.message)) { + self.warn('Warning: Redis server does not require a password, but a password was supplied.'); + err = null; + res = 'OK'; + } else if (loading.test(err.message)) { + // If redis is still loading the db, it will not authenticate and everything else will fail + debug('Redis still loading, trying to authenticate later'); + setTimeout(function () { + self.auth(pass, callback); + }, 200); + return; + } } - utils.callback_or_emit(self, callback, err, res); }); this.ready = ready; diff --git a/test/auth.spec.js b/test/auth.spec.js index 102bce91f8b..de55c3f23cb 100644 --- a/test/auth.spec.js +++ b/test/auth.spec.js @@ -45,6 +45,26 @@ describe("client authentication", function () { }); }); + it('support redis 2.4 with retrying auth commands if still loading', function (done) { + if (helper.redisProcess().spawnFailed()) this.skip(); + + client = redis.createClient.apply(null, args); + var time = Date.now(); + client.auth(auth, function (err, res) { + assert.strictEqual('retry worked', res); + assert(Date.now() - time >= 200, 'Time should be above 200 ms (the reconnect time)'); + assert(Date.now() - time < 300, 'Time should be below 300 ms (the reconnect should only take a bit above 200 ms)'); + done(); + }); + var tmp = client.command_queue.get(0).callback; + client.command_queue.get(0).callback = function (err, res) { + client.auth = function (pass, callback) { + callback(null, 'retry worked'); + }; + tmp(new Error('ERR redis is still LOADING')); + }; + }); + it("emits error when auth is bad without callback", function (done) { if (helper.redisProcess().spawnFailed()) this.skip(); From 359820c766f13716e83aebe898a530612ffd1d4c Mon Sep 17 00:00:00 2001 From: Ruben Bridgewater Date: Wed, 16 Mar 2016 23:40:25 +0100 Subject: [PATCH 0590/1748] Support redis 2.4 info command Fixes #1008 --- lib/individualCommands.js | 8 ++++---- test/commands/info.spec.js | 20 ++++++++++++++++---- 2 files changed, 20 insertions(+), 8 deletions(-) diff --git a/lib/individualCommands.js b/lib/individualCommands.js index 188372eea26..572228fe78e 100644 --- a/lib/individualCommands.js +++ b/lib/individualCommands.js @@ -37,14 +37,14 @@ RedisClient.prototype.select = RedisClient.prototype.SELECT = function select (d RedisClient.prototype.info = RedisClient.prototype.INFO = function info (section, callback) { var self = this; var ready = this.ready; + var args = []; if (typeof section === 'function') { callback = section; - section = 'default'; - } else if (section === undefined) { - section = 'default'; + } else if (section !== undefined) { + args = Array.isArray(section) ? section : [section]; } this.ready = ready || this.offline_queue.length === 0; // keep the execution order intakt - var tmp = this.send_command('info', [section], function (err, res) { + var tmp = this.send_command('info', args, function (err, res) { if (res) { var obj = {}; var lines = res.toString().split('\r\n'); diff --git a/test/commands/info.spec.js b/test/commands/info.spec.js index bb628a757b6..ca4ec700805 100644 --- a/test/commands/info.spec.js +++ b/test/commands/info.spec.js @@ -12,14 +12,14 @@ describe("The 'info' method", function () { describe("using " + parser + " and " + ip, function () { var client; - before(function (done) { + beforeEach(function (done) { client = redis.createClient.apply(null, args); client.once("ready", function () { client.flushall(done); }); }); - after(function () { + afterEach(function () { client.end(true); }); @@ -41,9 +41,10 @@ describe("The 'info' method", function () { client.set('foo', 'bar'); client.info('keyspace'); client.select(2, function () { - assert.strictEqual(Object.keys(client.server_info).length, 3, 'Key length should be three'); - assert(typeof client.server_info.db2 === 'object', 'db2 keyspace should be an object'); + assert.strictEqual(Object.keys(client.server_info).length, 2, 'Key length should be three'); + assert.strictEqual(typeof client.server_info.db0, 'object', 'db0 keyspace should be an object'); }); + client.info(['keyspace']); client.set('foo', 'bar'); client.info('all', function (err, res) { assert(Object.keys(client.server_info).length > 3, 'Key length should be way above three'); @@ -53,6 +54,17 @@ describe("The 'info' method", function () { }); }); + it('check redis v.2.4 support', function (done) { + var end = helper.callFuncAfter(done, 2); + client.send_command = function (command, args, callback) { + assert.strictEqual(args.length, 0); + assert.strictEqual(command, 'info'); + end(); + }; + client.info(); + client.info(function () {}); + }); + it("emit error after a failure", function (done) { client.info(); client.once('error', function (err) { From fc9967a14000dc871deeec3956d447afd61ec9c4 Mon Sep 17 00:00:00 2001 From: Ruben Bridgewater Date: Thu, 17 Mar 2016 00:02:08 +0100 Subject: [PATCH 0591/1748] v.2.5.2 --- changelog.md | 14 ++++++++++---- package.json | 2 +- 2 files changed, 11 insertions(+), 5 deletions(-) diff --git a/changelog.md b/changelog.md index f3b49a0960b..df22c86b108 100644 --- a/changelog.md +++ b/changelog.md @@ -1,17 +1,23 @@ Changelog ========= -## v.2.5.1 - 15 Mar, 2015 +## v.2.5.2 - 16 Mar, 2016 + +Bugfixes + +- Fixed breaking changes against Redis 2.4 introduced in 2.5.0 / 2.5.1 + +## v.2.5.1 - 15 Mar, 2016 Bugfixes - Fixed info command not working anymore with optional section argument -## v.2.5.0 - 15 Mar, 2015 +## v.2.5.0 - 15 Mar, 2016 Same changelog as the pre-release -## v.2.5.0-1 - 07 Mar, 2015 +## v.2.5.0-1 - 07 Mar, 2016 This is a big release with some substaintual underlining changes. Therefor this is released as a pre-release and I encourage anyone who's able to, to test this out. @@ -73,7 +79,7 @@ Deprecations - The `retry_max_delay` option is deprecated and will be removed in v.3.0.0. Please use the `retry_strategy` instead - The drain event is deprecated and will be removed in v.3.0.0. Please listen to the stream drain event instead - The idle event is deprecated and will likely be removed in v.3.0.0. If you rely on this feature please open a new ticket in node_redis with your use case -- Redis < v. 2.6.11 is not supported anymore and will not work in all cases. Please update to a newer redis version +- Redis < v. 2.6 is not officially supported anymore and might not work in all cases. Please update to a newer redis version as it is not possible to test for these old versions - Removed non documented command syntax (adding the callback to an arguments array instead of passing it as individual argument) ## v.2.4.2 - 27 Nov, 2015 diff --git a/package.json b/package.json index dbe60024e7f..42c9996b2c6 100644 --- a/package.json +++ b/package.json @@ -1,6 +1,6 @@ { "name": "redis", - "version": "2.5.1", + "version": "2.5.2", "description": "Redis client library", "keywords": [ "database", From 643eb95aaf9e4da22ddcf673f1277a407630ba99 Mon Sep 17 00:00:00 2001 From: Ruben Bridgewater Date: Thu, 17 Mar 2016 00:07:40 +0100 Subject: [PATCH 0592/1748] Require the redis version in the issue template too --- .github/ISSUE_TEMPLATE.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.github/ISSUE_TEMPLATE.md b/.github/ISSUE_TEMPLATE.md index 0b4f598a5b0..fb90edf57ad 100644 --- a/.github/ISSUE_TEMPLATE.md +++ b/.github/ISSUE_TEMPLATE.md @@ -8,7 +8,7 @@ the problem is provided. It is of course not always possible to reduce your code to a small test case, but it's highly appreciated to have as much data as possible. Thank you!_ -* **Version**: What node_redis version is the issue happening on? +* **Version**: What node_redis and what redis version is the issue happening on? * **Platform**: What platform / version? (For example Node.js 0.10 or Node.js 5.7.0) * **Description**: Description of your issue, stack traces from errors and code that reproduces the issue From ddbb94b598ea204d3aeb7c4a79ec6900d037bde8 Mon Sep 17 00:00:00 2001 From: Ruben Bridgewater Date: Mon, 21 Mar 2016 17:12:56 +0100 Subject: [PATCH 0593/1748] Fix file example Fixes #176 --- examples/file.js | 10 +++++++--- 1 file changed, 7 insertions(+), 3 deletions(-) diff --git a/examples/file.js b/examples/file.js index c68ec27d4d5..aff7e1677a8 100644 --- a/examples/file.js +++ b/examples/file.js @@ -3,12 +3,15 @@ // Read a file from disk, store it in Redis, then read it back from Redis. var redis = require('redis'), - client = redis.createClient(), + client = redis.createClient({ + return_buffers: true + }), fs = require('fs'), - filename = 'kids_in_cart.jpg'; + assert = require('assert'), + filename = 'grumpyCat.jpg'; // Get the file I use for testing like this: -// curl http://ranney.com/kids_in_cart.jpg -o kids_in_cart.jpg +// curl http://media4.popsugar-assets.com/files/2014/08/08/878/n/1922507/caef16ec354ca23b_thumb_temp_cover_file32304521407524949.xxxlarge/i/Funny-Cat-GIFs.jpg -o grumpyCat.jpg // or just use your own file. // Read a file from fs, store it in Redis, get it back from Redis, write it back to fs. @@ -21,6 +24,7 @@ fs.readFile(filename, function (err, data) { if (err) { console.log('Get error: ' + err); } else { + assert.strictEqual(data.inspect(), reply.inspect()); fs.writeFile('duplicate_' + filename, reply, function (err) { if (err) { console.log('Error on write: ' + err); From db6cf0a3b588c46612880f108359b3a2a12b451b Mon Sep 17 00:00:00 2001 From: Ruben Bridgewater Date: Mon, 21 Mar 2016 17:15:12 +0100 Subject: [PATCH 0594/1748] Don't throw on invalid data types but throw a warning instead Fixes #1013 --- index.js | 9 ++++++++- test/commands/hset.spec.js | 22 +++++++++++----------- 2 files changed, 19 insertions(+), 12 deletions(-) diff --git a/index.js b/index.js index f35dd1b6a57..b88c1ed4fbb 100644 --- a/index.js +++ b/index.js @@ -805,7 +805,7 @@ RedisClient.prototype.send_command = function (command, args, callback) { 'Please handle this in your code to make sure everything works as you intended it to.' ); args_copy[i] = 'null'; // Backwards compatible :/ - } else { + } else if (Buffer.isBuffer(args[i])) { args_copy[i] = args[i]; command_obj.buffer_args = true; big_data = true; @@ -813,6 +813,13 @@ RedisClient.prototype.send_command = function (command, args, callback) { this.pipeline += 2; this.writeDefault = this.writeBuffers; } + } else { + this.warn( + 'Deprecated: The ' + command.toUpperCase() + ' command contains a argument of type ' + args[i].constructor.name + '.\n' + + 'This is converted to "' + args[i].toString() + '" by using .toString() now and will return an error from v.3.0 on.\n' + + 'Please handle this in your code to make sure everything works as you intended it to.' + ); + args_copy[i] = args[i].toString(); // Backwards compatible :/ } } else if (typeof args[i] === 'undefined') { this.warn( diff --git a/test/commands/hset.spec.js b/test/commands/hset.spec.js index bd5415189c4..d87f4d04ce0 100644 --- a/test/commands/hset.spec.js +++ b/test/commands/hset.spec.js @@ -45,22 +45,22 @@ describe("The 'hset' method", function () { }); }); - it('throws a error if someone passed a array either as field or as value', function (done) { + it('warns if someone passed a array either as field or as value', function (done) { var hash = "test hash"; var field = "array"; // This would be converted to "array contents" but if you use more than one entry, // it'll result in e.g. "array contents,second content" and this is not supported and considered harmful var value = ["array contents"]; - try { - client.HMSET(hash, field, value); - throw new Error('test failed'); - } catch (err) { - if (/invalid data/.test(err.message)) { - done(); - } else { - done(err); - } - } + client.on('warning', function (msg) { + assert.strictEqual( + msg, + 'Deprecated: The HMSET command contains a argument of type Array.\n' + + 'This is converted to "array contents" by using .toString() now and will return an error from v.3.0 on.\n' + + 'Please handle this in your code to make sure everything works as you intended it to.' + ); + done(); + }); + client.HMSET(hash, field, value); }); it('does not error when a buffer and date are set as values on the same hash', function (done) { From bf568b6df7560782215c2f91731761bb7151d10e Mon Sep 17 00:00:00 2001 From: Ruben Bridgewater Date: Mon, 21 Mar 2016 17:23:11 +0100 Subject: [PATCH 0595/1748] v.2.5.3 --- changelog.md | 6 ++++++ package.json | 2 +- 2 files changed, 7 insertions(+), 1 deletion(-) diff --git a/changelog.md b/changelog.md index df22c86b108..1dba602acbd 100644 --- a/changelog.md +++ b/changelog.md @@ -1,6 +1,12 @@ Changelog ========= +## v.2.5.3 - 21 Mar, 2016 + +Bugfixes + +- Revert throwing on invalid data types and print a warning instead + ## v.2.5.2 - 16 Mar, 2016 Bugfixes diff --git a/package.json b/package.json index 42c9996b2c6..e53574749ae 100644 --- a/package.json +++ b/package.json @@ -1,6 +1,6 @@ { "name": "redis", - "version": "2.5.2", + "version": "2.5.3", "description": "Redis client library", "keywords": [ "database", From 344291a98a80bdd17ece6b5247b052aa7de0e153 Mon Sep 17 00:00:00 2001 From: Ruben Bridgewater Date: Fri, 25 Mar 2016 15:26:22 +0100 Subject: [PATCH 0596/1748] Fix monitoring mode --- README.md | 14 +++---- changelog.md | 14 +++++++ index.js | 77 ++++++++++------------------------- lib/individualCommands.js | 35 ++++++++++++++++ lib/utils.js | 5 +-- test/node_redis.spec.js | 86 ++++++++++++++++++++++++++++++++------- 6 files changed, 150 insertions(+), 81 deletions(-) diff --git a/README.md b/README.md index 355d43b7069..2c03e795cdf 100644 --- a/README.md +++ b/README.md @@ -545,22 +545,20 @@ If you fire many commands at once this is going to **boost the execution speed b Redis supports the `MONITOR` command, which lets you see all commands received by the Redis server across all client connections, including from other client libraries and other computers. -After you send the `MONITOR` command, no other commands are valid on that connection. `node_redis` -will emit a `monitor` event for every new monitor message that comes across. The callback for the -`monitor` event takes a timestamp from the Redis server and an array of command arguments. +A `monitor` event is going to be emitted for every command fired from any client connected to the server including the monitoring client itself. +The callback for the `monitor` event takes a timestamp from the Redis server, an array of command arguments and the raw monitoring string. Here is a simple example: ```js -var client = require("redis").createClient(), - util = require("util"); - +var client = require("redis").createClient(); client.monitor(function (err, res) { console.log("Entering monitoring mode."); }); +client.set('foo', 'bar'); -client.on("monitor", function (time, args) { - console.log(time + ": " + util.inspect(args)); +client.on("monitor", function (time, args, raw_reply) { + console.log(time + ": " + args); // 1458910076.446514:['set', 'foo', 'bar'] }); ``` diff --git a/changelog.md b/changelog.md index 1dba602acbd..992eb422166 100644 --- a/changelog.md +++ b/changelog.md @@ -1,6 +1,20 @@ Changelog ========= +## v.2.6.0 - XX Mar, 2016 + +Features + +- Monitor now works together with the offline queue + - All commands that were send after a connection loss are now going to be send after reconnecting +- Activating monitor mode does now work together with arbitrary commands including pub sub mode + +Bugfixes + +- Fixed calling monitor command while other commands are still running +- Fixed monitor and pub sub mode not working together +- Fixed monitor mode not working in combination with the offline queue + ## v.2.5.3 - 21 Mar, 2016 Bugfixes diff --git a/index.js b/index.js index b88c1ed4fbb..90a56d68e76 100644 --- a/index.js +++ b/index.js @@ -140,6 +140,7 @@ function RedisClient (options, stream) { this.pipeline = 0; this.times_connected = 0; this.options = options; + this.buffers = options.return_buffers || options.detect_buffers; // Init parser this.reply_parser = Parser({ returnReply: function (data) { @@ -154,7 +155,7 @@ function RedisClient (options, stream) { self.stream.destroy(); self.return_error(err); }, - returnBuffers: options.return_buffers || options.detect_buffers, + returnBuffers: this.buffers, name: options.parser }); this.create_stream(); @@ -329,9 +330,7 @@ RedisClient.prototype.on_error = function (err) { } err.message = 'Redis connection to ' + this.address + ' failed - ' + err.message; - debug(err.message); - this.connected = false; this.ready = false; @@ -369,12 +368,6 @@ RedisClient.prototype.on_ready = function () { debug('on_ready called ' + this.address + ' id ' + this.connection_id); this.ready = true; - if (this.old_state !== null) { - this.monitoring = this.old_state.monitoring; - this.pub_sub_mode = this.old_state.pub_sub_mode; - this.old_state = null; - } - var cork; if (!this.stream.cork) { cork = function (len) { @@ -393,16 +386,15 @@ RedisClient.prototype.on_ready = function () { } this.cork = cork; - // restore modal commands from previous connection + // restore modal commands from previous connection. The order of the commands is important if (this.selected_db !== undefined) { - // this trick works if and only if the following send_command - // never goes into the offline queue - var pub_sub_mode = this.pub_sub_mode; - this.pub_sub_mode = false; this.send_command('select', [this.selected_db]); - this.pub_sub_mode = pub_sub_mode; } - if (this.pub_sub_mode === true) { + if (this.old_state !== null) { + this.monitoring = this.old_state.monitoring; + this.pub_sub_mode = this.old_state.pub_sub_mode; + } + if (this.pub_sub_mode) { // only emit 'ready' when all subscriptions were made again var callback_count = 0; var callback = function () { @@ -424,12 +416,10 @@ RedisClient.prototype.on_ready = function () { }); return; } - if (this.monitoring) { this.send_command('monitor', []); - } else { - this.send_offline_queue(); } + this.send_offline_queue(); this.emit('ready'); }; @@ -525,15 +515,13 @@ RedisClient.prototype.connection_gone = function (why, error) { this.cork = noop; this.pipeline = 0; - if (this.old_state === null) { - var state = { - monitoring: this.monitoring, - pub_sub_mode: this.pub_sub_mode - }; - this.old_state = state; - this.monitoring = false; - this.pub_sub_mode = false; - } + var state = { + monitoring: this.monitoring, + pub_sub_mode: this.pub_sub_mode + }; + this.old_state = state; + this.monitoring = false; + this.pub_sub_mode = false; // since we are collapsing end and close, users don't expect to be called twice if (!this.emitted_end) { @@ -604,9 +592,7 @@ RedisClient.prototype.connection_gone = function (why, error) { }; RedisClient.prototype.return_error = function (err) { - var command_obj = this.command_queue.shift(), - queue_len = this.command_queue.length; - + var command_obj = this.command_queue.shift(); if (command_obj && command_obj.command && command_obj.command.toUpperCase) { err.command = command_obj.command.toUpperCase(); } @@ -617,8 +603,7 @@ RedisClient.prototype.return_error = function (err) { err.code = match[1]; } - this.emit_idle(queue_len); - + this.emit_idle(); utils.callback_or_emit(this, command_obj && command_obj.callback, err); }; @@ -627,8 +612,8 @@ RedisClient.prototype.drain = function () { this.should_buffer = false; }; -RedisClient.prototype.emit_idle = function (queue_len) { - if (queue_len === 0 && this.pub_sub_mode === false) { +RedisClient.prototype.emit_idle = function () { + if (this.command_queue.length === 0 && this.pub_sub_mode === false) { this.emit('idle'); } }; @@ -640,20 +625,6 @@ function queue_state_error (self, command_obj) { self.emit('error', err); } -function monitor (self, reply) { - if (typeof reply !== 'string') { - reply = reply.toString(); - } - // If in monitoring mode only two commands are valid ones: AUTH and MONITOR wich reply with OK - var len = reply.indexOf(' '); - var timestamp = reply.slice(0, len); - var argindex = reply.indexOf('"'); - var args = reply.slice(argindex + 1, -1).split('" "').map(function (elem) { - return elem.replace(/\\"/g, '"'); - }); - self.emit('monitor', timestamp, args); -} - function normal_reply (self, reply, command_obj) { if (typeof command_obj.callback === 'function') { if ('exec' !== command_obj.command) { @@ -716,7 +687,7 @@ RedisClient.prototype.return_reply = function (reply) { queue_len = this.command_queue.length; - this.emit_idle(queue_len); + this.emit_idle(); if (command_obj && !command_obj.sub_command) { normal_reply(this, reply, command_obj); @@ -724,9 +695,7 @@ RedisClient.prototype.return_reply = function (reply) { return_pub_sub(this, reply, command_obj); } /* istanbul ignore else: this is a safety check that we should not be able to trigger */ - else if (this.monitoring) { - monitor(this, reply); - } else { + else if (!this.monitoring) { queue_state_error(this, command_obj); } }; @@ -837,8 +806,6 @@ RedisClient.prototype.send_command = function (command, args, callback) { if (command === 'subscribe' || command === 'psubscribe' || command === 'unsubscribe' || command === 'punsubscribe') { this.pub_sub_command(command_obj); // TODO: This has to be moved to the result handler - } else if (command === 'monitor') { - this.monitoring = true; } else if (command === 'quit') { this.closing = true; } diff --git a/lib/individualCommands.js b/lib/individualCommands.js index 572228fe78e..f7b7341ec5b 100644 --- a/lib/individualCommands.js +++ b/lib/individualCommands.js @@ -33,6 +33,41 @@ RedisClient.prototype.select = RedisClient.prototype.SELECT = function select (d }); }; +RedisClient.prototype.monitor = RedisClient.prototype.MONITOR = function (callback) { + // Use a individual command, as this is a special case that does not has to be checked for any other command + var self = this; + return this.send_command('monitor', [], function (err, res) { + if (err === null) { + self.reply_parser.returnReply = function (reply) { + // If in monitor mode, all normal commands are still working and we only want to emit the streamlined commands + // As this is not the average use case and monitor is expensive anyway, let's change the code here, to improve + // the average performance of all other commands in case of no monitor mode + if (self.monitoring) { + var replyStr; + if (self.buffers && Buffer.isBuffer(reply)) { + replyStr = reply.toString(); + } else { + replyStr = reply; + } + // While reconnecting the redis server does not recognize the client as in monitor mode anymore + // Therefor the monitor command has to finish before it catches further commands + if (typeof replyStr === 'string' && utils.monitor_regex.test(replyStr)) { + var timestamp = replyStr.slice(0, replyStr.indexOf(' ')); + var args = replyStr.slice(replyStr.indexOf('"') + 1, -1).split('" "').map(function (elem) { + return elem.replace(/\\"/g, '"'); + }); + self.emit('monitor', timestamp, args, replyStr); + return; + } + } + self.return_reply(reply); + }; + self.monitoring = true; + } + utils.callback_or_emit(self, callback, err, res); + }); +}; + // Store info in this.server_info after each call RedisClient.prototype.info = RedisClient.prototype.INFO = function info (section, callback) { var self = this; diff --git a/lib/utils.js b/lib/utils.js index 069bf306452..b1b46c78296 100644 --- a/lib/utils.js +++ b/lib/utils.js @@ -39,8 +39,6 @@ function print (err, reply) { } } -var redisErrCode = /^([A-Z]+)\s+(.+)$/; - // Deep clone arbitrary objects with arrays. Can't handle cyclic structures (results in a range error) // Any attribute with a non primitive value besides object and array will be passed by reference (e.g. Buffers, Maps, Functions) function clone (obj) { @@ -102,7 +100,8 @@ module.exports = { reply_to_strings: replyToStrings, reply_to_object: replyToObject, print: print, - err_code: redisErrCode, + err_code: /^([A-Z]+)\s+(.+)$/, + monitor_regex: /^[0-9]{10,11}\.[0-9]+ \[[0-9]{1,3} [0-9]{1,3}\.[0-9]{1,3}\.[0-9]{1,3}\.[0-9]{1,3}:[0-9]{1,5}\].*/, clone: convenienceClone, callback_or_emit: callbackOrEmit, reply_in_order: replyInOrder diff --git a/test/node_redis.spec.js b/test/node_redis.spec.js index 44dc40f78f4..f45b95c3bdc 100644 --- a/test/node_redis.spec.js +++ b/test/node_redis.spec.js @@ -3,6 +3,7 @@ var assert = require("assert"); var config = require("./lib/config"); var helper = require('./helper'); +var utils = require('../lib/utils'); var fork = require("child_process").fork; var redis = config.redis; @@ -211,7 +212,7 @@ describe("The node_redis client", function () { it("return an error in the callback", function (done) { if (helper.redisProcess().spawnFailed()) this.skip(); - // TODO: Investigate why this test is failing hard and killing mocha. + // TODO: Investigate why this test is failing hard and killing mocha if using '/tmp/redis.sock'. // Seems like something is wrong with nyc while passing a socket connection to create client! client = redis.createClient(); client.quit(function() { @@ -366,34 +367,89 @@ describe("The node_redis client", function () { }); describe('monitor', function () { - it('monitors commands on all other redis clients', function (done) { + it('monitors commands on all redis clients and works in the correct order', function (done) { var monitorClient = redis.createClient.apply(null, args); var responses = []; + var end = helper.callFuncAfter(done, 5); + monitorClient.set('foo', 'bar'); + monitorClient.flushdb(); monitorClient.monitor(function (err, res) { + assert.strictEqual(res, 'OK'); client.mget("some", "keys", "foo", "bar"); client.set("json", JSON.stringify({ foo: "123", bar: "sdflkdfsjk", another: false })); + monitorClient.get('baz', function (err, res) { + assert.strictEqual(res, null); + end(err); + }); + monitorClient.set('foo', 'bar" "s are " " good!"', function (err, res) { + assert.strictEqual(res, 'OK'); + end(err); + }); + monitorClient.mget('foo', 'baz', function (err, res) { + assert.strictEqual(res[0], 'bar" "s are " " good!"'); + assert.strictEqual(res[1], null); + end(err); + }); + monitorClient.subscribe('foo', 'baz', function (err, res) { + // The return value might change in v.3 + // assert.strictEqual(res, 'baz'); + // TODO: Fix the return value of subscribe calls + end(err); + }); }); - monitorClient.on("monitor", function (time, args) { + monitorClient.on("monitor", function (time, args, rawOutput) { responses.push(args); - if (responses.length === 2) { - assert.strictEqual(5, responses[0].length); - assert.strictEqual("mget", responses[0][0]); - assert.strictEqual("some", responses[0][1]); - assert.strictEqual("keys", responses[0][2]); - assert.strictEqual("foo", responses[0][3]); - assert.strictEqual("bar", responses[0][4]); - assert.strictEqual(3, responses[1].length); - assert.strictEqual("set", responses[1][0]); - assert.strictEqual("json", responses[1][1]); - assert.strictEqual('{"foo":"123","bar":"sdflkdfsjk","another":false}', responses[1][2]); - monitorClient.quit(done); + assert(utils.monitor_regex.test(rawOutput), rawOutput); + if (responses.length === 6) { + assert.deepEqual(responses[0], ['mget', 'some', 'keys', 'foo', 'bar']); + assert.deepEqual(responses[1], ['set', 'json', '{"foo":"123","bar":"sdflkdfsjk","another":false}']); + assert.deepEqual(responses[2], ['get', 'baz']); + assert.deepEqual(responses[3], ['set', 'foo', 'bar" "s are " " good!"']); + assert.deepEqual(responses[4], ['mget', 'foo', 'baz']); + assert.deepEqual(responses[5], ['subscribe', 'foo', 'baz']); + monitorClient.quit(end); + } + }); + }); + + it('monitors returns strings in the rawOutput even with return_buffers activated', function (done) { + var monitorClient = redis.createClient({ + return_buffers: true + }); + + monitorClient.MONITOR(function (err, res) { + assert.strictEqual(res.inspect(), new Buffer('OK').inspect()); + client.mget("hello", new Buffer('world')); + }); + + monitorClient.on("monitor", function (time, args, rawOutput) { + assert.strictEqual(typeof rawOutput, 'string'); + assert(utils.monitor_regex.test(rawOutput), rawOutput); + assert.deepEqual(args, ['mget', 'hello', 'world']); + // Quit immediatly ends monitoring mode and therefor does not stream back the quit command + monitorClient.quit(done); + }); + }); + + it('monitors reconnects properly and works with the offline queue', function (done) { + var i = 0; + client.MONITOR(helper.isString('OK')); + client.mget("hello", 'world'); + client.on("monitor", function (time, args, rawOutput) { + assert(utils.monitor_regex.test(rawOutput), rawOutput); + assert.deepEqual(args, ['mget', 'hello', 'world']); + if (i++ === 2) { + // End after two reconnects + return done(); } + client.stream.destroy(); + client.mget("hello", 'world'); }); }); }); From a8e7d59e625c0c0f43a5df1057a1857b03944bed Mon Sep 17 00:00:00 2001 From: Ruben Bridgewater Date: Sat, 26 Mar 2016 00:41:50 +0100 Subject: [PATCH 0597/1748] Add a note about not prefixing the keys command Fixes #1012 --- README.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/README.md b/README.md index 2c03e795cdf..f5468354f84 100644 --- a/README.md +++ b/README.md @@ -215,7 +215,7 @@ limits total amount of connection tries. Setting this to 1 will prevent any reco * `rename_commands`: *null*; pass a object with renamed commands to use those instead of the original functions. See the [redis security topics](http://redis.io/topics/security) for more info. * `tls`: an object containing options to pass to [tls.connect](http://nodejs.org/api/tls.html#tls_tls_connect_port_host_options_callback), to set up a TLS connection to Redis (if, for example, it is set up to be accessible via a tunnel). -* `prefix`: *null*; pass a string to prefix all used keys with that string as prefix e.g. 'namespace:test' +* `prefix`: *null*; pass a string to prefix all used keys with that string as prefix e.g. 'namespace:test'. Please be aware, that the "keys" command is not going to be prefixed. The command has a "pattern" as argument and no key and it would be impossible to determine the existing keys in Redis if this would be prefixed. * `retry_strategy`: *function*; pass a function that receives a options object as parameter including the retry `attempt`, the `total_retry_time` indicating how much time passed since the last time connected, the `error` why the connection was lost and the number of `times_connected` in total. If you return a number from this function, the retry will happen exactly after that time in milliseconds. If you return a non-number no further retry is going to happen and all offline commands are flushed with errors. Return a error to return that specific error to all offline commands. Check out the example too. ```js From 7af90043c8a01c142319b09bdb00c8fa90d7ec6e Mon Sep 17 00:00:00 2001 From: Ruben Bridgewater Date: Sat, 26 Mar 2016 00:42:33 +0100 Subject: [PATCH 0598/1748] Improve test --- test/auth.spec.js | 6 ++++-- 1 file changed, 4 insertions(+), 2 deletions(-) diff --git a/test/auth.spec.js b/test/auth.spec.js index de55c3f23cb..331d0ad45fb 100644 --- a/test/auth.spec.js +++ b/test/auth.spec.js @@ -52,8 +52,10 @@ describe("client authentication", function () { var time = Date.now(); client.auth(auth, function (err, res) { assert.strictEqual('retry worked', res); - assert(Date.now() - time >= 200, 'Time should be above 200 ms (the reconnect time)'); - assert(Date.now() - time < 300, 'Time should be below 300 ms (the reconnect should only take a bit above 200 ms)'); + var now = Date.now(); + // Hint: setTimeout sometimes triggers early and therefor the value can be like one or two ms to early + assert(now - time >= 198, 'Time should be above 200 ms (the reconnect time) and is ' + (now - time)); + assert(now - time < 300, 'Time should be below 300 ms (the reconnect should only take a bit above 200 ms) and is ' + (now - time)); done(); }); var tmp = client.command_queue.get(0).callback; From 7a5a4aa535a86fcb07c22dc85ef2c7119d058745 Mon Sep 17 00:00:00 2001 From: Ruben Bridgewater Date: Fri, 25 Mar 2016 23:15:44 +0100 Subject: [PATCH 0599/1748] Fix pub sub mode There is likely a better and more performant way to fix this but this works so far and should be good enough to release and improve later. Make test more robust Add another test --- README.md | 38 ++--- changelog.md | 8 +- index.js | 238 +++++++++++++++-------------- lib/command.js | 14 +- test/auth.spec.js | 26 ++++ test/commands/hgetall.spec.js | 2 +- test/connection.spec.js | 3 + test/node_redis.spec.js | 75 ++++++++- test/pubsub.spec.js | 280 +++++++++++++++++++++++++++++++--- 9 files changed, 517 insertions(+), 167 deletions(-) diff --git a/README.md b/README.md index f5468354f84..f0d83e20c45 100644 --- a/README.md +++ b/README.md @@ -233,7 +233,7 @@ client.get("foo_rand000000000000", function (err, reply) { client.get(new Buffer("foo_rand000000000000"), function (err, reply) { console.log(reply.toString()); // Will print `` }); -client.end(); +client.quit(); ``` retry_strategy example @@ -302,7 +302,7 @@ client.get("foo_rand000000000000", function (err, reply) { }); ``` -`client.end()` without the flush parameter should NOT be used in production! +`client.end()` without the flush parameter set to true should NOT be used in production! ## client.unref() @@ -377,34 +377,34 @@ client connections, subscribes to a channel on one of them, and publishes to tha channel on the other: ```js -var redis = require("redis"), - client1 = redis.createClient(), client2 = redis.createClient(), - msg_count = 0; +var redis = require("redis"); +var sub = redis.createClient(), pub = redis.createClient(); +var msg_count = 0; -client1.on("subscribe", function (channel, count) { - client2.publish("a nice channel", "I am sending a message."); - client2.publish("a nice channel", "I am sending a second message."); - client2.publish("a nice channel", "I am sending my last message."); +sub.on("subscribe", function (channel, count) { + pub.publish("a nice channel", "I am sending a message."); + pub.publish("a nice channel", "I am sending a second message."); + pub.publish("a nice channel", "I am sending my last message."); }); -client1.on("message", function (channel, message) { - console.log("client1 channel " + channel + ": " + message); +sub.on("message", function (channel, message) { + console.log("sub channel " + channel + ": " + message); msg_count += 1; if (msg_count === 3) { - client1.unsubscribe(); - client1.end(); - client2.end(); + sub.unsubscribe(); + sub.quit(); + pub.quit(); } }); -client1.subscribe("a nice channel"); +sub.subscribe("a nice channel"); ``` When a client issues a `SUBSCRIBE` or `PSUBSCRIBE`, that connection is put into a "subscriber" mode. -At that point, only commands that modify the subscription set are valid. When the subscription +At that point, only commands that modify the subscription set are valid and quit (and depending on the redis version ping as well). When the subscription set is empty, the connection is put back into regular mode. -If you need to send regular commands to Redis while in subscriber mode, just open another connection. +If you need to send regular commands to Redis while in subscriber mode, just open another connection with a new client (hint: use `client.duplicate()`). ## Subscriber Events @@ -413,13 +413,13 @@ If a client has subscriptions active, it may emit these events: ### "message" (channel, message) Client will emit `message` for every message received that matches an active subscription. -Listeners are passed the channel name as `channel` and the message Buffer as `message`. +Listeners are passed the channel name as `channel` and the message as `message`. ### "pmessage" (pattern, channel, message) Client will emit `pmessage` for every message received that matches an active subscription pattern. Listeners are passed the original pattern used with `PSUBSCRIBE` as `pattern`, the sending channel -name as `channel`, and the message Buffer as `message`. +name as `channel`, and the message as `message`. ### "subscribe" (channel, count) diff --git a/changelog.md b/changelog.md index 992eb422166..69c5c6d39c5 100644 --- a/changelog.md +++ b/changelog.md @@ -5,15 +5,21 @@ Changelog Features -- Monitor now works together with the offline queue +- Monitor and pub sub mode now work together with the offline queue - All commands that were send after a connection loss are now going to be send after reconnecting - Activating monitor mode does now work together with arbitrary commands including pub sub mode +- Pub sub mode is completly rewritten and all known issues fixed Bugfixes - Fixed calling monitor command while other commands are still running - Fixed monitor and pub sub mode not working together - Fixed monitor mode not working in combination with the offline queue +- Fixed pub sub mode not working in combination with the offline queue +- Fixed pub sub mode resubscribing not working with non utf8 buffer channels +- Fixed pub sub mode crashing if calling unsubscribe / subscribe in various combinations +- Fixed pub sub mode emitting unsubscribe even if no channels were unsubscribed +- Fixed pub sub mode emitting a message without a message published ## v.2.5.3 - 21 Mar, 2016 diff --git a/index.js b/index.js index 90a56d68e76..8e13499cb4f 100644 --- a/index.js +++ b/index.js @@ -5,7 +5,8 @@ var tls = require('tls'); var util = require('util'); var utils = require('./lib/utils'); var Queue = require('double-ended-queue'); -var Command = require('./lib/command'); +var Command = require('./lib/command').Command; +var OfflineCommand = require('./lib/command').OfflineCommand; var EventEmitter = require('events'); var Parser = require('redis-parser'); var commands = require('redis-commands'); @@ -128,7 +129,7 @@ function RedisClient (options, stream) { ); } this.initialize_retry_vars(); - this.pub_sub_mode = false; + this.pub_sub_mode = 0; this.subscription_set = {}; this.monitoring = false; this.closing = false; @@ -222,6 +223,7 @@ RedisClient.prototype.create_stream = function () { // The buffer_from_socket.toString() has a significant impact on big chunks and therefor this should only be used if necessary debug('Net read ' + self.address + ' id ' + self.connection_id); // + ': ' + buffer_from_socket.toString()); self.reply_parser.execute(buffer_from_socket); + self.emit_idle(); }); this.stream.on('error', function (err) { @@ -386,7 +388,7 @@ RedisClient.prototype.on_ready = function () { } this.cork = cork; - // restore modal commands from previous connection. The order of the commands is important + // Restore modal commands from previous connection. The order of the commands is important if (this.selected_db !== undefined) { this.send_command('select', [this.selected_db]); } @@ -394,31 +396,29 @@ RedisClient.prototype.on_ready = function () { this.monitoring = this.old_state.monitoring; this.pub_sub_mode = this.old_state.pub_sub_mode; } - if (this.pub_sub_mode) { + if (this.monitoring) { // Monitor has to be fired before pub sub commands + this.send_command('monitor', []); + } + var callback_count = Object.keys(this.subscription_set).length; + if (!this.options.disable_resubscribing && callback_count) { // only emit 'ready' when all subscriptions were made again - var callback_count = 0; + // TODO: Remove the countdown for ready here. This is not coherent with all other modes and should therefor not be handled special + // We know we are ready as soon as all commands were fired var callback = function () { callback_count--; if (callback_count === 0) { self.emit('ready'); } }; - if (this.options.disable_resubscribing) { - this.emit('ready'); - return; + debug('Sending pub/sub on_ready commands'); + for (var key in this.subscription_set) { // jshint ignore: line + var command = key.slice(0, key.indexOf('_')); + var args = self.subscription_set[key]; + self.send_command(command, [args], callback); } - Object.keys(this.subscription_set).forEach(function (key) { - var space_index = key.indexOf(' '); - var parts = [key.slice(0, space_index), key.slice(space_index + 1)]; - debug('Sending pub/sub on_ready ' + parts[0] + ', ' + parts[1]); - callback_count++; - self.send_command(parts[0] + 'scribe', [parts[1]], callback); - }); + this.send_offline_queue(); return; } - if (this.monitoring) { - this.send_command('monitor', []); - } this.send_offline_queue(); this.emit('ready'); }; @@ -521,7 +521,7 @@ RedisClient.prototype.connection_gone = function (why, error) { }; this.old_state = state; this.monitoring = false; - this.pub_sub_mode = false; + this.pub_sub_mode = 0; // since we are collapsing end and close, users don't expect to be called twice if (!this.emitted_end) { @@ -603,7 +603,6 @@ RedisClient.prototype.return_error = function (err) { err.code = match[1]; } - this.emit_idle(); utils.callback_or_emit(this, command_obj && command_obj.callback, err); }; @@ -613,19 +612,13 @@ RedisClient.prototype.drain = function () { }; RedisClient.prototype.emit_idle = function () { - if (this.command_queue.length === 0 && this.pub_sub_mode === false) { + if (this.command_queue.length === 0 && this.pub_sub_mode === 0) { this.emit('idle'); } }; -/* istanbul ignore next: this is a safety check that we should not be able to trigger */ -function queue_state_error (self, command_obj) { - var err = new Error('node_redis command queue state error. If you can reproduce this, please report it.'); - err.command_obj = command_obj; - self.emit('error', err); -} - -function normal_reply (self, reply, command_obj) { +function normal_reply (self, reply) { + var command_obj = self.command_queue.shift(); if (typeof command_obj.callback === 'function') { if ('exec' !== command_obj.command) { reply = self.handle_reply(reply, command_obj.command, command_obj.buffer_args); @@ -636,67 +629,107 @@ function normal_reply (self, reply, command_obj) { } } -function return_pub_sub (self, reply, command_obj) { - if (reply instanceof Array) { - if ((!command_obj || command_obj.buffer_args === false) && !self.options.return_buffers) { - reply = utils.reply_to_strings(reply); +function set_subscribe (self, type, command_obj, subscribe, reply) { + var i = 0; + if (subscribe) { + // The channels have to be saved one after the other and the type has to be the same too, + // to make sure partly subscribe / unsubscribe works well together + for (; i < command_obj.args.length; i++) { + self.subscription_set[type + '_' + command_obj.args[i]] = command_obj.args[i]; } - var type = reply[0].toString(); - - // TODO: Add buffer emiters (we have to get all pubsub messages as buffers back in that case) - if (type === 'message') { - self.emit('message', reply[1], reply[2]); // channcel, message - } else if (type === 'pmessage') { - self.emit('pmessage', reply[1], reply[2], reply[3]); // pattern, channcel, message - } else if (type === 'subscribe' || type === 'unsubscribe' || type === 'psubscribe' || type === 'punsubscribe') { - if (reply[2].toString() === '0') { - self.pub_sub_mode = false; - debug('All subscriptions removed, exiting pub/sub mode'); - } else { - self.pub_sub_mode = true; - } - // Subscribe commands take an optional callback and also emit an event, but only the first response is included in the callback - // TODO - document this or fix it so it works in a more obvious way - if (command_obj && typeof command_obj.callback === 'function') { - command_obj.callback(null, reply[1]); + } else { + type = type === 'unsubscribe' ? 'subscribe' : 'psubscribe'; // Make types consistent + for (; i < command_obj.args.length; i++) { + delete self.subscription_set[type + '_' + command_obj.args[i]]; + } + if (reply[2] === 0) { // No channels left that this client is subscribed to + var running_command; + i = 0; + // This should be a rare case and therefor handling it this way should be good performance wise for the general case + while (running_command = self.command_queue.get(i++)) { + if ( + running_command.command === 'subscribe' || + running_command.command === 'psubscribe' || + running_command.command === 'unsubscribe' || + running_command.command === 'punsubscribe' + ) { + self.pub_sub_mode = i; + return; + } } - self.emit(type, reply[1], reply[2]); // channcel, count - } else { - self.emit('error', new Error('subscriptions are active but got unknown reply type ' + type)); + self.pub_sub_mode = 0; } - } else if (!self.closing) { - self.emit('error', new Error('subscriptions are active but got an invalid reply: ' + reply)); } } -RedisClient.prototype.return_reply = function (reply) { - var command_obj, type, queue_len; - - // If the 'reply' here is actually a message received asynchronously due to a - // pubsub subscription, don't pop the command queue as we'll only be consuming - // the head command prematurely. - if (this.pub_sub_mode && reply instanceof Array && reply[0]) { - type = reply[0].toString(); +function subscribe_unsubscribe (self, reply, type, subscribe) { + // Subscribe commands take an optional callback and also emit an event, but only the _last_ response is included in the callback + var command_obj = self.command_queue.get(0); + var buffer = self.options.return_buffers || self.options.detect_buffers && command_obj && command_obj.buffer_args || reply[1] === null; + var channel = buffer ? reply[1] : reply[1].toString(); + var count = reply[2]; + debug('Subscribe / unsubscribe command'); + + // Emit first, then return the callback + if (channel !== null) { // Do not emit something if there was no channel to unsubscribe from + self.emit(type, channel, count); + } + // The pub sub commands return each argument in a separate return value and have to be handled that way + if (command_obj.sub_commands_left <= 1) { + if (count !== 0 && !subscribe && command_obj.args.length === 0) { + command_obj.sub_commands_left = count; + return; + } + self.command_queue.shift(); + set_subscribe(self, type, command_obj, subscribe, reply); + if (typeof command_obj.callback === 'function') { + // TODO: The current return value is pretty useless. + // Evaluate to change this in v.3 to return all subscribed / unsubscribed channels in an array including the number of channels subscribed too + command_obj.callback(null, channel); + } + } else { + command_obj.sub_commands_left--; } +} - if (this.pub_sub_mode && (type === 'message' || type === 'pmessage')) { - debug('Received pubsub message'); +function return_pub_sub (self, reply) { + var type = reply[0].toString(); + if (type === 'message') { // channel, message + // TODO: Implement message_buffer + // if (self.buffers) { + // self.emit('message_buffer', reply[1], reply[2]); + // } + if (!self.options.return_buffers) { // backwards compatible. Refactor this in v.3 to always return a string on the normal emitter + self.emit('message', reply[1].toString(), reply[2].toString()); + } else { + self.emit('message', reply[1], reply[2]); + } + } else if (type === 'pmessage') { // pattern, channel, message + // if (self.buffers) { + // self.emit('pmessage_buffer', reply[1], reply[2], reply[3]); + // } + if (!self.options.return_buffers) { // backwards compatible. Refactor this in v.3 to always return a string on the normal emitter + self.emit('pmessage', reply[1].toString(), reply[2].toString(), reply[3].toString()); + } else { + self.emit('pmessage', reply[1], reply[2], reply[3]); + } + } else if (type === 'subscribe' || type === 'psubscribe') { + subscribe_unsubscribe(self, reply, type, true); + } else if (type === 'unsubscribe' || type === 'punsubscribe') { + subscribe_unsubscribe(self, reply, type, false); } else { - command_obj = this.command_queue.shift(); + normal_reply(self, reply); } +} - queue_len = this.command_queue.length; - - this.emit_idle(); - - if (command_obj && !command_obj.sub_command) { - normal_reply(this, reply, command_obj); - } else if (this.pub_sub_mode || command_obj && command_obj.sub_command) { - return_pub_sub(this, reply, command_obj); - } - /* istanbul ignore else: this is a safety check that we should not be able to trigger */ - else if (!this.monitoring) { - queue_state_error(this, command_obj); +RedisClient.prototype.return_reply = function (reply) { + if (this.pub_sub_mode === 1 && reply instanceof Array && reply.length !== 0 && reply[0]) { + return_pub_sub(this, reply); + } else { + if (this.pub_sub_mode !== 0 && this.pub_sub_mode !== 1) { + this.pub_sub_mode--; + } + normal_reply(this, reply); } }; @@ -731,16 +764,15 @@ RedisClient.prototype.send_command = function (command, args, callback) { var command_str = ''; var len = 0; var big_data = false; + var buffer_args = false; if (process.domain && callback) { callback = process.domain.bind(callback); } - var command_obj = new Command(command, args, callback); - if (this.ready === false || this.stream.writable === false) { // Handle offline commands right away - handle_offline_command(this, command_obj); + handle_offline_command(this, new OfflineCommand(command, args, callback)); return false; // Indicate buffering } @@ -776,7 +808,7 @@ RedisClient.prototype.send_command = function (command, args, callback) { args_copy[i] = 'null'; // Backwards compatible :/ } else if (Buffer.isBuffer(args[i])) { args_copy[i] = args[i]; - command_obj.buffer_args = true; + buffer_args = true; big_data = true; if (this.pipeline !== 0) { this.pipeline += 2; @@ -803,9 +835,15 @@ RedisClient.prototype.send_command = function (command, args, callback) { } } args = null; + var command_obj = new Command(command, args_copy, callback); + command_obj.buffer_args = buffer_args; if (command === 'subscribe' || command === 'psubscribe' || command === 'unsubscribe' || command === 'punsubscribe') { - this.pub_sub_command(command_obj); // TODO: This has to be moved to the result handler + // If pub sub is already activated, keep it that way, otherwise set the number of commands to resolve until pub sub mode activates + // Deactivation of the pub sub mode happens in the result handler + if (!this.pub_sub_mode) { + this.pub_sub_mode = this.command_queue.length + 1; + } } else if (command === 'quit') { this.closing = true; } @@ -886,38 +924,6 @@ RedisClient.prototype.write = function (data) { return; }; -RedisClient.prototype.pub_sub_command = function (command_obj) { - var i, key, command, args; - - if (this.pub_sub_mode === false) { - debug('Entering pub/sub mode from ' + command_obj.command); - } - this.pub_sub_mode = true; - command_obj.sub_command = true; - - command = command_obj.command; - args = command_obj.args; - if (command === 'subscribe' || command === 'psubscribe') { - if (command === 'subscribe') { - key = 'sub'; - } else { - key = 'psub'; - } - for (i = 0; i < args.length; i++) { - this.subscription_set[key + ' ' + args[i]] = true; - } - } else { - if (command === 'unsubscribe') { - key = 'sub'; - } else { - key = 'psub'; - } - for (i = 0; i < args.length; i++) { - delete this.subscription_set[key + ' ' + args[i]]; - } - } -}; - RedisClient.prototype.end = function (flush) { // Flush queue if wanted if (flush) { diff --git a/lib/command.js b/lib/command.js index 0c01976bb7c..20b7589db2f 100644 --- a/lib/command.js +++ b/lib/command.js @@ -4,9 +4,19 @@ // a named constructor helps it show up meaningfully in the V8 CPU profiler and in heap snapshots. function Command(command, args, callback) { this.command = command; - this.args = args; + this.args = args; // We only need the args for the offline commands => move them into another class. We need the number of args though for pub sub this.buffer_args = false; this.callback = callback; + this.sub_commands_left = args.length; +} + +function OfflineCommand(command, args, callback) { + this.command = command; + this.args = args; + this.callback = callback; } -module.exports = Command; +module.exports = { + Command: Command, + OfflineCommand: OfflineCommand +}; diff --git a/test/auth.spec.js b/test/auth.spec.js index 331d0ad45fb..cb78e2ee659 100644 --- a/test/auth.spec.js +++ b/test/auth.spec.js @@ -255,6 +255,32 @@ describe("client authentication", function () { done(); }); }); + + it('pubsub working with auth', function (done) { + if (helper.redisProcess().spawnFailed()) this.skip(); + + var args = config.configureClient(parser, ip, { + password: auth + }); + client = redis.createClient.apply(redis.createClient, args); + client.set('foo', 'bar'); + client.subscribe('somechannel', 'another channel', function (err, res) { + client.once('ready', function () { + assert.strictEqual(client.pub_sub_mode, 1); + client.get('foo', function (err, res) { + assert.strictEqual(err.message, 'ERR only (P)SUBSCRIBE / (P)UNSUBSCRIBE / QUIT allowed in this context'); + done(); + }); + }); + }); + client.once('ready', function () { + // Coherent behavior with all other offline commands fires commands before emitting but does not wait till they return + assert.strictEqual(client.pub_sub_mode, 2); + client.ping(function () { // Make sure all commands were properly processed already + client.stream.destroy(); + }); + }); + }); }); }); diff --git a/test/commands/hgetall.spec.js b/test/commands/hgetall.spec.js index 0195ba5f658..e6813798088 100644 --- a/test/commands/hgetall.spec.js +++ b/test/commands/hgetall.spec.js @@ -33,7 +33,7 @@ describe("The 'hgetall' method", function () { }); it('handles fetching keys set using an object', function (done) { - client.HMSET("msg_test", {message: "hello"}, helper.isString("OK")); + client.HMSET("msg_test", { message: "hello" }, helper.isString("OK")); client.hgetall("msg_test", function (err, obj) { assert.strictEqual(1, Object.keys(obj).length); assert.strictEqual(obj.message, "hello"); diff --git a/test/connection.spec.js b/test/connection.spec.js index 71ecf1c9b59..1f60111f238 100644 --- a/test/connection.spec.js +++ b/test/connection.spec.js @@ -230,6 +230,9 @@ describe("connection tests", function () { }); client.on('error', function(err) { + if (err.code === 'ENETUNREACH') { // The test is run without a internet connection. Pretent it works + return done(); + } assert(/Redis connection in broken state: connection timeout.*?exceeded./.test(err.message)); // The code execution on windows is very slow at times var add = process.platform !== 'win32' ? 25 : 125; diff --git a/test/node_redis.spec.js b/test/node_redis.spec.js index f45b95c3bdc..11b35e6dc4b 100644 --- a/test/node_redis.spec.js +++ b/test/node_redis.spec.js @@ -303,12 +303,7 @@ describe("The node_redis client", function () { }); }); - // TODO: we should only have a single subscription in this this - // test but unsubscribing from the single channel indicates - // that one subscriber still exists, let's dig into this. describe("and it's subscribed to a channel", function () { - // reconnect_select_db_after_pubsub - // Does not pass. // "Connection in subscriber mode, only subscriber commands may be used" it("reconnects, unsubscribes, and can retrieve the pre-existing data", function (done) { client.on("ready", function on_connect() { @@ -316,6 +311,28 @@ describe("The node_redis client", function () { client.on('unsubscribe', function (channel, count) { // we should now be out of subscriber mode. + assert.strictEqual(channel, "recon channel"); + assert.strictEqual(count, 0); + client.set('foo', 'bar', helper.isString('OK', done)); + }); + }); + + client.set("recon 1", "one"); + client.subscribe("recon channel", function (err, res) { + // Do not do this in normal programs. This is to simulate the server closing on us. + // For orderly shutdown in normal programs, do client.quit() + client.stream.destroy(); + }); + }); + + it("reconnects, unsubscribes, and can retrieve the pre-existing data of a explicit channel", function (done) { + client.on("ready", function on_connect() { + client.unsubscribe('recon channel', helper.isNotError()); + + client.on('unsubscribe', function (channel, count) { + // we should now be out of subscriber mode. + assert.strictEqual(channel, "recon channel"); + assert.strictEqual(count, 0); client.set('foo', 'bar', helper.isString('OK', done)); }); }); @@ -452,6 +469,54 @@ describe("The node_redis client", function () { client.mget("hello", 'world'); }); }); + + it('monitors works in combination with the pub sub mode and the offline queue', function (done) { + var responses = []; + var pub = redis.createClient(); + pub.on('ready', function () { + client.MONITOR(function (err, res) { + assert.strictEqual(res, 'OK'); + pub.get('foo', helper.isNull()); + }); + client.subscribe('/foo', '/bar'); + client.unsubscribe('/bar'); + setTimeout(function () { + client.stream.destroy(); + client.once('ready', function () { + pub.publish('/foo', 'hello world'); + }); + client.set('foo', 'bar', helper.isError()); + client.subscribe('baz'); + client.unsubscribe('baz'); + }, 150); + var called = false; + client.on("monitor", function (time, args, rawOutput) { + responses.push(args); + assert(utils.monitor_regex.test(rawOutput), rawOutput); + if (responses.length === 7) { + assert.deepEqual(responses[0], ['subscribe', '/foo', '/bar']); + assert.deepEqual(responses[1], ['unsubscribe', '/bar']); + assert.deepEqual(responses[2], ['get', 'foo']); + assert.deepEqual(responses[3], ['subscribe', '/foo']); + assert.deepEqual(responses[4], ['subscribe', 'baz']); + assert.deepEqual(responses[5], ['unsubscribe', 'baz']); + assert.deepEqual(responses[6], ['publish', '/foo', 'hello world']); + // The publish is called right after the reconnect and the monitor is called before the message is emitted. + // Therefor we have to wait till the next tick + process.nextTick(function () { + assert(called); + client.quit(done); + pub.end(false); + }); + } + }); + client.on('message', function (channel, msg) { + assert.strictEqual(channel, '/foo'); + assert.strictEqual(msg, 'hello world'); + called = true; + }); + }); + }); }); describe('idle', function () { diff --git a/test/pubsub.spec.js b/test/pubsub.spec.js index 081399fd484..2e54e8e8836 100644 --- a/test/pubsub.spec.js +++ b/test/pubsub.spec.js @@ -85,6 +85,29 @@ describe("publish/subscribe", function () { sub.subscribe(channel, channel2); }); + it('fires a subscribe event for each channel as buffer subscribed to even after reconnecting', function (done) { + var a = false; + sub.end(true); + sub = redis.createClient({ + detect_buffers: true + }); + sub.on("subscribe", function (chnl, count) { + if (chnl.inspect() === new Buffer([0xAA, 0xBB, 0x00, 0xF0]).inspect()) { + assert.equal(1, count); + if (a) { + return done(); + } + sub.stream.destroy(); + } + }); + + sub.on('reconnecting', function() { + a = true; + }); + + sub.subscribe(new Buffer([0xAA, 0xBB, 0x00, 0xF0]), channel2); + }); + it('receives messages on subscribed channel', function (done) { var end = helper.callFuncAfter(done, 2); sub.on("subscribe", function (chnl, count) { @@ -199,6 +222,216 @@ describe("publish/subscribe", function () { }); }); + describe("multiple subscribe / unsubscribe commands", function () { + + it("reconnects properly with pub sub and select command", function (done) { + var end = helper.callFuncAfter(done, 2); + sub.select(3); + sub.set('foo', 'bar'); + sub.subscribe('somechannel', 'another channel', function (err, res) { + end(); + sub.stream.destroy(); + }); + assert(sub.ready); + sub.on('ready', function () { + sub.unsubscribe(); + sub.del('foo'); + sub.info(end); + }); + }); + + it("should not go into pubsub mode with unsubscribe commands", function (done) { + sub.on('unsubscribe', function (msg) { + // The unsubscribe should not be triggered, as there was no corresponding channel + throw new Error('Test failed'); + }); + sub.set('foo', 'bar'); + sub.unsubscribe(function (err, res) { + assert.strictEqual(res, null); + }); + sub.del('foo', done); + }); + + it("handles multiple channels with the same channel name properly, even with buffers", function (done) { + var channels = ['a', 'b', 'a', new Buffer('a'), 'c', 'b']; + var subscribed_channels = [1, 2, 2, 2, 3, 3]; + var i = 0; + sub.subscribe(channels); + sub.on('subscribe', function (channel, count) { + if (Buffer.isBuffer(channel)) { + assert.strictEqual(channel.inspect(), new Buffer(channels[i]).inspect()); + } else { + assert.strictEqual(channel, channels[i].toString()); + } + assert.strictEqual(count, subscribed_channels[i]); + i++; + }); + sub.unsubscribe('a', 'c', 'b'); + sub.get('foo', done); + }); + + it('should only resubscribe to channels not unsubscribed earlier on a reconnect', function (done) { + sub.subscribe('/foo', '/bar'); + sub.unsubscribe('/bar', function () { + pub.pubsub('channels', function (err, res) { + assert.deepEqual(res, ['/foo']); + sub.stream.destroy(); + sub.once('ready', function () { + pub.pubsub('channels', function (err, res) { + assert.deepEqual(res, ['/foo']); + sub.unsubscribe('/foo', done); + }); + }); + }); + }); + }); + + it("unsubscribes, subscribes, unsubscribes... single and multiple entries mixed. Withouth callbacks", function (done) { + function subscribe(channels) { + sub.unsubscribe(helper.isNull); + sub.subscribe(channels, helper.isNull); + } + var all = false; + var subscribeMsg = ['1', '3', '2', '5', 'test', 'bla']; + sub.on('subscribe', function(msg, count) { + subscribeMsg.splice(subscribeMsg.indexOf(msg), 1); + if (subscribeMsg.length === 0 && all) { + assert.strictEqual(count, 3); + done(); + } + }); + var unsubscribeMsg = ['1', '3', '2']; + sub.on('unsubscribe', function(msg, count) { + unsubscribeMsg.splice(unsubscribeMsg.indexOf(msg), 1); + if (unsubscribeMsg.length === 0) { + assert.strictEqual(count, 0); + all = true; + } + }); + + subscribe(['1', '3']); + subscribe(['2']); + subscribe(['5', 'test', 'bla']); + }); + + it("unsubscribes, subscribes, unsubscribes... single and multiple entries mixed. Without callbacks", function (done) { + function subscribe(channels) { + sub.unsubscribe(); + sub.subscribe(channels); + } + var all = false; + var subscribeMsg = ['1', '3', '2', '5', 'test', 'bla']; + sub.on('subscribe', function(msg, count) { + subscribeMsg.splice(subscribeMsg.indexOf(msg), 1); + if (subscribeMsg.length === 0 && all) { + assert.strictEqual(count, 3); + done(); + } + }); + var unsubscribeMsg = ['1', '3', '2']; + sub.on('unsubscribe', function(msg, count) { + unsubscribeMsg.splice(unsubscribeMsg.indexOf(msg), 1); + if (unsubscribeMsg.length === 0) { + assert.strictEqual(count, 0); + all = true; + } + }); + + subscribe(['1', '3']); + subscribe(['2']); + subscribe(['5', 'test', 'bla']); + }); + + it("unsubscribes, subscribes, unsubscribes... single and multiple entries mixed. Without callback and concret channels", function (done) { + function subscribe(channels) { + sub.unsubscribe(channels); + sub.unsubscribe(channels); + sub.subscribe(channels); + } + var all = false; + var subscribeMsg = ['1', '3', '2', '5', 'test', 'bla']; + sub.on('subscribe', function(msg, count) { + subscribeMsg.splice(subscribeMsg.indexOf(msg), 1); + if (subscribeMsg.length === 0 && all) { + assert.strictEqual(count, 6); + done(); + } + }); + var unsubscribeMsg = ['1', '3', '2', '5', 'test', 'bla']; + sub.on('unsubscribe', function(msg, count) { + var pos = unsubscribeMsg.indexOf(msg); + if (pos !== -1) + unsubscribeMsg.splice(pos, 1); + if (unsubscribeMsg.length === 0) { + all = true; + } + }); + + subscribe(['1', '3']); + subscribe(['2']); + subscribe(['5', 'test', 'bla']); + }); + + it("unsubscribes, subscribes, unsubscribes... with pattern matching", function (done) { + function subscribe(channels, callback) { + sub.punsubscribe('prefix:*', helper.isNull); + sub.psubscribe(channels, function (err, res) { + helper.isNull(err); + if (callback) callback(err, res); + }); + } + var all = false; + var end = helper.callFuncAfter(done, 8); + var subscribeMsg = ['prefix:*', 'prefix:3', 'prefix:2', '5', 'test:a', 'bla']; + sub.on('psubscribe', function(msg, count) { + subscribeMsg.splice(subscribeMsg.indexOf(msg), 1); + if (subscribeMsg.length === 0) { + assert.strictEqual(count, 5); + all = true; + } + }); + var rest = 1; + var unsubscribeMsg = ['prefix:*', 'prefix:*', 'prefix:*', '*']; + sub.on('punsubscribe', function(msg, count) { + unsubscribeMsg.splice(unsubscribeMsg.indexOf(msg), 1); + if (all) { + assert.strictEqual(unsubscribeMsg.length, 0); + assert.strictEqual(count, rest--); // Print the remaining channels + end(); + } else { + assert.strictEqual(msg, 'prefix:*'); + assert.strictEqual(count, rest++ - 1); + } + }); + sub.on('pmessage', function (pattern, channel, msg) { + assert.strictEqual(msg, 'test'); + assert.strictEqual(pattern, 'prefix:*'); + assert.strictEqual(channel, 'prefix:1'); + end(); + }); + + subscribe(['prefix:*', 'prefix:3'], function () { + pub.publish('prefix:1', new Buffer('test'), function () { + subscribe(['prefix:2']); + subscribe(['5', 'test:a', 'bla'], function () { + assert(all); + }); + sub.punsubscribe(function (err, res) { + assert(!err); + assert.strictEqual(res, 'bla'); + assert(all); + all = false; // Make sure the callback is actually after the emit + end(); + }); + sub.pubsub('channels', function (err, res) { + assert.strictEqual(res.length, 0); + end(); + }); + }); + }); + }); + }); + describe('unsubscribe', function () { it('fires an unsubscribe event', function (done) { sub.on("subscribe", function (chnl, count) { @@ -237,22 +470,27 @@ describe("publish/subscribe", function () { it('executes callback when unsubscribe is called and there are no subscriptions', function (done) { pub.unsubscribe(function (err, results) { assert.strictEqual(null, results); - return done(err); + done(err); }); }); }); describe('psubscribe', function () { - // test motivated by issue #753 it('allows all channels to be subscribed to using a * pattern', function (done) { - sub.psubscribe('*'); - sub.on("pmessage", function(pattern, channel, message) { - assert.strictEqual(pattern, '*'); - assert.strictEqual(channel, '/foo'); - assert.strictEqual(message, 'hello world'); - return done(); + sub.end(false); + sub = redis.createClient({ + return_buffers: true + }); + sub.on('ready', function () { + sub.psubscribe('*'); + sub.on("pmessage", function(pattern, channel, message) { + assert.strictEqual(pattern.inspect(), new Buffer('*').inspect()); + assert.strictEqual(channel.inspect(), new Buffer('/foo').inspect()); + assert.strictEqual(message.inspect(), new Buffer('hello world').inspect()); + done(); + }); + pub.publish('/foo', 'hello world'); }); - pub.publish('/foo', 'hello world'); }); }); @@ -264,7 +502,7 @@ describe("publish/subscribe", function () { it('executes callback when punsubscribe is called and there are no subscriptions', function (done) { pub.punsubscribe(function (err, results) { assert.strictEqual(null, results); - return done(err); + done(err); }); }); }); @@ -330,19 +568,15 @@ describe("publish/subscribe", function () { }, 40); }); - // TODO: Fix pub sub - // And there's more than just those two issues - describe.skip('FIXME: broken pub sub', function () { - - it("should not publish a message without any publish command", function (done) { - pub.set('foo', 'message'); - pub.set('bar', 'hello'); - pub.mget('foo', 'bar'); - pub.subscribe('channel'); - pub.on('message', function (msg) { - done(new Error('This message should not have been published: ' + msg)); - }); - setTimeout(done, 200); + it("should not publish a message without any publish command", function (done) { + pub.set('foo', 'message'); + pub.set('bar', 'hello'); + pub.mget('foo', 'bar'); + pub.subscribe('channel', function () { + setTimeout(done, 50); + }); + pub.on('message', function (msg) { + done(new Error('This message should not have been published: ' + msg)); }); }); From 12579e5e8e7f992f99c7ee36e12214256d27c017 Mon Sep 17 00:00:00 2001 From: Ruben Bridgewater Date: Sat, 26 Mar 2016 04:53:27 +0100 Subject: [PATCH 0600/1748] Test pubsub numsub Closes #740 --- test/pubsub.spec.js | 18 +++++++++++------- 1 file changed, 11 insertions(+), 7 deletions(-) diff --git a/test/pubsub.spec.js b/test/pubsub.spec.js index 2e54e8e8836..cefe33f5d04 100644 --- a/test/pubsub.spec.js +++ b/test/pubsub.spec.js @@ -477,19 +477,23 @@ describe("publish/subscribe", function () { describe('psubscribe', function () { it('allows all channels to be subscribed to using a * pattern', function (done) { - sub.end(false); - sub = redis.createClient({ + sub.subscribe('/foo'); + var sub2 = redis.createClient({ return_buffers: true }); - sub.on('ready', function () { - sub.psubscribe('*'); - sub.on("pmessage", function(pattern, channel, message) { + sub2.on('ready', function () { + sub2.psubscribe('*'); + sub2.subscribe('/foo'); + sub2.on("pmessage", function(pattern, channel, message) { assert.strictEqual(pattern.inspect(), new Buffer('*').inspect()); assert.strictEqual(channel.inspect(), new Buffer('/foo').inspect()); assert.strictEqual(message.inspect(), new Buffer('hello world').inspect()); - done(); + sub2.quit(done); + }); + pub.pubsub('numsub', '/foo', function (err, res) { + assert.deepEqual(res, ['/foo', 2]); }); - pub.publish('/foo', 'hello world'); + pub.publish('/foo', 'hello world', helper.isNumber(3)); }); }); }); From 94e9f1fcfc3845a60f4eafa576477e7a4c5edd03 Mon Sep 17 00:00:00 2001 From: Ruben Bridgewater Date: Sat, 26 Mar 2016 04:05:43 +0100 Subject: [PATCH 0601/1748] Replace jshint with eslint and add lots of rules Fix eslint errors accordingly --- .jshintignore => .eslintignore | 2 +- .eslintrc | 108 ++++++++++ .jshintrc | 27 --- benchmarks/diff_multi_bench_output.js | 14 +- benchmarks/multi_bench.js | 12 +- examples/auth.js | 8 +- examples/backpressure_drain.js | 9 +- examples/eval.js | 4 +- examples/extend.js | 4 +- examples/file.js | 14 +- examples/mget.js | 2 +- examples/monitor.js | 4 +- examples/multi.js | 5 +- examples/multi2.js | 6 +- examples/psubscribe.js | 14 +- examples/pub_sub.js | 7 +- examples/scan.js | 9 +- examples/simple.js | 4 +- examples/sort.js | 6 +- examples/unix_socket.js | 9 +- examples/web_server.js | 6 +- index.js | 4 +- lib/command.js | 4 +- lib/multi.js | 6 +- lib/utils.js | 4 +- package.json | 4 +- test/auth.spec.js | 38 ++-- test/batch.spec.js | 147 ++++++------- test/commands/blpop.spec.js | 40 ++-- test/commands/client.spec.js | 34 +-- test/commands/dbsize.spec.js | 30 +-- test/commands/del.spec.js | 10 +- test/commands/eval.spec.js | 88 ++++---- test/commands/exists.spec.js | 10 +- test/commands/expire.spec.js | 22 +- test/commands/flushdb.spec.js | 44 ++-- test/commands/geoadd.spec.js | 12 +- test/commands/get.spec.js | 28 +-- test/commands/getset.spec.js | 28 +-- test/commands/hgetall.spec.js | 42 ++-- test/commands/hincrby.spec.js | 16 +- test/commands/hlen.spec.js | 16 +- test/commands/hmget.spec.js | 40 ++-- test/commands/hmset.spec.js | 20 +- test/commands/hset.spec.js | 46 ++-- test/commands/incr.spec.js | 52 ++--- test/commands/info.spec.js | 14 +- test/commands/keys.spec.js | 34 +-- test/commands/mget.spec.js | 50 ++--- test/commands/mset.spec.js | 36 ++-- test/commands/msetnx.spec.js | 22 +- test/commands/randomkey.test.js | 14 +- test/commands/rename.spec.js | 22 +- test/commands/renamenx.spec.js | 10 +- test/commands/rpush.spec.js | 12 +- test/commands/sadd.spec.js | 32 +-- test/commands/scard.spec.js | 10 +- test/commands/script.spec.js | 22 +- test/commands/sdiff.spec.js | 12 +- test/commands/sdiffstore.spec.js | 12 +- test/commands/select.spec.js | 60 +++--- test/commands/set.spec.js | 44 ++-- test/commands/setex.spec.js | 16 +- test/commands/setnx.spec.js | 10 +- test/commands/sinter.spec.js | 12 +- test/commands/sinterstore.spec.js | 12 +- test/commands/sismember.spec.js | 10 +- test/commands/slowlog.spec.js | 26 +-- test/commands/smembers.spec.js | 12 +- test/commands/smove.spec.js | 12 +- test/commands/sort.spec.js | 16 +- test/commands/spop.spec.js | 12 +- test/commands/srem.spec.js | 38 ++-- test/commands/sunion.spec.js | 12 +- test/commands/sunionstore.spec.js | 12 +- test/commands/ttl.spec.js | 18 +- test/commands/type.spec.js | 32 +-- test/commands/watch.spec.js | 28 +-- test/commands/zadd.spec.js | 18 +- test/commands/zscan.spec.js | 18 +- test/commands/zscore.spec.js | 10 +- test/conect.slave.spec.js | 8 +- test/connection.spec.js | 166 +++++++------- test/detect_buffers.spec.js | 162 +++++++------- test/helper.js | 54 ++--- test/lib/config.js | 4 +- test/lib/redis-process.js | 8 +- test/lib/stunnel-process.js | 22 +- test/lib/unref.js | 8 +- test/multi.spec.js | 260 +++++++++++----------- test/node_redis.spec.js | 297 +++++++++++++------------- test/prefix.spec.js | 36 ++-- test/pubsub.spec.js | 132 ++++++------ test/rename.spec.js | 32 +-- test/return_buffers.spec.js | 156 +++++++------- test/tls.spec.js | 28 +-- test/unify_options.spec.js | 6 +- test/utils.spec.js | 4 +- 98 files changed, 1621 insertions(+), 1551 deletions(-) rename .jshintignore => .eslintignore (82%) create mode 100644 .eslintrc delete mode 100644 .jshintrc diff --git a/.jshintignore b/.eslintignore similarity index 82% rename from .jshintignore rename to .eslintignore index ed454a5bac6..fd16de30886 100644 --- a/.jshintignore +++ b/.eslintignore @@ -1,4 +1,4 @@ node_modules/** coverage/** **.md -**.log \ No newline at end of file +**.log diff --git a/.eslintrc b/.eslintrc new file mode 100644 index 00000000000..75c73341b06 --- /dev/null +++ b/.eslintrc @@ -0,0 +1,108 @@ +env: + node: true + es6: false + +rules: + # Possible Errors + # http://eslint.org/docs/rules/#possible-errors + comma-dangle: [2, "only-multiline"] + no-constant-condition: 2 + no-control-regex: 2 + no-debugger: 2 + no-dupe-args: 2 + no-dupe-keys: 2 + no-duplicate-case: 2 + no-empty: 2 + no-empty-character-class: 2 + no-ex-assign: 2 + no-extra-boolean-cast : 2 + no-extra-parens: [2, "functions"] + no-extra-semi: 2 + no-func-assign: 2 + no-invalid-regexp: 2 + no-irregular-whitespace: 2 + no-negated-in-lhs: 2 + no-obj-calls: 2 + no-regex-spaces: 2 + no-sparse-arrays: 2 + no-inner-declarations: 2 + no-unexpected-multiline: 2 + no-unreachable: 2 + use-isnan: 2 + valid-typeof: 2 + + # Best Practices + # http://eslint.org/docs/rules/#best-practices + array-callback-return: 2 + block-scoped-var: 2 + dot-notation: 2 + eqeqeq: 2 + no-else-return: 2 + no-extend-native: 2 + no-floating-decimal: 2 + no-extra-bind: 2 + no-fallthrough: 2 + no-labels: 2 + no-lone-blocks: 2 + no-loop-func: 2 + no-multi-spaces: 2 + no-multi-str: 2 + no-native-reassign: 2 + no-new-wrappers: 2 + no-octal: 2 + no-proto: 2 + no-redeclare: 2 + no-return-assign: 2 + no-self-assign: 2 + no-self-compare: 2 + no-sequences: 2 + no-throw-literal: 2 + no-useless-call: 2 + no-useless-concat: 2 + no-useless-escape: 2 + no-void: 2 + no-unmodified-loop-condition: 2 + yoda: 2 + + # Strict Mode + # http://eslint.org/docs/rules/#strict-mode + strict: [2, "global"] + + # Variables + # http://eslint.org/docs/rules/#variables + no-delete-var: 2 + no-shadow-restricted-names: 2 + no-undef: 2 + no-unused-vars: [2, {"args": "none"}] + + # http://eslint.org/docs/rules/#nodejs-and-commonjs + no-mixed-requires: 2 + no-new-require: 2 + no-path-concat: 2 + + # Stylistic Issues + # http://eslint.org/docs/rules/#stylistic-issues + comma-spacing: 2 + eol-last: 2 + indent: [2, 4, {SwitchCase: 2}] + keyword-spacing: 2 + max-len: [2, 200, 2] + new-parens: 2 + no-mixed-spaces-and-tabs: 2 + no-multiple-empty-lines: [2, {max: 2}] + no-trailing-spaces: 2 + quotes: [2, "single", "avoid-escape"] + semi: 2 + space-before-blocks: [2, "always"] + space-before-function-paren: [2, "always"] + space-in-parens: [2, "never"] + space-infix-ops: 2 + space-unary-ops: 2 + +globals: + it: true + describe: true + before: true + after: true + beforeEach: true + afterEach: true diff --git a/.jshintrc b/.jshintrc deleted file mode 100644 index 92441eb976c..00000000000 --- a/.jshintrc +++ /dev/null @@ -1,27 +0,0 @@ -{ - "eqeqeq": true, // Prohibits the use of == and != in favor of === and !== - "noarg": true, // Prohibit use of `arguments.caller` and `arguments.callee` - "undef": true, // Require all non-global variables be declared before they are used. - "unused": "vars", // Warn unused variables, but not unused params - "strict": true, // Require `use strict` pragma in every file. - "nonbsp": true, // don't allow non utf-8 pages to break - "forin": true, // don't allow not filtert for in loops - "freeze": true, // prohibit overwriting prototypes of native objects - "nonew": true, // prohibit use of constructors with new when not assigning to a variable - "maxdepth": 6, - "latedef": true, - "maxparams": 5, - - // Environment options - "node": true, // Enable globals available when code is running inside of the NodeJS runtime environment. - "mocha": true, - - // Relaxing options - "boss": true, // Accept statements like `while (key = keys.pop()) {}` - - "overrides": { - "examples/*.js": { - "unused": false - } - } -} diff --git a/benchmarks/diff_multi_bench_output.js b/benchmarks/diff_multi_bench_output.js index 6ba8c6900af..dc0d3c227d9 100755 --- a/benchmarks/diff_multi_bench_output.js +++ b/benchmarks/diff_multi_bench_output.js @@ -23,11 +23,11 @@ var total_ops = new metrics.Histogram.createUniformHistogram(); console.log('Comparing before,', file1, '(', before_lines.length, 'lines)', 'to after,', file2, '(', after_lines.length, 'lines)'); -function is_whitespace(s) { +function is_whitespace (s) { return !!s.trim(); } -function pad(input, len, chr, right) { +function pad (input, len, chr, right) { var str = input.toString(); chr = chr || ' '; @@ -44,7 +44,7 @@ function pad(input, len, chr, right) { } // green if greater than 0, red otherwise -function humanize_diff(num, unit, toFixed) { +function humanize_diff (num, unit, toFixed) { unit = unit || ''; if (num > 0) { return ' +' + pad(num.toFixed(toFixed || 0) + unit, 7); @@ -52,12 +52,12 @@ function humanize_diff(num, unit, toFixed) { return ' -' + pad(Math.abs(num).toFixed(toFixed || 0) + unit, 7); } -function command_name(words) { +function command_name (words) { var line = words.join(' '); return line.substr(0, line.indexOf(',')); } -before_lines.forEach(function(b, i) { +before_lines.forEach(function (b, i) { var a = after_lines[i]; if (!a || !b || !b.trim() || !a.trim()) { // console.log('#ignored#', '>'+a+'<', '>'+b+'<'); @@ -66,10 +66,10 @@ before_lines.forEach(function(b, i) { var b_words = b.split(' ').filter(is_whitespace); var a_words = a.split(' ').filter(is_whitespace); - var ops = [b_words, a_words].map(function(words) { + var ops = [b_words, a_words].map(function (words) { // console.log(words); return words.slice(-2, -1) | 0; - }).filter(function(num) { + }).filter(function (num) { var isNaN = !num && num !== 0; return !isNaN; }); diff --git a/benchmarks/multi_bench.js b/benchmarks/multi_bench.js index f8b9299564b..f13b9bd845b 100644 --- a/benchmarks/multi_bench.js +++ b/benchmarks/multi_bench.js @@ -13,7 +13,7 @@ var tests = []; // bluebird.promisifyAll(redis.Multi.prototype); function returnArg (name, def) { - var matches = process.argv.filter(function(entry) { + var matches = process.argv.filter(function (entry) { return entry.indexOf(name + '=') === 0; }); if (matches.length) { @@ -30,7 +30,7 @@ var client_options = { }; var small_str, large_str, small_buf, large_buf, very_large_str, very_large_buf; -function lpad(input, len, chr) { +function lpad (input, len, chr) { var str = input.toString(); chr = chr || ' '; while (str.length < len) { @@ -44,7 +44,7 @@ metrics.Histogram.prototype.print_line = function () { return lpad((obj.min / 1e6).toFixed(2), 6) + '/' + lpad((obj.max / 1e6).toFixed(2), 6) + '/' + lpad((obj.mean / 1e6).toFixed(2), 6); }; -function Test(args) { +function Test (args) { this.args = args; this.callback = null; this.clients = []; @@ -101,7 +101,7 @@ Test.prototype.new_client = function (id) { }); // If no redis server is running, start one - new_client.on('error', function(err) { + new_client.on('error', function (err) { if (err.code === 'CONNECTION_BROKEN') { throw err; } @@ -268,7 +268,7 @@ tests.push(new Test({descr: 'GET 4MiB str', command: 'get', args: ['foo_rand0000 tests.push(new Test({descr: 'GET 4MiB buf', command: 'get', args: ['foo_rand000000000002'], pipeline: 1, client_opts: { return_buffers: true} })); tests.push(new Test({descr: 'GET 4MiB buf', command: 'get', args: ['foo_rand000000000002'], batch: 20, client_opts: { return_buffers: true} })); -function next() { +function next () { var test = tests.shift(); if (test) { test.run(function () { @@ -276,7 +276,7 @@ function next() { }); } else if (rp) { // Stop the redis process if started by the benchmark - rp.stop(function() { + rp.stop(function () { rp = undefined; next(); }); diff --git a/examples/auth.js b/examples/auth.js index c954a4047a4..e36b7006562 100644 --- a/examples/auth.js +++ b/examples/auth.js @@ -1,7 +1,7 @@ 'use strict'; -var redis = require('redis'), - client = redis.createClient(); - +var redis = require('redis'); // The client stashes the password and will reauthenticate on every connect. -client.auth('somepass'); +redis.createClient({ + password: 'somepass' +}); diff --git a/examples/backpressure_drain.js b/examples/backpressure_drain.js index f9ab961c332..0e9140c6b29 100644 --- a/examples/backpressure_drain.js +++ b/examples/backpressure_drain.js @@ -1,10 +1,11 @@ 'use strict'; -var redis = require('../index'), - client = redis.createClient(), - remaining_ops = 100000, paused = false; +var redis = require('../index'); +var client = redis.createClient(); +var remaining_ops = 100000; +var paused = false; -function op() { +function op () { if (remaining_ops <= 0) { console.error('Finished.'); process.exit(0); diff --git a/examples/eval.js b/examples/eval.js index 2fbcc427863..cffdb548a16 100644 --- a/examples/eval.js +++ b/examples/eval.js @@ -1,7 +1,7 @@ 'use strict'; -var redis = require('../index'), - client = redis.createClient(); +var redis = require('../index'); +var client = redis.createClient(); client.eval('return 100.5', 0, function (err, res) { console.dir(err); diff --git a/examples/extend.js b/examples/extend.js index c132cebd589..8128b41bdca 100644 --- a/examples/extend.js +++ b/examples/extend.js @@ -1,7 +1,7 @@ 'use strict'; -var redis = require('redis'), - client = redis.createClient(); +var redis = require('redis'); +var client = redis.createClient(); // Extend the RedisClient prototype to add a custom method // This one converts the results from 'INFO' into a JavaScript Object diff --git a/examples/file.js b/examples/file.js index aff7e1677a8..0bacd723b32 100644 --- a/examples/file.js +++ b/examples/file.js @@ -2,13 +2,13 @@ // Read a file from disk, store it in Redis, then read it back from Redis. -var redis = require('redis'), - client = redis.createClient({ - return_buffers: true - }), - fs = require('fs'), - assert = require('assert'), - filename = 'grumpyCat.jpg'; +var redis = require('redis'); +var client = redis.createClient({ + return_buffers: true +}); +var fs = require('fs'); +var assert = require('assert'); +var filename = 'grumpyCat.jpg'; // Get the file I use for testing like this: // curl http://media4.popsugar-assets.com/files/2014/08/08/878/n/1922507/caef16ec354ca23b_thumb_temp_cover_file32304521407524949.xxxlarge/i/Funny-Cat-GIFs.jpg -o grumpyCat.jpg diff --git a/examples/mget.js b/examples/mget.js index 64dd3daa54c..e774ac44e0e 100644 --- a/examples/mget.js +++ b/examples/mget.js @@ -4,4 +4,4 @@ var client = require('redis').createClient(); client.mget(['sessions started', 'sessions started', 'foo'], function (err, res) { console.dir(res); -}); \ No newline at end of file +}); diff --git a/examples/monitor.js b/examples/monitor.js index 01a21228234..93d5d100f5a 100644 --- a/examples/monitor.js +++ b/examples/monitor.js @@ -1,7 +1,7 @@ 'use strict'; -var client = require('../index').createClient(), - util = require('util'); +var client = require('../index').createClient(); +var util = require('util'); client.monitor(function (err, res) { console.log('Entering monitoring mode.'); diff --git a/examples/multi.js b/examples/multi.js index ac7f01b831b..e0e3bf13fcb 100644 --- a/examples/multi.js +++ b/examples/multi.js @@ -1,7 +1,8 @@ 'use strict'; -var redis = require('redis'), - client = redis.createClient(), set_size = 20; +var redis = require('redis'); +var client = redis.createClient(); +var set_size = 20; client.sadd('bigset', 'a member'); client.sadd('bigset', 'another member'); diff --git a/examples/multi2.js b/examples/multi2.js index 2071895dffb..3a0ceaec6a3 100644 --- a/examples/multi2.js +++ b/examples/multi2.js @@ -1,10 +1,10 @@ 'use strict'; -var redis = require('redis'), - client = redis.createClient(), multi; +var redis = require('redis'); +var client = redis.createClient(); // start a separate command queue for multi -multi = client.multi(); +var multi = client.multi(); multi.incr('incr thing', redis.print); multi.incr('incr other thing', redis.print); diff --git a/examples/psubscribe.js b/examples/psubscribe.js index 65b113cd907..ecd24274047 100644 --- a/examples/psubscribe.js +++ b/examples/psubscribe.js @@ -1,11 +1,11 @@ 'use strict'; -var redis = require('redis'), - client1 = redis.createClient(), - client2 = redis.createClient(), - client3 = redis.createClient(), - client4 = redis.createClient(), - msg_count = 0; +var redis = require('redis'); +var client1 = redis.createClient(); +var client2 = redis.createClient(); +var client3 = redis.createClient(); +var client4 = redis.createClient(); +var msg_count = 0; client1.on('psubscribe', function (pattern, count) { console.log('client1 psubscribed to ' + pattern + ', ' + count + ' total subscriptions'); @@ -23,7 +23,7 @@ client1.on('punsubscribe', function (pattern, count) { }); client1.on('pmessage', function (pattern, channel, message) { - console.log('('+ pattern +')' + ' client1 received message on ' + channel + ': ' + message); + console.log('(' + pattern + ') client1 received message on ' + channel + ': ' + message); msg_count += 1; if (msg_count === 3) { client1.punsubscribe(); diff --git a/examples/pub_sub.js b/examples/pub_sub.js index 8d691126daa..d0970eb502f 100644 --- a/examples/pub_sub.js +++ b/examples/pub_sub.js @@ -1,8 +1,9 @@ 'use strict'; -var redis = require('redis'), - client1 = redis.createClient(), msg_count = 0, - client2 = redis.createClient(); +var redis = require('redis'); +var client1 = redis.createClient(); +var msg_count = 0; +var client2 = redis.createClient(); // Most clients probably don't do much on 'subscribe'. This example uses it to coordinate things within one program. client1.on('subscribe', function (channel, count) { diff --git a/examples/scan.js b/examples/scan.js index 2f7a9d405ae..e6b67ea6ae0 100644 --- a/examples/scan.js +++ b/examples/scan.js @@ -1,16 +1,16 @@ 'use strict'; -var redis = require('redis'), - client = redis.createClient(); +var redis = require('redis'); +var client = redis.createClient(); var cursor = '0'; -function scan() { +function scan () { client.scan( cursor, 'MATCH', 'q:job:*', 'COUNT', '10', - function(err, res) { + function (err, res) { if (err) throw err; // Update the cursor position for the next scan @@ -48,3 +48,4 @@ function scan() { } ); } +scan(); diff --git a/examples/simple.js b/examples/simple.js index 3e67242ddca..2fc2c3aefbd 100644 --- a/examples/simple.js +++ b/examples/simple.js @@ -1,7 +1,7 @@ 'use strict'; -var redis = require('redis'), - client = redis.createClient(); +var redis = require('redis'); +var client = redis.createClient(); client.on('error', function (err) { console.log('error event - ' + client.host + ':' + client.port + ' - ' + err); diff --git a/examples/sort.js b/examples/sort.js index 2181cf30321..b09b06fbbf0 100644 --- a/examples/sort.js +++ b/examples/sort.js @@ -1,7 +1,7 @@ 'use strict'; -var redis = require('redis'), - client = redis.createClient(); +var redis = require('redis'); +var client = redis.createClient(); client.sadd('mylist', 1); client.sadd('mylist', 2); @@ -16,4 +16,4 @@ client.set('object_2', 'bar'); client.set('object_3', 'qux'); client.sort('mylist', 'by', 'weight_*', 'get', 'object_*', redis.print); -// Prints Reply: qux,foo,bar \ No newline at end of file +// Prints Reply: qux,foo,bar diff --git a/examples/unix_socket.js b/examples/unix_socket.js index 28e5eb0c515..b51aef2d11a 100644 --- a/examples/unix_socket.js +++ b/examples/unix_socket.js @@ -1,8 +1,8 @@ 'use strict'; -var redis = require('redis'), - client = redis.createClient('/tmp/redis.sock'), - profiler = require('v8-profiler'); +var redis = require('redis'); +var client = redis.createClient('/tmp/redis.sock'); +var profiler = require('v8-profiler'); client.on('connect', function () { console.log('Got Unix socket connection.'); @@ -18,7 +18,7 @@ setInterval(function () { client.get('space chars'); }, 100); -function done() { +function done () { client.info(function (err, reply) { console.log(reply.toString()); client.quit(); @@ -28,4 +28,5 @@ function done() { setTimeout(function () { console.log('Taking snapshot.'); profiler.takeSnapshot(); + done(); }, 5000); diff --git a/examples/web_server.js b/examples/web_server.js index 6afd0c96f22..ba5950d0384 100644 --- a/examples/web_server.js +++ b/examples/web_server.js @@ -2,10 +2,10 @@ // A simple web server that generates dyanmic content based on responses from Redis -var http = require('http'), server, - redis_client = require('redis').createClient(); +var http = require('http'); +var redis_client = require('redis').createClient(); -server = http.createServer(function (request, response) { +http.createServer(function (request, response) { // The server response.writeHead(200, { 'Content-Type': 'text/plain' }); diff --git a/index.js b/index.js index 8e13499cb4f..a306cf01009 100644 --- a/index.js +++ b/index.js @@ -620,7 +620,7 @@ RedisClient.prototype.emit_idle = function () { function normal_reply (self, reply) { var command_obj = self.command_queue.shift(); if (typeof command_obj.callback === 'function') { - if ('exec' !== command_obj.command) { + if (command_obj.command !== 'exec') { reply = self.handle_reply(reply, command_obj.command, command_obj.buffer_args); } command_obj.callback(null, reply); @@ -935,7 +935,7 @@ RedisClient.prototype.end = function (flush) { ); } // Clear retry_timer - if (this.retry_timer){ + if (this.retry_timer) { clearTimeout(this.retry_timer); this.retry_timer = null; } diff --git a/lib/command.js b/lib/command.js index 20b7589db2f..e4467fb55da 100644 --- a/lib/command.js +++ b/lib/command.js @@ -2,7 +2,7 @@ // This Command constructor is ever so slightly faster than using an object literal, but more importantly, using // a named constructor helps it show up meaningfully in the V8 CPU profiler and in heap snapshots. -function Command(command, args, callback) { +function Command (command, args, callback) { this.command = command; this.args = args; // We only need the args for the offline commands => move them into another class. We need the number of args though for pub sub this.buffer_args = false; @@ -10,7 +10,7 @@ function Command(command, args, callback) { this.sub_commands_left = args.length; } -function OfflineCommand(command, args, callback) { +function OfflineCommand (command, args, callback) { this.command = command; this.args = args; this.callback = callback; diff --git a/lib/multi.js b/lib/multi.js index 5a06e1096ab..4d9834378b6 100644 --- a/lib/multi.js +++ b/lib/multi.js @@ -3,7 +3,7 @@ var Queue = require('double-ended-queue'); var utils = require('./utils'); -function Multi(client, args) { +function Multi (client, args) { this._client = client; this.queue = new Queue(); var command, tmp_args; @@ -147,7 +147,7 @@ Multi.prototype.exec_transaction = function exec_transaction (callback) { pipeline_transaction_command(self, command, args[1], index, cb); } - self._client.send_command('exec', [], function(err, replies) { + self._client.send_command('exec', [], function (err, replies) { multi_callback(self, err, replies); }); self._client.uncork(); @@ -173,7 +173,6 @@ Multi.prototype.exec = Multi.prototype.EXEC = Multi.prototype.exec_batch = funct var len = self.queue.length; var index = 0; var args; - var args_len = 1; var callback_without_own_cb = function (err, res) { if (err) { self.results.push(err); @@ -203,7 +202,6 @@ Multi.prototype.exec = Multi.prototype.EXEC = Multi.prototype.exec_batch = funct while (args = self.queue.shift()) { var command = args[0]; var cb; - args_len = args[1].length - 1; if (typeof args[2] === 'function') { cb = batch_callback(self, args[2], index); } else { diff --git a/lib/utils.js b/lib/utils.js index b1b46c78296..61e9a64f755 100644 --- a/lib/utils.js +++ b/lib/utils.js @@ -2,7 +2,7 @@ // hgetall converts its replies to an Object. If the reply is empty, null is returned. // These function are only called with internal data and have therefor always the same instanceof X -function replyToObject(reply) { +function replyToObject (reply) { // The reply might be a string or a buffer if this is called in a transaction (multi) if (reply.length === 0 || !(reply instanceof Array)) { return null; @@ -14,7 +14,7 @@ function replyToObject(reply) { return obj; } -function replyToStrings(reply) { +function replyToStrings (reply) { if (reply instanceof Buffer) { return reply.toString(); } diff --git a/package.json b/package.json index e53574749ae..7e4d2348675 100644 --- a/package.json +++ b/package.json @@ -22,7 +22,7 @@ "benchmark": "node benchmarks/multi_bench.js", "test": "nyc --cache mocha ./test/*.js ./test/commands/*.js --timeout=8000", "pretest": "optional-dev-dependency hiredis", - "posttest": "jshint ." + "posttest": "eslint . --fix" }, "dependencies": { "double-ended-queue": "^2.1.0-0", @@ -36,7 +36,7 @@ "bluebird": "^3.0.2", "coveralls": "^2.11.2", "intercept-stdout": "~0.1.2", - "jshint": "^2.8.0", + "eslint": "^2.5.0", "metrics": "^0.1.9", "mocha": "^2.3.2", "nyc": "^6.0.0", diff --git a/test/auth.spec.js b/test/auth.spec.js index cb78e2ee659..fd79b36845e 100644 --- a/test/auth.spec.js +++ b/test/auth.spec.js @@ -1,7 +1,7 @@ 'use strict'; -var assert = require("assert"); -var config = require("./lib/config"); +var assert = require('assert'); +var config = require('./lib/config'); var helper = require('./helper'); var redis = config.redis; @@ -10,7 +10,7 @@ if (process.platform === 'win32') { return; } -describe("client authentication", function () { +describe('client authentication', function () { before(function (done) { helper.stopRedis(function () { helper.startRedis('./conf/password.conf', done); @@ -19,9 +19,9 @@ describe("client authentication", function () { helper.allTests({ allConnections: true - }, function(parser, ip, args) { + }, function (parser, ip, args) { - describe("using " + parser + " and " + ip, function () { + describe('using ' + parser + ' and ' + ip, function () { var auth = 'porkchopsandwiches'; var client = null; @@ -40,7 +40,7 @@ describe("client authentication", function () { client = redis.createClient.apply(null, args); client.auth(auth, function (err, res) { assert.strictEqual(null, err); - assert.strictEqual("OK", res.toString()); + assert.strictEqual('OK', res.toString()); return done(err); }); }); @@ -67,7 +67,7 @@ describe("client authentication", function () { }; }); - it("emits error when auth is bad without callback", function (done) { + it('emits error when auth is bad without callback', function (done) { if (helper.redisProcess().spawnFailed()) this.skip(); client = redis.createClient.apply(null, args); @@ -81,7 +81,7 @@ describe("client authentication", function () { client.auth(auth + 'bad'); }); - it("returns an error when auth is bad (empty string) with a callback", function (done) { + it('returns an error when auth is bad (empty string) with a callback', function (done) { if (helper.redisProcess().spawnFailed()) this.skip(); client = redis.createClient.apply(null, args); @@ -99,7 +99,7 @@ describe("client authentication", function () { var end = helper.callFuncAfter(done, 2); client = redis.createClient('redis://:' + auth + '@' + config.HOST[ip] + ':' + config.PORT); - client.on("ready", function () { + client.on('ready', function () { end(); }); // The info command may be used while loading but not if not yet authenticated @@ -116,7 +116,7 @@ describe("client authentication", function () { assert.strictEqual(client.options.db, '2'); assert.strictEqual(client.options.password, auth); assert.strictEqual(client.auth_pass, auth); - client.on("ready", function () { + client.on('ready', function () { // Set a key so the used database is returned in the info command client.set('foo', 'bar'); client.get('foo'); @@ -137,7 +137,7 @@ describe("client authentication", function () { auth_pass: auth }); client = redis.createClient.apply(null, args); - client.on("ready", done); + client.on('ready', done); }); it('allows auth and no_ready_check to be provided as config option for client', function (done) { @@ -148,7 +148,7 @@ describe("client authentication", function () { no_ready_check: true }); client = redis.createClient.apply(null, args); - client.on("ready", done); + client.on('ready', done); }); it('allows auth to be provided post-hoc with auth method', function (done) { @@ -157,7 +157,7 @@ describe("client authentication", function () { var args = config.configureClient(parser, ip); client = redis.createClient.apply(null, args); client.auth(auth); - client.on("ready", done); + client.on('ready', done); }); it('reconnects with appropriate authentication', function (done) { @@ -165,7 +165,7 @@ describe("client authentication", function () { client = redis.createClient.apply(null, args); client.auth(auth); - client.on("ready", function () { + client.on('ready', function () { if (this.times_connected === 1) { client.stream.destroy(); } else { @@ -182,7 +182,7 @@ describe("client authentication", function () { client = redis.createClient.apply(null, args); var async = true; - client.auth(undefined, function(err, res) { + client.auth(undefined, function (err, res) { assert.strictEqual(err.message, 'ERR invalid password'); assert.strictEqual(err.command, 'AUTH'); assert.strictEqual(res, undefined); @@ -211,7 +211,7 @@ describe("client authentication", function () { auth_pass: auth }); client = redis.createClient.apply(null, args); - client.on("ready", function () { + client.on('ready', function () { client.auth(auth, helper.isString('OK', done)); }); }); @@ -223,7 +223,7 @@ describe("client authentication", function () { no_ready_check: true }); client = redis.createClient.apply(null, args); - client.on("ready", function () { + client.on('ready', function () { client.set('foo', 'bar', function (err, res) { assert.equal(err.message, 'NOAUTH Authentication required.'); assert.equal(err.code, 'NOAUTH'); @@ -236,7 +236,7 @@ describe("client authentication", function () { it('does not allow auth to be provided post-hoc with auth method if not authenticated before', function (done) { if (helper.redisProcess().spawnFailed()) this.skip(); client = redis.createClient.apply(null, args); - client.on("error", function (err) { + client.on('error', function (err) { assert.equal(err.code, 'NOAUTH'); assert.equal(err.message, 'Ready check failed: NOAUTH Authentication required.'); assert.equal(err.command, 'INFO'); @@ -250,7 +250,7 @@ describe("client authentication", function () { password: 'wrong_password', parser: parser }); - client.once("error", function (err) { + client.once('error', function (err) { assert.strictEqual(err.message, 'ERR invalid password'); done(); }); diff --git a/test/batch.spec.js b/test/batch.spec.js index 5a1ace4e7da..2d02c371f17 100644 --- a/test/batch.spec.js +++ b/test/batch.spec.js @@ -1,35 +1,28 @@ 'use strict'; var assert = require('assert'); -var config = require("./lib/config"); +var config = require('./lib/config'); var helper = require('./helper'); var redis = config.redis; -var uuid = require('uuid'); describe("The 'batch' method", function () { - helper.allTests(function(parser, ip, args) { + helper.allTests(function (parser, ip, args) { - describe("using " + parser + " and " + ip, function () { - var key, value; + describe('using ' + parser + ' and ' + ip, function () { - beforeEach(function () { - key = uuid.v4(); - value = uuid.v4(); - }); - - describe("when not connected", function () { + describe('when not connected', function () { var client; beforeEach(function (done) { client = redis.createClient.apply(null, args); - client.once("connect", function () { + client.once('connect', function () { client.quit(); }); client.on('end', done); }); - it("returns an empty array", function (done) { + it('returns an empty array', function (done) { var batch = client.batch(); batch.exec(function (err, res) { assert.strictEqual(err, null); @@ -38,19 +31,19 @@ describe("The 'batch' method", function () { }); }); - it("returns an empty array if promisified", function () { - return client.batch().execAsync().then(function(res) { + it('returns an empty array if promisified', function () { + return client.batch().execAsync().then(function (res) { assert.strictEqual(res.length, 0); }); }); }); - describe("when connected", function () { + describe('when connected', function () { var client; beforeEach(function (done) { client = redis.createClient.apply(null, args); - client.once("ready", function () { + client.once('ready', function () { client.flushdb(function (err) { return done(err); }); @@ -61,7 +54,7 @@ describe("The 'batch' method", function () { client.end(true); }); - it("returns an empty array and keep the execution order in takt", function (done) { + it('returns an empty array and keep the execution order in takt', function (done) { var called = false; client.set('foo', 'bar', function (err, res) { called = true; @@ -75,19 +68,19 @@ describe("The 'batch' method", function () { }); }); - it("runs normal calls inbetween batch", function (done) { + it('runs normal calls inbetween batch', function (done) { var batch = client.batch(); - batch.set("m1", "123"); + batch.set('m1', '123'); client.set('m2', '456', done); }); - it("returns an empty array if promisified", function () { - return client.batch().execAsync().then(function(res) { + it('returns an empty array if promisified', function () { + return client.batch().execAsync().then(function (res) { assert.strictEqual(res.length, 0); }); }); - it("returns an empty result array", function (done) { + it('returns an empty result array', function (done) { var batch = client.batch(); var async = true; var notBuffering = batch.exec(function (err, res) { @@ -103,18 +96,18 @@ describe("The 'batch' method", function () { it('fail individually when one command fails using chaining notation', function (done) { var batch1, batch2; batch1 = client.batch(); - batch1.mset("batchfoo", "10", "batchbar", "20", helper.isString("OK")); + batch1.mset('batchfoo', '10', 'batchbar', '20', helper.isString('OK')); // Provoke an error at queue time - batch1.set("foo2", helper.isError()); - batch1.incr("batchfoo"); - batch1.incr("batchbar"); + batch1.set('foo2', helper.isError()); + batch1.incr('batchfoo'); + batch1.incr('batchbar'); batch1.exec(function () { // Confirm that the previous command, while containing an error, still worked. batch2 = client.batch(); batch2.get('foo2', helper.isNull()); - batch2.incr("batchbar", helper.isNumber(22)); - batch2.incr("batchfoo", helper.isNumber(12)); + batch2.incr('batchbar', helper.isNumber(22)); + batch2.incr('batchfoo', helper.isNumber(12)); batch2.exec(function (err, replies) { assert.strictEqual(null, replies[0]); assert.strictEqual(22, replies[1]); @@ -130,12 +123,12 @@ describe("The 'batch' method", function () { done(err); }); batch1 = client.batch(); - batch1.mset("batchfoo", "10", "batchbar", "20", helper.isString("OK")); + batch1.mset('batchfoo', '10', 'batchbar', '20', helper.isString('OK')); // Provoke an error at queue time - batch1.set("foo2"); - batch1.incr("batchfoo"); - batch1.incr("batchbar"); + batch1.set('foo2'); + batch1.incr('batchfoo'); + batch1.incr('batchbar'); batch1.exec(function (err, res) { assert.strictEqual(res[1].command, 'SET'); assert.strictEqual(res[1].code, 'ERR'); @@ -146,45 +139,45 @@ describe("The 'batch' method", function () { it('fail individually when one command in an array of commands fails', function (done) { // test nested batch-bulk replies client.batch([ - ["mget", "batchfoo", "batchbar", function (err, res) { + ['mget', 'batchfoo', 'batchbar', function (err, res) { assert.strictEqual(2, res.length); assert.strictEqual(0, +res[0]); assert.strictEqual(0, +res[1]); }], - ["set", "foo2", helper.isError()], - ["incr", "batchfoo"], - ["incr", "batchbar"] + ['set', 'foo2', helper.isError()], + ['incr', 'batchfoo'], + ['incr', 'batchbar'] ]).exec(function (err, replies) { assert.strictEqual(2, replies[0].length); assert.strictEqual(null, replies[0][0]); assert.strictEqual(null, replies[0][1]); assert.strictEqual('SET', replies[1].command); - assert.strictEqual("1", replies[2].toString()); - assert.strictEqual("1", replies[3].toString()); + assert.strictEqual('1', replies[2].toString()); + assert.strictEqual('1', replies[3].toString()); return done(); }); }); it('handles multiple operations being applied to a set', function (done) { - client.sadd("some set", "mem 1"); - client.sadd(["some set", "mem 2"]); - client.sadd("some set", "mem 3"); - client.sadd("some set", "mem 4"); + client.sadd('some set', 'mem 1'); + client.sadd(['some set', 'mem 2']); + client.sadd('some set', 'mem 3'); + client.sadd('some set', 'mem 4'); // make sure empty mb reply works - client.del("some missing set"); - client.smembers("some missing set", function (err, reply) { + client.del('some missing set'); + client.smembers('some missing set', function (err, reply) { // make sure empty mb reply works assert.strictEqual(0, reply.length); }); // test nested batch-bulk replies with empty mb elements. client.BATCH([ - ["smembers", ["some set"]], - ["del", "some set"], - ["smembers", "some set", undefined] // The explicit undefined is handled as a callback that is undefined + ['smembers', ['some set']], + ['del', 'some set'], + ['smembers', 'some set', undefined] // The explicit undefined is handled as a callback that is undefined ]) - .scard("some set") + .scard('some set') .exec(function (err, replies) { assert.strictEqual(4, replies[0].length); assert.strictEqual(0, replies[2].length); @@ -194,26 +187,26 @@ describe("The 'batch' method", function () { it('allows multiple operations to be performed using constructor with all kinds of syntax', function (done) { var now = Date.now(); - var arr = ["batchhmset", "batchbar", "batchbaz"]; + var arr = ['batchhmset', 'batchbar', 'batchbaz']; var arr2 = ['some manner of key', 'otherTypes']; - var arr3 = [5768, "batchbarx", "batchfoox"]; - var arr4 = ["mset", [578, "batchbar"], helper.isString('OK')]; + var arr3 = [5768, 'batchbarx', 'batchfoox']; + var arr4 = ['mset', [578, 'batchbar'], helper.isString('OK')]; client.batch([ arr4, - [["mset", "batchfoo2", "batchbar2", "batchfoo3", "batchbar3"], helper.isString('OK')], - ["hmset", arr], - [["hmset", "batchhmset2", "batchbar2", "batchfoo3", "batchbar3", "test"], helper.isString('OK')], - ["hmset", ["batchhmset", "batchbar", "batchfoo"], helper.isString('OK')], - ["hmset", arr3, helper.isString('OK')], - ['hmset', now, {123456789: "abcdefghij", "some manner of key": "a type of value", "otherTypes": 555}], - ['hmset', 'key2', {"0123456789": "abcdefghij", "some manner of key": "a type of value", "otherTypes": 999}, helper.isString('OK')], - ["HMSET", "batchhmset", ["batchbar", "batchbaz"]], - ["hmset", "batchhmset", ["batchbar", "batchbaz"], helper.isString('OK')], + [['mset', 'batchfoo2', 'batchbar2', 'batchfoo3', 'batchbar3'], helper.isString('OK')], + ['hmset', arr], + [['hmset', 'batchhmset2', 'batchbar2', 'batchfoo3', 'batchbar3', 'test'], helper.isString('OK')], + ['hmset', ['batchhmset', 'batchbar', 'batchfoo'], helper.isString('OK')], + ['hmset', arr3, helper.isString('OK')], + ['hmset', now, {123456789: 'abcdefghij', 'some manner of key': 'a type of value', 'otherTypes': 555}], + ['hmset', 'key2', {'0123456789': 'abcdefghij', 'some manner of key': 'a type of value', 'otherTypes': 999}, helper.isString('OK')], + ['HMSET', 'batchhmset', ['batchbar', 'batchbaz']], + ['hmset', 'batchhmset', ['batchbar', 'batchbaz'], helper.isString('OK')], ]) .hmget(now, 123456789, 'otherTypes') - .hmget('key2', arr2, function noop() {}) + .hmget('key2', arr2, function noop () {}) .hmget(['batchhmset2', 'some manner of key', 'batchbar3']) - .mget('batchfoo2', ['batchfoo3', 'batchfoo'], function(err, res) { + .mget('batchfoo2', ['batchfoo3', 'batchfoo'], function (err, res) { assert.strictEqual(res[0], 'batchbar2'); assert.strictEqual(res[1], 'batchbar3'); assert.strictEqual(res[2], null); @@ -235,7 +228,7 @@ describe("The 'batch' method", function () { }); }); - it('converts a non string key to a string', function(done) { + it('converts a non string key to a string', function (done) { // TODO: Converting the key might change soon again. client.batch().hmset(true, { test: 123, @@ -243,8 +236,8 @@ describe("The 'batch' method", function () { }).exec(done); }); - it('runs a batch without any further commands', function(done) { - var buffering = client.batch().exec(function(err, res) { + it('runs a batch without any further commands', function (done) { + var buffering = client.batch().exec(function (err, res) { assert.strictEqual(err, null); assert.strictEqual(res.length, 0); done(); @@ -252,7 +245,7 @@ describe("The 'batch' method", function () { assert(typeof buffering === 'boolean'); }); - it('runs a batch without any further commands and without callback', function() { + it('runs a batch without any further commands and without callback', function () { var buffering = client.batch().exec(); assert.strictEqual(buffering, true); }); @@ -310,8 +303,8 @@ describe("The 'batch' method", function () { it('allows an array to be provided indicating multiple operations to perform', function (done) { // test nested batch-bulk replies with nulls. client.batch([ - ["mget", ["batchfoo", "some", "random value", "keys"]], - ["incr", "batchfoo"] + ['mget', ['batchfoo', 'some', 'random value', 'keys']], + ['incr', 'batchfoo'] ]) .exec(function (err, replies) { assert.strictEqual(replies.length, 2); @@ -322,19 +315,19 @@ describe("The 'batch' method", function () { it('allows multiple operations to be performed on a hash', function (done) { client.batch() - .hmset("batchhash", "a", "foo", "b", 1) - .hmset("batchhash", { - extra: "fancy", - things: "here" + .hmset('batchhash', 'a', 'foo', 'b', 1) + .hmset('batchhash', { + extra: 'fancy', + things: 'here' }) - .hgetall("batchhash") + .hgetall('batchhash') .exec(done); }); - it("should work without any callback or arguments", function (done) { + it('should work without any callback or arguments', function (done) { var batch = client.batch(); - batch.set("baz", "binary"); - batch.set("foo", "bar"); + batch.set('baz', 'binary'); + batch.set('foo', 'bar'); batch.ping(); batch.exec(); diff --git a/test/commands/blpop.spec.js b/test/commands/blpop.spec.js index bf081233b89..d68521e6b5e 100644 --- a/test/commands/blpop.spec.js +++ b/test/commands/blpop.spec.js @@ -1,22 +1,22 @@ 'use strict'; -var assert = require("assert"); -var config = require("../lib/config"); -var helper = require("../helper"); +var assert = require('assert'); +var config = require('../lib/config'); +var helper = require('../helper'); var redis = config.redis; var intercept = require('intercept-stdout'); describe("The 'blpop' method", function () { - helper.allTests(function(parser, ip, args) { + helper.allTests(function (parser, ip, args) { - describe("using " + parser + " and " + ip, function () { + describe('using ' + parser + ' and ' + ip, function () { var client; var bclient; beforeEach(function (done) { client = redis.createClient.apply(null, args); - client.once("ready", function () { + client.once('ready', function () { client.flushdb(done); }); }); @@ -25,44 +25,44 @@ describe("The 'blpop' method", function () { bclient = redis.createClient.apply(null, args); redis.debug_mode = true; var text = ''; - var unhookIntercept = intercept(function(data) { + var unhookIntercept = intercept(function (data) { text += data; return ''; }); - client.rpush("blocking list", "initial value", helper.isNumber(1)); + client.rpush('blocking list', 'initial value', helper.isNumber(1)); unhookIntercept(); assert(/^Send 127\.0\.0\.1:6379 id [0-9]+: \*3\r\n\$5\r\nrpush\r\n\$13\r\nblocking list\r\n\$13\r\ninitial value\r\n\n$/.test(text)); redis.debug_mode = false; - bclient.blpop("blocking list", 0, function (err, value) { - assert.strictEqual(value[0], "blocking list"); - assert.strictEqual(value[1], "initial value"); + bclient.blpop('blocking list', 0, function (err, value) { + assert.strictEqual(value[0], 'blocking list'); + assert.strictEqual(value[1], 'initial value'); return done(err); }); }); it('pops value immediately if list contains values using array notation', function (done) { bclient = redis.createClient.apply(null, args); - client.rpush(["blocking list", "initial value"], helper.isNumber(1)); - bclient.blpop(["blocking list", 0], function (err, value) { - assert.strictEqual(value[0], "blocking list"); - assert.strictEqual(value[1], "initial value"); + client.rpush(['blocking list', 'initial value'], helper.isNumber(1)); + bclient.blpop(['blocking list', 0], function (err, value) { + assert.strictEqual(value[0], 'blocking list'); + assert.strictEqual(value[1], 'initial value'); return done(err); }); }); it('waits for value if list is not yet populated', function (done) { bclient = redis.createClient.apply(null, args); - bclient.blpop("blocking list 2", 5, function (err, value) { - assert.strictEqual(value[0], "blocking list 2"); - assert.strictEqual(value[1], "initial value"); + bclient.blpop('blocking list 2', 5, function (err, value) { + assert.strictEqual(value[0], 'blocking list 2'); + assert.strictEqual(value[1], 'initial value'); return done(err); }); - client.rpush("blocking list 2", "initial value", helper.isNumber(1)); + client.rpush('blocking list 2', 'initial value', helper.isNumber(1)); }); it('times out after specified time', function (done) { bclient = redis.createClient.apply(null, args); - bclient.BLPOP("blocking list", 1, function (err, res) { + bclient.BLPOP('blocking list', 1, function (err, res) { assert.strictEqual(res, null); return done(err); }); diff --git a/test/commands/client.spec.js b/test/commands/client.spec.js index 661f70d0be2..d73c5036aa5 100644 --- a/test/commands/client.spec.js +++ b/test/commands/client.spec.js @@ -1,21 +1,21 @@ 'use strict'; -var assert = require("assert"); -var config = require("../lib/config"); -var helper = require("../helper"); +var assert = require('assert'); +var config = require('../lib/config'); +var helper = require('../helper'); var redis = config.redis; describe("The 'client' method", function () { - helper.allTests(function(parser, ip, args) { + helper.allTests(function (parser, ip, args) { var pattern = /addr=/; - describe("using " + parser + " and " + ip, function () { + describe('using ' + parser + ' and ' + ip, function () { var client; beforeEach(function (done) { client = redis.createClient.apply(null, args); - client.once("ready", function () { + client.once('ready', function () { client.flushdb(done); }); }); @@ -26,18 +26,18 @@ describe("The 'client' method", function () { describe('list', function () { it('lists connected clients', function (done) { - client.client("LIST", helper.match(pattern, done)); + client.client('LIST', helper.match(pattern, done)); }); it("lists connected clients when invoked with multi's chaining syntax", function (done) { - client.multi().client("list").exec(function(err, results) { + client.multi().client('list').exec(function (err, results) { assert(pattern.test(results[0]), "expected string '" + results + "' to match " + pattern.toString()); return done(); }); }); - it("lists connected clients when invoked with array syntax on client", function (done) { - client.multi().client(["list"]).exec(function(err, results) { + it('lists connected clients when invoked with array syntax on client', function (done) { + client.multi().client(['list']).exec(function (err, results) { assert(pattern.test(results[0]), "expected string '" + results + "' to match " + pattern.toString()); return done(); }); @@ -46,7 +46,7 @@ describe("The 'client' method", function () { it("lists connected clients when invoked with multi's array syntax", function (done) { client.multi([ ['client', 'list'] - ]).exec(function(err, results) { + ]).exec(function (err, results) { assert(pattern.test(results[0]), "expected string '" + results + "' to match " + pattern.toString()); return done(); }); @@ -58,7 +58,7 @@ describe("The 'client' method", function () { beforeEach(function (done) { client2 = redis.createClient.apply(redis.createClient, args); - client2.once("ready", function () { + client2.once('ready', function () { done(); }); }); @@ -72,14 +72,14 @@ describe("The 'client' method", function () { // per chunk. So the execution order is only garanteed on each client var end = helper.callFuncAfter(done, 2); - client.client("setname", "RUTH", helper.isString('OK')); - client2.client("setname", "RENEE", helper.isString('OK')); - client2.client("setname", "MARTIN", helper.isString('OK')); - client2.client("getname", function(err, res) { + client.client('setname', 'RUTH', helper.isString('OK')); + client2.client('setname', 'RENEE', helper.isString('OK')); + client2.client('setname', 'MARTIN', helper.isString('OK')); + client2.client('getname', function (err, res) { assert.equal(res, 'MARTIN'); end(); }); - client.client("getname", function(err, res) { + client.client('getname', function (err, res) { assert.equal(res, 'RUTH'); end(); }); diff --git a/test/commands/dbsize.spec.js b/test/commands/dbsize.spec.js index 0bc922c2f39..b1a3d3c32e9 100644 --- a/test/commands/dbsize.spec.js +++ b/test/commands/dbsize.spec.js @@ -1,16 +1,16 @@ 'use strict'; var assert = require('assert'); -var config = require("../lib/config"); +var config = require('../lib/config'); var helper = require('../helper'); var redis = config.redis; var uuid = require('uuid'); describe("The 'dbsize' method", function () { - helper.allTests(function(parser, ip, args) { + helper.allTests(function (parser, ip, args) { - describe("using " + parser + " and " + ip, function () { + describe('using ' + parser + ' and ' + ip, function () { var key, value; beforeEach(function () { @@ -18,18 +18,18 @@ describe("The 'dbsize' method", function () { value = uuid.v4(); }); - describe("when not connected", function () { + describe('when not connected', function () { var client; beforeEach(function (done) { client = redis.createClient.apply(null, args); - client.once("ready", function () { + client.once('ready', function () { client.quit(); }); client.on('end', done); }); - it("reports an error", function (done) { + it('reports an error', function (done) { client.dbsize([], function (err, res) { assert(err.message.match(/The connection has already been closed/)); done(); @@ -37,14 +37,14 @@ describe("The 'dbsize' method", function () { }); }); - describe("when connected", function () { + describe('when connected', function () { var client; beforeEach(function (done) { client = redis.createClient.apply(null, args); - client.once("ready", function () { + client.once('ready', function () { client.flushdb(function (err, res) { - helper.isString("OK")(err, res); + helper.isString('OK')(err, res); done(); }); }); @@ -54,22 +54,22 @@ describe("The 'dbsize' method", function () { client.end(true); }); - it("returns a zero db size", function (done) { + it('returns a zero db size', function (done) { client.DBSIZE([], function (err, res) { helper.isNotError()(err, res); helper.isType.number()(err, res); - assert.strictEqual(res, 0, "Initial db size should be 0"); + assert.strictEqual(res, 0, 'Initial db size should be 0'); done(); }); }); - describe("when more data is added to Redis", function () { + describe('when more data is added to Redis', function () { var oldSize; beforeEach(function (done) { client.dbsize(function (err, res) { helper.isType.number()(err, res); - assert.strictEqual(res, 0, "Initial db size should be 0"); + assert.strictEqual(res, 0, 'Initial db size should be 0'); oldSize = res; @@ -80,11 +80,11 @@ describe("The 'dbsize' method", function () { }); }); - it("returns a larger db size", function (done) { + it('returns a larger db size', function (done) { client.dbsize([], function (err, res) { helper.isNotError()(err, res); helper.isType.positiveNumber()(err, res); - assert.strictEqual(true, (oldSize < res), "Adding data should increase db size."); + assert.strictEqual(true, (oldSize < res), 'Adding data should increase db size.'); done(); }); }); diff --git a/test/commands/del.spec.js b/test/commands/del.spec.js index 99f3e8fcaea..970d0886c43 100644 --- a/test/commands/del.spec.js +++ b/test/commands/del.spec.js @@ -1,19 +1,19 @@ 'use strict'; -var config = require("../lib/config"); -var helper = require("../helper"); +var config = require('../lib/config'); +var helper = require('../helper'); var redis = config.redis; describe("The 'del' method", function () { - helper.allTests(function(parser, ip, args) { + helper.allTests(function (parser, ip, args) { - describe("using " + parser + " and " + ip, function () { + describe('using ' + parser + ' and ' + ip, function () { var client; beforeEach(function (done) { client = redis.createClient.apply(null, args); - client.once("ready", function () { + client.once('ready', function () { client.flushdb(done); }); }); diff --git a/test/commands/eval.spec.js b/test/commands/eval.spec.js index 2fdd7857d5d..731d656d541 100644 --- a/test/commands/eval.spec.js +++ b/test/commands/eval.spec.js @@ -1,22 +1,22 @@ 'use strict'; -var assert = require("assert"); -var config = require("../lib/config"); -var crypto = require("crypto"); -var helper = require("../helper"); +var assert = require('assert'); +var config = require('../lib/config'); +var crypto = require('crypto'); +var helper = require('../helper'); var redis = config.redis; describe("The 'eval' method", function () { - helper.allTests(function(parser, ip, args) { + helper.allTests(function (parser, ip, args) { - describe("using " + parser + " and " + ip, function () { + describe('using ' + parser + ' and ' + ip, function () { var client; var source = "return redis.call('set', 'sha', 'test')"; beforeEach(function (done) { client = redis.createClient.apply(null, args); - client.once("ready", function () { + client.once('ready', function () { client.flushdb(done); }); }); @@ -26,7 +26,7 @@ describe("The 'eval' method", function () { }); it('converts a float to an integer when evaluated', function (done) { - client.eval("return 100.5", 0, helper.isNumber(100, done)); + client.eval('return 100.5', 0, helper.isNumber(100, done)); }); it('returns a string', function (done) { @@ -34,11 +34,11 @@ describe("The 'eval' method", function () { }); it('converts boolean true to integer 1', function (done) { - client.eval("return true", 0, helper.isNumber(1, done)); + client.eval('return true', 0, helper.isNumber(1, done)); }); it('converts boolean false to null', function (done) { - client.eval("return false", 0, helper.isNull(done)); + client.eval('return false', 0, helper.isNull(done)); }); it('converts lua status code to string representation', function (done) { @@ -46,7 +46,7 @@ describe("The 'eval' method", function () { }); it('converts lua error to an error response', function (done) { - client.eval("return {err='this is an error'}", 0, function(err) { + client.eval("return {err='this is an error'}", 0, function (err) { assert(err.code === undefined); helper.isError()(err); done(); @@ -59,7 +59,7 @@ describe("The 'eval' method", function () { assert.strictEqual(1, res[0]); assert.strictEqual(2, res[1]); assert.strictEqual(3, res[2]); - assert.strictEqual("ciao", res[3]); + assert.strictEqual('ciao', res[3]); assert.strictEqual(2, res[4].length); assert.strictEqual(1, res[4][0]); assert.strictEqual(2, res[4][1]); @@ -68,23 +68,23 @@ describe("The 'eval' method", function () { }); it('populates keys and argv correctly', function (done) { - client.eval("return {KEYS[1],KEYS[2],ARGV[1],ARGV[2]}", 2, "a", "b", "c", "d", function (err, res) { + client.eval('return {KEYS[1],KEYS[2],ARGV[1],ARGV[2]}', 2, 'a', 'b', 'c', 'd', function (err, res) { assert.strictEqual(4, res.length); - assert.strictEqual("a", res[0]); - assert.strictEqual("b", res[1]); - assert.strictEqual("c", res[2]); - assert.strictEqual("d", res[3]); + assert.strictEqual('a', res[0]); + assert.strictEqual('b', res[1]); + assert.strictEqual('c', res[2]); + assert.strictEqual('d', res[3]); return done(); }); }); it('allows arguments to be provided in array rather than as multiple parameters', function (done) { - client.eval(["return {KEYS[1],KEYS[2],ARGV[1],ARGV[2]}", 2, "a", "b", "c", "d"], function (err, res) { + client.eval(['return {KEYS[1],KEYS[2],ARGV[1],ARGV[2]}', 2, 'a', 'b', 'c', 'd'], function (err, res) { assert.strictEqual(4, res.length); - assert.strictEqual("a", res[0]); - assert.strictEqual("b", res[1]); - assert.strictEqual("c", res[2]); - assert.strictEqual("d", res[3]); + assert.strictEqual('a', res[0]); + assert.strictEqual('b', res[1]); + assert.strictEqual('c', res[2]); + assert.strictEqual('d', res[3]); return done(); }); }); @@ -113,7 +113,7 @@ describe("The 'eval' method", function () { it('emit an error if SHA does not exist without any callback', function (done) { client.evalsha('ffffffffffffffffffffffffffffffffffffffff', 0); - client.on('error', function(err) { + client.on('error', function (err) { assert.equal(err.code, 'NOSCRIPT'); assert(/NOSCRIPT No matching script. Please use EVAL./.test(err.message)); done(); @@ -130,11 +130,11 @@ describe("The 'eval' method", function () { }); it('allows a key to be incremented, and performs appropriate conversion from LUA type', function (done) { - client.set("incr key", 0, function (err, reply) { + client.set('incr key', 0, function (err, reply) { if (err) return done(err); - client.eval("local foo = redis.call('incr','incr key')\n" + "return {type(foo),foo}", 0, function (err, res) { + client.eval("local foo = redis.call('incr','incr key')\nreturn {type(foo),foo}", 0, function (err, res) { assert.strictEqual(2, res.length); - assert.strictEqual("number", res[0]); + assert.strictEqual('number', res[0]); assert.strictEqual(1, res[1]); return done(err); }); @@ -142,11 +142,11 @@ describe("The 'eval' method", function () { }); it('allows a bulk operation to be performed, and performs appropriate conversion from LUA type', function (done) { - client.set("bulk reply key", "bulk reply value", function (err, res) { + client.set('bulk reply key', 'bulk reply value', function (err, res) { client.eval("local foo = redis.call('get','bulk reply key'); return {type(foo),foo}", 0, function (err, res) { assert.strictEqual(2, res.length); - assert.strictEqual("string", res[0]); - assert.strictEqual("bulk reply value", res[1]); + assert.strictEqual('string', res[0]); + assert.strictEqual('bulk reply value', res[1]); return done(err); }); }); @@ -154,18 +154,18 @@ describe("The 'eval' method", function () { it('allows a multi mulk operation to be performed, with the appropriate type conversion', function (done) { client.multi() - .del("mylist") - .rpush("mylist", "a") - .rpush("mylist", "b") - .rpush("mylist", "c") + .del('mylist') + .rpush('mylist', 'a') + .rpush('mylist', 'b') + .rpush('mylist', 'c') .exec(function (err, replies) { if (err) return done(err); client.eval("local foo = redis.call('lrange','mylist',0,-1); return {type(foo),foo[1],foo[2],foo[3],# foo}", 0, function (err, res) { assert.strictEqual(5, res.length); - assert.strictEqual("table", res[0]); - assert.strictEqual("a", res[1]); - assert.strictEqual("b", res[2]); - assert.strictEqual("c", res[3]); + assert.strictEqual('table', res[0]); + assert.strictEqual('a', res[1]); + assert.strictEqual('b', res[2]); + assert.strictEqual('c', res[3]); assert.strictEqual(3, res[4]); return done(err); }); @@ -175,31 +175,31 @@ describe("The 'eval' method", function () { it('returns an appropriate representation of Lua status reply', function (done) { client.eval("local foo = redis.call('set','mykey','myval'); return {type(foo),foo['ok']}", 0, function (err, res) { assert.strictEqual(2, res.length); - assert.strictEqual("table", res[0]); - assert.strictEqual("OK", res[1]); + assert.strictEqual('table', res[0]); + assert.strictEqual('OK', res[1]); return done(err); }); }); it('returns an appropriate representation of a Lua error reply', function (done) { - client.set("error reply key", "error reply value", function (err, res) { + client.set('error reply key', 'error reply value', function (err, res) { if (err) return done(err); client.eval("local foo = redis.pcall('incr','error reply key'); return {type(foo),foo['err']}", 0, function (err, res) { assert.strictEqual(2, res.length); - assert.strictEqual("table", res[0]); - assert.strictEqual("ERR value is not an integer or out of range", res[1]); + assert.strictEqual('table', res[0]); + assert.strictEqual('ERR value is not an integer or out of range', res[1]); return done(err); }); }); }); it('returns an appropriate representation of a Lua nil reply', function (done) { - client.del("nil reply key", function (err, res) { + client.del('nil reply key', function (err, res) { if (err) return done(err); client.eval("local foo = redis.call('get','nil reply key'); return {type(foo),foo == false}", 0, function (err, res) { if (err) throw err; assert.strictEqual(2, res.length); - assert.strictEqual("boolean", res[0]); + assert.strictEqual('boolean', res[0]); assert.strictEqual(1, res[1]); return done(err); }); diff --git a/test/commands/exists.spec.js b/test/commands/exists.spec.js index 7135c10553a..01a1b6891d9 100644 --- a/test/commands/exists.spec.js +++ b/test/commands/exists.spec.js @@ -1,19 +1,19 @@ 'use strict'; -var config = require("../lib/config"); -var helper = require("../helper"); +var config = require('../lib/config'); +var helper = require('../helper'); var redis = config.redis; describe("The 'exists' method", function () { - helper.allTests(function(parser, ip, args) { + helper.allTests(function (parser, ip, args) { - describe("using " + parser + " and " + ip, function () { + describe('using ' + parser + ' and ' + ip, function () { var client; beforeEach(function (done) { client = redis.createClient.apply(null, args); - client.once("ready", function () { + client.once('ready', function () { client.flushdb(done); }); }); diff --git a/test/commands/expire.spec.js b/test/commands/expire.spec.js index 679b152f5a2..4994caf86ff 100644 --- a/test/commands/expire.spec.js +++ b/test/commands/expire.spec.js @@ -1,36 +1,36 @@ 'use strict'; -var config = require("../lib/config"); -var helper = require("../helper"); +var config = require('../lib/config'); +var helper = require('../helper'); var redis = config.redis; describe("The 'expire' method", function () { - helper.allTests(function(parser, ip, args) { + helper.allTests(function (parser, ip, args) { - describe("using " + parser + " and " + ip, function () { + describe('using ' + parser + ' and ' + ip, function () { var client; beforeEach(function (done) { client = redis.createClient.apply(null, args); - client.once("ready", function () { + client.once('ready', function () { client.flushdb(done); }); }); it('expires key after timeout', function (done) { - client.set(['expiry key', 'bar'], helper.isString("OK")); - client.EXPIRE("expiry key", "1", helper.isNumber(1)); + client.set(['expiry key', 'bar'], helper.isString('OK')); + client.EXPIRE('expiry key', '1', helper.isNumber(1)); setTimeout(function () { - client.exists(["expiry key"], helper.isNumber(0, done)); + client.exists(['expiry key'], helper.isNumber(0, done)); }, 1100); }); it('expires key after timeout with array syntax', function (done) { - client.set(['expiry key', 'bar'], helper.isString("OK")); - client.EXPIRE(["expiry key", "1"], helper.isNumber(1)); + client.set(['expiry key', 'bar'], helper.isString('OK')); + client.EXPIRE(['expiry key', '1'], helper.isNumber(1)); setTimeout(function () { - client.exists(["expiry key"], helper.isNumber(0, done)); + client.exists(['expiry key'], helper.isNumber(0, done)); }, 1100); }); diff --git a/test/commands/flushdb.spec.js b/test/commands/flushdb.spec.js index 97e46e36e1a..b96c4b10319 100644 --- a/test/commands/flushdb.spec.js +++ b/test/commands/flushdb.spec.js @@ -1,16 +1,16 @@ 'use strict'; var assert = require('assert'); -var config = require("../lib/config"); +var config = require('../lib/config'); var helper = require('../helper'); var redis = config.redis; var uuid = require('uuid'); describe("The 'flushdb' method", function () { - helper.allTests(function(parser, ip, args) { + helper.allTests(function (parser, ip, args) { - describe("using " + parser + " and " + ip, function () { + describe('using ' + parser + ' and ' + ip, function () { var key, key2; beforeEach(function () { @@ -18,18 +18,18 @@ describe("The 'flushdb' method", function () { key2 = uuid.v4(); }); - describe("when not connected", function () { + describe('when not connected', function () { var client; beforeEach(function (done) { client = redis.createClient.apply(null, args); - client.once("ready", function () { + client.once('ready', function () { client.quit(); }); client.on('end', done); }); - it("reports an error", function (done) { + it('reports an error', function (done) { client.flushdb(function (err, res) { assert(err.message.match(/The connection has already been closed/)); done(); @@ -37,12 +37,12 @@ describe("The 'flushdb' method", function () { }); }); - describe("when connected", function () { + describe('when connected', function () { var client; beforeEach(function (done) { client = redis.createClient.apply(null, args); - client.once("ready", function () { + client.once('ready', function () { done(); }); }); @@ -51,7 +51,7 @@ describe("The 'flushdb' method", function () { client.end(true); }); - describe("when there is data in Redis", function () { + describe('when there is data in Redis', function () { beforeEach(function (done) { client.mset(key, uuid.v4(), key2, uuid.v4(), helper.isNotError()); @@ -62,38 +62,38 @@ describe("The 'flushdb' method", function () { }); }); - it("deletes all the keys", function (done) { - client.flushdb(function(err, res) { + it('deletes all the keys', function (done) { + client.flushdb(function (err, res) { assert.equal(res, 'OK'); client.mget(key, key2, function (err, res) { - assert.strictEqual(null, err, "Unexpected error returned"); - assert.strictEqual(true, Array.isArray(res), "Results object should be an array."); - assert.strictEqual(2, res.length, "Results array should have length 2."); - assert.strictEqual(null, res[0], "Redis key should have been flushed."); - assert.strictEqual(null, res[1], "Redis key should have been flushed."); + assert.strictEqual(null, err, 'Unexpected error returned'); + assert.strictEqual(true, Array.isArray(res), 'Results object should be an array.'); + assert.strictEqual(2, res.length, 'Results array should have length 2.'); + assert.strictEqual(null, res[0], 'Redis key should have been flushed.'); + assert.strictEqual(null, res[1], 'Redis key should have been flushed.'); done(err); }); }); }); - it("results in a db size of zero", function (done) { - client.flushdb(function(err, res) { + it('results in a db size of zero', function (done) { + client.flushdb(function (err, res) { client.dbsize([], function (err, res) { helper.isNotError()(err, res); helper.isType.number()(err, res); - assert.strictEqual(0, res, "Flushing db should result in db size 0"); + assert.strictEqual(0, res, 'Flushing db should result in db size 0'); done(); }); }); }); - it("results in a db size of zero without a callback", function (done) { + it('results in a db size of zero without a callback', function (done) { client.flushdb(); - setTimeout(function(err, res) { + setTimeout(function (err, res) { client.dbsize([], function (err, res) { helper.isNotError()(err, res); helper.isType.number()(err, res); - assert.strictEqual(0, res, "Flushing db should result in db size 0"); + assert.strictEqual(0, res, 'Flushing db should result in db size 0'); done(); }); }, 25); diff --git a/test/commands/geoadd.spec.js b/test/commands/geoadd.spec.js index 026d0fe7174..3614910322d 100644 --- a/test/commands/geoadd.spec.js +++ b/test/commands/geoadd.spec.js @@ -1,26 +1,26 @@ 'use strict'; -var config = require("../lib/config"); -var helper = require("../helper"); +var config = require('../lib/config'); +var helper = require('../helper'); var redis = config.redis; describe("The 'geoadd' method", function () { - helper.allTests(function(parser, ip, args) { + helper.allTests(function (parser, ip, args) { - describe("using " + parser + " and " + ip, function () { + describe('using ' + parser + ' and ' + ip, function () { var client; beforeEach(function (done) { client = redis.createClient.apply(null, args); - client.once("ready", function () { + client.once('ready', function () { client.flushdb(done); }); }); it('returns 1 if the key exists', function (done) { helper.serverVersionAtLeast.call(this, client, [3, 2, 0]); - client.geoadd("mycity:21:0:location", "13.361389","38.115556","COR", function(err, res) { + client.geoadd('mycity:21:0:location', '13.361389', '38.115556', 'COR', function (err, res) { console.log(err, res); // geoadd is still in the unstable branch. As soon as it reaches the stable one, activate this test done(); diff --git a/test/commands/get.spec.js b/test/commands/get.spec.js index cc4a7386a12..dc4b92da25d 100644 --- a/test/commands/get.spec.js +++ b/test/commands/get.spec.js @@ -1,16 +1,16 @@ 'use strict'; var assert = require('assert'); -var config = require("../lib/config"); +var config = require('../lib/config'); var helper = require('../helper'); var redis = config.redis; var uuid = require('uuid'); describe("The 'get' method", function () { - helper.allTests(function(parser, ip, args) { + helper.allTests(function (parser, ip, args) { - describe("using " + parser + " and " + ip, function () { + describe('using ' + parser + ' and ' + ip, function () { var key, value; beforeEach(function () { @@ -18,37 +18,37 @@ describe("The 'get' method", function () { value = uuid.v4(); }); - describe("when not connected", function () { + describe('when not connected', function () { var client; beforeEach(function (done) { client = redis.createClient.apply(null, args); - client.once("ready", function () { + client.once('ready', function () { client.quit(); }); client.on('end', done); }); - it("reports an error", function (done) { + it('reports an error', function (done) { client.get(key, function (err, res) { assert(err.message.match(/The connection has already been closed/)); done(); }); }); - it("reports an error promisified", function () { + it('reports an error promisified', function () { return client.getAsync(key).then(assert, function (err) { assert(err.message.match(/The connection has already been closed/)); }); }); }); - describe("when connected", function () { + describe('when connected', function () { var client; beforeEach(function (done) { client = redis.createClient.apply(null, args); - client.once("ready", function () { + client.once('ready', function () { done(); }); }); @@ -57,7 +57,7 @@ describe("The 'get' method", function () { client.end(true); }); - describe("when the key exists in Redis", function () { + describe('when the key exists in Redis', function () { beforeEach(function (done) { client.set(key, value, function (err, res) { helper.isNotError()(err, res); @@ -65,7 +65,7 @@ describe("The 'get' method", function () { }); }); - it("gets the value correctly", function (done) { + it('gets the value correctly', function (done) { client.GET(key, function (err, res) { helper.isString(value)(err, res); done(err); @@ -74,15 +74,15 @@ describe("The 'get' method", function () { it("should not throw on a get without callback (even if it's not useful)", function (done) { client.GET(key); - client.on('error', function(err) { + client.on('error', function (err) { throw err; }); setTimeout(done, 50); }); }); - describe("when the key does not exist in Redis", function () { - it("gets a null value", function (done) { + describe('when the key does not exist in Redis', function () { + it('gets a null value', function (done) { client.get(key, function (err, res) { helper.isNull()(err, res); done(err); diff --git a/test/commands/getset.spec.js b/test/commands/getset.spec.js index 0ebbb12f157..ea6b5d5ef76 100644 --- a/test/commands/getset.spec.js +++ b/test/commands/getset.spec.js @@ -1,16 +1,16 @@ 'use strict'; var assert = require('assert'); -var config = require("../lib/config"); +var config = require('../lib/config'); var helper = require('../helper'); var redis = config.redis; var uuid = require('uuid'); describe("The 'getset' method", function () { - helper.allTests(function(parser, ip, args) { + helper.allTests(function (parser, ip, args) { - describe("using " + parser + " and " + ip, function () { + describe('using ' + parser + ' and ' + ip, function () { var key, value, value2; beforeEach(function () { @@ -19,18 +19,18 @@ describe("The 'getset' method", function () { value2 = uuid.v4(); }); - describe("when not connected", function () { + describe('when not connected', function () { var client; beforeEach(function (done) { client = redis.createClient.apply(null, args); - client.once("ready", function () { + client.once('ready', function () { client.quit(); }); client.on('end', done); }); - it("reports an error", function (done) { + it('reports an error', function (done) { client.get(key, function (err, res) { assert(err.message.match(/The connection has already been closed/)); done(); @@ -38,12 +38,12 @@ describe("The 'getset' method", function () { }); }); - describe("when connected", function () { + describe('when connected', function () { var client; beforeEach(function (done) { client = redis.createClient.apply(null, args); - client.once("ready", function () { + client.once('ready', function () { done(); }); }); @@ -52,7 +52,7 @@ describe("The 'getset' method", function () { client.end(true); }); - describe("when the key exists in Redis", function () { + describe('when the key exists in Redis', function () { beforeEach(function (done) { client.set(key, value, function (err, res) { helper.isNotError()(err, res); @@ -60,7 +60,7 @@ describe("The 'getset' method", function () { }); }); - it("gets the value correctly", function (done) { + it('gets the value correctly', function (done) { client.GETSET(key, value2, function (err, res) { helper.isString(value)(err, res); client.get(key, function (err, res) { @@ -70,7 +70,7 @@ describe("The 'getset' method", function () { }); }); - it("gets the value correctly with array syntax", function (done) { + it('gets the value correctly with array syntax', function (done) { client.GETSET([key, value2], function (err, res) { helper.isString(value)(err, res); client.get(key, function (err, res) { @@ -80,7 +80,7 @@ describe("The 'getset' method", function () { }); }); - it("gets the value correctly with array syntax style 2", function (done) { + it('gets the value correctly with array syntax style 2', function (done) { client.GETSET(key, [value2], function (err, res) { helper.isString(value)(err, res); client.get(key, function (err, res) { @@ -91,8 +91,8 @@ describe("The 'getset' method", function () { }); }); - describe("when the key does not exist in Redis", function () { - it("gets a null value", function (done) { + describe('when the key does not exist in Redis', function () { + it('gets a null value', function (done) { client.getset(key, value, function (err, res) { helper.isNull()(err, res); done(err); diff --git a/test/commands/hgetall.spec.js b/test/commands/hgetall.spec.js index e6813798088..d345b13c922 100644 --- a/test/commands/hgetall.spec.js +++ b/test/commands/hgetall.spec.js @@ -1,48 +1,48 @@ 'use strict'; -var assert = require("assert"); -var config = require("../lib/config"); -var helper = require("../helper"); +var assert = require('assert'); +var config = require('../lib/config'); +var helper = require('../helper'); var redis = config.redis; describe("The 'hgetall' method", function () { - helper.allTests(function(parser, ip, args) { + helper.allTests(function (parser, ip, args) { - describe("using " + parser + " and " + ip, function () { + describe('using ' + parser + ' and ' + ip, function () { var client; describe('regular client', function () { beforeEach(function (done) { client = redis.createClient.apply(null, args); - client.once("ready", function () { + client.once('ready', function () { client.flushdb(done); }); }); it('handles simple keys and values', function (done) { - client.hmset(["hosts", "mjr", "1", "another", "23", "home", "1234"], helper.isString("OK")); - client.HGETALL(["hosts"], function (err, obj) { + client.hmset(['hosts', 'mjr', '1', 'another', '23', 'home', '1234'], helper.isString('OK')); + client.HGETALL(['hosts'], function (err, obj) { assert.strictEqual(3, Object.keys(obj).length); - assert.strictEqual("1", obj.mjr.toString()); - assert.strictEqual("23", obj.another.toString()); - assert.strictEqual("1234", obj.home.toString()); + assert.strictEqual('1', obj.mjr.toString()); + assert.strictEqual('23', obj.another.toString()); + assert.strictEqual('1234', obj.home.toString()); return done(err); }); }); it('handles fetching keys set using an object', function (done) { - client.HMSET("msg_test", { message: "hello" }, helper.isString("OK")); - client.hgetall("msg_test", function (err, obj) { + client.HMSET('msg_test', { message: 'hello' }, helper.isString('OK')); + client.hgetall('msg_test', function (err, obj) { assert.strictEqual(1, Object.keys(obj).length); - assert.strictEqual(obj.message, "hello"); + assert.strictEqual(obj.message, 'hello'); return done(err); }); }); it('handles fetching a messing key', function (done) { - client.hgetall("missing", function (err, obj) { + client.hgetall('missing', function (err, obj) { assert.strictEqual(null, obj); return done(err); }); @@ -57,18 +57,18 @@ describe("The 'hgetall' method", function () { beforeEach(function (done) { client = redis.createClient.apply(null, args); - client.once("ready", function () { + client.once('ready', function () { client.flushdb(done); }); }); it('returns binary results', function (done) { - client.hmset(["bhosts", "mjr", "1", "another", "23", "home", "1234", new Buffer([0xAA, 0xBB, 0x00, 0xF0]), new Buffer([0xCC, 0xDD, 0x00, 0xF0])], helper.isString("OK")); - client.HGETALL("bhosts", function (err, obj) { + client.hmset(['bhosts', 'mjr', '1', 'another', '23', 'home', '1234', new Buffer([0xAA, 0xBB, 0x00, 0xF0]), new Buffer([0xCC, 0xDD, 0x00, 0xF0])], helper.isString('OK')); + client.HGETALL('bhosts', function (err, obj) { assert.strictEqual(4, Object.keys(obj).length); - assert.strictEqual("1", obj.mjr.toString()); - assert.strictEqual("23", obj.another.toString()); - assert.strictEqual("1234", obj.home.toString()); + assert.strictEqual('1', obj.mjr.toString()); + assert.strictEqual('23', obj.another.toString()); + assert.strictEqual('1234', obj.home.toString()); assert.strictEqual((new Buffer([0xAA, 0xBB, 0x00, 0xF0])).toString('binary'), Object.keys(obj)[3]); assert.strictEqual((new Buffer([0xCC, 0xDD, 0x00, 0xF0])).toString('binary'), obj[(new Buffer([0xAA, 0xBB, 0x00, 0xF0])).toString('binary')].toString('binary')); return done(err); diff --git a/test/commands/hincrby.spec.js b/test/commands/hincrby.spec.js index 5e933d23ce3..e94232d9a0e 100644 --- a/test/commands/hincrby.spec.js +++ b/test/commands/hincrby.spec.js @@ -1,33 +1,33 @@ 'use strict'; -var config = require("../lib/config"); -var helper = require("../helper"); +var config = require('../lib/config'); +var helper = require('../helper'); var redis = config.redis; describe("The 'hincrby' method", function () { - helper.allTests(function(parser, ip, args) { + helper.allTests(function (parser, ip, args) { - describe("using " + parser + " and " + ip, function () { + describe('using ' + parser + ' and ' + ip, function () { var client; - var hash = "test hash"; + var hash = 'test hash'; beforeEach(function (done) { client = redis.createClient.apply(null, args); - client.once("ready", function () { + client.once('ready', function () { client.flushdb(done); }); }); it('increments a key that has already been set', function (done) { - var field = "field 1"; + var field = 'field 1'; client.HSET(hash, field, 33); client.hincrby(hash, field, 10, helper.isNumber(43, done)); }); it('increments a key that has not been set', function (done) { - var field = "field 2"; + var field = 'field 2'; client.HINCRBY(hash, field, 10, helper.isNumber(10, done)); }); diff --git a/test/commands/hlen.spec.js b/test/commands/hlen.spec.js index b8fa3e99c6e..5dcab6ce8e2 100644 --- a/test/commands/hlen.spec.js +++ b/test/commands/hlen.spec.js @@ -1,27 +1,27 @@ 'use strict'; -var config = require("../lib/config"); -var helper = require("../helper"); +var config = require('../lib/config'); +var helper = require('../helper'); var redis = config.redis; describe("The 'hlen' method", function () { - helper.allTests(function(parser, ip, args) { + helper.allTests(function (parser, ip, args) { - describe("using " + parser + " and " + ip, function () { + describe('using ' + parser + ' and ' + ip, function () { var client; beforeEach(function (done) { client = redis.createClient.apply(null, args); - client.once("ready", function () { + client.once('ready', function () { client.flushdb(done); }); }); it('reports the count of keys', function (done) { - var hash = "test hash"; - var field1 = new Buffer("0123456789"); - var value1 = new Buffer("abcdefghij"); + var hash = 'test hash'; + var field1 = new Buffer('0123456789'); + var value1 = new Buffer('abcdefghij'); var field2 = new Buffer(0); var value2 = new Buffer(0); diff --git a/test/commands/hmget.spec.js b/test/commands/hmget.spec.js index 996ed255709..09bb89f258e 100644 --- a/test/commands/hmget.spec.js +++ b/test/commands/hmget.spec.js @@ -1,62 +1,62 @@ 'use strict'; -var assert = require("assert"); -var config = require("../lib/config"); -var helper = require("../helper"); +var assert = require('assert'); +var config = require('../lib/config'); +var helper = require('../helper'); var redis = config.redis; describe("The 'hmget' method", function () { - helper.allTests(function(parser, ip, args) { + helper.allTests(function (parser, ip, args) { - describe("using " + parser + " and " + ip, function () { + describe('using ' + parser + ' and ' + ip, function () { var client; var hash = 'test hash'; beforeEach(function (done) { client = redis.createClient.apply(null, args); - client.once("error", done); - client.once("ready", function () { + client.once('error', done); + client.once('ready', function () { client.flushdb(); - client.HMSET(hash, {"0123456789": "abcdefghij", "some manner of key": "a type of value"}, helper.isString('OK', done)); + client.HMSET(hash, {'0123456789': 'abcdefghij', 'some manner of key': 'a type of value'}, helper.isString('OK', done)); }); }); it('allows keys to be specified using multiple arguments', function (done) { - client.hmget(hash, "0123456789", "some manner of key", function (err, reply) { - assert.strictEqual("abcdefghij", reply[0].toString()); - assert.strictEqual("a type of value", reply[1].toString()); + client.hmget(hash, '0123456789', 'some manner of key', function (err, reply) { + assert.strictEqual('abcdefghij', reply[0].toString()); + assert.strictEqual('a type of value', reply[1].toString()); return done(err); }); }); it('allows keys to be specified by passing an array without manipulating the array', function (done) { - var data = ["0123456789", "some manner of key"]; + var data = ['0123456789', 'some manner of key']; client.HMGET(hash, data, function (err, reply) { assert.strictEqual(data.length, 2); - assert.strictEqual("abcdefghij", reply[0].toString()); - assert.strictEqual("a type of value", reply[1].toString()); + assert.strictEqual('abcdefghij', reply[0].toString()); + assert.strictEqual('a type of value', reply[1].toString()); return done(err); }); }); it('allows keys to be specified by passing an array as first argument', function (done) { - client.HMGET([hash, "0123456789", "some manner of key"], function (err, reply) { - assert.strictEqual("abcdefghij", reply[0].toString()); - assert.strictEqual("a type of value", reply[1].toString()); + client.HMGET([hash, '0123456789', 'some manner of key'], function (err, reply) { + assert.strictEqual('abcdefghij', reply[0].toString()); + assert.strictEqual('a type of value', reply[1].toString()); return done(err); }); }); it('allows a single key to be specified in an array', function (done) { - client.HMGET(hash, ["0123456789"], function (err, reply) { - assert.strictEqual("abcdefghij", reply[0].toString()); + client.HMGET(hash, ['0123456789'], function (err, reply) { + assert.strictEqual('abcdefghij', reply[0].toString()); return done(err); }); }); it('allows keys to be specified that have not yet been set', function (done) { - client.HMGET(hash, "missing thing", "another missing thing", function (err, reply) { + client.HMGET(hash, 'missing thing', 'another missing thing', function (err, reply) { assert.strictEqual(null, reply[0]); assert.strictEqual(null, reply[1]); return done(err); diff --git a/test/commands/hmset.spec.js b/test/commands/hmset.spec.js index e6f99312364..c96b7c5b670 100644 --- a/test/commands/hmset.spec.js +++ b/test/commands/hmset.spec.js @@ -1,27 +1,27 @@ 'use strict'; -var assert = require("assert"); -var config = require("../lib/config"); -var helper = require("../helper"); +var assert = require('assert'); +var config = require('../lib/config'); +var helper = require('../helper'); var redis = config.redis; describe("The 'hmset' method", function () { - helper.allTests(function(parser, ip, args) { + helper.allTests(function (parser, ip, args) { - describe("using " + parser + " and " + ip, function () { + describe('using ' + parser + ' and ' + ip, function () { var client; var hash = 'test hash'; beforeEach(function (done) { client = redis.createClient.apply(null, args); - client.once("ready", function () { + client.once('ready', function () { client.flushdb(done); }); }); it('handles redis-style syntax', function (done) { - client.HMSET(hash, "0123456789", "abcdefghij", "some manner of key", "a type of value", "otherTypes", 555, helper.isString('OK')); + client.HMSET(hash, '0123456789', 'abcdefghij', 'some manner of key', 'a type of value', 'otherTypes', 555, helper.isString('OK')); client.HGETALL(hash, function (err, obj) { assert.equal(obj['0123456789'], 'abcdefghij'); assert.equal(obj['some manner of key'], 'a type of value'); @@ -30,7 +30,7 @@ describe("The 'hmset' method", function () { }); it('handles object-style syntax', function (done) { - client.hmset(hash, {"0123456789": "abcdefghij", "some manner of key": "a type of value", "otherTypes": 555}, helper.isString('OK')); + client.hmset(hash, {'0123456789': 'abcdefghij', 'some manner of key': 'a type of value', 'otherTypes': 555}, helper.isString('OK')); client.HGETALL(hash, function (err, obj) { assert.equal(obj['0123456789'], 'abcdefghij'); assert.equal(obj['some manner of key'], 'a type of value'); @@ -39,7 +39,7 @@ describe("The 'hmset' method", function () { }); it('handles object-style syntax and the key being a number', function (done) { - client.HMSET(231232, {"0123456789": "abcdefghij", "some manner of key": "a type of value", "otherTypes": 555}, helper.isString('OK')); + client.HMSET(231232, {'0123456789': 'abcdefghij', 'some manner of key': 'a type of value', 'otherTypes': 555}, helper.isString('OK')); client.HGETALL(231232, function (err, obj) { assert.equal(obj['0123456789'], 'abcdefghij'); assert.equal(obj['some manner of key'], 'a type of value'); @@ -101,7 +101,7 @@ describe("The 'hmset' method", function () { }); it('handles object-style syntax without callback', function (done) { - client.HMSET(hash, {"0123456789": "abcdefghij", "some manner of key": "a type of value"}); + client.HMSET(hash, {'0123456789': 'abcdefghij', 'some manner of key': 'a type of value'}); client.HGETALL(hash, function (err, obj) { assert.equal(obj['0123456789'], 'abcdefghij'); assert.equal(obj['some manner of key'], 'a type of value'); diff --git a/test/commands/hset.spec.js b/test/commands/hset.spec.js index d87f4d04ce0..d0ce7f5481b 100644 --- a/test/commands/hset.spec.js +++ b/test/commands/hset.spec.js @@ -1,39 +1,39 @@ 'use strict'; -var assert = require("assert"); -var config = require("../lib/config"); -var helper = require("../helper"); +var assert = require('assert'); +var config = require('../lib/config'); +var helper = require('../helper'); var redis = config.redis; describe("The 'hset' method", function () { - helper.allTests(function(parser, ip, args) { + helper.allTests(function (parser, ip, args) { - describe("using " + parser + " and " + ip, function () { + describe('using ' + parser + ' and ' + ip, function () { var client; var hash = 'test hash'; beforeEach(function (done) { client = redis.createClient.apply(null, args); - client.once("ready", function () { + client.once('ready', function () { client.flushdb(done); }); }); it('allows a value to be set in a hash', function (done) { - var field = new Buffer("0123456789"); - var value = new Buffer("abcdefghij"); + var field = new Buffer('0123456789'); + var value = new Buffer('abcdefghij'); client.hset(hash, field, value, helper.isNumber(1)); client.HGET(hash, field, helper.isString(value.toString(), done)); }); it('handles an empty value', function (done) { - var field = new Buffer("0123456789"); + var field = new Buffer('0123456789'); var value = new Buffer(0); client.HSET(hash, field, value, helper.isNumber(1)); - client.HGET([hash, field], helper.isString("", done)); + client.HGET([hash, field], helper.isString('', done)); }); it('handles empty key and value', function (done) { @@ -46,11 +46,11 @@ describe("The 'hset' method", function () { }); it('warns if someone passed a array either as field or as value', function (done) { - var hash = "test hash"; - var field = "array"; + var hash = 'test hash'; + var field = 'array'; // This would be converted to "array contents" but if you use more than one entry, // it'll result in e.g. "array contents,second content" and this is not supported and considered harmful - var value = ["array contents"]; + var value = ['array contents']; client.on('warning', function (msg) { assert.strictEqual( msg, @@ -64,23 +64,23 @@ describe("The 'hset' method", function () { }); it('does not error when a buffer and date are set as values on the same hash', function (done) { - var hash = "test hash"; - var field1 = "buffer"; - var value1 = new Buffer("abcdefghij"); - var field2 = "date"; + var hash = 'test hash'; + var field1 = 'buffer'; + var value1 = new Buffer('abcdefghij'); + var field2 = 'date'; var value2 = new Date(); - client.HMSET(hash, field1, value1, field2, value2, helper.isString("OK", done)); + client.HMSET(hash, field1, value1, field2, value2, helper.isString('OK', done)); }); it('does not error when a buffer and date are set as fields on the same hash', function (done) { - var hash = "test hash"; - var value1 = "buffer"; - var field1 = new Buffer("abcdefghij"); - var value2 = "date"; + var hash = 'test hash'; + var value1 = 'buffer'; + var field1 = new Buffer('abcdefghij'); + var value2 = 'date'; var field2 = new Date(); - client.HMSET(hash, field1, value1, field2, value2, helper.isString("OK", done)); + client.HMSET(hash, field1, value1, field2, value2, helper.isString('OK', done)); }); afterEach(function () { diff --git a/test/commands/incr.spec.js b/test/commands/incr.spec.js index b0d605ae221..ab385ecbbf2 100644 --- a/test/commands/incr.spec.js +++ b/test/commands/incr.spec.js @@ -1,24 +1,24 @@ 'use strict'; var assert = require('assert'); -var config = require("../lib/config"); +var config = require('../lib/config'); var helper = require('../helper'); var redis = config.redis; describe("The 'incr' method", function () { - helper.allTests(function(parser, ip, args) { + helper.allTests(function (parser, ip, args) { - describe("using " + parser + " and " + ip, function () { - var key = "sequence"; + describe('using ' + parser + ' and ' + ip, function () { + var key = 'sequence'; - describe("when not connected", function () { + describe('when not connected', function () { var client; beforeEach(function (done) { client = redis.createClient.apply(null, args); - client.once("ready", function () { - client.set(key, "9007199254740992", function (err, res) { + client.once('ready', function () { + client.set(key, '9007199254740992', function (err, res) { helper.isNotError()(err, res); client.quit(); }); @@ -30,7 +30,7 @@ describe("The 'incr' method", function () { client.end(true); }); - it("reports an error", function (done) { + it('reports an error', function (done) { client.incr(function (err, res) { assert(err.message.match(/The connection has already been closed/)); done(); @@ -38,7 +38,7 @@ describe("The 'incr' method", function () { }); }); - describe("when connected and a value in Redis", function () { + describe('when connected and a value in Redis', function () { var client; before(function (done) { @@ -51,9 +51,9 @@ describe("The 'incr' method", function () { 9007199254740997 -> 9007199254740996 */ client = redis.createClient.apply(null, args); - client.once("error", done); - client.once("ready", function () { - client.set(key, "9007199254740992", function (err, res) { + client.once('error', done); + client.once('ready', function () { + client.set(key, '9007199254740992', function (err, res) { helper.isNotError()(err, res); done(); }); @@ -64,41 +64,41 @@ describe("The 'incr' method", function () { client.end(true); }); - it("changes the last digit from 2 to 3", function (done) { + it('changes the last digit from 2 to 3', function (done) { client.INCR(key, function (err, res) { - helper.isString("9007199254740993")(err, res); + helper.isString('9007199254740993')(err, res); done(err); }); }); - describe("and we call it again", function () { - it("changes the last digit from 3 to 4", function (done) { + describe('and we call it again', function () { + it('changes the last digit from 3 to 4', function (done) { client.incr(key, function (err, res) { - helper.isString("9007199254740994")(err, res); + helper.isString('9007199254740994')(err, res); done(err); }); }); - describe("and again", function () { - it("changes the last digit from 4 to 5", function (done) { + describe('and again', function () { + it('changes the last digit from 4 to 5', function (done) { client.incr(key, function (err, res) { - helper.isString("9007199254740995")(err, res); + helper.isString('9007199254740995')(err, res); done(err); }); }); - describe("and again", function () { - it("changes the last digit from 5 to 6", function (done) { + describe('and again', function () { + it('changes the last digit from 5 to 6', function (done) { client.incr(key, function (err, res) { - helper.isString("9007199254740996")(err, res); + helper.isString('9007199254740996')(err, res); done(err); }); }); - describe("and again", function () { - it("changes the last digit from 6 to 7", function (done) { + describe('and again', function () { + it('changes the last digit from 6 to 7', function (done) { client.incr(key, function (err, res) { - helper.isString("9007199254740997")(err, res); + helper.isString('9007199254740997')(err, res); done(err); }); }); diff --git a/test/commands/info.spec.js b/test/commands/info.spec.js index ca4ec700805..e0a01457ece 100644 --- a/test/commands/info.spec.js +++ b/test/commands/info.spec.js @@ -1,20 +1,20 @@ 'use strict'; var assert = require('assert'); -var config = require("../lib/config"); +var config = require('../lib/config'); var helper = require('../helper'); var redis = config.redis; describe("The 'info' method", function () { - helper.allTests(function(parser, ip, args) { + helper.allTests(function (parser, ip, args) { - describe("using " + parser + " and " + ip, function () { + describe('using ' + parser + ' and ' + ip, function () { var client; beforeEach(function (done) { client = redis.createClient.apply(null, args); - client.once("ready", function () { + client.once('ready', function () { client.flushall(done); }); }); @@ -23,7 +23,7 @@ describe("The 'info' method", function () { client.end(true); }); - it("update server_info after a info command", function (done) { + it('update server_info after a info command', function (done) { client.set('foo', 'bar'); client.info(); client.select(2, function () { @@ -37,7 +37,7 @@ describe("The 'info' method", function () { }, 150); }); - it("works with optional section provided with and without callback", function (done) { + it('works with optional section provided with and without callback', function (done) { client.set('foo', 'bar'); client.info('keyspace'); client.select(2, function () { @@ -65,7 +65,7 @@ describe("The 'info' method", function () { client.info(function () {}); }); - it("emit error after a failure", function (done) { + it('emit error after a failure', function (done) { client.info(); client.once('error', function (err) { assert.strictEqual(err.code, 'UNCERTAIN_STATE'); diff --git a/test/commands/keys.spec.js b/test/commands/keys.spec.js index 7af3ae40c97..1aff0cea146 100644 --- a/test/commands/keys.spec.js +++ b/test/commands/keys.spec.js @@ -1,31 +1,31 @@ 'use strict'; -var assert = require("assert"); -var config = require("../lib/config"); -var crypto = require("crypto"); -var helper = require("../helper"); +var assert = require('assert'); +var config = require('../lib/config'); +var crypto = require('crypto'); +var helper = require('../helper'); var redis = config.redis; describe("The 'keys' method", function () { - helper.allTests(function(parser, ip, args) { + helper.allTests(function (parser, ip, args) { - describe("using " + parser + " and " + ip, function () { + describe('using ' + parser + ' and ' + ip, function () { var client; beforeEach(function (done) { client = redis.createClient.apply(null, args); - client.once("ready", function () { + client.once('ready', function () { client.flushall(done); }); }); it('returns matching keys', function (done) { - client.mset(["test keys 1", "test val 1", "test keys 2", "test val 2"], helper.isString("OK")); - client.KEYS("test keys*", function (err, results) { + client.mset(['test keys 1', 'test val 1', 'test keys 2', 'test val 2'], helper.isString('OK')); + client.KEYS('test keys*', function (err, results) { assert.strictEqual(2, results.length); - assert.ok(~results.indexOf("test keys 1")); - assert.ok(~results.indexOf("test keys 2")); + assert.ok(~results.indexOf('test keys 1')); + assert.ok(~results.indexOf('test keys 2')); return done(err); }); }); @@ -35,18 +35,18 @@ describe("The 'keys' method", function () { for (var i = 0; i < 200; i++) { var key_value = [ - "multibulk:" + crypto.randomBytes(256).toString("hex"), // use long strings as keys to ensure generation of large packet - "test val " + i + 'multibulk:' + crypto.randomBytes(256).toString('hex'), // use long strings as keys to ensure generation of large packet + 'test val ' + i ]; keys_values.push(key_value); } - client.mset(keys_values.reduce(function(a, b) { + client.mset(keys_values.reduce(function (a, b) { return a.concat(b); - }), helper.isString("OK")); + }), helper.isString('OK')); - client.keys("multibulk:*", function(err, results) { - assert.deepEqual(keys_values.map(function(val) { + client.keys('multibulk:*', function (err, results) { + assert.deepEqual(keys_values.map(function (val) { return val[0]; }).sort(), results.sort()); return done(err); diff --git a/test/commands/mget.spec.js b/test/commands/mget.spec.js index 8e395dedbe7..83d69f837f9 100644 --- a/test/commands/mget.spec.js +++ b/test/commands/mget.spec.js @@ -1,64 +1,64 @@ 'use strict'; -var assert = require("assert"); -var config = require("../lib/config"); -var helper = require("../helper"); +var assert = require('assert'); +var config = require('../lib/config'); +var helper = require('../helper'); var redis = config.redis; describe("The 'mget' method", function () { - helper.allTests(function(parser, ip, args) { + helper.allTests(function (parser, ip, args) { - describe("using " + parser + " and " + ip, function () { + describe('using ' + parser + ' and ' + ip, function () { var client; beforeEach(function (done) { client = redis.createClient.apply(null, args); - client.once("error", done); - client.once("ready", function () { + client.once('error', done); + client.once('ready', function () { client.flushdb(); - client.mset(["mget keys 1", "mget val 1", "mget keys 2", "mget val 2", "mget keys 3", "mget val 3"], done); + client.mset(['mget keys 1', 'mget val 1', 'mget keys 2', 'mget val 2', 'mget keys 3', 'mget val 3'], done); }); }); it('handles fetching multiple keys in argument form', function (done) { - client.mset(["mget keys 1", "mget val 1", "mget keys 2", "mget val 2", "mget keys 3", "mget val 3"], helper.isString("OK")); - client.MGET("mget keys 1", "mget keys 2", "mget keys 3", function (err, results) { + client.mset(['mget keys 1', 'mget val 1', 'mget keys 2', 'mget val 2', 'mget keys 3', 'mget val 3'], helper.isString('OK')); + client.MGET('mget keys 1', 'mget keys 2', 'mget keys 3', function (err, results) { assert.strictEqual(3, results.length); - assert.strictEqual("mget val 1", results[0].toString()); - assert.strictEqual("mget val 2", results[1].toString()); - assert.strictEqual("mget val 3", results[2].toString()); + assert.strictEqual('mget val 1', results[0].toString()); + assert.strictEqual('mget val 2', results[1].toString()); + assert.strictEqual('mget val 3', results[2].toString()); return done(err); }); }); it('handles fetching multiple keys via an array', function (done) { - client.mget(["mget keys 1", "mget keys 2", "mget keys 3"], function (err, results) { - assert.strictEqual("mget val 1", results[0].toString()); - assert.strictEqual("mget val 2", results[1].toString()); - assert.strictEqual("mget val 3", results[2].toString()); + client.mget(['mget keys 1', 'mget keys 2', 'mget keys 3'], function (err, results) { + assert.strictEqual('mget val 1', results[0].toString()); + assert.strictEqual('mget val 2', results[1].toString()); + assert.strictEqual('mget val 3', results[2].toString()); return done(err); }); }); it('handles fetching multiple keys, when some keys do not exist', function (done) { - client.MGET("mget keys 1", ["some random shit", "mget keys 2", "mget keys 3"], function (err, results) { + client.MGET('mget keys 1', ['some random shit', 'mget keys 2', 'mget keys 3'], function (err, results) { assert.strictEqual(4, results.length); - assert.strictEqual("mget val 1", results[0].toString()); + assert.strictEqual('mget val 1', results[0].toString()); assert.strictEqual(null, results[1]); - assert.strictEqual("mget val 2", results[2].toString()); - assert.strictEqual("mget val 3", results[3].toString()); + assert.strictEqual('mget val 2', results[2].toString()); + assert.strictEqual('mget val 3', results[3].toString()); return done(err); }); }); it('handles fetching multiple keys, when some keys do not exist promisified', function () { - return client.MGETAsync("mget keys 1", ["some random shit", "mget keys 2", "mget keys 3"]).then(function (results) { + return client.MGETAsync('mget keys 1', ['some random shit', 'mget keys 2', 'mget keys 3']).then(function (results) { assert.strictEqual(4, results.length); - assert.strictEqual("mget val 1", results[0].toString()); + assert.strictEqual('mget val 1', results[0].toString()); assert.strictEqual(null, results[1]); - assert.strictEqual("mget val 2", results[2].toString()); - assert.strictEqual("mget val 3", results[3].toString()); + assert.strictEqual('mget val 2', results[2].toString()); + assert.strictEqual('mget val 3', results[3].toString()); }); }); diff --git a/test/commands/mset.spec.js b/test/commands/mset.spec.js index 81ab6d3bc67..c6963641a2d 100644 --- a/test/commands/mset.spec.js +++ b/test/commands/mset.spec.js @@ -1,16 +1,16 @@ 'use strict'; var assert = require('assert'); -var config = require("../lib/config"); +var config = require('../lib/config'); var helper = require('../helper'); var redis = config.redis; var uuid = require('uuid'); describe("The 'mset' method", function () { - helper.allTests(function(parser, ip, args) { + helper.allTests(function (parser, ip, args) { - describe("using " + parser + " and " + ip, function () { + describe('using ' + parser + ' and ' + ip, function () { var key, value, key2, value2; beforeEach(function () { @@ -20,18 +20,18 @@ describe("The 'mset' method", function () { value2 = uuid.v4(); }); - describe("when not connected", function () { + describe('when not connected', function () { var client; beforeEach(function (done) { client = redis.createClient.apply(null, args); - client.once("ready", function () { + client.once('ready', function () { client.quit(); }); client.on('end', done); }); - it("reports an error", function (done) { + it('reports an error', function (done) { client.mset(key, value, key2, value2, function (err, res) { assert(err.message.match(/The connection has already been closed/)); done(); @@ -39,12 +39,12 @@ describe("The 'mset' method", function () { }); }); - describe("when connected", function () { + describe('when connected', function () { var client; beforeEach(function (done) { client = redis.createClient.apply(null, args); - client.once("ready", function () { + client.once('ready', function () { done(); }); }); @@ -53,10 +53,10 @@ describe("The 'mset' method", function () { client.end(true); }); - describe("and a callback is specified", function () { - describe("with valid parameters", function () { - it("sets the value correctly", function (done) { - client.mset(key, value, key2, value2, function(err) { + describe('and a callback is specified', function () { + describe('with valid parameters', function () { + it('sets the value correctly', function (done) { + client.mset(key, value, key2, value2, function (err) { if (err) { return done(err); } @@ -67,7 +67,7 @@ describe("The 'mset' method", function () { }); describe("with undefined 'key' parameter and missing 'value' parameter", function () { - it("reports an error", function (done) { + it('reports an error', function (done) { client.mset(undefined, function (err, res) { helper.isError()(err, null); done(); @@ -77,15 +77,15 @@ describe("The 'mset' method", function () { }); - describe("and no callback is specified", function () { - describe("with valid parameters", function () { - it("sets the value correctly", function (done) { + describe('and no callback is specified', function () { + describe('with valid parameters', function () { + it('sets the value correctly', function (done) { client.mset(key, value2, key2, value); client.get(key, helper.isString(value2)); client.get(key2, helper.isString(value, done)); }); - it("sets the value correctly with array syntax", function (done) { + it('sets the value correctly with array syntax', function (done) { client.mset([key, value2, key2, value]); client.get(key, helper.isString(value2)); client.get(key2, helper.isString(value, done)); @@ -94,7 +94,7 @@ describe("The 'mset' method", function () { describe("with undefined 'key' and missing 'value' parameter", function () { // this behavior is different from the 'set' behavior. - it("emits an error", function (done) { + it('emits an error', function (done) { client.on('error', function (err) { assert.equal(err.message, "ERR wrong number of arguments for 'mset' command"); done(); diff --git a/test/commands/msetnx.spec.js b/test/commands/msetnx.spec.js index 59cd62187c5..a2f2ab17249 100644 --- a/test/commands/msetnx.spec.js +++ b/test/commands/msetnx.spec.js @@ -1,33 +1,33 @@ 'use strict'; -var config = require("../lib/config"); -var helper = require("../helper"); +var config = require('../lib/config'); +var helper = require('../helper'); var redis = config.redis; describe("The 'msetnx' method", function () { - helper.allTests(function(parser, ip, args) { + helper.allTests(function (parser, ip, args) { - describe("using " + parser + " and " + ip, function () { + describe('using ' + parser + ' and ' + ip, function () { var client; beforeEach(function (done) { client = redis.createClient.apply(null, args); - client.once("ready", function () { + client.once('ready', function () { client.flushdb(done); }); }); it('if any keys exist entire operation fails', function (done) { - client.mset(["mset1", "val1", "mset2", "val2", "mset3", "val3"], helper.isString("OK")); - client.MSETNX(["mset3", "val3", "mset4", "val4"], helper.isNumber(0)); - client.exists(["mset4"], helper.isNumber(0, done)); + client.mset(['mset1', 'val1', 'mset2', 'val2', 'mset3', 'val3'], helper.isString('OK')); + client.MSETNX(['mset3', 'val3', 'mset4', 'val4'], helper.isNumber(0)); + client.exists(['mset4'], helper.isNumber(0, done)); }); it('sets multiple keys if all keys are not set', function (done) { - client.msetnx(["mset3", "val3", "mset4", "val4"], helper.isNumber(1)); - client.exists(["mset3"], helper.isNumber(1)); - client.exists(["mset3"], helper.isNumber(1, done)); + client.msetnx(['mset3', 'val3', 'mset4', 'val4'], helper.isNumber(1)); + client.exists(['mset3'], helper.isNumber(1)); + client.exists(['mset3'], helper.isNumber(1, done)); }); afterEach(function () { diff --git a/test/commands/randomkey.test.js b/test/commands/randomkey.test.js index 671942effed..cf453e12e4b 100644 --- a/test/commands/randomkey.test.js +++ b/test/commands/randomkey.test.js @@ -1,26 +1,26 @@ 'use strict'; -var assert = require("assert"); -var config = require("../lib/config"); -var helper = require("../helper"); +var assert = require('assert'); +var config = require('../lib/config'); +var helper = require('../helper'); var redis = config.redis; describe("The 'randomkey' method", function () { - helper.allTests(function(parser, ip, args) { + helper.allTests(function (parser, ip, args) { - describe("using " + parser + " and " + ip, function () { + describe('using ' + parser + ' and ' + ip, function () { var client; beforeEach(function (done) { client = redis.createClient.apply(null, args); - client.once("ready", function () { + client.once('ready', function () { client.flushdb(done); }); }); it('returns a random key', function (done) { - client.mset(["test keys 1", "test val 1", "test keys 2", "test val 2"], helper.isString('OK')); + client.mset(['test keys 1', 'test val 1', 'test keys 2', 'test val 2'], helper.isString('OK')); client.RANDOMKEY([], function (err, results) { assert.strictEqual(true, /test keys.+/.test(results)); return done(err); diff --git a/test/commands/rename.spec.js b/test/commands/rename.spec.js index e6d8b938a10..cb3d7c31688 100644 --- a/test/commands/rename.spec.js +++ b/test/commands/rename.spec.js @@ -1,33 +1,33 @@ 'use strict'; -var config = require("../lib/config"); -var helper = require("../helper"); +var config = require('../lib/config'); +var helper = require('../helper'); var redis = config.redis; describe("The 'rename' method", function () { - helper.allTests(function(parser, ip, args) { + helper.allTests(function (parser, ip, args) { - describe("using " + parser + " and " + ip, function () { + describe('using ' + parser + ' and ' + ip, function () { var client; beforeEach(function (done) { client = redis.createClient.apply(null, args); - client.once("ready", function () { + client.once('ready', function () { client.flushdb(done); }); }); it('populates the new key', function (done) { - client.set(['foo', 'bar'], helper.isString("OK")); - client.rename(["foo", "new foo"], helper.isString("OK")); - client.exists(["new foo"], helper.isNumber(1, done)); + client.set(['foo', 'bar'], helper.isString('OK')); + client.rename(['foo', 'new foo'], helper.isString('OK')); + client.exists(['new foo'], helper.isNumber(1, done)); }); it('removes the old key', function (done) { - client.set(['foo', 'bar'], helper.isString("OK")); - client.RENAME(["foo", "new foo"], helper.isString("OK")); - client.exists(["foo"], helper.isNumber(0, done)); + client.set(['foo', 'bar'], helper.isString('OK')); + client.RENAME(['foo', 'new foo'], helper.isString('OK')); + client.exists(['foo'], helper.isNumber(0, done)); }); afterEach(function () { diff --git a/test/commands/renamenx.spec.js b/test/commands/renamenx.spec.js index ef503c96fb5..3da640a0d8d 100644 --- a/test/commands/renamenx.spec.js +++ b/test/commands/renamenx.spec.js @@ -1,19 +1,19 @@ 'use strict'; -var config = require("../lib/config"); -var helper = require("../helper"); +var config = require('../lib/config'); +var helper = require('../helper'); var redis = config.redis; describe("The 'renamenx' method", function () { - helper.allTests(function(parser, ip, args) { + helper.allTests(function (parser, ip, args) { - describe("using " + parser + " and " + ip, function () { + describe('using ' + parser + ' and ' + ip, function () { var client; beforeEach(function (done) { client = redis.createClient.apply(null, args); - client.once("ready", function () { + client.once('ready', function () { client.flushdb(done); }); }); diff --git a/test/commands/rpush.spec.js b/test/commands/rpush.spec.js index 59d776cbcc7..d137dc6473d 100644 --- a/test/commands/rpush.spec.js +++ b/test/commands/rpush.spec.js @@ -1,27 +1,27 @@ 'use strict'; -var config = require("../lib/config"); -var helper = require("../helper"); +var config = require('../lib/config'); +var helper = require('../helper'); var redis = config.redis; var assert = require('assert'); describe("The 'rpush' command", function () { - helper.allTests(function(parser, ip, args) { + helper.allTests(function (parser, ip, args) { - describe("using " + parser + " and " + ip, function () { + describe('using ' + parser + ' and ' + ip, function () { var client; beforeEach(function (done) { client = redis.createClient.apply(null, args); - client.once("ready", function () { + client.once('ready', function () { client.flushdb(done); }); }); it('inserts multiple values at a time into a list', function (done) { client.rpush('test', ['list key', 'should be a list']); - client.lrange('test', 0, -1, function(err, res) { + client.lrange('test', 0, -1, function (err, res) { assert.equal(res[0], 'list key'); assert.equal(res[1], 'should be a list'); done(err); diff --git a/test/commands/sadd.spec.js b/test/commands/sadd.spec.js index 6333d940ff2..c2316739b76 100644 --- a/test/commands/sadd.spec.js +++ b/test/commands/sadd.spec.js @@ -1,20 +1,20 @@ 'use strict'; -var assert = require("assert"); -var config = require("../lib/config"); -var helper = require("../helper"); +var assert = require('assert'); +var config = require('../lib/config'); +var helper = require('../helper'); var redis = config.redis; describe("The 'sadd' method", function () { - helper.allTests(function(parser, ip, args) { + helper.allTests(function (parser, ip, args) { - describe("using " + parser + " and " + ip, function () { + describe('using ' + parser + ' and ' + ip, function () { var client; beforeEach(function (done) { client = redis.createClient.apply(null, args); - client.once("ready", function () { + client.once('ready', function () { client.flushdb(done); }); }); @@ -33,23 +33,23 @@ describe("The 'sadd' method", function () { }); it('allows multiple values to be added to the set', function (done) { - client.sadd("set0", ["member0", "member1", "member2"], helper.isNumber(3)); - client.smembers("set0", function (err, res) { + client.sadd('set0', ['member0', 'member1', 'member2'], helper.isNumber(3)); + client.smembers('set0', function (err, res) { assert.strictEqual(res.length, 3); - assert.ok(~res.indexOf("member0")); - assert.ok(~res.indexOf("member1")); - assert.ok(~res.indexOf("member2")); + assert.ok(~res.indexOf('member0')); + assert.ok(~res.indexOf('member1')); + assert.ok(~res.indexOf('member2')); return done(err); }); }); it('allows multiple values to be added to the set with a different syntax', function (done) { - client.sadd(["set0", "member0", "member1", "member2"], helper.isNumber(3)); - client.smembers("set0", function (err, res) { + client.sadd(['set0', 'member0', 'member1', 'member2'], helper.isNumber(3)); + client.smembers('set0', function (err, res) { assert.strictEqual(res.length, 3); - assert.ok(~res.indexOf("member0")); - assert.ok(~res.indexOf("member1")); - assert.ok(~res.indexOf("member2")); + assert.ok(~res.indexOf('member0')); + assert.ok(~res.indexOf('member1')); + assert.ok(~res.indexOf('member2')); return done(err); }); }); diff --git a/test/commands/scard.spec.js b/test/commands/scard.spec.js index 162513b4ed8..5cf9b4eb73c 100644 --- a/test/commands/scard.spec.js +++ b/test/commands/scard.spec.js @@ -1,19 +1,19 @@ 'use strict'; -var config = require("../lib/config"); -var helper = require("../helper"); +var config = require('../lib/config'); +var helper = require('../helper'); var redis = config.redis; describe("The 'scard' method", function () { - helper.allTests(function(parser, ip, args) { + helper.allTests(function (parser, ip, args) { - describe("using " + parser + " and " + ip, function () { + describe('using ' + parser + ' and ' + ip, function () { var client; beforeEach(function (done) { client = redis.createClient.apply(null, args); - client.once("ready", function () { + client.once('ready', function () { client.flushdb(done); }); }); diff --git a/test/commands/script.spec.js b/test/commands/script.spec.js index e69f25025ea..25a44930b5e 100644 --- a/test/commands/script.spec.js +++ b/test/commands/script.spec.js @@ -1,23 +1,23 @@ 'use strict'; -var assert = require("assert"); -var config = require("../lib/config"); -var crypto = require("crypto"); -var helper = require("../helper"); +var assert = require('assert'); +var config = require('../lib/config'); +var crypto = require('crypto'); +var helper = require('../helper'); var redis = config.redis; describe("The 'script' method", function () { - helper.allTests(function(parser, ip, args) { - var command = "return 99"; + helper.allTests(function (parser, ip, args) { + var command = 'return 99'; var commandSha = crypto.createHash('sha1').update(command).digest('hex'); - describe("using " + parser + " and " + ip, function () { + describe('using ' + parser + ' and ' + ip, function () { var client; beforeEach(function (done) { client = redis.createClient.apply(null, args); - client.once("ready", function () { + client.once('ready', function () { client.flushdb(done); }); }); @@ -27,7 +27,7 @@ describe("The 'script' method", function () { }); it("loads script with client.script('load')", function (done) { - client.script("load", command, function(err, result) { + client.script('load', command, function (err, result) { assert.strictEqual(result, commandSha); return done(); }); @@ -38,14 +38,14 @@ describe("The 'script' method", function () { }); it('allows a script to be loaded as part of a chained transaction', function (done) { - client.multi().script("load", command).exec(function(err, result) { + client.multi().script('load', command).exec(function (err, result) { assert.strictEqual(result[0], commandSha); return done(); }); }); it("allows a script to be loaded using a transaction's array syntax", function (done) { - client.multi([['script', 'load', command]]).exec(function(err, result) { + client.multi([['script', 'load', command]]).exec(function (err, result) { assert.strictEqual(result[0], commandSha); return done(); }); diff --git a/test/commands/sdiff.spec.js b/test/commands/sdiff.spec.js index 4a9e8c651b7..e3963ecdf0c 100644 --- a/test/commands/sdiff.spec.js +++ b/test/commands/sdiff.spec.js @@ -1,20 +1,20 @@ 'use strict'; -var assert = require("assert"); -var config = require("../lib/config"); -var helper = require("../helper"); +var assert = require('assert'); +var config = require('../lib/config'); +var helper = require('../helper'); var redis = config.redis; describe("The 'sdiff' method", function () { - helper.allTests(function(parser, ip, args) { + helper.allTests(function (parser, ip, args) { - describe("using " + parser + " and " + ip, function () { + describe('using ' + parser + ' and ' + ip, function () { var client; beforeEach(function (done) { client = redis.createClient.apply(null, args); - client.once("ready", function () { + client.once('ready', function () { client.flushdb(done); }); }); diff --git a/test/commands/sdiffstore.spec.js b/test/commands/sdiffstore.spec.js index eb8fb79952a..1797ca9e435 100644 --- a/test/commands/sdiffstore.spec.js +++ b/test/commands/sdiffstore.spec.js @@ -1,20 +1,20 @@ 'use strict'; -var assert = require("assert"); -var config = require("../lib/config"); -var helper = require("../helper"); +var assert = require('assert'); +var config = require('../lib/config'); +var helper = require('../helper'); var redis = config.redis; describe("The 'sdiffstore' method", function () { - helper.allTests(function(parser, ip, args) { + helper.allTests(function (parser, ip, args) { - describe("using " + parser + " and " + ip, function () { + describe('using ' + parser + ' and ' + ip, function () { var client; beforeEach(function (done) { client = redis.createClient.apply(null, args); - client.once("ready", function () { + client.once('ready', function () { client.flushdb(done); }); }); diff --git a/test/commands/select.spec.js b/test/commands/select.spec.js index 2d0ac3a1046..4e418767fbc 100644 --- a/test/commands/select.spec.js +++ b/test/commands/select.spec.js @@ -1,27 +1,27 @@ 'use strict'; var assert = require('assert'); -var config = require("../lib/config"); +var config = require('../lib/config'); var helper = require('../helper'); var redis = config.redis; describe("The 'select' method", function () { - helper.allTests(function(parser, ip, args) { + helper.allTests(function (parser, ip, args) { - describe("using " + parser + " and " + ip, function () { - describe("when not connected", function () { + describe('using ' + parser + ' and ' + ip, function () { + describe('when not connected', function () { var client; beforeEach(function (done) { client = redis.createClient.apply(null, args); - client.once("ready", function () { + client.once('ready', function () { client.quit(); }); client.on('end', done); }); - it("returns an error if redis is not connected", function (done) { + it('returns an error if redis is not connected', function (done) { var buffering = client.select(1, function (err, res) { assert(err.message.match(/The connection has already been closed/)); done(); @@ -30,12 +30,12 @@ describe("The 'select' method", function () { }); }); - describe("when connected", function () { + describe('when connected', function () { var client; beforeEach(function (done) { client = redis.createClient.apply(null, args); - client.once("ready", function () { + client.once('ready', function () { client.flushdb(done); }); }); @@ -44,32 +44,32 @@ describe("The 'select' method", function () { client.end(true); }); - it("changes the database and calls the callback", function (done) { + it('changes the database and calls the callback', function (done) { // default value of null means database 0 will be used. - assert.strictEqual(client.selected_db, undefined, "default db should be undefined"); + assert.strictEqual(client.selected_db, undefined, 'default db should be undefined'); var buffering = client.SELECT(1, function (err, res) { helper.isNotError()(err, res); - assert.strictEqual(client.selected_db, 1, "db should be 1 after select"); + assert.strictEqual(client.selected_db, 1, 'db should be 1 after select'); done(); }); assert(typeof buffering === 'boolean'); }); - describe("and a callback is specified", function () { - describe("with a valid db index", function () { - it("selects the appropriate database", function (done) { - assert.strictEqual(client.selected_db, undefined, "default db should be undefined"); + describe('and a callback is specified', function () { + describe('with a valid db index', function () { + it('selects the appropriate database', function (done) { + assert.strictEqual(client.selected_db, undefined, 'default db should be undefined'); client.select(1, function (err) { assert.equal(err, null); - assert.equal(client.selected_db, 1, "we should have selected the new valid DB"); + assert.equal(client.selected_db, 1, 'we should have selected the new valid DB'); return done(); }); }); }); - describe("with an invalid db index", function () { - it("returns an error", function (done) { - assert.strictEqual(client.selected_db, undefined, "default db should be undefined"); + describe('with an invalid db index', function () { + it('returns an error', function (done) { + assert.strictEqual(client.selected_db, undefined, 'default db should be undefined'); client.select(9999, function (err) { assert.equal(err.code, 'ERR'); assert.equal(err.message, 'ERR invalid DB index'); @@ -79,21 +79,21 @@ describe("The 'select' method", function () { }); }); - describe("and no callback is specified", function () { - describe("with a valid db index", function () { - it("selects the appropriate database", function (done) { - assert.strictEqual(client.selected_db, undefined, "default db should be undefined"); + describe('and no callback is specified', function () { + describe('with a valid db index', function () { + it('selects the appropriate database', function (done) { + assert.strictEqual(client.selected_db, undefined, 'default db should be undefined'); client.select(1); setTimeout(function () { - assert.equal(client.selected_db, 1, "we should have selected the new valid DB"); + assert.equal(client.selected_db, 1, 'we should have selected the new valid DB'); return done(); }, 100); }); }); - describe("with an invalid db index", function () { - it("emits an error when callback not provided", function (done) { - assert.strictEqual(client.selected_db, undefined, "default db should be undefined"); + describe('with an invalid db index', function () { + it('emits an error when callback not provided', function (done) { + assert.strictEqual(client.selected_db, undefined, 'default db should be undefined'); client.on('error', function (err) { assert.strictEqual(err.command, 'SELECT'); @@ -106,9 +106,9 @@ describe("The 'select' method", function () { }); }); - describe("reconnection occurs", function () { - it("selects the appropriate database after a reconnect", function (done) { - assert.strictEqual(client.selected_db, undefined, "default db should be undefined"); + describe('reconnection occurs', function () { + it('selects the appropriate database after a reconnect', function (done) { + assert.strictEqual(client.selected_db, undefined, 'default db should be undefined'); client.select(3); client.set('foo', 'bar', function () { client.stream.destroy(); diff --git a/test/commands/set.spec.js b/test/commands/set.spec.js index 8cf6fe6cd1b..94b6cd69df1 100644 --- a/test/commands/set.spec.js +++ b/test/commands/set.spec.js @@ -1,16 +1,16 @@ 'use strict'; var assert = require('assert'); -var config = require("../lib/config"); +var config = require('../lib/config'); var helper = require('../helper'); var redis = config.redis; var uuid = require('uuid'); describe("The 'set' method", function () { - helper.allTests(function(parser, ip, args) { + helper.allTests(function (parser, ip, args) { - describe("using " + parser + " and " + ip, function () { + describe('using ' + parser + ' and ' + ip, function () { var key, value; beforeEach(function () { @@ -18,18 +18,18 @@ describe("The 'set' method", function () { value = uuid.v4(); }); - describe("when not connected", function () { + describe('when not connected', function () { var client; beforeEach(function (done) { client = redis.createClient.apply(null, args); - client.once("ready", function () { + client.once('ready', function () { client.quit(); }); client.on('end', done); }); - it("reports an error", function (done) { + it('reports an error', function (done) { client.set(key, value, function (err, res) { assert(err.message.match(/The connection has already been closed/)); done(); @@ -37,12 +37,12 @@ describe("The 'set' method", function () { }); }); - describe("when connected", function () { + describe('when connected', function () { var client; beforeEach(function (done) { client = redis.createClient.apply(null, args); - client.once("ready", function () { + client.once('ready', function () { done(); }); }); @@ -51,9 +51,9 @@ describe("The 'set' method", function () { client.end(true); }); - describe("and a callback is specified", function () { - describe("with valid parameters", function () { - it("sets the value correctly", function (done) { + describe('and a callback is specified', function () { + describe('with valid parameters', function () { + it('sets the value correctly', function (done) { client.SET(key, value, function (err, res) { helper.isNotError()(err, res); client.get(key, function (err, res) { @@ -64,7 +64,7 @@ describe("The 'set' method", function () { }); }); - describe("reports an error with invalid parameters", function () { + describe('reports an error with invalid parameters', function () { it("undefined 'key' and missing 'value' parameter", function (done) { client.set(undefined, function (err, res) { helper.isError()(err, null); @@ -73,7 +73,7 @@ describe("The 'set' method", function () { }); }); - it("empty array as second parameter", function (done) { + it('empty array as second parameter', function (done) { client.set('foo', [], function (err, res) { assert.strictEqual(err.message, "ERR wrong number of arguments for 'set' command"); done(); @@ -82,26 +82,26 @@ describe("The 'set' method", function () { }); }); - describe("and no callback is specified", function () { - describe("with valid parameters", function () { - it("sets the value correctly", function (done) { + describe('and no callback is specified', function () { + describe('with valid parameters', function () { + it('sets the value correctly', function (done) { client.set(key, value); client.get(key, helper.isString(value, done)); }); - it("sets the value correctly even if the callback is explicitly set to undefined", function (done) { + it('sets the value correctly even if the callback is explicitly set to undefined', function (done) { client.set(key, value, undefined); client.get(key, helper.isString(value, done)); }); - it("sets the value correctly with the array syntax", function (done) { + it('sets the value correctly with the array syntax', function (done) { client.set([key, value]); client.get(key, helper.isString(value, done)); }); }); describe("with undefined 'key' and missing 'value' parameter", function () { - it("emits an error without callback", function (done) { + it('emits an error without callback', function (done) { client.on('error', function (err) { assert.equal(err.message, "ERR wrong number of arguments for 'set' command"); assert.equal(err.command, 'SET'); @@ -117,7 +117,7 @@ describe("The 'set' method", function () { client.get('foo', helper.isString('null', done)); }); - it("emit an error with only the key set", function (done) { + it('emit an error with only the key set', function (done) { client.on('error', function (err) { assert.equal(err.message, "ERR wrong number of arguments for 'set' command"); done(); @@ -126,8 +126,8 @@ describe("The 'set' method", function () { client.set('foo'); }); - it("emit an error without any parameters", function (done) { - client.once("error", function (err) { + it('emit an error without any parameters', function (done) { + client.once('error', function (err) { assert.equal(err.message, "ERR wrong number of arguments for 'set' command"); assert.equal(err.command, 'SET'); done(); diff --git a/test/commands/setex.spec.js b/test/commands/setex.spec.js index 72019ae926b..9a3bb53f0b1 100644 --- a/test/commands/setex.spec.js +++ b/test/commands/setex.spec.js @@ -1,27 +1,27 @@ 'use strict'; -var assert = require("assert"); -var config = require("../lib/config"); -var helper = require("../helper"); +var assert = require('assert'); +var config = require('../lib/config'); +var helper = require('../helper'); var redis = config.redis; describe("The 'setex' method", function () { - helper.allTests(function(parser, ip, args) { + helper.allTests(function (parser, ip, args) { - describe("using " + parser + " and " + ip, function () { + describe('using ' + parser + ' and ' + ip, function () { var client; beforeEach(function (done) { client = redis.createClient.apply(null, args); - client.once("ready", function () { + client.once('ready', function () { client.flushdb(done); }); }); it('sets a key with an expiry', function (done) { - client.setex(["setex key", "100", "setex val"], helper.isString("OK")); - var buffering = client.exists(["setex key"], helper.isNumber(1)); + client.setex(['setex key', '100', 'setex val'], helper.isString('OK')); + var buffering = client.exists(['setex key'], helper.isNumber(1)); assert(typeof buffering === 'boolean'); client.ttl(['setex key'], function (err, ttl) { assert(ttl > 0); diff --git a/test/commands/setnx.spec.js b/test/commands/setnx.spec.js index 5468463b0af..2bce9f0a0ba 100644 --- a/test/commands/setnx.spec.js +++ b/test/commands/setnx.spec.js @@ -1,19 +1,19 @@ 'use strict'; -var config = require("../lib/config"); -var helper = require("../helper"); +var config = require('../lib/config'); +var helper = require('../helper'); var redis = config.redis; describe("The 'setnx' method", function () { - helper.allTests(function(parser, ip, args) { + helper.allTests(function (parser, ip, args) { - describe("using " + parser + " and " + ip, function () { + describe('using ' + parser + ' and ' + ip, function () { var client; beforeEach(function (done) { client = redis.createClient.apply(null, args); - client.once("ready", function () { + client.once('ready', function () { client.flushdb(done); }); }); diff --git a/test/commands/sinter.spec.js b/test/commands/sinter.spec.js index bd7c934fdc0..b614a41ea4c 100644 --- a/test/commands/sinter.spec.js +++ b/test/commands/sinter.spec.js @@ -1,20 +1,20 @@ 'use strict'; -var assert = require("assert"); -var config = require("../lib/config"); -var helper = require("../helper"); +var assert = require('assert'); +var config = require('../lib/config'); +var helper = require('../helper'); var redis = config.redis; describe("The 'sinter' method", function () { - helper.allTests(function(parser, ip, args) { + helper.allTests(function (parser, ip, args) { - describe("using " + parser + " and " + ip, function () { + describe('using ' + parser + ' and ' + ip, function () { var client; beforeEach(function (done) { client = redis.createClient.apply(null, args); - client.once("ready", function () { + client.once('ready', function () { client.flushdb(done); }); }); diff --git a/test/commands/sinterstore.spec.js b/test/commands/sinterstore.spec.js index 434bab9a600..de7de87e10f 100644 --- a/test/commands/sinterstore.spec.js +++ b/test/commands/sinterstore.spec.js @@ -1,20 +1,20 @@ 'use strict'; -var assert = require("assert"); -var config = require("../lib/config"); -var helper = require("../helper"); +var assert = require('assert'); +var config = require('../lib/config'); +var helper = require('../helper'); var redis = config.redis; describe("The 'sinterstore' method", function () { - helper.allTests(function(parser, ip, args) { + helper.allTests(function (parser, ip, args) { - describe("using " + parser + " and " + ip, function () { + describe('using ' + parser + ' and ' + ip, function () { var client; beforeEach(function (done) { client = redis.createClient.apply(null, args); - client.once("ready", function () { + client.once('ready', function () { client.flushdb(done); }); }); diff --git a/test/commands/sismember.spec.js b/test/commands/sismember.spec.js index 0ba5af64023..69482b3b564 100644 --- a/test/commands/sismember.spec.js +++ b/test/commands/sismember.spec.js @@ -1,19 +1,19 @@ 'use strict'; -var config = require("../lib/config"); -var helper = require("../helper"); +var config = require('../lib/config'); +var helper = require('../helper'); var redis = config.redis; describe("The 'sismember' method", function () { - helper.allTests(function(parser, ip, args) { + helper.allTests(function (parser, ip, args) { - describe("using " + parser + " and " + ip, function () { + describe('using ' + parser + ' and ' + ip, function () { var client; beforeEach(function (done) { client = redis.createClient.apply(null, args); - client.once("ready", function () { + client.once('ready', function () { client.flushdb(done); }); }); diff --git a/test/commands/slowlog.spec.js b/test/commands/slowlog.spec.js index e381ca28cf0..7f5417dbd8f 100644 --- a/test/commands/slowlog.spec.js +++ b/test/commands/slowlog.spec.js @@ -1,34 +1,34 @@ 'use strict'; -var assert = require("assert"); -var config = require("../lib/config"); -var helper = require("../helper"); +var assert = require('assert'); +var config = require('../lib/config'); +var helper = require('../helper'); var redis = config.redis; describe("The 'slowlog' method", function () { - helper.allTests(function(parser, ip, args) { + helper.allTests(function (parser, ip, args) { - describe("using " + parser + " and " + ip, function () { + describe('using ' + parser + ' and ' + ip, function () { var client; beforeEach(function (done) { client = redis.createClient.apply(null, args); - client.once("ready", function () { + client.once('ready', function () { client.flushdb(done); }); }); it('logs operations in slowlog', function (done) { - client.config("set", "slowlog-log-slower-than", 0, helper.isString("OK")); - client.slowlog("reset", helper.isString("OK")); - client.set("foo", "bar", helper.isString("OK")); - client.get("foo", helper.isString("bar")); - client.SLOWLOG("get", function (err, res) { + client.config('set', 'slowlog-log-slower-than', 0, helper.isString('OK')); + client.slowlog('reset', helper.isString('OK')); + client.set('foo', 'bar', helper.isString('OK')); + client.get('foo', helper.isString('bar')); + client.SLOWLOG('get', function (err, res) { assert.equal(res.length, 3); assert.equal(res[0][3].length, 2); - assert.deepEqual(res[1][3], ["set", "foo", "bar"]); - assert.deepEqual(res[2][3], ["slowlog", "reset"]); + assert.deepEqual(res[1][3], ['set', 'foo', 'bar']); + assert.deepEqual(res[2][3], ['slowlog', 'reset']); return done(err); }); }); diff --git a/test/commands/smembers.spec.js b/test/commands/smembers.spec.js index aab09895798..8fbdcbba5ed 100644 --- a/test/commands/smembers.spec.js +++ b/test/commands/smembers.spec.js @@ -1,20 +1,20 @@ 'use strict'; -var assert = require("assert"); -var config = require("../lib/config"); -var helper = require("../helper"); +var assert = require('assert'); +var config = require('../lib/config'); +var helper = require('../helper'); var redis = config.redis; describe("The 'smembers' method", function () { - helper.allTests(function(parser, ip, args) { + helper.allTests(function (parser, ip, args) { - describe("using " + parser + " and " + ip, function () { + describe('using ' + parser + ' and ' + ip, function () { var client; beforeEach(function (done) { client = redis.createClient.apply(null, args); - client.once("ready", function () { + client.once('ready', function () { client.flushdb(done); }); }); diff --git a/test/commands/smove.spec.js b/test/commands/smove.spec.js index ca2c6ae2561..eb8eae10545 100644 --- a/test/commands/smove.spec.js +++ b/test/commands/smove.spec.js @@ -1,19 +1,19 @@ 'use strict'; -var config = require("../lib/config"); -var helper = require("../helper"); +var config = require('../lib/config'); +var helper = require('../helper'); var redis = config.redis; describe("The 'smove' method", function () { - helper.allTests(function(parser, ip, args) { + helper.allTests(function (parser, ip, args) { - describe("using " + parser + " and " + ip, function () { + describe('using ' + parser + ' and ' + ip, function () { var client; beforeEach(function (done) { client = redis.createClient.apply(null, args); - client.once("ready", function () { + client.once('ready', function () { client.flushdb(done); }); }); @@ -25,7 +25,7 @@ describe("The 'smove' method", function () { client.sismember('bar', 'x', helper.isNumber(1, done)); }); - it("does not move a value if it does not exist in the first set", function (done) { + it('does not move a value if it does not exist in the first set', function (done) { client.sadd('foo', 'x', helper.isNumber(1)); client.SMOVE('foo', 'bar', 'y', helper.isNumber(0)); client.sismember('foo', 'y', helper.isNumber(0)); diff --git a/test/commands/sort.spec.js b/test/commands/sort.spec.js index 1a72664d9ea..63b933eaef6 100644 --- a/test/commands/sort.spec.js +++ b/test/commands/sort.spec.js @@ -1,11 +1,11 @@ 'use strict'; -var assert = require("assert"); -var config = require("../lib/config"); -var helper = require("../helper"); +var assert = require('assert'); +var config = require('../lib/config'); +var helper = require('../helper'); var redis = config.redis; -function setupData(client, done) { +function setupData (client, done) { client.rpush('y', 'd'); client.rpush('y', 'b'); client.rpush('y', 'a'); @@ -34,15 +34,15 @@ function setupData(client, done) { describe("The 'sort' method", function () { - helper.allTests(function(parser, ip, args) { + helper.allTests(function (parser, ip, args) { - describe("using " + parser + " and " + ip, function () { + describe('using ' + parser + ' and ' + ip, function () { var client; beforeEach(function (done) { client = redis.createClient.apply(null, args); - client.once("error", done); - client.once("connect", function () { + client.once('error', done); + client.once('connect', function () { client.flushdb(); setupData(client, done); }); diff --git a/test/commands/spop.spec.js b/test/commands/spop.spec.js index 224869e5b66..6e0697c02a5 100644 --- a/test/commands/spop.spec.js +++ b/test/commands/spop.spec.js @@ -1,20 +1,20 @@ 'use strict'; -var assert = require("assert"); -var config = require("../lib/config"); -var helper = require("../helper"); +var assert = require('assert'); +var config = require('../lib/config'); +var helper = require('../helper'); var redis = config.redis; describe("The 'spop' method", function () { - helper.allTests(function(parser, ip, args) { + helper.allTests(function (parser, ip, args) { - describe("using " + parser + " and " + ip, function () { + describe('using ' + parser + ' and ' + ip, function () { var client; beforeEach(function (done) { client = redis.createClient.apply(null, args); - client.once("ready", function () { + client.once('ready', function () { client.flushdb(done); }); }); diff --git a/test/commands/srem.spec.js b/test/commands/srem.spec.js index 7db8b6a8471..97c40d9ce2d 100644 --- a/test/commands/srem.spec.js +++ b/test/commands/srem.spec.js @@ -1,20 +1,20 @@ 'use strict'; -var assert = require("assert"); -var config = require("../lib/config"); -var helper = require("../helper"); +var assert = require('assert'); +var config = require('../lib/config'); +var helper = require('../helper'); var redis = config.redis; describe("The 'srem' method", function () { - helper.allTests(function(parser, ip, args) { + helper.allTests(function (parser, ip, args) { - describe("using " + parser + " and " + ip, function () { + describe('using ' + parser + ' and ' + ip, function () { var client; beforeEach(function (done) { client = redis.createClient.apply(null, args); - client.once("ready", function () { + client.once('ready', function () { client.flushdb(done); }); }); @@ -30,33 +30,33 @@ describe("The 'srem' method", function () { }); it('allows multiple values to be removed', function (done) { - client.sadd("set0", ["member0", "member1", "member2"], helper.isNumber(3)); - client.SREM("set0", ["member1", "member2"], helper.isNumber(2)); - client.smembers("set0", function (err, res) { + client.sadd('set0', ['member0', 'member1', 'member2'], helper.isNumber(3)); + client.SREM('set0', ['member1', 'member2'], helper.isNumber(2)); + client.smembers('set0', function (err, res) { assert.strictEqual(res.length, 1); - assert.ok(~res.indexOf("member0")); + assert.ok(~res.indexOf('member0')); return done(err); }); }); it('allows multiple values to be removed with send_command', function (done) { client.send_command('sadd', ['set0', 'member0', 'member1', 'member2'], helper.isNumber(3)); - client.send_command('srem', ["set0", "member1", "member2"], helper.isNumber(2)); - client.smembers("set0", function (err, res) { + client.send_command('srem', ['set0', 'member1', 'member2'], helper.isNumber(2)); + client.smembers('set0', function (err, res) { assert.strictEqual(res.length, 1); - assert.ok(~res.indexOf("member0")); + assert.ok(~res.indexOf('member0')); return done(err); }); }); it('handles a value missing from the set of values being removed', function (done) { - client.sadd(["set0", "member0", "member1", "member2"], helper.isNumber(3)); - client.SREM(["set0", "member3", "member4"], helper.isNumber(0)); - client.smembers("set0", function (err, res) { + client.sadd(['set0', 'member0', 'member1', 'member2'], helper.isNumber(3)); + client.SREM(['set0', 'member3', 'member4'], helper.isNumber(0)); + client.smembers('set0', function (err, res) { assert.strictEqual(res.length, 3); - assert.ok(~res.indexOf("member0")); - assert.ok(~res.indexOf("member1")); - assert.ok(~res.indexOf("member2")); + assert.ok(~res.indexOf('member0')); + assert.ok(~res.indexOf('member1')); + assert.ok(~res.indexOf('member2')); return done(err); }); }); diff --git a/test/commands/sunion.spec.js b/test/commands/sunion.spec.js index 835c258f215..44f5f247206 100644 --- a/test/commands/sunion.spec.js +++ b/test/commands/sunion.spec.js @@ -1,20 +1,20 @@ 'use strict'; -var assert = require("assert"); -var config = require("../lib/config"); -var helper = require("../helper"); +var assert = require('assert'); +var config = require('../lib/config'); +var helper = require('../helper'); var redis = config.redis; describe("The 'sunion' method", function () { - helper.allTests(function(parser, ip, args) { + helper.allTests(function (parser, ip, args) { - describe("using " + parser + " and " + ip, function () { + describe('using ' + parser + ' and ' + ip, function () { var client; beforeEach(function (done) { client = redis.createClient.apply(null, args); - client.once("ready", function () { + client.once('ready', function () { client.flushdb(done); }); }); diff --git a/test/commands/sunionstore.spec.js b/test/commands/sunionstore.spec.js index cbef9084fe7..c9869bdf881 100644 --- a/test/commands/sunionstore.spec.js +++ b/test/commands/sunionstore.spec.js @@ -1,20 +1,20 @@ 'use strict'; -var assert = require("assert"); -var config = require("../lib/config"); -var helper = require("../helper"); +var assert = require('assert'); +var config = require('../lib/config'); +var helper = require('../helper'); var redis = config.redis; describe("The 'sunionstore' method", function () { - helper.allTests(function(parser, ip, args) { + helper.allTests(function (parser, ip, args) { - describe("using " + parser + " and " + ip, function () { + describe('using ' + parser + ' and ' + ip, function () { var client; beforeEach(function (done) { client = redis.createClient.apply(null, args); - client.once("ready", function () { + client.once('ready', function () { client.flushdb(done); }); }); diff --git a/test/commands/ttl.spec.js b/test/commands/ttl.spec.js index 3448fcf1357..5309b70a1a9 100644 --- a/test/commands/ttl.spec.js +++ b/test/commands/ttl.spec.js @@ -1,29 +1,29 @@ 'use strict'; -var assert = require("assert"); -var config = require("../lib/config"); -var helper = require("../helper"); +var assert = require('assert'); +var config = require('../lib/config'); +var helper = require('../helper'); var redis = config.redis; describe("The 'ttl' method", function () { - helper.allTests(function(parser, ip, args) { + helper.allTests(function (parser, ip, args) { - describe("using " + parser + " and " + ip, function () { + describe('using ' + parser + ' and ' + ip, function () { var client; beforeEach(function (done) { client = redis.createClient.apply(null, args); - client.once("ready", function () { + client.once('ready', function () { client.flushdb(done); }); }); it('returns the current ttl on a key', function (done) { - client.set(["ttl key", "ttl val"], helper.isString("OK")); - client.expire(["ttl key", "100"], helper.isNumber(1)); + client.set(['ttl key', 'ttl val'], helper.isString('OK')); + client.expire(['ttl key', '100'], helper.isNumber(1)); setTimeout(function () { - client.TTL(["ttl key"], function (err, ttl) { + client.TTL(['ttl key'], function (err, ttl) { assert.ok(ttl > 50 && ttl <= 100); return done(err); }); diff --git a/test/commands/type.spec.js b/test/commands/type.spec.js index b6336153efd..4a0c4d2897c 100644 --- a/test/commands/type.spec.js +++ b/test/commands/type.spec.js @@ -1,50 +1,50 @@ 'use strict'; -var config = require("../lib/config"); -var helper = require("../helper"); +var config = require('../lib/config'); +var helper = require('../helper'); var redis = config.redis; describe("The 'type' method", function () { - helper.allTests(function(parser, ip, args) { + helper.allTests(function (parser, ip, args) { - describe("using " + parser + " and " + ip, function () { + describe('using ' + parser + ' and ' + ip, function () { var client; beforeEach(function (done) { client = redis.createClient.apply(null, args); - client.once("ready", function () { + client.once('ready', function () { client.flushdb(done); }); }); it('reports string type', function (done) { - client.set(["string key", "should be a string"], helper.isString("OK")); - client.TYPE(["string key"], helper.isString("string", done)); + client.set(['string key', 'should be a string'], helper.isString('OK')); + client.TYPE(['string key'], helper.isString('string', done)); }); it('reports list type', function (done) { - client.rpush(["list key", "should be a list"], helper.isNumber(1)); - client.type(["list key"], helper.isString("list", done)); + client.rpush(['list key', 'should be a list'], helper.isNumber(1)); + client.type(['list key'], helper.isString('list', done)); }); it('reports set type', function (done) { - client.sadd(["set key", "should be a set"], helper.isNumber(1)); - client.TYPE(["set key"], helper.isString("set", done)); + client.sadd(['set key', 'should be a set'], helper.isNumber(1)); + client.TYPE(['set key'], helper.isString('set', done)); }); it('reports zset type', function (done) { - client.zadd("zset key", ["10.0", "should be a zset"], helper.isNumber(1)); - client.TYPE(["zset key"], helper.isString("zset", done)); + client.zadd('zset key', ['10.0', 'should be a zset'], helper.isNumber(1)); + client.TYPE(['zset key'], helper.isString('zset', done)); }); it('reports hash type', function (done) { - client.hset("hash key", "hashtest", "should be a hash", helper.isNumber(1)); - client.TYPE(["hash key"], helper.isString("hash", done)); + client.hset('hash key', 'hashtest', 'should be a hash', helper.isNumber(1)); + client.TYPE(['hash key'], helper.isString('hash', done)); }); it('reports none for null key', function (done) { - client.TYPE("not here yet", helper.isString("none", done)); + client.TYPE('not here yet', helper.isString('none', done)); }); afterEach(function () { diff --git a/test/commands/watch.spec.js b/test/commands/watch.spec.js index 7214c9c8f34..cf908646750 100644 --- a/test/commands/watch.spec.js +++ b/test/commands/watch.spec.js @@ -1,22 +1,22 @@ 'use strict'; -var assert = require("assert"); -var config = require("../lib/config"); -var helper = require("../helper"); +var assert = require('assert'); +var config = require('../lib/config'); +var helper = require('../helper'); var redis = config.redis; describe("The 'watch' method", function () { - helper.allTests(function(parser, ip, args) { + helper.allTests(function (parser, ip, args) { var watched = 'foobar'; - describe("using " + parser + " and " + ip, function () { + describe('using ' + parser + ' and ' + ip, function () { var client; beforeEach(function (done) { client = redis.createClient.apply(null, args); - client.once("ready", function () { + client.once('ready', function () { client.flushdb(done); }); }); @@ -34,17 +34,17 @@ describe("The 'watch' method", function () { }); it('successfully modifies other keys independently of transaction', function (done) { - client.set("unwatched", 200); + client.set('unwatched', 200); - client.set(watched, 0); - client.watch(watched); - client.incr(watched); + client.set(watched, 0); + client.watch(watched); + client.incr(watched); - client.multi().incr(watched).exec(function (err, replies) { - assert.strictEqual(replies, null, "Aborted transaction multi-bulk reply should be null."); + client.multi().incr(watched).exec(function (err, replies) { + assert.strictEqual(replies, null, 'Aborted transaction multi-bulk reply should be null.'); - client.get("unwatched", function (err, reply) { - assert.equal(reply, 200, "Expected 200, got " + reply); + client.get('unwatched', function (err, reply) { + assert.equal(reply, 200, 'Expected 200, got ' + reply); return done(err); }); }); diff --git a/test/commands/zadd.spec.js b/test/commands/zadd.spec.js index 669d7a06413..e0153ba11bb 100644 --- a/test/commands/zadd.spec.js +++ b/test/commands/zadd.spec.js @@ -1,36 +1,36 @@ 'use strict'; -var config = require("../lib/config"); -var helper = require("../helper"); +var config = require('../lib/config'); +var helper = require('../helper'); var assert = require('assert'); var redis = config.redis; describe("The 'zadd' method", function () { - helper.allTests(function(parser, ip, args) { + helper.allTests(function (parser, ip, args) { - describe("using " + parser + " and " + ip, function () { + describe('using ' + parser + ' and ' + ip, function () { var client; beforeEach(function (done) { client = redis.createClient.apply(null, args); - client.once("ready", function () { + client.once('ready', function () { client.flushdb(done); }); }); it('reports an error', function (done) { if (helper.redisProcess().spawnFailed()) this.skip(); - client.zadd('infinity', [+'5t', "should not be possible"], helper.isError(done)); + client.zadd('infinity', [+'5t', 'should not be possible'], helper.isError(done)); }); it('return inf / -inf', function (done) { if (helper.redisProcess().spawnFailed()) this.skip(); helper.serverVersionAtLeast.call(this, client, [3, 0, 2]); - client.zadd('infinity', [+Infinity, "should be inf"], helper.isNumber(1)); + client.zadd('infinity', [+Infinity, 'should be inf'], helper.isNumber(1)); client.zadd('infinity', ['inf', 'should be also be inf'], helper.isNumber(1)); - client.zadd('infinity', -Infinity, "should be negative inf", helper.isNumber(1)); - client.zadd('infinity', [99999999999999999999999, "should not be inf"], helper.isNumber(1)); + client.zadd('infinity', -Infinity, 'should be negative inf', helper.isNumber(1)); + client.zadd('infinity', [99999999999999999999999, 'should not be inf'], helper.isNumber(1)); client.zrange('infinity', 0, -1, 'WITHSCORES', function (err, res) { assert.equal(res[5], 'inf'); assert.equal(res[1], '-inf'); diff --git a/test/commands/zscan.spec.js b/test/commands/zscan.spec.js index b7a5936f300..6d4a1a60c8a 100644 --- a/test/commands/zscan.spec.js +++ b/test/commands/zscan.spec.js @@ -7,14 +7,14 @@ var redis = config.redis; describe("The 'zscan' method", function () { - helper.allTests(function(parser, ip, args) { + helper.allTests(function (parser, ip, args) { - describe("using " + parser + " and " + ip, function () { + describe('using ' + parser + ' and ' + ip, function () { var client; beforeEach(function (done) { client = redis.createClient.apply(null, args); - client.once("ready", function () { + client.once('ready', function () { client.flushdb(done); }); }); @@ -24,14 +24,14 @@ describe("The 'zscan' method", function () { helper.serverVersionAtLeast.call(this, client, [2, 8, 0]); var hash = {}; var set = []; - var zset = ["zset:1"]; + var zset = ['zset:1']; for (var i = 0; i < 500; i++) { - hash["key_" + i] = "value_" + i; - set.push("member_" + i); - zset.push(i, "z_member_" + i); + hash['key_' + i] = 'value_' + i; + set.push('member_' + i); + zset.push(i, 'z_member_' + i); } - client.hmset("hash:1", hash); - client.sadd("set:1", set); + client.hmset('hash:1', hash); + client.sadd('set:1', set); client.zadd(zset); client.zscan('zset:1', 0, 'MATCH', '*', 'COUNT', 500, function (err, res) { assert(!err); diff --git a/test/commands/zscore.spec.js b/test/commands/zscore.spec.js index 8a285fba9d4..9d24d7a7cf0 100644 --- a/test/commands/zscore.spec.js +++ b/test/commands/zscore.spec.js @@ -1,20 +1,20 @@ 'use strict'; -var config = require("../lib/config"); -var helper = require("../helper"); +var config = require('../lib/config'); +var helper = require('../helper'); var assert = require('assert'); var redis = config.redis; describe("The 'zscore' method", function () { - helper.allTests(function(parser, ip, args) { + helper.allTests(function (parser, ip, args) { - describe("using " + parser + " and " + ip, function () { + describe('using ' + parser + ' and ' + ip, function () { var client; beforeEach(function (done) { client = redis.createClient.apply(null, args); - client.once("ready", function () { + client.once('ready', function () { client.flushdb(done); }); }); diff --git a/test/conect.slave.spec.js b/test/conect.slave.spec.js index 28f56d94594..5264a125e65 100644 --- a/test/conect.slave.spec.js +++ b/test/conect.slave.spec.js @@ -1,9 +1,9 @@ 'use strict'; -var assert = require("assert"); -var config = require("./lib/config"); +var assert = require('assert'); +var config = require('./lib/config'); var helper = require('./helper'); -var RedisProcess = require("./lib/redis-process"); +var RedisProcess = require('./lib/redis-process'); var rp; var path = require('path'); var redis = config.redis; @@ -38,7 +38,7 @@ describe('master slave sync', function () { multi.exec(done); }); - it("sync process and no master should delay ready being emitted for slaves", function (done) { + it('sync process and no master should delay ready being emitted for slaves', function (done) { if (helper.redisProcess().spawnFailed()) this.skip(); var port = 6381; diff --git a/test/connection.spec.js b/test/connection.spec.js index 1f60111f238..31e0cb0fed7 100644 --- a/test/connection.spec.js +++ b/test/connection.spec.js @@ -1,14 +1,14 @@ 'use strict'; -var assert = require("assert"); -var config = require("./lib/config"); +var assert = require('assert'); +var config = require('./lib/config'); var helper = require('./helper'); var redis = config.redis; var intercept = require('intercept-stdout'); var net = require('net'); var client; -describe("connection tests", function () { +describe('connection tests', function () { beforeEach(function () { client = null; @@ -34,12 +34,12 @@ describe("connection tests", function () { assert.strictEqual(client.stream.listeners('error').length, 1); }); - helper.allTests(function(parser, ip, args) { + helper.allTests(function (parser, ip, args) { - describe("using " + parser + " and " + ip, function () { + describe('using ' + parser + ' and ' + ip, function () { - describe("on lost connection", function () { - it("emit an error after max retry attempts and do not try to reconnect afterwards", function (done) { + describe('on lost connection', function () { + it('emit an error after max retry attempts and do not try to reconnect afterwards', function (done) { var max_attempts = 4; var options = { parser: parser, @@ -49,15 +49,15 @@ describe("connection tests", function () { assert.strictEqual(Object.keys(options).length, 2); var calls = 0; - client.once('ready', function() { + client.once('ready', function () { helper.killConnection(client); }); - client.on("reconnecting", function (params) { + client.on('reconnecting', function (params) { calls++; }); - client.on('error', function(err) { + client.on('error', function (err) { if (/Redis connection in broken state: maximum connection attempts.*?exceeded./.test(err.message)) { setTimeout(function () { assert.strictEqual(calls, max_attempts - 1); @@ -67,7 +67,7 @@ describe("connection tests", function () { }); }); - it("emit an error after max retry timeout and do not try to reconnect afterwards", function (done) { + it('emit an error after max retry timeout and do not try to reconnect afterwards', function (done) { // TODO: Investigate why this test fails with windows. Reconnect is only triggered once if (process.platform === 'win32') this.skip(); @@ -78,15 +78,15 @@ describe("connection tests", function () { }); var time = 0; - client.once('ready', function() { + client.once('ready', function () { helper.killConnection(client); }); - client.on("reconnecting", function (params) { + client.on('reconnecting', function (params) { time += params.delay; }); - client.on('error', function(err) { + client.on('error', function (err) { if (/Redis connection in broken state: connection timeout.*?exceeded./.test(err.message)) { setTimeout(function () { assert.strictEqual(client.retry_totaltime, connect_timeout); @@ -97,25 +97,25 @@ describe("connection tests", function () { }); }); - it("end connection while retry is still ongoing", function (done) { + it('end connection while retry is still ongoing', function (done) { var connect_timeout = 1000; // in ms client = redis.createClient({ parser: parser, connect_timeout: connect_timeout }); - client.once('ready', function() { + client.once('ready', function () { helper.killConnection(client); }); - client.on("reconnecting", function (params) { + client.on('reconnecting', function (params) { client.end(true); assert.strictEqual(params.times_connected, 1); setTimeout(done, 100); }); }); - it("can not connect with wrong host / port in the options object", function (done) { + it('can not connect with wrong host / port in the options object', function (done) { var options = { host: 'somewhere', port: 6379, @@ -134,21 +134,21 @@ describe("connection tests", function () { }); - it("emits error once if reconnecting after command has been executed but not yet returned without callback", function (done) { + it('emits error once if reconnecting after command has been executed but not yet returned without callback', function (done) { client = redis.createClient.apply(null, args); - client.on('error', function(err) { + client.on('error', function (err) { assert.strictEqual(err.code, 'UNCERTAIN_STATE'); done(); }); - client.on('ready', function() { - client.set("foo", 'bar'); + client.on('ready', function () { + client.set('foo', 'bar'); // Abort connection before the value returned client.stream.destroy(); }); }); - it("retry_strategy used to reconnect with individual error", function (done) { + it('retry_strategy used to reconnect with individual error', function (done) { var text = ''; var unhookIntercept = intercept(function (data) { text += data; @@ -172,7 +172,7 @@ describe("connection tests", function () { port: 9999 }); - client.on('error', function(err) { + client.on('error', function (err) { unhookIntercept(); assert.strictEqual( text, @@ -185,7 +185,7 @@ describe("connection tests", function () { }); }); - it("retry_strategy used to reconnect", function (done) { + it('retry_strategy used to reconnect', function (done) { var end = helper.callFuncAfter(done, 2); client = redis.createClient({ retry_strategy: function (options) { @@ -201,16 +201,16 @@ describe("connection tests", function () { port: 9999 }); - client.on('error', function(err) { + client.on('error', function (err) { assert.strictEqual(err.code, 'ECONNREFUSED'); end(); }); }); }); - describe("when not connected", function () { + describe('when not connected', function () { - it("emit an error after the socket timeout exceeded the connect_timeout time", function (done) { + it('emit an error after the socket timeout exceeded the connect_timeout time', function (done) { var connect_timeout = 500; // in ms var time = Date.now(); client = redis.createClient({ @@ -219,17 +219,17 @@ describe("connection tests", function () { host: '10.255.255.1', connect_timeout: connect_timeout }); - process.nextTick(function() { + process.nextTick(function () { assert.strictEqual(client.stream.listeners('timeout').length, 1); }); assert.strictEqual(client.address, '10.255.255.1:6379'); assert.strictEqual(client.connection_options.family, 4); - client.on("reconnecting", function (params) { + client.on('reconnecting', function (params) { throw new Error('No reconnect, since no connection was ever established'); }); - client.on('error', function(err) { + client.on('error', function (err) { if (err.code === 'ENETUNREACH') { // The test is run without a internet connection. Pretent it works return done(); } @@ -244,24 +244,24 @@ describe("connection tests", function () { }); }); - it("use the system socket timeout if the connect_timeout has not been provided", function () { + it('use the system socket timeout if the connect_timeout has not been provided', function () { client = redis.createClient({ parser: parser, host: '2001:db8::ff00:42:8329' // auto detect ip v6 }); assert.strictEqual(client.address, '2001:db8::ff00:42:8329:6379'); assert.strictEqual(client.connection_options.family, 6); - process.nextTick(function() { + process.nextTick(function () { assert.strictEqual(client.stream.listeners('timeout').length, 0); }); }); - it("clears the socket timeout after a connection has been established", function (done) { + it('clears the socket timeout after a connection has been established', function (done) { client = redis.createClient({ parser: parser, connect_timeout: 1000 }); - process.nextTick(function() { + process.nextTick(function () { assert.strictEqual(client.stream._idleTimeout, 1000); }); client.on('connect', function () { @@ -271,7 +271,7 @@ describe("connection tests", function () { }); }); - it("connect with host and port provided in the options object", function (done) { + it('connect with host and port provided in the options object', function (done) { client = redis.createClient({ host: 'localhost', port: '6379', @@ -282,7 +282,7 @@ describe("connection tests", function () { client.once('ready', done); }); - it("connect with path provided in the options object", function (done) { + it('connect with path provided in the options object', function (done) { if (process.platform === 'win32') { this.skip(); } @@ -298,105 +298,105 @@ describe("connection tests", function () { client.set('foo', 'bar', end); }); - it("connects correctly with args", function (done) { + it('connects correctly with args', function (done) { client = redis.createClient.apply(null, args); - client.on("error", done); + client.on('error', done); - client.once("ready", function () { - client.removeListener("error", done); - client.get("recon 1", done); + client.once('ready', function () { + client.removeListener('error', done); + client.get('recon 1', done); }); }); - it("connects correctly with default values", function (done) { + it('connects correctly with default values', function (done) { client = redis.createClient(); - client.on("error", done); + client.on('error', done); - client.once("ready", function () { - client.removeListener("error", done); - client.get("recon 1", done); + client.once('ready', function () { + client.removeListener('error', done); + client.get('recon 1', done); }); }); - it("connects with a port only", function (done) { + it('connects with a port only', function (done) { client = redis.createClient(6379); assert.strictEqual(client.connection_options.family, 4); - client.on("error", done); + client.on('error', done); - client.once("ready", function () { - client.removeListener("error", done); - client.get("recon 1", done); + client.once('ready', function () { + client.removeListener('error', done); + client.get('recon 1', done); }); }); - it("connects correctly to localhost", function (done) { + it('connects correctly to localhost', function (done) { client = redis.createClient(null, null); - client.on("error", done); + client.on('error', done); - client.once("ready", function () { - client.removeListener("error", done); - client.get("recon 1", done); + client.once('ready', function () { + client.removeListener('error', done); + client.get('recon 1', done); }); }); - it("connects correctly to the provided host with the port set to null", function (done) { + it('connects correctly to the provided host with the port set to null', function (done) { client = redis.createClient(null, 'localhost'); - client.on("error", done); + client.on('error', done); assert.strictEqual(client.address, 'localhost:6379'); - client.once("ready", function () { + client.once('ready', function () { client.set('foo', 'bar'); - client.get('foo', function(err, res) { + client.get('foo', function (err, res) { assert.strictEqual(res, 'bar'); done(err); }); }); }); - it("connects correctly to localhost and no ready check", function (done) { + it('connects correctly to localhost and no ready check', function (done) { client = redis.createClient(undefined, undefined, { no_ready_check: true }); - client.on("error", done); + client.on('error', done); - client.once("ready", function () { + client.once('ready', function () { client.set('foo', 'bar'); - client.get('foo', function(err, res) { + client.get('foo', function (err, res) { assert.strictEqual(res, 'bar'); done(err); }); }); }); - it("connects correctly to the provided host with the port set to undefined", function (done) { + it('connects correctly to the provided host with the port set to undefined', function (done) { client = redis.createClient(undefined, 'localhost', { no_ready_check: true }); - client.on("error", done); + client.on('error', done); assert.strictEqual(client.address, 'localhost:6379'); - client.once("ready", function () { + client.once('ready', function () { client.set('foo', 'bar'); - client.get('foo', function(err, res) { + client.get('foo', function (err, res) { assert.strictEqual(res, 'bar'); done(err); }); }); }); - it("connects correctly even if the info command is not present on the redis server", function (done) { + it('connects correctly even if the info command is not present on the redis server', function (done) { client = redis.createClient.apply(null, args); client.info = function (cb) { // Mock the result cb(new Error("ERR unknown command 'info'")); }; - client.once("ready", function () { + client.once('ready', function () { assert.strictEqual(Object.keys(client.server_info).length, 0); done(); }); }); - it("fake the stream to mock redis", function () { + it('fake the stream to mock redis', function () { // This is needed for libraries that want to mock the stream like fakeredis var temp = redis.RedisClient.prototype.create_stream; var create_stream_string = String(temp); @@ -418,7 +418,7 @@ describe("connection tests", function () { it('allows connecting with the redis url to the default host and port, select db 3 and warn about duplicate db option', function (done) { client = redis.createClient('redis:///3?db=3'); assert.strictEqual(client.selected_db, '3'); - client.on("ready", done); + client.on('ready', done); }); it('allows connecting with the redis url and the default port and auth provided even though it is not required', function (done) { @@ -428,7 +428,7 @@ describe("connection tests", function () { assert.strictEqual(msg, 'Warning: Redis server does not require a password, but a password was supplied.'); end(); }); - client.on("ready", end); + client.on('ready', end); }); it('allows connecting with the redis url as first parameter and the options as second parameter', function (done) { @@ -447,7 +447,7 @@ describe("connection tests", function () { assert.strictEqual(+client.selected_db, 3); assert(!client.options.port); assert.strictEqual(client.options.host, config.HOST[ip]); - client.on("ready", done); + client.on('ready', done); }); it('allows connecting with the redis url and no auth and options as second parameter', function (done) { @@ -456,18 +456,18 @@ describe("connection tests", function () { }; client = redis.createClient('redis://' + config.HOST[ip] + ':' + config.PORT, options); assert.strictEqual(Object.keys(options).length, 1); - client.on("ready", done); + client.on('ready', done); }); it('allows connecting with the redis url and no auth and options as third parameter', function (done) { client = redis.createClient('redis://' + config.HOST[ip] + ':' + config.PORT, null, { detect_buffers: false }); - client.on("ready", done); + client.on('ready', done); }); } - it("redis still loading <= 1000ms", function (done) { + it('redis still loading <= 1000ms', function (done) { client = redis.createClient.apply(null, args); var tmp = client.info.bind(client); var end = helper.callFuncAfter(done, 3); @@ -475,7 +475,7 @@ describe("connection tests", function () { var time; // Mock original function and pretent redis is still loading client.info = function (cb) { - tmp(function(err, res) { + tmp(function (err, res) { if (!delayed) { assert(!err); client.server_info.loading = 1; @@ -487,7 +487,7 @@ describe("connection tests", function () { cb(err, res); }); }; - client.on("ready", function () { + client.on('ready', function () { var rest = Date.now() - time; assert(rest >= 498, 'Rest should be equal or above 500 ms but is: ' + rest); // setTimeout might trigger early // Be on the safe side and accept 200ms above the original value @@ -497,7 +497,7 @@ describe("connection tests", function () { }); }); - it("redis still loading > 1000ms", function (done) { + it('redis still loading > 1000ms', function (done) { client = redis.createClient.apply(null, args); var tmp = client.info.bind(client); var end = helper.callFuncAfter(done, 3); @@ -505,7 +505,7 @@ describe("connection tests", function () { var time; // Mock original function and pretent redis is still loading client.info = function (cb) { - tmp(function(err, res) { + tmp(function (err, res) { if (!delayed) { assert(!err); // Try reconnecting after one second even if redis tells us the time needed is above one second @@ -518,7 +518,7 @@ describe("connection tests", function () { cb(err, res); }); }; - client.on("ready", function () { + client.on('ready', function () { var rest = Date.now() - time; assert(rest >= 998, '`rest` should be equal or above 1000 ms but is: ' + rest); // setTimeout might trigger early // Be on the safe side and accept 200ms above the original value diff --git a/test/detect_buffers.spec.js b/test/detect_buffers.spec.js index a7c97eac11e..59f6f6ae204 100644 --- a/test/detect_buffers.spec.js +++ b/test/detect_buffers.spec.js @@ -1,15 +1,15 @@ 'use strict'; -var assert = require("assert"); -var config = require("./lib/config"); +var assert = require('assert'); +var config = require('./lib/config'); var helper = require('./helper'); var redis = config.redis; -describe("detect_buffers", function () { +describe('detect_buffers', function () { - helper.allTests(function(parser, ip, args) { + helper.allTests(function (parser, ip, args) { - describe("using " + parser + " and " + ip, function () { + describe('using ' + parser + ' and ' + ip, function () { var client; var args = config.configureClient(parser, ip, { detect_buffers: true @@ -17,11 +17,11 @@ describe("detect_buffers", function () { beforeEach(function (done) { client = redis.createClient.apply(null, args); - client.once("error", done); - client.once("connect", function () { + client.once('error', done); + client.once('connect', function () { client.flushdb(function (err) { - client.hmset("hash key 2", "key 1", "val 1", "key 2", "val 2"); - client.set("string key 1", "string value"); + client.hmset('hash key 2', 'key 1', 'val 1', 'key 2', 'val 2'); + client.set('string key 1', 'string value'); return done(err); }); }); @@ -34,28 +34,28 @@ describe("detect_buffers", function () { describe('get', function () { describe('first argument is a string', function () { it('returns a string', function (done) { - client.get("string key 1", helper.isString("string value", done)); + client.get('string key 1', helper.isString('string value', done)); }); it('returns a string when executed as part of transaction', function (done) { - client.multi().get("string key 1").exec(helper.isString("string value", done)); + client.multi().get('string key 1').exec(helper.isString('string value', done)); }); }); describe('first argument is a buffer', function () { it('returns a buffer', function (done) { - client.get(new Buffer("string key 1"), function (err, reply) { + client.get(new Buffer('string key 1'), function (err, reply) { assert.strictEqual(true, Buffer.isBuffer(reply)); - assert.strictEqual("", reply.inspect()); + assert.strictEqual('', reply.inspect()); return done(err); }); }); it('returns a bufffer when executed as part of transaction', function (done) { - client.multi().get(new Buffer("string key 1")).exec(function (err, reply) { + client.multi().get(new Buffer('string key 1')).exec(function (err, reply) { assert.strictEqual(1, reply.length); assert.strictEqual(true, Buffer.isBuffer(reply[0])); - assert.strictEqual("", reply[0].inspect()); + assert.strictEqual('', reply[0].inspect()); return done(err); }); }); @@ -65,19 +65,19 @@ describe("detect_buffers", function () { describe('multi.hget', function () { it('can interleave string and buffer results', function (done) { client.multi() - .hget("hash key 2", "key 1") - .hget(new Buffer("hash key 2"), "key 1") - .hget("hash key 2", new Buffer("key 2")) - .hget("hash key 2", "key 2") + .hget('hash key 2', 'key 1') + .hget(new Buffer('hash key 2'), 'key 1') + .hget('hash key 2', new Buffer('key 2')) + .hget('hash key 2', 'key 2') .exec(function (err, reply) { assert.strictEqual(true, Array.isArray(reply)); assert.strictEqual(4, reply.length); - assert.strictEqual("val 1", reply[0]); + assert.strictEqual('val 1', reply[0]); assert.strictEqual(true, Buffer.isBuffer(reply[1])); - assert.strictEqual("", reply[1].inspect()); + assert.strictEqual('', reply[1].inspect()); assert.strictEqual(true, Buffer.isBuffer(reply[2])); - assert.strictEqual("", reply[2].inspect()); - assert.strictEqual("val 2", reply[3]); + assert.strictEqual('', reply[2].inspect()); + assert.strictEqual('val 2', reply[3]); return done(err); }); }); @@ -86,19 +86,19 @@ describe("detect_buffers", function () { describe('batch.hget', function () { it('can interleave string and buffer results', function (done) { client.batch() - .hget("hash key 2", "key 1") - .hget(new Buffer("hash key 2"), "key 1") - .hget("hash key 2", new Buffer("key 2")) - .hget("hash key 2", "key 2") + .hget('hash key 2', 'key 1') + .hget(new Buffer('hash key 2'), 'key 1') + .hget('hash key 2', new Buffer('key 2')) + .hget('hash key 2', 'key 2') .exec(function (err, reply) { assert.strictEqual(true, Array.isArray(reply)); assert.strictEqual(4, reply.length); - assert.strictEqual("val 1", reply[0]); + assert.strictEqual('val 1', reply[0]); assert.strictEqual(true, Buffer.isBuffer(reply[1])); - assert.strictEqual("", reply[1].inspect()); + assert.strictEqual('', reply[1].inspect()); assert.strictEqual(true, Buffer.isBuffer(reply[2])); - assert.strictEqual("", reply[2].inspect()); - assert.strictEqual("val 2", reply[3]); + assert.strictEqual('', reply[2].inspect()); + assert.strictEqual('val 2', reply[3]); return done(err); }); }); @@ -107,28 +107,28 @@ describe("detect_buffers", function () { describe('hmget', function () { describe('first argument is a string', function () { it('returns strings for keys requested', function (done) { - client.hmget("hash key 2", "key 1", "key 2", function (err, reply) { + client.hmget('hash key 2', 'key 1', 'key 2', function (err, reply) { assert.strictEqual(true, Array.isArray(reply)); assert.strictEqual(2, reply.length); - assert.strictEqual("val 1", reply[0]); - assert.strictEqual("val 2", reply[1]); + assert.strictEqual('val 1', reply[0]); + assert.strictEqual('val 2', reply[1]); return done(err); }); }); it('returns strings for keys requested in transaction', function (done) { - client.multi().hmget("hash key 2", "key 1", "key 2").exec(function (err, reply) { + client.multi().hmget('hash key 2', 'key 1', 'key 2').exec(function (err, reply) { assert.strictEqual(true, Array.isArray(reply)); assert.strictEqual(1, reply.length); assert.strictEqual(2, reply[0].length); - assert.strictEqual("val 1", reply[0][0]); - assert.strictEqual("val 2", reply[0][1]); + assert.strictEqual('val 1', reply[0][0]); + assert.strictEqual('val 2', reply[0][1]); return done(err); }); }); it('handles array of strings with undefined values (repro #344)', function (done) { - client.hmget("hash key 2", "key 3", "key 4", function(err, reply) { + client.hmget('hash key 2', 'key 3', 'key 4', function (err, reply) { assert.strictEqual(true, Array.isArray(reply)); assert.strictEqual(2, reply.length); assert.equal(null, reply[0]); @@ -138,7 +138,7 @@ describe("detect_buffers", function () { }); it('handles array of strings with undefined values in transaction (repro #344)', function (done) { - client.multi().hmget("hash key 2", "key 3", "key 4").exec(function(err, reply) { + client.multi().hmget('hash key 2', 'key 3', 'key 4').exec(function (err, reply) { assert.strictEqual(true, Array.isArray(reply)); assert.strictEqual(1, reply.length); assert.strictEqual(2, reply[0].length); @@ -151,39 +151,39 @@ describe("detect_buffers", function () { describe('first argument is a buffer', function () { it('returns buffers for keys requested', function (done) { - client.hmget(new Buffer("hash key 2"), "key 1", "key 2", function (err, reply) { + client.hmget(new Buffer('hash key 2'), 'key 1', 'key 2', function (err, reply) { assert.strictEqual(true, Array.isArray(reply)); assert.strictEqual(2, reply.length); assert.strictEqual(true, Buffer.isBuffer(reply[0])); assert.strictEqual(true, Buffer.isBuffer(reply[1])); - assert.strictEqual("", reply[0].inspect()); - assert.strictEqual("", reply[1].inspect()); + assert.strictEqual('', reply[0].inspect()); + assert.strictEqual('', reply[1].inspect()); return done(err); }); }); - it("returns buffers for keys requested in transaction", function (done) { - client.multi().hmget(new Buffer("hash key 2"), "key 1", "key 2").exec(function (err, reply) { + it('returns buffers for keys requested in transaction', function (done) { + client.multi().hmget(new Buffer('hash key 2'), 'key 1', 'key 2').exec(function (err, reply) { assert.strictEqual(true, Array.isArray(reply)); assert.strictEqual(1, reply.length); assert.strictEqual(2, reply[0].length); assert.strictEqual(true, Buffer.isBuffer(reply[0][0])); assert.strictEqual(true, Buffer.isBuffer(reply[0][1])); - assert.strictEqual("", reply[0][0].inspect()); - assert.strictEqual("", reply[0][1].inspect()); + assert.strictEqual('', reply[0][0].inspect()); + assert.strictEqual('', reply[0][1].inspect()); return done(err); }); }); - it("returns buffers for keys requested in .batch", function (done) { - client.batch().hmget(new Buffer("hash key 2"), "key 1", "key 2").exec(function (err, reply) { + it('returns buffers for keys requested in .batch', function (done) { + client.batch().hmget(new Buffer('hash key 2'), 'key 1', 'key 2').exec(function (err, reply) { assert.strictEqual(true, Array.isArray(reply)); assert.strictEqual(1, reply.length); assert.strictEqual(2, reply[0].length); assert.strictEqual(true, Buffer.isBuffer(reply[0][0])); assert.strictEqual(true, Buffer.isBuffer(reply[0][1])); - assert.strictEqual("", reply[0][0].inspect()); - assert.strictEqual("", reply[0][1].inspect()); + assert.strictEqual('', reply[0][0].inspect()); + assert.strictEqual('', reply[0][1].inspect()); return done(err); }); }); @@ -193,33 +193,33 @@ describe("detect_buffers", function () { describe('hgetall', function (done) { describe('first argument is a string', function () { it('returns string values', function (done) { - client.hgetall("hash key 2", function (err, reply) { - assert.strictEqual("object", typeof reply); + client.hgetall('hash key 2', function (err, reply) { + assert.strictEqual('object', typeof reply); assert.strictEqual(2, Object.keys(reply).length); - assert.strictEqual("val 1", reply["key 1"]); - assert.strictEqual("val 2", reply["key 2"]); + assert.strictEqual('val 1', reply['key 1']); + assert.strictEqual('val 2', reply['key 2']); return done(err); }); }); it('returns string values when executed in transaction', function (done) { - client.multi().hgetall("hash key 2").exec(function (err, reply) { + client.multi().hgetall('hash key 2').exec(function (err, reply) { assert.strictEqual(1, reply.length); - assert.strictEqual("object", typeof reply[0]); + assert.strictEqual('object', typeof reply[0]); assert.strictEqual(2, Object.keys(reply[0]).length); - assert.strictEqual("val 1", reply[0]["key 1"]); - assert.strictEqual("val 2", reply[0]["key 2"]); + assert.strictEqual('val 1', reply[0]['key 1']); + assert.strictEqual('val 2', reply[0]['key 2']); return done(err); }); }); it('returns string values when executed in .batch', function (done) { - client.batch().hgetall("hash key 2").exec(function (err, reply) { + client.batch().hgetall('hash key 2').exec(function (err, reply) { assert.strictEqual(1, reply.length); - assert.strictEqual("object", typeof reply[0]); + assert.strictEqual('object', typeof reply[0]); assert.strictEqual(2, Object.keys(reply[0]).length); - assert.strictEqual("val 1", reply[0]["key 1"]); - assert.strictEqual("val 2", reply[0]["key 2"]); + assert.strictEqual('val 1', reply[0]['key 1']); + assert.strictEqual('val 2', reply[0]['key 2']); return done(err); }); }); @@ -227,40 +227,40 @@ describe("detect_buffers", function () { describe('first argument is a buffer', function () { it('returns buffer values', function (done) { - client.hgetall(new Buffer("hash key 2"), function (err, reply) { + client.hgetall(new Buffer('hash key 2'), function (err, reply) { assert.strictEqual(null, err); - assert.strictEqual("object", typeof reply); + assert.strictEqual('object', typeof reply); assert.strictEqual(2, Object.keys(reply).length); - assert.strictEqual(true, Buffer.isBuffer(reply["key 1"])); - assert.strictEqual(true, Buffer.isBuffer(reply["key 2"])); - assert.strictEqual("", reply["key 1"].inspect()); - assert.strictEqual("", reply["key 2"].inspect()); + assert.strictEqual(true, Buffer.isBuffer(reply['key 1'])); + assert.strictEqual(true, Buffer.isBuffer(reply['key 2'])); + assert.strictEqual('', reply['key 1'].inspect()); + assert.strictEqual('', reply['key 2'].inspect()); return done(err); }); }); it('returns buffer values when executed in transaction', function (done) { - client.multi().hgetall(new Buffer("hash key 2")).exec(function (err, reply) { + client.multi().hgetall(new Buffer('hash key 2')).exec(function (err, reply) { assert.strictEqual(1, reply.length); - assert.strictEqual("object", typeof reply[0]); + assert.strictEqual('object', typeof reply[0]); assert.strictEqual(2, Object.keys(reply[0]).length); - assert.strictEqual(true, Buffer.isBuffer(reply[0]["key 1"])); - assert.strictEqual(true, Buffer.isBuffer(reply[0]["key 2"])); - assert.strictEqual("", reply[0]["key 1"].inspect()); - assert.strictEqual("", reply[0]["key 2"].inspect()); + assert.strictEqual(true, Buffer.isBuffer(reply[0]['key 1'])); + assert.strictEqual(true, Buffer.isBuffer(reply[0]['key 2'])); + assert.strictEqual('', reply[0]['key 1'].inspect()); + assert.strictEqual('', reply[0]['key 2'].inspect()); return done(err); }); }); it('returns buffer values when executed in .batch', function (done) { - client.batch().hgetall(new Buffer("hash key 2")).exec(function (err, reply) { + client.batch().hgetall(new Buffer('hash key 2')).exec(function (err, reply) { assert.strictEqual(1, reply.length); - assert.strictEqual("object", typeof reply[0]); + assert.strictEqual('object', typeof reply[0]); assert.strictEqual(2, Object.keys(reply[0]).length); - assert.strictEqual(true, Buffer.isBuffer(reply[0]["key 1"])); - assert.strictEqual(true, Buffer.isBuffer(reply[0]["key 2"])); - assert.strictEqual("", reply[0]["key 1"].inspect()); - assert.strictEqual("", reply[0]["key 2"].inspect()); + assert.strictEqual(true, Buffer.isBuffer(reply[0]['key 1'])); + assert.strictEqual(true, Buffer.isBuffer(reply[0]['key 2'])); + assert.strictEqual('', reply[0]['key 1'].inspect()); + assert.strictEqual('', reply[0]['key 2'].inspect()); return done(err); }); }); @@ -268,4 +268,4 @@ describe("detect_buffers", function () { }); }); }); -}); \ No newline at end of file +}); diff --git a/test/helper.js b/test/helper.js index dd30e527b0a..5564a49c5ef 100644 --- a/test/helper.js +++ b/test/helper.js @@ -1,10 +1,10 @@ 'use strict'; -var assert = require("assert"); +var assert = require('assert'); var path = require('path'); -var config = require("./lib/config"); -var RedisProcess = require("./lib/redis-process"); -var StunnelProcess = require("./lib/stunnel-process"); +var config = require('./lib/config'); +var RedisProcess = require('./lib/redis-process'); +var StunnelProcess = require('./lib/stunnel-process'); var rp; var stunnel_process; @@ -33,7 +33,7 @@ module.exports = { redisProcess: function () { return rp; }, - stopRedis: function(done) { + stopRedis: function (done) { rp.stop(done); }, startRedis: startRedis, @@ -52,59 +52,59 @@ module.exports = { }, isNumber: function (expected, done) { return function (err, results) { - assert.strictEqual(null, err, "expected " + expected + ", got error: " + err); - assert.strictEqual(expected, results, expected + " !== " + results); - assert.strictEqual(typeof results, "number", "expected a number, got " + typeof results); - if (done) return done(); + assert.strictEqual(null, err, 'expected ' + expected + ', got error: ' + err); + assert.strictEqual(expected, results, expected + ' !== ' + results); + assert.strictEqual(typeof results, 'number', 'expected a number, got ' + typeof results); + if (done) done(); }; }, isString: function (str, done) { return function (err, results) { assert.strictEqual(null, err, "expected string '" + str + "', got error: " + err); - assert.equal(str, results, str + " does not match " + results); - if (done) return done(); + assert.equal(str, results, str + ' does not match ' + results); + if (done) done(); }; }, isNull: function (done) { return function (err, results) { - assert.strictEqual(null, err, "expected null, got error: " + err); - assert.strictEqual(null, results, results + " is not null"); - if (done) return done(); + assert.strictEqual(null, err, 'expected null, got error: ' + err); + assert.strictEqual(null, results, results + ' is not null'); + if (done) done(); }; }, isError: function (done) { return function (err, results) { assert(err instanceof Error, "err is not instance of 'Error', but an error is expected here."); - if (done) return done(); + if (done) done(); }; }, isNotError: function (done) { return function (err, results) { - assert.strictEqual(err, null, "expected success, got an error: " + err); - if (done) return done(); + assert.strictEqual(err, null, 'expected success, got an error: ' + err); + if (done) done(); }; }, isType: { number: function (done) { return function (err, results) { - assert.strictEqual(null, err, "expected any number, got error: " + err); - assert.strictEqual(typeof results, "number", results + " is not a number"); - if (done) return done(); + assert.strictEqual(null, err, 'expected any number, got error: ' + err); + assert.strictEqual(typeof results, 'number', results + ' is not a number'); + if (done) done(); }; }, positiveNumber: function (done) { return function (err, results) { - assert.strictEqual(null, err, "expected positive number, got error: " + err); - assert.strictEqual(true, (results > 0), results + " is not a positive number"); - if (done) return done(); + assert.strictEqual(null, err, 'expected positive number, got error: ' + err); + assert.strictEqual(true, (results > 0), results + ' is not a positive number'); + if (done) done(); }; } }, match: function (pattern, done) { return function (err, results) { - assert.strictEqual(null, err, "expected " + pattern.toString() + ", got error: " + err); + assert.strictEqual(null, err, 'expected ' + pattern.toString() + ', got error: ' + err); assert(pattern.test(results), "expected string '" + results + "' to match " + pattern.toString()); - if (done) return done(); + if (done) done(); }; }, serverVersionAtLeast: function (connection, desired_version) { @@ -131,7 +131,7 @@ module.exports = { try { require('hiredis'); parsers.push('hiredis'); - } catch (e) {} + } catch (e) {/* ignore eslint */} if (process.platform !== 'win32') { protocols.push('IPv6', '/tmp/redis.sock'); } @@ -148,7 +148,7 @@ module.exports = { strOptions += key + ': ' + options[key] + '; '; } } - describe('using options: ' + strOptions, function() { + describe('using options: ' + strOptions, function () { parsers.forEach(function (parser) { protocols.forEach(function (ip, i) { if (i !== 0 && !opts.allConnections) { diff --git a/test/lib/config.js b/test/lib/config.js index ed7ef640c5d..0420b032ce3 100644 --- a/test/lib/config.js +++ b/test/lib/config.js @@ -13,8 +13,8 @@ var config = { redis: redis, PORT: 6379, HOST: { - IPv4: "127.0.0.1", - IPv6: "::1" + IPv4: '127.0.0.1', + IPv6: '::1' }, configureClient: function (parser, ip, opts) { var args = []; diff --git a/test/lib/redis-process.js b/test/lib/redis-process.js index a6e978494e6..e98f7fbffcd 100644 --- a/test/lib/redis-process.js +++ b/test/lib/redis-process.js @@ -52,11 +52,11 @@ module.exports = { var spawnFailed = false; // spawn redis with our testing configuration. var confFile = conf || path.resolve(__dirname, '../conf/redis.conf'); - var rp = spawn("redis-server", [confFile], {}); + var rp = spawn('redis-server', [confFile], {}); // capture a failure booting redis, and give // the user running the test some directions. - rp.once("exit", function (code) { + rp.once('exit', function (code) { if (code !== 0) spawnFailed = true; }); @@ -71,7 +71,7 @@ module.exports = { }, stop: function (done) { if (spawnFailed) return done(); - rp.once("exit", function (code) { + rp.once('exit', function (code) { var error = null; if (code !== null && code !== 0) { error = new Error('Redis shutdown failed with code ' + code); @@ -80,7 +80,7 @@ module.exports = { return done(error); }, port); }); - rp.kill("SIGTERM"); + rp.kill('SIGTERM'); } }); }, port); diff --git a/test/lib/stunnel-process.js b/test/lib/stunnel-process.js index f75049da179..d773f4f1ded 100644 --- a/test/lib/stunnel-process.js +++ b/test/lib/stunnel-process.js @@ -12,16 +12,16 @@ if (typeof EventEmitter !== 'function') { EventEmitter = EventEmitter.EventEmitter; } -function once(cb) { +function once (cb) { var called = false; - return function() { + return function () { if (called) return; called = true; cb.apply(this, arguments); }; } -function StunnelProcess(conf_dir) { +function StunnelProcess (conf_dir) { EventEmitter.call(this); // set up an stunnel to redis; edit the conf file to include required absolute paths @@ -33,16 +33,16 @@ function StunnelProcess(conf_dir) { // handle child process events, and failure to set up tunnel var self = this; - this.timer = setTimeout(function() { + this.timer = setTimeout(function () { self.emit('error', new Error('Timeout waiting for stunnel to start')); }, 8000); - stunnel.on('error', function(err) { + stunnel.on('error', function (err) { self.clear(); self.emit('error', err); }); - stunnel.on('exit', function(code) { + stunnel.on('exit', function (code) { self.clear(); if (code === 0) { self.emit('stopped'); @@ -52,7 +52,7 @@ function StunnelProcess(conf_dir) { }); // wait to stunnel to start - stunnel.stderr.on("data", function(data) { + stunnel.stderr.on('data', function (data) { if (data.toString().match(/Service.+redis.+bound/)) { clearTimeout(this.timer); self.emit('started'); @@ -61,25 +61,25 @@ function StunnelProcess(conf_dir) { } util.inherits(StunnelProcess, EventEmitter); -StunnelProcess.prototype.clear = function() { +StunnelProcess.prototype.clear = function () { this.stunnel = null; clearTimeout(this.timer); }; -StunnelProcess.prototype.stop = function(done) { +StunnelProcess.prototype.stop = function (done) { if (this.stunnel) { this.stunnel.kill(); } }; module.exports = { - start: function(done, conf_dir) { + start: function (done, conf_dir) { done = once(done); var stunnel = new StunnelProcess(conf_dir); stunnel.once('error', done.bind(done)); stunnel.once('started', done.bind(done, null, stunnel)); }, - stop: function(stunnel, done) { + stop: function (stunnel, done) { stunnel.removeAllListeners(); stunnel.stop(); stunnel.once('error', done.bind(done)); diff --git a/test/lib/unref.js b/test/lib/unref.js index 2fb24bd7485..5ad176238e8 100644 --- a/test/lib/unref.js +++ b/test/lib/unref.js @@ -3,15 +3,15 @@ // as soon as there are no outstanding commands. 'use strict'; -var redis = require("../../index"); +var redis = require('../../index'); var HOST = process.argv[2] || '127.0.0.1'; var PORT = process.argv[3]; var args = PORT ? [PORT, HOST] : [HOST]; var c = redis.createClient.apply(redis, args); c.info(function (err, reply) { - if (err) process.exit(-1); - if (!reply.length) process.exit(-1); - process.stdout.write(reply.length.toString()); + if (err) process.exit(-1); + if (!reply.length) process.exit(-1); + process.stdout.write(reply.length.toString()); }); c.unref(); diff --git a/test/multi.spec.js b/test/multi.spec.js index 20be0325a76..a94c2bae994 100644 --- a/test/multi.spec.js +++ b/test/multi.spec.js @@ -1,11 +1,10 @@ 'use strict'; var assert = require('assert'); -var config = require("./lib/config"); +var config = require('./lib/config'); var helper = require('./helper'); var redis = config.redis; var zlib = require('zlib'); -var uuid = require('uuid'); var client; describe("The 'multi' method", function () { @@ -23,14 +22,14 @@ describe("The 'multi' method", function () { // Some random object created from http://beta.json-generator.com/ var test_obj = { - "_id": "5642c4c33d4667c4a1fefd99","index": 0, "guid": "5baf1f1c-7621-41e7-ae7a-f8c6f3199b0f", "isActive": true, - "balance": "$1,028.63", "picture": "http://placehold.it/32x32", "age": 31, "eyeColor": "green", "name": {"first": "Shana", "last": "Long"}, - "company": "MANGLO", "email": "shana.long@manglo.us", "phone": "+1 (926) 405-3105", "address": "747 Dank Court, Norfolk, Ohio, 1112", - "about": "Eu pariatur in nisi occaecat enim qui consequat nostrud cupidatat id. " + - "Commodo commodo dolore esse irure minim quis deserunt anim laborum aute deserunt et est. Quis nisi laborum deserunt nisi quis.", - "registered": "Friday, April 18, 2014 9:56 AM", "latitude": "74.566613", "longitude": "-11.660432", "tags": [7, "excepteur"], - "range": [0, 1, 2, 3, 4, 5, 6, 7, 8, 9], "friends": [3, {"id": 1, "name": "Schultz Dyer"}], - "greeting": "Hello, Shana! You have 5 unread messages.", "favoriteFruit": "strawberry" + '_id': '5642c4c33d4667c4a1fefd99', 'index': 0, 'guid': '5baf1f1c-7621-41e7-ae7a-f8c6f3199b0f', 'isActive': true, + 'balance': '$1,028.63', 'picture': 'http://placehold.it/32x32', 'age': 31, 'eyeColor': 'green', 'name': {'first': 'Shana', 'last': 'Long'}, + 'company': 'MANGLO', 'email': 'shana.long@manglo.us', 'phone': '+1 (926) 405-3105', 'address': '747 Dank Court, Norfolk, Ohio, 1112', + 'about': 'Eu pariatur in nisi occaecat enim qui consequat nostrud cupidatat id. ' + + 'Commodo commodo dolore esse irure minim quis deserunt anim laborum aute deserunt et est. Quis nisi laborum deserunt nisi quis.', + 'registered': 'Friday, April 18, 2014 9:56 AM', 'latitude': '74.566613', 'longitude': '-11.660432', 'tags': [7, 'excepteur'], + 'range': [0, 1, 2, 3, 4, 5, 6, 7, 8, 9], 'friends': [3, {'id': 1, 'name': 'Schultz Dyer'}], + 'greeting': 'Hello, Shana! You have 5 unread messages.', 'favoriteFruit': 'strawberry' }; function run () { @@ -39,7 +38,8 @@ describe("The 'multi' method", function () { } // To demonstrate a big payload for hash set field values, let's create a big array var test_arr = []; - for (var i = 0; i < 80; i++) { + var i = 0; + for (; i < 80; i++) { var new_obj = JSON.parse(JSON.stringify(test_obj)); test_arr.push(new_obj); } @@ -92,27 +92,21 @@ describe("The 'multi' method", function () { }); - helper.allTests(function(parser, ip, args) { + helper.allTests(function (parser, ip, args) { - describe("using " + parser + " and " + ip, function () { - var key, value; + describe('using ' + parser + ' and ' + ip, function () { - beforeEach(function () { - key = uuid.v4(); - value = uuid.v4(); - }); - - describe("when not connected", function () { + describe('when not connected', function () { beforeEach(function (done) { client = redis.createClient.apply(null, args); - client.once("ready", function () { + client.once('ready', function () { client.quit(); }); client.once('end', done); }); - it("reports an error", function (done) { + it('reports an error', function (done) { var multi = client.multi(); var notBuffering = multi.exec(function (err, res) { assert(err.message.match(/The connection has already been closed/)); @@ -121,33 +115,33 @@ describe("The 'multi' method", function () { assert.strictEqual(notBuffering, false); }); - it("reports an error if promisified", function () { - return client.multi().execAsync().catch(function(err) { + it('reports an error if promisified', function () { + return client.multi().execAsync().catch(function (err) { assert(err.message.match(/The connection has already been closed/)); }); }); }); - describe("when connected", function () { + describe('when connected', function () { beforeEach(function (done) { client = redis.createClient.apply(null, args); - client.once("connect", done); + client.once('connect', done); }); - it("executes a pipelined multi properly in combination with the offline queue", function (done) { + it('executes a pipelined multi properly in combination with the offline queue', function (done) { var multi1 = client.multi(); - multi1.set("m1", "123"); + multi1.set('m1', '123'); multi1.get('m1'); multi1.exec(done); }); - it("executes a pipelined multi properly after a reconnect in combination with the offline queue", function (done) { + it('executes a pipelined multi properly after a reconnect in combination with the offline queue', function (done) { client.once('ready', function () { client.stream.destroy(); var called = false; var multi1 = client.multi(); - multi1.set("m1", "123"); + multi1.set('m1', '123'); multi1.get('m1'); multi1.exec(function (err, res) { assert(!err); @@ -155,7 +149,7 @@ describe("The 'multi' method", function () { }); client.once('ready', function () { var multi1 = client.multi(); - multi1.set("m2", "456"); + multi1.set('m2', '456'); multi1.get('m2'); multi1.exec(function (err, res) { assert(called); @@ -168,16 +162,16 @@ describe("The 'multi' method", function () { }); }); - describe("when connection is broken", function () { + describe('when connection is broken', function () { - it("return an error even if connection is in broken mode if callback is present", function (done) { + it('return an error even if connection is in broken mode if callback is present', function (done) { client = redis.createClient({ host: 'somewhere', port: 6379, max_attempts: 1 }); - client.on('error', function(err) { + client.on('error', function (err) { if (/Redis connection in broken state/.test(err.message)) { done(); } @@ -189,14 +183,14 @@ describe("The 'multi' method", function () { }); }); - it("does not emit an error twice if connection is in broken mode with no callback", function (done) { + it('does not emit an error twice if connection is in broken mode with no callback', function (done) { client = redis.createClient({ host: 'somewhere', port: 6379, max_attempts: 1 }); - client.on('error', function(err) { + client.on('error', function (err) { // Results in multiple done calls if test fails if (/Redis connection in broken state/.test(err.message)) { done(); @@ -207,18 +201,18 @@ describe("The 'multi' method", function () { }); }); - describe("when ready", function () { + describe('when ready', function () { beforeEach(function (done) { client = redis.createClient.apply(null, args); - client.once("ready", function () { + client.once('ready', function () { client.flushdb(function (err) { return done(err); }); }); }); - it("returns an empty result array", function (done) { + it('returns an empty result array', function (done) { var multi = client.multi(); var notBuffering = multi.exec(function (err, res) { assert.strictEqual(err, null); @@ -228,43 +222,43 @@ describe("The 'multi' method", function () { assert.strictEqual(notBuffering, true); }); - it("runs normal calls in-between multis", function (done) { + it('runs normal calls in-between multis', function (done) { var multi1 = client.multi(); - multi1.set("m1", "123"); + multi1.set('m1', '123'); client.set('m2', '456', done); }); - it("runs simultaneous multis with the same client", function (done) { + it('runs simultaneous multis with the same client', function (done) { var end = helper.callFuncAfter(done, 2); var multi1 = client.multi(); - multi1.set("m1", "123"); + multi1.set('m1', '123'); multi1.get('m1'); var multi2 = client.multi(); - multi2.set("m2", "456"); + multi2.set('m2', '456'); multi2.get('m2'); multi1.exec(end); - multi2.exec(function(err, res) { + multi2.exec(function (err, res) { assert.strictEqual(res[1], '456'); end(); }); }); - it("runs simultaneous multis with the same client version 2", function (done) { + it('runs simultaneous multis with the same client version 2', function (done) { var end = helper.callFuncAfter(done, 2); var multi2 = client.multi(); var multi1 = client.multi(); - multi2.set("m2", "456"); - multi1.set("m1", "123"); + multi2.set('m2', '456'); + multi1.set('m1', '123'); multi1.get('m1'); multi2.get('m2'); multi2.ping(); multi1.exec(end); - multi2.exec(function(err, res) { + multi2.exec(function (err, res) { assert.strictEqual(res[1], '456'); end(); }); @@ -274,11 +268,11 @@ describe("The 'multi' method", function () { var multi1, multi2; // Provoke an error at queue time multi1 = client.MULTI(); - multi1.mset("multifoo", "10", "multibar", "20", helper.isString("OK")); + multi1.mset('multifoo', '10', 'multibar', '20', helper.isString('OK')); - multi1.set("foo2", helper.isError()); - multi1.incr("multifoo"); - multi1.incr("multibar"); + multi1.set('foo2', helper.isError()); + multi1.incr('multifoo'); + multi1.incr('multibar'); multi1.exec(function () { // Redis 2.6.5+ will abort transactions with errors // see: http://redis.io/topics/transactions @@ -286,8 +280,8 @@ describe("The 'multi' method", function () { var multifoo_expected = 1; // Confirm that the previous command, while containing an error, still worked. multi2 = client.multi(); - multi2.incr("multibar", helper.isNumber(multibar_expected)); - multi2.incr("multifoo", helper.isNumber(multifoo_expected)); + multi2.incr('multibar', helper.isNumber(multibar_expected)); + multi2.incr('multifoo', helper.isNumber(multifoo_expected)); multi2.exec(function (err, replies) { assert.strictEqual(multibar_expected, replies[0]); assert.strictEqual(multifoo_expected, replies[1]); @@ -299,14 +293,14 @@ describe("The 'multi' method", function () { it('roles back a transaction when one command in an array of commands fails', function (done) { // test nested multi-bulk replies client.multi([ - ["mget", "multifoo", "multibar", function (err, res) { + ['mget', 'multifoo', 'multibar', function (err, res) { assert.strictEqual(2, res.length); assert.strictEqual(0, +res[0]); assert.strictEqual(0, +res[1]); }], - ["set", "foo2", helper.isError()], - ["incr", "multifoo"], - ["incr", "multibar"] + ['set', 'foo2', helper.isError()], + ['incr', 'multifoo'], + ['incr', 'multibar'] ]).exec(function (err, replies) { assert.notEqual(err, null); assert.equal(replies, undefined); @@ -315,25 +309,25 @@ describe("The 'multi' method", function () { }); it('handles multiple operations being applied to a set', function (done) { - client.sadd("some set", "mem 1"); - client.sadd(["some set", "mem 2"]); - client.sadd("some set", "mem 3"); - client.sadd("some set", "mem 4"); + client.sadd('some set', 'mem 1'); + client.sadd(['some set', 'mem 2']); + client.sadd('some set', 'mem 3'); + client.sadd('some set', 'mem 4'); // make sure empty mb reply works - client.del("some missing set"); - client.smembers("some missing set", function (err, reply) { + client.del('some missing set'); + client.smembers('some missing set', function (err, reply) { // make sure empty mb reply works assert.strictEqual(0, reply.length); }); // test nested multi-bulk replies with empty mb elements. client.multi([ - ["smembers", ["some set"]], - ["del", "some set"], - ["smembers", "some set"] + ['smembers', ['some set']], + ['del', 'some set'], + ['smembers', 'some set'] ]) - .scard("some set") + .scard('some set') .exec(function (err, replies) { assert.strictEqual(4, replies[0].length); assert.strictEqual(0, replies[2].length); @@ -343,26 +337,26 @@ describe("The 'multi' method", function () { it('allows multiple operations to be performed using constructor with all kinds of syntax', function (done) { var now = Date.now(); - var arr = ["multihmset", "multibar", "multibaz"]; + var arr = ['multihmset', 'multibar', 'multibaz']; var arr2 = ['some manner of key', 'otherTypes']; - var arr3 = [5768, "multibarx", "multifoox"]; - var arr4 = ["mset", [578, "multibar"], helper.isString('OK')]; + var arr3 = [5768, 'multibarx', 'multifoox']; + var arr4 = ['mset', [578, 'multibar'], helper.isString('OK')]; client.multi([ arr4, - [["mset", "multifoo2", "multibar2", "multifoo3", "multibar3"], helper.isString('OK')], - ["hmset", arr], - [["hmset", "multihmset2", "multibar2", "multifoo3", "multibar3", "test"], helper.isString('OK')], - ["hmset", ["multihmset", "multibar", "multifoo"], helper.isString('OK')], - ["hmset", arr3, helper.isString('OK')], - ['hmset', now, {123456789: "abcdefghij", "some manner of key": "a type of value", "otherTypes": 555}], - ['hmset', 'key2', {"0123456789": "abcdefghij", "some manner of key": "a type of value", "otherTypes": 999}, helper.isString('OK')], - ["HMSET", "multihmset", ["multibar", "multibaz"], undefined], // undefined is used as a explicit not set callback variable - ["hmset", "multihmset", ["multibar", "multibaz"], helper.isString('OK')], + [['mset', 'multifoo2', 'multibar2', 'multifoo3', 'multibar3'], helper.isString('OK')], + ['hmset', arr], + [['hmset', 'multihmset2', 'multibar2', 'multifoo3', 'multibar3', 'test'], helper.isString('OK')], + ['hmset', ['multihmset', 'multibar', 'multifoo'], helper.isString('OK')], + ['hmset', arr3, helper.isString('OK')], + ['hmset', now, {123456789: 'abcdefghij', 'some manner of key': 'a type of value', 'otherTypes': 555}], + ['hmset', 'key2', {'0123456789': 'abcdefghij', 'some manner of key': 'a type of value', 'otherTypes': 999}, helper.isString('OK')], + ['HMSET', 'multihmset', ['multibar', 'multibaz'], undefined], // undefined is used as a explicit not set callback variable + ['hmset', 'multihmset', ['multibar', 'multibaz'], helper.isString('OK')], ]) .hmget(now, 123456789, 'otherTypes') - .hmget('key2', arr2, function noop() {}) + .hmget('key2', arr2, function noop () {}) .hmget(['multihmset2', 'some manner of key', 'multibar3']) - .mget('multifoo2', ['multifoo3', 'multifoo'], function(err, res) { + .mget('multifoo2', ['multifoo3', 'multifoo'], function (err, res) { assert(res[0], 'multifoo3'); assert(res[1], 'multifoo'); }) @@ -383,7 +377,7 @@ describe("The 'multi' method", function () { }); }); - it('converts a non string key to a string', function(done) { + it('converts a non string key to a string', function (done) { // TODO: Converting the key might change soon again. client.multi().hmset(true, { test: 123, @@ -391,8 +385,8 @@ describe("The 'multi' method", function () { }).exec(done); }); - it('runs a multi without any further commands', function(done) { - var buffering = client.multi().exec(function(err, res) { + it('runs a multi without any further commands', function (done) { + var buffering = client.multi().exec(function (err, res) { assert.strictEqual(err, null); assert.strictEqual(res.length, 0); done(); @@ -453,8 +447,8 @@ describe("The 'multi' method", function () { it('allows an array to be provided indicating multiple operations to perform', function (done) { // test nested multi-bulk replies with nulls. client.multi([ - ["mget", ["multifoo", "some", "random value", "keys"]], - ["incr", "multifoo"] + ['mget', ['multifoo', 'some', 'random value', 'keys']], + ['incr', 'multifoo'] ]) .exec(function (err, replies) { assert.strictEqual(replies.length, 2); @@ -465,101 +459,101 @@ describe("The 'multi' method", function () { it('allows multiple operations to be performed on a hash', function (done) { client.multi() - .hmset("multihash", "a", "foo", "b", 1) - .hmset("multihash", { - extra: "fancy", - things: "here" + .hmset('multihash', 'a', 'foo', 'b', 1) + .hmset('multihash', { + extra: 'fancy', + things: 'here' }) - .hgetall("multihash") + .hgetall('multihash') .exec(function (err, replies) { assert.strictEqual(null, err); - assert.equal("OK", replies[0]); + assert.equal('OK', replies[0]); assert.equal(Object.keys(replies[2]).length, 4); - assert.equal("foo", replies[2].a); - assert.equal("1", replies[2].b); - assert.equal("fancy", replies[2].extra); - assert.equal("here", replies[2].things); + assert.equal('foo', replies[2].a); + assert.equal('1', replies[2].b); + assert.equal('fancy', replies[2].extra); + assert.equal('here', replies[2].things); return done(); }); }); it('reports EXECABORT exceptions when they occur (while queueing)', function (done) { - client.multi().config("bar").set("foo").set("bar").exec(function (err, reply) { - assert.equal(err.code, "EXECABORT"); - assert.equal(reply, undefined, "The reply should have been discarded"); - assert(err.message.match(/^EXECABORT/), "Error message should begin with EXECABORT"); - assert.equal(err.errors.length, 2, "err.errors should have 2 items"); + client.multi().config('bar').set('foo').set('bar').exec(function (err, reply) { + assert.equal(err.code, 'EXECABORT'); + assert.equal(reply, undefined, 'The reply should have been discarded'); + assert(err.message.match(/^EXECABORT/), 'Error message should begin with EXECABORT'); + assert.equal(err.errors.length, 2, 'err.errors should have 2 items'); assert.strictEqual(err.errors[0].command, 'SET'); assert.strictEqual(err.errors[0].code, 'ERR'); assert.strictEqual(err.errors[0].position, 1); - assert(/^ERR/.test(err.errors[0].message), "Actuall error message should begin with ERR"); + assert(/^ERR/.test(err.errors[0].message), 'Actuall error message should begin with ERR'); return done(); }); }); it('reports multiple exceptions when they occur (while EXEC is running)', function (done) { - client.multi().config("bar").debug("foo").eval("return {err='this is an error'}", 0).exec(function (err, reply) { + client.multi().config('bar').debug('foo').eval("return {err='this is an error'}", 0).exec(function (err, reply) { assert.strictEqual(reply.length, 3); assert.equal(reply[0].code, 'ERR'); assert.equal(reply[0].command, 'CONFIG'); assert.equal(reply[2].code, undefined); assert.equal(reply[2].command, 'EVAL'); assert(/^this is an error/.test(reply[2].message)); - assert(/^ERR/.test(reply[0].message), "Error message should begin with ERR"); - assert(/^ERR/.test(reply[1].message), "Error message should begin with ERR"); + assert(/^ERR/.test(reply[0].message), 'Error message should begin with ERR'); + assert(/^ERR/.test(reply[1].message), 'Error message should begin with ERR'); return done(); }); }); it('reports multiple exceptions when they occur (while EXEC is running) promisified', function () { - return client.multi().config("bar").debug("foo").eval("return {err='this is an error'}", 0).execAsync().then(function (reply) { + return client.multi().config('bar').debug('foo').eval("return {err='this is an error'}", 0).execAsync().then(function (reply) { assert.strictEqual(reply.length, 3); assert.equal(reply[0].code, 'ERR'); assert.equal(reply[0].command, 'CONFIG'); assert.equal(reply[2].code, undefined); assert.equal(reply[2].command, 'EVAL'); assert(/^this is an error/.test(reply[2].message)); - assert(/^ERR/.test(reply[0].message), "Error message should begin with ERR"); - assert(/^ERR/.test(reply[1].message), "Error message should begin with ERR"); + assert(/^ERR/.test(reply[0].message), 'Error message should begin with ERR'); + assert(/^ERR/.test(reply[1].message), 'Error message should begin with ERR'); }); }); it('reports multiple exceptions when they occur (while EXEC is running) and calls cb', function (done) { var multi = client.multi(); - multi.config("bar", helper.isError()); + multi.config('bar', helper.isError()); multi.set('foo', 'bar', helper.isString('OK')); - multi.debug("foo").exec(function (err, reply) { + multi.debug('foo').exec(function (err, reply) { assert.strictEqual(reply.length, 3); assert.strictEqual(reply[0].code, 'ERR'); - assert(/^ERR/.test(reply[0].message), "Error message should begin with ERR"); - assert(/^ERR/.test(reply[2].message), "Error message should begin with ERR"); - assert.strictEqual(reply[1], "OK"); + assert(/^ERR/.test(reply[0].message), 'Error message should begin with ERR'); + assert(/^ERR/.test(reply[2].message), 'Error message should begin with ERR'); + assert.strictEqual(reply[1], 'OK'); client.get('foo', helper.isString('bar', done)); }); }); - it("emits an error if no callback has been provided and execabort error occured", function (done) { + it('emits an error if no callback has been provided and execabort error occured', function (done) { var multi = client.multi(); - multi.config("bar"); - multi.set("foo"); + multi.config('bar'); + multi.set('foo'); multi.exec(); - client.on('error', function(err) { - assert.equal(err.code, "EXECABORT"); + client.on('error', function (err) { + assert.equal(err.code, 'EXECABORT'); done(); }); }); - it("should work without any callback", function (done) { + it('should work without any callback', function (done) { var multi = client.multi(); - multi.set("baz", "binary"); - multi.set("foo", "bar"); + multi.set('baz', 'binary'); + multi.set('foo', 'bar'); multi.exec(); client.get('foo', helper.isString('bar', done)); }); - it("should not use a transaction with exec_atomic if no command is used", function () { + it('should not use a transaction with exec_atomic if no command is used', function () { var multi = client.multi(); var test = false; multi.exec_batch = function () { @@ -569,30 +563,30 @@ describe("The 'multi' method", function () { assert(test); }); - it("should not use a transaction with exec_atomic if only one command is used", function () { + it('should not use a transaction with exec_atomic if only one command is used', function () { var multi = client.multi(); var test = false; multi.exec_batch = function () { test = true; }; - multi.set("baz", "binary"); + multi.set('baz', 'binary'); multi.exec_atomic(); assert(test); }); - it("should use transaction with exec_atomic and more than one command used", function (done) { + it('should use transaction with exec_atomic and more than one command used', function (done) { var multi = client.multi(); var test = false; multi.exec_batch = function () { test = true; }; - multi.set("baz", "binary"); + multi.set('baz', 'binary'); multi.get('baz'); multi.exec_atomic(done); assert(!test); }); - it("do not mutate arguments in the multi constructor", function (done) { + it('do not mutate arguments in the multi constructor', function (done) { var input = [['set', 'foo', 'bar'], ['get', 'foo']]; client.multi(input).exec(function (err, res) { assert.strictEqual(input.length, 2); @@ -602,7 +596,7 @@ describe("The 'multi' method", function () { }); }); - it("works properly after a reconnect. issue #897", function (done) { + it('works properly after a reconnect. issue #897', function (done) { client.stream.destroy(); client.on('error', function (err) { assert.strictEqual(err.code, 'ECONNREFUSED'); @@ -616,13 +610,13 @@ describe("The 'multi' method", function () { }); }); - it("emits error once if reconnecting after multi has been executed but not yet returned without callback", function (done) { - client.on('error', function(err) { + it('emits error once if reconnecting after multi has been executed but not yet returned without callback', function (done) { + client.on('error', function (err) { assert.strictEqual(err.code, 'UNCERTAIN_STATE'); done(); }); - client.multi().set("foo", 'bar').get('foo').exec(); + client.multi().set('foo', 'bar').get('foo').exec(); // Abort connection before the value returned client.stream.destroy(); }); diff --git a/test/node_redis.spec.js b/test/node_redis.spec.js index 11b35e6dc4b..45f523c4eba 100644 --- a/test/node_redis.spec.js +++ b/test/node_redis.spec.js @@ -1,33 +1,33 @@ 'use strict'; -var assert = require("assert"); -var config = require("./lib/config"); +var assert = require('assert'); +var config = require('./lib/config'); var helper = require('./helper'); var utils = require('../lib/utils'); -var fork = require("child_process").fork; +var fork = require('child_process').fork; var redis = config.redis; -describe("The node_redis client", function () { +describe('The node_redis client', function () { - helper.allTests(function(parser, ip, args) { + helper.allTests(function (parser, ip, args) { - describe("using " + parser + " and " + ip, function () { + describe('using ' + parser + ' and ' + ip, function () { var client; afterEach(function () { client.end(true); }); - describe("when connected", function () { + describe('when connected', function () { beforeEach(function (done) { client = redis.createClient.apply(null, args); - client.once("connect", function () { + client.once('connect', function () { client.flushdb(done); }); }); describe('duplicate', function () { - it('check if all options got copied properly', function(done) { + it('check if all options got copied properly', function (done) { client.selected_db = 2; var client2 = client.duplicate(); assert.strictEqual(client2.selected_db, 2); @@ -44,7 +44,7 @@ describe("The node_redis client", function () { }); }); - it('check if all new options replaced the old ones', function(done) { + it('check if all new options replaced the old ones', function (done) { var client2 = client.duplicate({ no_ready_check: true }); @@ -70,7 +70,7 @@ describe("The node_redis client", function () { describe('big data', function () { // Check if the fast mode for big strings is working correct - it('safe strings that are bigger than 30000 characters', function(done) { + it('safe strings that are bigger than 30000 characters', function (done) { var str = 'foo ಠ_ಠ bar '; while (str.length < 111111) { str += str; @@ -82,7 +82,7 @@ describe("The node_redis client", function () { }); }); - it('safe strings that are bigger than 30000 characters with multi', function(done) { + it('safe strings that are bigger than 30000 characters with multi', function (done) { var str = 'foo ಠ_ಠ bar '; while (str.length < 111111) { str += str; @@ -107,24 +107,24 @@ describe("The node_redis client", function () { }); }); - describe("send_command", function () { + describe('send_command', function () { - it("omitting args should be fine in some cases", function (done) { - client.send_command("info", undefined, function(err, res) { + it('omitting args should be fine in some cases', function (done) { + client.send_command('info', undefined, function (err, res) { assert(/redis_version/.test(res)); done(); }); }); - it("using another type as cb should just work as if there were no callback parameter", function (done) { + it('using another type as cb should just work as if there were no callback parameter', function (done) { client.send_command('set', ['test', 'bla'], [true]); - client.get('test', function(err, res) { + client.get('test', function (err, res) { assert.equal(res, 'bla'); done(); }); }); - it("misusing the function should eventually throw (no command)", function (done) { + it('misusing the function should eventually throw (no command)', function (done) { client.send_command(true, 'info', function (err, res) { assert(/ERR Protocol error/.test(err.message)); assert.equal(err.command, undefined); @@ -133,8 +133,8 @@ describe("The node_redis client", function () { }); }); - it("misusing the function should eventually throw (wrong args)", function (done) { - client.send_command('info', false, function(err, res) { + it('misusing the function should eventually throw (wrong args)', function (done) { + client.send_command('info', false, function (err, res) { assert.equal(err.message, 'ERR Protocol error: invalid multibulk length'); done(); }); @@ -142,38 +142,38 @@ describe("The node_redis client", function () { }); - describe("retry_unfulfilled_commands", function () { + describe('retry_unfulfilled_commands', function () { - it("should retry all commands instead of returning an error if a command did not yet return after a connection loss", function (done) { + it('should retry all commands instead of returning an error if a command did not yet return after a connection loss', function (done) { var bclient = redis.createClient({ parser: parser, retry_unfulfilled_commands: true }); - bclient.blpop("blocking list 2", 5, function (err, value) { - assert.strictEqual(value[0], "blocking list 2"); - assert.strictEqual(value[1], "initial value"); + bclient.blpop('blocking list 2', 5, function (err, value) { + assert.strictEqual(value[0], 'blocking list 2'); + assert.strictEqual(value[1], 'initial value'); return done(err); }); bclient.once('ready', function () { setTimeout(function () { bclient.stream.destroy(); - client.rpush("blocking list 2", "initial value", helper.isNumber(1)); + client.rpush('blocking list 2', 'initial value', helper.isNumber(1)); }, 100); }); }); }); - describe(".end", function () { + describe('.end', function () { - it('used without flush / flush set to false', function(done) { + it('used without flush / flush set to false', function (done) { var finished = false; - var end = helper.callFuncAfter(function() { + var end = helper.callFuncAfter(function () { if (!finished) { done(new Error('failed')); } }, 20); - var cb = function(err, res) { + var cb = function (err, res) { assert(/The connection has already been closed/.test(err.message)); end(); }; @@ -189,11 +189,11 @@ describe("The node_redis client", function () { }, 250); }); - it('used with flush set to true', function(done) { - var end = helper.callFuncAfter(function() { + it('used with flush set to true', function (done) { + var end = helper.callFuncAfter(function () { done(); }, 20); - var cb = function(err, res) { + var cb = function (err, res) { assert(/The connection has already been closed./.test(err.message)); end(); }; @@ -207,16 +207,16 @@ describe("The node_redis client", function () { }); - describe("commands after using .quit should fail", function () { + describe('commands after using .quit should fail', function () { - it("return an error in the callback", function (done) { + it('return an error in the callback', function (done) { if (helper.redisProcess().spawnFailed()) this.skip(); // TODO: Investigate why this test is failing hard and killing mocha if using '/tmp/redis.sock'. // Seems like something is wrong with nyc while passing a socket connection to create client! client = redis.createClient(); - client.quit(function() { - client.get("foo", function(err, res) { + client.quit(function () { + client.get('foo', function (err, res) { assert(err.message.indexOf('Redis connection gone') !== -1); assert.strictEqual(client.offline_queue.length, 0); done(); @@ -224,12 +224,12 @@ describe("The node_redis client", function () { }); }); - it("return an error in the callback version two", function (done) { + it('return an error in the callback version two', function (done) { if (helper.redisProcess().spawnFailed()) this.skip(); client.quit(); - setTimeout(function() { - client.get("foo", function(err, res) { + setTimeout(function () { + client.get('foo', function (err, res) { assert.strictEqual(err.message, 'GET can\'t be processed. The connection has already been closed.'); assert.strictEqual(err.command, 'GET'); assert.strictEqual(client.offline_queue.length, 0); @@ -238,64 +238,64 @@ describe("The node_redis client", function () { }, 100); }); - it("emit an error", function (done) { + it('emit an error', function (done) { if (helper.redisProcess().spawnFailed()) this.skip(); client.quit(); - client.on('error', function(err) { + client.on('error', function (err) { assert.strictEqual(err.message, 'SET can\'t be processed. The connection has already been closed.'); assert.strictEqual(err.command, 'SET'); assert.strictEqual(client.offline_queue.length, 0); done(); }); - setTimeout(function() { + setTimeout(function () { client.set('foo', 'bar'); }, 50); }); }); - describe("when redis closes unexpectedly", function () { - it("reconnects and can retrieve the pre-existing data", function (done) { - client.on("reconnecting", function on_recon(params) { - client.on("connect", function on_connect() { + describe('when redis closes unexpectedly', function () { + it('reconnects and can retrieve the pre-existing data', function (done) { + client.on('reconnecting', function on_recon (params) { + client.on('connect', function on_connect () { var end = helper.callFuncAfter(function () { - client.removeListener("connect", on_connect); - client.removeListener("reconnecting", on_recon); + client.removeListener('connect', on_connect); + client.removeListener('reconnecting', on_recon); assert.strictEqual(client.server_info.db0.keys, 2); assert.strictEqual(Object.keys(client.server_info.db0).length, 3); done(); }, 4); - client.get("recon 1", helper.isString("one", end)); - client.get("recon 1", helper.isString("one", end)); - client.get("recon 2", helper.isString("two", end)); - client.get("recon 2", helper.isString("two", end)); + client.get('recon 1', helper.isString('one', end)); + client.get('recon 1', helper.isString('one', end)); + client.get('recon 2', helper.isString('two', end)); + client.get('recon 2', helper.isString('two', end)); }); }); - client.set("recon 1", "one"); - client.set("recon 2", "two", function (err, res) { + client.set('recon 1', 'one'); + client.set('recon 2', 'two', function (err, res) { // Do not do this in normal programs. This is to simulate the server closing on us. // For orderly shutdown in normal programs, do client.quit() client.stream.destroy(); }); }); - it("reconnects properly when monitoring", function (done) { - client.on("reconnecting", function on_recon(params) { - client.on("ready", function on_ready() { - assert.strictEqual(client.monitoring, true, "monitoring after reconnect"); - client.removeListener("ready", on_ready); - client.removeListener("reconnecting", on_recon); + it('reconnects properly when monitoring', function (done) { + client.on('reconnecting', function on_recon (params) { + client.on('ready', function on_ready () { + assert.strictEqual(client.monitoring, true, 'monitoring after reconnect'); + client.removeListener('ready', on_ready); + client.removeListener('reconnecting', on_recon); done(); }); }); - assert.strictEqual(client.monitoring, false, "monitoring off at start"); - client.set("recon 1", "one"); + assert.strictEqual(client.monitoring, false, 'monitoring off at start'); + client.set('recon 1', 'one'); client.monitor(function (err, res) { - assert.strictEqual(client.monitoring, true, "monitoring on after monitor()"); - client.set("recon 2", "two", function (err, res) { + assert.strictEqual(client.monitoring, true, 'monitoring on after monitor()'); + client.set('recon 2', 'two', function (err, res) { // Do not do this in normal programs. This is to simulate the server closing on us. // For orderly shutdown in normal programs, do client.quit() client.stream.destroy(); @@ -305,40 +305,40 @@ describe("The node_redis client", function () { describe("and it's subscribed to a channel", function () { // "Connection in subscriber mode, only subscriber commands may be used" - it("reconnects, unsubscribes, and can retrieve the pre-existing data", function (done) { - client.on("ready", function on_connect() { + it('reconnects, unsubscribes, and can retrieve the pre-existing data', function (done) { + client.on('ready', function on_connect () { client.unsubscribe(helper.isNotError()); client.on('unsubscribe', function (channel, count) { // we should now be out of subscriber mode. - assert.strictEqual(channel, "recon channel"); + assert.strictEqual(channel, 'recon channel'); assert.strictEqual(count, 0); client.set('foo', 'bar', helper.isString('OK', done)); }); }); - client.set("recon 1", "one"); - client.subscribe("recon channel", function (err, res) { + client.set('recon 1', 'one'); + client.subscribe('recon channel', function (err, res) { // Do not do this in normal programs. This is to simulate the server closing on us. // For orderly shutdown in normal programs, do client.quit() client.stream.destroy(); }); }); - it("reconnects, unsubscribes, and can retrieve the pre-existing data of a explicit channel", function (done) { - client.on("ready", function on_connect() { + it('reconnects, unsubscribes, and can retrieve the pre-existing data of a explicit channel', function (done) { + client.on('ready', function on_connect () { client.unsubscribe('recon channel', helper.isNotError()); client.on('unsubscribe', function (channel, count) { // we should now be out of subscriber mode. - assert.strictEqual(channel, "recon channel"); + assert.strictEqual(channel, 'recon channel'); assert.strictEqual(count, 0); client.set('foo', 'bar', helper.isString('OK', done)); }); }); - client.set("recon 1", "one"); - client.subscribe("recon channel", function (err, res) { + client.set('recon 1', 'one'); + client.subscribe('recon channel', function (err, res) { // Do not do this in normal programs. This is to simulate the server closing on us. // For orderly shutdown in normal programs, do client.quit() client.stream.destroy(); @@ -393,10 +393,10 @@ describe("The node_redis client", function () { monitorClient.flushdb(); monitorClient.monitor(function (err, res) { assert.strictEqual(res, 'OK'); - client.mget("some", "keys", "foo", "bar"); - client.set("json", JSON.stringify({ - foo: "123", - bar: "sdflkdfsjk", + client.mget('some', 'keys', 'foo', 'bar'); + client.set('json', JSON.stringify({ + foo: '123', + bar: 'sdflkdfsjk', another: false })); monitorClient.get('baz', function (err, res) { @@ -420,7 +420,7 @@ describe("The node_redis client", function () { }); }); - monitorClient.on("monitor", function (time, args, rawOutput) { + monitorClient.on('monitor', function (time, args, rawOutput) { responses.push(args); assert(utils.monitor_regex.test(rawOutput), rawOutput); if (responses.length === 6) { @@ -442,10 +442,10 @@ describe("The node_redis client", function () { monitorClient.MONITOR(function (err, res) { assert.strictEqual(res.inspect(), new Buffer('OK').inspect()); - client.mget("hello", new Buffer('world')); + client.mget('hello', new Buffer('world')); }); - monitorClient.on("monitor", function (time, args, rawOutput) { + monitorClient.on('monitor', function (time, args, rawOutput) { assert.strictEqual(typeof rawOutput, 'string'); assert(utils.monitor_regex.test(rawOutput), rawOutput); assert.deepEqual(args, ['mget', 'hello', 'world']); @@ -457,8 +457,8 @@ describe("The node_redis client", function () { it('monitors reconnects properly and works with the offline queue', function (done) { var i = 0; client.MONITOR(helper.isString('OK')); - client.mget("hello", 'world'); - client.on("monitor", function (time, args, rawOutput) { + client.mget('hello', 'world'); + client.on('monitor', function (time, args, rawOutput) { assert(utils.monitor_regex.test(rawOutput), rawOutput); assert.deepEqual(args, ['mget', 'hello', 'world']); if (i++ === 2) { @@ -466,7 +466,7 @@ describe("The node_redis client", function () { return done(); } client.stream.destroy(); - client.mget("hello", 'world'); + client.mget('hello', 'world'); }); }); @@ -490,7 +490,7 @@ describe("The node_redis client", function () { client.unsubscribe('baz'); }, 150); var called = false; - client.on("monitor", function (time, args, rawOutput) { + client.on('monitor', function (time, args, rawOutput) { responses.push(args); assert(utils.monitor_regex.test(rawOutput), rawOutput); if (responses.length === 7) { @@ -531,7 +531,7 @@ describe("The node_redis client", function () { end(); }); client.on('idle', function onIdle () { - client.removeListener("idle", onIdle); + client.removeListener('idle', onIdle); client.get('foo', helper.isString('bar', end)); }); client.set('foo', 'bar'); @@ -540,12 +540,12 @@ describe("The node_redis client", function () { describe('utf8', function () { it('handles utf-8 keys', function (done) { - var utf8_sample = "ಠ_ಠ"; - client.set(["utf8test", utf8_sample], helper.isString("OK")); - client.get(["utf8test"], function (err, obj) { - assert.strictEqual(utf8_sample, obj); - return done(err); - }); + var utf8_sample = 'ಠ_ಠ'; + client.set(['utf8test', utf8_sample], helper.isString('OK')); + client.get(['utf8test'], function (err, obj) { + assert.strictEqual(utf8_sample, obj); + return done(err); + }); }); }); }); @@ -554,14 +554,14 @@ describe("The node_redis client", function () { it('exits subprocess as soon as final command is processed', function (done) { this.timeout(12000); var args = config.HOST[ip] ? [config.HOST[ip], config.PORT] : [ip]; - var external = fork("./test/lib/unref.js", args); + var external = fork('./test/lib/unref.js', args); var id = setTimeout(function () { external.kill(); return done(Error('unref subprocess timed out')); }, 8000); - external.on("close", function (code) { + external.on('close', function (code) { clearTimeout(id); assert.strictEqual(code, 0); return done(); @@ -612,7 +612,7 @@ describe("The node_redis client", function () { it("fires client.on('ready')", function (done) { client = redis.createClient.apply(null, args); - client.on("ready", function () { + client.on('ready', function () { assert.strictEqual(true, client.options.socket_nodelay); client.quit(); @@ -624,12 +624,12 @@ describe("The node_redis client", function () { it('client is functional', function (done) { client = redis.createClient.apply(null, args); - client.on("ready", function () { + client.on('ready', function () { assert.strictEqual(true, client.options.socket_nodelay); - client.set(["set key 1", "set val"], helper.isString("OK")); - client.set(["set key 2", "set val"], helper.isString("OK")); - client.get(["set key 1"], helper.isString("set val")); - client.get(["set key 2"], helper.isString("set val")); + client.set(['set key 1', 'set val'], helper.isString('OK')); + client.set(['set key 2', 'set val'], helper.isString('OK')); + client.get(['set key 1'], helper.isString('set val')); + client.get(['set key 2'], helper.isString('set val')); client.quit(); client.once('end', function () { @@ -646,7 +646,7 @@ describe("The node_redis client", function () { it("fires client.on('ready')", function (done) { client = redis.createClient.apply(null, args); - client.on("ready", function () { + client.on('ready', function () { assert.strictEqual(false, client.options.socket_nodelay); client.quit(); @@ -658,12 +658,12 @@ describe("The node_redis client", function () { it('client is functional', function (done) { client = redis.createClient.apply(null, args); - client.on("ready", function () { + client.on('ready', function () { assert.strictEqual(false, client.options.socket_nodelay); - client.set(["set key 1", "set val"], helper.isString("OK")); - client.set(["set key 2", "set val"], helper.isString("OK")); - client.get(["set key 1"], helper.isString("set val")); - client.get(["set key 2"], helper.isString("set val")); + client.set(['set key 1', 'set val'], helper.isString('OK')); + client.set(['set key 2', 'set val'], helper.isString('OK')); + client.get(['set key 1'], helper.isString('set val')); + client.get(['set key 2'], helper.isString('set val')); client.quit(); client.once('end', function () { @@ -677,7 +677,7 @@ describe("The node_redis client", function () { it("fires client.on('ready')", function (done) { client = redis.createClient.apply(null, args); - client.on("ready", function () { + client.on('ready', function () { assert.strictEqual(true, client.options.socket_nodelay); client.quit(); @@ -689,12 +689,12 @@ describe("The node_redis client", function () { it('client is functional', function (done) { client = redis.createClient.apply(null, args); - client.on("ready", function () { + client.on('ready', function () { assert.strictEqual(true, client.options.socket_nodelay); - client.set(["set key 1", "set val"], helper.isString("OK")); - client.set(["set key 2", "set val"], helper.isString("OK")); - client.get(["set key 1"], helper.isString("set val")); - client.get(["set key 2"], helper.isString("set val")); + client.set(['set key 1', 'set val'], helper.isString('OK')); + client.set(['set key 2', 'set val'], helper.isString('OK')); + client.get(['set key 1'], helper.isString('set val')); + client.get(['set key 2'], helper.isString('set val')); client.quit(); client.once('end', function () { @@ -706,14 +706,14 @@ describe("The node_redis client", function () { }); describe('retry_max_delay', function () { - it("sets upper bound on how long client waits before reconnecting", function (done) { + it('sets upper bound on how long client waits before reconnecting', function (done) { var time; var timeout = process.platform !== 'win32' ? 20 : 100; client = redis.createClient.apply(null, config.configureClient(parser, ip, { retry_max_delay: 1 // ms })); - client.on('ready', function() { + client.on('ready', function () { if (this.times_connected === 1) { this.stream.end(); time = Date.now(); @@ -735,9 +735,9 @@ describe("The node_redis client", function () { describe('protocol error', function () { - it("should gracefully recover and only fail on the already send commands", function (done) { + it('should gracefully recover and only fail on the already send commands', function (done) { client = redis.createClient.apply(null, args); - client.on('error', function(err) { + client.on('error', function (err) { assert.strictEqual(err.message, 'Protocol error, got "a" as reply type byte'); // After the hard failure work properly again. The set should have been processed properly too client.get('foo', function (err, res) { @@ -757,7 +757,7 @@ describe("The node_redis client", function () { describe('enable_offline_queue', function () { describe('true', function () { - it("should emit drain if offline queue is flushed and nothing to buffer", function (done) { + it('should emit drain if offline queue is flushed and nothing to buffer', function (done) { client = redis.createClient({ parser: parser, no_ready_check: true @@ -773,33 +773,32 @@ describe("The node_redis client", function () { ); end(); }); - client.on('drain', function() { + client.on('drain', function () { assert(client.offline_queue.length === 0); end(); }); }); - it("does not return an error and enqueues operation", function (done) { + it('does not return an error and enqueues operation', function (done) { client = redis.createClient(9999, null, { max_attempts: 0, parser: parser }); var finished = false; - client.on('error', function(e) { + client.on('error', function (e) { // ignore, b/c expecting a "can't connect" error }); - return setTimeout(function() { - client.set('foo', 'bar', function(err, result) { + return setTimeout(function () { + client.set('foo', 'bar', function (err, result) { if (!finished) { // This should never be called return done(err); - } else { - assert.strictEqual(err.message, "The command can't be processed. The connection has already been closed."); } + assert.strictEqual(err.message, "The command can't be processed. The connection has already been closed."); }); - return setTimeout(function() { + return setTimeout(function () { assert.strictEqual(client.offline_queue.length, 1); finished = true; return done(); @@ -807,14 +806,14 @@ describe("The node_redis client", function () { }, 50); }); - it("enqueues operation and keep the queue while trying to reconnect", function (done) { + it('enqueues operation and keep the queue while trying to reconnect', function (done) { client = redis.createClient(9999, null, { max_attempts: 4, parser: parser }); var i = 0; - client.on('error', function(err) { + client.on('error', function (err) { if (err.message === 'Redis connection in broken state: maximum connection attempts exceeded.') { assert(i, 3); assert.strictEqual(client.offline_queue.length, 0); @@ -826,7 +825,7 @@ describe("The node_redis client", function () { } }); - client.on('reconnecting', function(params) { + client.on('reconnecting', function (params) { i++; assert.equal(params.attempt, i); assert.strictEqual(params.times_connected, 0); @@ -837,35 +836,35 @@ describe("The node_redis client", function () { // Should work with either a callback or without client.set('baz', 13); - client.set('foo', 'bar', function(err, result) { + client.set('foo', 'bar', function (err, result) { assert(i, 3); assert(err); assert.strictEqual(client.offline_queue.length, 0); }); }); - it("flushes the command queue if connection is lost", function (done) { + it('flushes the command queue if connection is lost', function (done) { client = redis.createClient({ parser: parser }); - client.once('ready', function() { + client.once('ready', function () { var multi = client.multi(); - multi.config("bar"); - var cb = function(err, reply) { + multi.config('bar'); + var cb = function (err, reply) { assert.equal(err.code, 'UNCERTAIN_STATE'); }; for (var i = 0; i < 12; i += 3) { - client.set("foo" + i, "bar" + i); - multi.set("foo" + (i + 1), "bar" + (i + 1), cb); - multi.set("foo" + (i + 2), "bar" + (i + 2)); + client.set('foo' + i, 'bar' + i); + multi.set('foo' + (i + 1), 'bar' + (i + 1), cb); + multi.set('foo' + (i + 2), 'bar' + (i + 2)); } multi.exec(); assert.equal(client.command_queue.length, 15); helper.killConnection(client); }); - client.on('error', function(err) { + client.on('error', function (err) { if (/uncertain state/.test(err.message)) { assert.equal(client.command_queue.length, 0); done(); @@ -880,7 +879,7 @@ describe("The node_redis client", function () { describe('false', function () { - it('stream not writable', function(done) { + it('stream not writable', function (done) { client = redis.createClient({ parser: parser, enable_offline_queue: false @@ -894,7 +893,7 @@ describe("The node_redis client", function () { }); }); - it("emit an error and does not enqueues operation", function (done) { + it('emit an error and does not enqueues operation', function (done) { client = redis.createClient(9999, null, { parser: parser, max_attempts: 0, @@ -902,7 +901,7 @@ describe("The node_redis client", function () { }); var end = helper.callFuncAfter(done, 3); - client.on('error', function(err) { + client.on('error', function (err) { assert(/offline queue is deactivated|ECONNREFUSED/.test(err.message)); assert.equal(client.command_queue.length, 0); end(); @@ -919,30 +918,30 @@ describe("The node_redis client", function () { }); }); - it("flushes the command queue if connection is lost", function (done) { + it('flushes the command queue if connection is lost', function (done) { client = redis.createClient({ parser: parser, max_attempts: 2, enable_offline_queue: false }); - client.once('ready', function() { + client.once('ready', function () { var multi = client.multi(); - multi.config("bar"); - var cb = function(err, reply) { + multi.config('bar'); + var cb = function (err, reply) { assert.equal(err.code, 'UNCERTAIN_STATE'); }; for (var i = 0; i < 12; i += 3) { - client.set("foo" + i, "bar" + i); - multi.set("foo" + (i + 1), "bar" + (i + 1), cb); - multi.set("foo" + (i + 2), "bar" + (i + 2)); + client.set('foo' + i, 'bar' + i); + multi.set('foo' + (i + 1), 'bar' + (i + 1), cb); + multi.set('foo' + (i + 2), 'bar' + (i + 2)); } multi.exec(); assert.equal(client.command_queue.length, 15); helper.killConnection(client); }); - client.on('error', function(err) { + client.on('error', function (err) { if (err.code === 'UNCERTAIN_STATE') { assert.equal(client.command_queue.length, 0); done(); diff --git a/test/prefix.spec.js b/test/prefix.spec.js index 4c909f1af98..e34805a7a9b 100644 --- a/test/prefix.spec.js +++ b/test/prefix.spec.js @@ -1,18 +1,18 @@ 'use strict'; -var assert = require("assert"); -var config = require("./lib/config"); +var assert = require('assert'); +var config = require('./lib/config'); var helper = require('./helper'); var redis = config.redis; -describe("prefix key names", function () { +describe('prefix key names', function () { - helper.allTests(function(parser, ip, args) { + helper.allTests(function (parser, ip, args) { - describe("using " + parser + " and " + ip, function () { + describe('using ' + parser + ' and ' + ip, function () { var client = null; - beforeEach(function(done) { + beforeEach(function (done) { client = redis.createClient({ parser: parser, prefix: 'test:prefix:' @@ -28,14 +28,14 @@ describe("prefix key names", function () { client.end(true); }); - it("auto prefix set / get", function (done) { - client.set('key', 'value', function(err, reply) { + it('auto prefix set / get', function (done) { + client.set('key', 'value', function (err, reply) { assert.strictEqual(reply, 'OK'); }); - client.get('key', function(err, reply) { + client.get('key', function (err, reply) { assert.strictEqual(reply, 'value'); }); - client.getrange('key', 1, -1, function(err, reply) { + client.getrange('key', 1, -1, function (err, reply) { assert.strictEqual(reply, 'alue'); assert.strictEqual(err, null); }); @@ -56,15 +56,15 @@ describe("prefix key names", function () { }); }); - it("auto prefix set / get with .batch", function (done) { + it('auto prefix set / get with .batch', function (done) { var batch = client.batch(); - batch.set('key', 'value', function(err, reply) { + batch.set('key', 'value', function (err, reply) { assert.strictEqual(reply, 'OK'); }); - batch.get('key', function(err, reply) { + batch.get('key', function (err, reply) { assert.strictEqual(reply, 'value'); }); - batch.getrange('key', 1, -1, function(err, reply) { + batch.getrange('key', 1, -1, function (err, reply) { assert.strictEqual(reply, 'alue'); assert.strictEqual(err, null); }); @@ -85,15 +85,15 @@ describe("prefix key names", function () { batch.exec(done); }); - it("auto prefix set / get with .multi", function (done) { + it('auto prefix set / get with .multi', function (done) { var multi = client.multi(); - multi.set('key', 'value', function(err, reply) { + multi.set('key', 'value', function (err, reply) { assert.strictEqual(reply, 'OK'); }); - multi.get('key', function(err, reply) { + multi.get('key', function (err, reply) { assert.strictEqual(reply, 'value'); }); - multi.getrange('key', 1, -1, function(err, reply) { + multi.getrange('key', 1, -1, function (err, reply) { assert.strictEqual(reply, 'alue'); assert.strictEqual(err, null); }); diff --git a/test/pubsub.spec.js b/test/pubsub.spec.js index cefe33f5d04..63d61e01bb6 100644 --- a/test/pubsub.spec.js +++ b/test/pubsub.spec.js @@ -1,32 +1,32 @@ 'use strict'; -var assert = require("assert"); -var config = require("./lib/config"); -var helper = require("./helper"); +var assert = require('assert'); +var config = require('./lib/config'); +var helper = require('./helper'); var redis = config.redis; -describe("publish/subscribe", function () { +describe('publish/subscribe', function () { - helper.allTests(function(parser, ip, args) { + helper.allTests(function (parser, ip, args) { - describe("using " + parser + " and " + ip, function () { + describe('using ' + parser + ' and ' + ip, function () { var pub = null; var sub = null; - var channel = "test channel"; - var channel2 = "test channel 2"; - var message = "test message"; + var channel = 'test channel'; + var channel2 = 'test channel 2'; + var message = 'test message'; beforeEach(function (done) { var end = helper.callFuncAfter(done, 2); pub = redis.createClient.apply(redis.createClient, args); sub = redis.createClient.apply(redis.createClient, args); - pub.once("connect", function () { + pub.once('connect', function () { pub.flushdb(function () { end(); }); }); - sub.once("connect", function () { + sub.once('connect', function () { end(); }); }); @@ -37,14 +37,14 @@ describe("publish/subscribe", function () { sub = redis.createClient({ disable_resubscribing: true }); - sub.once("connect", function () { + sub.once('connect', function () { done(); }); }); it('does not fire subscribe events after reconnecting', function (done) { var a = false; - sub.on("subscribe", function (chnl, count) { + sub.on('subscribe', function (chnl, count) { if (chnl === channel2) { if (a) { return done(new Error('Test failed')); @@ -54,7 +54,7 @@ describe("publish/subscribe", function () { } }); - sub.on('reconnecting', function() { + sub.on('reconnecting', function () { a = true; sub.on('ready', function () { setTimeout(done, 250); @@ -68,7 +68,7 @@ describe("publish/subscribe", function () { describe('subscribe', function () { it('fires a subscribe event for each channel subscribed to even after reconnecting', function (done) { var a = false; - sub.on("subscribe", function (chnl, count) { + sub.on('subscribe', function (chnl, count) { if (chnl === channel2) { assert.equal(2, count); if (a) { @@ -78,7 +78,7 @@ describe("publish/subscribe", function () { } }); - sub.on('reconnecting', function() { + sub.on('reconnecting', function () { a = true; }); @@ -91,7 +91,7 @@ describe("publish/subscribe", function () { sub = redis.createClient({ detect_buffers: true }); - sub.on("subscribe", function (chnl, count) { + sub.on('subscribe', function (chnl, count) { if (chnl.inspect() === new Buffer([0xAA, 0xBB, 0x00, 0xF0]).inspect()) { assert.equal(1, count); if (a) { @@ -101,7 +101,7 @@ describe("publish/subscribe", function () { } }); - sub.on('reconnecting', function() { + sub.on('reconnecting', function () { a = true; }); @@ -110,14 +110,14 @@ describe("publish/subscribe", function () { it('receives messages on subscribed channel', function (done) { var end = helper.callFuncAfter(done, 2); - sub.on("subscribe", function (chnl, count) { + sub.on('subscribe', function (chnl, count) { pub.publish(channel, message, function (err, res) { helper.isNumber(1)(err, res); end(); }); }); - sub.on("message", function (chnl, msg) { + sub.on('message', function (chnl, msg) { assert.equal(chnl, channel); assert.equal(msg, message); end(); @@ -128,14 +128,14 @@ describe("publish/subscribe", function () { it('receives messages if subscribe is called after unsubscribe', function (done) { var end = helper.callFuncAfter(done, 2); - sub.once("subscribe", function (chnl, count) { + sub.once('subscribe', function (chnl, count) { pub.publish(channel, message, function (err, res) { helper.isNumber(1)(err, res); end(); }); }); - sub.on("message", function (chnl, msg) { + sub.on('message', function (chnl, msg) { assert.equal(chnl, channel); assert.equal(msg, message); end(); @@ -167,10 +167,10 @@ describe("publish/subscribe", function () { }); it('emits end event if quit is called from within subscribe', function (done) { - sub.on("end", function () { + sub.on('end', function () { return done(); }); - sub.on("subscribe", function (chnl, count) { + sub.on('subscribe', function (chnl, count) { sub.quit(); }); sub.subscribe(channel); @@ -186,45 +186,45 @@ describe("publish/subscribe", function () { When it resubscribes, c2 publishes the third message, on the second channel c1 gets the message and drops its connection. When it reconnects, the test ends. */ - sub.on("message", function(channel, message) { - if (channel === "chan1") { - assert.strictEqual(message, "hi on channel 1"); + sub.on('message', function (channel, message) { + if (channel === 'chan1') { + assert.strictEqual(message, 'hi on channel 1'); sub.stream.end(); - } else if (channel === "chan2") { - assert.strictEqual(message, "hi on channel 2"); + } else if (channel === 'chan2') { + assert.strictEqual(message, 'hi on channel 2'); sub.stream.end(); } else { sub.quit(); pub.quit(); - assert.fail("test failed"); + assert.fail('test failed'); } }); - sub.subscribe("chan1", "chan2"); + sub.subscribe('chan1', 'chan2'); - sub.on("ready", function(err, results) { + sub.on('ready', function (err, results) { count++; if (count === 1) { - pub.publish("chan1", "hi on channel 1"); + pub.publish('chan1', 'hi on channel 1'); return; } else if (count === 2) { - pub.publish("chan2", "hi on channel 2"); + pub.publish('chan2', 'hi on channel 2'); } else { - sub.quit(function() { - pub.quit(function() { + sub.quit(function () { + pub.quit(function () { return done(); }); }); } }); - pub.publish("chan1", "hi on channel 1"); + pub.publish('chan1', 'hi on channel 1'); }); }); - describe("multiple subscribe / unsubscribe commands", function () { + describe('multiple subscribe / unsubscribe commands', function () { - it("reconnects properly with pub sub and select command", function (done) { + it('reconnects properly with pub sub and select command', function (done) { var end = helper.callFuncAfter(done, 2); sub.select(3); sub.set('foo', 'bar'); @@ -240,7 +240,7 @@ describe("publish/subscribe", function () { }); }); - it("should not go into pubsub mode with unsubscribe commands", function (done) { + it('should not go into pubsub mode with unsubscribe commands', function (done) { sub.on('unsubscribe', function (msg) { // The unsubscribe should not be triggered, as there was no corresponding channel throw new Error('Test failed'); @@ -252,7 +252,7 @@ describe("publish/subscribe", function () { sub.del('foo', done); }); - it("handles multiple channels with the same channel name properly, even with buffers", function (done) { + it('handles multiple channels with the same channel name properly, even with buffers', function (done) { var channels = ['a', 'b', 'a', new Buffer('a'), 'c', 'b']; var subscribed_channels = [1, 2, 2, 2, 3, 3]; var i = 0; @@ -286,14 +286,14 @@ describe("publish/subscribe", function () { }); }); - it("unsubscribes, subscribes, unsubscribes... single and multiple entries mixed. Withouth callbacks", function (done) { - function subscribe(channels) { + it('unsubscribes, subscribes, unsubscribes... single and multiple entries mixed. Withouth callbacks', function (done) { + function subscribe (channels) { sub.unsubscribe(helper.isNull); sub.subscribe(channels, helper.isNull); } var all = false; var subscribeMsg = ['1', '3', '2', '5', 'test', 'bla']; - sub.on('subscribe', function(msg, count) { + sub.on('subscribe', function (msg, count) { subscribeMsg.splice(subscribeMsg.indexOf(msg), 1); if (subscribeMsg.length === 0 && all) { assert.strictEqual(count, 3); @@ -301,7 +301,7 @@ describe("publish/subscribe", function () { } }); var unsubscribeMsg = ['1', '3', '2']; - sub.on('unsubscribe', function(msg, count) { + sub.on('unsubscribe', function (msg, count) { unsubscribeMsg.splice(unsubscribeMsg.indexOf(msg), 1); if (unsubscribeMsg.length === 0) { assert.strictEqual(count, 0); @@ -314,14 +314,14 @@ describe("publish/subscribe", function () { subscribe(['5', 'test', 'bla']); }); - it("unsubscribes, subscribes, unsubscribes... single and multiple entries mixed. Without callbacks", function (done) { - function subscribe(channels) { + it('unsubscribes, subscribes, unsubscribes... single and multiple entries mixed. Without callbacks', function (done) { + function subscribe (channels) { sub.unsubscribe(); sub.subscribe(channels); } var all = false; var subscribeMsg = ['1', '3', '2', '5', 'test', 'bla']; - sub.on('subscribe', function(msg, count) { + sub.on('subscribe', function (msg, count) { subscribeMsg.splice(subscribeMsg.indexOf(msg), 1); if (subscribeMsg.length === 0 && all) { assert.strictEqual(count, 3); @@ -329,7 +329,7 @@ describe("publish/subscribe", function () { } }); var unsubscribeMsg = ['1', '3', '2']; - sub.on('unsubscribe', function(msg, count) { + sub.on('unsubscribe', function (msg, count) { unsubscribeMsg.splice(unsubscribeMsg.indexOf(msg), 1); if (unsubscribeMsg.length === 0) { assert.strictEqual(count, 0); @@ -342,15 +342,15 @@ describe("publish/subscribe", function () { subscribe(['5', 'test', 'bla']); }); - it("unsubscribes, subscribes, unsubscribes... single and multiple entries mixed. Without callback and concret channels", function (done) { - function subscribe(channels) { + it('unsubscribes, subscribes, unsubscribes... single and multiple entries mixed. Without callback and concret channels', function (done) { + function subscribe (channels) { sub.unsubscribe(channels); sub.unsubscribe(channels); sub.subscribe(channels); } var all = false; var subscribeMsg = ['1', '3', '2', '5', 'test', 'bla']; - sub.on('subscribe', function(msg, count) { + sub.on('subscribe', function (msg, count) { subscribeMsg.splice(subscribeMsg.indexOf(msg), 1); if (subscribeMsg.length === 0 && all) { assert.strictEqual(count, 6); @@ -358,7 +358,7 @@ describe("publish/subscribe", function () { } }); var unsubscribeMsg = ['1', '3', '2', '5', 'test', 'bla']; - sub.on('unsubscribe', function(msg, count) { + sub.on('unsubscribe', function (msg, count) { var pos = unsubscribeMsg.indexOf(msg); if (pos !== -1) unsubscribeMsg.splice(pos, 1); @@ -372,8 +372,8 @@ describe("publish/subscribe", function () { subscribe(['5', 'test', 'bla']); }); - it("unsubscribes, subscribes, unsubscribes... with pattern matching", function (done) { - function subscribe(channels, callback) { + it('unsubscribes, subscribes, unsubscribes... with pattern matching', function (done) { + function subscribe (channels, callback) { sub.punsubscribe('prefix:*', helper.isNull); sub.psubscribe(channels, function (err, res) { helper.isNull(err); @@ -383,7 +383,7 @@ describe("publish/subscribe", function () { var all = false; var end = helper.callFuncAfter(done, 8); var subscribeMsg = ['prefix:*', 'prefix:3', 'prefix:2', '5', 'test:a', 'bla']; - sub.on('psubscribe', function(msg, count) { + sub.on('psubscribe', function (msg, count) { subscribeMsg.splice(subscribeMsg.indexOf(msg), 1); if (subscribeMsg.length === 0) { assert.strictEqual(count, 5); @@ -392,7 +392,7 @@ describe("publish/subscribe", function () { }); var rest = 1; var unsubscribeMsg = ['prefix:*', 'prefix:*', 'prefix:*', '*']; - sub.on('punsubscribe', function(msg, count) { + sub.on('punsubscribe', function (msg, count) { unsubscribeMsg.splice(unsubscribeMsg.indexOf(msg), 1); if (all) { assert.strictEqual(unsubscribeMsg.length, 0); @@ -434,13 +434,13 @@ describe("publish/subscribe", function () { describe('unsubscribe', function () { it('fires an unsubscribe event', function (done) { - sub.on("subscribe", function (chnl, count) { + sub.on('subscribe', function (chnl, count) { sub.unsubscribe(channel); }); sub.subscribe(channel); - sub.on("unsubscribe", function (chnl, count) { + sub.on('unsubscribe', function (chnl, count) { assert.equal(chnl, channel); assert.strictEqual(count, 0); return done(); @@ -448,14 +448,14 @@ describe("publish/subscribe", function () { }); it('puts client back into write mode', function (done) { - sub.on("subscribe", function (chnl, count) { + sub.on('subscribe', function (chnl, count) { sub.unsubscribe(channel); }); sub.subscribe(channel); - sub.on("unsubscribe", function (chnl, count) { - pub.incr("foo", helper.isNumber(1, done)); + sub.on('unsubscribe', function (chnl, count) { + pub.incr('foo', helper.isNumber(1, done)); }); }); @@ -484,7 +484,7 @@ describe("publish/subscribe", function () { sub2.on('ready', function () { sub2.psubscribe('*'); sub2.subscribe('/foo'); - sub2.on("pmessage", function(pattern, channel, message) { + sub2.on('pmessage', function (pattern, channel, message) { assert.strictEqual(pattern.inspect(), new Buffer('*').inspect()); assert.strictEqual(channel.inspect(), new Buffer('/foo').inspect()); assert.strictEqual(message.inspect(), new Buffer('hello world').inspect()); @@ -539,10 +539,10 @@ describe("publish/subscribe", function () { }); }); - it("should not publish a message multiple times per command", function (done) { + it('should not publish a message multiple times per command', function (done) { var published = {}; - function subscribe(message) { + function subscribe (message) { sub.removeAllListeners('subscribe'); sub.removeAllListeners('message'); sub.removeAllListeners('unsubscribe'); @@ -572,7 +572,7 @@ describe("publish/subscribe", function () { }, 40); }); - it("should not publish a message without any publish command", function (done) { + it('should not publish a message without any publish command', function (done) { pub.set('foo', 'message'); pub.set('bar', 'hello'); pub.mget('foo', 'bar'); diff --git a/test/rename.spec.js b/test/rename.spec.js index 0d3aa0cc19e..b98661e9f5f 100644 --- a/test/rename.spec.js +++ b/test/rename.spec.js @@ -1,7 +1,7 @@ 'use strict'; -var assert = require("assert"); -var config = require("./lib/config"); +var assert = require('assert'); +var config = require('./lib/config'); var helper = require('./helper'); var redis = config.redis; @@ -10,19 +10,19 @@ if (process.platform === 'win32') { return; } -describe("rename commands", function () { +describe('rename commands', function () { before(function (done) { helper.stopRedis(function () { helper.startRedis('./conf/rename.conf', done); }); }); - helper.allTests(function(parser, ip, args) { + helper.allTests(function (parser, ip, args) { - describe("using " + parser + " and " + ip, function () { + describe('using ' + parser + ' and ' + ip, function () { var client = null; - beforeEach(function(done) { + beforeEach(function (done) { if (helper.redisProcess().spawnFailed()) return done(); client = redis.createClient({ rename_commands: { @@ -42,27 +42,27 @@ describe("rename commands", function () { client.end(true); }); - it("allows to use renamed functions", function (done) { + it('allows to use renamed functions', function (done) { if (helper.redisProcess().spawnFailed()) this.skip(); - client.set('key', 'value', function(err, reply) { + client.set('key', 'value', function (err, reply) { assert.strictEqual(reply, 'OK'); }); - client.get('key', function(err, reply) { + client.get('key', function (err, reply) { assert.strictEqual(err.message, "ERR unknown command 'get'"); assert.strictEqual(err.command, 'GET'); assert.strictEqual(reply, undefined); }); - client.getrange('key', 1, -1, function(err, reply) { + client.getrange('key', 1, -1, function (err, reply) { assert.strictEqual(reply, 'alue'); assert.strictEqual(err, null); done(); }); }); - it("should also work with batch", function (done) { + it('should also work with batch', function (done) { if (helper.redisProcess().spawnFailed()) this.skip(); client.batch([['set', 'key', 'value']]).exec(function (err, res) { @@ -79,7 +79,7 @@ describe("rename commands", function () { }); }); - it("should also work with multi", function (done) { + it('should also work with multi', function (done) { if (helper.redisProcess().spawnFailed()) this.skip(); client.multi([['set', 'key', 'value']]).exec(function (err, res) { @@ -96,12 +96,12 @@ describe("rename commands", function () { }); }); - it("should also work with multi and abort transaction", function (done) { + it('should also work with multi and abort transaction', function (done) { if (helper.redisProcess().spawnFailed()) this.skip(); var multi = client.multi(); multi.get('key'); - multi.getrange('key', 1, -1, function(err, reply) { + multi.getrange('key', 1, -1, function (err, reply) { assert.strictEqual(reply, 'alue'); assert.strictEqual(err, null); }); @@ -116,7 +116,7 @@ describe("rename commands", function () { }); }); - it("should also work prefixed commands", function (done) { + it('should also work prefixed commands', function (done) { if (helper.redisProcess().spawnFailed()) this.skip(); client.end(true); @@ -128,7 +128,7 @@ describe("rename commands", function () { prefix: 'baz' }); client.set('foo', 'bar'); - client.keys('*', function(err, reply) { + client.keys('*', function (err, reply) { assert.strictEqual(reply[0], 'bazfoo'); assert.strictEqual(err, null); done(); diff --git a/test/return_buffers.spec.js b/test/return_buffers.spec.js index ab12668c595..1274c8be9e8 100644 --- a/test/return_buffers.spec.js +++ b/test/return_buffers.spec.js @@ -1,15 +1,15 @@ 'use strict'; -var assert = require("assert"); -var config = require("./lib/config"); +var assert = require('assert'); +var config = require('./lib/config'); var helper = require('./helper'); var redis = config.redis; -describe("return_buffers", function () { +describe('return_buffers', function () { - helper.allTests(function(parser, ip, basicArgs) { + helper.allTests(function (parser, ip, basicArgs) { - describe("using " + parser + " and " + ip, function () { + describe('using ' + parser + ' and ' + ip, function () { var client; var args = config.configureClient(parser, ip, { return_buffers: true, @@ -30,11 +30,11 @@ describe("return_buffers", function () { assert.strictEqual(msg, 'WARNING: You activated return_buffers and detect_buffers at the same time. The return value is always going to be a buffer.'); end(); }); - client.once("error", done); - client.once("connect", function () { + client.once('error', done); + client.once('connect', function () { client.flushdb(function (err) { - client.hmset("hash key 2", "key 1", "val 1", "key 2", "val 2"); - client.set("string key 1", "string value"); + client.hmset('hash key 2', 'key 1', 'val 1', 'key 2', 'val 2'); + client.set('string key 1', 'string value'); end(err); }); }); @@ -43,18 +43,18 @@ describe("return_buffers", function () { describe('get', function () { describe('first argument is a string', function () { it('returns a buffer', function (done) { - client.get("string key 1", function (err, reply) { + client.get('string key 1', function (err, reply) { assert.strictEqual(true, Buffer.isBuffer(reply)); - assert.strictEqual("", reply.inspect()); + assert.strictEqual('', reply.inspect()); return done(err); }); }); it('returns a bufffer when executed as part of transaction', function (done) { - client.multi().get("string key 1").exec(function (err, reply) { + client.multi().get('string key 1').exec(function (err, reply) { assert.strictEqual(1, reply.length); assert.strictEqual(true, Buffer.isBuffer(reply[0])); - assert.strictEqual("", reply[0].inspect()); + assert.strictEqual('', reply[0].inspect()); return done(err); }); }); @@ -64,20 +64,20 @@ describe("return_buffers", function () { describe('multi.hget', function () { it('returns buffers', function (done) { client.multi() - .hget("hash key 2", "key 1") - .hget(new Buffer("hash key 2"), "key 1") - .hget("hash key 2", new Buffer("key 2")) - .hget("hash key 2", "key 2") + .hget('hash key 2', 'key 1') + .hget(new Buffer('hash key 2'), 'key 1') + .hget('hash key 2', new Buffer('key 2')) + .hget('hash key 2', 'key 2') .exec(function (err, reply) { assert.strictEqual(true, Array.isArray(reply)); assert.strictEqual(4, reply.length); - assert.strictEqual("", reply[0].inspect()); + assert.strictEqual('', reply[0].inspect()); assert.strictEqual(true, Buffer.isBuffer(reply[1])); - assert.strictEqual("", reply[1].inspect()); + assert.strictEqual('', reply[1].inspect()); assert.strictEqual(true, Buffer.isBuffer(reply[2])); - assert.strictEqual("", reply[2].inspect()); + assert.strictEqual('', reply[2].inspect()); assert.strictEqual(true, Buffer.isBuffer(reply[3])); - assert.strictEqual("", reply[3].inspect()); + assert.strictEqual('', reply[3].inspect()); return done(err); }); }); @@ -86,20 +86,20 @@ describe("return_buffers", function () { describe('batch.hget', function () { it('returns buffers', function (done) { client.batch() - .hget("hash key 2", "key 1") - .hget(new Buffer("hash key 2"), "key 1") - .hget("hash key 2", new Buffer("key 2")) - .hget("hash key 2", "key 2") + .hget('hash key 2', 'key 1') + .hget(new Buffer('hash key 2'), 'key 1') + .hget('hash key 2', new Buffer('key 2')) + .hget('hash key 2', 'key 2') .exec(function (err, reply) { assert.strictEqual(true, Array.isArray(reply)); assert.strictEqual(4, reply.length); - assert.strictEqual("", reply[0].inspect()); + assert.strictEqual('', reply[0].inspect()); assert.strictEqual(true, Buffer.isBuffer(reply[1])); - assert.strictEqual("", reply[1].inspect()); + assert.strictEqual('', reply[1].inspect()); assert.strictEqual(true, Buffer.isBuffer(reply[2])); - assert.strictEqual("", reply[2].inspect()); + assert.strictEqual('', reply[2].inspect()); assert.strictEqual(true, Buffer.isBuffer(reply[3])); - assert.strictEqual("", reply[3].inspect()); + assert.strictEqual('', reply[3].inspect()); return done(err); }); }); @@ -108,7 +108,7 @@ describe("return_buffers", function () { describe('hmget', function () { describe('first argument is a string', function () { it('handles array of strings with undefined values in transaction (repro #344)', function (done) { - client.multi().hmget("hash key 2", "key 3", "key 4").exec(function(err, reply) { + client.multi().hmget('hash key 2', 'key 3', 'key 4').exec(function (err, reply) { assert.strictEqual(true, Array.isArray(reply)); assert.strictEqual(1, reply.length); assert.strictEqual(2, reply[0].length); @@ -121,39 +121,39 @@ describe("return_buffers", function () { describe('first argument is a buffer', function () { it('returns buffers for keys requested', function (done) { - client.hmget(new Buffer("hash key 2"), "key 1", "key 2", function (err, reply) { + client.hmget(new Buffer('hash key 2'), 'key 1', 'key 2', function (err, reply) { assert.strictEqual(true, Array.isArray(reply)); assert.strictEqual(2, reply.length); assert.strictEqual(true, Buffer.isBuffer(reply[0])); assert.strictEqual(true, Buffer.isBuffer(reply[1])); - assert.strictEqual("", reply[0].inspect()); - assert.strictEqual("", reply[1].inspect()); + assert.strictEqual('', reply[0].inspect()); + assert.strictEqual('', reply[1].inspect()); return done(err); }); }); - it("returns buffers for keys requested in transaction", function (done) { - client.multi().hmget(new Buffer("hash key 2"), "key 1", "key 2").exec(function (err, reply) { + it('returns buffers for keys requested in transaction', function (done) { + client.multi().hmget(new Buffer('hash key 2'), 'key 1', 'key 2').exec(function (err, reply) { assert.strictEqual(true, Array.isArray(reply)); assert.strictEqual(1, reply.length); assert.strictEqual(2, reply[0].length); assert.strictEqual(true, Buffer.isBuffer(reply[0][0])); assert.strictEqual(true, Buffer.isBuffer(reply[0][1])); - assert.strictEqual("", reply[0][0].inspect()); - assert.strictEqual("", reply[0][1].inspect()); + assert.strictEqual('', reply[0][0].inspect()); + assert.strictEqual('', reply[0][1].inspect()); return done(err); }); }); - it("returns buffers for keys requested in .batch", function (done) { - client.batch().hmget(new Buffer("hash key 2"), "key 1", "key 2").exec(function (err, reply) { + it('returns buffers for keys requested in .batch', function (done) { + client.batch().hmget(new Buffer('hash key 2'), 'key 1', 'key 2').exec(function (err, reply) { assert.strictEqual(true, Array.isArray(reply)); assert.strictEqual(1, reply.length); assert.strictEqual(2, reply[0].length); assert.strictEqual(true, Buffer.isBuffer(reply[0][0])); assert.strictEqual(true, Buffer.isBuffer(reply[0][1])); - assert.strictEqual("", reply[0][0].inspect()); - assert.strictEqual("", reply[0][1].inspect()); + assert.strictEqual('', reply[0][0].inspect()); + assert.strictEqual('', reply[0][1].inspect()); return done(err); }); }); @@ -163,33 +163,33 @@ describe("return_buffers", function () { describe('hgetall', function (done) { describe('first argument is a string', function () { it('returns buffer values', function (done) { - client.hgetall("hash key 2", function (err, reply) { - assert.strictEqual("object", typeof reply); + client.hgetall('hash key 2', function (err, reply) { + assert.strictEqual('object', typeof reply); assert.strictEqual(2, Object.keys(reply).length); - assert.strictEqual("", reply["key 1"].inspect()); - assert.strictEqual("", reply["key 2"].inspect()); + assert.strictEqual('', reply['key 1'].inspect()); + assert.strictEqual('', reply['key 2'].inspect()); return done(err); }); }); it('returns buffer values when executed in transaction', function (done) { - client.multi().hgetall("hash key 2").exec(function (err, reply) { + client.multi().hgetall('hash key 2').exec(function (err, reply) { assert.strictEqual(1, reply.length); - assert.strictEqual("object", typeof reply[0]); + assert.strictEqual('object', typeof reply[0]); assert.strictEqual(2, Object.keys(reply[0]).length); - assert.strictEqual("", reply[0]["key 1"].inspect()); - assert.strictEqual("", reply[0]["key 2"].inspect()); + assert.strictEqual('', reply[0]['key 1'].inspect()); + assert.strictEqual('', reply[0]['key 2'].inspect()); return done(err); }); }); it('returns buffer values when executed in .batch', function (done) { - client.batch().hgetall("hash key 2").exec(function (err, reply) { + client.batch().hgetall('hash key 2').exec(function (err, reply) { assert.strictEqual(1, reply.length); - assert.strictEqual("object", typeof reply[0]); + assert.strictEqual('object', typeof reply[0]); assert.strictEqual(2, Object.keys(reply[0]).length); - assert.strictEqual("", reply[0]["key 1"].inspect()); - assert.strictEqual("", reply[0]["key 2"].inspect()); + assert.strictEqual('', reply[0]['key 1'].inspect()); + assert.strictEqual('', reply[0]['key 2'].inspect()); return done(err); }); }); @@ -197,40 +197,40 @@ describe("return_buffers", function () { describe('first argument is a buffer', function () { it('returns buffer values', function (done) { - client.hgetall(new Buffer("hash key 2"), function (err, reply) { + client.hgetall(new Buffer('hash key 2'), function (err, reply) { assert.strictEqual(null, err); - assert.strictEqual("object", typeof reply); + assert.strictEqual('object', typeof reply); assert.strictEqual(2, Object.keys(reply).length); - assert.strictEqual(true, Buffer.isBuffer(reply["key 1"])); - assert.strictEqual(true, Buffer.isBuffer(reply["key 2"])); - assert.strictEqual("", reply["key 1"].inspect()); - assert.strictEqual("", reply["key 2"].inspect()); + assert.strictEqual(true, Buffer.isBuffer(reply['key 1'])); + assert.strictEqual(true, Buffer.isBuffer(reply['key 2'])); + assert.strictEqual('', reply['key 1'].inspect()); + assert.strictEqual('', reply['key 2'].inspect()); return done(err); }); }); it('returns buffer values when executed in transaction', function (done) { - client.multi().hgetall(new Buffer("hash key 2")).exec(function (err, reply) { + client.multi().hgetall(new Buffer('hash key 2')).exec(function (err, reply) { assert.strictEqual(1, reply.length); - assert.strictEqual("object", typeof reply[0]); + assert.strictEqual('object', typeof reply[0]); assert.strictEqual(2, Object.keys(reply[0]).length); - assert.strictEqual(true, Buffer.isBuffer(reply[0]["key 1"])); - assert.strictEqual(true, Buffer.isBuffer(reply[0]["key 2"])); - assert.strictEqual("", reply[0]["key 1"].inspect()); - assert.strictEqual("", reply[0]["key 2"].inspect()); + assert.strictEqual(true, Buffer.isBuffer(reply[0]['key 1'])); + assert.strictEqual(true, Buffer.isBuffer(reply[0]['key 2'])); + assert.strictEqual('', reply[0]['key 1'].inspect()); + assert.strictEqual('', reply[0]['key 2'].inspect()); return done(err); }); }); it('returns buffer values when executed in .batch', function (done) { - client.batch().hgetall(new Buffer("hash key 2")).exec(function (err, reply) { + client.batch().hgetall(new Buffer('hash key 2')).exec(function (err, reply) { assert.strictEqual(1, reply.length); - assert.strictEqual("object", typeof reply[0]); + assert.strictEqual('object', typeof reply[0]); assert.strictEqual(2, Object.keys(reply[0]).length); - assert.strictEqual(true, Buffer.isBuffer(reply[0]["key 1"])); - assert.strictEqual(true, Buffer.isBuffer(reply[0]["key 2"])); - assert.strictEqual("", reply[0]["key 1"].inspect()); - assert.strictEqual("", reply[0]["key 2"].inspect()); + assert.strictEqual(true, Buffer.isBuffer(reply[0]['key 1'])); + assert.strictEqual(true, Buffer.isBuffer(reply[0]['key 2'])); + assert.strictEqual('', reply[0]['key 1'].inspect()); + assert.strictEqual('', reply[0]['key 2'].inspect()); return done(err); }); }); @@ -240,8 +240,8 @@ describe("return_buffers", function () { describe('publish/subscribe', function (done) { var pub; var sub; - var channel = "test channel"; - var message = new Buffer("test message"); + var channel = 'test channel'; + var message = new Buffer('test message'); var args = config.configureClient(parser, ip, { return_buffers: true @@ -253,7 +253,7 @@ describe("return_buffers", function () { pub = redis.createClient.apply(redis.createClient, basicArgs); sub = redis.createClient.apply(redis.createClient, args); - pub.once("connect", function () { + pub.once('connect', function () { pub.flushdb(function () { pubConnected = true; if (subConnected) { @@ -261,7 +261,7 @@ describe("return_buffers", function () { } }); }); - sub.once("connect", function () { + sub.once('connect', function () { subConnected = true; if (pubConnected) { done(); @@ -270,13 +270,13 @@ describe("return_buffers", function () { }); it('receives buffer messages', function (done) { - sub.on("subscribe", function (chnl, count) { + sub.on('subscribe', function (chnl, count) { pub.publish(channel, message); }); - sub.on("message", function (chnl, msg) { + sub.on('message', function (chnl, msg) { assert.strictEqual(true, Buffer.isBuffer(msg)); - assert.strictEqual("", msg.inspect()); + assert.strictEqual('', msg.inspect()); return done(); }); diff --git a/test/tls.spec.js b/test/tls.spec.js index 0fb051cf67c..68cc02d95f2 100644 --- a/test/tls.spec.js +++ b/test/tls.spec.js @@ -1,7 +1,7 @@ 'use strict'; -var assert = require("assert"); -var config = require("./lib/config"); +var assert = require('assert'); +var config = require('./lib/config'); var fs = require('fs'); var helper = require('./helper'); var path = require('path'); @@ -9,9 +9,9 @@ var redis = config.redis; var utils = require('../lib/utils'); var tls_options = { - servername: "redis.js.org", + servername: 'redis.js.org', rejectUnauthorized: true, - ca: [ String(fs.readFileSync(path.resolve(__dirname, "./conf/redis.js.org.cert"))) ] + ca: [ String(fs.readFileSync(path.resolve(__dirname, './conf/redis.js.org.cert'))) ] }; var tls_port = 6380; @@ -21,14 +21,14 @@ var skip = false; // Wait until stunnel4 is in the travis whitelist // Check: https://github.com/travis-ci/apt-package-whitelist/issues/403 // If this is merged, remove the travis env checks -describe("TLS connection tests", function () { +describe('TLS connection tests', function () { before(function (done) { // Print the warning when the tests run instead of while starting mocha if (process.platform === 'win32') { skip = true; console.warn('\nStunnel tests do not work on windows atm. If you think you can fix that, it would be warmly welcome.\n'); - } else if (process.env.TRAVIS === 'true') { + } else if (process.env.TRAVIS === 'true') { skip = true; console.warn('\nTravis does not support stunnel right now. Skipping tests.\nCheck: https://github.com/travis-ci/apt-package-whitelist/issues/403\n'); } @@ -50,8 +50,8 @@ describe("TLS connection tests", function () { client.end(true); }); - describe("on lost connection", function () { - it("emit an error after max retry timeout and do not try to reconnect afterwards", function (done) { + describe('on lost connection', function () { + it('emit an error after max retry timeout and do not try to reconnect afterwards', function (done) { if (skip) this.skip(); var connect_timeout = 500; // in ms client = redis.createClient({ @@ -61,15 +61,15 @@ describe("TLS connection tests", function () { }); var time = 0; - client.once('ready', function() { + client.once('ready', function () { helper.killConnection(client); }); - client.on("reconnecting", function (params) { + client.on('reconnecting', function (params) { time += params.delay; }); - client.on('error', function(err) { + client.on('error', function (err) { if (/Redis connection in broken state: connection timeout.*?exceeded./.test(err.message)) { setTimeout(function () { assert(time === connect_timeout); @@ -80,9 +80,9 @@ describe("TLS connection tests", function () { }); }); - describe("when not connected", function () { + describe('when not connected', function () { - it("connect with host and port provided in the options object", function (done) { + it('connect with host and port provided in the options object', function (done) { if (skip) this.skip(); client = redis.createClient({ host: 'localhost', @@ -103,7 +103,7 @@ describe("TLS connection tests", function () { it('fails to connect because the cert is not correct', function (done) { if (skip) this.skip(); var faulty_cert = utils.clone(tls_options); - faulty_cert.ca = [ String(fs.readFileSync(path.resolve(__dirname, "./conf/faulty.cert"))) ]; + faulty_cert.ca = [ String(fs.readFileSync(path.resolve(__dirname, './conf/faulty.cert'))) ]; client = redis.createClient({ host: 'localhost', connect_timeout: 1000, diff --git a/test/unify_options.spec.js b/test/unify_options.spec.js index 975ce2b53d4..eb441d81a66 100644 --- a/test/unify_options.spec.js +++ b/test/unify_options.spec.js @@ -113,7 +113,7 @@ describe('createClient options', function () { it('duplicated, identical query options including options obj', function () { var text = ''; - var unhookIntercept = intercept(function(data) { + var unhookIntercept = intercept(function (data) { text += data; return ''; }); @@ -152,7 +152,7 @@ describe('createClient options', function () { } }); - it("warns on protocol other than redis in the redis url", function () { + it('warns on protocol other than redis in the redis url', function () { var text = ''; var unhookIntercept = intercept(function (data) { text += data; @@ -229,7 +229,7 @@ describe('createClient options', function () { }); describe('faulty data', function () { - it("throws on strange connection info", function () { + it('throws on strange connection info', function () { try { unifyOptions(true); throw new Error('failed'); diff --git a/test/utils.spec.js b/test/utils.spec.js index c886648d398..d77c7eb607c 100644 --- a/test/utils.spec.js +++ b/test/utils.spec.js @@ -44,7 +44,7 @@ describe('utils.js', function () { describe('print helper', function () { it('callback with reply', function () { var text = ''; - var unhookIntercept = intercept(function(data) { + var unhookIntercept = intercept(function (data) { text += data; return ''; }); @@ -55,7 +55,7 @@ describe('utils.js', function () { it('callback with error', function () { var text = ''; - var unhookIntercept = intercept(function(data) { + var unhookIntercept = intercept(function (data) { text += data; return ''; }); From 68f2a8894eef18d79936ff84d03b0fbcde379639 Mon Sep 17 00:00:00 2001 From: Ruben Bridgewater Date: Sat, 26 Mar 2016 12:44:13 +0100 Subject: [PATCH 0602/1748] Make test more robust The redis server ends the connection and the stream end might be triggered before the quit command returned and is therefor racy. --- test/multi.spec.js | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/test/multi.spec.js b/test/multi.spec.js index a94c2bae994..6d7bb6fcecd 100644 --- a/test/multi.spec.js +++ b/test/multi.spec.js @@ -101,9 +101,8 @@ describe("The 'multi' method", function () { beforeEach(function (done) { client = redis.createClient.apply(null, args); client.once('ready', function () { - client.quit(); + client.quit(done); }); - client.once('end', done); }); it('reports an error', function (done) { From 2e68a7a270b8bffe96fac296ee5e52438c783608 Mon Sep 17 00:00:00 2001 From: Ruben Bridgewater Date: Sat, 26 Mar 2016 15:42:03 +0100 Subject: [PATCH 0603/1748] Make test more robust by waiting till redis answered The former fix was actually not working as expected --- test/multi.spec.js | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/test/multi.spec.js b/test/multi.spec.js index 6d7bb6fcecd..874d6201346 100644 --- a/test/multi.spec.js +++ b/test/multi.spec.js @@ -99,10 +99,12 @@ describe("The 'multi' method", function () { describe('when not connected', function () { beforeEach(function (done) { + var end = helper.callFuncAfter(done, 2); client = redis.createClient.apply(null, args); client.once('ready', function () { - client.quit(done); + client.quit(end); }); + client.once('end', end); }); it('reports an error', function (done) { From 0c5947be5195c6d03f1472460620120d02760947 Mon Sep 17 00:00:00 2001 From: Ruben Bridgewater Date: Sun, 27 Mar 2016 03:06:32 +0200 Subject: [PATCH 0604/1748] Add string_numbers option to handle very big numbers --- README.md | 1 + changelog.md | 1 + index.js | 7 +- package.json | 4 +- test/commands/incr.spec.js | 128 +++++++++++++---------------------- test/commands/script.spec.js | 2 +- test/detect_buffers.spec.js | 4 +- test/helper.js | 6 +- 8 files changed, 63 insertions(+), 90 deletions(-) diff --git a/README.md b/README.md index f0d83e20c45..2db31174cbc 100644 --- a/README.md +++ b/README.md @@ -184,6 +184,7 @@ If the redis server runs on the same machine as the client consider using unix s * `path`: *null*; The unix socket string to connect to * `url`: *null*; The redis url to connect to (`[redis:]//[user][:password@][host][:port][/db-number][?db=db-number[&password=bar[&option=value]]]` For more info check [IANA](http://www.iana.org/assignments/uri-schemes/prov/redis)) * `parser`: *hiredis*; Which Redis protocol reply parser to use. If `hiredis` is not installed it will fallback to `javascript`. +* `string_numbers`: *boolean*; pass true to get numbers back as strings instead of js numbers. This is necessary if you want to handle big numbers (above `Number.MAX_SAFE_INTEGER` === 2^53). If passed, the js parser is automatically choosen as parser no matter if the parser is set to hiredis or not, as hiredis is not capable of doing this. * `return_buffers`: *false*; If set to `true`, then all replies will be sent to callbacks as Buffers instead of Strings. * `detect_buffers`: *false*; If set to `true`, then replies will be sent to callbacks as Buffers. Please be aware that this can't work properly with the pubsub mode. A subscriber has to either always return strings or buffers. if any of the input arguments to the original command were Buffers. diff --git a/changelog.md b/changelog.md index 69c5c6d39c5..687e3bc875b 100644 --- a/changelog.md +++ b/changelog.md @@ -9,6 +9,7 @@ Features - All commands that were send after a connection loss are now going to be send after reconnecting - Activating monitor mode does now work together with arbitrary commands including pub sub mode - Pub sub mode is completly rewritten and all known issues fixed +- Added `string_numbers` option to get back strings instead of numbers Bugfixes diff --git a/index.js b/index.js index a306cf01009..5245f824c1b 100644 --- a/index.js +++ b/index.js @@ -147,8 +147,8 @@ function RedisClient (options, stream) { returnReply: function (data) { self.return_reply(data); }, - returnError: function (data) { - self.return_error(data); + returnError: function (err) { + self.return_error(err); }, returnFatalError: function (err) { // Error out all fired commands. Otherwise they might rely on faulty data. We have to reconnect to get in a working state again @@ -157,7 +157,8 @@ function RedisClient (options, stream) { self.return_error(err); }, returnBuffers: this.buffers, - name: options.parser + name: options.parser, + stringNumbers: options.string_numbers }); this.create_stream(); // The listeners will not be attached right away, so let's print the deprecation message while the listener is attached diff --git a/package.json b/package.json index 7e4d2348675..92ac9fd7d25 100644 --- a/package.json +++ b/package.json @@ -26,8 +26,8 @@ }, "dependencies": { "double-ended-queue": "^2.1.0-0", - "redis-commands": "^1.0.1", - "redis-parser": "^1.1.0" + "redis-commands": "^1.1.0", + "redis-parser": "^1.2.0" }, "engines": { "node": ">=0.10.0" diff --git a/test/commands/incr.spec.js b/test/commands/incr.spec.js index ab385ecbbf2..f1f1e78ee8c 100644 --- a/test/commands/incr.spec.js +++ b/test/commands/incr.spec.js @@ -10,101 +10,65 @@ describe("The 'incr' method", function () { helper.allTests(function (parser, ip, args) { describe('using ' + parser + ' and ' + ip, function () { - var key = 'sequence'; - describe('when not connected', function () { - var client; + describe('when connected and a value in Redis', function () { - beforeEach(function (done) { - client = redis.createClient.apply(null, args); - client.once('ready', function () { - client.set(key, '9007199254740992', function (err, res) { - helper.isNotError()(err, res); - client.quit(); - }); - }); - client.on('end', done); - }); + var client; + var key = 'ABOVE_SAFE_JAVASCRIPT_INTEGER'; + var MAX_SAFE_INTEGER = Number.MAX_SAFE_INTEGER || 9007199254740991; // Backwards compatible afterEach(function () { client.end(true); }); - it('reports an error', function (done) { - client.incr(function (err, res) { - assert(err.message.match(/The connection has already been closed/)); - done(); - }); - }); - }); - - describe('when connected and a value in Redis', function () { - var client; - - before(function (done) { - /* - 9007199254740992 -> 9007199254740992 - 9007199254740993 -> 9007199254740992 - 9007199254740994 -> 9007199254740994 - 9007199254740995 -> 9007199254740996 - 9007199254740996 -> 9007199254740996 - 9007199254740997 -> 9007199254740996 - */ + /* + Number.MAX_SAFE_INTEGER === Math.pow(2, 53) - 1 === 9007199254740991 + + 9007199254740992 -> 9007199254740992 + 9007199254740993 -> 9007199254740992 + 9007199254740994 -> 9007199254740994 + 9007199254740995 -> 9007199254740996 + 9007199254740996 -> 9007199254740996 + 9007199254740997 -> 9007199254740996 + ... + */ + it('count above the safe integers as numbers', function (done) { client = redis.createClient.apply(null, args); - client.once('error', done); - client.once('ready', function () { - client.set(key, '9007199254740992', function (err, res) { - helper.isNotError()(err, res); - done(); - }); - }); - }); - - after(function () { - client.end(true); - }); - - it('changes the last digit from 2 to 3', function (done) { + // Set a value to the maximum safe allowed javascript number (2^53) - 1 + client.set(key, MAX_SAFE_INTEGER, helper.isNotError()); + client.INCR(key, helper.isNumber(MAX_SAFE_INTEGER + 1)); + client.INCR(key, helper.isNumber(MAX_SAFE_INTEGER + 2)); + client.INCR(key, helper.isNumber(MAX_SAFE_INTEGER + 3)); + client.INCR(key, helper.isNumber(MAX_SAFE_INTEGER + 4)); + client.INCR(key, helper.isNumber(MAX_SAFE_INTEGER + 5)); client.INCR(key, function (err, res) { - helper.isString('9007199254740993')(err, res); - done(err); + helper.isNumber(MAX_SAFE_INTEGER + 6)(err, res); + assert.strictEqual(typeof res, 'number'); }); + client.INCR(key, helper.isNumber(MAX_SAFE_INTEGER + 7)); + client.INCR(key, helper.isNumber(MAX_SAFE_INTEGER + 8)); + client.INCR(key, helper.isNumber(MAX_SAFE_INTEGER + 9)); + client.INCR(key, helper.isNumber(MAX_SAFE_INTEGER + 10, done)); }); - describe('and we call it again', function () { - it('changes the last digit from 3 to 4', function (done) { - client.incr(key, function (err, res) { - helper.isString('9007199254740994')(err, res); - done(err); - }); - }); - - describe('and again', function () { - it('changes the last digit from 4 to 5', function (done) { - client.incr(key, function (err, res) { - helper.isString('9007199254740995')(err, res); - done(err); - }); - }); - - describe('and again', function () { - it('changes the last digit from 5 to 6', function (done) { - client.incr(key, function (err, res) { - helper.isString('9007199254740996')(err, res); - done(err); - }); - }); - - describe('and again', function () { - it('changes the last digit from 6 to 7', function (done) { - client.incr(key, function (err, res) { - helper.isString('9007199254740997')(err, res); - done(err); - }); - }); - }); - }); + it('count above the safe integers as strings', function (done) { + args[2].string_numbers = true; + client = redis.createClient.apply(null, args); + // Set a value to the maximum safe allowed javascript number (2^53) + client.set(key, MAX_SAFE_INTEGER, helper.isNotError()); + client.incr(key, helper.isString('9007199254740992')); + client.incr(key, helper.isString('9007199254740993')); + client.incr(key, helper.isString('9007199254740994')); + client.incr(key, helper.isString('9007199254740995')); + client.incr(key, helper.isString('9007199254740996')); + client.incr(key, function (err, res) { + helper.isString('9007199254740997')(err, res); + assert.strictEqual(typeof res, 'string'); }); + client.incr(key, helper.isString('9007199254740998')); + client.incr(key, helper.isString('9007199254740999')); + client.incr(key, helper.isString('9007199254741000')); + client.incr(key, helper.isString('9007199254741001', done)); }); }); }); diff --git a/test/commands/script.spec.js b/test/commands/script.spec.js index 25a44930b5e..2e29ad553ab 100644 --- a/test/commands/script.spec.js +++ b/test/commands/script.spec.js @@ -34,7 +34,7 @@ describe("The 'script' method", function () { }); it('allows a loaded script to be evaluated', function (done) { - client.evalsha(commandSha, 0, helper.isString('99', done)); + client.evalsha(commandSha, 0, helper.isNumber(99, done)); }); it('allows a script to be loaded as part of a chained transaction', function (done) { diff --git a/test/detect_buffers.spec.js b/test/detect_buffers.spec.js index 59f6f6ae204..82f1aa99e36 100644 --- a/test/detect_buffers.spec.js +++ b/test/detect_buffers.spec.js @@ -38,7 +38,9 @@ describe('detect_buffers', function () { }); it('returns a string when executed as part of transaction', function (done) { - client.multi().get('string key 1').exec(helper.isString('string value', done)); + client.multi().get('string key 1').exec(function (err, res) { + helper.isString('string value', done)(err, res[0]); + }); }); }); diff --git a/test/helper.js b/test/helper.js index 5564a49c5ef..f28568b5003 100644 --- a/test/helper.js +++ b/test/helper.js @@ -59,9 +59,13 @@ module.exports = { }; }, isString: function (str, done) { + str = '' + str; // Make sure it's a string return function (err, results) { assert.strictEqual(null, err, "expected string '" + str + "', got error: " + err); - assert.equal(str, results, str + ' does not match ' + results); + if (Buffer.isBuffer(results)) { // If options are passed to return either strings or buffers... + results = results.toString(); + } + assert.strictEqual(str, results, str + ' does not match ' + results); if (done) done(); }; }, From 48481552c951a9edaf30a720df0950588cabd848 Mon Sep 17 00:00:00 2001 From: Ruben Bridgewater Date: Thu, 24 Mar 2016 17:09:16 +0100 Subject: [PATCH 0605/1748] Calling quit should always close the connection --- README.md | 8 +++- changelog.md | 2 + index.js | 3 +- lib/individualCommands.js | 24 ++++++++++++ test/connection.spec.js | 79 +++++++++++++++++++++++++++++++++++++++ test/node_redis.spec.js | 3 +- 6 files changed, 114 insertions(+), 5 deletions(-) diff --git a/README.md b/README.md index 2db31174cbc..44906785152 100644 --- a/README.md +++ b/README.md @@ -277,10 +277,16 @@ something like this `Error: Ready check failed: ERR operation not permitted`. The client exposed the used [stream](https://nodejs.org/api/stream.html) in `client.stream` and if the stream or client had to [buffer](https://nodejs.org/api/stream.html#stream_writable_write_chunk_encoding_callback) the command in `client.should_buffer`. In combination this can be used to implement backpressure by checking the buffer state before sending a command and listening to the stream [drain](https://nodejs.org/api/stream.html#stream_event_drain) event. +## client.quit() + +This sends the quit command to the redis server and ends cleanly right after all running commands were properly handled. +If this is called while reconnecting (and therefor no connection to the redis server exists) it is going to end the connection right away instead of +resulting in further reconnections! All offline commands are going to be flushed with an error in that case. + ## client.end(flush) Forcibly close the connection to the Redis server. Note that this does not wait until all replies have been parsed. -If you want to exit cleanly, call `client.quit()` to send the `QUIT` command after you have handled all replies. +If you want to exit cleanly, call `client.quit()` as mentioned above. You should set flush to true, if you are not absolutely sure you do not care about any other commands. If you set flush to false all still running commands will silently fail. diff --git a/changelog.md b/changelog.md index 687e3bc875b..ab6368dbd11 100644 --- a/changelog.md +++ b/changelog.md @@ -10,6 +10,7 @@ Features - Activating monitor mode does now work together with arbitrary commands including pub sub mode - Pub sub mode is completly rewritten and all known issues fixed - Added `string_numbers` option to get back strings instead of numbers +- Quit command is from now on always going to end the connection properly Bugfixes @@ -21,6 +22,7 @@ Bugfixes - Fixed pub sub mode crashing if calling unsubscribe / subscribe in various combinations - Fixed pub sub mode emitting unsubscribe even if no channels were unsubscribed - Fixed pub sub mode emitting a message without a message published +- Fixed quit command not ending the connection and resulting in further reconnection if called while reconnecting ## v.2.5.3 - 21 Mar, 2016 diff --git a/index.js b/index.js index 5245f824c1b..036ad3d85aa 100644 --- a/index.js +++ b/index.js @@ -751,6 +751,7 @@ function handle_offline_command (self, command_obj) { } err = new Error(command + " can't be processed. " + msg); err.command = command; + err.code = 'NR_OFFLINE'; utils.reply_in_order(self, callback, err); } else { debug('Queueing ' + command + ' for next server connection.'); @@ -845,8 +846,6 @@ RedisClient.prototype.send_command = function (command, args, callback) { if (!this.pub_sub_mode) { this.pub_sub_mode = this.command_queue.length + 1; } - } else if (command === 'quit') { - this.closing = true; } this.command_queue.push(command_obj); diff --git a/lib/individualCommands.js b/lib/individualCommands.js index f7b7341ec5b..cf277b1c7e9 100644 --- a/lib/individualCommands.js +++ b/lib/individualCommands.js @@ -68,6 +68,30 @@ RedisClient.prototype.monitor = RedisClient.prototype.MONITOR = function (callba }); }; +RedisClient.prototype.quit = RedisClient.prototype.QUIT = function (callback) { + var self = this; + var callback_hook = function (err, res) { + // TODO: Improve this by handling everything with coherend error codes and find out if there's anything missing + if (err && (err.code === 'NR_OFFLINE' || + err.message === 'Redis connection gone from close event.' || + err.message === 'The command can\'t be processed. The connection has already been closed.' + )) { + // Pretent the quit command worked properly in this case. + // Either the quit landed in the offline queue and was flushed at the reconnect + // or the offline queue is deactivated and the command was rejected right away + // or the stream is not writable + // or while sending the quit, the connection dropped + err = null; + res = 'OK'; + } + utils.callback_or_emit(self, callback, err, res); + }; + var backpressure_indicator = this.send_command('quit', [], callback_hook); + // Calling quit should always end the connection, no matter if there's a connection or not + this.closing = true; + return backpressure_indicator; +}; + // Store info in this.server_info after each call RedisClient.prototype.info = RedisClient.prototype.INFO = function info (section, callback) { var self = this; diff --git a/test/connection.spec.js b/test/connection.spec.js index 31e0cb0fed7..c87a15816d7 100644 --- a/test/connection.spec.js +++ b/test/connection.spec.js @@ -34,6 +34,85 @@ describe('connection tests', function () { assert.strictEqual(client.stream.listeners('error').length, 1); }); + describe('quit on lost connections', function () { + + it('calling quit while the connection is down should not end in reconnecting version a', function (done) { + var called = 0; + client = redis.createClient({ + port: 9999, + retry_strategy: function (options) { + var bool = client.quit(function (err, res) { + assert.strictEqual(res, 'OK'); + assert.strictEqual(err, null); + assert.strictEqual(called++, -1); + setTimeout(done, 25); + }); + assert.strictEqual(bool, false); + assert.strictEqual(called++, 0); + return 5; + } + }); + client.set('foo', 'bar', function (err, res) { + assert.strictEqual(err.message, 'Redis connection gone from close event.'); + called = -1; + }); + }); + + it('calling quit while the connection is down should not end in reconnecting version b', function (done) { + var called = false; + client = redis.createClient(9999); + client.set('foo', 'bar', function (err, res) { + assert.strictEqual(err.message, 'Redis connection gone from close event.'); + called = true; + }); + var bool = client.quit(function (err, res) { + assert.strictEqual(res, 'OK'); + assert.strictEqual(err, null); + assert(called); + done(); + }); + assert.strictEqual(bool, false); + }); + + it('calling quit while the connection is down without offline queue should end the connection right away', function (done) { + var called = false; + client = redis.createClient(9999, { + enable_offline_queue: false + }); + client.set('foo', 'bar', function (err, res) { + assert.strictEqual(err.message, 'SET can\'t be processed. The connection is not yet established and the offline queue is deactivated.'); + called = true; + }); + var bool = client.quit(function (err, res) { + assert.strictEqual(res, 'OK'); + assert.strictEqual(err, null); + assert(called); + done(); + }); + assert.strictEqual(bool, false); + }); + + it('do not quit before connected or a connection issue is detected', function (done) { + client = redis.createClient(); + client.set('foo', 'bar', helper.isString('OK')); + var bool = client.quit(done); + assert.strictEqual(bool, false); + }); + + it('quit right away if connection drops while quit command is on the fly', function (done) { + client = redis.createClient(); + client.once('ready', function () { + client.set('foo', 'bar', helper.isError()); + var bool = client.quit(done); + assert.strictEqual(bool, true); + process.nextTick(function () { + client.stream.destroy(); + }); + }); + }); + + }); + helper.allTests(function (parser, ip, args) { describe('using ' + parser + ' and ' + ip, function () { diff --git a/test/node_redis.spec.js b/test/node_redis.spec.js index 45f523c4eba..31fd2147d5d 100644 --- a/test/node_redis.spec.js +++ b/test/node_redis.spec.js @@ -235,12 +235,11 @@ describe('The node_redis client', function () { assert.strictEqual(client.offline_queue.length, 0); done(); }); - }, 100); + }, 50); }); it('emit an error', function (done) { if (helper.redisProcess().spawnFailed()) this.skip(); - client.quit(); client.on('error', function (err) { assert.strictEqual(err.message, 'SET can\'t be processed. The connection has already been closed.'); From d223421b98ea30d4cfa93f94a902f507681ca7a9 Mon Sep 17 00:00:00 2001 From: Ruben Bridgewater Date: Sun, 27 Mar 2016 05:31:03 +0200 Subject: [PATCH 0606/1748] v.2.6.0-0 --- package.json | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/package.json b/package.json index 92ac9fd7d25..9efc03fde89 100644 --- a/package.json +++ b/package.json @@ -1,6 +1,6 @@ { "name": "redis", - "version": "2.5.3", + "version": "2.6.0-0", "description": "Redis client library", "keywords": [ "database", From 3c2e6b4a839a084390d493a168e69ffee412e616 Mon Sep 17 00:00:00 2001 From: Ruben Bridgewater Date: Sun, 27 Mar 2016 05:37:58 +0200 Subject: [PATCH 0607/1748] Update changelog entry --- changelog.md | 6 +++++- 1 file changed, 5 insertions(+), 1 deletion(-) diff --git a/changelog.md b/changelog.md index ab6368dbd11..bb0e22e7b5d 100644 --- a/changelog.md +++ b/changelog.md @@ -1,7 +1,11 @@ Changelog ========= -## v.2.6.0 - XX Mar, 2016 +## v.2.6.0-0 - 27 Mar, 2016 + +This is mainly a very important bug fix release with some smaller features. +The quit command did not end connections earlier if the connection was down at that time and this could have +lead to strange situations, therefor this was fixed to end the connection right away in those cases. Features From 861749f4d6be28ee861096765e7176dc988024f6 Mon Sep 17 00:00:00 2001 From: Ruben Bridgewater Date: Thu, 31 Mar 2016 19:17:29 +0200 Subject: [PATCH 0608/1748] Fix send_command working with hooked internal functions --- index.js | 22 ++++++++-------------- lib/commands.js | 2 +- lib/individualCommands.js | 14 ++++++++------ lib/multi.js | 6 +++--- test/commands/info.spec.js | 2 +- 5 files changed, 21 insertions(+), 25 deletions(-) diff --git a/index.js b/index.js index 036ad3d85aa..e8b02131b19 100644 --- a/index.js +++ b/index.js @@ -391,14 +391,14 @@ RedisClient.prototype.on_ready = function () { // Restore modal commands from previous connection. The order of the commands is important if (this.selected_db !== undefined) { - this.send_command('select', [this.selected_db]); + this.internal_send_command('select', [this.selected_db]); } if (this.old_state !== null) { this.monitoring = this.old_state.monitoring; this.pub_sub_mode = this.old_state.pub_sub_mode; } if (this.monitoring) { // Monitor has to be fired before pub sub commands - this.send_command('monitor', []); + this.internal_send_command('monitor', []); } var callback_count = Object.keys(this.subscription_set).length; if (!this.options.disable_resubscribing && callback_count) { @@ -415,7 +415,7 @@ RedisClient.prototype.on_ready = function () { for (var key in this.subscription_set) { // jshint ignore: line var command = key.slice(0, key.indexOf('_')); var args = self.subscription_set[key]; - self.send_command(command, [args], callback); + self.internal_send_command(command, [args], callback); } this.send_offline_queue(); return; @@ -478,7 +478,7 @@ RedisClient.prototype.ready_check = function () { RedisClient.prototype.send_offline_queue = function () { for (var command_obj = this.offline_queue.shift(); command_obj; command_obj = this.offline_queue.shift()) { debug('Sending offline command: ' + command_obj.command); - this.send_command(command_obj.command, command_obj.args, command_obj.callback); + this.internal_send_command(command_obj.command, command_obj.args, command_obj.callback); } this.drain(); // Even though items were shifted off, Queue backing store still uses memory until next add, so just get a new Queue @@ -760,13 +760,14 @@ function handle_offline_command (self, command_obj) { self.should_buffer = true; } -RedisClient.prototype.send_command = function (command, args, callback) { - var args_copy, arg, prefix_keys; +RedisClient.prototype.internal_send_command = function (command, args, callback) { + var arg, prefix_keys; var i = 0; var command_str = ''; - var len = 0; + var len = args.length; var big_data = false; var buffer_args = false; + var args_copy = new Array(len); if (process.domain && callback) { callback = process.domain.bind(callback); @@ -778,13 +779,6 @@ RedisClient.prototype.send_command = function (command, args, callback) { return false; // Indicate buffering } - if (typeof args === 'undefined') { - args_copy = []; - } else { - len = args.length; - args_copy = new Array(len); - } - for (i = 0; i < len; i += 1) { if (typeof args[i] === 'string') { // 30000 seemed to be a good value to switch to buffers after testing and checking the pros and cons diff --git a/lib/commands.js b/lib/commands.js index 734e7b7060c..a99d0257149 100644 --- a/lib/commands.js +++ b/lib/commands.js @@ -42,7 +42,7 @@ commands.list.forEach(function (command) { arr[i] = arguments[i]; } } - return this.send_command(command, arr, callback); + return this.internal_send_command(command, arr, callback); }; } diff --git a/lib/individualCommands.js b/lib/individualCommands.js index cf277b1c7e9..2bfeb6eb42f 100644 --- a/lib/individualCommands.js +++ b/lib/individualCommands.js @@ -25,7 +25,8 @@ RedisClient.prototype.batch = RedisClient.prototype.BATCH = function batch (args // Store db in this.select_db to restore it on reconnect RedisClient.prototype.select = RedisClient.prototype.SELECT = function select (db, callback) { var self = this; - return this.send_command('select', [db], function (err, res) { + db = Array.isArray(db) && db.length === 1 ? db[0] : db; + return this.internal_send_command('select', [db], function (err, res) { if (err === null) { self.selected_db = db; } @@ -36,7 +37,7 @@ RedisClient.prototype.select = RedisClient.prototype.SELECT = function select (d RedisClient.prototype.monitor = RedisClient.prototype.MONITOR = function (callback) { // Use a individual command, as this is a special case that does not has to be checked for any other command var self = this; - return this.send_command('monitor', [], function (err, res) { + return this.internal_send_command('monitor', [], function (err, res) { if (err === null) { self.reply_parser.returnReply = function (reply) { // If in monitor mode, all normal commands are still working and we only want to emit the streamlined commands @@ -86,7 +87,7 @@ RedisClient.prototype.quit = RedisClient.prototype.QUIT = function (callback) { } utils.callback_or_emit(self, callback, err, res); }; - var backpressure_indicator = this.send_command('quit', [], callback_hook); + var backpressure_indicator = this.internal_send_command('quit', [], callback_hook); // Calling quit should always end the connection, no matter if there's a connection or not this.closing = true; return backpressure_indicator; @@ -103,7 +104,7 @@ RedisClient.prototype.info = RedisClient.prototype.INFO = function info (section args = Array.isArray(section) ? section : [section]; } this.ready = ready || this.offline_queue.length === 0; // keep the execution order intakt - var tmp = this.send_command('info', args, function (err, res) { + var tmp = this.internal_send_command('info', args, function (err, res) { if (res) { var obj = {}; var lines = res.toString().split('\r\n'); @@ -148,9 +149,10 @@ RedisClient.prototype.auth = RedisClient.prototype.AUTH = function auth (pass, c debug('Sending auth to ' + self.address + ' id ' + self.connection_id); // Stash auth for connect and reconnect. + pass = Array.isArray(pass) && pass.length === 1 ? pass[0] : pass; this.auth_pass = pass; this.ready = this.offline_queue.length === 0; // keep the execution order intakt - var tmp = this.send_command('auth', [pass], function (err, res) { + var tmp = this.internal_send_command('auth', [pass], function (err, res) { if (err) { if (no_password_is_set.test(err.message)) { self.warn('Warning: Redis server does not require a password, but a password was supplied.'); @@ -207,5 +209,5 @@ RedisClient.prototype.hmset = RedisClient.prototype.HMSET = function hmset () { arr[i] = arguments[i]; } } - return this.send_command('hmset', arr, callback); + return this.internal_send_command('hmset', arr, callback); }; diff --git a/lib/multi.js b/lib/multi.js index 4d9834378b6..ca7afda4813 100644 --- a/lib/multi.js +++ b/lib/multi.js @@ -147,7 +147,7 @@ Multi.prototype.exec_transaction = function exec_transaction (callback) { pipeline_transaction_command(self, command, args[1], index, cb); } - self._client.send_command('exec', [], function (err, replies) { + self._client.internal_send_command('exec', [], function (err, replies) { multi_callback(self, err, replies); }); self._client.uncork(); @@ -207,10 +207,10 @@ Multi.prototype.exec = Multi.prototype.EXEC = Multi.prototype.exec_batch = funct } else { cb = callback_without_own_cb; } - if (callback && index === len - 1) { + if (typeof callback === 'function' && index === len - 1) { cb = last_callback(cb); } - self._client.send_command(command, args[1], cb); + self._client.internal_send_command(command, args[1], cb); index++; } self.queue = new Queue(); diff --git a/test/commands/info.spec.js b/test/commands/info.spec.js index e0a01457ece..ce4d486fb7b 100644 --- a/test/commands/info.spec.js +++ b/test/commands/info.spec.js @@ -56,7 +56,7 @@ describe("The 'info' method", function () { it('check redis v.2.4 support', function (done) { var end = helper.callFuncAfter(done, 2); - client.send_command = function (command, args, callback) { + client.internal_send_command = function (command, args, callback) { assert.strictEqual(args.length, 0); assert.strictEqual(command, 'info'); end(); From 3fd865bbb388c54e8ccfdf5a7c434e261f6f57d9 Mon Sep 17 00:00:00 2001 From: Ruben Bridgewater Date: Thu, 31 Mar 2016 19:14:30 +0200 Subject: [PATCH 0609/1748] Move the exposed and documented api into a separate file --- index.js | 57 +++------------- lib/extendedApi.js | 91 ++++++++++++++++++++++++++ test/node_redis.spec.js | 140 ++++++++++++++++++++++++++++++++++++---- 3 files changed, 228 insertions(+), 60 deletions(-) create mode 100644 lib/extendedApi.js diff --git a/index.js b/index.js index e8b02131b19..43d2b7cb9cd 100644 --- a/index.js +++ b/index.js @@ -180,6 +180,14 @@ util.inherits(RedisClient, EventEmitter); RedisClient.connection_id = 0; +/****************************************************************************** + + All functions in here are internal besides the RedisClient constructor + and the exported functions. Don't rely on them as they will be private + functions in node_redis v.3 + +******************************************************************************/ + // Attention: the function name "create_stream" should not be changed, as other libraries need this to mock the stream (e.g. fakeredis) RedisClient.prototype.create_stream = function () { var self = this; @@ -269,17 +277,6 @@ RedisClient.prototype.handle_reply = function (reply, command) { RedisClient.prototype.cork = noop; RedisClient.prototype.uncork = noop; -RedisClient.prototype.duplicate = function (options) { - var existing_options = utils.clone(this.options); - options = utils.clone(options); - for (var elem in options) { // jshint ignore: line - existing_options[elem] = options[elem]; - } - var client = new RedisClient(existing_options); - client.selected_db = this.selected_db; - return client; -}; - RedisClient.prototype.initialize_retry_vars = function () { this.retry_timer = null; this.retry_totaltime = 0; @@ -288,18 +285,6 @@ RedisClient.prototype.initialize_retry_vars = function () { this.attempts = 1; }; -RedisClient.prototype.unref = function () { - if (this.connected) { - debug("Unref'ing the socket connection"); - this.stream.unref(); - } else { - debug('Not connected yet, will unref later'); - this.once('connect', function () { - this.unref(); - }); - } -}; - RedisClient.prototype.warn = function (msg) { var self = this; // Warn on the next tick. Otherwise no event listener can be added @@ -918,29 +903,6 @@ RedisClient.prototype.write = function (data) { return; }; -RedisClient.prototype.end = function (flush) { - // Flush queue if wanted - if (flush) { - this.flush_and_error(new Error("The command can't be processed. The connection has already been closed.")); - } else if (arguments.length === 0) { - this.warn( - 'Using .end() without the flush parameter is deprecated and throws from v.3.0.0 on.\n' + - 'Please check the doku (https://github.com/NodeRedis/node_redis) and explictly use flush.' - ); - } - // Clear retry_timer - if (this.retry_timer) { - clearTimeout(this.retry_timer); - this.retry_timer = null; - } - this.stream.removeAllListeners(); - this.stream.on('error', noop); - this.connected = false; - this.ready = false; - this.closing = true; - return this.stream.destroySoon(); -}; - exports.createClient = function () { return new RedisClient(unifyOptions.apply(null, arguments)); }; @@ -948,6 +910,7 @@ exports.RedisClient = RedisClient; exports.print = utils.print; exports.Multi = require('./lib/multi'); -// Add all redis commands to the client +// Add all redis commands / node_redis api to the client require('./lib/individualCommands'); +require('./lib/extendedApi'); require('./lib/commands'); diff --git a/lib/extendedApi.js b/lib/extendedApi.js new file mode 100644 index 00000000000..e9182028e71 --- /dev/null +++ b/lib/extendedApi.js @@ -0,0 +1,91 @@ +'use strict'; + +var utils = require('./utils'); +var debug = require('./debug'); +var RedisClient = require('../').RedisClient; +var noop = function () {}; + +/********************************************** +All documented and exposed API belongs in here +**********************************************/ + +// Redirect calls to the appropriate function and use to send arbitrary / not supported commands +RedisClient.prototype.send_command = function (command, args, callback) { + // Throw to fail early instead of relying in order in this case + if (typeof command !== 'string') { + throw new Error('Wrong input type "' + (command !== null && command !== undefined ? command.constructor.name : command) + '" for command name'); + } + if (!Array.isArray(args)) { + if (args === undefined || args === null) { + args = []; + } else if (typeof args === 'function' && callback === undefined) { + callback = args; + args = []; + } else { + throw new Error('Wrong input type "' + args.constructor.name + '" for args'); + } + } + if (typeof callback !== 'function' && callback !== undefined) { + throw new Error('Wrong input type "' + (callback !== null ? callback.constructor.name : 'null') + '" for callback function'); + } + + // Using the raw multi command is only possible with this function + // If the command is not yet added to the client, the internal function should be called right away + // Otherwise we need to redirect the calls to make sure the interal functions don't get skipped + // The internal functions could actually be used for any non hooked function + // but this might change from time to time and at the moment there's no good way to distinguishe them + // from each other, so let's just do it do it this way for the time being + if (command === 'multi' || typeof this[command] !== 'function') { + return this.internal_send_command(command, args, callback); + } + if (typeof callback === 'function') { + args = args.concat([callback]); + } + return this[command].apply(this, args); +}; + +RedisClient.prototype.end = function (flush) { + // Flush queue if wanted + if (flush) { + this.flush_and_error(new Error("The command can't be processed. The connection has already been closed.")); + } else if (arguments.length === 0) { + this.warn( + 'Using .end() without the flush parameter is deprecated and throws from v.3.0.0 on.\n' + + 'Please check the doku (https://github.com/NodeRedis/node_redis) and explictly use flush.' + ); + } + // Clear retry_timer + if (this.retry_timer) { + clearTimeout(this.retry_timer); + this.retry_timer = null; + } + this.stream.removeAllListeners(); + this.stream.on('error', noop); + this.connected = false; + this.ready = false; + this.closing = true; + return this.stream.destroySoon(); +}; + +RedisClient.prototype.unref = function () { + if (this.connected) { + debug("Unref'ing the socket connection"); + this.stream.unref(); + } else { + debug('Not connected yet, will unref later'); + this.once('connect', function () { + this.unref(); + }); + } +}; + +RedisClient.prototype.duplicate = function (options) { + var existing_options = utils.clone(this.options); + options = utils.clone(options); + for (var elem in options) { // jshint ignore: line + existing_options[elem] = options[elem]; + } + var client = new RedisClient(existing_options); + client.selected_db = this.selected_db; + return client; +}; diff --git a/test/node_redis.spec.js b/test/node_redis.spec.js index 31fd2147d5d..be4646fe5f5 100644 --- a/test/node_redis.spec.js +++ b/test/node_redis.spec.js @@ -109,33 +109,147 @@ describe('The node_redis client', function () { describe('send_command', function () { - it('omitting args should be fine in some cases', function (done) { + it('omitting args should be fine', function (done) { + client.server_info = {}; + client.send_command('info'); + client.send_command('ping', function (err, res) { + assert.strictEqual(res, 'PONG'); + // Check if the previous info command used the internal individual info command + assert.notDeepEqual(client.server_info, {}); + client.server_info = {}; + }); + client.send_command('info', null, undefined); + client.send_command('ping', null, function (err, res) { + assert.strictEqual(res, 'PONG'); + // Check if the previous info command used the internal individual info command + assert.notDeepEqual(client.server_info, {}); + client.server_info = {}; + }); + client.send_command('info', undefined, undefined); + client.send_command('ping', function (err, res) { + assert.strictEqual(res, 'PONG'); + // Check if the previous info command used the internal individual info command + assert.notDeepEqual(client.server_info, {}); + client.server_info = {}; + }); client.send_command('info', undefined, function (err, res) { assert(/redis_version/.test(res)); + // The individual info command should also be called by using send_command + // console.log(info, client.server_info); + assert.notDeepEqual(client.server_info, {}); + done(); + }); + }); + + it('using multi with send_command should work as individual command instead of using the internal multi', function (done) { + // This is necessary to keep backwards compatibility and it is the only way to handle multis as you want in node_redis + client.send_command('multi'); + client.send_command('set', ['foo', 'bar'], helper.isString('QUEUED')); + client.get('foo'); + client.exec(function (err, res) { // exec is not manipulated if not fired by the individual multi command + // As the multi command is handled individually by the user he also has to handle the return value + assert.strictEqual(res[0].toString(), 'OK'); + assert.strictEqual(res[1].toString(), 'bar'); done(); }); }); - it('using another type as cb should just work as if there were no callback parameter', function (done) { - client.send_command('set', ['test', 'bla'], [true]); - client.get('test', function (err, res) { - assert.equal(res, 'bla'); + it('multi should be handled special', function (done) { + client.send_command('multi', undefined, helper.isString('OK')); + var args = ['test', 'bla']; + client.send_command('set', args, helper.isString('QUEUED')); + assert.deepEqual(args, ['test', 'bla']); // Check args manipulation + client.get('test', helper.isString('QUEUED')); + client.exec(function (err, res) { + // As the multi command is handled individually by the user he also has to handle the return value + assert.strictEqual(res[0].toString(), 'OK'); + assert.strictEqual(res[1].toString(), 'bla'); + done(); + }); + }); + + it('using another type as cb should throw', function () { + try { + client.send_command('set', ['test', 'bla'], [true]); + throw new Error('failed'); + } catch (err) { + assert.strictEqual(err.message, 'Wrong input type "Array" for callback function'); + } + try { + client.send_command('set', ['test', 'bla'], null); + throw new Error('failed'); + } catch (err) { + assert.strictEqual(err.message, 'Wrong input type "null" for callback function'); + } + }); + + it('command argument has to be of type string', function () { + try { + client.send_command(true, ['test', 'bla'], function () {}); + throw new Error('failed'); + } catch (err) { + assert.strictEqual(err.message, 'Wrong input type "Boolean" for command name'); + } + try { + client.send_command(undefined, ['test', 'bla'], function () {}); + throw new Error('failed'); + } catch (err) { + assert.strictEqual(err.message, 'Wrong input type "undefined" for command name'); + } + try { + client.send_command(null, ['test', 'bla'], function () {}); + throw new Error('failed'); + } catch (err) { + assert.strictEqual(err.message, 'Wrong input type "null" for command name'); + } + }); + + it('args may only be of type Array or undefined', function () { + try { + client.send_command('info', 123); + throw new Error('failed'); + } catch (err) { + assert.strictEqual(err.message, 'Wrong input type "Number" for args'); + } + }); + + it('passing a callback as args and as callback should throw', function () { + try { + client.send_command('info', function a () {}, function b () {}); + throw new Error('failed'); + } catch (err) { + assert.strictEqual(err.message, 'Wrong input type "Function" for args'); + } + }); + + it('multi should be handled special', function (done) { + client.send_command('multi', undefined, helper.isString('OK')); + var args = ['test', 'bla']; + client.send_command('set', args, helper.isString('QUEUED')); + assert.deepEqual(args, ['test', 'bla']); // Check args manipulation + client.get('test', helper.isString('QUEUED')); + client.exec(function (err, res) { + // As the multi command is handled individually by the user he also has to handle the return value + assert.strictEqual(res[0].toString(), 'OK'); + assert.strictEqual(res[1].toString(), 'bla'); done(); }); }); - it('misusing the function should eventually throw (no command)', function (done) { - client.send_command(true, 'info', function (err, res) { - assert(/ERR Protocol error/.test(err.message)); - assert.equal(err.command, undefined); - assert.equal(err.code, 'ERR'); + it('the args array may contain a arbitrary number of arguments', function (done) { + client.send_command('mset', ['foo', 1, 'bar', 2, 'baz', 3], helper.isString('OK')); + client.mget(['foo', 'bar', 'baz'], function (err, res) { + // As the multi command is handled individually by the user he also has to handle the return value + assert.strictEqual(res[0].toString(), '1'); + assert.strictEqual(res[1].toString(), '2'); + assert.strictEqual(res[2].toString(), '3'); done(); }); }); - it('misusing the function should eventually throw (wrong args)', function (done) { - client.send_command('info', false, function (err, res) { - assert.equal(err.message, 'ERR Protocol error: invalid multibulk length'); + it('send_command with callback as args', function (done) { + client.send_command('abcdef', function (err, res) { + assert.strictEqual(err.message, "ERR unknown command 'abcdef'"); done(); }); }); From 79c1767f862fe21e6d70419a165b70b2659d33f7 Mon Sep 17 00:00:00 2001 From: Ruben Bridgewater Date: Thu, 31 Mar 2016 19:19:41 +0200 Subject: [PATCH 0610/1748] Fix pubsub further Unsubscribing from all channels did not work properly with reconnect Pub sub did not work properly with the new `string_numbers` option --- index.js | 77 ++++++++++++++++++++++----------------------- test/pubsub.spec.js | 35 +++++++++++++++++++-- 2 files changed, 69 insertions(+), 43 deletions(-) diff --git a/index.js b/index.js index 43d2b7cb9cd..96cf6244020 100644 --- a/index.js +++ b/index.js @@ -12,6 +12,12 @@ var Parser = require('redis-parser'); var commands = require('redis-commands'); var debug = require('./lib/debug'); var unifyOptions = require('./lib/createClient'); +var SUBSCRIBE_COMMANDS = { + subscribe: true, + unsubscribe: true, + psubscribe: true, + punsubscribe: true +}; // Newer Node.js versions > 0.10 return the EventEmitter right away and using .EventEmitter was deprecated if (typeof EventEmitter !== 'function') { @@ -615,59 +621,52 @@ function normal_reply (self, reply) { } } -function set_subscribe (self, type, command_obj, subscribe, reply) { - var i = 0; +function set_subscribe (self, type, subscribe, channel) { + // Every channel has to be saved / removed one after the other and the type has to be the same too, + // to make sure partly subscribe / unsubscribe works well together if (subscribe) { - // The channels have to be saved one after the other and the type has to be the same too, - // to make sure partly subscribe / unsubscribe works well together - for (; i < command_obj.args.length; i++) { - self.subscription_set[type + '_' + command_obj.args[i]] = command_obj.args[i]; - } + self.subscription_set[type + '_' + channel] = channel; } else { type = type === 'unsubscribe' ? 'subscribe' : 'psubscribe'; // Make types consistent - for (; i < command_obj.args.length; i++) { - delete self.subscription_set[type + '_' + command_obj.args[i]]; - } - if (reply[2] === 0) { // No channels left that this client is subscribed to - var running_command; - i = 0; - // This should be a rare case and therefor handling it this way should be good performance wise for the general case - while (running_command = self.command_queue.get(i++)) { - if ( - running_command.command === 'subscribe' || - running_command.command === 'psubscribe' || - running_command.command === 'unsubscribe' || - running_command.command === 'punsubscribe' - ) { - self.pub_sub_mode = i; - return; - } - } - self.pub_sub_mode = 0; - } + delete self.subscription_set[type + '_' + channel]; } } function subscribe_unsubscribe (self, reply, type, subscribe) { // Subscribe commands take an optional callback and also emit an event, but only the _last_ response is included in the callback + // The pub sub commands return each argument in a separate return value and have to be handled that way var command_obj = self.command_queue.get(0); - var buffer = self.options.return_buffers || self.options.detect_buffers && command_obj && command_obj.buffer_args || reply[1] === null; - var channel = buffer ? reply[1] : reply[1].toString(); - var count = reply[2]; + var buffer = self.options.return_buffers || self.options.detect_buffers && command_obj.buffer_args; + var channel = (buffer || reply[1] === null) ? reply[1] : reply[1].toString(); + var count = +reply[2]; // Return the channel counter as number no matter if `string_numbers` is activated or not debug('Subscribe / unsubscribe command'); // Emit first, then return the callback - if (channel !== null) { // Do not emit something if there was no channel to unsubscribe from + if (channel !== null) { // Do not emit or "unsubscribe" something if there was no channel to unsubscribe from self.emit(type, channel, count); + set_subscribe(self, type, subscribe, channel); } - // The pub sub commands return each argument in a separate return value and have to be handled that way if (command_obj.sub_commands_left <= 1) { - if (count !== 0 && !subscribe && command_obj.args.length === 0) { - command_obj.sub_commands_left = count; - return; + if (count !== 0) { + if (!subscribe && command_obj.args.length === 0) { // Unsubscribe from all channels + command_obj.sub_commands_left = count; + return; + } + } else { + var running_command; + var i = 1; + // This should be a rare case and therefor handling it this way should be good performance wise for the general case + while (running_command = self.command_queue.get(i)) { + if (SUBSCRIBE_COMMANDS[running_command.command]) { + self.command_queue.shift(); + self.pub_sub_mode = i; + return; + } + i++; + } + self.pub_sub_mode = 0; } self.command_queue.shift(); - set_subscribe(self, type, command_obj, subscribe, reply); if (typeof command_obj.callback === 'function') { // TODO: The current return value is pretty useless. // Evaluate to change this in v.3 to return all subscribed / unsubscribed channels in an array including the number of channels subscribed too @@ -819,12 +818,10 @@ RedisClient.prototype.internal_send_command = function (command, args, callback) var command_obj = new Command(command, args_copy, callback); command_obj.buffer_args = buffer_args; - if (command === 'subscribe' || command === 'psubscribe' || command === 'unsubscribe' || command === 'punsubscribe') { + if (SUBSCRIBE_COMMANDS[command] && this.pub_sub_mode === 0) { // If pub sub is already activated, keep it that way, otherwise set the number of commands to resolve until pub sub mode activates // Deactivation of the pub sub mode happens in the result handler - if (!this.pub_sub_mode) { - this.pub_sub_mode = this.command_queue.length + 1; - } + this.pub_sub_mode = this.command_queue.length + 1; } this.command_queue.push(command_obj); diff --git a/test/pubsub.spec.js b/test/pubsub.spec.js index 63d61e01bb6..1651746a545 100644 --- a/test/pubsub.spec.js +++ b/test/pubsub.spec.js @@ -65,15 +65,44 @@ describe('publish/subscribe', function () { }); }); + describe('string_numbers and pub sub', function () { + beforeEach(function (done) { + sub.end(false); + sub = redis.createClient({ + string_numbers: true + }); + sub.once('connect', function () { + done(); + }); + }); + + it('does not fire subscribe events after reconnecting', function (done) { + var i = 0; + sub.on('subscribe', function (chnl, count) { + assert.strictEqual(typeof count, 'number'); + assert.strictEqual(++i, count); + }); + sub.on('unsubscribe', function (chnl, count) { + assert.strictEqual(typeof count, 'number'); + assert.strictEqual(--i, count); + }); + sub.subscribe(channel, channel2); + sub.unsubscribe(function (err, res) { // Do not pass a channel here! + assert.strictEqual(sub.pub_sub_mode, 2); + assert.deepEqual(sub.subscription_set, {}); + }); + sub.set('foo', 'bar', helper.isString('OK')); + sub.subscribe(channel2, done); + }); + }); + describe('subscribe', function () { it('fires a subscribe event for each channel subscribed to even after reconnecting', function (done) { var a = false; sub.on('subscribe', function (chnl, count) { if (chnl === channel2) { assert.equal(2, count); - if (a) { - return done(); - } + if (a) return done(); sub.stream.destroy(); } }); From 14170f9d029e307af55e603d2b61ec89c596f63d Mon Sep 17 00:00:00 2001 From: Ruben Bridgewater Date: Thu, 31 Mar 2016 19:21:14 +0200 Subject: [PATCH 0611/1748] Improve tests a bit Reduce timeouts if possible Extend timeouts if needed (windows tests need their time) Don't expose the redis socket to others than the owner Don't create the stunnel log --- index.js | 2 + lib/individualCommands.js | 4 +- test/auth.spec.js | 4 +- test/batch.spec.js | 2 + test/commands/expire.spec.js | 4 +- test/commands/flushdb.spec.js | 2 +- test/commands/get.spec.js | 2 +- test/commands/info.spec.js | 2 +- test/commands/select.spec.js | 8 ++-- test/commands/set.spec.js | 31 ++++++++++++- test/commands/ttl.spec.js | 11 +++-- test/conf/password.conf | 2 +- test/conf/redis.conf | 2 +- test/conf/rename.conf | 2 +- test/conf/slave.conf | 2 +- test/conf/stunnel.conf.template | 2 +- test/connection.spec.js | 47 ++++++++++++++------ test/multi.spec.js | 2 +- test/node_redis.spec.js | 77 +++++++++++++++------------------ test/pubsub.spec.js | 3 +- test/tls.spec.js | 11 +++-- 21 files changed, 136 insertions(+), 86 deletions(-) diff --git a/index.js b/index.js index 96cf6244020..9cdc46dc37f 100644 --- a/index.js +++ b/index.js @@ -568,6 +568,8 @@ RedisClient.prototype.connection_gone = function (why, error) { error.code = 'UNCERTAIN_STATE'; this.flush_and_error(error, ['command_queue']); error.message = 'Redis connection lost and commands aborted in uncertain state. They might have been processed.'; + // TODO: Reconsider emitting this always, as each running command is handled anyway + // This should likely be removed in v.3. This is different to the broken connection as we'll reconnect here this.emit('error', error); } diff --git a/lib/individualCommands.js b/lib/individualCommands.js index 2bfeb6eb42f..4f4c8ab7a57 100644 --- a/lib/individualCommands.js +++ b/lib/individualCommands.js @@ -25,7 +25,6 @@ RedisClient.prototype.batch = RedisClient.prototype.BATCH = function batch (args // Store db in this.select_db to restore it on reconnect RedisClient.prototype.select = RedisClient.prototype.SELECT = function select (db, callback) { var self = this; - db = Array.isArray(db) && db.length === 1 ? db[0] : db; return this.internal_send_command('select', [db], function (err, res) { if (err === null) { self.selected_db = db; @@ -149,7 +148,6 @@ RedisClient.prototype.auth = RedisClient.prototype.AUTH = function auth (pass, c debug('Sending auth to ' + self.address + ' id ' + self.connection_id); // Stash auth for connect and reconnect. - pass = Array.isArray(pass) && pass.length === 1 ? pass[0] : pass; this.auth_pass = pass; this.ready = this.offline_queue.length === 0; // keep the execution order intakt var tmp = this.internal_send_command('auth', [pass], function (err, res) { @@ -163,7 +161,7 @@ RedisClient.prototype.auth = RedisClient.prototype.AUTH = function auth (pass, c debug('Redis still loading, trying to authenticate later'); setTimeout(function () { self.auth(pass, callback); - }, 200); + }, 100); return; } } diff --git a/test/auth.spec.js b/test/auth.spec.js index fd79b36845e..47fa0964a2f 100644 --- a/test/auth.spec.js +++ b/test/auth.spec.js @@ -54,8 +54,8 @@ describe('client authentication', function () { assert.strictEqual('retry worked', res); var now = Date.now(); // Hint: setTimeout sometimes triggers early and therefor the value can be like one or two ms to early - assert(now - time >= 198, 'Time should be above 200 ms (the reconnect time) and is ' + (now - time)); - assert(now - time < 300, 'Time should be below 300 ms (the reconnect should only take a bit above 200 ms) and is ' + (now - time)); + assert(now - time >= 98, 'Time should be above 100 ms (the reconnect time) and is ' + (now - time)); + assert(now - time < 225, 'Time should be below 255 ms (the reconnect should only take a bit above 100 ms) and is ' + (now - time)); done(); }); var tmp = client.command_queue.get(0).callback; diff --git a/test/batch.spec.js b/test/batch.spec.js index 2d02c371f17..5f25be65cba 100644 --- a/test/batch.spec.js +++ b/test/batch.spec.js @@ -12,6 +12,8 @@ describe("The 'batch' method", function () { describe('using ' + parser + ' and ' + ip, function () { describe('when not connected', function () { + // TODO: This is somewhat broken and should be fixed in v.3 + // The commands should return an error instead of returning an empty result var client; beforeEach(function (done) { diff --git a/test/commands/expire.spec.js b/test/commands/expire.spec.js index 4994caf86ff..f9041b71091 100644 --- a/test/commands/expire.spec.js +++ b/test/commands/expire.spec.js @@ -23,7 +23,7 @@ describe("The 'expire' method", function () { client.EXPIRE('expiry key', '1', helper.isNumber(1)); setTimeout(function () { client.exists(['expiry key'], helper.isNumber(0, done)); - }, 1100); + }, 1050); }); it('expires key after timeout with array syntax', function (done) { @@ -31,7 +31,7 @@ describe("The 'expire' method", function () { client.EXPIRE(['expiry key', '1'], helper.isNumber(1)); setTimeout(function () { client.exists(['expiry key'], helper.isNumber(0, done)); - }, 1100); + }, 1050); }); afterEach(function () { diff --git a/test/commands/flushdb.spec.js b/test/commands/flushdb.spec.js index b96c4b10319..90811053499 100644 --- a/test/commands/flushdb.spec.js +++ b/test/commands/flushdb.spec.js @@ -90,7 +90,7 @@ describe("The 'flushdb' method", function () { it('results in a db size of zero without a callback', function (done) { client.flushdb(); setTimeout(function (err, res) { - client.dbsize([], function (err, res) { + client.dbsize(function (err, res) { helper.isNotError()(err, res); helper.isType.number()(err, res); assert.strictEqual(0, res, 'Flushing db should result in db size 0'); diff --git a/test/commands/get.spec.js b/test/commands/get.spec.js index dc4b92da25d..83a330ad12d 100644 --- a/test/commands/get.spec.js +++ b/test/commands/get.spec.js @@ -77,7 +77,7 @@ describe("The 'get' method", function () { client.on('error', function (err) { throw err; }); - setTimeout(done, 50); + setTimeout(done, 25); }); }); diff --git a/test/commands/info.spec.js b/test/commands/info.spec.js index ce4d486fb7b..838dac21b8e 100644 --- a/test/commands/info.spec.js +++ b/test/commands/info.spec.js @@ -34,7 +34,7 @@ describe("The 'info' method", function () { setTimeout(function () { assert.strictEqual(typeof client.server_info.db2, 'object'); done(); - }, 150); + }, 30); }); it('works with optional section provided with and without callback', function (done) { diff --git a/test/commands/select.spec.js b/test/commands/select.spec.js index 4e418767fbc..d8878fe48c3 100644 --- a/test/commands/select.spec.js +++ b/test/commands/select.spec.js @@ -62,7 +62,7 @@ describe("The 'select' method", function () { client.select(1, function (err) { assert.equal(err, null); assert.equal(client.selected_db, 1, 'we should have selected the new valid DB'); - return done(); + done(); }); }); }); @@ -73,7 +73,7 @@ describe("The 'select' method", function () { client.select(9999, function (err) { assert.equal(err.code, 'ERR'); assert.equal(err.message, 'ERR invalid DB index'); - return done(); + done(); }); }); }); @@ -86,8 +86,8 @@ describe("The 'select' method", function () { client.select(1); setTimeout(function () { assert.equal(client.selected_db, 1, 'we should have selected the new valid DB'); - return done(); - }, 100); + done(); + }, 25); }); }); diff --git a/test/commands/set.spec.js b/test/commands/set.spec.js index 94b6cd69df1..7cb4b8314a9 100644 --- a/test/commands/set.spec.js +++ b/test/commands/set.spec.js @@ -43,7 +43,7 @@ describe("The 'set' method", function () { beforeEach(function (done) { client = redis.createClient.apply(null, args); client.once('ready', function () { - done(); + client.flushdb(done); }); }); @@ -62,6 +62,35 @@ describe("The 'set' method", function () { }); }); }); + + it('set expire date in seconds', function (done) { + client.set('foo', 'bar', 'ex', 10, helper.isString('OK')); + client.pttl('foo', function (err, res) { + assert(res >= 10000 - 50); // Max 50 ms should have passed + assert(res <= 10000); // Max possible should be 10.000 + done(err); + }); + }); + + it('set expire date in milliseconds', function (done) { + client.set('foo', 'bar', 'px', 100, helper.isString('OK')); + client.pttl('foo', function (err, res) { + assert(res >= 50); // Max 50 ms should have passed + assert(res <= 100); // Max possible should be 100 + done(err); + }); + }); + + it('only set the key if (not) already set', function (done) { + client.set('foo', 'bar', 'NX', helper.isString('OK')); + client.set('foo', 'bar', 'nx', helper.isNull()); + client.set('foo', 'bar', 'EX', '10', 'XX', helper.isString('OK')); + client.ttl('foo', function (err, res) { + assert(res >= 9); // Min 9s should be left + assert(res <= 10); // Max 10s should be left + done(err); + }); + }); }); describe('reports an error with invalid parameters', function () { diff --git a/test/commands/ttl.spec.js b/test/commands/ttl.spec.js index 5309b70a1a9..65212f710c2 100644 --- a/test/commands/ttl.spec.js +++ b/test/commands/ttl.spec.js @@ -22,12 +22,11 @@ describe("The 'ttl' method", function () { it('returns the current ttl on a key', function (done) { client.set(['ttl key', 'ttl val'], helper.isString('OK')); client.expire(['ttl key', '100'], helper.isNumber(1)); - setTimeout(function () { - client.TTL(['ttl key'], function (err, ttl) { - assert.ok(ttl > 50 && ttl <= 100); - return done(err); - }); - }, 200); + client.TTL(['ttl key'], function (err, ttl) { + assert(ttl >= 99); + assert(ttl <= 100); + done(err); + }); }); afterEach(function () { diff --git a/test/conf/password.conf b/test/conf/password.conf index fae73a9415d..c2b8feb4478 100644 --- a/test/conf/password.conf +++ b/test/conf/password.conf @@ -2,4 +2,4 @@ requirepass porkchopsandwiches port 6379 bind ::1 127.0.0.1 unixsocket /tmp/redis.sock -unixsocketperm 755 +unixsocketperm 700 diff --git a/test/conf/redis.conf b/test/conf/redis.conf index 16f1434200b..fad47a5fc63 100644 --- a/test/conf/redis.conf +++ b/test/conf/redis.conf @@ -1,4 +1,4 @@ port 6379 bind ::1 127.0.0.1 unixsocket /tmp/redis.sock -unixsocketperm 755 +unixsocketperm 700 diff --git a/test/conf/rename.conf b/test/conf/rename.conf index 4da225053fd..ef839236558 100644 --- a/test/conf/rename.conf +++ b/test/conf/rename.conf @@ -1,7 +1,7 @@ port 6379 bind ::1 127.0.0.1 unixsocket /tmp/redis.sock -unixsocketperm 755 +unixsocketperm 700 rename-command SET 807081f5afa96845a02816a28b7258c3 rename-command GET f397808a43ceca3963e22b4e13deb672 rename-command GETRANGE 9e3102b15cf231c4e9e940f284744fe0 diff --git a/test/conf/slave.conf b/test/conf/slave.conf index 61ea50d9593..31cd801c269 100644 --- a/test/conf/slave.conf +++ b/test/conf/slave.conf @@ -1,6 +1,6 @@ port 6381 bind ::1 127.0.0.1 unixsocket /tmp/redis6381.sock -unixsocketperm 755 +unixsocketperm 700 slaveof localhost 6379 masterauth porkchopsandwiches diff --git a/test/conf/stunnel.conf.template b/test/conf/stunnel.conf.template index 2f239516522..17ee3e879c5 100644 --- a/test/conf/stunnel.conf.template +++ b/test/conf/stunnel.conf.template @@ -1,5 +1,5 @@ pid = __dirname/stunnel.pid -output = __dirname/stunnel.log +; output = __dirname/stunnel.log CAfile = __dirname/redis.js.org.cert cert = __dirname/redis.js.org.cert key = __dirname/redis.js.org.key diff --git a/test/connection.spec.js b/test/connection.spec.js index c87a15816d7..1279ec4e37b 100644 --- a/test/connection.spec.js +++ b/test/connection.spec.js @@ -89,6 +89,7 @@ describe('connection tests', function () { assert(called); done(); }); + // TODO: In v.3 the quit command would be fired right away, so bool should be true assert.strictEqual(bool, false); }); @@ -99,6 +100,18 @@ describe('connection tests', function () { assert.strictEqual(bool, false); }); + it('quit "succeeds" even if the client connection is closed while doing so', function (done) { + client = redis.createClient(); + client.set('foo', 'bar', function (err, res) { + assert.strictEqual(res, 'OK'); + client.quit(function (err, res) { + assert.strictEqual(res, 'OK'); + done(err); + }); + client.end(true); // Flushing the quit command should result in a success + }); + }); + it('quit right away if connection drops while quit command is on the fly', function (done) { client = redis.createClient(); client.once('ready', function () { @@ -119,7 +132,7 @@ describe('connection tests', function () { describe('on lost connection', function () { it('emit an error after max retry attempts and do not try to reconnect afterwards', function (done) { - var max_attempts = 4; + var max_attempts = 3; var options = { parser: parser, max_attempts: max_attempts @@ -138,10 +151,14 @@ describe('connection tests', function () { client.on('error', function (err) { if (/Redis connection in broken state: maximum connection attempts.*?exceeded./.test(err.message)) { - setTimeout(function () { + process.nextTick(function () { // End is called after the error got emitted assert.strictEqual(calls, max_attempts - 1); + assert.strictEqual(client.emitted_end, true); + assert.strictEqual(client.connected, false); + assert.strictEqual(client.ready, false); + assert.strictEqual(client.closing, true); done(); - }, 500); + }); } }); }); @@ -167,11 +184,15 @@ describe('connection tests', function () { client.on('error', function (err) { if (/Redis connection in broken state: connection timeout.*?exceeded./.test(err.message)) { - setTimeout(function () { + process.nextTick(function () { // End is called after the error got emitted + assert.strictEqual(client.emitted_end, true); + assert.strictEqual(client.connected, false); + assert.strictEqual(client.ready, false); + assert.strictEqual(client.closing, true); assert.strictEqual(client.retry_totaltime, connect_timeout); assert.strictEqual(time, connect_timeout); done(); - }, 500); + }); } }); }); @@ -190,7 +211,7 @@ describe('connection tests', function () { client.on('reconnecting', function (params) { client.end(true); assert.strictEqual(params.times_connected, 1); - setTimeout(done, 100); + setTimeout(done, 5); }); }); @@ -291,7 +312,6 @@ describe('connection tests', function () { it('emit an error after the socket timeout exceeded the connect_timeout time', function (done) { var connect_timeout = 500; // in ms - var time = Date.now(); client = redis.createClient({ parser: parser, // Auto detect ipv4 and use non routable ip to trigger the timeout @@ -308,17 +328,18 @@ describe('connection tests', function () { throw new Error('No reconnect, since no connection was ever established'); }); + var time = Date.now(); client.on('error', function (err) { if (err.code === 'ENETUNREACH') { // The test is run without a internet connection. Pretent it works return done(); } assert(/Redis connection in broken state: connection timeout.*?exceeded./.test(err.message)); // The code execution on windows is very slow at times - var add = process.platform !== 'win32' ? 25 : 125; + var add = process.platform !== 'win32' ? 15 : 200; var now = Date.now(); assert(now - time < connect_timeout + add, 'The real timeout time should be below ' + (connect_timeout + add) + 'ms but is: ' + (now - time)); // Timers sometimes trigger early (e.g. 1ms to early) - assert(now - time >= connect_timeout - 3, 'The real timeout time should be above ' + connect_timeout + 'ms, but it is: ' + (now - time)); + assert(now - time >= connect_timeout - 5, 'The real timeout time should be above ' + connect_timeout + 'ms, but it is: ' + (now - time)); done(); }); }); @@ -546,7 +567,7 @@ describe('connection tests', function () { }); } - it('redis still loading <= 1000ms', function (done) { + it('redis still loading <= 500', function (done) { client = redis.createClient.apply(null, args); var tmp = client.info.bind(client); var end = helper.callFuncAfter(done, 3); @@ -568,9 +589,9 @@ describe('connection tests', function () { }; client.on('ready', function () { var rest = Date.now() - time; - assert(rest >= 498, 'Rest should be equal or above 500 ms but is: ' + rest); // setTimeout might trigger early + assert(rest >= 495, 'Rest should be equal or above 500 ms but is: ' + rest); // setTimeout might trigger early // Be on the safe side and accept 200ms above the original value - assert(rest - 200 < 500, 'Rest - 200 should be below 500 ms but is: ' + (rest - 200)); + assert(rest - 250 < 500, 'Rest - 250 should be below 500 ms but is: ' + (rest - 250)); assert(delayed); end(); }); @@ -601,7 +622,7 @@ describe('connection tests', function () { var rest = Date.now() - time; assert(rest >= 998, '`rest` should be equal or above 1000 ms but is: ' + rest); // setTimeout might trigger early // Be on the safe side and accept 200ms above the original value - assert(rest - 200 < 1000, '`rest` - 200 should be below 1000 ms but is: ' + (rest - 200)); + assert(rest - 250 < 1000, '`rest` - 250 should be below 1000 ms but is: ' + (rest - 250)); assert(delayed); end(); }); diff --git a/test/multi.spec.js b/test/multi.spec.js index 874d6201346..817433db348 100644 --- a/test/multi.spec.js +++ b/test/multi.spec.js @@ -73,7 +73,7 @@ describe("The 'multi' method", function () { describe('pipeline limit', function () { it('do not exceed maximum string size', function (done) { - this.timeout(30000); // Windows tests are horribly slow + this.timeout(process.platform !== 'win32' ? 10000 : 35000); // Windows tests are horribly slow // Triggers a RangeError: Invalid string length if not handled properly client = redis.createClient(); var multi = client.multi(); diff --git a/test/node_redis.spec.js b/test/node_redis.spec.js index be4646fe5f5..060c62926c1 100644 --- a/test/node_redis.spec.js +++ b/test/node_redis.spec.js @@ -266,7 +266,7 @@ describe('The node_redis client', function () { bclient.blpop('blocking list 2', 5, function (err, value) { assert.strictEqual(value[0], 'blocking list 2'); assert.strictEqual(value[1], 'initial value'); - return done(err); + done(err); }); bclient.once('ready', function () { setTimeout(function () { @@ -297,10 +297,11 @@ describe('The node_redis client', function () { } client.set('foo', 'bar', cb); } + client.on('warning', function () {}); // Ignore deprecation message setTimeout(function () { finished = true; done(); - }, 250); + }, 25); }); it('used with flush set to true', function (done) { @@ -314,6 +315,7 @@ describe('The node_redis client', function () { for (var i = 0; i < 20; i++) { if (i === 10) { client.end(true); + client.stream.write('foo'); // Trigger an error on the closed stream that we ignore } client.set('foo', 'bar', cb); } @@ -583,6 +585,24 @@ describe('The node_redis client', function () { }); }); + it('monitor does not activate if the command could not be processed properly', function (done) { + client.MONITOR(function (err, res) { + assert.strictEqual(err.code, 'UNCERTAIN_STATE'); + }); + client.on('error', function (err) {}); // Ignore error here + client.stream.destroy(); + client.on('monitor', function (time, args, rawOutput) { + done(new Error('failed')); // Should not be activated + }); + client.on('reconnecting', function () { + client.get('foo', function (err, res) { + assert(!err); + assert.strictEqual(client.monitoring, false); + setTimeout(done, 10); // The monitor command might be returned a tiny bit later + }); + }); + }); + it('monitors works in combination with the pub sub mode and the offline queue', function (done) { var responses = []; var pub = redis.createClient(); @@ -657,7 +677,7 @@ describe('The node_redis client', function () { client.set(['utf8test', utf8_sample], helper.isString('OK')); client.get(['utf8test'], function (err, obj) { assert.strictEqual(utf8_sample, obj); - return done(err); + done(err); }); }); }); @@ -671,13 +691,13 @@ describe('The node_redis client', function () { var id = setTimeout(function () { external.kill(); - return done(Error('unref subprocess timed out')); + done(Error('unref subprocess timed out')); }, 8000); external.on('close', function (code) { clearTimeout(id); assert.strictEqual(code, 0); - return done(); + done(); }); }); }); @@ -727,11 +747,7 @@ describe('The node_redis client', function () { client = redis.createClient.apply(null, args); client.on('ready', function () { assert.strictEqual(true, client.options.socket_nodelay); - client.quit(); - - client.once('end', function () { - return done(); - }); + client.quit(done); }); }); @@ -743,11 +759,7 @@ describe('The node_redis client', function () { client.set(['set key 2', 'set val'], helper.isString('OK')); client.get(['set key 1'], helper.isString('set val')); client.get(['set key 2'], helper.isString('set val')); - client.quit(); - - client.once('end', function () { - return done(); - }); + client.quit(done); }); }); }); @@ -761,11 +773,7 @@ describe('The node_redis client', function () { client = redis.createClient.apply(null, args); client.on('ready', function () { assert.strictEqual(false, client.options.socket_nodelay); - client.quit(); - - client.once('end', function () { - return done(); - }); + client.quit(done); }); }); @@ -777,11 +785,7 @@ describe('The node_redis client', function () { client.set(['set key 2', 'set val'], helper.isString('OK')); client.get(['set key 1'], helper.isString('set val')); client.get(['set key 2'], helper.isString('set val')); - client.quit(); - - client.once('end', function () { - return done(); - }); + client.quit(done); }); }); }); @@ -792,11 +796,7 @@ describe('The node_redis client', function () { client = redis.createClient.apply(null, args); client.on('ready', function () { assert.strictEqual(true, client.options.socket_nodelay); - client.quit(); - - client.once('end', function () { - return done(); - }); + client.quit(done); }); }); @@ -808,11 +808,7 @@ describe('The node_redis client', function () { client.set(['set key 2', 'set val'], helper.isString('OK')); client.get(['set key 1'], helper.isString('set val')); client.get(['set key 2'], helper.isString('set val')); - client.quit(); - - client.once('end', function () { - return done(); - }); + client.quit(done); }); }); }); @@ -902,19 +898,16 @@ describe('The node_redis client', function () { // ignore, b/c expecting a "can't connect" error }); - return setTimeout(function () { + setTimeout(function () { client.set('foo', 'bar', function (err, result) { - if (!finished) { - // This should never be called - return done(err); - } + if (!finished) done(err); assert.strictEqual(err.message, "The command can't be processed. The connection has already been closed."); }); - return setTimeout(function () { + setTimeout(function () { assert.strictEqual(client.offline_queue.length, 1); finished = true; - return done(); + done(); }, 25); }, 50); }); diff --git a/test/pubsub.spec.js b/test/pubsub.spec.js index 1651746a545..b24c1899dc9 100644 --- a/test/pubsub.spec.js +++ b/test/pubsub.spec.js @@ -57,7 +57,8 @@ describe('publish/subscribe', function () { sub.on('reconnecting', function () { a = true; sub.on('ready', function () { - setTimeout(done, 250); + assert.strictEqual(sub.command_queue.length, 0); + done(); }); }); diff --git a/test/tls.spec.js b/test/tls.spec.js index 68cc02d95f2..40a424c27c2 100644 --- a/test/tls.spec.js +++ b/test/tls.spec.js @@ -71,10 +71,15 @@ describe('TLS connection tests', function () { client.on('error', function (err) { if (/Redis connection in broken state: connection timeout.*?exceeded./.test(err.message)) { - setTimeout(function () { - assert(time === connect_timeout); + process.nextTick(function () { + assert.strictEqual(time, connect_timeout); + assert.strictEqual(client.emitted_end, true); + assert.strictEqual(client.connected, false); + assert.strictEqual(client.ready, false); + assert.strictEqual(client.closing, true); + assert.strictEqual(time, connect_timeout); done(); - }, 100); + }); } }); }); From b59225e35e59cff8e65de63459e6182e3451576e Mon Sep 17 00:00:00 2001 From: Ruben Bridgewater Date: Thu, 31 Mar 2016 19:12:30 +0200 Subject: [PATCH 0612/1748] Update readme --- README.md | 9 ++++----- 1 file changed, 4 insertions(+), 5 deletions(-) diff --git a/README.md b/README.md index 44906785152..1e1288ffeb2 100644 --- a/README.md +++ b/README.md @@ -129,8 +129,7 @@ then replayed just before this event is emitted. ### "connect" -`client` will emit `connect` at the same time as it emits `ready` unless `client.options.no_ready_check` -is set. If this options is set, `connect` will be emitted when the stream is connected. +`client` will emit `connect` as soon as the stream is connected to the server. ### "reconnecting" @@ -621,9 +620,9 @@ Duplicate all current options and return a new redisClient instance. All options ## client.send_command(command_name[, [args][, callback]]) -Used internally to send commands to Redis. Nearly all Redis commands have been added to the `client` object. -However, if new commands are introduced before this library is updated, you can use `send_command()` to send arbitrary commands to Redis. -The command has to be lower case. +All Redis commands have been added to the `client` object. However, if new commands are introduced before this library is updated, +you can use `send_command()` to send arbitrary commands to Redis. +The command_name has to be lower case. All commands are sent as multi-bulk commands. `args` can either be an Array of arguments, or omitted / set to undefined. From a8c2f223acaabecfb044c55bdc3bc4d2e78fe01a Mon Sep 17 00:00:00 2001 From: Ruben Bridgewater Date: Thu, 31 Mar 2016 19:13:11 +0200 Subject: [PATCH 0613/1748] Add changelog --- changelog.md | 20 ++++++++++++++++++-- 1 file changed, 18 insertions(+), 2 deletions(-) diff --git a/changelog.md b/changelog.md index bb0e22e7b5d..ae2ed508dd4 100644 --- a/changelog.md +++ b/changelog.md @@ -1,11 +1,24 @@ Changelog ========= +## v.2.6.0-1 - 01 Apr, 2016 + +A second pre-release with further fixes. This is likely going to be released as 2.6.0 stable without further changes. + +Features + +- Added type validations for client.send_command arguments + +Bugfixes + +- Fixed client.send_command not working properly with every command and every option +- Fixed pub sub mode unsubscribing from all channels in combination with the new `string_numbers` option crashing +- Fixed pub sub mode unsubscribing from all channels not respected while reconnecting +- Fixed pub sub mode events in combination with the `string_numbers` option emitting the number of channels not as number + ## v.2.6.0-0 - 27 Mar, 2016 This is mainly a very important bug fix release with some smaller features. -The quit command did not end connections earlier if the connection was down at that time and this could have -lead to strange situations, therefor this was fixed to end the connection right away in those cases. Features @@ -28,6 +41,9 @@ Bugfixes - Fixed pub sub mode emitting a message without a message published - Fixed quit command not ending the connection and resulting in further reconnection if called while reconnecting +The quit command did not end connections earlier if the connection was down at that time and this could have +lead to strange situations, therefor this was fixed to end the connection right away in those cases. + ## v.2.5.3 - 21 Mar, 2016 Bugfixes From 7ffba03be6ade31203bfe40f8425c071ff29cafb Mon Sep 17 00:00:00 2001 From: Ruben Bridgewater Date: Fri, 1 Apr 2016 00:56:44 +0200 Subject: [PATCH 0614/1748] v.2.6.0-1 --- package.json | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/package.json b/package.json index 9efc03fde89..48af96aac2a 100644 --- a/package.json +++ b/package.json @@ -1,6 +1,6 @@ { "name": "redis", - "version": "2.6.0-0", + "version": "2.6.0-1", "description": "Redis client library", "keywords": [ "database", From 6c118c311f331e8ae4032bcf6d6babc1f04402cc Mon Sep 17 00:00:00 2001 From: Brian Rossmajer Date: Thu, 7 Apr 2016 23:05:56 -0400 Subject: [PATCH 0615/1748] Noting when client.duplicate() might be used Explicitly pointing out that when blocking and non-blocking functions are used on the same connection, unexpected results can occur --- README.md | 32 ++++++++++++++++++++++++++++++++ 1 file changed, 32 insertions(+) diff --git a/README.md b/README.md index 1e1288ffeb2..5e3980605d9 100644 --- a/README.md +++ b/README.md @@ -618,6 +618,38 @@ the second word as first parameter: Duplicate all current options and return a new redisClient instance. All options passed to the duplicate function are going to replace the original option. +An example of when to use duplicate() would be to accomodate the connection- +blocking redis commands BRPOP, BLPOP, and BRPOPLPUSH. If these commands +are used on the same redisClient instance as non-blocking commands, the +non-blocking ones may be queued up until after the blocking ones finish. + + var Redis=require('redis'); + var client = Redis.createClient(); + var get = function() { + console.log("get called"); + client.get("any_key",function() { console.log("get returned"); }); + setTimeout( get, 1000 ); + }; + var brpop = function() { + console.log("brpop called"); + client.brpop("nonexistent", 5, function() { + console.log("brpop return"); + setTimeout( brpop, 1000 ); + }); + }; + get(); + brpop(); + +These two repeating functions will interfere with each other -- the `get`s will +not return until after the `brpop` returns. This can be fixed by keeping the +blocking calls separate using `client.duplicate()`, eg: + + ... + var clientBlocking = client.duplicate(); + var brpop = function() { + console.log("brpop called"); + clientBlocking.brpop( ... + ## client.send_command(command_name[, [args][, callback]]) All Redis commands have been added to the `client` object. However, if new commands are introduced before this library is updated, From 0311198f7d08ced994493ee354b8da05673a29d0 Mon Sep 17 00:00:00 2001 From: Kyle Date: Fri, 15 Apr 2016 11:35:13 -0400 Subject: [PATCH 0616/1748] Altered createClient readme Fixed typos, made language consistent, removed opinion, switched from bullets to table --- README.md | 77 ++++++++++++++++++++++--------------------------------- 1 file changed, 31 insertions(+), 46 deletions(-) diff --git a/README.md b/README.md index 1e1288ffeb2..f36b2533b50 100644 --- a/README.md +++ b/README.md @@ -166,57 +166,42 @@ If false is returned the stream had to buffer. `client` will emit `idle` when there are no outstanding commands that are awaiting a response. ## redis.createClient() -If you have `redis-server` running on the same computer as node, then the defaults for -port and host are probably fine and you don't need to supply any arguments. `createClient()` returns a `RedisClient` object. +If you have `redis-server` running on the same machine as node, then the defaults for +port and host are probably fine and you don't need to supply any arguments. `createClient()` returns a `RedisClient` object. Otherwise, `createClient()` accepts these arguments: -If the redis server runs on the same machine as the client consider using unix sockets if possible to increase throughput. - -### overloading * `redis.createClient([options])` -* `redis.createClient(unix_socket[, options])` +* `redis.createClient(unix_socket[, options])` * `redis.createClient(redis_url[, options])` * `redis.createClient(port[, host][, options])` -#### `options` is an object with the following possible properties: -* `host`: *127.0.0.1*; The host to connect to -* `port`: *6379*; The port to connect to -* `path`: *null*; The unix socket string to connect to -* `url`: *null*; The redis url to connect to (`[redis:]//[user][:password@][host][:port][/db-number][?db=db-number[&password=bar[&option=value]]]` For more info check [IANA](http://www.iana.org/assignments/uri-schemes/prov/redis)) -* `parser`: *hiredis*; Which Redis protocol reply parser to use. If `hiredis` is not installed it will fallback to `javascript`. -* `string_numbers`: *boolean*; pass true to get numbers back as strings instead of js numbers. This is necessary if you want to handle big numbers (above `Number.MAX_SAFE_INTEGER` === 2^53). If passed, the js parser is automatically choosen as parser no matter if the parser is set to hiredis or not, as hiredis is not capable of doing this. -* `return_buffers`: *false*; If set to `true`, then all replies will be sent to callbacks as Buffers instead of Strings. -* `detect_buffers`: *false*; If set to `true`, then replies will be sent to callbacks as Buffers. Please be aware that this can't work properly with the pubsub mode. A subscriber has to either always return strings or buffers. -if any of the input arguments to the original command were Buffers. -This option lets you switch between Buffers and Strings on a per-command basis, whereas `return_buffers` applies to -every command on a client. -* `socket_keepalive` *true*; Whether the keep-alive functionality is enabled on the underlying socket. -* `no_ready_check`: *false*; When a connection is established to the Redis server, the server might still -be loading the database from disk. While loading the server will not respond to any commands. To work around this, -`node_redis` has a "ready check" which sends the `INFO` command to the server. The response from the `INFO` command -indicates whether the server is ready for more commands. When ready, `node_redis` emits a `ready` event. -Setting `no_ready_check` to `true` will inhibit this check. -* `enable_offline_queue`: *true*; By default, if there is no active -connection to the redis server, commands are added to a queue and are executed -once the connection has been established. Setting `enable_offline_queue` to -`false` will disable this feature and the callback will be executed immediately -with an error, or an error will be emitted if no callback is specified. -* `retry_max_delay`: *null*; By default every time the client tries to connect and fails the reconnection delay almost doubles. -This delay normally grows infinitely, but setting `retry_max_delay` limits it to the maximum value, provided in milliseconds. -* `connect_timeout`: *3600000*; Setting `connect_timeout` limits total time for client to connect and reconnect. -The value is provided in milliseconds and is counted from the moment on a new client is created / a connection is lost. The last retry is going to happen exactly at the timeout time. -Default is to try connecting until the default system socket timeout has been exceeded and to try reconnecting until 1h passed. -* `max_attempts`: *0*; (Deprecated, please use `retry_strategy` instead) By default client will try reconnecting until connected. Setting `max_attempts` -limits total amount of connection tries. Setting this to 1 will prevent any reconnect tries. -* `retry_unfulfilled_commands`: *false*; If set to true, all commands that were unfulfulled while the connection is lost will be retried after the connection has reestablished again. Use this with caution, if you use state altering commands (e.g. *incr*). This is especially useful if you use blocking commands. -* `password`: *null*; If set, client will run redis auth command on connect. Alias `auth_pass` (node_redis < 2.5 have to use auth_pass) -* `db`: *null*; If set, client will run redis select command on connect. This is [not recommended](https://groups.google.com/forum/#!topic/redis-db/vS5wX8X4Cjg). -* `family`: *IPv4*; You can force using IPv6 if you set the family to 'IPv6'. See Node.js [net](https://nodejs.org/api/net.html) or [dns](https://nodejs.org/api/dns.html) modules how to use the family type. -* `disable_resubscribing`: *false*; If set to `true`, a client won't resubscribe after disconnecting -* `rename_commands`: *null*; pass a object with renamed commands to use those instead of the original functions. See the [redis security topics](http://redis.io/topics/security) for more info. -* `tls`: an object containing options to pass to [tls.connect](http://nodejs.org/api/tls.html#tls_tls_connect_port_host_options_callback), -to set up a TLS connection to Redis (if, for example, it is set up to be accessible via a tunnel). -* `prefix`: *null*; pass a string to prefix all used keys with that string as prefix e.g. 'namespace:test'. Please be aware, that the "keys" command is not going to be prefixed. The command has a "pattern" as argument and no key and it would be impossible to determine the existing keys in Redis if this would be prefixed. -* `retry_strategy`: *function*; pass a function that receives a options object as parameter including the retry `attempt`, the `total_retry_time` indicating how much time passed since the last time connected, the `error` why the connection was lost and the number of `times_connected` in total. If you return a number from this function, the retry will happen exactly after that time in milliseconds. If you return a non-number no further retry is going to happen and all offline commands are flushed with errors. Return a error to return that specific error to all offline commands. Check out the example too. +__Tip:__ If the Redis server runs on the same machine as the client consider using unix sockets if possible to increase throughput. + +#### `options` object properties +| Property | Default | Description | +|-----------|-----------|-------------| +| host | 127.0.0.1 | IP address of the Redis server | +| port | 6379 | Port of the Redis server | +| path | null | The UNIX socket string of the Redis server | +| url | null | The URL of the Redis server. Uses the format `[redis:]//[user][:password@][host][:port][/db-number][?db=db-number[&password=bar[&option=value]]]` More info avaliable at [IANA](http://www.iana.org/assignments/uri-schemes/prov/redis) | +| parser | hiredis | If hiredis is not installed, automatic fallback to the built-in javascript parser | +| string_numbers | null | Set to `true`, `node_redis will return Redis number values as Strings instead of javascript Numbers. Useful if you need to handle big numbers (above `Number.MAX_SAFE_INTEGER === 2^53`). Hiredis is incapable of this behavior, so setting this option to `true` will result in the built-in javascript parser being used no matter the value of the `parser` option. | +| return_buffers | false | If set to `true`, then all replies will be sent to callbacks as Buffers instead of Strings. | +| detect_buffers | false | If set to `true`, then replies will be sent to callbacks as Buffers. This option lets you switch between Buffers and Strings on a per-command basis, whereas `return_buffers` applies to every command on a client. __Note__: This doesn't work properly with the pubsub mode. A subscriber has to either always return strings or buffers. | +| socket_keepalive | true | If set to `true`, the keep-alive functionality is enabled on the underlying socket | +| no_ready_check | false | When a connection is established to the Redis server, the server might still be loading the database from disk. While loading the server will not respond to any commands. To work around this, `node_redis` has a "ready check" which sends the `INFO` command to the server. The response from the `INFO` command indicates whether the server is ready for more commands. When ready, `node_redis` emits a `ready` event. Setting `no_ready_check` to `true` will inhibit this check. | +| enable_offline_queue | true | By default, if there is no active connection to the Redis server, commands are added to a queue and are executed once the connection has been established. Setting `enable_offline_queue` to `false` will disable this feature and the callback will be executed immediately with an error, or an error will be emitted if no callback is specified. | +| retry_max_delay | null | By default, every time the client tries to connect and fails the reconnection delay almost doubles. This delay normally grows infinitely, but setting `retry_max_delay` limits it to the maximum value, provided in milliseconds. | +| connect_timeout | 3600000 | Setting `connect_timeout` limits the total time for the client to connect and reconnect. The value is provided in milliseconds and is counted from the moment a new client is created or when a connection is lost. The last retry is going to happen exactly at the timeout time. Default is to try connecting until the default system socket timeout has been exceeded and to try reconnecting until 1h has elapsed. | +| max_attempts | 0 | __Deprecated__ _Please use `retry_strategy` instead._ By default client will try reconnecting until connected. Setting `max_attempts` limits total amount of connection tries. Setting this to 1 will prevent any reconnect tries. | +| retry_unfulfilled_commands | false | If set to `true`, all commands that were unfulfilled while the connection is lost will be retried after the connection has been reestablished. Use this with caution, if you use state altering commands (e.g. `incr`). This is especially useful if you use blocking commands. | +| password | null | If set, client will run Redis auth command on connect. Alias `auth_pass` __Note__ `node_redis` < 2.5 must use `auth_pass` | +| db | null | If set, client will run Redis `select` command on connect. | +| family | IPv4 | You can force using IPv6 if you set the family to 'IPv6'. See Node.js [net](https://nodejs.org/api/net.html) or [dns](https://nodejs.org/api/dns.html) modules how to use the family type. | +| disable_resubscribing | false | If set to `true`, a client won't resubscribe after disconnecting | +| rename_commands | null | Passing a object with renamed commands to use instead of the original functions. See the [Redis security topics](http://redis.io/topics/security) for more info. | +| tls | null | An object containing options to pass to [tls.connect](http://nodejs.org/api/tls.html#tls_tls_connect_port_host_options_callback) to set up a TLS connection to Redis (if, for example, it is set up to be accessible via a tunnel). | +| prefix | null | String to prefix all used keys with that string as prefix (e.g. `namespace:test`). Please be aware, that the `keys` command will not be prefixed. The `keys` command has a "pattern" as argument and no key and it would be impossible to determine the existing keys in Redis if this would be prefixed. | +| retry_strategy | function | Function that receives a options object as parameter including the retry `attempt`, the `total_retry_time` indicating how much time passed since the last time connected, the `error` why the connection was lost and the number of `times_connected` in total. If you return a number from this function, the retry will happen exactly after that time in milliseconds. If you return a non-number no further retry will happen and all offline commands are flushed with errors. Return a error to return that specific error to all offline commands. Example below. | ```js var redis = require("redis"); From c2c57ded9e701a6c179b0cff87a03cf0d80cd136 Mon Sep 17 00:00:00 2001 From: Ruben Bridgewater Date: Thu, 21 Apr 2016 04:07:09 +0200 Subject: [PATCH 0617/1748] Readme grammar fixes --- README.md | 30 +++++++++++++++--------------- 1 file changed, 15 insertions(+), 15 deletions(-) diff --git a/README.md b/README.md index f36b2533b50..e91da2973bf 100644 --- a/README.md +++ b/README.md @@ -170,7 +170,7 @@ If you have `redis-server` running on the same machine as node, then the default port and host are probably fine and you don't need to supply any arguments. `createClient()` returns a `RedisClient` object. Otherwise, `createClient()` accepts these arguments: * `redis.createClient([options])` -* `redis.createClient(unix_socket[, options])` +* `redis.createClient(unix_socket[, options])` * `redis.createClient(redis_url[, options])` * `redis.createClient(port[, host][, options])` @@ -182,26 +182,26 @@ __Tip:__ If the Redis server runs on the same machine as the client consider usi | host | 127.0.0.1 | IP address of the Redis server | | port | 6379 | Port of the Redis server | | path | null | The UNIX socket string of the Redis server | -| url | null | The URL of the Redis server. Uses the format `[redis:]//[user][:password@][host][:port][/db-number][?db=db-number[&password=bar[&option=value]]]` More info avaliable at [IANA](http://www.iana.org/assignments/uri-schemes/prov/redis) | +| url | null | The URL of the Redis server. Format: `[redis:]//[user][:password@][host][:port][/db-number][?db=db-number[&password=bar[&option=value]]]` (More info avaliable at [IANA](http://www.iana.org/assignments/uri-schemes/prov/redis)). | | parser | hiredis | If hiredis is not installed, automatic fallback to the built-in javascript parser | -| string_numbers | null | Set to `true`, `node_redis will return Redis number values as Strings instead of javascript Numbers. Useful if you need to handle big numbers (above `Number.MAX_SAFE_INTEGER === 2^53`). Hiredis is incapable of this behavior, so setting this option to `true` will result in the built-in javascript parser being used no matter the value of the `parser` option. | +| string_numbers | null | Set to `true`, `node_redis` will return Redis number values as Strings instead of javascript Numbers. Useful if you need to handle big numbers (above `Number.MAX_SAFE_INTEGER === 2^53`). Hiredis is incapable of this behavior, so setting this option to `true` will result in the built-in javascript parser being used no matter the value of the `parser` option. | | return_buffers | false | If set to `true`, then all replies will be sent to callbacks as Buffers instead of Strings. | -| detect_buffers | false | If set to `true`, then replies will be sent to callbacks as Buffers. This option lets you switch between Buffers and Strings on a per-command basis, whereas `return_buffers` applies to every command on a client. __Note__: This doesn't work properly with the pubsub mode. A subscriber has to either always return strings or buffers. | -| socket_keepalive | true | If set to `true`, the keep-alive functionality is enabled on the underlying socket | -| no_ready_check | false | When a connection is established to the Redis server, the server might still be loading the database from disk. While loading the server will not respond to any commands. To work around this, `node_redis` has a "ready check" which sends the `INFO` command to the server. The response from the `INFO` command indicates whether the server is ready for more commands. When ready, `node_redis` emits a `ready` event. Setting `no_ready_check` to `true` will inhibit this check. | +| detect_buffers | false | If set to `true`, then replies will be sent to callbacks as Buffers. This option lets you switch between Buffers and Strings on a per-command basis, whereas `return_buffers` applies to every command on a client. __Note__: This doesn't work properly with the pubsub mode. A subscriber has to either always return Strings or Buffers. | +| socket_keepalive | true | If set to `true`, the keep-alive functionality is enabled on the underlying socket. | +| no_ready_check | false | When a connection is established to the Redis server, the server might still be loading the database from disk. While loading, the server will not respond to any commands. To work around this, `node_redis` has a "ready check" which sends the `INFO` command to the server. The response from the `INFO` command indicates whether the server is ready for more commands. When ready, `node_redis` emits a `ready` event. Setting `no_ready_check` to `true` will inhibit this check. | | enable_offline_queue | true | By default, if there is no active connection to the Redis server, commands are added to a queue and are executed once the connection has been established. Setting `enable_offline_queue` to `false` will disable this feature and the callback will be executed immediately with an error, or an error will be emitted if no callback is specified. | -| retry_max_delay | null | By default, every time the client tries to connect and fails the reconnection delay almost doubles. This delay normally grows infinitely, but setting `retry_max_delay` limits it to the maximum value, provided in milliseconds. | -| connect_timeout | 3600000 | Setting `connect_timeout` limits the total time for the client to connect and reconnect. The value is provided in milliseconds and is counted from the moment a new client is created or when a connection is lost. The last retry is going to happen exactly at the timeout time. Default is to try connecting until the default system socket timeout has been exceeded and to try reconnecting until 1h has elapsed. | -| max_attempts | 0 | __Deprecated__ _Please use `retry_strategy` instead._ By default client will try reconnecting until connected. Setting `max_attempts` limits total amount of connection tries. Setting this to 1 will prevent any reconnect tries. | -| retry_unfulfilled_commands | false | If set to `true`, all commands that were unfulfilled while the connection is lost will be retried after the connection has been reestablished. Use this with caution, if you use state altering commands (e.g. `incr`). This is especially useful if you use blocking commands. | +| retry_max_delay | null | By default, every time the client tries to connect and fails, the reconnection delay almost doubles. This delay normally grows infinitely, but setting `retry_max_delay` limits it to the maximum value provided in milliseconds. | +| connect_timeout | 3600000 | Setting `connect_timeout` limits the total time for the client to connect and reconnect. The value is provided in milliseconds and is counted from the moment a new client is created or from the time the connection is lost. The last retry is going to happen exactly at the timeout time. Default is to try connecting until the default system socket timeout has been exceeded and to try reconnecting until 1h has elapsed. | +| max_attempts | 0 | __Deprecated__ _Please use `retry_strategy` instead._ By default, a client will try reconnecting until connected. Setting `max_attempts` limits total amount of connection attempts. Setting this to 1 will prevent any reconnect attempt. | +| retry_unfulfilled_commands | false | If set to `true`, all commands that were unfulfilled while the connection is lost will be retried after the connection has been reestablished. Use this with caution if you use state altering commands (e.g. `incr`). This is especially useful if you use blocking commands. | | password | null | If set, client will run Redis auth command on connect. Alias `auth_pass` __Note__ `node_redis` < 2.5 must use `auth_pass` | | db | null | If set, client will run Redis `select` command on connect. | -| family | IPv4 | You can force using IPv6 if you set the family to 'IPv6'. See Node.js [net](https://nodejs.org/api/net.html) or [dns](https://nodejs.org/api/dns.html) modules how to use the family type. | -| disable_resubscribing | false | If set to `true`, a client won't resubscribe after disconnecting | -| rename_commands | null | Passing a object with renamed commands to use instead of the original functions. See the [Redis security topics](http://redis.io/topics/security) for more info. | +| family | IPv4 | You can force using IPv6 if you set the family to 'IPv6'. See Node.js [net](https://nodejs.org/api/net.html) or [dns](https://nodejs.org/api/dns.html) modules on how to use the family type. | +| disable_resubscribing | false | If set to `true`, a client won't resubscribe after disconnecting. | +| rename_commands | null | Passing an object with renamed commands to use instead of the original functions. See the [Redis security topics](http://redis.io/topics/security) for more info. | | tls | null | An object containing options to pass to [tls.connect](http://nodejs.org/api/tls.html#tls_tls_connect_port_host_options_callback) to set up a TLS connection to Redis (if, for example, it is set up to be accessible via a tunnel). | -| prefix | null | String to prefix all used keys with that string as prefix (e.g. `namespace:test`). Please be aware, that the `keys` command will not be prefixed. The `keys` command has a "pattern" as argument and no key and it would be impossible to determine the existing keys in Redis if this would be prefixed. | -| retry_strategy | function | Function that receives a options object as parameter including the retry `attempt`, the `total_retry_time` indicating how much time passed since the last time connected, the `error` why the connection was lost and the number of `times_connected` in total. If you return a number from this function, the retry will happen exactly after that time in milliseconds. If you return a non-number no further retry will happen and all offline commands are flushed with errors. Return a error to return that specific error to all offline commands. Example below. | +| prefix | null | A string used to prefix all used keys (e.g. `namespace:test`). Please be aware that the `keys` command will not be prefixed. The `keys` command has a "pattern" as argument and no key and it would be impossible to determine the existing keys in Redis if this would be prefixed. | +| retry_strategy | function | A function that receives an options object as parameter including the retry `attempt`, the `total_retry_time` indicating how much time passed since the last time connected, the `error` why the connection was lost and the number of `times_connected` in total. If you return a number from this function, the retry will happen exactly after that time in milliseconds. If you return a non-number, no further retry will happen and all offline commands are flushed with errors. Return an error to return that specific error to all offline commands. Example below. | ```js var redis = require("redis"); From dfb1d93a4fd59b21843fae9ab8af4b85efcbf8aa Mon Sep 17 00:00:00 2001 From: Ruben Bridgewater Date: Wed, 13 Apr 2016 03:42:25 +0200 Subject: [PATCH 0618/1748] Explicitly ask for the platform in the issue template --- .github/ISSUE_TEMPLATE.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.github/ISSUE_TEMPLATE.md b/.github/ISSUE_TEMPLATE.md index fb90edf57ad..42f2d3eeb61 100644 --- a/.github/ISSUE_TEMPLATE.md +++ b/.github/ISSUE_TEMPLATE.md @@ -9,7 +9,7 @@ to a small test case, but it's highly appreciated to have as much data as possib Thank you!_ * **Version**: What node_redis and what redis version is the issue happening on? -* **Platform**: What platform / version? (For example Node.js 0.10 or Node.js 5.7.0) +* **Platform**: What platform / version? (For example Node.js 0.10 or Node.js 5.7.0 on Windows 7 / Ubuntu 15.10 / Azure) * **Description**: Description of your issue, stack traces from errors and code that reproduces the issue [gitter]: https://gitter.im/NodeRedis/node_redis?utm_source=badge&utm_medium=badge&utm_campaign=pr-badge \ No newline at end of file From 4e529814d40416250e3f19d9c702d373787e00ee Mon Sep 17 00:00:00 2001 From: Ruben Bridgewater Date: Wed, 13 Apr 2016 03:46:38 +0200 Subject: [PATCH 0619/1748] Small Readme fixes --- README.md | 12 +++++------- 1 file changed, 5 insertions(+), 7 deletions(-) diff --git a/README.md b/README.md index e91da2973bf..8bbbd501df3 100644 --- a/README.md +++ b/README.md @@ -12,9 +12,7 @@ Install with: npm install redis -## Usage - -Simple example, included as `examples/simple.js`: +## Usage Example ```js var redis = require("redis"), @@ -83,7 +81,7 @@ return client.multi().get('foo').execAsync().then(function(res) { Each Redis command is exposed as a function on the `client` object. All functions take either an `args` Array plus optional `callback` Function or a variable number of individual arguments followed by an optional callback. -Here are examples how to use the api: +Examples: ```js client.hmset(["key", "test keys 1", "test val 1", "test keys 2", "test val 2"], function (err, res) {}); @@ -182,7 +180,7 @@ __Tip:__ If the Redis server runs on the same machine as the client consider usi | host | 127.0.0.1 | IP address of the Redis server | | port | 6379 | Port of the Redis server | | path | null | The UNIX socket string of the Redis server | -| url | null | The URL of the Redis server. Format: `[redis:]//[user][:password@][host][:port][/db-number][?db=db-number[&password=bar[&option=value]]]` (More info avaliable at [IANA](http://www.iana.org/assignments/uri-schemes/prov/redis)). | +| url | null | The URL of the Redis server. Format: `[redis:]//[[user][:password@]][host][:port][/db-number][?db=db-number[&password=bar[&option=value]]]` (More info avaliable at [IANA](http://www.iana.org/assignments/uri-schemes/prov/redis)). | | parser | hiredis | If hiredis is not installed, automatic fallback to the built-in javascript parser | | string_numbers | null | Set to `true`, `node_redis` will return Redis number values as Strings instead of javascript Numbers. Useful if you need to handle big numbers (above `Number.MAX_SAFE_INTEGER === 2^53`). Hiredis is incapable of this behavior, so setting this option to `true` will result in the built-in javascript parser being used no matter the value of the `parser` option. | | return_buffers | false | If set to `true`, then all replies will be sent to callbacks as Buffers instead of Strings. | @@ -363,7 +361,7 @@ client.HMSET(key1, "0123456789", "abcdefghij", "some manner of key", "a type of ## Publish / Subscribe -Here is a simple example of the API for publish / subscribe. This program opens two +Example of the publish / subscribe API. This program opens two client connections, subscribes to a channel on one of them, and publishes to that channel on the other: @@ -539,7 +537,7 @@ across all client connections, including from other client libraries and other c A `monitor` event is going to be emitted for every command fired from any client connected to the server including the monitoring client itself. The callback for the `monitor` event takes a timestamp from the Redis server, an array of command arguments and the raw monitoring string. -Here is a simple example: +Example: ```js var client = require("redis").createClient(); From a11e0c5ff9f47da89e763cfe46a9bcd6f02b5098 Mon Sep 17 00:00:00 2001 From: Ruben Bridgewater Date: Wed, 13 Apr 2016 03:46:51 +0200 Subject: [PATCH 0620/1748] Don't expose internal variables --- README.md | 15 ++------------- 1 file changed, 2 insertions(+), 13 deletions(-) diff --git a/README.md b/README.md index 8bbbd501df3..d2b82a0ee2a 100644 --- a/README.md +++ b/README.md @@ -613,27 +613,16 @@ All commands are sent as multi-bulk commands. `args` can either be an Array of a Boolean tracking the state of the connection to the Redis server. -## client.command_queue.length +## client.command_queue_length The number of commands that have been sent to the Redis server but not yet replied to. You can use this to enforce some kind of maximum queue depth for commands while connected. -Don't mess with `client.command_queue` though unless you really know what you are doing. - -## client.offline_queue.length +## client.offline_queue_length The number of commands that have been queued up for a future connection. You can use this to enforce some kind of maximum queue depth for pre-connection commands. -## client.retry_delay - -Current delay in milliseconds before a connection retry will be attempted. This starts at `200`. - -## client.retry_backoff - -Multiplier for future retry timeouts. This should be larger than 1 to add more time between retries. -Defaults to 1.7. The default initial connection retry is 200, so the second retry will be 340, followed by 578, etc. - ### Commands with Optional and Keyword arguments This applies to anything that uses an optional `[WITHSCORES]` or `[LIMIT offset count]` in the [redis.io/commands](http://redis.io/commands) documentation. From 228573b8d78c7bb3d00c28413093422428b27325 Mon Sep 17 00:00:00 2001 From: Ruben Bridgewater Date: Wed, 13 Apr 2016 03:48:33 +0200 Subject: [PATCH 0621/1748] Support __proto__ and similar as object attribute in hgetall --- lib/rawObject.js | 8 ++++++ lib/utils.js | 4 ++- test/commands/hgetall.spec.js | 4 +-- test/pubsub.spec.js | 46 +++++++++-------------------------- 4 files changed, 25 insertions(+), 37 deletions(-) create mode 100644 lib/rawObject.js diff --git a/lib/rawObject.js b/lib/rawObject.js new file mode 100644 index 00000000000..26376fc3608 --- /dev/null +++ b/lib/rawObject.js @@ -0,0 +1,8 @@ +'use strict'; + +// Using a predefined object with this prototype is faster than calling `Object.create(null)` directly +// This is needed to make sure `__proto__` and similar reserved words can be used +function RawObject () {} +RawObject.prototype = Object.create(null); + +module.exports = RawObject; diff --git a/lib/utils.js b/lib/utils.js index 61e9a64f755..4093b61dcbd 100644 --- a/lib/utils.js +++ b/lib/utils.js @@ -1,5 +1,7 @@ 'use strict'; +var RawObject = require('./rawObject'); + // hgetall converts its replies to an Object. If the reply is empty, null is returned. // These function are only called with internal data and have therefor always the same instanceof X function replyToObject (reply) { @@ -7,7 +9,7 @@ function replyToObject (reply) { if (reply.length === 0 || !(reply instanceof Array)) { return null; } - var obj = {}; + var obj = new RawObject(); for (var i = 0; i < reply.length; i += 2) { obj[reply[i].toString('binary')] = reply[i + 1]; } diff --git a/test/commands/hgetall.spec.js b/test/commands/hgetall.spec.js index d345b13c922..55a0e247803 100644 --- a/test/commands/hgetall.spec.js +++ b/test/commands/hgetall.spec.js @@ -22,10 +22,10 @@ describe("The 'hgetall' method", function () { }); it('handles simple keys and values', function (done) { - client.hmset(['hosts', 'mjr', '1', 'another', '23', 'home', '1234'], helper.isString('OK')); + client.hmset(['hosts', '__proto__', '1', 'another', '23', 'home', '1234'], helper.isString('OK')); client.HGETALL(['hosts'], function (err, obj) { assert.strictEqual(3, Object.keys(obj).length); - assert.strictEqual('1', obj.mjr.toString()); + assert.strictEqual('1', obj.__proto__.toString()); // eslint-disable-line no-proto assert.strictEqual('23', obj.another.toString()); assert.strictEqual('1234', obj.home.toString()); return done(err); diff --git a/test/pubsub.spec.js b/test/pubsub.spec.js index b24c1899dc9..9cb388b03e1 100644 --- a/test/pubsub.spec.js +++ b/test/pubsub.spec.js @@ -206,49 +206,27 @@ describe('publish/subscribe', function () { sub.subscribe(channel); }); - it('handles SUBSCRIBE_CLOSE_RESUBSCRIBE', function (done) { + it('subscribe; close; resubscribe with prototype inherited property names', function (done) { var count = 0; - /* Create two clients. c1 subscribes to two channels, c2 will publish to them. - c2 publishes the first message. - c1 gets the message and drops its connection. It must resubscribe itself. - When it resubscribes, c2 publishes the second message, on the same channel - c1 gets the message and drops its connection. It must resubscribe itself, again. - When it resubscribes, c2 publishes the third message, on the second channel - c1 gets the message and drops its connection. When it reconnects, the test ends. - */ + var channels = ['__proto__', 'channel 2']; + var msg = ['hi from channel __proto__', 'hi from channel 2']; + sub.on('message', function (channel, message) { - if (channel === 'chan1') { - assert.strictEqual(message, 'hi on channel 1'); - sub.stream.end(); - } else if (channel === 'chan2') { - assert.strictEqual(message, 'hi on channel 2'); - sub.stream.end(); - } else { - sub.quit(); - pub.quit(); - assert.fail('test failed'); - } + var n = Math.max(count - 1, 0); + assert.strictEqual(channel, channels[n]); + assert.strictEqual(message, msg[n]); + if (count === 2) return done(); + sub.stream.end(); }); - sub.subscribe('chan1', 'chan2'); + sub.subscribe(channels); sub.on('ready', function (err, results) { + pub.publish(channels[count], msg[count]); count++; - if (count === 1) { - pub.publish('chan1', 'hi on channel 1'); - return; - } else if (count === 2) { - pub.publish('chan2', 'hi on channel 2'); - } else { - sub.quit(function () { - pub.quit(function () { - return done(); - }); - }); - } }); - pub.publish('chan1', 'hi on channel 1'); + pub.publish(channels[count], msg[count]); }); }); From 5e42302636432d0d997b72cda3a3f45609c5ecab Mon Sep 17 00:00:00 2001 From: Ruben Bridgewater Date: Wed, 13 Apr 2016 03:50:01 +0200 Subject: [PATCH 0622/1748] Accept arbitrary arguments in the debug function --- lib/debug.js | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/lib/debug.js b/lib/debug.js index 3f9d482bbca..0e6333f2ec3 100644 --- a/lib/debug.js +++ b/lib/debug.js @@ -2,9 +2,9 @@ var index = require('../'); -function debug (msg) { +function debug () { if (index.debug_mode) { - console.error(msg); + console.error.apply(null, arguments); } } From dfd493f6ee3ed49f47ad02be95d88b7e298dde17 Mon Sep 17 00:00:00 2001 From: Ruben Bridgewater Date: Wed, 13 Apr 2016 03:51:06 +0200 Subject: [PATCH 0623/1748] Update benchmark file --- benchmarks/multi_bench.js | 42 ++++++++++++++++++++------------------- 1 file changed, 22 insertions(+), 20 deletions(-) diff --git a/benchmarks/multi_bench.js b/benchmarks/multi_bench.js index f13b9bd845b..d93fce4439e 100644 --- a/benchmarks/multi_bench.js +++ b/benchmarks/multi_bench.js @@ -23,6 +23,7 @@ function returnArg (name, def) { } var num_clients = returnArg('clients', 1); var run_time = returnArg('time', 2500); // ms +var pipeline = returnArg('pipeline', 1); // number of concurrent commands var versions_logged = false; var client_options = { parser: returnArg('parser', 'hiredis'), @@ -41,17 +42,18 @@ function lpad (input, len, chr) { metrics.Histogram.prototype.print_line = function () { var obj = this.printObj(); - return lpad((obj.min / 1e6).toFixed(2), 6) + '/' + lpad((obj.max / 1e6).toFixed(2), 6) + '/' + lpad((obj.mean / 1e6).toFixed(2), 6); + return lpad((obj.mean / 1e6).toFixed(2), 6) + '/' + lpad((obj.max / 1e6).toFixed(2), 6); }; function Test (args) { this.args = args; + this.args.pipeline = +pipeline; this.callback = null; this.clients = []; this.clients_ready = 0; this.commands_sent = 0; this.commands_completed = 0; - this.max_pipeline = this.args.pipeline || 50; + this.max_pipeline = +pipeline; this.batch_pipeline = this.args.batch || 0; this.client_options = args.client_options || {}; this.client_options.parser = client_options.parser; @@ -206,7 +208,7 @@ Test.prototype.print_stats = function () { var duration = Date.now() - this.test_start; totalTime += duration; - console.log('min/max/avg: ' + this.command_latency.print_line() + ' ' + lpad(duration, 6) + 'ms total, ' + + console.log('avg/max: ' + this.command_latency.print_line() + lpad(duration, 5) + 'ms total, ' + lpad(Math.round(this.commands_completed / (duration / 1000)), 7) + ' ops/sec'); }; @@ -217,55 +219,55 @@ large_buf = new Buffer(large_str); very_large_str = (new Array((4 * 1024 * 1024) + 1).join('-')); very_large_buf = new Buffer(very_large_str); -tests.push(new Test({descr: 'PING', command: 'ping', args: [], pipeline: 1})); +tests.push(new Test({descr: 'PING', command: 'ping', args: []})); tests.push(new Test({descr: 'PING', command: 'ping', args: [], batch: 50})); -tests.push(new Test({descr: 'SET 4B str', command: 'set', args: ['foo_rand000000000000', small_str], pipeline: 1})); +tests.push(new Test({descr: 'SET 4B str', command: 'set', args: ['foo_rand000000000000', small_str]})); tests.push(new Test({descr: 'SET 4B str', command: 'set', args: ['foo_rand000000000000', small_str], batch: 50})); -tests.push(new Test({descr: 'SET 4B buf', command: 'set', args: ['foo_rand000000000000', small_buf], pipeline: 1})); +tests.push(new Test({descr: 'SET 4B buf', command: 'set', args: ['foo_rand000000000000', small_buf]})); tests.push(new Test({descr: 'SET 4B buf', command: 'set', args: ['foo_rand000000000000', small_buf], batch: 50})); -tests.push(new Test({descr: 'GET 4B str', command: 'get', args: ['foo_rand000000000000'], pipeline: 1})); +tests.push(new Test({descr: 'GET 4B str', command: 'get', args: ['foo_rand000000000000']})); tests.push(new Test({descr: 'GET 4B str', command: 'get', args: ['foo_rand000000000000'], batch: 50})); -tests.push(new Test({descr: 'GET 4B buf', command: 'get', args: ['foo_rand000000000000'], pipeline: 1, client_opts: { return_buffers: true} })); +tests.push(new Test({descr: 'GET 4B buf', command: 'get', args: ['foo_rand000000000000'], client_opts: { return_buffers: true} })); tests.push(new Test({descr: 'GET 4B buf', command: 'get', args: ['foo_rand000000000000'], batch: 50, client_opts: { return_buffers: true} })); -tests.push(new Test({descr: 'SET 4KiB str', command: 'set', args: ['foo_rand000000000001', large_str], pipeline: 1})); +tests.push(new Test({descr: 'SET 4KiB str', command: 'set', args: ['foo_rand000000000001', large_str]})); tests.push(new Test({descr: 'SET 4KiB str', command: 'set', args: ['foo_rand000000000001', large_str], batch: 50})); -tests.push(new Test({descr: 'SET 4KiB buf', command: 'set', args: ['foo_rand000000000001', large_buf], pipeline: 1})); +tests.push(new Test({descr: 'SET 4KiB buf', command: 'set', args: ['foo_rand000000000001', large_buf]})); tests.push(new Test({descr: 'SET 4KiB buf', command: 'set', args: ['foo_rand000000000001', large_buf], batch: 50})); -tests.push(new Test({descr: 'GET 4KiB str', command: 'get', args: ['foo_rand000000000001'], pipeline: 1})); +tests.push(new Test({descr: 'GET 4KiB str', command: 'get', args: ['foo_rand000000000001']})); tests.push(new Test({descr: 'GET 4KiB str', command: 'get', args: ['foo_rand000000000001'], batch: 50})); -tests.push(new Test({descr: 'GET 4KiB buf', command: 'get', args: ['foo_rand000000000001'], pipeline: 1, client_opts: { return_buffers: true} })); +tests.push(new Test({descr: 'GET 4KiB buf', command: 'get', args: ['foo_rand000000000001'], client_opts: { return_buffers: true} })); tests.push(new Test({descr: 'GET 4KiB buf', command: 'get', args: ['foo_rand000000000001'], batch: 50, client_opts: { return_buffers: true} })); -tests.push(new Test({descr: 'INCR', command: 'incr', args: ['counter_rand000000000000'], pipeline: 1})); +tests.push(new Test({descr: 'INCR', command: 'incr', args: ['counter_rand000000000000']})); tests.push(new Test({descr: 'INCR', command: 'incr', args: ['counter_rand000000000000'], batch: 50})); -tests.push(new Test({descr: 'LPUSH', command: 'lpush', args: ['mylist', small_str], pipeline: 1})); +tests.push(new Test({descr: 'LPUSH', command: 'lpush', args: ['mylist', small_str]})); tests.push(new Test({descr: 'LPUSH', command: 'lpush', args: ['mylist', small_str], batch: 50})); -tests.push(new Test({descr: 'LRANGE 10', command: 'lrange', args: ['mylist', '0', '9'], pipeline: 1})); +tests.push(new Test({descr: 'LRANGE 10', command: 'lrange', args: ['mylist', '0', '9']})); tests.push(new Test({descr: 'LRANGE 10', command: 'lrange', args: ['mylist', '0', '9'], batch: 50})); -tests.push(new Test({descr: 'LRANGE 100', command: 'lrange', args: ['mylist', '0', '99'], pipeline: 1})); +tests.push(new Test({descr: 'LRANGE 100', command: 'lrange', args: ['mylist', '0', '99']})); tests.push(new Test({descr: 'LRANGE 100', command: 'lrange', args: ['mylist', '0', '99'], batch: 50})); -tests.push(new Test({descr: 'SET 4MiB str', command: 'set', args: ['foo_rand000000000002', very_large_str], pipeline: 1})); +tests.push(new Test({descr: 'SET 4MiB str', command: 'set', args: ['foo_rand000000000002', very_large_str]})); tests.push(new Test({descr: 'SET 4MiB str', command: 'set', args: ['foo_rand000000000002', very_large_str], batch: 20})); -tests.push(new Test({descr: 'SET 4MiB buf', command: 'set', args: ['foo_rand000000000002', very_large_buf], pipeline: 1})); +tests.push(new Test({descr: 'SET 4MiB buf', command: 'set', args: ['foo_rand000000000002', very_large_buf]})); tests.push(new Test({descr: 'SET 4MiB buf', command: 'set', args: ['foo_rand000000000002', very_large_buf], batch: 20})); -tests.push(new Test({descr: 'GET 4MiB str', command: 'get', args: ['foo_rand000000000002'], pipeline: 1})); +tests.push(new Test({descr: 'GET 4MiB str', command: 'get', args: ['foo_rand000000000002']})); tests.push(new Test({descr: 'GET 4MiB str', command: 'get', args: ['foo_rand000000000002'], batch: 20})); -tests.push(new Test({descr: 'GET 4MiB buf', command: 'get', args: ['foo_rand000000000002'], pipeline: 1, client_opts: { return_buffers: true} })); +tests.push(new Test({descr: 'GET 4MiB buf', command: 'get', args: ['foo_rand000000000002'], client_opts: { return_buffers: true} })); tests.push(new Test({descr: 'GET 4MiB buf', command: 'get', args: ['foo_rand000000000002'], batch: 20, client_opts: { return_buffers: true} })); function next () { From d2b8f2f39171d008630d4567f339c27428cb0c23 Mon Sep 17 00:00:00 2001 From: Ruben Bridgewater Date: Wed, 13 Apr 2016 03:54:19 +0200 Subject: [PATCH 0624/1748] Add support for camelCase Fixes missing `EXEC_BATCH` on multi --- README.md | 4 +- index.js | 98 ++++++++++++++++++++++++++++++++++---- lib/extendedApi.js | 2 +- lib/multi.js | 2 +- lib/utils.js | 18 ++++++- test/auth.spec.js | 14 +++++- test/commands/info.spec.js | 6 +-- test/connection.spec.js | 18 +++---- test/multi.spec.js | 2 +- test/node_redis.spec.js | 5 +- test/utils.spec.js | 19 +++++++- 11 files changed, 154 insertions(+), 34 deletions(-) diff --git a/README.md b/README.md index d2b82a0ee2a..e9c92bdac55 100644 --- a/README.md +++ b/README.md @@ -49,6 +49,8 @@ This will display: mjr:~/work/node_redis (master)$ Note that the API is entirely asynchronous. To get data back from the server, you'll need to use a callback. +From v.2.6 on the API supports camelCase and snack_case and all options / variables / events etc. can be used either way. +It is recommended to use camelCase as this is the default for the Node.js landscape. ### Promises @@ -109,8 +111,6 @@ client.get("missingkey", function(err, reply) { For a list of Redis commands, see [Redis Command Reference](http://redis.io/commands) -The commands can be specified in uppercase or lowercase for convenience. `client.get()` is the same as `client.GET()`. - Minimal parsing is done on the replies. Commands that return a integer return JavaScript Numbers, arrays return JavaScript Array. `HGETALL` returns an Object keyed by the hash keys. All strings will either be returned as string or as buffer depending on your setting. Please be aware that sending null, undefined and Boolean values will result in the value coerced to a string! diff --git a/index.js b/index.js index 9cdc46dc37f..78e07e65799 100644 --- a/index.js +++ b/index.js @@ -479,13 +479,19 @@ RedisClient.prototype.send_offline_queue = function () { var retry_connection = function (self, error) { debug('Retrying connection...'); - self.emit('reconnecting', { + var reconnect_params = { delay: self.retry_delay, attempt: self.attempts, - error: error, - times_connected: self.times_connected, - total_retry_time: self.retry_totaltime - }); + error: error + }; + if (self.options.camel_case) { + reconnect_params.totalRetryTime = self.retry_totaltime; + reconnect_params.timesConnected = self.times_connected; + } else { + reconnect_params.total_retry_time = self.retry_totaltime; + reconnect_params.times_connected = self.times_connected; + } + self.emit('reconnecting', reconnect_params); self.retry_totaltime += self.retry_delay; self.attempts += 1; @@ -529,12 +535,18 @@ RedisClient.prototype.connection_gone = function (why, error) { } if (typeof this.options.retry_strategy === 'function') { - this.retry_delay = this.options.retry_strategy({ + var retry_params = { attempt: this.attempts, - error: error, - total_retry_time: this.retry_totaltime, - times_connected: this.times_connected - }); + error: error + }; + if (this.options.camel_case) { + retry_params.totalRetryTime = this.retry_totaltime; + retry_params.timesConnected = this.times_connected; + } else { + retry_params.total_retry_time = this.retry_totaltime; + retry_params.times_connected = this.times_connected; + } + this.retry_delay = this.options.retry_strategy(retry_params); if (typeof this.retry_delay !== 'number') { // Pass individual error through if (this.retry_delay instanceof Error) { @@ -902,6 +914,72 @@ RedisClient.prototype.write = function (data) { return; }; +Object.defineProperty(exports, 'debugMode', { + get: function () { + return this.debug_mode; + }, + set: function (val) { + this.debug_mode = val; + } +}); + +// Don't officially expose the command_queue directly but only the length as read only variable +Object.defineProperty(RedisClient.prototype, 'command_queue_length', { + get: function () { + return this.command_queue.length; + } +}); + +Object.defineProperty(RedisClient.prototype, 'offline_queue_length', { + get: function () { + return this.offline_queue.length; + } +}); + +// Add support for camelCase by adding read only properties to the client +// All known exposed snack_case variables are added here +Object.defineProperty(RedisClient.prototype, 'retryDelay', { + get: function () { + return this.retry_delay; + } +}); + +Object.defineProperty(RedisClient.prototype, 'retryBackoff', { + get: function () { + return this.retry_backoff; + } +}); + +Object.defineProperty(RedisClient.prototype, 'commandQueueLength', { + get: function () { + return this.command_queue.length; + } +}); + +Object.defineProperty(RedisClient.prototype, 'offlineQueueLength', { + get: function () { + return this.offline_queue.length; + } +}); + +Object.defineProperty(RedisClient.prototype, 'shouldBuffer', { + get: function () { + return this.should_buffer; + } +}); + +Object.defineProperty(RedisClient.prototype, 'connectionId', { + get: function () { + return this.connection_id; + } +}); + +Object.defineProperty(RedisClient.prototype, 'serverInfo', { + get: function () { + return this.server_info; + } +}); + exports.createClient = function () { return new RedisClient(unifyOptions.apply(null, arguments)); }; diff --git a/lib/extendedApi.js b/lib/extendedApi.js index e9182028e71..0d70bf14189 100644 --- a/lib/extendedApi.js +++ b/lib/extendedApi.js @@ -10,7 +10,7 @@ All documented and exposed API belongs in here **********************************************/ // Redirect calls to the appropriate function and use to send arbitrary / not supported commands -RedisClient.prototype.send_command = function (command, args, callback) { +RedisClient.prototype.send_command = RedisClient.prototype.sendCommand = function (command, args, callback) { // Throw to fail early instead of relying in order in this case if (typeof command !== 'string') { throw new Error('Wrong input type "' + (command !== null && command !== undefined ? command.constructor.name : command) + '" for command name'); diff --git a/lib/multi.js b/lib/multi.js index ca7afda4813..eaa6dd618e4 100644 --- a/lib/multi.js +++ b/lib/multi.js @@ -70,7 +70,7 @@ function pipeline_transaction_command (self, command, args, index, cb) { }); } -Multi.prototype.exec_atomic = function exec_atomic (callback) { +Multi.prototype.exec_atomic = Multi.prototype.EXEC_ATOMIC = Multi.prototype.execAtomic = function exec_atomic (callback) { if (this.queue.length < 2) { return this.exec_batch(callback); } diff --git a/lib/utils.js b/lib/utils.js index 4093b61dcbd..b46b08b1998 100644 --- a/lib/utils.js +++ b/lib/utils.js @@ -41,8 +41,10 @@ function print (err, reply) { } } +var camelCase; // Deep clone arbitrary objects with arrays. Can't handle cyclic structures (results in a range error) // Any attribute with a non primitive value besides object and array will be passed by reference (e.g. Buffers, Maps, Functions) +// All capital letters are going to be replaced with a lower case letter and a underscore infront of it function clone (obj) { var copy; if (Array.isArray(obj)) { @@ -57,7 +59,14 @@ function clone (obj) { var elems = Object.keys(obj); var elem; while (elem = elems.pop()) { - copy[elem] = clone(obj[elem]); + // Accept camelCase options and convert them to snack_case + var snack_case = elem.replace(/[A-Z][^A-Z]/g, '_$&').toLowerCase(); + // If camelCase is detected, pass it to the client, so all variables are going to be camelCased + // There are no deep nested options objects yet, but let's handle this future proof + if (snack_case !== elem.toLowerCase()) { + camelCase = true; + } + copy[snack_case] = clone(obj[elem]); } return copy; } @@ -65,7 +74,12 @@ function clone (obj) { } function convenienceClone (obj) { - return clone(obj) || {}; + camelCase = false; + obj = clone(obj) || {}; + if (camelCase) { + obj.camel_case = true; + } + return obj; } function callbackOrEmit (self, callback, err, res) { diff --git a/test/auth.spec.js b/test/auth.spec.js index 47fa0964a2f..b9bb3c033c1 100644 --- a/test/auth.spec.js +++ b/test/auth.spec.js @@ -166,8 +166,18 @@ describe('client authentication', function () { client = redis.createClient.apply(null, args); client.auth(auth); client.on('ready', function () { - if (this.times_connected === 1) { - client.stream.destroy(); + if (this.times_connected < 3) { + var interval = setInterval(function () { + if (client.commandQueueLength !== 0) { + return; + } + clearInterval(interval); + interval = null; + client.stream.destroy(); + client.set('foo', 'bar'); + client.get('foo'); // Errors would bubble + assert.strictEqual(client.offlineQueueLength, 2); + }, 1); } else { done(); } diff --git a/test/commands/info.spec.js b/test/commands/info.spec.js index 838dac21b8e..3a67a1a178f 100644 --- a/test/commands/info.spec.js +++ b/test/commands/info.spec.js @@ -23,16 +23,16 @@ describe("The 'info' method", function () { client.end(true); }); - it('update server_info after a info command', function (done) { + it('update serverInfo after a info command', function (done) { client.set('foo', 'bar'); client.info(); client.select(2, function () { - assert.strictEqual(client.server_info.db2, undefined); + assert.strictEqual(client.serverInfo.db2, undefined); }); client.set('foo', 'bar'); client.info(); setTimeout(function () { - assert.strictEqual(typeof client.server_info.db2, 'object'); + assert.strictEqual(typeof client.serverInfo.db2, 'object'); done(); }, 30); }); diff --git a/test/connection.spec.js b/test/connection.spec.js index 1279ec4e37b..41de60b3b4c 100644 --- a/test/connection.spec.js +++ b/test/connection.spec.js @@ -132,12 +132,14 @@ describe('connection tests', function () { describe('on lost connection', function () { it('emit an error after max retry attempts and do not try to reconnect afterwards', function (done) { - var max_attempts = 3; + var maxAttempts = 3; var options = { parser: parser, - max_attempts: max_attempts + maxAttempts: maxAttempts }; client = redis.createClient(options); + assert.strictEqual(client.retryBackoff, 1.7); + assert.strictEqual(client.retryDelay, 200); assert.strictEqual(Object.keys(options).length, 2); var calls = 0; @@ -152,7 +154,7 @@ describe('connection tests', function () { client.on('error', function (err) { if (/Redis connection in broken state: maximum connection attempts.*?exceeded./.test(err.message)) { process.nextTick(function () { // End is called after the error got emitted - assert.strictEqual(calls, max_attempts - 1); + assert.strictEqual(calls, maxAttempts - 1); assert.strictEqual(client.emitted_end, true); assert.strictEqual(client.connected, false); assert.strictEqual(client.ready, false); @@ -248,7 +250,7 @@ describe('connection tests', function () { }); }); - it('retry_strategy used to reconnect with individual error', function (done) { + it('retryStrategy used to reconnect with individual error', function (done) { var text = ''; var unhookIntercept = intercept(function (data) { text += data; @@ -256,8 +258,8 @@ describe('connection tests', function () { }); var end = helper.callFuncAfter(done, 2); client = redis.createClient({ - retry_strategy: function (options) { - if (options.total_retry_time > 150) { + retryStrategy: function (options) { + if (options.totalRetryTime > 150) { client.set('foo', 'bar', function (err, res) { assert.strictEqual(err.message, 'Connection timeout'); end(); @@ -267,8 +269,8 @@ describe('connection tests', function () { } return Math.min(options.attempt * 25, 200); }, - max_attempts: 5, - retry_max_delay: 123, + maxAttempts: 5, + retryMaxDelay: 123, port: 9999 }); diff --git a/test/multi.spec.js b/test/multi.spec.js index 817433db348..a969bfbbd53 100644 --- a/test/multi.spec.js +++ b/test/multi.spec.js @@ -571,7 +571,7 @@ describe("The 'multi' method", function () { test = true; }; multi.set('baz', 'binary'); - multi.exec_atomic(); + multi.EXEC_ATOMIC(); assert(test); }); diff --git a/test/node_redis.spec.js b/test/node_redis.spec.js index 060c62926c1..fd9fc2c2d0c 100644 --- a/test/node_redis.spec.js +++ b/test/node_redis.spec.js @@ -30,6 +30,7 @@ describe('The node_redis client', function () { it('check if all options got copied properly', function (done) { client.selected_db = 2; var client2 = client.duplicate(); + assert.strictEqual(client.connectionId + 1, client2.connection_id); assert.strictEqual(client2.selected_db, 2); assert(client.connected); assert(!client2.connected); @@ -360,7 +361,7 @@ describe('The node_redis client', function () { client.on('error', function (err) { assert.strictEqual(err.message, 'SET can\'t be processed. The connection has already been closed.'); assert.strictEqual(err.command, 'SET'); - assert.strictEqual(client.offline_queue.length, 0); + assert.strictEqual(client.offline_queue_length, 0); done(); }); setTimeout(function () { @@ -966,7 +967,7 @@ describe('The node_redis client', function () { multi.set('foo' + (i + 2), 'bar' + (i + 2)); } multi.exec(); - assert.equal(client.command_queue.length, 15); + assert.equal(client.command_queue_length, 15); helper.killConnection(client); }); diff --git a/test/utils.spec.js b/test/utils.spec.js index d77c7eb607c..17aaf89a84d 100644 --- a/test/utils.spec.js +++ b/test/utils.spec.js @@ -11,7 +11,7 @@ describe('utils.js', function () { it('ignore the object prototype and clone a nested array / object', function () { var obj = { a: [null, 'foo', ['bar'], { - "I'm special": true + "i'm special": true }], number: 5, fn: function noop () {} @@ -22,13 +22,28 @@ describe('utils.js', function () { assert(typeof clone.fn === 'function'); }); - it('replace faulty values with an empty object as return value', function () { + it('replace falsy values with an empty object as return value', function () { var a = utils.clone(); var b = utils.clone(null); assert.strictEqual(Object.keys(a).length, 0); assert.strictEqual(Object.keys(b).length, 0); }); + it('transform camelCase options to snack_case and add the camel_case option', function () { + var a = utils.clone({ + optionOneTwo: true, + retryStrategy: false, + nested: { + onlyContainCamelCaseOnce: true + } + }); + assert.strictEqual(Object.keys(a).length, 4); + assert.strictEqual(a.option_one_two, true); + assert.strictEqual(a.retry_strategy, false); + assert.strictEqual(a.camel_case, true); + assert.strictEqual(Object.keys(a.nested).length, 1); + }); + it('throws on circular data', function () { try { var a = {}; From 8e24380d533485298523b2a03c07ab8f13f897cb Mon Sep 17 00:00:00 2001 From: Ruben Bridgewater Date: Wed, 13 Apr 2016 04:00:23 +0200 Subject: [PATCH 0625/1748] Add optional callback option to duplicate function --- README.md | 3 ++- lib/extendedApi.js | 19 ++++++++++++++++++- test/node_redis.spec.js | 32 ++++++++++++++++++++++++++++++++ 3 files changed, 52 insertions(+), 2 deletions(-) diff --git a/README.md b/README.md index e9c92bdac55..5aa7804d1c1 100644 --- a/README.md +++ b/README.md @@ -597,9 +597,10 @@ the second word as first parameter: client.multi().script('load', 'return 1').exec(...); client.multi([['script', 'load', 'return 1']]).exec(...); -## client.duplicate([options]) +## client.duplicate([options][, callback]) Duplicate all current options and return a new redisClient instance. All options passed to the duplicate function are going to replace the original option. +If you pass a callback, duplicate is going to wait until the client is ready and returns it in the callback. If an error occurs in the meanwhile, that is going to return an error instead in the callback. ## client.send_command(command_name[, [args][, callback]]) diff --git a/lib/extendedApi.js b/lib/extendedApi.js index 0d70bf14189..21013146b50 100644 --- a/lib/extendedApi.js +++ b/lib/extendedApi.js @@ -79,7 +79,11 @@ RedisClient.prototype.unref = function () { } }; -RedisClient.prototype.duplicate = function (options) { +RedisClient.prototype.duplicate = function (options, callback) { + if (typeof options === 'function') { + callback = options; + options = null; + } var existing_options = utils.clone(this.options); options = utils.clone(options); for (var elem in options) { // jshint ignore: line @@ -87,5 +91,18 @@ RedisClient.prototype.duplicate = function (options) { } var client = new RedisClient(existing_options); client.selected_db = this.selected_db; + if (typeof callback === 'function') { + var ready_listener = function () { + callback(null, client); + client.removeAllListeners(error_listener); + }; + var error_listener = function (err) { + callback(err); + client.end(true); + }; + client.once('ready', ready_listener); + client.once('error', error_listener); + return; + } return client; }; diff --git a/test/node_redis.spec.js b/test/node_redis.spec.js index fd9fc2c2d0c..e556dc75054 100644 --- a/test/node_redis.spec.js +++ b/test/node_redis.spec.js @@ -66,6 +66,38 @@ describe('The node_redis client', function () { done(); }); }); + + it('works with a callback', function (done) { + client.duplicate(function (err, client) { + assert(!err); + assert.strictEqual(client.ready, true); + client.quit(done); + }); + }); + + it('works with a callback and errors out', function (done) { + client.duplicate({ + port: '9999' + }, function (err, client) { + assert.strictEqual(err.code, 'ECONNREFUSED'); + done(client); + }); + }); + + it('works with a promises', function () { + return client.duplicateAsync().then(function (client) { + assert.strictEqual(client.ready, true); + return client.quitAsync(); + }); + }); + + it('works with a promises and errors', function () { + return client.duplicateAsync({ + port: 9999 + }).catch(function (err) { + assert.strictEqual(err.code, 'ECONNREFUSED'); + }); + }); }); describe('big data', function () { From aff765adf0455a7df8318ecbcb236b3d5bff3c6f Mon Sep 17 00:00:00 2001 From: Ruben Bridgewater Date: Wed, 13 Apr 2016 04:04:03 +0200 Subject: [PATCH 0626/1748] Fix execution order If the command_queue and the offline_queue holds commands, the offline_queue should be choosen instead of the command_queue. --- lib/utils.js | 3 ++- test/utils.spec.js | 19 ++++++++++++------- 2 files changed, 14 insertions(+), 8 deletions(-) diff --git a/lib/utils.js b/lib/utils.js index b46b08b1998..f5a5c902e15 100644 --- a/lib/utils.js +++ b/lib/utils.js @@ -91,7 +91,8 @@ function callbackOrEmit (self, callback, err, res) { } function replyInOrder (self, callback, err, res) { - var command_obj = self.command_queue.peekBack() || self.offline_queue.peekBack(); + // The offline queue has to be checked first, as there might be commands in both queues at the same time + var command_obj = self.offline_queue.peekBack() || self.command_queue.peekBack(); if (!command_obj) { process.nextTick(function () { callbackOrEmit(self, callback, err, res); diff --git a/test/utils.spec.js b/test/utils.spec.js index 17aaf89a84d..094640066b5 100644 --- a/test/utils.spec.js +++ b/test/utils.spec.js @@ -107,7 +107,7 @@ describe('utils.js', function () { emitted = false; }); - it('no elements in either queue. Reply in the next tick', function (done) { + it('no elements in either queue. Reply in the next tick with callback', function (done) { var called = false; utils.reply_in_order(clientMock, function () { called = true; @@ -116,7 +116,7 @@ describe('utils.js', function () { assert(!called); }); - it('no elements in either queue. Reply in the next tick', function (done) { + it('no elements in either queue. Reply in the next tick without callback', function (done) { assert(!emitted); utils.reply_in_order(clientMock, null, new Error('tada')); assert(!emitted); @@ -153,16 +153,21 @@ describe('utils.js', function () { } }); - it('elements in the offline queue. Reply after the offline queue is empty and respect the command_obj', function (done) { - clientMock.command_queue.push(create_command_obj(), {}); - utils.reply_in_order(clientMock, function () { + it('elements in the offline queue and the command_queue. Reply all other commands got handled respect the command_obj', function (done) { + clientMock.command_queue.push(create_command_obj(), create_command_obj()); + clientMock.offline_queue.push(create_command_obj(), {}); + utils.reply_in_order(clientMock, function (err, res) { assert.strictEqual(clientMock.command_queue.length, 0); + assert.strictEqual(clientMock.offline_queue.length, 0); assert(!emitted); - assert.strictEqual(res_count, 1); + assert.strictEqual(res_count, 3); done(); }, null, null); + while (clientMock.offline_queue.length) { + clientMock.command_queue.push(clientMock.offline_queue.shift()); + } while (clientMock.command_queue.length) { - clientMock.command_queue.shift().callback(null, 'bar'); + clientMock.command_queue.shift().callback(null, 'hello world'); } }); }); From a9d565b8f457bd5de4570613700f40c7f187e94f Mon Sep 17 00:00:00 2001 From: Ruben Bridgewater Date: Wed, 13 Apr 2016 04:07:25 +0200 Subject: [PATCH 0627/1748] Fix auth regression Fixes #1028 --- index.js | 2 ++ lib/individualCommands.js | 2 +- test/auth.spec.js | 6 +++--- 3 files changed, 6 insertions(+), 4 deletions(-) diff --git a/index.js b/index.js index 78e07e65799..b5c62f3c65c 100644 --- a/index.js +++ b/index.js @@ -269,7 +269,9 @@ RedisClient.prototype.create_stream = function () { // Fire the command before redis is connected to be sure it's the first fired command if (this.auth_pass !== undefined) { + this.ready = true; this.auth(this.auth_pass); + this.ready = false; } }; diff --git a/lib/individualCommands.js b/lib/individualCommands.js index 4f4c8ab7a57..953a9337f24 100644 --- a/lib/individualCommands.js +++ b/lib/individualCommands.js @@ -149,7 +149,7 @@ RedisClient.prototype.auth = RedisClient.prototype.AUTH = function auth (pass, c // Stash auth for connect and reconnect. this.auth_pass = pass; - this.ready = this.offline_queue.length === 0; // keep the execution order intakt + this.ready = ready || this.offline_queue.length === 0; // keep the execution order intakt var tmp = this.internal_send_command('auth', [pass], function (err, res) { if (err) { if (no_password_is_set.test(err.message)) { diff --git a/test/auth.spec.js b/test/auth.spec.js index b9bb3c033c1..2e28f895dfb 100644 --- a/test/auth.spec.js +++ b/test/auth.spec.js @@ -160,7 +160,7 @@ describe('client authentication', function () { client.on('ready', done); }); - it('reconnects with appropriate authentication', function (done) { + it('reconnects with appropriate authentication while offline commands are present', function (done) { if (helper.redisProcess().spawnFailed()) this.skip(); client = redis.createClient.apply(null, args); @@ -187,7 +187,7 @@ describe('client authentication', function () { }); }); - it('should return an error if the password is not of type string and a callback has been provided', function (done) { + it('should return an error if the password is not correct and a callback has been provided', function (done) { if (helper.redisProcess().spawnFailed()) this.skip(); client = redis.createClient.apply(null, args); @@ -202,7 +202,7 @@ describe('client authentication', function () { assert(async); }); - it('should emit an error if the password is not of type string and no callback has been provided', function (done) { + it('should emit an error if the password is not correct and no callback has been provided', function (done) { if (helper.redisProcess().spawnFailed()) this.skip(); client = redis.createClient.apply(null, args); From cd58e1fd8961ffabafe2bfea960229df5a0bcc0c Mon Sep 17 00:00:00 2001 From: Ruben Bridgewater Date: Wed, 13 Apr 2016 04:18:39 +0200 Subject: [PATCH 0628/1748] Implement message_buffer and pmessage_buffer events --- README.md | 10 ++++++++ index.js | 60 +++++++++++++++++++++++++-------------------- test/pubsub.spec.js | 24 ++++++++++++++++++ 3 files changed, 67 insertions(+), 27 deletions(-) diff --git a/README.md b/README.md index 5aa7804d1c1..3a4abf9eb96 100644 --- a/README.md +++ b/README.md @@ -410,6 +410,16 @@ Client will emit `pmessage` for every message received that matches an active su Listeners are passed the original pattern used with `PSUBSCRIBE` as `pattern`, the sending channel name as `channel`, and the message as `message`. +### "message_buffer" (channel, message) + +This is the same as the `message` event with the exception, that it is always going to emit a buffer. +If you listen to the `message` event at the same time as the `message_buffer`, it is always going to emit a string. + +### "pmessage_buffer" (pattern, channel, message) + +This is the same as the `pmessage` event with the exception, that it is always going to emit a buffer. +If you listen to the `pmessage` event at the same time as the `pmessage_buffer`, it is always going to emit a string. + ### "subscribe" (channel, count) Client will emit `subscribe` in response to a `SUBSCRIBE` command. Listeners are passed the diff --git a/index.js b/index.js index b5c62f3c65c..5848279e005 100644 --- a/index.js +++ b/index.js @@ -27,7 +27,7 @@ if (typeof EventEmitter !== 'function') { function noop () {} function handle_detect_buffers_reply (reply, command, buffer_args) { - if (buffer_args === false) { + if (buffer_args === false || this.message_buffers) { // If detect_buffers option was specified, then the reply from the parser will be a buffer. // If this command did not use Buffer arguments, then convert the reply to Strings here. reply = utils.reply_to_strings(reply); @@ -138,6 +138,7 @@ function RedisClient (options, stream) { this.pub_sub_mode = 0; this.subscription_set = {}; this.monitoring = false; + this.message_buffers = false; this.closing = false; this.server_info = {}; this.auth_pass = options.auth_pass || options.password; @@ -149,23 +150,7 @@ function RedisClient (options, stream) { this.options = options; this.buffers = options.return_buffers || options.detect_buffers; // Init parser - this.reply_parser = Parser({ - returnReply: function (data) { - self.return_reply(data); - }, - returnError: function (err) { - self.return_error(err); - }, - returnFatalError: function (err) { - // Error out all fired commands. Otherwise they might rely on faulty data. We have to reconnect to get in a working state again - self.flush_and_error(err, ['command_queue']); - self.stream.destroy(); - self.return_error(err); - }, - returnBuffers: this.buffers, - name: options.parser, - stringNumbers: options.string_numbers - }); + this.reply_parser = create_parser(this, options); this.create_stream(); // The listeners will not be attached right away, so let's print the deprecation message while the listener is attached this.on('newListener', function (event) { @@ -179,6 +164,10 @@ function RedisClient (options, stream) { 'The drain event listener is deprecated and will be removed in v.3.0.0.\n' + 'If you want to keep on listening to this event please listen to the stream drain event directly.' ); + } else if (event === 'message_buffer' || event === 'pmessage_buffer' || event === 'messageBuffer' || event === 'pmessageBuffer' && !this.buffers) { + this.message_buffers = true; + this.handle_reply = handle_detect_buffers_reply; + this.reply_parser = create_parser(this); } }); } @@ -186,6 +175,26 @@ util.inherits(RedisClient, EventEmitter); RedisClient.connection_id = 0; +function create_parser (self) { + return Parser({ + returnReply: function (data) { + self.return_reply(data); + }, + returnError: function (err) { + self.return_error(err); + }, + returnFatalError: function (err) { + // Error out all fired commands. Otherwise they might rely on faulty data. We have to reconnect to get in a working state again + self.flush_and_error(err, ['command_queue']); + self.stream.destroy(); + self.return_error(err); + }, + returnBuffers: self.buffers || self.message_buffers, + name: self.options.parser, + stringNumbers: self.options.string_numbers + }); +} + /****************************************************************************** All functions in here are internal besides the RedisClient constructor @@ -696,21 +705,18 @@ function subscribe_unsubscribe (self, reply, type, subscribe) { function return_pub_sub (self, reply) { var type = reply[0].toString(); if (type === 'message') { // channel, message - // TODO: Implement message_buffer - // if (self.buffers) { - // self.emit('message_buffer', reply[1], reply[2]); - // } - if (!self.options.return_buffers) { // backwards compatible. Refactor this in v.3 to always return a string on the normal emitter + if (!self.options.return_buffers || self.message_buffers) { // backwards compatible. Refactor this in v.3 to always return a string on the normal emitter self.emit('message', reply[1].toString(), reply[2].toString()); + self.emit('message_buffer', reply[1], reply[2]); + self.emit('messageBuffer', reply[1], reply[2]); } else { self.emit('message', reply[1], reply[2]); } } else if (type === 'pmessage') { // pattern, channel, message - // if (self.buffers) { - // self.emit('pmessage_buffer', reply[1], reply[2], reply[3]); - // } - if (!self.options.return_buffers) { // backwards compatible. Refactor this in v.3 to always return a string on the normal emitter + if (!self.options.return_buffers || self.message_buffers) { // backwards compatible. Refactor this in v.3 to always return a string on the normal emitter self.emit('pmessage', reply[1].toString(), reply[2].toString(), reply[3].toString()); + self.emit('pmessage_buffer', reply[1], reply[2], reply[3]); + self.emit('pmessageBuffer', reply[1], reply[2], reply[3]); } else { self.emit('pmessage', reply[1], reply[2], reply[3]); } diff --git a/test/pubsub.spec.js b/test/pubsub.spec.js index 9cb388b03e1..f9401f63ad5 100644 --- a/test/pubsub.spec.js +++ b/test/pubsub.spec.js @@ -504,6 +504,30 @@ describe('publish/subscribe', function () { pub.publish('/foo', 'hello world', helper.isNumber(3)); }); }); + + it('allows to listen to pmessageBuffer and pmessage', function (done) { + var batch = sub.batch(); + batch.psubscribe('*'); + batch.subscribe('/foo'); + batch.unsubscribe('/foo'); + batch.unsubscribe(); + batch.subscribe(['/foo']); + batch.exec(); + assert.strictEqual(sub.shouldBuffer, false); + sub.on('pmessageBuffer', function (pattern, channel, message) { + assert.strictEqual(pattern.inspect(), new Buffer('*').inspect()); + assert.strictEqual(channel.inspect(), new Buffer('/foo').inspect()); + sub.quit(done); + }); + sub.on('pmessage', function (pattern, channel, message) { + assert.strictEqual(pattern, '*'); + assert.strictEqual(channel, '/foo'); + }); + pub.pubsub('numsub', '/foo', function (err, res) { + assert.deepEqual(res, ['/foo', 1]); + }); + pub.publish('/foo', 'hello world', helper.isNumber(2)); + }); }); describe('punsubscribe', function () { From 5d1265958338681a5f2662bd695efd3a07312310 Mon Sep 17 00:00:00 2001 From: Ruben Bridgewater Date: Wed, 13 Apr 2016 04:29:02 +0200 Subject: [PATCH 0629/1748] Fix typos / comments --- index.js | 15 ++++++++------- lib/extendedApi.js | 2 +- lib/utils.js | 2 +- test/auth.spec.js | 2 +- test/connection.spec.js | 2 +- test/node_redis.spec.js | 6 +++--- 6 files changed, 15 insertions(+), 14 deletions(-) diff --git a/index.js b/index.js index 5848279e005..e6e565c59b3 100644 --- a/index.js +++ b/index.js @@ -63,7 +63,7 @@ function RedisClient (options, stream) { cnx_options.family = (!options.family && net.isIP(cnx_options.host)) || (options.family === 'IPv6' ? 6 : 4); this.address = cnx_options.host + ':' + cnx_options.port; } - /* istanbul ignore next: travis does not work with stunnel atm. Therefor the tls tests are skipped on travis */ + /* istanbul ignore next: travis does not work with stunnel atm. Therefore the tls tests are skipped on travis */ for (var tls_option in options.tls) { // jshint ignore: line cnx_options[tls_option] = options.tls[tls_option]; } @@ -220,7 +220,7 @@ RedisClient.prototype.create_stream = function () { this.stream.destroy(); } - /* istanbul ignore if: travis does not work with stunnel atm. Therefor the tls tests are skipped on travis */ + /* istanbul ignore if: travis does not work with stunnel atm. Therefore the tls tests are skipped on travis */ if (this.options.tls) { this.stream = tls.connect(this.connection_options); } else { @@ -230,12 +230,13 @@ RedisClient.prototype.create_stream = function () { if (this.options.connect_timeout) { this.stream.setTimeout(this.connect_timeout, function () { + // Note: This is only tested if a internet connection is established self.retry_totaltime = self.connect_timeout; self.connection_gone('timeout', new Error('Redis connection gone from timeout event')); }); } - /* istanbul ignore next: travis does not work with stunnel atm. Therefor the tls tests are skipped on travis */ + /* istanbul ignore next: travis does not work with stunnel atm. Therefore the tls tests are skipped on travis */ var connect_event = this.options.tls ? 'secureConnect' : 'connect'; this.stream.once(connect_event, function () { this.removeAllListeners('timeout'); @@ -244,7 +245,7 @@ RedisClient.prototype.create_stream = function () { }); this.stream.on('data', function (buffer_from_socket) { - // The buffer_from_socket.toString() has a significant impact on big chunks and therefor this should only be used if necessary + // The buffer_from_socket.toString() has a significant impact on big chunks and therefore this should only be used if necessary debug('Net read ' + self.address + ' id ' + self.connection_id); // + ': ' + buffer_from_socket.toString()); self.reply_parser.execute(buffer_from_socket); self.emit_idle(); @@ -400,12 +401,12 @@ RedisClient.prototype.on_ready = function () { this.pub_sub_mode = this.old_state.pub_sub_mode; } if (this.monitoring) { // Monitor has to be fired before pub sub commands - this.internal_send_command('monitor', []); + this.internal_send_command('monitor', []); // The state is still set } var callback_count = Object.keys(this.subscription_set).length; if (!this.options.disable_resubscribing && callback_count) { // only emit 'ready' when all subscriptions were made again - // TODO: Remove the countdown for ready here. This is not coherent with all other modes and should therefor not be handled special + // TODO: Remove the countdown for ready here. This is not coherent with all other modes and should therefore not be handled special // We know we are ready as soon as all commands were fired var callback = function () { callback_count--; @@ -680,7 +681,7 @@ function subscribe_unsubscribe (self, reply, type, subscribe) { } else { var running_command; var i = 1; - // This should be a rare case and therefor handling it this way should be good performance wise for the general case + // This should be a rare case and therefore handling it this way should be good performance wise for the general case while (running_command = self.command_queue.get(i)) { if (SUBSCRIBE_COMMANDS[running_command.command]) { self.command_queue.shift(); diff --git a/lib/extendedApi.js b/lib/extendedApi.js index 21013146b50..5cd78038f06 100644 --- a/lib/extendedApi.js +++ b/lib/extendedApi.js @@ -39,7 +39,7 @@ RedisClient.prototype.send_command = RedisClient.prototype.sendCommand = functio return this.internal_send_command(command, args, callback); } if (typeof callback === 'function') { - args = args.concat([callback]); + args = args.concat([callback]); // Prevent manipulating the input array } return this[command].apply(this, args); }; diff --git a/lib/utils.js b/lib/utils.js index f5a5c902e15..1b6ee62124f 100644 --- a/lib/utils.js +++ b/lib/utils.js @@ -3,7 +3,7 @@ var RawObject = require('./rawObject'); // hgetall converts its replies to an Object. If the reply is empty, null is returned. -// These function are only called with internal data and have therefor always the same instanceof X +// These function are only called with internal data and have therefore always the same instanceof X function replyToObject (reply) { // The reply might be a string or a buffer if this is called in a transaction (multi) if (reply.length === 0 || !(reply instanceof Array)) { diff --git a/test/auth.spec.js b/test/auth.spec.js index 2e28f895dfb..4c262dccd23 100644 --- a/test/auth.spec.js +++ b/test/auth.spec.js @@ -53,7 +53,7 @@ describe('client authentication', function () { client.auth(auth, function (err, res) { assert.strictEqual('retry worked', res); var now = Date.now(); - // Hint: setTimeout sometimes triggers early and therefor the value can be like one or two ms to early + // Hint: setTimeout sometimes triggers early and therefore the value can be like one or two ms to early assert(now - time >= 98, 'Time should be above 100 ms (the reconnect time) and is ' + (now - time)); assert(now - time < 225, 'Time should be below 255 ms (the reconnect should only take a bit above 100 ms) and is ' + (now - time)); done(); diff --git a/test/connection.spec.js b/test/connection.spec.js index 41de60b3b4c..ad91730c350 100644 --- a/test/connection.spec.js +++ b/test/connection.spec.js @@ -20,7 +20,7 @@ describe('connection tests', function () { it('unofficially support for a private stream', function () { // While using a private stream, reconnection and other features are not going to work properly. // Besides that some functions also have to be monkey patched to be safe from errors in this case. - // Therefor this is not officially supported! + // Therefore this is not officially supported! var socket = new net.Socket(); client = new redis.RedisClient({ prefix: 'test' diff --git a/test/node_redis.spec.js b/test/node_redis.spec.js index e556dc75054..02a10f7c951 100644 --- a/test/node_redis.spec.js +++ b/test/node_redis.spec.js @@ -597,7 +597,7 @@ describe('The node_redis client', function () { assert.strictEqual(typeof rawOutput, 'string'); assert(utils.monitor_regex.test(rawOutput), rawOutput); assert.deepEqual(args, ['mget', 'hello', 'world']); - // Quit immediatly ends monitoring mode and therefor does not stream back the quit command + // Quit immediatly ends monitoring mode and therefore does not stream back the quit command monitorClient.quit(done); }); }); @@ -668,7 +668,7 @@ describe('The node_redis client', function () { assert.deepEqual(responses[5], ['unsubscribe', 'baz']); assert.deepEqual(responses[6], ['publish', '/foo', 'hello world']); // The publish is called right after the reconnect and the monitor is called before the message is emitted. - // Therefor we have to wait till the next tick + // Therefore we have to wait till the next tick process.nextTick(function () { assert(called); client.quit(done); @@ -891,7 +891,7 @@ describe('The node_redis client', function () { client.set('foo', 'bar', function (err, res) { assert.strictEqual(err.message, 'Protocol error, got "a" as reply type byte'); }); - // Fail the set answer. Has no corresponding command obj and will therefor land in the error handler and set + // Fail the set answer. Has no corresponding command obj and will therefore land in the error handler and set client.reply_parser.execute(new Buffer('a*1\r*1\r$1`zasd\r\na')); }); }); From 683815de9d715dd413aa79c72779c94b61914a58 Mon Sep 17 00:00:00 2001 From: Ruben Bridgewater Date: Thu, 14 Apr 2016 00:57:34 +0200 Subject: [PATCH 0630/1748] Refactor pipelining --- index.js | 72 ++++++++++++++++++----------------------- lib/multi.js | 7 ++-- test/multi.spec.js | 12 +++++-- test/node_redis.spec.js | 10 +++--- 4 files changed, 47 insertions(+), 54 deletions(-) diff --git a/index.js b/index.js index e6e565c59b3..ac2c0fc517a 100644 --- a/index.js +++ b/index.js @@ -122,6 +122,7 @@ function RedisClient (options, stream) { } this.command_queue = new Queue(); // Holds sent commands to de-pipeline them this.offline_queue = new Queue(); // Holds commands issued but not able to be sent + this.pipeline_queue = new Queue(); // Holds all pipelined commands // ATTENTION: connect_timeout should change in v.3.0 so it does not count towards ending reconnection attempts after x seconds // This should be done by the retry_strategy. Instead it should only be the timeout for connecting to redis this.connect_timeout = +options.connect_timeout || 3600000; // 60 * 60 * 1000 ms @@ -144,8 +145,8 @@ function RedisClient (options, stream) { this.auth_pass = options.auth_pass || options.password; this.selected_db = options.db; // Save the selected db here, used when reconnecting this.old_state = null; - this.send_anyway = false; - this.pipeline = 0; + this.fire_strings = true; // Determine if strings or buffers should be written to the stream + this.pipeline = false; this.times_connected = 0; this.options = options; this.buffers = options.return_buffers || options.detect_buffers; @@ -374,23 +375,25 @@ RedisClient.prototype.on_ready = function () { debug('on_ready called ' + this.address + ' id ' + this.connection_id); this.ready = true; - var cork; - if (!this.stream.cork) { - cork = function (len) { - self.pipeline = len; - self.pipeline_queue = new Queue(len); - }; - } else { - cork = function (len) { - self.pipeline = len; - self.pipeline_queue = new Queue(len); + this.cork = function () { + self.pipeline = true; + if (self.stream.cork) { self.stream.cork(); - }; - this.uncork = function () { + } + }; + this.uncork = function () { + if (self.fire_strings) { + self.write_strings(); + } else { + self.write_buffers(); + } + self.pipeline = false; + self.fire_strings = true; + if (self.stream.uncork) { + // TODO: Consider using next tick here. See https://github.com/NodeRedis/node_redis/issues/1033 self.stream.uncork(); - }; - } - this.cork = cork; + } + }; // Restore modal commands from previous connection. The order of the commands is important if (this.selected_db !== undefined) { @@ -523,7 +526,8 @@ RedisClient.prototype.connection_gone = function (why, error) { this.ready = false; // Deactivate cork to work with the offline queue this.cork = noop; - this.pipeline = 0; + this.uncork = noop; + this.pipeline = false; var state = { monitoring: this.monitoring, @@ -792,10 +796,6 @@ RedisClient.prototype.internal_send_command = function (command, args, callback) if (args[i].length > 30000) { big_data = true; args_copy[i] = new Buffer(args[i], 'utf8'); - if (this.pipeline !== 0) { - this.pipeline += 2; - this.writeDefault = this.writeBuffers; - } } else { args_copy[i] = args[i]; } @@ -813,10 +813,6 @@ RedisClient.prototype.internal_send_command = function (command, args, callback) args_copy[i] = args[i]; buffer_args = true; big_data = true; - if (this.pipeline !== 0) { - this.pipeline += 2; - this.writeDefault = this.writeBuffers; - } } else { this.warn( 'Deprecated: The ' + command.toUpperCase() + ' command contains a argument of type ' + args[i].constructor.name + '.\n' + @@ -870,6 +866,7 @@ RedisClient.prototype.internal_send_command = function (command, args, callback) this.write(command_str); } else { debug('Send command (' + command_str + ') has Buffer arguments'); + this.fire_strings = false; this.write(command_str); for (i = 0; i < len; i += 1) { @@ -887,40 +884,33 @@ RedisClient.prototype.internal_send_command = function (command, args, callback) return !this.should_buffer; }; -RedisClient.prototype.writeDefault = RedisClient.prototype.writeStrings = function (data) { +RedisClient.prototype.write_strings = function () { var str = ''; for (var command = this.pipeline_queue.shift(); command; command = this.pipeline_queue.shift()) { // Write to stream if the string is bigger than 4mb. The biggest string may be Math.pow(2, 28) - 15 chars long if (str.length + command.length > 4 * 1024 * 1024) { - this.stream.write(str); + this.should_buffer = !this.stream.write(str); str = ''; } str += command; } - this.should_buffer = !this.stream.write(str + data); + if (str !== '') { + this.should_buffer = !this.stream.write(str); + } }; -RedisClient.prototype.writeBuffers = function (data) { +RedisClient.prototype.write_buffers = function () { for (var command = this.pipeline_queue.shift(); command; command = this.pipeline_queue.shift()) { - this.stream.write(command); + this.should_buffer = !this.stream.write(command); } - this.should_buffer = !this.stream.write(data); }; RedisClient.prototype.write = function (data) { - if (this.pipeline === 0) { + if (this.pipeline === false) { this.should_buffer = !this.stream.write(data); return; } - - this.pipeline--; - if (this.pipeline === 0) { - this.writeDefault(data); - return; - } - this.pipeline_queue.push(data); - return; }; Object.defineProperty(exports, 'debugMode', { diff --git a/lib/multi.js b/lib/multi.js index eaa6dd618e4..bb2173cd2e6 100644 --- a/lib/multi.js +++ b/lib/multi.js @@ -126,7 +126,7 @@ Multi.prototype.exec_transaction = function exec_transaction (callback) { var len = self.queue.length; self.errors = []; self.callback = callback; - self._client.cork(len + 2); + self._client.cork(); self.wants_buffers = new Array(len); pipeline_transaction_command(self, 'multi', []); // Drain queue, callback will catch 'QUEUED' or error @@ -151,7 +151,6 @@ Multi.prototype.exec_transaction = function exec_transaction (callback) { multi_callback(self, err, replies); }); self._client.uncork(); - self._client.writeDefault = self._client.writeStrings; return !self._client.should_buffer; }; @@ -198,7 +197,7 @@ Multi.prototype.exec = Multi.prototype.EXEC = Multi.prototype.exec_batch = funct return true; } self.results = []; - self._client.cork(len); + self._client.cork(); while (args = self.queue.shift()) { var command = args[0]; var cb; @@ -213,9 +212,7 @@ Multi.prototype.exec = Multi.prototype.EXEC = Multi.prototype.exec_batch = funct self._client.internal_send_command(command, args[1], cb); index++; } - self.queue = new Queue(); self._client.uncork(); - self._client.writeDefault = self._client.writeStrings; return !self._client.should_buffer; }; diff --git a/test/multi.spec.js b/test/multi.spec.js index a969bfbbd53..2ad9a230512 100644 --- a/test/multi.spec.js +++ b/test/multi.spec.js @@ -125,9 +125,8 @@ describe("The 'multi' method", function () { describe('when connected', function () { - beforeEach(function (done) { + beforeEach(function () { client = redis.createClient.apply(null, args); - client.once('connect', done); }); it('executes a pipelined multi properly in combination with the offline queue', function (done) { @@ -135,6 +134,7 @@ describe("The 'multi' method", function () { multi1.set('m1', '123'); multi1.get('m1'); multi1.exec(done); + assert.strictEqual(client.offline_queue.length, 4); }); it('executes a pipelined multi properly after a reconnect in combination with the offline queue', function (done) { @@ -612,11 +612,17 @@ describe("The 'multi' method", function () { }); it('emits error once if reconnecting after multi has been executed but not yet returned without callback', function (done) { + // NOTE: If uncork is called async by postponing it to the next tick, this behavior is going to change. + // The command won't be processed anymore two errors are returned instead of one client.on('error', function (err) { assert.strictEqual(err.code, 'UNCERTAIN_STATE'); - done(); + client.get('foo', function (err, res) { + assert.strictEqual(res, 'bar'); + done(); + }); }); + // The commands should still be fired, no matter that the socket is destroyed on the same tick client.multi().set('foo', 'bar').get('foo').exec(); // Abort connection before the value returned client.stream.destroy(); diff --git a/test/node_redis.spec.js b/test/node_redis.spec.js index 02a10f7c951..fdfc503d534 100644 --- a/test/node_redis.spec.js +++ b/test/node_redis.spec.js @@ -121,12 +121,12 @@ describe('The node_redis client', function () { str += str; } var called = false; - var temp = client.writeBuffers.bind(client); - assert(String(client.writeBuffers) !== String(client.writeDefault)); - client.writeBuffers = function (data) { + var temp = client.write_buffers.bind(client); + assert(client.fire_strings); + client.write_buffers = function (data) { called = true; // To increase write performance for strings the value is converted to a buffer - assert(String(client.writeBuffers) === String(client.writeDefault)); + assert(!client.fire_strings); temp(data); }; client.multi().set('foo', str).get('foo', function (err, res) { @@ -136,7 +136,7 @@ describe('The node_redis client', function () { assert.strictEqual(res[1], str); done(); }); - assert(String(client.writeBuffers) !== String(client.writeDefault)); + assert(client.fire_strings); }); }); From 0424cb0bf39f747e2c9d66077d7bb8bd654d4701 Mon Sep 17 00:00:00 2001 From: Ruben Bridgewater Date: Thu, 14 Apr 2016 01:11:20 +0200 Subject: [PATCH 0631/1748] Move pub sub command into individual commands and use call_on_write --- index.js | 23 ++-- lib/command.js | 7 +- lib/individualCommands.js | 239 +++++++++++++++++++++++++++++++++++++- lib/multi.js | 86 +++++--------- test/multi.spec.js | 4 +- 5 files changed, 280 insertions(+), 79 deletions(-) diff --git a/index.js b/index.js index ac2c0fc517a..8b5d917be97 100644 --- a/index.js +++ b/index.js @@ -484,7 +484,7 @@ RedisClient.prototype.ready_check = function () { RedisClient.prototype.send_offline_queue = function () { for (var command_obj = this.offline_queue.shift(); command_obj; command_obj = this.offline_queue.shift()) { debug('Sending offline command: ' + command_obj.command); - this.internal_send_command(command_obj.command, command_obj.args, command_obj.callback); + this.internal_send_command(command_obj.command, command_obj.args, command_obj.callback, command_obj.call_on_write); } this.drain(); // Even though items were shifted off, Queue backing store still uses memory until next add, so just get a new Queue @@ -771,8 +771,10 @@ function handle_offline_command (self, command_obj) { self.should_buffer = true; } -RedisClient.prototype.internal_send_command = function (command, args, callback) { - var arg, prefix_keys; +// Do not call internal_send_command directly, if you are not absolutly certain it handles everything properly +// e.g. monitor / info does not work with internal_send_command only +RedisClient.prototype.internal_send_command = function (command, args, callback, call_on_write) { + var arg, prefix_keys, command_obj; var i = 0; var command_str = ''; var len = args.length; @@ -786,7 +788,7 @@ RedisClient.prototype.internal_send_command = function (command, args, callback) if (this.ready === false || this.stream.writable === false) { // Handle offline commands right away - handle_offline_command(this, new OfflineCommand(command, args, callback)); + handle_offline_command(this, new OfflineCommand(command, args, callback, call_on_write)); return false; // Indicate buffering } @@ -834,15 +836,7 @@ RedisClient.prototype.internal_send_command = function (command, args, callback) } } args = null; - var command_obj = new Command(command, args_copy, callback); - command_obj.buffer_args = buffer_args; - - if (SUBSCRIBE_COMMANDS[command] && this.pub_sub_mode === 0) { - // If pub sub is already activated, keep it that way, otherwise set the number of commands to resolve until pub sub mode activates - // Deactivation of the pub sub mode happens in the result handler - this.pub_sub_mode = this.command_queue.length + 1; - } - this.command_queue.push(command_obj); + command_obj = new Command(command, args_copy, buffer_args, callback); if (this.options.prefix) { prefix_keys = commands.getKeyIndexes(command, args_copy); @@ -881,6 +875,9 @@ RedisClient.prototype.internal_send_command = function (command, args, callback) debug('send_command: buffer send ' + arg.length + ' bytes'); } } + if (call_on_write) { + call_on_write(); + } return !this.should_buffer; }; diff --git a/lib/command.js b/lib/command.js index e4467fb55da..ee1181ea7e3 100644 --- a/lib/command.js +++ b/lib/command.js @@ -2,18 +2,19 @@ // This Command constructor is ever so slightly faster than using an object literal, but more importantly, using // a named constructor helps it show up meaningfully in the V8 CPU profiler and in heap snapshots. -function Command (command, args, callback) { +function Command (command, args, buffer_args, callback) { this.command = command; this.args = args; // We only need the args for the offline commands => move them into another class. We need the number of args though for pub sub - this.buffer_args = false; + this.buffer_args = buffer_args; this.callback = callback; this.sub_commands_left = args.length; } -function OfflineCommand (command, args, callback) { +function OfflineCommand (command, args, callback, call_on_write) { this.command = command; this.args = args; this.callback = callback; + this.call_on_write = call_on_write; } module.exports = { diff --git a/lib/individualCommands.js b/lib/individualCommands.js index 953a9337f24..4ebd8ae1705 100644 --- a/lib/individualCommands.js +++ b/lib/individualCommands.js @@ -7,9 +7,18 @@ var no_password_is_set = /no password is set/; var loading = /LOADING/; var RedisClient = require('../').RedisClient; -/******************************** -Replace built-in redis functions -********************************/ +/******************************************************************************************** + Replace built-in redis functions + + The callback may be hooked as needed. The same does not apply to the rest of the function. + State should not be set outside of the callback if not absolutly necessary. + This is important to make sure it works the same as single command or in a multi context. + To make sure everything works with the offline queue use the "call_on_write" function. + This is going to be executed while writing to the stream. + + TODO: Implement individal command generation as soon as possible to prevent divergent code + on single and multi calls! +********************************************************************************************/ RedisClient.prototype.multi = RedisClient.prototype.MULTI = function multi (args) { var multi = new Multi(this, args); @@ -209,3 +218,227 @@ RedisClient.prototype.hmset = RedisClient.prototype.HMSET = function hmset () { } return this.internal_send_command('hmset', arr, callback); }; + +RedisClient.prototype.subscribe = RedisClient.prototype.SUBSCRIBE = function subscribe () { + var arr, + len = arguments.length, + callback, + i = 0; + if (Array.isArray(arguments[0])) { + arr = arguments[0]; + callback = arguments[1]; + } else { + len = arguments.length; + // The later should not be the average use case + if (len !== 0 && (typeof arguments[len - 1] === 'function' || typeof arguments[len - 1] === 'undefined')) { + len--; + callback = arguments[len]; + } + arr = new Array(len); + for (; i < len; i += 1) { + arr[i] = arguments[i]; + } + } + var self = this; + var call_on_write = function () { + self.pub_sub_mode = self.pub_sub_mode || self.command_queue.length + 1; + }; + return this.internal_send_command('subscribe', arr, callback, call_on_write); +}; + +Multi.prototype.subscribe = Multi.prototype.SUBSCRIBE = function subscribe () { + var arr, + len = arguments.length, + callback, + i = 0; + if (Array.isArray(arguments[0])) { + arr = arguments[0]; + callback = arguments[1]; + } else { + len = arguments.length; + // The later should not be the average use case + if (len !== 0 && (typeof arguments[len - 1] === 'function' || typeof arguments[len - 1] === 'undefined')) { + len--; + callback = arguments[len]; + } + arr = new Array(len); + for (; i < len; i += 1) { + arr[i] = arguments[i]; + } + } + var self = this._client; + var call_on_write = function () { + self.pub_sub_mode = self.pub_sub_mode || self.command_queue.length + 1; + }; + this.queue.push(['subscribe', arr, callback, call_on_write]); + return this; +}; + +RedisClient.prototype.unsubscribe = RedisClient.prototype.UNSUBSCRIBE = function unsubscribe () { + var arr, + len = arguments.length, + callback, + i = 0; + if (Array.isArray(arguments[0])) { + arr = arguments[0]; + callback = arguments[1]; + } else { + len = arguments.length; + // The later should not be the average use case + if (len !== 0 && (typeof arguments[len - 1] === 'function' || typeof arguments[len - 1] === 'undefined')) { + len--; + callback = arguments[len]; + } + arr = new Array(len); + for (; i < len; i += 1) { + arr[i] = arguments[i]; + } + } + var self = this; + var call_on_write = function () { + // Pub sub has to be activated even if not in pub sub mode, as the return value is manipulated in the callback + self.pub_sub_mode = self.pub_sub_mode || self.command_queue.length + 1; + }; + return this.internal_send_command('unsubscribe', arr, callback, call_on_write); +}; + +Multi.prototype.unsubscribe = Multi.prototype.UNSUBSCRIBE = function unsubscribe () { + var arr, + len = arguments.length, + callback, + i = 0; + if (Array.isArray(arguments[0])) { + arr = arguments[0]; + callback = arguments[1]; + } else { + len = arguments.length; + // The later should not be the average use case + if (len !== 0 && (typeof arguments[len - 1] === 'function' || typeof arguments[len - 1] === 'undefined')) { + len--; + callback = arguments[len]; + } + arr = new Array(len); + for (; i < len; i += 1) { + arr[i] = arguments[i]; + } + } + var self = this._client; + var call_on_write = function () { + // Pub sub has to be activated even if not in pub sub mode, as the return value is manipulated in the callback + self.pub_sub_mode = self.pub_sub_mode || self.command_queue.length + 1; + }; + this.queue.push(['unsubscribe', arr, callback, call_on_write]); + return this; +}; + +RedisClient.prototype.psubscribe = RedisClient.prototype.PSUBSCRIBE = function psubscribe () { + var arr, + len = arguments.length, + callback, + i = 0; + if (Array.isArray(arguments[0])) { + arr = arguments[0]; + callback = arguments[1]; + } else { + len = arguments.length; + // The later should not be the average use case + if (len !== 0 && (typeof arguments[len - 1] === 'function' || typeof arguments[len - 1] === 'undefined')) { + len--; + callback = arguments[len]; + } + arr = new Array(len); + for (; i < len; i += 1) { + arr[i] = arguments[i]; + } + } + var self = this; + var call_on_write = function () { + self.pub_sub_mode = self.pub_sub_mode || self.command_queue.length + 1; + }; + return this.internal_send_command('psubscribe', arr, callback, call_on_write); +}; + +Multi.prototype.psubscribe = Multi.prototype.PSUBSCRIBE = function psubscribe () { + var arr, + len = arguments.length, + callback, + i = 0; + if (Array.isArray(arguments[0])) { + arr = arguments[0]; + callback = arguments[1]; + } else { + len = arguments.length; + // The later should not be the average use case + if (len !== 0 && (typeof arguments[len - 1] === 'function' || typeof arguments[len - 1] === 'undefined')) { + len--; + callback = arguments[len]; + } + arr = new Array(len); + for (; i < len; i += 1) { + arr[i] = arguments[i]; + } + } + var self = this; + var call_on_write = function () { + self.pub_sub_mode = self.pub_sub_mode || self.command_queue.length + 1; + }; + this.queue.push(['psubscribe', arr, callback, call_on_write]); + return this; +}; + +RedisClient.prototype.punsubscribe = RedisClient.prototype.PUNSUBSCRIBE = function punsubscribe () { + var arr, + len = arguments.length, + callback, + i = 0; + if (Array.isArray(arguments[0])) { + arr = arguments[0]; + callback = arguments[1]; + } else { + len = arguments.length; + // The later should not be the average use case + if (len !== 0 && (typeof arguments[len - 1] === 'function' || typeof arguments[len - 1] === 'undefined')) { + len--; + callback = arguments[len]; + } + arr = new Array(len); + for (; i < len; i += 1) { + arr[i] = arguments[i]; + } + } + var self = this; + var call_on_write = function () { + // Pub sub has to be activated even if not in pub sub mode, as the return value is manipulated in the callback + self.pub_sub_mode = self.pub_sub_mode || self.command_queue.length + 1; + }; + return this.internal_send_command('punsubscribe', arr, callback, call_on_write); +}; + +Multi.prototype.punsubscribe = Multi.prototype.PUNSUBSCRIBE = function punsubscribe () { + var arr, + len = arguments.length, + callback, + i = 0; + if (Array.isArray(arguments[0])) { + arr = arguments[0]; + callback = arguments[1]; + } else { + len = arguments.length; + // The later should not be the average use case + if (len !== 0 && (typeof arguments[len - 1] === 'function' || typeof arguments[len - 1] === 'undefined')) { + len--; + callback = arguments[len]; + } + arr = new Array(len); + for (; i < len; i += 1) { + arr[i] = arguments[i]; + } + } + var self = this; + var call_on_write = function () { + // Pub sub has to be activated even if not in pub sub mode, as the return value is manipulated in the callback + self.pub_sub_mode = self.pub_sub_mode || self.command_queue.length + 1; + }; + this.queue.push(['punsubscribe', arr, callback, call_on_write]); + return this; +}; diff --git a/lib/multi.js b/lib/multi.js index bb2173cd2e6..bdf37fe6ade 100644 --- a/lib/multi.js +++ b/lib/multi.js @@ -20,45 +20,8 @@ function Multi (client, args) { } } -Multi.prototype.hmset = Multi.prototype.HMSET = function hmset () { - var arr, - len = 0, - callback, - i = 0; - if (Array.isArray(arguments[0])) { - arr = arguments[0]; - callback = arguments[1]; - } else if (Array.isArray(arguments[1])) { - len = arguments[1].length; - arr = new Array(len + 1); - arr[0] = arguments[0]; - for (; i < len; i += 1) { - arr[i + 1] = arguments[1][i]; - } - callback = arguments[2]; - } else if (typeof arguments[1] === 'object' && (typeof arguments[2] === 'function' || typeof arguments[2] === 'undefined')) { - arr = [arguments[0]]; - for (var field in arguments[1]) { // jshint ignore: line - arr.push(field, arguments[1][field]); - } - callback = arguments[2]; - } else { - len = arguments.length; - // The later should not be the average use case - if (len !== 0 && (typeof arguments[len - 1] === 'function' || typeof arguments[len - 1] === 'undefined')) { - len--; - callback = arguments[len]; - } - arr = new Array(len); - for (; i < len; i += 1) { - arr[i] = arguments[i]; - } - } - this.queue.push(['hmset', arr, callback]); - return this; -}; - -function pipeline_transaction_command (self, command, args, index, cb) { +function pipeline_transaction_command (self, command, args, index, cb, call_on_write) { + // Queueing is done first, then the commands are executed self._client.send_command(command, args, function (err, reply) { if (err) { if (cb) { @@ -131,20 +94,22 @@ Multi.prototype.exec_transaction = function exec_transaction (callback) { pipeline_transaction_command(self, 'multi', []); // Drain queue, callback will catch 'QUEUED' or error for (var index = 0; index < len; index++) { - var args = self.queue.get(index); - var command = args[0]; - var cb = args[2]; + // The commands may not be shifted off, since they are needed in the result handler + var command_obj = self.queue.get(index); + var command = command_obj[0]; + var cb = command_obj[2]; + var call_on_write = command_obj.length === 4 ? command_obj[3] : undefined; // Keep track of who wants buffer responses: if (self._client.options.detect_buffers) { self.wants_buffers[index] = false; - for (var i = 0; i < args[1].length; i += 1) { - if (args[1][i] instanceof Buffer) { + for (var i = 0; i < command_obj[1].length; i += 1) { + if (command_obj[1][i] instanceof Buffer) { self.wants_buffers[index] = true; break; } } } - pipeline_transaction_command(self, command, args[1], index, cb); + pipeline_transaction_command(self, command, command_obj[1], index, cb, call_on_write); } self._client.internal_send_command('exec', [], function (err, replies) { @@ -171,7 +136,18 @@ Multi.prototype.exec = Multi.prototype.EXEC = Multi.prototype.exec_batch = funct var self = this; var len = self.queue.length; var index = 0; - var args; + var command_obj; + self._client.cork(); + if (!callback) { + while (command_obj = self.queue.shift()) { + self._client.internal_send_command(command_obj[0], command_obj[1], command_obj[2], (command_obj.length === 4 ? command_obj[3] : undefined)); + } + self._client.uncork(); + return !self._client.should_buffer; + } else if (len === 0) { + utils.reply_in_order(self._client, callback, null, []); + return !self._client.should_buffer; + } var callback_without_own_cb = function (err, res) { if (err) { self.results.push(err); @@ -190,26 +166,20 @@ Multi.prototype.exec = Multi.prototype.EXEC = Multi.prototype.exec_batch = funct callback(null, self.results); }; }; - if (len === 0) { - if (callback) { - utils.reply_in_order(self._client, callback, null, []); - } - return true; - } self.results = []; - self._client.cork(); - while (args = self.queue.shift()) { - var command = args[0]; + while (command_obj = self.queue.shift()) { + var command = command_obj[0]; + var call_on_write = command_obj.length === 4 ? command_obj[3] : undefined; var cb; - if (typeof args[2] === 'function') { - cb = batch_callback(self, args[2], index); + if (typeof command_obj[2] === 'function') { + cb = batch_callback(self, command_obj[2], index); } else { cb = callback_without_own_cb; } if (typeof callback === 'function' && index === len - 1) { cb = last_callback(cb); } - self._client.internal_send_command(command, args[1], cb); + this._client.internal_send_command(command, command_obj[1], cb, call_on_write); index++; } self._client.uncork(); diff --git a/test/multi.spec.js b/test/multi.spec.js index 2ad9a230512..3227ecda2af 100644 --- a/test/multi.spec.js +++ b/test/multi.spec.js @@ -255,12 +255,12 @@ describe("The 'multi' method", function () { multi2.set('m2', '456'); multi1.set('m1', '123'); multi1.get('m1'); - multi2.get('m2'); + multi2.get('m1'); multi2.ping(); multi1.exec(end); multi2.exec(function (err, res) { - assert.strictEqual(res[1], '456'); + assert.strictEqual(res[1], '123'); end(); }); }); From 3038c9043de88a1ca85ccb028d7f798782ef0ad5 Mon Sep 17 00:00:00 2001 From: Ruben Bridgewater Date: Thu, 14 Apr 2016 01:14:41 +0200 Subject: [PATCH 0632/1748] Make sure all individual handled command work in multi context the same Fix quit possibly resulting in reconnections --- index.js | 31 +++++- lib/individualCommands.js | 228 ++++++++++++++++++++++++++------------ test/auth.spec.js | 42 +++++++ test/batch.spec.js | 16 ++- test/connection.spec.js | 21 ++++ test/multi.spec.js | 34 ++++++ test/node_redis.spec.js | 61 ++++++---- 7 files changed, 334 insertions(+), 99 deletions(-) diff --git a/index.js b/index.js index 8b5d917be97..f9b93396370 100644 --- a/index.js +++ b/index.js @@ -735,12 +735,35 @@ function return_pub_sub (self, reply) { } RedisClient.prototype.return_reply = function (reply) { - if (this.pub_sub_mode === 1 && reply instanceof Array && reply.length !== 0 && reply[0]) { + // If in monitor mode, all normal commands are still working and we only want to emit the streamlined commands + // As this is not the average use case and monitor is expensive anyway, let's change the code here, to improve + // the average performance of all other commands in case of no monitor mode + if (this.monitoring) { + var replyStr; + if (this.buffers && Buffer.isBuffer(reply)) { + replyStr = reply.toString(); + } else { + replyStr = reply; + } + // While reconnecting the redis server does not recognize the client as in monitor mode anymore + // Therefore the monitor command has to finish before it catches further commands + if (typeof replyStr === 'string' && utils.monitor_regex.test(replyStr)) { + var timestamp = replyStr.slice(0, replyStr.indexOf(' ')); + var args = replyStr.slice(replyStr.indexOf('"') + 1, -1).split('" "').map(function (elem) { + return elem.replace(/\\"/g, '"'); + }); + this.emit('monitor', timestamp, args, replyStr); + return; + } + } + if (this.pub_sub_mode === 0) { + normal_reply(this, reply); + } else if (this.pub_sub_mode !== 1) { + this.pub_sub_mode--; + normal_reply(this, reply); + } else if (reply instanceof Array && reply.length > 2 && reply[0]) { return_pub_sub(this, reply); } else { - if (this.pub_sub_mode !== 0 && this.pub_sub_mode !== 1) { - this.pub_sub_mode--; - } normal_reply(this, reply); } }; diff --git a/lib/individualCommands.js b/lib/individualCommands.js index 4ebd8ae1705..bcbbecd2fa6 100644 --- a/lib/individualCommands.js +++ b/lib/individualCommands.js @@ -31,88 +31,101 @@ RedisClient.prototype.batch = RedisClient.prototype.BATCH = function batch (args return new Multi(this, args); }; -// Store db in this.select_db to restore it on reconnect -RedisClient.prototype.select = RedisClient.prototype.SELECT = function select (db, callback) { - var self = this; - return this.internal_send_command('select', [db], function (err, res) { +function select_callback (self, db, callback) { + return function (err, res) { if (err === null) { + // Store db in this.select_db to restore it on reconnect self.selected_db = db; } utils.callback_or_emit(self, callback, err, res); - }); + }; +} + +RedisClient.prototype.select = RedisClient.prototype.SELECT = function select (db, callback) { + return this.internal_send_command('select', [db], select_callback(this, db, callback)); }; -RedisClient.prototype.monitor = RedisClient.prototype.MONITOR = function (callback) { - // Use a individual command, as this is a special case that does not has to be checked for any other command - var self = this; - return this.internal_send_command('monitor', [], function (err, res) { +Multi.prototype.select = Multi.prototype.SELECT = function select (db, callback) { + this.queue.push(['select', [db], select_callback(this._client, db, callback)]); + return this; +}; + +function monitor_callback (self, callback) { + return function (err, res) { if (err === null) { - self.reply_parser.returnReply = function (reply) { - // If in monitor mode, all normal commands are still working and we only want to emit the streamlined commands - // As this is not the average use case and monitor is expensive anyway, let's change the code here, to improve - // the average performance of all other commands in case of no monitor mode - if (self.monitoring) { - var replyStr; - if (self.buffers && Buffer.isBuffer(reply)) { - replyStr = reply.toString(); - } else { - replyStr = reply; - } - // While reconnecting the redis server does not recognize the client as in monitor mode anymore - // Therefor the monitor command has to finish before it catches further commands - if (typeof replyStr === 'string' && utils.monitor_regex.test(replyStr)) { - var timestamp = replyStr.slice(0, replyStr.indexOf(' ')); - var args = replyStr.slice(replyStr.indexOf('"') + 1, -1).split('" "').map(function (elem) { - return elem.replace(/\\"/g, '"'); - }); - self.emit('monitor', timestamp, args, replyStr); - return; - } - } - self.return_reply(reply); - }; self.monitoring = true; } utils.callback_or_emit(self, callback, err, res); - }); + }; +} + +RedisClient.prototype.monitor = RedisClient.prototype.MONITOR = function monitor (callback) { + // Use a individual command, as this is a special case that does not has to be checked for any other command + return this.internal_send_command('monitor', [], monitor_callback(this, callback)); }; -RedisClient.prototype.quit = RedisClient.prototype.QUIT = function (callback) { - var self = this; - var callback_hook = function (err, res) { - // TODO: Improve this by handling everything with coherend error codes and find out if there's anything missing - if (err && (err.code === 'NR_OFFLINE' || - err.message === 'Redis connection gone from close event.' || - err.message === 'The command can\'t be processed. The connection has already been closed.' - )) { +// Only works with batch, not in a transaction +Multi.prototype.monitor = Multi.prototype.MONITOR = function monitor (callback) { + // Use a individual command, as this is a special case that does not has to be checked for any other command + if (this.exec !== this.exec_transaction) { + this.queue.push(['monitor', [], monitor_callback(this._client, callback)]); + return this; + } + var err = new Error( + 'You used the monitor command in combination with a transaction. Due to faulty return values of ' + + 'Redis in this context, the monitor command is now executed without transaction instead and ignored ' + + 'in the multi statement.' + ); + err.command = 'MONITOR'; + utils.reply_in_order(this._client, callback, err); + this._client.monitor('monitor', callback); + return this; +}; + +function quit_callback (self, callback) { + return function (err, res) { + if (err && err.code === 'NR_OFFLINE') { // Pretent the quit command worked properly in this case. // Either the quit landed in the offline queue and was flushed at the reconnect // or the offline queue is deactivated and the command was rejected right away // or the stream is not writable - // or while sending the quit, the connection dropped + // or while sending the quit, the connection ended / closed err = null; res = 'OK'; } utils.callback_or_emit(self, callback, err, res); + if (self.stream.writable) { + // If the socket is still alive, kill it. This could happen if quit got a NR_OFFLINE error code + self.stream.destroy(); + } }; - var backpressure_indicator = this.internal_send_command('quit', [], callback_hook); +} + +RedisClient.prototype.QUIT = RedisClient.prototype.quit = function (callback) { + // TODO: Consider this for v.3 + // Allow the quit command to be fired as soon as possible to prevent it landing in the offline queue. + // this.ready = this.offline_queue.length === 0; + var backpressure_indicator = this.internal_send_command('quit', [], quit_callback(this, callback)); // Calling quit should always end the connection, no matter if there's a connection or not this.closing = true; + this.ready = false; return backpressure_indicator; }; -// Store info in this.server_info after each call -RedisClient.prototype.info = RedisClient.prototype.INFO = function info (section, callback) { - var self = this; - var ready = this.ready; - var args = []; - if (typeof section === 'function') { - callback = section; - } else if (section !== undefined) { - args = Array.isArray(section) ? section : [section]; - } - this.ready = ready || this.offline_queue.length === 0; // keep the execution order intakt - var tmp = this.internal_send_command('info', args, function (err, res) { +// Only works with batch, not in a transaction +Multi.prototype.QUIT = Multi.prototype.quit = function (callback) { + var self = this._client; + var call_on_write = function () { + // If called in a multi context, we expect redis is available + self.closing = true; + self.ready = false; + }; + this.queue.push(['quit', [], quit_callback(self, callback), call_on_write]); + return this; +}; + +function info_callback (self, callback) { + return function (err, res) { if (res) { var obj = {}; var lines = res.toString().split('\r\n'); @@ -146,20 +159,33 @@ RedisClient.prototype.info = RedisClient.prototype.INFO = function info (section self.server_info = {}; } utils.callback_or_emit(self, callback, err, res); - }); - this.ready = ready; - return tmp; + }; +} + +// Store info in this.server_info after each call +RedisClient.prototype.info = RedisClient.prototype.INFO = function info (section, callback) { + var args = []; + if (typeof section === 'function') { + callback = section; + } else if (section !== undefined) { + args = Array.isArray(section) ? section : [section]; + } + return this.internal_send_command('info', args, info_callback(this, callback)); }; -RedisClient.prototype.auth = RedisClient.prototype.AUTH = function auth (pass, callback) { - var self = this; - var ready = this.ready; - debug('Sending auth to ' + self.address + ' id ' + self.connection_id); +Multi.prototype.info = Multi.prototype.INFO = function info (section, callback) { + var args = []; + if (typeof section === 'function') { + callback = section; + } else if (section !== undefined) { + args = Array.isArray(section) ? section : [section]; + } + this.queue.push(['info', args, info_callback(this._client, callback)]); + return this; +}; - // Stash auth for connect and reconnect. - this.auth_pass = pass; - this.ready = ready || this.offline_queue.length === 0; // keep the execution order intakt - var tmp = this.internal_send_command('auth', [pass], function (err, res) { +function auth_callback (self, pass, callback) { + return function (err, res) { if (err) { if (no_password_is_set.test(err.message)) { self.warn('Warning: Redis server does not require a password, but a password was supplied.'); @@ -175,11 +201,31 @@ RedisClient.prototype.auth = RedisClient.prototype.AUTH = function auth (pass, c } } utils.callback_or_emit(self, callback, err, res); - }); + }; +} + +RedisClient.prototype.auth = RedisClient.prototype.AUTH = function auth (pass, callback) { + debug('Sending auth to ' + this.address + ' id ' + this.connection_id); + + // Stash auth for connect and reconnect. + this.auth_pass = pass; + var ready = this.ready; + this.ready = ready || this.offline_queue.length === 0; + var tmp = this.internal_send_command('auth', [pass], auth_callback(this, pass, callback)); this.ready = ready; return tmp; }; +// Only works with batch, not in a transaction +Multi.prototype.auth = Multi.prototype.AUTH = function auth (pass, callback) { + debug('Sending auth to ' + this.address + ' id ' + this.connection_id); + + // Stash auth for connect and reconnect. + this.auth_pass = pass; + this.queue.push(['auth', [pass], auth_callback(this._client, callback)]); + return this; +}; + RedisClient.prototype.hmset = RedisClient.prototype.HMSET = function hmset () { var arr, len = arguments.length, @@ -198,7 +244,7 @@ RedisClient.prototype.hmset = RedisClient.prototype.HMSET = function hmset () { for (; i < len; i += 1) { arr[i + 1] = arguments[1][i]; } - } else if (typeof arguments[1] === 'object' && (arguments.length === 2 || arguments.length === 3 && typeof arguments[2] === 'function' || typeof arguments[2] === 'undefined')) { + } else if (typeof arguments[1] === 'object' && (arguments.length === 2 || arguments.length === 3 && (typeof arguments[2] === 'function' || typeof arguments[2] === 'undefined'))) { arr = [arguments[0]]; for (var field in arguments[1]) { // jshint ignore: line arr.push(field, arguments[1][field]); @@ -219,6 +265,46 @@ RedisClient.prototype.hmset = RedisClient.prototype.HMSET = function hmset () { return this.internal_send_command('hmset', arr, callback); }; +Multi.prototype.hmset = Multi.prototype.HMSET = function hmset () { + var arr, + len = arguments.length, + callback, + i = 0; + if (Array.isArray(arguments[0])) { + arr = arguments[0]; + callback = arguments[1]; + } else if (Array.isArray(arguments[1])) { + if (len === 3) { + callback = arguments[2]; + } + len = arguments[1].length; + arr = new Array(len + 1); + arr[0] = arguments[0]; + for (; i < len; i += 1) { + arr[i + 1] = arguments[1][i]; + } + } else if (typeof arguments[1] === 'object' && (arguments.length === 2 || arguments.length === 3 && (typeof arguments[2] === 'function' || typeof arguments[2] === 'undefined'))) { + arr = [arguments[0]]; + for (var field in arguments[1]) { // jshint ignore: line + arr.push(field, arguments[1][field]); + } + callback = arguments[2]; + } else { + len = arguments.length; + // The later should not be the average use case + if (len !== 0 && (typeof arguments[len - 1] === 'function' || typeof arguments[len - 1] === 'undefined')) { + len--; + callback = arguments[len]; + } + arr = new Array(len); + for (; i < len; i += 1) { + arr[i] = arguments[i]; + } + } + this.queue.push(['hmset', arr, callback]); + return this; +}; + RedisClient.prototype.subscribe = RedisClient.prototype.SUBSCRIBE = function subscribe () { var arr, len = arguments.length, @@ -378,7 +464,7 @@ Multi.prototype.psubscribe = Multi.prototype.PSUBSCRIBE = function psubscribe () arr[i] = arguments[i]; } } - var self = this; + var self = this._client; var call_on_write = function () { self.pub_sub_mode = self.pub_sub_mode || self.command_queue.length + 1; }; @@ -434,7 +520,7 @@ Multi.prototype.punsubscribe = Multi.prototype.PUNSUBSCRIBE = function punsubscr arr[i] = arguments[i]; } } - var self = this; + var self = this._client; var call_on_write = function () { // Pub sub has to be activated even if not in pub sub mode, as the return value is manipulated in the callback self.pub_sub_mode = self.pub_sub_mode || self.command_queue.length + 1; diff --git a/test/auth.spec.js b/test/auth.spec.js index 4c262dccd23..ac6548e0488 100644 --- a/test/auth.spec.js +++ b/test/auth.spec.js @@ -291,6 +291,48 @@ describe('client authentication', function () { }); }); }); + + it('indivdual commands work properly with batch', function (done) { + // quit => might return an error instead of "OK" in the exec callback... (if not connected) + // auth => might return an error instead of "OK" in the exec callback... (if no password is required / still loading on Redis <= 2.4) + // This could be fixed by checking the return value of the callback in the exec callback and + // returning the manipulated [error, result] from the callback. + // There should be a better solution though + + var args = config.configureClient(parser, 'localhost', { + noReadyCheck: true + }); + client = redis.createClient.apply(redis.createClient, args); + assert.strictEqual(client.selected_db, undefined); + var end = helper.callFuncAfter(done, 8); + client.on('monitor', function () { + end(); // Should be called for each command after monitor + }); + client.batch() + .auth(auth) + .SELECT(5, function (err, res) { + assert.strictEqual(client.selected_db, 5); + assert.strictEqual(res, 'OK'); + assert.notDeepEqual(client.serverInfo.db5, { avg_ttl: 0, expires: 0, keys: 1 }); + }) + .monitor() + .set('foo', 'bar', helper.isString('OK')) + .INFO(function (err, res) { + assert.strictEqual(res.indexOf('# Server\r\nredis_version:'), 0); + assert.deepEqual(client.serverInfo.db5, { avg_ttl: 0, expires: 0, keys: 1 }); + }) + .get('foo', helper.isString('bar')) + .subscribe(['foo', 'bar']) + .unsubscribe('foo') + .SUBSCRIBE('/foo', helper.isString('/foo')) + .psubscribe('*') + .quit(helper.isString('OK')) // this might be interesting + .exec(function (err, res) { + res[4] = res[4].substr(0, 10); + assert.deepEqual(res, ['OK', 'OK', 'OK', 'OK', '# Server\r\n', 'bar', 'bar', 'foo', '/foo', '*', 'OK']); + end(); + }); + }); }); }); diff --git a/test/batch.spec.js b/test/batch.spec.js index 5f25be65cba..7b382eee421 100644 --- a/test/batch.spec.js +++ b/test/batch.spec.js @@ -12,8 +12,6 @@ describe("The 'batch' method", function () { describe('using ' + parser + ' and ' + ip, function () { describe('when not connected', function () { - // TODO: This is somewhat broken and should be fixed in v.3 - // The commands should return an error instead of returning an empty result var client; beforeEach(function (done) { @@ -24,7 +22,7 @@ describe("The 'batch' method", function () { client.on('end', done); }); - it('returns an empty array', function (done) { + it('returns an empty array for missing commands', function (done) { var batch = client.batch(); batch.exec(function (err, res) { assert.strictEqual(err, null); @@ -33,7 +31,17 @@ describe("The 'batch' method", function () { }); }); - it('returns an empty array if promisified', function () { + it('returns an error for batch with commands', function (done) { + var batch = client.batch(); + batch.set('foo', 'bar'); + batch.exec(function (err, res) { + assert.strictEqual(err, null); + assert.strictEqual(res[0].code, 'NR_OFFLINE'); + done(); + }); + }); + + it('returns an empty array for missing commands if promisified', function () { return client.batch().execAsync().then(function (res) { assert.strictEqual(res.length, 0); }); diff --git a/test/connection.spec.js b/test/connection.spec.js index ad91730c350..531dce59bf5 100644 --- a/test/connection.spec.js +++ b/test/connection.spec.js @@ -93,6 +93,27 @@ describe('connection tests', function () { assert.strictEqual(bool, false); }); + it('calling quit while connected without offline queue should end the connection when all commands have finished', function (done) { + var called = false; + client = redis.createClient({ + enable_offline_queue: false + }); + client.on('ready', function () { + client.set('foo', 'bar', function (err, res) { + assert.strictEqual(res, 'OK'); + called = true; + }); + var bool = client.quit(function (err, res) { + assert.strictEqual(res, 'OK'); + assert.strictEqual(err, null); + assert(called); + done(); + }); + // TODO: In v.3 the quit command would be fired right away, so bool should be true + assert.strictEqual(bool, true); + }); + }); + it('do not quit before connected or a connection issue is detected', function (done) { client = redis.createClient(); client.set('foo', 'bar', helper.isString('OK')); diff --git a/test/multi.spec.js b/test/multi.spec.js index 3227ecda2af..ec2a81aafc1 100644 --- a/test/multi.spec.js +++ b/test/multi.spec.js @@ -628,6 +628,40 @@ describe("The 'multi' method", function () { client.stream.destroy(); }); + it('indivdual commands work properly with multi', function (done) { + // Neither of the following work properly in a transactions: + // (This is due to Redis not returning the reply as expected / resulting in undefined behavior) + // (Likely there are more commands that do not work with a transaction) + // + // auth => can't be called after a multi command + // monitor => results in faulty return values e.g. multi().monitor().set('foo', 'bar').get('foo') + // returns ['OK, 'OK', 'monitor reply'] instead of ['OK', 'OK', 'bar'] + // quit => ends the connection before the exec + // client reply skip|off => results in weird return values. Not sure what exactly happens + // subscribe => enters subscribe mode and this does not work in combination with exec (the same for psubscribe, unsubscribe...) + // + + assert.strictEqual(client.selected_db, undefined); + var multi = client.multi(); + multi.select(5, function (err, res) { + assert.strictEqual(client.selected_db, 5); + assert.strictEqual(res, 'OK'); + assert.notDeepEqual(client.server_info.db5, { avg_ttl: 0, expires: 0, keys: 1 }); + }); + // multi.client('reply', 'on', helper.isString('OK')); // Redis v.3.2 + multi.set('foo', 'bar', helper.isString('OK')); + multi.info(function (err, res) { + assert.strictEqual(res.indexOf('# Server\r\nredis_version:'), 0); + assert.deepEqual(client.server_info.db5, { avg_ttl: 0, expires: 0, keys: 1 }); + }); + multi.get('foo', helper.isString('bar')); + multi.exec(function (err, res) { + res[3] = res[3].substr(0, 10); + assert.deepEqual(res, ['OK', 'OK', '# Server\r\n', 'bar']); + done(); + }); + }); + }); }); }); diff --git a/test/node_redis.spec.js b/test/node_redis.spec.js index fdfc503d534..774e57a46d1 100644 --- a/test/node_redis.spec.js +++ b/test/node_redis.spec.js @@ -618,6 +618,26 @@ describe('The node_redis client', function () { }); }); + it('monitors reconnects properly and works with the offline queue in a batch statement', function (done) { + var i = 0; + var multi = client.batch(); + multi.MONITOR(helper.isString('OK')); + multi.mget('hello', 'world'); + multi.exec(function (err, res) { + assert.deepEqual(res, ['OK', [null, null]]); + }); + client.on('monitor', function (time, args, rawOutput) { + assert(utils.monitor_regex.test(rawOutput), rawOutput); + assert.deepEqual(args, ['mget', 'hello', 'world']); + if (i++ === 2) { + // End after two reconnects + return done(); + } + client.stream.destroy(); + client.mget('hello', 'world'); + }); + }); + it('monitor does not activate if the command could not be processed properly', function (done) { client.MONITOR(function (err, res) { assert.strictEqual(err.code, 'UNCERTAIN_STATE'); @@ -748,26 +768,27 @@ describe('The node_redis client', function () { }); }); - it('should fire early', function (done) { - client = redis.createClient.apply(null, args); - var fired = false; - client.info(function (err, res) { - fired = true; - }); - client.set('foo', 'bar', function (err, res) { - assert(fired); - done(); - }); - assert.strictEqual(client.offline_queue.length, 1); - assert.strictEqual(client.command_queue.length, 1); - client.on('connect', function () { - assert.strictEqual(client.offline_queue.length, 1); - assert.strictEqual(client.command_queue.length, 1); - }); - client.on('ready', function () { - assert.strictEqual(client.offline_queue.length, 0); - }); - }); + // TODO: consider allowing loading commands in v.3 + // it('should fire early', function (done) { + // client = redis.createClient.apply(null, args); + // var fired = false; + // client.info(function (err, res) { + // fired = true; + // }); + // client.set('foo', 'bar', function (err, res) { + // assert(fired); + // done(); + // }); + // assert.strictEqual(client.offline_queue.length, 1); + // assert.strictEqual(client.command_queue.length, 1); + // client.on('connect', function () { + // assert.strictEqual(client.offline_queue.length, 1); + // assert.strictEqual(client.command_queue.length, 1); + // }); + // client.on('ready', function () { + // assert.strictEqual(client.offline_queue.length, 0); + // }); + // }); }); describe('socket_nodelay', function () { From 97ae78877b3da8b5a99ba49486b852ae501cef8b Mon Sep 17 00:00:00 2001 From: Ruben Bridgewater Date: Thu, 14 Apr 2016 01:17:43 +0200 Subject: [PATCH 0633/1748] Implement CLIENT REPLY ON|OFF|SKIP --- index.js | 17 +++++++ lib/individualCommands.js | 91 ++++++++++++++++++++++++++++++++++++ lib/utils.js | 12 +++-- test/commands/client.spec.js | 86 +++++++++++++++++++++++++++++----- test/helper.js | 51 ++++++++++++++++---- 5 files changed, 233 insertions(+), 24 deletions(-) diff --git a/index.js b/index.js index f9b93396370..ad21aae0c2b 100644 --- a/index.js +++ b/index.js @@ -150,6 +150,7 @@ function RedisClient (options, stream) { this.times_connected = 0; this.options = options; this.buffers = options.return_buffers || options.detect_buffers; + this.reply = 'ON'; // Returning replies is the default // Init parser this.reply_parser = create_parser(this, options); this.create_stream(); @@ -901,6 +902,22 @@ RedisClient.prototype.internal_send_command = function (command, args, callback, if (call_on_write) { call_on_write(); } + // Handle `CLIENT REPLY ON|OFF|SKIP` + // This has to be checked after call_on_write + if (this.reply === 'ON') { + this.command_queue.push(command_obj); + } else { + // Do not expect a reply + // Does this work in combination with the pub sub mode? + if (callback) { + utils.reply_in_order(this, callback, null, undefined, this.command_queue); + } + if (this.reply === 'SKIP') { + this.reply = 'SKIP_ONE_MORE'; + } else if (this.reply === 'SKIP_ONE_MORE') { + this.reply = 'ON'; + } + } return !this.should_buffer; }; diff --git a/lib/individualCommands.js b/lib/individualCommands.js index bcbbecd2fa6..de03e210f37 100644 --- a/lib/individualCommands.js +++ b/lib/individualCommands.js @@ -226,6 +226,97 @@ Multi.prototype.auth = Multi.prototype.AUTH = function auth (pass, callback) { return this; }; +RedisClient.prototype.client = RedisClient.prototype.CLIENT = function client () { + var arr, + len = arguments.length, + callback, + i = 0; + if (Array.isArray(arguments[0])) { + arr = arguments[0]; + callback = arguments[1]; + } else if (Array.isArray(arguments[1])) { + if (len === 3) { + callback = arguments[2]; + } + len = arguments[1].length; + arr = new Array(len + 1); + arr[0] = arguments[0]; + for (; i < len; i += 1) { + arr[i + 1] = arguments[1][i]; + } + } else { + len = arguments.length; + // The later should not be the average use case + if (len !== 0 && (typeof arguments[len - 1] === 'function' || typeof arguments[len - 1] === 'undefined')) { + len--; + callback = arguments[len]; + } + arr = new Array(len); + for (; i < len; i += 1) { + arr[i] = arguments[i]; + } + } + var self = this; + var call_on_write = undefined; + // CLIENT REPLY ON|OFF|SKIP + /* istanbul ignore next: TODO: Remove this as soon as Travis runs Redis 3.2 */ + if (arr.length === 2 && arr[0].toString().toUpperCase() === 'REPLY') { + var reply_on_off = arr[1].toString().toUpperCase(); + if (reply_on_off === 'ON' || reply_on_off === 'OFF' || reply_on_off === 'SKIP') { + call_on_write = function () { + self.reply = reply_on_off; + }; + } + } + return this.internal_send_command('client', arr, callback, call_on_write); +}; + +Multi.prototype.client = Multi.prototype.CLIENT = function client () { + var arr, + len = arguments.length, + callback, + i = 0; + if (Array.isArray(arguments[0])) { + arr = arguments[0]; + callback = arguments[1]; + } else if (Array.isArray(arguments[1])) { + if (len === 3) { + callback = arguments[2]; + } + len = arguments[1].length; + arr = new Array(len + 1); + arr[0] = arguments[0]; + for (; i < len; i += 1) { + arr[i + 1] = arguments[1][i]; + } + } else { + len = arguments.length; + // The later should not be the average use case + if (len !== 0 && (typeof arguments[len - 1] === 'function' || typeof arguments[len - 1] === 'undefined')) { + len--; + callback = arguments[len]; + } + arr = new Array(len); + for (; i < len; i += 1) { + arr[i] = arguments[i]; + } + } + var self = this._client; + var call_on_write = undefined; + // CLIENT REPLY ON|OFF|SKIP + /* istanbul ignore next: TODO: Remove this as soon as Travis runs Redis 3.2 */ + if (arr.length === 2 && arr[0].toString().toUpperCase() === 'REPLY') { + var reply_on_off = arr[1].toString().toUpperCase(); + if (reply_on_off === 'ON' || reply_on_off === 'OFF' || reply_on_off === 'SKIP') { + call_on_write = function () { + self.reply = reply_on_off; + }; + } + } + this.queue.push(['client', arr, callback, call_on_write]); + return this; +}; + RedisClient.prototype.hmset = RedisClient.prototype.HMSET = function hmset () { var arr, len = arguments.length, diff --git a/lib/utils.js b/lib/utils.js index 1b6ee62124f..cd04429b0c7 100644 --- a/lib/utils.js +++ b/lib/utils.js @@ -90,9 +90,15 @@ function callbackOrEmit (self, callback, err, res) { } } -function replyInOrder (self, callback, err, res) { - // The offline queue has to be checked first, as there might be commands in both queues at the same time - var command_obj = self.offline_queue.peekBack() || self.command_queue.peekBack(); +function replyInOrder (self, callback, err, res, queue) { + // If the queue is explicitly passed, use that, otherwise fall back to the offline queue first, + // as there might be commands in both queues at the same time + var command_obj; + if (queue) { + command_obj = queue.peekBack(); + } else { + command_obj = self.offline_queue.peekBack() || self.command_queue.peekBack(); + } if (!command_obj) { process.nextTick(function () { callbackOrEmit(self, callback, err, res); diff --git a/test/commands/client.spec.js b/test/commands/client.spec.js index d73c5036aa5..935db8b6926 100644 --- a/test/commands/client.spec.js +++ b/test/commands/client.spec.js @@ -30,25 +30,89 @@ describe("The 'client' method", function () { }); it("lists connected clients when invoked with multi's chaining syntax", function (done) { - client.multi().client('list').exec(function (err, results) { - assert(pattern.test(results[0]), "expected string '" + results + "' to match " + pattern.toString()); - return done(); - }); + client.multi().client('list', helper.isType.string()).exec(helper.match(pattern, done)); }); it('lists connected clients when invoked with array syntax on client', function (done) { - client.multi().client(['list']).exec(function (err, results) { - assert(pattern.test(results[0]), "expected string '" + results + "' to match " + pattern.toString()); - return done(); - }); + client.multi().client(['list']).exec(helper.match(pattern, done)); }); it("lists connected clients when invoked with multi's array syntax", function (done) { client.multi([ ['client', 'list'] - ]).exec(function (err, results) { - assert(pattern.test(results[0]), "expected string '" + results + "' to match " + pattern.toString()); - return done(); + ]).exec(helper.match(pattern, done)); + }); + }); + + describe('reply', function () { + describe('as normal command', function () { + it('on', function (done) { + helper.serverVersionAtLeast.call(this, client, [3, 2, 0]); + assert.strictEqual(client.reply, 'ON'); + client.client('reply', 'on', helper.isString('OK')); + assert.strictEqual(client.reply, 'ON'); + client.set('foo', 'bar', done); + }); + + it('off', function (done) { + helper.serverVersionAtLeast.call(this, client, [3, 2, 0]); + assert.strictEqual(client.reply, 'ON'); + client.client(new Buffer('REPLY'), 'OFF', helper.isUndefined()); + assert.strictEqual(client.reply, 'OFF'); + client.set('foo', 'bar', helper.isUndefined(done)); + }); + + it('skip', function (done) { + helper.serverVersionAtLeast.call(this, client, [3, 2, 0]); + assert.strictEqual(client.reply, 'ON'); + client.client('REPLY', new Buffer('SKIP'), helper.isUndefined()); + assert.strictEqual(client.reply, 'SKIP_ONE_MORE'); + client.set('foo', 'bar', helper.isUndefined()); + client.get('foo', helper.isString('bar', done)); + }); + }); + + describe('in a batch context', function () { + it('on', function (done) { + helper.serverVersionAtLeast.call(this, client, [3, 2, 0]); + var batch = client.batch(); + assert.strictEqual(client.reply, 'ON'); + batch.client('reply', 'on', helper.isString('OK')); + assert.strictEqual(client.reply, 'ON'); + batch.set('foo', 'bar'); + batch.exec(function (err, res) { + assert.deepEqual(res, ['OK', 'OK']); + done(err); + }); + }); + + it('off', function (done) { + helper.serverVersionAtLeast.call(this, client, [3, 2, 0]); + var batch = client.batch(); + assert.strictEqual(client.reply, 'ON'); + batch.set('hello', 'world'); + batch.client(new Buffer('REPLY'), new Buffer('OFF'), helper.isUndefined()); + batch.set('foo', 'bar', helper.isUndefined()); + batch.exec(function (err, res) { + assert.strictEqual(client.reply, 'OFF'); + assert.deepEqual(res, ['OK', undefined, undefined]); + done(err); + }); + }); + + it('skip', function (done) { + helper.serverVersionAtLeast.call(this, client, [3, 2, 0]); + assert.strictEqual(client.reply, 'ON'); + client.batch() + .set('hello', 'world') + .client('REPLY', 'SKIP', helper.isUndefined()) + .set('foo', 'bar', helper.isUndefined()) + .get('foo') + .exec(function (err, res) { + assert.strictEqual(client.reply, 'ON'); + assert.deepEqual(res, ['OK', undefined, undefined, 'bar']); + done(err); + }); }); }); }); diff --git a/test/helper.js b/test/helper.js index f28568b5003..bb408a19fd1 100644 --- a/test/helper.js +++ b/test/helper.js @@ -29,6 +29,14 @@ if (!process.env.REDIS_TESTS_STARTED) { }); } +function arrayHelper (results) { + if (results instanceof Array) { + assert.strictEqual(results.length, 1, 'The array length may only be one element'); + return results[0]; + } + return results; +} + module.exports = { redisProcess: function () { return rp; @@ -52,8 +60,9 @@ module.exports = { }, isNumber: function (expected, done) { return function (err, results) { - assert.strictEqual(null, err, 'expected ' + expected + ', got error: ' + err); - assert.strictEqual(expected, results, expected + ' !== ' + results); + assert.strictEqual(err, null, 'expected ' + expected + ', got error: ' + err); + results = arrayHelper(results); + assert.strictEqual(results, expected, expected + ' !== ' + results); assert.strictEqual(typeof results, 'number', 'expected a number, got ' + typeof results); if (done) done(); }; @@ -61,18 +70,28 @@ module.exports = { isString: function (str, done) { str = '' + str; // Make sure it's a string return function (err, results) { - assert.strictEqual(null, err, "expected string '" + str + "', got error: " + err); + assert.strictEqual(err, null, "expected string '" + str + "', got error: " + err); + results = arrayHelper(results); if (Buffer.isBuffer(results)) { // If options are passed to return either strings or buffers... results = results.toString(); } - assert.strictEqual(str, results, str + ' does not match ' + results); + assert.strictEqual(results, str, str + ' does not match ' + results); if (done) done(); }; }, isNull: function (done) { return function (err, results) { - assert.strictEqual(null, err, 'expected null, got error: ' + err); - assert.strictEqual(null, results, results + ' is not null'); + assert.strictEqual(err, null, 'expected null, got error: ' + err); + results = arrayHelper(results); + assert.strictEqual(results, null, results + ' is not null'); + if (done) done(); + }; + }, + isUndefined: function (done) { + return function (err, results) { + assert.strictEqual(err, null, 'expected null, got error: ' + err); + results = arrayHelper(results); + assert.strictEqual(results, undefined, results + ' is not undefined'); if (done) done(); }; }, @@ -91,27 +110,39 @@ module.exports = { isType: { number: function (done) { return function (err, results) { - assert.strictEqual(null, err, 'expected any number, got error: ' + err); + assert.strictEqual(err, null, 'expected any number, got error: ' + err); assert.strictEqual(typeof results, 'number', results + ' is not a number'); if (done) done(); }; }, + string: function (done) { + return function (err, results) { + assert.strictEqual(err, null, 'expected any string, got error: ' + err); + assert.strictEqual(typeof results, 'string', results + ' is not a string'); + if (done) done(); + }; + }, positiveNumber: function (done) { return function (err, results) { - assert.strictEqual(null, err, 'expected positive number, got error: ' + err); - assert.strictEqual(true, (results > 0), results + ' is not a positive number'); + assert.strictEqual(err, null, 'expected positive number, got error: ' + err); + assert(results > 0, results + ' is not a positive number'); if (done) done(); }; } }, match: function (pattern, done) { return function (err, results) { - assert.strictEqual(null, err, 'expected ' + pattern.toString() + ', got error: ' + err); + assert.strictEqual(err, null, 'expected ' + pattern.toString() + ', got error: ' + err); + results = arrayHelper(results); assert(pattern.test(results), "expected string '" + results + "' to match " + pattern.toString()); if (done) done(); }; }, serverVersionAtLeast: function (connection, desired_version) { + // Wait until a connection has established (otherwise a timeout is going to be triggered at some point) + if (Object.keys(connection.server_info).length === 0) { + throw new Error('Version check not possible as the client is not yet ready or did not expose the version'); + } // Return true if the server version >= desired_version var version = connection.server_info.versions; for (var i = 0; i < 3; i++) { From a857829a36ba88ded2cfba9d82721964879169a4 Mon Sep 17 00:00:00 2001 From: Ruben Bridgewater Date: Thu, 14 Apr 2016 02:08:12 +0200 Subject: [PATCH 0634/1748] Improve error handling Arguments are now passed to an command error in case they exist An error is only emitted if that very same error is not already handled in a callback --- index.js | 58 +++++++++++++++++++++++++++++++---------- lib/customError.js | 16 ++++++++++++ lib/extendedApi.js | 5 +++- lib/multi.js | 12 ++++----- test/auth.spec.js | 2 +- test/connection.spec.js | 23 +++++----------- test/multi.spec.js | 3 ++- test/node_redis.spec.js | 30 ++++++++++++++++----- 8 files changed, 103 insertions(+), 46 deletions(-) create mode 100644 lib/customError.js diff --git a/index.js b/index.js index ad21aae0c2b..9161e358a47 100644 --- a/index.js +++ b/index.js @@ -5,6 +5,7 @@ var tls = require('tls'); var util = require('util'); var utils = require('./lib/utils'); var Queue = require('double-ended-queue'); +var CommandError = require('./lib/customError'); var Command = require('./lib/command').Command; var OfflineCommand = require('./lib/command').OfflineCommand; var EventEmitter = require('events'); @@ -264,11 +265,11 @@ RedisClient.prototype.create_stream = function () { }); this.stream.once('close', function (hadError) { - self.connection_gone('close', new Error('Stream connection closed' + (hadError ? ' because of a transmission error' : ''))); + self.connection_gone('close', hadError ? new Error('Stream connection closed with a transmission error') : null); }); this.stream.once('end', function () { - self.connection_gone('end', new Error('Stream connection ended')); + self.connection_gone('end', null); }); this.stream.on('drain', function () { @@ -320,16 +321,29 @@ RedisClient.prototype.warn = function (msg) { // Flush provided queues, erroring any items with a callback first RedisClient.prototype.flush_and_error = function (error, queue_names) { + var callbacks_not_called = []; queue_names = queue_names || ['offline_queue', 'command_queue']; for (var i = 0; i < queue_names.length; i++) { for (var command_obj = this[queue_names[i]].shift(); command_obj; command_obj = this[queue_names[i]].shift()) { + var err = new CommandError(error); + err.command = command_obj.command.toUpperCase(); + if (command_obj.args.length) { + err.args = command_obj.args; + } if (typeof command_obj.callback === 'function') { - error.command = command_obj.command.toUpperCase(); - command_obj.callback(error); + command_obj.callback(err); + } else { + callbacks_not_called.push(err); } } this[queue_names[i]] = new Queue(); } + // Mutate the original error that will be emitted + // This is fine, as we don't manipulate any user errors + if (callbacks_not_called.length !== 0) { + error.errors = callbacks_not_called; + } + return callbacks_not_called.length === 0; }; RedisClient.prototype.on_error = function (err) { @@ -546,8 +560,10 @@ RedisClient.prototype.connection_gone = function (why, error) { // If this is a requested shutdown, then don't retry if (this.closing) { - debug('Connection ended from quit command, not retrying.'); - this.flush_and_error(new Error('Redis connection gone from ' + why + ' event.')); + debug('Connection ended by quit / end command, not retrying.'); + error = new Error('Stream connection ended and running command aborted. It might have been processed.'); + error.code = 'NR_OFFLINE'; + this.flush_and_error(error); return; } @@ -567,10 +583,18 @@ RedisClient.prototype.connection_gone = function (why, error) { if (typeof this.retry_delay !== 'number') { // Pass individual error through if (this.retry_delay instanceof Error) { - error = this.retry_delay; + error = new CommandError(this.retry_delay); + } + // Attention: there might be the case where there's no error! + if (!error) { + error = new Error('Stream connection ended and running command aborted. It might have been processed.'); + error.code = 'NR_OFFLINE'; + } + // Only emit an error in case that a running command had no callback + if (!this.flush_and_error(error)) { + error.message = 'Stream connection ended and all running commands aborted. They might have been processed.'; + this.emit('error', error); } - this.flush_and_error(error); - this.emit('error', error); this.end(false); return; } @@ -595,11 +619,11 @@ RedisClient.prototype.connection_gone = function (why, error) { } else if (this.command_queue.length !== 0) { error = new Error('Redis connection lost and command aborted in uncertain state. It might have been processed.'); error.code = 'UNCERTAIN_STATE'; - this.flush_and_error(error, ['command_queue']); - error.message = 'Redis connection lost and commands aborted in uncertain state. They might have been processed.'; - // TODO: Reconsider emitting this always, as each running command is handled anyway - // This should likely be removed in v.3. This is different to the broken connection as we'll reconnect here - this.emit('error', error); + if (!this.flush_and_error(error, ['command_queue'])) { + // Only emit if not all commands had a callback that already handled the error + error.message = 'Redis connection lost and commands aborted in uncertain state. They might have been processed.'; + this.emit('error', error); + } } if (this.retry_max_delay !== null && this.retry_delay > this.retry_max_delay) { @@ -618,6 +642,9 @@ RedisClient.prototype.return_error = function (err) { var command_obj = this.command_queue.shift(); if (command_obj && command_obj.command && command_obj.command.toUpperCase) { err.command = command_obj.command.toUpperCase(); + if (command_obj.args.length) { + err.args = command_obj.args; + } } var match = err.message.match(utils.err_code); @@ -786,6 +813,9 @@ function handle_offline_command (self, command_obj) { } err = new Error(command + " can't be processed. " + msg); err.command = command; + if (command_obj.args.length) { + err.args = command_obj.args; + } err.code = 'NR_OFFLINE'; utils.reply_in_order(self, callback, err); } else { diff --git a/lib/customError.js b/lib/customError.js new file mode 100644 index 00000000000..07101b58063 --- /dev/null +++ b/lib/customError.js @@ -0,0 +1,16 @@ +'use strict'; + +var util = require('util'); + +function CommandError (error) { + Error.captureStackTrace(this, this.constructor); + this.name = this.constructor.name; + this.message = error.message; + for (var keys = Object.keys(error), key = keys.pop(); key; key = keys.pop()) { + this[key] = error[key]; + } +} + +util.inherits(CommandError, Error); + +module.exports = CommandError; diff --git a/lib/extendedApi.js b/lib/extendedApi.js index 5cd78038f06..e512ae97e36 100644 --- a/lib/extendedApi.js +++ b/lib/extendedApi.js @@ -47,7 +47,10 @@ RedisClient.prototype.send_command = RedisClient.prototype.sendCommand = functio RedisClient.prototype.end = function (flush) { // Flush queue if wanted if (flush) { - this.flush_and_error(new Error("The command can't be processed. The connection has already been closed.")); + var err = new Error("The command can't be processed. The connection has already been closed."); + err.code = 'NR_OFFLINE'; + this.flush_and_error(err); + // TODO: Emit an error in case a command did not have a callback } else if (arguments.length === 0) { this.warn( 'Using .end() without the flush parameter is deprecated and throws from v.3.0.0 on.\n' + diff --git a/lib/multi.js b/lib/multi.js index bdf37fe6ade..a4399473c98 100644 --- a/lib/multi.js +++ b/lib/multi.js @@ -23,7 +23,8 @@ function Multi (client, args) { function pipeline_transaction_command (self, command, args, index, cb, call_on_write) { // Queueing is done first, then the commands are executed self._client.send_command(command, args, function (err, reply) { - if (err) { + // Ignore the multi command. This is applied by node_redis and the user does not benefit by it + if (err && index !== -1) { if (cb) { cb(err); } @@ -44,13 +45,12 @@ function multi_callback (self, err, replies) { var i = 0, args; if (err) { - // The errors would be circular - var connection_error = ['CONNECTION_BROKEN', 'UNCERTAIN_STATE'].indexOf(err.code) !== -1; - err.errors = connection_error ? [] : self.errors; + err.errors = self.errors; + err.command = 'EXEC'; if (self.callback) { self.callback(err); // Exclude connection errors so that those errors won't be emitted twice - } else if (!connection_error) { + } else if (err.code !== 'CONNECTION_BROKEN') { self._client.emit('error', err); } return; @@ -91,7 +91,7 @@ Multi.prototype.exec_transaction = function exec_transaction (callback) { self.callback = callback; self._client.cork(); self.wants_buffers = new Array(len); - pipeline_transaction_command(self, 'multi', []); + pipeline_transaction_command(self, 'multi', [], -1); // Drain queue, callback will catch 'QUEUED' or error for (var index = 0; index < len; index++) { // The commands may not be shifted off, since they are needed in the result handler diff --git a/test/auth.spec.js b/test/auth.spec.js index ac6548e0488..38f46855609 100644 --- a/test/auth.spec.js +++ b/test/auth.spec.js @@ -183,7 +183,7 @@ describe('client authentication', function () { } }); client.on('reconnecting', function (params) { - assert.strictEqual(params.error.message, 'Stream connection closed'); + assert.strictEqual(params.error, null); }); }); diff --git a/test/connection.spec.js b/test/connection.spec.js index 531dce59bf5..9b5634ab67b 100644 --- a/test/connection.spec.js +++ b/test/connection.spec.js @@ -53,7 +53,7 @@ describe('connection tests', function () { } }); client.set('foo', 'bar', function (err, res) { - assert.strictEqual(err.message, 'Redis connection gone from close event.'); + assert.strictEqual(err.message, 'Stream connection ended and running command aborted. It might have been processed.'); called = -1; }); }); @@ -62,7 +62,7 @@ describe('connection tests', function () { var called = false; client = redis.createClient(9999); client.set('foo', 'bar', function (err, res) { - assert.strictEqual(err.message, 'Redis connection gone from close event.'); + assert.strictEqual(err.message, 'Stream connection ended and running command aborted. It might have been processed.'); called = true; }); var bool = client.quit(function (err, res) { @@ -277,13 +277,12 @@ describe('connection tests', function () { text += data; return ''; }); - var end = helper.callFuncAfter(done, 2); client = redis.createClient({ retryStrategy: function (options) { if (options.totalRetryTime > 150) { client.set('foo', 'bar', function (err, res) { assert.strictEqual(err.message, 'Connection timeout'); - end(); + done(); }); // Pass a individual error message to the error handler return new Error('Connection timeout'); @@ -294,28 +293,23 @@ describe('connection tests', function () { retryMaxDelay: 123, port: 9999 }); - - client.on('error', function (err) { - unhookIntercept(); + process.nextTick(function () { assert.strictEqual( text, 'node_redis: WARNING: You activated the retry_strategy and max_attempts at the same time. This is not possible and max_attempts will be ignored.\n' + 'node_redis: WARNING: You activated the retry_strategy and retry_max_delay at the same time. This is not possible and retry_max_delay will be ignored.\n' ); - assert.strictEqual(err.message, 'Connection timeout'); - assert(!err.code); - end(); + unhookIntercept(); }); }); it('retry_strategy used to reconnect', function (done) { - var end = helper.callFuncAfter(done, 2); client = redis.createClient({ retry_strategy: function (options) { if (options.total_retry_time > 150) { client.set('foo', 'bar', function (err, res) { assert.strictEqual(err.code, 'ECONNREFUSED'); - end(); + done(); }); return false; } @@ -323,11 +317,6 @@ describe('connection tests', function () { }, port: 9999 }); - - client.on('error', function (err) { - assert.strictEqual(err.code, 'ECONNREFUSED'); - end(); - }); }); }); diff --git a/test/multi.spec.js b/test/multi.spec.js index ec2a81aafc1..48acf3f63af 100644 --- a/test/multi.spec.js +++ b/test/multi.spec.js @@ -180,7 +180,8 @@ describe("The 'multi' method", function () { client.multi([['set', 'foo', 'bar'], ['get', 'foo']]).exec(function (err, res) { assert(/Redis connection in broken state/.test(err.message)); - assert.strictEqual(err.errors.length, 0); + assert.strictEqual(err.errors.length, 2); + assert.strictEqual(err.errors[0].args.length, 2); }); }); diff --git a/test/node_redis.spec.js b/test/node_redis.spec.js index 774e57a46d1..548e70151e0 100644 --- a/test/node_redis.spec.js +++ b/test/node_redis.spec.js @@ -366,7 +366,7 @@ describe('The node_redis client', function () { client = redis.createClient(); client.quit(function () { client.get('foo', function (err, res) { - assert(err.message.indexOf('Redis connection gone') !== -1); + assert.strictEqual(err.message, 'Stream connection ended and running command aborted. It might have been processed.'); assert.strictEqual(client.offline_queue.length, 0); done(); }); @@ -1024,14 +1024,25 @@ describe('The node_redis client', function () { helper.killConnection(client); }); + var end = helper.callFuncAfter(done, 3); client.on('error', function (err) { - if (/uncertain state/.test(err.message)) { - assert.equal(client.command_queue.length, 0); - done(); + if (err.command === 'EXEC') { + assert.strictEqual(client.command_queue.length, 0); + assert.strictEqual(err.errors.length, 9); + assert.strictEqual(err.errors[1].command, 'SET'); + assert.deepEqual(err.errors[1].args, ['foo1', 'bar1']); + end(); + } else if (err.code === 'UNCERTAIN_STATE') { + assert.strictEqual(client.command_queue.length, 0); + assert.strictEqual(err.errors.length, 4); + assert.strictEqual(err.errors[0].command, 'SET'); + assert.deepEqual(err.errors[0].args, ['foo0', 'bar0']); + end(); } else { assert.equal(err.code, 'ECONNREFUSED'); assert.equal(err.errno, 'ECONNREFUSED'); assert.equal(err.syscall, 'connect'); + end(); } }); }); @@ -1101,14 +1112,21 @@ describe('The node_redis client', function () { helper.killConnection(client); }); + var end = helper.callFuncAfter(done, 3); client.on('error', function (err) { - if (err.code === 'UNCERTAIN_STATE') { + if (err.command === 'EXEC') { assert.equal(client.command_queue.length, 0); - done(); + assert.equal(err.errors.length, 9); + end(); + } else if (err.code === 'UNCERTAIN_STATE') { + assert.equal(client.command_queue.length, 0); + assert.equal(err.errors.length, 4); + end(); } else { assert.equal(err.code, 'ECONNREFUSED'); assert.equal(err.errno, 'ECONNREFUSED'); assert.equal(err.syscall, 'connect'); + end(); } }); }); From e58e31022568d38c1b5829e353be9a88d7386c68 Mon Sep 17 00:00:00 2001 From: Ruben Bridgewater Date: Thu, 14 Apr 2016 02:09:25 +0200 Subject: [PATCH 0635/1748] Remove unnecessary unallocation. This is done by the queue itself The total size is kept in the queue but this does not have to be reset each time --- index.js | 2 -- 1 file changed, 2 deletions(-) diff --git a/index.js b/index.js index 9161e358a47..99438d4b9b6 100644 --- a/index.js +++ b/index.js @@ -502,8 +502,6 @@ RedisClient.prototype.send_offline_queue = function () { this.internal_send_command(command_obj.command, command_obj.args, command_obj.callback, command_obj.call_on_write); } this.drain(); - // Even though items were shifted off, Queue backing store still uses memory until next add, so just get a new Queue - this.offline_queue = new Queue(); }; var retry_connection = function (self, error) { From 8308a3e6ae8bb3ac1c4f72ae37ab9cb5b8b11ab8 Mon Sep 17 00:00:00 2001 From: Ruben Bridgewater Date: Thu, 14 Apr 2016 02:10:58 +0200 Subject: [PATCH 0636/1748] Update dependencies --- package.json | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/package.json b/package.json index 48af96aac2a..2c4ea07519e 100644 --- a/package.json +++ b/package.json @@ -26,8 +26,8 @@ }, "dependencies": { "double-ended-queue": "^2.1.0-0", - "redis-commands": "^1.1.0", - "redis-parser": "^1.2.0" + "redis-commands": "^1.2.0", + "redis-parser": "^1.3.0" }, "engines": { "node": ">=0.10.0" From 5fac5958c3d41e0c9e18fb8811ffe5fd9df871af Mon Sep 17 00:00:00 2001 From: Ruben Bridgewater Date: Thu, 14 Apr 2016 02:11:13 +0200 Subject: [PATCH 0637/1748] Fix async test executed sync --- test/connection.spec.js | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/test/connection.spec.js b/test/connection.spec.js index 9b5634ab67b..aa86a7e9db2 100644 --- a/test/connection.spec.js +++ b/test/connection.spec.js @@ -356,7 +356,7 @@ describe('connection tests', function () { }); }); - it('use the system socket timeout if the connect_timeout has not been provided', function () { + it('use the system socket timeout if the connect_timeout has not been provided', function (done) { client = redis.createClient({ parser: parser, host: '2001:db8::ff00:42:8329' // auto detect ip v6 @@ -365,6 +365,7 @@ describe('connection tests', function () { assert.strictEqual(client.connection_options.family, 6); process.nextTick(function () { assert.strictEqual(client.stream.listeners('timeout').length, 0); + done(); }); }); From 0dc45bd0a3937551c013899b202250c6cd9edeff Mon Sep 17 00:00:00 2001 From: Ruben Bridgewater Date: Tue, 19 Apr 2016 00:13:09 +0200 Subject: [PATCH 0638/1748] Improve pub sub mode further --- index.js | 66 ++++++++++++++++++++--------------------- lib/command.js | 3 +- test/pubsub.spec.js | 71 ++++++++++++++++++++++++++++++--------------- 3 files changed, 80 insertions(+), 60 deletions(-) diff --git a/index.js b/index.js index 99438d4b9b6..9ee001e6100 100644 --- a/index.js +++ b/index.js @@ -148,6 +148,7 @@ function RedisClient (options, stream) { this.old_state = null; this.fire_strings = true; // Determine if strings or buffers should be written to the stream this.pipeline = false; + this.sub_commands_left = 0; this.times_connected = 0; this.options = options; this.buffers = options.return_buffers || options.detect_buffers; @@ -645,6 +646,11 @@ RedisClient.prototype.return_error = function (err) { } } + // Count down pub sub mode if in entering modus + if (this.pub_sub_mode > 1) { + this.pub_sub_mode--; + } + var match = err.message.match(utils.err_code); // LUA script could return user errors that don't behave like all other errors! if (match) { @@ -677,50 +683,39 @@ function normal_reply (self, reply) { } } -function set_subscribe (self, type, subscribe, channel) { - // Every channel has to be saved / removed one after the other and the type has to be the same too, - // to make sure partly subscribe / unsubscribe works well together - if (subscribe) { - self.subscription_set[type + '_' + channel] = channel; - } else { - type = type === 'unsubscribe' ? 'subscribe' : 'psubscribe'; // Make types consistent - delete self.subscription_set[type + '_' + channel]; - } -} - -function subscribe_unsubscribe (self, reply, type, subscribe) { +function subscribe_unsubscribe (self, reply, type) { // Subscribe commands take an optional callback and also emit an event, but only the _last_ response is included in the callback // The pub sub commands return each argument in a separate return value and have to be handled that way var command_obj = self.command_queue.get(0); var buffer = self.options.return_buffers || self.options.detect_buffers && command_obj.buffer_args; var channel = (buffer || reply[1] === null) ? reply[1] : reply[1].toString(); var count = +reply[2]; // Return the channel counter as number no matter if `string_numbers` is activated or not - debug('Subscribe / unsubscribe command'); + debug(type, channel); // Emit first, then return the callback if (channel !== null) { // Do not emit or "unsubscribe" something if there was no channel to unsubscribe from self.emit(type, channel, count); - set_subscribe(self, type, subscribe, channel); - } - if (command_obj.sub_commands_left <= 1) { - if (count !== 0) { - if (!subscribe && command_obj.args.length === 0) { // Unsubscribe from all channels - command_obj.sub_commands_left = count; - return; - } + if (type === 'subscribe' || type === 'psubscribe') { + self.subscription_set[type + '_' + channel] = channel; } else { + type = type === 'unsubscribe' ? 'subscribe' : 'psubscribe'; // Make types consistent + delete self.subscription_set[type + '_' + channel]; + } + } + + if (command_obj.args.length === 1 || self.sub_commands_left === 1 || command_obj.args.length === 0 && (count === 0 || channel === null)) { + if (count === 0) { // unsubscribed from all channels var running_command; var i = 1; + self.pub_sub_mode = 0; // Deactivating pub sub mode // This should be a rare case and therefore handling it this way should be good performance wise for the general case while (running_command = self.command_queue.get(i)) { if (SUBSCRIBE_COMMANDS[running_command.command]) { - self.command_queue.shift(); - self.pub_sub_mode = i; - return; + self.pub_sub_mode = i; // Entering pub sub mode again + break; } i++; } - self.pub_sub_mode = 0; } self.command_queue.shift(); if (typeof command_obj.callback === 'function') { @@ -728,8 +723,13 @@ function subscribe_unsubscribe (self, reply, type, subscribe) { // Evaluate to change this in v.3 to return all subscribed / unsubscribed channels in an array including the number of channels subscribed too command_obj.callback(null, channel); } + self.sub_commands_left = 0; } else { - command_obj.sub_commands_left--; + if (self.sub_commands_left !== 0) { + self.sub_commands_left--; + } else { + self.sub_commands_left = command_obj.args.length ? command_obj.args.length - 1 : count; + } } } @@ -751,12 +751,8 @@ function return_pub_sub (self, reply) { } else { self.emit('pmessage', reply[1], reply[2], reply[3]); } - } else if (type === 'subscribe' || type === 'psubscribe') { - subscribe_unsubscribe(self, reply, type, true); - } else if (type === 'unsubscribe' || type === 'punsubscribe') { - subscribe_unsubscribe(self, reply, type, false); } else { - normal_reply(self, reply); + subscribe_unsubscribe(self, reply, type); } } @@ -787,10 +783,12 @@ RedisClient.prototype.return_reply = function (reply) { } else if (this.pub_sub_mode !== 1) { this.pub_sub_mode--; normal_reply(this, reply); - } else if (reply instanceof Array && reply.length > 2 && reply[0]) { - return_pub_sub(this, reply); - } else { + } else if (!(reply instanceof Array) || reply.length <= 2) { + // Only PING and QUIT are allowed in this context besides the pub sub commands + // Ping replies with ['pong', null|value] and quit with 'OK' normal_reply(this, reply); + } else { + return_pub_sub(this, reply); } }; diff --git a/lib/command.js b/lib/command.js index ee1181ea7e3..e63d338b5e7 100644 --- a/lib/command.js +++ b/lib/command.js @@ -4,10 +4,9 @@ // a named constructor helps it show up meaningfully in the V8 CPU profiler and in heap snapshots. function Command (command, args, buffer_args, callback) { this.command = command; - this.args = args; // We only need the args for the offline commands => move them into another class. We need the number of args though for pub sub + this.args = args; this.buffer_args = buffer_args; this.callback = callback; - this.sub_commands_left = args.length; } function OfflineCommand (command, args, callback, call_on_write) { diff --git a/test/pubsub.spec.js b/test/pubsub.spec.js index f9401f63ad5..ffce23f442d 100644 --- a/test/pubsub.spec.js +++ b/test/pubsub.spec.js @@ -79,6 +79,7 @@ describe('publish/subscribe', function () { it('does not fire subscribe events after reconnecting', function (done) { var i = 0; + var end = helper.callFuncAfter(done, 2); sub.on('subscribe', function (chnl, count) { assert.strictEqual(typeof count, 'number'); assert.strictEqual(++i, count); @@ -91,9 +92,10 @@ describe('publish/subscribe', function () { sub.unsubscribe(function (err, res) { // Do not pass a channel here! assert.strictEqual(sub.pub_sub_mode, 2); assert.deepEqual(sub.subscription_set, {}); + end(); }); sub.set('foo', 'bar', helper.isString('OK')); - sub.subscribe(channel2, done); + sub.subscribe(channel2, end); }); }); @@ -181,25 +183,19 @@ describe('publish/subscribe', function () { sub.subscribe('chan9'); sub.unsubscribe('chan9'); pub.publish('chan8', 'something'); - sub.subscribe('chan9', function () { - return done(); - }); + sub.subscribe('chan9', done); }); it('handles SUB_UNSUB_MSG_SUB 2', function (done) { - sub.psubscribe('abc*'); + sub.psubscribe('abc*', helper.isString('abc*')); sub.subscribe('xyz'); sub.unsubscribe('xyz'); pub.publish('abcd', 'something'); - sub.subscribe('xyz', function () { - return done(); - }); + sub.subscribe('xyz', done); }); it('emits end event if quit is called from within subscribe', function (done) { - sub.on('end', function () { - return done(); - }); + sub.on('end', done); sub.on('subscribe', function (chnl, count) { sub.quit(); }); @@ -236,6 +232,10 @@ describe('publish/subscribe', function () { var end = helper.callFuncAfter(done, 2); sub.select(3); sub.set('foo', 'bar'); + sub.set('failure', helper.isError()); // Triggering a warning while subscribing should work + sub.mget('foo', 'bar', 'baz', 'hello', 'world', function (err, res) { + assert.deepEqual(res, ['bar', null, null, null, null]); + }); sub.subscribe('somechannel', 'another channel', function (err, res) { end(); sub.stream.destroy(); @@ -280,7 +280,7 @@ describe('publish/subscribe', function () { it('should only resubscribe to channels not unsubscribed earlier on a reconnect', function (done) { sub.subscribe('/foo', '/bar'); - sub.unsubscribe('/bar', function () { + sub.batch().unsubscribe(['/bar'], function () { pub.pubsub('channels', function (err, res) { assert.deepEqual(res, ['/foo']); sub.stream.destroy(); @@ -291,7 +291,7 @@ describe('publish/subscribe', function () { }); }); }); - }); + }).exec(); }); it('unsubscribes, subscribes, unsubscribes... single and multiple entries mixed. Withouth callbacks', function (done) { @@ -490,7 +490,7 @@ describe('publish/subscribe', function () { return_buffers: true }); sub2.on('ready', function () { - sub2.psubscribe('*'); + sub2.batch().psubscribe('*', helper.isString('*')).exec(); sub2.subscribe('/foo'); sub2.on('pmessage', function (pattern, channel, message) { assert.strictEqual(pattern.inspect(), new Buffer('*').inspect()); @@ -501,32 +501,58 @@ describe('publish/subscribe', function () { pub.pubsub('numsub', '/foo', function (err, res) { assert.deepEqual(res, ['/foo', 2]); }); + // sub2 is counted twice as it subscribed with psubscribe and subscribe pub.publish('/foo', 'hello world', helper.isNumber(3)); }); }); it('allows to listen to pmessageBuffer and pmessage', function (done) { var batch = sub.batch(); + var end = helper.callFuncAfter(done, 6); + assert.strictEqual(sub.message_buffers, false); batch.psubscribe('*'); batch.subscribe('/foo'); batch.unsubscribe('/foo'); - batch.unsubscribe(); - batch.subscribe(['/foo']); + batch.unsubscribe(helper.isNull()); + batch.subscribe(['/foo'], helper.isString('/foo')); batch.exec(); assert.strictEqual(sub.shouldBuffer, false); sub.on('pmessageBuffer', function (pattern, channel, message) { assert.strictEqual(pattern.inspect(), new Buffer('*').inspect()); assert.strictEqual(channel.inspect(), new Buffer('/foo').inspect()); - sub.quit(done); + sub.quit(end); }); + // Either message_buffers or buffers has to be true, but not both at the same time + assert.notStrictEqual(sub.message_buffers, sub.buffers); sub.on('pmessage', function (pattern, channel, message) { assert.strictEqual(pattern, '*'); assert.strictEqual(channel, '/foo'); + assert.strictEqual(message, 'hello world'); + end(); }); - pub.pubsub('numsub', '/foo', function (err, res) { - assert.deepEqual(res, ['/foo', 1]); + sub.on('message', function (channel, message) { + assert.strictEqual(channel, '/foo'); + assert.strictEqual(message, 'hello world'); + end(); }); - pub.publish('/foo', 'hello world', helper.isNumber(2)); + setTimeout(function () { + pub.pubsub('numsub', '/foo', function (err, res) { + // There's one subscriber to this channel + assert.deepEqual(res, ['/foo', 1]); + end(); + }); + pub.pubsub('channels', function (err, res) { + // There's exactly one channel that is listened too + assert.deepEqual(res, ['/foo']); + end(); + }); + pub.pubsub('numpat', function (err, res) { + // One pattern is active + assert.strictEqual(res, 1); + end(); + }); + pub.publish('/foo', 'hello world', helper.isNumber(2)); + }, 50); }); }); @@ -536,10 +562,7 @@ describe('publish/subscribe', function () { }); it('executes callback when punsubscribe is called and there are no subscriptions', function (done) { - pub.punsubscribe(function (err, results) { - assert.strictEqual(null, results); - done(err); - }); + pub.batch().punsubscribe(helper.isNull()).exec(done); }); }); From f500398cabe52213e8075a86ed297856e903c7dc Mon Sep 17 00:00:00 2001 From: Ruben Bridgewater Date: Tue, 19 Apr 2016 00:17:58 +0200 Subject: [PATCH 0639/1748] Run tests only with the js parser instead of hiredis and js parser from now on This removes the optional-dev-dependency as this is not needed from now on anymore --- package.json | 2 -- test/helper.js | 9 +++++---- 2 files changed, 5 insertions(+), 6 deletions(-) diff --git a/package.json b/package.json index 2c4ea07519e..d92511f6217 100644 --- a/package.json +++ b/package.json @@ -21,7 +21,6 @@ "coverage": "nyc report --reporter=html", "benchmark": "node benchmarks/multi_bench.js", "test": "nyc --cache mocha ./test/*.js ./test/commands/*.js --timeout=8000", - "pretest": "optional-dev-dependency hiredis", "posttest": "eslint . --fix" }, "dependencies": { @@ -40,7 +39,6 @@ "metrics": "^0.1.9", "mocha": "^2.3.2", "nyc": "^6.0.0", - "optional-dev-dependency": "^1.1.0", "tcp-port-used": "^0.1.2", "uuid": "^2.0.1", "win-spawn": "^2.0.0" diff --git a/test/helper.js b/test/helper.js index bb408a19fd1..e851b7f7b35 100644 --- a/test/helper.js +++ b/test/helper.js @@ -163,10 +163,11 @@ module.exports = { } var parsers = ['javascript']; var protocols = ['IPv4']; - try { - require('hiredis'); - parsers.push('hiredis'); - } catch (e) {/* ignore eslint */} + // The js parser works the same as the hiredis parser, just activate this if you want to be on the safe side + // try { + // require('hiredis'); + // parsers.push('hiredis'); + // } catch (e) {/* ignore eslint */} if (process.platform !== 'win32') { protocols.push('IPv6', '/tmp/redis.sock'); } From 625f14e6baf9dc6333496132b18ceb68e0f41d2c Mon Sep 17 00:00:00 2001 From: Ruben Bridgewater Date: Tue, 19 Apr 2016 18:46:56 +0200 Subject: [PATCH 0640/1748] Fix address always set to 127.0.0.1:6379 in case the host/port is set in the tls options --- index.js | 12 ++++++++---- test/tls.spec.js | 12 ++++++++---- 2 files changed, 16 insertions(+), 8 deletions(-) diff --git a/index.js b/index.js index 9ee001e6100..777383173ef 100644 --- a/index.js +++ b/index.js @@ -50,6 +50,14 @@ function RedisClient (options, stream) { EventEmitter.call(this); var cnx_options = {}; var self = this; + /* istanbul ignore next: travis does not work with stunnel atm. Therefore the tls tests are skipped on travis */ + for (var tls_option in options.tls) { // jshint ignore: line + cnx_options[tls_option] = options.tls[tls_option]; + // Copy the tls options into the general options to make sure the address is set right + if (tls_option === 'port' || tls_option === 'host' || tls_option === 'path' || tls_option === 'family') { + options[tls_option] = options.tls[tls_option]; + } + } if (stream) { // The stream from the outside is used so no connection from this side is triggered but from the server this client should talk to // Reconnect etc won't work with this. This requires monkey patching to work, so it is not officially supported @@ -64,10 +72,6 @@ function RedisClient (options, stream) { cnx_options.family = (!options.family && net.isIP(cnx_options.host)) || (options.family === 'IPv6' ? 6 : 4); this.address = cnx_options.host + ':' + cnx_options.port; } - /* istanbul ignore next: travis does not work with stunnel atm. Therefore the tls tests are skipped on travis */ - for (var tls_option in options.tls) { // jshint ignore: line - cnx_options[tls_option] = options.tls[tls_option]; - } // Warn on misusing deprecated functions if (typeof options.retry_strategy === 'function') { if ('max_attempts' in options) { diff --git a/test/tls.spec.js b/test/tls.spec.js index 40a424c27c2..d977ee7d9ac 100644 --- a/test/tls.spec.js +++ b/test/tls.spec.js @@ -60,6 +60,7 @@ describe('TLS connection tests', function () { tls: tls_options }); var time = 0; + assert.strictEqual(client.address, '127.0.0.1:' + tls_port); client.once('ready', function () { helper.killConnection(client); @@ -87,18 +88,20 @@ describe('TLS connection tests', function () { describe('when not connected', function () { - it('connect with host and port provided in the options object', function (done) { + it('connect with host and port provided in the tls object', function (done) { if (skip) this.skip(); + var tls = utils.clone(tls_options); + tls.port = tls_port; + tls.host = 'localhost'; client = redis.createClient({ - host: 'localhost', connect_timeout: 1000, - port: tls_port, - tls: tls_options + tls: tls }); // verify connection is using TCP, not UNIX socket assert.strictEqual(client.connection_options.host, 'localhost'); assert.strictEqual(client.connection_options.port, tls_port); + assert.strictEqual(client.address, 'localhost:' + tls_port); assert(client.stream.encrypted); client.set('foo', 'bar'); @@ -115,6 +118,7 @@ describe('TLS connection tests', function () { port: tls_port, tls: faulty_cert }); + assert.strictEqual(client.address, 'localhost:' + tls_port); client.on('error', function (err) { assert(/DEPTH_ZERO_SELF_SIGNED_CERT/.test(err.code || err.message), err); client.end(true); From f7c4d131be82c4e0b42c1bfebd187b67f53b9981 Mon Sep 17 00:00:00 2001 From: Ruben Bridgewater Date: Thu, 21 Apr 2016 02:49:06 +0200 Subject: [PATCH 0641/1748] Remove jshint comments and update istanbul comments --- index.js | 7 ++++--- lib/createClient.js | 2 +- lib/extendedApi.js | 2 +- lib/individualCommands.js | 5 ++--- lib/utils.js | 1 + 5 files changed, 9 insertions(+), 8 deletions(-) diff --git a/index.js b/index.js index 777383173ef..a94710a67aa 100644 --- a/index.js +++ b/index.js @@ -51,7 +51,7 @@ function RedisClient (options, stream) { var cnx_options = {}; var self = this; /* istanbul ignore next: travis does not work with stunnel atm. Therefore the tls tests are skipped on travis */ - for (var tls_option in options.tls) { // jshint ignore: line + for (var tls_option in options.tls) { cnx_options[tls_option] = options.tls[tls_option]; // Copy the tls options into the general options to make sure the address is set right if (tls_option === 'port' || tls_option === 'host' || tls_option === 'path' || tls_option === 'family') { @@ -102,7 +102,7 @@ function RedisClient (options, stream) { if (options.socket_keepalive === undefined) { options.socket_keepalive = true; } - for (var command in options.rename_commands) { // jshint ignore: line + for (var command in options.rename_commands) { options.rename_commands[command.toLowerCase()] = options.rename_commands[command]; } options.return_buffers = !!options.return_buffers; @@ -438,7 +438,7 @@ RedisClient.prototype.on_ready = function () { } }; debug('Sending pub/sub on_ready commands'); - for (var key in this.subscription_set) { // jshint ignore: line + for (var key in this.subscription_set) { var command = key.slice(0, key.indexOf('_')); var args = self.subscription_set[key]; self.internal_send_command(command, [args], callback); @@ -934,6 +934,7 @@ RedisClient.prototype.internal_send_command = function (command, args, callback, } // Handle `CLIENT REPLY ON|OFF|SKIP` // This has to be checked after call_on_write + /* istanbul ignore else: TODO: Remove this as soon as we test Redis 3.2 on travis */ if (this.reply === 'ON') { this.command_queue.push(command_obj); } else { diff --git a/lib/createClient.js b/lib/createClient.js index f97823e01d2..72d5e2745cb 100644 --- a/lib/createClient.js +++ b/lib/createClient.js @@ -44,7 +44,7 @@ module.exports = function createClient (port_arg, host_arg, options) { } if (parsed.search !== '') { var elem; - for (elem in parsed.query) { // jshint ignore: line + for (elem in parsed.query) { // If options are passed twice, only the parsed options will be used if (elem in options) { if (options[elem] === parsed.query[elem]) { diff --git a/lib/extendedApi.js b/lib/extendedApi.js index e512ae97e36..44dc9e28232 100644 --- a/lib/extendedApi.js +++ b/lib/extendedApi.js @@ -89,7 +89,7 @@ RedisClient.prototype.duplicate = function (options, callback) { } var existing_options = utils.clone(this.options); options = utils.clone(options); - for (var elem in options) { // jshint ignore: line + for (var elem in options) { existing_options[elem] = options[elem]; } var client = new RedisClient(existing_options); diff --git a/lib/individualCommands.js b/lib/individualCommands.js index de03e210f37..f1b1984f07b 100644 --- a/lib/individualCommands.js +++ b/lib/individualCommands.js @@ -147,7 +147,6 @@ function info_callback (self, callback) { } } obj.versions = []; - /* istanbul ignore else: some redis servers do not send the version */ if (obj.redis_version) { obj.redis_version.split('.').forEach(function (num) { obj.versions.push(+num); @@ -337,7 +336,7 @@ RedisClient.prototype.hmset = RedisClient.prototype.HMSET = function hmset () { } } else if (typeof arguments[1] === 'object' && (arguments.length === 2 || arguments.length === 3 && (typeof arguments[2] === 'function' || typeof arguments[2] === 'undefined'))) { arr = [arguments[0]]; - for (var field in arguments[1]) { // jshint ignore: line + for (var field in arguments[1]) { arr.push(field, arguments[1][field]); } callback = arguments[2]; @@ -376,7 +375,7 @@ Multi.prototype.hmset = Multi.prototype.HMSET = function hmset () { } } else if (typeof arguments[1] === 'object' && (arguments.length === 2 || arguments.length === 3 && (typeof arguments[2] === 'function' || typeof arguments[2] === 'undefined'))) { arr = [arguments[0]]; - for (var field in arguments[1]) { // jshint ignore: line + for (var field in arguments[1]) { arr.push(field, arguments[1][field]); } callback = arguments[2]; diff --git a/lib/utils.js b/lib/utils.js index cd04429b0c7..d3d9c2fa594 100644 --- a/lib/utils.js +++ b/lib/utils.js @@ -94,6 +94,7 @@ function replyInOrder (self, callback, err, res, queue) { // If the queue is explicitly passed, use that, otherwise fall back to the offline queue first, // as there might be commands in both queues at the same time var command_obj; + /* istanbul ignore if: TODO: Remove this as soon as we test Redis 3.2 on travis */ if (queue) { command_obj = queue.peekBack(); } else { From eae16938cdfb498fb6201eb0539cfa7cb7827a65 Mon Sep 17 00:00:00 2001 From: Ruben Bridgewater Date: Thu, 21 Apr 2016 02:54:42 +0200 Subject: [PATCH 0642/1748] Add monitor transaction warning / error --- lib/individualCommands.js | 11 +++------ lib/multi.js | 8 +++++++ test/multi.spec.js | 48 +++++++++++++++++++++++++++++++++++++++ 3 files changed, 59 insertions(+), 8 deletions(-) diff --git a/lib/individualCommands.js b/lib/individualCommands.js index f1b1984f07b..c9cef49b1b6 100644 --- a/lib/individualCommands.js +++ b/lib/individualCommands.js @@ -71,14 +71,9 @@ Multi.prototype.monitor = Multi.prototype.MONITOR = function monitor (callback) this.queue.push(['monitor', [], monitor_callback(this._client, callback)]); return this; } - var err = new Error( - 'You used the monitor command in combination with a transaction. Due to faulty return values of ' + - 'Redis in this context, the monitor command is now executed without transaction instead and ignored ' + - 'in the multi statement.' - ); - err.command = 'MONITOR'; - utils.reply_in_order(this._client, callback, err); - this._client.monitor('monitor', callback); + // Set multi monitoring to indicate the exec that it should abort + // Remove this "hack" as soon as Redis might fix this + this.monitoring = true; return this; }; diff --git a/lib/multi.js b/lib/multi.js index a4399473c98..2a7431e9b66 100644 --- a/lib/multi.js +++ b/lib/multi.js @@ -85,6 +85,14 @@ function multi_callback (self, err, replies) { } Multi.prototype.exec_transaction = function exec_transaction (callback) { + if (this.monitoring || this._client.monitoring) { + var err = new Error( + 'Using transaction with a client that is in monitor mode does not work due to faulty return values of Redis.' + ); + err.command = 'EXEC'; + err.code = 'EXECABORT'; + return utils.reply_in_order(this._client, callback, err); + } var self = this; var len = self.queue.length; self.errors = []; diff --git a/test/multi.spec.js b/test/multi.spec.js index 48acf3f63af..e49e3390daa 100644 --- a/test/multi.spec.js +++ b/test/multi.spec.js @@ -3,6 +3,7 @@ var assert = require('assert'); var config = require('./lib/config'); var helper = require('./helper'); +var utils = require('../lib/utils'); var redis = config.redis; var zlib = require('zlib'); var client; @@ -129,6 +130,53 @@ describe("The 'multi' method", function () { client = redis.createClient.apply(null, args); }); + describe('monitor and transactions do not work together', function () { + + it('results in a execabort', function (done) { + // Check that transactions in combination with monitor result in an error + client.monitor(function (e) { + client.on('error', function (err) { + assert.strictEqual(err.code, 'EXECABORT'); + done(); + }); + var multi = client.multi(); + multi.set('hello', 'world'); + multi.exec(); + }); + }); + + it('results in a execabort #2', function (done) { + // Check that using monitor with a transactions results in an error + client.multi().set('foo', 'bar').monitor().exec(function (err, res) { + assert.strictEqual(err.code, 'EXECABORT'); + done(); + }); + }); + + it('sanity check', function (done) { + // Remove the listener and add it back again after the error + var mochaListener = helper.removeMochaListener(); + process.on('uncaughtException', function (err) { + helper.removeMochaListener(); + process.on('uncaughtException', mochaListener); + done(); + }); + // Check if Redis still has the error + client.monitor(); + client.send_command('multi'); + client.send_command('set', ['foo', 'bar']); + client.send_command('get', ['foo']); + client.send_command('exec', function (err, res) { + // res[0] is going to be the monitor result of set + // res[1] is going to be the result of the set command + assert(utils.monitor_regex.test(res[0])); + assert.strictEqual(res[1], 'OK'); + assert.strictEqual(res.length, 2); + client.end(false); + }); + }); + }); + it('executes a pipelined multi properly in combination with the offline queue', function (done) { var multi1 = client.multi(); multi1.set('m1', '123'); From ce1678c77809c62d115c1907a00da15583ec5141 Mon Sep 17 00:00:00 2001 From: Ruben Bridgewater Date: Thu, 21 Apr 2016 02:56:06 +0200 Subject: [PATCH 0643/1748] Improve coverage; make tests ready for Redis 3.2 Add command sanity check --- test/auth.spec.js | 18 +++++++++--------- test/commands/client.spec.js | 8 ++++---- test/commands/hgetall.spec.js | 8 ++++---- test/commands/hmset.spec.js | 2 +- test/connection.spec.js | 18 ++++++++++++++++++ test/multi.spec.js | 2 +- test/node_redis.spec.js | 19 +++++++++++++++++++ test/pubsub.spec.js | 26 ++++++++++++++++++++++---- test/return_buffers.spec.js | 2 +- 9 files changed, 79 insertions(+), 24 deletions(-) diff --git a/test/auth.spec.js b/test/auth.spec.js index 38f46855609..8411a4b618d 100644 --- a/test/auth.spec.js +++ b/test/auth.spec.js @@ -272,13 +272,13 @@ describe('client authentication', function () { var args = config.configureClient(parser, ip, { password: auth }); - client = redis.createClient.apply(redis.createClient, args); + client = redis.createClient.apply(null, args); client.set('foo', 'bar'); client.subscribe('somechannel', 'another channel', function (err, res) { client.once('ready', function () { assert.strictEqual(client.pub_sub_mode, 1); client.get('foo', function (err, res) { - assert.strictEqual(err.message, 'ERR only (P)SUBSCRIBE / (P)UNSUBSCRIBE / QUIT allowed in this context'); + assert(/ERR only \(P\)SUBSCRIBE \/ \(P\)UNSUBSCRIBE/.test(err.message)); done(); }); }); @@ -292,7 +292,7 @@ describe('client authentication', function () { }); }); - it('indivdual commands work properly with batch', function (done) { + it('individual commands work properly with batch', function (done) { // quit => might return an error instead of "OK" in the exec callback... (if not connected) // auth => might return an error instead of "OK" in the exec callback... (if no password is required / still loading on Redis <= 2.4) // This could be fixed by checking the return value of the callback in the exec callback and @@ -302,7 +302,7 @@ describe('client authentication', function () { var args = config.configureClient(parser, 'localhost', { noReadyCheck: true }); - client = redis.createClient.apply(redis.createClient, args); + client = redis.createClient.apply(null, args); assert.strictEqual(client.selected_db, undefined); var end = helper.callFuncAfter(done, 8); client.on('monitor', function () { @@ -317,9 +317,9 @@ describe('client authentication', function () { }) .monitor() .set('foo', 'bar', helper.isString('OK')) - .INFO(function (err, res) { - assert.strictEqual(res.indexOf('# Server\r\nredis_version:'), 0); - assert.deepEqual(client.serverInfo.db5, { avg_ttl: 0, expires: 0, keys: 1 }); + .INFO('stats', function (err, res) { + assert.strictEqual(res.indexOf('# Stats\r\n'), 0); + assert.strictEqual(client.serverInfo.sync_full, '0'); }) .get('foo', helper.isString('bar')) .subscribe(['foo', 'bar']) @@ -328,8 +328,8 @@ describe('client authentication', function () { .psubscribe('*') .quit(helper.isString('OK')) // this might be interesting .exec(function (err, res) { - res[4] = res[4].substr(0, 10); - assert.deepEqual(res, ['OK', 'OK', 'OK', 'OK', '# Server\r\n', 'bar', 'bar', 'foo', '/foo', '*', 'OK']); + res[4] = res[4].substr(0, 9); + assert.deepEqual(res, ['OK', 'OK', 'OK', 'OK', '# Stats\r\n', 'bar', 'bar', 'foo', '/foo', '*', 'OK']); end(); }); }); diff --git a/test/commands/client.spec.js b/test/commands/client.spec.js index 935db8b6926..7ac32ae41ac 100644 --- a/test/commands/client.spec.js +++ b/test/commands/client.spec.js @@ -121,7 +121,7 @@ describe("The 'client' method", function () { var client2; beforeEach(function (done) { - client2 = redis.createClient.apply(redis.createClient, args); + client2 = redis.createClient.apply(null, args); client2.once('ready', function () { done(); }); @@ -136,9 +136,9 @@ describe("The 'client' method", function () { // per chunk. So the execution order is only garanteed on each client var end = helper.callFuncAfter(done, 2); - client.client('setname', 'RUTH', helper.isString('OK')); - client2.client('setname', 'RENEE', helper.isString('OK')); - client2.client('setname', 'MARTIN', helper.isString('OK')); + client.client('setname', 'RUTH'); + client2.client('setname', ['RENEE'], helper.isString('OK')); + client2.client(['setname', 'MARTIN'], helper.isString('OK')); client2.client('getname', function (err, res) { assert.equal(res, 'MARTIN'); end(); diff --git a/test/commands/hgetall.spec.js b/test/commands/hgetall.spec.js index 55a0e247803..8e8b88a309c 100644 --- a/test/commands/hgetall.spec.js +++ b/test/commands/hgetall.spec.js @@ -28,23 +28,23 @@ describe("The 'hgetall' method", function () { assert.strictEqual('1', obj.__proto__.toString()); // eslint-disable-line no-proto assert.strictEqual('23', obj.another.toString()); assert.strictEqual('1234', obj.home.toString()); - return done(err); + done(err); }); }); it('handles fetching keys set using an object', function (done) { - client.HMSET('msg_test', { message: 'hello' }, helper.isString('OK')); + client.batch().HMSET('msg_test', { message: 'hello' }, undefined).exec(); client.hgetall('msg_test', function (err, obj) { assert.strictEqual(1, Object.keys(obj).length); assert.strictEqual(obj.message, 'hello'); - return done(err); + done(err); }); }); it('handles fetching a messing key', function (done) { client.hgetall('missing', function (err, obj) { assert.strictEqual(null, obj); - return done(err); + done(err); }); }); }); diff --git a/test/commands/hmset.spec.js b/test/commands/hmset.spec.js index c96b7c5b670..93514cc2295 100644 --- a/test/commands/hmset.spec.js +++ b/test/commands/hmset.spec.js @@ -39,7 +39,7 @@ describe("The 'hmset' method", function () { }); it('handles object-style syntax and the key being a number', function (done) { - client.HMSET(231232, {'0123456789': 'abcdefghij', 'some manner of key': 'a type of value', 'otherTypes': 555}, helper.isString('OK')); + client.HMSET(231232, {'0123456789': 'abcdefghij', 'some manner of key': 'a type of value', 'otherTypes': 555}, undefined); client.HGETALL(231232, function (err, obj) { assert.equal(obj['0123456789'], 'abcdefghij'); assert.equal(obj['some manner of key'], 'a type of value'); diff --git a/test/connection.spec.js b/test/connection.spec.js index aa86a7e9db2..eb807295ed8 100644 --- a/test/connection.spec.js +++ b/test/connection.spec.js @@ -318,6 +318,24 @@ describe('connection tests', function () { port: 9999 }); }); + + it('retry_strategy used to reconnect with defaults', function (done) { + client = redis.createClient({ + retry_strategy: function (options) { + client.set('foo', 'bar'); + return null; + } + }); + setTimeout(function () { + client.stream.destroy(); + }, 50); + client.on('error', function (err) { + assert.strictEqual(err.code, 'NR_OFFLINE'); + assert.strictEqual(err.errors.length, 1); + assert.notStrictEqual(err.message, err.errors[0].message); + done(); + }); + }); }); describe('when not connected', function () { diff --git a/test/multi.spec.js b/test/multi.spec.js index e49e3390daa..8f500958fab 100644 --- a/test/multi.spec.js +++ b/test/multi.spec.js @@ -705,7 +705,7 @@ describe("The 'multi' method", function () { }); multi.get('foo', helper.isString('bar')); multi.exec(function (err, res) { - res[3] = res[3].substr(0, 10); + res[2] = res[2].substr(0, 10); assert.deepEqual(res, ['OK', 'OK', '# Server\r\n', 'bar']); done(); }); diff --git a/test/node_redis.spec.js b/test/node_redis.spec.js index 548e70151e0..c87968dea8a 100644 --- a/test/node_redis.spec.js +++ b/test/node_redis.spec.js @@ -1,6 +1,8 @@ 'use strict'; var assert = require('assert'); +var fs = require('fs'); +var path = require('path'); var config = require('./lib/config'); var helper = require('./helper'); var utils = require('../lib/utils'); @@ -9,6 +11,23 @@ var redis = config.redis; describe('The node_redis client', function () { + it('individual commands sanity check', function (done) { + // All commands should work the same in multi context or without + // Therefor individual commands always have to be handled in both cases + fs.readFile(path.resolve(__dirname, '../lib/individualCommands.js'), 'utf8', function (err, data) { + var client_prototype = data.match(/(\n| = )RedisClient\.prototype.[a-zA-Z_]+/g); + var multi_prototype = data.match(/(\n| = )Multi\.prototype\.[a-zA-Z_]+/g); + // Check that every entry RedisClient entry has a correspondend Multi entry + assert.strictEqual(client_prototype.filter(function (entry) { + return multi_prototype.indexOf(entry.replace('RedisClient', 'Multi')) === -1; + }).length, 4); // multi and batch are included too + assert.strictEqual(client_prototype.length, multi_prototype.length + 4); + // Check that all entries exist in uppercase and in lowercase variants + assert.strictEqual(data.match(/(\n| = )RedisClient\.prototype.[a-z_]+/g).length * 2, client_prototype.length); + done(); + }); + }); + helper.allTests(function (parser, ip, args) { describe('using ' + parser + ' and ' + ip, function () { diff --git a/test/pubsub.spec.js b/test/pubsub.spec.js index ffce23f442d..1d33b129941 100644 --- a/test/pubsub.spec.js +++ b/test/pubsub.spec.js @@ -19,8 +19,8 @@ describe('publish/subscribe', function () { beforeEach(function (done) { var end = helper.callFuncAfter(done, 2); - pub = redis.createClient.apply(redis.createClient, args); - sub = redis.createClient.apply(redis.createClient, args); + pub = redis.createClient.apply(null, args); + sub = redis.createClient.apply(null, args); pub.once('connect', function () { pub.flushdb(function () { end(); @@ -576,7 +576,7 @@ describe('publish/subscribe', function () { }); // Get is forbidden sub.get('foo', function (err, res) { - assert.strictEqual(err.message, 'ERR only (P)SUBSCRIBE / (P)UNSUBSCRIBE / QUIT allowed in this context'); + assert(/^ERR only \(P\)SUBSCRIBE \/ \(P\)UNSUBSCRIBE/.test(err.message)); assert.strictEqual(err.command, 'GET'); }); // Quit is allowed @@ -586,7 +586,7 @@ describe('publish/subscribe', function () { it('emit error if only pub sub commands are allowed without callback', function (done) { sub.subscribe('channel'); sub.on('error', function (err) { - assert.strictEqual(err.message, 'ERR only (P)SUBSCRIBE / (P)UNSUBSCRIBE / QUIT allowed in this context'); + assert(/^ERR only \(P\)SUBSCRIBE \/ \(P\)UNSUBSCRIBE/.test(err.message)); assert.strictEqual(err.command, 'GET'); done(); }); @@ -639,6 +639,24 @@ describe('publish/subscribe', function () { }); }); + it('arguments variants', function (done) { + sub.batch() + .info(['stats']) + .info() + .client('KILL', ['type', 'pubsub']) + .client('KILL', ['type', 'pubsub'], function () {}) + .unsubscribe() + .psubscribe(['pattern:*']) + .punsubscribe('unkown*') + .punsubscribe(['pattern:*']) + .exec(function (err, res) { + sub.client('kill', ['type', 'pubsub']); + sub.psubscribe('*'); + sub.punsubscribe('pa*'); + sub.punsubscribe(['a', '*'], done); + }); + }); + afterEach(function () { // Explicitly ignore still running commands pub.end(false); diff --git a/test/return_buffers.spec.js b/test/return_buffers.spec.js index 1274c8be9e8..eb18d79393f 100644 --- a/test/return_buffers.spec.js +++ b/test/return_buffers.spec.js @@ -252,7 +252,7 @@ describe('return_buffers', function () { var subConnected; pub = redis.createClient.apply(redis.createClient, basicArgs); - sub = redis.createClient.apply(redis.createClient, args); + sub = redis.createClient.apply(null, args); pub.once('connect', function () { pub.flushdb(function () { pubConnected = true; From 5368e7477efba42d6d1d95b815c2608200911d60 Mon Sep 17 00:00:00 2001 From: Ruben Bridgewater Date: Fri, 22 Apr 2016 02:29:06 +0200 Subject: [PATCH 0644/1748] Add changelog --- changelog.md | 37 +++++++++++++++++++++++++++++++++++++ 1 file changed, 37 insertions(+) diff --git a/changelog.md b/changelog.md index ae2ed508dd4..274986b393c 100644 --- a/changelog.md +++ b/changelog.md @@ -1,6 +1,43 @@ Changelog ========= +## v.2.6.0-2 - xx Apr, 2016 + +Features + +- Added support for the new `CLIENT REPLY ON|OFF|SKIP` command (Redis v.3.2) +- Added support for camelCase + - The Node.js landscape default is to use camelCase. node_redis is a bit out of the box here + but from now on it is possible to use both, just as you prefer! + - If there's any documented variable missing as camelCased, please open a issue for it +- Improve error handling significantly + - Only emit an error if the error has not already been handled in a callback + - Emit an error if a command would otherwise silently fail (no callback present) + - Improved unspecific error messages e.g. "Connection gone from end / close event" + - Added `args` to command errors to improve identification of the error + - Added origin to errors if there's e.g. a connection error + - Added ReplyError class. All Redis errors are from now on going to be of that class + - Added AbortError class. A subclass of AbortError. All unresolved and by node_redis rejected commands are from now on of that class + - Added AggregateError class. If a unresolved and by node_redis rejected command has no callback and + this applies to more than a single command, the errors for the commands without callback are aggregated + to a single error that is emitted in debug_mode in that case. +- Added `message_buffer` / `pmessage_buffer` events. That event is always going to emit a buffer + - Listening to the `message` event at the same time is always going to return the same message as string +- Added callback option to the duplicate function +- Added support for `__proto__` and other reserved keywords as hgetall field +- Updated [redis-commands](https://github.com/NodeRedis/redis-commands) dependency ([changelog](https://github.com/NodeRedis/redis-commands/releases/tag/v.1.2.0)) + +Bugfixes + +- Fixed v.2.5.0 auth command regression (under special circumstances a reconnect would not authenticate properly) +- Fixed v.2.6.0-0 pub sub mode and quit command regressions: + - Entering pub sub mode not working if a earlier called and still running command returned an error + - Unsubscribe callback not called if unsubscribing from all channels and resubscribing right away + - Quit command resulting in an error in some cases +- Fixed special handled functions in batch and multi context not working the same as without (e.g. select and info) + - Be aware that not all commands work in combination with transactions but they all work with batch +- Fixed address always set to 127.0.0.1:6379 in case host / port is set in the `tls` options instead of the general options + ## v.2.6.0-1 - 01 Apr, 2016 A second pre-release with further fixes. This is likely going to be released as 2.6.0 stable without further changes. From bf394923fd548419928ee9108aa87bc16a6e8d33 Mon Sep 17 00:00:00 2001 From: Ruben Bridgewater Date: Mon, 25 Apr 2016 01:35:56 +0200 Subject: [PATCH 0645/1748] Use built-in error classes to make errors more specific --- lib/createClient.js | 10 +++++----- lib/extendedApi.js | 6 +++--- lib/multi.js | 3 +-- 3 files changed, 9 insertions(+), 10 deletions(-) diff --git a/lib/createClient.js b/lib/createClient.js index 72d5e2745cb..a019fc7a383 100644 --- a/lib/createClient.js +++ b/lib/createClient.js @@ -12,7 +12,7 @@ module.exports = function createClient (port_arg, host_arg, options) { host = host_arg; } else { if (options && host_arg) { - throw new Error('Unknown type of connection in createClient()'); + throw new TypeError('Unknown type of connection in createClient()'); } options = options || host_arg; } @@ -50,14 +50,14 @@ module.exports = function createClient (port_arg, host_arg, options) { if (options[elem] === parsed.query[elem]) { console.warn('node_redis: WARNING: You passed the ' + elem + ' option twice!'); } else { - throw new Error('The ' + elem + ' option is added twice and does not match'); + throw new RangeError('The ' + elem + ' option is added twice and does not match'); } } options[elem] = parsed.query[elem]; } } } else if (parsed.hostname) { - throw new Error('The redis url must begin with slashes "//" or contain slashes after the redis protocol'); + throw new RangeError('The redis url must begin with slashes "//" or contain slashes after the redis protocol'); } else { options.path = port_arg; } @@ -67,12 +67,12 @@ module.exports = function createClient (port_arg, host_arg, options) { options.host = options.host || host_arg; if (port_arg && arguments.length !== 1) { - throw new Error('To many arguments passed to createClient. Please only pass the options object'); + throw new TypeError('To many arguments passed to createClient. Please only pass the options object'); } } if (!options) { - throw new Error('Unknown type of connection in createClient()'); + throw new TypeError('Unknown type of connection in createClient()'); } return options; diff --git a/lib/extendedApi.js b/lib/extendedApi.js index 44dc9e28232..ef6a2faa6ea 100644 --- a/lib/extendedApi.js +++ b/lib/extendedApi.js @@ -13,7 +13,7 @@ All documented and exposed API belongs in here RedisClient.prototype.send_command = RedisClient.prototype.sendCommand = function (command, args, callback) { // Throw to fail early instead of relying in order in this case if (typeof command !== 'string') { - throw new Error('Wrong input type "' + (command !== null && command !== undefined ? command.constructor.name : command) + '" for command name'); + throw new TypeError('Wrong input type "' + (command !== null && command !== undefined ? command.constructor.name : command) + '" for command name'); } if (!Array.isArray(args)) { if (args === undefined || args === null) { @@ -22,11 +22,11 @@ RedisClient.prototype.send_command = RedisClient.prototype.sendCommand = functio callback = args; args = []; } else { - throw new Error('Wrong input type "' + args.constructor.name + '" for args'); + throw new TypeError('Wrong input type "' + args.constructor.name + '" for args'); } } if (typeof callback !== 'function' && callback !== undefined) { - throw new Error('Wrong input type "' + (callback !== null ? callback.constructor.name : 'null') + '" for callback function'); + throw new TypeError('Wrong input type "' + (callback !== null ? callback.constructor.name : 'null') + '" for callback function'); } // Using the raw multi command is only possible with this function diff --git a/lib/multi.js b/lib/multi.js index 2a7431e9b66..433c45bd501 100644 --- a/lib/multi.js +++ b/lib/multi.js @@ -46,7 +46,6 @@ function multi_callback (self, err, replies) { if (err) { err.errors = self.errors; - err.command = 'EXEC'; if (self.callback) { self.callback(err); // Exclude connection errors so that those errors won't be emitted twice @@ -86,7 +85,7 @@ function multi_callback (self, err, replies) { Multi.prototype.exec_transaction = function exec_transaction (callback) { if (this.monitoring || this._client.monitoring) { - var err = new Error( + var err = new RangeError( 'Using transaction with a client that is in monitor mode does not work due to faulty return values of Redis.' ); err.command = 'EXEC'; From 03f1a606f7337dd6b4ce9b16e2f96c039266c0f4 Mon Sep 17 00:00:00 2001 From: Ruben Bridgewater Date: Mon, 25 Apr 2016 01:36:49 +0200 Subject: [PATCH 0646/1748] Improve error handling Added individual error classes Don't silently fail for commands without callback from now on General polishing (e.g. better error messages) Fix typos --- README.md | 48 +++++++++- index.js | 164 +++++++++++++++++++++------------- lib/customError.js | 16 ---- lib/customErrors.js | 78 ++++++++++++++++ lib/extendedApi.js | 8 +- lib/individualCommands.js | 4 +- test/batch.spec.js | 2 +- test/commands/dbsize.spec.js | 2 +- test/commands/flushdb.spec.js | 2 +- test/commands/get.spec.js | 4 +- test/commands/getset.spec.js | 2 +- test/commands/hgetall.spec.js | 6 +- test/commands/mset.spec.js | 5 +- test/commands/select.spec.js | 2 +- test/commands/set.spec.js | 2 +- test/connection.spec.js | 36 +++++--- test/multi.spec.js | 4 +- test/node_redis.spec.js | 111 +++++++++++++++++++---- 18 files changed, 367 insertions(+), 129 deletions(-) delete mode 100644 lib/customError.js create mode 100644 lib/customErrors.js diff --git a/README.md b/README.md index 3a4abf9eb96..afe1c3ac15c 100644 --- a/README.md +++ b/README.md @@ -137,6 +137,7 @@ are passed an object containing `delay` (in ms) and `attempt` (the attempt #) at ### "error" `client` will emit `error` when encountering an error connecting to the Redis server or when any other in node_redis occurs. +If you use a command without callback and encounter a ReplyError it is going to be emitted to the error listener. So please attach the error listener to node_redis. @@ -293,6 +294,51 @@ client.get("foo_rand000000000000", function (err, reply) { `client.end()` without the flush parameter set to true should NOT be used in production! +## Error handling (>= v.2.6) + +All redis errors are returned as `ReplyError`. +All unresolved commands that get rejected due to what ever reason return a `AbortError`. +As subclass of the `AbortError` a `AggregateError` exists. This is emitted in case multiple unresolved commands without callback got rejected in debug_mode. +They are all aggregated and a single error is emitted in that case. + +Example: +```js +var redis = require('./'); +var assert = require('assert'); +var client = redis.createClient(); + +client.on('error', function (err) { + assert(err instanceof Error); + assert(err instanceof redis.AbortError); + assert(err instanceof redis.AggregateError); + assert.strictEqual(err.errors.length, 2); // The set and get got aggregated in here + assert.strictEqual(err.code, 'NR_CLOSED'); +}); +client.set('foo', 123, 'bar', function (err, res) { // To many arguments + assert(err instanceof redis.ReplyError); // => true + assert.strictEqual(err.command, 'SET'); + assert.deepStrictEqual(err.args, ['foo', 123, 'bar']); + + redis.debug_mode = true; + client.set('foo', 'bar'); + client.get('foo'); + process.nextTick(function () { + client.end(true); // Force closing the connection while the command did not yet return + redis.debug_mode = false; + }); +}); + +``` + +Every `ReplyError` contains the `command` name in all-caps and the arguments (`args`). + +If node_redis emits a library error because of another error, the triggering error is added to the returned error as `origin` attribute. + +___Error codes___ + +node_redis returns a `NR_CLOSED` error code if the clients connection dropped. If a command unresolved command got rejected a `UNERCTAIN_STATE` code is returned. +A `CONNECTION_BROKEN` error code is used in case node_redis gives up to reconnect. + ## client.unref() Call `unref()` on the underlying socket connection to the Redis server, allowing the program to exit once no more commands are pending. @@ -537,7 +583,7 @@ Redis. The interface in `node_redis` is to return an individual `Batch` object b The only difference between .batch and .multi is that no transaction is going to be used. Be aware that the errors are - just like in multi statements - in the result. Otherwise both, errors and results could be returned at the same time. -If you fire many commands at once this is going to **boost the execution speed by up to 400%** [sic!] compared to fireing the same commands in a loop without waiting for the result! See the benchmarks for further comparison. Please remember that all commands are kept in memory until they are fired. +If you fire many commands at once this is going to boost the execution speed significantly compared to fireing the same commands in a loop without waiting for the result! See the benchmarks for further comparison. Please remember that all commands are kept in memory until they are fired. ## Monitor mode diff --git a/index.js b/index.js index a94710a67aa..a4957bd2198 100644 --- a/index.js +++ b/index.js @@ -5,7 +5,7 @@ var tls = require('tls'); var util = require('util'); var utils = require('./lib/utils'); var Queue = require('double-ended-queue'); -var CommandError = require('./lib/customError'); +var errorClasses = require('./lib/customErrors'); var Command = require('./lib/command').Command; var OfflineCommand = require('./lib/command').OfflineCommand; var EventEmitter = require('events'); @@ -189,13 +189,24 @@ function create_parser (self) { self.return_reply(data); }, returnError: function (err) { - self.return_error(err); + // Return a ReplyError to indicate Redis returned an error + self.return_error(new errorClasses.ReplyError(err)); }, returnFatalError: function (err) { // Error out all fired commands. Otherwise they might rely on faulty data. We have to reconnect to get in a working state again - self.flush_and_error(err, ['command_queue']); - self.stream.destroy(); - self.return_error(err); + // Note: the execution order is important. First flush and emit, then create the stream + err = new errorClasses.ReplyError(err); + err.message += '. Please report this.'; + self.ready = false; + self.flush_and_error({ + message: 'Fatal error encountert. Command aborted.', + code: 'NR_FATAL' + }, { + error: err, + queues: ['command_queue'] + }); + self.emit('error', err); + self.create_stream(); }, returnBuffers: self.buffers || self.message_buffers, name: self.options.parser, @@ -240,7 +251,7 @@ RedisClient.prototype.create_stream = function () { this.stream.setTimeout(this.connect_timeout, function () { // Note: This is only tested if a internet connection is established self.retry_totaltime = self.connect_timeout; - self.connection_gone('timeout', new Error('Redis connection gone from timeout event')); + self.connection_gone('timeout'); }); } @@ -270,11 +281,11 @@ RedisClient.prototype.create_stream = function () { }); this.stream.once('close', function (hadError) { - self.connection_gone('close', hadError ? new Error('Stream connection closed with a transmission error') : null); + self.connection_gone('close'); }); this.stream.once('end', function () { - self.connection_gone('end', null); + self.connection_gone('end'); }); this.stream.on('drain', function () { @@ -325,30 +336,46 @@ RedisClient.prototype.warn = function (msg) { }; // Flush provided queues, erroring any items with a callback first -RedisClient.prototype.flush_and_error = function (error, queue_names) { - var callbacks_not_called = []; - queue_names = queue_names || ['offline_queue', 'command_queue']; +RedisClient.prototype.flush_and_error = function (error_attributes, options) { + options = options || {}; + var aggregated_errors = []; + var queue_names = options.queues || ['command_queue', 'offline_queue']; // Flush the command_queue first to keep the order intakt for (var i = 0; i < queue_names.length; i++) { + // If the command was fired it might have been processed so far + if (queue_names[i] === 'command_queue') { + error_attributes.message += ' It might have been processed.'; + } else { // As the command_queue is flushed first, remove this for the offline queue + error_attributes.message = error_attributes.message.replace(' It might have been processed.', ''); + } + // Don't flush everything from the queue for (var command_obj = this[queue_names[i]].shift(); command_obj; command_obj = this[queue_names[i]].shift()) { - var err = new CommandError(error); + var err = new errorClasses.AbortError(error_attributes); err.command = command_obj.command.toUpperCase(); - if (command_obj.args.length) { + if (command_obj.args && command_obj.args.length) { err.args = command_obj.args; } + if (options.error) { + err.origin = options.error; + } if (typeof command_obj.callback === 'function') { command_obj.callback(err); } else { - callbacks_not_called.push(err); + aggregated_errors.push(err); } } - this[queue_names[i]] = new Queue(); } - // Mutate the original error that will be emitted - // This is fine, as we don't manipulate any user errors - if (callbacks_not_called.length !== 0) { - error.errors = callbacks_not_called; + // Currently this would be a breaking change, therefore it's only emitted in debug_mode + if (exports.debug_mode && aggregated_errors.length) { + var error; + if (aggregated_errors.length === 1) { + error = aggregated_errors[0]; + } else { + error_attributes.message = error_attributes.message.replace('It', 'They').replace(/command/i, '$&s'); + error = new errorClasses.AggregateError(error_attributes); + error.errors = aggregated_errors; + } + this.emit('error', error); } - return callbacks_not_called.length === 0; }; RedisClient.prototype.on_error = function (err) { @@ -538,6 +565,7 @@ RedisClient.prototype.connection_gone = function (why, error) { if (this.retry_timer) { return; } + error = error || null; debug('Redis connection is gone from ' + why + ' event.'); this.connected = false; @@ -564,9 +592,12 @@ RedisClient.prototype.connection_gone = function (why, error) { // If this is a requested shutdown, then don't retry if (this.closing) { debug('Connection ended by quit / end command, not retrying.'); - error = new Error('Stream connection ended and running command aborted. It might have been processed.'); - error.code = 'NR_OFFLINE'; - this.flush_and_error(error); + this.flush_and_error({ + message: 'Stream connection ended and command aborted.', + code: 'NR_CLOSED' + }, { + error: error + }); return; } @@ -586,31 +617,39 @@ RedisClient.prototype.connection_gone = function (why, error) { if (typeof this.retry_delay !== 'number') { // Pass individual error through if (this.retry_delay instanceof Error) { - error = new CommandError(this.retry_delay); - } - // Attention: there might be the case where there's no error! - if (!error) { - error = new Error('Stream connection ended and running command aborted. It might have been processed.'); - error.code = 'NR_OFFLINE'; - } - // Only emit an error in case that a running command had no callback - if (!this.flush_and_error(error)) { - error.message = 'Stream connection ended and all running commands aborted. They might have been processed.'; - this.emit('error', error); + error = this.retry_delay; } + this.flush_and_error({ + message: 'Stream connection ended and command aborted.', + code: 'NR_CLOSED' + }, { + error: error + }); this.end(false); return; } } if (this.max_attempts !== 0 && this.attempts >= this.max_attempts || this.retry_totaltime >= this.connect_timeout) { - var message = this.retry_totaltime >= this.connect_timeout ? - 'connection timeout exceeded.' : - 'maximum connection attempts exceeded.'; - error = new Error('Redis connection in broken state: ' + message); - error.code = 'CONNECTION_BROKEN'; - this.flush_and_error(error); - this.emit('error', error); + var message = 'Redis connection in broken state: '; + if (this.retry_totaltime >= this.connect_timeout) { + message += 'connection timeout exceeded.'; + } else { + message += 'maximum connection attempts exceeded.'; + } + + this.flush_and_error({ + message: message, + code: 'CONNECTION_BROKEN', + }, { + error: error + }); + var err = new Error(message); + err.code = 'CONNECTION_BROKEN'; + if (error) { + err.origin = error; + } + this.emit('error', err); this.end(false); return; } @@ -620,13 +659,13 @@ RedisClient.prototype.connection_gone = function (why, error) { this.offline_queue.unshift.apply(this.offline_queue, this.command_queue.toArray()); this.command_queue.clear(); } else if (this.command_queue.length !== 0) { - error = new Error('Redis connection lost and command aborted in uncertain state. It might have been processed.'); - error.code = 'UNCERTAIN_STATE'; - if (!this.flush_and_error(error, ['command_queue'])) { - // Only emit if not all commands had a callback that already handled the error - error.message = 'Redis connection lost and commands aborted in uncertain state. They might have been processed.'; - this.emit('error', error); - } + this.flush_and_error({ + message: 'Redis connection lost and command aborted.', + code: 'UNCERTAIN_STATE' + }, { + error: error, + queues: ['command_queue'] + }); } if (this.retry_max_delay !== null && this.retry_delay > this.retry_max_delay) { @@ -643,11 +682,9 @@ RedisClient.prototype.connection_gone = function (why, error) { RedisClient.prototype.return_error = function (err) { var command_obj = this.command_queue.shift(); - if (command_obj && command_obj.command && command_obj.command.toUpperCase) { - err.command = command_obj.command.toUpperCase(); - if (command_obj.args.length) { - err.args = command_obj.args; - } + err.command = command_obj.command.toUpperCase(); + if (command_obj.args && command_obj.args.length) { + err.args = command_obj.args; } // Count down pub sub mode if in entering modus @@ -661,7 +698,7 @@ RedisClient.prototype.return_error = function (err) { err.code = match[1]; } - utils.callback_or_emit(this, command_obj && command_obj.callback, err); + utils.callback_or_emit(this, command_obj.callback, err); }; RedisClient.prototype.drain = function () { @@ -809,14 +846,16 @@ function handle_offline_command (self, command_obj) { msg = 'Stream not writeable.'; } } else { - msg = 'The connection has already been closed.'; + msg = 'The connection is already closed.'; } - err = new Error(command + " can't be processed. " + msg); - err.command = command; - if (command_obj.args.length) { + err = new errorClasses.AbortError({ + message: command + " can't be processed. " + msg, + code: 'NR_CLOSED', + command: command + }); + if (command_obj.args && command_obj.args.length) { err.args = command_obj.args; } - err.code = 'NR_OFFLINE'; utils.reply_in_order(self, callback, err); } else { debug('Queueing ' + command + ' for next server connection.'); @@ -889,8 +928,8 @@ RedisClient.prototype.internal_send_command = function (command, args, callback, args_copy[i] = '' + args[i]; } } - args = null; - command_obj = new Command(command, args_copy, buffer_args, callback); + // Pass the original args to make sure in error cases the original arguments are returned + command_obj = new Command(command, args, buffer_args, callback); if (this.options.prefix) { prefix_keys = commands.getKeyIndexes(command, args_copy); @@ -1053,6 +1092,9 @@ exports.createClient = function () { exports.RedisClient = RedisClient; exports.print = utils.print; exports.Multi = require('./lib/multi'); +exports.AbortError = errorClasses.AbortError; +exports.ReplyError = errorClasses.ReplyError; +exports.AggregateError = errorClasses.AggregateError; // Add all redis commands / node_redis api to the client require('./lib/individualCommands'); diff --git a/lib/customError.js b/lib/customError.js deleted file mode 100644 index 07101b58063..00000000000 --- a/lib/customError.js +++ /dev/null @@ -1,16 +0,0 @@ -'use strict'; - -var util = require('util'); - -function CommandError (error) { - Error.captureStackTrace(this, this.constructor); - this.name = this.constructor.name; - this.message = error.message; - for (var keys = Object.keys(error), key = keys.pop(); key; key = keys.pop()) { - this[key] = error[key]; - } -} - -util.inherits(CommandError, Error); - -module.exports = CommandError; diff --git a/lib/customErrors.js b/lib/customErrors.js new file mode 100644 index 00000000000..7c6e457036a --- /dev/null +++ b/lib/customErrors.js @@ -0,0 +1,78 @@ +'use strict'; + +var util = require('util'); + +function AbortError (obj) { + Error.captureStackTrace(this, this.constructor); + var message; + Object.defineProperty(this, 'name', { + get: function () { + return this.constructor.name; + } + }); + Object.defineProperty(this, 'message', { + get: function () { + return message; + }, + set: function (msg) { + message = msg; + } + }); + for (var keys = Object.keys(obj), key = keys.pop(); key; key = keys.pop()) { + this[key] = obj[key]; + } + // Explicitly add the message + // If the obj is a error itself, the message is not enumerable + this.message = obj.message; +} + +function ReplyError (obj) { + Error.captureStackTrace(this, this.constructor); + var tmp; + Object.defineProperty(this, 'name', { + get: function () { + return this.constructor.name; + } + }); + Object.defineProperty(this, 'message', { + get: function () { + return tmp; + }, + set: function (msg) { + tmp = msg; + } + }); + this.message = obj.message; +} + +function AggregateError (obj) { + Error.captureStackTrace(this, this.constructor); + var tmp; + Object.defineProperty(this, 'name', { + get: function () { + return this.constructor.name; + } + }); + Object.defineProperty(this, 'message', { + get: function () { + return tmp; + }, + set: function (msg) { + tmp = msg; + } + }); + for (var keys = Object.keys(obj), key = keys.pop(); key; key = keys.pop()) { + this[key] = obj[key]; + } + this.message = obj.message; +} + +util.inherits(ReplyError, Error); +util.inherits(AbortError, Error); +util.inherits(AggregateError, AbortError); + +module.exports = { + ReplyError: ReplyError, + AbortError: AbortError, + AggregateError: AggregateError +}; diff --git a/lib/extendedApi.js b/lib/extendedApi.js index ef6a2faa6ea..ece77d22fe2 100644 --- a/lib/extendedApi.js +++ b/lib/extendedApi.js @@ -47,10 +47,10 @@ RedisClient.prototype.send_command = RedisClient.prototype.sendCommand = functio RedisClient.prototype.end = function (flush) { // Flush queue if wanted if (flush) { - var err = new Error("The command can't be processed. The connection has already been closed."); - err.code = 'NR_OFFLINE'; - this.flush_and_error(err); - // TODO: Emit an error in case a command did not have a callback + this.flush_and_error({ + message: 'Connection forcefully ended and command aborted.', + code: 'NR_CLOSED' + }); } else if (arguments.length === 0) { this.warn( 'Using .end() without the flush parameter is deprecated and throws from v.3.0.0 on.\n' + diff --git a/lib/individualCommands.js b/lib/individualCommands.js index c9cef49b1b6..808ad99a0b0 100644 --- a/lib/individualCommands.js +++ b/lib/individualCommands.js @@ -79,7 +79,7 @@ Multi.prototype.monitor = Multi.prototype.MONITOR = function monitor (callback) function quit_callback (self, callback) { return function (err, res) { - if (err && err.code === 'NR_OFFLINE') { + if (err && err.code === 'NR_CLOSED') { // Pretent the quit command worked properly in this case. // Either the quit landed in the offline queue and was flushed at the reconnect // or the offline queue is deactivated and the command was rejected right away @@ -90,7 +90,7 @@ function quit_callback (self, callback) { } utils.callback_or_emit(self, callback, err, res); if (self.stream.writable) { - // If the socket is still alive, kill it. This could happen if quit got a NR_OFFLINE error code + // If the socket is still alive, kill it. This could happen if quit got a NR_CLOSED error code self.stream.destroy(); } }; diff --git a/test/batch.spec.js b/test/batch.spec.js index 7b382eee421..762b2f70491 100644 --- a/test/batch.spec.js +++ b/test/batch.spec.js @@ -36,7 +36,7 @@ describe("The 'batch' method", function () { batch.set('foo', 'bar'); batch.exec(function (err, res) { assert.strictEqual(err, null); - assert.strictEqual(res[0].code, 'NR_OFFLINE'); + assert.strictEqual(res[0].code, 'NR_CLOSED'); done(); }); }); diff --git a/test/commands/dbsize.spec.js b/test/commands/dbsize.spec.js index b1a3d3c32e9..4fdf80010d1 100644 --- a/test/commands/dbsize.spec.js +++ b/test/commands/dbsize.spec.js @@ -31,7 +31,7 @@ describe("The 'dbsize' method", function () { it('reports an error', function (done) { client.dbsize([], function (err, res) { - assert(err.message.match(/The connection has already been closed/)); + assert(err.message.match(/The connection is already closed/)); done(); }); }); diff --git a/test/commands/flushdb.spec.js b/test/commands/flushdb.spec.js index 90811053499..61535e2e714 100644 --- a/test/commands/flushdb.spec.js +++ b/test/commands/flushdb.spec.js @@ -31,7 +31,7 @@ describe("The 'flushdb' method", function () { it('reports an error', function (done) { client.flushdb(function (err, res) { - assert(err.message.match(/The connection has already been closed/)); + assert(err.message.match(/The connection is already closed/)); done(); }); }); diff --git a/test/commands/get.spec.js b/test/commands/get.spec.js index 83a330ad12d..e2b9a7db071 100644 --- a/test/commands/get.spec.js +++ b/test/commands/get.spec.js @@ -31,14 +31,14 @@ describe("The 'get' method", function () { it('reports an error', function (done) { client.get(key, function (err, res) { - assert(err.message.match(/The connection has already been closed/)); + assert(err.message.match(/The connection is already closed/)); done(); }); }); it('reports an error promisified', function () { return client.getAsync(key).then(assert, function (err) { - assert(err.message.match(/The connection has already been closed/)); + assert(err.message.match(/The connection is already closed/)); }); }); }); diff --git a/test/commands/getset.spec.js b/test/commands/getset.spec.js index ea6b5d5ef76..e5da8573119 100644 --- a/test/commands/getset.spec.js +++ b/test/commands/getset.spec.js @@ -32,7 +32,7 @@ describe("The 'getset' method", function () { it('reports an error', function (done) { client.get(key, function (err, res) { - assert(err.message.match(/The connection has already been closed/)); + assert(err.message.match(/The connection is already closed/)); done(); }); }); diff --git a/test/commands/hgetall.spec.js b/test/commands/hgetall.spec.js index 8e8b88a309c..ba443265100 100644 --- a/test/commands/hgetall.spec.js +++ b/test/commands/hgetall.spec.js @@ -24,8 +24,10 @@ describe("The 'hgetall' method", function () { it('handles simple keys and values', function (done) { client.hmset(['hosts', '__proto__', '1', 'another', '23', 'home', '1234'], helper.isString('OK')); client.HGETALL(['hosts'], function (err, obj) { - assert.strictEqual(3, Object.keys(obj).length); - assert.strictEqual('1', obj.__proto__.toString()); // eslint-disable-line no-proto + if (!/^v0\.10/.test(process.version)) { + assert.strictEqual(3, Object.keys(obj).length); + assert.strictEqual('1', obj.__proto__.toString()); // eslint-disable-line no-proto + } assert.strictEqual('23', obj.another.toString()); assert.strictEqual('1234', obj.home.toString()); done(err); diff --git a/test/commands/mset.spec.js b/test/commands/mset.spec.js index c6963641a2d..9fac90728cf 100644 --- a/test/commands/mset.spec.js +++ b/test/commands/mset.spec.js @@ -33,7 +33,7 @@ describe("The 'mset' method", function () { it('reports an error', function (done) { client.mset(key, value, key2, value2, function (err, res) { - assert(err.message.match(/The connection has already been closed/)); + assert(err.message.match(/The connection is already closed/)); done(); }); }); @@ -96,7 +96,8 @@ describe("The 'mset' method", function () { // this behavior is different from the 'set' behavior. it('emits an error', function (done) { client.on('error', function (err) { - assert.equal(err.message, "ERR wrong number of arguments for 'mset' command"); + assert.strictEqual(err.message, "ERR wrong number of arguments for 'mset' command"); + assert.strictEqual(err.name, 'ReplyError'); done(); }); diff --git a/test/commands/select.spec.js b/test/commands/select.spec.js index d8878fe48c3..4297dca7f34 100644 --- a/test/commands/select.spec.js +++ b/test/commands/select.spec.js @@ -23,7 +23,7 @@ describe("The 'select' method", function () { it('returns an error if redis is not connected', function (done) { var buffering = client.select(1, function (err, res) { - assert(err.message.match(/The connection has already been closed/)); + assert(err.message.match(/The connection is already closed/)); done(); }); assert(typeof buffering === 'boolean'); diff --git a/test/commands/set.spec.js b/test/commands/set.spec.js index 7cb4b8314a9..01b62443818 100644 --- a/test/commands/set.spec.js +++ b/test/commands/set.spec.js @@ -31,7 +31,7 @@ describe("The 'set' method", function () { it('reports an error', function (done) { client.set(key, value, function (err, res) { - assert(err.message.match(/The connection has already been closed/)); + assert(err.message.match(/The connection is already closed/)); done(); }); }); diff --git a/test/connection.spec.js b/test/connection.spec.js index eb807295ed8..9661f42d3bd 100644 --- a/test/connection.spec.js +++ b/test/connection.spec.js @@ -53,7 +53,7 @@ describe('connection tests', function () { } }); client.set('foo', 'bar', function (err, res) { - assert.strictEqual(err.message, 'Stream connection ended and running command aborted. It might have been processed.'); + assert.strictEqual(err.message, 'Stream connection ended and command aborted.'); called = -1; }); }); @@ -62,7 +62,7 @@ describe('connection tests', function () { var called = false; client = redis.createClient(9999); client.set('foo', 'bar', function (err, res) { - assert.strictEqual(err.message, 'Stream connection ended and running command aborted. It might have been processed.'); + assert.strictEqual(err.message, 'Stream connection ended and command aborted.'); called = true; }); var bool = client.quit(function (err, res) { @@ -259,13 +259,12 @@ describe('connection tests', function () { it('emits error once if reconnecting after command has been executed but not yet returned without callback', function (done) { client = redis.createClient.apply(null, args); - client.on('error', function (err) { - assert.strictEqual(err.code, 'UNCERTAIN_STATE'); - done(); - }); client.on('ready', function () { - client.set('foo', 'bar'); + client.set('foo', 'bar', function (err) { + assert.strictEqual(err.code, 'UNCERTAIN_STATE'); + done(); + }); // Abort connection before the value returned client.stream.destroy(); }); @@ -281,7 +280,8 @@ describe('connection tests', function () { retryStrategy: function (options) { if (options.totalRetryTime > 150) { client.set('foo', 'bar', function (err, res) { - assert.strictEqual(err.message, 'Connection timeout'); + assert.strictEqual(err.message, 'Stream connection ended and command aborted.'); + assert.strictEqual(err.origin.message, 'Connection timeout'); done(); }); // Pass a individual error message to the error handler @@ -308,7 +308,9 @@ describe('connection tests', function () { retry_strategy: function (options) { if (options.total_retry_time > 150) { client.set('foo', 'bar', function (err, res) { - assert.strictEqual(err.code, 'ECONNREFUSED'); + assert.strictEqual(err.message, 'Stream connection ended and command aborted.'); + assert.strictEqual(err.code, 'NR_CLOSED'); + assert.strictEqual(err.origin.code, 'ECONNREFUSED'); done(); }); return false; @@ -319,10 +321,15 @@ describe('connection tests', function () { }); }); - it('retry_strategy used to reconnect with defaults', function (done) { + it('retryStrategy used to reconnect with defaults', function (done) { + var unhookIntercept = intercept(function () { + return ''; + }); + redis.debugMode = true; client = redis.createClient({ - retry_strategy: function (options) { + retryStrategy: function (options) { client.set('foo', 'bar'); + assert(redis.debugMode); return null; } }); @@ -330,9 +337,10 @@ describe('connection tests', function () { client.stream.destroy(); }, 50); client.on('error', function (err) { - assert.strictEqual(err.code, 'NR_OFFLINE'); - assert.strictEqual(err.errors.length, 1); - assert.notStrictEqual(err.message, err.errors[0].message); + assert.strictEqual(err.code, 'NR_CLOSED'); + assert.strictEqual(err.message, 'Stream connection ended and command aborted.'); + unhookIntercept(); + redis.debugMode = false; done(); }); }); diff --git a/test/multi.spec.js b/test/multi.spec.js index 8f500958fab..8deae7f920b 100644 --- a/test/multi.spec.js +++ b/test/multi.spec.js @@ -111,7 +111,7 @@ describe("The 'multi' method", function () { it('reports an error', function (done) { var multi = client.multi(); var notBuffering = multi.exec(function (err, res) { - assert(err.message.match(/The connection has already been closed/)); + assert(err.message.match(/The connection is already closed/)); done(); }); assert.strictEqual(notBuffering, false); @@ -119,7 +119,7 @@ describe("The 'multi' method", function () { it('reports an error if promisified', function () { return client.multi().execAsync().catch(function (err) { - assert(err.message.match(/The connection has already been closed/)); + assert(err.message.match(/The connection is already closed/)); }); }); }); diff --git a/test/node_redis.spec.js b/test/node_redis.spec.js index c87968dea8a..8ba91fdfe47 100644 --- a/test/node_redis.spec.js +++ b/test/node_redis.spec.js @@ -3,6 +3,7 @@ var assert = require('assert'); var fs = require('fs'); var path = require('path'); +var intercept = require('intercept-stdout'); var config = require('./lib/config'); var helper = require('./helper'); var utils = require('../lib/utils'); @@ -58,6 +59,12 @@ describe('The node_redis client', function () { assert.strictEqual(client2.options[elem], client.options[elem]); } } + client2.on('error', function (err) { + assert.strictEqual(err.message, 'Connection forcefully ended and command aborted. It might have been processed.'); + assert.strictEqual(err.command, 'SELECT'); + assert(err instanceof Error); + assert.strictEqual(err.name, 'AbortError'); + }); client2.on('ready', function () { client2.end(true); done(); @@ -193,10 +200,10 @@ describe('The node_redis client', function () { }); }); - it('using multi with send_command should work as individual command instead of using the internal multi', function (done) { + it('using multi with sendCommand should work as individual command instead of using the internal multi', function (done) { // This is necessary to keep backwards compatibility and it is the only way to handle multis as you want in node_redis - client.send_command('multi'); - client.send_command('set', ['foo', 'bar'], helper.isString('QUEUED')); + client.sendCommand('multi'); + client.sendCommand('set', ['foo', 'bar'], helper.isString('QUEUED')); client.get('foo'); client.exec(function (err, res) { // exec is not manipulated if not fired by the individual multi command // As the multi command is handled individually by the user he also has to handle the return value @@ -340,7 +347,8 @@ describe('The node_redis client', function () { } }, 20); var cb = function (err, res) { - assert(/The connection has already been closed/.test(err.message)); + assert(/Connection forcefully ended|The connection is already closed./.test(err.message)); + assert.strictEqual(err.code, 'NR_CLOSED'); end(); }; for (var i = 0; i < 20; i++) { @@ -361,7 +369,7 @@ describe('The node_redis client', function () { done(); }, 20); var cb = function (err, res) { - assert(/The connection has already been closed./.test(err.message)); + assert(/Connection forcefully ended|The connection is already closed./.test(err.message)); end(); }; for (var i = 0; i < 20; i++) { @@ -373,6 +381,59 @@ describe('The node_redis client', function () { } }); + it('emits an aggregate error if no callback was present for multiple commands in debug_mode', function (done) { + redis.debug_mode = true; + var unhookIntercept = intercept(function (data) { + return ''; // Don't print the debug messages + }); + client.set('foo', 'bar'); + client.set('baz', 'hello world'); + client.on('error', function (err) { + assert(err instanceof Error); + assert(err instanceof redis.AbortError); + assert(err instanceof redis.AggregateError); + assert.strictEqual(err.name, 'AggregateError'); + assert.strictEqual(err.errors.length, 2); + assert.strictEqual(err.message, 'Connection forcefully ended and commands aborted.'); + assert.strictEqual(err.code, 'NR_CLOSED'); + assert.strictEqual(err.errors[0].message, 'Connection forcefully ended and command aborted. It might have been processed.'); + assert.strictEqual(err.errors[0].command, 'SET'); + assert.strictEqual(err.errors[0].code, 'NR_CLOSED'); + assert.deepEqual(err.errors[0].args, ['foo', 'bar']); + done(); + }); + client.end(true); + unhookIntercept(); + redis.debug_mode = false; + }); + + it('emits an abort error if no callback was present for a single commands', function (done) { + redis.debug_mode = true; + var unhookIntercept = intercept(function (data) { + return ''; // Don't print the debug messages + }); + client.set('foo', 'bar'); + client.on('error', function (err) { + assert(err instanceof Error); + assert(err instanceof redis.AbortError); + assert(!(err instanceof redis.AggregateError)); + assert.strictEqual(err.message, 'Connection forcefully ended and command aborted. It might have been processed.'); + assert.strictEqual(err.command, 'SET'); + assert.strictEqual(err.code, 'NR_CLOSED'); + assert.deepEqual(err.args, ['foo', 'bar']); + done(); + }); + client.end(true); + unhookIntercept(); + redis.debug_mode = false; + }); + + it('does not emit abort errors if no callback was present while not being in debug_mode ', function (done) { + client.set('foo', 'bar'); + client.end(true); + setTimeout(done, 100); + }); + }); describe('commands after using .quit should fail', function () { @@ -385,7 +446,7 @@ describe('The node_redis client', function () { client = redis.createClient(); client.quit(function () { client.get('foo', function (err, res) { - assert.strictEqual(err.message, 'Stream connection ended and running command aborted. It might have been processed.'); + assert.strictEqual(err.message, 'Stream connection ended and command aborted. It might have been processed.'); assert.strictEqual(client.offline_queue.length, 0); done(); }); @@ -398,7 +459,7 @@ describe('The node_redis client', function () { client.quit(); setTimeout(function () { client.get('foo', function (err, res) { - assert.strictEqual(err.message, 'GET can\'t be processed. The connection has already been closed.'); + assert.strictEqual(err.message, 'GET can\'t be processed. The connection is already closed.'); assert.strictEqual(err.command, 'GET'); assert.strictEqual(client.offline_queue.length, 0); done(); @@ -410,7 +471,7 @@ describe('The node_redis client', function () { if (helper.redisProcess().spawnFailed()) this.skip(); client.quit(); client.on('error', function (err) { - assert.strictEqual(err.message, 'SET can\'t be processed. The connection has already been closed.'); + assert.strictEqual(err.message, 'SET can\'t be processed. The connection is already closed.'); assert.strictEqual(err.command, 'SET'); assert.strictEqual(client.offline_queue_length, 0); done(); @@ -542,7 +603,7 @@ describe('The node_redis client', function () { }); domain.on('error', function (err) { - assert.strictEqual(err.message, 'SET can\'t be processed. The connection has already been closed.'); + assert.strictEqual(err.message, 'SET can\'t be processed. The connection is already closed.'); domain.exit(); done(); }); @@ -919,8 +980,11 @@ describe('The node_redis client', function () { it('should gracefully recover and only fail on the already send commands', function (done) { client = redis.createClient.apply(null, args); + var error; client.on('error', function (err) { - assert.strictEqual(err.message, 'Protocol error, got "a" as reply type byte'); + assert.strictEqual(err.message, 'Protocol error, got "a" as reply type byte. Please report this.'); + assert.strictEqual(err, error); + assert(err instanceof redis.ReplyError); // After the hard failure work properly again. The set should have been processed properly too client.get('foo', function (err, res) { assert.strictEqual(res, 'bar'); @@ -929,7 +993,10 @@ describe('The node_redis client', function () { }); client.once('ready', function () { client.set('foo', 'bar', function (err, res) { - assert.strictEqual(err.message, 'Protocol error, got "a" as reply type byte'); + assert.strictEqual(err.message, 'Fatal error encountert. Command aborted. It might have been processed.'); + assert.strictEqual(err.code, 'NR_FATAL'); + assert(err instanceof redis.AbortError); + error = err.origin; }); // Fail the set answer. Has no corresponding command obj and will therefore land in the error handler and set client.reply_parser.execute(new Buffer('a*1\r*1\r$1`zasd\r\na')); @@ -974,7 +1041,7 @@ describe('The node_redis client', function () { setTimeout(function () { client.set('foo', 'bar', function (err, result) { if (!finished) done(err); - assert.strictEqual(err.message, "The command can't be processed. The connection has already been closed."); + assert.strictEqual(err.message, 'Connection forcefully ended and command aborted.'); }); setTimeout(function () { @@ -993,10 +1060,15 @@ describe('The node_redis client', function () { var i = 0; client.on('error', function (err) { - if (err.message === 'Redis connection in broken state: maximum connection attempts exceeded.') { + if (err.code === 'CONNECTION_BROKEN') { assert(i, 3); assert.strictEqual(client.offline_queue.length, 0); - done(); + assert.strictEqual(err.origin.code, 'ECONNREFUSED'); + if (!(err instanceof redis.AbortError)) { + done(); + } else { + assert.strictEqual(err.command, 'SET'); + } } else { assert.equal(err.code, 'ECONNREFUSED'); assert.equal(err.errno, 'ECONNREFUSED'); @@ -1111,10 +1183,13 @@ describe('The node_redis client', function () { it('flushes the command queue if connection is lost', function (done) { client = redis.createClient({ parser: parser, - max_attempts: 2, enable_offline_queue: false }); + redis.debug_mode = true; + var unhookIntercept = intercept(function () { + return ''; + }); client.once('ready', function () { var multi = client.multi(); multi.config('bar'); @@ -1133,18 +1208,20 @@ describe('The node_redis client', function () { var end = helper.callFuncAfter(done, 3); client.on('error', function (err) { + assert.equal(client.command_queue.length, 0); if (err.command === 'EXEC') { - assert.equal(client.command_queue.length, 0); assert.equal(err.errors.length, 9); end(); } else if (err.code === 'UNCERTAIN_STATE') { - assert.equal(client.command_queue.length, 0); assert.equal(err.errors.length, 4); end(); } else { assert.equal(err.code, 'ECONNREFUSED'); assert.equal(err.errno, 'ECONNREFUSED'); assert.equal(err.syscall, 'connect'); + redis.debug_mode = false; + client.end(true); + unhookIntercept(); end(); } }); From 6fdf629595852dd5a790f75cdb32fa9b6d979466 Mon Sep 17 00:00:00 2001 From: Ruben Bridgewater Date: Fri, 29 Apr 2016 04:42:09 +0200 Subject: [PATCH 0647/1748] v.2.6.0-2 --- package.json | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/package.json b/package.json index d92511f6217..15ef0ba86cf 100644 --- a/package.json +++ b/package.json @@ -1,6 +1,6 @@ { "name": "redis", - "version": "2.6.0-1", + "version": "2.6.0-2", "description": "Redis client library", "keywords": [ "database", From a1755b91fb7ace72f79e3a42aeaf79374cd6023c Mon Sep 17 00:00:00 2001 From: Ruben Bridgewater Date: Fri, 29 Apr 2016 04:43:56 +0200 Subject: [PATCH 0648/1748] Add release date --- changelog.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/changelog.md b/changelog.md index 274986b393c..168a1854c39 100644 --- a/changelog.md +++ b/changelog.md @@ -1,7 +1,7 @@ Changelog ========= -## v.2.6.0-2 - xx Apr, 2016 +## v.2.6.0-2 - 29 Apr, 2016 Features From bd633d7306564687824f76ca90b4586cb190e623 Mon Sep 17 00:00:00 2001 From: Kevin Leung Date: Wed, 4 May 2016 15:18:26 +0800 Subject: [PATCH 0649/1748] Do you mean snake_case instead of snack_case? --- README.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/README.md b/README.md index afe1c3ac15c..6539fc1170c 100644 --- a/README.md +++ b/README.md @@ -49,7 +49,7 @@ This will display: mjr:~/work/node_redis (master)$ Note that the API is entirely asynchronous. To get data back from the server, you'll need to use a callback. -From v.2.6 on the API supports camelCase and snack_case and all options / variables / events etc. can be used either way. +From v.2.6 on the API supports camelCase and snake_case and all options / variables / events etc. can be used either way. It is recommended to use camelCase as this is the default for the Node.js landscape. ### Promises From 3e03f1961b27e83ef5b9bb717d455c7fea1fa5f8 Mon Sep 17 00:00:00 2001 From: Alejandro Oviedo Date: Tue, 17 May 2016 10:01:07 -0300 Subject: [PATCH 0650/1748] link build badge to master branch --- README.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/README.md b/README.md index 6539fc1170c..101421ae046 100644 --- a/README.md +++ b/README.md @@ -1,7 +1,7 @@ redis - a node.js redis client =========================== -[![Build Status](https://travis-ci.org/NodeRedis/node_redis.png)](https://travis-ci.org/NodeRedis/node_redis) +[![Build Status](https://travis-ci.org/NodeRedis/node_redis.svg?branch=master)](https://travis-ci.org/NodeRedis/node_redis) [![Coverage Status](https://coveralls.io/repos/NodeRedis/node_redis/badge.svg?branch=)](https://coveralls.io/r/NodeRedis/node_redis?branch=) [![Windows Tests](https://img.shields.io/appveyor/ci/BridgeAR/node-redis/master.svg?label=Windows%20Tests)](https://ci.appveyor.com/project/BridgeAR/node-redis/branch/master) [![Gitter](https://badges.gitter.im/Join%20Chat.svg)](https://gitter.im/NodeRedis/node_redis?utm_source=badge&utm_medium=badge&utm_campaign=pr-badge) From 27eac2f594f58a3acf579f0bbfb2ea276312c9a9 Mon Sep 17 00:00:00 2001 From: Ruben Bridgewater Date: Fri, 29 Apr 2016 13:07:40 +0200 Subject: [PATCH 0651/1748] Prevent a race condition by just forcefully ending the connection in the test --- test/multi.spec.js | 2 ++ 1 file changed, 2 insertions(+) diff --git a/test/multi.spec.js b/test/multi.spec.js index 8deae7f920b..90107593789 100644 --- a/test/multi.spec.js +++ b/test/multi.spec.js @@ -137,6 +137,7 @@ describe("The 'multi' method", function () { client.monitor(function (e) { client.on('error', function (err) { assert.strictEqual(err.code, 'EXECABORT'); + client.end(false); done(); }); var multi = client.multi(); @@ -149,6 +150,7 @@ describe("The 'multi' method", function () { // Check that using monitor with a transactions results in an error client.multi().set('foo', 'bar').monitor().exec(function (err, res) { assert.strictEqual(err.code, 'EXECABORT'); + client.end(false); done(); }); }); From 84abf5c4c24d3a306b09293e3a5ae98a3b09df9d Mon Sep 17 00:00:00 2001 From: Ruben Bridgewater Date: Wed, 25 May 2016 20:35:13 +0300 Subject: [PATCH 0652/1748] Fix benchmark not using individual options --- benchmarks/multi_bench.js | 12 ++++++------ 1 file changed, 6 insertions(+), 6 deletions(-) diff --git a/benchmarks/multi_bench.js b/benchmarks/multi_bench.js index d93fce4439e..c65876b9035 100644 --- a/benchmarks/multi_bench.js +++ b/benchmarks/multi_bench.js @@ -231,8 +231,8 @@ tests.push(new Test({descr: 'SET 4B buf', command: 'set', args: ['foo_rand000000 tests.push(new Test({descr: 'GET 4B str', command: 'get', args: ['foo_rand000000000000']})); tests.push(new Test({descr: 'GET 4B str', command: 'get', args: ['foo_rand000000000000'], batch: 50})); -tests.push(new Test({descr: 'GET 4B buf', command: 'get', args: ['foo_rand000000000000'], client_opts: { return_buffers: true} })); -tests.push(new Test({descr: 'GET 4B buf', command: 'get', args: ['foo_rand000000000000'], batch: 50, client_opts: { return_buffers: true} })); +tests.push(new Test({descr: 'GET 4B buf', command: 'get', args: ['foo_rand000000000000'], client_options: { return_buffers: true} })); +tests.push(new Test({descr: 'GET 4B buf', command: 'get', args: ['foo_rand000000000000'], batch: 50, client_options: { return_buffers: true} })); tests.push(new Test({descr: 'SET 4KiB str', command: 'set', args: ['foo_rand000000000001', large_str]})); tests.push(new Test({descr: 'SET 4KiB str', command: 'set', args: ['foo_rand000000000001', large_str], batch: 50})); @@ -243,8 +243,8 @@ tests.push(new Test({descr: 'SET 4KiB buf', command: 'set', args: ['foo_rand0000 tests.push(new Test({descr: 'GET 4KiB str', command: 'get', args: ['foo_rand000000000001']})); tests.push(new Test({descr: 'GET 4KiB str', command: 'get', args: ['foo_rand000000000001'], batch: 50})); -tests.push(new Test({descr: 'GET 4KiB buf', command: 'get', args: ['foo_rand000000000001'], client_opts: { return_buffers: true} })); -tests.push(new Test({descr: 'GET 4KiB buf', command: 'get', args: ['foo_rand000000000001'], batch: 50, client_opts: { return_buffers: true} })); +tests.push(new Test({descr: 'GET 4KiB buf', command: 'get', args: ['foo_rand000000000001'], client_options: { return_buffers: true} })); +tests.push(new Test({descr: 'GET 4KiB buf', command: 'get', args: ['foo_rand000000000001'], batch: 50, client_options: { return_buffers: true} })); tests.push(new Test({descr: 'INCR', command: 'incr', args: ['counter_rand000000000000']})); tests.push(new Test({descr: 'INCR', command: 'incr', args: ['counter_rand000000000000'], batch: 50})); @@ -267,8 +267,8 @@ tests.push(new Test({descr: 'SET 4MiB buf', command: 'set', args: ['foo_rand0000 tests.push(new Test({descr: 'GET 4MiB str', command: 'get', args: ['foo_rand000000000002']})); tests.push(new Test({descr: 'GET 4MiB str', command: 'get', args: ['foo_rand000000000002'], batch: 20})); -tests.push(new Test({descr: 'GET 4MiB buf', command: 'get', args: ['foo_rand000000000002'], client_opts: { return_buffers: true} })); -tests.push(new Test({descr: 'GET 4MiB buf', command: 'get', args: ['foo_rand000000000002'], batch: 20, client_opts: { return_buffers: true} })); +tests.push(new Test({descr: 'GET 4MiB buf', command: 'get', args: ['foo_rand000000000002'], client_options: { return_buffers: true} })); +tests.push(new Test({descr: 'GET 4MiB buf', command: 'get', args: ['foo_rand000000000002'], batch: 20, client_options: { return_buffers: true} })); function next () { var test = tests.shift(); From fe00bf271df4504a328bc592423396bf153c2f34 Mon Sep 17 00:00:00 2001 From: Ruben Bridgewater Date: Wed, 25 May 2016 22:47:09 +0300 Subject: [PATCH 0653/1748] Update redis-parser to v.2.0.0 Update all code accordingly --- README.md | 87 +++++++++++++++++-------------------- benchmarks/multi_bench.js | 2 +- changelog.md | 14 ++++++ index.js | 15 +++---- lib/customErrors.js | 57 +++++------------------- package.json | 5 ++- test/custom_errors.spec.js | 89 ++++++++++++++++++++++++++++++++++++++ test/node_redis.spec.js | 8 +++- 8 files changed, 171 insertions(+), 106 deletions(-) create mode 100644 test/custom_errors.spec.js diff --git a/README.md b/README.md index afe1c3ac15c..f1680c65414 100644 --- a/README.md +++ b/README.md @@ -182,8 +182,8 @@ __Tip:__ If the Redis server runs on the same machine as the client consider usi | port | 6379 | Port of the Redis server | | path | null | The UNIX socket string of the Redis server | | url | null | The URL of the Redis server. Format: `[redis:]//[[user][:password@]][host][:port][/db-number][?db=db-number[&password=bar[&option=value]]]` (More info avaliable at [IANA](http://www.iana.org/assignments/uri-schemes/prov/redis)). | -| parser | hiredis | If hiredis is not installed, automatic fallback to the built-in javascript parser | -| string_numbers | null | Set to `true`, `node_redis` will return Redis number values as Strings instead of javascript Numbers. Useful if you need to handle big numbers (above `Number.MAX_SAFE_INTEGER === 2^53`). Hiredis is incapable of this behavior, so setting this option to `true` will result in the built-in javascript parser being used no matter the value of the `parser` option. | +| parser | javascript | __Deprecated__ Use either the built-in JS parser [`javascript`]() or the native [`hiredis`]() parser. __Note__ `node_redis` < 2.6 uses hiredis as default if installed. This changed in v.2.6.0. | +| string_numbers | null | Set to `true`, `node_redis` will return Redis number values as Strings instead of javascript Numbers. Useful if you need to handle big numbers (above `Number.MAX_SAFE_INTEGER === 2^53`). Hiredis is incapable of this behavior, so setting this option to `true` will result in the built-in javascript parser being used no matter the value of the `parser` option. | | return_buffers | false | If set to `true`, then all replies will be sent to callbacks as Buffers instead of Strings. | | detect_buffers | false | If set to `true`, then replies will be sent to callbacks as Buffers. This option lets you switch between Buffers and Strings on a per-command basis, whereas `return_buffers` applies to every command on a client. __Note__: This doesn't work properly with the pubsub mode. A subscriber has to either always return Strings or Buffers. | | socket_keepalive | true | If set to `true`, the keep-alive functionality is enabled on the underlying socket. | @@ -712,56 +712,47 @@ client.zadd(args, function (err, response) { ## Performance Much effort has been spent to make `node_redis` as fast as possible for common -operations. As pipelining happens naturally from shared connections, overall -efficiency goes up. - -Here are results of `multi_bench.js` which is similar to `redis-benchmark` from the Redis distribution. - -hiredis parser (Lenovo T450s i7-5600U): +operations. ``` -Client count: 1, node version: 4.2.2, server version: 3.0.3, parser: hiredis - PING, 1/1 min/max/avg: 0/ 2/ 0.02/ 2501ms total, 47503.80 ops/sec - PING, batch 50/1 min/max/avg: 0/ 2/ 0.09/ 2501ms total, 529668.13 ops/sec - SET 4B str, 1/1 min/max/avg: 0/ 2/ 0.02/ 2501ms total, 41900.04 ops/sec - SET 4B str, batch 50/1 min/max/avg: 0/ 2/ 0.14/ 2501ms total, 354658.14 ops/sec - SET 4B buf, 1/1 min/max/avg: 0/ 4/ 0.04/ 2501ms total, 23499.00 ops/sec - SET 4B buf, batch 50/1 min/max/avg: 0/ 2/ 0.31/ 2501ms total, 159836.07 ops/sec - GET 4B str, 1/1 min/max/avg: 0/ 4/ 0.02/ 2501ms total, 43489.80 ops/sec - GET 4B str, batch 50/1 min/max/avg: 0/ 2/ 0.11/ 2501ms total, 444202.32 ops/sec - GET 4B buf, 1/1 min/max/avg: 0/ 3/ 0.02/ 2501ms total, 38561.38 ops/sec - GET 4B buf, batch 50/1 min/max/avg: 0/ 2/ 0.11/ 2501ms total, 452139.14 ops/sec - SET 4KiB str, 1/1 min/max/avg: 0/ 2/ 0.03/ 2501ms total, 32990.80 ops/sec - SET 4KiB str, batch 50/1 min/max/avg: 0/ 2/ 0.34/ 2501ms total, 146161.54 ops/sec - SET 4KiB buf, 1/1 min/max/avg: 0/ 1/ 0.04/ 2501ms total, 23294.28 ops/sec - SET 4KiB buf, batch 50/1 min/max/avg: 0/ 2/ 0.36/ 2501ms total, 137584.97 ops/sec - GET 4KiB str, 1/1 min/max/avg: 0/ 2/ 0.03/ 2501ms total, 36350.66 ops/sec - GET 4KiB str, batch 50/1 min/max/avg: 0/ 2/ 0.32/ 2501ms total, 155157.94 ops/sec - GET 4KiB buf, 1/1 min/max/avg: 0/ 4/ 0.02/ 2501ms total, 39776.49 ops/sec - GET 4KiB buf, batch 50/1 min/max/avg: 0/ 2/ 0.32/ 2501ms total, 155457.82 ops/sec - INCR, 1/1 min/max/avg: 0/ 3/ 0.02/ 2501ms total, 43972.41 ops/sec - INCR, batch 50/1 min/max/avg: 0/ 1/ 0.12/ 2501ms total, 425809.68 ops/sec - LPUSH, 1/1 min/max/avg: 0/ 2/ 0.02/ 2501ms total, 38998.40 ops/sec - LPUSH, batch 50/1 min/max/avg: 0/ 4/ 0.14/ 2501ms total, 365013.99 ops/sec - LRANGE 10, 1/1 min/max/avg: 0/ 2/ 0.03/ 2501ms total, 31879.25 ops/sec - LRANGE 10, batch 50/1 min/max/avg: 0/ 1/ 0.32/ 2501ms total, 153698.52 ops/sec - LRANGE 100, 1/1 min/max/avg: 0/ 4/ 0.06/ 2501ms total, 16676.13 ops/sec - LRANGE 100, batch 50/1 min/max/avg: 1/ 6/ 2.03/ 2502ms total, 24520.38 ops/sec - SET 4MiB str, 1/1 min/max/avg: 1/ 6/ 2.11/ 2502ms total, 472.82 ops/sec - SET 4MiB str, batch 20/1 min/max/avg: 85/ 112/ 94.93/ 2563ms total, 210.69 ops/sec - SET 4MiB buf, 1/1 min/max/avg: 1/ 8/ 2.02/ 2502ms total, 490.01 ops/sec - SET 4MiB buf, batch 20/1 min/max/avg: 37/ 52/ 39.48/ 2528ms total, 506.33 ops/sec - GET 4MiB str, 1/1 min/max/avg: 3/ 13/ 5.26/ 2504ms total, 190.10 ops/sec - GET 4MiB str, batch 20/1 min/max/avg: 70/ 106/ 89.36/ 2503ms total, 223.73 ops/sec - GET 4MiB buf, 1/1 min/max/avg: 3/ 11/ 5.04/ 2502ms total, 198.24 ops/sec - GET 4MiB buf, batch 20/1 min/max/avg: 70/ 105/ 88.07/ 2554ms total, 227.09 ops/sec +Lenovo T450s, i7-5600U and 12gb memory +clients: 1, NodeJS: 6.2.0, Redis: 3.2.0, parser: javascript, connected by: tcp + PING, 1/1 avg/max: 0.02/ 5.26 2501ms total, 46916 ops/sec + PING, batch 50/1 avg/max: 0.06/ 4.35 2501ms total, 755178 ops/sec + SET 4B str, 1/1 avg/max: 0.02/ 4.75 2501ms total, 40856 ops/sec + SET 4B str, batch 50/1 avg/max: 0.11/ 1.51 2501ms total, 432727 ops/sec + SET 4B buf, 1/1 avg/max: 0.05/ 2.76 2501ms total, 20659 ops/sec + SET 4B buf, batch 50/1 avg/max: 0.25/ 1.76 2501ms total, 194962 ops/sec + GET 4B str, 1/1 avg/max: 0.02/ 1.55 2501ms total, 45156 ops/sec + GET 4B str, batch 50/1 avg/max: 0.09/ 3.15 2501ms total, 524110 ops/sec + GET 4B buf, 1/1 avg/max: 0.02/ 3.07 2501ms total, 44563 ops/sec + GET 4B buf, batch 50/1 avg/max: 0.10/ 3.18 2501ms total, 473171 ops/sec + SET 4KiB str, 1/1 avg/max: 0.03/ 1.54 2501ms total, 32627 ops/sec + SET 4KiB str, batch 50/1 avg/max: 0.34/ 1.89 2501ms total, 146861 ops/sec + SET 4KiB buf, 1/1 avg/max: 0.05/ 2.85 2501ms total, 20688 ops/sec + SET 4KiB buf, batch 50/1 avg/max: 0.36/ 1.83 2501ms total, 138165 ops/sec + GET 4KiB str, 1/1 avg/max: 0.02/ 1.37 2501ms total, 39389 ops/sec + GET 4KiB str, batch 50/1 avg/max: 0.24/ 1.81 2501ms total, 208157 ops/sec + GET 4KiB buf, 1/1 avg/max: 0.02/ 2.63 2501ms total, 39918 ops/sec + GET 4KiB buf, batch 50/1 avg/max: 0.31/ 8.56 2501ms total, 161575 ops/sec + INCR, 1/1 avg/max: 0.02/ 4.69 2501ms total, 45685 ops/sec + INCR, batch 50/1 avg/max: 0.09/ 3.06 2501ms total, 539964 ops/sec + LPUSH, 1/1 avg/max: 0.02/ 3.04 2501ms total, 41253 ops/sec + LPUSH, batch 50/1 avg/max: 0.12/ 1.94 2501ms total, 425090 ops/sec + LRANGE 10, 1/1 avg/max: 0.02/ 2.28 2501ms total, 39850 ops/sec + LRANGE 10, batch 50/1 avg/max: 0.25/ 1.85 2501ms total, 194302 ops/sec + LRANGE 100, 1/1 avg/max: 0.05/ 2.93 2501ms total, 21026 ops/sec + LRANGE 100, batch 50/1 avg/max: 1.52/ 2.89 2501ms total, 32767 ops/sec + SET 4MiB str, 1/1 avg/max: 5.16/ 15.55 2502ms total, 193 ops/sec + SET 4MiB str, batch 20/1 avg/max: 89.73/ 99.96 2513ms total, 223 ops/sec + SET 4MiB buf, 1/1 avg/max: 2.23/ 8.35 2501ms total, 446 ops/sec + SET 4MiB buf, batch 20/1 avg/max: 41.47/ 50.91 2530ms total, 482 ops/sec + GET 4MiB str, 1/1 avg/max: 2.79/ 10.91 2502ms total, 358 ops/sec + GET 4MiB str, batch 20/1 avg/max: 101.61/118.11 2541ms total, 197 ops/sec + GET 4MiB buf, 1/1 avg/max: 2.32/ 14.93 2502ms total, 430 ops/sec + GET 4MiB buf, batch 20/1 avg/max: 65.01/ 84.72 2536ms total, 308 ops/sec ``` -The hiredis and js parser should most of the time be on the same level. But if you use Redis for big SUNION/SINTER/LRANGE/ZRANGE hiredis is faster. -Therefor the hiredis parser is the default used in node_redis. To use `hiredis`, do: - - npm install hiredis redis - ## Debugging To get debug output run your `node_redis` application with `NODE_DEBUG=redis`. diff --git a/benchmarks/multi_bench.js b/benchmarks/multi_bench.js index c65876b9035..a79d92e83c9 100644 --- a/benchmarks/multi_bench.js +++ b/benchmarks/multi_bench.js @@ -26,7 +26,7 @@ var run_time = returnArg('time', 2500); // ms var pipeline = returnArg('pipeline', 1); // number of concurrent commands var versions_logged = false; var client_options = { - parser: returnArg('parser', 'hiredis'), + parser: returnArg('parser', 'javascript'), path: returnArg('socket') // '/tmp/redis.sock' }; var small_str, large_str, small_buf, large_buf, very_large_str, very_large_buf; diff --git a/changelog.md b/changelog.md index 168a1854c39..ce2f6037b46 100644 --- a/changelog.md +++ b/changelog.md @@ -1,6 +1,20 @@ Changelog ========= +## v.2.6.0 - 25 Mai, 2016 + +In addition to the pre-releases the following changes exist in v.2.6.0: + +Features + +- Updated [redis-parser](https://github.com/NodeRedis/redis-parser) dependency ([changelog](https://github.com/NodeRedis/redis-parser/releases/tag/v.2.0.0)) + - The JS parser is from now on the new default as it is a lot faster than the hiredis parser + - This is no BC as there is no changed behavior for the user at all but just a performance improvement. Explicitly requireing the Hiredis parser is still possible. + +Deprecations + +- The `parser` option is deprecated and should be removed. The built-in Javascript parser is a lot faster than the hiredis parser and has more features + ## v.2.6.0-2 - 29 Apr, 2016 Features diff --git a/index.js b/index.js index a4957bd2198..406c8c817f1 100644 --- a/index.js +++ b/index.js @@ -154,11 +154,11 @@ function RedisClient (options, stream) { this.pipeline = false; this.sub_commands_left = 0; this.times_connected = 0; - this.options = options; this.buffers = options.return_buffers || options.detect_buffers; + this.options = options; this.reply = 'ON'; // Returning replies is the default // Init parser - this.reply_parser = create_parser(this, options); + this.reply_parser = create_parser(this); this.create_stream(); // The listeners will not be attached right away, so let's print the deprecation message while the listener is attached this.on('newListener', function (event) { @@ -184,18 +184,17 @@ util.inherits(RedisClient, EventEmitter); RedisClient.connection_id = 0; function create_parser (self) { - return Parser({ + return new Parser({ returnReply: function (data) { self.return_reply(data); }, returnError: function (err) { // Return a ReplyError to indicate Redis returned an error - self.return_error(new errorClasses.ReplyError(err)); + self.return_error(err); }, returnFatalError: function (err) { // Error out all fired commands. Otherwise they might rely on faulty data. We have to reconnect to get in a working state again // Note: the execution order is important. First flush and emit, then create the stream - err = new errorClasses.ReplyError(err); err.message += '. Please report this.'; self.ready = false; self.flush_and_error({ @@ -209,8 +208,8 @@ function create_parser (self) { self.create_stream(); }, returnBuffers: self.buffers || self.message_buffers, - name: self.options.parser, - stringNumbers: self.options.string_numbers + name: self.options.parser || 'javascript', + stringNumbers: self.options.string_numbers || false }); } @@ -1093,7 +1092,7 @@ exports.RedisClient = RedisClient; exports.print = utils.print; exports.Multi = require('./lib/multi'); exports.AbortError = errorClasses.AbortError; -exports.ReplyError = errorClasses.ReplyError; +exports.ReplyError = Parser.ReplyError; exports.AggregateError = errorClasses.AggregateError; // Add all redis commands / node_redis api to the client diff --git a/lib/customErrors.js b/lib/customErrors.js index 7c6e457036a..0b192f78df3 100644 --- a/lib/customErrors.js +++ b/lib/customErrors.js @@ -4,75 +4,42 @@ var util = require('util'); function AbortError (obj) { Error.captureStackTrace(this, this.constructor); - var message; Object.defineProperty(this, 'name', { - get: function () { - return this.constructor.name; - } + value: 'AbortError', + configurable: true, + writable: true }); Object.defineProperty(this, 'message', { - get: function () { - return message; - }, - set: function (msg) { - message = msg; - } + value: obj.message || '', + configurable: true, + writable: true }); for (var keys = Object.keys(obj), key = keys.pop(); key; key = keys.pop()) { this[key] = obj[key]; } - // Explicitly add the message - // If the obj is a error itself, the message is not enumerable - this.message = obj.message; -} - -function ReplyError (obj) { - Error.captureStackTrace(this, this.constructor); - var tmp; - Object.defineProperty(this, 'name', { - get: function () { - return this.constructor.name; - } - }); - Object.defineProperty(this, 'message', { - get: function () { - return tmp; - }, - set: function (msg) { - tmp = msg; - } - }); - this.message = obj.message; } function AggregateError (obj) { Error.captureStackTrace(this, this.constructor); - var tmp; Object.defineProperty(this, 'name', { - get: function () { - return this.constructor.name; - } + value: 'AggregateError', + configurable: true, + writable: true }); Object.defineProperty(this, 'message', { - get: function () { - return tmp; - }, - set: function (msg) { - tmp = msg; - } + value: obj.message || '', + configurable: true, + writable: true }); for (var keys = Object.keys(obj), key = keys.pop(); key; key = keys.pop()) { this[key] = obj[key]; } - this.message = obj.message; } -util.inherits(ReplyError, Error); util.inherits(AbortError, Error); util.inherits(AggregateError, AbortError); module.exports = { - ReplyError: ReplyError, AbortError: AbortError, AggregateError: AggregateError }; diff --git a/package.json b/package.json index 15ef0ba86cf..82e7f2bdde8 100644 --- a/package.json +++ b/package.json @@ -21,12 +21,13 @@ "coverage": "nyc report --reporter=html", "benchmark": "node benchmarks/multi_bench.js", "test": "nyc --cache mocha ./test/*.js ./test/commands/*.js --timeout=8000", - "posttest": "eslint . --fix" + "posttest": "eslint . --fix && npm run coverage", + "compare": "node benchmarks/diff_multi_bench_output.js beforeBench.txt afterBench.txt" }, "dependencies": { "double-ended-queue": "^2.1.0-0", "redis-commands": "^1.2.0", - "redis-parser": "^1.3.0" + "redis-parser": "^2.0.0" }, "engines": { "node": ">=0.10.0" diff --git a/test/custom_errors.spec.js b/test/custom_errors.spec.js new file mode 100644 index 00000000000..d7c6ebb6022 --- /dev/null +++ b/test/custom_errors.spec.js @@ -0,0 +1,89 @@ +'use strict'; + +var assert = require('assert'); +var errors = require('../lib/customErrors'); + +describe('errors', function () { + + describe('AbortError', function () { + it('should inherit from Error', function () { + var e = new errors.AbortError({}); + assert.strictEqual(e.message, ''); + assert.strictEqual(e.name, 'AbortError'); + assert.strictEqual(Object.keys(e).length, 0); + assert(e instanceof Error); + assert(e instanceof errors.AbortError); + }); + + it('should list options properties but not name and message', function () { + var e = new errors.AbortError({ + name: 'weird', + message: 'hello world', + property: true + }); + assert.strictEqual(e.message, 'hello world'); + assert.strictEqual(e.name, 'weird'); + assert.strictEqual(e.property, true); + assert.strictEqual(Object.keys(e).length, 1); + assert(e instanceof Error); + assert(e instanceof errors.AbortError); + assert(delete e.name); + assert.strictEqual(e.name, 'Error'); + }); + + it('should change name and message', function () { + var e = new errors.AbortError({ + message: 'hello world', + property: true + }); + assert.strictEqual(e.name, 'AbortError'); + assert.strictEqual(e.message, 'hello world'); + e.name = 'foo'; + e.message = 'foobar'; + assert.strictEqual(e.name, 'foo'); + assert.strictEqual(e.message, 'foobar'); + }); + }); + + describe('AggregateError', function () { + it('should inherit from Error and AbortError', function () { + var e = new errors.AggregateError({}); + assert.strictEqual(e.message, ''); + assert.strictEqual(e.name, 'AggregateError'); + assert.strictEqual(Object.keys(e).length, 0); + assert(e instanceof Error); + assert(e instanceof errors.AggregateError); + assert(e instanceof errors.AbortError); + }); + + it('should list options properties but not name and message', function () { + var e = new errors.AggregateError({ + name: 'weird', + message: 'hello world', + property: true + }); + assert.strictEqual(e.message, 'hello world'); + assert.strictEqual(e.name, 'weird'); + assert.strictEqual(e.property, true); + assert.strictEqual(Object.keys(e).length, 1); + assert(e instanceof Error); + assert(e instanceof errors.AggregateError); + assert(e instanceof errors.AbortError); + assert(delete e.name); + assert.strictEqual(e.name, 'Error'); + }); + + it('should change name and message', function () { + var e = new errors.AggregateError({ + message: 'hello world', + property: true + }); + assert.strictEqual(e.name, 'AggregateError'); + assert.strictEqual(e.message, 'hello world'); + e.name = 'foo'; + e.message = 'foobar'; + assert.strictEqual(e.name, 'foo'); + assert.strictEqual(e.message, 'foobar'); + }); + }); +}); diff --git a/test/node_redis.spec.js b/test/node_redis.spec.js index 8ba91fdfe47..cb36b6c4af3 100644 --- a/test/node_redis.spec.js +++ b/test/node_redis.spec.js @@ -998,8 +998,12 @@ describe('The node_redis client', function () { assert(err instanceof redis.AbortError); error = err.origin; }); - // Fail the set answer. Has no corresponding command obj and will therefore land in the error handler and set - client.reply_parser.execute(new Buffer('a*1\r*1\r$1`zasd\r\na')); + // Make sure we call execute out of the reply + // ready is called in a reply + process.nextTick(function () { + // Fail the set answer. Has no corresponding command obj and will therefore land in the error handler and set + client.reply_parser.execute(new Buffer('a*1\r*1\r$1`zasd\r\na')); + }); }); }); }); From 55528d8b1b102b2c181253ab6618d6adcd3b9973 Mon Sep 17 00:00:00 2001 From: Ruben Bridgewater Date: Wed, 25 May 2016 22:52:31 +0300 Subject: [PATCH 0654/1748] Revert 228573b8d78c7bb3d00c28413093422428b27325 Not inheriting from the prototype is a BC --- changelog.md | 4 ++++ lib/rawObject.js | 8 -------- lib/utils.js | 4 +--- test/commands/hgetall.spec.js | 8 +++----- test/pubsub.spec.js | 4 ++-- 5 files changed, 10 insertions(+), 18 deletions(-) delete mode 100644 lib/rawObject.js diff --git a/changelog.md b/changelog.md index ce2f6037b46..8694afa4a46 100644 --- a/changelog.md +++ b/changelog.md @@ -11,6 +11,10 @@ Features - The JS parser is from now on the new default as it is a lot faster than the hiredis parser - This is no BC as there is no changed behavior for the user at all but just a performance improvement. Explicitly requireing the Hiredis parser is still possible. +Bugfixes + +- Reverted support for `__proto__` (v.2.6.0-2) to prevent and breaking change + Deprecations - The `parser` option is deprecated and should be removed. The built-in Javascript parser is a lot faster than the hiredis parser and has more features diff --git a/lib/rawObject.js b/lib/rawObject.js deleted file mode 100644 index 26376fc3608..00000000000 --- a/lib/rawObject.js +++ /dev/null @@ -1,8 +0,0 @@ -'use strict'; - -// Using a predefined object with this prototype is faster than calling `Object.create(null)` directly -// This is needed to make sure `__proto__` and similar reserved words can be used -function RawObject () {} -RawObject.prototype = Object.create(null); - -module.exports = RawObject; diff --git a/lib/utils.js b/lib/utils.js index d3d9c2fa594..fafffdac7fe 100644 --- a/lib/utils.js +++ b/lib/utils.js @@ -1,7 +1,5 @@ 'use strict'; -var RawObject = require('./rawObject'); - // hgetall converts its replies to an Object. If the reply is empty, null is returned. // These function are only called with internal data and have therefore always the same instanceof X function replyToObject (reply) { @@ -9,7 +7,7 @@ function replyToObject (reply) { if (reply.length === 0 || !(reply instanceof Array)) { return null; } - var obj = new RawObject(); + var obj = {}; for (var i = 0; i < reply.length; i += 2) { obj[reply[i].toString('binary')] = reply[i + 1]; } diff --git a/test/commands/hgetall.spec.js b/test/commands/hgetall.spec.js index ba443265100..6c00e3ed099 100644 --- a/test/commands/hgetall.spec.js +++ b/test/commands/hgetall.spec.js @@ -22,12 +22,10 @@ describe("The 'hgetall' method", function () { }); it('handles simple keys and values', function (done) { - client.hmset(['hosts', '__proto__', '1', 'another', '23', 'home', '1234'], helper.isString('OK')); + client.hmset(['hosts', 'hasOwnProperty', '1', 'another', '23', 'home', '1234'], helper.isString('OK')); client.HGETALL(['hosts'], function (err, obj) { - if (!/^v0\.10/.test(process.version)) { - assert.strictEqual(3, Object.keys(obj).length); - assert.strictEqual('1', obj.__proto__.toString()); // eslint-disable-line no-proto - } + assert.strictEqual(3, Object.keys(obj).length); + assert.strictEqual('1', obj.hasOwnProperty.toString()); assert.strictEqual('23', obj.another.toString()); assert.strictEqual('1234', obj.home.toString()); done(err); diff --git a/test/pubsub.spec.js b/test/pubsub.spec.js index 1d33b129941..65fce290145 100644 --- a/test/pubsub.spec.js +++ b/test/pubsub.spec.js @@ -204,8 +204,8 @@ describe('publish/subscribe', function () { it('subscribe; close; resubscribe with prototype inherited property names', function (done) { var count = 0; - var channels = ['__proto__', 'channel 2']; - var msg = ['hi from channel __proto__', 'hi from channel 2']; + var channels = ['channel 1', 'channel 2']; + var msg = ['hi from channel 1', 'hi from channel 2']; sub.on('message', function (channel, message) { var n = Math.max(count - 1, 0); From ffaaf0f6d5311326f9a1f8f6c6a3fd7d060a0b44 Mon Sep 17 00:00:00 2001 From: Ruben Bridgewater Date: Fri, 27 May 2016 16:25:59 +0200 Subject: [PATCH 0655/1748] Add name property to all Redis functions --- changelog.md | 1 + lib/commands.js | 6 ++++++ lib/individualCommands.js | 4 ++-- 3 files changed, 9 insertions(+), 2 deletions(-) diff --git a/changelog.md b/changelog.md index 8694afa4a46..74630e63a73 100644 --- a/changelog.md +++ b/changelog.md @@ -10,6 +10,7 @@ Features - Updated [redis-parser](https://github.com/NodeRedis/redis-parser) dependency ([changelog](https://github.com/NodeRedis/redis-parser/releases/tag/v.2.0.0)) - The JS parser is from now on the new default as it is a lot faster than the hiredis parser - This is no BC as there is no changed behavior for the user at all but just a performance improvement. Explicitly requireing the Hiredis parser is still possible. +- Added name property to all Redis functions Bugfixes diff --git a/lib/commands.js b/lib/commands.js index a99d0257149..fc20ff8165e 100644 --- a/lib/commands.js +++ b/lib/commands.js @@ -44,6 +44,9 @@ commands.list.forEach(function (command) { } return this.internal_send_command(command, arr, callback); }; + Object.defineProperty(RedisClient.prototype[command], 'name', { + value: command + }); } // Do not override existing functions @@ -82,5 +85,8 @@ commands.list.forEach(function (command) { this.queue.push([command, arr, callback]); return this; }; + Object.defineProperty(Multi.prototype[command], 'name', { + value: command + }); } }); diff --git a/lib/individualCommands.js b/lib/individualCommands.js index 808ad99a0b0..d676b0ce7a9 100644 --- a/lib/individualCommands.js +++ b/lib/individualCommands.js @@ -96,7 +96,7 @@ function quit_callback (self, callback) { }; } -RedisClient.prototype.QUIT = RedisClient.prototype.quit = function (callback) { +RedisClient.prototype.QUIT = RedisClient.prototype.quit = function quit (callback) { // TODO: Consider this for v.3 // Allow the quit command to be fired as soon as possible to prevent it landing in the offline queue. // this.ready = this.offline_queue.length === 0; @@ -108,7 +108,7 @@ RedisClient.prototype.QUIT = RedisClient.prototype.quit = function (callback) { }; // Only works with batch, not in a transaction -Multi.prototype.QUIT = Multi.prototype.quit = function (callback) { +Multi.prototype.QUIT = Multi.prototype.quit = function quit (callback) { var self = this._client; var call_on_write = function () { // If called in a multi context, we expect redis is available From 8008fb5eb488b19cfd3526b6b970fea30f6626de Mon Sep 17 00:00:00 2001 From: Ruben Bridgewater Date: Fri, 27 May 2016 16:29:43 +0200 Subject: [PATCH 0656/1748] Fix changelog entry --- changelog.md | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/changelog.md b/changelog.md index 74630e63a73..683d9d94c60 100644 --- a/changelog.md +++ b/changelog.md @@ -24,14 +24,13 @@ Deprecations Features -- Added support for the new `CLIENT REPLY ON|OFF|SKIP` command (Redis v.3.2) +- Added support for the new [CLIENT REPLY ON|OFF|SKIP](http://redis.io/commands/client-reply) command (Redis v.3.2) - Added support for camelCase - The Node.js landscape default is to use camelCase. node_redis is a bit out of the box here but from now on it is possible to use both, just as you prefer! - If there's any documented variable missing as camelCased, please open a issue for it - Improve error handling significantly - Only emit an error if the error has not already been handled in a callback - - Emit an error if a command would otherwise silently fail (no callback present) - Improved unspecific error messages e.g. "Connection gone from end / close event" - Added `args` to command errors to improve identification of the error - Added origin to errors if there's e.g. a connection error From a90b791295b446f7f9a05a277757b4a23c434409 Mon Sep 17 00:00:00 2001 From: Ruben Bridgewater Date: Fri, 27 May 2016 16:35:09 +0200 Subject: [PATCH 0657/1748] Only run detect_buffers tests once --- test/detect_buffers.spec.js | 445 ++++++++++++++++++------------------ 1 file changed, 220 insertions(+), 225 deletions(-) diff --git a/test/detect_buffers.spec.js b/test/detect_buffers.spec.js index 82f1aa99e36..2e2f7f1ed02 100644 --- a/test/detect_buffers.spec.js +++ b/test/detect_buffers.spec.js @@ -7,265 +7,260 @@ var redis = config.redis; describe('detect_buffers', function () { - helper.allTests(function (parser, ip, args) { + var client; + var args = config.configureClient('javascript', 'localhost', { + detect_buffers: true + }); + + beforeEach(function (done) { + client = redis.createClient.apply(null, args); + client.once('error', done); + client.once('connect', function () { + client.flushdb(function (err) { + client.hmset('hash key 2', 'key 1', 'val 1', 'key 2', 'val 2'); + client.set('string key 1', 'string value'); + return done(err); + }); + }); + }); - describe('using ' + parser + ' and ' + ip, function () { - var client; - var args = config.configureClient(parser, ip, { - detect_buffers: true + afterEach(function () { + client.end(true); + }); + + describe('get', function () { + describe('first argument is a string', function () { + it('returns a string', function (done) { + client.get('string key 1', helper.isString('string value', done)); }); - beforeEach(function (done) { - client = redis.createClient.apply(null, args); - client.once('error', done); - client.once('connect', function () { - client.flushdb(function (err) { - client.hmset('hash key 2', 'key 1', 'val 1', 'key 2', 'val 2'); - client.set('string key 1', 'string value'); - return done(err); - }); + it('returns a string when executed as part of transaction', function (done) { + client.multi().get('string key 1').exec(function (err, res) { + helper.isString('string value', done)(err, res[0]); }); }); + }); - afterEach(function () { - client.end(true); + describe('first argument is a buffer', function () { + it('returns a buffer', function (done) { + client.get(new Buffer('string key 1'), function (err, reply) { + assert.strictEqual(true, Buffer.isBuffer(reply)); + assert.strictEqual('', reply.inspect()); + return done(err); + }); }); - describe('get', function () { - describe('first argument is a string', function () { - it('returns a string', function (done) { - client.get('string key 1', helper.isString('string value', done)); - }); + it('returns a bufffer when executed as part of transaction', function (done) { + client.multi().get(new Buffer('string key 1')).exec(function (err, reply) { + assert.strictEqual(1, reply.length); + assert.strictEqual(true, Buffer.isBuffer(reply[0])); + assert.strictEqual('', reply[0].inspect()); + return done(err); + }); + }); + }); + }); - it('returns a string when executed as part of transaction', function (done) { - client.multi().get('string key 1').exec(function (err, res) { - helper.isString('string value', done)(err, res[0]); - }); - }); + describe('multi.hget', function () { + it('can interleave string and buffer results', function (done) { + client.multi() + .hget('hash key 2', 'key 1') + .hget(new Buffer('hash key 2'), 'key 1') + .hget('hash key 2', new Buffer('key 2')) + .hget('hash key 2', 'key 2') + .exec(function (err, reply) { + assert.strictEqual(true, Array.isArray(reply)); + assert.strictEqual(4, reply.length); + assert.strictEqual('val 1', reply[0]); + assert.strictEqual(true, Buffer.isBuffer(reply[1])); + assert.strictEqual('', reply[1].inspect()); + assert.strictEqual(true, Buffer.isBuffer(reply[2])); + assert.strictEqual('', reply[2].inspect()); + assert.strictEqual('val 2', reply[3]); + return done(err); }); + }); + }); - describe('first argument is a buffer', function () { - it('returns a buffer', function (done) { - client.get(new Buffer('string key 1'), function (err, reply) { - assert.strictEqual(true, Buffer.isBuffer(reply)); - assert.strictEqual('', reply.inspect()); - return done(err); - }); - }); + describe('batch.hget', function () { + it('can interleave string and buffer results', function (done) { + client.batch() + .hget('hash key 2', 'key 1') + .hget(new Buffer('hash key 2'), 'key 1') + .hget('hash key 2', new Buffer('key 2')) + .hget('hash key 2', 'key 2') + .exec(function (err, reply) { + assert.strictEqual(true, Array.isArray(reply)); + assert.strictEqual(4, reply.length); + assert.strictEqual('val 1', reply[0]); + assert.strictEqual(true, Buffer.isBuffer(reply[1])); + assert.strictEqual('', reply[1].inspect()); + assert.strictEqual(true, Buffer.isBuffer(reply[2])); + assert.strictEqual('', reply[2].inspect()); + assert.strictEqual('val 2', reply[3]); + return done(err); + }); + }); + }); - it('returns a bufffer when executed as part of transaction', function (done) { - client.multi().get(new Buffer('string key 1')).exec(function (err, reply) { - assert.strictEqual(1, reply.length); - assert.strictEqual(true, Buffer.isBuffer(reply[0])); - assert.strictEqual('', reply[0].inspect()); - return done(err); - }); - }); + describe('hmget', function () { + describe('first argument is a string', function () { + it('returns strings for keys requested', function (done) { + client.hmget('hash key 2', 'key 1', 'key 2', function (err, reply) { + assert.strictEqual(true, Array.isArray(reply)); + assert.strictEqual(2, reply.length); + assert.strictEqual('val 1', reply[0]); + assert.strictEqual('val 2', reply[1]); + return done(err); }); }); - describe('multi.hget', function () { - it('can interleave string and buffer results', function (done) { - client.multi() - .hget('hash key 2', 'key 1') - .hget(new Buffer('hash key 2'), 'key 1') - .hget('hash key 2', new Buffer('key 2')) - .hget('hash key 2', 'key 2') - .exec(function (err, reply) { - assert.strictEqual(true, Array.isArray(reply)); - assert.strictEqual(4, reply.length); - assert.strictEqual('val 1', reply[0]); - assert.strictEqual(true, Buffer.isBuffer(reply[1])); - assert.strictEqual('', reply[1].inspect()); - assert.strictEqual(true, Buffer.isBuffer(reply[2])); - assert.strictEqual('', reply[2].inspect()); - assert.strictEqual('val 2', reply[3]); - return done(err); - }); + it('returns strings for keys requested in transaction', function (done) { + client.multi().hmget('hash key 2', 'key 1', 'key 2').exec(function (err, reply) { + assert.strictEqual(true, Array.isArray(reply)); + assert.strictEqual(1, reply.length); + assert.strictEqual(2, reply[0].length); + assert.strictEqual('val 1', reply[0][0]); + assert.strictEqual('val 2', reply[0][1]); + return done(err); }); }); - describe('batch.hget', function () { - it('can interleave string and buffer results', function (done) { - client.batch() - .hget('hash key 2', 'key 1') - .hget(new Buffer('hash key 2'), 'key 1') - .hget('hash key 2', new Buffer('key 2')) - .hget('hash key 2', 'key 2') - .exec(function (err, reply) { - assert.strictEqual(true, Array.isArray(reply)); - assert.strictEqual(4, reply.length); - assert.strictEqual('val 1', reply[0]); - assert.strictEqual(true, Buffer.isBuffer(reply[1])); - assert.strictEqual('', reply[1].inspect()); - assert.strictEqual(true, Buffer.isBuffer(reply[2])); - assert.strictEqual('', reply[2].inspect()); - assert.strictEqual('val 2', reply[3]); - return done(err); - }); + it('handles array of strings with undefined values (repro #344)', function (done) { + client.hmget('hash key 2', 'key 3', 'key 4', function (err, reply) { + assert.strictEqual(true, Array.isArray(reply)); + assert.strictEqual(2, reply.length); + assert.equal(null, reply[0]); + assert.equal(null, reply[1]); + return done(err); }); }); - describe('hmget', function () { - describe('first argument is a string', function () { - it('returns strings for keys requested', function (done) { - client.hmget('hash key 2', 'key 1', 'key 2', function (err, reply) { - assert.strictEqual(true, Array.isArray(reply)); - assert.strictEqual(2, reply.length); - assert.strictEqual('val 1', reply[0]); - assert.strictEqual('val 2', reply[1]); - return done(err); - }); - }); - - it('returns strings for keys requested in transaction', function (done) { - client.multi().hmget('hash key 2', 'key 1', 'key 2').exec(function (err, reply) { - assert.strictEqual(true, Array.isArray(reply)); - assert.strictEqual(1, reply.length); - assert.strictEqual(2, reply[0].length); - assert.strictEqual('val 1', reply[0][0]); - assert.strictEqual('val 2', reply[0][1]); - return done(err); - }); - }); - - it('handles array of strings with undefined values (repro #344)', function (done) { - client.hmget('hash key 2', 'key 3', 'key 4', function (err, reply) { - assert.strictEqual(true, Array.isArray(reply)); - assert.strictEqual(2, reply.length); - assert.equal(null, reply[0]); - assert.equal(null, reply[1]); - return done(err); - }); - }); - - it('handles array of strings with undefined values in transaction (repro #344)', function (done) { - client.multi().hmget('hash key 2', 'key 3', 'key 4').exec(function (err, reply) { - assert.strictEqual(true, Array.isArray(reply)); - assert.strictEqual(1, reply.length); - assert.strictEqual(2, reply[0].length); - assert.equal(null, reply[0][0]); - assert.equal(null, reply[0][1]); - return done(err); - }); - }); + it('handles array of strings with undefined values in transaction (repro #344)', function (done) { + client.multi().hmget('hash key 2', 'key 3', 'key 4').exec(function (err, reply) { + assert.strictEqual(true, Array.isArray(reply)); + assert.strictEqual(1, reply.length); + assert.strictEqual(2, reply[0].length); + assert.equal(null, reply[0][0]); + assert.equal(null, reply[0][1]); + return done(err); }); + }); + }); - describe('first argument is a buffer', function () { - it('returns buffers for keys requested', function (done) { - client.hmget(new Buffer('hash key 2'), 'key 1', 'key 2', function (err, reply) { - assert.strictEqual(true, Array.isArray(reply)); - assert.strictEqual(2, reply.length); - assert.strictEqual(true, Buffer.isBuffer(reply[0])); - assert.strictEqual(true, Buffer.isBuffer(reply[1])); - assert.strictEqual('', reply[0].inspect()); - assert.strictEqual('', reply[1].inspect()); - return done(err); - }); - }); + describe('first argument is a buffer', function () { + it('returns buffers for keys requested', function (done) { + client.hmget(new Buffer('hash key 2'), 'key 1', 'key 2', function (err, reply) { + assert.strictEqual(true, Array.isArray(reply)); + assert.strictEqual(2, reply.length); + assert.strictEqual(true, Buffer.isBuffer(reply[0])); + assert.strictEqual(true, Buffer.isBuffer(reply[1])); + assert.strictEqual('', reply[0].inspect()); + assert.strictEqual('', reply[1].inspect()); + return done(err); + }); + }); - it('returns buffers for keys requested in transaction', function (done) { - client.multi().hmget(new Buffer('hash key 2'), 'key 1', 'key 2').exec(function (err, reply) { - assert.strictEqual(true, Array.isArray(reply)); - assert.strictEqual(1, reply.length); - assert.strictEqual(2, reply[0].length); - assert.strictEqual(true, Buffer.isBuffer(reply[0][0])); - assert.strictEqual(true, Buffer.isBuffer(reply[0][1])); - assert.strictEqual('', reply[0][0].inspect()); - assert.strictEqual('', reply[0][1].inspect()); - return done(err); - }); - }); + it('returns buffers for keys requested in transaction', function (done) { + client.multi().hmget(new Buffer('hash key 2'), 'key 1', 'key 2').exec(function (err, reply) { + assert.strictEqual(true, Array.isArray(reply)); + assert.strictEqual(1, reply.length); + assert.strictEqual(2, reply[0].length); + assert.strictEqual(true, Buffer.isBuffer(reply[0][0])); + assert.strictEqual(true, Buffer.isBuffer(reply[0][1])); + assert.strictEqual('', reply[0][0].inspect()); + assert.strictEqual('', reply[0][1].inspect()); + return done(err); + }); + }); - it('returns buffers for keys requested in .batch', function (done) { - client.batch().hmget(new Buffer('hash key 2'), 'key 1', 'key 2').exec(function (err, reply) { - assert.strictEqual(true, Array.isArray(reply)); - assert.strictEqual(1, reply.length); - assert.strictEqual(2, reply[0].length); - assert.strictEqual(true, Buffer.isBuffer(reply[0][0])); - assert.strictEqual(true, Buffer.isBuffer(reply[0][1])); - assert.strictEqual('', reply[0][0].inspect()); - assert.strictEqual('', reply[0][1].inspect()); - return done(err); - }); - }); + it('returns buffers for keys requested in .batch', function (done) { + client.batch().hmget(new Buffer('hash key 2'), 'key 1', 'key 2').exec(function (err, reply) { + assert.strictEqual(true, Array.isArray(reply)); + assert.strictEqual(1, reply.length); + assert.strictEqual(2, reply[0].length); + assert.strictEqual(true, Buffer.isBuffer(reply[0][0])); + assert.strictEqual(true, Buffer.isBuffer(reply[0][1])); + assert.strictEqual('', reply[0][0].inspect()); + assert.strictEqual('', reply[0][1].inspect()); + return done(err); }); }); + }); + }); - describe('hgetall', function (done) { - describe('first argument is a string', function () { - it('returns string values', function (done) { - client.hgetall('hash key 2', function (err, reply) { - assert.strictEqual('object', typeof reply); - assert.strictEqual(2, Object.keys(reply).length); - assert.strictEqual('val 1', reply['key 1']); - assert.strictEqual('val 2', reply['key 2']); - return done(err); - }); - }); + describe('hgetall', function (done) { + describe('first argument is a string', function () { + it('returns string values', function (done) { + client.hgetall('hash key 2', function (err, reply) { + assert.strictEqual('object', typeof reply); + assert.strictEqual(2, Object.keys(reply).length); + assert.strictEqual('val 1', reply['key 1']); + assert.strictEqual('val 2', reply['key 2']); + return done(err); + }); + }); - it('returns string values when executed in transaction', function (done) { - client.multi().hgetall('hash key 2').exec(function (err, reply) { - assert.strictEqual(1, reply.length); - assert.strictEqual('object', typeof reply[0]); - assert.strictEqual(2, Object.keys(reply[0]).length); - assert.strictEqual('val 1', reply[0]['key 1']); - assert.strictEqual('val 2', reply[0]['key 2']); - return done(err); - }); - }); + it('returns string values when executed in transaction', function (done) { + client.multi().hgetall('hash key 2').exec(function (err, reply) { + assert.strictEqual(1, reply.length); + assert.strictEqual('object', typeof reply[0]); + assert.strictEqual(2, Object.keys(reply[0]).length); + assert.strictEqual('val 1', reply[0]['key 1']); + assert.strictEqual('val 2', reply[0]['key 2']); + return done(err); + }); + }); - it('returns string values when executed in .batch', function (done) { - client.batch().hgetall('hash key 2').exec(function (err, reply) { - assert.strictEqual(1, reply.length); - assert.strictEqual('object', typeof reply[0]); - assert.strictEqual(2, Object.keys(reply[0]).length); - assert.strictEqual('val 1', reply[0]['key 1']); - assert.strictEqual('val 2', reply[0]['key 2']); - return done(err); - }); - }); + it('returns string values when executed in .batch', function (done) { + client.batch().hgetall('hash key 2').exec(function (err, reply) { + assert.strictEqual(1, reply.length); + assert.strictEqual('object', typeof reply[0]); + assert.strictEqual(2, Object.keys(reply[0]).length); + assert.strictEqual('val 1', reply[0]['key 1']); + assert.strictEqual('val 2', reply[0]['key 2']); + return done(err); }); + }); + }); - describe('first argument is a buffer', function () { - it('returns buffer values', function (done) { - client.hgetall(new Buffer('hash key 2'), function (err, reply) { - assert.strictEqual(null, err); - assert.strictEqual('object', typeof reply); - assert.strictEqual(2, Object.keys(reply).length); - assert.strictEqual(true, Buffer.isBuffer(reply['key 1'])); - assert.strictEqual(true, Buffer.isBuffer(reply['key 2'])); - assert.strictEqual('', reply['key 1'].inspect()); - assert.strictEqual('', reply['key 2'].inspect()); - return done(err); - }); - }); + describe('first argument is a buffer', function () { + it('returns buffer values', function (done) { + client.hgetall(new Buffer('hash key 2'), function (err, reply) { + assert.strictEqual(null, err); + assert.strictEqual('object', typeof reply); + assert.strictEqual(2, Object.keys(reply).length); + assert.strictEqual(true, Buffer.isBuffer(reply['key 1'])); + assert.strictEqual(true, Buffer.isBuffer(reply['key 2'])); + assert.strictEqual('', reply['key 1'].inspect()); + assert.strictEqual('', reply['key 2'].inspect()); + return done(err); + }); + }); - it('returns buffer values when executed in transaction', function (done) { - client.multi().hgetall(new Buffer('hash key 2')).exec(function (err, reply) { - assert.strictEqual(1, reply.length); - assert.strictEqual('object', typeof reply[0]); - assert.strictEqual(2, Object.keys(reply[0]).length); - assert.strictEqual(true, Buffer.isBuffer(reply[0]['key 1'])); - assert.strictEqual(true, Buffer.isBuffer(reply[0]['key 2'])); - assert.strictEqual('', reply[0]['key 1'].inspect()); - assert.strictEqual('', reply[0]['key 2'].inspect()); - return done(err); - }); - }); + it('returns buffer values when executed in transaction', function (done) { + client.multi().hgetall(new Buffer('hash key 2')).exec(function (err, reply) { + assert.strictEqual(1, reply.length); + assert.strictEqual('object', typeof reply[0]); + assert.strictEqual(2, Object.keys(reply[0]).length); + assert.strictEqual(true, Buffer.isBuffer(reply[0]['key 1'])); + assert.strictEqual(true, Buffer.isBuffer(reply[0]['key 2'])); + assert.strictEqual('', reply[0]['key 1'].inspect()); + assert.strictEqual('', reply[0]['key 2'].inspect()); + return done(err); + }); + }); - it('returns buffer values when executed in .batch', function (done) { - client.batch().hgetall(new Buffer('hash key 2')).exec(function (err, reply) { - assert.strictEqual(1, reply.length); - assert.strictEqual('object', typeof reply[0]); - assert.strictEqual(2, Object.keys(reply[0]).length); - assert.strictEqual(true, Buffer.isBuffer(reply[0]['key 1'])); - assert.strictEqual(true, Buffer.isBuffer(reply[0]['key 2'])); - assert.strictEqual('', reply[0]['key 1'].inspect()); - assert.strictEqual('', reply[0]['key 2'].inspect()); - return done(err); - }); - }); + it('returns buffer values when executed in .batch', function (done) { + client.batch().hgetall(new Buffer('hash key 2')).exec(function (err, reply) { + assert.strictEqual(1, reply.length); + assert.strictEqual('object', typeof reply[0]); + assert.strictEqual(2, Object.keys(reply[0]).length); + assert.strictEqual(true, Buffer.isBuffer(reply[0]['key 1'])); + assert.strictEqual(true, Buffer.isBuffer(reply[0]['key 2'])); + assert.strictEqual('', reply[0]['key 1'].inspect()); + assert.strictEqual('', reply[0]['key 2'].inspect()); + return done(err); }); }); }); From 899f9b7fe457d23376176c81f036fc4093678c8f Mon Sep 17 00:00:00 2001 From: Ruben Bridgewater Date: Fri, 27 May 2016 16:51:43 +0200 Subject: [PATCH 0658/1748] Fix hungry developer typo --- index.js | 2 +- lib/utils.js | 8 ++++---- test/utils.spec.js | 2 +- 3 files changed, 6 insertions(+), 6 deletions(-) diff --git a/index.js b/index.js index 406c8c817f1..3f6fa1df6e6 100644 --- a/index.js +++ b/index.js @@ -1042,7 +1042,7 @@ Object.defineProperty(RedisClient.prototype, 'offline_queue_length', { }); // Add support for camelCase by adding read only properties to the client -// All known exposed snack_case variables are added here +// All known exposed snake_case variables are added here Object.defineProperty(RedisClient.prototype, 'retryDelay', { get: function () { return this.retry_delay; diff --git a/lib/utils.js b/lib/utils.js index fafffdac7fe..bc685b9a4f8 100644 --- a/lib/utils.js +++ b/lib/utils.js @@ -57,14 +57,14 @@ function clone (obj) { var elems = Object.keys(obj); var elem; while (elem = elems.pop()) { - // Accept camelCase options and convert them to snack_case - var snack_case = elem.replace(/[A-Z][^A-Z]/g, '_$&').toLowerCase(); + // Accept camelCase options and convert them to snake_case + var snake_case = elem.replace(/[A-Z][^A-Z]/g, '_$&').toLowerCase(); // If camelCase is detected, pass it to the client, so all variables are going to be camelCased // There are no deep nested options objects yet, but let's handle this future proof - if (snack_case !== elem.toLowerCase()) { + if (snake_case !== elem.toLowerCase()) { camelCase = true; } - copy[snack_case] = clone(obj[elem]); + copy[snake_case] = clone(obj[elem]); } return copy; } diff --git a/test/utils.spec.js b/test/utils.spec.js index 094640066b5..f6fa8d4e7dd 100644 --- a/test/utils.spec.js +++ b/test/utils.spec.js @@ -29,7 +29,7 @@ describe('utils.js', function () { assert.strictEqual(Object.keys(b).length, 0); }); - it('transform camelCase options to snack_case and add the camel_case option', function () { + it('transform camelCase options to snake_case and add the camel_case option', function () { var a = utils.clone({ optionOneTwo: true, retryStrategy: false, From 8b6f2dd35ec232a7adaf8c8da1ea062c0dbb1c78 Mon Sep 17 00:00:00 2001 From: Ruben Bridgewater Date: Fri, 27 May 2016 17:32:42 +0200 Subject: [PATCH 0659/1748] Refactor command parsing --- index.js | 52 +++++++++++--------------- lib/command.js | 16 ++------ lib/commands.js | 5 ++- lib/extendedApi.js | 3 +- lib/individualCommands.js | 64 ++++++++++++++++---------------- lib/multi.js | 76 +++++++++++++++++--------------------- test/commands/info.spec.js | 6 +-- test/multi.spec.js | 5 +++ test/node_redis.spec.js | 1 + test/pubsub.spec.js | 1 + 10 files changed, 106 insertions(+), 123 deletions(-) diff --git a/index.js b/index.js index 3f6fa1df6e6..84c9b644272 100644 --- a/index.js +++ b/index.js @@ -6,8 +6,6 @@ var util = require('util'); var utils = require('./lib/utils'); var Queue = require('double-ended-queue'); var errorClasses = require('./lib/customErrors'); -var Command = require('./lib/command').Command; -var OfflineCommand = require('./lib/command').OfflineCommand; var EventEmitter = require('events'); var Parser = require('redis-parser'); var commands = require('redis-commands'); @@ -156,6 +154,7 @@ function RedisClient (options, stream) { this.times_connected = 0; this.buffers = options.return_buffers || options.detect_buffers; this.options = options; + this.old_state = {}; this.reply = 'ON'; // Returning replies is the default // Init parser this.reply_parser = create_parser(this); @@ -443,14 +442,10 @@ RedisClient.prototype.on_ready = function () { // Restore modal commands from previous connection. The order of the commands is important if (this.selected_db !== undefined) { - this.internal_send_command('select', [this.selected_db]); + this.select(this.selected_db); } - if (this.old_state !== null) { - this.monitoring = this.old_state.monitoring; - this.pub_sub_mode = this.old_state.pub_sub_mode; - } - if (this.monitoring) { // Monitor has to be fired before pub sub commands - this.internal_send_command('monitor', []); // The state is still set + if (this.old_state.monitoring) { // Monitor has to be fired before pub sub commands + this.monitor(); } var callback_count = Object.keys(this.subscription_set).length; if (!this.options.disable_resubscribing && callback_count) { @@ -466,8 +461,8 @@ RedisClient.prototype.on_ready = function () { debug('Sending pub/sub on_ready commands'); for (var key in this.subscription_set) { var command = key.slice(0, key.indexOf('_')); - var args = self.subscription_set[key]; - self.internal_send_command(command, [args], callback); + var args = this.subscription_set[key]; + this[command]([args], callback); } this.send_offline_queue(); return; @@ -530,7 +525,7 @@ RedisClient.prototype.ready_check = function () { RedisClient.prototype.send_offline_queue = function () { for (var command_obj = this.offline_queue.shift(); command_obj; command_obj = this.offline_queue.shift()) { debug('Sending offline command: ' + command_obj.command); - this.internal_send_command(command_obj.command, command_obj.args, command_obj.callback, command_obj.call_on_write); + this.internal_send_command(command_obj); } this.drain(); }; @@ -575,8 +570,7 @@ RedisClient.prototype.connection_gone = function (why, error) { this.pipeline = false; var state = { - monitoring: this.monitoring, - pub_sub_mode: this.pub_sub_mode + monitoring: this.monitoring }; this.old_state = state; this.monitoring = false; @@ -834,7 +828,6 @@ RedisClient.prototype.return_reply = function (reply) { function handle_offline_command (self, command_obj) { var command = command_obj.command; - var callback = command_obj.callback; var err, msg; if (self.closing || !self.enable_offline_queue) { command = command.toUpperCase(); @@ -852,10 +845,10 @@ function handle_offline_command (self, command_obj) { code: 'NR_CLOSED', command: command }); - if (command_obj.args && command_obj.args.length) { + if (command_obj.args.length) { err.args = command_obj.args; } - utils.reply_in_order(self, callback, err); + utils.reply_in_order(self, command_obj.callback, err); } else { debug('Queueing ' + command + ' for next server connection.'); self.offline_queue.push(command_obj); @@ -865,22 +858,23 @@ function handle_offline_command (self, command_obj) { // Do not call internal_send_command directly, if you are not absolutly certain it handles everything properly // e.g. monitor / info does not work with internal_send_command only -RedisClient.prototype.internal_send_command = function (command, args, callback, call_on_write) { - var arg, prefix_keys, command_obj; +RedisClient.prototype.internal_send_command = function (command_obj) { + var arg, prefix_keys; var i = 0; var command_str = ''; + var args = command_obj.args; + var command = command_obj.command; var len = args.length; var big_data = false; - var buffer_args = false; var args_copy = new Array(len); - if (process.domain && callback) { - callback = process.domain.bind(callback); + if (process.domain && command_obj.callback) { + command_obj.callback = process.domain.bind(command_obj.callback); } if (this.ready === false || this.stream.writable === false) { // Handle offline commands right away - handle_offline_command(this, new OfflineCommand(command, args, callback, call_on_write)); + handle_offline_command(this, command_obj); return false; // Indicate buffering } @@ -905,7 +899,7 @@ RedisClient.prototype.internal_send_command = function (command, args, callback, args_copy[i] = 'null'; // Backwards compatible :/ } else if (Buffer.isBuffer(args[i])) { args_copy[i] = args[i]; - buffer_args = true; + command_obj.buffer_args = true; big_data = true; } else { this.warn( @@ -927,8 +921,6 @@ RedisClient.prototype.internal_send_command = function (command, args, callback, args_copy[i] = '' + args[i]; } } - // Pass the original args to make sure in error cases the original arguments are returned - command_obj = new Command(command, args, buffer_args, callback); if (this.options.prefix) { prefix_keys = commands.getKeyIndexes(command, args_copy); @@ -967,8 +959,8 @@ RedisClient.prototype.internal_send_command = function (command, args, callback, debug('send_command: buffer send ' + arg.length + ' bytes'); } } - if (call_on_write) { - call_on_write(); + if (command_obj.call_on_write) { + command_obj.call_on_write(); } // Handle `CLIENT REPLY ON|OFF|SKIP` // This has to be checked after call_on_write @@ -978,8 +970,8 @@ RedisClient.prototype.internal_send_command = function (command, args, callback, } else { // Do not expect a reply // Does this work in combination with the pub sub mode? - if (callback) { - utils.reply_in_order(this, callback, null, undefined, this.command_queue); + if (command_obj.callback) { + utils.reply_in_order(this, command_obj.callback, null, undefined, this.command_queue); } if (this.reply === 'SKIP') { this.reply = 'SKIP_ONE_MORE'; diff --git a/lib/command.js b/lib/command.js index e63d338b5e7..6414d4d6fc5 100644 --- a/lib/command.js +++ b/lib/command.js @@ -1,22 +1,12 @@ 'use strict'; -// This Command constructor is ever so slightly faster than using an object literal, but more importantly, using -// a named constructor helps it show up meaningfully in the V8 CPU profiler and in heap snapshots. -function Command (command, args, buffer_args, callback) { - this.command = command; - this.args = args; - this.buffer_args = buffer_args; - this.callback = callback; -} -function OfflineCommand (command, args, callback, call_on_write) { +function Command (command, args, callback, call_on_write) { this.command = command; this.args = args; + this.buffer_args = false; this.callback = callback; this.call_on_write = call_on_write; } -module.exports = { - Command: Command, - OfflineCommand: OfflineCommand -}; +module.exports = Command; diff --git a/lib/commands.js b/lib/commands.js index fc20ff8165e..805c3b9a500 100644 --- a/lib/commands.js +++ b/lib/commands.js @@ -3,6 +3,7 @@ var commands = require('redis-commands'); var Multi = require('./multi'); var RedisClient = require('../').RedisClient; +var Command = require('./command'); // TODO: Rewrite this including the invidual commands into a Commands class // that provided a functionality to add new commands to the client @@ -42,7 +43,7 @@ commands.list.forEach(function (command) { arr[i] = arguments[i]; } } - return this.internal_send_command(command, arr, callback); + return this.internal_send_command(new Command(command, arr, callback)); }; Object.defineProperty(RedisClient.prototype[command], 'name', { value: command @@ -82,7 +83,7 @@ commands.list.forEach(function (command) { arr[i] = arguments[i]; } } - this.queue.push([command, arr, callback]); + this.queue.push(new Command(command, arr, callback)); return this; }; Object.defineProperty(Multi.prototype[command], 'name', { diff --git a/lib/extendedApi.js b/lib/extendedApi.js index ece77d22fe2..0e9589775be 100644 --- a/lib/extendedApi.js +++ b/lib/extendedApi.js @@ -3,6 +3,7 @@ var utils = require('./utils'); var debug = require('./debug'); var RedisClient = require('../').RedisClient; +var Command = require('./command'); var noop = function () {}; /********************************************** @@ -36,7 +37,7 @@ RedisClient.prototype.send_command = RedisClient.prototype.sendCommand = functio // but this might change from time to time and at the moment there's no good way to distinguishe them // from each other, so let's just do it do it this way for the time being if (command === 'multi' || typeof this[command] !== 'function') { - return this.internal_send_command(command, args, callback); + return this.internal_send_command(new Command(command, args, callback)); } if (typeof callback === 'function') { args = args.concat([callback]); // Prevent manipulating the input array diff --git a/lib/individualCommands.js b/lib/individualCommands.js index d676b0ce7a9..b7306c84d43 100644 --- a/lib/individualCommands.js +++ b/lib/individualCommands.js @@ -3,6 +3,7 @@ var utils = require('./utils'); var debug = require('./debug'); var Multi = require('./multi'); +var Command = require('./command'); var no_password_is_set = /no password is set/; var loading = /LOADING/; var RedisClient = require('../').RedisClient; @@ -42,33 +43,34 @@ function select_callback (self, db, callback) { } RedisClient.prototype.select = RedisClient.prototype.SELECT = function select (db, callback) { - return this.internal_send_command('select', [db], select_callback(this, db, callback)); + return this.internal_send_command(new Command('select', [db], select_callback(this, db, callback))); }; Multi.prototype.select = Multi.prototype.SELECT = function select (db, callback) { - this.queue.push(['select', [db], select_callback(this._client, db, callback)]); + this.queue.push(new Command('select', [db], select_callback(this._client, db, callback))); return this; }; -function monitor_callback (self, callback) { - return function (err, res) { - if (err === null) { - self.monitoring = true; - } - utils.callback_or_emit(self, callback, err, res); - }; -} - RedisClient.prototype.monitor = RedisClient.prototype.MONITOR = function monitor (callback) { // Use a individual command, as this is a special case that does not has to be checked for any other command - return this.internal_send_command('monitor', [], monitor_callback(this, callback)); + var self = this; + var call_on_write = function () { + // Activating monitor mode has to happen before Redis returned the callback, + // as the client could receive monitoring commands before the callback returned through a race condition + self.monitoring = true; + }; + return this.internal_send_command(new Command('monitor', [], callback, call_on_write)); }; // Only works with batch, not in a transaction Multi.prototype.monitor = Multi.prototype.MONITOR = function monitor (callback) { // Use a individual command, as this is a special case that does not has to be checked for any other command if (this.exec !== this.exec_transaction) { - this.queue.push(['monitor', [], monitor_callback(this._client, callback)]); + var self = this; + var call_on_write = function () { + self._client.monitoring = true; + }; + this.queue.push(new Command('monitor', [], callback, call_on_write)); return this; } // Set multi monitoring to indicate the exec that it should abort @@ -100,7 +102,7 @@ RedisClient.prototype.QUIT = RedisClient.prototype.quit = function quit (callbac // TODO: Consider this for v.3 // Allow the quit command to be fired as soon as possible to prevent it landing in the offline queue. // this.ready = this.offline_queue.length === 0; - var backpressure_indicator = this.internal_send_command('quit', [], quit_callback(this, callback)); + var backpressure_indicator = this.internal_send_command(new Command('quit', [], quit_callback(this, callback))); // Calling quit should always end the connection, no matter if there's a connection or not this.closing = true; this.ready = false; @@ -115,7 +117,7 @@ Multi.prototype.QUIT = Multi.prototype.quit = function quit (callback) { self.closing = true; self.ready = false; }; - this.queue.push(['quit', [], quit_callback(self, callback), call_on_write]); + this.queue.push(new Command('quit', [], quit_callback(self, callback), call_on_write)); return this; }; @@ -164,7 +166,7 @@ RedisClient.prototype.info = RedisClient.prototype.INFO = function info (section } else if (section !== undefined) { args = Array.isArray(section) ? section : [section]; } - return this.internal_send_command('info', args, info_callback(this, callback)); + return this.internal_send_command(new Command('info', args, info_callback(this, callback))); }; Multi.prototype.info = Multi.prototype.INFO = function info (section, callback) { @@ -174,7 +176,7 @@ Multi.prototype.info = Multi.prototype.INFO = function info (section, callback) } else if (section !== undefined) { args = Array.isArray(section) ? section : [section]; } - this.queue.push(['info', args, info_callback(this._client, callback)]); + this.queue.push(new Command('info', args, info_callback(this._client, callback))); return this; }; @@ -205,7 +207,7 @@ RedisClient.prototype.auth = RedisClient.prototype.AUTH = function auth (pass, c this.auth_pass = pass; var ready = this.ready; this.ready = ready || this.offline_queue.length === 0; - var tmp = this.internal_send_command('auth', [pass], auth_callback(this, pass, callback)); + var tmp = this.internal_send_command(new Command('auth', [pass], auth_callback(this, pass, callback))); this.ready = ready; return tmp; }; @@ -216,7 +218,7 @@ Multi.prototype.auth = Multi.prototype.AUTH = function auth (pass, callback) { // Stash auth for connect and reconnect. this.auth_pass = pass; - this.queue.push(['auth', [pass], auth_callback(this._client, callback)]); + this.queue.push(new Command('auth', [pass], auth_callback(this._client, callback))); return this; }; @@ -262,7 +264,7 @@ RedisClient.prototype.client = RedisClient.prototype.CLIENT = function client () }; } } - return this.internal_send_command('client', arr, callback, call_on_write); + return this.internal_send_command(new Command('client', arr, callback, call_on_write)); }; Multi.prototype.client = Multi.prototype.CLIENT = function client () { @@ -307,7 +309,7 @@ Multi.prototype.client = Multi.prototype.CLIENT = function client () { }; } } - this.queue.push(['client', arr, callback, call_on_write]); + this.queue.push(new Command('client', arr, callback, call_on_write)); return this; }; @@ -347,7 +349,7 @@ RedisClient.prototype.hmset = RedisClient.prototype.HMSET = function hmset () { arr[i] = arguments[i]; } } - return this.internal_send_command('hmset', arr, callback); + return this.internal_send_command(new Command('hmset', arr, callback)); }; Multi.prototype.hmset = Multi.prototype.HMSET = function hmset () { @@ -386,7 +388,7 @@ Multi.prototype.hmset = Multi.prototype.HMSET = function hmset () { arr[i] = arguments[i]; } } - this.queue.push(['hmset', arr, callback]); + this.queue.push(new Command('hmset', arr, callback)); return this; }; @@ -414,7 +416,7 @@ RedisClient.prototype.subscribe = RedisClient.prototype.SUBSCRIBE = function sub var call_on_write = function () { self.pub_sub_mode = self.pub_sub_mode || self.command_queue.length + 1; }; - return this.internal_send_command('subscribe', arr, callback, call_on_write); + return this.internal_send_command(new Command('subscribe', arr, callback, call_on_write)); }; Multi.prototype.subscribe = Multi.prototype.SUBSCRIBE = function subscribe () { @@ -441,7 +443,7 @@ Multi.prototype.subscribe = Multi.prototype.SUBSCRIBE = function subscribe () { var call_on_write = function () { self.pub_sub_mode = self.pub_sub_mode || self.command_queue.length + 1; }; - this.queue.push(['subscribe', arr, callback, call_on_write]); + this.queue.push(new Command('subscribe', arr, callback, call_on_write)); return this; }; @@ -470,7 +472,7 @@ RedisClient.prototype.unsubscribe = RedisClient.prototype.UNSUBSCRIBE = function // Pub sub has to be activated even if not in pub sub mode, as the return value is manipulated in the callback self.pub_sub_mode = self.pub_sub_mode || self.command_queue.length + 1; }; - return this.internal_send_command('unsubscribe', arr, callback, call_on_write); + return this.internal_send_command(new Command('unsubscribe', arr, callback, call_on_write)); }; Multi.prototype.unsubscribe = Multi.prototype.UNSUBSCRIBE = function unsubscribe () { @@ -498,7 +500,7 @@ Multi.prototype.unsubscribe = Multi.prototype.UNSUBSCRIBE = function unsubscribe // Pub sub has to be activated even if not in pub sub mode, as the return value is manipulated in the callback self.pub_sub_mode = self.pub_sub_mode || self.command_queue.length + 1; }; - this.queue.push(['unsubscribe', arr, callback, call_on_write]); + this.queue.push(new Command('unsubscribe', arr, callback, call_on_write)); return this; }; @@ -526,7 +528,7 @@ RedisClient.prototype.psubscribe = RedisClient.prototype.PSUBSCRIBE = function p var call_on_write = function () { self.pub_sub_mode = self.pub_sub_mode || self.command_queue.length + 1; }; - return this.internal_send_command('psubscribe', arr, callback, call_on_write); + return this.internal_send_command(new Command('psubscribe', arr, callback, call_on_write)); }; Multi.prototype.psubscribe = Multi.prototype.PSUBSCRIBE = function psubscribe () { @@ -553,7 +555,7 @@ Multi.prototype.psubscribe = Multi.prototype.PSUBSCRIBE = function psubscribe () var call_on_write = function () { self.pub_sub_mode = self.pub_sub_mode || self.command_queue.length + 1; }; - this.queue.push(['psubscribe', arr, callback, call_on_write]); + this.queue.push(new Command('psubscribe', arr, callback, call_on_write)); return this; }; @@ -582,7 +584,7 @@ RedisClient.prototype.punsubscribe = RedisClient.prototype.PUNSUBSCRIBE = functi // Pub sub has to be activated even if not in pub sub mode, as the return value is manipulated in the callback self.pub_sub_mode = self.pub_sub_mode || self.command_queue.length + 1; }; - return this.internal_send_command('punsubscribe', arr, callback, call_on_write); + return this.internal_send_command(new Command('punsubscribe', arr, callback, call_on_write)); }; Multi.prototype.punsubscribe = Multi.prototype.PUNSUBSCRIBE = function punsubscribe () { @@ -610,6 +612,6 @@ Multi.prototype.punsubscribe = Multi.prototype.PUNSUBSCRIBE = function punsubscr // Pub sub has to be activated even if not in pub sub mode, as the return value is manipulated in the callback self.pub_sub_mode = self.pub_sub_mode || self.command_queue.length + 1; }; - this.queue.push(['punsubscribe', arr, callback, call_on_write]); + this.queue.push(new Command('punsubscribe', arr, callback, call_on_write)); return this; }; diff --git a/lib/multi.js b/lib/multi.js index 433c45bd501..f80526b5c29 100644 --- a/lib/multi.js +++ b/lib/multi.js @@ -2,6 +2,7 @@ var Queue = require('double-ended-queue'); var utils = require('./utils'); +var Command = require('./command'); function Multi (client, args) { this._client = client; @@ -20,18 +21,23 @@ function Multi (client, args) { } } -function pipeline_transaction_command (self, command, args, index, cb, call_on_write) { +function pipeline_transaction_command (self, command_obj, index) { // Queueing is done first, then the commands are executed - self._client.send_command(command, args, function (err, reply) { + var tmp = command_obj.callback; + command_obj.callback = function (err, reply) { // Ignore the multi command. This is applied by node_redis and the user does not benefit by it if (err && index !== -1) { - if (cb) { - cb(err); + if (tmp) { + tmp(err); } err.position = index; self.errors.push(err); } - }); + // Keep track of who wants buffer responses: + // By the time the callback is called the command_obj got the buffer_args attribute attached + self.wants_buffers[index] = command_obj.buffer_args; + }; + self._client.internal_send_command(command_obj); } Multi.prototype.exec_atomic = Multi.prototype.EXEC_ATOMIC = Multi.prototype.execAtomic = function exec_atomic (callback) { @@ -42,7 +48,7 @@ Multi.prototype.exec_atomic = Multi.prototype.EXEC_ATOMIC = Multi.prototype.exec }; function multi_callback (self, err, replies) { - var i = 0, args; + var i = 0, command_obj; if (err) { err.errors = self.errors; @@ -56,22 +62,22 @@ function multi_callback (self, err, replies) { } if (replies) { - while (args = self.queue.shift()) { + while (command_obj = self.queue.shift()) { if (replies[i] instanceof Error) { var match = replies[i].message.match(utils.err_code); // LUA script could return user errors that don't behave like all other errors! if (match) { replies[i].code = match[1]; } - replies[i].command = args[0].toUpperCase(); - if (typeof args[2] === 'function') { - args[2](replies[i]); + replies[i].command = command_obj.command.toUpperCase(); + if (typeof command_obj.callback === 'function') { + command_obj.callback(replies[i]); } } else { // If we asked for strings, even in detect_buffers mode, then return strings: - replies[i] = self._client.handle_reply(replies[i], args[0], self.wants_buffers[i]); - if (typeof args[2] === 'function') { - args[2](null, replies[i]); + replies[i] = self._client.handle_reply(replies[i], command_obj.command, self.wants_buffers[i]); + if (typeof command_obj.callback === 'function') { + command_obj.callback(null, replies[i]); } } i++; @@ -98,30 +104,16 @@ Multi.prototype.exec_transaction = function exec_transaction (callback) { self.callback = callback; self._client.cork(); self.wants_buffers = new Array(len); - pipeline_transaction_command(self, 'multi', [], -1); + pipeline_transaction_command(self, new Command('multi', []), -1); // Drain queue, callback will catch 'QUEUED' or error for (var index = 0; index < len; index++) { // The commands may not be shifted off, since they are needed in the result handler - var command_obj = self.queue.get(index); - var command = command_obj[0]; - var cb = command_obj[2]; - var call_on_write = command_obj.length === 4 ? command_obj[3] : undefined; - // Keep track of who wants buffer responses: - if (self._client.options.detect_buffers) { - self.wants_buffers[index] = false; - for (var i = 0; i < command_obj[1].length; i += 1) { - if (command_obj[1][i] instanceof Buffer) { - self.wants_buffers[index] = true; - break; - } - } - } - pipeline_transaction_command(self, command, command_obj[1], index, cb, call_on_write); + pipeline_transaction_command(self, self.queue.get(index), index); } - self._client.internal_send_command('exec', [], function (err, replies) { + self._client.internal_send_command(new Command('exec', [], function (err, replies) { multi_callback(self, err, replies); - }); + })); self._client.uncork(); return !self._client.should_buffer; }; @@ -144,16 +136,17 @@ Multi.prototype.exec = Multi.prototype.EXEC = Multi.prototype.exec_batch = funct var len = self.queue.length; var index = 0; var command_obj; + if (len === 0) { + utils.reply_in_order(self._client, callback, null, []); + return !self._client.should_buffer; + } self._client.cork(); if (!callback) { while (command_obj = self.queue.shift()) { - self._client.internal_send_command(command_obj[0], command_obj[1], command_obj[2], (command_obj.length === 4 ? command_obj[3] : undefined)); + self._client.internal_send_command(command_obj); } self._client.uncork(); return !self._client.should_buffer; - } else if (len === 0) { - utils.reply_in_order(self._client, callback, null, []); - return !self._client.should_buffer; } var callback_without_own_cb = function (err, res) { if (err) { @@ -175,18 +168,15 @@ Multi.prototype.exec = Multi.prototype.EXEC = Multi.prototype.exec_batch = funct }; self.results = []; while (command_obj = self.queue.shift()) { - var command = command_obj[0]; - var call_on_write = command_obj.length === 4 ? command_obj[3] : undefined; - var cb; - if (typeof command_obj[2] === 'function') { - cb = batch_callback(self, command_obj[2], index); + if (typeof command_obj.callback === 'function') { + command_obj.callback = batch_callback(self, command_obj.callback, index); } else { - cb = callback_without_own_cb; + command_obj.callback = callback_without_own_cb; } if (typeof callback === 'function' && index === len - 1) { - cb = last_callback(cb); + command_obj.callback = last_callback(command_obj.callback); } - this._client.internal_send_command(command, command_obj[1], cb, call_on_write); + this._client.internal_send_command(command_obj); index++; } self._client.uncork(); diff --git a/test/commands/info.spec.js b/test/commands/info.spec.js index 3a67a1a178f..39a9e9f5cce 100644 --- a/test/commands/info.spec.js +++ b/test/commands/info.spec.js @@ -56,9 +56,9 @@ describe("The 'info' method", function () { it('check redis v.2.4 support', function (done) { var end = helper.callFuncAfter(done, 2); - client.internal_send_command = function (command, args, callback) { - assert.strictEqual(args.length, 0); - assert.strictEqual(command, 'info'); + client.internal_send_command = function (command_obj) { + assert.strictEqual(command_obj.args.length, 0); + assert.strictEqual(command_obj.command, 'info'); end(); }; client.info(); diff --git a/test/multi.spec.js b/test/multi.spec.js index 90107593789..1b13642a3e7 100644 --- a/test/multi.spec.js +++ b/test/multi.spec.js @@ -692,6 +692,11 @@ describe("The 'multi' method", function () { // subscribe => enters subscribe mode and this does not work in combination with exec (the same for psubscribe, unsubscribe...) // + // Make sure send_command is not called + client.send_command = function () { + throw new Error('failed'); + }; + assert.strictEqual(client.selected_db, undefined); var multi = client.multi(); multi.select(5, function (err, res) { diff --git a/test/node_redis.spec.js b/test/node_redis.spec.js index cb36b6c4af3..1c897116a6c 100644 --- a/test/node_redis.spec.js +++ b/test/node_redis.spec.js @@ -325,6 +325,7 @@ describe('The node_redis client', function () { bclient.blpop('blocking list 2', 5, function (err, value) { assert.strictEqual(value[0], 'blocking list 2'); assert.strictEqual(value[1], 'initial value'); + bclient.end(true); done(err); }); bclient.once('ready', function () { diff --git a/test/pubsub.spec.js b/test/pubsub.spec.js index 65fce290145..74d92c96e36 100644 --- a/test/pubsub.spec.js +++ b/test/pubsub.spec.js @@ -215,6 +215,7 @@ describe('publish/subscribe', function () { sub.stream.end(); }); + sub.select(3); sub.subscribe(channels); sub.on('ready', function (err, results) { From 25aa8f67105df65d38fdc1ac2c7d739233a07f97 Mon Sep 17 00:00:00 2001 From: Ruben Bridgewater Date: Fri, 27 May 2016 20:34:44 +0200 Subject: [PATCH 0660/1748] Fix monitoring mode not always activating soon enough --- lib/individualCommands.js | 4 ++-- test/node_redis.spec.js | 34 ++++++++++++++++++++++++++++++---- 2 files changed, 32 insertions(+), 6 deletions(-) diff --git a/lib/individualCommands.js b/lib/individualCommands.js index b7306c84d43..88fca126887 100644 --- a/lib/individualCommands.js +++ b/lib/individualCommands.js @@ -55,8 +55,8 @@ RedisClient.prototype.monitor = RedisClient.prototype.MONITOR = function monitor // Use a individual command, as this is a special case that does not has to be checked for any other command var self = this; var call_on_write = function () { - // Activating monitor mode has to happen before Redis returned the callback, - // as the client could receive monitoring commands before the callback returned through a race condition + // Activating monitor mode has to happen before Redis returned the callback. The monitor result is returned first. + // Therefore we expect the command to be properly processed. If this is not the case, it's not an issue either. self.monitoring = true; }; return this.internal_send_command(new Command('monitor', [], callback, call_on_write)); diff --git a/test/node_redis.spec.js b/test/node_redis.spec.js index 1c897116a6c..d9d06672347 100644 --- a/test/node_redis.spec.js +++ b/test/node_redis.spec.js @@ -336,6 +336,26 @@ describe('The node_redis client', function () { }); }); + it('should retry all commands even if the offline queue is disabled', function (done) { + var bclient = redis.createClient({ + parser: parser, + enableOfflineQueue: false, + retryUnfulfilledCommands: true + }); + bclient.once('ready', function () { + bclient.blpop('blocking list 2', 5, function (err, value) { + assert.strictEqual(value[0], 'blocking list 2'); + assert.strictEqual(value[1], 'initial value'); + bclient.end(true); + done(err); + }); + setTimeout(function () { + bclient.stream.destroy(); + client.rpush('blocking list 2', 'initial value', helper.isNumber(1)); + }, 100); + }); + }); + }); describe('.end', function () { @@ -650,6 +670,7 @@ describe('The node_redis client', function () { }); monitorClient.on('monitor', function (time, args, rawOutput) { + assert.strictEqual(monitorClient.monitoring, true); responses.push(args); assert(utils.monitor_regex.test(rawOutput), rawOutput); if (responses.length === 6) { @@ -670,6 +691,7 @@ describe('The node_redis client', function () { }); monitorClient.MONITOR(function (err, res) { + assert.strictEqual(monitorClient.monitoring, true); assert.strictEqual(res.inspect(), new Buffer('OK').inspect()); client.mget('hello', new Buffer('world')); }); @@ -688,6 +710,7 @@ describe('The node_redis client', function () { client.MONITOR(helper.isString('OK')); client.mget('hello', 'world'); client.on('monitor', function (time, args, rawOutput) { + assert.strictEqual(client.monitoring, true); assert(utils.monitor_regex.test(rawOutput), rawOutput); assert.deepEqual(args, ['mget', 'hello', 'world']); if (i++ === 2) { @@ -708,6 +731,7 @@ describe('The node_redis client', function () { assert.deepEqual(res, ['OK', [null, null]]); }); client.on('monitor', function (time, args, rawOutput) { + assert.strictEqual(client.monitoring, true); assert(utils.monitor_regex.test(rawOutput), rawOutput); assert.deepEqual(args, ['mget', 'hello', 'world']); if (i++ === 2) { @@ -719,20 +743,22 @@ describe('The node_redis client', function () { }); }); - it('monitor does not activate if the command could not be processed properly', function (done) { + it('monitor activates even if the command could not be processed properly after a reconnect', function (done) { client.MONITOR(function (err, res) { assert.strictEqual(err.code, 'UNCERTAIN_STATE'); }); client.on('error', function (err) {}); // Ignore error here client.stream.destroy(); + var end = helper.callFuncAfter(done, 2); client.on('monitor', function (time, args, rawOutput) { - done(new Error('failed')); // Should not be activated + assert.strictEqual(client.monitoring, true); + end(); }); client.on('reconnecting', function () { client.get('foo', function (err, res) { assert(!err); - assert.strictEqual(client.monitoring, false); - setTimeout(done, 10); // The monitor command might be returned a tiny bit later + assert.strictEqual(client.monitoring, true); + end(); }); }); }); From ce44213d656da792644c0d7a093327d6ecce544a Mon Sep 17 00:00:00 2001 From: Ruben Bridgewater Date: Sat, 28 May 2016 14:57:31 +0200 Subject: [PATCH 0661/1748] A function name is only configurable from v8 >= v.4.3 --- changelog.md | 2 +- lib/commands.js | 28 ++++++++++++++++++++++------ 2 files changed, 23 insertions(+), 7 deletions(-) diff --git a/changelog.md b/changelog.md index 683d9d94c60..a416a04aa94 100644 --- a/changelog.md +++ b/changelog.md @@ -10,7 +10,7 @@ Features - Updated [redis-parser](https://github.com/NodeRedis/redis-parser) dependency ([changelog](https://github.com/NodeRedis/redis-parser/releases/tag/v.2.0.0)) - The JS parser is from now on the new default as it is a lot faster than the hiredis parser - This is no BC as there is no changed behavior for the user at all but just a performance improvement. Explicitly requireing the Hiredis parser is still possible. -- Added name property to all Redis functions +- Added name property to all Redis functions (Node.js >= 4.0) Bugfixes diff --git a/lib/commands.js b/lib/commands.js index 805c3b9a500..00823304711 100644 --- a/lib/commands.js +++ b/lib/commands.js @@ -4,6 +4,18 @@ var commands = require('redis-commands'); var Multi = require('./multi'); var RedisClient = require('../').RedisClient; var Command = require('./command'); +// Feature detect if a function may change it's name +var changeFunctionName = (function () { + var fn = function abc () {}; + try { + Object.defineProperty(fn, 'name', { + value: 'foobar' + }); + return true; + } catch (e) { + return false; + } +}()); // TODO: Rewrite this including the invidual commands into a Commands class // that provided a functionality to add new commands to the client @@ -45,9 +57,11 @@ commands.list.forEach(function (command) { } return this.internal_send_command(new Command(command, arr, callback)); }; - Object.defineProperty(RedisClient.prototype[command], 'name', { - value: command - }); + if (changeFunctionName) { + Object.defineProperty(RedisClient.prototype[command], 'name', { + value: command + }); + } } // Do not override existing functions @@ -86,8 +100,10 @@ commands.list.forEach(function (command) { this.queue.push(new Command(command, arr, callback)); return this; }; - Object.defineProperty(Multi.prototype[command], 'name', { - value: command - }); + if (changeFunctionName) { + Object.defineProperty(Multi.prototype[command], 'name', { + value: command + }); + } } }); From 2c6e1e0cc08cf7a1e24639bfe7f065c37a24eafe Mon Sep 17 00:00:00 2001 From: Ruben Bridgewater Date: Sun, 29 May 2016 01:24:26 +0200 Subject: [PATCH 0662/1748] Improve error stacks in development and debug mode --- README.md | 29 +++++++++++++++++++++++++++++ changelog.md | 1 + index.js | 6 ++++++ lib/command.js | 5 +++++ 4 files changed, 41 insertions(+) diff --git a/README.md b/README.md index f1680c65414..0b5a554a92e 100644 --- a/README.md +++ b/README.md @@ -757,6 +757,35 @@ clients: 1, NodeJS: 6.2.0, Redis: 3.2.0, parser: javascript, connected by: tcp To get debug output run your `node_redis` application with `NODE_DEBUG=redis`. +This is also going to result in good stack traces opposed to useless ones otherwise for any async operation. +If you only want to have good stack traces but not the debug output run your application in development mode instead (`NODE_ENV=development`). + +Good stack traces are only activated in development and debug mode as this results in a significant performance penalty. + +___Comparison___: +Useless stack trace: +``` +ReplyError: ERR wrong number of arguments for 'set' command + at parseError (/home/ruben/repos/redis/node_modules/redis-parser/lib/parser.js:158:12) + at parseType (/home/ruben/repos/redis/node_modules/redis-parser/lib/parser.js:219:14) +``` +Good stack trace: +``` +ReplyError: ERR wrong number of arguments for 'set' command + at new Command (/home/ruben/repos/redis/lib/command.js:9:902) + at RedisClient.set (/home/ruben/repos/redis/lib/commands.js:9:3238) + at Context. (/home/ruben/repos/redis/test/good_stacks.spec.js:20:20) + at callFnAsync (/home/ruben/repos/redis/node_modules/mocha/lib/runnable.js:349:8) + at Test.Runnable.run (/home/ruben/repos/redis/node_modules/mocha/lib/runnable.js:301:7) + at Runner.runTest (/home/ruben/repos/redis/node_modules/mocha/lib/runner.js:422:10) + at /home/ruben/repos/redis/node_modules/mocha/lib/runner.js:528:12 + at next (/home/ruben/repos/redis/node_modules/mocha/lib/runner.js:342:14) + at /home/ruben/repos/redis/node_modules/mocha/lib/runner.js:352:7 + at next (/home/ruben/repos/redis/node_modules/mocha/lib/runner.js:284:14) + at Immediate._onImmediate (/home/ruben/repos/redis/node_modules/mocha/lib/runner.js:320:5) + at processImmediate [as _immediateCallback] (timers.js:383:17) +``` + ## How to Contribute - Open a pull request or an issue about what you want to implement / change. We're glad for any help! - Please be aware that we'll only accept fully tested code. diff --git a/changelog.md b/changelog.md index a416a04aa94..a8fdd30fdac 100644 --- a/changelog.md +++ b/changelog.md @@ -11,6 +11,7 @@ Features - The JS parser is from now on the new default as it is a lot faster than the hiredis parser - This is no BC as there is no changed behavior for the user at all but just a performance improvement. Explicitly requireing the Hiredis parser is still possible. - Added name property to all Redis functions (Node.js >= 4.0) +- Improved stack traces in development and debug mode Bugfixes diff --git a/index.js b/index.js index 84c9b644272..cd4edb1a5dd 100644 --- a/index.js +++ b/index.js @@ -348,6 +348,9 @@ RedisClient.prototype.flush_and_error = function (error_attributes, options) { // Don't flush everything from the queue for (var command_obj = this[queue_names[i]].shift(); command_obj; command_obj = this[queue_names[i]].shift()) { var err = new errorClasses.AbortError(error_attributes); + if (command_obj.error) { + err.stack = err.stack + command_obj.error.stack.replace(/^Error.*?\n/, '\n'); + } err.command = command_obj.command.toUpperCase(); if (command_obj.args && command_obj.args.length) { err.args = command_obj.args; @@ -675,6 +678,9 @@ RedisClient.prototype.connection_gone = function (why, error) { RedisClient.prototype.return_error = function (err) { var command_obj = this.command_queue.shift(); + if (command_obj.error) { + err.stack = command_obj.error.stack.replace(/^Error.*?\n/, 'ReplyError: ' + err.message + '\n'); + } err.command = command_obj.command.toUpperCase(); if (command_obj.args && command_obj.args.length) { err.args = command_obj.args; diff --git a/lib/command.js b/lib/command.js index 6414d4d6fc5..1c14b2d41f7 100644 --- a/lib/command.js +++ b/lib/command.js @@ -1,5 +1,7 @@ 'use strict'; +var debug_mode = require('../').debug_mode; +var betterStackTraces = process.env.NODE_ENV && process.env.NODE_ENV.toUpperCase() === 'DEVELOPMENT' || debug_mode; function Command (command, args, callback, call_on_write) { this.command = command; @@ -7,6 +9,9 @@ function Command (command, args, callback, call_on_write) { this.buffer_args = false; this.callback = callback; this.call_on_write = call_on_write; + if (betterStackTraces) { + this.error = new Error(); + } } module.exports = Command; From b3e89fee312de6df2aef1dfdfe63dec259457285 Mon Sep 17 00:00:00 2001 From: Ruben Bridgewater Date: Sun, 29 May 2016 01:39:51 +0200 Subject: [PATCH 0663/1748] Support Node.js 6 --- .travis.yml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.travis.yml b/.travis.yml index c87df9f781b..11a502e3ee0 100644 --- a/.travis.yml +++ b/.travis.yml @@ -12,5 +12,5 @@ node_js: - "0.10" - "0.12" - "4" - - "5" + - "6" after_success: npm run coveralls From c1f7755142ad95e0f6b18a28f714709b66c6e657 Mon Sep 17 00:00:00 2001 From: Ruben Bridgewater Date: Tue, 31 May 2016 15:11:18 +0200 Subject: [PATCH 0664/1748] Keep monitoring mode if once activated and use internal function for select and monitor while connecting --- index.js | 22 ++++++++-------------- 1 file changed, 8 insertions(+), 14 deletions(-) diff --git a/index.js b/index.js index cd4edb1a5dd..a196394ae2b 100644 --- a/index.js +++ b/index.js @@ -4,6 +4,7 @@ var net = require('net'); var tls = require('tls'); var util = require('util'); var utils = require('./lib/utils'); +var Command = require('./lib/command'); var Queue = require('double-ended-queue'); var errorClasses = require('./lib/customErrors'); var EventEmitter = require('events'); @@ -154,7 +155,6 @@ function RedisClient (options, stream) { this.times_connected = 0; this.buffers = options.return_buffers || options.detect_buffers; this.options = options; - this.old_state = {}; this.reply = 'ON'; // Returning replies is the default // Init parser this.reply_parser = create_parser(this); @@ -445,10 +445,10 @@ RedisClient.prototype.on_ready = function () { // Restore modal commands from previous connection. The order of the commands is important if (this.selected_db !== undefined) { - this.select(this.selected_db); + this.internal_send_command(new Command('select', [this.selected_db])); } - if (this.old_state.monitoring) { // Monitor has to be fired before pub sub commands - this.monitor(); + if (this.monitoring) { // Monitor has to be fired before pub sub commands + this.internal_send_command(new Command('monitor', [])); } var callback_count = Object.keys(this.subscription_set).length; if (!this.options.disable_resubscribing && callback_count) { @@ -571,12 +571,6 @@ RedisClient.prototype.connection_gone = function (why, error) { this.cork = noop; this.uncork = noop; this.pipeline = false; - - var state = { - monitoring: this.monitoring - }; - this.old_state = state; - this.monitoring = false; this.pub_sub_mode = 0; // since we are collapsing end and close, users don't expect to be called twice @@ -874,16 +868,16 @@ RedisClient.prototype.internal_send_command = function (command_obj) { var big_data = false; var args_copy = new Array(len); - if (process.domain && command_obj.callback) { - command_obj.callback = process.domain.bind(command_obj.callback); - } - if (this.ready === false || this.stream.writable === false) { // Handle offline commands right away handle_offline_command(this, command_obj); return false; // Indicate buffering } + if (process.domain && command_obj.callback) { + command_obj.callback = process.domain.bind(command_obj.callback); + } + for (i = 0; i < len; i += 1) { if (typeof args[i] === 'string') { // 30000 seemed to be a good value to switch to buffers after testing and checking the pros and cons From a0c7431787618ee8d734c4571729e2f219141e3f Mon Sep 17 00:00:00 2001 From: Ruben Bridgewater Date: Tue, 31 May 2016 15:11:57 +0200 Subject: [PATCH 0665/1748] Inherit the name property in the error classes --- lib/customErrors.js | 21 +++++++++++---------- test/custom_errors.spec.js | 8 ++++---- 2 files changed, 15 insertions(+), 14 deletions(-) diff --git a/lib/customErrors.js b/lib/customErrors.js index 0b192f78df3..bdf2feda0c0 100644 --- a/lib/customErrors.js +++ b/lib/customErrors.js @@ -4,11 +4,6 @@ var util = require('util'); function AbortError (obj) { Error.captureStackTrace(this, this.constructor); - Object.defineProperty(this, 'name', { - value: 'AbortError', - configurable: true, - writable: true - }); Object.defineProperty(this, 'message', { value: obj.message || '', configurable: true, @@ -21,11 +16,6 @@ function AbortError (obj) { function AggregateError (obj) { Error.captureStackTrace(this, this.constructor); - Object.defineProperty(this, 'name', { - value: 'AggregateError', - configurable: true, - writable: true - }); Object.defineProperty(this, 'message', { value: obj.message || '', configurable: true, @@ -39,6 +29,17 @@ function AggregateError (obj) { util.inherits(AbortError, Error); util.inherits(AggregateError, AbortError); +Object.defineProperty(AbortError.prototype, 'name', { + value: 'AbortError', + // configurable: true, + writable: true +}); +Object.defineProperty(AggregateError.prototype, 'name', { + value: 'AggregateError', + // configurable: true, + writable: true +}); + module.exports = { AbortError: AbortError, AggregateError: AggregateError diff --git a/test/custom_errors.spec.js b/test/custom_errors.spec.js index d7c6ebb6022..90a5aaeb66a 100644 --- a/test/custom_errors.spec.js +++ b/test/custom_errors.spec.js @@ -24,11 +24,11 @@ describe('errors', function () { assert.strictEqual(e.message, 'hello world'); assert.strictEqual(e.name, 'weird'); assert.strictEqual(e.property, true); - assert.strictEqual(Object.keys(e).length, 1); + assert.strictEqual(Object.keys(e).length, 2); assert(e instanceof Error); assert(e instanceof errors.AbortError); assert(delete e.name); - assert.strictEqual(e.name, 'Error'); + assert.strictEqual(e.name, 'AbortError'); }); it('should change name and message', function () { @@ -65,12 +65,12 @@ describe('errors', function () { assert.strictEqual(e.message, 'hello world'); assert.strictEqual(e.name, 'weird'); assert.strictEqual(e.property, true); - assert.strictEqual(Object.keys(e).length, 1); + assert.strictEqual(Object.keys(e).length, 2); assert(e instanceof Error); assert(e instanceof errors.AggregateError); assert(e instanceof errors.AbortError); assert(delete e.name); - assert.strictEqual(e.name, 'Error'); + assert.strictEqual(e.name, 'AggregateError'); }); it('should change name and message', function () { From 579584d62965987c6420086e8b2d7258907e7cff Mon Sep 17 00:00:00 2001 From: Ruben Bridgewater Date: Tue, 31 May 2016 15:57:04 +0200 Subject: [PATCH 0666/1748] Test Node.js 6 on appyevor --- appveyor.yml | 7 +++---- 1 file changed, 3 insertions(+), 4 deletions(-) diff --git a/appveyor.yml b/appveyor.yml index 1d5c6cdbfd1..9e79508e4c7 100644 --- a/appveyor.yml +++ b/appveyor.yml @@ -6,7 +6,7 @@ environment: - nodejs_version: "0.10" - nodejs_version: "0.12" - nodejs_version: "4" - - nodejs_version: "5" + - nodejs_version: "6" pull_requests: do_not_increment_build_number: true @@ -21,11 +21,10 @@ install: - redis-64\tools\redis-server.exe --service-install - redis-64\tools\redis-server.exe --service-start - '@ECHO Redis Started' - # Get the latest stable version of Node 0.STABLE.latest + # Get the required Node version - ps: Install-Product node $env:nodejs_version - # Typical npm stuff. Use msvs 2013 for the hiredis parser + # Typical npm stuff - npm install - - npm install hiredis --msvs_version=2013 # Post-install test scripts. test_script: From a41cfa9aaea788d6af878bbc44faf848cd40c3dd Mon Sep 17 00:00:00 2001 From: Ruben Bridgewater Date: Wed, 1 Jun 2016 14:04:25 +0200 Subject: [PATCH 0667/1748] Add good stack traces tests and fix stack traces in debug mode --- lib/command.js | 3 +- test/good_traces.spec.js | 59 ++++++++++++++++++++++++++++++++++++++++ test/lib/good-traces.js | 20 ++++++++++++++ test/node_redis.spec.js | 2 +- 4 files changed, 81 insertions(+), 3 deletions(-) create mode 100644 test/good_traces.spec.js create mode 100644 test/lib/good-traces.js diff --git a/lib/command.js b/lib/command.js index 1c14b2d41f7..717115c82eb 100644 --- a/lib/command.js +++ b/lib/command.js @@ -1,7 +1,6 @@ 'use strict'; -var debug_mode = require('../').debug_mode; -var betterStackTraces = process.env.NODE_ENV && process.env.NODE_ENV.toUpperCase() === 'DEVELOPMENT' || debug_mode; +var betterStackTraces = /development/i.test(process.env.NODE_ENV) || /\bredis\b/i.test(process.env.NODE_DEBUG); function Command (command, args, callback, call_on_write) { this.command = command; diff --git a/test/good_traces.spec.js b/test/good_traces.spec.js new file mode 100644 index 00000000000..d8759b0f9a1 --- /dev/null +++ b/test/good_traces.spec.js @@ -0,0 +1,59 @@ +'use strict'; + +var assert = require('assert'); +var config = require('./lib/config'); +var fork = require('child_process').fork; +var redis = config.redis; + +describe('stack traces', function () { + + it('should return good traces with NODE_ENV=development set', function (done) { + var external = fork('./test/lib/good-traces.js', { + env: { + NODE_ENV: 'development' + } + }); + + var id = setTimeout(function () { + external.kill(); + done(new Error('Timeout')); + }, 6000); + + external.on('close', function (code) { + clearTimeout(id); + assert.strictEqual(code, 0); + done(); + }); + }); + + it('should return good traces with NODE_DEBUG=redis env set', function (done) { + var external = fork('./test/lib/good-traces.js', { + env: { + NODE_DEBUG: 'redis' + }, + silent: true + }); + + var id = setTimeout(function () { + external.kill(); + done(new Error('Timeout')); + }, 6000); + + external.on('close', function (code) { + clearTimeout(id); + assert.strictEqual(code, 0); + done(); + }); + }); + + // This is always going to return good stack traces + it('should always return good stack traces for rejected offline commands', function (done) { + var client = redis.createClient({ + enable_offline_queue: false + }); + client.set('foo', function (err, res) { + assert(/good_traces.spec.js/.test(err.stack)); + client.quit(done); + }); + }); +}); diff --git a/test/lib/good-traces.js b/test/lib/good-traces.js new file mode 100644 index 00000000000..c583da2ae27 --- /dev/null +++ b/test/lib/good-traces.js @@ -0,0 +1,20 @@ +// Spawned by the good_stacks.spec.js tests +'use strict'; + +var assert = require('assert'); +var redis = require('../../index'); +var client = redis.createClient(); + +// Both error cases would normally return bad stack traces +client.set('foo', function (err, res) { + assert(/good-traces.js:9:8/.test(err.stack)); + client.set('foo', 'bar', function (err, res) { + assert(/good-traces.js:11:12/.test(err.stack)); + client.quit(function () { + process.exit(0); + }); + }); + process.nextTick(function () { + client.stream.destroy(); + }); +}); diff --git a/test/node_redis.spec.js b/test/node_redis.spec.js index d9d06672347..6c3376c7f3e 100644 --- a/test/node_redis.spec.js +++ b/test/node_redis.spec.js @@ -851,7 +851,7 @@ describe('The node_redis client', function () { var id = setTimeout(function () { external.kill(); - done(Error('unref subprocess timed out')); + done(new Error('unref subprocess timed out')); }, 8000); external.on('close', function (code) { From dffc27d83f8360c863429c7fd7b907916b52446d Mon Sep 17 00:00:00 2001 From: Ruben Bridgewater Date: Wed, 1 Jun 2016 16:24:26 +0200 Subject: [PATCH 0668/1748] v.2.6.0 --- changelog.md | 2 +- package.json | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/changelog.md b/changelog.md index a8fdd30fdac..f1563616f55 100644 --- a/changelog.md +++ b/changelog.md @@ -1,7 +1,7 @@ Changelog ========= -## v.2.6.0 - 25 Mai, 2016 +## v.2.6.0 - 01 Jun, 2016 In addition to the pre-releases the following changes exist in v.2.6.0: diff --git a/package.json b/package.json index 82e7f2bdde8..0b526584337 100644 --- a/package.json +++ b/package.json @@ -1,6 +1,6 @@ { "name": "redis", - "version": "2.6.0-2", + "version": "2.6.0", "description": "Redis client library", "keywords": [ "database", From 68ca5c760b4bd6f46201820277e3720b975d55c1 Mon Sep 17 00:00:00 2001 From: Dan MacTough Date: Wed, 1 Jun 2016 13:15:00 -0400 Subject: [PATCH 0669/1748] Ensure synthetic function names conform to naming requirements The "restore-asking" function name is not valid and was causing co-redis (by way of its usage of thenify) to throw because thenify uses the function name to rewrite async functions with promises. This PR will change the name of the "restore-asking" function to "restore_asking", which is valid. This sanitation is a bit stricter than necessary, since it also sanitizes valid unicode characters, but it covers this module's potential use cases just fine. --- lib/commands.js | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/lib/commands.js b/lib/commands.js index 00823304711..a64773ebf6d 100644 --- a/lib/commands.js +++ b/lib/commands.js @@ -21,6 +21,7 @@ var changeFunctionName = (function () { // that provided a functionality to add new commands to the client commands.list.forEach(function (command) { + var commandName = command.replace(/(?:^([0-9])|[^a-zA-Z0-9_$])/g, '_$1'); // Do not override existing functions if (!RedisClient.prototype[command]) { @@ -59,7 +60,7 @@ commands.list.forEach(function (command) { }; if (changeFunctionName) { Object.defineProperty(RedisClient.prototype[command], 'name', { - value: command + value: commandName }); } } @@ -102,7 +103,7 @@ commands.list.forEach(function (command) { }; if (changeFunctionName) { Object.defineProperty(Multi.prototype[command], 'name', { - value: command + value: commandName }); } } From 7eaba8c10ddc419f35d5e68595b4eddfa44a943b Mon Sep 17 00:00:00 2001 From: Ruben Bridgewater Date: Thu, 2 Jun 2016 00:52:19 +0200 Subject: [PATCH 0670/1748] Add tests to make sure no invalid function names get exported --- changelog.md | 6 ++++++ lib/commands.js | 3 +++ test/node_redis.spec.js | 11 ++++++++++- 3 files changed, 19 insertions(+), 1 deletion(-) diff --git a/changelog.md b/changelog.md index f1563616f55..5f2b175e3d5 100644 --- a/changelog.md +++ b/changelog.md @@ -1,6 +1,12 @@ Changelog ========= +## v.2.6.1 - 02 Jun, 2016 + +Bugfixes + +- Fixed invalid function name being exported + ## v.2.6.0 - 01 Jun, 2016 In addition to the pre-releases the following changes exist in v.2.6.0: diff --git a/lib/commands.js b/lib/commands.js index a64773ebf6d..6ca01df2c79 100644 --- a/lib/commands.js +++ b/lib/commands.js @@ -21,6 +21,9 @@ var changeFunctionName = (function () { // that provided a functionality to add new commands to the client commands.list.forEach(function (command) { + + // Some rare Redis commands use special characters in their command name + // Convert those to a underscore to prevent using invalid function names var commandName = command.replace(/(?:^([0-9])|[^a-zA-Z0-9_$])/g, '_$1'); // Do not override existing functions diff --git a/test/node_redis.spec.js b/test/node_redis.spec.js index 6c3376c7f3e..8f4a53c11b6 100644 --- a/test/node_redis.spec.js +++ b/test/node_redis.spec.js @@ -9,6 +9,7 @@ var helper = require('./helper'); var utils = require('../lib/utils'); var fork = require('child_process').fork; var redis = config.redis; +var client; describe('The node_redis client', function () { @@ -29,10 +30,18 @@ describe('The node_redis client', function () { }); }); + it('convert minus to underscore in Redis function names', function (done) { + var names = Object.keys(redis.RedisClient.prototype); + client = redis.createClient(); + for (var i = 0; i < names.length; i++) { + assert(/^([a-zA-Z_][a-zA-Z_0-9]*)?$/.test(client[names[i]].name)); + } + client.quit(done); + }); + helper.allTests(function (parser, ip, args) { describe('using ' + parser + ' and ' + ip, function () { - var client; afterEach(function () { client.end(true); From 2543e11b29c4b9042666582401fe48b60d01f9ec Mon Sep 17 00:00:00 2001 From: Ruben Bridgewater Date: Thu, 2 Jun 2016 00:53:07 +0200 Subject: [PATCH 0671/1748] v.2.6.1 --- package.json | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/package.json b/package.json index 0b526584337..cce6ffaac9a 100644 --- a/package.json +++ b/package.json @@ -1,6 +1,6 @@ { "name": "redis", - "version": "2.6.0", + "version": "2.6.1", "description": "Redis client library", "keywords": [ "database", From ed57f440c792e0d3fe7151358596b76a0f659751 Mon Sep 17 00:00:00 2001 From: Michael Diarmid Date: Thu, 2 Jun 2016 00:35:29 +0100 Subject: [PATCH 0672/1748] fix broken links --- changelog.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/changelog.md b/changelog.md index 5f2b175e3d5..c1a4cdf8a15 100644 --- a/changelog.md +++ b/changelog.md @@ -13,7 +13,7 @@ In addition to the pre-releases the following changes exist in v.2.6.0: Features -- Updated [redis-parser](https://github.com/NodeRedis/redis-parser) dependency ([changelog](https://github.com/NodeRedis/redis-parser/releases/tag/v.2.0.0)) +- Updated [redis-parser](https://github.com/NodeRedis/node-redis-parser) dependency ([changelog](https://github.com/NodeRedis/node-redis-parser/releases/tag/v.2.0.0)) - The JS parser is from now on the new default as it is a lot faster than the hiredis parser - This is no BC as there is no changed behavior for the user at all but just a performance improvement. Explicitly requireing the Hiredis parser is still possible. - Added name property to all Redis functions (Node.js >= 4.0) From fc2c0dd78f439e1c6675e4d37c5ca9761df644a5 Mon Sep 17 00:00:00 2001 From: Brian Rossmajer Date: Sat, 4 Jun 2016 23:05:01 -0400 Subject: [PATCH 0673/1748] Update README.md Removing the negative example --- README.md | 19 +++++++------------ 1 file changed, 7 insertions(+), 12 deletions(-) diff --git a/README.md b/README.md index 5e3980605d9..69769857124 100644 --- a/README.md +++ b/README.md @@ -618,13 +618,15 @@ the second word as first parameter: Duplicate all current options and return a new redisClient instance. All options passed to the duplicate function are going to replace the original option. -An example of when to use duplicate() would be to accomodate the connection- +One example of when to use duplicate() would be to accomodate the connection- blocking redis commands BRPOP, BLPOP, and BRPOPLPUSH. If these commands are used on the same redisClient instance as non-blocking commands, the non-blocking ones may be queued up until after the blocking ones finish. var Redis=require('redis'); var client = Redis.createClient(); + var clientBlocking = client.duplicate(); + var get = function() { console.log("get called"); client.get("any_key",function() { console.log("get returned"); }); @@ -632,7 +634,7 @@ non-blocking ones may be queued up until after the blocking ones finish. }; var brpop = function() { console.log("brpop called"); - client.brpop("nonexistent", 5, function() { + clientBlocking.brpop("nonexistent", 5, function() { console.log("brpop return"); setTimeout( brpop, 1000 ); }); @@ -640,16 +642,9 @@ non-blocking ones may be queued up until after the blocking ones finish. get(); brpop(); -These two repeating functions will interfere with each other -- the `get`s will -not return until after the `brpop` returns. This can be fixed by keeping the -blocking calls separate using `client.duplicate()`, eg: - - ... - var clientBlocking = client.duplicate(); - var brpop = function() { - console.log("brpop called"); - clientBlocking.brpop( ... - +Another reason to use duplicate() is when multiple DBs on the same server are +accessed via the redis SELECT command. Each DB could use its own connection. + ## client.send_command(command_name[, [args][, callback]]) All Redis commands have been added to the `client` object. However, if new commands are introduced before this library is updated, From a5b5ccbba331ce22ecba02219c4c3a3a0509e620 Mon Sep 17 00:00:00 2001 From: Austin Winslow Date: Tue, 14 Jun 2016 13:24:20 -0500 Subject: [PATCH 0674/1748] Noting in docs that retry_max_delay and connect_timeout are deprecated --- README.md | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/README.md b/README.md index 9d5f87a4b4a..42390422977 100644 --- a/README.md +++ b/README.md @@ -189,8 +189,8 @@ __Tip:__ If the Redis server runs on the same machine as the client consider usi | socket_keepalive | true | If set to `true`, the keep-alive functionality is enabled on the underlying socket. | | no_ready_check | false | When a connection is established to the Redis server, the server might still be loading the database from disk. While loading, the server will not respond to any commands. To work around this, `node_redis` has a "ready check" which sends the `INFO` command to the server. The response from the `INFO` command indicates whether the server is ready for more commands. When ready, `node_redis` emits a `ready` event. Setting `no_ready_check` to `true` will inhibit this check. | | enable_offline_queue | true | By default, if there is no active connection to the Redis server, commands are added to a queue and are executed once the connection has been established. Setting `enable_offline_queue` to `false` will disable this feature and the callback will be executed immediately with an error, or an error will be emitted if no callback is specified. | -| retry_max_delay | null | By default, every time the client tries to connect and fails, the reconnection delay almost doubles. This delay normally grows infinitely, but setting `retry_max_delay` limits it to the maximum value provided in milliseconds. | -| connect_timeout | 3600000 | Setting `connect_timeout` limits the total time for the client to connect and reconnect. The value is provided in milliseconds and is counted from the moment a new client is created or from the time the connection is lost. The last retry is going to happen exactly at the timeout time. Default is to try connecting until the default system socket timeout has been exceeded and to try reconnecting until 1h has elapsed. | +| retry_max_delay | null | __Deprecated__ _Please use `retry_strategy` instead._ By default, every time the client tries to connect and fails, the reconnection delay almost doubles. This delay normally grows infinitely, but setting `retry_max_delay` limits it to the maximum value provided in milliseconds. | +| connect_timeout | 3600000 | __Deprecated__ _Please use `retry_strategy` instead._ Setting `connect_timeout` limits the total time for the client to connect and reconnect. The value is provided in milliseconds and is counted from the moment a new client is created or from the time the connection is lost. The last retry is going to happen exactly at the timeout time. Default is to try connecting until the default system socket timeout has been exceeded and to try reconnecting until 1h has elapsed. | | max_attempts | 0 | __Deprecated__ _Please use `retry_strategy` instead._ By default, a client will try reconnecting until connected. Setting `max_attempts` limits total amount of connection attempts. Setting this to 1 will prevent any reconnect attempt. | | retry_unfulfilled_commands | false | If set to `true`, all commands that were unfulfilled while the connection is lost will be retried after the connection has been reestablished. Use this with caution if you use state altering commands (e.g. `incr`). This is especially useful if you use blocking commands. | | password | null | If set, client will run Redis auth command on connect. Alias `auth_pass` __Note__ `node_redis` < 2.5 must use `auth_pass` | From de0a9628aabb9daecdccc898d10b375ab1b98566 Mon Sep 17 00:00:00 2001 From: Ruben Bridgewater Date: Thu, 16 Jun 2016 12:03:47 +0200 Subject: [PATCH 0675/1748] Fire the individual original callbacks when using transactions Fixes #1089 --- changelog.md | 6 ++++++ lib/multi.js | 1 + test/multi.spec.js | 3 +++ 3 files changed, 10 insertions(+) diff --git a/changelog.md b/changelog.md index c1a4cdf8a15..afeb2239667 100644 --- a/changelog.md +++ b/changelog.md @@ -1,6 +1,12 @@ Changelog ========= +## v.2.6.2 - 16 Jun, 2016 + +Bugfixes + +- Fixed individual callbacks of a transaction not being called (2.6.0 regression) + ## v.2.6.1 - 02 Jun, 2016 Bugfixes diff --git a/lib/multi.js b/lib/multi.js index f80526b5c29..63f5d210856 100644 --- a/lib/multi.js +++ b/lib/multi.js @@ -36,6 +36,7 @@ function pipeline_transaction_command (self, command_obj, index) { // Keep track of who wants buffer responses: // By the time the callback is called the command_obj got the buffer_args attribute attached self.wants_buffers[index] = command_obj.buffer_args; + command_obj.callback = tmp; }; self._client.internal_send_command(command_obj); } diff --git a/test/multi.spec.js b/test/multi.spec.js index 1b13642a3e7..1f2bbb1b818 100644 --- a/test/multi.spec.js +++ b/test/multi.spec.js @@ -393,6 +393,7 @@ describe("The 'multi' method", function () { var arr2 = ['some manner of key', 'otherTypes']; var arr3 = [5768, 'multibarx', 'multifoox']; var arr4 = ['mset', [578, 'multibar'], helper.isString('OK')]; + var called = false; client.multi([ arr4, [['mset', 'multifoo2', 'multibar2', 'multifoo3', 'multibar3'], helper.isString('OK')], @@ -411,8 +412,10 @@ describe("The 'multi' method", function () { .mget('multifoo2', ['multifoo3', 'multifoo'], function (err, res) { assert(res[0], 'multifoo3'); assert(res[1], 'multifoo'); + called = true; }) .exec(function (err, replies) { + assert(called); assert.equal(arr.length, 3); assert.equal(arr2.length, 2); assert.equal(arr3.length, 3); From 01f723a0dd2ff4a5c453b4283f4c0e7ee9fbdeba Mon Sep 17 00:00:00 2001 From: Ruben Bridgewater Date: Thu, 16 Jun 2016 12:21:34 +0200 Subject: [PATCH 0676/1748] v.2.6.2 --- package.json | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/package.json b/package.json index cce6ffaac9a..69b3c74766e 100644 --- a/package.json +++ b/package.json @@ -1,6 +1,6 @@ { "name": "redis", - "version": "2.6.1", + "version": "2.6.2", "description": "Redis client library", "keywords": [ "database", From 0c6edbd8a6ca4dbdfb9f03e0bd0e513044c7160a Mon Sep 17 00:00:00 2001 From: Ruben Bridgewater Date: Thu, 16 Jun 2016 12:39:08 +0200 Subject: [PATCH 0677/1748] Make test idempotent --- test/multi.spec.js | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/test/multi.spec.js b/test/multi.spec.js index 1f2bbb1b818..e654375dc8d 100644 --- a/test/multi.spec.js +++ b/test/multi.spec.js @@ -717,7 +717,7 @@ describe("The 'multi' method", function () { multi.exec(function (err, res) { res[2] = res[2].substr(0, 10); assert.deepEqual(res, ['OK', 'OK', '# Server\r\n', 'bar']); - done(); + client.flushdb(done); }); }); From 0171935600f5c2e3ea4ffbe8f619882b175745a0 Mon Sep 17 00:00:00 2001 From: greenkeeperio-bot Date: Sat, 9 Jul 2016 09:23:57 +0200 Subject: [PATCH 0678/1748] chore(package): update nyc to version 7.0.0 https://greenkeeper.io/ --- package.json | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/package.json b/package.json index 69b3c74766e..275ab2fcfa2 100644 --- a/package.json +++ b/package.json @@ -39,7 +39,7 @@ "eslint": "^2.5.0", "metrics": "^0.1.9", "mocha": "^2.3.2", - "nyc": "^6.0.0", + "nyc": "^7.0.0", "tcp-port-used": "^0.1.2", "uuid": "^2.0.1", "win-spawn": "^2.0.0" From d3fedc648fd4b6f9575632e5d1983c2645280bca Mon Sep 17 00:00:00 2001 From: Greenkeeper Date: Fri, 16 Sep 2016 17:59:31 +0200 Subject: [PATCH 0679/1748] chore(package): update nyc to version 8.3.0 (#1143) https://greenkeeper.io/ --- package.json | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/package.json b/package.json index 275ab2fcfa2..09964dfaa4b 100644 --- a/package.json +++ b/package.json @@ -39,7 +39,7 @@ "eslint": "^2.5.0", "metrics": "^0.1.9", "mocha": "^2.3.2", - "nyc": "^7.0.0", + "nyc": "^8.3.0", "tcp-port-used": "^0.1.2", "uuid": "^2.0.1", "win-spawn": "^2.0.0" From 782f2553f92498d961e97cb9a6fdfc9a826b7b55 Mon Sep 17 00:00:00 2001 From: Ruben Bridgewater Date: Sat, 8 Oct 2016 15:39:58 +0200 Subject: [PATCH 0680/1748] README improvements highlight that all Redis commands are supported. --- README.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/README.md b/README.md index 42390422977..efadd392f2c 100644 --- a/README.md +++ b/README.md @@ -6,7 +6,7 @@ redis - a node.js redis client [![Windows Tests](https://img.shields.io/appveyor/ci/BridgeAR/node-redis/master.svg?label=Windows%20Tests)](https://ci.appveyor.com/project/BridgeAR/node-redis/branch/master) [![Gitter](https://badges.gitter.im/Join%20Chat.svg)](https://gitter.im/NodeRedis/node_redis?utm_source=badge&utm_medium=badge&utm_campaign=pr-badge) -This is a complete and feature rich Redis client for node.js. It supports all Redis commands and focuses on high performance. +This is a complete and feature rich Redis client for node.js. __It supports all Redis commands__ and focuses on high performance. Install with: From dff87d78f53cc3cddd6a2f386bb1cd0885998b16 Mon Sep 17 00:00:00 2001 From: greenkeeperio-bot Date: Tue, 11 Oct 2016 09:21:28 +0200 Subject: [PATCH 0681/1748] chore(package): update mocha to version 3.1.2 https://greenkeeper.io/ --- package.json | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/package.json b/package.json index 09964dfaa4b..f1073464bfe 100644 --- a/package.json +++ b/package.json @@ -38,7 +38,7 @@ "intercept-stdout": "~0.1.2", "eslint": "^2.5.0", "metrics": "^0.1.9", - "mocha": "^2.3.2", + "mocha": "^3.1.2", "nyc": "^8.3.0", "tcp-port-used": "^0.1.2", "uuid": "^2.0.1", From 41d26dc0c81b02799cf01eb37390b7c80829e7bb Mon Sep 17 00:00:00 2001 From: Ruben Bridgewater Date: Sun, 30 Oct 2016 21:29:35 +0100 Subject: [PATCH 0682/1748] Fix domain handling and tls camelCase settings Fixes #1106 Fixes #1103 Closes #1104 --- changelog.md | 7 +++++++ index.js | 8 ++++---- lib/utils.js | 4 ++++ test/node_redis.spec.js | 13 +++++++++++++ test/utils.spec.js | 6 +++++- 5 files changed, 33 insertions(+), 5 deletions(-) diff --git a/changelog.md b/changelog.md index afeb2239667..e90f258966e 100644 --- a/changelog.md +++ b/changelog.md @@ -1,6 +1,13 @@ Changelog ========= +## v.2.6.3 - 31 Oct, 2016 + +Bugfixes + +- Do not change the tls setting to camel_case +- Fix domain handling in combination with the offline queue (2.5.3 regression) + ## v.2.6.2 - 16 Jun, 2016 Bugfixes diff --git a/index.js b/index.js index a196394ae2b..2f9c2490128 100644 --- a/index.js +++ b/index.js @@ -868,16 +868,16 @@ RedisClient.prototype.internal_send_command = function (command_obj) { var big_data = false; var args_copy = new Array(len); + if (process.domain && command_obj.callback) { + command_obj.callback = process.domain.bind(command_obj.callback); + } + if (this.ready === false || this.stream.writable === false) { // Handle offline commands right away handle_offline_command(this, command_obj); return false; // Indicate buffering } - if (process.domain && command_obj.callback) { - command_obj.callback = process.domain.bind(command_obj.callback); - } - for (i = 0; i < len; i += 1) { if (typeof args[i] === 'string') { // 30000 seemed to be a good value to switch to buffers after testing and checking the pros and cons diff --git a/lib/utils.js b/lib/utils.js index bc685b9a4f8..e44ed89ea6e 100644 --- a/lib/utils.js +++ b/lib/utils.js @@ -57,6 +57,10 @@ function clone (obj) { var elems = Object.keys(obj); var elem; while (elem = elems.pop()) { + if (elem === 'tls') { // special handle tls + copy[elem] = obj[elem]; + continue; + } // Accept camelCase options and convert them to snake_case var snake_case = elem.replace(/[A-Z][^A-Z]/g, '_$&').toLowerCase(); // If camelCase is detected, pass it to the client, so all variables are going to be camelCased diff --git a/test/node_redis.spec.js b/test/node_redis.spec.js index 8f4a53c11b6..e85b3fe347f 100644 --- a/test/node_redis.spec.js +++ b/test/node_redis.spec.js @@ -623,6 +623,19 @@ describe('The node_redis client', function () { }); }); + it('keeps the same domain by using the offline queue', function (done) { + client.end(true); + client = redis.createClient(); + var testDomain = require('domain').create(); + testDomain.run(function () { + client.set('FOOBAR', 'def', function () { + assert.strictEqual(process.domain, testDomain); + done(); + }); + }); + require('domain').create(); + }); + it('catches all errors from within the domain', function (done) { var domain = require('domain').create(); diff --git a/test/utils.spec.js b/test/utils.spec.js index f6fa8d4e7dd..e036d84167c 100644 --- a/test/utils.spec.js +++ b/test/utils.spec.js @@ -35,12 +35,16 @@ describe('utils.js', function () { retryStrategy: false, nested: { onlyContainCamelCaseOnce: true + }, + tls: { + rejectUnauthorized: true } }); - assert.strictEqual(Object.keys(a).length, 4); + assert.strictEqual(Object.keys(a).length, 5); assert.strictEqual(a.option_one_two, true); assert.strictEqual(a.retry_strategy, false); assert.strictEqual(a.camel_case, true); + assert.strictEqual(a.tls.rejectUnauthorized, true); assert.strictEqual(Object.keys(a.nested).length, 1); }); From 4e98cb944226485bf171703df3d8a3e4af8ac3e1 Mon Sep 17 00:00:00 2001 From: Ruben Bridgewater Date: Mon, 31 Oct 2016 20:49:06 +0100 Subject: [PATCH 0683/1748] Improve error message --- test/connection.spec.js | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/test/connection.spec.js b/test/connection.spec.js index 9661f42d3bd..f92dca1949b 100644 --- a/test/connection.spec.js +++ b/test/connection.spec.js @@ -371,7 +371,7 @@ describe('connection tests', function () { if (err.code === 'ENETUNREACH') { // The test is run without a internet connection. Pretent it works return done(); } - assert(/Redis connection in broken state: connection timeout.*?exceeded./.test(err.message)); + assert(/Redis connection in broken state: connection timeout.*?exceeded./.test(err.message), err.message); // The code execution on windows is very slow at times var add = process.platform !== 'win32' ? 15 : 200; var now = Date.now(); From bc35ed3172e73f3d33c7ace3155d6e95a7046265 Mon Sep 17 00:00:00 2001 From: Ruben Bridgewater Date: Mon, 31 Oct 2016 21:03:03 +0100 Subject: [PATCH 0684/1748] Add support for Node.js 7 --- .travis.yml | 1 + 1 file changed, 1 insertion(+) diff --git a/.travis.yml b/.travis.yml index 11a502e3ee0..eefd2354065 100644 --- a/.travis.yml +++ b/.travis.yml @@ -13,4 +13,5 @@ node_js: - "0.12" - "4" - "6" + - "7" after_success: npm run coveralls From be07c12fbd039b7b3b1cb5a71f06e24464bc0593 Mon Sep 17 00:00:00 2001 From: Ruben Bridgewater Date: Mon, 31 Oct 2016 21:09:08 +0100 Subject: [PATCH 0685/1748] v.2.6.3 --- package.json | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/package.json b/package.json index 09964dfaa4b..87b76576b8a 100644 --- a/package.json +++ b/package.json @@ -1,6 +1,6 @@ { "name": "redis", - "version": "2.6.2", + "version": "2.6.3", "description": "Redis client library", "keywords": [ "database", From dd3179a183e5a2ad4bbf56cac0ae6c0a9e0da45a Mon Sep 17 00:00:00 2001 From: "Kenji.Kato" Date: Sat, 5 Nov 2016 03:31:20 +0900 Subject: [PATCH 0686/1748] Check options.error MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit options.error it sometimes null. Example code should check it before reference options.error.code. (See also #1159 ) --- README.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/README.md b/README.md index 52b60f0f284..fcd9ef499ea 100644 --- a/README.md +++ b/README.md @@ -224,7 +224,7 @@ retry_strategy example ```js var client = redis.createClient({ retry_strategy: function (options) { - if (options.error.code === 'ECONNREFUSED') { + if (options.error && options.error.code === 'ECONNREFUSED') { // End reconnecting on a specific error and flush all commands with a individual error return new Error('The server refused the connection'); } From 18d5f604f583db773e5203aa7960b799aed959bc Mon Sep 17 00:00:00 2001 From: Mark Wubben Date: Thu, 10 Nov 2016 15:55:25 +0000 Subject: [PATCH 0687/1748] Update retry_strategy example Presumably `Math.min()` was intended, otherwise the `options.attempt * 100` result would be ignored for the first 30 attempts. IMHO the initial retries should be quick, but then the delay should be capped at a reasonable interval. --- README.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/README.md b/README.md index 52b60f0f284..71b6bc73a06 100644 --- a/README.md +++ b/README.md @@ -237,7 +237,7 @@ var client = redis.createClient({ return undefined; } // reconnect after - return Math.max(options.attempt * 100, 3000); + return Math.min(options.attempt * 100, 3000); } }); ``` From b9540d49650f6f28e63cc54d54a000e89a7db34e Mon Sep 17 00:00:00 2001 From: Ruben Bridgewater Date: Thu, 12 Jan 2017 20:00:55 +0100 Subject: [PATCH 0688/1748] Fix monitor mode not working with IPv6, sockets or lua scripts Fixes #1189 Fixes #1037 --- changelog.md | 6 + index.js | 6 +- lib/utils.js | 2 +- test/commands/monitor.spec.js | 212 ++++++++++++++++++++++++++++++++++ test/node_redis.spec.js | 181 ----------------------------- 5 files changed, 220 insertions(+), 187 deletions(-) create mode 100644 test/commands/monitor.spec.js diff --git a/changelog.md b/changelog.md index e90f258966e..add6d9e5ada 100644 --- a/changelog.md +++ b/changelog.md @@ -1,6 +1,12 @@ Changelog ========= +## v.2.6.4 - 12 Jan, 2017 + +Bugfixes + +- Fixed monitor mode not working in combination with IPv6, sockets or lua scripts (2.6.0 regression) + ## v.2.6.3 - 31 Oct, 2016 Bugfixes diff --git a/index.js b/index.js index 2f9c2490128..f81519d6011 100644 --- a/index.js +++ b/index.js @@ -791,9 +791,6 @@ function return_pub_sub (self, reply) { } RedisClient.prototype.return_reply = function (reply) { - // If in monitor mode, all normal commands are still working and we only want to emit the streamlined commands - // As this is not the average use case and monitor is expensive anyway, let's change the code here, to improve - // the average performance of all other commands in case of no monitor mode if (this.monitoring) { var replyStr; if (this.buffers && Buffer.isBuffer(reply)) { @@ -801,8 +798,7 @@ RedisClient.prototype.return_reply = function (reply) { } else { replyStr = reply; } - // While reconnecting the redis server does not recognize the client as in monitor mode anymore - // Therefore the monitor command has to finish before it catches further commands + // If in monitor mode, all normal commands are still working and we only want to emit the streamlined commands if (typeof replyStr === 'string' && utils.monitor_regex.test(replyStr)) { var timestamp = replyStr.slice(0, replyStr.indexOf(' ')); var args = replyStr.slice(replyStr.indexOf('"') + 1, -1).split('" "').map(function (elem) { diff --git a/lib/utils.js b/lib/utils.js index e44ed89ea6e..84f05119695 100644 --- a/lib/utils.js +++ b/lib/utils.js @@ -127,7 +127,7 @@ module.exports = { reply_to_object: replyToObject, print: print, err_code: /^([A-Z]+)\s+(.+)$/, - monitor_regex: /^[0-9]{10,11}\.[0-9]+ \[[0-9]{1,3} [0-9]{1,3}\.[0-9]{1,3}\.[0-9]{1,3}\.[0-9]{1,3}:[0-9]{1,5}\].*/, + monitor_regex: /^[0-9]{10,11}\.[0-9]+ \[[0-9]{1,3} (.(?!\]))+.\]( ".+?")+$/, clone: convenienceClone, callback_or_emit: callbackOrEmit, reply_in_order: replyInOrder diff --git a/test/commands/monitor.spec.js b/test/commands/monitor.spec.js new file mode 100644 index 00000000000..ec287ccba71 --- /dev/null +++ b/test/commands/monitor.spec.js @@ -0,0 +1,212 @@ +'use strict'; + +var assert = require('assert'); +var config = require('../lib/config'); +var helper = require('../helper'); +var utils = require('../../lib/utils'); +var redis = config.redis; + +describe("The 'monitor' method", function () { + + helper.allTests(function (parser, ip, args) { + + var client; + + afterEach(function () { + client.end(true); + }); + + beforeEach(function (done) { + client = redis.createClient.apply(null, args); + client.once('connect', function () { + client.flushdb(done); + }); + }); + + it('monitors commands on all redis clients and works in the correct order', function (done) { + var monitorClient = redis.createClient.apply(null, args); + var responses = [ + ['mget', 'some', 'keys', 'foo', 'bar'], + ['set', 'json', '{"foo":"123","bar":"sdflkdfsjk","another":false}'], + ['eval', "return redis.call('set', 'sha', 'test')", '0'], + ['set', 'sha', 'test'], + ['get', 'baz'], + ['set', 'foo', 'bar" "s are " " good!"'], + ['mget', 'foo', 'baz'], + ['subscribe', 'foo', 'baz'] + ]; + var end = helper.callFuncAfter(done, 5); + + monitorClient.set('foo', 'bar'); + monitorClient.flushdb(); + monitorClient.monitor(function (err, res) { + assert.strictEqual(res, 'OK'); + client.mget('some', 'keys', 'foo', 'bar'); + client.set('json', JSON.stringify({ + foo: '123', + bar: 'sdflkdfsjk', + another: false + })); + client.eval("return redis.call('set', 'sha', 'test')", 0); + monitorClient.get('baz', function (err, res) { + assert.strictEqual(res, null); + end(err); + }); + monitorClient.set('foo', 'bar" "s are " " good!"', function (err, res) { + assert.strictEqual(res, 'OK'); + end(err); + }); + monitorClient.mget('foo', 'baz', function (err, res) { + assert.strictEqual(res[0], 'bar" "s are " " good!"'); + assert.strictEqual(res[1], null); + end(err); + }); + monitorClient.subscribe('foo', 'baz', function (err, res) { + // The return value might change in v.3 + // assert.strictEqual(res, 'baz'); + // TODO: Fix the return value of subscribe calls + end(err); + }); + }); + + monitorClient.on('monitor', function (time, args, rawOutput) { + assert.strictEqual(monitorClient.monitoring, true); + assert.deepEqual(args, responses.shift()); + assert(utils.monitor_regex.test(rawOutput), rawOutput); + if (responses.length === 0) { + monitorClient.quit(end); + } + }); + }); + + it('monitors returns strings in the rawOutput even with return_buffers activated', function (done) { + var monitorClient = redis.createClient({ + return_buffers: true, + path: '/tmp/redis.sock' + }); + + monitorClient.MONITOR(function (err, res) { + assert.strictEqual(monitorClient.monitoring, true); + assert.strictEqual(res.inspect(), new Buffer('OK').inspect()); + monitorClient.mget('hello', new Buffer('world')); + }); + + monitorClient.on('monitor', function (time, args, rawOutput) { + assert.strictEqual(typeof rawOutput, 'string'); + assert(utils.monitor_regex.test(rawOutput), rawOutput); + assert.deepEqual(args, ['mget', 'hello', 'world']); + // Quit immediatly ends monitoring mode and therefore does not stream back the quit command + monitorClient.quit(done); + }); + }); + + it('monitors reconnects properly and works with the offline queue', function (done) { + var called = false; + client.MONITOR(helper.isString('OK')); + client.mget('hello', 'world'); + client.on('monitor', function (time, args, rawOutput) { + assert.strictEqual(client.monitoring, true); + assert(utils.monitor_regex.test(rawOutput), rawOutput); + assert.deepEqual(args, ['mget', 'hello', 'world']); + if (called) { + // End after a reconnect + return done(); + } + client.stream.destroy(); + client.mget('hello', 'world'); + called = true; + }); + }); + + it('monitors reconnects properly and works with the offline queue in a batch statement', function (done) { + var called = false; + var multi = client.batch(); + multi.MONITOR(helper.isString('OK')); + multi.mget('hello', 'world'); + multi.exec(function (err, res) { + assert.deepEqual(res, ['OK', [null, null]]); + }); + client.on('monitor', function (time, args, rawOutput) { + assert.strictEqual(client.monitoring, true); + assert(utils.monitor_regex.test(rawOutput), rawOutput); + assert.deepEqual(args, ['mget', 'hello', 'world']); + if (called) { + // End after a reconnect + return done(); + } + client.stream.destroy(); + client.mget('hello', 'world'); + called = true; + }); + }); + + it('monitor activates even if the command could not be processed properly after a reconnect', function (done) { + client.MONITOR(function (err, res) { + assert.strictEqual(err.code, 'UNCERTAIN_STATE'); + }); + client.on('error', function (err) {}); // Ignore error here + client.stream.destroy(); + var end = helper.callFuncAfter(done, 2); + client.on('monitor', function (time, args, rawOutput) { + assert.strictEqual(client.monitoring, true); + end(); + }); + client.on('reconnecting', function () { + client.get('foo', function (err, res) { + assert(!err); + assert.strictEqual(client.monitoring, true); + end(); + }); + }); + }); + + it('monitors works in combination with the pub sub mode and the offline queue', function (done) { + var responses = [ + ['subscribe', '/foo', '/bar'], + ['unsubscribe', '/bar'], + ['get', 'foo'], + ['subscribe', '/foo'], + ['subscribe', 'baz'], + ['unsubscribe', 'baz'], + ['publish', '/foo', 'hello world'] + ]; + var pub = redis.createClient(); + pub.on('ready', function () { + client.MONITOR(function (err, res) { + assert.strictEqual(res, 'OK'); + pub.get('foo', helper.isNull()); + }); + client.subscribe('/foo', '/bar'); + client.unsubscribe('/bar'); + setTimeout(function () { + client.stream.destroy(); + client.once('ready', function () { + pub.publish('/foo', 'hello world'); + }); + client.set('foo', 'bar', helper.isError()); + client.subscribe('baz'); + client.unsubscribe('baz'); + }, 150); + var called = false; + client.on('monitor', function (time, args, rawOutput) { + assert.deepEqual(args, responses.shift()); + assert(utils.monitor_regex.test(rawOutput), rawOutput); + if (responses.length === 0) { + // The publish is called right after the reconnect and the monitor is called before the message is emitted. + // Therefore we have to wait till the next tick + process.nextTick(function () { + assert(called); + client.quit(done); + pub.end(false); + }); + } + }); + client.on('message', function (channel, msg) { + assert.strictEqual(channel, '/foo'); + assert.strictEqual(msg, 'hello world'); + called = true; + }); + }); + }); + }); +}); diff --git a/test/node_redis.spec.js b/test/node_redis.spec.js index e85b3fe347f..e6e5f0987fd 100644 --- a/test/node_redis.spec.js +++ b/test/node_redis.spec.js @@ -6,7 +6,6 @@ var path = require('path'); var intercept = require('intercept-stdout'); var config = require('./lib/config'); var helper = require('./helper'); -var utils = require('../lib/utils'); var fork = require('child_process').fork; var redis = config.redis; var client; @@ -654,186 +653,6 @@ describe('The node_redis client', function () { }); }); - describe('monitor', function () { - it('monitors commands on all redis clients and works in the correct order', function (done) { - var monitorClient = redis.createClient.apply(null, args); - var responses = []; - var end = helper.callFuncAfter(done, 5); - - monitorClient.set('foo', 'bar'); - monitorClient.flushdb(); - monitorClient.monitor(function (err, res) { - assert.strictEqual(res, 'OK'); - client.mget('some', 'keys', 'foo', 'bar'); - client.set('json', JSON.stringify({ - foo: '123', - bar: 'sdflkdfsjk', - another: false - })); - monitorClient.get('baz', function (err, res) { - assert.strictEqual(res, null); - end(err); - }); - monitorClient.set('foo', 'bar" "s are " " good!"', function (err, res) { - assert.strictEqual(res, 'OK'); - end(err); - }); - monitorClient.mget('foo', 'baz', function (err, res) { - assert.strictEqual(res[0], 'bar" "s are " " good!"'); - assert.strictEqual(res[1], null); - end(err); - }); - monitorClient.subscribe('foo', 'baz', function (err, res) { - // The return value might change in v.3 - // assert.strictEqual(res, 'baz'); - // TODO: Fix the return value of subscribe calls - end(err); - }); - }); - - monitorClient.on('monitor', function (time, args, rawOutput) { - assert.strictEqual(monitorClient.monitoring, true); - responses.push(args); - assert(utils.monitor_regex.test(rawOutput), rawOutput); - if (responses.length === 6) { - assert.deepEqual(responses[0], ['mget', 'some', 'keys', 'foo', 'bar']); - assert.deepEqual(responses[1], ['set', 'json', '{"foo":"123","bar":"sdflkdfsjk","another":false}']); - assert.deepEqual(responses[2], ['get', 'baz']); - assert.deepEqual(responses[3], ['set', 'foo', 'bar" "s are " " good!"']); - assert.deepEqual(responses[4], ['mget', 'foo', 'baz']); - assert.deepEqual(responses[5], ['subscribe', 'foo', 'baz']); - monitorClient.quit(end); - } - }); - }); - - it('monitors returns strings in the rawOutput even with return_buffers activated', function (done) { - var monitorClient = redis.createClient({ - return_buffers: true - }); - - monitorClient.MONITOR(function (err, res) { - assert.strictEqual(monitorClient.monitoring, true); - assert.strictEqual(res.inspect(), new Buffer('OK').inspect()); - client.mget('hello', new Buffer('world')); - }); - - monitorClient.on('monitor', function (time, args, rawOutput) { - assert.strictEqual(typeof rawOutput, 'string'); - assert(utils.monitor_regex.test(rawOutput), rawOutput); - assert.deepEqual(args, ['mget', 'hello', 'world']); - // Quit immediatly ends monitoring mode and therefore does not stream back the quit command - monitorClient.quit(done); - }); - }); - - it('monitors reconnects properly and works with the offline queue', function (done) { - var i = 0; - client.MONITOR(helper.isString('OK')); - client.mget('hello', 'world'); - client.on('monitor', function (time, args, rawOutput) { - assert.strictEqual(client.monitoring, true); - assert(utils.monitor_regex.test(rawOutput), rawOutput); - assert.deepEqual(args, ['mget', 'hello', 'world']); - if (i++ === 2) { - // End after two reconnects - return done(); - } - client.stream.destroy(); - client.mget('hello', 'world'); - }); - }); - - it('monitors reconnects properly and works with the offline queue in a batch statement', function (done) { - var i = 0; - var multi = client.batch(); - multi.MONITOR(helper.isString('OK')); - multi.mget('hello', 'world'); - multi.exec(function (err, res) { - assert.deepEqual(res, ['OK', [null, null]]); - }); - client.on('monitor', function (time, args, rawOutput) { - assert.strictEqual(client.monitoring, true); - assert(utils.monitor_regex.test(rawOutput), rawOutput); - assert.deepEqual(args, ['mget', 'hello', 'world']); - if (i++ === 2) { - // End after two reconnects - return done(); - } - client.stream.destroy(); - client.mget('hello', 'world'); - }); - }); - - it('monitor activates even if the command could not be processed properly after a reconnect', function (done) { - client.MONITOR(function (err, res) { - assert.strictEqual(err.code, 'UNCERTAIN_STATE'); - }); - client.on('error', function (err) {}); // Ignore error here - client.stream.destroy(); - var end = helper.callFuncAfter(done, 2); - client.on('monitor', function (time, args, rawOutput) { - assert.strictEqual(client.monitoring, true); - end(); - }); - client.on('reconnecting', function () { - client.get('foo', function (err, res) { - assert(!err); - assert.strictEqual(client.monitoring, true); - end(); - }); - }); - }); - - it('monitors works in combination with the pub sub mode and the offline queue', function (done) { - var responses = []; - var pub = redis.createClient(); - pub.on('ready', function () { - client.MONITOR(function (err, res) { - assert.strictEqual(res, 'OK'); - pub.get('foo', helper.isNull()); - }); - client.subscribe('/foo', '/bar'); - client.unsubscribe('/bar'); - setTimeout(function () { - client.stream.destroy(); - client.once('ready', function () { - pub.publish('/foo', 'hello world'); - }); - client.set('foo', 'bar', helper.isError()); - client.subscribe('baz'); - client.unsubscribe('baz'); - }, 150); - var called = false; - client.on('monitor', function (time, args, rawOutput) { - responses.push(args); - assert(utils.monitor_regex.test(rawOutput), rawOutput); - if (responses.length === 7) { - assert.deepEqual(responses[0], ['subscribe', '/foo', '/bar']); - assert.deepEqual(responses[1], ['unsubscribe', '/bar']); - assert.deepEqual(responses[2], ['get', 'foo']); - assert.deepEqual(responses[3], ['subscribe', '/foo']); - assert.deepEqual(responses[4], ['subscribe', 'baz']); - assert.deepEqual(responses[5], ['unsubscribe', 'baz']); - assert.deepEqual(responses[6], ['publish', '/foo', 'hello world']); - // The publish is called right after the reconnect and the monitor is called before the message is emitted. - // Therefore we have to wait till the next tick - process.nextTick(function () { - assert(called); - client.quit(done); - pub.end(false); - }); - } - }); - client.on('message', function (channel, msg) { - assert.strictEqual(channel, '/foo'); - assert.strictEqual(msg, 'hello world'); - called = true; - }); - }); - }); - }); - describe('idle', function () { it('emits idle as soon as there are no outstanding commands', function (done) { var end = helper.callFuncAfter(done, 2); From 1051719dda0678acd56539796ab8626d82e6510b Mon Sep 17 00:00:00 2001 From: Ruben Bridgewater Date: Thu, 12 Jan 2017 23:50:37 +0100 Subject: [PATCH 0689/1748] Update eslint --- package.json | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/package.json b/package.json index 87b76576b8a..2006519a027 100644 --- a/package.json +++ b/package.json @@ -21,7 +21,7 @@ "coverage": "nyc report --reporter=html", "benchmark": "node benchmarks/multi_bench.js", "test": "nyc --cache mocha ./test/*.js ./test/commands/*.js --timeout=8000", - "posttest": "eslint . --fix && npm run coverage", + "lint": "eslint . --fix && npm run coverage", "compare": "node benchmarks/diff_multi_bench_output.js beforeBench.txt afterBench.txt" }, "dependencies": { @@ -36,7 +36,7 @@ "bluebird": "^3.0.2", "coveralls": "^2.11.2", "intercept-stdout": "~0.1.2", - "eslint": "^2.5.0", + "eslint": "^3.5.0", "metrics": "^0.1.9", "mocha": "^2.3.2", "nyc": "^8.3.0", From 670b774101d644e3c754551976fd0f13cf8d75eb Mon Sep 17 00:00:00 2001 From: Ruben Bridgewater Date: Thu, 12 Jan 2017 23:51:00 +0100 Subject: [PATCH 0690/1748] v.2.6.4 --- package.json | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/package.json b/package.json index 2006519a027..d860421171f 100644 --- a/package.json +++ b/package.json @@ -1,6 +1,6 @@ { "name": "redis", - "version": "2.6.3", + "version": "2.6.4", "description": "Redis client library", "keywords": [ "database", From dffa8a6aee4ec39679aee584a50da485807e8ae5 Mon Sep 17 00:00:00 2001 From: Ruben Bridgewater Date: Sun, 15 Jan 2017 12:53:09 +0100 Subject: [PATCH 0691/1748] Fix parser being reset in case (p)message_buffer is attached without the parser set to return buffers. This might result in corrupt data if the listener is attached while the parser holds partial data. --- changelog.md | 6 +++ index.js | 10 ++++- test/pubsub.spec.js | 92 +++++++++++++++++++++++++-------------------- 3 files changed, 66 insertions(+), 42 deletions(-) diff --git a/changelog.md b/changelog.md index add6d9e5ada..3b0e7b8cfc8 100644 --- a/changelog.md +++ b/changelog.md @@ -1,6 +1,12 @@ Changelog ========= +## v.2.6.5 - 15 Jan, 2017 + +Bugfixes + +- Fixed parser reset if (p)message_buffer listener is attached + ## v.2.6.4 - 12 Jan, 2017 Bugfixes diff --git a/index.js b/index.js index f81519d6011..1b094f90b6f 100644 --- a/index.js +++ b/index.js @@ -171,10 +171,16 @@ function RedisClient (options, stream) { 'The drain event listener is deprecated and will be removed in v.3.0.0.\n' + 'If you want to keep on listening to this event please listen to the stream drain event directly.' ); - } else if (event === 'message_buffer' || event === 'pmessage_buffer' || event === 'messageBuffer' || event === 'pmessageBuffer' && !this.buffers) { + } else if ((event === 'message_buffer' || event === 'pmessage_buffer' || event === 'messageBuffer' || event === 'pmessageBuffer') && !this.buffers && !this.message_buffers) { + if (this.reply_parser.name !== 'javascript') { + return this.warn( + 'You attached the ' + event + ' without the hiredis parser without the returnBuffers option set to true.\n' + + 'Please use the JavaScript parser or set the returnBuffers option to true to return buffers.' + ); + } + this.reply_parser.optionReturnBuffers = true; this.message_buffers = true; this.handle_reply = handle_detect_buffers_reply; - this.reply_parser = create_parser(this); } }); } diff --git a/test/pubsub.spec.js b/test/pubsub.spec.js index 74d92c96e36..94b0fc1880c 100644 --- a/test/pubsub.spec.js +++ b/test/pubsub.spec.js @@ -508,52 +508,64 @@ describe('publish/subscribe', function () { }); it('allows to listen to pmessageBuffer and pmessage', function (done) { - var batch = sub.batch(); var end = helper.callFuncAfter(done, 6); - assert.strictEqual(sub.message_buffers, false); - batch.psubscribe('*'); - batch.subscribe('/foo'); - batch.unsubscribe('/foo'); - batch.unsubscribe(helper.isNull()); - batch.subscribe(['/foo'], helper.isString('/foo')); - batch.exec(); - assert.strictEqual(sub.shouldBuffer, false); - sub.on('pmessageBuffer', function (pattern, channel, message) { - assert.strictEqual(pattern.inspect(), new Buffer('*').inspect()); - assert.strictEqual(channel.inspect(), new Buffer('/foo').inspect()); - sub.quit(end); - }); - // Either message_buffers or buffers has to be true, but not both at the same time - assert.notStrictEqual(sub.message_buffers, sub.buffers); - sub.on('pmessage', function (pattern, channel, message) { - assert.strictEqual(pattern, '*'); - assert.strictEqual(channel, '/foo'); - assert.strictEqual(message, 'hello world'); - end(); - }); - sub.on('message', function (channel, message) { - assert.strictEqual(channel, '/foo'); - assert.strictEqual(message, 'hello world'); - end(); - }); - setTimeout(function () { - pub.pubsub('numsub', '/foo', function (err, res) { - // There's one subscriber to this channel - assert.deepEqual(res, ['/foo', 1]); - end(); + var data = Array(10000).join('äüs^öéÉÉ`e'); + sub.set('foo', data, function () { + sub.get('foo'); + sub.stream.once('data', function () { + assert.strictEqual(sub.message_buffers, false); + assert.strictEqual(sub.shouldBuffer, false); + sub.on('pmessageBuffer', function (pattern, channel, message) { + if (parser !== 'javascript' && typeof pattern === 'string') { + pattern = new Buffer(pattern); + channel = new Buffer(channel); + } + assert.strictEqual(pattern.inspect(), new Buffer('*').inspect()); + assert.strictEqual(channel.inspect(), new Buffer('/foo').inspect()); + sub.quit(end); + }); + if (parser === 'javascript') { + assert.notStrictEqual(sub.message_buffers, sub.buffers); + } + }); - pub.pubsub('channels', function (err, res) { - // There's exactly one channel that is listened too - assert.deepEqual(res, ['/foo']); + var batch = sub.batch(); + batch.psubscribe('*'); + batch.subscribe('/foo'); + batch.unsubscribe('/foo'); + batch.unsubscribe(helper.isNull()); + batch.subscribe(['/foo'], helper.isString('/foo')); + batch.exec(function () { + pub.pubsub('numsub', '/foo', function (err, res) { + // There's one subscriber to this channel + assert.deepEqual(res, ['/foo', 1]); + end(); + }); + pub.pubsub('channels', function (err, res) { + // There's exactly one channel that is listened too + assert.deepEqual(res, ['/foo']); + end(); + }); + pub.pubsub('numpat', function (err, res) { + // One pattern is active + assert.strictEqual(res, 1); + end(); + }); + pub.publish('/foo', 'hello world', helper.isNumber(2)); + }); + // Either message_buffers or buffers has to be true, but not both at the same time + sub.on('pmessage', function (pattern, channel, message) { + assert.strictEqual(pattern, '*'); + assert.strictEqual(channel, '/foo'); + assert.strictEqual(message, 'hello world'); end(); }); - pub.pubsub('numpat', function (err, res) { - // One pattern is active - assert.strictEqual(res, 1); + sub.on('message', function (channel, message) { + assert.strictEqual(channel, '/foo'); + assert.strictEqual(message, 'hello world'); end(); }); - pub.publish('/foo', 'hello world', helper.isNumber(2)); - }, 50); + }); }); }); From db0e8c53cc7a68e65b591dc2e457b5a7e7f5065b Mon Sep 17 00:00:00 2001 From: Ruben Bridgewater Date: Sun, 15 Jan 2017 13:09:15 +0100 Subject: [PATCH 0692/1748] Fixed parser not being reset in case the redis connection closed ASAP for overcoming of output buffer limits. Fixes #1190 --- changelog.md | 1 + index.js | 5 +++-- test/node_redis.spec.js | 18 ++++++++++++++++++ 3 files changed, 22 insertions(+), 2 deletions(-) diff --git a/changelog.md b/changelog.md index 3b0e7b8cfc8..ee7b4c9d232 100644 --- a/changelog.md +++ b/changelog.md @@ -5,6 +5,7 @@ Changelog Bugfixes +- Fixed parser not being reset in case the redis connection closed ASAP for overcoming of output buffer limits - Fixed parser reset if (p)message_buffer listener is attached ## v.2.6.4 - 12 Jan, 2017 diff --git a/index.js b/index.js index 1b094f90b6f..f587bea577c 100644 --- a/index.js +++ b/index.js @@ -156,8 +156,6 @@ function RedisClient (options, stream) { this.buffers = options.return_buffers || options.detect_buffers; this.options = options; this.reply = 'ON'; // Returning replies is the default - // Init parser - this.reply_parser = create_parser(this); this.create_stream(); // The listeners will not be attached right away, so let's print the deprecation message while the listener is attached this.on('newListener', function (event) { @@ -230,6 +228,9 @@ function create_parser (self) { RedisClient.prototype.create_stream = function () { var self = this; + // Init parser + this.reply_parser = create_parser(this); + if (this.options.stream) { // Only add the listeners once in case of a reconnect try (that won't work) if (this.stream) { diff --git a/test/node_redis.spec.js b/test/node_redis.spec.js index e6e5f0987fd..64be131bba4 100644 --- a/test/node_redis.spec.js +++ b/test/node_redis.spec.js @@ -38,6 +38,24 @@ describe('The node_redis client', function () { client.quit(done); }); + it('reset the parser while reconnecting', function (done) { + var client = redis.createClient({ + retryStrategy: function () { + return 5; + } + }); + client.once('reconnecting', function () { + process.nextTick(function () { + assert.strictEqual(client.reply_parser.buffer, null); + done(); + }); + }); + var partialInput = new Buffer('$100\r\nabcdef'); + client.reply_parser.execute(partialInput); + assert.strictEqual(client.reply_parser.buffer.inspect(), partialInput.inspect()); + client.stream.destroy(); + }); + helper.allTests(function (parser, ip, args) { describe('using ' + parser + ' and ' + ip, function () { From 1f730a803ae2e6482b21ca7cd1382d380831be08 Mon Sep 17 00:00:00 2001 From: Ruben Bridgewater Date: Sun, 15 Jan 2017 13:23:38 +0100 Subject: [PATCH 0693/1748] v.2.6.5 --- package.json | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/package.json b/package.json index d860421171f..2a613a66e2a 100644 --- a/package.json +++ b/package.json @@ -1,6 +1,6 @@ { "name": "redis", - "version": "2.6.4", + "version": "2.6.5", "description": "Redis client library", "keywords": [ "database", From 476e554fcd4f61e8dbb2d2b077322697fa893afb Mon Sep 17 00:00:00 2001 From: Ruben Bridgewater Date: Mon, 16 Jan 2017 09:47:30 +0100 Subject: [PATCH 0694/1748] Fix typo --- index.js | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/index.js b/index.js index f587bea577c..798b78954bb 100644 --- a/index.js +++ b/index.js @@ -172,7 +172,7 @@ function RedisClient (options, stream) { } else if ((event === 'message_buffer' || event === 'pmessage_buffer' || event === 'messageBuffer' || event === 'pmessageBuffer') && !this.buffers && !this.message_buffers) { if (this.reply_parser.name !== 'javascript') { return this.warn( - 'You attached the ' + event + ' without the hiredis parser without the returnBuffers option set to true.\n' + + 'You attached the "' + event + '" listener without the returnBuffers option set to true.\n' + 'Please use the JavaScript parser or set the returnBuffers option to true to return buffers.' ); } From 8688986051d97a7974d7dd1c008ed86106f888a6 Mon Sep 17 00:00:00 2001 From: Ruben Bridgewater Date: Mon, 16 Jan 2017 09:47:37 +0100 Subject: [PATCH 0695/1748] Add issue to corresponding spec --- test/node_redis.spec.js | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/test/node_redis.spec.js b/test/node_redis.spec.js index 64be131bba4..dfb744354b1 100644 --- a/test/node_redis.spec.js +++ b/test/node_redis.spec.js @@ -38,7 +38,7 @@ describe('The node_redis client', function () { client.quit(done); }); - it('reset the parser while reconnecting', function (done) { + it('reset the parser while reconnecting (See #1190)', function (done) { var client = redis.createClient({ retryStrategy: function () { return 5; From c4b9b4ea2636772133eb0521d6358d4ed0e11585 Mon Sep 17 00:00:00 2001 From: Ruben Bridgewater Date: Mon, 16 Jan 2017 09:53:31 +0100 Subject: [PATCH 0696/1748] Fix test on windows --- test/commands/monitor.spec.js | 3 +++ 1 file changed, 3 insertions(+) diff --git a/test/commands/monitor.spec.js b/test/commands/monitor.spec.js index ec287ccba71..a6c98747309 100644 --- a/test/commands/monitor.spec.js +++ b/test/commands/monitor.spec.js @@ -80,6 +80,9 @@ describe("The 'monitor' method", function () { }); it('monitors returns strings in the rawOutput even with return_buffers activated', function (done) { + if (process.platform === 'win32') { + this.skip(); + } var monitorClient = redis.createClient({ return_buffers: true, path: '/tmp/redis.sock' From 6bb0d0007f1451fca219956a33c5b3458f1d8606 Mon Sep 17 00:00:00 2001 From: Jacky Liu Date: Thu, 2 Mar 2017 11:35:44 +0800 Subject: [PATCH 0697/1748] Fix option param null bug options.rename_commands could be null, and the repo README.md list the default value is "null"; --- index.js | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/index.js b/index.js index 798b78954bb..23c11ef3336 100644 --- a/index.js +++ b/index.js @@ -931,7 +931,7 @@ RedisClient.prototype.internal_send_command = function (command_obj) { args_copy[i] = this.options.prefix + args_copy[i]; } } - if (typeof this.options.rename_commands !== 'undefined' && this.options.rename_commands[command]) { + if (this.options.rename_commands && this.options.rename_commands[command]) { command = this.options.rename_commands[command]; } // Always use 'Multi bulk commands', but if passed any Buffer args, then do multiple writes, one for each arg. From 0a4a4ebfa18eab9b35173575bb351883851d1f91 Mon Sep 17 00:00:00 2001 From: "Kyle J. Davis" Date: Mon, 6 Mar 2017 13:54:33 -0500 Subject: [PATCH 0698/1748] Added example for `rename_commands` Fixes #1192. --- README.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/README.md b/README.md index ffa1ad8fa77..961589d4888 100644 --- a/README.md +++ b/README.md @@ -197,7 +197,7 @@ __Tip:__ If the Redis server runs on the same machine as the client consider usi | db | null | If set, client will run Redis `select` command on connect. | | family | IPv4 | You can force using IPv6 if you set the family to 'IPv6'. See Node.js [net](https://nodejs.org/api/net.html) or [dns](https://nodejs.org/api/dns.html) modules on how to use the family type. | | disable_resubscribing | false | If set to `true`, a client won't resubscribe after disconnecting. | -| rename_commands | null | Passing an object with renamed commands to use instead of the original functions. See the [Redis security topics](http://redis.io/topics/security) for more info. | +| rename_commands | null | Passing an object with renamed commands to use instead of the original functions. For example, if you renamed the command KEYS to "DO-NOT-USE" then the rename_commands object would be: `{ KEYS : "DO-NOT-USE" }` . See the [Redis security topics](http://redis.io/topics/security) for more info. | | tls | null | An object containing options to pass to [tls.connect](http://nodejs.org/api/tls.html#tls_tls_connect_port_host_options_callback) to set up a TLS connection to Redis (if, for example, it is set up to be accessible via a tunnel). | | prefix | null | A string used to prefix all used keys (e.g. `namespace:test`). Please be aware that the `keys` command will not be prefixed. The `keys` command has a "pattern" as argument and no key and it would be impossible to determine the existing keys in Redis if this would be prefixed. | | retry_strategy | function | A function that receives an options object as parameter including the retry `attempt`, the `total_retry_time` indicating how much time passed since the last time connected, the `error` why the connection was lost and the number of `times_connected` in total. If you return a number from this function, the retry will happen exactly after that time in milliseconds. If you return a non-number, no further retry will happen and all offline commands are flushed with errors. Return an error to return that specific error to all offline commands. Example below. | From e4ce21e12c7891091e335735527f9dfa3da24a9c Mon Sep 17 00:00:00 2001 From: Ruben Bridgewater Date: Thu, 9 Mar 2017 19:13:27 -0300 Subject: [PATCH 0699/1748] Use the newest redis-parser version --- package.json | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/package.json b/package.json index 1cc6acef924..7f9ef46d9fe 100644 --- a/package.json +++ b/package.json @@ -27,7 +27,7 @@ "dependencies": { "double-ended-queue": "^2.1.0-0", "redis-commands": "^1.2.0", - "redis-parser": "^2.0.0" + "redis-parser": "^2.5.0" }, "engines": { "node": ">=0.10.0" From a2255d7fe2e156a221925dbaf3623672bcbcab96 Mon Sep 17 00:00:00 2001 From: Ruben Bridgewater Date: Thu, 9 Mar 2017 19:13:04 -0300 Subject: [PATCH 0700/1748] Fix error messages not being visible in the stack trace of AbortErrors --- index.js | 2 ++ lib/customErrors.js | 25 +++++++++++++++++++------ test/node_redis.spec.js | 2 +- 3 files changed, 22 insertions(+), 7 deletions(-) diff --git a/index.js b/index.js index 23c11ef3336..909ebb1a919 100644 --- a/index.js +++ b/index.js @@ -1087,6 +1087,8 @@ exports.RedisClient = RedisClient; exports.print = utils.print; exports.Multi = require('./lib/multi'); exports.AbortError = errorClasses.AbortError; +exports.RedisError = Parser.RedisError; +exports.ParserError = Parser.ParserError; exports.ReplyError = Parser.ReplyError; exports.AggregateError = errorClasses.AggregateError; diff --git a/lib/customErrors.js b/lib/customErrors.js index bdf2feda0c0..0ffdff400fe 100644 --- a/lib/customErrors.js +++ b/lib/customErrors.js @@ -1,42 +1,55 @@ 'use strict'; var util = require('util'); +var assert = require('assert') +var RedisError = require('redis-parser').RedisError +var ADD_STACKTRACE = false -function AbortError (obj) { - Error.captureStackTrace(this, this.constructor); +function AbortError (obj, stack) { + assert(obj, 'The options argument is required') + assert.strictEqual(typeof obj, 'object', 'The options argument has to be of type object') + + RedisError.call(this, obj.message, ADD_STACKTRACE) Object.defineProperty(this, 'message', { value: obj.message || '', configurable: true, writable: true }); + if (stack || stack === undefined) { + Error.captureStackTrace(this, AbortError) + } for (var keys = Object.keys(obj), key = keys.pop(); key; key = keys.pop()) { this[key] = obj[key]; } } function AggregateError (obj) { - Error.captureStackTrace(this, this.constructor); + assert(obj, 'The options argument is required') + assert.strictEqual(typeof obj, 'object', 'The options argument has to be of type object') + + AbortError.call(this, obj, ADD_STACKTRACE) Object.defineProperty(this, 'message', { value: obj.message || '', configurable: true, writable: true }); + Error.captureStackTrace(this, AggregateError); for (var keys = Object.keys(obj), key = keys.pop(); key; key = keys.pop()) { this[key] = obj[key]; } } -util.inherits(AbortError, Error); +util.inherits(AbortError, RedisError); util.inherits(AggregateError, AbortError); Object.defineProperty(AbortError.prototype, 'name', { value: 'AbortError', - // configurable: true, + configurable: true, writable: true }); Object.defineProperty(AggregateError.prototype, 'name', { value: 'AggregateError', - // configurable: true, + configurable: true, writable: true }); diff --git a/test/node_redis.spec.js b/test/node_redis.spec.js index dfb744354b1..d9fbc097284 100644 --- a/test/node_redis.spec.js +++ b/test/node_redis.spec.js @@ -870,7 +870,7 @@ describe('The node_redis client', function () { client.on('error', function (err) { assert.strictEqual(err.message, 'Protocol error, got "a" as reply type byte. Please report this.'); assert.strictEqual(err, error); - assert(err instanceof redis.ReplyError); + assert(err instanceof redis.ParserError); // After the hard failure work properly again. The set should have been processed properly too client.get('foo', function (err, res) { assert.strictEqual(res, 'bar'); From 7effa210a98c4676e2e2f8b210f148342a9e0509 Mon Sep 17 00:00:00 2001 From: Ruben Bridgewater Date: Sat, 11 Mar 2017 14:33:38 -0300 Subject: [PATCH 0701/1748] Improve documentation --- README.md | 13 +++++++++---- 1 file changed, 9 insertions(+), 4 deletions(-) diff --git a/README.md b/README.md index 961589d4888..2b889d32712 100644 --- a/README.md +++ b/README.md @@ -296,10 +296,15 @@ client.get("foo_rand000000000000", function (err, reply) { ## Error handling (>= v.2.6) -All redis errors are returned as `ReplyError`. -All unresolved commands that get rejected due to what ever reason return a `AbortError`. -As subclass of the `AbortError` a `AggregateError` exists. This is emitted in case multiple unresolved commands without callback got rejected in debug_mode. -They are all aggregated and a single error is emitted in that case. +Currently the following error subclasses exist: + +* `RedisError`: _All errors_ returned by the client +* `ReplyError` subclass of `RedisError`: All errors returned by __Redis__ itself +* `AbortError` subclass of `RedisError`: All commands that could not finish due to what ever reason +* `ParserError` subclass of `RedisError`: Returned in case of a parser error (this should not happen) +* `AggregateError` subclass of `AbortError`: Emitted in case multiple unresolved commands without callback got rejected in debug_mode instead of lots of `AbortError`s. + +All error classes are exported by the module. Example: ```js From 974742e71c46e02478aee2b832a4bf779f42048d Mon Sep 17 00:00:00 2001 From: Ruben Bridgewater Date: Sat, 11 Mar 2017 14:36:14 -0300 Subject: [PATCH 0702/1748] Add changelog entry --- changelog.md | 11 +++++++++++ 1 file changed, 11 insertions(+) diff --git a/changelog.md b/changelog.md index ee7b4c9d232..3c7fddd0f2c 100644 --- a/changelog.md +++ b/changelog.md @@ -1,6 +1,17 @@ Changelog ========= +## v.2.7.0 - xx Mar, 2017 + +Features + +- All returned errors are from now a subclass of `RedisError`. + +Bugfixes + +- Fixed rename_commands not accepting `null` as value +- Fixed `AbortError`s and `AggregateError`s not showing the error message in the stack trace + ## v.2.6.5 - 15 Jan, 2017 Bugfixes From b81e8cf63229771452fbe2139dac689682c1324a Mon Sep 17 00:00:00 2001 From: Ruben Bridgewater Date: Sat, 11 Mar 2017 15:14:18 -0300 Subject: [PATCH 0703/1748] v.2.7.0 --- changelog.md | 2 +- package.json | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/changelog.md b/changelog.md index 3c7fddd0f2c..f603d9aa0d1 100644 --- a/changelog.md +++ b/changelog.md @@ -1,7 +1,7 @@ Changelog ========= -## v.2.7.0 - xx Mar, 2017 +## v.2.7.0 - 11 Mar, 2017 Features diff --git a/package.json b/package.json index 7f9ef46d9fe..a168213d315 100644 --- a/package.json +++ b/package.json @@ -1,6 +1,6 @@ { "name": "redis", - "version": "2.6.5", + "version": "2.7.0", "description": "Redis client library", "keywords": [ "database", From 60998b0b2026064b283205cc789e8f38bc79a339 Mon Sep 17 00:00:00 2001 From: Ruben Bridgewater Date: Sat, 11 Mar 2017 15:30:09 -0300 Subject: [PATCH 0704/1748] Fix missing semicolons --- lib/customErrors.js | 20 ++++++++++---------- 1 file changed, 10 insertions(+), 10 deletions(-) diff --git a/lib/customErrors.js b/lib/customErrors.js index 0ffdff400fe..d9b34421a71 100644 --- a/lib/customErrors.js +++ b/lib/customErrors.js @@ -1,22 +1,22 @@ 'use strict'; var util = require('util'); -var assert = require('assert') -var RedisError = require('redis-parser').RedisError -var ADD_STACKTRACE = false +var assert = require('assert'); +var RedisError = require('redis-parser').RedisError; +var ADD_STACKTRACE = false; function AbortError (obj, stack) { - assert(obj, 'The options argument is required') - assert.strictEqual(typeof obj, 'object', 'The options argument has to be of type object') + assert(obj, 'The options argument is required'); + assert.strictEqual(typeof obj, 'object', 'The options argument has to be of type object'); - RedisError.call(this, obj.message, ADD_STACKTRACE) + RedisError.call(this, obj.message, ADD_STACKTRACE); Object.defineProperty(this, 'message', { value: obj.message || '', configurable: true, writable: true }); if (stack || stack === undefined) { - Error.captureStackTrace(this, AbortError) + Error.captureStackTrace(this, AbortError); } for (var keys = Object.keys(obj), key = keys.pop(); key; key = keys.pop()) { this[key] = obj[key]; @@ -24,10 +24,10 @@ function AbortError (obj, stack) { } function AggregateError (obj) { - assert(obj, 'The options argument is required') - assert.strictEqual(typeof obj, 'object', 'The options argument has to be of type object') + assert(obj, 'The options argument is required'); + assert.strictEqual(typeof obj, 'object', 'The options argument has to be of type object'); - AbortError.call(this, obj, ADD_STACKTRACE) + AbortError.call(this, obj, ADD_STACKTRACE); Object.defineProperty(this, 'message', { value: obj.message || '', configurable: true, From 3f1b9ce650d4fb2af2cdf9b0068cfe8d2775bc64 Mon Sep 17 00:00:00 2001 From: Josh Yudaken Date: Tue, 14 Mar 2017 08:59:23 -0700 Subject: [PATCH 0705/1748] Generalise monitor_regex --- lib/utils.js | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/lib/utils.js b/lib/utils.js index 84f05119695..52e58ecfa6c 100644 --- a/lib/utils.js +++ b/lib/utils.js @@ -127,7 +127,7 @@ module.exports = { reply_to_object: replyToObject, print: print, err_code: /^([A-Z]+)\s+(.+)$/, - monitor_regex: /^[0-9]{10,11}\.[0-9]+ \[[0-9]{1,3} (.(?!\]))+.\]( ".+?")+$/, + monitor_regex: /^[0-9]{10,11}\.[0-9]+ \[[0-9]+ .+\]( ".+?")+$/, clone: convenienceClone, callback_or_emit: callbackOrEmit, reply_in_order: replyInOrder From 5d73f5efa246f5f16f0e6ce2480fe4cdad8b0cb8 Mon Sep 17 00:00:00 2001 From: Ruben Bridgewater Date: Tue, 14 Mar 2017 18:15:57 -0300 Subject: [PATCH 0706/1748] v.2.7.1 --- changelog.md | 6 ++++++ package.json | 2 +- 2 files changed, 7 insertions(+), 1 deletion(-) diff --git a/changelog.md b/changelog.md index f603d9aa0d1..6ec0e3c663f 100644 --- a/changelog.md +++ b/changelog.md @@ -1,6 +1,12 @@ Changelog ========= +## v.2.7.1 - 14 Mar, 2017 + +Bugfixes + +- Fixed monitor mode not working in combination with IPv6 (2.6.0 regression) + ## v.2.7.0 - 11 Mar, 2017 Features diff --git a/package.json b/package.json index a168213d315..417a9c486cf 100644 --- a/package.json +++ b/package.json @@ -1,6 +1,6 @@ { "name": "redis", - "version": "2.7.0", + "version": "2.7.1", "description": "Redis client library", "keywords": [ "database", From 439d629c81518486a921fc5fe3efb1213a55fbf7 Mon Sep 17 00:00:00 2001 From: Rahul Date: Wed, 15 Mar 2017 14:38:04 -0700 Subject: [PATCH 0707/1748] Minor grammar fix --- index.js | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/index.js b/index.js index 909ebb1a919..2b93b5aa562 100644 --- a/index.js +++ b/index.js @@ -120,7 +120,7 @@ function RedisClient (options, stream) { if ('max_attempts' in options) { self.warn( 'max_attempts is deprecated and will be removed in v.3.0.0.\n' + - 'To reduce the amount of options and the improve the reconnection handling please use the new `retry_strategy` option instead.\n' + + 'To reduce the number of options and to improve the reconnection handling please use the new `retry_strategy` option instead.\n' + 'This replaces the max_attempts and retry_max_delay option.' ); } From 740d0e525d506231ecc36e21fac0d4b1e9d66b56 Mon Sep 17 00:00:00 2001 From: Andrew Shapro Date: Thu, 16 Mar 2017 14:44:37 -0700 Subject: [PATCH 0708/1748] change `times_connected` to `attempt` in the `retry_strategy` --- README.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/README.md b/README.md index 2b889d32712..44760cf31bf 100644 --- a/README.md +++ b/README.md @@ -232,7 +232,7 @@ var client = redis.createClient({ // End reconnecting after a specific timeout and flush all commands with a individual error return new Error('Retry time exhausted'); } - if (options.times_connected > 10) { + if (options.attempt > 10) { // End reconnecting with built in error return undefined; } From a5ee5c86ca8f4deaefcb31141ab98ee0a2943cf1 Mon Sep 17 00:00:00 2001 From: Pavel Ivanov Date: Mon, 3 Apr 2017 09:40:33 +0300 Subject: [PATCH 0709/1748] Fix error code in the readme --- README.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/README.md b/README.md index 44760cf31bf..57b70afb1de 100644 --- a/README.md +++ b/README.md @@ -341,7 +341,7 @@ If node_redis emits a library error because of another error, the triggering err ___Error codes___ -node_redis returns a `NR_CLOSED` error code if the clients connection dropped. If a command unresolved command got rejected a `UNERCTAIN_STATE` code is returned. +node_redis returns a `NR_CLOSED` error code if the clients connection dropped. If a command unresolved command got rejected a `UNCERTAIN_STATE` code is returned. A `CONNECTION_BROKEN` error code is used in case node_redis gives up to reconnect. ## client.unref() From e93df43c9efb0eceedcb9cc9cd8a450a625d3973 Mon Sep 17 00:00:00 2001 From: Prayag Verma Date: Tue, 18 Apr 2017 16:37:59 +0530 Subject: [PATCH 0710/1748] Fix typos in Readme MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit fireing → firing accomodate → accommodate maintance → maintenance --- README.md | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/README.md b/README.md index 57b70afb1de..0bbc38e351e 100644 --- a/README.md +++ b/README.md @@ -588,7 +588,7 @@ Redis. The interface in `node_redis` is to return an individual `Batch` object b The only difference between .batch and .multi is that no transaction is going to be used. Be aware that the errors are - just like in multi statements - in the result. Otherwise both, errors and results could be returned at the same time. -If you fire many commands at once this is going to boost the execution speed significantly compared to fireing the same commands in a loop without waiting for the result! See the benchmarks for further comparison. Please remember that all commands are kept in memory until they are fired. +If you fire many commands at once this is going to boost the execution speed significantly compared to firing the same commands in a loop without waiting for the result! See the benchmarks for further comparison. Please remember that all commands are kept in memory until they are fired. ## Monitor mode @@ -663,7 +663,7 @@ the second word as first parameter: Duplicate all current options and return a new redisClient instance. All options passed to the duplicate function are going to replace the original option. If you pass a callback, duplicate is going to wait until the client is ready and returns it in the callback. If an error occurs in the meanwhile, that is going to return an error instead in the callback. -One example of when to use duplicate() would be to accomodate the connection- +One example of when to use duplicate() would be to accommodate the connection- blocking redis commands BRPOP, BLPOP, and BRPOPLPUSH. If these commands are used on the same redisClient instance as non-blocking commands, the non-blocking ones may be queued up until after the blocking ones finish. @@ -838,7 +838,7 @@ Many [others](https://github.com/NodeRedis/node_redis/graphs/contributors) contr Right now there are two great redis clients around and both have some advantages above each other. We speak about ioredis and node_redis. So after talking to each other about how we could improve in working together we (that is @luin and @BridgeAR) decided to work towards a single library on the long run. But step by step. -First of all, we want to split small parts of our libraries into others so that we're both able to use the same code. Those libraries are going to be maintained under the NodeRedis organization. This is going to reduce the maintance overhead, allows others to use the very same code, if they need it and it's way easyer for others to contribute to both libraries. +First of all, we want to split small parts of our libraries into others so that we're both able to use the same code. Those libraries are going to be maintained under the NodeRedis organization. This is going to reduce the maintenance overhead, allows others to use the very same code, if they need it and it's way easyer for others to contribute to both libraries. We're very happy about this step towards working together as we both want to give you the best redis experience possible. From d4a332aea444b9849e1472a925f0cfed300e85bc Mon Sep 17 00:00:00 2001 From: Kien Pham Date: Wed, 26 Apr 2017 17:23:54 -0700 Subject: [PATCH 0711/1748] quick expire example --- README.md | 21 ++++++++++++++++----- 1 file changed, 16 insertions(+), 5 deletions(-) diff --git a/README.md b/README.md index 0bbc38e351e..75444e6027e 100644 --- a/README.md +++ b/README.md @@ -114,6 +114,17 @@ For a list of Redis commands, see [Redis Command Reference](http://redis.io/comm Minimal parsing is done on the replies. Commands that return a integer return JavaScript Numbers, arrays return JavaScript Array. `HGETALL` returns an Object keyed by the hash keys. All strings will either be returned as string or as buffer depending on your setting. Please be aware that sending null, undefined and Boolean values will result in the value coerced to a string! +# Redis Commands +This library is a 1 to 1 mapping to [Redis commands](https://redis.io/commands). It is not a cache library so please refer to Redis commands page for full usage details. + +Example setting key to auto expire using [setex command](https://redis.io/commands/setex) + +```js +// this key will expires after 10 seconds +client.setex('myKey', 10, 'Awesome value!'); +``` + + # API ## Connection and other Events @@ -663,9 +674,9 @@ the second word as first parameter: Duplicate all current options and return a new redisClient instance. All options passed to the duplicate function are going to replace the original option. If you pass a callback, duplicate is going to wait until the client is ready and returns it in the callback. If an error occurs in the meanwhile, that is going to return an error instead in the callback. -One example of when to use duplicate() would be to accommodate the connection- +One example of when to use duplicate() would be to accommodate the connection- blocking redis commands BRPOP, BLPOP, and BRPOPLPUSH. If these commands -are used on the same redisClient instance as non-blocking commands, the +are used on the same redisClient instance as non-blocking commands, the non-blocking ones may be queued up until after the blocking ones finish. var Redis=require('redis'); @@ -686,10 +697,10 @@ non-blocking ones may be queued up until after the blocking ones finish. }; get(); brpop(); - -Another reason to use duplicate() is when multiple DBs on the same server are + +Another reason to use duplicate() is when multiple DBs on the same server are accessed via the redis SELECT command. Each DB could use its own connection. - + ## client.send_command(command_name[, [args][, callback]]) All Redis commands have been added to the `client` object. However, if new commands are introduced before this library is updated, From a8ea06314fb8677612018034777920a9678949f0 Mon Sep 17 00:00:00 2001 From: Kien Pham Date: Wed, 26 Apr 2017 18:29:27 -0700 Subject: [PATCH 0712/1748] use set example instead --- README.md | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/README.md b/README.md index 75444e6027e..8109a7679d1 100644 --- a/README.md +++ b/README.md @@ -117,11 +117,11 @@ Please be aware that sending null, undefined and Boolean values will result in t # Redis Commands This library is a 1 to 1 mapping to [Redis commands](https://redis.io/commands). It is not a cache library so please refer to Redis commands page for full usage details. -Example setting key to auto expire using [setex command](https://redis.io/commands/setex) +Example setting key to auto expire using [SET command](https://redis.io/commands/set) ```js // this key will expires after 10 seconds -client.setex('myKey', 10, 'Awesome value!'); +client.set('key', 'value!', 'EX', 10); ``` From 5cd9d80397bd005ca5edca598da528204de41206 Mon Sep 17 00:00:00 2001 From: Nick Haughton Date: Fri, 7 Jul 2017 07:31:05 -0400 Subject: [PATCH 0713/1748] Fixing documentation typo --- README.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/README.md b/README.md index 8109a7679d1..1de45c853b5 100644 --- a/README.md +++ b/README.md @@ -120,7 +120,7 @@ This library is a 1 to 1 mapping to [Redis commands](https://redis.io/commands). Example setting key to auto expire using [SET command](https://redis.io/commands/set) ```js -// this key will expires after 10 seconds +// this key will expire after 10 seconds client.set('key', 'value!', 'EX', 10); ``` From 7ce73189b9568a4453cbed5830ea3fe228fda05a Mon Sep 17 00:00:00 2001 From: Karl Svartholm Date: Thu, 13 Jul 2017 20:06:54 +0200 Subject: [PATCH 0714/1748] :memo: fix typo: To / Too --- README.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/README.md b/README.md index 1de45c853b5..cfd5bfe5f7e 100644 --- a/README.md +++ b/README.md @@ -330,7 +330,7 @@ client.on('error', function (err) { assert.strictEqual(err.errors.length, 2); // The set and get got aggregated in here assert.strictEqual(err.code, 'NR_CLOSED'); }); -client.set('foo', 123, 'bar', function (err, res) { // To many arguments +client.set('foo', 123, 'bar', function (err, res) { // Too many arguments assert(err instanceof redis.ReplyError); // => true assert.strictEqual(err.command, 'SET'); assert.deepStrictEqual(err.args, ['foo', 123, 'bar']); From 5f62055dd9c4b2b972cbdfc83ab5be1ddf219873 Mon Sep 17 00:00:00 2001 From: Matt Audesse Date: Mon, 17 Jul 2017 16:34:30 -0400 Subject: [PATCH 0715/1748] Fix "therefor" vs. "therefore" typo in README.md See: https://english.stackexchange.com/a/58617 --- README.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/README.md b/README.md index cfd5bfe5f7e..a43ac49bebe 100644 --- a/README.md +++ b/README.md @@ -274,7 +274,7 @@ In combination this can be used to implement backpressure by checking the buffer ## client.quit() This sends the quit command to the redis server and ends cleanly right after all running commands were properly handled. -If this is called while reconnecting (and therefor no connection to the redis server exists) it is going to end the connection right away instead of +If this is called while reconnecting (and therefore no connection to the redis server exists) it is going to end the connection right away instead of resulting in further reconnections! All offline commands are going to be flushed with an error in that case. ## client.end(flush) From 18924eb2400dac02e3309aba98f507db39227720 Mon Sep 17 00:00:00 2001 From: Luke Childs Date: Sat, 15 Jul 2017 16:22:43 +0100 Subject: [PATCH 0716/1748] Save url for later, port_arg might be an object --- lib/createClient.js | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/lib/createClient.js b/lib/createClient.js index a019fc7a383..2f3b09fdb36 100644 --- a/lib/createClient.js +++ b/lib/createClient.js @@ -23,7 +23,8 @@ module.exports = function createClient (port_arg, host_arg, options) { } else if (typeof port_arg === 'string' || port_arg && port_arg.url) { options = utils.clone(port_arg.url ? port_arg : host_arg || options); - var parsed = URL.parse(port_arg.url || port_arg, true, true); + var url = port_arg.url || port_arg; + var parsed = URL.parse(url, true, true); // [redis:]//[[user][:password]@][host][:port][/db-number][?db=db-number[&password=bar[&option=value]]] if (parsed.slashes) { // We require slashes @@ -59,7 +60,7 @@ module.exports = function createClient (port_arg, host_arg, options) { } else if (parsed.hostname) { throw new RangeError('The redis url must begin with slashes "//" or contain slashes after the redis protocol'); } else { - options.path = port_arg; + options.path = url; } } else if (typeof port_arg === 'object' || port_arg === undefined) { From b7ccf7194262fb1ebcf1fcffb30de2fd6c8972f0 Mon Sep 17 00:00:00 2001 From: Ruben Bridgewater Date: Fri, 5 May 2017 18:25:20 +0200 Subject: [PATCH 0717/1748] chore: order devDependencies --- package.json | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/package.json b/package.json index 417a9c486cf..8fa67d8bbb1 100644 --- a/package.json +++ b/package.json @@ -35,8 +35,8 @@ "devDependencies": { "bluebird": "^3.0.2", "coveralls": "^2.11.2", - "intercept-stdout": "~0.1.2", "eslint": "^3.5.0", + "intercept-stdout": "~0.1.2", "metrics": "^0.1.9", "mocha": "^3.1.2", "nyc": "^8.3.0", From 4271a7067c73077f54a42f5813a27ebd8b988389 Mon Sep 17 00:00:00 2001 From: Ruben Bridgewater Date: Fri, 5 May 2017 18:40:49 +0200 Subject: [PATCH 0718/1748] doc: fix some typos --- changelog.md | 34 +++++++++++++++++----------------- 1 file changed, 17 insertions(+), 17 deletions(-) diff --git a/changelog.md b/changelog.md index 6ec0e3c663f..da42bdd9264 100644 --- a/changelog.md +++ b/changelog.md @@ -5,50 +5,50 @@ Changelog Bugfixes -- Fixed monitor mode not working in combination with IPv6 (2.6.0 regression) +- Fixed monitor mode not working in combination with IPv6 (2.6.0 regression) ## v.2.7.0 - 11 Mar, 2017 Features -- All returned errors are from now a subclass of `RedisError`. +- All returned errors are from now a subclass of `RedisError`. Bugfixes -- Fixed rename_commands not accepting `null` as value -- Fixed `AbortError`s and `AggregateError`s not showing the error message in the stack trace +- Fixed rename_commands not accepting `null` as value +- Fixed `AbortError`s and `AggregateError`s not showing the error message in the stack trace ## v.2.6.5 - 15 Jan, 2017 Bugfixes -- Fixed parser not being reset in case the redis connection closed ASAP for overcoming of output buffer limits -- Fixed parser reset if (p)message_buffer listener is attached +- Fixed parser not being reset in case the redis connection closed ASAP for overcoming of output buffer limits +- Fixed parser reset if (p)message_buffer listener is attached ## v.2.6.4 - 12 Jan, 2017 Bugfixes -- Fixed monitor mode not working in combination with IPv6, sockets or lua scripts (2.6.0 regression) +- Fixed monitor mode not working in combination with IPv6, sockets or lua scripts (2.6.0 regression) ## v.2.6.3 - 31 Oct, 2016 Bugfixes -- Do not change the tls setting to camel_case -- Fix domain handling in combination with the offline queue (2.5.3 regression) +- Do not change the tls setting to camel_case +- Fix domain handling in combination with the offline queue (2.5.3 regression) ## v.2.6.2 - 16 Jun, 2016 Bugfixes -- Fixed individual callbacks of a transaction not being called (2.6.0 regression) +- Fixed individual callbacks of a transaction not being called (2.6.0 regression) ## v.2.6.1 - 02 Jun, 2016 Bugfixes -- Fixed invalid function name being exported +- Fixed invalid function name being exported ## v.2.6.0 - 01 Jun, 2016 @@ -130,7 +130,7 @@ Features - Monitor and pub sub mode now work together with the offline queue - All commands that were send after a connection loss are now going to be send after reconnecting - Activating monitor mode does now work together with arbitrary commands including pub sub mode -- Pub sub mode is completly rewritten and all known issues fixed +- Pub sub mode is completely rewritten and all known issues fixed - Added `string_numbers` option to get back strings instead of numbers - Quit command is from now on always going to end the connection properly @@ -173,7 +173,7 @@ Same changelog as the pre-release ## v.2.5.0-1 - 07 Mar, 2016 -This is a big release with some substaintual underlining changes. Therefor this is released as a pre-release and I encourage anyone who's able to, to test this out. +This is a big release with some substantial underlining changes. Therefor this is released as a pre-release and I encourage anyone who's able to, to test this out. It took way to long to release this one and the next release cycles will be shorter again. @@ -193,7 +193,7 @@ Features - Added a `warning` emitter that receives node_redis warnings like auth not required and deprecation messages - Added a `retry_strategy` option that replaces all reconnect options - The reconnecting event from now on also receives: - - The error message why the reconnect happend (params.error) + - The error message why the reconnect happened (params.error) - The amount of times the client was connected (params.times_connected) - The total reconnecting time since the last time connected (params.total_retry_time) - Always respect the command execution order no matter if the reply could be returned sync or not (former exceptions: [#937](https://github.com/NodeRedis/node_redis/issues/937#issuecomment-167525939)) @@ -208,9 +208,9 @@ Bugfixes - Fixed do not run toString on an array argument and throw a "invalid data" error instead - This is not considered as breaking change, as this is likely a error in your code and if you want to have such a behavior you should handle this beforehand - The same applies to Map / Set and individual Object types -- Fixed redis url not accepting the protocol being omitted or protocols other than the redis protocol for convienence +- Fixed redis url not accepting the protocol being omitted or protocols other than the redis protocol for convenience - Fixed parsing the db keyspace even if the first database does not begin with a zero -- Fixed handling of errors occuring while receiving pub sub messages +- Fixed handling of errors occurring while receiving pub sub messages - Fixed huge string pipelines crashing NodeJS (Pipeline size above 256mb) - Fixed rename_commands and prefix option not working together - Fixed ready being emitted to early in case a slave is still syncing / master down @@ -223,7 +223,7 @@ Deprecations - Using SET or SETEX with a undefined or null value will from now on also result in converting the value to "null" / "undefined" to have a consistent behavior. This is not considered as breaking change, as it returned an error earlier. - Using .end(flush) without the flush parameter is deprecated and the flush parameter should explicitly be used - From v.3.0.0 on using .end without flush will result in an error - - Using .end without flush means that any command that did not yet return is going to silently fail. Therefor this is considered harmfull and you should explicitly silence such errors if you are sure you want this + - Using .end without flush means that any command that did not yet return is going to silently fail. Therefor this is considered harmful and you should explicitly silence such errors if you are sure you want this - Depending on the return value of a command to detect the backpressure is deprecated - From version 3.0.0 on node_redis might not return true / false as a return value anymore. Please rely on client.should_buffer instead - The `socket_nodelay` option is deprecated and will be removed in v.3.0.0 From 6934270a04de4c194e86837c82ab7f6fe2aa0a87 Mon Sep 17 00:00:00 2001 From: Ruben Bridgewater Date: Fri, 5 May 2017 18:41:04 +0200 Subject: [PATCH 0719/1748] fix: always copy subscribe unsubscribe arguments --- changelog.md | 9 +++++++-- lib/individualCommands.js | 16 ++++++++-------- 2 files changed, 15 insertions(+), 10 deletions(-) diff --git a/changelog.md b/changelog.md index da42bdd9264..60ab4e58f1e 100644 --- a/changelog.md +++ b/changelog.md @@ -1,5 +1,10 @@ -Changelog -========= +# Changelog + +## v.2.7.2 - 14 Mar, 2017 + +Bugfixes + +- Fixed not always copying subscribe unsubscribe arguments ## v.2.7.1 - 14 Mar, 2017 diff --git a/lib/individualCommands.js b/lib/individualCommands.js index 88fca126887..d366b642502 100644 --- a/lib/individualCommands.js +++ b/lib/individualCommands.js @@ -398,7 +398,7 @@ RedisClient.prototype.subscribe = RedisClient.prototype.SUBSCRIBE = function sub callback, i = 0; if (Array.isArray(arguments[0])) { - arr = arguments[0]; + arr = arguments[0].slice(0); callback = arguments[1]; } else { len = arguments.length; @@ -425,7 +425,7 @@ Multi.prototype.subscribe = Multi.prototype.SUBSCRIBE = function subscribe () { callback, i = 0; if (Array.isArray(arguments[0])) { - arr = arguments[0]; + arr = arguments[0].slice(0); callback = arguments[1]; } else { len = arguments.length; @@ -453,7 +453,7 @@ RedisClient.prototype.unsubscribe = RedisClient.prototype.UNSUBSCRIBE = function callback, i = 0; if (Array.isArray(arguments[0])) { - arr = arguments[0]; + arr = arguments[0].slice(0); callback = arguments[1]; } else { len = arguments.length; @@ -481,7 +481,7 @@ Multi.prototype.unsubscribe = Multi.prototype.UNSUBSCRIBE = function unsubscribe callback, i = 0; if (Array.isArray(arguments[0])) { - arr = arguments[0]; + arr = arguments[0].slice(0); callback = arguments[1]; } else { len = arguments.length; @@ -510,7 +510,7 @@ RedisClient.prototype.psubscribe = RedisClient.prototype.PSUBSCRIBE = function p callback, i = 0; if (Array.isArray(arguments[0])) { - arr = arguments[0]; + arr = arguments[0].slice(0); callback = arguments[1]; } else { len = arguments.length; @@ -537,7 +537,7 @@ Multi.prototype.psubscribe = Multi.prototype.PSUBSCRIBE = function psubscribe () callback, i = 0; if (Array.isArray(arguments[0])) { - arr = arguments[0]; + arr = arguments[0].slice(0); callback = arguments[1]; } else { len = arguments.length; @@ -565,7 +565,7 @@ RedisClient.prototype.punsubscribe = RedisClient.prototype.PUNSUBSCRIBE = functi callback, i = 0; if (Array.isArray(arguments[0])) { - arr = arguments[0]; + arr = arguments[0].slice(0); callback = arguments[1]; } else { len = arguments.length; @@ -593,7 +593,7 @@ Multi.prototype.punsubscribe = Multi.prototype.PUNSUBSCRIBE = function punsubscr callback, i = 0; if (Array.isArray(arguments[0])) { - arr = arguments[0]; + arr = arguments[0].slice(0); callback = arguments[1]; } else { len = arguments.length; From 789471b30a4e97e295e871b185166ced7a1ffdf5 Mon Sep 17 00:00:00 2001 From: Ruben Bridgewater Date: Fri, 5 May 2017 18:41:34 +0200 Subject: [PATCH 0720/1748] chore: update peer dependency version --- package.json | 10 +++++----- test/lib/redis-process.js | 2 +- 2 files changed, 6 insertions(+), 6 deletions(-) diff --git a/package.json b/package.json index 8fa67d8bbb1..5769de31719 100644 --- a/package.json +++ b/package.json @@ -27,7 +27,7 @@ "dependencies": { "double-ended-queue": "^2.1.0-0", "redis-commands": "^1.2.0", - "redis-parser": "^2.5.0" + "redis-parser": "^2.6.0" }, "engines": { "node": ">=0.10.0" @@ -35,14 +35,14 @@ "devDependencies": { "bluebird": "^3.0.2", "coveralls": "^2.11.2", - "eslint": "^3.5.0", + "cross-spawn": "^5.0.0", + "eslint": "^4.2.0", "intercept-stdout": "~0.1.2", "metrics": "^0.1.9", "mocha": "^3.1.2", - "nyc": "^8.3.0", + "nyc": "^11.0.0", "tcp-port-used": "^0.1.2", - "uuid": "^2.0.1", - "win-spawn": "^2.0.0" + "uuid": "^2.0.1" }, "repository": { "type": "git", diff --git a/test/lib/redis-process.js b/test/lib/redis-process.js index e98f7fbffcd..d1724e06e98 100644 --- a/test/lib/redis-process.js +++ b/test/lib/redis-process.js @@ -4,7 +4,7 @@ var config = require('./config'); var fs = require('fs'); var path = require('path'); -var spawn = require('win-spawn'); +var spawn = require('cross-spawn'); var tcpPortUsed = require('tcp-port-used'); var bluebird = require('bluebird'); From 50774aed8a19836527b6c4f101442e3b09f19996 Mon Sep 17 00:00:00 2001 From: Ruben Bridgewater Date: Thu, 20 Jul 2017 20:04:50 -0300 Subject: [PATCH 0721/1748] fix: accept UPPER_CASE commands in send_command --- README.md | 1 - changelog.md | 6 +++++- lib/extendedApi.js | 5 +++-- 3 files changed, 8 insertions(+), 4 deletions(-) diff --git a/README.md b/README.md index a43ac49bebe..2f01ed8a42c 100644 --- a/README.md +++ b/README.md @@ -705,7 +705,6 @@ accessed via the redis SELECT command. Each DB could use its own connection. All Redis commands have been added to the `client` object. However, if new commands are introduced before this library is updated, you can use `send_command()` to send arbitrary commands to Redis. -The command_name has to be lower case. All commands are sent as multi-bulk commands. `args` can either be an Array of arguments, or omitted / set to undefined. diff --git a/changelog.md b/changelog.md index 60ab4e58f1e..791b968c6c3 100644 --- a/changelog.md +++ b/changelog.md @@ -1,6 +1,10 @@ # Changelog -## v.2.7.2 - 14 Mar, 2017 +## v.2.8.0 - 20 Jul, 2017 + +Features + +- Accept UPPER_CASE commands in send_command Bugfixes diff --git a/lib/extendedApi.js b/lib/extendedApi.js index 0e9589775be..bac36914139 100644 --- a/lib/extendedApi.js +++ b/lib/extendedApi.js @@ -16,6 +16,7 @@ RedisClient.prototype.send_command = RedisClient.prototype.sendCommand = functio if (typeof command !== 'string') { throw new TypeError('Wrong input type "' + (command !== null && command !== undefined ? command.constructor.name : command) + '" for command name'); } + command = command.toLowerCase(); if (!Array.isArray(args)) { if (args === undefined || args === null) { args = []; @@ -32,9 +33,9 @@ RedisClient.prototype.send_command = RedisClient.prototype.sendCommand = functio // Using the raw multi command is only possible with this function // If the command is not yet added to the client, the internal function should be called right away - // Otherwise we need to redirect the calls to make sure the interal functions don't get skipped + // Otherwise we need to redirect the calls to make sure the internal functions don't get skipped // The internal functions could actually be used for any non hooked function - // but this might change from time to time and at the moment there's no good way to distinguishe them + // but this might change from time to time and at the moment there's no good way to distinguish them // from each other, so let's just do it do it this way for the time being if (command === 'multi' || typeof this[command] !== 'function') { return this.internal_send_command(new Command(command, args, callback)); From 16632f43f16a258fbb14572db8e75eac56226701 Mon Sep 17 00:00:00 2001 From: Ruben Bridgewater Date: Thu, 20 Jul 2017 20:23:58 -0300 Subject: [PATCH 0722/1748] fix emitting internal auth error on reconnect --- changelog.md | 1 + index.js | 10 +++++++++- test/auth.spec.js | 5 +++-- 3 files changed, 13 insertions(+), 3 deletions(-) diff --git a/changelog.md b/changelog.md index 791b968c6c3..3de9fff9923 100644 --- a/changelog.md +++ b/changelog.md @@ -9,6 +9,7 @@ Features Bugfixes - Fixed not always copying subscribe unsubscribe arguments +- Fixed emitting internal errors while reconnecting with auth ## v.2.7.1 - 14 Mar, 2017 diff --git a/index.js b/index.js index 2b93b5aa562..c3c3e1ec2ba 100644 --- a/index.js +++ b/index.js @@ -228,6 +228,8 @@ function create_parser (self) { RedisClient.prototype.create_stream = function () { var self = this; + var first_attempt = !this.stream; + // Init parser this.reply_parser = create_parser(this); @@ -304,7 +306,13 @@ RedisClient.prototype.create_stream = function () { // Fire the command before redis is connected to be sure it's the first fired command if (this.auth_pass !== undefined) { this.ready = true; - this.auth(this.auth_pass); + // Fail silently as we might not be able to connect + this.auth(this.auth_pass, function (err) { + if (err && first_attempt) { + self.command_queue.get(0).callback = noop; + self.emit('error', err); + } + }); this.ready = false; } }; diff --git a/test/auth.spec.js b/test/auth.spec.js index 8411a4b618d..5697c1663b3 100644 --- a/test/auth.spec.js +++ b/test/auth.spec.js @@ -260,9 +260,10 @@ describe('client authentication', function () { password: 'wrong_password', parser: parser }); - client.once('error', function (err) { + client.on('error', function (err) { assert.strictEqual(err.message, 'ERR invalid password'); - done(); + // Make sure no other errors are reported + setTimeout(done, 50); }); }); From 7e9dda184b0ff8f3d119d6f26cd683c6667dfbff Mon Sep 17 00:00:00 2001 From: Ruben Bridgewater Date: Thu, 20 Jul 2017 21:23:19 -0300 Subject: [PATCH 0723/1748] chore: test node 8 --- .travis.yml | 1 + appveyor.yml | 1 + 2 files changed, 2 insertions(+) diff --git a/.travis.yml b/.travis.yml index eefd2354065..40cfa5f9e1c 100644 --- a/.travis.yml +++ b/.travis.yml @@ -14,4 +14,5 @@ node_js: - "4" - "6" - "7" + - "8" after_success: npm run coveralls diff --git a/appveyor.yml b/appveyor.yml index 9e79508e4c7..7930f05f8ab 100644 --- a/appveyor.yml +++ b/appveyor.yml @@ -7,6 +7,7 @@ environment: - nodejs_version: "0.12" - nodejs_version: "4" - nodejs_version: "6" + - nodejs_version: "8" pull_requests: do_not_increment_build_number: true From e5c8f813ecffceff13fd51d6b14479c908831211 Mon Sep 17 00:00:00 2001 From: Ruben Bridgewater Date: Fri, 21 Jul 2017 01:04:43 -0300 Subject: [PATCH 0724/1748] fix: revert some dev dependency updates to run tests --- package.json | 6 +++--- test/lib/redis-process.js | 2 +- 2 files changed, 4 insertions(+), 4 deletions(-) diff --git a/package.json b/package.json index 5769de31719..3f0e66ecbf0 100644 --- a/package.json +++ b/package.json @@ -35,14 +35,14 @@ "devDependencies": { "bluebird": "^3.0.2", "coveralls": "^2.11.2", - "cross-spawn": "^5.0.0", "eslint": "^4.2.0", "intercept-stdout": "~0.1.2", "metrics": "^0.1.9", "mocha": "^3.1.2", - "nyc": "^11.0.0", + "nyc": "^10.0.0", "tcp-port-used": "^0.1.2", - "uuid": "^2.0.1" + "uuid": "^2.0.1", + "win-spawn": "^2.2.3" }, "repository": { "type": "git", diff --git a/test/lib/redis-process.js b/test/lib/redis-process.js index d1724e06e98..e98f7fbffcd 100644 --- a/test/lib/redis-process.js +++ b/test/lib/redis-process.js @@ -4,7 +4,7 @@ var config = require('./config'); var fs = require('fs'); var path = require('path'); -var spawn = require('cross-spawn'); +var spawn = require('win-spawn'); var tcpPortUsed = require('tcp-port-used'); var bluebird = require('bluebird'); From 937081b4d9258051aa5588fe123d08ae55a372c9 Mon Sep 17 00:00:00 2001 From: Ruben Bridgewater Date: Fri, 21 Jul 2017 01:17:10 -0300 Subject: [PATCH 0725/1748] fix: dependency --- package.json | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/package.json b/package.json index 3f0e66ecbf0..b9f5ffa718c 100644 --- a/package.json +++ b/package.json @@ -42,7 +42,7 @@ "nyc": "^10.0.0", "tcp-port-used": "^0.1.2", "uuid": "^2.0.1", - "win-spawn": "^2.2.3" + "win-spawn": "^2.0.0" }, "repository": { "type": "git", From 42e8bd607fd62b774eaf35af94eba1d0788c6002 Mon Sep 17 00:00:00 2001 From: Ruben Bridgewater Date: Sun, 23 Jul 2017 14:00:00 -0300 Subject: [PATCH 0726/1748] chore: stop testing Node.js 7 --- .travis.yml | 1 - 1 file changed, 1 deletion(-) diff --git a/.travis.yml b/.travis.yml index 40cfa5f9e1c..51745c8819d 100644 --- a/.travis.yml +++ b/.travis.yml @@ -13,6 +13,5 @@ node_js: - "0.12" - "4" - "6" - - "7" - "8" after_success: npm run coveralls From 4f7f1adf50263d43a878ef936e191600a9b2da4d Mon Sep 17 00:00:00 2001 From: Ruben Bridgewater Date: Sun, 23 Jul 2017 14:11:16 -0300 Subject: [PATCH 0727/1748] fix: silence auth errors on reconnect --- index.js | 5 +---- test/auth.spec.js | 5 ++--- 2 files changed, 3 insertions(+), 7 deletions(-) diff --git a/index.js b/index.js index c3c3e1ec2ba..bf73b5c9e42 100644 --- a/index.js +++ b/index.js @@ -228,8 +228,6 @@ function create_parser (self) { RedisClient.prototype.create_stream = function () { var self = this; - var first_attempt = !this.stream; - // Init parser this.reply_parser = create_parser(this); @@ -308,8 +306,7 @@ RedisClient.prototype.create_stream = function () { this.ready = true; // Fail silently as we might not be able to connect this.auth(this.auth_pass, function (err) { - if (err && first_attempt) { - self.command_queue.get(0).callback = noop; + if (err && err.code !== 'UNCERTAIN_STATE') { self.emit('error', err); } }); diff --git a/test/auth.spec.js b/test/auth.spec.js index 5697c1663b3..8411a4b618d 100644 --- a/test/auth.spec.js +++ b/test/auth.spec.js @@ -260,10 +260,9 @@ describe('client authentication', function () { password: 'wrong_password', parser: parser }); - client.on('error', function (err) { + client.once('error', function (err) { assert.strictEqual(err.message, 'ERR invalid password'); - // Make sure no other errors are reported - setTimeout(done, 50); + done(); }); }); From 0437aa49851365bdf82711b7ff6ac7dc369ec7bb Mon Sep 17 00:00:00 2001 From: Kyle Davis Date: Thu, 17 Nov 2016 15:13:57 -0500 Subject: [PATCH 0728/1748] enabled adding abritary commands added test and add_command alias original and special char commands tweaked code spacing --- index.js | 4 +++- lib/commands.js | 24 ++++++++++++++------- test/commands/addCommand.spec.js | 36 ++++++++++++++++++++++++++++++++ 3 files changed, 55 insertions(+), 9 deletions(-) create mode 100644 test/commands/addCommand.spec.js diff --git a/index.js b/index.js index bf73b5c9e42..58fcf84c6c9 100644 --- a/index.js +++ b/index.js @@ -1100,4 +1100,6 @@ exports.AggregateError = errorClasses.AggregateError; // Add all redis commands / node_redis api to the client require('./lib/individualCommands'); require('./lib/extendedApi'); -require('./lib/commands'); + +//enables adding new commands (for modules and new commands) +exports.addCommand = exports.add_command = require('./lib/commands'); \ No newline at end of file diff --git a/lib/commands.js b/lib/commands.js index 6ca01df2c79..0800ea0b8bb 100644 --- a/lib/commands.js +++ b/lib/commands.js @@ -17,11 +17,7 @@ var changeFunctionName = (function () { } }()); -// TODO: Rewrite this including the invidual commands into a Commands class -// that provided a functionality to add new commands to the client - -commands.list.forEach(function (command) { - +var addCommand = function (command) { // Some rare Redis commands use special characters in their command name // Convert those to a underscore to prevent using invalid function names var commandName = command.replace(/(?:^([0-9])|[^a-zA-Z0-9_$])/g, '_$1'); @@ -61,8 +57,12 @@ commands.list.forEach(function (command) { } return this.internal_send_command(new Command(command, arr, callback)); }; + //alias commands with illegal function names (e.g. NR.RUN becomes NR_RUN and nr_run) + if (commandName !== command) { + RedisClient.prototype[commandName.toUpperCase()] = RedisClient.prototype[commandName] = RedisClient.prototype[command]; + } if (changeFunctionName) { - Object.defineProperty(RedisClient.prototype[command], 'name', { + Object.defineProperty(RedisClient.prototype[commandName], 'name', { value: commandName }); } @@ -104,10 +104,18 @@ commands.list.forEach(function (command) { this.queue.push(new Command(command, arr, callback)); return this; }; + //alias commands with illegal function names (e.g. NR.RUN becomes NR_RUN and nr_run) + if (commandName !== command) { + Multi.prototype[commandName.toUpperCase()] = Multi.prototype[commandName] = Multi.prototype[command]; + } if (changeFunctionName) { - Object.defineProperty(Multi.prototype[command], 'name', { + Object.defineProperty(Multi.prototype[commandName], 'name', { value: commandName }); } } -}); +}; + +commands.list.forEach(addCommand); + +module.exports = addCommand; diff --git a/test/commands/addCommand.spec.js b/test/commands/addCommand.spec.js new file mode 100644 index 00000000000..7a0ce4df586 --- /dev/null +++ b/test/commands/addCommand.spec.js @@ -0,0 +1,36 @@ +'use strict'; + +var config = require('../lib/config'); +var redis = config.redis; +var assert = require('assert'); + +describe("The 'addCommand/add_command' method", function () { + var client = redis.createClient(); + var testCommands = { + newcommand : 'newcommand', + nonJsSafe : 'really-new.command', + jsSafe : 'really_new_command' + }; + + it('camel case version exists', function () { + assert.strictEqual(typeof redis.addCommand, 'function'); + }); + it('snake version exists', function () { + assert.strictEqual(typeof redis.add_command, 'function'); + }); + it('does not already have the test standard command', function () { + assert.strictEqual(client[testCommands.newcommand], undefined); + }); + it('generates a new method for an added command', function () { + redis.addCommand(testCommands.newcommand); + assert.strictEqual(typeof client[testCommands.newcommand], 'function'); + }); + it('does not already have the test non-JS-safe command', function () { + assert.strictEqual(client[testCommands.nonJsSafe], undefined); + }); + it('converts illegal command names to JS-safe functions', function () { + redis.addCommand(testCommands.nonJsSafe); + assert.strictEqual(typeof client[testCommands.jsSafe], 'function'); + }); + client.quit(); +}); From 51fdbb7fc2f8b1f6a176528691335aa6ef8a96cf Mon Sep 17 00:00:00 2001 From: Ruben Bridgewater Date: Tue, 1 Aug 2017 00:09:19 -0300 Subject: [PATCH 0729/1748] chore: improve new add_command and add documentation --- README.md | 6 ++++++ changelog.md | 4 +++- lib/commands.js | 8 +++---- test/commands/addCommand.spec.js | 36 -------------------------------- test/node_redis.spec.js | 18 ++++++++++++++++ 5 files changed, 31 insertions(+), 41 deletions(-) delete mode 100644 test/commands/addCommand.spec.js diff --git a/README.md b/README.md index 2f01ed8a42c..64972bfeb57 100644 --- a/README.md +++ b/README.md @@ -708,6 +708,12 @@ you can use `send_command()` to send arbitrary commands to Redis. All commands are sent as multi-bulk commands. `args` can either be an Array of arguments, or omitted / set to undefined. +## client.add_command(command_name) + +Calling add_command will add a new command to the prototype. The exact command +name will be used when calling using this new command. Using arbitrary arguments +is possible as with any other command. + ## client.connected Boolean tracking the state of the connection to the Redis server. diff --git a/changelog.md b/changelog.md index 3de9fff9923..d650f9ba394 100644 --- a/changelog.md +++ b/changelog.md @@ -1,15 +1,17 @@ # Changelog -## v.2.8.0 - 20 Jul, 2017 +## v.2.8.0 - 31 Jul, 2017 Features - Accept UPPER_CASE commands in send_command +- Add arbitrary commands to the prototype by using `Redis.addCommand(name)` Bugfixes - Fixed not always copying subscribe unsubscribe arguments - Fixed emitting internal errors while reconnecting with auth +- Fixed crashing with invalid url option ## v.2.7.1 - 14 Mar, 2017 diff --git a/lib/commands.js b/lib/commands.js index 0800ea0b8bb..6275ec8bf6b 100644 --- a/lib/commands.js +++ b/lib/commands.js @@ -57,12 +57,12 @@ var addCommand = function (command) { } return this.internal_send_command(new Command(command, arr, callback)); }; - //alias commands with illegal function names (e.g. NR.RUN becomes NR_RUN and nr_run) + // Alias special function names (e.g. NR.RUN becomes NR_RUN and nr_run) if (commandName !== command) { RedisClient.prototype[commandName.toUpperCase()] = RedisClient.prototype[commandName] = RedisClient.prototype[command]; } if (changeFunctionName) { - Object.defineProperty(RedisClient.prototype[commandName], 'name', { + Object.defineProperty(RedisClient.prototype[command], 'name', { value: commandName }); } @@ -104,12 +104,12 @@ var addCommand = function (command) { this.queue.push(new Command(command, arr, callback)); return this; }; - //alias commands with illegal function names (e.g. NR.RUN becomes NR_RUN and nr_run) + // Alias special function names (e.g. NR.RUN becomes NR_RUN and nr_run) if (commandName !== command) { Multi.prototype[commandName.toUpperCase()] = Multi.prototype[commandName] = Multi.prototype[command]; } if (changeFunctionName) { - Object.defineProperty(Multi.prototype[commandName], 'name', { + Object.defineProperty(Multi.prototype[command], 'name', { value: commandName }); } diff --git a/test/commands/addCommand.spec.js b/test/commands/addCommand.spec.js deleted file mode 100644 index 7a0ce4df586..00000000000 --- a/test/commands/addCommand.spec.js +++ /dev/null @@ -1,36 +0,0 @@ -'use strict'; - -var config = require('../lib/config'); -var redis = config.redis; -var assert = require('assert'); - -describe("The 'addCommand/add_command' method", function () { - var client = redis.createClient(); - var testCommands = { - newcommand : 'newcommand', - nonJsSafe : 'really-new.command', - jsSafe : 'really_new_command' - }; - - it('camel case version exists', function () { - assert.strictEqual(typeof redis.addCommand, 'function'); - }); - it('snake version exists', function () { - assert.strictEqual(typeof redis.add_command, 'function'); - }); - it('does not already have the test standard command', function () { - assert.strictEqual(client[testCommands.newcommand], undefined); - }); - it('generates a new method for an added command', function () { - redis.addCommand(testCommands.newcommand); - assert.strictEqual(typeof client[testCommands.newcommand], 'function'); - }); - it('does not already have the test non-JS-safe command', function () { - assert.strictEqual(client[testCommands.nonJsSafe], undefined); - }); - it('converts illegal command names to JS-safe functions', function () { - redis.addCommand(testCommands.nonJsSafe); - assert.strictEqual(typeof client[testCommands.jsSafe], 'function'); - }); - client.quit(); -}); diff --git a/test/node_redis.spec.js b/test/node_redis.spec.js index d9fbc097284..758e5c89d99 100644 --- a/test/node_redis.spec.js +++ b/test/node_redis.spec.js @@ -12,6 +12,24 @@ var client; describe('The node_redis client', function () { + describe.only("The 'add_command' method", function () { + + it('camel case and snakeCase version exists', function () { + assert.strictEqual(typeof redis.addCommand, 'function'); + assert.strictEqual(typeof redis.add_command, 'function'); + }); + + it('converts special characters in functions names to lowercase', function () { + var command = 'really-new.command'; + assert.strictEqual(redis.prototype[command], undefined); + redis.addCommand(command); + assert.strictEqual(redis.prototype[command].name, 'really_new_command'); + assert.strictEqual(redis.prototype[command.toUpperCase()].name, 'really_new_command'); + assert.strictEqual(redis.prototype.really_new_command.name, 'really_new_command'); + assert.strictEqual(redis.prototype.REALLY_NEW_COMMAND.name, 'really_new_command'); + }); + }); + it('individual commands sanity check', function (done) { // All commands should work the same in multi context or without // Therefor individual commands always have to be handled in both cases From 79558c524ff783000a6027fb159739770f98b10e Mon Sep 17 00:00:00 2001 From: Ruben Bridgewater Date: Tue, 1 Aug 2017 00:06:08 -0300 Subject: [PATCH 0730/1748] doc: improve README readability by limiting chars to 80 --- README.md | 356 +++++++++++++++++++++++++++++++++++------------------- 1 file changed, 230 insertions(+), 126 deletions(-) diff --git a/README.md b/README.md index 64972bfeb57..205503ba039 100644 --- a/README.md +++ b/README.md @@ -6,7 +6,8 @@ redis - a node.js redis client [![Windows Tests](https://img.shields.io/appveyor/ci/BridgeAR/node-redis/master.svg?label=Windows%20Tests)](https://ci.appveyor.com/project/BridgeAR/node-redis/branch/master) [![Gitter](https://badges.gitter.im/Join%20Chat.svg)](https://gitter.im/NodeRedis/node_redis?utm_source=badge&utm_medium=badge&utm_campaign=pr-badge) -This is a complete and feature rich Redis client for node.js. __It supports all Redis commands__ and focuses on high performance. +This is a complete and feature rich Redis client for node.js. __It supports all +Redis commands__ and focuses on high performance. Install with: @@ -48,13 +49,16 @@ This will display: 1: hashtest 2 mjr:~/work/node_redis (master)$ -Note that the API is entirely asynchronous. To get data back from the server, you'll need to use a callback. -From v.2.6 on the API supports camelCase and snake_case and all options / variables / events etc. can be used either way. -It is recommended to use camelCase as this is the default for the Node.js landscape. +Note that the API is entirely asynchronous. To get data back from the server, +you'll need to use a callback. From v.2.6 on the API supports camelCase and +snake_case and all options / variables / events etc. can be used either way. It +is recommended to use camelCase as this is the default for the Node.js +landscape. ### Promises -You can also use node_redis with promises by promisifying node_redis with [bluebird](https://github.com/petkaantonov/bluebird) as in: +You can also use node_redis with promises by promisifying node_redis with +[bluebird](https://github.com/petkaantonov/bluebird) as in: ```js var redis = require('redis'); @@ -100,7 +104,8 @@ client.set("some key", "some val"); client.set(["some other key", "some val"]); ``` -If the key is missing, reply will be null. Only if the [Redis Command Reference](http://redis.io/commands) states something else it will not be null. +If the key is missing, reply will be null. Only if the [Redis Command +Reference](http://redis.io/commands) states something else it will not be null. ```js client.get("missingkey", function(err, reply) { @@ -111,11 +116,17 @@ client.get("missingkey", function(err, reply) { For a list of Redis commands, see [Redis Command Reference](http://redis.io/commands) -Minimal parsing is done on the replies. Commands that return a integer return JavaScript Numbers, arrays return JavaScript Array. `HGETALL` returns an Object keyed by the hash keys. All strings will either be returned as string or as buffer depending on your setting. -Please be aware that sending null, undefined and Boolean values will result in the value coerced to a string! +Minimal parsing is done on the replies. Commands that return a integer return +JavaScript Numbers, arrays return JavaScript Array. `HGETALL` returns an Object +keyed by the hash keys. All strings will either be returned as string or as +buffer depending on your setting. Please be aware that sending null, undefined +and Boolean values will result in the value coerced to a string! # Redis Commands -This library is a 1 to 1 mapping to [Redis commands](https://redis.io/commands). It is not a cache library so please refer to Redis commands page for full usage details. + +This library is a 1 to 1 mapping to [Redis commands](https://redis.io/commands). +It is not a cache library so please refer to Redis commands page for full usage +details. Example setting key to auto expire using [SET command](https://redis.io/commands/set) @@ -124,7 +135,6 @@ Example setting key to auto expire using [SET command](https://redis.io/commands client.set('key', 'value!', 'EX', 10); ``` - # API ## Connection and other Events @@ -133,8 +143,9 @@ client.set('key', 'value!', 'EX', 10); ### "ready" -`client` will emit `ready` once a connection is established. Commands issued before the `ready` event are queued, -then replayed just before this event is emitted. +`client` will emit `ready` once a connection is established. Commands issued +before the `ready` event are queued, then replayed just before this event is +emitted. ### "connect" @@ -142,13 +153,16 @@ then replayed just before this event is emitted. ### "reconnecting" -`client` will emit `reconnecting` when trying to reconnect to the Redis server after losing the connection. Listeners -are passed an object containing `delay` (in ms) and `attempt` (the attempt #) attributes. +`client` will emit `reconnecting` when trying to reconnect to the Redis server +after losing the connection. Listeners are passed an object containing `delay` +(in ms) and `attempt` (the attempt #) attributes. ### "error" -`client` will emit `error` when encountering an error connecting to the Redis server or when any other in node_redis occurs. -If you use a command without callback and encounter a ReplyError it is going to be emitted to the error listener. +`client` will emit `error` when encountering an error connecting to the Redis +server or when any other in node_redis occurs. If you use a command without +callback and encounter a ReplyError it is going to be emitted to the error +listener. So please attach the error listener to node_redis. @@ -158,33 +172,41 @@ So please attach the error listener to node_redis. ### "drain" (deprecated) -`client` will emit `drain` when the TCP connection to the Redis server has been buffering, but is now -writable. This event can be used to stream commands in to Redis and adapt to backpressure. +`client` will emit `drain` when the TCP connection to the Redis server has been +buffering, but is now writable. This event can be used to stream commands in to +Redis and adapt to backpressure. -If the stream is buffering `client.should_buffer` is set to true. Otherwise the variable is always set to false. -That way you can decide when to reduce your send rate and resume sending commands when you get `drain`. +If the stream is buffering `client.should_buffer` is set to true. Otherwise the +variable is always set to false. That way you can decide when to reduce your +send rate and resume sending commands when you get `drain`. -You can also check the return value of each command as it will also return the backpressure indicator (deprecated). -If false is returned the stream had to buffer. +You can also check the return value of each command as it will also return the +backpressure indicator (deprecated). If false is returned the stream had to +buffer. ### "warning" -`client` will emit `warning` when password was set but none is needed and if a deprecated option / function / similar is used. +`client` will emit `warning` when password was set but none is needed and if a +deprecated option / function / similar is used. ### "idle" (deprecated) -`client` will emit `idle` when there are no outstanding commands that are awaiting a response. +`client` will emit `idle` when there are no outstanding commands that are +awaiting a response. ## redis.createClient() -If you have `redis-server` running on the same machine as node, then the defaults for -port and host are probably fine and you don't need to supply any arguments. `createClient()` returns a `RedisClient` object. Otherwise, `createClient()` accepts these arguments: +If you have `redis-server` running on the same machine as node, then the +defaults for port and host are probably fine and you don't need to supply any +arguments. `createClient()` returns a `RedisClient` object. Otherwise, +`createClient()` accepts these arguments: * `redis.createClient([options])` * `redis.createClient(unix_socket[, options])` * `redis.createClient(redis_url[, options])` * `redis.createClient(port[, host][, options])` -__Tip:__ If the Redis server runs on the same machine as the client consider using unix sockets if possible to increase throughput. +__Tip:__ If the Redis server runs on the same machine as the client consider +using unix sockets if possible to increase throughput. #### `options` object properties | Property | Default | Description | @@ -232,15 +254,18 @@ client.quit(); ``` retry_strategy example + ```js var client = redis.createClient({ retry_strategy: function (options) { if (options.error && options.error.code === 'ECONNREFUSED') { - // End reconnecting on a specific error and flush all commands with a individual error + // End reconnecting on a specific error and flush all commands with + // a individual error return new Error('The server refused the connection'); } if (options.total_retry_time > 1000 * 60 * 60) { - // End reconnecting after a specific timeout and flush all commands with a individual error + // End reconnecting after a specific timeout and flush all commands + // with a individual error return new Error('Retry time exhausted'); } if (options.attempt > 10) { @@ -255,11 +280,12 @@ var client = redis.createClient({ ## client.auth(password[, callback]) -When connecting to a Redis server that requires authentication, the `AUTH` command must be sent as the -first command after connecting. This can be tricky to coordinate with reconnections, the ready check, -etc. To make this easier, `client.auth()` stashes `password` and will send it after each connection, -including reconnections. `callback` is invoked only once, after the response to the very first -`AUTH` command sent. +When connecting to a Redis server that requires authentication, the `AUTH` +command must be sent as the first command after connecting. This can be tricky +to coordinate with reconnections, the ready check, etc. To make this easier, +`client.auth()` stashes `password` and will send it after each connection, +including reconnections. `callback` is invoked only once, after the response to +the very first `AUTH` command sent. NOTE: Your call to `client.auth()` should not be inside the ready handler. If you are doing this wrong, `client` will emit an error that looks something like this `Error: Ready check failed: ERR operation not permitted`. @@ -268,25 +294,34 @@ something like this `Error: Ready check failed: ERR operation not permitted`. ### stream -The client exposed the used [stream](https://nodejs.org/api/stream.html) in `client.stream` and if the stream or client had to [buffer](https://nodejs.org/api/stream.html#stream_writable_write_chunk_encoding_callback) the command in `client.should_buffer`. -In combination this can be used to implement backpressure by checking the buffer state before sending a command and listening to the stream [drain](https://nodejs.org/api/stream.html#stream_event_drain) event. +The client exposed the used [stream](https://nodejs.org/api/stream.html) in +`client.stream` and if the stream or client had to +[buffer](https://nodejs.org/api/stream.html#stream_writable_write_chunk_encoding_callback) +the command in `client.should_buffer`. In combination this can be used to +implement backpressure by checking the buffer state before sending a command and +listening to the stream +[drain](https://nodejs.org/api/stream.html#stream_event_drain) event. ## client.quit() -This sends the quit command to the redis server and ends cleanly right after all running commands were properly handled. -If this is called while reconnecting (and therefore no connection to the redis server exists) it is going to end the connection right away instead of -resulting in further reconnections! All offline commands are going to be flushed with an error in that case. +This sends the quit command to the redis server and ends cleanly right after all +running commands were properly handled. If this is called while reconnecting +(and therefore no connection to the redis server exists) it is going to end the +connection right away instead of resulting in further reconnections! All offline +commands are going to be flushed with an error in that case. ## client.end(flush) -Forcibly close the connection to the Redis server. Note that this does not wait until all replies have been parsed. -If you want to exit cleanly, call `client.quit()` as mentioned above. +Forcibly close the connection to the Redis server. Note that this does not wait +until all replies have been parsed. If you want to exit cleanly, call +`client.quit()` as mentioned above. -You should set flush to true, if you are not absolutely sure you do not care about any other commands. -If you set flush to false all still running commands will silently fail. +You should set flush to true, if you are not absolutely sure you do not care +about any other commands. If you set flush to false all still running commands +will silently fail. -This example closes the connection to the Redis server before the replies have been read. You probably don't -want to do this: +This example closes the connection to the Redis server before the replies have +been read. You probably don't want to do this: ```js var redis = require("redis"), @@ -311,9 +346,13 @@ Currently the following error subclasses exist: * `RedisError`: _All errors_ returned by the client * `ReplyError` subclass of `RedisError`: All errors returned by __Redis__ itself -* `AbortError` subclass of `RedisError`: All commands that could not finish due to what ever reason -* `ParserError` subclass of `RedisError`: Returned in case of a parser error (this should not happen) -* `AggregateError` subclass of `AbortError`: Emitted in case multiple unresolved commands without callback got rejected in debug_mode instead of lots of `AbortError`s. +* `AbortError` subclass of `RedisError`: All commands that could not finish due + to what ever reason +* `ParserError` subclass of `RedisError`: Returned in case of a parser error + (this should not happen) +* `AggregateError` subclass of `AbortError`: Emitted in case multiple unresolved + commands without callback got rejected in debug_mode instead of lots of + `AbortError`s. All error classes are exported by the module. @@ -327,7 +366,8 @@ client.on('error', function (err) { assert(err instanceof Error); assert(err instanceof redis.AbortError); assert(err instanceof redis.AggregateError); - assert.strictEqual(err.errors.length, 2); // The set and get got aggregated in here + // The set and get get aggregated in here + assert.strictEqual(err.errors.length, 2); assert.strictEqual(err.code, 'NR_CLOSED'); }); client.set('foo', 123, 'bar', function (err, res) { // Too many arguments @@ -339,7 +379,8 @@ client.set('foo', 123, 'bar', function (err, res) { // Too many arguments client.set('foo', 'bar'); client.get('foo'); process.nextTick(function () { - client.end(true); // Force closing the connection while the command did not yet return + // Force closing the connection while the command did not yet return + client.end(true); redis.debug_mode = false; }); }); @@ -348,25 +389,33 @@ client.set('foo', 123, 'bar', function (err, res) { // Too many arguments Every `ReplyError` contains the `command` name in all-caps and the arguments (`args`). -If node_redis emits a library error because of another error, the triggering error is added to the returned error as `origin` attribute. +If node_redis emits a library error because of another error, the triggering +error is added to the returned error as `origin` attribute. ___Error codes___ -node_redis returns a `NR_CLOSED` error code if the clients connection dropped. If a command unresolved command got rejected a `UNCERTAIN_STATE` code is returned. -A `CONNECTION_BROKEN` error code is used in case node_redis gives up to reconnect. +node_redis returns a `NR_CLOSED` error code if the clients connection dropped. +If a command unresolved command got rejected a `UNCERTAIN_STATE` code is +returned. A `CONNECTION_BROKEN` error code is used in case node_redis gives up +to reconnect. ## client.unref() -Call `unref()` on the underlying socket connection to the Redis server, allowing the program to exit once no more commands are pending. +Call `unref()` on the underlying socket connection to the Redis server, allowing +the program to exit once no more commands are pending. -This is an **experimental** feature, and only supports a subset of the Redis protocol. Any commands where client state is saved on the Redis server, e.g. `*SUBSCRIBE` or the blocking `BL*` commands will *NOT* work with `.unref()`. +This is an **experimental** feature, and only supports a subset of the Redis +protocol. Any commands where client state is saved on the Redis server, e.g. +`*SUBSCRIBE` or the blocking `BL*` commands will *NOT* work with `.unref()`. ```js var redis = require("redis") var client = redis.createClient() /* - Calling unref() will allow this program to exit immediately after the get command finishes. Otherwise the client would hang as long as the client-server connection is alive. + Calling unref() will allow this program to exit immediately after the get + command finishes. Otherwise the client would hang as long as the + client-server connection is alive. */ client.unref() client.get("foo", function (err, value){ @@ -377,13 +426,15 @@ client.get("foo", function (err, value){ ## Friendlier hash commands -Most Redis commands take a single String or an Array of Strings as arguments, and replies are sent back as a single String or an Array of Strings. -When dealing with hash values, there are a couple of useful exceptions to this. +Most Redis commands take a single String or an Array of Strings as arguments, +and replies are sent back as a single String or an Array of Strings. When +dealing with hash values, there are a couple of useful exceptions to this. ### client.hgetall(hash, callback) -The reply from an HGETALL command will be converted into a JavaScript Object by `node_redis`. That way you can interact -with the responses using JavaScript syntax. +The reply from an HGETALL command will be converted into a JavaScript Object by +`node_redis`. That way you can interact with the responses using JavaScript +syntax. Example: @@ -411,7 +462,8 @@ client.HMSET(key2, { }); ``` -The properties and values of this Object will be set as keys and values in the Redis hash. +The properties and values of this Object will be set as keys and values in the +Redis hash. ### client.hmset(hash, key1, val1, ... keyn, valn, [callback]) @@ -451,11 +503,13 @@ sub.on("message", function (channel, message) { sub.subscribe("a nice channel"); ``` -When a client issues a `SUBSCRIBE` or `PSUBSCRIBE`, that connection is put into a "subscriber" mode. -At that point, only commands that modify the subscription set are valid and quit (and depending on the redis version ping as well). When the subscription -set is empty, the connection is put back into regular mode. +When a client issues a `SUBSCRIBE` or `PSUBSCRIBE`, that connection is put into +a "subscriber" mode. At that point, only commands that modify the subscription +set are valid and quit (and depending on the redis version ping as well). When +the subscription set is empty, the connection is put back into regular mode. -If you need to send regular commands to Redis while in subscriber mode, just open another connection with a new client (hint: use `client.duplicate()`). +If you need to send regular commands to Redis while in subscriber mode, just +open another connection with a new client (hint: use `client.duplicate()`). ## Subscriber Events @@ -468,47 +522,57 @@ Listeners are passed the channel name as `channel` and the message as `message`. ### "pmessage" (pattern, channel, message) -Client will emit `pmessage` for every message received that matches an active subscription pattern. -Listeners are passed the original pattern used with `PSUBSCRIBE` as `pattern`, the sending channel -name as `channel`, and the message as `message`. +Client will emit `pmessage` for every message received that matches an active +subscription pattern. Listeners are passed the original pattern used with +`PSUBSCRIBE` as `pattern`, the sending channel name as `channel`, and the +message as `message`. ### "message_buffer" (channel, message) -This is the same as the `message` event with the exception, that it is always going to emit a buffer. -If you listen to the `message` event at the same time as the `message_buffer`, it is always going to emit a string. +This is the same as the `message` event with the exception, that it is always +going to emit a buffer. If you listen to the `message` event at the same time as +the `message_buffer`, it is always going to emit a string. ### "pmessage_buffer" (pattern, channel, message) -This is the same as the `pmessage` event with the exception, that it is always going to emit a buffer. -If you listen to the `pmessage` event at the same time as the `pmessage_buffer`, it is always going to emit a string. +This is the same as the `pmessage` event with the exception, that it is always +going to emit a buffer. If you listen to the `pmessage` event at the same time +as the `pmessage_buffer`, it is always going to emit a string. ### "subscribe" (channel, count) -Client will emit `subscribe` in response to a `SUBSCRIBE` command. Listeners are passed the -channel name as `channel` and the new count of subscriptions for this client as `count`. +Client will emit `subscribe` in response to a `SUBSCRIBE` command. Listeners are +passed the channel name as `channel` and the new count of subscriptions for this +client as `count`. ### "psubscribe" (pattern, count) -Client will emit `psubscribe` in response to a `PSUBSCRIBE` command. Listeners are passed the -original pattern as `pattern`, and the new count of subscriptions for this client as `count`. +Client will emit `psubscribe` in response to a `PSUBSCRIBE` command. Listeners +are passed the original pattern as `pattern`, and the new count of subscriptions +for this client as `count`. ### "unsubscribe" (channel, count) -Client will emit `unsubscribe` in response to a `UNSUBSCRIBE` command. Listeners are passed the -channel name as `channel` and the new count of subscriptions for this client as `count`. When -`count` is 0, this client has left subscriber mode and no more subscriber events will be emitted. +Client will emit `unsubscribe` in response to a `UNSUBSCRIBE` command. Listeners +are passed the channel name as `channel` and the new count of subscriptions for +this client as `count`. When `count` is 0, this client has left subscriber mode +and no more subscriber events will be emitted. ### "punsubscribe" (pattern, count) -Client will emit `punsubscribe` in response to a `PUNSUBSCRIBE` command. Listeners are passed the -channel name as `channel` and the new count of subscriptions for this client as `count`. When -`count` is 0, this client has left subscriber mode and no more subscriber events will be emitted. +Client will emit `punsubscribe` in response to a `PUNSUBSCRIBE` command. +Listeners are passed the channel name as `channel` and the new count of +subscriptions for this client as `count`. When `count` is 0, this client has +left subscriber mode and no more subscriber events will be emitted. ## client.multi([commands]) -`MULTI` commands are queued up until an `EXEC` is issued, and then all commands are run atomically by -Redis. The interface in `node_redis` is to return an individual `Multi` object by calling `client.multi()`. -If any command fails to queue, all commands are rolled back and none is going to be executed (For further information look at [transactions](http://redis.io/topics/transactions)). +`MULTI` commands are queued up until an `EXEC` is issued, and then all commands +are run atomically by Redis. The interface in `node_redis` is to return an +individual `Multi` object by calling `client.multi()`. If any command fails to +queue, all commands are rolled back and none is going to be executed (For +further information look at +[transactions](http://redis.io/topics/transactions)). ```js var redis = require("./index"), @@ -542,15 +606,20 @@ client.multi() ### Multi.exec([callback]) -`client.multi()` is a constructor that returns a `Multi` object. `Multi` objects share all of the -same command methods as `client` objects do. Commands are queued up inside the `Multi` object -until `Multi.exec()` is invoked. +`client.multi()` is a constructor that returns a `Multi` object. `Multi` objects +share all of the same command methods as `client` objects do. Commands are +queued up inside the `Multi` object until `Multi.exec()` is invoked. -If your code contains an syntax error an EXECABORT error is going to be thrown and all commands are going to be aborted. That error contains a `.errors` property that contains the concret errors. -If all commands were queued successfully and an error is thrown by redis while processing the commands that error is going to be returned in the result array! No other command is going to be aborted though than the onces failing. +If your code contains an syntax error an EXECABORT error is going to be thrown +and all commands are going to be aborted. That error contains a `.errors` +property that contains the concrete errors. +If all commands were queued successfully and an error is thrown by redis while +processing the commands that error is going to be returned in the result array! +No other command is going to be aborted though than the onces failing. -You can either chain together `MULTI` commands as in the above example, or you can queue individual -commands while still sending regular client command as in this example: +You can either chain together `MULTI` commands as in the above example, or you +can queue individual commands while still sending regular client command as in +this example: ```js var redis = require("redis"), @@ -570,8 +639,8 @@ multi.exec(function (err, replies) { }); ``` -In addition to adding commands to the `MULTI` queue individually, you can also pass an array -of commands and arguments to the constructor: +In addition to adding commands to the `MULTI` queue individually, you can also +pass an array of commands and arguments to the constructor: ```js var redis = require("redis"), @@ -588,26 +657,36 @@ client.multi([ ### Multi.exec_atomic([callback]) -Identical to Multi.exec but with the difference that executing a single command will not use transactions. +Identical to Multi.exec but with the difference that executing a single command +will not use transactions. ## client.batch([commands]) -Identical to .multi without transactions. This is recommended if you want to execute many commands at once but don't have to rely on transactions. +Identical to .multi without transactions. This is recommended if you want to +execute many commands at once but don't have to rely on transactions. -`BATCH` commands are queued up until an `EXEC` is issued, and then all commands are run atomically by -Redis. The interface in `node_redis` is to return an individual `Batch` object by calling `client.batch()`. -The only difference between .batch and .multi is that no transaction is going to be used. -Be aware that the errors are - just like in multi statements - in the result. Otherwise both, errors and results could be returned at the same time. +`BATCH` commands are queued up until an `EXEC` is issued, and then all commands +are run atomically by Redis. The interface in `node_redis` is to return an +individual `Batch` object by calling `client.batch()`. The only difference +between .batch and .multi is that no transaction is going to be used. +Be aware that the errors are - just like in multi statements - in the result. +Otherwise both, errors and results could be returned at the same time. -If you fire many commands at once this is going to boost the execution speed significantly compared to firing the same commands in a loop without waiting for the result! See the benchmarks for further comparison. Please remember that all commands are kept in memory until they are fired. +If you fire many commands at once this is going to boost the execution speed +significantly compared to firing the same commands in a loop without waiting for +the result! See the benchmarks for further comparison. Please remember that all +commands are kept in memory until they are fired. ## Monitor mode -Redis supports the `MONITOR` command, which lets you see all commands received by the Redis server -across all client connections, including from other client libraries and other computers. +Redis supports the `MONITOR` command, which lets you see all commands received +by the Redis server across all client connections, including from other client +libraries and other computers. -A `monitor` event is going to be emitted for every command fired from any client connected to the server including the monitoring client itself. -The callback for the `monitor` event takes a timestamp from the Redis server, an array of command arguments and the raw monitoring string. +A `monitor` event is going to be emitted for every command fired from any client +connected to the server including the monitoring client itself. The callback for +the `monitor` event takes a timestamp from the Redis server, an array of command +arguments and the raw monitoring string. Example: @@ -629,10 +708,11 @@ Some other things you might like to know about. ## client.server_info -After the ready probe completes, the results from the INFO command are saved in the `client.server_info` -object. +After the ready probe completes, the results from the INFO command are saved in +the `client.server_info` object. -The `versions` key contains an array of the elements of the version string for easy comparison. +The `versions` key contains an array of the elements of the version string for +easy comparison. > client.server_info.redis_version '2.3.0' @@ -671,8 +751,11 @@ the second word as first parameter: ## client.duplicate([options][, callback]) -Duplicate all current options and return a new redisClient instance. All options passed to the duplicate function are going to replace the original option. -If you pass a callback, duplicate is going to wait until the client is ready and returns it in the callback. If an error occurs in the meanwhile, that is going to return an error instead in the callback. +Duplicate all current options and return a new redisClient instance. All options +passed to the duplicate function are going to replace the original option. If +you pass a callback, duplicate is going to wait until the client is ready and +returns it in the callback. If an error occurs in the meanwhile, that is going +to return an error instead in the callback. One example of when to use duplicate() would be to accommodate the connection- blocking redis commands BRPOP, BLPOP, and BRPOPLPUSH. If these commands @@ -703,10 +786,13 @@ accessed via the redis SELECT command. Each DB could use its own connection. ## client.send_command(command_name[, [args][, callback]]) -All Redis commands have been added to the `client` object. However, if new commands are introduced before this library is updated, -you can use `send_command()` to send arbitrary commands to Redis. +All Redis commands have been added to the `client` object. However, if new +commands are introduced before this library is updated or if you want to add +individual commands you can use `send_command()` to send arbitrary commands to +Redis. -All commands are sent as multi-bulk commands. `args` can either be an Array of arguments, or omitted / set to undefined. +All commands are sent as multi-bulk commands. `args` can either be an Array of +arguments, or omitted / set to undefined. ## client.add_command(command_name) @@ -720,19 +806,23 @@ Boolean tracking the state of the connection to the Redis server. ## client.command_queue_length -The number of commands that have been sent to the Redis server but not yet replied to. You can use this to -enforce some kind of maximum queue depth for commands while connected. +The number of commands that have been sent to the Redis server but not yet +replied to. You can use this to enforce some kind of maximum queue depth for +commands while connected. ## client.offline_queue_length -The number of commands that have been queued up for a future connection. You can use this to enforce -some kind of maximum queue depth for pre-connection commands. +The number of commands that have been queued up for a future connection. You can +use this to enforce some kind of maximum queue depth for pre-connection +commands. ### Commands with Optional and Keyword arguments -This applies to anything that uses an optional `[WITHSCORES]` or `[LIMIT offset count]` in the [redis.io/commands](http://redis.io/commands) documentation. +This applies to anything that uses an optional `[WITHSCORES]` or `[LIMIT offset +count]` in the [redis.io/commands](http://redis.io/commands) documentation. Example: + ```js var args = [ 'myzset', 1, 'one', 2, 'two', 3, 'three', 99, 'ninety-nine' ]; client.zadd(args, function (err, response) { @@ -805,10 +895,13 @@ clients: 1, NodeJS: 6.2.0, Redis: 3.2.0, parser: javascript, connected by: tcp To get debug output run your `node_redis` application with `NODE_DEBUG=redis`. -This is also going to result in good stack traces opposed to useless ones otherwise for any async operation. -If you only want to have good stack traces but not the debug output run your application in development mode instead (`NODE_ENV=development`). +This is also going to result in good stack traces opposed to useless ones +otherwise for any async operation. +If you only want to have good stack traces but not the debug output run your +application in development mode instead (`NODE_ENV=development`). -Good stack traces are only activated in development and debug mode as this results in a significant performance penalty. +Good stack traces are only activated in development and debug mode as this +results in a significant performance penalty. ___Comparison___: Useless stack trace: @@ -844,7 +937,8 @@ The original author of node_redis is [Matthew Ranney](https://github.com/mranney The current lead maintainer is [Ruben Bridgewater](https://github.com/BridgeAR) -Many [others](https://github.com/NodeRedis/node_redis/graphs/contributors) contributed to `node_redis` too. Thanks to all of them! +Many [others](https://github.com/NodeRedis/node_redis/graphs/contributors) +contributed to `node_redis` too. Thanks to all of them! ## License @@ -852,10 +946,20 @@ Many [others](https://github.com/NodeRedis/node_redis/graphs/contributors) contr ### Consolidation: It's time for celebration -Right now there are two great redis clients around and both have some advantages above each other. We speak about ioredis and node_redis. So after talking to each other about how we could improve in working together we (that is @luin and @BridgeAR) decided to work towards a single library on the long run. But step by step. +Right now there are two great redis clients around and both have some advantages +above each other. We speak about ioredis and node_redis. So after talking to +each other about how we could improve in working together we (that is @luin and +@BridgeAR) decided to work towards a single library on the long run. But step by +step. -First of all, we want to split small parts of our libraries into others so that we're both able to use the same code. Those libraries are going to be maintained under the NodeRedis organization. This is going to reduce the maintenance overhead, allows others to use the very same code, if they need it and it's way easyer for others to contribute to both libraries. +First of all, we want to split small parts of our libraries into others so that +we're both able to use the same code. Those libraries are going to be maintained +under the NodeRedis organization. This is going to reduce the maintenance +overhead, allows others to use the very same code, if they need it and it's way +easyer for others to contribute to both libraries. -We're very happy about this step towards working together as we both want to give you the best redis experience possible. +We're very happy about this step towards working together as we both want to +give you the best redis experience possible. -If you want to join our cause by help maintaining something, please don't hesitate to contact either one of us. +If you want to join our cause by help maintaining something, please don't +hesitate to contact either one of us. From ad8355a1372240b5ceb5ae3acde54bc7171b562a Mon Sep 17 00:00:00 2001 From: Ruben Bridgewater Date: Tue, 1 Aug 2017 00:25:52 -0300 Subject: [PATCH 0731/1748] fix: add command tests --- test/node_redis.spec.js | 14 ++++++++------ 1 file changed, 8 insertions(+), 6 deletions(-) diff --git a/test/node_redis.spec.js b/test/node_redis.spec.js index 758e5c89d99..95c8bdef7b9 100644 --- a/test/node_redis.spec.js +++ b/test/node_redis.spec.js @@ -12,7 +12,9 @@ var client; describe('The node_redis client', function () { - describe.only("The 'add_command' method", function () { + describe("The 'add_command' method", function () { + + var Redis = redis.RedisClient; it('camel case and snakeCase version exists', function () { assert.strictEqual(typeof redis.addCommand, 'function'); @@ -21,12 +23,12 @@ describe('The node_redis client', function () { it('converts special characters in functions names to lowercase', function () { var command = 'really-new.command'; - assert.strictEqual(redis.prototype[command], undefined); + assert.strictEqual(Redis.prototype[command], undefined); redis.addCommand(command); - assert.strictEqual(redis.prototype[command].name, 'really_new_command'); - assert.strictEqual(redis.prototype[command.toUpperCase()].name, 'really_new_command'); - assert.strictEqual(redis.prototype.really_new_command.name, 'really_new_command'); - assert.strictEqual(redis.prototype.REALLY_NEW_COMMAND.name, 'really_new_command'); + assert.strictEqual(Redis.prototype[command].name, 'really_new_command'); + assert.strictEqual(Redis.prototype[command.toUpperCase()].name, 'really_new_command'); + assert.strictEqual(Redis.prototype.really_new_command.name, 'really_new_command'); + assert.strictEqual(Redis.prototype.REALLY_NEW_COMMAND.name, 'really_new_command'); }); }); From 6694c91b5425c195b288fd163de3e59572a324f5 Mon Sep 17 00:00:00 2001 From: Ruben Bridgewater Date: Tue, 1 Aug 2017 00:26:25 -0300 Subject: [PATCH 0732/1748] v.2.8.0 --- package.json | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/package.json b/package.json index b9f5ffa718c..4b6d6e94567 100644 --- a/package.json +++ b/package.json @@ -1,6 +1,6 @@ { "name": "redis", - "version": "2.7.1", + "version": "2.8.0", "description": "Redis client library", "keywords": [ "database", From 1380ad67a3f2b4a8b3dc31767e352e3bc4e63576 Mon Sep 17 00:00:00 2001 From: Ruben Bridgewater Date: Tue, 1 Aug 2017 00:36:12 -0300 Subject: [PATCH 0733/1748] fix: test on old node versions --- test/node_redis.spec.js | 10 ++++++---- 1 file changed, 6 insertions(+), 4 deletions(-) diff --git a/test/node_redis.spec.js b/test/node_redis.spec.js index 95c8bdef7b9..1557730312c 100644 --- a/test/node_redis.spec.js +++ b/test/node_redis.spec.js @@ -25,10 +25,12 @@ describe('The node_redis client', function () { var command = 'really-new.command'; assert.strictEqual(Redis.prototype[command], undefined); redis.addCommand(command); - assert.strictEqual(Redis.prototype[command].name, 'really_new_command'); - assert.strictEqual(Redis.prototype[command.toUpperCase()].name, 'really_new_command'); - assert.strictEqual(Redis.prototype.really_new_command.name, 'really_new_command'); - assert.strictEqual(Redis.prototype.REALLY_NEW_COMMAND.name, 'really_new_command'); + if (Redis.prototype[command].name !== '') { + assert.strictEqual(Redis.prototype[command].name, 'really_new_command'); + assert.strictEqual(Redis.prototype[command.toUpperCase()].name, 'really_new_command'); + assert.strictEqual(Redis.prototype.really_new_command.name, 'really_new_command'); + assert.strictEqual(Redis.prototype.REALLY_NEW_COMMAND.name, 'really_new_command'); + } }); }); From eb3441c7e1e0e4720f095a598fc085979731d904 Mon Sep 17 00:00:00 2001 From: "Kyle J. Davis" Date: Thu, 10 Aug 2017 15:37:38 -0700 Subject: [PATCH 0734/1748] added note about user input as per #1265 --- README.md | 2 ++ 1 file changed, 2 insertions(+) diff --git a/README.md b/README.md index 205503ba039..f9e19c09f4d 100644 --- a/README.md +++ b/README.md @@ -97,6 +97,8 @@ client.hmset("key", ["test keys 1", "test val 1", "test keys 2", "test val 2"], client.hmset("key", "test keys 1", "test val 1", "test keys 2", "test val 2", function (err, res) {}); ``` +Care should be taken with user input if arrays are possible (via body-parser, query string or other method), as single arguments could be unintentionally interpreted as multiple args. + Note that in either form the `callback` is optional: ```js From 009479537eb920d2c34045026a55d31febd1edd7 Mon Sep 17 00:00:00 2001 From: Greg Walden Date: Wed, 30 Aug 2017 12:00:23 -0400 Subject: [PATCH 0735/1748] remove unused var in readme example --- README.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/README.md b/README.md index f9e19c09f4d..088f1be9828 100644 --- a/README.md +++ b/README.md @@ -646,7 +646,7 @@ pass an array of commands and arguments to the constructor: ```js var redis = require("redis"), - client = redis.createClient(), multi; + client = redis.createClient(); client.multi([ ["mget", "multifoo", "multibar", redis.print], From 0ad516fd67cb49a98b176b13c8679a392b2101fd Mon Sep 17 00:00:00 2001 From: dcharbonnier Date: Mon, 9 Oct 2017 17:55:53 +0200 Subject: [PATCH 0736/1748] document camel-case commands --- README.md | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/README.md b/README.md index 088f1be9828..a0114896dda 100644 --- a/README.md +++ b/README.md @@ -786,19 +786,19 @@ non-blocking ones may be queued up until after the blocking ones finish. Another reason to use duplicate() is when multiple DBs on the same server are accessed via the redis SELECT command. Each DB could use its own connection. -## client.send_command(command_name[, [args][, callback]]) +## client.sendCommand(command_name[, [args][, callback]]) All Redis commands have been added to the `client` object. However, if new commands are introduced before this library is updated or if you want to add -individual commands you can use `send_command()` to send arbitrary commands to +individual commands you can use `sendCommand()` to send arbitrary commands to Redis. All commands are sent as multi-bulk commands. `args` can either be an Array of arguments, or omitted / set to undefined. -## client.add_command(command_name) +## client.addCommand(command_name) -Calling add_command will add a new command to the prototype. The exact command +Calling addCommand will add a new command to the prototype. The exact command name will be used when calling using this new command. Using arbitrary arguments is possible as with any other command. From 1ef099c790e6fdedc0c2add94badff0f2bfc830e Mon Sep 17 00:00:00 2001 From: Liran Date: Tue, 7 Nov 2017 09:36:21 +0200 Subject: [PATCH 0737/1748] Native Promises with util.promisify explain how to promisify to native promises (instead of bluebird) with node's v8 util.promisify. --- README.md | 18 +++++++++++++++++- 1 file changed, 17 insertions(+), 1 deletion(-) diff --git a/README.md b/README.md index 088f1be9828..90cde13997b 100644 --- a/README.md +++ b/README.md @@ -57,11 +57,27 @@ landscape. ### Promises +#### Native Promises +If you are using node v8 or higher, you can promisify node_redis with [util.promisify](https://nodejs.org/api/util.html#util_util_promisify_original) as in: +```js +const {promisify} = require('util'); +const getAsync = promisify(client.get).bind(client); +``` +now *getAsync* is a promisified version of *client.get*: +```js +// We expect a value 'foo': 'bar' to be present +// So instead of writing client.get('foo', cb); you have to write: +return getAsync('foo').then(function(res) { + console.log(res); // => 'bar' +}); +``` + +#### Bluebird Promises You can also use node_redis with promises by promisifying node_redis with [bluebird](https://github.com/petkaantonov/bluebird) as in: ```js -var redis = require('redis'); +const redis = require('redis'); bluebird.promisifyAll(redis.RedisClient.prototype); bluebird.promisifyAll(redis.Multi.prototype); ``` From 80f6960fb57548cebc2bfc670f594a003906036a Mon Sep 17 00:00:00 2001 From: Liran Date: Wed, 8 Nov 2017 10:51:15 +0200 Subject: [PATCH 0738/1748] add async await example --- README.md | 6 ++++++ 1 file changed, 6 insertions(+) diff --git a/README.md b/README.md index 90cde13997b..4490f33654d 100644 --- a/README.md +++ b/README.md @@ -70,6 +70,12 @@ now *getAsync* is a promisified version of *client.get*: return getAsync('foo').then(function(res) { console.log(res); // => 'bar' }); + +// or using [async await](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Statements/async_function) +async myFunc() { + const res = await getAsync('foo'); + console.log(res); +} ``` #### Bluebird Promises From 2a586e35b68b3ea9459abb0acaaca2cadfc78d4a Mon Sep 17 00:00:00 2001 From: Liran Date: Wed, 8 Nov 2017 10:52:53 +0200 Subject: [PATCH 0739/1748] comment with reference out of code block --- README.md | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/README.md b/README.md index 4490f33654d..c0effcdf7c4 100644 --- a/README.md +++ b/README.md @@ -70,8 +70,10 @@ now *getAsync* is a promisified version of *client.get*: return getAsync('foo').then(function(res) { console.log(res); // => 'bar' }); +``` -// or using [async await](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Statements/async_function) +or using [async await](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Statements/async_function): +```js async myFunc() { const res = await getAsync('foo'); console.log(res); From 5d6e471d0c14df39974000427719c5d85fa4f21f Mon Sep 17 00:00:00 2001 From: calebboyd Date: Fri, 27 Oct 2017 16:32:33 -0500 Subject: [PATCH 0740/1748] feat: add support for rediss protocol in url --- README.md | 2 +- lib/createClient.js | 6 +++++- test/tls.spec.js | 23 +++++++++++++++++++++++ 3 files changed, 29 insertions(+), 2 deletions(-) diff --git a/README.md b/README.md index c0effcdf7c4..1ec63dacdda 100644 --- a/README.md +++ b/README.md @@ -240,7 +240,7 @@ using unix sockets if possible to increase throughput. | host | 127.0.0.1 | IP address of the Redis server | | port | 6379 | Port of the Redis server | | path | null | The UNIX socket string of the Redis server | -| url | null | The URL of the Redis server. Format: `[redis:]//[[user][:password@]][host][:port][/db-number][?db=db-number[&password=bar[&option=value]]]` (More info avaliable at [IANA](http://www.iana.org/assignments/uri-schemes/prov/redis)). | +| url | null | The URL of the Redis server. Format: `[redis:][rediss:]//[[user][:password@]][host][:port][/db-number][?db=db-number[&password=bar[&option=value]]]` (More info avaliable at [IANA](http://www.iana.org/assignments/uri-schemes/prov/redis)). | | parser | javascript | __Deprecated__ Use either the built-in JS parser [`javascript`]() or the native [`hiredis`]() parser. __Note__ `node_redis` < 2.6 uses hiredis as default if installed. This changed in v.2.6.0. | | string_numbers | null | Set to `true`, `node_redis` will return Redis number values as Strings instead of javascript Numbers. Useful if you need to handle big numbers (above `Number.MAX_SAFE_INTEGER === 2^53`). Hiredis is incapable of this behavior, so setting this option to `true` will result in the built-in javascript parser being used no matter the value of the `parser` option. | | return_buffers | false | If set to `true`, then all replies will be sent to callbacks as Buffers instead of Strings. | diff --git a/lib/createClient.js b/lib/createClient.js index 2f3b09fdb36..8c3a8508a28 100644 --- a/lib/createClient.js +++ b/lib/createClient.js @@ -32,7 +32,11 @@ module.exports = function createClient (port_arg, host_arg, options) { options.password = parsed.auth.split(':')[1]; } if (parsed.protocol && parsed.protocol !== 'redis:') { - console.warn('node_redis: WARNING: You passed "' + parsed.protocol.substring(0, parsed.protocol.length - 1) + '" as protocol instead of the "redis" protocol!'); + if (parsed.protocol === 'rediss:') { + options.tls = options.tls || {}; + } else { + console.warn('node_redis: WARNING: You passed "' + parsed.protocol.substring(0, parsed.protocol.length - 1) + '" as protocol instead of the "redis" protocol!'); + } } if (parsed.pathname && parsed.pathname !== '/') { options.db = parsed.pathname.substr(1); diff --git a/test/tls.spec.js b/test/tls.spec.js index d977ee7d9ac..6a7e021e33c 100644 --- a/test/tls.spec.js +++ b/test/tls.spec.js @@ -108,6 +108,29 @@ describe('TLS connection tests', function () { client.get('foo', helper.isString('bar', done)); }); + describe('using rediss as url protocol', function (done) { + var tls = require('tls') + var tlsConnect = tls.connect + beforeEach(function () { + tls.connect = function (options) { + options = utils.clone(options) + options.ca = tls_options.ca; + return tlsConnect.call(tls, options); + } + }) + afterEach(function () { + tls.connect = tlsConnect; + }) + it('connect with tls when rediss is used as the protocol', function (done) { + if (skip) this.skip(); + client = redis.createClient('rediss://localhost:' + tls_port); + // verify connection is using TCP, not UNIX socket + assert(client.stream.encrypted); + client.set('foo', 'bar'); + client.get('foo', helper.isString('bar', done)); + }); + }) + it('fails to connect because the cert is not correct', function (done) { if (skip) this.skip(); var faulty_cert = utils.clone(tls_options); From 1a4c410b4179d68aee922b80aff583a336251163 Mon Sep 17 00:00:00 2001 From: calebboyd Date: Tue, 9 Jan 2018 10:54:16 -0600 Subject: [PATCH 0741/1748] docs: add note about rediss usage --- README.md | 4 +++- lib/createClient.js | 4 ++-- test/tls.spec.js | 16 ++++++++-------- 3 files changed, 13 insertions(+), 11 deletions(-) diff --git a/README.md b/README.md index 1ec63dacdda..c3aabeb6858 100644 --- a/README.md +++ b/README.md @@ -234,13 +234,15 @@ arguments. `createClient()` returns a `RedisClient` object. Otherwise, __Tip:__ If the Redis server runs on the same machine as the client consider using unix sockets if possible to increase throughput. +__Note:__ Using `'rediss://...` for the protocol in a `redis_url` will enable a TLS socket connection. However, additional TLS options will need to be passed in `options`, if required. + #### `options` object properties | Property | Default | Description | |-----------|-----------|-------------| | host | 127.0.0.1 | IP address of the Redis server | | port | 6379 | Port of the Redis server | | path | null | The UNIX socket string of the Redis server | -| url | null | The URL of the Redis server. Format: `[redis:][rediss:]//[[user][:password@]][host][:port][/db-number][?db=db-number[&password=bar[&option=value]]]` (More info avaliable at [IANA](http://www.iana.org/assignments/uri-schemes/prov/redis)). | +| url | null | The URL of the Redis server. Format: `[redis[s]:]//[[user][:password@]][host][:port][/db-number][?db=db-number[&password=bar[&option=value]]]` (More info avaliable at [IANA](http://www.iana.org/assignments/uri-schemes/prov/redis)). | | parser | javascript | __Deprecated__ Use either the built-in JS parser [`javascript`]() or the native [`hiredis`]() parser. __Note__ `node_redis` < 2.6 uses hiredis as default if installed. This changed in v.2.6.0. | | string_numbers | null | Set to `true`, `node_redis` will return Redis number values as Strings instead of javascript Numbers. Useful if you need to handle big numbers (above `Number.MAX_SAFE_INTEGER === 2^53`). Hiredis is incapable of this behavior, so setting this option to `true` will result in the built-in javascript parser being used no matter the value of the `parser` option. | | return_buffers | false | If set to `true`, then all replies will be sent to callbacks as Buffers instead of Strings. | diff --git a/lib/createClient.js b/lib/createClient.js index 8c3a8508a28..133f0b758e9 100644 --- a/lib/createClient.js +++ b/lib/createClient.js @@ -31,10 +31,10 @@ module.exports = function createClient (port_arg, host_arg, options) { if (parsed.auth) { options.password = parsed.auth.split(':')[1]; } - if (parsed.protocol && parsed.protocol !== 'redis:') { + if (parsed.protocol) { if (parsed.protocol === 'rediss:') { options.tls = options.tls || {}; - } else { + } else if (parsed.protocol !== 'redis:') { console.warn('node_redis: WARNING: You passed "' + parsed.protocol.substring(0, parsed.protocol.length - 1) + '" as protocol instead of the "redis" protocol!'); } } diff --git a/test/tls.spec.js b/test/tls.spec.js index 6a7e021e33c..3b0e18197ac 100644 --- a/test/tls.spec.js +++ b/test/tls.spec.js @@ -7,6 +7,7 @@ var helper = require('./helper'); var path = require('path'); var redis = config.redis; var utils = require('../lib/utils'); +var tls = require('tls'); var tls_options = { servername: 'redis.js.org', @@ -90,12 +91,12 @@ describe('TLS connection tests', function () { it('connect with host and port provided in the tls object', function (done) { if (skip) this.skip(); - var tls = utils.clone(tls_options); - tls.port = tls_port; - tls.host = 'localhost'; + var tls_opts = utils.clone(tls_options); + tls_opts.port = tls_port; + tls_opts.host = 'localhost'; client = redis.createClient({ connect_timeout: 1000, - tls: tls + tls: tls_opts }); // verify connection is using TCP, not UNIX socket @@ -109,17 +110,16 @@ describe('TLS connection tests', function () { }); describe('using rediss as url protocol', function (done) { - var tls = require('tls') - var tlsConnect = tls.connect + var tls_connect = tls.connect beforeEach(function () { tls.connect = function (options) { options = utils.clone(options) options.ca = tls_options.ca; - return tlsConnect.call(tls, options); + return tls_connect.call(tls, options); } }) afterEach(function () { - tls.connect = tlsConnect; + tls.connect = tls_connect; }) it('connect with tls when rediss is used as the protocol', function (done) { if (skip) this.skip(); From 648e65a2bc74215d9d8755a7a6d3d146bca29b94 Mon Sep 17 00:00:00 2001 From: Salakar Date: Sun, 11 Feb 2018 14:18:34 +0000 Subject: [PATCH 0742/1748] swap out queue library --- index.js | 2 +- lib/multi.js | 2 +- package.json | 2 +- 3 files changed, 3 insertions(+), 3 deletions(-) diff --git a/index.js b/index.js index 58fcf84c6c9..271d53db437 100644 --- a/index.js +++ b/index.js @@ -5,7 +5,7 @@ var tls = require('tls'); var util = require('util'); var utils = require('./lib/utils'); var Command = require('./lib/command'); -var Queue = require('double-ended-queue'); +var Queue = require('denque'); var errorClasses = require('./lib/customErrors'); var EventEmitter = require('events'); var Parser = require('redis-parser'); diff --git a/lib/multi.js b/lib/multi.js index 63f5d210856..d89cffbbd48 100644 --- a/lib/multi.js +++ b/lib/multi.js @@ -1,6 +1,6 @@ 'use strict'; -var Queue = require('double-ended-queue'); +var Queue = require('denque'); var utils = require('./utils'); var Command = require('./command'); diff --git a/package.json b/package.json index 4b6d6e94567..c4e722ac010 100644 --- a/package.json +++ b/package.json @@ -25,7 +25,7 @@ "compare": "node benchmarks/diff_multi_bench_output.js beforeBench.txt afterBench.txt" }, "dependencies": { - "double-ended-queue": "^2.1.0-0", + "denque": "^1.2.3", "redis-commands": "^1.2.0", "redis-parser": "^2.6.0" }, From cf0a323fbc71e26c03c137d849ff1d3d7bf49618 Mon Sep 17 00:00:00 2001 From: Salakar Date: Sun, 11 Feb 2018 14:34:50 +0000 Subject: [PATCH 0743/1748] utils spec change - forgot to push --- test/utils.spec.js | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/test/utils.spec.js b/test/utils.spec.js index e036d84167c..0d9ae96c637 100644 --- a/test/utils.spec.js +++ b/test/utils.spec.js @@ -1,7 +1,7 @@ 'use strict'; var assert = require('assert'); -var Queue = require('double-ended-queue'); +var Queue = require('denque'); var utils = require('../lib/utils'); var intercept = require('intercept-stdout'); From cc48de824869ba3a76026df4f92cca9562008e7b Mon Sep 17 00:00:00 2001 From: Tim Marshall Date: Mon, 19 Feb 2018 11:09:40 -0800 Subject: [PATCH 0744/1748] consistent ;s --- README.md | 14 +++++++------- 1 file changed, 7 insertions(+), 7 deletions(-) diff --git a/README.md b/README.md index c3aabeb6858..1b3de89d98a 100644 --- a/README.md +++ b/README.md @@ -437,19 +437,19 @@ protocol. Any commands where client state is saved on the Redis server, e.g. `*SUBSCRIBE` or the blocking `BL*` commands will *NOT* work with `.unref()`. ```js -var redis = require("redis") -var client = redis.createClient() +var redis = require("redis"); +var client = redis.createClient(); /* Calling unref() will allow this program to exit immediately after the get command finishes. Otherwise the client would hang as long as the client-server connection is alive. */ -client.unref() -client.get("foo", function (err, value){ - if (err) throw(err) - console.log(value) -}) +client.unref(); +client.get("foo", function (err, value) { + if (err) throw(err); + console.log(value); +}); ``` ## Friendlier hash commands From e3a8706c6ef3b1f356e784968169795b8e0dafd5 Mon Sep 17 00:00:00 2001 From: Tim Marshall Date: Mon, 19 Feb 2018 11:11:01 -0800 Subject: [PATCH 0745/1748] js highlighting --- README.md | 46 +++++++++++++++++++++++++--------------------- 1 file changed, 25 insertions(+), 21 deletions(-) diff --git a/README.md b/README.md index 1b3de89d98a..a2375279f0f 100644 --- a/README.md +++ b/README.md @@ -773,9 +773,11 @@ Note that this program will not exit cleanly because the client is still connect To execute redis multi-word commands like `SCRIPT LOAD` or `CLIENT LIST` pass the second word as first parameter: - client.script('load', 'return 1'); - client.multi().script('load', 'return 1').exec(...); - client.multi([['script', 'load', 'return 1']]).exec(...); +```js +client.script('load', 'return 1'); +client.multi().script('load', 'return 1').exec(...); +client.multi([['script', 'load', 'return 1']]).exec(...); +``` ## client.duplicate([options][, callback]) @@ -790,24 +792,26 @@ blocking redis commands BRPOP, BLPOP, and BRPOPLPUSH. If these commands are used on the same redisClient instance as non-blocking commands, the non-blocking ones may be queued up until after the blocking ones finish. - var Redis=require('redis'); - var client = Redis.createClient(); - var clientBlocking = client.duplicate(); - - var get = function() { - console.log("get called"); - client.get("any_key",function() { console.log("get returned"); }); - setTimeout( get, 1000 ); - }; - var brpop = function() { - console.log("brpop called"); - clientBlocking.brpop("nonexistent", 5, function() { - console.log("brpop return"); - setTimeout( brpop, 1000 ); - }); - }; - get(); - brpop(); +```js +var Redis=require('redis'); +var client = Redis.createClient(); +var clientBlocking = client.duplicate(); + +var get = function() { + console.log("get called"); + client.get("any_key",function() { console.log("get returned"); }); + setTimeout( get, 1000 ); +}; +var brpop = function() { + console.log("brpop called"); + clientBlocking.brpop("nonexistent", 5, function() { + console.log("brpop return"); + setTimeout( brpop, 1000 ); + }); +}; +get(); +brpop(); +``` Another reason to use duplicate() is when multiple DBs on the same server are accessed via the redis SELECT command. Each DB could use its own connection. From 2a15fc8825d8fc34a5f629354a70b164f4231004 Mon Sep 17 00:00:00 2001 From: Tim Marshall Date: Mon, 19 Feb 2018 11:12:28 -0800 Subject: [PATCH 0746/1748] fix indent --- README.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/README.md b/README.md index a2375279f0f..414f75e9a4c 100644 --- a/README.md +++ b/README.md @@ -961,7 +961,7 @@ ReplyError: ERR wrong number of arguments for 'set' command ## How to Contribute - Open a pull request or an issue about what you want to implement / change. We're glad for any help! - - Please be aware that we'll only accept fully tested code. +- Please be aware that we'll only accept fully tested code. ## Contributors From 19c80c617c864c2bdbf37623e9075cdcbacd098c Mon Sep 17 00:00:00 2001 From: Mark McNelis Date: Fri, 23 Feb 2018 16:59:02 +0000 Subject: [PATCH 0747/1748] fix: client duplicate function now allows db param to be passed --- lib/extendedApi.js | 2 +- test/node_redis.spec.js | 3 +++ 2 files changed, 4 insertions(+), 1 deletion(-) diff --git a/lib/extendedApi.js b/lib/extendedApi.js index bac36914139..27ed4215d6a 100644 --- a/lib/extendedApi.js +++ b/lib/extendedApi.js @@ -95,7 +95,7 @@ RedisClient.prototype.duplicate = function (options, callback) { existing_options[elem] = options[elem]; } var client = new RedisClient(existing_options); - client.selected_db = this.selected_db; + client.selected_db = options.db || this.selected_db; if (typeof callback === 'function') { var ready_listener = function () { callback(null, client); diff --git a/test/node_redis.spec.js b/test/node_redis.spec.js index 1557730312c..3375b759d91 100644 --- a/test/node_redis.spec.js +++ b/test/node_redis.spec.js @@ -120,11 +120,14 @@ describe('The node_redis client', function () { }); it('check if all new options replaced the old ones', function (done) { + client.selected_db = 1; var client2 = client.duplicate({ + db: 2, no_ready_check: true }); assert(client.connected); assert(!client2.connected); + assert.notEqual(client.selected_db, client2.selected_db); assert.strictEqual(client.options.no_ready_check, undefined); assert.strictEqual(client2.options.no_ready_check, true); assert.notDeepEqual(client.options, client2.options); From 7ba8163a7fc178c3ddebcc484e7f96fe0e97d95e Mon Sep 17 00:00:00 2001 From: Chris Date: Tue, 27 Mar 2018 22:28:50 -0700 Subject: [PATCH 0748/1748] update readme to reflect usage of .add_command --- README.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/README.md b/README.md index 414f75e9a4c..534138a48f9 100644 --- a/README.md +++ b/README.md @@ -826,7 +826,7 @@ Redis. All commands are sent as multi-bulk commands. `args` can either be an Array of arguments, or omitted / set to undefined. -## client.add_command(command_name) +## redis.add_command(command_name) Calling add_command will add a new command to the prototype. The exact command name will be used when calling using this new command. Using arbitrary arguments From c57d62c857f30ab0eeb257041bcc6fa1a41a4605 Mon Sep 17 00:00:00 2001 From: Ingo Fischer Date: Tue, 3 Apr 2018 14:31:24 +0200 Subject: [PATCH 0749/1748] te,porary solution to enable IPv6 on travis-ci in trusty images --- .travis.yml | 8 +++++++- 1 file changed, 7 insertions(+), 1 deletion(-) diff --git a/.travis.yml b/.travis.yml index 51745c8819d..f2422e979bd 100644 --- a/.travis.yml +++ b/.travis.yml @@ -1,5 +1,5 @@ language: node_js -sudo: false +sudo: required env: - CXX=g++-4.8 TRAVIS=true addons: @@ -15,3 +15,9 @@ node_js: - "6" - "8" after_success: npm run coveralls +before_script: + # Add an IPv6 config - see the corresponding Travis issue + # https://github.com/travis-ci/travis-ci/issues/8361 + - if [ "${TRAVIS_OS_NAME}" == "linux" ]; then + sudo sh -c 'echo 0 > /proc/sys/net/ipv6/conf/all/disable_ipv6'; + fi From 6b294db7a22bc709b53b601e913d0f583e2c6936 Mon Sep 17 00:00:00 2001 From: Ingo Fischer Date: Tue, 3 Apr 2018 14:32:14 +0200 Subject: [PATCH 0750/1748] fix test because error message seems changed --- test/commands/select.spec.js | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/test/commands/select.spec.js b/test/commands/select.spec.js index 4297dca7f34..fc054a7e4a2 100644 --- a/test/commands/select.spec.js +++ b/test/commands/select.spec.js @@ -72,7 +72,7 @@ describe("The 'select' method", function () { assert.strictEqual(client.selected_db, undefined, 'default db should be undefined'); client.select(9999, function (err) { assert.equal(err.code, 'ERR'); - assert.equal(err.message, 'ERR invalid DB index'); + assert.equal(err.message, 'ERR DB index is out of range'); done(); }); }); @@ -97,7 +97,7 @@ describe("The 'select' method", function () { client.on('error', function (err) { assert.strictEqual(err.command, 'SELECT'); - assert.equal(err.message, 'ERR invalid DB index'); + assert.equal(err.message, 'ERR DB index is out of range'); done(); }); From 673b1770756bca1ffa912e911f799657ca4e259d Mon Sep 17 00:00:00 2001 From: Ingo Fischer Date: Tue, 3 Apr 2018 21:27:45 +0200 Subject: [PATCH 0751/1748] accept both error messages (win!=Linux) --- test/commands/select.spec.js | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/test/commands/select.spec.js b/test/commands/select.spec.js index fc054a7e4a2..1dff2025d9c 100644 --- a/test/commands/select.spec.js +++ b/test/commands/select.spec.js @@ -72,7 +72,7 @@ describe("The 'select' method", function () { assert.strictEqual(client.selected_db, undefined, 'default db should be undefined'); client.select(9999, function (err) { assert.equal(err.code, 'ERR'); - assert.equal(err.message, 'ERR DB index is out of range'); + assert((err.message == 'ERR DB index is out of range' || err.message == 'ERR invalid DB index')); done(); }); }); @@ -97,7 +97,7 @@ describe("The 'select' method", function () { client.on('error', function (err) { assert.strictEqual(err.command, 'SELECT'); - assert.equal(err.message, 'ERR DB index is out of range'); + assert((err.message == 'ERR DB index is out of range' || err.message == 'ERR invalid DB index')); done(); }); From 59db10d2be4c6b761263fa4c16c596784a49f050 Mon Sep 17 00:00:00 2001 From: Gonen Dukas Date: Wed, 25 Apr 2018 13:18:10 +0300 Subject: [PATCH 0752/1748] Simpler promisifyAll example --- README.md | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/README.md b/README.md index 534138a48f9..60b408c9fb1 100644 --- a/README.md +++ b/README.md @@ -86,8 +86,7 @@ You can also use node_redis with promises by promisifying node_redis with ```js const redis = require('redis'); -bluebird.promisifyAll(redis.RedisClient.prototype); -bluebird.promisifyAll(redis.Multi.prototype); +bluebird.promisifyAll(redis); ``` It'll add a *Async* to all node_redis functions (e.g. return client.getAsync().then()) From 35facdb1fb5945a358d00b48a6282c91a7e3165c Mon Sep 17 00:00:00 2001 From: chestermax01 Date: Mon, 21 May 2018 15:46:30 +0800 Subject: [PATCH 0753/1748] Adapt Alibaba Cloud kvstore --- lib/createClient.js | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/lib/createClient.js b/lib/createClient.js index 133f0b758e9..13c213470a5 100644 --- a/lib/createClient.js +++ b/lib/createClient.js @@ -29,7 +29,7 @@ module.exports = function createClient (port_arg, host_arg, options) { // [redis:]//[[user][:password]@][host][:port][/db-number][?db=db-number[&password=bar[&option=value]]] if (parsed.slashes) { // We require slashes if (parsed.auth) { - options.password = parsed.auth.split(':')[1]; + options.password = parsed.auth.slice(parsed.auth.indexOf(':') + 1); } if (parsed.protocol) { if (parsed.protocol === 'rediss:') { From c159815e62af9032aca8bd52602541ddd2e25894 Mon Sep 17 00:00:00 2001 From: Salakar Date: Tue, 22 May 2018 22:04:47 +0100 Subject: [PATCH 0754/1748] update utils.spec queue tests to support denque push() differences --- test/utils.spec.js | 17 ++++++++++++----- 1 file changed, 12 insertions(+), 5 deletions(-) diff --git a/test/utils.spec.js b/test/utils.spec.js index 0d9ae96c637..592600fb6d8 100644 --- a/test/utils.spec.js +++ b/test/utils.spec.js @@ -131,17 +131,22 @@ describe('utils.js', function () { }); it('elements in the offline queue. Reply after the offline queue is empty and respect the command_obj callback', function (done) { - clientMock.offline_queue.push(create_command_obj(), create_command_obj()); + clientMock.offline_queue.push(create_command_obj()); + clientMock.offline_queue.push(create_command_obj()); utils.reply_in_order(clientMock, function () { assert.strictEqual(clientMock.offline_queue.length, 0); assert.strictEqual(res_count, 2); done(); }, null, null); - while (clientMock.offline_queue.length) clientMock.offline_queue.shift().callback(null, 'foo'); + while (clientMock.offline_queue.length) { + clientMock.offline_queue.shift().callback(null, 'foo'); + } }); it('elements in the offline queue. Reply after the offline queue is empty and respect the command_obj error emit', function (done) { - clientMock.command_queue.push({}, create_command_obj(), {}); + clientMock.command_queue.push({}); + clientMock.command_queue.push(create_command_obj()); + clientMock.command_queue.push({}); utils.reply_in_order(clientMock, function () { assert.strictEqual(clientMock.command_queue.length, 0); assert(emitted); @@ -158,8 +163,10 @@ describe('utils.js', function () { }); it('elements in the offline queue and the command_queue. Reply all other commands got handled respect the command_obj', function (done) { - clientMock.command_queue.push(create_command_obj(), create_command_obj()); - clientMock.offline_queue.push(create_command_obj(), {}); + clientMock.command_queue.push(create_command_obj()); + clientMock.command_queue.push(create_command_obj()); + clientMock.command_queue.push(create_command_obj()); + clientMock.offline_queue.push({}); utils.reply_in_order(clientMock, function (err, res) { assert.strictEqual(clientMock.command_queue.length, 0); assert.strictEqual(clientMock.offline_queue.length, 0); From 0dc626262cf7f18c9b61d5edb9b39efc071d21f2 Mon Sep 17 00:00:00 2001 From: Pedro Miguel Pereira Serrano Martins Date: Sun, 10 Jun 2018 10:44:51 +0200 Subject: [PATCH 0755/1748] Added documentation for REDIS optimistic lock system. --- README.md | 93 +++++++++++++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 93 insertions(+) diff --git a/README.md b/README.md index 60b408c9fb1..d2e9b905832 100644 --- a/README.md +++ b/README.md @@ -704,6 +704,99 @@ significantly compared to firing the same commands in a loop without waiting for the result! See the benchmarks for further comparison. Please remember that all commands are kept in memory until they are fired. +## Optimistic Locks + +Using `multi` you can make sure your modifications run as a transaction, but you +can't be sure you got there first. What if another client modified a key while +you were working with it's data? + +To solve this, Redis supports the [WATCH](https://redis.io/topics/transactions) +command, which is meant to be used with MULTI: + +```js +var redis = require("redis"), + client = redis.createClient(); + +client.watch("foo", function( err ){ + if(err) throw err; + + client.get("foo", function(err, result) { + if(err) throw err; + + // Process result + // Heavy and time consuming operation here + + client.multi() + .set("foo", "some heavy computation") + .exec(function(err, results) { + + /** + * If err is null, it means Redis successfully attempted + * the operation. + */ + if(err) throw err; + + /** + * If results === null, it means that a concurrent client + * changed the key while we were processing it and thus + * the execution of the MULTI command was not performed. + * + * NOTICE: Failing an execution of MULTI is not considered + * an error. So you will have err === null and results === null + */ + + }); + }); +}); +``` + +The above snippet shows the correct usage of `watch` with `multi`. Every time a +watched key is changed before the execution of a `multi` command, the execution +will return `null`. On a normal situation, the execution will return an array of +values with the results of the operations. + +As stated in the snippet, failing the execution of a `multi` command being watched +is not considered an error. The execution may return an error if, for example, the +client cannot connect to Redis. + +An example where we can see the execution of a `multi` command fail is as follows: + +```js +let clients = {}; +clients.watcher = redis.createClient({ ... } ); +clients.alterer = clients.watcher.duplicate(); + +clients.watcher.watch('foo',function(err) { + if (err) { throw err; } + //if you comment out the next line, the transaction will work + clients.alterer.set('foo',Math.random(), (err) => {if (err) { throw err; }}) + + //using a setTimeout here to ensure that the MULTI/EXEC will come after the SET. + //Normally, you would use a callback to ensure order, but I want the above SET command + //to be easily comment-out-able. + setTimeout(function() { + clients.watcher + .multi() + .set('foo','abc') + .set('bar','1234') + .exec((err,results) => { + if (err) { throw err; } + if (results === null) { + console.log('transaction aborted because results were null'); + } else { + console.log('transaction worked and returned',results) + } + clients.watcher.quit(); + clients.alterer.quit(); + }); + },1000); +}); +``` + +**NOTE**: Redis WATCH does not work on fields of hashes and other objects. You can watch a hash +to see if anything inside it was modified, but you cannot watch a specific hash field for a +modification. + ## Monitor mode Redis supports the `MONITOR` command, which lets you see all commands received From 7b575780f6ed6790b1f28aa14555bae2150ad017 Mon Sep 17 00:00:00 2001 From: Pedro Miguel Pereira Serrano Martins Date: Sun, 10 Jun 2018 19:02:25 +0200 Subject: [PATCH 0756/1748] Improved QA of code snippets, improved documentatino on WATCH limitations. --- README.md | 37 +++++++++++++++++++++++++++++++++---- 1 file changed, 33 insertions(+), 4 deletions(-) diff --git a/README.md b/README.md index d2e9b905832..ff15de9355b 100644 --- a/README.md +++ b/README.md @@ -715,7 +715,7 @@ command, which is meant to be used with MULTI: ```js var redis = require("redis"), - client = redis.createClient(); + client = redis.createClient({ ... }); client.watch("foo", function( err ){ if(err) throw err; @@ -793,9 +793,38 @@ clients.watcher.watch('foo',function(err) { }); ``` -**NOTE**: Redis WATCH does not work on fields of hashes and other objects. You can watch a hash -to see if anything inside it was modified, but you cannot watch a specific hash field for a -modification. +### WATCH limitations + +Redis WATCH works only on *whole* key values. For example, with WATCH you can +watch a hash for modifications, but you cannot watch a specific field of a hash. + +The following example would watch the keys `foo` and `hello`, not the field `hello` +of hash `foo`: + +```js +var redis = require("redis"), + client = redis.createClient({ ... }); + +client.hget( "foo", "hello", function(err, result){ + + //Do some processing with the value from this field and watch it after + + client.watch("foo", "hello", function( err ){ + if(err) throw err; + + /** + * WRONG: This is now watching the keys 'foo' and 'hello'. It is not + * watching the field 'hello' of hash 'foo'. Because the key 'foo' + * refers to a hash, this command is now watching the entire hash + * for modifications. + */ + }); +} ) + +``` + +This limitation also applies to sets ( cannot watch individual set members ) +and any other collections. ## Monitor mode From 7ab0804a27a7b9dc86bd58eb3a5d18f04d7c3eff Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Akseli=20Pal=C3=A9n?= Date: Thu, 21 Jun 2018 14:18:29 +0300 Subject: [PATCH 0757/1748] Clarify reconnecting delay Without clarification `delay` can mean two things: time to the next attempt (wrong) or time passed from the previous attempt (correct). The wrong interpretation is tempting because it makes easy to print "Trying to reconnect in {delay} ms." --- README.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/README.md b/README.md index ff15de9355b..04c653e44cb 100644 --- a/README.md +++ b/README.md @@ -180,7 +180,7 @@ emitted. `client` will emit `reconnecting` when trying to reconnect to the Redis server after losing the connection. Listeners are passed an object containing `delay` -(in ms) and `attempt` (the attempt #) attributes. +(in ms from the previous try) and `attempt` (the attempt #) attributes. ### "error" From 62f30c08cbc9f83a0443f281f2bbd964003c22e9 Mon Sep 17 00:00:00 2001 From: Mario Daskalov Date: Fri, 10 Aug 2018 17:14:44 +0300 Subject: [PATCH 0758/1748] Update client.quit definition in README.md to include callback --- README.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/README.md b/README.md index 04c653e44cb..eb05cc864f6 100644 --- a/README.md +++ b/README.md @@ -329,7 +329,7 @@ implement backpressure by checking the buffer state before sending a command and listening to the stream [drain](https://nodejs.org/api/stream.html#stream_event_drain) event. -## client.quit() +## client.quit(callback) This sends the quit command to the redis server and ends cleanly right after all running commands were properly handled. If this is called while reconnecting From 6bb90bff5a423fea7684324dc48f76940cdb258b Mon Sep 17 00:00:00 2001 From: Vasyl Boroviak Date: Wed, 22 Aug 2018 12:01:50 +1000 Subject: [PATCH 0759/1748] Avoid distributing unnecessary development files --- .npmignore | 3 +++ 1 file changed, 3 insertions(+) diff --git a/.npmignore b/.npmignore index b0238e058cb..569f3efb2b8 100644 --- a/.npmignore +++ b/.npmignore @@ -3,6 +3,9 @@ benchmarks/ test/ .nyc_output/ coverage/ +.github/ +.eslintignore +.eslintrc .tern-port *.log *.rdb From 96fdc7794bbdf86bec13421a18c786fec18b0159 Mon Sep 17 00:00:00 2001 From: Guy Korland Date: Mon, 12 Nov 2018 17:36:41 +0200 Subject: [PATCH 0760/1748] add a simple streams example --- examples/streams.js | 47 +++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 47 insertions(+) create mode 100644 examples/streams.js diff --git a/examples/streams.js b/examples/streams.js new file mode 100644 index 00000000000..1aeeca0a081 --- /dev/null +++ b/examples/streams.js @@ -0,0 +1,47 @@ +'use strict'; + +var redis = require('redis'); +var client1 = redis.createClient(); +var client2 = redis.createClient(); +var client3 = redis.createClient(); + +client1.xadd('mystream', '*', 'field1', 'm1', function (err) { + if(err){ + return console.error(err); + } + client1.xgroup('CREATE', 'mystream', 'mygroup', '$', function (err) { + if(err){ + return console.error(err); + } + }); + + client2.xreadgroup('GROUP', 'mygroup', 'consumer', 'Block', 1000, + 'STREAMS', 'mystream', '>', function (err, stream) { + if(err){ + return console.error(err); + } + console.log('client2 ' + stream); + }); + + client3.xreadgroup('GROUP', 'mygroup', 'consumer', 'Block', 1000, + 'STREAMS', 'mystream', '>', function (err, stream) { + if(err){ + return console.error(err); + } + console.log('client3 ' + stream); + }); + + + client1.xadd('mystream', '*', 'field1', 'm2', function (err) { + if(err){ + return console.error(err); + } + }); + + client1.xadd('mystream', '*', 'field1', 'm3', function (err) { + if(err){ + return console.error(err); + } + }); + +}); From 383f09702981fe55fed914af8d725084fb2ce0fe Mon Sep 17 00:00:00 2001 From: Guy Korland Date: Wed, 14 Nov 2018 04:51:56 +0200 Subject: [PATCH 0761/1748] Added 'NOACK' to xreadgroup call Added 'NOACK' to xreadgroup call, to make sure entries are auto ACK --- examples/streams.js | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/examples/streams.js b/examples/streams.js index 1aeeca0a081..2fda1293ce4 100644 --- a/examples/streams.js +++ b/examples/streams.js @@ -15,7 +15,7 @@ client1.xadd('mystream', '*', 'field1', 'm1', function (err) { } }); - client2.xreadgroup('GROUP', 'mygroup', 'consumer', 'Block', 1000, + client2.xreadgroup('GROUP', 'mygroup', 'consumer', 'Block', 1000, 'NOACK', 'STREAMS', 'mystream', '>', function (err, stream) { if(err){ return console.error(err); @@ -23,7 +23,7 @@ client1.xadd('mystream', '*', 'field1', 'm1', function (err) { console.log('client2 ' + stream); }); - client3.xreadgroup('GROUP', 'mygroup', 'consumer', 'Block', 1000, + client3.xreadgroup('GROUP', 'mygroup', 'consumer', 'Block', 1000, 'NOACK', 'STREAMS', 'mystream', '>', function (err, stream) { if(err){ return console.error(err); From 8ee1b9822b1d42f57bb84dcc86d8727266bd5e4c Mon Sep 17 00:00:00 2001 From: Xin Date: Thu, 29 Nov 2018 11:59:44 +1100 Subject: [PATCH 0762/1748] add option socket_initialdelay --- index.js | 6 +++++- 1 file changed, 5 insertions(+), 1 deletion(-) diff --git a/index.js b/index.js index 271d53db437..ce667a9dd49 100644 --- a/index.js +++ b/index.js @@ -101,6 +101,10 @@ function RedisClient (options, stream) { if (options.socket_keepalive === undefined) { options.socket_keepalive = true; } + if (options.socket_initialdelay === undefined) { + options.socket_initialdelay = 0; + // set default to 0, which is aligned to https://nodejs.org/api/net.html#net_socket_setkeepalive_enable_initialdelay + } for (var command in options.rename_commands) { options.rename_commands[command.toLowerCase()] = options.rename_commands[command]; } @@ -416,7 +420,7 @@ RedisClient.prototype.on_connect = function () { this.connected = true; this.ready = false; this.emitted_end = false; - this.stream.setKeepAlive(this.options.socket_keepalive); + this.stream.setKeepAlive(this.options.socket_keepalive, this.options.socket_initialdelay); this.stream.setTimeout(0); this.emit('connect'); From 3a615e825208d99630d9f64a4dd38276a803fb23 Mon Sep 17 00:00:00 2001 From: Xin Date: Thu, 29 Nov 2018 12:02:31 +1100 Subject: [PATCH 0763/1748] add comment for socket_initialdelay --- README.md | 1 + 1 file changed, 1 insertion(+) diff --git a/README.md b/README.md index eb05cc864f6..b2089159919 100644 --- a/README.md +++ b/README.md @@ -247,6 +247,7 @@ __Note:__ Using `'rediss://...` for the protocol in a `redis_url` will enable a | return_buffers | false | If set to `true`, then all replies will be sent to callbacks as Buffers instead of Strings. | | detect_buffers | false | If set to `true`, then replies will be sent to callbacks as Buffers. This option lets you switch between Buffers and Strings on a per-command basis, whereas `return_buffers` applies to every command on a client. __Note__: This doesn't work properly with the pubsub mode. A subscriber has to either always return Strings or Buffers. | | socket_keepalive | true | If set to `true`, the keep-alive functionality is enabled on the underlying socket. | +| socket_initialdelay | 0 | Initial Delay in milliseconds, and this will also behave the interval keep alive message sending to Redis. | | no_ready_check | false | When a connection is established to the Redis server, the server might still be loading the database from disk. While loading, the server will not respond to any commands. To work around this, `node_redis` has a "ready check" which sends the `INFO` command to the server. The response from the `INFO` command indicates whether the server is ready for more commands. When ready, `node_redis` emits a `ready` event. Setting `no_ready_check` to `true` will inhibit this check. | | enable_offline_queue | true | By default, if there is no active connection to the Redis server, commands are added to a queue and are executed once the connection has been established. Setting `enable_offline_queue` to `false` will disable this feature and the callback will be executed immediately with an error, or an error will be emitted if no callback is specified. | | retry_max_delay | null | __Deprecated__ _Please use `retry_strategy` instead._ By default, every time the client tries to connect and fails, the reconnection delay almost doubles. This delay normally grows infinitely, but setting `retry_max_delay` limits it to the maximum value provided in milliseconds. | From 34eac0db40967b231907a5835453da75de93a91f Mon Sep 17 00:00:00 2001 From: Thomas Hunter II Date: Thu, 27 Dec 2018 14:53:01 -0800 Subject: [PATCH 0764/1748] Fix typo in createClient.js --- lib/createClient.js | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/lib/createClient.js b/lib/createClient.js index 13c213470a5..1533d73d1b7 100644 --- a/lib/createClient.js +++ b/lib/createClient.js @@ -72,7 +72,7 @@ module.exports = function createClient (port_arg, host_arg, options) { options.host = options.host || host_arg; if (port_arg && arguments.length !== 1) { - throw new TypeError('To many arguments passed to createClient. Please only pass the options object'); + throw new TypeError('Too many arguments passed to createClient. Please only pass the options object'); } } From 06635b77cc07ac34ebde633264f668f837ae9f97 Mon Sep 17 00:00:00 2001 From: andy addington Date: Thu, 17 Jan 2019 06:56:41 -0800 Subject: [PATCH 0765/1748] test: Fix typo in assertEquals message --- test/unify_options.spec.js | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/test/unify_options.spec.js b/test/unify_options.spec.js index eb441d81a66..40bdf22a8b5 100644 --- a/test/unify_options.spec.js +++ b/test/unify_options.spec.js @@ -209,7 +209,7 @@ describe('createClient options', function () { }, undefined); throw new Error('failed'); } catch (err) { - assert.strictEqual(err.message, 'To many arguments passed to createClient. Please only pass the options object'); + assert.strictEqual(err.message, 'Too many arguments passed to createClient. Please only pass the options object'); } }); From 171cbc5a096745a3247fafc1785f93e1a709aaf1 Mon Sep 17 00:00:00 2001 From: Andy Ganchrow Date: Tue, 5 Feb 2019 09:02:04 -0800 Subject: [PATCH 0766/1748] Fix typo in index.js --- index.js | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/index.js b/index.js index ce667a9dd49..ae0848cd629 100644 --- a/index.js +++ b/index.js @@ -205,7 +205,7 @@ function create_parser (self) { err.message += '. Please report this.'; self.ready = false; self.flush_and_error({ - message: 'Fatal error encountert. Command aborted.', + message: 'Fatal error encountered. Command aborted.', code: 'NR_FATAL' }, { error: err, @@ -1106,4 +1106,4 @@ require('./lib/individualCommands'); require('./lib/extendedApi'); //enables adding new commands (for modules and new commands) -exports.addCommand = exports.add_command = require('./lib/commands'); \ No newline at end of file +exports.addCommand = exports.add_command = require('./lib/commands'); From 128ca62346bf15c3813d9e45fe194ec1e4ca8f3c Mon Sep 17 00:00:00 2001 From: Andy Ganchrow Date: Tue, 5 Feb 2019 11:06:16 -0800 Subject: [PATCH 0767/1748] test: Fix typo encountert in test --- test/node_redis.spec.js | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/test/node_redis.spec.js b/test/node_redis.spec.js index 3375b759d91..fe9a35c2799 100644 --- a/test/node_redis.spec.js +++ b/test/node_redis.spec.js @@ -904,7 +904,7 @@ describe('The node_redis client', function () { }); client.once('ready', function () { client.set('foo', 'bar', function (err, res) { - assert.strictEqual(err.message, 'Fatal error encountert. Command aborted. It might have been processed.'); + assert.strictEqual(err.message, 'Fatal error encountered. Command aborted. It might have been processed.'); assert.strictEqual(err.code, 'NR_FATAL'); assert(err instanceof redis.AbortError); error = err.origin; From 50b4009005a7adf1d3fa2af2739dac402ca7f9fc Mon Sep 17 00:00:00 2001 From: JinHyuk Kim Date: Wed, 6 Feb 2019 23:19:03 +0900 Subject: [PATCH 0768/1748] fix incorrect code for eslint rules (#1367) * fix code for eslint rules --- test/batch.spec.js | 68 +++++++++++++++++------------------ test/commands/select.spec.js | 4 +-- test/connection.spec.js | 2 +- test/lib/redis-process.js | 25 ++++++------- test/multi.spec.js | 70 ++++++++++++++++++------------------ test/node_redis.spec.js | 2 +- test/tls.spec.js | 12 +++---- 7 files changed, 92 insertions(+), 91 deletions(-) diff --git a/test/batch.spec.js b/test/batch.spec.js index 762b2f70491..df038e77e6a 100644 --- a/test/batch.spec.js +++ b/test/batch.spec.js @@ -187,12 +187,12 @@ describe("The 'batch' method", function () { ['del', 'some set'], ['smembers', 'some set', undefined] // The explicit undefined is handled as a callback that is undefined ]) - .scard('some set') - .exec(function (err, replies) { - assert.strictEqual(4, replies[0].length); - assert.strictEqual(0, replies[2].length); - return done(); - }); + .scard('some set') + .exec(function (err, replies) { + assert.strictEqual(4, replies[0].length); + assert.strictEqual(0, replies[2].length); + return done(); + }); }); it('allows multiple operations to be performed using constructor with all kinds of syntax', function (done) { @@ -213,29 +213,29 @@ describe("The 'batch' method", function () { ['HMSET', 'batchhmset', ['batchbar', 'batchbaz']], ['hmset', 'batchhmset', ['batchbar', 'batchbaz'], helper.isString('OK')], ]) - .hmget(now, 123456789, 'otherTypes') - .hmget('key2', arr2, function noop () {}) - .hmget(['batchhmset2', 'some manner of key', 'batchbar3']) - .mget('batchfoo2', ['batchfoo3', 'batchfoo'], function (err, res) { - assert.strictEqual(res[0], 'batchbar2'); - assert.strictEqual(res[1], 'batchbar3'); - assert.strictEqual(res[2], null); - }) - .exec(function (err, replies) { - assert.equal(arr.length, 3); - assert.equal(arr2.length, 2); - assert.equal(arr3.length, 3); - assert.equal(arr4.length, 3); - assert.strictEqual(null, err); - assert.equal(replies[10][1], '555'); - assert.equal(replies[11][0], 'a type of value'); - assert.strictEqual(replies[12][0], null); - assert.equal(replies[12][1], 'test'); - assert.equal(replies[13][0], 'batchbar2'); - assert.equal(replies[13].length, 3); - assert.equal(replies.length, 14); - return done(); - }); + .hmget(now, 123456789, 'otherTypes') + .hmget('key2', arr2, function noop () {}) + .hmget(['batchhmset2', 'some manner of key', 'batchbar3']) + .mget('batchfoo2', ['batchfoo3', 'batchfoo'], function (err, res) { + assert.strictEqual(res[0], 'batchbar2'); + assert.strictEqual(res[1], 'batchbar3'); + assert.strictEqual(res[2], null); + }) + .exec(function (err, replies) { + assert.equal(arr.length, 3); + assert.equal(arr2.length, 2); + assert.equal(arr3.length, 3); + assert.equal(arr4.length, 3); + assert.strictEqual(null, err); + assert.equal(replies[10][1], '555'); + assert.equal(replies[11][0], 'a type of value'); + assert.strictEqual(replies[12][0], null); + assert.equal(replies[12][1], 'test'); + assert.equal(replies[13][0], 'batchbar2'); + assert.equal(replies[13].length, 3); + assert.equal(replies.length, 14); + return done(); + }); }); it('converts a non string key to a string', function (done) { @@ -316,11 +316,11 @@ describe("The 'batch' method", function () { ['mget', ['batchfoo', 'some', 'random value', 'keys']], ['incr', 'batchfoo'] ]) - .exec(function (err, replies) { - assert.strictEqual(replies.length, 2); - assert.strictEqual(replies[0].length, 4); - return done(); - }); + .exec(function (err, replies) { + assert.strictEqual(replies.length, 2); + assert.strictEqual(replies[0].length, 4); + return done(); + }); }); it('allows multiple operations to be performed on a hash', function (done) { diff --git a/test/commands/select.spec.js b/test/commands/select.spec.js index 1dff2025d9c..66a66f389b0 100644 --- a/test/commands/select.spec.js +++ b/test/commands/select.spec.js @@ -72,7 +72,7 @@ describe("The 'select' method", function () { assert.strictEqual(client.selected_db, undefined, 'default db should be undefined'); client.select(9999, function (err) { assert.equal(err.code, 'ERR'); - assert((err.message == 'ERR DB index is out of range' || err.message == 'ERR invalid DB index')); + assert((err.message === 'ERR DB index is out of range' || err.message === 'ERR invalid DB index')); done(); }); }); @@ -97,7 +97,7 @@ describe("The 'select' method", function () { client.on('error', function (err) { assert.strictEqual(err.command, 'SELECT'); - assert((err.message == 'ERR DB index is out of range' || err.message == 'ERR invalid DB index')); + assert((err.message === 'ERR DB index is out of range' || err.message === 'ERR invalid DB index')); done(); }); diff --git a/test/connection.spec.js b/test/connection.spec.js index f92dca1949b..c33d1b8a837 100644 --- a/test/connection.spec.js +++ b/test/connection.spec.js @@ -376,7 +376,7 @@ describe('connection tests', function () { var add = process.platform !== 'win32' ? 15 : 200; var now = Date.now(); assert(now - time < connect_timeout + add, 'The real timeout time should be below ' + (connect_timeout + add) + 'ms but is: ' + (now - time)); - // Timers sometimes trigger early (e.g. 1ms to early) + // Timers sometimes trigger early (e.g. 1ms to early) assert(now - time >= connect_timeout - 5, 'The real timeout time should be above ' + connect_timeout + 'ms, but it is: ' + (now - time)); done(); }); diff --git a/test/lib/redis-process.js b/test/lib/redis-process.js index e98f7fbffcd..c46fd8d70f0 100644 --- a/test/lib/redis-process.js +++ b/test/lib/redis-process.js @@ -27,20 +27,21 @@ function waitForRedis (available, cb, port) { bluebird.join( tcpPortUsed.check(port, '127.0.0.1'), tcpPortUsed.check(port, '::1'), - function (ipV4, ipV6) { - if (ipV6 === available && ipV4 === available) { - if (fs.existsSync(socket) === available) { - clearInterval(id); - return cb(); + function (ipV4, ipV6) { + if (ipV6 === available && ipV4 === available) { + if (fs.existsSync(socket) === available) { + clearInterval(id); + return cb(); + } + // The same message applies for can't stop but we ignore that case + throw new Error('Port ' + port + ' is already in use. Tests can\'t start.\n'); } - // The same message applies for can't stop but we ignore that case - throw new Error('Port ' + port + ' is already in use. Tests can\'t start.\n'); - } - if (Date.now() - time > 6000) { - throw new Error('Redis could not start on port ' + (port || config.PORT) + '\n'); + if (Date.now() - time > 6000) { + throw new Error('Redis could not start on port ' + (port || config.PORT) + '\n'); + } + running = false; } - running = false; - }).catch(function (err) { + ).catch(function (err) { console.error('\x1b[31m' + err.stack + '\x1b[0m\n'); process.exit(1); }); diff --git a/test/multi.spec.js b/test/multi.spec.js index e654375dc8d..33ffdbe16ce 100644 --- a/test/multi.spec.js +++ b/test/multi.spec.js @@ -379,12 +379,12 @@ describe("The 'multi' method", function () { ['del', 'some set'], ['smembers', 'some set'] ]) - .scard('some set') - .exec(function (err, replies) { - assert.strictEqual(4, replies[0].length); - assert.strictEqual(0, replies[2].length); - return done(); - }); + .scard('some set') + .exec(function (err, replies) { + assert.strictEqual(4, replies[0].length); + assert.strictEqual(0, replies[2].length); + return done(); + }); }); it('allows multiple operations to be performed using constructor with all kinds of syntax', function (done) { @@ -406,30 +406,30 @@ describe("The 'multi' method", function () { ['HMSET', 'multihmset', ['multibar', 'multibaz'], undefined], // undefined is used as a explicit not set callback variable ['hmset', 'multihmset', ['multibar', 'multibaz'], helper.isString('OK')], ]) - .hmget(now, 123456789, 'otherTypes') - .hmget('key2', arr2, function noop () {}) - .hmget(['multihmset2', 'some manner of key', 'multibar3']) - .mget('multifoo2', ['multifoo3', 'multifoo'], function (err, res) { - assert(res[0], 'multifoo3'); - assert(res[1], 'multifoo'); - called = true; - }) - .exec(function (err, replies) { - assert(called); - assert.equal(arr.length, 3); - assert.equal(arr2.length, 2); - assert.equal(arr3.length, 3); - assert.equal(arr4.length, 3); - assert.strictEqual(null, err); - assert.equal(replies[10][1], '555'); - assert.equal(replies[11][0], 'a type of value'); - assert.strictEqual(replies[12][0], null); - assert.equal(replies[12][1], 'test'); - assert.equal(replies[13][0], 'multibar2'); - assert.equal(replies[13].length, 3); - assert.equal(replies.length, 14); - return done(); - }); + .hmget(now, 123456789, 'otherTypes') + .hmget('key2', arr2, function noop () {}) + .hmget(['multihmset2', 'some manner of key', 'multibar3']) + .mget('multifoo2', ['multifoo3', 'multifoo'], function (err, res) { + assert(res[0], 'multifoo3'); + assert(res[1], 'multifoo'); + called = true; + }) + .exec(function (err, replies) { + assert(called); + assert.equal(arr.length, 3); + assert.equal(arr2.length, 2); + assert.equal(arr3.length, 3); + assert.equal(arr4.length, 3); + assert.strictEqual(null, err); + assert.equal(replies[10][1], '555'); + assert.equal(replies[11][0], 'a type of value'); + assert.strictEqual(replies[12][0], null); + assert.equal(replies[12][1], 'test'); + assert.equal(replies[13][0], 'multibar2'); + assert.equal(replies[13].length, 3); + assert.equal(replies.length, 14); + return done(); + }); }); it('converts a non string key to a string', function (done) { @@ -505,11 +505,11 @@ describe("The 'multi' method", function () { ['mget', ['multifoo', 'some', 'random value', 'keys']], ['incr', 'multifoo'] ]) - .exec(function (err, replies) { - assert.strictEqual(replies.length, 2); - assert.strictEqual(replies[0].length, 4); - return done(); - }); + .exec(function (err, replies) { + assert.strictEqual(replies.length, 2); + assert.strictEqual(replies[0].length, 4); + return done(); + }); }); it('allows multiple operations to be performed on a hash', function (done) { diff --git a/test/node_redis.spec.js b/test/node_redis.spec.js index fe9a35c2799..e553f8521e8 100644 --- a/test/node_redis.spec.js +++ b/test/node_redis.spec.js @@ -122,7 +122,7 @@ describe('The node_redis client', function () { it('check if all new options replaced the old ones', function (done) { client.selected_db = 1; var client2 = client.duplicate({ - db: 2, + db: 2, no_ready_check: true }); assert(client.connected); diff --git a/test/tls.spec.js b/test/tls.spec.js index 3b0e18197ac..e3c758285be 100644 --- a/test/tls.spec.js +++ b/test/tls.spec.js @@ -110,17 +110,17 @@ describe('TLS connection tests', function () { }); describe('using rediss as url protocol', function (done) { - var tls_connect = tls.connect + var tls_connect = tls.connect; beforeEach(function () { tls.connect = function (options) { - options = utils.clone(options) + options = utils.clone(options); options.ca = tls_options.ca; return tls_connect.call(tls, options); - } - }) + }; + }); afterEach(function () { tls.connect = tls_connect; - }) + }); it('connect with tls when rediss is used as the protocol', function (done) { if (skip) this.skip(); client = redis.createClient('rediss://localhost:' + tls_port); @@ -129,7 +129,7 @@ describe('TLS connection tests', function () { client.set('foo', 'bar'); client.get('foo', helper.isString('bar', done)); }); - }) + }); it('fails to connect because the cert is not correct', function (done) { if (skip) this.skip(); From 12265a5079a133d2003bef9cdb0f2deee0251518 Mon Sep 17 00:00:00 2001 From: Bryan English Date: Tue, 17 Jul 2018 20:48:48 -0700 Subject: [PATCH 0769/1748] Remove clientError handler This event is never emitted on net.Socket or tls.TLSSocket streams in any of the supported versions of Node.js. --- index.js | 6 ------ 1 file changed, 6 deletions(-) diff --git a/index.js b/index.js index ae0848cd629..c43203a7f64 100644 --- a/index.js +++ b/index.js @@ -283,12 +283,6 @@ RedisClient.prototype.create_stream = function () { self.on_error(err); }); - /* istanbul ignore next: difficult to test and not important as long as we keep this listener */ - this.stream.on('clientError', function (err) { - debug('clientError occured'); - self.on_error(err); - }); - this.stream.once('close', function (hadError) { self.connection_gone('close'); }); From f384e8630203a479c55a89054f37b71394169a84 Mon Sep 17 00:00:00 2001 From: Mario de Frutos Dieguez Date: Tue, 7 May 2019 22:50:25 +0200 Subject: [PATCH 0770/1748] Added timestamp to debug traces (#1426) * Added timestamp to debug traces * Make it compatible with node 0.10 and 0.12 --- lib/debug.js | 4 +++- test/commands/blpop.spec.js | 2 +- 2 files changed, 4 insertions(+), 2 deletions(-) diff --git a/lib/debug.js b/lib/debug.js index 0e6333f2ec3..d69c1ea6246 100644 --- a/lib/debug.js +++ b/lib/debug.js @@ -4,7 +4,9 @@ var index = require('../'); function debug () { if (index.debug_mode) { - console.error.apply(null, arguments); + var data = Array.prototype.slice.call(arguments); + data.unshift(new Date().toISOString()); + console.error.apply(null, data); } } diff --git a/test/commands/blpop.spec.js b/test/commands/blpop.spec.js index d68521e6b5e..e8d3f59aabc 100644 --- a/test/commands/blpop.spec.js +++ b/test/commands/blpop.spec.js @@ -31,7 +31,7 @@ describe("The 'blpop' method", function () { }); client.rpush('blocking list', 'initial value', helper.isNumber(1)); unhookIntercept(); - assert(/^Send 127\.0\.0\.1:6379 id [0-9]+: \*3\r\n\$5\r\nrpush\r\n\$13\r\nblocking list\r\n\$13\r\ninitial value\r\n\n$/.test(text)); + assert(/Send 127\.0\.0\.1:6379 id [0-9]+: \*3\r\n\$5\r\nrpush\r\n\$13\r\nblocking list\r\n\$13\r\ninitial value\r\n\n$/.test(text)); redis.debug_mode = false; bclient.blpop('blocking list', 0, function (err, value) { assert.strictEqual(value[0], 'blocking list'); From a60261da0461d4bb114b1533fada87e97fa985c8 Mon Sep 17 00:00:00 2001 From: Arun Raghavan Date: Tue, 7 May 2019 20:51:11 +0000 Subject: [PATCH 0771/1748] Update to recent redis-commands (#1425) This includes commands from Redis 5.0, including streams-related API. --- package.json | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/package.json b/package.json index c4e722ac010..4ec213335e8 100644 --- a/package.json +++ b/package.json @@ -26,7 +26,7 @@ }, "dependencies": { "denque": "^1.2.3", - "redis-commands": "^1.2.0", + "redis-commands": "^1.4.0", "redis-parser": "^2.6.0" }, "engines": { From 87e382a1df6b829a804f574571014e61d4c53566 Mon Sep 17 00:00:00 2001 From: medifle <13296643+medifle@users.noreply.github.com> Date: Mon, 3 Feb 2020 08:59:34 -0500 Subject: [PATCH 0772/1748] docs(*): fix async function syntax error (#1490) --- README.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/README.md b/README.md index b2089159919..329d0466533 100644 --- a/README.md +++ b/README.md @@ -74,7 +74,7 @@ return getAsync('foo').then(function(res) { or using [async await](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Statements/async_function): ```js -async myFunc() { +async function myFunc() { const res = await getAsync('foo'); console.log(res); } From 1f082aca54f191f5aacf12ab95d01597cc95d4fa Mon Sep 17 00:00:00 2001 From: German Capuano Date: Mon, 3 Feb 2020 09:45:00 -0500 Subject: [PATCH 0773/1748] docs(*): additional clarification of Pub/Sub section (#1489) The previous phrasing wasn't clear to me. The oxford comma is necessary to avoid ambiguity. --- README.md | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/README.md b/README.md index 329d0466533..91695a8b8bc 100644 --- a/README.md +++ b/README.md @@ -532,8 +532,8 @@ sub.subscribe("a nice channel"); ``` When a client issues a `SUBSCRIBE` or `PSUBSCRIBE`, that connection is put into -a "subscriber" mode. At that point, only commands that modify the subscription -set are valid and quit (and depending on the redis version ping as well). When +a "subscriber" mode. At that point, the only valid commands are those that modify the subscription +set, and quit (also ping on some redis versions). When the subscription set is empty, the connection is put back into regular mode. If you need to send regular commands to Redis while in subscriber mode, just From 78936ac50c43ceb74654501a5da4b2994efe1217 Mon Sep 17 00:00:00 2001 From: Vissarut Tantiwattanarom Date: Mon, 3 Feb 2020 21:53:56 +0400 Subject: [PATCH 0774/1748] feat(parser): update to latest Redis parser & errors (#1470) * Update redis errors and redis parser * Fix lint errors * Add node 12 to travis ci * Add appveyor node 12 * Fix some of existing errors This drops support for hiredis. --- .travis.yml | 1 + appveyor.yml | 1 + benchmarks/diff_multi_bench_output.js | 2 +- examples/streams.js | 76 +++++++++++++-------------- index.js | 7 +-- lib/customErrors.js | 3 +- package.json | 3 +- test/node_redis.spec.js | 2 +- test/rename.spec.js | 4 +- 9 files changed, 51 insertions(+), 48 deletions(-) diff --git a/.travis.yml b/.travis.yml index f2422e979bd..09398ddac0a 100644 --- a/.travis.yml +++ b/.travis.yml @@ -14,6 +14,7 @@ node_js: - "4" - "6" - "8" + - "12" after_success: npm run coveralls before_script: # Add an IPv6 config - see the corresponding Travis issue diff --git a/appveyor.yml b/appveyor.yml index 7930f05f8ab..e3d2c2a940a 100644 --- a/appveyor.yml +++ b/appveyor.yml @@ -8,6 +8,7 @@ environment: - nodejs_version: "4" - nodejs_version: "6" - nodejs_version: "8" + - nodejs_version: "12" pull_requests: do_not_increment_build_number: true diff --git a/benchmarks/diff_multi_bench_output.js b/benchmarks/diff_multi_bench_output.js index dc0d3c227d9..c3ed47a5fb5 100755 --- a/benchmarks/diff_multi_bench_output.js +++ b/benchmarks/diff_multi_bench_output.js @@ -2,7 +2,7 @@ var fs = require('fs'); var metrics = require('metrics'); - // `node diff_multi_bench_output.js beforeBench.txt afterBench.txt` +// `node diff_multi_bench_output.js beforeBench.txt afterBench.txt` var file1 = process.argv[2]; var file2 = process.argv[3]; diff --git a/examples/streams.js b/examples/streams.js index 2fda1293ce4..726e4adf920 100644 --- a/examples/streams.js +++ b/examples/streams.js @@ -5,43 +5,43 @@ var client1 = redis.createClient(); var client2 = redis.createClient(); var client3 = redis.createClient(); -client1.xadd('mystream', '*', 'field1', 'm1', function (err) { - if(err){ - return console.error(err); - } - client1.xgroup('CREATE', 'mystream', 'mygroup', '$', function (err) { - if(err){ - return console.error(err); - } - }); - - client2.xreadgroup('GROUP', 'mygroup', 'consumer', 'Block', 1000, 'NOACK', - 'STREAMS', 'mystream', '>', function (err, stream) { - if(err){ - return console.error(err); - } - console.log('client2 ' + stream); - }); - - client3.xreadgroup('GROUP', 'mygroup', 'consumer', 'Block', 1000, 'NOACK', - 'STREAMS', 'mystream', '>', function (err, stream) { - if(err){ - return console.error(err); - } - console.log('client3 ' + stream); - }); - - - client1.xadd('mystream', '*', 'field1', 'm2', function (err) { - if(err){ - return console.error(err); - } - }); - - client1.xadd('mystream', '*', 'field1', 'm3', function (err) { - if(err){ - return console.error(err); - } - }); +client1.xadd('mystream', '*', 'field1', 'm1', function (err) { + if (err) { + return console.error(err); + } + client1.xgroup('CREATE', 'mystream', 'mygroup', '$', function (err) { + if (err) { + return console.error(err); + } + }); + + client2.xreadgroup('GROUP', 'mygroup', 'consumer', 'Block', 1000, 'NOACK', + 'STREAMS', 'mystream', '>', function (err, stream) { + if (err) { + return console.error(err); + } + console.log('client2 ' + stream); + }); + + client3.xreadgroup('GROUP', 'mygroup', 'consumer', 'Block', 1000, 'NOACK', + 'STREAMS', 'mystream', '>', function (err, stream) { + if (err) { + return console.error(err); + } + console.log('client3 ' + stream); + }); + + + client1.xadd('mystream', '*', 'field1', 'm2', function (err) { + if (err) { + return console.error(err); + } + }); + + client1.xadd('mystream', '*', 'field1', 'm3', function (err) { + if (err) { + return console.error(err); + } + }); }); diff --git a/index.js b/index.js index c43203a7f64..402b9b1e476 100644 --- a/index.js +++ b/index.js @@ -9,6 +9,7 @@ var Queue = require('denque'); var errorClasses = require('./lib/customErrors'); var EventEmitter = require('events'); var Parser = require('redis-parser'); +var RedisErrors = require('redis-errors'); var commands = require('redis-commands'); var debug = require('./lib/debug'); var unifyOptions = require('./lib/createClient'); @@ -1090,9 +1091,9 @@ exports.RedisClient = RedisClient; exports.print = utils.print; exports.Multi = require('./lib/multi'); exports.AbortError = errorClasses.AbortError; -exports.RedisError = Parser.RedisError; -exports.ParserError = Parser.ParserError; -exports.ReplyError = Parser.ReplyError; +exports.RedisError = RedisErrors.RedisError; +exports.ParserError = RedisErrors.ParserError; +exports.ReplyError = RedisErrors.ReplyError; exports.AggregateError = errorClasses.AggregateError; // Add all redis commands / node_redis api to the client diff --git a/lib/customErrors.js b/lib/customErrors.js index d9b34421a71..2483db0d8d4 100644 --- a/lib/customErrors.js +++ b/lib/customErrors.js @@ -2,14 +2,13 @@ var util = require('util'); var assert = require('assert'); -var RedisError = require('redis-parser').RedisError; +var RedisError = require('redis-errors').RedisError; var ADD_STACKTRACE = false; function AbortError (obj, stack) { assert(obj, 'The options argument is required'); assert.strictEqual(typeof obj, 'object', 'The options argument has to be of type object'); - RedisError.call(this, obj.message, ADD_STACKTRACE); Object.defineProperty(this, 'message', { value: obj.message || '', configurable: true, diff --git a/package.json b/package.json index 4ec213335e8..d294765fc97 100644 --- a/package.json +++ b/package.json @@ -27,7 +27,8 @@ "dependencies": { "denque": "^1.2.3", "redis-commands": "^1.4.0", - "redis-parser": "^2.6.0" + "redis-errors": "^1.2.0", + "redis-parser": "^3.0.0" }, "engines": { "node": ">=0.10.0" diff --git a/test/node_redis.spec.js b/test/node_redis.spec.js index e553f8521e8..82b3e4e09f3 100644 --- a/test/node_redis.spec.js +++ b/test/node_redis.spec.js @@ -359,7 +359,7 @@ describe('The node_redis client', function () { it('send_command with callback as args', function (done) { client.send_command('abcdef', function (err, res) { - assert.strictEqual(err.message, "ERR unknown command 'abcdef'"); + assert.strictEqual(err.message, 'ERR unknown command `abcdef`, with args beginning with: '); done(); }); }); diff --git a/test/rename.spec.js b/test/rename.spec.js index b98661e9f5f..b8f7c65d0d8 100644 --- a/test/rename.spec.js +++ b/test/rename.spec.js @@ -50,7 +50,7 @@ describe('rename commands', function () { }); client.get('key', function (err, reply) { - assert.strictEqual(err.message, "ERR unknown command 'get'"); + assert.strictEqual(err.message, 'ERR unknown command `get`, with args beginning with: `key`, '); assert.strictEqual(err.command, 'GET'); assert.strictEqual(reply, undefined); }); @@ -108,7 +108,7 @@ describe('rename commands', function () { multi.exec(function (err, res) { assert(err); assert.strictEqual(err.message, 'EXECABORT Transaction discarded because of previous errors.'); - assert.strictEqual(err.errors[0].message, "ERR unknown command 'get'"); + assert.strictEqual(err.errors[0].message, 'ERR unknown command `get`, with args beginning with: `key`, '); assert.strictEqual(err.errors[0].command, 'GET'); assert.strictEqual(err.code, 'EXECABORT'); assert.strictEqual(err.errors[0].code, 'ERR'); From 1f5555d96eedecf7995e3928748533f992c15450 Mon Sep 17 00:00:00 2001 From: "Indospace.io" Date: Mon, 3 Feb 2020 09:57:59 -0800 Subject: [PATCH 0775/1748] feat(commands): update redis-commands version (#1454) update redis-commands and redis-parser Co-authored-by: Mike Diarmid --- package.json | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/package.json b/package.json index d294765fc97..827083920e6 100644 --- a/package.json +++ b/package.json @@ -26,7 +26,7 @@ }, "dependencies": { "denque": "^1.2.3", - "redis-commands": "^1.4.0", + "redis-commands": "^1.5.0", "redis-errors": "^1.2.0", "redis-parser": "^3.0.0" }, From c78e55d874dc1a791ee75d02b30196a01d9202a4 Mon Sep 17 00:00:00 2001 From: Salakar Date: Thu, 6 Feb 2020 21:25:28 +0000 Subject: [PATCH 0776/1748] tests: use stream timeout property instead of internal property fixes broken test --- test/connection.spec.js | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/test/connection.spec.js b/test/connection.spec.js index c33d1b8a837..f467f195787 100644 --- a/test/connection.spec.js +++ b/test/connection.spec.js @@ -401,10 +401,10 @@ describe('connection tests', function () { connect_timeout: 1000 }); process.nextTick(function () { - assert.strictEqual(client.stream._idleTimeout, 1000); + assert.strictEqual(client.stream.timeout, 1000); }); client.on('connect', function () { - assert.strictEqual(client.stream._idleTimeout, -1); + assert.strictEqual(client.stream.timeout, 0); assert.strictEqual(client.stream.listeners('timeout').length, 0); client.on('ready', done); }); From fbdac9e0ad338e730445116daabdde1893f9f7ba Mon Sep 17 00:00:00 2001 From: Salakar Date: Thu, 6 Feb 2020 21:26:53 +0000 Subject: [PATCH 0777/1748] tests: remove hiredis from testing suite as parser no longer supports hiredis --- test/helper.js | 5 ----- test/pubsub.spec.js | 8 ++------ 2 files changed, 2 insertions(+), 11 deletions(-) diff --git a/test/helper.js b/test/helper.js index e851b7f7b35..29a07d6f319 100644 --- a/test/helper.js +++ b/test/helper.js @@ -163,11 +163,6 @@ module.exports = { } var parsers = ['javascript']; var protocols = ['IPv4']; - // The js parser works the same as the hiredis parser, just activate this if you want to be on the safe side - // try { - // require('hiredis'); - // parsers.push('hiredis'); - // } catch (e) {/* ignore eslint */} if (process.platform !== 'win32') { protocols.push('IPv6', '/tmp/redis.sock'); } diff --git a/test/pubsub.spec.js b/test/pubsub.spec.js index 94b0fc1880c..c017cdb36fb 100644 --- a/test/pubsub.spec.js +++ b/test/pubsub.spec.js @@ -515,8 +515,8 @@ describe('publish/subscribe', function () { sub.stream.once('data', function () { assert.strictEqual(sub.message_buffers, false); assert.strictEqual(sub.shouldBuffer, false); - sub.on('pmessageBuffer', function (pattern, channel, message) { - if (parser !== 'javascript' && typeof pattern === 'string') { + sub.on('pmessageBuffer', function (pattern, channel) { + if (typeof pattern === 'string') { pattern = new Buffer(pattern); channel = new Buffer(channel); } @@ -524,10 +524,6 @@ describe('publish/subscribe', function () { assert.strictEqual(channel.inspect(), new Buffer('/foo').inspect()); sub.quit(end); }); - if (parser === 'javascript') { - assert.notStrictEqual(sub.message_buffers, sub.buffers); - } - }); var batch = sub.batch(); batch.psubscribe('*'); From 7c551bf4741b97bf5d67271b5a20bc2829cbfb76 Mon Sep 17 00:00:00 2001 From: Salakar Date: Thu, 6 Feb 2020 21:27:49 +0000 Subject: [PATCH 0778/1748] tests: fix tls tests and enable stunnel tests for travis --- test/tls.spec.js | 12 ++++-------- 1 file changed, 4 insertions(+), 8 deletions(-) diff --git a/test/tls.spec.js b/test/tls.spec.js index e3c758285be..127a2cfb8de 100644 --- a/test/tls.spec.js +++ b/test/tls.spec.js @@ -19,9 +19,6 @@ var tls_port = 6380; // Use skip instead of returning to indicate what tests really got skipped var skip = false; -// Wait until stunnel4 is in the travis whitelist -// Check: https://github.com/travis-ci/apt-package-whitelist/issues/403 -// If this is merged, remove the travis env checks describe('TLS connection tests', function () { before(function (done) { @@ -29,9 +26,6 @@ describe('TLS connection tests', function () { if (process.platform === 'win32') { skip = true; console.warn('\nStunnel tests do not work on windows atm. If you think you can fix that, it would be warmly welcome.\n'); - } else if (process.env.TRAVIS === 'true') { - skip = true; - console.warn('\nTravis does not support stunnel right now. Skipping tests.\nCheck: https://github.com/travis-ci/apt-package-whitelist/issues/403\n'); } if (skip) return done(); helper.stopStunnel(function () { @@ -58,7 +52,7 @@ describe('TLS connection tests', function () { client = redis.createClient({ connect_timeout: connect_timeout, port: tls_port, - tls: tls_options + tls: utils.clone(tls_options) }); var time = 0; assert.strictEqual(client.address, '127.0.0.1:' + tls_port); @@ -109,12 +103,14 @@ describe('TLS connection tests', function () { client.get('foo', helper.isString('bar', done)); }); - describe('using rediss as url protocol', function (done) { + describe('using rediss as url protocol', function () { var tls_connect = tls.connect; beforeEach(function () { tls.connect = function (options) { options = utils.clone(options); options.ca = tls_options.ca; + options.servername = 'redis.js.org'; + options.rejectUnauthorized = true; return tls_connect.call(tls, options); }; }); From a4fec3c361f513f57bdd5b7ce0fa0a96c03c9260 Mon Sep 17 00:00:00 2001 From: Salakar Date: Thu, 6 Feb 2020 21:29:02 +0000 Subject: [PATCH 0779/1748] tests(travis): drop < node v4, add v13 & install stunnel --- .travis.yml | 10 ++++++++-- 1 file changed, 8 insertions(+), 2 deletions(-) diff --git a/.travis.yml b/.travis.yml index 09398ddac0a..0248be673fd 100644 --- a/.travis.yml +++ b/.travis.yml @@ -9,12 +9,18 @@ addons: packages: - g++-4.8 node_js: - - "0.10" - - "0.12" - "4" - "6" - "8" - "12" + - "13" + +before_install: + - if [[ ! -f downloads/stunnel-5.54.tar.gz ]]; then wget -O downloads/stunnel-5.54.tar.gz ftp://ftp.stunnel.org/stunnel/archive/5.x/stunnel-5.54.tar.gz; fi + - if [[ ! -f ./stunnel-5.54/configure ]]; then tar -xzf downloads/stunnel-5.54.tar.gz; fi + - if [[ ! -f ./stunnel-5.54/src/stunnel ]]; then cd ./stunnel-5.54; ./configure; make; cd ..; fi + - export PATH="$PATH:$(pwd)/stunnel-5.54/src" + after_success: npm run coveralls before_script: # Add an IPv6 config - see the corresponding Travis issue From 17c12339887c4dce5b96e073e48105c4f83076bd Mon Sep 17 00:00:00 2001 From: Salakar Date: Thu, 6 Feb 2020 21:30:15 +0000 Subject: [PATCH 0780/1748] tests(appveyor): drop < node v4 & add v13 --- appveyor.yml | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/appveyor.yml b/appveyor.yml index e3d2c2a940a..42155ac1d23 100644 --- a/appveyor.yml +++ b/appveyor.yml @@ -3,12 +3,11 @@ # Test against these versions of Node.js. environment: matrix: - - nodejs_version: "0.10" - - nodejs_version: "0.12" - nodejs_version: "4" - nodejs_version: "6" - nodejs_version: "8" - nodejs_version: "12" + - nodejs_version: "13" pull_requests: do_not_increment_build_number: true From 694154b377f1ad8e2bcbd67d846301e0275627e6 Mon Sep 17 00:00:00 2001 From: Salakar Date: Thu, 6 Feb 2020 21:33:03 +0000 Subject: [PATCH 0781/1748] chore: add IDE files to .gitignore --- .gitignore | 11 +++++++++++ 1 file changed, 11 insertions(+) diff --git a/.gitignore b/.gitignore index 2290a8ec51d..43529fa00b5 100644 --- a/.gitignore +++ b/.gitignore @@ -7,3 +7,14 @@ coverage stunnel.conf stunnel.pid *.out +package-lock.json + +# IntelliJ IDEs +.idea + +# VisualStudioCode IDEs +.vscode/* +!.vscode/settings.json +!.vscode/tasks.json +!.vscode/launch.json +!.vscode/extensions.json From 0adcb738014b969de9b7bdd4e977cab7693667ef Mon Sep 17 00:00:00 2001 From: Salakar Date: Thu, 6 Feb 2020 21:45:34 +0000 Subject: [PATCH 0782/1748] tests(travis): cache stunnel install --- .travis.yml | 8 ++++++-- 1 file changed, 6 insertions(+), 2 deletions(-) diff --git a/.travis.yml b/.travis.yml index 0248be673fd..e0c8e7d152c 100644 --- a/.travis.yml +++ b/.travis.yml @@ -16,11 +16,15 @@ node_js: - "13" before_install: - - if [[ ! -f downloads/stunnel-5.54.tar.gz ]]; then wget -O downloads/stunnel-5.54.tar.gz ftp://ftp.stunnel.org/stunnel/archive/5.x/stunnel-5.54.tar.gz; fi - - if [[ ! -f ./stunnel-5.54/configure ]]; then tar -xzf downloads/stunnel-5.54.tar.gz; fi + - if [[ ! -f stunnel.tar.gz ]]; then wget -O stunnel.tar.gz ftp://ftp.stunnel.org/stunnel/archive/5.x/stunnel-5.54.tar.gz; fi + - if [[ ! -f ./stunnel-5.54/configure ]]; then tar -xzf stunnel.tar.gz; fi - if [[ ! -f ./stunnel-5.54/src/stunnel ]]; then cd ./stunnel-5.54; ./configure; make; cd ..; fi - export PATH="$PATH:$(pwd)/stunnel-5.54/src" +cache: + directories: + - '$TRAVIS_BUILD_DIR/stunnel-5.54' + after_success: npm run coveralls before_script: # Add an IPv6 config - see the corresponding Travis issue From 5be0d3a78e93daa6cdd227a6d079c534cc162a47 Mon Sep 17 00:00:00 2001 From: Salakar Date: Thu, 6 Feb 2020 22:00:11 +0000 Subject: [PATCH 0783/1748] chore: update .gitignore --- .gitignore | 8 ++------ 1 file changed, 2 insertions(+), 6 deletions(-) diff --git a/.gitignore b/.gitignore index 43529fa00b5..037016a27fb 100644 --- a/.gitignore +++ b/.gitignore @@ -11,10 +11,6 @@ package-lock.json # IntelliJ IDEs .idea - # VisualStudioCode IDEs -.vscode/* -!.vscode/settings.json -!.vscode/tasks.json -!.vscode/launch.json -!.vscode/extensions.json +.vscode +.vs From 031e00f22e8672e152d8f2bc58968a940840bbf9 Mon Sep 17 00:00:00 2001 From: Salakar Date: Thu, 6 Feb 2020 22:13:22 +0000 Subject: [PATCH 0784/1748] tests: fix socket timeout test Node <= 4 --- test/connection.spec.js | 16 ++++++++++++++-- 1 file changed, 14 insertions(+), 2 deletions(-) diff --git a/test/connection.spec.js b/test/connection.spec.js index f467f195787..f6dc8310cb2 100644 --- a/test/connection.spec.js +++ b/test/connection.spec.js @@ -401,10 +401,22 @@ describe('connection tests', function () { connect_timeout: 1000 }); process.nextTick(function () { - assert.strictEqual(client.stream.timeout, 1000); + // node > 4 + var timeout = client.stream.timeout; + // node <= 4 + if (timeout === undefined) timeout = client.stream._idleTimeout; + assert.strictEqual(timeout, 1000); }); client.on('connect', function () { - assert.strictEqual(client.stream.timeout, 0); + // node > 4 + var expected = 0; + var timeout = client.stream.timeout; + // node <= 4 + if (timeout === undefined) { + timeout = client.stream._idleTimeout; + expected = -1; + } + assert.strictEqual(timeout, expected); assert.strictEqual(client.stream.listeners('timeout').length, 0); client.on('ready', done); }); From 36b62fe5792ee4f33a58ad7f8916e708e0bd0b1d Mon Sep 17 00:00:00 2001 From: Salakar Date: Thu, 6 Feb 2020 22:26:55 +0000 Subject: [PATCH 0785/1748] tests: fix error codes test Node >= 13 --- test/node_redis.spec.js | 9 ++++++++- 1 file changed, 8 insertions(+), 1 deletion(-) diff --git a/test/node_redis.spec.js b/test/node_redis.spec.js index 82b3e4e09f3..a6504ddf2b0 100644 --- a/test/node_redis.spec.js +++ b/test/node_redis.spec.js @@ -986,7 +986,14 @@ describe('The node_redis client', function () { } } else { assert.equal(err.code, 'ECONNREFUSED'); - assert.equal(err.errno, 'ECONNREFUSED'); + + if (typeof err.errno === 'number') { + // >= Node 13 + assert.equal(err.errno, -61); + } else { + // < Node 13 + assert.equal(err.errno, 'ECONNREFUSED'); + } assert.equal(err.syscall, 'connect'); } }); From 13ec6afe5802cfd426cc411b74d4ac0a22b3e3e1 Mon Sep 17 00:00:00 2001 From: Salakar Date: Thu, 6 Feb 2020 23:59:31 +0000 Subject: [PATCH 0786/1748] tests: fix Error & Domain tests on Node >= 13 --- test/node_redis.spec.js | 26 ++++++++++++++++++++++---- 1 file changed, 22 insertions(+), 4 deletions(-) diff --git a/test/node_redis.spec.js b/test/node_redis.spec.js index a6504ddf2b0..d23cd7b81ce 100644 --- a/test/node_redis.spec.js +++ b/test/node_redis.spec.js @@ -675,15 +675,21 @@ describe('The node_redis client', function () { done(); }); }); - require('domain').create(); }); it('catches all errors from within the domain', function (done) { var domain = require('domain').create(); domain.run(function () { - // Trigger an error within the domain + if (process.versions.node.split('.')[0] >= 13) { + // Node >= 13 + // Recreate client in domain so error handlers run this domain + // Changed in: "error handler runs outside of its domain" + // https://github.com/nodejs/node/pull/26211 + client = redis.createClient(); + } client.end(true); + // Trigger an error within the domain client.set('domain', 'value'); }); @@ -1053,7 +1059,13 @@ describe('The node_redis client', function () { end(); } else { assert.equal(err.code, 'ECONNREFUSED'); - assert.equal(err.errno, 'ECONNREFUSED'); + if (typeof err.errno === 'number') { + // >= Node 13 + assert.equal(err.errno, -61); + } else { + // < Node 13 + assert.equal(err.errno, 'ECONNREFUSED'); + } assert.equal(err.syscall, 'connect'); end(); } @@ -1139,7 +1151,13 @@ describe('The node_redis client', function () { end(); } else { assert.equal(err.code, 'ECONNREFUSED'); - assert.equal(err.errno, 'ECONNREFUSED'); + if (typeof err.errno === 'number') { + // >= Node 13 + assert.equal(err.errno, -61); + } else { + // < Node 13 + assert.equal(err.errno, 'ECONNREFUSED'); + } assert.equal(err.syscall, 'connect'); redis.debug_mode = false; client.end(true); From 9d65e1c3ededcf3f266ddab0a509a47432dc7ddc Mon Sep 17 00:00:00 2001 From: Salakar Date: Fri, 7 Feb 2020 00:31:08 +0000 Subject: [PATCH 0787/1748] tests: fix buffer test (failed due to new parser) & remove unused node feature checks (< 4) --- index.js | 12 ------------ lib/commands.js | 28 ++++++---------------------- test/pubsub.spec.js | 2 ++ 3 files changed, 8 insertions(+), 34 deletions(-) diff --git a/index.js b/index.js index 402b9b1e476..b1f265262c2 100644 --- a/index.js +++ b/index.js @@ -20,11 +20,6 @@ var SUBSCRIBE_COMMANDS = { punsubscribe: true }; -// Newer Node.js versions > 0.10 return the EventEmitter right away and using .EventEmitter was deprecated -if (typeof EventEmitter !== 'function') { - EventEmitter = EventEmitter.EventEmitter; -} - function noop () {} function handle_detect_buffers_reply (reply, command, buffer_args) { @@ -153,7 +148,6 @@ function RedisClient (options, stream) { this.server_info = {}; this.auth_pass = options.auth_pass || options.password; this.selected_db = options.db; // Save the selected db here, used when reconnecting - this.old_state = null; this.fire_strings = true; // Determine if strings or buffers should be written to the stream this.pipeline = false; this.sub_commands_left = 0; @@ -175,12 +169,6 @@ function RedisClient (options, stream) { 'If you want to keep on listening to this event please listen to the stream drain event directly.' ); } else if ((event === 'message_buffer' || event === 'pmessage_buffer' || event === 'messageBuffer' || event === 'pmessageBuffer') && !this.buffers && !this.message_buffers) { - if (this.reply_parser.name !== 'javascript') { - return this.warn( - 'You attached the "' + event + '" listener without the returnBuffers option set to true.\n' + - 'Please use the JavaScript parser or set the returnBuffers option to true to return buffers.' - ); - } this.reply_parser.optionReturnBuffers = true; this.message_buffers = true; this.handle_reply = handle_detect_buffers_reply; diff --git a/lib/commands.js b/lib/commands.js index 6275ec8bf6b..a3b5189698b 100644 --- a/lib/commands.js +++ b/lib/commands.js @@ -4,18 +4,6 @@ var commands = require('redis-commands'); var Multi = require('./multi'); var RedisClient = require('../').RedisClient; var Command = require('./command'); -// Feature detect if a function may change it's name -var changeFunctionName = (function () { - var fn = function abc () {}; - try { - Object.defineProperty(fn, 'name', { - value: 'foobar' - }); - return true; - } catch (e) { - return false; - } -}()); var addCommand = function (command) { // Some rare Redis commands use special characters in their command name @@ -61,11 +49,9 @@ var addCommand = function (command) { if (commandName !== command) { RedisClient.prototype[commandName.toUpperCase()] = RedisClient.prototype[commandName] = RedisClient.prototype[command]; } - if (changeFunctionName) { - Object.defineProperty(RedisClient.prototype[command], 'name', { - value: commandName - }); - } + Object.defineProperty(RedisClient.prototype[command], 'name', { + value: commandName + }); } // Do not override existing functions @@ -108,11 +94,9 @@ var addCommand = function (command) { if (commandName !== command) { Multi.prototype[commandName.toUpperCase()] = Multi.prototype[commandName] = Multi.prototype[command]; } - if (changeFunctionName) { - Object.defineProperty(Multi.prototype[command], 'name', { - value: commandName - }); - } + Object.defineProperty(Multi.prototype[command], 'name', { + value: commandName + }); } }; diff --git a/test/pubsub.spec.js b/test/pubsub.spec.js index c017cdb36fb..da2561459fe 100644 --- a/test/pubsub.spec.js +++ b/test/pubsub.spec.js @@ -524,6 +524,8 @@ describe('publish/subscribe', function () { assert.strictEqual(channel.inspect(), new Buffer('/foo').inspect()); sub.quit(end); }); + // Either message_buffers or buffers has to be true, but not both at the same time + assert.notStrictEqual(sub.message_buffers, sub.buffers); }); var batch = sub.batch(); batch.psubscribe('*'); From 9e3b57eae7f8a1d0668ee7645e8180f79fc6e349 Mon Sep 17 00:00:00 2001 From: Salakar Date: Fri, 7 Feb 2020 00:54:52 +0000 Subject: [PATCH 0788/1748] tests: use util.getSystemErrorName for errno --- index.js | 1 - test/node_redis.spec.js | 7 ++++--- 2 files changed, 4 insertions(+), 4 deletions(-) diff --git a/index.js b/index.js index b1f265262c2..b5acf785eb7 100644 --- a/index.js +++ b/index.js @@ -204,7 +204,6 @@ function create_parser (self) { self.create_stream(); }, returnBuffers: self.buffers || self.message_buffers, - name: self.options.parser || 'javascript', stringNumbers: self.options.string_numbers || false }); } diff --git a/test/node_redis.spec.js b/test/node_redis.spec.js index d23cd7b81ce..22bd74463c6 100644 --- a/test/node_redis.spec.js +++ b/test/node_redis.spec.js @@ -2,6 +2,7 @@ var assert = require('assert'); var fs = require('fs'); +var util = require('util'); var path = require('path'); var intercept = require('intercept-stdout'); var config = require('./lib/config'); @@ -995,7 +996,7 @@ describe('The node_redis client', function () { if (typeof err.errno === 'number') { // >= Node 13 - assert.equal(err.errno, -61); + assert.equal(err.errno, util.getSystemErrorName(err.errno)); } else { // < Node 13 assert.equal(err.errno, 'ECONNREFUSED'); @@ -1061,7 +1062,7 @@ describe('The node_redis client', function () { assert.equal(err.code, 'ECONNREFUSED'); if (typeof err.errno === 'number') { // >= Node 13 - assert.equal(err.errno, -61); + assert.equal(err.errno, util.getSystemErrorName(err.errno)); } else { // < Node 13 assert.equal(err.errno, 'ECONNREFUSED'); @@ -1153,7 +1154,7 @@ describe('The node_redis client', function () { assert.equal(err.code, 'ECONNREFUSED'); if (typeof err.errno === 'number') { // >= Node 13 - assert.equal(err.errno, -61); + assert.equal(err.errno, util.getSystemErrorName(err.errno)); } else { // < Node 13 assert.equal(err.errno, 'ECONNREFUSED'); From 9220016c7d70b0bfebd4bbeb890396f5fe4b2b0a Mon Sep 17 00:00:00 2001 From: Salakar Date: Fri, 7 Feb 2020 01:06:47 +0000 Subject: [PATCH 0789/1748] tests: use util.getSystemErrorName for errno --- test/node_redis.spec.js | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/test/node_redis.spec.js b/test/node_redis.spec.js index 22bd74463c6..317723f56a4 100644 --- a/test/node_redis.spec.js +++ b/test/node_redis.spec.js @@ -996,7 +996,7 @@ describe('The node_redis client', function () { if (typeof err.errno === 'number') { // >= Node 13 - assert.equal(err.errno, util.getSystemErrorName(err.errno)); + assert.equal(util.getSystemErrorName(err.errno), 'ECONNREFUSED'); } else { // < Node 13 assert.equal(err.errno, 'ECONNREFUSED'); @@ -1062,7 +1062,7 @@ describe('The node_redis client', function () { assert.equal(err.code, 'ECONNREFUSED'); if (typeof err.errno === 'number') { // >= Node 13 - assert.equal(err.errno, util.getSystemErrorName(err.errno)); + assert.equal(util.getSystemErrorName(err.errno), 'ECONNREFUSED'); } else { // < Node 13 assert.equal(err.errno, 'ECONNREFUSED'); @@ -1154,7 +1154,7 @@ describe('The node_redis client', function () { assert.equal(err.code, 'ECONNREFUSED'); if (typeof err.errno === 'number') { // >= Node 13 - assert.equal(err.errno, util.getSystemErrorName(err.errno)); + assert.equal(util.getSystemErrorName(err.errno), 'ECONNREFUSED'); } else { // < Node 13 assert.equal(err.errno, 'ECONNREFUSED'); From 6ce845f620bc5b0aa6d4704492fe27365347d60f Mon Sep 17 00:00:00 2001 From: Salakar Date: Fri, 7 Feb 2020 01:23:17 +0000 Subject: [PATCH 0790/1748] chore: set package min node version to >=v4 --- package.json | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/package.json b/package.json index 827083920e6..3cedc98b8f9 100644 --- a/package.json +++ b/package.json @@ -31,7 +31,7 @@ "redis-parser": "^3.0.0" }, "engines": { - "node": ">=0.10.0" + "node": ">=4.0.0" }, "devDependencies": { "bluebird": "^3.0.2", From caaae9c97f8756b0c476b5467613ba126e9f17c7 Mon Sep 17 00:00:00 2001 From: Salakar Date: Fri, 7 Feb 2020 01:24:25 +0000 Subject: [PATCH 0791/1748] chore: update denque to v1.4.1 --- package.json | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/package.json b/package.json index 3cedc98b8f9..015b93a8c74 100644 --- a/package.json +++ b/package.json @@ -25,7 +25,7 @@ "compare": "node benchmarks/diff_multi_bench_output.js beforeBench.txt afterBench.txt" }, "dependencies": { - "denque": "^1.2.3", + "denque": "^1.4.1", "redis-commands": "^1.5.0", "redis-errors": "^1.2.0", "redis-parser": "^3.0.0" From 3569a68f5df7df910e9b3961f3bd66fb49ba3210 Mon Sep 17 00:00:00 2001 From: Salakar Date: Fri, 7 Feb 2020 01:46:35 +0000 Subject: [PATCH 0792/1748] tests: fix test on windows --- test/node_redis.spec.js | 6 +++++- 1 file changed, 5 insertions(+), 1 deletion(-) diff --git a/test/node_redis.spec.js b/test/node_redis.spec.js index 317723f56a4..a77c5146a81 100644 --- a/test/node_redis.spec.js +++ b/test/node_redis.spec.js @@ -360,7 +360,11 @@ describe('The node_redis client', function () { it('send_command with callback as args', function (done) { client.send_command('abcdef', function (err, res) { - assert.strictEqual(err.message, 'ERR unknown command `abcdef`, with args beginning with: '); + if (process.platform === 'win32') { + assert.strictEqual(err.message, 'ERR unknown command `abcdef`'); + } else { + assert.strictEqual(err.message, 'ERR unknown command `abcdef`, with args beginning with: '); + } done(); }); }); From feb4652ad246c488498dd1d0371981354c0a751a Mon Sep 17 00:00:00 2001 From: Salakar Date: Fri, 7 Feb 2020 01:48:23 +0000 Subject: [PATCH 0793/1748] tests: disable node > 12 tests on windows, nyc fails to launch nyc needs upgrading to v15, but v15 only works in node versions that support let/const --- appveyor.yml | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/appveyor.yml b/appveyor.yml index 42155ac1d23..d9cd4f4b635 100644 --- a/appveyor.yml +++ b/appveyor.yml @@ -6,8 +6,9 @@ environment: - nodejs_version: "4" - nodejs_version: "6" - nodejs_version: "8" - - nodejs_version: "12" - - nodejs_version: "13" + - nodejs_version: "10" +# - nodejs_version: "12" +# - nodejs_version: "13" pull_requests: do_not_increment_build_number: true From 1d8fa45689faded4b4ae8ebf70cbb3813dce477f Mon Sep 17 00:00:00 2001 From: Mike Diarmid Date: Fri, 7 Feb 2020 02:01:52 +0000 Subject: [PATCH 0794/1748] tests: fix windows text --- test/node_redis.spec.js | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/test/node_redis.spec.js b/test/node_redis.spec.js index a77c5146a81..d529d40da3e 100644 --- a/test/node_redis.spec.js +++ b/test/node_redis.spec.js @@ -361,7 +361,7 @@ describe('The node_redis client', function () { it('send_command with callback as args', function (done) { client.send_command('abcdef', function (err, res) { if (process.platform === 'win32') { - assert.strictEqual(err.message, 'ERR unknown command `abcdef`'); + assert.strictEqual(err.message, "ERR unknown command 'abcdef'"); } else { assert.strictEqual(err.message, 'ERR unknown command `abcdef`, with args beginning with: '); } From c0cc0bfab44d5ce5fa2fdc5c9af85401c4ff0b5c Mon Sep 17 00:00:00 2001 From: Salakar Date: Sun, 9 Feb 2020 02:05:21 +0000 Subject: [PATCH 0795/1748] start of work towards v3 release --- .github/FUNDING.yml | 1 + .npmignore | 6 ++ .travis.yml | 1 - changelog.md => CHANGELOG.md | 29 +++++++ CODE_OF_CONDUCT.md | 46 ++++++++++ CONTRIBUTING.md | 1 + LICENSE | 6 +- README.md | 83 +++++------------- appveyor.yml | 1 - index.js | 143 +++++++++++-------------------- package.json | 40 ++++++--- test/auth.spec.js | 2 +- test/commands/hset.spec.js | 18 ++-- test/commands/set.spec.js | 12 ++- test/connection.spec.js | 67 +++------------ test/lib/redis-process.js | 2 +- test/multi.spec.js | 20 +++-- test/node_redis.spec.js | 158 ++--------------------------------- 18 files changed, 235 insertions(+), 401 deletions(-) create mode 100644 .github/FUNDING.yml rename changelog.md => CHANGELOG.md (97%) create mode 100644 CODE_OF_CONDUCT.md create mode 100644 CONTRIBUTING.md diff --git a/.github/FUNDING.yml b/.github/FUNDING.yml new file mode 100644 index 00000000000..1893f87aadd --- /dev/null +++ b/.github/FUNDING.yml @@ -0,0 +1 @@ +open_collective: node-redis diff --git a/.npmignore b/.npmignore index 569f3efb2b8..497486e4e0d 100644 --- a/.npmignore +++ b/.npmignore @@ -11,3 +11,9 @@ coverage/ *.rdb *.out *.yml +CHANGELOG.md +CONTRIBUTING.md +CODE_OF_CONDUCT.md +.travis.yml +appveyor.yml +package-lock.json diff --git a/.travis.yml b/.travis.yml index e0c8e7d152c..d1bd2e3cef0 100644 --- a/.travis.yml +++ b/.travis.yml @@ -9,7 +9,6 @@ addons: packages: - g++-4.8 node_js: - - "4" - "6" - "8" - "12" diff --git a/changelog.md b/CHANGELOG.md similarity index 97% rename from changelog.md rename to CHANGELOG.md index d650f9ba394..daea8eeefc0 100644 --- a/changelog.md +++ b/CHANGELOG.md @@ -1,5 +1,34 @@ # Changelog +## v.3.0.0 - 09 Feb, 2020 + +This version is mainly a release to distribute all the unreleased changes on master since 2017 and additionally removes +a lot of old deprecated features and old internals in preparation for an upcoming modernization refactor (v4). + +### Breaking Changes + + - Dropped support for Node.js < 6 + - Dropped support for `hiredis` (no longer required) + - Removed previously deprecated `drain` event + - Removed previously deprecated `idle` event + - Removed previously deprecated `parser` option + - Removed previously deprecated `max_delay` option + - Removed previously deprecated `max_attempts` option + - Removed previously deprecated `socket_no_delay` option + +### Bug Fixes + + - Removed development files from published package (#1370) + - Duplicate function now allows db param to be passed (#1311) + +### Features + + - Upgraded to latest `redis-commands` package + - Upgraded to latest `redis-parser` package, v3.0.0, which brings performance improvements + - Replaced `double-ended-queue` with `denque`, which brings performance improvements + - Add timestamps to debug traces + - Add `socket_initial_delay` option for `socket.setKeepAlive` (#1396) + ## v.2.8.0 - 31 Jul, 2017 Features diff --git a/CODE_OF_CONDUCT.md b/CODE_OF_CONDUCT.md new file mode 100644 index 00000000000..00aad77d088 --- /dev/null +++ b/CODE_OF_CONDUCT.md @@ -0,0 +1,46 @@ +# Contributor Covenant Code of Conduct + +## Our Pledge + +In the interest of fostering an open and welcoming environment, we as contributors and maintainers pledge to making participation in our project and our community a harassment-free experience for everyone, regardless of age, body size, disability, ethnicity, gender identity and expression, level of experience, nationality, personal appearance, race, religion, or sexual identity and orientation. + +## Our Standards + +Examples of behavior that contributes to creating a positive environment include: + +* Using welcoming and inclusive language +* Being respectful of differing viewpoints and experiences +* Gracefully accepting constructive criticism +* Focusing on what is best for the community +* Showing empathy towards other community members + +Examples of unacceptable behavior by participants include: + +* The use of sexualized language or imagery and unwelcome sexual attention or advances +* Trolling, insulting/derogatory comments, and personal or political attacks +* Public or private harassment +* Publishing others' private information, such as a physical or electronic address, without explicit permission +* Other conduct which could reasonably be considered inappropriate in a professional setting + +## Our Responsibilities + +Project maintainers are responsible for clarifying the standards of acceptable behavior and are expected to take appropriate and fair corrective action in response to any instances of unacceptable behavior. + +Project maintainers have the right and responsibility to remove, edit, or reject comments, commits, code, wiki edits, issues, and other contributions that are not aligned to this Code of Conduct, or to ban temporarily or permanently any contributor for other behaviors that they deem inappropriate, threatening, offensive, or harmful. + +## Scope + +This Code of Conduct applies both within project spaces and in public spaces when an individual is representing the project or its community. Examples of representing a project or community include using an official project e-mail address, posting via an official social media account, or acting as an appointed representative at an online or offline event. Representation of a project may be further defined and clarified by project maintainers. + +## Enforcement + +Instances of abusive, harassing, or otherwise unacceptable behavior may be reported by contacting the project team at `redis @ invertase.io`. The project team will review and investigate all complaints, and will respond in a way that it deems appropriate to the circumstances. The project team is obligated to maintain confidentiality with regard to the reporter of an incident. Further details of specific enforcement policies may be posted separately. + +Project maintainers who do not follow or enforce the Code of Conduct in good faith may face temporary or permanent repercussions as determined by other members of the project's leadership. + +## Attribution + +This Code of Conduct is adapted from the [Contributor Covenant][homepage], version 1.4, available at [http://contributor-covenant.org/version/1/4][version] + +[homepage]: http://contributor-covenant.org +[version]: http://contributor-covenant.org/version/1/4/ diff --git a/CONTRIBUTING.md b/CONTRIBUTING.md new file mode 100644 index 00000000000..cf873b4c072 --- /dev/null +++ b/CONTRIBUTING.md @@ -0,0 +1 @@ + diff --git a/LICENSE b/LICENSE index 710407f442b..7b7ba1393b4 100644 --- a/LICENSE +++ b/LICENSE @@ -1,6 +1,6 @@ -LICENSE - "MIT License" +MIT License -Copyright (c) 2016 by NodeRedis +Copyright (c) Node Redis contributors. Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation @@ -21,4 +21,4 @@ NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR -OTHER DEALINGS IN THE SOFTWARE. \ No newline at end of file +OTHER DEALINGS IN THE SOFTWARE. diff --git a/README.md b/README.md index 91695a8b8bc..e4f60d3ab32 100644 --- a/README.md +++ b/README.md @@ -1,10 +1,21 @@ -redis - a node.js redis client -=========================== - -[![Build Status](https://travis-ci.org/NodeRedis/node_redis.svg?branch=master)](https://travis-ci.org/NodeRedis/node_redis) -[![Coverage Status](https://coveralls.io/repos/NodeRedis/node_redis/badge.svg?branch=)](https://coveralls.io/r/NodeRedis/node_redis?branch=) -[![Windows Tests](https://img.shields.io/appveyor/ci/BridgeAR/node-redis/master.svg?label=Windows%20Tests)](https://ci.appveyor.com/project/BridgeAR/node-redis/branch/master) -[![Gitter](https://badges.gitter.im/Join%20Chat.svg)](https://gitter.im/NodeRedis/node_redis?utm_source=badge&utm_medium=badge&utm_campaign=pr-badge) +

+ + + +

Node Redis

+ A high performance Node.js Redis client. +

+ +

+ NPM downloads + NPM version + Build Status + Coverage Status + Windows Tests + Follow on Twitter +

+ +--- This is a complete and feature rich Redis client for node.js. __It supports all Redis commands__ and focuses on high performance. @@ -195,30 +206,11 @@ So please attach the error listener to node_redis. `client` will emit `end` when an established Redis server connection has closed. -### "drain" (deprecated) - -`client` will emit `drain` when the TCP connection to the Redis server has been -buffering, but is now writable. This event can be used to stream commands in to -Redis and adapt to backpressure. - -If the stream is buffering `client.should_buffer` is set to true. Otherwise the -variable is always set to false. That way you can decide when to reduce your -send rate and resume sending commands when you get `drain`. - -You can also check the return value of each command as it will also return the -backpressure indicator (deprecated). If false is returned the stream had to -buffer. - ### "warning" `client` will emit `warning` when password was set but none is needed and if a deprecated option / function / similar is used. -### "idle" (deprecated) - -`client` will emit `idle` when there are no outstanding commands that are -awaiting a response. - ## redis.createClient() If you have `redis-server` running on the same machine as node, then the defaults for port and host are probably fine and you don't need to supply any @@ -242,17 +234,13 @@ __Note:__ Using `'rediss://...` for the protocol in a `redis_url` will enable a | port | 6379 | Port of the Redis server | | path | null | The UNIX socket string of the Redis server | | url | null | The URL of the Redis server. Format: `[redis[s]:]//[[user][:password@]][host][:port][/db-number][?db=db-number[&password=bar[&option=value]]]` (More info avaliable at [IANA](http://www.iana.org/assignments/uri-schemes/prov/redis)). | -| parser | javascript | __Deprecated__ Use either the built-in JS parser [`javascript`]() or the native [`hiredis`]() parser. __Note__ `node_redis` < 2.6 uses hiredis as default if installed. This changed in v.2.6.0. | | string_numbers | null | Set to `true`, `node_redis` will return Redis number values as Strings instead of javascript Numbers. Useful if you need to handle big numbers (above `Number.MAX_SAFE_INTEGER === 2^53`). Hiredis is incapable of this behavior, so setting this option to `true` will result in the built-in javascript parser being used no matter the value of the `parser` option. | | return_buffers | false | If set to `true`, then all replies will be sent to callbacks as Buffers instead of Strings. | | detect_buffers | false | If set to `true`, then replies will be sent to callbacks as Buffers. This option lets you switch between Buffers and Strings on a per-command basis, whereas `return_buffers` applies to every command on a client. __Note__: This doesn't work properly with the pubsub mode. A subscriber has to either always return Strings or Buffers. | | socket_keepalive | true | If set to `true`, the keep-alive functionality is enabled on the underlying socket. | -| socket_initialdelay | 0 | Initial Delay in milliseconds, and this will also behave the interval keep alive message sending to Redis. | +| socket_initial_delay | 0 | Initial Delay in milliseconds, and this will also behave the interval keep alive message sending to Redis. | | no_ready_check | false | When a connection is established to the Redis server, the server might still be loading the database from disk. While loading, the server will not respond to any commands. To work around this, `node_redis` has a "ready check" which sends the `INFO` command to the server. The response from the `INFO` command indicates whether the server is ready for more commands. When ready, `node_redis` emits a `ready` event. Setting `no_ready_check` to `true` will inhibit this check. | | enable_offline_queue | true | By default, if there is no active connection to the Redis server, commands are added to a queue and are executed once the connection has been established. Setting `enable_offline_queue` to `false` will disable this feature and the callback will be executed immediately with an error, or an error will be emitted if no callback is specified. | -| retry_max_delay | null | __Deprecated__ _Please use `retry_strategy` instead._ By default, every time the client tries to connect and fails, the reconnection delay almost doubles. This delay normally grows infinitely, but setting `retry_max_delay` limits it to the maximum value provided in milliseconds. | -| connect_timeout | 3600000 | __Deprecated__ _Please use `retry_strategy` instead._ Setting `connect_timeout` limits the total time for the client to connect and reconnect. The value is provided in milliseconds and is counted from the moment a new client is created or from the time the connection is lost. The last retry is going to happen exactly at the timeout time. Default is to try connecting until the default system socket timeout has been exceeded and to try reconnecting until 1h has elapsed. | -| max_attempts | 0 | __Deprecated__ _Please use `retry_strategy` instead._ By default, a client will try reconnecting until connected. Setting `max_attempts` limits total amount of connection attempts. Setting this to 1 will prevent any reconnect attempt. | | retry_unfulfilled_commands | false | If set to `true`, all commands that were unfulfilled while the connection is lost will be retried after the connection has been reestablished. Use this with caution if you use state altering commands (e.g. `incr`). This is especially useful if you use blocking commands. | | password | null | If set, client will run Redis auth command on connect. Alias `auth_pass` __Note__ `node_redis` < 2.5 must use `auth_pass` | | db | null | If set, client will run Redis `select` command on connect. | @@ -473,12 +461,6 @@ client.hgetall("hosts", function (err, obj) { }); ``` -Output: - -```js -{ mjr: '1', another: '23', home: '1234' } -``` - ### client.hmset(hash, obj[, callback]) Multiple values in a hash can be set by supplying an object: @@ -770,7 +752,7 @@ clients.alterer = clients.watcher.duplicate(); clients.watcher.watch('foo',function(err) { if (err) { throw err; } //if you comment out the next line, the transaction will work - clients.alterer.set('foo',Math.random(), (err) => {if (err) { throw err; }}) + clients.alterer.set('foo',Math.random(), (err) => {if (err) { throw err; }}); //using a setTimeout here to ensure that the MULTI/EXEC will come after the SET. //Normally, you would use a callback to ensure order, but I want the above SET command @@ -897,8 +879,8 @@ the second word as first parameter: ```js client.script('load', 'return 1'); -client.multi().script('load', 'return 1').exec(...); -client.multi([['script', 'load', 'return 1']]).exec(...); +client.multi().script('load', 'return 1').exec(); +client.multi([['script', 'load', 'return 1']]).exec(); ``` ## client.duplicate([options][, callback]) @@ -1082,6 +1064,7 @@ ReplyError: ERR wrong number of arguments for 'set' command ``` ## How to Contribute + - Open a pull request or an issue about what you want to implement / change. We're glad for any help! - Please be aware that we'll only accept fully tested code. @@ -1097,23 +1080,3 @@ contributed to `node_redis` too. Thanks to all of them! ## License [MIT](LICENSE) - -### Consolidation: It's time for celebration - -Right now there are two great redis clients around and both have some advantages -above each other. We speak about ioredis and node_redis. So after talking to -each other about how we could improve in working together we (that is @luin and -@BridgeAR) decided to work towards a single library on the long run. But step by -step. - -First of all, we want to split small parts of our libraries into others so that -we're both able to use the same code. Those libraries are going to be maintained -under the NodeRedis organization. This is going to reduce the maintenance -overhead, allows others to use the very same code, if they need it and it's way -easyer for others to contribute to both libraries. - -We're very happy about this step towards working together as we both want to -give you the best redis experience possible. - -If you want to join our cause by help maintaining something, please don't -hesitate to contact either one of us. diff --git a/appveyor.yml b/appveyor.yml index d9cd4f4b635..f8c16e9a4de 100644 --- a/appveyor.yml +++ b/appveyor.yml @@ -3,7 +3,6 @@ # Test against these versions of Node.js. environment: matrix: - - nodejs_version: "4" - nodejs_version: "6" - nodejs_version: "8" - nodejs_version: "10" diff --git a/index.js b/index.js index b5acf785eb7..bdbf46daf51 100644 --- a/index.js +++ b/index.js @@ -67,38 +67,16 @@ function RedisClient (options, stream) { cnx_options.family = (!options.family && net.isIP(cnx_options.host)) || (options.family === 'IPv6' ? 6 : 4); this.address = cnx_options.host + ':' + cnx_options.port; } - // Warn on misusing deprecated functions - if (typeof options.retry_strategy === 'function') { - if ('max_attempts' in options) { - self.warn('WARNING: You activated the retry_strategy and max_attempts at the same time. This is not possible and max_attempts will be ignored.'); - // Do not print deprecation warnings twice - delete options.max_attempts; - } - if ('retry_max_delay' in options) { - self.warn('WARNING: You activated the retry_strategy and retry_max_delay at the same time. This is not possible and retry_max_delay will be ignored.'); - // Do not print deprecation warnings twice - delete options.retry_max_delay; - } - } this.connection_options = cnx_options; this.connection_id = RedisClient.connection_id++; this.connected = false; this.ready = false; - if (options.socket_nodelay === undefined) { - options.socket_nodelay = true; - } else if (!options.socket_nodelay) { // Only warn users with this set to false - self.warn( - 'socket_nodelay is deprecated and will be removed in v.3.0.0.\n' + - 'Setting socket_nodelay to false likely results in a reduced throughput. Please use .batch for pipelining instead.\n' + - 'If you are sure you rely on the NAGLE-algorithm you can activate it by calling client.stream.setNoDelay(false) instead.' - ); - } if (options.socket_keepalive === undefined) { options.socket_keepalive = true; } - if (options.socket_initialdelay === undefined) { - options.socket_initialdelay = 0; + if (options.socket_initial_delay === undefined) { + options.socket_initial_delay = 0; // set default to 0, which is aligned to https://nodejs.org/api/net.html#net_socket_setkeepalive_enable_initialdelay } for (var command in options.rename_commands) { @@ -116,14 +94,6 @@ function RedisClient (options, stream) { this.handle_reply = handle_detect_buffers_reply; } this.should_buffer = false; - this.max_attempts = options.max_attempts | 0; - if ('max_attempts' in options) { - self.warn( - 'max_attempts is deprecated and will be removed in v.3.0.0.\n' + - 'To reduce the number of options and to improve the reconnection handling please use the new `retry_strategy` option instead.\n' + - 'This replaces the max_attempts and retry_max_delay option.' - ); - } this.command_queue = new Queue(); // Holds sent commands to de-pipeline them this.offline_queue = new Queue(); // Holds commands issued but not able to be sent this.pipeline_queue = new Queue(); // Holds all pipelined commands @@ -131,14 +101,6 @@ function RedisClient (options, stream) { // This should be done by the retry_strategy. Instead it should only be the timeout for connecting to redis this.connect_timeout = +options.connect_timeout || 3600000; // 60 * 60 * 1000 ms this.enable_offline_queue = options.enable_offline_queue === false ? false : true; - this.retry_max_delay = +options.retry_max_delay || null; - if ('retry_max_delay' in options) { - self.warn( - 'retry_max_delay is deprecated and will be removed in v.3.0.0.\n' + - 'To reduce the amount of options and the improve the reconnection handling please use the new `retry_strategy` option instead.\n' + - 'This replaces the max_attempts and retry_max_delay option.' - ); - } this.initialize_retry_vars(); this.pub_sub_mode = 0; this.subscription_set = {}; @@ -158,17 +120,7 @@ function RedisClient (options, stream) { this.create_stream(); // The listeners will not be attached right away, so let's print the deprecation message while the listener is attached this.on('newListener', function (event) { - if (event === 'idle') { - this.warn( - 'The idle event listener is deprecated and will likely be removed in v.3.0.0.\n' + - 'If you rely on this feature please open a new ticket in node_redis with your use case' - ); - } else if (event === 'drain') { - this.warn( - 'The drain event listener is deprecated and will be removed in v.3.0.0.\n' + - 'If you want to keep on listening to this event please listen to the stream drain event directly.' - ); - } else if ((event === 'message_buffer' || event === 'pmessage_buffer' || event === 'messageBuffer' || event === 'pmessageBuffer') && !this.buffers && !this.message_buffers) { + if ((event === 'message_buffer' || event === 'pmessage_buffer' || event === 'messageBuffer' || event === 'pmessageBuffer') && !this.buffers && !this.message_buffers) { this.reply_parser.optionReturnBuffers = true; this.message_buffers = true; this.handle_reply = handle_detect_buffers_reply; @@ -264,7 +216,6 @@ RedisClient.prototype.create_stream = function () { // The buffer_from_socket.toString() has a significant impact on big chunks and therefore this should only be used if necessary debug('Net read ' + self.address + ' id ' + self.connection_id); // + ': ' + buffer_from_socket.toString()); self.reply_parser.execute(buffer_from_socket); - self.emit_idle(); }); this.stream.on('error', function (err) { @@ -283,9 +234,7 @@ RedisClient.prototype.create_stream = function () { self.drain(); }); - if (this.options.socket_nodelay) { - this.stream.setNoDelay(); - } + this.stream.setNoDelay(); // Fire the command before redis is connected to be sure it's the first fired command if (this.auth_pass !== undefined) { @@ -387,7 +336,7 @@ RedisClient.prototype.on_error = function (err) { this.connected = false; this.ready = false; - // Only emit the error if the retry_stategy option is not set + // Only emit the error if the retry_strategy option is not set if (!this.options.retry_strategy) { this.emit('error', err); } @@ -402,7 +351,7 @@ RedisClient.prototype.on_connect = function () { this.connected = true; this.ready = false; this.emitted_end = false; - this.stream.setKeepAlive(this.options.socket_keepalive, this.options.socket_initialdelay); + this.stream.setKeepAlive(this.options.socket_keepalive, this.options.socket_initial_delay); this.stream.setTimeout(0); this.emit('connect'); @@ -607,18 +556,31 @@ RedisClient.prototype.connection_gone = function (why, error) { if (this.retry_delay instanceof Error) { error = this.retry_delay; } + var errorMessage = 'Redis connection in broken state: '; + if (this.retry_totaltime >= this.connect_timeout) { + errorMessage += 'connection timeout exceeded.'; + } else { + errorMessage += 'maximum connection attempts exceeded.'; + } + this.flush_and_error({ - message: 'Stream connection ended and command aborted.', - code: 'NR_CLOSED' + message: errorMessage, + code: 'CONNECTION_BROKEN', }, { error: error }); + var retryError = new Error(errorMessage); + retryError.code = 'CONNECTION_BROKEN'; + if (error) { + retryError.origin = error; + } this.end(false); + this.emit('error', retryError); return; } } - if (this.max_attempts !== 0 && this.attempts >= this.max_attempts || this.retry_totaltime >= this.connect_timeout) { + if (this.retry_totaltime >= this.connect_timeout) { var message = 'Redis connection in broken state: '; if (this.retry_totaltime >= this.connect_timeout) { message += 'connection timeout exceeded.'; @@ -637,8 +599,8 @@ RedisClient.prototype.connection_gone = function (why, error) { if (error) { err.origin = error; } - this.emit('error', err); this.end(false); + this.emit('error', err); return; } @@ -656,15 +618,12 @@ RedisClient.prototype.connection_gone = function (why, error) { }); } - if (this.retry_max_delay !== null && this.retry_delay > this.retry_max_delay) { - this.retry_delay = this.retry_max_delay; - } else if (this.retry_totaltime + this.retry_delay > this.connect_timeout) { + if (this.retry_totaltime + this.retry_delay > this.connect_timeout) { // Do not exceed the maximum this.retry_delay = this.connect_timeout - this.retry_totaltime; } debug('Retry connection in ' + this.retry_delay + ' ms'); - this.retry_timer = setTimeout(retry_connection, this.retry_delay, this, error); }; @@ -693,16 +652,9 @@ RedisClient.prototype.return_error = function (err) { }; RedisClient.prototype.drain = function () { - this.emit('drain'); this.should_buffer = false; }; -RedisClient.prototype.emit_idle = function () { - if (this.command_queue.length === 0 && this.pub_sub_mode === 0) { - this.emit('idle'); - } -}; - function normal_reply (self, reply) { var command_obj = self.command_queue.shift(); if (typeof command_obj.callback === 'function') { @@ -752,7 +704,7 @@ function subscribe_unsubscribe (self, reply, type) { self.command_queue.shift(); if (typeof command_obj.callback === 'function') { // TODO: The current return value is pretty useless. - // Evaluate to change this in v.3 to return all subscribed / unsubscribed channels in an array including the number of channels subscribed too + // Evaluate to change this in v.4 to return all subscribed / unsubscribed channels in an array including the number of channels subscribed too command_obj.callback(null, channel); } self.sub_commands_left = 0; @@ -768,7 +720,7 @@ function subscribe_unsubscribe (self, reply, type) { function return_pub_sub (self, reply) { var type = reply[0].toString(); if (type === 'message') { // channel, message - if (!self.options.return_buffers || self.message_buffers) { // backwards compatible. Refactor this in v.3 to always return a string on the normal emitter + if (!self.options.return_buffers || self.message_buffers) { // backwards compatible. Refactor this in v.4 to always return a string on the normal emitter self.emit('message', reply[1].toString(), reply[2].toString()); self.emit('message_buffer', reply[1], reply[2]); self.emit('messageBuffer', reply[1], reply[2]); @@ -776,7 +728,7 @@ function return_pub_sub (self, reply) { self.emit('message', reply[1], reply[2]); } } else if (type === 'pmessage') { // pattern, channel, message - if (!self.options.return_buffers || self.message_buffers) { // backwards compatible. Refactor this in v.3 to always return a string on the normal emitter + if (!self.options.return_buffers || self.message_buffers) { // backwards compatible. Refactor this in v.4 to always return a string on the normal emitter self.emit('pmessage', reply[1].toString(), reply[2].toString(), reply[3].toString()); self.emit('pmessage_buffer', reply[1], reply[2], reply[3]); self.emit('pmessageBuffer', reply[1], reply[2], reply[3]); @@ -884,32 +836,39 @@ RedisClient.prototype.internal_send_command = function (command_obj) { } else if (typeof args[i] === 'object') { // Checking for object instead of Buffer.isBuffer helps us finding data types that we can't handle properly if (args[i] instanceof Date) { // Accept dates as valid input args_copy[i] = args[i].toString(); - } else if (args[i] === null) { - this.warn( - 'Deprecated: The ' + command.toUpperCase() + ' command contains a "null" argument.\n' + - 'This is converted to a "null" string now and will return an error from v.3.0 on.\n' + - 'Please handle this in your code to make sure everything works as you intended it to.' - ); - args_copy[i] = 'null'; // Backwards compatible :/ } else if (Buffer.isBuffer(args[i])) { args_copy[i] = args[i]; command_obj.buffer_args = true; big_data = true; } else { - this.warn( - 'Deprecated: The ' + command.toUpperCase() + ' command contains a argument of type ' + args[i].constructor.name + '.\n' + - 'This is converted to "' + args[i].toString() + '" by using .toString() now and will return an error from v.3.0 on.\n' + - 'Please handle this in your code to make sure everything works as you intended it to.' + var invalidArgError = new Error( + 'node_redis: The ' + command.toUpperCase() + ' command contains a invalid argument type.\n' + + 'Only strings, dates and buffers are accepted. Please update your code to use valid argument types.' ); - args_copy[i] = args[i].toString(); // Backwards compatible :/ + invalidArgError.command = command_obj.command.toUpperCase(); + if (command_obj.args && command_obj.args.length) { + invalidArgError.args = command_obj.args; + } + if (command_obj.callback) { + command_obj.callback(invalidArgError); + return false; + } + throw invalidArgError; } } else if (typeof args[i] === 'undefined') { - this.warn( - 'Deprecated: The ' + command.toUpperCase() + ' command contains a "undefined" argument.\n' + - 'This is converted to a "undefined" string now and will return an error from v.3.0 on.\n' + - 'Please handle this in your code to make sure everything works as you intended it to.' + var undefinedArgError = new Error( + 'node_redis: The ' + command.toUpperCase() + ' command contains a invalid argument type of "undefined".\n' + + 'Only strings, dates and buffers are accepted. Please update your code to use valid argument types.' ); - args_copy[i] = 'undefined'; // Backwards compatible :/ + undefinedArgError.command = command_obj.command.toUpperCase(); + if (command_obj.args && command_obj.args.length) { + undefinedArgError.args = command_obj.args; + } + if (command_obj.callback) { + command_obj.callback(undefinedArgError); + return false; + } + throw undefinedArgError; } else { // Seems like numbers are converted fast using string concatenation args_copy[i] = '' + args[i]; diff --git a/package.json b/package.json index 015b93a8c74..e322fc6ef45 100644 --- a/package.json +++ b/package.json @@ -1,7 +1,7 @@ { "name": "redis", "version": "2.8.0", - "description": "Redis client library", + "description": "A high performance Redis client.", "keywords": [ "database", "redis", @@ -14,6 +14,16 @@ "backpressure" ], "author": "Matt Ranney ", + "contributors": [ + { + "name": "Mike Diarmid (Salakar)", + "url": "https://github.com/salakar" + }, + { + "name": "Ruben Bridgewater (BridgeAR)", + "url": "https://github.com/BridgeAR" + } + ], "license": "MIT", "main": "./index.js", "scripts": { @@ -31,30 +41,34 @@ "redis-parser": "^3.0.0" }, "engines": { - "node": ">=4.0.0" + "node": ">=6" }, "devDependencies": { - "bluebird": "^3.0.2", + "bluebird": "^3.7.2", "coveralls": "^2.11.2", - "eslint": "^4.2.0", + "eslint": "^6.8.0", "intercept-stdout": "~0.1.2", - "metrics": "^0.1.9", - "mocha": "^3.1.2", - "nyc": "^10.0.0", - "tcp-port-used": "^0.1.2", - "uuid": "^2.0.1", - "win-spawn": "^2.0.0" + "metrics": "^0.1.21", + "mocha": "^4.1.0", + "nyc": "^14.1.1", + "tcp-port-used": "^1.0.1", + "uuid": "^3.4.0", + "cross-spawn": "^6.0.5" }, "repository": { "type": "git", - "url": "git://github.com/NodeRedis/node_redis.git" + "url": "git://github.com/NodeRedis/node-redis.git" }, "bugs": { - "url": "https://github.com/NodeRedis/node_redis/issues" + "url": "https://github.com/NodeRedis/node-redis/issues" }, - "homepage": "https://github.com/NodeRedis/node_redis", + "homepage": "https://github.com/NodeRedis/node-redis", "directories": { "example": "examples", "test": "test" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/node-redis" } } diff --git a/test/auth.spec.js b/test/auth.spec.js index 8411a4b618d..67e6d0924a1 100644 --- a/test/auth.spec.js +++ b/test/auth.spec.js @@ -192,7 +192,7 @@ describe('client authentication', function () { client = redis.createClient.apply(null, args); var async = true; - client.auth(undefined, function (err, res) { + client.auth('undefined', function (err, res) { assert.strictEqual(err.message, 'ERR invalid password'); assert.strictEqual(err.command, 'AUTH'); assert.strictEqual(res, undefined); diff --git a/test/commands/hset.spec.js b/test/commands/hset.spec.js index d0ce7f5481b..c62b17713b0 100644 --- a/test/commands/hset.spec.js +++ b/test/commands/hset.spec.js @@ -45,22 +45,16 @@ describe("The 'hset' method", function () { }); }); - it('warns if someone passed a array either as field or as value', function (done) { + it('errors if someone passed a array either as field or as value', function (done) { var hash = 'test hash'; var field = 'array'; - // This would be converted to "array contents" but if you use more than one entry, - // it'll result in e.g. "array contents,second content" and this is not supported and considered harmful var value = ['array contents']; - client.on('warning', function (msg) { - assert.strictEqual( - msg, - 'Deprecated: The HMSET command contains a argument of type Array.\n' + - 'This is converted to "array contents" by using .toString() now and will return an error from v.3.0 on.\n' + - 'Please handle this in your code to make sure everything works as you intended it to.' - ); + try { + client.HMSET(hash, field, value); + } catch (error) { + assert(/node_redis: The HMSET command contains a invalid argument type./.test(error.message)); done(); - }); - client.HMSET(hash, field, value); + } }); it('does not error when a buffer and date are set as values on the same hash', function (done) { diff --git a/test/commands/set.spec.js b/test/commands/set.spec.js index 01b62443818..360cffe5f59 100644 --- a/test/commands/set.spec.js +++ b/test/commands/set.spec.js @@ -140,10 +140,14 @@ describe("The 'set' method", function () { }); }); - // TODO: This test has to be refactored from v.3.0 on to expect an error instead - it("converts null to 'null'", function (done) { - client.set('foo', null); - client.get('foo', helper.isString('null', done)); + it('errors if null value is passed', function (done) { + try { + client.set('foo', null); + assert(false); + } catch (error) { + assert(/The SET command contains a invalid argument type./.test(error.message)); + } + client.get('foo', helper.isNull(done)); }); it('emit an error with only the key set', function (done) { diff --git a/test/connection.spec.js b/test/connection.spec.js index f6dc8310cb2..7ecee698530 100644 --- a/test/connection.spec.js +++ b/test/connection.spec.js @@ -152,40 +152,6 @@ describe('connection tests', function () { describe('using ' + parser + ' and ' + ip, function () { describe('on lost connection', function () { - it('emit an error after max retry attempts and do not try to reconnect afterwards', function (done) { - var maxAttempts = 3; - var options = { - parser: parser, - maxAttempts: maxAttempts - }; - client = redis.createClient(options); - assert.strictEqual(client.retryBackoff, 1.7); - assert.strictEqual(client.retryDelay, 200); - assert.strictEqual(Object.keys(options).length, 2); - var calls = 0; - - client.once('ready', function () { - helper.killConnection(client); - }); - - client.on('reconnecting', function (params) { - calls++; - }); - - client.on('error', function (err) { - if (/Redis connection in broken state: maximum connection attempts.*?exceeded./.test(err.message)) { - process.nextTick(function () { // End is called after the error got emitted - assert.strictEqual(calls, maxAttempts - 1); - assert.strictEqual(client.emitted_end, true); - assert.strictEqual(client.connected, false); - assert.strictEqual(client.ready, false); - assert.strictEqual(client.closing, true); - done(); - }); - } - }); - }); - it('emit an error after max retry timeout and do not try to reconnect afterwards', function (done) { // TODO: Investigate why this test fails with windows. Reconnect is only triggered once if (process.platform === 'win32') this.skip(); @@ -271,16 +237,11 @@ describe('connection tests', function () { }); it('retryStrategy used to reconnect with individual error', function (done) { - var text = ''; - var unhookIntercept = intercept(function (data) { - text += data; - return ''; - }); client = redis.createClient({ retryStrategy: function (options) { if (options.totalRetryTime > 150) { client.set('foo', 'bar', function (err, res) { - assert.strictEqual(err.message, 'Stream connection ended and command aborted.'); + assert.strictEqual(err.message, 'Redis connection in broken state: maximum connection attempts exceeded.'); assert.strictEqual(err.origin.message, 'Connection timeout'); done(); }); @@ -289,18 +250,8 @@ describe('connection tests', function () { } return Math.min(options.attempt * 25, 200); }, - maxAttempts: 5, - retryMaxDelay: 123, port: 9999 }); - process.nextTick(function () { - assert.strictEqual( - text, - 'node_redis: WARNING: You activated the retry_strategy and max_attempts at the same time. This is not possible and max_attempts will be ignored.\n' + - 'node_redis: WARNING: You activated the retry_strategy and retry_max_delay at the same time. This is not possible and retry_max_delay will be ignored.\n' - ); - unhookIntercept(); - }); }); it('retry_strategy used to reconnect', function (done) { @@ -308,8 +259,8 @@ describe('connection tests', function () { retry_strategy: function (options) { if (options.total_retry_time > 150) { client.set('foo', 'bar', function (err, res) { - assert.strictEqual(err.message, 'Stream connection ended and command aborted.'); - assert.strictEqual(err.code, 'NR_CLOSED'); + assert.strictEqual(err.message, 'Redis connection in broken state: maximum connection attempts exceeded.'); + assert.strictEqual(err.code, 'CONNECTION_BROKEN'); assert.strictEqual(err.origin.code, 'ECONNREFUSED'); done(); }); @@ -337,11 +288,13 @@ describe('connection tests', function () { client.stream.destroy(); }, 50); client.on('error', function (err) { - assert.strictEqual(err.code, 'NR_CLOSED'); - assert.strictEqual(err.message, 'Stream connection ended and command aborted.'); - unhookIntercept(); - redis.debugMode = false; - done(); + if (err instanceof redis.AbortError) { + assert.strictEqual(err.message, 'Redis connection in broken state: maximum connection attempts exceeded.'); + assert.strictEqual(err.code, 'CONNECTION_BROKEN'); + unhookIntercept(); + redis.debugMode = false; + done(); + } }); }); }); diff --git a/test/lib/redis-process.js b/test/lib/redis-process.js index c46fd8d70f0..ce2f881edd6 100644 --- a/test/lib/redis-process.js +++ b/test/lib/redis-process.js @@ -4,7 +4,7 @@ var config = require('./config'); var fs = require('fs'); var path = require('path'); -var spawn = require('win-spawn'); +var spawn = require('cross-spawn'); var tcpPortUsed = require('tcp-port-used'); var bluebird = require('bluebird'); diff --git a/test/multi.spec.js b/test/multi.spec.js index 33ffdbe16ce..233ead776c1 100644 --- a/test/multi.spec.js +++ b/test/multi.spec.js @@ -219,17 +219,22 @@ describe("The 'multi' method", function () { client = redis.createClient({ host: 'somewhere', port: 6379, - max_attempts: 1 + retry_strategy: function (options) { + if (options.attempt > 1) { + // End reconnecting with built in error + return undefined; + } + } }); client.on('error', function (err) { - if (/Redis connection in broken state/.test(err.message)) { + if (/Redis connection to somewhere:6379 failed/.test(err.origin.message)) { done(); } }); client.multi([['set', 'foo', 'bar'], ['get', 'foo']]).exec(function (err, res) { - assert(/Redis connection in broken state/.test(err.message)); + assert(/Redis connection in broken state: maximum connection attempts exceeded/.test(err.message)); assert.strictEqual(err.errors.length, 2); assert.strictEqual(err.errors[0].args.length, 2); }); @@ -239,12 +244,17 @@ describe("The 'multi' method", function () { client = redis.createClient({ host: 'somewhere', port: 6379, - max_attempts: 1 + retry_strategy: function (options) { + if (options.attempt > 1) { + // End reconnecting with built in error + return undefined; + } + } }); client.on('error', function (err) { // Results in multiple done calls if test fails - if (/Redis connection in broken state/.test(err.message)) { + if (/Redis connection to somewhere:6379 failed/.test(err.origin.message)) { done(); } }); diff --git a/test/node_redis.spec.js b/test/node_redis.spec.js index d529d40da3e..3c511ec9af9 100644 --- a/test/node_redis.spec.js +++ b/test/node_redis.spec.js @@ -707,25 +707,6 @@ describe('The node_redis client', function () { }); }); - describe('idle', function () { - it('emits idle as soon as there are no outstanding commands', function (done) { - var end = helper.callFuncAfter(done, 2); - client.on('warning', function (msg) { - assert.strictEqual( - msg, - 'The idle event listener is deprecated and will likely be removed in v.3.0.0.\n' + - 'If you rely on this feature please open a new ticket in node_redis with your use case' - ); - end(); - }); - client.on('idle', function onIdle () { - client.removeListener('idle', onIdle); - client.get('foo', helper.isString('bar', end)); - }); - client.set('foo', 'bar'); - }); - }); - describe('utf8', function () { it('handles utf-8 keys', function (done) { var utf8_sample = 'ಠ_ಠ'; @@ -793,111 +774,6 @@ describe('The node_redis client', function () { // }); }); - describe('socket_nodelay', function () { - describe('true', function () { - var args = config.configureClient(parser, ip, { - socket_nodelay: true - }); - - it("fires client.on('ready')", function (done) { - client = redis.createClient.apply(null, args); - client.on('ready', function () { - assert.strictEqual(true, client.options.socket_nodelay); - client.quit(done); - }); - }); - - it('client is functional', function (done) { - client = redis.createClient.apply(null, args); - client.on('ready', function () { - assert.strictEqual(true, client.options.socket_nodelay); - client.set(['set key 1', 'set val'], helper.isString('OK')); - client.set(['set key 2', 'set val'], helper.isString('OK')); - client.get(['set key 1'], helper.isString('set val')); - client.get(['set key 2'], helper.isString('set val')); - client.quit(done); - }); - }); - }); - - describe('false', function () { - var args = config.configureClient(parser, ip, { - socket_nodelay: false - }); - - it("fires client.on('ready')", function (done) { - client = redis.createClient.apply(null, args); - client.on('ready', function () { - assert.strictEqual(false, client.options.socket_nodelay); - client.quit(done); - }); - }); - - it('client is functional', function (done) { - client = redis.createClient.apply(null, args); - client.on('ready', function () { - assert.strictEqual(false, client.options.socket_nodelay); - client.set(['set key 1', 'set val'], helper.isString('OK')); - client.set(['set key 2', 'set val'], helper.isString('OK')); - client.get(['set key 1'], helper.isString('set val')); - client.get(['set key 2'], helper.isString('set val')); - client.quit(done); - }); - }); - }); - - describe('defaults to true', function () { - - it("fires client.on('ready')", function (done) { - client = redis.createClient.apply(null, args); - client.on('ready', function () { - assert.strictEqual(true, client.options.socket_nodelay); - client.quit(done); - }); - }); - - it('client is functional', function (done) { - client = redis.createClient.apply(null, args); - client.on('ready', function () { - assert.strictEqual(true, client.options.socket_nodelay); - client.set(['set key 1', 'set val'], helper.isString('OK')); - client.set(['set key 2', 'set val'], helper.isString('OK')); - client.get(['set key 1'], helper.isString('set val')); - client.get(['set key 2'], helper.isString('set val')); - client.quit(done); - }); - }); - }); - }); - - describe('retry_max_delay', function () { - it('sets upper bound on how long client waits before reconnecting', function (done) { - var time; - var timeout = process.platform !== 'win32' ? 20 : 100; - - client = redis.createClient.apply(null, config.configureClient(parser, ip, { - retry_max_delay: 1 // ms - })); - client.on('ready', function () { - if (this.times_connected === 1) { - this.stream.end(); - time = Date.now(); - } else { - done(); - } - }); - client.on('reconnecting', function () { - time = Date.now() - time; - assert(time < timeout, 'The reconnect should not have taken longer than ' + timeout + ' but it took ' + time); - }); - client.on('error', function (err) { - // This is rare but it might be triggered. - // So let's have a robust test - assert.strictEqual(err.code, 'ECONNRESET'); - }); - }); - }); - describe('protocol error', function () { it('should gracefully recover and only fail on the already send commands', function (done) { @@ -932,33 +808,9 @@ describe('The node_redis client', function () { describe('enable_offline_queue', function () { describe('true', function () { - it('should emit drain if offline queue is flushed and nothing to buffer', function (done) { - client = redis.createClient({ - parser: parser, - no_ready_check: true - }); - var end = helper.callFuncAfter(done, 3); - client.set('foo', 'bar'); - client.get('foo', end); - client.on('warning', function (msg) { - assert.strictEqual( - msg, - 'The drain event listener is deprecated and will be removed in v.3.0.0.\n' + - 'If you want to keep on listening to this event please listen to the stream drain event directly.' - ); - end(); - }); - client.on('drain', function () { - assert(client.offline_queue.length === 0); - end(); - }); - }); it('does not return an error and enqueues operation', function (done) { - client = redis.createClient(9999, null, { - max_attempts: 0, - parser: parser - }); + client = redis.createClient(9999, null); var finished = false; client.on('error', function (e) { // ignore, b/c expecting a "can't connect" error @@ -980,8 +832,12 @@ describe('The node_redis client', function () { it('enqueues operation and keep the queue while trying to reconnect', function (done) { client = redis.createClient(9999, null, { - max_attempts: 4, - parser: parser + retry_strategy: function (options) { + if (options.attempt > 4) { + return undefined; + } + return 100; + }, }); var i = 0; From 144b7edd8562b2c3cb5046fc8c0797201ccadaaf Mon Sep 17 00:00:00 2001 From: Salakar Date: Sun, 9 Feb 2020 02:08:46 +0000 Subject: [PATCH 0796/1748] start of work towards v3 release --- README.md | 20 +++++++++++++------- 1 file changed, 13 insertions(+), 7 deletions(-) diff --git a/README.md b/README.md index e4f60d3ab32..77197ed7172 100644 --- a/README.md +++ b/README.md @@ -1,34 +1,38 @@

- +

Node Redis

- A high performance Node.js Redis client. +

A high performance Node.js Redis client.

+--- +

NPM downloads NPM version Build Status Coverage Status Windows Tests - Follow on Twitter + Follow on Twitter

--- -This is a complete and feature rich Redis client for node.js. __It supports all +This is a complete and feature rich Redis client for Node.js. __It supports all Redis commands__ and focuses on high performance. Install with: - npm install redis +```bash +npm install redis +``` ## Usage Example ```js -var redis = require("redis"), - client = redis.createClient(); +const redis = require("redis"); +const client = redis.createClient(); // if you'd like to select database 3, instead of 0 (default), call // client.select(3, function() { /* ... */ }); @@ -69,6 +73,7 @@ landscape. ### Promises #### Native Promises + If you are using node v8 or higher, you can promisify node_redis with [util.promisify](https://nodejs.org/api/util.html#util_util_promisify_original) as in: ```js const {promisify} = require('util'); @@ -92,6 +97,7 @@ async function myFunc() { ``` #### Bluebird Promises + You can also use node_redis with promises by promisifying node_redis with [bluebird](https://github.com/petkaantonov/bluebird) as in: From bb05064b143584f8e01bd4da3b52b93ee90ea64e Mon Sep 17 00:00:00 2001 From: Salakar Date: Sun, 9 Feb 2020 02:15:44 +0000 Subject: [PATCH 0797/1748] format docs --- .prettierrc | 11 + CODE_OF_CONDUCT.md | 20 +- README.md | 532 ++++++++++++++++++++++++--------------------- package.json | 1 + 4 files changed, 305 insertions(+), 259 deletions(-) create mode 100644 .prettierrc diff --git a/.prettierrc b/.prettierrc new file mode 100644 index 00000000000..951c38fd13a --- /dev/null +++ b/.prettierrc @@ -0,0 +1,11 @@ +{ + "arrowParens": "avoid", + "trailingComma": "all", + "useTabs": false, + "semi": true, + "singleQuote": true, + "bracketSpacing": true, + "jsxBracketSameLine": false, + "tabWidth": 2, + "printWidth": 100 +} diff --git a/CODE_OF_CONDUCT.md b/CODE_OF_CONDUCT.md index 00aad77d088..50b0a741084 100644 --- a/CODE_OF_CONDUCT.md +++ b/CODE_OF_CONDUCT.md @@ -8,19 +8,19 @@ In the interest of fostering an open and welcoming environment, we as contributo Examples of behavior that contributes to creating a positive environment include: -* Using welcoming and inclusive language -* Being respectful of differing viewpoints and experiences -* Gracefully accepting constructive criticism -* Focusing on what is best for the community -* Showing empathy towards other community members +- Using welcoming and inclusive language +- Being respectful of differing viewpoints and experiences +- Gracefully accepting constructive criticism +- Focusing on what is best for the community +- Showing empathy towards other community members Examples of unacceptable behavior by participants include: -* The use of sexualized language or imagery and unwelcome sexual attention or advances -* Trolling, insulting/derogatory comments, and personal or political attacks -* Public or private harassment -* Publishing others' private information, such as a physical or electronic address, without explicit permission -* Other conduct which could reasonably be considered inappropriate in a professional setting +- The use of sexualized language or imagery and unwelcome sexual attention or advances +- Trolling, insulting/derogatory comments, and personal or political attacks +- Public or private harassment +- Publishing others' private information, such as a physical or electronic address, without explicit permission +- Other conduct which could reasonably be considered inappropriate in a professional setting ## Our Responsibilities diff --git a/README.md b/README.md index 77197ed7172..bd124739b24 100644 --- a/README.md +++ b/README.md @@ -1,6 +1,6 @@

- +

Node Redis

A high performance Node.js Redis client.

@@ -12,15 +12,15 @@ NPM downloads NPM version Build Status - Coverage Status Windows Tests + Coverage Status Follow on Twitter

--- -This is a complete and feature rich Redis client for Node.js. __It supports all -Redis commands__ and focuses on high performance. +This is a complete and feature rich Redis client for Node.js. **It supports all +Redis commands** and focuses on high performance. Install with: @@ -31,25 +31,25 @@ npm install redis ## Usage Example ```js -const redis = require("redis"); +const redis = require('redis'); const client = redis.createClient(); // if you'd like to select database 3, instead of 0 (default), call // client.select(3, function() { /* ... */ }); -client.on("error", function (err) { - console.log("Error " + err); +client.on('error', function(err) { + console.log('Error ' + err); }); -client.set("string key", "string val", redis.print); -client.hset("hash key", "hashtest 1", "some value", redis.print); -client.hset(["hash key", "hashtest 2", "some other value"], redis.print); -client.hkeys("hash key", function (err, replies) { - console.log(replies.length + " replies:"); - replies.forEach(function (reply, i) { - console.log(" " + i + ": " + reply); - }); - client.quit(); +client.set('string key', 'string val', redis.print); +client.hset('hash key', 'hashtest 1', 'some value', redis.print); +client.hset(['hash key', 'hashtest 2', 'some other value'], redis.print); +client.hkeys('hash key', function(err, replies) { + console.log(replies.length + ' replies:'); + replies.forEach(function(reply, i) { + console.log(' ' + i + ': ' + reply); + }); + client.quit(); }); ``` @@ -75,24 +75,28 @@ landscape. #### Native Promises If you are using node v8 or higher, you can promisify node_redis with [util.promisify](https://nodejs.org/api/util.html#util_util_promisify_original) as in: + ```js -const {promisify} = require('util'); +const { promisify } = require('util'); const getAsync = promisify(client.get).bind(client); ``` -now *getAsync* is a promisified version of *client.get*: + +now _getAsync_ is a promisified version of _client.get_: + ```js // We expect a value 'foo': 'bar' to be present // So instead of writing client.get('foo', cb); you have to write: return getAsync('foo').then(function(res) { - console.log(res); // => 'bar' + console.log(res); // => 'bar' }); ``` or using [async await](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Statements/async_function): + ```js async function myFunc() { - const res = await getAsync('foo'); - console.log(res); + const res = await getAsync('foo'); + console.log(res); } ``` @@ -106,20 +110,24 @@ const redis = require('redis'); bluebird.promisifyAll(redis); ``` -It'll add a *Async* to all node_redis functions (e.g. return client.getAsync().then()) +It'll add a _Async_ to all node_redis functions (e.g. return client.getAsync().then()) ```js // We expect a value 'foo': 'bar' to be present // So instead of writing client.get('foo', cb); you have to write: return client.getAsync('foo').then(function(res) { - console.log(res); // => 'bar' + console.log(res); // => 'bar' }); // Using multi with promises looks like: -return client.multi().get('foo').execAsync().then(function(res) { +return client + .multi() + .get('foo') + .execAsync() + .then(function(res) { console.log(res); // => 'bar' -}); + }); ``` ### Sending Commands @@ -130,11 +138,18 @@ a variable number of individual arguments followed by an optional callback. Examples: ```js -client.hmset(["key", "test keys 1", "test val 1", "test keys 2", "test val 2"], function (err, res) {}); +client.hmset(['key', 'test keys 1', 'test val 1', 'test keys 2', 'test val 2'], function( + err, + res, +) {}); // Works the same as -client.hmset("key", ["test keys 1", "test val 1", "test keys 2", "test val 2"], function (err, res) {}); +client.hmset('key', ['test keys 1', 'test val 1', 'test keys 2', 'test val 2'], function(err, res) { + // ... +}); // Or -client.hmset("key", "test keys 1", "test val 1", "test keys 2", "test val 2", function (err, res) {}); +client.hmset('key', 'test keys 1', 'test val 1', 'test keys 2', 'test val 2', function(err, res) { + // ... +}); ``` Care should be taken with user input if arrays are possible (via body-parser, query string or other method), as single arguments could be unintentionally interpreted as multiple args. @@ -142,17 +157,17 @@ Care should be taken with user input if arrays are possible (via body-parser, qu Note that in either form the `callback` is optional: ```js -client.set("some key", "some val"); -client.set(["some other key", "some val"]); +client.set('some key', 'some val'); +client.set(['some other key', 'some val']); ``` If the key is missing, reply will be null. Only if the [Redis Command Reference](http://redis.io/commands) states something else it will not be null. ```js -client.get("missingkey", function(err, reply) { - // reply is null when the key is missing - console.log(reply); +client.get('missingkey', function(err, reply) { + // reply is null when the key is missing + console.log(reply); }); ``` @@ -218,85 +233,87 @@ So please attach the error listener to node_redis. deprecated option / function / similar is used. ## redis.createClient() + If you have `redis-server` running on the same machine as node, then the defaults for port and host are probably fine and you don't need to supply any arguments. `createClient()` returns a `RedisClient` object. Otherwise, `createClient()` accepts these arguments: -* `redis.createClient([options])` -* `redis.createClient(unix_socket[, options])` -* `redis.createClient(redis_url[, options])` -* `redis.createClient(port[, host][, options])` +- `redis.createClient([options])` +- `redis.createClient(unix_socket[, options])` +- `redis.createClient(redis_url[, options])` +- `redis.createClient(port[, host][, options])` -__Tip:__ If the Redis server runs on the same machine as the client consider +**Tip:** If the Redis server runs on the same machine as the client consider using unix sockets if possible to increase throughput. -__Note:__ Using `'rediss://...` for the protocol in a `redis_url` will enable a TLS socket connection. However, additional TLS options will need to be passed in `options`, if required. +**Note:** Using `'rediss://...` for the protocol in a `redis_url` will enable a TLS socket connection. However, additional TLS options will need to be passed in `options`, if required. #### `options` object properties -| Property | Default | Description | -|-----------|-----------|-------------| -| host | 127.0.0.1 | IP address of the Redis server | -| port | 6379 | Port of the Redis server | -| path | null | The UNIX socket string of the Redis server | -| url | null | The URL of the Redis server. Format: `[redis[s]:]//[[user][:password@]][host][:port][/db-number][?db=db-number[&password=bar[&option=value]]]` (More info avaliable at [IANA](http://www.iana.org/assignments/uri-schemes/prov/redis)). | -| string_numbers | null | Set to `true`, `node_redis` will return Redis number values as Strings instead of javascript Numbers. Useful if you need to handle big numbers (above `Number.MAX_SAFE_INTEGER === 2^53`). Hiredis is incapable of this behavior, so setting this option to `true` will result in the built-in javascript parser being used no matter the value of the `parser` option. | -| return_buffers | false | If set to `true`, then all replies will be sent to callbacks as Buffers instead of Strings. | -| detect_buffers | false | If set to `true`, then replies will be sent to callbacks as Buffers. This option lets you switch between Buffers and Strings on a per-command basis, whereas `return_buffers` applies to every command on a client. __Note__: This doesn't work properly with the pubsub mode. A subscriber has to either always return Strings or Buffers. | -| socket_keepalive | true | If set to `true`, the keep-alive functionality is enabled on the underlying socket. | -| socket_initial_delay | 0 | Initial Delay in milliseconds, and this will also behave the interval keep alive message sending to Redis. | -| no_ready_check | false | When a connection is established to the Redis server, the server might still be loading the database from disk. While loading, the server will not respond to any commands. To work around this, `node_redis` has a "ready check" which sends the `INFO` command to the server. The response from the `INFO` command indicates whether the server is ready for more commands. When ready, `node_redis` emits a `ready` event. Setting `no_ready_check` to `true` will inhibit this check. | -| enable_offline_queue | true | By default, if there is no active connection to the Redis server, commands are added to a queue and are executed once the connection has been established. Setting `enable_offline_queue` to `false` will disable this feature and the callback will be executed immediately with an error, or an error will be emitted if no callback is specified. | -| retry_unfulfilled_commands | false | If set to `true`, all commands that were unfulfilled while the connection is lost will be retried after the connection has been reestablished. Use this with caution if you use state altering commands (e.g. `incr`). This is especially useful if you use blocking commands. | -| password | null | If set, client will run Redis auth command on connect. Alias `auth_pass` __Note__ `node_redis` < 2.5 must use `auth_pass` | -| db | null | If set, client will run Redis `select` command on connect. | -| family | IPv4 | You can force using IPv6 if you set the family to 'IPv6'. See Node.js [net](https://nodejs.org/api/net.html) or [dns](https://nodejs.org/api/dns.html) modules on how to use the family type. | -| disable_resubscribing | false | If set to `true`, a client won't resubscribe after disconnecting. | -| rename_commands | null | Passing an object with renamed commands to use instead of the original functions. For example, if you renamed the command KEYS to "DO-NOT-USE" then the rename_commands object would be: `{ KEYS : "DO-NOT-USE" }` . See the [Redis security topics](http://redis.io/topics/security) for more info. | -| tls | null | An object containing options to pass to [tls.connect](http://nodejs.org/api/tls.html#tls_tls_connect_port_host_options_callback) to set up a TLS connection to Redis (if, for example, it is set up to be accessible via a tunnel). | -| prefix | null | A string used to prefix all used keys (e.g. `namespace:test`). Please be aware that the `keys` command will not be prefixed. The `keys` command has a "pattern" as argument and no key and it would be impossible to determine the existing keys in Redis if this would be prefixed. | -| retry_strategy | function | A function that receives an options object as parameter including the retry `attempt`, the `total_retry_time` indicating how much time passed since the last time connected, the `error` why the connection was lost and the number of `times_connected` in total. If you return a number from this function, the retry will happen exactly after that time in milliseconds. If you return a non-number, no further retry will happen and all offline commands are flushed with errors. Return an error to return that specific error to all offline commands. Example below. | + +| Property | Default | Description | +| -------------------------- | --------- | ----------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- | +| host | 127.0.0.1 | IP address of the Redis server | +| port | 6379 | Port of the Redis server | +| path | null | The UNIX socket string of the Redis server | +| url | null | The URL of the Redis server. Format: `[redis[s]:]//[[user][:password@]][host][:port][/db-number][?db=db-number[&password=bar[&option=value]]]` (More info avaliable at [IANA](http://www.iana.org/assignments/uri-schemes/prov/redis)). | +| string_numbers | null | Set to `true`, `node_redis` will return Redis number values as Strings instead of javascript Numbers. Useful if you need to handle big numbers (above `Number.MAX_SAFE_INTEGER === 2^53`). Hiredis is incapable of this behavior, so setting this option to `true` will result in the built-in javascript parser being used no matter the value of the `parser` option. | +| return_buffers | false | If set to `true`, then all replies will be sent to callbacks as Buffers instead of Strings. | +| detect_buffers | false | If set to `true`, then replies will be sent to callbacks as Buffers. This option lets you switch between Buffers and Strings on a per-command basis, whereas `return_buffers` applies to every command on a client. **Note**: This doesn't work properly with the pubsub mode. A subscriber has to either always return Strings or Buffers. | +| socket_keepalive | true | If set to `true`, the keep-alive functionality is enabled on the underlying socket. | +| socket_initial_delay | 0 | Initial Delay in milliseconds, and this will also behave the interval keep alive message sending to Redis. | +| no_ready_check | false | When a connection is established to the Redis server, the server might still be loading the database from disk. While loading, the server will not respond to any commands. To work around this, `node_redis` has a "ready check" which sends the `INFO` command to the server. The response from the `INFO` command indicates whether the server is ready for more commands. When ready, `node_redis` emits a `ready` event. Setting `no_ready_check` to `true` will inhibit this check. | +| enable_offline_queue | true | By default, if there is no active connection to the Redis server, commands are added to a queue and are executed once the connection has been established. Setting `enable_offline_queue` to `false` will disable this feature and the callback will be executed immediately with an error, or an error will be emitted if no callback is specified. | +| retry_unfulfilled_commands | false | If set to `true`, all commands that were unfulfilled while the connection is lost will be retried after the connection has been reestablished. Use this with caution if you use state altering commands (e.g. `incr`). This is especially useful if you use blocking commands. | +| password | null | If set, client will run Redis auth command on connect. Alias `auth_pass` **Note** `node_redis` < 2.5 must use `auth_pass` | +| db | null | If set, client will run Redis `select` command on connect. | +| family | IPv4 | You can force using IPv6 if you set the family to 'IPv6'. See Node.js [net](https://nodejs.org/api/net.html) or [dns](https://nodejs.org/api/dns.html) modules on how to use the family type. | +| disable_resubscribing | false | If set to `true`, a client won't resubscribe after disconnecting. | +| rename_commands | null | Passing an object with renamed commands to use instead of the original functions. For example, if you renamed the command KEYS to "DO-NOT-USE" then the rename_commands object would be: `{ KEYS : "DO-NOT-USE" }` . See the [Redis security topics](http://redis.io/topics/security) for more info. | +| tls | null | An object containing options to pass to [tls.connect](http://nodejs.org/api/tls.html#tls_tls_connect_port_host_options_callback) to set up a TLS connection to Redis (if, for example, it is set up to be accessible via a tunnel). | +| prefix | null | A string used to prefix all used keys (e.g. `namespace:test`). Please be aware that the `keys` command will not be prefixed. The `keys` command has a "pattern" as argument and no key and it would be impossible to determine the existing keys in Redis if this would be prefixed. | +| retry_strategy | function | A function that receives an options object as parameter including the retry `attempt`, the `total_retry_time` indicating how much time passed since the last time connected, the `error` why the connection was lost and the number of `times_connected` in total. If you return a number from this function, the retry will happen exactly after that time in milliseconds. If you return a non-number, no further retry will happen and all offline commands are flushed with errors. Return an error to return that specific error to all offline commands. Example below. | ```js -var redis = require("redis"); -var client = redis.createClient({detect_buffers: true}); +var redis = require('redis'); +var client = redis.createClient({ detect_buffers: true }); -client.set("foo_rand000000000000", "OK"); +client.set('foo_rand000000000000', 'OK'); // This will return a JavaScript String -client.get("foo_rand000000000000", function (err, reply) { - console.log(reply.toString()); // Will print `OK` +client.get('foo_rand000000000000', function(err, reply) { + console.log(reply.toString()); // Will print `OK` }); // This will return a Buffer since original key is specified as a Buffer -client.get(new Buffer("foo_rand000000000000"), function (err, reply) { - console.log(reply.toString()); // Will print `` +client.get(new Buffer('foo_rand000000000000'), function(err, reply) { + console.log(reply.toString()); // Will print `` }); client.quit(); ``` -retry_strategy example +#### `retry_strategy` example ```js var client = redis.createClient({ - retry_strategy: function (options) { - if (options.error && options.error.code === 'ECONNREFUSED') { - // End reconnecting on a specific error and flush all commands with - // a individual error - return new Error('The server refused the connection'); - } - if (options.total_retry_time > 1000 * 60 * 60) { - // End reconnecting after a specific timeout and flush all commands - // with a individual error - return new Error('Retry time exhausted'); - } - if (options.attempt > 10) { - // End reconnecting with built in error - return undefined; - } - // reconnect after - return Math.min(options.attempt * 100, 3000); + retry_strategy: function(options) { + if (options.error && options.error.code === 'ECONNREFUSED') { + // End reconnecting on a specific error and flush all commands with + // a individual error + return new Error('The server refused the connection'); + } + if (options.total_retry_time > 1000 * 60 * 60) { + // End reconnecting after a specific timeout and flush all commands + // with a individual error + return new Error('Retry time exhausted'); + } + if (options.attempt > 10) { + // End reconnecting with built in error + return undefined; } + // reconnect after + return Math.min(options.attempt * 100, 3000); + }, }); ``` @@ -346,17 +363,17 @@ This example closes the connection to the Redis server before the replies have been read. You probably don't want to do this: ```js -var redis = require("redis"), - client = redis.createClient(); +var redis = require('redis'), + client = redis.createClient(); -client.set("foo_rand000000000000", "some fantastic value", function (err, reply) { - // This will either result in an error (flush parameter is set to true) - // or will silently fail and this callback will not be called at all (flush set to false) - console.log(err); +client.set('foo_rand000000000000', 'some fantastic value', function(err, reply) { + // This will either result in an error (flush parameter is set to true) + // or will silently fail and this callback will not be called at all (flush set to false) + console.log(err); }); client.end(true); // No further commands will be processed -client.get("foo_rand000000000000", function (err, reply) { - console.log(err); // => 'The connection has already been closed.' +client.get('foo_rand000000000000', function(err, reply) { + console.log(err); // => 'The connection has already been closed.' }); ``` @@ -366,47 +383,48 @@ client.get("foo_rand000000000000", function (err, reply) { Currently the following error subclasses exist: -* `RedisError`: _All errors_ returned by the client -* `ReplyError` subclass of `RedisError`: All errors returned by __Redis__ itself -* `AbortError` subclass of `RedisError`: All commands that could not finish due +- `RedisError`: _All errors_ returned by the client +- `ReplyError` subclass of `RedisError`: All errors returned by **Redis** itself +- `AbortError` subclass of `RedisError`: All commands that could not finish due to what ever reason -* `ParserError` subclass of `RedisError`: Returned in case of a parser error +- `ParserError` subclass of `RedisError`: Returned in case of a parser error (this should not happen) -* `AggregateError` subclass of `AbortError`: Emitted in case multiple unresolved +- `AggregateError` subclass of `AbortError`: Emitted in case multiple unresolved commands without callback got rejected in debug_mode instead of lots of `AbortError`s. All error classes are exported by the module. Example: + ```js var redis = require('./'); var assert = require('assert'); var client = redis.createClient(); -client.on('error', function (err) { - assert(err instanceof Error); - assert(err instanceof redis.AbortError); - assert(err instanceof redis.AggregateError); - // The set and get get aggregated in here - assert.strictEqual(err.errors.length, 2); - assert.strictEqual(err.code, 'NR_CLOSED'); +client.on('error', function(err) { + assert(err instanceof Error); + assert(err instanceof redis.AbortError); + assert(err instanceof redis.AggregateError); + // The set and get get aggregated in here + assert.strictEqual(err.errors.length, 2); + assert.strictEqual(err.code, 'NR_CLOSED'); }); -client.set('foo', 123, 'bar', function (err, res) { // Too many arguments - assert(err instanceof redis.ReplyError); // => true - assert.strictEqual(err.command, 'SET'); - assert.deepStrictEqual(err.args, ['foo', 123, 'bar']); - - redis.debug_mode = true; - client.set('foo', 'bar'); - client.get('foo'); - process.nextTick(function () { - // Force closing the connection while the command did not yet return - client.end(true); - redis.debug_mode = false; - }); +client.set('foo', 123, 'bar', function(err, res) { + // Too many arguments + assert(err instanceof redis.ReplyError); // => true + assert.strictEqual(err.command, 'SET'); + assert.deepStrictEqual(err.args, ['foo', 123, 'bar']); + + redis.debug_mode = true; + client.set('foo', 'bar'); + client.get('foo'); + process.nextTick(function() { + // Force closing the connection while the command did not yet return + client.end(true); + redis.debug_mode = false; + }); }); - ``` Every `ReplyError` contains the `command` name in all-caps and the arguments (`args`). @@ -414,7 +432,7 @@ Every `ReplyError` contains the `command` name in all-caps and the arguments (`a If node_redis emits a library error because of another error, the triggering error is added to the returned error as `origin` attribute. -___Error codes___ +**_Error codes_** node_redis returns a `NR_CLOSED` error code if the clients connection dropped. If a command unresolved command got rejected a `UNCERTAIN_STATE` code is @@ -428,10 +446,10 @@ the program to exit once no more commands are pending. This is an **experimental** feature, and only supports a subset of the Redis protocol. Any commands where client state is saved on the Redis server, e.g. -`*SUBSCRIBE` or the blocking `BL*` commands will *NOT* work with `.unref()`. +`*SUBSCRIBE` or the blocking `BL*` commands will _NOT_ work with `.unref()`. ```js -var redis = require("redis"); +var redis = require('redis'); var client = redis.createClient(); /* @@ -440,9 +458,9 @@ var client = redis.createClient(); client-server connection is alive. */ client.unref(); -client.get("foo", function (err, value) { - if (err) throw(err); - console.log(value); +client.get('foo', function(err, value) { + if (err) throw err; + console.log(value); }); ``` @@ -461,9 +479,9 @@ syntax. Example: ```js -client.hmset("hosts", "mjr", "1", "another", "23", "home", "1234"); -client.hgetall("hosts", function (err, obj) { - console.dir(obj); +client.hmset('hosts', 'mjr', '1', 'another', '23', 'home', '1234'); +client.hgetall('hosts', function(err, obj) { + console.dir(obj); }); ``` @@ -473,8 +491,8 @@ Multiple values in a hash can be set by supplying an object: ```js client.HMSET(key2, { - "0123456789": "abcdefghij", // NOTE: key and value will be coerced to strings - "some manner of key": "a type of value" + '0123456789': 'abcdefghij', // NOTE: key and value will be coerced to strings + 'some manner of key': 'a type of value', }); ``` @@ -486,7 +504,7 @@ Redis hash. Multiple values may also be set by supplying a list: ```js -client.HMSET(key1, "0123456789", "abcdefghij", "some manner of key", "a type of value"); +client.HMSET(key1, '0123456789', 'abcdefghij', 'some manner of key', 'a type of value'); ``` ## Publish / Subscribe @@ -496,27 +514,28 @@ client connections, subscribes to a channel on one of them, and publishes to tha channel on the other: ```js -var redis = require("redis"); -var sub = redis.createClient(), pub = redis.createClient(); +var redis = require('redis'); +var sub = redis.createClient(), + pub = redis.createClient(); var msg_count = 0; -sub.on("subscribe", function (channel, count) { - pub.publish("a nice channel", "I am sending a message."); - pub.publish("a nice channel", "I am sending a second message."); - pub.publish("a nice channel", "I am sending my last message."); +sub.on('subscribe', function(channel, count) { + pub.publish('a nice channel', 'I am sending a message.'); + pub.publish('a nice channel', 'I am sending a second message.'); + pub.publish('a nice channel', 'I am sending my last message.'); }); -sub.on("message", function (channel, message) { - console.log("sub channel " + channel + ": " + message); - msg_count += 1; - if (msg_count === 3) { - sub.unsubscribe(); - sub.quit(); - pub.quit(); - } +sub.on('message', function(channel, message) { + console.log('sub channel ' + channel + ': ' + message); + msg_count += 1; + if (msg_count === 3) { + sub.unsubscribe(); + sub.quit(); + pub.quit(); + } }); -sub.subscribe("a nice channel"); +sub.subscribe('a nice channel'); ``` When a client issues a `SUBSCRIBE` or `PSUBSCRIBE`, that connection is put into @@ -591,33 +610,35 @@ further information look at [transactions](http://redis.io/topics/transactions)). ```js -var redis = require("./index"), - client = redis.createClient(), set_size = 20; +var redis = require('./index'), + client = redis.createClient(), + set_size = 20; -client.sadd("bigset", "a member"); -client.sadd("bigset", "another member"); +client.sadd('bigset', 'a member'); +client.sadd('bigset', 'another member'); while (set_size > 0) { - client.sadd("bigset", "member " + set_size); - set_size -= 1; + client.sadd('bigset', 'member ' + set_size); + set_size -= 1; } // multi chain with an individual callback -client.multi() - .scard("bigset") - .smembers("bigset") - .keys("*", function (err, replies) { - // NOTE: code in this callback is NOT atomic - // this only happens after the the .exec call finishes. - client.mget(replies, redis.print); - }) - .dbsize() - .exec(function (err, replies) { - console.log("MULTI got " + replies.length + " replies"); - replies.forEach(function (reply, index) { - console.log("Reply " + index + ": " + reply.toString()); - }); +client + .multi() + .scard('bigset') + .smembers('bigset') + .keys('*', function(err, replies) { + // NOTE: code in this callback is NOT atomic + // this only happens after the the .exec call finishes. + client.mget(replies, redis.print); + }) + .dbsize() + .exec(function(err, replies) { + console.log('MULTI got ' + replies.length + ' replies'); + replies.forEach(function(reply, index) { + console.log('Reply ' + index + ': ' + reply.toString()); }); + }); ``` ### Multi.exec([callback]) @@ -638,20 +659,21 @@ can queue individual commands while still sending regular client command as in this example: ```js -var redis = require("redis"), - client = redis.createClient(), multi; +var redis = require('redis'), + client = redis.createClient(), + multi; // start a separate multi command queue multi = client.multi(); -multi.incr("incr thing", redis.print); -multi.incr("incr other thing", redis.print); +multi.incr('incr thing', redis.print); +multi.incr('incr other thing', redis.print); // runs immediately -client.mset("incr thing", 100, "incr other thing", 1, redis.print); +client.mset('incr thing', 100, 'incr other thing', 1, redis.print); // drains multi queue and runs atomically -multi.exec(function (err, replies) { - console.log(replies); // 101, 2 +multi.exec(function(err, replies) { + console.log(replies); // 101, 2 }); ``` @@ -659,16 +681,18 @@ In addition to adding commands to the `MULTI` queue individually, you can also pass an array of commands and arguments to the constructor: ```js -var redis = require("redis"), - client = redis.createClient(); - -client.multi([ - ["mget", "multifoo", "multibar", redis.print], - ["incr", "multifoo"], - ["incr", "multibar"] -]).exec(function (err, replies) { +var redis = require('redis'), + client = redis.createClient(); + +client + .multi([ + ['mget', 'multifoo', 'multibar', redis.print], + ['incr', 'multifoo'], + ['incr', 'multibar'], + ]) + .exec(function(err, replies) { console.log(replies); -}); + }); ``` ### Multi.exec_atomic([callback]) @@ -695,11 +719,11 @@ commands are kept in memory until they are fired. ## Optimistic Locks -Using `multi` you can make sure your modifications run as a transaction, but you -can't be sure you got there first. What if another client modified a key while +Using `multi` you can make sure your modifications run as a transaction, but you +can't be sure you got there first. What if another client modified a key while you were working with it's data? -To solve this, Redis supports the [WATCH](https://redis.io/topics/transactions) +To solve this, Redis supports the [WATCH](https://redis.io/topics/transactions) command, which is meant to be used with MULTI: ```js @@ -711,25 +735,25 @@ client.watch("foo", function( err ){ client.get("foo", function(err, result) { if(err) throw err; - + // Process result // Heavy and time consuming operation here client.multi() .set("foo", "some heavy computation") .exec(function(err, results) { - + /** - * If err is null, it means Redis successfully attempted + * If err is null, it means Redis successfully attempted * the operation. - */ + */ if(err) throw err; - + /** * If results === null, it means that a concurrent client - * changed the key while we were processing it and thus + * changed the key while we were processing it and thus * the execution of the MULTI command was not performed. - * + * * NOTICE: Failing an execution of MULTI is not considered * an error. So you will have err === null and results === null */ @@ -741,11 +765,11 @@ client.watch("foo", function( err ){ The above snippet shows the correct usage of `watch` with `multi`. Every time a watched key is changed before the execution of a `multi` command, the execution -will return `null`. On a normal situation, the execution will return an array of -values with the results of the operations. +will return `null`. On a normal situation, the execution will return an array of +values with the results of the operations. As stated in the snippet, failing the execution of a `multi` command being watched -is not considered an error. The execution may return an error if, for example, the +is not considered an error. The execution may return an error if, for example, the client cannot connect to Redis. An example where we can see the execution of a `multi` command fail is as follows: @@ -759,7 +783,7 @@ clients.watcher.watch('foo',function(err) { if (err) { throw err; } //if you comment out the next line, the transaction will work clients.alterer.set('foo',Math.random(), (err) => {if (err) { throw err; }}); - + //using a setTimeout here to ensure that the MULTI/EXEC will come after the SET. //Normally, you would use a callback to ensure order, but I want the above SET command //to be easily comment-out-able. @@ -769,7 +793,7 @@ clients.watcher.watch('foo',function(err) { .set('foo','abc') .set('bar','1234') .exec((err,results) => { - if (err) { throw err; } + if (err) { throw err; } if (results === null) { console.log('transaction aborted because results were null'); } else { @@ -784,7 +808,7 @@ clients.watcher.watch('foo',function(err) { ### WATCH limitations -Redis WATCH works only on *whole* key values. For example, with WATCH you can +Redis WATCH works only on _whole_ key values. For example, with WATCH you can watch a hash for modifications, but you cannot watch a specific field of a hash. The following example would watch the keys `foo` and `hello`, not the field `hello` @@ -797,22 +821,22 @@ var redis = require("redis"), client.hget( "foo", "hello", function(err, result){ //Do some processing with the value from this field and watch it after - + client.watch("foo", "hello", function( err ){ if(err) throw err; /** * WRONG: This is now watching the keys 'foo' and 'hello'. It is not * watching the field 'hello' of hash 'foo'. Because the key 'foo' - * refers to a hash, this command is now watching the entire hash - * for modifications. - */ + * refers to a hash, this command is now watching the entire hash + * for modifications. + */ }); } ) ``` -This limitation also applies to sets ( cannot watch individual set members ) +This limitation also applies to sets ( cannot watch individual set members ) and any other collections. ## Monitor mode @@ -829,14 +853,14 @@ arguments and the raw monitoring string. Example: ```js -var client = require("redis").createClient(); -client.monitor(function (err, res) { - console.log("Entering monitoring mode."); +var client = require('redis').createClient(); +client.monitor(function(err, res) { + console.log('Entering monitoring mode.'); }); client.set('foo', 'bar'); -client.on("monitor", function (time, args, raw_reply) { - console.log(time + ": " + args); // 1458910076.446514:['set', 'foo', 'bar'] +client.on('monitor', function(time, args, raw_reply) { + console.log(time + ': ' + args); // 1458910076.446514:['set', 'foo', 'bar'] }); ``` @@ -862,12 +886,12 @@ easy comparison. A handy callback function for displaying return values when testing. Example: ```js -var redis = require("redis"), - client = redis.createClient(); +var redis = require('redis'), + client = redis.createClient(); -client.on("connect", function () { - client.set("foo_rand000000000000", "some fantastic value", redis.print); - client.get("foo_rand000000000000", redis.print); +client.on('connect', function() { + client.set('foo_rand000000000000', 'some fantastic value', redis.print); + client.get('foo_rand000000000000', redis.print); }); ``` @@ -885,7 +909,10 @@ the second word as first parameter: ```js client.script('load', 'return 1'); -client.multi().script('load', 'return 1').exec(); +client + .multi() + .script('load', 'return 1') + .exec(); client.multi([['script', 'load', 'return 1']]).exec(); ``` @@ -898,33 +925,35 @@ returns it in the callback. If an error occurs in the meanwhile, that is going to return an error instead in the callback. One example of when to use duplicate() would be to accommodate the connection- -blocking redis commands BRPOP, BLPOP, and BRPOPLPUSH. If these commands +blocking redis commands BRPOP, BLPOP, and BRPOPLPUSH. If these commands are used on the same redisClient instance as non-blocking commands, the non-blocking ones may be queued up until after the blocking ones finish. ```js -var Redis=require('redis'); +var Redis = require('redis'); var client = Redis.createClient(); var clientBlocking = client.duplicate(); var get = function() { - console.log("get called"); - client.get("any_key",function() { console.log("get returned"); }); - setTimeout( get, 1000 ); + console.log('get called'); + client.get('any_key', function() { + console.log('get returned'); + }); + setTimeout(get, 1000); }; var brpop = function() { - console.log("brpop called"); - clientBlocking.brpop("nonexistent", 5, function() { - console.log("brpop return"); - setTimeout( brpop, 1000 ); - }); + console.log('brpop called'); + clientBlocking.brpop('nonexistent', 5, function() { + console.log('brpop return'); + setTimeout(brpop, 1000); + }); }; get(); brpop(); ``` Another reason to use duplicate() is when multiple DBs on the same server are -accessed via the redis SELECT command. Each DB could use its own connection. +accessed via the redis SELECT command. Each DB could use its own connection. ## client.send_command(command_name[, [args][, callback]]) @@ -960,32 +989,34 @@ commands. ### Commands with Optional and Keyword arguments -This applies to anything that uses an optional `[WITHSCORES]` or `[LIMIT offset -count]` in the [redis.io/commands](http://redis.io/commands) documentation. +This applies to anything that uses an optional `[WITHSCORES]` or `[LIMIT offset count]` in the [redis.io/commands](http://redis.io/commands) documentation. Example: ```js -var args = [ 'myzset', 1, 'one', 2, 'two', 3, 'three', 99, 'ninety-nine' ]; -client.zadd(args, function (err, response) { +var args = ['myzset', 1, 'one', 2, 'two', 3, 'three', 99, 'ninety-nine']; +client.zadd(args, function(err, response) { + if (err) throw err; + console.log('added ' + response + ' items.'); + + // -Infinity and +Infinity also work + var args1 = ['myzset', '+inf', '-inf']; + client.zrevrangebyscore(args1, function(err, response) { if (err) throw err; - console.log('added '+response+' items.'); - - // -Infinity and +Infinity also work - var args1 = [ 'myzset', '+inf', '-inf' ]; - client.zrevrangebyscore(args1, function (err, response) { - if (err) throw err; - console.log('example1', response); - // write your code here - }); - - var max = 3, min = 1, offset = 1, count = 2; - var args2 = [ 'myzset', max, min, 'WITHSCORES', 'LIMIT', offset, count ]; - client.zrevrangebyscore(args2, function (err, response) { - if (err) throw err; - console.log('example2', response); - // write your code here - }); + console.log('example1', response); + // write your code here + }); + + var max = 3, + min = 1, + offset = 1, + count = 2; + var args2 = ['myzset', max, min, 'WITHSCORES', 'LIMIT', offset, count]; + client.zrevrangebyscore(args2, function(err, response) { + if (err) throw err; + console.log('example2', response); + // write your code here + }); }); ``` @@ -1031,7 +1062,7 @@ clients: 1, NodeJS: 6.2.0, Redis: 3.2.0, parser: javascript, connected by: tcp GET 4MiB str, batch 20/1 avg/max: 101.61/118.11 2541ms total, 197 ops/sec GET 4MiB buf, 1/1 avg/max: 2.32/ 14.93 2502ms total, 430 ops/sec GET 4MiB buf, batch 20/1 avg/max: 65.01/ 84.72 2536ms total, 308 ops/sec - ``` +``` ## Debugging @@ -1045,14 +1076,17 @@ application in development mode instead (`NODE_ENV=development`). Good stack traces are only activated in development and debug mode as this results in a significant performance penalty. -___Comparison___: +**_Comparison_**: Useless stack trace: + ``` ReplyError: ERR wrong number of arguments for 'set' command at parseError (/home/ruben/repos/redis/node_modules/redis-parser/lib/parser.js:158:12) at parseType (/home/ruben/repos/redis/node_modules/redis-parser/lib/parser.js:219:14) ``` + Good stack trace: + ``` ReplyError: ERR wrong number of arguments for 'set' command at new Command (/home/ruben/repos/redis/lib/command.js:9:902) diff --git a/package.json b/package.json index e322fc6ef45..13f610f7d43 100644 --- a/package.json +++ b/package.json @@ -36,6 +36,7 @@ }, "dependencies": { "denque": "^1.4.1", + "prettier": "^1.19.1", "redis-commands": "^1.5.0", "redis-errors": "^1.2.0", "redis-parser": "^3.0.0" From 7ce08584563184640dafdfd4ab23a32b339a66d2 Mon Sep 17 00:00:00 2001 From: Salakar Date: Sun, 9 Feb 2020 02:28:12 +0000 Subject: [PATCH 0798/1748] docs --- .prettierrc | 2 +- CHANGELOG.md | 663 ++++++++++++++++++++++++++------------------------- README.md | 277 ++++++++------------- 3 files changed, 437 insertions(+), 505 deletions(-) diff --git a/.prettierrc b/.prettierrc index 951c38fd13a..1ca516a86c0 100644 --- a/.prettierrc +++ b/.prettierrc @@ -3,7 +3,7 @@ "trailingComma": "all", "useTabs": false, "semi": true, - "singleQuote": true, + "singleQuote": false, "bracketSpacing": true, "jsxBracketSameLine": false, "tabWidth": 2, diff --git a/CHANGELOG.md b/CHANGELOG.md index daea8eeefc0..49be75d7d1b 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -1,35 +1,35 @@ # Changelog -## v.3.0.0 - 09 Feb, 2020 +## v3.0.0 - 09 Feb, 2020 -This version is mainly a release to distribute all the unreleased changes on master since 2017 and additionally removes +This version is mainly a release to distribute all the unreleased changes on master since 2017 and additionally removes a lot of old deprecated features and old internals in preparation for an upcoming modernization refactor (v4). ### Breaking Changes - - Dropped support for Node.js < 6 - - Dropped support for `hiredis` (no longer required) - - Removed previously deprecated `drain` event - - Removed previously deprecated `idle` event - - Removed previously deprecated `parser` option - - Removed previously deprecated `max_delay` option - - Removed previously deprecated `max_attempts` option - - Removed previously deprecated `socket_no_delay` option +- Dropped support for Node.js < 6 +- Dropped support for `hiredis` (no longer required) +- Removed previously deprecated `drain` event +- Removed previously deprecated `idle` event +- Removed previously deprecated `parser` option +- Removed previously deprecated `max_delay` option +- Removed previously deprecated `max_attempts` option +- Removed previously deprecated `socket_no_delay` option ### Bug Fixes - - Removed development files from published package (#1370) - - Duplicate function now allows db param to be passed (#1311) +- Removed development files from published package (#1370) +- Duplicate function now allows db param to be passed (#1311) ### Features - - Upgraded to latest `redis-commands` package - - Upgraded to latest `redis-parser` package, v3.0.0, which brings performance improvements - - Replaced `double-ended-queue` with `denque`, which brings performance improvements - - Add timestamps to debug traces - - Add `socket_initial_delay` option for `socket.setKeepAlive` (#1396) - -## v.2.8.0 - 31 Jul, 2017 +- Upgraded to latest `redis-commands` package +- Upgraded to latest `redis-parser` package, v3.0.0, which brings performance improvements +- Replaced `double-ended-queue` with `denque`, which brings performance improvements +- Add timestamps to debug traces +- Add `socket_initial_delay` option for `socket.setKeepAlive` (#1396) + +## v2.8.0 - 31 Jul, 2017 Features @@ -42,13 +42,13 @@ Bugfixes - Fixed emitting internal errors while reconnecting with auth - Fixed crashing with invalid url option -## v.2.7.1 - 14 Mar, 2017 +## v2.7.1 - 14 Mar, 2017 Bugfixes - Fixed monitor mode not working in combination with IPv6 (2.6.0 regression) -## v.2.7.0 - 11 Mar, 2017 +## v2.7.0 - 11 Mar, 2017 Features @@ -59,160 +59,160 @@ Bugfixes - Fixed rename_commands not accepting `null` as value - Fixed `AbortError`s and `AggregateError`s not showing the error message in the stack trace -## v.2.6.5 - 15 Jan, 2017 +## v2.6.5 - 15 Jan, 2017 Bugfixes - Fixed parser not being reset in case the redis connection closed ASAP for overcoming of output buffer limits - Fixed parser reset if (p)message_buffer listener is attached -## v.2.6.4 - 12 Jan, 2017 +## v2.6.4 - 12 Jan, 2017 Bugfixes - Fixed monitor mode not working in combination with IPv6, sockets or lua scripts (2.6.0 regression) -## v.2.6.3 - 31 Oct, 2016 +## v2.6.3 - 31 Oct, 2016 Bugfixes - Do not change the tls setting to camel_case - Fix domain handling in combination with the offline queue (2.5.3 regression) -## v.2.6.2 - 16 Jun, 2016 +## v2.6.2 - 16 Jun, 2016 Bugfixes - Fixed individual callbacks of a transaction not being called (2.6.0 regression) -## v.2.6.1 - 02 Jun, 2016 +## v2.6.1 - 02 Jun, 2016 Bugfixes - Fixed invalid function name being exported -## v.2.6.0 - 01 Jun, 2016 +## v2.6.0 - 01 Jun, 2016 In addition to the pre-releases the following changes exist in v.2.6.0: Features -- Updated [redis-parser](https://github.com/NodeRedis/node-redis-parser) dependency ([changelog](https://github.com/NodeRedis/node-redis-parser/releases/tag/v.2.0.0)) - - The JS parser is from now on the new default as it is a lot faster than the hiredis parser - - This is no BC as there is no changed behavior for the user at all but just a performance improvement. Explicitly requireing the Hiredis parser is still possible. -- Added name property to all Redis functions (Node.js >= 4.0) -- Improved stack traces in development and debug mode +- Updated [redis-parser](https://github.com/NodeRedis/node-redis-parser) dependency ([changelog](https://github.com/NodeRedis/node-redis-parser/releases/tag/v.2.0.0)) +- The JS parser is from now on the new default as it is a lot faster than the hiredis parser +- This is no BC as there is no changed behavior for the user at all but just a performance improvement. Explicitly requireing the Hiredis parser is still possible. +- Added name property to all Redis functions (Node.js >= 4.0) +- Improved stack traces in development and debug mode Bugfixes -- Reverted support for `__proto__` (v.2.6.0-2) to prevent and breaking change +- Reverted support for `__proto__` (v.2.6.0-2) to prevent and breaking change Deprecations -- The `parser` option is deprecated and should be removed. The built-in Javascript parser is a lot faster than the hiredis parser and has more features +- The `parser` option is deprecated and should be removed. The built-in Javascript parser is a lot faster than the hiredis parser and has more features -## v.2.6.0-2 - 29 Apr, 2016 +## v2.6.0-2 - 29 Apr, 2016 Features -- Added support for the new [CLIENT REPLY ON|OFF|SKIP](http://redis.io/commands/client-reply) command (Redis v.3.2) -- Added support for camelCase - - The Node.js landscape default is to use camelCase. node_redis is a bit out of the box here - but from now on it is possible to use both, just as you prefer! - - If there's any documented variable missing as camelCased, please open a issue for it -- Improve error handling significantly - - Only emit an error if the error has not already been handled in a callback - - Improved unspecific error messages e.g. "Connection gone from end / close event" - - Added `args` to command errors to improve identification of the error - - Added origin to errors if there's e.g. a connection error - - Added ReplyError class. All Redis errors are from now on going to be of that class - - Added AbortError class. A subclass of AbortError. All unresolved and by node_redis rejected commands are from now on of that class - - Added AggregateError class. If a unresolved and by node_redis rejected command has no callback and - this applies to more than a single command, the errors for the commands without callback are aggregated - to a single error that is emitted in debug_mode in that case. -- Added `message_buffer` / `pmessage_buffer` events. That event is always going to emit a buffer - - Listening to the `message` event at the same time is always going to return the same message as string -- Added callback option to the duplicate function -- Added support for `__proto__` and other reserved keywords as hgetall field -- Updated [redis-commands](https://github.com/NodeRedis/redis-commands) dependency ([changelog](https://github.com/NodeRedis/redis-commands/releases/tag/v.1.2.0)) +- Added support for the new [CLIENT REPLY ON|OFF|SKIP](http://redis.io/commands/client-reply) command (Redis v.3.2) +- Added support for camelCase +- The Node.js landscape default is to use camelCase. node_redis is a bit out of the box here + but from now on it is possible to use both, just as you prefer! +- If there's any documented variable missing as camelCased, please open a issue for it +- Improve error handling significantly +- Only emit an error if the error has not already been handled in a callback +- Improved unspecific error messages e.g. "Connection gone from end / close event" +- Added `args` to command errors to improve identification of the error +- Added origin to errors if there's e.g. a connection error +- Added ReplyError class. All Redis errors are from now on going to be of that class +- Added AbortError class. A subclass of AbortError. All unresolved and by node_redis rejected commands are from now on of that class +- Added AggregateError class. If a unresolved and by node_redis rejected command has no callback and + this applies to more than a single command, the errors for the commands without callback are aggregated + to a single error that is emitted in debug_mode in that case. +- Added `message_buffer` / `pmessage_buffer` events. That event is always going to emit a buffer +- Listening to the `message` event at the same time is always going to return the same message as string +- Added callback option to the duplicate function +- Added support for `__proto__` and other reserved keywords as hgetall field +- Updated [redis-commands](https://github.com/NodeRedis/redis-commands) dependency ([changelog](https://github.com/NodeRedis/redis-commands/releases/tag/v.1.2.0)) Bugfixes -- Fixed v.2.5.0 auth command regression (under special circumstances a reconnect would not authenticate properly) -- Fixed v.2.6.0-0 pub sub mode and quit command regressions: - - Entering pub sub mode not working if a earlier called and still running command returned an error - - Unsubscribe callback not called if unsubscribing from all channels and resubscribing right away - - Quit command resulting in an error in some cases -- Fixed special handled functions in batch and multi context not working the same as without (e.g. select and info) - - Be aware that not all commands work in combination with transactions but they all work with batch -- Fixed address always set to 127.0.0.1:6379 in case host / port is set in the `tls` options instead of the general options +- Fixed v.2.5.0 auth command regression (under special circumstances a reconnect would not authenticate properly) +- Fixed v.2.6.0-0 pub sub mode and quit command regressions: +- Entering pub sub mode not working if a earlier called and still running command returned an error +- Unsubscribe callback not called if unsubscribing from all channels and resubscribing right away +- Quit command resulting in an error in some cases +- Fixed special handled functions in batch and multi context not working the same as without (e.g. select and info) +- Be aware that not all commands work in combination with transactions but they all work with batch +- Fixed address always set to 127.0.0.1:6379 in case host / port is set in the `tls` options instead of the general options -## v.2.6.0-1 - 01 Apr, 2016 +## v2.6.0-1 - 01 Apr, 2016 A second pre-release with further fixes. This is likely going to be released as 2.6.0 stable without further changes. Features -- Added type validations for client.send_command arguments +- Added type validations for client.send_command arguments Bugfixes -- Fixed client.send_command not working properly with every command and every option -- Fixed pub sub mode unsubscribing from all channels in combination with the new `string_numbers` option crashing -- Fixed pub sub mode unsubscribing from all channels not respected while reconnecting -- Fixed pub sub mode events in combination with the `string_numbers` option emitting the number of channels not as number +- Fixed client.send_command not working properly with every command and every option +- Fixed pub sub mode unsubscribing from all channels in combination with the new `string_numbers` option crashing +- Fixed pub sub mode unsubscribing from all channels not respected while reconnecting +- Fixed pub sub mode events in combination with the `string_numbers` option emitting the number of channels not as number -## v.2.6.0-0 - 27 Mar, 2016 +## v2.6.0-0 - 27 Mar, 2016 This is mainly a very important bug fix release with some smaller features. Features -- Monitor and pub sub mode now work together with the offline queue - - All commands that were send after a connection loss are now going to be send after reconnecting -- Activating monitor mode does now work together with arbitrary commands including pub sub mode -- Pub sub mode is completely rewritten and all known issues fixed -- Added `string_numbers` option to get back strings instead of numbers -- Quit command is from now on always going to end the connection properly +- Monitor and pub sub mode now work together with the offline queue +- All commands that were send after a connection loss are now going to be send after reconnecting +- Activating monitor mode does now work together with arbitrary commands including pub sub mode +- Pub sub mode is completely rewritten and all known issues fixed +- Added `string_numbers` option to get back strings instead of numbers +- Quit command is from now on always going to end the connection properly Bugfixes -- Fixed calling monitor command while other commands are still running -- Fixed monitor and pub sub mode not working together -- Fixed monitor mode not working in combination with the offline queue -- Fixed pub sub mode not working in combination with the offline queue -- Fixed pub sub mode resubscribing not working with non utf8 buffer channels -- Fixed pub sub mode crashing if calling unsubscribe / subscribe in various combinations -- Fixed pub sub mode emitting unsubscribe even if no channels were unsubscribed -- Fixed pub sub mode emitting a message without a message published -- Fixed quit command not ending the connection and resulting in further reconnection if called while reconnecting +- Fixed calling monitor command while other commands are still running +- Fixed monitor and pub sub mode not working together +- Fixed monitor mode not working in combination with the offline queue +- Fixed pub sub mode not working in combination with the offline queue +- Fixed pub sub mode resubscribing not working with non utf8 buffer channels +- Fixed pub sub mode crashing if calling unsubscribe / subscribe in various combinations +- Fixed pub sub mode emitting unsubscribe even if no channels were unsubscribed +- Fixed pub sub mode emitting a message without a message published +- Fixed quit command not ending the connection and resulting in further reconnection if called while reconnecting The quit command did not end connections earlier if the connection was down at that time and this could have lead to strange situations, therefor this was fixed to end the connection right away in those cases. -## v.2.5.3 - 21 Mar, 2016 +## v2.5.3 - 21 Mar, 2016 Bugfixes -- Revert throwing on invalid data types and print a warning instead +- Revert throwing on invalid data types and print a warning instead -## v.2.5.2 - 16 Mar, 2016 +## v2.5.2 - 16 Mar, 2016 Bugfixes -- Fixed breaking changes against Redis 2.4 introduced in 2.5.0 / 2.5.1 +- Fixed breaking changes against Redis 2.4 introduced in 2.5.0 / 2.5.1 -## v.2.5.1 - 15 Mar, 2016 +## v2.5.1 - 15 Mar, 2016 Bugfixes -- Fixed info command not working anymore with optional section argument +- Fixed info command not working anymore with optional section argument -## v.2.5.0 - 15 Mar, 2016 +## v2.5.0 - 15 Mar, 2016 Same changelog as the pre-release -## v.2.5.0-1 - 07 Mar, 2016 +## v2.5.0-1 - 07 Mar, 2016 This is a big release with some substantial underlining changes. Therefor this is released as a pre-release and I encourage anyone who's able to, to test this out. @@ -222,173 +222,173 @@ This release is also going to deprecate a couple things to prepare for a future Features -- The parsers moved into the [redis-parser](https://github.com/NodeRedis/node-redis-parser) module and will be maintained in there from now on - - Improve js parser speed significantly for big SUNION/SINTER/LRANGE/ZRANGE -- Improve redis-url parsing to also accept the database-number and options as query parameters as suggested in [IANA](http://www.iana.org/assignments/uri-schemes/prov/redis) -- Added a `retry_unfulfilled_commands` option - - Setting this to 'true' results in retrying all commands that were not fulfilled on a connection loss after the reconnect. Use with caution -- Added a `db` option to select the database while connecting (this is [not recommended](https://groups.google.com/forum/#!topic/redis-db/vS5wX8X4Cjg)) -- Added a `password` option as alias for auth_pass -- The client.server_info is from now on updated while using the info command -- Gracefuly handle redis protocol errors from now on -- Added a `warning` emitter that receives node_redis warnings like auth not required and deprecation messages -- Added a `retry_strategy` option that replaces all reconnect options -- The reconnecting event from now on also receives: - - The error message why the reconnect happened (params.error) - - The amount of times the client was connected (params.times_connected) - - The total reconnecting time since the last time connected (params.total_retry_time) -- Always respect the command execution order no matter if the reply could be returned sync or not (former exceptions: [#937](https://github.com/NodeRedis/node_redis/issues/937#issuecomment-167525939)) -- redis.createClient is now checking input values stricter and detects more faulty input -- Started refactoring internals into individual modules -- Pipelining speed improvements +- The parsers moved into the [redis-parser](https://github.com/NodeRedis/node-redis-parser) module and will be maintained in there from now on +- Improve js parser speed significantly for big SUNION/SINTER/LRANGE/ZRANGE +- Improve redis-url parsing to also accept the database-number and options as query parameters as suggested in [IANA](http://www.iana.org/assignments/uri-schemes/prov/redis) +- Added a `retry_unfulfilled_commands` option +- Setting this to 'true' results in retrying all commands that were not fulfilled on a connection loss after the reconnect. Use with caution +- Added a `db` option to select the database while connecting (this is [not recommended](https://groups.google.com/forum/#!topic/redis-db/vS5wX8X4Cjg)) +- Added a `password` option as alias for auth_pass +- The client.server_info is from now on updated while using the info command +- Gracefuly handle redis protocol errors from now on +- Added a `warning` emitter that receives node_redis warnings like auth not required and deprecation messages +- Added a `retry_strategy` option that replaces all reconnect options +- The reconnecting event from now on also receives: +- The error message why the reconnect happened (params.error) +- The amount of times the client was connected (params.times_connected) +- The total reconnecting time since the last time connected (params.total_retry_time) +- Always respect the command execution order no matter if the reply could be returned sync or not (former exceptions: [#937](https://github.com/NodeRedis/node_redis/issues/937#issuecomment-167525939)) +- redis.createClient is now checking input values stricter and detects more faulty input +- Started refactoring internals into individual modules +- Pipelining speed improvements Bugfixes -- Fixed explicit undefined as a command callback in a multi context -- Fixed hmset failing to detect the first key as buffer or date if the key is of that type -- Fixed do not run toString on an array argument and throw a "invalid data" error instead - - This is not considered as breaking change, as this is likely a error in your code and if you want to have such a behavior you should handle this beforehand - - The same applies to Map / Set and individual Object types -- Fixed redis url not accepting the protocol being omitted or protocols other than the redis protocol for convenience -- Fixed parsing the db keyspace even if the first database does not begin with a zero -- Fixed handling of errors occurring while receiving pub sub messages -- Fixed huge string pipelines crashing NodeJS (Pipeline size above 256mb) -- Fixed rename_commands and prefix option not working together -- Fixed ready being emitted to early in case a slave is still syncing / master down +- Fixed explicit undefined as a command callback in a multi context +- Fixed hmset failing to detect the first key as buffer or date if the key is of that type +- Fixed do not run toString on an array argument and throw a "invalid data" error instead +- This is not considered as breaking change, as this is likely a error in your code and if you want to have such a behavior you should handle this beforehand +- The same applies to Map / Set and individual Object types +- Fixed redis url not accepting the protocol being omitted or protocols other than the redis protocol for convenience +- Fixed parsing the db keyspace even if the first database does not begin with a zero +- Fixed handling of errors occurring while receiving pub sub messages +- Fixed huge string pipelines crashing NodeJS (Pipeline size above 256mb) +- Fixed rename_commands and prefix option not working together +- Fixed ready being emitted to early in case a slave is still syncing / master down Deprecations -- Using any command with a argument being set to null or undefined is deprecated - - From v.3.0.0 on using a command with such an argument will return an error instead - - If you want to keep the old behavior please use a precheck in your code that converts the arguments to a string. - - Using SET or SETEX with a undefined or null value will from now on also result in converting the value to "null" / "undefined" to have a consistent behavior. This is not considered as breaking change, as it returned an error earlier. -- Using .end(flush) without the flush parameter is deprecated and the flush parameter should explicitly be used - - From v.3.0.0 on using .end without flush will result in an error - - Using .end without flush means that any command that did not yet return is going to silently fail. Therefor this is considered harmful and you should explicitly silence such errors if you are sure you want this -- Depending on the return value of a command to detect the backpressure is deprecated - - From version 3.0.0 on node_redis might not return true / false as a return value anymore. Please rely on client.should_buffer instead -- The `socket_nodelay` option is deprecated and will be removed in v.3.0.0 - - If you want to buffer commands you should use [.batch or .multi](./README.md) instead. This is necessary to reduce the amount of different options and this is very likely reducing your throughput if set to false. - - If you are sure you want to activate the NAGLE algorithm you can still activate it by using client.stream.setNoDelay(false) -- The `max_attempts` option is deprecated and will be removed in v.3.0.0. Please use the `retry_strategy` instead -- The `retry_max_delay` option is deprecated and will be removed in v.3.0.0. Please use the `retry_strategy` instead -- The drain event is deprecated and will be removed in v.3.0.0. Please listen to the stream drain event instead -- The idle event is deprecated and will likely be removed in v.3.0.0. If you rely on this feature please open a new ticket in node_redis with your use case -- Redis < v. 2.6 is not officially supported anymore and might not work in all cases. Please update to a newer redis version as it is not possible to test for these old versions -- Removed non documented command syntax (adding the callback to an arguments array instead of passing it as individual argument) - -## v.2.4.2 - 27 Nov, 2015 +- Using any command with a argument being set to null or undefined is deprecated +- From v.3.0.0 on using a command with such an argument will return an error instead +- If you want to keep the old behavior please use a precheck in your code that converts the arguments to a string. +- Using SET or SETEX with a undefined or null value will from now on also result in converting the value to "null" / "undefined" to have a consistent behavior. This is not considered as breaking change, as it returned an error earlier. +- Using .end(flush) without the flush parameter is deprecated and the flush parameter should explicitly be used +- From v.3.0.0 on using .end without flush will result in an error +- Using .end without flush means that any command that did not yet return is going to silently fail. Therefor this is considered harmful and you should explicitly silence such errors if you are sure you want this +- Depending on the return value of a command to detect the backpressure is deprecated +- From version 3.0.0 on node_redis might not return true / false as a return value anymore. Please rely on client.should_buffer instead +- The `socket_nodelay` option is deprecated and will be removed in v.3.0.0 +- If you want to buffer commands you should use [.batch or .multi](./README.md) instead. This is necessary to reduce the amount of different options and this is very likely reducing your throughput if set to false. +- If you are sure you want to activate the NAGLE algorithm you can still activate it by using client.stream.setNoDelay(false) +- The `max_attempts` option is deprecated and will be removed in v.3.0.0. Please use the `retry_strategy` instead +- The `retry_max_delay` option is deprecated and will be removed in v.3.0.0. Please use the `retry_strategy` instead +- The drain event is deprecated and will be removed in v.3.0.0. Please listen to the stream drain event instead +- The idle event is deprecated and will likely be removed in v.3.0.0. If you rely on this feature please open a new ticket in node_redis with your use case +- Redis < v. 2.6 is not officially supported anymore and might not work in all cases. Please update to a newer redis version as it is not possible to test for these old versions +- Removed non documented command syntax (adding the callback to an arguments array instead of passing it as individual argument) + +## v2.4.2 - 27 Nov, 2015 Bugfixes -- Fixed not emitting ready after reconnect with disable_resubscribing ([@maxgalbu](https://github.com/maxgalbu)) +- Fixed not emitting ready after reconnect with disable_resubscribing ([@maxgalbu](https://github.com/maxgalbu)) -## v.2.4.1 - 25 Nov, 2015 +## v2.4.1 - 25 Nov, 2015 Bugfixes -- Fixed a js parser regression introduced in 2.4.0 ([@BridgeAR](https://github.com/BridgeAR)) +- Fixed a js parser regression introduced in 2.4.0 ([@BridgeAR](https://github.com/BridgeAR)) -## v.2.4.0 - 25 Nov, 2015 +## v2.4.0 - 25 Nov, 2015 Features -- Added `tls` option to initiate a connection to a redis server behind a TLS proxy. Thanks ([@paddybyers](https://github.com/paddybyers)) -- Added `prefix` option to auto key prefix any command with the provided prefix ([@luin](https://github.com/luin) & [@BridgeAR](https://github.com/BridgeAR)) -- Added `url` option to pass the connection url with the options object ([@BridgeAR](https://github.com/BridgeAR)) -- Added `client.duplicate([options])` to duplicate the current client and return a new one with the same options ([@BridgeAR](https://github.com/BridgeAR)) -- Improve performance by up to 20% on almost all use cases ([@BridgeAR](https://github.com/BridgeAR)) +- Added `tls` option to initiate a connection to a redis server behind a TLS proxy. Thanks ([@paddybyers](https://github.com/paddybyers)) +- Added `prefix` option to auto key prefix any command with the provided prefix ([@luin](https://github.com/luin) & [@BridgeAR](https://github.com/BridgeAR)) +- Added `url` option to pass the connection url with the options object ([@BridgeAR](https://github.com/BridgeAR)) +- Added `client.duplicate([options])` to duplicate the current client and return a new one with the same options ([@BridgeAR](https://github.com/BridgeAR)) +- Improve performance by up to 20% on almost all use cases ([@BridgeAR](https://github.com/BridgeAR)) Bugfixes -- Fixed js parser handling big values slow ([@BridgeAR](https://github.com/BridgeAR)) - - The speed is now on par with the hiredis parser. +- Fixed js parser handling big values slow ([@BridgeAR](https://github.com/BridgeAR)) +- The speed is now on par with the hiredis parser. -## v.2.3.1 - 18 Nov, 2015 +## v2.3.1 - 18 Nov, 2015 Bugfixes -- Fixed saving buffers with charsets other than utf-8 while using multi ([@BridgeAR](https://github.com/BridgeAR)) -- Fixed js parser handling big values very slow ([@BridgeAR](https://github.com/BridgeAR)) - - The speed is up to ~500% faster than before but still up to ~50% slower than the hiredis parser. +- Fixed saving buffers with charsets other than utf-8 while using multi ([@BridgeAR](https://github.com/BridgeAR)) +- Fixed js parser handling big values very slow ([@BridgeAR](https://github.com/BridgeAR)) +- The speed is up to ~500% faster than before but still up to ~50% slower than the hiredis parser. -## v.2.3.0 - 30 Oct, 2015 +## v2.3.0 - 30 Oct, 2015 Features -- Improve speed further for: ([@BridgeAR](https://github.com/BridgeAR)) - - saving big strings (up to +300%) - - using .multi / .batch (up to +50% / on Node.js 0.10.x +300%) - - saving small buffers -- Increased coverage to 99% ([@BridgeAR](https://github.com/BridgeAR)) -- Refactored manual backpressure control ([@BridgeAR](https://github.com/BridgeAR)) - - Removed the high water mark and low water mark. Such a mechanism should be implemented by a user instead - - The `drain` event is from now on only emitted if the stream really had to buffer -- Reduced the default connect_timeout to be one hour instead of 24h ([@BridgeAR](https://github.com/BridgeAR)) -- Added .path to redis.createClient(options); ([@BridgeAR](https://github.com/BridgeAR)) -- Ignore info command, if not available on server ([@ivanB1975](https://github.com/ivanB1975)) +- Improve speed further for: ([@BridgeAR](https://github.com/BridgeAR)) +- saving big strings (up to +300%) +- using .multi / .batch (up to +50% / on Node.js 0.10.x +300%) +- saving small buffers +- Increased coverage to 99% ([@BridgeAR](https://github.com/BridgeAR)) +- Refactored manual backpressure control ([@BridgeAR](https://github.com/BridgeAR)) +- Removed the high water mark and low water mark. Such a mechanism should be implemented by a user instead +- The `drain` event is from now on only emitted if the stream really had to buffer +- Reduced the default connect_timeout to be one hour instead of 24h ([@BridgeAR](https://github.com/BridgeAR)) +- Added .path to redis.createClient(options); ([@BridgeAR](https://github.com/BridgeAR)) +- Ignore info command, if not available on server ([@ivanB1975](https://github.com/ivanB1975)) Bugfixes -- Fixed a js parser error that could result in a timeout ([@BridgeAR](https://github.com/BridgeAR)) -- Fixed .multi / .batch used with Node.js 0.10.x not working properly after a reconnect ([@BridgeAR](https://github.com/BridgeAR)) -- Fixed fired but not yet returned commands not being rejected after a connection loss ([@BridgeAR](https://github.com/BridgeAR)) -- Fixed connect_timeout not respected if no connection has ever been established ([@gagle](https://github.com/gagle) & [@benjie](https://github.com/benjie)) -- Fixed return_buffers in pub sub mode ([@komachi](https://github.com/komachi)) +- Fixed a js parser error that could result in a timeout ([@BridgeAR](https://github.com/BridgeAR)) +- Fixed .multi / .batch used with Node.js 0.10.x not working properly after a reconnect ([@BridgeAR](https://github.com/BridgeAR)) +- Fixed fired but not yet returned commands not being rejected after a connection loss ([@BridgeAR](https://github.com/BridgeAR)) +- Fixed connect_timeout not respected if no connection has ever been established ([@gagle](https://github.com/gagle) & [@benjie](https://github.com/benjie)) +- Fixed return_buffers in pub sub mode ([@komachi](https://github.com/komachi)) -## v.2.2.5 - 18 Oct, 2015 +## v2.2.5 - 18 Oct, 2015 Bugfixes -- Fixed undefined options passed to a new instance not accepted (possible with individual .createClient functions) ([@BridgeAR](https://github.com/BridgeAR)) +- Fixed undefined options passed to a new instance not accepted (possible with individual .createClient functions) ([@BridgeAR](https://github.com/BridgeAR)) -## v.2.2.4 - 17 Oct, 2015 +## v2.2.4 - 17 Oct, 2015 Bugfixes -- Fixed unspecific error message for unresolvable commands ([@BridgeAR](https://github.com/BridgeAR)) -- Fixed not allowed command error in pubsub mode not being returned in a provided callback ([@BridgeAR](https://github.com/BridgeAR)) -- Fixed to many commands forbidden in pub sub mode ([@BridgeAR](https://github.com/BridgeAR)) -- Fixed mutation of the arguments array passed to .multi / .batch constructor ([@BridgeAR](https://github.com/BridgeAR)) -- Fixed mutation of the options object passed to createClient ([@BridgeAR](https://github.com/BridgeAR)) -- Fixed error callback in .multi not called if connection in broken mode ([@BridgeAR](https://github.com/BridgeAR)) +- Fixed unspecific error message for unresolvable commands ([@BridgeAR](https://github.com/BridgeAR)) +- Fixed not allowed command error in pubsub mode not being returned in a provided callback ([@BridgeAR](https://github.com/BridgeAR)) +- Fixed to many commands forbidden in pub sub mode ([@BridgeAR](https://github.com/BridgeAR)) +- Fixed mutation of the arguments array passed to .multi / .batch constructor ([@BridgeAR](https://github.com/BridgeAR)) +- Fixed mutation of the options object passed to createClient ([@BridgeAR](https://github.com/BridgeAR)) +- Fixed error callback in .multi not called if connection in broken mode ([@BridgeAR](https://github.com/BridgeAR)) -## v.2.2.3 - 14 Oct, 2015 +## v2.2.3 - 14 Oct, 2015 Bugfixes -- Fixed multi not being executed on Node 0.10.x if node_redis not yet ready ([@BridgeAR](https://github.com/BridgeAR)) +- Fixed multi not being executed on Node 0.10.x if node_redis not yet ready ([@BridgeAR](https://github.com/BridgeAR)) -## v.2.2.2 - 14 Oct, 2015 +## v2.2.2 - 14 Oct, 2015 Bugfixes -- Fixed regular commands not being executed after a .multi until .exec was called ([@BridgeAR](https://github.com/BridgeAR)) +- Fixed regular commands not being executed after a .multi until .exec was called ([@BridgeAR](https://github.com/BridgeAR)) -## v.2.2.1 - 12 Oct, 2015 +## v2.2.1 - 12 Oct, 2015 No code change -## v.2.2.0 - 12 Oct, 2015 - The peregrino falcon +## v2.2.0 - 12 Oct, 2015 - The peregrino falcon The peregrino falcon is the fasted bird on earth and this is what this release is all about: Increased performance for heavy usage by up to **400%** [sic!] and increased overall performance for any command as well. Please check the benchmarks in the [README.md](README.md) for further details. Features -- Added rename_commands options to handle renamed commands from the redis config ([@digmxl](https://github.com/digmxl) & [@BridgeAR](https://github.com/BridgeAR)) -- Added disable_resubscribing option to prevent a client from resubscribing after reconnecting ([@BridgeAR](https://github.com/BridgeAR)) -- Increased performance ([@BridgeAR](https://github.com/BridgeAR)) - - exchanging built in queue with [@petkaantonov](https://github.com/petkaantonov)'s [double-ended queue](https://github.com/petkaantonov/deque) - - prevent polymorphism - - optimize statements -- Added *.batch* command, similar to .multi but without transaction ([@BridgeAR](https://github.com/BridgeAR)) -- Improved pipelining to minimize the [RTT](http://redis.io/topics/pipelining) further ([@BridgeAR](https://github.com/BridgeAR)) +- Added rename_commands options to handle renamed commands from the redis config ([@digmxl](https://github.com/digmxl) & [@BridgeAR](https://github.com/BridgeAR)) +- Added disable_resubscribing option to prevent a client from resubscribing after reconnecting ([@BridgeAR](https://github.com/BridgeAR)) +- Increased performance ([@BridgeAR](https://github.com/BridgeAR)) +- exchanging built in queue with [@petkaantonov](https://github.com/petkaantonov)'s [double-ended queue](https://github.com/petkaantonov/deque) +- prevent polymorphism +- optimize statements +- Added _.batch_ command, similar to .multi but without transaction ([@BridgeAR](https://github.com/BridgeAR)) +- Improved pipelining to minimize the [RTT](http://redis.io/topics/pipelining) further ([@BridgeAR](https://github.com/BridgeAR)) Bugfixes -- Fixed a javascript parser regression introduced in 2.0 that could result in timeouts on high load. ([@BridgeAR](https://github.com/BridgeAR)) - - I was not able to write a regression test for this, since the error seems to only occur under heavy load with special conditions. So please have a look for timeouts with the js parser, if you use it and report all issues and switch to the hiredis parser in the meanwhile. If you're able to come up with a reproducable test case, this would be even better :) -- Fixed should_buffer boolean for .exec, .select and .auth commands not being returned and fix a couple special conditions ([@BridgeAR](https://github.com/BridgeAR)) +- Fixed a javascript parser regression introduced in 2.0 that could result in timeouts on high load. ([@BridgeAR](https://github.com/BridgeAR)) +- I was not able to write a regression test for this, since the error seems to only occur under heavy load with special conditions. So please have a look for timeouts with the js parser, if you use it and report all issues and switch to the hiredis parser in the meanwhile. If you're able to come up with a reproducable test case, this would be even better :) +- Fixed should_buffer boolean for .exec, .select and .auth commands not being returned and fix a couple special conditions ([@BridgeAR](https://github.com/BridgeAR)) If you do not rely on transactions but want to reduce the RTT you can use .batch from now on. It'll behave just the same as .multi but it does not have any transaction and therefor won't roll back any failed commands.
Both .multi and .batch are from now on going to cache the commands and release them while calling .exec. @@ -425,29 +425,29 @@ To conclude: we can proudly say that node_redis is very likely outperforming any Known issues -- The pub sub system has some flaws and those will be addressed in the next minor release +- The pub sub system has some flaws and those will be addressed in the next minor release ## v2.1.0 - Oct 02, 2015 Features: -- Addded optional flush parameter to `.end`. If set to true, commands fired after using .end are going to be rejected instead of being ignored. (@crispy1989) -- Addded: host and port can now be provided in a single options object. E.g. redis.createClient({ host: 'localhost', port: 1337, max_attempts: 5 }); (@BridgeAR) -- Speedup common cases (@BridgeAR) +- Addded optional flush parameter to `.end`. If set to true, commands fired after using .end are going to be rejected instead of being ignored. (@crispy1989) +- Addded: host and port can now be provided in a single options object. E.g. redis.createClient({ host: 'localhost', port: 1337, max_attempts: 5 }); (@BridgeAR) +- Speedup common cases (@BridgeAR) Bugfixes: -- Fix argument mutation while using the array notation with the multi constructor (@BridgeAR) -- Fix multi.hmset key not being type converted if used with an object and key not being a string (@BridgeAR) -- Fix parser errors not being catched properly (@BridgeAR) -- Fix a crash that could occur if a redis server does not return the info command as usual #541 (@BridgeAR) -- Explicitly passing undefined as a callback statement will work again. E.g. client.publish('channel', 'message', undefined); (@BridgeAR) +- Fix argument mutation while using the array notation with the multi constructor (@BridgeAR) +- Fix multi.hmset key not being type converted if used with an object and key not being a string (@BridgeAR) +- Fix parser errors not being catched properly (@BridgeAR) +- Fix a crash that could occur if a redis server does not return the info command as usual #541 (@BridgeAR) +- Explicitly passing undefined as a callback statement will work again. E.g. client.publish('channel', 'message', undefined); (@BridgeAR) ## v2.0.1 - Sep 24, 2015 Bugfixes: -- Fix argument mutation while using the array notation in combination with keys / callbacks ([#866](.)). (@BridgeAR) +- Fix argument mutation while using the array notation in combination with keys / callbacks ([#866](.)). (@BridgeAR) ## v2.0.0 - Sep 21, 2015 @@ -473,7 +473,7 @@ This is the biggest release that node_redis had since it was released in 2010. A - Added: Network errors and other stream errors will from now on include the error code as `err.code` property (@BridgeAR) - Added: Errors thrown by redis will now include the redis error code as `err.code` property. (@skeggse & @BridgeAR) - Added: Errors thrown by node_redis will now include a `err.command` property for the command used (@BridgeAR) -- Added new commands and drop support for deprecated *substr* (@BridgeAR) +- Added new commands and drop support for deprecated _substr_ (@BridgeAR) - Added new possibilities how to provide the command arguments (@BridgeAR) - The entries in the keyspace of the server_info is now an object instead of a string. (@SinisterLight & @BridgeAR) - Small speedup here and there (e.g. by not using .toLowerCase() anymore) (@BridgeAR) @@ -482,7 +482,7 @@ This is the biggest release that node_redis had since it was released in 2010. A - Remove dead code, clean up and refactor very old chunks (@BridgeAR) - Don't flush the offline queue if reconnecting (@BridgeAR) - Emit all errors insteaf of throwing sometimes and sometimes emitting them (@BridgeAR) -- *auth_pass* passwords are now checked to be a valid password (@jcppman & @BridgeAR) +- _auth_pass_ passwords are now checked to be a valid password (@jcppman & @BridgeAR) ## Bug fixes: @@ -512,11 +512,13 @@ This is the biggest release that node_redis had since it was released in 2010. A 2. Error messages have changed quite a bit. If you depend on a specific wording please check your application carfully. 3. Errors are from now on always either returned if a callback is present or emitted. They won't be thrown (neither sync, nor async). 4. The Multi error handling has changed a lot! - - All errors are from now on errors instead of strings (this only applied to the js parser). - - If an error occurs while queueing the commands an EXECABORT error will be returned including the failed commands as `.errors` property instead of an array with errors. - - If an error occurs while executing the commands and that command has a callback it'll return the error as first parameter (`err, undefined` instead of `null, undefined`). - - All the errors occuring while executing the commands will stay in the result value as error instance (if you used the js parser before they would have been strings). Be aware that the transaction won't be aborted if those error occurr! - - If `multi.exec` does not have a callback and an EXECABORT error occurrs, it'll emit that error instead. + +- All errors are from now on errors instead of strings (this only applied to the js parser). +- If an error occurs while queueing the commands an EXECABORT error will be returned including the failed commands as `.errors` property instead of an array with errors. +- If an error occurs while executing the commands and that command has a callback it'll return the error as first parameter (`err, undefined` instead of `null, undefined`). +- All the errors occuring while executing the commands will stay in the result value as error instance (if you used the js parser before they would have been strings). Be aware that the transaction won't be aborted if those error occurr! +- If `multi.exec` does not have a callback and an EXECABORT error occurrs, it'll emit that error instead. + 5. If redis can't connect to your redis server it'll give up after a certain point of failures (either max connection attempts or connection timeout exceeded). If that is the case it'll emit an CONNECTION_BROKEN error. You'll have to initiate a new client to try again afterwards. 6. The offline queue is not flushed anymore on a reconnect. It'll stay until node_redis gives up trying to reach the server or until you close the connection. 7. Before this release node_redis catched user errors and threw them async back. This is not the case anymore! No user behavior of what so ever will be tracked or catched. @@ -530,110 +532,113 @@ From now on we'll push new releases more frequently out and fix further long out ## v1.0.0 - Aug 30, 2015 -* Huge issue and pull-request cleanup. Thanks Blain! (@blainsmith) -* [#658](https://github.com/NodeRedis/node_redis/pull/658) Client now parses URL-format connection strings (e.g., redis://foo:pass@127.0.0.1:8080) (@kuwabarahiroshi) -* [#749](https://github.com/NodeRedis/node_redis/pull/749) Fix reconnection bug when client is in monitoring mode (@danielbprice) -* [#786](https://github.com/NodeRedis/node_redis/pull/786) Refactor createClient. Fixes #651 (@BridgeAR) -* [#793](https://github.com/NodeRedis/node_redis/pull/793) Refactor tests and improve test coverage (@erinspice, @bcoe) -* [#733](https://github.com/NodeRedis/node_redis/pull/733) Fixes detect_buffers functionality in the context of exec. Fixes #732, #263 (@raydog) -* [#785](https://github.com/NodeRedis/node_redis/pull/785) Tiny speedup by using 'use strict' (@BridgeAR) -* Fix extraneous error output due to pubsub tests (Mikael Kohlmyr) +- Huge issue and pull-request cleanup. Thanks Blain! (@blainsmith) +- [#658](https://github.com/NodeRedis/node_redis/pull/658) Client now parses URL-format connection strings (e.g., redis://foo:pass@127.0.0.1:8080) (@kuwabarahiroshi) +- [#749](https://github.com/NodeRedis/node_redis/pull/749) Fix reconnection bug when client is in monitoring mode (@danielbprice) +- [#786](https://github.com/NodeRedis/node_redis/pull/786) Refactor createClient. Fixes #651 (@BridgeAR) +- [#793](https://github.com/NodeRedis/node_redis/pull/793) Refactor tests and improve test coverage (@erinspice, @bcoe) +- [#733](https://github.com/NodeRedis/node_redis/pull/733) Fixes detect_buffers functionality in the context of exec. Fixes #732, #263 (@raydog) +- [#785](https://github.com/NodeRedis/node_redis/pull/785) Tiny speedup by using 'use strict' (@BridgeAR) +- Fix extraneous error output due to pubsub tests (Mikael Kohlmyr) ## v0.12.1 - Aug 10, 2014 -* Fix IPv6/IPv4 family selection in node 0.11+ (Various) + +- Fix IPv6/IPv4 family selection in node 0.11+ (Various) ## v0.12.0 - Aug 9, 2014 -* Fix unix socket support (Jack Tang) -* Improve createClient argument handling (Jack Tang) + +- Fix unix socket support (Jack Tang) +- Improve createClient argument handling (Jack Tang) ## v0.11.0 - Jul 10, 2014 -* IPv6 Support. (Yann Stephan) -* Revert error emitting and go back to throwing errors. (Bryce Baril) -* Set socket_keepalive to prevent long-lived client timeouts. (mohit) -* Correctly reset retry timer. (ouotuo) -* Domains protection from bad user exit. (Jake Verbaten) -* Fix reconnection socket logic to prevent misqueued entries. (Iain Proctor) +- IPv6 Support. (Yann Stephan) +- Revert error emitting and go back to throwing errors. (Bryce Baril) +- Set socket_keepalive to prevent long-lived client timeouts. (mohit) +- Correctly reset retry timer. (ouotuo) +- Domains protection from bad user exit. (Jake Verbaten) +- Fix reconnection socket logic to prevent misqueued entries. (Iain Proctor) ## v0.10.3 - May 22, 2014 -* Update command list to match Redis 2.8.9 (Charles Feng) +- Update command list to match Redis 2.8.9 (Charles Feng) ## v0.10.2 - May 18, 2014 -* Better binary key handling for HGETALL. (Nick Apperson) -* Fix test not resetting `error` handler. (CrypticSwarm) -* Fix SELECT error semantics. (Bryan English) +- Better binary key handling for HGETALL. (Nick Apperson) +- Fix test not resetting `error` handler. (CrypticSwarm) +- Fix SELECT error semantics. (Bryan English) ## v0.10.1 - February 17, 2014 -* Skip plucking redis version from the INFO stream if INFO results weren't provided. (Robert Sköld) +- Skip plucking redis version from the INFO stream if INFO results weren't provided. (Robert Sköld) ## v0.10.0 - December 21, 2013 -* Instead of throwing errors asynchronously, emit errors on client. (Bryce Baril) +- Instead of throwing errors asynchronously, emit errors on client. (Bryce Baril) ## v0.9.2 - December 15, 2013 -* Regenerate commands for new 2.8.x Redis commands. (Marek Ventur) -* Correctly time reconnect counts when using 'auth'. (William Hockey) +- Regenerate commands for new 2.8.x Redis commands. (Marek Ventur) +- Correctly time reconnect counts when using 'auth'. (William Hockey) ## v0.9.1 - November 23, 2013 -* Allow hmset to accept numeric keys. (Alex Stokes) -* Fix TypeError for multiple MULTI/EXEC errors. (Kwangsu Kim) +- Allow hmset to accept numeric keys. (Alex Stokes) +- Fix TypeError for multiple MULTI/EXEC errors. (Kwangsu Kim) ## v0.9.0 - October 17, 2013 -* Domains support. (Forrest L Norvell) +- Domains support. (Forrest L Norvell) ## v0.8.6 - October 2, 2013 -* If error is already an Error, don't wrap it in another Error. (Mathieu M-Gosselin) -* Fix retry delay logic (Ian Babrou) -* Return Errors instead of strings where Errors are expected (Ian Babrou) -* Add experimental `.unref()` method to RedisClient (Bryce Baril / Olivier Lalonde) -* Strengthen checking of reply to prevent conflating "message" or "pmessage" fields with pub_sub replies. (Bryce Baril) +- If error is already an Error, don't wrap it in another Error. (Mathieu M-Gosselin) +- Fix retry delay logic (Ian Babrou) +- Return Errors instead of strings where Errors are expected (Ian Babrou) +- Add experimental `.unref()` method to RedisClient (Bryce Baril / Olivier Lalonde) +- Strengthen checking of reply to prevent conflating "message" or "pmessage" fields with pub_sub replies. (Bryce Baril) ## v0.8.5 - September 26, 2013 -* Add `auth_pass` option to connect and immediately authenticate (Henrik Peinar) +- Add `auth_pass` option to connect and immediately authenticate (Henrik Peinar) ## v0.8.4 - June 24, 2013 Many contributed features and fixes, including: -* Ignore password set if not needed. (jbergknoff) -* Improved compatibility with 0.10.X for tests and client.end() (Bryce Baril) -* Protect connection retries from application exceptions. (Amos Barreto) -* Better exception handling for Multi/Exec (Thanasis Polychronakis) -* Renamed pubsub mode to subscriber mode (Luke Plaster) -* Treat SREM like SADD when passed an array (Martin Ciparelli) -* Fix empty unsub/punsub TypeError (Jeff Barczewski) -* Only attempt to run a callback if it one was provided (jifeng) + +- Ignore password set if not needed. (jbergknoff) +- Improved compatibility with 0.10.X for tests and client.end() (Bryce Baril) +- Protect connection retries from application exceptions. (Amos Barreto) +- Better exception handling for Multi/Exec (Thanasis Polychronakis) +- Renamed pubsub mode to subscriber mode (Luke Plaster) +- Treat SREM like SADD when passed an array (Martin Ciparelli) +- Fix empty unsub/punsub TypeError (Jeff Barczewski) +- Only attempt to run a callback if it one was provided (jifeng) ## v0.8.3 - April 09, 2013 Many contributed features and fixes, including: -* Fix some tests for Node.js version 0.9.x+ changes (Roman Ivanilov) -* Fix error when commands submitted after idle event handler (roamm) -* Bypass Redis for no-op SET/SETEX commands (jifeng) -* Fix HMGET + detect_buffers (Joffrey F) -* Fix CLIENT LOAD functionality (Jonas Dohse) -* Add percentage outputs to diff_multi_bench_output.js (Bryce Baril) -* Add retry_max_delay option (Tomasz Durka) -* Fix parser off-by-one errors with nested multi-bulk replies (Bryce Baril) -* Prevent parser from sinking application-side exceptions (Bryce Baril) -* Fix parser incorrect buffer skip when parsing multi-bulk errors (Bryce Baril) -* Reverted previous change with throwing on non-string values with HMSET (David Trejo) -* Fix command queue sync issue when using pubsub (Tom Leach) -* Fix compatibility with two-word Redis commands (Jonas Dohse) -* Add EVAL with array syntax (dmoena) -* Fix tests due to Redis reply order changes in 2.6.5+ (Bryce Baril) -* Added a test for the SLOWLOG command (Nitesh Sinha) -* Fix SMEMBERS order dependency in test broken by Redis changes (Garrett Johnson) -* Update commands for new Redis commands (David Trejo) -* Prevent exception from SELECT on subscriber reconnection (roamm) +- Fix some tests for Node.js version 0.9.x+ changes (Roman Ivanilov) +- Fix error when commands submitted after idle event handler (roamm) +- Bypass Redis for no-op SET/SETEX commands (jifeng) +- Fix HMGET + detect_buffers (Joffrey F) +- Fix CLIENT LOAD functionality (Jonas Dohse) +- Add percentage outputs to diff_multi_bench_output.js (Bryce Baril) +- Add retry_max_delay option (Tomasz Durka) +- Fix parser off-by-one errors with nested multi-bulk replies (Bryce Baril) +- Prevent parser from sinking application-side exceptions (Bryce Baril) +- Fix parser incorrect buffer skip when parsing multi-bulk errors (Bryce Baril) +- Reverted previous change with throwing on non-string values with HMSET (David Trejo) +- Fix command queue sync issue when using pubsub (Tom Leach) +- Fix compatibility with two-word Redis commands (Jonas Dohse) +- Add EVAL with array syntax (dmoena) +- Fix tests due to Redis reply order changes in 2.6.5+ (Bryce Baril) +- Added a test for the SLOWLOG command (Nitesh Sinha) +- Fix SMEMBERS order dependency in test broken by Redis changes (Garrett Johnson) +- Update commands for new Redis commands (David Trejo) +- Prevent exception from SELECT on subscriber reconnection (roamm) ## v0.8.2 - November 11, 2012 @@ -650,20 +655,20 @@ Important bug fix for null responses (Jerry Sievert) Many contributed features and fixes, including: -* Pure JavaScript reply parser that is usually faster than hiredis (Jerry Sievert) -* Remove hiredis as optionalDependency from package.json. It still works if you want it. -* Restore client state on reconnect, including select, subscribe, and monitor. (Ignacio Burgueño) -* Fix idle event (Trae Robrock) -* Many documentation improvements and bug fixes (David Trejo) +- Pure JavaScript reply parser that is usually faster than hiredis (Jerry Sievert) +- Remove hiredis as optionalDependency from package.json. It still works if you want it. +- Restore client state on reconnect, including select, subscribe, and monitor. (Ignacio Burgueño) +- Fix idle event (Trae Robrock) +- Many documentation improvements and bug fixes (David Trejo) ## v0.7.2 - April 29, 2012 Many contributed fixes. Thank you, contributors. -* [GH-190] - pub/sub mode fix (Brian Noguchi) -* [GH-165] - parser selection fix (TEHEK) -* numerous documentation and examples updates -* auth errors emit Errors instead of Strings (David Trejo) +- [GH-190] - pub/sub mode fix (Brian Noguchi) +- [GH-165] - parser selection fix (TEHEK) +- numerous documentation and examples updates +- auth errors emit Errors instead of Strings (David Trejo) ## v0.7.1 - November 15, 2011 @@ -675,21 +680,21 @@ Very much need automated tests for reconnection and queue logic. Many contributed fixes. Thanks everybody. -* [GH-127] - properly re-initialize parser on reconnect -* [GH-136] - handle passing undefined as callback (Ian Babrou) -* [GH-139] - properly handle exceptions thrown in pub/sub event handlers (Felix Geisendörfer) -* [GH-141] - detect closing state on stream error (Felix Geisendörfer) -* [GH-142] - re-select database on reconnection (Jean-Hugues Pinson) -* [GH-146] - add sort example (Maksim Lin) +- [GH-127] - properly re-initialize parser on reconnect +- [GH-136] - handle passing undefined as callback (Ian Babrou) +- [GH-139] - properly handle exceptions thrown in pub/sub event handlers (Felix Geisendörfer) +- [GH-141] - detect closing state on stream error (Felix Geisendörfer) +- [GH-142] - re-select database on reconnection (Jean-Hugues Pinson) +- [GH-146] - add sort example (Maksim Lin) Some more goodies: -* Fix bugs with node 0.6 -* Performance improvements -* New version of `multi_bench.js` that tests more realistic scenarios -* [GH-140] - support optional callback for subscribe commands -* Properly flush and error out command queue when connection fails -* Initial work on reconnection thresholds +- Fix bugs with node 0.6 +- Performance improvements +- New version of `multi_bench.js` that tests more realistic scenarios +- [GH-140] - support optional callback for subscribe commands +- Properly flush and error out command queue when connection fails +- Initial work on reconnection thresholds ## v0.6.7 - July 30, 2011 @@ -698,16 +703,16 @@ Some more goodies: Fix and test for [GH-123] Passing an Array as as the last argument should expand as users -expect. The old behavior was to coerce the arguments into Strings, +expect. The old behavior was to coerce the arguments into Strings, which did surprising things with Arrays. ## v0.6.5 - July 6, 2011 Contributed changes: -* Support SlowBuffers (Umair Siddique) -* Add Multi to exports (Louis-Philippe Perron) -* Fix for drain event calculation (Vladimir Dronnikov) +- Support SlowBuffers (Umair Siddique) +- Add Multi to exports (Louis-Philippe Perron) +- Fix for drain event calculation (Vladimir Dronnikov) Thanks! @@ -719,15 +724,15 @@ Fix bug with optional callbacks for hmset. Bugs fixed: -* authentication retry while server is loading db (danmaz74) [GH-101] -* command arguments processing issue with arrays +- authentication retry while server is loading db (danmaz74) [GH-101] +- command arguments processing issue with arrays New features: -* Auto update of new commands from redis.io (Dave Hoover) -* Performance improvements and backpressure controls. -* Commands now return the true/false value from the underlying socket write(s). -* Implement command_queue high water and low water for more better control of queueing. +- Auto update of new commands from redis.io (Dave Hoover) +- Performance improvements and backpressure controls. +- Commands now return the true/false value from the underlying socket write(s). +- Implement command_queue high water and low water for more better control of queueing. See `examples/backpressure_drain.js` for more information. @@ -735,7 +740,7 @@ See `examples/backpressure_drain.js` for more information. Add support and tests for Redis scripting through EXEC command. -Bug fix for monitor mode. (forddg) +Bug fix for monitor mode. (forddg) Auto update of new commands from redis.io (Dave Hoover) @@ -743,12 +748,12 @@ Auto update of new commands from redis.io (Dave Hoover) Lots of bugs fixed. -* connection error did not properly trigger reconnection logic [GH-85] -* client.hmget(key, [val1, val2]) was not expanding properly [GH-66] -* client.quit() while in pub/sub mode would throw an error [GH-87] -* client.multi(['hmset', 'key', {foo: 'bar'}]) fails [GH-92] -* unsubscribe before subscribe would make things very confused [GH-88] -* Add BRPOPLPUSH [GH-79] +- connection error did not properly trigger reconnection logic [GH-85] +- client.hmget(key, [val1, val2]) was not expanding properly [GH-66] +- client.quit() while in pub/sub mode would throw an error [GH-87] +- client.multi(['hmset', 'key', {foo: 'bar'}]) fails [GH-92] +- unsubscribe before subscribe would make things very confused [GH-88] +- Add BRPOPLPUSH [GH-79] ## v0.5.11 - April 7, 2011 @@ -788,17 +793,17 @@ Add probe for server readiness. When a Redis server starts up, it might take a while to load the dataset into memory. During this time, the server will accept connections, but will return errors for all non-INFO -commands. Now node_redis will send an INFO command whenever it connects to a server. +commands. Now node_redis will send an INFO command whenever it connects to a server. If the info command indicates that the server is not ready, the client will keep trying until -the server is ready. Once it is ready, the client will emit a "ready" event as well as the -"connect" event. The client will queue up all commands sent before the server is ready, just -like it did before. When the server is ready, all offline/non-ready commands will be replayed. +the server is ready. Once it is ready, the client will emit a "ready" event as well as the +"connect" event. The client will queue up all commands sent before the server is ready, just +like it did before. When the server is ready, all offline/non-ready commands will be replayed. This should be backward compatible with previous versions. To disable this ready check behavior, set `options.no_ready_check` when creating the client. As a side effect of this change, the key/val params from the info command are available as -`client.server_options`. Further, the version string is decomposed into individual elements +`client.server_options`. Further, the version string is decomposed into individual elements in `client.server_options.versions`. ## v0.5.4 - February 11, 2011 @@ -825,17 +830,17 @@ Fix bug where subscribe commands would not handle redis-server startup error pro Some bug fixes: -* An important bug fix in reconnection logic. Previously, reply callbacks would be invoked twice after +- An important bug fix in reconnection logic. Previously, reply callbacks would be invoked twice after a reconnect. -* Changed error callback argument to be an actual Error object. +- Changed error callback argument to be an actual Error object. New feature: -* Add friendly syntax for HMSET using an object. +- Add friendly syntax for HMSET using an object. ## v0.4.1 - December 8, 2010 -Remove warning about missing hiredis. You probably do want it though. +Remove warning about missing hiredis. You probably do want it though. ## v0.4.0 - December 5, 2010 @@ -865,9 +870,9 @@ Send a friendlier "error" event message on stream errors like connection refused A few bug fixes. -* Fixed bug with `nil` multi-bulk reply lengths that showed up with `BLPOP` timeouts. -* Only emit `end` once when connection goes away. -* Fixed bug in `test.js` where driver finished before all tests completed. +- Fixed bug with `nil` multi-bulk reply lengths that showed up with `BLPOP` timeouts. +- Only emit `end` once when connection goes away. +- Fixed bug in `test.js` where driver finished before all tests completed. ## unversioned wasteland diff --git a/README.md b/README.md index bd124739b24..49a21019edb 100644 --- a/README.md +++ b/README.md @@ -19,115 +19,43 @@ --- -This is a complete and feature rich Redis client for Node.js. **It supports all -Redis commands** and focuses on high performance. - -Install with: +## Installation ```bash npm install redis ``` -## Usage Example +## Usage + +#### Example ```js -const redis = require('redis'); +const redis = require("redis"); const client = redis.createClient(); -// if you'd like to select database 3, instead of 0 (default), call -// client.select(3, function() { /* ... */ }); - -client.on('error', function(err) { - console.log('Error ' + err); +client.on("error", function(err) { + console.log("Error " + err); }); -client.set('string key', 'string val', redis.print); -client.hset('hash key', 'hashtest 1', 'some value', redis.print); -client.hset(['hash key', 'hashtest 2', 'some other value'], redis.print); -client.hkeys('hash key', function(err, replies) { - console.log(replies.length + ' replies:'); - replies.forEach(function(reply, i) { - console.log(' ' + i + ': ' + reply); - }); - client.quit(); -}); +client.set("string key", "string val", redis.print); +client.get("string key", redis.print); ``` -This will display: - - mjr:~/work/node_redis (master)$ node example.js - Reply: OK - Reply: 0 - Reply: 0 - 2 replies: - 0: hashtest 1 - 1: hashtest 2 - mjr:~/work/node_redis (master)$ - Note that the API is entirely asynchronous. To get data back from the server, -you'll need to use a callback. From v.2.6 on the API supports camelCase and -snake_case and all options / variables / events etc. can be used either way. It -is recommended to use camelCase as this is the default for the Node.js -landscape. +you'll need to use a callback. ### Promises -#### Native Promises - -If you are using node v8 or higher, you can promisify node_redis with [util.promisify](https://nodejs.org/api/util.html#util_util_promisify_original) as in: +Node Redis currently doesn't natively support promises (this is coming in v4), however you can wrap the methods you +want to use with promises using the built-in Node.js `util.promisify` method on Node.js >= v8; ```js -const { promisify } = require('util'); +const { promisify } = require("util"); const getAsync = promisify(client.get).bind(client); -``` - -now _getAsync_ is a promisified version of _client.get_: - -```js -// We expect a value 'foo': 'bar' to be present -// So instead of writing client.get('foo', cb); you have to write: -return getAsync('foo').then(function(res) { - console.log(res); // => 'bar' -}); -``` - -or using [async await](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Statements/async_function): - -```js -async function myFunc() { - const res = await getAsync('foo'); - console.log(res); -} -``` - -#### Bluebird Promises - -You can also use node_redis with promises by promisifying node_redis with -[bluebird](https://github.com/petkaantonov/bluebird) as in: -```js -const redis = require('redis'); -bluebird.promisifyAll(redis); -``` - -It'll add a _Async_ to all node_redis functions (e.g. return client.getAsync().then()) - -```js -// We expect a value 'foo': 'bar' to be present -// So instead of writing client.get('foo', cb); you have to write: -return client.getAsync('foo').then(function(res) { - console.log(res); // => 'bar' -}); - -// Using multi with promises looks like: - -return client - .multi() - .get('foo') - .execAsync() - .then(function(res) { - console.log(res); // => 'bar' - }); +getAsync + .then(console.log) + .catch(console.error); ``` ### Sending Commands @@ -138,16 +66,15 @@ a variable number of individual arguments followed by an optional callback. Examples: ```js -client.hmset(['key', 'test keys 1', 'test val 1', 'test keys 2', 'test val 2'], function( - err, - res, -) {}); +client.hmset(["key", "test keys 1", "test val 1", "test keys 2", "test val 2"], function(err, res) { + // ... +}); // Works the same as -client.hmset('key', ['test keys 1', 'test val 1', 'test keys 2', 'test val 2'], function(err, res) { +client.hmset("key", ["test keys 1", "test val 1", "test keys 2", "test val 2"], function(err, res) { // ... }); // Or -client.hmset('key', 'test keys 1', 'test val 1', 'test keys 2', 'test val 2', function(err, res) { +client.hmset("key", "test keys 1", "test val 1", "test keys 2", "test val 2", function(err, res) { // ... }); ``` @@ -157,15 +84,15 @@ Care should be taken with user input if arrays are possible (via body-parser, qu Note that in either form the `callback` is optional: ```js -client.set('some key', 'some val'); -client.set(['some other key', 'some val']); +client.set("some key", "some val"); +client.set(["some other key", "some val"]); ``` If the key is missing, reply will be null. Only if the [Redis Command Reference](http://redis.io/commands) states something else it will not be null. ```js -client.get('missingkey', function(err, reply) { +client.get("missingkey", function(err, reply) { // reply is null when the key is missing console.log(reply); }); @@ -189,7 +116,7 @@ Example setting key to auto expire using [SET command](https://redis.io/commands ```js // this key will expire after 10 seconds -client.set('key', 'value!', 'EX', 10); +client.set("key", "value!", "EX", 10); ``` # API @@ -275,18 +202,18 @@ using unix sockets if possible to increase throughput. | retry_strategy | function | A function that receives an options object as parameter including the retry `attempt`, the `total_retry_time` indicating how much time passed since the last time connected, the `error` why the connection was lost and the number of `times_connected` in total. If you return a number from this function, the retry will happen exactly after that time in milliseconds. If you return a non-number, no further retry will happen and all offline commands are flushed with errors. Return an error to return that specific error to all offline commands. Example below. | ```js -var redis = require('redis'); +var redis = require("redis"); var client = redis.createClient({ detect_buffers: true }); -client.set('foo_rand000000000000', 'OK'); +client.set("foo_rand000000000000", "OK"); // This will return a JavaScript String -client.get('foo_rand000000000000', function(err, reply) { +client.get("foo_rand000000000000", function(err, reply) { console.log(reply.toString()); // Will print `OK` }); // This will return a Buffer since original key is specified as a Buffer -client.get(new Buffer('foo_rand000000000000'), function(err, reply) { +client.get(new Buffer("foo_rand000000000000"), function(err, reply) { console.log(reply.toString()); // Will print `` }); client.quit(); @@ -297,15 +224,15 @@ client.quit(); ```js var client = redis.createClient({ retry_strategy: function(options) { - if (options.error && options.error.code === 'ECONNREFUSED') { + if (options.error && options.error.code === "ECONNREFUSED") { // End reconnecting on a specific error and flush all commands with // a individual error - return new Error('The server refused the connection'); + return new Error("The server refused the connection"); } if (options.total_retry_time > 1000 * 60 * 60) { // End reconnecting after a specific timeout and flush all commands // with a individual error - return new Error('Retry time exhausted'); + return new Error("Retry time exhausted"); } if (options.attempt > 10) { // End reconnecting with built in error @@ -363,23 +290,23 @@ This example closes the connection to the Redis server before the replies have been read. You probably don't want to do this: ```js -var redis = require('redis'), +var redis = require("redis"), client = redis.createClient(); -client.set('foo_rand000000000000', 'some fantastic value', function(err, reply) { +client.set("foo_rand000000000000", "some fantastic value", function(err, reply) { // This will either result in an error (flush parameter is set to true) // or will silently fail and this callback will not be called at all (flush set to false) console.log(err); }); client.end(true); // No further commands will be processed -client.get('foo_rand000000000000', function(err, reply) { +client.get("foo_rand000000000000", function(err, reply) { console.log(err); // => 'The connection has already been closed.' }); ``` `client.end()` without the flush parameter set to true should NOT be used in production! -## Error handling (>= v.2.6) +## Error handling (>= v2.6) Currently the following error subclasses exist: @@ -398,27 +325,27 @@ All error classes are exported by the module. Example: ```js -var redis = require('./'); -var assert = require('assert'); +var redis = require("./"); +var assert = require("assert"); var client = redis.createClient(); -client.on('error', function(err) { +client.on("error", function(err) { assert(err instanceof Error); assert(err instanceof redis.AbortError); assert(err instanceof redis.AggregateError); // The set and get get aggregated in here assert.strictEqual(err.errors.length, 2); - assert.strictEqual(err.code, 'NR_CLOSED'); + assert.strictEqual(err.code, "NR_CLOSED"); }); -client.set('foo', 123, 'bar', function(err, res) { +client.set("foo", 123, "bar", function(err, res) { // Too many arguments assert(err instanceof redis.ReplyError); // => true - assert.strictEqual(err.command, 'SET'); - assert.deepStrictEqual(err.args, ['foo', 123, 'bar']); + assert.strictEqual(err.command, "SET"); + assert.deepStrictEqual(err.args, ["foo", 123, "bar"]); redis.debug_mode = true; - client.set('foo', 'bar'); - client.get('foo'); + client.set("foo", "bar"); + client.get("foo"); process.nextTick(function() { // Force closing the connection while the command did not yet return client.end(true); @@ -449,7 +376,7 @@ protocol. Any commands where client state is saved on the Redis server, e.g. `*SUBSCRIBE` or the blocking `BL*` commands will _NOT_ work with `.unref()`. ```js -var redis = require('redis'); +var redis = require("redis"); var client = redis.createClient(); /* @@ -458,7 +385,7 @@ var client = redis.createClient(); client-server connection is alive. */ client.unref(); -client.get('foo', function(err, value) { +client.get("foo", function(err, value) { if (err) throw err; console.log(value); }); @@ -479,8 +406,8 @@ syntax. Example: ```js -client.hmset('hosts', 'mjr', '1', 'another', '23', 'home', '1234'); -client.hgetall('hosts', function(err, obj) { +client.hmset("hosts", "mjr", "1", "another", "23", "home", "1234"); +client.hgetall("hosts", function(err, obj) { console.dir(obj); }); ``` @@ -491,8 +418,8 @@ Multiple values in a hash can be set by supplying an object: ```js client.HMSET(key2, { - '0123456789': 'abcdefghij', // NOTE: key and value will be coerced to strings - 'some manner of key': 'a type of value', + "0123456789": "abcdefghij", // NOTE: key and value will be coerced to strings + "some manner of key": "a type of value", }); ``` @@ -504,7 +431,7 @@ Redis hash. Multiple values may also be set by supplying a list: ```js -client.HMSET(key1, '0123456789', 'abcdefghij', 'some manner of key', 'a type of value'); +client.HMSET(key1, "0123456789", "abcdefghij", "some manner of key", "a type of value"); ``` ## Publish / Subscribe @@ -514,19 +441,19 @@ client connections, subscribes to a channel on one of them, and publishes to tha channel on the other: ```js -var redis = require('redis'); +var redis = require("redis"); var sub = redis.createClient(), pub = redis.createClient(); var msg_count = 0; -sub.on('subscribe', function(channel, count) { - pub.publish('a nice channel', 'I am sending a message.'); - pub.publish('a nice channel', 'I am sending a second message.'); - pub.publish('a nice channel', 'I am sending my last message.'); +sub.on("subscribe", function(channel, count) { + pub.publish("a nice channel", "I am sending a message."); + pub.publish("a nice channel", "I am sending a second message."); + pub.publish("a nice channel", "I am sending my last message."); }); -sub.on('message', function(channel, message) { - console.log('sub channel ' + channel + ': ' + message); +sub.on("message", function(channel, message) { + console.log("sub channel " + channel + ": " + message); msg_count += 1; if (msg_count === 3) { sub.unsubscribe(); @@ -535,7 +462,7 @@ sub.on('message', function(channel, message) { } }); -sub.subscribe('a nice channel'); +sub.subscribe("a nice channel"); ``` When a client issues a `SUBSCRIBE` or `PSUBSCRIBE`, that connection is put into @@ -610,33 +537,33 @@ further information look at [transactions](http://redis.io/topics/transactions)). ```js -var redis = require('./index'), +var redis = require("./index"), client = redis.createClient(), set_size = 20; -client.sadd('bigset', 'a member'); -client.sadd('bigset', 'another member'); +client.sadd("bigset", "a member"); +client.sadd("bigset", "another member"); while (set_size > 0) { - client.sadd('bigset', 'member ' + set_size); + client.sadd("bigset", "member " + set_size); set_size -= 1; } // multi chain with an individual callback client .multi() - .scard('bigset') - .smembers('bigset') - .keys('*', function(err, replies) { + .scard("bigset") + .smembers("bigset") + .keys("*", function(err, replies) { // NOTE: code in this callback is NOT atomic // this only happens after the the .exec call finishes. client.mget(replies, redis.print); }) .dbsize() .exec(function(err, replies) { - console.log('MULTI got ' + replies.length + ' replies'); + console.log("MULTI got " + replies.length + " replies"); replies.forEach(function(reply, index) { - console.log('Reply ' + index + ': ' + reply.toString()); + console.log("Reply " + index + ": " + reply.toString()); }); }); ``` @@ -659,17 +586,17 @@ can queue individual commands while still sending regular client command as in this example: ```js -var redis = require('redis'), +var redis = require("redis"), client = redis.createClient(), multi; // start a separate multi command queue multi = client.multi(); -multi.incr('incr thing', redis.print); -multi.incr('incr other thing', redis.print); +multi.incr("incr thing", redis.print); +multi.incr("incr other thing", redis.print); // runs immediately -client.mset('incr thing', 100, 'incr other thing', 1, redis.print); +client.mset("incr thing", 100, "incr other thing", 1, redis.print); // drains multi queue and runs atomically multi.exec(function(err, replies) { @@ -681,14 +608,14 @@ In addition to adding commands to the `MULTI` queue individually, you can also pass an array of commands and arguments to the constructor: ```js -var redis = require('redis'), +var redis = require("redis"), client = redis.createClient(); client .multi([ - ['mget', 'multifoo', 'multibar', redis.print], - ['incr', 'multifoo'], - ['incr', 'multibar'], + ["mget", "multifoo", "multibar", redis.print], + ["incr", "multifoo"], + ["incr", "multibar"], ]) .exec(function(err, replies) { console.log(replies); @@ -853,14 +780,14 @@ arguments and the raw monitoring string. Example: ```js -var client = require('redis').createClient(); +var client = require("redis").createClient(); client.monitor(function(err, res) { - console.log('Entering monitoring mode.'); + console.log("Entering monitoring mode."); }); -client.set('foo', 'bar'); +client.set("foo", "bar"); -client.on('monitor', function(time, args, raw_reply) { - console.log(time + ': ' + args); // 1458910076.446514:['set', 'foo', 'bar'] +client.on("monitor", function(time, args, raw_reply) { + console.log(time + ": " + args); // 1458910076.446514:['set', 'foo', 'bar'] }); ``` @@ -886,12 +813,12 @@ easy comparison. A handy callback function for displaying return values when testing. Example: ```js -var redis = require('redis'), +var redis = require("redis"), client = redis.createClient(); -client.on('connect', function() { - client.set('foo_rand000000000000', 'some fantastic value', redis.print); - client.get('foo_rand000000000000', redis.print); +client.on("connect", function() { + client.set("foo_rand000000000000", "some fantastic value", redis.print); + client.get("foo_rand000000000000", redis.print); }); ``` @@ -908,12 +835,12 @@ To execute redis multi-word commands like `SCRIPT LOAD` or `CLIENT LIST` pass the second word as first parameter: ```js -client.script('load', 'return 1'); +client.script("load", "return 1"); client .multi() - .script('load', 'return 1') + .script("load", "return 1") .exec(); -client.multi([['script', 'load', 'return 1']]).exec(); +client.multi([["script", "load", "return 1"]]).exec(); ``` ## client.duplicate([options][, callback]) @@ -930,21 +857,21 @@ are used on the same redisClient instance as non-blocking commands, the non-blocking ones may be queued up until after the blocking ones finish. ```js -var Redis = require('redis'); +var Redis = require("redis"); var client = Redis.createClient(); var clientBlocking = client.duplicate(); var get = function() { - console.log('get called'); - client.get('any_key', function() { - console.log('get returned'); + console.log("get called"); + client.get("any_key", function() { + console.log("get returned"); }); setTimeout(get, 1000); }; var brpop = function() { - console.log('brpop called'); - clientBlocking.brpop('nonexistent', 5, function() { - console.log('brpop return'); + console.log("brpop called"); + clientBlocking.brpop("nonexistent", 5, function() { + console.log("brpop return"); setTimeout(brpop, 1000); }); }; @@ -994,16 +921,16 @@ This applies to anything that uses an optional `[WITHSCORES]` or `[LIMIT offset Example: ```js -var args = ['myzset', 1, 'one', 2, 'two', 3, 'three', 99, 'ninety-nine']; +var args = ["myzset", 1, "one", 2, "two", 3, "three", 99, "ninety-nine"]; client.zadd(args, function(err, response) { if (err) throw err; - console.log('added ' + response + ' items.'); + console.log("added " + response + " items."); // -Infinity and +Infinity also work - var args1 = ['myzset', '+inf', '-inf']; + var args1 = ["myzset", "+inf", "-inf"]; client.zrevrangebyscore(args1, function(err, response) { if (err) throw err; - console.log('example1', response); + console.log("example1", response); // write your code here }); @@ -1011,10 +938,10 @@ client.zadd(args, function(err, response) { min = 1, offset = 1, count = 2; - var args2 = ['myzset', max, min, 'WITHSCORES', 'LIMIT', offset, count]; + var args2 = ["myzset", max, min, "WITHSCORES", "LIMIT", offset, count]; client.zrevrangebyscore(args2, function(err, response) { if (err) throw err; - console.log('example2', response); + console.log("example2", response); // write your code here }); }); From 41c8177e8def055042cedc13dca90bb0b5887523 Mon Sep 17 00:00:00 2001 From: Salakar Date: Sun, 9 Feb 2020 02:43:10 +0000 Subject: [PATCH 0799/1748] docs --- README.md | 152 +++++++++++++++++++++++++----------------------------- 1 file changed, 70 insertions(+), 82 deletions(-) diff --git a/README.md b/README.md index 49a21019edb..935338cdbd2 100644 --- a/README.md +++ b/README.md @@ -47,18 +47,18 @@ you'll need to use a callback. ### Promises Node Redis currently doesn't natively support promises (this is coming in v4), however you can wrap the methods you -want to use with promises using the built-in Node.js `util.promisify` method on Node.js >= v8; +want to use with promises using the built-in Node.js `util.promisify` method on Node.js >= v8; ```js const { promisify } = require("util"); const getAsync = promisify(client.get).bind(client); -getAsync - .then(console.log) - .catch(console.error); +getAsync.then(console.log).catch(console.error); ``` -### Sending Commands +### Commands + +This library is a 1 to 1 mapping of the [Redis commands](https://redis.io/commands). Each Redis command is exposed as a function on the `client` object. All functions take either an `args` Array plus optional `callback` Function or @@ -98,50 +98,35 @@ client.get("missingkey", function(err, reply) { }); ``` -For a list of Redis commands, see [Redis Command Reference](http://redis.io/commands) - Minimal parsing is done on the replies. Commands that return a integer return JavaScript Numbers, arrays return JavaScript Array. `HGETALL` returns an Object keyed by the hash keys. All strings will either be returned as string or as buffer depending on your setting. Please be aware that sending null, undefined and Boolean values will result in the value coerced to a string! -# Redis Commands - -This library is a 1 to 1 mapping to [Redis commands](https://redis.io/commands). -It is not a cache library so please refer to Redis commands page for full usage -details. - -Example setting key to auto expire using [SET command](https://redis.io/commands/set) - -```js -// this key will expire after 10 seconds -client.set("key", "value!", "EX", 10); -``` - -# API +## API -## Connection and other Events +### Connection and other Events `client` will emit some events about the state of the connection to the Redis server. -### "ready" +#### "ready" `client` will emit `ready` once a connection is established. Commands issued before the `ready` event are queued, then replayed just before this event is emitted. -### "connect" +#### "connect" `client` will emit `connect` as soon as the stream is connected to the server. -### "reconnecting" +#### "reconnecting" `client` will emit `reconnecting` when trying to reconnect to the Redis server after losing the connection. Listeners are passed an object containing `delay` (in ms from the previous try) and `attempt` (the attempt #) attributes. -### "error" +#### "error" `client` will emit `error` when encountering an error connecting to the Redis server or when any other in node_redis occurs. If you use a command without @@ -150,16 +135,16 @@ listener. So please attach the error listener to node_redis. -### "end" +#### "end" `client` will emit `end` when an established Redis server connection has closed. -### "warning" +#### "warning" `client` will emit `warning` when password was set but none is needed and if a deprecated option / function / similar is used. -## redis.createClient() +### redis.createClient() If you have `redis-server` running on the same machine as node, then the defaults for port and host are probably fine and you don't need to supply any @@ -201,9 +186,11 @@ using unix sockets if possible to increase throughput. | prefix | null | A string used to prefix all used keys (e.g. `namespace:test`). Please be aware that the `keys` command will not be prefixed. The `keys` command has a "pattern" as argument and no key and it would be impossible to determine the existing keys in Redis if this would be prefixed. | | retry_strategy | function | A function that receives an options object as parameter including the retry `attempt`, the `total_retry_time` indicating how much time passed since the last time connected, the `error` why the connection was lost and the number of `times_connected` in total. If you return a number from this function, the retry will happen exactly after that time in milliseconds. If you return a non-number, no further retry will happen and all offline commands are flushed with errors. Return an error to return that specific error to all offline commands. Example below. | +**`detect_buffers` example:** + ```js -var redis = require("redis"); -var client = redis.createClient({ detect_buffers: true }); +const redis = require("redis"); +const client = redis.createClient({ detect_buffers: true }); client.set("foo_rand000000000000", "OK"); @@ -216,10 +203,9 @@ client.get("foo_rand000000000000", function(err, reply) { client.get(new Buffer("foo_rand000000000000"), function(err, reply) { console.log(reply.toString()); // Will print `` }); -client.quit(); ``` -#### `retry_strategy` example +**`retry_strategy` example:** ```js var client = redis.createClient({ @@ -244,7 +230,7 @@ var client = redis.createClient({ }); ``` -## client.auth(password[, callback]) +### client.auth(password[, callback]) When connecting to a Redis server that requires authentication, the `AUTH` command must be sent as the first command after connecting. This can be tricky @@ -256,19 +242,7 @@ NOTE: Your call to `client.auth()` should not be inside the ready handler. If you are doing this wrong, `client` will emit an error that looks something like this `Error: Ready check failed: ERR operation not permitted`. -## backpressure - -### stream - -The client exposed the used [stream](https://nodejs.org/api/stream.html) in -`client.stream` and if the stream or client had to -[buffer](https://nodejs.org/api/stream.html#stream_writable_write_chunk_encoding_callback) -the command in `client.should_buffer`. In combination this can be used to -implement backpressure by checking the buffer state before sending a command and -listening to the stream -[drain](https://nodejs.org/api/stream.html#stream_event_drain) event. - -## client.quit(callback) +### client.quit(callback) This sends the quit command to the redis server and ends cleanly right after all running commands were properly handled. If this is called while reconnecting @@ -276,7 +250,7 @@ running commands were properly handled. If this is called while reconnecting connection right away instead of resulting in further reconnections! All offline commands are going to be flushed with an error in that case. -## client.end(flush) +### client.end(flush) Forcibly close the connection to the Redis server. Note that this does not wait until all replies have been parsed. If you want to exit cleanly, call @@ -290,25 +264,28 @@ This example closes the connection to the Redis server before the replies have been read. You probably don't want to do this: ```js -var redis = require("redis"), - client = redis.createClient(); +const redis = require("redis"); +const client = redis.createClient(); -client.set("foo_rand000000000000", "some fantastic value", function(err, reply) { +client.set("hello", "world", function(err) { // This will either result in an error (flush parameter is set to true) // or will silently fail and this callback will not be called at all (flush set to false) - console.log(err); + console.error(err); }); -client.end(true); // No further commands will be processed -client.get("foo_rand000000000000", function(err, reply) { - console.log(err); // => 'The connection has already been closed.' + +// No further commands will be processed +client.end(true); + +client.get("hello", function(err) { + console.error(err); // => 'The connection has already been closed.' }); ``` `client.end()` without the flush parameter set to true should NOT be used in production! -## Error handling (>= v2.6) +### Error Handling -Currently the following error subclasses exist: +Currently the following `Error` subclasses exist: - `RedisError`: _All errors_ returned by the client - `ReplyError` subclass of `RedisError`: All errors returned by **Redis** itself @@ -322,30 +299,36 @@ Currently the following error subclasses exist: All error classes are exported by the module. -Example: +#### Example ```js -var redis = require("./"); -var assert = require("assert"); -var client = redis.createClient(); +const assert = require("assert"); + +const redis = require("redis"); +const { AbortError, AggregateError, ReplyError } = require("redis"); +const client = redis.createClient(); client.on("error", function(err) { assert(err instanceof Error); - assert(err instanceof redis.AbortError); - assert(err instanceof redis.AggregateError); - // The set and get get aggregated in here + assert(err instanceof AbortError); + assert(err instanceof AggregateError); + + // The set and get are aggregated in here assert.strictEqual(err.errors.length, 2); assert.strictEqual(err.code, "NR_CLOSED"); }); -client.set("foo", 123, "bar", function(err, res) { + +client.set("foo", "bar", "baz", function(err, res) { // Too many arguments - assert(err instanceof redis.ReplyError); // => true + assert(err instanceof ReplyError); // => true assert.strictEqual(err.command, "SET"); assert.deepStrictEqual(err.args, ["foo", 123, "bar"]); redis.debug_mode = true; + client.set("foo", "bar"); client.get("foo"); + process.nextTick(function() { // Force closing the connection while the command did not yet return client.end(true); @@ -366,7 +349,7 @@ If a command unresolved command got rejected a `UNCERTAIN_STATE` code is returned. A `CONNECTION_BROKEN` error code is used in case node_redis gives up to reconnect. -## client.unref() +### client.unref() Call `unref()` on the underlying socket connection to the Redis server, allowing the program to exit once no more commands are pending. @@ -376,34 +359,35 @@ protocol. Any commands where client state is saved on the Redis server, e.g. `*SUBSCRIBE` or the blocking `BL*` commands will _NOT_ work with `.unref()`. ```js -var redis = require("redis"); -var client = redis.createClient(); +const redis = require("redis"); +const client = redis.createClient(); /* - Calling unref() will allow this program to exit immediately after the get - command finishes. Otherwise the client would hang as long as the - client-server connection is alive. -*/ + * Calling unref() will allow this program to exit immediately after the get + * command finishes. Otherwise the client would hang as long as the + * client-server connection is alive. + */ client.unref(); + client.get("foo", function(err, value) { if (err) throw err; console.log(value); }); ``` -## Friendlier hash commands +### Hash Commands Most Redis commands take a single String or an Array of Strings as arguments, and replies are sent back as a single String or an Array of Strings. When dealing with hash values, there are a couple of useful exceptions to this. -### client.hgetall(hash, callback) +#### client.hgetall(hash, callback) -The reply from an HGETALL command will be converted into a JavaScript Object by +The reply from an `HGETALL` command will be converted into a JavaScript Object by `node_redis`. That way you can interact with the responses using JavaScript syntax. -Example: +**Example:** ```js client.hmset("hosts", "mjr", "1", "another", "23", "home", "1234"); @@ -412,9 +396,11 @@ client.hgetall("hosts", function(err, obj) { }); ``` -### client.hmset(hash, obj[, callback]) +#### client.hmset(hash, obj[, callback]) -Multiple values in a hash can be set by supplying an object: +Multiple values in a hash can be set by supplying an object. + +**Example:** ```js client.HMSET(key2, { @@ -426,15 +412,17 @@ client.HMSET(key2, { The properties and values of this Object will be set as keys and values in the Redis hash. -### client.hmset(hash, key1, val1, ... keyn, valn, [callback]) +#### client.hmset(hash, key1, val1, ...keyN, valN, [callback]) + +Multiple values may also be set by supplying more arguments. -Multiple values may also be set by supplying a list: +**Example:** ```js client.HMSET(key1, "0123456789", "abcdefghij", "some manner of key", "a type of value"); ``` -## Publish / Subscribe +### Publish / Subscribe Example of the publish / subscribe API. This program opens two client connections, subscribes to a channel on one of them, and publishes to that From 6803cf4e8fb3649b1a3d07763a33a41952febcac Mon Sep 17 00:00:00 2001 From: Salakar Date: Sun, 9 Feb 2020 03:07:01 +0000 Subject: [PATCH 0800/1748] docs --- README.md | 169 ++++++++++++++++++++++++++---------------------------- 1 file changed, 80 insertions(+), 89 deletions(-) diff --git a/README.md b/README.md index 935338cdbd2..94b70f8396b 100644 --- a/README.md +++ b/README.md @@ -33,12 +33,12 @@ npm install redis const redis = require("redis"); const client = redis.createClient(); -client.on("error", function(err) { - console.log("Error " + err); +client.on("error", function(error) { + console.error(error); }); -client.set("string key", "string val", redis.print); -client.get("string key", redis.print); +client.set("key", "value", redis.print); +client.get("key", redis.print); ``` Note that the API is entirely asynchronous. To get data back from the server, @@ -66,15 +66,17 @@ a variable number of individual arguments followed by an optional callback. Examples: ```js -client.hmset(["key", "test keys 1", "test val 1", "test keys 2", "test val 2"], function(err, res) { +client.hmset(["key", "foo", "bar"], function(err, res) { // ... }); + // Works the same as -client.hmset("key", ["test keys 1", "test val 1", "test keys 2", "test val 2"], function(err, res) { +client.hmset("key", ["foo", "bar"], function(err, res) { // ... }); + // Or -client.hmset("key", "test keys 1", "test val 1", "test keys 2", "test val 2", function(err, res) { +client.hmset("key", "foo", "bar", function(err, res) { // ... }); ``` @@ -84,15 +86,15 @@ Care should be taken with user input if arrays are possible (via body-parser, qu Note that in either form the `callback` is optional: ```js -client.set("some key", "some val"); -client.set(["some other key", "some val"]); +client.set("foo", "bar"); +client.set(["hello", "world"]); ``` If the key is missing, reply will be null. Only if the [Redis Command Reference](http://redis.io/commands) states something else it will not be null. ```js -client.get("missingkey", function(err, reply) { +client.get("missing_key", function(err, reply) { // reply is null when the key is missing console.log(reply); }); @@ -110,23 +112,23 @@ and Boolean values will result in the value coerced to a string! `client` will emit some events about the state of the connection to the Redis server. -#### "ready" +#### `"ready"` `client` will emit `ready` once a connection is established. Commands issued before the `ready` event are queued, then replayed just before this event is emitted. -#### "connect" +#### `"connect"` `client` will emit `connect` as soon as the stream is connected to the server. -#### "reconnecting" +#### `"reconnecting"` `client` will emit `reconnecting` when trying to reconnect to the Redis server after losing the connection. Listeners are passed an object containing `delay` (in ms from the previous try) and `attempt` (the attempt #) attributes. -#### "error" +#### `"error"` `client` will emit `error` when encountering an error connecting to the Redis server or when any other in node_redis occurs. If you use a command without @@ -135,11 +137,11 @@ listener. So please attach the error listener to node_redis. -#### "end" +#### `"end"` `client` will emit `end` when an established Redis server connection has closed. -#### "warning" +#### `"warning"` `client` will emit `warning` when password was set but none is needed and if a deprecated option / function / similar is used. @@ -390,184 +392,173 @@ syntax. **Example:** ```js -client.hmset("hosts", "mjr", "1", "another", "23", "home", "1234"); -client.hgetall("hosts", function(err, obj) { - console.dir(obj); +client.hmset("key", "foo", "bar", "hello", "world"); + +client.hgetall("hosts", function(err, value) { + console.log(value.foo); // > "bar" + console.log(value.hello); // > "world" }); ``` -#### client.hmset(hash, obj[, callback]) +#### client.hmset(hash, key1, val1, ...keyN, valN, [callback]) -Multiple values in a hash can be set by supplying an object. +Multiple values may also be set by supplying more arguments. **Example:** ```js -client.HMSET(key2, { - "0123456789": "abcdefghij", // NOTE: key and value will be coerced to strings - "some manner of key": "a type of value", -}); +// key +// 1) foo => bar +// 2) hello => world +client.HMSET("key", "foo", "bar", "hello", "world"); ``` -The properties and values of this Object will be set as keys and values in the -Redis hash. +### PubSub -#### client.hmset(hash, key1, val1, ...keyN, valN, [callback]) - -Multiple values may also be set by supplying more arguments. +#### Example -**Example:** +This example opens two client connections, subscribes to a channel on one of them, and publishes to that +channel on the other. ```js -client.HMSET(key1, "0123456789", "abcdefghij", "some manner of key", "a type of value"); -``` +const redis = require("redis"); -### Publish / Subscribe +const subscriber = redis.createClient(); +const publisher = redis.createClient(); -Example of the publish / subscribe API. This program opens two -client connections, subscribes to a channel on one of them, and publishes to that -channel on the other: +let messageCount = 0; -```js -var redis = require("redis"); -var sub = redis.createClient(), - pub = redis.createClient(); -var msg_count = 0; - -sub.on("subscribe", function(channel, count) { - pub.publish("a nice channel", "I am sending a message."); - pub.publish("a nice channel", "I am sending a second message."); - pub.publish("a nice channel", "I am sending my last message."); +subscriber.on("subscribe", function(channel, count) { + publisher.publish("a channel", "a message"); + publisher.publish("a channel", "another message"); }); -sub.on("message", function(channel, message) { - console.log("sub channel " + channel + ": " + message); - msg_count += 1; - if (msg_count === 3) { - sub.unsubscribe(); - sub.quit(); - pub.quit(); +subscriber.on("message", function(channel, message) { + messageCount += 1; + + console.log("Subscriber received message in channel '" + channel + "': " + message); + + if (messageCount === 2) { + subscriber.unsubscribe(); + subscriber.quit(); + publisher.quit(); } }); -sub.subscribe("a nice channel"); +subscriber.subscribe("a channel"); ``` When a client issues a `SUBSCRIBE` or `PSUBSCRIBE`, that connection is put into -a "subscriber" mode. At that point, the only valid commands are those that modify the subscription +a `"subscriber"` mode. At that point, the only valid commands are those that modify the subscription set, and quit (also ping on some redis versions). When the subscription set is empty, the connection is put back into regular mode. If you need to send regular commands to Redis while in subscriber mode, just -open another connection with a new client (hint: use `client.duplicate()`). +open another connection with a new client (use `client.duplicate()` to quickly duplicate an existing client). -## Subscriber Events +#### Subscriber Events If a client has subscriptions active, it may emit these events: -### "message" (channel, message) +**"message" (channel, message)**: Client will emit `message` for every message received that matches an active subscription. Listeners are passed the channel name as `channel` and the message as `message`. -### "pmessage" (pattern, channel, message) +**"pmessage" (pattern, channel, message)**: Client will emit `pmessage` for every message received that matches an active subscription pattern. Listeners are passed the original pattern used with `PSUBSCRIBE` as `pattern`, the sending channel name as `channel`, and the message as `message`. -### "message_buffer" (channel, message) +**"message_buffer" (channel, message)**: This is the same as the `message` event with the exception, that it is always going to emit a buffer. If you listen to the `message` event at the same time as the `message_buffer`, it is always going to emit a string. -### "pmessage_buffer" (pattern, channel, message) +**"pmessage_buffer" (pattern, channel, message)**: This is the same as the `pmessage` event with the exception, that it is always going to emit a buffer. If you listen to the `pmessage` event at the same time as the `pmessage_buffer`, it is always going to emit a string. -### "subscribe" (channel, count) +**"subscribe" (channel, count)**: Client will emit `subscribe` in response to a `SUBSCRIBE` command. Listeners are passed the channel name as `channel` and the new count of subscriptions for this client as `count`. -### "psubscribe" (pattern, count) +**"psubscribe" (pattern, count)**: Client will emit `psubscribe` in response to a `PSUBSCRIBE` command. Listeners are passed the original pattern as `pattern`, and the new count of subscriptions for this client as `count`. -### "unsubscribe" (channel, count) +**"unsubscribe" (channel, count)**: Client will emit `unsubscribe` in response to a `UNSUBSCRIBE` command. Listeners are passed the channel name as `channel` and the new count of subscriptions for this client as `count`. When `count` is 0, this client has left subscriber mode and no more subscriber events will be emitted. -### "punsubscribe" (pattern, count) +**"punsubscribe" (pattern, count)**: Client will emit `punsubscribe` in response to a `PUNSUBSCRIBE` command. Listeners are passed the channel name as `channel` and the new count of subscriptions for this client as `count`. When `count` is 0, this client has left subscriber mode and no more subscriber events will be emitted. -## client.multi([commands]) +### client.multi([commands]) `MULTI` commands are queued up until an `EXEC` is issued, and then all commands are run atomically by Redis. The interface in `node_redis` is to return an individual `Multi` object by calling `client.multi()`. If any command fails to queue, all commands are rolled back and none is going to be executed (For -further information look at -[transactions](http://redis.io/topics/transactions)). +further information see the [Redis transactions](http://redis.io/topics/transactions) documentation). ```js -var redis = require("./index"), - client = redis.createClient(), - set_size = 20; +const redis = require("redis"); +const client = redis.createClient(); + +let setSize = 20; -client.sadd("bigset", "a member"); -client.sadd("bigset", "another member"); +client.sadd("key", "member1"); +client.sadd("key", "member2"); -while (set_size > 0) { - client.sadd("bigset", "member " + set_size); - set_size -= 1; +while (setSize > 0) { + client.sadd("key", "member" + setSize); + setSize -= 1; } -// multi chain with an individual callback +// chain commands client .multi() - .scard("bigset") - .smembers("bigset") - .keys("*", function(err, replies) { - // NOTE: code in this callback is NOT atomic - // this only happens after the the .exec call finishes. - client.mget(replies, redis.print); - }) + .scard("key") + .smembers("key") + .keys("*") .dbsize() .exec(function(err, replies) { console.log("MULTI got " + replies.length + " replies"); replies.forEach(function(reply, index) { - console.log("Reply " + index + ": " + reply.toString()); + console.log("REPLY @ index " + index + ": " + reply.toString()); }); }); ``` -### Multi.exec([callback]) +#### Multi.exec([callback]) `client.multi()` is a constructor that returns a `Multi` object. `Multi` objects share all of the same command methods as `client` objects do. Commands are queued up inside the `Multi` object until `Multi.exec()` is invoked. -If your code contains an syntax error an EXECABORT error is going to be thrown +If your code contains an syntax error an `EXECABORT` error is going to be thrown and all commands are going to be aborted. That error contains a `.errors` property that contains the concrete errors. If all commands were queued successfully and an error is thrown by redis while processing the commands that error is going to be returned in the result array! -No other command is going to be aborted though than the onces failing. +No other command is going to be aborted though than the ones failing. You can either chain together `MULTI` commands as in the above example, or you can queue individual commands while still sending regular client command as in From 24824efdcc838d753f93bdec9572eca099b5d4ad Mon Sep 17 00:00:00 2001 From: Salakar Date: Sun, 9 Feb 2020 03:58:55 +0000 Subject: [PATCH 0801/1748] remove deprecated `new Buffer()` usage --- benchmarks/multi_bench.js | 6 +++--- index.js | 2 +- 2 files changed, 4 insertions(+), 4 deletions(-) diff --git a/benchmarks/multi_bench.js b/benchmarks/multi_bench.js index a79d92e83c9..86cf9329ce9 100644 --- a/benchmarks/multi_bench.js +++ b/benchmarks/multi_bench.js @@ -213,11 +213,11 @@ Test.prototype.print_stats = function () { }; small_str = '1234'; -small_buf = new Buffer(small_str); +small_buf = Buffer.from(small_str); large_str = (new Array(4096 + 1).join('-')); -large_buf = new Buffer(large_str); +large_buf = Buffer.from(large_str); very_large_str = (new Array((4 * 1024 * 1024) + 1).join('-')); -very_large_buf = new Buffer(very_large_str); +very_large_buf = Buffer.from(very_large_str); tests.push(new Test({descr: 'PING', command: 'ping', args: []})); tests.push(new Test({descr: 'PING', command: 'ping', args: [], batch: 50})); diff --git a/index.js b/index.js index bdbf46daf51..6cea8bd4c6c 100644 --- a/index.js +++ b/index.js @@ -829,7 +829,7 @@ RedisClient.prototype.internal_send_command = function (command_obj) { // 30000 seemed to be a good value to switch to buffers after testing and checking the pros and cons if (args[i].length > 30000) { big_data = true; - args_copy[i] = new Buffer(args[i], 'utf8'); + args_copy[i] = Buffer.from(args[i], 'utf8'); } else { args_copy[i] = args[i]; } From 30dd8cf8796d96c239d4324be42f689806685812 Mon Sep 17 00:00:00 2001 From: Salakar Date: Sun, 9 Feb 2020 03:59:02 +0000 Subject: [PATCH 0802/1748] docs --- README.md | 423 ++++++++++++++++++++++++++---------------------------- 1 file changed, 201 insertions(+), 222 deletions(-) diff --git a/README.md b/README.md index 94b70f8396b..236a214b5c1 100644 --- a/README.md +++ b/README.md @@ -308,6 +308,7 @@ const assert = require("assert"); const redis = require("redis"); const { AbortError, AggregateError, ReplyError } = require("redis"); + const client = redis.createClient(); client.on("error", function(err) { @@ -565,21 +566,22 @@ can queue individual commands while still sending regular client command as in this example: ```js -var redis = require("redis"), - client = redis.createClient(), - multi; +const redis = require("redis"); +const client = redis.createClient(); // start a separate multi command queue -multi = client.multi(); -multi.incr("incr thing", redis.print); -multi.incr("incr other thing", redis.print); +const multi = client.multi(); -// runs immediately -client.mset("incr thing", 100, "incr other thing", 1, redis.print); +// add some commands to the queue +multi.incr("count_cats", redis.print); +multi.incr("count_dogs", redis.print); -// drains multi queue and runs atomically +// runs a command immediately outside of the `multi` instance +client.mset("count_cats", 100, "count_dogs", 50, redis.print); + +// drains the multi queue and runs each command atomically multi.exec(function(err, replies) { - console.log(replies); // 101, 2 + console.log(replies); // 101, 51 }); ``` @@ -587,43 +589,26 @@ In addition to adding commands to the `MULTI` queue individually, you can also pass an array of commands and arguments to the constructor: ```js -var redis = require("redis"), - client = redis.createClient(); +const redis = require("redis"); + +const client = redis.createClient(); client .multi([ - ["mget", "multifoo", "multibar", redis.print], - ["incr", "multifoo"], - ["incr", "multibar"], + ["mget", "foo", "bar", redis.print], + ["incr", "hello"], ]) .exec(function(err, replies) { console.log(replies); }); ``` -### Multi.exec_atomic([callback]) +#### Multi.exec_atomic([callback]) Identical to Multi.exec but with the difference that executing a single command will not use transactions. -## client.batch([commands]) - -Identical to .multi without transactions. This is recommended if you want to -execute many commands at once but don't have to rely on transactions. - -`BATCH` commands are queued up until an `EXEC` is issued, and then all commands -are run atomically by Redis. The interface in `node_redis` is to return an -individual `Batch` object by calling `client.batch()`. The only difference -between .batch and .multi is that no transaction is going to be used. -Be aware that the errors are - just like in multi statements - in the result. -Otherwise both, errors and results could be returned at the same time. - -If you fire many commands at once this is going to boost the execution speed -significantly compared to firing the same commands in a loop without waiting for -the result! See the benchmarks for further comparison. Please remember that all -commands are kept in memory until they are fired. - -## Optimistic Locks +#### Optimistic Locks Using `multi` you can make sure your modifications run as a transaction, but you can't be sure you got there first. What if another client modified a key while @@ -633,39 +618,39 @@ To solve this, Redis supports the [WATCH](https://redis.io/topics/transactions) command, which is meant to be used with MULTI: ```js -var redis = require("redis"), - client = redis.createClient({ ... }); - -client.watch("foo", function( err ){ - if(err) throw err; - - client.get("foo", function(err, result) { - if(err) throw err; - - // Process result - // Heavy and time consuming operation here - - client.multi() - .set("foo", "some heavy computation") - .exec(function(err, results) { - - /** - * If err is null, it means Redis successfully attempted - * the operation. - */ - if(err) throw err; - - /** - * If results === null, it means that a concurrent client - * changed the key while we were processing it and thus - * the execution of the MULTI command was not performed. - * - * NOTICE: Failing an execution of MULTI is not considered - * an error. So you will have err === null and results === null - */ - - }); - }); +const redis = require("redis"); + +const client = redis.createClient(); + +client.watch("foo", function(watchError) { + if (watchError) throw watchError; + + client.get("foo", function(getError, result) { + if (getError) throw getError; + + // Process result + // Heavy and time consuming operation here to generate "bar" + + client + .multi() + .set("foo", "bar") + .exec(function(execError, results) { + /** + * If err is null, it means Redis successfully attempted + * the operation. + */ + if (execError) throw err; + + /** + * If results === null, it means that a concurrent client + * changed the key while we were processing it and thus + * the execution of the MULTI command was not performed. + * + * NOTICE: Failing an execution of MULTI is not considered + * an error. So you will have err === null and results === null + */ + }); + }); }); ``` @@ -681,14 +666,18 @@ client cannot connect to Redis. An example where we can see the execution of a `multi` command fail is as follows: ```js -let clients = {}; -clients.watcher = redis.createClient({ ... } ); -clients.alterer = clients.watcher.duplicate(); +const clients = { + watcher: redis.createClient(), + modifier: redis.createClient(), +}; -clients.watcher.watch('foo',function(err) { - if (err) { throw err; } - //if you comment out the next line, the transaction will work - clients.alterer.set('foo',Math.random(), (err) => {if (err) { throw err; }}); +clients.watcher.watch("foo", function(watchError) { + if (watchError) throw watchError; + + // if you comment out the next line, the transaction will work + clients.modifier.set("foo", Math.random(), setError => { + if (setError) throw err; + }); //using a setTimeout here to ensure that the MULTI/EXEC will come after the SET. //Normally, you would use a callback to ensure order, but I want the above SET command @@ -696,23 +685,25 @@ clients.watcher.watch('foo',function(err) { setTimeout(function() { clients.watcher .multi() - .set('foo','abc') - .set('bar','1234') - .exec((err,results) => { - if (err) { throw err; } + .set("foo", "bar") + .set("hello", "world") + .exec((multiExecError, results) => { + if (multiExecError) throw multiExecError; + if (results === null) { - console.log('transaction aborted because results were null'); + console.log("transaction aborted because results were null"); } else { - console.log('transaction worked and returned',results) + console.log("transaction worked and returned", results); } + clients.watcher.quit(); - clients.alterer.quit(); + clients.modifier.quit(); }); - },1000); + }, 1000); }); ``` -### WATCH limitations +#### `WATCH` limitations Redis WATCH works only on _whole_ key values. For example, with WATCH you can watch a hash for modifications, but you cannot watch a specific field of a hash. @@ -721,31 +712,49 @@ The following example would watch the keys `foo` and `hello`, not the field `hel of hash `foo`: ```js -var redis = require("redis"), - client = redis.createClient({ ... }); +const redis = require("redis"); -client.hget( "foo", "hello", function(err, result){ +const client = redis.createClient(); - //Do some processing with the value from this field and watch it after +client.hget("foo", "hello", function(hashGetError, result) { + if (hashGetError) throw hashGetError; - client.watch("foo", "hello", function( err ){ - if(err) throw err; + //Do some processing with the value from this field and watch it after - /** - * WRONG: This is now watching the keys 'foo' and 'hello'. It is not - * watching the field 'hello' of hash 'foo'. Because the key 'foo' - * refers to a hash, this command is now watching the entire hash - * for modifications. - */ - }); -} ) + client.watch("foo", "hello", function(watchError) { + if (watchError) throw watchError; + /** + * This is now watching the keys 'foo' and 'hello'. It is not + * watching the field 'hello' of hash 'foo'. Because the key 'foo' + * refers to a hash, this command is now watching the entire hash + * for modifications. + */ + }); +}); ``` -This limitation also applies to sets ( cannot watch individual set members ) +This limitation also applies to sets (you can not watch individual set members) and any other collections. -## Monitor mode +### client.batch([commands]) + +Identical to `.multi()` without transactions. This is recommended if you want to +execute many commands at once but don't need to rely on transactions. + +`BATCH` commands are queued up until an `EXEC` is issued, and then all commands +are run atomically by Redis. The interface in `node_redis` is to return an +individual `Batch` object by calling `client.batch()`. The only difference +between .batch and .multi is that no transaction is going to be used. +Be aware that the errors are - just like in multi statements - in the result. +Otherwise both, errors and results could be returned at the same time. + +If you fire many commands at once this is going to boost the execution speed +significantly compared to firing the same commands in a loop without waiting for +the result! See the benchmarks for further comparison. Please remember that all +commands are kept in memory until they are fired. + +### Monitor mode Redis supports the `MONITOR` command, which lets you see all commands received by the Redis server across all client connections, including from other client @@ -756,25 +765,28 @@ connected to the server including the monitoring client itself. The callback for the `monitor` event takes a timestamp from the Redis server, an array of command arguments and the raw monitoring string. -Example: +#### Example: ```js -var client = require("redis").createClient(); +const redis = require("redis"); +const client = redis.createClient(); + client.monitor(function(err, res) { console.log("Entering monitoring mode."); }); + client.set("foo", "bar"); -client.on("monitor", function(time, args, raw_reply) { +client.on("monitor", function(time, args, rawReply) { console.log(time + ": " + args); // 1458910076.446514:['set', 'foo', 'bar'] }); ``` -# Extras +## Extras -Some other things you might like to know about. +Some other things you might find useful. -## client.server_info +### `client.server_info` After the ready probe completes, the results from the INFO command are saved in the `client.server_info` object. @@ -782,47 +794,45 @@ the `client.server_info` object. The `versions` key contains an array of the elements of the version string for easy comparison. - > client.server_info.redis_version - '2.3.0' - > client.server_info.versions - [ 2, 3, 0 ] +``` +> client.server_info.redis_version +'2.3.0' +> client.server_info.versions +[ 2, 3, 0 ] +``` -## redis.print() +### `redis.print()` A handy callback function for displaying return values when testing. Example: ```js -var redis = require("redis"), - client = redis.createClient(); +const redis = require("redis"); +const client = redis.createClient(); client.on("connect", function() { - client.set("foo_rand000000000000", "some fantastic value", redis.print); - client.get("foo_rand000000000000", redis.print); + client.set("foo", "bar", redis.print); // => "Reply: OK" + client.get("foo", redis.print); // => "Reply: bar" + client.quit(); }); ``` -This will print: - - Reply: OK - Reply: some fantastic value - -Note that this program will not exit cleanly because the client is still connected. - -## Multi-word commands +### Multi-word commands To execute redis multi-word commands like `SCRIPT LOAD` or `CLIENT LIST` pass the second word as first parameter: ```js client.script("load", "return 1"); + client .multi() .script("load", "return 1") .exec(); + client.multi([["script", "load", "return 1"]]).exec(); ``` -## client.duplicate([options][, callback]) +### `client.duplicate([options][, callback])` Duplicate all current options and return a new redisClient instance. All options passed to the duplicate function are going to replace the original option. If @@ -832,36 +842,13 @@ to return an error instead in the callback. One example of when to use duplicate() would be to accommodate the connection- blocking redis commands BRPOP, BLPOP, and BRPOPLPUSH. If these commands -are used on the same redisClient instance as non-blocking commands, the +are used on the same Redis client instance as non-blocking commands, the non-blocking ones may be queued up until after the blocking ones finish. -```js -var Redis = require("redis"); -var client = Redis.createClient(); -var clientBlocking = client.duplicate(); - -var get = function() { - console.log("get called"); - client.get("any_key", function() { - console.log("get returned"); - }); - setTimeout(get, 1000); -}; -var brpop = function() { - console.log("brpop called"); - clientBlocking.brpop("nonexistent", 5, function() { - console.log("brpop return"); - setTimeout(brpop, 1000); - }); -}; -get(); -brpop(); -``` - Another reason to use duplicate() is when multiple DBs on the same server are accessed via the redis SELECT command. Each DB could use its own connection. -## client.send_command(command_name[, [args][, callback]]) +### `client.send_command(command_name[, [args][, callback]])` All Redis commands have been added to the `client` object. However, if new commands are introduced before this library is updated or if you want to add @@ -871,23 +858,23 @@ Redis. All commands are sent as multi-bulk commands. `args` can either be an Array of arguments, or omitted / set to undefined. -## redis.add_command(command_name) +### `redis.add_command(command_name)` Calling add_command will add a new command to the prototype. The exact command name will be used when calling using this new command. Using arbitrary arguments is possible as with any other command. -## client.connected +### `client.connected` Boolean tracking the state of the connection to the Redis server. -## client.command_queue_length +### `client.command_queue_length` The number of commands that have been sent to the Redis server but not yet replied to. You can use this to enforce some kind of maximum queue depth for commands while connected. -## client.offline_queue_length +### `client.offline_queue_length` The number of commands that have been queued up for a future connection. You can use this to enforce some kind of maximum queue depth for pre-connection @@ -897,31 +884,32 @@ commands. This applies to anything that uses an optional `[WITHSCORES]` or `[LIMIT offset count]` in the [redis.io/commands](http://redis.io/commands) documentation. -Example: +#### Example ```js -var args = ["myzset", 1, "one", 2, "two", 3, "three", 99, "ninety-nine"]; -client.zadd(args, function(err, response) { - if (err) throw err; - console.log("added " + response + " items."); +const args = ["myzset", 1, "one", 2, "two", 3, "three", 99, "ninety-nine"]; + +client.zadd(args, function(addError, addResponse) { + if (addError) throw addError; + console.log("added " + addResponse + " items."); // -Infinity and +Infinity also work - var args1 = ["myzset", "+inf", "-inf"]; - client.zrevrangebyscore(args1, function(err, response) { - if (err) throw err; - console.log("example1", response); - // write your code here + const args1 = ["myzset", "+inf", "-inf"]; + client.zrevrangebyscore(args1, function(rangeError, rangeResponse) { + if (rangeError) throw rangeError; + console.log("response1", rangeResponse); + // ... }); - var max = 3, - min = 1, - offset = 1, - count = 2; - var args2 = ["myzset", max, min, "WITHSCORES", "LIMIT", offset, count]; - client.zrevrangebyscore(args2, function(err, response) { - if (err) throw err; - console.log("example2", response); - // write your code here + const max = 3; + const min = 1; + const offset = 1; + const count = 2; + const args2 = ["myzset", max, min, "WITHSCORES", "LIMIT", offset, count]; + client.zrevrangebyscore(args2, function(rangeError, rangeResponse) { + if (rangeError) throw rangeError; + console.log("response2", rangeResponse); + // ... }); }); ``` @@ -932,42 +920,42 @@ Much effort has been spent to make `node_redis` as fast as possible for common operations. ``` -Lenovo T450s, i7-5600U and 12gb memory -clients: 1, NodeJS: 6.2.0, Redis: 3.2.0, parser: javascript, connected by: tcp - PING, 1/1 avg/max: 0.02/ 5.26 2501ms total, 46916 ops/sec - PING, batch 50/1 avg/max: 0.06/ 4.35 2501ms total, 755178 ops/sec - SET 4B str, 1/1 avg/max: 0.02/ 4.75 2501ms total, 40856 ops/sec - SET 4B str, batch 50/1 avg/max: 0.11/ 1.51 2501ms total, 432727 ops/sec - SET 4B buf, 1/1 avg/max: 0.05/ 2.76 2501ms total, 20659 ops/sec - SET 4B buf, batch 50/1 avg/max: 0.25/ 1.76 2501ms total, 194962 ops/sec - GET 4B str, 1/1 avg/max: 0.02/ 1.55 2501ms total, 45156 ops/sec - GET 4B str, batch 50/1 avg/max: 0.09/ 3.15 2501ms total, 524110 ops/sec - GET 4B buf, 1/1 avg/max: 0.02/ 3.07 2501ms total, 44563 ops/sec - GET 4B buf, batch 50/1 avg/max: 0.10/ 3.18 2501ms total, 473171 ops/sec - SET 4KiB str, 1/1 avg/max: 0.03/ 1.54 2501ms total, 32627 ops/sec - SET 4KiB str, batch 50/1 avg/max: 0.34/ 1.89 2501ms total, 146861 ops/sec - SET 4KiB buf, 1/1 avg/max: 0.05/ 2.85 2501ms total, 20688 ops/sec - SET 4KiB buf, batch 50/1 avg/max: 0.36/ 1.83 2501ms total, 138165 ops/sec - GET 4KiB str, 1/1 avg/max: 0.02/ 1.37 2501ms total, 39389 ops/sec - GET 4KiB str, batch 50/1 avg/max: 0.24/ 1.81 2501ms total, 208157 ops/sec - GET 4KiB buf, 1/1 avg/max: 0.02/ 2.63 2501ms total, 39918 ops/sec - GET 4KiB buf, batch 50/1 avg/max: 0.31/ 8.56 2501ms total, 161575 ops/sec - INCR, 1/1 avg/max: 0.02/ 4.69 2501ms total, 45685 ops/sec - INCR, batch 50/1 avg/max: 0.09/ 3.06 2501ms total, 539964 ops/sec - LPUSH, 1/1 avg/max: 0.02/ 3.04 2501ms total, 41253 ops/sec - LPUSH, batch 50/1 avg/max: 0.12/ 1.94 2501ms total, 425090 ops/sec - LRANGE 10, 1/1 avg/max: 0.02/ 2.28 2501ms total, 39850 ops/sec - LRANGE 10, batch 50/1 avg/max: 0.25/ 1.85 2501ms total, 194302 ops/sec - LRANGE 100, 1/1 avg/max: 0.05/ 2.93 2501ms total, 21026 ops/sec - LRANGE 100, batch 50/1 avg/max: 1.52/ 2.89 2501ms total, 32767 ops/sec - SET 4MiB str, 1/1 avg/max: 5.16/ 15.55 2502ms total, 193 ops/sec - SET 4MiB str, batch 20/1 avg/max: 89.73/ 99.96 2513ms total, 223 ops/sec - SET 4MiB buf, 1/1 avg/max: 2.23/ 8.35 2501ms total, 446 ops/sec - SET 4MiB buf, batch 20/1 avg/max: 41.47/ 50.91 2530ms total, 482 ops/sec - GET 4MiB str, 1/1 avg/max: 2.79/ 10.91 2502ms total, 358 ops/sec - GET 4MiB str, batch 20/1 avg/max: 101.61/118.11 2541ms total, 197 ops/sec - GET 4MiB buf, 1/1 avg/max: 2.32/ 14.93 2502ms total, 430 ops/sec - GET 4MiB buf, batch 20/1 avg/max: 65.01/ 84.72 2536ms total, 308 ops/sec +Mac mini (2018), i7-3.2GHz and 32gb memory +clients: 1, NodeJS: 12.15.0, Redis: 5.0.6, parser: javascript, connected by: tcp + PING, 1/1 avg/max: 0.03/ 3.28 2501ms total, 31926 ops/sec + PING, batch 50/1 avg/max: 0.08/ 3.35 2501ms total, 599460 ops/sec + SET 4B str, 1/1 avg/max: 0.03/ 3.54 2501ms total, 29483 ops/sec + SET 4B str, batch 50/1 avg/max: 0.10/ 1.39 2501ms total, 477689 ops/sec + SET 4B buf, 1/1 avg/max: 0.04/ 1.52 2501ms total, 23449 ops/sec + SET 4B buf, batch 50/1 avg/max: 0.20/ 2.09 2501ms total, 244382 ops/sec + GET 4B str, 1/1 avg/max: 0.03/ 1.35 2501ms total, 32205 ops/sec + GET 4B str, batch 50/1 avg/max: 0.09/ 2.02 2501ms total, 568992 ops/sec + GET 4B buf, 1/1 avg/max: 0.03/ 2.93 2501ms total, 32802 ops/sec + GET 4B buf, batch 50/1 avg/max: 0.08/ 1.03 2501ms total, 592863 ops/sec + SET 4KiB str, 1/1 avg/max: 0.03/ 0.76 2501ms total, 29287 ops/sec + SET 4KiB str, batch 50/1 avg/max: 0.35/ 2.97 2501ms total, 143163 ops/sec + SET 4KiB buf, 1/1 avg/max: 0.04/ 1.21 2501ms total, 23070 ops/sec + SET 4KiB buf, batch 50/1 avg/max: 0.28/ 2.34 2501ms total, 176809 ops/sec + GET 4KiB str, 1/1 avg/max: 0.03/ 1.54 2501ms total, 29555 ops/sec + GET 4KiB str, batch 50/1 avg/max: 0.18/ 1.59 2501ms total, 279188 ops/sec + GET 4KiB buf, 1/1 avg/max: 0.03/ 1.80 2501ms total, 30681 ops/sec + GET 4KiB buf, batch 50/1 avg/max: 0.17/ 5.00 2501ms total, 285886 ops/sec + INCR, 1/1 avg/max: 0.03/ 1.99 2501ms total, 32757 ops/sec + INCR, batch 50/1 avg/max: 0.09/ 2.54 2501ms total, 538964 ops/sec + LPUSH, 1/1 avg/max: 0.05/ 4.85 2501ms total, 19482 ops/sec + LPUSH, batch 50/1 avg/max: 0.12/ 9.52 2501ms total, 395562 ops/sec + LRANGE 10, 1/1 avg/max: 0.06/ 9.21 2501ms total, 17062 ops/sec + LRANGE 10, batch 50/1 avg/max: 0.22/ 1.03 2501ms total, 228269 ops/sec + LRANGE 100, 1/1 avg/max: 0.05/ 1.44 2501ms total, 19051 ops/sec + LRANGE 100, batch 50/1 avg/max: 0.99/ 3.46 2501ms total, 50480 ops/sec + SET 4MiB str, 1/1 avg/max: 4.11/ 13.96 2501ms total, 243 ops/sec + SET 4MiB str, batch 20/1 avg/max: 91.16/145.01 2553ms total, 219 ops/sec + SET 4MiB buf, 1/1 avg/max: 2.81/ 11.90 2502ms total, 354 ops/sec + SET 4MiB buf, batch 20/1 avg/max: 36.21/ 70.96 2535ms total, 552 ops/sec + GET 4MiB str, 1/1 avg/max: 2.82/ 19.10 2503ms total, 354 ops/sec + GET 4MiB str, batch 20/1 avg/max: 128.57/207.86 2572ms total, 156 ops/sec + GET 4MiB buf, 1/1 avg/max: 3.13/ 23.88 2501ms total, 318 ops/sec + GET 4MiB buf, batch 20/1 avg/max: 65.91/ 87.59 2572ms total, 303 ops/sec ``` ## Debugging @@ -983,7 +971,8 @@ Good stack traces are only activated in development and debug mode as this results in a significant performance penalty. **_Comparison_**: -Useless stack trace: + +Standard stack trace: ``` ReplyError: ERR wrong number of arguments for 'set' command @@ -991,7 +980,7 @@ ReplyError: ERR wrong number of arguments for 'set' command at parseType (/home/ruben/repos/redis/node_modules/redis-parser/lib/parser.js:219:14) ``` -Good stack trace: +Debug stack trace: ``` ReplyError: ERR wrong number of arguments for 'set' command @@ -1009,20 +998,10 @@ ReplyError: ERR wrong number of arguments for 'set' command at processImmediate [as _immediateCallback] (timers.js:383:17) ``` -## How to Contribute - -- Open a pull request or an issue about what you want to implement / change. We're glad for any help! -- Please be aware that we'll only accept fully tested code. - -## Contributors - -The original author of node_redis is [Matthew Ranney](https://github.com/mranney) - -The current lead maintainer is [Ruben Bridgewater](https://github.com/BridgeAR) +## Contributing -Many [others](https://github.com/NodeRedis/node_redis/graphs/contributors) -contributed to `node_redis` too. Thanks to all of them! +Please see the [contributing guide](CONTRIBUTING.md). ## License -[MIT](LICENSE) +This repository is licensed under the "MIT" license. See [LICENSE](LICENSE). From d92665d931379f74e94e5c63ddf94d3a275b1d45 Mon Sep 17 00:00:00 2001 From: Salakar Date: Sun, 9 Feb 2020 04:48:48 +0000 Subject: [PATCH 0803/1748] docs --- .github/ISSUE_TEMPLATE.md | 39 ++++++++--- .github/PULL_REQUEST_TEMPLATE.md | 20 +++--- CONTRIBUTING.md | 107 ++++++++++++++++++++++++++++++- 3 files changed, 147 insertions(+), 19 deletions(-) diff --git a/.github/ISSUE_TEMPLATE.md b/.github/ISSUE_TEMPLATE.md index 42f2d3eeb61..c74eb12b803 100644 --- a/.github/ISSUE_TEMPLATE.md +++ b/.github/ISSUE_TEMPLATE.md @@ -1,15 +1,34 @@ -_Thanks for wanting to report an issue you've found in node_redis. Please delete +--- +title: ⚠️ Bug report +labels: needs-triage +--- + +### Issue + + + +> Describe your issue here + + +--- -* **Version**: What node_redis and what redis version is the issue happening on? -* **Platform**: What platform / version? (For example Node.js 0.10 or Node.js 5.7.0 on Windows 7 / Ubuntu 15.10 / Azure) -* **Description**: Description of your issue, stack traces from errors and code that reproduces the issue +### Environment -[gitter]: https://gitter.im/NodeRedis/node_redis?utm_source=badge&utm_medium=badge&utm_campaign=pr-badge \ No newline at end of file + + - **Node.js Version**: `VERSION_HERE` + + + - **Redis Version**: `VERSION_HERE` + + + - **Platform**: `PLATFORM_HERE` diff --git a/.github/PULL_REQUEST_TEMPLATE.md b/.github/PULL_REQUEST_TEMPLATE.md index 9706621c1b1..5c29bc26e23 100644 --- a/.github/PULL_REQUEST_TEMPLATE.md +++ b/.github/PULL_REQUEST_TEMPLATE.md @@ -1,14 +1,18 @@ -### Pull Request check-list +### Description -_Please make sure to review and check all of these items:_ + + + + + + +### Check List + + - [ ] Does `npm test` pass with this change (including linting)? - [ ] Is the new or changed code fully tested? - [ ] Is a documentation update included (if this change modifies existing APIs, or introduces new ones)? -_NOTE: these things are not required to open a PR and can be done -afterwards / while the PR is open._ - -### Description of change - -_Please provide a description of the change here._ \ No newline at end of file + diff --git a/CONTRIBUTING.md b/CONTRIBUTING.md index cf873b4c072..e82846fbee8 100644 --- a/CONTRIBUTING.md +++ b/CONTRIBUTING.md @@ -1 +1,106 @@ - +# Introduction + +First, thank you for considering contributing to Node Redis! It's people like you that make the open source community such a great community! 😊 + +We welcome any type of contribution, not just code. You can help with; + +- **QA**: file bug reports, the more details you can give the better (e.g. platform versions, screenshots sdk versions & logs) +- **Docs**: improve reference coverage, add more examples, fix typos or anything else you can spot. At the top of every page on our docs site you can click the `Edit` pencil to go to that pages markdown file, or view the [Docs Repo](https://github.com/invertase/react-native-firebase-docs) directly +- **Code**: take a look at the open issues. Even if you can't write code, commenting on them, showing that you care about a given issue matters. +- **Donations**: we welcome financial contributions in full transparency on our [open collective](https://opencollective.com/node-redis). + +--- + +## Project Guidelines + +As maintainers of this project, we want to ensure that the project lives and continues to grow. Not blocked by any +singular person's time. + +One of the simplest ways of doing this is by encouraging a larger set of shallow contributors. Through this we hope to +mitigate the problems of a project that needs updates but there is no-one who has the power to do so. + +### Continuous Deployment + + +Coming soon. + +### How can we help you get comfortable contributing? + +It is normal for a first pull request to be a potential fix for a problem but moving on from there to helping the +project's direction can be difficult. + +We try to help contributors cross that barrier by offering good first step issues (labelled `good-first-issue`). These +issues can be fixed without feeling like you are stepping on toes. Generally, these should be non-critical issues that +are well defined. They will be purposely avoided by mature contributors to the project, to make space for others. + +Additionally issues labelled `needs-triage` or `help-wanted` can also be picked up, these may not necessarily require +code changes but rather help with debugging and finding the cause of the issue whether it's a bug or a users incorrect +setup of the library or project. + +We aim to keep all project discussion inside GitHub issues. This is to make sure valuable discussion is accessible via +search. If you have questions about how to use the library, or how the project is running - GitHub issues are the goto +tool for this project. + +### Our expectations on you as a contributor + +You shouldn't feel bad for not contributing to open source. We want contributors like yourself to provide ideas, keep +the ship shipping and to take some of the load from others. It is non-obligatory; we’re here to get things done in an +enjoyable way. :trophy: + +We only ask that you follow the conduct guidelines set out in our [Code of Conduct](/CODE_OF_CONDUCT.md) throughout your +contribution journey. + +### What about if you have problems that cannot be discussed in public? + +You can reach out to us directly via email (`redis[AT]invertase.io`) or direct message us on +[Twitter](https://twitter.com/NodeRedis) if you'd like to discuss something privately. + +#### Project Maintainers + + - Mike Diarmid ([Salakar](https://github.com/Salakar)) @ [Invertase](https://invertase.io) + - Twitter: [@mikediarmid](https://twitter.com/mikediarmid) + - Elliot Hesp ([Ehesp](https://github.com/Ehesp)) @ [Invertase](https://invertase.io) + - Twitter: [@elliothesp](https://twitter.com/elliothesp) + - Ruben Bridgewater ([BridgeAR](https://github.com/BridgeAR)) + - Twitter: [@BridgeAR](https://twitter.com/BridgeAR) + +Huge thanks to the original author of Node Redis, [Matthew Ranney](https://github.com/mranney) and also to +[Ruben Bridgewater](https://github.com/BridgeAR) for handing over this project over to new maintainers so it could be +continuously maintained. + +--- + +## Code Guidelines + +### Your First Contribution + +Working on your first Pull Request? You can learn how from this _free_ series, +[How to Contribute to an Open Source Project on GitHub](https://egghead.io/series/how-to-contribute-to-an-open-source-project-on-github). + +### Testing Code + +Node Redis has a full test suite with coverage setup. + +To run the tests use the `npm test` command. To check detailed coverage locally run the `npm run coverage` command after +testing and open the generated `./coverage/index.html` in your browser. + +### Submitting code for review + +The bigger the pull request, the longer it will take to review and merge. Where possible try to break down large pull +requests into smaller chunks that are easier to review and merge. It is also always helpful to have some context for +your pull request. What was the purpose? Why does it matter to you? What problem are you trying to solve? Tag in any linked issues. + +To aid review we also ask that you fill out the pull request template as much as possible. + +> Use a `draft` pull request if your pull request is not complete or ready for review. + +### Code review process + +Pull Requests to the protected branches require two or more peer-review approvals and passing status checks to be able +to be merged. + +When reviewing a Pull Request please check the following steps on top of the existing automated checks: + +- Does the it provide or update the docs if docs changes are required? +- Have the tests been updated or new tests been added to test any newly implemented or changed functionality? +- Is the testing coverage ok and not worse than previously? From bf673baca70a156695ae8c860a64d06ffb65832c Mon Sep 17 00:00:00 2001 From: Salakar Date: Sun, 9 Feb 2020 04:50:48 +0000 Subject: [PATCH 0804/1748] docs --- CONTRIBUTING.md | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/CONTRIBUTING.md b/CONTRIBUTING.md index e82846fbee8..b7bc2c3b179 100644 --- a/CONTRIBUTING.md +++ b/CONTRIBUTING.md @@ -5,8 +5,8 @@ First, thank you for considering contributing to Node Redis! It's people like yo We welcome any type of contribution, not just code. You can help with; - **QA**: file bug reports, the more details you can give the better (e.g. platform versions, screenshots sdk versions & logs) -- **Docs**: improve reference coverage, add more examples, fix typos or anything else you can spot. At the top of every page on our docs site you can click the `Edit` pencil to go to that pages markdown file, or view the [Docs Repo](https://github.com/invertase/react-native-firebase-docs) directly -- **Code**: take a look at the open issues. Even if you can't write code, commenting on them, showing that you care about a given issue matters. +- **Docs**: improve reference coverage, add more examples, fix typos or anything else you can spot. +- **Code**: take a look at the open issues and help triage them. - **Donations**: we welcome financial contributions in full transparency on our [open collective](https://opencollective.com/node-redis). --- From f45850505d9ad31286c55b07edb0d31457b0be96 Mon Sep 17 00:00:00 2001 From: Salakar Date: Sun, 9 Feb 2020 04:54:12 +0000 Subject: [PATCH 0805/1748] docs --- .github/PULL_REQUEST_TEMPLATE.md | 7 ++++++- CONTRIBUTING.md | 4 ++-- 2 files changed, 8 insertions(+), 3 deletions(-) diff --git a/.github/PULL_REQUEST_TEMPLATE.md b/.github/PULL_REQUEST_TEMPLATE.md index 5c29bc26e23..98e3d312605 100644 --- a/.github/PULL_REQUEST_TEMPLATE.md +++ b/.github/PULL_REQUEST_TEMPLATE.md @@ -1,12 +1,17 @@ + + ### Description +> Description your pull request here + +--- -### Check List +### Checklist diff --git a/CONTRIBUTING.md b/CONTRIBUTING.md index b7bc2c3b179..192219267a9 100644 --- a/CONTRIBUTING.md +++ b/CONTRIBUTING.md @@ -57,9 +57,9 @@ You can reach out to us directly via email (`redis[AT]invertase.io`) or direct m #### Project Maintainers - - Mike Diarmid ([Salakar](https://github.com/Salakar)) @ [Invertase](https://invertase.io) + - Mike Diarmid ([Salakar](https://github.com/Salakar)) @ [Invertase](https://github.com/invertase) - Twitter: [@mikediarmid](https://twitter.com/mikediarmid) - - Elliot Hesp ([Ehesp](https://github.com/Ehesp)) @ [Invertase](https://invertase.io) + - Elliot Hesp ([Ehesp](https://github.com/Ehesp)) @ [Invertase](https://github.com/invertase) - Twitter: [@elliothesp](https://twitter.com/elliothesp) - Ruben Bridgewater ([BridgeAR](https://github.com/BridgeAR)) - Twitter: [@BridgeAR](https://twitter.com/BridgeAR) From caa5ac70982d3cf6747cc74854f07c2f8eb288e2 Mon Sep 17 00:00:00 2001 From: Salakar Date: Sun, 9 Feb 2020 04:55:18 +0000 Subject: [PATCH 0806/1748] docs & npm ignore --- .npmignore | 1 + CONTRIBUTING.md | 51 +++++++++++++++++++++++++------------------------ README.md | 2 +- 3 files changed, 28 insertions(+), 26 deletions(-) diff --git a/.npmignore b/.npmignore index 497486e4e0d..6e559f54aa4 100644 --- a/.npmignore +++ b/.npmignore @@ -17,3 +17,4 @@ CODE_OF_CONDUCT.md .travis.yml appveyor.yml package-lock.json +.prettierrc diff --git a/CONTRIBUTING.md b/CONTRIBUTING.md index 192219267a9..f9cba8b4a61 100644 --- a/CONTRIBUTING.md +++ b/CONTRIBUTING.md @@ -13,38 +13,39 @@ We welcome any type of contribution, not just code. You can help with; ## Project Guidelines -As maintainers of this project, we want to ensure that the project lives and continues to grow. Not blocked by any +As maintainers of this project, we want to ensure that the project lives and continues to grow. Not blocked by any singular person's time. -One of the simplest ways of doing this is by encouraging a larger set of shallow contributors. Through this we hope to +One of the simplest ways of doing this is by encouraging a larger set of shallow contributors. Through this we hope to mitigate the problems of a project that needs updates but there is no-one who has the power to do so. ### Continuous Deployment + Coming soon. ### How can we help you get comfortable contributing? -It is normal for a first pull request to be a potential fix for a problem but moving on from there to helping the +It is normal for a first pull request to be a potential fix for a problem but moving on from there to helping the project's direction can be difficult. -We try to help contributors cross that barrier by offering good first step issues (labelled `good-first-issue`). These -issues can be fixed without feeling like you are stepping on toes. Generally, these should be non-critical issues that +We try to help contributors cross that barrier by offering good first step issues (labelled `good-first-issue`). These +issues can be fixed without feeling like you are stepping on toes. Generally, these should be non-critical issues that are well defined. They will be purposely avoided by mature contributors to the project, to make space for others. -Additionally issues labelled `needs-triage` or `help-wanted` can also be picked up, these may not necessarily require -code changes but rather help with debugging and finding the cause of the issue whether it's a bug or a users incorrect +Additionally issues labelled `needs-triage` or `help-wanted` can also be picked up, these may not necessarily require +code changes but rather help with debugging and finding the cause of the issue whether it's a bug or a users incorrect setup of the library or project. -We aim to keep all project discussion inside GitHub issues. This is to make sure valuable discussion is accessible via -search. If you have questions about how to use the library, or how the project is running - GitHub issues are the goto +We aim to keep all project discussion inside GitHub issues. This is to make sure valuable discussion is accessible via +search. If you have questions about how to use the library, or how the project is running - GitHub issues are the goto tool for this project. ### Our expectations on you as a contributor -You shouldn't feel bad for not contributing to open source. We want contributors like yourself to provide ideas, keep -the ship shipping and to take some of the load from others. It is non-obligatory; we’re here to get things done in an +You shouldn't feel bad for not contributing to open source. We want contributors like yourself to provide ideas, keep +the ship shipping and to take some of the load from others. It is non-obligatory; we’re here to get things done in an enjoyable way. :trophy: We only ask that you follow the conduct guidelines set out in our [Code of Conduct](/CODE_OF_CONDUCT.md) throughout your @@ -52,20 +53,20 @@ contribution journey. ### What about if you have problems that cannot be discussed in public? -You can reach out to us directly via email (`redis[AT]invertase.io`) or direct message us on +You can reach out to us directly via email (`redis[AT]invertase.io`) or direct message us on [Twitter](https://twitter.com/NodeRedis) if you'd like to discuss something privately. #### Project Maintainers - - Mike Diarmid ([Salakar](https://github.com/Salakar)) @ [Invertase](https://github.com/invertase) - - Twitter: [@mikediarmid](https://twitter.com/mikediarmid) - - Elliot Hesp ([Ehesp](https://github.com/Ehesp)) @ [Invertase](https://github.com/invertase) - - Twitter: [@elliothesp](https://twitter.com/elliothesp) - - Ruben Bridgewater ([BridgeAR](https://github.com/BridgeAR)) - - Twitter: [@BridgeAR](https://twitter.com/BridgeAR) - -Huge thanks to the original author of Node Redis, [Matthew Ranney](https://github.com/mranney) and also to -[Ruben Bridgewater](https://github.com/BridgeAR) for handing over this project over to new maintainers so it could be +- Mike Diarmid ([Salakar](https://github.com/Salakar)) @ [Invertase](https://github.com/invertase) + - Twitter: [@mikediarmid](https://twitter.com/mikediarmid) +- Elliot Hesp ([Ehesp](https://github.com/Ehesp)) @ [Invertase](https://github.com/invertase) + - Twitter: [@elliothesp](https://twitter.com/elliothesp) +- Ruben Bridgewater ([BridgeAR](https://github.com/BridgeAR)) + - Twitter: [@BridgeAR](https://twitter.com/BridgeAR) + +Huge thanks to the original author of Node Redis, [Matthew Ranney](https://github.com/mranney) and also to +[Ruben Bridgewater](https://github.com/BridgeAR) for handing over this project over to new maintainers so it could be continuously maintained. --- @@ -74,7 +75,7 @@ continuously maintained. ### Your First Contribution -Working on your first Pull Request? You can learn how from this _free_ series, +Working on your first Pull Request? You can learn how from this _free_ series, [How to Contribute to an Open Source Project on GitHub](https://egghead.io/series/how-to-contribute-to-an-open-source-project-on-github). ### Testing Code @@ -86,8 +87,8 @@ testing and open the generated `./coverage/index.html` in your browser. ### Submitting code for review -The bigger the pull request, the longer it will take to review and merge. Where possible try to break down large pull -requests into smaller chunks that are easier to review and merge. It is also always helpful to have some context for +The bigger the pull request, the longer it will take to review and merge. Where possible try to break down large pull +requests into smaller chunks that are easier to review and merge. It is also always helpful to have some context for your pull request. What was the purpose? Why does it matter to you? What problem are you trying to solve? Tag in any linked issues. To aid review we also ask that you fill out the pull request template as much as possible. @@ -96,7 +97,7 @@ To aid review we also ask that you fill out the pull request template as much as ### Code review process -Pull Requests to the protected branches require two or more peer-review approvals and passing status checks to be able +Pull Requests to the protected branches require two or more peer-review approvals and passing status checks to be able to be merged. When reviewing a Pull Request please check the following steps on top of the existing automated checks: diff --git a/README.md b/README.md index 236a214b5c1..abc9afdbeeb 100644 --- a/README.md +++ b/README.md @@ -971,7 +971,7 @@ Good stack traces are only activated in development and debug mode as this results in a significant performance penalty. **_Comparison_**: - + Standard stack trace: ``` From 5b94673fb691f48076af5c76b71bf038a48ba3cf Mon Sep 17 00:00:00 2001 From: Salakar Date: Sun, 9 Feb 2020 04:59:59 +0000 Subject: [PATCH 0807/1748] docs --- README.md | 34 ++++++++++++++++------------------ 1 file changed, 16 insertions(+), 18 deletions(-) diff --git a/README.md b/README.md index abc9afdbeeb..847dd8bd20c 100644 --- a/README.md +++ b/README.md @@ -11,9 +11,9 @@

NPM downloads NPM version - Build Status + Build Status Windows Tests - Coverage Status + Coverage Status Follow on Twitter

@@ -131,11 +131,11 @@ after losing the connection. Listeners are passed an object containing `delay` #### `"error"` `client` will emit `error` when encountering an error connecting to the Redis -server or when any other in node_redis occurs. If you use a command without +server or when any other in Node Redis occurs. If you use a command without callback and encounter a ReplyError it is going to be emitted to the error listener. -So please attach the error listener to node_redis. +So please attach the error listener to Node Redis. #### `"end"` @@ -171,15 +171,15 @@ using unix sockets if possible to increase throughput. | port | 6379 | Port of the Redis server | | path | null | The UNIX socket string of the Redis server | | url | null | The URL of the Redis server. Format: `[redis[s]:]//[[user][:password@]][host][:port][/db-number][?db=db-number[&password=bar[&option=value]]]` (More info avaliable at [IANA](http://www.iana.org/assignments/uri-schemes/prov/redis)). | -| string_numbers | null | Set to `true`, `node_redis` will return Redis number values as Strings instead of javascript Numbers. Useful if you need to handle big numbers (above `Number.MAX_SAFE_INTEGER === 2^53`). Hiredis is incapable of this behavior, so setting this option to `true` will result in the built-in javascript parser being used no matter the value of the `parser` option. | +| string_numbers | null | Set to `true`, Node Redis will return Redis number values as Strings instead of javascript Numbers. Useful if you need to handle big numbers (above `Number.MAX_SAFE_INTEGER === 2^53`). Hiredis is incapable of this behavior, so setting this option to `true` will result in the built-in javascript parser being used no matter the value of the `parser` option. | | return_buffers | false | If set to `true`, then all replies will be sent to callbacks as Buffers instead of Strings. | | detect_buffers | false | If set to `true`, then replies will be sent to callbacks as Buffers. This option lets you switch between Buffers and Strings on a per-command basis, whereas `return_buffers` applies to every command on a client. **Note**: This doesn't work properly with the pubsub mode. A subscriber has to either always return Strings or Buffers. | | socket_keepalive | true | If set to `true`, the keep-alive functionality is enabled on the underlying socket. | | socket_initial_delay | 0 | Initial Delay in milliseconds, and this will also behave the interval keep alive message sending to Redis. | -| no_ready_check | false | When a connection is established to the Redis server, the server might still be loading the database from disk. While loading, the server will not respond to any commands. To work around this, `node_redis` has a "ready check" which sends the `INFO` command to the server. The response from the `INFO` command indicates whether the server is ready for more commands. When ready, `node_redis` emits a `ready` event. Setting `no_ready_check` to `true` will inhibit this check. | +| no_ready_check | false | When a connection is established to the Redis server, the server might still be loading the database from disk. While loading, the server will not respond to any commands. To work around this, Node Redis has a "ready check" which sends the `INFO` command to the server. The response from the `INFO` command indicates whether the server is ready for more commands. When ready, `node_redis` emits a `ready` event. Setting `no_ready_check` to `true` will inhibit this check. | | enable_offline_queue | true | By default, if there is no active connection to the Redis server, commands are added to a queue and are executed once the connection has been established. Setting `enable_offline_queue` to `false` will disable this feature and the callback will be executed immediately with an error, or an error will be emitted if no callback is specified. | | retry_unfulfilled_commands | false | If set to `true`, all commands that were unfulfilled while the connection is lost will be retried after the connection has been reestablished. Use this with caution if you use state altering commands (e.g. `incr`). This is especially useful if you use blocking commands. | -| password | null | If set, client will run Redis auth command on connect. Alias `auth_pass` **Note** `node_redis` < 2.5 must use `auth_pass` | +| password | null | If set, client will run Redis auth command on connect. Alias `auth_pass` **Note** Node Redis < 2.5 must use `auth_pass` | | db | null | If set, client will run Redis `select` command on connect. | | family | IPv4 | You can force using IPv6 if you set the family to 'IPv6'. See Node.js [net](https://nodejs.org/api/net.html) or [dns](https://nodejs.org/api/dns.html) modules on how to use the family type. | | disable_resubscribing | false | If set to `true`, a client won't resubscribe after disconnecting. | @@ -342,14 +342,14 @@ client.set("foo", "bar", "baz", function(err, res) { Every `ReplyError` contains the `command` name in all-caps and the arguments (`args`). -If node_redis emits a library error because of another error, the triggering +If Node Redis emits a library error because of another error, the triggering error is added to the returned error as `origin` attribute. **_Error codes_** -node_redis returns a `NR_CLOSED` error code if the clients connection dropped. +Node Redis returns a `NR_CLOSED` error code if the clients connection dropped. If a command unresolved command got rejected a `UNCERTAIN_STATE` code is -returned. A `CONNECTION_BROKEN` error code is used in case node_redis gives up +returned. A `CONNECTION_BROKEN` error code is used in case Node Redis gives up to reconnect. ### client.unref() @@ -386,9 +386,8 @@ dealing with hash values, there are a couple of useful exceptions to this. #### client.hgetall(hash, callback) -The reply from an `HGETALL` command will be converted into a JavaScript Object by -`node_redis`. That way you can interact with the responses using JavaScript -syntax. +The reply from an `HGETALL` command will be converted into a JavaScript Object. That way you can interact with the +responses using JavaScript syntax. **Example:** @@ -514,7 +513,7 @@ left subscriber mode and no more subscriber events will be emitted. ### client.multi([commands]) `MULTI` commands are queued up until an `EXEC` is issued, and then all commands -are run atomically by Redis. The interface in `node_redis` is to return an +are run atomically by Redis. The interface returns an individual `Multi` object by calling `client.multi()`. If any command fails to queue, all commands are rolled back and none is going to be executed (For further information see the [Redis transactions](http://redis.io/topics/transactions) documentation). @@ -743,7 +742,7 @@ Identical to `.multi()` without transactions. This is recommended if you want to execute many commands at once but don't need to rely on transactions. `BATCH` commands are queued up until an `EXEC` is issued, and then all commands -are run atomically by Redis. The interface in `node_redis` is to return an +are run atomically by Redis. The interface returns an individual `Batch` object by calling `client.batch()`. The only difference between .batch and .multi is that no transaction is going to be used. Be aware that the errors are - just like in multi statements - in the result. @@ -916,8 +915,7 @@ client.zadd(args, function(addError, addResponse) { ## Performance -Much effort has been spent to make `node_redis` as fast as possible for common -operations. +Much effort has been spent to make Node Redis as fast as possible for common operations. ``` Mac mini (2018), i7-3.2GHz and 32gb memory @@ -960,7 +958,7 @@ clients: 1, NodeJS: 12.15.0, Redis: 5.0.6, parser: javascript, connected by: tcp ## Debugging -To get debug output run your `node_redis` application with `NODE_DEBUG=redis`. +To get debug output run your Node Redis application with `NODE_DEBUG=redis`. This is also going to result in good stack traces opposed to useless ones otherwise for any async operation. From d7470f257af1a6aac85d6bb51171b4dbc4f68cc5 Mon Sep 17 00:00:00 2001 From: Salakar Date: Sun, 9 Feb 2020 05:26:34 +0000 Subject: [PATCH 0808/1748] docs: issue template formatting --- .github/ISSUE_TEMPLATE.md | 2 +- .github/PULL_REQUEST_TEMPLATE.md | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/.github/ISSUE_TEMPLATE.md b/.github/ISSUE_TEMPLATE.md index 42f2d3eeb61..c398d32c379 100644 --- a/.github/ISSUE_TEMPLATE.md +++ b/.github/ISSUE_TEMPLATE.md @@ -12,4 +12,4 @@ Thank you!_ * **Platform**: What platform / version? (For example Node.js 0.10 or Node.js 5.7.0 on Windows 7 / Ubuntu 15.10 / Azure) * **Description**: Description of your issue, stack traces from errors and code that reproduces the issue -[gitter]: https://gitter.im/NodeRedis/node_redis?utm_source=badge&utm_medium=badge&utm_campaign=pr-badge \ No newline at end of file +[gitter]: https://gitter.im/NodeRedis/node_redis?utm_source=badge&utm_medium=badge&utm_campaign=pr-badge diff --git a/.github/PULL_REQUEST_TEMPLATE.md b/.github/PULL_REQUEST_TEMPLATE.md index 9706621c1b1..31dc37c8720 100644 --- a/.github/PULL_REQUEST_TEMPLATE.md +++ b/.github/PULL_REQUEST_TEMPLATE.md @@ -11,4 +11,4 @@ afterwards / while the PR is open._ ### Description of change -_Please provide a description of the change here._ \ No newline at end of file +_Please provide a description of the change here._ From e0fddd4deac3a626703b7c7770814baf3ad00e5b Mon Sep 17 00:00:00 2001 From: Salakar Date: Sun, 9 Feb 2020 05:34:48 +0000 Subject: [PATCH 0809/1748] chore: add IDE files to .npmignore --- .npmignore | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/.npmignore b/.npmignore index 6e559f54aa4..262ee0c0b4d 100644 --- a/.npmignore +++ b/.npmignore @@ -11,7 +11,8 @@ coverage/ *.rdb *.out *.yml -CHANGELOG.md +.vscode +.idea CONTRIBUTING.md CODE_OF_CONDUCT.md .travis.yml From 59817914cdad782a220239bcaa99840b5f155680 Mon Sep 17 00:00:00 2001 From: Salakar Date: Sun, 9 Feb 2020 05:42:37 +0000 Subject: [PATCH 0810/1748] chore: formatting --- .travis.yml | 3 +++ CODE_OF_CONDUCT.md | 28 +++++++++++++++++++++------- appveyor.yml | 2 +- 3 files changed, 25 insertions(+), 8 deletions(-) diff --git a/.travis.yml b/.travis.yml index 74f406b15f0..7ff40c58a51 100644 --- a/.travis.yml +++ b/.travis.yml @@ -1,13 +1,16 @@ language: node_js sudo: required + env: - CXX=g++-4.8 TRAVIS=true + addons: apt: sources: - ubuntu-toolchain-r-test packages: - g++-4.8 + node_js: - "6" - "8" diff --git a/CODE_OF_CONDUCT.md b/CODE_OF_CONDUCT.md index 50b0a741084..2adee6acb34 100644 --- a/CODE_OF_CONDUCT.md +++ b/CODE_OF_CONDUCT.md @@ -2,7 +2,10 @@ ## Our Pledge -In the interest of fostering an open and welcoming environment, we as contributors and maintainers pledge to making participation in our project and our community a harassment-free experience for everyone, regardless of age, body size, disability, ethnicity, gender identity and expression, level of experience, nationality, personal appearance, race, religion, or sexual identity and orientation. +In the interest of fostering an open and welcoming environment, we as contributors and maintainers pledge to making +participation in our project and our community a harassment-free experience for everyone, regardless of age, body size, +disability, ethnicity, gender identity and expression, level of experience, nationality, personal appearance, race, +religion, or sexual identity and orientation. ## Our Standards @@ -24,23 +27,34 @@ Examples of unacceptable behavior by participants include: ## Our Responsibilities -Project maintainers are responsible for clarifying the standards of acceptable behavior and are expected to take appropriate and fair corrective action in response to any instances of unacceptable behavior. +Project maintainers are responsible for clarifying the standards of acceptable behavior and are expected to take +appropriate and fair corrective action in response to any instances of unacceptable behavior. -Project maintainers have the right and responsibility to remove, edit, or reject comments, commits, code, wiki edits, issues, and other contributions that are not aligned to this Code of Conduct, or to ban temporarily or permanently any contributor for other behaviors that they deem inappropriate, threatening, offensive, or harmful. +Project maintainers have the right and responsibility to remove, edit, or reject comments, commits, code, wiki edits, + issues, and other contributions that are not aligned to this Code of Conduct, or to ban temporarily or permanently any + contributor for other behaviors that they deem inappropriate, threatening, offensive, or harmful. ## Scope -This Code of Conduct applies both within project spaces and in public spaces when an individual is representing the project or its community. Examples of representing a project or community include using an official project e-mail address, posting via an official social media account, or acting as an appointed representative at an online or offline event. Representation of a project may be further defined and clarified by project maintainers. +This Code of Conduct applies both within project spaces and in public spaces when an individual is representing the +project or its community. Examples of representing a project or community include using an official project e-mail address, +posting via an official social media account, or acting as an appointed representative at an online or offline event. +Representation of a project may be further defined and clarified by project maintainers. ## Enforcement -Instances of abusive, harassing, or otherwise unacceptable behavior may be reported by contacting the project team at `redis @ invertase.io`. The project team will review and investigate all complaints, and will respond in a way that it deems appropriate to the circumstances. The project team is obligated to maintain confidentiality with regard to the reporter of an incident. Further details of specific enforcement policies may be posted separately. +Instances of abusive, harassing, or otherwise unacceptable behavior may be reported by contacting the project team at +`redis[AT]invertase.io`. The project team will review and investigate all complaints, and will respond in a way that it +deems appropriate to the circumstances. The project team is obligated to maintain confidentiality with regard to the +reporter of an incident. Further details of specific enforcement policies may be posted separately. -Project maintainers who do not follow or enforce the Code of Conduct in good faith may face temporary or permanent repercussions as determined by other members of the project's leadership. +Project maintainers who do not follow or enforce the Code of Conduct in good faith may face temporary or permanent +repercussions as determined by other members of the project's leadership. ## Attribution -This Code of Conduct is adapted from the [Contributor Covenant][homepage], version 1.4, available at [http://contributor-covenant.org/version/1/4][version] +This Code of Conduct is adapted from the [Contributor Covenant][homepage], version 1.4, available at +[http://contributor-covenant.org/version/1/4][version] [homepage]: http://contributor-covenant.org [version]: http://contributor-covenant.org/version/1/4/ diff --git a/appveyor.yml b/appveyor.yml index f8c16e9a4de..a67e581b333 100644 --- a/appveyor.yml +++ b/appveyor.yml @@ -17,7 +17,7 @@ shallow_clone: true # Install scripts. (runs after repo cloning) install: - # Install the Redis + # Install Redis - nuget install redis-64 -excludeversion - redis-64\tools\redis-server.exe --service-install - redis-64\tools\redis-server.exe --service-start From 9d68ae76cd4d5d07be4f18f5c8c51054554b7d2b Mon Sep 17 00:00:00 2001 From: Salakar Date: Sun, 9 Feb 2020 05:44:10 +0000 Subject: [PATCH 0811/1748] chore: update license --- LICENSE | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/LICENSE b/LICENSE index 7b7ba1393b4..db86cc4de7f 100644 --- a/LICENSE +++ b/LICENSE @@ -1,6 +1,6 @@ MIT License -Copyright (c) Node Redis contributors. +Copyright (c) 2016-present Node Redis contributors. Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation From a513c8b7e221667801faa070de3323c5d21db2b0 Mon Sep 17 00:00:00 2001 From: Salakar Date: Sun, 9 Feb 2020 05:48:50 +0000 Subject: [PATCH 0812/1748] docs: update readme --- README.md | 10 +++++----- 1 file changed, 5 insertions(+), 5 deletions(-) diff --git a/README.md b/README.md index 847dd8bd20c..0167d5dbd0e 100644 --- a/README.md +++ b/README.md @@ -210,7 +210,7 @@ client.get(new Buffer("foo_rand000000000000"), function(err, reply) { **`retry_strategy` example:** ```js -var client = redis.createClient({ +const client = redis.createClient({ retry_strategy: function(options) { if (options.error && options.error.code === "ECONNREFUSED") { // End reconnecting on a specific error and flush all commands with @@ -678,9 +678,9 @@ clients.watcher.watch("foo", function(watchError) { if (setError) throw err; }); - //using a setTimeout here to ensure that the MULTI/EXEC will come after the SET. - //Normally, you would use a callback to ensure order, but I want the above SET command - //to be easily comment-out-able. + // using a setTimeout here to ensure that the MULTI/EXEC will come after the SET. + // Normally, you would use a callback to ensure order, but I want the above SET command + // to be easily comment-out-able. setTimeout(function() { clients.watcher .multi() @@ -840,7 +840,7 @@ returns it in the callback. If an error occurs in the meanwhile, that is going to return an error instead in the callback. One example of when to use duplicate() would be to accommodate the connection- -blocking redis commands BRPOP, BLPOP, and BRPOPLPUSH. If these commands +blocking redis commands `BRPOP`, `BLPOP`, and `BRPOPLPUSH`. If these commands are used on the same Redis client instance as non-blocking commands, the non-blocking ones may be queued up until after the blocking ones finish. From 79927c56a0e2fb7819afbb0e13c6078d0015564e Mon Sep 17 00:00:00 2001 From: Salakar Date: Sun, 9 Feb 2020 06:08:30 +0000 Subject: [PATCH 0813/1748] tests: remove unused `parser` client option from all tests & helpers --- test/auth.spec.js | 19 +++++++++---------- test/batch.spec.js | 4 ++-- test/commands/blpop.spec.js | 4 ++-- test/commands/client.spec.js | 4 ++-- test/commands/dbsize.spec.js | 4 ++-- test/commands/del.spec.js | 4 ++-- test/commands/eval.spec.js | 4 ++-- test/commands/exists.spec.js | 4 ++-- test/commands/expire.spec.js | 4 ++-- test/commands/flushdb.spec.js | 4 ++-- test/commands/geoadd.spec.js | 4 ++-- test/commands/get.spec.js | 4 ++-- test/commands/getset.spec.js | 4 ++-- test/commands/hgetall.spec.js | 6 +++--- test/commands/hincrby.spec.js | 4 ++-- test/commands/hlen.spec.js | 4 ++-- test/commands/hmget.spec.js | 4 ++-- test/commands/hmset.spec.js | 4 ++-- test/commands/hset.spec.js | 4 ++-- test/commands/incr.spec.js | 4 ++-- test/commands/info.spec.js | 4 ++-- test/commands/keys.spec.js | 4 ++-- test/commands/mget.spec.js | 4 ++-- test/commands/monitor.spec.js | 4 ++-- test/commands/mset.spec.js | 4 ++-- test/commands/msetnx.spec.js | 4 ++-- test/commands/randomkey.test.js | 4 ++-- test/commands/rename.spec.js | 4 ++-- test/commands/renamenx.spec.js | 4 ++-- test/commands/rpush.spec.js | 4 ++-- test/commands/sadd.spec.js | 4 ++-- test/commands/scard.spec.js | 4 ++-- test/commands/script.spec.js | 4 ++-- test/commands/sdiff.spec.js | 4 ++-- test/commands/sdiffstore.spec.js | 4 ++-- test/commands/select.spec.js | 4 ++-- test/commands/set.spec.js | 4 ++-- test/commands/setex.spec.js | 4 ++-- test/commands/setnx.spec.js | 4 ++-- test/commands/sinter.spec.js | 4 ++-- test/commands/sinterstore.spec.js | 4 ++-- test/commands/sismember.spec.js | 4 ++-- test/commands/slowlog.spec.js | 4 ++-- test/commands/smembers.spec.js | 4 ++-- test/commands/smove.spec.js | 4 ++-- test/commands/sort.spec.js | 4 ++-- test/commands/spop.spec.js | 4 ++-- test/commands/srem.spec.js | 4 ++-- test/commands/sunion.spec.js | 4 ++-- test/commands/sunionstore.spec.js | 4 ++-- test/commands/ttl.spec.js | 4 ++-- test/commands/type.spec.js | 4 ++-- test/commands/watch.spec.js | 4 ++-- test/commands/zadd.spec.js | 4 ++-- test/commands/zscan.spec.js | 4 ++-- test/commands/zscore.spec.js | 4 ++-- test/connection.spec.js | 19 ++++++------------- test/detect_buffers.spec.js | 2 +- test/helper.js | 13 +++++-------- test/lib/config.js | 4 +--- test/multi.spec.js | 4 ++-- test/node_redis.spec.js | 15 ++++----------- test/prefix.spec.js | 5 ++--- test/pubsub.spec.js | 4 ++-- test/rename.spec.js | 6 ++---- test/return_buffers.spec.js | 8 ++++---- 66 files changed, 149 insertions(+), 172 deletions(-) diff --git a/test/auth.spec.js b/test/auth.spec.js index 67e6d0924a1..d88ac6304e3 100644 --- a/test/auth.spec.js +++ b/test/auth.spec.js @@ -19,9 +19,9 @@ describe('client authentication', function () { helper.allTests({ allConnections: true - }, function (parser, ip, args) { + }, function (ip, args) { - describe('using ' + parser + ' and ' + ip, function () { + describe('using ' + ip, function () { var auth = 'porkchopsandwiches'; var client = null; @@ -133,7 +133,7 @@ describe('client authentication', function () { it('allows auth to be provided as config option for client', function (done) { if (helper.redisProcess().spawnFailed()) this.skip(); - var args = config.configureClient(parser, ip, { + var args = config.configureClient(ip, { auth_pass: auth }); client = redis.createClient.apply(null, args); @@ -143,7 +143,7 @@ describe('client authentication', function () { it('allows auth and no_ready_check to be provided as config option for client', function (done) { if (helper.redisProcess().spawnFailed()) this.skip(); - var args = config.configureClient(parser, ip, { + var args = config.configureClient(ip, { password: auth, no_ready_check: true }); @@ -154,7 +154,7 @@ describe('client authentication', function () { it('allows auth to be provided post-hoc with auth method', function (done) { if (helper.redisProcess().spawnFailed()) this.skip(); - var args = config.configureClient(parser, ip); + var args = config.configureClient(ip); client = redis.createClient.apply(null, args); client.auth(auth); client.on('ready', done); @@ -217,7 +217,7 @@ describe('client authentication', function () { it('allows auth to be provided post-hoc with auth method again', function (done) { if (helper.redisProcess().spawnFailed()) this.skip(); - var args = config.configureClient(parser, ip, { + var args = config.configureClient(ip, { auth_pass: auth }); client = redis.createClient.apply(null, args); @@ -229,7 +229,7 @@ describe('client authentication', function () { it('does not allow any commands to be processed if not authenticated using no_ready_check true', function (done) { if (helper.redisProcess().spawnFailed()) this.skip(); - var args = config.configureClient(parser, ip, { + var args = config.configureClient(ip, { no_ready_check: true }); client = redis.createClient.apply(null, args); @@ -258,7 +258,6 @@ describe('client authentication', function () { if (helper.redisProcess().spawnFailed()) this.skip(); client = redis.createClient({ password: 'wrong_password', - parser: parser }); client.once('error', function (err) { assert.strictEqual(err.message, 'ERR invalid password'); @@ -269,7 +268,7 @@ describe('client authentication', function () { it('pubsub working with auth', function (done) { if (helper.redisProcess().spawnFailed()) this.skip(); - var args = config.configureClient(parser, ip, { + var args = config.configureClient(ip, { password: auth }); client = redis.createClient.apply(null, args); @@ -299,7 +298,7 @@ describe('client authentication', function () { // returning the manipulated [error, result] from the callback. // There should be a better solution though - var args = config.configureClient(parser, 'localhost', { + var args = config.configureClient('localhost', { noReadyCheck: true }); client = redis.createClient.apply(null, args); diff --git a/test/batch.spec.js b/test/batch.spec.js index df038e77e6a..05cee80681a 100644 --- a/test/batch.spec.js +++ b/test/batch.spec.js @@ -7,9 +7,9 @@ var redis = config.redis; describe("The 'batch' method", function () { - helper.allTests(function (parser, ip, args) { + helper.allTests(function (ip, args) { - describe('using ' + parser + ' and ' + ip, function () { + describe('using ' + ip, function () { describe('when not connected', function () { var client; diff --git a/test/commands/blpop.spec.js b/test/commands/blpop.spec.js index e8d3f59aabc..64aedf81eae 100644 --- a/test/commands/blpop.spec.js +++ b/test/commands/blpop.spec.js @@ -8,9 +8,9 @@ var intercept = require('intercept-stdout'); describe("The 'blpop' method", function () { - helper.allTests(function (parser, ip, args) { + helper.allTests(function (ip, args) { - describe('using ' + parser + ' and ' + ip, function () { + describe('using ' + ip, function () { var client; var bclient; diff --git a/test/commands/client.spec.js b/test/commands/client.spec.js index 7ac32ae41ac..51a55e3f16b 100644 --- a/test/commands/client.spec.js +++ b/test/commands/client.spec.js @@ -7,10 +7,10 @@ var redis = config.redis; describe("The 'client' method", function () { - helper.allTests(function (parser, ip, args) { + helper.allTests(function (ip, args) { var pattern = /addr=/; - describe('using ' + parser + ' and ' + ip, function () { + describe('using ' + ip, function () { var client; beforeEach(function (done) { diff --git a/test/commands/dbsize.spec.js b/test/commands/dbsize.spec.js index 4fdf80010d1..bd8b1467898 100644 --- a/test/commands/dbsize.spec.js +++ b/test/commands/dbsize.spec.js @@ -8,9 +8,9 @@ var uuid = require('uuid'); describe("The 'dbsize' method", function () { - helper.allTests(function (parser, ip, args) { + helper.allTests(function (ip, args) { - describe('using ' + parser + ' and ' + ip, function () { + describe('using ' + ip, function () { var key, value; beforeEach(function () { diff --git a/test/commands/del.spec.js b/test/commands/del.spec.js index 970d0886c43..86c1f4bb3af 100644 --- a/test/commands/del.spec.js +++ b/test/commands/del.spec.js @@ -6,9 +6,9 @@ var redis = config.redis; describe("The 'del' method", function () { - helper.allTests(function (parser, ip, args) { + helper.allTests(function (ip, args) { - describe('using ' + parser + ' and ' + ip, function () { + describe('using ' + ip, function () { var client; beforeEach(function (done) { diff --git a/test/commands/eval.spec.js b/test/commands/eval.spec.js index 731d656d541..db74372db4d 100644 --- a/test/commands/eval.spec.js +++ b/test/commands/eval.spec.js @@ -8,9 +8,9 @@ var redis = config.redis; describe("The 'eval' method", function () { - helper.allTests(function (parser, ip, args) { + helper.allTests(function (ip, args) { - describe('using ' + parser + ' and ' + ip, function () { + describe('using ' + ip, function () { var client; var source = "return redis.call('set', 'sha', 'test')"; diff --git a/test/commands/exists.spec.js b/test/commands/exists.spec.js index 01a1b6891d9..399a0382f49 100644 --- a/test/commands/exists.spec.js +++ b/test/commands/exists.spec.js @@ -6,9 +6,9 @@ var redis = config.redis; describe("The 'exists' method", function () { - helper.allTests(function (parser, ip, args) { + helper.allTests(function (ip, args) { - describe('using ' + parser + ' and ' + ip, function () { + describe('using ' + ip, function () { var client; beforeEach(function (done) { diff --git a/test/commands/expire.spec.js b/test/commands/expire.spec.js index f9041b71091..2891890edc0 100644 --- a/test/commands/expire.spec.js +++ b/test/commands/expire.spec.js @@ -6,9 +6,9 @@ var redis = config.redis; describe("The 'expire' method", function () { - helper.allTests(function (parser, ip, args) { + helper.allTests(function (ip, args) { - describe('using ' + parser + ' and ' + ip, function () { + describe('using ' + ip, function () { var client; beforeEach(function (done) { diff --git a/test/commands/flushdb.spec.js b/test/commands/flushdb.spec.js index 61535e2e714..a4f761d3753 100644 --- a/test/commands/flushdb.spec.js +++ b/test/commands/flushdb.spec.js @@ -8,9 +8,9 @@ var uuid = require('uuid'); describe("The 'flushdb' method", function () { - helper.allTests(function (parser, ip, args) { + helper.allTests(function (ip, args) { - describe('using ' + parser + ' and ' + ip, function () { + describe('using ' + ip, function () { var key, key2; beforeEach(function () { diff --git a/test/commands/geoadd.spec.js b/test/commands/geoadd.spec.js index 3614910322d..b45df7c83a9 100644 --- a/test/commands/geoadd.spec.js +++ b/test/commands/geoadd.spec.js @@ -6,9 +6,9 @@ var redis = config.redis; describe("The 'geoadd' method", function () { - helper.allTests(function (parser, ip, args) { + helper.allTests(function (ip, args) { - describe('using ' + parser + ' and ' + ip, function () { + describe('using ' + ip, function () { var client; beforeEach(function (done) { diff --git a/test/commands/get.spec.js b/test/commands/get.spec.js index e2b9a7db071..acbfc0d10db 100644 --- a/test/commands/get.spec.js +++ b/test/commands/get.spec.js @@ -8,9 +8,9 @@ var uuid = require('uuid'); describe("The 'get' method", function () { - helper.allTests(function (parser, ip, args) { + helper.allTests(function (ip, args) { - describe('using ' + parser + ' and ' + ip, function () { + describe('using ' + ip, function () { var key, value; beforeEach(function () { diff --git a/test/commands/getset.spec.js b/test/commands/getset.spec.js index e5da8573119..48dd7e9d739 100644 --- a/test/commands/getset.spec.js +++ b/test/commands/getset.spec.js @@ -8,9 +8,9 @@ var uuid = require('uuid'); describe("The 'getset' method", function () { - helper.allTests(function (parser, ip, args) { + helper.allTests(function (ip, args) { - describe('using ' + parser + ' and ' + ip, function () { + describe('using ' + ip, function () { var key, value, value2; beforeEach(function () { diff --git a/test/commands/hgetall.spec.js b/test/commands/hgetall.spec.js index 6c00e3ed099..b74995427a1 100644 --- a/test/commands/hgetall.spec.js +++ b/test/commands/hgetall.spec.js @@ -7,9 +7,9 @@ var redis = config.redis; describe("The 'hgetall' method", function () { - helper.allTests(function (parser, ip, args) { + helper.allTests(function (ip, args) { - describe('using ' + parser + ' and ' + ip, function () { + describe('using ' + ip, function () { var client; describe('regular client', function () { @@ -51,7 +51,7 @@ describe("The 'hgetall' method", function () { describe('binary client', function () { var client; - var args = config.configureClient(parser, ip, { + var args = config.configureClient(ip, { return_buffers: true }); diff --git a/test/commands/hincrby.spec.js b/test/commands/hincrby.spec.js index e94232d9a0e..10b4523b3f7 100644 --- a/test/commands/hincrby.spec.js +++ b/test/commands/hincrby.spec.js @@ -6,9 +6,9 @@ var redis = config.redis; describe("The 'hincrby' method", function () { - helper.allTests(function (parser, ip, args) { + helper.allTests(function (ip, args) { - describe('using ' + parser + ' and ' + ip, function () { + describe('using ' + ip, function () { var client; var hash = 'test hash'; diff --git a/test/commands/hlen.spec.js b/test/commands/hlen.spec.js index 5dcab6ce8e2..f1f94fec025 100644 --- a/test/commands/hlen.spec.js +++ b/test/commands/hlen.spec.js @@ -6,9 +6,9 @@ var redis = config.redis; describe("The 'hlen' method", function () { - helper.allTests(function (parser, ip, args) { + helper.allTests(function (ip, args) { - describe('using ' + parser + ' and ' + ip, function () { + describe('using ' + ip, function () { var client; beforeEach(function (done) { diff --git a/test/commands/hmget.spec.js b/test/commands/hmget.spec.js index 09bb89f258e..3676b5b7312 100644 --- a/test/commands/hmget.spec.js +++ b/test/commands/hmget.spec.js @@ -7,9 +7,9 @@ var redis = config.redis; describe("The 'hmget' method", function () { - helper.allTests(function (parser, ip, args) { + helper.allTests(function (ip, args) { - describe('using ' + parser + ' and ' + ip, function () { + describe('using ' + ip, function () { var client; var hash = 'test hash'; diff --git a/test/commands/hmset.spec.js b/test/commands/hmset.spec.js index 93514cc2295..8ba54ecc3f2 100644 --- a/test/commands/hmset.spec.js +++ b/test/commands/hmset.spec.js @@ -7,9 +7,9 @@ var redis = config.redis; describe("The 'hmset' method", function () { - helper.allTests(function (parser, ip, args) { + helper.allTests(function (ip, args) { - describe('using ' + parser + ' and ' + ip, function () { + describe('using ' + ip, function () { var client; var hash = 'test hash'; diff --git a/test/commands/hset.spec.js b/test/commands/hset.spec.js index c62b17713b0..d94a7aa0f2b 100644 --- a/test/commands/hset.spec.js +++ b/test/commands/hset.spec.js @@ -7,9 +7,9 @@ var redis = config.redis; describe("The 'hset' method", function () { - helper.allTests(function (parser, ip, args) { + helper.allTests(function (ip, args) { - describe('using ' + parser + ' and ' + ip, function () { + describe('using ' + ip, function () { var client; var hash = 'test hash'; diff --git a/test/commands/incr.spec.js b/test/commands/incr.spec.js index f1f1e78ee8c..0caab84859a 100644 --- a/test/commands/incr.spec.js +++ b/test/commands/incr.spec.js @@ -7,9 +7,9 @@ var redis = config.redis; describe("The 'incr' method", function () { - helper.allTests(function (parser, ip, args) { + helper.allTests(function (ip, args) { - describe('using ' + parser + ' and ' + ip, function () { + describe('using ' + ip, function () { describe('when connected and a value in Redis', function () { diff --git a/test/commands/info.spec.js b/test/commands/info.spec.js index 39a9e9f5cce..4e5bb481fc9 100644 --- a/test/commands/info.spec.js +++ b/test/commands/info.spec.js @@ -7,9 +7,9 @@ var redis = config.redis; describe("The 'info' method", function () { - helper.allTests(function (parser, ip, args) { + helper.allTests(function (ip, args) { - describe('using ' + parser + ' and ' + ip, function () { + describe('using ' + ip, function () { var client; beforeEach(function (done) { diff --git a/test/commands/keys.spec.js b/test/commands/keys.spec.js index 1aff0cea146..6ce45790b6d 100644 --- a/test/commands/keys.spec.js +++ b/test/commands/keys.spec.js @@ -8,9 +8,9 @@ var redis = config.redis; describe("The 'keys' method", function () { - helper.allTests(function (parser, ip, args) { + helper.allTests(function (ip, args) { - describe('using ' + parser + ' and ' + ip, function () { + describe('using ' + ip, function () { var client; beforeEach(function (done) { diff --git a/test/commands/mget.spec.js b/test/commands/mget.spec.js index 83d69f837f9..a2c671f683c 100644 --- a/test/commands/mget.spec.js +++ b/test/commands/mget.spec.js @@ -7,9 +7,9 @@ var redis = config.redis; describe("The 'mget' method", function () { - helper.allTests(function (parser, ip, args) { + helper.allTests(function (ip, args) { - describe('using ' + parser + ' and ' + ip, function () { + describe('using ' + ip, function () { var client; beforeEach(function (done) { diff --git a/test/commands/monitor.spec.js b/test/commands/monitor.spec.js index a6c98747309..7465ac4eb84 100644 --- a/test/commands/monitor.spec.js +++ b/test/commands/monitor.spec.js @@ -8,7 +8,7 @@ var redis = config.redis; describe("The 'monitor' method", function () { - helper.allTests(function (parser, ip, args) { + helper.allTests(function (ip, args) { var client; @@ -62,7 +62,7 @@ describe("The 'monitor' method", function () { end(err); }); monitorClient.subscribe('foo', 'baz', function (err, res) { - // The return value might change in v.3 + // The return value might change in v.4 // assert.strictEqual(res, 'baz'); // TODO: Fix the return value of subscribe calls end(err); diff --git a/test/commands/mset.spec.js b/test/commands/mset.spec.js index 9fac90728cf..b60f383134b 100644 --- a/test/commands/mset.spec.js +++ b/test/commands/mset.spec.js @@ -8,9 +8,9 @@ var uuid = require('uuid'); describe("The 'mset' method", function () { - helper.allTests(function (parser, ip, args) { + helper.allTests(function (ip, args) { - describe('using ' + parser + ' and ' + ip, function () { + describe('using ' + ip, function () { var key, value, key2, value2; beforeEach(function () { diff --git a/test/commands/msetnx.spec.js b/test/commands/msetnx.spec.js index a2f2ab17249..179f33744e6 100644 --- a/test/commands/msetnx.spec.js +++ b/test/commands/msetnx.spec.js @@ -6,9 +6,9 @@ var redis = config.redis; describe("The 'msetnx' method", function () { - helper.allTests(function (parser, ip, args) { + helper.allTests(function (ip, args) { - describe('using ' + parser + ' and ' + ip, function () { + describe('using ' + ip, function () { var client; beforeEach(function (done) { diff --git a/test/commands/randomkey.test.js b/test/commands/randomkey.test.js index cf453e12e4b..226194f9214 100644 --- a/test/commands/randomkey.test.js +++ b/test/commands/randomkey.test.js @@ -7,9 +7,9 @@ var redis = config.redis; describe("The 'randomkey' method", function () { - helper.allTests(function (parser, ip, args) { + helper.allTests(function (ip, args) { - describe('using ' + parser + ' and ' + ip, function () { + describe('using ' + ip, function () { var client; beforeEach(function (done) { diff --git a/test/commands/rename.spec.js b/test/commands/rename.spec.js index cb3d7c31688..284fba310ed 100644 --- a/test/commands/rename.spec.js +++ b/test/commands/rename.spec.js @@ -6,9 +6,9 @@ var redis = config.redis; describe("The 'rename' method", function () { - helper.allTests(function (parser, ip, args) { + helper.allTests(function (ip, args) { - describe('using ' + parser + ' and ' + ip, function () { + describe('using ' + ip, function () { var client; beforeEach(function (done) { diff --git a/test/commands/renamenx.spec.js b/test/commands/renamenx.spec.js index 3da640a0d8d..b56b0a1a5c9 100644 --- a/test/commands/renamenx.spec.js +++ b/test/commands/renamenx.spec.js @@ -6,9 +6,9 @@ var redis = config.redis; describe("The 'renamenx' method", function () { - helper.allTests(function (parser, ip, args) { + helper.allTests(function (ip, args) { - describe('using ' + parser + ' and ' + ip, function () { + describe('using ' + ip, function () { var client; beforeEach(function (done) { diff --git a/test/commands/rpush.spec.js b/test/commands/rpush.spec.js index d137dc6473d..793d5d2d804 100644 --- a/test/commands/rpush.spec.js +++ b/test/commands/rpush.spec.js @@ -7,9 +7,9 @@ var assert = require('assert'); describe("The 'rpush' command", function () { - helper.allTests(function (parser, ip, args) { + helper.allTests(function (ip, args) { - describe('using ' + parser + ' and ' + ip, function () { + describe('using ' + ip, function () { var client; beforeEach(function (done) { diff --git a/test/commands/sadd.spec.js b/test/commands/sadd.spec.js index c2316739b76..442f391b9de 100644 --- a/test/commands/sadd.spec.js +++ b/test/commands/sadd.spec.js @@ -7,9 +7,9 @@ var redis = config.redis; describe("The 'sadd' method", function () { - helper.allTests(function (parser, ip, args) { + helper.allTests(function (ip, args) { - describe('using ' + parser + ' and ' + ip, function () { + describe('using ' + ip, function () { var client; beforeEach(function (done) { diff --git a/test/commands/scard.spec.js b/test/commands/scard.spec.js index 5cf9b4eb73c..e327eb282a2 100644 --- a/test/commands/scard.spec.js +++ b/test/commands/scard.spec.js @@ -6,9 +6,9 @@ var redis = config.redis; describe("The 'scard' method", function () { - helper.allTests(function (parser, ip, args) { + helper.allTests(function (ip, args) { - describe('using ' + parser + ' and ' + ip, function () { + describe('using ' + ip, function () { var client; beforeEach(function (done) { diff --git a/test/commands/script.spec.js b/test/commands/script.spec.js index 2e29ad553ab..c374f5b5e17 100644 --- a/test/commands/script.spec.js +++ b/test/commands/script.spec.js @@ -8,11 +8,11 @@ var redis = config.redis; describe("The 'script' method", function () { - helper.allTests(function (parser, ip, args) { + helper.allTests(function (ip, args) { var command = 'return 99'; var commandSha = crypto.createHash('sha1').update(command).digest('hex'); - describe('using ' + parser + ' and ' + ip, function () { + describe('using ' + ip, function () { var client; beforeEach(function (done) { diff --git a/test/commands/sdiff.spec.js b/test/commands/sdiff.spec.js index e3963ecdf0c..95f81f09bd0 100644 --- a/test/commands/sdiff.spec.js +++ b/test/commands/sdiff.spec.js @@ -7,9 +7,9 @@ var redis = config.redis; describe("The 'sdiff' method", function () { - helper.allTests(function (parser, ip, args) { + helper.allTests(function (ip, args) { - describe('using ' + parser + ' and ' + ip, function () { + describe('using ' + ip, function () { var client; beforeEach(function (done) { diff --git a/test/commands/sdiffstore.spec.js b/test/commands/sdiffstore.spec.js index 1797ca9e435..fe822b561b5 100644 --- a/test/commands/sdiffstore.spec.js +++ b/test/commands/sdiffstore.spec.js @@ -7,9 +7,9 @@ var redis = config.redis; describe("The 'sdiffstore' method", function () { - helper.allTests(function (parser, ip, args) { + helper.allTests(function (ip, args) { - describe('using ' + parser + ' and ' + ip, function () { + describe('using ' + ip, function () { var client; beforeEach(function (done) { diff --git a/test/commands/select.spec.js b/test/commands/select.spec.js index 66a66f389b0..053496e337f 100644 --- a/test/commands/select.spec.js +++ b/test/commands/select.spec.js @@ -7,9 +7,9 @@ var redis = config.redis; describe("The 'select' method", function () { - helper.allTests(function (parser, ip, args) { + helper.allTests(function (ip, args) { - describe('using ' + parser + ' and ' + ip, function () { + describe('using ' + ip, function () { describe('when not connected', function () { var client; diff --git a/test/commands/set.spec.js b/test/commands/set.spec.js index 360cffe5f59..d2d36491a96 100644 --- a/test/commands/set.spec.js +++ b/test/commands/set.spec.js @@ -8,9 +8,9 @@ var uuid = require('uuid'); describe("The 'set' method", function () { - helper.allTests(function (parser, ip, args) { + helper.allTests(function (ip, args) { - describe('using ' + parser + ' and ' + ip, function () { + describe('using ' + ip, function () { var key, value; beforeEach(function () { diff --git a/test/commands/setex.spec.js b/test/commands/setex.spec.js index 9a3bb53f0b1..a2126e6dbb6 100644 --- a/test/commands/setex.spec.js +++ b/test/commands/setex.spec.js @@ -7,9 +7,9 @@ var redis = config.redis; describe("The 'setex' method", function () { - helper.allTests(function (parser, ip, args) { + helper.allTests(function (ip, args) { - describe('using ' + parser + ' and ' + ip, function () { + describe('using ' + ip, function () { var client; beforeEach(function (done) { diff --git a/test/commands/setnx.spec.js b/test/commands/setnx.spec.js index 2bce9f0a0ba..4b4688c0a68 100644 --- a/test/commands/setnx.spec.js +++ b/test/commands/setnx.spec.js @@ -6,9 +6,9 @@ var redis = config.redis; describe("The 'setnx' method", function () { - helper.allTests(function (parser, ip, args) { + helper.allTests(function (ip, args) { - describe('using ' + parser + ' and ' + ip, function () { + describe('using ' + ip, function () { var client; beforeEach(function (done) { diff --git a/test/commands/sinter.spec.js b/test/commands/sinter.spec.js index b614a41ea4c..c4fc7759556 100644 --- a/test/commands/sinter.spec.js +++ b/test/commands/sinter.spec.js @@ -7,9 +7,9 @@ var redis = config.redis; describe("The 'sinter' method", function () { - helper.allTests(function (parser, ip, args) { + helper.allTests(function (ip, args) { - describe('using ' + parser + ' and ' + ip, function () { + describe('using ' + ip, function () { var client; beforeEach(function (done) { diff --git a/test/commands/sinterstore.spec.js b/test/commands/sinterstore.spec.js index de7de87e10f..1ea4c4b109b 100644 --- a/test/commands/sinterstore.spec.js +++ b/test/commands/sinterstore.spec.js @@ -7,9 +7,9 @@ var redis = config.redis; describe("The 'sinterstore' method", function () { - helper.allTests(function (parser, ip, args) { + helper.allTests(function (ip, args) { - describe('using ' + parser + ' and ' + ip, function () { + describe('using ' + ip, function () { var client; beforeEach(function (done) { diff --git a/test/commands/sismember.spec.js b/test/commands/sismember.spec.js index 69482b3b564..37ac1c466aa 100644 --- a/test/commands/sismember.spec.js +++ b/test/commands/sismember.spec.js @@ -6,9 +6,9 @@ var redis = config.redis; describe("The 'sismember' method", function () { - helper.allTests(function (parser, ip, args) { + helper.allTests(function (ip, args) { - describe('using ' + parser + ' and ' + ip, function () { + describe('using ' + ip, function () { var client; beforeEach(function (done) { diff --git a/test/commands/slowlog.spec.js b/test/commands/slowlog.spec.js index 7f5417dbd8f..21f3f8007ac 100644 --- a/test/commands/slowlog.spec.js +++ b/test/commands/slowlog.spec.js @@ -7,9 +7,9 @@ var redis = config.redis; describe("The 'slowlog' method", function () { - helper.allTests(function (parser, ip, args) { + helper.allTests(function (ip, args) { - describe('using ' + parser + ' and ' + ip, function () { + describe('using ' + ip, function () { var client; beforeEach(function (done) { diff --git a/test/commands/smembers.spec.js b/test/commands/smembers.spec.js index 8fbdcbba5ed..0bc8143719f 100644 --- a/test/commands/smembers.spec.js +++ b/test/commands/smembers.spec.js @@ -7,9 +7,9 @@ var redis = config.redis; describe("The 'smembers' method", function () { - helper.allTests(function (parser, ip, args) { + helper.allTests(function (ip, args) { - describe('using ' + parser + ' and ' + ip, function () { + describe('using ' + ip, function () { var client; beforeEach(function (done) { diff --git a/test/commands/smove.spec.js b/test/commands/smove.spec.js index eb8eae10545..969c264b756 100644 --- a/test/commands/smove.spec.js +++ b/test/commands/smove.spec.js @@ -6,9 +6,9 @@ var redis = config.redis; describe("The 'smove' method", function () { - helper.allTests(function (parser, ip, args) { + helper.allTests(function (ip, args) { - describe('using ' + parser + ' and ' + ip, function () { + describe('using ' + ip, function () { var client; beforeEach(function (done) { diff --git a/test/commands/sort.spec.js b/test/commands/sort.spec.js index 63b933eaef6..2ee08c44e21 100644 --- a/test/commands/sort.spec.js +++ b/test/commands/sort.spec.js @@ -34,9 +34,9 @@ function setupData (client, done) { describe("The 'sort' method", function () { - helper.allTests(function (parser, ip, args) { + helper.allTests(function (ip, args) { - describe('using ' + parser + ' and ' + ip, function () { + describe('using ' + ip, function () { var client; beforeEach(function (done) { diff --git a/test/commands/spop.spec.js b/test/commands/spop.spec.js index 6e0697c02a5..ec3e93fda3f 100644 --- a/test/commands/spop.spec.js +++ b/test/commands/spop.spec.js @@ -7,9 +7,9 @@ var redis = config.redis; describe("The 'spop' method", function () { - helper.allTests(function (parser, ip, args) { + helper.allTests(function (ip, args) { - describe('using ' + parser + ' and ' + ip, function () { + describe('using ' + ip, function () { var client; beforeEach(function (done) { diff --git a/test/commands/srem.spec.js b/test/commands/srem.spec.js index 97c40d9ce2d..d325cb57151 100644 --- a/test/commands/srem.spec.js +++ b/test/commands/srem.spec.js @@ -7,9 +7,9 @@ var redis = config.redis; describe("The 'srem' method", function () { - helper.allTests(function (parser, ip, args) { + helper.allTests(function (ip, args) { - describe('using ' + parser + ' and ' + ip, function () { + describe('using ' + ip, function () { var client; beforeEach(function (done) { diff --git a/test/commands/sunion.spec.js b/test/commands/sunion.spec.js index 44f5f247206..cc8eb624758 100644 --- a/test/commands/sunion.spec.js +++ b/test/commands/sunion.spec.js @@ -7,9 +7,9 @@ var redis = config.redis; describe("The 'sunion' method", function () { - helper.allTests(function (parser, ip, args) { + helper.allTests(function (ip, args) { - describe('using ' + parser + ' and ' + ip, function () { + describe('using ' + ip, function () { var client; beforeEach(function (done) { diff --git a/test/commands/sunionstore.spec.js b/test/commands/sunionstore.spec.js index c9869bdf881..bd64c6f6b79 100644 --- a/test/commands/sunionstore.spec.js +++ b/test/commands/sunionstore.spec.js @@ -7,9 +7,9 @@ var redis = config.redis; describe("The 'sunionstore' method", function () { - helper.allTests(function (parser, ip, args) { + helper.allTests(function (ip, args) { - describe('using ' + parser + ' and ' + ip, function () { + describe('using ' + ip, function () { var client; beforeEach(function (done) { diff --git a/test/commands/ttl.spec.js b/test/commands/ttl.spec.js index 65212f710c2..e176d41cb84 100644 --- a/test/commands/ttl.spec.js +++ b/test/commands/ttl.spec.js @@ -7,9 +7,9 @@ var redis = config.redis; describe("The 'ttl' method", function () { - helper.allTests(function (parser, ip, args) { + helper.allTests(function (ip, args) { - describe('using ' + parser + ' and ' + ip, function () { + describe('using ' + ip, function () { var client; beforeEach(function (done) { diff --git a/test/commands/type.spec.js b/test/commands/type.spec.js index 4a0c4d2897c..f70d79e9ef6 100644 --- a/test/commands/type.spec.js +++ b/test/commands/type.spec.js @@ -6,9 +6,9 @@ var redis = config.redis; describe("The 'type' method", function () { - helper.allTests(function (parser, ip, args) { + helper.allTests(function (ip, args) { - describe('using ' + parser + ' and ' + ip, function () { + describe('using ' + ip, function () { var client; beforeEach(function (done) { diff --git a/test/commands/watch.spec.js b/test/commands/watch.spec.js index cf908646750..52a9b26f751 100644 --- a/test/commands/watch.spec.js +++ b/test/commands/watch.spec.js @@ -7,11 +7,11 @@ var redis = config.redis; describe("The 'watch' method", function () { - helper.allTests(function (parser, ip, args) { + helper.allTests(function (ip, args) { var watched = 'foobar'; - describe('using ' + parser + ' and ' + ip, function () { + describe('using ' + ip, function () { var client; beforeEach(function (done) { diff --git a/test/commands/zadd.spec.js b/test/commands/zadd.spec.js index e0153ba11bb..827630a389c 100644 --- a/test/commands/zadd.spec.js +++ b/test/commands/zadd.spec.js @@ -7,9 +7,9 @@ var redis = config.redis; describe("The 'zadd' method", function () { - helper.allTests(function (parser, ip, args) { + helper.allTests(function (ip, args) { - describe('using ' + parser + ' and ' + ip, function () { + describe('using ' + ip, function () { var client; beforeEach(function (done) { diff --git a/test/commands/zscan.spec.js b/test/commands/zscan.spec.js index 6d4a1a60c8a..eb8acf44dbf 100644 --- a/test/commands/zscan.spec.js +++ b/test/commands/zscan.spec.js @@ -7,9 +7,9 @@ var redis = config.redis; describe("The 'zscan' method", function () { - helper.allTests(function (parser, ip, args) { + helper.allTests(function (ip, args) { - describe('using ' + parser + ' and ' + ip, function () { + describe('using ' + ip, function () { var client; beforeEach(function (done) { diff --git a/test/commands/zscore.spec.js b/test/commands/zscore.spec.js index 9d24d7a7cf0..8b95e527641 100644 --- a/test/commands/zscore.spec.js +++ b/test/commands/zscore.spec.js @@ -7,9 +7,9 @@ var redis = config.redis; describe("The 'zscore' method", function () { - helper.allTests(function (parser, ip, args) { + helper.allTests(function (ip, args) { - describe('using ' + parser + ' and ' + ip, function () { + describe('using ' + ip, function () { var client; beforeEach(function (done) { diff --git a/test/connection.spec.js b/test/connection.spec.js index 7ecee698530..0913a26ff6b 100644 --- a/test/connection.spec.js +++ b/test/connection.spec.js @@ -147,9 +147,9 @@ describe('connection tests', function () { }); - helper.allTests(function (parser, ip, args) { + helper.allTests(function (ip, args) { - describe('using ' + parser + ' and ' + ip, function () { + describe('using ' + ip, function () { describe('on lost connection', function () { it('emit an error after max retry timeout and do not try to reconnect afterwards', function (done) { @@ -158,7 +158,6 @@ describe('connection tests', function () { var connect_timeout = 600; // in ms client = redis.createClient({ - parser: parser, connect_timeout: connect_timeout }); var time = 0; @@ -189,7 +188,6 @@ describe('connection tests', function () { it('end connection while retry is still ongoing', function (done) { var connect_timeout = 1000; // in ms client = redis.createClient({ - parser: parser, connect_timeout: connect_timeout }); @@ -304,7 +302,6 @@ describe('connection tests', function () { it('emit an error after the socket timeout exceeded the connect_timeout time', function (done) { var connect_timeout = 500; // in ms client = redis.createClient({ - parser: parser, // Auto detect ipv4 and use non routable ip to trigger the timeout host: '10.255.255.1', connect_timeout: connect_timeout @@ -337,7 +334,6 @@ describe('connection tests', function () { it('use the system socket timeout if the connect_timeout has not been provided', function (done) { client = redis.createClient({ - parser: parser, host: '2001:db8::ff00:42:8329' // auto detect ip v6 }); assert.strictEqual(client.address, '2001:db8::ff00:42:8329:6379'); @@ -350,21 +346,20 @@ describe('connection tests', function () { it('clears the socket timeout after a connection has been established', function (done) { client = redis.createClient({ - parser: parser, connect_timeout: 1000 }); process.nextTick(function () { - // node > 4 + // node > 6 var timeout = client.stream.timeout; - // node <= 4 + // node <= 6 if (timeout === undefined) timeout = client.stream._idleTimeout; assert.strictEqual(timeout, 1000); }); client.on('connect', function () { - // node > 4 + // node > 6 var expected = 0; var timeout = client.stream.timeout; - // node <= 4 + // node <= 6 if (timeout === undefined) { timeout = client.stream._idleTimeout; expected = -1; @@ -379,7 +374,6 @@ describe('connection tests', function () { client = redis.createClient({ host: 'localhost', port: '6379', - parser: parser, connect_timeout: 1000 }); @@ -392,7 +386,6 @@ describe('connection tests', function () { } client = redis.createClient({ path: '/tmp/redis.sock', - parser: parser, connect_timeout: 1000 }); diff --git a/test/detect_buffers.spec.js b/test/detect_buffers.spec.js index 2e2f7f1ed02..a656c549435 100644 --- a/test/detect_buffers.spec.js +++ b/test/detect_buffers.spec.js @@ -8,7 +8,7 @@ var redis = config.redis; describe('detect_buffers', function () { var client; - var args = config.configureClient('javascript', 'localhost', { + var args = config.configureClient('localhost', { detect_buffers: true }); diff --git a/test/helper.js b/test/helper.js index 29a07d6f319..3e1437d14ab 100644 --- a/test/helper.js +++ b/test/helper.js @@ -161,7 +161,6 @@ module.exports = { cb = opts; opts = {}; } - var parsers = ['javascript']; var protocols = ['IPv4']; if (process.platform !== 'win32') { protocols.push('IPv6', '/tmp/redis.sock'); @@ -180,13 +179,11 @@ module.exports = { } } describe('using options: ' + strOptions, function () { - parsers.forEach(function (parser) { - protocols.forEach(function (ip, i) { - if (i !== 0 && !opts.allConnections) { - return; - } - cb(parser, ip, config.configureClient(parser, ip, options)); - }); + protocols.forEach(function (ip, i) { + if (i !== 0 && !opts.allConnections) { + return; + } + cb(ip, config.configureClient(ip, options)); }); }); }); diff --git a/test/lib/config.js b/test/lib/config.js index 0420b032ce3..d363f83e04b 100644 --- a/test/lib/config.js +++ b/test/lib/config.js @@ -16,7 +16,7 @@ var config = { IPv4: '127.0.0.1', IPv6: '::1' }, - configureClient: function (parser, ip, opts) { + configureClient: function (ip, opts) { var args = []; // Do not manipulate the opts => copy them each time opts = opts ? JSON.parse(JSON.stringify(opts)) : {}; @@ -28,8 +28,6 @@ var config = { args.push(config.HOST[ip]); opts.family = ip; } - - opts.parser = parser; args.push(opts); return args; diff --git a/test/multi.spec.js b/test/multi.spec.js index 233ead776c1..498168f9d56 100644 --- a/test/multi.spec.js +++ b/test/multi.spec.js @@ -93,9 +93,9 @@ describe("The 'multi' method", function () { }); - helper.allTests(function (parser, ip, args) { + helper.allTests(function (ip, args) { - describe('using ' + parser + ' and ' + ip, function () { + describe('using ' + ip, function () { describe('when not connected', function () { diff --git a/test/node_redis.spec.js b/test/node_redis.spec.js index 3c511ec9af9..79f58dd5187 100644 --- a/test/node_redis.spec.js +++ b/test/node_redis.spec.js @@ -79,9 +79,9 @@ describe('The node_redis client', function () { client.stream.destroy(); }); - helper.allTests(function (parser, ip, args) { + helper.allTests(function (ip, args) { - describe('using ' + parser + ' and ' + ip, function () { + describe('using ' + ip, function () { afterEach(function () { client.end(true); @@ -375,7 +375,6 @@ describe('The node_redis client', function () { it('should retry all commands instead of returning an error if a command did not yet return after a connection loss', function (done) { var bclient = redis.createClient({ - parser: parser, retry_unfulfilled_commands: true }); bclient.blpop('blocking list 2', 5, function (err, value) { @@ -394,7 +393,6 @@ describe('The node_redis client', function () { it('should retry all commands even if the offline queue is disabled', function (done) { var bclient = redis.createClient({ - parser: parser, enableOfflineQueue: false, retryUnfulfilledCommands: true }); @@ -751,7 +749,7 @@ describe('The node_redis client', function () { }); }); - // TODO: consider allowing loading commands in v.3 + // TODO: consider allowing loading commands in v.4 // it('should fire early', function (done) { // client = redis.createClient.apply(null, args); // var fired = false; @@ -884,9 +882,7 @@ describe('The node_redis client', function () { }); it('flushes the command queue if connection is lost', function (done) { - client = redis.createClient({ - parser: parser - }); + client = redis.createClient(); client.once('ready', function () { var multi = client.multi(); @@ -938,7 +934,6 @@ describe('The node_redis client', function () { it('stream not writable', function (done) { client = redis.createClient({ - parser: parser, enable_offline_queue: false }); client.on('ready', function () { @@ -952,7 +947,6 @@ describe('The node_redis client', function () { it('emit an error and does not enqueues operation', function (done) { client = redis.createClient(9999, null, { - parser: parser, max_attempts: 0, enable_offline_queue: false }); @@ -977,7 +971,6 @@ describe('The node_redis client', function () { it('flushes the command queue if connection is lost', function (done) { client = redis.createClient({ - parser: parser, enable_offline_queue: false }); diff --git a/test/prefix.spec.js b/test/prefix.spec.js index e34805a7a9b..52fd39c1ccd 100644 --- a/test/prefix.spec.js +++ b/test/prefix.spec.js @@ -7,14 +7,13 @@ var redis = config.redis; describe('prefix key names', function () { - helper.allTests(function (parser, ip, args) { + helper.allTests(function (ip, args) { - describe('using ' + parser + ' and ' + ip, function () { + describe('using ' + ip, function () { var client = null; beforeEach(function (done) { client = redis.createClient({ - parser: parser, prefix: 'test:prefix:' }); client.on('ready', function () { diff --git a/test/pubsub.spec.js b/test/pubsub.spec.js index da2561459fe..e0c71adf91d 100644 --- a/test/pubsub.spec.js +++ b/test/pubsub.spec.js @@ -7,9 +7,9 @@ var redis = config.redis; describe('publish/subscribe', function () { - helper.allTests(function (parser, ip, args) { + helper.allTests(function (ip, args) { - describe('using ' + parser + ' and ' + ip, function () { + describe('using ' + ip, function () { var pub = null; var sub = null; var channel = 'test channel'; diff --git a/test/rename.spec.js b/test/rename.spec.js index b8f7c65d0d8..f43eee35ca8 100644 --- a/test/rename.spec.js +++ b/test/rename.spec.js @@ -17,9 +17,9 @@ describe('rename commands', function () { }); }); - helper.allTests(function (parser, ip, args) { + helper.allTests(function (ip, args) { - describe('using ' + parser + ' and ' + ip, function () { + describe('using ' + ip, function () { var client = null; beforeEach(function (done) { @@ -29,7 +29,6 @@ describe('rename commands', function () { set: '807081f5afa96845a02816a28b7258c3', GETRANGE: '9e3102b15cf231c4e9e940f284744fe0' }, - parser: parser }); client.on('ready', function () { @@ -124,7 +123,6 @@ describe('rename commands', function () { rename_commands: { set: '807081f5afa96845a02816a28b7258c3' }, - parser: parser, prefix: 'baz' }); client.set('foo', 'bar'); diff --git a/test/return_buffers.spec.js b/test/return_buffers.spec.js index eb18d79393f..ba40c26c373 100644 --- a/test/return_buffers.spec.js +++ b/test/return_buffers.spec.js @@ -7,11 +7,11 @@ var redis = config.redis; describe('return_buffers', function () { - helper.allTests(function (parser, ip, basicArgs) { + helper.allTests(function (ip, basicArgs) { - describe('using ' + parser + ' and ' + ip, function () { + describe('using ' + ip, function () { var client; - var args = config.configureClient(parser, ip, { + var args = config.configureClient(ip, { return_buffers: true, detect_buffers: true }); @@ -243,7 +243,7 @@ describe('return_buffers', function () { var channel = 'test channel'; var message = new Buffer('test message'); - var args = config.configureClient(parser, ip, { + var args = config.configureClient(ip, { return_buffers: true }); From 92ea8e65a9fd45ac246e17ccfbbda57838bed83d Mon Sep 17 00:00:00 2001 From: Salakar Date: Sun, 9 Feb 2020 06:12:51 +0000 Subject: [PATCH 0814/1748] docs: update changelog --- CHANGELOG.md | 1 + 1 file changed, 1 insertion(+) diff --git a/CHANGELOG.md b/CHANGELOG.md index 49be75d7d1b..186da332a45 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -28,6 +28,7 @@ a lot of old deprecated features and old internals in preparation for an upcomin - Replaced `double-ended-queue` with `denque`, which brings performance improvements - Add timestamps to debug traces - Add `socket_initial_delay` option for `socket.setKeepAlive` (#1396) +- Add support for `rediss` protocol in url (#1282) ## v2.8.0 - 31 Jul, 2017 From 8f727993a736a33c3eefc48fc7ae4bf9bc2e509d Mon Sep 17 00:00:00 2001 From: Salakar Date: Sun, 9 Feb 2020 12:42:47 +0000 Subject: [PATCH 0815/1748] chore: release v3.0.1 --- package.json | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/package.json b/package.json index 1a5752d8c4b..e36c8924845 100644 --- a/package.json +++ b/package.json @@ -1,6 +1,6 @@ { "name": "redis", - "version": "3.0.0", + "version": "3.0.1", "description": "A high performance Redis client.", "keywords": [ "database", @@ -36,7 +36,6 @@ }, "dependencies": { "denque": "^1.4.1", - "prettier": "^1.19.1", "redis-commands": "^1.5.0", "redis-errors": "^1.2.0", "redis-parser": "^3.0.0" @@ -45,6 +44,7 @@ "node": ">=6" }, "devDependencies": { + "prettier": "^1.19.1", "bluebird": "^3.7.2", "coveralls": "^2.11.2", "eslint": "^6.8.0", From 33363e6a07472704207baba41175e9f8ca68c247 Mon Sep 17 00:00:00 2001 From: Salakar Date: Sun, 9 Feb 2020 13:11:47 +0000 Subject: [PATCH 0816/1748] chore: configure travis for windows builds --- .travis.yml | 30 ++++++++++++++++++++++++++---- 1 file changed, 26 insertions(+), 4 deletions(-) diff --git a/.travis.yml b/.travis.yml index 7ff40c58a51..473d6218276 100644 --- a/.travis.yml +++ b/.travis.yml @@ -1,6 +1,10 @@ language: node_js sudo: required +os: + - windows + - linux + env: - CXX=g++-4.8 TRAVIS=true @@ -11,6 +15,7 @@ addons: packages: - g++-4.8 + node_js: - "6" - "8" @@ -18,13 +23,30 @@ node_js: - "13" before_install: - - if [[ ! -f stunnel.tar.gz ]]; then wget -O stunnel.tar.gz ftp://ftp.stunnel.org/stunnel/archive/5.x/stunnel-5.54.tar.gz; fi - - if [[ ! -f ./stunnel-5.54/configure ]]; then tar -xzf stunnel.tar.gz; fi - - if [[ ! -f ./stunnel-5.54/src/stunnel ]]; then cd ./stunnel-5.54; ./configure; make; cd ..; fi - - export PATH="$PATH:$(pwd)/stunnel-5.54/src" + - |- + case $TRAVIS_OS_NAME in + linux) + if [[ ! -f stunnel.tar.gz ]]; then wget -O stunnel.tar.gz ftp://ftp.stunnel.org/stunnel/archive/5.x/stunnel-5.54.tar.gz; fi + if [[ ! -f ./stunnel-5.54/configure ]]; then tar -xzf stunnel.tar.gz; fi + if [[ ! -f ./stunnel-5.54/src/stunnel ]]; then cd ./stunnel-5.54; ./configure; make; cd ..; fi + export PATH="$PATH:$(pwd)/stunnel-5.54/src" + ;; + esac + - |- + case $TRAVIS_OS_NAME in + windows) + choco install redis-64 + C:\ProgramData\chocolatey\lib\redis-64\tools\redis-server.exe --service-install + C:\ProgramData\chocolatey\lib\redis-64\tools\redis-server.exe --service-start + '@ECHO Redis Started' + node --version + npm --version + ;; + esac cache: directories: + - $HOME/AppData/Local/Temp/chocolatey - "$TRAVIS_BUILD_DIR/stunnel-5.54" after_success: npm run coveralls From 853a837df8bcf221b0d867995379ebc8ba7aefa1 Mon Sep 17 00:00:00 2001 From: Salakar Date: Sun, 9 Feb 2020 13:18:22 +0000 Subject: [PATCH 0817/1748] chore: add node 10 to builds and wrap windows paths in quotes --- .travis.yml | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/.travis.yml b/.travis.yml index 473d6218276..a2b2941edb3 100644 --- a/.travis.yml +++ b/.travis.yml @@ -19,6 +19,7 @@ addons: node_js: - "6" - "8" + - "10" - "12" - "13" @@ -36,9 +37,8 @@ before_install: case $TRAVIS_OS_NAME in windows) choco install redis-64 - C:\ProgramData\chocolatey\lib\redis-64\tools\redis-server.exe --service-install - C:\ProgramData\chocolatey\lib\redis-64\tools\redis-server.exe --service-start - '@ECHO Redis Started' + "C:\ProgramData\chocolatey\lib\redis-64\tools\redis-server.exe" --service-install + "C:\ProgramData\chocolatey\lib\redis-64\tools\redis-server.exe" --service-start node --version npm --version ;; From cb5d163ccfa2602d87a81ac14743af87f57b4b13 Mon Sep 17 00:00:00 2001 From: Salakar Date: Sun, 9 Feb 2020 13:26:26 +0000 Subject: [PATCH 0818/1748] chore: remove g++ - no longer required --- .travis.yml | 19 +++---------------- 1 file changed, 3 insertions(+), 16 deletions(-) diff --git a/.travis.yml b/.travis.yml index a2b2941edb3..ffe8c897f38 100644 --- a/.travis.yml +++ b/.travis.yml @@ -5,17 +5,6 @@ os: - windows - linux -env: - - CXX=g++-4.8 TRAVIS=true - -addons: - apt: - sources: - - ubuntu-toolchain-r-test - packages: - - g++-4.8 - - node_js: - "6" - "8" @@ -37,16 +26,14 @@ before_install: case $TRAVIS_OS_NAME in windows) choco install redis-64 - "C:\ProgramData\chocolatey\lib\redis-64\tools\redis-server.exe" --service-install - "C:\ProgramData\chocolatey\lib\redis-64\tools\redis-server.exe" --service-start - node --version - npm --version + redis-server --service-install + redis-server --service-start ;; esac cache: directories: - - $HOME/AppData/Local/Temp/chocolatey + - "$HOME/AppData/Local/Temp/chocolatey" - "$TRAVIS_BUILD_DIR/stunnel-5.54" after_success: npm run coveralls From 377db618d2fc88d917ed8f470cfefeea3f0c2eae Mon Sep 17 00:00:00 2001 From: Salakar Date: Sun, 9 Feb 2020 13:33:12 +0000 Subject: [PATCH 0819/1748] chore: remove appveyor --- appveyor.yml | 45 --------------------------------------------- 1 file changed, 45 deletions(-) delete mode 100644 appveyor.yml diff --git a/appveyor.yml b/appveyor.yml deleted file mode 100644 index a67e581b333..00000000000 --- a/appveyor.yml +++ /dev/null @@ -1,45 +0,0 @@ -# http://www.appveyor.com/docs/appveyor-yml - -# Test against these versions of Node.js. -environment: - matrix: - - nodejs_version: "6" - - nodejs_version: "8" - - nodejs_version: "10" -# - nodejs_version: "12" -# - nodejs_version: "13" - -pull_requests: - do_not_increment_build_number: true - -platform: Any CPU -shallow_clone: true - -# Install scripts. (runs after repo cloning) -install: - # Install Redis - - nuget install redis-64 -excludeversion - - redis-64\tools\redis-server.exe --service-install - - redis-64\tools\redis-server.exe --service-start - - '@ECHO Redis Started' - # Get the required Node version - - ps: Install-Product node $env:nodejs_version - # Typical npm stuff - - npm install - -# Post-install test scripts. -test_script: - # Output useful info for debugging. - - node --version - - npm --version - - cmd: npm t - -os: - - Default Azure - - Windows Server 2012 R2 - -# Don't actually build using MSBuild -build: off - -# Set build version format here instead of in the admin panel. -version: "{build}" From 46246bea06f620f71e6680ec0b80293c0a702589 Mon Sep 17 00:00:00 2001 From: Salakar Date: Sun, 9 Feb 2020 13:33:47 +0000 Subject: [PATCH 0820/1748] chore: disable `stop-writes-on-bgsave-error` on windows (otherwise MISCONF error) --- .travis.yml | 1 + 1 file changed, 1 insertion(+) diff --git a/.travis.yml b/.travis.yml index ffe8c897f38..0f30c603431 100644 --- a/.travis.yml +++ b/.travis.yml @@ -28,6 +28,7 @@ before_install: choco install redis-64 redis-server --service-install redis-server --service-start + redis-cli config set stop-writes-on-bgsave-error no ;; esac From a6af0c28893f3ed154537a1ff37fdf64db104e92 Mon Sep 17 00:00:00 2001 From: Salakar Date: Sun, 9 Feb 2020 13:48:58 +0000 Subject: [PATCH 0821/1748] chore: update travis config --- .travis.yml | 7 +++++-- 1 file changed, 5 insertions(+), 2 deletions(-) diff --git a/.travis.yml b/.travis.yml index 0f30c603431..80bd615b415 100644 --- a/.travis.yml +++ b/.travis.yml @@ -2,8 +2,12 @@ language: node_js sudo: required os: - - windows - linux + - windows + +# fix travis windows hanging issue +# https://travis-ci.community/t/build-doesnt-finish-after-completing-tests/288/25 +env: YARN_GPG=no node_js: - "6" @@ -34,7 +38,6 @@ before_install: cache: directories: - - "$HOME/AppData/Local/Temp/chocolatey" - "$TRAVIS_BUILD_DIR/stunnel-5.54" after_success: npm run coveralls From a7149e39f18e5d7346d442541dcc497402d96705 Mon Sep 17 00:00:00 2001 From: Salakar Date: Sun, 9 Feb 2020 13:58:19 +0000 Subject: [PATCH 0822/1748] chore: update travis config --- .travis.yml | 16 ++++++++++------ 1 file changed, 10 insertions(+), 6 deletions(-) diff --git a/.travis.yml b/.travis.yml index 80bd615b415..1263695a867 100644 --- a/.travis.yml +++ b/.travis.yml @@ -2,12 +2,8 @@ language: node_js sudo: required os: - - linux - windows - -# fix travis windows hanging issue -# https://travis-ci.community/t/build-doesnt-finish-after-completing-tests/288/25 -env: YARN_GPG=no + - linux node_js: - "6" @@ -40,10 +36,18 @@ cache: directories: - "$TRAVIS_BUILD_DIR/stunnel-5.54" -after_success: npm run coveralls before_script: # Add an IPv6 config - see the corresponding Travis issue # https://github.com/travis-ci/travis-ci/issues/8361 - if [ "${TRAVIS_OS_NAME}" == "linux" ]; then sudo sh -c 'echo 0 > /proc/sys/net/ipv6/conf/all/disable_ipv6'; fi +after_script: + - |- + case $TRAVIS_OS_NAME in + windows) + redis-server --service-stop + ;; + esac + +after_success: npm run coveralls From 9f01198c0491e419ed50902fd196df324d8abcee Mon Sep 17 00:00:00 2001 From: Salakar Date: Sun, 9 Feb 2020 14:13:05 +0000 Subject: [PATCH 0823/1748] chore: try workaround travis windows hanging issue --- .travis.yml | 8 +------- test/node_redis.spec.js | 8 ++++++++ 2 files changed, 9 insertions(+), 7 deletions(-) diff --git a/.travis.yml b/.travis.yml index 1263695a867..18e3dbdeac3 100644 --- a/.travis.yml +++ b/.travis.yml @@ -34,6 +34,7 @@ before_install: cache: directories: + - "$HOME/AppData/Local/Temp/chocolatey" - "$TRAVIS_BUILD_DIR/stunnel-5.54" before_script: @@ -42,12 +43,5 @@ before_script: - if [ "${TRAVIS_OS_NAME}" == "linux" ]; then sudo sh -c 'echo 0 > /proc/sys/net/ipv6/conf/all/disable_ipv6'; fi -after_script: - - |- - case $TRAVIS_OS_NAME in - windows) - redis-server --service-stop - ;; - esac after_success: npm run coveralls diff --git a/test/node_redis.spec.js b/test/node_redis.spec.js index 79f58dd5187..83a44f074dc 100644 --- a/test/node_redis.spec.js +++ b/test/node_redis.spec.js @@ -11,6 +11,14 @@ var fork = require('child_process').fork; var redis = config.redis; var client; +after(function (done) { + if (process.platform !== 'win32' || !process.env.CI) { + return done(); + } + require('cross-spawn').sync('redis-server', ['--service-stop'], {}); + done(); +}); + describe('The node_redis client', function () { describe("The 'add_command' method", function () { From 176bfc210122d0e9d3165e82239f938827f6ea15 Mon Sep 17 00:00:00 2001 From: Salakar Date: Sun, 9 Feb 2020 14:23:47 +0000 Subject: [PATCH 0824/1748] tests: add comments about travis windows issue workaround --- test/node_redis.spec.js | 12 ++++++++++-- 1 file changed, 10 insertions(+), 2 deletions(-) diff --git a/test/node_redis.spec.js b/test/node_redis.spec.js index 83a44f074dc..8937a270c96 100644 --- a/test/node_redis.spec.js +++ b/test/node_redis.spec.js @@ -11,12 +11,20 @@ var fork = require('child_process').fork; var redis = config.redis; var client; +// Currently Travis Windows builds hang after completing if any processes are still running, +// we shutdown redis-server after all tests complete (can't do this in a +// `after_script` Travis hook as it hangs before the `after` life cycles) +// to workaround the issue. +// +// See: https://github.com/travis-ci/travis-ci/issues/8082 after(function (done) { if (process.platform !== 'win32' || !process.env.CI) { return done(); } - require('cross-spawn').sync('redis-server', ['--service-stop'], {}); - done(); + process.nextTick(function () { + require('cross-spawn').sync('redis-server', ['--service-stop'], {}); + done(); + }); }); describe('The node_redis client', function () { From 4623cec3c406f6caa0e7aaa6229e46d5d5138281 Mon Sep 17 00:00:00 2001 From: Salakar Date: Sun, 9 Feb 2020 14:35:37 +0000 Subject: [PATCH 0825/1748] docs: update readme to remove unused badge --- README.md | 1 - 1 file changed, 1 deletion(-) diff --git a/README.md b/README.md index 0167d5dbd0e..7ee8b29eb88 100644 --- a/README.md +++ b/README.md @@ -12,7 +12,6 @@ NPM downloads NPM version Build Status - Windows Tests Coverage Status Follow on Twitter

From da31ade348f9b98ebc5a3164d59a93576a865906 Mon Sep 17 00:00:00 2001 From: Salakar Date: Sun, 9 Feb 2020 15:44:52 +0000 Subject: [PATCH 0826/1748] tests: improve coverage & fix unreachable code branches --- index.js | 24 ++++++------------------ test/commands/set.spec.js | 4 ++++ test/connection.spec.js | 6 +++--- test/multi.spec.js | 2 +- 4 files changed, 14 insertions(+), 22 deletions(-) diff --git a/index.js b/index.js index 6cea8bd4c6c..439c784c8d9 100644 --- a/index.js +++ b/index.js @@ -556,12 +556,8 @@ RedisClient.prototype.connection_gone = function (why, error) { if (this.retry_delay instanceof Error) { error = this.retry_delay; } - var errorMessage = 'Redis connection in broken state: '; - if (this.retry_totaltime >= this.connect_timeout) { - errorMessage += 'connection timeout exceeded.'; - } else { - errorMessage += 'maximum connection attempts exceeded.'; - } + + var errorMessage = 'Redis connection in broken state: retry aborted.'; this.flush_and_error({ message: errorMessage, @@ -581,13 +577,7 @@ RedisClient.prototype.connection_gone = function (why, error) { } if (this.retry_totaltime >= this.connect_timeout) { - var message = 'Redis connection in broken state: '; - if (this.retry_totaltime >= this.connect_timeout) { - message += 'connection timeout exceeded.'; - } else { - message += 'maximum connection attempts exceeded.'; - } - + var message = 'Redis connection in broken state: connection timeout exceeded.'; this.flush_and_error({ message: message, code: 'CONNECTION_BROKEN', @@ -864,11 +854,9 @@ RedisClient.prototype.internal_send_command = function (command_obj) { if (command_obj.args && command_obj.args.length) { undefinedArgError.args = command_obj.args; } - if (command_obj.callback) { - command_obj.callback(undefinedArgError); - return false; - } - throw undefinedArgError; + // there is always a callback in this scenario + command_obj.callback(undefinedArgError); + return false; } else { // Seems like numbers are converted fast using string concatenation args_copy[i] = '' + args[i]; diff --git a/test/commands/set.spec.js b/test/commands/set.spec.js index d2d36491a96..33a8bfa22c0 100644 --- a/test/commands/set.spec.js +++ b/test/commands/set.spec.js @@ -150,6 +150,10 @@ describe("The 'set' method", function () { client.get('foo', helper.isNull(done)); }); + it('calls callback with error if null value is passed', function (done) { + client.set('foo', null, helper.isError(done)); + }); + it('emit an error with only the key set', function (done) { client.on('error', function (err) { assert.equal(err.message, "ERR wrong number of arguments for 'set' command"); diff --git a/test/connection.spec.js b/test/connection.spec.js index 0913a26ff6b..7c153b3926e 100644 --- a/test/connection.spec.js +++ b/test/connection.spec.js @@ -239,7 +239,7 @@ describe('connection tests', function () { retryStrategy: function (options) { if (options.totalRetryTime > 150) { client.set('foo', 'bar', function (err, res) { - assert.strictEqual(err.message, 'Redis connection in broken state: maximum connection attempts exceeded.'); + assert.strictEqual(err.message, 'Redis connection in broken state: retry aborted.'); assert.strictEqual(err.origin.message, 'Connection timeout'); done(); }); @@ -257,7 +257,7 @@ describe('connection tests', function () { retry_strategy: function (options) { if (options.total_retry_time > 150) { client.set('foo', 'bar', function (err, res) { - assert.strictEqual(err.message, 'Redis connection in broken state: maximum connection attempts exceeded.'); + assert.strictEqual(err.message, 'Redis connection in broken state: retry aborted.'); assert.strictEqual(err.code, 'CONNECTION_BROKEN'); assert.strictEqual(err.origin.code, 'ECONNREFUSED'); done(); @@ -287,7 +287,7 @@ describe('connection tests', function () { }, 50); client.on('error', function (err) { if (err instanceof redis.AbortError) { - assert.strictEqual(err.message, 'Redis connection in broken state: maximum connection attempts exceeded.'); + assert.strictEqual(err.message, 'Redis connection in broken state: retry aborted.'); assert.strictEqual(err.code, 'CONNECTION_BROKEN'); unhookIntercept(); redis.debugMode = false; diff --git a/test/multi.spec.js b/test/multi.spec.js index 498168f9d56..2465dfc2d0b 100644 --- a/test/multi.spec.js +++ b/test/multi.spec.js @@ -234,7 +234,7 @@ describe("The 'multi' method", function () { }); client.multi([['set', 'foo', 'bar'], ['get', 'foo']]).exec(function (err, res) { - assert(/Redis connection in broken state: maximum connection attempts exceeded/.test(err.message)); + assert(/Redis connection in broken state: retry aborted/.test(err.message)); assert.strictEqual(err.errors.length, 2); assert.strictEqual(err.errors[0].args.length, 2); }); From 61318e6ed6be71ddb5458d17d9d874ad9314c97e Mon Sep 17 00:00:00 2001 From: Salakar Date: Sun, 9 Feb 2020 15:46:49 +0000 Subject: [PATCH 0827/1748] 3.0.2 --- package.json | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/package.json b/package.json index e36c8924845..45c7040863c 100644 --- a/package.json +++ b/package.json @@ -1,6 +1,6 @@ { "name": "redis", - "version": "3.0.1", + "version": "3.0.2", "description": "A high performance Redis client.", "keywords": [ "database", From f2050f9daf00f9cbfe7ab9756ceb273a32f878b1 Mon Sep 17 00:00:00 2001 From: Mike Diarmid Date: Tue, 11 Feb 2020 00:17:09 +0000 Subject: [PATCH 0828/1748] tests: setup GitHub Actions to replace Travis (#1497) Additionally adds testing against many Node.js versions and Redis versions. --- .eslintrc | 1 + .github/workflows/benchmark.yml | 38 +++++++++++++++++++++ .github/workflows/linting.yml | 31 +++++++++++++++++ .github/workflows/tests.yml | 52 +++++++++++++++++++++++++++++ .github/workflows/tests_windows.yml | 49 +++++++++++++++++++++++++++ .gitignore | 1 + .npmignore | 1 + .travis.yml | 47 -------------------------- package.json | 6 ++-- test/commands/zadd.spec.js | 6 +++- test/lib/redis-process.js | 15 +++++++-- test/node_redis.spec.js | 15 ++++----- test/rename.spec.js | 4 ++- 13 files changed, 204 insertions(+), 62 deletions(-) create mode 100644 .github/workflows/benchmark.yml create mode 100644 .github/workflows/linting.yml create mode 100644 .github/workflows/tests.yml create mode 100644 .github/workflows/tests_windows.yml delete mode 100644 .travis.yml diff --git a/.eslintrc b/.eslintrc index 75c73341b06..81e5e9b9cfd 100644 --- a/.eslintrc +++ b/.eslintrc @@ -102,6 +102,7 @@ rules: globals: it: true describe: true + xdescribe: true before: true after: true beforeEach: true diff --git a/.github/workflows/benchmark.yml b/.github/workflows/benchmark.yml new file mode 100644 index 00000000000..88974e42dac --- /dev/null +++ b/.github/workflows/benchmark.yml @@ -0,0 +1,38 @@ +name: Benchmarking + +on: [pull_request] + +jobs: + benchmark: + name: Benchmark + runs-on: ubuntu-latest + strategy: + fail-fast: false + matrix: + node-version: [8.x, 10.x, 12.x] + redis-version: [5] + + steps: + - uses: actions/checkout@v1 + with: + fetch-depth: 1 + + - name: Use Node.js ${{ matrix.node-version }} + uses: actions/setup-node@v1 + with: + node-version: ${{ matrix.node-version }} + + - name: Setup Redis + uses: shogo82148/actions-setup-redis@v1.0.1 + with: + redis-version: ${{ matrix.redis-version }} + auto-start: "true" + + - run: npm i --no-audit --prefer-offline + - name: Run Benchmark + run: npm run benchmark > benchmark-output.txt && cat benchmark-output.txt + - name: Upload Benchmark Result + uses: actions/upload-artifact@v1 + with: + name: benchmark-output.txt + path: benchmark-output.txt diff --git a/.github/workflows/linting.yml b/.github/workflows/linting.yml new file mode 100644 index 00000000000..08a5f1e5ac3 --- /dev/null +++ b/.github/workflows/linting.yml @@ -0,0 +1,31 @@ +name: Linting + +on: [pull_request] + +jobs: + eslint: + name: ESLint + runs-on: ubuntu-latest + steps: + - uses: actions/checkout@v1 + with: + fetch-depth: 1 + - uses: actions/setup-node@v1 + with: + node-version: 12 + - run: npm i --no-audit --prefer-offline + - name: Test Code Linting + run: npm run lint + - name: Save Code Linting Report JSON + run: npm run lint:report + continue-on-error: true + - name: Annotate Code Linting Results + uses: ataylorme/eslint-annotate-action@1.0.4 + with: + repo-token: "${{ secrets.GITHUB_TOKEN }}" + report-json: "eslint-report.json" + - name: Upload ESLint report + uses: actions/upload-artifact@v1 + with: + name: eslint-report.json + path: eslint-report.json diff --git a/.github/workflows/tests.yml b/.github/workflows/tests.yml new file mode 100644 index 00000000000..b680815084b --- /dev/null +++ b/.github/workflows/tests.yml @@ -0,0 +1,52 @@ +name: Tests + +on: [pull_request] + +jobs: + testing: + name: Test + runs-on: ubuntu-latest + strategy: + fail-fast: false + matrix: + node-version: [6.x, 8.x, 10.x, 12.x] + redis-version: [4.x, 5.x] + + steps: + - uses: actions/checkout@v1 + with: + fetch-depth: 1 + + - name: Use Node.js ${{ matrix.node-version }} + uses: actions/setup-node@v1 + with: + node-version: ${{ matrix.node-version }} + + - name: Setup Redis + uses: shogo82148/actions-setup-redis@v1.0.1 + with: + redis-version: ${{ matrix.redis-version }} + auto-start: "false" + + - name: Disable IPv6 + run: sudo sh -c 'echo 0 > /proc/sys/net/ipv6/conf/all/disable_ipv6'; + + - name: Setup Stunnel + run: sudo apt-get install stunnel4 + + - name: Install Packages + run: npm i --no-audit --prefer-offline + + - name: Run Tests + run: npm test + + - name: Submit Coverage + run: npm run coveralls + env: + COVERALLS_REPO_TOKEN: ${{ secrets.COVERALLS_TOKEN }} + + - name: Upload Coverage Report + uses: actions/upload-artifact@v1 + with: + name: coverage + path: coverage diff --git a/.github/workflows/tests_windows.yml b/.github/workflows/tests_windows.yml new file mode 100644 index 00000000000..e2e8f53cda7 --- /dev/null +++ b/.github/workflows/tests_windows.yml @@ -0,0 +1,49 @@ +name: Tests Windows + +on: [pull_request] + +jobs: + testing-windows: + name: Test Windows + runs-on: windows-latest + strategy: + fail-fast: false + matrix: + node-version: [6.x, 8.x, 10.x, 12.x] + steps: + - uses: actions/checkout@v1 + with: + fetch-depth: 1 + + - name: Install Redis + uses: crazy-max/ghaction-chocolatey@v1 + with: + args: install redis-64 --no-progress + + - name: Start Redis + run: | + redis-server --service-install + redis-server --service-start + redis-cli config set stop-writes-on-bgsave-error no + + - name: Setup Node.js ${{ matrix.node-version }} + uses: actions/setup-node@v1 + with: + node-version: ${{ matrix.node-version }} + + - name: Install Packages + run: npm i --no-audit --prefer-offline + + - name: Run Tests + run: npm test + + - name: Submit Coverage + run: npm run coveralls + env: + COVERALLS_REPO_TOKEN: ${{ secrets.COVERALLS_TOKEN }} + + - name: Upload Coverage Report + uses: actions/upload-artifact@v1 + with: + name: coverage + path: coverage diff --git a/.gitignore b/.gitignore index 037016a27fb..64b4143dc6f 100644 --- a/.gitignore +++ b/.gitignore @@ -14,3 +14,4 @@ package-lock.json # VisualStudioCode IDEs .vscode .vs +eslint-report.json diff --git a/.npmignore b/.npmignore index 262ee0c0b4d..f1cf466f08d 100644 --- a/.npmignore +++ b/.npmignore @@ -19,3 +19,4 @@ CODE_OF_CONDUCT.md appveyor.yml package-lock.json .prettierrc +eslint-report.json diff --git a/.travis.yml b/.travis.yml deleted file mode 100644 index 18e3dbdeac3..00000000000 --- a/.travis.yml +++ /dev/null @@ -1,47 +0,0 @@ -language: node_js -sudo: required - -os: - - windows - - linux - -node_js: - - "6" - - "8" - - "10" - - "12" - - "13" - -before_install: - - |- - case $TRAVIS_OS_NAME in - linux) - if [[ ! -f stunnel.tar.gz ]]; then wget -O stunnel.tar.gz ftp://ftp.stunnel.org/stunnel/archive/5.x/stunnel-5.54.tar.gz; fi - if [[ ! -f ./stunnel-5.54/configure ]]; then tar -xzf stunnel.tar.gz; fi - if [[ ! -f ./stunnel-5.54/src/stunnel ]]; then cd ./stunnel-5.54; ./configure; make; cd ..; fi - export PATH="$PATH:$(pwd)/stunnel-5.54/src" - ;; - esac - - |- - case $TRAVIS_OS_NAME in - windows) - choco install redis-64 - redis-server --service-install - redis-server --service-start - redis-cli config set stop-writes-on-bgsave-error no - ;; - esac - -cache: - directories: - - "$HOME/AppData/Local/Temp/chocolatey" - - "$TRAVIS_BUILD_DIR/stunnel-5.54" - -before_script: - # Add an IPv6 config - see the corresponding Travis issue - # https://github.com/travis-ci/travis-ci/issues/8361 - - if [ "${TRAVIS_OS_NAME}" == "linux" ]; then - sudo sh -c 'echo 0 > /proc/sys/net/ipv6/conf/all/disable_ipv6'; - fi - -after_success: npm run coveralls diff --git a/package.json b/package.json index 45c7040863c..75489c3ec44 100644 --- a/package.json +++ b/package.json @@ -30,8 +30,10 @@ "coveralls": "nyc report --reporter=text-lcov | coveralls", "coverage": "nyc report --reporter=html", "benchmark": "node benchmarks/multi_bench.js", - "test": "nyc --cache mocha ./test/*.js ./test/commands/*.js --timeout=8000", - "lint": "eslint . --fix && npm run coverage", + "test": "nyc --cache mocha ./test/*.js ./test/commands/*.js --timeout=8000 && npm run coverage", + "lint": "eslint .", + "lint:fix": "eslint . --fix", + "lint:report": "eslint --output-file=eslint-report.json --format=json .", "compare": "node benchmarks/diff_multi_bench_output.js beforeBench.txt afterBench.txt" }, "dependencies": { diff --git a/test/commands/zadd.spec.js b/test/commands/zadd.spec.js index 827630a389c..f22963416c9 100644 --- a/test/commands/zadd.spec.js +++ b/test/commands/zadd.spec.js @@ -34,7 +34,11 @@ describe("The 'zadd' method", function () { client.zrange('infinity', 0, -1, 'WITHSCORES', function (err, res) { assert.equal(res[5], 'inf'); assert.equal(res[1], '-inf'); - assert.equal(res[3], '9.9999999999999992e+22'); + if (process.platform !== 'win32') { + assert.equal(res[3], '9.9999999999999992e+22'); + } else { + assert.equal(res[3], '9.9999999999999992e+022'); + } done(); }); }); diff --git a/test/lib/redis-process.js b/test/lib/redis-process.js index ce2f881edd6..23ff2e18153 100644 --- a/test/lib/redis-process.js +++ b/test/lib/redis-process.js @@ -17,7 +17,7 @@ function waitForRedis (available, cb, port) { var running = false; var socket = '/tmp/redis.sock'; if (port) { - // We have to distinguishe the redis sockets if we have more than a single redis instance running + // We have to distinguish the redis sockets if we have more than a single redis instance running socket = '/tmp/redis' + port + '.sock'; } port = port || config.PORT; @@ -51,6 +51,14 @@ function waitForRedis (available, cb, port) { module.exports = { start: function (done, conf, port) { var spawnFailed = false; + if (process.platform === 'win32') return done(null, { + spawnFailed: function () { + return spawnFailed; + }, + stop: function (done) { + return done(); + } + }); // spawn redis with our testing configuration. var confFile = conf || path.resolve(__dirname, '../conf/redis.conf'); var rp = spawn('redis-server', [confFile], {}); @@ -58,7 +66,10 @@ module.exports = { // capture a failure booting redis, and give // the user running the test some directions. rp.once('exit', function (code) { - if (code !== 0) spawnFailed = true; + if (code !== 0) { + spawnFailed = true; + throw new Error('TESTS: Redis Spawn Failed'); + } }); // wait for redis to become available, by diff --git a/test/node_redis.spec.js b/test/node_redis.spec.js index 8937a270c96..a2c7fac69e0 100644 --- a/test/node_redis.spec.js +++ b/test/node_redis.spec.js @@ -11,20 +11,17 @@ var fork = require('child_process').fork; var redis = config.redis; var client; -// Currently Travis Windows builds hang after completing if any processes are still running, -// we shutdown redis-server after all tests complete (can't do this in a -// `after_script` Travis hook as it hangs before the `after` life cycles) -// to workaround the issue. -// -// See: https://github.com/travis-ci/travis-ci/issues/8082 +// Currently GitHub Actions on Windows (and event travis) builds hang after completing if +// any processes are still running, we shutdown redis-server after all tests complete (can't do this in a +// `after_script` hook as it hangs before the `after` life cycles) to workaround the issue. after(function (done) { - if (process.platform !== 'win32' || !process.env.CI) { + if (process.platform !== 'win32' || !process.env.GITHUB_ACTION) { return done(); } - process.nextTick(function () { + setTimeout(function () { require('cross-spawn').sync('redis-server', ['--service-stop'], {}); done(); - }); + }, 2000); }); describe('The node_redis client', function () { diff --git a/test/rename.spec.js b/test/rename.spec.js index f43eee35ca8..68adc5699f0 100644 --- a/test/rename.spec.js +++ b/test/rename.spec.js @@ -10,7 +10,9 @@ if (process.platform === 'win32') { return; } -describe('rename commands', function () { +// TODO these tests are causing flakey tests - looks like redis-server is not +// being started with new configuration after or before these tests +xdescribe('rename commands', function () { before(function (done) { helper.stopRedis(function () { helper.startRedis('./conf/rename.conf', done); From cd357652ca377fc1ff999ea2b95eaa6812ec18e0 Mon Sep 17 00:00:00 2001 From: Salakar Date: Tue, 11 Feb 2020 00:19:13 +0000 Subject: [PATCH 0829/1748] chore: run tests on push --- .github/workflows/tests.yml | 2 +- .github/workflows/tests_windows.yml | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/.github/workflows/tests.yml b/.github/workflows/tests.yml index b680815084b..a7829a9d1b5 100644 --- a/.github/workflows/tests.yml +++ b/.github/workflows/tests.yml @@ -1,6 +1,6 @@ name: Tests -on: [pull_request] +on: [push] jobs: testing: diff --git a/.github/workflows/tests_windows.yml b/.github/workflows/tests_windows.yml index e2e8f53cda7..504cb6a4f8f 100644 --- a/.github/workflows/tests_windows.yml +++ b/.github/workflows/tests_windows.yml @@ -1,6 +1,6 @@ name: Tests Windows -on: [pull_request] +on: [push] jobs: testing-windows: From 0041e3e53d5292b13d96ce076653c5b91b314fda Mon Sep 17 00:00:00 2001 From: Salakar Date: Tue, 11 Feb 2020 00:29:04 +0000 Subject: [PATCH 0830/1748] docs: replace travis badges with GH actions badges --- README.md | 9 +++++---- 1 file changed, 5 insertions(+), 4 deletions(-) diff --git a/README.md b/README.md index 7ee8b29eb88..6cae87a270e 100644 --- a/README.md +++ b/README.md @@ -9,10 +9,11 @@ ---

- NPM downloads - NPM version - Build Status - Coverage Status + NPM downloads + NPM version + Build Status + Windows Build Status + Coverage Status Follow on Twitter

From 9e86cf3c52d24f5834a0f1be7be0d643a6186fd8 Mon Sep 17 00:00:00 2001 From: Nikhil Savaliya Date: Mon, 6 Jul 2020 13:20:12 +0530 Subject: [PATCH 0831/1748] Update README.md In multi example replacing err => setError --- README.md | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/README.md b/README.md index 6cae87a270e..de221124407 100644 --- a/README.md +++ b/README.md @@ -638,7 +638,7 @@ client.watch("foo", function(watchError) { * If err is null, it means Redis successfully attempted * the operation. */ - if (execError) throw err; + if (execError) throw execError; /** * If results === null, it means that a concurrent client @@ -675,7 +675,7 @@ clients.watcher.watch("foo", function(watchError) { // if you comment out the next line, the transaction will work clients.modifier.set("foo", Math.random(), setError => { - if (setError) throw err; + if (setError) throw setError; }); // using a setTimeout here to ensure that the MULTI/EXEC will come after the SET. From 9478453e8efcac0db495a35e0e8adb5edc4c1a08 Mon Sep 17 00:00:00 2001 From: Marnik Vander Elst Date: Sat, 18 Jul 2020 10:45:14 +0200 Subject: [PATCH 0832/1748] more info to run tests --- CONTRIBUTING.md | 6 +++++- 1 file changed, 5 insertions(+), 1 deletion(-) diff --git a/CONTRIBUTING.md b/CONTRIBUTING.md index f9cba8b4a61..d9fac6a450c 100644 --- a/CONTRIBUTING.md +++ b/CONTRIBUTING.md @@ -82,9 +82,13 @@ Working on your first Pull Request? You can learn how from this _free_ series, Node Redis has a full test suite with coverage setup. -To run the tests use the `npm test` command. To check detailed coverage locally run the `npm run coverage` command after +To run the tests, run `npm install` to install all dependencies, and then run `npm test`. To check detailed coverage locally run the `npm run coverage` command after testing and open the generated `./coverage/index.html` in your browser. +Note that the test suite assumes that a few tools are installed in your environment, such as: +- redis (make sure redis-server is not running when starting the tests, it's part of the test-suite to start it and you'll end up with a "port already in use" error) +- stunnel (for TLS tests) + ### Submitting code for review The bigger the pull request, the longer it will take to review and merge. Where possible try to break down large pull From c42841adf87956e1f67cbf58ffb82aff5e39b1af Mon Sep 17 00:00:00 2001 From: Marnik Vander Elst Date: Sat, 18 Jul 2020 10:53:38 +0200 Subject: [PATCH 0833/1748] add info on connect_timeout option, and some cosmetics --- README.md | 7 ++++--- 1 file changed, 4 insertions(+), 3 deletions(-) diff --git a/README.md b/README.md index 6cae87a270e..0ec4773f79c 100644 --- a/README.md +++ b/README.md @@ -171,15 +171,15 @@ using unix sockets if possible to increase throughput. | port | 6379 | Port of the Redis server | | path | null | The UNIX socket string of the Redis server | | url | null | The URL of the Redis server. Format: `[redis[s]:]//[[user][:password@]][host][:port][/db-number][?db=db-number[&password=bar[&option=value]]]` (More info avaliable at [IANA](http://www.iana.org/assignments/uri-schemes/prov/redis)). | -| string_numbers | null | Set to `true`, Node Redis will return Redis number values as Strings instead of javascript Numbers. Useful if you need to handle big numbers (above `Number.MAX_SAFE_INTEGER === 2^53`). Hiredis is incapable of this behavior, so setting this option to `true` will result in the built-in javascript parser being used no matter the value of the `parser` option. | +| string_numbers | null | Set to `true`, Node Redis will return Redis number values as Strings instead of javascript Numbers. Useful if you need to handle big numbers (above `Number.MAX_SAFE_INTEGER === 2^53`). Hiredis is incapable of this behavior, so setting this option to `true` will result in the built-in javascript parser being used no matter the value of the `parser` option. | | return_buffers | false | If set to `true`, then all replies will be sent to callbacks as Buffers instead of Strings. | | detect_buffers | false | If set to `true`, then replies will be sent to callbacks as Buffers. This option lets you switch between Buffers and Strings on a per-command basis, whereas `return_buffers` applies to every command on a client. **Note**: This doesn't work properly with the pubsub mode. A subscriber has to either always return Strings or Buffers. | | socket_keepalive | true | If set to `true`, the keep-alive functionality is enabled on the underlying socket. | | socket_initial_delay | 0 | Initial Delay in milliseconds, and this will also behave the interval keep alive message sending to Redis. | -| no_ready_check | false | When a connection is established to the Redis server, the server might still be loading the database from disk. While loading, the server will not respond to any commands. To work around this, Node Redis has a "ready check" which sends the `INFO` command to the server. The response from the `INFO` command indicates whether the server is ready for more commands. When ready, `node_redis` emits a `ready` event. Setting `no_ready_check` to `true` will inhibit this check. | +| no_ready_check | false | When a connection is established to the Redis server, the server might still be loading the database from disk. While loading, the server will not respond to any commands. To work around this, Node Redis has a "ready check" which sends the `INFO` command to the server. The response from the `INFO` command indicates whether the server is ready for more commands. When ready, `node_redis` emits a `ready` event. Setting `no_ready_check` to `true` will inhibit this check. | | enable_offline_queue | true | By default, if there is no active connection to the Redis server, commands are added to a queue and are executed once the connection has been established. Setting `enable_offline_queue` to `false` will disable this feature and the callback will be executed immediately with an error, or an error will be emitted if no callback is specified. | | retry_unfulfilled_commands | false | If set to `true`, all commands that were unfulfilled while the connection is lost will be retried after the connection has been reestablished. Use this with caution if you use state altering commands (e.g. `incr`). This is especially useful if you use blocking commands. | -| password | null | If set, client will run Redis auth command on connect. Alias `auth_pass` **Note** Node Redis < 2.5 must use `auth_pass` | +| password | null | If set, client will run Redis auth command on connect. Alias `auth_pass` **Note** Node Redis < 2.5 must use `auth_pass` | | db | null | If set, client will run Redis `select` command on connect. | | family | IPv4 | You can force using IPv6 if you set the family to 'IPv6'. See Node.js [net](https://nodejs.org/api/net.html) or [dns](https://nodejs.org/api/dns.html) modules on how to use the family type. | | disable_resubscribing | false | If set to `true`, a client won't resubscribe after disconnecting. | @@ -187,6 +187,7 @@ using unix sockets if possible to increase throughput. | tls | null | An object containing options to pass to [tls.connect](http://nodejs.org/api/tls.html#tls_tls_connect_port_host_options_callback) to set up a TLS connection to Redis (if, for example, it is set up to be accessible via a tunnel). | | prefix | null | A string used to prefix all used keys (e.g. `namespace:test`). Please be aware that the `keys` command will not be prefixed. The `keys` command has a "pattern" as argument and no key and it would be impossible to determine the existing keys in Redis if this would be prefixed. | | retry_strategy | function | A function that receives an options object as parameter including the retry `attempt`, the `total_retry_time` indicating how much time passed since the last time connected, the `error` why the connection was lost and the number of `times_connected` in total. If you return a number from this function, the retry will happen exactly after that time in milliseconds. If you return a non-number, no further retry will happen and all offline commands are flushed with errors. Return an error to return that specific error to all offline commands. Example below. | +| connect_timeout | 3600000 | In milliseconds. This should only be the timeout for connecting to redis, but for now it interferes with `retry_strategy` and stops it from reconnecting after this timeout. | **`detect_buffers` example:** From e4cb073be29ca2e346f40e4dfe9050f6e3be06ee Mon Sep 17 00:00:00 2001 From: Likai Ren Date: Wed, 3 Feb 2021 13:25:37 +0200 Subject: [PATCH 0834/1748] Update README.md --- README.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/README.md b/README.md index 6cae87a270e..abf95b10fa3 100644 --- a/README.md +++ b/README.md @@ -394,7 +394,7 @@ responses using JavaScript syntax. ```js client.hmset("key", "foo", "bar", "hello", "world"); -client.hgetall("hosts", function(err, value) { +client.hgetall("key", function(err, value) { console.log(value.foo); // > "bar" console.log(value.hello); // > "world" }); From 69b7094bbad056e5a5cd4d54857d11268d9599fe Mon Sep 17 00:00:00 2001 From: Leibale Eidelman Date: Fri, 5 Mar 2021 04:39:39 -0500 Subject: [PATCH 0835/1748] Workflows fixes (#1570) * upgrade workflow actions * fix setup-node version * change redis-64 version to 3.0.503 --- .github/workflows/benchmark.yml | 8 ++++---- .github/workflows/linting.yml | 8 ++++---- .github/workflows/tests.yml | 8 ++++---- .github/workflows/tests_windows.yml | 10 +++++----- 4 files changed, 17 insertions(+), 17 deletions(-) diff --git a/.github/workflows/benchmark.yml b/.github/workflows/benchmark.yml index 88974e42dac..c3db31f3bb1 100644 --- a/.github/workflows/benchmark.yml +++ b/.github/workflows/benchmark.yml @@ -13,17 +13,17 @@ jobs: redis-version: [5] steps: - - uses: actions/checkout@v1 + - uses: actions/checkout@v2.3.4 with: fetch-depth: 1 - name: Use Node.js ${{ matrix.node-version }} - uses: actions/setup-node@v1 + uses: actions/setup-node@v2.1.5 with: node-version: ${{ matrix.node-version }} - name: Setup Redis - uses: shogo82148/actions-setup-redis@v1.0.1 + uses: shogo82148/actions-setup-redis@v1.9.7 with: redis-version: ${{ matrix.redis-version }} auto-start: "true" @@ -32,7 +32,7 @@ jobs: - name: Run Benchmark run: npm run benchmark > benchmark-output.txt && cat benchmark-output.txt - name: Upload Benchmark Result - uses: actions/upload-artifact@v1 + uses: actions/upload-artifact@v2.2.2 with: name: benchmark-output.txt path: benchmark-output.txt diff --git a/.github/workflows/linting.yml b/.github/workflows/linting.yml index 08a5f1e5ac3..d110707ee09 100644 --- a/.github/workflows/linting.yml +++ b/.github/workflows/linting.yml @@ -7,10 +7,10 @@ jobs: name: ESLint runs-on: ubuntu-latest steps: - - uses: actions/checkout@v1 + - uses: actions/checkout@v2.3.4 with: fetch-depth: 1 - - uses: actions/setup-node@v1 + - uses: actions/setup-node@v2.1.5 with: node-version: 12 - run: npm i --no-audit --prefer-offline @@ -20,12 +20,12 @@ jobs: run: npm run lint:report continue-on-error: true - name: Annotate Code Linting Results - uses: ataylorme/eslint-annotate-action@1.0.4 + uses: ataylorme/eslint-annotate-action@1.1.2 with: repo-token: "${{ secrets.GITHUB_TOKEN }}" report-json: "eslint-report.json" - name: Upload ESLint report - uses: actions/upload-artifact@v1 + uses: actions/upload-artifact@v2.2.2 with: name: eslint-report.json path: eslint-report.json diff --git a/.github/workflows/tests.yml b/.github/workflows/tests.yml index a7829a9d1b5..b3a608ac77f 100644 --- a/.github/workflows/tests.yml +++ b/.github/workflows/tests.yml @@ -13,17 +13,17 @@ jobs: redis-version: [4.x, 5.x] steps: - - uses: actions/checkout@v1 + - uses: actions/checkout@v2.3.4 with: fetch-depth: 1 - name: Use Node.js ${{ matrix.node-version }} - uses: actions/setup-node@v1 + uses: actions/setup-node@v2.1.5 with: node-version: ${{ matrix.node-version }} - name: Setup Redis - uses: shogo82148/actions-setup-redis@v1.0.1 + uses: shogo82148/actions-setup-redis@v1.9.7 with: redis-version: ${{ matrix.redis-version }} auto-start: "false" @@ -46,7 +46,7 @@ jobs: COVERALLS_REPO_TOKEN: ${{ secrets.COVERALLS_TOKEN }} - name: Upload Coverage Report - uses: actions/upload-artifact@v1 + uses: actions/upload-artifact@v2.2.2 with: name: coverage path: coverage diff --git a/.github/workflows/tests_windows.yml b/.github/workflows/tests_windows.yml index 504cb6a4f8f..2f5e7e441ab 100644 --- a/.github/workflows/tests_windows.yml +++ b/.github/workflows/tests_windows.yml @@ -11,14 +11,14 @@ jobs: matrix: node-version: [6.x, 8.x, 10.x, 12.x] steps: - - uses: actions/checkout@v1 + - uses: actions/checkout@v2.3.4 with: fetch-depth: 1 - name: Install Redis - uses: crazy-max/ghaction-chocolatey@v1 + uses: crazy-max/ghaction-chocolatey@v1.4.0 with: - args: install redis-64 --no-progress + args: install redis-64 --version=3.0.503 --no-progress - name: Start Redis run: | @@ -27,7 +27,7 @@ jobs: redis-cli config set stop-writes-on-bgsave-error no - name: Setup Node.js ${{ matrix.node-version }} - uses: actions/setup-node@v1 + uses: actions/setup-node@v2.1.5 with: node-version: ${{ matrix.node-version }} @@ -43,7 +43,7 @@ jobs: COVERALLS_REPO_TOKEN: ${{ secrets.COVERALLS_TOKEN }} - name: Upload Coverage Report - uses: actions/upload-artifact@v1 + uses: actions/upload-artifact@v2.2.2 with: name: coverage path: coverage From 2a34d41558cf7f8d40c5a7981492d58db86790c9 Mon Sep 17 00:00:00 2001 From: Guy Korland Date: Sun, 7 Mar 2021 00:22:33 +0200 Subject: [PATCH 0836/1748] Add LGTM badge (#1571) --- README.md | 1 + 1 file changed, 1 insertion(+) diff --git a/README.md b/README.md index 3ee50dd9619..4e47c6f9bdc 100644 --- a/README.md +++ b/README.md @@ -15,6 +15,7 @@ Windows Build Status Coverage Status Follow on Twitter + Coverage Status

--- From 32861b5c06cd331fb855f0b3c6867b2ae2509af4 Mon Sep 17 00:00:00 2001 From: Guy Korland Date: Sun, 7 Mar 2021 20:38:57 +0200 Subject: [PATCH 0837/1748] Create .deepsource.toml (#1574) --- .deepsource.toml | 8 ++++++++ 1 file changed, 8 insertions(+) create mode 100644 .deepsource.toml diff --git a/.deepsource.toml b/.deepsource.toml new file mode 100644 index 00000000000..acb0dce9087 --- /dev/null +++ b/.deepsource.toml @@ -0,0 +1,8 @@ +version = 1 + +[[analyzers]] +name = "javascript" +enabled = true + + [analyzers.meta] + environment = ["nodejs"] From 218874432ec8aa1a5e583d448f5613207b9f373c Mon Sep 17 00:00:00 2001 From: Guy Korland Date: Mon, 8 Mar 2021 08:32:04 +0200 Subject: [PATCH 0838/1748] Create codeql-analysis.yml (#1577) --- .github/workflows/codeql-analysis.yml | 67 +++++++++++++++++++++++++++ 1 file changed, 67 insertions(+) create mode 100644 .github/workflows/codeql-analysis.yml diff --git a/.github/workflows/codeql-analysis.yml b/.github/workflows/codeql-analysis.yml new file mode 100644 index 00000000000..98c615d0c55 --- /dev/null +++ b/.github/workflows/codeql-analysis.yml @@ -0,0 +1,67 @@ +# For most projects, this workflow file will not need changing; you simply need +# to commit it to your repository. +# +# You may wish to alter this file to override the set of languages analyzed, +# or to provide custom queries or build logic. +# +# ******** NOTE ******** +# We have attempted to detect the languages in your repository. Please check +# the `language` matrix defined below to confirm you have the correct set of +# supported CodeQL languages. +# +name: "CodeQL" + +on: + push: + branches: [ master ] + pull_request: + # The branches below must be a subset of the branches above + branches: [ master ] + schedule: + - cron: '35 0 * * 4' + +jobs: + analyze: + name: Analyze + runs-on: ubuntu-latest + + strategy: + fail-fast: false + matrix: + language: [ 'javascript' ] + # CodeQL supports [ 'cpp', 'csharp', 'go', 'java', 'javascript', 'python' ] + # Learn more: + # https://docs.github.com/en/free-pro-team@latest/github/finding-security-vulnerabilities-and-errors-in-your-code/configuring-code-scanning#changing-the-languages-that-are-analyzed + + steps: + - name: Checkout repository + uses: actions/checkout@v2 + + # Initializes the CodeQL tools for scanning. + - name: Initialize CodeQL + uses: github/codeql-action/init@v1 + with: + languages: ${{ matrix.language }} + # If you wish to specify custom queries, you can do so here or in a config file. + # By default, queries listed here will override any specified in a config file. + # Prefix the list here with "+" to use these queries and those in the config file. + # queries: ./path/to/local/query, your-org/your-repo/queries@main + + # Autobuild attempts to build any compiled languages (C/C++, C#, or Java). + # If this step fails, then you should remove it and run the build manually (see below) + - name: Autobuild + uses: github/codeql-action/autobuild@v1 + + # ℹ️ Command-line programs to run using the OS shell. + # 📚 https://git.io/JvXDl + + # ✏️ If the Autobuild fails above, remove it and uncomment the following three lines + # and modify them (or add more) to build your code if your project + # uses a compiled language + + #- run: | + # make bootstrap + # make release + + - name: Perform CodeQL Analysis + uses: github/codeql-action/analyze@v1 From fbca5cda0a477c2c2bce2c85dcabfa1da9325ab4 Mon Sep 17 00:00:00 2001 From: Leibale Eidelman Date: Mon, 8 Mar 2021 14:12:26 -0500 Subject: [PATCH 0839/1748] Upgrade node and dependencies (#1578) * upgrade workflow actions * fix setup-node version * change redis-64 version to 3.0.503 * fix "no password is set" for redis6, fix tests to work with redis6, add redis6 to workflows * do not use assert.match (was added only at v13.6.0 & v12.16.0) * fix errors.subscribeUnsubscribeOnly regex * fix invaliodPassword typo * send --save "" to redis-server in tests * upgrade dependencies, set node minimum version to 10, use current LTS versions in tests and benchmark workflows * change windows tests too * revert mocha back to ^4.1.0 * fix for f5528504a0b69ff7ca836868ead4590e491b8ef4 - revert mocha back to ^4.1.0 * fix some tests and upgrade mocha * fix two more tests * try to fix tests in windows * upgrade denque and redis-commands ref #1575 * replace `new Buffer` (deprecated) with `Buffer.from` * Buffer.from(0) should be Buffer.alloc(0) --- .github/workflows/benchmark.yml | 4 ++-- .github/workflows/tests.yml | 4 ++-- .github/workflows/tests_windows.yml | 2 +- lib/individualCommands.js | 2 +- package.json | 22 +++++++++---------- test/auth.spec.js | 34 +++++++++++++++++++---------- test/commands/client.spec.js | 6 ++--- test/commands/hgetall.spec.js | 7 +++--- test/commands/hlen.spec.js | 8 +++---- test/commands/hset.spec.js | 16 +++++++------- test/commands/monitor.spec.js | 4 ++-- test/conf/password.conf | 1 + test/conf/redis.conf | 1 + test/conf/rename.conf | 1 + test/conf/slave.conf | 1 + test/connection.spec.js | 13 +++++++---- test/detect_buffers.spec.js | 24 ++++++++++---------- test/errors.js | 6 +++++ test/multi.spec.js | 2 +- test/node_redis.spec.js | 14 +++++++----- test/pubsub.spec.js | 29 ++++++++++++------------ test/return_buffers.spec.js | 26 ++++++++++++---------- 22 files changed, 129 insertions(+), 98 deletions(-) create mode 100644 test/errors.js diff --git a/.github/workflows/benchmark.yml b/.github/workflows/benchmark.yml index c3db31f3bb1..3ec398bb627 100644 --- a/.github/workflows/benchmark.yml +++ b/.github/workflows/benchmark.yml @@ -9,8 +9,8 @@ jobs: strategy: fail-fast: false matrix: - node-version: [8.x, 10.x, 12.x] - redis-version: [5] + node-version: [10.x, 12.x, 14.x, 15.x] + redis-version: [5.x, 6.x] steps: - uses: actions/checkout@v2.3.4 diff --git a/.github/workflows/tests.yml b/.github/workflows/tests.yml index b3a608ac77f..06b0e57ec3e 100644 --- a/.github/workflows/tests.yml +++ b/.github/workflows/tests.yml @@ -9,8 +9,8 @@ jobs: strategy: fail-fast: false matrix: - node-version: [6.x, 8.x, 10.x, 12.x] - redis-version: [4.x, 5.x] + node-version: [10.x, 12.x, 14.x, 15.x] + redis-version: [4.x, 5.x, 6.x] steps: - uses: actions/checkout@v2.3.4 diff --git a/.github/workflows/tests_windows.yml b/.github/workflows/tests_windows.yml index 2f5e7e441ab..7a2e00a9c93 100644 --- a/.github/workflows/tests_windows.yml +++ b/.github/workflows/tests_windows.yml @@ -9,7 +9,7 @@ jobs: strategy: fail-fast: false matrix: - node-version: [6.x, 8.x, 10.x, 12.x] + node-version: [10.x, 12.x, 14.x, 15.x] steps: - uses: actions/checkout@v2.3.4 with: diff --git a/lib/individualCommands.js b/lib/individualCommands.js index d366b642502..44973324993 100644 --- a/lib/individualCommands.js +++ b/lib/individualCommands.js @@ -4,7 +4,7 @@ var utils = require('./utils'); var debug = require('./debug'); var Multi = require('./multi'); var Command = require('./command'); -var no_password_is_set = /no password is set/; +var no_password_is_set = /no password is set|called without any password configured/; var loading = /LOADING/; var RedisClient = require('../').RedisClient; diff --git a/package.json b/package.json index 75489c3ec44..a8ae6da26ea 100644 --- a/package.json +++ b/package.json @@ -30,33 +30,33 @@ "coveralls": "nyc report --reporter=text-lcov | coveralls", "coverage": "nyc report --reporter=html", "benchmark": "node benchmarks/multi_bench.js", - "test": "nyc --cache mocha ./test/*.js ./test/commands/*.js --timeout=8000 && npm run coverage", + "test": "nyc --cache mocha ./test/*.spec.js ./test/commands/*.spec.js --timeout=8000 && npm run coverage", "lint": "eslint .", "lint:fix": "eslint . --fix", "lint:report": "eslint --output-file=eslint-report.json --format=json .", "compare": "node benchmarks/diff_multi_bench_output.js beforeBench.txt afterBench.txt" }, "dependencies": { - "denque": "^1.4.1", - "redis-commands": "^1.5.0", + "denque": "^1.5.0", + "redis-commands": "^1.7.0", "redis-errors": "^1.2.0", "redis-parser": "^3.0.0" }, "engines": { - "node": ">=6" + "node": ">=10" }, "devDependencies": { - "prettier": "^1.19.1", "bluebird": "^3.7.2", - "coveralls": "^2.11.2", - "eslint": "^6.8.0", + "coveralls": "^3.1.0", + "cross-spawn": "^7.0.3", + "eslint": "^7.21.0", "intercept-stdout": "~0.1.2", "metrics": "^0.1.21", - "mocha": "^4.1.0", - "nyc": "^14.1.1", + "mocha": "^8.3.0", + "nyc": "^15.1.0", + "prettier": "^2.2.1", "tcp-port-used": "^1.0.1", - "uuid": "^3.4.0", - "cross-spawn": "^6.0.5" + "uuid": "^8.3.2" }, "repository": { "type": "git", diff --git a/test/auth.spec.js b/test/auth.spec.js index d88ac6304e3..995d98136b3 100644 --- a/test/auth.spec.js +++ b/test/auth.spec.js @@ -3,6 +3,7 @@ var assert = require('assert'); var config = require('./lib/config'); var helper = require('./helper'); +var errors = require('./errors'); var redis = config.redis; if (process.platform === 'win32') { @@ -70,11 +71,13 @@ describe('client authentication', function () { it('emits error when auth is bad without callback', function (done) { if (helper.redisProcess().spawnFailed()) this.skip(); - client = redis.createClient.apply(null, args); + client = redis.createClient.apply(null, config.configureClient(ip, { + no_ready_check: true + })); client.once('error', function (err) { assert.strictEqual(err.command, 'AUTH'); - assert.ok(/ERR invalid password/.test(err.message)); + assert.ok(errors.invalidPassword.test(err.message)); return done(); }); @@ -84,11 +87,13 @@ describe('client authentication', function () { it('returns an error when auth is bad (empty string) with a callback', function (done) { if (helper.redisProcess().spawnFailed()) this.skip(); - client = redis.createClient.apply(null, args); + client = redis.createClient.apply(null, config.configureClient(ip, { + no_ready_check: true + })); client.auth('', function (err, res) { assert.strictEqual(err.command, 'AUTH'); - assert.ok(/ERR invalid password/.test(err.message)); + assert.ok(errors.invalidPassword.test(err.message)); done(); }); }); @@ -190,10 +195,12 @@ describe('client authentication', function () { it('should return an error if the password is not correct and a callback has been provided', function (done) { if (helper.redisProcess().spawnFailed()) this.skip(); - client = redis.createClient.apply(null, args); + client = redis.createClient.apply(null, config.configureClient(ip, { + no_ready_check: true + })); var async = true; client.auth('undefined', function (err, res) { - assert.strictEqual(err.message, 'ERR invalid password'); + assert.ok(errors.invalidPassword.test(err.message)); assert.strictEqual(err.command, 'AUTH'); assert.strictEqual(res, undefined); async = false; @@ -205,9 +212,11 @@ describe('client authentication', function () { it('should emit an error if the password is not correct and no callback has been provided', function (done) { if (helper.redisProcess().spawnFailed()) this.skip(); - client = redis.createClient.apply(null, args); + client = redis.createClient.apply(null, config.configureClient(ip, { + no_ready_check: true + })); client.on('error', function (err) { - assert.strictEqual(err.message, 'ERR invalid password'); + assert.ok(errors.invalidPassword.test(err.message)); assert.strictEqual(err.command, 'AUTH'); done(); }); @@ -235,7 +244,7 @@ describe('client authentication', function () { client = redis.createClient.apply(null, args); client.on('ready', function () { client.set('foo', 'bar', function (err, res) { - assert.equal(err.message, 'NOAUTH Authentication required.'); + assert.ok(/^NOAUTH Authentication required\.(\r\n)?$/.test(err.message)); assert.equal(err.code, 'NOAUTH'); assert.equal(err.command, 'SET'); done(); @@ -248,7 +257,7 @@ describe('client authentication', function () { client = redis.createClient.apply(null, args); client.on('error', function (err) { assert.equal(err.code, 'NOAUTH'); - assert.equal(err.message, 'Ready check failed: NOAUTH Authentication required.'); + assert.ok(/^Ready check failed: NOAUTH Authentication required\.(\r\n)?$/.test(err.message)); assert.equal(err.command, 'INFO'); done(); }); @@ -258,9 +267,10 @@ describe('client authentication', function () { if (helper.redisProcess().spawnFailed()) this.skip(); client = redis.createClient({ password: 'wrong_password', + no_ready_check: true }); client.once('error', function (err) { - assert.strictEqual(err.message, 'ERR invalid password'); + assert.ok(errors.invalidPassword.test(err.message)); done(); }); }); @@ -277,7 +287,7 @@ describe('client authentication', function () { client.once('ready', function () { assert.strictEqual(client.pub_sub_mode, 1); client.get('foo', function (err, res) { - assert(/ERR only \(P\)SUBSCRIBE \/ \(P\)UNSUBSCRIBE/.test(err.message)); + assert.ok(errors.subscribeUnsubscribeOnly.test(err.message)); done(); }); }); diff --git a/test/commands/client.spec.js b/test/commands/client.spec.js index 51a55e3f16b..3214243107c 100644 --- a/test/commands/client.spec.js +++ b/test/commands/client.spec.js @@ -57,7 +57,7 @@ describe("The 'client' method", function () { it('off', function (done) { helper.serverVersionAtLeast.call(this, client, [3, 2, 0]); assert.strictEqual(client.reply, 'ON'); - client.client(new Buffer('REPLY'), 'OFF', helper.isUndefined()); + client.client(Buffer.from('REPLY'), 'OFF', helper.isUndefined()); assert.strictEqual(client.reply, 'OFF'); client.set('foo', 'bar', helper.isUndefined(done)); }); @@ -65,7 +65,7 @@ describe("The 'client' method", function () { it('skip', function (done) { helper.serverVersionAtLeast.call(this, client, [3, 2, 0]); assert.strictEqual(client.reply, 'ON'); - client.client('REPLY', new Buffer('SKIP'), helper.isUndefined()); + client.client('REPLY', Buffer.from('SKIP'), helper.isUndefined()); assert.strictEqual(client.reply, 'SKIP_ONE_MORE'); client.set('foo', 'bar', helper.isUndefined()); client.get('foo', helper.isString('bar', done)); @@ -91,7 +91,7 @@ describe("The 'client' method", function () { var batch = client.batch(); assert.strictEqual(client.reply, 'ON'); batch.set('hello', 'world'); - batch.client(new Buffer('REPLY'), new Buffer('OFF'), helper.isUndefined()); + batch.client(Buffer.from('REPLY'), Buffer.from('OFF'), helper.isUndefined()); batch.set('foo', 'bar', helper.isUndefined()); batch.exec(function (err, res) { assert.strictEqual(client.reply, 'OFF'); diff --git a/test/commands/hgetall.spec.js b/test/commands/hgetall.spec.js index b74995427a1..5bfa609d0bc 100644 --- a/test/commands/hgetall.spec.js +++ b/test/commands/hgetall.spec.js @@ -50,7 +50,6 @@ describe("The 'hgetall' method", function () { }); describe('binary client', function () { - var client; var args = config.configureClient(ip, { return_buffers: true }); @@ -63,14 +62,14 @@ describe("The 'hgetall' method", function () { }); it('returns binary results', function (done) { - client.hmset(['bhosts', 'mjr', '1', 'another', '23', 'home', '1234', new Buffer([0xAA, 0xBB, 0x00, 0xF0]), new Buffer([0xCC, 0xDD, 0x00, 0xF0])], helper.isString('OK')); + client.hmset(['bhosts', 'mjr', '1', 'another', '23', 'home', '1234', Buffer.from([0xAA, 0xBB, 0x00, 0xF0]), Buffer.from([0xCC, 0xDD, 0x00, 0xF0])], helper.isString('OK')); client.HGETALL('bhosts', function (err, obj) { assert.strictEqual(4, Object.keys(obj).length); assert.strictEqual('1', obj.mjr.toString()); assert.strictEqual('23', obj.another.toString()); assert.strictEqual('1234', obj.home.toString()); - assert.strictEqual((new Buffer([0xAA, 0xBB, 0x00, 0xF0])).toString('binary'), Object.keys(obj)[3]); - assert.strictEqual((new Buffer([0xCC, 0xDD, 0x00, 0xF0])).toString('binary'), obj[(new Buffer([0xAA, 0xBB, 0x00, 0xF0])).toString('binary')].toString('binary')); + assert.strictEqual((Buffer.from([0xAA, 0xBB, 0x00, 0xF0])).toString('binary'), Object.keys(obj)[3]); + assert.strictEqual((Buffer.from([0xCC, 0xDD, 0x00, 0xF0])).toString('binary'), obj[(Buffer.from([0xAA, 0xBB, 0x00, 0xF0])).toString('binary')].toString('binary')); return done(err); }); }); diff --git a/test/commands/hlen.spec.js b/test/commands/hlen.spec.js index f1f94fec025..874cb2970a1 100644 --- a/test/commands/hlen.spec.js +++ b/test/commands/hlen.spec.js @@ -20,10 +20,10 @@ describe("The 'hlen' method", function () { it('reports the count of keys', function (done) { var hash = 'test hash'; - var field1 = new Buffer('0123456789'); - var value1 = new Buffer('abcdefghij'); - var field2 = new Buffer(0); - var value2 = new Buffer(0); + var field1 = Buffer.from('0123456789'); + var value1 = Buffer.from('abcdefghij'); + var field2 = Buffer.alloc(0); + var value2 = Buffer.alloc(0); client.HSET(hash, field1, value1, helper.isNumber(1)); client.HSET(hash, field2, value2, helper.isNumber(1)); diff --git a/test/commands/hset.spec.js b/test/commands/hset.spec.js index d94a7aa0f2b..746176f3d8c 100644 --- a/test/commands/hset.spec.js +++ b/test/commands/hset.spec.js @@ -21,24 +21,24 @@ describe("The 'hset' method", function () { }); it('allows a value to be set in a hash', function (done) { - var field = new Buffer('0123456789'); - var value = new Buffer('abcdefghij'); + var field = Buffer.from('0123456789'); + var value = Buffer.from('abcdefghij'); client.hset(hash, field, value, helper.isNumber(1)); client.HGET(hash, field, helper.isString(value.toString(), done)); }); it('handles an empty value', function (done) { - var field = new Buffer('0123456789'); - var value = new Buffer(0); + var field = Buffer.from('0123456789'); + var value = Buffer.alloc(0); client.HSET(hash, field, value, helper.isNumber(1)); client.HGET([hash, field], helper.isString('', done)); }); it('handles empty key and value', function (done) { - var field = new Buffer(0); - var value = new Buffer(0); + var field = Buffer.alloc(0); + var value = Buffer.alloc(0); client.HSET([hash, field, value], function (err, res) { assert.strictEqual(res, 1); client.HSET(hash, field, value, helper.isNumber(0, done)); @@ -60,7 +60,7 @@ describe("The 'hset' method", function () { it('does not error when a buffer and date are set as values on the same hash', function (done) { var hash = 'test hash'; var field1 = 'buffer'; - var value1 = new Buffer('abcdefghij'); + var value1 = Buffer.from('abcdefghij'); var field2 = 'date'; var value2 = new Date(); @@ -70,7 +70,7 @@ describe("The 'hset' method", function () { it('does not error when a buffer and date are set as fields on the same hash', function (done) { var hash = 'test hash'; var value1 = 'buffer'; - var field1 = new Buffer('abcdefghij'); + var field1 = Buffer.from('abcdefghij'); var value2 = 'date'; var field2 = new Date(); diff --git a/test/commands/monitor.spec.js b/test/commands/monitor.spec.js index 7465ac4eb84..679277ffcac 100644 --- a/test/commands/monitor.spec.js +++ b/test/commands/monitor.spec.js @@ -90,8 +90,8 @@ describe("The 'monitor' method", function () { monitorClient.MONITOR(function (err, res) { assert.strictEqual(monitorClient.monitoring, true); - assert.strictEqual(res.inspect(), new Buffer('OK').inspect()); - monitorClient.mget('hello', new Buffer('world')); + assert.strictEqual(res.inspect(), Buffer.from('OK').inspect()); + monitorClient.mget('hello', Buffer.from('world')); }); monitorClient.on('monitor', function (time, args, rawOutput) { diff --git a/test/conf/password.conf b/test/conf/password.conf index c2b8feb4478..3b3c02f346e 100644 --- a/test/conf/password.conf +++ b/test/conf/password.conf @@ -3,3 +3,4 @@ port 6379 bind ::1 127.0.0.1 unixsocket /tmp/redis.sock unixsocketperm 700 +save "" diff --git a/test/conf/redis.conf b/test/conf/redis.conf index fad47a5fc63..9bf706c6543 100644 --- a/test/conf/redis.conf +++ b/test/conf/redis.conf @@ -2,3 +2,4 @@ port 6379 bind ::1 127.0.0.1 unixsocket /tmp/redis.sock unixsocketperm 700 +save "" \ No newline at end of file diff --git a/test/conf/rename.conf b/test/conf/rename.conf index ef839236558..207fe156221 100644 --- a/test/conf/rename.conf +++ b/test/conf/rename.conf @@ -2,6 +2,7 @@ port 6379 bind ::1 127.0.0.1 unixsocket /tmp/redis.sock unixsocketperm 700 +save "" rename-command SET 807081f5afa96845a02816a28b7258c3 rename-command GET f397808a43ceca3963e22b4e13deb672 rename-command GETRANGE 9e3102b15cf231c4e9e940f284744fe0 diff --git a/test/conf/slave.conf b/test/conf/slave.conf index 31cd801c269..f5632bbffcb 100644 --- a/test/conf/slave.conf +++ b/test/conf/slave.conf @@ -4,3 +4,4 @@ unixsocket /tmp/redis6381.sock unixsocketperm 700 slaveof localhost 6379 masterauth porkchopsandwiches +save "" diff --git a/test/connection.spec.js b/test/connection.spec.js index 7c153b3926e..26a982a92cc 100644 --- a/test/connection.spec.js +++ b/test/connection.spec.js @@ -14,6 +14,8 @@ describe('connection tests', function () { client = null; }); afterEach(function () { + if (!client) return; + client.end(true); }); @@ -238,7 +240,8 @@ describe('connection tests', function () { client = redis.createClient({ retryStrategy: function (options) { if (options.totalRetryTime > 150) { - client.set('foo', 'bar', function (err, res) { + client.set('foo', 'bar'); + client.once('error', function (err) { assert.strictEqual(err.message, 'Redis connection in broken state: retry aborted.'); assert.strictEqual(err.origin.message, 'Connection timeout'); done(); @@ -256,7 +259,8 @@ describe('connection tests', function () { client = redis.createClient({ retry_strategy: function (options) { if (options.total_retry_time > 150) { - client.set('foo', 'bar', function (err, res) { + client.set('foo', 'bar'); + client.once('error', function (err) { assert.strictEqual(err.message, 'Redis connection in broken state: retry aborted.'); assert.strictEqual(err.code, 'CONNECTION_BROKEN'); assert.strictEqual(err.origin.code, 'ECONNREFUSED'); @@ -334,9 +338,10 @@ describe('connection tests', function () { it('use the system socket timeout if the connect_timeout has not been provided', function (done) { client = redis.createClient({ - host: '2001:db8::ff00:42:8329' // auto detect ip v6 + host: '0:0:0:0:0:0:0:1', // auto detect ip v6 + no_ready_check: true }); - assert.strictEqual(client.address, '2001:db8::ff00:42:8329:6379'); + assert.strictEqual(client.address, '0:0:0:0:0:0:0:1:6379'); assert.strictEqual(client.connection_options.family, 6); process.nextTick(function () { assert.strictEqual(client.stream.listeners('timeout').length, 0); diff --git a/test/detect_buffers.spec.js b/test/detect_buffers.spec.js index a656c549435..faa63efb1f1 100644 --- a/test/detect_buffers.spec.js +++ b/test/detect_buffers.spec.js @@ -43,7 +43,7 @@ describe('detect_buffers', function () { describe('first argument is a buffer', function () { it('returns a buffer', function (done) { - client.get(new Buffer('string key 1'), function (err, reply) { + client.get(Buffer.from('string key 1'), function (err, reply) { assert.strictEqual(true, Buffer.isBuffer(reply)); assert.strictEqual('', reply.inspect()); return done(err); @@ -51,7 +51,7 @@ describe('detect_buffers', function () { }); it('returns a bufffer when executed as part of transaction', function (done) { - client.multi().get(new Buffer('string key 1')).exec(function (err, reply) { + client.multi().get(Buffer.from('string key 1')).exec(function (err, reply) { assert.strictEqual(1, reply.length); assert.strictEqual(true, Buffer.isBuffer(reply[0])); assert.strictEqual('', reply[0].inspect()); @@ -65,8 +65,8 @@ describe('detect_buffers', function () { it('can interleave string and buffer results', function (done) { client.multi() .hget('hash key 2', 'key 1') - .hget(new Buffer('hash key 2'), 'key 1') - .hget('hash key 2', new Buffer('key 2')) + .hget(Buffer.from('hash key 2'), 'key 1') + .hget('hash key 2', Buffer.from('key 2')) .hget('hash key 2', 'key 2') .exec(function (err, reply) { assert.strictEqual(true, Array.isArray(reply)); @@ -86,8 +86,8 @@ describe('detect_buffers', function () { it('can interleave string and buffer results', function (done) { client.batch() .hget('hash key 2', 'key 1') - .hget(new Buffer('hash key 2'), 'key 1') - .hget('hash key 2', new Buffer('key 2')) + .hget(Buffer.from('hash key 2'), 'key 1') + .hget('hash key 2', Buffer.from('key 2')) .hget('hash key 2', 'key 2') .exec(function (err, reply) { assert.strictEqual(true, Array.isArray(reply)); @@ -150,7 +150,7 @@ describe('detect_buffers', function () { describe('first argument is a buffer', function () { it('returns buffers for keys requested', function (done) { - client.hmget(new Buffer('hash key 2'), 'key 1', 'key 2', function (err, reply) { + client.hmget(Buffer.from('hash key 2'), 'key 1', 'key 2', function (err, reply) { assert.strictEqual(true, Array.isArray(reply)); assert.strictEqual(2, reply.length); assert.strictEqual(true, Buffer.isBuffer(reply[0])); @@ -162,7 +162,7 @@ describe('detect_buffers', function () { }); it('returns buffers for keys requested in transaction', function (done) { - client.multi().hmget(new Buffer('hash key 2'), 'key 1', 'key 2').exec(function (err, reply) { + client.multi().hmget(Buffer.from('hash key 2'), 'key 1', 'key 2').exec(function (err, reply) { assert.strictEqual(true, Array.isArray(reply)); assert.strictEqual(1, reply.length); assert.strictEqual(2, reply[0].length); @@ -175,7 +175,7 @@ describe('detect_buffers', function () { }); it('returns buffers for keys requested in .batch', function (done) { - client.batch().hmget(new Buffer('hash key 2'), 'key 1', 'key 2').exec(function (err, reply) { + client.batch().hmget(Buffer.from('hash key 2'), 'key 1', 'key 2').exec(function (err, reply) { assert.strictEqual(true, Array.isArray(reply)); assert.strictEqual(1, reply.length); assert.strictEqual(2, reply[0].length); @@ -226,7 +226,7 @@ describe('detect_buffers', function () { describe('first argument is a buffer', function () { it('returns buffer values', function (done) { - client.hgetall(new Buffer('hash key 2'), function (err, reply) { + client.hgetall(Buffer.from('hash key 2'), function (err, reply) { assert.strictEqual(null, err); assert.strictEqual('object', typeof reply); assert.strictEqual(2, Object.keys(reply).length); @@ -239,7 +239,7 @@ describe('detect_buffers', function () { }); it('returns buffer values when executed in transaction', function (done) { - client.multi().hgetall(new Buffer('hash key 2')).exec(function (err, reply) { + client.multi().hgetall(Buffer.from('hash key 2')).exec(function (err, reply) { assert.strictEqual(1, reply.length); assert.strictEqual('object', typeof reply[0]); assert.strictEqual(2, Object.keys(reply[0]).length); @@ -252,7 +252,7 @@ describe('detect_buffers', function () { }); it('returns buffer values when executed in .batch', function (done) { - client.batch().hgetall(new Buffer('hash key 2')).exec(function (err, reply) { + client.batch().hgetall(Buffer.from('hash key 2')).exec(function (err, reply) { assert.strictEqual(1, reply.length); assert.strictEqual('object', typeof reply[0]); assert.strictEqual(2, Object.keys(reply[0]).length); diff --git a/test/errors.js b/test/errors.js new file mode 100644 index 00000000000..060afab585a --- /dev/null +++ b/test/errors.js @@ -0,0 +1,6 @@ +'use strict'; + +module.exports = { + invalidPassword: /^(ERR invalid password|WRONGPASS invalid username-password pair)/, + subscribeUnsubscribeOnly: /^ERR( Can't execute 'get':)? only \(P\)SUBSCRIBE \/ \(P\)UNSUBSCRIBE/ +}; diff --git a/test/multi.spec.js b/test/multi.spec.js index 2465dfc2d0b..5b0e801c875 100644 --- a/test/multi.spec.js +++ b/test/multi.spec.js @@ -46,7 +46,7 @@ describe("The 'multi' method", function () { } var json = JSON.stringify(test_arr); - zlib.deflate(new Buffer(json), function (err, buffer) { + zlib.deflate(Buffer.from(json), function (err, buffer) { if (err) { done(err); return; diff --git a/test/node_redis.spec.js b/test/node_redis.spec.js index a2c7fac69e0..12ad70d5ef6 100644 --- a/test/node_redis.spec.js +++ b/test/node_redis.spec.js @@ -83,10 +83,11 @@ describe('The node_redis client', function () { client.once('reconnecting', function () { process.nextTick(function () { assert.strictEqual(client.reply_parser.buffer, null); + client.end(true); done(); }); }); - var partialInput = new Buffer('$100\r\nabcdef'); + var partialInput = Buffer.from('$100\r\nabcdef'); client.reply_parser.execute(partialInput); assert.strictEqual(client.reply_parser.buffer.inspect(), partialInput.inspect()); client.stream.destroy(); @@ -531,11 +532,11 @@ describe('The node_redis client', function () { // TODO: Investigate why this test is failing hard and killing mocha if using '/tmp/redis.sock'. // Seems like something is wrong with nyc while passing a socket connection to create client! - client = redis.createClient(); - client.quit(function () { - client.get('foo', function (err, res) { + var client2 = redis.createClient(); + client2.quit(function () { + client2.get('foo', function (err, res) { assert.strictEqual(err.message, 'Stream connection ended and command aborted. It might have been processed.'); - assert.strictEqual(client.offline_queue.length, 0); + assert.strictEqual(client2.offline_queue.length, 0); done(); }); }); @@ -702,6 +703,7 @@ describe('The node_redis client', function () { // Recreate client in domain so error handlers run this domain // Changed in: "error handler runs outside of its domain" // https://github.com/nodejs/node/pull/26211 + client.end(true); // make sure to close current client client = redis.createClient(); } client.end(true); @@ -811,7 +813,7 @@ describe('The node_redis client', function () { // ready is called in a reply process.nextTick(function () { // Fail the set answer. Has no corresponding command obj and will therefore land in the error handler and set - client.reply_parser.execute(new Buffer('a*1\r*1\r$1`zasd\r\na')); + client.reply_parser.execute(Buffer.from('a*1\r*1\r$1`zasd\r\na')); }); }); }); diff --git a/test/pubsub.spec.js b/test/pubsub.spec.js index e0c71adf91d..34e93f37f2c 100644 --- a/test/pubsub.spec.js +++ b/test/pubsub.spec.js @@ -3,6 +3,7 @@ var assert = require('assert'); var config = require('./lib/config'); var helper = require('./helper'); +var errors = require('./errors'); var redis = config.redis; describe('publish/subscribe', function () { @@ -124,7 +125,7 @@ describe('publish/subscribe', function () { detect_buffers: true }); sub.on('subscribe', function (chnl, count) { - if (chnl.inspect() === new Buffer([0xAA, 0xBB, 0x00, 0xF0]).inspect()) { + if (chnl.inspect() === Buffer.from([0xAA, 0xBB, 0x00, 0xF0]).inspect()) { assert.equal(1, count); if (a) { return done(); @@ -137,7 +138,7 @@ describe('publish/subscribe', function () { a = true; }); - sub.subscribe(new Buffer([0xAA, 0xBB, 0x00, 0xF0]), channel2); + sub.subscribe(Buffer.from([0xAA, 0xBB, 0x00, 0xF0]), channel2); }); it('receives messages on subscribed channel', function (done) { @@ -262,13 +263,13 @@ describe('publish/subscribe', function () { }); it('handles multiple channels with the same channel name properly, even with buffers', function (done) { - var channels = ['a', 'b', 'a', new Buffer('a'), 'c', 'b']; + var channels = ['a', 'b', 'a', Buffer.from('a'), 'c', 'b']; var subscribed_channels = [1, 2, 2, 2, 3, 3]; var i = 0; sub.subscribe(channels); sub.on('subscribe', function (channel, count) { if (Buffer.isBuffer(channel)) { - assert.strictEqual(channel.inspect(), new Buffer(channels[i]).inspect()); + assert.strictEqual(channel.inspect(), Buffer.from(channels[i]).inspect()); } else { assert.strictEqual(channel, channels[i].toString()); } @@ -420,7 +421,7 @@ describe('publish/subscribe', function () { }); subscribe(['prefix:*', 'prefix:3'], function () { - pub.publish('prefix:1', new Buffer('test'), function () { + pub.publish('prefix:1', Buffer.from('test'), function () { subscribe(['prefix:2']); subscribe(['5', 'test:a', 'bla'], function () { assert(all); @@ -494,9 +495,9 @@ describe('publish/subscribe', function () { sub2.batch().psubscribe('*', helper.isString('*')).exec(); sub2.subscribe('/foo'); sub2.on('pmessage', function (pattern, channel, message) { - assert.strictEqual(pattern.inspect(), new Buffer('*').inspect()); - assert.strictEqual(channel.inspect(), new Buffer('/foo').inspect()); - assert.strictEqual(message.inspect(), new Buffer('hello world').inspect()); + assert.strictEqual(pattern.inspect(), Buffer.from('*').inspect()); + assert.strictEqual(channel.inspect(), Buffer.from('/foo').inspect()); + assert.strictEqual(message.inspect(), Buffer.from('hello world').inspect()); sub2.quit(done); }); pub.pubsub('numsub', '/foo', function (err, res) { @@ -517,11 +518,11 @@ describe('publish/subscribe', function () { assert.strictEqual(sub.shouldBuffer, false); sub.on('pmessageBuffer', function (pattern, channel) { if (typeof pattern === 'string') { - pattern = new Buffer(pattern); - channel = new Buffer(channel); + pattern = Buffer.from(pattern); + channel = Buffer.from(channel); } - assert.strictEqual(pattern.inspect(), new Buffer('*').inspect()); - assert.strictEqual(channel.inspect(), new Buffer('/foo').inspect()); + assert.strictEqual(pattern.inspect(), Buffer.from('*').inspect()); + assert.strictEqual(channel.inspect(), Buffer.from('/foo').inspect()); sub.quit(end); }); // Either message_buffers or buffers has to be true, but not both at the same time @@ -587,7 +588,7 @@ describe('publish/subscribe', function () { }); // Get is forbidden sub.get('foo', function (err, res) { - assert(/^ERR only \(P\)SUBSCRIBE \/ \(P\)UNSUBSCRIBE/.test(err.message)); + assert.ok(errors.subscribeUnsubscribeOnly.test(err.message)); assert.strictEqual(err.command, 'GET'); }); // Quit is allowed @@ -597,7 +598,7 @@ describe('publish/subscribe', function () { it('emit error if only pub sub commands are allowed without callback', function (done) { sub.subscribe('channel'); sub.on('error', function (err) { - assert(/^ERR only \(P\)SUBSCRIBE \/ \(P\)UNSUBSCRIBE/.test(err.message)); + assert.ok(errors.subscribeUnsubscribeOnly.test(err.message)); assert.strictEqual(err.command, 'GET'); done(); }); diff --git a/test/return_buffers.spec.js b/test/return_buffers.spec.js index ba40c26c373..22efb31a04f 100644 --- a/test/return_buffers.spec.js +++ b/test/return_buffers.spec.js @@ -40,6 +40,10 @@ describe('return_buffers', function () { }); }); + afterEach(function () { + client.end(true); + }); + describe('get', function () { describe('first argument is a string', function () { it('returns a buffer', function (done) { @@ -65,8 +69,8 @@ describe('return_buffers', function () { it('returns buffers', function (done) { client.multi() .hget('hash key 2', 'key 1') - .hget(new Buffer('hash key 2'), 'key 1') - .hget('hash key 2', new Buffer('key 2')) + .hget(Buffer.from('hash key 2'), 'key 1') + .hget('hash key 2', Buffer.from('key 2')) .hget('hash key 2', 'key 2') .exec(function (err, reply) { assert.strictEqual(true, Array.isArray(reply)); @@ -87,8 +91,8 @@ describe('return_buffers', function () { it('returns buffers', function (done) { client.batch() .hget('hash key 2', 'key 1') - .hget(new Buffer('hash key 2'), 'key 1') - .hget('hash key 2', new Buffer('key 2')) + .hget(Buffer.from('hash key 2'), 'key 1') + .hget('hash key 2', Buffer.from('key 2')) .hget('hash key 2', 'key 2') .exec(function (err, reply) { assert.strictEqual(true, Array.isArray(reply)); @@ -121,7 +125,7 @@ describe('return_buffers', function () { describe('first argument is a buffer', function () { it('returns buffers for keys requested', function (done) { - client.hmget(new Buffer('hash key 2'), 'key 1', 'key 2', function (err, reply) { + client.hmget(Buffer.from('hash key 2'), 'key 1', 'key 2', function (err, reply) { assert.strictEqual(true, Array.isArray(reply)); assert.strictEqual(2, reply.length); assert.strictEqual(true, Buffer.isBuffer(reply[0])); @@ -133,7 +137,7 @@ describe('return_buffers', function () { }); it('returns buffers for keys requested in transaction', function (done) { - client.multi().hmget(new Buffer('hash key 2'), 'key 1', 'key 2').exec(function (err, reply) { + client.multi().hmget(Buffer.from('hash key 2'), 'key 1', 'key 2').exec(function (err, reply) { assert.strictEqual(true, Array.isArray(reply)); assert.strictEqual(1, reply.length); assert.strictEqual(2, reply[0].length); @@ -146,7 +150,7 @@ describe('return_buffers', function () { }); it('returns buffers for keys requested in .batch', function (done) { - client.batch().hmget(new Buffer('hash key 2'), 'key 1', 'key 2').exec(function (err, reply) { + client.batch().hmget(Buffer.from('hash key 2'), 'key 1', 'key 2').exec(function (err, reply) { assert.strictEqual(true, Array.isArray(reply)); assert.strictEqual(1, reply.length); assert.strictEqual(2, reply[0].length); @@ -197,7 +201,7 @@ describe('return_buffers', function () { describe('first argument is a buffer', function () { it('returns buffer values', function (done) { - client.hgetall(new Buffer('hash key 2'), function (err, reply) { + client.hgetall(Buffer.from('hash key 2'), function (err, reply) { assert.strictEqual(null, err); assert.strictEqual('object', typeof reply); assert.strictEqual(2, Object.keys(reply).length); @@ -210,7 +214,7 @@ describe('return_buffers', function () { }); it('returns buffer values when executed in transaction', function (done) { - client.multi().hgetall(new Buffer('hash key 2')).exec(function (err, reply) { + client.multi().hgetall(Buffer.from('hash key 2')).exec(function (err, reply) { assert.strictEqual(1, reply.length); assert.strictEqual('object', typeof reply[0]); assert.strictEqual(2, Object.keys(reply[0]).length); @@ -223,7 +227,7 @@ describe('return_buffers', function () { }); it('returns buffer values when executed in .batch', function (done) { - client.batch().hgetall(new Buffer('hash key 2')).exec(function (err, reply) { + client.batch().hgetall(Buffer.from('hash key 2')).exec(function (err, reply) { assert.strictEqual(1, reply.length); assert.strictEqual('object', typeof reply[0]); assert.strictEqual(2, Object.keys(reply[0]).length); @@ -241,7 +245,7 @@ describe('return_buffers', function () { var pub; var sub; var channel = 'test channel'; - var message = new Buffer('test message'); + var message = Buffer.from('test message'); var args = config.configureClient(ip, { return_buffers: true From 47e2e3837e6527c74c596b758ec9b8643055a5d5 Mon Sep 17 00:00:00 2001 From: Leibale Eidelman Date: Mon, 8 Mar 2021 16:30:24 -0500 Subject: [PATCH 0840/1748] Exclude examples from deepsource (#1579) --- .deepsource.toml | 1 + 1 file changed, 1 insertion(+) diff --git a/.deepsource.toml b/.deepsource.toml index acb0dce9087..34bfad29479 100644 --- a/.deepsource.toml +++ b/.deepsource.toml @@ -1,4 +1,5 @@ version = 1 +exclude_patterns = ["examples/**"] [[analyzers]] name = "javascript" From bb208d0b9d7d4f4ce7677108f4d74459d842092a Mon Sep 17 00:00:00 2001 From: Guy Korland Date: Wed, 10 Mar 2021 12:36:13 +0200 Subject: [PATCH 0841/1748] Add codeclimate badge (#1572) Co-authored-by: Leibale Eidelman --- README.md | 1 + 1 file changed, 1 insertion(+) diff --git a/README.md b/README.md index 4e47c6f9bdc..2f91956bf78 100644 --- a/README.md +++ b/README.md @@ -15,6 +15,7 @@ Windows Build Status Coverage Status Follow on Twitter + Coverage Status

From 428e1c8a7b2322c2650294638cb1663ac5692728 Mon Sep 17 00:00:00 2001 From: Guy Korland Date: Thu, 18 Mar 2021 01:21:37 +0200 Subject: [PATCH 0842/1748] Add support for Redis 6 `auth pass [user]` (#1508) * Add support for `auth pass user` * fix lint issues * fix typo * fix more lint issues * more lints fixes * reverse password user order * update redis-commands * Update individualCommands.js Clean code * Update individualCommands.js * Update auth.spec.js * Update index.js Co-authored-by: Leibale Eidelman --- index.js | 3 ++- lib/createClient.js | 6 +++++- lib/individualCommands.js | 24 ++++++++++++++++++------ test/auth.spec.js | 2 +- 4 files changed, 26 insertions(+), 9 deletions(-) diff --git a/index.js b/index.js index 439c784c8d9..fe79c5f3935 100644 --- a/index.js +++ b/index.js @@ -109,6 +109,7 @@ function RedisClient (options, stream) { this.closing = false; this.server_info = {}; this.auth_pass = options.auth_pass || options.password; + this.auth_user = options.auth_user || options.user; this.selected_db = options.db; // Save the selected db here, used when reconnecting this.fire_strings = true; // Determine if strings or buffers should be written to the stream this.pipeline = false; @@ -240,7 +241,7 @@ RedisClient.prototype.create_stream = function () { if (this.auth_pass !== undefined) { this.ready = true; // Fail silently as we might not be able to connect - this.auth(this.auth_pass, function (err) { + this.auth(this.auth_pass, this.auth_user, function (err) { if (err && err.code !== 'UNCERTAIN_STATE') { self.emit('error', err); } diff --git a/lib/createClient.js b/lib/createClient.js index 1533d73d1b7..b03bb575399 100644 --- a/lib/createClient.js +++ b/lib/createClient.js @@ -29,7 +29,11 @@ module.exports = function createClient (port_arg, host_arg, options) { // [redis:]//[[user][:password]@][host][:port][/db-number][?db=db-number[&password=bar[&option=value]]] if (parsed.slashes) { // We require slashes if (parsed.auth) { - options.password = parsed.auth.slice(parsed.auth.indexOf(':') + 1); + var columnIndex = parsed.auth.indexOf(':'); + options.password = parsed.auth.slice(columnIndex + 1); + if (columnIndex > 0) { + options.user = parsed.auth.slice(0, columnIndex); + } } if (parsed.protocol) { if (parsed.protocol === 'rediss:') { diff --git a/lib/individualCommands.js b/lib/individualCommands.js index 44973324993..4d6f0a70942 100644 --- a/lib/individualCommands.js +++ b/lib/individualCommands.js @@ -180,7 +180,7 @@ Multi.prototype.info = Multi.prototype.INFO = function info (section, callback) return this; }; -function auth_callback (self, pass, callback) { +function auth_callback (self, pass, user, callback) { return function (err, res) { if (err) { if (no_password_is_set.test(err.message)) { @@ -191,7 +191,7 @@ function auth_callback (self, pass, callback) { // If redis is still loading the db, it will not authenticate and everything else will fail debug('Redis still loading, trying to authenticate later'); setTimeout(function () { - self.auth(pass, callback); + self.auth(user, pass, callback); }, 100); return; } @@ -200,25 +200,37 @@ function auth_callback (self, pass, callback) { }; } -RedisClient.prototype.auth = RedisClient.prototype.AUTH = function auth (pass, callback) { +RedisClient.prototype.auth = RedisClient.prototype.AUTH = function auth (pass, user, callback) { debug('Sending auth to ' + this.address + ' id ' + this.connection_id); + // Backward compatibility support for auth with password only + if (user instanceof Function) { + callback = user; + user = null; + } // Stash auth for connect and reconnect. this.auth_pass = pass; + this.auth_user = user; var ready = this.ready; this.ready = ready || this.offline_queue.length === 0; - var tmp = this.internal_send_command(new Command('auth', [pass], auth_callback(this, pass, callback))); + var tmp = this.internal_send_command(new Command('auth', user ? [user, pass] : [pass], auth_callback(this, pass, user, callback))); this.ready = ready; return tmp; }; // Only works with batch, not in a transaction -Multi.prototype.auth = Multi.prototype.AUTH = function auth (pass, callback) { +Multi.prototype.auth = Multi.prototype.AUTH = function auth (pass, user, callback) { debug('Sending auth to ' + this.address + ' id ' + this.connection_id); + // Backward compatibility support for auth with password only + if (user instanceof Function) { + callback = user; + user = null; + } // Stash auth for connect and reconnect. this.auth_pass = pass; - this.queue.push(new Command('auth', [pass], auth_callback(this._client, callback))); + this.auth_user = user; + this.queue.push(new Command('auth', user ? [user, pass] : [pass], auth_callback(this._client, pass, user, callback))); return this; }; diff --git a/test/auth.spec.js b/test/auth.spec.js index 995d98136b3..d1b596e5ae3 100644 --- a/test/auth.spec.js +++ b/test/auth.spec.js @@ -61,7 +61,7 @@ describe('client authentication', function () { }); var tmp = client.command_queue.get(0).callback; client.command_queue.get(0).callback = function (err, res) { - client.auth = function (pass, callback) { + client.auth = function (pass, user, callback) { callback(null, 'retry worked'); }; tmp(new Error('ERR redis is still LOADING')); From 09f0fe822862baf0f2a5d2b37537babffaae66d8 Mon Sep 17 00:00:00 2001 From: leibale Date: Wed, 17 Mar 2021 19:28:05 -0400 Subject: [PATCH 0843/1748] "fix" tests --- test/connection.spec.js | 2 +- test/unify_options.spec.js | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/test/connection.spec.js b/test/connection.spec.js index 26a982a92cc..827ff69c8aa 100644 --- a/test/connection.spec.js +++ b/test/connection.spec.js @@ -543,7 +543,7 @@ describe('connection tests', function () { it('allows connecting with the redis url in the options object and works with protocols other than the redis protocol (e.g. http)', function (done) { client = redis.createClient({ - url: 'http://foo:porkchopsandwiches@' + config.HOST[ip] + '/3' + url: 'http://:porkchopsandwiches@' + config.HOST[ip] + '/3' }); assert.strictEqual(client.auth_pass, 'porkchopsandwiches'); assert.strictEqual(+client.selected_db, 3); diff --git a/test/unify_options.spec.js b/test/unify_options.spec.js index 40bdf22a8b5..dcdd46d330b 100644 --- a/test/unify_options.spec.js +++ b/test/unify_options.spec.js @@ -218,7 +218,7 @@ describe('createClient options', function () { option: [1, 2, 3], url: '//hm:abc@localhost:123/3' }); - assert.strictEqual(Object.keys(options).length, 6); + assert.strictEqual(Object.keys(options).length, 7); assert.strictEqual(options.option.length, 3); assert.strictEqual(options.host, 'localhost'); assert.strictEqual(options.port, '123'); From 7fdc54ea2f1c08de5de63ba9cbccea044ae3801a Mon Sep 17 00:00:00 2001 From: leibale Date: Wed, 31 Mar 2021 14:36:51 -0400 Subject: [PATCH 0844/1748] fix for 428e1c8a7b2322c2650294638cb1663ac5692728 - fix auth retry when redis is in loading state --- lib/individualCommands.js | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/lib/individualCommands.js b/lib/individualCommands.js index 4d6f0a70942..c3ea3da0df3 100644 --- a/lib/individualCommands.js +++ b/lib/individualCommands.js @@ -191,7 +191,7 @@ function auth_callback (self, pass, user, callback) { // If redis is still loading the db, it will not authenticate and everything else will fail debug('Redis still loading, trying to authenticate later'); setTimeout(function () { - self.auth(user, pass, callback); + self.auth(pass, user, callback); }, 100); return; } From 79f34c22a45eb767e713d7b115970aa40e578b6c Mon Sep 17 00:00:00 2001 From: Guy Korland Date: Thu, 1 Apr 2021 00:43:06 +0300 Subject: [PATCH 0845/1748] Bump version to 3.1.0 (#1590) --- package.json | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/package.json b/package.json index a8ae6da26ea..be84ce4f11a 100644 --- a/package.json +++ b/package.json @@ -1,6 +1,6 @@ { "name": "redis", - "version": "3.0.2", + "version": "3.1.0", "description": "A high performance Redis client.", "keywords": [ "database", From b797cf28f5911e65269d43260b49a2961b7f626a Mon Sep 17 00:00:00 2001 From: leibale Date: Wed, 31 Mar 2021 17:50:03 -0400 Subject: [PATCH 0846/1748] add user to README.md --- README.md | 1 + 1 file changed, 1 insertion(+) diff --git a/README.md b/README.md index 2f91956bf78..8f830d9827e 100644 --- a/README.md +++ b/README.md @@ -182,6 +182,7 @@ using unix sockets if possible to increase throughput. | enable_offline_queue | true | By default, if there is no active connection to the Redis server, commands are added to a queue and are executed once the connection has been established. Setting `enable_offline_queue` to `false` will disable this feature and the callback will be executed immediately with an error, or an error will be emitted if no callback is specified. | | retry_unfulfilled_commands | false | If set to `true`, all commands that were unfulfilled while the connection is lost will be retried after the connection has been reestablished. Use this with caution if you use state altering commands (e.g. `incr`). This is especially useful if you use blocking commands. | | password | null | If set, client will run Redis auth command on connect. Alias `auth_pass` **Note** Node Redis < 2.5 must use `auth_pass` | +| user | null | The ACL user (only valid when `password` is set) | | db | null | If set, client will run Redis `select` command on connect. | | family | IPv4 | You can force using IPv6 if you set the family to 'IPv6'. See Node.js [net](https://nodejs.org/api/net.html) or [dns](https://nodejs.org/api/dns.html) modules on how to use the family type. | | disable_resubscribing | false | If set to `true`, a client won't resubscribe after disconnecting. | From 7e77de84bc80b0742321939c59612dc27559bbff Mon Sep 17 00:00:00 2001 From: Guy Korland Date: Thu, 8 Apr 2021 17:03:22 +0300 Subject: [PATCH 0847/1748] Add Chat (#1594) --- README.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/README.md b/README.md index 8f830d9827e..10f0c044886 100644 --- a/README.md +++ b/README.md @@ -14,9 +14,9 @@ Build Status Windows Build Status Coverage Status - Follow on Twitter Coverage Status +

--- From 2d11b6dc9b9774464a91fb4b448bad8bf699629e Mon Sep 17 00:00:00 2001 From: Leibale Eidelman Date: Thu, 8 Apr 2021 18:04:34 -0400 Subject: [PATCH 0848/1748] fix #1569 - improve monitor_regex (#1595) Co-authored-by: Guy Korland --- lib/utils.js | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/lib/utils.js b/lib/utils.js index 52e58ecfa6c..d0336ae9c1d 100644 --- a/lib/utils.js +++ b/lib/utils.js @@ -127,7 +127,7 @@ module.exports = { reply_to_object: replyToObject, print: print, err_code: /^([A-Z]+)\s+(.+)$/, - monitor_regex: /^[0-9]{10,11}\.[0-9]+ \[[0-9]+ .+\]( ".+?")+$/, + monitor_regex: /^[0-9]{10,11}\.[0-9]+ \[[0-9]+ .+\].*"$/, clone: convenienceClone, callback_or_emit: callbackOrEmit, reply_in_order: replyInOrder From fc28860dd4d4baa0449078948bbb0cf1e77c587d Mon Sep 17 00:00:00 2001 From: Leibale Eidelman Date: Tue, 13 Apr 2021 12:42:37 -0400 Subject: [PATCH 0849/1748] Bump version to 3.1.1 (#1597) --- package.json | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/package.json b/package.json index be84ce4f11a..762a798c34e 100644 --- a/package.json +++ b/package.json @@ -1,6 +1,6 @@ { "name": "redis", - "version": "3.1.0", + "version": "3.1.1", "description": "A high performance Redis client.", "keywords": [ "database", From 4f85030e42da2eed6a178e54994330af5062761e Mon Sep 17 00:00:00 2001 From: Leibale Eidelman Date: Tue, 20 Apr 2021 18:25:35 -0400 Subject: [PATCH 0850/1748] fix #1600 - exclude unnecessary files from tarball (#1601) --- .npmignore | 1 + package.json | 2 +- 2 files changed, 2 insertions(+), 1 deletion(-) diff --git a/.npmignore b/.npmignore index f1cf466f08d..605ed4543cc 100644 --- a/.npmignore +++ b/.npmignore @@ -20,3 +20,4 @@ appveyor.yml package-lock.json .prettierrc eslint-report.json +.deepsource.toml diff --git a/package.json b/package.json index 762a798c34e..f6806391641 100644 --- a/package.json +++ b/package.json @@ -1,6 +1,6 @@ { "name": "redis", - "version": "3.1.1", + "version": "3.1.2", "description": "A high performance Redis client.", "keywords": [ "database", From 4e6d018d77c259f7ff7cc9137153b4ffc4cdbba0 Mon Sep 17 00:00:00 2001 From: Leibale Eidelman Date: Thu, 2 Sep 2021 10:04:48 -0400 Subject: [PATCH 0851/1748] V4 (#1624) * init v4 * add .gitignore to benchmark * spawn redis-servers for tests, add some tests, fix client auth on connect * add tests coverage report * add tests workflow, replace nyc text reporter with text-summary * run tests with node 16.x & redis 6.x only (for now) * add socket events on client, stop reconnectiong when manually calling disconnect, remove abort signal listener when a command is written on the socket * add isOpen boolean getter on client, add maxLength option to command queue, add test for client.multi * move to use CommonJS * add MULTI and EXEC commands to when executing multi command, make client.multi return type innerit the module commands, clean some tests, exclute spec files from coverage report * missing file from commit 61edd4f1b5436640cadb842f783edce36b07aa80 * exclude spec files from coverage report * add support for options in a command function (.get, .set, ...), add support for the SELECT command, implement a couple of commands, fix client socket reconnection strategy, add support for using replicas (RO) in cluster, and more.. * fix client.blPop test * use which to find redis-server path * change command options to work with Symbol rather then WeakSet * implement more commands * Add support for lua scripts in client & muilti, fix client socket initiator, implement simple cluster nodes discovery strategy * replace `callbackify` with `legacyMode` * add the SCAN command and client.scanIterator * rename scanIterator * init benchmark workflow * fix benchmark workflow * fix benchmark workflow * fix benchmark workflow * push coverage report to Coveralls * fix Coveralls * generator lcov (for Coveralls) * fix .nycrc.json * PubSub * add support for all set commands (including sScanIterator) * support pipeline * fix KEEPTTL in SET * remove console.log * add HyperLogLog commands * update README.md (thanks to @guyroyse) * add support for most of the "keys commands" * fix EXPIREAT.spec.ts * add support for date in both EXPIREAT & EXPIRE * add tests * better cluster nodes discorvery strategy after MOVED error, add PubSub test * fix PubSub UNSUBSCRIBE/PUNSUBSCRIBE without channel and/or listener * fix PubSub * add release-it to dev dependencies * Release 4.0.0-next.0 * fix .npmignore * Release 4.0.0-next.1 * fix links in README.md * fix .npmignore * Release 4.0.0-next.2 * add support for all sorted set commands * add support for most stream commands * add missing file from commit 53de279afe58f8359b6704c0390e32632787b346 * lots of todo commends * make PubSub test more stable * clean ZPOPMAX * add support for lua scripts and modules in cluster, spawn cluster for tests, add some cluster tests, fix pubsub listener arguments * GET.spec.ts * add support for List commands, fix some Sorted Set commands, add some cluster commands, spawn cluster for testing, add support for command options in cluster, and more * add missing file from commit faab94fab2262e26b44159646f7c99090da789a5 * clean ZRANK and ZREVRANK * add XREAD and XREADGROUP commands * remove unused files * implement a couple of more commands, make cluster random iterator be per node (instead of per slot) * Release 4.0.0-next.3 * app spec files to npmignore * fix some code analyzers (LGTM, deepsource, codeclimate) issues * fix CLUSTER_NODES, add some tests * add HSCAN, clean some commands, add tests for generic transformers * add missing files from 0feb35a1fbf423c017831f2f3dd02f55e58be6a2 * update README.md (thanks to @guyroyse) * handle ASK errors, add some commands and tests * Release 4.0.0-next.4 * replace "modern" with "v4" * remove unused imports * add all ACL subcommands, all MODULE subcommands, and some other commands * remove 2 unused imports * fix BITFIELD command * fix XTRIM spec file * clean code * fix package.json types field * better modules support, fix some bugs in legacy mode, add some tests * remove unused function * add test for hScanIterator * change node mimimum version to 12 (latest LTS) * update tsconfig.json to support node 12, run tests on Redis 5 & 6 and on all node live versions * remove future node releases :P * remove "lib" from ts compiler options * Update tsconfig.json * fix build * run some tests only on supported redis versions, use coveralls parallel mode * fix tests * Do not use "timers/promises", fix "isRedisVersionGreaterThan" * skip AbortController tests when not available * use 'fs'.promises instead of 'fs/promises' * add some missing commands * run GETDEL tests only if the redis version is greater than 6.2 * implement some GEO commands, improve scan generic transformer, expose RPUSHX * fix GEOSEARCH & GEOSEARCHSTORE * use socket.setNoDelay and queueMicrotask to improve latency * commands-queue.ts: String length / byte length counting issue (#1630) * Update commands-queue.ts Hopefully fixing #1628 * Reverted 2fa5ea6, and implemented test for byte length check * Changed back to Buffer.byteLength, due to issue author input. Updated test to look for 4 bytes. * Fixed. There were two places that length was calculated. * Removed redundant string assignment * add 2 bytes test as well Co-authored-by: Leibale Eidelman * fix scripts in multi * do not hide bugs in redis * fix for e7bf09644b28c57287bcf84d3433265abdac2c71 * remove unused import * implement WATCH command, fix ZRANGESTORE & GEOSEARCHSTORE tests * update README.md Co-authored-by: @GuyRoyse * use typedoc to auto generate documentation * run "npm install" before "npm run documentation" * clean documentation workflow * fix WATCH spec file * increase "CLUSTER_NODE_TIMEOUT" to 5000ms to avoid "CLUSTERDOWN" errors in tests * pull cluster state every 100 ms * await meetPromises before pulling the cluster state * enhance the way commanders (client/multi/cluster) get extended with modules and scripts * add test for socket retry strategy * implement more commands * set GETEX minimum version to 6.2 * remove unused imports * add support for multi in cluster * upgrade dependencies * Release 4.0.0-next.5 * remove unused imports * improve benchmarking * use the same Multi with duplicated clients * exclude some files from the documentation, add some exports, clean code * fix #1636 - handle null in multi.exec * remove unused import * add supoprt for tuples in HSET * add FIRST_KEY_INDEX to HSET * add a bunch of missing commands, fix MSET and HELLO, add some tests * add FIRST_KEY_INDEX to MSET and MSETNX * upgrade actions * fix coverallsapp/github-action version * Update documentation.yml * Update documentation.yml * clean code * remove unused imports * use "npm ci" instead of "npm install" * fix `self` binding on client modules, use connection pool for `duplicateConnection` * add client.executeIsolated, rename "duplicateConnection" to "isolated", update README.md (thanks to @GuyRoyse and @SimonPrickett) * update README (thanks to @GuyRoyse), add some tests * try to fix "cluster is down" errors in tests * try to fix "cluster is down" errors in tests * upgrade dependencies * update package-lock * Release 4.0.0-next.6 * fix #1636 - fix WatchError * fix for f1bf0beebf29bae7e87bc961a7aa73c1af9acba2 - remove .only from multi tests * Release 4.0.0-next.7 * update README and other markdown files Co-authored-by: @GuyRoyse & @SimonPrickett * Doc updates. (#1640) * update docs, upgrade dependencies * fix README * Release 4.0.0-rc.0 * Update README.md * update docs, add `connectTimeout` options, fix tls Co-authored-by: Guy Royse * npm update, "fix" some tests, clean code * fix AssertionError import * fix #1642 - fix XREAD, XREADGROUP and XTRIM * fix #1644 - add the QUIT command * add socket.noDelay and socket.keepAlive configurations * Update README.md (#1645) * Update README.md Fixed issue with how connection string was specified. Now you can have user@host without having to specify a password, which just makes more sense * Update client-configuration.md as well Co-authored-by: Leibale Eidelman * update socket.reconnectStrategy description * fix borken link in v3-to-v4.md * increase test coverage, fix bug in cluster redirection strategy, implement CLIENT_ID, remove unused EXEC command Co-authored-by: Nova Co-authored-by: Simon Prickett Co-authored-by: Guy Royse --- .deepsource.toml | 9 - .eslintignore | 4 - .eslintrc | 109 - .github/FUNDING.yml | 1 - .github/ISSUE_TEMPLATE.md | 19 +- .github/PULL_REQUEST_TEMPLATE.md | 7 +- .github/workflows/benchmark.yml | 37 +- .github/workflows/codeql-analysis.yml | 67 - .github/workflows/documentation.yml | 31 + .github/workflows/linting.yml | 31 - .github/workflows/tests.yml | 51 +- .github/workflows/tests_windows.yml | 49 - .gitignore | 25 +- .npmignore | 36 +- .nycrc.json | 4 + .prettierrc | 11 - CHANGELOG.md | 23 + CODE_OF_CONDUCT.md | 60 - CONTRIBUTING.md | 92 +- LICENSE | 2 +- README.md | 1072 +- benchmark/.gitignore | 1 + benchmark/index.js | 81 + benchmark/package-lock.json | 926 ++ benchmark/package.json | 17 + benchmarks/diff_multi_bench_output.js | 95 - benchmarks/multi_bench.js | 291 - docs/FAQ.md | 13 + docs/client-configuration.md | 30 + docs/isolated-execution.md | 67 + docs/v3-to-v4.md | 35 + examples/auth.js | 7 - examples/backpressure_drain.js | 34 - examples/eval.js | 14 - examples/extend.js | 26 - examples/file.js | 38 - examples/mget.js | 7 - examples/monitor.js | 12 - examples/multi.js | 49 - examples/multi2.js | 31 - examples/psubscribe.js | 33 - examples/pub_sub.js | 42 - examples/scan.js | 51 - examples/simple.js | 26 - examples/sort.js | 19 - examples/streams.js | 47 - examples/subqueries.js | 17 - examples/subquery.js | 17 - examples/unix_socket.js | 32 - examples/web_server.js | 33 - index.js | 1039 -- index.ts | 10 + lib/client.spec.ts | 562 + lib/client.ts | 468 + lib/cluster-slots.ts | 221 + lib/cluster.spec.ts | 115 + lib/cluster.ts | 202 + lib/command-options.ts | 14 + lib/command.js | 16 - lib/commander.spec.ts | 28 + lib/commander.ts | 109 + lib/commands-queue.ts | 333 + lib/commands.js | 105 - lib/commands/ACL_CAT.spec.ts | 23 + lib/commands/ACL_CAT.ts | 13 + lib/commands/ACL_DELUSER.spec.ts | 30 + lib/commands/ACL_DELUSER.ts | 7 + lib/commands/ACL_GENPASS.spec.ts | 23 + lib/commands/ACL_GENPASS.ts | 13 + lib/commands/ACL_GETUSER.spec.ts | 27 + lib/commands/ACL_GETUSER.ts | 34 + lib/commands/ACL_LIST.spec.ts | 14 + lib/commands/ACL_LIST.ts | 7 + lib/commands/ACL_LOAD.spec.ts | 14 + lib/commands/ACL_LOAD.ts | 7 + lib/commands/ACL_LOG.spec.ts | 53 + lib/commands/ACL_LOG.ts | 48 + lib/commands/ACL_LOG_RESET.spec.ts | 14 + lib/commands/ACL_LOG_RESET.ts | 7 + lib/commands/ACL_SAVE.spec.ts | 14 + lib/commands/ACL_SAVE.ts | 7 + lib/commands/ACL_SETUSER.spec.ts | 23 + lib/commands/ACL_SETUSER.ts | 7 + lib/commands/ACL_USERS.spec.ts | 14 + lib/commands/ACL_USERS.ts | 7 + lib/commands/ACL_WHOAMI.spec.ts | 14 + lib/commands/ACL_WHOAMI.ts | 7 + lib/commands/APPEND.spec.ts | 11 + lib/commands/APPEND.ts | 9 + lib/commands/ASKING.spec.ts | 11 + lib/commands/ASKING.ts | 7 + lib/commands/AUTH.spec.ts | 25 + lib/commands/AUTH.ts | 16 + lib/commands/BGREWRITEAOF.spec.ts | 11 + lib/commands/BGREWRITEAOF.ts | 7 + lib/commands/BGSAVE.spec.ts | 23 + lib/commands/BGSAVE.ts | 17 + lib/commands/BITCOUNT.spec.ts | 31 + lib/commands/BITCOUNT.ts | 25 + lib/commands/BITFIELD.spec.ts | 42 + lib/commands/BITFIELD.ts | 84 + lib/commands/BITOP.spec.ts | 35 + lib/commands/BITOP.ts | 11 + lib/commands/BITPOS.spec.ts | 42 + lib/commands/BITPOS.ts | 21 + lib/commands/BLMOVE.spec.ts | 43 + lib/commands/BLMOVE.ts | 23 + lib/commands/BLPOP.spec.ts | 79 + lib/commands/BLPOP.ts | 25 + lib/commands/BRPOP.spec.ts | 79 + lib/commands/BRPOP.ts | 25 + lib/commands/BRPOPLPUSH.spec.ts | 47 + lib/commands/BRPOPLPUSH.ts | 9 + lib/commands/BZPOPMAX.spec.ts | 66 + lib/commands/BZPOPMAX.ts | 27 + lib/commands/BZPOPMIN.spec.ts | 65 + lib/commands/BZPOPMIN.ts | 27 + lib/commands/CLIENT_ID.spec.ts | 19 + lib/commands/CLIENT_ID.ts | 9 + lib/commands/CLIENT_INFO.spec.ts | 42 + lib/commands/CLIENT_INFO.ts | 85 + lib/commands/CLUSTER_ADDSLOTS.spec.ts | 20 + lib/commands/CLUSTER_ADDSLOTS.ts | 15 + lib/commands/CLUSTER_FLUSHSLOTS.spec.ts | 11 + lib/commands/CLUSTER_FLUSHSLOTS.ts | 7 + lib/commands/CLUSTER_GETKEYSINSLOT.spec.ts | 11 + lib/commands/CLUSTER_GETKEYSINSLOT.ts | 7 + lib/commands/CLUSTER_INFO.spec.ts | 64 + lib/commands/CLUSTER_INFO.ts | 47 + lib/commands/CLUSTER_MEET.spec.ts | 11 + lib/commands/CLUSTER_MEET.ts | 7 + lib/commands/CLUSTER_NODES.spec.ts | 116 + lib/commands/CLUSTER_NODES.ts | 96 + lib/commands/CLUSTER_RESET.spec.ts | 27 + lib/commands/CLUSTER_RESET.ts | 15 + lib/commands/CLUSTER_SETSLOT.spec.ts | 20 + lib/commands/CLUSTER_SETSLOT.ts | 20 + lib/commands/CONFIG_GET.spec.ts | 11 + lib/commands/CONFIG_GET.ts | 7 + lib/commands/CONFIG_RESETSTAT.spec.ts | 11 + lib/commands/CONFIG_RESETSTAT.ts | 7 + lib/commands/CONFIG_REWRITE.spec.ts | 11 + lib/commands/CONFIG_REWRITE.ts | 7 + lib/commands/CONFIG_SET.spec.ts | 11 + lib/commands/CONFIG_SET.ts | 7 + lib/commands/COPY.spec.ts | 67 + lib/commands/COPY.ts | 24 + lib/commands/DBSIZE.spec.ts | 26 + lib/commands/DBSIZE.ts | 9 + lib/commands/DECR.spec.ts | 19 + lib/commands/DECR.ts | 9 + lib/commands/DECRBY.spec.ts | 19 + lib/commands/DECRBY.ts | 9 + lib/commands/DEL.spec.ts | 28 + lib/commands/DEL.ts | 7 + lib/commands/DISCARD.spec.ts | 11 + lib/commands/DISCARD.ts | 7 + lib/commands/DUMP.spec.ts | 11 + lib/commands/DUMP.ts | 7 + lib/commands/ECHO.spec.ts | 26 + lib/commands/ECHO.ts | 9 + lib/commands/EVAL.spec.ts | 29 + lib/commands/EVAL.ts | 9 + lib/commands/EVALSHA.spec.ts | 14 + lib/commands/EVALSHA.ts | 9 + lib/commands/EXISTS.spec.ts | 28 + lib/commands/EXISTS.ts | 11 + lib/commands/EXPIRE.spec.ts | 19 + lib/commands/EXPIRE.ts | 7 + lib/commands/EXPIREAT.spec.ts | 29 + lib/commands/EXPIREAT.ts | 11 + lib/commands/FAILOVER.spec.ts | 72 + lib/commands/FAILOVER.ts | 35 + lib/commands/FLUSHALL.spec.ts | 35 + lib/commands/FLUSHALL.ts | 18 + lib/commands/FLUSHDB.spec.ts | 36 + lib/commands/FLUSHDB.ts | 14 + lib/commands/GEOADD.spec.ts | 95 + lib/commands/GEOADD.ts | 49 + lib/commands/GEODIST.spec.ts | 35 + lib/commands/GEODIST.ts | 24 + lib/commands/GEOHASH.spec.ts | 35 + lib/commands/GEOHASH.ts | 11 + lib/commands/GEOPOS.spec.ts | 35 + lib/commands/GEOPOS.ts | 21 + lib/commands/GEOSEARCH.spec.ts | 37 + lib/commands/GEOSEARCH.ts | 16 + lib/commands/GEOSEARCHSTORE.spec.ts | 74 + lib/commands/GEOSEARCHSTORE.ts | 39 + lib/commands/GEOSEARCH_WITH.spec.ts | 42 + lib/commands/GEOSEARCH_WITH.ts | 23 + lib/commands/GET.spec.ts | 26 + lib/commands/GET.ts | 11 + lib/commands/GETBIT.spec.ts | 26 + lib/commands/GETBIT.ts | 11 + lib/commands/GETDEL.spec.ts | 29 + lib/commands/GETDEL.ts | 9 + lib/commands/GETEX.spec.ts | 96 + lib/commands/GETEX.ts | 35 + lib/commands/GETRANGE.spec.ts | 27 + lib/commands/GETRANGE.ts | 11 + lib/commands/GETSET.spec.ts | 26 + lib/commands/GETSET.ts | 9 + lib/commands/HDEL.spec.ts | 28 + lib/commands/HDEL.ts | 9 + lib/commands/HELLO.spec.ts | 77 + lib/commands/HELLO.ts | 64 + lib/commands/HEXISTS.spec.ts | 19 + lib/commands/HEXISTS.ts | 9 + lib/commands/HGET.spec.ts | 19 + lib/commands/HGET.ts | 9 + lib/commands/HGETALL.spec.ts | 41 + lib/commands/HGETALL.ts | 9 + lib/commands/HINCRBY.spec.ts | 19 + lib/commands/HINCRBY.ts | 9 + lib/commands/HINCRBYFLOAT.spec.ts | 19 + lib/commands/HINCRBYFLOAT.ts | 9 + lib/commands/HKEYS.spec.ts | 19 + lib/commands/HKEYS.ts | 9 + lib/commands/HLEN.spec.ts | 19 + lib/commands/HLEN.ts | 9 + lib/commands/HMGET.spec.ts | 28 + lib/commands/HMGET.ts | 11 + lib/commands/HRANDFIELD.spec.ts | 21 + lib/commands/HRANDFIELD.ts | 9 + lib/commands/HRANDFIELD_COUNT.spec.ts | 21 + lib/commands/HRANDFIELD_COUNT.ts | 13 + .../HRANDFIELD_COUNT_WITHVALUES.spec.ts | 21 + lib/commands/HRANDFIELD_COUNT_WITHVALUES.ts | 13 + lib/commands/HSCAN.spec.ts | 77 + lib/commands/HSCAN.ts | 37 + lib/commands/HSET.spec.ts | 44 + lib/commands/HSET.ts | 41 + lib/commands/HSETNX.spec.ts | 19 + lib/commands/HSETNX.ts | 9 + lib/commands/HSTRLEN.spec.ts | 19 + lib/commands/HSTRLEN.ts | 9 + lib/commands/HVALS.spec.ts | 19 + lib/commands/HVALS.ts | 9 + lib/commands/INCR.spec.ts | 19 + lib/commands/INCR.ts | 9 + lib/commands/INCRBY.spec.ts | 19 + lib/commands/INCRBY.ts | 9 + lib/commands/INCRBYFLOAT.spec.ts | 19 + lib/commands/INCRBYFLOAT.ts | 9 + lib/commands/INFO.spec.ts | 20 + lib/commands/INFO.ts | 15 + lib/commands/KEYS.spec.ts | 11 + lib/commands/KEYS.ts | 7 + lib/commands/LASTSAVE.spec.ts | 20 + lib/commands/LASTSAVE.ts | 9 + lib/commands/LINDEX.spec.ts | 26 + lib/commands/LINDEX.ts | 11 + lib/commands/LINSERT.spec.ts | 26 + lib/commands/LINSERT.ts | 22 + lib/commands/LLEN.spec.ts | 26 + lib/commands/LLEN.ts | 11 + lib/commands/LMOVE.spec.ts | 28 + lib/commands/LMOVE.ts | 22 + lib/commands/LOLWUT.spec.ts | 43 + lib/commands/LOLWUT.ts | 19 + lib/commands/LPOP.spec.ts | 26 + lib/commands/LPOP.ts | 9 + lib/commands/LPOP_COUNT.spec.ts | 28 + lib/commands/LPOP_COUNT.ts | 9 + lib/commands/LPOS.spec.ts | 58 + lib/commands/LPOS.ts | 26 + lib/commands/LPOS_COUNT.spec.ts | 58 + lib/commands/LPOS_COUNT.ts | 22 + lib/commands/LPUSH.spec.ts | 35 + lib/commands/LPUSH.ts | 8 + lib/commands/LPUSHX.spec.ts | 35 + lib/commands/LPUSHX.ts | 9 + lib/commands/LRANGE.spec.ts | 27 + lib/commands/LRANGE.ts | 16 + lib/commands/LREM.spec.ts | 27 + lib/commands/LREM.ts | 14 + lib/commands/LSET.spec.ts | 28 + lib/commands/LSET.ts | 14 + lib/commands/LTRIM.spec.ts | 26 + lib/commands/LTRIM.ts | 14 + lib/commands/MEMORY_DOCTOR.spec.ts | 26 + lib/commands/MEMORY_DOCTOR.ts | 7 + lib/commands/MEMORY_MALLOC-STATS.spec.ts | 26 + lib/commands/MEMORY_MALLOC-STATS.ts | 7 + lib/commands/MEMORY_PURGE.spec.ts | 26 + lib/commands/MEMORY_PURGE.ts | 7 + lib/commands/MEMORY_STATS.spec.ts | 108 + lib/commands/MEMORY_STATS.ts | 93 + lib/commands/MEMORY_USAGE.spec.ts | 37 + lib/commands/MEMORY_USAGE.ts | 21 + lib/commands/MGET.spec.ts | 26 + lib/commands/MGET.ts | 11 + lib/commands/MIGRATE.spec.ts | 76 + lib/commands/MIGRATE.ts | 65 + lib/commands/MODULE_LIST.spec.ts | 11 + lib/commands/MODULE_LIST.ts | 7 + lib/commands/MODULE_LOAD.spec.ts | 20 + lib/commands/MODULE_LOAD.ts | 13 + lib/commands/MODULE_UNLOAD.spec.ts | 11 + lib/commands/MODULE_UNLOAD.ts | 7 + lib/commands/MOVE.spec.ts | 19 + lib/commands/MOVE.ts | 7 + lib/commands/MSET.spec.ts | 42 + lib/commands/MSET.ts | 19 + lib/commands/MSETNX.spec.ts | 42 + lib/commands/MSETNX.ts | 19 + lib/commands/PERSIST.spec.ts | 19 + lib/commands/PERSIST.ts | 9 + lib/commands/PEXPIRE.spec.ts | 19 + lib/commands/PEXPIRE.ts | 9 + lib/commands/PEXPIREAT.spec.ts | 29 + lib/commands/PEXPIREAT.ts | 13 + lib/commands/PFADD.spec.ts | 28 + lib/commands/PFADD.ts | 9 + lib/commands/PFCOUNT.spec.ts | 28 + lib/commands/PFCOUNT.ts | 9 + lib/commands/PFMERGE.spec.ts | 28 + lib/commands/PFMERGE.ts | 9 + lib/commands/PING.spec.ts | 18 + lib/commands/PING.ts | 7 + lib/commands/PSETEX.spec.ts | 26 + lib/commands/PSETEX.ts | 14 + lib/commands/PTTL.spec.ts | 19 + lib/commands/PTTL.ts | 11 + lib/commands/PUBLISH.spec.ts | 19 + lib/commands/PUBLISH.ts | 7 + lib/commands/PUBSUB_CHANNELS.spec.ts | 35 + lib/commands/PUBSUB_CHANNELS.ts | 15 + lib/commands/PUBSUB_NUMPAT.spec.ts | 26 + lib/commands/PUBSUB_NUMPAT.ts | 9 + lib/commands/PUBSUB_NUMSUB.spec.ts | 42 + lib/commands/PUBSUB_NUMSUB.ts | 23 + lib/commands/RANDOMKEY.spec.ts | 19 + lib/commands/RANDOMKEY.ts | 9 + lib/commands/READONLY.spec.ts | 11 + lib/commands/READONLY.ts | 7 + lib/commands/READWRITE.spec.ts | 11 + lib/commands/READWRITE.ts | 7 + lib/commands/RENAME.spec.ts | 21 + lib/commands/RENAME.ts | 9 + lib/commands/RENAMENX.spec.ts | 21 + lib/commands/RENAMENX.ts | 9 + lib/commands/REPLICAOF.spec.ts | 11 + lib/commands/REPLICAOF.ts | 7 + lib/commands/RESTORE-ASKING.spec.ts | 11 + lib/commands/RESTORE-ASKING.ts | 7 + lib/commands/ROLE.spec.ts | 69 + lib/commands/ROLE.ts | 75 + lib/commands/RPOP.spec.ts | 26 + lib/commands/RPOP.ts | 9 + lib/commands/RPOPLPUSH.spec.ts | 26 + lib/commands/RPOPLPUSH.ts | 9 + lib/commands/RPOP_COUNT.spec.ts | 28 + lib/commands/RPOP_COUNT.ts | 9 + lib/commands/RPUSH.spec.ts | 35 + lib/commands/RPUSH.ts | 9 + lib/commands/RPUSHX.spec.ts | 35 + lib/commands/RPUSHX.ts | 9 + lib/commands/SADD.spec.ts | 28 + lib/commands/SADD.ts | 9 + lib/commands/SAVE.spec.ts | 11 + lib/commands/SAVE.ts | 7 + lib/commands/SCAN.spec.ts | 84 + lib/commands/SCAN.ts | 28 + lib/commands/SCARD.spec.ts | 19 + lib/commands/SCARD.ts | 9 + lib/commands/SCRIPT_DEBUG.spec.ts | 26 + lib/commands/SCRIPT_DEBUG.ts | 7 + lib/commands/SCRIPT_EXISTS.spec.ts | 35 + lib/commands/SCRIPT_EXISTS.ts | 7 + lib/commands/SCRIPT_FLUSH.spec.ts | 35 + lib/commands/SCRIPT_FLUSH.ts | 13 + lib/commands/SCRIPT_KILL.spec.ts | 11 + lib/commands/SCRIPT_KILL.ts | 7 + lib/commands/SCRIPT_LOAD.spec.ts | 30 + lib/commands/SCRIPT_LOAD.ts | 7 + lib/commands/SDIFF.spec.ts | 28 + lib/commands/SDIFF.ts | 9 + lib/commands/SDIFFSTORE.spec.ts | 28 + lib/commands/SDIFFSTORE.ts | 9 + lib/commands/SET.spec.ts | 121 + lib/commands/SET.ts | 75 + lib/commands/SETBIT.spec.ts | 26 + lib/commands/SETBIT.ts | 9 + lib/commands/SETEX.spec.ts | 26 + lib/commands/SETEX.ts | 14 + lib/commands/SETNX .spec.ts | 26 + lib/commands/SETNX.ts | 9 + lib/commands/SETRANGE.spec.ts | 26 + lib/commands/SETRANGE.ts | 9 + lib/commands/SHUTDOWN.spec.ts | 27 + lib/commands/SHUTDOWN.ts | 13 + lib/commands/SINTER.spec.ts | 28 + lib/commands/SINTER.ts | 9 + lib/commands/SINTERSTORE.spec.ts | 28 + lib/commands/SINTERSTORE.ts | 9 + lib/commands/SISMEMBER.spec.ts | 21 + lib/commands/SISMEMBER.ts | 9 + lib/commands/SMEMBERS.spec.ts | 19 + lib/commands/SMEMBERS.ts | 9 + lib/commands/SMISMEMBER.spec.ts | 21 + lib/commands/SMISMEMBER.ts | 9 + lib/commands/SMOVE.spec.ts | 19 + lib/commands/SMOVE.ts | 9 + lib/commands/SORT.spec.ts | 106 + lib/commands/SORT.ts | 57 + lib/commands/SPOP.spec.ts | 28 + lib/commands/SPOP.ts | 15 + lib/commands/SRANDMEMBER.spec.ts | 19 + lib/commands/SRANDMEMBER.ts | 9 + lib/commands/SRANDMEMBER_COUNT.spec.ts | 19 + lib/commands/SRANDMEMBER_COUNT.ts | 13 + lib/commands/SREM.spec.ts | 28 + lib/commands/SREM.ts | 9 + lib/commands/SSCAN.spec.ts | 74 + lib/commands/SSCAN.ts | 24 + lib/commands/STRLEN.spec.ts | 26 + lib/commands/STRLEN.ts | 11 + lib/commands/SUNION.spec.ts | 28 + lib/commands/SUNION.ts | 11 + lib/commands/SUNIONSTORE.spec.ts | 28 + lib/commands/SUNIONSTORE.ts | 9 + lib/commands/SWAPDB.spec.ts | 19 + lib/commands/SWAPDB.ts | 7 + lib/commands/TIME.spec.ts | 18 + lib/commands/TIME.ts | 15 + lib/commands/TOUCH.spec.ts | 28 + lib/commands/TOUCH.ts | 9 + lib/commands/TTL.spec.ts | 19 + lib/commands/TTL.ts | 11 + lib/commands/TYPE.spec.ts | 19 + lib/commands/TYPE.ts | 11 + lib/commands/UNLINK.spec.ts | 28 + lib/commands/UNLINK.ts | 9 + lib/commands/UNWATCH.spec.ts | 26 + lib/commands/UNWATCH.ts | 7 + lib/commands/WAIT.spec.ts | 19 + lib/commands/WAIT.ts | 9 + lib/commands/WATCH.spec.ts | 20 + lib/commands/WATCH.ts | 7 + lib/commands/XACK.spec.ts | 28 + lib/commands/XACK.ts | 9 + lib/commands/XADD.spec.ts | 118 + lib/commands/XADD.ts | 48 + lib/commands/XAUTOCLAIM.spec.ts | 42 + lib/commands/XAUTOCLAIM.ts | 36 + lib/commands/XAUTOCLAIM_JUSTID.spec.ts | 31 + lib/commands/XAUTOCLAIM_JUSTID.ts | 22 + lib/commands/XCLAIM.spec.ts | 90 + lib/commands/XCLAIM.ts | 46 + lib/commands/XCLAIM_JUSTID.spec.ts | 23 + lib/commands/XCLAIM_JUSTID.ts | 13 + lib/commands/XDEL.spec.ts | 28 + lib/commands/XDEL.ts | 9 + lib/commands/XGROUP_CREATE.spec.ts | 32 + lib/commands/XGROUP_CREATE.ts | 19 + lib/commands/XGROUP_CREATECONSUMER.spec.ts | 25 + lib/commands/XGROUP_CREATECONSUMER.ts | 9 + lib/commands/XGROUP_DELCONSUMER.spec.ts | 23 + lib/commands/XGROUP_DELCONSUMER.ts | 9 + lib/commands/XGROUP_DESTROY.spec.ts | 23 + lib/commands/XGROUP_DESTROY.ts | 9 + lib/commands/XGROUP_SETID.spec.ts | 23 + lib/commands/XGROUP_SETID.ts | 9 + lib/commands/XINFO_CONSUMERS.spec.ts | 41 + lib/commands/XINFO_CONSUMERS.ts | 21 + lib/commands/XINFO_GROUPS.spec.ts | 48 + lib/commands/XINFO_GROUPS.ts | 23 + lib/commands/XINFO_STREAM.spec.ts | 72 + lib/commands/XINFO_STREAM.ts | 63 + lib/commands/XLEN.spec.ts | 19 + lib/commands/XLEN.ts | 11 + lib/commands/XPENDING.spec.ts | 30 + lib/commands/XPENDING.ts | 23 + lib/commands/XPENDING_RANGE.spec.ts | 53 + lib/commands/XPENDING_RANGE.ts | 35 + lib/commands/XRANGE.spec.ts | 30 + lib/commands/XRANGE.ts | 21 + lib/commands/XREAD.spec.ts | 87 + lib/commands/XREAD.ts | 43 + lib/commands/XREADGROUP.spec.ts | 137 + lib/commands/XREADGROUP.ts | 57 + lib/commands/XREVRANGE.spec.ts | 30 + lib/commands/XREVRANGE.ts | 17 + lib/commands/XTRIM.spec.ts | 49 + lib/commands/XTRIM.ts | 26 + lib/commands/ZADD.spec.ts | 127 + lib/commands/ZADD.ts | 66 + lib/commands/ZCARD.spec.ts | 19 + lib/commands/ZCARD.ts | 11 + lib/commands/ZCOUNT.spec.ts | 19 + lib/commands/ZCOUNT.ts | 16 + lib/commands/ZDIFF.spec.ts | 30 + lib/commands/ZDIFF.ts | 11 + lib/commands/ZDIFFSTORE.spec.ts | 30 + lib/commands/ZDIFFSTORE.ts | 9 + lib/commands/ZDIFF_WITHSCORES.spec.ts | 30 + lib/commands/ZDIFF_WITHSCORES.ts | 13 + lib/commands/ZINCRBY.spec.ts | 19 + lib/commands/ZINCRBY.ts | 14 + lib/commands/ZINTER.spec.ts | 58 + lib/commands/ZINTER.ts | 29 + lib/commands/ZINTERSTORE.spec.ts | 56 + lib/commands/ZINTERSTORE.ts | 27 + lib/commands/ZINTER_WITHSCORES.spec.ts | 58 + lib/commands/ZINTER_WITHSCORES.ts | 13 + lib/commands/ZLEXCOUNT.spec.ts | 19 + lib/commands/ZLEXCOUNT.ts | 16 + lib/commands/ZMSCORE.spec.ts | 30 + lib/commands/ZMSCORE.ts | 11 + lib/commands/ZPOPMAX.spec.ts | 41 + lib/commands/ZPOPMAX.ts | 19 + lib/commands/ZPOPMAX_COUNT.spec.ts | 19 + lib/commands/ZPOPMAX_COUNT.ts | 13 + lib/commands/ZPOPMIN.spec.ts | 41 + lib/commands/ZPOPMIN.ts | 19 + lib/commands/ZPOPMIN_COUNT.spec.ts | 19 + lib/commands/ZPOPMIN_COUNT.ts | 13 + lib/commands/ZRANDMEMBER.spec.ts | 21 + lib/commands/ZRANDMEMBER.ts | 11 + lib/commands/ZRANDMEMBER_COUNT.spec.ts | 21 + lib/commands/ZRANDMEMBER_COUNT.ts | 13 + .../ZRANDMEMBER_COUNT_WITHSCORES.spec.ts | 21 + lib/commands/ZRANDMEMBER_COUNT_WITHSCORES.ts | 13 + lib/commands/ZRANGE.spec.ts | 74 + lib/commands/ZRANGE.ts | 45 + lib/commands/ZRANGESTORE.spec.ts | 82 + lib/commands/ZRANGESTORE.ts | 55 + lib/commands/ZRANGE_WITHSCORES.spec.ts | 65 + lib/commands/ZRANGE_WITHSCORES.ts | 13 + lib/commands/ZRANK.spec.ts | 19 + lib/commands/ZRANK.ts | 11 + lib/commands/ZREM.spec.ts | 28 + lib/commands/ZREM.ts | 9 + lib/commands/ZREMRANGEBYLEX.spec.ts | 19 + lib/commands/ZREMRANGEBYLEX.ts | 9 + lib/commands/ZREMRANGEBYRANK.spec.ts | 19 + lib/commands/ZREMRANGEBYRANK.ts | 9 + lib/commands/ZREMRANGEBYSCORE.spec.ts | 19 + lib/commands/ZREMRANGEBYSCORE.ts | 9 + lib/commands/ZREVRANK.spec.ts | 19 + lib/commands/ZREVRANK.ts | 11 + lib/commands/ZSCAN.spec.ts | 77 + lib/commands/ZSCAN.ts | 32 + lib/commands/ZSCORE.spec.ts | 19 + lib/commands/ZSCORE.ts | 11 + lib/commands/ZUNION.spec.ts | 48 + lib/commands/ZUNION.ts | 26 + lib/commands/ZUNIONSTORE.spec.ts | 56 + lib/commands/ZUNIONSTORE.ts | 24 + lib/commands/ZUNION_WITHSCORES.spec.ts | 48 + lib/commands/ZUNION_WITHSCORES.ts | 13 + lib/commands/generic-transformers.spec.ts | 623 ++ lib/commands/generic-transformers.ts | 381 + lib/commands/index.ts | 755 ++ lib/createClient.js | 88 - lib/customErrors.js | 58 - lib/debug.js | 13 - lib/errors.ts | 23 + lib/extendedApi.js | 113 - lib/individualCommands.js | 629 -- lib/lua-script.ts | 28 + lib/multi-command.spec.ts | 127 + lib/multi-command.ts | 210 + lib/multi.js | 187 - lib/socket.spec.ts | 38 + lib/socket.ts | 254 + lib/test-utils.ts | 373 + lib/ts-declarations/cluster-key-slot.d.ts | 3 + lib/ts-declarations/redis-parser.d.ts | 13 + lib/utils.js | 134 - lib/utils.ts | 3 + package-lock.json | 9965 +++++++++++++++++ package.json | 74 +- test/auth.spec.js | 354 - test/batch.spec.js | 350 - test/commands/blpop.spec.js | 77 - test/commands/client.spec.js | 155 - test/commands/dbsize.spec.js | 95 - test/commands/del.spec.js | 57 - test/commands/eval.spec.js | 210 - test/commands/exists.spec.js | 40 - test/commands/expire.spec.js | 42 - test/commands/flushdb.spec.js | 105 - test/commands/geoadd.spec.js | 35 - test/commands/get.spec.js | 95 - test/commands/getset.spec.js | 105 - test/commands/hgetall.spec.js | 83 - test/commands/hincrby.spec.js | 40 - test/commands/hlen.spec.js | 38 - test/commands/hmget.spec.js | 71 - test/commands/hmset.spec.js | 117 - test/commands/hset.spec.js | 85 - test/commands/incr.spec.js | 76 - test/commands/info.spec.js | 79 - test/commands/keys.spec.js | 69 - test/commands/mget.spec.js | 70 - test/commands/monitor.spec.js | 215 - test/commands/mset.spec.js | 111 - test/commands/msetnx.spec.js | 38 - test/commands/randomkey.test.js | 35 - test/commands/rename.spec.js | 38 - test/commands/renamenx.spec.js | 41 - test/commands/rpush.spec.js | 36 - test/commands/sadd.spec.js | 62 - test/commands/scard.spec.js | 31 - test/commands/script.spec.js | 55 - test/commands/sdiff.spec.js | 47 - test/commands/sdiffstore.spec.js | 47 - test/commands/select.spec.js | 126 - test/commands/set.spec.js | 178 - test/commands/setex.spec.js | 37 - test/commands/setnx.spec.js | 37 - test/commands/sinter.spec.js | 63 - test/commands/sinterstore.spec.js | 48 - test/commands/sismember.spec.js | 35 - test/commands/slowlog.spec.js | 41 - test/commands/smembers.spec.js | 38 - test/commands/smove.spec.js | 40 - test/commands/sort.spec.js | 130 - test/commands/spop.spec.js | 38 - test/commands/srem.spec.js | 69 - test/commands/sunion.spec.js | 46 - test/commands/sunionstore.spec.js | 49 - test/commands/ttl.spec.js | 37 - test/commands/type.spec.js | 56 - test/commands/watch.spec.js | 54 - test/commands/zadd.spec.js | 52 - test/commands/zscan.spec.js | 50 - test/commands/zscore.spec.js | 35 - test/conect.slave.spec.js | 99 - test/conf/faulty.cert | 19 - test/conf/password.conf | 6 - test/conf/redis.conf | 5 - test/conf/redis.js.org.cert | 19 - test/conf/redis.js.org.key | 27 - test/conf/rename.conf | 8 - test/conf/slave.conf | 7 - test/conf/stunnel.conf.template | 11 - test/connection.spec.js | 637 -- test/custom_errors.spec.js | 89 - test/detect_buffers.spec.js | 268 - test/errors.js | 6 - test/good_traces.spec.js | 59 - test/helper.js | 220 - test/lib/config.js | 37 - test/lib/good-traces.js | 20 - test/lib/redis-process.js | 100 - test/lib/stunnel-process.js | 88 - test/lib/unref.js | 17 - test/multi.spec.js | 737 -- test/node_redis.spec.js | 1043 -- test/prefix.spec.js | 118 - test/pubsub.spec.js | 679 -- test/rename.spec.js | 147 - test/return_buffers.spec.js | 297 - test/tls.spec.js | 151 - test/unify_options.spec.js | 241 - test/utils.spec.js | 185 - tsconfig.json | 37 + 661 files changed, 28837 insertions(+), 14549 deletions(-) delete mode 100644 .deepsource.toml delete mode 100644 .eslintignore delete mode 100644 .eslintrc delete mode 100644 .github/FUNDING.yml delete mode 100644 .github/workflows/codeql-analysis.yml create mode 100644 .github/workflows/documentation.yml delete mode 100644 .github/workflows/linting.yml delete mode 100644 .github/workflows/tests_windows.yml create mode 100644 .nycrc.json delete mode 100644 .prettierrc delete mode 100644 CODE_OF_CONDUCT.md create mode 100644 benchmark/.gitignore create mode 100644 benchmark/index.js create mode 100644 benchmark/package-lock.json create mode 100644 benchmark/package.json delete mode 100755 benchmarks/diff_multi_bench_output.js delete mode 100644 benchmarks/multi_bench.js create mode 100644 docs/FAQ.md create mode 100644 docs/client-configuration.md create mode 100644 docs/isolated-execution.md create mode 100644 docs/v3-to-v4.md delete mode 100644 examples/auth.js delete mode 100644 examples/backpressure_drain.js delete mode 100644 examples/eval.js delete mode 100644 examples/extend.js delete mode 100644 examples/file.js delete mode 100644 examples/mget.js delete mode 100644 examples/monitor.js delete mode 100644 examples/multi.js delete mode 100644 examples/multi2.js delete mode 100644 examples/psubscribe.js delete mode 100644 examples/pub_sub.js delete mode 100644 examples/scan.js delete mode 100644 examples/simple.js delete mode 100644 examples/sort.js delete mode 100644 examples/streams.js delete mode 100644 examples/subqueries.js delete mode 100644 examples/subquery.js delete mode 100644 examples/unix_socket.js delete mode 100644 examples/web_server.js delete mode 100644 index.js create mode 100644 index.ts create mode 100644 lib/client.spec.ts create mode 100644 lib/client.ts create mode 100644 lib/cluster-slots.ts create mode 100644 lib/cluster.spec.ts create mode 100644 lib/cluster.ts create mode 100644 lib/command-options.ts delete mode 100644 lib/command.js create mode 100644 lib/commander.spec.ts create mode 100644 lib/commander.ts create mode 100644 lib/commands-queue.ts delete mode 100644 lib/commands.js create mode 100644 lib/commands/ACL_CAT.spec.ts create mode 100644 lib/commands/ACL_CAT.ts create mode 100644 lib/commands/ACL_DELUSER.spec.ts create mode 100644 lib/commands/ACL_DELUSER.ts create mode 100644 lib/commands/ACL_GENPASS.spec.ts create mode 100644 lib/commands/ACL_GENPASS.ts create mode 100644 lib/commands/ACL_GETUSER.spec.ts create mode 100644 lib/commands/ACL_GETUSER.ts create mode 100644 lib/commands/ACL_LIST.spec.ts create mode 100644 lib/commands/ACL_LIST.ts create mode 100644 lib/commands/ACL_LOAD.spec.ts create mode 100644 lib/commands/ACL_LOAD.ts create mode 100644 lib/commands/ACL_LOG.spec.ts create mode 100644 lib/commands/ACL_LOG.ts create mode 100644 lib/commands/ACL_LOG_RESET.spec.ts create mode 100644 lib/commands/ACL_LOG_RESET.ts create mode 100644 lib/commands/ACL_SAVE.spec.ts create mode 100644 lib/commands/ACL_SAVE.ts create mode 100644 lib/commands/ACL_SETUSER.spec.ts create mode 100644 lib/commands/ACL_SETUSER.ts create mode 100644 lib/commands/ACL_USERS.spec.ts create mode 100644 lib/commands/ACL_USERS.ts create mode 100644 lib/commands/ACL_WHOAMI.spec.ts create mode 100644 lib/commands/ACL_WHOAMI.ts create mode 100644 lib/commands/APPEND.spec.ts create mode 100644 lib/commands/APPEND.ts create mode 100644 lib/commands/ASKING.spec.ts create mode 100644 lib/commands/ASKING.ts create mode 100644 lib/commands/AUTH.spec.ts create mode 100644 lib/commands/AUTH.ts create mode 100644 lib/commands/BGREWRITEAOF.spec.ts create mode 100644 lib/commands/BGREWRITEAOF.ts create mode 100644 lib/commands/BGSAVE.spec.ts create mode 100644 lib/commands/BGSAVE.ts create mode 100644 lib/commands/BITCOUNT.spec.ts create mode 100644 lib/commands/BITCOUNT.ts create mode 100644 lib/commands/BITFIELD.spec.ts create mode 100644 lib/commands/BITFIELD.ts create mode 100644 lib/commands/BITOP.spec.ts create mode 100644 lib/commands/BITOP.ts create mode 100644 lib/commands/BITPOS.spec.ts create mode 100644 lib/commands/BITPOS.ts create mode 100644 lib/commands/BLMOVE.spec.ts create mode 100644 lib/commands/BLMOVE.ts create mode 100644 lib/commands/BLPOP.spec.ts create mode 100644 lib/commands/BLPOP.ts create mode 100644 lib/commands/BRPOP.spec.ts create mode 100644 lib/commands/BRPOP.ts create mode 100644 lib/commands/BRPOPLPUSH.spec.ts create mode 100644 lib/commands/BRPOPLPUSH.ts create mode 100644 lib/commands/BZPOPMAX.spec.ts create mode 100644 lib/commands/BZPOPMAX.ts create mode 100644 lib/commands/BZPOPMIN.spec.ts create mode 100644 lib/commands/BZPOPMIN.ts create mode 100644 lib/commands/CLIENT_ID.spec.ts create mode 100644 lib/commands/CLIENT_ID.ts create mode 100644 lib/commands/CLIENT_INFO.spec.ts create mode 100644 lib/commands/CLIENT_INFO.ts create mode 100644 lib/commands/CLUSTER_ADDSLOTS.spec.ts create mode 100644 lib/commands/CLUSTER_ADDSLOTS.ts create mode 100644 lib/commands/CLUSTER_FLUSHSLOTS.spec.ts create mode 100644 lib/commands/CLUSTER_FLUSHSLOTS.ts create mode 100644 lib/commands/CLUSTER_GETKEYSINSLOT.spec.ts create mode 100644 lib/commands/CLUSTER_GETKEYSINSLOT.ts create mode 100644 lib/commands/CLUSTER_INFO.spec.ts create mode 100644 lib/commands/CLUSTER_INFO.ts create mode 100644 lib/commands/CLUSTER_MEET.spec.ts create mode 100644 lib/commands/CLUSTER_MEET.ts create mode 100644 lib/commands/CLUSTER_NODES.spec.ts create mode 100644 lib/commands/CLUSTER_NODES.ts create mode 100644 lib/commands/CLUSTER_RESET.spec.ts create mode 100644 lib/commands/CLUSTER_RESET.ts create mode 100644 lib/commands/CLUSTER_SETSLOT.spec.ts create mode 100644 lib/commands/CLUSTER_SETSLOT.ts create mode 100644 lib/commands/CONFIG_GET.spec.ts create mode 100644 lib/commands/CONFIG_GET.ts create mode 100644 lib/commands/CONFIG_RESETSTAT.spec.ts create mode 100644 lib/commands/CONFIG_RESETSTAT.ts create mode 100644 lib/commands/CONFIG_REWRITE.spec.ts create mode 100644 lib/commands/CONFIG_REWRITE.ts create mode 100644 lib/commands/CONFIG_SET.spec.ts create mode 100644 lib/commands/CONFIG_SET.ts create mode 100644 lib/commands/COPY.spec.ts create mode 100644 lib/commands/COPY.ts create mode 100644 lib/commands/DBSIZE.spec.ts create mode 100644 lib/commands/DBSIZE.ts create mode 100644 lib/commands/DECR.spec.ts create mode 100644 lib/commands/DECR.ts create mode 100644 lib/commands/DECRBY.spec.ts create mode 100644 lib/commands/DECRBY.ts create mode 100644 lib/commands/DEL.spec.ts create mode 100644 lib/commands/DEL.ts create mode 100644 lib/commands/DISCARD.spec.ts create mode 100644 lib/commands/DISCARD.ts create mode 100644 lib/commands/DUMP.spec.ts create mode 100644 lib/commands/DUMP.ts create mode 100644 lib/commands/ECHO.spec.ts create mode 100644 lib/commands/ECHO.ts create mode 100644 lib/commands/EVAL.spec.ts create mode 100644 lib/commands/EVAL.ts create mode 100644 lib/commands/EVALSHA.spec.ts create mode 100644 lib/commands/EVALSHA.ts create mode 100644 lib/commands/EXISTS.spec.ts create mode 100644 lib/commands/EXISTS.ts create mode 100644 lib/commands/EXPIRE.spec.ts create mode 100644 lib/commands/EXPIRE.ts create mode 100644 lib/commands/EXPIREAT.spec.ts create mode 100644 lib/commands/EXPIREAT.ts create mode 100644 lib/commands/FAILOVER.spec.ts create mode 100644 lib/commands/FAILOVER.ts create mode 100644 lib/commands/FLUSHALL.spec.ts create mode 100644 lib/commands/FLUSHALL.ts create mode 100644 lib/commands/FLUSHDB.spec.ts create mode 100644 lib/commands/FLUSHDB.ts create mode 100644 lib/commands/GEOADD.spec.ts create mode 100644 lib/commands/GEOADD.ts create mode 100644 lib/commands/GEODIST.spec.ts create mode 100644 lib/commands/GEODIST.ts create mode 100644 lib/commands/GEOHASH.spec.ts create mode 100644 lib/commands/GEOHASH.ts create mode 100644 lib/commands/GEOPOS.spec.ts create mode 100644 lib/commands/GEOPOS.ts create mode 100644 lib/commands/GEOSEARCH.spec.ts create mode 100644 lib/commands/GEOSEARCH.ts create mode 100644 lib/commands/GEOSEARCHSTORE.spec.ts create mode 100644 lib/commands/GEOSEARCHSTORE.ts create mode 100644 lib/commands/GEOSEARCH_WITH.spec.ts create mode 100644 lib/commands/GEOSEARCH_WITH.ts create mode 100644 lib/commands/GET.spec.ts create mode 100644 lib/commands/GET.ts create mode 100644 lib/commands/GETBIT.spec.ts create mode 100644 lib/commands/GETBIT.ts create mode 100644 lib/commands/GETDEL.spec.ts create mode 100644 lib/commands/GETDEL.ts create mode 100644 lib/commands/GETEX.spec.ts create mode 100644 lib/commands/GETEX.ts create mode 100644 lib/commands/GETRANGE.spec.ts create mode 100644 lib/commands/GETRANGE.ts create mode 100644 lib/commands/GETSET.spec.ts create mode 100644 lib/commands/GETSET.ts create mode 100644 lib/commands/HDEL.spec.ts create mode 100644 lib/commands/HDEL.ts create mode 100644 lib/commands/HELLO.spec.ts create mode 100644 lib/commands/HELLO.ts create mode 100644 lib/commands/HEXISTS.spec.ts create mode 100644 lib/commands/HEXISTS.ts create mode 100644 lib/commands/HGET.spec.ts create mode 100644 lib/commands/HGET.ts create mode 100644 lib/commands/HGETALL.spec.ts create mode 100644 lib/commands/HGETALL.ts create mode 100644 lib/commands/HINCRBY.spec.ts create mode 100644 lib/commands/HINCRBY.ts create mode 100644 lib/commands/HINCRBYFLOAT.spec.ts create mode 100644 lib/commands/HINCRBYFLOAT.ts create mode 100644 lib/commands/HKEYS.spec.ts create mode 100644 lib/commands/HKEYS.ts create mode 100644 lib/commands/HLEN.spec.ts create mode 100644 lib/commands/HLEN.ts create mode 100644 lib/commands/HMGET.spec.ts create mode 100644 lib/commands/HMGET.ts create mode 100644 lib/commands/HRANDFIELD.spec.ts create mode 100644 lib/commands/HRANDFIELD.ts create mode 100644 lib/commands/HRANDFIELD_COUNT.spec.ts create mode 100644 lib/commands/HRANDFIELD_COUNT.ts create mode 100644 lib/commands/HRANDFIELD_COUNT_WITHVALUES.spec.ts create mode 100644 lib/commands/HRANDFIELD_COUNT_WITHVALUES.ts create mode 100644 lib/commands/HSCAN.spec.ts create mode 100644 lib/commands/HSCAN.ts create mode 100644 lib/commands/HSET.spec.ts create mode 100644 lib/commands/HSET.ts create mode 100644 lib/commands/HSETNX.spec.ts create mode 100644 lib/commands/HSETNX.ts create mode 100644 lib/commands/HSTRLEN.spec.ts create mode 100644 lib/commands/HSTRLEN.ts create mode 100644 lib/commands/HVALS.spec.ts create mode 100644 lib/commands/HVALS.ts create mode 100644 lib/commands/INCR.spec.ts create mode 100644 lib/commands/INCR.ts create mode 100644 lib/commands/INCRBY.spec.ts create mode 100644 lib/commands/INCRBY.ts create mode 100644 lib/commands/INCRBYFLOAT.spec.ts create mode 100644 lib/commands/INCRBYFLOAT.ts create mode 100644 lib/commands/INFO.spec.ts create mode 100644 lib/commands/INFO.ts create mode 100644 lib/commands/KEYS.spec.ts create mode 100644 lib/commands/KEYS.ts create mode 100644 lib/commands/LASTSAVE.spec.ts create mode 100644 lib/commands/LASTSAVE.ts create mode 100644 lib/commands/LINDEX.spec.ts create mode 100644 lib/commands/LINDEX.ts create mode 100644 lib/commands/LINSERT.spec.ts create mode 100644 lib/commands/LINSERT.ts create mode 100644 lib/commands/LLEN.spec.ts create mode 100644 lib/commands/LLEN.ts create mode 100644 lib/commands/LMOVE.spec.ts create mode 100644 lib/commands/LMOVE.ts create mode 100644 lib/commands/LOLWUT.spec.ts create mode 100644 lib/commands/LOLWUT.ts create mode 100644 lib/commands/LPOP.spec.ts create mode 100644 lib/commands/LPOP.ts create mode 100644 lib/commands/LPOP_COUNT.spec.ts create mode 100644 lib/commands/LPOP_COUNT.ts create mode 100644 lib/commands/LPOS.spec.ts create mode 100644 lib/commands/LPOS.ts create mode 100644 lib/commands/LPOS_COUNT.spec.ts create mode 100644 lib/commands/LPOS_COUNT.ts create mode 100644 lib/commands/LPUSH.spec.ts create mode 100644 lib/commands/LPUSH.ts create mode 100644 lib/commands/LPUSHX.spec.ts create mode 100644 lib/commands/LPUSHX.ts create mode 100644 lib/commands/LRANGE.spec.ts create mode 100644 lib/commands/LRANGE.ts create mode 100644 lib/commands/LREM.spec.ts create mode 100644 lib/commands/LREM.ts create mode 100644 lib/commands/LSET.spec.ts create mode 100644 lib/commands/LSET.ts create mode 100644 lib/commands/LTRIM.spec.ts create mode 100644 lib/commands/LTRIM.ts create mode 100644 lib/commands/MEMORY_DOCTOR.spec.ts create mode 100644 lib/commands/MEMORY_DOCTOR.ts create mode 100644 lib/commands/MEMORY_MALLOC-STATS.spec.ts create mode 100644 lib/commands/MEMORY_MALLOC-STATS.ts create mode 100644 lib/commands/MEMORY_PURGE.spec.ts create mode 100644 lib/commands/MEMORY_PURGE.ts create mode 100644 lib/commands/MEMORY_STATS.spec.ts create mode 100644 lib/commands/MEMORY_STATS.ts create mode 100644 lib/commands/MEMORY_USAGE.spec.ts create mode 100644 lib/commands/MEMORY_USAGE.ts create mode 100644 lib/commands/MGET.spec.ts create mode 100644 lib/commands/MGET.ts create mode 100644 lib/commands/MIGRATE.spec.ts create mode 100644 lib/commands/MIGRATE.ts create mode 100644 lib/commands/MODULE_LIST.spec.ts create mode 100644 lib/commands/MODULE_LIST.ts create mode 100644 lib/commands/MODULE_LOAD.spec.ts create mode 100644 lib/commands/MODULE_LOAD.ts create mode 100644 lib/commands/MODULE_UNLOAD.spec.ts create mode 100644 lib/commands/MODULE_UNLOAD.ts create mode 100644 lib/commands/MOVE.spec.ts create mode 100644 lib/commands/MOVE.ts create mode 100644 lib/commands/MSET.spec.ts create mode 100644 lib/commands/MSET.ts create mode 100644 lib/commands/MSETNX.spec.ts create mode 100644 lib/commands/MSETNX.ts create mode 100644 lib/commands/PERSIST.spec.ts create mode 100644 lib/commands/PERSIST.ts create mode 100644 lib/commands/PEXPIRE.spec.ts create mode 100644 lib/commands/PEXPIRE.ts create mode 100644 lib/commands/PEXPIREAT.spec.ts create mode 100644 lib/commands/PEXPIREAT.ts create mode 100644 lib/commands/PFADD.spec.ts create mode 100644 lib/commands/PFADD.ts create mode 100644 lib/commands/PFCOUNT.spec.ts create mode 100644 lib/commands/PFCOUNT.ts create mode 100644 lib/commands/PFMERGE.spec.ts create mode 100644 lib/commands/PFMERGE.ts create mode 100644 lib/commands/PING.spec.ts create mode 100644 lib/commands/PING.ts create mode 100644 lib/commands/PSETEX.spec.ts create mode 100644 lib/commands/PSETEX.ts create mode 100644 lib/commands/PTTL.spec.ts create mode 100644 lib/commands/PTTL.ts create mode 100644 lib/commands/PUBLISH.spec.ts create mode 100644 lib/commands/PUBLISH.ts create mode 100644 lib/commands/PUBSUB_CHANNELS.spec.ts create mode 100644 lib/commands/PUBSUB_CHANNELS.ts create mode 100644 lib/commands/PUBSUB_NUMPAT.spec.ts create mode 100644 lib/commands/PUBSUB_NUMPAT.ts create mode 100644 lib/commands/PUBSUB_NUMSUB.spec.ts create mode 100644 lib/commands/PUBSUB_NUMSUB.ts create mode 100644 lib/commands/RANDOMKEY.spec.ts create mode 100644 lib/commands/RANDOMKEY.ts create mode 100644 lib/commands/READONLY.spec.ts create mode 100644 lib/commands/READONLY.ts create mode 100644 lib/commands/READWRITE.spec.ts create mode 100644 lib/commands/READWRITE.ts create mode 100644 lib/commands/RENAME.spec.ts create mode 100644 lib/commands/RENAME.ts create mode 100644 lib/commands/RENAMENX.spec.ts create mode 100644 lib/commands/RENAMENX.ts create mode 100644 lib/commands/REPLICAOF.spec.ts create mode 100644 lib/commands/REPLICAOF.ts create mode 100644 lib/commands/RESTORE-ASKING.spec.ts create mode 100644 lib/commands/RESTORE-ASKING.ts create mode 100644 lib/commands/ROLE.spec.ts create mode 100644 lib/commands/ROLE.ts create mode 100644 lib/commands/RPOP.spec.ts create mode 100644 lib/commands/RPOP.ts create mode 100644 lib/commands/RPOPLPUSH.spec.ts create mode 100644 lib/commands/RPOPLPUSH.ts create mode 100644 lib/commands/RPOP_COUNT.spec.ts create mode 100644 lib/commands/RPOP_COUNT.ts create mode 100644 lib/commands/RPUSH.spec.ts create mode 100644 lib/commands/RPUSH.ts create mode 100644 lib/commands/RPUSHX.spec.ts create mode 100644 lib/commands/RPUSHX.ts create mode 100644 lib/commands/SADD.spec.ts create mode 100644 lib/commands/SADD.ts create mode 100644 lib/commands/SAVE.spec.ts create mode 100644 lib/commands/SAVE.ts create mode 100644 lib/commands/SCAN.spec.ts create mode 100644 lib/commands/SCAN.ts create mode 100644 lib/commands/SCARD.spec.ts create mode 100644 lib/commands/SCARD.ts create mode 100644 lib/commands/SCRIPT_DEBUG.spec.ts create mode 100644 lib/commands/SCRIPT_DEBUG.ts create mode 100644 lib/commands/SCRIPT_EXISTS.spec.ts create mode 100644 lib/commands/SCRIPT_EXISTS.ts create mode 100644 lib/commands/SCRIPT_FLUSH.spec.ts create mode 100644 lib/commands/SCRIPT_FLUSH.ts create mode 100644 lib/commands/SCRIPT_KILL.spec.ts create mode 100644 lib/commands/SCRIPT_KILL.ts create mode 100644 lib/commands/SCRIPT_LOAD.spec.ts create mode 100644 lib/commands/SCRIPT_LOAD.ts create mode 100644 lib/commands/SDIFF.spec.ts create mode 100644 lib/commands/SDIFF.ts create mode 100644 lib/commands/SDIFFSTORE.spec.ts create mode 100644 lib/commands/SDIFFSTORE.ts create mode 100644 lib/commands/SET.spec.ts create mode 100644 lib/commands/SET.ts create mode 100644 lib/commands/SETBIT.spec.ts create mode 100644 lib/commands/SETBIT.ts create mode 100644 lib/commands/SETEX.spec.ts create mode 100644 lib/commands/SETEX.ts create mode 100644 lib/commands/SETNX .spec.ts create mode 100644 lib/commands/SETNX.ts create mode 100644 lib/commands/SETRANGE.spec.ts create mode 100644 lib/commands/SETRANGE.ts create mode 100644 lib/commands/SHUTDOWN.spec.ts create mode 100644 lib/commands/SHUTDOWN.ts create mode 100644 lib/commands/SINTER.spec.ts create mode 100644 lib/commands/SINTER.ts create mode 100644 lib/commands/SINTERSTORE.spec.ts create mode 100644 lib/commands/SINTERSTORE.ts create mode 100644 lib/commands/SISMEMBER.spec.ts create mode 100644 lib/commands/SISMEMBER.ts create mode 100644 lib/commands/SMEMBERS.spec.ts create mode 100644 lib/commands/SMEMBERS.ts create mode 100644 lib/commands/SMISMEMBER.spec.ts create mode 100644 lib/commands/SMISMEMBER.ts create mode 100644 lib/commands/SMOVE.spec.ts create mode 100644 lib/commands/SMOVE.ts create mode 100644 lib/commands/SORT.spec.ts create mode 100644 lib/commands/SORT.ts create mode 100644 lib/commands/SPOP.spec.ts create mode 100644 lib/commands/SPOP.ts create mode 100644 lib/commands/SRANDMEMBER.spec.ts create mode 100644 lib/commands/SRANDMEMBER.ts create mode 100644 lib/commands/SRANDMEMBER_COUNT.spec.ts create mode 100644 lib/commands/SRANDMEMBER_COUNT.ts create mode 100644 lib/commands/SREM.spec.ts create mode 100644 lib/commands/SREM.ts create mode 100644 lib/commands/SSCAN.spec.ts create mode 100644 lib/commands/SSCAN.ts create mode 100644 lib/commands/STRLEN.spec.ts create mode 100644 lib/commands/STRLEN.ts create mode 100644 lib/commands/SUNION.spec.ts create mode 100644 lib/commands/SUNION.ts create mode 100644 lib/commands/SUNIONSTORE.spec.ts create mode 100644 lib/commands/SUNIONSTORE.ts create mode 100644 lib/commands/SWAPDB.spec.ts create mode 100644 lib/commands/SWAPDB.ts create mode 100644 lib/commands/TIME.spec.ts create mode 100644 lib/commands/TIME.ts create mode 100644 lib/commands/TOUCH.spec.ts create mode 100644 lib/commands/TOUCH.ts create mode 100644 lib/commands/TTL.spec.ts create mode 100644 lib/commands/TTL.ts create mode 100644 lib/commands/TYPE.spec.ts create mode 100644 lib/commands/TYPE.ts create mode 100644 lib/commands/UNLINK.spec.ts create mode 100644 lib/commands/UNLINK.ts create mode 100644 lib/commands/UNWATCH.spec.ts create mode 100644 lib/commands/UNWATCH.ts create mode 100644 lib/commands/WAIT.spec.ts create mode 100644 lib/commands/WAIT.ts create mode 100644 lib/commands/WATCH.spec.ts create mode 100644 lib/commands/WATCH.ts create mode 100644 lib/commands/XACK.spec.ts create mode 100644 lib/commands/XACK.ts create mode 100644 lib/commands/XADD.spec.ts create mode 100644 lib/commands/XADD.ts create mode 100644 lib/commands/XAUTOCLAIM.spec.ts create mode 100644 lib/commands/XAUTOCLAIM.ts create mode 100644 lib/commands/XAUTOCLAIM_JUSTID.spec.ts create mode 100644 lib/commands/XAUTOCLAIM_JUSTID.ts create mode 100644 lib/commands/XCLAIM.spec.ts create mode 100644 lib/commands/XCLAIM.ts create mode 100644 lib/commands/XCLAIM_JUSTID.spec.ts create mode 100644 lib/commands/XCLAIM_JUSTID.ts create mode 100644 lib/commands/XDEL.spec.ts create mode 100644 lib/commands/XDEL.ts create mode 100644 lib/commands/XGROUP_CREATE.spec.ts create mode 100644 lib/commands/XGROUP_CREATE.ts create mode 100644 lib/commands/XGROUP_CREATECONSUMER.spec.ts create mode 100644 lib/commands/XGROUP_CREATECONSUMER.ts create mode 100644 lib/commands/XGROUP_DELCONSUMER.spec.ts create mode 100644 lib/commands/XGROUP_DELCONSUMER.ts create mode 100644 lib/commands/XGROUP_DESTROY.spec.ts create mode 100644 lib/commands/XGROUP_DESTROY.ts create mode 100644 lib/commands/XGROUP_SETID.spec.ts create mode 100644 lib/commands/XGROUP_SETID.ts create mode 100644 lib/commands/XINFO_CONSUMERS.spec.ts create mode 100644 lib/commands/XINFO_CONSUMERS.ts create mode 100644 lib/commands/XINFO_GROUPS.spec.ts create mode 100644 lib/commands/XINFO_GROUPS.ts create mode 100644 lib/commands/XINFO_STREAM.spec.ts create mode 100644 lib/commands/XINFO_STREAM.ts create mode 100644 lib/commands/XLEN.spec.ts create mode 100644 lib/commands/XLEN.ts create mode 100644 lib/commands/XPENDING.spec.ts create mode 100644 lib/commands/XPENDING.ts create mode 100644 lib/commands/XPENDING_RANGE.spec.ts create mode 100644 lib/commands/XPENDING_RANGE.ts create mode 100644 lib/commands/XRANGE.spec.ts create mode 100644 lib/commands/XRANGE.ts create mode 100644 lib/commands/XREAD.spec.ts create mode 100644 lib/commands/XREAD.ts create mode 100644 lib/commands/XREADGROUP.spec.ts create mode 100644 lib/commands/XREADGROUP.ts create mode 100644 lib/commands/XREVRANGE.spec.ts create mode 100644 lib/commands/XREVRANGE.ts create mode 100644 lib/commands/XTRIM.spec.ts create mode 100644 lib/commands/XTRIM.ts create mode 100644 lib/commands/ZADD.spec.ts create mode 100644 lib/commands/ZADD.ts create mode 100644 lib/commands/ZCARD.spec.ts create mode 100644 lib/commands/ZCARD.ts create mode 100644 lib/commands/ZCOUNT.spec.ts create mode 100644 lib/commands/ZCOUNT.ts create mode 100644 lib/commands/ZDIFF.spec.ts create mode 100644 lib/commands/ZDIFF.ts create mode 100644 lib/commands/ZDIFFSTORE.spec.ts create mode 100644 lib/commands/ZDIFFSTORE.ts create mode 100644 lib/commands/ZDIFF_WITHSCORES.spec.ts create mode 100644 lib/commands/ZDIFF_WITHSCORES.ts create mode 100644 lib/commands/ZINCRBY.spec.ts create mode 100644 lib/commands/ZINCRBY.ts create mode 100644 lib/commands/ZINTER.spec.ts create mode 100644 lib/commands/ZINTER.ts create mode 100644 lib/commands/ZINTERSTORE.spec.ts create mode 100644 lib/commands/ZINTERSTORE.ts create mode 100644 lib/commands/ZINTER_WITHSCORES.spec.ts create mode 100644 lib/commands/ZINTER_WITHSCORES.ts create mode 100644 lib/commands/ZLEXCOUNT.spec.ts create mode 100644 lib/commands/ZLEXCOUNT.ts create mode 100644 lib/commands/ZMSCORE.spec.ts create mode 100644 lib/commands/ZMSCORE.ts create mode 100644 lib/commands/ZPOPMAX.spec.ts create mode 100644 lib/commands/ZPOPMAX.ts create mode 100644 lib/commands/ZPOPMAX_COUNT.spec.ts create mode 100644 lib/commands/ZPOPMAX_COUNT.ts create mode 100644 lib/commands/ZPOPMIN.spec.ts create mode 100644 lib/commands/ZPOPMIN.ts create mode 100644 lib/commands/ZPOPMIN_COUNT.spec.ts create mode 100644 lib/commands/ZPOPMIN_COUNT.ts create mode 100644 lib/commands/ZRANDMEMBER.spec.ts create mode 100644 lib/commands/ZRANDMEMBER.ts create mode 100644 lib/commands/ZRANDMEMBER_COUNT.spec.ts create mode 100644 lib/commands/ZRANDMEMBER_COUNT.ts create mode 100644 lib/commands/ZRANDMEMBER_COUNT_WITHSCORES.spec.ts create mode 100644 lib/commands/ZRANDMEMBER_COUNT_WITHSCORES.ts create mode 100644 lib/commands/ZRANGE.spec.ts create mode 100644 lib/commands/ZRANGE.ts create mode 100644 lib/commands/ZRANGESTORE.spec.ts create mode 100644 lib/commands/ZRANGESTORE.ts create mode 100644 lib/commands/ZRANGE_WITHSCORES.spec.ts create mode 100644 lib/commands/ZRANGE_WITHSCORES.ts create mode 100644 lib/commands/ZRANK.spec.ts create mode 100644 lib/commands/ZRANK.ts create mode 100644 lib/commands/ZREM.spec.ts create mode 100644 lib/commands/ZREM.ts create mode 100644 lib/commands/ZREMRANGEBYLEX.spec.ts create mode 100644 lib/commands/ZREMRANGEBYLEX.ts create mode 100644 lib/commands/ZREMRANGEBYRANK.spec.ts create mode 100644 lib/commands/ZREMRANGEBYRANK.ts create mode 100644 lib/commands/ZREMRANGEBYSCORE.spec.ts create mode 100644 lib/commands/ZREMRANGEBYSCORE.ts create mode 100644 lib/commands/ZREVRANK.spec.ts create mode 100644 lib/commands/ZREVRANK.ts create mode 100644 lib/commands/ZSCAN.spec.ts create mode 100644 lib/commands/ZSCAN.ts create mode 100644 lib/commands/ZSCORE.spec.ts create mode 100644 lib/commands/ZSCORE.ts create mode 100644 lib/commands/ZUNION.spec.ts create mode 100644 lib/commands/ZUNION.ts create mode 100644 lib/commands/ZUNIONSTORE.spec.ts create mode 100644 lib/commands/ZUNIONSTORE.ts create mode 100644 lib/commands/ZUNION_WITHSCORES.spec.ts create mode 100644 lib/commands/ZUNION_WITHSCORES.ts create mode 100644 lib/commands/generic-transformers.spec.ts create mode 100644 lib/commands/generic-transformers.ts create mode 100644 lib/commands/index.ts delete mode 100644 lib/createClient.js delete mode 100644 lib/customErrors.js delete mode 100644 lib/debug.js create mode 100644 lib/errors.ts delete mode 100644 lib/extendedApi.js delete mode 100644 lib/individualCommands.js create mode 100644 lib/lua-script.ts create mode 100644 lib/multi-command.spec.ts create mode 100644 lib/multi-command.ts delete mode 100644 lib/multi.js create mode 100644 lib/socket.spec.ts create mode 100644 lib/socket.ts create mode 100644 lib/test-utils.ts create mode 100644 lib/ts-declarations/cluster-key-slot.d.ts create mode 100644 lib/ts-declarations/redis-parser.d.ts delete mode 100644 lib/utils.js create mode 100644 lib/utils.ts create mode 100644 package-lock.json delete mode 100644 test/auth.spec.js delete mode 100644 test/batch.spec.js delete mode 100644 test/commands/blpop.spec.js delete mode 100644 test/commands/client.spec.js delete mode 100644 test/commands/dbsize.spec.js delete mode 100644 test/commands/del.spec.js delete mode 100644 test/commands/eval.spec.js delete mode 100644 test/commands/exists.spec.js delete mode 100644 test/commands/expire.spec.js delete mode 100644 test/commands/flushdb.spec.js delete mode 100644 test/commands/geoadd.spec.js delete mode 100644 test/commands/get.spec.js delete mode 100644 test/commands/getset.spec.js delete mode 100644 test/commands/hgetall.spec.js delete mode 100644 test/commands/hincrby.spec.js delete mode 100644 test/commands/hlen.spec.js delete mode 100644 test/commands/hmget.spec.js delete mode 100644 test/commands/hmset.spec.js delete mode 100644 test/commands/hset.spec.js delete mode 100644 test/commands/incr.spec.js delete mode 100644 test/commands/info.spec.js delete mode 100644 test/commands/keys.spec.js delete mode 100644 test/commands/mget.spec.js delete mode 100644 test/commands/monitor.spec.js delete mode 100644 test/commands/mset.spec.js delete mode 100644 test/commands/msetnx.spec.js delete mode 100644 test/commands/randomkey.test.js delete mode 100644 test/commands/rename.spec.js delete mode 100644 test/commands/renamenx.spec.js delete mode 100644 test/commands/rpush.spec.js delete mode 100644 test/commands/sadd.spec.js delete mode 100644 test/commands/scard.spec.js delete mode 100644 test/commands/script.spec.js delete mode 100644 test/commands/sdiff.spec.js delete mode 100644 test/commands/sdiffstore.spec.js delete mode 100644 test/commands/select.spec.js delete mode 100644 test/commands/set.spec.js delete mode 100644 test/commands/setex.spec.js delete mode 100644 test/commands/setnx.spec.js delete mode 100644 test/commands/sinter.spec.js delete mode 100644 test/commands/sinterstore.spec.js delete mode 100644 test/commands/sismember.spec.js delete mode 100644 test/commands/slowlog.spec.js delete mode 100644 test/commands/smembers.spec.js delete mode 100644 test/commands/smove.spec.js delete mode 100644 test/commands/sort.spec.js delete mode 100644 test/commands/spop.spec.js delete mode 100644 test/commands/srem.spec.js delete mode 100644 test/commands/sunion.spec.js delete mode 100644 test/commands/sunionstore.spec.js delete mode 100644 test/commands/ttl.spec.js delete mode 100644 test/commands/type.spec.js delete mode 100644 test/commands/watch.spec.js delete mode 100644 test/commands/zadd.spec.js delete mode 100644 test/commands/zscan.spec.js delete mode 100644 test/commands/zscore.spec.js delete mode 100644 test/conect.slave.spec.js delete mode 100644 test/conf/faulty.cert delete mode 100644 test/conf/password.conf delete mode 100644 test/conf/redis.conf delete mode 100644 test/conf/redis.js.org.cert delete mode 100644 test/conf/redis.js.org.key delete mode 100644 test/conf/rename.conf delete mode 100644 test/conf/slave.conf delete mode 100644 test/conf/stunnel.conf.template delete mode 100644 test/connection.spec.js delete mode 100644 test/custom_errors.spec.js delete mode 100644 test/detect_buffers.spec.js delete mode 100644 test/errors.js delete mode 100644 test/good_traces.spec.js delete mode 100644 test/helper.js delete mode 100644 test/lib/config.js delete mode 100644 test/lib/good-traces.js delete mode 100644 test/lib/redis-process.js delete mode 100644 test/lib/stunnel-process.js delete mode 100644 test/lib/unref.js delete mode 100644 test/multi.spec.js delete mode 100644 test/node_redis.spec.js delete mode 100644 test/prefix.spec.js delete mode 100644 test/pubsub.spec.js delete mode 100644 test/rename.spec.js delete mode 100644 test/return_buffers.spec.js delete mode 100644 test/tls.spec.js delete mode 100644 test/unify_options.spec.js delete mode 100644 test/utils.spec.js create mode 100644 tsconfig.json diff --git a/.deepsource.toml b/.deepsource.toml deleted file mode 100644 index 34bfad29479..00000000000 --- a/.deepsource.toml +++ /dev/null @@ -1,9 +0,0 @@ -version = 1 -exclude_patterns = ["examples/**"] - -[[analyzers]] -name = "javascript" -enabled = true - - [analyzers.meta] - environment = ["nodejs"] diff --git a/.eslintignore b/.eslintignore deleted file mode 100644 index fd16de30886..00000000000 --- a/.eslintignore +++ /dev/null @@ -1,4 +0,0 @@ -node_modules/** -coverage/** -**.md -**.log diff --git a/.eslintrc b/.eslintrc deleted file mode 100644 index 81e5e9b9cfd..00000000000 --- a/.eslintrc +++ /dev/null @@ -1,109 +0,0 @@ -env: - node: true - es6: false - -rules: - # Possible Errors - # http://eslint.org/docs/rules/#possible-errors - comma-dangle: [2, "only-multiline"] - no-constant-condition: 2 - no-control-regex: 2 - no-debugger: 2 - no-dupe-args: 2 - no-dupe-keys: 2 - no-duplicate-case: 2 - no-empty: 2 - no-empty-character-class: 2 - no-ex-assign: 2 - no-extra-boolean-cast : 2 - no-extra-parens: [2, "functions"] - no-extra-semi: 2 - no-func-assign: 2 - no-invalid-regexp: 2 - no-irregular-whitespace: 2 - no-negated-in-lhs: 2 - no-obj-calls: 2 - no-regex-spaces: 2 - no-sparse-arrays: 2 - no-inner-declarations: 2 - no-unexpected-multiline: 2 - no-unreachable: 2 - use-isnan: 2 - valid-typeof: 2 - - # Best Practices - # http://eslint.org/docs/rules/#best-practices - array-callback-return: 2 - block-scoped-var: 2 - dot-notation: 2 - eqeqeq: 2 - no-else-return: 2 - no-extend-native: 2 - no-floating-decimal: 2 - no-extra-bind: 2 - no-fallthrough: 2 - no-labels: 2 - no-lone-blocks: 2 - no-loop-func: 2 - no-multi-spaces: 2 - no-multi-str: 2 - no-native-reassign: 2 - no-new-wrappers: 2 - no-octal: 2 - no-proto: 2 - no-redeclare: 2 - no-return-assign: 2 - no-self-assign: 2 - no-self-compare: 2 - no-sequences: 2 - no-throw-literal: 2 - no-useless-call: 2 - no-useless-concat: 2 - no-useless-escape: 2 - no-void: 2 - no-unmodified-loop-condition: 2 - yoda: 2 - - # Strict Mode - # http://eslint.org/docs/rules/#strict-mode - strict: [2, "global"] - - # Variables - # http://eslint.org/docs/rules/#variables - no-delete-var: 2 - no-shadow-restricted-names: 2 - no-undef: 2 - no-unused-vars: [2, {"args": "none"}] - - # http://eslint.org/docs/rules/#nodejs-and-commonjs - no-mixed-requires: 2 - no-new-require: 2 - no-path-concat: 2 - - # Stylistic Issues - # http://eslint.org/docs/rules/#stylistic-issues - comma-spacing: 2 - eol-last: 2 - indent: [2, 4, {SwitchCase: 2}] - keyword-spacing: 2 - max-len: [2, 200, 2] - new-parens: 2 - no-mixed-spaces-and-tabs: 2 - no-multiple-empty-lines: [2, {max: 2}] - no-trailing-spaces: 2 - quotes: [2, "single", "avoid-escape"] - semi: 2 - space-before-blocks: [2, "always"] - space-before-function-paren: [2, "always"] - space-in-parens: [2, "never"] - space-infix-ops: 2 - space-unary-ops: 2 - -globals: - it: true - describe: true - xdescribe: true - before: true - after: true - beforeEach: true - afterEach: true diff --git a/.github/FUNDING.yml b/.github/FUNDING.yml deleted file mode 100644 index 1893f87aadd..00000000000 --- a/.github/FUNDING.yml +++ /dev/null @@ -1 +0,0 @@ -open_collective: node-redis diff --git a/.github/ISSUE_TEMPLATE.md b/.github/ISSUE_TEMPLATE.md index c74eb12b803..9b181d43149 100644 --- a/.github/ISSUE_TEMPLATE.md +++ b/.github/ISSUE_TEMPLATE.md @@ -5,30 +5,17 @@ labels: needs-triage ### Issue - - > Describe your issue here - --- ### Environment - **Node.js Version**: `VERSION_HERE` - + - - **Redis Version**: `VERSION_HERE` - + - **Redis Server Version**: `VERSION_HERE` + - **Platform**: `PLATFORM_HERE` diff --git a/.github/PULL_REQUEST_TEMPLATE.md b/.github/PULL_REQUEST_TEMPLATE.md index 98e3d312605..d4f8b8f2d9b 100644 --- a/.github/PULL_REQUEST_TEMPLATE.md +++ b/.github/PULL_REQUEST_TEMPLATE.md @@ -1,13 +1,10 @@ - - ### Description - + -> Description your pull request here - +> Describe your pull request here --- diff --git a/.github/workflows/benchmark.yml b/.github/workflows/benchmark.yml index 3ec398bb627..b6e5802a914 100644 --- a/.github/workflows/benchmark.yml +++ b/.github/workflows/benchmark.yml @@ -1,6 +1,9 @@ -name: Benchmarking +name: Benchmark -on: [pull_request] +on: + push: + branches: + - v4 jobs: benchmark: @@ -9,8 +12,8 @@ jobs: strategy: fail-fast: false matrix: - node-version: [10.x, 12.x, 14.x, 15.x] - redis-version: [5.x, 6.x] + node-version: [16.x] + redis-version: [6.x] steps: - uses: actions/checkout@v2.3.4 @@ -18,21 +21,25 @@ jobs: fetch-depth: 1 - name: Use Node.js ${{ matrix.node-version }} - uses: actions/setup-node@v2.1.5 + uses: actions/setup-node@v2.3.0 with: node-version: ${{ matrix.node-version }} - name: Setup Redis - uses: shogo82148/actions-setup-redis@v1.9.7 + uses: shogo82148/actions-setup-redis@v1.12.0 with: redis-version: ${{ matrix.redis-version }} - auto-start: "true" - - run: npm i --no-audit --prefer-offline - - name: Run Benchmark - run: npm run benchmark > benchmark-output.txt && cat benchmark-output.txt - - name: Upload Benchmark Result - uses: actions/upload-artifact@v2.2.2 - with: - name: benchmark-output.txt - path: benchmark-output.txt + - name: Install Packages + run: npm ci + + - name: Build + run: npm run build + + - name: Install Benchmark Packages + run: npm ci + working-directory: ./benchmark + + - name: Benchmark + run: npm run start + working-directory: ./benchmark diff --git a/.github/workflows/codeql-analysis.yml b/.github/workflows/codeql-analysis.yml deleted file mode 100644 index 98c615d0c55..00000000000 --- a/.github/workflows/codeql-analysis.yml +++ /dev/null @@ -1,67 +0,0 @@ -# For most projects, this workflow file will not need changing; you simply need -# to commit it to your repository. -# -# You may wish to alter this file to override the set of languages analyzed, -# or to provide custom queries or build logic. -# -# ******** NOTE ******** -# We have attempted to detect the languages in your repository. Please check -# the `language` matrix defined below to confirm you have the correct set of -# supported CodeQL languages. -# -name: "CodeQL" - -on: - push: - branches: [ master ] - pull_request: - # The branches below must be a subset of the branches above - branches: [ master ] - schedule: - - cron: '35 0 * * 4' - -jobs: - analyze: - name: Analyze - runs-on: ubuntu-latest - - strategy: - fail-fast: false - matrix: - language: [ 'javascript' ] - # CodeQL supports [ 'cpp', 'csharp', 'go', 'java', 'javascript', 'python' ] - # Learn more: - # https://docs.github.com/en/free-pro-team@latest/github/finding-security-vulnerabilities-and-errors-in-your-code/configuring-code-scanning#changing-the-languages-that-are-analyzed - - steps: - - name: Checkout repository - uses: actions/checkout@v2 - - # Initializes the CodeQL tools for scanning. - - name: Initialize CodeQL - uses: github/codeql-action/init@v1 - with: - languages: ${{ matrix.language }} - # If you wish to specify custom queries, you can do so here or in a config file. - # By default, queries listed here will override any specified in a config file. - # Prefix the list here with "+" to use these queries and those in the config file. - # queries: ./path/to/local/query, your-org/your-repo/queries@main - - # Autobuild attempts to build any compiled languages (C/C++, C#, or Java). - # If this step fails, then you should remove it and run the build manually (see below) - - name: Autobuild - uses: github/codeql-action/autobuild@v1 - - # ℹ️ Command-line programs to run using the OS shell. - # 📚 https://git.io/JvXDl - - # ✏️ If the Autobuild fails above, remove it and uncomment the following three lines - # and modify them (or add more) to build your code if your project - # uses a compiled language - - #- run: | - # make bootstrap - # make release - - - name: Perform CodeQL Analysis - uses: github/codeql-action/analyze@v1 diff --git a/.github/workflows/documentation.yml b/.github/workflows/documentation.yml new file mode 100644 index 00000000000..16ca16b5608 --- /dev/null +++ b/.github/workflows/documentation.yml @@ -0,0 +1,31 @@ +name: Documentation + +on: + push: + branches: + - v4 + +jobs: + documentation: + runs-on: ubuntu-latest + + steps: + - uses: actions/checkout@v2.3.4 + with: + fetch-depth: 1 + + - name: Use Node.js + uses: actions/setup-node@v2.3.0 + + - name: Install Packages + run: npm ci + + - name: Generate Documentation + run: npm run documentation + + - name: Upload Documentation to Wiki + uses: SwiftDocOrg/github-wiki-publish-action@v1 + with: + path: documentation + env: + GH_PERSONAL_ACCESS_TOKEN: ${{ secrets.BOT_PERSONAL_ACCESS_TOKEN }} diff --git a/.github/workflows/linting.yml b/.github/workflows/linting.yml deleted file mode 100644 index d110707ee09..00000000000 --- a/.github/workflows/linting.yml +++ /dev/null @@ -1,31 +0,0 @@ -name: Linting - -on: [pull_request] - -jobs: - eslint: - name: ESLint - runs-on: ubuntu-latest - steps: - - uses: actions/checkout@v2.3.4 - with: - fetch-depth: 1 - - uses: actions/setup-node@v2.1.5 - with: - node-version: 12 - - run: npm i --no-audit --prefer-offline - - name: Test Code Linting - run: npm run lint - - name: Save Code Linting Report JSON - run: npm run lint:report - continue-on-error: true - - name: Annotate Code Linting Results - uses: ataylorme/eslint-annotate-action@1.1.2 - with: - repo-token: "${{ secrets.GITHUB_TOKEN }}" - report-json: "eslint-report.json" - - name: Upload ESLint report - uses: actions/upload-artifact@v2.2.2 - with: - name: eslint-report.json - path: eslint-report.json diff --git a/.github/workflows/tests.yml b/.github/workflows/tests.yml index 06b0e57ec3e..028600f1a17 100644 --- a/.github/workflows/tests.yml +++ b/.github/workflows/tests.yml @@ -1,16 +1,18 @@ name: Tests -on: [push] +on: + push: + branches: + - v4 jobs: - testing: - name: Test + tests: runs-on: ubuntu-latest strategy: fail-fast: false matrix: - node-version: [10.x, 12.x, 14.x, 15.x] - redis-version: [4.x, 5.x, 6.x] + node-version: [12.x, 14.x, 16.x] + redis-version: [5.x, 6.x] steps: - uses: actions/checkout@v2.3.4 @@ -18,35 +20,38 @@ jobs: fetch-depth: 1 - name: Use Node.js ${{ matrix.node-version }} - uses: actions/setup-node@v2.1.5 + uses: actions/setup-node@v2.3.0 with: node-version: ${{ matrix.node-version }} - name: Setup Redis - uses: shogo82148/actions-setup-redis@v1.9.7 + uses: shogo82148/actions-setup-redis@v1.12.0 with: redis-version: ${{ matrix.redis-version }} auto-start: "false" - - name: Disable IPv6 - run: sudo sh -c 'echo 0 > /proc/sys/net/ipv6/conf/all/disable_ipv6'; - - - name: Setup Stunnel - run: sudo apt-get install stunnel4 - - name: Install Packages - run: npm i --no-audit --prefer-offline + run: npm ci - name: Run Tests - run: npm test + run: npm run test - - name: Submit Coverage - run: npm run coveralls - env: - COVERALLS_REPO_TOKEN: ${{ secrets.COVERALLS_TOKEN }} + - name: Generate lcov + run: ./node_modules/.bin/nyc report -r lcov - - name: Upload Coverage Report - uses: actions/upload-artifact@v2.2.2 + - name: Coveralls + uses: coverallsapp/github-action@1.1.3 with: - name: coverage - path: coverage + github-token: ${{ secrets.GITHUB_TOKEN }} + flag-name: Node ${{ matrix.node-version }} Redis ${{ matrix.redis-version }} + parallel: true + + finish: + needs: tests + runs-on: ubuntu-latest + steps: + - name: Coveralls Finished + uses: coverallsapp/github-action@1.1.3 + with: + github-token: ${{ secrets.github_token }} + parallel-finished: true diff --git a/.github/workflows/tests_windows.yml b/.github/workflows/tests_windows.yml deleted file mode 100644 index 7a2e00a9c93..00000000000 --- a/.github/workflows/tests_windows.yml +++ /dev/null @@ -1,49 +0,0 @@ -name: Tests Windows - -on: [push] - -jobs: - testing-windows: - name: Test Windows - runs-on: windows-latest - strategy: - fail-fast: false - matrix: - node-version: [10.x, 12.x, 14.x, 15.x] - steps: - - uses: actions/checkout@v2.3.4 - with: - fetch-depth: 1 - - - name: Install Redis - uses: crazy-max/ghaction-chocolatey@v1.4.0 - with: - args: install redis-64 --version=3.0.503 --no-progress - - - name: Start Redis - run: | - redis-server --service-install - redis-server --service-start - redis-cli config set stop-writes-on-bgsave-error no - - - name: Setup Node.js ${{ matrix.node-version }} - uses: actions/setup-node@v2.1.5 - with: - node-version: ${{ matrix.node-version }} - - - name: Install Packages - run: npm i --no-audit --prefer-offline - - - name: Run Tests - run: npm test - - - name: Submit Coverage - run: npm run coveralls - env: - COVERALLS_REPO_TOKEN: ${{ secrets.COVERALLS_TOKEN }} - - - name: Upload Coverage Report - uses: actions/upload-artifact@v2.2.2 - with: - name: coverage - path: coverage diff --git a/.gitignore b/.gitignore index 64b4143dc6f..0bdff14c7ff 100644 --- a/.gitignore +++ b/.gitignore @@ -1,17 +1,8 @@ -node_modules -.tern-port -.nyc_output -coverage -*.log -*.rdb -stunnel.conf -stunnel.pid -*.out -package-lock.json - -# IntelliJ IDEs -.idea -# VisualStudioCode IDEs -.vscode -.vs -eslint-report.json +.vscode/ +.idea/ +node_modules/ +dist/ +.nyc_output/ +coverage/ +dump.rdb +documentation/ diff --git a/.npmignore b/.npmignore index 605ed4543cc..fa2d8841124 100644 --- a/.npmignore +++ b/.npmignore @@ -1,23 +1,17 @@ -examples/ -benchmarks/ -test/ -.nyc_output/ +.vscode/ +.idea/ +node_modules/ +.nyc_output coverage/ -.github/ -.eslintignore -.eslintrc -.tern-port -*.log -*.rdb -*.out -*.yml -.vscode -.idea +dump.rdb +documentation/ CONTRIBUTING.md -CODE_OF_CONDUCT.md -.travis.yml -appveyor.yml -package-lock.json -.prettierrc -eslint-report.json -.deepsource.toml +tsconfig.json +.nycrc.json +benchmark/ +.github/ +scripts/ +lib/ +index.ts +*.spec.* +dist/lib/test-utils.* diff --git a/.nycrc.json b/.nycrc.json new file mode 100644 index 00000000000..925d954248c --- /dev/null +++ b/.nycrc.json @@ -0,0 +1,4 @@ +{ + "extends": "@istanbuljs/nyc-config-typescript", + "exclude": ["**/*.spec.ts", "lib/test-utils.ts"] +} \ No newline at end of file diff --git a/.prettierrc b/.prettierrc deleted file mode 100644 index 1ca516a86c0..00000000000 --- a/.prettierrc +++ /dev/null @@ -1,11 +0,0 @@ -{ - "arrowParens": "avoid", - "trailingComma": "all", - "useTabs": false, - "semi": true, - "singleQuote": false, - "bracketSpacing": true, - "jsxBracketSameLine": false, - "tabWidth": 2, - "printWidth": 100 -} diff --git a/CHANGELOG.md b/CHANGELOG.md index 186da332a45..d0095714010 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -1,5 +1,28 @@ # Changelog +## v4.0.0 + +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. + +### Breaking Changes + +- All functions return Promises by default +- Dropped support for Node.js 10.x, the minimum supported Node.js version is now 12.x +- `createClient` takes new and different arguments +- The `prefix`, `rename_commands` configuration options to `createClient` have been removed +- The `enable_offline_queue` configuration option is removed, executing commands on a closed client (without calling `.connect()` or after calling `.disconnect()`) will reject immediately +- Login credentials are no longer saved when using `.auth()` directly + +### Features + +- Added support for Promises +- Added built-in TypeScript declaration files enabling code completion +- 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.0.0 - 09 Feb, 2020 This version is mainly a release to distribute all the unreleased changes on master since 2017 and additionally removes diff --git a/CODE_OF_CONDUCT.md b/CODE_OF_CONDUCT.md deleted file mode 100644 index 2adee6acb34..00000000000 --- a/CODE_OF_CONDUCT.md +++ /dev/null @@ -1,60 +0,0 @@ -# Contributor Covenant Code of Conduct - -## Our Pledge - -In the interest of fostering an open and welcoming environment, we as contributors and maintainers pledge to making -participation in our project and our community a harassment-free experience for everyone, regardless of age, body size, -disability, ethnicity, gender identity and expression, level of experience, nationality, personal appearance, race, -religion, or sexual identity and orientation. - -## Our Standards - -Examples of behavior that contributes to creating a positive environment include: - -- Using welcoming and inclusive language -- Being respectful of differing viewpoints and experiences -- Gracefully accepting constructive criticism -- Focusing on what is best for the community -- Showing empathy towards other community members - -Examples of unacceptable behavior by participants include: - -- The use of sexualized language or imagery and unwelcome sexual attention or advances -- Trolling, insulting/derogatory comments, and personal or political attacks -- Public or private harassment -- Publishing others' private information, such as a physical or electronic address, without explicit permission -- Other conduct which could reasonably be considered inappropriate in a professional setting - -## Our Responsibilities - -Project maintainers are responsible for clarifying the standards of acceptable behavior and are expected to take -appropriate and fair corrective action in response to any instances of unacceptable behavior. - -Project maintainers have the right and responsibility to remove, edit, or reject comments, commits, code, wiki edits, - issues, and other contributions that are not aligned to this Code of Conduct, or to ban temporarily or permanently any - contributor for other behaviors that they deem inappropriate, threatening, offensive, or harmful. - -## Scope - -This Code of Conduct applies both within project spaces and in public spaces when an individual is representing the -project or its community. Examples of representing a project or community include using an official project e-mail address, -posting via an official social media account, or acting as an appointed representative at an online or offline event. -Representation of a project may be further defined and clarified by project maintainers. - -## Enforcement - -Instances of abusive, harassing, or otherwise unacceptable behavior may be reported by contacting the project team at -`redis[AT]invertase.io`. The project team will review and investigate all complaints, and will respond in a way that it -deems appropriate to the circumstances. The project team is obligated to maintain confidentiality with regard to the -reporter of an incident. Further details of specific enforcement policies may be posted separately. - -Project maintainers who do not follow or enforce the Code of Conduct in good faith may face temporary or permanent -repercussions as determined by other members of the project's leadership. - -## Attribution - -This Code of Conduct is adapted from the [Contributor Covenant][homepage], version 1.4, available at -[http://contributor-covenant.org/version/1/4][version] - -[homepage]: http://contributor-covenant.org -[version]: http://contributor-covenant.org/version/1/4/ diff --git a/CONTRIBUTING.md b/CONTRIBUTING.md index d9fac6a450c..fbad5205081 100644 --- a/CONTRIBUTING.md +++ b/CONTRIBUTING.md @@ -2,110 +2,70 @@ First, thank you for considering contributing to Node Redis! It's people like you that make the open source community such a great community! 😊 -We welcome any type of contribution, not just code. You can help with; +We welcome any type of contribution, not just code. You can help with: -- **QA**: file bug reports, the more details you can give the better (e.g. platform versions, screenshots sdk versions & logs) -- **Docs**: improve reference coverage, add more examples, fix typos or anything else you can spot. -- **Code**: take a look at the open issues and help triage them. -- **Donations**: we welcome financial contributions in full transparency on our [open collective](https://opencollective.com/node-redis). +- **QA**: file bug reports, the more details you can give the better (e.g. platform versions, screenshots, SDK versions, logs) +- **Docs**: improve reference coverage, add more examples, fix typos or anything else you can spot +- **Code**: take a look at the open issues and help triage them --- ## Project Guidelines -As maintainers of this project, we want to ensure that the project lives and continues to grow. Not blocked by any -singular person's time. +As maintainers of this project, we want to ensure that the project lives and continues to grow. Progress should not be blocked by any one person's availability. -One of the simplest ways of doing this is by encouraging a larger set of shallow contributors. Through this we hope to -mitigate the problems of a project that needs updates but there is no-one who has the power to do so. +One of the simplest ways of doing this is by encouraging a larger set of contributors. Using this approach we hope to mitigate the challenges of maintaining a project that needs regular updates. -### Continuous Deployment +### Getting Comfortable Contributing - +It is normal for your first pull request to be a potential fix for a problem but moving on from there to helping the project's direction can be difficult. -Coming soon. +We try to help contributors cross that barrier by identifying good first step issues (labelled `good-first-issue`). These issues are considered appropriate for first time contributors. Generally, these should be non-critical issues that are well defined. Established contributors will not work on these, to make space for others. -### How can we help you get comfortable contributing? +New contributors may consider picking up issues labelled `needs-triage` or `help-wanted`. These may not necessarily require code changes but rather help with debugging and finding the cause of the issue whether it's a bug or a user's incorrect setup of the library or project. -It is normal for a first pull request to be a potential fix for a problem but moving on from there to helping the -project's direction can be difficult. +We keep all project discussion inside GitHub issues. This ensures that valuable information can be searched easily. GitHub issues are the go to tool for questions about how to use the library, or how the project is run. -We try to help contributors cross that barrier by offering good first step issues (labelled `good-first-issue`). These -issues can be fixed without feeling like you are stepping on toes. Generally, these should be non-critical issues that -are well defined. They will be purposely avoided by mature contributors to the project, to make space for others. +### Expectations of Contributors -Additionally issues labelled `needs-triage` or `help-wanted` can also be picked up, these may not necessarily require -code changes but rather help with debugging and finding the cause of the issue whether it's a bug or a users incorrect -setup of the library or project. +You shouldn't feel bad for not contributing to open source. We want contributors like yourself to provide ideas, keep the ship shipping and to take some of the load from others. It is non-obligatory; we’re here to get things done in an enjoyable way. :trophy: -We aim to keep all project discussion inside GitHub issues. This is to make sure valuable discussion is accessible via -search. If you have questions about how to use the library, or how the project is running - GitHub issues are the goto -tool for this project. +We only ask that you follow the conduct guidelines set out in our [Code of Conduct](https://redis.com/community/community-guidelines-code-of-conduct/) throughout your contribution journey. -### Our expectations on you as a contributor -You shouldn't feel bad for not contributing to open source. We want contributors like yourself to provide ideas, keep -the ship shipping and to take some of the load from others. It is non-obligatory; we’re here to get things done in an -enjoyable way. :trophy: +#### Special Thanks -We only ask that you follow the conduct guidelines set out in our [Code of Conduct](/CODE_OF_CONDUCT.md) throughout your -contribution journey. - -### What about if you have problems that cannot be discussed in public? - -You can reach out to us directly via email (`redis[AT]invertase.io`) or direct message us on -[Twitter](https://twitter.com/NodeRedis) if you'd like to discuss something privately. - -#### Project Maintainers - -- Mike Diarmid ([Salakar](https://github.com/Salakar)) @ [Invertase](https://github.com/invertase) - - Twitter: [@mikediarmid](https://twitter.com/mikediarmid) -- Elliot Hesp ([Ehesp](https://github.com/Ehesp)) @ [Invertase](https://github.com/invertase) - - Twitter: [@elliothesp](https://twitter.com/elliothesp) -- Ruben Bridgewater ([BridgeAR](https://github.com/BridgeAR)) - - Twitter: [@BridgeAR](https://twitter.com/BridgeAR) - -Huge thanks to the original author of Node Redis, [Matthew Ranney](https://github.com/mranney) and also to -[Ruben Bridgewater](https://github.com/BridgeAR) for handing over this project over to new maintainers so it could be -continuously maintained. +A huge thank you to the original author of Node Redis, [Matthew Ranney](https://github.com/mranney). --- ## Code Guidelines -### Your First Contribution - -Working on your first Pull Request? You can learn how from this _free_ series, -[How to Contribute to an Open Source Project on GitHub](https://egghead.io/series/how-to-contribute-to-an-open-source-project-on-github). - ### Testing Code Node Redis has a full test suite with coverage setup. -To run the tests, run `npm install` to install all dependencies, and then run `npm test`. To check detailed coverage locally run the `npm run coverage` command after -testing and open the generated `./coverage/index.html` in your browser. +To run the tests, run `npm install` to install dependencies, then run `npm test`. Note that the test suite assumes that a few tools are installed in your environment, such as: + - redis (make sure redis-server is not running when starting the tests, it's part of the test-suite to start it and you'll end up with a "port already in use" error) - stunnel (for TLS tests) -### Submitting code for review +### Submitting Code for Review -The bigger the pull request, the longer it will take to review and merge. Where possible try to break down large pull -requests into smaller chunks that are easier to review and merge. It is also always helpful to have some context for -your pull request. What was the purpose? Why does it matter to you? What problem are you trying to solve? Tag in any linked issues. +The bigger the pull request, the longer it will take to review and merge. Where possible try to break down large pull requests into smaller chunks that are easier to review and merge. It is also always helpful to have some context for your pull request. What was the purpose? Why does it matter to you? What problem are you trying to solve? Tag in any relevant issues. -To aid review we also ask that you fill out the pull request template as much as possible. +To assist reviewers, we ask that you fill out the pull request template as much as possible. > Use a `draft` pull request if your pull request is not complete or ready for review. -### Code review process +### Code Review Process -Pull Requests to the protected branches require two or more peer-review approvals and passing status checks to be able -to be merged. +Pull Requests to the protected branches require peer-review approvals and passing status checks to be able to be merged. -When reviewing a Pull Request please check the following steps on top of the existing automated checks: +When reviewing a Pull Request please check the following steps as well as the existing automated checks: -- Does the it provide or update the docs if docs changes are required? +- Does your Pull Request provide or update the docs if docs changes are required? - Have the tests been updated or new tests been added to test any newly implemented or changed functionality? -- Is the testing coverage ok and not worse than previously? +- Is the test coverage at the same level as before (preferably more!)? diff --git a/LICENSE b/LICENSE index db86cc4de7f..5cb3bb4180b 100644 --- a/LICENSE +++ b/LICENSE @@ -1,4 +1,4 @@ -MIT License + MIT License Copyright (c) 2016-present Node Redis contributors. diff --git a/README.md b/README.md index 10f0c044886..acc229b69c2 100644 --- a/README.md +++ b/README.md @@ -1,1008 +1,290 @@

- - + +

Node Redis

-

A high performance Node.js Redis client.

---- - -

- NPM downloads - NPM version - Build Status - Windows Build Status - Coverage Status - - Coverage Status - -

+ --- ## Installation ```bash -npm install redis -``` - -## Usage - -#### Example - -```js -const redis = require("redis"); -const client = redis.createClient(); - -client.on("error", function(error) { - console.error(error); -}); - -client.set("key", "value", redis.print); -client.get("key", redis.print); -``` - -Note that the API is entirely asynchronous. To get data back from the server, -you'll need to use a callback. - -### Promises - -Node Redis currently doesn't natively support promises (this is coming in v4), however you can wrap the methods you -want to use with promises using the built-in Node.js `util.promisify` method on Node.js >= v8; - -```js -const { promisify } = require("util"); -const getAsync = promisify(client.get).bind(client); - -getAsync.then(console.log).catch(console.error); +npm install redis@next ``` -### Commands - -This library is a 1 to 1 mapping of the [Redis commands](https://redis.io/commands). - -Each Redis command is exposed as a function on the `client` object. -All functions take either an `args` Array plus optional `callback` Function or -a variable number of individual arguments followed by an optional callback. -Examples: - -```js -client.hmset(["key", "foo", "bar"], function(err, res) { - // ... -}); - -// Works the same as -client.hmset("key", ["foo", "bar"], function(err, res) { - // ... -}); - -// Or -client.hmset("key", "foo", "bar", function(err, res) { - // ... -}); -``` - -Care should be taken with user input if arrays are possible (via body-parser, query string or other method), as single arguments could be unintentionally interpreted as multiple args. - -Note that in either form the `callback` is optional: - -```js -client.set("foo", "bar"); -client.set(["hello", "world"]); -``` - -If the key is missing, reply will be null. Only if the [Redis Command -Reference](http://redis.io/commands) states something else it will not be null. - -```js -client.get("missing_key", function(err, reply) { - // reply is null when the key is missing - console.log(reply); -}); -``` - -Minimal parsing is done on the replies. Commands that return a integer return -JavaScript Numbers, arrays return JavaScript Array. `HGETALL` returns an Object -keyed by the hash keys. All strings will either be returned as string or as -buffer depending on your setting. Please be aware that sending null, undefined -and Boolean values will result in the value coerced to a string! - -## API - -### Connection and other Events - -`client` will emit some events about the state of the connection to the Redis server. - -#### `"ready"` - -`client` will emit `ready` once a connection is established. Commands issued -before the `ready` event are queued, then replayed just before this event is -emitted. - -#### `"connect"` - -`client` will emit `connect` as soon as the stream is connected to the server. - -#### `"reconnecting"` - -`client` will emit `reconnecting` when trying to reconnect to the Redis server -after losing the connection. Listeners are passed an object containing `delay` -(in ms from the previous try) and `attempt` (the attempt #) attributes. - -#### `"error"` - -`client` will emit `error` when encountering an error connecting to the Redis -server or when any other in Node Redis occurs. If you use a command without -callback and encounter a ReplyError it is going to be emitted to the error -listener. - -So please attach the error listener to Node Redis. - -#### `"end"` - -`client` will emit `end` when an established Redis server connection has closed. - -#### `"warning"` +> :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). -`client` will emit `warning` when password was set but none is needed and if a -deprecated option / function / similar is used. - -### redis.createClient() - -If you have `redis-server` running on the same machine as node, then the -defaults for port and host are probably fine and you don't need to supply any -arguments. `createClient()` returns a `RedisClient` object. Otherwise, -`createClient()` accepts these arguments: - -- `redis.createClient([options])` -- `redis.createClient(unix_socket[, options])` -- `redis.createClient(redis_url[, options])` -- `redis.createClient(port[, host][, options])` - -**Tip:** If the Redis server runs on the same machine as the client consider -using unix sockets if possible to increase throughput. - -**Note:** Using `'rediss://...` for the protocol in a `redis_url` will enable a TLS socket connection. However, additional TLS options will need to be passed in `options`, if required. - -#### `options` object properties +## Usage -| Property | Default | Description | -| -------------------------- | --------- | ----------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- | -| host | 127.0.0.1 | IP address of the Redis server | -| port | 6379 | Port of the Redis server | -| path | null | The UNIX socket string of the Redis server | -| url | null | The URL of the Redis server. Format: `[redis[s]:]//[[user][:password@]][host][:port][/db-number][?db=db-number[&password=bar[&option=value]]]` (More info avaliable at [IANA](http://www.iana.org/assignments/uri-schemes/prov/redis)). | -| string_numbers | null | Set to `true`, Node Redis will return Redis number values as Strings instead of javascript Numbers. Useful if you need to handle big numbers (above `Number.MAX_SAFE_INTEGER === 2^53`). Hiredis is incapable of this behavior, so setting this option to `true` will result in the built-in javascript parser being used no matter the value of the `parser` option. | -| return_buffers | false | If set to `true`, then all replies will be sent to callbacks as Buffers instead of Strings. | -| detect_buffers | false | If set to `true`, then replies will be sent to callbacks as Buffers. This option lets you switch between Buffers and Strings on a per-command basis, whereas `return_buffers` applies to every command on a client. **Note**: This doesn't work properly with the pubsub mode. A subscriber has to either always return Strings or Buffers. | -| socket_keepalive | true | If set to `true`, the keep-alive functionality is enabled on the underlying socket. | -| socket_initial_delay | 0 | Initial Delay in milliseconds, and this will also behave the interval keep alive message sending to Redis. | -| no_ready_check | false | When a connection is established to the Redis server, the server might still be loading the database from disk. While loading, the server will not respond to any commands. To work around this, Node Redis has a "ready check" which sends the `INFO` command to the server. The response from the `INFO` command indicates whether the server is ready for more commands. When ready, `node_redis` emits a `ready` event. Setting `no_ready_check` to `true` will inhibit this check. | -| enable_offline_queue | true | By default, if there is no active connection to the Redis server, commands are added to a queue and are executed once the connection has been established. Setting `enable_offline_queue` to `false` will disable this feature and the callback will be executed immediately with an error, or an error will be emitted if no callback is specified. | -| retry_unfulfilled_commands | false | If set to `true`, all commands that were unfulfilled while the connection is lost will be retried after the connection has been reestablished. Use this with caution if you use state altering commands (e.g. `incr`). This is especially useful if you use blocking commands. | -| password | null | If set, client will run Redis auth command on connect. Alias `auth_pass` **Note** Node Redis < 2.5 must use `auth_pass` | -| user | null | The ACL user (only valid when `password` is set) | -| db | null | If set, client will run Redis `select` command on connect. | -| family | IPv4 | You can force using IPv6 if you set the family to 'IPv6'. See Node.js [net](https://nodejs.org/api/net.html) or [dns](https://nodejs.org/api/dns.html) modules on how to use the family type. | -| disable_resubscribing | false | If set to `true`, a client won't resubscribe after disconnecting. | -| rename_commands | null | Passing an object with renamed commands to use instead of the original functions. For example, if you renamed the command KEYS to "DO-NOT-USE" then the rename_commands object would be: `{ KEYS : "DO-NOT-USE" }` . See the [Redis security topics](http://redis.io/topics/security) for more info. | -| tls | null | An object containing options to pass to [tls.connect](http://nodejs.org/api/tls.html#tls_tls_connect_port_host_options_callback) to set up a TLS connection to Redis (if, for example, it is set up to be accessible via a tunnel). | -| prefix | null | A string used to prefix all used keys (e.g. `namespace:test`). Please be aware that the `keys` command will not be prefixed. The `keys` command has a "pattern" as argument and no key and it would be impossible to determine the existing keys in Redis if this would be prefixed. | -| retry_strategy | function | A function that receives an options object as parameter including the retry `attempt`, the `total_retry_time` indicating how much time passed since the last time connected, the `error` why the connection was lost and the number of `times_connected` in total. If you return a number from this function, the retry will happen exactly after that time in milliseconds. If you return a non-number, no further retry will happen and all offline commands are flushed with errors. Return an error to return that specific error to all offline commands. Example below. | -| connect_timeout | 3600000 | In milliseconds. This should only be the timeout for connecting to redis, but for now it interferes with `retry_strategy` and stops it from reconnecting after this timeout. | +### Basic Example -**`detect_buffers` example:** +```typescript +import { createClient } from 'redis'; -```js -const redis = require("redis"); -const client = redis.createClient({ detect_buffers: true }); +(async () => { + const client = createClient(); -client.set("foo_rand000000000000", "OK"); + client.on('error', (err) => console.log('Redis Client Error', err)); -// This will return a JavaScript String -client.get("foo_rand000000000000", function(err, reply) { - console.log(reply.toString()); // Will print `OK` -}); + await client.connect(); -// This will return a Buffer since original key is specified as a Buffer -client.get(new Buffer("foo_rand000000000000"), function(err, reply) { - console.log(reply.toString()); // Will print `` -}); + await client.set('key', 'value'); + const value = await client.get('key'); +})(); ``` -**`retry_strategy` example:** +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]`: -```js -const client = redis.createClient({ - retry_strategy: function(options) { - if (options.error && options.error.code === "ECONNREFUSED") { - // End reconnecting on a specific error and flush all commands with - // a individual error - return new Error("The server refused the connection"); - } - if (options.total_retry_time > 1000 * 60 * 60) { - // End reconnecting after a specific timeout and flush all commands - // with a individual error - return new Error("Retry time exhausted"); - } - if (options.attempt > 10) { - // End reconnecting with built in error - return undefined; +```typescript +createClient({ + socket: { + url: 'redis://alice:foobared@awesome.redis.server:6380' } - // reconnect after - return Math.min(options.attempt * 100, 3000); - }, }); ``` -### client.auth(password[, callback]) - -When connecting to a Redis server that requires authentication, the `AUTH` -command must be sent as the first command after connecting. This can be tricky -to coordinate with reconnections, the ready check, etc. To make this easier, -`client.auth()` stashes `password` and will send it after each connection, -including reconnections. `callback` is invoked only once, after the response to -the very first `AUTH` command sent. -NOTE: Your call to `client.auth()` should not be inside the ready handler. If -you are doing this wrong, `client` will emit an error that looks -something like this `Error: Ready check failed: ERR operation not permitted`. - -### client.quit(callback) - -This sends the quit command to the redis server and ends cleanly right after all -running commands were properly handled. If this is called while reconnecting -(and therefore no connection to the redis server exists) it is going to end the -connection right away instead of resulting in further reconnections! All offline -commands are going to be flushed with an error in that case. +You can also use discrete parameters, UNIX sockets, and even TLS to connect. Details can be found in in the [Wiki](https://github.com/NodeRedis/node-redis/wiki/lib.socket#RedisSocketOptions). -### client.end(flush) +### Redis Commands -Forcibly close the connection to the Redis server. Note that this does not wait -until all replies have been parsed. If you want to exit cleanly, call -`client.quit()` as mentioned above. +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.): -You should set flush to true, if you are not absolutely sure you do not care -about any other commands. If you set flush to false all still running commands -will silently fail. +```typescript +// raw Redis commands +await client.HSET('key', 'field', 'value'); +await client.HGETALL('key'); -This example closes the connection to the Redis server before the replies have -been read. You probably don't want to do this: - -```js -const redis = require("redis"); -const client = redis.createClient(); - -client.set("hello", "world", function(err) { - // This will either result in an error (flush parameter is set to true) - // or will silently fail and this callback will not be called at all (flush set to false) - console.error(err); -}); - -// No further commands will be processed -client.end(true); - -client.get("hello", function(err) { - console.error(err); // => 'The connection has already been closed.' -}); +// friendly JavaScript commands +await client.hSet('key', 'field', 'value'); +await client.hGetAll('key'); ``` -`client.end()` without the flush parameter set to true should NOT be used in production! - -### Error Handling - -Currently the following `Error` subclasses exist: - -- `RedisError`: _All errors_ returned by the client -- `ReplyError` subclass of `RedisError`: All errors returned by **Redis** itself -- `AbortError` subclass of `RedisError`: All commands that could not finish due - to what ever reason -- `ParserError` subclass of `RedisError`: Returned in case of a parser error - (this should not happen) -- `AggregateError` subclass of `AbortError`: Emitted in case multiple unresolved - commands without callback got rejected in debug_mode instead of lots of - `AbortError`s. - -All error classes are exported by the module. - -#### Example - -```js -const assert = require("assert"); - -const redis = require("redis"); -const { AbortError, AggregateError, ReplyError } = require("redis"); +Modifiers to commands are specified using a JavaScript object: -const client = redis.createClient(); - -client.on("error", function(err) { - assert(err instanceof Error); - assert(err instanceof AbortError); - assert(err instanceof AggregateError); - - // The set and get are aggregated in here - assert.strictEqual(err.errors.length, 2); - assert.strictEqual(err.code, "NR_CLOSED"); -}); - -client.set("foo", "bar", "baz", function(err, res) { - // Too many arguments - assert(err instanceof ReplyError); // => true - assert.strictEqual(err.command, "SET"); - assert.deepStrictEqual(err.args, ["foo", 123, "bar"]); - - redis.debug_mode = true; - - client.set("foo", "bar"); - client.get("foo"); - - process.nextTick(function() { - // Force closing the connection while the command did not yet return - client.end(true); - redis.debug_mode = false; - }); +```typescript +await client.set('key', 'value', { + EX: 10, + NX: true }); ``` -Every `ReplyError` contains the `command` name in all-caps and the arguments (`args`). - -If Node Redis emits a library error because of another error, the triggering -error is added to the returned error as `origin` attribute. - -**_Error codes_** - -Node Redis returns a `NR_CLOSED` error code if the clients connection dropped. -If a command unresolved command got rejected a `UNCERTAIN_STATE` code is -returned. A `CONNECTION_BROKEN` error code is used in case Node Redis gives up -to reconnect. - -### client.unref() - -Call `unref()` on the underlying socket connection to the Redis server, allowing -the program to exit once no more commands are pending. - -This is an **experimental** feature, and only supports a subset of the Redis -protocol. Any commands where client state is saved on the Redis server, e.g. -`*SUBSCRIBE` or the blocking `BL*` commands will _NOT_ work with `.unref()`. - -```js -const redis = require("redis"); -const client = redis.createClient(); +Replies will be transformed into useful data structures: -/* - * Calling unref() will allow this program to exit immediately after the get - * command finishes. Otherwise the client would hang as long as the - * client-server connection is alive. - */ -client.unref(); - -client.get("foo", function(err, value) { - if (err) throw err; - console.log(value); -}); +```typescript +await client.hGetAll('key'); // { field1: 'value1', field2: 'value2' } +await client.hVals('key'); // ['value1', 'value2'] ``` -### Hash Commands - -Most Redis commands take a single String or an Array of Strings as arguments, -and replies are sent back as a single String or an Array of Strings. When -dealing with hash values, there are a couple of useful exceptions to this. - -#### client.hgetall(hash, callback) +### Unsupported Redis Commands -The reply from an `HGETALL` command will be converted into a JavaScript Object. That way you can interact with the -responses using JavaScript syntax. +If you want to run commands and/or use arguments that Node Redis doesn't know about (yet!) use `.sendCommand()`: -**Example:** +```typescript +await client.sendCommand(['SET', 'key', 'value', 'NX']); // 'OK' -```js -client.hmset("key", "foo", "bar", "hello", "world"); - -client.hgetall("key", function(err, value) { - console.log(value.foo); // > "bar" - console.log(value.hello); // > "world" -}); +await client.sendCommand(['HGETALL', 'key']); // ['key1', 'field1', 'key2', 'field2'] ``` -#### client.hmset(hash, key1, val1, ...keyN, valN, [callback]) +### Transactions (Multi/Exec) -Multiple values may also be set by supplying more arguments. +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: -**Example:** +```typescript +await client.set('another-key', 'another-value'); -```js -// key -// 1) foo => bar -// 2) hello => world -client.HMSET("key", "foo", "bar", "hello", "world"); +const [ setKeyReply, otherKeyValue ] = await client.multi() + .set('key', 'value') + .get('another-key') + .exec() +]); // ['OK', 'another-value'] ``` -### PubSub +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. -#### Example +To dig deeper into transactions, check out the [Isolated Execution Guide](./docs/isolated-execution.md). -This example opens two client connections, subscribes to a channel on one of them, and publishes to that -channel on the other. +### Blocking Commands -```js -const redis = require("redis"); +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. -const subscriber = redis.createClient(); -const publisher = redis.createClient(); +This pattern works especially well for blocking commands—such as `BLPOP` and `BLMOVE`: -let messageCount = 0; +```typescript +import { commandOptions } from 'redis'; -subscriber.on("subscribe", function(channel, count) { - publisher.publish("a channel", "a message"); - publisher.publish("a channel", "another message"); -}); +const blPopPromise = client.blPop( + commandOptions({ isolated: true }), + 'key' +); -subscriber.on("message", function(channel, message) { - messageCount += 1; +await client.lPush('key', ['1', '2']); - console.log("Subscriber received message in channel '" + channel + "': " + message); - - if (messageCount === 2) { - subscriber.unsubscribe(); - subscriber.quit(); - publisher.quit(); - } -}); - -subscriber.subscribe("a channel"); +await blPopPromise; // '2' ``` -When a client issues a `SUBSCRIBE` or `PSUBSCRIBE`, that connection is put into -a `"subscriber"` mode. At that point, the only valid commands are those that modify the subscription -set, and quit (also ping on some redis versions). When -the subscription set is empty, the connection is put back into regular mode. - -If you need to send regular commands to Redis while in subscriber mode, just -open another connection with a new client (use `client.duplicate()` to quickly duplicate an existing client). - -#### Subscriber Events - -If a client has subscriptions active, it may emit these events: +To learn more about isolated execution, check out the [guide](./docs/isolated-execution.md). -**"message" (channel, message)**: +### Pub/Sub -Client will emit `message` for every message received that matches an active subscription. -Listeners are passed the channel name as `channel` and the message as `message`. +Subscribing to a channel requires a dedicated stand-alone connection. You can easily get one by `.duplicate()`ing an existing Redis connection. -**"pmessage" (pattern, channel, message)**: +```typescript +const subscriber = client.duplicate(); -Client will emit `pmessage` for every message received that matches an active -subscription pattern. Listeners are passed the original pattern used with -`PSUBSCRIBE` as `pattern`, the sending channel name as `channel`, and the -message as `message`. - -**"message_buffer" (channel, message)**: - -This is the same as the `message` event with the exception, that it is always -going to emit a buffer. If you listen to the `message` event at the same time as -the `message_buffer`, it is always going to emit a string. - -**"pmessage_buffer" (pattern, channel, message)**: - -This is the same as the `pmessage` event with the exception, that it is always -going to emit a buffer. If you listen to the `pmessage` event at the same time -as the `pmessage_buffer`, it is always going to emit a string. - -**"subscribe" (channel, count)**: - -Client will emit `subscribe` in response to a `SUBSCRIBE` command. Listeners are -passed the channel name as `channel` and the new count of subscriptions for this -client as `count`. - -**"psubscribe" (pattern, count)**: - -Client will emit `psubscribe` in response to a `PSUBSCRIBE` command. Listeners -are passed the original pattern as `pattern`, and the new count of subscriptions -for this client as `count`. - -**"unsubscribe" (channel, count)**: - -Client will emit `unsubscribe` in response to a `UNSUBSCRIBE` command. Listeners -are passed the channel name as `channel` and the new count of subscriptions for -this client as `count`. When `count` is 0, this client has left subscriber mode -and no more subscriber events will be emitted. - -**"punsubscribe" (pattern, count)**: - -Client will emit `punsubscribe` in response to a `PUNSUBSCRIBE` command. -Listeners are passed the channel name as `channel` and the new count of -subscriptions for this client as `count`. When `count` is 0, this client has -left subscriber mode and no more subscriber events will be emitted. - -### client.multi([commands]) - -`MULTI` commands are queued up until an `EXEC` is issued, and then all commands -are run atomically by Redis. The interface returns an -individual `Multi` object by calling `client.multi()`. If any command fails to -queue, all commands are rolled back and none is going to be executed (For -further information see the [Redis transactions](http://redis.io/topics/transactions) documentation). - -```js -const redis = require("redis"); -const client = redis.createClient(); - -let setSize = 20; - -client.sadd("key", "member1"); -client.sadd("key", "member2"); - -while (setSize > 0) { - client.sadd("key", "member" + setSize); - setSize -= 1; -} - -// chain commands -client - .multi() - .scard("key") - .smembers("key") - .keys("*") - .dbsize() - .exec(function(err, replies) { - console.log("MULTI got " + replies.length + " replies"); - replies.forEach(function(reply, index) { - console.log("REPLY @ index " + index + ": " + reply.toString()); - }); - }); +await subscriber.connect(); ``` -#### Multi.exec([callback]) - -`client.multi()` is a constructor that returns a `Multi` object. `Multi` objects -share all of the same command methods as `client` objects do. Commands are -queued up inside the `Multi` object until `Multi.exec()` is invoked. - -If your code contains an syntax error an `EXECABORT` error is going to be thrown -and all commands are going to be aborted. That error contains a `.errors` -property that contains the concrete errors. -If all commands were queued successfully and an error is thrown by redis while -processing the commands that error is going to be returned in the result array! -No other command is going to be aborted though than the ones failing. - -You can either chain together `MULTI` commands as in the above example, or you -can queue individual commands while still sending regular client command as in -this example: - -```js -const redis = require("redis"); -const client = redis.createClient(); - -// start a separate multi command queue -const multi = client.multi(); - -// add some commands to the queue -multi.incr("count_cats", redis.print); -multi.incr("count_dogs", redis.print); +Once you have one, simply subscribe and unsubscribe as needed: -// runs a command immediately outside of the `multi` instance -client.mset("count_cats", 100, "count_dogs", 50, redis.print); - -// drains the multi queue and runs each command atomically -multi.exec(function(err, replies) { - console.log(replies); // 101, 51 +```typescript +await subscriber.subscribe('channel', message => { + console.log(message); // 'message' }); -``` - -In addition to adding commands to the `MULTI` queue individually, you can also -pass an array of commands and arguments to the constructor: - -```js -const redis = require("redis"); - -const client = redis.createClient(); - -client - .multi([ - ["mget", "foo", "bar", redis.print], - ["incr", "hello"], - ]) - .exec(function(err, replies) { - console.log(replies); - }); -``` - -#### Multi.exec_atomic([callback]) - -Identical to Multi.exec but with the difference that executing a single command -will not use transactions. - -#### Optimistic Locks - -Using `multi` you can make sure your modifications run as a transaction, but you -can't be sure you got there first. What if another client modified a key while -you were working with it's data? - -To solve this, Redis supports the [WATCH](https://redis.io/topics/transactions) -command, which is meant to be used with MULTI: - -```js -const redis = require("redis"); - -const client = redis.createClient(); -client.watch("foo", function(watchError) { - if (watchError) throw watchError; - - client.get("foo", function(getError, result) { - if (getError) throw getError; - - // Process result - // Heavy and time consuming operation here to generate "bar" - - client - .multi() - .set("foo", "bar") - .exec(function(execError, results) { - /** - * If err is null, it means Redis successfully attempted - * the operation. - */ - if (execError) throw execError; - - /** - * If results === null, it means that a concurrent client - * changed the key while we were processing it and thus - * the execution of the MULTI command was not performed. - * - * NOTICE: Failing an execution of MULTI is not considered - * an error. So you will have err === null and results === null - */ - }); - }); +await subscriber.pSubscribe('channe*', (message, channel) => { + console.log(message, channel); // 'message', 'channel' }); -``` -The above snippet shows the correct usage of `watch` with `multi`. Every time a -watched key is changed before the execution of a `multi` command, the execution -will return `null`. On a normal situation, the execution will return an array of -values with the results of the operations. - -As stated in the snippet, failing the execution of a `multi` command being watched -is not considered an error. The execution may return an error if, for example, the -client cannot connect to Redis. - -An example where we can see the execution of a `multi` command fail is as follows: - -```js -const clients = { - watcher: redis.createClient(), - modifier: redis.createClient(), -}; - -clients.watcher.watch("foo", function(watchError) { - if (watchError) throw watchError; - - // if you comment out the next line, the transaction will work - clients.modifier.set("foo", Math.random(), setError => { - if (setError) throw setError; - }); - - // using a setTimeout here to ensure that the MULTI/EXEC will come after the SET. - // Normally, you would use a callback to ensure order, but I want the above SET command - // to be easily comment-out-able. - setTimeout(function() { - clients.watcher - .multi() - .set("foo", "bar") - .set("hello", "world") - .exec((multiExecError, results) => { - if (multiExecError) throw multiExecError; - - if (results === null) { - console.log("transaction aborted because results were null"); - } else { - console.log("transaction worked and returned", results); - } +await subscriber.unsubscribe('channel'); - clients.watcher.quit(); - clients.modifier.quit(); - }); - }, 1000); -}); +await subscriber.pUnsubscribe('channe*'); ``` -#### `WATCH` limitations - -Redis WATCH works only on _whole_ key values. For example, with WATCH you can -watch a hash for modifications, but you cannot watch a specific field of a hash. - -The following example would watch the keys `foo` and `hello`, not the field `hello` -of hash `foo`: - -```js -const redis = require("redis"); - -const client = redis.createClient(); - -client.hget("foo", "hello", function(hashGetError, result) { - if (hashGetError) throw hashGetError; - - //Do some processing with the value from this field and watch it after +Publish a message on a channel: - client.watch("foo", "hello", function(watchError) { - if (watchError) throw watchError; - - /** - * This is now watching the keys 'foo' and 'hello'. It is not - * watching the field 'hello' of hash 'foo'. Because the key 'foo' - * refers to a hash, this command is now watching the entire hash - * for modifications. - */ - }); -}); +```typescript +await publisher.publish('channel', 'message'); ``` -This limitation also applies to sets (you can not watch individual set members) -and any other collections. - -### client.batch([commands]) - -Identical to `.multi()` without transactions. This is recommended if you want to -execute many commands at once but don't need to rely on transactions. - -`BATCH` commands are queued up until an `EXEC` is issued, and then all commands -are run atomically by Redis. The interface returns an -individual `Batch` object by calling `client.batch()`. The only difference -between .batch and .multi is that no transaction is going to be used. -Be aware that the errors are - just like in multi statements - in the result. -Otherwise both, errors and results could be returned at the same time. - -If you fire many commands at once this is going to boost the execution speed -significantly compared to firing the same commands in a loop without waiting for -the result! See the benchmarks for further comparison. Please remember that all -commands are kept in memory until they are fired. - -### Monitor mode +### Scan Iterator -Redis supports the `MONITOR` command, which lets you see all commands received -by the Redis server across all client connections, including from other client -libraries and other computers. +[`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): -A `monitor` event is going to be emitted for every command fired from any client -connected to the server including the monitoring client itself. The callback for -the `monitor` event takes a timestamp from the Redis server, an array of command -arguments and the raw monitoring string. - -#### Example: - -```js -const redis = require("redis"); -const client = redis.createClient(); - -client.monitor(function(err, res) { - console.log("Entering monitoring mode."); -}); - -client.set("foo", "bar"); - -client.on("monitor", function(time, args, rawReply) { - console.log(time + ": " + args); // 1458910076.446514:['set', 'foo', 'bar'] -}); +```typescript +for await (const key of client.scanIterator()) { + // use the key! + await client.get(key); +} ``` -## Extras - -Some other things you might find useful. - -### `client.server_info` - -After the ready probe completes, the results from the INFO command are saved in -the `client.server_info` object. +This works with `HSCAN`, `SSCAN`, and `ZSCAN` too: -The `versions` key contains an array of the elements of the version string for -easy comparison. - -``` -> client.server_info.redis_version -'2.3.0' -> client.server_info.versions -[ 2, 3, 0 ] +```typescript +for await (const member of client.hScanIterator('hash')) {} +for await (const { field, value } of client.sScanIterator('set')) {} +for await (const { member, score } of client.zScanIterator('sorted-set')) {} ``` -### `redis.print()` - -A handy callback function for displaying return values when testing. Example: +You can override the default options by providing a configuration object: -```js -const redis = require("redis"); -const client = redis.createClient(); - -client.on("connect", function() { - client.set("foo", "bar", redis.print); // => "Reply: OK" - client.get("foo", redis.print); // => "Reply: bar" - client.quit(); +```typescript +client.scanIterator({ + TYPE: 'string', // `SCAN` only + MATCH: 'patter*', + COUNT: 100 }); ``` -### Multi-word commands - -To execute redis multi-word commands like `SCRIPT LOAD` or `CLIENT LIST` pass -the second word as first parameter: - -```js -client.script("load", "return 1"); +### 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 { + return [key, number.toString()]; + }, + transformReply(reply: number): number { + return reply; + } + }) + } + }); -client - .multi() - .script("load", "return 1") - .exec(); + await client.connect(); -client.multi([["script", "load", "return 1"]]).exec(); + await client.set('key', '1'); + await client.add('key', 2); // 3 +})(); ``` -### `client.duplicate([options][, callback])` - -Duplicate all current options and return a new redisClient instance. All options -passed to the duplicate function are going to replace the original option. If -you pass a callback, duplicate is going to wait until the client is ready and -returns it in the callback. If an error occurs in the meanwhile, that is going -to return an error instead in the callback. +### Cluster -One example of when to use duplicate() would be to accommodate the connection- -blocking redis commands `BRPOP`, `BLPOP`, and `BRPOPLPUSH`. If these commands -are used on the same Redis client instance as non-blocking commands, the -non-blocking ones may be queued up until after the blocking ones finish. +Connecting to a cluster is a bit different. Create the client by specifying some (or all) of the nodes in your cluster and then use it like a non-clustered client: -Another reason to use duplicate() is when multiple DBs on the same server are -accessed via the redis SELECT command. Each DB could use its own connection. +```typescript +import { createCluster } from 'redis'; -### `client.sendCommand(command_name[, [args][, callback]])` - -All Redis commands have been added to the `client` object. However, if new -commands are introduced before this library is updated or if you want to add -individual commands you can use `sendCommand()` to send arbitrary commands to -Redis. - -All commands are sent as multi-bulk commands. `args` can either be an Array of -arguments, or omitted / set to undefined. - -### `redis.addCommand(command_name)` - -Calling addCommand will add a new command to the prototype. The exact command -name will be used when calling using this new command. Using arbitrary arguments -is possible as with any other command. - -### `client.connected` - -Boolean tracking the state of the connection to the Redis server. - -### `client.command_queue_length` - -The number of commands that have been sent to the Redis server but not yet -replied to. You can use this to enforce some kind of maximum queue depth for -commands while connected. - -### `client.offline_queue_length` - -The number of commands that have been queued up for a future connection. You can -use this to enforce some kind of maximum queue depth for pre-connection -commands. - -### Commands with Optional and Keyword arguments - -This applies to anything that uses an optional `[WITHSCORES]` or `[LIMIT offset count]` in the [redis.io/commands](http://redis.io/commands) documentation. - -#### Example - -```js -const args = ["myzset", 1, "one", 2, "two", 3, "three", 99, "ninety-nine"]; +(async () => { + const cluster = createCluster({ + rootNodes: [{ + host: '10.0.0.1', + port: 30001 + }, { + host: '10.0.0.2', + port: 30002 + }] + }); -client.zadd(args, function(addError, addResponse) { - if (addError) throw addError; - console.log("added " + addResponse + " items."); + cluster.on('error', (err) => console.log('Redis Cluster Error', err)); - // -Infinity and +Infinity also work - const args1 = ["myzset", "+inf", "-inf"]; - client.zrevrangebyscore(args1, function(rangeError, rangeResponse) { - if (rangeError) throw rangeError; - console.log("response1", rangeResponse); - // ... - }); + await cluster.connect(); - const max = 3; - const min = 1; - const offset = 1; - const count = 2; - const args2 = ["myzset", max, min, "WITHSCORES", "LIMIT", offset, count]; - client.zrevrangebyscore(args2, function(rangeError, rangeResponse) { - if (rangeError) throw rangeError; - console.log("response2", rangeResponse); - // ... - }); -}); + await cluster.set('key', 'value'); + const value = await cluster.get('key'); +})(); ``` -## Performance +### Auto-Pipelining -Much effort has been spent to make Node Redis as fast as possible for common operations. +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=='); ``` -Mac mini (2018), i7-3.2GHz and 32gb memory -clients: 1, NodeJS: 12.15.0, Redis: 5.0.6, parser: javascript, connected by: tcp - PING, 1/1 avg/max: 0.03/ 3.28 2501ms total, 31926 ops/sec - PING, batch 50/1 avg/max: 0.08/ 3.35 2501ms total, 599460 ops/sec - SET 4B str, 1/1 avg/max: 0.03/ 3.54 2501ms total, 29483 ops/sec - SET 4B str, batch 50/1 avg/max: 0.10/ 1.39 2501ms total, 477689 ops/sec - SET 4B buf, 1/1 avg/max: 0.04/ 1.52 2501ms total, 23449 ops/sec - SET 4B buf, batch 50/1 avg/max: 0.20/ 2.09 2501ms total, 244382 ops/sec - GET 4B str, 1/1 avg/max: 0.03/ 1.35 2501ms total, 32205 ops/sec - GET 4B str, batch 50/1 avg/max: 0.09/ 2.02 2501ms total, 568992 ops/sec - GET 4B buf, 1/1 avg/max: 0.03/ 2.93 2501ms total, 32802 ops/sec - GET 4B buf, batch 50/1 avg/max: 0.08/ 1.03 2501ms total, 592863 ops/sec - SET 4KiB str, 1/1 avg/max: 0.03/ 0.76 2501ms total, 29287 ops/sec - SET 4KiB str, batch 50/1 avg/max: 0.35/ 2.97 2501ms total, 143163 ops/sec - SET 4KiB buf, 1/1 avg/max: 0.04/ 1.21 2501ms total, 23070 ops/sec - SET 4KiB buf, batch 50/1 avg/max: 0.28/ 2.34 2501ms total, 176809 ops/sec - GET 4KiB str, 1/1 avg/max: 0.03/ 1.54 2501ms total, 29555 ops/sec - GET 4KiB str, batch 50/1 avg/max: 0.18/ 1.59 2501ms total, 279188 ops/sec - GET 4KiB buf, 1/1 avg/max: 0.03/ 1.80 2501ms total, 30681 ops/sec - GET 4KiB buf, batch 50/1 avg/max: 0.17/ 5.00 2501ms total, 285886 ops/sec - INCR, 1/1 avg/max: 0.03/ 1.99 2501ms total, 32757 ops/sec - INCR, batch 50/1 avg/max: 0.09/ 2.54 2501ms total, 538964 ops/sec - LPUSH, 1/1 avg/max: 0.05/ 4.85 2501ms total, 19482 ops/sec - LPUSH, batch 50/1 avg/max: 0.12/ 9.52 2501ms total, 395562 ops/sec - LRANGE 10, 1/1 avg/max: 0.06/ 9.21 2501ms total, 17062 ops/sec - LRANGE 10, batch 50/1 avg/max: 0.22/ 1.03 2501ms total, 228269 ops/sec - LRANGE 100, 1/1 avg/max: 0.05/ 1.44 2501ms total, 19051 ops/sec - LRANGE 100, batch 50/1 avg/max: 0.99/ 3.46 2501ms total, 50480 ops/sec - SET 4MiB str, 1/1 avg/max: 4.11/ 13.96 2501ms total, 243 ops/sec - SET 4MiB str, batch 20/1 avg/max: 91.16/145.01 2553ms total, 219 ops/sec - SET 4MiB buf, 1/1 avg/max: 2.81/ 11.90 2502ms total, 354 ops/sec - SET 4MiB buf, batch 20/1 avg/max: 36.21/ 70.96 2535ms total, 552 ops/sec - GET 4MiB str, 1/1 avg/max: 2.82/ 19.10 2503ms total, 354 ops/sec - GET 4MiB str, batch 20/1 avg/max: 128.57/207.86 2572ms total, 156 ops/sec - GET 4MiB buf, 1/1 avg/max: 3.13/ 23.88 2501ms total, 318 ops/sec - GET 4MiB buf, batch 20/1 avg/max: 65.91/ 87.59 2572ms total, 303 ops/sec -``` - -## Debugging - -To get debug output run your Node Redis application with `NODE_DEBUG=redis`. - -This is also going to result in good stack traces opposed to useless ones -otherwise for any async operation. -If you only want to have good stack traces but not the debug output run your -application in development mode instead (`NODE_ENV=development`). - -Good stack traces are only activated in development and debug mode as this -results in a significant performance penalty. -**_Comparison_**: +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()`. -Standard stack trace: - -``` -ReplyError: ERR wrong number of arguments for 'set' command - at parseError (/home/ruben/repos/redis/node_modules/redis-parser/lib/parser.js:158:12) - at parseType (/home/ruben/repos/redis/node_modules/redis-parser/lib/parser.js:219:14) +```typescript +await Promise.all([ + client.set('Tm9kZSBSZWRpcw==', 'users:1'), + client.sAdd('users:1:tokens', 'Tm9kZSBSZWRpcw==') +]); ``` -Debug stack trace: +## Contributing -``` -ReplyError: ERR wrong number of arguments for 'set' command - at new Command (/home/ruben/repos/redis/lib/command.js:9:902) - at RedisClient.set (/home/ruben/repos/redis/lib/commands.js:9:3238) - at Context. (/home/ruben/repos/redis/test/good_stacks.spec.js:20:20) - at callFnAsync (/home/ruben/repos/redis/node_modules/mocha/lib/runnable.js:349:8) - at Test.Runnable.run (/home/ruben/repos/redis/node_modules/mocha/lib/runnable.js:301:7) - at Runner.runTest (/home/ruben/repos/redis/node_modules/mocha/lib/runner.js:422:10) - at /home/ruben/repos/redis/node_modules/mocha/lib/runner.js:528:12 - at next (/home/ruben/repos/redis/node_modules/mocha/lib/runner.js:342:14) - at /home/ruben/repos/redis/node_modules/mocha/lib/runner.js:352:7 - at next (/home/ruben/repos/redis/node_modules/mocha/lib/runner.js:284:14) - at Immediate._onImmediate (/home/ruben/repos/redis/node_modules/mocha/lib/runner.js:320:5) - at processImmediate [as _immediateCallback] (timers.js:383:17) -``` +If you'd like to contribute, check out the [contributing guide](CONTRIBUTING.md). -## Contributing +Thank you to all the people who already contributed to Node Redis! -Please see the [contributing guide](CONTRIBUTING.md). + ## License diff --git a/benchmark/.gitignore b/benchmark/.gitignore new file mode 100644 index 00000000000..3c3629e647f --- /dev/null +++ b/benchmark/.gitignore @@ -0,0 +1 @@ +node_modules diff --git a/benchmark/index.js b/benchmark/index.js new file mode 100644 index 00000000000..37f88176653 --- /dev/null +++ b/benchmark/index.js @@ -0,0 +1,81 @@ +import { add, suite, cycle, complete } from 'benny'; +import v4 from 'v4'; +import v3 from 'v3'; +import { once } from 'events'; + +const v4Client = v4.createClient(), + v4LegacyClient = v4.createClient({ + legacyMode: true + }), + v3Client = v3.createClient(); + +await Promise.all([ + v4Client.connect(), + v4LegacyClient.connect(), + once(v3Client, 'connect') +]); + +const key = random(100), + value = random(100); + +function random(size) { + const result = []; + + for (let i = 0; i < size; i++) { + result.push(Math.floor(Math.random() * 10)); + } + + return result.join(''); +} + +suite( + 'SET GET', + add('v4', async () => { + await Promise.all([ + v4Client.set(key, value), + v4Client.get(key) + ]); + }), + add('v4 - legacy mode', () => { + return new Promise((resolve, reject) => { + v4LegacyClient.set(key, value); + v4LegacyClient.get(key, (err, reply) => { + if (err) { + reject(err); + } else { + resolve(reply); + } + }); + }); + }), + add('v3', () => { + return new Promise((resolve, reject) => { + v3Client.set(key, value); + v3Client.get(key, (err, reply) => { + if (err) { + reject(err); + } else { + resolve(reply); + } + }); + }); + }), + cycle(), + complete(), + complete(() => { + return Promise.all([ + v4Client.disconnect(), + v4LegacyClient.disconnect(), + new Promise((resolve, reject) => { + v3Client.quit((err) => { + if (err) { + reject(err); + } else { + resolve(err); + } + }); + }) + ]); + }) +); + diff --git a/benchmark/package-lock.json b/benchmark/package-lock.json new file mode 100644 index 00000000000..a16a420f8cb --- /dev/null +++ b/benchmark/package-lock.json @@ -0,0 +1,926 @@ +{ + "name": "benchmark", + "lockfileVersion": 2, + "requires": true, + "packages": { + "": { + "license": "ISC", + "dependencies": { + "@probe.gl/bench": "^3.4.0", + "benny": "^3.6.15", + "v3": "npm:redis@3.1.2", + "v4": "file:../" + } + }, + "..": { + "name": "redis", + "version": "4.0.0-next.5", + "license": "MIT", + "dependencies": { + "cluster-key-slot": "1.1.0", + "redis-parser": "3.0.0", + "yallist": "4.0.0" + }, + "devDependencies": { + "@istanbuljs/nyc-config-typescript": "^1.0.1", + "@types/mocha": "^9.0.0", + "@types/node": "^16.4.5", + "@types/sinon": "^10.0.2", + "@types/which": "^2.0.1", + "@types/yallist": "^4.0.1", + "mocha": "^9.0.3", + "nyc": "^15.1.0", + "release-it": "^14.10.1", + "sinon": "^11.1.2", + "source-map-support": "^0.5.19", + "ts-node": "^10.1.0", + "typedoc": "^0.21.4", + "typedoc-github-wiki-theme": "^0.5.1", + "typedoc-plugin-markdown": "^3.10.4", + "typescript": "^4.3.5", + "which": "^2.0.2" + }, + "engines": { + "node": ">=12" + } + }, + "node_modules/@arrows/array": { + "version": "1.4.1", + "resolved": "https://registry.npmjs.org/@arrows/array/-/array-1.4.1.tgz", + "integrity": "sha512-MGYS8xi3c4tTy1ivhrVntFvufoNzje0PchjEz6G/SsWRgUKxL4tKwS6iPdO8vsaJYldagAeWMd5KRD0aX3Q39g==", + "dependencies": { + "@arrows/composition": "^1.2.2" + } + }, + "node_modules/@arrows/composition": { + "version": "1.2.2", + "resolved": "https://registry.npmjs.org/@arrows/composition/-/composition-1.2.2.tgz", + "integrity": "sha512-9fh1yHwrx32lundiB3SlZ/VwuStPB4QakPsSLrGJFH6rCXvdrd060ivAZ7/2vlqPnEjBkPRRXOcG1YOu19p2GQ==" + }, + "node_modules/@arrows/dispatch": { + "version": "1.0.3", + "resolved": "https://registry.npmjs.org/@arrows/dispatch/-/dispatch-1.0.3.tgz", + "integrity": "sha512-v/HwvrFonitYZM2PmBlAlCqVqxrkIIoiEuy5bQgn0BdfvlL0ooSBzcPzTMrtzY8eYktPyYcHg8fLbSgyybXEqw==", + "dependencies": { + "@arrows/composition": "^1.2.2" + } + }, + "node_modules/@arrows/error": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/@arrows/error/-/error-1.0.2.tgz", + "integrity": "sha512-yvkiv1ay4Z3+Z6oQsUkedsQm5aFdyPpkBUQs8vejazU/RmANABx6bMMcBPPHI4aW43VPQmXFfBzr/4FExwWTEA==" + }, + "node_modules/@arrows/multimethod": { + "version": "1.1.7", + "resolved": "https://registry.npmjs.org/@arrows/multimethod/-/multimethod-1.1.7.tgz", + "integrity": "sha512-EjHD3XuGAV4G28rm7mu8k7zQJh/EOizh104/p9i2ofGcnL5mgKONFH/Bq6H3SJjM+WDAlKcR9WBpNhaAKCnH2g==", + "dependencies": { + "@arrows/array": "^1.4.0", + "@arrows/composition": "^1.2.2", + "@arrows/error": "^1.0.2", + "fast-deep-equal": "^3.1.1" + } + }, + "node_modules/@babel/runtime": { + "version": "7.14.8", + "resolved": "https://registry.npmjs.org/@babel/runtime/-/runtime-7.14.8.tgz", + "integrity": "sha512-twj3L8Og5SaCRCErB4x4ajbvBIVV77CGeFglHpeg5WC5FF8TZzBWXtTJ4MqaD9QszLYTtr+IsaAL2rEUevb+eg==", + "dependencies": { + "regenerator-runtime": "^0.13.4" + }, + "engines": { + "node": ">=6.9.0" + } + }, + "node_modules/@probe.gl/bench": { + "version": "3.4.0", + "resolved": "https://registry.npmjs.org/@probe.gl/bench/-/bench-3.4.0.tgz", + "integrity": "sha512-S7iNPz5G3zEfEP0S4SAMvtj+dwP7EWfVBaA8Cy5CVIgM1lnpUbXvqoAJxlVEedNC32Icxwq65XQheufy1Zzmug==", + "dependencies": { + "@babel/runtime": "^7.0.0", + "probe.gl": "3.4.0" + } + }, + "node_modules/@probe.gl/stats": { + "version": "3.4.0", + "resolved": "https://registry.npmjs.org/@probe.gl/stats/-/stats-3.4.0.tgz", + "integrity": "sha512-Gl37r9qGuiKadIvTZdSZvzCNOttJYw6RcY1oT0oDuB8r2uhuZAdSMQRQTy9FTinp6MY6O9wngGnV6EpQ8wSBAw==", + "dependencies": { + "@babel/runtime": "^7.0.0" + } + }, + "node_modules/ansi-escapes": { + "version": "4.3.2", + "resolved": "https://registry.npmjs.org/ansi-escapes/-/ansi-escapes-4.3.2.tgz", + "integrity": "sha512-gKXj5ALrKWQLsYG9jlTRmR/xKluxHV+Z9QEwNIgCfM1/uwPMCuzVVnh5mwTd+OuBZcwSIMbqssNWRm1lE51QaQ==", + "dependencies": { + "type-fest": "^0.21.3" + }, + "engines": { + "node": ">=8" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/ansi-regex": { + "version": "5.0.0", + "resolved": "https://registry.npmjs.org/ansi-regex/-/ansi-regex-5.0.0.tgz", + "integrity": "sha512-bY6fj56OUQ0hU1KjFNDQuJFezqKdrAyFdIevADiqrWHwSlbmBNMHp5ak2f40Pm8JTFyM2mqxkG6ngkHO11f/lg==", + "engines": { + "node": ">=8" + } + }, + "node_modules/ansi-styles": { + "version": "4.3.0", + "resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-4.3.0.tgz", + "integrity": "sha512-zbB9rCJAT1rbjiVDb2hqKFHNYLxgtk8NURxZ3IZwD3F6NtxbXZQCnnSi1Lkx+IDohdPlFp222wVALIheZJQSEg==", + "dependencies": { + "color-convert": "^2.0.1" + }, + "engines": { + "node": ">=8" + }, + "funding": { + "url": "https://github.com/chalk/ansi-styles?sponsor=1" + } + }, + "node_modules/astral-regex": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/astral-regex/-/astral-regex-2.0.0.tgz", + "integrity": "sha512-Z7tMw1ytTXt5jqMcOP+OQteU1VuNK9Y02uuJtKQ1Sv69jXQKKg5cibLwGJow8yzZP+eAc18EmLGPal0bp36rvQ==", + "engines": { + "node": ">=8" + } + }, + "node_modules/at-least-node": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/at-least-node/-/at-least-node-1.0.0.tgz", + "integrity": "sha512-+q/t7Ekv1EDY2l6Gda6LLiX14rU9TV20Wa3ofeQmwPFZbOMo9DXrLbOjFaaclkXKWidIaopwAObQDqwWtGUjqg==", + "engines": { + "node": ">= 4.0.0" + } + }, + "node_modules/benchmark": { + "version": "2.1.4", + "resolved": "https://registry.npmjs.org/benchmark/-/benchmark-2.1.4.tgz", + "integrity": "sha1-CfPeMckWQl1JjMLuVloOvzwqVik=", + "dependencies": { + "lodash": "^4.17.4", + "platform": "^1.3.3" + } + }, + "node_modules/benny": { + "version": "3.6.15", + "resolved": "https://registry.npmjs.org/benny/-/benny-3.6.15.tgz", + "integrity": "sha512-kq6XVGGYVou3Y8KNPs3SEF881vi5fJ8sIf9w69D2rreiNfRicWVWK6u6/mObMw6BiexoHHumtipn5gcu0Tngng==", + "dependencies": { + "@arrows/composition": "^1.0.0", + "@arrows/dispatch": "^1.0.2", + "@arrows/multimethod": "^1.1.6", + "benchmark": "^2.1.4", + "fs-extra": "^9.0.1", + "json2csv": "^5.0.4", + "kleur": "^4.1.3", + "log-update": "^4.0.0", + "prettier": "^2.1.2", + "stats-median": "^1.0.1" + } + }, + "node_modules/cli-cursor": { + "version": "3.1.0", + "resolved": "https://registry.npmjs.org/cli-cursor/-/cli-cursor-3.1.0.tgz", + "integrity": "sha512-I/zHAwsKf9FqGoXM4WWRACob9+SNukZTd94DWF57E4toouRulbCxcUh6RKUEOQlYTHJnzkPMySvPNaaSLNfLZw==", + "dependencies": { + "restore-cursor": "^3.1.0" + }, + "engines": { + "node": ">=8" + } + }, + "node_modules/color-convert": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/color-convert/-/color-convert-2.0.1.tgz", + "integrity": "sha512-RRECPsj7iu/xb5oKYcsFHSppFNnsj/52OVTRKb4zP5onXwVF3zVmmToNcOfGC+CRDpfK/U584fMg38ZHCaElKQ==", + "dependencies": { + "color-name": "~1.1.4" + }, + "engines": { + "node": ">=7.0.0" + } + }, + "node_modules/color-name": { + "version": "1.1.4", + "resolved": "https://registry.npmjs.org/color-name/-/color-name-1.1.4.tgz", + "integrity": "sha512-dOy+3AuW3a2wNbZHIuMZpTcgjGuLU/uBL/ubcZF9OXbDo8ff4O8yVp5Bf0efS8uEoYo5q4Fx7dY9OgQGXgAsQA==" + }, + "node_modules/commander": { + "version": "6.2.1", + "resolved": "https://registry.npmjs.org/commander/-/commander-6.2.1.tgz", + "integrity": "sha512-U7VdrJFnJgo4xjrHpTzu0yrHPGImdsmD95ZlgYSEajAn2JKzDhDTPG9kBTefmObL2w/ngeZnilk+OV9CG3d7UA==", + "engines": { + "node": ">= 6" + } + }, + "node_modules/denque": { + "version": "1.5.0", + "resolved": "https://registry.npmjs.org/denque/-/denque-1.5.0.tgz", + "integrity": "sha512-CYiCSgIF1p6EUByQPlGkKnP1M9g0ZV3qMIrqMqZqdwazygIA/YP2vrbcyl1h/WppKJTdl1F85cXIle+394iDAQ==", + "engines": { + "node": ">=0.10" + } + }, + "node_modules/emoji-regex": { + "version": "8.0.0", + "resolved": "https://registry.npmjs.org/emoji-regex/-/emoji-regex-8.0.0.tgz", + "integrity": "sha512-MSjYzcWNOA0ewAHpz0MxpYFvwg6yjy1NG3xteoqz644VCo/RPgnr1/GGt+ic3iJTzQ8Eu3TdM14SawnVUmGE6A==" + }, + "node_modules/fast-deep-equal": { + "version": "3.1.3", + "resolved": "https://registry.npmjs.org/fast-deep-equal/-/fast-deep-equal-3.1.3.tgz", + "integrity": "sha512-f3qQ9oQy9j2AhBe/H9VC91wLmKBCCU/gDOnKNAYG5hswO7BLKj09Hc5HYNz9cGI++xlpDCIgDaitVs03ATR84Q==" + }, + "node_modules/fs-extra": { + "version": "9.1.0", + "resolved": "https://registry.npmjs.org/fs-extra/-/fs-extra-9.1.0.tgz", + "integrity": "sha512-hcg3ZmepS30/7BSFqRvoo3DOMQu7IjqxO5nCDt+zM9XWjb33Wg7ziNT+Qvqbuc3+gWpzO02JubVyk2G4Zvo1OQ==", + "dependencies": { + "at-least-node": "^1.0.0", + "graceful-fs": "^4.2.0", + "jsonfile": "^6.0.1", + "universalify": "^2.0.0" + }, + "engines": { + "node": ">=10" + } + }, + "node_modules/graceful-fs": { + "version": "4.2.6", + "resolved": "https://registry.npmjs.org/graceful-fs/-/graceful-fs-4.2.6.tgz", + "integrity": "sha512-nTnJ528pbqxYanhpDYsi4Rd8MAeaBA67+RZ10CM1m3bTAVFEDcd5AuA4a6W5YkGZ1iNXHzZz8T6TBKLeBuNriQ==" + }, + "node_modules/is-fullwidth-code-point": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/is-fullwidth-code-point/-/is-fullwidth-code-point-3.0.0.tgz", + "integrity": "sha512-zymm5+u+sCsSWyD9qNaejV3DFvhCKclKdizYaJUuHA83RLjb7nSuGnddCHGv0hk+KY7BMAlsWeK4Ueg6EV6XQg==", + "engines": { + "node": ">=8" + } + }, + "node_modules/json2csv": { + "version": "5.0.6", + "resolved": "https://registry.npmjs.org/json2csv/-/json2csv-5.0.6.tgz", + "integrity": "sha512-0/4Lv6IenJV0qj2oBdgPIAmFiKKnh8qh7bmLFJ+/ZZHLjSeiL3fKKGX3UryvKPbxFbhV+JcYo9KUC19GJ/Z/4A==", + "dependencies": { + "commander": "^6.1.0", + "jsonparse": "^1.3.1", + "lodash.get": "^4.4.2" + }, + "bin": { + "json2csv": "bin/json2csv.js" + }, + "engines": { + "node": ">= 10", + "npm": ">= 6.13.0" + } + }, + "node_modules/jsonfile": { + "version": "6.1.0", + "resolved": "https://registry.npmjs.org/jsonfile/-/jsonfile-6.1.0.tgz", + "integrity": "sha512-5dgndWOriYSm5cnYaJNhalLNDKOqFwyDB/rr1E9ZsGciGvKPs8R2xYGCacuf3z6K1YKDz182fd+fY3cn3pMqXQ==", + "dependencies": { + "universalify": "^2.0.0" + }, + "optionalDependencies": { + "graceful-fs": "^4.1.6" + } + }, + "node_modules/jsonparse": { + "version": "1.3.1", + "resolved": "https://registry.npmjs.org/jsonparse/-/jsonparse-1.3.1.tgz", + "integrity": "sha1-P02uSpH6wxX3EGL4UhzCOfE2YoA=", + "engines": [ + "node >= 0.2.0" + ] + }, + "node_modules/kleur": { + "version": "4.1.4", + "resolved": "https://registry.npmjs.org/kleur/-/kleur-4.1.4.tgz", + "integrity": "sha512-8QADVssbrFjivHWQU7KkMgptGTl6WAcSdlbBPY4uNF+mWr6DGcKrvY2w4FQJoXch7+fKMjj0dRrL75vk3k23OA==", + "engines": { + "node": ">=6" + } + }, + "node_modules/lodash": { + "version": "4.17.21", + "resolved": "https://registry.npmjs.org/lodash/-/lodash-4.17.21.tgz", + "integrity": "sha512-v2kDEe57lecTulaDIuNTPy3Ry4gLGJ6Z1O3vE1krgXZNrsQ+LFTGHVxVjcXPs17LhbZVGedAJv8XZ1tvj5FvSg==" + }, + "node_modules/lodash.get": { + "version": "4.4.2", + "resolved": "https://registry.npmjs.org/lodash.get/-/lodash.get-4.4.2.tgz", + "integrity": "sha1-LRd/ZS+jHpObRDjVNBSZ36OCXpk=" + }, + "node_modules/log-update": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/log-update/-/log-update-4.0.0.tgz", + "integrity": "sha512-9fkkDevMefjg0mmzWFBW8YkFP91OrizzkW3diF7CpG+S2EYdy4+TVfGwz1zeF8x7hCx1ovSPTOE9Ngib74qqUg==", + "dependencies": { + "ansi-escapes": "^4.3.0", + "cli-cursor": "^3.1.0", + "slice-ansi": "^4.0.0", + "wrap-ansi": "^6.2.0" + }, + "engines": { + "node": ">=10" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/mimic-fn": { + "version": "2.1.0", + "resolved": "https://registry.npmjs.org/mimic-fn/-/mimic-fn-2.1.0.tgz", + "integrity": "sha512-OqbOk5oEQeAZ8WXWydlu9HJjz9WVdEIvamMCcXmuqUYjTknH/sqsWvhQ3vgwKFRR1HpjvNBKQ37nbJgYzGqGcg==", + "engines": { + "node": ">=6" + } + }, + "node_modules/onetime": { + "version": "5.1.2", + "resolved": "https://registry.npmjs.org/onetime/-/onetime-5.1.2.tgz", + "integrity": "sha512-kbpaSSGJTWdAY5KPVeMOKXSrPtr8C8C7wodJbcsd51jRnmD+GZu8Y0VoU6Dm5Z4vWr0Ig/1NKuWRKf7j5aaYSg==", + "dependencies": { + "mimic-fn": "^2.1.0" + }, + "engines": { + "node": ">=6" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/platform": { + "version": "1.3.6", + "resolved": "https://registry.npmjs.org/platform/-/platform-1.3.6.tgz", + "integrity": "sha512-fnWVljUchTro6RiCFvCXBbNhJc2NijN7oIQxbwsyL0buWJPG85v81ehlHI9fXrJsMNgTofEoWIQeClKpgxFLrg==" + }, + "node_modules/prettier": { + "version": "2.3.2", + "resolved": "https://registry.npmjs.org/prettier/-/prettier-2.3.2.tgz", + "integrity": "sha512-lnJzDfJ66zkMy58OL5/NY5zp70S7Nz6KqcKkXYzn2tMVrNxvbqaBpg7H3qHaLxCJ5lNMsGuM8+ohS7cZrthdLQ==", + "bin": { + "prettier": "bin-prettier.js" + }, + "engines": { + "node": ">=10.13.0" + } + }, + "node_modules/probe.gl": { + "version": "3.4.0", + "resolved": "https://registry.npmjs.org/probe.gl/-/probe.gl-3.4.0.tgz", + "integrity": "sha512-9CLByZATuhuG/Viq3ckfWU+dAhb7dMmjzsyCy4s7ds9ueTejcVRENxL197/XacOK/AN61YrEERB0QnouB0Qc0Q==", + "dependencies": { + "@babel/runtime": "^7.0.0", + "@probe.gl/stats": "3.4.0" + } + }, + "node_modules/redis-commands": { + "version": "1.7.0", + "resolved": "https://registry.npmjs.org/redis-commands/-/redis-commands-1.7.0.tgz", + "integrity": "sha512-nJWqw3bTFy21hX/CPKHth6sfhZbdiHP6bTawSgQBlKOVRG7EZkfHbbHwQJnrE4vsQf0CMNE+3gJ4Fmm16vdVlQ==" + }, + "node_modules/redis-errors": { + "version": "1.2.0", + "resolved": "https://registry.npmjs.org/redis-errors/-/redis-errors-1.2.0.tgz", + "integrity": "sha1-62LSrbFeTq9GEMBK/hUpOEJQq60=", + "engines": { + "node": ">=4" + } + }, + "node_modules/redis-parser": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/redis-parser/-/redis-parser-3.0.0.tgz", + "integrity": "sha1-tm2CjNyv5rS4pCin3vTGvKwxyLQ=", + "dependencies": { + "redis-errors": "^1.0.0" + }, + "engines": { + "node": ">=4" + } + }, + "node_modules/regenerator-runtime": { + "version": "0.13.9", + "resolved": "https://registry.npmjs.org/regenerator-runtime/-/regenerator-runtime-0.13.9.tgz", + "integrity": "sha512-p3VT+cOEgxFsRRA9X4lkI1E+k2/CtnKtU4gcxyaCUreilL/vqI6CdZ3wxVUx3UOUg+gnUOQQcRI7BmSI656MYA==" + }, + "node_modules/restore-cursor": { + "version": "3.1.0", + "resolved": "https://registry.npmjs.org/restore-cursor/-/restore-cursor-3.1.0.tgz", + "integrity": "sha512-l+sSefzHpj5qimhFSE5a8nufZYAM3sBSVMAPtYkmC+4EH2anSGaEMXSD0izRQbu9nfyQ9y5JrVmp7E8oZrUjvA==", + "dependencies": { + "onetime": "^5.1.0", + "signal-exit": "^3.0.2" + }, + "engines": { + "node": ">=8" + } + }, + "node_modules/signal-exit": { + "version": "3.0.3", + "resolved": "https://registry.npmjs.org/signal-exit/-/signal-exit-3.0.3.tgz", + "integrity": "sha512-VUJ49FC8U1OxwZLxIbTTrDvLnf/6TDgxZcK8wxR8zs13xpx7xbG60ndBlhNrFi2EMuFRoeDoJO7wthSLq42EjA==" + }, + "node_modules/slice-ansi": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/slice-ansi/-/slice-ansi-4.0.0.tgz", + "integrity": "sha512-qMCMfhY040cVHT43K9BFygqYbUPFZKHOg7K73mtTWJRb8pyP3fzf4Ixd5SzdEJQ6MRUg/WBnOLxghZtKKurENQ==", + "dependencies": { + "ansi-styles": "^4.0.0", + "astral-regex": "^2.0.0", + "is-fullwidth-code-point": "^3.0.0" + }, + "engines": { + "node": ">=10" + }, + "funding": { + "url": "https://github.com/chalk/slice-ansi?sponsor=1" + } + }, + "node_modules/stats-median": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/stats-median/-/stats-median-1.0.1.tgz", + "integrity": "sha512-IYsheLg6dasD3zT/w9+8Iq9tcIQqqu91ZIpJOnIEM25C3X/g4Tl8mhXwW2ZQpbrsJISr9+wizEYgsibN5/b32Q==" + }, + "node_modules/string-width": { + "version": "4.2.2", + "resolved": "https://registry.npmjs.org/string-width/-/string-width-4.2.2.tgz", + "integrity": "sha512-XBJbT3N4JhVumXE0eoLU9DCjcaF92KLNqTmFCnG1pf8duUxFGwtP6AD6nkjw9a3IdiRtL3E2w3JDiE/xi3vOeA==", + "dependencies": { + "emoji-regex": "^8.0.0", + "is-fullwidth-code-point": "^3.0.0", + "strip-ansi": "^6.0.0" + }, + "engines": { + "node": ">=8" + } + }, + "node_modules/strip-ansi": { + "version": "6.0.0", + "resolved": "https://registry.npmjs.org/strip-ansi/-/strip-ansi-6.0.0.tgz", + "integrity": "sha512-AuvKTrTfQNYNIctbR1K/YGTR1756GycPsg7b9bdV9Duqur4gv6aKqHXah67Z8ImS7WEz5QVcOtlfW2rZEugt6w==", + "dependencies": { + "ansi-regex": "^5.0.0" + }, + "engines": { + "node": ">=8" + } + }, + "node_modules/type-fest": { + "version": "0.21.3", + "resolved": "https://registry.npmjs.org/type-fest/-/type-fest-0.21.3.tgz", + "integrity": "sha512-t0rzBq87m3fVcduHDUFhKmyyX+9eo6WQjZvf51Ea/M0Q7+T374Jp1aUiyUl0GKxp8M/OETVHSDvmkyPgvX+X2w==", + "engines": { + "node": ">=10" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/universalify": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/universalify/-/universalify-2.0.0.tgz", + "integrity": "sha512-hAZsKq7Yy11Zu1DE0OzWjw7nnLZmJZYTDZZyEFHZdUhV8FkH5MCfoU1XMaxXovpyW5nq5scPqq0ZDP9Zyl04oQ==", + "engines": { + "node": ">= 10.0.0" + } + }, + "node_modules/v3": { + "name": "redis", + "version": "3.1.2", + "resolved": "https://registry.npmjs.org/redis/-/redis-3.1.2.tgz", + "integrity": "sha512-grn5KoZLr/qrRQVwoSkmzdbw6pwF+/rwODtrOr6vuBRiR/f3rjSTGupbF90Zpqm2oenix8Do6RV7pYEkGwlKkw==", + "dependencies": { + "denque": "^1.5.0", + "redis-commands": "^1.7.0", + "redis-errors": "^1.2.0", + "redis-parser": "^3.0.0" + }, + "engines": { + "node": ">=10" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/node-redis" + } + }, + "node_modules/v4": { + "resolved": "..", + "link": true + }, + "node_modules/wrap-ansi": { + "version": "6.2.0", + "resolved": "https://registry.npmjs.org/wrap-ansi/-/wrap-ansi-6.2.0.tgz", + "integrity": "sha512-r6lPcBGxZXlIcymEu7InxDMhdW0KDxpLgoFLcguasxCaJ/SOIZwINatK9KY/tf+ZrlywOKU0UDj3ATXUBfxJXA==", + "dependencies": { + "ansi-styles": "^4.0.0", + "string-width": "^4.1.0", + "strip-ansi": "^6.0.0" + }, + "engines": { + "node": ">=8" + } + } + }, + "dependencies": { + "@arrows/array": { + "version": "1.4.1", + "resolved": "https://registry.npmjs.org/@arrows/array/-/array-1.4.1.tgz", + "integrity": "sha512-MGYS8xi3c4tTy1ivhrVntFvufoNzje0PchjEz6G/SsWRgUKxL4tKwS6iPdO8vsaJYldagAeWMd5KRD0aX3Q39g==", + "requires": { + "@arrows/composition": "^1.2.2" + } + }, + "@arrows/composition": { + "version": "1.2.2", + "resolved": "https://registry.npmjs.org/@arrows/composition/-/composition-1.2.2.tgz", + "integrity": "sha512-9fh1yHwrx32lundiB3SlZ/VwuStPB4QakPsSLrGJFH6rCXvdrd060ivAZ7/2vlqPnEjBkPRRXOcG1YOu19p2GQ==" + }, + "@arrows/dispatch": { + "version": "1.0.3", + "resolved": "https://registry.npmjs.org/@arrows/dispatch/-/dispatch-1.0.3.tgz", + "integrity": "sha512-v/HwvrFonitYZM2PmBlAlCqVqxrkIIoiEuy5bQgn0BdfvlL0ooSBzcPzTMrtzY8eYktPyYcHg8fLbSgyybXEqw==", + "requires": { + "@arrows/composition": "^1.2.2" + } + }, + "@arrows/error": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/@arrows/error/-/error-1.0.2.tgz", + "integrity": "sha512-yvkiv1ay4Z3+Z6oQsUkedsQm5aFdyPpkBUQs8vejazU/RmANABx6bMMcBPPHI4aW43VPQmXFfBzr/4FExwWTEA==" + }, + "@arrows/multimethod": { + "version": "1.1.7", + "resolved": "https://registry.npmjs.org/@arrows/multimethod/-/multimethod-1.1.7.tgz", + "integrity": "sha512-EjHD3XuGAV4G28rm7mu8k7zQJh/EOizh104/p9i2ofGcnL5mgKONFH/Bq6H3SJjM+WDAlKcR9WBpNhaAKCnH2g==", + "requires": { + "@arrows/array": "^1.4.0", + "@arrows/composition": "^1.2.2", + "@arrows/error": "^1.0.2", + "fast-deep-equal": "^3.1.1" + } + }, + "@babel/runtime": { + "version": "7.14.8", + "resolved": "https://registry.npmjs.org/@babel/runtime/-/runtime-7.14.8.tgz", + "integrity": "sha512-twj3L8Og5SaCRCErB4x4ajbvBIVV77CGeFglHpeg5WC5FF8TZzBWXtTJ4MqaD9QszLYTtr+IsaAL2rEUevb+eg==", + "requires": { + "regenerator-runtime": "^0.13.4" + } + }, + "@probe.gl/bench": { + "version": "3.4.0", + "resolved": "https://registry.npmjs.org/@probe.gl/bench/-/bench-3.4.0.tgz", + "integrity": "sha512-S7iNPz5G3zEfEP0S4SAMvtj+dwP7EWfVBaA8Cy5CVIgM1lnpUbXvqoAJxlVEedNC32Icxwq65XQheufy1Zzmug==", + "requires": { + "@babel/runtime": "^7.0.0", + "probe.gl": "3.4.0" + } + }, + "@probe.gl/stats": { + "version": "3.4.0", + "resolved": "https://registry.npmjs.org/@probe.gl/stats/-/stats-3.4.0.tgz", + "integrity": "sha512-Gl37r9qGuiKadIvTZdSZvzCNOttJYw6RcY1oT0oDuB8r2uhuZAdSMQRQTy9FTinp6MY6O9wngGnV6EpQ8wSBAw==", + "requires": { + "@babel/runtime": "^7.0.0" + } + }, + "ansi-escapes": { + "version": "4.3.2", + "resolved": "https://registry.npmjs.org/ansi-escapes/-/ansi-escapes-4.3.2.tgz", + "integrity": "sha512-gKXj5ALrKWQLsYG9jlTRmR/xKluxHV+Z9QEwNIgCfM1/uwPMCuzVVnh5mwTd+OuBZcwSIMbqssNWRm1lE51QaQ==", + "requires": { + "type-fest": "^0.21.3" + } + }, + "ansi-regex": { + "version": "5.0.0", + "resolved": "https://registry.npmjs.org/ansi-regex/-/ansi-regex-5.0.0.tgz", + "integrity": "sha512-bY6fj56OUQ0hU1KjFNDQuJFezqKdrAyFdIevADiqrWHwSlbmBNMHp5ak2f40Pm8JTFyM2mqxkG6ngkHO11f/lg==" + }, + "ansi-styles": { + "version": "4.3.0", + "resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-4.3.0.tgz", + "integrity": "sha512-zbB9rCJAT1rbjiVDb2hqKFHNYLxgtk8NURxZ3IZwD3F6NtxbXZQCnnSi1Lkx+IDohdPlFp222wVALIheZJQSEg==", + "requires": { + "color-convert": "^2.0.1" + } + }, + "astral-regex": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/astral-regex/-/astral-regex-2.0.0.tgz", + "integrity": "sha512-Z7tMw1ytTXt5jqMcOP+OQteU1VuNK9Y02uuJtKQ1Sv69jXQKKg5cibLwGJow8yzZP+eAc18EmLGPal0bp36rvQ==" + }, + "at-least-node": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/at-least-node/-/at-least-node-1.0.0.tgz", + "integrity": "sha512-+q/t7Ekv1EDY2l6Gda6LLiX14rU9TV20Wa3ofeQmwPFZbOMo9DXrLbOjFaaclkXKWidIaopwAObQDqwWtGUjqg==" + }, + "benchmark": { + "version": "2.1.4", + "resolved": "https://registry.npmjs.org/benchmark/-/benchmark-2.1.4.tgz", + "integrity": "sha1-CfPeMckWQl1JjMLuVloOvzwqVik=", + "requires": { + "lodash": "^4.17.4", + "platform": "^1.3.3" + } + }, + "benny": { + "version": "3.6.15", + "resolved": "https://registry.npmjs.org/benny/-/benny-3.6.15.tgz", + "integrity": "sha512-kq6XVGGYVou3Y8KNPs3SEF881vi5fJ8sIf9w69D2rreiNfRicWVWK6u6/mObMw6BiexoHHumtipn5gcu0Tngng==", + "requires": { + "@arrows/composition": "^1.0.0", + "@arrows/dispatch": "^1.0.2", + "@arrows/multimethod": "^1.1.6", + "benchmark": "^2.1.4", + "fs-extra": "^9.0.1", + "json2csv": "^5.0.4", + "kleur": "^4.1.3", + "log-update": "^4.0.0", + "prettier": "^2.1.2", + "stats-median": "^1.0.1" + } + }, + "cli-cursor": { + "version": "3.1.0", + "resolved": "https://registry.npmjs.org/cli-cursor/-/cli-cursor-3.1.0.tgz", + "integrity": "sha512-I/zHAwsKf9FqGoXM4WWRACob9+SNukZTd94DWF57E4toouRulbCxcUh6RKUEOQlYTHJnzkPMySvPNaaSLNfLZw==", + "requires": { + "restore-cursor": "^3.1.0" + } + }, + "color-convert": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/color-convert/-/color-convert-2.0.1.tgz", + "integrity": "sha512-RRECPsj7iu/xb5oKYcsFHSppFNnsj/52OVTRKb4zP5onXwVF3zVmmToNcOfGC+CRDpfK/U584fMg38ZHCaElKQ==", + "requires": { + "color-name": "~1.1.4" + } + }, + "color-name": { + "version": "1.1.4", + "resolved": "https://registry.npmjs.org/color-name/-/color-name-1.1.4.tgz", + "integrity": "sha512-dOy+3AuW3a2wNbZHIuMZpTcgjGuLU/uBL/ubcZF9OXbDo8ff4O8yVp5Bf0efS8uEoYo5q4Fx7dY9OgQGXgAsQA==" + }, + "commander": { + "version": "6.2.1", + "resolved": "https://registry.npmjs.org/commander/-/commander-6.2.1.tgz", + "integrity": "sha512-U7VdrJFnJgo4xjrHpTzu0yrHPGImdsmD95ZlgYSEajAn2JKzDhDTPG9kBTefmObL2w/ngeZnilk+OV9CG3d7UA==" + }, + "denque": { + "version": "1.5.0", + "resolved": "https://registry.npmjs.org/denque/-/denque-1.5.0.tgz", + "integrity": "sha512-CYiCSgIF1p6EUByQPlGkKnP1M9g0ZV3qMIrqMqZqdwazygIA/YP2vrbcyl1h/WppKJTdl1F85cXIle+394iDAQ==" + }, + "emoji-regex": { + "version": "8.0.0", + "resolved": "https://registry.npmjs.org/emoji-regex/-/emoji-regex-8.0.0.tgz", + "integrity": "sha512-MSjYzcWNOA0ewAHpz0MxpYFvwg6yjy1NG3xteoqz644VCo/RPgnr1/GGt+ic3iJTzQ8Eu3TdM14SawnVUmGE6A==" + }, + "fast-deep-equal": { + "version": "3.1.3", + "resolved": "https://registry.npmjs.org/fast-deep-equal/-/fast-deep-equal-3.1.3.tgz", + "integrity": "sha512-f3qQ9oQy9j2AhBe/H9VC91wLmKBCCU/gDOnKNAYG5hswO7BLKj09Hc5HYNz9cGI++xlpDCIgDaitVs03ATR84Q==" + }, + "fs-extra": { + "version": "9.1.0", + "resolved": "https://registry.npmjs.org/fs-extra/-/fs-extra-9.1.0.tgz", + "integrity": "sha512-hcg3ZmepS30/7BSFqRvoo3DOMQu7IjqxO5nCDt+zM9XWjb33Wg7ziNT+Qvqbuc3+gWpzO02JubVyk2G4Zvo1OQ==", + "requires": { + "at-least-node": "^1.0.0", + "graceful-fs": "^4.2.0", + "jsonfile": "^6.0.1", + "universalify": "^2.0.0" + } + }, + "graceful-fs": { + "version": "4.2.6", + "resolved": "https://registry.npmjs.org/graceful-fs/-/graceful-fs-4.2.6.tgz", + "integrity": "sha512-nTnJ528pbqxYanhpDYsi4Rd8MAeaBA67+RZ10CM1m3bTAVFEDcd5AuA4a6W5YkGZ1iNXHzZz8T6TBKLeBuNriQ==" + }, + "is-fullwidth-code-point": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/is-fullwidth-code-point/-/is-fullwidth-code-point-3.0.0.tgz", + "integrity": "sha512-zymm5+u+sCsSWyD9qNaejV3DFvhCKclKdizYaJUuHA83RLjb7nSuGnddCHGv0hk+KY7BMAlsWeK4Ueg6EV6XQg==" + }, + "json2csv": { + "version": "5.0.6", + "resolved": "https://registry.npmjs.org/json2csv/-/json2csv-5.0.6.tgz", + "integrity": "sha512-0/4Lv6IenJV0qj2oBdgPIAmFiKKnh8qh7bmLFJ+/ZZHLjSeiL3fKKGX3UryvKPbxFbhV+JcYo9KUC19GJ/Z/4A==", + "requires": { + "commander": "^6.1.0", + "jsonparse": "^1.3.1", + "lodash.get": "^4.4.2" + } + }, + "jsonfile": { + "version": "6.1.0", + "resolved": "https://registry.npmjs.org/jsonfile/-/jsonfile-6.1.0.tgz", + "integrity": "sha512-5dgndWOriYSm5cnYaJNhalLNDKOqFwyDB/rr1E9ZsGciGvKPs8R2xYGCacuf3z6K1YKDz182fd+fY3cn3pMqXQ==", + "requires": { + "graceful-fs": "^4.1.6", + "universalify": "^2.0.0" + } + }, + "jsonparse": { + "version": "1.3.1", + "resolved": "https://registry.npmjs.org/jsonparse/-/jsonparse-1.3.1.tgz", + "integrity": "sha1-P02uSpH6wxX3EGL4UhzCOfE2YoA=" + }, + "kleur": { + "version": "4.1.4", + "resolved": "https://registry.npmjs.org/kleur/-/kleur-4.1.4.tgz", + "integrity": "sha512-8QADVssbrFjivHWQU7KkMgptGTl6WAcSdlbBPY4uNF+mWr6DGcKrvY2w4FQJoXch7+fKMjj0dRrL75vk3k23OA==" + }, + "lodash": { + "version": "4.17.21", + "resolved": "https://registry.npmjs.org/lodash/-/lodash-4.17.21.tgz", + "integrity": "sha512-v2kDEe57lecTulaDIuNTPy3Ry4gLGJ6Z1O3vE1krgXZNrsQ+LFTGHVxVjcXPs17LhbZVGedAJv8XZ1tvj5FvSg==" + }, + "lodash.get": { + "version": "4.4.2", + "resolved": "https://registry.npmjs.org/lodash.get/-/lodash.get-4.4.2.tgz", + "integrity": "sha1-LRd/ZS+jHpObRDjVNBSZ36OCXpk=" + }, + "log-update": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/log-update/-/log-update-4.0.0.tgz", + "integrity": "sha512-9fkkDevMefjg0mmzWFBW8YkFP91OrizzkW3diF7CpG+S2EYdy4+TVfGwz1zeF8x7hCx1ovSPTOE9Ngib74qqUg==", + "requires": { + "ansi-escapes": "^4.3.0", + "cli-cursor": "^3.1.0", + "slice-ansi": "^4.0.0", + "wrap-ansi": "^6.2.0" + } + }, + "mimic-fn": { + "version": "2.1.0", + "resolved": "https://registry.npmjs.org/mimic-fn/-/mimic-fn-2.1.0.tgz", + "integrity": "sha512-OqbOk5oEQeAZ8WXWydlu9HJjz9WVdEIvamMCcXmuqUYjTknH/sqsWvhQ3vgwKFRR1HpjvNBKQ37nbJgYzGqGcg==" + }, + "onetime": { + "version": "5.1.2", + "resolved": "https://registry.npmjs.org/onetime/-/onetime-5.1.2.tgz", + "integrity": "sha512-kbpaSSGJTWdAY5KPVeMOKXSrPtr8C8C7wodJbcsd51jRnmD+GZu8Y0VoU6Dm5Z4vWr0Ig/1NKuWRKf7j5aaYSg==", + "requires": { + "mimic-fn": "^2.1.0" + } + }, + "platform": { + "version": "1.3.6", + "resolved": "https://registry.npmjs.org/platform/-/platform-1.3.6.tgz", + "integrity": "sha512-fnWVljUchTro6RiCFvCXBbNhJc2NijN7oIQxbwsyL0buWJPG85v81ehlHI9fXrJsMNgTofEoWIQeClKpgxFLrg==" + }, + "prettier": { + "version": "2.3.2", + "resolved": "https://registry.npmjs.org/prettier/-/prettier-2.3.2.tgz", + "integrity": "sha512-lnJzDfJ66zkMy58OL5/NY5zp70S7Nz6KqcKkXYzn2tMVrNxvbqaBpg7H3qHaLxCJ5lNMsGuM8+ohS7cZrthdLQ==" + }, + "probe.gl": { + "version": "3.4.0", + "resolved": "https://registry.npmjs.org/probe.gl/-/probe.gl-3.4.0.tgz", + "integrity": "sha512-9CLByZATuhuG/Viq3ckfWU+dAhb7dMmjzsyCy4s7ds9ueTejcVRENxL197/XacOK/AN61YrEERB0QnouB0Qc0Q==", + "requires": { + "@babel/runtime": "^7.0.0", + "@probe.gl/stats": "3.4.0" + } + }, + "redis-commands": { + "version": "1.7.0", + "resolved": "https://registry.npmjs.org/redis-commands/-/redis-commands-1.7.0.tgz", + "integrity": "sha512-nJWqw3bTFy21hX/CPKHth6sfhZbdiHP6bTawSgQBlKOVRG7EZkfHbbHwQJnrE4vsQf0CMNE+3gJ4Fmm16vdVlQ==" + }, + "redis-errors": { + "version": "1.2.0", + "resolved": "https://registry.npmjs.org/redis-errors/-/redis-errors-1.2.0.tgz", + "integrity": "sha1-62LSrbFeTq9GEMBK/hUpOEJQq60=" + }, + "redis-parser": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/redis-parser/-/redis-parser-3.0.0.tgz", + "integrity": "sha1-tm2CjNyv5rS4pCin3vTGvKwxyLQ=", + "requires": { + "redis-errors": "^1.0.0" + } + }, + "regenerator-runtime": { + "version": "0.13.9", + "resolved": "https://registry.npmjs.org/regenerator-runtime/-/regenerator-runtime-0.13.9.tgz", + "integrity": "sha512-p3VT+cOEgxFsRRA9X4lkI1E+k2/CtnKtU4gcxyaCUreilL/vqI6CdZ3wxVUx3UOUg+gnUOQQcRI7BmSI656MYA==" + }, + "restore-cursor": { + "version": "3.1.0", + "resolved": "https://registry.npmjs.org/restore-cursor/-/restore-cursor-3.1.0.tgz", + "integrity": "sha512-l+sSefzHpj5qimhFSE5a8nufZYAM3sBSVMAPtYkmC+4EH2anSGaEMXSD0izRQbu9nfyQ9y5JrVmp7E8oZrUjvA==", + "requires": { + "onetime": "^5.1.0", + "signal-exit": "^3.0.2" + } + }, + "signal-exit": { + "version": "3.0.3", + "resolved": "https://registry.npmjs.org/signal-exit/-/signal-exit-3.0.3.tgz", + "integrity": "sha512-VUJ49FC8U1OxwZLxIbTTrDvLnf/6TDgxZcK8wxR8zs13xpx7xbG60ndBlhNrFi2EMuFRoeDoJO7wthSLq42EjA==" + }, + "slice-ansi": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/slice-ansi/-/slice-ansi-4.0.0.tgz", + "integrity": "sha512-qMCMfhY040cVHT43K9BFygqYbUPFZKHOg7K73mtTWJRb8pyP3fzf4Ixd5SzdEJQ6MRUg/WBnOLxghZtKKurENQ==", + "requires": { + "ansi-styles": "^4.0.0", + "astral-regex": "^2.0.0", + "is-fullwidth-code-point": "^3.0.0" + } + }, + "stats-median": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/stats-median/-/stats-median-1.0.1.tgz", + "integrity": "sha512-IYsheLg6dasD3zT/w9+8Iq9tcIQqqu91ZIpJOnIEM25C3X/g4Tl8mhXwW2ZQpbrsJISr9+wizEYgsibN5/b32Q==" + }, + "string-width": { + "version": "4.2.2", + "resolved": "https://registry.npmjs.org/string-width/-/string-width-4.2.2.tgz", + "integrity": "sha512-XBJbT3N4JhVumXE0eoLU9DCjcaF92KLNqTmFCnG1pf8duUxFGwtP6AD6nkjw9a3IdiRtL3E2w3JDiE/xi3vOeA==", + "requires": { + "emoji-regex": "^8.0.0", + "is-fullwidth-code-point": "^3.0.0", + "strip-ansi": "^6.0.0" + } + }, + "strip-ansi": { + "version": "6.0.0", + "resolved": "https://registry.npmjs.org/strip-ansi/-/strip-ansi-6.0.0.tgz", + "integrity": "sha512-AuvKTrTfQNYNIctbR1K/YGTR1756GycPsg7b9bdV9Duqur4gv6aKqHXah67Z8ImS7WEz5QVcOtlfW2rZEugt6w==", + "requires": { + "ansi-regex": "^5.0.0" + } + }, + "type-fest": { + "version": "0.21.3", + "resolved": "https://registry.npmjs.org/type-fest/-/type-fest-0.21.3.tgz", + "integrity": "sha512-t0rzBq87m3fVcduHDUFhKmyyX+9eo6WQjZvf51Ea/M0Q7+T374Jp1aUiyUl0GKxp8M/OETVHSDvmkyPgvX+X2w==" + }, + "universalify": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/universalify/-/universalify-2.0.0.tgz", + "integrity": "sha512-hAZsKq7Yy11Zu1DE0OzWjw7nnLZmJZYTDZZyEFHZdUhV8FkH5MCfoU1XMaxXovpyW5nq5scPqq0ZDP9Zyl04oQ==" + }, + "v3": { + "version": "npm:redis@3.1.2", + "resolved": "https://registry.npmjs.org/redis/-/redis-3.1.2.tgz", + "integrity": "sha512-grn5KoZLr/qrRQVwoSkmzdbw6pwF+/rwODtrOr6vuBRiR/f3rjSTGupbF90Zpqm2oenix8Do6RV7pYEkGwlKkw==", + "requires": { + "denque": "^1.5.0", + "redis-commands": "^1.7.0", + "redis-errors": "^1.2.0", + "redis-parser": "^3.0.0" + } + }, + "v4": { + "version": "file:..", + "requires": { + "@istanbuljs/nyc-config-typescript": "^1.0.1", + "@types/mocha": "^9.0.0", + "@types/node": "^16.4.5", + "@types/sinon": "^10.0.2", + "@types/which": "^2.0.1", + "@types/yallist": "^4.0.1", + "cluster-key-slot": "1.1.0", + "mocha": "^9.0.3", + "nyc": "^15.1.0", + "redis-parser": "3.0.0", + "release-it": "^14.10.1", + "sinon": "^11.1.2", + "source-map-support": "^0.5.19", + "ts-node": "^10.1.0", + "typedoc": "^0.21.4", + "typedoc-github-wiki-theme": "^0.5.1", + "typedoc-plugin-markdown": "^3.10.4", + "typescript": "^4.3.5", + "which": "^2.0.2", + "yallist": "4.0.0" + } + }, + "wrap-ansi": { + "version": "6.2.0", + "resolved": "https://registry.npmjs.org/wrap-ansi/-/wrap-ansi-6.2.0.tgz", + "integrity": "sha512-r6lPcBGxZXlIcymEu7InxDMhdW0KDxpLgoFLcguasxCaJ/SOIZwINatK9KY/tf+ZrlywOKU0UDj3ATXUBfxJXA==", + "requires": { + "ansi-styles": "^4.0.0", + "string-width": "^4.1.0", + "strip-ansi": "^6.0.0" + } + } + } +} diff --git a/benchmark/package.json b/benchmark/package.json new file mode 100644 index 00000000000..5226a5b0c89 --- /dev/null +++ b/benchmark/package.json @@ -0,0 +1,17 @@ +{ + "name": "benchmark", + "private": true, + "description": "", + "main": "index.js", + "type": "module", + "scripts": { + "start": "node ./" + }, + "author": "", + "license": "ISC", + "dependencies": { + "benny": "^3.6.15", + "v3": "npm:redis@3.1.2", + "v4": "file:../" + } +} diff --git a/benchmarks/diff_multi_bench_output.js b/benchmarks/diff_multi_bench_output.js deleted file mode 100755 index c3ed47a5fb5..00000000000 --- a/benchmarks/diff_multi_bench_output.js +++ /dev/null @@ -1,95 +0,0 @@ -'use strict'; - -var fs = require('fs'); -var metrics = require('metrics'); -// `node diff_multi_bench_output.js beforeBench.txt afterBench.txt` -var file1 = process.argv[2]; -var file2 = process.argv[3]; - -if (!file1 || !file2) { - console.log('Please supply two file arguments:'); - var n = __filename; - n = n.substring(n.lastIndexOf('/', n.length)); - console.log(' node .' + n + ' benchBefore.txt benchAfter.txt\n'); - console.log('To generate the benchmark files, run'); - console.log(' npm run benchmark > benchBefore.txt\n'); - console.log('Thank you for benchmarking responsibly.'); - return; -} - -var before_lines = fs.readFileSync(file1, 'utf8').split('\n'); -var after_lines = fs.readFileSync(file2, 'utf8').split('\n'); -var total_ops = new metrics.Histogram.createUniformHistogram(); - -console.log('Comparing before,', file1, '(', before_lines.length, 'lines)', 'to after,', file2, '(', after_lines.length, 'lines)'); - -function is_whitespace (s) { - return !!s.trim(); -} - -function pad (input, len, chr, right) { - var str = input.toString(); - chr = chr || ' '; - - if (right) { - while (str.length < len) { - str += chr; - } - } else { - while (str.length < len) { - str = chr + str; - } - } - return str; -} - -// green if greater than 0, red otherwise -function humanize_diff (num, unit, toFixed) { - unit = unit || ''; - if (num > 0) { - return ' +' + pad(num.toFixed(toFixed || 0) + unit, 7); - } - return ' -' + pad(Math.abs(num).toFixed(toFixed || 0) + unit, 7); -} - -function command_name (words) { - var line = words.join(' '); - return line.substr(0, line.indexOf(',')); -} - -before_lines.forEach(function (b, i) { - var a = after_lines[i]; - if (!a || !b || !b.trim() || !a.trim()) { - // console.log('#ignored#', '>'+a+'<', '>'+b+'<'); - return; - } - var b_words = b.split(' ').filter(is_whitespace); - var a_words = a.split(' ').filter(is_whitespace); - - var ops = [b_words, a_words].map(function (words) { - // console.log(words); - return words.slice(-2, -1) | 0; - }).filter(function (num) { - var isNaN = !num && num !== 0; - return !isNaN; - }); - if (ops.length !== 2) { - return; - } - var delta = ops[1] - ops[0]; - var pct = +((delta / ops[0]) * 100); - ops[0] = pad(ops[0], 6); - ops[1] = pad(ops[1], 6); - total_ops.update(delta); - delta = humanize_diff(delta); - var small_delta = pct < 3 && pct > -3; - // Let's mark differences above 20% bold - var big_delta = pct > 20 || pct < -20 ? ';1' : ''; - pct = humanize_diff(pct, '', 2) + '%'; - var str = pad((command_name(a_words) === command_name(b_words) ? command_name(a_words) + ':' : '404:'), 14, false, true) + - (pad(ops.join(' -> '), 15) + ' ops/sec (∆' + delta + pct + ')'); - str = (small_delta ? '' : (/-[^>]/.test(str) ? '\x1b[31' : '\x1b[32') + big_delta + 'm') + str + '\x1b[0m'; - console.log(str); -}); - -console.log('Mean difference in ops/sec:', humanize_diff(total_ops.mean(), '', 1)); diff --git a/benchmarks/multi_bench.js b/benchmarks/multi_bench.js deleted file mode 100644 index 86cf9329ce9..00000000000 --- a/benchmarks/multi_bench.js +++ /dev/null @@ -1,291 +0,0 @@ -'use strict'; - -var path = require('path'); -var RedisProcess = require('../test/lib/redis-process'); -var rp; -var client_nr = 0; -var redis = require('../index'); -var totalTime = 0; -var metrics = require('metrics'); -var tests = []; -// var bluebird = require('bluebird'); -// bluebird.promisifyAll(redis.RedisClient.prototype); -// bluebird.promisifyAll(redis.Multi.prototype); - -function returnArg (name, def) { - var matches = process.argv.filter(function (entry) { - return entry.indexOf(name + '=') === 0; - }); - if (matches.length) { - return matches[0].substr(name.length + 1); - } - return def; -} -var num_clients = returnArg('clients', 1); -var run_time = returnArg('time', 2500); // ms -var pipeline = returnArg('pipeline', 1); // number of concurrent commands -var versions_logged = false; -var client_options = { - parser: returnArg('parser', 'javascript'), - path: returnArg('socket') // '/tmp/redis.sock' -}; -var small_str, large_str, small_buf, large_buf, very_large_str, very_large_buf; - -function lpad (input, len, chr) { - var str = input.toString(); - chr = chr || ' '; - while (str.length < len) { - str = chr + str; - } - return str; -} - -metrics.Histogram.prototype.print_line = function () { - var obj = this.printObj(); - return lpad((obj.mean / 1e6).toFixed(2), 6) + '/' + lpad((obj.max / 1e6).toFixed(2), 6); -}; - -function Test (args) { - this.args = args; - this.args.pipeline = +pipeline; - this.callback = null; - this.clients = []; - this.clients_ready = 0; - this.commands_sent = 0; - this.commands_completed = 0; - this.max_pipeline = +pipeline; - this.batch_pipeline = this.args.batch || 0; - this.client_options = args.client_options || {}; - this.client_options.parser = client_options.parser; - this.client_options.connect_timeout = 1000; - if (client_options.path) { - this.client_options.path = client_options.path; - } - this.connect_latency = new metrics.Histogram(); - this.ready_latency = new metrics.Histogram(); - this.command_latency = new metrics.Histogram(); -} - -Test.prototype.run = function (callback) { - var i; - this.callback = callback; - for (i = 0; i < num_clients ; i++) { - this.new_client(i); - } -}; - -Test.prototype.new_client = function (id) { - var self = this, new_client; - - new_client = redis.createClient(this.client_options); - new_client.create_time = Date.now(); - - new_client.on('connect', function () { - self.connect_latency.update(Date.now() - new_client.create_time); - }); - - new_client.on('ready', function () { - if (!versions_logged) { - console.log( - 'clients: ' + num_clients + - ', NodeJS: ' + process.versions.node + - ', Redis: ' + new_client.server_info.redis_version + - ', parser: ' + client_options.parser + - ', connected by: ' + (client_options.path ? 'socket' : 'tcp') - ); - versions_logged = true; - } - self.ready_latency.update(Date.now() - new_client.create_time); - self.clients_ready++; - if (self.clients_ready === self.clients.length) { - self.on_clients_ready(); - } - }); - - // If no redis server is running, start one - new_client.on('error', function (err) { - if (err.code === 'CONNECTION_BROKEN') { - throw err; - } - if (rp) { - return; - } - rp = true; - var conf = '../test/conf/redis.conf'; - RedisProcess.start(function (err, _rp) { - if (err) { - throw err; - } - rp = _rp; - }, path.resolve(__dirname, conf)); - }); - - self.clients[id] = new_client; -}; - -Test.prototype.on_clients_ready = function () { - process.stdout.write(lpad(this.args.descr, 13) + ', ' + (this.args.batch ? lpad('batch ' + this.args.batch, 9) : lpad(this.args.pipeline, 9)) + '/' + this.clients_ready + ' '); - this.test_start = Date.now(); - this.fill_pipeline(); -}; - -Test.prototype.fill_pipeline = function () { - var pipeline = this.commands_sent - this.commands_completed; - - if (this.test_start < Date.now() - run_time) { - if (this.ended) { - return; - } - this.ended = true; - this.print_stats(); - this.stop_clients(); - return; - } - - if (this.batch_pipeline) { - this.batch(); - } else { - while (pipeline < this.max_pipeline) { - this.commands_sent++; - pipeline++; - this.send_next(); - } - } -}; - -Test.prototype.batch = function () { - var self = this, - cur_client = client_nr++ % this.clients.length, - start = process.hrtime(), - i = 0, - batch = this.clients[cur_client].batch(); - - while (i++ < this.batch_pipeline) { - this.commands_sent++; - batch[this.args.command](this.args.args); - } - - batch.exec(function (err, res) { - if (err) { - throw err; - } - self.commands_completed += res.length; - self.command_latency.update(process.hrtime(start)[1]); - self.fill_pipeline(); - }); -}; - -Test.prototype.stop_clients = function () { - var self = this; - - this.clients.forEach(function (client, pos) { - if (pos === self.clients.length - 1) { - client.quit(function (err, res) { - self.callback(); - }); - } else { - client.quit(); - } - }); -}; - -Test.prototype.send_next = function () { - var self = this, - cur_client = this.commands_sent % this.clients.length, - start = process.hrtime(); - - this.clients[cur_client][this.args.command](this.args.args, function (err, res) { - if (err) { - throw err; - } - self.commands_completed++; - self.command_latency.update(process.hrtime(start)[1]); - self.fill_pipeline(); - }); -}; - -Test.prototype.print_stats = function () { - var duration = Date.now() - this.test_start; - totalTime += duration; - - console.log('avg/max: ' + this.command_latency.print_line() + lpad(duration, 5) + 'ms total, ' + - lpad(Math.round(this.commands_completed / (duration / 1000)), 7) + ' ops/sec'); -}; - -small_str = '1234'; -small_buf = Buffer.from(small_str); -large_str = (new Array(4096 + 1).join('-')); -large_buf = Buffer.from(large_str); -very_large_str = (new Array((4 * 1024 * 1024) + 1).join('-')); -very_large_buf = Buffer.from(very_large_str); - -tests.push(new Test({descr: 'PING', command: 'ping', args: []})); -tests.push(new Test({descr: 'PING', command: 'ping', args: [], batch: 50})); - -tests.push(new Test({descr: 'SET 4B str', command: 'set', args: ['foo_rand000000000000', small_str]})); -tests.push(new Test({descr: 'SET 4B str', command: 'set', args: ['foo_rand000000000000', small_str], batch: 50})); - -tests.push(new Test({descr: 'SET 4B buf', command: 'set', args: ['foo_rand000000000000', small_buf]})); -tests.push(new Test({descr: 'SET 4B buf', command: 'set', args: ['foo_rand000000000000', small_buf], batch: 50})); - -tests.push(new Test({descr: 'GET 4B str', command: 'get', args: ['foo_rand000000000000']})); -tests.push(new Test({descr: 'GET 4B str', command: 'get', args: ['foo_rand000000000000'], batch: 50})); - -tests.push(new Test({descr: 'GET 4B buf', command: 'get', args: ['foo_rand000000000000'], client_options: { return_buffers: true} })); -tests.push(new Test({descr: 'GET 4B buf', command: 'get', args: ['foo_rand000000000000'], batch: 50, client_options: { return_buffers: true} })); - -tests.push(new Test({descr: 'SET 4KiB str', command: 'set', args: ['foo_rand000000000001', large_str]})); -tests.push(new Test({descr: 'SET 4KiB str', command: 'set', args: ['foo_rand000000000001', large_str], batch: 50})); - -tests.push(new Test({descr: 'SET 4KiB buf', command: 'set', args: ['foo_rand000000000001', large_buf]})); -tests.push(new Test({descr: 'SET 4KiB buf', command: 'set', args: ['foo_rand000000000001', large_buf], batch: 50})); - -tests.push(new Test({descr: 'GET 4KiB str', command: 'get', args: ['foo_rand000000000001']})); -tests.push(new Test({descr: 'GET 4KiB str', command: 'get', args: ['foo_rand000000000001'], batch: 50})); - -tests.push(new Test({descr: 'GET 4KiB buf', command: 'get', args: ['foo_rand000000000001'], client_options: { return_buffers: true} })); -tests.push(new Test({descr: 'GET 4KiB buf', command: 'get', args: ['foo_rand000000000001'], batch: 50, client_options: { return_buffers: true} })); - -tests.push(new Test({descr: 'INCR', command: 'incr', args: ['counter_rand000000000000']})); -tests.push(new Test({descr: 'INCR', command: 'incr', args: ['counter_rand000000000000'], batch: 50})); - -tests.push(new Test({descr: 'LPUSH', command: 'lpush', args: ['mylist', small_str]})); -tests.push(new Test({descr: 'LPUSH', command: 'lpush', args: ['mylist', small_str], batch: 50})); - -tests.push(new Test({descr: 'LRANGE 10', command: 'lrange', args: ['mylist', '0', '9']})); -tests.push(new Test({descr: 'LRANGE 10', command: 'lrange', args: ['mylist', '0', '9'], batch: 50})); - -tests.push(new Test({descr: 'LRANGE 100', command: 'lrange', args: ['mylist', '0', '99']})); -tests.push(new Test({descr: 'LRANGE 100', command: 'lrange', args: ['mylist', '0', '99'], batch: 50})); - -tests.push(new Test({descr: 'SET 4MiB str', command: 'set', args: ['foo_rand000000000002', very_large_str]})); -tests.push(new Test({descr: 'SET 4MiB str', command: 'set', args: ['foo_rand000000000002', very_large_str], batch: 20})); - -tests.push(new Test({descr: 'SET 4MiB buf', command: 'set', args: ['foo_rand000000000002', very_large_buf]})); -tests.push(new Test({descr: 'SET 4MiB buf', command: 'set', args: ['foo_rand000000000002', very_large_buf], batch: 20})); - -tests.push(new Test({descr: 'GET 4MiB str', command: 'get', args: ['foo_rand000000000002']})); -tests.push(new Test({descr: 'GET 4MiB str', command: 'get', args: ['foo_rand000000000002'], batch: 20})); - -tests.push(new Test({descr: 'GET 4MiB buf', command: 'get', args: ['foo_rand000000000002'], client_options: { return_buffers: true} })); -tests.push(new Test({descr: 'GET 4MiB buf', command: 'get', args: ['foo_rand000000000002'], batch: 20, client_options: { return_buffers: true} })); - -function next () { - var test = tests.shift(); - if (test) { - test.run(function () { - next(); - }); - } else if (rp) { - // Stop the redis process if started by the benchmark - rp.stop(function () { - rp = undefined; - next(); - }); - } else { - console.log('End of tests. Total time elapsed:', totalTime, 'ms'); - process.exit(0); - } -} - -next(); diff --git a/docs/FAQ.md b/docs/FAQ.md new file mode 100644 index 00000000000..b5074e73025 --- /dev/null +++ b/docs/FAQ.md @@ -0,0 +1,13 @@ +# F.A.Q. + +Nobody has *actually* asked these questions. But, we needed somewhere to put all the important bits and bobs that didn't fit anywhere else. So, here you go! + +## What happens when the network goes down? + +When a socket closed unexpectedly, all the commands that were already sent will reject as they might have been executed on the server. The rest will remain queued in memory until a new socket is established. If the client is closed—either by returning an error from [`reconnectStrategy`](./client-configuration.md#reconnect-strategy) or by manually calling `.disconnect()`—they will be rejected. + +## How are commands batched? + +Commands are pipelined using [`queueMicrotask`](https://nodejs.org/api/globals.html#globals_queuemicrotask_callback). Commands from the same "tick" will be sent in batches and respect the [`writableHighWaterMark`](https://nodejs.org/api/stream.html#stream_new_stream_writable_options). + +If `socket.write()` returns `false`—meaning that ["all or part of the data was queued in user memory"](https://nodejs.org/api/net.html#net_socket_write_data_encoding_callback:~:text=all%20or%20part%20of%20the%20data%20was%20queued%20in%20user%20memory)—the commands will stack in memory until the [`drain`](https://nodejs.org/api/net.html#net_event_drain) event is fired. diff --git a/docs/client-configuration.md b/docs/client-configuration.md new file mode 100644 index 00000000000..4b93340ad8f --- /dev/null +++ b/docs/client-configuration.md @@ -0,0 +1,30 @@ +# `createClient` configuration + +| Property | Default | Description | +|--------------------------|------------------------------------------|------------------------------------------------------------------------------------------------------------------------------| +| socket | | Object defining socket connection properties | +| socket.url | | `[redis[s]:]//[[username][:password]@][host][:port]` | +| socket.host | `'localhost'` | Hostname to connect to | +| socket.port | `6379` | Port to connect to | +| socket.username | | ACL username ([see ACL guide](https://redis.io/topics/acl)) | +| socket.password | | ACL password or the old "--requirepass" password | +| socket.connectTimeout | `5000` | The timeout for connecting to the Redis Server (in milliseconds) | +| socket.noDelay | `true` | Enable/disable the use of [`Nagle's algorithm`](https://nodejs.org/api/net.html#net_socket_setnodelay_nodelay) | +| socket.keepAlive | `5000` | Enable/disable the [`keep-alive`](https://nodejs.org/api/net.html#net_socket_setkeepalive_enable_initialdelay) functionality | +| socket.tls | | Set to `true` to enable [TLS Configuration](https://nodejs.org/api/tls.html#tls_tls_connect_options_callback) | +| socket.reconnectStrategy | `retries => Math.min(retries * 50, 500)` | A function containing the [Reconnect Strategy](#reconnect-strategy) logic | +| modules | | Object defining which [Redis Modules](https://redis.io/modules) to include (TODO - document) | +| 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)) | +| isolationPoolOptions | | See the [Isolated Execution Guide](./isolated-execution.md) | + +## Reconnect Strategy + +You can implement a custom reconnect strategy as a function that should: + +- Receives the number of retries attempted so far. +- Should return `number | Error`: + - `number`: the time in milliseconds to wait before trying to reconnect again. + - `Error`: close the client and flush the commands queue. diff --git a/docs/isolated-execution.md b/docs/isolated-execution.md new file mode 100644 index 00000000000..78b34252a0f --- /dev/null +++ b/docs/isolated-execution.md @@ -0,0 +1,67 @@ +# Isolated Execution + +Sometimes you want to run your commands on an exclusive connection. There are a few reasons to do this: + +- You're using [transactions]() and need to `WATCH` a key or keys for changes. +- You want to run a blocking command that will take over the connection, such as `BLPOP` or `BLMOVE`. +- You're using the `MONITOR` command which also takes over a connection. + +Below are several examples of how to use isolated execution. + +> NOTE: Behind the scences we're using [`generic-pool`](https://www.npmjs.com/package/generic-pool) to provide a pool of connections that can be isolated. Go there to learn more. + +## The Simple Secnario + +This just isolates execution on a single connection. Do what you want with that connection: + +```typescript +await client.executeIsolated(async isolatedClient => { + await isolatedClient.set('key', 'value'); + await isolatedClient.get('key'); +}); +``` + +## Transactions + +Things get a little more complex with transactions. Here we are `.watch()`ing some keys. If the keys change during the transaction, a `WatchError` is thrown when `.exec()` is called: + +```typescript +try { + await client.executeIsolated(async isolatedClient => { + await isolatedClient.watch('key'); + + const multi = isolatedClient.multi() + .ping() + .get('key'); + + if (Math.random() > 0.5) { + await isolatedClient.watch('another-key'); + multi.set('another-key', await isolatedClient.get('another-key') / 2); + } + + return multi.exec(); + }); +} catch (err) { + if (err instanceof WatchError) { + // the transaction aborted + } +} + +``` + +## Blocking Commands + +For blocking commands, you can execute a tidy little one-liner: + +```typescript +await client.executeIsolated(isolatedClient => isolatedClient.blPop('key')); +``` + +Or, you can just run the command directly, and provide the `isolated` option: + +```typescript +await client.blPop( + commandOptions({ isolated: true }), + 'key' +); +``` diff --git a/docs/v3-to-v4.md b/docs/v3-to-v4.md new file mode 100644 index 00000000000..7c3e9880431 --- /dev/null +++ b/docs/v3-to-v4.md @@ -0,0 +1,35 @@ +# v3 to v4 Migration Guide + +Version 4 of Node Redis is a major refactor. While we have tried to maintain backwards compatibility where possible, several interfaces have changed. Read this guide to understand the differences and how to implement version 4 in your application. + +## Breaking Changes + +See the [Change Log](../CHANGELOG.md). + +## Promises + +Node Redis now uses native [Promises](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Promise) by default for all functions. + +## Legacy Mode + +Use legacy mode to preserve the backwards compatibility of commands while still getting access to the updated experience: + +```typescript +const client = createClient({ + legacyMode: true +}); + +// legacy mode +client.set('key', 'value', 'NX', (err, reply) => { + // ... +}); + +// version 4 interface is still accessible +await client.v4.set('key', 'value', { + NX: true +}); +``` + +## `createClient` + +The configuration object passed to `createClient` has changed significantly with this release. See the [client configuration guide](./client-configuration.md) for details. diff --git a/examples/auth.js b/examples/auth.js deleted file mode 100644 index e36b7006562..00000000000 --- a/examples/auth.js +++ /dev/null @@ -1,7 +0,0 @@ -'use strict'; - -var redis = require('redis'); -// The client stashes the password and will reauthenticate on every connect. -redis.createClient({ - password: 'somepass' -}); diff --git a/examples/backpressure_drain.js b/examples/backpressure_drain.js deleted file mode 100644 index 0e9140c6b29..00000000000 --- a/examples/backpressure_drain.js +++ /dev/null @@ -1,34 +0,0 @@ -'use strict'; - -var redis = require('../index'); -var client = redis.createClient(); -var remaining_ops = 100000; -var paused = false; - -function op () { - if (remaining_ops <= 0) { - console.error('Finished.'); - process.exit(0); - } - - remaining_ops--; - client.hset('test hash', 'val ' + remaining_ops, remaining_ops); - if (client.should_buffer === true) { - console.log('Pausing at ' + remaining_ops); - paused = true; - } else { - setTimeout(op, 1); - } -} - -client.on('drain', function () { - if (paused) { - console.log('Resuming at ' + remaining_ops); - paused = false; - process.nextTick(op); - } else { - console.log('Got drain while not paused at ' + remaining_ops); - } -}); - -op(); diff --git a/examples/eval.js b/examples/eval.js deleted file mode 100644 index cffdb548a16..00000000000 --- a/examples/eval.js +++ /dev/null @@ -1,14 +0,0 @@ -'use strict'; - -var redis = require('../index'); -var client = redis.createClient(); - -client.eval('return 100.5', 0, function (err, res) { - console.dir(err); - console.dir(res); -}); - -client.eval([ 'return 100.5', 0 ], function (err, res) { - console.dir(err); - console.dir(res); -}); diff --git a/examples/extend.js b/examples/extend.js deleted file mode 100644 index 8128b41bdca..00000000000 --- a/examples/extend.js +++ /dev/null @@ -1,26 +0,0 @@ -'use strict'; - -var redis = require('redis'); -var client = redis.createClient(); - -// Extend the RedisClient prototype to add a custom method -// This one converts the results from 'INFO' into a JavaScript Object - -redis.RedisClient.prototype.parse_info = function (callback) { - this.info(function (err, res) { - var lines = res.toString().split('\r\n').sort(); - var obj = {}; - lines.forEach(function (line) { - var parts = line.split(':'); - if (parts[1]) { - obj[parts[0]] = parts[1]; - } - }); - callback(obj); - }); -}; - -client.parse_info(function (info) { - console.dir(info); - client.quit(); -}); diff --git a/examples/file.js b/examples/file.js deleted file mode 100644 index 0bacd723b32..00000000000 --- a/examples/file.js +++ /dev/null @@ -1,38 +0,0 @@ -'use strict'; - -// Read a file from disk, store it in Redis, then read it back from Redis. - -var redis = require('redis'); -var client = redis.createClient({ - return_buffers: true -}); -var fs = require('fs'); -var assert = require('assert'); -var filename = 'grumpyCat.jpg'; - -// Get the file I use for testing like this: -// curl http://media4.popsugar-assets.com/files/2014/08/08/878/n/1922507/caef16ec354ca23b_thumb_temp_cover_file32304521407524949.xxxlarge/i/Funny-Cat-GIFs.jpg -o grumpyCat.jpg -// or just use your own file. - -// Read a file from fs, store it in Redis, get it back from Redis, write it back to fs. -fs.readFile(filename, function (err, data) { - if (err) throw err; - console.log('Read ' + data.length + ' bytes from filesystem.'); - - client.set(filename, data, redis.print); // set entire file - client.get(filename, function (err, reply) { // get entire file - if (err) { - console.log('Get error: ' + err); - } else { - assert.strictEqual(data.inspect(), reply.inspect()); - fs.writeFile('duplicate_' + filename, reply, function (err) { - if (err) { - console.log('Error on write: ' + err); - } else { - console.log('File written.'); - } - client.end(); - }); - } - }); -}); diff --git a/examples/mget.js b/examples/mget.js deleted file mode 100644 index e774ac44e0e..00000000000 --- a/examples/mget.js +++ /dev/null @@ -1,7 +0,0 @@ -'use strict'; - -var client = require('redis').createClient(); - -client.mget(['sessions started', 'sessions started', 'foo'], function (err, res) { - console.dir(res); -}); diff --git a/examples/monitor.js b/examples/monitor.js deleted file mode 100644 index 93d5d100f5a..00000000000 --- a/examples/monitor.js +++ /dev/null @@ -1,12 +0,0 @@ -'use strict'; - -var client = require('../index').createClient(); -var util = require('util'); - -client.monitor(function (err, res) { - console.log('Entering monitoring mode.'); -}); - -client.on('monitor', function (time, args) { - console.log(time + ': ' + util.inspect(args)); -}); diff --git a/examples/multi.js b/examples/multi.js deleted file mode 100644 index e0e3bf13fcb..00000000000 --- a/examples/multi.js +++ /dev/null @@ -1,49 +0,0 @@ -'use strict'; - -var redis = require('redis'); -var client = redis.createClient(); -var set_size = 20; - -client.sadd('bigset', 'a member'); -client.sadd('bigset', 'another member'); - -while (set_size > 0) { - client.sadd('bigset', 'member ' + set_size); - set_size -= 1; -} - -// multi chain with an individual callback -client.multi() - .scard('bigset') - .smembers('bigset') - .keys('*', function (err, replies) { - client.mget(replies, redis.print); - }) - .dbsize() - .exec(function (err, replies) { - console.log('MULTI got ' + replies.length + ' replies'); - replies.forEach(function (reply, index) { - console.log('Reply ' + index + ': ' + reply.toString()); - }); - }); - -client.mset('incr thing', 100, 'incr other thing', 1, redis.print); - -// start a separate multi command queue -var multi = client.multi(); -multi.incr('incr thing', redis.print); -multi.incr('incr other thing', redis.print); - -// runs immediately -client.get('incr thing', redis.print); // 100 - -// drains multi queue and runs atomically -multi.exec(function (err, replies) { - console.log(replies); // 101, 2 -}); - -// you can re-run the same transaction if you like -multi.exec(function (err, replies) { - console.log(replies); // 102, 3 - client.quit(); -}); diff --git a/examples/multi2.js b/examples/multi2.js deleted file mode 100644 index 3a0ceaec6a3..00000000000 --- a/examples/multi2.js +++ /dev/null @@ -1,31 +0,0 @@ -'use strict'; - -var redis = require('redis'); -var client = redis.createClient(); - -// start a separate command queue for multi -var multi = client.multi(); -multi.incr('incr thing', redis.print); -multi.incr('incr other thing', redis.print); - -// runs immediately -client.mset('incr thing', 100, 'incr other thing', 1, redis.print); - -// drains multi queue and runs atomically -multi.exec(function (err, replies) { - console.log(replies); // 101, 2 -}); - -// you can re-run the same transaction if you like -multi.exec(function (err, replies) { - console.log(replies); // 102, 3 - client.quit(); -}); - -client.multi([ - ['mget', 'multifoo', 'multibar', redis.print], - ['incr', 'multifoo'], - ['incr', 'multibar'] -]).exec(function (err, replies) { - console.log(replies.toString()); -}); diff --git a/examples/psubscribe.js b/examples/psubscribe.js deleted file mode 100644 index ecd24274047..00000000000 --- a/examples/psubscribe.js +++ /dev/null @@ -1,33 +0,0 @@ -'use strict'; - -var redis = require('redis'); -var client1 = redis.createClient(); -var client2 = redis.createClient(); -var client3 = redis.createClient(); -var client4 = redis.createClient(); -var msg_count = 0; - -client1.on('psubscribe', function (pattern, count) { - console.log('client1 psubscribed to ' + pattern + ', ' + count + ' total subscriptions'); - client2.publish('channeltwo', 'Me!'); - client3.publish('channelthree', 'Me too!'); - client4.publish('channelfour', 'And me too!'); -}); - -client1.on('punsubscribe', function (pattern, count) { - console.log('client1 punsubscribed from ' + pattern + ', ' + count + ' total subscriptions'); - client4.end(); - client3.end(); - client2.end(); - client1.end(); -}); - -client1.on('pmessage', function (pattern, channel, message) { - console.log('(' + pattern + ') client1 received message on ' + channel + ': ' + message); - msg_count += 1; - if (msg_count === 3) { - client1.punsubscribe(); - } -}); - -client1.psubscribe('channel*'); diff --git a/examples/pub_sub.js b/examples/pub_sub.js deleted file mode 100644 index d0970eb502f..00000000000 --- a/examples/pub_sub.js +++ /dev/null @@ -1,42 +0,0 @@ -'use strict'; - -var redis = require('redis'); -var client1 = redis.createClient(); -var msg_count = 0; -var client2 = redis.createClient(); - -// Most clients probably don't do much on 'subscribe'. This example uses it to coordinate things within one program. -client1.on('subscribe', function (channel, count) { - console.log('client1 subscribed to ' + channel + ', ' + count + ' total subscriptions'); - if (count === 2) { - client2.publish('a nice channel', 'I am sending a message.'); - client2.publish('another one', 'I am sending a second message.'); - client2.publish('a nice channel', 'I am sending my last message.'); - } -}); - -client1.on('unsubscribe', function (channel, count) { - console.log('client1 unsubscribed from ' + channel + ', ' + count + ' total subscriptions'); - if (count === 0) { - client2.end(); - client1.end(); - } -}); - -client1.on('message', function (channel, message) { - console.log('client1 channel ' + channel + ': ' + message); - msg_count += 1; - if (msg_count === 3) { - client1.unsubscribe(); - } -}); - -client1.on('ready', function () { - // if you need auth, do it here - client1.incr('did a thing'); - client1.subscribe('a nice channel', 'another one'); -}); - -client2.on('ready', function () { - // if you need auth, do it here -}); diff --git a/examples/scan.js b/examples/scan.js deleted file mode 100644 index e6b67ea6ae0..00000000000 --- a/examples/scan.js +++ /dev/null @@ -1,51 +0,0 @@ -'use strict'; - -var redis = require('redis'); -var client = redis.createClient(); - -var cursor = '0'; - -function scan () { - client.scan( - cursor, - 'MATCH', 'q:job:*', - 'COUNT', '10', - function (err, res) { - if (err) throw err; - - // Update the cursor position for the next scan - cursor = res[0]; - // get the SCAN result for this iteration - var keys = res[1]; - - // Remember: more or less than COUNT or no keys may be returned - // See http://redis.io/commands/scan#the-count-option - // Also, SCAN may return the same key multiple times - // See http://redis.io/commands/scan#scan-guarantees - // Additionally, you should always have the code that uses the keys - // before the code checking the cursor. - if (keys.length > 0) { - console.log('Array of matching keys', keys); - } - - // It's important to note that the cursor and returned keys - // vary independently. The scan is never complete until redis - // returns a non-zero cursor. However, with MATCH and large - // collections, most iterations will return an empty keys array. - - // Still, a cursor of zero DOES NOT mean that there are no keys. - // A zero cursor just means that the SCAN is complete, but there - // might be one last batch of results to process. - - // From : - // 'An iteration starts when the cursor is set to 0, - // and terminates when the cursor returned by the server is 0.' - if (cursor === '0') { - return console.log('Iteration complete'); - } - - return scan(); - } - ); -} -scan(); diff --git a/examples/simple.js b/examples/simple.js deleted file mode 100644 index 2fc2c3aefbd..00000000000 --- a/examples/simple.js +++ /dev/null @@ -1,26 +0,0 @@ -'use strict'; - -var redis = require('redis'); -var client = redis.createClient(); - -client.on('error', function (err) { - console.log('error event - ' + client.host + ':' + client.port + ' - ' + err); -}); - -client.set('string key', 'string val', redis.print); -client.hset('hash key', 'hashtest 1', 'some value', redis.print); -client.hset(['hash key', 'hashtest 2', 'some other value'], redis.print); -client.hkeys('hash key', function (err, replies) { - if (err) { - return console.error('error response - ' + err); - } - - console.log(replies.length + ' replies:'); - replies.forEach(function (reply, i) { - console.log(' ' + i + ': ' + reply); - }); -}); - -client.quit(function (err, res) { - console.log('Exiting from quit command.'); -}); diff --git a/examples/sort.js b/examples/sort.js deleted file mode 100644 index b09b06fbbf0..00000000000 --- a/examples/sort.js +++ /dev/null @@ -1,19 +0,0 @@ -'use strict'; - -var redis = require('redis'); -var client = redis.createClient(); - -client.sadd('mylist', 1); -client.sadd('mylist', 2); -client.sadd('mylist', 3); - -client.set('weight_1', 5); -client.set('weight_2', 500); -client.set('weight_3', 1); - -client.set('object_1', 'foo'); -client.set('object_2', 'bar'); -client.set('object_3', 'qux'); - -client.sort('mylist', 'by', 'weight_*', 'get', 'object_*', redis.print); -// Prints Reply: qux,foo,bar diff --git a/examples/streams.js b/examples/streams.js deleted file mode 100644 index 726e4adf920..00000000000 --- a/examples/streams.js +++ /dev/null @@ -1,47 +0,0 @@ -'use strict'; - -var redis = require('redis'); -var client1 = redis.createClient(); -var client2 = redis.createClient(); -var client3 = redis.createClient(); - -client1.xadd('mystream', '*', 'field1', 'm1', function (err) { - if (err) { - return console.error(err); - } - client1.xgroup('CREATE', 'mystream', 'mygroup', '$', function (err) { - if (err) { - return console.error(err); - } - }); - - client2.xreadgroup('GROUP', 'mygroup', 'consumer', 'Block', 1000, 'NOACK', - 'STREAMS', 'mystream', '>', function (err, stream) { - if (err) { - return console.error(err); - } - console.log('client2 ' + stream); - }); - - client3.xreadgroup('GROUP', 'mygroup', 'consumer', 'Block', 1000, 'NOACK', - 'STREAMS', 'mystream', '>', function (err, stream) { - if (err) { - return console.error(err); - } - console.log('client3 ' + stream); - }); - - - client1.xadd('mystream', '*', 'field1', 'm2', function (err) { - if (err) { - return console.error(err); - } - }); - - client1.xadd('mystream', '*', 'field1', 'm3', function (err) { - if (err) { - return console.error(err); - } - }); - -}); diff --git a/examples/subqueries.js b/examples/subqueries.js deleted file mode 100644 index 5677c129195..00000000000 --- a/examples/subqueries.js +++ /dev/null @@ -1,17 +0,0 @@ -'use strict'; - -// Sending commands in response to other commands. -// This example runs 'type' against every key in the database -// -var client = require('redis').createClient(); - -client.keys('*', function (err, keys) { - keys.forEach(function (key, pos) { - client.type(key, function (err, keytype) { - console.log(key + ' is ' + keytype); - if (pos === (keys.length - 1)) { - client.quit(); - } - }); - }); -}); diff --git a/examples/subquery.js b/examples/subquery.js deleted file mode 100644 index 355dd94abca..00000000000 --- a/examples/subquery.js +++ /dev/null @@ -1,17 +0,0 @@ -'use strict'; - -var client = require('redis').createClient(); - -// build a map of all keys and their types -client.keys('*', function (err, all_keys) { - var key_types = {}; - - all_keys.forEach(function (key, pos) { // use second arg of forEach to get pos - client.type(key, function (err, type) { - key_types[key] = type; - if (pos === all_keys.length - 1) { // callbacks all run in order - console.dir(key_types); - } - }); - }); -}); diff --git a/examples/unix_socket.js b/examples/unix_socket.js deleted file mode 100644 index b51aef2d11a..00000000000 --- a/examples/unix_socket.js +++ /dev/null @@ -1,32 +0,0 @@ -'use strict'; - -var redis = require('redis'); -var client = redis.createClient('/tmp/redis.sock'); -var profiler = require('v8-profiler'); - -client.on('connect', function () { - console.log('Got Unix socket connection.'); -}); - -client.on('error', function (err) { - console.log(err.message); -}); - -client.set('space chars', 'space value'); - -setInterval(function () { - client.get('space chars'); -}, 100); - -function done () { - client.info(function (err, reply) { - console.log(reply.toString()); - client.quit(); - }); -} - -setTimeout(function () { - console.log('Taking snapshot.'); - profiler.takeSnapshot(); - done(); -}, 5000); diff --git a/examples/web_server.js b/examples/web_server.js deleted file mode 100644 index ba5950d0384..00000000000 --- a/examples/web_server.js +++ /dev/null @@ -1,33 +0,0 @@ -'use strict'; - -// A simple web server that generates dyanmic content based on responses from Redis - -var http = require('http'); -var redis_client = require('redis').createClient(); - -http.createServer(function (request, response) { // The server - response.writeHead(200, { - 'Content-Type': 'text/plain' - }); - - var redis_info, total_requests; - - redis_client.info(function (err, reply) { - redis_info = reply; // stash response in outer scope - }); - redis_client.incr('requests', function (err, reply) { - total_requests = reply; // stash response in outer scope - }); - redis_client.hincrby('ip', request.connection.remoteAddress, 1); - redis_client.hgetall('ip', function (err, reply) { - // This is the last reply, so all of the previous replies must have completed already - response.write('This page was generated after talking to redis.\n\n' + - 'Redis info:\n' + redis_info + '\n' + - 'Total requests: ' + total_requests + '\n\n' + - 'IP count: \n'); - Object.keys(reply).forEach(function (ip) { - response.write(' ' + ip + ': ' + reply[ip] + '\n'); - }); - response.end(); - }); -}).listen(80); diff --git a/index.js b/index.js deleted file mode 100644 index fe79c5f3935..00000000000 --- a/index.js +++ /dev/null @@ -1,1039 +0,0 @@ -'use strict'; - -var net = require('net'); -var tls = require('tls'); -var util = require('util'); -var utils = require('./lib/utils'); -var Command = require('./lib/command'); -var Queue = require('denque'); -var errorClasses = require('./lib/customErrors'); -var EventEmitter = require('events'); -var Parser = require('redis-parser'); -var RedisErrors = require('redis-errors'); -var commands = require('redis-commands'); -var debug = require('./lib/debug'); -var unifyOptions = require('./lib/createClient'); -var SUBSCRIBE_COMMANDS = { - subscribe: true, - unsubscribe: true, - psubscribe: true, - punsubscribe: true -}; - -function noop () {} - -function handle_detect_buffers_reply (reply, command, buffer_args) { - if (buffer_args === false || this.message_buffers) { - // If detect_buffers option was specified, then the reply from the parser will be a buffer. - // If this command did not use Buffer arguments, then convert the reply to Strings here. - reply = utils.reply_to_strings(reply); - } - - if (command === 'hgetall') { - reply = utils.reply_to_object(reply); - } - return reply; -} - -exports.debug_mode = /\bredis\b/i.test(process.env.NODE_DEBUG); - -// Attention: The second parameter might be removed at will and is not officially supported. -// Do not rely on this -function RedisClient (options, stream) { - // Copy the options so they are not mutated - options = utils.clone(options); - EventEmitter.call(this); - var cnx_options = {}; - var self = this; - /* istanbul ignore next: travis does not work with stunnel atm. Therefore the tls tests are skipped on travis */ - for (var tls_option in options.tls) { - cnx_options[tls_option] = options.tls[tls_option]; - // Copy the tls options into the general options to make sure the address is set right - if (tls_option === 'port' || tls_option === 'host' || tls_option === 'path' || tls_option === 'family') { - options[tls_option] = options.tls[tls_option]; - } - } - if (stream) { - // The stream from the outside is used so no connection from this side is triggered but from the server this client should talk to - // Reconnect etc won't work with this. This requires monkey patching to work, so it is not officially supported - options.stream = stream; - this.address = '"Private stream"'; - } else if (options.path) { - cnx_options.path = options.path; - this.address = options.path; - } else { - cnx_options.port = +options.port || 6379; - cnx_options.host = options.host || '127.0.0.1'; - cnx_options.family = (!options.family && net.isIP(cnx_options.host)) || (options.family === 'IPv6' ? 6 : 4); - this.address = cnx_options.host + ':' + cnx_options.port; - } - - this.connection_options = cnx_options; - this.connection_id = RedisClient.connection_id++; - this.connected = false; - this.ready = false; - if (options.socket_keepalive === undefined) { - options.socket_keepalive = true; - } - if (options.socket_initial_delay === undefined) { - options.socket_initial_delay = 0; - // set default to 0, which is aligned to https://nodejs.org/api/net.html#net_socket_setkeepalive_enable_initialdelay - } - for (var command in options.rename_commands) { - options.rename_commands[command.toLowerCase()] = options.rename_commands[command]; - } - options.return_buffers = !!options.return_buffers; - options.detect_buffers = !!options.detect_buffers; - // Override the detect_buffers setting if return_buffers is active and print a warning - if (options.return_buffers && options.detect_buffers) { - self.warn('WARNING: You activated return_buffers and detect_buffers at the same time. The return value is always going to be a buffer.'); - options.detect_buffers = false; - } - if (options.detect_buffers) { - // We only need to look at the arguments if we do not know what we have to return - this.handle_reply = handle_detect_buffers_reply; - } - this.should_buffer = false; - this.command_queue = new Queue(); // Holds sent commands to de-pipeline them - this.offline_queue = new Queue(); // Holds commands issued but not able to be sent - this.pipeline_queue = new Queue(); // Holds all pipelined commands - // ATTENTION: connect_timeout should change in v.3.0 so it does not count towards ending reconnection attempts after x seconds - // This should be done by the retry_strategy. Instead it should only be the timeout for connecting to redis - this.connect_timeout = +options.connect_timeout || 3600000; // 60 * 60 * 1000 ms - this.enable_offline_queue = options.enable_offline_queue === false ? false : true; - this.initialize_retry_vars(); - this.pub_sub_mode = 0; - this.subscription_set = {}; - this.monitoring = false; - this.message_buffers = false; - this.closing = false; - this.server_info = {}; - this.auth_pass = options.auth_pass || options.password; - this.auth_user = options.auth_user || options.user; - this.selected_db = options.db; // Save the selected db here, used when reconnecting - this.fire_strings = true; // Determine if strings or buffers should be written to the stream - this.pipeline = false; - this.sub_commands_left = 0; - this.times_connected = 0; - this.buffers = options.return_buffers || options.detect_buffers; - this.options = options; - this.reply = 'ON'; // Returning replies is the default - this.create_stream(); - // The listeners will not be attached right away, so let's print the deprecation message while the listener is attached - this.on('newListener', function (event) { - if ((event === 'message_buffer' || event === 'pmessage_buffer' || event === 'messageBuffer' || event === 'pmessageBuffer') && !this.buffers && !this.message_buffers) { - this.reply_parser.optionReturnBuffers = true; - this.message_buffers = true; - this.handle_reply = handle_detect_buffers_reply; - } - }); -} -util.inherits(RedisClient, EventEmitter); - -RedisClient.connection_id = 0; - -function create_parser (self) { - return new Parser({ - returnReply: function (data) { - self.return_reply(data); - }, - returnError: function (err) { - // Return a ReplyError to indicate Redis returned an error - self.return_error(err); - }, - returnFatalError: function (err) { - // Error out all fired commands. Otherwise they might rely on faulty data. We have to reconnect to get in a working state again - // Note: the execution order is important. First flush and emit, then create the stream - err.message += '. Please report this.'; - self.ready = false; - self.flush_and_error({ - message: 'Fatal error encountered. Command aborted.', - code: 'NR_FATAL' - }, { - error: err, - queues: ['command_queue'] - }); - self.emit('error', err); - self.create_stream(); - }, - returnBuffers: self.buffers || self.message_buffers, - stringNumbers: self.options.string_numbers || false - }); -} - -/****************************************************************************** - - All functions in here are internal besides the RedisClient constructor - and the exported functions. Don't rely on them as they will be private - functions in node_redis v.3 - -******************************************************************************/ - -// Attention: the function name "create_stream" should not be changed, as other libraries need this to mock the stream (e.g. fakeredis) -RedisClient.prototype.create_stream = function () { - var self = this; - - // Init parser - this.reply_parser = create_parser(this); - - if (this.options.stream) { - // Only add the listeners once in case of a reconnect try (that won't work) - if (this.stream) { - return; - } - this.stream = this.options.stream; - } else { - // On a reconnect destroy the former stream and retry - if (this.stream) { - this.stream.removeAllListeners(); - this.stream.destroy(); - } - - /* istanbul ignore if: travis does not work with stunnel atm. Therefore the tls tests are skipped on travis */ - if (this.options.tls) { - this.stream = tls.connect(this.connection_options); - } else { - this.stream = net.createConnection(this.connection_options); - } - } - - if (this.options.connect_timeout) { - this.stream.setTimeout(this.connect_timeout, function () { - // Note: This is only tested if a internet connection is established - self.retry_totaltime = self.connect_timeout; - self.connection_gone('timeout'); - }); - } - - /* istanbul ignore next: travis does not work with stunnel atm. Therefore the tls tests are skipped on travis */ - var connect_event = this.options.tls ? 'secureConnect' : 'connect'; - this.stream.once(connect_event, function () { - this.removeAllListeners('timeout'); - self.times_connected++; - self.on_connect(); - }); - - this.stream.on('data', function (buffer_from_socket) { - // The buffer_from_socket.toString() has a significant impact on big chunks and therefore this should only be used if necessary - debug('Net read ' + self.address + ' id ' + self.connection_id); // + ': ' + buffer_from_socket.toString()); - self.reply_parser.execute(buffer_from_socket); - }); - - this.stream.on('error', function (err) { - self.on_error(err); - }); - - this.stream.once('close', function (hadError) { - self.connection_gone('close'); - }); - - this.stream.once('end', function () { - self.connection_gone('end'); - }); - - this.stream.on('drain', function () { - self.drain(); - }); - - this.stream.setNoDelay(); - - // Fire the command before redis is connected to be sure it's the first fired command - if (this.auth_pass !== undefined) { - this.ready = true; - // Fail silently as we might not be able to connect - this.auth(this.auth_pass, this.auth_user, function (err) { - if (err && err.code !== 'UNCERTAIN_STATE') { - self.emit('error', err); - } - }); - this.ready = false; - } -}; - -RedisClient.prototype.handle_reply = function (reply, command) { - if (command === 'hgetall') { - reply = utils.reply_to_object(reply); - } - return reply; -}; - -RedisClient.prototype.cork = noop; -RedisClient.prototype.uncork = noop; - -RedisClient.prototype.initialize_retry_vars = function () { - this.retry_timer = null; - this.retry_totaltime = 0; - this.retry_delay = 200; - this.retry_backoff = 1.7; - this.attempts = 1; -}; - -RedisClient.prototype.warn = function (msg) { - var self = this; - // Warn on the next tick. Otherwise no event listener can be added - // for warnings that are emitted in the redis client constructor - process.nextTick(function () { - if (self.listeners('warning').length !== 0) { - self.emit('warning', msg); - } else { - console.warn('node_redis:', msg); - } - }); -}; - -// Flush provided queues, erroring any items with a callback first -RedisClient.prototype.flush_and_error = function (error_attributes, options) { - options = options || {}; - var aggregated_errors = []; - var queue_names = options.queues || ['command_queue', 'offline_queue']; // Flush the command_queue first to keep the order intakt - for (var i = 0; i < queue_names.length; i++) { - // If the command was fired it might have been processed so far - if (queue_names[i] === 'command_queue') { - error_attributes.message += ' It might have been processed.'; - } else { // As the command_queue is flushed first, remove this for the offline queue - error_attributes.message = error_attributes.message.replace(' It might have been processed.', ''); - } - // Don't flush everything from the queue - for (var command_obj = this[queue_names[i]].shift(); command_obj; command_obj = this[queue_names[i]].shift()) { - var err = new errorClasses.AbortError(error_attributes); - if (command_obj.error) { - err.stack = err.stack + command_obj.error.stack.replace(/^Error.*?\n/, '\n'); - } - err.command = command_obj.command.toUpperCase(); - if (command_obj.args && command_obj.args.length) { - err.args = command_obj.args; - } - if (options.error) { - err.origin = options.error; - } - if (typeof command_obj.callback === 'function') { - command_obj.callback(err); - } else { - aggregated_errors.push(err); - } - } - } - // Currently this would be a breaking change, therefore it's only emitted in debug_mode - if (exports.debug_mode && aggregated_errors.length) { - var error; - if (aggregated_errors.length === 1) { - error = aggregated_errors[0]; - } else { - error_attributes.message = error_attributes.message.replace('It', 'They').replace(/command/i, '$&s'); - error = new errorClasses.AggregateError(error_attributes); - error.errors = aggregated_errors; - } - this.emit('error', error); - } -}; - -RedisClient.prototype.on_error = function (err) { - if (this.closing) { - return; - } - - err.message = 'Redis connection to ' + this.address + ' failed - ' + err.message; - debug(err.message); - this.connected = false; - this.ready = false; - - // Only emit the error if the retry_strategy option is not set - if (!this.options.retry_strategy) { - this.emit('error', err); - } - // 'error' events get turned into exceptions if they aren't listened for. If the user handled this error - // then we should try to reconnect. - this.connection_gone('error', err); -}; - -RedisClient.prototype.on_connect = function () { - debug('Stream connected ' + this.address + ' id ' + this.connection_id); - - this.connected = true; - this.ready = false; - this.emitted_end = false; - this.stream.setKeepAlive(this.options.socket_keepalive, this.options.socket_initial_delay); - this.stream.setTimeout(0); - - this.emit('connect'); - this.initialize_retry_vars(); - - if (this.options.no_ready_check) { - this.on_ready(); - } else { - this.ready_check(); - } -}; - -RedisClient.prototype.on_ready = function () { - var self = this; - - debug('on_ready called ' + this.address + ' id ' + this.connection_id); - this.ready = true; - - this.cork = function () { - self.pipeline = true; - if (self.stream.cork) { - self.stream.cork(); - } - }; - this.uncork = function () { - if (self.fire_strings) { - self.write_strings(); - } else { - self.write_buffers(); - } - self.pipeline = false; - self.fire_strings = true; - if (self.stream.uncork) { - // TODO: Consider using next tick here. See https://github.com/NodeRedis/node_redis/issues/1033 - self.stream.uncork(); - } - }; - - // Restore modal commands from previous connection. The order of the commands is important - if (this.selected_db !== undefined) { - this.internal_send_command(new Command('select', [this.selected_db])); - } - if (this.monitoring) { // Monitor has to be fired before pub sub commands - this.internal_send_command(new Command('monitor', [])); - } - var callback_count = Object.keys(this.subscription_set).length; - if (!this.options.disable_resubscribing && callback_count) { - // only emit 'ready' when all subscriptions were made again - // TODO: Remove the countdown for ready here. This is not coherent with all other modes and should therefore not be handled special - // We know we are ready as soon as all commands were fired - var callback = function () { - callback_count--; - if (callback_count === 0) { - self.emit('ready'); - } - }; - debug('Sending pub/sub on_ready commands'); - for (var key in this.subscription_set) { - var command = key.slice(0, key.indexOf('_')); - var args = this.subscription_set[key]; - this[command]([args], callback); - } - this.send_offline_queue(); - return; - } - this.send_offline_queue(); - this.emit('ready'); -}; - -RedisClient.prototype.on_info_cmd = function (err, res) { - if (err) { - if (err.message === "ERR unknown command 'info'") { - this.on_ready(); - return; - } - err.message = 'Ready check failed: ' + err.message; - this.emit('error', err); - return; - } - - /* istanbul ignore if: some servers might not respond with any info data. This is just a safety check that is difficult to test */ - if (!res) { - debug('The info command returned without any data.'); - this.on_ready(); - return; - } - - if (!this.server_info.loading || this.server_info.loading === '0') { - // If the master_link_status exists but the link is not up, try again after 50 ms - if (this.server_info.master_link_status && this.server_info.master_link_status !== 'up') { - this.server_info.loading_eta_seconds = 0.05; - } else { - // Eta loading should change - debug('Redis server ready.'); - this.on_ready(); - return; - } - } - - var retry_time = +this.server_info.loading_eta_seconds * 1000; - if (retry_time > 1000) { - retry_time = 1000; - } - debug('Redis server still loading, trying again in ' + retry_time); - setTimeout(function (self) { - self.ready_check(); - }, retry_time, this); -}; - -RedisClient.prototype.ready_check = function () { - var self = this; - debug('Checking server ready state...'); - // Always fire this info command as first command even if other commands are already queued up - this.ready = true; - this.info(function (err, res) { - self.on_info_cmd(err, res); - }); - this.ready = false; -}; - -RedisClient.prototype.send_offline_queue = function () { - for (var command_obj = this.offline_queue.shift(); command_obj; command_obj = this.offline_queue.shift()) { - debug('Sending offline command: ' + command_obj.command); - this.internal_send_command(command_obj); - } - this.drain(); -}; - -var retry_connection = function (self, error) { - debug('Retrying connection...'); - - var reconnect_params = { - delay: self.retry_delay, - attempt: self.attempts, - error: error - }; - if (self.options.camel_case) { - reconnect_params.totalRetryTime = self.retry_totaltime; - reconnect_params.timesConnected = self.times_connected; - } else { - reconnect_params.total_retry_time = self.retry_totaltime; - reconnect_params.times_connected = self.times_connected; - } - self.emit('reconnecting', reconnect_params); - - self.retry_totaltime += self.retry_delay; - self.attempts += 1; - self.retry_delay = Math.round(self.retry_delay * self.retry_backoff); - self.create_stream(); - self.retry_timer = null; -}; - -RedisClient.prototype.connection_gone = function (why, error) { - // If a retry is already in progress, just let that happen - if (this.retry_timer) { - return; - } - error = error || null; - - debug('Redis connection is gone from ' + why + ' event.'); - this.connected = false; - this.ready = false; - // Deactivate cork to work with the offline queue - this.cork = noop; - this.uncork = noop; - this.pipeline = false; - this.pub_sub_mode = 0; - - // since we are collapsing end and close, users don't expect to be called twice - if (!this.emitted_end) { - this.emit('end'); - this.emitted_end = true; - } - - // If this is a requested shutdown, then don't retry - if (this.closing) { - debug('Connection ended by quit / end command, not retrying.'); - this.flush_and_error({ - message: 'Stream connection ended and command aborted.', - code: 'NR_CLOSED' - }, { - error: error - }); - return; - } - - if (typeof this.options.retry_strategy === 'function') { - var retry_params = { - attempt: this.attempts, - error: error - }; - if (this.options.camel_case) { - retry_params.totalRetryTime = this.retry_totaltime; - retry_params.timesConnected = this.times_connected; - } else { - retry_params.total_retry_time = this.retry_totaltime; - retry_params.times_connected = this.times_connected; - } - this.retry_delay = this.options.retry_strategy(retry_params); - if (typeof this.retry_delay !== 'number') { - // Pass individual error through - if (this.retry_delay instanceof Error) { - error = this.retry_delay; - } - - var errorMessage = 'Redis connection in broken state: retry aborted.'; - - this.flush_and_error({ - message: errorMessage, - code: 'CONNECTION_BROKEN', - }, { - error: error - }); - var retryError = new Error(errorMessage); - retryError.code = 'CONNECTION_BROKEN'; - if (error) { - retryError.origin = error; - } - this.end(false); - this.emit('error', retryError); - return; - } - } - - if (this.retry_totaltime >= this.connect_timeout) { - var message = 'Redis connection in broken state: connection timeout exceeded.'; - this.flush_and_error({ - message: message, - code: 'CONNECTION_BROKEN', - }, { - error: error - }); - var err = new Error(message); - err.code = 'CONNECTION_BROKEN'; - if (error) { - err.origin = error; - } - this.end(false); - this.emit('error', err); - return; - } - - // Retry commands after a reconnect instead of throwing an error. Use this with caution - if (this.options.retry_unfulfilled_commands) { - this.offline_queue.unshift.apply(this.offline_queue, this.command_queue.toArray()); - this.command_queue.clear(); - } else if (this.command_queue.length !== 0) { - this.flush_and_error({ - message: 'Redis connection lost and command aborted.', - code: 'UNCERTAIN_STATE' - }, { - error: error, - queues: ['command_queue'] - }); - } - - if (this.retry_totaltime + this.retry_delay > this.connect_timeout) { - // Do not exceed the maximum - this.retry_delay = this.connect_timeout - this.retry_totaltime; - } - - debug('Retry connection in ' + this.retry_delay + ' ms'); - this.retry_timer = setTimeout(retry_connection, this.retry_delay, this, error); -}; - -RedisClient.prototype.return_error = function (err) { - var command_obj = this.command_queue.shift(); - if (command_obj.error) { - err.stack = command_obj.error.stack.replace(/^Error.*?\n/, 'ReplyError: ' + err.message + '\n'); - } - err.command = command_obj.command.toUpperCase(); - if (command_obj.args && command_obj.args.length) { - err.args = command_obj.args; - } - - // Count down pub sub mode if in entering modus - if (this.pub_sub_mode > 1) { - this.pub_sub_mode--; - } - - var match = err.message.match(utils.err_code); - // LUA script could return user errors that don't behave like all other errors! - if (match) { - err.code = match[1]; - } - - utils.callback_or_emit(this, command_obj.callback, err); -}; - -RedisClient.prototype.drain = function () { - this.should_buffer = false; -}; - -function normal_reply (self, reply) { - var command_obj = self.command_queue.shift(); - if (typeof command_obj.callback === 'function') { - if (command_obj.command !== 'exec') { - reply = self.handle_reply(reply, command_obj.command, command_obj.buffer_args); - } - command_obj.callback(null, reply); - } else { - debug('No callback for reply'); - } -} - -function subscribe_unsubscribe (self, reply, type) { - // Subscribe commands take an optional callback and also emit an event, but only the _last_ response is included in the callback - // The pub sub commands return each argument in a separate return value and have to be handled that way - var command_obj = self.command_queue.get(0); - var buffer = self.options.return_buffers || self.options.detect_buffers && command_obj.buffer_args; - var channel = (buffer || reply[1] === null) ? reply[1] : reply[1].toString(); - var count = +reply[2]; // Return the channel counter as number no matter if `string_numbers` is activated or not - debug(type, channel); - - // Emit first, then return the callback - if (channel !== null) { // Do not emit or "unsubscribe" something if there was no channel to unsubscribe from - self.emit(type, channel, count); - if (type === 'subscribe' || type === 'psubscribe') { - self.subscription_set[type + '_' + channel] = channel; - } else { - type = type === 'unsubscribe' ? 'subscribe' : 'psubscribe'; // Make types consistent - delete self.subscription_set[type + '_' + channel]; - } - } - - if (command_obj.args.length === 1 || self.sub_commands_left === 1 || command_obj.args.length === 0 && (count === 0 || channel === null)) { - if (count === 0) { // unsubscribed from all channels - var running_command; - var i = 1; - self.pub_sub_mode = 0; // Deactivating pub sub mode - // This should be a rare case and therefore handling it this way should be good performance wise for the general case - while (running_command = self.command_queue.get(i)) { - if (SUBSCRIBE_COMMANDS[running_command.command]) { - self.pub_sub_mode = i; // Entering pub sub mode again - break; - } - i++; - } - } - self.command_queue.shift(); - if (typeof command_obj.callback === 'function') { - // TODO: The current return value is pretty useless. - // Evaluate to change this in v.4 to return all subscribed / unsubscribed channels in an array including the number of channels subscribed too - command_obj.callback(null, channel); - } - self.sub_commands_left = 0; - } else { - if (self.sub_commands_left !== 0) { - self.sub_commands_left--; - } else { - self.sub_commands_left = command_obj.args.length ? command_obj.args.length - 1 : count; - } - } -} - -function return_pub_sub (self, reply) { - var type = reply[0].toString(); - if (type === 'message') { // channel, message - if (!self.options.return_buffers || self.message_buffers) { // backwards compatible. Refactor this in v.4 to always return a string on the normal emitter - self.emit('message', reply[1].toString(), reply[2].toString()); - self.emit('message_buffer', reply[1], reply[2]); - self.emit('messageBuffer', reply[1], reply[2]); - } else { - self.emit('message', reply[1], reply[2]); - } - } else if (type === 'pmessage') { // pattern, channel, message - if (!self.options.return_buffers || self.message_buffers) { // backwards compatible. Refactor this in v.4 to always return a string on the normal emitter - self.emit('pmessage', reply[1].toString(), reply[2].toString(), reply[3].toString()); - self.emit('pmessage_buffer', reply[1], reply[2], reply[3]); - self.emit('pmessageBuffer', reply[1], reply[2], reply[3]); - } else { - self.emit('pmessage', reply[1], reply[2], reply[3]); - } - } else { - subscribe_unsubscribe(self, reply, type); - } -} - -RedisClient.prototype.return_reply = function (reply) { - if (this.monitoring) { - var replyStr; - if (this.buffers && Buffer.isBuffer(reply)) { - replyStr = reply.toString(); - } else { - replyStr = reply; - } - // If in monitor mode, all normal commands are still working and we only want to emit the streamlined commands - if (typeof replyStr === 'string' && utils.monitor_regex.test(replyStr)) { - var timestamp = replyStr.slice(0, replyStr.indexOf(' ')); - var args = replyStr.slice(replyStr.indexOf('"') + 1, -1).split('" "').map(function (elem) { - return elem.replace(/\\"/g, '"'); - }); - this.emit('monitor', timestamp, args, replyStr); - return; - } - } - if (this.pub_sub_mode === 0) { - normal_reply(this, reply); - } else if (this.pub_sub_mode !== 1) { - this.pub_sub_mode--; - normal_reply(this, reply); - } else if (!(reply instanceof Array) || reply.length <= 2) { - // Only PING and QUIT are allowed in this context besides the pub sub commands - // Ping replies with ['pong', null|value] and quit with 'OK' - normal_reply(this, reply); - } else { - return_pub_sub(this, reply); - } -}; - -function handle_offline_command (self, command_obj) { - var command = command_obj.command; - var err, msg; - if (self.closing || !self.enable_offline_queue) { - command = command.toUpperCase(); - if (!self.closing) { - if (self.stream.writable) { - msg = 'The connection is not yet established and the offline queue is deactivated.'; - } else { - msg = 'Stream not writeable.'; - } - } else { - msg = 'The connection is already closed.'; - } - err = new errorClasses.AbortError({ - message: command + " can't be processed. " + msg, - code: 'NR_CLOSED', - command: command - }); - if (command_obj.args.length) { - err.args = command_obj.args; - } - utils.reply_in_order(self, command_obj.callback, err); - } else { - debug('Queueing ' + command + ' for next server connection.'); - self.offline_queue.push(command_obj); - } - self.should_buffer = true; -} - -// Do not call internal_send_command directly, if you are not absolutly certain it handles everything properly -// e.g. monitor / info does not work with internal_send_command only -RedisClient.prototype.internal_send_command = function (command_obj) { - var arg, prefix_keys; - var i = 0; - var command_str = ''; - var args = command_obj.args; - var command = command_obj.command; - var len = args.length; - var big_data = false; - var args_copy = new Array(len); - - if (process.domain && command_obj.callback) { - command_obj.callback = process.domain.bind(command_obj.callback); - } - - if (this.ready === false || this.stream.writable === false) { - // Handle offline commands right away - handle_offline_command(this, command_obj); - return false; // Indicate buffering - } - - for (i = 0; i < len; i += 1) { - if (typeof args[i] === 'string') { - // 30000 seemed to be a good value to switch to buffers after testing and checking the pros and cons - if (args[i].length > 30000) { - big_data = true; - args_copy[i] = Buffer.from(args[i], 'utf8'); - } else { - args_copy[i] = args[i]; - } - } else if (typeof args[i] === 'object') { // Checking for object instead of Buffer.isBuffer helps us finding data types that we can't handle properly - if (args[i] instanceof Date) { // Accept dates as valid input - args_copy[i] = args[i].toString(); - } else if (Buffer.isBuffer(args[i])) { - args_copy[i] = args[i]; - command_obj.buffer_args = true; - big_data = true; - } else { - var invalidArgError = new Error( - 'node_redis: The ' + command.toUpperCase() + ' command contains a invalid argument type.\n' + - 'Only strings, dates and buffers are accepted. Please update your code to use valid argument types.' - ); - invalidArgError.command = command_obj.command.toUpperCase(); - if (command_obj.args && command_obj.args.length) { - invalidArgError.args = command_obj.args; - } - if (command_obj.callback) { - command_obj.callback(invalidArgError); - return false; - } - throw invalidArgError; - } - } else if (typeof args[i] === 'undefined') { - var undefinedArgError = new Error( - 'node_redis: The ' + command.toUpperCase() + ' command contains a invalid argument type of "undefined".\n' + - 'Only strings, dates and buffers are accepted. Please update your code to use valid argument types.' - ); - undefinedArgError.command = command_obj.command.toUpperCase(); - if (command_obj.args && command_obj.args.length) { - undefinedArgError.args = command_obj.args; - } - // there is always a callback in this scenario - command_obj.callback(undefinedArgError); - return false; - } else { - // Seems like numbers are converted fast using string concatenation - args_copy[i] = '' + args[i]; - } - } - - if (this.options.prefix) { - prefix_keys = commands.getKeyIndexes(command, args_copy); - for (i = prefix_keys.pop(); i !== undefined; i = prefix_keys.pop()) { - args_copy[i] = this.options.prefix + args_copy[i]; - } - } - if (this.options.rename_commands && this.options.rename_commands[command]) { - command = this.options.rename_commands[command]; - } - // Always use 'Multi bulk commands', but if passed any Buffer args, then do multiple writes, one for each arg. - // This means that using Buffers in commands is going to be slower, so use Strings if you don't already have a Buffer. - command_str = '*' + (len + 1) + '\r\n$' + command.length + '\r\n' + command + '\r\n'; - - if (big_data === false) { // Build up a string and send entire command in one write - for (i = 0; i < len; i += 1) { - arg = args_copy[i]; - command_str += '$' + Buffer.byteLength(arg) + '\r\n' + arg + '\r\n'; - } - debug('Send ' + this.address + ' id ' + this.connection_id + ': ' + command_str); - this.write(command_str); - } else { - debug('Send command (' + command_str + ') has Buffer arguments'); - this.fire_strings = false; - this.write(command_str); - - for (i = 0; i < len; i += 1) { - arg = args_copy[i]; - if (typeof arg === 'string') { - this.write('$' + Buffer.byteLength(arg) + '\r\n' + arg + '\r\n'); - } else { // buffer - this.write('$' + arg.length + '\r\n'); - this.write(arg); - this.write('\r\n'); - } - debug('send_command: buffer send ' + arg.length + ' bytes'); - } - } - if (command_obj.call_on_write) { - command_obj.call_on_write(); - } - // Handle `CLIENT REPLY ON|OFF|SKIP` - // This has to be checked after call_on_write - /* istanbul ignore else: TODO: Remove this as soon as we test Redis 3.2 on travis */ - if (this.reply === 'ON') { - this.command_queue.push(command_obj); - } else { - // Do not expect a reply - // Does this work in combination with the pub sub mode? - if (command_obj.callback) { - utils.reply_in_order(this, command_obj.callback, null, undefined, this.command_queue); - } - if (this.reply === 'SKIP') { - this.reply = 'SKIP_ONE_MORE'; - } else if (this.reply === 'SKIP_ONE_MORE') { - this.reply = 'ON'; - } - } - return !this.should_buffer; -}; - -RedisClient.prototype.write_strings = function () { - var str = ''; - for (var command = this.pipeline_queue.shift(); command; command = this.pipeline_queue.shift()) { - // Write to stream if the string is bigger than 4mb. The biggest string may be Math.pow(2, 28) - 15 chars long - if (str.length + command.length > 4 * 1024 * 1024) { - this.should_buffer = !this.stream.write(str); - str = ''; - } - str += command; - } - if (str !== '') { - this.should_buffer = !this.stream.write(str); - } -}; - -RedisClient.prototype.write_buffers = function () { - for (var command = this.pipeline_queue.shift(); command; command = this.pipeline_queue.shift()) { - this.should_buffer = !this.stream.write(command); - } -}; - -RedisClient.prototype.write = function (data) { - if (this.pipeline === false) { - this.should_buffer = !this.stream.write(data); - return; - } - this.pipeline_queue.push(data); -}; - -Object.defineProperty(exports, 'debugMode', { - get: function () { - return this.debug_mode; - }, - set: function (val) { - this.debug_mode = val; - } -}); - -// Don't officially expose the command_queue directly but only the length as read only variable -Object.defineProperty(RedisClient.prototype, 'command_queue_length', { - get: function () { - return this.command_queue.length; - } -}); - -Object.defineProperty(RedisClient.prototype, 'offline_queue_length', { - get: function () { - return this.offline_queue.length; - } -}); - -// Add support for camelCase by adding read only properties to the client -// All known exposed snake_case variables are added here -Object.defineProperty(RedisClient.prototype, 'retryDelay', { - get: function () { - return this.retry_delay; - } -}); - -Object.defineProperty(RedisClient.prototype, 'retryBackoff', { - get: function () { - return this.retry_backoff; - } -}); - -Object.defineProperty(RedisClient.prototype, 'commandQueueLength', { - get: function () { - return this.command_queue.length; - } -}); - -Object.defineProperty(RedisClient.prototype, 'offlineQueueLength', { - get: function () { - return this.offline_queue.length; - } -}); - -Object.defineProperty(RedisClient.prototype, 'shouldBuffer', { - get: function () { - return this.should_buffer; - } -}); - -Object.defineProperty(RedisClient.prototype, 'connectionId', { - get: function () { - return this.connection_id; - } -}); - -Object.defineProperty(RedisClient.prototype, 'serverInfo', { - get: function () { - return this.server_info; - } -}); - -exports.createClient = function () { - return new RedisClient(unifyOptions.apply(null, arguments)); -}; -exports.RedisClient = RedisClient; -exports.print = utils.print; -exports.Multi = require('./lib/multi'); -exports.AbortError = errorClasses.AbortError; -exports.RedisError = RedisErrors.RedisError; -exports.ParserError = RedisErrors.ParserError; -exports.ReplyError = RedisErrors.ReplyError; -exports.AggregateError = errorClasses.AggregateError; - -// Add all redis commands / node_redis api to the client -require('./lib/individualCommands'); -require('./lib/extendedApi'); - -//enables adding new commands (for modules and new commands) -exports.addCommand = exports.add_command = require('./lib/commands'); diff --git a/index.ts b/index.ts new file mode 100644 index 00000000000..408cbe3b996 --- /dev/null +++ b/index.ts @@ -0,0 +1,10 @@ +import RedisClient from './lib/client'; +import RedisCluster from './lib/cluster'; + +export const createClient = RedisClient.create; + +export const commandOptions = RedisClient.commandOptions; + +export const createCluster = RedisCluster.create; + +export { defineScript } from './lib/lua-script'; diff --git a/lib/client.spec.ts b/lib/client.spec.ts new file mode 100644 index 00000000000..f73049d2286 --- /dev/null +++ b/lib/client.spec.ts @@ -0,0 +1,562 @@ +import { strict as assert, AssertionError } from 'assert'; +import { once } from 'events'; +import { itWithClient, TEST_REDIS_SERVERS, TestRedisServers, waitTillBeenCalled, isRedisVersionGreaterThan } from './test-utils'; +import RedisClient from './client'; +import { AbortError, ClientClosedError, ConnectionTimeoutError, WatchError } from './errors'; +import { defineScript } from './lua-script'; +import { spy } from 'sinon'; + +export const SQUARE_SCRIPT = defineScript({ + NUMBER_OF_KEYS: 0, + SCRIPT: 'return ARGV[1] * ARGV[1];', + transformArguments(number: number): Array { + return [number.toString()]; + }, + transformReply(reply: number): number { + return reply; + } +}); + +describe('Client', () => { + describe('authentication', () => { + itWithClient(TestRedisServers.PASSWORD, 'Client should be authenticated', async client => { + assert.equal( + await client.ping(), + 'PONG' + ); + }); + + it('should not retry connecting if failed due to wrong auth', async () => { + const client = RedisClient.create({ + socket: { + ...TEST_REDIS_SERVERS[TestRedisServers.PASSWORD], + password: 'wrongpassword' + } + }); + + await assert.rejects( + client.connect(), + { + message: isRedisVersionGreaterThan([6]) ? + 'WRONGPASS invalid username-password pair or user is disabled.' : + 'ERR invalid password' + } + ); + + assert.equal(client.isOpen, false); + }); + }); + + describe('legacyMode', () => { + const client = RedisClient.create({ + socket: TEST_REDIS_SERVERS[TestRedisServers.OPEN], + scripts: { + square: SQUARE_SCRIPT + }, + legacyMode: true + }); + + before(() => client.connect()); + afterEach(() => client.v4.flushAll()); + after(() => client.disconnect()); + + it('client.sendCommand should call the callback', done => { + (client as any).sendCommand('PING', (err?: Error, reply?: string) => { + if (err) { + return done(err); + } + + try { + assert.equal(reply, 'PONG'); + done(); + } catch (err) { + done(err); + } + }); + }); + + it('client.sendCommand should work without callback', async () => { + (client as any).sendCommand('PING'); + await client.v4.ping(); // make sure the first command was replied + }); + + it('client.v4.sendCommand should return a promise', async () => { + assert.equal( + await client.v4.sendCommand(['PING']), + 'PONG' + ); + }); + + it('client.{command} should accept vardict arguments', done => { + (client as any).set('a', 'b', (err?: Error, reply?: string) => { + if (err) { + return done(err); + } + + try { + assert.equal(reply, 'OK'); + done(); + } catch (err) { + done(err); + } + }); + }); + + it('client.{command} should accept arguments array', done => { + (client as any).set(['a', 'b'], (err?: Error, reply?: string) => { + if (err) { + return done(err); + } + + try { + assert.equal(reply, 'OK'); + done(); + } catch (err) { + done(err); + } + }); + }); + + it('client.{command} should accept mix of strings and array of strings', done => { + (client as any).set(['a'], 'b', ['XX'], (err?: Error, reply?: string) => { + if (err) { + return done(err); + } + + try { + assert.equal(reply, null); + done(); + } catch (err) { + done(err); + } + }); + }); + + it('client.multi.ping.exec should call the callback', done => { + (client as any).multi() + .ping() + .exec((err?: Error, reply?: string) => { + if (err) { + return done(err); + } + + try { + assert.deepEqual(reply, ['PONG']); + done(); + } catch (err) { + done(err); + } + }); + }); + + it('client.multi.ping.exec should work without callback', async () => { + (client as any).multi() + .ping() + .exec(); + await client.v4.ping(); // make sure the first command was replied + }); + + it('client.multi.ping.v4.ping.v4.exec should return a promise', async () => { + assert.deepEqual( + await ((client as any).multi() + .ping() + .v4.ping() + .v4.exec()), + ['PONG', 'PONG'] + ); + }); + + it('client.{script} should return a promise', async () => { + assert.equal(await client.square(2), 4); + }); + }); + + describe('events', () => { + it('connect, ready, end', async () => { + const client = RedisClient.create({ + socket: TEST_REDIS_SERVERS[TestRedisServers.OPEN] + }); + + await Promise.all([ + client.connect(), + once(client, 'connect'), + once(client, 'ready') + ]); + + await Promise.all([ + client.disconnect(), + once(client, 'end') + ]); + }); + }); + + describe('sendCommand', () => { + itWithClient(TestRedisServers.OPEN, 'PING', async client => { + assert.equal(await client.sendCommand(['PING']), 'PONG'); + }); + + describe('AbortController', () => { + before(function () { + if (!global.AbortController) { + this.skip(); + } + }); + + itWithClient(TestRedisServers.OPEN, 'success', async client => { + await client.sendCommand(['PING'], { + signal: new AbortController().signal + }); + }); + + itWithClient(TestRedisServers.OPEN, 'AbortError', client => { + const controller = new AbortController(); + controller.abort(); + + return assert.rejects( + client.sendCommand(['PING'], { + signal: controller.signal + }), + AbortError + ); + }); + }); + }); + + describe('multi', () => { + itWithClient(TestRedisServers.OPEN, 'simple', async client => { + assert.deepEqual( + await client.multi() + .ping() + .set('key', 'value') + .get('key') + .exec(), + ['PONG', 'OK', 'value'] + ); + }); + + itWithClient(TestRedisServers.OPEN, 'should reject the whole chain on error', client => { + client.on('error', () => { + // ignore errors + }); + + return assert.rejects( + client.multi() + .ping() + .addCommand(['DEBUG', 'RESTART']) + .ping() + .exec() + ); + }); + + it('with script', async () => { + const client = RedisClient.create({ + scripts: { + square: SQUARE_SCRIPT + } + }); + + await client.connect(); + + try { + assert.deepEqual( + await client.multi() + .square(2) + .exec(), + [4] + ); + } finally { + await client.disconnect(); + } + }); + + itWithClient(TestRedisServers.OPEN, 'WatchError', async client => { + await client.watch('key'); + + await client.set( + RedisClient.commandOptions({ + isolated: true + }), + 'key', + '1' + ); + + await assert.rejects( + client.multi() + .decr('key') + .exec(), + WatchError + ); + }); + }); + + it('scripts', async () => { + const client = RedisClient.create({ + scripts: { + square: SQUARE_SCRIPT + } + }); + + await client.connect(); + + try { + assert.equal( + await client.square(2), + 4 + ); + } finally { + await client.disconnect(); + } + }); + + it('modules', async () => { + const client = RedisClient.create({ + modules: { + module: { + echo: { + transformArguments(message: string): Array { + return ['ECHO', message]; + }, + transformReply(reply: string): string { + return reply; + } + } + } + } + }); + + await client.connect(); + + try { + assert.equal( + await client.module.echo('message'), + 'message' + ); + } finally { + await client.disconnect(); + } + }); + + itWithClient(TestRedisServers.OPEN, 'executeIsolated', async client => { + await client.sendCommand(['CLIENT', 'SETNAME', 'client']); + + assert.equal( + await client.executeIsolated(isolatedClient => + isolatedClient.sendCommand(['CLIENT', 'GETNAME']) + ), + null + ); + }); + + itWithClient(TestRedisServers.OPEN, 'should reconnect after DEBUG RESTART', async client => { + client.on('error', () => { + // ignore errors + }); + + await client.sendCommand(['CLIENT', 'SETNAME', 'client']); + await assert.rejects(client.sendCommand(['DEBUG', 'RESTART'])); + assert.ok(await client.sendCommand(['CLIENT', 'GETNAME']) === null); + }); + + itWithClient(TestRedisServers.OPEN, 'should SELECT db after reconnection', async client => { + client.on('error', () => { + // ignore errors + }); + + await client.select(1); + await assert.rejects(client.sendCommand(['DEBUG', 'RESTART'])); + assert.equal( + (await client.clientInfo()).db, + 1 + ); + }, { + // because of CLIENT INFO + minimumRedisVersion: [6, 2] + }); + + itWithClient(TestRedisServers.OPEN, 'scanIterator', async client => { + const promises = [], + keys = new Set(); + for (let i = 0; i < 100; i++) { + const key = i.toString(); + keys.add(key); + promises.push(client.set(key, '')); + } + + await Promise.all(promises); + + const results = new Set(); + for await (const key of client.scanIterator()) { + results.add(key); + } + + assert.deepEqual(keys, results); + }); + + itWithClient(TestRedisServers.OPEN, 'hScanIterator', async client => { + const hash: Record = {}; + for (let i = 0; i < 100; i++) { + hash[i.toString()] = i.toString(); + } + + await client.hSet('key', hash); + + const results: Record = {}; + for await (const { field, value } of client.hScanIterator('key')) { + results[field] = value; + } + + assert.deepEqual(hash, results); + }); + + itWithClient(TestRedisServers.OPEN, 'sScanIterator', async client => { + const members = new Set(); + for (let i = 0; i < 100; i++) { + members.add(i.toString()); + } + + await client.sAdd('key', Array.from(members)); + + const results = new Set(); + for await (const key of client.sScanIterator('key')) { + results.add(key); + } + + assert.deepEqual(members, results); + }); + + itWithClient(TestRedisServers.OPEN, 'zScanIterator', async client => { + const members = []; + for (let i = 0; i < 100; i++) { + members.push({ + score: 1, + value: i.toString() + }); + } + + await client.zAdd('key', members); + + const map = new Map(); + for await (const member of client.zScanIterator('key')) { + map.set(member.value, member.score); + } + + type MemberTuple = [string, number]; + + function sort(a: MemberTuple, b: MemberTuple) { + return Number(b[0]) - Number(a[0]); + } + + assert.deepEqual( + [...map.entries()].sort(sort), + members.map(member => [member.value, member.score]).sort(sort) + ); + }); + + itWithClient(TestRedisServers.OPEN, 'PubSub', async publisher => { + const subscriber = publisher.duplicate(); + + await subscriber.connect(); + + try { + const channelListener1 = spy(), + channelListener2 = spy(), + patternListener = spy(); + + await Promise.all([ + subscriber.subscribe('channel', channelListener1), + subscriber.subscribe('channel', channelListener2), + subscriber.pSubscribe('channel*', patternListener) + ]); + + await Promise.all([ + waitTillBeenCalled(channelListener1), + waitTillBeenCalled(channelListener2), + waitTillBeenCalled(patternListener), + publisher.publish('channel', 'message') + ]); + + assert.ok(channelListener1.calledOnceWithExactly('message', 'channel')); + assert.ok(channelListener2.calledOnceWithExactly('message', 'channel')); + assert.ok(patternListener.calledOnceWithExactly('message', 'channel')); + + await subscriber.unsubscribe('channel', channelListener1); + 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); + } finally { + await subscriber.disconnect(); + } + }); + + it('ConnectionTimeoutError', async () => { + const client = RedisClient.create({ + socket: { + ...TEST_REDIS_SERVERS[TestRedisServers.OPEN], + connectTimeout: 1 + } + }); + + try { + const promise = assert.rejects(client.connect(), ConnectionTimeoutError), + start = process.hrtime.bigint(); + + // block the event loop for 1ms, to make sure the connection will timeout + while (process.hrtime.bigint() - start < 1_000_000) {} + + await promise; + } catch (err) { + if (err instanceof AssertionError) { + await client.disconnect(); + } + + throw err; + } + }); + + it('client.quit', async () => { + const client = RedisClient.create({ + socket: TEST_REDIS_SERVERS[TestRedisServers.OPEN] + }); + + await client.connect(); + + try { + const quitPromise = client.quit(); + assert.equal(client.isOpen, false); + await Promise.all([ + quitPromise, + assert.rejects(client.ping(), ClientClosedError) + ]); + } finally { + if (client.isOpen) { + await client.disconnect(); + } + } + }); +}); diff --git a/lib/client.ts b/lib/client.ts new file mode 100644 index 00000000000..a8da7f5ddd5 --- /dev/null +++ b/lib/client.ts @@ -0,0 +1,468 @@ +import RedisSocket, { RedisSocketOptions } from './socket'; +import RedisCommandsQueue, { PubSubListener, PubSubSubscribeCommands, PubSubUnsubscribeCommands, QueueCommandOptions } from './commands-queue'; +import COMMANDS from './commands'; +import { RedisCommand, RedisModules, RedisReply } from './commands'; +import RedisMultiCommand, { MultiQueuedCommand, RedisMultiCommandType } from './multi-command'; +import EventEmitter from 'events'; +import { CommandOptions, commandOptions, isCommandOptions } from './command-options'; +import { RedisLuaScript, RedisLuaScripts } from './lua-script'; +import { ScanOptions, ZMember } from './commands/generic-transformers'; +import { ScanCommandOptions } from './commands/SCAN'; +import { HScanTuple } from './commands/HSCAN'; +import { encodeCommand, extendWithDefaultCommands, extendWithModulesAndScripts, transformCommandArguments } from './commander'; +import { Pool, Options as PoolOptions, createPool } from 'generic-pool'; +import { ClientClosedError } from './errors'; + +export interface RedisClientOptions { + socket?: RedisSocketOptions; + modules?: M; + scripts?: S; + commandsQueueMaxLength?: number; + readonly?: boolean; + legacyMode?: boolean; + isolationPoolOptions?: PoolOptions; +} + +export type RedisCommandSignature = + (...args: Parameters | [options: CommandOptions, ...rest: Parameters]) => Promise>; + +type WithCommands = { + [P in keyof typeof COMMANDS]: RedisCommandSignature<(typeof COMMANDS)[P]>; +}; + +type WithModules = { + [P in keyof M]: { + [C in keyof M[P]]: RedisCommandSignature; + }; +}; + +type WithScripts = { + [P in keyof S]: RedisCommandSignature; +}; + +export type WithPlugins = + WithCommands & WithModules & WithScripts; + +export type RedisClientType = + WithPlugins & RedisClient; + +export interface ClientCommandOptions extends QueueCommandOptions { + isolated?: boolean; +} + +export default class RedisClient extends EventEmitter { + static commandOptions(options: ClientCommandOptions): CommandOptions { + return commandOptions(options); + } + + static async commandsExecutor( + this: RedisClient, + command: RedisCommand, + args: Array + ): Promise> { + const { args: redisArgs, options } = transformCommandArguments(command, args); + + const reply = command.transformReply( + await this.#sendCommand(redisArgs, options), + redisArgs.preserve + ); + + return reply; + } + + static async #scriptsExecutor( + this: RedisClient, + script: RedisLuaScript, + args: Array + ): Promise { + const { args: redisArgs, options } = transformCommandArguments(script, args); + + const reply = script.transformReply( + await this.executeScript(script, redisArgs, options), + redisArgs.preserve + ); + + return reply; + } + + static create(options?: RedisClientOptions): RedisClientType { + const Client = (extendWithModulesAndScripts({ + BaseClass: RedisClient, + modules: options?.modules, + modulesCommandsExecutor: RedisClient.commandsExecutor, + scripts: options?.scripts, + scriptsExecutor: RedisClient.#scriptsExecutor + })); + + if (Client !== RedisClient) { + Client.prototype.Multi = RedisMultiCommand.extend(options); + } + + return new Client(options); + } + + readonly #options?: RedisClientOptions; + readonly #socket: RedisSocket; + readonly #queue: RedisCommandsQueue; + readonly #isolationPool: Pool>; + readonly #v4: Record = {}; + #selectedDB = 0; + + get options(): RedisClientOptions | null | undefined { + return this.#options; + } + + get isOpen(): boolean { + return this.#socket.isOpen; + } + + get v4(): Record { + if (!this.#options?.legacyMode) { + throw new Error('the client is not in "legacy mode"'); + } + + return this.#v4; + } + + constructor(options?: RedisClientOptions) { + super(); + this.#options = options; + this.#socket = this.#initiateSocket(); + this.#queue = this.#initiateQueue(); + this.#isolationPool = createPool({ + create: async () => { + const duplicate = this.duplicate(); + await duplicate.connect(); + return duplicate; + }, + destroy: client => client.disconnect() + }, options?.isolationPoolOptions); + this.#legacyMode(); + } + + #initiateSocket(): RedisSocket { + const socketInitiator = async (): Promise => { + const v4Commands = this.#options?.legacyMode ? this.#v4 : this, + promises = []; + + if (this.#selectedDB !== 0) { + promises.push(v4Commands.select(RedisClient.commandOptions({ asap: true }), this.#selectedDB)); + } + + if (this.#options?.readonly) { + promises.push(v4Commands.readonly(RedisClient.commandOptions({ asap: true }))); + } + + if (this.#options?.socket?.username || this.#options?.socket?.password) { + promises.push(v4Commands.auth(RedisClient.commandOptions({ asap: true }), this.#options.socket)); + } + + const resubscribePromise = this.#queue.resubscribe(); + if (resubscribePromise) { + promises.push(resubscribePromise); + this.#tick(); + } + + await Promise.all(promises); + }; + + return new RedisSocket(socketInitiator, this.#options?.socket) + .on('data', data => this.#queue.parseResponse(data)) + .on('error', err => { + this.emit('error', err); + this.#queue.flushWaitingForReply(err); + }) + .on('connect', () => this.emit('connect')) + .on('ready', () => { + this.emit('ready'); + this.#tick(); + }) + .on('reconnecting', () => this.emit('reconnecting')) + .on('end', () => this.emit('end')); + } + + #initiateQueue(): RedisCommandsQueue { + return new RedisCommandsQueue( + this.#options?.commandsQueueMaxLength, + (encodedCommands: string) => this.#socket.write(encodedCommands) + ); + } + + #legacyMode(): void { + if (!this.#options?.legacyMode) return; + + (this as any).#v4.sendCommand = this.#sendCommand.bind(this); + (this as any).sendCommand = (...args: Array): void => { + const callback = typeof args[args.length - 1] === 'function' ? args[args.length - 1] as Function : undefined, + actualArgs = !callback ? args : args.slice(0, -1); + this.#sendCommand(actualArgs.flat() as Array) + .then((reply: unknown) => { + if (!callback) return; + + // https://github.com/NodeRedis/node-redis#commands:~:text=minimal%20parsing + + callback(null, reply); + }) + .catch((err: Error) => { + if (!callback) { + this.emit('error', err); + return; + } + + callback(err); + }); + }; + + for (const name of Object.keys(COMMANDS)) { + this.#defineLegacyCommand(name); + } + + // hard coded commands + this.#defineLegacyCommand('SELECT'); + this.#defineLegacyCommand('select'); + this.#defineLegacyCommand('SUBSCRIBE'); + this.#defineLegacyCommand('subscribe'); + this.#defineLegacyCommand('PSUBSCRIBE'); + this.#defineLegacyCommand('pSubscribe'); + this.#defineLegacyCommand('UNSUBSCRIBE'); + this.#defineLegacyCommand('unsubscribe'); + this.#defineLegacyCommand('PUNSUBSCRIBE'); + this.#defineLegacyCommand('pUnsubscribe'); + this.#defineLegacyCommand('QUIT'); + this.#defineLegacyCommand('quit'); + } + + #defineLegacyCommand(name: string): void { + (this as any).#v4[name] = (this as any)[name].bind(this); + (this as any)[name] = (...args: Array): void => { + (this as any).sendCommand(name, ...args); + }; + } + + duplicate(): RedisClientType { + return new (Object.getPrototypeOf(this).constructor)(this.#options); + } + + async connect(): Promise { + await this.#socket.connect(); + } + + async SELECT(db: number): Promise; + async SELECT(options: CommandOptions, db: number): Promise; + async SELECT(options?: any, db?: any): Promise { + if (!isCommandOptions(options)) { + db = options; + options = null; + } + + await this.#sendCommand(['SELECT', db.toString()], options); + this.#selectedDB = db; + } + + select = this.SELECT; + + SUBSCRIBE(channels: string | Array, listener: PubSubListener): Promise { + return this.#subscribe(PubSubSubscribeCommands.SUBSCRIBE, channels, listener); + } + + subscribe = this.SUBSCRIBE; + + PSUBSCRIBE(patterns: string | Array, listener: PubSubListener): Promise { + return this.#subscribe(PubSubSubscribeCommands.PSUBSCRIBE, patterns, listener); + } + + pSubscribe = this.PSUBSCRIBE; + + #subscribe(command: PubSubSubscribeCommands, channels: string | Array, listener: PubSubListener): Promise { + const promise = this.#queue.subscribe(command, channels, listener); + this.#tick(); + return promise; + } + + UNSUBSCRIBE(channels?: string | Array, listener?: PubSubListener): Promise { + return this.#unsubscribe(PubSubUnsubscribeCommands.UNSUBSCRIBE, channels, listener); + } + + unsubscribe = this.UNSUBSCRIBE; + + PUNSUBSCRIBE(patterns?: string | Array, listener?: PubSubListener): Promise { + return this.#unsubscribe(PubSubUnsubscribeCommands.PUNSUBSCRIBE, patterns, listener); + } + + pUnsubscribe = this.PUNSUBSCRIBE; + + #unsubscribe(command: PubSubUnsubscribeCommands, channels?: string | Array, listener?: PubSubListener): Promise { + const promise = this.#queue.unsubscribe(command, channels, listener); + this.#tick(); + return promise; + } + + QUIT(): Promise { + return this.#socket.quit(async () => { + this.#queue.addEncodedCommand(encodeCommand(['QUIT'])); + this.#tick(); + }); + } + + quit = this.QUIT; + + sendCommand(args: Array, options?: ClientCommandOptions): Promise { + return this.#sendCommand(args, options); + } + + // using `#sendCommand` cause `sendCommand` is overwritten in legacy mode + #sendCommand(args: Array, options?: ClientCommandOptions): Promise { + return this.sendEncodedCommand(encodeCommand(args), options); + } + + async sendEncodedCommand(encodedCommand: string, options?: ClientCommandOptions): Promise { + if (!this.#socket.isOpen) { + throw new ClientClosedError(); + } + + if (options?.isolated) { + return this.executeIsolated(isolatedClient => + isolatedClient.sendEncodedCommand(encodedCommand, { + ...options, + isolated: false + }) + ); + } + + const promise = this.#queue.addEncodedCommand(encodedCommand, options); + this.#tick(); + return await promise; + } + + executeIsolated(fn: (client: RedisClientType) => T | Promise): Promise { + return this.#isolationPool.use(fn); + } + + async executeScript(script: RedisLuaScript, args: Array, options?: ClientCommandOptions): Promise> { + try { + return await this.#sendCommand([ + 'EVALSHA', + script.SHA1, + script.NUMBER_OF_KEYS.toString(), + ...args + ], options); + } catch (err: any) { + if (!err?.message?.startsWith?.('NOSCRIPT')) { + throw err; + } + + return await this.#sendCommand([ + 'EVAL', + script.SCRIPT, + script.NUMBER_OF_KEYS.toString(), + ...args + ], options); + } + } + + #multiExecutor(commands: Array, chainId?: symbol): Promise> { + const promise = Promise.all( + commands.map(({encodedCommand}) => { + return this.#queue.addEncodedCommand(encodedCommand, RedisClient.commandOptions({ + chainId + })); + }) + ); + + this.#tick(); + + return promise; + } + + multi(): RedisMultiCommandType { + return new (this as any).Multi( + this.#multiExecutor.bind(this), + this.#options + ); + } + + async* scanIterator(options?: ScanCommandOptions): AsyncIterable { + let cursor = 0; + do { + const reply = await (this as any).scan(cursor, options); + cursor = reply.cursor; + for (const key of reply.keys) { + yield key; + } + } while (cursor !== 0) + } + + async* hScanIterator(key: string, options?: ScanOptions): AsyncIterable { + let cursor = 0; + do { + const reply = await (this as any).hScan(key, cursor, options); + cursor = reply.cursor; + for (const tuple of reply.tuples) { + yield tuple; + } + } while (cursor !== 0) + } + + async* sScanIterator(key: string, options?: ScanOptions): AsyncIterable { + let cursor = 0; + do { + const reply = await (this as any).sScan(key, cursor, options); + cursor = reply.cursor; + for (const member of reply.members) { + yield member; + } + } while (cursor !== 0) + } + + async* zScanIterator(key: string, options?: ScanOptions): AsyncIterable { + let cursor = 0; + do { + const reply = await (this as any).zScan(key, cursor, options); + cursor = reply.cursor; + for (const member of reply.members) { + yield member; + } + } while (cursor !== 0) + } + + async disconnect(): Promise { + this.#queue.flushAll(new Error('Disconnecting')); + await Promise.all([ + this.#socket.disconnect(), + this.#destroyIsolationPool() + ]); + } + + async #destroyIsolationPool(): Promise { + await this.#isolationPool.drain(); + await this.#isolationPool.clear(); + } + + #isTickQueued = false; + + #tick(): void { + const {chunkRecommendedSize} = this.#socket; + if (!chunkRecommendedSize) { + return; + } + + if (!this.#isTickQueued && this.#queue.waitingToBeSentCommandsLength < chunkRecommendedSize) { + queueMicrotask(() => this.#tick()); + this.#isTickQueued = true; + return; + } + + const isBuffering = this.#queue.executeChunk(chunkRecommendedSize); + if (isBuffering === true) { + this.#socket.once('drain', () => this.#tick()); + } else if (isBuffering === false) { + this.#tick(); + return; + } + + this.#isTickQueued = false; + } +} + +extendWithDefaultCommands(RedisClient, RedisClient.commandsExecutor); +(RedisClient.prototype as any).Multi = RedisMultiCommand.extend(); diff --git a/lib/cluster-slots.ts b/lib/cluster-slots.ts new file mode 100644 index 00000000000..3e255fc2a66 --- /dev/null +++ b/lib/cluster-slots.ts @@ -0,0 +1,221 @@ +import calculateSlot from 'cluster-key-slot'; +import RedisClient, { RedisClientType } from './client'; +import { RedisSocketOptions } from './socket'; +import { RedisClusterMasterNode, RedisClusterReplicaNode } from './commands/CLUSTER_NODES'; +import { RedisClusterOptions } from './cluster'; +import { RedisModules } from './commands'; +import { RedisLuaScripts } from './lua-script'; + +export interface ClusterNode { + id: string; + client: RedisClientType; +} + +interface SlotNodes { + master: ClusterNode; + replicas: Array>; + clientIterator: IterableIterator> | undefined; +} + +export default class RedisClusterSlots { + readonly #options: RedisClusterOptions; + readonly #nodeByUrl = new Map>(); + readonly #slots: Array> = []; + + constructor(options: RedisClusterOptions) { + this.#options = options; + } + + async connect(): Promise { + for (const rootNode of this.#options.rootNodes) { + try { + await this.#discoverNodes(rootNode); + return; + } catch (err) { + console.error(err); + // this.emit('error', err); + } + } + + throw new Error('None of the root nodes is available'); + } + + async discover(startWith: RedisClientType): Promise { + try { + await this.#discoverNodes(startWith.options?.socket); + return; + } catch (err) { + console.error(err); + // this.emit('error', err); + } + + for (const { client } of this.#nodeByUrl.values()) { + if (client === startWith) continue; + + try { + await this.#discoverNodes(client.options?.socket); + return; + } catch (err) { + console.error(err); + // this.emit('error', err); + } + } + + throw new Error('None of the cluster nodes is available'); + } + + async #discoverNodes(socketOptions?: RedisSocketOptions): Promise { + const client = RedisClient.create({ + socket: socketOptions + }); + + await client.connect(); + + try { + await this.#reset(await client.clusterNodes()); + } finally { + await client.disconnect(); // TODO: catch error from disconnect? + } + } + + async #reset(masters: Array): Promise { + // Override this.#slots and add not existing clients to this.#clientByKey + const promises: Array> = [], + clientsInUse = new Set(); + for (const master of masters) { + const slot = { + master: this.#initiateClientForNode(master, false, clientsInUse, promises), + replicas: this.#options.useReplicas ? + master.replicas.map(replica => this.#initiateClientForNode(replica, true, clientsInUse, promises)) : + [], + clientIterator: undefined // will be initiated in use + }; + + for (const { from, to } of master.slots) { + for (let i = from; i < to; i++) { + this.#slots[i] = slot; + } + } + } + + // Remove unused clients from this.#clientBykey using clientsInUse + for (const [url, { client }] of this.#nodeByUrl.entries()) { + if (clientsInUse.has(url)) continue; + + // TODO: ignore error from `.disconnect`? + promises.push(client.disconnect()); + this.#nodeByUrl.delete(url); + } + + await Promise.all(promises); + } + + #initiateClientForNode(nodeData: RedisClusterMasterNode | RedisClusterReplicaNode, readonly: boolean, clientsInUse: Set, promises: Array>): ClusterNode { + const url = `${nodeData.host}:${nodeData.port}`; + clientsInUse.add(url); + + let node = this.#nodeByUrl.get(url); + if (!node) { + node = { + id: nodeData.id, + client: RedisClient.create({ + socket: { + host: nodeData.host, + port: nodeData.port + }, + readonly + }) + }; + promises.push(node.client.connect()); + this.#nodeByUrl.set(url, node); + } + + return node; + } + + getSlotMaster(slot: number): ClusterNode { + return this.#slots[slot].master; + } + + *#slotClientIterator(slotNumber: number): IterableIterator> { + const slot = this.#slots[slotNumber]; + yield slot.master.client; + + for (const replica of slot.replicas) { + yield replica.client; + } + } + + #getSlotClient(slotNumber: number): RedisClientType { + const slot = this.#slots[slotNumber]; + if (!slot.clientIterator) { + slot.clientIterator = this.#slotClientIterator(slotNumber); + } + + const {done, value} = slot.clientIterator.next(); + if (done) { + slot.clientIterator = undefined; + return this.#getSlotClient(slotNumber); + } + + return value; + } + + #randomClientIterator?: IterableIterator>; + + #getRandomClient(): RedisClientType { + if (!this.#nodeByUrl.size) { + throw new Error('Cluster is not connected'); + } + + if (!this.#randomClientIterator) { + this.#randomClientIterator = this.#nodeByUrl.values(); + } + + const {done, value} = this.#randomClientIterator.next(); + if (done) { + this.#randomClientIterator = undefined; + return this.#getRandomClient(); + } + + return value.client; + } + + getClient(firstKey?: string, isReadonly?: boolean): RedisClientType { + if (!firstKey) { + return this.#getRandomClient(); + } + + const slot = calculateSlot(firstKey); + if (!isReadonly || !this.#options.useReplicas) { + return this.getSlotMaster(slot).client; + } + + return this.#getSlotClient(slot); + } + + getMasters(): Array> { + const masters = []; + + for (const node of this.#nodeByUrl.values()) { + if (node.client.options?.readonly) continue; + + masters.push(node); + } + + return masters; + } + + getNodeByUrl(url: string): ClusterNode | undefined { + return this.#nodeByUrl.get(url); + } + + async disconnect(): Promise { + await Promise.all( + [...this.#nodeByUrl.values()].map(({ client }) => client.disconnect()) + ); + + this.#nodeByUrl.clear(); + this.#slots.splice(0); + } +} diff --git a/lib/cluster.spec.ts b/lib/cluster.spec.ts new file mode 100644 index 00000000000..b7dbe50c908 --- /dev/null +++ b/lib/cluster.spec.ts @@ -0,0 +1,115 @@ +import { strict as assert } from 'assert'; +import RedisCluster from './cluster'; +import { defineScript } from './lua-script'; +import { itWithCluster, itWithDedicatedCluster, TestRedisClusters, TEST_REDIS_CLUSTERES } from './test-utils'; +import calculateSlot from 'cluster-key-slot'; +import { ClusterSlotStates } from './commands/CLUSTER_SETSLOT'; + +describe('Cluster', () => { + it('sendCommand', async () => { + const cluster = RedisCluster.create({ + rootNodes: TEST_REDIS_CLUSTERES[TestRedisClusters.OPEN], + useReplicas: true + }); + + await cluster.connect(); + + try { + await cluster.ping(); + await cluster.set('a', 'b'); + await cluster.set('a{a}', 'bb'); + await cluster.set('aa', 'bb'); + await cluster.get('aa'); + await cluster.get('aa'); + await cluster.get('aa'); + await cluster.get('aa'); + } finally { + await cluster.disconnect(); + } + }); + + itWithCluster(TestRedisClusters.OPEN, 'multi', async cluster => { + const key = 'key'; + assert.deepEqual( + await cluster.multi(key) + .ping() + .set(key, 'value') + .get(key) + .exec(), + ['PONG', 'OK', 'value'] + ); + }); + + it('scripts', async () => { + const cluster = RedisCluster.create({ + rootNodes: TEST_REDIS_CLUSTERES[TestRedisClusters.OPEN], + scripts: { + add: defineScript({ + NUMBER_OF_KEYS: 0, + SCRIPT: 'return ARGV[1] + 1;', + transformArguments(number: number): Array { + assert.equal(number, 1); + return [number.toString()]; + }, + transformReply(reply: number): number { + assert.equal(reply, 2); + return reply; + } + }) + } + }); + + await cluster.connect(); + + try { + assert.equal( + await cluster.add(1), + 2 + ); + } finally { + await cluster.disconnect(); + } + }); + + itWithDedicatedCluster('should handle live resharding', async cluster => { + const key = 'key', + value = 'value'; + await cluster.set(key, value); + + const slot = calculateSlot(key), + from = cluster.getSlotMaster(slot), + to = cluster.getMasters().find(node => node.id !== from.id); + + await to!.client.clusterSetSlot(slot, ClusterSlotStates.IMPORTING, from.id); + + // should be able to get the key from the original node before it was migrated + assert.equal( + await cluster.get(key), + value + ); + + await from.client.clusterSetSlot(slot, ClusterSlotStates.MIGRATING, to!.id); + + // should be able to get the key from the original node using the "ASKING" command + assert.equal( + await cluster.get(key), + value + ); + + const { port: toPort } = to!.client.options!.socket; + + await from.client.migrate( + '127.0.0.1', + toPort, + key, + 0, + 10 + ); + + // should be able to get the key from the new node + assert.equal( + await cluster.get(key), + value + ); + }); +}); diff --git a/lib/cluster.ts b/lib/cluster.ts new file mode 100644 index 00000000000..6da6dc55f46 --- /dev/null +++ b/lib/cluster.ts @@ -0,0 +1,202 @@ +import { RedisCommand, RedisModules } from './commands'; +import RedisClient, { ClientCommandOptions, RedisClientType, WithPlugins } from './client'; +import { RedisSocketOptions } from './socket'; +import RedisClusterSlots, { ClusterNode } from './cluster-slots'; +import { RedisLuaScript, RedisLuaScripts } from './lua-script'; +import { extendWithModulesAndScripts, extendWithDefaultCommands, transformCommandArguments } from './commander'; +import RedisMultiCommand, { MultiQueuedCommand, RedisMultiCommandType } from './multi-command'; + +export interface RedisClusterOptions { + rootNodes: Array; + modules?: M; + scripts?: S; + useReplicas?: boolean; + maxCommandRedirections?: number; +} + +export type RedisClusterType = + WithPlugins & RedisCluster; + +export default class RedisCluster { + static #extractFirstKey(command: RedisCommand, originalArgs: Array, redisArgs: Array): string | undefined { + if (command.FIRST_KEY_INDEX === undefined) { + return undefined; + } else if (typeof command.FIRST_KEY_INDEX === 'number') { + return redisArgs[command.FIRST_KEY_INDEX]; + } + + return command.FIRST_KEY_INDEX(...originalArgs); + } + + static async commandsExecutor( + this: RedisCluster, + command: RedisCommand, + args: Array + ): Promise> { + const { args: redisArgs, options } = transformCommandArguments(command, args); + + const reply = command.transformReply( + await this.sendCommand( + RedisCluster.#extractFirstKey(command, args, redisArgs), + command.IS_READ_ONLY, + redisArgs, + options + ), + redisArgs.preserve + ); + + return reply; + } + + static async #scriptsExecutor( + this: RedisCluster, + script: RedisLuaScript, + args: Array + ): Promise { + const { args: redisArgs, options } = transformCommandArguments(script, args); + + const reply = script.transformReply( + await this.executeScript( + script, + args, + redisArgs, + options + ), + redisArgs.preserve + ); + + return reply; + } + + static create(options?: RedisClusterOptions): RedisClusterType { + return new (extendWithModulesAndScripts({ + BaseClass: RedisCluster, + modules: options?.modules, + modulesCommandsExecutor: RedisCluster.commandsExecutor, + scripts: options?.scripts, + scriptsExecutor: RedisCluster.#scriptsExecutor + }))(options); + } + + readonly #options: RedisClusterOptions; + readonly #slots: RedisClusterSlots; + readonly #Multi: new (...args: ConstructorParameters) => RedisMultiCommandType; + + constructor(options: RedisClusterOptions) { + this.#options = options; + this.#slots = new RedisClusterSlots(options); + this.#Multi = RedisMultiCommand.extend(options); + } + + async connect(): Promise { + return this.#slots.connect(); + } + + async sendCommand( + firstKey: string | undefined, + isReadonly: boolean | undefined, + args: Array, + options?: ClientCommandOptions, + redirections = 0 + ): Promise> { + const client = this.#slots.getClient(firstKey, isReadonly); + + try { + return await client.sendCommand(args, options); + } catch (err: any) { + const shouldRetry = await this.#handleCommandError(err, client, redirections); + if (shouldRetry === true) { + return this.sendCommand(firstKey, isReadonly, args, options, redirections + 1); + } else if (shouldRetry) { + return shouldRetry.sendCommand(args, options); + } + + throw err; + } + } + + async executeScript( + script: RedisLuaScript, + originalArgs: Array, + redisArgs: Array, + options?: ClientCommandOptions, + redirections = 0 + ): Promise> { + const client = this.#slots.getClient( + RedisCluster.#extractFirstKey(script, originalArgs, redisArgs), + script.IS_READ_ONLY + ); + + try { + return await client.executeScript(script, redisArgs, options); + } catch (err: any) { + const shouldRetry = await this.#handleCommandError(err, client, redirections); + if (shouldRetry === true) { + return this.executeScript(script, originalArgs, redisArgs, options, redirections + 1); + } else if (shouldRetry) { + return shouldRetry.executeScript(script, redisArgs, options); + } + + throw err; + } + } + + async #handleCommandError(err: Error, client: RedisClientType, redirections: number): Promise> { + if (redirections > (this.#options.maxCommandRedirections ?? 16)) { + throw err; + } + + if (err.message.startsWith('ASK')) { + const url = err.message.substring(err.message.lastIndexOf(' ') + 1); + let node = this.#slots.getNodeByUrl(url); + if (!node) { + await this.#slots.discover(client); + node = this.#slots.getNodeByUrl(url); + + if (!node) { + throw new Error(`Cannot find node ${url}`); + } + } + + await node.client.asking(); + return node.client; + } else if (err.message.startsWith('MOVED')) { + await this.#slots.discover(client); + return true; + } + + throw err; + } + + multi(routing: string): RedisMultiCommandType { + return new this.#Multi( + async (commands: Array, chainId?: symbol) => { + const client = this.#slots.getClient(routing); + + return Promise.all( + commands.map(({encodedCommand}) => { + return client.sendEncodedCommand(encodedCommand, RedisClient.commandOptions({ + chainId + })); + }) + ); + }, + this.#options + ); + } + + getMasters(): Array> { + return this.#slots.getMasters(); + } + + getSlotMaster(slot: number): ClusterNode { + return this.#slots.getSlotMaster(slot); + } + + disconnect(): Promise { + return this.#slots.disconnect(); + } +} + +extendWithDefaultCommands(RedisCluster, RedisCluster.commandsExecutor); + diff --git a/lib/command-options.ts b/lib/command-options.ts new file mode 100644 index 00000000000..2096258046f --- /dev/null +++ b/lib/command-options.ts @@ -0,0 +1,14 @@ +const symbol = Symbol('Command Options'); + +export type CommandOptions = T & { + readonly [symbol]: true; +}; + +export function commandOptions(options: T): CommandOptions { + (options as any)[symbol] = true; + return options as CommandOptions; +} + +export function isCommandOptions(options: any): options is CommandOptions { + return options && options[symbol] === true; +} diff --git a/lib/command.js b/lib/command.js deleted file mode 100644 index 717115c82eb..00000000000 --- a/lib/command.js +++ /dev/null @@ -1,16 +0,0 @@ -'use strict'; - -var betterStackTraces = /development/i.test(process.env.NODE_ENV) || /\bredis\b/i.test(process.env.NODE_DEBUG); - -function Command (command, args, callback, call_on_write) { - this.command = command; - this.args = args; - this.buffer_args = false; - this.callback = callback; - this.call_on_write = call_on_write; - if (betterStackTraces) { - this.error = new Error(); - } -} - -module.exports = Command; diff --git a/lib/commander.spec.ts b/lib/commander.spec.ts new file mode 100644 index 00000000000..a38330abada --- /dev/null +++ b/lib/commander.spec.ts @@ -0,0 +1,28 @@ +import { strict as assert } from 'assert'; +import { describe } from 'mocha'; +import { encodeCommand } from './commander'; + +describe('Commander', () => { + describe('encodeCommand (see #1628)', () => { + it('1 byte', () => { + assert.equal( + encodeCommand(['a', 'z']), + '*2\r\n$1\r\na\r\n$1\r\nz\r\n' + ); + }); + + it('2 bytes', () => { + assert.equal( + encodeCommand(['א', 'ת']), + '*2\r\n$2\r\nא\r\n$2\r\nת\r\n' + ); + }); + + it('4 bytes', () => { + assert.equal( + encodeCommand(['🐣', '🐤']), + '*2\r\n$4\r\n🐣\r\n$4\r\n🐤\r\n' + ); + }); + }); +}); diff --git a/lib/commander.ts b/lib/commander.ts new file mode 100644 index 00000000000..51adc417ba9 --- /dev/null +++ b/lib/commander.ts @@ -0,0 +1,109 @@ + +import COMMANDS, { RedisCommand, RedisModules, TransformArgumentsReply } from './commands'; +import { RedisLuaScript, RedisLuaScripts } from './lua-script'; +import { CommandOptions, isCommandOptions } from './command-options'; + +type Instantiable = new(...args: Array) => T; + +type CommandExecutor = (this: InstanceType, command: RedisCommand, args: Array) => unknown; + +export function extendWithDefaultCommands(BaseClass: T, executor: CommandExecutor): void { + for (const [name, command] of Object.entries(COMMANDS)) { + BaseClass.prototype[name] = function (...args: Array): unknown { + return executor.call(this, command, args); + }; + } +} + +interface ExtendWithModulesAndScriptsConfig< + T extends Instantiable, + M extends RedisModules, + S extends RedisLuaScripts +> { + BaseClass: T; + modules: M | undefined; + modulesCommandsExecutor: CommandExecutor; + scripts: S | undefined; + scriptsExecutor(this: InstanceType, script: RedisLuaScript, args: Array): unknown; +} + +export function extendWithModulesAndScripts< + T extends Instantiable, + M extends RedisModules, + S extends RedisLuaScripts, +>(config: ExtendWithModulesAndScriptsConfig): T { + let Commander: T | undefined; + + if (config.modules) { + Commander = class extends config.BaseClass { + constructor(...args: Array) { + super(...args); + + for (const module of Object.keys(config.modules as RedisModules)) { + this[module] = new this[module](this); + } + } + }; + + for (const [moduleName, module] of Object.entries(config.modules)) { + Commander.prototype[moduleName] = class { + readonly self: T; + + constructor(self: InstanceType) { + this.self = self; + } + }; + + for (const [commandName, command] of Object.entries(module)) { + Commander.prototype[moduleName].prototype[commandName] = function (...args: Array): unknown { + return config.modulesCommandsExecutor.call(this.self, command, args); + }; + } + } + } + + if (config.scripts) { + Commander ??= class extends config.BaseClass {}; + + for (const [name, script] of Object.entries(config.scripts)) { + Commander.prototype[name] = function (...args: Array): unknown { + return config.scriptsExecutor.call(this, script, args); + }; + } + } + + return (Commander ?? config.BaseClass) as any; +} + +export function transformCommandArguments( + command: RedisCommand, + args: Array +): { + args: TransformArgumentsReply; + options: CommandOptions | undefined; +} { + let options; + if (isCommandOptions(args[0])) { + options = args[0]; + args = args.slice(1); + } + + return { + args: command.transformArguments(...args), + options + }; +} + +export function encodeCommand(args: Array): string { + const encoded = [ + `*${args.length}`, + `$${Buffer.byteLength(args[0])}`, + args[0] + ]; + + for (let i = 1; i < args.length; i++) { + encoded.push(`$${Buffer.byteLength(args[i])}`, args[i]); + } + + return encoded.join('\r\n') + '\r\n'; +} diff --git a/lib/commands-queue.ts b/lib/commands-queue.ts new file mode 100644 index 00000000000..1890e0a00a9 --- /dev/null +++ b/lib/commands-queue.ts @@ -0,0 +1,333 @@ +import LinkedList from 'yallist'; +import RedisParser from 'redis-parser'; +import { AbortError } from './errors'; +import { RedisReply } from './commands'; +import { encodeCommand } from './commander'; + +export interface QueueCommandOptions { + asap?: boolean; + signal?: any; // TODO: `AbortSignal` type is incorrect + chainId?: symbol; +} + +interface CommandWaitingToBeSent extends CommandWaitingForReply { + encodedCommand: string; + chainId?: symbol; + abort?: { + signal: any; // TODO: `AbortSignal` type is incorrect + listener(): void; + }; +} + +interface CommandWaitingForReply { + resolve(reply?: any): void; + reject(err: Error): void; + channelsCounter?: number; +} + +export type CommandsQueueExecutor = (encodedCommands: string) => boolean | undefined; + +export enum PubSubSubscribeCommands { + SUBSCRIBE = 'SUBSCRIBE', + PSUBSCRIBE = 'PSUBSCRIBE' +} + +export enum PubSubUnsubscribeCommands { + UNSUBSCRIBE = 'UNSUBSCRIBE', + PUNSUBSCRIBE = 'PUNSUBSCRIBE' +} + +export type PubSubListener = (message: string, channel: string) => unknown; + +export type PubSubListenersMap = Map>; + +export default class RedisCommandsQueue { + static #flushQueue(queue: LinkedList, err: Error): void { + while (queue.length) { + queue.shift()!.reject(err); + } + } + + static #emitPubSubMessage(listeners: Set, message: string, channel: string): void { + for (const listener of listeners) { + listener(message, channel); + } + } + + readonly #maxLength: number | null | undefined; + + readonly #executor: CommandsQueueExecutor; + + readonly #waitingToBeSent = new LinkedList(); + + #waitingToBeSentCommandsLength = 0; + + get waitingToBeSentCommandsLength() { + return this.#waitingToBeSentCommandsLength; + } + + readonly #waitingForReply = new LinkedList(); + + readonly #pubSubState = { + subscribing: 0, + subscribed: 0, + unsubscribing: 0 + }; + + readonly #pubSubListeners = { + channels: new Map(), + patterns: new Map() + }; + + 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; + } + } + + this.#shiftWaitingForReply().resolve(reply); + }, + returnError: (err: Error) => this.#shiftWaitingForReply().reject(err) + }); + + #chainInExecution: symbol | undefined; + + constructor(maxLength: number | null | undefined, executor: CommandsQueueExecutor) { + this.#maxLength = maxLength; + this.#executor = executor; + } + + addEncodedCommand(encodedCommand: string, options?: QueueCommandOptions): Promise { + if (this.#pubSubState.subscribing || this.#pubSubState.subscribed) { + 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({ + encodedCommand, + chainId: options?.chainId, + resolve, + reject + }); + + if (options?.signal) { + const listener = () => { + this.#waitingToBeSent.removeNode(node); + node.value.reject(new AbortError()); + }; + + node.value.abort = { + signal: options.signal, + listener + }; + options.signal.addEventListener('abort', listener, { + once: true + }); + } + + if (options?.asap) { + this.#waitingToBeSent.unshiftNode(node); + } else { + this.#waitingToBeSent.pushNode(node); + } + + this.#waitingToBeSentCommandsLength += encodedCommand.length; + }); + } + + subscribe(command: PubSubSubscribeCommands, channels: string | Array, listener: PubSubListener): Promise { + const channelsToSubscribe: Array = [], + listeners = command === PubSubSubscribeCommands.SUBSCRIBE ? this.#pubSubListeners.channels : this.#pubSubListeners.patterns; + for (const channel of (Array.isArray(channels) ? channels : [channels])) { + if (listeners.has(channel)) { + listeners.get(channel)!.add(listener); + continue; + } + + listeners.set(channel, new Set([listener])); + channelsToSubscribe.push(channel); + } + + if (!channelsToSubscribe.length) { + return Promise.resolve(); + } + + return this.#pushPubSubCommand(command, channelsToSubscribe); + } + + unsubscribe(command: PubSubUnsubscribeCommands, channels?: string | Array, listener?: PubSubListener): Promise { + const listeners = command === PubSubUnsubscribeCommands.UNSUBSCRIBE ? this.#pubSubListeners.channels : this.#pubSubListeners.patterns; + if (!channels) { + listeners.clear(); + return this.#pushPubSubCommand(command); + } + + const channelsToUnsubscribe = []; + for (const channel of (Array.isArray(channels) ? channels : [channels])) { + const set = listeners.get(channel); + if (!set) continue; + + let shouldUnsubscribe = !listener; + if (listener) { + set.delete(listener); + shouldUnsubscribe = set.size === 0; + } + + if (shouldUnsubscribe) { + channelsToUnsubscribe.push(channel); + listeners.delete(channel); + } + } + + if (!channelsToUnsubscribe.length) { + return Promise.resolve(); + } + + return this.#pushPubSubCommand(command, channelsToUnsubscribe); + } + + #pushPubSubCommand(command: PubSubSubscribeCommands | PubSubUnsubscribeCommands, channels?: Array): Promise { + return new Promise((resolve, reject) => { + const isSubscribe = command === PubSubSubscribeCommands.SUBSCRIBE || command === PubSubSubscribeCommands.PSUBSCRIBE, + inProgressKey = isSubscribe ? 'subscribing' : 'unsubscribing', + commandArgs: Array = [command]; + let channelsCounter: number; + if (channels?.length) { + commandArgs.push(...channels); + channelsCounter = channels.length; + } else { + // unsubscribe only + channelsCounter = ( + command[0] === 'P' ? + this.#pubSubListeners.patterns : + this.#pubSubListeners.channels + ).size; + } + + this.#pubSubState[inProgressKey] += channelsCounter; + this.#waitingToBeSent.push({ + encodedCommand: encodeCommand(commandArgs), + channelsCounter, + resolve: () => { + this.#pubSubState[inProgressKey] -= channelsCounter; + this.#pubSubState.subscribed += channelsCounter * (isSubscribe ? 1 : -1); + resolve(); + }, + reject: () => { + this.#pubSubState[inProgressKey] -= channelsCounter; + reject(); + } + }); + }); + } + + resubscribe(): Promise | undefined { + if (!this.#pubSubState.subscribed && !this.#pubSubState.subscribing) { + 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()]) + ]); + } + + executeChunk(recommendedSize: number): boolean | undefined { + if (!this.#waitingToBeSent.length) return; + + const encoded: Array = []; + let size = 0, + lastCommandChainId: symbol | undefined; + for (const command of this.#waitingToBeSent) { + encoded.push(command.encodedCommand); + size += command.encodedCommand.length; + if (size > recommendedSize) { + lastCommandChainId = command.chainId; + break; + } + } + + if (!lastCommandChainId && encoded.length === this.#waitingToBeSent.length) { + lastCommandChainId = this.#waitingToBeSent.tail!.value.chainId; + } + + lastCommandChainId ??= this.#waitingToBeSent.tail?.value.chainId; + + this.#executor(encoded.join('')); + + for (let i = 0; i < encoded.length; i++) { + const waitingToBeSent = this.#waitingToBeSent.shift()!; + if (waitingToBeSent.abort) { + waitingToBeSent.abort.signal.removeEventListener('abort', waitingToBeSent.abort.listener); + } + + this.#waitingForReply.push({ + resolve: waitingToBeSent.resolve, + reject: waitingToBeSent.reject, + channelsCounter: waitingToBeSent.channelsCounter + }); + } + + this.#chainInExecution = lastCommandChainId; + this.#waitingToBeSentCommandsLength -= size; + } + + parseResponse(data: Buffer): void { + this.#parser.execute(data); + } + + #shiftWaitingForReply(): CommandWaitingForReply { + 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); + } +} diff --git a/lib/commands.js b/lib/commands.js deleted file mode 100644 index a3b5189698b..00000000000 --- a/lib/commands.js +++ /dev/null @@ -1,105 +0,0 @@ -'use strict'; - -var commands = require('redis-commands'); -var Multi = require('./multi'); -var RedisClient = require('../').RedisClient; -var Command = require('./command'); - -var addCommand = function (command) { - // Some rare Redis commands use special characters in their command name - // Convert those to a underscore to prevent using invalid function names - var commandName = command.replace(/(?:^([0-9])|[^a-zA-Z0-9_$])/g, '_$1'); - - // Do not override existing functions - if (!RedisClient.prototype[command]) { - RedisClient.prototype[command.toUpperCase()] = RedisClient.prototype[command] = function () { - var arr; - var len = arguments.length; - var callback; - var i = 0; - if (Array.isArray(arguments[0])) { - arr = arguments[0]; - if (len === 2) { - callback = arguments[1]; - } - } else if (len > 1 && Array.isArray(arguments[1])) { - if (len === 3) { - callback = arguments[2]; - } - len = arguments[1].length; - arr = new Array(len + 1); - arr[0] = arguments[0]; - for (; i < len; i += 1) { - arr[i + 1] = arguments[1][i]; - } - } else { - // The later should not be the average use case - if (len !== 0 && (typeof arguments[len - 1] === 'function' || typeof arguments[len - 1] === 'undefined')) { - len--; - callback = arguments[len]; - } - arr = new Array(len); - for (; i < len; i += 1) { - arr[i] = arguments[i]; - } - } - return this.internal_send_command(new Command(command, arr, callback)); - }; - // Alias special function names (e.g. NR.RUN becomes NR_RUN and nr_run) - if (commandName !== command) { - RedisClient.prototype[commandName.toUpperCase()] = RedisClient.prototype[commandName] = RedisClient.prototype[command]; - } - Object.defineProperty(RedisClient.prototype[command], 'name', { - value: commandName - }); - } - - // Do not override existing functions - if (!Multi.prototype[command]) { - Multi.prototype[command.toUpperCase()] = Multi.prototype[command] = function () { - var arr; - var len = arguments.length; - var callback; - var i = 0; - if (Array.isArray(arguments[0])) { - arr = arguments[0]; - if (len === 2) { - callback = arguments[1]; - } - } else if (len > 1 && Array.isArray(arguments[1])) { - if (len === 3) { - callback = arguments[2]; - } - len = arguments[1].length; - arr = new Array(len + 1); - arr[0] = arguments[0]; - for (; i < len; i += 1) { - arr[i + 1] = arguments[1][i]; - } - } else { - // The later should not be the average use case - if (len !== 0 && (typeof arguments[len - 1] === 'function' || typeof arguments[len - 1] === 'undefined')) { - len--; - callback = arguments[len]; - } - arr = new Array(len); - for (; i < len; i += 1) { - arr[i] = arguments[i]; - } - } - this.queue.push(new Command(command, arr, callback)); - return this; - }; - // Alias special function names (e.g. NR.RUN becomes NR_RUN and nr_run) - if (commandName !== command) { - Multi.prototype[commandName.toUpperCase()] = Multi.prototype[commandName] = Multi.prototype[command]; - } - Object.defineProperty(Multi.prototype[command], 'name', { - value: commandName - }); - } -}; - -commands.list.forEach(addCommand); - -module.exports = addCommand; diff --git a/lib/commands/ACL_CAT.spec.ts b/lib/commands/ACL_CAT.spec.ts new file mode 100644 index 00000000000..77ed1cb7a07 --- /dev/null +++ b/lib/commands/ACL_CAT.spec.ts @@ -0,0 +1,23 @@ +import { strict as assert } from 'assert'; +import { describeHandleMinimumRedisVersion } from '../test-utils'; +import { transformArguments } from './ACL_CAT'; + +describe('ACL CAT', () => { + describeHandleMinimumRedisVersion([6]); + + describe('transformArguments', () => { + it('simple', () => { + assert.deepEqual( + transformArguments(), + ['ACL', 'CAT'] + ); + }); + + it('with categoryName', () => { + assert.deepEqual( + transformArguments('dangerous'), + ['ACL', 'CAT', 'dangerous'] + ); + }); + }); +}); diff --git a/lib/commands/ACL_CAT.ts b/lib/commands/ACL_CAT.ts new file mode 100644 index 00000000000..d1620ef1584 --- /dev/null +++ b/lib/commands/ACL_CAT.ts @@ -0,0 +1,13 @@ +import { transformReplyStringArray } from './generic-transformers'; + +export function transformArguments(categoryName?: string): Array { + const args = ['ACL', 'CAT']; + + if (categoryName) { + args.push(categoryName); + } + + return args; +} + +export const transformReply = transformReplyStringArray; diff --git a/lib/commands/ACL_DELUSER.spec.ts b/lib/commands/ACL_DELUSER.spec.ts new file mode 100644 index 00000000000..c64e8db1965 --- /dev/null +++ b/lib/commands/ACL_DELUSER.spec.ts @@ -0,0 +1,30 @@ +import { strict as assert } from 'assert'; +import { describeHandleMinimumRedisVersion, itWithClient, TestRedisServers } from '../test-utils'; +import { transformArguments } from './ACL_DELUSER'; + +describe('ACL DELUSER', () => { + describeHandleMinimumRedisVersion([6]); + + describe('transformArguments', () => { + it('string', () => { + assert.deepEqual( + transformArguments('username'), + ['ACL', 'DELUSER', 'username'] + ); + }); + + it('array', () => { + assert.deepEqual( + transformArguments(['1', '2']), + ['ACL', 'DELUSER', '1', '2'] + ); + }); + }); + + itWithClient(TestRedisServers.OPEN, 'client.aclDelUser', async client => { + assert.equal( + await client.aclDelUser('dosenotexists'), + 0 + ); + }); +}); diff --git a/lib/commands/ACL_DELUSER.ts b/lib/commands/ACL_DELUSER.ts new file mode 100644 index 00000000000..7fb4904be41 --- /dev/null +++ b/lib/commands/ACL_DELUSER.ts @@ -0,0 +1,7 @@ +import { pushVerdictArguments, transformReplyNumber } from './generic-transformers'; + +export function transformArguments(username: string | Array): Array { + return pushVerdictArguments(['ACL', 'DELUSER'], username); +} + +export const transformReply = transformReplyNumber; diff --git a/lib/commands/ACL_GENPASS.spec.ts b/lib/commands/ACL_GENPASS.spec.ts new file mode 100644 index 00000000000..a288a4f7142 --- /dev/null +++ b/lib/commands/ACL_GENPASS.spec.ts @@ -0,0 +1,23 @@ +import { strict as assert } from 'assert'; +import { describeHandleMinimumRedisVersion } from '../test-utils'; +import { transformArguments } from './ACL_GENPASS'; + +describe('ACL GENPASS', () => { + describeHandleMinimumRedisVersion([6]); + + describe('transformArguments', () => { + it('simple', () => { + assert.deepEqual( + transformArguments(), + ['ACL', 'GENPASS'] + ); + }); + + it('with bits', () => { + assert.deepEqual( + transformArguments(128), + ['ACL', 'GENPASS', '128'] + ); + }); + }); +}); diff --git a/lib/commands/ACL_GENPASS.ts b/lib/commands/ACL_GENPASS.ts new file mode 100644 index 00000000000..ec55aebdb07 --- /dev/null +++ b/lib/commands/ACL_GENPASS.ts @@ -0,0 +1,13 @@ +import { transformReplyString } from './generic-transformers'; + +export function transformArguments(bits?: number): Array { + const args = ['ACL', 'GENPASS']; + + if (bits) { + args.push(bits.toString()); + } + + return args; +} + +export const transformReply = transformReplyString; diff --git a/lib/commands/ACL_GETUSER.spec.ts b/lib/commands/ACL_GETUSER.spec.ts new file mode 100644 index 00000000000..c43cdc364ae --- /dev/null +++ b/lib/commands/ACL_GETUSER.spec.ts @@ -0,0 +1,27 @@ +import { strict as assert } from 'assert'; +import { describeHandleMinimumRedisVersion, itWithClient, TestRedisServers } from '../test-utils'; +import { transformArguments } from './ACL_GETUSER'; + +describe('ACL GETUSER', () => { + describeHandleMinimumRedisVersion([6]); + + it('transformArguments', () => { + assert.deepEqual( + transformArguments('username'), + ['ACL', 'GETUSER', 'username'] + ); + }); + + itWithClient(TestRedisServers.OPEN, 'client.aclGetUser', async client => { + assert.deepEqual( + await client.aclGetUser('default'), + { + flags: ['on', 'allkeys', 'allchannels', 'allcommands', 'nopass'], + passwords: [], + commands: '+@all', + keys: ['*'], + channels: ['*'] + } + ); + }); +}); diff --git a/lib/commands/ACL_GETUSER.ts b/lib/commands/ACL_GETUSER.ts new file mode 100644 index 00000000000..876a723c390 --- /dev/null +++ b/lib/commands/ACL_GETUSER.ts @@ -0,0 +1,34 @@ +export function transformArguments(username: string): Array { + return ['ACL', 'GETUSER', username]; +} + +type AclGetUserRawReply = [ + _: string, + flags: Array, + _: string, + passwords: Array, + _: string, + commands: string, + _: string, + keys: Array, + _: string, + channels: Array +]; + +interface AclUser { + flags: Array; + passwords: Array; + commands: string; + keys: Array; + channels: Array +} + +export function transformReply(reply: AclGetUserRawReply): AclUser { + return { + flags: reply[1], + passwords: reply[3], + commands: reply[5], + keys: reply[7], + channels: reply[9] + }; +} diff --git a/lib/commands/ACL_LIST.spec.ts b/lib/commands/ACL_LIST.spec.ts new file mode 100644 index 00000000000..ab6bae762f1 --- /dev/null +++ b/lib/commands/ACL_LIST.spec.ts @@ -0,0 +1,14 @@ +import { strict as assert } from 'assert'; +import { describeHandleMinimumRedisVersion } from '../test-utils'; +import { transformArguments } from './ACL_LIST'; + +describe('ACL LIST', () => { + describeHandleMinimumRedisVersion([6]); + + it('transformArguments', () => { + assert.deepEqual( + transformArguments(), + ['ACL', 'LIST'] + ); + }); +}); diff --git a/lib/commands/ACL_LIST.ts b/lib/commands/ACL_LIST.ts new file mode 100644 index 00000000000..3f2845b907a --- /dev/null +++ b/lib/commands/ACL_LIST.ts @@ -0,0 +1,7 @@ +import { transformReplyStringArray } from './generic-transformers'; + +export function transformArguments(): Array { + return ['ACL', 'LIST']; +} + +export const transformReply = transformReplyStringArray; diff --git a/lib/commands/ACL_LOAD.spec.ts b/lib/commands/ACL_LOAD.spec.ts new file mode 100644 index 00000000000..d173d7f1355 --- /dev/null +++ b/lib/commands/ACL_LOAD.spec.ts @@ -0,0 +1,14 @@ +import { strict as assert } from 'assert'; +import { describeHandleMinimumRedisVersion } from '../test-utils'; +import { transformArguments } from './ACL_SAVE'; + +describe('ACL SAVE', () => { + describeHandleMinimumRedisVersion([6]); + + it('transformArguments', () => { + assert.deepEqual( + transformArguments(), + ['ACL', 'SAVE'] + ); + }); +}); diff --git a/lib/commands/ACL_LOAD.ts b/lib/commands/ACL_LOAD.ts new file mode 100644 index 00000000000..59418614ed3 --- /dev/null +++ b/lib/commands/ACL_LOAD.ts @@ -0,0 +1,7 @@ +import { transformReplyString } from './generic-transformers'; + +export function transformArguments(): Array { + return ['ACL', 'LOAD']; +} + +export const transformReply = transformReplyString; diff --git a/lib/commands/ACL_LOG.spec.ts b/lib/commands/ACL_LOG.spec.ts new file mode 100644 index 00000000000..3ce76ce4563 --- /dev/null +++ b/lib/commands/ACL_LOG.spec.ts @@ -0,0 +1,53 @@ +import { strict as assert } from 'assert'; +import { describeHandleMinimumRedisVersion } from '../test-utils'; +import { transformArguments, transformReply } from './ACL_LOG'; + +describe('ACL LOG', () => { + describeHandleMinimumRedisVersion([6]); + + describe('transformArguments', () => { + it('simple', () => { + assert.deepEqual( + transformArguments(), + ['ACL', 'LOG'] + ); + }); + + it('with count', () => { + assert.deepEqual( + transformArguments(10), + ['ACL', 'LOG', '10'] + ); + }); + }); + + it('transformReply', () => { + assert.deepEqual( + transformReply([[ + 'count', + 1, + 'reason', + 'auth', + 'context', + 'toplevel', + 'object', + 'AUTH', + 'username', + 'someuser', + 'age-seconds', + '4.096', + 'client-info', + 'id=6 addr=127.0.0.1:63026 fd=8 name= age=9 idle=0 flags=N db=0 sub=0 psub=0 multi=-1 qbuf=48 qbuf-free=32720 obl=0 oll=0 omem=0 events=r cmd=auth user=default' + ]]), + [{ + count: 1, + reason: 'auth', + context: 'toplevel', + object: 'AUTH', + username: 'someuser', + ageSeconds: 4.096, + clientInfo: 'id=6 addr=127.0.0.1:63026 fd=8 name= age=9 idle=0 flags=N db=0 sub=0 psub=0 multi=-1 qbuf=48 qbuf-free=32720 obl=0 oll=0 omem=0 events=r cmd=auth user=default' + }] + ); + }); +}); diff --git a/lib/commands/ACL_LOG.ts b/lib/commands/ACL_LOG.ts new file mode 100644 index 00000000000..ed0590b5783 --- /dev/null +++ b/lib/commands/ACL_LOG.ts @@ -0,0 +1,48 @@ +export function transformArguments(count?: number): Array { + const args = ['ACL', 'LOG']; + + if (count) { + args.push(count.toString()); + } + + return args; +} + +type AclLogRawReply = [ + _: string, + count: number, + _: string, + reason: string, + _: string, + context: string, + _: string, + object: string, + _: string, + username: string, + _: string, + ageSeconds: string, + _: string, + clientInfo: string +]; + +interface AclLog { + count: number; + reason: string; + context: string; + object: string; + username: string; + ageSeconds: number; + clientInfo: string; +} + +export function transformReply(reply: Array): Array { + return reply.map(log => ({ + count: log[1], + reason: log[3], + context: log[5], + object: log[7], + username: log[9], + ageSeconds: Number(log[11]), + clientInfo: log[13] + })); +} diff --git a/lib/commands/ACL_LOG_RESET.spec.ts b/lib/commands/ACL_LOG_RESET.spec.ts new file mode 100644 index 00000000000..3f0e628d9f0 --- /dev/null +++ b/lib/commands/ACL_LOG_RESET.spec.ts @@ -0,0 +1,14 @@ +import { strict as assert } from 'assert'; +import { describeHandleMinimumRedisVersion } from '../test-utils'; +import { transformArguments } from './ACL_LOG_RESET'; + +describe('ACL LOG RESET', () => { + describeHandleMinimumRedisVersion([6]); + + it('transformArguments', () => { + assert.deepEqual( + transformArguments(), + ['ACL', 'LOG', 'RESET'] + ); + }); +}); diff --git a/lib/commands/ACL_LOG_RESET.ts b/lib/commands/ACL_LOG_RESET.ts new file mode 100644 index 00000000000..30b8ccb20c7 --- /dev/null +++ b/lib/commands/ACL_LOG_RESET.ts @@ -0,0 +1,7 @@ +import { transformReplyString } from './generic-transformers'; + +export function transformArguments(): Array { + return ['ACL', 'LOG', 'RESET']; +} + +export const transformReply = transformReplyString; diff --git a/lib/commands/ACL_SAVE.spec.ts b/lib/commands/ACL_SAVE.spec.ts new file mode 100644 index 00000000000..b34c7bb0e6f --- /dev/null +++ b/lib/commands/ACL_SAVE.spec.ts @@ -0,0 +1,14 @@ +import { strict as assert } from 'assert'; +import { describeHandleMinimumRedisVersion } from '../test-utils'; +import { transformArguments } from './ACL_LOAD'; + +describe('ACL LOAD', () => { + describeHandleMinimumRedisVersion([6]); + + it('transformArguments', () => { + assert.deepEqual( + transformArguments(), + ['ACL', 'LOAD'] + ); + }); +}); diff --git a/lib/commands/ACL_SAVE.ts b/lib/commands/ACL_SAVE.ts new file mode 100644 index 00000000000..5b9c7b84cc3 --- /dev/null +++ b/lib/commands/ACL_SAVE.ts @@ -0,0 +1,7 @@ +import { transformReplyString } from './generic-transformers'; + +export function transformArguments(): Array { + return ['ACL', 'SAVE']; +} + +export const transformReply = transformReplyString; diff --git a/lib/commands/ACL_SETUSER.spec.ts b/lib/commands/ACL_SETUSER.spec.ts new file mode 100644 index 00000000000..f3badfcdca8 --- /dev/null +++ b/lib/commands/ACL_SETUSER.spec.ts @@ -0,0 +1,23 @@ +import { strict as assert } from 'assert'; +import { describeHandleMinimumRedisVersion } from '../test-utils'; +import { transformArguments } from './ACL_SETUSER'; + +describe('ACL SETUSER', () => { + describeHandleMinimumRedisVersion([6]); + + describe('transformArguments', () => { + it('string', () => { + assert.deepEqual( + transformArguments('username', 'allkeys'), + ['ACL', 'SETUSER', 'username', 'allkeys'] + ); + }); + + it('array', () => { + assert.deepEqual( + transformArguments('username', ['allkeys', 'allchannels']), + ['ACL', 'SETUSER', 'username', 'allkeys', 'allchannels'] + ); + }); + }); +}); diff --git a/lib/commands/ACL_SETUSER.ts b/lib/commands/ACL_SETUSER.ts new file mode 100644 index 00000000000..b2829ca964f --- /dev/null +++ b/lib/commands/ACL_SETUSER.ts @@ -0,0 +1,7 @@ +import { pushVerdictArguments, transformReplyString } from './generic-transformers'; + +export function transformArguments(username: string, rule: string | Array): Array { + return pushVerdictArguments(['ACL', 'SETUSER', username], rule); +} + +export const transformReply = transformReplyString; diff --git a/lib/commands/ACL_USERS.spec.ts b/lib/commands/ACL_USERS.spec.ts new file mode 100644 index 00000000000..14b76725fcd --- /dev/null +++ b/lib/commands/ACL_USERS.spec.ts @@ -0,0 +1,14 @@ +import { strict as assert } from 'assert'; +import { describeHandleMinimumRedisVersion } from '../test-utils'; +import { transformArguments } from './ACL_USERS'; + +describe('ACL USERS', () => { + describeHandleMinimumRedisVersion([6]); + + it('transformArguments', () => { + assert.deepEqual( + transformArguments(), + ['ACL', 'USERS'] + ); + }); +}); diff --git a/lib/commands/ACL_USERS.ts b/lib/commands/ACL_USERS.ts new file mode 100644 index 00000000000..f9e837a4347 --- /dev/null +++ b/lib/commands/ACL_USERS.ts @@ -0,0 +1,7 @@ +import { transformReplyStringArray } from './generic-transformers'; + +export function transformArguments(): Array { + return ['ACL', 'USERS']; +} + +export const transformReply = transformReplyStringArray; diff --git a/lib/commands/ACL_WHOAMI.spec.ts b/lib/commands/ACL_WHOAMI.spec.ts new file mode 100644 index 00000000000..a933057ea9d --- /dev/null +++ b/lib/commands/ACL_WHOAMI.spec.ts @@ -0,0 +1,14 @@ +import { strict as assert } from 'assert'; +import { describeHandleMinimumRedisVersion } from '../test-utils'; +import { transformArguments } from './ACL_WHOAMI'; + +describe('ACL WHOAMI', () => { + describeHandleMinimumRedisVersion([6]); + + it('transformArguments', () => { + assert.deepEqual( + transformArguments(), + ['ACL', 'WHOAMI'] + ); + }); +}); diff --git a/lib/commands/ACL_WHOAMI.ts b/lib/commands/ACL_WHOAMI.ts new file mode 100644 index 00000000000..3fc70649f87 --- /dev/null +++ b/lib/commands/ACL_WHOAMI.ts @@ -0,0 +1,7 @@ +import { transformReplyString } from './generic-transformers'; + +export function transformArguments(): Array { + return ['ACL', 'WHOAMI']; +} + +export const transformReply = transformReplyString; diff --git a/lib/commands/APPEND.spec.ts b/lib/commands/APPEND.spec.ts new file mode 100644 index 00000000000..283ab807956 --- /dev/null +++ b/lib/commands/APPEND.spec.ts @@ -0,0 +1,11 @@ +import { strict as assert } from 'assert'; +import { transformArguments } from './APPEND'; + +describe('AUTH', () => { + it('transformArguments', () => { + assert.deepEqual( + transformArguments('key', 'value'), + ['APPEND', 'key', 'value'] + ); + }); +}); diff --git a/lib/commands/APPEND.ts b/lib/commands/APPEND.ts new file mode 100644 index 00000000000..f64e835113c --- /dev/null +++ b/lib/commands/APPEND.ts @@ -0,0 +1,9 @@ +import { transformReplyString } from './generic-transformers'; + +export const FIRST_KEY_INDEX = 1; + +export function transformArguments(key: string, value: string): Array { + return ['APPEND', key, value]; +} + +export const transformReply = transformReplyString; diff --git a/lib/commands/ASKING.spec.ts b/lib/commands/ASKING.spec.ts new file mode 100644 index 00000000000..3da2015199e --- /dev/null +++ b/lib/commands/ASKING.spec.ts @@ -0,0 +1,11 @@ +import { strict as assert } from 'assert'; +import { transformArguments } from './ASKING'; + +describe('ASKING', () => { + it('transformArguments', () => { + assert.deepEqual( + transformArguments(), + ['ASKING'] + ); + }); +}); diff --git a/lib/commands/ASKING.ts b/lib/commands/ASKING.ts new file mode 100644 index 00000000000..3f836131ac1 --- /dev/null +++ b/lib/commands/ASKING.ts @@ -0,0 +1,7 @@ +import { transformReplyString } from './generic-transformers'; + +export function transformArguments(): Array { + return ['ASKING']; +} + +export const transformReply = transformReplyString; diff --git a/lib/commands/AUTH.spec.ts b/lib/commands/AUTH.spec.ts new file mode 100644 index 00000000000..1907488346e --- /dev/null +++ b/lib/commands/AUTH.spec.ts @@ -0,0 +1,25 @@ +import { strict as assert } from 'assert'; +import { transformArguments } from './AUTH'; + +describe('AUTH', () => { + describe('transformArguments', () => { + it('password only', () => { + assert.deepEqual( + transformArguments({ + password: 'password' + }), + ['AUTH', 'password'] + ); + }); + + it('username & password', () => { + assert.deepEqual( + transformArguments({ + username: 'username', + password: 'password' + }), + ['AUTH', 'username', 'password'] + ); + }); + }); +}); diff --git a/lib/commands/AUTH.ts b/lib/commands/AUTH.ts new file mode 100644 index 00000000000..750f0f54354 --- /dev/null +++ b/lib/commands/AUTH.ts @@ -0,0 +1,16 @@ +import { transformReplyString } from './generic-transformers'; + +export interface AuthOptions { + username?: string; + password: string; +} + +export function transformArguments({username, password}: AuthOptions): Array { + if (!username) { + return ['AUTH', password]; + } + + return ['AUTH', username, password]; +} + +export const transformReply = transformReplyString; diff --git a/lib/commands/BGREWRITEAOF.spec.ts b/lib/commands/BGREWRITEAOF.spec.ts new file mode 100644 index 00000000000..d0e150e155b --- /dev/null +++ b/lib/commands/BGREWRITEAOF.spec.ts @@ -0,0 +1,11 @@ +import { strict as assert } from 'assert'; +import { transformArguments } from './BGREWRITEAOF'; + +describe('BGREWRITEAOF', () => { + it('transformArguments', () => { + assert.deepEqual( + transformArguments(), + ['BGREWRITEAOF'] + ); + }); +}); diff --git a/lib/commands/BGREWRITEAOF.ts b/lib/commands/BGREWRITEAOF.ts new file mode 100644 index 00000000000..52d3f4df9da --- /dev/null +++ b/lib/commands/BGREWRITEAOF.ts @@ -0,0 +1,7 @@ +import { transformReplyString } from './generic-transformers'; + +export function transformArguments(): Array { + return ['BGREWRITEAOF']; +} + +export const transformReply = transformReplyString; diff --git a/lib/commands/BGSAVE.spec.ts b/lib/commands/BGSAVE.spec.ts new file mode 100644 index 00000000000..8e4de5eef5b --- /dev/null +++ b/lib/commands/BGSAVE.spec.ts @@ -0,0 +1,23 @@ +import { strict as assert } from 'assert'; +import { describe } from 'mocha'; +import { transformArguments } from './BGSAVE'; + +describe('BGSAVE', () => { + describe('transformArguments', () => { + it('simple', () => { + assert.deepEqual( + transformArguments(), + ['BGSAVE'] + ); + }); + + it('with SCHEDULE', () => { + assert.deepEqual( + transformArguments({ + SCHEDULE: true + }), + ['BGSAVE', 'SCHEDULE'] + ); + }); + }); +}); diff --git a/lib/commands/BGSAVE.ts b/lib/commands/BGSAVE.ts new file mode 100644 index 00000000000..f09f906ade7 --- /dev/null +++ b/lib/commands/BGSAVE.ts @@ -0,0 +1,17 @@ +import { transformReplyString } from './generic-transformers'; + +interface BgSaveOptions { + SCHEDULE?: true; +} + +export function transformArguments(options?: BgSaveOptions): Array { + const args = ['BGSAVE']; + + if (options?.SCHEDULE) { + args.push('SCHEDULE'); + } + + return args; +} + +export const transformReply = transformReplyString; \ No newline at end of file diff --git a/lib/commands/BITCOUNT.spec.ts b/lib/commands/BITCOUNT.spec.ts new file mode 100644 index 00000000000..bf4cf39cab6 --- /dev/null +++ b/lib/commands/BITCOUNT.spec.ts @@ -0,0 +1,31 @@ +import { strict as assert } from 'assert'; +import { TestRedisServers, itWithClient } from '../test-utils'; +import { transformArguments } from './BITCOUNT'; + +describe('BITCOUNT', () => { + describe('transformArguments', () => { + it('simple', () => { + assert.deepEqual( + transformArguments('key'), + ['BITCOUNT', 'key'] + ); + }); + + it('with range', () => { + assert.deepEqual( + transformArguments('key', { + start: 0, + end: 1 + }), + ['BITCOUNT', 'key', '0', '1'] + ); + }); + }); + + itWithClient(TestRedisServers.OPEN, 'client.bitCount', async client => { + assert.equal( + await client.bitCount('key'), + 0 + ); + }); +}); diff --git a/lib/commands/BITCOUNT.ts b/lib/commands/BITCOUNT.ts new file mode 100644 index 00000000000..1aececc377e --- /dev/null +++ b/lib/commands/BITCOUNT.ts @@ -0,0 +1,25 @@ +import { transformReplyNumber } from './generic-transformers'; + +export const FIRST_KEY_INDEX = 1; + +export const IS_READ_ONLY = true; + +interface BitCountRange { + start: number; + end: number; +} + +export function transformArguments(key: string, range?: BitCountRange): Array { + const args = ['BITCOUNT', key]; + + if (range) { + args.push( + range.start.toString(), + range.end.toString() + ); + } + + return args; +} + +export const transformReply = transformReplyNumber; \ No newline at end of file diff --git a/lib/commands/BITFIELD.spec.ts b/lib/commands/BITFIELD.spec.ts new file mode 100644 index 00000000000..4d6d9d11c1a --- /dev/null +++ b/lib/commands/BITFIELD.spec.ts @@ -0,0 +1,42 @@ +import { strict as assert } from 'assert'; +import { TestRedisServers, itWithClient } from '../test-utils'; +import { transformArguments } from './BITFIELD'; + +describe('BITFIELD', () => { + it('transformArguments', () => { + assert.deepEqual( + transformArguments('key', [{ + operation: 'OVERFLOW', + behavior: 'WRAP' + }, { + operation: 'GET', + type: 'i8', + offset: 0 + }, { + operation: 'OVERFLOW', + behavior: 'SAT' + }, { + operation: 'SET', + type: 'i16', + offset: 1, + value: 0 + }, { + operation: 'OVERFLOW', + behavior: 'FAIL' + }, { + operation: 'INCRBY', + type: 'i32', + offset: 2, + increment: 1 + }]), + ['BITFIELD', 'key', 'OVERFLOW', 'WRAP', 'GET', 'i8', '0', 'OVERFLOW', 'SAT', 'SET', 'i16', '1', '0', 'OVERFLOW', 'FAIL', 'INCRBY', 'i32', '2', '1'] + ); + }); + + itWithClient(TestRedisServers.OPEN, 'client.bitField', async client => { + assert.deepEqual( + await client.bitField('key', []), + [] + ); + }); +}); diff --git a/lib/commands/BITFIELD.ts b/lib/commands/BITFIELD.ts new file mode 100644 index 00000000000..445c26e28a3 --- /dev/null +++ b/lib/commands/BITFIELD.ts @@ -0,0 +1,84 @@ +import { transformReplyNumberNullArray } from './generic-transformers'; + +export const FIRST_KEY_INDEX = 1; + +export const IS_READ_ONLY = true; + +type BitFieldType = string; // TODO 'i[1-64]' | 'u[1-63]' + +interface BitFieldOperation { + operation: S; +} + +interface BitFieldGetOperation extends BitFieldOperation<'GET'> { + type: BitFieldType; + offset: number | string; +} + +interface BitFieldSetOperation extends BitFieldOperation<'SET'> { + type: BitFieldType; + offset: number | string; + value: number; +} + +interface BitFieldIncrByOperation extends BitFieldOperation<'INCRBY'> { + type: BitFieldType; + offset: number | string; + increment: number; +} + +interface BitFieldOverflowOperation extends BitFieldOperation<'OVERFLOW'> { + behavior: string; +} + +type BitFieldOperations = Array< + BitFieldGetOperation | + BitFieldSetOperation | + BitFieldIncrByOperation | + BitFieldOverflowOperation +>; + +export function transformArguments(key: string, operations: BitFieldOperations): Array { + const args = ['BITFIELD', key]; + + for (const options of operations) { + switch (options.operation) { + case 'GET': + args.push( + 'GET', + options.type, + options.offset.toString() + ); + break; + + case 'SET': + args.push( + 'SET', + options.type, + options.offset.toString(), + options.value.toString() + ); + break; + + case 'INCRBY': + args.push( + 'INCRBY', + options.type, + options.offset.toString(), + options.increment.toString() + ) + break; + + case 'OVERFLOW': + args.push( + 'OVERFLOW', + options.behavior + ); + break; + } + } + + return args; +} + +export const transformReply = transformReplyNumberNullArray; diff --git a/lib/commands/BITOP.spec.ts b/lib/commands/BITOP.spec.ts new file mode 100644 index 00000000000..aa863e5f2d2 --- /dev/null +++ b/lib/commands/BITOP.spec.ts @@ -0,0 +1,35 @@ +import { strict as assert } from 'assert'; +import { TestRedisServers, itWithClient, TestRedisClusters, itWithCluster } from '../test-utils'; +import { transformArguments } from './BITOP'; + +describe('BITOP', () => { + describe('transformArguments', () => { + it('single key', () => { + assert.deepEqual( + transformArguments('AND', 'destKey', 'key'), + ['BITOP', 'AND', 'destKey', 'key'] + ); + }); + + it('multiple keys', () => { + assert.deepEqual( + transformArguments('AND', 'destKey', ['1', '2']), + ['BITOP', 'AND', 'destKey', '1', '2'] + ); + }); + }); + + itWithClient(TestRedisServers.OPEN, 'client.bitOp', async client => { + assert.equal( + await client.bitOp('AND', 'destKey', 'key'), + 0 + ); + }); + + itWithCluster(TestRedisClusters.OPEN, 'cluster.bitOp', async cluster => { + assert.equal( + await cluster.bitOp('AND', '{tag}destKey', '{tag}key'), + 0 + ); + }); +}); diff --git a/lib/commands/BITOP.ts b/lib/commands/BITOP.ts new file mode 100644 index 00000000000..fe7d339f5d1 --- /dev/null +++ b/lib/commands/BITOP.ts @@ -0,0 +1,11 @@ +import { pushVerdictArguments, transformReplyNumber } from './generic-transformers'; + +export const FIRST_KEY_INDEX = 2; + +type BitOperations = 'AND' | 'OR' | 'XOR' | 'NOT'; + +export function transformArguments(operation: BitOperations, destKey: string, key: string | Array): Array { + return pushVerdictArguments(['BITOP', operation, destKey], key); +} + +export const transformReply = transformReplyNumber; diff --git a/lib/commands/BITPOS.spec.ts b/lib/commands/BITPOS.spec.ts new file mode 100644 index 00000000000..ad08e708c54 --- /dev/null +++ b/lib/commands/BITPOS.spec.ts @@ -0,0 +1,42 @@ +import { strict as assert } from 'assert'; +import { TestRedisServers, itWithClient, TestRedisClusters, itWithCluster } from '../test-utils'; +import { transformArguments } from './BITPOS'; + +describe('BITPOS', () => { + describe('transformArguments', () => { + it('simple', () => { + assert.deepEqual( + transformArguments('key', 1), + ['BITPOS', 'key', '1'] + ); + }); + + it('with start', () => { + assert.deepEqual( + transformArguments('key', 1, 1), + ['BITPOS', 'key', '1', '1'] + ); + }); + + it('with start, end', () => { + assert.deepEqual( + transformArguments('key', 1, 1, -1), + ['BITPOS', 'key', '1', '1', '-1'] + ); + }); + }); + + itWithClient(TestRedisServers.OPEN, 'client.bitPos', async client => { + assert.equal( + await client.bitPos('key', 1, 1), + -1 + ); + }); + + itWithCluster(TestRedisClusters.OPEN, 'cluster.bitPos', async cluster => { + assert.equal( + await cluster.bitPos('key', 1, 1), + -1 + ); + }); +}); diff --git a/lib/commands/BITPOS.ts b/lib/commands/BITPOS.ts new file mode 100644 index 00000000000..bebc45b03c3 --- /dev/null +++ b/lib/commands/BITPOS.ts @@ -0,0 +1,21 @@ +import { BitValue, transformReplyNumber } from './generic-transformers'; + +export const FIRST_KEY_INDEX = 1; + +export const IS_READ_ONLY = true; + +export function transformArguments(key: string, bit: BitValue, start?: number, end?: number): Array { + const args = ['BITPOS', key, bit.toString()]; + + if (typeof start === 'number') { + args.push(start.toString()); + } + + if (typeof end === 'number') { + args.push(end.toString()); + } + + return args; +} + +export const transformReply = transformReplyNumber; \ No newline at end of file diff --git a/lib/commands/BLMOVE.spec.ts b/lib/commands/BLMOVE.spec.ts new file mode 100644 index 00000000000..b942864758f --- /dev/null +++ b/lib/commands/BLMOVE.spec.ts @@ -0,0 +1,43 @@ +import { strict as assert } from 'assert'; +import { TestRedisServers, itWithClient, itWithCluster, TestRedisClusters, describeHandleMinimumRedisVersion } from '../test-utils'; +import { transformArguments } from './BLMOVE'; +import { commandOptions } from '../../index'; + +describe('BLMOVE', () => { + describeHandleMinimumRedisVersion([6, 2]); + + it('transformArguments', () => { + assert.deepEqual( + transformArguments('source', 'destination', 'LEFT', 'RIGHT', 0), + ['BLMOVE', 'source', 'destination', 'LEFT', 'RIGHT', '0'] + ); + }); + + itWithClient(TestRedisServers.OPEN, 'client.blMove', async client => { + const [blMoveReply] = await Promise.all([ + client.blMove(commandOptions({ + isolated: true + }), 'source', 'destination', 'LEFT', 'RIGHT', 0), + client.lPush('source', 'element') + ]); + + assert.equal( + blMoveReply, + 'element' + ); + }); + + itWithCluster(TestRedisClusters.OPEN, 'cluster.blMove', async cluster => { + const [blMoveReply] = await Promise.all([ + cluster.blMove(commandOptions({ + isolated: true + }), '{tag}source', '{tag}destination', 'LEFT', 'RIGHT', 0), + cluster.lPush('{tag}source', 'element') + ]); + + assert.equal( + blMoveReply, + 'element' + ); + }); +}); diff --git a/lib/commands/BLMOVE.ts b/lib/commands/BLMOVE.ts new file mode 100644 index 00000000000..74a2eed4aa5 --- /dev/null +++ b/lib/commands/BLMOVE.ts @@ -0,0 +1,23 @@ +import { transformReplyStringNull } from './generic-transformers'; +import { LMoveSide } from './LMOVE'; + +export const FIRST_KEY_INDEX = 1; + +export function transformArguments( + source: string, + destination: string, + sourceDirection: LMoveSide, + destinationDirection: LMoveSide, + timeout: number +): Array { + return [ + 'BLMOVE', + source, + destination, + sourceDirection, + destinationDirection, + timeout.toString() + ]; +} + +export const transformReply = transformReplyStringNull; diff --git a/lib/commands/BLPOP.spec.ts b/lib/commands/BLPOP.spec.ts new file mode 100644 index 00000000000..651dd09eaf3 --- /dev/null +++ b/lib/commands/BLPOP.spec.ts @@ -0,0 +1,79 @@ +import { strict as assert } from 'assert'; +import { TestRedisServers, itWithClient, itWithCluster, TestRedisClusters } from '../test-utils'; +import { transformArguments, transformReply } from './BLPOP'; +import { commandOptions } from '../../index'; + +describe('BLPOP', () => { + describe('transformArguments', () => { + it('single', () => { + assert.deepEqual( + transformArguments('key', 0), + ['BLPOP', 'key', '0'] + ); + }); + + it('multiple', () => { + assert.deepEqual( + transformArguments(['key1', 'key2'], 0), + ['BLPOP', 'key1', 'key2', '0'] + ); + }); + }); + + describe('transformReply', () => { + it('null', () => { + assert.equal( + transformReply(null), + null + ); + }); + + it('member', () => { + assert.deepEqual( + transformReply(['key', 'element']), + { + key: 'key', + element: 'element' + } + ); + }); + }); + + itWithClient(TestRedisServers.OPEN, 'client.blPop', async client => { + const [ blPopReply ] = await Promise.all([ + client.blPop( + commandOptions({ isolated: true }), + 'key', + 1 + ), + client.lPush('key', 'element'), + ]); + + assert.deepEqual( + blPopReply, + { + key: 'key', + element: 'element' + } + ); + }); + + itWithCluster(TestRedisClusters.OPEN, 'cluster.blPop', async cluster => { + const [ blPopReply ] = await Promise.all([ + cluster.blPop( + commandOptions({ isolated: true }), + 'key', + 1 + ), + cluster.lPush('key', 'element'), + ]); + + assert.deepEqual( + blPopReply, + { + key: 'key', + element: 'element' + } + ); + }); +}); diff --git a/lib/commands/BLPOP.ts b/lib/commands/BLPOP.ts new file mode 100644 index 00000000000..7c352951fb3 --- /dev/null +++ b/lib/commands/BLPOP.ts @@ -0,0 +1,25 @@ +import { pushVerdictArguments } from './generic-transformers'; + +export const FIRST_KEY_INDEX = 1; + +export function transformArguments(keys: string | Array, timeout: number): Array { + const args = pushVerdictArguments(['BLPOP'], keys); + + args.push(timeout.toString()); + + return args; +} + +type BLPOPReply = null | { + key: string; + element: string; +}; + +export function transformReply(reply: null | [string, string]): BLPOPReply { + if (reply === null) return null; + + return { + key: reply[0], + element: reply[1] + }; +} diff --git a/lib/commands/BRPOP.spec.ts b/lib/commands/BRPOP.spec.ts new file mode 100644 index 00000000000..9a7d0bbc37d --- /dev/null +++ b/lib/commands/BRPOP.spec.ts @@ -0,0 +1,79 @@ +import { strict as assert } from 'assert'; +import { TestRedisServers, itWithClient, itWithCluster, TestRedisClusters } from '../test-utils'; +import { transformArguments, transformReply } from './BRPOP'; +import { commandOptions } from '../../index'; + +describe('BRPOP', () => { + describe('transformArguments', () => { + it('single', () => { + assert.deepEqual( + transformArguments('key', 0), + ['BRPOP', 'key', '0'] + ); + }); + + it('multiple', () => { + assert.deepEqual( + transformArguments(['key1', 'key2'], 0), + ['BRPOP', 'key1', 'key2', '0'] + ); + }); + }); + + describe('transformReply', () => { + it('null', () => { + assert.equal( + transformReply(null), + null + ); + }); + + it('member', () => { + assert.deepEqual( + transformReply(['key', 'element']), + { + key: 'key', + element: 'element' + } + ); + }); + }); + + itWithClient(TestRedisServers.OPEN, 'client.brPop', async client => { + const [ brPopReply ] = await Promise.all([ + client.brPop( + commandOptions({ isolated: true }), + 'key', + 1 + ), + client.lPush('key', 'element'), + ]); + + assert.deepEqual( + brPopReply, + { + key: 'key', + element: 'element' + } + ); + }); + + itWithCluster(TestRedisClusters.OPEN, 'cluster.brPop', async cluster => { + const [ brPopReply ] = await Promise.all([ + cluster.brPop( + commandOptions({ isolated: true }), + 'key', + 1 + ), + cluster.lPush('key', 'element'), + ]); + + assert.deepEqual( + brPopReply, + { + key: 'key', + element: 'element' + } + ); + }); +}); diff --git a/lib/commands/BRPOP.ts b/lib/commands/BRPOP.ts new file mode 100644 index 00000000000..a03c278309a --- /dev/null +++ b/lib/commands/BRPOP.ts @@ -0,0 +1,25 @@ +import { pushVerdictArguments } from './generic-transformers'; + +export const FIRST_KEY_INDEX = 1; + +export function transformArguments(key: string | Array, timeout: number): Array { + const args = pushVerdictArguments(['BRPOP'], key); + + args.push(timeout.toString()); + + return args; +} + +type BRPOPReply = null | { + key: string; + element: string; +}; + +export function transformReply(reply: null | [string, string]): BRPOPReply { + if (reply === null) return null; + + return { + key: reply[0], + element: reply[1] + }; +} diff --git a/lib/commands/BRPOPLPUSH.spec.ts b/lib/commands/BRPOPLPUSH.spec.ts new file mode 100644 index 00000000000..08bcf5e4d94 --- /dev/null +++ b/lib/commands/BRPOPLPUSH.spec.ts @@ -0,0 +1,47 @@ +import { strict as assert } from 'assert'; +import { TestRedisServers, itWithClient, itWithCluster, TestRedisClusters } from '../test-utils'; +import { transformArguments } from './BRPOPLPUSH'; +import { commandOptions } from '../../index'; + +describe('BRPOPLPUSH', () => { + it('transformArguments', () => { + assert.deepEqual( + transformArguments('source', 'destination', 0), + ['BRPOPLPUSH', 'source', 'destination', '0'] + ); + }); + + itWithClient(TestRedisServers.OPEN, 'client.brPopLPush', async client => { + const [ popReply ] = await Promise.all([ + client.brPopLPush( + commandOptions({ isolated: true }), + 'source', + 'destination', + 0 + ), + client.lPush('source', 'element') + ]); + + assert.equal( + popReply, + 'element' + ); + }); + + itWithCluster(TestRedisClusters.OPEN, 'cluster.brPopLPush', async cluster => { + const [ popReply ] = await Promise.all([ + cluster.brPopLPush( + commandOptions({ isolated: true }), + '{tag}source', + '{tag}destination', + 0 + ), + cluster.lPush('{tag}source', 'element') + ]); + + assert.equal( + popReply, + 'element' + ); + }); +}); diff --git a/lib/commands/BRPOPLPUSH.ts b/lib/commands/BRPOPLPUSH.ts new file mode 100644 index 00000000000..f6a3bc1b8a6 --- /dev/null +++ b/lib/commands/BRPOPLPUSH.ts @@ -0,0 +1,9 @@ +import { transformReplyNumberNull } from './generic-transformers'; + +export const FIRST_KEY_INDEX = 1; + +export function transformArguments(source: string, destination: string, timeout: number): Array { + return ['BRPOPLPUSH', source, destination, timeout.toString()]; +} + +export const transformReply = transformReplyNumberNull; diff --git a/lib/commands/BZPOPMAX.spec.ts b/lib/commands/BZPOPMAX.spec.ts new file mode 100644 index 00000000000..c4bcc321b29 --- /dev/null +++ b/lib/commands/BZPOPMAX.spec.ts @@ -0,0 +1,66 @@ +import { strict as assert } from 'assert'; +import { TestRedisServers, itWithClient } from '../test-utils'; +import { transformArguments, transformReply } from './BZPOPMAX'; +import { commandOptions } from '../../index'; +import { describe } from 'mocha'; + +describe('BZPOPMAX', () => { + describe('transformArguments', () => { + it('single', () => { + assert.deepEqual( + transformArguments('key', 0), + ['BZPOPMAX', 'key', '0'] + ); + }); + + it('multiple', () => { + assert.deepEqual( + transformArguments(['1', '2'], 0), + ['BZPOPMAX', '1', '2', '0'] + ); + }); + }); + + describe('transformReply', () => { + it('null', () => { + assert.equal( + transformReply(null), + null + ); + }); + + it('member', () => { + assert.deepEqual( + transformReply(['key', 'value', '1']), + { + key: 'key', + value: 'value', + score: 1 + } + ); + }); + }); + + itWithClient(TestRedisServers.OPEN, 'client.bzPopMax', async client => { + const [ bzPopMaxReply ] = await Promise.all([ + client.bzPopMax( + commandOptions({ isolated: true }), + 'key', + 0 + ), + client.zAdd('key', [{ + value: '1', + score: 1 + }]) + ]); + + assert.deepEqual( + bzPopMaxReply, + { + key: 'key', + value: '1', + score: 1 + } + ); + }); +}); diff --git a/lib/commands/BZPOPMAX.ts b/lib/commands/BZPOPMAX.ts new file mode 100644 index 00000000000..ccd84272a50 --- /dev/null +++ b/lib/commands/BZPOPMAX.ts @@ -0,0 +1,27 @@ +import { pushVerdictArguments, transformReplyNumberInfinity, ZMember } from './generic-transformers'; + +export const FIRST_KEY_INDEX = 1; + +export function transformArguments(key: string | Array, timeout: number): Array { + const args = pushVerdictArguments(['BZPOPMAX'], key); + + args.push(timeout.toString()); + + return args; +} + +interface ZMemberWithKey extends ZMember { + key: string; +} + +type BZPopMaxReply = ZMemberWithKey | null; + +export function transformReply(reply: [key: string, value: string, score: string] | null): BZPopMaxReply | null { + if (!reply) return null; + + return { + key: reply[0], + value: reply[1], + score: transformReplyNumberInfinity(reply[2]) + }; +} diff --git a/lib/commands/BZPOPMIN.spec.ts b/lib/commands/BZPOPMIN.spec.ts new file mode 100644 index 00000000000..8b8977f9b3a --- /dev/null +++ b/lib/commands/BZPOPMIN.spec.ts @@ -0,0 +1,65 @@ +import { strict as assert } from 'assert'; +import { TestRedisServers, itWithClient } from '../test-utils'; +import { transformArguments, transformReply } from './BZPOPMIN'; +import { commandOptions } from '../../index'; + +describe('BZPOPMIN', () => { + describe('transformArguments', () => { + it('single', () => { + assert.deepEqual( + transformArguments('key', 0), + ['BZPOPMIN', 'key', '0'] + ); + }); + + it('multiple', () => { + assert.deepEqual( + transformArguments(['1', '2'], 0), + ['BZPOPMIN', '1', '2', '0'] + ); + }); + }); + + describe('transformReply', () => { + it('null', () => { + assert.equal( + transformReply(null), + null + ); + }); + + it('member', () => { + assert.deepEqual( + transformReply(['key', 'value', '1']), + { + key: 'key', + value: 'value', + score: 1 + } + ); + }); + }); + + itWithClient(TestRedisServers.OPEN, 'client.bzPopMin', async client => { + const [ bzPopMinReply ] = await Promise.all([ + client.bzPopMin( + commandOptions({ isolated: true }), + 'key', + 0 + ), + client.zAdd('key', [{ + value: '1', + score: 1 + }]) + ]); + + assert.deepEqual( + bzPopMinReply, + { + key: 'key', + value: '1', + score: 1 + } + ); + }); +}); diff --git a/lib/commands/BZPOPMIN.ts b/lib/commands/BZPOPMIN.ts new file mode 100644 index 00000000000..0c299cdb9df --- /dev/null +++ b/lib/commands/BZPOPMIN.ts @@ -0,0 +1,27 @@ +import { pushVerdictArguments, transformReplyNumberInfinity, ZMember } from './generic-transformers'; + +export const FIRST_KEY_INDEX = 1; + +export function transformArguments(key: string | Array, timeout: number): Array { + const args = pushVerdictArguments(['BZPOPMIN'], key); + + args.push(timeout.toString()); + + return args; +} + +interface ZMemberWithKey extends ZMember { + key: string; +} + +type BZPopMinReply = ZMemberWithKey | null; + +export function transformReply(reply: [key: string, value: string, score: string] | null): BZPopMinReply | null { + if (!reply) return null; + + return { + key: reply[0], + value: reply[1], + score: transformReplyNumberInfinity(reply[2]) + }; +} diff --git a/lib/commands/CLIENT_ID.spec.ts b/lib/commands/CLIENT_ID.spec.ts new file mode 100644 index 00000000000..cb7dfd9f730 --- /dev/null +++ b/lib/commands/CLIENT_ID.spec.ts @@ -0,0 +1,19 @@ +import { strict as assert } from 'assert'; +import { TestRedisServers, itWithClient } from '../test-utils'; +import { transformArguments } from './CLIENT_ID'; + +describe('CLIENT ID', () => { + it('transformArguments', () => { + assert.deepEqual( + transformArguments(), + ['CLIENT', 'ID'] + ); + }); + + itWithClient(TestRedisServers.OPEN, 'client.clientId', async client => { + assert.equal( + typeof (await client.clientId()), + 'number' + ); + }); +}); diff --git a/lib/commands/CLIENT_ID.ts b/lib/commands/CLIENT_ID.ts new file mode 100644 index 00000000000..baeab148eba --- /dev/null +++ b/lib/commands/CLIENT_ID.ts @@ -0,0 +1,9 @@ +import { transformReplyNumber } from './generic-transformers'; + +export const IS_READ_ONLY = true; + +export function transformArguments(): Array { + return ['CLIENT', 'ID']; +} + +export const transformReply = transformReplyNumber; diff --git a/lib/commands/CLIENT_INFO.spec.ts b/lib/commands/CLIENT_INFO.spec.ts new file mode 100644 index 00000000000..ee87df4a199 --- /dev/null +++ b/lib/commands/CLIENT_INFO.spec.ts @@ -0,0 +1,42 @@ +import { strict as assert } from 'assert'; +import { transformArguments, transformReply } from './CLIENT_INFO'; + +describe('CLIENT INFO', () => { + it('transformArguments', () => { + assert.deepEqual( + transformArguments(), + ['CLIENT', 'INFO'] + ); + }); + + it('transformReply', () => { + assert.deepEqual( + transformReply('id=526512 addr=127.0.0.1:36244 laddr=127.0.0.1:6379 fd=8 name= age=11213 idle=0 flags=N db=0 sub=0 psub=0 multi=-1 qbuf=26 qbuf-free=40928 argv-mem=10 obl=0 oll=0 omem=0 tot-mem=61466 events=r cmd=client user=default redir=-1\n'), + { + id: 526512, + addr: '127.0.0.1:36244', + laddr: '127.0.0.1:6379', + fd: 8, + name: '', + age: 11213, + idle: 0, + flags: 'N', + db: 0, + sub: 0, + psub: 0, + multi: -1, + qbuf: 26, + qbufFree: 40928, + argvMem: 10, + obl: 0, + oll: 0, + omem: 0, + totMem: 61466, + events: 'r', + cmd: 'client', + user: 'default', + redir: -1 + } + ); + }); +}); diff --git a/lib/commands/CLIENT_INFO.ts b/lib/commands/CLIENT_INFO.ts new file mode 100644 index 00000000000..8dd30b70590 --- /dev/null +++ b/lib/commands/CLIENT_INFO.ts @@ -0,0 +1,85 @@ +export function transformArguments(): Array { + return ['CLIENT', 'INFO']; +} + +interface ClientInfoReply { + id: number; + addr: string; + laddr: string; + fd: number; + name: string; + age: number; + idle: number; + flags: string; + db: number; + sub: number; + psub: number; + multi: number; + qbuf: number; + qbufFree: number; + argvMem: number; + obl: number; + oll: number; + omem: number; + totMem: number; + events: string; + cmd: string; + user: string; + redir: number; +} + +const REGEX = /=([^\s]*)/g; + +export function transformReply(reply: string): ClientInfoReply { + const [ + [, id], + [, addr], + [, laddr], + [, fd], + [, name], + [, age], + [, idle], + [, flags], + [, db], + [, sub], + [, psub], + [, multi], + [, qbuf], + [, qbufFree], + [, argvMem], + [, obl], + [, oll], + [, omem], + [, totMem], + [, events], + [, cmd], + [, user], + [, redir] + ] = [...reply.matchAll(REGEX)]; + + return { + id: Number(id), + addr, + laddr, + fd: Number(fd), + name, + age: Number(age), + idle: Number(idle), + flags, + db: Number(db), + sub: Number(sub), + psub: Number(psub), + multi: Number(multi), + qbuf: Number(qbuf), + qbufFree: Number(qbufFree), + argvMem: Number(argvMem), + obl: Number(obl), + oll: Number(oll), + omem: Number(omem), + totMem: Number(totMem), + events, + cmd, + user, + redir: Number(redir) + }; +} diff --git a/lib/commands/CLUSTER_ADDSLOTS.spec.ts b/lib/commands/CLUSTER_ADDSLOTS.spec.ts new file mode 100644 index 00000000000..c16476de436 --- /dev/null +++ b/lib/commands/CLUSTER_ADDSLOTS.spec.ts @@ -0,0 +1,20 @@ +import { strict as assert } from 'assert'; +import { transformArguments } from './CLUSTER_ADDSLOTS'; + +describe('CLUSTER ADDSLOTS', () => { + describe('transformArguments', () => { + it('single', () => { + assert.deepEqual( + transformArguments(0), + ['CLUSTER', 'ADDSLOTS', '0'] + ); + }); + + it('multiple', () => { + assert.deepEqual( + transformArguments([0, 1]), + ['CLUSTER', 'ADDSLOTS', '0', '1'] + ); + }); + }); +}); diff --git a/lib/commands/CLUSTER_ADDSLOTS.ts b/lib/commands/CLUSTER_ADDSLOTS.ts new file mode 100644 index 00000000000..594eae77c75 --- /dev/null +++ b/lib/commands/CLUSTER_ADDSLOTS.ts @@ -0,0 +1,15 @@ +import { transformReplyString } from './generic-transformers'; + +export function transformArguments(slots: number | Array): Array { + const args = ['CLUSTER', 'ADDSLOTS']; + + if (typeof slots === 'number') { + args.push(slots.toString()); + } else { + args.push(...slots.map(String)); + } + + return args; +} + +export const transformReply = transformReplyString; diff --git a/lib/commands/CLUSTER_FLUSHSLOTS.spec.ts b/lib/commands/CLUSTER_FLUSHSLOTS.spec.ts new file mode 100644 index 00000000000..f91a9a70cfd --- /dev/null +++ b/lib/commands/CLUSTER_FLUSHSLOTS.spec.ts @@ -0,0 +1,11 @@ +import { strict as assert } from 'assert'; +import { transformArguments } from './CLUSTER_FLUSHSLOTS'; + +describe('CLUSTER FLUSHSLOTS', () => { + it('transformArguments', () => { + assert.deepEqual( + transformArguments(), + ['CLUSTER', 'FLUSHSLOTS'] + ); + }); +}); diff --git a/lib/commands/CLUSTER_FLUSHSLOTS.ts b/lib/commands/CLUSTER_FLUSHSLOTS.ts new file mode 100644 index 00000000000..28fbcc1fab1 --- /dev/null +++ b/lib/commands/CLUSTER_FLUSHSLOTS.ts @@ -0,0 +1,7 @@ +import { transformReplyString } from './generic-transformers'; + +export function transformArguments(): Array { + return ['CLUSTER', 'FLUSHSLOTS']; +} + +export const transformReply = transformReplyString; diff --git a/lib/commands/CLUSTER_GETKEYSINSLOT.spec.ts b/lib/commands/CLUSTER_GETKEYSINSLOT.spec.ts new file mode 100644 index 00000000000..bb20f7521de --- /dev/null +++ b/lib/commands/CLUSTER_GETKEYSINSLOT.spec.ts @@ -0,0 +1,11 @@ +import { strict as assert } from 'assert'; +import { transformArguments } from './CLUSTER_GETKEYSINSLOT'; + +describe('CLUSTER GETKEYSINSLOT', () => { + it('transformArguments', () => { + assert.deepEqual( + transformArguments(0, 10), + ['CLUSTER', 'GETKEYSINSLOT', '0', '10'] + ); + }); +}); diff --git a/lib/commands/CLUSTER_GETKEYSINSLOT.ts b/lib/commands/CLUSTER_GETKEYSINSLOT.ts new file mode 100644 index 00000000000..c5719848cf5 --- /dev/null +++ b/lib/commands/CLUSTER_GETKEYSINSLOT.ts @@ -0,0 +1,7 @@ +import { transformReplyString } from './generic-transformers'; + +export function transformArguments(slot: number, count: number): Array { + return ['CLUSTER', 'GETKEYSINSLOT', slot.toString(), count.toString()]; +} + +export const transformReply = transformReplyString; diff --git a/lib/commands/CLUSTER_INFO.spec.ts b/lib/commands/CLUSTER_INFO.spec.ts new file mode 100644 index 00000000000..ce41151b67f --- /dev/null +++ b/lib/commands/CLUSTER_INFO.spec.ts @@ -0,0 +1,64 @@ +import { strict as assert } from 'assert'; +import { itWithCluster, TestRedisClusters } from '../test-utils'; +import { transformArguments, transformReply } from './CLUSTER_INFO'; + +describe('CLUSTER INFO', () => { + it('transformArguments', () => { + assert.deepEqual( + transformArguments(), + ['CLUSTER', 'INFO'] + ); + }); + + it('transformReply', () => { + assert.deepEqual( + transformReply([ + 'cluster_state:ok', + 'cluster_slots_assigned:16384', + 'cluster_slots_ok:16384', + 'cluster_slots_pfail:0', + 'cluster_slots_fail:0', + 'cluster_known_nodes:6', + 'cluster_size:3', + 'cluster_current_epoch:6', + 'cluster_my_epoch:2', + 'cluster_stats_messages_sent:1483972', + 'cluster_stats_messages_received:1483968' + ].join('\r\n')), + { + state: 'ok', + slots: { + assigned: 16384, + ok: 16384, + pfail: 0, + fail: 0 + }, + knownNodes: 6, + size: 3, + currentEpoch: 6, + myEpoch: 2, + stats: { + messagesSent: 1483972, + messagesReceived: 1483968 + } + } + ); + }); + + itWithCluster(TestRedisClusters.OPEN, 'cluster.clusterInfo', async cluster => { + const info = await cluster.clusterInfo(); + assert.equal(info.state, 'ok'); + assert.deepEqual(info.slots, { + assigned: 16384, + ok: 16384, + pfail: 0, + fail: 0 + }); + assert.equal(info.knownNodes, 3); + assert.equal(info.size, 3); + assert.equal(typeof info.currentEpoch, 'number'); + assert.equal(typeof info.myEpoch, 'number'); + assert.equal(typeof info.stats.messagesReceived, 'number'); + assert.equal(typeof info.stats.messagesSent, 'number'); + }); +}); diff --git a/lib/commands/CLUSTER_INFO.ts b/lib/commands/CLUSTER_INFO.ts new file mode 100644 index 00000000000..634515f927c --- /dev/null +++ b/lib/commands/CLUSTER_INFO.ts @@ -0,0 +1,47 @@ +export function transformArguments(): Array { + return ['CLUSTER', 'INFO']; +} + +interface ClusterInfoReply { + state: string; + slots: { + assigned: number; + ok: number; + pfail: number; + fail: number; + }; + knownNodes: number; + size: number; + currentEpoch: number; + myEpoch: number; + stats: { + messagesSent: number; + messagesReceived: number; + }; +} + +export function transformReply(reply: string): ClusterInfoReply { + const lines = reply.split('\r\n'); + + return { + state: extractLineValue(lines[0]), + slots: { + assigned: Number(extractLineValue(lines[1])), + ok: Number(extractLineValue(lines[2])), + pfail: Number(extractLineValue(lines[3])), + fail: Number(extractLineValue(lines[4])) + }, + knownNodes: Number(extractLineValue(lines[5])), + size: Number(extractLineValue(lines[6])), + currentEpoch: Number(extractLineValue(lines[7])), + myEpoch: Number(extractLineValue(lines[8])), + stats: { + messagesSent: Number(extractLineValue(lines[9])), + messagesReceived: Number(extractLineValue(lines[10])) + } + }; +} + +export function extractLineValue(line: string): string { + return line.substring(line.indexOf(':') + 1); +} diff --git a/lib/commands/CLUSTER_MEET.spec.ts b/lib/commands/CLUSTER_MEET.spec.ts new file mode 100644 index 00000000000..50a5393efa2 --- /dev/null +++ b/lib/commands/CLUSTER_MEET.spec.ts @@ -0,0 +1,11 @@ +import { strict as assert } from 'assert'; +import { transformArguments } from './CLUSTER_MEET'; + +describe('CLUSTER MEET', () => { + it('transformArguments', () => { + assert.deepEqual( + transformArguments('127.0.0.1', 6379), + ['CLUSTER', 'MEET', '127.0.0.1', '6379'] + ); + }); +}); diff --git a/lib/commands/CLUSTER_MEET.ts b/lib/commands/CLUSTER_MEET.ts new file mode 100644 index 00000000000..19da150356c --- /dev/null +++ b/lib/commands/CLUSTER_MEET.ts @@ -0,0 +1,7 @@ +import { transformReplyString } from './generic-transformers'; + +export function transformArguments(ip: string, port: number): Array { + return ['CLUSTER', 'MEET', ip, port.toString()]; +} + +export const transformReply = transformReplyString; diff --git a/lib/commands/CLUSTER_NODES.spec.ts b/lib/commands/CLUSTER_NODES.spec.ts new file mode 100644 index 00000000000..1f0e9dd425a --- /dev/null +++ b/lib/commands/CLUSTER_NODES.spec.ts @@ -0,0 +1,116 @@ +import { strict as assert } from 'assert'; +import { itWithCluster, TestRedisClusters } from '../test-utils'; +import { RedisClusterNodeLinkStates, transformArguments, transformReply } from './CLUSTER_NODES'; + +describe('CLUSTER NODES', () => { + it('transformArguments', () => { + assert.deepEqual( + transformArguments(), + ['CLUSTER', 'NODES'] + ); + }); + + describe('transformReply', () => { + it('simple', () => { + assert.deepEqual( + transformReply([ + 'master 127.0.0.1:30001@31001 myself,master - 0 0 1 connected 0-16384', + 'slave 127.0.0.1:30002@31002 slave master 0 0 1 connected', + '' + ].join('\n')), + [{ + id: 'master', + url: '127.0.0.1:30001@31001', + host: '127.0.0.1', + port: 30001, + cport: 31001, + flags: ['myself', 'master'], + pingSent: 0, + pongRecv: 0, + configEpoch: 1, + linkState: RedisClusterNodeLinkStates.CONNECTED, + slots: [{ + from: 0, + to: 16384 + }], + replicas: [{ + id: 'slave', + url: '127.0.0.1:30002@31002', + host: '127.0.0.1', + port: 30002, + cport: 31002, + flags: ['slave'], + pingSent: 0, + pongRecv: 0, + configEpoch: 1, + linkState: RedisClusterNodeLinkStates.CONNECTED + }] + }] + ); + }); + + it.skip('with importing slots', () => { + assert.deepEqual( + transformReply( + 'id 127.0.0.1:30001@31001 master - 0 0 0 connected 0-<-16384\n' + ), + [{ + id: 'id', + url: '127.0.0.1:30001@31001', + host: '127.0.0.1', + port: 30001, + cport: 31001, + flags: ['master'], + pingSent: 0, + pongRecv: 0, + configEpoch: 0, + linkState: RedisClusterNodeLinkStates.CONNECTED, + slots: [], // TODO + replicas: [] + }] + ); + }); + + it.skip('with migrating slots', () => { + assert.deepEqual( + transformReply( + 'id 127.0.0.1:30001@31001 master - 0 0 0 connected 0->-16384\n' + ), + [{ + id: 'id', + url: '127.0.0.1:30001@31001', + host: '127.0.0.1', + port: 30001, + cport: 31001, + flags: ['master'], + pingSent: 0, + pongRecv: 0, + configEpoch: 0, + linkState: RedisClusterNodeLinkStates.CONNECTED, + slots: [], // TODO + replicas: [] + }] + ); + }); + }); + + itWithCluster(TestRedisClusters.OPEN, 'cluster.clusterNodes', async cluster => { + for (const node of (await cluster.clusterNodes())) { + assert.equal(typeof node.id, 'string'); + assert.equal(typeof node.url, 'string'); + assert.equal(typeof node.host, 'string'); + assert.equal(typeof node.port, 'number'); + assert.equal(typeof node.cport, 'number'); + assert.ok(Array.isArray(node.flags)); + assert.equal(typeof node.pingSent, 'number'); + assert.equal(typeof node.pongRecv, 'number'); + assert.equal(typeof node.configEpoch, 'number'); + assert.equal(typeof node.linkState, 'string'); + + for (const slot of node.slots) { + assert.equal(typeof slot.from, 'number'); + assert.equal(typeof slot.to, 'number'); + } + } + }); +}); diff --git a/lib/commands/CLUSTER_NODES.ts b/lib/commands/CLUSTER_NODES.ts new file mode 100644 index 00000000000..d04ffc10a1d --- /dev/null +++ b/lib/commands/CLUSTER_NODES.ts @@ -0,0 +1,96 @@ +export function transformArguments(): Array { + return ['CLUSTER', 'NODES']; +} + +export enum RedisClusterNodeLinkStates { + CONNECTED = 'connected', + DISCONNECTED = 'disconnected' +} + +interface RedisClusterNodeTransformedUrl { + host: string; + port: number; + cport: number; +} + +export interface RedisClusterReplicaNode extends RedisClusterNodeTransformedUrl { + id: string; + url: string; + flags: Array; + pingSent: number; + pongRecv: number; + configEpoch: number; + linkState: RedisClusterNodeLinkStates; +} + +export interface RedisClusterMasterNode extends RedisClusterReplicaNode { + slots: Array<{ + from: number; + to: number; + }>; + replicas: Array; +} + +export function transformReply(reply: string): Array { + const lines = reply.split('\n'); + lines.pop(); // last line is empty + + const mastersMap = new Map(), + replicasMap = new Map>(); + + for (const line of lines) { + const [id, url, flags, masterId, pingSent, pongRecv, configEpoch, linkState, ...slots] = line.split(' '), + node = { + id, + url, + ...transformNodeUrl(url), + flags: flags.split(','), + pingSent: Number(pingSent), + pongRecv: Number(pongRecv), + configEpoch: Number(configEpoch), + linkState: (linkState as RedisClusterNodeLinkStates) + }; + + if (masterId === '-') { + let replicas = replicasMap.get(id); + if (!replicas) { + replicas = []; + replicasMap.set(id, replicas); + } + + mastersMap.set(id, { + ...node, + slots: slots.map(slot => { + // TODO: importing & exporting (https://redis.io/commands/cluster-nodes#special-slot-entries) + const [fromString, toString] = slot.split('-', 2), + from = Number(fromString); + return { + from, + to: toString ? Number(toString) : from + }; + }), + replicas + }); + } else { + const replicas = replicasMap.get(masterId); + if (!replicas) { + replicasMap.set(masterId, [node]); + } else { + replicas.push(node); + } + } + } + + return [...mastersMap.values()]; +} + +function transformNodeUrl(url: string): RedisClusterNodeTransformedUrl { + const indexOfColon = url.indexOf(':'), + indexOfAt = url.indexOf('@', indexOfColon); + + return { + host: url.substring(0, indexOfColon), + port: Number(url.substring(indexOfColon + 1, indexOfAt)), + cport: Number(url.substring(indexOfAt + 1)) + }; +} diff --git a/lib/commands/CLUSTER_RESET.spec.ts b/lib/commands/CLUSTER_RESET.spec.ts new file mode 100644 index 00000000000..c077e7f8874 --- /dev/null +++ b/lib/commands/CLUSTER_RESET.spec.ts @@ -0,0 +1,27 @@ +import { strict as assert } from 'assert'; +import { transformArguments } from './CLUSTER_RESET'; + +describe('CLUSTER RESET', () => { + describe('transformArguments', () => { + it('simple', () => { + assert.deepEqual( + transformArguments(), + ['CLUSTER', 'RESET'] + ); + }); + + it('HARD', () => { + assert.deepEqual( + transformArguments('HARD'), + ['CLUSTER', 'RESET', 'HARD'] + ); + }); + + it('SOFT', () => { + assert.deepEqual( + transformArguments('SOFT'), + ['CLUSTER', 'RESET', 'SOFT'] + ); + }); + }); +}); diff --git a/lib/commands/CLUSTER_RESET.ts b/lib/commands/CLUSTER_RESET.ts new file mode 100644 index 00000000000..ec27b45eeb7 --- /dev/null +++ b/lib/commands/CLUSTER_RESET.ts @@ -0,0 +1,15 @@ +import { transformReplyString } from './generic-transformers'; + +export type ClusterResetModes = 'HARD' | 'SOFT'; + +export function transformArguments(mode?: ClusterResetModes): Array { + const args = ['CLUSTER', 'RESET']; + + if (mode) { + args.push(mode); + } + + return args; +} + +export const transformReply = transformReplyString; diff --git a/lib/commands/CLUSTER_SETSLOT.spec.ts b/lib/commands/CLUSTER_SETSLOT.spec.ts new file mode 100644 index 00000000000..0f46aafd13e --- /dev/null +++ b/lib/commands/CLUSTER_SETSLOT.spec.ts @@ -0,0 +1,20 @@ +import { strict as assert } from 'assert'; +import { ClusterSlotStates, transformArguments } from './CLUSTER_SETSLOT'; + +describe('CLUSTER SETSLOT', () => { + describe('transformArguments', () => { + it('simple', () => { + assert.deepEqual( + transformArguments(0, ClusterSlotStates.IMPORTING), + ['CLUSTER', 'SETSLOT', '0', 'IMPORTING'] + ); + }); + + it('with nodeId', () => { + assert.deepEqual( + transformArguments(0, ClusterSlotStates.IMPORTING, 'nodeId'), + ['CLUSTER', 'SETSLOT', '0', 'IMPORTING', 'nodeId'] + ); + }); + }); +}); diff --git a/lib/commands/CLUSTER_SETSLOT.ts b/lib/commands/CLUSTER_SETSLOT.ts new file mode 100644 index 00000000000..c665b349622 --- /dev/null +++ b/lib/commands/CLUSTER_SETSLOT.ts @@ -0,0 +1,20 @@ +import { transformReplyString } from './generic-transformers'; + +export enum ClusterSlotStates { + IMPORTING = 'IMPORTING', + MIGRATING = 'MIGRATING', + STABLE = 'STABLE', + NODE = 'NODE' +} + +export function transformArguments(slot: number, state: ClusterSlotStates, nodeId?: string): Array { + const args = ['CLUSTER', 'SETSLOT', slot.toString(), state]; + + if (nodeId) { + args.push(nodeId); + } + + return args; +} + +export const transformReply = transformReplyString; diff --git a/lib/commands/CONFIG_GET.spec.ts b/lib/commands/CONFIG_GET.spec.ts new file mode 100644 index 00000000000..83b5c410cfb --- /dev/null +++ b/lib/commands/CONFIG_GET.spec.ts @@ -0,0 +1,11 @@ +import { strict as assert } from 'assert'; +import { transformArguments } from './CONFIG_GET'; + +describe('CONFIG GET', () => { + it('transformArguments', () => { + assert.deepEqual( + transformArguments('*'), + ['CONFIG', 'GET', '*'] + ); + }); +}); diff --git a/lib/commands/CONFIG_GET.ts b/lib/commands/CONFIG_GET.ts new file mode 100644 index 00000000000..423683c13a4 --- /dev/null +++ b/lib/commands/CONFIG_GET.ts @@ -0,0 +1,7 @@ +import { transformReplyTuples } from './generic-transformers'; + +export function transformArguments(parameter: string): Array { + return ['CONFIG', 'GET', parameter]; +} + +export const transformReply = transformReplyTuples; diff --git a/lib/commands/CONFIG_RESETSTAT.spec.ts b/lib/commands/CONFIG_RESETSTAT.spec.ts new file mode 100644 index 00000000000..d3f3048b944 --- /dev/null +++ b/lib/commands/CONFIG_RESETSTAT.spec.ts @@ -0,0 +1,11 @@ +import { strict as assert } from 'assert'; +import { transformArguments } from './CONFIG_RESETSTAT'; + +describe('CONFIG RESETSTAT', () => { + it('transformArguments', () => { + assert.deepEqual( + transformArguments(), + ['CONFIG', 'RESETSTAT'] + ); + }); +}); diff --git a/lib/commands/CONFIG_RESETSTAT.ts b/lib/commands/CONFIG_RESETSTAT.ts new file mode 100644 index 00000000000..3c87b08d88b --- /dev/null +++ b/lib/commands/CONFIG_RESETSTAT.ts @@ -0,0 +1,7 @@ +import { transformReplyString } from './generic-transformers'; + +export function transformArguments(): Array { + return ['CONFIG', 'RESETSTAT']; +} + +export const transformReply = transformReplyString; diff --git a/lib/commands/CONFIG_REWRITE.spec.ts b/lib/commands/CONFIG_REWRITE.spec.ts new file mode 100644 index 00000000000..cbc3e5b59d8 --- /dev/null +++ b/lib/commands/CONFIG_REWRITE.spec.ts @@ -0,0 +1,11 @@ +import { strict as assert } from 'assert'; +import { transformArguments } from './CONFIG_REWRITE'; + +describe('CONFIG REWRITE', () => { + it('transformArguments', () => { + assert.deepEqual( + transformArguments(), + ['CONFIG', 'REWRITE'] + ); + }); +}); diff --git a/lib/commands/CONFIG_REWRITE.ts b/lib/commands/CONFIG_REWRITE.ts new file mode 100644 index 00000000000..06247517128 --- /dev/null +++ b/lib/commands/CONFIG_REWRITE.ts @@ -0,0 +1,7 @@ +import { transformReplyString } from './generic-transformers'; + +export function transformArguments(): Array { + return ['CONFIG', 'REWRITE']; +} + +export const transformReply = transformReplyString; diff --git a/lib/commands/CONFIG_SET.spec.ts b/lib/commands/CONFIG_SET.spec.ts new file mode 100644 index 00000000000..3127e9732bf --- /dev/null +++ b/lib/commands/CONFIG_SET.spec.ts @@ -0,0 +1,11 @@ +import { strict as assert } from 'assert'; +import { transformArguments } from './CONFIG_SET'; + +describe('CONFIG SET', () => { + it('transformArguments', () => { + assert.deepEqual( + transformArguments('parameter', 'value'), + ['CONFIG', 'SET', 'parameter', 'value'] + ); + }); +}); diff --git a/lib/commands/CONFIG_SET.ts b/lib/commands/CONFIG_SET.ts new file mode 100644 index 00000000000..894a95cb1cc --- /dev/null +++ b/lib/commands/CONFIG_SET.ts @@ -0,0 +1,7 @@ +import { transformReplyString } from './generic-transformers'; + +export function transformArguments(parameter: string, value: string): Array { + return ['CONFIG', 'SET', parameter, value]; +} + +export const transformReply = transformReplyString; diff --git a/lib/commands/COPY.spec.ts b/lib/commands/COPY.spec.ts new file mode 100644 index 00000000000..fb35be863ab --- /dev/null +++ b/lib/commands/COPY.spec.ts @@ -0,0 +1,67 @@ +import { strict as assert } from 'assert'; +import { TestRedisServers, itWithClient, describeHandleMinimumRedisVersion } from '../test-utils'; +import { transformArguments, transformReply } from './COPY'; + +describe('COPY', () => { + describeHandleMinimumRedisVersion([6, 2]); + + describe('transformArguments', () => { + it('simple', () => { + assert.deepEqual( + transformArguments('source', 'destination'), + ['COPY', 'source', 'destination'] + ); + }); + + it('with destination DB flag', () => { + assert.deepEqual( + transformArguments('source', 'destination', { + destinationDb: 1 + }), + ['COPY', 'source', 'destination', 'DB', '1'] + ); + }); + + it('with replace flag', () => { + assert.deepEqual( + transformArguments('source', 'destination', { + replace: true + }), + ['COPY', 'source', 'destination', 'REPLACE'] + ); + }); + + it('with both flags', () => { + assert.deepEqual( + transformArguments('source', 'destination', { + destinationDb: 1, + replace: true + }), + ['COPY', 'source', 'destination', 'DB', '1', 'REPLACE'] + ); + }); + }); + + describe('transformReply', () => { + it('0', () => { + assert.equal( + transformReply(0), + false + ); + }); + + it('1', () => { + assert.equal( + transformReply(1), + true + ); + }); + }); + + itWithClient(TestRedisServers.OPEN, 'client.copy', async client => { + assert.equal( + await client.copy('source', 'destination'), + false + ); + }); +}); diff --git a/lib/commands/COPY.ts b/lib/commands/COPY.ts new file mode 100644 index 00000000000..534b0d9c48c --- /dev/null +++ b/lib/commands/COPY.ts @@ -0,0 +1,24 @@ +import { transformReplyBoolean } from './generic-transformers'; + +interface CopyCommandOptions { + destinationDb?: number; + replace?: boolean; +} + +export const FIRST_KEY_INDEX = 1; + +export function transformArguments(source: string, destination: string, options?: CopyCommandOptions): Array { + const args = ['COPY', source, destination]; + + if (options?.destinationDb) { + args.push('DB', options.destinationDb.toString()); + } + + if (options?.replace) { + args.push('REPLACE'); + } + + return args; +} + +export const transformReply = transformReplyBoolean; diff --git a/lib/commands/DBSIZE.spec.ts b/lib/commands/DBSIZE.spec.ts new file mode 100644 index 00000000000..87e3c154534 --- /dev/null +++ b/lib/commands/DBSIZE.spec.ts @@ -0,0 +1,26 @@ +import { strict as assert } from 'assert'; +import { TestRedisServers, itWithClient, TestRedisClusters, itWithCluster } from '../test-utils'; +import { transformArguments } from './DBSIZE'; + +describe('DBSIZE', () => { + it('transformArguments', () => { + assert.deepEqual( + transformArguments(), + ['DBSIZE'] + ); + }); + + itWithClient(TestRedisServers.OPEN, 'client.dbSize', async client => { + assert.equal( + await client.dbSize(), + 0 + ); + }); + + itWithCluster(TestRedisClusters.OPEN, 'cluster.dbSize', async cluster => { + assert.equal( + await cluster.dbSize(), + 0 + ); + }); +}); diff --git a/lib/commands/DBSIZE.ts b/lib/commands/DBSIZE.ts new file mode 100644 index 00000000000..72933930f70 --- /dev/null +++ b/lib/commands/DBSIZE.ts @@ -0,0 +1,9 @@ +import { transformReplyNumber } from './generic-transformers'; + +export const IS_READ_ONLY = true; + +export function transformArguments(): Array { + return ['DBSIZE']; +} + +export const transformReply = transformReplyNumber; diff --git a/lib/commands/DECR.spec.ts b/lib/commands/DECR.spec.ts new file mode 100644 index 00000000000..5b4b4f0fd33 --- /dev/null +++ b/lib/commands/DECR.spec.ts @@ -0,0 +1,19 @@ +import { strict as assert } from 'assert'; +import { TestRedisServers, itWithClient } from '../test-utils'; +import { transformArguments } from './DECR'; + +describe('DECR', () => { + it('transformArguments', () => { + assert.deepEqual( + transformArguments('key'), + ['DECR', 'key'] + ); + }); + + itWithClient(TestRedisServers.OPEN, 'client.decr', async client => { + assert.equal( + await client.decr('key'), + -1 + ); + }); +}); diff --git a/lib/commands/DECR.ts b/lib/commands/DECR.ts new file mode 100644 index 00000000000..cac6e07f053 --- /dev/null +++ b/lib/commands/DECR.ts @@ -0,0 +1,9 @@ +import { transformReplyNumber } from './generic-transformers'; + +export const FIRST_KEY_INDEX = 1; + +export function transformArguments(key: string): Array { + return ['DECR', key]; +} + +export const transformReply = transformReplyNumber; diff --git a/lib/commands/DECRBY.spec.ts b/lib/commands/DECRBY.spec.ts new file mode 100644 index 00000000000..1c9ac69bb96 --- /dev/null +++ b/lib/commands/DECRBY.spec.ts @@ -0,0 +1,19 @@ +import { strict as assert } from 'assert'; +import { TestRedisServers, itWithClient } from '../test-utils'; +import { transformArguments } from './DECRBY'; + +describe('DECRBY', () => { + it('transformArguments', () => { + assert.deepEqual( + transformArguments('key', 2), + ['DECRBY', 'key', '2'] + ); + }); + + itWithClient(TestRedisServers.OPEN, 'client.decrBy', async client => { + assert.equal( + await client.decrBy('key', 2), + -2 + ); + }); +}); diff --git a/lib/commands/DECRBY.ts b/lib/commands/DECRBY.ts new file mode 100644 index 00000000000..cc163cbe824 --- /dev/null +++ b/lib/commands/DECRBY.ts @@ -0,0 +1,9 @@ +import { transformReplyNumber } from './generic-transformers'; + +export const FIRST_KEY_INDEX = 1; + +export function transformArguments(key: string, decrement: number): Array { + return ['DECRBY', key, decrement.toString()]; +} + +export const transformReply = transformReplyNumber; diff --git a/lib/commands/DEL.spec.ts b/lib/commands/DEL.spec.ts new file mode 100644 index 00000000000..ec780de67a0 --- /dev/null +++ b/lib/commands/DEL.spec.ts @@ -0,0 +1,28 @@ +import { strict as assert } from 'assert'; +import { TestRedisServers, itWithClient } from '../test-utils'; +import { transformArguments } from './DEL'; + +describe('DEL', () => { + describe('transformArguments', () => { + it('string', () => { + assert.deepEqual( + transformArguments('key'), + ['DEL', 'key'] + ); + }); + + it('array', () => { + assert.deepEqual( + transformArguments(['key1', 'key2']), + ['DEL', 'key1', 'key2'] + ); + }); + }); + + itWithClient(TestRedisServers.OPEN, 'client.del', async client => { + assert.equal( + await client.del('key'), + 0 + ); + }); +}); diff --git a/lib/commands/DEL.ts b/lib/commands/DEL.ts new file mode 100644 index 00000000000..3d9a78212f8 --- /dev/null +++ b/lib/commands/DEL.ts @@ -0,0 +1,7 @@ +import { pushVerdictArguments, transformReplyNumber } from './generic-transformers'; + +export function transformArguments(keys: string | Array): Array { + return pushVerdictArguments(['DEL'], keys); +} + +export const transformReply = transformReplyNumber; diff --git a/lib/commands/DISCARD.spec.ts b/lib/commands/DISCARD.spec.ts new file mode 100644 index 00000000000..b01f9d650d9 --- /dev/null +++ b/lib/commands/DISCARD.spec.ts @@ -0,0 +1,11 @@ +import { strict as assert } from 'assert'; +import { transformArguments } from './DISCARD'; + +describe('DISCARD', () => { + it('transformArguments', () => { + assert.deepEqual( + transformArguments(), + ['DISCARD'] + ); + }); +}); diff --git a/lib/commands/DISCARD.ts b/lib/commands/DISCARD.ts new file mode 100644 index 00000000000..b5aaf45cc8d --- /dev/null +++ b/lib/commands/DISCARD.ts @@ -0,0 +1,7 @@ +import { transformReplyString } from './generic-transformers'; + +export function transformArguments(): Array { + return ['DISCARD']; +} + +export const transformReply = transformReplyString; diff --git a/lib/commands/DUMP.spec.ts b/lib/commands/DUMP.spec.ts new file mode 100644 index 00000000000..e3f42c57578 --- /dev/null +++ b/lib/commands/DUMP.spec.ts @@ -0,0 +1,11 @@ +import { strict as assert } from 'assert'; +import { TestRedisServers, itWithClient } from '../test-utils'; + +describe('DUMP', () => { + itWithClient(TestRedisServers.OPEN, 'client.dump', async client => { + assert.equal( + await client.dump('key'), + null + ); + }); +}); diff --git a/lib/commands/DUMP.ts b/lib/commands/DUMP.ts new file mode 100644 index 00000000000..1c72110f21f --- /dev/null +++ b/lib/commands/DUMP.ts @@ -0,0 +1,7 @@ +import { transformReplyString } from './generic-transformers'; + +export function transformArguments(key: string): Array { + return ['DUMP', key]; +} + +export const transformReply = transformReplyString; diff --git a/lib/commands/ECHO.spec.ts b/lib/commands/ECHO.spec.ts new file mode 100644 index 00000000000..4a1bf8fe378 --- /dev/null +++ b/lib/commands/ECHO.spec.ts @@ -0,0 +1,26 @@ +import { strict as assert } from 'assert'; +import { TestRedisServers, itWithClient, TestRedisClusters, itWithCluster } from '../test-utils'; +import { transformArguments } from './ECHO'; + +describe('ECHO', () => { + it('transformArguments', () => { + assert.deepEqual( + transformArguments('message'), + ['ECHO', 'message'] + ); + }); + + itWithClient(TestRedisServers.OPEN, 'client.echo', async client => { + assert.equal( + await client.echo('message'), + 'message' + ); + }); + + itWithCluster(TestRedisClusters.OPEN, 'cluster.echo', async cluster => { + assert.equal( + await cluster.echo('message'), + 'message' + ); + }); +}); diff --git a/lib/commands/ECHO.ts b/lib/commands/ECHO.ts new file mode 100644 index 00000000000..007b8f27646 --- /dev/null +++ b/lib/commands/ECHO.ts @@ -0,0 +1,9 @@ +import { transformReplyString } from './generic-transformers'; + +export const IS_READ_ONLY = true; + +export function transformArguments(message: string): Array { + return ['ECHO', message]; +} + +export const transformReply = transformReplyString; diff --git a/lib/commands/EVAL.spec.ts b/lib/commands/EVAL.spec.ts new file mode 100644 index 00000000000..2be1aedf08a --- /dev/null +++ b/lib/commands/EVAL.spec.ts @@ -0,0 +1,29 @@ +import { strict as assert } from 'assert'; +import { TestRedisServers, itWithClient, TestRedisClusters, itWithCluster } from '../test-utils'; +import { transformArguments } from './EVAL'; + +describe('EVAL', () => { + it('transformArguments', () => { + assert.deepEqual( + transformArguments('return KEYS[1] + ARGV[1]', { + keys: ['key'], + arguments: ['argument'] + }), + ['EVAL', 'return KEYS[1] + ARGV[1]', '1', 'key', 'argument'] + ); + }); + + itWithClient(TestRedisServers.OPEN, 'client.eval', async client => { + assert.equal( + await client.eval('return 1'), + 1 + ); + }); + + itWithCluster(TestRedisClusters.OPEN, 'cluster.eval', async cluster => { + assert.equal( + await cluster.eval('return 1'), + 1 + ); + }); +}); diff --git a/lib/commands/EVAL.ts b/lib/commands/EVAL.ts new file mode 100644 index 00000000000..89645df9f3e --- /dev/null +++ b/lib/commands/EVAL.ts @@ -0,0 +1,9 @@ +import { EvalOptions, pushEvalArguments } from './generic-transformers'; + +export function transformArguments(script: string, options?: EvalOptions): Array { + return pushEvalArguments(['EVAL', script], options); +} + +export function transformReply(reply: unknown): unknown { + return reply; +} diff --git a/lib/commands/EVALSHA.spec.ts b/lib/commands/EVALSHA.spec.ts new file mode 100644 index 00000000000..08b330ac4f5 --- /dev/null +++ b/lib/commands/EVALSHA.spec.ts @@ -0,0 +1,14 @@ +import { strict as assert } from 'assert'; +import { transformArguments } from './EVALSHA'; + +describe('EVALSHA', () => { + it('transformArguments', () => { + assert.deepEqual( + transformArguments('sha1', { + keys: ['key'], + arguments: ['argument'] + }), + ['EVALSHA', 'sha1', '1', 'key', 'argument'] + ); + }); +}); diff --git a/lib/commands/EVALSHA.ts b/lib/commands/EVALSHA.ts new file mode 100644 index 00000000000..a81595bc4c0 --- /dev/null +++ b/lib/commands/EVALSHA.ts @@ -0,0 +1,9 @@ +import { EvalOptions, pushEvalArguments } from './generic-transformers'; + +export function transformArguments(sha1: string, options?: EvalOptions): Array { + return pushEvalArguments(['EVALSHA', sha1], options); +} + +export function transformReply(reply: unknown): unknown { + return reply; +} diff --git a/lib/commands/EXISTS.spec.ts b/lib/commands/EXISTS.spec.ts new file mode 100644 index 00000000000..3cba44b563f --- /dev/null +++ b/lib/commands/EXISTS.spec.ts @@ -0,0 +1,28 @@ +import { strict as assert } from 'assert'; +import { TestRedisServers, itWithClient } from '../test-utils'; +import { transformArguments } from './EXISTS'; + +describe('EXISTS', () => { + describe('transformArguments', () => { + it('string', () => { + assert.deepEqual( + transformArguments('key'), + ['EXISTS', 'key'] + ); + }); + + it('array', () => { + assert.deepEqual( + transformArguments(['1', '2']), + ['EXISTS', '1', '2'] + ); + }); + }); + + itWithClient(TestRedisServers.OPEN, 'client.exists', async client => { + assert.equal( + await client.exists('key'), + false + ); + }); +}); diff --git a/lib/commands/EXISTS.ts b/lib/commands/EXISTS.ts new file mode 100644 index 00000000000..5a76ca833fb --- /dev/null +++ b/lib/commands/EXISTS.ts @@ -0,0 +1,11 @@ +import { pushVerdictArguments, transformReplyBoolean } from './generic-transformers'; + +export const FIRST_KEY_INDEX = 1; + +export const IS_READ_ONLY = true; + +export function transformArguments(keys: string | Array): Array { + return pushVerdictArguments(['EXISTS'], keys); +} + +export const transformReply = transformReplyBoolean; diff --git a/lib/commands/EXPIRE.spec.ts b/lib/commands/EXPIRE.spec.ts new file mode 100644 index 00000000000..6550532cab2 --- /dev/null +++ b/lib/commands/EXPIRE.spec.ts @@ -0,0 +1,19 @@ +import { strict as assert } from 'assert'; +import { TestRedisServers, itWithClient } from '../test-utils'; +import { transformArguments } from './EXPIRE'; + +describe('EXPIRE', () => { + it('transformArguments', () => { + assert.deepEqual( + transformArguments('key', 1), + ['EXPIRE', 'key', '1'] + ); + }); + + itWithClient(TestRedisServers.OPEN, 'client.expire', async client => { + assert.equal( + await client.expire('key', 0), + false + ); + }); +}); diff --git a/lib/commands/EXPIRE.ts b/lib/commands/EXPIRE.ts new file mode 100644 index 00000000000..04b0504a6f7 --- /dev/null +++ b/lib/commands/EXPIRE.ts @@ -0,0 +1,7 @@ +import { transformReplyBoolean } from './generic-transformers'; + +export function transformArguments(key: string, seconds: number): Array { + return ['EXPIRE', key, seconds.toString()]; +} + +export const transformReply = transformReplyBoolean; diff --git a/lib/commands/EXPIREAT.spec.ts b/lib/commands/EXPIREAT.spec.ts new file mode 100644 index 00000000000..cefe9fa9b8b --- /dev/null +++ b/lib/commands/EXPIREAT.spec.ts @@ -0,0 +1,29 @@ +import { strict as assert } from 'assert'; +import { TestRedisServers, itWithClient } from '../test-utils'; +import { transformArguments } from './EXPIREAT'; + +describe('EXPIREAT', () => { + describe('transformArguments', () => { + it('number', () => { + assert.deepEqual( + transformArguments('key', 1), + ['EXPIREAT', 'key', '1'] + ); + }); + + it('date', () => { + const d = new Date(); + assert.deepEqual( + transformArguments('key', d), + ['EXPIREAT', 'key', Math.floor(d.getTime() / 1000).toString()] + ); + }); + }); + + itWithClient(TestRedisServers.OPEN, 'client.expireAt', async client => { + assert.equal( + await client.expireAt('key', 1), + false + ); + }); +}); diff --git a/lib/commands/EXPIREAT.ts b/lib/commands/EXPIREAT.ts new file mode 100644 index 00000000000..b7bfdcaa422 --- /dev/null +++ b/lib/commands/EXPIREAT.ts @@ -0,0 +1,11 @@ +import { transformEXAT, transformReplyBoolean } from './generic-transformers'; + +export function transformArguments(key: string, timestamp: number | Date): Array { + return [ + 'EXPIREAT', + key, + transformEXAT(timestamp) + ]; +} + +export const transformReply = transformReplyBoolean; diff --git a/lib/commands/FAILOVER.spec.ts b/lib/commands/FAILOVER.spec.ts new file mode 100644 index 00000000000..16094a0dbc3 --- /dev/null +++ b/lib/commands/FAILOVER.spec.ts @@ -0,0 +1,72 @@ +import { strict as assert } from 'assert'; +import { transformArguments } from './FAILOVER'; + +describe('FAILOVER', () => { + describe('transformArguments', () => { + it('simple', () => { + assert.deepEqual( + transformArguments(), + ['FAILOVER'] + ); + }); + + describe('with TO', () => { + it('simple', () => { + assert.deepEqual( + transformArguments({ + TO: { + host: 'host', + port: 6379 + } + }), + ['FAILOVER', 'TO', 'host', '6379'] + ); + }); + + it('with FORCE', () => { + assert.deepEqual( + transformArguments({ + TO: { + host: 'host', + port: 6379, + FORCE: true + } + }), + ['FAILOVER', 'TO', 'host', '6379', 'FORCE'] + ); + }); + }); + + it('with ABORT', () => { + assert.deepEqual( + transformArguments({ + ABORT: true + }), + ['FAILOVER', 'ABORT'] + ); + }); + + it('with TIMEOUT', () => { + assert.deepEqual( + transformArguments({ + TIMEOUT: 1 + }), + ['FAILOVER', 'TIMEOUT', '1'] + ); + }); + + it('with TO, ABORT, TIMEOUT', () => { + assert.deepEqual( + transformArguments({ + TO: { + host: 'host', + port: 6379 + }, + ABORT: true, + TIMEOUT: 1 + }), + ['FAILOVER', 'TO', 'host', '6379', 'ABORT', 'TIMEOUT', '1'] + ); + }); + }); +}); diff --git a/lib/commands/FAILOVER.ts b/lib/commands/FAILOVER.ts new file mode 100644 index 00000000000..11ccb32a5cf --- /dev/null +++ b/lib/commands/FAILOVER.ts @@ -0,0 +1,35 @@ +import { transformReplyString } from './generic-transformers'; + +interface FailoverOptions { + TO?: { + host: string; + port: number; + FORCE?: true; + }; + ABORT?: true; + TIMEOUT?: number; +} + +export function transformArguments(options?: FailoverOptions): Array { + const args = ['FAILOVER']; + + if (options?.TO) { + args.push('TO', options.TO.host, options.TO.port.toString()); + + if (options.TO.FORCE) { + args.push('FORCE'); + } + } + + if (options?.ABORT) { + args.push('ABORT'); + } + + if (options?.TIMEOUT) { + args.push('TIMEOUT', options.TIMEOUT.toString()); + } + + return args; +} + +export const transformReply = transformReplyString; diff --git a/lib/commands/FLUSHALL.spec.ts b/lib/commands/FLUSHALL.spec.ts new file mode 100644 index 00000000000..7f1c5ffd282 --- /dev/null +++ b/lib/commands/FLUSHALL.spec.ts @@ -0,0 +1,35 @@ +import { strict as assert } from 'assert'; +import { TestRedisServers, itWithClient } from '../test-utils'; +import { RedisFlushModes, transformArguments } from './FLUSHALL'; + +describe('FLUSHALL', () => { + describe('transformArguments', () => { + it('default', () => { + assert.deepEqual( + transformArguments(), + ['FLUSHALL'] + ); + }); + + it('ASYNC', () => { + assert.deepEqual( + transformArguments(RedisFlushModes.ASYNC), + ['FLUSHALL', 'ASYNC'] + ); + }); + + it('SYNC', () => { + assert.deepEqual( + transformArguments(RedisFlushModes.SYNC), + ['FLUSHALL', 'SYNC'] + ); + }); + }); + + itWithClient(TestRedisServers.OPEN, 'client.flushAll', async client => { + assert.equal( + await client.flushAll(), + 'OK' + ); + }); +}); diff --git a/lib/commands/FLUSHALL.ts b/lib/commands/FLUSHALL.ts new file mode 100644 index 00000000000..4be3474f7e2 --- /dev/null +++ b/lib/commands/FLUSHALL.ts @@ -0,0 +1,18 @@ +import { transformReplyString } from './generic-transformers'; + +export enum RedisFlushModes { + ASYNC = 'ASYNC', + SYNC = 'SYNC' +} + +export function transformArguments(mode?: RedisFlushModes): Array { + const args = ['FLUSHALL']; + + if (mode) { + args.push(mode); + } + + return args; +} + +export const transformReply = transformReplyString; diff --git a/lib/commands/FLUSHDB.spec.ts b/lib/commands/FLUSHDB.spec.ts new file mode 100644 index 00000000000..e237e527680 --- /dev/null +++ b/lib/commands/FLUSHDB.spec.ts @@ -0,0 +1,36 @@ +import { strict as assert } from 'assert'; +import { TestRedisServers, itWithClient } from '../test-utils'; +import { RedisFlushModes } from './FLUSHALL'; +import { transformArguments } from './FLUSHDB'; + +describe('FLUSHDB', () => { + describe('transformArguments', () => { + it('default', () => { + assert.deepEqual( + transformArguments(), + ['FLUSHDB'] + ); + }); + + it('ASYNC', () => { + assert.deepEqual( + transformArguments(RedisFlushModes.ASYNC), + ['FLUSHDB', 'ASYNC'] + ); + }); + + it('SYNC', () => { + assert.deepEqual( + transformArguments(RedisFlushModes.SYNC), + ['FLUSHDB', 'SYNC'] + ); + }); + }); + + itWithClient(TestRedisServers.OPEN, 'client.flushDb', async client => { + assert.equal( + await client.flushDb(), + 'OK' + ); + }); +}); diff --git a/lib/commands/FLUSHDB.ts b/lib/commands/FLUSHDB.ts new file mode 100644 index 00000000000..a85c0933c4c --- /dev/null +++ b/lib/commands/FLUSHDB.ts @@ -0,0 +1,14 @@ +import { RedisFlushModes } from './FLUSHALL'; +import { transformReplyString } from './generic-transformers'; + +export function transformArguments(mode?: RedisFlushModes): Array { + const args = ['FLUSHDB']; + + if (mode) { + args.push(mode); + } + + return args; +} + +export const transformReply = transformReplyString; diff --git a/lib/commands/GEOADD.spec.ts b/lib/commands/GEOADD.spec.ts new file mode 100644 index 00000000000..673e962093f --- /dev/null +++ b/lib/commands/GEOADD.spec.ts @@ -0,0 +1,95 @@ +import { strict as assert } from 'assert'; +import { TestRedisServers, itWithClient, TestRedisClusters, itWithCluster } from '../test-utils'; +import { transformArguments } from './GEOADD'; + +describe('GEOADD', () => { + describe('transformArguments', () => { + it('one member', () => { + assert.deepEqual( + transformArguments('key', { + member: 'member', + longitude: 1, + latitude: 2 + }), + ['GEOADD', 'key', '1', '2', 'member'] + ); + }); + + it('multiple members', () => { + assert.deepEqual( + transformArguments('key', [{ + longitude: 1, + latitude: 2, + member: '3', + }, { + longitude: 4, + latitude: 5, + member: '6', + }]), + ['GEOADD', 'key', '1', '2', '3', '4', '5', '6'] + ); + }); + + it('with NX', () => { + assert.deepEqual( + transformArguments('key', { + longitude: 1, + latitude: 2, + member: 'member' + }, { + NX: true + }), + ['GEOADD', 'key', 'NX', '1', '2', 'member'] + ); + }); + + it('with CH', () => { + assert.deepEqual( + transformArguments('key', { + longitude: 1, + latitude: 2, + member: 'member' + }, { + CH: true + }), + ['GEOADD', 'key', 'CH', '1', '2', 'member'] + ); + }); + + it('with XX, CH', () => { + assert.deepEqual( + transformArguments('key', { + longitude: 1, + latitude: 2, + member: 'member' + }, { + XX: true, + CH: true + }), + ['GEOADD', 'key', 'XX', 'CH', '1', '2', 'member'] + ); + }); + }); + + itWithClient(TestRedisServers.OPEN, 'client.geoAdd', async client => { + assert.equal( + await client.geoAdd('key', { + member: 'member', + longitude: 1, + latitude: 2 + }), + 1 + ); + }); + + itWithCluster(TestRedisClusters.OPEN, 'cluster.geoAdd', async cluster => { + assert.equal( + await cluster.geoAdd('key', { + member: 'member', + longitude: 1, + latitude: 2 + }), + 1 + ); + }); +}); diff --git a/lib/commands/GEOADD.ts b/lib/commands/GEOADD.ts new file mode 100644 index 00000000000..1236563d541 --- /dev/null +++ b/lib/commands/GEOADD.ts @@ -0,0 +1,49 @@ +import { GeoCoordinates, transformReplyNumber } from './generic-transformers'; + +interface GeoMember extends GeoCoordinates { + member: string; +} + +interface NX { + NX?: true; +} + +interface XX { + XX?: true; +} + +type SetGuards = NX | XX; + +interface GeoAddCommonOptions { + CH?: true; +} + +type GeoAddOptions = SetGuards & GeoAddCommonOptions; + +export const FIRST_KEY_INDEX = 1; + +export function transformArguments(key: string, toAdd: GeoMember | Array, options?: GeoAddOptions): Array { + const args = ['GEOADD', key]; + + if ((options as NX)?.NX) { + args.push('NX'); + } else if ((options as XX)?.XX) { + args.push('XX'); + } + + if (options?.CH) { + args.push('CH'); + } + + for (const { longitude, latitude, member } of (Array.isArray(toAdd) ? toAdd : [toAdd])) { + args.push( + longitude.toString(), + latitude.toString(), + member + ); + } + + return args; +} + +export const transformReply = transformReplyNumber; diff --git a/lib/commands/GEODIST.spec.ts b/lib/commands/GEODIST.spec.ts new file mode 100644 index 00000000000..c1168259312 --- /dev/null +++ b/lib/commands/GEODIST.spec.ts @@ -0,0 +1,35 @@ +import { strict as assert } from 'assert'; +import { TestRedisServers, itWithClient, TestRedisClusters, itWithCluster } from '../test-utils'; +import { transformArguments } from './GEODIST'; + +describe('GEODIST', () => { + describe('transformArguments', () => { + it('simple', () => { + assert.deepEqual( + transformArguments('key', '1', '2'), + ['GEODIST', 'key', '1', '2'] + ); + }); + + it('with unit', () => { + assert.deepEqual( + transformArguments('key', '1', '2', 'm'), + ['GEODIST', 'key', '1', '2', 'm'] + ); + }); + }); + + itWithClient(TestRedisServers.OPEN, 'client.geoDist', async client => { + assert.equal( + await client.geoDist('key', '1', '2'), + null + ); + }); + + itWithCluster(TestRedisClusters.OPEN, 'cluster.geoDist', async cluster => { + assert.equal( + await cluster.geoDist('key', '1', '2'), + null + ); + }); +}); diff --git a/lib/commands/GEODIST.ts b/lib/commands/GEODIST.ts new file mode 100644 index 00000000000..6fe6fbb47ab --- /dev/null +++ b/lib/commands/GEODIST.ts @@ -0,0 +1,24 @@ +import { GeoUnits } from './generic-transformers'; + +export const FIRST_KEY_INDEX = 1; + +export const IS_READ_ONLY = true; + +export function transformArguments( + key: string, + member1: string, + member2: string, + unit?: GeoUnits +): Array { + const args = ['GEODIST', key, member1, member2]; + + if (unit) { + args.push(unit); + } + + return args; +} + +export function transformReply(reply: string | null): number | null { + return reply === null ? null : Number(reply); +} diff --git a/lib/commands/GEOHASH.spec.ts b/lib/commands/GEOHASH.spec.ts new file mode 100644 index 00000000000..b79de235557 --- /dev/null +++ b/lib/commands/GEOHASH.spec.ts @@ -0,0 +1,35 @@ +import { strict as assert } from 'assert'; +import { TestRedisServers, itWithClient, TestRedisClusters, itWithCluster } from '../test-utils'; +import { transformArguments } from './GEOHASH'; + +describe('GEOHASH', () => { + describe('transformArguments', () => { + it('single member', () => { + assert.deepEqual( + transformArguments('key', 'member'), + ['GEOHASH', 'key', 'member'] + ); + }); + + it('multiple members', () => { + assert.deepEqual( + transformArguments('key', ['1', '2']), + ['GEOHASH', 'key', '1', '2'] + ); + }); + }); + + itWithClient(TestRedisServers.OPEN, 'client.geoHash', async client => { + assert.deepEqual( + await client.geoHash('key', 'member'), + [null] + ); + }); + + itWithCluster(TestRedisClusters.OPEN, 'cluster.geoHash', async cluster => { + assert.deepEqual( + await cluster.geoHash('key', 'member'), + [null] + ); + }); +}); diff --git a/lib/commands/GEOHASH.ts b/lib/commands/GEOHASH.ts new file mode 100644 index 00000000000..a46738955d3 --- /dev/null +++ b/lib/commands/GEOHASH.ts @@ -0,0 +1,11 @@ +import { pushVerdictArguments, transformReplyStringArray } from './generic-transformers'; + +export const FIRST_KEY_INDEX = 1; + +export const IS_READ_ONLY = true; + +export function transformArguments(key: string, member: string | Array): Array { + return pushVerdictArguments(['GEOHASH', key], member); +} + +export const transformReply = transformReplyStringArray; diff --git a/lib/commands/GEOPOS.spec.ts b/lib/commands/GEOPOS.spec.ts new file mode 100644 index 00000000000..98cfa6aa2d3 --- /dev/null +++ b/lib/commands/GEOPOS.spec.ts @@ -0,0 +1,35 @@ +import { strict as assert } from 'assert'; +import { TestRedisServers, itWithClient, TestRedisClusters, itWithCluster } from '../test-utils'; +import { transformArguments } from './GEOPOS'; + +describe('GEOPOS', () => { + describe('transformArguments', () => { + it('single member', () => { + assert.deepEqual( + transformArguments('key', 'member'), + ['GEOPOS', 'key', 'member'] + ); + }); + + it('multiple members', () => { + assert.deepEqual( + transformArguments('key', ['1', '2']), + ['GEOPOS', 'key', '1', '2'] + ); + }); + }); + + itWithClient(TestRedisServers.OPEN, 'client.geoPos', async client => { + assert.deepEqual( + await client.geoPos('key', 'member'), + [null] + ); + }); + + itWithCluster(TestRedisClusters.OPEN, 'cluster.geoPos', async cluster => { + assert.deepEqual( + await cluster.geoPos('key', 'member'), + [null] + ); + }); +}); diff --git a/lib/commands/GEOPOS.ts b/lib/commands/GEOPOS.ts new file mode 100644 index 00000000000..46b0a153ba9 --- /dev/null +++ b/lib/commands/GEOPOS.ts @@ -0,0 +1,21 @@ +import { pushVerdictArguments } from './generic-transformers'; + +export const FIRST_KEY_INDEX = 1; + +export const IS_READ_ONLY = true; + +export function transformArguments(key: string, member: string | Array): Array { + return pushVerdictArguments(['GEOPOS', key], member); +} + +interface GeoCoordinates { + longitude: string; + latitude: string; +} + +export function transformReply(reply: Array<[string, string] | null>): Array { + return reply.map(coordinates => coordinates === null ? null : { + longitude: coordinates[0], + latitude: coordinates[1] + }); +} diff --git a/lib/commands/GEOSEARCH.spec.ts b/lib/commands/GEOSEARCH.spec.ts new file mode 100644 index 00000000000..a8606b3f74c --- /dev/null +++ b/lib/commands/GEOSEARCH.spec.ts @@ -0,0 +1,37 @@ +import { strict as assert } from 'assert'; +import { TestRedisServers, itWithClient, TestRedisClusters, itWithCluster, describeHandleMinimumRedisVersion } from '../test-utils'; +import { transformArguments } from './GEOSEARCH'; + +describe('GEOSEARCH', () => { + describeHandleMinimumRedisVersion([6, 2]); + + it('transformArguments', () => { + assert.deepEqual( + transformArguments('key', 'member', { + radius: 1, + unit: 'm' + }), + ['GEOSEARCH', 'key', 'FROMMEMBER', 'member', 'BYRADIUS', '1', 'm'] + ); + }); + + itWithClient(TestRedisServers.OPEN, 'client.geoSearch', async client => { + assert.deepEqual( + await client.geoSearch('key', 'member', { + radius: 1, + unit: 'm' + }), + [] + ); + }); + + itWithCluster(TestRedisClusters.OPEN, 'cluster.geoSearch', async cluster => { + assert.deepEqual( + await cluster.geoSearch('key', 'member', { + radius: 1, + unit: 'm' + }), + [] + ); + }); +}); diff --git a/lib/commands/GEOSEARCH.ts b/lib/commands/GEOSEARCH.ts new file mode 100644 index 00000000000..3872f11c6cb --- /dev/null +++ b/lib/commands/GEOSEARCH.ts @@ -0,0 +1,16 @@ +import { GeoSearchFrom, GeoSearchBy, GeoSearchOptions, pushGeoSearchArguments, transformReplyStringArray } from './generic-transformers'; + +export const FIRST_KEY_INDEX = 1; + +export const IS_READ_ONLY = true; + +export function transformArguments( + key: string, + from: GeoSearchFrom, + by: GeoSearchBy, + options?: GeoSearchOptions +): Array { + return pushGeoSearchArguments(['GEOSEARCH'], key, from, by, options); +} + +export const transformReply = transformReplyStringArray; diff --git a/lib/commands/GEOSEARCHSTORE.spec.ts b/lib/commands/GEOSEARCHSTORE.spec.ts new file mode 100644 index 00000000000..1983537077c --- /dev/null +++ b/lib/commands/GEOSEARCHSTORE.spec.ts @@ -0,0 +1,74 @@ +import { strict as assert } from 'assert'; +import { TestRedisServers, itWithClient, TestRedisClusters, itWithCluster, describeHandleMinimumRedisVersion } from '../test-utils'; +import { transformArguments } from './GEOSEARCHSTORE'; + +describe('GEOSEARCHSTORE', () => { + describeHandleMinimumRedisVersion([6, 2]); + + describe('transformArguments', () => { + it('simple', () => { + assert.deepEqual( + transformArguments('destination', 'source', 'member', { + radius: 1, + unit: 'm' + }, { + SORT: 'ASC', + COUNT: { + value: 1, + ANY: true + } + }), + ['GEOSEARCHSTORE', 'destination', 'source', 'FROMMEMBER', 'member', 'BYRADIUS', '1', 'm', 'ASC', 'COUNT', '1', 'ANY'] + ); + }); + + it('with STOREDIST', () => { + assert.deepEqual( + transformArguments('destination', 'source', 'member', { + radius: 1, + unit: 'm' + }, { + SORT: 'ASC', + COUNT: { + value: 1, + ANY: true + }, + STOREDIST: true + }), + ['GEOSEARCHSTORE', 'destination', 'source', 'FROMMEMBER', 'member', 'BYRADIUS', '1', 'm', 'ASC', 'COUNT', '1', 'ANY', 'STOREDIST'] + ); + }); + }); + + itWithClient(TestRedisServers.OPEN, 'client.geoSearchStore', async client => { + await client.geoAdd('source', { + longitude: 1, + latitude: 1, + member: 'member' + }); + + assert.equal( + await client.geoSearchStore('destination', 'source', 'member', { + radius: 1, + unit: 'm' + }), + 1 + ); + }); + + itWithCluster(TestRedisClusters.OPEN, 'cluster.geoSearchStore', async cluster => { + await cluster.geoAdd('{tag}source', { + longitude: 1, + latitude: 1, + member: 'member' + }); + + assert.equal( + await cluster.geoSearchStore('{tag}destination', '{tag}source', 'member', { + radius: 1, + unit: 'm' + }), + 1 + ); + }); +}); diff --git a/lib/commands/GEOSEARCHSTORE.ts b/lib/commands/GEOSEARCHSTORE.ts new file mode 100644 index 00000000000..e10622052ba --- /dev/null +++ b/lib/commands/GEOSEARCHSTORE.ts @@ -0,0 +1,39 @@ +import { GeoSearchFrom, GeoSearchBy, GeoSearchOptions, pushGeoSearchArguments } from './generic-transformers'; + +export const FIRST_KEY_INDEX = 1; + +export const IS_READ_ONLY = true; + +interface GeoSearchStoreOptions extends GeoSearchOptions { + STOREDIST?: true; +} + +export function transformArguments( + destination: string, + source: string, + from: GeoSearchFrom, + by: GeoSearchBy, + options?: GeoSearchStoreOptions +): Array { + const args = pushGeoSearchArguments( + ['GEOSEARCHSTORE', destination], + source, + from, + by, + options + ); + + if (options?.STOREDIST) { + args.push('STOREDIST'); + } + + return args; +} + +export function transformReply(reply: number): number { + if (typeof reply !== 'number') { + throw new TypeError(`https://github.com/redis/redis/issues/9261`); + } + + return reply; +} diff --git a/lib/commands/GEOSEARCH_WITH.spec.ts b/lib/commands/GEOSEARCH_WITH.spec.ts new file mode 100644 index 00000000000..a400fb965c6 --- /dev/null +++ b/lib/commands/GEOSEARCH_WITH.spec.ts @@ -0,0 +1,42 @@ +import { strict as assert } from 'assert'; +import { TransformArgumentsReply } from '.'; +import { TestRedisServers, itWithClient, TestRedisClusters, itWithCluster, describeHandleMinimumRedisVersion } from '../test-utils'; +import { GeoReplyWith } from './generic-transformers'; +import { transformArguments } from './GEOSEARCH_WITH'; + +describe('GEOSEARCH WITH', () => { + describeHandleMinimumRedisVersion([6, 2]); + + it('transformArguments', () => { + const expectedReply: TransformArgumentsReply = ['GEOSEARCH', 'key', 'FROMMEMBER', 'member', 'BYRADIUS', '1', 'm', 'WITHDIST'] + expectedReply.preserve = ['WITHDIST']; + + assert.deepEqual( + transformArguments('key', 'member', { + radius: 1, + unit: 'm' + }, [GeoReplyWith.DISTANCE]), + expectedReply + ); + }); + + itWithClient(TestRedisServers.OPEN, 'client.geoSearchWith', async client => { + assert.deepEqual( + await client.geoSearchWith('key', 'member', { + radius: 1, + unit: 'm' + }, [GeoReplyWith.DISTANCE]), + [] + ); + }); + + itWithCluster(TestRedisClusters.OPEN, 'cluster.geoSearchWith', async cluster => { + assert.deepEqual( + await cluster.geoSearchWith('key', 'member', { + radius: 1, + unit: 'm' + }, [GeoReplyWith.DISTANCE]), + [] + ); + }); +}); diff --git a/lib/commands/GEOSEARCH_WITH.ts b/lib/commands/GEOSEARCH_WITH.ts new file mode 100644 index 00000000000..ef19ca5dfc9 --- /dev/null +++ b/lib/commands/GEOSEARCH_WITH.ts @@ -0,0 +1,23 @@ +import { TransformArgumentsReply } from '.'; +import { GeoSearchFrom, GeoSearchBy, GeoReplyWith, GeoSearchOptions, transformGeoMembersWithReply } from './generic-transformers'; +import { transformArguments as geoSearchTransformArguments } from './GEOSEARCH'; + +export { FIRST_KEY_INDEX, IS_READ_ONLY } from './GEOSEARCH'; + +export function transformArguments( + key: string, + from: GeoSearchFrom, + by: GeoSearchBy, + replyWith: Array, + options?: GeoSearchOptions +): TransformArgumentsReply { + const args: TransformArgumentsReply = geoSearchTransformArguments(key, from, by, options); + + args.push(...replyWith); + + args.preserve = replyWith; + + return args; +} + +export const transformReply = transformGeoMembersWithReply; diff --git a/lib/commands/GET.spec.ts b/lib/commands/GET.spec.ts new file mode 100644 index 00000000000..303be60fe99 --- /dev/null +++ b/lib/commands/GET.spec.ts @@ -0,0 +1,26 @@ +import { strict as assert } from 'assert'; +import { TestRedisServers, itWithClient, TestRedisClusters, itWithCluster } from '../test-utils'; +import { transformArguments } from './GET'; + +describe('GET', () => { + it('transformArguments', () => { + assert.deepEqual( + transformArguments('key'), + ['GET', 'key'] + ); + }); + + itWithClient(TestRedisServers.OPEN, 'client.get', async client => { + assert.equal( + await client.get('key'), + null + ); + }); + + itWithCluster(TestRedisClusters.OPEN, 'cluster.get', async cluster => { + assert.equal( + await cluster.get('key'), + null + ); + }); +}); diff --git a/lib/commands/GET.ts b/lib/commands/GET.ts new file mode 100644 index 00000000000..714ad953d8e --- /dev/null +++ b/lib/commands/GET.ts @@ -0,0 +1,11 @@ +import { transformReplyString } from './generic-transformers'; + +export const FIRST_KEY_INDEX = 1; + +export const IS_READ_ONLY = true; + +export function transformArguments(key: string): Array { + return ['GET', key]; +} + +export const transformReply = transformReplyString; diff --git a/lib/commands/GETBIT.spec.ts b/lib/commands/GETBIT.spec.ts new file mode 100644 index 00000000000..7163b4ba255 --- /dev/null +++ b/lib/commands/GETBIT.spec.ts @@ -0,0 +1,26 @@ +import { strict as assert } from 'assert'; +import { TestRedisServers, itWithClient, TestRedisClusters, itWithCluster } from '../test-utils'; +import { transformArguments } from './GETBIT'; + +describe('GETBIT', () => { + it('transformArguments', () => { + assert.deepEqual( + transformArguments('key', 0), + ['GETBIT', 'key', '0'] + ); + }); + + itWithClient(TestRedisServers.OPEN, 'client.getBit', async client => { + assert.equal( + await client.getBit('key', 0), + 0 + ); + }); + + itWithCluster(TestRedisClusters.OPEN, 'cluster.getBit', async cluster => { + assert.equal( + await cluster.getBit('key', 0), + 0 + ); + }); +}); diff --git a/lib/commands/GETBIT.ts b/lib/commands/GETBIT.ts new file mode 100644 index 00000000000..c7e878f75a8 --- /dev/null +++ b/lib/commands/GETBIT.ts @@ -0,0 +1,11 @@ +import { transformReplyBit } from './generic-transformers'; + +export const FIRST_KEY_INDEX = 1; + +export const IS_READ_ONLY = true; + +export function transformArguments(key: string, offset: number): Array { + return ['GETBIT', key, offset.toString()]; +} + +export const transformReply = transformReplyBit; diff --git a/lib/commands/GETDEL.spec.ts b/lib/commands/GETDEL.spec.ts new file mode 100644 index 00000000000..232c08b9500 --- /dev/null +++ b/lib/commands/GETDEL.spec.ts @@ -0,0 +1,29 @@ +import { strict as assert } from 'assert'; +import { TestRedisServers, itWithClient, TestRedisClusters, itWithCluster, describeHandleMinimumRedisVersion } from '../test-utils'; +import { transformArguments } from './GETDEL'; + +describe('GETDEL', () => { + describeHandleMinimumRedisVersion([6, 2]); + + it('transformArguments', () => { + assert.deepEqual( + transformArguments('key'), + ['GETDEL', 'key'] + ); + }); + + itWithClient(TestRedisServers.OPEN, 'client.getDel', async client => { + assert.equal( + await client.getDel('key'), + null + ); + }); + + + itWithCluster(TestRedisClusters.OPEN, 'cluster.getDel', async cluster => { + assert.equal( + await cluster.getDel('key'), + null + ); + }); +}); diff --git a/lib/commands/GETDEL.ts b/lib/commands/GETDEL.ts new file mode 100644 index 00000000000..218e057637d --- /dev/null +++ b/lib/commands/GETDEL.ts @@ -0,0 +1,9 @@ +import { transformReplyStringNull } from './generic-transformers'; + +export const FIRST_KEY_INDEX = 1; + +export function transformArguments(key: string): Array { + return ['GETDEL', key]; +} + +export const transformReply = transformReplyStringNull; diff --git a/lib/commands/GETEX.spec.ts b/lib/commands/GETEX.spec.ts new file mode 100644 index 00000000000..830f12cedf8 --- /dev/null +++ b/lib/commands/GETEX.spec.ts @@ -0,0 +1,96 @@ +import { strict as assert } from 'assert'; +import { TestRedisServers, itWithClient, TestRedisClusters, itWithCluster, describeHandleMinimumRedisVersion } from '../test-utils'; +import { transformArguments } from './GETEX'; + +describe('GETEX', () => { + describeHandleMinimumRedisVersion([6, 2]); + + describe('transformArguments', () => { + it('EX', () => { + assert.deepEqual( + transformArguments('key', { + EX: 1 + }), + ['GETEX', 'key', 'EX', '1'] + ); + }); + + it('PX', () => { + assert.deepEqual( + transformArguments('key', { + PX: 1 + }), + ['GETEX', 'key', 'PX', '1'] + ); + }); + + describe('EXAT', () => { + it('number', () => { + assert.deepEqual( + transformArguments('key', { + EXAT: 1 + }), + ['GETEX', 'key', 'EXAT', '1'] + ); + }); + + it('date', () => { + const d = new Date(); + assert.deepEqual( + transformArguments('key', { + EXAT: d + }), + ['GETEX', 'key', 'EXAT', Math.floor(d.getTime() / 1000).toString()] + ); + }); + }); + + describe('PXAT', () => { + it('number', () => { + assert.deepEqual( + transformArguments('key', { + PXAT: 1 + }), + ['GETEX', 'key', 'PXAT', '1'] + ); + }); + + it('date', () => { + const d = new Date(); + assert.deepEqual( + transformArguments('key', { + PXAT: d + }), + ['GETEX', 'key', 'PXAT', d.getTime().toString()] + ); + }); + }); + + it('PERSIST', () => { + assert.deepEqual( + transformArguments('key', { + PERSIST: true + }), + ['GETEX', 'key', 'PERSIST'] + ); + }); + }); + + itWithClient(TestRedisServers.OPEN, 'client.getEx', async client => { + assert.equal( + await client.getEx('key', { + PERSIST: true + }), + null + ); + }); + + itWithCluster(TestRedisClusters.OPEN, 'cluster.getEx', async cluster => { + assert.equal( + await cluster.getEx('key', { + PERSIST: true + }), + null + ); + }); +}); diff --git a/lib/commands/GETEX.ts b/lib/commands/GETEX.ts new file mode 100644 index 00000000000..ca1465b7ee5 --- /dev/null +++ b/lib/commands/GETEX.ts @@ -0,0 +1,35 @@ +import { transformEXAT, transformPXAT, transformReplyStringNull } from './generic-transformers'; + +export const FIRST_KEY_INDEX = 1; + +type GetExModes = { + EX: number; +} | { + PX: number; +} | { + EXAT: number | Date; +} | { + PXAT: number | Date; +} | { + PERSIST: true; +}; + +export function transformArguments(key: string, mode: GetExModes) { + const args = ['GETEX', key]; + + if ('EX' in mode) { + args.push('EX', mode.EX.toString()); + } else if ('PX' in mode) { + args.push('PX', mode.PX.toString()); + } else if ('EXAT' in mode) { + args.push('EXAT', transformEXAT(mode.EXAT)); + } else if ('PXAT' in mode) { + args.push('PXAT', transformPXAT(mode.PXAT)); + } else { // PERSIST + args.push('PERSIST'); + } + + return args; +} + +export const transformReply = transformReplyStringNull; diff --git a/lib/commands/GETRANGE.spec.ts b/lib/commands/GETRANGE.spec.ts new file mode 100644 index 00000000000..726311e6844 --- /dev/null +++ b/lib/commands/GETRANGE.spec.ts @@ -0,0 +1,27 @@ +import { strict as assert } from 'assert'; +import { TestRedisServers, itWithClient, TestRedisClusters, itWithCluster } from '../test-utils'; +import { transformArguments } from './GETRANGE'; + +describe('GETRANGE', () => { + it('transformArguments', () => { + assert.deepEqual( + transformArguments('key', 0, -1), + ['GETRANGE', 'key', '0', '-1'] + ); + }); + + itWithClient(TestRedisServers.OPEN, 'client.getRange', async client => { + assert.equal( + await client.getRange('key', 0, -1), + '' + ); + }); + + + itWithCluster(TestRedisClusters.OPEN, 'cluster.lTrim', async cluster => { + assert.equal( + await cluster.getRange('key', 0, -1), + '' + ); + }); +}); diff --git a/lib/commands/GETRANGE.ts b/lib/commands/GETRANGE.ts new file mode 100644 index 00000000000..9488dd53d5e --- /dev/null +++ b/lib/commands/GETRANGE.ts @@ -0,0 +1,11 @@ +import { transformReplyString } from './generic-transformers'; + +export const FIRST_KEY_INDEX = 1; + +export const IS_READ_ONLY = true; + +export function transformArguments(key: string, start: number, end: number): Array { + return ['GETRANGE', key, start.toString(), end.toString()]; +} + +export const transformReply = transformReplyString; diff --git a/lib/commands/GETSET.spec.ts b/lib/commands/GETSET.spec.ts new file mode 100644 index 00000000000..4af5ab39ca2 --- /dev/null +++ b/lib/commands/GETSET.spec.ts @@ -0,0 +1,26 @@ +import { strict as assert } from 'assert'; +import { TestRedisServers, itWithClient, TestRedisClusters, itWithCluster } from '../test-utils'; +import { transformArguments } from './GETSET'; + +describe('GETSET', () => { + it('transformArguments', () => { + assert.deepEqual( + transformArguments('key', 'value'), + ['GETSET', 'key', 'value'] + ); + }); + + itWithClient(TestRedisServers.OPEN, 'client.getSet', async client => { + assert.equal( + await client.getSet('key', 'value'), + null + ); + }); + + itWithCluster(TestRedisClusters.OPEN, 'cluster.getSet', async cluster => { + assert.equal( + await cluster.getSet('key', 'value'), + null + ); + }); +}); diff --git a/lib/commands/GETSET.ts b/lib/commands/GETSET.ts new file mode 100644 index 00000000000..1b9b9d6bc75 --- /dev/null +++ b/lib/commands/GETSET.ts @@ -0,0 +1,9 @@ +import { transformReplyStringNull } from './generic-transformers'; + +export const FIRST_KEY_INDEX = 1; + +export function transformArguments(key: string, value: string): Array { + return ['GETSET', key, value]; +} + +export const transformReply = transformReplyStringNull; diff --git a/lib/commands/HDEL.spec.ts b/lib/commands/HDEL.spec.ts new file mode 100644 index 00000000000..04191f51ada --- /dev/null +++ b/lib/commands/HDEL.spec.ts @@ -0,0 +1,28 @@ +import { strict as assert } from 'assert'; +import { TestRedisServers, itWithClient } from '../test-utils'; +import { transformArguments } from './HDEL'; + +describe('HDEL', () => { + describe('transformArguments', () => { + it('string', () => { + assert.deepEqual( + transformArguments('key', 'field'), + ['HDEL', 'key', 'field'] + ); + }); + + it('array', () => { + assert.deepEqual( + transformArguments('key', ['1', '2']), + ['HDEL', 'key', '1', '2'] + ); + }); + }); + + itWithClient(TestRedisServers.OPEN, 'client.hDel', async client => { + assert.equal( + await client.hDel('key', 'field'), + 0 + ); + }); +}); diff --git a/lib/commands/HDEL.ts b/lib/commands/HDEL.ts new file mode 100644 index 00000000000..ee961931449 --- /dev/null +++ b/lib/commands/HDEL.ts @@ -0,0 +1,9 @@ +import { pushVerdictArguments, transformReplyNumber } from './generic-transformers'; + +export const FIRST_KEY_INDEX = 1; + +export function transformArguments(key: string, field: string | Array): Array { + return pushVerdictArguments(['HDEL', key], field); +} + +export const transformReply = transformReplyNumber; diff --git a/lib/commands/HELLO.spec.ts b/lib/commands/HELLO.spec.ts new file mode 100644 index 00000000000..7642f739d92 --- /dev/null +++ b/lib/commands/HELLO.spec.ts @@ -0,0 +1,77 @@ +import { strict as assert } from 'assert'; +import { REDIS_VERSION, TestRedisServers, itWithClient, describeHandleMinimumRedisVersion } from '../test-utils'; +import { transformArguments } from './HELLO'; + +describe('HELLO', () => { + describeHandleMinimumRedisVersion([6]); + + describe('transformArguments', () => { + it('simple', () => { + assert.deepEqual( + transformArguments(), + ['HELLO'] + ); + }); + + it('with protover', () => { + assert.deepEqual( + transformArguments({ + protover: 3 + }), + ['HELLO', '3'] + ); + }); + + it('with protover, auth', () => { + assert.deepEqual( + transformArguments({ + protover: 3, + auth: { + username: 'username', + password: 'password' + } + }), + ['HELLO', '3', 'AUTH', 'username', 'password'] + ); + }); + + it('with protover, clientName', () => { + assert.deepEqual( + transformArguments({ + protover: 3, + clientName: 'clientName' + }), + ['HELLO', '3', 'SETNAME', 'clientName'] + ); + }); + + it('with protover, auth, clientName', () => { + assert.deepEqual( + transformArguments({ + protover: 3, + auth: { + username: 'username', + password: 'password' + }, + clientName: 'clientName' + }), + ['HELLO', '3', 'AUTH', 'username', 'password', 'SETNAME', 'clientName'] + ); + }); + }); + + itWithClient(TestRedisServers.OPEN, 'client.hello', async client => { + assert.deepEqual( + await client.hello(), + { + server: 'redis', + version: REDIS_VERSION.join('.'), + proto: 2, + id: await client.clientId(), + mode: 'standalone', + role: 'master', + modules: [] + } + ); + }); +}); diff --git a/lib/commands/HELLO.ts b/lib/commands/HELLO.ts new file mode 100644 index 00000000000..efb96890fcd --- /dev/null +++ b/lib/commands/HELLO.ts @@ -0,0 +1,64 @@ +import { AuthOptions } from './AUTH'; + +interface HelloOptions { + protover: number; + auth?: Required; + clientName?: string; +} + +export function transformArguments(options?: HelloOptions): Array { + const args = ['HELLO']; + + if (options) { + args.push(options.protover.toString()); + + if (options?.auth) { + args.push('AUTH', options.auth.username, options.auth.password); + } + + if (options.clientName) { + args.push('SETNAME', options.clientName); + } + } + + return args; +} + +type HelloRawReply = [ + _: never, + server: string, + _: never, + version: string, + _: never, + proto: number, + _: never, + id: number, + _: never, + mode: string, + _: never, + role: string, + _: never, + modules: Array +]; + +interface HelloTransformedReply { + server: string; + version: string; + proto: number; + id: number; + mode: string; + role: string; + modules: Array; +} + +export function transformReply(reply: HelloRawReply): HelloTransformedReply { + return { + server: reply[1], + version: reply[3], + proto: reply[5], + id: reply[7], + mode: reply[9], + role: reply[11], + modules: reply[13] + }; +} diff --git a/lib/commands/HEXISTS.spec.ts b/lib/commands/HEXISTS.spec.ts new file mode 100644 index 00000000000..26c411c432d --- /dev/null +++ b/lib/commands/HEXISTS.spec.ts @@ -0,0 +1,19 @@ +import { strict as assert } from 'assert'; +import { TestRedisServers, itWithClient } from '../test-utils'; +import { transformArguments } from './HEXISTS'; + +describe('HEXISTS', () => { + it('transformArguments', () => { + assert.deepEqual( + transformArguments('key', 'field'), + ['HEXISTS', 'key', 'field'] + ); + }); + + itWithClient(TestRedisServers.OPEN, 'client.hExists', async client => { + assert.equal( + await client.hExists('key', 'field'), + false + ); + }); +}); diff --git a/lib/commands/HEXISTS.ts b/lib/commands/HEXISTS.ts new file mode 100644 index 00000000000..7cf0b158d92 --- /dev/null +++ b/lib/commands/HEXISTS.ts @@ -0,0 +1,9 @@ +import { transformReplyBoolean } from './generic-transformers'; + +export const FIRST_KEY_INDEX = 1; + +export function transformArguments(key: string, field: string): Array { + return ['HEXISTS', key, field]; +} + +export const transformReply = transformReplyBoolean; diff --git a/lib/commands/HGET.spec.ts b/lib/commands/HGET.spec.ts new file mode 100644 index 00000000000..c78550c5179 --- /dev/null +++ b/lib/commands/HGET.spec.ts @@ -0,0 +1,19 @@ +import { strict as assert } from 'assert'; +import { TestRedisServers, itWithClient } from '../test-utils'; +import { transformArguments } from './HGET'; + +describe('HGET', () => { + it('transformArguments', () => { + assert.deepEqual( + transformArguments('key', 'field'), + ['HGET', 'key', 'field'] + ); + }); + + itWithClient(TestRedisServers.OPEN, 'client.hGet', async client => { + assert.equal( + await client.hGet('key', 'field'), + null + ); + }); +}); diff --git a/lib/commands/HGET.ts b/lib/commands/HGET.ts new file mode 100644 index 00000000000..edabbcd6bc7 --- /dev/null +++ b/lib/commands/HGET.ts @@ -0,0 +1,9 @@ +export const FIRST_KEY_INDEX = 1; + +export function transformArguments(key: string, field: string): Array { + return ['HGET', key, field]; +} + +export function transformReply(reply?: string): string | undefined { + return reply; +} diff --git a/lib/commands/HGETALL.spec.ts b/lib/commands/HGETALL.spec.ts new file mode 100644 index 00000000000..68b51a2902b --- /dev/null +++ b/lib/commands/HGETALL.spec.ts @@ -0,0 +1,41 @@ +import { strict as assert } from 'assert'; +import { TestRedisServers, itWithClient } from '../test-utils'; +import { transformReply } from './HGETALL'; + +describe('HGETALL', () => { + describe('transformReply', () => { + it('empty', () => { + assert.deepEqual( + transformReply([]), + Object.create(null) + ); + }); + + it('with values', () => { + assert.deepEqual( + transformReply(['key1', 'value1', 'key2', 'value2']), + Object.create(null, { + key1: { + value: 'value1', + configurable: true, + enumerable: true, + writable: true + }, + key2: { + value: 'value2', + configurable: true, + enumerable: true, + writable: true + } + }) + ); + }); + }); + + itWithClient(TestRedisServers.OPEN, 'client.hGetAll', async client => { + assert.deepEqual( + await client.hGetAll('key'), + Object.create(null) + ); + }); +}); diff --git a/lib/commands/HGETALL.ts b/lib/commands/HGETALL.ts new file mode 100644 index 00000000000..8ac14ec4963 --- /dev/null +++ b/lib/commands/HGETALL.ts @@ -0,0 +1,9 @@ +import { transformReplyTuples } from './generic-transformers'; + +export const FIRST_KEY_INDEX = 1; + +export function transformArguments(key: string): Array { + return ['HGETALL', key]; +} + +export const transformReply = transformReplyTuples; diff --git a/lib/commands/HINCRBY.spec.ts b/lib/commands/HINCRBY.spec.ts new file mode 100644 index 00000000000..898dfd1172f --- /dev/null +++ b/lib/commands/HINCRBY.spec.ts @@ -0,0 +1,19 @@ +import { strict as assert } from 'assert'; +import { TestRedisServers, itWithClient } from '../test-utils'; +import { transformArguments } from './HINCRBY'; + +describe('HINCRBY', () => { + it('transformArguments', () => { + assert.deepEqual( + transformArguments('key', 'field', 1), + ['HINCRBY', 'key', 'field', '1'] + ); + }); + + itWithClient(TestRedisServers.OPEN, 'client.hIncrBy', async client => { + assert.equal( + await client.hIncrBy('key', 'field', 1), + 1 + ); + }); +}); diff --git a/lib/commands/HINCRBY.ts b/lib/commands/HINCRBY.ts new file mode 100644 index 00000000000..192dac456e1 --- /dev/null +++ b/lib/commands/HINCRBY.ts @@ -0,0 +1,9 @@ +import { transformReplyNumber } from './generic-transformers'; + +export const FIRST_KEY_INDEX = 1; + +export function transformArguments(key: string, field: string, increment: number): Array { + return ['HINCRBY', key, field, increment.toString()]; +} + +export const transformReply = transformReplyNumber; diff --git a/lib/commands/HINCRBYFLOAT.spec.ts b/lib/commands/HINCRBYFLOAT.spec.ts new file mode 100644 index 00000000000..83e87538c54 --- /dev/null +++ b/lib/commands/HINCRBYFLOAT.spec.ts @@ -0,0 +1,19 @@ +import { strict as assert } from 'assert'; +import { TestRedisServers, itWithClient } from '../test-utils'; +import { transformArguments } from './HINCRBYFLOAT'; + +describe('HINCRBYFLOAT', () => { + it('transformArguments', () => { + assert.deepEqual( + transformArguments('key', 'field', 1.5), + ['HINCRBYFLOAT', 'key', 'field', '1.5'] + ); + }); + + itWithClient(TestRedisServers.OPEN, 'client.hIncrByFloat', async client => { + assert.equal( + await client.hIncrByFloat('key', 'field', 1.5), + '1.5' + ); + }); +}); diff --git a/lib/commands/HINCRBYFLOAT.ts b/lib/commands/HINCRBYFLOAT.ts new file mode 100644 index 00000000000..10c949b8d93 --- /dev/null +++ b/lib/commands/HINCRBYFLOAT.ts @@ -0,0 +1,9 @@ +import { transformReplyNumber } from './generic-transformers'; + +export const FIRST_KEY_INDEX = 1; + +export function transformArguments(key: string, field: string, increment: number): Array { + return ['HINCRBYFLOAT', key, field, increment.toString()]; +} + +export const transformReply = transformReplyNumber; diff --git a/lib/commands/HKEYS.spec.ts b/lib/commands/HKEYS.spec.ts new file mode 100644 index 00000000000..12190668b0b --- /dev/null +++ b/lib/commands/HKEYS.spec.ts @@ -0,0 +1,19 @@ +import { strict as assert } from 'assert'; +import { TestRedisServers, itWithClient } from '../test-utils'; +import { transformArguments } from './HKEYS'; + +describe('HKEYS', () => { + it('transformArguments', () => { + assert.deepEqual( + transformArguments('key'), + ['HKEYS', 'key'] + ); + }); + + itWithClient(TestRedisServers.OPEN, 'client.hKeys', async client => { + assert.deepEqual( + await client.hKeys('key'), + [] + ); + }); +}); diff --git a/lib/commands/HKEYS.ts b/lib/commands/HKEYS.ts new file mode 100644 index 00000000000..d79d2c1d134 --- /dev/null +++ b/lib/commands/HKEYS.ts @@ -0,0 +1,9 @@ +import { transformReplyStringArray } from './generic-transformers'; + +export const FIRST_KEY_INDEX = 1; + +export function transformArguments(key: string): Array { + return ['HKEYS', key]; +} + +export const transformReply = transformReplyStringArray; diff --git a/lib/commands/HLEN.spec.ts b/lib/commands/HLEN.spec.ts new file mode 100644 index 00000000000..e9aaa64e6e5 --- /dev/null +++ b/lib/commands/HLEN.spec.ts @@ -0,0 +1,19 @@ +import { strict as assert } from 'assert'; +import { TestRedisServers, itWithClient } from '../test-utils'; +import { transformArguments } from './HLEN'; + +describe('HLEN', () => { + it('transformArguments', () => { + assert.deepEqual( + transformArguments('key'), + ['HLEN', 'key'] + ); + }); + + itWithClient(TestRedisServers.OPEN, 'client.hLen', async client => { + assert.equal( + await client.hLen('key'), + 0 + ); + }); +}); diff --git a/lib/commands/HLEN.ts b/lib/commands/HLEN.ts new file mode 100644 index 00000000000..ba7ccc3aed9 --- /dev/null +++ b/lib/commands/HLEN.ts @@ -0,0 +1,9 @@ +import { transformReplyNumber } from './generic-transformers'; + +export const FIRST_KEY_INDEX = 1; + +export function transformArguments(key: string): Array { + return ['HLEN', key]; +} + +export const transformReply = transformReplyNumber; diff --git a/lib/commands/HMGET.spec.ts b/lib/commands/HMGET.spec.ts new file mode 100644 index 00000000000..3b1c286e748 --- /dev/null +++ b/lib/commands/HMGET.spec.ts @@ -0,0 +1,28 @@ +import { strict as assert } from 'assert'; +import { TestRedisServers, itWithClient } from '../test-utils'; +import { transformArguments } from './HMGET'; + +describe('HMGET', () => { + describe('transformArguments', () => { + it('string', () => { + assert.deepEqual( + transformArguments('key', 'field'), + ['HMGET', 'key', 'field'] + ); + }); + + it('array', () => { + assert.deepEqual( + transformArguments('key', ['field1', 'field2']), + ['HMGET', 'key', 'field1', 'field2'] + ); + }); + }); + + itWithClient(TestRedisServers.OPEN, 'client.hmGet', async client => { + assert.deepEqual( + await client.hmGet('key', 'field'), + [null] + ); + }); +}); diff --git a/lib/commands/HMGET.ts b/lib/commands/HMGET.ts new file mode 100644 index 00000000000..fc0f91d8224 --- /dev/null +++ b/lib/commands/HMGET.ts @@ -0,0 +1,11 @@ +import { pushVerdictArguments, transformReplyStringArray } from './generic-transformers'; + +export const FIRST_KEY_INDEX = 1; + +export const IS_READ_ONLY = true; + +export function transformArguments(key: string, fields: string | Array): Array { + return pushVerdictArguments(['HMGET', key], fields); +} + +export const transformReply = transformReplyStringArray; diff --git a/lib/commands/HRANDFIELD.spec.ts b/lib/commands/HRANDFIELD.spec.ts new file mode 100644 index 00000000000..70e2585cf93 --- /dev/null +++ b/lib/commands/HRANDFIELD.spec.ts @@ -0,0 +1,21 @@ +import { strict as assert } from 'assert'; +import { TestRedisServers, itWithClient, describeHandleMinimumRedisVersion } from '../test-utils'; +import { transformArguments } from './HRANDFIELD'; + +describe('HRANDFIELD', () => { + describeHandleMinimumRedisVersion([6, 2]); + + it('transformArguments', () => { + assert.deepEqual( + transformArguments('key'), + ['HRANDFIELD', 'key'] + ); + }); + + itWithClient(TestRedisServers.OPEN, 'client.hRandField', async client => { + assert.equal( + await client.hRandField('key'), + null + ); + }); +}); diff --git a/lib/commands/HRANDFIELD.ts b/lib/commands/HRANDFIELD.ts new file mode 100644 index 00000000000..e0c6ee392d5 --- /dev/null +++ b/lib/commands/HRANDFIELD.ts @@ -0,0 +1,9 @@ +import { transformReplyStringNull } from './generic-transformers'; + +export const FIRST_KEY_INDEX = 1; + +export function transformArguments(key: string): Array { + return ['HRANDFIELD', key]; +} + +export const transformReply = transformReplyStringNull; \ No newline at end of file diff --git a/lib/commands/HRANDFIELD_COUNT.spec.ts b/lib/commands/HRANDFIELD_COUNT.spec.ts new file mode 100644 index 00000000000..6954bd484af --- /dev/null +++ b/lib/commands/HRANDFIELD_COUNT.spec.ts @@ -0,0 +1,21 @@ +import { strict as assert } from 'assert'; +import { TestRedisServers, itWithClient, describeHandleMinimumRedisVersion } from '../test-utils'; +import { transformArguments } from './HRANDFIELD_COUNT'; + +describe('HRANDFIELD COUNT', () => { + describeHandleMinimumRedisVersion([6, 2, 5]); + + it('transformArguments', () => { + assert.deepEqual( + transformArguments('key', 1), + ['HRANDFIELD', 'key', '1'] + ); + }); + + itWithClient(TestRedisServers.OPEN, 'client.hRandFieldCount', async client => { + assert.deepEqual( + await client.hRandFieldCount('key', 1), + [] + ); + }); +}); diff --git a/lib/commands/HRANDFIELD_COUNT.ts b/lib/commands/HRANDFIELD_COUNT.ts new file mode 100644 index 00000000000..d615b86ee8b --- /dev/null +++ b/lib/commands/HRANDFIELD_COUNT.ts @@ -0,0 +1,13 @@ +import { transformReplyStringArray } from './generic-transformers'; +import { transformArguments as transformHRandFieldArguments } from './HRANDFIELD'; + +export { FIRST_KEY_INDEX } from './HRANDFIELD'; + +export function transformArguments(key: string, count: number): Array { + return [ + ...transformHRandFieldArguments(key), + count.toString() + ]; +} + +export const transformReply = transformReplyStringArray; diff --git a/lib/commands/HRANDFIELD_COUNT_WITHVALUES.spec.ts b/lib/commands/HRANDFIELD_COUNT_WITHVALUES.spec.ts new file mode 100644 index 00000000000..0c26cbc7938 --- /dev/null +++ b/lib/commands/HRANDFIELD_COUNT_WITHVALUES.spec.ts @@ -0,0 +1,21 @@ +import { strict as assert } from 'assert'; +import { TestRedisServers, itWithClient, describeHandleMinimumRedisVersion } from '../test-utils'; +import { transformArguments } from './HRANDFIELD_COUNT_WITHVALUES'; + +describe('HRANDFIELD COUNT WITHVALUES', () => { + describeHandleMinimumRedisVersion([6, 2, 5]); + + it('transformArguments', () => { + assert.deepEqual( + transformArguments('key', 1), + ['HRANDFIELD', 'key', '1', 'WITHVALUES'] + ); + }); + + itWithClient(TestRedisServers.OPEN, 'client.hRandFieldCountWithValues', async client => { + assert.deepEqual( + await client.hRandFieldCountWithValues('key', 1), + Object.create(null) + ); + }); +}); diff --git a/lib/commands/HRANDFIELD_COUNT_WITHVALUES.ts b/lib/commands/HRANDFIELD_COUNT_WITHVALUES.ts new file mode 100644 index 00000000000..53856c1984e --- /dev/null +++ b/lib/commands/HRANDFIELD_COUNT_WITHVALUES.ts @@ -0,0 +1,13 @@ +import { transformReplyTuples } from './generic-transformers'; +import { transformArguments as transformHRandFieldCountArguments } from './HRANDFIELD_COUNT'; + +export { FIRST_KEY_INDEX } from './HRANDFIELD_COUNT'; + +export function transformArguments(key: string, count: number): Array { + return [ + ...transformHRandFieldCountArguments(key, count), + 'WITHVALUES' + ]; +} + +export const transformReply = transformReplyTuples; diff --git a/lib/commands/HSCAN.spec.ts b/lib/commands/HSCAN.spec.ts new file mode 100644 index 00000000000..7441dd48d52 --- /dev/null +++ b/lib/commands/HSCAN.spec.ts @@ -0,0 +1,77 @@ +import { strict as assert } from 'assert'; +import { TestRedisServers, itWithClient } from '../test-utils'; +import { transformArguments, transformReply } from './HSCAN'; + +describe('HSCAN', () => { + describe('transformArguments', () => { + it('cusror only', () => { + assert.deepEqual( + transformArguments('key', 0), + ['HSCAN', 'key', '0'] + ); + }); + + it('with MATCH', () => { + assert.deepEqual( + transformArguments('key', 0, { + MATCH: 'pattern' + }), + ['HSCAN', 'key', '0', 'MATCH', 'pattern'] + ); + }); + + it('with COUNT', () => { + assert.deepEqual( + transformArguments('key', 0, { + COUNT: 1 + }), + ['HSCAN', 'key', '0', 'COUNT', '1'] + ); + }); + + it('with MATCH & COUNT', () => { + assert.deepEqual( + transformArguments('key', 0, { + MATCH: 'pattern', + COUNT: 1 + }), + ['HSCAN', 'key', '0', 'MATCH', 'pattern', 'COUNT', '1'] + ); + }); + }); + + describe('transformReply', () => { + it('without tuples', () => { + assert.deepEqual( + transformReply(['0', []]), + { + cursor: 0, + tuples: [] + } + ); + }); + + it('with tuples', () => { + assert.deepEqual( + transformReply(['0', ['field', 'value']]), + { + cursor: 0, + tuples: [{ + field: 'field', + value: 'value' + }] + } + ); + }); + }); + + itWithClient(TestRedisServers.OPEN, 'client.hScan', async client => { + assert.deepEqual( + await client.hScan('key', 0), + { + cursor: 0, + tuples: [] + } + ); + }); +}); diff --git a/lib/commands/HSCAN.ts b/lib/commands/HSCAN.ts new file mode 100644 index 00000000000..18b1355b594 --- /dev/null +++ b/lib/commands/HSCAN.ts @@ -0,0 +1,37 @@ +import { ScanOptions, pushScanArguments } from './generic-transformers'; + +export const FIRST_KEY_INDEX = 1; + +export const IS_READ_ONLY = true; + +export function transformArguments(key: string, cursor: number, options?: ScanOptions): Array { + return pushScanArguments([ + 'HSCAN', + key + ], cursor, options); +} + +export interface HScanTuple { + field: string; + value: string; +} + +interface HScanReply { + cursor: number; + tuples: Array; +} + +export function transformReply([cursor, rawTuples]: [string, Array]): HScanReply { + const parsedTuples = []; + for (let i = 0; i < rawTuples.length; i += 2) { + parsedTuples.push({ + field: rawTuples[i], + value: rawTuples[i + 1] + }); + } + + return { + cursor: Number(cursor), + tuples: parsedTuples + }; +} diff --git a/lib/commands/HSET.spec.ts b/lib/commands/HSET.spec.ts new file mode 100644 index 00000000000..af7bcb6eb20 --- /dev/null +++ b/lib/commands/HSET.spec.ts @@ -0,0 +1,44 @@ +import { strict as assert } from 'assert'; +import { transformArguments } from './HSET'; +import { TestRedisServers, itWithClient, TestRedisClusters, itWithCluster } from '../test-utils'; + +describe('HSET', () => { + describe('transformArguments', () => { + it('Map', () => { + assert.deepEqual( + transformArguments('key', new Map([['field', 'value']])), + ['HSET', 'key', 'field', 'value'] + ); + }); + + it('Array', () => { + assert.deepEqual( + transformArguments('key', [['field', 'value']]), + ['HSET', 'key', 'field', 'value'] + ); + }); + + it('Object', () => { + it('Array', () => { + assert.deepEqual( + transformArguments('key', { field: 'value' }), + ['HSET', 'key', 'field', 'value'] + ); + }); + }); + }); + + itWithClient(TestRedisServers.OPEN, 'client.hSet', async client => { + assert.equal( + await client.hSet('key', { field: 'value' }), + 1 + ); + }); + + itWithCluster(TestRedisClusters.OPEN, 'cluster.hSet', async cluster => { + assert.equal( + await cluster.hSet('key', { field: 'value' }), + 1 + ); + }); +}); \ No newline at end of file diff --git a/lib/commands/HSET.ts b/lib/commands/HSET.ts new file mode 100644 index 00000000000..3edaa64b4e8 --- /dev/null +++ b/lib/commands/HSET.ts @@ -0,0 +1,41 @@ +import { transformReplyString } from './generic-transformers'; + +type HSETObject = Record; + +type HSETMap = Map; + +type HSETTuples = Array<[string, string]> | Array; + +export const FIRST_KEY_INDEX = 1; + +export function transformArguments(key: string, value: HSETObject | HSETMap | HSETTuples): Array { + const args = ['HSET', key]; + + if (value instanceof Map) { + pushMap(args, value); + } else if (Array.isArray(value)) { + pushTuples(args, value); + } else if (typeof value === 'object' && value !== null) { + pushObject(args, value); + } + + return args; +} + +function pushMap(args: Array, map: HSETMap): void { + for (const [key, value] of map.entries()) { + args.push(key.toString(), value.toString()); + } +} + +function pushTuples(args: Array, tuples: HSETTuples): void { + args.push(...tuples.flat()); +} + +function pushObject(args: Array, object: HSETObject): void { + for (const key of Object.keys(object)) { + args.push(key.toString(), object[key].toString()); + } +} + +export const transformReply = transformReplyString; diff --git a/lib/commands/HSETNX.spec.ts b/lib/commands/HSETNX.spec.ts new file mode 100644 index 00000000000..f810c5e2b9a --- /dev/null +++ b/lib/commands/HSETNX.spec.ts @@ -0,0 +1,19 @@ +import { strict as assert } from 'assert'; +import { TestRedisServers, itWithClient } from '../test-utils'; +import { transformArguments } from './HSETNX'; + +describe('HSETNX', () => { + it('transformArguments', () => { + assert.deepEqual( + transformArguments('key', 'field', 'value'), + ['HSETNX', 'key', 'field', 'value'] + ); + }); + + itWithClient(TestRedisServers.OPEN, 'client.hSetNX', async client => { + assert.equal( + await client.hSetNX('key', 'field', 'value'), + true + ); + }); +}); diff --git a/lib/commands/HSETNX.ts b/lib/commands/HSETNX.ts new file mode 100644 index 00000000000..0eef8752529 --- /dev/null +++ b/lib/commands/HSETNX.ts @@ -0,0 +1,9 @@ +import { transformReplyBoolean } from './generic-transformers'; + +export const FIRST_KEY_INDEX = 1; + +export function transformArguments(key: string, field: string, value: string): Array { + return ['HSETNX', key, field, value]; +} + +export const transformReply = transformReplyBoolean; diff --git a/lib/commands/HSTRLEN.spec.ts b/lib/commands/HSTRLEN.spec.ts new file mode 100644 index 00000000000..35bf08d54c4 --- /dev/null +++ b/lib/commands/HSTRLEN.spec.ts @@ -0,0 +1,19 @@ +import { strict as assert } from 'assert'; +import { TestRedisServers, itWithClient } from '../test-utils'; +import { transformArguments } from './HSTRLEN'; + +describe('HSTRLEN', () => { + it('transformArguments', () => { + assert.deepEqual( + transformArguments('key', 'field'), + ['HSTRLEN', 'key', 'field'] + ); + }); + + itWithClient(TestRedisServers.OPEN, 'client.hStrLen', async client => { + assert.equal( + await client.hStrLen('key', 'field'), + 0 + ); + }); +}); diff --git a/lib/commands/HSTRLEN.ts b/lib/commands/HSTRLEN.ts new file mode 100644 index 00000000000..4181cde8517 --- /dev/null +++ b/lib/commands/HSTRLEN.ts @@ -0,0 +1,9 @@ +import { transformReplyNumber } from './generic-transformers'; + +export const FIRST_KEY_INDEX = 1; + +export function transformArguments(key: string, field: string): Array { + return ['HSTRLEN', key, field]; +} + +export const transformReply = transformReplyNumber; diff --git a/lib/commands/HVALS.spec.ts b/lib/commands/HVALS.spec.ts new file mode 100644 index 00000000000..9e6451f500e --- /dev/null +++ b/lib/commands/HVALS.spec.ts @@ -0,0 +1,19 @@ +import { strict as assert } from 'assert'; +import { TestRedisServers, itWithClient } from '../test-utils'; +import { transformArguments } from './HVALS'; + +describe('HVALS', () => { + it('transformArguments', () => { + assert.deepEqual( + transformArguments('key'), + ['HVALS', 'key'] + ); + }); + + itWithClient(TestRedisServers.OPEN, 'client.hVals', async client => { + assert.deepEqual( + await client.hVals('key'), + [] + ); + }); +}); diff --git a/lib/commands/HVALS.ts b/lib/commands/HVALS.ts new file mode 100644 index 00000000000..7f924623cf3 --- /dev/null +++ b/lib/commands/HVALS.ts @@ -0,0 +1,9 @@ +import { transformReplyStringArray } from './generic-transformers'; + +export const FIRST_KEY_INDEX = 1; + +export function transformArguments(key: string): Array { + return ['HVALS', key]; +} + +export const transformReply = transformReplyStringArray; diff --git a/lib/commands/INCR.spec.ts b/lib/commands/INCR.spec.ts new file mode 100644 index 00000000000..d64c3696af4 --- /dev/null +++ b/lib/commands/INCR.spec.ts @@ -0,0 +1,19 @@ +import { strict as assert } from 'assert'; +import { TestRedisServers, itWithClient } from '../test-utils'; +import { transformArguments } from './INCR'; + +describe('INCR', () => { + it('transformArguments', () => { + assert.deepEqual( + transformArguments('key'), + ['INCR', 'key'] + ); + }); + + itWithClient(TestRedisServers.OPEN, 'client.incr', async client => { + assert.equal( + await client.incr('key'), + 1 + ); + }); +}); diff --git a/lib/commands/INCR.ts b/lib/commands/INCR.ts new file mode 100644 index 00000000000..00747f0f7e2 --- /dev/null +++ b/lib/commands/INCR.ts @@ -0,0 +1,9 @@ +import { transformReplyNumber } from './generic-transformers'; + +export const FIRST_KEY_INDEX = 1; + +export function transformArguments(key: string): Array { + return ['INCR', key]; +} + +export const transformReply = transformReplyNumber; diff --git a/lib/commands/INCRBY.spec.ts b/lib/commands/INCRBY.spec.ts new file mode 100644 index 00000000000..875277570cb --- /dev/null +++ b/lib/commands/INCRBY.spec.ts @@ -0,0 +1,19 @@ +import { strict as assert } from 'assert'; +import { TestRedisServers, itWithClient } from '../test-utils'; +import { transformArguments } from './INCRBY'; + +describe('INCR', () => { + it('transformArguments', () => { + assert.deepEqual( + transformArguments('key', 1), + ['INCRBY', 'key', '1'] + ); + }); + + itWithClient(TestRedisServers.OPEN, 'client.incrBy', async client => { + assert.equal( + await client.incrBy('key', 1), + 1 + ); + }); +}); diff --git a/lib/commands/INCRBY.ts b/lib/commands/INCRBY.ts new file mode 100644 index 00000000000..8fd31d03380 --- /dev/null +++ b/lib/commands/INCRBY.ts @@ -0,0 +1,9 @@ +import { transformReplyNumber } from './generic-transformers'; + +export const FIRST_KEY_INDEX = 1; + +export function transformArguments(key: string, increment: number): Array { + return ['INCRBY', key, increment.toString()]; +} + +export const transformReply = transformReplyNumber; diff --git a/lib/commands/INCRBYFLOAT.spec.ts b/lib/commands/INCRBYFLOAT.spec.ts new file mode 100644 index 00000000000..fe062b62905 --- /dev/null +++ b/lib/commands/INCRBYFLOAT.spec.ts @@ -0,0 +1,19 @@ +import { strict as assert } from 'assert'; +import { TestRedisServers, itWithClient } from '../test-utils'; +import { transformArguments } from './INCRBYFLOAT'; + +describe('INCRBYFLOAT', () => { + it('transformArguments', () => { + assert.deepEqual( + transformArguments('key', 1.5), + ['INCRBYFLOAT', 'key', '1.5'] + ); + }); + + itWithClient(TestRedisServers.OPEN, 'client.incrByFloat', async client => { + assert.equal( + await client.incrByFloat('key', 1.5), + '1.5' + ); + }); +}); diff --git a/lib/commands/INCRBYFLOAT.ts b/lib/commands/INCRBYFLOAT.ts new file mode 100644 index 00000000000..38912cbdc94 --- /dev/null +++ b/lib/commands/INCRBYFLOAT.ts @@ -0,0 +1,9 @@ +import { transformReplyString } from './generic-transformers'; + +export const FIRST_KEY_INDEX = 1; + +export function transformArguments(key: string, increment: number): Array { + return ['INCRBYFLOAT', key, increment.toString()]; +} + +export const transformReply = transformReplyString; diff --git a/lib/commands/INFO.spec.ts b/lib/commands/INFO.spec.ts new file mode 100644 index 00000000000..118682c7da1 --- /dev/null +++ b/lib/commands/INFO.spec.ts @@ -0,0 +1,20 @@ +import { strict as assert } from 'assert'; +import { transformArguments } from './INFO'; + +describe('INFO', () => { + describe('transformArguments', () => { + it('simple', () => { + assert.deepEqual( + transformArguments(), + ['INFO'] + ); + }); + + it('server section', () => { + assert.deepEqual( + transformArguments('server'), + ['INFO', 'server'] + ); + }); + }); +}); diff --git a/lib/commands/INFO.ts b/lib/commands/INFO.ts new file mode 100644 index 00000000000..437b5e5b83b --- /dev/null +++ b/lib/commands/INFO.ts @@ -0,0 +1,15 @@ +import { transformReplyString } from './generic-transformers'; + +export const IS_READ_ONLY = true; + +export function transformArguments(section?: string): Array { + const args = ['INFO']; + + if (section) { + args.push(section); + } + + return args; +} + +export const transformReply = transformReplyString; diff --git a/lib/commands/KEYS.spec.ts b/lib/commands/KEYS.spec.ts new file mode 100644 index 00000000000..d11e8a0f58b --- /dev/null +++ b/lib/commands/KEYS.spec.ts @@ -0,0 +1,11 @@ +import { strict as assert } from 'assert'; +import { TestRedisServers, itWithClient } from '../test-utils'; + +describe('KEYS', () => { + itWithClient(TestRedisServers.OPEN, 'client.keys', async client => { + assert.deepEqual( + await client.keys('pattern'), + [] + ); + }); +}); diff --git a/lib/commands/KEYS.ts b/lib/commands/KEYS.ts new file mode 100644 index 00000000000..99c99c11521 --- /dev/null +++ b/lib/commands/KEYS.ts @@ -0,0 +1,7 @@ +export function transformArguments(pattern: string): Array { + return ['KEYS', pattern]; +} + +export function transformReply(keys: Array): Array { + return keys; +} \ No newline at end of file diff --git a/lib/commands/LASTSAVE.spec.ts b/lib/commands/LASTSAVE.spec.ts new file mode 100644 index 00000000000..1b13bed5d20 --- /dev/null +++ b/lib/commands/LASTSAVE.spec.ts @@ -0,0 +1,20 @@ +import { strict as assert } from 'assert'; +import { TestRedisServers, itWithClient, itWithCluster, TestRedisClusters } from '../test-utils'; +import { transformArguments } from './LASTSAVE'; + +describe('LASTSAVE', () => { + it('transformArguments', () => { + assert.deepEqual( + transformArguments(), + ['LASTSAVE'] + ); + }); + + itWithClient(TestRedisServers.OPEN, 'client.lastSave', async client => { + assert.ok((await client.lastSave()) instanceof Date); + }); + + itWithCluster(TestRedisClusters.OPEN, 'cluster.lastSave', async cluster => { + assert.ok((await cluster.lastSave()) instanceof Date); + }); +}); diff --git a/lib/commands/LASTSAVE.ts b/lib/commands/LASTSAVE.ts new file mode 100644 index 00000000000..76944d3548b --- /dev/null +++ b/lib/commands/LASTSAVE.ts @@ -0,0 +1,9 @@ +export const IS_READ_ONLY = true; + +export function transformArguments(): Array { + return ['LASTSAVE']; +} + +export function transformReply(reply: number): Date { + return new Date(reply); +} diff --git a/lib/commands/LINDEX.spec.ts b/lib/commands/LINDEX.spec.ts new file mode 100644 index 00000000000..74a6706ecdc --- /dev/null +++ b/lib/commands/LINDEX.spec.ts @@ -0,0 +1,26 @@ +import { strict as assert } from 'assert'; +import { TestRedisServers, itWithClient, itWithCluster, TestRedisClusters } from '../test-utils'; +import { transformArguments } from './LINDEX'; + +describe('LINDEX', () => { + it('transformArguments', () => { + assert.deepEqual( + transformArguments('key', 'element'), + ['LINDEX', 'key', 'element'] + ); + }); + + itWithClient(TestRedisServers.OPEN, 'client.lIndex', async client => { + assert.equal( + await client.lIndex('key', 'element'), + null + ); + }); + + itWithCluster(TestRedisClusters.OPEN, 'cluster.lIndex', async cluster => { + assert.equal( + await cluster.lIndex('key', 'element'), + null + ); + }); +}); diff --git a/lib/commands/LINDEX.ts b/lib/commands/LINDEX.ts new file mode 100644 index 00000000000..0237a4705b1 --- /dev/null +++ b/lib/commands/LINDEX.ts @@ -0,0 +1,11 @@ +import { transformReplyNumberNull } from './generic-transformers'; + +export const FIRST_KEY_INDEX = 1; + +export const IS_READ_ONLY = true; + +export function transformArguments(key: string, element: string): Array { + return ['LINDEX', key, element]; +} + +export const transformReply = transformReplyNumberNull; diff --git a/lib/commands/LINSERT.spec.ts b/lib/commands/LINSERT.spec.ts new file mode 100644 index 00000000000..286e61d06d6 --- /dev/null +++ b/lib/commands/LINSERT.spec.ts @@ -0,0 +1,26 @@ +import { strict as assert } from 'assert'; +import { TestRedisServers, itWithClient, itWithCluster, TestRedisClusters } from '../test-utils'; +import { transformArguments } from './LINSERT'; + +describe('LINSERT', () => { + it('transformArguments', () => { + assert.deepEqual( + transformArguments('key', 'BEFORE', 'pivot', 'element'), + ['LINSERT', 'key', 'BEFORE', 'pivot', 'element'] + ); + }); + + itWithClient(TestRedisServers.OPEN, 'client.lInsert', async client => { + assert.equal( + await client.lInsert('key', 'BEFORE', 'pivot', 'element'), + 0 + ); + }); + + itWithCluster(TestRedisClusters.OPEN, 'cluster.lLen', async cluster => { + assert.equal( + await cluster.lInsert('key', 'BEFORE', 'pivot', 'element'), + 0 + ); + }); +}); diff --git a/lib/commands/LINSERT.ts b/lib/commands/LINSERT.ts new file mode 100644 index 00000000000..40bd4e3d4df --- /dev/null +++ b/lib/commands/LINSERT.ts @@ -0,0 +1,22 @@ +import { transformReplyNumber } from './generic-transformers'; + +export const FIRST_KEY_INDEX = 1; + +type LInsertPosition = 'BEFORE' | 'AFTER'; + +export function transformArguments( + key: string, + position: LInsertPosition, + pivot: string, + element: string +): Array { + return [ + 'LINSERT', + key, + position, + pivot, + element + ]; +} + +export const transformReply = transformReplyNumber; diff --git a/lib/commands/LLEN.spec.ts b/lib/commands/LLEN.spec.ts new file mode 100644 index 00000000000..6e4581ddd1f --- /dev/null +++ b/lib/commands/LLEN.spec.ts @@ -0,0 +1,26 @@ +import { strict as assert } from 'assert'; +import { TestRedisServers, itWithClient, itWithCluster, TestRedisClusters } from '../test-utils'; +import { transformArguments } from './LLEN'; + +describe('LLEN', () => { + it('transformArguments', () => { + assert.deepEqual( + transformArguments('key'), + ['LLEN', 'key'] + ); + }); + + itWithClient(TestRedisServers.OPEN, 'client.lLen', async client => { + assert.equal( + await client.lLen('key'), + 0 + ); + }); + + itWithCluster(TestRedisClusters.OPEN, 'cluster.lLen', async cluster => { + assert.equal( + await cluster.lLen('key'), + 0 + ); + }); +}); diff --git a/lib/commands/LLEN.ts b/lib/commands/LLEN.ts new file mode 100644 index 00000000000..61aae604c97 --- /dev/null +++ b/lib/commands/LLEN.ts @@ -0,0 +1,11 @@ +import { transformReplyNumber } from './generic-transformers'; + +export const FIRST_KEY_INDEX = 1; + +export const IS_READ_ONLY = true; + +export function transformArguments(key: string): Array { + return ['LLEN', key]; +} + +export const transformReply = transformReplyNumber; \ No newline at end of file diff --git a/lib/commands/LMOVE.spec.ts b/lib/commands/LMOVE.spec.ts new file mode 100644 index 00000000000..bcb897f76ac --- /dev/null +++ b/lib/commands/LMOVE.spec.ts @@ -0,0 +1,28 @@ +import { strict as assert } from 'assert'; +import { TestRedisServers, itWithClient, itWithCluster, TestRedisClusters, describeHandleMinimumRedisVersion } from '../test-utils'; +import { transformArguments } from './LMOVE'; + +describe('LMOVE', () => { + describeHandleMinimumRedisVersion([6, 2]); + + it('transformArguments', () => { + assert.deepEqual( + transformArguments('source', 'destination', 'LEFT', 'RIGHT'), + ['LMOVE', 'source', 'destination', 'LEFT', 'RIGHT'] + ); + }); + + itWithClient(TestRedisServers.OPEN, 'client.lMove', async client => { + assert.equal( + await client.lMove('source', 'destination', 'LEFT', 'RIGHT'), + null + ); + }); + + itWithCluster(TestRedisClusters.OPEN, 'cluster.lMove', async cluster => { + assert.equal( + await cluster.lMove('{tag}source', '{tag}destination', 'LEFT', 'RIGHT'), + null + ); + }); +}); diff --git a/lib/commands/LMOVE.ts b/lib/commands/LMOVE.ts new file mode 100644 index 00000000000..1e99297d812 --- /dev/null +++ b/lib/commands/LMOVE.ts @@ -0,0 +1,22 @@ +import { transformReplyStringNull } from './generic-transformers'; + +export type LMoveSide = 'LEFT' | 'RIGHT'; + +export const FIRST_KEY_INDEX = 1; + +export function transformArguments( + source: string, + destination: string, + sourceSide: LMoveSide, + destinationSide: LMoveSide +): Array { + return [ + 'LMOVE', + source, + destination, + sourceSide, + destinationSide, + ]; +} + +export const transformReply = transformReplyStringNull; diff --git a/lib/commands/LOLWUT.spec.ts b/lib/commands/LOLWUT.spec.ts new file mode 100644 index 00000000000..8e77b85b599 --- /dev/null +++ b/lib/commands/LOLWUT.spec.ts @@ -0,0 +1,43 @@ +import { strict as assert } from 'assert'; +import { TestRedisServers, itWithClient, itWithCluster, TestRedisClusters } from '../test-utils'; +import { transformArguments } from './LOLWUT'; + +describe('LOLWUT', () => { + describe('transformArguments', () => { + it('simple', () => { + assert.deepEqual( + transformArguments(), + ['LOLWUT'] + ); + }); + + it('with version', () => { + assert.deepEqual( + transformArguments(5), + ['LOLWUT', 'VERSION', '5'] + ); + }); + + it('with version and optional arguments', () => { + assert.deepEqual( + transformArguments(5, 1, 2, 3), + ['LOLWUT', 'VERSION', '5', '1', '2', '3'] + ); + }); + }); + + + itWithClient(TestRedisServers.OPEN, 'client.LOLWUT', async client => { + assert.equal( + typeof (await client.LOLWUT()), + 'string' + ); + }); + + itWithCluster(TestRedisClusters.OPEN, 'cluster.LOLWUT', async cluster => { + assert.equal( + typeof (await cluster.LOLWUT()), + 'string' + ); + }); +}); diff --git a/lib/commands/LOLWUT.ts b/lib/commands/LOLWUT.ts new file mode 100644 index 00000000000..f0cd20d4471 --- /dev/null +++ b/lib/commands/LOLWUT.ts @@ -0,0 +1,19 @@ +import { transformReplyString } from './generic-transformers'; + +export const IS_READ_ONLY = true; + +export function transformArguments(version?: number, ...optionalArguments: Array): Array { + const args = ['LOLWUT']; + + if (version) { + args.push( + 'VERSION', + version.toString(), + ...optionalArguments.map(String), + ); + } + + return args; +} + +export const transformReply = transformReplyString; diff --git a/lib/commands/LPOP.spec.ts b/lib/commands/LPOP.spec.ts new file mode 100644 index 00000000000..b593f657427 --- /dev/null +++ b/lib/commands/LPOP.spec.ts @@ -0,0 +1,26 @@ +import { strict as assert } from 'assert'; +import { TestRedisServers, itWithClient, itWithCluster, TestRedisClusters } from '../test-utils'; +import { transformArguments } from './LPOP'; + +describe('LPOP', () => { + it('transformArguments', () => { + assert.deepEqual( + transformArguments('key'), + ['LPOP', 'key'] + ); + }); + + itWithClient(TestRedisServers.OPEN, 'client.lPop', async client => { + assert.equal( + await client.lPop('key'), + null + ); + }); + + itWithCluster(TestRedisClusters.OPEN, 'cluster.lPop', async cluster => { + assert.equal( + await cluster.lPop('key'), + null + ); + }); +}); diff --git a/lib/commands/LPOP.ts b/lib/commands/LPOP.ts new file mode 100644 index 00000000000..30595a5491a --- /dev/null +++ b/lib/commands/LPOP.ts @@ -0,0 +1,9 @@ +import { transformReplyStringNull } from './generic-transformers'; + +export const FIRST_KEY_INDEX = 1; + +export function transformArguments(key: string): Array { + return ['LPOP', key]; +} + +export const transformReply = transformReplyStringNull; diff --git a/lib/commands/LPOP_COUNT.spec.ts b/lib/commands/LPOP_COUNT.spec.ts new file mode 100644 index 00000000000..89150dbf4de --- /dev/null +++ b/lib/commands/LPOP_COUNT.spec.ts @@ -0,0 +1,28 @@ +import { strict as assert } from 'assert'; +import { TestRedisServers, itWithClient, itWithCluster, TestRedisClusters, describeHandleMinimumRedisVersion } from '../test-utils'; +import { transformArguments } from './LPOP_COUNT'; + +describe('LPOP COUNT', () => { + describeHandleMinimumRedisVersion([6, 2]); + + it('transformArguments', () => { + assert.deepEqual( + transformArguments('key', 1), + ['LPOP', 'key', '1'] + ); + }); + + itWithClient(TestRedisServers.OPEN, 'client.lPopCount', async client => { + assert.equal( + await client.lPopCount('key', 1), + null + ); + }); + + itWithCluster(TestRedisClusters.OPEN, 'cluster.lPop', async cluster => { + assert.equal( + await cluster.lPopCount('key', 1), + null + ); + }); +}); diff --git a/lib/commands/LPOP_COUNT.ts b/lib/commands/LPOP_COUNT.ts new file mode 100644 index 00000000000..432d2c47c09 --- /dev/null +++ b/lib/commands/LPOP_COUNT.ts @@ -0,0 +1,9 @@ +import { transformReplyStringArrayNull } from './generic-transformers'; + +export const FIRST_KEY_INDEX = 1; + +export function transformArguments(key: string, count: number): Array { + return ['LPOP', key, count.toString()]; +} + +export const transformReply = transformReplyStringArrayNull; diff --git a/lib/commands/LPOS.spec.ts b/lib/commands/LPOS.spec.ts new file mode 100644 index 00000000000..1cf9e35209d --- /dev/null +++ b/lib/commands/LPOS.spec.ts @@ -0,0 +1,58 @@ +import { strict as assert } from 'assert'; +import { TestRedisServers, itWithClient, TestRedisClusters, itWithCluster, describeHandleMinimumRedisVersion } from '../test-utils'; +import { transformArguments } from './LPOS'; + +describe('LPOS', () => { + describeHandleMinimumRedisVersion([6, 0, 6]); + + describe('transformArguments', () => { + it('simple', () => { + assert.deepEqual( + transformArguments('key', 'element'), + ['LPOS', 'key', 'element'] + ); + }); + + it('with RANK', () => { + assert.deepEqual( + transformArguments('key', 'element', { + RANK: 0 + }), + ['LPOS', 'key', 'element', 'RANK', '0'] + ); + }); + + it('with MAXLEN', () => { + assert.deepEqual( + transformArguments('key', 'element', { + MAXLEN: 10 + }), + ['LPOS', 'key', 'element', 'MAXLEN', '10'] + ); + }); + + it('with RANK, MAXLEN', () => { + assert.deepEqual( + transformArguments('key', 'element', { + RANK: 0, + MAXLEN: 10 + }), + ['LPOS', 'key', 'element', 'RANK', '0', 'MAXLEN', '10'] + ); + }); + }); + + itWithClient(TestRedisServers.OPEN, 'client.lPos', async client => { + assert.equal( + await client.lPos('key', 'element'), + null + ); + }); + + itWithCluster(TestRedisClusters.OPEN, 'cluster.lPos', async cluster => { + assert.equal( + await cluster.lPos('key', 'element'), + null + ); + }); +}); diff --git a/lib/commands/LPOS.ts b/lib/commands/LPOS.ts new file mode 100644 index 00000000000..fc160dbcbb8 --- /dev/null +++ b/lib/commands/LPOS.ts @@ -0,0 +1,26 @@ +import { transformReplyNumberNull } from './generic-transformers'; + +export const FIRST_KEY_INDEX = 1; + +export const IS_READ_ONLY = true; + +export interface LPosOptions { + RANK?: number; + MAXLEN?: number; +} + +export function transformArguments(key: string, element: string, options?: LPosOptions): Array { + const args = ['LPOS', key, element]; + + if (typeof options?.RANK === 'number') { + args.push('RANK', options.RANK.toString()); + } + + if (typeof options?.MAXLEN === 'number') { + args.push('MAXLEN', options.MAXLEN.toString()); + } + + return args; +} + +export const transformReply = transformReplyNumberNull; diff --git a/lib/commands/LPOS_COUNT.spec.ts b/lib/commands/LPOS_COUNT.spec.ts new file mode 100644 index 00000000000..1d80bd45c3c --- /dev/null +++ b/lib/commands/LPOS_COUNT.spec.ts @@ -0,0 +1,58 @@ +import { strict as assert } from 'assert'; +import { TestRedisServers, itWithClient, itWithCluster, TestRedisClusters, describeHandleMinimumRedisVersion } from '../test-utils'; +import { transformArguments } from './LPOS_COUNT'; + +describe('LPOS COUNT', () => { + describeHandleMinimumRedisVersion([6, 0, 6]); + + describe('transformArguments', () => { + it('simple', () => { + assert.deepEqual( + transformArguments('key', 'element', 0), + ['LPOS', 'key', 'element', 'COUNT', '0'] + ); + }); + + it('with RANK', () => { + assert.deepEqual( + transformArguments('key', 'element', 0, { + RANK: 0 + }), + ['LPOS', 'key', 'element', 'RANK', '0', 'COUNT', '0'] + ); + }); + + it('with MAXLEN', () => { + assert.deepEqual( + transformArguments('key', 'element', 0, { + MAXLEN: 10 + }), + ['LPOS', 'key', 'element', 'COUNT', '0', 'MAXLEN', '10'] + ); + }); + + it('with RANK, MAXLEN', () => { + assert.deepEqual( + transformArguments('key', 'element', 0, { + RANK: 0, + MAXLEN: 10 + }), + ['LPOS', 'key', 'element', 'RANK', '0', 'COUNT', '0', 'MAXLEN', '10'] + ); + }); + }); + + itWithClient(TestRedisServers.OPEN, 'client.lPosCount', async client => { + assert.deepEqual( + await client.lPosCount('key', 'element', 0), + [] + ); + }); + + itWithCluster(TestRedisClusters.OPEN, 'cluster.lPosCount', async cluster => { + assert.deepEqual( + await cluster.lPosCount('key', 'element', 0), + [] + ); + }); +}); diff --git a/lib/commands/LPOS_COUNT.ts b/lib/commands/LPOS_COUNT.ts new file mode 100644 index 00000000000..2a1d3068583 --- /dev/null +++ b/lib/commands/LPOS_COUNT.ts @@ -0,0 +1,22 @@ +import { transformReplyNumberArray } from './generic-transformers'; +import { LPosOptions } from './LPOS'; + +export { FIRST_KEY_INDEX, IS_READ_ONLY } from './LPOS'; + +export function transformArguments(key: string, element: string, count: number, options?: LPosOptions): Array { + const args = ['LPOS', key, element]; + + if (typeof options?.RANK === 'number') { + args.push('RANK', options.RANK.toString()); + } + + args.push('COUNT', count.toString()); + + if (typeof options?.MAXLEN === 'number') { + args.push('MAXLEN', options.MAXLEN.toString()); + } + + return args; +} + +export const transformReply = transformReplyNumberArray; diff --git a/lib/commands/LPUSH.spec.ts b/lib/commands/LPUSH.spec.ts new file mode 100644 index 00000000000..44cf8c12d5f --- /dev/null +++ b/lib/commands/LPUSH.spec.ts @@ -0,0 +1,35 @@ +import { strict as assert } from 'assert'; +import { TestRedisServers, itWithClient, itWithCluster, TestRedisClusters } from '../test-utils'; +import { transformArguments } from './LPUSH'; + +describe('LPUSH', () => { + describe('transformArguments', () => { + it('string', () => { + assert.deepEqual( + transformArguments('key', 'field'), + ['LPUSH', 'key', 'field'] + ); + }); + + it('array', () => { + assert.deepEqual( + transformArguments('key', ['1', '2']), + ['LPUSH', 'key', '1', '2'] + ); + }); + }); + + itWithClient(TestRedisServers.OPEN, 'client.lPush', async client => { + assert.equal( + await client.lPush('key', 'field'), + 1 + ); + }); + + itWithCluster(TestRedisClusters.OPEN, 'cluster.lPush', async cluster => { + assert.equal( + await cluster.lPush('key', 'field'), + 1 + ); + }); +}); diff --git a/lib/commands/LPUSH.ts b/lib/commands/LPUSH.ts new file mode 100644 index 00000000000..434ad619cb7 --- /dev/null +++ b/lib/commands/LPUSH.ts @@ -0,0 +1,8 @@ +import { pushVerdictArguments, transformReplyNumber } from './generic-transformers'; + +export const FIRST_KEY_INDEX = 1; + +export function transformArguments(key: string, elements: string | Array): Array { + return pushVerdictArguments(['LPUSH', key], elements);} + +export const transformReply = transformReplyNumber; diff --git a/lib/commands/LPUSHX.spec.ts b/lib/commands/LPUSHX.spec.ts new file mode 100644 index 00000000000..1150c4d64dd --- /dev/null +++ b/lib/commands/LPUSHX.spec.ts @@ -0,0 +1,35 @@ +import { strict as assert } from 'assert'; +import { TestRedisServers, itWithClient, itWithCluster, TestRedisClusters } from '../test-utils'; +import { transformArguments } from './LPUSHX'; + +describe('LPUSHX', () => { + describe('transformArguments', () => { + it('string', () => { + assert.deepEqual( + transformArguments('key', 'element'), + ['LPUSHX', 'key', 'element'] + ); + }); + + it('array', () => { + assert.deepEqual( + transformArguments('key', ['1', '2']), + ['LPUSHX', 'key', '1', '2'] + ); + }); + }); + + itWithClient(TestRedisServers.OPEN, 'client.lPushX', async client => { + assert.equal( + await client.lPushX('key', 'element'), + 0 + ); + }); + + itWithCluster(TestRedisClusters.OPEN, 'cluster.lPushX', async cluster => { + assert.equal( + await cluster.lPushX('key', 'element'), + 0 + ); + }); +}); diff --git a/lib/commands/LPUSHX.ts b/lib/commands/LPUSHX.ts new file mode 100644 index 00000000000..f1a989d9625 --- /dev/null +++ b/lib/commands/LPUSHX.ts @@ -0,0 +1,9 @@ +import { pushVerdictArguments, transformReplyNumber } from './generic-transformers'; + +export const FIRST_KEY_INDEX = 1; + +export function transformArguments(key: string, element: string | Array): Array { + return pushVerdictArguments(['LPUSHX', key], element); +} + +export const transformReply = transformReplyNumber; diff --git a/lib/commands/LRANGE.spec.ts b/lib/commands/LRANGE.spec.ts new file mode 100644 index 00000000000..843b7b8815e --- /dev/null +++ b/lib/commands/LRANGE.spec.ts @@ -0,0 +1,27 @@ + +import { strict as assert } from 'assert'; +import { TestRedisServers, itWithClient, itWithCluster, TestRedisClusters } from '../test-utils'; +import { transformArguments } from './LRANGE'; + +describe('LRANGE', () => { + it('transformArguments', () => { + assert.deepEqual( + transformArguments('key', 0, -1), + ['LRANGE', 'key', '0', '-1'] + ); + }); + + itWithClient(TestRedisServers.OPEN, 'client.lRange', async client => { + assert.deepEqual( + await client.lRange('key', 0, -1), + [] + ); + }); + + itWithCluster(TestRedisClusters.OPEN, 'cluster.lRange', async cluster => { + assert.deepEqual( + await cluster.lRange('key', 0, -1), + [] + ); + }); +}); diff --git a/lib/commands/LRANGE.ts b/lib/commands/LRANGE.ts new file mode 100644 index 00000000000..cbed9a75ded --- /dev/null +++ b/lib/commands/LRANGE.ts @@ -0,0 +1,16 @@ +import { transformReplyStringArray } from './generic-transformers'; + +export const FIRST_KEY_INDEX = 1; + +export const IS_READ_ONLY = true; + +export function transformArguments(key: string, start: number, stop: number): Array { + return [ + 'LRANGE', + key, + start.toString(), + stop.toString() + ]; +} + +export const transformReply = transformReplyStringArray; diff --git a/lib/commands/LREM.spec.ts b/lib/commands/LREM.spec.ts new file mode 100644 index 00000000000..e2f027ffeb8 --- /dev/null +++ b/lib/commands/LREM.spec.ts @@ -0,0 +1,27 @@ + +import { strict as assert } from 'assert'; +import { TestRedisServers, itWithClient, itWithCluster, TestRedisClusters } from '../test-utils'; +import { transformArguments } from './LREM'; + +describe('LREM', () => { + it('transformArguments', () => { + assert.deepEqual( + transformArguments('key', 0, 'element'), + ['LREM', 'key', '0', 'element'] + ); + }); + + itWithClient(TestRedisServers.OPEN, 'client.lRem', async client => { + assert.equal( + await client.lRem('key', 0, 'element'), + 0 + ); + }); + + itWithCluster(TestRedisClusters.OPEN, 'cluster.lRem', async cluster => { + assert.equal( + await cluster.lRem('key', 0, 'element'), + 0 + ); + }); +}); diff --git a/lib/commands/LREM.ts b/lib/commands/LREM.ts new file mode 100644 index 00000000000..5eabbc9194e --- /dev/null +++ b/lib/commands/LREM.ts @@ -0,0 +1,14 @@ +import { transformReplyNumber } from './generic-transformers'; + +export const FIRST_KEY_INDEX = 1; + +export function transformArguments(key: string, count: number, element: string): Array { + return [ + 'LREM', + key, + count.toString(), + element + ]; +} + +export const transformReply = transformReplyNumber; diff --git a/lib/commands/LSET.spec.ts b/lib/commands/LSET.spec.ts new file mode 100644 index 00000000000..a5fe78cf4c3 --- /dev/null +++ b/lib/commands/LSET.spec.ts @@ -0,0 +1,28 @@ +import { strict as assert } from 'assert'; +import { TestRedisServers, itWithClient, itWithCluster, TestRedisClusters } from '../test-utils'; +import { transformArguments } from './LSET'; + +describe('LSET', () => { + it('transformArguments', () => { + assert.deepEqual( + transformArguments('key', 0, 'element'), + ['LSET', 'key', '0', 'element'] + ); + }); + + itWithClient(TestRedisServers.OPEN, 'client.lSet', async client => { + await client.lPush('key', 'element'); + assert.equal( + await client.lSet('key', 0, 'element'), + 'OK' + ); + }); + + itWithCluster(TestRedisClusters.OPEN, 'cluster.lSet', async cluster => { + await cluster.lPush('key', 'element'); + assert.equal( + await cluster.lSet('key', 0, 'element'), + 'OK' + ); + }); +}); diff --git a/lib/commands/LSET.ts b/lib/commands/LSET.ts new file mode 100644 index 00000000000..0e910dd6a1c --- /dev/null +++ b/lib/commands/LSET.ts @@ -0,0 +1,14 @@ +import { transformReplyString } from './generic-transformers'; + +export const FIRST_KEY_INDEX = 1; + +export function transformArguments(key: string, index: number, element: string): Array { + return [ + 'LSET', + key, + index.toString(), + element + ]; +} + +export const transformReply = transformReplyString; diff --git a/lib/commands/LTRIM.spec.ts b/lib/commands/LTRIM.spec.ts new file mode 100644 index 00000000000..8092ba6af1e --- /dev/null +++ b/lib/commands/LTRIM.spec.ts @@ -0,0 +1,26 @@ +import { strict as assert } from 'assert'; +import { TestRedisServers, itWithClient, itWithCluster, TestRedisClusters } from '../test-utils'; +import { transformArguments } from './LTRIM'; + +describe('LTRIM', () => { + it('transformArguments', () => { + assert.deepEqual( + transformArguments('key', 0, -1), + ['LTRIM', 'key', '0', '-1'] + ); + }); + + itWithClient(TestRedisServers.OPEN, 'client.lTrim', async client => { + assert.equal( + await client.lTrim('key', 0, -1), + 'OK' + ); + }); + + itWithCluster(TestRedisClusters.OPEN, 'cluster.lTrim', async cluster => { + assert.equal( + await cluster.lTrim('key', 0, -1), + 'OK' + ); + }); +}); diff --git a/lib/commands/LTRIM.ts b/lib/commands/LTRIM.ts new file mode 100644 index 00000000000..3ccfa751af6 --- /dev/null +++ b/lib/commands/LTRIM.ts @@ -0,0 +1,14 @@ +import { transformReplyString } from './generic-transformers'; + +export const FIRST_KEY_INDEX = 1; + +export function transformArguments(key: string, start: number, stop: number): Array { + return [ + 'LTRIM', + key, + start.toString(), + stop.toString() + ] +} + +export const transformReply = transformReplyString; diff --git a/lib/commands/MEMORY_DOCTOR.spec.ts b/lib/commands/MEMORY_DOCTOR.spec.ts new file mode 100644 index 00000000000..da883deeb77 --- /dev/null +++ b/lib/commands/MEMORY_DOCTOR.spec.ts @@ -0,0 +1,26 @@ +import { strict as assert } from 'assert'; +import { TestRedisServers, itWithClient, TestRedisClusters, itWithCluster } from '../test-utils'; +import { transformArguments } from './MEMORY_DOCTOR'; + +describe('MEMORY DOCTOR', () => { + it('transformArguments', () => { + assert.deepEqual( + transformArguments(), + ['MEMORY', 'DOCTOR'] + ); + }); + + itWithClient(TestRedisServers.OPEN, 'client.memoryDoctor', async client => { + assert.equal( + typeof (await client.memoryDoctor()), + 'string' + ); + }); + + itWithCluster(TestRedisClusters.OPEN, 'cluster.memoryDoctor', async cluster => { + assert.equal( + typeof (await cluster.memoryDoctor()), + 'string' + ); + }); +}); diff --git a/lib/commands/MEMORY_DOCTOR.ts b/lib/commands/MEMORY_DOCTOR.ts new file mode 100644 index 00000000000..0d02bf93360 --- /dev/null +++ b/lib/commands/MEMORY_DOCTOR.ts @@ -0,0 +1,7 @@ +import { transformReplyString } from './generic-transformers'; + +export function transformArguments(): Array { + return ['MEMORY', 'DOCTOR']; +} + +export const transformReply = transformReplyString; diff --git a/lib/commands/MEMORY_MALLOC-STATS.spec.ts b/lib/commands/MEMORY_MALLOC-STATS.spec.ts new file mode 100644 index 00000000000..2750ebdf7a0 --- /dev/null +++ b/lib/commands/MEMORY_MALLOC-STATS.spec.ts @@ -0,0 +1,26 @@ +import { strict as assert } from 'assert'; +import { TestRedisServers, itWithClient, TestRedisClusters, itWithCluster } from '../test-utils'; +import { transformArguments } from './MEMORY_MALLOC-STATS'; + +describe('MEMORY MALLOC-STATS', () => { + it('transformArguments', () => { + assert.deepEqual( + transformArguments(), + ['MEMORY', 'MALLOC-STATS'] + ); + }); + + itWithClient(TestRedisServers.OPEN, 'client.memoryMallocStats', async client => { + assert.equal( + typeof (await client.memoryDoctor()), + 'string' + ); + }); + + itWithCluster(TestRedisClusters.OPEN, 'cluster.memoryDoctor', async cluster => { + assert.equal( + typeof (await cluster.memoryDoctor()), + 'string' + ); + }); +}); diff --git a/lib/commands/MEMORY_MALLOC-STATS.ts b/lib/commands/MEMORY_MALLOC-STATS.ts new file mode 100644 index 00000000000..7dd997c48b1 --- /dev/null +++ b/lib/commands/MEMORY_MALLOC-STATS.ts @@ -0,0 +1,7 @@ +import { transformReplyString } from './generic-transformers'; + +export function transformArguments(): Array { + return ['MEMORY', 'MALLOC-STATS']; +} + +export const transformReply = transformReplyString; diff --git a/lib/commands/MEMORY_PURGE.spec.ts b/lib/commands/MEMORY_PURGE.spec.ts new file mode 100644 index 00000000000..ac9198ccfc8 --- /dev/null +++ b/lib/commands/MEMORY_PURGE.spec.ts @@ -0,0 +1,26 @@ +import { strict as assert } from 'assert'; +import { TestRedisServers, itWithClient, TestRedisClusters, itWithCluster } from '../test-utils'; +import { transformArguments } from './MEMORY_PURGE'; + +describe('MEMORY PURGE', () => { + it('transformArguments', () => { + assert.deepEqual( + transformArguments(), + ['MEMORY', 'PURGE'] + ); + }); + + itWithClient(TestRedisServers.OPEN, 'client.memoryPurge', async client => { + assert.equal( + await client.memoryPurge(), + 'OK' + ); + }); + + itWithCluster(TestRedisClusters.OPEN, 'cluster.memoryPurge', async cluster => { + assert.equal( + await cluster.memoryPurge(), + 'OK' + ); + }); +}); diff --git a/lib/commands/MEMORY_PURGE.ts b/lib/commands/MEMORY_PURGE.ts new file mode 100644 index 00000000000..7aaeee7e6aa --- /dev/null +++ b/lib/commands/MEMORY_PURGE.ts @@ -0,0 +1,7 @@ +import { transformReplyString } from './generic-transformers'; + +export function transformArguments(): Array { + return ['MEMORY', 'PURGE']; +} + +export const transformReply = transformReplyString; diff --git a/lib/commands/MEMORY_STATS.spec.ts b/lib/commands/MEMORY_STATS.spec.ts new file mode 100644 index 00000000000..12aa21181e6 --- /dev/null +++ b/lib/commands/MEMORY_STATS.spec.ts @@ -0,0 +1,108 @@ +import { strict as assert } from 'assert'; +import { transformArguments, transformReply } from './MEMORY_STATS'; + +describe('MEMORY STATS', () => { + it('transformArguments', () => { + assert.deepEqual( + transformArguments(), + ['MEMORY', 'STATS'] + ); + }); + + it('transformReply', () => { + assert.deepEqual( + transformReply([ + 'peak.allocated', + 952728, + 'total.allocated', + 892904, + 'startup.allocated', + 809952, + 'replication.backlog', + 0, + 'clients.slaves', + 0, + 'clients.normal', + 41000, + 'aof.buffer', + 0, + 'lua.caches', + 0, + 'db.0', + [ + 'overhead.hashtable.main', + 72, + 'overhead.hashtable.expires', + 0 + ], + 'overhead.total', + 850952, + 'keys.count', + 0, + 'keys.bytes-per-key', + 0, + 'dataset.bytes', + 41952, + 'dataset.percentage', + '50.573825836181641', + 'peak.percentage', + '93.720771789550781', + 'allocator.allocated', + 937632, + 'allocator.active', + 1191936, + 'allocator.resident', + 4005888, + 'allocator-fragmentation.ratio', + '1.2712193727493286', + 'allocator-fragmentation.bytes', + 254304, + 'allocator-rss.ratio', + '3.3608248233795166', + 'allocator-rss.bytes', + 2813952, + 'rss-overhead.ratio', + '2.4488751888275146', + 'rss-overhead.bytes', + 5804032, + 'fragmentation', + '11.515504837036133', + 'fragmentation.bytes', + 8958032 + ]), + { + peakAllocated: 952728, + totalAllocated: 892904, + startupAllocated: 809952, + replicationBacklog: 0, + clientsReplicas: 0, + clientsNormal: 41000, + aofBuffer: 0, + luaCaches: 0, + overheadTotal: 850952, + keysCount: 0, + keysBytesPerKey: 0, + datasetBytes: 41952, + datasetPercentage: 50.573825836181641, + peakPercentage: 93.720771789550781, + allocatorAllocated: 937632, + allocatorActive: 1191936, + allocatorResident: 4005888, + allocatorFragmentationRatio: 1.2712193727493286, + allocatorFragmentationBytes: 254304, + allocatorRssRatio: 3.3608248233795166, + allocatorRssBytes: 2813952, + rssOverheadRatio: 2.4488751888275146, + rssOverheadBytes: 5804032, + fragmentation: 11.515504837036133, + fragmentationBytes: 8958032, + db: { + 0: { + overheadHashtableMain: 72, + overheadHashtableExpires: 0 + } + } + } + ); + }); +}); diff --git a/lib/commands/MEMORY_STATS.ts b/lib/commands/MEMORY_STATS.ts new file mode 100644 index 00000000000..8ae83d2239a --- /dev/null +++ b/lib/commands/MEMORY_STATS.ts @@ -0,0 +1,93 @@ +export function transformArguments(): Array { + return ['MEMORY', 'STATS']; +} + +interface MemoryStatsReply { + peakAllocated: number; + totalAllocated: number; + startupAllocated: number; + replicationBacklog: number; + clientsReplicas: number; + clientsNormal: number; + aofBuffer: number; + luaCaches: number; + overheadTotal: number; + keysCount: number; + keysBytesPerKey: number; + datasetBytes: number; + datasetPercentage: number; + peakPercentage: number; + allocatorAllocated?: number, + allocatorActive?: number; + allocatorResident?: number; + allocatorFragmentationRatio?: number; + allocatorFragmentationBytes?: number; + allocatorRssRatio?: number; + allocatorRssBytes?: number; + rssOverheadRatio?: number; + rssOverheadBytes?: number; + fragmentation?: number; + fragmentationBytes: number; + db: { + [key: number]: { + overheadHashtableMain: number; + overheadHashtableExpires: number; + }; + }; +} + +const FIELDS_MAPPING = { + 'peak.allocated': 'peakAllocated', + 'total.allocated': 'totalAllocated', + 'startup.allocated': 'startupAllocated', + 'replication.backlog': 'replicationBacklog', + 'clients.slaves': 'clientsReplicas', + 'clients.normal': 'clientsNormal', + 'aof.buffer': 'aofBuffer', + 'lua.caches': 'luaCaches', + 'overhead.total': 'overheadTotal', + 'keys.count': 'keysCount', + 'keys.bytes-per-key': 'keysBytesPerKey', + 'dataset.bytes': 'datasetBytes', + 'dataset.percentage': 'datasetPercentage', + 'peak.percentage': 'peakPercentage', + 'allocator.allocated': 'allocatorAllocated', + 'allocator.active': 'allocatorActive', + 'allocator.resident': 'allocatorResident', + 'allocator-fragmentation.ratio': 'allocatorFragmentationRatio', + 'allocator-fragmentation.bytes': 'allocatorFragmentationBytes', + 'allocator-rss.ratio': 'allocatorRssRatio', + 'allocator-rss.bytes': 'allocatorRssBytes', + 'rss-overhead.ratio': 'rssOverheadRatio', + 'rss-overhead.bytes': 'rssOverheadBytes', + 'fragmentation': 'fragmentation', + 'fragmentation.bytes': 'fragmentationBytes' + }, + DB_FIELDS_MAPPING = { + 'overhead.hashtable.main': 'overheadHashtableMain', + 'overhead.hashtable.expires': 'overheadHashtableExpires' + }; + +export function transformReply(rawReply: Array>): MemoryStatsReply { + const reply: any = { + db: {} + }; + + for (let i = 0; i < rawReply.length; i += 2) { + const key = rawReply[i] as string; + if (key.startsWith('db.')) { + const dbTuples = rawReply[i + 1] as Array, + db: any = {}; + for (let j = 0; j < dbTuples.length; j += 2) { + db[DB_FIELDS_MAPPING[dbTuples[j] as keyof typeof DB_FIELDS_MAPPING]] = dbTuples[j + 1]; + } + + reply.db[key.substring(3)] = db; + continue; + } + + reply[FIELDS_MAPPING[key as keyof typeof FIELDS_MAPPING]] = Number(rawReply[i + 1]); + } + + return reply as MemoryStatsReply; +} diff --git a/lib/commands/MEMORY_USAGE.spec.ts b/lib/commands/MEMORY_USAGE.spec.ts new file mode 100644 index 00000000000..7487e7e4ffc --- /dev/null +++ b/lib/commands/MEMORY_USAGE.spec.ts @@ -0,0 +1,37 @@ +import { strict as assert } from 'assert'; +import { TestRedisServers, itWithClient, TestRedisClusters, itWithCluster } from '../test-utils'; +import { transformArguments } from './MEMORY_USAGE'; + +describe('MEMORY USAGE', () => { + describe('transformArguments', () => { + it('simple', () => { + assert.deepEqual( + transformArguments('key'), + ['MEMORY', 'USAGE', 'key'] + ); + }); + + it('with SAMPLES', () => { + assert.deepEqual( + transformArguments('key', { + SAMPLES: 1 + }), + ['MEMORY', 'USAGE', 'key', 'SAMPLES', '1'] + ); + }); + }); + + itWithClient(TestRedisServers.OPEN, 'client.memoryUsage', async client => { + assert.equal( + await client.memoryUsage('key'), + null + ); + }); + + itWithCluster(TestRedisClusters.OPEN, 'cluster.memoryUsage', async cluster => { + assert.equal( + await cluster.memoryUsage('key'), + null + ); + }); +}); diff --git a/lib/commands/MEMORY_USAGE.ts b/lib/commands/MEMORY_USAGE.ts new file mode 100644 index 00000000000..0868b162268 --- /dev/null +++ b/lib/commands/MEMORY_USAGE.ts @@ -0,0 +1,21 @@ +import { transformReplyNumberNull } from './generic-transformers'; + +export const FIRST_KEY_INDEX = 1; + +export const IS_READ_ONLY = true; + +interface MemoryUsageOptions { + SAMPLES?: number; +} + +export function transformArguments(key: string, options?: MemoryUsageOptions): Array { + const args = ['MEMORY', 'USAGE', key]; + + if (options?.SAMPLES) { + args.push('SAMPLES', options.SAMPLES.toString()); + } + + return args; +} + +export const transformReply = transformReplyNumberNull; diff --git a/lib/commands/MGET.spec.ts b/lib/commands/MGET.spec.ts new file mode 100644 index 00000000000..c8c020fe433 --- /dev/null +++ b/lib/commands/MGET.spec.ts @@ -0,0 +1,26 @@ +import { strict as assert } from 'assert'; +import { TestRedisServers, itWithClient, TestRedisClusters, itWithCluster } from '../test-utils'; +import { transformArguments } from './MGET'; + +describe('MGET', () => { + it('transformArguments', () => { + assert.deepEqual( + transformArguments(['1', '2']), + ['MGET', '1', '2'] + ); + }); + + itWithClient(TestRedisServers.OPEN, 'client.mGet', async client => { + assert.deepEqual( + await client.mGet(['key']), + [null] + ); + }); + + itWithCluster(TestRedisClusters.OPEN, 'cluster.mGet', async cluster => { + assert.deepEqual( + await cluster.mGet(['key']), + [null] + ); + }); +}); diff --git a/lib/commands/MGET.ts b/lib/commands/MGET.ts new file mode 100644 index 00000000000..fdf5b3dde85 --- /dev/null +++ b/lib/commands/MGET.ts @@ -0,0 +1,11 @@ +import { transformReplyStringNullArray } from './generic-transformers'; + +export const FIRST_KEY_INDEX = 1; + +export const IS_READ_ONLY = true; + +export function transformArguments(keys: Array): Array { + return ['MGET', ...keys]; +} + +export const transformReply = transformReplyStringNullArray; diff --git a/lib/commands/MIGRATE.spec.ts b/lib/commands/MIGRATE.spec.ts new file mode 100644 index 00000000000..eb233f22904 --- /dev/null +++ b/lib/commands/MIGRATE.spec.ts @@ -0,0 +1,76 @@ +import { strict as assert } from 'assert'; +import { transformArguments } from './MIGRATE'; + +describe('MIGRATE', () => { + describe('transformArguments', () => { + it('single key', () => { + assert.deepEqual( + transformArguments('127.0.0.1', 6379, 'key', 0, 10), + ['MIGRATE', '127.0.0.1', '6379', 'key', '0', '10'] + ); + }); + + it('multiple keys', () => { + assert.deepEqual( + transformArguments('127.0.0.1', 6379, ['1', '2'], 0, 10), + ['MIGRATE', '127.0.0.1', '6379', '""', '0', '10', 'KEYS', '1', '2'] + ); + }); + + it('with COPY', () => { + assert.deepEqual( + transformArguments('127.0.0.1', 6379, 'key', 0, 10, { + COPY: true + }), + ['MIGRATE', '127.0.0.1', '6379', 'key', '0', '10', 'COPY'] + ); + }); + + it('with REPLACE', () => { + assert.deepEqual( + transformArguments('127.0.0.1', 6379, 'key', 0, 10, { + REPLACE: true + }), + ['MIGRATE', '127.0.0.1', '6379', 'key', '0', '10', 'REPLACE'] + ); + }); + + describe('with AUTH', () => { + it('password only', () => { + assert.deepEqual( + transformArguments('127.0.0.1', 6379, 'key', 0, 10, { + AUTH: { + password: 'password' + } + }), + ['MIGRATE', '127.0.0.1', '6379', 'key', '0', '10', 'AUTH', 'password'] + ); + }); + + it('username & password', () => { + assert.deepEqual( + transformArguments('127.0.0.1', 6379, 'key', 0, 10, { + AUTH: { + username: 'username', + password: 'password' + } + }), + ['MIGRATE', '127.0.0.1', '6379', 'key', '0', '10', 'AUTH2', 'username', 'password'] + ); + }); + }); + + it('with COPY, REPLACE, AUTH', () => { + assert.deepEqual( + transformArguments('127.0.0.1', 6379, 'key', 0, 10, { + COPY: true, + REPLACE: true, + AUTH: { + password: 'password' + } + }), + ['MIGRATE', '127.0.0.1', '6379', 'key', '0', '10', 'COPY', 'REPLACE', 'AUTH', 'password'] + ); + }); + }); +}); diff --git a/lib/commands/MIGRATE.ts b/lib/commands/MIGRATE.ts new file mode 100644 index 00000000000..1d2fc075efe --- /dev/null +++ b/lib/commands/MIGRATE.ts @@ -0,0 +1,65 @@ +import { AuthOptions } from './AUTH'; +import { transformReplyString } from './generic-transformers'; + +interface MigrateOptions { + COPY?: true; + REPLACE?: true; + AUTH?: AuthOptions; +} + +export function transformArguments( + host: string, + port: number, + key: string | Array, + destinationDb: number, + timeout: number, + options?: MigrateOptions +): Array { + const args = ['MIGRATE', host, port.toString()], + isKeyString = typeof key === 'string'; + + if (isKeyString) { + args.push(key as string); + } else { + args.push('""'); + } + + args.push( + destinationDb.toString(), + timeout.toString() + ); + + if (options?.COPY) { + args.push('COPY'); + } + + if (options?.REPLACE) { + args.push('REPLACE'); + } + + if (options?.AUTH) { + if (options.AUTH.username) { + args.push( + 'AUTH2', + options.AUTH.username, + options.AUTH.password + ); + } else { + args.push( + 'AUTH', + options.AUTH.password + ); + } + } + + if (!isKeyString) { + args.push( + 'KEYS', + ...key + ); + } + + return args; +} + +export const transformReply = transformReplyString; diff --git a/lib/commands/MODULE_LIST.spec.ts b/lib/commands/MODULE_LIST.spec.ts new file mode 100644 index 00000000000..eeeb774ebff --- /dev/null +++ b/lib/commands/MODULE_LIST.spec.ts @@ -0,0 +1,11 @@ +import { strict as assert } from 'assert'; +import { transformArguments } from './MODULE_LIST'; + +describe('MODULE LIST', () => { + it('transformArguments', () => { + assert.deepEqual( + transformArguments(), + ['MODULE', 'LIST'] + ); + }); +}); diff --git a/lib/commands/MODULE_LIST.ts b/lib/commands/MODULE_LIST.ts new file mode 100644 index 00000000000..53ad14b68eb --- /dev/null +++ b/lib/commands/MODULE_LIST.ts @@ -0,0 +1,7 @@ +import { transformReplyString } from './generic-transformers'; + +export function transformArguments(): Array { + return ['MODULE', 'LIST']; +} + +export const transformReply = transformReplyString; diff --git a/lib/commands/MODULE_LOAD.spec.ts b/lib/commands/MODULE_LOAD.spec.ts new file mode 100644 index 00000000000..5a99a232ca4 --- /dev/null +++ b/lib/commands/MODULE_LOAD.spec.ts @@ -0,0 +1,20 @@ +import { strict as assert } from 'assert'; +import { transformArguments } from './MODULE_LOAD'; + +describe('MODULE LOAD', () => { + describe('transformArguments', () => { + it('simple', () => { + assert.deepEqual( + transformArguments('path'), + ['MODULE', 'LOAD', 'path'] + ); + }); + + it('with module args', () => { + assert.deepEqual( + transformArguments('path', ['1', '2']), + ['MODULE', 'LOAD', 'path', '1', '2'] + ); + }); + }); +}); diff --git a/lib/commands/MODULE_LOAD.ts b/lib/commands/MODULE_LOAD.ts new file mode 100644 index 00000000000..cd2347af24c --- /dev/null +++ b/lib/commands/MODULE_LOAD.ts @@ -0,0 +1,13 @@ +import { transformReplyString } from './generic-transformers'; + +export function transformArguments(path: string, moduleArgs?: Array): Array { + const args = ['MODULE', 'LOAD', path]; + + if (moduleArgs) { + args.push(...moduleArgs); + } + + return args; +} + +export const transformReply = transformReplyString; diff --git a/lib/commands/MODULE_UNLOAD.spec.ts b/lib/commands/MODULE_UNLOAD.spec.ts new file mode 100644 index 00000000000..d8af96c54f1 --- /dev/null +++ b/lib/commands/MODULE_UNLOAD.spec.ts @@ -0,0 +1,11 @@ +import { strict as assert } from 'assert'; +import { transformArguments } from './MODULE_UNLOAD'; + +describe('MODULE UNLOAD', () => { + it('transformArguments', () => { + assert.deepEqual( + transformArguments('name'), + ['MODULE', 'UNLOAD', 'name'] + ); + }); +}); diff --git a/lib/commands/MODULE_UNLOAD.ts b/lib/commands/MODULE_UNLOAD.ts new file mode 100644 index 00000000000..3737784f000 --- /dev/null +++ b/lib/commands/MODULE_UNLOAD.ts @@ -0,0 +1,7 @@ +import { transformReplyString } from './generic-transformers'; + +export function transformArguments(name: string): Array { + return ['MODULE', 'UNLOAD', name]; +} + +export const transformReply = transformReplyString; diff --git a/lib/commands/MOVE.spec.ts b/lib/commands/MOVE.spec.ts new file mode 100644 index 00000000000..a05ca4613e9 --- /dev/null +++ b/lib/commands/MOVE.spec.ts @@ -0,0 +1,19 @@ +import { strict as assert } from 'assert'; +import { TestRedisServers, itWithClient } from '../test-utils'; +import { transformArguments } from './MOVE'; + +describe('MOVE', () => { + it('transformArguments', () => { + assert.deepEqual( + transformArguments('key', 1), + ['MOVE', 'key', '1'] + ); + }); + + itWithClient(TestRedisServers.OPEN, 'client.move', async client => { + assert.equal( + await client.move('key', 1), + false + ); + }); +}); diff --git a/lib/commands/MOVE.ts b/lib/commands/MOVE.ts new file mode 100644 index 00000000000..74bb88c5e74 --- /dev/null +++ b/lib/commands/MOVE.ts @@ -0,0 +1,7 @@ +import { transformReplyBoolean } from './generic-transformers'; + +export function transformArguments(key: string, db: number): Array { + return ['MOVE', key, db.toString()]; +} + +export const transformReply = transformReplyBoolean; \ No newline at end of file diff --git a/lib/commands/MSET.spec.ts b/lib/commands/MSET.spec.ts new file mode 100644 index 00000000000..4445f4a7281 --- /dev/null +++ b/lib/commands/MSET.spec.ts @@ -0,0 +1,42 @@ +import { strict as assert } from 'assert'; +import { TestRedisServers, itWithClient, TestRedisClusters, itWithCluster } from '../test-utils'; +import { transformArguments } from './MSET'; + +describe('MSET', () => { + describe('transformArguments', () => { + it("['key1', 'value1', 'key2', 'value2']", () => { + assert.deepEqual( + transformArguments(['key1', 'value1', 'key2', 'value2']), + ['MSET', 'key1', 'value1', 'key2', 'value2'] + ); + }); + + it("[['key1', 'value1'], ['key2', 'value2']]", () => { + assert.deepEqual( + transformArguments([['key1', 'value1'], ['key2', 'value2']]), + ['MSET', 'key1', 'value1', 'key2', 'value2'] + ); + }); + + it("{key1: 'value1'. key2: 'value2'}", () => { + assert.deepEqual( + transformArguments({ key1: 'value1', key2: 'value2' }), + ['MSET', 'key1', 'value1', 'key2', 'value2'] + ); + }); + }); + + itWithClient(TestRedisServers.OPEN, 'client.mSet', async client => { + assert.equal( + await client.mSet(['key1', 'value1', 'key2', 'value2']), + 'OK' + ); + }); + + itWithCluster(TestRedisClusters.OPEN, 'cluster.mSet', async cluster => { + assert.equal( + await cluster.mSet(['{key}1', 'value1', '{key}2', 'value2']), + 'OK' + ); + }); +}); diff --git a/lib/commands/MSET.ts b/lib/commands/MSET.ts new file mode 100644 index 00000000000..d51790caeed --- /dev/null +++ b/lib/commands/MSET.ts @@ -0,0 +1,19 @@ +import { transformReplyString } from './generic-transformers'; + +export const FIRST_KEY_INDEX = 1; + +export function transformArguments(toSet: Array<[string, string]> | Array | Record): Array { + const args = ['MSET']; + + if (Array.isArray(toSet)) { + args.push(...toSet.flat()); + } else { + for (const key of Object.keys(toSet)) { + args.push(key, toSet[key]); + } + } + + return args; +} + +export const transformReply = transformReplyString; \ No newline at end of file diff --git a/lib/commands/MSETNX.spec.ts b/lib/commands/MSETNX.spec.ts new file mode 100644 index 00000000000..7f61a43e8d0 --- /dev/null +++ b/lib/commands/MSETNX.spec.ts @@ -0,0 +1,42 @@ +import { strict as assert } from 'assert'; +import { TestRedisServers, itWithClient, TestRedisClusters, itWithCluster } from '../test-utils'; +import { transformArguments } from './MSETNX'; + +describe('MSETNX', () => { + describe('transformArguments', () => { + it("['key1', 'value1', 'key2', 'value2']", () => { + assert.deepEqual( + transformArguments(['key1', 'value1', 'key2', 'value2']), + ['MSETNX', 'key1', 'value1', 'key2', 'value2'] + ); + }); + + it("[['key1', 'value1'], ['key2', 'value2']]", () => { + assert.deepEqual( + transformArguments([['key1', 'value1'], ['key2', 'value2']]), + ['MSETNX', 'key1', 'value1', 'key2', 'value2'] + ); + }); + + it("{key1: 'value1'. key2: 'value2'}", () => { + assert.deepEqual( + transformArguments({ key1: 'value1', key2: 'value2' }), + ['MSETNX', 'key1', 'value1', 'key2', 'value2'] + ); + }); + }); + + itWithClient(TestRedisServers.OPEN, 'client.mSetNX', async client => { + assert.equal( + await client.mSetNX(['key1', 'value1', 'key2', 'value2']), + true + ); + }); + + itWithCluster(TestRedisClusters.OPEN, 'cluster.mSetNX', async cluster => { + assert.equal( + await cluster.mSetNX(['{key}1', 'value1', '{key}2', 'value2']), + true + ); + }); +}); diff --git a/lib/commands/MSETNX.ts b/lib/commands/MSETNX.ts new file mode 100644 index 00000000000..c9c8c840374 --- /dev/null +++ b/lib/commands/MSETNX.ts @@ -0,0 +1,19 @@ +import { transformReplyBoolean } from './generic-transformers'; + +export const FIRST_KEY_INDEX = 1; + +export function transformArguments(toSet: Array<[string, string]> | Array | Record): Array { + const args = ['MSETNX']; + + if (Array.isArray(toSet)) { + args.push(...toSet.flat()); + } else { + for (const key of Object.keys(toSet)) { + args.push(key, toSet[key]); + } + } + + return args; +} + +export const transformReply = transformReplyBoolean; \ No newline at end of file diff --git a/lib/commands/PERSIST.spec.ts b/lib/commands/PERSIST.spec.ts new file mode 100644 index 00000000000..05c0e7aed8e --- /dev/null +++ b/lib/commands/PERSIST.spec.ts @@ -0,0 +1,19 @@ +import { strict as assert } from 'assert'; +import { TestRedisServers, itWithClient } from '../test-utils'; +import { transformArguments } from './PERSIST'; + +describe('PERSIST', () => { + it('transformArguments', () => { + assert.deepEqual( + transformArguments('key'), + ['PERSIST', 'key'] + ); + }); + + itWithClient(TestRedisServers.OPEN, 'client.persist', async client => { + assert.equal( + await client.persist('key'), + false + ); + }); +}); diff --git a/lib/commands/PERSIST.ts b/lib/commands/PERSIST.ts new file mode 100644 index 00000000000..fc85a21c98c --- /dev/null +++ b/lib/commands/PERSIST.ts @@ -0,0 +1,9 @@ +import { transformReplyBoolean } from './generic-transformers'; + +export const FIRST_KEY_INDEX = 1; + +export function transformArguments(key: string): Array { + return ['PERSIST', key]; +} + +export const transformReply = transformReplyBoolean; diff --git a/lib/commands/PEXPIRE.spec.ts b/lib/commands/PEXPIRE.spec.ts new file mode 100644 index 00000000000..b7c4e1df461 --- /dev/null +++ b/lib/commands/PEXPIRE.spec.ts @@ -0,0 +1,19 @@ +import { strict as assert } from 'assert'; +import { TestRedisServers, itWithClient } from '../test-utils'; +import { transformArguments } from './PEXPIRE'; + +describe('PEXPIRE', () => { + it('transformArguments', () => { + assert.deepEqual( + transformArguments('key', 1), + ['PEXPIRE', 'key', '1'] + ); + }); + + itWithClient(TestRedisServers.OPEN, 'client.pExpire', async client => { + assert.equal( + await client.pExpire('key', 1), + false + ); + }); +}); diff --git a/lib/commands/PEXPIRE.ts b/lib/commands/PEXPIRE.ts new file mode 100644 index 00000000000..d795f2fc0d0 --- /dev/null +++ b/lib/commands/PEXPIRE.ts @@ -0,0 +1,9 @@ +import { transformReplyBoolean } from './generic-transformers'; + +export const FIRST_KEY_INDEX = 1; + +export function transformArguments(key: string, milliseconds: number): Array { + return ['PEXPIRE', key, milliseconds.toString()]; +} + +export const transformReply = transformReplyBoolean; diff --git a/lib/commands/PEXPIREAT.spec.ts b/lib/commands/PEXPIREAT.spec.ts new file mode 100644 index 00000000000..6e5fc37ed5c --- /dev/null +++ b/lib/commands/PEXPIREAT.spec.ts @@ -0,0 +1,29 @@ +import { strict as assert } from 'assert'; +import { TestRedisServers, itWithClient } from '../test-utils'; +import { transformArguments } from './PEXPIREAT'; + +describe('PEXPIREAT', () => { + describe('transformArguments', () => { + it('number', () => { + assert.deepEqual( + transformArguments('key', 1), + ['PEXPIREAT', 'key', '1'] + ); + }); + + it('date', () => { + const d = new Date(); + assert.deepEqual( + transformArguments('key', d), + ['PEXPIREAT', 'key', d.getTime().toString()] + ); + }); + }); + + itWithClient(TestRedisServers.OPEN, 'client.pExpireAt', async client => { + assert.equal( + await client.pExpireAt('key', 1), + false + ); + }); +}); diff --git a/lib/commands/PEXPIREAT.ts b/lib/commands/PEXPIREAT.ts new file mode 100644 index 00000000000..91f38f88946 --- /dev/null +++ b/lib/commands/PEXPIREAT.ts @@ -0,0 +1,13 @@ +import { transformPXAT, transformReplyBoolean } from './generic-transformers'; + +export const FIRST_KEY_INDEX = 1; + +export function transformArguments(key: string, millisecondsTimestamp: number | Date): Array { + return [ + 'PEXPIREAT', + key, + transformPXAT(millisecondsTimestamp) + ]; +} + +export const transformReply = transformReplyBoolean; diff --git a/lib/commands/PFADD.spec.ts b/lib/commands/PFADD.spec.ts new file mode 100644 index 00000000000..74f03ea3ce2 --- /dev/null +++ b/lib/commands/PFADD.spec.ts @@ -0,0 +1,28 @@ +import { strict as assert } from 'assert'; +import { TestRedisServers, itWithClient } from '../test-utils'; +import { transformArguments } from './PFADD'; + +describe('PFADD', () => { + describe('transformArguments', () => { + it('string', () => { + assert.deepEqual( + transformArguments('key', 'element'), + ['PFADD', 'key', 'element'] + ); + }); + + it('array', () => { + assert.deepEqual( + transformArguments('key', ['1', '2']), + ['PFADD', 'key', '1', '2'] + ); + }); + }); + + itWithClient(TestRedisServers.OPEN, 'client.pfAdd', async client => { + assert.equal( + await client.pfAdd('key', '1'), + true + ); + }); +}); diff --git a/lib/commands/PFADD.ts b/lib/commands/PFADD.ts new file mode 100644 index 00000000000..3348a98852a --- /dev/null +++ b/lib/commands/PFADD.ts @@ -0,0 +1,9 @@ +import { pushVerdictArguments, transformReplyBoolean } from './generic-transformers'; + +export const FIRST_KEY_INDEX = 1; + +export function transformArguments(key: string, element: string | Array): Array { + return pushVerdictArguments(['PFADD', key], element); +} + +export const transformReply = transformReplyBoolean; diff --git a/lib/commands/PFCOUNT.spec.ts b/lib/commands/PFCOUNT.spec.ts new file mode 100644 index 00000000000..049fa2c200c --- /dev/null +++ b/lib/commands/PFCOUNT.spec.ts @@ -0,0 +1,28 @@ +import { strict as assert } from 'assert'; +import { TestRedisServers, itWithClient } from '../test-utils'; +import { transformArguments } from './PFCOUNT'; + +describe('PFCOUNT', () => { + describe('transformArguments', () => { + it('string', () => { + assert.deepEqual( + transformArguments('key'), + ['PFCOUNT', 'key'] + ); + }); + + it('array', () => { + assert.deepEqual( + transformArguments(['1', '2']), + ['PFCOUNT', '1', '2'] + ); + }); + }); + + itWithClient(TestRedisServers.OPEN, 'client.pfCount', async client => { + assert.equal( + await client.pfCount('key'), + 0 + ); + }); +}); diff --git a/lib/commands/PFCOUNT.ts b/lib/commands/PFCOUNT.ts new file mode 100644 index 00000000000..eac710a3543 --- /dev/null +++ b/lib/commands/PFCOUNT.ts @@ -0,0 +1,9 @@ +import { pushVerdictArguments, transformReplyNumber } from './generic-transformers'; + +export const FIRST_KEY_INDEX = 1; + +export function transformArguments(key: string | Array): Array { + return pushVerdictArguments(['PFCOUNT'], key); +} + +export const transformReply = transformReplyNumber; diff --git a/lib/commands/PFMERGE.spec.ts b/lib/commands/PFMERGE.spec.ts new file mode 100644 index 00000000000..1f6ed24bcd1 --- /dev/null +++ b/lib/commands/PFMERGE.spec.ts @@ -0,0 +1,28 @@ +import { strict as assert } from 'assert'; +import { TestRedisServers, itWithClient } from '../test-utils'; +import { transformArguments } from './PFMERGE'; + +describe('PFMERGE', () => { + describe('transformArguments', () => { + it('string', () => { + assert.deepEqual( + transformArguments('destination', 'source'), + ['PFMERGE', 'destination', 'source'] + ); + }); + + it('array', () => { + assert.deepEqual( + transformArguments('destination', ['1', '2']), + ['PFMERGE', 'destination', '1', '2'] + ); + }); + }); + + itWithClient(TestRedisServers.OPEN, 'client.pfMerge', async client => { + assert.equal( + await client.pfMerge('destination', 'source'), + 'OK' + ); + }); +}); diff --git a/lib/commands/PFMERGE.ts b/lib/commands/PFMERGE.ts new file mode 100644 index 00000000000..73a4a2edb9a --- /dev/null +++ b/lib/commands/PFMERGE.ts @@ -0,0 +1,9 @@ +import { pushVerdictArguments, transformReplyString } from './generic-transformers'; + +export const FIRST_KEY_INDEX = 1; + +export function transformArguments(destination: string, source: string | Array): Array { + return pushVerdictArguments(['PFMERGE', destination], source); +} + +export const transformReply = transformReplyString; diff --git a/lib/commands/PING.spec.ts b/lib/commands/PING.spec.ts new file mode 100644 index 00000000000..87d9359a36d --- /dev/null +++ b/lib/commands/PING.spec.ts @@ -0,0 +1,18 @@ +import { strict as assert } from 'assert'; +import { TestRedisServers, itWithClient, itWithCluster, TestRedisClusters } from '../test-utils'; + +describe('PING', () => { + itWithClient(TestRedisServers.OPEN, 'client.ping', async client => { + assert.equal( + await client.ping(), + 'PONG' + ); + }); + + itWithCluster(TestRedisClusters.OPEN, 'cluster.ping', async cluster => { + assert.equal( + await cluster.ping(), + 'PONG' + ); + }); +}); diff --git a/lib/commands/PING.ts b/lib/commands/PING.ts new file mode 100644 index 00000000000..36e92a08cfe --- /dev/null +++ b/lib/commands/PING.ts @@ -0,0 +1,7 @@ +import { transformReplyString } from './generic-transformers'; + +export function transformArguments(): Array { + return ['PING']; +} + +export const transformReply = transformReplyString; diff --git a/lib/commands/PSETEX.spec.ts b/lib/commands/PSETEX.spec.ts new file mode 100644 index 00000000000..c98142effa9 --- /dev/null +++ b/lib/commands/PSETEX.spec.ts @@ -0,0 +1,26 @@ +import { strict as assert } from 'assert'; +import { TestRedisServers, itWithClient, TestRedisClusters, itWithCluster } from '../test-utils'; +import { transformArguments } from './PSETEX'; + +describe('PSETEX', () => { + it('transformArguments', () => { + assert.deepEqual( + transformArguments('key', 1, 'value'), + ['PSETEX', 'key', '1', 'value'] + ); + }); + + itWithClient(TestRedisServers.OPEN, 'client.pSetEx', async client => { + assert.equal( + await client.pSetEx('key', 1, 'value'), + 'OK' + ); + }); + + itWithCluster(TestRedisClusters.OPEN, 'cluster.pSetEx', async cluster => { + assert.equal( + await cluster.pSetEx('key', 1, 'value'), + 'OK' + ); + }); +}); diff --git a/lib/commands/PSETEX.ts b/lib/commands/PSETEX.ts new file mode 100644 index 00000000000..101030d2e63 --- /dev/null +++ b/lib/commands/PSETEX.ts @@ -0,0 +1,14 @@ +import { transformReplyString } from './generic-transformers'; + +export const FIRST_KEY_INDEX = 1; + +export function transformArguments(key: string, milliseconds: number, value: string): Array { + return [ + 'PSETEX', + key, + milliseconds.toString(), + value + ]; +} + +export const transformReply = transformReplyString; diff --git a/lib/commands/PTTL.spec.ts b/lib/commands/PTTL.spec.ts new file mode 100644 index 00000000000..35f48c2cc3e --- /dev/null +++ b/lib/commands/PTTL.spec.ts @@ -0,0 +1,19 @@ +import { strict as assert } from 'assert'; +import { TestRedisServers, itWithClient } from '../test-utils'; +import { transformArguments } from './PTTL'; + +describe('PTTL', () => { + it('transformArguments', () => { + assert.deepEqual( + transformArguments('key'), + ['PTTL', 'key'] + ); + }); + + itWithClient(TestRedisServers.OPEN, 'client.pTTL', async client => { + assert.equal( + await client.pTTL('key'), + -2 + ); + }); +}); diff --git a/lib/commands/PTTL.ts b/lib/commands/PTTL.ts new file mode 100644 index 00000000000..8356c75bbd9 --- /dev/null +++ b/lib/commands/PTTL.ts @@ -0,0 +1,11 @@ +import { transformReplyNumber } from './generic-transformers'; + +export const FIRST_KEY_INDEX = 1; + +export const IS_READ_ONLY = true; + +export function transformArguments(key: string): Array { + return ['PTTL', key]; +} + +export const transformReply = transformReplyNumber; diff --git a/lib/commands/PUBLISH.spec.ts b/lib/commands/PUBLISH.spec.ts new file mode 100644 index 00000000000..e746b9490e0 --- /dev/null +++ b/lib/commands/PUBLISH.spec.ts @@ -0,0 +1,19 @@ +import { strict as assert } from 'assert'; +import { TestRedisServers, itWithClient } from '../test-utils'; +import { transformArguments } from './PUBLISH'; + +describe('PUBLISH', () => { + it('transformArguments', () => { + assert.deepEqual( + transformArguments('channel', 'message'), + ['PUBLISH', 'channel', 'message'] + ); + }); + + itWithClient(TestRedisServers.OPEN, 'client.publish', async client => { + assert.equal( + await client.publish('channel', 'message'), + 0 + ); + }); +}); diff --git a/lib/commands/PUBLISH.ts b/lib/commands/PUBLISH.ts new file mode 100644 index 00000000000..51387a6803f --- /dev/null +++ b/lib/commands/PUBLISH.ts @@ -0,0 +1,7 @@ +import { transformReplyNumber } from './generic-transformers'; + +export function transformArguments(channel: string, message: string): Array { + return ['PUBLISH', channel, message]; +} + +export const transformReply = transformReplyNumber; diff --git a/lib/commands/PUBSUB_CHANNELS.spec.ts b/lib/commands/PUBSUB_CHANNELS.spec.ts new file mode 100644 index 00000000000..5ff9db60df8 --- /dev/null +++ b/lib/commands/PUBSUB_CHANNELS.spec.ts @@ -0,0 +1,35 @@ +import { strict as assert } from 'assert'; +import { TestRedisServers, itWithClient, TestRedisClusters, itWithCluster } from '../test-utils'; +import { transformArguments } from './PUBSUB_CHANNELS'; + +describe('PUBSUB CHANNELS', () => { + describe('transformArguments', () => { + it('simple', () => { + assert.deepEqual( + transformArguments(), + ['PUBSUB', 'CHANNELS'] + ); + }); + + it('with pattern', () => { + assert.deepEqual( + transformArguments('patter*'), + ['PUBSUB', 'CHANNELS', 'patter*'] + ); + }); + }); + + itWithClient(TestRedisServers.OPEN, 'client.pubSubChannels', async client => { + assert.deepEqual( + await client.pubSubChannels(), + [] + ); + }); + + itWithCluster(TestRedisClusters.OPEN, 'cluster.pubSubChannels', async cluster => { + assert.deepEqual( + await cluster.pubSubChannels(), + [] + ); + }); +}); diff --git a/lib/commands/PUBSUB_CHANNELS.ts b/lib/commands/PUBSUB_CHANNELS.ts new file mode 100644 index 00000000000..aa7a0749fc6 --- /dev/null +++ b/lib/commands/PUBSUB_CHANNELS.ts @@ -0,0 +1,15 @@ +import { transformReplyStringArray } from './generic-transformers'; + +export const IS_READ_ONLY = true; + +export function transformArguments(pattern?: string): Array { + const args = ['PUBSUB', 'CHANNELS']; + + if (pattern) { + args.push(pattern); + } + + return args; +} + +export const transformReply = transformReplyStringArray; diff --git a/lib/commands/PUBSUB_NUMPAT.spec.ts b/lib/commands/PUBSUB_NUMPAT.spec.ts new file mode 100644 index 00000000000..49a39eedae0 --- /dev/null +++ b/lib/commands/PUBSUB_NUMPAT.spec.ts @@ -0,0 +1,26 @@ +import { strict as assert } from 'assert'; +import { TestRedisServers, itWithClient, TestRedisClusters, itWithCluster } from '../test-utils'; +import { transformArguments } from './PUBSUB_NUMPAT'; + +describe('PUBSUB NUMPAT', () => { + it('transformArguments', () => { + assert.deepEqual( + transformArguments(), + ['PUBSUB', 'NUMPAT'] + ); + }); + + itWithClient(TestRedisServers.OPEN, 'client.pubSubNumPat', async client => { + assert.equal( + await client.pubSubNumPat(), + 0 + ); + }); + + itWithCluster(TestRedisClusters.OPEN, 'cluster.pubSubNumPat', async cluster => { + assert.equal( + await cluster.pubSubNumPat(), + 0 + ); + }); +}); diff --git a/lib/commands/PUBSUB_NUMPAT.ts b/lib/commands/PUBSUB_NUMPAT.ts new file mode 100644 index 00000000000..966a8d237c7 --- /dev/null +++ b/lib/commands/PUBSUB_NUMPAT.ts @@ -0,0 +1,9 @@ +import { transformReplyString } from './generic-transformers'; + +export const IS_READ_ONLY = true; + +export function transformArguments(): Array { + return ['PUBSUB', 'NUMPAT']; +} + +export const transformReply = transformReplyString; diff --git a/lib/commands/PUBSUB_NUMSUB.spec.ts b/lib/commands/PUBSUB_NUMSUB.spec.ts new file mode 100644 index 00000000000..74065dbb48f --- /dev/null +++ b/lib/commands/PUBSUB_NUMSUB.spec.ts @@ -0,0 +1,42 @@ +import { strict as assert } from 'assert'; +import { TestRedisServers, itWithClient, TestRedisClusters, itWithCluster } from '../test-utils'; +import { transformArguments } from './PUBSUB_NUMSUB'; + +describe('PUBSUB NUMSUB', () => { + describe('transformArguments', () => { + it('simple', () => { + assert.deepEqual( + transformArguments(), + ['PUBSUB', 'NUMSUB'] + ); + }); + + it('string', () => { + assert.deepEqual( + transformArguments('channel'), + ['PUBSUB', 'NUMSUB', 'channel'] + ); + }); + + it('array', () => { + assert.deepEqual( + transformArguments(['1', '2']), + ['PUBSUB', 'NUMSUB', '1', '2'] + ); + }); + }); + + itWithClient(TestRedisServers.OPEN, 'client.pubSubNumSub', async client => { + assert.deepEqual( + await client.pubSubNumSub(), + Object.create(null) + ); + }); + + itWithCluster(TestRedisClusters.OPEN, 'cluster.pubSubNumPat', async cluster => { + assert.deepEqual( + await cluster.pubSubNumSub(), + Object.create(null) + ); + }); +}); diff --git a/lib/commands/PUBSUB_NUMSUB.ts b/lib/commands/PUBSUB_NUMSUB.ts new file mode 100644 index 00000000000..c68b0d9a7f1 --- /dev/null +++ b/lib/commands/PUBSUB_NUMSUB.ts @@ -0,0 +1,23 @@ +import { pushVerdictArguments } from './generic-transformers'; + +export const IS_READ_ONLY = true; + +export function transformArguments(channels?: Array | string): Array { + const args = ['PUBSUB', 'NUMSUB']; + + if (channels) { + pushVerdictArguments(args, channels); + } + + return args; +} + +export function transformReply(rawReply: Array): Record { + const transformedReply = Object.create(null); + + for (let i = 0; i < rawReply.length; i +=2) { + transformedReply[rawReply[i]] = rawReply[i + 1]; + } + + return transformedReply; +} diff --git a/lib/commands/RANDOMKEY.spec.ts b/lib/commands/RANDOMKEY.spec.ts new file mode 100644 index 00000000000..171c42be116 --- /dev/null +++ b/lib/commands/RANDOMKEY.spec.ts @@ -0,0 +1,19 @@ +import { strict as assert } from 'assert'; +import { TestRedisServers, itWithClient } from '../test-utils'; +import { transformArguments } from './RANDOMKEY'; + +describe('RANDOMKEY', () => { + it('transformArguments', () => { + assert.deepEqual( + transformArguments(), + ['RANDOMKEY'] + ); + }); + + itWithClient(TestRedisServers.OPEN, 'client.randomKey', async client => { + assert.equal( + await client.randomKey(), + null + ); + }); +}); diff --git a/lib/commands/RANDOMKEY.ts b/lib/commands/RANDOMKEY.ts new file mode 100644 index 00000000000..fad0b073c71 --- /dev/null +++ b/lib/commands/RANDOMKEY.ts @@ -0,0 +1,9 @@ +export const IS_READ_ONLY = true; + +export function transformArguments(): Array { + return ['RANDOMKEY']; +} + +export function transformReply(reply: string | null): string | null { + return reply; +} diff --git a/lib/commands/READONLY.spec.ts b/lib/commands/READONLY.spec.ts new file mode 100644 index 00000000000..aa4db47f81a --- /dev/null +++ b/lib/commands/READONLY.spec.ts @@ -0,0 +1,11 @@ +import { strict as assert } from 'assert'; +import { transformArguments } from './READONLY'; + +describe('READONLY', () => { + it('transformArguments', () => { + assert.deepEqual( + transformArguments(), + ['READONLY'] + ); + }); +}); diff --git a/lib/commands/READONLY.ts b/lib/commands/READONLY.ts new file mode 100644 index 00000000000..00fbe4e4351 --- /dev/null +++ b/lib/commands/READONLY.ts @@ -0,0 +1,7 @@ +import { transformReplyString } from './generic-transformers'; + +export function transformArguments(): Array { + return ['READONLY']; +} + +export const transformReply = transformReplyString; diff --git a/lib/commands/READWRITE.spec.ts b/lib/commands/READWRITE.spec.ts new file mode 100644 index 00000000000..6ce4a3ee56a --- /dev/null +++ b/lib/commands/READWRITE.spec.ts @@ -0,0 +1,11 @@ +import { strict as assert } from 'assert'; +import { transformArguments } from './READWRITE'; + +describe('READWRITE', () => { + it('transformArguments', () => { + assert.deepEqual( + transformArguments(), + ['READWRITE'] + ); + }); +}); diff --git a/lib/commands/READWRITE.ts b/lib/commands/READWRITE.ts new file mode 100644 index 00000000000..16f95604407 --- /dev/null +++ b/lib/commands/READWRITE.ts @@ -0,0 +1,7 @@ +import { transformReplyString } from './generic-transformers'; + +export function transformArguments(): Array { + return ['READWRITE']; +} + +export const transformReply = transformReplyString; diff --git a/lib/commands/RENAME.spec.ts b/lib/commands/RENAME.spec.ts new file mode 100644 index 00000000000..9d447c600b6 --- /dev/null +++ b/lib/commands/RENAME.spec.ts @@ -0,0 +1,21 @@ +import { strict as assert } from 'assert'; +import { TestRedisServers, itWithClient } from '../test-utils'; +import { transformArguments } from './RENAME'; + +describe('RENAME', () => { + it('transformArguments', () => { + assert.deepEqual( + transformArguments('from', 'to'), + ['RENAME', 'from', 'to'] + ); + }); + + itWithClient(TestRedisServers.OPEN, 'client.rename', async client => { + await client.set('from', 'value'); + + assert.equal( + await client.rename('from', 'to'), + 'OK' + ); + }); +}); diff --git a/lib/commands/RENAME.ts b/lib/commands/RENAME.ts new file mode 100644 index 00000000000..0f9582677f1 --- /dev/null +++ b/lib/commands/RENAME.ts @@ -0,0 +1,9 @@ +import { transformReplyString } from './generic-transformers'; + +export const FIRST_KEY_INDEX = 1; + +export function transformArguments(key: string, newKey: string): Array { + return ['RENAME', key, newKey]; +} + +export const transformReply = transformReplyString; diff --git a/lib/commands/RENAMENX.spec.ts b/lib/commands/RENAMENX.spec.ts new file mode 100644 index 00000000000..f438834b90e --- /dev/null +++ b/lib/commands/RENAMENX.spec.ts @@ -0,0 +1,21 @@ +import { strict as assert } from 'assert'; +import { TestRedisServers, itWithClient } from '../test-utils'; +import { transformArguments } from './RENAMENX'; + +describe('RENAMENX', () => { + it('transformArguments', () => { + assert.deepEqual( + transformArguments('from', 'to'), + ['RENAMENX', 'from', 'to'] + ); + }); + + itWithClient(TestRedisServers.OPEN, 'client.renameNX', async client => { + await client.set('from', 'value'); + + assert.equal( + await client.renameNX('from', 'to'), + true + ); + }); +}); diff --git a/lib/commands/RENAMENX.ts b/lib/commands/RENAMENX.ts new file mode 100644 index 00000000000..883d2ca2961 --- /dev/null +++ b/lib/commands/RENAMENX.ts @@ -0,0 +1,9 @@ +import { transformReplyBoolean } from './generic-transformers'; + +export const FIRST_KEY_INDEX = 1; + +export function transformArguments(key: string, newKey: string): Array { + return ['RENAMENX', key, newKey]; +} + +export const transformReply = transformReplyBoolean; diff --git a/lib/commands/REPLICAOF.spec.ts b/lib/commands/REPLICAOF.spec.ts new file mode 100644 index 00000000000..ab1906944cf --- /dev/null +++ b/lib/commands/REPLICAOF.spec.ts @@ -0,0 +1,11 @@ +import { strict as assert } from 'assert'; +import { transformArguments } from './REPLICAOF'; + +describe('REPLICAOF', () => { + it('transformArguments', () => { + assert.deepEqual( + transformArguments('host', 1), + ['REPLICAOF', 'host', '1'] + ); + }); +}); diff --git a/lib/commands/REPLICAOF.ts b/lib/commands/REPLICAOF.ts new file mode 100644 index 00000000000..0b56bd74dc6 --- /dev/null +++ b/lib/commands/REPLICAOF.ts @@ -0,0 +1,7 @@ +import { transformReplyString } from './generic-transformers'; + +export function transformArguments(host: string, port: number): Array { + return ['REPLICAOF', host, port.toString()]; +} + +export const transformReply = transformReplyString; diff --git a/lib/commands/RESTORE-ASKING.spec.ts b/lib/commands/RESTORE-ASKING.spec.ts new file mode 100644 index 00000000000..de9fce5c628 --- /dev/null +++ b/lib/commands/RESTORE-ASKING.spec.ts @@ -0,0 +1,11 @@ +import { strict as assert } from 'assert'; +import { transformArguments } from './RESTORE-ASKING'; + +describe('RESTORE-ASKING', () => { + it('transformArguments', () => { + assert.deepEqual( + transformArguments(), + ['RESTORE-ASKING'] + ); + }); +}); diff --git a/lib/commands/RESTORE-ASKING.ts b/lib/commands/RESTORE-ASKING.ts new file mode 100644 index 00000000000..4d178cb1f0b --- /dev/null +++ b/lib/commands/RESTORE-ASKING.ts @@ -0,0 +1,7 @@ +import { transformReplyString } from './generic-transformers'; + +export function transformArguments(): Array { + return ['RESTORE-ASKING']; +} + +export const transformReply = transformReplyString; diff --git a/lib/commands/ROLE.spec.ts b/lib/commands/ROLE.spec.ts new file mode 100644 index 00000000000..5b647e07ca6 --- /dev/null +++ b/lib/commands/ROLE.spec.ts @@ -0,0 +1,69 @@ +import { strict as assert } from 'assert'; +import { itWithClient, TestRedisServers } from '../test-utils'; +import { transformArguments, transformReply } from './ROLE'; + +describe('ROLE', () => { + it('transformArguments', () => { + assert.deepEqual( + transformArguments(), + ['ROLE'] + ); + }); + + describe('transformReply', () => { + it('master', () => { + assert.deepEqual( + transformReply(['master', 3129659, [['127.0.0.1', '9001', '3129242'], ['127.0.0.1', '9002', '3129543']]]), + { + role: 'master', + replicationOffest: 3129659, + replicas: [{ + ip: '127.0.0.1', + port: 9001, + replicationOffest: 3129242 + }, { + ip: '127.0.0.1', + port: 9002, + replicationOffest: 3129543 + }] + } + ); + }); + + it('replica', () => { + assert.deepEqual( + transformReply(['slave', '127.0.0.1', 9000, 'connected', 3167038]), + { + role: 'slave', + master: { + ip: '127.0.0.1', + port: 9000 + }, + state: 'connected', + dataReceived: 3167038 + } + ); + }); + + it('sentinel', () => { + assert.deepEqual( + transformReply(['sentinel', ['resque-master', 'html-fragments-master', 'stats-master', 'metadata-master']]), + { + role: 'sentinel', + masterNames: ['resque-master', 'html-fragments-master', 'stats-master', 'metadata-master'] + } + ); + }); + }); + + itWithClient(TestRedisServers.OPEN, 'client.role', async client => { + assert.deepEqual( + await client.role(), + { + role: 'master', + replicationOffest: 0, + replicas: [] + } + ); + }); +}); diff --git a/lib/commands/ROLE.ts b/lib/commands/ROLE.ts new file mode 100644 index 00000000000..b1d6041fdfa --- /dev/null +++ b/lib/commands/ROLE.ts @@ -0,0 +1,75 @@ +export const IS_READ_ONLY = true; + +export function transformArguments(): Array { + return ['ROLE']; +} + +interface RoleReplyInterface { + role: T; +} + +type RoleMasterRawReply = ['master', number, Array<[string, string, string]>]; + +interface RoleMasterReply extends RoleReplyInterface<'master'> { + replicationOffest: number; + replicas: Array<{ + ip: string; + port: number; + replicationOffest: number; + }>; +} + +type RoleReplicaState = 'connect' | 'connecting' | 'sync' | 'connected'; + +type RoleReplicaRawReply = ['slave', string, number, RoleReplicaState, number]; + +interface RoleReplicaReply extends RoleReplyInterface<'slave'> { + master: { + ip: string; + port: number; + }; + state: RoleReplicaState; + dataReceived: number; +} + +type RoleSentinelRawReply = ['sentinel', Array]; + +interface RoleSentinelReply extends RoleReplyInterface<'sentinel'> { + masterNames: Array; +} + +type RoleRawReply = RoleMasterRawReply | RoleReplicaRawReply | RoleSentinelRawReply; + +type RoleReply = RoleMasterReply | RoleReplicaReply | RoleSentinelReply; + +export function transformReply(reply: RoleRawReply): RoleReply { + switch (reply[0]) { + case 'master': + return { + role: 'master', + replicationOffest: reply[1], + replicas: reply[2].map(([ip, port, replicationOffest]) => ({ + ip, + port: Number(port), + replicationOffest: Number(replicationOffest) + })) + }; + + case 'slave': + return { + role: 'slave', + master: { + ip: reply[1], + port: reply[2] + }, + state: reply[3], + dataReceived: reply[4] + }; + + case 'sentinel': + return { + role: 'sentinel', + masterNames: reply[1] + }; + } +} diff --git a/lib/commands/RPOP.spec.ts b/lib/commands/RPOP.spec.ts new file mode 100644 index 00000000000..2a753ff1a66 --- /dev/null +++ b/lib/commands/RPOP.spec.ts @@ -0,0 +1,26 @@ +import { strict as assert } from 'assert'; +import { TestRedisServers, itWithClient, itWithCluster, TestRedisClusters } from '../test-utils'; +import { transformArguments } from './RPOP'; + +describe('RPOP', () => { + it('transformArguments', () => { + assert.deepEqual( + transformArguments('key'), + ['RPOP', 'key'] + ); + }); + + itWithClient(TestRedisServers.OPEN, 'client.rPop', async client => { + assert.equal( + await client.rPop('key'), + null + ); + }); + + itWithCluster(TestRedisClusters.OPEN, 'cluster.rPop', async cluster => { + assert.equal( + await cluster.rPop('key'), + null + ); + }); +}); diff --git a/lib/commands/RPOP.ts b/lib/commands/RPOP.ts new file mode 100644 index 00000000000..daccbf5d42d --- /dev/null +++ b/lib/commands/RPOP.ts @@ -0,0 +1,9 @@ +import { transformReplyStringNull } from './generic-transformers'; + +export const FIRST_KEY_INDEX = 1; + +export function transformArguments(key: string): Array { + return ['RPOP', key]; +} + +export const transformReply = transformReplyStringNull; diff --git a/lib/commands/RPOPLPUSH.spec.ts b/lib/commands/RPOPLPUSH.spec.ts new file mode 100644 index 00000000000..75b5f2e18f9 --- /dev/null +++ b/lib/commands/RPOPLPUSH.spec.ts @@ -0,0 +1,26 @@ +import { strict as assert } from 'assert'; +import { TestRedisServers, itWithClient, itWithCluster, TestRedisClusters } from '../test-utils'; +import { transformArguments } from './RPOPLPUSH'; + +describe('RPOPLPUSH', () => { + it('transformArguments', () => { + assert.deepEqual( + transformArguments('source', 'destination'), + ['RPOPLPUSH', 'source', 'destination'] + ); + }); + + itWithClient(TestRedisServers.OPEN, 'client.rPopLPush', async client => { + assert.equal( + await client.rPopLPush('source', 'destination'), + null + ); + }); + + itWithCluster(TestRedisClusters.OPEN, 'cluster.rPopLPush', async cluster => { + assert.equal( + await cluster.rPopLPush('{tag}source', '{tag}destination'), + null + ); + }); +}); diff --git a/lib/commands/RPOPLPUSH.ts b/lib/commands/RPOPLPUSH.ts new file mode 100644 index 00000000000..db388906d3e --- /dev/null +++ b/lib/commands/RPOPLPUSH.ts @@ -0,0 +1,9 @@ +import { transformReplyNumberNull } from './generic-transformers'; + +export const FIRST_KEY_INDEX = 1; + +export function transformArguments(source: string, destination: string): Array { + return ['RPOPLPUSH', source, destination]; +} + +export const transformReply = transformReplyNumberNull; diff --git a/lib/commands/RPOP_COUNT.spec.ts b/lib/commands/RPOP_COUNT.spec.ts new file mode 100644 index 00000000000..2624540f124 --- /dev/null +++ b/lib/commands/RPOP_COUNT.spec.ts @@ -0,0 +1,28 @@ +import { strict as assert } from 'assert'; +import { TestRedisServers, itWithClient, itWithCluster, TestRedisClusters, describeHandleMinimumRedisVersion } from '../test-utils'; +import { transformArguments } from './RPOP_COUNT'; + +describe('RPOP COUNT', () => { + describeHandleMinimumRedisVersion([6, 2]); + + it('transformArguments', () => { + assert.deepEqual( + transformArguments('key', 1), + ['RPOP', 'key', '1'] + ); + }); + + itWithClient(TestRedisServers.OPEN, 'client.rPopCount', async client => { + assert.equal( + await client.rPopCount('key', 1), + null + ); + }); + + itWithCluster(TestRedisClusters.OPEN, 'cluster.rPopCount', async cluster => { + assert.equal( + await cluster.rPopCount('key', 1), + null + ); + }); +}); diff --git a/lib/commands/RPOP_COUNT.ts b/lib/commands/RPOP_COUNT.ts new file mode 100644 index 00000000000..205704274f7 --- /dev/null +++ b/lib/commands/RPOP_COUNT.ts @@ -0,0 +1,9 @@ +import { transformReplyStringArrayNull } from './generic-transformers'; + +export const FIRST_KEY_INDEX = 1; + +export function transformArguments(key: string, count: number): Array { + return ['RPOP', key, count.toString()]; +} + +export const transformReply = transformReplyStringArrayNull; diff --git a/lib/commands/RPUSH.spec.ts b/lib/commands/RPUSH.spec.ts new file mode 100644 index 00000000000..4336d10c9a3 --- /dev/null +++ b/lib/commands/RPUSH.spec.ts @@ -0,0 +1,35 @@ +import { strict as assert } from 'assert'; +import { TestRedisServers, itWithClient, itWithCluster, TestRedisClusters } from '../test-utils'; +import { transformArguments } from './RPUSH'; + +describe('RPUSH', () => { + describe('transformArguments', () => { + it('string', () => { + assert.deepEqual( + transformArguments('key', 'element'), + ['RPUSH', 'key', 'element'] + ); + }); + + it('array', () => { + assert.deepEqual( + transformArguments('key', ['1', '2']), + ['RPUSH', 'key', '1', '2'] + ); + }); + }); + + itWithClient(TestRedisServers.OPEN, 'client.rPush', async client => { + assert.equal( + await client.rPush('key', 'element'), + 1 + ); + }); + + itWithCluster(TestRedisClusters.OPEN, 'cluster.rPush', async cluster => { + assert.equal( + await cluster.rPush('key', 'element'), + 1 + ); + }); +}); diff --git a/lib/commands/RPUSH.ts b/lib/commands/RPUSH.ts new file mode 100644 index 00000000000..191d2704e09 --- /dev/null +++ b/lib/commands/RPUSH.ts @@ -0,0 +1,9 @@ +import { pushVerdictArguments, transformReplyNumber } from './generic-transformers'; + +export const FIRST_KEY_INDEX = 1; + +export function transformArguments(key: string, element: string | Array): Array { + return pushVerdictArguments(['RPUSH', key], element); +} + +export const transformReply = transformReplyNumber; diff --git a/lib/commands/RPUSHX.spec.ts b/lib/commands/RPUSHX.spec.ts new file mode 100644 index 00000000000..18f91e8bef6 --- /dev/null +++ b/lib/commands/RPUSHX.spec.ts @@ -0,0 +1,35 @@ +import { strict as assert } from 'assert'; +import { TestRedisServers, itWithClient, itWithCluster, TestRedisClusters } from '../test-utils'; +import { transformArguments } from './RPUSHX'; + +describe('RPUSHX', () => { + describe('transformArguments', () => { + it('string', () => { + assert.deepEqual( + transformArguments('key', 'element'), + ['RPUSHX', 'key', 'element'] + ); + }); + + it('array', () => { + assert.deepEqual( + transformArguments('key', ['1', '2']), + ['RPUSHX', 'key', '1', '2'] + ); + }); + }); + + itWithClient(TestRedisServers.OPEN, 'client.rPushX', async client => { + assert.equal( + await client.rPushX('key', 'element'), + 0 + ); + }); + + itWithCluster(TestRedisClusters.OPEN, 'cluster.rPushX', async cluster => { + assert.equal( + await cluster.rPushX('key', 'element'), + 0 + ); + }); +}); diff --git a/lib/commands/RPUSHX.ts b/lib/commands/RPUSHX.ts new file mode 100644 index 00000000000..a07615a58e0 --- /dev/null +++ b/lib/commands/RPUSHX.ts @@ -0,0 +1,9 @@ +import { pushVerdictArguments, transformReplyNumber } from './generic-transformers'; + +export const FIRST_KEY_INDEX = 1; + +export function transformArguments(key: string, element: string | Array): Array { + return pushVerdictArguments(['RPUSHX', key], element); +} + +export const transformReply = transformReplyNumber; diff --git a/lib/commands/SADD.spec.ts b/lib/commands/SADD.spec.ts new file mode 100644 index 00000000000..bf1ee48fe7f --- /dev/null +++ b/lib/commands/SADD.spec.ts @@ -0,0 +1,28 @@ +import { strict as assert } from 'assert'; +import { TestRedisServers, itWithClient } from '../test-utils'; +import { transformArguments } from './SADD'; + +describe('SADD', () => { + describe('transformArguments', () => { + it('string', () => { + assert.deepEqual( + transformArguments('key', 'member'), + ['SADD', 'key', 'member'] + ); + }); + + it('array', () => { + assert.deepEqual( + transformArguments('key', ['1', '2']), + ['SADD', 'key', '1', '2'] + ); + }); + }); + + itWithClient(TestRedisServers.OPEN, 'client.sAdd', async client => { + assert.equal( + await client.sAdd('key', 'member'), + 1 + ); + }); +}); diff --git a/lib/commands/SADD.ts b/lib/commands/SADD.ts new file mode 100644 index 00000000000..a14ba1686c0 --- /dev/null +++ b/lib/commands/SADD.ts @@ -0,0 +1,9 @@ +import { pushVerdictArguments, transformReplyNumber } from './generic-transformers'; + +export const FIRST_KEY_INDEX = 1; + +export function transformArguments(key: string, members: string | Array): Array { + return pushVerdictArguments(['SADD', key], members); +} + +export const transformReply = transformReplyNumber; diff --git a/lib/commands/SAVE.spec.ts b/lib/commands/SAVE.spec.ts new file mode 100644 index 00000000000..1e1987b5ab8 --- /dev/null +++ b/lib/commands/SAVE.spec.ts @@ -0,0 +1,11 @@ +import { strict as assert } from 'assert'; +import { transformArguments } from './SAVE'; + +describe('SAVE', () => { + it('transformArguments', () => { + assert.deepEqual( + transformArguments(), + ['SAVE'] + ); + }); +}); diff --git a/lib/commands/SAVE.ts b/lib/commands/SAVE.ts new file mode 100644 index 00000000000..38a397892f8 --- /dev/null +++ b/lib/commands/SAVE.ts @@ -0,0 +1,7 @@ +import { transformReplyString } from './generic-transformers'; + +export function transformArguments(): Array { + return ['SAVE']; +} + +export const transformReply = transformReplyString; diff --git a/lib/commands/SCAN.spec.ts b/lib/commands/SCAN.spec.ts new file mode 100644 index 00000000000..975c4cb6d2f --- /dev/null +++ b/lib/commands/SCAN.spec.ts @@ -0,0 +1,84 @@ +import { strict as assert } from 'assert'; +import { TestRedisServers, itWithClient } from '../test-utils'; +import { transformArguments, transformReply } from './SCAN'; + +describe('SCAN', () => { + describe('transformArguments', () => { + it('cusror only', () => { + assert.deepEqual( + transformArguments(0), + ['SCAN', '0'] + ); + }); + + it('with MATCH', () => { + assert.deepEqual( + transformArguments(0, { + MATCH: 'pattern' + }), + ['SCAN', '0', 'MATCH', 'pattern'] + ); + }); + + it('with COUNT', () => { + assert.deepEqual( + transformArguments(0, { + COUNT: 1 + }), + ['SCAN', '0', 'COUNT', '1'] + ); + }); + + it('with TYPE', () => { + assert.deepEqual( + transformArguments(0, { + TYPE: 'stream' + }), + ['SCAN', '0', 'TYPE', 'stream'] + ); + }); + + it('with MATCH & COUNT & TYPE', () => { + assert.deepEqual( + transformArguments(0, { + MATCH: 'pattern', + COUNT: 1, + TYPE: 'stream' + }), + ['SCAN', '0', 'MATCH', 'pattern', 'COUNT', '1', 'TYPE', 'stream'] + ); + }); + }); + + describe('transformReply', () => { + it('without keys', () => { + assert.deepEqual( + transformReply(['0', []]), + { + cursor: 0, + keys: [] + } + ); + }); + + it('with keys', () => { + assert.deepEqual( + transformReply(['0', ['key']]), + { + cursor: 0, + keys: ['key'] + } + ); + }); + }); + + itWithClient(TestRedisServers.OPEN, 'client.scan', async client => { + assert.deepEqual( + await client.scan(0), + { + cursor: 0, + keys: [] + } + ); + }); +}); diff --git a/lib/commands/SCAN.ts b/lib/commands/SCAN.ts new file mode 100644 index 00000000000..e3541ea9a74 --- /dev/null +++ b/lib/commands/SCAN.ts @@ -0,0 +1,28 @@ +import { ScanOptions, pushScanArguments } from './generic-transformers'; + +export const IS_READ_ONLY = true; +export interface ScanCommandOptions extends ScanOptions { + TYPE?: string; +} + +export function transformArguments(cursor: number, options?: ScanCommandOptions): Array { + const args = pushScanArguments(['SCAN'], cursor, options); + + if (options?.TYPE) { + args.push('TYPE', options.TYPE); + } + + return args; +} + +export interface ScanReply { + cursor: number; + keys: Array; +} + +export function transformReply([cursor, keys]: [string, Array]): ScanReply { + return { + cursor: Number(cursor), + keys + }; +} diff --git a/lib/commands/SCARD.spec.ts b/lib/commands/SCARD.spec.ts new file mode 100644 index 00000000000..b6681693814 --- /dev/null +++ b/lib/commands/SCARD.spec.ts @@ -0,0 +1,19 @@ +import { strict as assert } from 'assert'; +import { TestRedisServers, itWithClient } from '../test-utils'; +import { transformArguments } from './SCARD'; + +describe('SCARD', () => { + it('transformArguments', () => { + assert.deepEqual( + transformArguments('key'), + ['SCARD', 'key'] + ); + }); + + itWithClient(TestRedisServers.OPEN, 'client.sCard', async client => { + assert.equal( + await client.sCard('key'), + 0 + ); + }); +}); diff --git a/lib/commands/SCARD.ts b/lib/commands/SCARD.ts new file mode 100644 index 00000000000..8a90bd3b029 --- /dev/null +++ b/lib/commands/SCARD.ts @@ -0,0 +1,9 @@ +import { transformReplyNumber } from './generic-transformers'; + +export const FIRST_KEY_INDEX = 1; + +export function transformArguments(key: string): Array { + return ['SCARD', key]; +} + +export const transformReply = transformReplyNumber; diff --git a/lib/commands/SCRIPT_DEBUG.spec.ts b/lib/commands/SCRIPT_DEBUG.spec.ts new file mode 100644 index 00000000000..9096605143a --- /dev/null +++ b/lib/commands/SCRIPT_DEBUG.spec.ts @@ -0,0 +1,26 @@ +import { strict as assert } from 'assert'; +import { TestRedisServers, itWithClient, TestRedisClusters, itWithCluster } from '../test-utils'; +import { transformArguments } from './SCRIPT_DEBUG'; + +describe('SCRIPT DEBUG', () => { + it('transformArguments', () => { + assert.deepEqual( + transformArguments('NO'), + ['SCRIPT', 'DEBUG', 'NO'] + ); + }); + + itWithClient(TestRedisServers.OPEN, 'client.scriptDebug', async client => { + assert.equal( + await client.scriptDebug('NO'), + 'OK' + ); + }); + + itWithCluster(TestRedisClusters.OPEN, 'cluster.scriptDebug', async cluster => { + assert.equal( + await cluster.scriptDebug('NO'), + 'OK' + ); + }); +}); diff --git a/lib/commands/SCRIPT_DEBUG.ts b/lib/commands/SCRIPT_DEBUG.ts new file mode 100644 index 00000000000..e93443a5860 --- /dev/null +++ b/lib/commands/SCRIPT_DEBUG.ts @@ -0,0 +1,7 @@ +import { transformReplyString } from './generic-transformers'; + +export function transformArguments(mode: 'YES' | 'SYNC' | 'NO'): Array { + return ['SCRIPT', 'DEBUG', mode]; +} + +export const transformReply = transformReplyString; diff --git a/lib/commands/SCRIPT_EXISTS.spec.ts b/lib/commands/SCRIPT_EXISTS.spec.ts new file mode 100644 index 00000000000..d03521a5c60 --- /dev/null +++ b/lib/commands/SCRIPT_EXISTS.spec.ts @@ -0,0 +1,35 @@ +import { strict as assert } from 'assert'; +import { TestRedisServers, itWithClient, TestRedisClusters, itWithCluster } from '../test-utils'; +import { transformArguments } from './SCRIPT_EXISTS'; + +describe('SCRIPT EXISTS', () => { + describe('transformArguments', () => { + it('string', () => { + assert.deepEqual( + transformArguments('sha1'), + ['SCRIPT', 'EXISTS', 'sha1'] + ); + }); + + it('array', () => { + assert.deepEqual( + transformArguments(['1', '2']), + ['SCRIPT', 'EXISTS', '1', '2'] + ); + }); + }); + + itWithClient(TestRedisServers.OPEN, 'client.scriptExists', async client => { + assert.deepEqual( + await client.scriptExists('sha1'), + [false] + ); + }); + + itWithCluster(TestRedisClusters.OPEN, 'cluster.scriptExists', async cluster => { + assert.deepEqual( + await cluster.scriptExists('sha1'), + [false] + ); + }); +}); diff --git a/lib/commands/SCRIPT_EXISTS.ts b/lib/commands/SCRIPT_EXISTS.ts new file mode 100644 index 00000000000..b127a0b261b --- /dev/null +++ b/lib/commands/SCRIPT_EXISTS.ts @@ -0,0 +1,7 @@ +import { pushVerdictArguments, transformReplyBooleanArray } from './generic-transformers'; + +export function transformArguments(sha1: string | Array): Array { + return pushVerdictArguments(['SCRIPT', 'EXISTS'], sha1); +} + +export const transformReply = transformReplyBooleanArray; diff --git a/lib/commands/SCRIPT_FLUSH.spec.ts b/lib/commands/SCRIPT_FLUSH.spec.ts new file mode 100644 index 00000000000..c1321676ebe --- /dev/null +++ b/lib/commands/SCRIPT_FLUSH.spec.ts @@ -0,0 +1,35 @@ +import { strict as assert } from 'assert'; +import { TestRedisServers, itWithClient, TestRedisClusters, itWithCluster } from '../test-utils'; +import { transformArguments } from './SCRIPT_FLUSH'; + +describe('SCRIPT FLUSH', () => { + describe('transformArguments', () => { + it('simple', () => { + assert.deepEqual( + transformArguments(), + ['SCRIPT', 'FLUSH'] + ); + }); + + it('with mode', () => { + assert.deepEqual( + transformArguments('SYNC'), + ['SCRIPT', 'FLUSH', 'SYNC'] + ); + }); + }); + + itWithClient(TestRedisServers.OPEN, 'client.scriptFlush', async client => { + assert.equal( + await client.scriptFlush(), + 'OK' + ); + }); + + itWithCluster(TestRedisClusters.OPEN, 'cluster.scriptFlush', async cluster => { + assert.equal( + await cluster.scriptFlush(), + 'OK' + ); + }); +}); diff --git a/lib/commands/SCRIPT_FLUSH.ts b/lib/commands/SCRIPT_FLUSH.ts new file mode 100644 index 00000000000..83bc9e2b5d8 --- /dev/null +++ b/lib/commands/SCRIPT_FLUSH.ts @@ -0,0 +1,13 @@ +import { transformReplyString } from './generic-transformers'; + +export function transformArguments(mode?: 'ASYNC' | 'SYNC'): Array { + const args = ['SCRIPT', 'FLUSH']; + + if (mode) { + args.push(mode); + } + + return args; +} + +export const transformReply = transformReplyString; diff --git a/lib/commands/SCRIPT_KILL.spec.ts b/lib/commands/SCRIPT_KILL.spec.ts new file mode 100644 index 00000000000..e57265aa61a --- /dev/null +++ b/lib/commands/SCRIPT_KILL.spec.ts @@ -0,0 +1,11 @@ +import { strict as assert } from 'assert'; +import { transformArguments } from './SCRIPT_KILL'; + +describe('SCRIPT KILL', () => { + it('transformArguments', () => { + assert.deepEqual( + transformArguments(), + ['SCRIPT', 'KILL'] + ); + }); +}); diff --git a/lib/commands/SCRIPT_KILL.ts b/lib/commands/SCRIPT_KILL.ts new file mode 100644 index 00000000000..5c175b74d6c --- /dev/null +++ b/lib/commands/SCRIPT_KILL.ts @@ -0,0 +1,7 @@ +import { transformReplyString } from './generic-transformers'; + +export function transformArguments(): Array { + return ['SCRIPT', 'KILL']; +} + +export const transformReply = transformReplyString; diff --git a/lib/commands/SCRIPT_LOAD.spec.ts b/lib/commands/SCRIPT_LOAD.spec.ts new file mode 100644 index 00000000000..46490f35c8a --- /dev/null +++ b/lib/commands/SCRIPT_LOAD.spec.ts @@ -0,0 +1,30 @@ +import { strict as assert } from 'assert'; +import { scriptSha1 } from '../lua-script'; +import { TestRedisServers, itWithClient, TestRedisClusters, itWithCluster } from '../test-utils'; +import { transformArguments } from './SCRIPT_LOAD'; + +describe('SCRIPT LOAD', () => { + const SCRIPT = 'return 1;', + SCRIPT_SHA1 = scriptSha1(SCRIPT); + + it('transformArguments', () => { + assert.deepEqual( + transformArguments(SCRIPT), + ['SCRIPT', 'LOAD', SCRIPT] + ); + }); + + itWithClient(TestRedisServers.OPEN, 'client.scriptLoad', async client => { + assert.equal( + await client.scriptLoad(SCRIPT), + SCRIPT_SHA1 + ); + }); + + itWithCluster(TestRedisClusters.OPEN, 'cluster.scriptLoad', async cluster => { + assert.equal( + await cluster.scriptLoad(SCRIPT), + SCRIPT_SHA1 + ); + }); +}); diff --git a/lib/commands/SCRIPT_LOAD.ts b/lib/commands/SCRIPT_LOAD.ts new file mode 100644 index 00000000000..378fbf1e76a --- /dev/null +++ b/lib/commands/SCRIPT_LOAD.ts @@ -0,0 +1,7 @@ +import { transformReplyString } from './generic-transformers'; + +export function transformArguments(script: string): Array { + return ['SCRIPT', 'LOAD', script]; +} + +export const transformReply = transformReplyString; diff --git a/lib/commands/SDIFF.spec.ts b/lib/commands/SDIFF.spec.ts new file mode 100644 index 00000000000..82ef2dac6fc --- /dev/null +++ b/lib/commands/SDIFF.spec.ts @@ -0,0 +1,28 @@ +import { strict as assert } from 'assert'; +import { TestRedisServers, itWithClient } from '../test-utils'; +import { transformArguments } from './SDIFF'; + +describe('SDIFF', () => { + describe('transformArguments', () => { + it('string', () => { + assert.deepEqual( + transformArguments('key'), + ['SDIFF', 'key'] + ); + }); + + it('array', () => { + assert.deepEqual( + transformArguments(['1', '2']), + ['SDIFF', '1', '2'] + ); + }); + }); + + itWithClient(TestRedisServers.OPEN, 'client.sDiff', async client => { + assert.deepEqual( + await client.sDiff('key'), + [] + ); + }); +}); diff --git a/lib/commands/SDIFF.ts b/lib/commands/SDIFF.ts new file mode 100644 index 00000000000..496ed593370 --- /dev/null +++ b/lib/commands/SDIFF.ts @@ -0,0 +1,9 @@ +import { pushVerdictArguments, transformReplyStringArray } from './generic-transformers'; + +export const FIRST_KEY_INDEX = 1; + +export function transformArguments(keys: string | Array): Array { + return pushVerdictArguments(['SDIFF'], keys); +} + +export const transformReply = transformReplyStringArray; diff --git a/lib/commands/SDIFFSTORE.spec.ts b/lib/commands/SDIFFSTORE.spec.ts new file mode 100644 index 00000000000..1e7f5f6f32c --- /dev/null +++ b/lib/commands/SDIFFSTORE.spec.ts @@ -0,0 +1,28 @@ +import { strict as assert } from 'assert'; +import { TestRedisServers, itWithClient } from '../test-utils'; +import { transformArguments } from './SDIFFSTORE'; + +describe('SDIFFSTORE', () => { + describe('transformArguments', () => { + it('string', () => { + assert.deepEqual( + transformArguments('destination', 'key'), + ['SDIFFSTORE', 'destination', 'key'] + ); + }); + + it('array', () => { + assert.deepEqual( + transformArguments('destination', ['1', '2']), + ['SDIFFSTORE', 'destination', '1', '2'] + ); + }); + }); + + itWithClient(TestRedisServers.OPEN, 'client.sDiffStore', async client => { + assert.equal( + await client.sDiffStore('destination', 'key'), + 0 + ); + }); +}); diff --git a/lib/commands/SDIFFSTORE.ts b/lib/commands/SDIFFSTORE.ts new file mode 100644 index 00000000000..295433602fb --- /dev/null +++ b/lib/commands/SDIFFSTORE.ts @@ -0,0 +1,9 @@ +import { pushVerdictArguments, transformReplyNumber } from './generic-transformers'; + +export const FIRST_KEY_INDEX = 1; + +export function transformArguments(destination: string, keys: string | Array): Array { + return pushVerdictArguments(['SDIFFSTORE', destination], keys); +} + +export const transformReply = transformReplyNumber; diff --git a/lib/commands/SET.spec.ts b/lib/commands/SET.spec.ts new file mode 100644 index 00000000000..a587f6c3120 --- /dev/null +++ b/lib/commands/SET.spec.ts @@ -0,0 +1,121 @@ +import { strict as assert } from 'assert'; +import { TestRedisServers, itWithClient } from '../test-utils'; +import { transformArguments } from './SET'; + +describe('SET', () => { + describe('transformArguments', () => { + it('simple', () => { + assert.deepEqual( + transformArguments('key', 'value'), + ['SET', 'key', 'value'] + ); + }); + + describe('TTL', () => { + it('with EX', () => { + assert.deepEqual( + transformArguments('key', 'value', { + EX: 1 + }), + ['SET', 'key', 'value', 'EX', '1'] + ); + }); + + it('with PX', () => { + assert.deepEqual( + transformArguments('key', 'value', { + PX: 1 + }), + ['SET', 'key', 'value', 'PX', '1'] + ); + }); + + it('with EXAT', () => { + assert.deepEqual( + transformArguments('key', 'value', { + EXAT: 1 + }), + ['SET', 'key', 'value', 'EXAT', '1'] + ); + }); + + it('with PXAT', () => { + assert.deepEqual( + transformArguments('key', 'value', { + PXAT: 1 + }), + ['SET', 'key', 'value', 'PXAT', '1'] + ); + }); + + it('with KEEPTTL', () => { + assert.deepEqual( + transformArguments('key', 'value', { + KEEPTTL: true + }), + ['SET', 'key', 'value', 'KEEPTTL'] + ); + }); + }); + + describe('Guards', () => { + it('with NX', () => { + assert.deepEqual( + transformArguments('key', 'value', { + NX: true + }), + ['SET', 'key', 'value', 'NX'] + ); + }); + + it('with XX', () => { + assert.deepEqual( + transformArguments('key', 'value', { + XX: true + }), + ['SET', 'key', 'value', 'XX'] + ); + }); + }); + + it('with GET', () => { + assert.deepEqual( + transformArguments('key', 'value', { + GET: true + }), + ['SET', 'key', 'value', 'GET'] + ); + }); + + it('with EX, NX, GET', () => { + assert.deepEqual( + transformArguments('key', 'value', { + EX: 1, + NX: true, + GET: true + }), + ['SET', 'key', 'value', 'EX', '1', 'NX', 'GET'] + ); + }); + }); + + describe('client.set', () => { + itWithClient(TestRedisServers.OPEN, 'simple', async client => { + assert.equal( + await client.set('key', 'value'), + 'OK' + ); + }); + + itWithClient(TestRedisServers.OPEN, 'with GET on empty key', async client => { + assert.equal( + await client.set('key', 'value', { + GET: true + }), + null + ); + }, { + minimumRedisVersion: [6, 2] + }); + }); +}); diff --git a/lib/commands/SET.ts b/lib/commands/SET.ts new file mode 100644 index 00000000000..4d5919cde21 --- /dev/null +++ b/lib/commands/SET.ts @@ -0,0 +1,75 @@ +export const FIRST_KEY_INDEX = 1; + +interface EX { + EX: number; +} + +interface PX { + PX: number +} + +interface EXAT { + EXAT: number; +} + +interface PXAT { + PXAT: number; +} + +interface KEEPTTL { + KEEPTTL: true; +} + +type SetTTL = EX | PX | EXAT | PXAT | KEEPTTL | {}; + +interface NX { + NX: true; +} + +interface XX { + XX: true; +} + +type SetGuards = NX | XX | {}; + +interface SetCommonOptions { + GET: true +} + +type SetOptions = SetTTL & SetGuards & (SetCommonOptions | {}); + +export function transformArguments(key: string, value: string, options?: SetOptions): Array { + const args = ['SET', key, value]; + + if (!options) { + return args; + } + + if ('EX' in options) { + args.push('EX', options.EX.toString()); + } else if ('PX' in options) { + args.push('PX', options.PX.toString()); + } else if ('EXAT' in options) { + args.push('EXAT', options.EXAT.toString()); + } else if ('PXAT' in options) { + args.push('PXAT', options.PXAT.toString()); + } else if ((options).KEEPTTL) { + args.push('KEEPTTL'); + } + + if ((options).NX) { + args.push('NX'); + } else if ((options).XX) { + args.push('XX'); + } + + if ((options).GET) { + args.push('GET'); + } + + return args; +} + +export function transformReply(reply?: string): string | null { + return reply ?? null; +} diff --git a/lib/commands/SETBIT.spec.ts b/lib/commands/SETBIT.spec.ts new file mode 100644 index 00000000000..7347913f293 --- /dev/null +++ b/lib/commands/SETBIT.spec.ts @@ -0,0 +1,26 @@ +import { strict as assert } from 'assert'; +import { TestRedisServers, itWithClient, TestRedisClusters, itWithCluster } from '../test-utils'; +import { transformArguments } from './SETBIT'; + +describe('SETBIT', () => { + it('transformArguments', () => { + assert.deepEqual( + transformArguments('key', 0, 1), + ['SETBIT', 'key', '0', '1'] + ); + }); + + itWithClient(TestRedisServers.OPEN, 'client.setBit', async client => { + assert.equal( + await client.setBit('key', 0, 1), + 0 + ); + }); + + itWithCluster(TestRedisClusters.OPEN, 'cluster.setBit', async cluster => { + assert.equal( + await cluster.setBit('key', 0, 1), + 0 + ); + }); +}); diff --git a/lib/commands/SETBIT.ts b/lib/commands/SETBIT.ts new file mode 100644 index 00000000000..0cd41d1b975 --- /dev/null +++ b/lib/commands/SETBIT.ts @@ -0,0 +1,9 @@ +import { BitValue, transformReplyBit } from './generic-transformers'; + +export const FIRST_KEY_INDEX = 1; + +export function transformArguments(key: string, offset: number, value: BitValue) { + return ['SETBIT', key, offset.toString(), value.toString()]; +} + +export const transformReply = transformReplyBit; diff --git a/lib/commands/SETEX.spec.ts b/lib/commands/SETEX.spec.ts new file mode 100644 index 00000000000..7ea55eba83c --- /dev/null +++ b/lib/commands/SETEX.spec.ts @@ -0,0 +1,26 @@ +import { strict as assert } from 'assert'; +import { TestRedisServers, itWithClient, TestRedisClusters, itWithCluster } from '../test-utils'; +import { transformArguments } from './SETEX'; + +describe('SETEX', () => { + it('transformArguments', () => { + assert.deepEqual( + transformArguments('key', 1, 'value'), + ['SETEX', 'key', '1', 'value'] + ); + }); + + itWithClient(TestRedisServers.OPEN, 'client.setEx', async client => { + assert.equal( + await client.setEx('key', 1, 'value'), + 'OK' + ); + }); + + itWithCluster(TestRedisClusters.OPEN, 'cluster.setEx', async cluster => { + assert.equal( + await cluster.setEx('key', 1, 'value'), + 'OK' + ); + }); +}); diff --git a/lib/commands/SETEX.ts b/lib/commands/SETEX.ts new file mode 100644 index 00000000000..57c32db6ffe --- /dev/null +++ b/lib/commands/SETEX.ts @@ -0,0 +1,14 @@ +import { transformReplyString } from './generic-transformers'; + +export const FIRST_KEY_INDEX = 1; + +export function transformArguments(key: string, seconds: number, value: string): Array { + return [ + 'SETEX', + key, + seconds.toString(), + value + ]; +} + +export const transformReply = transformReplyString; diff --git a/lib/commands/SETNX .spec.ts b/lib/commands/SETNX .spec.ts new file mode 100644 index 00000000000..daf3ca6e76a --- /dev/null +++ b/lib/commands/SETNX .spec.ts @@ -0,0 +1,26 @@ +import { strict as assert } from 'assert'; +import { TestRedisServers, itWithClient, TestRedisClusters, itWithCluster } from '../test-utils'; +import { transformArguments } from './SETNX'; + +describe('SETNX', () => { + it('transformArguments', () => { + assert.deepEqual( + transformArguments('key', 'value'), + ['SETNX', 'key', 'value'] + ); + }); + + itWithClient(TestRedisServers.OPEN, 'client.setNX', async client => { + assert.equal( + await client.setNX('key', 'value'), + true + ); + }); + + itWithCluster(TestRedisClusters.OPEN, 'cluster.setNX', async cluster => { + assert.equal( + await cluster.setNX('key', 'value'), + true + ); + }); +}); diff --git a/lib/commands/SETNX.ts b/lib/commands/SETNX.ts new file mode 100644 index 00000000000..f0097836581 --- /dev/null +++ b/lib/commands/SETNX.ts @@ -0,0 +1,9 @@ +import { transformReplyBoolean } from './generic-transformers'; + +export const FIRST_KEY_INDEX = 1; + +export function transformArguments(key: string, value: string): Array { + return ['SETNX', key, value]; +} + +export const transformReply = transformReplyBoolean; diff --git a/lib/commands/SETRANGE.spec.ts b/lib/commands/SETRANGE.spec.ts new file mode 100644 index 00000000000..766c56c5ff1 --- /dev/null +++ b/lib/commands/SETRANGE.spec.ts @@ -0,0 +1,26 @@ +import { strict as assert } from 'assert'; +import { TestRedisServers, itWithClient, itWithCluster, TestRedisClusters } from '../test-utils'; +import { transformArguments } from './SETRANGE'; + +describe('SETRANGE', () => { + it('transformArguments', () => { + assert.deepEqual( + transformArguments('key', 0, 'value'), + ['SETRANGE', 'key', '0', 'value'] + ); + }); + + itWithClient(TestRedisServers.OPEN, 'client.setRange', async client => { + assert.equal( + await client.setRange('key', 0, 'value'), + 5 + ); + }); + + itWithCluster(TestRedisClusters.OPEN, 'cluster.setRange', async cluster => { + assert.equal( + await cluster.setRange('key', 0, 'value'), + 5 + ); + }); +}); diff --git a/lib/commands/SETRANGE.ts b/lib/commands/SETRANGE.ts new file mode 100644 index 00000000000..a303487ddd4 --- /dev/null +++ b/lib/commands/SETRANGE.ts @@ -0,0 +1,9 @@ +import { transformReplyNumber } from './generic-transformers'; + +export const FIRST_KEY_INDEX = 1; + +export function transformArguments(key: string, offset: number, value: string): Array { + return ['SETRANGE', key, offset.toString(), value]; +} + +export const transformReply = transformReplyNumber; diff --git a/lib/commands/SHUTDOWN.spec.ts b/lib/commands/SHUTDOWN.spec.ts new file mode 100644 index 00000000000..d58cf4443c7 --- /dev/null +++ b/lib/commands/SHUTDOWN.spec.ts @@ -0,0 +1,27 @@ +import { strict as assert } from 'assert'; +import { transformArguments } from './SHUTDOWN'; + +describe('SHUTDOWN', () => { + describe('transformArguments', () => { + it('simple', () => { + assert.deepEqual( + transformArguments(), + ['SHUTDOWN'] + ); + }); + + it('NOSAVE', () => { + assert.deepEqual( + transformArguments('NOSAVE'), + ['SHUTDOWN', 'NOSAVE'] + ); + }); + + it('SAVE', () => { + assert.deepEqual( + transformArguments('SAVE'), + ['SHUTDOWN', 'SAVE'] + ); + }); + }); +}); diff --git a/lib/commands/SHUTDOWN.ts b/lib/commands/SHUTDOWN.ts new file mode 100644 index 00000000000..0dd2cf3a5b3 --- /dev/null +++ b/lib/commands/SHUTDOWN.ts @@ -0,0 +1,13 @@ +import { transformReplyVoid } from './generic-transformers'; + +export function transformArguments(mode?: 'NOSAVE' | 'SAVE'): Array { + const args = ['SHUTDOWN']; + + if (mode) { + args.push(mode); + } + + return args; +} + +export const transformReply = transformReplyVoid; diff --git a/lib/commands/SINTER.spec.ts b/lib/commands/SINTER.spec.ts new file mode 100644 index 00000000000..8fee35427cf --- /dev/null +++ b/lib/commands/SINTER.spec.ts @@ -0,0 +1,28 @@ +import { strict as assert } from 'assert'; +import { TestRedisServers, itWithClient } from '../test-utils'; +import { transformArguments } from './SINTER'; + +describe('SINTER', () => { + describe('transformArguments', () => { + it('string', () => { + assert.deepEqual( + transformArguments('key'), + ['SINTER', 'key'] + ); + }); + + it('array', () => { + assert.deepEqual( + transformArguments(['1', '2']), + ['SINTER', '1', '2'] + ); + }); + }); + + itWithClient(TestRedisServers.OPEN, 'client.sInter', async client => { + assert.deepEqual( + await client.sInter('key'), + [] + ); + }); +}); diff --git a/lib/commands/SINTER.ts b/lib/commands/SINTER.ts new file mode 100644 index 00000000000..104e81b9214 --- /dev/null +++ b/lib/commands/SINTER.ts @@ -0,0 +1,9 @@ +import { pushVerdictArguments, transformReplyStringArray } from './generic-transformers'; + +export const FIRST_KEY_INDEX = 1; + +export function transformArguments(keys: string | Array): Array { + return pushVerdictArguments(['SINTER'], keys); +} + +export const transformReply = transformReplyStringArray; diff --git a/lib/commands/SINTERSTORE.spec.ts b/lib/commands/SINTERSTORE.spec.ts new file mode 100644 index 00000000000..013931d2312 --- /dev/null +++ b/lib/commands/SINTERSTORE.spec.ts @@ -0,0 +1,28 @@ +import { strict as assert } from 'assert'; +import { TestRedisServers, itWithClient } from '../test-utils'; +import { transformArguments } from './SINTERSTORE'; + +describe('SINTERSTORE', () => { + describe('transformArguments', () => { + it('string', () => { + assert.deepEqual( + transformArguments('destination', 'key'), + ['SINTERSTORE', 'destination', 'key'] + ); + }); + + it('array', () => { + assert.deepEqual( + transformArguments('destination', ['1', '2']), + ['SINTERSTORE', 'destination', '1', '2'] + ); + }); + }); + + itWithClient(TestRedisServers.OPEN, 'client.sInterStore', async client => { + assert.equal( + await client.sInterStore('destination', 'key'), + 0 + ); + }); +}); diff --git a/lib/commands/SINTERSTORE.ts b/lib/commands/SINTERSTORE.ts new file mode 100644 index 00000000000..a7a4d4fd106 --- /dev/null +++ b/lib/commands/SINTERSTORE.ts @@ -0,0 +1,9 @@ +import { pushVerdictArguments, transformReplyStringArray } from './generic-transformers'; + +export const FIRST_KEY_INDEX = 1; + +export function transformArguments(destination: string, keys: string | Array): Array { + return pushVerdictArguments(['SINTERSTORE', destination], keys); +} + +export const transformReply = transformReplyStringArray; diff --git a/lib/commands/SISMEMBER.spec.ts b/lib/commands/SISMEMBER.spec.ts new file mode 100644 index 00000000000..fec4ebfc57d --- /dev/null +++ b/lib/commands/SISMEMBER.spec.ts @@ -0,0 +1,21 @@ +import { strict as assert } from 'assert'; +import { TestRedisServers, itWithClient, describeHandleMinimumRedisVersion } from '../test-utils'; +import { transformArguments } from './SISMEMBER'; + +describe('SISMEMBER', () => { + describeHandleMinimumRedisVersion([6, 2]); + + it('transformArguments', () => { + assert.deepEqual( + transformArguments('key', 'member'), + ['SISMEMBER', 'key', 'member'] + ); + }); + + itWithClient(TestRedisServers.OPEN, 'client.sIsMember', async client => { + assert.equal( + await client.sIsMember('key', 'member'), + false + ); + }); +}); diff --git a/lib/commands/SISMEMBER.ts b/lib/commands/SISMEMBER.ts new file mode 100644 index 00000000000..661410fce0d --- /dev/null +++ b/lib/commands/SISMEMBER.ts @@ -0,0 +1,9 @@ +import { transformReplyBoolean } from './generic-transformers'; + +export const FIRST_KEY_INDEX = 1; + +export function transformArguments(key: string, member: string): Array { + return ['SISMEMBER', key, member]; +} + +export const transformReply = transformReplyBoolean; diff --git a/lib/commands/SMEMBERS.spec.ts b/lib/commands/SMEMBERS.spec.ts new file mode 100644 index 00000000000..2398dbaa8c6 --- /dev/null +++ b/lib/commands/SMEMBERS.spec.ts @@ -0,0 +1,19 @@ +import { strict as assert } from 'assert'; +import { TestRedisServers, itWithClient } from '../test-utils'; +import { transformArguments } from './SMEMBERS'; + +describe('SMEMBERS', () => { + it('transformArguments', () => { + assert.deepEqual( + transformArguments('key'), + ['SMEMBERS', 'key'] + ); + }); + + itWithClient(TestRedisServers.OPEN, 'client.sMembers', async client => { + assert.deepEqual( + await client.sMembers('key'), + [] + ); + }); +}); diff --git a/lib/commands/SMEMBERS.ts b/lib/commands/SMEMBERS.ts new file mode 100644 index 00000000000..d7e75daaa3e --- /dev/null +++ b/lib/commands/SMEMBERS.ts @@ -0,0 +1,9 @@ +import { transformReplyStringArray } from './generic-transformers'; + +export const FIRST_KEY_INDEX = 1; + +export function transformArguments(key: string): Array { + return ['SMEMBERS', key]; +} + +export const transformReply = transformReplyStringArray; diff --git a/lib/commands/SMISMEMBER.spec.ts b/lib/commands/SMISMEMBER.spec.ts new file mode 100644 index 00000000000..320f60d4ba2 --- /dev/null +++ b/lib/commands/SMISMEMBER.spec.ts @@ -0,0 +1,21 @@ +import { strict as assert } from 'assert'; +import { TestRedisServers, itWithClient, describeHandleMinimumRedisVersion } from '../test-utils'; +import { transformArguments } from './SMISMEMBER'; + +describe('SMISMEMBER', () => { + describeHandleMinimumRedisVersion([6, 2]); + + it('transformArguments', () => { + assert.deepEqual( + transformArguments('key', ['1', '2']), + ['SMISMEMBER', 'key', '1', '2'] + ); + }); + + itWithClient(TestRedisServers.OPEN, 'client.smIsMember', async client => { + assert.deepEqual( + await client.smIsMember('key', ['1', '2']), + [false, false] + ); + }); +}); diff --git a/lib/commands/SMISMEMBER.ts b/lib/commands/SMISMEMBER.ts new file mode 100644 index 00000000000..07637a689b6 --- /dev/null +++ b/lib/commands/SMISMEMBER.ts @@ -0,0 +1,9 @@ +import { transformReplyBooleanArray } from './generic-transformers'; + +export const FIRST_KEY_INDEX = 1; + +export function transformArguments(key: string, members: Array): Array { + return ['SMISMEMBER', key, ...members]; +} + +export const transformReply = transformReplyBooleanArray; diff --git a/lib/commands/SMOVE.spec.ts b/lib/commands/SMOVE.spec.ts new file mode 100644 index 00000000000..97e938a46bb --- /dev/null +++ b/lib/commands/SMOVE.spec.ts @@ -0,0 +1,19 @@ +import { strict as assert } from 'assert'; +import { TestRedisServers, itWithClient } from '../test-utils'; +import { transformArguments } from './SMOVE'; + +describe('SMOVE', () => { + it('transformArguments', () => { + assert.deepEqual( + transformArguments('source', 'destination', 'member'), + ['SMOVE', 'source', 'destination', 'member'] + ); + }); + + itWithClient(TestRedisServers.OPEN, 'client.sMove', async client => { + assert.equal( + await client.sMove('source', 'destination', 'member'), + false + ); + }); +}); diff --git a/lib/commands/SMOVE.ts b/lib/commands/SMOVE.ts new file mode 100644 index 00000000000..f8922f6ca3f --- /dev/null +++ b/lib/commands/SMOVE.ts @@ -0,0 +1,9 @@ +import { transformReplyBoolean } from './generic-transformers'; + +export const FIRST_KEY_INDEX = 1; + +export function transformArguments(source: string, destination: string, member: string): Array { + return ['SMOVE', source, destination, member]; +} + +export const transformReply = transformReplyBoolean; diff --git a/lib/commands/SORT.spec.ts b/lib/commands/SORT.spec.ts new file mode 100644 index 00000000000..c449e0511f0 --- /dev/null +++ b/lib/commands/SORT.spec.ts @@ -0,0 +1,106 @@ +import { strict as assert } from 'assert'; +import { TestRedisServers, itWithClient } from '../test-utils'; +import { transformArguments } from './SORT'; + +describe('SORT', () => { + describe('transformArguments', () => { + it('simple', () => { + assert.deepEqual( + transformArguments('key'), + ['SORT', 'key'] + ); + }); + + it('with BY', () => { + assert.deepEqual( + transformArguments('key', { + BY: 'pattern' + }), + ['SORT', 'key', 'BY', 'pattern'] + ); + }); + + it('with LIMIT', () => { + assert.deepEqual( + transformArguments('key', { + LIMIT: { + offset: 0, + count: 1 + } + }), + ['SORT', 'key', 'LIMIT', '0', '1'] + ); + }); + + describe('with GET', () => { + it('string', () => { + assert.deepEqual( + transformArguments('key', { + GET: 'pattern' + }), + ['SORT', 'key', 'GET', 'pattern'] + ); + }); + + it('array', () => { + assert.deepEqual( + transformArguments('key', { + GET: ['1', '2'] + }), + ['SORT', 'key', 'GET', '1', 'GET', '2'] + ); + }); + }); + + it('with DIRECTION', () => { + assert.deepEqual( + transformArguments('key', { + DIRECTION: 'ASC' + }), + ['SORT', 'key', 'ASC'] + ); + }); + + it('with ALPHA', () => { + assert.deepEqual( + transformArguments('key', { + ALPHA: true + }), + ['SORT', 'key', 'ALPHA'] + ); + }); + + it('with STORE', () => { + assert.deepEqual( + transformArguments('key', { + STORE: 'destination' + }), + ['SORT', 'key', 'STORE', 'destination'] + ); + }); + + it('with BY, LIMIT, GET, DIRECTION, ALPHA, STORE', () => { + assert.deepEqual( + transformArguments('key', { + BY: 'pattern', + LIMIT: { + offset: 0, + count: 1 + }, + GET: 'pattern', + DIRECTION: 'ASC', + ALPHA: true, + STORE: 'destination' + }), + ['SORT', 'key', 'BY', 'pattern', 'LIMIT', '0', '1', 'GET', 'pattern', 'ASC', 'ALPHA', 'STORE', 'destination'] + ); + }); + }); + + itWithClient(TestRedisServers.OPEN, 'client.sort', async client => { + assert.deepEqual( + await client.sort('key'), + [] + ); + }); +}); diff --git a/lib/commands/SORT.ts b/lib/commands/SORT.ts new file mode 100644 index 00000000000..0305d93144c --- /dev/null +++ b/lib/commands/SORT.ts @@ -0,0 +1,57 @@ +export const FIRST_KEY_INDEX = 1; + +export const IS_READ_ONLY = true; + + +interface SortOptions { + BY?: string; + LIMIT?: { + offset: number; + count: number; + }, + GET?: string | Array; + DIRECTION?: 'ASC' | 'DESC'; + ALPHA?: true; + STORE?: string; +} + +export function transformArguments(key: string, options?: SortOptions): Array { + const args = ['SORT', key]; + + if (options?.BY) { + args.push('BY', options.BY); + } + + if (options?.LIMIT) { + args.push( + 'LIMIT', + options.LIMIT.offset.toString(), + options.LIMIT.count.toString() + ); + } + + if (options?.GET) { + for (const pattern of (typeof options.GET === 'string' ? [options.GET] : options.GET)) { + args.push('GET', pattern); + } + } + + if (options?.DIRECTION) { + args.push(options.DIRECTION); + } + + if (options?.ALPHA) { + args.push('ALPHA'); + } + + if (options?.STORE) { + args.push('STORE', options.STORE); + } + + return args; +} + +// integer when using `STORE` +export function transformReply(reply: Array | number): Array | number { + return reply; +} diff --git a/lib/commands/SPOP.spec.ts b/lib/commands/SPOP.spec.ts new file mode 100644 index 00000000000..238c58f4796 --- /dev/null +++ b/lib/commands/SPOP.spec.ts @@ -0,0 +1,28 @@ +import { strict as assert } from 'assert'; +import { TestRedisServers, itWithClient } from '../test-utils'; +import { transformArguments } from './SPOP'; + +describe('SPOP', () => { + describe('transformArguments', () => { + it('simple', () => { + assert.deepEqual( + transformArguments('key'), + ['SPOP', 'key'] + ); + }); + + it('with count', () => { + assert.deepEqual( + transformArguments('key', 2), + ['SPOP', 'key', '2'] + ); + }); + }); + + itWithClient(TestRedisServers.OPEN, 'client.sPop', async client => { + assert.equal( + await client.sPop('key'), + null + ); + }); +}); diff --git a/lib/commands/SPOP.ts b/lib/commands/SPOP.ts new file mode 100644 index 00000000000..a389fed5cc5 --- /dev/null +++ b/lib/commands/SPOP.ts @@ -0,0 +1,15 @@ +import { transformReplyStringArray } from './generic-transformers'; + +export const FIRST_KEY_INDEX = 1; + +export function transformArguments(key: string, count?: number): Array { + const args = ['SPOP', key]; + + if (typeof count === 'number') { + args.push(count.toString()); + } + + return args; +} + +export const transformReply = transformReplyStringArray; diff --git a/lib/commands/SRANDMEMBER.spec.ts b/lib/commands/SRANDMEMBER.spec.ts new file mode 100644 index 00000000000..5c359f73f96 --- /dev/null +++ b/lib/commands/SRANDMEMBER.spec.ts @@ -0,0 +1,19 @@ +import { strict as assert } from 'assert'; +import { TestRedisServers, itWithClient } from '../test-utils'; +import { transformArguments } from './SRANDMEMBER'; + +describe('SRANDMEMBER', () => { + it('transformArguments', () => { + assert.deepEqual( + transformArguments('key'), + ['SRANDMEMBER', 'key'] + ); + }); + + itWithClient(TestRedisServers.OPEN, 'client.sRandMember', async client => { + assert.equal( + await client.sRandMember('key'), + null + ); + }); +}); diff --git a/lib/commands/SRANDMEMBER.ts b/lib/commands/SRANDMEMBER.ts new file mode 100644 index 00000000000..2e8cd539273 --- /dev/null +++ b/lib/commands/SRANDMEMBER.ts @@ -0,0 +1,9 @@ +import { transformReplyStringNull } from './generic-transformers'; + +export const FIRST_KEY_INDEX = 1; + +export function transformArguments(key: string): Array { + return ['SRANDMEMBER', key]; +} + +export const transformReply = transformReplyStringNull; diff --git a/lib/commands/SRANDMEMBER_COUNT.spec.ts b/lib/commands/SRANDMEMBER_COUNT.spec.ts new file mode 100644 index 00000000000..81a4fd45f31 --- /dev/null +++ b/lib/commands/SRANDMEMBER_COUNT.spec.ts @@ -0,0 +1,19 @@ +import { strict as assert } from 'assert'; +import { TestRedisServers, itWithClient } from '../test-utils'; +import { transformArguments } from './SRANDMEMBER_COUNT'; + +describe('SRANDMEMBER COUNT', () => { + it('transformArguments', () => { + assert.deepEqual( + transformArguments('key', 1), + ['SRANDMEMBER', 'key', '1'] + ); + }); + + itWithClient(TestRedisServers.OPEN, 'client.sRandMemberCount', async client => { + assert.deepEqual( + await client.sRandMemberCount('key', 1), + [] + ); + }); +}); diff --git a/lib/commands/SRANDMEMBER_COUNT.ts b/lib/commands/SRANDMEMBER_COUNT.ts new file mode 100644 index 00000000000..b7fa8ebeb3a --- /dev/null +++ b/lib/commands/SRANDMEMBER_COUNT.ts @@ -0,0 +1,13 @@ +import { transformReplyStringArray } from './generic-transformers'; +import { transformArguments as transformSRandMemberArguments } from './SRANDMEMBER'; + +export { FIRST_KEY_INDEX } from './SRANDMEMBER'; + +export function transformArguments(key: string, count: number): Array { + return [ + ...transformSRandMemberArguments(key), + count.toString() + ]; +} + +export const transformReply = transformReplyStringArray; diff --git a/lib/commands/SREM.spec.ts b/lib/commands/SREM.spec.ts new file mode 100644 index 00000000000..c9270624ae9 --- /dev/null +++ b/lib/commands/SREM.spec.ts @@ -0,0 +1,28 @@ +import { strict as assert } from 'assert'; +import { TestRedisServers, itWithClient } from '../test-utils'; +import { transformArguments } from './SREM'; + +describe('SREM', () => { + describe('transformArguments', () => { + it('string', () => { + assert.deepEqual( + transformArguments('key', 'member'), + ['SREM', 'key', 'member'] + ); + }); + + it('array', () => { + assert.deepEqual( + transformArguments('key', ['1', '2']), + ['SREM', 'key', '1', '2'] + ); + }); + }); + + itWithClient(TestRedisServers.OPEN, 'client.sRem', async client => { + assert.equal( + await client.sRem('key', 'member'), + 0 + ); + }); +}); diff --git a/lib/commands/SREM.ts b/lib/commands/SREM.ts new file mode 100644 index 00000000000..d1021bb3a19 --- /dev/null +++ b/lib/commands/SREM.ts @@ -0,0 +1,9 @@ +import { pushVerdictArguments, transformReplyNumber } from './generic-transformers'; + +export const FIRST_KEY_INDEX = 1; + +export function transformArguments(key: string, members: string | Array): Array { + return pushVerdictArguments(['SREM', key], members); +} + +export const transformReply = transformReplyNumber; diff --git a/lib/commands/SSCAN.spec.ts b/lib/commands/SSCAN.spec.ts new file mode 100644 index 00000000000..9b203ffb83e --- /dev/null +++ b/lib/commands/SSCAN.spec.ts @@ -0,0 +1,74 @@ +import { strict as assert } from 'assert'; +import { TestRedisServers, itWithClient } from '../test-utils'; +import { transformArguments, transformReply } from './SSCAN'; + +describe('SSCAN', () => { + describe('transformArguments', () => { + it('cusror only', () => { + assert.deepEqual( + transformArguments('key', 0), + ['SSCAN', 'key', '0'] + ); + }); + + it('with MATCH', () => { + assert.deepEqual( + transformArguments('key', 0, { + MATCH: 'pattern' + }), + ['SSCAN', 'key', '0', 'MATCH', 'pattern'] + ); + }); + + it('with COUNT', () => { + assert.deepEqual( + transformArguments('key', 0, { + COUNT: 1 + }), + ['SSCAN', 'key', '0', 'COUNT', '1'] + ); + }); + + it('with MATCH & COUNT', () => { + assert.deepEqual( + transformArguments('key', 0, { + MATCH: 'pattern', + COUNT: 1 + }), + ['SSCAN', 'key', '0', 'MATCH', 'pattern', 'COUNT', '1'] + ); + }); + }); + + describe('transformReply', () => { + it('without members', () => { + assert.deepEqual( + transformReply(['0', []]), + { + cursor: 0, + members: [] + } + ); + }); + + it('with members', () => { + assert.deepEqual( + transformReply(['0', ['member']]), + { + cursor: 0, + members: ['member'] + } + ); + }); + }); + + itWithClient(TestRedisServers.OPEN, 'client.sScan', async client => { + assert.deepEqual( + await client.sScan('key', 0), + { + cursor: 0, + members: [] + } + ); + }); +}); diff --git a/lib/commands/SSCAN.ts b/lib/commands/SSCAN.ts new file mode 100644 index 00000000000..9b881f5d882 --- /dev/null +++ b/lib/commands/SSCAN.ts @@ -0,0 +1,24 @@ +import { ScanOptions, pushScanArguments } from './generic-transformers'; + +export const FIRST_KEY_INDEX = 1; + +export const IS_READ_ONLY = true; + +export function transformArguments(key: string, cursor: number, options?: ScanOptions): Array { + return pushScanArguments([ + 'SSCAN', + key, + ], cursor, options); +} + +interface SScanReply { + cursor: number; + members: Array; +} + +export function transformReply([cursor, members]: [string, Array]): SScanReply { + return { + cursor: Number(cursor), + members + }; +} diff --git a/lib/commands/STRLEN.spec.ts b/lib/commands/STRLEN.spec.ts new file mode 100644 index 00000000000..3d24e360372 --- /dev/null +++ b/lib/commands/STRLEN.spec.ts @@ -0,0 +1,26 @@ +import { strict as assert } from 'assert'; +import { TestRedisServers, itWithClient, itWithCluster, TestRedisClusters } from '../test-utils'; +import { transformArguments } from './STRLEN'; + +describe('STRLEN', () => { + it('transformArguments', () => { + assert.deepEqual( + transformArguments('key'), + ['STRLEN', 'key'] + ); + }); + + itWithClient(TestRedisServers.OPEN, 'client.strLen', async client => { + assert.equal( + await client.strLen('key'), + 0 + ); + }); + + itWithCluster(TestRedisClusters.OPEN, 'cluster.strLen', async cluster => { + assert.equal( + await cluster.strLen('key'), + 0 + ); + }); +}); diff --git a/lib/commands/STRLEN.ts b/lib/commands/STRLEN.ts new file mode 100644 index 00000000000..d8112ce7d1f --- /dev/null +++ b/lib/commands/STRLEN.ts @@ -0,0 +1,11 @@ +import { transformReplyNumber } from './generic-transformers'; + +export const FIRST_KEY_INDEX = 1; + +export const IS_READ_ONLY = true; + +export function transformArguments(key: string): Array { + return ['STRLEN', key]; +} + +export const transformReply = transformReplyNumber; diff --git a/lib/commands/SUNION.spec.ts b/lib/commands/SUNION.spec.ts new file mode 100644 index 00000000000..fdf97668971 --- /dev/null +++ b/lib/commands/SUNION.spec.ts @@ -0,0 +1,28 @@ +import { strict as assert } from 'assert'; +import { TestRedisServers, itWithClient } from '../test-utils'; +import { transformArguments } from './SUNION'; + +describe('SUNION', () => { + describe('transformArguments', () => { + it('string', () => { + assert.deepEqual( + transformArguments('key'), + ['SUNION', 'key'] + ); + }); + + it('array', () => { + assert.deepEqual( + transformArguments(['1', '2']), + ['SUNION', '1', '2'] + ); + }); + }); + + itWithClient(TestRedisServers.OPEN, 'client.sUnion', async client => { + assert.deepEqual( + await client.sUnion('key'), + [] + ); + }); +}); diff --git a/lib/commands/SUNION.ts b/lib/commands/SUNION.ts new file mode 100644 index 00000000000..3f06138b1b6 --- /dev/null +++ b/lib/commands/SUNION.ts @@ -0,0 +1,11 @@ +import { pushVerdictArguments, transformReplyStringArray } from './generic-transformers'; + +export const FIRST_KEY_INDEX = 1; + +export const IS_READ_ONLY = true; + +export function transformArguments(keys: string | Array): Array { + return pushVerdictArguments(['SUNION'], keys); +} + +export const transformReply = transformReplyStringArray; diff --git a/lib/commands/SUNIONSTORE.spec.ts b/lib/commands/SUNIONSTORE.spec.ts new file mode 100644 index 00000000000..82c9a03a0b8 --- /dev/null +++ b/lib/commands/SUNIONSTORE.spec.ts @@ -0,0 +1,28 @@ +import { strict as assert } from 'assert'; +import { TestRedisServers, itWithClient } from '../test-utils'; +import { transformArguments } from './SUNIONSTORE'; + +describe('SUNIONSTORE', () => { + describe('transformArguments', () => { + it('string', () => { + assert.deepEqual( + transformArguments('destination', 'key'), + ['SUNIONSTORE', 'destination', 'key'] + ); + }); + + it('array', () => { + assert.deepEqual( + transformArguments('destination', ['1', '2']), + ['SUNIONSTORE', 'destination', '1', '2'] + ); + }); + }); + + itWithClient(TestRedisServers.OPEN, 'client.sUnionStore', async client => { + assert.equal( + await client.sUnionStore('destination', 'key'), + 0 + ); + }); +}); diff --git a/lib/commands/SUNIONSTORE.ts b/lib/commands/SUNIONSTORE.ts new file mode 100644 index 00000000000..7a1aab80117 --- /dev/null +++ b/lib/commands/SUNIONSTORE.ts @@ -0,0 +1,9 @@ +import { pushVerdictArguments, transformReplyNumber } from './generic-transformers'; + +export const FIRST_KEY_INDEX = 1; + +export function transformArguments(destination: string, keys: string | Array): Array { + return pushVerdictArguments(['SUNIONSTORE', destination], keys); +} + +export const transformReply = transformReplyNumber; diff --git a/lib/commands/SWAPDB.spec.ts b/lib/commands/SWAPDB.spec.ts new file mode 100644 index 00000000000..1a5637ae43d --- /dev/null +++ b/lib/commands/SWAPDB.spec.ts @@ -0,0 +1,19 @@ +import { strict as assert } from 'assert'; +import { itWithClient, TestRedisServers } from '../test-utils'; +import { transformArguments } from './SWAPDB'; + +describe('SWAPDB', () => { + it('transformArguments', () => { + assert.deepEqual( + transformArguments(0, 1), + ['SWAPDB', '0', '1'] + ); + }); + + itWithClient(TestRedisServers.OPEN, 'client.swapDb', async client => { + assert.equal( + await client.swapDb(0, 1), + 'OK' + ); + }); +}); diff --git a/lib/commands/SWAPDB.ts b/lib/commands/SWAPDB.ts new file mode 100644 index 00000000000..f0d4dacf3fc --- /dev/null +++ b/lib/commands/SWAPDB.ts @@ -0,0 +1,7 @@ +import { transformReplyString } from './generic-transformers'; + +export function transformArguments(index1: number, index2: number): Array { + return ['SWAPDB', index1.toString(), index2.toString()]; +} + +export const transformReply = transformReplyString; diff --git a/lib/commands/TIME.spec.ts b/lib/commands/TIME.spec.ts new file mode 100644 index 00000000000..1a07114af4b --- /dev/null +++ b/lib/commands/TIME.spec.ts @@ -0,0 +1,18 @@ +import { strict as assert } from 'assert'; +import { TestRedisServers, itWithClient } from '../test-utils'; +import { transformArguments } from './TIME'; + +describe('TIME', () => { + it('transformArguments', () => { + assert.deepEqual( + transformArguments(), + ['TIME'] + ); + }); + + itWithClient(TestRedisServers.OPEN, 'client.time', async client => { + const reply = await client.time(); + assert.ok(reply instanceof Date); + assert.ok(typeof reply.microseconds === 'number'); + }); +}); diff --git a/lib/commands/TIME.ts b/lib/commands/TIME.ts new file mode 100644 index 00000000000..4579845339e --- /dev/null +++ b/lib/commands/TIME.ts @@ -0,0 +1,15 @@ +export function transformArguments(): Array { + return ['TIME']; +} + +interface TimeReply extends Date { + microseconds: number; +} + +export function transformReply(reply: [string, string]): TimeReply { + const seconds = Number(reply[0]), + microseconds = Number(reply[1]), + d: Partial = new Date(seconds + Math.round(microseconds / 1000)); + d.microseconds = microseconds; + return d as TimeReply; +} diff --git a/lib/commands/TOUCH.spec.ts b/lib/commands/TOUCH.spec.ts new file mode 100644 index 00000000000..c4cb4356291 --- /dev/null +++ b/lib/commands/TOUCH.spec.ts @@ -0,0 +1,28 @@ +import { strict as assert } from 'assert'; +import { TestRedisServers, itWithClient } from '../test-utils'; +import { transformArguments } from './TOUCH'; + +describe('TOUCH', () => { + describe('transformArguments', () => { + it('string', () => { + assert.deepEqual( + transformArguments('key'), + ['TOUCH', 'key'] + ); + }); + + it('array', () => { + assert.deepEqual( + transformArguments(['1', '2']), + ['TOUCH', '1', '2'] + ); + }); + }); + + itWithClient(TestRedisServers.OPEN, 'client.touch', async client => { + assert.equal( + await client.touch('key'), + 0 + ); + }); +}); diff --git a/lib/commands/TOUCH.ts b/lib/commands/TOUCH.ts new file mode 100644 index 00000000000..f2fb0548970 --- /dev/null +++ b/lib/commands/TOUCH.ts @@ -0,0 +1,9 @@ +import { pushVerdictArguments, transformReplyNumber } from './generic-transformers'; + +export const FIRST_KEY_INDEX = 1; + +export function transformArguments(key: string | Array): Array { + return pushVerdictArguments(['TOUCH'], key); +} + +export const transformReply = transformReplyNumber; diff --git a/lib/commands/TTL.spec.ts b/lib/commands/TTL.spec.ts new file mode 100644 index 00000000000..bcabe8d39e5 --- /dev/null +++ b/lib/commands/TTL.spec.ts @@ -0,0 +1,19 @@ +import { strict as assert } from 'assert'; +import { TestRedisServers, itWithClient } from '../test-utils'; +import { transformArguments } from './TTL'; + +describe('TTL', () => { + it('transformArguments', () => { + assert.deepEqual( + transformArguments('key'), + ['TTL', 'key'] + ); + }); + + itWithClient(TestRedisServers.OPEN, 'client.ttl', async client => { + assert.equal( + await client.ttl('key'), + -2 + ); + }); +}); diff --git a/lib/commands/TTL.ts b/lib/commands/TTL.ts new file mode 100644 index 00000000000..aa8462dfea3 --- /dev/null +++ b/lib/commands/TTL.ts @@ -0,0 +1,11 @@ +import { transformReplyNumber } from './generic-transformers'; + +export const FIRST_KEY_INDEX = 1; + +export const IS_READ_ONLY = true; + +export function transformArguments(key: string): Array { + return ['TTL', key]; +} + +export const transformReply = transformReplyNumber; diff --git a/lib/commands/TYPE.spec.ts b/lib/commands/TYPE.spec.ts new file mode 100644 index 00000000000..d40f724242d --- /dev/null +++ b/lib/commands/TYPE.spec.ts @@ -0,0 +1,19 @@ +import { strict as assert } from 'assert'; +import { TestRedisServers, itWithClient } from '../test-utils'; +import { transformArguments } from './TYPE'; + +describe('TYPE', () => { + it('transformArguments', () => { + assert.deepEqual( + transformArguments('key'), + ['TYPE', 'key'] + ); + }); + + itWithClient(TestRedisServers.OPEN, 'client.type', async client => { + assert.equal( + await client.type('key'), + 'none' + ); + }); +}); diff --git a/lib/commands/TYPE.ts b/lib/commands/TYPE.ts new file mode 100644 index 00000000000..4f27b29d2b6 --- /dev/null +++ b/lib/commands/TYPE.ts @@ -0,0 +1,11 @@ +import { transformReplyString } from './generic-transformers'; + +export const FIRST_KEY_INDEX = 1; + +export const IS_READ_ONLY = true; + +export function transformArguments(key: string): Array { + return ['TYPE', key]; +} + +export const transformReply = transformReplyString; diff --git a/lib/commands/UNLINK.spec.ts b/lib/commands/UNLINK.spec.ts new file mode 100644 index 00000000000..a0dddf54f25 --- /dev/null +++ b/lib/commands/UNLINK.spec.ts @@ -0,0 +1,28 @@ +import { strict as assert } from 'assert'; +import { TestRedisServers, itWithClient } from '../test-utils'; +import { transformArguments } from './UNLINK'; + +describe('UNLINK', () => { + describe('transformArguments', () => { + it('string', () => { + assert.deepEqual( + transformArguments('key'), + ['UNLINK', 'key'] + ); + }); + + it('array', () => { + assert.deepEqual( + transformArguments(['1', '2']), + ['UNLINK', '1', '2'] + ); + }); + }); + + itWithClient(TestRedisServers.OPEN, 'client.unlink', async client => { + assert.equal( + await client.unlink('key'), + 0 + ); + }); +}); diff --git a/lib/commands/UNLINK.ts b/lib/commands/UNLINK.ts new file mode 100644 index 00000000000..9dfe0ca48ea --- /dev/null +++ b/lib/commands/UNLINK.ts @@ -0,0 +1,9 @@ +import { pushVerdictArguments, transformReplyNumber } from './generic-transformers'; + +export const FIRST_KEY_INDEX = 1; + +export function transformArguments(key: string | Array): Array { + return pushVerdictArguments(['UNLINK'], key); +} + +export const transformReply = transformReplyNumber; diff --git a/lib/commands/UNWATCH.spec.ts b/lib/commands/UNWATCH.spec.ts new file mode 100644 index 00000000000..238ffdc59b3 --- /dev/null +++ b/lib/commands/UNWATCH.spec.ts @@ -0,0 +1,26 @@ +import { strict as assert } from 'assert'; +import { TestRedisServers, itWithClient, TestRedisClusters, itWithCluster } from '../test-utils'; +import { transformArguments } from './UNWATCH'; + +describe('UNWATCH', () => { + it('transformArguments', () => { + assert.deepEqual( + transformArguments(), + ['UNWATCH'] + ); + }); + + itWithClient(TestRedisServers.OPEN, 'client.unwatch', async client => { + assert.equal( + await client.unwatch(), + 'OK' + ); + }); + + itWithCluster(TestRedisClusters.OPEN, 'cluster.unwatch', async cluster => { + assert.equal( + await cluster.unwatch(), + 'OK' + ); + }); +}); diff --git a/lib/commands/UNWATCH.ts b/lib/commands/UNWATCH.ts new file mode 100644 index 00000000000..d0ede556f3a --- /dev/null +++ b/lib/commands/UNWATCH.ts @@ -0,0 +1,7 @@ +import { transformReplyString } from './generic-transformers'; + +export function transformArguments(): Array { + return ['UNWATCH']; +} + +export const transformReply = transformReplyString; diff --git a/lib/commands/WAIT.spec.ts b/lib/commands/WAIT.spec.ts new file mode 100644 index 00000000000..c3f53b7db70 --- /dev/null +++ b/lib/commands/WAIT.spec.ts @@ -0,0 +1,19 @@ +import { strict as assert } from 'assert'; +import { TestRedisServers, itWithClient } from '../test-utils'; +import { transformArguments } from './WAIT'; + +describe('WAIT', () => { + it('transformArguments', () => { + assert.deepEqual( + transformArguments(0, 1), + ['WAIT', '0', '1'] + ); + }); + + itWithClient(TestRedisServers.OPEN, 'client.wait', async client => { + assert.equal( + await client.wait(0, 1), + 0 + ); + }); +}); diff --git a/lib/commands/WAIT.ts b/lib/commands/WAIT.ts new file mode 100644 index 00000000000..214fb356688 --- /dev/null +++ b/lib/commands/WAIT.ts @@ -0,0 +1,9 @@ +import { transformReplyNumber } from './generic-transformers'; + +export const FIRST_KEY_INDEX = 1; + +export function transformArguments(numberOfReplicas: number, timeout: number): Array { + return ['WAIT', numberOfReplicas.toString(), timeout.toString()]; +} + +export const transformReply = transformReplyNumber; diff --git a/lib/commands/WATCH.spec.ts b/lib/commands/WATCH.spec.ts new file mode 100644 index 00000000000..acaa062874f --- /dev/null +++ b/lib/commands/WATCH.spec.ts @@ -0,0 +1,20 @@ +import { strict as assert } from 'assert'; +import { transformArguments } from './WATCH'; + +describe('WATCH', () => { + describe('transformArguments', () => { + it('string', () => { + assert.deepEqual( + transformArguments('key'), + ['WATCH', 'key'] + ); + }); + + it('array', () => { + assert.deepEqual( + transformArguments(['1', '2']), + ['WATCH', '1', '2'] + ); + }); + }); +}); diff --git a/lib/commands/WATCH.ts b/lib/commands/WATCH.ts new file mode 100644 index 00000000000..5e24ca37952 --- /dev/null +++ b/lib/commands/WATCH.ts @@ -0,0 +1,7 @@ +import { pushVerdictArguments, transformReplyString } from './generic-transformers'; + +export function transformArguments(key: string | Array): Array { + return pushVerdictArguments(['WATCH'], key); +} + +export const transformReply = transformReplyString; diff --git a/lib/commands/XACK.spec.ts b/lib/commands/XACK.spec.ts new file mode 100644 index 00000000000..fb267c355eb --- /dev/null +++ b/lib/commands/XACK.spec.ts @@ -0,0 +1,28 @@ +import { strict as assert } from 'assert'; +import { TestRedisServers, itWithClient } from '../test-utils'; +import { transformArguments } from './XACK'; + +describe('XACK', () => { + describe('transformArguments', () => { + it('string', () => { + assert.deepEqual( + transformArguments('key', 'group', '1-0'), + ['XACK', 'key', 'group', '1-0'] + ); + }); + + it('array', () => { + assert.deepEqual( + transformArguments('key', 'group', ['1-0', '2-0']), + ['XACK', 'key', 'group', '1-0', '2-0'] + ); + }); + }); + + itWithClient(TestRedisServers.OPEN, 'client.xAck', async client => { + assert.equal( + await client.xAck('key', 'group', '1-0'), + 0 + ); + }); +}); diff --git a/lib/commands/XACK.ts b/lib/commands/XACK.ts new file mode 100644 index 00000000000..969f9b6a8b9 --- /dev/null +++ b/lib/commands/XACK.ts @@ -0,0 +1,9 @@ +import { pushVerdictArguments, transformReplyNumber } from './generic-transformers'; + +export const FIRST_KEY_INDEX = 1; + +export function transformArguments(key: string, group: string, id: string | Array): Array { + return pushVerdictArguments(['XACK', key, group], id); +} + +export const transformReply = transformReplyNumber; diff --git a/lib/commands/XADD.spec.ts b/lib/commands/XADD.spec.ts new file mode 100644 index 00000000000..02e6888051c --- /dev/null +++ b/lib/commands/XADD.spec.ts @@ -0,0 +1,118 @@ +import { strict as assert } from 'assert'; +import { TestRedisServers, itWithClient } from '../test-utils'; +import { transformArguments } from './XADD'; + +describe('XADD', () => { + describe('transformArguments', () => { + it('single field', () => { + assert.deepEqual( + transformArguments('key', '*', { + field: 'value' + }), + ['XADD', 'key', '*', 'field', 'value'] + ); + }); + + it('multiple fields', () => { + assert.deepEqual( + transformArguments('key', '*', { + '1': 'I', + '2': 'II' + }), + ['XADD', 'key', '*', '1', 'I', '2', 'II'] + ); + }); + + it('with NOMKSTREAM', () => { + assert.deepEqual( + transformArguments('key', '*', { + field: 'value' + }, { + NOMKSTREAM: true + }), + ['XADD', 'key', 'NOMKSTREAM', '*', 'field', 'value'] + ); + }); + + it('with TRIM', () => { + assert.deepEqual( + transformArguments('key', '*', { + field: 'value' + }, { + TRIM: { + threshold: 1000 + } + }), + ['XADD', 'key', '1000', '*', 'field', 'value'] + ); + }); + + it('with TRIM.strategy', () => { + assert.deepEqual( + transformArguments('key', '*', { + field: 'value' + }, { + TRIM: { + strategy: 'MAXLEN', + threshold: 1000 + } + }), + ['XADD', 'key', 'MAXLEN', '1000', '*','field', 'value'] + ); + }); + + it('with TRIM.strategyModifier', () => { + assert.deepEqual( + transformArguments('key', '*', { + field: 'value' + }, { + TRIM: { + strategyModifier: '=', + threshold: 1000 + } + }), + ['XADD', 'key', '=', '1000', '*', 'field', 'value'] + ); + }); + + it('with TRIM.limit', () => { + assert.deepEqual( + transformArguments('key', '*', { + field: 'value' + }, { + TRIM: { + threshold: 1000, + limit: 1 + } + }), + ['XADD', 'key', '1000', 'LIMIT', '1', '*', 'field', 'value'] + ); + }); + + it('with NOMKSTREAM, TRIM, TRIM.*', () => { + assert.deepEqual( + transformArguments('key', '*', { + field: 'value' + }, { + NOMKSTREAM: true, + TRIM: { + strategy: 'MAXLEN', + strategyModifier: '=', + threshold: 1000, + limit: 1 + } + }), + ['XADD', 'key', 'NOMKSTREAM', 'MAXLEN', '=', '1000', 'LIMIT', '1', '*', 'field', 'value'] + ); + }); + }); + + itWithClient(TestRedisServers.OPEN, 'client.xAdd', async client => { + assert.equal( + typeof await client.xAdd('key', '*', { + field: 'value' + }), + 'string' + ); + }); +}); diff --git a/lib/commands/XADD.ts b/lib/commands/XADD.ts new file mode 100644 index 00000000000..0500a2fde65 --- /dev/null +++ b/lib/commands/XADD.ts @@ -0,0 +1,48 @@ +import { TuplesObject, transformReplyString } from './generic-transformers'; + +export const FIRST_KEY_INDEX = 1; + + +interface XAddOptions { + NOMKSTREAM?: true; + TRIM?: { + strategy?: 'MAXLEN' | 'MINID'; + strategyModifier?: '=' | '~'; + threshold: number; + limit?: number; + } +} + +export function transformArguments(key: string, id: string, message: TuplesObject, options?: XAddOptions): Array { + const args = ['XADD', key]; + + if (options?.NOMKSTREAM) { + args.push('NOMKSTREAM'); + } + + if (options?.TRIM) { + if (options.TRIM.strategy) { + args.push(options.TRIM.strategy); + } + + if (options.TRIM.strategyModifier) { + args.push(options.TRIM.strategyModifier); + } + + args.push(options.TRIM.threshold.toString()); + + if (options.TRIM.limit) { + args.push('LIMIT', options.TRIM.limit.toString()); + } + } + + args.push(id); + + for (const [key, value] of Object.entries(message)) { + args.push(key, value); + } + + return args; +} + +export const transformReply = transformReplyString; diff --git a/lib/commands/XAUTOCLAIM.spec.ts b/lib/commands/XAUTOCLAIM.spec.ts new file mode 100644 index 00000000000..a0818d5c2c3 --- /dev/null +++ b/lib/commands/XAUTOCLAIM.spec.ts @@ -0,0 +1,42 @@ +import { strict as assert } from 'assert'; +import { TestRedisServers, itWithClient, describeHandleMinimumRedisVersion } from '../test-utils'; +import { transformArguments } from './XAUTOCLAIM'; + +describe('XAUTOCLAIM', () => { + describeHandleMinimumRedisVersion([6, 2]); + + describe('transformArguments', () => { + it('simple', () => { + assert.deepEqual( + transformArguments('key', 'group', 'consumer', 1, '0-0'), + ['XAUTOCLAIM', 'key', 'group', 'consumer', '1', '0-0'] + ); + }); + + it('with COUNT', () => { + assert.deepEqual( + transformArguments('key', 'group', 'consumer', 1, '0-0', { + COUNT: 1 + }), + ['XAUTOCLAIM', 'key', 'group', 'consumer', '1', '0-0', 'COUNT', '1'] + ); + }); + }); + + itWithClient(TestRedisServers.OPEN, 'client.xAutoClaim', async client => { + await Promise.all([ + client.xGroupCreate('key', 'group', '$', { + MKSTREAM: true + }), + client.xGroupCreateConsumer('key', 'group', 'consumer'), + ]); + + assert.deepEqual( + await client.xAutoClaim('key', 'group', 'consumer', 1, '0-0'), + { + nextId: '0-0', + messages: [] + } + ); + }); +}); diff --git a/lib/commands/XAUTOCLAIM.ts b/lib/commands/XAUTOCLAIM.ts new file mode 100644 index 00000000000..f02ccbaa2e2 --- /dev/null +++ b/lib/commands/XAUTOCLAIM.ts @@ -0,0 +1,36 @@ +import { StreamMessagesReply, transformReplyStreamMessages } from './generic-transformers'; + +export const FIRST_KEY_INDEX = 1; + +export interface XAutoClaimOptions { + COUNT?: number; +} + +export function transformArguments( + key: string, + group: string, + consumer: string, + minIdleTime: number, + start: string, + options?: XAutoClaimOptions +): Array { + const args = ['XAUTOCLAIM', key, group, consumer, minIdleTime.toString(), start]; + + if (options?.COUNT) { + args.push('COUNT', options.COUNT.toString()); + } + + return args; +} + +interface XAutoClaimReply { + nextId: string; + messages: StreamMessagesReply; +} + +export function transformReply(reply: [string, Array]): XAutoClaimReply { + return { + nextId: reply[0], + messages: transformReplyStreamMessages(reply[1]) + }; +} diff --git a/lib/commands/XAUTOCLAIM_JUSTID.spec.ts b/lib/commands/XAUTOCLAIM_JUSTID.spec.ts new file mode 100644 index 00000000000..d076f28751a --- /dev/null +++ b/lib/commands/XAUTOCLAIM_JUSTID.spec.ts @@ -0,0 +1,31 @@ +import { strict as assert } from 'assert'; +import { TestRedisServers, itWithClient, describeHandleMinimumRedisVersion } from '../test-utils'; +import { transformArguments } from './XAUTOCLAIM_JUSTID'; + +describe('XAUTOCLAIM JUSTID', () => { + describeHandleMinimumRedisVersion([6, 2]); + + it('transformArguments', () => { + assert.deepEqual( + transformArguments('key', 'group', 'consumer', 1, '0-0'), + ['XAUTOCLAIM', 'key', 'group', 'consumer', '1', '0-0', 'JUSTID'] + ); + }); + + itWithClient(TestRedisServers.OPEN, 'client.xAutoClaimJustId', async client => { + await Promise.all([ + client.xGroupCreate('key', 'group', '$', { + MKSTREAM: true + }), + client.xGroupCreateConsumer('key', 'group', 'consumer'), + ]); + + assert.deepEqual( + await client.xAutoClaimJustId('key', 'group', 'consumer', 1, '0-0'), + { + nextId: '0-0', + messages: [] + } + ); + }); +}); diff --git a/lib/commands/XAUTOCLAIM_JUSTID.ts b/lib/commands/XAUTOCLAIM_JUSTID.ts new file mode 100644 index 00000000000..70c1a07f920 --- /dev/null +++ b/lib/commands/XAUTOCLAIM_JUSTID.ts @@ -0,0 +1,22 @@ +import { transformArguments as transformXAutoClaimArguments } from './XAUTOCLAIM'; + +export { FIRST_KEY_INDEX } from './XAUTOCLAIM'; + +export function transformArguments(...args: Parameters): Array { + return [ + ...transformXAutoClaimArguments(...args), + 'JUSTID' + ]; +} + +interface XAutoClaimJustIdReply { + nextId: string; + messages: Array; +} + +export function transformReply(reply: [string, Array]): XAutoClaimJustIdReply { + return { + nextId: reply[0], + messages: reply[1] + }; +} diff --git a/lib/commands/XCLAIM.spec.ts b/lib/commands/XCLAIM.spec.ts new file mode 100644 index 00000000000..ff4b445dcf3 --- /dev/null +++ b/lib/commands/XCLAIM.spec.ts @@ -0,0 +1,90 @@ +import { strict as assert } from 'assert'; +import { TestRedisServers, itWithClient } from '../test-utils'; +import { transformArguments } from './XCLAIM'; + +describe('XCLAIM', () => { + describe('transformArguments', () => { + it('single id (string)', () => { + assert.deepEqual( + transformArguments('key', 'group', 'consumer', 1, '0-0'), + ['XCLAIM', 'key', 'group', 'consumer', '1', '0-0'] + ); + }); + + it('multiple ids (array)', () => { + assert.deepEqual( + transformArguments('key', 'group', 'consumer', 1, ['0-0', '1-0']), + ['XCLAIM', 'key', 'group', 'consumer', '1', '0-0', '1-0'] + ); + }); + + it('with IDLE', () => { + assert.deepEqual( + transformArguments('key', 'group', 'consumer', 1, '0-0', { + IDLE: 1 + }), + ['XCLAIM', 'key', 'group', 'consumer', '1', '0-0', 'IDLE', '1'] + ); + }); + + it('with TIME (number)', () => { + assert.deepEqual( + transformArguments('key', 'group', 'consumer', 1, '0-0', { + TIME: 1 + }), + ['XCLAIM', 'key', 'group', 'consumer', '1', '0-0', 'TIME', '1'] + ); + }); + + it('with TIME (date)', () => { + const d = new Date(); + assert.deepEqual( + transformArguments('key', 'group', 'consumer', 1, '0-0', { + TIME: d + }), + ['XCLAIM', 'key', 'group', 'consumer', '1', '0-0', 'TIME', d.getTime().toString()] + ); + }); + + it('with RETRYCOUNT', () => { + assert.deepEqual( + transformArguments('key', 'group', 'consumer', 1, '0-0', { + RETRYCOUNT: 1 + }), + ['XCLAIM', 'key', 'group', 'consumer', '1', '0-0', 'RETRYCOUNT', '1'] + ); + }); + + it('with FORCE', () => { + assert.deepEqual( + transformArguments('key', 'group', 'consumer', 1, '0-0', { + FORCE: true + }), + ['XCLAIM', 'key', 'group', 'consumer', '1', '0-0', 'FORCE'] + ); + }); + + it('with IDLE, TIME, RETRYCOUNT, FORCE, JUSTID', () => { + assert.deepEqual( + transformArguments('key', 'group', 'consumer', 1, '0-0', { + IDLE: 1, + TIME: 1, + RETRYCOUNT: 1, + FORCE: true + }), + ['XCLAIM', 'key', 'group', 'consumer', '1', '0-0', 'IDLE', '1', 'TIME', '1', 'RETRYCOUNT', '1', 'FORCE'] + ); + }); + }); + + itWithClient(TestRedisServers.OPEN, 'client.xClaim', async client => { + await client.xGroupCreate('key', 'group', '$', { + MKSTREAM: true + }); + + assert.deepEqual( + await client.xClaim('key', 'group', 'consumer', 1, '0-0'), + [] + ); + }); +}); diff --git a/lib/commands/XCLAIM.ts b/lib/commands/XCLAIM.ts new file mode 100644 index 00000000000..c5890a75797 --- /dev/null +++ b/lib/commands/XCLAIM.ts @@ -0,0 +1,46 @@ +import { pushVerdictArguments, transformReplyStreamMessages } from './generic-transformers'; + +export const FIRST_KEY_INDEX = 1; + +export interface XClaimOptions { + IDLE?: number; + TIME?: number | Date; + RETRYCOUNT?: number; + FORCE?: true; +} + +export function transformArguments( + key: string, + group: string, + consumer: string, + minIdleTime: number, + id: string | Array, + options?: XClaimOptions +): Array { + const args = ['XCLAIM', key, group, consumer, minIdleTime.toString()]; + + pushVerdictArguments(args, id); + + if (options?.IDLE) { + args.push('IDLE', options.IDLE.toString()); + } + + if (options?.TIME) { + args.push( + 'TIME', + (typeof options.TIME === 'number' ? options.TIME : options.TIME.getTime()).toString() + ); + } + + if (options?.RETRYCOUNT) { + args.push('RETRYCOUNT', options.RETRYCOUNT.toString()); + } + + if (options?.FORCE) { + args.push('FORCE'); + } + + return args; +} + +export const transformReply = transformReplyStreamMessages; diff --git a/lib/commands/XCLAIM_JUSTID.spec.ts b/lib/commands/XCLAIM_JUSTID.spec.ts new file mode 100644 index 00000000000..bb31f2c4532 --- /dev/null +++ b/lib/commands/XCLAIM_JUSTID.spec.ts @@ -0,0 +1,23 @@ +import { strict as assert } from 'assert'; +import { TestRedisServers, itWithClient } from '../test-utils'; +import { transformArguments } from './XCLAIM_JUSTID'; + +describe('XCLAIM JUSTID', () => { + it('transformArguments', () => { + assert.deepEqual( + transformArguments('key', 'group', 'consumer', 1, '0-0'), + ['XCLAIM', 'key', 'group', 'consumer', '1', '0-0', 'JUSTID'] + ); + }); + + itWithClient(TestRedisServers.OPEN, 'client.xClaimJustId', async client => { + await client.xGroupCreate('key', 'group', '$', { + MKSTREAM: true + }); + + assert.deepEqual( + await client.xClaimJustId('key', 'group', 'consumer', 1, '0-0'), + [] + ); + }); +}); diff --git a/lib/commands/XCLAIM_JUSTID.ts b/lib/commands/XCLAIM_JUSTID.ts new file mode 100644 index 00000000000..dcf274ed821 --- /dev/null +++ b/lib/commands/XCLAIM_JUSTID.ts @@ -0,0 +1,13 @@ +import { transformReplyStringArray } from './generic-transformers'; +import { transformArguments as transformArgumentsXClaim } from './XCLAIM'; + +export { FIRST_KEY_INDEX } from './XCLAIM'; + +export function transformArguments(...args: Parameters): Array { + return [ + ...transformArgumentsXClaim(...args), + 'JUSTID' + ]; +} + +export const transformReply = transformReplyStringArray; diff --git a/lib/commands/XDEL.spec.ts b/lib/commands/XDEL.spec.ts new file mode 100644 index 00000000000..1a3015538f4 --- /dev/null +++ b/lib/commands/XDEL.spec.ts @@ -0,0 +1,28 @@ +import { strict as assert } from 'assert'; +import { TestRedisServers, itWithClient } from '../test-utils'; +import { transformArguments } from './XDEL'; + +describe('XDEL', () => { + describe('transformArguments', () => { + it('string', () => { + assert.deepEqual( + transformArguments('key', '0-0'), + ['XDEL', 'key', '0-0'] + ); + }); + + it('array', () => { + assert.deepEqual( + transformArguments('key', ['0-0', '1-0']), + ['XDEL', 'key', '0-0', '1-0'] + ); + }); + }); + + itWithClient(TestRedisServers.OPEN, 'client.xDel', async client => { + assert.equal( + await client.xDel('key', '0-0'), + 0 + ); + }); +}); diff --git a/lib/commands/XDEL.ts b/lib/commands/XDEL.ts new file mode 100644 index 00000000000..9d173271c28 --- /dev/null +++ b/lib/commands/XDEL.ts @@ -0,0 +1,9 @@ +import { pushVerdictArguments, transformReplyNumber } from './generic-transformers'; + +export const FIRST_KEY_INDEX = 1; + +export function transformArguments(key: string, id: string | Array): Array { + return pushVerdictArguments(['XDEL', key], id); +} + +export const transformReply = transformReplyNumber; diff --git a/lib/commands/XGROUP_CREATE.spec.ts b/lib/commands/XGROUP_CREATE.spec.ts new file mode 100644 index 00000000000..fdbb796f107 --- /dev/null +++ b/lib/commands/XGROUP_CREATE.spec.ts @@ -0,0 +1,32 @@ +import { strict as assert } from 'assert'; +import { TestRedisServers, itWithClient } from '../test-utils'; +import { transformArguments } from './XGROUP_CREATE'; + +describe('XGROUP CREATE', () => { + describe('transformArguments', () => { + it('simple', () => { + assert.deepEqual( + transformArguments('key', 'group', '$'), + ['XGROUP', 'CREATE', 'key', 'group', '$'] + ); + }); + + it('with MKSTREAM', () => { + assert.deepEqual( + transformArguments('key', 'group', '$', { + MKSTREAM: true + }), + ['XGROUP', 'CREATE', 'key', 'group', '$', 'MKSTREAM'] + ); + }); + }); + + itWithClient(TestRedisServers.OPEN, 'client.xGroupCreate', async client => { + assert.equal( + await client.xGroupCreate('key', 'group', '$', { + MKSTREAM: true + }), + 'OK' + ); + }); +}); diff --git a/lib/commands/XGROUP_CREATE.ts b/lib/commands/XGROUP_CREATE.ts new file mode 100644 index 00000000000..167197b263c --- /dev/null +++ b/lib/commands/XGROUP_CREATE.ts @@ -0,0 +1,19 @@ +import { transformReplyString } from './generic-transformers'; + +export const FIRST_KEY_INDEX = 2; + +interface XGroupCreateOptions { + MKSTREAM?: true; +} + +export function transformArguments(key: string, group: string, id: string, options?: XGroupCreateOptions): Array { + const args = ['XGROUP', 'CREATE', key, group, id]; + + if (options?.MKSTREAM) { + args.push('MKSTREAM'); + } + + return args; +} + +export const transformReply = transformReplyString; diff --git a/lib/commands/XGROUP_CREATECONSUMER.spec.ts b/lib/commands/XGROUP_CREATECONSUMER.spec.ts new file mode 100644 index 00000000000..5b06188e302 --- /dev/null +++ b/lib/commands/XGROUP_CREATECONSUMER.spec.ts @@ -0,0 +1,25 @@ +import { strict as assert } from 'assert'; +import { TestRedisServers, itWithClient, describeHandleMinimumRedisVersion } from '../test-utils'; +import { transformArguments } from './XGROUP_CREATECONSUMER'; + +describe('XGROUP CREATECONSUMER', () => { + describeHandleMinimumRedisVersion([6, 2]); + + it('transformArguments', () => { + assert.deepEqual( + transformArguments('key', 'group', 'consumer'), + ['XGROUP', 'CREATECONSUMER', 'key', 'group', 'consumer'] + ); + }); + + itWithClient(TestRedisServers.OPEN, 'client.xGroupCreateConsumer', async client => { + await client.xGroupCreate('key', 'group', '$', { + MKSTREAM: true + }); + + assert.equal( + await client.xGroupCreateConsumer('key', 'group', 'consumer'), + true + ); + }); +}); diff --git a/lib/commands/XGROUP_CREATECONSUMER.ts b/lib/commands/XGROUP_CREATECONSUMER.ts new file mode 100644 index 00000000000..395688706e2 --- /dev/null +++ b/lib/commands/XGROUP_CREATECONSUMER.ts @@ -0,0 +1,9 @@ +import { transformReplyBoolean } from './generic-transformers'; + +export const FIRST_KEY_INDEX = 2; + +export function transformArguments(key: string, group: string, consumer: string): Array { + return ['XGROUP', 'CREATECONSUMER', key, group, consumer]; +} + +export const transformReply = transformReplyBoolean; diff --git a/lib/commands/XGROUP_DELCONSUMER.spec.ts b/lib/commands/XGROUP_DELCONSUMER.spec.ts new file mode 100644 index 00000000000..c3cf3c2378a --- /dev/null +++ b/lib/commands/XGROUP_DELCONSUMER.spec.ts @@ -0,0 +1,23 @@ +import { strict as assert } from 'assert'; +import { TestRedisServers, itWithClient } from '../test-utils'; +import { transformArguments } from './XGROUP_DELCONSUMER'; + +describe('XGROUP DELCONSUMER', () => { + it('transformArguments', () => { + assert.deepEqual( + transformArguments('key', 'group', 'consumer'), + ['XGROUP', 'DELCONSUMER', 'key', 'group', 'consumer'] + ); + }); + + itWithClient(TestRedisServers.OPEN, 'client.xGroupDelConsumer', async client => { + await client.xGroupCreate('key', 'group', '$', { + MKSTREAM: true + }); + + assert.equal( + await client.xGroupDelConsumer('key', 'group', 'consumer'), + 0 + ); + }); +}); diff --git a/lib/commands/XGROUP_DELCONSUMER.ts b/lib/commands/XGROUP_DELCONSUMER.ts new file mode 100644 index 00000000000..91a36f91a30 --- /dev/null +++ b/lib/commands/XGROUP_DELCONSUMER.ts @@ -0,0 +1,9 @@ +import { transformReplyNumber } from './generic-transformers'; + +export const FIRST_KEY_INDEX = 2; + +export function transformArguments(key: string, group: string, consumer: string): Array { + return ['XGROUP', 'DELCONSUMER', key, group, consumer]; +} + +export const transformReply = transformReplyNumber; diff --git a/lib/commands/XGROUP_DESTROY.spec.ts b/lib/commands/XGROUP_DESTROY.spec.ts new file mode 100644 index 00000000000..e991bc0d667 --- /dev/null +++ b/lib/commands/XGROUP_DESTROY.spec.ts @@ -0,0 +1,23 @@ +import { strict as assert } from 'assert'; +import { TestRedisServers, itWithClient } from '../test-utils'; +import { transformArguments } from './XGROUP_DESTROY'; + +describe('XGROUP DESTROY', () => { + it('transformArguments', () => { + assert.deepEqual( + transformArguments('key', 'group'), + ['XGROUP', 'DESTROY', 'key', 'group'] + ); + }); + + itWithClient(TestRedisServers.OPEN, 'client.xGroupDestroy', async client => { + await client.xGroupCreate('key', 'group', '$', { + MKSTREAM: true + }); + + assert.equal( + await client.xGroupDestroy('key', 'group'), + true + ); + }); +}); diff --git a/lib/commands/XGROUP_DESTROY.ts b/lib/commands/XGROUP_DESTROY.ts new file mode 100644 index 00000000000..1fd25550c33 --- /dev/null +++ b/lib/commands/XGROUP_DESTROY.ts @@ -0,0 +1,9 @@ +import { transformReplyBoolean } from './generic-transformers'; + +export const FIRST_KEY_INDEX = 2; + +export function transformArguments(key: string, group: string): Array { + return ['XGROUP', 'DESTROY', key, group]; +} + +export const transformReply = transformReplyBoolean; diff --git a/lib/commands/XGROUP_SETID.spec.ts b/lib/commands/XGROUP_SETID.spec.ts new file mode 100644 index 00000000000..0fa10cdb0b7 --- /dev/null +++ b/lib/commands/XGROUP_SETID.spec.ts @@ -0,0 +1,23 @@ +import { strict as assert } from 'assert'; +import { TestRedisServers, itWithClient } from '../test-utils'; +import { transformArguments } from './XGROUP_SETID'; + +describe('XGROUP SETID', () => { + it('transformArguments', () => { + assert.deepEqual( + transformArguments('key', 'group', '0'), + ['XGROUP', 'SETID', 'key', 'group', '0'] + ); + }); + + itWithClient(TestRedisServers.OPEN, 'client.xGroupSetId', async client => { + await client.xGroupCreate('key', 'group', '$', { + MKSTREAM: true + }); + + assert.equal( + await client.xGroupSetId('key', 'group', '0'), + 'OK' + ); + }); +}); diff --git a/lib/commands/XGROUP_SETID.ts b/lib/commands/XGROUP_SETID.ts new file mode 100644 index 00000000000..ce4a13086d3 --- /dev/null +++ b/lib/commands/XGROUP_SETID.ts @@ -0,0 +1,9 @@ +import { transformReplyString } from './generic-transformers'; + +export const FIRST_KEY_INDEX = 2; + +export function transformArguments(key: string, group: string, id: string): Array { + return ['XGROUP', 'SETID', key, group, id]; +} + +export const transformReply = transformReplyString; diff --git a/lib/commands/XINFO_CONSUMERS.spec.ts b/lib/commands/XINFO_CONSUMERS.spec.ts new file mode 100644 index 00000000000..08ef17e51aa --- /dev/null +++ b/lib/commands/XINFO_CONSUMERS.spec.ts @@ -0,0 +1,41 @@ +import { strict as assert } from 'assert'; +import { TestRedisServers, itWithClient } from '../test-utils'; +import { transformArguments, transformReply } from './XINFO_CONSUMERS'; + +describe('XINFO CONSUMERS', () => { + it('transformArguments', () => { + assert.deepEqual( + transformArguments('key', 'group'), + ['XINFO', 'CONSUMERS', 'key', 'group'] + ); + }); + + it('transformReply', () => { + assert.deepEqual( + transformReply([ + ['name', 'Alice', 'pending', 1, 'idle', 9104628], + ['name', 'Bob', 'pending', 1, 'idle', 83841983] + ]), + [{ + name: 'Alice', + pending: 1, + idle: 9104628 + }, { + name: 'Bob', + pending: 1, + idle: 83841983 + }] + ); + }) + + itWithClient(TestRedisServers.OPEN, 'client.xInfoConsumers', async client => { + await client.xGroupCreate('key', 'group', '$', { + MKSTREAM: true + }); + + assert.deepEqual( + await client.xInfoConsumers('key', 'group'), + [] + ); + }); +}); diff --git a/lib/commands/XINFO_CONSUMERS.ts b/lib/commands/XINFO_CONSUMERS.ts new file mode 100644 index 00000000000..57e60db832d --- /dev/null +++ b/lib/commands/XINFO_CONSUMERS.ts @@ -0,0 +1,21 @@ +export const FIRST_KEY_INDEX = 2; + +export const IS_READ_ONLY = true; + +export function transformArguments(key: string, group: string): Array { + return ['XINFO', 'CONSUMERS', key, group]; +} + +type XInfoConsumersReply = Array<{ + name: string; + pending: number; + idle: number; +}>; + +export function transformReply(rawReply: Array): XInfoConsumersReply { + return rawReply.map(consumer => ({ + name: consumer[1], + pending: consumer[3], + idle: consumer[5] + })); +} diff --git a/lib/commands/XINFO_GROUPS.spec.ts b/lib/commands/XINFO_GROUPS.spec.ts new file mode 100644 index 00000000000..8fbd86ee3ee --- /dev/null +++ b/lib/commands/XINFO_GROUPS.spec.ts @@ -0,0 +1,48 @@ +import { strict as assert } from 'assert'; +import { TestRedisServers, itWithClient } from '../test-utils'; +import { transformArguments, transformReply } from './XINFO_GROUPS'; + +describe('XINFO GROUPS', () => { + it('transformArguments', () => { + assert.deepEqual( + transformArguments('key'), + ['XINFO', 'GROUPS', 'key'] + ); + }); + + it('transformReply', () => { + assert.deepEqual( + transformReply([ + ['name', 'mygroup', 'consumers', 2, 'pending', 2, 'last-delivered-id', '1588152489012-0'], + ['name', 'some-other-group', 'consumers', 1, 'pending', 0, 'last-delivered-id', '1588152498034-0'] + ]), + [{ + name: 'mygroup', + consumers: 2, + pending: 2, + lastDeliveredId: '1588152489012-0' + }, { + name: 'some-other-group', + consumers: 1, + pending: 0, + lastDeliveredId: '1588152498034-0' + }] + ); + }) + + itWithClient(TestRedisServers.OPEN, 'client.xInfoGroups', async client => { + await client.xGroupCreate('key', 'group', '$', { + MKSTREAM: true + }); + + assert.deepEqual( + await client.xInfoGroups('key'), + [{ + name: 'group', + consumers: 0, + pending: 0, + lastDeliveredId: '0-0' + }] + ); + }); +}); diff --git a/lib/commands/XINFO_GROUPS.ts b/lib/commands/XINFO_GROUPS.ts new file mode 100644 index 00000000000..04fb9ca39a6 --- /dev/null +++ b/lib/commands/XINFO_GROUPS.ts @@ -0,0 +1,23 @@ +export const FIRST_KEY_INDEX = 2; + +export const IS_READ_ONLY = true; + +export function transformArguments(key: string): Array { + return ['XINFO', 'GROUPS', key]; +} + +type XInfoGroupsReply = Array<{ + name: string; + consumers: number; + pending: number; + lastDeliveredId: string; +}>; + +export function transformReply(rawReply: Array): XInfoGroupsReply { + return rawReply.map(group => ({ + name: group[1], + consumers: group[3], + pending: group[5], + lastDeliveredId: group[7] + })); +} diff --git a/lib/commands/XINFO_STREAM.spec.ts b/lib/commands/XINFO_STREAM.spec.ts new file mode 100644 index 00000000000..ecab605e4e3 --- /dev/null +++ b/lib/commands/XINFO_STREAM.spec.ts @@ -0,0 +1,72 @@ +import { strict as assert } from 'assert'; +import { TestRedisServers, itWithClient } from '../test-utils'; +import { transformArguments, transformReply } from './XINFO_STREAM'; + +describe('XINFO STREAM', () => { + it('transformArguments', () => { + assert.deepEqual( + transformArguments('key'), + ['XINFO', 'STREAM', 'key'] + ); + }); + + it('transformReply', () => { + assert.deepEqual( + transformReply([ + 'length', 2, + 'radix-tree-keys', 1, + 'radix-tree-nodes', 2, + 'last-generated-id', '1538385846314-0', + 'groups', 2, + 'first-entry', ['1538385820729-0', ['foo', 'bar']], + 'last-entry', ['1538385846314-0', ['field', 'value']] + ]), + { + length: 2, + radixTreeKeys: 1, + radixTreeNodes: 2, + groups: 2, + lastGeneratedId: '1538385846314-0', + firstEntry: { + id: '1538385820729-0', + message: Object.create(null, { + foo: { + value: 'bar', + configurable: true, + enumerable: true + } + }) + }, + lastEntry: { + id: '1538385846314-0', + message: Object.create(null, { + field: { + value: 'value', + configurable: true, + enumerable: true + } + }) + } + } + ); + }); + + itWithClient(TestRedisServers.OPEN, 'client.xInfoStream', async client => { + await client.xGroupCreate('key', 'group', '$', { + MKSTREAM: true + }); + + assert.deepEqual( + await client.xInfoStream('key'), + { + length: 0, + radixTreeKeys: 0, + radixTreeNodes: 1, + groups: 1, + lastGeneratedId: '0-0', + firstEntry: null, + lastEntry: null + } + ); + }); +}); diff --git a/lib/commands/XINFO_STREAM.ts b/lib/commands/XINFO_STREAM.ts new file mode 100644 index 00000000000..0bb44721873 --- /dev/null +++ b/lib/commands/XINFO_STREAM.ts @@ -0,0 +1,63 @@ +import { StreamMessageReply, transformReplyTuples } from './generic-transformers'; + +export const FIRST_KEY_INDEX = 2; + +export const IS_READ_ONLY = true; + +export function transformArguments(key: string): Array { + return ['XINFO', 'STREAM', key]; +} + +interface XInfoStreamReply { + length: number; + radixTreeKeys: number; + radixTreeNodes: number; + groups: number; + lastGeneratedId: string; + firstEntry: StreamMessageReply | null; + lastEntry: StreamMessageReply | null; +} + +export function transformReply(rawReply: Array): XInfoStreamReply { + const parsedReply: Partial = {}; + + for (let i = 0; i < rawReply.length; i+= 2) { + switch (rawReply[i]) { + case 'length': + parsedReply.length = rawReply[i + 1]; + break; + + case 'radix-tree-keys': + parsedReply.radixTreeKeys = rawReply[i + 1]; + break; + + case 'radix-tree-nodes': + parsedReply.radixTreeNodes = rawReply[i + 1]; + break; + + case 'groups': + parsedReply.groups = rawReply[i + 1]; + break; + + case 'last-generated-id': + parsedReply.lastGeneratedId = rawReply[i + 1]; + break; + + case 'first-entry': + parsedReply.firstEntry = rawReply[i + 1] ? { + id: rawReply[i + 1][0], + message: transformReplyTuples(rawReply[i + 1][1]) + } : null; + break; + + case 'last-entry': + parsedReply.lastEntry = rawReply[i + 1] ? { + id: rawReply[i + 1][0], + message: transformReplyTuples(rawReply[i + 1][1]) + } : null; + break; + } + } + + return parsedReply as XInfoStreamReply; +} diff --git a/lib/commands/XLEN.spec.ts b/lib/commands/XLEN.spec.ts new file mode 100644 index 00000000000..c4f62dbc4f2 --- /dev/null +++ b/lib/commands/XLEN.spec.ts @@ -0,0 +1,19 @@ +import { strict as assert } from 'assert'; +import { TestRedisServers, itWithClient } from '../test-utils'; +import { transformArguments } from './XLEN'; + +describe('XLEN', () => { + it('transformArguments', () => { + assert.deepEqual( + transformArguments('key'), + ['XLEN', 'key'] + ); + }); + + itWithClient(TestRedisServers.OPEN, 'client.xLen', async client => { + assert.equal( + await client.xLen('key'), + 0 + ); + }); +}); diff --git a/lib/commands/XLEN.ts b/lib/commands/XLEN.ts new file mode 100644 index 00000000000..d7ba033e612 --- /dev/null +++ b/lib/commands/XLEN.ts @@ -0,0 +1,11 @@ +import { transformReplyNumber } from './generic-transformers'; + +export const FIRST_KEY_INDEX = 1; + +export const IS_READ_ONLY = true; + +export function transformArguments(key: string): Array { + return ['XLEN', key]; +} + +export const transformReply = transformReplyNumber; diff --git a/lib/commands/XPENDING.spec.ts b/lib/commands/XPENDING.spec.ts new file mode 100644 index 00000000000..31ffeeb4230 --- /dev/null +++ b/lib/commands/XPENDING.spec.ts @@ -0,0 +1,30 @@ +import { strict as assert } from 'assert'; +import { TestRedisServers, itWithClient } from '../test-utils'; +import { transformArguments } from './XPENDING'; + +describe('XPENDING', () => { + describe('transformArguments', () => { + it('transformArguments', () => { + assert.deepEqual( + transformArguments('key', 'group'), + ['XPENDING', 'key', 'group'] + ); + }); + }); + + itWithClient(TestRedisServers.OPEN, 'client.xPending', async client => { + await client.xGroupCreate('key', 'group', '$', { + MKSTREAM: true + }); + + assert.deepEqual( + await client.xPending('key', 'group'), + { + pending: 0, + firstId: null, + lastId: null, + consumers: null + } + ); + }); +}); diff --git a/lib/commands/XPENDING.ts b/lib/commands/XPENDING.ts new file mode 100644 index 00000000000..6695ab6614c --- /dev/null +++ b/lib/commands/XPENDING.ts @@ -0,0 +1,23 @@ +export const FIRST_KEY_INDEX = 1; + +export const IS_READ_ONLY = true; + +export function transformArguments(key: string, group: string): Array { + return ['XPENDING', key, group]; +} + +interface XPendingReply { + pending: number; + firstId: string | null; + lastId: number | null + consumers: Array | null; +} + +export function transformReply(reply: [number, string | null, number | null, Array | null]): XPendingReply { + return { + pending: reply[0], + firstId: reply[1], + lastId: reply[2], + consumers: reply[3] + }; +} diff --git a/lib/commands/XPENDING_RANGE.spec.ts b/lib/commands/XPENDING_RANGE.spec.ts new file mode 100644 index 00000000000..76a582d3db5 --- /dev/null +++ b/lib/commands/XPENDING_RANGE.spec.ts @@ -0,0 +1,53 @@ +import { strict as assert } from 'assert'; +import { TestRedisServers, itWithClient } from '../test-utils'; +import { transformArguments } from './XPENDING_RANGE'; + +describe('XPENDING RANGE', () => { + describe('transformArguments', () => { + it('simple', () => { + assert.deepEqual( + transformArguments('key', 'group', '-', '+', 1), + ['XPENDING', 'key', 'group', '-', '+', '1'] + ); + }); + + it('with IDLE', () => { + assert.deepEqual( + transformArguments('key', 'group', '-', '+', 1, { + IDLE: 1, + }), + ['XPENDING', 'key', 'group', 'IDLE', '1', '-', '+', '1'] + ); + }); + + it('with consumer', () => { + assert.deepEqual( + transformArguments('key', 'group', '-', '+', 1, { + consumer: 'consumer' + }), + ['XPENDING', 'key', 'group', '-', '+', '1', 'consumer'] + ); + }); + + it('with IDLE, consumer', () => { + assert.deepEqual( + transformArguments('key', 'group', '-', '+', 1, { + IDLE: 1, + consumer: 'consumer' + }), + ['XPENDING', 'key', 'group', 'IDLE', '1', '-', '+', '1', 'consumer'] + ); + }); + }); + + itWithClient(TestRedisServers.OPEN, 'client.xPendingRange', async client => { + await client.xGroupCreate('key', 'group', '$', { + MKSTREAM: true + }); + + assert.deepEqual( + await client.xPendingRange('key', 'group', '-', '+', 1), + [] + ); + }); +}); diff --git a/lib/commands/XPENDING_RANGE.ts b/lib/commands/XPENDING_RANGE.ts new file mode 100644 index 00000000000..c9e8d898e86 --- /dev/null +++ b/lib/commands/XPENDING_RANGE.ts @@ -0,0 +1,35 @@ +import { transformReplyStreamMessages } from './generic-transformers'; + +export const FIRST_KEY_INDEX = 1; + +export const IS_READ_ONLY = true; + +interface XPendingRangeOptions { + IDLE?: number; + consumer?: string; +} + +export function transformArguments( + key: string, + group: string, + start: string, + end: string, + count: number, + options?: XPendingRangeOptions +): Array { + const args = ['XPENDING', key, group]; + + if (options?.IDLE) { + args.push('IDLE', options.IDLE.toString()); + } + + args.push(start, end, count.toString()); + + if (options?.consumer) { + args.push(options.consumer); + } + + return args; +} + +export const transformReply = transformReplyStreamMessages; diff --git a/lib/commands/XRANGE.spec.ts b/lib/commands/XRANGE.spec.ts new file mode 100644 index 00000000000..55efa9d7729 --- /dev/null +++ b/lib/commands/XRANGE.spec.ts @@ -0,0 +1,30 @@ +import { strict as assert } from 'assert'; +import { TestRedisServers, itWithClient } from '../test-utils'; +import { transformArguments } from './XRANGE'; + +describe('XRANGE', () => { + describe('transformArguments', () => { + it('simple', () => { + assert.deepEqual( + transformArguments('key', '-', '+'), + ['XRANGE', 'key', '-', '+'] + ); + }); + + it('with COUNT', () => { + assert.deepEqual( + transformArguments('key', '-', '+', { + COUNT: 1 + }), + ['XRANGE', 'key', '-', '+', 'COUNT', '1'] + ); + }); + }); + + itWithClient(TestRedisServers.OPEN, 'client.xRange', async client => { + assert.deepEqual( + await client.xRange('key', '+', '-'), + [] + ); + }); +}); diff --git a/lib/commands/XRANGE.ts b/lib/commands/XRANGE.ts new file mode 100644 index 00000000000..2902d0743db --- /dev/null +++ b/lib/commands/XRANGE.ts @@ -0,0 +1,21 @@ +import { transformReplyStreamMessages } from './generic-transformers'; + +export const FIRST_KEY_INDEX = 1; + +export const IS_READ_ONLY = true; + +interface XRangeOptions { + COUNT?: number; +} + +export function transformArguments(key: string, start: string, end: string, options?: XRangeOptions): Array { + const args = ['XRANGE', key, start, end]; + + if (options?.COUNT) { + args.push('COUNT', options.COUNT.toString()); + } + + return args; +} + +export const transformReply = transformReplyStreamMessages; diff --git a/lib/commands/XREAD.spec.ts b/lib/commands/XREAD.spec.ts new file mode 100644 index 00000000000..e7bf127a90a --- /dev/null +++ b/lib/commands/XREAD.spec.ts @@ -0,0 +1,87 @@ +import { strict as assert } from 'assert'; +import { TestRedisServers, itWithClient, itWithCluster, TestRedisClusters } from '../test-utils'; +import { transformArguments } from './XREAD'; + +describe('XREAD', () => { + describe('transformArguments', () => { + it('single stream', () => { + assert.deepEqual( + transformArguments({ + key: 'key', + id: '0' + }), + ['XREAD', 'STREAMS', 'key', '0'] + ); + }); + + it('multiple streams', () => { + assert.deepEqual( + transformArguments([{ + key: '1', + id: '0' + }, { + key: '2', + id: '0' + }]), + ['XREAD', 'STREAMS', '1', '2', '0', '0'] + ); + }); + + it('with COUNT', () => { + assert.deepEqual( + transformArguments({ + key: 'key', + id: '0' + }, { + COUNT: 1 + }), + ['XREAD', 'COUNT', '1', 'STREAMS', 'key', '0'] + ); + }); + + it('with BLOCK', () => { + assert.deepEqual( + transformArguments({ + key: 'key', + id: '0' + }, { + BLOCK: 0 + }), + ['XREAD', 'BLOCK', '0', 'STREAMS', 'key', '0'] + ); + }); + + it('with COUNT, BLOCK', () => { + assert.deepEqual( + transformArguments({ + key: 'key', + id: '0' + }, { + COUNT: 1, + BLOCK: 0 + }), + ['XREAD', 'COUNT', '1', 'BLOCK', '0', 'STREAMS', 'key', '0'] + ); + }); + }); + + itWithClient(TestRedisServers.OPEN, 'client.xRead', async client => { + assert.equal( + await client.xRead({ + key: 'key', + id: '0' + }), + null + ); + }); + + itWithCluster(TestRedisClusters.OPEN, 'cluster.xRead', async cluster => { + assert.equal( + await cluster.xRead({ + key: 'key', + id: '0' + }), + null + ); + }); +}); diff --git a/lib/commands/XREAD.ts b/lib/commands/XREAD.ts new file mode 100644 index 00000000000..1f4932837a2 --- /dev/null +++ b/lib/commands/XREAD.ts @@ -0,0 +1,43 @@ +import { transformReplyStreamsMessages } from './generic-transformers'; + +export const FIRST_KEY_INDEX = (streams: Array | XReadStream): string => { + return Array.isArray(streams) ? streams[0].key : streams.key; +}; + +export const IS_READ_ONLY = true; + +interface XReadStream { + key: string; + id: string; +} + +interface XReadOptions { + COUNT?: number; + BLOCK?: number; +} + +export function transformArguments(streams: Array | XReadStream, options?: XReadOptions): Array { + const args = ['XREAD']; + + if (options?.COUNT) { + args.push('COUNT', options.COUNT.toString()); + } + + if (typeof options?.BLOCK === 'number') { + args.push('BLOCK', options.BLOCK.toString()); + } + + args.push('STREAMS'); + + const streamsArray = Array.isArray(streams) ? streams : [streams], + argsLength = args.length; + for (let i = 0; i < streamsArray.length; i++) { + const stream = streamsArray[i]; + args[argsLength + i] = stream.key; + args[argsLength + streamsArray.length + i] = stream.id; + } + + return args; +} + +export const transformReply = transformReplyStreamsMessages; diff --git a/lib/commands/XREADGROUP.spec.ts b/lib/commands/XREADGROUP.spec.ts new file mode 100644 index 00000000000..8f7693c333b --- /dev/null +++ b/lib/commands/XREADGROUP.spec.ts @@ -0,0 +1,137 @@ +import { strict as assert } from 'assert'; +import { TestRedisServers, itWithClient, itWithCluster, TestRedisClusters } from '../test-utils'; +import { transformArguments } from './XREADGROUP'; + +describe('XREADGROUP', () => { + describe('transformArguments', () => { + it('single stream', () => { + assert.deepEqual( + transformArguments('group', 'consumer', { + key: 'key', + id: '0' + }), + ['XREADGROUP', 'GROUP', 'group', 'consumer', 'STREAMS', 'key', '0'] + ); + }); + + it('multiple streams', () => { + assert.deepEqual( + transformArguments('group', 'consumer', [{ + key: '1', + id: '0' + }, { + key: '2', + id: '0' + }]), + ['XREADGROUP', 'GROUP', 'group', 'consumer', 'STREAMS', '1', '2', '0', '0'] + ); + }); + + it('with COUNT', () => { + assert.deepEqual( + transformArguments('group', 'consumer', { + key: 'key', + id: '0' + }, { + COUNT: 1 + }), + ['XREADGROUP', 'GROUP', 'group', 'consumer', 'COUNT', '1', 'STREAMS', 'key', '0'] + ); + }); + + it('with BLOCK', () => { + assert.deepEqual( + transformArguments('group', 'consumer', { + key: 'key', + id: '0' + }, { + BLOCK: 0 + }), + ['XREADGROUP', 'GROUP', 'group', 'consumer', 'BLOCK', '0', 'STREAMS', 'key', '0'] + ); + }); + + it('with NOACK', () => { + assert.deepEqual( + transformArguments('group', 'consumer', { + key: 'key', + id: '0' + }, { + NOACK: true + }), + ['XREADGROUP', 'GROUP', 'group', 'consumer', 'NOACK', 'STREAMS', 'key', '0'] + ); + }); + + it('with COUNT, BLOCK, NOACK', () => { + assert.deepEqual( + transformArguments('group', 'consumer', { + key: 'key', + id: '0' + }, { + COUNT: 1, + BLOCK: 0, + NOACK: true + }), + ['XREADGROUP', 'GROUP', 'group', 'consumer', 'COUNT', '1', 'BLOCK', '0', 'NOACK', 'STREAMS', 'key', '0'] + ); + }); + }); + + describe('client.xReadGroup', () => { + itWithClient(TestRedisServers.OPEN, 'null', async client => { + const [, readGroupReply] = await Promise.all([ + client.xGroupCreate('key', 'group', '$', { + MKSTREAM: true + }), + client.xReadGroup('group', 'consumer', { + key: 'key', + id: '>' + }) + ]); + + assert.equal(readGroupReply, null); + }); + + itWithClient(TestRedisServers.OPEN, 'with a message', async client => { + const [, id, readGroupReply] = await Promise.all([ + client.xGroupCreate('key', 'group', '$', { + MKSTREAM: true + }), + client.xAdd('key', '*', { field: 'value' }), + client.xReadGroup('group', 'consumer', { + key: 'key', + id: '>' + }) + ]); + + assert.deepEqual(readGroupReply, [{ + name: 'key', + messages: [{ + id, + message: Object.create(null, { + field: { + value: 'value', + configurable: true, + enumerable: true + } + }) + }] + }]); + }); + }); + + itWithCluster(TestRedisClusters.OPEN, 'cluster.xReadGroup', async cluster => { + const [, readGroupReply] = await Promise.all([ + cluster.xGroupCreate('key', 'group', '$', { + MKSTREAM: true + }), + cluster.xReadGroup('group', 'consumer', { + key: 'key', + id: '>' + }) + ]); + + assert.equal(readGroupReply, null); + }); +}); diff --git a/lib/commands/XREADGROUP.ts b/lib/commands/XREADGROUP.ts new file mode 100644 index 00000000000..b01385e7c2f --- /dev/null +++ b/lib/commands/XREADGROUP.ts @@ -0,0 +1,57 @@ +import { transformReplyStreamsMessages } from './generic-transformers'; + +export interface XReadGroupStream { + key: string; + id: string; +} + +export interface XReadGroupOptions { + COUNT?: number; + BLOCK?: number; + NOACK?: true; +} + +export const FIRST_KEY_INDEX = ( + _group: string, + _consumer: string, + streams: Array | XReadGroupStream +): string => { + return Array.isArray(streams) ? streams[0].key : streams.key; +}; + +export const IS_READ_ONLY = true; + +export function transformArguments( + group: string, + consumer: string, + streams: Array | XReadGroupStream, + options?: XReadGroupOptions +): Array { + const args = ['XREADGROUP', 'GROUP', group, consumer]; + + if (options?.COUNT) { + args.push('COUNT', options.COUNT.toString()); + } + + if (typeof options?.BLOCK === 'number') { + args.push('BLOCK', options.BLOCK.toString()); + } + + if (options?.NOACK) { + args.push('NOACK'); + } + + args.push('STREAMS'); + + const streamsArray = Array.isArray(streams) ? streams : [streams], + argsLength = args.length; + for (let i = 0; i < streamsArray.length; i++) { + const stream = streamsArray[i]; + args[argsLength + i] = stream.key; + args[argsLength + streamsArray.length + i] = stream.id; + } + + return args; +} + +export const transformReply = transformReplyStreamsMessages; diff --git a/lib/commands/XREVRANGE.spec.ts b/lib/commands/XREVRANGE.spec.ts new file mode 100644 index 00000000000..ba009cc2bbe --- /dev/null +++ b/lib/commands/XREVRANGE.spec.ts @@ -0,0 +1,30 @@ +import { strict as assert } from 'assert'; +import { TestRedisServers, itWithClient } from '../test-utils'; +import { transformArguments } from './XREVRANGE'; + +describe('XREVRANGE', () => { + describe('transformArguments', () => { + it('simple', () => { + assert.deepEqual( + transformArguments('key', '-', '+'), + ['XREVRANGE', 'key', '-', '+'] + ); + }); + + it('with COUNT', () => { + assert.deepEqual( + transformArguments('key', '-', '+', { + COUNT: 1 + }), + ['XREVRANGE', 'key', '-', '+', 'COUNT', '1'] + ); + }); + }); + + itWithClient(TestRedisServers.OPEN, 'client.xRevRange', async client => { + assert.deepEqual( + await client.xRevRange('key', '+', '-'), + [] + ); + }); +}); diff --git a/lib/commands/XREVRANGE.ts b/lib/commands/XREVRANGE.ts new file mode 100644 index 00000000000..a1fbbbc128a --- /dev/null +++ b/lib/commands/XREVRANGE.ts @@ -0,0 +1,17 @@ +import { transformReplyStreamMessages } from './generic-transformers'; + +interface XRangeRevOptions { + COUNT?: number; +} + +export function transformArguments(key: string, start: string, end: string, options?: XRangeRevOptions): Array { + const args = ['XREVRANGE', key, start, end]; + + if (options?.COUNT) { + args.push('COUNT', options.COUNT.toString()); + } + + return args; +} + +export const transformReply = transformReplyStreamMessages; diff --git a/lib/commands/XTRIM.spec.ts b/lib/commands/XTRIM.spec.ts new file mode 100644 index 00000000000..0b48fd6a2d6 --- /dev/null +++ b/lib/commands/XTRIM.spec.ts @@ -0,0 +1,49 @@ +import { strict as assert } from 'assert'; +import { TestRedisServers, itWithClient } from '../test-utils'; +import { transformArguments } from './XTRIM'; + +describe('XTRIM', () => { + describe('transformArguments', () => { + it('simple', () => { + assert.deepEqual( + transformArguments('key', 'MAXLEN', 1), + ['XTRIM', 'key', 'MAXLEN', '1'] + ); + }); + + it('with strategyModifier', () => { + assert.deepEqual( + transformArguments('key', 'MAXLEN', 1, { + strategyModifier: '=' + }), + ['XTRIM', 'key', 'MAXLEN', '=', '1'] + ); + }); + + it('with LIMIT', () => { + assert.deepEqual( + transformArguments('key', 'MAXLEN', 1, { + LIMIT: 1 + }), + ['XTRIM', 'key', 'MAXLEN', '1', 'LIMIT', '1'] + ); + }); + + it('with strategyModifier, LIMIT', () => { + assert.deepEqual( + transformArguments('key', 'MAXLEN', 1, { + strategyModifier: '=', + LIMIT: 1 + }), + ['XTRIM', 'key', 'MAXLEN', '=', '1', 'LIMIT', '1'] + ); + }); + }); + + itWithClient(TestRedisServers.OPEN, 'client.xTrim', async client => { + assert.equal( + await client.xTrim('key', 'MAXLEN', 1), + 0 + ); + }); +}); diff --git a/lib/commands/XTRIM.ts b/lib/commands/XTRIM.ts new file mode 100644 index 00000000000..8175ba70df3 --- /dev/null +++ b/lib/commands/XTRIM.ts @@ -0,0 +1,26 @@ +import { transformReplyNumber } from './generic-transformers'; + +export const FIRST_KEY_INDEX = 1; + +interface XTrimOptions { + strategyModifier?: '=' | '~'; + LIMIT?: number; +} + +export function transformArguments(key: string, strategy: 'MAXLEN' | 'MINID', threshold: number, options?: XTrimOptions): Array { + const args = ['XTRIM', key, strategy]; + + if (options?.strategyModifier) { + args.push(options.strategyModifier); + } + + args.push(threshold.toString()); + + if (options?.LIMIT) { + args.push('LIMIT', options.LIMIT.toString()); + } + + return args; +} + +export const transformReply = transformReplyNumber; diff --git a/lib/commands/ZADD.spec.ts b/lib/commands/ZADD.spec.ts new file mode 100644 index 00000000000..7c017e45410 --- /dev/null +++ b/lib/commands/ZADD.spec.ts @@ -0,0 +1,127 @@ +import { strict as assert } from 'assert'; +import { TestRedisServers, itWithClient } from '../test-utils'; +import { transformArguments } from './ZADD'; + +describe('ZADD', () => { + describe('transformArguments', () => { + it('single member', () => { + assert.deepEqual( + transformArguments('key', { + value: '1', + score: 1 + }), + ['ZADD', 'key', '1', '1'] + ); + }); + + it('multiple members', () => { + assert.deepEqual( + transformArguments('key', [{ + value: '1', + score: 1 + }, { + value: '2', + score: 2 + }]), + ['ZADD', 'key', '1', '1', '2', '2'] + ); + }); + + it('with NX', () => { + assert.deepEqual( + transformArguments('key', { + value: '1', + score: 1 + }, { + NX: true + }), + ['ZADD', 'key', 'NX', '1', '1'] + ); + }); + + it('with XX', () => { + assert.deepEqual( + transformArguments('key', { + value: '1', + score: 1 + }, { + XX: true + }), + ['ZADD', 'key', 'XX', '1', '1'] + ); + }); + + it('with GT', () => { + assert.deepEqual( + transformArguments('key', { + value: '1', + score: 1 + }, { + GT: true + }), + ['ZADD', 'key', 'GT', '1', '1'] + ); + }); + + it('with LT', () => { + assert.deepEqual( + transformArguments('key', { + value: '1', + score: 1 + }, { + LT: true + }), + ['ZADD', 'key', 'LT', '1', '1'] + ); + }); + + it('with CH', () => { + assert.deepEqual( + transformArguments('key', { + value: '1', + score: 1 + }, { + CH: true + }), + ['ZADD', 'key', 'CH', '1', '1'] + ); + }); + + it('with INCR', () => { + assert.deepEqual( + transformArguments('key', { + value: '1', + score: 1 + }, { + INCR: true + }), + ['ZADD', 'key', 'INCR', '1', '1'] + ); + }); + + it('with XX, GT, CH, INCR', () => { + assert.deepEqual( + transformArguments('key', { + value: '1', + score: 1 + }, { + XX: true, + GT: true, + CH: true, + INCR: true + }), + ['ZADD', 'key', 'XX', 'GT', 'CH', 'INCR', '1', '1'] + ); + }); + }); + + itWithClient(TestRedisServers.OPEN, 'client.zAdd', async client => { + assert.equal( + await client.zAdd('key', { + value: '1', + score: 1 + }), + 1 + ); + }); +}); diff --git a/lib/commands/ZADD.ts b/lib/commands/ZADD.ts new file mode 100644 index 00000000000..0e43ecaf508 --- /dev/null +++ b/lib/commands/ZADD.ts @@ -0,0 +1,66 @@ +import { transformArgumentNumberInfinity, transformReplyNumberInfinity, ZMember } from './generic-transformers'; + +export const FIRST_KEY_INDEX = 1; + +interface NX { + NX?: true; +} + +interface XX { + XX?: true; +} + +interface LT { + LT?: true; +} + +interface GT { + GT?: true; +} + +interface CH { + CH?: true; +} + +interface INCR { + INCR?: true; +} + +type ZAddOptions = (NX | (XX & LT & GT)) & CH & INCR; + +export function transformArguments(key: string, members: ZMember | Array, options?: ZAddOptions): Array { + const args = ['ZADD', key]; + + if ((options)?.NX) { + args.push('NX'); + } else { + if ((options)?.XX) { + args.push('XX'); + } + + if ((options)?.GT) { + args.push('GT'); + } else if ((options)?.LT) { + args.push('LT'); + } + } + + if ((options)?.CH) { + args.push('CH'); + } + + if ((options)?.INCR) { + args.push('INCR'); + } + + for (const { score, value } of (Array.isArray(members) ? members : [members])) { + args.push( + transformArgumentNumberInfinity(score), + value + ); + } + + return args; +} + +export const transformReply = transformReplyNumberInfinity; diff --git a/lib/commands/ZCARD.spec.ts b/lib/commands/ZCARD.spec.ts new file mode 100644 index 00000000000..03bfe59cfc3 --- /dev/null +++ b/lib/commands/ZCARD.spec.ts @@ -0,0 +1,19 @@ +import { strict as assert } from 'assert'; +import { TestRedisServers, itWithClient } from '../test-utils'; +import { transformArguments } from './ZCARD'; + +describe('ZCARD', () => { + it('transformArguments', () => { + assert.deepEqual( + transformArguments('key'), + ['ZCARD', 'key'] + ); + }); + + itWithClient(TestRedisServers.OPEN, 'client.zCard', async client => { + assert.equal( + await client.zCard('key'), + 0 + ); + }); +}); diff --git a/lib/commands/ZCARD.ts b/lib/commands/ZCARD.ts new file mode 100644 index 00000000000..f6e4ea5f6cd --- /dev/null +++ b/lib/commands/ZCARD.ts @@ -0,0 +1,11 @@ +import { transformReplyNumber } from './generic-transformers'; + +export const FIRST_KEY_INDEX = 1; + +export const IS_READ_ONLY = true; + +export function transformArguments(key: string): Array { + return ['ZCARD', key]; +} + +export const transformReply = transformReplyNumber; diff --git a/lib/commands/ZCOUNT.spec.ts b/lib/commands/ZCOUNT.spec.ts new file mode 100644 index 00000000000..e461241ce1c --- /dev/null +++ b/lib/commands/ZCOUNT.spec.ts @@ -0,0 +1,19 @@ +import { strict as assert } from 'assert'; +import { TestRedisServers, itWithClient } from '../test-utils'; +import { transformArguments } from './ZCOUNT'; + +describe('ZCOUNT', () => { + it('transformArguments', () => { + assert.deepEqual( + transformArguments('key', 0, 1), + ['ZCOUNT', 'key', '0', '1'] + ); + }); + + itWithClient(TestRedisServers.OPEN, 'client.zCount', async client => { + assert.equal( + await client.zCount('key', 0, 1), + 0 + ); + }); +}); diff --git a/lib/commands/ZCOUNT.ts b/lib/commands/ZCOUNT.ts new file mode 100644 index 00000000000..fd73c384489 --- /dev/null +++ b/lib/commands/ZCOUNT.ts @@ -0,0 +1,16 @@ +import { transformArgumentNumberInfinity, transformReplyNumber } from './generic-transformers'; + +export const FIRST_KEY_INDEX = 1; + +export const IS_READ_ONLY = true; + +export function transformArguments(key: string, min: number, max: number): Array { + return [ + 'ZCOUNT', + key, + transformArgumentNumberInfinity(min), + transformArgumentNumberInfinity(max) + ]; +} + +export const transformReply = transformReplyNumber; diff --git a/lib/commands/ZDIFF.spec.ts b/lib/commands/ZDIFF.spec.ts new file mode 100644 index 00000000000..f45b2af7edc --- /dev/null +++ b/lib/commands/ZDIFF.spec.ts @@ -0,0 +1,30 @@ +import { strict as assert } from 'assert'; +import { TestRedisServers, itWithClient, describeHandleMinimumRedisVersion } from '../test-utils'; +import { transformArguments } from './ZDIFF'; + +describe('ZDIFF', () => { + describeHandleMinimumRedisVersion([6, 2]); + + describe('transformArguments', () => { + it('string', () => { + assert.deepEqual( + transformArguments('key'), + ['ZDIFF', '1', 'key'] + ); + }); + + it('array', () => { + assert.deepEqual( + transformArguments(['1', '2']), + ['ZDIFF', '2', '1', '2'] + ); + }); + }); + + itWithClient(TestRedisServers.OPEN, 'client.zDiff', async client => { + assert.deepEqual( + await client.zDiff('key'), + [] + ); + }); +}); diff --git a/lib/commands/ZDIFF.ts b/lib/commands/ZDIFF.ts new file mode 100644 index 00000000000..f557b597ec4 --- /dev/null +++ b/lib/commands/ZDIFF.ts @@ -0,0 +1,11 @@ +import { pushVerdictArgument, transformReplyStringArray } from './generic-transformers'; + +export const FIRST_KEY_INDEX = 2; + +export const IS_READ_ONLY = true; + +export function transformArguments(keys: Array | string): Array { + return pushVerdictArgument(['ZDIFF'], keys); +} + +export const transformReply = transformReplyStringArray; diff --git a/lib/commands/ZDIFFSTORE.spec.ts b/lib/commands/ZDIFFSTORE.spec.ts new file mode 100644 index 00000000000..5fbeebaf502 --- /dev/null +++ b/lib/commands/ZDIFFSTORE.spec.ts @@ -0,0 +1,30 @@ +import { strict as assert } from 'assert'; +import { TestRedisServers, itWithClient, describeHandleMinimumRedisVersion } from '../test-utils'; +import { transformArguments } from './ZDIFFSTORE'; + +describe('ZDIFFSTORE', () => { + describeHandleMinimumRedisVersion([6, 2]); + + describe('transformArguments', () => { + it('string', () => { + assert.deepEqual( + transformArguments('destination', 'key'), + ['ZDIFFSTORE', 'destination', '1', 'key'] + ); + }); + + it('array', () => { + assert.deepEqual( + transformArguments('destination', ['1', '2']), + ['ZDIFFSTORE', 'destination', '2', '1', '2'] + ); + }); + }); + + itWithClient(TestRedisServers.OPEN, 'client.zDiffStore', async client => { + assert.equal( + await client.zDiffStore('destination', 'key'), + 0 + ); + }); +}); diff --git a/lib/commands/ZDIFFSTORE.ts b/lib/commands/ZDIFFSTORE.ts new file mode 100644 index 00000000000..de409c0939a --- /dev/null +++ b/lib/commands/ZDIFFSTORE.ts @@ -0,0 +1,9 @@ +import { pushVerdictArgument, transformReplyNumber } from './generic-transformers'; + +export const FIRST_KEY_INDEX = 1; + +export function transformArguments(destination: string, keys: Array | string): Array { + return pushVerdictArgument(['ZDIFFSTORE', destination], keys); +} + +export const transformReply = transformReplyNumber; diff --git a/lib/commands/ZDIFF_WITHSCORES.spec.ts b/lib/commands/ZDIFF_WITHSCORES.spec.ts new file mode 100644 index 00000000000..99c23108292 --- /dev/null +++ b/lib/commands/ZDIFF_WITHSCORES.spec.ts @@ -0,0 +1,30 @@ +import { strict as assert } from 'assert'; +import { TestRedisServers, itWithClient, describeHandleMinimumRedisVersion } from '../test-utils'; +import { transformArguments } from './ZDIFF_WITHSCORES'; + +describe('ZDIFF WITHSCORES', () => { + describeHandleMinimumRedisVersion([6, 2]); + + describe('transformArguments', () => { + it('string', () => { + assert.deepEqual( + transformArguments('key'), + ['ZDIFF', '1', 'key', 'WITHSCORES'] + ); + }); + + it('array', () => { + assert.deepEqual( + transformArguments(['1', '2']), + ['ZDIFF', '2', '1', '2', 'WITHSCORES'] + ); + }); + }); + + itWithClient(TestRedisServers.OPEN, 'client.zDiffWithScores', async client => { + assert.deepEqual( + await client.zDiffWithScores('key'), + [] + ); + }); +}); diff --git a/lib/commands/ZDIFF_WITHSCORES.ts b/lib/commands/ZDIFF_WITHSCORES.ts new file mode 100644 index 00000000000..26effab7189 --- /dev/null +++ b/lib/commands/ZDIFF_WITHSCORES.ts @@ -0,0 +1,13 @@ +import { transformReplySortedSetWithScores } from './generic-transformers'; +import { transformArguments as transformZDiffArguments } from './ZDIFF'; + +export { FIRST_KEY_INDEX, IS_READ_ONLY } from './ZDIFF'; + +export function transformArguments(...args: Parameters): Array { + return [ + ...transformZDiffArguments(...args), + 'WITHSCORES' + ]; +} + +export const transformReply = transformReplySortedSetWithScores; diff --git a/lib/commands/ZINCRBY.spec.ts b/lib/commands/ZINCRBY.spec.ts new file mode 100644 index 00000000000..2196c63ec06 --- /dev/null +++ b/lib/commands/ZINCRBY.spec.ts @@ -0,0 +1,19 @@ +import { strict as assert } from 'assert'; +import { TestRedisServers, itWithClient } from '../test-utils'; +import { transformArguments } from './ZINCRBY'; + +describe('ZINCRBY', () => { + it('transformArguments', () => { + assert.deepEqual( + transformArguments('key', 1, 'member'), + ['ZINCRBY', 'key', '1', 'member'] + ); + }); + + itWithClient(TestRedisServers.OPEN, 'client.zIncrBy', async client => { + assert.equal( + await client.zIncrBy('destination', 1, 'member'), + 1 + ); + }); +}); diff --git a/lib/commands/ZINCRBY.ts b/lib/commands/ZINCRBY.ts new file mode 100644 index 00000000000..39f32c165f7 --- /dev/null +++ b/lib/commands/ZINCRBY.ts @@ -0,0 +1,14 @@ +import { transformArgumentNumberInfinity, transformReplyNumberInfinity } from './generic-transformers'; + +export const FIRST_KEY_INDEX = 1; + +export function transformArguments(key: string, increment: number, member: string): Array { + return [ + 'ZINCRBY', + key, + transformArgumentNumberInfinity(increment), + member + ]; +} + +export const transformReply = transformReplyNumberInfinity; diff --git a/lib/commands/ZINTER.spec.ts b/lib/commands/ZINTER.spec.ts new file mode 100644 index 00000000000..998c46fd3e0 --- /dev/null +++ b/lib/commands/ZINTER.spec.ts @@ -0,0 +1,58 @@ +import { strict as assert } from 'assert'; +import { TestRedisServers, itWithClient, describeHandleMinimumRedisVersion } from '../test-utils'; +import { transformArguments } from './ZINTER'; + +describe('ZINTER', () => { + describeHandleMinimumRedisVersion([6, 2]); + + describe('transformArguments', () => { + it('key (string)', () => { + assert.deepEqual( + transformArguments('key'), + ['ZINTER', '1', 'key'] + ); + }); + + it('keys (array)', () => { + assert.deepEqual( + transformArguments(['1', '2']), + ['ZINTER', '2', '1', '2'] + ); + }); + + it('with WEIGHTS', () => { + assert.deepEqual( + transformArguments('key', { + WEIGHTS: [1] + }), + ['ZINTER', '1', 'key', 'WEIGHTS', '1'] + ); + }); + + it('with AGGREGATE', () => { + assert.deepEqual( + transformArguments('key', { + AGGREGATE: 'SUM' + }), + ['ZINTER', '1', 'key', 'AGGREGATE', 'SUM'] + ); + }); + + it('with WEIGHTS, AGGREGATE', () => { + assert.deepEqual( + transformArguments('key', { + WEIGHTS: [1], + AGGREGATE: 'SUM' + }), + ['ZINTER', '1', 'key', 'WEIGHTS', '1', 'AGGREGATE', 'SUM'] + ); + }); + }); + + itWithClient(TestRedisServers.OPEN, 'client.zInter', async client => { + assert.deepEqual( + await client.zInter('key'), + [] + ); + }); +}); diff --git a/lib/commands/ZINTER.ts b/lib/commands/ZINTER.ts new file mode 100644 index 00000000000..90a42eda0d3 --- /dev/null +++ b/lib/commands/ZINTER.ts @@ -0,0 +1,29 @@ +import { pushVerdictArgument, transformReplyStringArray } from './generic-transformers'; + +export const FIRST_KEY_INDEX = 2; + +export const IS_READ_ONLY = true; + +interface ZInterOptions { + WEIGHTS?: Array; + AGGREGATE?: 'SUM' | 'MIN' | 'MAX'; +} + +export function transformArguments(keys: Array | string, options?: ZInterOptions): Array { + const args = pushVerdictArgument(['ZINTER'], keys); + + if (options?.WEIGHTS) { + args.push( + 'WEIGHTS', + ...options.WEIGHTS.map(weight => weight.toString()) + ); + } + + if (options?.AGGREGATE) { + args.push('AGGREGATE', options?.AGGREGATE); + } + + return args; +} + +export const transformReply = transformReplyStringArray; diff --git a/lib/commands/ZINTERSTORE.spec.ts b/lib/commands/ZINTERSTORE.spec.ts new file mode 100644 index 00000000000..fca03157cb2 --- /dev/null +++ b/lib/commands/ZINTERSTORE.spec.ts @@ -0,0 +1,56 @@ +import { strict as assert } from 'assert'; +import { TestRedisServers, itWithClient } from '../test-utils'; +import { transformArguments } from './ZINTERSTORE'; + +describe('ZINTERSTORE', () => { + describe('transformArguments', () => { + it('key (string)', () => { + assert.deepEqual( + transformArguments('destination', 'key'), + ['ZINTERSTORE', 'destination', '1', 'key'] + ); + }); + + it('keys (array)', () => { + assert.deepEqual( + transformArguments('destination', ['1', '2']), + ['ZINTERSTORE', 'destination', '2', '1', '2'] + ); + }); + + it('with WEIGHTS', () => { + assert.deepEqual( + transformArguments('destination', 'key', { + WEIGHTS: [1] + }), + ['ZINTERSTORE', 'destination', '1', 'key', 'WEIGHTS', '1'] + ); + }); + + it('with AGGREGATE', () => { + assert.deepEqual( + transformArguments('destination', 'key', { + AGGREGATE: 'SUM' + }), + ['ZINTERSTORE', 'destination', '1', 'key', 'AGGREGATE', 'SUM'] + ); + }); + + it('with WEIGHTS, AGGREGATE', () => { + assert.deepEqual( + transformArguments('destination', 'key', { + WEIGHTS: [1], + AGGREGATE: 'SUM' + }), + ['ZINTERSTORE', 'destination', '1', 'key', 'WEIGHTS', '1', 'AGGREGATE', 'SUM'] + ); + }); + }); + + itWithClient(TestRedisServers.OPEN, 'client.zInterStore', async client => { + assert.equal( + await client.zInterStore('destination', 'key'), + 0 + ); + }); +}); diff --git a/lib/commands/ZINTERSTORE.ts b/lib/commands/ZINTERSTORE.ts new file mode 100644 index 00000000000..a026916ce1f --- /dev/null +++ b/lib/commands/ZINTERSTORE.ts @@ -0,0 +1,27 @@ +import { pushVerdictArgument, transformReplyNumber } from './generic-transformers'; + +export const FIRST_KEY_INDEX = 1; + +interface ZInterStoreOptions { + WEIGHTS?: Array; + AGGREGATE?: 'SUM' | 'MIN' | 'MAX'; +} + +export function transformArguments(destination: string, keys: Array | string, options?: ZInterStoreOptions): Array { + const args = pushVerdictArgument(['ZINTERSTORE', destination], keys); + + if (options?.WEIGHTS) { + args.push( + 'WEIGHTS', + ...options.WEIGHTS.map(weight => weight.toString()) + ); + } + + if (options?.AGGREGATE) { + args.push('AGGREGATE', options?.AGGREGATE); + } + + return args; +} + +export const transformReply = transformReplyNumber; diff --git a/lib/commands/ZINTER_WITHSCORES.spec.ts b/lib/commands/ZINTER_WITHSCORES.spec.ts new file mode 100644 index 00000000000..f66787e3def --- /dev/null +++ b/lib/commands/ZINTER_WITHSCORES.spec.ts @@ -0,0 +1,58 @@ +import { strict as assert } from 'assert'; +import { TestRedisServers, itWithClient, describeHandleMinimumRedisVersion } from '../test-utils'; +import { transformArguments } from './ZINTER_WITHSCORES'; + +describe('ZINTER WITHSCORES', () => { + describeHandleMinimumRedisVersion([6, 2]); + + describe('transformArguments', () => { + it('key (string)', () => { + assert.deepEqual( + transformArguments('key'), + ['ZINTER', '1', 'key', 'WITHSCORES'] + ); + }); + + it('keys (array)', () => { + assert.deepEqual( + transformArguments(['1', '2']), + ['ZINTER', '2', '1', '2', 'WITHSCORES'] + ); + }); + + it('with WEIGHTS', () => { + assert.deepEqual( + transformArguments('key', { + WEIGHTS: [1] + }), + ['ZINTER', '1', 'key', 'WEIGHTS', '1', 'WITHSCORES'] + ); + }); + + it('with AGGREGATE', () => { + assert.deepEqual( + transformArguments('key', { + AGGREGATE: 'SUM' + }), + ['ZINTER', '1', 'key', 'AGGREGATE', 'SUM', 'WITHSCORES'] + ); + }); + + it('with WEIGHTS, AGGREGATE', () => { + assert.deepEqual( + transformArguments('key', { + WEIGHTS: [1], + AGGREGATE: 'SUM' + }), + ['ZINTER', '1', 'key', 'WEIGHTS', '1', 'AGGREGATE', 'SUM', 'WITHSCORES'] + ); + }); + }); + + itWithClient(TestRedisServers.OPEN, 'client.zInterWithScores', async client => { + assert.deepEqual( + await client.zInterWithScores('key'), + [] + ); + }); +}); diff --git a/lib/commands/ZINTER_WITHSCORES.ts b/lib/commands/ZINTER_WITHSCORES.ts new file mode 100644 index 00000000000..0a82228fce9 --- /dev/null +++ b/lib/commands/ZINTER_WITHSCORES.ts @@ -0,0 +1,13 @@ +import { transformReplySortedSetWithScores } from './generic-transformers'; +import { transformArguments as transformZInterArguments } from './ZINTER'; + +export { FIRST_KEY_INDEX, IS_READ_ONLY } from './ZINTER'; + +export function transformArguments(...args: Parameters): Array { + return [ + ...transformZInterArguments(...args), + 'WITHSCORES' + ]; +} + +export const transformReply = transformReplySortedSetWithScores; diff --git a/lib/commands/ZLEXCOUNT.spec.ts b/lib/commands/ZLEXCOUNT.spec.ts new file mode 100644 index 00000000000..b106ba0cdc5 --- /dev/null +++ b/lib/commands/ZLEXCOUNT.spec.ts @@ -0,0 +1,19 @@ +import { strict as assert } from 'assert'; +import { TestRedisServers, itWithClient } from '../test-utils'; +import { transformArguments } from './ZLEXCOUNT'; + +describe('ZLEXCOUNT', () => { + it('transformArguments', () => { + assert.deepEqual( + transformArguments('key', '[a', '[b'), + ['ZLEXCOUNT', 'key', '[a', '[b'] + ); + }); + + itWithClient(TestRedisServers.OPEN, 'client.zLexCount', async client => { + assert.equal( + await client.zLexCount('key', '[a', '[b'), + 0 + ); + }); +}); diff --git a/lib/commands/ZLEXCOUNT.ts b/lib/commands/ZLEXCOUNT.ts new file mode 100644 index 00000000000..2ba50dda73e --- /dev/null +++ b/lib/commands/ZLEXCOUNT.ts @@ -0,0 +1,16 @@ +import { transformReplyNumber } from './generic-transformers'; + +export const FIRST_KEY_INDEX = 1; + +export const IS_READ_ONLY = true; + +export function transformArguments(key: string, min: string, max: string): Array { + return [ + 'ZLEXCOUNT', + key, + min, + max + ]; +} + +export const transformReply = transformReplyNumber; diff --git a/lib/commands/ZMSCORE.spec.ts b/lib/commands/ZMSCORE.spec.ts new file mode 100644 index 00000000000..3cf3845392d --- /dev/null +++ b/lib/commands/ZMSCORE.spec.ts @@ -0,0 +1,30 @@ +import { strict as assert } from 'assert'; +import { TestRedisServers, itWithClient, describeHandleMinimumRedisVersion } from '../test-utils'; +import { transformArguments } from './ZMSCORE'; + +describe('ZMSCORE', () => { + describeHandleMinimumRedisVersion([6, 2]); + + describe('transformArguments', () => { + it('string', () => { + assert.deepEqual( + transformArguments('key', 'member'), + ['ZMSCORE', 'key', 'member'] + ); + }); + + it('array', () => { + assert.deepEqual( + transformArguments('key', ['1', '2']), + ['ZMSCORE', 'key', '1', '2'] + ); + }); + }); + + itWithClient(TestRedisServers.OPEN, 'client.zmScore', async client => { + assert.deepEqual( + await client.zmScore('key', 'member'), + [null] + ); + }); +}); diff --git a/lib/commands/ZMSCORE.ts b/lib/commands/ZMSCORE.ts new file mode 100644 index 00000000000..8a6f73c7836 --- /dev/null +++ b/lib/commands/ZMSCORE.ts @@ -0,0 +1,11 @@ +import { pushVerdictArguments, transformReplyNumberInfinityNullArray } from './generic-transformers'; + +export const FIRST_KEY_INDEX = 1; + +export const IS_READ_ONLY = true; + +export function transformArguments(key: string, member: string | Array): Array { + return pushVerdictArguments(['ZMSCORE', key], member); +} + +export const transformReply = transformReplyNumberInfinityNullArray; diff --git a/lib/commands/ZPOPMAX.spec.ts b/lib/commands/ZPOPMAX.spec.ts new file mode 100644 index 00000000000..ceab3cad1d0 --- /dev/null +++ b/lib/commands/ZPOPMAX.spec.ts @@ -0,0 +1,41 @@ +import { strict as assert } from 'assert'; +import { TestRedisServers, itWithClient } from '../test-utils'; +import { transformArguments, transformReply } from './ZPOPMAX'; + +describe('ZPOPMAX', () => { + it('transformArguments', () => { + assert.deepEqual( + transformArguments('key'), + ['ZPOPMAX', 'key'] + ); + }); + + it('transformReply', () => { + assert.deepEqual( + transformReply(['value', '1']), + { + value: 'value', + score: 1 + } + ); + }); + + describe('client.zPopMax', () => { + itWithClient(TestRedisServers.OPEN, 'null', async client => { + assert.equal( + await client.zPopMax('key'), + null + ); + }); + + itWithClient(TestRedisServers.OPEN, 'member', async client => { + const member = { score: 1, value: 'value' }, + [, zPopMaxReply] = await Promise.all([ + client.zAdd('key', member), + client.zPopMax('key') + ]); + + assert.deepEqual(zPopMaxReply, member); + }); + }); +}); diff --git a/lib/commands/ZPOPMAX.ts b/lib/commands/ZPOPMAX.ts new file mode 100644 index 00000000000..8da20731abf --- /dev/null +++ b/lib/commands/ZPOPMAX.ts @@ -0,0 +1,19 @@ +import { transformReplyNumberInfinity, ZMember } from './generic-transformers'; + +export const FIRST_KEY_INDEX = 1; + +export function transformArguments(key: string): Array { + return [ + 'ZPOPMAX', + key + ]; +} + +export function transformReply(reply: [string, string] | []): ZMember | null { + if (!reply.length) return null; + + return { + value: reply[0], + score: transformReplyNumberInfinity(reply[1]) + }; +} diff --git a/lib/commands/ZPOPMAX_COUNT.spec.ts b/lib/commands/ZPOPMAX_COUNT.spec.ts new file mode 100644 index 00000000000..c0e71977ee8 --- /dev/null +++ b/lib/commands/ZPOPMAX_COUNT.spec.ts @@ -0,0 +1,19 @@ +import { strict as assert } from 'assert'; +import { TestRedisServers, itWithClient } from '../test-utils'; +import { transformArguments } from './ZPOPMAX_COUNT'; + +describe('ZPOPMAX COUNT', () => { + it('transformArguments', () => { + assert.deepEqual( + transformArguments('key', 1), + ['ZPOPMAX', 'key', '1'] + ); + }); + + itWithClient(TestRedisServers.OPEN, 'client.zPopMaxCount', async client => { + assert.deepEqual( + await client.zPopMaxCount('key', 1), + [] + ); + }); +}); diff --git a/lib/commands/ZPOPMAX_COUNT.ts b/lib/commands/ZPOPMAX_COUNT.ts new file mode 100644 index 00000000000..abfa8494ec4 --- /dev/null +++ b/lib/commands/ZPOPMAX_COUNT.ts @@ -0,0 +1,13 @@ +import { transformReplySortedSetWithScores } from './generic-transformers'; +import { transformArguments as transformZPopMaxArguments } from './ZPOPMAX'; + +export { FIRST_KEY_INDEX } from './ZPOPMAX'; + +export function transformArguments(key: string, count: number): Array { + return [ + ...transformZPopMaxArguments(key), + count.toString() + ]; +} + +export const transformReply = transformReplySortedSetWithScores; diff --git a/lib/commands/ZPOPMIN.spec.ts b/lib/commands/ZPOPMIN.spec.ts new file mode 100644 index 00000000000..c69ca7c27f7 --- /dev/null +++ b/lib/commands/ZPOPMIN.spec.ts @@ -0,0 +1,41 @@ +import { strict as assert } from 'assert'; +import { TestRedisServers, itWithClient } from '../test-utils'; +import { transformArguments, transformReply } from './ZPOPMIN'; + +describe('ZPOPMIN', () => { + it('transformArguments', () => { + assert.deepEqual( + transformArguments('key'), + ['ZPOPMIN', 'key'] + ); + }); + + it('transformReply', () => { + assert.deepEqual( + transformReply(['value', '1']), + { + value: 'value', + score: 1 + } + ); + }); + + describe('client.zPopMin', () => { + itWithClient(TestRedisServers.OPEN, 'null', async client => { + assert.equal( + await client.zPopMin('key'), + null + ); + }); + + itWithClient(TestRedisServers.OPEN, 'member', async client => { + const member = { score: 1, value: 'value' }, + [, zPopMinReply] = await Promise.all([ + client.zAdd('key', member), + client.zPopMin('key') + ]); + + assert.deepEqual(zPopMinReply, member); + }); + }); +}); diff --git a/lib/commands/ZPOPMIN.ts b/lib/commands/ZPOPMIN.ts new file mode 100644 index 00000000000..9e1a7bdfa3d --- /dev/null +++ b/lib/commands/ZPOPMIN.ts @@ -0,0 +1,19 @@ +import { transformReplyNumberInfinity, ZMember } from './generic-transformers'; + +export const FIRST_KEY_INDEX = 1; + +export function transformArguments(key: string): Array { + return [ + 'ZPOPMIN', + key + ]; +} + +export function transformReply(reply: [string, string] | []): ZMember | null { + if (!reply.length) return null; + + return { + value: reply[0], + score: transformReplyNumberInfinity(reply[1]) + }; +} diff --git a/lib/commands/ZPOPMIN_COUNT.spec.ts b/lib/commands/ZPOPMIN_COUNT.spec.ts new file mode 100644 index 00000000000..1c2745a0fdf --- /dev/null +++ b/lib/commands/ZPOPMIN_COUNT.spec.ts @@ -0,0 +1,19 @@ +import { strict as assert } from 'assert'; +import { TestRedisServers, itWithClient } from '../test-utils'; +import { transformArguments } from './ZPOPMIN_COUNT'; + +describe('ZPOPMIN COUNT', () => { + it('transformArguments', () => { + assert.deepEqual( + transformArguments('key', 1), + ['ZPOPMIN', 'key', '1'] + ); + }); + + itWithClient(TestRedisServers.OPEN, 'client.zPopMinCount', async client => { + assert.deepEqual( + await client.zPopMinCount('key', 1), + [] + ); + }); +}); diff --git a/lib/commands/ZPOPMIN_COUNT.ts b/lib/commands/ZPOPMIN_COUNT.ts new file mode 100644 index 00000000000..e313b32dc8a --- /dev/null +++ b/lib/commands/ZPOPMIN_COUNT.ts @@ -0,0 +1,13 @@ +import { transformReplySortedSetWithScores } from './generic-transformers'; +import { transformArguments as transformZPopMinArguments } from './ZPOPMIN'; + +export { FIRST_KEY_INDEX } from './ZPOPMIN'; + +export function transformArguments(key: string, count: number): Array { + return [ + ...transformZPopMinArguments(key), + count.toString() + ]; +} + +export const transformReply = transformReplySortedSetWithScores; diff --git a/lib/commands/ZRANDMEMBER.spec.ts b/lib/commands/ZRANDMEMBER.spec.ts new file mode 100644 index 00000000000..da31641a18c --- /dev/null +++ b/lib/commands/ZRANDMEMBER.spec.ts @@ -0,0 +1,21 @@ +import { strict as assert } from 'assert'; +import { TestRedisServers, itWithClient, describeHandleMinimumRedisVersion } from '../test-utils'; +import { transformArguments } from './ZRANDMEMBER'; + +describe('ZRANDMEMBER', () => { + describeHandleMinimumRedisVersion([6, 2]); + + it('transformArguments', () => { + assert.deepEqual( + transformArguments('key'), + ['ZRANDMEMBER', 'key'] + ); + }); + + itWithClient(TestRedisServers.OPEN, 'client.zRandMember', async client => { + assert.equal( + await client.zRandMember('key'), + null + ); + }); +}); diff --git a/lib/commands/ZRANDMEMBER.ts b/lib/commands/ZRANDMEMBER.ts new file mode 100644 index 00000000000..27bb7cefa50 --- /dev/null +++ b/lib/commands/ZRANDMEMBER.ts @@ -0,0 +1,11 @@ +import { transformReplyStringNull } from './generic-transformers'; + +export const FIRST_KEY_INDEX = 1; + +export const IS_READ_ONLY = true; + +export function transformArguments(key: string): Array { + return ['ZRANDMEMBER', key]; +} + +export const transformReply = transformReplyStringNull; diff --git a/lib/commands/ZRANDMEMBER_COUNT.spec.ts b/lib/commands/ZRANDMEMBER_COUNT.spec.ts new file mode 100644 index 00000000000..4c873c82d90 --- /dev/null +++ b/lib/commands/ZRANDMEMBER_COUNT.spec.ts @@ -0,0 +1,21 @@ +import { strict as assert } from 'assert'; +import { TestRedisServers, itWithClient, describeHandleMinimumRedisVersion } from '../test-utils'; +import { transformArguments } from './ZRANDMEMBER_COUNT'; + +describe('ZRANDMEMBER COUNT', () => { + describeHandleMinimumRedisVersion([6, 2, 5]); + + it('transformArguments', () => { + assert.deepEqual( + transformArguments('key', 1), + ['ZRANDMEMBER', 'key', '1'] + ); + }); + + itWithClient(TestRedisServers.OPEN, 'client.zRandMemberCount', async client => { + assert.deepEqual( + await client.zRandMemberCount('key', 1), + [] + ); + }); +}); diff --git a/lib/commands/ZRANDMEMBER_COUNT.ts b/lib/commands/ZRANDMEMBER_COUNT.ts new file mode 100644 index 00000000000..f7eef456d05 --- /dev/null +++ b/lib/commands/ZRANDMEMBER_COUNT.ts @@ -0,0 +1,13 @@ +import { transformReplyStringArray } from './generic-transformers'; +import { transformArguments as transformZRandMemberArguments } from './ZRANDMEMBER'; + +export { FIRST_KEY_INDEX, IS_READ_ONLY } from './ZRANDMEMBER'; + +export function transformArguments(key: string, count: number): Array { + return [ + ...transformZRandMemberArguments(key), + count.toString() + ]; +} + +export const transformReply = transformReplyStringArray; diff --git a/lib/commands/ZRANDMEMBER_COUNT_WITHSCORES.spec.ts b/lib/commands/ZRANDMEMBER_COUNT_WITHSCORES.spec.ts new file mode 100644 index 00000000000..55624361fb6 --- /dev/null +++ b/lib/commands/ZRANDMEMBER_COUNT_WITHSCORES.spec.ts @@ -0,0 +1,21 @@ +import { strict as assert } from 'assert'; +import { TestRedisServers, itWithClient, describeHandleMinimumRedisVersion } from '../test-utils'; +import { transformArguments } from './ZRANDMEMBER_COUNT_WITHSCORES'; + +describe('ZRANDMEMBER COUNT WITHSCORES', () => { + describeHandleMinimumRedisVersion([6, 2, 5]); + + it('transformArguments', () => { + assert.deepEqual( + transformArguments('key', 1), + ['ZRANDMEMBER', 'key', '1', 'WITHSCORES'] + ); + }); + + itWithClient(TestRedisServers.OPEN, 'client.zRandMemberCountWithScores', async client => { + assert.deepEqual( + await client.zRandMemberCountWithScores('key', 1), + [] + ); + }); +}); diff --git a/lib/commands/ZRANDMEMBER_COUNT_WITHSCORES.ts b/lib/commands/ZRANDMEMBER_COUNT_WITHSCORES.ts new file mode 100644 index 00000000000..6d79f41c955 --- /dev/null +++ b/lib/commands/ZRANDMEMBER_COUNT_WITHSCORES.ts @@ -0,0 +1,13 @@ +import { transformReplySortedSetWithScores } from './generic-transformers'; +import { transformArguments as transformZRandMemberCountArguments } from './ZRANDMEMBER_COUNT'; + +export { FIRST_KEY_INDEX, IS_READ_ONLY } from './ZRANDMEMBER_COUNT'; + +export function transformArguments(...args: Parameters): Array { + return [ + ...transformZRandMemberCountArguments(...args), + 'WITHSCORES' + ]; +} + +export const transformReply = transformReplySortedSetWithScores; diff --git a/lib/commands/ZRANGE.spec.ts b/lib/commands/ZRANGE.spec.ts new file mode 100644 index 00000000000..72d83931ff4 --- /dev/null +++ b/lib/commands/ZRANGE.spec.ts @@ -0,0 +1,74 @@ +import { strict as assert } from 'assert'; +import { TestRedisServers, itWithClient } from '../test-utils'; +import { transformArguments } from './ZRANGE'; + +describe('ZRANGE', () => { + describe('transformArguments', () => { + it('simple', () => { + assert.deepEqual( + transformArguments('src', 0, 1), + ['ZRANGE', 'src', '0', '1'] + ); + }); + + it('with BYSCORE', () => { + assert.deepEqual( + transformArguments('src', 0, 1, { + BY: 'SCORE' + }), + ['ZRANGE', 'src', '0', '1', 'BYSCORE'] + ); + }); + + it('with BYLEX', () => { + assert.deepEqual( + transformArguments('src', 0, 1, { + BY: 'LEX' + }), + ['ZRANGE', 'src', '0', '1', 'BYLEX'] + ); + }); + + it('with REV', () => { + assert.deepEqual( + transformArguments('src', 0, 1, { + REV: true + }), + ['ZRANGE', 'src', '0', '1', 'REV'] + ); + }); + + it('with LIMIT', () => { + assert.deepEqual( + transformArguments('src', 0, 1, { + LIMIT: { + offset: 0, + count: 1 + } + }), + ['ZRANGE', 'src', '0', '1', 'LIMIT', '0', '1'] + ); + }); + + it('with BY & REV & LIMIT', () => { + assert.deepEqual( + transformArguments('src', 0, 1, { + BY: 'SCORE', + REV: true, + LIMIT: { + offset: 0, + count: 1 + } + }), + ['ZRANGE', 'src', '0', '1', 'BYSCORE', 'REV', 'LIMIT', '0', '1'] + ); + }); + }); + + itWithClient(TestRedisServers.OPEN, 'client.zRange', async client => { + assert.deepEqual( + await client.zRange('src', 0, 1), + [] + ); + }); +}); diff --git a/lib/commands/ZRANGE.ts b/lib/commands/ZRANGE.ts new file mode 100644 index 00000000000..9037210d69f --- /dev/null +++ b/lib/commands/ZRANGE.ts @@ -0,0 +1,45 @@ +import { transformArgumentNumberInfinity, transformReplyStringArray } from './generic-transformers'; + +export const FIRST_KEY_INDEX = 1; + +export const IS_READ_ONLY = true; + +interface ZRangeOptions { + BY?: 'SCORE' | 'LEX'; + REV?: true; + LIMIT?: { + offset: number; + count: number; + }; +} + +export function transformArguments(key: string, min: string | number, max: string | number, options?: ZRangeOptions): Array { + const args = [ + 'ZRANGE', + key, + typeof min === 'string' ? min : transformArgumentNumberInfinity(min), + typeof max === 'string' ? max : transformArgumentNumberInfinity(max) + ]; + + switch (options?.BY) { + case 'SCORE': + args.push('BYSCORE'); + break; + + case 'LEX': + args.push('BYLEX'); + break; + } + + if (options?.REV) { + args.push('REV'); + } + + if (options?.LIMIT) { + args.push('LIMIT', options.LIMIT.offset.toString(), options.LIMIT.count.toString()); + } + + return args; +} + +export const transformReply = transformReplyStringArray; diff --git a/lib/commands/ZRANGESTORE.spec.ts b/lib/commands/ZRANGESTORE.spec.ts new file mode 100644 index 00000000000..30dee7c0b5b --- /dev/null +++ b/lib/commands/ZRANGESTORE.spec.ts @@ -0,0 +1,82 @@ +import { strict as assert } from 'assert'; +import { TestRedisServers, itWithClient, describeHandleMinimumRedisVersion } from '../test-utils'; +import { transformArguments } from './ZRANGESTORE'; + +describe('ZRANGESTORE', () => { + describeHandleMinimumRedisVersion([6, 2]); + + describe('transformArguments', () => { + it('simple', () => { + assert.deepEqual( + transformArguments('dst', 'src', 0, 1), + ['ZRANGESTORE', 'dst', 'src', '0', '1'] + ); + }); + + it('with BYSCORE', () => { + assert.deepEqual( + transformArguments('dst', 'src', 0, 1, { + BY: 'SCORE' + }), + ['ZRANGESTORE', 'dst', 'src', '0', '1', 'BYSCORE'] + ); + }); + + it('with BYLEX', () => { + assert.deepEqual( + transformArguments('dst', 'src', 0, 1, { + BY: 'LEX' + }), + ['ZRANGESTORE', 'dst', 'src', '0', '1', 'BYLEX'] + ); + }); + + it('with REV', () => { + assert.deepEqual( + transformArguments('dst', 'src', 0, 1, { + REV: true + }), + ['ZRANGESTORE', 'dst', 'src', '0', '1', 'REV'] + ); + }); + + it('with LIMIT', () => { + assert.deepEqual( + transformArguments('dst', 'src', 0, 1, { + LIMIT: { + offset: 0, + count: 1 + } + }), + ['ZRANGESTORE', 'dst', 'src', '0', '1', 'LIMIT', '0', '1'] + ); + }); + + it('with BY & REV & LIMIT', () => { + assert.deepEqual( + transformArguments('dst', 'src', 0, 1, { + BY: 'SCORE', + REV: true, + LIMIT: { + offset: 0, + count: 1 + }, + WITHSCORES: true + }), + ['ZRANGESTORE', 'dst', 'src', '0', '1', 'BYSCORE', 'REV', 'LIMIT', '0', '1', 'WITHSCORES'] + ); + }); + }); + + itWithClient(TestRedisServers.OPEN, 'client.zRangeStore', async client => { + await client.zAdd('src', { + score: 0.5, + value: 'value' + }); + + assert.equal( + await client.zRangeStore('dst', 'src', 0, 1), + 1 + ); + }); +}); diff --git a/lib/commands/ZRANGESTORE.ts b/lib/commands/ZRANGESTORE.ts new file mode 100644 index 00000000000..6ad75661668 --- /dev/null +++ b/lib/commands/ZRANGESTORE.ts @@ -0,0 +1,55 @@ +import { transformArgumentNumberInfinity } from './generic-transformers'; + +export const FIRST_KEY_INDEX = 1; + +interface ZRangeStoreOptions { + BY?: 'SCORE' | 'LEX'; + REV?: true; + LIMIT?: { + offset: number; + count: number; + }; + WITHSCORES?: true; +} + +export function transformArguments(dst: string, src: string, min: number, max: number, options?: ZRangeStoreOptions): Array { + const args = [ + 'ZRANGESTORE', + dst, + src, + transformArgumentNumberInfinity(min), + transformArgumentNumberInfinity(max) + ]; + + switch (options?.BY) { + case 'SCORE': + args.push('BYSCORE'); + break; + + case 'LEX': + args.push('BYLEX'); + break; + } + + if (options?.REV) { + args.push('REV'); + } + + if (options?.LIMIT) { + args.push('LIMIT', options.LIMIT.offset.toString(), options.LIMIT.count.toString()); + } + + if (options?.WITHSCORES) { + args.push('WITHSCORES'); + } + + return args; +} + +export function transformReply(reply: number): number { + if (typeof reply !== 'number') { + throw new TypeError(`Upgrade to Redis 6.2.5 and up (https://github.com/redis/redis/pull/9089)`); + } + + return reply; +} diff --git a/lib/commands/ZRANGE_WITHSCORES.spec.ts b/lib/commands/ZRANGE_WITHSCORES.spec.ts new file mode 100644 index 00000000000..4c739b3d3b3 --- /dev/null +++ b/lib/commands/ZRANGE_WITHSCORES.spec.ts @@ -0,0 +1,65 @@ +import { strict as assert } from 'assert'; +import { TestRedisServers, itWithClient } from '../test-utils'; +import { transformArguments } from './ZRANGE_WITHSCORES'; + +describe('ZRANGE WITHSCORES', () => { + describe('transformArguments', () => { + it('simple', () => { + assert.deepEqual( + transformArguments('src', 0, 1), + ['ZRANGE', 'src', '0', '1', 'WITHSCORES'] + ); + }); + + it('with BY', () => { + assert.deepEqual( + transformArguments('src', 0, 1, { + BY: 'SCORE' + }), + ['ZRANGE', 'src', '0', '1', 'BYSCORE', 'WITHSCORES'] + ); + }); + + it('with REV', () => { + assert.deepEqual( + transformArguments('src', 0, 1, { + REV: true + }), + ['ZRANGE', 'src', '0', '1', 'REV', 'WITHSCORES'] + ); + }); + + it('with LIMIT', () => { + assert.deepEqual( + transformArguments('src', 0, 1, { + LIMIT: { + offset: 0, + count: 1 + } + }), + ['ZRANGE', 'src', '0', '1', 'LIMIT', '0', '1', 'WITHSCORES'] + ); + }); + + it('with BY & REV & LIMIT', () => { + assert.deepEqual( + transformArguments('src', 0, 1, { + BY: 'SCORE', + REV: true, + LIMIT: { + offset: 0, + count: 1 + } + }), + ['ZRANGE', 'src', '0', '1', 'BYSCORE', 'REV', 'LIMIT', '0', '1', 'WITHSCORES'] + ); + }); + }); + + itWithClient(TestRedisServers.OPEN, 'client.zRangeWithScores', async client => { + assert.deepEqual( + await client.zRangeWithScores('src', 0, 1), + [] + ); + }); +}); diff --git a/lib/commands/ZRANGE_WITHSCORES.ts b/lib/commands/ZRANGE_WITHSCORES.ts new file mode 100644 index 00000000000..0f777132f27 --- /dev/null +++ b/lib/commands/ZRANGE_WITHSCORES.ts @@ -0,0 +1,13 @@ +import { transformReplySortedSetWithScores } from './generic-transformers'; +import { transformArguments as transformZRangeArguments } from './ZRANGE'; + +export { FIRST_KEY_INDEX, IS_READ_ONLY } from './ZRANGE'; + +export function transformArguments(...args: Parameters): Array { + return [ + ...transformZRangeArguments(...args), + 'WITHSCORES' + ]; +} + +export const transformReply = transformReplySortedSetWithScores; diff --git a/lib/commands/ZRANK.spec.ts b/lib/commands/ZRANK.spec.ts new file mode 100644 index 00000000000..8dd9c924ccc --- /dev/null +++ b/lib/commands/ZRANK.spec.ts @@ -0,0 +1,19 @@ +import { strict as assert } from 'assert'; +import { TestRedisServers, itWithClient } from '../test-utils'; +import { transformArguments } from './ZRANK'; + +describe('ZRANK', () => { + it('transformArguments', () => { + assert.deepEqual( + transformArguments('key', 'member'), + ['ZRANK', 'key', 'member'] + ); + }); + + itWithClient(TestRedisServers.OPEN, 'client.zRank', async client => { + assert.equal( + await client.zRank('key', 'member'), + null + ); + }); +}); diff --git a/lib/commands/ZRANK.ts b/lib/commands/ZRANK.ts new file mode 100644 index 00000000000..84f9c7d4a9e --- /dev/null +++ b/lib/commands/ZRANK.ts @@ -0,0 +1,11 @@ +import { transformReplyNumberNull } from './generic-transformers'; + +export const FIRST_KEY_INDEX = 1; + +export const IS_READ_ONLY = true; + +export function transformArguments(key: string, member: string): Array { + return ['ZRANK', key, member]; +} + +export const transformReply = transformReplyNumberNull; diff --git a/lib/commands/ZREM.spec.ts b/lib/commands/ZREM.spec.ts new file mode 100644 index 00000000000..d613832035d --- /dev/null +++ b/lib/commands/ZREM.spec.ts @@ -0,0 +1,28 @@ +import { strict as assert } from 'assert'; +import { TestRedisServers, itWithClient } from '../test-utils'; +import { transformArguments } from './ZREM'; + +describe('ZREM', () => { + describe('transformArguments', () => { + it('string', () => { + assert.deepEqual( + transformArguments('key', 'member'), + ['ZREM', 'key', 'member'] + ); + }); + + it('array', () => { + assert.deepEqual( + transformArguments('key', ['1', '2']), + ['ZREM', 'key', '1', '2'] + ); + }); + }); + + itWithClient(TestRedisServers.OPEN, 'client.zRem', async client => { + assert.equal( + await client.zRem('key', 'member'), + 0 + ); + }); +}); diff --git a/lib/commands/ZREM.ts b/lib/commands/ZREM.ts new file mode 100644 index 00000000000..089b6136afd --- /dev/null +++ b/lib/commands/ZREM.ts @@ -0,0 +1,9 @@ +import { pushVerdictArguments, transformReplyNumber } from './generic-transformers'; + +export const FIRST_KEY_INDEX = 1; + +export function transformArguments(key: string, member: string | Array): Array { + return pushVerdictArguments(['ZREM', key], member); +} + +export const transformReply = transformReplyNumber; diff --git a/lib/commands/ZREMRANGEBYLEX.spec.ts b/lib/commands/ZREMRANGEBYLEX.spec.ts new file mode 100644 index 00000000000..7aae059480c --- /dev/null +++ b/lib/commands/ZREMRANGEBYLEX.spec.ts @@ -0,0 +1,19 @@ +import { strict as assert } from 'assert'; +import { TestRedisServers, itWithClient } from '../test-utils'; +import { transformArguments } from './ZREMRANGEBYLEX'; + +describe('ZREMRANGEBYLEX', () => { + it('transformArguments', () => { + assert.deepEqual( + transformArguments('key', '[a', '[b'), + ['ZREMRANGEBYLEX', 'key', '[a', '[b'] + ); + }); + + itWithClient(TestRedisServers.OPEN, 'client.zRemRangeByLex', async client => { + assert.equal( + await client.zRemRangeByLex('key', '[a', '[b'), + 0 + ); + }); +}); diff --git a/lib/commands/ZREMRANGEBYLEX.ts b/lib/commands/ZREMRANGEBYLEX.ts new file mode 100644 index 00000000000..aaf92992f98 --- /dev/null +++ b/lib/commands/ZREMRANGEBYLEX.ts @@ -0,0 +1,9 @@ +import { transformReplyNumber } from './generic-transformers'; + +export const FIRST_KEY_INDEX = 1; + +export function transformArguments(key: string, min: string, max: string): Array { + return ['ZREMRANGEBYLEX', key, min, max]; +} + +export const transformReply = transformReplyNumber; diff --git a/lib/commands/ZREMRANGEBYRANK.spec.ts b/lib/commands/ZREMRANGEBYRANK.spec.ts new file mode 100644 index 00000000000..401b57c8e2a --- /dev/null +++ b/lib/commands/ZREMRANGEBYRANK.spec.ts @@ -0,0 +1,19 @@ +import { strict as assert } from 'assert'; +import { TestRedisServers, itWithClient } from '../test-utils'; +import { transformArguments } from './ZREMRANGEBYRANK'; + +describe('ZREMRANGEBYRANK', () => { + it('transformArguments', () => { + assert.deepEqual( + transformArguments('key', 0, 1), + ['ZREMRANGEBYRANK', 'key', '0', '1'] + ); + }); + + itWithClient(TestRedisServers.OPEN, 'client.zRemRangeByRank', async client => { + assert.equal( + await client.zRemRangeByRank('key', 0, 1), + 0 + ); + }); +}); diff --git a/lib/commands/ZREMRANGEBYRANK.ts b/lib/commands/ZREMRANGEBYRANK.ts new file mode 100644 index 00000000000..89bf63d8e34 --- /dev/null +++ b/lib/commands/ZREMRANGEBYRANK.ts @@ -0,0 +1,9 @@ +import { transformReplyNumber } from './generic-transformers'; + +export const FIRST_KEY_INDEX = 1; + +export function transformArguments(key: string, start: number, stop: number): Array { + return ['ZREMRANGEBYRANK', key, start.toString(), stop.toString()]; +} + +export const transformReply = transformReplyNumber; diff --git a/lib/commands/ZREMRANGEBYSCORE.spec.ts b/lib/commands/ZREMRANGEBYSCORE.spec.ts new file mode 100644 index 00000000000..141392e772b --- /dev/null +++ b/lib/commands/ZREMRANGEBYSCORE.spec.ts @@ -0,0 +1,19 @@ +import { strict as assert } from 'assert'; +import { TestRedisServers, itWithClient } from '../test-utils'; +import { transformArguments } from './ZREMRANGEBYSCORE'; + +describe('ZREMRANGEBYSCORE', () => { + it('transformArguments', () => { + assert.deepEqual( + transformArguments('key', 0, 1), + ['ZREMRANGEBYSCORE', 'key', '0', '1'] + ); + }); + + itWithClient(TestRedisServers.OPEN, 'client.zRemRangeByScore', async client => { + assert.equal( + await client.zRemRangeByScore('key', 0, 1), + 0 + ); + }); +}); diff --git a/lib/commands/ZREMRANGEBYSCORE.ts b/lib/commands/ZREMRANGEBYSCORE.ts new file mode 100644 index 00000000000..64d14a4eb41 --- /dev/null +++ b/lib/commands/ZREMRANGEBYSCORE.ts @@ -0,0 +1,9 @@ +import { transformReplyNumber } from './generic-transformers'; + +export const FIRST_KEY_INDEX = 1; + +export function transformArguments(key: string, min: number, max: number): Array { + return ['ZREMRANGEBYSCORE', key, min.toString(), max.toString()]; +} + +export const transformReply = transformReplyNumber; diff --git a/lib/commands/ZREVRANK.spec.ts b/lib/commands/ZREVRANK.spec.ts new file mode 100644 index 00000000000..727a61a35a6 --- /dev/null +++ b/lib/commands/ZREVRANK.spec.ts @@ -0,0 +1,19 @@ +import { strict as assert } from 'assert'; +import { TestRedisServers, itWithClient } from '../test-utils'; +import { transformArguments } from './ZREVRANK'; + +describe('ZREVRANK', () => { + it('transformArguments', () => { + assert.deepEqual( + transformArguments('key', 'member'), + ['ZREVRANK', 'key', 'member'] + ); + }); + + itWithClient(TestRedisServers.OPEN, 'client.zRevRank', async client => { + assert.equal( + await client.zRevRank('key', 'member'), + null + ); + }); +}); diff --git a/lib/commands/ZREVRANK.ts b/lib/commands/ZREVRANK.ts new file mode 100644 index 00000000000..7d4c4ce2ab5 --- /dev/null +++ b/lib/commands/ZREVRANK.ts @@ -0,0 +1,11 @@ +import { transformReplyNumberNull } from './generic-transformers'; + +export const FIRST_KEY_INDEX = 1; + +export const IS_READ_ONLY = true; + +export function transformArguments(key: string, member: string): Array { + return ['ZREVRANK', key, member]; +} + +export const transformReply = transformReplyNumberNull; diff --git a/lib/commands/ZSCAN.spec.ts b/lib/commands/ZSCAN.spec.ts new file mode 100644 index 00000000000..3ff0c0a52b2 --- /dev/null +++ b/lib/commands/ZSCAN.spec.ts @@ -0,0 +1,77 @@ +import { strict as assert } from 'assert'; +import { TestRedisServers, itWithClient } from '../test-utils'; +import { transformArguments, transformReply } from './ZSCAN'; + +describe('ZSCAN', () => { + describe('transformArguments', () => { + it('cusror only', () => { + assert.deepEqual( + transformArguments('key', 0), + ['ZSCAN', 'key', '0'] + ); + }); + + it('with MATCH', () => { + assert.deepEqual( + transformArguments('key', 0, { + MATCH: 'pattern' + }), + ['ZSCAN', 'key', '0', 'MATCH', 'pattern'] + ); + }); + + it('with COUNT', () => { + assert.deepEqual( + transformArguments('key', 0, { + COUNT: 1 + }), + ['ZSCAN', 'key', '0', 'COUNT', '1'] + ); + }); + + it('with MATCH & COUNT', () => { + assert.deepEqual( + transformArguments('key', 0, { + MATCH: 'pattern', + COUNT: 1 + }), + ['ZSCAN', 'key', '0', 'MATCH', 'pattern', 'COUNT', '1'] + ); + }); + }); + + describe('transformReply', () => { + it('without members', () => { + assert.deepEqual( + transformReply(['0', []]), + { + cursor: 0, + members: [] + } + ); + }); + + it('with members', () => { + assert.deepEqual( + transformReply(['0', ['member', '-inf']]), + { + cursor: 0, + members: [{ + value: 'member', + score: -Infinity + }] + } + ); + }); + }); + + itWithClient(TestRedisServers.OPEN, 'client.zScan', async client => { + assert.deepEqual( + await client.zScan('key', 0), + { + cursor: 0, + members: [] + } + ); + }); +}); diff --git a/lib/commands/ZSCAN.ts b/lib/commands/ZSCAN.ts new file mode 100644 index 00000000000..79e5464db6e --- /dev/null +++ b/lib/commands/ZSCAN.ts @@ -0,0 +1,32 @@ +import { ScanOptions, transformReplyNumberInfinity, pushScanArguments, ZMember } from './generic-transformers'; + +export const FIRST_KEY_INDEX = 1; + +export const IS_READ_ONLY = true; + +export function transformArguments(key: string, cursor: number, options?: ScanOptions): Array { + return pushScanArguments([ + 'ZSCAN', + key + ], cursor, options); +} + +interface ZScanReply { + cursor: number; + members: Array; +} + +export function transformReply([cursor, rawMembers]: [string, Array]): ZScanReply { + const parsedMembers: Array = []; + for (let i = 0; i < rawMembers.length; i += 2) { + parsedMembers.push({ + value: rawMembers[i], + score: transformReplyNumberInfinity(rawMembers[i + 1]) + }); + } + + return { + cursor: Number(cursor), + members: parsedMembers + }; +} diff --git a/lib/commands/ZSCORE.spec.ts b/lib/commands/ZSCORE.spec.ts new file mode 100644 index 00000000000..d346a2e2c81 --- /dev/null +++ b/lib/commands/ZSCORE.spec.ts @@ -0,0 +1,19 @@ +import { strict as assert } from 'assert'; +import { TestRedisServers, itWithClient } from '../test-utils'; +import { transformArguments } from './ZSCORE'; + +describe('ZSCORE', () => { + it('transformArguments', () => { + assert.deepEqual( + transformArguments('key', 'member'), + ['ZSCORE', 'key', 'member'] + ); + }); + + itWithClient(TestRedisServers.OPEN, 'client.zScore', async client => { + assert.equal( + await client.zScore('key', 'member'), + null + ); + }); +}); diff --git a/lib/commands/ZSCORE.ts b/lib/commands/ZSCORE.ts new file mode 100644 index 00000000000..18b664a3217 --- /dev/null +++ b/lib/commands/ZSCORE.ts @@ -0,0 +1,11 @@ +import { transformReplyNumberInfinityNull } from './generic-transformers'; + +export const FIRST_KEY_INDEX = 1; + +export const IS_READ_ONLY = true; + +export function transformArguments(key: string, member: string): Array { + return ['ZSCORE', key, member]; +} + +export const transformReply = transformReplyNumberInfinityNull; diff --git a/lib/commands/ZUNION.spec.ts b/lib/commands/ZUNION.spec.ts new file mode 100644 index 00000000000..12e92833931 --- /dev/null +++ b/lib/commands/ZUNION.spec.ts @@ -0,0 +1,48 @@ +import { strict as assert } from 'assert'; +import { TestRedisServers, itWithClient, describeHandleMinimumRedisVersion } from '../test-utils'; +import { transformArguments } from './ZUNION'; + +describe('ZUNION', () => { + describeHandleMinimumRedisVersion([6, 2]); + + describe('transformArguments', () => { + it('key (string)', () => { + assert.deepEqual( + transformArguments('key'), + ['ZUNION', '1', 'key'] + ); + }); + + it('keys (array)', () => { + assert.deepEqual( + transformArguments(['1', '2']), + ['ZUNION', '2', '1', '2'] + ); + }); + + it('with WEIGHTS', () => { + assert.deepEqual( + transformArguments('key', { + WEIGHTS: [1] + }), + ['ZUNION', '1', 'key', 'WEIGHTS', '1'] + ); + }); + + it('with AGGREGATE', () => { + assert.deepEqual( + transformArguments('key', { + AGGREGATE: 'SUM' + }), + ['ZUNION', '1', 'key', 'AGGREGATE', 'SUM'] + ); + }); + }); + + itWithClient(TestRedisServers.OPEN, 'client.zUnion', async client => { + assert.deepEqual( + await client.zUnion('key'), + [] + ); + }); +}); diff --git a/lib/commands/ZUNION.ts b/lib/commands/ZUNION.ts new file mode 100644 index 00000000000..efdfccb1ff4 --- /dev/null +++ b/lib/commands/ZUNION.ts @@ -0,0 +1,26 @@ +import { pushVerdictArgument, transformReplyStringArray } from './generic-transformers'; + +export const FIRST_KEY_INDEX = 2; + +export const IS_READ_ONLY = true; + +interface ZUnionOptions { + WEIGHTS?: Array; + AGGREGATE?: 'SUM' | 'MIN' | 'MAX'; +} + +export function transformArguments(keys: Array | string, options?: ZUnionOptions): Array { + const args = pushVerdictArgument(['ZUNION'], keys); + + if (options?.WEIGHTS) { + args.push('WEIGHTS', ...options.WEIGHTS.map(weight => weight.toString())); + } + + if (options?.AGGREGATE) { + args.push('AGGREGATE', options.AGGREGATE); + } + + return args; +} + +export const transformReply = transformReplyStringArray; diff --git a/lib/commands/ZUNIONSTORE.spec.ts b/lib/commands/ZUNIONSTORE.spec.ts new file mode 100644 index 00000000000..0c4d7a3006b --- /dev/null +++ b/lib/commands/ZUNIONSTORE.spec.ts @@ -0,0 +1,56 @@ +import { strict as assert } from 'assert'; +import { TestRedisServers, itWithClient } from '../test-utils'; +import { transformArguments } from './ZUNIONSTORE'; + +describe('ZUNIONSTORE', () => { + describe('transformArguments', () => { + it('key (string)', () => { + assert.deepEqual( + transformArguments('destination', 'key'), + ['ZUNIONSTORE', 'destination', '1', 'key'] + ); + }); + + it('keys (array)', () => { + assert.deepEqual( + transformArguments('destination', ['1', '2']), + ['ZUNIONSTORE', 'destination', '2', '1', '2'] + ); + }); + + it('with WEIGHTS', () => { + assert.deepEqual( + transformArguments('destination', 'key', { + WEIGHTS: [1] + }), + ['ZUNIONSTORE', 'destination', '1', 'key', 'WEIGHTS', '1'] + ); + }); + + it('with AGGREGATE', () => { + assert.deepEqual( + transformArguments('destination', 'key', { + AGGREGATE: 'SUM' + }), + ['ZUNIONSTORE', 'destination', '1', 'key', 'AGGREGATE', 'SUM'] + ); + }); + + it('with WEIGHTS, AGGREGATE', () => { + assert.deepEqual( + transformArguments('destination', 'key', { + WEIGHTS: [1], + AGGREGATE: 'SUM' + }), + ['ZUNIONSTORE', 'destination', '1', 'key', 'WEIGHTS', '1', 'AGGREGATE', 'SUM'] + ); + }); + }); + + itWithClient(TestRedisServers.OPEN, 'client.zUnionStore', async client => { + assert.equal( + await client.zUnionStore('destination', 'key'), + 0 + ); + }); +}); diff --git a/lib/commands/ZUNIONSTORE.ts b/lib/commands/ZUNIONSTORE.ts new file mode 100644 index 00000000000..c03f1203706 --- /dev/null +++ b/lib/commands/ZUNIONSTORE.ts @@ -0,0 +1,24 @@ +import { pushVerdictArgument, transformReplyNumber } from './generic-transformers'; + +export const FIRST_KEY_INDEX = 1; + +interface ZUnionOptions { + WEIGHTS?: Array; + AGGREGATE?: 'SUM' | 'MIN' | 'MAX'; +} + +export function transformArguments(destination: string, keys: Array | string, options?: ZUnionOptions): Array { + const args = pushVerdictArgument(['ZUNIONSTORE', destination], keys); + + if (options?.WEIGHTS) { + args.push('WEIGHTS', ...options.WEIGHTS.map(weight => weight.toString())); + } + + if (options?.AGGREGATE) { + args.push('AGGREGATE', options.AGGREGATE); + } + + return args; +} + +export const transformReply = transformReplyNumber; diff --git a/lib/commands/ZUNION_WITHSCORES.spec.ts b/lib/commands/ZUNION_WITHSCORES.spec.ts new file mode 100644 index 00000000000..d9c65ba5e4b --- /dev/null +++ b/lib/commands/ZUNION_WITHSCORES.spec.ts @@ -0,0 +1,48 @@ +import { strict as assert } from 'assert'; +import { TestRedisServers, itWithClient, describeHandleMinimumRedisVersion } from '../test-utils'; +import { transformArguments } from './ZUNION_WITHSCORES'; + +describe('ZUNION WITHSCORES', () => { + describeHandleMinimumRedisVersion([6, 2]); + + describe('transformArguments', () => { + it('key (string)', () => { + assert.deepEqual( + transformArguments('key'), + ['ZUNION', '1', 'key', 'WITHSCORES'] + ); + }); + + it('keys (array)', () => { + assert.deepEqual( + transformArguments(['1', '2']), + ['ZUNION', '2', '1', '2', 'WITHSCORES'] + ); + }); + + it('with WEIGHTS', () => { + assert.deepEqual( + transformArguments('key', { + WEIGHTS: [1] + }), + ['ZUNION', '1', 'key', 'WEIGHTS', '1', 'WITHSCORES'] + ); + }); + + it('with AGGREGATE', () => { + assert.deepEqual( + transformArguments('key', { + AGGREGATE: 'SUM' + }), + ['ZUNION', '1', 'key', 'AGGREGATE', 'SUM', 'WITHSCORES'] + ); + }); + }); + + itWithClient(TestRedisServers.OPEN, 'client.zUnionWithScores', async client => { + assert.deepEqual( + await client.zUnionWithScores('key'), + [] + ); + }); +}); diff --git a/lib/commands/ZUNION_WITHSCORES.ts b/lib/commands/ZUNION_WITHSCORES.ts new file mode 100644 index 00000000000..d0cef45cfb1 --- /dev/null +++ b/lib/commands/ZUNION_WITHSCORES.ts @@ -0,0 +1,13 @@ +import { transformReplySortedSetWithScores } from './generic-transformers'; +import { transformArguments as transformZUnionArguments } from './ZUNION'; + +export { FIRST_KEY_INDEX, IS_READ_ONLY } from './ZUNION'; + +export function transformArguments(...args: Parameters): Array { + return [ + ...transformZUnionArguments(...args), + 'WITHSCORES' + ]; +} + +export const transformReply = transformReplySortedSetWithScores; diff --git a/lib/commands/generic-transformers.spec.ts b/lib/commands/generic-transformers.spec.ts new file mode 100644 index 00000000000..5335255f910 --- /dev/null +++ b/lib/commands/generic-transformers.spec.ts @@ -0,0 +1,623 @@ +import { strict as assert } from 'assert'; +import { isObject } from 'util'; +import { + transformReplyBoolean, + transformReplyBooleanArray, + pushScanArguments, + transformReplyNumberInfinity, + transformReplyNumberInfinityArray, + transformReplyNumberInfinityNull, + transformArgumentNumberInfinity, + transformReplyTuples, + transformReplyStreamMessages, + transformReplyStreamsMessages, + transformReplySortedSetWithScores, + pushGeoCountArgument, + pushGeoSearchArguments, + GeoReplyWith, + transformGeoMembersWithReply, + transformEXAT, + transformPXAT, + pushEvalArguments, + pushStringTuplesArguments, + pushVerdictArguments, + pushVerdictArgument, + pushOptionalVerdictArgument +} from './generic-transformers'; + +describe('Generic Transformers', () => { + describe('transformReplyBoolean', () => { + it('0', () => { + assert.equal( + transformReplyBoolean(0), + false + ); + }); + + it('1', () => { + assert.equal( + transformReplyBoolean(1), + true + ); + }); + }); + + describe('transformReplyBooleanArray', () => { + it('empty array', () => { + assert.deepEqual( + transformReplyBooleanArray([]), + [] + ); + }); + + it('0, 1', () => { + assert.deepEqual( + transformReplyBooleanArray([0, 1]), + [false, true] + ); + }); + }); + + describe('pushScanArguments', () => { + it('cusror only', () => { + assert.deepEqual( + pushScanArguments([], 0), + ['0'] + ); + }); + + it('with MATCH', () => { + assert.deepEqual( + pushScanArguments([], 0, { + MATCH: 'pattern' + }), + ['0', 'MATCH', 'pattern'] + ); + }); + + it('with COUNT', () => { + assert.deepEqual( + pushScanArguments([], 0, { + COUNT: 1 + }), + ['0', 'COUNT', '1'] + ); + }); + + it('with MATCH & COUNT', () => { + assert.deepEqual( + pushScanArguments([], 0, { + MATCH: 'pattern', + COUNT: 1 + }), + ['0', 'MATCH', 'pattern', 'COUNT', '1'] + ); + }); + }); + + describe('transformReplyNumberInfinity', () => { + it('0.5', () => { + assert.equal( + transformReplyNumberInfinity('0.5'), + 0.5 + ); + }); + + it('+inf', () => { + assert.equal( + transformReplyNumberInfinity('+inf'), + Infinity + ); + }); + + it('-inf', () => { + assert.equal( + transformReplyNumberInfinity('-inf'), + -Infinity + ); + }); + }); + + describe('transformReplyNumberInfinityArray', () => { + it('empty array', () => { + assert.deepEqual( + transformReplyNumberInfinityArray([]), + [] + ); + }); + + it('0.5, +inf, -inf', () => { + assert.deepEqual( + transformReplyNumberInfinityArray(['0.5', '+inf', '-inf']), + [0.5, Infinity, -Infinity] + ); + }); + }); + + describe('transformReplyNumberInfinityNull', () => { + it('null', () => { + assert.equal( + transformReplyNumberInfinityNull(null), + null + ); + }); + + it('1', () => { + assert.equal( + transformReplyNumberInfinityNull('1'), + 1 + ); + }); + }); + + describe('transformArgumentNumberInfinity', () => { + it('0.5', () => { + assert.equal( + transformArgumentNumberInfinity(0.5), + '0.5' + ); + }); + + it('Infinity', () => { + assert.equal( + transformArgumentNumberInfinity(Infinity), + '+inf' + ); + }); + + it('-Infinity', () => { + assert.equal( + transformArgumentNumberInfinity(-Infinity), + '-inf' + ); + }); + }); + + it('transformReplyTuples', () => { + assert.deepEqual( + transformReplyTuples(['key1', 'value1', 'key2', 'value2']), + Object.create(null, { + key1: { + value: 'value1', + configurable: true, + enumerable: true + }, + key2: { + value: 'value2', + configurable: true, + enumerable: true + } + }) + ); + }); + + it('transformReplyStreamMessages', () => { + assert.deepEqual( + transformReplyStreamMessages([['0-0', ['0key', '0value']], ['1-0', ['1key', '1value']]]), + [{ + id: '0-0', + message: Object.create(null, { + '0key': { + value: '0value', + configurable: true, + enumerable: true + } + }) + }, { + id: '1-0', + message: Object.create(null, { + '1key': { + value: '1value', + configurable: true, + enumerable: true + } + }) + }] + ); + }); + + describe('transformReplyStreamsMessages', () => { + it('null', () => { + assert.equal( + transformReplyStreamsMessages(null), + null + ); + }); + + it('with messages', () => { + assert.deepEqual( + transformReplyStreamsMessages([['stream1', [['0-1', ['11key', '11value']], ['1-1', ['12key', '12value']]]], ['stream2', [['0-2', ['2key1', '2value1', '2key2', '2value2']]]]]), + [{ + name: 'stream1', + messages: [{ + id: '0-1', + message: Object.create(null, { + '11key': { + value: '11value', + configurable: true, + enumerable: true + } + }) + }, { + id: '1-1', + message: Object.create(null, { + '12key': { + value: '12value', + configurable: true, + enumerable: true + } + }) + }] + }, { + name: 'stream2', + messages: [{ + id: '0-2', + message: Object.create(null, { + '2key1': { + value: '2value1', + configurable: true, + enumerable: true + }, + '2key2': { + value: '2value2', + configurable: true, + enumerable: true + } + }) + }] + }] + ) + }); + }); + + it('transformReplySortedSetWithScores', () => { + assert.deepEqual( + transformReplySortedSetWithScores(['member1', '0.5', 'member2', '+inf', 'member3', '-inf']), + [{ + value: 'member1', + score: 0.5 + }, { + value: 'member2', + score: Infinity + }, { + value: 'member3', + score: -Infinity + }] + ); + }); + + describe('pushGeoCountArgument', () => { + it('undefined', () => { + assert.deepEqual( + pushGeoCountArgument([], undefined), + [] + ); + }); + + it('number', () => { + assert.deepEqual( + pushGeoCountArgument([], 1), + ['COUNT', '1'] + ); + }); + + it('with ANY', () => { + assert.deepEqual( + pushGeoCountArgument([], { + value: 1, + ANY: true + }), + ['COUNT', '1', 'ANY'] + ); + }); + }); + + describe('pushGeoSearchArguments', () => { + it('FROMMEMBER, BYRADIUS', () => { + assert.deepEqual( + pushGeoSearchArguments([], 'key', 'member', { + radius: 1, + unit: 'm' + }), + ['key', 'FROMMEMBER', 'member', 'BYRADIUS', '1', 'm'] + ); + }); + + it('FROMLONLAT, BYBOX', () => { + assert.deepEqual( + pushGeoSearchArguments([], 'key', { + longitude: 1, + latitude: 2 + }, { + width: 1, + height: 2, + unit: 'm' + }), + ['key', 'FROMLONLAT', '1', '2', 'BYBOX', '1', '2', 'm'] + ); + }); + + it('with SORT', () => { + assert.deepEqual( + pushGeoSearchArguments([], 'key', 'member', { + radius: 1, + unit: 'm' + }, { + SORT: 'ASC' + }), + ['key', 'FROMMEMBER', 'member', 'BYRADIUS', '1', 'm', 'ASC'] + ); + }); + }); + + describe('transformGeoMembersWithReply', () => { + it('DISTANCE', () => { + assert.deepEqual( + transformGeoMembersWithReply([ + [ + '1', + '2' + ], + [ + '3', + '4' + ] + ], [GeoReplyWith.DISTANCE]), + [{ + member: '1', + distance: '2' + }, { + member: '3', + distance: '4' + }] + ); + }); + + it('HASH', () => { + assert.deepEqual( + transformGeoMembersWithReply([ + [ + '1', + 2 + ], + [ + '3', + 4 + ] + ], [GeoReplyWith.HASH]), + [{ + member: '1', + hash: 2 + }, { + member: '3', + hash: 4 + }] + ); + }); + + it('COORDINATES', () => { + assert.deepEqual( + transformGeoMembersWithReply([ + [ + '1', + [ + '2', + '3' + ] + ], + [ + '4', + [ + '5', + '6' + ] + ] + ], [GeoReplyWith.COORDINATES]), + [{ + member: '1', + coordinates: { + longitude: '2', + latitude: '3' + } + }, { + member: '4', + coordinates: { + longitude: '5', + latitude: '6' + } + }] + ); + }); + + it('DISTANCE, HASH, COORDINATES', () => { + assert.deepEqual( + transformGeoMembersWithReply([ + [ + '1', + '2', + 3, + [ + '4', + '5' + ] + ], + [ + '6', + '7', + 8, + [ + '9', + '10' + ] + ] + ], [GeoReplyWith.DISTANCE, GeoReplyWith.HASH, GeoReplyWith.COORDINATES]), + [{ + member: '1', + distance: '2', + hash: 3, + coordinates: { + longitude: '4', + latitude: '5' + } + }, { + member: '6', + distance: '7', + hash: 8, + coordinates: { + longitude: '9', + latitude: '10' + } + }] + ); + }); + }); + + describe('transformEXAT', () => { + it('number', () => { + assert.equal( + transformEXAT(1), + '1' + ); + }); + + it('date', () => { + const d = new Date(); + assert.equal( + transformEXAT(d), + Math.floor(d.getTime() / 1000).toString() + ); + }); + }); + + describe('transformPXAT', () => { + it('number', () => { + assert.equal( + transformPXAT(1), + '1' + ); + }); + + it('date', () => { + const d = new Date(); + assert.equal( + transformPXAT(d), + d.getTime().toString() + ); + }); + }); + + describe('pushEvalArguments', () => { + it('empty', () => { + assert.deepEqual( + pushEvalArguments([]), + ['0'] + ); + }); + + it('with keys', () => { + assert.deepEqual( + pushEvalArguments([], { + keys: ['key'] + }), + ['1', 'key'] + ); + }); + + it('with arguments', () => { + assert.deepEqual( + pushEvalArguments([], { + arguments: ['argument'] + }), + ['0', 'argument'] + ); + }); + + it('with keys and arguments', () => { + assert.deepEqual( + pushEvalArguments([], { + keys: ['key'], + arguments: ['argument'] + }), + ['1', 'key', 'argument'] + ); + }); + }); + + describe('pushStringTuplesArguments', () => { + it("['key1', 'value1', 'key2', 'value2']", () => { + assert.deepEqual( + pushStringTuplesArguments([], ['key1', 'value1', 'key2', 'value2']), + ['key1', 'value1', 'key2', 'value2'] + ); + }); + + it("[['key1', 'value1'], ['key2', 'value2']]", () => { + assert.deepEqual( + pushStringTuplesArguments([], [['key1', 'value1'], ['key2', 'value2']]), + ['key1', 'value1', 'key2', 'value2'] + ); + }); + + it("{key1: 'value1'. key2: 'value2'}", () => { + assert.deepEqual( + pushStringTuplesArguments([], { key1: 'value1', key2: 'value2' }), + ['key1', 'value1', 'key2', 'value2'] + ); + }); + }); + + describe('pushVerdictArguments', () => { + it('string', () => { + assert.deepEqual( + pushVerdictArguments([], 'string'), + ['string'] + ); + }); + + it('array', () => { + assert.deepEqual( + pushVerdictArguments([], ['1', '2']), + ['1', '2'] + ); + }); + }); + + describe('pushVerdictArgument', () => { + it('string', () => { + assert.deepEqual( + pushVerdictArgument([], 'string'), + ['1', 'string'] + ); + }); + + it('array', () => { + assert.deepEqual( + pushVerdictArgument([], ['1', '2']), + ['2', '1', '2'] + ); + }); + }); + + describe('pushOptionalVerdictArgument', () => { + it('undefined', () => { + assert.deepEqual( + pushOptionalVerdictArgument([], 'name', undefined), + [] + ); + }); + + it('string', () => { + assert.deepEqual( + pushOptionalVerdictArgument([], 'name', 'string'), + ['name', '1', 'string'] + ); + }); + + it('array', () => { + assert.deepEqual( + pushOptionalVerdictArgument([], 'name', ['1', '2']), + ['name', '2', '1', '2'] + ); + }); + }); +}); diff --git a/lib/commands/generic-transformers.ts b/lib/commands/generic-transformers.ts new file mode 100644 index 00000000000..8105bfe903f --- /dev/null +++ b/lib/commands/generic-transformers.ts @@ -0,0 +1,381 @@ +import { TransformArgumentsReply } from '.'; + +export function transformReplyNumber(reply: number): number { + return reply; +} + +export function transformReplyNumberNull(reply: number | null): number | null { + return reply; +} + +export function transformReplyNumberArray(reply: Array): Array { + return reply; +} + +export function transformReplyNumberNullArray(reply: Array): Array { + return reply; +} + +export function transformReplyString(reply: string): string { + return reply; +} + +export function transformReplyStringNull(reply: string | null): string | null { + return reply; +} + +export function transformReplyStringArray(reply: Array): Array { + return reply; +} + +export function transformReplyStringArrayNull(reply: Array | null): Array | null { + return reply; +} + +export function transformReplyStringNullArray(reply: Array): Array { + return reply; +} + +export function transformReplyBoolean(reply: number): boolean { + return reply === 1; +} + +export function transformReplyBooleanArray(reply: Array): Array { + return reply.map(transformReplyBoolean); +} + +export type BitValue = 0 | 1; + +export function transformReplyBit(reply: BitValue): BitValue { + return reply; +} + +export function transformReplyVoid(): void {} + +export interface ScanOptions { + MATCH?: string; + COUNT?: number; +} + +export function pushScanArguments(args: Array, cursor: number, options?: ScanOptions): Array { + args.push(cursor.toString()); + + if (options?.MATCH) { + args.push('MATCH', options.MATCH); + } + + if (options?.COUNT) { + args.push('COUNT', options.COUNT.toString()); + } + + return args; +} + +export function transformReplyNumberInfinity(reply: string): number { + switch (reply) { + case '+inf': + return Infinity; + + case '-inf': + return -Infinity; + + default: + return Number(reply); + } +} + +export function transformReplyNumberInfinityArray(reply: Array): Array { + return reply.map(transformReplyNumberInfinity); +} + +export function transformReplyNumberInfinityNull(reply: string | null): number | null { + if (reply === null) return null; + + return transformReplyNumberInfinity(reply); +} + +export function transformReplyNumberInfinityNullArray(reply: Array): Array { + return reply.map(transformReplyNumberInfinityNull); +} + +export function transformArgumentNumberInfinity(num: number): string { + switch (num) { + case Infinity: + return '+inf'; + + case -Infinity: + return '-inf'; + + default: + return num.toString(); + } +} + +export interface TuplesObject { + [field: string]: string; +} + +export function transformReplyTuples(reply: Array): TuplesObject { + const message = Object.create(null); + + for (let i = 0; i < reply.length; i += 2) { + message[reply[i]] = reply[i + 1]; + } + + return message; +} + +export interface StreamMessageReply { + id: string; + message: TuplesObject; +} + +export type StreamMessagesReply = Array; + +export function transformReplyStreamMessages(reply: Array): StreamMessagesReply { + const messages = []; + + for (const [id, message] of reply) { + messages.push({ + id, + message: transformReplyTuples(message) + }); + } + + return messages; +} + +export type StreamsMessagesReply = Array<{ + name: string; + messages: StreamMessagesReply; +}> | null; + +export function transformReplyStreamsMessages(reply: Array | null): StreamsMessagesReply | null { + if (reply === null) return null; + + return reply.map(([name, rawMessages]) => ({ + name, + messages: transformReplyStreamMessages(rawMessages) + })); +} + +export interface ZMember { + score: number; + value: string; +} + +export function transformReplySortedSetWithScores(reply: Array): Array { + const members = []; + + for (let i = 0; i < reply.length; i += 2) { + members.push({ + value: reply[i], + score: transformReplyNumberInfinity(reply[i + 1]) + }); + } + + return members; +} + +type GeoCountArgument = number | { + value: number; + ANY?: true +}; + +export function pushGeoCountArgument(args: Array, count: GeoCountArgument | undefined): Array { + if (typeof count === 'number') { + args.push('COUNT', count.toString()); + } else if (count) { + args.push('COUNT', count.value.toString()); + + if (count.ANY) { + args.push('ANY'); + } + } + + return args; +} + +export type GeoUnits = 'm' | 'km' | 'mi' | 'ft'; + +export interface GeoCoordinates { + longitude: string | number; + latitude: string | number; +} + +type GeoSearchFromMember = string; + +export type GeoSearchFrom = GeoSearchFromMember | GeoCoordinates; + +interface GeoSearchByRadius { + radius: number; + unit: GeoUnits; +} + +interface GeoSearchByBox { + width: number; + height: number; + unit: GeoUnits; +} + +export type GeoSearchBy = GeoSearchByRadius | GeoSearchByBox; + +export interface GeoSearchOptions { + SORT?: 'ASC' | 'DESC'; + COUNT?: GeoCountArgument; +} + +export function pushGeoSearchArguments( + args: Array, + key: string, + from: GeoSearchFrom, + by: GeoSearchBy, + options?: GeoSearchOptions +): Array { + args.push(key); + + if (typeof from === 'string') { + args.push('FROMMEMBER', from); + } else { + args.push('FROMLONLAT', from.longitude.toString(), from.latitude.toString()); + } + + if ('radius' in by) { + args.push('BYRADIUS', by.radius.toString()); + } else { + args.push('BYBOX', by.width.toString(), by.height.toString()); + } + + if (by.unit) { + args.push(by.unit); + } + + if (options?.SORT) { + args.push(options?.SORT); + } + + pushGeoCountArgument(args, options?.COUNT); + + return args; +} + +export enum GeoReplyWith { + DISTANCE = 'WITHDIST', + HASH = 'WITHHASH', + COORDINATES = 'WITHCOORD' +} + +export interface GeoReplyWithMember { + member: string; + distance?: number; + hash?: string; + coordinates?: { + longitude: string; + latitude: string; + }; +} + +export function transformGeoMembersWithReply(reply: Array>, replyWith: Array): Array { + const replyWithSet = new Set(replyWith); + + let index = 0; + const distanceIndex = replyWithSet.has(GeoReplyWith.DISTANCE) && ++index, + hashIndex = replyWithSet.has(GeoReplyWith.HASH) && ++index, + coordinatesIndex = replyWithSet.has(GeoReplyWith.COORDINATES) && ++index; + + return reply.map(member => { + const transformedMember: GeoReplyWithMember = { + member: member[0] + }; + + if (distanceIndex) { + transformedMember.distance = member[distanceIndex]; + } + + if (hashIndex) { + transformedMember.hash = member[hashIndex]; + } + + if (coordinatesIndex) { + const [longitude, latitude] = member[coordinatesIndex]; + transformedMember.coordinates = { + longitude, + latitude + }; + } + + return transformedMember; + }); +} + +export function transformEXAT(EXAT: number | Date): string { + return (typeof EXAT === 'number' ? EXAT : Math.floor(EXAT.getTime() / 1000)).toString(); +} + +export function transformPXAT(PXAT: number | Date): string { + return (typeof PXAT === 'number' ? PXAT : PXAT.getTime()).toString(); +} + +export interface EvalOptions { + keys?: Array; + arguments?: Array; +} + +export function pushEvalArguments(args: Array, options?: EvalOptions): Array { + if (options?.keys) { + args.push( + options.keys.length.toString(), + ...options.keys + ); + } else { + args.push('0'); + } + + if (options?.arguments) { + args.push(...options.arguments); + } + + return args; +} + +export type StringTuplesArguments = Array<[string, string]> | Array | Record; + +export function pushStringTuplesArguments(args: Array, tuples: StringTuplesArguments): Array { + if (Array.isArray(tuples)) { + args.push(...tuples.flat()); + } else { + for (const key of Object.keys(tuples)) { + args.push(key, tuples[key]); + } + } + + return args; +} + +export function pushVerdictArguments(args: TransformArgumentsReply, value: string | Array): TransformArgumentsReply { + if (typeof value === 'string') { + args.push(value); + } else { + args.push(...value); + } + + return args; +} + +export function pushVerdictArgument(args: TransformArgumentsReply, value: string | Array): TransformArgumentsReply { + if (typeof value === 'string') { + args.push('1', value); + } else { + args.push(value.length.toString(), ...value); + } + + return args; +} + +export function pushOptionalVerdictArgument(args: TransformArgumentsReply, name: string, value: undefined | string | Array): TransformArgumentsReply { + if (value === undefined) return args; + + args.push(name); + + return pushVerdictArgument(args, value); +} diff --git a/lib/commands/index.ts b/lib/commands/index.ts new file mode 100644 index 00000000000..cffb47c668a --- /dev/null +++ b/lib/commands/index.ts @@ -0,0 +1,755 @@ +import * as ACL_CAT from './ACL_CAT'; +import * as ACL_DELUSER from './ACL_DELUSER'; +import * as ACL_GENPASS from './ACL_GENPASS'; +import * as ACL_GETUSER from './ACL_GETUSER'; +import * as ACL_LIST from './ACL_LIST'; +import * as ACL_LOAD from './ACL_LOAD'; +import * as ACL_LOG_RESET from './ACL_LOG_RESET'; +import * as ACL_LOG from './ACL_LOG'; +import * as ACL_SAVE from './ACL_SAVE'; +import * as ACL_SETUSER from './ACL_SETUSER'; +import * as ACL_USERS from './ACL_USERS'; +import * as ACL_WHOAMI from './ACL_WHOAMI'; +import * as APPEND from './APPEND'; +import * as ASKING from './ASKING'; +import * as AUTH from './AUTH'; +import * as BGREWRITEAOF from './BGREWRITEAOF'; +import * as BGSAVE from './BGSAVE'; +import * as BITCOUNT from './BITCOUNT'; +import * as BITFIELD from './BITFIELD'; +import * as BITOP from './BITOP'; +import * as BITPOS from './BITPOS'; +import * as BLMOVE from './BLMOVE'; +import * as BLPOP from './BLPOP'; +import * as BRPOP from './BRPOP'; +import * as BRPOPLPUSH from './BRPOPLPUSH'; +import * as BZPOPMAX from './BZPOPMAX'; +import * as BZPOPMIN from './BZPOPMIN'; +import * as CLIENT_ID from './CLIENT_ID'; +import * as CLIENT_INFO from './CLIENT_INFO'; +import * as CLUSTER_ADDSLOTS from './CLUSTER_ADDSLOTS'; +import * as CLUSTER_FLUSHSLOTS from './CLUSTER_FLUSHSLOTS'; +import * as CLUSTER_INFO from './CLUSTER_INFO'; +import * as CLUSTER_NODES from './CLUSTER_NODES'; +import * as CLUSTER_MEET from './CLUSTER_MEET'; +import * as CLUSTER_RESET from './CLUSTER_RESET'; +import * as CLUSTER_SETSLOT from './CLUSTER_SETSLOT'; +import * as CONFIG_GET from './CONFIG_GET'; +import * as CONFIG_RESETASTAT from './CONFIG_RESETSTAT'; +import * as CONFIG_REWRITE from './CONFIG_REWRITE'; +import * as CONFIG_SET from './CONFIG_SET'; +import * as COPY from './COPY'; +import * as DBSIZE from './DBSIZE'; +import * as DECR from './DECR'; +import * as DECRBY from './DECRBY'; +import * as DEL from './DEL'; +import * as DISCARD from './DISCARD'; +import * as DUMP from './DUMP'; +import * as ECHO from './ECHO'; +import * as EVAL from './EVAL'; +import * as EVALSHA from './EVALSHA'; +import * as EXISTS from './EXISTS'; +import * as EXPIRE from './EXPIRE'; +import * as EXPIREAT from './EXPIREAT'; +import * as FAILOVER from './FAILOVER'; +import * as FLUSHALL from './FLUSHALL'; +import * as FLUSHDB from './FLUSHDB'; +import * as GEOADD from './GEOADD'; +import * as GEODIST from './GEODIST'; +import * as GEOHASH from './GEOHASH'; +import * as GEOPOS from './GEOPOS'; +import * as GEOSEARCH_WITH from './GEOSEARCH_WITH'; +import * as GEOSEARCH from './GEOSEARCH'; +import * as GEOSEARCHSTORE from './GEOSEARCHSTORE'; +import * as GET from './GET'; +import * as GETBIT from './GETBIT'; +import * as GETDEL from './GETDEL'; +import * as GETEX from './GETEX'; +import * as GETRANGE from './GETRANGE'; +import * as GETSET from './GETSET'; +import * as HDEL from './HDEL'; +import * as HELLO from './HELLO'; +import * as HEXISTS from './HEXISTS'; +import * as HGET from './HGET'; +import * as HGETALL from './HGETALL'; +import * as HINCRBY from './HINCRBY'; +import * as HINCRBYFLOAT from './HINCRBYFLOAT'; +import * as HKEYS from './HKEYS'; +import * as HLEN from './HLEN'; +import * as HMGET from './HMGET'; +import * as HRANDFIELD_COUNT_WITHVALUES from './HRANDFIELD_COUNT_WITHVALUES'; +import * as HRANDFIELD_COUNT from './HRANDFIELD_COUNT'; +import * as HRANDFIELD from './HRANDFIELD'; +import * as HSCAN from './HSCAN'; +import * as HSET from './HSET'; +import * as HSETNX from './HSETNX'; +import * as HSTRLEN from './HSTRLEN'; +import * as HVALS from './HVALS'; +import * as INCR from './INCR'; +import * as INCRBY from './INCRBY'; +import * as INCRBYFLOAT from './INCRBYFLOAT'; +import * as INFO from './INFO'; +import * as KEYS from './KEYS'; +import * as LASTSAVE from './LASTSAVE'; +import * as LINDEX from './LINDEX'; +import * as LINSERT from './LINSERT'; +import * as LLEN from './LLEN'; +import * as LMOVE from './LMOVE'; +import * as LOLWUT from './LOLWUT'; +import * as LPOP_COUNT from './LPOP_COUNT'; +import * as LPOP from './LPOP'; +import * as LPOS_COUNT from './LPOS_COUNT'; +import * as LPOS from './LPOS'; +import * as LPUSH from './LPUSH'; +import * as LPUSHX from './LPUSHX'; +import * as LRANGE from './LRANGE'; +import * as LREM from './LREM'; +import * as LSET from './LSET'; +import * as LTRIM from './LTRIM'; +import * as MEMOERY_DOCTOR from './MEMORY_DOCTOR'; +import * as MEMORY_MALLOC_STATS from './MEMORY_MALLOC-STATS'; +import * as MEMORY_PURGE from './MEMORY_PURGE'; +import * as MEMORY_STATS from './MEMORY_STATS'; +import * as MEMORY_USAGE from './MEMORY_USAGE'; +import * as MGET from './MGET'; +import * as MIGRATE from './MIGRATE'; +import * as MODULE_LIST from './MODULE_LIST'; +import * as MODULE_LOAD from './MODULE_LOAD'; +import * as MODULE_UNLOAD from './MODULE_UNLOAD'; +import * as MOVE from './MOVE'; +import * as MSET from './MSET'; +import * as MSETNX from './MSETNX'; +import * as PERSIST from './PERSIST'; +import * as PEXPIRE from './PEXPIRE'; +import * as PEXPIREAT from './PEXPIREAT'; +import * as PFADD from './PFADD'; +import * as PFCOUNT from './PFCOUNT'; +import * as PFMERGE from './PFMERGE'; +import * as PING from './PING'; +import * as PSETEX from './PSETEX'; +import * as PTTL from './PTTL'; +import * as PUBLISH from './PUBLISH'; +import * as PUBSUB_CHANNELS from './PUBSUB_CHANNELS'; +import * as PUBSUB_NUMPAT from './PUBSUB_NUMPAT'; +import * as PUBSUB_NUMSUB from './PUBSUB_NUMSUB'; +import * as RANDOMKEY from './RANDOMKEY'; +import * as READONLY from './READONLY'; +import * as READWRITE from './READWRITE'; +import * as RENAME from './RENAME'; +import * as RENAMENX from './RENAMENX'; +import * as REPLICAOF from './REPLICAOF'; +import * as RESTORE_ASKING from './RESTORE-ASKING'; +import * as ROLE from './ROLE'; +import * as RPOP_COUNT from './RPOP_COUNT'; +import * as RPOP from './RPOP'; +import * as RPOPLPUSH from './RPOPLPUSH'; +import * as RPUSH from './RPUSH'; +import * as RPUSHX from './RPUSHX'; +import * as SADD from './SADD'; +import * as SAVE from './SAVE'; +import * as SCAN from './SCAN'; +import * as SCARD from './SCARD'; +import * as SCRIPT_DEBUG from './SCRIPT_DEBUG'; +import * as SCRIPT_EXISTS from './SCRIPT_EXISTS'; +import * as SCRIPT_FLUSH from './SCRIPT_FLUSH'; +import * as SCRIPT_KILL from './SCRIPT_KILL'; +import * as SCRIPT_LOAD from './SCRIPT_LOAD'; +import * as SDIFF from './SDIFF'; +import * as SDIFFSTORE from './SDIFFSTORE'; +import * as SET from './SET'; +import * as SETBIT from './SETBIT'; +import * as SETEX from './SETEX'; +import * as SETNX from './SETNX'; +import * as SETRANGE from './SETRANGE'; +import * as SHUTDOWN from './SHUTDOWN'; +import * as SINTER from './SINTER'; +import * as SINTERSTORE from './SINTERSTORE'; +import * as SISMEMBER from './SISMEMBER'; +import * as SMEMBERS from './SMEMBERS'; +import * as SMISMEMBER from './SMISMEMBER'; +import * as SMOVE from './SMOVE'; +import * as SORT from './SORT'; +import * as SPOP from './SPOP'; +import * as SRANDMEMBER_COUNT from './SRANDMEMBER_COUNT'; +import * as SRANDMEMBER from './SRANDMEMBER'; +import * as SREM from './SREM'; +import * as SSCAN from './SSCAN'; +import * as STRLEN from './STRLEN'; +import * as SUNION from './SUNION'; +import * as SUNIONSTORE from './SUNIONSTORE'; +import * as SWAPDB from './SWAPDB'; +import * as TIME from './TIME'; +import * as TOUCH from './TOUCH'; +import * as TTL from './TTL'; +import * as TYPE from './TYPE'; +import * as UNLINK from './UNLINK'; +import * as UNWATCH from './UNWATCH'; +import * as WAIT from './WAIT'; +import * as WATCH from './WATCH'; +import * as XACK from './XACK'; +import * as XADD from './XADD'; +import * as XAUTOCLAIM_JUSTID from './XAUTOCLAIM_JUSTID'; +import * as XAUTOCLAIM from './XAUTOCLAIM'; +import * as XCLAIM from './XCLAIM'; +import * as XCLAIM_JUSTID from './XCLAIM_JUSTID'; +import * as XDEL from './XDEL'; +import * as XGROUP_CREATE from './XGROUP_CREATE'; +import * as XGROUP_CREATECONSUMER from './XGROUP_CREATECONSUMER'; +import * as XGROUP_DELCONSUMER from './XGROUP_DELCONSUMER'; +import * as XGROUP_DESTROY from './XGROUP_DESTROY'; +import * as XGROUP_SETID from './XGROUP_SETID'; +import * as XINFO_CONSUMERS from './XINFO_CONSUMERS'; +import * as XINFO_GROUPS from './XINFO_GROUPS'; +import * as XINFO_STREAM from './XINFO_STREAM'; +import * as XLEN from './XLEN'; +import * as XPENDING_RANGE from './XPENDING_RANGE'; +import * as XPENDING from './XPENDING'; +import * as XRANGE from './XRANGE'; +import * as XREAD from './XREAD'; +import * as XREADGROUP from './XREADGROUP'; +import * as XREVRANGE from './XREVRANGE'; +import * as XTRIM from './XTRIM'; +import * as ZADD from './ZADD'; +import * as ZCARD from './ZCARD'; +import * as ZCOUNT from './ZCOUNT'; +import * as ZDIFF_WITHSCORES from './ZDIFF_WITHSCORES'; +import * as ZDIFF from './ZDIFF'; +import * as ZDIFFSTORE from './ZDIFFSTORE'; +import * as ZINCRBY from './ZINCRBY'; +import * as ZINTER_WITHSCORES from './ZINTER_WITHSCORES'; +import * as ZINTER from './ZINTER'; +import * as ZINTERSTORE from './ZINTERSTORE'; +import * as ZLEXCOUNT from './ZLEXCOUNT'; +import * as ZMSCORE from './ZMSCORE'; +import * as ZPOPMAX_COUNT from './ZPOPMAX_COUNT'; +import * as ZPOPMAX from './ZPOPMAX'; +import * as ZPOPMIN_COUNT from './ZPOPMIN_COUNT'; +import * as ZPOPMIN from './ZPOPMIN'; +import * as ZRANDMEMBER_COUNT_WITHSCORES from './ZRANDMEMBER_COUNT_WITHSCORES'; +import * as ZRANDMEMBER_COUNT from './ZRANDMEMBER_COUNT'; +import * as ZRANDMEMBER from './ZRANDMEMBER'; +import * as ZRANGE_WITHSCORES from './ZRANGE_WITHSCORES'; +import * as ZRANGE from './ZRANGE'; +import * as ZRANGESTORE from './ZRANGESTORE'; +import * as ZRANK from './ZRANK'; +import * as ZREM from './ZREM'; +import * as ZREMRANGEBYLEX from './ZREMRANGEBYLEX'; +import * as ZREMRANGEBYRANK from './ZREMRANGEBYRANK'; +import * as ZREMRANGEBYSCORE from './ZREMRANGEBYSCORE'; +import * as ZREVRANK from './ZREVRANK'; +import * as ZSCAN from './ZSCAN'; +import * as ZSCORE from './ZSCORE'; +import * as ZUNION_WITHSCORES from './ZUNION_WITHSCORES'; +import * as ZUNION from './ZUNION'; +import * as ZUNIONSTORE from './ZUNIONSTORE'; + +export default { + ACL_CAT, + aclCat: ACL_CAT, + ACL_DELUSER, + aclDelUser: ACL_DELUSER, + ACL_GENPASS, + aclGenPass: ACL_GENPASS, + ACL_GETUSER, + aclGetUser: ACL_GETUSER, + ACL_LIST, + aclList: ACL_LIST, + ACL_LOAD, + aclLoad: ACL_LOAD, + ACL_LOG_RESET, + aclLogReset: ACL_LOG_RESET, + ACL_LOG, + aclLog: ACL_LOG, + ACL_SAVE, + aclSave: ACL_SAVE, + ACL_SETUSER, + aclSetUser: ACL_SETUSER, + ACL_USERS, + aclUsers: ACL_USERS, + ACL_WHOAMI, + aclWhoAmI: ACL_WHOAMI, + APPEND, + append: APPEND, + ASKING, + asking: ASKING, + AUTH, + auth: AUTH, + BGREWRITEAOF, + bgRewriteAof: BGREWRITEAOF, + BGSAVE, + bgSave: BGSAVE, + BITCOUNT, + bitCount: BITCOUNT, + BITFIELD, + bitField: BITFIELD, + BITOP, + bitOp: BITOP, + BITPOS, + bitPos: BITPOS, + BLMOVE, + blMove: BLMOVE, + BLPOP, + blPop: BLPOP, + BRPOP, + brPop: BRPOP, + BRPOPLPUSH, + brPopLPush: BRPOPLPUSH, + BZPOPMAX, + bzPopMax: BZPOPMAX, + BZPOPMIN, + bzPopMin: BZPOPMIN, + CLIENT_ID, + clientId: CLIENT_ID, + CLIENT_INFO, + clientInfo: CLIENT_INFO, + CLUSTER_ADDSLOTS, + clusterAddSlots: CLUSTER_ADDSLOTS, + CLUSTER_FLUSHSLOTS, + clusterFlushSlots: CLUSTER_FLUSHSLOTS, + CLUSTER_INFO, + clusterInfo: CLUSTER_INFO, + CLUSTER_NODES, + clusterNodes: CLUSTER_NODES, + CLUSTER_MEET, + clusterMeet: CLUSTER_MEET, + CLUSTER_RESET, + clusterReset: CLUSTER_RESET, + CLUSTER_SETSLOT, + clusterSetSlot: CLUSTER_SETSLOT, + CONFIG_GET, + configGet: CONFIG_GET, + CONFIG_RESETASTAT, + configResetStat: CONFIG_RESETASTAT, + CONFIG_REWRITE, + configRewrite: CONFIG_REWRITE, + CONFIG_SET, + configSet: CONFIG_SET, + COPY, + copy: COPY, + DBSIZE, + dbSize: DBSIZE, + DECR, + decr: DECR, + DECRBY, + decrBy: DECRBY, + DEL, + del: DEL, + DISCARD, + discard: DISCARD, + DUMP, + dump: DUMP, + ECHO, + echo: ECHO, + EVAL, + eval: EVAL, + EVALSHA, + evalSha: EVALSHA, + EXISTS, + exists: EXISTS, + EXPIRE, + expire: EXPIRE, + EXPIREAT, + expireAt: EXPIREAT, + FAILOVER, + failover: FAILOVER, + FLUSHALL, + flushAll: FLUSHALL, + FLUSHDB, + flushDb: FLUSHDB, + GEOADD, + geoAdd: GEOADD, + GEODIST, + geoDist: GEODIST, + GEOHASH, + geoHash: GEOHASH, + GEOPOS, + geoPos: GEOPOS, + GEOSEARCH_WITH, + geoSearchWith: GEOSEARCH_WITH, + GEOSEARCH, + geoSearch: GEOSEARCH, + GEOSEARCHSTORE, + geoSearchStore: GEOSEARCHSTORE, + GET, + get: GET, + GETBIT, + getBit: GETBIT, + GETDEL, + getDel: GETDEL, + GETEX, + getEx: GETEX, + GETRANGE, + getRange: GETRANGE, + GETSET, + getSet: GETSET, + HDEL, + hDel: HDEL, + HELLO, + hello: HELLO, + HEXISTS, + hExists: HEXISTS, + HGET, + hGet: HGET, + HGETALL, + hGetAll: HGETALL, + HINCRBY, + hIncrBy: HINCRBY, + HINCRBYFLOAT, + hIncrByFloat: HINCRBYFLOAT, + HKEYS, + hKeys: HKEYS, + HLEN, + hLen: HLEN, + HMGET, + hmGet: HMGET, + HRANDFIELD_COUNT_WITHVALUES, + hRandFieldCountWithValues: HRANDFIELD_COUNT_WITHVALUES, + HRANDFIELD_COUNT, + hRandFieldCount: HRANDFIELD_COUNT, + HRANDFIELD, + hRandField: HRANDFIELD, + HSCAN, + hScan: HSCAN, + HSET, + hSet: HSET, + HSETNX, + hSetNX: HSETNX, + HSTRLEN, + hStrLen: HSTRLEN, + HVALS, + hVals: HVALS, + INCR, + incr: INCR, + INCRBY, + incrBy: INCRBY, + INCRBYFLOAT, + incrByFloat: INCRBYFLOAT, + INFO, + info: INFO, + KEYS, + keys: KEYS, + LASTSAVE, + lastSave: LASTSAVE, + LINDEX, + lIndex: LINDEX, + LINSERT, + lInsert: LINSERT, + LLEN, + lLen: LLEN, + LMOVE, + lMove: LMOVE, + LOLWUT, + LPOP_COUNT, + lPopCount: LPOP_COUNT, + LPOP, + lPop: LPOP, + LPOS_COUNT, + lPosCount: LPOS_COUNT, + LPOS, + lPos: LPOS, + LPUSH, + lPush: LPUSH, + LPUSHX, + lPushX: LPUSHX, + LRANGE, + lRange: LRANGE, + LREM, + lRem: LREM, + LSET, + lSet: LSET, + LTRIM, + lTrim: LTRIM, + MEMOERY_DOCTOR, + memoryDoctor: MEMOERY_DOCTOR, + 'MEMORY_MALLOC-STATS': MEMORY_MALLOC_STATS, + memoryMallocStats: MEMORY_MALLOC_STATS, + MEMORY_PURGE, + memoryPurge: MEMORY_PURGE, + MEMORY_STATS, + memoryStats: MEMORY_STATS, + MEMORY_USAGE, + memoryUsage: MEMORY_USAGE, + MGET, + mGet: MGET, + MIGRATE, + migrate: MIGRATE, + MODULE_LIST, + moduleList: MODULE_LIST, + MODULE_LOAD, + moduleLoad: MODULE_LOAD, + MODULE_UNLOAD, + moduleUnload: MODULE_UNLOAD, + MOVE, + move: MOVE, + MSET, + mSet: MSET, + MSETNX, + mSetNX: MSETNX, + PERSIST, + persist: PERSIST, + PEXPIRE, + pExpire: PEXPIRE, + PEXPIREAT, + pExpireAt: PEXPIREAT, + PFADD, + pfAdd: PFADD, + PFCOUNT, + pfCount: PFCOUNT, + PFMERGE, + pfMerge: PFMERGE, + PING, + ping: PING, + PSETEX, + pSetEx: PSETEX, + PTTL, + pTTL: PTTL, + PUBLISH, + publish: PUBLISH, + PUBSUB_CHANNELS, + pubSubChannels: PUBSUB_CHANNELS, + PUBSUB_NUMPAT, + pubSubNumPat: PUBSUB_NUMPAT, + PUBSUB_NUMSUB, + pubSubNumSub: PUBSUB_NUMSUB, + RANDOMKEY, + randomKey: RANDOMKEY, + READONLY, + readonly: READONLY, + READWRITE, + readwrite: READWRITE, + RENAME, + rename: RENAME, + RENAMENX, + renameNX: RENAMENX, + REPLICAOF, + replicaOf: REPLICAOF, + 'RESTORE-ASKING': RESTORE_ASKING, + restoreAsking: RESTORE_ASKING, + ROLE, + role: ROLE, + RPOP_COUNT, + rPopCount: RPOP_COUNT, + RPOP, + rPop: RPOP, + RPOPLPUSH, + rPopLPush: RPOPLPUSH, + RPUSH, + rPush: RPUSH, + RPUSHX, + rPushX: RPUSHX, + SADD, + sAdd: SADD, + SAVE, + save: SAVE, + SCAN, + scan: SCAN, + SCARD, + sCard: SCARD, + SCRIPT_DEBUG, + scriptDebug: SCRIPT_DEBUG, + SCRIPT_EXISTS, + scriptExists: SCRIPT_EXISTS, + SCRIPT_FLUSH, + scriptFlush: SCRIPT_FLUSH, + SCRIPT_KILL, + scriptKill: SCRIPT_KILL, + SCRIPT_LOAD, + scriptLoad: SCRIPT_LOAD, + SDIFF, + sDiff: SDIFF, + SDIFFSTORE, + sDiffStore: SDIFFSTORE, + SINTER, + sInter: SINTER, + SINTERSTORE, + sInterStore: SINTERSTORE, + SET, + set: SET, + SETBIT, + setBit: SETBIT, + SETEX, + setEx: SETEX, + SETNX, + setNX: SETNX, + SETRANGE, + setRange: SETRANGE, + SHUTDOWN, + shutdown: SHUTDOWN, + SISMEMBER, + sIsMember: SISMEMBER, + SMEMBERS, + sMembers: SMEMBERS, + SMISMEMBER, + smIsMember: SMISMEMBER, + SMOVE, + sMove: SMOVE, + SORT, + sort: SORT, + SPOP, + sPop: SPOP, + SRANDMEMBER_COUNT, + sRandMemberCount: SRANDMEMBER_COUNT, + SRANDMEMBER, + sRandMember: SRANDMEMBER, + SREM, + sRem: SREM, + SSCAN, + sScan: SSCAN, + STRLEN, + strLen: STRLEN, + SUNION, + sUnion: SUNION, + SUNIONSTORE, + sUnionStore: SUNIONSTORE, + SWAPDB, + swapDb: SWAPDB, + TIME, + time: TIME, + TOUCH, + touch: TOUCH, + TTL, + ttl: TTL, + TYPE, + type: TYPE, + UNLINK, + unlink: UNLINK, + UNWATCH, + unwatch: UNWATCH, + WAIT, + wait: WAIT, + WATCH, + watch: WATCH, + XACK, + xAck: XACK, + XADD, + xAdd: XADD, + XAUTOCLAIM_JUSTID, + xAutoClaimJustId: XAUTOCLAIM_JUSTID, + XAUTOCLAIM, + xAutoClaim: XAUTOCLAIM, + XCLAIM, + xClaim: XCLAIM, + XCLAIM_JUSTID, + xClaimJustId: XCLAIM_JUSTID, + XDEL, + xDel: XDEL, + XGROUP_CREATE, + xGroupCreate: XGROUP_CREATE, + XGROUP_CREATECONSUMER, + xGroupCreateConsumer: XGROUP_CREATECONSUMER, + XGROUP_DELCONSUMER, + xGroupDelConsumer: XGROUP_DELCONSUMER, + XGROUP_DESTROY, + xGroupDestroy: XGROUP_DESTROY, + XGROUP_SETID, + xGroupSetId: XGROUP_SETID, + XINFO_CONSUMERS, + xInfoConsumers: XINFO_CONSUMERS, + XINFO_GROUPS, + xInfoGroups: XINFO_GROUPS, + XINFO_STREAM, + xInfoStream: XINFO_STREAM, + XLEN, + xLen: XLEN, + XPENDING_RANGE, + xPendingRange: XPENDING_RANGE, + XPENDING, + xPending: XPENDING, + XRANGE, + xRange: XRANGE, + XREAD, + xRead: XREAD, + XREADGROUP, + xReadGroup: XREADGROUP, + XREVRANGE, + xRevRange: XREVRANGE, + XTRIM, + xTrim: XTRIM, + ZADD, + zAdd: ZADD, + ZCARD, + zCard: ZCARD, + ZCOUNT, + zCount: ZCOUNT, + ZDIFF_WITHSCORES, + zDiffWithScores: ZDIFF_WITHSCORES, + ZDIFF, + zDiff: ZDIFF, + ZDIFFSTORE, + zDiffStore: ZDIFFSTORE, + ZINCRBY, + zIncrBy: ZINCRBY, + ZINTER_WITHSCORES, + zInterWithScores: ZINTER_WITHSCORES, + ZINTER, + zInter: ZINTER, + ZINTERSTORE, + zInterStore: ZINTERSTORE, + ZLEXCOUNT, + zLexCount: ZLEXCOUNT, + ZMSCORE, + zmScore: ZMSCORE, + ZPOPMAX_COUNT, + zPopMaxCount: ZPOPMAX_COUNT, + ZPOPMAX, + zPopMax: ZPOPMAX, + ZPOPMIN_COUNT, + zPopMinCount: ZPOPMIN_COUNT, + ZPOPMIN, + zPopMin: ZPOPMIN, + ZRANDMEMBER_COUNT_WITHSCORES, + zRandMemberCountWithScores: ZRANDMEMBER_COUNT_WITHSCORES, + ZRANDMEMBER_COUNT, + zRandMemberCount: ZRANDMEMBER_COUNT, + ZRANDMEMBER, + zRandMember: ZRANDMEMBER, + ZRANGE_WITHSCORES, + zRangeWithScores: ZRANGE_WITHSCORES, + ZRANGE, + zRange: ZRANGE, + ZRANGESTORE, + zRangeStore: ZRANGESTORE, + ZRANK, + zRank: ZRANK, + ZREM, + zRem: ZREM, + ZREMRANGEBYLEX, + zRemRangeByLex: ZREMRANGEBYLEX, + ZREMRANGEBYRANK, + zRemRangeByRank: ZREMRANGEBYRANK, + ZREMRANGEBYSCORE, + zRemRangeByScore: ZREMRANGEBYSCORE, + ZREVRANK, + zRevRank: ZREVRANK, + ZSCAN, + zScan: ZSCAN, + ZSCORE, + zScore: ZSCORE, + ZUNION_WITHSCORES, + zUnionWithScores: ZUNION_WITHSCORES, + ZUNION, + zUnion: ZUNION, + ZUNIONSTORE, + zUnionStore: ZUNIONSTORE +}; + +export type RedisReply = string | number | Array | null | undefined; + +export type TransformArgumentsReply = Array & { preserve?: unknown }; + +export interface RedisCommand { + FIRST_KEY_INDEX?: number | ((...args: Array) => string); + IS_READ_ONLY?: boolean; + transformArguments(...args: Array): TransformArgumentsReply; + transformReply(reply: RedisReply, preserved: unknown): any; +} + +export interface RedisCommands { + [command: string]: RedisCommand; +} + +export interface RedisModule { + [key: string]: RedisCommand; +} + +export type RedisModules = Record; diff --git a/lib/createClient.js b/lib/createClient.js deleted file mode 100644 index b03bb575399..00000000000 --- a/lib/createClient.js +++ /dev/null @@ -1,88 +0,0 @@ -'use strict'; - -var utils = require('./utils'); -var URL = require('url'); - -module.exports = function createClient (port_arg, host_arg, options) { - - if (typeof port_arg === 'number' || typeof port_arg === 'string' && /^\d+$/.test(port_arg)) { - - var host; - if (typeof host_arg === 'string') { - host = host_arg; - } else { - if (options && host_arg) { - throw new TypeError('Unknown type of connection in createClient()'); - } - options = options || host_arg; - } - options = utils.clone(options); - options.host = host || options.host; - options.port = port_arg; - - } else if (typeof port_arg === 'string' || port_arg && port_arg.url) { - - options = utils.clone(port_arg.url ? port_arg : host_arg || options); - var url = port_arg.url || port_arg; - var parsed = URL.parse(url, true, true); - - // [redis:]//[[user][:password]@][host][:port][/db-number][?db=db-number[&password=bar[&option=value]]] - if (parsed.slashes) { // We require slashes - if (parsed.auth) { - var columnIndex = parsed.auth.indexOf(':'); - options.password = parsed.auth.slice(columnIndex + 1); - if (columnIndex > 0) { - options.user = parsed.auth.slice(0, columnIndex); - } - } - if (parsed.protocol) { - if (parsed.protocol === 'rediss:') { - options.tls = options.tls || {}; - } else if (parsed.protocol !== 'redis:') { - console.warn('node_redis: WARNING: You passed "' + parsed.protocol.substring(0, parsed.protocol.length - 1) + '" as protocol instead of the "redis" protocol!'); - } - } - if (parsed.pathname && parsed.pathname !== '/') { - options.db = parsed.pathname.substr(1); - } - if (parsed.hostname) { - options.host = parsed.hostname; - } - if (parsed.port) { - options.port = parsed.port; - } - if (parsed.search !== '') { - var elem; - for (elem in parsed.query) { - // If options are passed twice, only the parsed options will be used - if (elem in options) { - if (options[elem] === parsed.query[elem]) { - console.warn('node_redis: WARNING: You passed the ' + elem + ' option twice!'); - } else { - throw new RangeError('The ' + elem + ' option is added twice and does not match'); - } - } - options[elem] = parsed.query[elem]; - } - } - } else if (parsed.hostname) { - throw new RangeError('The redis url must begin with slashes "//" or contain slashes after the redis protocol'); - } else { - options.path = url; - } - - } else if (typeof port_arg === 'object' || port_arg === undefined) { - options = utils.clone(port_arg || options); - options.host = options.host || host_arg; - - if (port_arg && arguments.length !== 1) { - throw new TypeError('Too many arguments passed to createClient. Please only pass the options object'); - } - } - - if (!options) { - throw new TypeError('Unknown type of connection in createClient()'); - } - - return options; -}; diff --git a/lib/customErrors.js b/lib/customErrors.js deleted file mode 100644 index 2483db0d8d4..00000000000 --- a/lib/customErrors.js +++ /dev/null @@ -1,58 +0,0 @@ -'use strict'; - -var util = require('util'); -var assert = require('assert'); -var RedisError = require('redis-errors').RedisError; -var ADD_STACKTRACE = false; - -function AbortError (obj, stack) { - assert(obj, 'The options argument is required'); - assert.strictEqual(typeof obj, 'object', 'The options argument has to be of type object'); - - Object.defineProperty(this, 'message', { - value: obj.message || '', - configurable: true, - writable: true - }); - if (stack || stack === undefined) { - Error.captureStackTrace(this, AbortError); - } - for (var keys = Object.keys(obj), key = keys.pop(); key; key = keys.pop()) { - this[key] = obj[key]; - } -} - -function AggregateError (obj) { - assert(obj, 'The options argument is required'); - assert.strictEqual(typeof obj, 'object', 'The options argument has to be of type object'); - - AbortError.call(this, obj, ADD_STACKTRACE); - Object.defineProperty(this, 'message', { - value: obj.message || '', - configurable: true, - writable: true - }); - Error.captureStackTrace(this, AggregateError); - for (var keys = Object.keys(obj), key = keys.pop(); key; key = keys.pop()) { - this[key] = obj[key]; - } -} - -util.inherits(AbortError, RedisError); -util.inherits(AggregateError, AbortError); - -Object.defineProperty(AbortError.prototype, 'name', { - value: 'AbortError', - configurable: true, - writable: true -}); -Object.defineProperty(AggregateError.prototype, 'name', { - value: 'AggregateError', - configurable: true, - writable: true -}); - -module.exports = { - AbortError: AbortError, - AggregateError: AggregateError -}; diff --git a/lib/debug.js b/lib/debug.js deleted file mode 100644 index d69c1ea6246..00000000000 --- a/lib/debug.js +++ /dev/null @@ -1,13 +0,0 @@ -'use strict'; - -var index = require('../'); - -function debug () { - if (index.debug_mode) { - var data = Array.prototype.slice.call(arguments); - data.unshift(new Date().toISOString()); - console.error.apply(null, data); - } -} - -module.exports = debug; diff --git a/lib/errors.ts b/lib/errors.ts new file mode 100644 index 00000000000..86a65587cf5 --- /dev/null +++ b/lib/errors.ts @@ -0,0 +1,23 @@ +export class AbortError extends Error { + constructor() { + super('The command was aborted'); + } +} + +export class WatchError extends Error { + constructor() { + super('One (or more) of the watched keys has been changed'); + } +} + +export class ConnectionTimeoutError extends Error { + constructor() { + super('Connection timeout'); + } +} + +export class ClientClosedError extends Error { + constructor() { + super('The client is closed'); + } +} diff --git a/lib/extendedApi.js b/lib/extendedApi.js deleted file mode 100644 index 27ed4215d6a..00000000000 --- a/lib/extendedApi.js +++ /dev/null @@ -1,113 +0,0 @@ -'use strict'; - -var utils = require('./utils'); -var debug = require('./debug'); -var RedisClient = require('../').RedisClient; -var Command = require('./command'); -var noop = function () {}; - -/********************************************** -All documented and exposed API belongs in here -**********************************************/ - -// Redirect calls to the appropriate function and use to send arbitrary / not supported commands -RedisClient.prototype.send_command = RedisClient.prototype.sendCommand = function (command, args, callback) { - // Throw to fail early instead of relying in order in this case - if (typeof command !== 'string') { - throw new TypeError('Wrong input type "' + (command !== null && command !== undefined ? command.constructor.name : command) + '" for command name'); - } - command = command.toLowerCase(); - if (!Array.isArray(args)) { - if (args === undefined || args === null) { - args = []; - } else if (typeof args === 'function' && callback === undefined) { - callback = args; - args = []; - } else { - throw new TypeError('Wrong input type "' + args.constructor.name + '" for args'); - } - } - if (typeof callback !== 'function' && callback !== undefined) { - throw new TypeError('Wrong input type "' + (callback !== null ? callback.constructor.name : 'null') + '" for callback function'); - } - - // Using the raw multi command is only possible with this function - // If the command is not yet added to the client, the internal function should be called right away - // Otherwise we need to redirect the calls to make sure the internal functions don't get skipped - // The internal functions could actually be used for any non hooked function - // but this might change from time to time and at the moment there's no good way to distinguish them - // from each other, so let's just do it do it this way for the time being - if (command === 'multi' || typeof this[command] !== 'function') { - return this.internal_send_command(new Command(command, args, callback)); - } - if (typeof callback === 'function') { - args = args.concat([callback]); // Prevent manipulating the input array - } - return this[command].apply(this, args); -}; - -RedisClient.prototype.end = function (flush) { - // Flush queue if wanted - if (flush) { - this.flush_and_error({ - message: 'Connection forcefully ended and command aborted.', - code: 'NR_CLOSED' - }); - } else if (arguments.length === 0) { - this.warn( - 'Using .end() without the flush parameter is deprecated and throws from v.3.0.0 on.\n' + - 'Please check the doku (https://github.com/NodeRedis/node_redis) and explictly use flush.' - ); - } - // Clear retry_timer - if (this.retry_timer) { - clearTimeout(this.retry_timer); - this.retry_timer = null; - } - this.stream.removeAllListeners(); - this.stream.on('error', noop); - this.connected = false; - this.ready = false; - this.closing = true; - return this.stream.destroySoon(); -}; - -RedisClient.prototype.unref = function () { - if (this.connected) { - debug("Unref'ing the socket connection"); - this.stream.unref(); - } else { - debug('Not connected yet, will unref later'); - this.once('connect', function () { - this.unref(); - }); - } -}; - -RedisClient.prototype.duplicate = function (options, callback) { - if (typeof options === 'function') { - callback = options; - options = null; - } - var existing_options = utils.clone(this.options); - options = utils.clone(options); - for (var elem in options) { - existing_options[elem] = options[elem]; - } - var client = new RedisClient(existing_options); - client.selected_db = options.db || this.selected_db; - if (typeof callback === 'function') { - var ready_listener = function () { - callback(null, client); - client.removeAllListeners(error_listener); - }; - var error_listener = function (err) { - callback(err); - client.end(true); - }; - client.once('ready', ready_listener); - client.once('error', error_listener); - return; - } - return client; -}; diff --git a/lib/individualCommands.js b/lib/individualCommands.js deleted file mode 100644 index c3ea3da0df3..00000000000 --- a/lib/individualCommands.js +++ /dev/null @@ -1,629 +0,0 @@ -'use strict'; - -var utils = require('./utils'); -var debug = require('./debug'); -var Multi = require('./multi'); -var Command = require('./command'); -var no_password_is_set = /no password is set|called without any password configured/; -var loading = /LOADING/; -var RedisClient = require('../').RedisClient; - -/******************************************************************************************** - Replace built-in redis functions - - The callback may be hooked as needed. The same does not apply to the rest of the function. - State should not be set outside of the callback if not absolutly necessary. - This is important to make sure it works the same as single command or in a multi context. - To make sure everything works with the offline queue use the "call_on_write" function. - This is going to be executed while writing to the stream. - - TODO: Implement individal command generation as soon as possible to prevent divergent code - on single and multi calls! -********************************************************************************************/ - -RedisClient.prototype.multi = RedisClient.prototype.MULTI = function multi (args) { - var multi = new Multi(this, args); - multi.exec = multi.EXEC = multi.exec_transaction; - return multi; -}; - -// ATTENTION: This is not a native function but is still handled as a individual command as it behaves just the same as multi -RedisClient.prototype.batch = RedisClient.prototype.BATCH = function batch (args) { - return new Multi(this, args); -}; - -function select_callback (self, db, callback) { - return function (err, res) { - if (err === null) { - // Store db in this.select_db to restore it on reconnect - self.selected_db = db; - } - utils.callback_or_emit(self, callback, err, res); - }; -} - -RedisClient.prototype.select = RedisClient.prototype.SELECT = function select (db, callback) { - return this.internal_send_command(new Command('select', [db], select_callback(this, db, callback))); -}; - -Multi.prototype.select = Multi.prototype.SELECT = function select (db, callback) { - this.queue.push(new Command('select', [db], select_callback(this._client, db, callback))); - return this; -}; - -RedisClient.prototype.monitor = RedisClient.prototype.MONITOR = function monitor (callback) { - // Use a individual command, as this is a special case that does not has to be checked for any other command - var self = this; - var call_on_write = function () { - // Activating monitor mode has to happen before Redis returned the callback. The monitor result is returned first. - // Therefore we expect the command to be properly processed. If this is not the case, it's not an issue either. - self.monitoring = true; - }; - return this.internal_send_command(new Command('monitor', [], callback, call_on_write)); -}; - -// Only works with batch, not in a transaction -Multi.prototype.monitor = Multi.prototype.MONITOR = function monitor (callback) { - // Use a individual command, as this is a special case that does not has to be checked for any other command - if (this.exec !== this.exec_transaction) { - var self = this; - var call_on_write = function () { - self._client.monitoring = true; - }; - this.queue.push(new Command('monitor', [], callback, call_on_write)); - return this; - } - // Set multi monitoring to indicate the exec that it should abort - // Remove this "hack" as soon as Redis might fix this - this.monitoring = true; - return this; -}; - -function quit_callback (self, callback) { - return function (err, res) { - if (err && err.code === 'NR_CLOSED') { - // Pretent the quit command worked properly in this case. - // Either the quit landed in the offline queue and was flushed at the reconnect - // or the offline queue is deactivated and the command was rejected right away - // or the stream is not writable - // or while sending the quit, the connection ended / closed - err = null; - res = 'OK'; - } - utils.callback_or_emit(self, callback, err, res); - if (self.stream.writable) { - // If the socket is still alive, kill it. This could happen if quit got a NR_CLOSED error code - self.stream.destroy(); - } - }; -} - -RedisClient.prototype.QUIT = RedisClient.prototype.quit = function quit (callback) { - // TODO: Consider this for v.3 - // Allow the quit command to be fired as soon as possible to prevent it landing in the offline queue. - // this.ready = this.offline_queue.length === 0; - var backpressure_indicator = this.internal_send_command(new Command('quit', [], quit_callback(this, callback))); - // Calling quit should always end the connection, no matter if there's a connection or not - this.closing = true; - this.ready = false; - return backpressure_indicator; -}; - -// Only works with batch, not in a transaction -Multi.prototype.QUIT = Multi.prototype.quit = function quit (callback) { - var self = this._client; - var call_on_write = function () { - // If called in a multi context, we expect redis is available - self.closing = true; - self.ready = false; - }; - this.queue.push(new Command('quit', [], quit_callback(self, callback), call_on_write)); - return this; -}; - -function info_callback (self, callback) { - return function (err, res) { - if (res) { - var obj = {}; - var lines = res.toString().split('\r\n'); - var line, parts, sub_parts; - - for (var i = 0; i < lines.length; i++) { - parts = lines[i].split(':'); - if (parts[1]) { - if (parts[0].indexOf('db') === 0) { - sub_parts = parts[1].split(','); - obj[parts[0]] = {}; - while (line = sub_parts.pop()) { - line = line.split('='); - obj[parts[0]][line[0]] = +line[1]; - } - } else { - obj[parts[0]] = parts[1]; - } - } - } - obj.versions = []; - if (obj.redis_version) { - obj.redis_version.split('.').forEach(function (num) { - obj.versions.push(+num); - }); - } - // Expose info key/vals to users - self.server_info = obj; - } else { - self.server_info = {}; - } - utils.callback_or_emit(self, callback, err, res); - }; -} - -// Store info in this.server_info after each call -RedisClient.prototype.info = RedisClient.prototype.INFO = function info (section, callback) { - var args = []; - if (typeof section === 'function') { - callback = section; - } else if (section !== undefined) { - args = Array.isArray(section) ? section : [section]; - } - return this.internal_send_command(new Command('info', args, info_callback(this, callback))); -}; - -Multi.prototype.info = Multi.prototype.INFO = function info (section, callback) { - var args = []; - if (typeof section === 'function') { - callback = section; - } else if (section !== undefined) { - args = Array.isArray(section) ? section : [section]; - } - this.queue.push(new Command('info', args, info_callback(this._client, callback))); - return this; -}; - -function auth_callback (self, pass, user, callback) { - return function (err, res) { - if (err) { - if (no_password_is_set.test(err.message)) { - self.warn('Warning: Redis server does not require a password, but a password was supplied.'); - err = null; - res = 'OK'; - } else if (loading.test(err.message)) { - // If redis is still loading the db, it will not authenticate and everything else will fail - debug('Redis still loading, trying to authenticate later'); - setTimeout(function () { - self.auth(pass, user, callback); - }, 100); - return; - } - } - utils.callback_or_emit(self, callback, err, res); - }; -} - -RedisClient.prototype.auth = RedisClient.prototype.AUTH = function auth (pass, user, callback) { - debug('Sending auth to ' + this.address + ' id ' + this.connection_id); - - // Backward compatibility support for auth with password only - if (user instanceof Function) { - callback = user; - user = null; - } - // Stash auth for connect and reconnect. - this.auth_pass = pass; - this.auth_user = user; - var ready = this.ready; - this.ready = ready || this.offline_queue.length === 0; - var tmp = this.internal_send_command(new Command('auth', user ? [user, pass] : [pass], auth_callback(this, pass, user, callback))); - this.ready = ready; - return tmp; -}; - -// Only works with batch, not in a transaction -Multi.prototype.auth = Multi.prototype.AUTH = function auth (pass, user, callback) { - debug('Sending auth to ' + this.address + ' id ' + this.connection_id); - - // Backward compatibility support for auth with password only - if (user instanceof Function) { - callback = user; - user = null; - } - // Stash auth for connect and reconnect. - this.auth_pass = pass; - this.auth_user = user; - this.queue.push(new Command('auth', user ? [user, pass] : [pass], auth_callback(this._client, pass, user, callback))); - return this; -}; - -RedisClient.prototype.client = RedisClient.prototype.CLIENT = function client () { - var arr, - len = arguments.length, - callback, - i = 0; - if (Array.isArray(arguments[0])) { - arr = arguments[0]; - callback = arguments[1]; - } else if (Array.isArray(arguments[1])) { - if (len === 3) { - callback = arguments[2]; - } - len = arguments[1].length; - arr = new Array(len + 1); - arr[0] = arguments[0]; - for (; i < len; i += 1) { - arr[i + 1] = arguments[1][i]; - } - } else { - len = arguments.length; - // The later should not be the average use case - if (len !== 0 && (typeof arguments[len - 1] === 'function' || typeof arguments[len - 1] === 'undefined')) { - len--; - callback = arguments[len]; - } - arr = new Array(len); - for (; i < len; i += 1) { - arr[i] = arguments[i]; - } - } - var self = this; - var call_on_write = undefined; - // CLIENT REPLY ON|OFF|SKIP - /* istanbul ignore next: TODO: Remove this as soon as Travis runs Redis 3.2 */ - if (arr.length === 2 && arr[0].toString().toUpperCase() === 'REPLY') { - var reply_on_off = arr[1].toString().toUpperCase(); - if (reply_on_off === 'ON' || reply_on_off === 'OFF' || reply_on_off === 'SKIP') { - call_on_write = function () { - self.reply = reply_on_off; - }; - } - } - return this.internal_send_command(new Command('client', arr, callback, call_on_write)); -}; - -Multi.prototype.client = Multi.prototype.CLIENT = function client () { - var arr, - len = arguments.length, - callback, - i = 0; - if (Array.isArray(arguments[0])) { - arr = arguments[0]; - callback = arguments[1]; - } else if (Array.isArray(arguments[1])) { - if (len === 3) { - callback = arguments[2]; - } - len = arguments[1].length; - arr = new Array(len + 1); - arr[0] = arguments[0]; - for (; i < len; i += 1) { - arr[i + 1] = arguments[1][i]; - } - } else { - len = arguments.length; - // The later should not be the average use case - if (len !== 0 && (typeof arguments[len - 1] === 'function' || typeof arguments[len - 1] === 'undefined')) { - len--; - callback = arguments[len]; - } - arr = new Array(len); - for (; i < len; i += 1) { - arr[i] = arguments[i]; - } - } - var self = this._client; - var call_on_write = undefined; - // CLIENT REPLY ON|OFF|SKIP - /* istanbul ignore next: TODO: Remove this as soon as Travis runs Redis 3.2 */ - if (arr.length === 2 && arr[0].toString().toUpperCase() === 'REPLY') { - var reply_on_off = arr[1].toString().toUpperCase(); - if (reply_on_off === 'ON' || reply_on_off === 'OFF' || reply_on_off === 'SKIP') { - call_on_write = function () { - self.reply = reply_on_off; - }; - } - } - this.queue.push(new Command('client', arr, callback, call_on_write)); - return this; -}; - -RedisClient.prototype.hmset = RedisClient.prototype.HMSET = function hmset () { - var arr, - len = arguments.length, - callback, - i = 0; - if (Array.isArray(arguments[0])) { - arr = arguments[0]; - callback = arguments[1]; - } else if (Array.isArray(arguments[1])) { - if (len === 3) { - callback = arguments[2]; - } - len = arguments[1].length; - arr = new Array(len + 1); - arr[0] = arguments[0]; - for (; i < len; i += 1) { - arr[i + 1] = arguments[1][i]; - } - } else if (typeof arguments[1] === 'object' && (arguments.length === 2 || arguments.length === 3 && (typeof arguments[2] === 'function' || typeof arguments[2] === 'undefined'))) { - arr = [arguments[0]]; - for (var field in arguments[1]) { - arr.push(field, arguments[1][field]); - } - callback = arguments[2]; - } else { - len = arguments.length; - // The later should not be the average use case - if (len !== 0 && (typeof arguments[len - 1] === 'function' || typeof arguments[len - 1] === 'undefined')) { - len--; - callback = arguments[len]; - } - arr = new Array(len); - for (; i < len; i += 1) { - arr[i] = arguments[i]; - } - } - return this.internal_send_command(new Command('hmset', arr, callback)); -}; - -Multi.prototype.hmset = Multi.prototype.HMSET = function hmset () { - var arr, - len = arguments.length, - callback, - i = 0; - if (Array.isArray(arguments[0])) { - arr = arguments[0]; - callback = arguments[1]; - } else if (Array.isArray(arguments[1])) { - if (len === 3) { - callback = arguments[2]; - } - len = arguments[1].length; - arr = new Array(len + 1); - arr[0] = arguments[0]; - for (; i < len; i += 1) { - arr[i + 1] = arguments[1][i]; - } - } else if (typeof arguments[1] === 'object' && (arguments.length === 2 || arguments.length === 3 && (typeof arguments[2] === 'function' || typeof arguments[2] === 'undefined'))) { - arr = [arguments[0]]; - for (var field in arguments[1]) { - arr.push(field, arguments[1][field]); - } - callback = arguments[2]; - } else { - len = arguments.length; - // The later should not be the average use case - if (len !== 0 && (typeof arguments[len - 1] === 'function' || typeof arguments[len - 1] === 'undefined')) { - len--; - callback = arguments[len]; - } - arr = new Array(len); - for (; i < len; i += 1) { - arr[i] = arguments[i]; - } - } - this.queue.push(new Command('hmset', arr, callback)); - return this; -}; - -RedisClient.prototype.subscribe = RedisClient.prototype.SUBSCRIBE = function subscribe () { - var arr, - len = arguments.length, - callback, - i = 0; - if (Array.isArray(arguments[0])) { - arr = arguments[0].slice(0); - callback = arguments[1]; - } else { - len = arguments.length; - // The later should not be the average use case - if (len !== 0 && (typeof arguments[len - 1] === 'function' || typeof arguments[len - 1] === 'undefined')) { - len--; - callback = arguments[len]; - } - arr = new Array(len); - for (; i < len; i += 1) { - arr[i] = arguments[i]; - } - } - var self = this; - var call_on_write = function () { - self.pub_sub_mode = self.pub_sub_mode || self.command_queue.length + 1; - }; - return this.internal_send_command(new Command('subscribe', arr, callback, call_on_write)); -}; - -Multi.prototype.subscribe = Multi.prototype.SUBSCRIBE = function subscribe () { - var arr, - len = arguments.length, - callback, - i = 0; - if (Array.isArray(arguments[0])) { - arr = arguments[0].slice(0); - callback = arguments[1]; - } else { - len = arguments.length; - // The later should not be the average use case - if (len !== 0 && (typeof arguments[len - 1] === 'function' || typeof arguments[len - 1] === 'undefined')) { - len--; - callback = arguments[len]; - } - arr = new Array(len); - for (; i < len; i += 1) { - arr[i] = arguments[i]; - } - } - var self = this._client; - var call_on_write = function () { - self.pub_sub_mode = self.pub_sub_mode || self.command_queue.length + 1; - }; - this.queue.push(new Command('subscribe', arr, callback, call_on_write)); - return this; -}; - -RedisClient.prototype.unsubscribe = RedisClient.prototype.UNSUBSCRIBE = function unsubscribe () { - var arr, - len = arguments.length, - callback, - i = 0; - if (Array.isArray(arguments[0])) { - arr = arguments[0].slice(0); - callback = arguments[1]; - } else { - len = arguments.length; - // The later should not be the average use case - if (len !== 0 && (typeof arguments[len - 1] === 'function' || typeof arguments[len - 1] === 'undefined')) { - len--; - callback = arguments[len]; - } - arr = new Array(len); - for (; i < len; i += 1) { - arr[i] = arguments[i]; - } - } - var self = this; - var call_on_write = function () { - // Pub sub has to be activated even if not in pub sub mode, as the return value is manipulated in the callback - self.pub_sub_mode = self.pub_sub_mode || self.command_queue.length + 1; - }; - return this.internal_send_command(new Command('unsubscribe', arr, callback, call_on_write)); -}; - -Multi.prototype.unsubscribe = Multi.prototype.UNSUBSCRIBE = function unsubscribe () { - var arr, - len = arguments.length, - callback, - i = 0; - if (Array.isArray(arguments[0])) { - arr = arguments[0].slice(0); - callback = arguments[1]; - } else { - len = arguments.length; - // The later should not be the average use case - if (len !== 0 && (typeof arguments[len - 1] === 'function' || typeof arguments[len - 1] === 'undefined')) { - len--; - callback = arguments[len]; - } - arr = new Array(len); - for (; i < len; i += 1) { - arr[i] = arguments[i]; - } - } - var self = this._client; - var call_on_write = function () { - // Pub sub has to be activated even if not in pub sub mode, as the return value is manipulated in the callback - self.pub_sub_mode = self.pub_sub_mode || self.command_queue.length + 1; - }; - this.queue.push(new Command('unsubscribe', arr, callback, call_on_write)); - return this; -}; - -RedisClient.prototype.psubscribe = RedisClient.prototype.PSUBSCRIBE = function psubscribe () { - var arr, - len = arguments.length, - callback, - i = 0; - if (Array.isArray(arguments[0])) { - arr = arguments[0].slice(0); - callback = arguments[1]; - } else { - len = arguments.length; - // The later should not be the average use case - if (len !== 0 && (typeof arguments[len - 1] === 'function' || typeof arguments[len - 1] === 'undefined')) { - len--; - callback = arguments[len]; - } - arr = new Array(len); - for (; i < len; i += 1) { - arr[i] = arguments[i]; - } - } - var self = this; - var call_on_write = function () { - self.pub_sub_mode = self.pub_sub_mode || self.command_queue.length + 1; - }; - return this.internal_send_command(new Command('psubscribe', arr, callback, call_on_write)); -}; - -Multi.prototype.psubscribe = Multi.prototype.PSUBSCRIBE = function psubscribe () { - var arr, - len = arguments.length, - callback, - i = 0; - if (Array.isArray(arguments[0])) { - arr = arguments[0].slice(0); - callback = arguments[1]; - } else { - len = arguments.length; - // The later should not be the average use case - if (len !== 0 && (typeof arguments[len - 1] === 'function' || typeof arguments[len - 1] === 'undefined')) { - len--; - callback = arguments[len]; - } - arr = new Array(len); - for (; i < len; i += 1) { - arr[i] = arguments[i]; - } - } - var self = this._client; - var call_on_write = function () { - self.pub_sub_mode = self.pub_sub_mode || self.command_queue.length + 1; - }; - this.queue.push(new Command('psubscribe', arr, callback, call_on_write)); - return this; -}; - -RedisClient.prototype.punsubscribe = RedisClient.prototype.PUNSUBSCRIBE = function punsubscribe () { - var arr, - len = arguments.length, - callback, - i = 0; - if (Array.isArray(arguments[0])) { - arr = arguments[0].slice(0); - callback = arguments[1]; - } else { - len = arguments.length; - // The later should not be the average use case - if (len !== 0 && (typeof arguments[len - 1] === 'function' || typeof arguments[len - 1] === 'undefined')) { - len--; - callback = arguments[len]; - } - arr = new Array(len); - for (; i < len; i += 1) { - arr[i] = arguments[i]; - } - } - var self = this; - var call_on_write = function () { - // Pub sub has to be activated even if not in pub sub mode, as the return value is manipulated in the callback - self.pub_sub_mode = self.pub_sub_mode || self.command_queue.length + 1; - }; - return this.internal_send_command(new Command('punsubscribe', arr, callback, call_on_write)); -}; - -Multi.prototype.punsubscribe = Multi.prototype.PUNSUBSCRIBE = function punsubscribe () { - var arr, - len = arguments.length, - callback, - i = 0; - if (Array.isArray(arguments[0])) { - arr = arguments[0].slice(0); - callback = arguments[1]; - } else { - len = arguments.length; - // The later should not be the average use case - if (len !== 0 && (typeof arguments[len - 1] === 'function' || typeof arguments[len - 1] === 'undefined')) { - len--; - callback = arguments[len]; - } - arr = new Array(len); - for (; i < len; i += 1) { - arr[i] = arguments[i]; - } - } - var self = this._client; - var call_on_write = function () { - // Pub sub has to be activated even if not in pub sub mode, as the return value is manipulated in the callback - self.pub_sub_mode = self.pub_sub_mode || self.command_queue.length + 1; - }; - this.queue.push(new Command('punsubscribe', arr, callback, call_on_write)); - return this; -}; diff --git a/lib/lua-script.ts b/lib/lua-script.ts new file mode 100644 index 00000000000..183c42f219c --- /dev/null +++ b/lib/lua-script.ts @@ -0,0 +1,28 @@ +import { createHash } from 'crypto'; +import { RedisCommand } from './commands'; + +export interface RedisLuaScriptConfig extends RedisCommand { + SCRIPT: string; + NUMBER_OF_KEYS: number; +} + +export interface SHA1 { + SHA1: string; +} + +export type RedisLuaScript = RedisLuaScriptConfig & SHA1; + +export interface RedisLuaScripts { + [key: string]: RedisLuaScript; +} + +export function defineScript(script: S): S & SHA1 { + return { + ...script, + SHA1: scriptSha1(script.SCRIPT) + }; +} + +export function scriptSha1(script: string): string { + return createHash('sha1').update(script).digest('hex'); +} diff --git a/lib/multi-command.spec.ts b/lib/multi-command.spec.ts new file mode 100644 index 00000000000..a78cc8b2e08 --- /dev/null +++ b/lib/multi-command.spec.ts @@ -0,0 +1,127 @@ +import { strict as assert } from 'assert'; +import RedisMultiCommand from './multi-command'; +import { encodeCommand } from './commander'; +import { WatchError } from './errors'; +import { spy } from 'sinon'; +import { SQUARE_SCRIPT } from './client.spec'; + +describe('Multi Command', () => { + describe('exec', () => { + it('simple', async () => { + const multi = RedisMultiCommand.create((queue, symbol) => { + assert.deepEqual( + queue.map(({encodedCommand}) => encodedCommand), + [ + encodeCommand(['MULTI']), + encodeCommand(['PING']), + encodeCommand(['EXEC']), + ] + ); + + assert.equal( + typeof symbol, + 'symbol' + ); + + return Promise.resolve(['QUEUED', 'QUEUED', ['PONG']]); + }); + + multi.ping(); + + assert.deepEqual( + await multi.exec(), + ['PONG'] + ); + }); + + it('executing an empty queue should resolve without executing on the server', async () => { + const executor = spy(); + + assert.deepEqual( + await RedisMultiCommand.create(executor).exec(), + [] + ); + + assert.ok(executor.notCalled); + }); + + it('WatchError', () => { + return assert.rejects( + RedisMultiCommand.create(() => Promise.resolve([null])).ping().exec(), + WatchError + ); + }); + + it('execAsPipeline', async () => { + const multi = RedisMultiCommand.create(queue => { + assert.deepEqual( + queue.map(({encodedCommand}) => encodedCommand), + [encodeCommand(['PING'])] + ); + + return Promise.resolve(['PONG']); + }); + + multi.ping(); + + assert.deepEqual( + await multi.exec(true), + ['PONG'] + ); + }); + }); + + describe('execAsPipeline', () => { + it('simple', async () => { + const multi = RedisMultiCommand.create(queue => { + assert.deepEqual( + queue.map(({encodedCommand}) => encodedCommand), + [encodeCommand(['PING'])] + ); + + return Promise.resolve(['PONG']); + }); + + multi.ping(); + + assert.deepEqual( + await multi.execAsPipeline(), + ['PONG'] + ); + }); + + it('executing an empty queue should resolve without executing on the server', async () => { + const executor = spy(); + + assert.deepEqual( + await RedisMultiCommand.create(executor).execAsPipeline(), + [] + ); + + assert.ok(executor.notCalled); + }); + + it('with scripts', async () => { + const MultiWithScript = RedisMultiCommand.extend({ + scripts: { + square: SQUARE_SCRIPT + } + }); + + assert.deepEqual( + await new MultiWithScript(queue => { + assert.deepEqual( + queue.map(({encodedCommand}) => encodedCommand), + [ + encodeCommand(['EVAL', SQUARE_SCRIPT.SCRIPT, '0', '2']), + encodeCommand(['EVALSHA', SQUARE_SCRIPT.SHA1, '0', '3']), + ] + ); + + return Promise.resolve([4, 9]); + }).square(2).square(3).execAsPipeline(), + [4, 9] + ); + }); + }); +}); diff --git a/lib/multi-command.ts b/lib/multi-command.ts new file mode 100644 index 00000000000..c8a50765967 --- /dev/null +++ b/lib/multi-command.ts @@ -0,0 +1,210 @@ +import COMMANDS, { TransformArgumentsReply } from './commands'; +import { RedisCommand, RedisModules, RedisReply } from './commands'; +import { RedisLuaScript, RedisLuaScripts } from './lua-script'; +import { RedisClientOptions } from './client'; +import { extendWithModulesAndScripts, extendWithDefaultCommands, encodeCommand } from './commander'; +import { WatchError } from './errors'; + +type RedisMultiCommandSignature = (...args: Parameters) => RedisMultiCommandType; + +type WithCommands = { + [P in keyof typeof COMMANDS]: RedisMultiCommandSignature<(typeof COMMANDS)[P], M, S> +}; + +type WithModules = { + [P in keyof M]: { + [C in keyof M[P]]: RedisMultiCommandSignature; + }; +}; + +type WithScripts = { + [P in keyof S]: RedisMultiCommandSignature +}; + +export type RedisMultiCommandType = RedisMultiCommand & WithCommands & WithModules & WithScripts; + +export interface MultiQueuedCommand { + encodedCommand: string; + preservedArguments?: unknown; + transformReply?: RedisCommand['transformReply']; +} + +export type RedisMultiExecutor = (queue: Array, chainId?: symbol) => Promise>; + +export default class RedisMultiCommand { + static commandsExecutor(this: RedisMultiCommand, command: RedisCommand, args: Array): RedisMultiCommand { + return this.addCommand( + command.transformArguments(...args), + command.transformReply + ); + } + + static #scriptsExecutor( + this: RedisMultiCommand, + script: RedisLuaScript, + args: Array + ): RedisMultiCommand { + const transformedArguments: TransformArgumentsReply = []; + if (this.#scriptsInUse.has(script.SHA1)) { + transformedArguments.push( + 'EVALSHA', + script.SHA1 + ); + } else { + this.#scriptsInUse.add(script.SHA1); + transformedArguments.push( + 'EVAL', + script.SCRIPT + ); + } + + transformedArguments.push(script.NUMBER_OF_KEYS.toString()); + + const scriptArguments = script.transformArguments(...args); + transformedArguments.push(...scriptArguments); + transformedArguments.preserve = scriptArguments.preserve; + + return this.addCommand( + transformedArguments, + script.transformReply + ); + } + + static extend( + clientOptions?: RedisClientOptions + ): new (...args: ConstructorParameters) => RedisMultiCommandType { + return extendWithModulesAndScripts({ + BaseClass: RedisMultiCommand, + modules: clientOptions?.modules, + modulesCommandsExecutor: RedisMultiCommand.commandsExecutor, + scripts: clientOptions?.scripts, + scriptsExecutor: RedisMultiCommand.#scriptsExecutor + }); + } + + static create( + executor: RedisMultiExecutor, + clientOptions?: RedisClientOptions + ): RedisMultiCommandType { + return new this(executor, clientOptions); + } + + readonly #executor: RedisMultiExecutor; + + readonly #clientOptions: RedisClientOptions | undefined; + + readonly #queue: Array = []; + + readonly #scriptsInUse = new Set(); + + readonly #v4: Record = {}; + + get v4(): Record { + if (!this.#clientOptions?.legacyMode) { + throw new Error('client is not in "legacy mode"'); + } + + return this.#v4; + } + + constructor(executor: RedisMultiExecutor, clientOptions?: RedisClientOptions) { + this.#executor = executor; + this.#clientOptions = clientOptions; + this.#legacyMode(); + } + + #legacyMode(): void { + if (!this.#clientOptions?.legacyMode) return; + + this.#v4.addCommand = this.addCommand.bind(this); + (this as any).addCommand = (...args: Array): this => { + this.#queue.push({ + encodedCommand: encodeCommand(args.flat() as Array) + }); + return this; + } + this.#v4.exec = this.exec.bind(this); + (this as any).exec = (callback?: (err: Error | null, replies?: Array) => unknown): void => { + this.#v4.exec() + .then((reply: Array) => { + if (!callback) return; + + callback(null, reply); + }) + .catch((err: Error) => { + if (!callback) { + // this.emit('error', err); + return; + } + + callback(err); + }); + }; + + for (const name of Object.keys(COMMANDS)) { + this.#defineLegacyCommand(name); + } + } + + #defineLegacyCommand(name: string): void { + (this as any).#v4[name] = (this as any)[name].bind(this.#v4); + (this as any)[name] = (...args: Array): void => (this as any).addCommand(name, args); + } + + addCommand(args: TransformArgumentsReply, transformReply?: RedisCommand['transformReply']): this { + this.#queue.push({ + encodedCommand: encodeCommand(args), + preservedArguments: args.preserve, + transformReply + }); + + return this; + } + + async exec(execAsPipeline = false): Promise> { + if (execAsPipeline) { + return this.execAsPipeline(); + } else if (!this.#queue.length) { + return []; + } + + const queue = this.#queue.splice(0), + rawReplies = await this.#executor([ + { + encodedCommand: encodeCommand(['MULTI']) + }, + ...queue, + { + encodedCommand: encodeCommand(['EXEC']) + } + ], Symbol('[RedisMultiCommand] Chain ID')), + execReply = rawReplies[rawReplies.length - 1] as (null | Array); + + if (execReply === null) { + throw new WatchError(); + } + + return this.#transformReplies(execReply, queue); + } + + async execAsPipeline(): Promise> { + if (!this.#queue.length) { + return []; + } + + const queue = this.#queue.splice(0); + return this.#transformReplies( + await this.#executor(queue), + queue + ); + } + + #transformReplies(rawReplies: Array, queue: Array): Array { + return rawReplies.map((reply, i) => { + const { transformReply, preservedArguments } = queue[i]; + return transformReply ? transformReply(reply, preservedArguments) : reply; + }); + } +} + +extendWithDefaultCommands(RedisMultiCommand, RedisMultiCommand.commandsExecutor); diff --git a/lib/multi.js b/lib/multi.js deleted file mode 100644 index d89cffbbd48..00000000000 --- a/lib/multi.js +++ /dev/null @@ -1,187 +0,0 @@ -'use strict'; - -var Queue = require('denque'); -var utils = require('./utils'); -var Command = require('./command'); - -function Multi (client, args) { - this._client = client; - this.queue = new Queue(); - var command, tmp_args; - if (args) { // Either undefined or an array. Fail hard if it's not an array - for (var i = 0; i < args.length; i++) { - command = args[i][0]; - tmp_args = args[i].slice(1); - if (Array.isArray(command)) { - this[command[0]].apply(this, command.slice(1).concat(tmp_args)); - } else { - this[command].apply(this, tmp_args); - } - } - } -} - -function pipeline_transaction_command (self, command_obj, index) { - // Queueing is done first, then the commands are executed - var tmp = command_obj.callback; - command_obj.callback = function (err, reply) { - // Ignore the multi command. This is applied by node_redis and the user does not benefit by it - if (err && index !== -1) { - if (tmp) { - tmp(err); - } - err.position = index; - self.errors.push(err); - } - // Keep track of who wants buffer responses: - // By the time the callback is called the command_obj got the buffer_args attribute attached - self.wants_buffers[index] = command_obj.buffer_args; - command_obj.callback = tmp; - }; - self._client.internal_send_command(command_obj); -} - -Multi.prototype.exec_atomic = Multi.prototype.EXEC_ATOMIC = Multi.prototype.execAtomic = function exec_atomic (callback) { - if (this.queue.length < 2) { - return this.exec_batch(callback); - } - return this.exec(callback); -}; - -function multi_callback (self, err, replies) { - var i = 0, command_obj; - - if (err) { - err.errors = self.errors; - if (self.callback) { - self.callback(err); - // Exclude connection errors so that those errors won't be emitted twice - } else if (err.code !== 'CONNECTION_BROKEN') { - self._client.emit('error', err); - } - return; - } - - if (replies) { - while (command_obj = self.queue.shift()) { - if (replies[i] instanceof Error) { - var match = replies[i].message.match(utils.err_code); - // LUA script could return user errors that don't behave like all other errors! - if (match) { - replies[i].code = match[1]; - } - replies[i].command = command_obj.command.toUpperCase(); - if (typeof command_obj.callback === 'function') { - command_obj.callback(replies[i]); - } - } else { - // If we asked for strings, even in detect_buffers mode, then return strings: - replies[i] = self._client.handle_reply(replies[i], command_obj.command, self.wants_buffers[i]); - if (typeof command_obj.callback === 'function') { - command_obj.callback(null, replies[i]); - } - } - i++; - } - } - - if (self.callback) { - self.callback(null, replies); - } -} - -Multi.prototype.exec_transaction = function exec_transaction (callback) { - if (this.monitoring || this._client.monitoring) { - var err = new RangeError( - 'Using transaction with a client that is in monitor mode does not work due to faulty return values of Redis.' - ); - err.command = 'EXEC'; - err.code = 'EXECABORT'; - return utils.reply_in_order(this._client, callback, err); - } - var self = this; - var len = self.queue.length; - self.errors = []; - self.callback = callback; - self._client.cork(); - self.wants_buffers = new Array(len); - pipeline_transaction_command(self, new Command('multi', []), -1); - // Drain queue, callback will catch 'QUEUED' or error - for (var index = 0; index < len; index++) { - // The commands may not be shifted off, since they are needed in the result handler - pipeline_transaction_command(self, self.queue.get(index), index); - } - - self._client.internal_send_command(new Command('exec', [], function (err, replies) { - multi_callback(self, err, replies); - })); - self._client.uncork(); - return !self._client.should_buffer; -}; - -function batch_callback (self, cb, i) { - return function batch_callback (err, res) { - if (err) { - self.results[i] = err; - // Add the position to the error - self.results[i].position = i; - } else { - self.results[i] = res; - } - cb(err, res); - }; -} - -Multi.prototype.exec = Multi.prototype.EXEC = Multi.prototype.exec_batch = function exec_batch (callback) { - var self = this; - var len = self.queue.length; - var index = 0; - var command_obj; - if (len === 0) { - utils.reply_in_order(self._client, callback, null, []); - return !self._client.should_buffer; - } - self._client.cork(); - if (!callback) { - while (command_obj = self.queue.shift()) { - self._client.internal_send_command(command_obj); - } - self._client.uncork(); - return !self._client.should_buffer; - } - var callback_without_own_cb = function (err, res) { - if (err) { - self.results.push(err); - // Add the position to the error - var i = self.results.length - 1; - self.results[i].position = i; - } else { - self.results.push(res); - } - // Do not emit an error here. Otherwise each error would result in one emit. - // The errors will be returned in the result anyway - }; - var last_callback = function (cb) { - return function (err, res) { - cb(err, res); - callback(null, self.results); - }; - }; - self.results = []; - while (command_obj = self.queue.shift()) { - if (typeof command_obj.callback === 'function') { - command_obj.callback = batch_callback(self, command_obj.callback, index); - } else { - command_obj.callback = callback_without_own_cb; - } - if (typeof callback === 'function' && index === len - 1) { - command_obj.callback = last_callback(command_obj.callback); - } - this._client.internal_send_command(command_obj); - index++; - } - self._client.uncork(); - return !self._client.should_buffer; -}; - -module.exports = Multi; diff --git a/lib/socket.spec.ts b/lib/socket.spec.ts new file mode 100644 index 00000000000..11c02d0885c --- /dev/null +++ b/lib/socket.spec.ts @@ -0,0 +1,38 @@ +import { strict as assert } from 'assert'; +import { SinonFakeTimers, useFakeTimers, spy } from 'sinon'; +import RedisSocket from './socket'; + +describe('Socket', () => { + describe('reconnectStrategy', () => { + let clock: SinonFakeTimers; + beforeEach(() => clock = useFakeTimers()); + afterEach(() => clock.uninstall()); + + it('custom strategy', () => { + const reconnectStrategy = spy((retries: number): number | Error => { + assert.equal(retries + 1, reconnectStrategy.callCount); + + if (retries === 50) { + return Error('50'); + } + + const time = retries * 2; + queueMicrotask(() => clock.tick(time)); + return time; + }); + + const socket = new RedisSocket(undefined, { + host: 'error', + reconnectStrategy + }); + + socket.on('error', () => { + // ignore errors + }); + + return assert.rejects(socket.connect(), { + message: '50' + }); + }) + }); +}); diff --git a/lib/socket.ts b/lib/socket.ts new file mode 100644 index 00000000000..66cd28d91d5 --- /dev/null +++ b/lib/socket.ts @@ -0,0 +1,254 @@ +import EventEmitter from 'events'; +import net from 'net'; +import tls from 'tls'; +import { URL } from 'url'; +import { ConnectionTimeoutError, ClientClosedError } from './errors'; +import { promiseTimeout } from './utils'; + +export interface RedisSocketCommonOptions { + username?: string; + password?: string; + connectTimeout?: number; + noDelay?: boolean; + keepAlive?: number | false; + reconnectStrategy?(retries: number): number | Error; +} + +export interface RedisNetSocketOptions extends RedisSocketCommonOptions { + port?: number; + host?: string; +} + +export interface RedisUrlSocketOptions extends RedisSocketCommonOptions { + url: string; +} + +export interface RedisUnixSocketOptions extends RedisSocketCommonOptions { + path: string; +} + +export interface RedisTlsSocketOptions extends RedisNetSocketOptions, tls.SecureContextOptions { + tls: true; +} + +export type RedisSocketOptions = RedisNetSocketOptions | RedisUrlSocketOptions | RedisUnixSocketOptions | RedisTlsSocketOptions; + +interface CreateSocketReturn { + connectEvent: string; + socket: T; +} + +export type RedisSocketInitiator = () => Promise; + +export default class RedisSocket extends EventEmitter { + static #initiateOptions(options?: RedisSocketOptions): RedisSocketOptions { + options ??= {}; + if (!RedisSocket.#isUnixSocket(options)) { + if (RedisSocket.#isUrlSocket(options)) { + const url = new URL(options.url); + (options as RedisNetSocketOptions).port = Number(url.port); + (options as RedisNetSocketOptions).host = url.hostname; + options.username = url.username; + options.password = url.password; + } + + (options as RedisNetSocketOptions).port ??= 6379; + (options as RedisNetSocketOptions).host ??= '127.0.0.1'; + } + + options.connectTimeout ??= 5000; + options.keepAlive ??= 5000; + options.noDelay ??= true; + + return options; + } + + static #defaultReconnectStrategy(retries: number): number { + return Math.min(retries * 50, 500); + } + + static #isUrlSocket(options: RedisSocketOptions): options is RedisUrlSocketOptions { + return Object.prototype.hasOwnProperty.call(options, 'url'); + } + + static #isUnixSocket(options: RedisSocketOptions): options is RedisUnixSocketOptions { + return Object.prototype.hasOwnProperty.call(options, 'path'); + } + + static #isTlsSocket(options: RedisSocketOptions): options is RedisTlsSocketOptions { + return (options as RedisTlsSocketOptions).tls === true; + } + + readonly #initiator?: RedisSocketInitiator; + + readonly #options: RedisSocketOptions; + + #socket?: net.Socket | tls.TLSSocket; + + #isOpen = false; + + get isOpen(): boolean { + return this.#isOpen; + } + + get chunkRecommendedSize(): number { + if (!this.#socket) return 0; + + return this.#socket.writableHighWaterMark - this.#socket.writableLength; + } + + constructor(initiator?: RedisSocketInitiator, options?: RedisSocketOptions) { + super(); + + this.#initiator = initiator; + this.#options = RedisSocket.#initiateOptions(options); + } + + async connect(): Promise { + if (this.#isOpen) { + throw new Error('Socket is connection/connecting'); + } + + this.#isOpen = true; + + try { + await this.#connect(); + } catch (err) { + this.#isOpen = false; + throw err; + } + } + + async #connect(hadError?: boolean): Promise { + this.#socket = await this.#retryConnection(0, hadError); + this.emit('connect'); + + if (this.#initiator) { + try { + await this.#initiator(); + } catch (err) { + this.#socket.end(); + this.#socket = undefined; + throw err; + } + } + + this.emit('ready'); + } + + async #retryConnection(retries: number, hadError?: boolean): Promise { + if (retries > 0 || hadError) { + this.emit('reconnecting'); + } + + try { + return await this.#createSocket(); + } catch (err) { + this.emit('error', err); + + if (!this.#isOpen) { + throw err; + } + + const retryIn = (this.#options?.reconnectStrategy ?? RedisSocket.#defaultReconnectStrategy)(retries); + if (retryIn instanceof Error) { + throw retryIn; + } + + await promiseTimeout(retryIn); + return this.#retryConnection(retries + 1); + } + } + + #createSocket(): Promise { + return new Promise((resolve, reject) => { + const {connectEvent, socket} = RedisSocket.#isTlsSocket(this.#options) ? + this.#createTlsSocket() : + this.#createNetSocket(); + + if (this.#options.connectTimeout) { + socket.setTimeout(this.#options.connectTimeout, () => socket.destroy(new ConnectionTimeoutError())); + } + + socket + .setNoDelay(this.#options.noDelay) + .setKeepAlive(this.#options.keepAlive !== false, this.#options.keepAlive || 0) + .once('error', reject) + .once(connectEvent, () => { + socket + .setTimeout(0) + .off('error', reject) + .once('error', (err: Error) => this.#onSocketError(err)) + .once('close', hadError => { + if (!hadError && this.#isOpen) { + this.#onSocketError(new Error('Socket closed unexpectedly')); + } + }) + .on('drain', () => this.emit('drain')) + .on('data', (data: Buffer) => this.emit('data', data)); + + resolve(socket); + }); + }); + } + + #createNetSocket(): CreateSocketReturn { + return { + connectEvent: 'connect', + socket: net.connect(this.#options as net.NetConnectOpts) // TODO + }; + } + + #createTlsSocket(): CreateSocketReturn { + return { + connectEvent: 'secureConnect', + socket: tls.connect(this.#options as tls.ConnectionOptions) // TODO + }; + } + + #onSocketError(err: Error): void { + this.#socket = undefined; + this.emit('error', err); + + this.#connect(true) + .catch(err => this.emit('error', err)); + } + + write(encodedCommands: string): boolean { + if (!this.#socket) { + throw new ClientClosedError(); + } + + return this.#socket.write(encodedCommands); + } + + async disconnect(ignoreIsOpen = false): Promise { + if ((!ignoreIsOpen && !this.#isOpen) || !this.#socket) { + throw new ClientClosedError(); + } else { + this.#isOpen = false; + } + + this.#socket.end(); + await EventEmitter.once(this.#socket, 'end'); + this.#socket = undefined; + this.emit('end'); + } + + async quit(fn: () => Promise): Promise { + if (!this.#isOpen) { + throw new ClientClosedError(); + } + + this.#isOpen = false; + + + try { + await fn(); + await this.disconnect(true); + } catch (err) { + this.#isOpen = true; + throw err; + } + } +} diff --git a/lib/test-utils.ts b/lib/test-utils.ts new file mode 100644 index 00000000000..e23d90d47ea --- /dev/null +++ b/lib/test-utils.ts @@ -0,0 +1,373 @@ +import { strict as assert } from 'assert'; +import RedisClient, { RedisClientType } from './client'; +import { RedisModules } from './commands'; +import { RedisLuaScripts } from './lua-script'; +import { execSync, spawn } from 'child_process'; +import { once } from 'events'; +import { RedisSocketOptions } from './socket'; +import which from 'which'; +import { SinonSpy } from 'sinon'; +import RedisCluster, { RedisClusterType } from './cluster'; +import { promises as fs } from 'fs'; +import { Context as MochaContext } from 'mocha'; +import { promiseTimeout } from './utils'; + +type RedisVersion = [major: number, minor: number, patch: number]; + +type PartialRedisVersion = RedisVersion | [major: number, minor: number] | [major: number]; + +const REDIS_PATH = which.sync('redis-server'); +export const REDIS_VERSION = getRedisVersion(); + +function getRedisVersion(): RedisVersion { + const raw = execSync(`${REDIS_PATH} -v`).toString(), + indexOfVersion = raw.indexOf('v='); + + if (indexOfVersion === -1) { + throw new Error('Unknown redis version'); + } + + const start = indexOfVersion + 2; + return raw.substring( + start, + raw.indexOf(' ', start) + ).split('.', 3).map(Number) as RedisVersion; +} + +export function isRedisVersionGreaterThan(minimumVersion: PartialRedisVersion | undefined): boolean { + if (minimumVersion === undefined) return true; + + const lastIndex = minimumVersion.length - 1; + for (let i = 0; i < lastIndex; i++) { + if (REDIS_VERSION[i] > minimumVersion[i]) { + return true; + } else if (minimumVersion[i] > REDIS_VERSION[i]) { + return false; + } + } + + return REDIS_VERSION[lastIndex] >= minimumVersion[lastIndex]; +} + +export enum TestRedisServers { + OPEN, + PASSWORD +} + +export const TEST_REDIS_SERVERS: Record = {}; + +export enum TestRedisClusters { + OPEN +} + +export const TEST_REDIS_CLUSTERES: Record> = {}; + +let port = 6379; + +interface SpawnRedisServerResult { + port: number; + cleanup: () => Promise; +} + +async function spawnRedisServer(args?: Array): Promise { + const currentPort = port++, + process = spawn(REDIS_PATH, [ + '--save', + '', + '--port', + currentPort.toString(), + ...(args ?? []) + ]); + + process + .once('error', err => console.error('Redis process error', err)) + .once('close', code => console.error(`Redis process closed unexpectedly with code ${code}`)); + + for await (const chunk of process.stdout) { + if (chunk.toString().includes('Ready to accept connections')) { + break; + } + } + + if (process.exitCode !== null) { + throw new Error('Error while spawning redis server'); + } + + return { + port: currentPort, + async cleanup(): Promise { + process.removeAllListeners('close'); + assert.ok(process.kill()); + await once(process, 'close'); + } + }; +} + +async function spawnGlobalRedisServer(args?: Array): Promise { + const { port, cleanup } = await spawnRedisServer(args); + after(cleanup); + return port; +} + +const SLOTS = 16384; + +interface SpawnRedisClusterNodeResult extends SpawnRedisServerResult { + client: RedisClientType +} + +async function spawnRedisClusterNode( + type: TestRedisClusters | null, + nodeIndex: number, + fromSlot: number, + toSlot: number, + args?: Array +): Promise { + const clusterConfigFile = `/tmp/${type}-${nodeIndex}.conf`, + { port, cleanup: originalCleanup } = await spawnRedisServer([ + '--cluster-enabled', + 'yes', + '--cluster-node-timeout', + '5000', + '--cluster-config-file', + clusterConfigFile, + ...(args ?? []) + ]); + + const client = RedisClient.create({ + socket: { + port + } + }); + + await client.connect(); + + const range = []; + for (let i = fromSlot; i < toSlot; i++) { + range.push(i); + } + + await Promise.all([ + client.clusterFlushSlots(), + client.clusterAddSlots(range) + ]); + + return { + port, + async cleanup(): Promise { + await originalCleanup(); + + try { + await fs.unlink(clusterConfigFile); + } catch (err: any) { + if (err.code === 'ENOENT') return; + + throw err; + } + }, + client + }; +} + +export async function spawnRedisCluster(type: TestRedisClusters | null, numberOfNodes: number, args?: Array): Promise> { + const spawnPromises = [], + slotsPerNode = Math.floor(SLOTS / numberOfNodes); + for (let i = 0; i < numberOfNodes; i++) { + const fromSlot = i * slotsPerNode; + spawnPromises.push( + spawnRedisClusterNode( + type, + i, + fromSlot, + i === numberOfNodes - 1 ? SLOTS : fromSlot + slotsPerNode, + args + ) + ); + } + + const spawnResults = await Promise.all(spawnPromises), + meetPromises = []; + for (let i = 1; i < spawnResults.length; i++) { + meetPromises.push( + spawnResults[i].client.clusterMeet( + '127.0.0.1', + spawnResults[i - 1].port + ) + ); + } + + await Promise.all(meetPromises); + + while (!(await clusterIsReady(spawnResults))) { + await promiseTimeout(100); + } + + await Promise.all( + spawnResults.map(result => result.client.disconnect()) + ); + + return spawnResults; +} + +async function clusterIsReady(spawnResults: Array): Promise { + const nodesClusetrInfo = await Promise.all( + spawnResults.map(result => result.client.clusterInfo()) + ); + + return nodesClusetrInfo.every(({ state }) => state === 'ok'); +} + +export async function spawnGlobalRedisCluster(type: TestRedisClusters | null, numberOfNodes: number, args?: Array): Promise> { + const results = await spawnRedisCluster(type, numberOfNodes, args); + + after(() => Promise.all( + results.map(({ cleanup }) => cleanup()) + )); + + return results.map(({ port }) => port); +} + +async function spawnOpenServer(): Promise { + TEST_REDIS_SERVERS[TestRedisServers.OPEN] = { + port: await spawnGlobalRedisServer() + }; +} + +async function spawnPasswordServer(): Promise { + TEST_REDIS_SERVERS[TestRedisServers.PASSWORD] = { + port: await spawnGlobalRedisServer(['--requirepass', 'password']), + password: 'password' + }; + + if (isRedisVersionGreaterThan([6])) { + TEST_REDIS_SERVERS[TestRedisServers.PASSWORD].username = 'default'; + } +} + +async function spawnOpenCluster(): Promise { + TEST_REDIS_CLUSTERES[TestRedisClusters.OPEN] = (await spawnGlobalRedisCluster(TestRedisClusters.OPEN, 3)).map(port => ({ + port + })); +} + +before(function () { + this.timeout(10000); + + return Promise.all([ + spawnOpenServer(), + spawnPasswordServer(), + spawnOpenCluster() + ]); +}); + +interface RedisTestOptions { + minimumRedisVersion?: PartialRedisVersion; +} + +export function handleMinimumRedisVersion(mochaContext: MochaContext, minimumVersion: PartialRedisVersion | undefined): boolean { + if (isRedisVersionGreaterThan(minimumVersion)) { + return false; + } + + mochaContext.skip(); + return true; +} + +export function describeHandleMinimumRedisVersion(minimumVersion: PartialRedisVersion): void { + before(function () { + handleMinimumRedisVersion(this, minimumVersion); + }); +} + +export function itWithClient( + type: TestRedisServers, + title: string, + fn: (client: RedisClientType) => Promise, + options?: RedisTestOptions +): void { + it(title, async function () { + if (handleMinimumRedisVersion(this, options?.minimumRedisVersion)) return; + + const client = RedisClient.create({ + socket: TEST_REDIS_SERVERS[type] + }); + + await client.connect(); + + try { + await client.flushAll(); + await fn(client); + } finally { + await client.flushAll(); + await client.disconnect(); + } + }); +} + +export function itWithCluster( + type: TestRedisClusters, + title: string, + fn: (cluster: RedisClusterType) => Promise, + options?: RedisTestOptions +): void { + it(title, async function () { + if (handleMinimumRedisVersion(this, options?.minimumRedisVersion)) return; + + const cluster = RedisCluster.create({ + rootNodes: TEST_REDIS_CLUSTERES[type] + }); + + await cluster.connect(); + + try { + await clusterFlushAll(cluster); + await fn(cluster); + } finally { + await clusterFlushAll(cluster); + await cluster.disconnect(); + } + }); +} + +export function itWithDedicatedCluster(title: string, fn: (cluster: RedisClusterType) => Promise): void { + it(title, async function () { + this.timeout(10000); + + const spawnResults = await spawnRedisCluster(null, 3), + cluster = RedisCluster.create({ + rootNodes: [{ + port: spawnResults[0].port + }] + }); + + await cluster.connect(); + + try { + await fn(cluster); + } finally { + await cluster.disconnect(); + + for (const { cleanup } of spawnResults) { + await cleanup(); + } + } + }); +} + +async function clusterFlushAll(cluster: RedisCluster): Promise { + await Promise.all( + cluster.getMasters().map(({ client }) => client.flushAll()) + ); +} + +export async function waitTillBeenCalled(spy: SinonSpy): Promise { + const start = process.hrtime.bigint(), + calls = spy.callCount; + + do { + if (process.hrtime.bigint() - start > 1_000_000_000) { + throw new Error('Waiting for more than 1 second'); + } + + await promiseTimeout(1); + } while (spy.callCount === calls) +} \ No newline at end of file diff --git a/lib/ts-declarations/cluster-key-slot.d.ts b/lib/ts-declarations/cluster-key-slot.d.ts new file mode 100644 index 00000000000..5774c50fbd4 --- /dev/null +++ b/lib/ts-declarations/cluster-key-slot.d.ts @@ -0,0 +1,3 @@ +declare module 'cluster-key-slot' { + export default function calculateSlot(key: string): number; +} diff --git a/lib/ts-declarations/redis-parser.d.ts b/lib/ts-declarations/redis-parser.d.ts new file mode 100644 index 00000000000..68659616b93 --- /dev/null +++ b/lib/ts-declarations/redis-parser.d.ts @@ -0,0 +1,13 @@ +declare module 'redis-parser' { + interface RedisParserCallbacks { + returnReply(reply: unknown): void; + returnError(err: Error): void; + returnFatalError?(err: Error): void; + } + + export default class RedisParser { + constructor(callbacks: RedisParserCallbacks); + + execute(buffer: Buffer): void; + } +} diff --git a/lib/utils.js b/lib/utils.js deleted file mode 100644 index d0336ae9c1d..00000000000 --- a/lib/utils.js +++ /dev/null @@ -1,134 +0,0 @@ -'use strict'; - -// hgetall converts its replies to an Object. If the reply is empty, null is returned. -// These function are only called with internal data and have therefore always the same instanceof X -function replyToObject (reply) { - // The reply might be a string or a buffer if this is called in a transaction (multi) - if (reply.length === 0 || !(reply instanceof Array)) { - return null; - } - var obj = {}; - for (var i = 0; i < reply.length; i += 2) { - obj[reply[i].toString('binary')] = reply[i + 1]; - } - return obj; -} - -function replyToStrings (reply) { - if (reply instanceof Buffer) { - return reply.toString(); - } - if (reply instanceof Array) { - var res = new Array(reply.length); - for (var i = 0; i < reply.length; i++) { - // Recusivly call the function as slowlog returns deep nested replies - res[i] = replyToStrings(reply[i]); - } - return res; - } - - return reply; -} - -function print (err, reply) { - if (err) { - // A error always begins with Error: - console.log(err.toString()); - } else { - console.log('Reply: ' + reply); - } -} - -var camelCase; -// Deep clone arbitrary objects with arrays. Can't handle cyclic structures (results in a range error) -// Any attribute with a non primitive value besides object and array will be passed by reference (e.g. Buffers, Maps, Functions) -// All capital letters are going to be replaced with a lower case letter and a underscore infront of it -function clone (obj) { - var copy; - if (Array.isArray(obj)) { - copy = new Array(obj.length); - for (var i = 0; i < obj.length; i++) { - copy[i] = clone(obj[i]); - } - return copy; - } - if (Object.prototype.toString.call(obj) === '[object Object]') { - copy = {}; - var elems = Object.keys(obj); - var elem; - while (elem = elems.pop()) { - if (elem === 'tls') { // special handle tls - copy[elem] = obj[elem]; - continue; - } - // Accept camelCase options and convert them to snake_case - var snake_case = elem.replace(/[A-Z][^A-Z]/g, '_$&').toLowerCase(); - // If camelCase is detected, pass it to the client, so all variables are going to be camelCased - // There are no deep nested options objects yet, but let's handle this future proof - if (snake_case !== elem.toLowerCase()) { - camelCase = true; - } - copy[snake_case] = clone(obj[elem]); - } - return copy; - } - return obj; -} - -function convenienceClone (obj) { - camelCase = false; - obj = clone(obj) || {}; - if (camelCase) { - obj.camel_case = true; - } - return obj; -} - -function callbackOrEmit (self, callback, err, res) { - if (callback) { - callback(err, res); - } else if (err) { - self.emit('error', err); - } -} - -function replyInOrder (self, callback, err, res, queue) { - // If the queue is explicitly passed, use that, otherwise fall back to the offline queue first, - // as there might be commands in both queues at the same time - var command_obj; - /* istanbul ignore if: TODO: Remove this as soon as we test Redis 3.2 on travis */ - if (queue) { - command_obj = queue.peekBack(); - } else { - command_obj = self.offline_queue.peekBack() || self.command_queue.peekBack(); - } - if (!command_obj) { - process.nextTick(function () { - callbackOrEmit(self, callback, err, res); - }); - } else { - var tmp = command_obj.callback; - command_obj.callback = tmp ? - function (e, r) { - tmp(e, r); - callbackOrEmit(self, callback, err, res); - } : - function (e, r) { - if (e) { - self.emit('error', e); - } - callbackOrEmit(self, callback, err, res); - }; - } -} - -module.exports = { - reply_to_strings: replyToStrings, - reply_to_object: replyToObject, - print: print, - err_code: /^([A-Z]+)\s+(.+)$/, - monitor_regex: /^[0-9]{10,11}\.[0-9]+ \[[0-9]+ .+\].*"$/, - clone: convenienceClone, - callback_or_emit: callbackOrEmit, - reply_in_order: replyInOrder -}; diff --git a/lib/utils.ts b/lib/utils.ts new file mode 100644 index 00000000000..55bed419813 --- /dev/null +++ b/lib/utils.ts @@ -0,0 +1,3 @@ +export function promiseTimeout(ms: number): Promise { + return new Promise(resolve => setTimeout(resolve, ms)); +} diff --git a/package-lock.json b/package-lock.json new file mode 100644 index 00000000000..3b7397b61e4 --- /dev/null +++ b/package-lock.json @@ -0,0 +1,9965 @@ +{ + "name": "redis", + "version": "4.0.0-rc.0", + "lockfileVersion": 2, + "requires": true, + "packages": { + "": { + "name": "redis", + "version": "4.0.0-rc.0", + "license": "MIT", + "dependencies": { + "cluster-key-slot": "1.1.0", + "generic-pool": "3.8.2", + "redis-parser": "3.0.0", + "yallist": "4.0.0" + }, + "devDependencies": { + "@istanbuljs/nyc-config-typescript": "^1.0.1", + "@types/mocha": "^9.0.0", + "@types/node": "^16.7.8", + "@types/sinon": "^10.0.2", + "@types/which": "^2.0.1", + "@types/yallist": "^4.0.1", + "mocha": "^9.1.1", + "nyc": "^15.1.0", + "release-it": "^14.11.5", + "sinon": "^11.1.2", + "source-map-support": "^0.5.19", + "ts-node": "^10.2.1", + "typedoc": "^0.21.9", + "typedoc-github-wiki-theme": "^0.5.1", + "typedoc-plugin-markdown": "^3.10.4", + "typescript": "^4.4.2", + "which": "^2.0.2" + }, + "engines": { + "node": ">=12" + } + }, + "node_modules/@babel/code-frame": { + "version": "7.14.5", + "resolved": "https://registry.npmjs.org/@babel/code-frame/-/code-frame-7.14.5.tgz", + "integrity": "sha512-9pzDqyc6OLDaqe+zbACgFkb6fKMNG6CObKpnYXChRsvYGyEdc7CA2BaqeOM+vOtCS5ndmJicPJhKAwYRI6UfFw==", + "dev": true, + "dependencies": { + "@babel/highlight": "^7.14.5" + }, + "engines": { + "node": ">=6.9.0" + } + }, + "node_modules/@babel/compat-data": { + "version": "7.15.0", + "resolved": "https://registry.npmjs.org/@babel/compat-data/-/compat-data-7.15.0.tgz", + "integrity": "sha512-0NqAC1IJE0S0+lL1SWFMxMkz1pKCNCjI4tr2Zx4LJSXxCLAdr6KyArnY+sno5m3yH9g737ygOyPABDsnXkpxiA==", + "dev": true, + "engines": { + "node": ">=6.9.0" + } + }, + "node_modules/@babel/core": { + "version": "7.15.0", + "resolved": "https://registry.npmjs.org/@babel/core/-/core-7.15.0.tgz", + "integrity": "sha512-tXtmTminrze5HEUPn/a0JtOzzfp0nk+UEXQ/tqIJo3WDGypl/2OFQEMll/zSFU8f/lfmfLXvTaORHF3cfXIQMw==", + "dev": true, + "dependencies": { + "@babel/code-frame": "^7.14.5", + "@babel/generator": "^7.15.0", + "@babel/helper-compilation-targets": "^7.15.0", + "@babel/helper-module-transforms": "^7.15.0", + "@babel/helpers": "^7.14.8", + "@babel/parser": "^7.15.0", + "@babel/template": "^7.14.5", + "@babel/traverse": "^7.15.0", + "@babel/types": "^7.15.0", + "convert-source-map": "^1.7.0", + "debug": "^4.1.0", + "gensync": "^1.0.0-beta.2", + "json5": "^2.1.2", + "semver": "^6.3.0", + "source-map": "^0.5.0" + }, + "engines": { + "node": ">=6.9.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/babel" + } + }, + "node_modules/@babel/generator": { + "version": "7.15.0", + "resolved": "https://registry.npmjs.org/@babel/generator/-/generator-7.15.0.tgz", + "integrity": "sha512-eKl4XdMrbpYvuB505KTta4AV9g+wWzmVBW69tX0H2NwKVKd2YJbKgyK6M8j/rgLbmHOYJn6rUklV677nOyJrEQ==", + "dev": true, + "dependencies": { + "@babel/types": "^7.15.0", + "jsesc": "^2.5.1", + "source-map": "^0.5.0" + }, + "engines": { + "node": ">=6.9.0" + } + }, + "node_modules/@babel/helper-compilation-targets": { + "version": "7.15.0", + "resolved": "https://registry.npmjs.org/@babel/helper-compilation-targets/-/helper-compilation-targets-7.15.0.tgz", + "integrity": "sha512-h+/9t0ncd4jfZ8wsdAsoIxSa61qhBYlycXiHWqJaQBCXAhDCMbPRSMTGnZIkkmt1u4ag+UQmuqcILwqKzZ4N2A==", + "dev": true, + "dependencies": { + "@babel/compat-data": "^7.15.0", + "@babel/helper-validator-option": "^7.14.5", + "browserslist": "^4.16.6", + "semver": "^6.3.0" + }, + "engines": { + "node": ">=6.9.0" + }, + "peerDependencies": { + "@babel/core": "^7.0.0" + } + }, + "node_modules/@babel/helper-function-name": { + "version": "7.14.5", + "resolved": "https://registry.npmjs.org/@babel/helper-function-name/-/helper-function-name-7.14.5.tgz", + "integrity": "sha512-Gjna0AsXWfFvrAuX+VKcN/aNNWonizBj39yGwUzVDVTlMYJMK2Wp6xdpy72mfArFq5uK+NOuexfzZlzI1z9+AQ==", + "dev": true, + "dependencies": { + "@babel/helper-get-function-arity": "^7.14.5", + "@babel/template": "^7.14.5", + "@babel/types": "^7.14.5" + }, + "engines": { + "node": ">=6.9.0" + } + }, + "node_modules/@babel/helper-get-function-arity": { + "version": "7.14.5", + "resolved": "https://registry.npmjs.org/@babel/helper-get-function-arity/-/helper-get-function-arity-7.14.5.tgz", + "integrity": "sha512-I1Db4Shst5lewOM4V+ZKJzQ0JGGaZ6VY1jYvMghRjqs6DWgxLCIyFt30GlnKkfUeFLpJt2vzbMVEXVSXlIFYUg==", + "dev": true, + "dependencies": { + "@babel/types": "^7.14.5" + }, + "engines": { + "node": ">=6.9.0" + } + }, + "node_modules/@babel/helper-hoist-variables": { + "version": "7.14.5", + "resolved": "https://registry.npmjs.org/@babel/helper-hoist-variables/-/helper-hoist-variables-7.14.5.tgz", + "integrity": "sha512-R1PXiz31Uc0Vxy4OEOm07x0oSjKAdPPCh3tPivn/Eo8cvz6gveAeuyUUPB21Hoiif0uoPQSSdhIPS3352nvdyQ==", + "dev": true, + "dependencies": { + "@babel/types": "^7.14.5" + }, + "engines": { + "node": ">=6.9.0" + } + }, + "node_modules/@babel/helper-member-expression-to-functions": { + "version": "7.15.0", + "resolved": "https://registry.npmjs.org/@babel/helper-member-expression-to-functions/-/helper-member-expression-to-functions-7.15.0.tgz", + "integrity": "sha512-Jq8H8U2kYiafuj2xMTPQwkTBnEEdGKpT35lJEQsRRjnG0LW3neucsaMWLgKcwu3OHKNeYugfw+Z20BXBSEs2Lg==", + "dev": true, + "dependencies": { + "@babel/types": "^7.15.0" + }, + "engines": { + "node": ">=6.9.0" + } + }, + "node_modules/@babel/helper-module-imports": { + "version": "7.14.5", + "resolved": "https://registry.npmjs.org/@babel/helper-module-imports/-/helper-module-imports-7.14.5.tgz", + "integrity": "sha512-SwrNHu5QWS84XlHwGYPDtCxcA0hrSlL2yhWYLgeOc0w7ccOl2qv4s/nARI0aYZW+bSwAL5CukeXA47B/1NKcnQ==", + "dev": true, + "dependencies": { + "@babel/types": "^7.14.5" + }, + "engines": { + "node": ">=6.9.0" + } + }, + "node_modules/@babel/helper-module-transforms": { + "version": "7.15.0", + "resolved": "https://registry.npmjs.org/@babel/helper-module-transforms/-/helper-module-transforms-7.15.0.tgz", + "integrity": "sha512-RkGiW5Rer7fpXv9m1B3iHIFDZdItnO2/BLfWVW/9q7+KqQSDY5kUfQEbzdXM1MVhJGcugKV7kRrNVzNxmk7NBg==", + "dev": true, + "dependencies": { + "@babel/helper-module-imports": "^7.14.5", + "@babel/helper-replace-supers": "^7.15.0", + "@babel/helper-simple-access": "^7.14.8", + "@babel/helper-split-export-declaration": "^7.14.5", + "@babel/helper-validator-identifier": "^7.14.9", + "@babel/template": "^7.14.5", + "@babel/traverse": "^7.15.0", + "@babel/types": "^7.15.0" + }, + "engines": { + "node": ">=6.9.0" + } + }, + "node_modules/@babel/helper-optimise-call-expression": { + "version": "7.14.5", + "resolved": "https://registry.npmjs.org/@babel/helper-optimise-call-expression/-/helper-optimise-call-expression-7.14.5.tgz", + "integrity": "sha512-IqiLIrODUOdnPU9/F8ib1Fx2ohlgDhxnIDU7OEVi+kAbEZcyiF7BLU8W6PfvPi9LzztjS7kcbzbmL7oG8kD6VA==", + "dev": true, + "dependencies": { + "@babel/types": "^7.14.5" + }, + "engines": { + "node": ">=6.9.0" + } + }, + "node_modules/@babel/helper-replace-supers": { + "version": "7.15.0", + "resolved": "https://registry.npmjs.org/@babel/helper-replace-supers/-/helper-replace-supers-7.15.0.tgz", + "integrity": "sha512-6O+eWrhx+HEra/uJnifCwhwMd6Bp5+ZfZeJwbqUTuqkhIT6YcRhiZCOOFChRypOIe0cV46kFrRBlm+t5vHCEaA==", + "dev": true, + "dependencies": { + "@babel/helper-member-expression-to-functions": "^7.15.0", + "@babel/helper-optimise-call-expression": "^7.14.5", + "@babel/traverse": "^7.15.0", + "@babel/types": "^7.15.0" + }, + "engines": { + "node": ">=6.9.0" + } + }, + "node_modules/@babel/helper-simple-access": { + "version": "7.14.8", + "resolved": "https://registry.npmjs.org/@babel/helper-simple-access/-/helper-simple-access-7.14.8.tgz", + "integrity": "sha512-TrFN4RHh9gnWEU+s7JloIho2T76GPwRHhdzOWLqTrMnlas8T9O7ec+oEDNsRXndOmru9ymH9DFrEOxpzPoSbdg==", + "dev": true, + "dependencies": { + "@babel/types": "^7.14.8" + }, + "engines": { + "node": ">=6.9.0" + } + }, + "node_modules/@babel/helper-split-export-declaration": { + "version": "7.14.5", + "resolved": "https://registry.npmjs.org/@babel/helper-split-export-declaration/-/helper-split-export-declaration-7.14.5.tgz", + "integrity": "sha512-hprxVPu6e5Kdp2puZUmvOGjaLv9TCe58E/Fl6hRq4YiVQxIcNvuq6uTM2r1mT/oPskuS9CgR+I94sqAYv0NGKA==", + "dev": true, + "dependencies": { + "@babel/types": "^7.14.5" + }, + "engines": { + "node": ">=6.9.0" + } + }, + "node_modules/@babel/helper-validator-identifier": { + "version": "7.14.9", + "resolved": "https://registry.npmjs.org/@babel/helper-validator-identifier/-/helper-validator-identifier-7.14.9.tgz", + "integrity": "sha512-pQYxPY0UP6IHISRitNe8bsijHex4TWZXi2HwKVsjPiltzlhse2znVcm9Ace510VT1kxIHjGJCZZQBX2gJDbo0g==", + "dev": true, + "engines": { + "node": ">=6.9.0" + } + }, + "node_modules/@babel/helper-validator-option": { + "version": "7.14.5", + "resolved": "https://registry.npmjs.org/@babel/helper-validator-option/-/helper-validator-option-7.14.5.tgz", + "integrity": "sha512-OX8D5eeX4XwcroVW45NMvoYaIuFI+GQpA2a8Gi+X/U/cDUIRsV37qQfF905F0htTRCREQIB4KqPeaveRJUl3Ow==", + "dev": true, + "engines": { + "node": ">=6.9.0" + } + }, + "node_modules/@babel/helpers": { + "version": "7.15.3", + "resolved": "https://registry.npmjs.org/@babel/helpers/-/helpers-7.15.3.tgz", + "integrity": "sha512-HwJiz52XaS96lX+28Tnbu31VeFSQJGOeKHJeaEPQlTl7PnlhFElWPj8tUXtqFIzeN86XxXoBr+WFAyK2PPVz6g==", + "dev": true, + "dependencies": { + "@babel/template": "^7.14.5", + "@babel/traverse": "^7.15.0", + "@babel/types": "^7.15.0" + }, + "engines": { + "node": ">=6.9.0" + } + }, + "node_modules/@babel/highlight": { + "version": "7.14.5", + "resolved": "https://registry.npmjs.org/@babel/highlight/-/highlight-7.14.5.tgz", + "integrity": "sha512-qf9u2WFWVV0MppaL877j2dBtQIDgmidgjGk5VIMw3OadXvYaXn66U1BFlH2t4+t3i+8PhedppRv+i40ABzd+gg==", + "dev": true, + "dependencies": { + "@babel/helper-validator-identifier": "^7.14.5", + "chalk": "^2.0.0", + "js-tokens": "^4.0.0" + }, + "engines": { + "node": ">=6.9.0" + } + }, + "node_modules/@babel/highlight/node_modules/ansi-styles": { + "version": "3.2.1", + "resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-3.2.1.tgz", + "integrity": "sha512-VT0ZI6kZRdTh8YyJw3SMbYm/u+NqfsAxEpWO0Pf9sq8/e94WxxOpPKx9FR1FlyCtOVDNOQ+8ntlqFxiRc+r5qA==", + "dev": true, + "dependencies": { + "color-convert": "^1.9.0" + }, + "engines": { + "node": ">=4" + } + }, + "node_modules/@babel/highlight/node_modules/chalk": { + "version": "2.4.2", + "resolved": "https://registry.npmjs.org/chalk/-/chalk-2.4.2.tgz", + "integrity": "sha512-Mti+f9lpJNcwF4tWV8/OrTTtF1gZi+f8FqlyAdouralcFWFQWF2+NgCHShjkCb+IFBLq9buZwE1xckQU4peSuQ==", + "dev": true, + "dependencies": { + "ansi-styles": "^3.2.1", + "escape-string-regexp": "^1.0.5", + "supports-color": "^5.3.0" + }, + "engines": { + "node": ">=4" + } + }, + "node_modules/@babel/highlight/node_modules/color-convert": { + "version": "1.9.3", + "resolved": "https://registry.npmjs.org/color-convert/-/color-convert-1.9.3.tgz", + "integrity": "sha512-QfAUtd+vFdAtFQcC8CCyYt1fYWxSqAiK2cSD6zDB8N3cpsEBAvRxp9zOGg6G/SHHJYAT88/az/IuDGALsNVbGg==", + "dev": true, + "dependencies": { + "color-name": "1.1.3" + } + }, + "node_modules/@babel/highlight/node_modules/color-name": { + "version": "1.1.3", + "resolved": "https://registry.npmjs.org/color-name/-/color-name-1.1.3.tgz", + "integrity": "sha1-p9BVi9icQveV3UIyj3QIMcpTvCU=", + "dev": true + }, + "node_modules/@babel/highlight/node_modules/escape-string-regexp": { + "version": "1.0.5", + "resolved": "https://registry.npmjs.org/escape-string-regexp/-/escape-string-regexp-1.0.5.tgz", + "integrity": "sha1-G2HAViGQqN/2rjuyzwIAyhMLhtQ=", + "dev": true, + "engines": { + "node": ">=0.8.0" + } + }, + "node_modules/@babel/highlight/node_modules/has-flag": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/has-flag/-/has-flag-3.0.0.tgz", + "integrity": "sha1-tdRU3CGZriJWmfNGfloH87lVuv0=", + "dev": true, + "engines": { + "node": ">=4" + } + }, + "node_modules/@babel/highlight/node_modules/supports-color": { + "version": "5.5.0", + "resolved": "https://registry.npmjs.org/supports-color/-/supports-color-5.5.0.tgz", + "integrity": "sha512-QjVjwdXIt408MIiAqCX4oUKsgU2EqAGzs2Ppkm4aQYbjm+ZEWEcW4SfFNTr4uMNZma0ey4f5lgLrkB0aX0QMow==", + "dev": true, + "dependencies": { + "has-flag": "^3.0.0" + }, + "engines": { + "node": ">=4" + } + }, + "node_modules/@babel/parser": { + "version": "7.15.3", + "resolved": "https://registry.npmjs.org/@babel/parser/-/parser-7.15.3.tgz", + "integrity": "sha512-O0L6v/HvqbdJawj0iBEfVQMc3/6WP+AeOsovsIgBFyJaG+W2w7eqvZB7puddATmWuARlm1SX7DwxJ/JJUnDpEA==", + "dev": true, + "bin": { + "parser": "bin/babel-parser.js" + }, + "engines": { + "node": ">=6.0.0" + } + }, + "node_modules/@babel/template": { + "version": "7.14.5", + "resolved": "https://registry.npmjs.org/@babel/template/-/template-7.14.5.tgz", + "integrity": "sha512-6Z3Po85sfxRGachLULUhOmvAaOo7xCvqGQtxINai2mEGPFm6pQ4z5QInFnUrRpfoSV60BnjyF5F3c+15fxFV1g==", + "dev": true, + "dependencies": { + "@babel/code-frame": "^7.14.5", + "@babel/parser": "^7.14.5", + "@babel/types": "^7.14.5" + }, + "engines": { + "node": ">=6.9.0" + } + }, + "node_modules/@babel/traverse": { + "version": "7.15.0", + "resolved": "https://registry.npmjs.org/@babel/traverse/-/traverse-7.15.0.tgz", + "integrity": "sha512-392d8BN0C9eVxVWd8H6x9WfipgVH5IaIoLp23334Sc1vbKKWINnvwRpb4us0xtPaCumlwbTtIYNA0Dv/32sVFw==", + "dev": true, + "dependencies": { + "@babel/code-frame": "^7.14.5", + "@babel/generator": "^7.15.0", + "@babel/helper-function-name": "^7.14.5", + "@babel/helper-hoist-variables": "^7.14.5", + "@babel/helper-split-export-declaration": "^7.14.5", + "@babel/parser": "^7.15.0", + "@babel/types": "^7.15.0", + "debug": "^4.1.0", + "globals": "^11.1.0" + }, + "engines": { + "node": ">=6.9.0" + } + }, + "node_modules/@babel/types": { + "version": "7.15.0", + "resolved": "https://registry.npmjs.org/@babel/types/-/types-7.15.0.tgz", + "integrity": "sha512-OBvfqnllOIdX4ojTHpwZbpvz4j3EWyjkZEdmjH0/cgsd6QOdSgU8rLSk6ard/pcW7rlmjdVSX/AWOaORR1uNOQ==", + "dev": true, + "dependencies": { + "@babel/helper-validator-identifier": "^7.14.9", + "to-fast-properties": "^2.0.0" + }, + "engines": { + "node": ">=6.9.0" + } + }, + "node_modules/@cspotcode/source-map-consumer": { + "version": "0.8.0", + "resolved": "https://registry.npmjs.org/@cspotcode/source-map-consumer/-/source-map-consumer-0.8.0.tgz", + "integrity": "sha512-41qniHzTU8yAGbCp04ohlmSrZf8bkf/iJsl3V0dRGsQN/5GFfx+LbCSsCpp2gqrqjTVg/K6O8ycoV35JIwAzAg==", + "dev": true, + "engines": { + "node": ">= 12" + } + }, + "node_modules/@cspotcode/source-map-support": { + "version": "0.6.1", + "resolved": "https://registry.npmjs.org/@cspotcode/source-map-support/-/source-map-support-0.6.1.tgz", + "integrity": "sha512-DX3Z+T5dt1ockmPdobJS/FAsQPW4V4SrWEhD2iYQT2Cb2tQsiMnYxrcUH9By/Z3B+v0S5LMBkQtV/XOBbpLEOg==", + "dev": true, + "dependencies": { + "@cspotcode/source-map-consumer": "0.8.0" + }, + "engines": { + "node": ">=12" + } + }, + "node_modules/@iarna/toml": { + "version": "2.2.5", + "resolved": "https://registry.npmjs.org/@iarna/toml/-/toml-2.2.5.tgz", + "integrity": "sha512-trnsAYxU3xnS1gPHPyU961coFyLkh4gAD/0zQ5mymY4yOZ+CYvsPqUbOFSw0aDM4y0tV7tiFxL/1XfXPNC6IPg==", + "dev": true + }, + "node_modules/@istanbuljs/load-nyc-config": { + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/@istanbuljs/load-nyc-config/-/load-nyc-config-1.1.0.tgz", + "integrity": "sha512-VjeHSlIzpv/NyD3N0YuHfXOPDIixcA1q2ZV98wsMqcYlPmv2n3Yb2lYP9XMElnaFVXg5A7YLTeLu6V84uQDjmQ==", + "dev": true, + "dependencies": { + "camelcase": "^5.3.1", + "find-up": "^4.1.0", + "get-package-type": "^0.1.0", + "js-yaml": "^3.13.1", + "resolve-from": "^5.0.0" + }, + "engines": { + "node": ">=8" + } + }, + "node_modules/@istanbuljs/load-nyc-config/node_modules/argparse": { + "version": "1.0.10", + "resolved": "https://registry.npmjs.org/argparse/-/argparse-1.0.10.tgz", + "integrity": "sha512-o5Roy6tNG4SL/FOkCAN6RzjiakZS25RLYFrcMttJqbdd8BWrnA+fGz57iN5Pb06pvBGvl5gQ0B48dJlslXvoTg==", + "dev": true, + "dependencies": { + "sprintf-js": "~1.0.2" + } + }, + "node_modules/@istanbuljs/load-nyc-config/node_modules/find-up": { + "version": "4.1.0", + "resolved": "https://registry.npmjs.org/find-up/-/find-up-4.1.0.tgz", + "integrity": "sha512-PpOwAdQ/YlXQ2vj8a3h8IipDuYRi3wceVQQGYWxNINccq40Anw7BlsEXCMbt1Zt+OLA6Fq9suIpIWD0OsnISlw==", + "dev": true, + "dependencies": { + "locate-path": "^5.0.0", + "path-exists": "^4.0.0" + }, + "engines": { + "node": ">=8" + } + }, + "node_modules/@istanbuljs/load-nyc-config/node_modules/js-yaml": { + "version": "3.14.1", + "resolved": "https://registry.npmjs.org/js-yaml/-/js-yaml-3.14.1.tgz", + "integrity": "sha512-okMH7OXXJ7YrN9Ok3/SXrnu4iX9yOk+25nqX4imS2npuvTYDmo/QEZoqwZkYaIDk3jVvBOTOIEgEhaLOynBS9g==", + "dev": true, + "dependencies": { + "argparse": "^1.0.7", + "esprima": "^4.0.0" + }, + "bin": { + "js-yaml": "bin/js-yaml.js" + } + }, + "node_modules/@istanbuljs/load-nyc-config/node_modules/locate-path": { + "version": "5.0.0", + "resolved": "https://registry.npmjs.org/locate-path/-/locate-path-5.0.0.tgz", + "integrity": "sha512-t7hw9pI+WvuwNJXwk5zVHpyhIqzg2qTlklJOf0mVxGSbe3Fp2VieZcduNYjaLDoy6p9uGpQEGWG87WpMKlNq8g==", + "dev": true, + "dependencies": { + "p-locate": "^4.1.0" + }, + "engines": { + "node": ">=8" + } + }, + "node_modules/@istanbuljs/load-nyc-config/node_modules/p-limit": { + "version": "2.3.0", + "resolved": "https://registry.npmjs.org/p-limit/-/p-limit-2.3.0.tgz", + "integrity": "sha512-//88mFWSJx8lxCzwdAABTJL2MyWB12+eIY7MDL2SqLmAkeKU9qxRvWuSyTjm3FUmpBEMuFfckAIqEaVGUDxb6w==", + "dev": true, + "dependencies": { + "p-try": "^2.0.0" + }, + "engines": { + "node": ">=6" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/@istanbuljs/load-nyc-config/node_modules/p-locate": { + "version": "4.1.0", + "resolved": "https://registry.npmjs.org/p-locate/-/p-locate-4.1.0.tgz", + "integrity": "sha512-R79ZZ/0wAxKGu3oYMlz8jy/kbhsNrS7SKZ7PxEHBgJ5+F2mtFW2fK2cOtBh1cHYkQsbzFV7I+EoRKe6Yt0oK7A==", + "dev": true, + "dependencies": { + "p-limit": "^2.2.0" + }, + "engines": { + "node": ">=8" + } + }, + "node_modules/@istanbuljs/nyc-config-typescript": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/@istanbuljs/nyc-config-typescript/-/nyc-config-typescript-1.0.1.tgz", + "integrity": "sha512-/gz6LgVpky205LuoOfwEZmnUtaSmdk0QIMcNFj9OvxhiMhPpKftMgZmGN7jNj7jR+lr8IB1Yks3QSSSNSxfoaQ==", + "dev": true, + "dependencies": { + "@istanbuljs/schema": "^0.1.2" + }, + "engines": { + "node": ">=8" + }, + "peerDependencies": { + "nyc": ">=15", + "source-map-support": "*", + "ts-node": "*" + } + }, + "node_modules/@istanbuljs/schema": { + "version": "0.1.3", + "resolved": "https://registry.npmjs.org/@istanbuljs/schema/-/schema-0.1.3.tgz", + "integrity": "sha512-ZXRY4jNvVgSVQ8DL3LTcakaAtXwTVUxE81hslsyD2AtoXW/wVob10HkOJ1X/pAlcI7D+2YoZKg5do8G/w6RYgA==", + "dev": true, + "engines": { + "node": ">=8" + } + }, + "node_modules/@nodelib/fs.scandir": { + "version": "2.1.5", + "resolved": "https://registry.npmjs.org/@nodelib/fs.scandir/-/fs.scandir-2.1.5.tgz", + "integrity": "sha512-vq24Bq3ym5HEQm2NKCr3yXDwjc7vTsEThRDnkp2DK9p1uqLR+DHurm/NOTo0KG7HYHU7eppKZj3MyqYuMBf62g==", + "dev": true, + "dependencies": { + "@nodelib/fs.stat": "2.0.5", + "run-parallel": "^1.1.9" + }, + "engines": { + "node": ">= 8" + } + }, + "node_modules/@nodelib/fs.stat": { + "version": "2.0.5", + "resolved": "https://registry.npmjs.org/@nodelib/fs.stat/-/fs.stat-2.0.5.tgz", + "integrity": "sha512-RkhPPp2zrqDAQA/2jNhnztcPAlv64XdhIp7a7454A5ovI7Bukxgt7MX7udwAu3zg1DcpPU0rz3VV1SeaqvY4+A==", + "dev": true, + "engines": { + "node": ">= 8" + } + }, + "node_modules/@nodelib/fs.walk": { + "version": "1.2.8", + "resolved": "https://registry.npmjs.org/@nodelib/fs.walk/-/fs.walk-1.2.8.tgz", + "integrity": "sha512-oGB+UxlgWcgQkgwo8GcEGwemoTFt3FIO9ababBmaGwXIoBKZ+GTy0pP185beGg7Llih/NSHSV2XAs1lnznocSg==", + "dev": true, + "dependencies": { + "@nodelib/fs.scandir": "2.1.5", + "fastq": "^1.6.0" + }, + "engines": { + "node": ">= 8" + } + }, + "node_modules/@octokit/auth-token": { + "version": "2.4.5", + "resolved": "https://registry.npmjs.org/@octokit/auth-token/-/auth-token-2.4.5.tgz", + "integrity": "sha512-BpGYsPgJt05M7/L/5FoE1PiAbdxXFZkX/3kDYcsvd1v6UhlnE5e96dTDr0ezX/EFwciQxf3cNV0loipsURU+WA==", + "dev": true, + "dependencies": { + "@octokit/types": "^6.0.3" + } + }, + "node_modules/@octokit/core": { + "version": "3.5.1", + "resolved": "https://registry.npmjs.org/@octokit/core/-/core-3.5.1.tgz", + "integrity": "sha512-omncwpLVxMP+GLpLPgeGJBF6IWJFjXDS5flY5VbppePYX9XehevbDykRH9PdCdvqt9TS5AOTiDide7h0qrkHjw==", + "dev": true, + "dependencies": { + "@octokit/auth-token": "^2.4.4", + "@octokit/graphql": "^4.5.8", + "@octokit/request": "^5.6.0", + "@octokit/request-error": "^2.0.5", + "@octokit/types": "^6.0.3", + "before-after-hook": "^2.2.0", + "universal-user-agent": "^6.0.0" + } + }, + "node_modules/@octokit/endpoint": { + "version": "6.0.12", + "resolved": "https://registry.npmjs.org/@octokit/endpoint/-/endpoint-6.0.12.tgz", + "integrity": "sha512-lF3puPwkQWGfkMClXb4k/eUT/nZKQfxinRWJrdZaJO85Dqwo/G0yOC434Jr2ojwafWJMYqFGFa5ms4jJUgujdA==", + "dev": true, + "dependencies": { + "@octokit/types": "^6.0.3", + "is-plain-object": "^5.0.0", + "universal-user-agent": "^6.0.0" + } + }, + "node_modules/@octokit/graphql": { + "version": "4.7.0", + "resolved": "https://registry.npmjs.org/@octokit/graphql/-/graphql-4.7.0.tgz", + "integrity": "sha512-diY0qMPyQjfu4rDu3kDhJ9qIZadIm4IISO3RJSv9ajYUWJUCO0AykbgzLcg1xclxtXgzY583u3gAv66M6zz5SA==", + "dev": true, + "dependencies": { + "@octokit/request": "^5.6.0", + "@octokit/types": "^6.0.3", + "universal-user-agent": "^6.0.0" + } + }, + "node_modules/@octokit/openapi-types": { + "version": "9.7.0", + "resolved": "https://registry.npmjs.org/@octokit/openapi-types/-/openapi-types-9.7.0.tgz", + "integrity": "sha512-TUJ16DJU8mekne6+KVcMV5g6g/rJlrnIKn7aALG9QrNpnEipFc1xjoarh0PKaAWf2Hf+HwthRKYt+9mCm5RsRg==", + "dev": true + }, + "node_modules/@octokit/plugin-paginate-rest": { + "version": "2.15.1", + "resolved": "https://registry.npmjs.org/@octokit/plugin-paginate-rest/-/plugin-paginate-rest-2.15.1.tgz", + "integrity": "sha512-47r52KkhQDkmvUKZqXzA1lKvcyJEfYh3TKAIe5+EzMeyDM3d+/s5v11i2gTk8/n6No6DPi3k5Ind6wtDbo/AEg==", + "dev": true, + "dependencies": { + "@octokit/types": "^6.24.0" + }, + "peerDependencies": { + "@octokit/core": ">=2" + } + }, + "node_modules/@octokit/plugin-request-log": { + "version": "1.0.4", + "resolved": "https://registry.npmjs.org/@octokit/plugin-request-log/-/plugin-request-log-1.0.4.tgz", + "integrity": "sha512-mLUsMkgP7K/cnFEw07kWqXGF5LKrOkD+lhCrKvPHXWDywAwuDUeDwWBpc69XK3pNX0uKiVt8g5z96PJ6z9xCFA==", + "dev": true, + "peerDependencies": { + "@octokit/core": ">=3" + } + }, + "node_modules/@octokit/plugin-rest-endpoint-methods": { + "version": "5.7.0", + "resolved": "https://registry.npmjs.org/@octokit/plugin-rest-endpoint-methods/-/plugin-rest-endpoint-methods-5.7.0.tgz", + "integrity": "sha512-G7sgccWRYQMwcHJXkDY/sDxbXeKiZkFQqUtzBCwmrzCNj2GQf3VygQ4T/BFL2crLVpIbenkE/c0ErhYOte2MPw==", + "dev": true, + "dependencies": { + "@octokit/types": "^6.24.0", + "deprecation": "^2.3.1" + }, + "peerDependencies": { + "@octokit/core": ">=3" + } + }, + "node_modules/@octokit/request": { + "version": "5.6.1", + "resolved": "https://registry.npmjs.org/@octokit/request/-/request-5.6.1.tgz", + "integrity": "sha512-Ls2cfs1OfXaOKzkcxnqw5MR6drMA/zWX/LIS/p8Yjdz7QKTPQLMsB3R+OvoxE6XnXeXEE2X7xe4G4l4X0gRiKQ==", + "dev": true, + "dependencies": { + "@octokit/endpoint": "^6.0.1", + "@octokit/request-error": "^2.1.0", + "@octokit/types": "^6.16.1", + "is-plain-object": "^5.0.0", + "node-fetch": "^2.6.1", + "universal-user-agent": "^6.0.0" + } + }, + "node_modules/@octokit/request-error": { + "version": "2.1.0", + "resolved": "https://registry.npmjs.org/@octokit/request-error/-/request-error-2.1.0.tgz", + "integrity": "sha512-1VIvgXxs9WHSjicsRwq8PlR2LR2x6DwsJAaFgzdi0JfJoGSO8mYI/cHJQ+9FbN21aa+DrgNLnwObmyeSC8Rmpg==", + "dev": true, + "dependencies": { + "@octokit/types": "^6.0.3", + "deprecation": "^2.0.0", + "once": "^1.4.0" + } + }, + "node_modules/@octokit/rest": { + "version": "18.9.0", + "resolved": "https://registry.npmjs.org/@octokit/rest/-/rest-18.9.0.tgz", + "integrity": "sha512-VrmrE8gjpuOoDAGjrQq2j9ZhOE6LxaqxaQg0yMrrEnnQZy2ZcAnr5qbVfKsMF0up/48PRV/VFS/2GSMhA7nTdA==", + "dev": true, + "dependencies": { + "@octokit/core": "^3.5.0", + "@octokit/plugin-paginate-rest": "^2.6.2", + "@octokit/plugin-request-log": "^1.0.2", + "@octokit/plugin-rest-endpoint-methods": "5.7.0" + } + }, + "node_modules/@octokit/types": { + "version": "6.25.0", + "resolved": "https://registry.npmjs.org/@octokit/types/-/types-6.25.0.tgz", + "integrity": "sha512-bNvyQKfngvAd/08COlYIN54nRgxskmejgywodizQNyiKoXmWRAjKup2/LYwm+T9V0gsKH6tuld1gM0PzmOiB4Q==", + "dev": true, + "dependencies": { + "@octokit/openapi-types": "^9.5.0" + } + }, + "node_modules/@sindresorhus/is": { + "version": "4.0.1", + "resolved": "https://registry.npmjs.org/@sindresorhus/is/-/is-4.0.1.tgz", + "integrity": "sha512-Qm9hBEBu18wt1PO2flE7LPb30BHMQt1eQgbV76YntdNk73XZGpn3izvGTYxbGgzXKgbCjiia0uxTd3aTNQrY/g==", + "dev": true, + "engines": { + "node": ">=10" + }, + "funding": { + "url": "https://github.com/sindresorhus/is?sponsor=1" + } + }, + "node_modules/@sinonjs/commons": { + "version": "1.8.3", + "resolved": "https://registry.npmjs.org/@sinonjs/commons/-/commons-1.8.3.tgz", + "integrity": "sha512-xkNcLAn/wZaX14RPlwizcKicDk9G3F8m2nU3L7Ukm5zBgTwiT0wsoFAHx9Jq56fJA1z/7uKGtCRu16sOUCLIHQ==", + "dev": true, + "dependencies": { + "type-detect": "4.0.8" + } + }, + "node_modules/@sinonjs/fake-timers": { + "version": "7.1.2", + "resolved": "https://registry.npmjs.org/@sinonjs/fake-timers/-/fake-timers-7.1.2.tgz", + "integrity": "sha512-iQADsW4LBMISqZ6Ci1dupJL9pprqwcVFTcOsEmQOEhW+KLCVn/Y4Jrvg2k19fIHCp+iFprriYPTdRcQR8NbUPg==", + "dev": true, + "dependencies": { + "@sinonjs/commons": "^1.7.0" + } + }, + "node_modules/@sinonjs/samsam": { + "version": "6.0.2", + "resolved": "https://registry.npmjs.org/@sinonjs/samsam/-/samsam-6.0.2.tgz", + "integrity": "sha512-jxPRPp9n93ci7b8hMfJOFDPRLFYadN6FSpeROFTR4UNF4i5b+EK6m4QXPO46BDhFgRy1JuS87zAnFOzCUwMJcQ==", + "dev": true, + "dependencies": { + "@sinonjs/commons": "^1.6.0", + "lodash.get": "^4.4.2", + "type-detect": "^4.0.8" + } + }, + "node_modules/@sinonjs/text-encoding": { + "version": "0.7.1", + "resolved": "https://registry.npmjs.org/@sinonjs/text-encoding/-/text-encoding-0.7.1.tgz", + "integrity": "sha512-+iTbntw2IZPb/anVDbypzfQa+ay64MW0Zo8aJ8gZPWMMK6/OubMVb6lUPMagqjOPnmtauXnFCACVl3O7ogjeqQ==", + "dev": true + }, + "node_modules/@szmarczak/http-timer": { + "version": "4.0.6", + "resolved": "https://registry.npmjs.org/@szmarczak/http-timer/-/http-timer-4.0.6.tgz", + "integrity": "sha512-4BAffykYOgO+5nzBWYwE3W90sBgLJoUPRWWcL8wlyiM8IB8ipJz3UMJ9KXQd1RKQXpKp8Tutn80HZtWsu2u76w==", + "dev": true, + "dependencies": { + "defer-to-connect": "^2.0.0" + }, + "engines": { + "node": ">=10" + } + }, + "node_modules/@tsconfig/node10": { + "version": "1.0.8", + "resolved": "https://registry.npmjs.org/@tsconfig/node10/-/node10-1.0.8.tgz", + "integrity": "sha512-6XFfSQmMgq0CFLY1MslA/CPUfhIL919M1rMsa5lP2P097N2Wd1sSX0tx1u4olM16fLNhtHZpRhedZJphNJqmZg==", + "dev": true + }, + "node_modules/@tsconfig/node12": { + "version": "1.0.9", + "resolved": "https://registry.npmjs.org/@tsconfig/node12/-/node12-1.0.9.tgz", + "integrity": "sha512-/yBMcem+fbvhSREH+s14YJi18sp7J9jpuhYByADT2rypfajMZZN4WQ6zBGgBKp53NKmqI36wFYDb3yaMPurITw==", + "dev": true + }, + "node_modules/@tsconfig/node14": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/@tsconfig/node14/-/node14-1.0.1.tgz", + "integrity": "sha512-509r2+yARFfHHE7T6Puu2jjkoycftovhXRqW328PDXTVGKihlb1P8Z9mMZH04ebyajfRY7dedfGynlrFHJUQCg==", + "dev": true + }, + "node_modules/@tsconfig/node16": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/@tsconfig/node16/-/node16-1.0.2.tgz", + "integrity": "sha512-eZxlbI8GZscaGS7kkc/trHTT5xgrjH3/1n2JDwusC9iahPKWMRvRjJSAN5mCXviuTGQ/lHnhvv8Q1YTpnfz9gA==", + "dev": true + }, + "node_modules/@types/cacheable-request": { + "version": "6.0.2", + "resolved": "https://registry.npmjs.org/@types/cacheable-request/-/cacheable-request-6.0.2.tgz", + "integrity": "sha512-B3xVo+dlKM6nnKTcmm5ZtY/OL8bOAOd2Olee9M1zft65ox50OzjEHW91sDiU9j6cvW8Ejg1/Qkf4xd2kugApUA==", + "dev": true, + "dependencies": { + "@types/http-cache-semantics": "*", + "@types/keyv": "*", + "@types/node": "*", + "@types/responselike": "*" + } + }, + "node_modules/@types/http-cache-semantics": { + "version": "4.0.1", + "resolved": "https://registry.npmjs.org/@types/http-cache-semantics/-/http-cache-semantics-4.0.1.tgz", + "integrity": "sha512-SZs7ekbP8CN0txVG2xVRH6EgKmEm31BOxA07vkFaETzZz1xh+cbt8BcI0slpymvwhx5dlFnQG2rTlPVQn+iRPQ==", + "dev": true + }, + "node_modules/@types/keyv": { + "version": "3.1.2", + "resolved": "https://registry.npmjs.org/@types/keyv/-/keyv-3.1.2.tgz", + "integrity": "sha512-/FvAK2p4jQOaJ6CGDHJTqZcUtbZe820qIeTg7o0Shg7drB4JHeL+V/dhSaly7NXx6u8eSee+r7coT+yuJEvDLg==", + "dev": true, + "dependencies": { + "@types/node": "*" + } + }, + "node_modules/@types/mocha": { + "version": "9.0.0", + "resolved": "https://registry.npmjs.org/@types/mocha/-/mocha-9.0.0.tgz", + "integrity": "sha512-scN0hAWyLVAvLR9AyW7HoFF5sJZglyBsbPuHO4fv7JRvfmPBMfp1ozWqOf/e4wwPNxezBZXRfWzMb6iFLgEVRA==", + "dev": true + }, + "node_modules/@types/node": { + "version": "16.7.8", + "resolved": "https://registry.npmjs.org/@types/node/-/node-16.7.8.tgz", + "integrity": "sha512-8upnoQU0OPzbIkm+ZMM0zCeFCkw2s3mS0IWdx0+AAaWqm4fkBb0UJp8Edl7FVKRamYbpJC/aVsHpKWBIbiC7Zg==", + "dev": true + }, + "node_modules/@types/parse-json": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/@types/parse-json/-/parse-json-4.0.0.tgz", + "integrity": "sha512-//oorEZjL6sbPcKUaCdIGlIUeH26mgzimjBB77G6XRgnDl/L5wOnpyBGRe/Mmf5CVW3PwEBE1NjiMZ/ssFh4wA==", + "dev": true + }, + "node_modules/@types/responselike": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/@types/responselike/-/responselike-1.0.0.tgz", + "integrity": "sha512-85Y2BjiufFzaMIlvJDvTTB8Fxl2xfLo4HgmHzVBz08w4wDePCTjYw66PdrolO0kzli3yam/YCgRufyo1DdQVTA==", + "dev": true, + "dependencies": { + "@types/node": "*" + } + }, + "node_modules/@types/sinon": { + "version": "10.0.2", + "resolved": "https://registry.npmjs.org/@types/sinon/-/sinon-10.0.2.tgz", + "integrity": "sha512-BHn8Bpkapj8Wdfxvh2jWIUoaYB/9/XhsL0oOvBfRagJtKlSl9NWPcFOz2lRukI9szwGxFtYZCTejJSqsGDbdmw==", + "dev": true, + "dependencies": { + "@sinonjs/fake-timers": "^7.1.0" + } + }, + "node_modules/@types/which": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/@types/which/-/which-2.0.1.tgz", + "integrity": "sha512-Jjakcv8Roqtio6w1gr0D7y6twbhx6gGgFGF5BLwajPpnOIOxFkakFhCq+LmyyeAz7BX6ULrjBOxdKaCDy+4+dQ==", + "dev": true + }, + "node_modules/@types/yallist": { + "version": "4.0.1", + "resolved": "https://registry.npmjs.org/@types/yallist/-/yallist-4.0.1.tgz", + "integrity": "sha512-G3FNJfaYtN8URU6wd6+uwFI62KO79j7n3XTYcwcFncP8gkfoi0b821GoVVt0oqKVnCqKYOMNKIGpakPoFhzAGA==", + "dev": true + }, + "node_modules/@ungap/promise-all-settled": { + "version": "1.1.2", + "resolved": "https://registry.npmjs.org/@ungap/promise-all-settled/-/promise-all-settled-1.1.2.tgz", + "integrity": "sha512-sL/cEvJWAnClXw0wHk85/2L0G6Sj8UB0Ctc1TEMbKSsmpRosqhwj9gWgFRZSrBr2f9tiXISwNhCPmlfqUqyb9Q==", + "dev": true + }, + "node_modules/acorn": { + "version": "8.4.1", + "resolved": "https://registry.npmjs.org/acorn/-/acorn-8.4.1.tgz", + "integrity": "sha512-asabaBSkEKosYKMITunzX177CXxQ4Q8BSSzMTKD+FefUhipQC70gfW5SiUDhYQ3vk8G+81HqQk7Fv9OXwwn9KA==", + "dev": true, + "bin": { + "acorn": "bin/acorn" + }, + "engines": { + "node": ">=0.4.0" + } + }, + "node_modules/acorn-walk": { + "version": "8.1.1", + "resolved": "https://registry.npmjs.org/acorn-walk/-/acorn-walk-8.1.1.tgz", + "integrity": "sha512-FbJdceMlPHEAWJOILDk1fXD8lnTlEIWFkqtfk+MvmL5q/qlHfN7GEHcsFZWt/Tea9jRNPWUZG4G976nqAAmU9w==", + "dev": true, + "engines": { + "node": ">=0.4.0" + } + }, + "node_modules/aggregate-error": { + "version": "3.1.0", + "resolved": "https://registry.npmjs.org/aggregate-error/-/aggregate-error-3.1.0.tgz", + "integrity": "sha512-4I7Td01quW/RpocfNayFdFVk1qSuoh0E7JrbRJ16nH01HhKFQ88INq9Sd+nd72zqRySlr9BmDA8xlEJ6vJMrYA==", + "dev": true, + "dependencies": { + "clean-stack": "^2.0.0", + "indent-string": "^4.0.0" + }, + "engines": { + "node": ">=8" + } + }, + "node_modules/ansi-align": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/ansi-align/-/ansi-align-3.0.0.tgz", + "integrity": "sha512-ZpClVKqXN3RGBmKibdfWzqCY4lnjEuoNzU5T0oEFpfd/z5qJHVarukridD4juLO2FXMiwUQxr9WqQtaYa8XRYw==", + "dev": true, + "dependencies": { + "string-width": "^3.0.0" + } + }, + "node_modules/ansi-align/node_modules/ansi-regex": { + "version": "4.1.0", + "resolved": "https://registry.npmjs.org/ansi-regex/-/ansi-regex-4.1.0.tgz", + "integrity": "sha512-1apePfXM1UOSqw0o9IiFAovVz9M5S1Dg+4TrDwfMewQ6p/rmMueb7tWZjQ1rx4Loy1ArBggoqGpfqqdI4rondg==", + "dev": true, + "engines": { + "node": ">=6" + } + }, + "node_modules/ansi-align/node_modules/emoji-regex": { + "version": "7.0.3", + "resolved": "https://registry.npmjs.org/emoji-regex/-/emoji-regex-7.0.3.tgz", + "integrity": "sha512-CwBLREIQ7LvYFB0WyRvwhq5N5qPhc6PMjD6bYggFlI5YyDgl+0vxq5VHbMOFqLg7hfWzmu8T5Z1QofhmTIhItA==", + "dev": true + }, + "node_modules/ansi-align/node_modules/is-fullwidth-code-point": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/is-fullwidth-code-point/-/is-fullwidth-code-point-2.0.0.tgz", + "integrity": "sha1-o7MKXE8ZkYMWeqq5O+764937ZU8=", + "dev": true, + "engines": { + "node": ">=4" + } + }, + "node_modules/ansi-align/node_modules/string-width": { + "version": "3.1.0", + "resolved": "https://registry.npmjs.org/string-width/-/string-width-3.1.0.tgz", + "integrity": "sha512-vafcv6KjVZKSgz06oM/H6GDBrAtz8vdhQakGjFIvNrHA6y3HCF1CInLy+QLq8dTJPQ1b+KDUqDFctkdRW44e1w==", + "dev": true, + "dependencies": { + "emoji-regex": "^7.0.1", + "is-fullwidth-code-point": "^2.0.0", + "strip-ansi": "^5.1.0" + }, + "engines": { + "node": ">=6" + } + }, + "node_modules/ansi-align/node_modules/strip-ansi": { + "version": "5.2.0", + "resolved": "https://registry.npmjs.org/strip-ansi/-/strip-ansi-5.2.0.tgz", + "integrity": "sha512-DuRs1gKbBqsMKIZlrffwlug8MHkcnpjs5VPmL1PAh+mA30U0DTotfDZ0d2UUsXpPmPmMMJ6W773MaA3J+lbiWA==", + "dev": true, + "dependencies": { + "ansi-regex": "^4.1.0" + }, + "engines": { + "node": ">=6" + } + }, + "node_modules/ansi-colors": { + "version": "4.1.1", + "resolved": "https://registry.npmjs.org/ansi-colors/-/ansi-colors-4.1.1.tgz", + "integrity": "sha512-JoX0apGbHaUJBNl6yF+p6JAFYZ666/hhCGKN5t9QFjbJQKUU/g8MNbFDbvfrgKXvI1QpZplPOnwIo99lX/AAmA==", + "dev": true, + "engines": { + "node": ">=6" + } + }, + "node_modules/ansi-escapes": { + "version": "4.3.2", + "resolved": "https://registry.npmjs.org/ansi-escapes/-/ansi-escapes-4.3.2.tgz", + "integrity": "sha512-gKXj5ALrKWQLsYG9jlTRmR/xKluxHV+Z9QEwNIgCfM1/uwPMCuzVVnh5mwTd+OuBZcwSIMbqssNWRm1lE51QaQ==", + "dev": true, + "dependencies": { + "type-fest": "^0.21.3" + }, + "engines": { + "node": ">=8" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/ansi-escapes/node_modules/type-fest": { + "version": "0.21.3", + "resolved": "https://registry.npmjs.org/type-fest/-/type-fest-0.21.3.tgz", + "integrity": "sha512-t0rzBq87m3fVcduHDUFhKmyyX+9eo6WQjZvf51Ea/M0Q7+T374Jp1aUiyUl0GKxp8M/OETVHSDvmkyPgvX+X2w==", + "dev": true, + "engines": { + "node": ">=10" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/ansi-regex": { + "version": "5.0.0", + "resolved": "https://registry.npmjs.org/ansi-regex/-/ansi-regex-5.0.0.tgz", + "integrity": "sha512-bY6fj56OUQ0hU1KjFNDQuJFezqKdrAyFdIevADiqrWHwSlbmBNMHp5ak2f40Pm8JTFyM2mqxkG6ngkHO11f/lg==", + "dev": true, + "engines": { + "node": ">=8" + } + }, + "node_modules/ansi-styles": { + "version": "4.3.0", + "resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-4.3.0.tgz", + "integrity": "sha512-zbB9rCJAT1rbjiVDb2hqKFHNYLxgtk8NURxZ3IZwD3F6NtxbXZQCnnSi1Lkx+IDohdPlFp222wVALIheZJQSEg==", + "dev": true, + "dependencies": { + "color-convert": "^2.0.1" + }, + "engines": { + "node": ">=8" + }, + "funding": { + "url": "https://github.com/chalk/ansi-styles?sponsor=1" + } + }, + "node_modules/anymatch": { + "version": "3.1.2", + "resolved": "https://registry.npmjs.org/anymatch/-/anymatch-3.1.2.tgz", + "integrity": "sha512-P43ePfOAIupkguHUycrc4qJ9kz8ZiuOUijaETwX7THt0Y/GNK7v0aa8rY816xWjZ7rJdA5XdMcpVFTKMq+RvWg==", + "dev": true, + "dependencies": { + "normalize-path": "^3.0.0", + "picomatch": "^2.0.4" + }, + "engines": { + "node": ">= 8" + } + }, + "node_modules/append-transform": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/append-transform/-/append-transform-2.0.0.tgz", + "integrity": "sha512-7yeyCEurROLQJFv5Xj4lEGTy0borxepjFv1g22oAdqFu//SrAlDl1O1Nxx15SH1RoliUml6p8dwJW9jvZughhg==", + "dev": true, + "dependencies": { + "default-require-extensions": "^3.0.0" + }, + "engines": { + "node": ">=8" + } + }, + "node_modules/archy": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/archy/-/archy-1.0.0.tgz", + "integrity": "sha1-+cjBN1fMHde8N5rHeyxipcKGjEA=", + "dev": true + }, + "node_modules/arg": { + "version": "4.1.3", + "resolved": "https://registry.npmjs.org/arg/-/arg-4.1.3.tgz", + "integrity": "sha512-58S9QDqG0Xx27YwPSt9fJxivjYl432YCwfDMfZ+71RAqUrZef7LrKQZ3LHLOwCS4FLNBplP533Zx895SeOCHvA==", + "dev": true + }, + "node_modules/argparse": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/argparse/-/argparse-2.0.1.tgz", + "integrity": "sha512-8+9WqebbFzpX9OR+Wa6O29asIogeRMzcGtAINdpMHHyAg10f05aSFVBbcEqGf/PXw1EjAZ+q2/bEBg3DvurK3Q==", + "dev": true + }, + "node_modules/array-union": { + "version": "2.1.0", + "resolved": "https://registry.npmjs.org/array-union/-/array-union-2.1.0.tgz", + "integrity": "sha512-HGyxoOTYUyCM6stUe6EJgnd4EoewAI7zMdfqO+kGjnlZmBDz/cR5pf8r/cR4Wq60sL/p0IkcjUEEPwS3GFrIyw==", + "dev": true, + "engines": { + "node": ">=8" + } + }, + "node_modules/async-retry": { + "version": "1.3.1", + "resolved": "https://registry.npmjs.org/async-retry/-/async-retry-1.3.1.tgz", + "integrity": "sha512-aiieFW/7h3hY0Bq5d+ktDBejxuwR78vRu9hDUdR8rNhSaQ29VzPL4AoIRG7D/c7tdenwOcKvgPM6tIxB3cB6HA==", + "dev": true, + "dependencies": { + "retry": "0.12.0" + } + }, + "node_modules/asynckit": { + "version": "0.4.0", + "resolved": "https://registry.npmjs.org/asynckit/-/asynckit-0.4.0.tgz", + "integrity": "sha1-x57Zf380y48robyXkLzDZkdLS3k=", + "dev": true + }, + "node_modules/balanced-match": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/balanced-match/-/balanced-match-1.0.2.tgz", + "integrity": "sha512-3oSeUO0TMV67hN1AmbXsK4yaqU7tjiHlbxRDZOpH0KW9+CeX4bRAaX0Anxt0tx2MrpRpWwQaPwIlISEJhYU5Pw==", + "dev": true + }, + "node_modules/base64-js": { + "version": "1.5.1", + "resolved": "https://registry.npmjs.org/base64-js/-/base64-js-1.5.1.tgz", + "integrity": "sha512-AKpaYlHn8t4SVbOHCy+b5+KKgvR4vrsD8vbvrbiQJps7fKDTkjkDry6ji0rUJjC0kzbNePLwzxq8iypo41qeWA==", + "dev": true, + "funding": [ + { + "type": "github", + "url": "https://github.com/sponsors/feross" + }, + { + "type": "patreon", + "url": "https://www.patreon.com/feross" + }, + { + "type": "consulting", + "url": "https://feross.org/support" + } + ] + }, + "node_modules/before-after-hook": { + "version": "2.2.2", + "resolved": "https://registry.npmjs.org/before-after-hook/-/before-after-hook-2.2.2.tgz", + "integrity": "sha512-3pZEU3NT5BFUo/AD5ERPWOgQOCZITni6iavr5AUw5AUwQjMlI0kzu5btnyD39AF0gUEsDPwJT+oY1ORBJijPjQ==", + "dev": true + }, + "node_modules/binary-extensions": { + "version": "2.2.0", + "resolved": "https://registry.npmjs.org/binary-extensions/-/binary-extensions-2.2.0.tgz", + "integrity": "sha512-jDctJ/IVQbZoJykoeHbhXpOlNBqGNcwXJKJog42E5HDPUwQTSdjCHdihjj0DlnheQ7blbT6dHOafNAiS8ooQKA==", + "dev": true, + "engines": { + "node": ">=8" + } + }, + "node_modules/bl": { + "version": "4.1.0", + "resolved": "https://registry.npmjs.org/bl/-/bl-4.1.0.tgz", + "integrity": "sha512-1W07cM9gS6DcLperZfFSj+bWLtaPGSOHWhPiGzXmvVJbRLdG82sH/Kn8EtW1VqWVA54AKf2h5k5BbnIbwF3h6w==", + "dev": true, + "dependencies": { + "buffer": "^5.5.0", + "inherits": "^2.0.4", + "readable-stream": "^3.4.0" + } + }, + "node_modules/boxen": { + "version": "5.0.1", + "resolved": "https://registry.npmjs.org/boxen/-/boxen-5.0.1.tgz", + "integrity": "sha512-49VBlw+PrWEF51aCmy7QIteYPIFZxSpvqBdP/2itCPPlJ49kj9zg/XPRFrdkne2W+CfwXUls8exMvu1RysZpKA==", + "dev": true, + "dependencies": { + "ansi-align": "^3.0.0", + "camelcase": "^6.2.0", + "chalk": "^4.1.0", + "cli-boxes": "^2.2.1", + "string-width": "^4.2.0", + "type-fest": "^0.20.2", + "widest-line": "^3.1.0", + "wrap-ansi": "^7.0.0" + }, + "engines": { + "node": ">=10" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/boxen/node_modules/camelcase": { + "version": "6.2.0", + "resolved": "https://registry.npmjs.org/camelcase/-/camelcase-6.2.0.tgz", + "integrity": "sha512-c7wVvbw3f37nuobQNtgsgG9POC9qMbNuMQmTCqZv23b6MIz0fcYpBiOlv9gEN/hdLdnZTDQhg6e9Dq5M1vKvfg==", + "dev": true, + "engines": { + "node": ">=10" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/boxen/node_modules/type-fest": { + "version": "0.20.2", + "resolved": "https://registry.npmjs.org/type-fest/-/type-fest-0.20.2.tgz", + "integrity": "sha512-Ne+eE4r0/iWnpAxD852z3A+N0Bt5RN//NjJwRd2VFHEmrywxf5vsZlh4R6lixl6B+wz/8d+maTSAkN1FIkI3LQ==", + "dev": true, + "engines": { + "node": ">=10" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/brace-expansion": { + "version": "1.1.11", + "resolved": "https://registry.npmjs.org/brace-expansion/-/brace-expansion-1.1.11.tgz", + "integrity": "sha512-iCuPHDFgrHX7H2vEI/5xpz07zSHB00TpugqhmYtVmMO6518mCuRMoOYFldEBl0g187ufozdaHgWKcYFb61qGiA==", + "dev": true, + "dependencies": { + "balanced-match": "^1.0.0", + "concat-map": "0.0.1" + } + }, + "node_modules/braces": { + "version": "3.0.2", + "resolved": "https://registry.npmjs.org/braces/-/braces-3.0.2.tgz", + "integrity": "sha512-b8um+L1RzM3WDSzvhm6gIz1yfTbBt6YTlcEKAvsmqCZZFw46z626lVj9j1yEPW33H5H+lBQpZMP1k8l+78Ha0A==", + "dev": true, + "dependencies": { + "fill-range": "^7.0.1" + }, + "engines": { + "node": ">=8" + } + }, + "node_modules/browser-stdout": { + "version": "1.3.1", + "resolved": "https://registry.npmjs.org/browser-stdout/-/browser-stdout-1.3.1.tgz", + "integrity": "sha512-qhAVI1+Av2X7qelOfAIYwXONood6XlZE/fXaBSmW/T5SzLAmCgzi+eiWE7fUvbHaeNBQH13UftjpXxsfLkMpgw==", + "dev": true + }, + "node_modules/browserslist": { + "version": "4.16.8", + "resolved": "https://registry.npmjs.org/browserslist/-/browserslist-4.16.8.tgz", + "integrity": "sha512-sc2m9ohR/49sWEbPj14ZSSZqp+kbi16aLao42Hmn3Z8FpjuMaq2xCA2l4zl9ITfyzvnvyE0hcg62YkIGKxgaNQ==", + "dev": true, + "dependencies": { + "caniuse-lite": "^1.0.30001251", + "colorette": "^1.3.0", + "electron-to-chromium": "^1.3.811", + "escalade": "^3.1.1", + "node-releases": "^1.1.75" + }, + "bin": { + "browserslist": "cli.js" + }, + "engines": { + "node": "^6 || ^7 || ^8 || ^9 || ^10 || ^11 || ^12 || >=13.7" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/browserslist" + } + }, + "node_modules/buffer": { + "version": "5.7.1", + "resolved": "https://registry.npmjs.org/buffer/-/buffer-5.7.1.tgz", + "integrity": "sha512-EHcyIPBQ4BSGlvjB16k5KgAJ27CIsHY/2JBmCRReo48y9rQ3MaUzWX3KVlBa4U7MyX02HdVj0K7C3WaB3ju7FQ==", + "dev": true, + "funding": [ + { + "type": "github", + "url": "https://github.com/sponsors/feross" + }, + { + "type": "patreon", + "url": "https://www.patreon.com/feross" + }, + { + "type": "consulting", + "url": "https://feross.org/support" + } + ], + "dependencies": { + "base64-js": "^1.3.1", + "ieee754": "^1.1.13" + } + }, + "node_modules/buffer-from": { + "version": "1.1.2", + "resolved": "https://registry.npmjs.org/buffer-from/-/buffer-from-1.1.2.tgz", + "integrity": "sha512-E+XQCRwSbaaiChtv6k6Dwgc+bx+Bs6vuKJHHl5kox/BaKbhiXzqQOwK4cO22yElGp2OCmjwVhT3HmxgyPGnJfQ==", + "dev": true + }, + "node_modules/cacheable-lookup": { + "version": "5.0.4", + "resolved": "https://registry.npmjs.org/cacheable-lookup/-/cacheable-lookup-5.0.4.tgz", + "integrity": "sha512-2/kNscPhpcxrOigMZzbiWF7dz8ilhb/nIHU3EyZiXWXpeq/au8qJ8VhdftMkty3n7Gj6HIGalQG8oiBNB3AJgA==", + "dev": true, + "engines": { + "node": ">=10.6.0" + } + }, + "node_modules/cacheable-request": { + "version": "7.0.2", + "resolved": "https://registry.npmjs.org/cacheable-request/-/cacheable-request-7.0.2.tgz", + "integrity": "sha512-pouW8/FmiPQbuGpkXQ9BAPv/Mo5xDGANgSNXzTzJ8DrKGuXOssM4wIQRjfanNRh3Yu5cfYPvcorqbhg2KIJtew==", + "dev": true, + "dependencies": { + "clone-response": "^1.0.2", + "get-stream": "^5.1.0", + "http-cache-semantics": "^4.0.0", + "keyv": "^4.0.0", + "lowercase-keys": "^2.0.0", + "normalize-url": "^6.0.1", + "responselike": "^2.0.0" + }, + "engines": { + "node": ">=8" + } + }, + "node_modules/cacheable-request/node_modules/get-stream": { + "version": "5.2.0", + "resolved": "https://registry.npmjs.org/get-stream/-/get-stream-5.2.0.tgz", + "integrity": "sha512-nBF+F1rAZVCu/p7rjzgA+Yb4lfYXrpl7a6VmJrU8wF9I1CKvP/QwPNZHnOlwbTkY6dvtFIzFMSyQXbLoTQPRpA==", + "dev": true, + "dependencies": { + "pump": "^3.0.0" + }, + "engines": { + "node": ">=8" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/caching-transform": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/caching-transform/-/caching-transform-4.0.0.tgz", + "integrity": "sha512-kpqOvwXnjjN44D89K5ccQC+RUrsy7jB/XLlRrx0D7/2HNcTPqzsb6XgYoErwko6QsV184CA2YgS1fxDiiDZMWA==", + "dev": true, + "dependencies": { + "hasha": "^5.0.0", + "make-dir": "^3.0.0", + "package-hash": "^4.0.0", + "write-file-atomic": "^3.0.0" + }, + "engines": { + "node": ">=8" + } + }, + "node_modules/call-bind": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/call-bind/-/call-bind-1.0.2.tgz", + "integrity": "sha512-7O+FbCihrB5WGbFYesctwmTKae6rOiIzmz1icreWJ+0aA7LJfuqhEso2T9ncpcFtzMQtzXf2QGGueWJGTYsqrA==", + "dev": true, + "dependencies": { + "function-bind": "^1.1.1", + "get-intrinsic": "^1.0.2" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/callsites": { + "version": "3.1.0", + "resolved": "https://registry.npmjs.org/callsites/-/callsites-3.1.0.tgz", + "integrity": "sha512-P8BjAsXvZS+VIDUI11hHCQEv74YT67YUi5JJFNWIqL235sBmjX4+qx9Muvls5ivyNENctx46xQLQ3aTuE7ssaQ==", + "dev": true, + "engines": { + "node": ">=6" + } + }, + "node_modules/camelcase": { + "version": "5.3.1", + "resolved": "https://registry.npmjs.org/camelcase/-/camelcase-5.3.1.tgz", + "integrity": "sha512-L28STB170nwWS63UjtlEOE3dldQApaJXZkOI1uMFfzf3rRuPegHaHesyee+YxQ+W6SvRDQV6UrdOdRiR153wJg==", + "dev": true, + "engines": { + "node": ">=6" + } + }, + "node_modules/caniuse-lite": { + "version": "1.0.30001252", + "resolved": "https://registry.npmjs.org/caniuse-lite/-/caniuse-lite-1.0.30001252.tgz", + "integrity": "sha512-I56jhWDGMtdILQORdusxBOH+Nl/KgQSdDmpJezYddnAkVOmnoU8zwjTV9xAjMIYxr0iPreEAVylCGcmHCjfaOw==", + "dev": true, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/browserslist" + } + }, + "node_modules/chalk": { + "version": "4.1.2", + "resolved": "https://registry.npmjs.org/chalk/-/chalk-4.1.2.tgz", + "integrity": "sha512-oKnbhFyRIXpUuez8iBMmyEa4nbj4IOQyuhc/wy9kY7/WVPcwIO9VA668Pu8RkO7+0G76SLROeyw9CpQ061i4mA==", + "dev": true, + "dependencies": { + "ansi-styles": "^4.1.0", + "supports-color": "^7.1.0" + }, + "engines": { + "node": ">=10" + }, + "funding": { + "url": "https://github.com/chalk/chalk?sponsor=1" + } + }, + "node_modules/chalk/node_modules/supports-color": { + "version": "7.2.0", + "resolved": "https://registry.npmjs.org/supports-color/-/supports-color-7.2.0.tgz", + "integrity": "sha512-qpCAvRl9stuOHveKsn7HncJRvv501qIacKzQlO/+Lwxc9+0q2wLyv4Dfvt80/DPn2pqOBsJdDiogXGR9+OvwRw==", + "dev": true, + "dependencies": { + "has-flag": "^4.0.0" + }, + "engines": { + "node": ">=8" + } + }, + "node_modules/chardet": { + "version": "0.7.0", + "resolved": "https://registry.npmjs.org/chardet/-/chardet-0.7.0.tgz", + "integrity": "sha512-mT8iDcrh03qDGRRmoA2hmBJnxpllMR+0/0qlzjqZES6NdiWDcZkCNAk4rPFZ9Q85r27unkiNNg8ZOiwZXBHwcA==", + "dev": true + }, + "node_modules/chokidar": { + "version": "3.5.2", + "resolved": "https://registry.npmjs.org/chokidar/-/chokidar-3.5.2.tgz", + "integrity": "sha512-ekGhOnNVPgT77r4K/U3GDhu+FQ2S8TnK/s2KbIGXi0SZWuwkZ2QNyfWdZW+TVfn84DpEP7rLeCt2UI6bJ8GwbQ==", + "dev": true, + "dependencies": { + "anymatch": "~3.1.2", + "braces": "~3.0.2", + "glob-parent": "~5.1.2", + "is-binary-path": "~2.1.0", + "is-glob": "~4.0.1", + "normalize-path": "~3.0.0", + "readdirp": "~3.6.0" + }, + "engines": { + "node": ">= 8.10.0" + }, + "optionalDependencies": { + "fsevents": "~2.3.2" + } + }, + "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==", + "dev": true + }, + "node_modules/clean-stack": { + "version": "2.2.0", + "resolved": "https://registry.npmjs.org/clean-stack/-/clean-stack-2.2.0.tgz", + "integrity": "sha512-4diC9HaTE+KRAMWhDhrGOECgWZxoevMc5TlkObMqNSsVU62PYzXZ/SMTjzyGAFF1YusgxGcSWTEXBhp0CPwQ1A==", + "dev": true, + "engines": { + "node": ">=6" + } + }, + "node_modules/cli-boxes": { + "version": "2.2.1", + "resolved": "https://registry.npmjs.org/cli-boxes/-/cli-boxes-2.2.1.tgz", + "integrity": "sha512-y4coMcylgSCdVinjiDBuR8PCC2bLjyGTwEmPb9NHR/QaNU6EUOXcTY/s6VjGMD6ENSEaeQYHCY0GNGS5jfMwPw==", + "dev": true, + "engines": { + "node": ">=6" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/cli-cursor": { + "version": "3.1.0", + "resolved": "https://registry.npmjs.org/cli-cursor/-/cli-cursor-3.1.0.tgz", + "integrity": "sha512-I/zHAwsKf9FqGoXM4WWRACob9+SNukZTd94DWF57E4toouRulbCxcUh6RKUEOQlYTHJnzkPMySvPNaaSLNfLZw==", + "dev": true, + "dependencies": { + "restore-cursor": "^3.1.0" + }, + "engines": { + "node": ">=8" + } + }, + "node_modules/cli-spinners": { + "version": "2.6.0", + "resolved": "https://registry.npmjs.org/cli-spinners/-/cli-spinners-2.6.0.tgz", + "integrity": "sha512-t+4/y50K/+4xcCRosKkA7W4gTr1MySvLV0q+PxmG7FJ5g+66ChKurYjxBCjHggHH3HA5Hh9cy+lcUGWDqVH+4Q==", + "dev": true, + "engines": { + "node": ">=6" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/cli-width": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/cli-width/-/cli-width-3.0.0.tgz", + "integrity": "sha512-FxqpkPPwu1HjuN93Omfm4h8uIanXofW0RxVEW3k5RKx+mJJYSthzNhp32Kzxxy3YAEZ/Dc/EWN1vZRY0+kOhbw==", + "dev": true, + "engines": { + "node": ">= 10" + } + }, + "node_modules/cliui": { + "version": "7.0.4", + "resolved": "https://registry.npmjs.org/cliui/-/cliui-7.0.4.tgz", + "integrity": "sha512-OcRE68cOsVMXp1Yvonl/fzkQOyjLSu/8bhPDfQt0e0/Eb283TKP20Fs2MqoPsr9SwA595rRCA+QMzYc9nBP+JQ==", + "dev": true, + "dependencies": { + "string-width": "^4.2.0", + "strip-ansi": "^6.0.0", + "wrap-ansi": "^7.0.0" + } + }, + "node_modules/clone": { + "version": "1.0.4", + "resolved": "https://registry.npmjs.org/clone/-/clone-1.0.4.tgz", + "integrity": "sha1-2jCcwmPfFZlMaIypAheco8fNfH4=", + "dev": true, + "engines": { + "node": ">=0.8" + } + }, + "node_modules/clone-response": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/clone-response/-/clone-response-1.0.2.tgz", + "integrity": "sha1-0dyXOSAxTfZ/vrlCI7TuNQI56Ws=", + "dev": true, + "dependencies": { + "mimic-response": "^1.0.0" + } + }, + "node_modules/cluster-key-slot": { + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/cluster-key-slot/-/cluster-key-slot-1.1.0.tgz", + "integrity": "sha512-2Nii8p3RwAPiFwsnZvukotvow2rIHM+yQ6ZcBXGHdniadkYGZYiGmkHJIbZPIV9nfv7m/U1IPMVVcAhoWFeklw==", + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/color-convert": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/color-convert/-/color-convert-2.0.1.tgz", + "integrity": "sha512-RRECPsj7iu/xb5oKYcsFHSppFNnsj/52OVTRKb4zP5onXwVF3zVmmToNcOfGC+CRDpfK/U584fMg38ZHCaElKQ==", + "dev": true, + "dependencies": { + "color-name": "~1.1.4" + }, + "engines": { + "node": ">=7.0.0" + } + }, + "node_modules/color-name": { + "version": "1.1.4", + "resolved": "https://registry.npmjs.org/color-name/-/color-name-1.1.4.tgz", + "integrity": "sha512-dOy+3AuW3a2wNbZHIuMZpTcgjGuLU/uBL/ubcZF9OXbDo8ff4O8yVp5Bf0efS8uEoYo5q4Fx7dY9OgQGXgAsQA==", + "dev": true + }, + "node_modules/colorette": { + "version": "1.3.0", + "resolved": "https://registry.npmjs.org/colorette/-/colorette-1.3.0.tgz", + "integrity": "sha512-ecORCqbSFP7Wm8Y6lyqMJjexBQqXSF7SSeaTyGGphogUjBlFP9m9o08wy86HL2uB7fMTxtOUzLMk7ogKcxMg1w==", + "dev": true + }, + "node_modules/combined-stream": { + "version": "1.0.8", + "resolved": "https://registry.npmjs.org/combined-stream/-/combined-stream-1.0.8.tgz", + "integrity": "sha512-FQN4MRfuJeHf7cBbBMJFXhKSDq+2kAArBlmRBvcvFE5BB1HZKXtSFASDhdlz9zOYwxh8lDdnvmMOe/+5cdoEdg==", + "dev": true, + "dependencies": { + "delayed-stream": "~1.0.0" + }, + "engines": { + "node": ">= 0.8" + } + }, + "node_modules/commondir": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/commondir/-/commondir-1.0.1.tgz", + "integrity": "sha1-3dgA2gxmEnOTzKWVDqloo6rxJTs=", + "dev": true + }, + "node_modules/concat-map": { + "version": "0.0.1", + "resolved": "https://registry.npmjs.org/concat-map/-/concat-map-0.0.1.tgz", + "integrity": "sha1-2Klr13/Wjfd5OnMDajug1UBdR3s=", + "dev": true + }, + "node_modules/configstore": { + "version": "5.0.1", + "resolved": "https://registry.npmjs.org/configstore/-/configstore-5.0.1.tgz", + "integrity": "sha512-aMKprgk5YhBNyH25hj8wGt2+D52Sw1DRRIzqBwLp2Ya9mFmY8KPvvtvmna8SxVR9JMZ4kzMD68N22vlaRpkeFA==", + "dev": true, + "dependencies": { + "dot-prop": "^5.2.0", + "graceful-fs": "^4.1.2", + "make-dir": "^3.0.0", + "unique-string": "^2.0.0", + "write-file-atomic": "^3.0.0", + "xdg-basedir": "^4.0.0" + }, + "engines": { + "node": ">=8" + } + }, + "node_modules/convert-source-map": { + "version": "1.8.0", + "resolved": "https://registry.npmjs.org/convert-source-map/-/convert-source-map-1.8.0.tgz", + "integrity": "sha512-+OQdjP49zViI/6i7nIJpA8rAl4sV/JdPfU9nZs3VqOwGIgizICvuN2ru6fMd+4llL0tar18UYJXfZ/TWtmhUjA==", + "dev": true, + "dependencies": { + "safe-buffer": "~5.1.1" + } + }, + "node_modules/cosmiconfig": { + "version": "7.0.0", + "resolved": "https://registry.npmjs.org/cosmiconfig/-/cosmiconfig-7.0.0.tgz", + "integrity": "sha512-pondGvTuVYDk++upghXJabWzL6Kxu6f26ljFw64Swq9v6sQPUL3EUlVDV56diOjpCayKihL6hVe8exIACU4XcA==", + "dev": true, + "dependencies": { + "@types/parse-json": "^4.0.0", + "import-fresh": "^3.2.1", + "parse-json": "^5.0.0", + "path-type": "^4.0.0", + "yaml": "^1.10.0" + }, + "engines": { + "node": ">=10" + } + }, + "node_modules/create-require": { + "version": "1.1.1", + "resolved": "https://registry.npmjs.org/create-require/-/create-require-1.1.1.tgz", + "integrity": "sha512-dcKFX3jn0MpIaXjisoRvexIJVEKzaq7z2rZKxf+MSr9TkdmHmsU4m2lcLojrj/FHl8mk5VxMmYA+ftRkP/3oKQ==", + "dev": true + }, + "node_modules/cross-spawn": { + "version": "7.0.3", + "resolved": "https://registry.npmjs.org/cross-spawn/-/cross-spawn-7.0.3.tgz", + "integrity": "sha512-iRDPJKUPVEND7dHPO8rkbOnPpyDygcDFtWjpeWNCgy8WP2rXcxXL8TskReQl6OrB2G7+UJrags1q15Fudc7G6w==", + "dev": true, + "dependencies": { + "path-key": "^3.1.0", + "shebang-command": "^2.0.0", + "which": "^2.0.1" + }, + "engines": { + "node": ">= 8" + } + }, + "node_modules/crypto-random-string": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/crypto-random-string/-/crypto-random-string-2.0.0.tgz", + "integrity": "sha512-v1plID3y9r/lPhviJ1wrXpLeyUIGAZ2SHNYTEapm7/8A9nLPoyvVp3RK/EPFqn5kEznyWgYZNsRtYYIWbuG8KA==", + "dev": true, + "engines": { + "node": ">=8" + } + }, + "node_modules/debug": { + "version": "4.3.1", + "resolved": "https://registry.npmjs.org/debug/-/debug-4.3.1.tgz", + "integrity": "sha512-doEwdvm4PCeK4K3RQN2ZC2BYUBaxwLARCqZmMjtF8a51J2Rb0xpVloFRnCODwqjpwnAoao4pelN8l3RJdv3gRQ==", + "dev": true, + "dependencies": { + "ms": "2.1.2" + }, + "engines": { + "node": ">=6.0" + }, + "peerDependenciesMeta": { + "supports-color": { + "optional": true + } + } + }, + "node_modules/debug/node_modules/ms": { + "version": "2.1.2", + "resolved": "https://registry.npmjs.org/ms/-/ms-2.1.2.tgz", + "integrity": "sha512-sGkPx+VjMtmA6MX27oA4FBFELFCZZ4S4XqeGOXCv68tT+jb3vk/RyaKWP0PTKyWtmLSM0b+adUTEvbs1PEaH2w==", + "dev": true + }, + "node_modules/decamelize": { + "version": "1.2.0", + "resolved": "https://registry.npmjs.org/decamelize/-/decamelize-1.2.0.tgz", + "integrity": "sha1-9lNNFRSCabIDUue+4m9QH5oZEpA=", + "dev": true, + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/decode-uri-component": { + "version": "0.2.0", + "resolved": "https://registry.npmjs.org/decode-uri-component/-/decode-uri-component-0.2.0.tgz", + "integrity": "sha1-6zkTMzRYd1y4TNGh+uBiEGu4dUU=", + "dev": true, + "engines": { + "node": ">=0.10" + } + }, + "node_modules/decompress-response": { + "version": "6.0.0", + "resolved": "https://registry.npmjs.org/decompress-response/-/decompress-response-6.0.0.tgz", + "integrity": "sha512-aW35yZM6Bb/4oJlZncMH2LCoZtJXTRxES17vE3hoRiowU2kWHaJKFkSBDnDR+cm9J+9QhXmREyIfv0pji9ejCQ==", + "dev": true, + "dependencies": { + "mimic-response": "^3.1.0" + }, + "engines": { + "node": ">=10" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/decompress-response/node_modules/mimic-response": { + "version": "3.1.0", + "resolved": "https://registry.npmjs.org/mimic-response/-/mimic-response-3.1.0.tgz", + "integrity": "sha512-z0yWI+4FDrrweS8Zmt4Ej5HdJmky15+L2e6Wgn3+iK5fWzb6T3fhNFq2+MeTRb064c6Wr4N/wv0DzQTjNzHNGQ==", + "dev": true, + "engines": { + "node": ">=10" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/deep-extend": { + "version": "0.6.0", + "resolved": "https://registry.npmjs.org/deep-extend/-/deep-extend-0.6.0.tgz", + "integrity": "sha512-LOHxIOaPYdHlJRtCQfDIVZtfw/ufM8+rVj649RIHzcm/vGwQRXFt6OPqIFWsm2XEMrNIEtWR64sY1LEKD2vAOA==", + "dev": true, + "engines": { + "node": ">=4.0.0" + } + }, + "node_modules/default-require-extensions": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/default-require-extensions/-/default-require-extensions-3.0.0.tgz", + "integrity": "sha512-ek6DpXq/SCpvjhpFsLFRVtIxJCRw6fUR42lYMVZuUMK7n8eMz4Uh5clckdBjEpLhn/gEBZo7hDJnJcwdKLKQjg==", + "dev": true, + "dependencies": { + "strip-bom": "^4.0.0" + }, + "engines": { + "node": ">=8" + } + }, + "node_modules/defaults": { + "version": "1.0.3", + "resolved": "https://registry.npmjs.org/defaults/-/defaults-1.0.3.tgz", + "integrity": "sha1-xlYFHpgX2f8I7YgUd/P+QBnz730=", + "dev": true, + "dependencies": { + "clone": "^1.0.2" + } + }, + "node_modules/defer-to-connect": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/defer-to-connect/-/defer-to-connect-2.0.1.tgz", + "integrity": "sha512-4tvttepXG1VaYGrRibk5EwJd1t4udunSOVMdLSAL6mId1ix438oPwPZMALY41FCijukO1L0twNcGsdzS7dHgDg==", + "dev": true, + "engines": { + "node": ">=10" + } + }, + "node_modules/delayed-stream": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/delayed-stream/-/delayed-stream-1.0.0.tgz", + "integrity": "sha1-3zrhmayt+31ECqrgsp4icrJOxhk=", + "dev": true, + "engines": { + "node": ">=0.4.0" + } + }, + "node_modules/deprecated-obj": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/deprecated-obj/-/deprecated-obj-2.0.0.tgz", + "integrity": "sha512-CkdywZC2rJ8RGh+y3MM1fw1EJ4oO/oNExGbRFv0AQoMS+faTd3nO7slYjkj/6t8OnIMUE+wxh6G97YHhK1ytrw==", + "dev": true, + "dependencies": { + "flat": "^5.0.2", + "lodash": "^4.17.20" + }, + "engines": { + "node": ">=10" + } + }, + "node_modules/deprecation": { + "version": "2.3.1", + "resolved": "https://registry.npmjs.org/deprecation/-/deprecation-2.3.1.tgz", + "integrity": "sha512-xmHIy4F3scKVwMsQ4WnVaS8bHOx0DmVwRywosKhaILI0ywMDWPtBSku2HNxRvF7jtwDRsoEwYQSfbxj8b7RlJQ==", + "dev": true + }, + "node_modules/diff": { + "version": "5.0.0", + "resolved": "https://registry.npmjs.org/diff/-/diff-5.0.0.tgz", + "integrity": "sha512-/VTCrvm5Z0JGty/BWHljh+BAiw3IK+2j87NGMu8Nwc/f48WoDAC395uomO9ZD117ZOBaHmkX1oyLvkVM/aIT3w==", + "dev": true, + "engines": { + "node": ">=0.3.1" + } + }, + "node_modules/dir-glob": { + "version": "3.0.1", + "resolved": "https://registry.npmjs.org/dir-glob/-/dir-glob-3.0.1.tgz", + "integrity": "sha512-WkrWp9GR4KXfKGYzOLmTuGVi1UWFfws377n9cc55/tb6DuqyF6pcQ5AbiHEshaDpY9v6oaSr2XCDidGmMwdzIA==", + "dev": true, + "dependencies": { + "path-type": "^4.0.0" + }, + "engines": { + "node": ">=8" + } + }, + "node_modules/dot-prop": { + "version": "5.3.0", + "resolved": "https://registry.npmjs.org/dot-prop/-/dot-prop-5.3.0.tgz", + "integrity": "sha512-QM8q3zDe58hqUqjraQOmzZ1LIH9SWQJTlEKCH4kJ2oQvLZk7RbQXvtDM2XEq3fwkV9CCvvH4LA0AV+ogFsBM2Q==", + "dev": true, + "dependencies": { + "is-obj": "^2.0.0" + }, + "engines": { + "node": ">=8" + } + }, + "node_modules/duplexer3": { + "version": "0.1.4", + "resolved": "https://registry.npmjs.org/duplexer3/-/duplexer3-0.1.4.tgz", + "integrity": "sha1-7gHdHKwO08vH/b6jfcCo8c4ALOI=", + "dev": true + }, + "node_modules/electron-to-chromium": { + "version": "1.3.822", + "resolved": "https://registry.npmjs.org/electron-to-chromium/-/electron-to-chromium-1.3.822.tgz", + "integrity": "sha512-k7jG5oYYHxF4jx6PcqwHX3JVME/OjzolqOZiIogi9xtsfsmTjTdie4x88OakYFPEa8euciTgCCzvVNwvmjHb1Q==", + "dev": true + }, + "node_modules/emoji-regex": { + "version": "8.0.0", + "resolved": "https://registry.npmjs.org/emoji-regex/-/emoji-regex-8.0.0.tgz", + "integrity": "sha512-MSjYzcWNOA0ewAHpz0MxpYFvwg6yjy1NG3xteoqz644VCo/RPgnr1/GGt+ic3iJTzQ8Eu3TdM14SawnVUmGE6A==", + "dev": true + }, + "node_modules/end-of-stream": { + "version": "1.4.4", + "resolved": "https://registry.npmjs.org/end-of-stream/-/end-of-stream-1.4.4.tgz", + "integrity": "sha512-+uw1inIHVPQoaVuHzRyXd21icM+cnt4CzD5rW+NC1wjOUSTOs+Te7FOv7AhN7vS9x/oIyhLP5PR1H+phQAHu5Q==", + "dev": true, + "dependencies": { + "once": "^1.4.0" + } + }, + "node_modules/error-ex": { + "version": "1.3.2", + "resolved": "https://registry.npmjs.org/error-ex/-/error-ex-1.3.2.tgz", + "integrity": "sha512-7dFHNmqeFSEt2ZBsCriorKnn3Z2pj+fd9kmI6QoWw4//DL+icEBfc0U7qJCisqrTsKTjw4fNFy2pW9OqStD84g==", + "dev": true, + "dependencies": { + "is-arrayish": "^0.2.1" + } + }, + "node_modules/es6-error": { + "version": "4.1.1", + "resolved": "https://registry.npmjs.org/es6-error/-/es6-error-4.1.1.tgz", + "integrity": "sha512-Um/+FxMr9CISWh0bi5Zv0iOD+4cFh5qLeks1qhAopKVAJw3drgKbKySikp7wGhDL0HPeaja0P5ULZrxLkniUVg==", + "dev": true + }, + "node_modules/escalade": { + "version": "3.1.1", + "resolved": "https://registry.npmjs.org/escalade/-/escalade-3.1.1.tgz", + "integrity": "sha512-k0er2gUkLf8O0zKJiAhmkTnJlTvINGv7ygDNPbeIsX/TJjGJZHuh9B2UxbsaEkmlEo9MfhrSzmhIlhRlI2GXnw==", + "dev": true, + "engines": { + "node": ">=6" + } + }, + "node_modules/escape-goat": { + "version": "2.1.1", + "resolved": "https://registry.npmjs.org/escape-goat/-/escape-goat-2.1.1.tgz", + "integrity": "sha512-8/uIhbG12Csjy2JEW7D9pHbreaVaS/OpN3ycnyvElTdwM5n6GY6W6e2IPemfvGZeUMqZ9A/3GqIZMgKnBhAw/Q==", + "dev": true, + "engines": { + "node": ">=8" + } + }, + "node_modules/escape-string-regexp": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/escape-string-regexp/-/escape-string-regexp-4.0.0.tgz", + "integrity": "sha512-TtpcNJ3XAzx3Gq8sWRzJaVajRs0uVxA2YAkdb1jm2YkPz4G6egUFAyA3n5vtEIZefPk5Wa4UXbKuS5fKkJWdgA==", + "dev": true, + "engines": { + "node": ">=10" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/esprima": { + "version": "4.0.1", + "resolved": "https://registry.npmjs.org/esprima/-/esprima-4.0.1.tgz", + "integrity": "sha512-eGuFFw7Upda+g4p+QHvnW0RyTX/SVeJBDM/gCtMARO0cLuT2HcEKnTPvhjV6aGeqrCB/sbNop0Kszm0jsaWU4A==", + "dev": true, + "bin": { + "esparse": "bin/esparse.js", + "esvalidate": "bin/esvalidate.js" + }, + "engines": { + "node": ">=4" + } + }, + "node_modules/execa": { + "version": "5.1.1", + "resolved": "https://registry.npmjs.org/execa/-/execa-5.1.1.tgz", + "integrity": "sha512-8uSpZZocAZRBAPIEINJj3Lo9HyGitllczc27Eh5YYojjMFMn8yHMDMaUHE2Jqfq05D/wucwI4JGURyXt1vchyg==", + "dev": true, + "dependencies": { + "cross-spawn": "^7.0.3", + "get-stream": "^6.0.0", + "human-signals": "^2.1.0", + "is-stream": "^2.0.0", + "merge-stream": "^2.0.0", + "npm-run-path": "^4.0.1", + "onetime": "^5.1.2", + "signal-exit": "^3.0.3", + "strip-final-newline": "^2.0.0" + }, + "engines": { + "node": ">=10" + }, + "funding": { + "url": "https://github.com/sindresorhus/execa?sponsor=1" + } + }, + "node_modules/external-editor": { + "version": "3.1.0", + "resolved": "https://registry.npmjs.org/external-editor/-/external-editor-3.1.0.tgz", + "integrity": "sha512-hMQ4CX1p1izmuLYyZqLMO/qGNw10wSv9QDCPfzXfyFrOaCSSoRfqE1Kf1s5an66J5JZC62NewG+mK49jOCtQew==", + "dev": true, + "dependencies": { + "chardet": "^0.7.0", + "iconv-lite": "^0.4.24", + "tmp": "^0.0.33" + }, + "engines": { + "node": ">=4" + } + }, + "node_modules/fast-glob": { + "version": "3.2.7", + "resolved": "https://registry.npmjs.org/fast-glob/-/fast-glob-3.2.7.tgz", + "integrity": "sha512-rYGMRwip6lUMvYD3BTScMwT1HtAs2d71SMv66Vrxs0IekGZEjhM0pcMfjQPnknBt2zeCwQMEupiN02ZP4DiT1Q==", + "dev": true, + "dependencies": { + "@nodelib/fs.stat": "^2.0.2", + "@nodelib/fs.walk": "^1.2.3", + "glob-parent": "^5.1.2", + "merge2": "^1.3.0", + "micromatch": "^4.0.4" + }, + "engines": { + "node": ">=8" + } + }, + "node_modules/fastq": { + "version": "1.12.0", + "resolved": "https://registry.npmjs.org/fastq/-/fastq-1.12.0.tgz", + "integrity": "sha512-VNX0QkHK3RsXVKr9KrlUv/FoTa0NdbYoHHl7uXHv2rzyHSlxjdNAKug2twd9luJxpcyNeAgf5iPPMutJO67Dfg==", + "dev": true, + "dependencies": { + "reusify": "^1.0.4" + } + }, + "node_modules/figures": { + "version": "3.2.0", + "resolved": "https://registry.npmjs.org/figures/-/figures-3.2.0.tgz", + "integrity": "sha512-yaduQFRKLXYOGgEn6AZau90j3ggSOyiqXU0F9JZfeXYhNa+Jk4X+s45A2zg5jns87GAFa34BBm2kXw4XpNcbdg==", + "dev": true, + "dependencies": { + "escape-string-regexp": "^1.0.5" + }, + "engines": { + "node": ">=8" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/figures/node_modules/escape-string-regexp": { + "version": "1.0.5", + "resolved": "https://registry.npmjs.org/escape-string-regexp/-/escape-string-regexp-1.0.5.tgz", + "integrity": "sha1-G2HAViGQqN/2rjuyzwIAyhMLhtQ=", + "dev": true, + "engines": { + "node": ">=0.8.0" + } + }, + "node_modules/fill-range": { + "version": "7.0.1", + "resolved": "https://registry.npmjs.org/fill-range/-/fill-range-7.0.1.tgz", + "integrity": "sha512-qOo9F+dMUmC2Lcb4BbVvnKJxTPjCm+RRpe4gDuGrzkL7mEVl/djYSu2OdQ2Pa302N4oqkSg9ir6jaLWJ2USVpQ==", + "dev": true, + "dependencies": { + "to-regex-range": "^5.0.1" + }, + "engines": { + "node": ">=8" + } + }, + "node_modules/filter-obj": { + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/filter-obj/-/filter-obj-1.1.0.tgz", + "integrity": "sha1-mzERErxsYSehbgFsbF1/GeCAXFs=", + "dev": true, + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/find-cache-dir": { + "version": "3.3.2", + "resolved": "https://registry.npmjs.org/find-cache-dir/-/find-cache-dir-3.3.2.tgz", + "integrity": "sha512-wXZV5emFEjrridIgED11OoUKLxiYjAcqot/NJdAkOhlJ+vGzwhOAfcG5OX1jP+S0PcjEn8bdMJv+g2jwQ3Onig==", + "dev": true, + "dependencies": { + "commondir": "^1.0.1", + "make-dir": "^3.0.2", + "pkg-dir": "^4.1.0" + }, + "engines": { + "node": ">=8" + }, + "funding": { + "url": "https://github.com/avajs/find-cache-dir?sponsor=1" + } + }, + "node_modules/find-up": { + "version": "5.0.0", + "resolved": "https://registry.npmjs.org/find-up/-/find-up-5.0.0.tgz", + "integrity": "sha512-78/PXT1wlLLDgTzDs7sjq9hzz0vXD+zn+7wypEe4fXQxCmdmqfGsEPQxmiCSQI3ajFV91bVSsvNtrJRiW6nGng==", + "dev": true, + "dependencies": { + "locate-path": "^6.0.0", + "path-exists": "^4.0.0" + }, + "engines": { + "node": ">=10" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/flat": { + "version": "5.0.2", + "resolved": "https://registry.npmjs.org/flat/-/flat-5.0.2.tgz", + "integrity": "sha512-b6suED+5/3rTpUBdG1gupIl8MPFCAMA0QXwmljLhvCUKcUvdE4gWky9zpuGCcXHOsz4J9wPGNWq6OKpmIzz3hQ==", + "dev": true, + "bin": { + "flat": "cli.js" + } + }, + "node_modules/foreground-child": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/foreground-child/-/foreground-child-2.0.0.tgz", + "integrity": "sha512-dCIq9FpEcyQyXKCkyzmlPTFNgrCzPudOe+mhvJU5zAtlBnGVy2yKxtfsxK2tQBThwq225jcvBjpw1Gr40uzZCA==", + "dev": true, + "dependencies": { + "cross-spawn": "^7.0.0", + "signal-exit": "^3.0.2" + }, + "engines": { + "node": ">=8.0.0" + } + }, + "node_modules/form-data": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/form-data/-/form-data-4.0.0.tgz", + "integrity": "sha512-ETEklSGi5t0QMZuiXoA/Q6vcnxcLQP5vdugSpuAyi6SVGi2clPPp+xgEhuMaHC+zGgn31Kd235W35f7Hykkaww==", + "dev": true, + "dependencies": { + "asynckit": "^0.4.0", + "combined-stream": "^1.0.8", + "mime-types": "^2.1.12" + }, + "engines": { + "node": ">= 6" + } + }, + "node_modules/fromentries": { + "version": "1.3.2", + "resolved": "https://registry.npmjs.org/fromentries/-/fromentries-1.3.2.tgz", + "integrity": "sha512-cHEpEQHUg0f8XdtZCc2ZAhrHzKzT0MrFUTcvx+hfxYu7rGMDc5SKoXFh+n4YigxsHXRzc6OrCshdR1bWH6HHyg==", + "dev": true, + "funding": [ + { + "type": "github", + "url": "https://github.com/sponsors/feross" + }, + { + "type": "patreon", + "url": "https://www.patreon.com/feross" + }, + { + "type": "consulting", + "url": "https://feross.org/support" + } + ] + }, + "node_modules/fs.realpath": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/fs.realpath/-/fs.realpath-1.0.0.tgz", + "integrity": "sha1-FQStJSMVjKpA20onh8sBQRmU6k8=", + "dev": true + }, + "node_modules/fsevents": { + "version": "2.3.2", + "resolved": "https://registry.npmjs.org/fsevents/-/fsevents-2.3.2.tgz", + "integrity": "sha512-xiqMQR4xAeHTuB9uWm+fFRcIOgKBMiOBP+eXiyT7jsgVCq1bkVygt00oASowB7EdtpOHaaPgKt812P9ab+DDKA==", + "dev": true, + "hasInstallScript": true, + "optional": true, + "os": [ + "darwin" + ], + "engines": { + "node": "^8.16.0 || ^10.6.0 || >=11.0.0" + } + }, + "node_modules/function-bind": { + "version": "1.1.1", + "resolved": "https://registry.npmjs.org/function-bind/-/function-bind-1.1.1.tgz", + "integrity": "sha512-yIovAzMX49sF8Yl58fSCWJ5svSLuaibPxXQJFLmBObTuCr0Mf1KiPopGM9NiFjiYBCbfaa2Fh6breQ6ANVTI0A==", + "dev": true + }, + "node_modules/generic-pool": { + "version": "3.8.2", + "resolved": "https://registry.npmjs.org/generic-pool/-/generic-pool-3.8.2.tgz", + "integrity": "sha512-nGToKy6p3PAbYQ7p1UlWl6vSPwfwU6TMSWK7TTu+WUY4ZjyZQGniGGt2oNVvyNSpyZYSB43zMXVLcBm08MTMkg==", + "engines": { + "node": ">= 4" + } + }, + "node_modules/gensync": { + "version": "1.0.0-beta.2", + "resolved": "https://registry.npmjs.org/gensync/-/gensync-1.0.0-beta.2.tgz", + "integrity": "sha512-3hN7NaskYvMDLQY55gnW3NQ+mesEAepTqlg+VEbj7zzqEMBVNhzcGYYeqFo/TlYz6eQiFcp1HcsCZO+nGgS8zg==", + "dev": true, + "engines": { + "node": ">=6.9.0" + } + }, + "node_modules/get-caller-file": { + "version": "2.0.5", + "resolved": "https://registry.npmjs.org/get-caller-file/-/get-caller-file-2.0.5.tgz", + "integrity": "sha512-DyFP3BM/3YHTQOCUL/w0OZHR0lpKeGrxotcHWcqNEdnltqFwXVfhEBQ94eIo34AfQpo0rGki4cyIiftY06h2Fg==", + "dev": true, + "engines": { + "node": "6.* || 8.* || >= 10.*" + } + }, + "node_modules/get-intrinsic": { + "version": "1.1.1", + "resolved": "https://registry.npmjs.org/get-intrinsic/-/get-intrinsic-1.1.1.tgz", + "integrity": "sha512-kWZrnVM42QCiEA2Ig1bG8zjoIMOgxWwYCEeNdwY6Tv/cOSeGpcoX4pXHfKUxNKVoArnrEr2e9srnAxxGIraS9Q==", + "dev": true, + "dependencies": { + "function-bind": "^1.1.1", + "has": "^1.0.3", + "has-symbols": "^1.0.1" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/get-package-type": { + "version": "0.1.0", + "resolved": "https://registry.npmjs.org/get-package-type/-/get-package-type-0.1.0.tgz", + "integrity": "sha512-pjzuKtY64GYfWizNAJ0fr9VqttZkNiK2iS430LtIHzjBEr6bX8Am2zm4sW4Ro5wjWW5cAlRL1qAMTcXbjNAO2Q==", + "dev": true, + "engines": { + "node": ">=8.0.0" + } + }, + "node_modules/get-stream": { + "version": "6.0.1", + "resolved": "https://registry.npmjs.org/get-stream/-/get-stream-6.0.1.tgz", + "integrity": "sha512-ts6Wi+2j3jQjqi70w5AlN8DFnkSwC+MqmxEzdEALB2qXZYV3X/b1CTfgPLGJNMeAWxdPfU8FO1ms3NUfaHCPYg==", + "dev": true, + "engines": { + "node": ">=10" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/git-up": { + "version": "4.0.5", + "resolved": "https://registry.npmjs.org/git-up/-/git-up-4.0.5.tgz", + "integrity": "sha512-YUvVDg/vX3d0syBsk/CKUTib0srcQME0JyHkL5BaYdwLsiCslPWmDSi8PUMo9pXYjrryMcmsCoCgsTpSCJEQaA==", + "dev": true, + "dependencies": { + "is-ssh": "^1.3.0", + "parse-url": "^6.0.0" + } + }, + "node_modules/git-url-parse": { + "version": "11.5.0", + "resolved": "https://registry.npmjs.org/git-url-parse/-/git-url-parse-11.5.0.tgz", + "integrity": "sha512-TZYSMDeM37r71Lqg1mbnMlOqlHd7BSij9qN7XwTkRqSAYFMihGLGhfHwgqQob3GUhEneKnV4nskN9rbQw2KGxA==", + "dev": true, + "dependencies": { + "git-up": "^4.0.0" + } + }, + "node_modules/glob": { + "version": "7.1.7", + "resolved": "https://registry.npmjs.org/glob/-/glob-7.1.7.tgz", + "integrity": "sha512-OvD9ENzPLbegENnYP5UUfJIirTg4+XwMWGaQfQTY0JenxNvvIKP3U3/tAQSPIu/lHxXYSZmpXlUHeqAIdKzBLQ==", + "dev": true, + "dependencies": { + "fs.realpath": "^1.0.0", + "inflight": "^1.0.4", + "inherits": "2", + "minimatch": "^3.0.4", + "once": "^1.3.0", + "path-is-absolute": "^1.0.0" + }, + "engines": { + "node": "*" + }, + "funding": { + "url": "https://github.com/sponsors/isaacs" + } + }, + "node_modules/glob-parent": { + "version": "5.1.2", + "resolved": "https://registry.npmjs.org/glob-parent/-/glob-parent-5.1.2.tgz", + "integrity": "sha512-AOIgSQCepiJYwP3ARnGx+5VnTu2HBYdzbGP45eLw1vr3zB3vZLeyed1sC9hnbcOc9/SrMyM5RPQrkGz4aS9Zow==", + "dev": true, + "dependencies": { + "is-glob": "^4.0.1" + }, + "engines": { + "node": ">= 6" + } + }, + "node_modules/global-dirs": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/global-dirs/-/global-dirs-3.0.0.tgz", + "integrity": "sha512-v8ho2DS5RiCjftj1nD9NmnfaOzTdud7RRnVd9kFNOjqZbISlx5DQ+OrTkywgd0dIt7oFCvKetZSHoHcP3sDdiA==", + "dev": true, + "dependencies": { + "ini": "2.0.0" + }, + "engines": { + "node": ">=10" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/globals": { + "version": "11.12.0", + "resolved": "https://registry.npmjs.org/globals/-/globals-11.12.0.tgz", + "integrity": "sha512-WOBp/EEGUiIsJSp7wcv/y6MO+lV9UoncWqxuFfm8eBwzWNgyfBd6Gz+IeKQ9jCmyhoH99g15M3T+QaVHFjizVA==", + "dev": true, + "engines": { + "node": ">=4" + } + }, + "node_modules/globby": { + "version": "11.0.4", + "resolved": "https://registry.npmjs.org/globby/-/globby-11.0.4.tgz", + "integrity": "sha512-9O4MVG9ioZJ08ffbcyVYyLOJLk5JQ688pJ4eMGLpdWLHq/Wr1D9BlriLQyL0E+jbkuePVZXYFj47QM/v093wHg==", + "dev": true, + "dependencies": { + "array-union": "^2.1.0", + "dir-glob": "^3.0.1", + "fast-glob": "^3.1.1", + "ignore": "^5.1.4", + "merge2": "^1.3.0", + "slash": "^3.0.0" + }, + "engines": { + "node": ">=10" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "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==", + "dev": true, + "dependencies": { + "@sindresorhus/is": "^4.0.0", + "@szmarczak/http-timer": "^4.0.5", + "@types/cacheable-request": "^6.0.1", + "@types/responselike": "^1.0.0", + "cacheable-lookup": "^5.0.3", + "cacheable-request": "^7.0.1", + "decompress-response": "^6.0.0", + "http2-wrapper": "^1.0.0-beta.5.2", + "lowercase-keys": "^2.0.0", + "p-cancelable": "^2.0.0", + "responselike": "^2.0.0" + }, + "engines": { + "node": ">=10.19.0" + }, + "funding": { + "url": "https://github.com/sindresorhus/got?sponsor=1" + } + }, + "node_modules/graceful-fs": { + "version": "4.2.8", + "resolved": "https://registry.npmjs.org/graceful-fs/-/graceful-fs-4.2.8.tgz", + "integrity": "sha512-qkIilPUYcNhJpd33n0GBXTB1MMPp14TxEsEs0pTrsSVucApsYzW5V+Q8Qxhik6KU3evy+qkAAowTByymK0avdg==", + "dev": true + }, + "node_modules/growl": { + "version": "1.10.5", + "resolved": "https://registry.npmjs.org/growl/-/growl-1.10.5.tgz", + "integrity": "sha512-qBr4OuELkhPenW6goKVXiv47US3clb3/IbuWF9KNKEijAy9oeHxU9IgzjvJhHkUzhaj7rOUD7+YGWqUjLp5oSA==", + "dev": true, + "engines": { + "node": ">=4.x" + } + }, + "node_modules/handlebars": { + "version": "4.7.7", + "resolved": "https://registry.npmjs.org/handlebars/-/handlebars-4.7.7.tgz", + "integrity": "sha512-aAcXm5OAfE/8IXkcZvCepKU3VzW1/39Fb5ZuqMtgI/hT8X2YgoMvBY5dLhq/cpOvw7Lk1nK/UF71aLG/ZnVYRA==", + "dev": true, + "dependencies": { + "minimist": "^1.2.5", + "neo-async": "^2.6.0", + "source-map": "^0.6.1", + "wordwrap": "^1.0.0" + }, + "bin": { + "handlebars": "bin/handlebars" + }, + "engines": { + "node": ">=0.4.7" + }, + "optionalDependencies": { + "uglify-js": "^3.1.4" + } + }, + "node_modules/handlebars/node_modules/source-map": { + "version": "0.6.1", + "resolved": "https://registry.npmjs.org/source-map/-/source-map-0.6.1.tgz", + "integrity": "sha512-UjgapumWlbMhkBgzT7Ykc5YXUT46F0iKu8SGXq0bcwP5dz/h0Plj6enJqjz1Zbq2l5WaqYnrVbwWOWMyF3F47g==", + "dev": true, + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/has": { + "version": "1.0.3", + "resolved": "https://registry.npmjs.org/has/-/has-1.0.3.tgz", + "integrity": "sha512-f2dvO0VU6Oej7RkWJGrehjbzMAjFp5/VKPp5tTpWIV4JHHZK1/BxbFRtf/siA2SWTe09caDmVtYYzWEIbBS4zw==", + "dev": true, + "dependencies": { + "function-bind": "^1.1.1" + }, + "engines": { + "node": ">= 0.4.0" + } + }, + "node_modules/has-flag": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/has-flag/-/has-flag-4.0.0.tgz", + "integrity": "sha512-EykJT/Q1KjTWctppgIAgfSO0tKVuZUjhgMr17kqTumMl6Afv3EISleU7qZUzoXDFTAHTDC4NOoG/ZxU3EvlMPQ==", + "dev": true, + "engines": { + "node": ">=8" + } + }, + "node_modules/has-symbols": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/has-symbols/-/has-symbols-1.0.2.tgz", + "integrity": "sha512-chXa79rL/UC2KlX17jo3vRGz0azaWEx5tGqZg5pO3NUyEJVB17dMruQlzCCOfUvElghKcm5194+BCRvi2Rv/Gw==", + "dev": true, + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/has-yarn": { + "version": "2.1.0", + "resolved": "https://registry.npmjs.org/has-yarn/-/has-yarn-2.1.0.tgz", + "integrity": "sha512-UqBRqi4ju7T+TqGNdqAO0PaSVGsDGJUBQvk9eUWNGRY1CFGDzYhLWoM7JQEemnlvVcv/YEmc2wNW8BC24EnUsw==", + "dev": true, + "engines": { + "node": ">=8" + } + }, + "node_modules/hasha": { + "version": "5.2.2", + "resolved": "https://registry.npmjs.org/hasha/-/hasha-5.2.2.tgz", + "integrity": "sha512-Hrp5vIK/xr5SkeN2onO32H0MgNZ0f17HRNH39WfL0SYUNOTZ5Lz1TJ8Pajo/87dYGEFlLMm7mIc/k/s6Bvz9HQ==", + "dev": true, + "dependencies": { + "is-stream": "^2.0.0", + "type-fest": "^0.8.0" + }, + "engines": { + "node": ">=8" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/he": { + "version": "1.2.0", + "resolved": "https://registry.npmjs.org/he/-/he-1.2.0.tgz", + "integrity": "sha512-F/1DnUGPopORZi0ni+CvrCgHQ5FyEAHRLSApuYWMmrbSwoN2Mn/7k+Gl38gJnR7yyDZk6WLXwiGod1JOWNDKGw==", + "dev": true, + "bin": { + "he": "bin/he" + } + }, + "node_modules/html-escaper": { + "version": "2.0.2", + "resolved": "https://registry.npmjs.org/html-escaper/-/html-escaper-2.0.2.tgz", + "integrity": "sha512-H2iMtd0I4Mt5eYiapRdIDjp+XzelXQ0tFE4JS7YFwFevXXMmOp9myNrUvCg0D6ws8iqkRPBfKHgbwig1SmlLfg==", + "dev": true + }, + "node_modules/http-cache-semantics": { + "version": "4.1.0", + "resolved": "https://registry.npmjs.org/http-cache-semantics/-/http-cache-semantics-4.1.0.tgz", + "integrity": "sha512-carPklcUh7ROWRK7Cv27RPtdhYhUsela/ue5/jKzjegVvXDqM2ILE9Q2BGn9JZJh1g87cp56su/FgQSzcWS8cQ==", + "dev": true + }, + "node_modules/http2-wrapper": { + "version": "1.0.3", + "resolved": "https://registry.npmjs.org/http2-wrapper/-/http2-wrapper-1.0.3.tgz", + "integrity": "sha512-V+23sDMr12Wnz7iTcDeJr3O6AIxlnvT/bmaAAAP/Xda35C90p9599p0F1eHR/N1KILWSoWVAiOMFjBBXaXSMxg==", + "dev": true, + "dependencies": { + "quick-lru": "^5.1.1", + "resolve-alpn": "^1.0.0" + }, + "engines": { + "node": ">=10.19.0" + } + }, + "node_modules/human-signals": { + "version": "2.1.0", + "resolved": "https://registry.npmjs.org/human-signals/-/human-signals-2.1.0.tgz", + "integrity": "sha512-B4FFZ6q/T2jhhksgkbEW3HBvWIfDW85snkQgawt07S7J5QXTk6BkNV+0yAeZrM5QpMAdYlocGoljn0sJ/WQkFw==", + "dev": true, + "engines": { + "node": ">=10.17.0" + } + }, + "node_modules/iconv-lite": { + "version": "0.4.24", + "resolved": "https://registry.npmjs.org/iconv-lite/-/iconv-lite-0.4.24.tgz", + "integrity": "sha512-v3MXnZAcvnywkTUEZomIActle7RXXeedOR31wwl7VlyoXO4Qi9arvSenNQWne1TcRwhCL1HwLI21bEqdpj8/rA==", + "dev": true, + "dependencies": { + "safer-buffer": ">= 2.1.2 < 3" + }, + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/ieee754": { + "version": "1.2.1", + "resolved": "https://registry.npmjs.org/ieee754/-/ieee754-1.2.1.tgz", + "integrity": "sha512-dcyqhDvX1C46lXZcVqCpK+FtMRQVdIMN6/Df5js2zouUsqG7I6sFxitIC+7KYK29KdXOLHdu9zL4sFnoVQnqaA==", + "dev": true, + "funding": [ + { + "type": "github", + "url": "https://github.com/sponsors/feross" + }, + { + "type": "patreon", + "url": "https://www.patreon.com/feross" + }, + { + "type": "consulting", + "url": "https://feross.org/support" + } + ] + }, + "node_modules/ignore": { + "version": "5.1.8", + "resolved": "https://registry.npmjs.org/ignore/-/ignore-5.1.8.tgz", + "integrity": "sha512-BMpfD7PpiETpBl/A6S498BaIJ6Y/ABT93ETbby2fP00v4EbvPBXWEoaR1UBPKs3iR53pJY7EtZk5KACI57i1Uw==", + "dev": true, + "engines": { + "node": ">= 4" + } + }, + "node_modules/import-cwd": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/import-cwd/-/import-cwd-3.0.0.tgz", + "integrity": "sha512-4pnzH16plW+hgvRECbDWpQl3cqtvSofHWh44met7ESfZ8UZOWWddm8hEyDTqREJ9RbYHY8gi8DqmaelApoOGMg==", + "dev": true, + "dependencies": { + "import-from": "^3.0.0" + }, + "engines": { + "node": ">=8" + } + }, + "node_modules/import-fresh": { + "version": "3.3.0", + "resolved": "https://registry.npmjs.org/import-fresh/-/import-fresh-3.3.0.tgz", + "integrity": "sha512-veYYhQa+D1QBKznvhUHxb8faxlrwUnxseDAbAp457E0wLNio2bOSKnjYDhMj+YiAq61xrMGhQk9iXVk5FzgQMw==", + "dev": true, + "dependencies": { + "parent-module": "^1.0.0", + "resolve-from": "^4.0.0" + }, + "engines": { + "node": ">=6" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/import-fresh/node_modules/resolve-from": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/resolve-from/-/resolve-from-4.0.0.tgz", + "integrity": "sha512-pb/MYmXstAkysRFx8piNI1tGFNQIFA3vkE3Gq4EuA1dF6gHp/+vgZqsCGJapvy8N3Q+4o7FwvquPJcnZ7RYy4g==", + "dev": true, + "engines": { + "node": ">=4" + } + }, + "node_modules/import-from": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/import-from/-/import-from-3.0.0.tgz", + "integrity": "sha512-CiuXOFFSzkU5x/CR0+z7T91Iht4CXgfCxVOFRhh2Zyhg5wOpWvvDLQUsWl+gcN+QscYBjez8hDCt85O7RLDttQ==", + "dev": true, + "dependencies": { + "resolve-from": "^5.0.0" + }, + "engines": { + "node": ">=8" + } + }, + "node_modules/import-lazy": { + "version": "2.1.0", + "resolved": "https://registry.npmjs.org/import-lazy/-/import-lazy-2.1.0.tgz", + "integrity": "sha1-BWmOPUXIjo1+nZLLBYTnfwlvPkM=", + "dev": true, + "engines": { + "node": ">=4" + } + }, + "node_modules/imurmurhash": { + "version": "0.1.4", + "resolved": "https://registry.npmjs.org/imurmurhash/-/imurmurhash-0.1.4.tgz", + "integrity": "sha1-khi5srkoojixPcT7a21XbyMUU+o=", + "dev": true, + "engines": { + "node": ">=0.8.19" + } + }, + "node_modules/indent-string": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/indent-string/-/indent-string-4.0.0.tgz", + "integrity": "sha512-EdDDZu4A2OyIK7Lr/2zG+w5jmbuk1DVBnEwREQvBzspBJkCEbRa8GxU1lghYcaGJCnRWibjDXlq779X1/y5xwg==", + "dev": true, + "engines": { + "node": ">=8" + } + }, + "node_modules/inflight": { + "version": "1.0.6", + "resolved": "https://registry.npmjs.org/inflight/-/inflight-1.0.6.tgz", + "integrity": "sha1-Sb1jMdfQLQwJvJEKEHW6gWW1bfk=", + "dev": true, + "dependencies": { + "once": "^1.3.0", + "wrappy": "1" + } + }, + "node_modules/inherits": { + "version": "2.0.4", + "resolved": "https://registry.npmjs.org/inherits/-/inherits-2.0.4.tgz", + "integrity": "sha512-k/vGaX4/Yla3WzyMCvTQOXYeIHvqOKtnqBduzTHpzpQZzAskKMhZ2K+EnBiSM9zGSoIFeMpXKxa4dYeZIQqewQ==", + "dev": true + }, + "node_modules/ini": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/ini/-/ini-2.0.0.tgz", + "integrity": "sha512-7PnF4oN3CvZF23ADhA5wRaYEQpJ8qygSkbtTXWBeXWXmEVRXK+1ITciHWwHhsjv1TmW0MgacIv6hEi5pX5NQdA==", + "dev": true, + "engines": { + "node": ">=10" + } + }, + "node_modules/inquirer": { + "version": "8.1.2", + "resolved": "https://registry.npmjs.org/inquirer/-/inquirer-8.1.2.tgz", + "integrity": "sha512-DHLKJwLPNgkfwNmsuEUKSejJFbkv0FMO9SMiQbjI3n5NQuCrSIBqP66ggqyz2a6t2qEolKrMjhQ3+W/xXgUQ+Q==", + "dev": true, + "dependencies": { + "ansi-escapes": "^4.2.1", + "chalk": "^4.1.1", + "cli-cursor": "^3.1.0", + "cli-width": "^3.0.0", + "external-editor": "^3.0.3", + "figures": "^3.0.0", + "lodash": "^4.17.21", + "mute-stream": "0.0.8", + "ora": "^5.3.0", + "run-async": "^2.4.0", + "rxjs": "^7.2.0", + "string-width": "^4.1.0", + "strip-ansi": "^6.0.0", + "through": "^2.3.6" + }, + "engines": { + "node": ">=8.0.0" + } + }, + "node_modules/interpret": { + "version": "1.4.0", + "resolved": "https://registry.npmjs.org/interpret/-/interpret-1.4.0.tgz", + "integrity": "sha512-agE4QfB2Lkp9uICn7BAqoscw4SZP9kTE2hxiFI3jBPmXJfdqiahTbUuKGsMoN2GtqL9AxhYioAcVvgsb1HvRbA==", + "dev": true, + "engines": { + "node": ">= 0.10" + } + }, + "node_modules/is-arrayish": { + "version": "0.2.1", + "resolved": "https://registry.npmjs.org/is-arrayish/-/is-arrayish-0.2.1.tgz", + "integrity": "sha1-d8mYQFJ6qOyxqLppe4BkWnqSap0=", + "dev": true + }, + "node_modules/is-binary-path": { + "version": "2.1.0", + "resolved": "https://registry.npmjs.org/is-binary-path/-/is-binary-path-2.1.0.tgz", + "integrity": "sha512-ZMERYes6pDydyuGidse7OsHxtbI7WVeUEozgR/g7rd0xUimYNlvZRE/K2MgZTjWy725IfelLeVcEM97mmtRGXw==", + "dev": true, + "dependencies": { + "binary-extensions": "^2.0.0" + }, + "engines": { + "node": ">=8" + } + }, + "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==", + "dev": true, + "dependencies": { + "ci-info": "^3.1.1" + }, + "bin": { + "is-ci": "bin.js" + } + }, + "node_modules/is-core-module": { + "version": "2.6.0", + "resolved": "https://registry.npmjs.org/is-core-module/-/is-core-module-2.6.0.tgz", + "integrity": "sha512-wShG8vs60jKfPWpF2KZRaAtvt3a20OAn7+IJ6hLPECpSABLcKtFKTTI4ZtH5QcBruBHlq+WsdHWyz0BCZW7svQ==", + "dev": true, + "dependencies": { + "has": "^1.0.3" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/is-docker": { + "version": "2.2.1", + "resolved": "https://registry.npmjs.org/is-docker/-/is-docker-2.2.1.tgz", + "integrity": "sha512-F+i2BKsFrH66iaUFc0woD8sLy8getkwTwtOBjvs56Cx4CgJDeKQeqfz8wAYiSb8JOprWhHH5p77PbmYCvvUuXQ==", + "dev": true, + "bin": { + "is-docker": "cli.js" + }, + "engines": { + "node": ">=8" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/is-extglob": { + "version": "2.1.1", + "resolved": "https://registry.npmjs.org/is-extglob/-/is-extglob-2.1.1.tgz", + "integrity": "sha1-qIwCU1eR8C7TfHahueqXc8gz+MI=", + "dev": true, + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/is-fullwidth-code-point": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/is-fullwidth-code-point/-/is-fullwidth-code-point-3.0.0.tgz", + "integrity": "sha512-zymm5+u+sCsSWyD9qNaejV3DFvhCKclKdizYaJUuHA83RLjb7nSuGnddCHGv0hk+KY7BMAlsWeK4Ueg6EV6XQg==", + "dev": true, + "engines": { + "node": ">=8" + } + }, + "node_modules/is-glob": { + "version": "4.0.1", + "resolved": "https://registry.npmjs.org/is-glob/-/is-glob-4.0.1.tgz", + "integrity": "sha512-5G0tKtBTFImOqDnLB2hG6Bp2qcKEFduo4tZu9MT/H6NQv/ghhy30o55ufafxJ/LdH79LLs2Kfrn85TLKyA7BUg==", + "dev": true, + "dependencies": { + "is-extglob": "^2.1.1" + }, + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/is-installed-globally": { + "version": "0.4.0", + "resolved": "https://registry.npmjs.org/is-installed-globally/-/is-installed-globally-0.4.0.tgz", + "integrity": "sha512-iwGqO3J21aaSkC7jWnHP/difazwS7SFeIqxv6wEtLU8Y5KlzFTjyqcSIT0d8s4+dDhKytsk9PJZ2BkS5eZwQRQ==", + "dev": true, + "dependencies": { + "global-dirs": "^3.0.0", + "is-path-inside": "^3.0.2" + }, + "engines": { + "node": ">=10" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/is-interactive": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/is-interactive/-/is-interactive-1.0.0.tgz", + "integrity": "sha512-2HvIEKRoqS62guEC+qBjpvRubdX910WCMuJTZ+I9yvqKU2/12eSL549HMwtabb4oupdj2sMP50k+XJfB/8JE6w==", + "dev": true, + "engines": { + "node": ">=8" + } + }, + "node_modules/is-npm": { + "version": "5.0.0", + "resolved": "https://registry.npmjs.org/is-npm/-/is-npm-5.0.0.tgz", + "integrity": "sha512-WW/rQLOazUq+ST/bCAVBp/2oMERWLsR7OrKyt052dNDk4DHcDE0/7QSXITlmi+VBcV13DfIbysG3tZJm5RfdBA==", + "dev": true, + "engines": { + "node": ">=10" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/is-number": { + "version": "7.0.0", + "resolved": "https://registry.npmjs.org/is-number/-/is-number-7.0.0.tgz", + "integrity": "sha512-41Cifkg6e8TylSpdtTpeLVMqvSBEVzTttHvERD741+pnZ8ANv0004MRL43QKPDlK9cGvNp6NZWZUBlbGXYxxng==", + "dev": true, + "engines": { + "node": ">=0.12.0" + } + }, + "node_modules/is-obj": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/is-obj/-/is-obj-2.0.0.tgz", + "integrity": "sha512-drqDG3cbczxxEJRoOXcOjtdp1J/lyp1mNn0xaznRs8+muBhgQcrnbspox5X5fOw0HnMnbfDzvnEMEtqDEJEo8w==", + "dev": true, + "engines": { + "node": ">=8" + } + }, + "node_modules/is-path-inside": { + "version": "3.0.3", + "resolved": "https://registry.npmjs.org/is-path-inside/-/is-path-inside-3.0.3.tgz", + "integrity": "sha512-Fd4gABb+ycGAmKou8eMftCupSir5lRxqf4aD/vd0cD2qc4HL07OjCeuHMr8Ro4CoMaeCKDB0/ECBOVWjTwUvPQ==", + "dev": true, + "engines": { + "node": ">=8" + } + }, + "node_modules/is-plain-obj": { + "version": "2.1.0", + "resolved": "https://registry.npmjs.org/is-plain-obj/-/is-plain-obj-2.1.0.tgz", + "integrity": "sha512-YWnfyRwxL/+SsrWYfOpUtz5b3YD+nyfkHvjbcanzk8zgyO4ASD67uVMRt8k5bM4lLMDnXfriRhOpemw+NfT1eA==", + "dev": true, + "engines": { + "node": ">=8" + } + }, + "node_modules/is-plain-object": { + "version": "5.0.0", + "resolved": "https://registry.npmjs.org/is-plain-object/-/is-plain-object-5.0.0.tgz", + "integrity": "sha512-VRSzKkbMm5jMDoKLbltAkFQ5Qr7VDiTFGXxYFXXowVj387GeGNOCsOH6Msy00SGZ3Fp84b1Naa1psqgcCIEP5Q==", + "dev": true, + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/is-ssh": { + "version": "1.3.3", + "resolved": "https://registry.npmjs.org/is-ssh/-/is-ssh-1.3.3.tgz", + "integrity": "sha512-NKzJmQzJfEEma3w5cJNcUMxoXfDjz0Zj0eyCalHn2E6VOwlzjZo0yuO2fcBSf8zhFuVCL/82/r5gRcoi6aEPVQ==", + "dev": true, + "dependencies": { + "protocols": "^1.1.0" + } + }, + "node_modules/is-stream": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/is-stream/-/is-stream-2.0.1.tgz", + "integrity": "sha512-hFoiJiTl63nn+kstHGBtewWSKnQLpyb155KHheA1l39uvtO9nWIop1p3udqPcUd/xbF1VLMO4n7OI6p7RbngDg==", + "dev": true, + "engines": { + "node": ">=8" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/is-typedarray": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/is-typedarray/-/is-typedarray-1.0.0.tgz", + "integrity": "sha1-5HnICFjfDBsR3dppQPlgEfzaSpo=", + "dev": true + }, + "node_modules/is-unicode-supported": { + "version": "0.1.0", + "resolved": "https://registry.npmjs.org/is-unicode-supported/-/is-unicode-supported-0.1.0.tgz", + "integrity": "sha512-knxG2q4UC3u8stRGyAVJCOdxFmv5DZiRcdlIaAQXAbSfJya+OhopNotLQrstBhququ4ZpuKbDc/8S6mgXgPFPw==", + "dev": true, + "engines": { + "node": ">=10" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/is-windows": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/is-windows/-/is-windows-1.0.2.tgz", + "integrity": "sha512-eXK1UInq2bPmjyX6e3VHIzMLobc4J94i4AWn+Hpq3OU5KkrRC96OAcR3PRJ/pGu6m8TRnBHP9dkXQVsT/COVIA==", + "dev": true, + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/is-wsl": { + "version": "2.2.0", + "resolved": "https://registry.npmjs.org/is-wsl/-/is-wsl-2.2.0.tgz", + "integrity": "sha512-fKzAra0rGJUUBwGBgNkHZuToZcn+TtXHpeCgmkMJMMYx1sQDYaCSyjJBSCa2nH1DGm7s3n1oBnohoVTBaN7Lww==", + "dev": true, + "dependencies": { + "is-docker": "^2.0.0" + }, + "engines": { + "node": ">=8" + } + }, + "node_modules/is-yarn-global": { + "version": "0.3.0", + "resolved": "https://registry.npmjs.org/is-yarn-global/-/is-yarn-global-0.3.0.tgz", + "integrity": "sha512-VjSeb/lHmkoyd8ryPVIKvOCn4D1koMqY+vqyjjUfc3xyKtP4dYOxM44sZrnqQSzSds3xyOrUTLTC9LVCVgLngw==", + "dev": true + }, + "node_modules/isarray": { + "version": "0.0.1", + "resolved": "https://registry.npmjs.org/isarray/-/isarray-0.0.1.tgz", + "integrity": "sha1-ihis/Kmo9Bd+Cav8YDiTmwXR7t8=", + "dev": true + }, + "node_modules/isexe": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/isexe/-/isexe-2.0.0.tgz", + "integrity": "sha1-6PvzdNxVb/iUehDcsFctYz8s+hA=", + "dev": true + }, + "node_modules/istanbul-lib-coverage": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/istanbul-lib-coverage/-/istanbul-lib-coverage-3.0.0.tgz", + "integrity": "sha512-UiUIqxMgRDET6eR+o5HbfRYP1l0hqkWOs7vNxC/mggutCMUIhWMm8gAHb8tHlyfD3/l6rlgNA5cKdDzEAf6hEg==", + "dev": true, + "engines": { + "node": ">=8" + } + }, + "node_modules/istanbul-lib-hook": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/istanbul-lib-hook/-/istanbul-lib-hook-3.0.0.tgz", + "integrity": "sha512-Pt/uge1Q9s+5VAZ+pCo16TYMWPBIl+oaNIjgLQxcX0itS6ueeaA+pEfThZpH8WxhFgCiEb8sAJY6MdUKgiIWaQ==", + "dev": true, + "dependencies": { + "append-transform": "^2.0.0" + }, + "engines": { + "node": ">=8" + } + }, + "node_modules/istanbul-lib-instrument": { + "version": "4.0.3", + "resolved": "https://registry.npmjs.org/istanbul-lib-instrument/-/istanbul-lib-instrument-4.0.3.tgz", + "integrity": "sha512-BXgQl9kf4WTCPCCpmFGoJkz/+uhvm7h7PFKUYxh7qarQd3ER33vHG//qaE8eN25l07YqZPpHXU9I09l/RD5aGQ==", + "dev": true, + "dependencies": { + "@babel/core": "^7.7.5", + "@istanbuljs/schema": "^0.1.2", + "istanbul-lib-coverage": "^3.0.0", + "semver": "^6.3.0" + }, + "engines": { + "node": ">=8" + } + }, + "node_modules/istanbul-lib-processinfo": { + "version": "2.0.2", + "resolved": "https://registry.npmjs.org/istanbul-lib-processinfo/-/istanbul-lib-processinfo-2.0.2.tgz", + "integrity": "sha512-kOwpa7z9hme+IBPZMzQ5vdQj8srYgAtaRqeI48NGmAQ+/5yKiHLV0QbYqQpxsdEF0+w14SoB8YbnHKcXE2KnYw==", + "dev": true, + "dependencies": { + "archy": "^1.0.0", + "cross-spawn": "^7.0.0", + "istanbul-lib-coverage": "^3.0.0-alpha.1", + "make-dir": "^3.0.0", + "p-map": "^3.0.0", + "rimraf": "^3.0.0", + "uuid": "^3.3.3" + }, + "engines": { + "node": ">=8" + } + }, + "node_modules/istanbul-lib-report": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/istanbul-lib-report/-/istanbul-lib-report-3.0.0.tgz", + "integrity": "sha512-wcdi+uAKzfiGT2abPpKZ0hSU1rGQjUQnLvtY5MpQ7QCTahD3VODhcu4wcfY1YtkGaDD5yuydOLINXsfbus9ROw==", + "dev": true, + "dependencies": { + "istanbul-lib-coverage": "^3.0.0", + "make-dir": "^3.0.0", + "supports-color": "^7.1.0" + }, + "engines": { + "node": ">=8" + } + }, + "node_modules/istanbul-lib-report/node_modules/supports-color": { + "version": "7.2.0", + "resolved": "https://registry.npmjs.org/supports-color/-/supports-color-7.2.0.tgz", + "integrity": "sha512-qpCAvRl9stuOHveKsn7HncJRvv501qIacKzQlO/+Lwxc9+0q2wLyv4Dfvt80/DPn2pqOBsJdDiogXGR9+OvwRw==", + "dev": true, + "dependencies": { + "has-flag": "^4.0.0" + }, + "engines": { + "node": ">=8" + } + }, + "node_modules/istanbul-lib-source-maps": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/istanbul-lib-source-maps/-/istanbul-lib-source-maps-4.0.0.tgz", + "integrity": "sha512-c16LpFRkR8vQXyHZ5nLpY35JZtzj1PQY1iZmesUbf1FZHbIupcWfjgOXBY9YHkLEQ6puz1u4Dgj6qmU/DisrZg==", + "dev": true, + "dependencies": { + "debug": "^4.1.1", + "istanbul-lib-coverage": "^3.0.0", + "source-map": "^0.6.1" + }, + "engines": { + "node": ">=8" + } + }, + "node_modules/istanbul-lib-source-maps/node_modules/source-map": { + "version": "0.6.1", + "resolved": "https://registry.npmjs.org/source-map/-/source-map-0.6.1.tgz", + "integrity": "sha512-UjgapumWlbMhkBgzT7Ykc5YXUT46F0iKu8SGXq0bcwP5dz/h0Plj6enJqjz1Zbq2l5WaqYnrVbwWOWMyF3F47g==", + "dev": true, + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/istanbul-reports": { + "version": "3.0.2", + "resolved": "https://registry.npmjs.org/istanbul-reports/-/istanbul-reports-3.0.2.tgz", + "integrity": "sha512-9tZvz7AiR3PEDNGiV9vIouQ/EAcqMXFmkcA1CDFTwOB98OZVDL0PH9glHotf5Ugp6GCOTypfzGWI/OqjWNCRUw==", + "dev": true, + "dependencies": { + "html-escaper": "^2.0.0", + "istanbul-lib-report": "^3.0.0" + }, + "engines": { + "node": ">=8" + } + }, + "node_modules/js-tokens": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/js-tokens/-/js-tokens-4.0.0.tgz", + "integrity": "sha512-RdJUflcE3cUzKiMqQgsCu06FPu9UdIJO0beYbPhHN4k6apgJtifcoCtT9bcxOpYBtpD2kCM6Sbzg4CausW/PKQ==", + "dev": true + }, + "node_modules/js-yaml": { + "version": "4.1.0", + "resolved": "https://registry.npmjs.org/js-yaml/-/js-yaml-4.1.0.tgz", + "integrity": "sha512-wpxZs9NoxZaJESJGIZTyDEaYpl0FKSA+FB9aJiyemKhMwkxQg63h4T1KJgUGHpTqPDNRcmmYLugrRjJlBtWvRA==", + "dev": true, + "dependencies": { + "argparse": "^2.0.1" + }, + "bin": { + "js-yaml": "bin/js-yaml.js" + } + }, + "node_modules/jsesc": { + "version": "2.5.2", + "resolved": "https://registry.npmjs.org/jsesc/-/jsesc-2.5.2.tgz", + "integrity": "sha512-OYu7XEzjkCQ3C5Ps3QIZsQfNpqoJyZZA99wd9aWd05NCtC5pWOkShK2mkL6HXQR6/Cy2lbNdPlZBpuQHXE63gA==", + "dev": true, + "bin": { + "jsesc": "bin/jsesc" + }, + "engines": { + "node": ">=4" + } + }, + "node_modules/json-buffer": { + "version": "3.0.1", + "resolved": "https://registry.npmjs.org/json-buffer/-/json-buffer-3.0.1.tgz", + "integrity": "sha512-4bV5BfR2mqfQTJm+V5tPPdf+ZpuhiIvTuAB5g8kcrXOZpTT/QwwVRWBywX1ozr6lEuPdbHxwaJlm9G6mI2sfSQ==", + "dev": true + }, + "node_modules/json-parse-even-better-errors": { + "version": "2.3.1", + "resolved": "https://registry.npmjs.org/json-parse-even-better-errors/-/json-parse-even-better-errors-2.3.1.tgz", + "integrity": "sha512-xyFwyhro/JEof6Ghe2iz2NcXoj2sloNsWr/XsERDK/oiPCfaNhl5ONfp+jQdAZRQQ0IJWNzH9zIZF7li91kh2w==", + "dev": true + }, + "node_modules/json5": { + "version": "2.2.0", + "resolved": "https://registry.npmjs.org/json5/-/json5-2.2.0.tgz", + "integrity": "sha512-f+8cldu7X/y7RAJurMEJmdoKXGB/X550w2Nr3tTbezL6RwEE/iMcm+tZnXeoZtKuOq6ft8+CqzEkrIgx1fPoQA==", + "dev": true, + "dependencies": { + "minimist": "^1.2.5" + }, + "bin": { + "json5": "lib/cli.js" + }, + "engines": { + "node": ">=6" + } + }, + "node_modules/just-extend": { + "version": "4.2.1", + "resolved": "https://registry.npmjs.org/just-extend/-/just-extend-4.2.1.tgz", + "integrity": "sha512-g3UB796vUFIY90VIv/WX3L2c8CS2MdWUww3CNrYmqza1Fg0DURc2K/O4YrnklBdQarSJ/y8JnJYDGc+1iumQjg==", + "dev": true + }, + "node_modules/keyv": { + "version": "4.0.3", + "resolved": "https://registry.npmjs.org/keyv/-/keyv-4.0.3.tgz", + "integrity": "sha512-zdGa2TOpSZPq5mU6iowDARnMBZgtCqJ11dJROFi6tg6kTn4nuUdU09lFyLFSaHrWqpIJ+EBq4E8/Dc0Vx5vLdA==", + "dev": true, + "dependencies": { + "json-buffer": "3.0.1" + } + }, + "node_modules/latest-version": { + "version": "5.1.0", + "resolved": "https://registry.npmjs.org/latest-version/-/latest-version-5.1.0.tgz", + "integrity": "sha512-weT+r0kTkRQdCdYCNtkMwWXQTMEswKrFBkm4ckQOMVhhqhIMI1UT2hMj+1iigIhgSZm5gTmrRXBNoGUgaTY1xA==", + "dev": true, + "dependencies": { + "package-json": "^6.3.0" + }, + "engines": { + "node": ">=8" + } + }, + "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=", + "dev": true + }, + "node_modules/locate-path": { + "version": "6.0.0", + "resolved": "https://registry.npmjs.org/locate-path/-/locate-path-6.0.0.tgz", + "integrity": "sha512-iPZK6eYjbxRu3uB4/WZ3EsEIMJFMqAoopl3R+zuq0UjcAm/MO6KCweDgPfP3elTztoKP3KtnVHxTn2NHBSDVUw==", + "dev": true, + "dependencies": { + "p-locate": "^5.0.0" + }, + "engines": { + "node": ">=10" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/lodash": { + "version": "4.17.21", + "resolved": "https://registry.npmjs.org/lodash/-/lodash-4.17.21.tgz", + "integrity": "sha512-v2kDEe57lecTulaDIuNTPy3Ry4gLGJ6Z1O3vE1krgXZNrsQ+LFTGHVxVjcXPs17LhbZVGedAJv8XZ1tvj5FvSg==", + "dev": true + }, + "node_modules/lodash.flattendeep": { + "version": "4.4.0", + "resolved": "https://registry.npmjs.org/lodash.flattendeep/-/lodash.flattendeep-4.4.0.tgz", + "integrity": "sha1-+wMJF/hqMTTlvJvsDWngAT3f7bI=", + "dev": true + }, + "node_modules/lodash.get": { + "version": "4.4.2", + "resolved": "https://registry.npmjs.org/lodash.get/-/lodash.get-4.4.2.tgz", + "integrity": "sha1-LRd/ZS+jHpObRDjVNBSZ36OCXpk=", + "dev": true + }, + "node_modules/log-symbols": { + "version": "4.1.0", + "resolved": "https://registry.npmjs.org/log-symbols/-/log-symbols-4.1.0.tgz", + "integrity": "sha512-8XPvpAA8uyhfteu8pIvQxpJZ7SYYdpUivZpGy6sFsBuKRY/7rQGavedeB8aK+Zkyq6upMFVL/9AW6vOYzfRyLg==", + "dev": true, + "dependencies": { + "chalk": "^4.1.0", + "is-unicode-supported": "^0.1.0" + }, + "engines": { + "node": ">=10" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/lowercase-keys": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/lowercase-keys/-/lowercase-keys-2.0.0.tgz", + "integrity": "sha512-tqNXrS78oMOE73NMxK4EMLQsQowWf8jKooH9g7xPavRT706R6bkQJ6DY2Te7QukaZsulxa30wQ7bk0pm4XiHmA==", + "dev": true, + "engines": { + "node": ">=8" + } + }, + "node_modules/lru-cache": { + "version": "5.1.1", + "resolved": "https://registry.npmjs.org/lru-cache/-/lru-cache-5.1.1.tgz", + "integrity": "sha512-KpNARQA3Iwv+jTA0utUVVbrh+Jlrr1Fv0e56GGzAFOXN7dk/FviaDW8LHmK52DlcH4WP2n6gI8vN1aesBFgo9w==", + "dev": true, + "dependencies": { + "yallist": "^3.0.2" + } + }, + "node_modules/lru-cache/node_modules/yallist": { + "version": "3.1.1", + "resolved": "https://registry.npmjs.org/yallist/-/yallist-3.1.1.tgz", + "integrity": "sha512-a4UGQaWPH59mOXUYnAG2ewncQS4i4F43Tv3JoAM+s2VDAmS9NsK8GpDMLrCHPksFT7h3K6TOoUNn2pb7RoXx4g==", + "dev": true + }, + "node_modules/lunr": { + "version": "2.3.9", + "resolved": "https://registry.npmjs.org/lunr/-/lunr-2.3.9.tgz", + "integrity": "sha512-zTU3DaZaF3Rt9rhN3uBMGQD3dD2/vFQqnvZCDv4dl5iOzq2IZQqTxu90r4E5J+nP70J3ilqVCrbho2eWaeW8Ow==", + "dev": true + }, + "node_modules/macos-release": { + "version": "2.5.0", + "resolved": "https://registry.npmjs.org/macos-release/-/macos-release-2.5.0.tgz", + "integrity": "sha512-EIgv+QZ9r+814gjJj0Bt5vSLJLzswGmSUbUpbi9AIr/fsN2IWFBl2NucV9PAiek+U1STK468tEkxmVYUtuAN3g==", + "dev": true, + "engines": { + "node": ">=6" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/make-dir": { + "version": "3.1.0", + "resolved": "https://registry.npmjs.org/make-dir/-/make-dir-3.1.0.tgz", + "integrity": "sha512-g3FeP20LNwhALb/6Cz6Dd4F2ngze0jz7tbzrD2wAV+o9FeNHe4rL+yK2md0J/fiSf1sa1ADhXqi5+oVwOM/eGw==", + "dev": true, + "dependencies": { + "semver": "^6.0.0" + }, + "engines": { + "node": ">=8" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/make-error": { + "version": "1.3.6", + "resolved": "https://registry.npmjs.org/make-error/-/make-error-1.3.6.tgz", + "integrity": "sha512-s8UhlNe7vPKomQhC1qFelMokr/Sc3AgNbso3n74mVPA5LTZwkB9NlXf4XPamLxJE8h0gh73rM94xvwRT2CVInw==", + "dev": true + }, + "node_modules/marked": { + "version": "3.0.2", + "resolved": "https://registry.npmjs.org/marked/-/marked-3.0.2.tgz", + "integrity": "sha512-TMJQQ79Z0e3rJYazY0tIoMsFzteUGw9fB3FD+gzuIT3zLuG9L9ckIvUfF51apdJkcqc208jJN2KbtPbOvXtbjA==", + "dev": true, + "bin": { + "marked": "bin/marked" + }, + "engines": { + "node": ">= 12" + } + }, + "node_modules/merge-stream": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/merge-stream/-/merge-stream-2.0.0.tgz", + "integrity": "sha512-abv/qOcuPfk3URPfDzmZU1LKmuw8kT+0nIHvKrKgFrwifol/doWcdA4ZqsWQ8ENrFKkd67Mfpo/LovbIUsbt3w==", + "dev": true + }, + "node_modules/merge2": { + "version": "1.4.1", + "resolved": "https://registry.npmjs.org/merge2/-/merge2-1.4.1.tgz", + "integrity": "sha512-8q7VEgMJW4J8tcfVPy8g09NcQwZdbwFEqhe/WZkoIzjn/3TGDwtOCYtXGxA3O8tPzpczCCDgv+P2P5y00ZJOOg==", + "dev": true, + "engines": { + "node": ">= 8" + } + }, + "node_modules/micromatch": { + "version": "4.0.4", + "resolved": "https://registry.npmjs.org/micromatch/-/micromatch-4.0.4.tgz", + "integrity": "sha512-pRmzw/XUcwXGpD9aI9q/0XOwLNygjETJ8y0ao0wdqprrzDa4YnxLcz7fQRZr8voh8V10kGhABbNcHVk5wHgWwg==", + "dev": true, + "dependencies": { + "braces": "^3.0.1", + "picomatch": "^2.2.3" + }, + "engines": { + "node": ">=8.6" + } + }, + "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==", + "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==", + "dev": true, + "dependencies": { + "mime-db": "1.49.0" + }, + "engines": { + "node": ">= 0.6" + } + }, + "node_modules/mimic-fn": { + "version": "2.1.0", + "resolved": "https://registry.npmjs.org/mimic-fn/-/mimic-fn-2.1.0.tgz", + "integrity": "sha512-OqbOk5oEQeAZ8WXWydlu9HJjz9WVdEIvamMCcXmuqUYjTknH/sqsWvhQ3vgwKFRR1HpjvNBKQ37nbJgYzGqGcg==", + "dev": true, + "engines": { + "node": ">=6" + } + }, + "node_modules/mimic-response": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/mimic-response/-/mimic-response-1.0.1.tgz", + "integrity": "sha512-j5EctnkH7amfV/q5Hgmoal1g2QHFJRraOtmx0JpIqkxhBhI/lJSl1nMpQ45hVarwNETOoWEimndZ4QK0RHxuxQ==", + "dev": true, + "engines": { + "node": ">=4" + } + }, + "node_modules/minimatch": { + "version": "3.0.4", + "resolved": "https://registry.npmjs.org/minimatch/-/minimatch-3.0.4.tgz", + "integrity": "sha512-yJHVQEhyqPLUTgt9B83PXu6W3rx4MvvHvSUvToogpwoGDOUQ+yDrR0HRot+yOCdCO7u4hX3pWft6kWBBcqh0UA==", + "dev": true, + "dependencies": { + "brace-expansion": "^1.1.7" + }, + "engines": { + "node": "*" + } + }, + "node_modules/minimist": { + "version": "1.2.5", + "resolved": "https://registry.npmjs.org/minimist/-/minimist-1.2.5.tgz", + "integrity": "sha512-FM9nNUYrRBAELZQT3xeZQ7fmMOBg6nWNmJKTcgsJeaLstP/UODVpGsr5OhXhhXg6f+qtJ8uiZ+PUxkDWcgIXLw==", + "dev": true + }, + "node_modules/mocha": { + "version": "9.1.1", + "resolved": "https://registry.npmjs.org/mocha/-/mocha-9.1.1.tgz", + "integrity": "sha512-0wE74YMgOkCgBUj8VyIDwmLUjTsS13WV1Pg7l0SHea2qzZzlq7MDnfbPsHKcELBRk3+izEVkRofjmClpycudCA==", + "dev": true, + "dependencies": { + "@ungap/promise-all-settled": "1.1.2", + "ansi-colors": "4.1.1", + "browser-stdout": "1.3.1", + "chokidar": "3.5.2", + "debug": "4.3.1", + "diff": "5.0.0", + "escape-string-regexp": "4.0.0", + "find-up": "5.0.0", + "glob": "7.1.7", + "growl": "1.10.5", + "he": "1.2.0", + "js-yaml": "4.1.0", + "log-symbols": "4.1.0", + "minimatch": "3.0.4", + "ms": "2.1.3", + "nanoid": "3.1.23", + "serialize-javascript": "6.0.0", + "strip-json-comments": "3.1.1", + "supports-color": "8.1.1", + "which": "2.0.2", + "wide-align": "1.1.3", + "workerpool": "6.1.5", + "yargs": "16.2.0", + "yargs-parser": "20.2.4", + "yargs-unparser": "2.0.0" + }, + "bin": { + "_mocha": "bin/_mocha", + "mocha": "bin/mocha" + }, + "engines": { + "node": ">= 12.0.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/mochajs" + } + }, + "node_modules/ms": { + "version": "2.1.3", + "resolved": "https://registry.npmjs.org/ms/-/ms-2.1.3.tgz", + "integrity": "sha512-6FlzubTLZG3J2a/NVCAleEhjzq5oxgHyaCU9yYXvcLsvoVaHJq/s5xXI6/XXP6tz7R9xAOtHnSO/tXtF3WRTlA==", + "dev": true + }, + "node_modules/mute-stream": { + "version": "0.0.8", + "resolved": "https://registry.npmjs.org/mute-stream/-/mute-stream-0.0.8.tgz", + "integrity": "sha512-nnbWWOkoWyUsTjKrhgD0dcz22mdkSnpYqbEjIm2nhwhuxlSkpywJmBo8h0ZqJdkp73mb90SssHkN4rsRaBAfAA==", + "dev": true + }, + "node_modules/nanoid": { + "version": "3.1.23", + "resolved": "https://registry.npmjs.org/nanoid/-/nanoid-3.1.23.tgz", + "integrity": "sha512-FiB0kzdP0FFVGDKlRLEQ1BgDzU87dy5NnzjeW9YZNt+/c3+q82EQDUwniSAUxp/F0gFNI1ZhKU1FqYsMuqZVnw==", + "dev": true, + "bin": { + "nanoid": "bin/nanoid.cjs" + }, + "engines": { + "node": "^10 || ^12 || ^13.7 || ^14 || >=15.0.1" + } + }, + "node_modules/neo-async": { + "version": "2.6.2", + "resolved": "https://registry.npmjs.org/neo-async/-/neo-async-2.6.2.tgz", + "integrity": "sha512-Yd3UES5mWCSqR+qNT93S3UoYUkqAZ9lLg8a7g9rimsWmYGK8cVToA4/sF3RrshdyV3sAGMXVUmpMYOw+dLpOuw==", + "dev": true + }, + "node_modules/new-github-release-url": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/new-github-release-url/-/new-github-release-url-1.0.0.tgz", + "integrity": "sha512-dle7yf655IMjyFUqn6Nxkb18r4AOAkzRcgcZv6WZ0IqrOH4QCEZ8Sm6I7XX21zvHdBeeMeTkhR9qT2Z0EJDx6A==", + "dev": true, + "dependencies": { + "type-fest": "^0.4.1" + }, + "engines": { + "node": ">=10" + } + }, + "node_modules/new-github-release-url/node_modules/type-fest": { + "version": "0.4.1", + "resolved": "https://registry.npmjs.org/type-fest/-/type-fest-0.4.1.tgz", + "integrity": "sha512-IwzA/LSfD2vC1/YDYMv/zHP4rDF1usCwllsDpbolT3D4fUepIO7f9K70jjmUewU/LmGUKJcwcVtDCpnKk4BPMw==", + "dev": true, + "engines": { + "node": ">=6" + } + }, + "node_modules/nise": { + "version": "5.1.0", + "resolved": "https://registry.npmjs.org/nise/-/nise-5.1.0.tgz", + "integrity": "sha512-W5WlHu+wvo3PaKLsJJkgPup2LrsXCcm7AWwyNZkUnn5rwPkuPBi3Iwk5SQtN0mv+K65k7nKKjwNQ30wg3wLAQQ==", + "dev": true, + "dependencies": { + "@sinonjs/commons": "^1.7.0", + "@sinonjs/fake-timers": "^7.0.4", + "@sinonjs/text-encoding": "^0.7.1", + "just-extend": "^4.0.2", + "path-to-regexp": "^1.7.0" + } + }, + "node_modules/node-fetch": { + "version": "2.6.1", + "resolved": "https://registry.npmjs.org/node-fetch/-/node-fetch-2.6.1.tgz", + "integrity": "sha512-V4aYg89jEoVRxRb2fJdAg8FHvI7cEyYdVAh94HH0UIK8oJxUfkjlDQN9RbMx+bEjP7+ggMiFRprSti032Oipxw==", + "dev": true, + "engines": { + "node": "4.x || >=6.0.0" + } + }, + "node_modules/node-preload": { + "version": "0.2.1", + "resolved": "https://registry.npmjs.org/node-preload/-/node-preload-0.2.1.tgz", + "integrity": "sha512-RM5oyBy45cLEoHqCeh+MNuFAxO0vTFBLskvQbOKnEE7YTTSN4tbN8QWDIPQ6L+WvKsB/qLEGpYe2ZZ9d4W9OIQ==", + "dev": true, + "dependencies": { + "process-on-spawn": "^1.0.0" + }, + "engines": { + "node": ">=8" + } + }, + "node_modules/node-releases": { + "version": "1.1.75", + "resolved": "https://registry.npmjs.org/node-releases/-/node-releases-1.1.75.tgz", + "integrity": "sha512-Qe5OUajvqrqDSy6wrWFmMwfJ0jVgwiw4T3KqmbTcZ62qW0gQkheXYhcFM1+lOVcGUoRxcEcfyvFMAnDgaF1VWw==", + "dev": true + }, + "node_modules/normalize-path": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/normalize-path/-/normalize-path-3.0.0.tgz", + "integrity": "sha512-6eZs5Ls3WtCisHWp9S2GUy8dqkpGi4BVSz3GaqiE6ezub0512ESztXUwUB6C6IKbQkY2Pnb/mD4WYojCRwcwLA==", + "dev": true, + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/normalize-url": { + "version": "6.1.0", + "resolved": "https://registry.npmjs.org/normalize-url/-/normalize-url-6.1.0.tgz", + "integrity": "sha512-DlL+XwOy3NxAQ8xuC0okPgK46iuVNAK01YN7RueYBqqFeGsBjV9XmCAzAdgt+667bCl5kPh9EqKKDwnaPG1I7A==", + "dev": true, + "engines": { + "node": ">=10" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/npm-run-path": { + "version": "4.0.1", + "resolved": "https://registry.npmjs.org/npm-run-path/-/npm-run-path-4.0.1.tgz", + "integrity": "sha512-S48WzZW777zhNIrn7gxOlISNAqi9ZC/uQFnRdbeIHhZhCA6UqpkOT8T1G7BvfdgP4Er8gF4sUbaS0i7QvIfCWw==", + "dev": true, + "dependencies": { + "path-key": "^3.0.0" + }, + "engines": { + "node": ">=8" + } + }, + "node_modules/nyc": { + "version": "15.1.0", + "resolved": "https://registry.npmjs.org/nyc/-/nyc-15.1.0.tgz", + "integrity": "sha512-jMW04n9SxKdKi1ZMGhvUTHBN0EICCRkHemEoE5jm6mTYcqcdas0ATzgUgejlQUHMvpnOZqGB5Xxsv9KxJW1j8A==", + "dev": true, + "dependencies": { + "@istanbuljs/load-nyc-config": "^1.0.0", + "@istanbuljs/schema": "^0.1.2", + "caching-transform": "^4.0.0", + "convert-source-map": "^1.7.0", + "decamelize": "^1.2.0", + "find-cache-dir": "^3.2.0", + "find-up": "^4.1.0", + "foreground-child": "^2.0.0", + "get-package-type": "^0.1.0", + "glob": "^7.1.6", + "istanbul-lib-coverage": "^3.0.0", + "istanbul-lib-hook": "^3.0.0", + "istanbul-lib-instrument": "^4.0.0", + "istanbul-lib-processinfo": "^2.0.2", + "istanbul-lib-report": "^3.0.0", + "istanbul-lib-source-maps": "^4.0.0", + "istanbul-reports": "^3.0.2", + "make-dir": "^3.0.0", + "node-preload": "^0.2.1", + "p-map": "^3.0.0", + "process-on-spawn": "^1.0.0", + "resolve-from": "^5.0.0", + "rimraf": "^3.0.0", + "signal-exit": "^3.0.2", + "spawn-wrap": "^2.0.0", + "test-exclude": "^6.0.0", + "yargs": "^15.0.2" + }, + "bin": { + "nyc": "bin/nyc.js" + }, + "engines": { + "node": ">=8.9" + } + }, + "node_modules/nyc/node_modules/cliui": { + "version": "6.0.0", + "resolved": "https://registry.npmjs.org/cliui/-/cliui-6.0.0.tgz", + "integrity": "sha512-t6wbgtoCXvAzst7QgXxJYqPt0usEfbgQdftEPbLL/cvv6HPE5VgvqCuAIDR0NgU52ds6rFwqrgakNLrHEjCbrQ==", + "dev": true, + "dependencies": { + "string-width": "^4.2.0", + "strip-ansi": "^6.0.0", + "wrap-ansi": "^6.2.0" + } + }, + "node_modules/nyc/node_modules/find-up": { + "version": "4.1.0", + "resolved": "https://registry.npmjs.org/find-up/-/find-up-4.1.0.tgz", + "integrity": "sha512-PpOwAdQ/YlXQ2vj8a3h8IipDuYRi3wceVQQGYWxNINccq40Anw7BlsEXCMbt1Zt+OLA6Fq9suIpIWD0OsnISlw==", + "dev": true, + "dependencies": { + "locate-path": "^5.0.0", + "path-exists": "^4.0.0" + }, + "engines": { + "node": ">=8" + } + }, + "node_modules/nyc/node_modules/locate-path": { + "version": "5.0.0", + "resolved": "https://registry.npmjs.org/locate-path/-/locate-path-5.0.0.tgz", + "integrity": "sha512-t7hw9pI+WvuwNJXwk5zVHpyhIqzg2qTlklJOf0mVxGSbe3Fp2VieZcduNYjaLDoy6p9uGpQEGWG87WpMKlNq8g==", + "dev": true, + "dependencies": { + "p-locate": "^4.1.0" + }, + "engines": { + "node": ">=8" + } + }, + "node_modules/nyc/node_modules/p-limit": { + "version": "2.3.0", + "resolved": "https://registry.npmjs.org/p-limit/-/p-limit-2.3.0.tgz", + "integrity": "sha512-//88mFWSJx8lxCzwdAABTJL2MyWB12+eIY7MDL2SqLmAkeKU9qxRvWuSyTjm3FUmpBEMuFfckAIqEaVGUDxb6w==", + "dev": true, + "dependencies": { + "p-try": "^2.0.0" + }, + "engines": { + "node": ">=6" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/nyc/node_modules/p-locate": { + "version": "4.1.0", + "resolved": "https://registry.npmjs.org/p-locate/-/p-locate-4.1.0.tgz", + "integrity": "sha512-R79ZZ/0wAxKGu3oYMlz8jy/kbhsNrS7SKZ7PxEHBgJ5+F2mtFW2fK2cOtBh1cHYkQsbzFV7I+EoRKe6Yt0oK7A==", + "dev": true, + "dependencies": { + "p-limit": "^2.2.0" + }, + "engines": { + "node": ">=8" + } + }, + "node_modules/nyc/node_modules/wrap-ansi": { + "version": "6.2.0", + "resolved": "https://registry.npmjs.org/wrap-ansi/-/wrap-ansi-6.2.0.tgz", + "integrity": "sha512-r6lPcBGxZXlIcymEu7InxDMhdW0KDxpLgoFLcguasxCaJ/SOIZwINatK9KY/tf+ZrlywOKU0UDj3ATXUBfxJXA==", + "dev": true, + "dependencies": { + "ansi-styles": "^4.0.0", + "string-width": "^4.1.0", + "strip-ansi": "^6.0.0" + }, + "engines": { + "node": ">=8" + } + }, + "node_modules/nyc/node_modules/y18n": { + "version": "4.0.3", + "resolved": "https://registry.npmjs.org/y18n/-/y18n-4.0.3.tgz", + "integrity": "sha512-JKhqTOwSrqNA1NY5lSztJ1GrBiUodLMmIZuLiDaMRJ+itFd+ABVE8XBjOvIWL+rSqNDC74LCSFmlb/U4UZ4hJQ==", + "dev": true + }, + "node_modules/nyc/node_modules/yargs": { + "version": "15.4.1", + "resolved": "https://registry.npmjs.org/yargs/-/yargs-15.4.1.tgz", + "integrity": "sha512-aePbxDmcYW++PaqBsJ+HYUFwCdv4LVvdnhBy78E57PIor8/OVvhMrADFFEDh8DHDFRv/O9i3lPhsENjO7QX0+A==", + "dev": true, + "dependencies": { + "cliui": "^6.0.0", + "decamelize": "^1.2.0", + "find-up": "^4.1.0", + "get-caller-file": "^2.0.1", + "require-directory": "^2.1.1", + "require-main-filename": "^2.0.0", + "set-blocking": "^2.0.0", + "string-width": "^4.2.0", + "which-module": "^2.0.0", + "y18n": "^4.0.0", + "yargs-parser": "^18.1.2" + }, + "engines": { + "node": ">=8" + } + }, + "node_modules/nyc/node_modules/yargs-parser": { + "version": "18.1.3", + "resolved": "https://registry.npmjs.org/yargs-parser/-/yargs-parser-18.1.3.tgz", + "integrity": "sha512-o50j0JeToy/4K6OZcaQmW6lyXXKhq7csREXcDwk2omFPJEwUNOVtJKvmDr9EI1fAJZUyZcRF7kxGBWmRXudrCQ==", + "dev": true, + "dependencies": { + "camelcase": "^5.0.0", + "decamelize": "^1.2.0" + }, + "engines": { + "node": ">=6" + } + }, + "node_modules/object-inspect": { + "version": "1.11.0", + "resolved": "https://registry.npmjs.org/object-inspect/-/object-inspect-1.11.0.tgz", + "integrity": "sha512-jp7ikS6Sd3GxQfZJPyH3cjcbJF6GZPClgdV+EFygjFLQ5FmW/dRUnTd9PQ9k0JhoNDabWFbpF1yCdSWCC6gexg==", + "dev": true, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/once": { + "version": "1.4.0", + "resolved": "https://registry.npmjs.org/once/-/once-1.4.0.tgz", + "integrity": "sha1-WDsap3WWHUsROsF9nFC6753Xa9E=", + "dev": true, + "dependencies": { + "wrappy": "1" + } + }, + "node_modules/onetime": { + "version": "5.1.2", + "resolved": "https://registry.npmjs.org/onetime/-/onetime-5.1.2.tgz", + "integrity": "sha512-kbpaSSGJTWdAY5KPVeMOKXSrPtr8C8C7wodJbcsd51jRnmD+GZu8Y0VoU6Dm5Z4vWr0Ig/1NKuWRKf7j5aaYSg==", + "dev": true, + "dependencies": { + "mimic-fn": "^2.1.0" + }, + "engines": { + "node": ">=6" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/onigasm": { + "version": "2.2.5", + "resolved": "https://registry.npmjs.org/onigasm/-/onigasm-2.2.5.tgz", + "integrity": "sha512-F+th54mPc0l1lp1ZcFMyL/jTs2Tlq4SqIHKIXGZOR/VkHkF9A7Fr5rRr5+ZG/lWeRsyrClLYRq7s/yFQ/XhWCA==", + "dev": true, + "dependencies": { + "lru-cache": "^5.1.1" + } + }, + "node_modules/open": { + "version": "7.4.2", + "resolved": "https://registry.npmjs.org/open/-/open-7.4.2.tgz", + "integrity": "sha512-MVHddDVweXZF3awtlAS+6pgKLlm/JgxZ90+/NBurBoQctVOOB/zDdVjcyPzQ+0laDGbsWgrRkflI65sQeOgT9Q==", + "dev": true, + "dependencies": { + "is-docker": "^2.0.0", + "is-wsl": "^2.1.1" + }, + "engines": { + "node": ">=8" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/ora": { + "version": "5.4.1", + "resolved": "https://registry.npmjs.org/ora/-/ora-5.4.1.tgz", + "integrity": "sha512-5b6Y85tPxZZ7QytO+BQzysW31HJku27cRIlkbAXaNx+BdcVi+LlRFmVXzeF6a7JCwJpyw5c4b+YSVImQIrBpuQ==", + "dev": true, + "dependencies": { + "bl": "^4.1.0", + "chalk": "^4.1.0", + "cli-cursor": "^3.1.0", + "cli-spinners": "^2.5.0", + "is-interactive": "^1.0.0", + "is-unicode-supported": "^0.1.0", + "log-symbols": "^4.1.0", + "strip-ansi": "^6.0.0", + "wcwidth": "^1.0.1" + }, + "engines": { + "node": ">=10" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/os-name": { + "version": "4.0.1", + "resolved": "https://registry.npmjs.org/os-name/-/os-name-4.0.1.tgz", + "integrity": "sha512-xl9MAoU97MH1Xt5K9ERft2YfCAoaO6msy1OBA0ozxEC0x0TmIoE6K3QvgJMMZA9yKGLmHXNY/YZoDbiGDj4zYw==", + "dev": true, + "dependencies": { + "macos-release": "^2.5.0", + "windows-release": "^4.0.0" + }, + "engines": { + "node": ">=10" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/os-tmpdir": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/os-tmpdir/-/os-tmpdir-1.0.2.tgz", + "integrity": "sha1-u+Z0BseaqFxc/sdm/lc0VV36EnQ=", + "dev": true, + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/p-cancelable": { + "version": "2.1.1", + "resolved": "https://registry.npmjs.org/p-cancelable/-/p-cancelable-2.1.1.tgz", + "integrity": "sha512-BZOr3nRQHOntUjTrH8+Lh54smKHoHyur8We1V8DSMVrl5A2malOOwuJRnKRDjSnkoeBh4at6BwEnb5I7Jl31wg==", + "dev": true, + "engines": { + "node": ">=8" + } + }, + "node_modules/p-limit": { + "version": "3.1.0", + "resolved": "https://registry.npmjs.org/p-limit/-/p-limit-3.1.0.tgz", + "integrity": "sha512-TYOanM3wGwNGsZN2cVTYPArw454xnXj5qmWF1bEoAc4+cU/ol7GVh7odevjp1FNHduHc3KZMcFduxU5Xc6uJRQ==", + "dev": true, + "dependencies": { + "yocto-queue": "^0.1.0" + }, + "engines": { + "node": ">=10" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/p-locate": { + "version": "5.0.0", + "resolved": "https://registry.npmjs.org/p-locate/-/p-locate-5.0.0.tgz", + "integrity": "sha512-LaNjtRWUBY++zB5nE/NwcaoMylSPk+S+ZHNB1TzdbMJMny6dynpAGt7X/tl/QYq3TIeE6nxHppbo2LGymrG5Pw==", + "dev": true, + "dependencies": { + "p-limit": "^3.0.2" + }, + "engines": { + "node": ">=10" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/p-map": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/p-map/-/p-map-3.0.0.tgz", + "integrity": "sha512-d3qXVTF/s+W+CdJ5A29wywV2n8CQQYahlgz2bFiA+4eVNJbHJodPZ+/gXwPGh0bOqA+j8S+6+ckmvLGPk1QpxQ==", + "dev": true, + "dependencies": { + "aggregate-error": "^3.0.0" + }, + "engines": { + "node": ">=8" + } + }, + "node_modules/p-try": { + "version": "2.2.0", + "resolved": "https://registry.npmjs.org/p-try/-/p-try-2.2.0.tgz", + "integrity": "sha512-R4nPAVTAU0B9D35/Gk3uJf/7XYbQcyohSKdvAxIRSNghFl4e71hVoGnBNQz9cWaXxO2I10KTC+3jMdvvoKw6dQ==", + "dev": true, + "engines": { + "node": ">=6" + } + }, + "node_modules/package-hash": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/package-hash/-/package-hash-4.0.0.tgz", + "integrity": "sha512-whdkPIooSu/bASggZ96BWVvZTRMOFxnyUG5PnTSGKoJE2gd5mbVNmR2Nj20QFzxYYgAXpoqC+AiXzl+UMRh7zQ==", + "dev": true, + "dependencies": { + "graceful-fs": "^4.1.15", + "hasha": "^5.0.0", + "lodash.flattendeep": "^4.4.0", + "release-zalgo": "^1.0.0" + }, + "engines": { + "node": ">=8" + } + }, + "node_modules/package-json": { + "version": "6.5.0", + "resolved": "https://registry.npmjs.org/package-json/-/package-json-6.5.0.tgz", + "integrity": "sha512-k3bdm2n25tkyxcjSKzB5x8kfVxlMdgsbPr0GkZcwHsLpba6cBjqCt1KlcChKEvxHIcTB1FVMuwoijZ26xex5MQ==", + "dev": true, + "dependencies": { + "got": "^9.6.0", + "registry-auth-token": "^4.0.0", + "registry-url": "^5.0.0", + "semver": "^6.2.0" + }, + "engines": { + "node": ">=8" + } + }, + "node_modules/package-json/node_modules/@sindresorhus/is": { + "version": "0.14.0", + "resolved": "https://registry.npmjs.org/@sindresorhus/is/-/is-0.14.0.tgz", + "integrity": "sha512-9NET910DNaIPngYnLLPeg+Ogzqsi9uM4mSboU5y6p8S5DzMTVEsJZrawi+BoDNUVBa2DhJqQYUFvMDfgU062LQ==", + "dev": true, + "engines": { + "node": ">=6" + } + }, + "node_modules/package-json/node_modules/@szmarczak/http-timer": { + "version": "1.1.2", + "resolved": "https://registry.npmjs.org/@szmarczak/http-timer/-/http-timer-1.1.2.tgz", + "integrity": "sha512-XIB2XbzHTN6ieIjfIMV9hlVcfPU26s2vafYWQcZHWXHOxiaRZYEDKEwdl129Zyg50+foYV2jCgtrqSA6qNuNSA==", + "dev": true, + "dependencies": { + "defer-to-connect": "^1.0.1" + }, + "engines": { + "node": ">=6" + } + }, + "node_modules/package-json/node_modules/cacheable-request": { + "version": "6.1.0", + "resolved": "https://registry.npmjs.org/cacheable-request/-/cacheable-request-6.1.0.tgz", + "integrity": "sha512-Oj3cAGPCqOZX7Rz64Uny2GYAZNliQSqfbePrgAQ1wKAihYmCUnraBtJtKcGR4xz7wF+LoJC+ssFZvv5BgF9Igg==", + "dev": true, + "dependencies": { + "clone-response": "^1.0.2", + "get-stream": "^5.1.0", + "http-cache-semantics": "^4.0.0", + "keyv": "^3.0.0", + "lowercase-keys": "^2.0.0", + "normalize-url": "^4.1.0", + "responselike": "^1.0.2" + }, + "engines": { + "node": ">=8" + } + }, + "node_modules/package-json/node_modules/cacheable-request/node_modules/get-stream": { + "version": "5.2.0", + "resolved": "https://registry.npmjs.org/get-stream/-/get-stream-5.2.0.tgz", + "integrity": "sha512-nBF+F1rAZVCu/p7rjzgA+Yb4lfYXrpl7a6VmJrU8wF9I1CKvP/QwPNZHnOlwbTkY6dvtFIzFMSyQXbLoTQPRpA==", + "dev": true, + "dependencies": { + "pump": "^3.0.0" + }, + "engines": { + "node": ">=8" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/package-json/node_modules/decompress-response": { + "version": "3.3.0", + "resolved": "https://registry.npmjs.org/decompress-response/-/decompress-response-3.3.0.tgz", + "integrity": "sha1-gKTdMjdIOEv6JICDYirt7Jgq3/M=", + "dev": true, + "dependencies": { + "mimic-response": "^1.0.0" + }, + "engines": { + "node": ">=4" + } + }, + "node_modules/package-json/node_modules/defer-to-connect": { + "version": "1.1.3", + "resolved": "https://registry.npmjs.org/defer-to-connect/-/defer-to-connect-1.1.3.tgz", + "integrity": "sha512-0ISdNousHvZT2EiFlZeZAHBUvSxmKswVCEf8hW7KWgG4a8MVEu/3Vb6uWYozkjylyCxe0JBIiRB1jV45S70WVQ==", + "dev": true + }, + "node_modules/package-json/node_modules/get-stream": { + "version": "4.1.0", + "resolved": "https://registry.npmjs.org/get-stream/-/get-stream-4.1.0.tgz", + "integrity": "sha512-GMat4EJ5161kIy2HevLlr4luNjBgvmj413KaQA7jt4V8B4RDsfpHk7WQ9GVqfYyyx8OS/L66Kox+rJRNklLK7w==", + "dev": true, + "dependencies": { + "pump": "^3.0.0" + }, + "engines": { + "node": ">=6" + } + }, + "node_modules/package-json/node_modules/got": { + "version": "9.6.0", + "resolved": "https://registry.npmjs.org/got/-/got-9.6.0.tgz", + "integrity": "sha512-R7eWptXuGYxwijs0eV+v3o6+XH1IqVK8dJOEecQfTmkncw9AV4dcw/Dhxi8MdlqPthxxpZyizMzyg8RTmEsG+Q==", + "dev": true, + "dependencies": { + "@sindresorhus/is": "^0.14.0", + "@szmarczak/http-timer": "^1.1.2", + "cacheable-request": "^6.0.0", + "decompress-response": "^3.3.0", + "duplexer3": "^0.1.4", + "get-stream": "^4.1.0", + "lowercase-keys": "^1.0.1", + "mimic-response": "^1.0.1", + "p-cancelable": "^1.0.0", + "to-readable-stream": "^1.0.0", + "url-parse-lax": "^3.0.0" + }, + "engines": { + "node": ">=8.6" + } + }, + "node_modules/package-json/node_modules/got/node_modules/lowercase-keys": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/lowercase-keys/-/lowercase-keys-1.0.1.tgz", + "integrity": "sha512-G2Lj61tXDnVFFOi8VZds+SoQjtQC3dgokKdDG2mTm1tx4m50NUHBOZSBwQQHyy0V12A0JTG4icfZQH+xPyh8VA==", + "dev": true, + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/package-json/node_modules/json-buffer": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/json-buffer/-/json-buffer-3.0.0.tgz", + "integrity": "sha1-Wx85evx11ne96Lz8Dkfh+aPZqJg=", + "dev": true + }, + "node_modules/package-json/node_modules/keyv": { + "version": "3.1.0", + "resolved": "https://registry.npmjs.org/keyv/-/keyv-3.1.0.tgz", + "integrity": "sha512-9ykJ/46SN/9KPM/sichzQ7OvXyGDYKGTaDlKMGCAlg2UK8KRy4jb0d8sFc+0Tt0YYnThq8X2RZgCg74RPxgcVA==", + "dev": true, + "dependencies": { + "json-buffer": "3.0.0" + } + }, + "node_modules/package-json/node_modules/normalize-url": { + "version": "4.5.1", + "resolved": "https://registry.npmjs.org/normalize-url/-/normalize-url-4.5.1.tgz", + "integrity": "sha512-9UZCFRHQdNrfTpGg8+1INIg93B6zE0aXMVFkw1WFwvO4SlZywU6aLg5Of0Ap/PgcbSw4LNxvMWXMeugwMCX0AA==", + "dev": true, + "engines": { + "node": ">=8" + } + }, + "node_modules/package-json/node_modules/p-cancelable": { + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/p-cancelable/-/p-cancelable-1.1.0.tgz", + "integrity": "sha512-s73XxOZ4zpt1edZYZzvhqFa6uvQc1vwUa0K0BdtIZgQMAJj9IbebH+JkgKZc9h+B05PKHLOTl4ajG1BmNrVZlw==", + "dev": true, + "engines": { + "node": ">=6" + } + }, + "node_modules/package-json/node_modules/responselike": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/responselike/-/responselike-1.0.2.tgz", + "integrity": "sha1-kYcg7ztjHFZCvgaPFa3lpG9Loec=", + "dev": true, + "dependencies": { + "lowercase-keys": "^1.0.0" + } + }, + "node_modules/package-json/node_modules/responselike/node_modules/lowercase-keys": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/lowercase-keys/-/lowercase-keys-1.0.1.tgz", + "integrity": "sha512-G2Lj61tXDnVFFOi8VZds+SoQjtQC3dgokKdDG2mTm1tx4m50NUHBOZSBwQQHyy0V12A0JTG4icfZQH+xPyh8VA==", + "dev": true, + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/parent-module": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/parent-module/-/parent-module-1.0.1.tgz", + "integrity": "sha512-GQ2EWRpQV8/o+Aw8YqtfZZPfNRWZYkbidE9k5rpl/hC3vtHHBfGm2Ifi6qWV+coDGkrUKZAxE3Lot5kcsRlh+g==", + "dev": true, + "dependencies": { + "callsites": "^3.0.0" + }, + "engines": { + "node": ">=6" + } + }, + "node_modules/parse-json": { + "version": "5.2.0", + "resolved": "https://registry.npmjs.org/parse-json/-/parse-json-5.2.0.tgz", + "integrity": "sha512-ayCKvm/phCGxOkYRSCM82iDwct8/EonSEgCSxWxD7ve6jHggsFl4fZVQBPRNgQoKiuV/odhFrGzQXZwbifC8Rg==", + "dev": true, + "dependencies": { + "@babel/code-frame": "^7.0.0", + "error-ex": "^1.3.1", + "json-parse-even-better-errors": "^2.3.0", + "lines-and-columns": "^1.1.6" + }, + "engines": { + "node": ">=8" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/parse-path": { + "version": "4.0.3", + "resolved": "https://registry.npmjs.org/parse-path/-/parse-path-4.0.3.tgz", + "integrity": "sha512-9Cepbp2asKnWTJ9x2kpw6Fe8y9JDbqwahGCTvklzd/cEq5C5JC59x2Xb0Kx+x0QZ8bvNquGO8/BWP0cwBHzSAA==", + "dev": true, + "dependencies": { + "is-ssh": "^1.3.0", + "protocols": "^1.4.0", + "qs": "^6.9.4", + "query-string": "^6.13.8" + } + }, + "node_modules/parse-url": { + "version": "6.0.0", + "resolved": "https://registry.npmjs.org/parse-url/-/parse-url-6.0.0.tgz", + "integrity": "sha512-cYyojeX7yIIwuJzledIHeLUBVJ6COVLeT4eF+2P6aKVzwvgKQPndCBv3+yQ7pcWjqToYwaligxzSYNNmGoMAvw==", + "dev": true, + "dependencies": { + "is-ssh": "^1.3.0", + "normalize-url": "^6.1.0", + "parse-path": "^4.0.0", + "protocols": "^1.4.0" + } + }, + "node_modules/path-exists": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/path-exists/-/path-exists-4.0.0.tgz", + "integrity": "sha512-ak9Qy5Q7jYb2Wwcey5Fpvg2KoAc/ZIhLSLOSBmRmygPsGwkVVt0fZa0qrtMz+m6tJTAHfZQ8FnmB4MG4LWy7/w==", + "dev": true, + "engines": { + "node": ">=8" + } + }, + "node_modules/path-is-absolute": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/path-is-absolute/-/path-is-absolute-1.0.1.tgz", + "integrity": "sha1-F0uSaHNVNP+8es5r9TpanhtcX18=", + "dev": true, + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/path-key": { + "version": "3.1.1", + "resolved": "https://registry.npmjs.org/path-key/-/path-key-3.1.1.tgz", + "integrity": "sha512-ojmeN0qd+y0jszEtoY48r0Peq5dwMEkIlCOu6Q5f41lfkswXuKtYrhgoTpLnyIcHm24Uhqx+5Tqm2InSwLhE6Q==", + "dev": true, + "engines": { + "node": ">=8" + } + }, + "node_modules/path-parse": { + "version": "1.0.7", + "resolved": "https://registry.npmjs.org/path-parse/-/path-parse-1.0.7.tgz", + "integrity": "sha512-LDJzPVEEEPR+y48z93A0Ed0yXb8pAByGWo/k5YYdYgpY2/2EsOsksJrq7lOHxryrVOn1ejG6oAp8ahvOIQD8sw==", + "dev": true + }, + "node_modules/path-to-regexp": { + "version": "1.8.0", + "resolved": "https://registry.npmjs.org/path-to-regexp/-/path-to-regexp-1.8.0.tgz", + "integrity": "sha512-n43JRhlUKUAlibEJhPeir1ncUID16QnEjNpwzNdO3Lm4ywrBpBZ5oLD0I6br9evr1Y9JTqwRtAh7JLoOzAQdVA==", + "dev": true, + "dependencies": { + "isarray": "0.0.1" + } + }, + "node_modules/path-type": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/path-type/-/path-type-4.0.0.tgz", + "integrity": "sha512-gDKb8aZMDeD/tZWs9P6+q0J9Mwkdl6xMV8TjnGP3qJVJ06bdMgkbBlLU8IdfOsIsFz2BW1rNVT3XuNEl8zPAvw==", + "dev": true, + "engines": { + "node": ">=8" + } + }, + "node_modules/picomatch": { + "version": "2.3.0", + "resolved": "https://registry.npmjs.org/picomatch/-/picomatch-2.3.0.tgz", + "integrity": "sha512-lY1Q/PiJGC2zOv/z391WOTD+Z02bCgsFfvxoXXf6h7kv9o+WmsmzYqrAwY63sNgOxE4xEdq0WyUnXfKeBrSvYw==", + "dev": true, + "engines": { + "node": ">=8.6" + }, + "funding": { + "url": "https://github.com/sponsors/jonschlinkert" + } + }, + "node_modules/pkg-dir": { + "version": "4.2.0", + "resolved": "https://registry.npmjs.org/pkg-dir/-/pkg-dir-4.2.0.tgz", + "integrity": "sha512-HRDzbaKjC+AOWVXxAU/x54COGeIv9eb+6CkDSQoNTt4XyWoIJvuPsXizxu/Fr23EiekbtZwmh1IcIG/l/a10GQ==", + "dev": true, + "dependencies": { + "find-up": "^4.0.0" + }, + "engines": { + "node": ">=8" + } + }, + "node_modules/pkg-dir/node_modules/find-up": { + "version": "4.1.0", + "resolved": "https://registry.npmjs.org/find-up/-/find-up-4.1.0.tgz", + "integrity": "sha512-PpOwAdQ/YlXQ2vj8a3h8IipDuYRi3wceVQQGYWxNINccq40Anw7BlsEXCMbt1Zt+OLA6Fq9suIpIWD0OsnISlw==", + "dev": true, + "dependencies": { + "locate-path": "^5.0.0", + "path-exists": "^4.0.0" + }, + "engines": { + "node": ">=8" + } + }, + "node_modules/pkg-dir/node_modules/locate-path": { + "version": "5.0.0", + "resolved": "https://registry.npmjs.org/locate-path/-/locate-path-5.0.0.tgz", + "integrity": "sha512-t7hw9pI+WvuwNJXwk5zVHpyhIqzg2qTlklJOf0mVxGSbe3Fp2VieZcduNYjaLDoy6p9uGpQEGWG87WpMKlNq8g==", + "dev": true, + "dependencies": { + "p-locate": "^4.1.0" + }, + "engines": { + "node": ">=8" + } + }, + "node_modules/pkg-dir/node_modules/p-limit": { + "version": "2.3.0", + "resolved": "https://registry.npmjs.org/p-limit/-/p-limit-2.3.0.tgz", + "integrity": "sha512-//88mFWSJx8lxCzwdAABTJL2MyWB12+eIY7MDL2SqLmAkeKU9qxRvWuSyTjm3FUmpBEMuFfckAIqEaVGUDxb6w==", + "dev": true, + "dependencies": { + "p-try": "^2.0.0" + }, + "engines": { + "node": ">=6" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/pkg-dir/node_modules/p-locate": { + "version": "4.1.0", + "resolved": "https://registry.npmjs.org/p-locate/-/p-locate-4.1.0.tgz", + "integrity": "sha512-R79ZZ/0wAxKGu3oYMlz8jy/kbhsNrS7SKZ7PxEHBgJ5+F2mtFW2fK2cOtBh1cHYkQsbzFV7I+EoRKe6Yt0oK7A==", + "dev": true, + "dependencies": { + "p-limit": "^2.2.0" + }, + "engines": { + "node": ">=8" + } + }, + "node_modules/prepend-http": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/prepend-http/-/prepend-http-2.0.0.tgz", + "integrity": "sha1-6SQ0v6XqjBn0HN/UAddBo8gZ2Jc=", + "dev": true, + "engines": { + "node": ">=4" + } + }, + "node_modules/process-on-spawn": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/process-on-spawn/-/process-on-spawn-1.0.0.tgz", + "integrity": "sha512-1WsPDsUSMmZH5LeMLegqkPDrsGgsWwk1Exipy2hvB0o/F0ASzbpIctSCcZIK1ykJvtTJULEH+20WOFjMvGnCTg==", + "dev": true, + "dependencies": { + "fromentries": "^1.2.0" + }, + "engines": { + "node": ">=8" + } + }, + "node_modules/progress": { + "version": "2.0.3", + "resolved": "https://registry.npmjs.org/progress/-/progress-2.0.3.tgz", + "integrity": "sha512-7PiHtLll5LdnKIMw100I+8xJXR5gW2QwWYkT6iJva0bXitZKa/XMrSbdmg3r2Xnaidz9Qumd0VPaMrZlF9V9sA==", + "dev": true, + "engines": { + "node": ">=0.4.0" + } + }, + "node_modules/protocols": { + "version": "1.4.8", + "resolved": "https://registry.npmjs.org/protocols/-/protocols-1.4.8.tgz", + "integrity": "sha512-IgjKyaUSjsROSO8/D49Ab7hP8mJgTYcqApOqdPhLoPxAplXmkp+zRvsrSQjFn5by0rhm4VH0GAUELIPpx7B1yg==", + "dev": true + }, + "node_modules/pump": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/pump/-/pump-3.0.0.tgz", + "integrity": "sha512-LwZy+p3SFs1Pytd/jYct4wpv49HiYCqd9Rlc5ZVdk0V+8Yzv6jR5Blk3TRmPL1ft69TxP0IMZGJ+WPFU2BFhww==", + "dev": true, + "dependencies": { + "end-of-stream": "^1.1.0", + "once": "^1.3.1" + } + }, + "node_modules/pupa": { + "version": "2.1.1", + "resolved": "https://registry.npmjs.org/pupa/-/pupa-2.1.1.tgz", + "integrity": "sha512-l1jNAspIBSFqbT+y+5FosojNpVpF94nlI+wDUpqP9enwOTfHx9f0gh5nB96vl+6yTpsJsypeNrwfzPrKuHB41A==", + "dev": true, + "dependencies": { + "escape-goat": "^2.0.0" + }, + "engines": { + "node": ">=8" + } + }, + "node_modules/qs": { + "version": "6.10.1", + "resolved": "https://registry.npmjs.org/qs/-/qs-6.10.1.tgz", + "integrity": "sha512-M528Hph6wsSVOBiYUnGf+K/7w0hNshs/duGsNXPUCLH5XAqjEtiPGwNONLV0tBH8NoGb0mvD5JubnUTrujKDTg==", + "dev": true, + "dependencies": { + "side-channel": "^1.0.4" + }, + "engines": { + "node": ">=0.6" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/query-string": { + "version": "6.14.1", + "resolved": "https://registry.npmjs.org/query-string/-/query-string-6.14.1.tgz", + "integrity": "sha512-XDxAeVmpfu1/6IjyT/gXHOl+S0vQ9owggJ30hhWKdHAsNPOcasn5o9BW0eejZqL2e4vMjhAxoW3jVHcD6mbcYw==", + "dev": true, + "dependencies": { + "decode-uri-component": "^0.2.0", + "filter-obj": "^1.1.0", + "split-on-first": "^1.0.0", + "strict-uri-encode": "^2.0.0" + }, + "engines": { + "node": ">=6" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/queue-microtask": { + "version": "1.2.3", + "resolved": "https://registry.npmjs.org/queue-microtask/-/queue-microtask-1.2.3.tgz", + "integrity": "sha512-NuaNSa6flKT5JaSYQzJok04JzTL1CA6aGhv5rfLW3PgqA+M2ChpZQnAC8h8i4ZFkBS8X5RqkDBHA7r4hej3K9A==", + "dev": true, + "funding": [ + { + "type": "github", + "url": "https://github.com/sponsors/feross" + }, + { + "type": "patreon", + "url": "https://www.patreon.com/feross" + }, + { + "type": "consulting", + "url": "https://feross.org/support" + } + ] + }, + "node_modules/quick-lru": { + "version": "5.1.1", + "resolved": "https://registry.npmjs.org/quick-lru/-/quick-lru-5.1.1.tgz", + "integrity": "sha512-WuyALRjWPDGtt/wzJiadO5AXY+8hZ80hVpe6MyivgraREW751X3SbhRvG3eLKOYN+8VEvqLcf3wdnt44Z4S4SA==", + "dev": true, + "engines": { + "node": ">=10" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/randombytes": { + "version": "2.1.0", + "resolved": "https://registry.npmjs.org/randombytes/-/randombytes-2.1.0.tgz", + "integrity": "sha512-vYl3iOX+4CKUWuxGi9Ukhie6fsqXqS9FE2Zaic4tNFD2N2QQaXOMFbuKK4QmDHC0JO6B1Zp41J0LpT0oR68amQ==", + "dev": true, + "dependencies": { + "safe-buffer": "^5.1.0" + } + }, + "node_modules/rc": { + "version": "1.2.8", + "resolved": "https://registry.npmjs.org/rc/-/rc-1.2.8.tgz", + "integrity": "sha512-y3bGgqKj3QBdxLbLkomlohkvsA8gdAiUQlSBJnBhfn+BPxg4bc62d8TcBW15wavDfgexCgccckhcZvywyQYPOw==", + "dev": true, + "dependencies": { + "deep-extend": "^0.6.0", + "ini": "~1.3.0", + "minimist": "^1.2.0", + "strip-json-comments": "~2.0.1" + }, + "bin": { + "rc": "cli.js" + } + }, + "node_modules/rc/node_modules/ini": { + "version": "1.3.8", + "resolved": "https://registry.npmjs.org/ini/-/ini-1.3.8.tgz", + "integrity": "sha512-JV/yugV2uzW5iMRSiZAyDtQd+nxtUnjeLt0acNdw98kKLrvuRVyB80tsREOE7yvGVgalhZ6RNXCmEHkUKBKxew==", + "dev": true + }, + "node_modules/rc/node_modules/strip-json-comments": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/strip-json-comments/-/strip-json-comments-2.0.1.tgz", + "integrity": "sha1-PFMZQukIwml8DsNEhYwobHygpgo=", + "dev": true, + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/readable-stream": { + "version": "3.6.0", + "resolved": "https://registry.npmjs.org/readable-stream/-/readable-stream-3.6.0.tgz", + "integrity": "sha512-BViHy7LKeTz4oNnkcLJ+lVSL6vpiFeX6/d3oSH8zCW7UxP2onchk+vTGB143xuFjHS3deTgkKoXXymXqymiIdA==", + "dev": true, + "dependencies": { + "inherits": "^2.0.3", + "string_decoder": "^1.1.1", + "util-deprecate": "^1.0.1" + }, + "engines": { + "node": ">= 6" + } + }, + "node_modules/readdirp": { + "version": "3.6.0", + "resolved": "https://registry.npmjs.org/readdirp/-/readdirp-3.6.0.tgz", + "integrity": "sha512-hOS089on8RduqdbhvQ5Z37A0ESjsqz6qnRcffsMU3495FuTdqSm+7bhJ29JvIOsBDEEnan5DPu9t3To9VRlMzA==", + "dev": true, + "dependencies": { + "picomatch": "^2.2.1" + }, + "engines": { + "node": ">=8.10.0" + } + }, + "node_modules/rechoir": { + "version": "0.6.2", + "resolved": "https://registry.npmjs.org/rechoir/-/rechoir-0.6.2.tgz", + "integrity": "sha1-hSBLVNuoLVdC4oyWdW70OvUOM4Q=", + "dev": true, + "dependencies": { + "resolve": "^1.1.6" + }, + "engines": { + "node": ">= 0.10" + } + }, + "node_modules/redis-errors": { + "version": "1.2.0", + "resolved": "https://registry.npmjs.org/redis-errors/-/redis-errors-1.2.0.tgz", + "integrity": "sha1-62LSrbFeTq9GEMBK/hUpOEJQq60=", + "engines": { + "node": ">=4" + } + }, + "node_modules/redis-parser": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/redis-parser/-/redis-parser-3.0.0.tgz", + "integrity": "sha1-tm2CjNyv5rS4pCin3vTGvKwxyLQ=", + "dependencies": { + "redis-errors": "^1.0.0" + }, + "engines": { + "node": ">=4" + } + }, + "node_modules/registry-auth-token": { + "version": "4.2.1", + "resolved": "https://registry.npmjs.org/registry-auth-token/-/registry-auth-token-4.2.1.tgz", + "integrity": "sha512-6gkSb4U6aWJB4SF2ZvLb76yCBjcvufXBqvvEx1HbmKPkutswjW1xNVRY0+daljIYRbogN7O0etYSlbiaEQyMyw==", + "dev": true, + "dependencies": { + "rc": "^1.2.8" + }, + "engines": { + "node": ">=6.0.0" + } + }, + "node_modules/registry-url": { + "version": "5.1.0", + "resolved": "https://registry.npmjs.org/registry-url/-/registry-url-5.1.0.tgz", + "integrity": "sha512-8acYXXTI0AkQv6RAOjE3vOaIXZkT9wo4LOFbBKYQEEnnMNBpKqdUrI6S4NT0KPIo/WVvJ5tE/X5LF/TQUf0ekw==", + "dev": true, + "dependencies": { + "rc": "^1.2.8" + }, + "engines": { + "node": ">=8" + } + }, + "node_modules/release-it": { + "version": "14.11.5", + "resolved": "https://registry.npmjs.org/release-it/-/release-it-14.11.5.tgz", + "integrity": "sha512-9BaPdq7ZKOwtzz3p1mRhg/tOH/cT/y2tUnPYzUwQiVdj42JaGI1Vo2l3WbgK8ICsbFyrhc0tri1+iqI8OvkI1A==", + "dev": true, + "dependencies": { + "@iarna/toml": "2.2.5", + "@octokit/rest": "18.9.0", + "async-retry": "1.3.1", + "chalk": "4.1.2", + "cosmiconfig": "7.0.0", + "debug": "4.3.2", + "deprecated-obj": "2.0.0", + "execa": "5.1.1", + "form-data": "4.0.0", + "git-url-parse": "11.5.0", + "globby": "11.0.4", + "got": "11.8.2", + "import-cwd": "3.0.0", + "inquirer": "8.1.2", + "is-ci": "3.0.0", + "lodash": "4.17.21", + "mime-types": "2.1.32", + "new-github-release-url": "1.0.0", + "open": "7.4.2", + "ora": "5.4.1", + "os-name": "4.0.1", + "parse-json": "5.2.0", + "semver": "7.3.5", + "shelljs": "0.8.4", + "update-notifier": "5.1.0", + "url-join": "4.0.1", + "uuid": "8.3.2", + "yaml": "1.10.2", + "yargs-parser": "20.2.9" + }, + "bin": { + "release-it": "bin/release-it.js" + }, + "engines": { + "node": ">=10" + } + }, + "node_modules/release-it/node_modules/debug": { + "version": "4.3.2", + "resolved": "https://registry.npmjs.org/debug/-/debug-4.3.2.tgz", + "integrity": "sha512-mOp8wKcvj7XxC78zLgw/ZA+6TSgkoE2C/ienthhRD298T7UNwAg9diBpLRxC0mOezLl4B0xV7M0cCO6P/O0Xhw==", + "dev": true, + "dependencies": { + "ms": "2.1.2" + }, + "engines": { + "node": ">=6.0" + }, + "peerDependenciesMeta": { + "supports-color": { + "optional": true + } + } + }, + "node_modules/release-it/node_modules/lru-cache": { + "version": "6.0.0", + "resolved": "https://registry.npmjs.org/lru-cache/-/lru-cache-6.0.0.tgz", + "integrity": "sha512-Jo6dJ04CmSjuznwJSS3pUeWmd/H0ffTlkXXgwZi+eq1UCmqQwCh+eLsYOYCwY991i2Fah4h1BEMCx4qThGbsiA==", + "dev": true, + "dependencies": { + "yallist": "^4.0.0" + }, + "engines": { + "node": ">=10" + } + }, + "node_modules/release-it/node_modules/ms": { + "version": "2.1.2", + "resolved": "https://registry.npmjs.org/ms/-/ms-2.1.2.tgz", + "integrity": "sha512-sGkPx+VjMtmA6MX27oA4FBFELFCZZ4S4XqeGOXCv68tT+jb3vk/RyaKWP0PTKyWtmLSM0b+adUTEvbs1PEaH2w==", + "dev": true + }, + "node_modules/release-it/node_modules/semver": { + "version": "7.3.5", + "resolved": "https://registry.npmjs.org/semver/-/semver-7.3.5.tgz", + "integrity": "sha512-PoeGJYh8HK4BTO/a9Tf6ZG3veo/A7ZVsYrSA6J8ny9nb3B1VrpkuN+z9OE5wfE5p6H4LchYZsegiQgbJD94ZFQ==", + "dev": true, + "dependencies": { + "lru-cache": "^6.0.0" + }, + "bin": { + "semver": "bin/semver.js" + }, + "engines": { + "node": ">=10" + } + }, + "node_modules/release-it/node_modules/uuid": { + "version": "8.3.2", + "resolved": "https://registry.npmjs.org/uuid/-/uuid-8.3.2.tgz", + "integrity": "sha512-+NYs2QeMWy+GWFOEm9xnn6HCDp0l7QBD7ml8zLUmJ+93Q5NF0NocErnwkTkXVFNiX3/fpC6afS8Dhb/gz7R7eg==", + "dev": true, + "bin": { + "uuid": "dist/bin/uuid" + } + }, + "node_modules/release-it/node_modules/yargs-parser": { + "version": "20.2.9", + "resolved": "https://registry.npmjs.org/yargs-parser/-/yargs-parser-20.2.9.tgz", + "integrity": "sha512-y11nGElTIV+CT3Zv9t7VKl+Q3hTQoT9a1Qzezhhl6Rp21gJ/IVTW7Z3y9EWXhuUBC2Shnf+DX0antecpAwSP8w==", + "dev": true, + "engines": { + "node": ">=10" + } + }, + "node_modules/release-zalgo": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/release-zalgo/-/release-zalgo-1.0.0.tgz", + "integrity": "sha1-CXALflB0Mpc5Mw5TXFqQ+2eFFzA=", + "dev": true, + "dependencies": { + "es6-error": "^4.0.1" + }, + "engines": { + "node": ">=4" + } + }, + "node_modules/require-directory": { + "version": "2.1.1", + "resolved": "https://registry.npmjs.org/require-directory/-/require-directory-2.1.1.tgz", + "integrity": "sha1-jGStX9MNqxyXbiNE/+f3kqam30I=", + "dev": true, + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/require-main-filename": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/require-main-filename/-/require-main-filename-2.0.0.tgz", + "integrity": "sha512-NKN5kMDylKuldxYLSUfrbo5Tuzh4hd+2E8NPPX02mZtn1VuREQToYe/ZdlJy+J3uCpfaiGF05e7B8W0iXbQHmg==", + "dev": true + }, + "node_modules/resolve": { + "version": "1.20.0", + "resolved": "https://registry.npmjs.org/resolve/-/resolve-1.20.0.tgz", + "integrity": "sha512-wENBPt4ySzg4ybFQW2TT1zMQucPK95HSh/nq2CFTZVOGut2+pQvSsgtda4d26YrYcr067wjbmzOG8byDPBX63A==", + "dev": true, + "dependencies": { + "is-core-module": "^2.2.0", + "path-parse": "^1.0.6" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/resolve-alpn": { + "version": "1.2.1", + "resolved": "https://registry.npmjs.org/resolve-alpn/-/resolve-alpn-1.2.1.tgz", + "integrity": "sha512-0a1F4l73/ZFZOakJnQ3FvkJ2+gSTQWz/r2KE5OdDY0TxPm5h4GkqkWWfM47T7HsbnOtcJVEF4epCVy6u7Q3K+g==", + "dev": true + }, + "node_modules/resolve-from": { + "version": "5.0.0", + "resolved": "https://registry.npmjs.org/resolve-from/-/resolve-from-5.0.0.tgz", + "integrity": "sha512-qYg9KP24dD5qka9J47d0aVky0N+b4fTU89LN9iDnjB5waksiC49rvMB0PrUJQGoTmH50XPiqOvAjDfaijGxYZw==", + "dev": true, + "engines": { + "node": ">=8" + } + }, + "node_modules/responselike": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/responselike/-/responselike-2.0.0.tgz", + "integrity": "sha512-xH48u3FTB9VsZw7R+vvgaKeLKzT6jOogbQhEe/jewwnZgzPcnyWui2Av6JpoYZF/91uueC+lqhWqeURw5/qhCw==", + "dev": true, + "dependencies": { + "lowercase-keys": "^2.0.0" + } + }, + "node_modules/restore-cursor": { + "version": "3.1.0", + "resolved": "https://registry.npmjs.org/restore-cursor/-/restore-cursor-3.1.0.tgz", + "integrity": "sha512-l+sSefzHpj5qimhFSE5a8nufZYAM3sBSVMAPtYkmC+4EH2anSGaEMXSD0izRQbu9nfyQ9y5JrVmp7E8oZrUjvA==", + "dev": true, + "dependencies": { + "onetime": "^5.1.0", + "signal-exit": "^3.0.2" + }, + "engines": { + "node": ">=8" + } + }, + "node_modules/retry": { + "version": "0.12.0", + "resolved": "https://registry.npmjs.org/retry/-/retry-0.12.0.tgz", + "integrity": "sha1-G0KmJmoh8HQh0bC1S33BZ7AcATs=", + "dev": true, + "engines": { + "node": ">= 4" + } + }, + "node_modules/reusify": { + "version": "1.0.4", + "resolved": "https://registry.npmjs.org/reusify/-/reusify-1.0.4.tgz", + "integrity": "sha512-U9nH88a3fc/ekCF1l0/UP1IosiuIjyTh7hBvXVMHYgVcfGvt897Xguj2UOLDeI5BG2m7/uwyaLVT6fbtCwTyzw==", + "dev": true, + "engines": { + "iojs": ">=1.0.0", + "node": ">=0.10.0" + } + }, + "node_modules/rimraf": { + "version": "3.0.2", + "resolved": "https://registry.npmjs.org/rimraf/-/rimraf-3.0.2.tgz", + "integrity": "sha512-JZkJMZkAGFFPP2YqXZXPbMlMBgsxzE8ILs4lMIX/2o0L9UBw9O/Y3o6wFw/i9YLapcUJWwqbi3kdxIPdC62TIA==", + "dev": true, + "dependencies": { + "glob": "^7.1.3" + }, + "bin": { + "rimraf": "bin.js" + }, + "funding": { + "url": "https://github.com/sponsors/isaacs" + } + }, + "node_modules/run-async": { + "version": "2.4.1", + "resolved": "https://registry.npmjs.org/run-async/-/run-async-2.4.1.tgz", + "integrity": "sha512-tvVnVv01b8c1RrA6Ep7JkStj85Guv/YrMcwqYQnwjsAS2cTmmPGBBjAjpCW7RrSodNSoE2/qg9O4bceNvUuDgQ==", + "dev": true, + "engines": { + "node": ">=0.12.0" + } + }, + "node_modules/run-parallel": { + "version": "1.2.0", + "resolved": "https://registry.npmjs.org/run-parallel/-/run-parallel-1.2.0.tgz", + "integrity": "sha512-5l4VyZR86LZ/lDxZTR6jqL8AFE2S0IFLMP26AbjsLVADxHdhB/c0GUsH+y39UfCi3dzz8OlQuPmnaJOMoDHQBA==", + "dev": true, + "funding": [ + { + "type": "github", + "url": "https://github.com/sponsors/feross" + }, + { + "type": "patreon", + "url": "https://www.patreon.com/feross" + }, + { + "type": "consulting", + "url": "https://feross.org/support" + } + ], + "dependencies": { + "queue-microtask": "^1.2.2" + } + }, + "node_modules/rxjs": { + "version": "7.3.0", + "resolved": "https://registry.npmjs.org/rxjs/-/rxjs-7.3.0.tgz", + "integrity": "sha512-p2yuGIg9S1epc3vrjKf6iVb3RCaAYjYskkO+jHIaV0IjOPlJop4UnodOoFb2xeNwlguqLYvGw1b1McillYb5Gw==", + "dev": true, + "dependencies": { + "tslib": "~2.1.0" + } + }, + "node_modules/safe-buffer": { + "version": "5.1.2", + "resolved": "https://registry.npmjs.org/safe-buffer/-/safe-buffer-5.1.2.tgz", + "integrity": "sha512-Gd2UZBJDkXlY7GbJxfsE8/nvKkUEU1G38c1siN6QP6a9PT9MmHB8GnpscSmMJSoF8LOIrt8ud/wPtojys4G6+g==", + "dev": true + }, + "node_modules/safer-buffer": { + "version": "2.1.2", + "resolved": "https://registry.npmjs.org/safer-buffer/-/safer-buffer-2.1.2.tgz", + "integrity": "sha512-YZo3K82SD7Riyi0E1EQPojLz7kpepnSQI9IyPbHHg1XXXevb5dJI7tpyN2ADxGcQbHG7vcyRHk0cbwqcQriUtg==", + "dev": true + }, + "node_modules/semver": { + "version": "6.3.0", + "resolved": "https://registry.npmjs.org/semver/-/semver-6.3.0.tgz", + "integrity": "sha512-b39TBaTSfV6yBrapU89p5fKekE2m/NwnDocOVruQFS1/veMgdzuPcnOM34M6CwxW8jH/lxEa5rBoDeUwu5HHTw==", + "dev": true, + "bin": { + "semver": "bin/semver.js" + } + }, + "node_modules/semver-diff": { + "version": "3.1.1", + "resolved": "https://registry.npmjs.org/semver-diff/-/semver-diff-3.1.1.tgz", + "integrity": "sha512-GX0Ix/CJcHyB8c4ykpHGIAvLyOwOobtM/8d+TQkAd81/bEjgPHrfba41Vpesr7jX/t8Uh+R3EX9eAS5be+jQYg==", + "dev": true, + "dependencies": { + "semver": "^6.3.0" + }, + "engines": { + "node": ">=8" + } + }, + "node_modules/serialize-javascript": { + "version": "6.0.0", + "resolved": "https://registry.npmjs.org/serialize-javascript/-/serialize-javascript-6.0.0.tgz", + "integrity": "sha512-Qr3TosvguFt8ePWqsvRfrKyQXIiW+nGbYpy8XK24NQHE83caxWt+mIymTT19DGFbNWNLfEwsrkSmN64lVWB9ag==", + "dev": true, + "dependencies": { + "randombytes": "^2.1.0" + } + }, + "node_modules/set-blocking": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/set-blocking/-/set-blocking-2.0.0.tgz", + "integrity": "sha1-BF+XgtARrppoA93TgrJDkrPYkPc=", + "dev": true + }, + "node_modules/shebang-command": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/shebang-command/-/shebang-command-2.0.0.tgz", + "integrity": "sha512-kHxr2zZpYtdmrN1qDjrrX/Z1rR1kG8Dx+gkpK1G4eXmvXswmcE1hTWBWYUzlraYw1/yZp6YuDY77YtvbN0dmDA==", + "dev": true, + "dependencies": { + "shebang-regex": "^3.0.0" + }, + "engines": { + "node": ">=8" + } + }, + "node_modules/shebang-regex": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/shebang-regex/-/shebang-regex-3.0.0.tgz", + "integrity": "sha512-7++dFhtcx3353uBaq8DDR4NuxBetBzC7ZQOhmTQInHEd6bSrXdiEyzCvG07Z44UYdLShWUyXt5M/yhz8ekcb1A==", + "dev": true, + "engines": { + "node": ">=8" + } + }, + "node_modules/shelljs": { + "version": "0.8.4", + "resolved": "https://registry.npmjs.org/shelljs/-/shelljs-0.8.4.tgz", + "integrity": "sha512-7gk3UZ9kOfPLIAbslLzyWeGiEqx9e3rxwZM0KE6EL8GlGwjym9Mrlx5/p33bWTu9YG6vcS4MBxYZDHYr5lr8BQ==", + "dev": true, + "dependencies": { + "glob": "^7.0.0", + "interpret": "^1.0.0", + "rechoir": "^0.6.2" + }, + "bin": { + "shjs": "bin/shjs" + }, + "engines": { + "node": ">=4" + } + }, + "node_modules/shiki": { + "version": "0.9.8", + "resolved": "https://registry.npmjs.org/shiki/-/shiki-0.9.8.tgz", + "integrity": "sha512-499zQUTjcNTVwwiaPrWldUTXIV3T9HZWxDwE82bY+9GE7P2uD6hpHUTXNbTof3iOG6WT+/062+OMbl0lDoG8WQ==", + "dev": true, + "dependencies": { + "json5": "^2.2.0", + "onigasm": "^2.2.5", + "vscode-textmate": "5.2.0" + } + }, + "node_modules/side-channel": { + "version": "1.0.4", + "resolved": "https://registry.npmjs.org/side-channel/-/side-channel-1.0.4.tgz", + "integrity": "sha512-q5XPytqFEIKHkGdiMIrY10mvLRvnQh42/+GoBlFW3b2LXLE2xxJpZFdm94we0BaoV3RwJyGqg5wS7epxTv0Zvw==", + "dev": true, + "dependencies": { + "call-bind": "^1.0.0", + "get-intrinsic": "^1.0.2", + "object-inspect": "^1.9.0" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/signal-exit": { + "version": "3.0.3", + "resolved": "https://registry.npmjs.org/signal-exit/-/signal-exit-3.0.3.tgz", + "integrity": "sha512-VUJ49FC8U1OxwZLxIbTTrDvLnf/6TDgxZcK8wxR8zs13xpx7xbG60ndBlhNrFi2EMuFRoeDoJO7wthSLq42EjA==", + "dev": true + }, + "node_modules/sinon": { + "version": "11.1.2", + "resolved": "https://registry.npmjs.org/sinon/-/sinon-11.1.2.tgz", + "integrity": "sha512-59237HChms4kg7/sXhiRcUzdSkKuydDeTiamT/jesUVHshBgL8XAmhgFo0GfK6RruMDM/iRSij1EybmMog9cJw==", + "dev": true, + "dependencies": { + "@sinonjs/commons": "^1.8.3", + "@sinonjs/fake-timers": "^7.1.2", + "@sinonjs/samsam": "^6.0.2", + "diff": "^5.0.0", + "nise": "^5.1.0", + "supports-color": "^7.2.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/sinon" + } + }, + "node_modules/sinon/node_modules/supports-color": { + "version": "7.2.0", + "resolved": "https://registry.npmjs.org/supports-color/-/supports-color-7.2.0.tgz", + "integrity": "sha512-qpCAvRl9stuOHveKsn7HncJRvv501qIacKzQlO/+Lwxc9+0q2wLyv4Dfvt80/DPn2pqOBsJdDiogXGR9+OvwRw==", + "dev": true, + "dependencies": { + "has-flag": "^4.0.0" + }, + "engines": { + "node": ">=8" + } + }, + "node_modules/slash": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/slash/-/slash-3.0.0.tgz", + "integrity": "sha512-g9Q1haeby36OSStwb4ntCGGGaKsaVSjQ68fBxoQcutl5fS1vuY18H3wSt3jFyFtrkx+Kz0V1G85A4MyAdDMi2Q==", + "dev": true, + "engines": { + "node": ">=8" + } + }, + "node_modules/source-map": { + "version": "0.5.7", + "resolved": "https://registry.npmjs.org/source-map/-/source-map-0.5.7.tgz", + "integrity": "sha1-igOdLRAh0i0eoUyA2OpGi6LvP8w=", + "dev": true, + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/source-map-support": { + "version": "0.5.19", + "resolved": "https://registry.npmjs.org/source-map-support/-/source-map-support-0.5.19.tgz", + "integrity": "sha512-Wonm7zOCIJzBGQdB+thsPar0kYuCIzYvxZwlBa87yi/Mdjv7Tip2cyVbLj5o0cFPN4EVkuTwb3GDDyUx2DGnGw==", + "dev": true, + "dependencies": { + "buffer-from": "^1.0.0", + "source-map": "^0.6.0" + } + }, + "node_modules/source-map-support/node_modules/source-map": { + "version": "0.6.1", + "resolved": "https://registry.npmjs.org/source-map/-/source-map-0.6.1.tgz", + "integrity": "sha512-UjgapumWlbMhkBgzT7Ykc5YXUT46F0iKu8SGXq0bcwP5dz/h0Plj6enJqjz1Zbq2l5WaqYnrVbwWOWMyF3F47g==", + "dev": true, + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/spawn-wrap": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/spawn-wrap/-/spawn-wrap-2.0.0.tgz", + "integrity": "sha512-EeajNjfN9zMnULLwhZZQU3GWBoFNkbngTUPfaawT4RkMiviTxcX0qfhVbGey39mfctfDHkWtuecgQ8NJcyQWHg==", + "dev": true, + "dependencies": { + "foreground-child": "^2.0.0", + "is-windows": "^1.0.2", + "make-dir": "^3.0.0", + "rimraf": "^3.0.0", + "signal-exit": "^3.0.2", + "which": "^2.0.1" + }, + "engines": { + "node": ">=8" + } + }, + "node_modules/split-on-first": { + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/split-on-first/-/split-on-first-1.1.0.tgz", + "integrity": "sha512-43ZssAJaMusuKWL8sKUBQXHWOpq8d6CfN/u1p4gUzfJkM05C8rxTmYrkIPTXapZpORA6LkkzcUulJ8FqA7Uudw==", + "dev": true, + "engines": { + "node": ">=6" + } + }, + "node_modules/sprintf-js": { + "version": "1.0.3", + "resolved": "https://registry.npmjs.org/sprintf-js/-/sprintf-js-1.0.3.tgz", + "integrity": "sha1-BOaSb2YolTVPPdAVIDYzuFcpfiw=", + "dev": true + }, + "node_modules/strict-uri-encode": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/strict-uri-encode/-/strict-uri-encode-2.0.0.tgz", + "integrity": "sha1-ucczDHBChi9rFC3CdLvMWGbONUY=", + "dev": true, + "engines": { + "node": ">=4" + } + }, + "node_modules/string_decoder": { + "version": "1.3.0", + "resolved": "https://registry.npmjs.org/string_decoder/-/string_decoder-1.3.0.tgz", + "integrity": "sha512-hkRX8U1WjJFd8LsDJ2yQ/wWWxaopEsABU1XfkM8A+j0+85JAGppt16cr1Whg6KIbb4okU6Mql6BOj+uup/wKeA==", + "dev": true, + "dependencies": { + "safe-buffer": "~5.2.0" + } + }, + "node_modules/string_decoder/node_modules/safe-buffer": { + "version": "5.2.1", + "resolved": "https://registry.npmjs.org/safe-buffer/-/safe-buffer-5.2.1.tgz", + "integrity": "sha512-rp3So07KcdmmKbGvgaNxQSJr7bGVSVk5S9Eq1F+ppbRo70+YeaDxkw5Dd8NPN+GD6bjnYm2VuPuCXmpuYvmCXQ==", + "dev": true, + "funding": [ + { + "type": "github", + "url": "https://github.com/sponsors/feross" + }, + { + "type": "patreon", + "url": "https://www.patreon.com/feross" + }, + { + "type": "consulting", + "url": "https://feross.org/support" + } + ] + }, + "node_modules/string-width": { + "version": "4.2.2", + "resolved": "https://registry.npmjs.org/string-width/-/string-width-4.2.2.tgz", + "integrity": "sha512-XBJbT3N4JhVumXE0eoLU9DCjcaF92KLNqTmFCnG1pf8duUxFGwtP6AD6nkjw9a3IdiRtL3E2w3JDiE/xi3vOeA==", + "dev": true, + "dependencies": { + "emoji-regex": "^8.0.0", + "is-fullwidth-code-point": "^3.0.0", + "strip-ansi": "^6.0.0" + }, + "engines": { + "node": ">=8" + } + }, + "node_modules/strip-ansi": { + "version": "6.0.0", + "resolved": "https://registry.npmjs.org/strip-ansi/-/strip-ansi-6.0.0.tgz", + "integrity": "sha512-AuvKTrTfQNYNIctbR1K/YGTR1756GycPsg7b9bdV9Duqur4gv6aKqHXah67Z8ImS7WEz5QVcOtlfW2rZEugt6w==", + "dev": true, + "dependencies": { + "ansi-regex": "^5.0.0" + }, + "engines": { + "node": ">=8" + } + }, + "node_modules/strip-bom": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/strip-bom/-/strip-bom-4.0.0.tgz", + "integrity": "sha512-3xurFv5tEgii33Zi8Jtp55wEIILR9eh34FAW00PZf+JnSsTmV/ioewSgQl97JHvgjoRGwPShsWm+IdrxB35d0w==", + "dev": true, + "engines": { + "node": ">=8" + } + }, + "node_modules/strip-final-newline": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/strip-final-newline/-/strip-final-newline-2.0.0.tgz", + "integrity": "sha512-BrpvfNAE3dcvq7ll3xVumzjKjZQ5tI1sEUIKr3Uoks0XUl45St3FlatVqef9prk4jRDzhW6WZg+3bk93y6pLjA==", + "dev": true, + "engines": { + "node": ">=6" + } + }, + "node_modules/strip-json-comments": { + "version": "3.1.1", + "resolved": "https://registry.npmjs.org/strip-json-comments/-/strip-json-comments-3.1.1.tgz", + "integrity": "sha512-6fPc+R4ihwqP6N/aIv2f1gMH8lOVtWQHoqC4yK6oSDVVocumAsfCqjkXnqiYMhmMwS/mEHLp7Vehlt3ql6lEig==", + "dev": true, + "engines": { + "node": ">=8" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/supports-color": { + "version": "8.1.1", + "resolved": "https://registry.npmjs.org/supports-color/-/supports-color-8.1.1.tgz", + "integrity": "sha512-MpUEN2OodtUzxvKQl72cUF7RQ5EiHsGvSsVG0ia9c5RbWGL2CI4C7EpPS8UTBIplnlzZiNuV56w+FuNxy3ty2Q==", + "dev": true, + "dependencies": { + "has-flag": "^4.0.0" + }, + "engines": { + "node": ">=10" + }, + "funding": { + "url": "https://github.com/chalk/supports-color?sponsor=1" + } + }, + "node_modules/test-exclude": { + "version": "6.0.0", + "resolved": "https://registry.npmjs.org/test-exclude/-/test-exclude-6.0.0.tgz", + "integrity": "sha512-cAGWPIyOHU6zlmg88jwm7VRyXnMN7iV68OGAbYDk/Mh/xC/pzVPlQtY6ngoIH/5/tciuhGfvESU8GrHrcxD56w==", + "dev": true, + "dependencies": { + "@istanbuljs/schema": "^0.1.2", + "glob": "^7.1.4", + "minimatch": "^3.0.4" + }, + "engines": { + "node": ">=8" + } + }, + "node_modules/through": { + "version": "2.3.8", + "resolved": "https://registry.npmjs.org/through/-/through-2.3.8.tgz", + "integrity": "sha1-DdTJ/6q8NXlgsbckEV1+Doai4fU=", + "dev": true + }, + "node_modules/tmp": { + "version": "0.0.33", + "resolved": "https://registry.npmjs.org/tmp/-/tmp-0.0.33.tgz", + "integrity": "sha512-jRCJlojKnZ3addtTOjdIqoRuPEKBvNXcGYqzO6zWZX8KfKEpnGY5jfggJQ3EjKuu8D4bJRr0y+cYJFmYbImXGw==", + "dev": true, + "dependencies": { + "os-tmpdir": "~1.0.2" + }, + "engines": { + "node": ">=0.6.0" + } + }, + "node_modules/to-fast-properties": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/to-fast-properties/-/to-fast-properties-2.0.0.tgz", + "integrity": "sha1-3F5pjL0HkmW8c+A3doGk5Og/YW4=", + "dev": true, + "engines": { + "node": ">=4" + } + }, + "node_modules/to-readable-stream": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/to-readable-stream/-/to-readable-stream-1.0.0.tgz", + "integrity": "sha512-Iq25XBt6zD5npPhlLVXGFN3/gyR2/qODcKNNyTMd4vbm39HUaOiAM4PMq0eMVC/Tkxz+Zjdsc55g9yyz+Yq00Q==", + "dev": true, + "engines": { + "node": ">=6" + } + }, + "node_modules/to-regex-range": { + "version": "5.0.1", + "resolved": "https://registry.npmjs.org/to-regex-range/-/to-regex-range-5.0.1.tgz", + "integrity": "sha512-65P7iz6X5yEr1cwcgvQxbbIw7Uk3gOy5dIdtZ4rDveLqhrdJP+Li/Hx6tyK0NEb+2GCyneCMJiGqrADCSNk8sQ==", + "dev": true, + "dependencies": { + "is-number": "^7.0.0" + }, + "engines": { + "node": ">=8.0" + } + }, + "node_modules/ts-node": { + "version": "10.2.1", + "resolved": "https://registry.npmjs.org/ts-node/-/ts-node-10.2.1.tgz", + "integrity": "sha512-hCnyOyuGmD5wHleOQX6NIjJtYVIO8bPP8F2acWkB4W06wdlkgyvJtubO/I9NkI88hCFECbsEgoLc0VNkYmcSfw==", + "dev": true, + "dependencies": { + "@cspotcode/source-map-support": "0.6.1", + "@tsconfig/node10": "^1.0.7", + "@tsconfig/node12": "^1.0.7", + "@tsconfig/node14": "^1.0.0", + "@tsconfig/node16": "^1.0.2", + "acorn": "^8.4.1", + "acorn-walk": "^8.1.1", + "arg": "^4.1.0", + "create-require": "^1.1.0", + "diff": "^4.0.1", + "make-error": "^1.1.1", + "yn": "3.1.1" + }, + "bin": { + "ts-node": "dist/bin.js", + "ts-node-cwd": "dist/bin-cwd.js", + "ts-node-script": "dist/bin-script.js", + "ts-node-transpile-only": "dist/bin-transpile.js", + "ts-script": "dist/bin-script-deprecated.js" + }, + "engines": { + "node": ">=12.0.0" + }, + "peerDependencies": { + "@swc/core": ">=1.2.50", + "@swc/wasm": ">=1.2.50", + "@types/node": "*", + "typescript": ">=2.7" + }, + "peerDependenciesMeta": { + "@swc/core": { + "optional": true + }, + "@swc/wasm": { + "optional": true + } + } + }, + "node_modules/ts-node/node_modules/diff": { + "version": "4.0.2", + "resolved": "https://registry.npmjs.org/diff/-/diff-4.0.2.tgz", + "integrity": "sha512-58lmxKSA4BNyLz+HHMUzlOEpg09FV+ev6ZMe3vJihgdxzgcwZ8VoEEPmALCZG9LmqfVoNMMKpttIYTVG6uDY7A==", + "dev": true, + "engines": { + "node": ">=0.3.1" + } + }, + "node_modules/tslib": { + "version": "2.1.0", + "resolved": "https://registry.npmjs.org/tslib/-/tslib-2.1.0.tgz", + "integrity": "sha512-hcVC3wYEziELGGmEEXue7D75zbwIIVUMWAVbHItGPx0ziyXxrOMQx4rQEVEV45Ut/1IotuEvwqPopzIOkDMf0A==", + "dev": true + }, + "node_modules/type-detect": { + "version": "4.0.8", + "resolved": "https://registry.npmjs.org/type-detect/-/type-detect-4.0.8.tgz", + "integrity": "sha512-0fr/mIH1dlO+x7TlcMy+bIDqKPsw/70tVyeHW787goQjhmqaZe10uwLujubK9q9Lg6Fiho1KUKDYz0Z7k7g5/g==", + "dev": true, + "engines": { + "node": ">=4" + } + }, + "node_modules/type-fest": { + "version": "0.8.1", + "resolved": "https://registry.npmjs.org/type-fest/-/type-fest-0.8.1.tgz", + "integrity": "sha512-4dbzIzqvjtgiM5rw1k5rEHtBANKmdudhGyBEajN01fEyhaAIhsoKNy6y7+IN93IfpFtwY9iqi7kD+xwKhQsNJA==", + "dev": true, + "engines": { + "node": ">=8" + } + }, + "node_modules/typedarray-to-buffer": { + "version": "3.1.5", + "resolved": "https://registry.npmjs.org/typedarray-to-buffer/-/typedarray-to-buffer-3.1.5.tgz", + "integrity": "sha512-zdu8XMNEDepKKR+XYOXAVPtWui0ly0NtohUscw+UmaHiAWT8hrV1rr//H6V+0DvJ3OQ19S979M0laLfX8rm82Q==", + "dev": true, + "dependencies": { + "is-typedarray": "^1.0.0" + } + }, + "node_modules/typedoc": { + "version": "0.21.9", + "resolved": "https://registry.npmjs.org/typedoc/-/typedoc-0.21.9.tgz", + "integrity": "sha512-VRo7aII4bnYaBBM1lhw4bQFmUcDQV8m8tqgjtc7oXl87jc1Slbhfw2X5MccfcR2YnEClHDWgsiQGgNB8KJXocA==", + "dev": true, + "dependencies": { + "glob": "^7.1.7", + "handlebars": "^4.7.7", + "lunr": "^2.3.9", + "marked": "^3.0.2", + "minimatch": "^3.0.0", + "progress": "^2.0.3", + "shiki": "^0.9.8", + "typedoc-default-themes": "^0.12.10" + }, + "bin": { + "typedoc": "bin/typedoc" + }, + "engines": { + "node": ">= 12.10.0" + }, + "peerDependencies": { + "typescript": "4.0.x || 4.1.x || 4.2.x || 4.3.x || 4.4.x" + } + }, + "node_modules/typedoc-default-themes": { + "version": "0.12.10", + "resolved": "https://registry.npmjs.org/typedoc-default-themes/-/typedoc-default-themes-0.12.10.tgz", + "integrity": "sha512-fIS001cAYHkyQPidWXmHuhs8usjP5XVJjWB8oZGqkTowZaz3v7g3KDZeeqE82FBrmkAnIBOY3jgy7lnPnqATbA==", + "dev": true, + "engines": { + "node": ">= 8" + } + }, + "node_modules/typedoc-github-wiki-theme": { + "version": "0.5.1", + "resolved": "https://registry.npmjs.org/typedoc-github-wiki-theme/-/typedoc-github-wiki-theme-0.5.1.tgz", + "integrity": "sha512-ED2Uc3WUjbv6xIdCkpMz3yBtcEciJnAhDQdRWLYgw4K+FKY0T3PzbI+ssNsBVgwDnYQP/XuaqfZkeQ3EQsOm9g==", + "dev": true, + "peerDependencies": { + "typedoc": ">=0.20.0", + "typedoc-plugin-markdown": ">=3.4.0" + } + }, + "node_modules/typedoc-plugin-markdown": { + "version": "3.10.4", + "resolved": "https://registry.npmjs.org/typedoc-plugin-markdown/-/typedoc-plugin-markdown-3.10.4.tgz", + "integrity": "sha512-if9w7S9fXLg73AYi/EoRSEhTOZlg3I8mIP8YEmvzSE33VrNXC9/hA0nVcLEwFVJeQY7ay6z67I6kW0KIv7LjeA==", + "dev": true, + "dependencies": { + "handlebars": "^4.7.7" + }, + "peerDependencies": { + "typedoc": ">=0.21.2" + } + }, + "node_modules/typescript": { + "version": "4.4.2", + "resolved": "https://registry.npmjs.org/typescript/-/typescript-4.4.2.tgz", + "integrity": "sha512-gzP+t5W4hdy4c+68bfcv0t400HVJMMd2+H9B7gae1nQlBzCqvrXX+6GL/b3GAgyTH966pzrZ70/fRjwAtZksSQ==", + "dev": true, + "bin": { + "tsc": "bin/tsc", + "tsserver": "bin/tsserver" + }, + "engines": { + "node": ">=4.2.0" + } + }, + "node_modules/uglify-js": { + "version": "3.14.1", + "resolved": "https://registry.npmjs.org/uglify-js/-/uglify-js-3.14.1.tgz", + "integrity": "sha512-JhS3hmcVaXlp/xSo3PKY5R0JqKs5M3IV+exdLHW99qKvKivPO4Z8qbej6mte17SOPqAOVMjt/XGgWacnFSzM3g==", + "dev": true, + "optional": true, + "bin": { + "uglifyjs": "bin/uglifyjs" + }, + "engines": { + "node": ">=0.8.0" + } + }, + "node_modules/unique-string": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/unique-string/-/unique-string-2.0.0.tgz", + "integrity": "sha512-uNaeirEPvpZWSgzwsPGtU2zVSTrn/8L5q/IexZmH0eH6SA73CmAA5U4GwORTxQAZs95TAXLNqeLoPPNO5gZfWg==", + "dev": true, + "dependencies": { + "crypto-random-string": "^2.0.0" + }, + "engines": { + "node": ">=8" + } + }, + "node_modules/universal-user-agent": { + "version": "6.0.0", + "resolved": "https://registry.npmjs.org/universal-user-agent/-/universal-user-agent-6.0.0.tgz", + "integrity": "sha512-isyNax3wXoKaulPDZWHQqbmIx1k2tb9fb3GGDBRxCscfYV2Ch7WxPArBsFEG8s/safwXTT7H4QGhaIkTp9447w==", + "dev": true + }, + "node_modules/update-notifier": { + "version": "5.1.0", + "resolved": "https://registry.npmjs.org/update-notifier/-/update-notifier-5.1.0.tgz", + "integrity": "sha512-ItnICHbeMh9GqUy31hFPrD1kcuZ3rpxDZbf4KUDavXwS0bW5m7SLbDQpGX3UYr072cbrF5hFUs3r5tUsPwjfHw==", + "dev": true, + "dependencies": { + "boxen": "^5.0.0", + "chalk": "^4.1.0", + "configstore": "^5.0.1", + "has-yarn": "^2.1.0", + "import-lazy": "^2.1.0", + "is-ci": "^2.0.0", + "is-installed-globally": "^0.4.0", + "is-npm": "^5.0.0", + "is-yarn-global": "^0.3.0", + "latest-version": "^5.1.0", + "pupa": "^2.1.1", + "semver": "^7.3.4", + "semver-diff": "^3.1.1", + "xdg-basedir": "^4.0.0" + }, + "engines": { + "node": ">=10" + }, + "funding": { + "url": "https://github.com/yeoman/update-notifier?sponsor=1" + } + }, + "node_modules/update-notifier/node_modules/ci-info": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/ci-info/-/ci-info-2.0.0.tgz", + "integrity": "sha512-5tK7EtrZ0N+OLFMthtqOj4fI2Jeb88C4CAZPu25LDVUgXJ0A3Js4PMGqrn0JU1W0Mh1/Z8wZzYPxqUrXeBboCQ==", + "dev": true + }, + "node_modules/update-notifier/node_modules/is-ci": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/is-ci/-/is-ci-2.0.0.tgz", + "integrity": "sha512-YfJT7rkpQB0updsdHLGWrvhBJfcfzNNawYDNIyQXJz0IViGf75O8EBPKSdvw2rF+LGCsX4FZ8tcr3b19LcZq4w==", + "dev": true, + "dependencies": { + "ci-info": "^2.0.0" + }, + "bin": { + "is-ci": "bin.js" + } + }, + "node_modules/update-notifier/node_modules/lru-cache": { + "version": "6.0.0", + "resolved": "https://registry.npmjs.org/lru-cache/-/lru-cache-6.0.0.tgz", + "integrity": "sha512-Jo6dJ04CmSjuznwJSS3pUeWmd/H0ffTlkXXgwZi+eq1UCmqQwCh+eLsYOYCwY991i2Fah4h1BEMCx4qThGbsiA==", + "dev": true, + "dependencies": { + "yallist": "^4.0.0" + }, + "engines": { + "node": ">=10" + } + }, + "node_modules/update-notifier/node_modules/semver": { + "version": "7.3.5", + "resolved": "https://registry.npmjs.org/semver/-/semver-7.3.5.tgz", + "integrity": "sha512-PoeGJYh8HK4BTO/a9Tf6ZG3veo/A7ZVsYrSA6J8ny9nb3B1VrpkuN+z9OE5wfE5p6H4LchYZsegiQgbJD94ZFQ==", + "dev": true, + "dependencies": { + "lru-cache": "^6.0.0" + }, + "bin": { + "semver": "bin/semver.js" + }, + "engines": { + "node": ">=10" + } + }, + "node_modules/url-join": { + "version": "4.0.1", + "resolved": "https://registry.npmjs.org/url-join/-/url-join-4.0.1.tgz", + "integrity": "sha512-jk1+QP6ZJqyOiuEI9AEWQfju/nB2Pw466kbA0LEZljHwKeMgd9WrAEgEGxjPDD2+TNbbb37rTyhEfrCXfuKXnA==", + "dev": true + }, + "node_modules/url-parse-lax": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/url-parse-lax/-/url-parse-lax-3.0.0.tgz", + "integrity": "sha1-FrXK/Afb42dsGxmZF3gj1lA6yww=", + "dev": true, + "dependencies": { + "prepend-http": "^2.0.0" + }, + "engines": { + "node": ">=4" + } + }, + "node_modules/util-deprecate": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/util-deprecate/-/util-deprecate-1.0.2.tgz", + "integrity": "sha1-RQ1Nyfpw3nMnYvvS1KKJgUGaDM8=", + "dev": true + }, + "node_modules/uuid": { + "version": "3.4.0", + "resolved": "https://registry.npmjs.org/uuid/-/uuid-3.4.0.tgz", + "integrity": "sha512-HjSDRw6gZE5JMggctHBcjVak08+KEVhSIiDzFnT9S9aegmp85S/bReBVTb4QTFaRNptJ9kuYaNhnbNEOkbKb/A==", + "deprecated": "Please upgrade to version 7 or higher. Older versions may use Math.random() in certain circumstances, which is known to be problematic. See https://v8.dev/blog/math-random for details.", + "dev": true, + "bin": { + "uuid": "bin/uuid" + } + }, + "node_modules/vscode-textmate": { + "version": "5.2.0", + "resolved": "https://registry.npmjs.org/vscode-textmate/-/vscode-textmate-5.2.0.tgz", + "integrity": "sha512-Uw5ooOQxRASHgu6C7GVvUxisKXfSgW4oFlO+aa+PAkgmH89O3CXxEEzNRNtHSqtXFTl0nAC1uYj0GMSH27uwtQ==", + "dev": true + }, + "node_modules/wcwidth": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/wcwidth/-/wcwidth-1.0.1.tgz", + "integrity": "sha1-8LDc+RW8X/FSivrbLA4XtTLaL+g=", + "dev": true, + "dependencies": { + "defaults": "^1.0.3" + } + }, + "node_modules/which": { + "version": "2.0.2", + "resolved": "https://registry.npmjs.org/which/-/which-2.0.2.tgz", + "integrity": "sha512-BLI3Tl1TW3Pvl70l3yq3Y64i+awpwXqsGBYWkkqMtnbXgrMD+yj7rhW0kuEDxzJaYXGjEW5ogapKNMEKNMjibA==", + "dev": true, + "dependencies": { + "isexe": "^2.0.0" + }, + "bin": { + "node-which": "bin/node-which" + }, + "engines": { + "node": ">= 8" + } + }, + "node_modules/which-module": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/which-module/-/which-module-2.0.0.tgz", + "integrity": "sha1-2e8H3Od7mQK4o6j6SzHD4/fm6Ho=", + "dev": true + }, + "node_modules/wide-align": { + "version": "1.1.3", + "resolved": "https://registry.npmjs.org/wide-align/-/wide-align-1.1.3.tgz", + "integrity": "sha512-QGkOQc8XL6Bt5PwnsExKBPuMKBxnGxWWW3fU55Xt4feHozMUhdUMaBCk290qpm/wG5u/RSKzwdAC4i51YigihA==", + "dev": true, + "dependencies": { + "string-width": "^1.0.2 || 2" + } + }, + "node_modules/wide-align/node_modules/ansi-regex": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/ansi-regex/-/ansi-regex-3.0.0.tgz", + "integrity": "sha1-7QMXwyIGT3lGbAKWa922Bas32Zg=", + "dev": true, + "engines": { + "node": ">=4" + } + }, + "node_modules/wide-align/node_modules/is-fullwidth-code-point": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/is-fullwidth-code-point/-/is-fullwidth-code-point-2.0.0.tgz", + "integrity": "sha1-o7MKXE8ZkYMWeqq5O+764937ZU8=", + "dev": true, + "engines": { + "node": ">=4" + } + }, + "node_modules/wide-align/node_modules/string-width": { + "version": "2.1.1", + "resolved": "https://registry.npmjs.org/string-width/-/string-width-2.1.1.tgz", + "integrity": "sha512-nOqH59deCq9SRHlxq1Aw85Jnt4w6KvLKqWVik6oA9ZklXLNIOlqg4F2yrT1MVaTjAqvVwdfeZ7w7aCvJD7ugkw==", + "dev": true, + "dependencies": { + "is-fullwidth-code-point": "^2.0.0", + "strip-ansi": "^4.0.0" + }, + "engines": { + "node": ">=4" + } + }, + "node_modules/wide-align/node_modules/strip-ansi": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/strip-ansi/-/strip-ansi-4.0.0.tgz", + "integrity": "sha1-qEeQIusaw2iocTibY1JixQXuNo8=", + "dev": true, + "dependencies": { + "ansi-regex": "^3.0.0" + }, + "engines": { + "node": ">=4" + } + }, + "node_modules/widest-line": { + "version": "3.1.0", + "resolved": "https://registry.npmjs.org/widest-line/-/widest-line-3.1.0.tgz", + "integrity": "sha512-NsmoXalsWVDMGupxZ5R08ka9flZjjiLvHVAWYOKtiKM8ujtZWr9cRffak+uSE48+Ob8ObalXpwyeUiyDD6QFgg==", + "dev": true, + "dependencies": { + "string-width": "^4.0.0" + }, + "engines": { + "node": ">=8" + } + }, + "node_modules/windows-release": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/windows-release/-/windows-release-4.0.0.tgz", + "integrity": "sha512-OxmV4wzDKB1x7AZaZgXMVsdJ1qER1ed83ZrTYd5Bwq2HfJVg3DJS8nqlAG4sMoJ7mu8cuRmLEYyU13BKwctRAg==", + "dev": true, + "dependencies": { + "execa": "^4.0.2" + }, + "engines": { + "node": ">=10" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/windows-release/node_modules/execa": { + "version": "4.1.0", + "resolved": "https://registry.npmjs.org/execa/-/execa-4.1.0.tgz", + "integrity": "sha512-j5W0//W7f8UxAn8hXVnwG8tLwdiUy4FJLcSupCg6maBYZDpyBvTApK7KyuI4bKj8KOh1r2YH+6ucuYtJv1bTZA==", + "dev": true, + "dependencies": { + "cross-spawn": "^7.0.0", + "get-stream": "^5.0.0", + "human-signals": "^1.1.1", + "is-stream": "^2.0.0", + "merge-stream": "^2.0.0", + "npm-run-path": "^4.0.0", + "onetime": "^5.1.0", + "signal-exit": "^3.0.2", + "strip-final-newline": "^2.0.0" + }, + "engines": { + "node": ">=10" + }, + "funding": { + "url": "https://github.com/sindresorhus/execa?sponsor=1" + } + }, + "node_modules/windows-release/node_modules/get-stream": { + "version": "5.2.0", + "resolved": "https://registry.npmjs.org/get-stream/-/get-stream-5.2.0.tgz", + "integrity": "sha512-nBF+F1rAZVCu/p7rjzgA+Yb4lfYXrpl7a6VmJrU8wF9I1CKvP/QwPNZHnOlwbTkY6dvtFIzFMSyQXbLoTQPRpA==", + "dev": true, + "dependencies": { + "pump": "^3.0.0" + }, + "engines": { + "node": ">=8" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/windows-release/node_modules/human-signals": { + "version": "1.1.1", + "resolved": "https://registry.npmjs.org/human-signals/-/human-signals-1.1.1.tgz", + "integrity": "sha512-SEQu7vl8KjNL2eoGBLF3+wAjpsNfA9XMlXAYj/3EdaNfAlxKthD1xjEQfGOUhllCGGJVNY34bRr6lPINhNjyZw==", + "dev": true, + "engines": { + "node": ">=8.12.0" + } + }, + "node_modules/wordwrap": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/wordwrap/-/wordwrap-1.0.0.tgz", + "integrity": "sha1-J1hIEIkUVqQXHI0CJkQa3pDLyus=", + "dev": true + }, + "node_modules/workerpool": { + "version": "6.1.5", + "resolved": "https://registry.npmjs.org/workerpool/-/workerpool-6.1.5.tgz", + "integrity": "sha512-XdKkCK0Zqc6w3iTxLckiuJ81tiD/o5rBE/m+nXpRCB+/Sq4DqkfXZ/x0jW02DG1tGsfUGXbTJyZDP+eu67haSw==", + "dev": true + }, + "node_modules/wrap-ansi": { + "version": "7.0.0", + "resolved": "https://registry.npmjs.org/wrap-ansi/-/wrap-ansi-7.0.0.tgz", + "integrity": "sha512-YVGIj2kamLSTxw6NsZjoBxfSwsn0ycdesmc4p+Q21c5zPuZ1pl+NfxVdxPtdHvmNVOQ6XSYG4AUtyt/Fi7D16Q==", + "dev": true, + "dependencies": { + "ansi-styles": "^4.0.0", + "string-width": "^4.1.0", + "strip-ansi": "^6.0.0" + }, + "engines": { + "node": ">=10" + }, + "funding": { + "url": "https://github.com/chalk/wrap-ansi?sponsor=1" + } + }, + "node_modules/wrappy": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/wrappy/-/wrappy-1.0.2.tgz", + "integrity": "sha1-tSQ9jz7BqjXxNkYFvA0QNuMKtp8=", + "dev": true + }, + "node_modules/write-file-atomic": { + "version": "3.0.3", + "resolved": "https://registry.npmjs.org/write-file-atomic/-/write-file-atomic-3.0.3.tgz", + "integrity": "sha512-AvHcyZ5JnSfq3ioSyjrBkH9yW4m7Ayk8/9My/DD9onKeu/94fwrMocemO2QAJFAlnnDN+ZDS+ZjAR5ua1/PV/Q==", + "dev": true, + "dependencies": { + "imurmurhash": "^0.1.4", + "is-typedarray": "^1.0.0", + "signal-exit": "^3.0.2", + "typedarray-to-buffer": "^3.1.5" + } + }, + "node_modules/xdg-basedir": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/xdg-basedir/-/xdg-basedir-4.0.0.tgz", + "integrity": "sha512-PSNhEJDejZYV7h50BohL09Er9VaIefr2LMAf3OEmpCkjOi34eYyQYAXUTjEQtZJTKcF0E2UKTh+osDLsgNim9Q==", + "dev": true, + "engines": { + "node": ">=8" + } + }, + "node_modules/y18n": { + "version": "5.0.8", + "resolved": "https://registry.npmjs.org/y18n/-/y18n-5.0.8.tgz", + "integrity": "sha512-0pfFzegeDWJHJIAmTLRP2DwHjdF5s7jo9tuztdQxAhINCdvS+3nGINqPd00AphqJR/0LhANUS6/+7SCb98YOfA==", + "dev": true, + "engines": { + "node": ">=10" + } + }, + "node_modules/yallist": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/yallist/-/yallist-4.0.0.tgz", + "integrity": "sha512-3wdGidZyq5PB084XLES5TpOSRA3wjXAlIWMhum2kRcv/41Sn2emQ0dycQW4uZXLejwKvg6EsvbdlVL+FYEct7A==" + }, + "node_modules/yaml": { + "version": "1.10.2", + "resolved": "https://registry.npmjs.org/yaml/-/yaml-1.10.2.tgz", + "integrity": "sha512-r3vXyErRCYJ7wg28yvBY5VSoAF8ZvlcW9/BwUzEtUsjvX/DKs24dIkuwjtuprwJJHsbyUbLApepYTR1BN4uHrg==", + "dev": true, + "engines": { + "node": ">= 6" + } + }, + "node_modules/yargs": { + "version": "16.2.0", + "resolved": "https://registry.npmjs.org/yargs/-/yargs-16.2.0.tgz", + "integrity": "sha512-D1mvvtDG0L5ft/jGWkLpG1+m0eQxOfaBvTNELraWj22wSVUMWxZUvYgJYcKh6jGGIkJFhH4IZPQhR4TKpc8mBw==", + "dev": true, + "dependencies": { + "cliui": "^7.0.2", + "escalade": "^3.1.1", + "get-caller-file": "^2.0.5", + "require-directory": "^2.1.1", + "string-width": "^4.2.0", + "y18n": "^5.0.5", + "yargs-parser": "^20.2.2" + }, + "engines": { + "node": ">=10" + } + }, + "node_modules/yargs-parser": { + "version": "20.2.4", + "resolved": "https://registry.npmjs.org/yargs-parser/-/yargs-parser-20.2.4.tgz", + "integrity": "sha512-WOkpgNhPTlE73h4VFAFsOnomJVaovO8VqLDzy5saChRBFQFBoMYirowyW+Q9HB4HFF4Z7VZTiG3iSzJJA29yRA==", + "dev": true, + "engines": { + "node": ">=10" + } + }, + "node_modules/yargs-unparser": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/yargs-unparser/-/yargs-unparser-2.0.0.tgz", + "integrity": "sha512-7pRTIA9Qc1caZ0bZ6RYRGbHJthJWuakf+WmHK0rVeLkNrrGhfoabBNdue6kdINI6r4if7ocq9aD/n7xwKOdzOA==", + "dev": true, + "dependencies": { + "camelcase": "^6.0.0", + "decamelize": "^4.0.0", + "flat": "^5.0.2", + "is-plain-obj": "^2.1.0" + }, + "engines": { + "node": ">=10" + } + }, + "node_modules/yargs-unparser/node_modules/camelcase": { + "version": "6.2.0", + "resolved": "https://registry.npmjs.org/camelcase/-/camelcase-6.2.0.tgz", + "integrity": "sha512-c7wVvbw3f37nuobQNtgsgG9POC9qMbNuMQmTCqZv23b6MIz0fcYpBiOlv9gEN/hdLdnZTDQhg6e9Dq5M1vKvfg==", + "dev": true, + "engines": { + "node": ">=10" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/yargs-unparser/node_modules/decamelize": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/decamelize/-/decamelize-4.0.0.tgz", + "integrity": "sha512-9iE1PgSik9HeIIw2JO94IidnE3eBoQrFJ3w7sFuzSX4DpmZ3v5sZpUiV5Swcf6mQEF+Y0ru8Neo+p+nyh2J+hQ==", + "dev": true, + "engines": { + "node": ">=10" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/yn": { + "version": "3.1.1", + "resolved": "https://registry.npmjs.org/yn/-/yn-3.1.1.tgz", + "integrity": "sha512-Ux4ygGWsu2c7isFWe8Yu1YluJmqVhxqK2cLXNQA5AcC3QfbGNpM7fu0Y8b/z16pXLnFxZYvWhd3fhBY9DLmC6Q==", + "dev": true, + "engines": { + "node": ">=6" + } + }, + "node_modules/yocto-queue": { + "version": "0.1.0", + "resolved": "https://registry.npmjs.org/yocto-queue/-/yocto-queue-0.1.0.tgz", + "integrity": "sha512-rVksvsnNCdJ/ohGc6xgPwyN8eheCxsiLM8mxuE/t/mOVqJewPuO1miLpTHQiRgTKCLexL4MeAFVagts7HmNZ2Q==", + "dev": true, + "engines": { + "node": ">=10" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + } + }, + "dependencies": { + "@babel/code-frame": { + "version": "7.14.5", + "resolved": "https://registry.npmjs.org/@babel/code-frame/-/code-frame-7.14.5.tgz", + "integrity": "sha512-9pzDqyc6OLDaqe+zbACgFkb6fKMNG6CObKpnYXChRsvYGyEdc7CA2BaqeOM+vOtCS5ndmJicPJhKAwYRI6UfFw==", + "dev": true, + "requires": { + "@babel/highlight": "^7.14.5" + } + }, + "@babel/compat-data": { + "version": "7.15.0", + "resolved": "https://registry.npmjs.org/@babel/compat-data/-/compat-data-7.15.0.tgz", + "integrity": "sha512-0NqAC1IJE0S0+lL1SWFMxMkz1pKCNCjI4tr2Zx4LJSXxCLAdr6KyArnY+sno5m3yH9g737ygOyPABDsnXkpxiA==", + "dev": true + }, + "@babel/core": { + "version": "7.15.0", + "resolved": "https://registry.npmjs.org/@babel/core/-/core-7.15.0.tgz", + "integrity": "sha512-tXtmTminrze5HEUPn/a0JtOzzfp0nk+UEXQ/tqIJo3WDGypl/2OFQEMll/zSFU8f/lfmfLXvTaORHF3cfXIQMw==", + "dev": true, + "requires": { + "@babel/code-frame": "^7.14.5", + "@babel/generator": "^7.15.0", + "@babel/helper-compilation-targets": "^7.15.0", + "@babel/helper-module-transforms": "^7.15.0", + "@babel/helpers": "^7.14.8", + "@babel/parser": "^7.15.0", + "@babel/template": "^7.14.5", + "@babel/traverse": "^7.15.0", + "@babel/types": "^7.15.0", + "convert-source-map": "^1.7.0", + "debug": "^4.1.0", + "gensync": "^1.0.0-beta.2", + "json5": "^2.1.2", + "semver": "^6.3.0", + "source-map": "^0.5.0" + } + }, + "@babel/generator": { + "version": "7.15.0", + "resolved": "https://registry.npmjs.org/@babel/generator/-/generator-7.15.0.tgz", + "integrity": "sha512-eKl4XdMrbpYvuB505KTta4AV9g+wWzmVBW69tX0H2NwKVKd2YJbKgyK6M8j/rgLbmHOYJn6rUklV677nOyJrEQ==", + "dev": true, + "requires": { + "@babel/types": "^7.15.0", + "jsesc": "^2.5.1", + "source-map": "^0.5.0" + } + }, + "@babel/helper-compilation-targets": { + "version": "7.15.0", + "resolved": "https://registry.npmjs.org/@babel/helper-compilation-targets/-/helper-compilation-targets-7.15.0.tgz", + "integrity": "sha512-h+/9t0ncd4jfZ8wsdAsoIxSa61qhBYlycXiHWqJaQBCXAhDCMbPRSMTGnZIkkmt1u4ag+UQmuqcILwqKzZ4N2A==", + "dev": true, + "requires": { + "@babel/compat-data": "^7.15.0", + "@babel/helper-validator-option": "^7.14.5", + "browserslist": "^4.16.6", + "semver": "^6.3.0" + } + }, + "@babel/helper-function-name": { + "version": "7.14.5", + "resolved": "https://registry.npmjs.org/@babel/helper-function-name/-/helper-function-name-7.14.5.tgz", + "integrity": "sha512-Gjna0AsXWfFvrAuX+VKcN/aNNWonizBj39yGwUzVDVTlMYJMK2Wp6xdpy72mfArFq5uK+NOuexfzZlzI1z9+AQ==", + "dev": true, + "requires": { + "@babel/helper-get-function-arity": "^7.14.5", + "@babel/template": "^7.14.5", + "@babel/types": "^7.14.5" + } + }, + "@babel/helper-get-function-arity": { + "version": "7.14.5", + "resolved": "https://registry.npmjs.org/@babel/helper-get-function-arity/-/helper-get-function-arity-7.14.5.tgz", + "integrity": "sha512-I1Db4Shst5lewOM4V+ZKJzQ0JGGaZ6VY1jYvMghRjqs6DWgxLCIyFt30GlnKkfUeFLpJt2vzbMVEXVSXlIFYUg==", + "dev": true, + "requires": { + "@babel/types": "^7.14.5" + } + }, + "@babel/helper-hoist-variables": { + "version": "7.14.5", + "resolved": "https://registry.npmjs.org/@babel/helper-hoist-variables/-/helper-hoist-variables-7.14.5.tgz", + "integrity": "sha512-R1PXiz31Uc0Vxy4OEOm07x0oSjKAdPPCh3tPivn/Eo8cvz6gveAeuyUUPB21Hoiif0uoPQSSdhIPS3352nvdyQ==", + "dev": true, + "requires": { + "@babel/types": "^7.14.5" + } + }, + "@babel/helper-member-expression-to-functions": { + "version": "7.15.0", + "resolved": "https://registry.npmjs.org/@babel/helper-member-expression-to-functions/-/helper-member-expression-to-functions-7.15.0.tgz", + "integrity": "sha512-Jq8H8U2kYiafuj2xMTPQwkTBnEEdGKpT35lJEQsRRjnG0LW3neucsaMWLgKcwu3OHKNeYugfw+Z20BXBSEs2Lg==", + "dev": true, + "requires": { + "@babel/types": "^7.15.0" + } + }, + "@babel/helper-module-imports": { + "version": "7.14.5", + "resolved": "https://registry.npmjs.org/@babel/helper-module-imports/-/helper-module-imports-7.14.5.tgz", + "integrity": "sha512-SwrNHu5QWS84XlHwGYPDtCxcA0hrSlL2yhWYLgeOc0w7ccOl2qv4s/nARI0aYZW+bSwAL5CukeXA47B/1NKcnQ==", + "dev": true, + "requires": { + "@babel/types": "^7.14.5" + } + }, + "@babel/helper-module-transforms": { + "version": "7.15.0", + "resolved": "https://registry.npmjs.org/@babel/helper-module-transforms/-/helper-module-transforms-7.15.0.tgz", + "integrity": "sha512-RkGiW5Rer7fpXv9m1B3iHIFDZdItnO2/BLfWVW/9q7+KqQSDY5kUfQEbzdXM1MVhJGcugKV7kRrNVzNxmk7NBg==", + "dev": true, + "requires": { + "@babel/helper-module-imports": "^7.14.5", + "@babel/helper-replace-supers": "^7.15.0", + "@babel/helper-simple-access": "^7.14.8", + "@babel/helper-split-export-declaration": "^7.14.5", + "@babel/helper-validator-identifier": "^7.14.9", + "@babel/template": "^7.14.5", + "@babel/traverse": "^7.15.0", + "@babel/types": "^7.15.0" + } + }, + "@babel/helper-optimise-call-expression": { + "version": "7.14.5", + "resolved": "https://registry.npmjs.org/@babel/helper-optimise-call-expression/-/helper-optimise-call-expression-7.14.5.tgz", + "integrity": "sha512-IqiLIrODUOdnPU9/F8ib1Fx2ohlgDhxnIDU7OEVi+kAbEZcyiF7BLU8W6PfvPi9LzztjS7kcbzbmL7oG8kD6VA==", + "dev": true, + "requires": { + "@babel/types": "^7.14.5" + } + }, + "@babel/helper-replace-supers": { + "version": "7.15.0", + "resolved": "https://registry.npmjs.org/@babel/helper-replace-supers/-/helper-replace-supers-7.15.0.tgz", + "integrity": "sha512-6O+eWrhx+HEra/uJnifCwhwMd6Bp5+ZfZeJwbqUTuqkhIT6YcRhiZCOOFChRypOIe0cV46kFrRBlm+t5vHCEaA==", + "dev": true, + "requires": { + "@babel/helper-member-expression-to-functions": "^7.15.0", + "@babel/helper-optimise-call-expression": "^7.14.5", + "@babel/traverse": "^7.15.0", + "@babel/types": "^7.15.0" + } + }, + "@babel/helper-simple-access": { + "version": "7.14.8", + "resolved": "https://registry.npmjs.org/@babel/helper-simple-access/-/helper-simple-access-7.14.8.tgz", + "integrity": "sha512-TrFN4RHh9gnWEU+s7JloIho2T76GPwRHhdzOWLqTrMnlas8T9O7ec+oEDNsRXndOmru9ymH9DFrEOxpzPoSbdg==", + "dev": true, + "requires": { + "@babel/types": "^7.14.8" + } + }, + "@babel/helper-split-export-declaration": { + "version": "7.14.5", + "resolved": "https://registry.npmjs.org/@babel/helper-split-export-declaration/-/helper-split-export-declaration-7.14.5.tgz", + "integrity": "sha512-hprxVPu6e5Kdp2puZUmvOGjaLv9TCe58E/Fl6hRq4YiVQxIcNvuq6uTM2r1mT/oPskuS9CgR+I94sqAYv0NGKA==", + "dev": true, + "requires": { + "@babel/types": "^7.14.5" + } + }, + "@babel/helper-validator-identifier": { + "version": "7.14.9", + "resolved": "https://registry.npmjs.org/@babel/helper-validator-identifier/-/helper-validator-identifier-7.14.9.tgz", + "integrity": "sha512-pQYxPY0UP6IHISRitNe8bsijHex4TWZXi2HwKVsjPiltzlhse2znVcm9Ace510VT1kxIHjGJCZZQBX2gJDbo0g==", + "dev": true + }, + "@babel/helper-validator-option": { + "version": "7.14.5", + "resolved": "https://registry.npmjs.org/@babel/helper-validator-option/-/helper-validator-option-7.14.5.tgz", + "integrity": "sha512-OX8D5eeX4XwcroVW45NMvoYaIuFI+GQpA2a8Gi+X/U/cDUIRsV37qQfF905F0htTRCREQIB4KqPeaveRJUl3Ow==", + "dev": true + }, + "@babel/helpers": { + "version": "7.15.3", + "resolved": "https://registry.npmjs.org/@babel/helpers/-/helpers-7.15.3.tgz", + "integrity": "sha512-HwJiz52XaS96lX+28Tnbu31VeFSQJGOeKHJeaEPQlTl7PnlhFElWPj8tUXtqFIzeN86XxXoBr+WFAyK2PPVz6g==", + "dev": true, + "requires": { + "@babel/template": "^7.14.5", + "@babel/traverse": "^7.15.0", + "@babel/types": "^7.15.0" + } + }, + "@babel/highlight": { + "version": "7.14.5", + "resolved": "https://registry.npmjs.org/@babel/highlight/-/highlight-7.14.5.tgz", + "integrity": "sha512-qf9u2WFWVV0MppaL877j2dBtQIDgmidgjGk5VIMw3OadXvYaXn66U1BFlH2t4+t3i+8PhedppRv+i40ABzd+gg==", + "dev": true, + "requires": { + "@babel/helper-validator-identifier": "^7.14.5", + "chalk": "^2.0.0", + "js-tokens": "^4.0.0" + }, + "dependencies": { + "ansi-styles": { + "version": "3.2.1", + "resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-3.2.1.tgz", + "integrity": "sha512-VT0ZI6kZRdTh8YyJw3SMbYm/u+NqfsAxEpWO0Pf9sq8/e94WxxOpPKx9FR1FlyCtOVDNOQ+8ntlqFxiRc+r5qA==", + "dev": true, + "requires": { + "color-convert": "^1.9.0" + } + }, + "chalk": { + "version": "2.4.2", + "resolved": "https://registry.npmjs.org/chalk/-/chalk-2.4.2.tgz", + "integrity": "sha512-Mti+f9lpJNcwF4tWV8/OrTTtF1gZi+f8FqlyAdouralcFWFQWF2+NgCHShjkCb+IFBLq9buZwE1xckQU4peSuQ==", + "dev": true, + "requires": { + "ansi-styles": "^3.2.1", + "escape-string-regexp": "^1.0.5", + "supports-color": "^5.3.0" + } + }, + "color-convert": { + "version": "1.9.3", + "resolved": "https://registry.npmjs.org/color-convert/-/color-convert-1.9.3.tgz", + "integrity": "sha512-QfAUtd+vFdAtFQcC8CCyYt1fYWxSqAiK2cSD6zDB8N3cpsEBAvRxp9zOGg6G/SHHJYAT88/az/IuDGALsNVbGg==", + "dev": true, + "requires": { + "color-name": "1.1.3" + } + }, + "color-name": { + "version": "1.1.3", + "resolved": "https://registry.npmjs.org/color-name/-/color-name-1.1.3.tgz", + "integrity": "sha1-p9BVi9icQveV3UIyj3QIMcpTvCU=", + "dev": true + }, + "escape-string-regexp": { + "version": "1.0.5", + "resolved": "https://registry.npmjs.org/escape-string-regexp/-/escape-string-regexp-1.0.5.tgz", + "integrity": "sha1-G2HAViGQqN/2rjuyzwIAyhMLhtQ=", + "dev": true + }, + "has-flag": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/has-flag/-/has-flag-3.0.0.tgz", + "integrity": "sha1-tdRU3CGZriJWmfNGfloH87lVuv0=", + "dev": true + }, + "supports-color": { + "version": "5.5.0", + "resolved": "https://registry.npmjs.org/supports-color/-/supports-color-5.5.0.tgz", + "integrity": "sha512-QjVjwdXIt408MIiAqCX4oUKsgU2EqAGzs2Ppkm4aQYbjm+ZEWEcW4SfFNTr4uMNZma0ey4f5lgLrkB0aX0QMow==", + "dev": true, + "requires": { + "has-flag": "^3.0.0" + } + } + } + }, + "@babel/parser": { + "version": "7.15.3", + "resolved": "https://registry.npmjs.org/@babel/parser/-/parser-7.15.3.tgz", + "integrity": "sha512-O0L6v/HvqbdJawj0iBEfVQMc3/6WP+AeOsovsIgBFyJaG+W2w7eqvZB7puddATmWuARlm1SX7DwxJ/JJUnDpEA==", + "dev": true + }, + "@babel/template": { + "version": "7.14.5", + "resolved": "https://registry.npmjs.org/@babel/template/-/template-7.14.5.tgz", + "integrity": "sha512-6Z3Po85sfxRGachLULUhOmvAaOo7xCvqGQtxINai2mEGPFm6pQ4z5QInFnUrRpfoSV60BnjyF5F3c+15fxFV1g==", + "dev": true, + "requires": { + "@babel/code-frame": "^7.14.5", + "@babel/parser": "^7.14.5", + "@babel/types": "^7.14.5" + } + }, + "@babel/traverse": { + "version": "7.15.0", + "resolved": "https://registry.npmjs.org/@babel/traverse/-/traverse-7.15.0.tgz", + "integrity": "sha512-392d8BN0C9eVxVWd8H6x9WfipgVH5IaIoLp23334Sc1vbKKWINnvwRpb4us0xtPaCumlwbTtIYNA0Dv/32sVFw==", + "dev": true, + "requires": { + "@babel/code-frame": "^7.14.5", + "@babel/generator": "^7.15.0", + "@babel/helper-function-name": "^7.14.5", + "@babel/helper-hoist-variables": "^7.14.5", + "@babel/helper-split-export-declaration": "^7.14.5", + "@babel/parser": "^7.15.0", + "@babel/types": "^7.15.0", + "debug": "^4.1.0", + "globals": "^11.1.0" + } + }, + "@babel/types": { + "version": "7.15.0", + "resolved": "https://registry.npmjs.org/@babel/types/-/types-7.15.0.tgz", + "integrity": "sha512-OBvfqnllOIdX4ojTHpwZbpvz4j3EWyjkZEdmjH0/cgsd6QOdSgU8rLSk6ard/pcW7rlmjdVSX/AWOaORR1uNOQ==", + "dev": true, + "requires": { + "@babel/helper-validator-identifier": "^7.14.9", + "to-fast-properties": "^2.0.0" + } + }, + "@cspotcode/source-map-consumer": { + "version": "0.8.0", + "resolved": "https://registry.npmjs.org/@cspotcode/source-map-consumer/-/source-map-consumer-0.8.0.tgz", + "integrity": "sha512-41qniHzTU8yAGbCp04ohlmSrZf8bkf/iJsl3V0dRGsQN/5GFfx+LbCSsCpp2gqrqjTVg/K6O8ycoV35JIwAzAg==", + "dev": true + }, + "@cspotcode/source-map-support": { + "version": "0.6.1", + "resolved": "https://registry.npmjs.org/@cspotcode/source-map-support/-/source-map-support-0.6.1.tgz", + "integrity": "sha512-DX3Z+T5dt1ockmPdobJS/FAsQPW4V4SrWEhD2iYQT2Cb2tQsiMnYxrcUH9By/Z3B+v0S5LMBkQtV/XOBbpLEOg==", + "dev": true, + "requires": { + "@cspotcode/source-map-consumer": "0.8.0" + } + }, + "@iarna/toml": { + "version": "2.2.5", + "resolved": "https://registry.npmjs.org/@iarna/toml/-/toml-2.2.5.tgz", + "integrity": "sha512-trnsAYxU3xnS1gPHPyU961coFyLkh4gAD/0zQ5mymY4yOZ+CYvsPqUbOFSw0aDM4y0tV7tiFxL/1XfXPNC6IPg==", + "dev": true + }, + "@istanbuljs/load-nyc-config": { + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/@istanbuljs/load-nyc-config/-/load-nyc-config-1.1.0.tgz", + "integrity": "sha512-VjeHSlIzpv/NyD3N0YuHfXOPDIixcA1q2ZV98wsMqcYlPmv2n3Yb2lYP9XMElnaFVXg5A7YLTeLu6V84uQDjmQ==", + "dev": true, + "requires": { + "camelcase": "^5.3.1", + "find-up": "^4.1.0", + "get-package-type": "^0.1.0", + "js-yaml": "^3.13.1", + "resolve-from": "^5.0.0" + }, + "dependencies": { + "argparse": { + "version": "1.0.10", + "resolved": "https://registry.npmjs.org/argparse/-/argparse-1.0.10.tgz", + "integrity": "sha512-o5Roy6tNG4SL/FOkCAN6RzjiakZS25RLYFrcMttJqbdd8BWrnA+fGz57iN5Pb06pvBGvl5gQ0B48dJlslXvoTg==", + "dev": true, + "requires": { + "sprintf-js": "~1.0.2" + } + }, + "find-up": { + "version": "4.1.0", + "resolved": "https://registry.npmjs.org/find-up/-/find-up-4.1.0.tgz", + "integrity": "sha512-PpOwAdQ/YlXQ2vj8a3h8IipDuYRi3wceVQQGYWxNINccq40Anw7BlsEXCMbt1Zt+OLA6Fq9suIpIWD0OsnISlw==", + "dev": true, + "requires": { + "locate-path": "^5.0.0", + "path-exists": "^4.0.0" + } + }, + "js-yaml": { + "version": "3.14.1", + "resolved": "https://registry.npmjs.org/js-yaml/-/js-yaml-3.14.1.tgz", + "integrity": "sha512-okMH7OXXJ7YrN9Ok3/SXrnu4iX9yOk+25nqX4imS2npuvTYDmo/QEZoqwZkYaIDk3jVvBOTOIEgEhaLOynBS9g==", + "dev": true, + "requires": { + "argparse": "^1.0.7", + "esprima": "^4.0.0" + } + }, + "locate-path": { + "version": "5.0.0", + "resolved": "https://registry.npmjs.org/locate-path/-/locate-path-5.0.0.tgz", + "integrity": "sha512-t7hw9pI+WvuwNJXwk5zVHpyhIqzg2qTlklJOf0mVxGSbe3Fp2VieZcduNYjaLDoy6p9uGpQEGWG87WpMKlNq8g==", + "dev": true, + "requires": { + "p-locate": "^4.1.0" + } + }, + "p-limit": { + "version": "2.3.0", + "resolved": "https://registry.npmjs.org/p-limit/-/p-limit-2.3.0.tgz", + "integrity": "sha512-//88mFWSJx8lxCzwdAABTJL2MyWB12+eIY7MDL2SqLmAkeKU9qxRvWuSyTjm3FUmpBEMuFfckAIqEaVGUDxb6w==", + "dev": true, + "requires": { + "p-try": "^2.0.0" + } + }, + "p-locate": { + "version": "4.1.0", + "resolved": "https://registry.npmjs.org/p-locate/-/p-locate-4.1.0.tgz", + "integrity": "sha512-R79ZZ/0wAxKGu3oYMlz8jy/kbhsNrS7SKZ7PxEHBgJ5+F2mtFW2fK2cOtBh1cHYkQsbzFV7I+EoRKe6Yt0oK7A==", + "dev": true, + "requires": { + "p-limit": "^2.2.0" + } + } + } + }, + "@istanbuljs/nyc-config-typescript": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/@istanbuljs/nyc-config-typescript/-/nyc-config-typescript-1.0.1.tgz", + "integrity": "sha512-/gz6LgVpky205LuoOfwEZmnUtaSmdk0QIMcNFj9OvxhiMhPpKftMgZmGN7jNj7jR+lr8IB1Yks3QSSSNSxfoaQ==", + "dev": true, + "requires": { + "@istanbuljs/schema": "^0.1.2" + } + }, + "@istanbuljs/schema": { + "version": "0.1.3", + "resolved": "https://registry.npmjs.org/@istanbuljs/schema/-/schema-0.1.3.tgz", + "integrity": "sha512-ZXRY4jNvVgSVQ8DL3LTcakaAtXwTVUxE81hslsyD2AtoXW/wVob10HkOJ1X/pAlcI7D+2YoZKg5do8G/w6RYgA==", + "dev": true + }, + "@nodelib/fs.scandir": { + "version": "2.1.5", + "resolved": "https://registry.npmjs.org/@nodelib/fs.scandir/-/fs.scandir-2.1.5.tgz", + "integrity": "sha512-vq24Bq3ym5HEQm2NKCr3yXDwjc7vTsEThRDnkp2DK9p1uqLR+DHurm/NOTo0KG7HYHU7eppKZj3MyqYuMBf62g==", + "dev": true, + "requires": { + "@nodelib/fs.stat": "2.0.5", + "run-parallel": "^1.1.9" + } + }, + "@nodelib/fs.stat": { + "version": "2.0.5", + "resolved": "https://registry.npmjs.org/@nodelib/fs.stat/-/fs.stat-2.0.5.tgz", + "integrity": "sha512-RkhPPp2zrqDAQA/2jNhnztcPAlv64XdhIp7a7454A5ovI7Bukxgt7MX7udwAu3zg1DcpPU0rz3VV1SeaqvY4+A==", + "dev": true + }, + "@nodelib/fs.walk": { + "version": "1.2.8", + "resolved": "https://registry.npmjs.org/@nodelib/fs.walk/-/fs.walk-1.2.8.tgz", + "integrity": "sha512-oGB+UxlgWcgQkgwo8GcEGwemoTFt3FIO9ababBmaGwXIoBKZ+GTy0pP185beGg7Llih/NSHSV2XAs1lnznocSg==", + "dev": true, + "requires": { + "@nodelib/fs.scandir": "2.1.5", + "fastq": "^1.6.0" + } + }, + "@octokit/auth-token": { + "version": "2.4.5", + "resolved": "https://registry.npmjs.org/@octokit/auth-token/-/auth-token-2.4.5.tgz", + "integrity": "sha512-BpGYsPgJt05M7/L/5FoE1PiAbdxXFZkX/3kDYcsvd1v6UhlnE5e96dTDr0ezX/EFwciQxf3cNV0loipsURU+WA==", + "dev": true, + "requires": { + "@octokit/types": "^6.0.3" + } + }, + "@octokit/core": { + "version": "3.5.1", + "resolved": "https://registry.npmjs.org/@octokit/core/-/core-3.5.1.tgz", + "integrity": "sha512-omncwpLVxMP+GLpLPgeGJBF6IWJFjXDS5flY5VbppePYX9XehevbDykRH9PdCdvqt9TS5AOTiDide7h0qrkHjw==", + "dev": true, + "requires": { + "@octokit/auth-token": "^2.4.4", + "@octokit/graphql": "^4.5.8", + "@octokit/request": "^5.6.0", + "@octokit/request-error": "^2.0.5", + "@octokit/types": "^6.0.3", + "before-after-hook": "^2.2.0", + "universal-user-agent": "^6.0.0" + } + }, + "@octokit/endpoint": { + "version": "6.0.12", + "resolved": "https://registry.npmjs.org/@octokit/endpoint/-/endpoint-6.0.12.tgz", + "integrity": "sha512-lF3puPwkQWGfkMClXb4k/eUT/nZKQfxinRWJrdZaJO85Dqwo/G0yOC434Jr2ojwafWJMYqFGFa5ms4jJUgujdA==", + "dev": true, + "requires": { + "@octokit/types": "^6.0.3", + "is-plain-object": "^5.0.0", + "universal-user-agent": "^6.0.0" + } + }, + "@octokit/graphql": { + "version": "4.7.0", + "resolved": "https://registry.npmjs.org/@octokit/graphql/-/graphql-4.7.0.tgz", + "integrity": "sha512-diY0qMPyQjfu4rDu3kDhJ9qIZadIm4IISO3RJSv9ajYUWJUCO0AykbgzLcg1xclxtXgzY583u3gAv66M6zz5SA==", + "dev": true, + "requires": { + "@octokit/request": "^5.6.0", + "@octokit/types": "^6.0.3", + "universal-user-agent": "^6.0.0" + } + }, + "@octokit/openapi-types": { + "version": "9.7.0", + "resolved": "https://registry.npmjs.org/@octokit/openapi-types/-/openapi-types-9.7.0.tgz", + "integrity": "sha512-TUJ16DJU8mekne6+KVcMV5g6g/rJlrnIKn7aALG9QrNpnEipFc1xjoarh0PKaAWf2Hf+HwthRKYt+9mCm5RsRg==", + "dev": true + }, + "@octokit/plugin-paginate-rest": { + "version": "2.15.1", + "resolved": "https://registry.npmjs.org/@octokit/plugin-paginate-rest/-/plugin-paginate-rest-2.15.1.tgz", + "integrity": "sha512-47r52KkhQDkmvUKZqXzA1lKvcyJEfYh3TKAIe5+EzMeyDM3d+/s5v11i2gTk8/n6No6DPi3k5Ind6wtDbo/AEg==", + "dev": true, + "requires": { + "@octokit/types": "^6.24.0" + } + }, + "@octokit/plugin-request-log": { + "version": "1.0.4", + "resolved": "https://registry.npmjs.org/@octokit/plugin-request-log/-/plugin-request-log-1.0.4.tgz", + "integrity": "sha512-mLUsMkgP7K/cnFEw07kWqXGF5LKrOkD+lhCrKvPHXWDywAwuDUeDwWBpc69XK3pNX0uKiVt8g5z96PJ6z9xCFA==", + "dev": true, + "requires": {} + }, + "@octokit/plugin-rest-endpoint-methods": { + "version": "5.7.0", + "resolved": "https://registry.npmjs.org/@octokit/plugin-rest-endpoint-methods/-/plugin-rest-endpoint-methods-5.7.0.tgz", + "integrity": "sha512-G7sgccWRYQMwcHJXkDY/sDxbXeKiZkFQqUtzBCwmrzCNj2GQf3VygQ4T/BFL2crLVpIbenkE/c0ErhYOte2MPw==", + "dev": true, + "requires": { + "@octokit/types": "^6.24.0", + "deprecation": "^2.3.1" + } + }, + "@octokit/request": { + "version": "5.6.1", + "resolved": "https://registry.npmjs.org/@octokit/request/-/request-5.6.1.tgz", + "integrity": "sha512-Ls2cfs1OfXaOKzkcxnqw5MR6drMA/zWX/LIS/p8Yjdz7QKTPQLMsB3R+OvoxE6XnXeXEE2X7xe4G4l4X0gRiKQ==", + "dev": true, + "requires": { + "@octokit/endpoint": "^6.0.1", + "@octokit/request-error": "^2.1.0", + "@octokit/types": "^6.16.1", + "is-plain-object": "^5.0.0", + "node-fetch": "^2.6.1", + "universal-user-agent": "^6.0.0" + } + }, + "@octokit/request-error": { + "version": "2.1.0", + "resolved": "https://registry.npmjs.org/@octokit/request-error/-/request-error-2.1.0.tgz", + "integrity": "sha512-1VIvgXxs9WHSjicsRwq8PlR2LR2x6DwsJAaFgzdi0JfJoGSO8mYI/cHJQ+9FbN21aa+DrgNLnwObmyeSC8Rmpg==", + "dev": true, + "requires": { + "@octokit/types": "^6.0.3", + "deprecation": "^2.0.0", + "once": "^1.4.0" + } + }, + "@octokit/rest": { + "version": "18.9.0", + "resolved": "https://registry.npmjs.org/@octokit/rest/-/rest-18.9.0.tgz", + "integrity": "sha512-VrmrE8gjpuOoDAGjrQq2j9ZhOE6LxaqxaQg0yMrrEnnQZy2ZcAnr5qbVfKsMF0up/48PRV/VFS/2GSMhA7nTdA==", + "dev": true, + "requires": { + "@octokit/core": "^3.5.0", + "@octokit/plugin-paginate-rest": "^2.6.2", + "@octokit/plugin-request-log": "^1.0.2", + "@octokit/plugin-rest-endpoint-methods": "5.7.0" + } + }, + "@octokit/types": { + "version": "6.25.0", + "resolved": "https://registry.npmjs.org/@octokit/types/-/types-6.25.0.tgz", + "integrity": "sha512-bNvyQKfngvAd/08COlYIN54nRgxskmejgywodizQNyiKoXmWRAjKup2/LYwm+T9V0gsKH6tuld1gM0PzmOiB4Q==", + "dev": true, + "requires": { + "@octokit/openapi-types": "^9.5.0" + } + }, + "@sindresorhus/is": { + "version": "4.0.1", + "resolved": "https://registry.npmjs.org/@sindresorhus/is/-/is-4.0.1.tgz", + "integrity": "sha512-Qm9hBEBu18wt1PO2flE7LPb30BHMQt1eQgbV76YntdNk73XZGpn3izvGTYxbGgzXKgbCjiia0uxTd3aTNQrY/g==", + "dev": true + }, + "@sinonjs/commons": { + "version": "1.8.3", + "resolved": "https://registry.npmjs.org/@sinonjs/commons/-/commons-1.8.3.tgz", + "integrity": "sha512-xkNcLAn/wZaX14RPlwizcKicDk9G3F8m2nU3L7Ukm5zBgTwiT0wsoFAHx9Jq56fJA1z/7uKGtCRu16sOUCLIHQ==", + "dev": true, + "requires": { + "type-detect": "4.0.8" + } + }, + "@sinonjs/fake-timers": { + "version": "7.1.2", + "resolved": "https://registry.npmjs.org/@sinonjs/fake-timers/-/fake-timers-7.1.2.tgz", + "integrity": "sha512-iQADsW4LBMISqZ6Ci1dupJL9pprqwcVFTcOsEmQOEhW+KLCVn/Y4Jrvg2k19fIHCp+iFprriYPTdRcQR8NbUPg==", + "dev": true, + "requires": { + "@sinonjs/commons": "^1.7.0" + } + }, + "@sinonjs/samsam": { + "version": "6.0.2", + "resolved": "https://registry.npmjs.org/@sinonjs/samsam/-/samsam-6.0.2.tgz", + "integrity": "sha512-jxPRPp9n93ci7b8hMfJOFDPRLFYadN6FSpeROFTR4UNF4i5b+EK6m4QXPO46BDhFgRy1JuS87zAnFOzCUwMJcQ==", + "dev": true, + "requires": { + "@sinonjs/commons": "^1.6.0", + "lodash.get": "^4.4.2", + "type-detect": "^4.0.8" + } + }, + "@sinonjs/text-encoding": { + "version": "0.7.1", + "resolved": "https://registry.npmjs.org/@sinonjs/text-encoding/-/text-encoding-0.7.1.tgz", + "integrity": "sha512-+iTbntw2IZPb/anVDbypzfQa+ay64MW0Zo8aJ8gZPWMMK6/OubMVb6lUPMagqjOPnmtauXnFCACVl3O7ogjeqQ==", + "dev": true + }, + "@szmarczak/http-timer": { + "version": "4.0.6", + "resolved": "https://registry.npmjs.org/@szmarczak/http-timer/-/http-timer-4.0.6.tgz", + "integrity": "sha512-4BAffykYOgO+5nzBWYwE3W90sBgLJoUPRWWcL8wlyiM8IB8ipJz3UMJ9KXQd1RKQXpKp8Tutn80HZtWsu2u76w==", + "dev": true, + "requires": { + "defer-to-connect": "^2.0.0" + } + }, + "@tsconfig/node10": { + "version": "1.0.8", + "resolved": "https://registry.npmjs.org/@tsconfig/node10/-/node10-1.0.8.tgz", + "integrity": "sha512-6XFfSQmMgq0CFLY1MslA/CPUfhIL919M1rMsa5lP2P097N2Wd1sSX0tx1u4olM16fLNhtHZpRhedZJphNJqmZg==", + "dev": true + }, + "@tsconfig/node12": { + "version": "1.0.9", + "resolved": "https://registry.npmjs.org/@tsconfig/node12/-/node12-1.0.9.tgz", + "integrity": "sha512-/yBMcem+fbvhSREH+s14YJi18sp7J9jpuhYByADT2rypfajMZZN4WQ6zBGgBKp53NKmqI36wFYDb3yaMPurITw==", + "dev": true + }, + "@tsconfig/node14": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/@tsconfig/node14/-/node14-1.0.1.tgz", + "integrity": "sha512-509r2+yARFfHHE7T6Puu2jjkoycftovhXRqW328PDXTVGKihlb1P8Z9mMZH04ebyajfRY7dedfGynlrFHJUQCg==", + "dev": true + }, + "@tsconfig/node16": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/@tsconfig/node16/-/node16-1.0.2.tgz", + "integrity": "sha512-eZxlbI8GZscaGS7kkc/trHTT5xgrjH3/1n2JDwusC9iahPKWMRvRjJSAN5mCXviuTGQ/lHnhvv8Q1YTpnfz9gA==", + "dev": true + }, + "@types/cacheable-request": { + "version": "6.0.2", + "resolved": "https://registry.npmjs.org/@types/cacheable-request/-/cacheable-request-6.0.2.tgz", + "integrity": "sha512-B3xVo+dlKM6nnKTcmm5ZtY/OL8bOAOd2Olee9M1zft65ox50OzjEHW91sDiU9j6cvW8Ejg1/Qkf4xd2kugApUA==", + "dev": true, + "requires": { + "@types/http-cache-semantics": "*", + "@types/keyv": "*", + "@types/node": "*", + "@types/responselike": "*" + } + }, + "@types/http-cache-semantics": { + "version": "4.0.1", + "resolved": "https://registry.npmjs.org/@types/http-cache-semantics/-/http-cache-semantics-4.0.1.tgz", + "integrity": "sha512-SZs7ekbP8CN0txVG2xVRH6EgKmEm31BOxA07vkFaETzZz1xh+cbt8BcI0slpymvwhx5dlFnQG2rTlPVQn+iRPQ==", + "dev": true + }, + "@types/keyv": { + "version": "3.1.2", + "resolved": "https://registry.npmjs.org/@types/keyv/-/keyv-3.1.2.tgz", + "integrity": "sha512-/FvAK2p4jQOaJ6CGDHJTqZcUtbZe820qIeTg7o0Shg7drB4JHeL+V/dhSaly7NXx6u8eSee+r7coT+yuJEvDLg==", + "dev": true, + "requires": { + "@types/node": "*" + } + }, + "@types/mocha": { + "version": "9.0.0", + "resolved": "https://registry.npmjs.org/@types/mocha/-/mocha-9.0.0.tgz", + "integrity": "sha512-scN0hAWyLVAvLR9AyW7HoFF5sJZglyBsbPuHO4fv7JRvfmPBMfp1ozWqOf/e4wwPNxezBZXRfWzMb6iFLgEVRA==", + "dev": true + }, + "@types/node": { + "version": "16.7.8", + "resolved": "https://registry.npmjs.org/@types/node/-/node-16.7.8.tgz", + "integrity": "sha512-8upnoQU0OPzbIkm+ZMM0zCeFCkw2s3mS0IWdx0+AAaWqm4fkBb0UJp8Edl7FVKRamYbpJC/aVsHpKWBIbiC7Zg==", + "dev": true + }, + "@types/parse-json": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/@types/parse-json/-/parse-json-4.0.0.tgz", + "integrity": "sha512-//oorEZjL6sbPcKUaCdIGlIUeH26mgzimjBB77G6XRgnDl/L5wOnpyBGRe/Mmf5CVW3PwEBE1NjiMZ/ssFh4wA==", + "dev": true + }, + "@types/responselike": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/@types/responselike/-/responselike-1.0.0.tgz", + "integrity": "sha512-85Y2BjiufFzaMIlvJDvTTB8Fxl2xfLo4HgmHzVBz08w4wDePCTjYw66PdrolO0kzli3yam/YCgRufyo1DdQVTA==", + "dev": true, + "requires": { + "@types/node": "*" + } + }, + "@types/sinon": { + "version": "10.0.2", + "resolved": "https://registry.npmjs.org/@types/sinon/-/sinon-10.0.2.tgz", + "integrity": "sha512-BHn8Bpkapj8Wdfxvh2jWIUoaYB/9/XhsL0oOvBfRagJtKlSl9NWPcFOz2lRukI9szwGxFtYZCTejJSqsGDbdmw==", + "dev": true, + "requires": { + "@sinonjs/fake-timers": "^7.1.0" + } + }, + "@types/which": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/@types/which/-/which-2.0.1.tgz", + "integrity": "sha512-Jjakcv8Roqtio6w1gr0D7y6twbhx6gGgFGF5BLwajPpnOIOxFkakFhCq+LmyyeAz7BX6ULrjBOxdKaCDy+4+dQ==", + "dev": true + }, + "@types/yallist": { + "version": "4.0.1", + "resolved": "https://registry.npmjs.org/@types/yallist/-/yallist-4.0.1.tgz", + "integrity": "sha512-G3FNJfaYtN8URU6wd6+uwFI62KO79j7n3XTYcwcFncP8gkfoi0b821GoVVt0oqKVnCqKYOMNKIGpakPoFhzAGA==", + "dev": true + }, + "@ungap/promise-all-settled": { + "version": "1.1.2", + "resolved": "https://registry.npmjs.org/@ungap/promise-all-settled/-/promise-all-settled-1.1.2.tgz", + "integrity": "sha512-sL/cEvJWAnClXw0wHk85/2L0G6Sj8UB0Ctc1TEMbKSsmpRosqhwj9gWgFRZSrBr2f9tiXISwNhCPmlfqUqyb9Q==", + "dev": true + }, + "acorn": { + "version": "8.4.1", + "resolved": "https://registry.npmjs.org/acorn/-/acorn-8.4.1.tgz", + "integrity": "sha512-asabaBSkEKosYKMITunzX177CXxQ4Q8BSSzMTKD+FefUhipQC70gfW5SiUDhYQ3vk8G+81HqQk7Fv9OXwwn9KA==", + "dev": true + }, + "acorn-walk": { + "version": "8.1.1", + "resolved": "https://registry.npmjs.org/acorn-walk/-/acorn-walk-8.1.1.tgz", + "integrity": "sha512-FbJdceMlPHEAWJOILDk1fXD8lnTlEIWFkqtfk+MvmL5q/qlHfN7GEHcsFZWt/Tea9jRNPWUZG4G976nqAAmU9w==", + "dev": true + }, + "aggregate-error": { + "version": "3.1.0", + "resolved": "https://registry.npmjs.org/aggregate-error/-/aggregate-error-3.1.0.tgz", + "integrity": "sha512-4I7Td01quW/RpocfNayFdFVk1qSuoh0E7JrbRJ16nH01HhKFQ88INq9Sd+nd72zqRySlr9BmDA8xlEJ6vJMrYA==", + "dev": true, + "requires": { + "clean-stack": "^2.0.0", + "indent-string": "^4.0.0" + } + }, + "ansi-align": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/ansi-align/-/ansi-align-3.0.0.tgz", + "integrity": "sha512-ZpClVKqXN3RGBmKibdfWzqCY4lnjEuoNzU5T0oEFpfd/z5qJHVarukridD4juLO2FXMiwUQxr9WqQtaYa8XRYw==", + "dev": true, + "requires": { + "string-width": "^3.0.0" + }, + "dependencies": { + "ansi-regex": { + "version": "4.1.0", + "resolved": "https://registry.npmjs.org/ansi-regex/-/ansi-regex-4.1.0.tgz", + "integrity": "sha512-1apePfXM1UOSqw0o9IiFAovVz9M5S1Dg+4TrDwfMewQ6p/rmMueb7tWZjQ1rx4Loy1ArBggoqGpfqqdI4rondg==", + "dev": true + }, + "emoji-regex": { + "version": "7.0.3", + "resolved": "https://registry.npmjs.org/emoji-regex/-/emoji-regex-7.0.3.tgz", + "integrity": "sha512-CwBLREIQ7LvYFB0WyRvwhq5N5qPhc6PMjD6bYggFlI5YyDgl+0vxq5VHbMOFqLg7hfWzmu8T5Z1QofhmTIhItA==", + "dev": true + }, + "is-fullwidth-code-point": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/is-fullwidth-code-point/-/is-fullwidth-code-point-2.0.0.tgz", + "integrity": "sha1-o7MKXE8ZkYMWeqq5O+764937ZU8=", + "dev": true + }, + "string-width": { + "version": "3.1.0", + "resolved": "https://registry.npmjs.org/string-width/-/string-width-3.1.0.tgz", + "integrity": "sha512-vafcv6KjVZKSgz06oM/H6GDBrAtz8vdhQakGjFIvNrHA6y3HCF1CInLy+QLq8dTJPQ1b+KDUqDFctkdRW44e1w==", + "dev": true, + "requires": { + "emoji-regex": "^7.0.1", + "is-fullwidth-code-point": "^2.0.0", + "strip-ansi": "^5.1.0" + } + }, + "strip-ansi": { + "version": "5.2.0", + "resolved": "https://registry.npmjs.org/strip-ansi/-/strip-ansi-5.2.0.tgz", + "integrity": "sha512-DuRs1gKbBqsMKIZlrffwlug8MHkcnpjs5VPmL1PAh+mA30U0DTotfDZ0d2UUsXpPmPmMMJ6W773MaA3J+lbiWA==", + "dev": true, + "requires": { + "ansi-regex": "^4.1.0" + } + } + } + }, + "ansi-colors": { + "version": "4.1.1", + "resolved": "https://registry.npmjs.org/ansi-colors/-/ansi-colors-4.1.1.tgz", + "integrity": "sha512-JoX0apGbHaUJBNl6yF+p6JAFYZ666/hhCGKN5t9QFjbJQKUU/g8MNbFDbvfrgKXvI1QpZplPOnwIo99lX/AAmA==", + "dev": true + }, + "ansi-escapes": { + "version": "4.3.2", + "resolved": "https://registry.npmjs.org/ansi-escapes/-/ansi-escapes-4.3.2.tgz", + "integrity": "sha512-gKXj5ALrKWQLsYG9jlTRmR/xKluxHV+Z9QEwNIgCfM1/uwPMCuzVVnh5mwTd+OuBZcwSIMbqssNWRm1lE51QaQ==", + "dev": true, + "requires": { + "type-fest": "^0.21.3" + }, + "dependencies": { + "type-fest": { + "version": "0.21.3", + "resolved": "https://registry.npmjs.org/type-fest/-/type-fest-0.21.3.tgz", + "integrity": "sha512-t0rzBq87m3fVcduHDUFhKmyyX+9eo6WQjZvf51Ea/M0Q7+T374Jp1aUiyUl0GKxp8M/OETVHSDvmkyPgvX+X2w==", + "dev": true + } + } + }, + "ansi-regex": { + "version": "5.0.0", + "resolved": "https://registry.npmjs.org/ansi-regex/-/ansi-regex-5.0.0.tgz", + "integrity": "sha512-bY6fj56OUQ0hU1KjFNDQuJFezqKdrAyFdIevADiqrWHwSlbmBNMHp5ak2f40Pm8JTFyM2mqxkG6ngkHO11f/lg==", + "dev": true + }, + "ansi-styles": { + "version": "4.3.0", + "resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-4.3.0.tgz", + "integrity": "sha512-zbB9rCJAT1rbjiVDb2hqKFHNYLxgtk8NURxZ3IZwD3F6NtxbXZQCnnSi1Lkx+IDohdPlFp222wVALIheZJQSEg==", + "dev": true, + "requires": { + "color-convert": "^2.0.1" + } + }, + "anymatch": { + "version": "3.1.2", + "resolved": "https://registry.npmjs.org/anymatch/-/anymatch-3.1.2.tgz", + "integrity": "sha512-P43ePfOAIupkguHUycrc4qJ9kz8ZiuOUijaETwX7THt0Y/GNK7v0aa8rY816xWjZ7rJdA5XdMcpVFTKMq+RvWg==", + "dev": true, + "requires": { + "normalize-path": "^3.0.0", + "picomatch": "^2.0.4" + } + }, + "append-transform": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/append-transform/-/append-transform-2.0.0.tgz", + "integrity": "sha512-7yeyCEurROLQJFv5Xj4lEGTy0borxepjFv1g22oAdqFu//SrAlDl1O1Nxx15SH1RoliUml6p8dwJW9jvZughhg==", + "dev": true, + "requires": { + "default-require-extensions": "^3.0.0" + } + }, + "archy": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/archy/-/archy-1.0.0.tgz", + "integrity": "sha1-+cjBN1fMHde8N5rHeyxipcKGjEA=", + "dev": true + }, + "arg": { + "version": "4.1.3", + "resolved": "https://registry.npmjs.org/arg/-/arg-4.1.3.tgz", + "integrity": "sha512-58S9QDqG0Xx27YwPSt9fJxivjYl432YCwfDMfZ+71RAqUrZef7LrKQZ3LHLOwCS4FLNBplP533Zx895SeOCHvA==", + "dev": true + }, + "argparse": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/argparse/-/argparse-2.0.1.tgz", + "integrity": "sha512-8+9WqebbFzpX9OR+Wa6O29asIogeRMzcGtAINdpMHHyAg10f05aSFVBbcEqGf/PXw1EjAZ+q2/bEBg3DvurK3Q==", + "dev": true + }, + "array-union": { + "version": "2.1.0", + "resolved": "https://registry.npmjs.org/array-union/-/array-union-2.1.0.tgz", + "integrity": "sha512-HGyxoOTYUyCM6stUe6EJgnd4EoewAI7zMdfqO+kGjnlZmBDz/cR5pf8r/cR4Wq60sL/p0IkcjUEEPwS3GFrIyw==", + "dev": true + }, + "async-retry": { + "version": "1.3.1", + "resolved": "https://registry.npmjs.org/async-retry/-/async-retry-1.3.1.tgz", + "integrity": "sha512-aiieFW/7h3hY0Bq5d+ktDBejxuwR78vRu9hDUdR8rNhSaQ29VzPL4AoIRG7D/c7tdenwOcKvgPM6tIxB3cB6HA==", + "dev": true, + "requires": { + "retry": "0.12.0" + } + }, + "asynckit": { + "version": "0.4.0", + "resolved": "https://registry.npmjs.org/asynckit/-/asynckit-0.4.0.tgz", + "integrity": "sha1-x57Zf380y48robyXkLzDZkdLS3k=", + "dev": true + }, + "balanced-match": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/balanced-match/-/balanced-match-1.0.2.tgz", + "integrity": "sha512-3oSeUO0TMV67hN1AmbXsK4yaqU7tjiHlbxRDZOpH0KW9+CeX4bRAaX0Anxt0tx2MrpRpWwQaPwIlISEJhYU5Pw==", + "dev": true + }, + "base64-js": { + "version": "1.5.1", + "resolved": "https://registry.npmjs.org/base64-js/-/base64-js-1.5.1.tgz", + "integrity": "sha512-AKpaYlHn8t4SVbOHCy+b5+KKgvR4vrsD8vbvrbiQJps7fKDTkjkDry6ji0rUJjC0kzbNePLwzxq8iypo41qeWA==", + "dev": true + }, + "before-after-hook": { + "version": "2.2.2", + "resolved": "https://registry.npmjs.org/before-after-hook/-/before-after-hook-2.2.2.tgz", + "integrity": "sha512-3pZEU3NT5BFUo/AD5ERPWOgQOCZITni6iavr5AUw5AUwQjMlI0kzu5btnyD39AF0gUEsDPwJT+oY1ORBJijPjQ==", + "dev": true + }, + "binary-extensions": { + "version": "2.2.0", + "resolved": "https://registry.npmjs.org/binary-extensions/-/binary-extensions-2.2.0.tgz", + "integrity": "sha512-jDctJ/IVQbZoJykoeHbhXpOlNBqGNcwXJKJog42E5HDPUwQTSdjCHdihjj0DlnheQ7blbT6dHOafNAiS8ooQKA==", + "dev": true + }, + "bl": { + "version": "4.1.0", + "resolved": "https://registry.npmjs.org/bl/-/bl-4.1.0.tgz", + "integrity": "sha512-1W07cM9gS6DcLperZfFSj+bWLtaPGSOHWhPiGzXmvVJbRLdG82sH/Kn8EtW1VqWVA54AKf2h5k5BbnIbwF3h6w==", + "dev": true, + "requires": { + "buffer": "^5.5.0", + "inherits": "^2.0.4", + "readable-stream": "^3.4.0" + } + }, + "boxen": { + "version": "5.0.1", + "resolved": "https://registry.npmjs.org/boxen/-/boxen-5.0.1.tgz", + "integrity": "sha512-49VBlw+PrWEF51aCmy7QIteYPIFZxSpvqBdP/2itCPPlJ49kj9zg/XPRFrdkne2W+CfwXUls8exMvu1RysZpKA==", + "dev": true, + "requires": { + "ansi-align": "^3.0.0", + "camelcase": "^6.2.0", + "chalk": "^4.1.0", + "cli-boxes": "^2.2.1", + "string-width": "^4.2.0", + "type-fest": "^0.20.2", + "widest-line": "^3.1.0", + "wrap-ansi": "^7.0.0" + }, + "dependencies": { + "camelcase": { + "version": "6.2.0", + "resolved": "https://registry.npmjs.org/camelcase/-/camelcase-6.2.0.tgz", + "integrity": "sha512-c7wVvbw3f37nuobQNtgsgG9POC9qMbNuMQmTCqZv23b6MIz0fcYpBiOlv9gEN/hdLdnZTDQhg6e9Dq5M1vKvfg==", + "dev": true + }, + "type-fest": { + "version": "0.20.2", + "resolved": "https://registry.npmjs.org/type-fest/-/type-fest-0.20.2.tgz", + "integrity": "sha512-Ne+eE4r0/iWnpAxD852z3A+N0Bt5RN//NjJwRd2VFHEmrywxf5vsZlh4R6lixl6B+wz/8d+maTSAkN1FIkI3LQ==", + "dev": true + } + } + }, + "brace-expansion": { + "version": "1.1.11", + "resolved": "https://registry.npmjs.org/brace-expansion/-/brace-expansion-1.1.11.tgz", + "integrity": "sha512-iCuPHDFgrHX7H2vEI/5xpz07zSHB00TpugqhmYtVmMO6518mCuRMoOYFldEBl0g187ufozdaHgWKcYFb61qGiA==", + "dev": true, + "requires": { + "balanced-match": "^1.0.0", + "concat-map": "0.0.1" + } + }, + "braces": { + "version": "3.0.2", + "resolved": "https://registry.npmjs.org/braces/-/braces-3.0.2.tgz", + "integrity": "sha512-b8um+L1RzM3WDSzvhm6gIz1yfTbBt6YTlcEKAvsmqCZZFw46z626lVj9j1yEPW33H5H+lBQpZMP1k8l+78Ha0A==", + "dev": true, + "requires": { + "fill-range": "^7.0.1" + } + }, + "browser-stdout": { + "version": "1.3.1", + "resolved": "https://registry.npmjs.org/browser-stdout/-/browser-stdout-1.3.1.tgz", + "integrity": "sha512-qhAVI1+Av2X7qelOfAIYwXONood6XlZE/fXaBSmW/T5SzLAmCgzi+eiWE7fUvbHaeNBQH13UftjpXxsfLkMpgw==", + "dev": true + }, + "browserslist": { + "version": "4.16.8", + "resolved": "https://registry.npmjs.org/browserslist/-/browserslist-4.16.8.tgz", + "integrity": "sha512-sc2m9ohR/49sWEbPj14ZSSZqp+kbi16aLao42Hmn3Z8FpjuMaq2xCA2l4zl9ITfyzvnvyE0hcg62YkIGKxgaNQ==", + "dev": true, + "requires": { + "caniuse-lite": "^1.0.30001251", + "colorette": "^1.3.0", + "electron-to-chromium": "^1.3.811", + "escalade": "^3.1.1", + "node-releases": "^1.1.75" + } + }, + "buffer": { + "version": "5.7.1", + "resolved": "https://registry.npmjs.org/buffer/-/buffer-5.7.1.tgz", + "integrity": "sha512-EHcyIPBQ4BSGlvjB16k5KgAJ27CIsHY/2JBmCRReo48y9rQ3MaUzWX3KVlBa4U7MyX02HdVj0K7C3WaB3ju7FQ==", + "dev": true, + "requires": { + "base64-js": "^1.3.1", + "ieee754": "^1.1.13" + } + }, + "buffer-from": { + "version": "1.1.2", + "resolved": "https://registry.npmjs.org/buffer-from/-/buffer-from-1.1.2.tgz", + "integrity": "sha512-E+XQCRwSbaaiChtv6k6Dwgc+bx+Bs6vuKJHHl5kox/BaKbhiXzqQOwK4cO22yElGp2OCmjwVhT3HmxgyPGnJfQ==", + "dev": true + }, + "cacheable-lookup": { + "version": "5.0.4", + "resolved": "https://registry.npmjs.org/cacheable-lookup/-/cacheable-lookup-5.0.4.tgz", + "integrity": "sha512-2/kNscPhpcxrOigMZzbiWF7dz8ilhb/nIHU3EyZiXWXpeq/au8qJ8VhdftMkty3n7Gj6HIGalQG8oiBNB3AJgA==", + "dev": true + }, + "cacheable-request": { + "version": "7.0.2", + "resolved": "https://registry.npmjs.org/cacheable-request/-/cacheable-request-7.0.2.tgz", + "integrity": "sha512-pouW8/FmiPQbuGpkXQ9BAPv/Mo5xDGANgSNXzTzJ8DrKGuXOssM4wIQRjfanNRh3Yu5cfYPvcorqbhg2KIJtew==", + "dev": true, + "requires": { + "clone-response": "^1.0.2", + "get-stream": "^5.1.0", + "http-cache-semantics": "^4.0.0", + "keyv": "^4.0.0", + "lowercase-keys": "^2.0.0", + "normalize-url": "^6.0.1", + "responselike": "^2.0.0" + }, + "dependencies": { + "get-stream": { + "version": "5.2.0", + "resolved": "https://registry.npmjs.org/get-stream/-/get-stream-5.2.0.tgz", + "integrity": "sha512-nBF+F1rAZVCu/p7rjzgA+Yb4lfYXrpl7a6VmJrU8wF9I1CKvP/QwPNZHnOlwbTkY6dvtFIzFMSyQXbLoTQPRpA==", + "dev": true, + "requires": { + "pump": "^3.0.0" + } + } + } + }, + "caching-transform": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/caching-transform/-/caching-transform-4.0.0.tgz", + "integrity": "sha512-kpqOvwXnjjN44D89K5ccQC+RUrsy7jB/XLlRrx0D7/2HNcTPqzsb6XgYoErwko6QsV184CA2YgS1fxDiiDZMWA==", + "dev": true, + "requires": { + "hasha": "^5.0.0", + "make-dir": "^3.0.0", + "package-hash": "^4.0.0", + "write-file-atomic": "^3.0.0" + } + }, + "call-bind": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/call-bind/-/call-bind-1.0.2.tgz", + "integrity": "sha512-7O+FbCihrB5WGbFYesctwmTKae6rOiIzmz1icreWJ+0aA7LJfuqhEso2T9ncpcFtzMQtzXf2QGGueWJGTYsqrA==", + "dev": true, + "requires": { + "function-bind": "^1.1.1", + "get-intrinsic": "^1.0.2" + } + }, + "callsites": { + "version": "3.1.0", + "resolved": "https://registry.npmjs.org/callsites/-/callsites-3.1.0.tgz", + "integrity": "sha512-P8BjAsXvZS+VIDUI11hHCQEv74YT67YUi5JJFNWIqL235sBmjX4+qx9Muvls5ivyNENctx46xQLQ3aTuE7ssaQ==", + "dev": true + }, + "camelcase": { + "version": "5.3.1", + "resolved": "https://registry.npmjs.org/camelcase/-/camelcase-5.3.1.tgz", + "integrity": "sha512-L28STB170nwWS63UjtlEOE3dldQApaJXZkOI1uMFfzf3rRuPegHaHesyee+YxQ+W6SvRDQV6UrdOdRiR153wJg==", + "dev": true + }, + "caniuse-lite": { + "version": "1.0.30001252", + "resolved": "https://registry.npmjs.org/caniuse-lite/-/caniuse-lite-1.0.30001252.tgz", + "integrity": "sha512-I56jhWDGMtdILQORdusxBOH+Nl/KgQSdDmpJezYddnAkVOmnoU8zwjTV9xAjMIYxr0iPreEAVylCGcmHCjfaOw==", + "dev": true + }, + "chalk": { + "version": "4.1.2", + "resolved": "https://registry.npmjs.org/chalk/-/chalk-4.1.2.tgz", + "integrity": "sha512-oKnbhFyRIXpUuez8iBMmyEa4nbj4IOQyuhc/wy9kY7/WVPcwIO9VA668Pu8RkO7+0G76SLROeyw9CpQ061i4mA==", + "dev": true, + "requires": { + "ansi-styles": "^4.1.0", + "supports-color": "^7.1.0" + }, + "dependencies": { + "supports-color": { + "version": "7.2.0", + "resolved": "https://registry.npmjs.org/supports-color/-/supports-color-7.2.0.tgz", + "integrity": "sha512-qpCAvRl9stuOHveKsn7HncJRvv501qIacKzQlO/+Lwxc9+0q2wLyv4Dfvt80/DPn2pqOBsJdDiogXGR9+OvwRw==", + "dev": true, + "requires": { + "has-flag": "^4.0.0" + } + } + } + }, + "chardet": { + "version": "0.7.0", + "resolved": "https://registry.npmjs.org/chardet/-/chardet-0.7.0.tgz", + "integrity": "sha512-mT8iDcrh03qDGRRmoA2hmBJnxpllMR+0/0qlzjqZES6NdiWDcZkCNAk4rPFZ9Q85r27unkiNNg8ZOiwZXBHwcA==", + "dev": true + }, + "chokidar": { + "version": "3.5.2", + "resolved": "https://registry.npmjs.org/chokidar/-/chokidar-3.5.2.tgz", + "integrity": "sha512-ekGhOnNVPgT77r4K/U3GDhu+FQ2S8TnK/s2KbIGXi0SZWuwkZ2QNyfWdZW+TVfn84DpEP7rLeCt2UI6bJ8GwbQ==", + "dev": true, + "requires": { + "anymatch": "~3.1.2", + "braces": "~3.0.2", + "fsevents": "~2.3.2", + "glob-parent": "~5.1.2", + "is-binary-path": "~2.1.0", + "is-glob": "~4.0.1", + "normalize-path": "~3.0.0", + "readdirp": "~3.6.0" + } + }, + "ci-info": { + "version": "3.2.0", + "resolved": "https://registry.npmjs.org/ci-info/-/ci-info-3.2.0.tgz", + "integrity": "sha512-dVqRX7fLUm8J6FgHJ418XuIgDLZDkYcDFTeL6TA2gt5WlIZUQrrH6EZrNClwT/H0FateUsZkGIOPRrLbP+PR9A==", + "dev": true + }, + "clean-stack": { + "version": "2.2.0", + "resolved": "https://registry.npmjs.org/clean-stack/-/clean-stack-2.2.0.tgz", + "integrity": "sha512-4diC9HaTE+KRAMWhDhrGOECgWZxoevMc5TlkObMqNSsVU62PYzXZ/SMTjzyGAFF1YusgxGcSWTEXBhp0CPwQ1A==", + "dev": true + }, + "cli-boxes": { + "version": "2.2.1", + "resolved": "https://registry.npmjs.org/cli-boxes/-/cli-boxes-2.2.1.tgz", + "integrity": "sha512-y4coMcylgSCdVinjiDBuR8PCC2bLjyGTwEmPb9NHR/QaNU6EUOXcTY/s6VjGMD6ENSEaeQYHCY0GNGS5jfMwPw==", + "dev": true + }, + "cli-cursor": { + "version": "3.1.0", + "resolved": "https://registry.npmjs.org/cli-cursor/-/cli-cursor-3.1.0.tgz", + "integrity": "sha512-I/zHAwsKf9FqGoXM4WWRACob9+SNukZTd94DWF57E4toouRulbCxcUh6RKUEOQlYTHJnzkPMySvPNaaSLNfLZw==", + "dev": true, + "requires": { + "restore-cursor": "^3.1.0" + } + }, + "cli-spinners": { + "version": "2.6.0", + "resolved": "https://registry.npmjs.org/cli-spinners/-/cli-spinners-2.6.0.tgz", + "integrity": "sha512-t+4/y50K/+4xcCRosKkA7W4gTr1MySvLV0q+PxmG7FJ5g+66ChKurYjxBCjHggHH3HA5Hh9cy+lcUGWDqVH+4Q==", + "dev": true + }, + "cli-width": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/cli-width/-/cli-width-3.0.0.tgz", + "integrity": "sha512-FxqpkPPwu1HjuN93Omfm4h8uIanXofW0RxVEW3k5RKx+mJJYSthzNhp32Kzxxy3YAEZ/Dc/EWN1vZRY0+kOhbw==", + "dev": true + }, + "cliui": { + "version": "7.0.4", + "resolved": "https://registry.npmjs.org/cliui/-/cliui-7.0.4.tgz", + "integrity": "sha512-OcRE68cOsVMXp1Yvonl/fzkQOyjLSu/8bhPDfQt0e0/Eb283TKP20Fs2MqoPsr9SwA595rRCA+QMzYc9nBP+JQ==", + "dev": true, + "requires": { + "string-width": "^4.2.0", + "strip-ansi": "^6.0.0", + "wrap-ansi": "^7.0.0" + } + }, + "clone": { + "version": "1.0.4", + "resolved": "https://registry.npmjs.org/clone/-/clone-1.0.4.tgz", + "integrity": "sha1-2jCcwmPfFZlMaIypAheco8fNfH4=", + "dev": true + }, + "clone-response": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/clone-response/-/clone-response-1.0.2.tgz", + "integrity": "sha1-0dyXOSAxTfZ/vrlCI7TuNQI56Ws=", + "dev": true, + "requires": { + "mimic-response": "^1.0.0" + } + }, + "cluster-key-slot": { + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/cluster-key-slot/-/cluster-key-slot-1.1.0.tgz", + "integrity": "sha512-2Nii8p3RwAPiFwsnZvukotvow2rIHM+yQ6ZcBXGHdniadkYGZYiGmkHJIbZPIV9nfv7m/U1IPMVVcAhoWFeklw==" + }, + "color-convert": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/color-convert/-/color-convert-2.0.1.tgz", + "integrity": "sha512-RRECPsj7iu/xb5oKYcsFHSppFNnsj/52OVTRKb4zP5onXwVF3zVmmToNcOfGC+CRDpfK/U584fMg38ZHCaElKQ==", + "dev": true, + "requires": { + "color-name": "~1.1.4" + } + }, + "color-name": { + "version": "1.1.4", + "resolved": "https://registry.npmjs.org/color-name/-/color-name-1.1.4.tgz", + "integrity": "sha512-dOy+3AuW3a2wNbZHIuMZpTcgjGuLU/uBL/ubcZF9OXbDo8ff4O8yVp5Bf0efS8uEoYo5q4Fx7dY9OgQGXgAsQA==", + "dev": true + }, + "colorette": { + "version": "1.3.0", + "resolved": "https://registry.npmjs.org/colorette/-/colorette-1.3.0.tgz", + "integrity": "sha512-ecORCqbSFP7Wm8Y6lyqMJjexBQqXSF7SSeaTyGGphogUjBlFP9m9o08wy86HL2uB7fMTxtOUzLMk7ogKcxMg1w==", + "dev": true + }, + "combined-stream": { + "version": "1.0.8", + "resolved": "https://registry.npmjs.org/combined-stream/-/combined-stream-1.0.8.tgz", + "integrity": "sha512-FQN4MRfuJeHf7cBbBMJFXhKSDq+2kAArBlmRBvcvFE5BB1HZKXtSFASDhdlz9zOYwxh8lDdnvmMOe/+5cdoEdg==", + "dev": true, + "requires": { + "delayed-stream": "~1.0.0" + } + }, + "commondir": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/commondir/-/commondir-1.0.1.tgz", + "integrity": "sha1-3dgA2gxmEnOTzKWVDqloo6rxJTs=", + "dev": true + }, + "concat-map": { + "version": "0.0.1", + "resolved": "https://registry.npmjs.org/concat-map/-/concat-map-0.0.1.tgz", + "integrity": "sha1-2Klr13/Wjfd5OnMDajug1UBdR3s=", + "dev": true + }, + "configstore": { + "version": "5.0.1", + "resolved": "https://registry.npmjs.org/configstore/-/configstore-5.0.1.tgz", + "integrity": "sha512-aMKprgk5YhBNyH25hj8wGt2+D52Sw1DRRIzqBwLp2Ya9mFmY8KPvvtvmna8SxVR9JMZ4kzMD68N22vlaRpkeFA==", + "dev": true, + "requires": { + "dot-prop": "^5.2.0", + "graceful-fs": "^4.1.2", + "make-dir": "^3.0.0", + "unique-string": "^2.0.0", + "write-file-atomic": "^3.0.0", + "xdg-basedir": "^4.0.0" + } + }, + "convert-source-map": { + "version": "1.8.0", + "resolved": "https://registry.npmjs.org/convert-source-map/-/convert-source-map-1.8.0.tgz", + "integrity": "sha512-+OQdjP49zViI/6i7nIJpA8rAl4sV/JdPfU9nZs3VqOwGIgizICvuN2ru6fMd+4llL0tar18UYJXfZ/TWtmhUjA==", + "dev": true, + "requires": { + "safe-buffer": "~5.1.1" + } + }, + "cosmiconfig": { + "version": "7.0.0", + "resolved": "https://registry.npmjs.org/cosmiconfig/-/cosmiconfig-7.0.0.tgz", + "integrity": "sha512-pondGvTuVYDk++upghXJabWzL6Kxu6f26ljFw64Swq9v6sQPUL3EUlVDV56diOjpCayKihL6hVe8exIACU4XcA==", + "dev": true, + "requires": { + "@types/parse-json": "^4.0.0", + "import-fresh": "^3.2.1", + "parse-json": "^5.0.0", + "path-type": "^4.0.0", + "yaml": "^1.10.0" + } + }, + "create-require": { + "version": "1.1.1", + "resolved": "https://registry.npmjs.org/create-require/-/create-require-1.1.1.tgz", + "integrity": "sha512-dcKFX3jn0MpIaXjisoRvexIJVEKzaq7z2rZKxf+MSr9TkdmHmsU4m2lcLojrj/FHl8mk5VxMmYA+ftRkP/3oKQ==", + "dev": true + }, + "cross-spawn": { + "version": "7.0.3", + "resolved": "https://registry.npmjs.org/cross-spawn/-/cross-spawn-7.0.3.tgz", + "integrity": "sha512-iRDPJKUPVEND7dHPO8rkbOnPpyDygcDFtWjpeWNCgy8WP2rXcxXL8TskReQl6OrB2G7+UJrags1q15Fudc7G6w==", + "dev": true, + "requires": { + "path-key": "^3.1.0", + "shebang-command": "^2.0.0", + "which": "^2.0.1" + } + }, + "crypto-random-string": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/crypto-random-string/-/crypto-random-string-2.0.0.tgz", + "integrity": "sha512-v1plID3y9r/lPhviJ1wrXpLeyUIGAZ2SHNYTEapm7/8A9nLPoyvVp3RK/EPFqn5kEznyWgYZNsRtYYIWbuG8KA==", + "dev": true + }, + "debug": { + "version": "4.3.1", + "resolved": "https://registry.npmjs.org/debug/-/debug-4.3.1.tgz", + "integrity": "sha512-doEwdvm4PCeK4K3RQN2ZC2BYUBaxwLARCqZmMjtF8a51J2Rb0xpVloFRnCODwqjpwnAoao4pelN8l3RJdv3gRQ==", + "dev": true, + "requires": { + "ms": "2.1.2" + }, + "dependencies": { + "ms": { + "version": "2.1.2", + "resolved": "https://registry.npmjs.org/ms/-/ms-2.1.2.tgz", + "integrity": "sha512-sGkPx+VjMtmA6MX27oA4FBFELFCZZ4S4XqeGOXCv68tT+jb3vk/RyaKWP0PTKyWtmLSM0b+adUTEvbs1PEaH2w==", + "dev": true + } + } + }, + "decamelize": { + "version": "1.2.0", + "resolved": "https://registry.npmjs.org/decamelize/-/decamelize-1.2.0.tgz", + "integrity": "sha1-9lNNFRSCabIDUue+4m9QH5oZEpA=", + "dev": true + }, + "decode-uri-component": { + "version": "0.2.0", + "resolved": "https://registry.npmjs.org/decode-uri-component/-/decode-uri-component-0.2.0.tgz", + "integrity": "sha1-6zkTMzRYd1y4TNGh+uBiEGu4dUU=", + "dev": true + }, + "decompress-response": { + "version": "6.0.0", + "resolved": "https://registry.npmjs.org/decompress-response/-/decompress-response-6.0.0.tgz", + "integrity": "sha512-aW35yZM6Bb/4oJlZncMH2LCoZtJXTRxES17vE3hoRiowU2kWHaJKFkSBDnDR+cm9J+9QhXmREyIfv0pji9ejCQ==", + "dev": true, + "requires": { + "mimic-response": "^3.1.0" + }, + "dependencies": { + "mimic-response": { + "version": "3.1.0", + "resolved": "https://registry.npmjs.org/mimic-response/-/mimic-response-3.1.0.tgz", + "integrity": "sha512-z0yWI+4FDrrweS8Zmt4Ej5HdJmky15+L2e6Wgn3+iK5fWzb6T3fhNFq2+MeTRb064c6Wr4N/wv0DzQTjNzHNGQ==", + "dev": true + } + } + }, + "deep-extend": { + "version": "0.6.0", + "resolved": "https://registry.npmjs.org/deep-extend/-/deep-extend-0.6.0.tgz", + "integrity": "sha512-LOHxIOaPYdHlJRtCQfDIVZtfw/ufM8+rVj649RIHzcm/vGwQRXFt6OPqIFWsm2XEMrNIEtWR64sY1LEKD2vAOA==", + "dev": true + }, + "default-require-extensions": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/default-require-extensions/-/default-require-extensions-3.0.0.tgz", + "integrity": "sha512-ek6DpXq/SCpvjhpFsLFRVtIxJCRw6fUR42lYMVZuUMK7n8eMz4Uh5clckdBjEpLhn/gEBZo7hDJnJcwdKLKQjg==", + "dev": true, + "requires": { + "strip-bom": "^4.0.0" + } + }, + "defaults": { + "version": "1.0.3", + "resolved": "https://registry.npmjs.org/defaults/-/defaults-1.0.3.tgz", + "integrity": "sha1-xlYFHpgX2f8I7YgUd/P+QBnz730=", + "dev": true, + "requires": { + "clone": "^1.0.2" + } + }, + "defer-to-connect": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/defer-to-connect/-/defer-to-connect-2.0.1.tgz", + "integrity": "sha512-4tvttepXG1VaYGrRibk5EwJd1t4udunSOVMdLSAL6mId1ix438oPwPZMALY41FCijukO1L0twNcGsdzS7dHgDg==", + "dev": true + }, + "delayed-stream": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/delayed-stream/-/delayed-stream-1.0.0.tgz", + "integrity": "sha1-3zrhmayt+31ECqrgsp4icrJOxhk=", + "dev": true + }, + "deprecated-obj": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/deprecated-obj/-/deprecated-obj-2.0.0.tgz", + "integrity": "sha512-CkdywZC2rJ8RGh+y3MM1fw1EJ4oO/oNExGbRFv0AQoMS+faTd3nO7slYjkj/6t8OnIMUE+wxh6G97YHhK1ytrw==", + "dev": true, + "requires": { + "flat": "^5.0.2", + "lodash": "^4.17.20" + } + }, + "deprecation": { + "version": "2.3.1", + "resolved": "https://registry.npmjs.org/deprecation/-/deprecation-2.3.1.tgz", + "integrity": "sha512-xmHIy4F3scKVwMsQ4WnVaS8bHOx0DmVwRywosKhaILI0ywMDWPtBSku2HNxRvF7jtwDRsoEwYQSfbxj8b7RlJQ==", + "dev": true + }, + "diff": { + "version": "5.0.0", + "resolved": "https://registry.npmjs.org/diff/-/diff-5.0.0.tgz", + "integrity": "sha512-/VTCrvm5Z0JGty/BWHljh+BAiw3IK+2j87NGMu8Nwc/f48WoDAC395uomO9ZD117ZOBaHmkX1oyLvkVM/aIT3w==", + "dev": true + }, + "dir-glob": { + "version": "3.0.1", + "resolved": "https://registry.npmjs.org/dir-glob/-/dir-glob-3.0.1.tgz", + "integrity": "sha512-WkrWp9GR4KXfKGYzOLmTuGVi1UWFfws377n9cc55/tb6DuqyF6pcQ5AbiHEshaDpY9v6oaSr2XCDidGmMwdzIA==", + "dev": true, + "requires": { + "path-type": "^4.0.0" + } + }, + "dot-prop": { + "version": "5.3.0", + "resolved": "https://registry.npmjs.org/dot-prop/-/dot-prop-5.3.0.tgz", + "integrity": "sha512-QM8q3zDe58hqUqjraQOmzZ1LIH9SWQJTlEKCH4kJ2oQvLZk7RbQXvtDM2XEq3fwkV9CCvvH4LA0AV+ogFsBM2Q==", + "dev": true, + "requires": { + "is-obj": "^2.0.0" + } + }, + "duplexer3": { + "version": "0.1.4", + "resolved": "https://registry.npmjs.org/duplexer3/-/duplexer3-0.1.4.tgz", + "integrity": "sha1-7gHdHKwO08vH/b6jfcCo8c4ALOI=", + "dev": true + }, + "electron-to-chromium": { + "version": "1.3.822", + "resolved": "https://registry.npmjs.org/electron-to-chromium/-/electron-to-chromium-1.3.822.tgz", + "integrity": "sha512-k7jG5oYYHxF4jx6PcqwHX3JVME/OjzolqOZiIogi9xtsfsmTjTdie4x88OakYFPEa8euciTgCCzvVNwvmjHb1Q==", + "dev": true + }, + "emoji-regex": { + "version": "8.0.0", + "resolved": "https://registry.npmjs.org/emoji-regex/-/emoji-regex-8.0.0.tgz", + "integrity": "sha512-MSjYzcWNOA0ewAHpz0MxpYFvwg6yjy1NG3xteoqz644VCo/RPgnr1/GGt+ic3iJTzQ8Eu3TdM14SawnVUmGE6A==", + "dev": true + }, + "end-of-stream": { + "version": "1.4.4", + "resolved": "https://registry.npmjs.org/end-of-stream/-/end-of-stream-1.4.4.tgz", + "integrity": "sha512-+uw1inIHVPQoaVuHzRyXd21icM+cnt4CzD5rW+NC1wjOUSTOs+Te7FOv7AhN7vS9x/oIyhLP5PR1H+phQAHu5Q==", + "dev": true, + "requires": { + "once": "^1.4.0" + } + }, + "error-ex": { + "version": "1.3.2", + "resolved": "https://registry.npmjs.org/error-ex/-/error-ex-1.3.2.tgz", + "integrity": "sha512-7dFHNmqeFSEt2ZBsCriorKnn3Z2pj+fd9kmI6QoWw4//DL+icEBfc0U7qJCisqrTsKTjw4fNFy2pW9OqStD84g==", + "dev": true, + "requires": { + "is-arrayish": "^0.2.1" + } + }, + "es6-error": { + "version": "4.1.1", + "resolved": "https://registry.npmjs.org/es6-error/-/es6-error-4.1.1.tgz", + "integrity": "sha512-Um/+FxMr9CISWh0bi5Zv0iOD+4cFh5qLeks1qhAopKVAJw3drgKbKySikp7wGhDL0HPeaja0P5ULZrxLkniUVg==", + "dev": true + }, + "escalade": { + "version": "3.1.1", + "resolved": "https://registry.npmjs.org/escalade/-/escalade-3.1.1.tgz", + "integrity": "sha512-k0er2gUkLf8O0zKJiAhmkTnJlTvINGv7ygDNPbeIsX/TJjGJZHuh9B2UxbsaEkmlEo9MfhrSzmhIlhRlI2GXnw==", + "dev": true + }, + "escape-goat": { + "version": "2.1.1", + "resolved": "https://registry.npmjs.org/escape-goat/-/escape-goat-2.1.1.tgz", + "integrity": "sha512-8/uIhbG12Csjy2JEW7D9pHbreaVaS/OpN3ycnyvElTdwM5n6GY6W6e2IPemfvGZeUMqZ9A/3GqIZMgKnBhAw/Q==", + "dev": true + }, + "escape-string-regexp": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/escape-string-regexp/-/escape-string-regexp-4.0.0.tgz", + "integrity": "sha512-TtpcNJ3XAzx3Gq8sWRzJaVajRs0uVxA2YAkdb1jm2YkPz4G6egUFAyA3n5vtEIZefPk5Wa4UXbKuS5fKkJWdgA==", + "dev": true + }, + "esprima": { + "version": "4.0.1", + "resolved": "https://registry.npmjs.org/esprima/-/esprima-4.0.1.tgz", + "integrity": "sha512-eGuFFw7Upda+g4p+QHvnW0RyTX/SVeJBDM/gCtMARO0cLuT2HcEKnTPvhjV6aGeqrCB/sbNop0Kszm0jsaWU4A==", + "dev": true + }, + "execa": { + "version": "5.1.1", + "resolved": "https://registry.npmjs.org/execa/-/execa-5.1.1.tgz", + "integrity": "sha512-8uSpZZocAZRBAPIEINJj3Lo9HyGitllczc27Eh5YYojjMFMn8yHMDMaUHE2Jqfq05D/wucwI4JGURyXt1vchyg==", + "dev": true, + "requires": { + "cross-spawn": "^7.0.3", + "get-stream": "^6.0.0", + "human-signals": "^2.1.0", + "is-stream": "^2.0.0", + "merge-stream": "^2.0.0", + "npm-run-path": "^4.0.1", + "onetime": "^5.1.2", + "signal-exit": "^3.0.3", + "strip-final-newline": "^2.0.0" + } + }, + "external-editor": { + "version": "3.1.0", + "resolved": "https://registry.npmjs.org/external-editor/-/external-editor-3.1.0.tgz", + "integrity": "sha512-hMQ4CX1p1izmuLYyZqLMO/qGNw10wSv9QDCPfzXfyFrOaCSSoRfqE1Kf1s5an66J5JZC62NewG+mK49jOCtQew==", + "dev": true, + "requires": { + "chardet": "^0.7.0", + "iconv-lite": "^0.4.24", + "tmp": "^0.0.33" + } + }, + "fast-glob": { + "version": "3.2.7", + "resolved": "https://registry.npmjs.org/fast-glob/-/fast-glob-3.2.7.tgz", + "integrity": "sha512-rYGMRwip6lUMvYD3BTScMwT1HtAs2d71SMv66Vrxs0IekGZEjhM0pcMfjQPnknBt2zeCwQMEupiN02ZP4DiT1Q==", + "dev": true, + "requires": { + "@nodelib/fs.stat": "^2.0.2", + "@nodelib/fs.walk": "^1.2.3", + "glob-parent": "^5.1.2", + "merge2": "^1.3.0", + "micromatch": "^4.0.4" + } + }, + "fastq": { + "version": "1.12.0", + "resolved": "https://registry.npmjs.org/fastq/-/fastq-1.12.0.tgz", + "integrity": "sha512-VNX0QkHK3RsXVKr9KrlUv/FoTa0NdbYoHHl7uXHv2rzyHSlxjdNAKug2twd9luJxpcyNeAgf5iPPMutJO67Dfg==", + "dev": true, + "requires": { + "reusify": "^1.0.4" + } + }, + "figures": { + "version": "3.2.0", + "resolved": "https://registry.npmjs.org/figures/-/figures-3.2.0.tgz", + "integrity": "sha512-yaduQFRKLXYOGgEn6AZau90j3ggSOyiqXU0F9JZfeXYhNa+Jk4X+s45A2zg5jns87GAFa34BBm2kXw4XpNcbdg==", + "dev": true, + "requires": { + "escape-string-regexp": "^1.0.5" + }, + "dependencies": { + "escape-string-regexp": { + "version": "1.0.5", + "resolved": "https://registry.npmjs.org/escape-string-regexp/-/escape-string-regexp-1.0.5.tgz", + "integrity": "sha1-G2HAViGQqN/2rjuyzwIAyhMLhtQ=", + "dev": true + } + } + }, + "fill-range": { + "version": "7.0.1", + "resolved": "https://registry.npmjs.org/fill-range/-/fill-range-7.0.1.tgz", + "integrity": "sha512-qOo9F+dMUmC2Lcb4BbVvnKJxTPjCm+RRpe4gDuGrzkL7mEVl/djYSu2OdQ2Pa302N4oqkSg9ir6jaLWJ2USVpQ==", + "dev": true, + "requires": { + "to-regex-range": "^5.0.1" + } + }, + "filter-obj": { + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/filter-obj/-/filter-obj-1.1.0.tgz", + "integrity": "sha1-mzERErxsYSehbgFsbF1/GeCAXFs=", + "dev": true + }, + "find-cache-dir": { + "version": "3.3.2", + "resolved": "https://registry.npmjs.org/find-cache-dir/-/find-cache-dir-3.3.2.tgz", + "integrity": "sha512-wXZV5emFEjrridIgED11OoUKLxiYjAcqot/NJdAkOhlJ+vGzwhOAfcG5OX1jP+S0PcjEn8bdMJv+g2jwQ3Onig==", + "dev": true, + "requires": { + "commondir": "^1.0.1", + "make-dir": "^3.0.2", + "pkg-dir": "^4.1.0" + } + }, + "find-up": { + "version": "5.0.0", + "resolved": "https://registry.npmjs.org/find-up/-/find-up-5.0.0.tgz", + "integrity": "sha512-78/PXT1wlLLDgTzDs7sjq9hzz0vXD+zn+7wypEe4fXQxCmdmqfGsEPQxmiCSQI3ajFV91bVSsvNtrJRiW6nGng==", + "dev": true, + "requires": { + "locate-path": "^6.0.0", + "path-exists": "^4.0.0" + } + }, + "flat": { + "version": "5.0.2", + "resolved": "https://registry.npmjs.org/flat/-/flat-5.0.2.tgz", + "integrity": "sha512-b6suED+5/3rTpUBdG1gupIl8MPFCAMA0QXwmljLhvCUKcUvdE4gWky9zpuGCcXHOsz4J9wPGNWq6OKpmIzz3hQ==", + "dev": true + }, + "foreground-child": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/foreground-child/-/foreground-child-2.0.0.tgz", + "integrity": "sha512-dCIq9FpEcyQyXKCkyzmlPTFNgrCzPudOe+mhvJU5zAtlBnGVy2yKxtfsxK2tQBThwq225jcvBjpw1Gr40uzZCA==", + "dev": true, + "requires": { + "cross-spawn": "^7.0.0", + "signal-exit": "^3.0.2" + } + }, + "form-data": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/form-data/-/form-data-4.0.0.tgz", + "integrity": "sha512-ETEklSGi5t0QMZuiXoA/Q6vcnxcLQP5vdugSpuAyi6SVGi2clPPp+xgEhuMaHC+zGgn31Kd235W35f7Hykkaww==", + "dev": true, + "requires": { + "asynckit": "^0.4.0", + "combined-stream": "^1.0.8", + "mime-types": "^2.1.12" + } + }, + "fromentries": { + "version": "1.3.2", + "resolved": "https://registry.npmjs.org/fromentries/-/fromentries-1.3.2.tgz", + "integrity": "sha512-cHEpEQHUg0f8XdtZCc2ZAhrHzKzT0MrFUTcvx+hfxYu7rGMDc5SKoXFh+n4YigxsHXRzc6OrCshdR1bWH6HHyg==", + "dev": true + }, + "fs.realpath": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/fs.realpath/-/fs.realpath-1.0.0.tgz", + "integrity": "sha1-FQStJSMVjKpA20onh8sBQRmU6k8=", + "dev": true + }, + "fsevents": { + "version": "2.3.2", + "resolved": "https://registry.npmjs.org/fsevents/-/fsevents-2.3.2.tgz", + "integrity": "sha512-xiqMQR4xAeHTuB9uWm+fFRcIOgKBMiOBP+eXiyT7jsgVCq1bkVygt00oASowB7EdtpOHaaPgKt812P9ab+DDKA==", + "dev": true, + "optional": true + }, + "function-bind": { + "version": "1.1.1", + "resolved": "https://registry.npmjs.org/function-bind/-/function-bind-1.1.1.tgz", + "integrity": "sha512-yIovAzMX49sF8Yl58fSCWJ5svSLuaibPxXQJFLmBObTuCr0Mf1KiPopGM9NiFjiYBCbfaa2Fh6breQ6ANVTI0A==", + "dev": true + }, + "generic-pool": { + "version": "3.8.2", + "resolved": "https://registry.npmjs.org/generic-pool/-/generic-pool-3.8.2.tgz", + "integrity": "sha512-nGToKy6p3PAbYQ7p1UlWl6vSPwfwU6TMSWK7TTu+WUY4ZjyZQGniGGt2oNVvyNSpyZYSB43zMXVLcBm08MTMkg==" + }, + "gensync": { + "version": "1.0.0-beta.2", + "resolved": "https://registry.npmjs.org/gensync/-/gensync-1.0.0-beta.2.tgz", + "integrity": "sha512-3hN7NaskYvMDLQY55gnW3NQ+mesEAepTqlg+VEbj7zzqEMBVNhzcGYYeqFo/TlYz6eQiFcp1HcsCZO+nGgS8zg==", + "dev": true + }, + "get-caller-file": { + "version": "2.0.5", + "resolved": "https://registry.npmjs.org/get-caller-file/-/get-caller-file-2.0.5.tgz", + "integrity": "sha512-DyFP3BM/3YHTQOCUL/w0OZHR0lpKeGrxotcHWcqNEdnltqFwXVfhEBQ94eIo34AfQpo0rGki4cyIiftY06h2Fg==", + "dev": true + }, + "get-intrinsic": { + "version": "1.1.1", + "resolved": "https://registry.npmjs.org/get-intrinsic/-/get-intrinsic-1.1.1.tgz", + "integrity": "sha512-kWZrnVM42QCiEA2Ig1bG8zjoIMOgxWwYCEeNdwY6Tv/cOSeGpcoX4pXHfKUxNKVoArnrEr2e9srnAxxGIraS9Q==", + "dev": true, + "requires": { + "function-bind": "^1.1.1", + "has": "^1.0.3", + "has-symbols": "^1.0.1" + } + }, + "get-package-type": { + "version": "0.1.0", + "resolved": "https://registry.npmjs.org/get-package-type/-/get-package-type-0.1.0.tgz", + "integrity": "sha512-pjzuKtY64GYfWizNAJ0fr9VqttZkNiK2iS430LtIHzjBEr6bX8Am2zm4sW4Ro5wjWW5cAlRL1qAMTcXbjNAO2Q==", + "dev": true + }, + "get-stream": { + "version": "6.0.1", + "resolved": "https://registry.npmjs.org/get-stream/-/get-stream-6.0.1.tgz", + "integrity": "sha512-ts6Wi+2j3jQjqi70w5AlN8DFnkSwC+MqmxEzdEALB2qXZYV3X/b1CTfgPLGJNMeAWxdPfU8FO1ms3NUfaHCPYg==", + "dev": true + }, + "git-up": { + "version": "4.0.5", + "resolved": "https://registry.npmjs.org/git-up/-/git-up-4.0.5.tgz", + "integrity": "sha512-YUvVDg/vX3d0syBsk/CKUTib0srcQME0JyHkL5BaYdwLsiCslPWmDSi8PUMo9pXYjrryMcmsCoCgsTpSCJEQaA==", + "dev": true, + "requires": { + "is-ssh": "^1.3.0", + "parse-url": "^6.0.0" + } + }, + "git-url-parse": { + "version": "11.5.0", + "resolved": "https://registry.npmjs.org/git-url-parse/-/git-url-parse-11.5.0.tgz", + "integrity": "sha512-TZYSMDeM37r71Lqg1mbnMlOqlHd7BSij9qN7XwTkRqSAYFMihGLGhfHwgqQob3GUhEneKnV4nskN9rbQw2KGxA==", + "dev": true, + "requires": { + "git-up": "^4.0.0" + } + }, + "glob": { + "version": "7.1.7", + "resolved": "https://registry.npmjs.org/glob/-/glob-7.1.7.tgz", + "integrity": "sha512-OvD9ENzPLbegENnYP5UUfJIirTg4+XwMWGaQfQTY0JenxNvvIKP3U3/tAQSPIu/lHxXYSZmpXlUHeqAIdKzBLQ==", + "dev": true, + "requires": { + "fs.realpath": "^1.0.0", + "inflight": "^1.0.4", + "inherits": "2", + "minimatch": "^3.0.4", + "once": "^1.3.0", + "path-is-absolute": "^1.0.0" + } + }, + "glob-parent": { + "version": "5.1.2", + "resolved": "https://registry.npmjs.org/glob-parent/-/glob-parent-5.1.2.tgz", + "integrity": "sha512-AOIgSQCepiJYwP3ARnGx+5VnTu2HBYdzbGP45eLw1vr3zB3vZLeyed1sC9hnbcOc9/SrMyM5RPQrkGz4aS9Zow==", + "dev": true, + "requires": { + "is-glob": "^4.0.1" + } + }, + "global-dirs": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/global-dirs/-/global-dirs-3.0.0.tgz", + "integrity": "sha512-v8ho2DS5RiCjftj1nD9NmnfaOzTdud7RRnVd9kFNOjqZbISlx5DQ+OrTkywgd0dIt7oFCvKetZSHoHcP3sDdiA==", + "dev": true, + "requires": { + "ini": "2.0.0" + } + }, + "globals": { + "version": "11.12.0", + "resolved": "https://registry.npmjs.org/globals/-/globals-11.12.0.tgz", + "integrity": "sha512-WOBp/EEGUiIsJSp7wcv/y6MO+lV9UoncWqxuFfm8eBwzWNgyfBd6Gz+IeKQ9jCmyhoH99g15M3T+QaVHFjizVA==", + "dev": true + }, + "globby": { + "version": "11.0.4", + "resolved": "https://registry.npmjs.org/globby/-/globby-11.0.4.tgz", + "integrity": "sha512-9O4MVG9ioZJ08ffbcyVYyLOJLk5JQ688pJ4eMGLpdWLHq/Wr1D9BlriLQyL0E+jbkuePVZXYFj47QM/v093wHg==", + "dev": true, + "requires": { + "array-union": "^2.1.0", + "dir-glob": "^3.0.1", + "fast-glob": "^3.1.1", + "ignore": "^5.1.4", + "merge2": "^1.3.0", + "slash": "^3.0.0" + } + }, + "got": { + "version": "11.8.2", + "resolved": "https://registry.npmjs.org/got/-/got-11.8.2.tgz", + "integrity": "sha512-D0QywKgIe30ODs+fm8wMZiAcZjypcCodPNuMz5H9Mny7RJ+IjJ10BdmGW7OM7fHXP+O7r6ZwapQ/YQmMSvB0UQ==", + "dev": true, + "requires": { + "@sindresorhus/is": "^4.0.0", + "@szmarczak/http-timer": "^4.0.5", + "@types/cacheable-request": "^6.0.1", + "@types/responselike": "^1.0.0", + "cacheable-lookup": "^5.0.3", + "cacheable-request": "^7.0.1", + "decompress-response": "^6.0.0", + "http2-wrapper": "^1.0.0-beta.5.2", + "lowercase-keys": "^2.0.0", + "p-cancelable": "^2.0.0", + "responselike": "^2.0.0" + } + }, + "graceful-fs": { + "version": "4.2.8", + "resolved": "https://registry.npmjs.org/graceful-fs/-/graceful-fs-4.2.8.tgz", + "integrity": "sha512-qkIilPUYcNhJpd33n0GBXTB1MMPp14TxEsEs0pTrsSVucApsYzW5V+Q8Qxhik6KU3evy+qkAAowTByymK0avdg==", + "dev": true + }, + "growl": { + "version": "1.10.5", + "resolved": "https://registry.npmjs.org/growl/-/growl-1.10.5.tgz", + "integrity": "sha512-qBr4OuELkhPenW6goKVXiv47US3clb3/IbuWF9KNKEijAy9oeHxU9IgzjvJhHkUzhaj7rOUD7+YGWqUjLp5oSA==", + "dev": true + }, + "handlebars": { + "version": "4.7.7", + "resolved": "https://registry.npmjs.org/handlebars/-/handlebars-4.7.7.tgz", + "integrity": "sha512-aAcXm5OAfE/8IXkcZvCepKU3VzW1/39Fb5ZuqMtgI/hT8X2YgoMvBY5dLhq/cpOvw7Lk1nK/UF71aLG/ZnVYRA==", + "dev": true, + "requires": { + "minimist": "^1.2.5", + "neo-async": "^2.6.0", + "source-map": "^0.6.1", + "uglify-js": "^3.1.4", + "wordwrap": "^1.0.0" + }, + "dependencies": { + "source-map": { + "version": "0.6.1", + "resolved": "https://registry.npmjs.org/source-map/-/source-map-0.6.1.tgz", + "integrity": "sha512-UjgapumWlbMhkBgzT7Ykc5YXUT46F0iKu8SGXq0bcwP5dz/h0Plj6enJqjz1Zbq2l5WaqYnrVbwWOWMyF3F47g==", + "dev": true + } + } + }, + "has": { + "version": "1.0.3", + "resolved": "https://registry.npmjs.org/has/-/has-1.0.3.tgz", + "integrity": "sha512-f2dvO0VU6Oej7RkWJGrehjbzMAjFp5/VKPp5tTpWIV4JHHZK1/BxbFRtf/siA2SWTe09caDmVtYYzWEIbBS4zw==", + "dev": true, + "requires": { + "function-bind": "^1.1.1" + } + }, + "has-flag": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/has-flag/-/has-flag-4.0.0.tgz", + "integrity": "sha512-EykJT/Q1KjTWctppgIAgfSO0tKVuZUjhgMr17kqTumMl6Afv3EISleU7qZUzoXDFTAHTDC4NOoG/ZxU3EvlMPQ==", + "dev": true + }, + "has-symbols": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/has-symbols/-/has-symbols-1.0.2.tgz", + "integrity": "sha512-chXa79rL/UC2KlX17jo3vRGz0azaWEx5tGqZg5pO3NUyEJVB17dMruQlzCCOfUvElghKcm5194+BCRvi2Rv/Gw==", + "dev": true + }, + "has-yarn": { + "version": "2.1.0", + "resolved": "https://registry.npmjs.org/has-yarn/-/has-yarn-2.1.0.tgz", + "integrity": "sha512-UqBRqi4ju7T+TqGNdqAO0PaSVGsDGJUBQvk9eUWNGRY1CFGDzYhLWoM7JQEemnlvVcv/YEmc2wNW8BC24EnUsw==", + "dev": true + }, + "hasha": { + "version": "5.2.2", + "resolved": "https://registry.npmjs.org/hasha/-/hasha-5.2.2.tgz", + "integrity": "sha512-Hrp5vIK/xr5SkeN2onO32H0MgNZ0f17HRNH39WfL0SYUNOTZ5Lz1TJ8Pajo/87dYGEFlLMm7mIc/k/s6Bvz9HQ==", + "dev": true, + "requires": { + "is-stream": "^2.0.0", + "type-fest": "^0.8.0" + } + }, + "he": { + "version": "1.2.0", + "resolved": "https://registry.npmjs.org/he/-/he-1.2.0.tgz", + "integrity": "sha512-F/1DnUGPopORZi0ni+CvrCgHQ5FyEAHRLSApuYWMmrbSwoN2Mn/7k+Gl38gJnR7yyDZk6WLXwiGod1JOWNDKGw==", + "dev": true + }, + "html-escaper": { + "version": "2.0.2", + "resolved": "https://registry.npmjs.org/html-escaper/-/html-escaper-2.0.2.tgz", + "integrity": "sha512-H2iMtd0I4Mt5eYiapRdIDjp+XzelXQ0tFE4JS7YFwFevXXMmOp9myNrUvCg0D6ws8iqkRPBfKHgbwig1SmlLfg==", + "dev": true + }, + "http-cache-semantics": { + "version": "4.1.0", + "resolved": "https://registry.npmjs.org/http-cache-semantics/-/http-cache-semantics-4.1.0.tgz", + "integrity": "sha512-carPklcUh7ROWRK7Cv27RPtdhYhUsela/ue5/jKzjegVvXDqM2ILE9Q2BGn9JZJh1g87cp56su/FgQSzcWS8cQ==", + "dev": true + }, + "http2-wrapper": { + "version": "1.0.3", + "resolved": "https://registry.npmjs.org/http2-wrapper/-/http2-wrapper-1.0.3.tgz", + "integrity": "sha512-V+23sDMr12Wnz7iTcDeJr3O6AIxlnvT/bmaAAAP/Xda35C90p9599p0F1eHR/N1KILWSoWVAiOMFjBBXaXSMxg==", + "dev": true, + "requires": { + "quick-lru": "^5.1.1", + "resolve-alpn": "^1.0.0" + } + }, + "human-signals": { + "version": "2.1.0", + "resolved": "https://registry.npmjs.org/human-signals/-/human-signals-2.1.0.tgz", + "integrity": "sha512-B4FFZ6q/T2jhhksgkbEW3HBvWIfDW85snkQgawt07S7J5QXTk6BkNV+0yAeZrM5QpMAdYlocGoljn0sJ/WQkFw==", + "dev": true + }, + "iconv-lite": { + "version": "0.4.24", + "resolved": "https://registry.npmjs.org/iconv-lite/-/iconv-lite-0.4.24.tgz", + "integrity": "sha512-v3MXnZAcvnywkTUEZomIActle7RXXeedOR31wwl7VlyoXO4Qi9arvSenNQWne1TcRwhCL1HwLI21bEqdpj8/rA==", + "dev": true, + "requires": { + "safer-buffer": ">= 2.1.2 < 3" + } + }, + "ieee754": { + "version": "1.2.1", + "resolved": "https://registry.npmjs.org/ieee754/-/ieee754-1.2.1.tgz", + "integrity": "sha512-dcyqhDvX1C46lXZcVqCpK+FtMRQVdIMN6/Df5js2zouUsqG7I6sFxitIC+7KYK29KdXOLHdu9zL4sFnoVQnqaA==", + "dev": true + }, + "ignore": { + "version": "5.1.8", + "resolved": "https://registry.npmjs.org/ignore/-/ignore-5.1.8.tgz", + "integrity": "sha512-BMpfD7PpiETpBl/A6S498BaIJ6Y/ABT93ETbby2fP00v4EbvPBXWEoaR1UBPKs3iR53pJY7EtZk5KACI57i1Uw==", + "dev": true + }, + "import-cwd": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/import-cwd/-/import-cwd-3.0.0.tgz", + "integrity": "sha512-4pnzH16plW+hgvRECbDWpQl3cqtvSofHWh44met7ESfZ8UZOWWddm8hEyDTqREJ9RbYHY8gi8DqmaelApoOGMg==", + "dev": true, + "requires": { + "import-from": "^3.0.0" + } + }, + "import-fresh": { + "version": "3.3.0", + "resolved": "https://registry.npmjs.org/import-fresh/-/import-fresh-3.3.0.tgz", + "integrity": "sha512-veYYhQa+D1QBKznvhUHxb8faxlrwUnxseDAbAp457E0wLNio2bOSKnjYDhMj+YiAq61xrMGhQk9iXVk5FzgQMw==", + "dev": true, + "requires": { + "parent-module": "^1.0.0", + "resolve-from": "^4.0.0" + }, + "dependencies": { + "resolve-from": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/resolve-from/-/resolve-from-4.0.0.tgz", + "integrity": "sha512-pb/MYmXstAkysRFx8piNI1tGFNQIFA3vkE3Gq4EuA1dF6gHp/+vgZqsCGJapvy8N3Q+4o7FwvquPJcnZ7RYy4g==", + "dev": true + } + } + }, + "import-from": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/import-from/-/import-from-3.0.0.tgz", + "integrity": "sha512-CiuXOFFSzkU5x/CR0+z7T91Iht4CXgfCxVOFRhh2Zyhg5wOpWvvDLQUsWl+gcN+QscYBjez8hDCt85O7RLDttQ==", + "dev": true, + "requires": { + "resolve-from": "^5.0.0" + } + }, + "import-lazy": { + "version": "2.1.0", + "resolved": "https://registry.npmjs.org/import-lazy/-/import-lazy-2.1.0.tgz", + "integrity": "sha1-BWmOPUXIjo1+nZLLBYTnfwlvPkM=", + "dev": true + }, + "imurmurhash": { + "version": "0.1.4", + "resolved": "https://registry.npmjs.org/imurmurhash/-/imurmurhash-0.1.4.tgz", + "integrity": "sha1-khi5srkoojixPcT7a21XbyMUU+o=", + "dev": true + }, + "indent-string": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/indent-string/-/indent-string-4.0.0.tgz", + "integrity": "sha512-EdDDZu4A2OyIK7Lr/2zG+w5jmbuk1DVBnEwREQvBzspBJkCEbRa8GxU1lghYcaGJCnRWibjDXlq779X1/y5xwg==", + "dev": true + }, + "inflight": { + "version": "1.0.6", + "resolved": "https://registry.npmjs.org/inflight/-/inflight-1.0.6.tgz", + "integrity": "sha1-Sb1jMdfQLQwJvJEKEHW6gWW1bfk=", + "dev": true, + "requires": { + "once": "^1.3.0", + "wrappy": "1" + } + }, + "inherits": { + "version": "2.0.4", + "resolved": "https://registry.npmjs.org/inherits/-/inherits-2.0.4.tgz", + "integrity": "sha512-k/vGaX4/Yla3WzyMCvTQOXYeIHvqOKtnqBduzTHpzpQZzAskKMhZ2K+EnBiSM9zGSoIFeMpXKxa4dYeZIQqewQ==", + "dev": true + }, + "ini": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/ini/-/ini-2.0.0.tgz", + "integrity": "sha512-7PnF4oN3CvZF23ADhA5wRaYEQpJ8qygSkbtTXWBeXWXmEVRXK+1ITciHWwHhsjv1TmW0MgacIv6hEi5pX5NQdA==", + "dev": true + }, + "inquirer": { + "version": "8.1.2", + "resolved": "https://registry.npmjs.org/inquirer/-/inquirer-8.1.2.tgz", + "integrity": "sha512-DHLKJwLPNgkfwNmsuEUKSejJFbkv0FMO9SMiQbjI3n5NQuCrSIBqP66ggqyz2a6t2qEolKrMjhQ3+W/xXgUQ+Q==", + "dev": true, + "requires": { + "ansi-escapes": "^4.2.1", + "chalk": "^4.1.1", + "cli-cursor": "^3.1.0", + "cli-width": "^3.0.0", + "external-editor": "^3.0.3", + "figures": "^3.0.0", + "lodash": "^4.17.21", + "mute-stream": "0.0.8", + "ora": "^5.3.0", + "run-async": "^2.4.0", + "rxjs": "^7.2.0", + "string-width": "^4.1.0", + "strip-ansi": "^6.0.0", + "through": "^2.3.6" + } + }, + "interpret": { + "version": "1.4.0", + "resolved": "https://registry.npmjs.org/interpret/-/interpret-1.4.0.tgz", + "integrity": "sha512-agE4QfB2Lkp9uICn7BAqoscw4SZP9kTE2hxiFI3jBPmXJfdqiahTbUuKGsMoN2GtqL9AxhYioAcVvgsb1HvRbA==", + "dev": true + }, + "is-arrayish": { + "version": "0.2.1", + "resolved": "https://registry.npmjs.org/is-arrayish/-/is-arrayish-0.2.1.tgz", + "integrity": "sha1-d8mYQFJ6qOyxqLppe4BkWnqSap0=", + "dev": true + }, + "is-binary-path": { + "version": "2.1.0", + "resolved": "https://registry.npmjs.org/is-binary-path/-/is-binary-path-2.1.0.tgz", + "integrity": "sha512-ZMERYes6pDydyuGidse7OsHxtbI7WVeUEozgR/g7rd0xUimYNlvZRE/K2MgZTjWy725IfelLeVcEM97mmtRGXw==", + "dev": true, + "requires": { + "binary-extensions": "^2.0.0" + } + }, + "is-ci": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/is-ci/-/is-ci-3.0.0.tgz", + "integrity": "sha512-kDXyttuLeslKAHYL/K28F2YkM3x5jvFPEw3yXbRptXydjD9rpLEz+C5K5iutY9ZiUu6AP41JdvRQwF4Iqs4ZCQ==", + "dev": true, + "requires": { + "ci-info": "^3.1.1" + } + }, + "is-core-module": { + "version": "2.6.0", + "resolved": "https://registry.npmjs.org/is-core-module/-/is-core-module-2.6.0.tgz", + "integrity": "sha512-wShG8vs60jKfPWpF2KZRaAtvt3a20OAn7+IJ6hLPECpSABLcKtFKTTI4ZtH5QcBruBHlq+WsdHWyz0BCZW7svQ==", + "dev": true, + "requires": { + "has": "^1.0.3" + } + }, + "is-docker": { + "version": "2.2.1", + "resolved": "https://registry.npmjs.org/is-docker/-/is-docker-2.2.1.tgz", + "integrity": "sha512-F+i2BKsFrH66iaUFc0woD8sLy8getkwTwtOBjvs56Cx4CgJDeKQeqfz8wAYiSb8JOprWhHH5p77PbmYCvvUuXQ==", + "dev": true + }, + "is-extglob": { + "version": "2.1.1", + "resolved": "https://registry.npmjs.org/is-extglob/-/is-extglob-2.1.1.tgz", + "integrity": "sha1-qIwCU1eR8C7TfHahueqXc8gz+MI=", + "dev": true + }, + "is-fullwidth-code-point": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/is-fullwidth-code-point/-/is-fullwidth-code-point-3.0.0.tgz", + "integrity": "sha512-zymm5+u+sCsSWyD9qNaejV3DFvhCKclKdizYaJUuHA83RLjb7nSuGnddCHGv0hk+KY7BMAlsWeK4Ueg6EV6XQg==", + "dev": true + }, + "is-glob": { + "version": "4.0.1", + "resolved": "https://registry.npmjs.org/is-glob/-/is-glob-4.0.1.tgz", + "integrity": "sha512-5G0tKtBTFImOqDnLB2hG6Bp2qcKEFduo4tZu9MT/H6NQv/ghhy30o55ufafxJ/LdH79LLs2Kfrn85TLKyA7BUg==", + "dev": true, + "requires": { + "is-extglob": "^2.1.1" + } + }, + "is-installed-globally": { + "version": "0.4.0", + "resolved": "https://registry.npmjs.org/is-installed-globally/-/is-installed-globally-0.4.0.tgz", + "integrity": "sha512-iwGqO3J21aaSkC7jWnHP/difazwS7SFeIqxv6wEtLU8Y5KlzFTjyqcSIT0d8s4+dDhKytsk9PJZ2BkS5eZwQRQ==", + "dev": true, + "requires": { + "global-dirs": "^3.0.0", + "is-path-inside": "^3.0.2" + } + }, + "is-interactive": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/is-interactive/-/is-interactive-1.0.0.tgz", + "integrity": "sha512-2HvIEKRoqS62guEC+qBjpvRubdX910WCMuJTZ+I9yvqKU2/12eSL549HMwtabb4oupdj2sMP50k+XJfB/8JE6w==", + "dev": true + }, + "is-npm": { + "version": "5.0.0", + "resolved": "https://registry.npmjs.org/is-npm/-/is-npm-5.0.0.tgz", + "integrity": "sha512-WW/rQLOazUq+ST/bCAVBp/2oMERWLsR7OrKyt052dNDk4DHcDE0/7QSXITlmi+VBcV13DfIbysG3tZJm5RfdBA==", + "dev": true + }, + "is-number": { + "version": "7.0.0", + "resolved": "https://registry.npmjs.org/is-number/-/is-number-7.0.0.tgz", + "integrity": "sha512-41Cifkg6e8TylSpdtTpeLVMqvSBEVzTttHvERD741+pnZ8ANv0004MRL43QKPDlK9cGvNp6NZWZUBlbGXYxxng==", + "dev": true + }, + "is-obj": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/is-obj/-/is-obj-2.0.0.tgz", + "integrity": "sha512-drqDG3cbczxxEJRoOXcOjtdp1J/lyp1mNn0xaznRs8+muBhgQcrnbspox5X5fOw0HnMnbfDzvnEMEtqDEJEo8w==", + "dev": true + }, + "is-path-inside": { + "version": "3.0.3", + "resolved": "https://registry.npmjs.org/is-path-inside/-/is-path-inside-3.0.3.tgz", + "integrity": "sha512-Fd4gABb+ycGAmKou8eMftCupSir5lRxqf4aD/vd0cD2qc4HL07OjCeuHMr8Ro4CoMaeCKDB0/ECBOVWjTwUvPQ==", + "dev": true + }, + "is-plain-obj": { + "version": "2.1.0", + "resolved": "https://registry.npmjs.org/is-plain-obj/-/is-plain-obj-2.1.0.tgz", + "integrity": "sha512-YWnfyRwxL/+SsrWYfOpUtz5b3YD+nyfkHvjbcanzk8zgyO4ASD67uVMRt8k5bM4lLMDnXfriRhOpemw+NfT1eA==", + "dev": true + }, + "is-plain-object": { + "version": "5.0.0", + "resolved": "https://registry.npmjs.org/is-plain-object/-/is-plain-object-5.0.0.tgz", + "integrity": "sha512-VRSzKkbMm5jMDoKLbltAkFQ5Qr7VDiTFGXxYFXXowVj387GeGNOCsOH6Msy00SGZ3Fp84b1Naa1psqgcCIEP5Q==", + "dev": true + }, + "is-ssh": { + "version": "1.3.3", + "resolved": "https://registry.npmjs.org/is-ssh/-/is-ssh-1.3.3.tgz", + "integrity": "sha512-NKzJmQzJfEEma3w5cJNcUMxoXfDjz0Zj0eyCalHn2E6VOwlzjZo0yuO2fcBSf8zhFuVCL/82/r5gRcoi6aEPVQ==", + "dev": true, + "requires": { + "protocols": "^1.1.0" + } + }, + "is-stream": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/is-stream/-/is-stream-2.0.1.tgz", + "integrity": "sha512-hFoiJiTl63nn+kstHGBtewWSKnQLpyb155KHheA1l39uvtO9nWIop1p3udqPcUd/xbF1VLMO4n7OI6p7RbngDg==", + "dev": true + }, + "is-typedarray": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/is-typedarray/-/is-typedarray-1.0.0.tgz", + "integrity": "sha1-5HnICFjfDBsR3dppQPlgEfzaSpo=", + "dev": true + }, + "is-unicode-supported": { + "version": "0.1.0", + "resolved": "https://registry.npmjs.org/is-unicode-supported/-/is-unicode-supported-0.1.0.tgz", + "integrity": "sha512-knxG2q4UC3u8stRGyAVJCOdxFmv5DZiRcdlIaAQXAbSfJya+OhopNotLQrstBhququ4ZpuKbDc/8S6mgXgPFPw==", + "dev": true + }, + "is-windows": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/is-windows/-/is-windows-1.0.2.tgz", + "integrity": "sha512-eXK1UInq2bPmjyX6e3VHIzMLobc4J94i4AWn+Hpq3OU5KkrRC96OAcR3PRJ/pGu6m8TRnBHP9dkXQVsT/COVIA==", + "dev": true + }, + "is-wsl": { + "version": "2.2.0", + "resolved": "https://registry.npmjs.org/is-wsl/-/is-wsl-2.2.0.tgz", + "integrity": "sha512-fKzAra0rGJUUBwGBgNkHZuToZcn+TtXHpeCgmkMJMMYx1sQDYaCSyjJBSCa2nH1DGm7s3n1oBnohoVTBaN7Lww==", + "dev": true, + "requires": { + "is-docker": "^2.0.0" + } + }, + "is-yarn-global": { + "version": "0.3.0", + "resolved": "https://registry.npmjs.org/is-yarn-global/-/is-yarn-global-0.3.0.tgz", + "integrity": "sha512-VjSeb/lHmkoyd8ryPVIKvOCn4D1koMqY+vqyjjUfc3xyKtP4dYOxM44sZrnqQSzSds3xyOrUTLTC9LVCVgLngw==", + "dev": true + }, + "isarray": { + "version": "0.0.1", + "resolved": "https://registry.npmjs.org/isarray/-/isarray-0.0.1.tgz", + "integrity": "sha1-ihis/Kmo9Bd+Cav8YDiTmwXR7t8=", + "dev": true + }, + "isexe": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/isexe/-/isexe-2.0.0.tgz", + "integrity": "sha1-6PvzdNxVb/iUehDcsFctYz8s+hA=", + "dev": true + }, + "istanbul-lib-coverage": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/istanbul-lib-coverage/-/istanbul-lib-coverage-3.0.0.tgz", + "integrity": "sha512-UiUIqxMgRDET6eR+o5HbfRYP1l0hqkWOs7vNxC/mggutCMUIhWMm8gAHb8tHlyfD3/l6rlgNA5cKdDzEAf6hEg==", + "dev": true + }, + "istanbul-lib-hook": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/istanbul-lib-hook/-/istanbul-lib-hook-3.0.0.tgz", + "integrity": "sha512-Pt/uge1Q9s+5VAZ+pCo16TYMWPBIl+oaNIjgLQxcX0itS6ueeaA+pEfThZpH8WxhFgCiEb8sAJY6MdUKgiIWaQ==", + "dev": true, + "requires": { + "append-transform": "^2.0.0" + } + }, + "istanbul-lib-instrument": { + "version": "4.0.3", + "resolved": "https://registry.npmjs.org/istanbul-lib-instrument/-/istanbul-lib-instrument-4.0.3.tgz", + "integrity": "sha512-BXgQl9kf4WTCPCCpmFGoJkz/+uhvm7h7PFKUYxh7qarQd3ER33vHG//qaE8eN25l07YqZPpHXU9I09l/RD5aGQ==", + "dev": true, + "requires": { + "@babel/core": "^7.7.5", + "@istanbuljs/schema": "^0.1.2", + "istanbul-lib-coverage": "^3.0.0", + "semver": "^6.3.0" + } + }, + "istanbul-lib-processinfo": { + "version": "2.0.2", + "resolved": "https://registry.npmjs.org/istanbul-lib-processinfo/-/istanbul-lib-processinfo-2.0.2.tgz", + "integrity": "sha512-kOwpa7z9hme+IBPZMzQ5vdQj8srYgAtaRqeI48NGmAQ+/5yKiHLV0QbYqQpxsdEF0+w14SoB8YbnHKcXE2KnYw==", + "dev": true, + "requires": { + "archy": "^1.0.0", + "cross-spawn": "^7.0.0", + "istanbul-lib-coverage": "^3.0.0-alpha.1", + "make-dir": "^3.0.0", + "p-map": "^3.0.0", + "rimraf": "^3.0.0", + "uuid": "^3.3.3" + } + }, + "istanbul-lib-report": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/istanbul-lib-report/-/istanbul-lib-report-3.0.0.tgz", + "integrity": "sha512-wcdi+uAKzfiGT2abPpKZ0hSU1rGQjUQnLvtY5MpQ7QCTahD3VODhcu4wcfY1YtkGaDD5yuydOLINXsfbus9ROw==", + "dev": true, + "requires": { + "istanbul-lib-coverage": "^3.0.0", + "make-dir": "^3.0.0", + "supports-color": "^7.1.0" + }, + "dependencies": { + "supports-color": { + "version": "7.2.0", + "resolved": "https://registry.npmjs.org/supports-color/-/supports-color-7.2.0.tgz", + "integrity": "sha512-qpCAvRl9stuOHveKsn7HncJRvv501qIacKzQlO/+Lwxc9+0q2wLyv4Dfvt80/DPn2pqOBsJdDiogXGR9+OvwRw==", + "dev": true, + "requires": { + "has-flag": "^4.0.0" + } + } + } + }, + "istanbul-lib-source-maps": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/istanbul-lib-source-maps/-/istanbul-lib-source-maps-4.0.0.tgz", + "integrity": "sha512-c16LpFRkR8vQXyHZ5nLpY35JZtzj1PQY1iZmesUbf1FZHbIupcWfjgOXBY9YHkLEQ6puz1u4Dgj6qmU/DisrZg==", + "dev": true, + "requires": { + "debug": "^4.1.1", + "istanbul-lib-coverage": "^3.0.0", + "source-map": "^0.6.1" + }, + "dependencies": { + "source-map": { + "version": "0.6.1", + "resolved": "https://registry.npmjs.org/source-map/-/source-map-0.6.1.tgz", + "integrity": "sha512-UjgapumWlbMhkBgzT7Ykc5YXUT46F0iKu8SGXq0bcwP5dz/h0Plj6enJqjz1Zbq2l5WaqYnrVbwWOWMyF3F47g==", + "dev": true + } + } + }, + "istanbul-reports": { + "version": "3.0.2", + "resolved": "https://registry.npmjs.org/istanbul-reports/-/istanbul-reports-3.0.2.tgz", + "integrity": "sha512-9tZvz7AiR3PEDNGiV9vIouQ/EAcqMXFmkcA1CDFTwOB98OZVDL0PH9glHotf5Ugp6GCOTypfzGWI/OqjWNCRUw==", + "dev": true, + "requires": { + "html-escaper": "^2.0.0", + "istanbul-lib-report": "^3.0.0" + } + }, + "js-tokens": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/js-tokens/-/js-tokens-4.0.0.tgz", + "integrity": "sha512-RdJUflcE3cUzKiMqQgsCu06FPu9UdIJO0beYbPhHN4k6apgJtifcoCtT9bcxOpYBtpD2kCM6Sbzg4CausW/PKQ==", + "dev": true + }, + "js-yaml": { + "version": "4.1.0", + "resolved": "https://registry.npmjs.org/js-yaml/-/js-yaml-4.1.0.tgz", + "integrity": "sha512-wpxZs9NoxZaJESJGIZTyDEaYpl0FKSA+FB9aJiyemKhMwkxQg63h4T1KJgUGHpTqPDNRcmmYLugrRjJlBtWvRA==", + "dev": true, + "requires": { + "argparse": "^2.0.1" + } + }, + "jsesc": { + "version": "2.5.2", + "resolved": "https://registry.npmjs.org/jsesc/-/jsesc-2.5.2.tgz", + "integrity": "sha512-OYu7XEzjkCQ3C5Ps3QIZsQfNpqoJyZZA99wd9aWd05NCtC5pWOkShK2mkL6HXQR6/Cy2lbNdPlZBpuQHXE63gA==", + "dev": true + }, + "json-buffer": { + "version": "3.0.1", + "resolved": "https://registry.npmjs.org/json-buffer/-/json-buffer-3.0.1.tgz", + "integrity": "sha512-4bV5BfR2mqfQTJm+V5tPPdf+ZpuhiIvTuAB5g8kcrXOZpTT/QwwVRWBywX1ozr6lEuPdbHxwaJlm9G6mI2sfSQ==", + "dev": true + }, + "json-parse-even-better-errors": { + "version": "2.3.1", + "resolved": "https://registry.npmjs.org/json-parse-even-better-errors/-/json-parse-even-better-errors-2.3.1.tgz", + "integrity": "sha512-xyFwyhro/JEof6Ghe2iz2NcXoj2sloNsWr/XsERDK/oiPCfaNhl5ONfp+jQdAZRQQ0IJWNzH9zIZF7li91kh2w==", + "dev": true + }, + "json5": { + "version": "2.2.0", + "resolved": "https://registry.npmjs.org/json5/-/json5-2.2.0.tgz", + "integrity": "sha512-f+8cldu7X/y7RAJurMEJmdoKXGB/X550w2Nr3tTbezL6RwEE/iMcm+tZnXeoZtKuOq6ft8+CqzEkrIgx1fPoQA==", + "dev": true, + "requires": { + "minimist": "^1.2.5" + } + }, + "just-extend": { + "version": "4.2.1", + "resolved": "https://registry.npmjs.org/just-extend/-/just-extend-4.2.1.tgz", + "integrity": "sha512-g3UB796vUFIY90VIv/WX3L2c8CS2MdWUww3CNrYmqza1Fg0DURc2K/O4YrnklBdQarSJ/y8JnJYDGc+1iumQjg==", + "dev": true + }, + "keyv": { + "version": "4.0.3", + "resolved": "https://registry.npmjs.org/keyv/-/keyv-4.0.3.tgz", + "integrity": "sha512-zdGa2TOpSZPq5mU6iowDARnMBZgtCqJ11dJROFi6tg6kTn4nuUdU09lFyLFSaHrWqpIJ+EBq4E8/Dc0Vx5vLdA==", + "dev": true, + "requires": { + "json-buffer": "3.0.1" + } + }, + "latest-version": { + "version": "5.1.0", + "resolved": "https://registry.npmjs.org/latest-version/-/latest-version-5.1.0.tgz", + "integrity": "sha512-weT+r0kTkRQdCdYCNtkMwWXQTMEswKrFBkm4ckQOMVhhqhIMI1UT2hMj+1iigIhgSZm5gTmrRXBNoGUgaTY1xA==", + "dev": true, + "requires": { + "package-json": "^6.3.0" + } + }, + "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=", + "dev": true + }, + "locate-path": { + "version": "6.0.0", + "resolved": "https://registry.npmjs.org/locate-path/-/locate-path-6.0.0.tgz", + "integrity": "sha512-iPZK6eYjbxRu3uB4/WZ3EsEIMJFMqAoopl3R+zuq0UjcAm/MO6KCweDgPfP3elTztoKP3KtnVHxTn2NHBSDVUw==", + "dev": true, + "requires": { + "p-locate": "^5.0.0" + } + }, + "lodash": { + "version": "4.17.21", + "resolved": "https://registry.npmjs.org/lodash/-/lodash-4.17.21.tgz", + "integrity": "sha512-v2kDEe57lecTulaDIuNTPy3Ry4gLGJ6Z1O3vE1krgXZNrsQ+LFTGHVxVjcXPs17LhbZVGedAJv8XZ1tvj5FvSg==", + "dev": true + }, + "lodash.flattendeep": { + "version": "4.4.0", + "resolved": "https://registry.npmjs.org/lodash.flattendeep/-/lodash.flattendeep-4.4.0.tgz", + "integrity": "sha1-+wMJF/hqMTTlvJvsDWngAT3f7bI=", + "dev": true + }, + "lodash.get": { + "version": "4.4.2", + "resolved": "https://registry.npmjs.org/lodash.get/-/lodash.get-4.4.2.tgz", + "integrity": "sha1-LRd/ZS+jHpObRDjVNBSZ36OCXpk=", + "dev": true + }, + "log-symbols": { + "version": "4.1.0", + "resolved": "https://registry.npmjs.org/log-symbols/-/log-symbols-4.1.0.tgz", + "integrity": "sha512-8XPvpAA8uyhfteu8pIvQxpJZ7SYYdpUivZpGy6sFsBuKRY/7rQGavedeB8aK+Zkyq6upMFVL/9AW6vOYzfRyLg==", + "dev": true, + "requires": { + "chalk": "^4.1.0", + "is-unicode-supported": "^0.1.0" + } + }, + "lowercase-keys": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/lowercase-keys/-/lowercase-keys-2.0.0.tgz", + "integrity": "sha512-tqNXrS78oMOE73NMxK4EMLQsQowWf8jKooH9g7xPavRT706R6bkQJ6DY2Te7QukaZsulxa30wQ7bk0pm4XiHmA==", + "dev": true + }, + "lru-cache": { + "version": "5.1.1", + "resolved": "https://registry.npmjs.org/lru-cache/-/lru-cache-5.1.1.tgz", + "integrity": "sha512-KpNARQA3Iwv+jTA0utUVVbrh+Jlrr1Fv0e56GGzAFOXN7dk/FviaDW8LHmK52DlcH4WP2n6gI8vN1aesBFgo9w==", + "dev": true, + "requires": { + "yallist": "^3.0.2" + }, + "dependencies": { + "yallist": { + "version": "3.1.1", + "resolved": "https://registry.npmjs.org/yallist/-/yallist-3.1.1.tgz", + "integrity": "sha512-a4UGQaWPH59mOXUYnAG2ewncQS4i4F43Tv3JoAM+s2VDAmS9NsK8GpDMLrCHPksFT7h3K6TOoUNn2pb7RoXx4g==", + "dev": true + } + } + }, + "lunr": { + "version": "2.3.9", + "resolved": "https://registry.npmjs.org/lunr/-/lunr-2.3.9.tgz", + "integrity": "sha512-zTU3DaZaF3Rt9rhN3uBMGQD3dD2/vFQqnvZCDv4dl5iOzq2IZQqTxu90r4E5J+nP70J3ilqVCrbho2eWaeW8Ow==", + "dev": true + }, + "macos-release": { + "version": "2.5.0", + "resolved": "https://registry.npmjs.org/macos-release/-/macos-release-2.5.0.tgz", + "integrity": "sha512-EIgv+QZ9r+814gjJj0Bt5vSLJLzswGmSUbUpbi9AIr/fsN2IWFBl2NucV9PAiek+U1STK468tEkxmVYUtuAN3g==", + "dev": true + }, + "make-dir": { + "version": "3.1.0", + "resolved": "https://registry.npmjs.org/make-dir/-/make-dir-3.1.0.tgz", + "integrity": "sha512-g3FeP20LNwhALb/6Cz6Dd4F2ngze0jz7tbzrD2wAV+o9FeNHe4rL+yK2md0J/fiSf1sa1ADhXqi5+oVwOM/eGw==", + "dev": true, + "requires": { + "semver": "^6.0.0" + } + }, + "make-error": { + "version": "1.3.6", + "resolved": "https://registry.npmjs.org/make-error/-/make-error-1.3.6.tgz", + "integrity": "sha512-s8UhlNe7vPKomQhC1qFelMokr/Sc3AgNbso3n74mVPA5LTZwkB9NlXf4XPamLxJE8h0gh73rM94xvwRT2CVInw==", + "dev": true + }, + "marked": { + "version": "3.0.2", + "resolved": "https://registry.npmjs.org/marked/-/marked-3.0.2.tgz", + "integrity": "sha512-TMJQQ79Z0e3rJYazY0tIoMsFzteUGw9fB3FD+gzuIT3zLuG9L9ckIvUfF51apdJkcqc208jJN2KbtPbOvXtbjA==", + "dev": true + }, + "merge-stream": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/merge-stream/-/merge-stream-2.0.0.tgz", + "integrity": "sha512-abv/qOcuPfk3URPfDzmZU1LKmuw8kT+0nIHvKrKgFrwifol/doWcdA4ZqsWQ8ENrFKkd67Mfpo/LovbIUsbt3w==", + "dev": true + }, + "merge2": { + "version": "1.4.1", + "resolved": "https://registry.npmjs.org/merge2/-/merge2-1.4.1.tgz", + "integrity": "sha512-8q7VEgMJW4J8tcfVPy8g09NcQwZdbwFEqhe/WZkoIzjn/3TGDwtOCYtXGxA3O8tPzpczCCDgv+P2P5y00ZJOOg==", + "dev": true + }, + "micromatch": { + "version": "4.0.4", + "resolved": "https://registry.npmjs.org/micromatch/-/micromatch-4.0.4.tgz", + "integrity": "sha512-pRmzw/XUcwXGpD9aI9q/0XOwLNygjETJ8y0ao0wdqprrzDa4YnxLcz7fQRZr8voh8V10kGhABbNcHVk5wHgWwg==", + "dev": true, + "requires": { + "braces": "^3.0.1", + "picomatch": "^2.2.3" + } + }, + "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==", + "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==", + "dev": true, + "requires": { + "mime-db": "1.49.0" + } + }, + "mimic-fn": { + "version": "2.1.0", + "resolved": "https://registry.npmjs.org/mimic-fn/-/mimic-fn-2.1.0.tgz", + "integrity": "sha512-OqbOk5oEQeAZ8WXWydlu9HJjz9WVdEIvamMCcXmuqUYjTknH/sqsWvhQ3vgwKFRR1HpjvNBKQ37nbJgYzGqGcg==", + "dev": true + }, + "mimic-response": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/mimic-response/-/mimic-response-1.0.1.tgz", + "integrity": "sha512-j5EctnkH7amfV/q5Hgmoal1g2QHFJRraOtmx0JpIqkxhBhI/lJSl1nMpQ45hVarwNETOoWEimndZ4QK0RHxuxQ==", + "dev": true + }, + "minimatch": { + "version": "3.0.4", + "resolved": "https://registry.npmjs.org/minimatch/-/minimatch-3.0.4.tgz", + "integrity": "sha512-yJHVQEhyqPLUTgt9B83PXu6W3rx4MvvHvSUvToogpwoGDOUQ+yDrR0HRot+yOCdCO7u4hX3pWft6kWBBcqh0UA==", + "dev": true, + "requires": { + "brace-expansion": "^1.1.7" + } + }, + "minimist": { + "version": "1.2.5", + "resolved": "https://registry.npmjs.org/minimist/-/minimist-1.2.5.tgz", + "integrity": "sha512-FM9nNUYrRBAELZQT3xeZQ7fmMOBg6nWNmJKTcgsJeaLstP/UODVpGsr5OhXhhXg6f+qtJ8uiZ+PUxkDWcgIXLw==", + "dev": true + }, + "mocha": { + "version": "9.1.1", + "resolved": "https://registry.npmjs.org/mocha/-/mocha-9.1.1.tgz", + "integrity": "sha512-0wE74YMgOkCgBUj8VyIDwmLUjTsS13WV1Pg7l0SHea2qzZzlq7MDnfbPsHKcELBRk3+izEVkRofjmClpycudCA==", + "dev": true, + "requires": { + "@ungap/promise-all-settled": "1.1.2", + "ansi-colors": "4.1.1", + "browser-stdout": "1.3.1", + "chokidar": "3.5.2", + "debug": "4.3.1", + "diff": "5.0.0", + "escape-string-regexp": "4.0.0", + "find-up": "5.0.0", + "glob": "7.1.7", + "growl": "1.10.5", + "he": "1.2.0", + "js-yaml": "4.1.0", + "log-symbols": "4.1.0", + "minimatch": "3.0.4", + "ms": "2.1.3", + "nanoid": "3.1.23", + "serialize-javascript": "6.0.0", + "strip-json-comments": "3.1.1", + "supports-color": "8.1.1", + "which": "2.0.2", + "wide-align": "1.1.3", + "workerpool": "6.1.5", + "yargs": "16.2.0", + "yargs-parser": "20.2.4", + "yargs-unparser": "2.0.0" + } + }, + "ms": { + "version": "2.1.3", + "resolved": "https://registry.npmjs.org/ms/-/ms-2.1.3.tgz", + "integrity": "sha512-6FlzubTLZG3J2a/NVCAleEhjzq5oxgHyaCU9yYXvcLsvoVaHJq/s5xXI6/XXP6tz7R9xAOtHnSO/tXtF3WRTlA==", + "dev": true + }, + "mute-stream": { + "version": "0.0.8", + "resolved": "https://registry.npmjs.org/mute-stream/-/mute-stream-0.0.8.tgz", + "integrity": "sha512-nnbWWOkoWyUsTjKrhgD0dcz22mdkSnpYqbEjIm2nhwhuxlSkpywJmBo8h0ZqJdkp73mb90SssHkN4rsRaBAfAA==", + "dev": true + }, + "nanoid": { + "version": "3.1.23", + "resolved": "https://registry.npmjs.org/nanoid/-/nanoid-3.1.23.tgz", + "integrity": "sha512-FiB0kzdP0FFVGDKlRLEQ1BgDzU87dy5NnzjeW9YZNt+/c3+q82EQDUwniSAUxp/F0gFNI1ZhKU1FqYsMuqZVnw==", + "dev": true + }, + "neo-async": { + "version": "2.6.2", + "resolved": "https://registry.npmjs.org/neo-async/-/neo-async-2.6.2.tgz", + "integrity": "sha512-Yd3UES5mWCSqR+qNT93S3UoYUkqAZ9lLg8a7g9rimsWmYGK8cVToA4/sF3RrshdyV3sAGMXVUmpMYOw+dLpOuw==", + "dev": true + }, + "new-github-release-url": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/new-github-release-url/-/new-github-release-url-1.0.0.tgz", + "integrity": "sha512-dle7yf655IMjyFUqn6Nxkb18r4AOAkzRcgcZv6WZ0IqrOH4QCEZ8Sm6I7XX21zvHdBeeMeTkhR9qT2Z0EJDx6A==", + "dev": true, + "requires": { + "type-fest": "^0.4.1" + }, + "dependencies": { + "type-fest": { + "version": "0.4.1", + "resolved": "https://registry.npmjs.org/type-fest/-/type-fest-0.4.1.tgz", + "integrity": "sha512-IwzA/LSfD2vC1/YDYMv/zHP4rDF1usCwllsDpbolT3D4fUepIO7f9K70jjmUewU/LmGUKJcwcVtDCpnKk4BPMw==", + "dev": true + } + } + }, + "nise": { + "version": "5.1.0", + "resolved": "https://registry.npmjs.org/nise/-/nise-5.1.0.tgz", + "integrity": "sha512-W5WlHu+wvo3PaKLsJJkgPup2LrsXCcm7AWwyNZkUnn5rwPkuPBi3Iwk5SQtN0mv+K65k7nKKjwNQ30wg3wLAQQ==", + "dev": true, + "requires": { + "@sinonjs/commons": "^1.7.0", + "@sinonjs/fake-timers": "^7.0.4", + "@sinonjs/text-encoding": "^0.7.1", + "just-extend": "^4.0.2", + "path-to-regexp": "^1.7.0" + } + }, + "node-fetch": { + "version": "2.6.1", + "resolved": "https://registry.npmjs.org/node-fetch/-/node-fetch-2.6.1.tgz", + "integrity": "sha512-V4aYg89jEoVRxRb2fJdAg8FHvI7cEyYdVAh94HH0UIK8oJxUfkjlDQN9RbMx+bEjP7+ggMiFRprSti032Oipxw==", + "dev": true + }, + "node-preload": { + "version": "0.2.1", + "resolved": "https://registry.npmjs.org/node-preload/-/node-preload-0.2.1.tgz", + "integrity": "sha512-RM5oyBy45cLEoHqCeh+MNuFAxO0vTFBLskvQbOKnEE7YTTSN4tbN8QWDIPQ6L+WvKsB/qLEGpYe2ZZ9d4W9OIQ==", + "dev": true, + "requires": { + "process-on-spawn": "^1.0.0" + } + }, + "node-releases": { + "version": "1.1.75", + "resolved": "https://registry.npmjs.org/node-releases/-/node-releases-1.1.75.tgz", + "integrity": "sha512-Qe5OUajvqrqDSy6wrWFmMwfJ0jVgwiw4T3KqmbTcZ62qW0gQkheXYhcFM1+lOVcGUoRxcEcfyvFMAnDgaF1VWw==", + "dev": true + }, + "normalize-path": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/normalize-path/-/normalize-path-3.0.0.tgz", + "integrity": "sha512-6eZs5Ls3WtCisHWp9S2GUy8dqkpGi4BVSz3GaqiE6ezub0512ESztXUwUB6C6IKbQkY2Pnb/mD4WYojCRwcwLA==", + "dev": true + }, + "normalize-url": { + "version": "6.1.0", + "resolved": "https://registry.npmjs.org/normalize-url/-/normalize-url-6.1.0.tgz", + "integrity": "sha512-DlL+XwOy3NxAQ8xuC0okPgK46iuVNAK01YN7RueYBqqFeGsBjV9XmCAzAdgt+667bCl5kPh9EqKKDwnaPG1I7A==", + "dev": true + }, + "npm-run-path": { + "version": "4.0.1", + "resolved": "https://registry.npmjs.org/npm-run-path/-/npm-run-path-4.0.1.tgz", + "integrity": "sha512-S48WzZW777zhNIrn7gxOlISNAqi9ZC/uQFnRdbeIHhZhCA6UqpkOT8T1G7BvfdgP4Er8gF4sUbaS0i7QvIfCWw==", + "dev": true, + "requires": { + "path-key": "^3.0.0" + } + }, + "nyc": { + "version": "15.1.0", + "resolved": "https://registry.npmjs.org/nyc/-/nyc-15.1.0.tgz", + "integrity": "sha512-jMW04n9SxKdKi1ZMGhvUTHBN0EICCRkHemEoE5jm6mTYcqcdas0ATzgUgejlQUHMvpnOZqGB5Xxsv9KxJW1j8A==", + "dev": true, + "requires": { + "@istanbuljs/load-nyc-config": "^1.0.0", + "@istanbuljs/schema": "^0.1.2", + "caching-transform": "^4.0.0", + "convert-source-map": "^1.7.0", + "decamelize": "^1.2.0", + "find-cache-dir": "^3.2.0", + "find-up": "^4.1.0", + "foreground-child": "^2.0.0", + "get-package-type": "^0.1.0", + "glob": "^7.1.6", + "istanbul-lib-coverage": "^3.0.0", + "istanbul-lib-hook": "^3.0.0", + "istanbul-lib-instrument": "^4.0.0", + "istanbul-lib-processinfo": "^2.0.2", + "istanbul-lib-report": "^3.0.0", + "istanbul-lib-source-maps": "^4.0.0", + "istanbul-reports": "^3.0.2", + "make-dir": "^3.0.0", + "node-preload": "^0.2.1", + "p-map": "^3.0.0", + "process-on-spawn": "^1.0.0", + "resolve-from": "^5.0.0", + "rimraf": "^3.0.0", + "signal-exit": "^3.0.2", + "spawn-wrap": "^2.0.0", + "test-exclude": "^6.0.0", + "yargs": "^15.0.2" + }, + "dependencies": { + "cliui": { + "version": "6.0.0", + "resolved": "https://registry.npmjs.org/cliui/-/cliui-6.0.0.tgz", + "integrity": "sha512-t6wbgtoCXvAzst7QgXxJYqPt0usEfbgQdftEPbLL/cvv6HPE5VgvqCuAIDR0NgU52ds6rFwqrgakNLrHEjCbrQ==", + "dev": true, + "requires": { + "string-width": "^4.2.0", + "strip-ansi": "^6.0.0", + "wrap-ansi": "^6.2.0" + } + }, + "find-up": { + "version": "4.1.0", + "resolved": "https://registry.npmjs.org/find-up/-/find-up-4.1.0.tgz", + "integrity": "sha512-PpOwAdQ/YlXQ2vj8a3h8IipDuYRi3wceVQQGYWxNINccq40Anw7BlsEXCMbt1Zt+OLA6Fq9suIpIWD0OsnISlw==", + "dev": true, + "requires": { + "locate-path": "^5.0.0", + "path-exists": "^4.0.0" + } + }, + "locate-path": { + "version": "5.0.0", + "resolved": "https://registry.npmjs.org/locate-path/-/locate-path-5.0.0.tgz", + "integrity": "sha512-t7hw9pI+WvuwNJXwk5zVHpyhIqzg2qTlklJOf0mVxGSbe3Fp2VieZcduNYjaLDoy6p9uGpQEGWG87WpMKlNq8g==", + "dev": true, + "requires": { + "p-locate": "^4.1.0" + } + }, + "p-limit": { + "version": "2.3.0", + "resolved": "https://registry.npmjs.org/p-limit/-/p-limit-2.3.0.tgz", + "integrity": "sha512-//88mFWSJx8lxCzwdAABTJL2MyWB12+eIY7MDL2SqLmAkeKU9qxRvWuSyTjm3FUmpBEMuFfckAIqEaVGUDxb6w==", + "dev": true, + "requires": { + "p-try": "^2.0.0" + } + }, + "p-locate": { + "version": "4.1.0", + "resolved": "https://registry.npmjs.org/p-locate/-/p-locate-4.1.0.tgz", + "integrity": "sha512-R79ZZ/0wAxKGu3oYMlz8jy/kbhsNrS7SKZ7PxEHBgJ5+F2mtFW2fK2cOtBh1cHYkQsbzFV7I+EoRKe6Yt0oK7A==", + "dev": true, + "requires": { + "p-limit": "^2.2.0" + } + }, + "wrap-ansi": { + "version": "6.2.0", + "resolved": "https://registry.npmjs.org/wrap-ansi/-/wrap-ansi-6.2.0.tgz", + "integrity": "sha512-r6lPcBGxZXlIcymEu7InxDMhdW0KDxpLgoFLcguasxCaJ/SOIZwINatK9KY/tf+ZrlywOKU0UDj3ATXUBfxJXA==", + "dev": true, + "requires": { + "ansi-styles": "^4.0.0", + "string-width": "^4.1.0", + "strip-ansi": "^6.0.0" + } + }, + "y18n": { + "version": "4.0.3", + "resolved": "https://registry.npmjs.org/y18n/-/y18n-4.0.3.tgz", + "integrity": "sha512-JKhqTOwSrqNA1NY5lSztJ1GrBiUodLMmIZuLiDaMRJ+itFd+ABVE8XBjOvIWL+rSqNDC74LCSFmlb/U4UZ4hJQ==", + "dev": true + }, + "yargs": { + "version": "15.4.1", + "resolved": "https://registry.npmjs.org/yargs/-/yargs-15.4.1.tgz", + "integrity": "sha512-aePbxDmcYW++PaqBsJ+HYUFwCdv4LVvdnhBy78E57PIor8/OVvhMrADFFEDh8DHDFRv/O9i3lPhsENjO7QX0+A==", + "dev": true, + "requires": { + "cliui": "^6.0.0", + "decamelize": "^1.2.0", + "find-up": "^4.1.0", + "get-caller-file": "^2.0.1", + "require-directory": "^2.1.1", + "require-main-filename": "^2.0.0", + "set-blocking": "^2.0.0", + "string-width": "^4.2.0", + "which-module": "^2.0.0", + "y18n": "^4.0.0", + "yargs-parser": "^18.1.2" + } + }, + "yargs-parser": { + "version": "18.1.3", + "resolved": "https://registry.npmjs.org/yargs-parser/-/yargs-parser-18.1.3.tgz", + "integrity": "sha512-o50j0JeToy/4K6OZcaQmW6lyXXKhq7csREXcDwk2omFPJEwUNOVtJKvmDr9EI1fAJZUyZcRF7kxGBWmRXudrCQ==", + "dev": true, + "requires": { + "camelcase": "^5.0.0", + "decamelize": "^1.2.0" + } + } + } + }, + "object-inspect": { + "version": "1.11.0", + "resolved": "https://registry.npmjs.org/object-inspect/-/object-inspect-1.11.0.tgz", + "integrity": "sha512-jp7ikS6Sd3GxQfZJPyH3cjcbJF6GZPClgdV+EFygjFLQ5FmW/dRUnTd9PQ9k0JhoNDabWFbpF1yCdSWCC6gexg==", + "dev": true + }, + "once": { + "version": "1.4.0", + "resolved": "https://registry.npmjs.org/once/-/once-1.4.0.tgz", + "integrity": "sha1-WDsap3WWHUsROsF9nFC6753Xa9E=", + "dev": true, + "requires": { + "wrappy": "1" + } + }, + "onetime": { + "version": "5.1.2", + "resolved": "https://registry.npmjs.org/onetime/-/onetime-5.1.2.tgz", + "integrity": "sha512-kbpaSSGJTWdAY5KPVeMOKXSrPtr8C8C7wodJbcsd51jRnmD+GZu8Y0VoU6Dm5Z4vWr0Ig/1NKuWRKf7j5aaYSg==", + "dev": true, + "requires": { + "mimic-fn": "^2.1.0" + } + }, + "onigasm": { + "version": "2.2.5", + "resolved": "https://registry.npmjs.org/onigasm/-/onigasm-2.2.5.tgz", + "integrity": "sha512-F+th54mPc0l1lp1ZcFMyL/jTs2Tlq4SqIHKIXGZOR/VkHkF9A7Fr5rRr5+ZG/lWeRsyrClLYRq7s/yFQ/XhWCA==", + "dev": true, + "requires": { + "lru-cache": "^5.1.1" + } + }, + "open": { + "version": "7.4.2", + "resolved": "https://registry.npmjs.org/open/-/open-7.4.2.tgz", + "integrity": "sha512-MVHddDVweXZF3awtlAS+6pgKLlm/JgxZ90+/NBurBoQctVOOB/zDdVjcyPzQ+0laDGbsWgrRkflI65sQeOgT9Q==", + "dev": true, + "requires": { + "is-docker": "^2.0.0", + "is-wsl": "^2.1.1" + } + }, + "ora": { + "version": "5.4.1", + "resolved": "https://registry.npmjs.org/ora/-/ora-5.4.1.tgz", + "integrity": "sha512-5b6Y85tPxZZ7QytO+BQzysW31HJku27cRIlkbAXaNx+BdcVi+LlRFmVXzeF6a7JCwJpyw5c4b+YSVImQIrBpuQ==", + "dev": true, + "requires": { + "bl": "^4.1.0", + "chalk": "^4.1.0", + "cli-cursor": "^3.1.0", + "cli-spinners": "^2.5.0", + "is-interactive": "^1.0.0", + "is-unicode-supported": "^0.1.0", + "log-symbols": "^4.1.0", + "strip-ansi": "^6.0.0", + "wcwidth": "^1.0.1" + } + }, + "os-name": { + "version": "4.0.1", + "resolved": "https://registry.npmjs.org/os-name/-/os-name-4.0.1.tgz", + "integrity": "sha512-xl9MAoU97MH1Xt5K9ERft2YfCAoaO6msy1OBA0ozxEC0x0TmIoE6K3QvgJMMZA9yKGLmHXNY/YZoDbiGDj4zYw==", + "dev": true, + "requires": { + "macos-release": "^2.5.0", + "windows-release": "^4.0.0" + } + }, + "os-tmpdir": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/os-tmpdir/-/os-tmpdir-1.0.2.tgz", + "integrity": "sha1-u+Z0BseaqFxc/sdm/lc0VV36EnQ=", + "dev": true + }, + "p-cancelable": { + "version": "2.1.1", + "resolved": "https://registry.npmjs.org/p-cancelable/-/p-cancelable-2.1.1.tgz", + "integrity": "sha512-BZOr3nRQHOntUjTrH8+Lh54smKHoHyur8We1V8DSMVrl5A2malOOwuJRnKRDjSnkoeBh4at6BwEnb5I7Jl31wg==", + "dev": true + }, + "p-limit": { + "version": "3.1.0", + "resolved": "https://registry.npmjs.org/p-limit/-/p-limit-3.1.0.tgz", + "integrity": "sha512-TYOanM3wGwNGsZN2cVTYPArw454xnXj5qmWF1bEoAc4+cU/ol7GVh7odevjp1FNHduHc3KZMcFduxU5Xc6uJRQ==", + "dev": true, + "requires": { + "yocto-queue": "^0.1.0" + } + }, + "p-locate": { + "version": "5.0.0", + "resolved": "https://registry.npmjs.org/p-locate/-/p-locate-5.0.0.tgz", + "integrity": "sha512-LaNjtRWUBY++zB5nE/NwcaoMylSPk+S+ZHNB1TzdbMJMny6dynpAGt7X/tl/QYq3TIeE6nxHppbo2LGymrG5Pw==", + "dev": true, + "requires": { + "p-limit": "^3.0.2" + } + }, + "p-map": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/p-map/-/p-map-3.0.0.tgz", + "integrity": "sha512-d3qXVTF/s+W+CdJ5A29wywV2n8CQQYahlgz2bFiA+4eVNJbHJodPZ+/gXwPGh0bOqA+j8S+6+ckmvLGPk1QpxQ==", + "dev": true, + "requires": { + "aggregate-error": "^3.0.0" + } + }, + "p-try": { + "version": "2.2.0", + "resolved": "https://registry.npmjs.org/p-try/-/p-try-2.2.0.tgz", + "integrity": "sha512-R4nPAVTAU0B9D35/Gk3uJf/7XYbQcyohSKdvAxIRSNghFl4e71hVoGnBNQz9cWaXxO2I10KTC+3jMdvvoKw6dQ==", + "dev": true + }, + "package-hash": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/package-hash/-/package-hash-4.0.0.tgz", + "integrity": "sha512-whdkPIooSu/bASggZ96BWVvZTRMOFxnyUG5PnTSGKoJE2gd5mbVNmR2Nj20QFzxYYgAXpoqC+AiXzl+UMRh7zQ==", + "dev": true, + "requires": { + "graceful-fs": "^4.1.15", + "hasha": "^5.0.0", + "lodash.flattendeep": "^4.4.0", + "release-zalgo": "^1.0.0" + } + }, + "package-json": { + "version": "6.5.0", + "resolved": "https://registry.npmjs.org/package-json/-/package-json-6.5.0.tgz", + "integrity": "sha512-k3bdm2n25tkyxcjSKzB5x8kfVxlMdgsbPr0GkZcwHsLpba6cBjqCt1KlcChKEvxHIcTB1FVMuwoijZ26xex5MQ==", + "dev": true, + "requires": { + "got": "^9.6.0", + "registry-auth-token": "^4.0.0", + "registry-url": "^5.0.0", + "semver": "^6.2.0" + }, + "dependencies": { + "@sindresorhus/is": { + "version": "0.14.0", + "resolved": "https://registry.npmjs.org/@sindresorhus/is/-/is-0.14.0.tgz", + "integrity": "sha512-9NET910DNaIPngYnLLPeg+Ogzqsi9uM4mSboU5y6p8S5DzMTVEsJZrawi+BoDNUVBa2DhJqQYUFvMDfgU062LQ==", + "dev": true + }, + "@szmarczak/http-timer": { + "version": "1.1.2", + "resolved": "https://registry.npmjs.org/@szmarczak/http-timer/-/http-timer-1.1.2.tgz", + "integrity": "sha512-XIB2XbzHTN6ieIjfIMV9hlVcfPU26s2vafYWQcZHWXHOxiaRZYEDKEwdl129Zyg50+foYV2jCgtrqSA6qNuNSA==", + "dev": true, + "requires": { + "defer-to-connect": "^1.0.1" + } + }, + "cacheable-request": { + "version": "6.1.0", + "resolved": "https://registry.npmjs.org/cacheable-request/-/cacheable-request-6.1.0.tgz", + "integrity": "sha512-Oj3cAGPCqOZX7Rz64Uny2GYAZNliQSqfbePrgAQ1wKAihYmCUnraBtJtKcGR4xz7wF+LoJC+ssFZvv5BgF9Igg==", + "dev": true, + "requires": { + "clone-response": "^1.0.2", + "get-stream": "^5.1.0", + "http-cache-semantics": "^4.0.0", + "keyv": "^3.0.0", + "lowercase-keys": "^2.0.0", + "normalize-url": "^4.1.0", + "responselike": "^1.0.2" + }, + "dependencies": { + "get-stream": { + "version": "5.2.0", + "resolved": "https://registry.npmjs.org/get-stream/-/get-stream-5.2.0.tgz", + "integrity": "sha512-nBF+F1rAZVCu/p7rjzgA+Yb4lfYXrpl7a6VmJrU8wF9I1CKvP/QwPNZHnOlwbTkY6dvtFIzFMSyQXbLoTQPRpA==", + "dev": true, + "requires": { + "pump": "^3.0.0" + } + } + } + }, + "decompress-response": { + "version": "3.3.0", + "resolved": "https://registry.npmjs.org/decompress-response/-/decompress-response-3.3.0.tgz", + "integrity": "sha1-gKTdMjdIOEv6JICDYirt7Jgq3/M=", + "dev": true, + "requires": { + "mimic-response": "^1.0.0" + } + }, + "defer-to-connect": { + "version": "1.1.3", + "resolved": "https://registry.npmjs.org/defer-to-connect/-/defer-to-connect-1.1.3.tgz", + "integrity": "sha512-0ISdNousHvZT2EiFlZeZAHBUvSxmKswVCEf8hW7KWgG4a8MVEu/3Vb6uWYozkjylyCxe0JBIiRB1jV45S70WVQ==", + "dev": true + }, + "get-stream": { + "version": "4.1.0", + "resolved": "https://registry.npmjs.org/get-stream/-/get-stream-4.1.0.tgz", + "integrity": "sha512-GMat4EJ5161kIy2HevLlr4luNjBgvmj413KaQA7jt4V8B4RDsfpHk7WQ9GVqfYyyx8OS/L66Kox+rJRNklLK7w==", + "dev": true, + "requires": { + "pump": "^3.0.0" + } + }, + "got": { + "version": "9.6.0", + "resolved": "https://registry.npmjs.org/got/-/got-9.6.0.tgz", + "integrity": "sha512-R7eWptXuGYxwijs0eV+v3o6+XH1IqVK8dJOEecQfTmkncw9AV4dcw/Dhxi8MdlqPthxxpZyizMzyg8RTmEsG+Q==", + "dev": true, + "requires": { + "@sindresorhus/is": "^0.14.0", + "@szmarczak/http-timer": "^1.1.2", + "cacheable-request": "^6.0.0", + "decompress-response": "^3.3.0", + "duplexer3": "^0.1.4", + "get-stream": "^4.1.0", + "lowercase-keys": "^1.0.1", + "mimic-response": "^1.0.1", + "p-cancelable": "^1.0.0", + "to-readable-stream": "^1.0.0", + "url-parse-lax": "^3.0.0" + }, + "dependencies": { + "lowercase-keys": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/lowercase-keys/-/lowercase-keys-1.0.1.tgz", + "integrity": "sha512-G2Lj61tXDnVFFOi8VZds+SoQjtQC3dgokKdDG2mTm1tx4m50NUHBOZSBwQQHyy0V12A0JTG4icfZQH+xPyh8VA==", + "dev": true + } + } + }, + "json-buffer": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/json-buffer/-/json-buffer-3.0.0.tgz", + "integrity": "sha1-Wx85evx11ne96Lz8Dkfh+aPZqJg=", + "dev": true + }, + "keyv": { + "version": "3.1.0", + "resolved": "https://registry.npmjs.org/keyv/-/keyv-3.1.0.tgz", + "integrity": "sha512-9ykJ/46SN/9KPM/sichzQ7OvXyGDYKGTaDlKMGCAlg2UK8KRy4jb0d8sFc+0Tt0YYnThq8X2RZgCg74RPxgcVA==", + "dev": true, + "requires": { + "json-buffer": "3.0.0" + } + }, + "normalize-url": { + "version": "4.5.1", + "resolved": "https://registry.npmjs.org/normalize-url/-/normalize-url-4.5.1.tgz", + "integrity": "sha512-9UZCFRHQdNrfTpGg8+1INIg93B6zE0aXMVFkw1WFwvO4SlZywU6aLg5Of0Ap/PgcbSw4LNxvMWXMeugwMCX0AA==", + "dev": true + }, + "p-cancelable": { + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/p-cancelable/-/p-cancelable-1.1.0.tgz", + "integrity": "sha512-s73XxOZ4zpt1edZYZzvhqFa6uvQc1vwUa0K0BdtIZgQMAJj9IbebH+JkgKZc9h+B05PKHLOTl4ajG1BmNrVZlw==", + "dev": true + }, + "responselike": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/responselike/-/responselike-1.0.2.tgz", + "integrity": "sha1-kYcg7ztjHFZCvgaPFa3lpG9Loec=", + "dev": true, + "requires": { + "lowercase-keys": "^1.0.0" + }, + "dependencies": { + "lowercase-keys": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/lowercase-keys/-/lowercase-keys-1.0.1.tgz", + "integrity": "sha512-G2Lj61tXDnVFFOi8VZds+SoQjtQC3dgokKdDG2mTm1tx4m50NUHBOZSBwQQHyy0V12A0JTG4icfZQH+xPyh8VA==", + "dev": true + } + } + } + } + }, + "parent-module": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/parent-module/-/parent-module-1.0.1.tgz", + "integrity": "sha512-GQ2EWRpQV8/o+Aw8YqtfZZPfNRWZYkbidE9k5rpl/hC3vtHHBfGm2Ifi6qWV+coDGkrUKZAxE3Lot5kcsRlh+g==", + "dev": true, + "requires": { + "callsites": "^3.0.0" + } + }, + "parse-json": { + "version": "5.2.0", + "resolved": "https://registry.npmjs.org/parse-json/-/parse-json-5.2.0.tgz", + "integrity": "sha512-ayCKvm/phCGxOkYRSCM82iDwct8/EonSEgCSxWxD7ve6jHggsFl4fZVQBPRNgQoKiuV/odhFrGzQXZwbifC8Rg==", + "dev": true, + "requires": { + "@babel/code-frame": "^7.0.0", + "error-ex": "^1.3.1", + "json-parse-even-better-errors": "^2.3.0", + "lines-and-columns": "^1.1.6" + } + }, + "parse-path": { + "version": "4.0.3", + "resolved": "https://registry.npmjs.org/parse-path/-/parse-path-4.0.3.tgz", + "integrity": "sha512-9Cepbp2asKnWTJ9x2kpw6Fe8y9JDbqwahGCTvklzd/cEq5C5JC59x2Xb0Kx+x0QZ8bvNquGO8/BWP0cwBHzSAA==", + "dev": true, + "requires": { + "is-ssh": "^1.3.0", + "protocols": "^1.4.0", + "qs": "^6.9.4", + "query-string": "^6.13.8" + } + }, + "parse-url": { + "version": "6.0.0", + "resolved": "https://registry.npmjs.org/parse-url/-/parse-url-6.0.0.tgz", + "integrity": "sha512-cYyojeX7yIIwuJzledIHeLUBVJ6COVLeT4eF+2P6aKVzwvgKQPndCBv3+yQ7pcWjqToYwaligxzSYNNmGoMAvw==", + "dev": true, + "requires": { + "is-ssh": "^1.3.0", + "normalize-url": "^6.1.0", + "parse-path": "^4.0.0", + "protocols": "^1.4.0" + } + }, + "path-exists": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/path-exists/-/path-exists-4.0.0.tgz", + "integrity": "sha512-ak9Qy5Q7jYb2Wwcey5Fpvg2KoAc/ZIhLSLOSBmRmygPsGwkVVt0fZa0qrtMz+m6tJTAHfZQ8FnmB4MG4LWy7/w==", + "dev": true + }, + "path-is-absolute": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/path-is-absolute/-/path-is-absolute-1.0.1.tgz", + "integrity": "sha1-F0uSaHNVNP+8es5r9TpanhtcX18=", + "dev": true + }, + "path-key": { + "version": "3.1.1", + "resolved": "https://registry.npmjs.org/path-key/-/path-key-3.1.1.tgz", + "integrity": "sha512-ojmeN0qd+y0jszEtoY48r0Peq5dwMEkIlCOu6Q5f41lfkswXuKtYrhgoTpLnyIcHm24Uhqx+5Tqm2InSwLhE6Q==", + "dev": true + }, + "path-parse": { + "version": "1.0.7", + "resolved": "https://registry.npmjs.org/path-parse/-/path-parse-1.0.7.tgz", + "integrity": "sha512-LDJzPVEEEPR+y48z93A0Ed0yXb8pAByGWo/k5YYdYgpY2/2EsOsksJrq7lOHxryrVOn1ejG6oAp8ahvOIQD8sw==", + "dev": true + }, + "path-to-regexp": { + "version": "1.8.0", + "resolved": "https://registry.npmjs.org/path-to-regexp/-/path-to-regexp-1.8.0.tgz", + "integrity": "sha512-n43JRhlUKUAlibEJhPeir1ncUID16QnEjNpwzNdO3Lm4ywrBpBZ5oLD0I6br9evr1Y9JTqwRtAh7JLoOzAQdVA==", + "dev": true, + "requires": { + "isarray": "0.0.1" + } + }, + "path-type": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/path-type/-/path-type-4.0.0.tgz", + "integrity": "sha512-gDKb8aZMDeD/tZWs9P6+q0J9Mwkdl6xMV8TjnGP3qJVJ06bdMgkbBlLU8IdfOsIsFz2BW1rNVT3XuNEl8zPAvw==", + "dev": true + }, + "picomatch": { + "version": "2.3.0", + "resolved": "https://registry.npmjs.org/picomatch/-/picomatch-2.3.0.tgz", + "integrity": "sha512-lY1Q/PiJGC2zOv/z391WOTD+Z02bCgsFfvxoXXf6h7kv9o+WmsmzYqrAwY63sNgOxE4xEdq0WyUnXfKeBrSvYw==", + "dev": true + }, + "pkg-dir": { + "version": "4.2.0", + "resolved": "https://registry.npmjs.org/pkg-dir/-/pkg-dir-4.2.0.tgz", + "integrity": "sha512-HRDzbaKjC+AOWVXxAU/x54COGeIv9eb+6CkDSQoNTt4XyWoIJvuPsXizxu/Fr23EiekbtZwmh1IcIG/l/a10GQ==", + "dev": true, + "requires": { + "find-up": "^4.0.0" + }, + "dependencies": { + "find-up": { + "version": "4.1.0", + "resolved": "https://registry.npmjs.org/find-up/-/find-up-4.1.0.tgz", + "integrity": "sha512-PpOwAdQ/YlXQ2vj8a3h8IipDuYRi3wceVQQGYWxNINccq40Anw7BlsEXCMbt1Zt+OLA6Fq9suIpIWD0OsnISlw==", + "dev": true, + "requires": { + "locate-path": "^5.0.0", + "path-exists": "^4.0.0" + } + }, + "locate-path": { + "version": "5.0.0", + "resolved": "https://registry.npmjs.org/locate-path/-/locate-path-5.0.0.tgz", + "integrity": "sha512-t7hw9pI+WvuwNJXwk5zVHpyhIqzg2qTlklJOf0mVxGSbe3Fp2VieZcduNYjaLDoy6p9uGpQEGWG87WpMKlNq8g==", + "dev": true, + "requires": { + "p-locate": "^4.1.0" + } + }, + "p-limit": { + "version": "2.3.0", + "resolved": "https://registry.npmjs.org/p-limit/-/p-limit-2.3.0.tgz", + "integrity": "sha512-//88mFWSJx8lxCzwdAABTJL2MyWB12+eIY7MDL2SqLmAkeKU9qxRvWuSyTjm3FUmpBEMuFfckAIqEaVGUDxb6w==", + "dev": true, + "requires": { + "p-try": "^2.0.0" + } + }, + "p-locate": { + "version": "4.1.0", + "resolved": "https://registry.npmjs.org/p-locate/-/p-locate-4.1.0.tgz", + "integrity": "sha512-R79ZZ/0wAxKGu3oYMlz8jy/kbhsNrS7SKZ7PxEHBgJ5+F2mtFW2fK2cOtBh1cHYkQsbzFV7I+EoRKe6Yt0oK7A==", + "dev": true, + "requires": { + "p-limit": "^2.2.0" + } + } + } + }, + "prepend-http": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/prepend-http/-/prepend-http-2.0.0.tgz", + "integrity": "sha1-6SQ0v6XqjBn0HN/UAddBo8gZ2Jc=", + "dev": true + }, + "process-on-spawn": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/process-on-spawn/-/process-on-spawn-1.0.0.tgz", + "integrity": "sha512-1WsPDsUSMmZH5LeMLegqkPDrsGgsWwk1Exipy2hvB0o/F0ASzbpIctSCcZIK1ykJvtTJULEH+20WOFjMvGnCTg==", + "dev": true, + "requires": { + "fromentries": "^1.2.0" + } + }, + "progress": { + "version": "2.0.3", + "resolved": "https://registry.npmjs.org/progress/-/progress-2.0.3.tgz", + "integrity": "sha512-7PiHtLll5LdnKIMw100I+8xJXR5gW2QwWYkT6iJva0bXitZKa/XMrSbdmg3r2Xnaidz9Qumd0VPaMrZlF9V9sA==", + "dev": true + }, + "protocols": { + "version": "1.4.8", + "resolved": "https://registry.npmjs.org/protocols/-/protocols-1.4.8.tgz", + "integrity": "sha512-IgjKyaUSjsROSO8/D49Ab7hP8mJgTYcqApOqdPhLoPxAplXmkp+zRvsrSQjFn5by0rhm4VH0GAUELIPpx7B1yg==", + "dev": true + }, + "pump": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/pump/-/pump-3.0.0.tgz", + "integrity": "sha512-LwZy+p3SFs1Pytd/jYct4wpv49HiYCqd9Rlc5ZVdk0V+8Yzv6jR5Blk3TRmPL1ft69TxP0IMZGJ+WPFU2BFhww==", + "dev": true, + "requires": { + "end-of-stream": "^1.1.0", + "once": "^1.3.1" + } + }, + "pupa": { + "version": "2.1.1", + "resolved": "https://registry.npmjs.org/pupa/-/pupa-2.1.1.tgz", + "integrity": "sha512-l1jNAspIBSFqbT+y+5FosojNpVpF94nlI+wDUpqP9enwOTfHx9f0gh5nB96vl+6yTpsJsypeNrwfzPrKuHB41A==", + "dev": true, + "requires": { + "escape-goat": "^2.0.0" + } + }, + "qs": { + "version": "6.10.1", + "resolved": "https://registry.npmjs.org/qs/-/qs-6.10.1.tgz", + "integrity": "sha512-M528Hph6wsSVOBiYUnGf+K/7w0hNshs/duGsNXPUCLH5XAqjEtiPGwNONLV0tBH8NoGb0mvD5JubnUTrujKDTg==", + "dev": true, + "requires": { + "side-channel": "^1.0.4" + } + }, + "query-string": { + "version": "6.14.1", + "resolved": "https://registry.npmjs.org/query-string/-/query-string-6.14.1.tgz", + "integrity": "sha512-XDxAeVmpfu1/6IjyT/gXHOl+S0vQ9owggJ30hhWKdHAsNPOcasn5o9BW0eejZqL2e4vMjhAxoW3jVHcD6mbcYw==", + "dev": true, + "requires": { + "decode-uri-component": "^0.2.0", + "filter-obj": "^1.1.0", + "split-on-first": "^1.0.0", + "strict-uri-encode": "^2.0.0" + } + }, + "queue-microtask": { + "version": "1.2.3", + "resolved": "https://registry.npmjs.org/queue-microtask/-/queue-microtask-1.2.3.tgz", + "integrity": "sha512-NuaNSa6flKT5JaSYQzJok04JzTL1CA6aGhv5rfLW3PgqA+M2ChpZQnAC8h8i4ZFkBS8X5RqkDBHA7r4hej3K9A==", + "dev": true + }, + "quick-lru": { + "version": "5.1.1", + "resolved": "https://registry.npmjs.org/quick-lru/-/quick-lru-5.1.1.tgz", + "integrity": "sha512-WuyALRjWPDGtt/wzJiadO5AXY+8hZ80hVpe6MyivgraREW751X3SbhRvG3eLKOYN+8VEvqLcf3wdnt44Z4S4SA==", + "dev": true + }, + "randombytes": { + "version": "2.1.0", + "resolved": "https://registry.npmjs.org/randombytes/-/randombytes-2.1.0.tgz", + "integrity": "sha512-vYl3iOX+4CKUWuxGi9Ukhie6fsqXqS9FE2Zaic4tNFD2N2QQaXOMFbuKK4QmDHC0JO6B1Zp41J0LpT0oR68amQ==", + "dev": true, + "requires": { + "safe-buffer": "^5.1.0" + } + }, + "rc": { + "version": "1.2.8", + "resolved": "https://registry.npmjs.org/rc/-/rc-1.2.8.tgz", + "integrity": "sha512-y3bGgqKj3QBdxLbLkomlohkvsA8gdAiUQlSBJnBhfn+BPxg4bc62d8TcBW15wavDfgexCgccckhcZvywyQYPOw==", + "dev": true, + "requires": { + "deep-extend": "^0.6.0", + "ini": "~1.3.0", + "minimist": "^1.2.0", + "strip-json-comments": "~2.0.1" + }, + "dependencies": { + "ini": { + "version": "1.3.8", + "resolved": "https://registry.npmjs.org/ini/-/ini-1.3.8.tgz", + "integrity": "sha512-JV/yugV2uzW5iMRSiZAyDtQd+nxtUnjeLt0acNdw98kKLrvuRVyB80tsREOE7yvGVgalhZ6RNXCmEHkUKBKxew==", + "dev": true + }, + "strip-json-comments": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/strip-json-comments/-/strip-json-comments-2.0.1.tgz", + "integrity": "sha1-PFMZQukIwml8DsNEhYwobHygpgo=", + "dev": true + } + } + }, + "readable-stream": { + "version": "3.6.0", + "resolved": "https://registry.npmjs.org/readable-stream/-/readable-stream-3.6.0.tgz", + "integrity": "sha512-BViHy7LKeTz4oNnkcLJ+lVSL6vpiFeX6/d3oSH8zCW7UxP2onchk+vTGB143xuFjHS3deTgkKoXXymXqymiIdA==", + "dev": true, + "requires": { + "inherits": "^2.0.3", + "string_decoder": "^1.1.1", + "util-deprecate": "^1.0.1" + } + }, + "readdirp": { + "version": "3.6.0", + "resolved": "https://registry.npmjs.org/readdirp/-/readdirp-3.6.0.tgz", + "integrity": "sha512-hOS089on8RduqdbhvQ5Z37A0ESjsqz6qnRcffsMU3495FuTdqSm+7bhJ29JvIOsBDEEnan5DPu9t3To9VRlMzA==", + "dev": true, + "requires": { + "picomatch": "^2.2.1" + } + }, + "rechoir": { + "version": "0.6.2", + "resolved": "https://registry.npmjs.org/rechoir/-/rechoir-0.6.2.tgz", + "integrity": "sha1-hSBLVNuoLVdC4oyWdW70OvUOM4Q=", + "dev": true, + "requires": { + "resolve": "^1.1.6" + } + }, + "redis-errors": { + "version": "1.2.0", + "resolved": "https://registry.npmjs.org/redis-errors/-/redis-errors-1.2.0.tgz", + "integrity": "sha1-62LSrbFeTq9GEMBK/hUpOEJQq60=" + }, + "redis-parser": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/redis-parser/-/redis-parser-3.0.0.tgz", + "integrity": "sha1-tm2CjNyv5rS4pCin3vTGvKwxyLQ=", + "requires": { + "redis-errors": "^1.0.0" + } + }, + "registry-auth-token": { + "version": "4.2.1", + "resolved": "https://registry.npmjs.org/registry-auth-token/-/registry-auth-token-4.2.1.tgz", + "integrity": "sha512-6gkSb4U6aWJB4SF2ZvLb76yCBjcvufXBqvvEx1HbmKPkutswjW1xNVRY0+daljIYRbogN7O0etYSlbiaEQyMyw==", + "dev": true, + "requires": { + "rc": "^1.2.8" + } + }, + "registry-url": { + "version": "5.1.0", + "resolved": "https://registry.npmjs.org/registry-url/-/registry-url-5.1.0.tgz", + "integrity": "sha512-8acYXXTI0AkQv6RAOjE3vOaIXZkT9wo4LOFbBKYQEEnnMNBpKqdUrI6S4NT0KPIo/WVvJ5tE/X5LF/TQUf0ekw==", + "dev": true, + "requires": { + "rc": "^1.2.8" + } + }, + "release-it": { + "version": "14.11.5", + "resolved": "https://registry.npmjs.org/release-it/-/release-it-14.11.5.tgz", + "integrity": "sha512-9BaPdq7ZKOwtzz3p1mRhg/tOH/cT/y2tUnPYzUwQiVdj42JaGI1Vo2l3WbgK8ICsbFyrhc0tri1+iqI8OvkI1A==", + "dev": true, + "requires": { + "@iarna/toml": "2.2.5", + "@octokit/rest": "18.9.0", + "async-retry": "1.3.1", + "chalk": "4.1.2", + "cosmiconfig": "7.0.0", + "debug": "4.3.2", + "deprecated-obj": "2.0.0", + "execa": "5.1.1", + "form-data": "4.0.0", + "git-url-parse": "11.5.0", + "globby": "11.0.4", + "got": "11.8.2", + "import-cwd": "3.0.0", + "inquirer": "8.1.2", + "is-ci": "3.0.0", + "lodash": "4.17.21", + "mime-types": "2.1.32", + "new-github-release-url": "1.0.0", + "open": "7.4.2", + "ora": "5.4.1", + "os-name": "4.0.1", + "parse-json": "5.2.0", + "semver": "7.3.5", + "shelljs": "0.8.4", + "update-notifier": "5.1.0", + "url-join": "4.0.1", + "uuid": "8.3.2", + "yaml": "1.10.2", + "yargs-parser": "20.2.9" + }, + "dependencies": { + "debug": { + "version": "4.3.2", + "resolved": "https://registry.npmjs.org/debug/-/debug-4.3.2.tgz", + "integrity": "sha512-mOp8wKcvj7XxC78zLgw/ZA+6TSgkoE2C/ienthhRD298T7UNwAg9diBpLRxC0mOezLl4B0xV7M0cCO6P/O0Xhw==", + "dev": true, + "requires": { + "ms": "2.1.2" + } + }, + "lru-cache": { + "version": "6.0.0", + "resolved": "https://registry.npmjs.org/lru-cache/-/lru-cache-6.0.0.tgz", + "integrity": "sha512-Jo6dJ04CmSjuznwJSS3pUeWmd/H0ffTlkXXgwZi+eq1UCmqQwCh+eLsYOYCwY991i2Fah4h1BEMCx4qThGbsiA==", + "dev": true, + "requires": { + "yallist": "^4.0.0" + } + }, + "ms": { + "version": "2.1.2", + "resolved": "https://registry.npmjs.org/ms/-/ms-2.1.2.tgz", + "integrity": "sha512-sGkPx+VjMtmA6MX27oA4FBFELFCZZ4S4XqeGOXCv68tT+jb3vk/RyaKWP0PTKyWtmLSM0b+adUTEvbs1PEaH2w==", + "dev": true + }, + "semver": { + "version": "7.3.5", + "resolved": "https://registry.npmjs.org/semver/-/semver-7.3.5.tgz", + "integrity": "sha512-PoeGJYh8HK4BTO/a9Tf6ZG3veo/A7ZVsYrSA6J8ny9nb3B1VrpkuN+z9OE5wfE5p6H4LchYZsegiQgbJD94ZFQ==", + "dev": true, + "requires": { + "lru-cache": "^6.0.0" + } + }, + "uuid": { + "version": "8.3.2", + "resolved": "https://registry.npmjs.org/uuid/-/uuid-8.3.2.tgz", + "integrity": "sha512-+NYs2QeMWy+GWFOEm9xnn6HCDp0l7QBD7ml8zLUmJ+93Q5NF0NocErnwkTkXVFNiX3/fpC6afS8Dhb/gz7R7eg==", + "dev": true + }, + "yargs-parser": { + "version": "20.2.9", + "resolved": "https://registry.npmjs.org/yargs-parser/-/yargs-parser-20.2.9.tgz", + "integrity": "sha512-y11nGElTIV+CT3Zv9t7VKl+Q3hTQoT9a1Qzezhhl6Rp21gJ/IVTW7Z3y9EWXhuUBC2Shnf+DX0antecpAwSP8w==", + "dev": true + } + } + }, + "release-zalgo": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/release-zalgo/-/release-zalgo-1.0.0.tgz", + "integrity": "sha1-CXALflB0Mpc5Mw5TXFqQ+2eFFzA=", + "dev": true, + "requires": { + "es6-error": "^4.0.1" + } + }, + "require-directory": { + "version": "2.1.1", + "resolved": "https://registry.npmjs.org/require-directory/-/require-directory-2.1.1.tgz", + "integrity": "sha1-jGStX9MNqxyXbiNE/+f3kqam30I=", + "dev": true + }, + "require-main-filename": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/require-main-filename/-/require-main-filename-2.0.0.tgz", + "integrity": "sha512-NKN5kMDylKuldxYLSUfrbo5Tuzh4hd+2E8NPPX02mZtn1VuREQToYe/ZdlJy+J3uCpfaiGF05e7B8W0iXbQHmg==", + "dev": true + }, + "resolve": { + "version": "1.20.0", + "resolved": "https://registry.npmjs.org/resolve/-/resolve-1.20.0.tgz", + "integrity": "sha512-wENBPt4ySzg4ybFQW2TT1zMQucPK95HSh/nq2CFTZVOGut2+pQvSsgtda4d26YrYcr067wjbmzOG8byDPBX63A==", + "dev": true, + "requires": { + "is-core-module": "^2.2.0", + "path-parse": "^1.0.6" + } + }, + "resolve-alpn": { + "version": "1.2.1", + "resolved": "https://registry.npmjs.org/resolve-alpn/-/resolve-alpn-1.2.1.tgz", + "integrity": "sha512-0a1F4l73/ZFZOakJnQ3FvkJ2+gSTQWz/r2KE5OdDY0TxPm5h4GkqkWWfM47T7HsbnOtcJVEF4epCVy6u7Q3K+g==", + "dev": true + }, + "resolve-from": { + "version": "5.0.0", + "resolved": "https://registry.npmjs.org/resolve-from/-/resolve-from-5.0.0.tgz", + "integrity": "sha512-qYg9KP24dD5qka9J47d0aVky0N+b4fTU89LN9iDnjB5waksiC49rvMB0PrUJQGoTmH50XPiqOvAjDfaijGxYZw==", + "dev": true + }, + "responselike": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/responselike/-/responselike-2.0.0.tgz", + "integrity": "sha512-xH48u3FTB9VsZw7R+vvgaKeLKzT6jOogbQhEe/jewwnZgzPcnyWui2Av6JpoYZF/91uueC+lqhWqeURw5/qhCw==", + "dev": true, + "requires": { + "lowercase-keys": "^2.0.0" + } + }, + "restore-cursor": { + "version": "3.1.0", + "resolved": "https://registry.npmjs.org/restore-cursor/-/restore-cursor-3.1.0.tgz", + "integrity": "sha512-l+sSefzHpj5qimhFSE5a8nufZYAM3sBSVMAPtYkmC+4EH2anSGaEMXSD0izRQbu9nfyQ9y5JrVmp7E8oZrUjvA==", + "dev": true, + "requires": { + "onetime": "^5.1.0", + "signal-exit": "^3.0.2" + } + }, + "retry": { + "version": "0.12.0", + "resolved": "https://registry.npmjs.org/retry/-/retry-0.12.0.tgz", + "integrity": "sha1-G0KmJmoh8HQh0bC1S33BZ7AcATs=", + "dev": true + }, + "reusify": { + "version": "1.0.4", + "resolved": "https://registry.npmjs.org/reusify/-/reusify-1.0.4.tgz", + "integrity": "sha512-U9nH88a3fc/ekCF1l0/UP1IosiuIjyTh7hBvXVMHYgVcfGvt897Xguj2UOLDeI5BG2m7/uwyaLVT6fbtCwTyzw==", + "dev": true + }, + "rimraf": { + "version": "3.0.2", + "resolved": "https://registry.npmjs.org/rimraf/-/rimraf-3.0.2.tgz", + "integrity": "sha512-JZkJMZkAGFFPP2YqXZXPbMlMBgsxzE8ILs4lMIX/2o0L9UBw9O/Y3o6wFw/i9YLapcUJWwqbi3kdxIPdC62TIA==", + "dev": true, + "requires": { + "glob": "^7.1.3" + } + }, + "run-async": { + "version": "2.4.1", + "resolved": "https://registry.npmjs.org/run-async/-/run-async-2.4.1.tgz", + "integrity": "sha512-tvVnVv01b8c1RrA6Ep7JkStj85Guv/YrMcwqYQnwjsAS2cTmmPGBBjAjpCW7RrSodNSoE2/qg9O4bceNvUuDgQ==", + "dev": true + }, + "run-parallel": { + "version": "1.2.0", + "resolved": "https://registry.npmjs.org/run-parallel/-/run-parallel-1.2.0.tgz", + "integrity": "sha512-5l4VyZR86LZ/lDxZTR6jqL8AFE2S0IFLMP26AbjsLVADxHdhB/c0GUsH+y39UfCi3dzz8OlQuPmnaJOMoDHQBA==", + "dev": true, + "requires": { + "queue-microtask": "^1.2.2" + } + }, + "rxjs": { + "version": "7.3.0", + "resolved": "https://registry.npmjs.org/rxjs/-/rxjs-7.3.0.tgz", + "integrity": "sha512-p2yuGIg9S1epc3vrjKf6iVb3RCaAYjYskkO+jHIaV0IjOPlJop4UnodOoFb2xeNwlguqLYvGw1b1McillYb5Gw==", + "dev": true, + "requires": { + "tslib": "~2.1.0" + } + }, + "safe-buffer": { + "version": "5.1.2", + "resolved": "https://registry.npmjs.org/safe-buffer/-/safe-buffer-5.1.2.tgz", + "integrity": "sha512-Gd2UZBJDkXlY7GbJxfsE8/nvKkUEU1G38c1siN6QP6a9PT9MmHB8GnpscSmMJSoF8LOIrt8ud/wPtojys4G6+g==", + "dev": true + }, + "safer-buffer": { + "version": "2.1.2", + "resolved": "https://registry.npmjs.org/safer-buffer/-/safer-buffer-2.1.2.tgz", + "integrity": "sha512-YZo3K82SD7Riyi0E1EQPojLz7kpepnSQI9IyPbHHg1XXXevb5dJI7tpyN2ADxGcQbHG7vcyRHk0cbwqcQriUtg==", + "dev": true + }, + "semver": { + "version": "6.3.0", + "resolved": "https://registry.npmjs.org/semver/-/semver-6.3.0.tgz", + "integrity": "sha512-b39TBaTSfV6yBrapU89p5fKekE2m/NwnDocOVruQFS1/veMgdzuPcnOM34M6CwxW8jH/lxEa5rBoDeUwu5HHTw==", + "dev": true + }, + "semver-diff": { + "version": "3.1.1", + "resolved": "https://registry.npmjs.org/semver-diff/-/semver-diff-3.1.1.tgz", + "integrity": "sha512-GX0Ix/CJcHyB8c4ykpHGIAvLyOwOobtM/8d+TQkAd81/bEjgPHrfba41Vpesr7jX/t8Uh+R3EX9eAS5be+jQYg==", + "dev": true, + "requires": { + "semver": "^6.3.0" + } + }, + "serialize-javascript": { + "version": "6.0.0", + "resolved": "https://registry.npmjs.org/serialize-javascript/-/serialize-javascript-6.0.0.tgz", + "integrity": "sha512-Qr3TosvguFt8ePWqsvRfrKyQXIiW+nGbYpy8XK24NQHE83caxWt+mIymTT19DGFbNWNLfEwsrkSmN64lVWB9ag==", + "dev": true, + "requires": { + "randombytes": "^2.1.0" + } + }, + "set-blocking": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/set-blocking/-/set-blocking-2.0.0.tgz", + "integrity": "sha1-BF+XgtARrppoA93TgrJDkrPYkPc=", + "dev": true + }, + "shebang-command": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/shebang-command/-/shebang-command-2.0.0.tgz", + "integrity": "sha512-kHxr2zZpYtdmrN1qDjrrX/Z1rR1kG8Dx+gkpK1G4eXmvXswmcE1hTWBWYUzlraYw1/yZp6YuDY77YtvbN0dmDA==", + "dev": true, + "requires": { + "shebang-regex": "^3.0.0" + } + }, + "shebang-regex": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/shebang-regex/-/shebang-regex-3.0.0.tgz", + "integrity": "sha512-7++dFhtcx3353uBaq8DDR4NuxBetBzC7ZQOhmTQInHEd6bSrXdiEyzCvG07Z44UYdLShWUyXt5M/yhz8ekcb1A==", + "dev": true + }, + "shelljs": { + "version": "0.8.4", + "resolved": "https://registry.npmjs.org/shelljs/-/shelljs-0.8.4.tgz", + "integrity": "sha512-7gk3UZ9kOfPLIAbslLzyWeGiEqx9e3rxwZM0KE6EL8GlGwjym9Mrlx5/p33bWTu9YG6vcS4MBxYZDHYr5lr8BQ==", + "dev": true, + "requires": { + "glob": "^7.0.0", + "interpret": "^1.0.0", + "rechoir": "^0.6.2" + } + }, + "shiki": { + "version": "0.9.8", + "resolved": "https://registry.npmjs.org/shiki/-/shiki-0.9.8.tgz", + "integrity": "sha512-499zQUTjcNTVwwiaPrWldUTXIV3T9HZWxDwE82bY+9GE7P2uD6hpHUTXNbTof3iOG6WT+/062+OMbl0lDoG8WQ==", + "dev": true, + "requires": { + "json5": "^2.2.0", + "onigasm": "^2.2.5", + "vscode-textmate": "5.2.0" + } + }, + "side-channel": { + "version": "1.0.4", + "resolved": "https://registry.npmjs.org/side-channel/-/side-channel-1.0.4.tgz", + "integrity": "sha512-q5XPytqFEIKHkGdiMIrY10mvLRvnQh42/+GoBlFW3b2LXLE2xxJpZFdm94we0BaoV3RwJyGqg5wS7epxTv0Zvw==", + "dev": true, + "requires": { + "call-bind": "^1.0.0", + "get-intrinsic": "^1.0.2", + "object-inspect": "^1.9.0" + } + }, + "signal-exit": { + "version": "3.0.3", + "resolved": "https://registry.npmjs.org/signal-exit/-/signal-exit-3.0.3.tgz", + "integrity": "sha512-VUJ49FC8U1OxwZLxIbTTrDvLnf/6TDgxZcK8wxR8zs13xpx7xbG60ndBlhNrFi2EMuFRoeDoJO7wthSLq42EjA==", + "dev": true + }, + "sinon": { + "version": "11.1.2", + "resolved": "https://registry.npmjs.org/sinon/-/sinon-11.1.2.tgz", + "integrity": "sha512-59237HChms4kg7/sXhiRcUzdSkKuydDeTiamT/jesUVHshBgL8XAmhgFo0GfK6RruMDM/iRSij1EybmMog9cJw==", + "dev": true, + "requires": { + "@sinonjs/commons": "^1.8.3", + "@sinonjs/fake-timers": "^7.1.2", + "@sinonjs/samsam": "^6.0.2", + "diff": "^5.0.0", + "nise": "^5.1.0", + "supports-color": "^7.2.0" + }, + "dependencies": { + "supports-color": { + "version": "7.2.0", + "resolved": "https://registry.npmjs.org/supports-color/-/supports-color-7.2.0.tgz", + "integrity": "sha512-qpCAvRl9stuOHveKsn7HncJRvv501qIacKzQlO/+Lwxc9+0q2wLyv4Dfvt80/DPn2pqOBsJdDiogXGR9+OvwRw==", + "dev": true, + "requires": { + "has-flag": "^4.0.0" + } + } + } + }, + "slash": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/slash/-/slash-3.0.0.tgz", + "integrity": "sha512-g9Q1haeby36OSStwb4ntCGGGaKsaVSjQ68fBxoQcutl5fS1vuY18H3wSt3jFyFtrkx+Kz0V1G85A4MyAdDMi2Q==", + "dev": true + }, + "source-map": { + "version": "0.5.7", + "resolved": "https://registry.npmjs.org/source-map/-/source-map-0.5.7.tgz", + "integrity": "sha1-igOdLRAh0i0eoUyA2OpGi6LvP8w=", + "dev": true + }, + "source-map-support": { + "version": "0.5.19", + "resolved": "https://registry.npmjs.org/source-map-support/-/source-map-support-0.5.19.tgz", + "integrity": "sha512-Wonm7zOCIJzBGQdB+thsPar0kYuCIzYvxZwlBa87yi/Mdjv7Tip2cyVbLj5o0cFPN4EVkuTwb3GDDyUx2DGnGw==", + "dev": true, + "requires": { + "buffer-from": "^1.0.0", + "source-map": "^0.6.0" + }, + "dependencies": { + "source-map": { + "version": "0.6.1", + "resolved": "https://registry.npmjs.org/source-map/-/source-map-0.6.1.tgz", + "integrity": "sha512-UjgapumWlbMhkBgzT7Ykc5YXUT46F0iKu8SGXq0bcwP5dz/h0Plj6enJqjz1Zbq2l5WaqYnrVbwWOWMyF3F47g==", + "dev": true + } + } + }, + "spawn-wrap": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/spawn-wrap/-/spawn-wrap-2.0.0.tgz", + "integrity": "sha512-EeajNjfN9zMnULLwhZZQU3GWBoFNkbngTUPfaawT4RkMiviTxcX0qfhVbGey39mfctfDHkWtuecgQ8NJcyQWHg==", + "dev": true, + "requires": { + "foreground-child": "^2.0.0", + "is-windows": "^1.0.2", + "make-dir": "^3.0.0", + "rimraf": "^3.0.0", + "signal-exit": "^3.0.2", + "which": "^2.0.1" + } + }, + "split-on-first": { + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/split-on-first/-/split-on-first-1.1.0.tgz", + "integrity": "sha512-43ZssAJaMusuKWL8sKUBQXHWOpq8d6CfN/u1p4gUzfJkM05C8rxTmYrkIPTXapZpORA6LkkzcUulJ8FqA7Uudw==", + "dev": true + }, + "sprintf-js": { + "version": "1.0.3", + "resolved": "https://registry.npmjs.org/sprintf-js/-/sprintf-js-1.0.3.tgz", + "integrity": "sha1-BOaSb2YolTVPPdAVIDYzuFcpfiw=", + "dev": true + }, + "strict-uri-encode": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/strict-uri-encode/-/strict-uri-encode-2.0.0.tgz", + "integrity": "sha1-ucczDHBChi9rFC3CdLvMWGbONUY=", + "dev": true + }, + "string_decoder": { + "version": "1.3.0", + "resolved": "https://registry.npmjs.org/string_decoder/-/string_decoder-1.3.0.tgz", + "integrity": "sha512-hkRX8U1WjJFd8LsDJ2yQ/wWWxaopEsABU1XfkM8A+j0+85JAGppt16cr1Whg6KIbb4okU6Mql6BOj+uup/wKeA==", + "dev": true, + "requires": { + "safe-buffer": "~5.2.0" + }, + "dependencies": { + "safe-buffer": { + "version": "5.2.1", + "resolved": "https://registry.npmjs.org/safe-buffer/-/safe-buffer-5.2.1.tgz", + "integrity": "sha512-rp3So07KcdmmKbGvgaNxQSJr7bGVSVk5S9Eq1F+ppbRo70+YeaDxkw5Dd8NPN+GD6bjnYm2VuPuCXmpuYvmCXQ==", + "dev": true + } + } + }, + "string-width": { + "version": "4.2.2", + "resolved": "https://registry.npmjs.org/string-width/-/string-width-4.2.2.tgz", + "integrity": "sha512-XBJbT3N4JhVumXE0eoLU9DCjcaF92KLNqTmFCnG1pf8duUxFGwtP6AD6nkjw9a3IdiRtL3E2w3JDiE/xi3vOeA==", + "dev": true, + "requires": { + "emoji-regex": "^8.0.0", + "is-fullwidth-code-point": "^3.0.0", + "strip-ansi": "^6.0.0" + } + }, + "strip-ansi": { + "version": "6.0.0", + "resolved": "https://registry.npmjs.org/strip-ansi/-/strip-ansi-6.0.0.tgz", + "integrity": "sha512-AuvKTrTfQNYNIctbR1K/YGTR1756GycPsg7b9bdV9Duqur4gv6aKqHXah67Z8ImS7WEz5QVcOtlfW2rZEugt6w==", + "dev": true, + "requires": { + "ansi-regex": "^5.0.0" + } + }, + "strip-bom": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/strip-bom/-/strip-bom-4.0.0.tgz", + "integrity": "sha512-3xurFv5tEgii33Zi8Jtp55wEIILR9eh34FAW00PZf+JnSsTmV/ioewSgQl97JHvgjoRGwPShsWm+IdrxB35d0w==", + "dev": true + }, + "strip-final-newline": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/strip-final-newline/-/strip-final-newline-2.0.0.tgz", + "integrity": "sha512-BrpvfNAE3dcvq7ll3xVumzjKjZQ5tI1sEUIKr3Uoks0XUl45St3FlatVqef9prk4jRDzhW6WZg+3bk93y6pLjA==", + "dev": true + }, + "strip-json-comments": { + "version": "3.1.1", + "resolved": "https://registry.npmjs.org/strip-json-comments/-/strip-json-comments-3.1.1.tgz", + "integrity": "sha512-6fPc+R4ihwqP6N/aIv2f1gMH8lOVtWQHoqC4yK6oSDVVocumAsfCqjkXnqiYMhmMwS/mEHLp7Vehlt3ql6lEig==", + "dev": true + }, + "supports-color": { + "version": "8.1.1", + "resolved": "https://registry.npmjs.org/supports-color/-/supports-color-8.1.1.tgz", + "integrity": "sha512-MpUEN2OodtUzxvKQl72cUF7RQ5EiHsGvSsVG0ia9c5RbWGL2CI4C7EpPS8UTBIplnlzZiNuV56w+FuNxy3ty2Q==", + "dev": true, + "requires": { + "has-flag": "^4.0.0" + } + }, + "test-exclude": { + "version": "6.0.0", + "resolved": "https://registry.npmjs.org/test-exclude/-/test-exclude-6.0.0.tgz", + "integrity": "sha512-cAGWPIyOHU6zlmg88jwm7VRyXnMN7iV68OGAbYDk/Mh/xC/pzVPlQtY6ngoIH/5/tciuhGfvESU8GrHrcxD56w==", + "dev": true, + "requires": { + "@istanbuljs/schema": "^0.1.2", + "glob": "^7.1.4", + "minimatch": "^3.0.4" + } + }, + "through": { + "version": "2.3.8", + "resolved": "https://registry.npmjs.org/through/-/through-2.3.8.tgz", + "integrity": "sha1-DdTJ/6q8NXlgsbckEV1+Doai4fU=", + "dev": true + }, + "tmp": { + "version": "0.0.33", + "resolved": "https://registry.npmjs.org/tmp/-/tmp-0.0.33.tgz", + "integrity": "sha512-jRCJlojKnZ3addtTOjdIqoRuPEKBvNXcGYqzO6zWZX8KfKEpnGY5jfggJQ3EjKuu8D4bJRr0y+cYJFmYbImXGw==", + "dev": true, + "requires": { + "os-tmpdir": "~1.0.2" + } + }, + "to-fast-properties": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/to-fast-properties/-/to-fast-properties-2.0.0.tgz", + "integrity": "sha1-3F5pjL0HkmW8c+A3doGk5Og/YW4=", + "dev": true + }, + "to-readable-stream": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/to-readable-stream/-/to-readable-stream-1.0.0.tgz", + "integrity": "sha512-Iq25XBt6zD5npPhlLVXGFN3/gyR2/qODcKNNyTMd4vbm39HUaOiAM4PMq0eMVC/Tkxz+Zjdsc55g9yyz+Yq00Q==", + "dev": true + }, + "to-regex-range": { + "version": "5.0.1", + "resolved": "https://registry.npmjs.org/to-regex-range/-/to-regex-range-5.0.1.tgz", + "integrity": "sha512-65P7iz6X5yEr1cwcgvQxbbIw7Uk3gOy5dIdtZ4rDveLqhrdJP+Li/Hx6tyK0NEb+2GCyneCMJiGqrADCSNk8sQ==", + "dev": true, + "requires": { + "is-number": "^7.0.0" + } + }, + "ts-node": { + "version": "10.2.1", + "resolved": "https://registry.npmjs.org/ts-node/-/ts-node-10.2.1.tgz", + "integrity": "sha512-hCnyOyuGmD5wHleOQX6NIjJtYVIO8bPP8F2acWkB4W06wdlkgyvJtubO/I9NkI88hCFECbsEgoLc0VNkYmcSfw==", + "dev": true, + "requires": { + "@cspotcode/source-map-support": "0.6.1", + "@tsconfig/node10": "^1.0.7", + "@tsconfig/node12": "^1.0.7", + "@tsconfig/node14": "^1.0.0", + "@tsconfig/node16": "^1.0.2", + "acorn": "^8.4.1", + "acorn-walk": "^8.1.1", + "arg": "^4.1.0", + "create-require": "^1.1.0", + "diff": "^4.0.1", + "make-error": "^1.1.1", + "yn": "3.1.1" + }, + "dependencies": { + "diff": { + "version": "4.0.2", + "resolved": "https://registry.npmjs.org/diff/-/diff-4.0.2.tgz", + "integrity": "sha512-58lmxKSA4BNyLz+HHMUzlOEpg09FV+ev6ZMe3vJihgdxzgcwZ8VoEEPmALCZG9LmqfVoNMMKpttIYTVG6uDY7A==", + "dev": true + } + } + }, + "tslib": { + "version": "2.1.0", + "resolved": "https://registry.npmjs.org/tslib/-/tslib-2.1.0.tgz", + "integrity": "sha512-hcVC3wYEziELGGmEEXue7D75zbwIIVUMWAVbHItGPx0ziyXxrOMQx4rQEVEV45Ut/1IotuEvwqPopzIOkDMf0A==", + "dev": true + }, + "type-detect": { + "version": "4.0.8", + "resolved": "https://registry.npmjs.org/type-detect/-/type-detect-4.0.8.tgz", + "integrity": "sha512-0fr/mIH1dlO+x7TlcMy+bIDqKPsw/70tVyeHW787goQjhmqaZe10uwLujubK9q9Lg6Fiho1KUKDYz0Z7k7g5/g==", + "dev": true + }, + "type-fest": { + "version": "0.8.1", + "resolved": "https://registry.npmjs.org/type-fest/-/type-fest-0.8.1.tgz", + "integrity": "sha512-4dbzIzqvjtgiM5rw1k5rEHtBANKmdudhGyBEajN01fEyhaAIhsoKNy6y7+IN93IfpFtwY9iqi7kD+xwKhQsNJA==", + "dev": true + }, + "typedarray-to-buffer": { + "version": "3.1.5", + "resolved": "https://registry.npmjs.org/typedarray-to-buffer/-/typedarray-to-buffer-3.1.5.tgz", + "integrity": "sha512-zdu8XMNEDepKKR+XYOXAVPtWui0ly0NtohUscw+UmaHiAWT8hrV1rr//H6V+0DvJ3OQ19S979M0laLfX8rm82Q==", + "dev": true, + "requires": { + "is-typedarray": "^1.0.0" + } + }, + "typedoc": { + "version": "0.21.9", + "resolved": "https://registry.npmjs.org/typedoc/-/typedoc-0.21.9.tgz", + "integrity": "sha512-VRo7aII4bnYaBBM1lhw4bQFmUcDQV8m8tqgjtc7oXl87jc1Slbhfw2X5MccfcR2YnEClHDWgsiQGgNB8KJXocA==", + "dev": true, + "requires": { + "glob": "^7.1.7", + "handlebars": "^4.7.7", + "lunr": "^2.3.9", + "marked": "^3.0.2", + "minimatch": "^3.0.0", + "progress": "^2.0.3", + "shiki": "^0.9.8", + "typedoc-default-themes": "^0.12.10" + } + }, + "typedoc-default-themes": { + "version": "0.12.10", + "resolved": "https://registry.npmjs.org/typedoc-default-themes/-/typedoc-default-themes-0.12.10.tgz", + "integrity": "sha512-fIS001cAYHkyQPidWXmHuhs8usjP5XVJjWB8oZGqkTowZaz3v7g3KDZeeqE82FBrmkAnIBOY3jgy7lnPnqATbA==", + "dev": true + }, + "typedoc-github-wiki-theme": { + "version": "0.5.1", + "resolved": "https://registry.npmjs.org/typedoc-github-wiki-theme/-/typedoc-github-wiki-theme-0.5.1.tgz", + "integrity": "sha512-ED2Uc3WUjbv6xIdCkpMz3yBtcEciJnAhDQdRWLYgw4K+FKY0T3PzbI+ssNsBVgwDnYQP/XuaqfZkeQ3EQsOm9g==", + "dev": true, + "requires": {} + }, + "typedoc-plugin-markdown": { + "version": "3.10.4", + "resolved": "https://registry.npmjs.org/typedoc-plugin-markdown/-/typedoc-plugin-markdown-3.10.4.tgz", + "integrity": "sha512-if9w7S9fXLg73AYi/EoRSEhTOZlg3I8mIP8YEmvzSE33VrNXC9/hA0nVcLEwFVJeQY7ay6z67I6kW0KIv7LjeA==", + "dev": true, + "requires": { + "handlebars": "^4.7.7" + } + }, + "typescript": { + "version": "4.4.2", + "resolved": "https://registry.npmjs.org/typescript/-/typescript-4.4.2.tgz", + "integrity": "sha512-gzP+t5W4hdy4c+68bfcv0t400HVJMMd2+H9B7gae1nQlBzCqvrXX+6GL/b3GAgyTH966pzrZ70/fRjwAtZksSQ==", + "dev": true + }, + "uglify-js": { + "version": "3.14.1", + "resolved": "https://registry.npmjs.org/uglify-js/-/uglify-js-3.14.1.tgz", + "integrity": "sha512-JhS3hmcVaXlp/xSo3PKY5R0JqKs5M3IV+exdLHW99qKvKivPO4Z8qbej6mte17SOPqAOVMjt/XGgWacnFSzM3g==", + "dev": true, + "optional": true + }, + "unique-string": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/unique-string/-/unique-string-2.0.0.tgz", + "integrity": "sha512-uNaeirEPvpZWSgzwsPGtU2zVSTrn/8L5q/IexZmH0eH6SA73CmAA5U4GwORTxQAZs95TAXLNqeLoPPNO5gZfWg==", + "dev": true, + "requires": { + "crypto-random-string": "^2.0.0" + } + }, + "universal-user-agent": { + "version": "6.0.0", + "resolved": "https://registry.npmjs.org/universal-user-agent/-/universal-user-agent-6.0.0.tgz", + "integrity": "sha512-isyNax3wXoKaulPDZWHQqbmIx1k2tb9fb3GGDBRxCscfYV2Ch7WxPArBsFEG8s/safwXTT7H4QGhaIkTp9447w==", + "dev": true + }, + "update-notifier": { + "version": "5.1.0", + "resolved": "https://registry.npmjs.org/update-notifier/-/update-notifier-5.1.0.tgz", + "integrity": "sha512-ItnICHbeMh9GqUy31hFPrD1kcuZ3rpxDZbf4KUDavXwS0bW5m7SLbDQpGX3UYr072cbrF5hFUs3r5tUsPwjfHw==", + "dev": true, + "requires": { + "boxen": "^5.0.0", + "chalk": "^4.1.0", + "configstore": "^5.0.1", + "has-yarn": "^2.1.0", + "import-lazy": "^2.1.0", + "is-ci": "^2.0.0", + "is-installed-globally": "^0.4.0", + "is-npm": "^5.0.0", + "is-yarn-global": "^0.3.0", + "latest-version": "^5.1.0", + "pupa": "^2.1.1", + "semver": "^7.3.4", + "semver-diff": "^3.1.1", + "xdg-basedir": "^4.0.0" + }, + "dependencies": { + "ci-info": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/ci-info/-/ci-info-2.0.0.tgz", + "integrity": "sha512-5tK7EtrZ0N+OLFMthtqOj4fI2Jeb88C4CAZPu25LDVUgXJ0A3Js4PMGqrn0JU1W0Mh1/Z8wZzYPxqUrXeBboCQ==", + "dev": true + }, + "is-ci": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/is-ci/-/is-ci-2.0.0.tgz", + "integrity": "sha512-YfJT7rkpQB0updsdHLGWrvhBJfcfzNNawYDNIyQXJz0IViGf75O8EBPKSdvw2rF+LGCsX4FZ8tcr3b19LcZq4w==", + "dev": true, + "requires": { + "ci-info": "^2.0.0" + } + }, + "lru-cache": { + "version": "6.0.0", + "resolved": "https://registry.npmjs.org/lru-cache/-/lru-cache-6.0.0.tgz", + "integrity": "sha512-Jo6dJ04CmSjuznwJSS3pUeWmd/H0ffTlkXXgwZi+eq1UCmqQwCh+eLsYOYCwY991i2Fah4h1BEMCx4qThGbsiA==", + "dev": true, + "requires": { + "yallist": "^4.0.0" + } + }, + "semver": { + "version": "7.3.5", + "resolved": "https://registry.npmjs.org/semver/-/semver-7.3.5.tgz", + "integrity": "sha512-PoeGJYh8HK4BTO/a9Tf6ZG3veo/A7ZVsYrSA6J8ny9nb3B1VrpkuN+z9OE5wfE5p6H4LchYZsegiQgbJD94ZFQ==", + "dev": true, + "requires": { + "lru-cache": "^6.0.0" + } + } + } + }, + "url-join": { + "version": "4.0.1", + "resolved": "https://registry.npmjs.org/url-join/-/url-join-4.0.1.tgz", + "integrity": "sha512-jk1+QP6ZJqyOiuEI9AEWQfju/nB2Pw466kbA0LEZljHwKeMgd9WrAEgEGxjPDD2+TNbbb37rTyhEfrCXfuKXnA==", + "dev": true + }, + "url-parse-lax": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/url-parse-lax/-/url-parse-lax-3.0.0.tgz", + "integrity": "sha1-FrXK/Afb42dsGxmZF3gj1lA6yww=", + "dev": true, + "requires": { + "prepend-http": "^2.0.0" + } + }, + "util-deprecate": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/util-deprecate/-/util-deprecate-1.0.2.tgz", + "integrity": "sha1-RQ1Nyfpw3nMnYvvS1KKJgUGaDM8=", + "dev": true + }, + "uuid": { + "version": "3.4.0", + "resolved": "https://registry.npmjs.org/uuid/-/uuid-3.4.0.tgz", + "integrity": "sha512-HjSDRw6gZE5JMggctHBcjVak08+KEVhSIiDzFnT9S9aegmp85S/bReBVTb4QTFaRNptJ9kuYaNhnbNEOkbKb/A==", + "dev": true + }, + "vscode-textmate": { + "version": "5.2.0", + "resolved": "https://registry.npmjs.org/vscode-textmate/-/vscode-textmate-5.2.0.tgz", + "integrity": "sha512-Uw5ooOQxRASHgu6C7GVvUxisKXfSgW4oFlO+aa+PAkgmH89O3CXxEEzNRNtHSqtXFTl0nAC1uYj0GMSH27uwtQ==", + "dev": true + }, + "wcwidth": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/wcwidth/-/wcwidth-1.0.1.tgz", + "integrity": "sha1-8LDc+RW8X/FSivrbLA4XtTLaL+g=", + "dev": true, + "requires": { + "defaults": "^1.0.3" + } + }, + "which": { + "version": "2.0.2", + "resolved": "https://registry.npmjs.org/which/-/which-2.0.2.tgz", + "integrity": "sha512-BLI3Tl1TW3Pvl70l3yq3Y64i+awpwXqsGBYWkkqMtnbXgrMD+yj7rhW0kuEDxzJaYXGjEW5ogapKNMEKNMjibA==", + "dev": true, + "requires": { + "isexe": "^2.0.0" + } + }, + "which-module": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/which-module/-/which-module-2.0.0.tgz", + "integrity": "sha1-2e8H3Od7mQK4o6j6SzHD4/fm6Ho=", + "dev": true + }, + "wide-align": { + "version": "1.1.3", + "resolved": "https://registry.npmjs.org/wide-align/-/wide-align-1.1.3.tgz", + "integrity": "sha512-QGkOQc8XL6Bt5PwnsExKBPuMKBxnGxWWW3fU55Xt4feHozMUhdUMaBCk290qpm/wG5u/RSKzwdAC4i51YigihA==", + "dev": true, + "requires": { + "string-width": "^1.0.2 || 2" + }, + "dependencies": { + "ansi-regex": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/ansi-regex/-/ansi-regex-3.0.0.tgz", + "integrity": "sha1-7QMXwyIGT3lGbAKWa922Bas32Zg=", + "dev": true + }, + "is-fullwidth-code-point": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/is-fullwidth-code-point/-/is-fullwidth-code-point-2.0.0.tgz", + "integrity": "sha1-o7MKXE8ZkYMWeqq5O+764937ZU8=", + "dev": true + }, + "string-width": { + "version": "2.1.1", + "resolved": "https://registry.npmjs.org/string-width/-/string-width-2.1.1.tgz", + "integrity": "sha512-nOqH59deCq9SRHlxq1Aw85Jnt4w6KvLKqWVik6oA9ZklXLNIOlqg4F2yrT1MVaTjAqvVwdfeZ7w7aCvJD7ugkw==", + "dev": true, + "requires": { + "is-fullwidth-code-point": "^2.0.0", + "strip-ansi": "^4.0.0" + } + }, + "strip-ansi": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/strip-ansi/-/strip-ansi-4.0.0.tgz", + "integrity": "sha1-qEeQIusaw2iocTibY1JixQXuNo8=", + "dev": true, + "requires": { + "ansi-regex": "^3.0.0" + } + } + } + }, + "widest-line": { + "version": "3.1.0", + "resolved": "https://registry.npmjs.org/widest-line/-/widest-line-3.1.0.tgz", + "integrity": "sha512-NsmoXalsWVDMGupxZ5R08ka9flZjjiLvHVAWYOKtiKM8ujtZWr9cRffak+uSE48+Ob8ObalXpwyeUiyDD6QFgg==", + "dev": true, + "requires": { + "string-width": "^4.0.0" + } + }, + "windows-release": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/windows-release/-/windows-release-4.0.0.tgz", + "integrity": "sha512-OxmV4wzDKB1x7AZaZgXMVsdJ1qER1ed83ZrTYd5Bwq2HfJVg3DJS8nqlAG4sMoJ7mu8cuRmLEYyU13BKwctRAg==", + "dev": true, + "requires": { + "execa": "^4.0.2" + }, + "dependencies": { + "execa": { + "version": "4.1.0", + "resolved": "https://registry.npmjs.org/execa/-/execa-4.1.0.tgz", + "integrity": "sha512-j5W0//W7f8UxAn8hXVnwG8tLwdiUy4FJLcSupCg6maBYZDpyBvTApK7KyuI4bKj8KOh1r2YH+6ucuYtJv1bTZA==", + "dev": true, + "requires": { + "cross-spawn": "^7.0.0", + "get-stream": "^5.0.0", + "human-signals": "^1.1.1", + "is-stream": "^2.0.0", + "merge-stream": "^2.0.0", + "npm-run-path": "^4.0.0", + "onetime": "^5.1.0", + "signal-exit": "^3.0.2", + "strip-final-newline": "^2.0.0" + } + }, + "get-stream": { + "version": "5.2.0", + "resolved": "https://registry.npmjs.org/get-stream/-/get-stream-5.2.0.tgz", + "integrity": "sha512-nBF+F1rAZVCu/p7rjzgA+Yb4lfYXrpl7a6VmJrU8wF9I1CKvP/QwPNZHnOlwbTkY6dvtFIzFMSyQXbLoTQPRpA==", + "dev": true, + "requires": { + "pump": "^3.0.0" + } + }, + "human-signals": { + "version": "1.1.1", + "resolved": "https://registry.npmjs.org/human-signals/-/human-signals-1.1.1.tgz", + "integrity": "sha512-SEQu7vl8KjNL2eoGBLF3+wAjpsNfA9XMlXAYj/3EdaNfAlxKthD1xjEQfGOUhllCGGJVNY34bRr6lPINhNjyZw==", + "dev": true + } + } + }, + "wordwrap": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/wordwrap/-/wordwrap-1.0.0.tgz", + "integrity": "sha1-J1hIEIkUVqQXHI0CJkQa3pDLyus=", + "dev": true + }, + "workerpool": { + "version": "6.1.5", + "resolved": "https://registry.npmjs.org/workerpool/-/workerpool-6.1.5.tgz", + "integrity": "sha512-XdKkCK0Zqc6w3iTxLckiuJ81tiD/o5rBE/m+nXpRCB+/Sq4DqkfXZ/x0jW02DG1tGsfUGXbTJyZDP+eu67haSw==", + "dev": true + }, + "wrap-ansi": { + "version": "7.0.0", + "resolved": "https://registry.npmjs.org/wrap-ansi/-/wrap-ansi-7.0.0.tgz", + "integrity": "sha512-YVGIj2kamLSTxw6NsZjoBxfSwsn0ycdesmc4p+Q21c5zPuZ1pl+NfxVdxPtdHvmNVOQ6XSYG4AUtyt/Fi7D16Q==", + "dev": true, + "requires": { + "ansi-styles": "^4.0.0", + "string-width": "^4.1.0", + "strip-ansi": "^6.0.0" + } + }, + "wrappy": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/wrappy/-/wrappy-1.0.2.tgz", + "integrity": "sha1-tSQ9jz7BqjXxNkYFvA0QNuMKtp8=", + "dev": true + }, + "write-file-atomic": { + "version": "3.0.3", + "resolved": "https://registry.npmjs.org/write-file-atomic/-/write-file-atomic-3.0.3.tgz", + "integrity": "sha512-AvHcyZ5JnSfq3ioSyjrBkH9yW4m7Ayk8/9My/DD9onKeu/94fwrMocemO2QAJFAlnnDN+ZDS+ZjAR5ua1/PV/Q==", + "dev": true, + "requires": { + "imurmurhash": "^0.1.4", + "is-typedarray": "^1.0.0", + "signal-exit": "^3.0.2", + "typedarray-to-buffer": "^3.1.5" + } + }, + "xdg-basedir": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/xdg-basedir/-/xdg-basedir-4.0.0.tgz", + "integrity": "sha512-PSNhEJDejZYV7h50BohL09Er9VaIefr2LMAf3OEmpCkjOi34eYyQYAXUTjEQtZJTKcF0E2UKTh+osDLsgNim9Q==", + "dev": true + }, + "y18n": { + "version": "5.0.8", + "resolved": "https://registry.npmjs.org/y18n/-/y18n-5.0.8.tgz", + "integrity": "sha512-0pfFzegeDWJHJIAmTLRP2DwHjdF5s7jo9tuztdQxAhINCdvS+3nGINqPd00AphqJR/0LhANUS6/+7SCb98YOfA==", + "dev": true + }, + "yallist": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/yallist/-/yallist-4.0.0.tgz", + "integrity": "sha512-3wdGidZyq5PB084XLES5TpOSRA3wjXAlIWMhum2kRcv/41Sn2emQ0dycQW4uZXLejwKvg6EsvbdlVL+FYEct7A==" + }, + "yaml": { + "version": "1.10.2", + "resolved": "https://registry.npmjs.org/yaml/-/yaml-1.10.2.tgz", + "integrity": "sha512-r3vXyErRCYJ7wg28yvBY5VSoAF8ZvlcW9/BwUzEtUsjvX/DKs24dIkuwjtuprwJJHsbyUbLApepYTR1BN4uHrg==", + "dev": true + }, + "yargs": { + "version": "16.2.0", + "resolved": "https://registry.npmjs.org/yargs/-/yargs-16.2.0.tgz", + "integrity": "sha512-D1mvvtDG0L5ft/jGWkLpG1+m0eQxOfaBvTNELraWj22wSVUMWxZUvYgJYcKh6jGGIkJFhH4IZPQhR4TKpc8mBw==", + "dev": true, + "requires": { + "cliui": "^7.0.2", + "escalade": "^3.1.1", + "get-caller-file": "^2.0.5", + "require-directory": "^2.1.1", + "string-width": "^4.2.0", + "y18n": "^5.0.5", + "yargs-parser": "^20.2.2" + } + }, + "yargs-parser": { + "version": "20.2.4", + "resolved": "https://registry.npmjs.org/yargs-parser/-/yargs-parser-20.2.4.tgz", + "integrity": "sha512-WOkpgNhPTlE73h4VFAFsOnomJVaovO8VqLDzy5saChRBFQFBoMYirowyW+Q9HB4HFF4Z7VZTiG3iSzJJA29yRA==", + "dev": true + }, + "yargs-unparser": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/yargs-unparser/-/yargs-unparser-2.0.0.tgz", + "integrity": "sha512-7pRTIA9Qc1caZ0bZ6RYRGbHJthJWuakf+WmHK0rVeLkNrrGhfoabBNdue6kdINI6r4if7ocq9aD/n7xwKOdzOA==", + "dev": true, + "requires": { + "camelcase": "^6.0.0", + "decamelize": "^4.0.0", + "flat": "^5.0.2", + "is-plain-obj": "^2.1.0" + }, + "dependencies": { + "camelcase": { + "version": "6.2.0", + "resolved": "https://registry.npmjs.org/camelcase/-/camelcase-6.2.0.tgz", + "integrity": "sha512-c7wVvbw3f37nuobQNtgsgG9POC9qMbNuMQmTCqZv23b6MIz0fcYpBiOlv9gEN/hdLdnZTDQhg6e9Dq5M1vKvfg==", + "dev": true + }, + "decamelize": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/decamelize/-/decamelize-4.0.0.tgz", + "integrity": "sha512-9iE1PgSik9HeIIw2JO94IidnE3eBoQrFJ3w7sFuzSX4DpmZ3v5sZpUiV5Swcf6mQEF+Y0ru8Neo+p+nyh2J+hQ==", + "dev": true + } + } + }, + "yn": { + "version": "3.1.1", + "resolved": "https://registry.npmjs.org/yn/-/yn-3.1.1.tgz", + "integrity": "sha512-Ux4ygGWsu2c7isFWe8Yu1YluJmqVhxqK2cLXNQA5AcC3QfbGNpM7fu0Y8b/z16pXLnFxZYvWhd3fhBY9DLmC6Q==", + "dev": true + }, + "yocto-queue": { + "version": "0.1.0", + "resolved": "https://registry.npmjs.org/yocto-queue/-/yocto-queue-0.1.0.tgz", + "integrity": "sha512-rVksvsnNCdJ/ohGc6xgPwyN8eheCxsiLM8mxuE/t/mOVqJewPuO1miLpTHQiRgTKCLexL4MeAFVagts7HmNZ2Q==", + "dev": true + } + } +} diff --git a/package.json b/package.json index f6806391641..be909a2197e 100644 --- a/package.json +++ b/package.json @@ -1,17 +1,11 @@ { "name": "redis", - "version": "3.1.2", + "version": "4.0.0-rc.0", "description": "A high performance Redis client.", "keywords": [ "database", "redis", - "transaction", - "pipelining", - "performance", - "queue", - "nodejs", - "pubsub", - "backpressure" + "pubsub" ], "author": "Matt Ranney ", "contributors": [ @@ -25,38 +19,40 @@ } ], "license": "MIT", - "main": "./index.js", + "main": "./dist/index.js", + "types": "./dist/index.d.ts", "scripts": { - "coveralls": "nyc report --reporter=text-lcov | coveralls", - "coverage": "nyc report --reporter=html", - "benchmark": "node benchmarks/multi_bench.js", - "test": "nyc --cache mocha ./test/*.spec.js ./test/commands/*.spec.js --timeout=8000 && npm run coverage", - "lint": "eslint .", - "lint:fix": "eslint . --fix", - "lint:report": "eslint --output-file=eslint-report.json --format=json .", - "compare": "node benchmarks/diff_multi_bench_output.js beforeBench.txt afterBench.txt" + "test": "nyc -r text-summary -r html mocha -r source-map-support/register -r ts-node/register './lib/**/*.spec.ts'", + "build": "tsc", + "documentation": "typedoc" }, "dependencies": { - "denque": "^1.5.0", - "redis-commands": "^1.7.0", - "redis-errors": "^1.2.0", - "redis-parser": "^3.0.0" - }, - "engines": { - "node": ">=10" + "cluster-key-slot": "1.1.0", + "generic-pool": "3.8.2", + "redis-parser": "3.0.0", + "yallist": "4.0.0" }, "devDependencies": { - "bluebird": "^3.7.2", - "coveralls": "^3.1.0", - "cross-spawn": "^7.0.3", - "eslint": "^7.21.0", - "intercept-stdout": "~0.1.2", - "metrics": "^0.1.21", - "mocha": "^8.3.0", + "@istanbuljs/nyc-config-typescript": "^1.0.1", + "@types/mocha": "^9.0.0", + "@types/node": "^16.7.8", + "@types/sinon": "^10.0.2", + "@types/which": "^2.0.1", + "@types/yallist": "^4.0.1", + "mocha": "^9.1.1", "nyc": "^15.1.0", - "prettier": "^2.2.1", - "tcp-port-used": "^1.0.1", - "uuid": "^8.3.2" + "release-it": "^14.11.5", + "sinon": "^11.1.2", + "source-map-support": "^0.5.19", + "ts-node": "^10.2.1", + "typedoc": "^0.21.9", + "typedoc-github-wiki-theme": "^0.5.1", + "typedoc-plugin-markdown": "^3.10.4", + "typescript": "^4.4.2", + "which": "^2.0.2" + }, + "engines": { + "node": ">=12" }, "repository": { "type": "git", @@ -65,13 +61,5 @@ "bugs": { "url": "https://github.com/NodeRedis/node-redis/issues" }, - "homepage": "https://github.com/NodeRedis/node-redis", - "directories": { - "example": "examples", - "test": "test" - }, - "funding": { - "type": "opencollective", - "url": "https://opencollective.com/node-redis" - } + "homepage": "https://github.com/NodeRedis/node-redis/tree/v4" } diff --git a/test/auth.spec.js b/test/auth.spec.js deleted file mode 100644 index d1b596e5ae3..00000000000 --- a/test/auth.spec.js +++ /dev/null @@ -1,354 +0,0 @@ -'use strict'; - -var assert = require('assert'); -var config = require('./lib/config'); -var helper = require('./helper'); -var errors = require('./errors'); -var redis = config.redis; - -if (process.platform === 'win32') { - // TODO: Fix redis process spawn on windows - return; -} - -describe('client authentication', function () { - before(function (done) { - helper.stopRedis(function () { - helper.startRedis('./conf/password.conf', done); - }); - }); - - helper.allTests({ - allConnections: true - }, function (ip, args) { - - describe('using ' + ip, function () { - var auth = 'porkchopsandwiches'; - var client = null; - - beforeEach(function () { - client = null; - }); - afterEach(function () { - // Explicitly ignore still running commands - // The ready command could still be running - client.end(false); - }); - - it("allows auth to be provided with 'auth' method", function (done) { - if (helper.redisProcess().spawnFailed()) this.skip(); - - client = redis.createClient.apply(null, args); - client.auth(auth, function (err, res) { - assert.strictEqual(null, err); - assert.strictEqual('OK', res.toString()); - return done(err); - }); - }); - - it('support redis 2.4 with retrying auth commands if still loading', function (done) { - if (helper.redisProcess().spawnFailed()) this.skip(); - - client = redis.createClient.apply(null, args); - var time = Date.now(); - client.auth(auth, function (err, res) { - assert.strictEqual('retry worked', res); - var now = Date.now(); - // Hint: setTimeout sometimes triggers early and therefore the value can be like one or two ms to early - assert(now - time >= 98, 'Time should be above 100 ms (the reconnect time) and is ' + (now - time)); - assert(now - time < 225, 'Time should be below 255 ms (the reconnect should only take a bit above 100 ms) and is ' + (now - time)); - done(); - }); - var tmp = client.command_queue.get(0).callback; - client.command_queue.get(0).callback = function (err, res) { - client.auth = function (pass, user, callback) { - callback(null, 'retry worked'); - }; - tmp(new Error('ERR redis is still LOADING')); - }; - }); - - it('emits error when auth is bad without callback', function (done) { - if (helper.redisProcess().spawnFailed()) this.skip(); - - client = redis.createClient.apply(null, config.configureClient(ip, { - no_ready_check: true - })); - - client.once('error', function (err) { - assert.strictEqual(err.command, 'AUTH'); - assert.ok(errors.invalidPassword.test(err.message)); - return done(); - }); - - client.auth(auth + 'bad'); - }); - - it('returns an error when auth is bad (empty string) with a callback', function (done) { - if (helper.redisProcess().spawnFailed()) this.skip(); - - client = redis.createClient.apply(null, config.configureClient(ip, { - no_ready_check: true - })); - - client.auth('', function (err, res) { - assert.strictEqual(err.command, 'AUTH'); - assert.ok(errors.invalidPassword.test(err.message)); - done(); - }); - }); - - if (ip === 'IPv4') { - it('allows auth to be provided as part of redis url and do not fire commands before auth is done', function (done) { - if (helper.redisProcess().spawnFailed()) this.skip(); - - var end = helper.callFuncAfter(done, 2); - client = redis.createClient('redis://:' + auth + '@' + config.HOST[ip] + ':' + config.PORT); - client.on('ready', function () { - end(); - }); - // The info command may be used while loading but not if not yet authenticated - client.info(function (err, res) { - assert(!err); - end(); - }); - }); - - it('allows auth and database to be provided as part of redis url query parameter', function (done) { - if (helper.redisProcess().spawnFailed()) this.skip(); - - client = redis.createClient('redis://' + config.HOST[ip] + ':' + config.PORT + '?db=2&password=' + auth); - assert.strictEqual(client.options.db, '2'); - assert.strictEqual(client.options.password, auth); - assert.strictEqual(client.auth_pass, auth); - client.on('ready', function () { - // Set a key so the used database is returned in the info command - client.set('foo', 'bar'); - client.get('foo'); - assert.strictEqual(client.server_info.db2, undefined); - // Using the info command should update the server_info - client.info(function (err, res) { - assert(typeof client.server_info.db2 === 'object'); - }); - client.flushdb(done); - }); - }); - } - - it('allows auth to be provided as config option for client', function (done) { - if (helper.redisProcess().spawnFailed()) this.skip(); - - var args = config.configureClient(ip, { - auth_pass: auth - }); - client = redis.createClient.apply(null, args); - client.on('ready', done); - }); - - it('allows auth and no_ready_check to be provided as config option for client', function (done) { - if (helper.redisProcess().spawnFailed()) this.skip(); - - var args = config.configureClient(ip, { - password: auth, - no_ready_check: true - }); - client = redis.createClient.apply(null, args); - client.on('ready', done); - }); - - it('allows auth to be provided post-hoc with auth method', function (done) { - if (helper.redisProcess().spawnFailed()) this.skip(); - - var args = config.configureClient(ip); - client = redis.createClient.apply(null, args); - client.auth(auth); - client.on('ready', done); - }); - - it('reconnects with appropriate authentication while offline commands are present', function (done) { - if (helper.redisProcess().spawnFailed()) this.skip(); - - client = redis.createClient.apply(null, args); - client.auth(auth); - client.on('ready', function () { - if (this.times_connected < 3) { - var interval = setInterval(function () { - if (client.commandQueueLength !== 0) { - return; - } - clearInterval(interval); - interval = null; - client.stream.destroy(); - client.set('foo', 'bar'); - client.get('foo'); // Errors would bubble - assert.strictEqual(client.offlineQueueLength, 2); - }, 1); - } else { - done(); - } - }); - client.on('reconnecting', function (params) { - assert.strictEqual(params.error, null); - }); - }); - - it('should return an error if the password is not correct and a callback has been provided', function (done) { - if (helper.redisProcess().spawnFailed()) this.skip(); - - client = redis.createClient.apply(null, config.configureClient(ip, { - no_ready_check: true - })); - var async = true; - client.auth('undefined', function (err, res) { - assert.ok(errors.invalidPassword.test(err.message)); - assert.strictEqual(err.command, 'AUTH'); - assert.strictEqual(res, undefined); - async = false; - done(); - }); - assert(async); - }); - - it('should emit an error if the password is not correct and no callback has been provided', function (done) { - if (helper.redisProcess().spawnFailed()) this.skip(); - - client = redis.createClient.apply(null, config.configureClient(ip, { - no_ready_check: true - })); - client.on('error', function (err) { - assert.ok(errors.invalidPassword.test(err.message)); - assert.strictEqual(err.command, 'AUTH'); - done(); - }); - client.auth(234567); - }); - - it('allows auth to be provided post-hoc with auth method again', function (done) { - if (helper.redisProcess().spawnFailed()) this.skip(); - - var args = config.configureClient(ip, { - auth_pass: auth - }); - client = redis.createClient.apply(null, args); - client.on('ready', function () { - client.auth(auth, helper.isString('OK', done)); - }); - }); - - it('does not allow any commands to be processed if not authenticated using no_ready_check true', function (done) { - if (helper.redisProcess().spawnFailed()) this.skip(); - - var args = config.configureClient(ip, { - no_ready_check: true - }); - client = redis.createClient.apply(null, args); - client.on('ready', function () { - client.set('foo', 'bar', function (err, res) { - assert.ok(/^NOAUTH Authentication required\.(\r\n)?$/.test(err.message)); - assert.equal(err.code, 'NOAUTH'); - assert.equal(err.command, 'SET'); - done(); - }); - }); - }); - - it('does not allow auth to be provided post-hoc with auth method if not authenticated before', function (done) { - if (helper.redisProcess().spawnFailed()) this.skip(); - client = redis.createClient.apply(null, args); - client.on('error', function (err) { - assert.equal(err.code, 'NOAUTH'); - assert.ok(/^Ready check failed: NOAUTH Authentication required\.(\r\n)?$/.test(err.message)); - assert.equal(err.command, 'INFO'); - done(); - }); - }); - - it('should emit an error if the provided password is faulty', function (done) { - if (helper.redisProcess().spawnFailed()) this.skip(); - client = redis.createClient({ - password: 'wrong_password', - no_ready_check: true - }); - client.once('error', function (err) { - assert.ok(errors.invalidPassword.test(err.message)); - done(); - }); - }); - - it('pubsub working with auth', function (done) { - if (helper.redisProcess().spawnFailed()) this.skip(); - - var args = config.configureClient(ip, { - password: auth - }); - client = redis.createClient.apply(null, args); - client.set('foo', 'bar'); - client.subscribe('somechannel', 'another channel', function (err, res) { - client.once('ready', function () { - assert.strictEqual(client.pub_sub_mode, 1); - client.get('foo', function (err, res) { - assert.ok(errors.subscribeUnsubscribeOnly.test(err.message)); - done(); - }); - }); - }); - client.once('ready', function () { - // Coherent behavior with all other offline commands fires commands before emitting but does not wait till they return - assert.strictEqual(client.pub_sub_mode, 2); - client.ping(function () { // Make sure all commands were properly processed already - client.stream.destroy(); - }); - }); - }); - - it('individual commands work properly with batch', function (done) { - // quit => might return an error instead of "OK" in the exec callback... (if not connected) - // auth => might return an error instead of "OK" in the exec callback... (if no password is required / still loading on Redis <= 2.4) - // This could be fixed by checking the return value of the callback in the exec callback and - // returning the manipulated [error, result] from the callback. - // There should be a better solution though - - var args = config.configureClient('localhost', { - noReadyCheck: true - }); - client = redis.createClient.apply(null, args); - assert.strictEqual(client.selected_db, undefined); - var end = helper.callFuncAfter(done, 8); - client.on('monitor', function () { - end(); // Should be called for each command after monitor - }); - client.batch() - .auth(auth) - .SELECT(5, function (err, res) { - assert.strictEqual(client.selected_db, 5); - assert.strictEqual(res, 'OK'); - assert.notDeepEqual(client.serverInfo.db5, { avg_ttl: 0, expires: 0, keys: 1 }); - }) - .monitor() - .set('foo', 'bar', helper.isString('OK')) - .INFO('stats', function (err, res) { - assert.strictEqual(res.indexOf('# Stats\r\n'), 0); - assert.strictEqual(client.serverInfo.sync_full, '0'); - }) - .get('foo', helper.isString('bar')) - .subscribe(['foo', 'bar']) - .unsubscribe('foo') - .SUBSCRIBE('/foo', helper.isString('/foo')) - .psubscribe('*') - .quit(helper.isString('OK')) // this might be interesting - .exec(function (err, res) { - res[4] = res[4].substr(0, 9); - assert.deepEqual(res, ['OK', 'OK', 'OK', 'OK', '# Stats\r\n', 'bar', 'bar', 'foo', '/foo', '*', 'OK']); - end(); - }); - }); - }); - }); - - after(function (done) { - if (helper.redisProcess().spawnFailed()) return done(); - helper.stopRedis(function () { - helper.startRedis('./conf/redis.conf', done); - }); - }); -}); diff --git a/test/batch.spec.js b/test/batch.spec.js deleted file mode 100644 index 05cee80681a..00000000000 --- a/test/batch.spec.js +++ /dev/null @@ -1,350 +0,0 @@ -'use strict'; - -var assert = require('assert'); -var config = require('./lib/config'); -var helper = require('./helper'); -var redis = config.redis; - -describe("The 'batch' method", function () { - - helper.allTests(function (ip, args) { - - describe('using ' + ip, function () { - - describe('when not connected', function () { - var client; - - beforeEach(function (done) { - client = redis.createClient.apply(null, args); - client.once('connect', function () { - client.quit(); - }); - client.on('end', done); - }); - - it('returns an empty array for missing commands', function (done) { - var batch = client.batch(); - batch.exec(function (err, res) { - assert.strictEqual(err, null); - assert.strictEqual(res.length, 0); - done(); - }); - }); - - it('returns an error for batch with commands', function (done) { - var batch = client.batch(); - batch.set('foo', 'bar'); - batch.exec(function (err, res) { - assert.strictEqual(err, null); - assert.strictEqual(res[0].code, 'NR_CLOSED'); - done(); - }); - }); - - it('returns an empty array for missing commands if promisified', function () { - return client.batch().execAsync().then(function (res) { - assert.strictEqual(res.length, 0); - }); - }); - }); - - describe('when connected', function () { - var client; - - beforeEach(function (done) { - client = redis.createClient.apply(null, args); - client.once('ready', function () { - client.flushdb(function (err) { - return done(err); - }); - }); - }); - - afterEach(function () { - client.end(true); - }); - - it('returns an empty array and keep the execution order in takt', function (done) { - var called = false; - client.set('foo', 'bar', function (err, res) { - called = true; - }); - var batch = client.batch(); - batch.exec(function (err, res) { - assert.strictEqual(err, null); - assert.strictEqual(res.length, 0); - assert(called); - done(); - }); - }); - - it('runs normal calls inbetween batch', function (done) { - var batch = client.batch(); - batch.set('m1', '123'); - client.set('m2', '456', done); - }); - - it('returns an empty array if promisified', function () { - return client.batch().execAsync().then(function (res) { - assert.strictEqual(res.length, 0); - }); - }); - - it('returns an empty result array', function (done) { - var batch = client.batch(); - var async = true; - var notBuffering = batch.exec(function (err, res) { - assert.strictEqual(err, null); - assert.strictEqual(res.length, 0); - async = false; - done(); - }); - assert(async); - assert.strictEqual(notBuffering, true); - }); - - it('fail individually when one command fails using chaining notation', function (done) { - var batch1, batch2; - batch1 = client.batch(); - batch1.mset('batchfoo', '10', 'batchbar', '20', helper.isString('OK')); - - // Provoke an error at queue time - batch1.set('foo2', helper.isError()); - batch1.incr('batchfoo'); - batch1.incr('batchbar'); - batch1.exec(function () { - // Confirm that the previous command, while containing an error, still worked. - batch2 = client.batch(); - batch2.get('foo2', helper.isNull()); - batch2.incr('batchbar', helper.isNumber(22)); - batch2.incr('batchfoo', helper.isNumber(12)); - batch2.exec(function (err, replies) { - assert.strictEqual(null, replies[0]); - assert.strictEqual(22, replies[1]); - assert.strictEqual(12, replies[2]); - return done(); - }); - }); - }); - - it('fail individually when one command fails and emit the error if no callback has been provided', function (done) { - var batch1; - client.on('error', function (err) { - done(err); - }); - batch1 = client.batch(); - batch1.mset('batchfoo', '10', 'batchbar', '20', helper.isString('OK')); - - // Provoke an error at queue time - batch1.set('foo2'); - batch1.incr('batchfoo'); - batch1.incr('batchbar'); - batch1.exec(function (err, res) { - assert.strictEqual(res[1].command, 'SET'); - assert.strictEqual(res[1].code, 'ERR'); - done(); - }); - }); - - it('fail individually when one command in an array of commands fails', function (done) { - // test nested batch-bulk replies - client.batch([ - ['mget', 'batchfoo', 'batchbar', function (err, res) { - assert.strictEqual(2, res.length); - assert.strictEqual(0, +res[0]); - assert.strictEqual(0, +res[1]); - }], - ['set', 'foo2', helper.isError()], - ['incr', 'batchfoo'], - ['incr', 'batchbar'] - ]).exec(function (err, replies) { - assert.strictEqual(2, replies[0].length); - assert.strictEqual(null, replies[0][0]); - assert.strictEqual(null, replies[0][1]); - assert.strictEqual('SET', replies[1].command); - assert.strictEqual('1', replies[2].toString()); - assert.strictEqual('1', replies[3].toString()); - return done(); - }); - }); - - it('handles multiple operations being applied to a set', function (done) { - client.sadd('some set', 'mem 1'); - client.sadd(['some set', 'mem 2']); - client.sadd('some set', 'mem 3'); - client.sadd('some set', 'mem 4'); - - // make sure empty mb reply works - client.del('some missing set'); - client.smembers('some missing set', function (err, reply) { - // make sure empty mb reply works - assert.strictEqual(0, reply.length); - }); - - // test nested batch-bulk replies with empty mb elements. - client.BATCH([ - ['smembers', ['some set']], - ['del', 'some set'], - ['smembers', 'some set', undefined] // The explicit undefined is handled as a callback that is undefined - ]) - .scard('some set') - .exec(function (err, replies) { - assert.strictEqual(4, replies[0].length); - assert.strictEqual(0, replies[2].length); - return done(); - }); - }); - - it('allows multiple operations to be performed using constructor with all kinds of syntax', function (done) { - var now = Date.now(); - var arr = ['batchhmset', 'batchbar', 'batchbaz']; - var arr2 = ['some manner of key', 'otherTypes']; - var arr3 = [5768, 'batchbarx', 'batchfoox']; - var arr4 = ['mset', [578, 'batchbar'], helper.isString('OK')]; - client.batch([ - arr4, - [['mset', 'batchfoo2', 'batchbar2', 'batchfoo3', 'batchbar3'], helper.isString('OK')], - ['hmset', arr], - [['hmset', 'batchhmset2', 'batchbar2', 'batchfoo3', 'batchbar3', 'test'], helper.isString('OK')], - ['hmset', ['batchhmset', 'batchbar', 'batchfoo'], helper.isString('OK')], - ['hmset', arr3, helper.isString('OK')], - ['hmset', now, {123456789: 'abcdefghij', 'some manner of key': 'a type of value', 'otherTypes': 555}], - ['hmset', 'key2', {'0123456789': 'abcdefghij', 'some manner of key': 'a type of value', 'otherTypes': 999}, helper.isString('OK')], - ['HMSET', 'batchhmset', ['batchbar', 'batchbaz']], - ['hmset', 'batchhmset', ['batchbar', 'batchbaz'], helper.isString('OK')], - ]) - .hmget(now, 123456789, 'otherTypes') - .hmget('key2', arr2, function noop () {}) - .hmget(['batchhmset2', 'some manner of key', 'batchbar3']) - .mget('batchfoo2', ['batchfoo3', 'batchfoo'], function (err, res) { - assert.strictEqual(res[0], 'batchbar2'); - assert.strictEqual(res[1], 'batchbar3'); - assert.strictEqual(res[2], null); - }) - .exec(function (err, replies) { - assert.equal(arr.length, 3); - assert.equal(arr2.length, 2); - assert.equal(arr3.length, 3); - assert.equal(arr4.length, 3); - assert.strictEqual(null, err); - assert.equal(replies[10][1], '555'); - assert.equal(replies[11][0], 'a type of value'); - assert.strictEqual(replies[12][0], null); - assert.equal(replies[12][1], 'test'); - assert.equal(replies[13][0], 'batchbar2'); - assert.equal(replies[13].length, 3); - assert.equal(replies.length, 14); - return done(); - }); - }); - - it('converts a non string key to a string', function (done) { - // TODO: Converting the key might change soon again. - client.batch().hmset(true, { - test: 123, - bar: 'baz' - }).exec(done); - }); - - it('runs a batch without any further commands', function (done) { - var buffering = client.batch().exec(function (err, res) { - assert.strictEqual(err, null); - assert.strictEqual(res.length, 0); - done(); - }); - assert(typeof buffering === 'boolean'); - }); - - it('runs a batch without any further commands and without callback', function () { - var buffering = client.batch().exec(); - assert.strictEqual(buffering, true); - }); - - it('allows multiple operations to be performed using a chaining API', function (done) { - client.batch() - .mset('some', '10', 'keys', '20') - .incr('some') - .incr('keys') - .mget('some', 'keys') - .exec(function (err, replies) { - assert.strictEqual(null, err); - assert.equal('OK', replies[0]); - assert.equal(11, replies[1]); - assert.equal(21, replies[2]); - assert.equal(11, replies[3][0].toString()); - assert.equal(21, replies[3][1].toString()); - return done(); - }); - }); - - it('allows multiple commands to work the same as normal to be performed using a chaining API', function (done) { - client.batch() - .mset(['some', '10', 'keys', '20']) - .incr('some', helper.isNumber(11)) - .incr(['keys'], helper.isNumber(21)) - .mget('some', 'keys') - .exec(function (err, replies) { - assert.strictEqual(null, err); - assert.equal('OK', replies[0]); - assert.equal(11, replies[1]); - assert.equal(21, replies[2]); - assert.equal(11, replies[3][0].toString()); - assert.equal(21, replies[3][1].toString()); - return done(); - }); - }); - - it('allows multiple commands to work the same as normal to be performed using a chaining API promisified', function () { - return client.batch() - .mset(['some', '10', 'keys', '20']) - .incr('some', helper.isNumber(11)) - .incr(['keys'], helper.isNumber(21)) - .mget('some', 'keys') - .execAsync() - .then(function (replies) { - assert.equal('OK', replies[0]); - assert.equal(11, replies[1]); - assert.equal(21, replies[2]); - assert.equal(11, replies[3][0].toString()); - assert.equal(21, replies[3][1].toString()); - }); - }); - - it('allows an array to be provided indicating multiple operations to perform', function (done) { - // test nested batch-bulk replies with nulls. - client.batch([ - ['mget', ['batchfoo', 'some', 'random value', 'keys']], - ['incr', 'batchfoo'] - ]) - .exec(function (err, replies) { - assert.strictEqual(replies.length, 2); - assert.strictEqual(replies[0].length, 4); - return done(); - }); - }); - - it('allows multiple operations to be performed on a hash', function (done) { - client.batch() - .hmset('batchhash', 'a', 'foo', 'b', 1) - .hmset('batchhash', { - extra: 'fancy', - things: 'here' - }) - .hgetall('batchhash') - .exec(done); - }); - - it('should work without any callback or arguments', function (done) { - var batch = client.batch(); - batch.set('baz', 'binary'); - batch.set('foo', 'bar'); - batch.ping(); - batch.exec(); - - client.get('foo', helper.isString('bar', done)); - }); - - }); - }); - }); -}); diff --git a/test/commands/blpop.spec.js b/test/commands/blpop.spec.js deleted file mode 100644 index 64aedf81eae..00000000000 --- a/test/commands/blpop.spec.js +++ /dev/null @@ -1,77 +0,0 @@ -'use strict'; - -var assert = require('assert'); -var config = require('../lib/config'); -var helper = require('../helper'); -var redis = config.redis; -var intercept = require('intercept-stdout'); - -describe("The 'blpop' method", function () { - - helper.allTests(function (ip, args) { - - describe('using ' + ip, function () { - var client; - var bclient; - - beforeEach(function (done) { - client = redis.createClient.apply(null, args); - client.once('ready', function () { - client.flushdb(done); - }); - }); - - it('pops value immediately if list contains values', function (done) { - bclient = redis.createClient.apply(null, args); - redis.debug_mode = true; - var text = ''; - var unhookIntercept = intercept(function (data) { - text += data; - return ''; - }); - client.rpush('blocking list', 'initial value', helper.isNumber(1)); - unhookIntercept(); - assert(/Send 127\.0\.0\.1:6379 id [0-9]+: \*3\r\n\$5\r\nrpush\r\n\$13\r\nblocking list\r\n\$13\r\ninitial value\r\n\n$/.test(text)); - redis.debug_mode = false; - bclient.blpop('blocking list', 0, function (err, value) { - assert.strictEqual(value[0], 'blocking list'); - assert.strictEqual(value[1], 'initial value'); - return done(err); - }); - }); - - it('pops value immediately if list contains values using array notation', function (done) { - bclient = redis.createClient.apply(null, args); - client.rpush(['blocking list', 'initial value'], helper.isNumber(1)); - bclient.blpop(['blocking list', 0], function (err, value) { - assert.strictEqual(value[0], 'blocking list'); - assert.strictEqual(value[1], 'initial value'); - return done(err); - }); - }); - - it('waits for value if list is not yet populated', function (done) { - bclient = redis.createClient.apply(null, args); - bclient.blpop('blocking list 2', 5, function (err, value) { - assert.strictEqual(value[0], 'blocking list 2'); - assert.strictEqual(value[1], 'initial value'); - return done(err); - }); - client.rpush('blocking list 2', 'initial value', helper.isNumber(1)); - }); - - it('times out after specified time', function (done) { - bclient = redis.createClient.apply(null, args); - bclient.BLPOP('blocking list', 1, function (err, res) { - assert.strictEqual(res, null); - return done(err); - }); - }); - - afterEach(function () { - client.end(true); - bclient.end(true); - }); - }); - }); -}); diff --git a/test/commands/client.spec.js b/test/commands/client.spec.js deleted file mode 100644 index 3214243107c..00000000000 --- a/test/commands/client.spec.js +++ /dev/null @@ -1,155 +0,0 @@ -'use strict'; - -var assert = require('assert'); -var config = require('../lib/config'); -var helper = require('../helper'); -var redis = config.redis; - -describe("The 'client' method", function () { - - helper.allTests(function (ip, args) { - var pattern = /addr=/; - - describe('using ' + ip, function () { - var client; - - beforeEach(function (done) { - client = redis.createClient.apply(null, args); - client.once('ready', function () { - client.flushdb(done); - }); - }); - - afterEach(function () { - client.end(true); - }); - - describe('list', function () { - it('lists connected clients', function (done) { - client.client('LIST', helper.match(pattern, done)); - }); - - it("lists connected clients when invoked with multi's chaining syntax", function (done) { - client.multi().client('list', helper.isType.string()).exec(helper.match(pattern, done)); - }); - - it('lists connected clients when invoked with array syntax on client', function (done) { - client.multi().client(['list']).exec(helper.match(pattern, done)); - }); - - it("lists connected clients when invoked with multi's array syntax", function (done) { - client.multi([ - ['client', 'list'] - ]).exec(helper.match(pattern, done)); - }); - }); - - describe('reply', function () { - describe('as normal command', function () { - it('on', function (done) { - helper.serverVersionAtLeast.call(this, client, [3, 2, 0]); - assert.strictEqual(client.reply, 'ON'); - client.client('reply', 'on', helper.isString('OK')); - assert.strictEqual(client.reply, 'ON'); - client.set('foo', 'bar', done); - }); - - it('off', function (done) { - helper.serverVersionAtLeast.call(this, client, [3, 2, 0]); - assert.strictEqual(client.reply, 'ON'); - client.client(Buffer.from('REPLY'), 'OFF', helper.isUndefined()); - assert.strictEqual(client.reply, 'OFF'); - client.set('foo', 'bar', helper.isUndefined(done)); - }); - - it('skip', function (done) { - helper.serverVersionAtLeast.call(this, client, [3, 2, 0]); - assert.strictEqual(client.reply, 'ON'); - client.client('REPLY', Buffer.from('SKIP'), helper.isUndefined()); - assert.strictEqual(client.reply, 'SKIP_ONE_MORE'); - client.set('foo', 'bar', helper.isUndefined()); - client.get('foo', helper.isString('bar', done)); - }); - }); - - describe('in a batch context', function () { - it('on', function (done) { - helper.serverVersionAtLeast.call(this, client, [3, 2, 0]); - var batch = client.batch(); - assert.strictEqual(client.reply, 'ON'); - batch.client('reply', 'on', helper.isString('OK')); - assert.strictEqual(client.reply, 'ON'); - batch.set('foo', 'bar'); - batch.exec(function (err, res) { - assert.deepEqual(res, ['OK', 'OK']); - done(err); - }); - }); - - it('off', function (done) { - helper.serverVersionAtLeast.call(this, client, [3, 2, 0]); - var batch = client.batch(); - assert.strictEqual(client.reply, 'ON'); - batch.set('hello', 'world'); - batch.client(Buffer.from('REPLY'), Buffer.from('OFF'), helper.isUndefined()); - batch.set('foo', 'bar', helper.isUndefined()); - batch.exec(function (err, res) { - assert.strictEqual(client.reply, 'OFF'); - assert.deepEqual(res, ['OK', undefined, undefined]); - done(err); - }); - }); - - it('skip', function (done) { - helper.serverVersionAtLeast.call(this, client, [3, 2, 0]); - assert.strictEqual(client.reply, 'ON'); - client.batch() - .set('hello', 'world') - .client('REPLY', 'SKIP', helper.isUndefined()) - .set('foo', 'bar', helper.isUndefined()) - .get('foo') - .exec(function (err, res) { - assert.strictEqual(client.reply, 'ON'); - assert.deepEqual(res, ['OK', undefined, undefined, 'bar']); - done(err); - }); - }); - }); - }); - - describe('setname / getname', function () { - var client2; - - beforeEach(function (done) { - client2 = redis.createClient.apply(null, args); - client2.once('ready', function () { - done(); - }); - }); - - afterEach(function () { - client2.end(true); - }); - - it('sets the name', function (done) { - // The querys are auto pipelined and the response is a response to all querys of one client - // per chunk. So the execution order is only garanteed on each client - var end = helper.callFuncAfter(done, 2); - - client.client('setname', 'RUTH'); - client2.client('setname', ['RENEE'], helper.isString('OK')); - client2.client(['setname', 'MARTIN'], helper.isString('OK')); - client2.client('getname', function (err, res) { - assert.equal(res, 'MARTIN'); - end(); - }); - client.client('getname', function (err, res) { - assert.equal(res, 'RUTH'); - end(); - }); - }); - - }); - }); - }); -}); diff --git a/test/commands/dbsize.spec.js b/test/commands/dbsize.spec.js deleted file mode 100644 index bd8b1467898..00000000000 --- a/test/commands/dbsize.spec.js +++ /dev/null @@ -1,95 +0,0 @@ -'use strict'; - -var assert = require('assert'); -var config = require('../lib/config'); -var helper = require('../helper'); -var redis = config.redis; -var uuid = require('uuid'); - -describe("The 'dbsize' method", function () { - - helper.allTests(function (ip, args) { - - describe('using ' + ip, function () { - var key, value; - - beforeEach(function () { - key = uuid.v4(); - value = uuid.v4(); - }); - - describe('when not connected', function () { - var client; - - beforeEach(function (done) { - client = redis.createClient.apply(null, args); - client.once('ready', function () { - client.quit(); - }); - client.on('end', done); - }); - - it('reports an error', function (done) { - client.dbsize([], function (err, res) { - assert(err.message.match(/The connection is already closed/)); - done(); - }); - }); - }); - - describe('when connected', function () { - var client; - - beforeEach(function (done) { - client = redis.createClient.apply(null, args); - client.once('ready', function () { - client.flushdb(function (err, res) { - helper.isString('OK')(err, res); - done(); - }); - }); - }); - - afterEach(function () { - client.end(true); - }); - - it('returns a zero db size', function (done) { - client.DBSIZE([], function (err, res) { - helper.isNotError()(err, res); - helper.isType.number()(err, res); - assert.strictEqual(res, 0, 'Initial db size should be 0'); - done(); - }); - }); - - describe('when more data is added to Redis', function () { - var oldSize; - - beforeEach(function (done) { - client.dbsize(function (err, res) { - helper.isType.number()(err, res); - assert.strictEqual(res, 0, 'Initial db size should be 0'); - - oldSize = res; - - client.set(key, value, function (err, res) { - helper.isNotError()(err, res); - done(); - }); - }); - }); - - it('returns a larger db size', function (done) { - client.dbsize([], function (err, res) { - helper.isNotError()(err, res); - helper.isType.positiveNumber()(err, res); - assert.strictEqual(true, (oldSize < res), 'Adding data should increase db size.'); - done(); - }); - }); - }); - }); - }); - }); -}); diff --git a/test/commands/del.spec.js b/test/commands/del.spec.js deleted file mode 100644 index 86c1f4bb3af..00000000000 --- a/test/commands/del.spec.js +++ /dev/null @@ -1,57 +0,0 @@ -'use strict'; - -var config = require('../lib/config'); -var helper = require('../helper'); -var redis = config.redis; - -describe("The 'del' method", function () { - - helper.allTests(function (ip, args) { - - describe('using ' + ip, function () { - var client; - - beforeEach(function (done) { - client = redis.createClient.apply(null, args); - client.once('ready', function () { - client.flushdb(done); - }); - }); - - it('allows a single key to be deleted', function (done) { - client.set('foo', 'bar'); - client.DEL('foo', helper.isNumber(1)); - client.get('foo', helper.isNull(done)); - }); - - it('allows del to be called on a key that does not exist', function (done) { - client.del('foo', helper.isNumber(0, done)); - }); - - it('allows multiple keys to be deleted', function (done) { - client.mset('foo', 'bar', 'apple', 'banana'); - client.del('foo', 'apple', helper.isNumber(2)); - client.get('foo', helper.isNull()); - client.get('apple', helper.isNull(done)); - }); - - it('allows multiple keys to be deleted with the array syntax', function (done) { - client.mset('foo', 'bar', 'apple', 'banana'); - client.del(['foo', 'apple'], helper.isNumber(2)); - client.get('foo', helper.isNull()); - client.get('apple', helper.isNull(done)); - }); - - it('allows multiple keys to be deleted with the array syntax and no callback', function (done) { - client.mset('foo', 'bar', 'apple', 'banana'); - client.del(['foo', 'apple']); - client.get('foo', helper.isNull()); - client.get('apple', helper.isNull(done)); - }); - - afterEach(function () { - client.end(true); - }); - }); - }); -}); diff --git a/test/commands/eval.spec.js b/test/commands/eval.spec.js deleted file mode 100644 index db74372db4d..00000000000 --- a/test/commands/eval.spec.js +++ /dev/null @@ -1,210 +0,0 @@ -'use strict'; - -var assert = require('assert'); -var config = require('../lib/config'); -var crypto = require('crypto'); -var helper = require('../helper'); -var redis = config.redis; - -describe("The 'eval' method", function () { - - helper.allTests(function (ip, args) { - - describe('using ' + ip, function () { - var client; - var source = "return redis.call('set', 'sha', 'test')"; - - beforeEach(function (done) { - client = redis.createClient.apply(null, args); - client.once('ready', function () { - client.flushdb(done); - }); - }); - - afterEach(function () { - client.end(true); - }); - - it('converts a float to an integer when evaluated', function (done) { - client.eval('return 100.5', 0, helper.isNumber(100, done)); - }); - - it('returns a string', function (done) { - client.eval("return 'hello world'", 0, helper.isString('hello world', done)); - }); - - it('converts boolean true to integer 1', function (done) { - client.eval('return true', 0, helper.isNumber(1, done)); - }); - - it('converts boolean false to null', function (done) { - client.eval('return false', 0, helper.isNull(done)); - }); - - it('converts lua status code to string representation', function (done) { - client.eval("return {ok='fine'}", 0, helper.isString('fine', done)); - }); - - it('converts lua error to an error response', function (done) { - client.eval("return {err='this is an error'}", 0, function (err) { - assert(err.code === undefined); - helper.isError()(err); - done(); - }); - }); - - it('represents a lua table appropritely', function (done) { - client.eval("return {1,2,3,'ciao',{1,2}}", 0, function (err, res) { - assert.strictEqual(5, res.length); - assert.strictEqual(1, res[0]); - assert.strictEqual(2, res[1]); - assert.strictEqual(3, res[2]); - assert.strictEqual('ciao', res[3]); - assert.strictEqual(2, res[4].length); - assert.strictEqual(1, res[4][0]); - assert.strictEqual(2, res[4][1]); - return done(); - }); - }); - - it('populates keys and argv correctly', function (done) { - client.eval('return {KEYS[1],KEYS[2],ARGV[1],ARGV[2]}', 2, 'a', 'b', 'c', 'd', function (err, res) { - assert.strictEqual(4, res.length); - assert.strictEqual('a', res[0]); - assert.strictEqual('b', res[1]); - assert.strictEqual('c', res[2]); - assert.strictEqual('d', res[3]); - return done(); - }); - }); - - it('allows arguments to be provided in array rather than as multiple parameters', function (done) { - client.eval(['return {KEYS[1],KEYS[2],ARGV[1],ARGV[2]}', 2, 'a', 'b', 'c', 'd'], function (err, res) { - assert.strictEqual(4, res.length); - assert.strictEqual('a', res[0]); - assert.strictEqual('b', res[1]); - assert.strictEqual('c', res[2]); - assert.strictEqual('d', res[3]); - return done(); - }); - }); - - it('allows a script to be executed that accesses the redis API without callback', function (done) { - client.eval(source, 0); - client.get('sha', helper.isString('test', done)); - }); - - describe('evalsha', function () { - var sha = crypto.createHash('sha1').update(source).digest('hex'); - - it('allows a script to be executed that accesses the redis API', function (done) { - client.eval(source, 0, helper.isString('OK')); - client.get('sha', helper.isString('test', done)); - }); - - it('can execute a script if the SHA exists', function (done) { - client.evalsha(sha, 0, helper.isString('OK')); - client.get('sha', helper.isString('test', done)); - }); - - it('returns an error if SHA does not exist', function (done) { - client.evalsha('ffffffffffffffffffffffffffffffffffffffff', 0, helper.isError(done)); - }); - - it('emit an error if SHA does not exist without any callback', function (done) { - client.evalsha('ffffffffffffffffffffffffffffffffffffffff', 0); - client.on('error', function (err) { - assert.equal(err.code, 'NOSCRIPT'); - assert(/NOSCRIPT No matching script. Please use EVAL./.test(err.message)); - done(); - }); - }); - - it('emits an error if SHA does not exist and no callback has been provided', function (done) { - client.on('error', function (err) { - assert.equal(err.message, 'NOSCRIPT No matching script. Please use EVAL.'); - done(); - }); - client.evalsha('ffffffffffffffffffffffffffffffffffffffff', 0); - }); - }); - - it('allows a key to be incremented, and performs appropriate conversion from LUA type', function (done) { - client.set('incr key', 0, function (err, reply) { - if (err) return done(err); - client.eval("local foo = redis.call('incr','incr key')\nreturn {type(foo),foo}", 0, function (err, res) { - assert.strictEqual(2, res.length); - assert.strictEqual('number', res[0]); - assert.strictEqual(1, res[1]); - return done(err); - }); - }); - }); - - it('allows a bulk operation to be performed, and performs appropriate conversion from LUA type', function (done) { - client.set('bulk reply key', 'bulk reply value', function (err, res) { - client.eval("local foo = redis.call('get','bulk reply key'); return {type(foo),foo}", 0, function (err, res) { - assert.strictEqual(2, res.length); - assert.strictEqual('string', res[0]); - assert.strictEqual('bulk reply value', res[1]); - return done(err); - }); - }); - }); - - it('allows a multi mulk operation to be performed, with the appropriate type conversion', function (done) { - client.multi() - .del('mylist') - .rpush('mylist', 'a') - .rpush('mylist', 'b') - .rpush('mylist', 'c') - .exec(function (err, replies) { - if (err) return done(err); - client.eval("local foo = redis.call('lrange','mylist',0,-1); return {type(foo),foo[1],foo[2],foo[3],# foo}", 0, function (err, res) { - assert.strictEqual(5, res.length); - assert.strictEqual('table', res[0]); - assert.strictEqual('a', res[1]); - assert.strictEqual('b', res[2]); - assert.strictEqual('c', res[3]); - assert.strictEqual(3, res[4]); - return done(err); - }); - }); - }); - - it('returns an appropriate representation of Lua status reply', function (done) { - client.eval("local foo = redis.call('set','mykey','myval'); return {type(foo),foo['ok']}", 0, function (err, res) { - assert.strictEqual(2, res.length); - assert.strictEqual('table', res[0]); - assert.strictEqual('OK', res[1]); - return done(err); - }); - }); - - it('returns an appropriate representation of a Lua error reply', function (done) { - client.set('error reply key', 'error reply value', function (err, res) { - if (err) return done(err); - client.eval("local foo = redis.pcall('incr','error reply key'); return {type(foo),foo['err']}", 0, function (err, res) { - assert.strictEqual(2, res.length); - assert.strictEqual('table', res[0]); - assert.strictEqual('ERR value is not an integer or out of range', res[1]); - return done(err); - }); - }); - }); - - it('returns an appropriate representation of a Lua nil reply', function (done) { - client.del('nil reply key', function (err, res) { - if (err) return done(err); - client.eval("local foo = redis.call('get','nil reply key'); return {type(foo),foo == false}", 0, function (err, res) { - if (err) throw err; - assert.strictEqual(2, res.length); - assert.strictEqual('boolean', res[0]); - assert.strictEqual(1, res[1]); - return done(err); - }); - }); - }); - }); - }); -}); diff --git a/test/commands/exists.spec.js b/test/commands/exists.spec.js deleted file mode 100644 index 399a0382f49..00000000000 --- a/test/commands/exists.spec.js +++ /dev/null @@ -1,40 +0,0 @@ -'use strict'; - -var config = require('../lib/config'); -var helper = require('../helper'); -var redis = config.redis; - -describe("The 'exists' method", function () { - - helper.allTests(function (ip, args) { - - describe('using ' + ip, function () { - var client; - - beforeEach(function (done) { - client = redis.createClient.apply(null, args); - client.once('ready', function () { - client.flushdb(done); - }); - }); - - it('returns 1 if the key exists', function (done) { - client.set('foo', 'bar'); - client.EXISTS('foo', helper.isNumber(1, done)); - }); - - it('returns 1 if the key exists with array syntax', function (done) { - client.set('foo', 'bar'); - client.EXISTS(['foo'], helper.isNumber(1, done)); - }); - - it('returns 0 if the key does not exist', function (done) { - client.exists('bar', helper.isNumber(0, done)); - }); - - afterEach(function () { - client.end(true); - }); - }); - }); -}); diff --git a/test/commands/expire.spec.js b/test/commands/expire.spec.js deleted file mode 100644 index 2891890edc0..00000000000 --- a/test/commands/expire.spec.js +++ /dev/null @@ -1,42 +0,0 @@ -'use strict'; - -var config = require('../lib/config'); -var helper = require('../helper'); -var redis = config.redis; - -describe("The 'expire' method", function () { - - helper.allTests(function (ip, args) { - - describe('using ' + ip, function () { - var client; - - beforeEach(function (done) { - client = redis.createClient.apply(null, args); - client.once('ready', function () { - client.flushdb(done); - }); - }); - - it('expires key after timeout', function (done) { - client.set(['expiry key', 'bar'], helper.isString('OK')); - client.EXPIRE('expiry key', '1', helper.isNumber(1)); - setTimeout(function () { - client.exists(['expiry key'], helper.isNumber(0, done)); - }, 1050); - }); - - it('expires key after timeout with array syntax', function (done) { - client.set(['expiry key', 'bar'], helper.isString('OK')); - client.EXPIRE(['expiry key', '1'], helper.isNumber(1)); - setTimeout(function () { - client.exists(['expiry key'], helper.isNumber(0, done)); - }, 1050); - }); - - afterEach(function () { - client.end(true); - }); - }); - }); -}); diff --git a/test/commands/flushdb.spec.js b/test/commands/flushdb.spec.js deleted file mode 100644 index a4f761d3753..00000000000 --- a/test/commands/flushdb.spec.js +++ /dev/null @@ -1,105 +0,0 @@ -'use strict'; - -var assert = require('assert'); -var config = require('../lib/config'); -var helper = require('../helper'); -var redis = config.redis; -var uuid = require('uuid'); - -describe("The 'flushdb' method", function () { - - helper.allTests(function (ip, args) { - - describe('using ' + ip, function () { - var key, key2; - - beforeEach(function () { - key = uuid.v4(); - key2 = uuid.v4(); - }); - - describe('when not connected', function () { - var client; - - beforeEach(function (done) { - client = redis.createClient.apply(null, args); - client.once('ready', function () { - client.quit(); - }); - client.on('end', done); - }); - - it('reports an error', function (done) { - client.flushdb(function (err, res) { - assert(err.message.match(/The connection is already closed/)); - done(); - }); - }); - }); - - describe('when connected', function () { - var client; - - beforeEach(function (done) { - client = redis.createClient.apply(null, args); - client.once('ready', function () { - done(); - }); - }); - - afterEach(function () { - client.end(true); - }); - - describe('when there is data in Redis', function () { - - beforeEach(function (done) { - client.mset(key, uuid.v4(), key2, uuid.v4(), helper.isNotError()); - client.dbsize([], function (err, res) { - helper.isType.positiveNumber()(err, res); - assert.equal(res, 2, 'Two keys should have been inserted'); - done(); - }); - }); - - it('deletes all the keys', function (done) { - client.flushdb(function (err, res) { - assert.equal(res, 'OK'); - client.mget(key, key2, function (err, res) { - assert.strictEqual(null, err, 'Unexpected error returned'); - assert.strictEqual(true, Array.isArray(res), 'Results object should be an array.'); - assert.strictEqual(2, res.length, 'Results array should have length 2.'); - assert.strictEqual(null, res[0], 'Redis key should have been flushed.'); - assert.strictEqual(null, res[1], 'Redis key should have been flushed.'); - done(err); - }); - }); - }); - - it('results in a db size of zero', function (done) { - client.flushdb(function (err, res) { - client.dbsize([], function (err, res) { - helper.isNotError()(err, res); - helper.isType.number()(err, res); - assert.strictEqual(0, res, 'Flushing db should result in db size 0'); - done(); - }); - }); - }); - - it('results in a db size of zero without a callback', function (done) { - client.flushdb(); - setTimeout(function (err, res) { - client.dbsize(function (err, res) { - helper.isNotError()(err, res); - helper.isType.number()(err, res); - assert.strictEqual(0, res, 'Flushing db should result in db size 0'); - done(); - }); - }, 25); - }); - }); - }); - }); - }); -}); diff --git a/test/commands/geoadd.spec.js b/test/commands/geoadd.spec.js deleted file mode 100644 index b45df7c83a9..00000000000 --- a/test/commands/geoadd.spec.js +++ /dev/null @@ -1,35 +0,0 @@ -'use strict'; - -var config = require('../lib/config'); -var helper = require('../helper'); -var redis = config.redis; - -describe("The 'geoadd' method", function () { - - helper.allTests(function (ip, args) { - - describe('using ' + ip, function () { - var client; - - beforeEach(function (done) { - client = redis.createClient.apply(null, args); - client.once('ready', function () { - client.flushdb(done); - }); - }); - - it('returns 1 if the key exists', function (done) { - helper.serverVersionAtLeast.call(this, client, [3, 2, 0]); - client.geoadd('mycity:21:0:location', '13.361389', '38.115556', 'COR', function (err, res) { - console.log(err, res); - // geoadd is still in the unstable branch. As soon as it reaches the stable one, activate this test - done(); - }); - }); - - afterEach(function () { - client.end(true); - }); - }); - }); -}); diff --git a/test/commands/get.spec.js b/test/commands/get.spec.js deleted file mode 100644 index acbfc0d10db..00000000000 --- a/test/commands/get.spec.js +++ /dev/null @@ -1,95 +0,0 @@ -'use strict'; - -var assert = require('assert'); -var config = require('../lib/config'); -var helper = require('../helper'); -var redis = config.redis; -var uuid = require('uuid'); - -describe("The 'get' method", function () { - - helper.allTests(function (ip, args) { - - describe('using ' + ip, function () { - var key, value; - - beforeEach(function () { - key = uuid.v4(); - value = uuid.v4(); - }); - - describe('when not connected', function () { - var client; - - beforeEach(function (done) { - client = redis.createClient.apply(null, args); - client.once('ready', function () { - client.quit(); - }); - client.on('end', done); - }); - - it('reports an error', function (done) { - client.get(key, function (err, res) { - assert(err.message.match(/The connection is already closed/)); - done(); - }); - }); - - it('reports an error promisified', function () { - return client.getAsync(key).then(assert, function (err) { - assert(err.message.match(/The connection is already closed/)); - }); - }); - }); - - describe('when connected', function () { - var client; - - beforeEach(function (done) { - client = redis.createClient.apply(null, args); - client.once('ready', function () { - done(); - }); - }); - - afterEach(function () { - client.end(true); - }); - - describe('when the key exists in Redis', function () { - beforeEach(function (done) { - client.set(key, value, function (err, res) { - helper.isNotError()(err, res); - done(); - }); - }); - - it('gets the value correctly', function (done) { - client.GET(key, function (err, res) { - helper.isString(value)(err, res); - done(err); - }); - }); - - it("should not throw on a get without callback (even if it's not useful)", function (done) { - client.GET(key); - client.on('error', function (err) { - throw err; - }); - setTimeout(done, 25); - }); - }); - - describe('when the key does not exist in Redis', function () { - it('gets a null value', function (done) { - client.get(key, function (err, res) { - helper.isNull()(err, res); - done(err); - }); - }); - }); - }); - }); - }); -}); diff --git a/test/commands/getset.spec.js b/test/commands/getset.spec.js deleted file mode 100644 index 48dd7e9d739..00000000000 --- a/test/commands/getset.spec.js +++ /dev/null @@ -1,105 +0,0 @@ -'use strict'; - -var assert = require('assert'); -var config = require('../lib/config'); -var helper = require('../helper'); -var redis = config.redis; -var uuid = require('uuid'); - -describe("The 'getset' method", function () { - - helper.allTests(function (ip, args) { - - describe('using ' + ip, function () { - var key, value, value2; - - beforeEach(function () { - key = uuid.v4(); - value = uuid.v4(); - value2 = uuid.v4(); - }); - - describe('when not connected', function () { - var client; - - beforeEach(function (done) { - client = redis.createClient.apply(null, args); - client.once('ready', function () { - client.quit(); - }); - client.on('end', done); - }); - - it('reports an error', function (done) { - client.get(key, function (err, res) { - assert(err.message.match(/The connection is already closed/)); - done(); - }); - }); - }); - - describe('when connected', function () { - var client; - - beforeEach(function (done) { - client = redis.createClient.apply(null, args); - client.once('ready', function () { - done(); - }); - }); - - afterEach(function () { - client.end(true); - }); - - describe('when the key exists in Redis', function () { - beforeEach(function (done) { - client.set(key, value, function (err, res) { - helper.isNotError()(err, res); - done(); - }); - }); - - it('gets the value correctly', function (done) { - client.GETSET(key, value2, function (err, res) { - helper.isString(value)(err, res); - client.get(key, function (err, res) { - helper.isString(value2)(err, res); - done(err); - }); - }); - }); - - it('gets the value correctly with array syntax', function (done) { - client.GETSET([key, value2], function (err, res) { - helper.isString(value)(err, res); - client.get(key, function (err, res) { - helper.isString(value2)(err, res); - done(err); - }); - }); - }); - - it('gets the value correctly with array syntax style 2', function (done) { - client.GETSET(key, [value2], function (err, res) { - helper.isString(value)(err, res); - client.get(key, function (err, res) { - helper.isString(value2)(err, res); - done(err); - }); - }); - }); - }); - - describe('when the key does not exist in Redis', function () { - it('gets a null value', function (done) { - client.getset(key, value, function (err, res) { - helper.isNull()(err, res); - done(err); - }); - }); - }); - }); - }); - }); -}); diff --git a/test/commands/hgetall.spec.js b/test/commands/hgetall.spec.js deleted file mode 100644 index 5bfa609d0bc..00000000000 --- a/test/commands/hgetall.spec.js +++ /dev/null @@ -1,83 +0,0 @@ -'use strict'; - -var assert = require('assert'); -var config = require('../lib/config'); -var helper = require('../helper'); -var redis = config.redis; - -describe("The 'hgetall' method", function () { - - helper.allTests(function (ip, args) { - - describe('using ' + ip, function () { - var client; - - describe('regular client', function () { - - beforeEach(function (done) { - client = redis.createClient.apply(null, args); - client.once('ready', function () { - client.flushdb(done); - }); - }); - - it('handles simple keys and values', function (done) { - client.hmset(['hosts', 'hasOwnProperty', '1', 'another', '23', 'home', '1234'], helper.isString('OK')); - client.HGETALL(['hosts'], function (err, obj) { - assert.strictEqual(3, Object.keys(obj).length); - assert.strictEqual('1', obj.hasOwnProperty.toString()); - assert.strictEqual('23', obj.another.toString()); - assert.strictEqual('1234', obj.home.toString()); - done(err); - }); - }); - - it('handles fetching keys set using an object', function (done) { - client.batch().HMSET('msg_test', { message: 'hello' }, undefined).exec(); - client.hgetall('msg_test', function (err, obj) { - assert.strictEqual(1, Object.keys(obj).length); - assert.strictEqual(obj.message, 'hello'); - done(err); - }); - }); - - it('handles fetching a messing key', function (done) { - client.hgetall('missing', function (err, obj) { - assert.strictEqual(null, obj); - done(err); - }); - }); - }); - - describe('binary client', function () { - var args = config.configureClient(ip, { - return_buffers: true - }); - - beforeEach(function (done) { - client = redis.createClient.apply(null, args); - client.once('ready', function () { - client.flushdb(done); - }); - }); - - it('returns binary results', function (done) { - client.hmset(['bhosts', 'mjr', '1', 'another', '23', 'home', '1234', Buffer.from([0xAA, 0xBB, 0x00, 0xF0]), Buffer.from([0xCC, 0xDD, 0x00, 0xF0])], helper.isString('OK')); - client.HGETALL('bhosts', function (err, obj) { - assert.strictEqual(4, Object.keys(obj).length); - assert.strictEqual('1', obj.mjr.toString()); - assert.strictEqual('23', obj.another.toString()); - assert.strictEqual('1234', obj.home.toString()); - assert.strictEqual((Buffer.from([0xAA, 0xBB, 0x00, 0xF0])).toString('binary'), Object.keys(obj)[3]); - assert.strictEqual((Buffer.from([0xCC, 0xDD, 0x00, 0xF0])).toString('binary'), obj[(Buffer.from([0xAA, 0xBB, 0x00, 0xF0])).toString('binary')].toString('binary')); - return done(err); - }); - }); - }); - - afterEach(function () { - client.end(true); - }); - }); - }); -}); diff --git a/test/commands/hincrby.spec.js b/test/commands/hincrby.spec.js deleted file mode 100644 index 10b4523b3f7..00000000000 --- a/test/commands/hincrby.spec.js +++ /dev/null @@ -1,40 +0,0 @@ -'use strict'; - -var config = require('../lib/config'); -var helper = require('../helper'); -var redis = config.redis; - -describe("The 'hincrby' method", function () { - - helper.allTests(function (ip, args) { - - describe('using ' + ip, function () { - var client; - var hash = 'test hash'; - - beforeEach(function (done) { - client = redis.createClient.apply(null, args); - client.once('ready', function () { - client.flushdb(done); - }); - }); - - it('increments a key that has already been set', function (done) { - var field = 'field 1'; - - client.HSET(hash, field, 33); - client.hincrby(hash, field, 10, helper.isNumber(43, done)); - }); - - it('increments a key that has not been set', function (done) { - var field = 'field 2'; - - client.HINCRBY(hash, field, 10, helper.isNumber(10, done)); - }); - - afterEach(function () { - client.end(true); - }); - }); - }); -}); diff --git a/test/commands/hlen.spec.js b/test/commands/hlen.spec.js deleted file mode 100644 index 874cb2970a1..00000000000 --- a/test/commands/hlen.spec.js +++ /dev/null @@ -1,38 +0,0 @@ -'use strict'; - -var config = require('../lib/config'); -var helper = require('../helper'); -var redis = config.redis; - -describe("The 'hlen' method", function () { - - helper.allTests(function (ip, args) { - - describe('using ' + ip, function () { - var client; - - beforeEach(function (done) { - client = redis.createClient.apply(null, args); - client.once('ready', function () { - client.flushdb(done); - }); - }); - - it('reports the count of keys', function (done) { - var hash = 'test hash'; - var field1 = Buffer.from('0123456789'); - var value1 = Buffer.from('abcdefghij'); - var field2 = Buffer.alloc(0); - var value2 = Buffer.alloc(0); - - client.HSET(hash, field1, value1, helper.isNumber(1)); - client.HSET(hash, field2, value2, helper.isNumber(1)); - client.HLEN(hash, helper.isNumber(2, done)); - }); - - afterEach(function () { - client.end(true); - }); - }); - }); -}); diff --git a/test/commands/hmget.spec.js b/test/commands/hmget.spec.js deleted file mode 100644 index 3676b5b7312..00000000000 --- a/test/commands/hmget.spec.js +++ /dev/null @@ -1,71 +0,0 @@ -'use strict'; - -var assert = require('assert'); -var config = require('../lib/config'); -var helper = require('../helper'); -var redis = config.redis; - -describe("The 'hmget' method", function () { - - helper.allTests(function (ip, args) { - - describe('using ' + ip, function () { - var client; - var hash = 'test hash'; - - beforeEach(function (done) { - client = redis.createClient.apply(null, args); - client.once('error', done); - client.once('ready', function () { - client.flushdb(); - client.HMSET(hash, {'0123456789': 'abcdefghij', 'some manner of key': 'a type of value'}, helper.isString('OK', done)); - }); - }); - - it('allows keys to be specified using multiple arguments', function (done) { - client.hmget(hash, '0123456789', 'some manner of key', function (err, reply) { - assert.strictEqual('abcdefghij', reply[0].toString()); - assert.strictEqual('a type of value', reply[1].toString()); - return done(err); - }); - }); - - it('allows keys to be specified by passing an array without manipulating the array', function (done) { - var data = ['0123456789', 'some manner of key']; - client.HMGET(hash, data, function (err, reply) { - assert.strictEqual(data.length, 2); - assert.strictEqual('abcdefghij', reply[0].toString()); - assert.strictEqual('a type of value', reply[1].toString()); - return done(err); - }); - }); - - it('allows keys to be specified by passing an array as first argument', function (done) { - client.HMGET([hash, '0123456789', 'some manner of key'], function (err, reply) { - assert.strictEqual('abcdefghij', reply[0].toString()); - assert.strictEqual('a type of value', reply[1].toString()); - return done(err); - }); - }); - - it('allows a single key to be specified in an array', function (done) { - client.HMGET(hash, ['0123456789'], function (err, reply) { - assert.strictEqual('abcdefghij', reply[0].toString()); - return done(err); - }); - }); - - it('allows keys to be specified that have not yet been set', function (done) { - client.HMGET(hash, 'missing thing', 'another missing thing', function (err, reply) { - assert.strictEqual(null, reply[0]); - assert.strictEqual(null, reply[1]); - return done(err); - }); - }); - - afterEach(function () { - client.end(true); - }); - }); - }); -}); diff --git a/test/commands/hmset.spec.js b/test/commands/hmset.spec.js deleted file mode 100644 index 8ba54ecc3f2..00000000000 --- a/test/commands/hmset.spec.js +++ /dev/null @@ -1,117 +0,0 @@ -'use strict'; - -var assert = require('assert'); -var config = require('../lib/config'); -var helper = require('../helper'); -var redis = config.redis; - -describe("The 'hmset' method", function () { - - helper.allTests(function (ip, args) { - - describe('using ' + ip, function () { - var client; - var hash = 'test hash'; - - beforeEach(function (done) { - client = redis.createClient.apply(null, args); - client.once('ready', function () { - client.flushdb(done); - }); - }); - - it('handles redis-style syntax', function (done) { - client.HMSET(hash, '0123456789', 'abcdefghij', 'some manner of key', 'a type of value', 'otherTypes', 555, helper.isString('OK')); - client.HGETALL(hash, function (err, obj) { - assert.equal(obj['0123456789'], 'abcdefghij'); - assert.equal(obj['some manner of key'], 'a type of value'); - return done(err); - }); - }); - - it('handles object-style syntax', function (done) { - client.hmset(hash, {'0123456789': 'abcdefghij', 'some manner of key': 'a type of value', 'otherTypes': 555}, helper.isString('OK')); - client.HGETALL(hash, function (err, obj) { - assert.equal(obj['0123456789'], 'abcdefghij'); - assert.equal(obj['some manner of key'], 'a type of value'); - return done(err); - }); - }); - - it('handles object-style syntax and the key being a number', function (done) { - client.HMSET(231232, {'0123456789': 'abcdefghij', 'some manner of key': 'a type of value', 'otherTypes': 555}, undefined); - client.HGETALL(231232, function (err, obj) { - assert.equal(obj['0123456789'], 'abcdefghij'); - assert.equal(obj['some manner of key'], 'a type of value'); - return done(err); - }); - }); - - it('allows a numeric key', function (done) { - client.HMSET(hash, 99, 'banana', helper.isString('OK')); - client.HGETALL(hash, function (err, obj) { - assert.equal(obj['99'], 'banana'); - return done(err); - }); - }); - - it('allows a numeric key without callback', function (done) { - client.HMSET(hash, 99, 'banana', 'test', 25); - client.HGETALL(hash, function (err, obj) { - assert.equal(obj['99'], 'banana'); - assert.equal(obj.test, '25'); - return done(err); - }); - }); - - it('allows an array without callback', function (done) { - client.HMSET([hash, 99, 'banana', 'test', 25]); - client.HGETALL(hash, function (err, obj) { - assert.equal(obj['99'], 'banana'); - assert.equal(obj.test, '25'); - return done(err); - }); - }); - - it('allows an array and a callback', function (done) { - client.HMSET([hash, 99, 'banana', 'test', 25], helper.isString('OK')); - client.HGETALL(hash, function (err, obj) { - assert.equal(obj['99'], 'banana'); - assert.equal(obj.test, '25'); - return done(err); - }); - }); - - it('allows a key plus array without callback', function (done) { - client.HMSET(hash, [99, 'banana', 'test', 25]); - client.HGETALL(hash, function (err, obj) { - assert.equal(obj['99'], 'banana'); - assert.equal(obj.test, '25'); - return done(err); - }); - }); - - it('allows a key plus array and a callback', function (done) { - client.HMSET(hash, [99, 'banana', 'test', 25], helper.isString('OK')); - client.HGETALL(hash, function (err, obj) { - assert.equal(obj['99'], 'banana'); - assert.equal(obj.test, '25'); - return done(err); - }); - }); - - it('handles object-style syntax without callback', function (done) { - client.HMSET(hash, {'0123456789': 'abcdefghij', 'some manner of key': 'a type of value'}); - client.HGETALL(hash, function (err, obj) { - assert.equal(obj['0123456789'], 'abcdefghij'); - assert.equal(obj['some manner of key'], 'a type of value'); - return done(err); - }); - }); - - afterEach(function () { - client.end(true); - }); - }); - }); -}); diff --git a/test/commands/hset.spec.js b/test/commands/hset.spec.js deleted file mode 100644 index 746176f3d8c..00000000000 --- a/test/commands/hset.spec.js +++ /dev/null @@ -1,85 +0,0 @@ -'use strict'; - -var assert = require('assert'); -var config = require('../lib/config'); -var helper = require('../helper'); -var redis = config.redis; - -describe("The 'hset' method", function () { - - helper.allTests(function (ip, args) { - - describe('using ' + ip, function () { - var client; - var hash = 'test hash'; - - beforeEach(function (done) { - client = redis.createClient.apply(null, args); - client.once('ready', function () { - client.flushdb(done); - }); - }); - - it('allows a value to be set in a hash', function (done) { - var field = Buffer.from('0123456789'); - var value = Buffer.from('abcdefghij'); - - client.hset(hash, field, value, helper.isNumber(1)); - client.HGET(hash, field, helper.isString(value.toString(), done)); - }); - - it('handles an empty value', function (done) { - var field = Buffer.from('0123456789'); - var value = Buffer.alloc(0); - - client.HSET(hash, field, value, helper.isNumber(1)); - client.HGET([hash, field], helper.isString('', done)); - }); - - it('handles empty key and value', function (done) { - var field = Buffer.alloc(0); - var value = Buffer.alloc(0); - client.HSET([hash, field, value], function (err, res) { - assert.strictEqual(res, 1); - client.HSET(hash, field, value, helper.isNumber(0, done)); - }); - }); - - it('errors if someone passed a array either as field or as value', function (done) { - var hash = 'test hash'; - var field = 'array'; - var value = ['array contents']; - try { - client.HMSET(hash, field, value); - } catch (error) { - assert(/node_redis: The HMSET command contains a invalid argument type./.test(error.message)); - done(); - } - }); - - it('does not error when a buffer and date are set as values on the same hash', function (done) { - var hash = 'test hash'; - var field1 = 'buffer'; - var value1 = Buffer.from('abcdefghij'); - var field2 = 'date'; - var value2 = new Date(); - - client.HMSET(hash, field1, value1, field2, value2, helper.isString('OK', done)); - }); - - it('does not error when a buffer and date are set as fields on the same hash', function (done) { - var hash = 'test hash'; - var value1 = 'buffer'; - var field1 = Buffer.from('abcdefghij'); - var value2 = 'date'; - var field2 = new Date(); - - client.HMSET(hash, field1, value1, field2, value2, helper.isString('OK', done)); - }); - - afterEach(function () { - client.end(true); - }); - }); - }); -}); diff --git a/test/commands/incr.spec.js b/test/commands/incr.spec.js deleted file mode 100644 index 0caab84859a..00000000000 --- a/test/commands/incr.spec.js +++ /dev/null @@ -1,76 +0,0 @@ -'use strict'; - -var assert = require('assert'); -var config = require('../lib/config'); -var helper = require('../helper'); -var redis = config.redis; - -describe("The 'incr' method", function () { - - helper.allTests(function (ip, args) { - - describe('using ' + ip, function () { - - describe('when connected and a value in Redis', function () { - - var client; - var key = 'ABOVE_SAFE_JAVASCRIPT_INTEGER'; - var MAX_SAFE_INTEGER = Number.MAX_SAFE_INTEGER || 9007199254740991; // Backwards compatible - - afterEach(function () { - client.end(true); - }); - - /* - Number.MAX_SAFE_INTEGER === Math.pow(2, 53) - 1 === 9007199254740991 - - 9007199254740992 -> 9007199254740992 - 9007199254740993 -> 9007199254740992 - 9007199254740994 -> 9007199254740994 - 9007199254740995 -> 9007199254740996 - 9007199254740996 -> 9007199254740996 - 9007199254740997 -> 9007199254740996 - ... - */ - it('count above the safe integers as numbers', function (done) { - client = redis.createClient.apply(null, args); - // Set a value to the maximum safe allowed javascript number (2^53) - 1 - client.set(key, MAX_SAFE_INTEGER, helper.isNotError()); - client.INCR(key, helper.isNumber(MAX_SAFE_INTEGER + 1)); - client.INCR(key, helper.isNumber(MAX_SAFE_INTEGER + 2)); - client.INCR(key, helper.isNumber(MAX_SAFE_INTEGER + 3)); - client.INCR(key, helper.isNumber(MAX_SAFE_INTEGER + 4)); - client.INCR(key, helper.isNumber(MAX_SAFE_INTEGER + 5)); - client.INCR(key, function (err, res) { - helper.isNumber(MAX_SAFE_INTEGER + 6)(err, res); - assert.strictEqual(typeof res, 'number'); - }); - client.INCR(key, helper.isNumber(MAX_SAFE_INTEGER + 7)); - client.INCR(key, helper.isNumber(MAX_SAFE_INTEGER + 8)); - client.INCR(key, helper.isNumber(MAX_SAFE_INTEGER + 9)); - client.INCR(key, helper.isNumber(MAX_SAFE_INTEGER + 10, done)); - }); - - it('count above the safe integers as strings', function (done) { - args[2].string_numbers = true; - client = redis.createClient.apply(null, args); - // Set a value to the maximum safe allowed javascript number (2^53) - client.set(key, MAX_SAFE_INTEGER, helper.isNotError()); - client.incr(key, helper.isString('9007199254740992')); - client.incr(key, helper.isString('9007199254740993')); - client.incr(key, helper.isString('9007199254740994')); - client.incr(key, helper.isString('9007199254740995')); - client.incr(key, helper.isString('9007199254740996')); - client.incr(key, function (err, res) { - helper.isString('9007199254740997')(err, res); - assert.strictEqual(typeof res, 'string'); - }); - client.incr(key, helper.isString('9007199254740998')); - client.incr(key, helper.isString('9007199254740999')); - client.incr(key, helper.isString('9007199254741000')); - client.incr(key, helper.isString('9007199254741001', done)); - }); - }); - }); - }); -}); diff --git a/test/commands/info.spec.js b/test/commands/info.spec.js deleted file mode 100644 index 4e5bb481fc9..00000000000 --- a/test/commands/info.spec.js +++ /dev/null @@ -1,79 +0,0 @@ -'use strict'; - -var assert = require('assert'); -var config = require('../lib/config'); -var helper = require('../helper'); -var redis = config.redis; - -describe("The 'info' method", function () { - - helper.allTests(function (ip, args) { - - describe('using ' + ip, function () { - var client; - - beforeEach(function (done) { - client = redis.createClient.apply(null, args); - client.once('ready', function () { - client.flushall(done); - }); - }); - - afterEach(function () { - client.end(true); - }); - - it('update serverInfo after a info command', function (done) { - client.set('foo', 'bar'); - client.info(); - client.select(2, function () { - assert.strictEqual(client.serverInfo.db2, undefined); - }); - client.set('foo', 'bar'); - client.info(); - setTimeout(function () { - assert.strictEqual(typeof client.serverInfo.db2, 'object'); - done(); - }, 30); - }); - - it('works with optional section provided with and without callback', function (done) { - client.set('foo', 'bar'); - client.info('keyspace'); - client.select(2, function () { - assert.strictEqual(Object.keys(client.server_info).length, 2, 'Key length should be three'); - assert.strictEqual(typeof client.server_info.db0, 'object', 'db0 keyspace should be an object'); - }); - client.info(['keyspace']); - client.set('foo', 'bar'); - client.info('all', function (err, res) { - assert(Object.keys(client.server_info).length > 3, 'Key length should be way above three'); - assert.strictEqual(typeof client.server_info.redis_version, 'string'); - assert.strictEqual(typeof client.server_info.db2, 'object'); - done(); - }); - }); - - it('check redis v.2.4 support', function (done) { - var end = helper.callFuncAfter(done, 2); - client.internal_send_command = function (command_obj) { - assert.strictEqual(command_obj.args.length, 0); - assert.strictEqual(command_obj.command, 'info'); - end(); - }; - client.info(); - client.info(function () {}); - }); - - it('emit error after a failure', function (done) { - client.info(); - client.once('error', function (err) { - assert.strictEqual(err.code, 'UNCERTAIN_STATE'); - assert.strictEqual(err.command, 'INFO'); - done(); - }); - client.stream.destroy(); - }); - }); - }); -}); diff --git a/test/commands/keys.spec.js b/test/commands/keys.spec.js deleted file mode 100644 index 6ce45790b6d..00000000000 --- a/test/commands/keys.spec.js +++ /dev/null @@ -1,69 +0,0 @@ -'use strict'; - -var assert = require('assert'); -var config = require('../lib/config'); -var crypto = require('crypto'); -var helper = require('../helper'); -var redis = config.redis; - -describe("The 'keys' method", function () { - - helper.allTests(function (ip, args) { - - describe('using ' + ip, function () { - var client; - - beforeEach(function (done) { - client = redis.createClient.apply(null, args); - client.once('ready', function () { - client.flushall(done); - }); - }); - - it('returns matching keys', function (done) { - client.mset(['test keys 1', 'test val 1', 'test keys 2', 'test val 2'], helper.isString('OK')); - client.KEYS('test keys*', function (err, results) { - assert.strictEqual(2, results.length); - assert.ok(~results.indexOf('test keys 1')); - assert.ok(~results.indexOf('test keys 2')); - return done(err); - }); - }); - - it('handles a large packet size', function (done) { - var keys_values = []; - - for (var i = 0; i < 200; i++) { - var key_value = [ - 'multibulk:' + crypto.randomBytes(256).toString('hex'), // use long strings as keys to ensure generation of large packet - 'test val ' + i - ]; - keys_values.push(key_value); - } - - client.mset(keys_values.reduce(function (a, b) { - return a.concat(b); - }), helper.isString('OK')); - - client.keys('multibulk:*', function (err, results) { - assert.deepEqual(keys_values.map(function (val) { - return val[0]; - }).sort(), results.sort()); - return done(err); - }); - }); - - it('handles an empty response', function (done) { - client.KEYS(['users:*'], function (err, results) { - assert.strictEqual(results.length, 0); - assert.ok(Array.isArray(results)); - return done(err); - }); - }); - - afterEach(function () { - client.end(true); - }); - }); - }); -}); diff --git a/test/commands/mget.spec.js b/test/commands/mget.spec.js deleted file mode 100644 index a2c671f683c..00000000000 --- a/test/commands/mget.spec.js +++ /dev/null @@ -1,70 +0,0 @@ -'use strict'; - -var assert = require('assert'); -var config = require('../lib/config'); -var helper = require('../helper'); -var redis = config.redis; - -describe("The 'mget' method", function () { - - helper.allTests(function (ip, args) { - - describe('using ' + ip, function () { - var client; - - beforeEach(function (done) { - client = redis.createClient.apply(null, args); - client.once('error', done); - client.once('ready', function () { - client.flushdb(); - client.mset(['mget keys 1', 'mget val 1', 'mget keys 2', 'mget val 2', 'mget keys 3', 'mget val 3'], done); - }); - }); - - it('handles fetching multiple keys in argument form', function (done) { - client.mset(['mget keys 1', 'mget val 1', 'mget keys 2', 'mget val 2', 'mget keys 3', 'mget val 3'], helper.isString('OK')); - client.MGET('mget keys 1', 'mget keys 2', 'mget keys 3', function (err, results) { - assert.strictEqual(3, results.length); - assert.strictEqual('mget val 1', results[0].toString()); - assert.strictEqual('mget val 2', results[1].toString()); - assert.strictEqual('mget val 3', results[2].toString()); - return done(err); - }); - }); - - it('handles fetching multiple keys via an array', function (done) { - client.mget(['mget keys 1', 'mget keys 2', 'mget keys 3'], function (err, results) { - assert.strictEqual('mget val 1', results[0].toString()); - assert.strictEqual('mget val 2', results[1].toString()); - assert.strictEqual('mget val 3', results[2].toString()); - return done(err); - }); - }); - - it('handles fetching multiple keys, when some keys do not exist', function (done) { - client.MGET('mget keys 1', ['some random shit', 'mget keys 2', 'mget keys 3'], function (err, results) { - assert.strictEqual(4, results.length); - assert.strictEqual('mget val 1', results[0].toString()); - assert.strictEqual(null, results[1]); - assert.strictEqual('mget val 2', results[2].toString()); - assert.strictEqual('mget val 3', results[3].toString()); - return done(err); - }); - }); - - it('handles fetching multiple keys, when some keys do not exist promisified', function () { - return client.MGETAsync('mget keys 1', ['some random shit', 'mget keys 2', 'mget keys 3']).then(function (results) { - assert.strictEqual(4, results.length); - assert.strictEqual('mget val 1', results[0].toString()); - assert.strictEqual(null, results[1]); - assert.strictEqual('mget val 2', results[2].toString()); - assert.strictEqual('mget val 3', results[3].toString()); - }); - }); - - afterEach(function () { - client.end(true); - }); - }); - }); -}); diff --git a/test/commands/monitor.spec.js b/test/commands/monitor.spec.js deleted file mode 100644 index 679277ffcac..00000000000 --- a/test/commands/monitor.spec.js +++ /dev/null @@ -1,215 +0,0 @@ -'use strict'; - -var assert = require('assert'); -var config = require('../lib/config'); -var helper = require('../helper'); -var utils = require('../../lib/utils'); -var redis = config.redis; - -describe("The 'monitor' method", function () { - - helper.allTests(function (ip, args) { - - var client; - - afterEach(function () { - client.end(true); - }); - - beforeEach(function (done) { - client = redis.createClient.apply(null, args); - client.once('connect', function () { - client.flushdb(done); - }); - }); - - it('monitors commands on all redis clients and works in the correct order', function (done) { - var monitorClient = redis.createClient.apply(null, args); - var responses = [ - ['mget', 'some', 'keys', 'foo', 'bar'], - ['set', 'json', '{"foo":"123","bar":"sdflkdfsjk","another":false}'], - ['eval', "return redis.call('set', 'sha', 'test')", '0'], - ['set', 'sha', 'test'], - ['get', 'baz'], - ['set', 'foo', 'bar" "s are " " good!"'], - ['mget', 'foo', 'baz'], - ['subscribe', 'foo', 'baz'] - ]; - var end = helper.callFuncAfter(done, 5); - - monitorClient.set('foo', 'bar'); - monitorClient.flushdb(); - monitorClient.monitor(function (err, res) { - assert.strictEqual(res, 'OK'); - client.mget('some', 'keys', 'foo', 'bar'); - client.set('json', JSON.stringify({ - foo: '123', - bar: 'sdflkdfsjk', - another: false - })); - client.eval("return redis.call('set', 'sha', 'test')", 0); - monitorClient.get('baz', function (err, res) { - assert.strictEqual(res, null); - end(err); - }); - monitorClient.set('foo', 'bar" "s are " " good!"', function (err, res) { - assert.strictEqual(res, 'OK'); - end(err); - }); - monitorClient.mget('foo', 'baz', function (err, res) { - assert.strictEqual(res[0], 'bar" "s are " " good!"'); - assert.strictEqual(res[1], null); - end(err); - }); - monitorClient.subscribe('foo', 'baz', function (err, res) { - // The return value might change in v.4 - // assert.strictEqual(res, 'baz'); - // TODO: Fix the return value of subscribe calls - end(err); - }); - }); - - monitorClient.on('monitor', function (time, args, rawOutput) { - assert.strictEqual(monitorClient.monitoring, true); - assert.deepEqual(args, responses.shift()); - assert(utils.monitor_regex.test(rawOutput), rawOutput); - if (responses.length === 0) { - monitorClient.quit(end); - } - }); - }); - - it('monitors returns strings in the rawOutput even with return_buffers activated', function (done) { - if (process.platform === 'win32') { - this.skip(); - } - var monitorClient = redis.createClient({ - return_buffers: true, - path: '/tmp/redis.sock' - }); - - monitorClient.MONITOR(function (err, res) { - assert.strictEqual(monitorClient.monitoring, true); - assert.strictEqual(res.inspect(), Buffer.from('OK').inspect()); - monitorClient.mget('hello', Buffer.from('world')); - }); - - monitorClient.on('monitor', function (time, args, rawOutput) { - assert.strictEqual(typeof rawOutput, 'string'); - assert(utils.monitor_regex.test(rawOutput), rawOutput); - assert.deepEqual(args, ['mget', 'hello', 'world']); - // Quit immediatly ends monitoring mode and therefore does not stream back the quit command - monitorClient.quit(done); - }); - }); - - it('monitors reconnects properly and works with the offline queue', function (done) { - var called = false; - client.MONITOR(helper.isString('OK')); - client.mget('hello', 'world'); - client.on('monitor', function (time, args, rawOutput) { - assert.strictEqual(client.monitoring, true); - assert(utils.monitor_regex.test(rawOutput), rawOutput); - assert.deepEqual(args, ['mget', 'hello', 'world']); - if (called) { - // End after a reconnect - return done(); - } - client.stream.destroy(); - client.mget('hello', 'world'); - called = true; - }); - }); - - it('monitors reconnects properly and works with the offline queue in a batch statement', function (done) { - var called = false; - var multi = client.batch(); - multi.MONITOR(helper.isString('OK')); - multi.mget('hello', 'world'); - multi.exec(function (err, res) { - assert.deepEqual(res, ['OK', [null, null]]); - }); - client.on('monitor', function (time, args, rawOutput) { - assert.strictEqual(client.monitoring, true); - assert(utils.monitor_regex.test(rawOutput), rawOutput); - assert.deepEqual(args, ['mget', 'hello', 'world']); - if (called) { - // End after a reconnect - return done(); - } - client.stream.destroy(); - client.mget('hello', 'world'); - called = true; - }); - }); - - it('monitor activates even if the command could not be processed properly after a reconnect', function (done) { - client.MONITOR(function (err, res) { - assert.strictEqual(err.code, 'UNCERTAIN_STATE'); - }); - client.on('error', function (err) {}); // Ignore error here - client.stream.destroy(); - var end = helper.callFuncAfter(done, 2); - client.on('monitor', function (time, args, rawOutput) { - assert.strictEqual(client.monitoring, true); - end(); - }); - client.on('reconnecting', function () { - client.get('foo', function (err, res) { - assert(!err); - assert.strictEqual(client.monitoring, true); - end(); - }); - }); - }); - - it('monitors works in combination with the pub sub mode and the offline queue', function (done) { - var responses = [ - ['subscribe', '/foo', '/bar'], - ['unsubscribe', '/bar'], - ['get', 'foo'], - ['subscribe', '/foo'], - ['subscribe', 'baz'], - ['unsubscribe', 'baz'], - ['publish', '/foo', 'hello world'] - ]; - var pub = redis.createClient(); - pub.on('ready', function () { - client.MONITOR(function (err, res) { - assert.strictEqual(res, 'OK'); - pub.get('foo', helper.isNull()); - }); - client.subscribe('/foo', '/bar'); - client.unsubscribe('/bar'); - setTimeout(function () { - client.stream.destroy(); - client.once('ready', function () { - pub.publish('/foo', 'hello world'); - }); - client.set('foo', 'bar', helper.isError()); - client.subscribe('baz'); - client.unsubscribe('baz'); - }, 150); - var called = false; - client.on('monitor', function (time, args, rawOutput) { - assert.deepEqual(args, responses.shift()); - assert(utils.monitor_regex.test(rawOutput), rawOutput); - if (responses.length === 0) { - // The publish is called right after the reconnect and the monitor is called before the message is emitted. - // Therefore we have to wait till the next tick - process.nextTick(function () { - assert(called); - client.quit(done); - pub.end(false); - }); - } - }); - client.on('message', function (channel, msg) { - assert.strictEqual(channel, '/foo'); - assert.strictEqual(msg, 'hello world'); - called = true; - }); - }); - }); - }); -}); diff --git a/test/commands/mset.spec.js b/test/commands/mset.spec.js deleted file mode 100644 index b60f383134b..00000000000 --- a/test/commands/mset.spec.js +++ /dev/null @@ -1,111 +0,0 @@ -'use strict'; - -var assert = require('assert'); -var config = require('../lib/config'); -var helper = require('../helper'); -var redis = config.redis; -var uuid = require('uuid'); - -describe("The 'mset' method", function () { - - helper.allTests(function (ip, args) { - - describe('using ' + ip, function () { - var key, value, key2, value2; - - beforeEach(function () { - key = uuid.v4(); - value = uuid.v4(); - key2 = uuid.v4(); - value2 = uuid.v4(); - }); - - describe('when not connected', function () { - var client; - - beforeEach(function (done) { - client = redis.createClient.apply(null, args); - client.once('ready', function () { - client.quit(); - }); - client.on('end', done); - }); - - it('reports an error', function (done) { - client.mset(key, value, key2, value2, function (err, res) { - assert(err.message.match(/The connection is already closed/)); - done(); - }); - }); - }); - - describe('when connected', function () { - var client; - - beforeEach(function (done) { - client = redis.createClient.apply(null, args); - client.once('ready', function () { - done(); - }); - }); - - afterEach(function () { - client.end(true); - }); - - describe('and a callback is specified', function () { - describe('with valid parameters', function () { - it('sets the value correctly', function (done) { - client.mset(key, value, key2, value2, function (err) { - if (err) { - return done(err); - } - client.get(key, helper.isString(value)); - client.get(key2, helper.isString(value2, done)); - }); - }); - }); - - describe("with undefined 'key' parameter and missing 'value' parameter", function () { - it('reports an error', function (done) { - client.mset(undefined, function (err, res) { - helper.isError()(err, null); - done(); - }); - }); - }); - - }); - - describe('and no callback is specified', function () { - describe('with valid parameters', function () { - it('sets the value correctly', function (done) { - client.mset(key, value2, key2, value); - client.get(key, helper.isString(value2)); - client.get(key2, helper.isString(value, done)); - }); - - it('sets the value correctly with array syntax', function (done) { - client.mset([key, value2, key2, value]); - client.get(key, helper.isString(value2)); - client.get(key2, helper.isString(value, done)); - }); - }); - - describe("with undefined 'key' and missing 'value' parameter", function () { - // this behavior is different from the 'set' behavior. - it('emits an error', function (done) { - client.on('error', function (err) { - assert.strictEqual(err.message, "ERR wrong number of arguments for 'mset' command"); - assert.strictEqual(err.name, 'ReplyError'); - done(); - }); - - client.mset(); - }); - }); - }); - }); - }); - }); -}); diff --git a/test/commands/msetnx.spec.js b/test/commands/msetnx.spec.js deleted file mode 100644 index 179f33744e6..00000000000 --- a/test/commands/msetnx.spec.js +++ /dev/null @@ -1,38 +0,0 @@ -'use strict'; - -var config = require('../lib/config'); -var helper = require('../helper'); -var redis = config.redis; - -describe("The 'msetnx' method", function () { - - helper.allTests(function (ip, args) { - - describe('using ' + ip, function () { - var client; - - beforeEach(function (done) { - client = redis.createClient.apply(null, args); - client.once('ready', function () { - client.flushdb(done); - }); - }); - - it('if any keys exist entire operation fails', function (done) { - client.mset(['mset1', 'val1', 'mset2', 'val2', 'mset3', 'val3'], helper.isString('OK')); - client.MSETNX(['mset3', 'val3', 'mset4', 'val4'], helper.isNumber(0)); - client.exists(['mset4'], helper.isNumber(0, done)); - }); - - it('sets multiple keys if all keys are not set', function (done) { - client.msetnx(['mset3', 'val3', 'mset4', 'val4'], helper.isNumber(1)); - client.exists(['mset3'], helper.isNumber(1)); - client.exists(['mset3'], helper.isNumber(1, done)); - }); - - afterEach(function () { - client.end(true); - }); - }); - }); -}); diff --git a/test/commands/randomkey.test.js b/test/commands/randomkey.test.js deleted file mode 100644 index 226194f9214..00000000000 --- a/test/commands/randomkey.test.js +++ /dev/null @@ -1,35 +0,0 @@ -'use strict'; - -var assert = require('assert'); -var config = require('../lib/config'); -var helper = require('../helper'); -var redis = config.redis; - -describe("The 'randomkey' method", function () { - - helper.allTests(function (ip, args) { - - describe('using ' + ip, function () { - var client; - - beforeEach(function (done) { - client = redis.createClient.apply(null, args); - client.once('ready', function () { - client.flushdb(done); - }); - }); - - it('returns a random key', function (done) { - client.mset(['test keys 1', 'test val 1', 'test keys 2', 'test val 2'], helper.isString('OK')); - client.RANDOMKEY([], function (err, results) { - assert.strictEqual(true, /test keys.+/.test(results)); - return done(err); - }); - }); - - afterEach(function () { - client.end(true); - }); - }); - }); -}); diff --git a/test/commands/rename.spec.js b/test/commands/rename.spec.js deleted file mode 100644 index 284fba310ed..00000000000 --- a/test/commands/rename.spec.js +++ /dev/null @@ -1,38 +0,0 @@ -'use strict'; - -var config = require('../lib/config'); -var helper = require('../helper'); -var redis = config.redis; - -describe("The 'rename' method", function () { - - helper.allTests(function (ip, args) { - - describe('using ' + ip, function () { - var client; - - beforeEach(function (done) { - client = redis.createClient.apply(null, args); - client.once('ready', function () { - client.flushdb(done); - }); - }); - - it('populates the new key', function (done) { - client.set(['foo', 'bar'], helper.isString('OK')); - client.rename(['foo', 'new foo'], helper.isString('OK')); - client.exists(['new foo'], helper.isNumber(1, done)); - }); - - it('removes the old key', function (done) { - client.set(['foo', 'bar'], helper.isString('OK')); - client.RENAME(['foo', 'new foo'], helper.isString('OK')); - client.exists(['foo'], helper.isNumber(0, done)); - }); - - afterEach(function () { - client.end(true); - }); - }); - }); -}); diff --git a/test/commands/renamenx.spec.js b/test/commands/renamenx.spec.js deleted file mode 100644 index b56b0a1a5c9..00000000000 --- a/test/commands/renamenx.spec.js +++ /dev/null @@ -1,41 +0,0 @@ -'use strict'; - -var config = require('../lib/config'); -var helper = require('../helper'); -var redis = config.redis; - -describe("The 'renamenx' method", function () { - - helper.allTests(function (ip, args) { - - describe('using ' + ip, function () { - var client; - - beforeEach(function (done) { - client = redis.createClient.apply(null, args); - client.once('ready', function () { - client.flushdb(done); - }); - }); - - it('renames the key if target does not yet exist', function (done) { - client.set('foo', 'bar', helper.isString('OK')); - client.RENAMENX('foo', 'foo2', helper.isNumber(1)); - client.exists('foo', helper.isNumber(0)); - client.exists(['foo2'], helper.isNumber(1, done)); - }); - - it('does not rename the key if the target exists', function (done) { - client.set('foo', 'bar', helper.isString('OK')); - client.set('foo2', 'apple', helper.isString('OK')); - client.renamenx('foo', 'foo2', helper.isNumber(0)); - client.exists('foo', helper.isNumber(1)); - client.exists(['foo2'], helper.isNumber(1, done)); - }); - - afterEach(function () { - client.end(true); - }); - }); - }); -}); diff --git a/test/commands/rpush.spec.js b/test/commands/rpush.spec.js deleted file mode 100644 index 793d5d2d804..00000000000 --- a/test/commands/rpush.spec.js +++ /dev/null @@ -1,36 +0,0 @@ -'use strict'; - -var config = require('../lib/config'); -var helper = require('../helper'); -var redis = config.redis; -var assert = require('assert'); - -describe("The 'rpush' command", function () { - - helper.allTests(function (ip, args) { - - describe('using ' + ip, function () { - var client; - - beforeEach(function (done) { - client = redis.createClient.apply(null, args); - client.once('ready', function () { - client.flushdb(done); - }); - }); - - it('inserts multiple values at a time into a list', function (done) { - client.rpush('test', ['list key', 'should be a list']); - client.lrange('test', 0, -1, function (err, res) { - assert.equal(res[0], 'list key'); - assert.equal(res[1], 'should be a list'); - done(err); - }); - }); - - afterEach(function () { - client.end(true); - }); - }); - }); -}); diff --git a/test/commands/sadd.spec.js b/test/commands/sadd.spec.js deleted file mode 100644 index 442f391b9de..00000000000 --- a/test/commands/sadd.spec.js +++ /dev/null @@ -1,62 +0,0 @@ -'use strict'; - -var assert = require('assert'); -var config = require('../lib/config'); -var helper = require('../helper'); -var redis = config.redis; - -describe("The 'sadd' method", function () { - - helper.allTests(function (ip, args) { - - describe('using ' + ip, function () { - var client; - - beforeEach(function (done) { - client = redis.createClient.apply(null, args); - client.once('ready', function () { - client.flushdb(done); - }); - }); - - it('allows a single value to be added to the set', function (done) { - client.SADD('set0', 'member0', helper.isNumber(1)); - client.smembers('set0', function (err, res) { - assert.ok(~res.indexOf('member0')); - return done(err); - }); - }); - - it('does not add the same value to the set twice', function (done) { - client.sadd('set0', 'member0', helper.isNumber(1)); - client.SADD('set0', 'member0', helper.isNumber(0, done)); - }); - - it('allows multiple values to be added to the set', function (done) { - client.sadd('set0', ['member0', 'member1', 'member2'], helper.isNumber(3)); - client.smembers('set0', function (err, res) { - assert.strictEqual(res.length, 3); - assert.ok(~res.indexOf('member0')); - assert.ok(~res.indexOf('member1')); - assert.ok(~res.indexOf('member2')); - return done(err); - }); - }); - - it('allows multiple values to be added to the set with a different syntax', function (done) { - client.sadd(['set0', 'member0', 'member1', 'member2'], helper.isNumber(3)); - client.smembers('set0', function (err, res) { - assert.strictEqual(res.length, 3); - assert.ok(~res.indexOf('member0')); - assert.ok(~res.indexOf('member1')); - assert.ok(~res.indexOf('member2')); - return done(err); - }); - }); - - afterEach(function () { - client.end(true); - }); - }); - }); -}); diff --git a/test/commands/scard.spec.js b/test/commands/scard.spec.js deleted file mode 100644 index e327eb282a2..00000000000 --- a/test/commands/scard.spec.js +++ /dev/null @@ -1,31 +0,0 @@ -'use strict'; - -var config = require('../lib/config'); -var helper = require('../helper'); -var redis = config.redis; - -describe("The 'scard' method", function () { - - helper.allTests(function (ip, args) { - - describe('using ' + ip, function () { - var client; - - beforeEach(function (done) { - client = redis.createClient.apply(null, args); - client.once('ready', function () { - client.flushdb(done); - }); - }); - - it('returns the number of values in a set', function (done) { - client.sadd('foo', [1, 2, 3], helper.isNumber(3)); - client.scard('foo', helper.isNumber(3, done)); - }); - - afterEach(function () { - client.end(true); - }); - }); - }); -}); diff --git a/test/commands/script.spec.js b/test/commands/script.spec.js deleted file mode 100644 index c374f5b5e17..00000000000 --- a/test/commands/script.spec.js +++ /dev/null @@ -1,55 +0,0 @@ -'use strict'; - -var assert = require('assert'); -var config = require('../lib/config'); -var crypto = require('crypto'); -var helper = require('../helper'); -var redis = config.redis; - -describe("The 'script' method", function () { - - helper.allTests(function (ip, args) { - var command = 'return 99'; - var commandSha = crypto.createHash('sha1').update(command).digest('hex'); - - describe('using ' + ip, function () { - var client; - - beforeEach(function (done) { - client = redis.createClient.apply(null, args); - client.once('ready', function () { - client.flushdb(done); - }); - }); - - afterEach(function () { - client.end(true); - }); - - it("loads script with client.script('load')", function (done) { - client.script('load', command, function (err, result) { - assert.strictEqual(result, commandSha); - return done(); - }); - }); - - it('allows a loaded script to be evaluated', function (done) { - client.evalsha(commandSha, 0, helper.isNumber(99, done)); - }); - - it('allows a script to be loaded as part of a chained transaction', function (done) { - client.multi().script('load', command).exec(function (err, result) { - assert.strictEqual(result[0], commandSha); - return done(); - }); - }); - - it("allows a script to be loaded using a transaction's array syntax", function (done) { - client.multi([['script', 'load', command]]).exec(function (err, result) { - assert.strictEqual(result[0], commandSha); - return done(); - }); - }); - }); - }); -}); diff --git a/test/commands/sdiff.spec.js b/test/commands/sdiff.spec.js deleted file mode 100644 index 95f81f09bd0..00000000000 --- a/test/commands/sdiff.spec.js +++ /dev/null @@ -1,47 +0,0 @@ -'use strict'; - -var assert = require('assert'); -var config = require('../lib/config'); -var helper = require('../helper'); -var redis = config.redis; - -describe("The 'sdiff' method", function () { - - helper.allTests(function (ip, args) { - - describe('using ' + ip, function () { - var client; - - beforeEach(function (done) { - client = redis.createClient.apply(null, args); - client.once('ready', function () { - client.flushdb(done); - }); - }); - - it('returns set difference', function (done) { - client.sadd('foo', 'x', helper.isNumber(1)); - client.sadd('foo', ['a'], helper.isNumber(1)); - client.sadd('foo', 'b', helper.isNumber(1)); - client.sadd(['foo', 'c'], helper.isNumber(1)); - - client.sadd(['bar', 'c', helper.isNumber(1)]); - - client.sadd('baz', 'a', helper.isNumber(1)); - client.sadd('baz', 'd', helper.isNumber(1)); - - client.sdiff('foo', 'bar', 'baz', function (err, values) { - values.sort(); - assert.equal(values.length, 2); - assert.equal(values[0], 'b'); - assert.equal(values[1], 'x'); - return done(err); - }); - }); - - afterEach(function () { - client.end(true); - }); - }); - }); -}); diff --git a/test/commands/sdiffstore.spec.js b/test/commands/sdiffstore.spec.js deleted file mode 100644 index fe822b561b5..00000000000 --- a/test/commands/sdiffstore.spec.js +++ /dev/null @@ -1,47 +0,0 @@ -'use strict'; - -var assert = require('assert'); -var config = require('../lib/config'); -var helper = require('../helper'); -var redis = config.redis; - -describe("The 'sdiffstore' method", function () { - - helper.allTests(function (ip, args) { - - describe('using ' + ip, function () { - var client; - - beforeEach(function (done) { - client = redis.createClient.apply(null, args); - client.once('ready', function () { - client.flushdb(done); - }); - }); - - it('calculates set difference ands stores it in a key', function (done) { - client.sadd('foo', 'x', helper.isNumber(1)); - client.sadd('foo', 'a', helper.isNumber(1)); - client.sadd('foo', 'b', helper.isNumber(1)); - client.sadd('foo', 'c', helper.isNumber(1)); - - client.sadd('bar', 'c', helper.isNumber(1)); - - client.sadd('baz', 'a', helper.isNumber(1)); - client.sadd('baz', 'd', helper.isNumber(1)); - - client.sdiffstore('quux', 'foo', 'bar', 'baz', helper.isNumber(2)); - - client.smembers('quux', function (err, values) { - var members = values.sort(); - assert.deepEqual(members, [ 'b', 'x' ]); - return done(err); - }); - }); - - afterEach(function () { - client.end(true); - }); - }); - }); -}); diff --git a/test/commands/select.spec.js b/test/commands/select.spec.js deleted file mode 100644 index 053496e337f..00000000000 --- a/test/commands/select.spec.js +++ /dev/null @@ -1,126 +0,0 @@ -'use strict'; - -var assert = require('assert'); -var config = require('../lib/config'); -var helper = require('../helper'); -var redis = config.redis; - -describe("The 'select' method", function () { - - helper.allTests(function (ip, args) { - - describe('using ' + ip, function () { - describe('when not connected', function () { - var client; - - beforeEach(function (done) { - client = redis.createClient.apply(null, args); - client.once('ready', function () { - client.quit(); - }); - client.on('end', done); - }); - - it('returns an error if redis is not connected', function (done) { - var buffering = client.select(1, function (err, res) { - assert(err.message.match(/The connection is already closed/)); - done(); - }); - assert(typeof buffering === 'boolean'); - }); - }); - - describe('when connected', function () { - var client; - - beforeEach(function (done) { - client = redis.createClient.apply(null, args); - client.once('ready', function () { - client.flushdb(done); - }); - }); - - afterEach(function () { - client.end(true); - }); - - it('changes the database and calls the callback', function (done) { - // default value of null means database 0 will be used. - assert.strictEqual(client.selected_db, undefined, 'default db should be undefined'); - var buffering = client.SELECT(1, function (err, res) { - helper.isNotError()(err, res); - assert.strictEqual(client.selected_db, 1, 'db should be 1 after select'); - done(); - }); - assert(typeof buffering === 'boolean'); - }); - - describe('and a callback is specified', function () { - describe('with a valid db index', function () { - it('selects the appropriate database', function (done) { - assert.strictEqual(client.selected_db, undefined, 'default db should be undefined'); - client.select(1, function (err) { - assert.equal(err, null); - assert.equal(client.selected_db, 1, 'we should have selected the new valid DB'); - done(); - }); - }); - }); - - describe('with an invalid db index', function () { - it('returns an error', function (done) { - assert.strictEqual(client.selected_db, undefined, 'default db should be undefined'); - client.select(9999, function (err) { - assert.equal(err.code, 'ERR'); - assert((err.message === 'ERR DB index is out of range' || err.message === 'ERR invalid DB index')); - done(); - }); - }); - }); - }); - - describe('and no callback is specified', function () { - describe('with a valid db index', function () { - it('selects the appropriate database', function (done) { - assert.strictEqual(client.selected_db, undefined, 'default db should be undefined'); - client.select(1); - setTimeout(function () { - assert.equal(client.selected_db, 1, 'we should have selected the new valid DB'); - done(); - }, 25); - }); - }); - - describe('with an invalid db index', function () { - it('emits an error when callback not provided', function (done) { - assert.strictEqual(client.selected_db, undefined, 'default db should be undefined'); - - client.on('error', function (err) { - assert.strictEqual(err.command, 'SELECT'); - assert((err.message === 'ERR DB index is out of range' || err.message === 'ERR invalid DB index')); - done(); - }); - - client.select(9999); - }); - }); - }); - - describe('reconnection occurs', function () { - it('selects the appropriate database after a reconnect', function (done) { - assert.strictEqual(client.selected_db, undefined, 'default db should be undefined'); - client.select(3); - client.set('foo', 'bar', function () { - client.stream.destroy(); - }); - client.once('ready', function () { - assert.strictEqual(client.selected_db, 3); - assert(typeof client.server_info.db3 === 'object'); - done(); - }); - }); - }); - }); - }); - }); -}); diff --git a/test/commands/set.spec.js b/test/commands/set.spec.js deleted file mode 100644 index 33a8bfa22c0..00000000000 --- a/test/commands/set.spec.js +++ /dev/null @@ -1,178 +0,0 @@ -'use strict'; - -var assert = require('assert'); -var config = require('../lib/config'); -var helper = require('../helper'); -var redis = config.redis; -var uuid = require('uuid'); - -describe("The 'set' method", function () { - - helper.allTests(function (ip, args) { - - describe('using ' + ip, function () { - var key, value; - - beforeEach(function () { - key = uuid.v4(); - value = uuid.v4(); - }); - - describe('when not connected', function () { - var client; - - beforeEach(function (done) { - client = redis.createClient.apply(null, args); - client.once('ready', function () { - client.quit(); - }); - client.on('end', done); - }); - - it('reports an error', function (done) { - client.set(key, value, function (err, res) { - assert(err.message.match(/The connection is already closed/)); - done(); - }); - }); - }); - - describe('when connected', function () { - var client; - - beforeEach(function (done) { - client = redis.createClient.apply(null, args); - client.once('ready', function () { - client.flushdb(done); - }); - }); - - afterEach(function () { - client.end(true); - }); - - describe('and a callback is specified', function () { - describe('with valid parameters', function () { - it('sets the value correctly', function (done) { - client.SET(key, value, function (err, res) { - helper.isNotError()(err, res); - client.get(key, function (err, res) { - helper.isString(value)(err, res); - done(); - }); - }); - }); - - it('set expire date in seconds', function (done) { - client.set('foo', 'bar', 'ex', 10, helper.isString('OK')); - client.pttl('foo', function (err, res) { - assert(res >= 10000 - 50); // Max 50 ms should have passed - assert(res <= 10000); // Max possible should be 10.000 - done(err); - }); - }); - - it('set expire date in milliseconds', function (done) { - client.set('foo', 'bar', 'px', 100, helper.isString('OK')); - client.pttl('foo', function (err, res) { - assert(res >= 50); // Max 50 ms should have passed - assert(res <= 100); // Max possible should be 100 - done(err); - }); - }); - - it('only set the key if (not) already set', function (done) { - client.set('foo', 'bar', 'NX', helper.isString('OK')); - client.set('foo', 'bar', 'nx', helper.isNull()); - client.set('foo', 'bar', 'EX', '10', 'XX', helper.isString('OK')); - client.ttl('foo', function (err, res) { - assert(res >= 9); // Min 9s should be left - assert(res <= 10); // Max 10s should be left - done(err); - }); - }); - }); - - describe('reports an error with invalid parameters', function () { - it("undefined 'key' and missing 'value' parameter", function (done) { - client.set(undefined, function (err, res) { - helper.isError()(err, null); - assert.equal(err.command, 'SET'); - done(); - }); - }); - - it('empty array as second parameter', function (done) { - client.set('foo', [], function (err, res) { - assert.strictEqual(err.message, "ERR wrong number of arguments for 'set' command"); - done(); - }); - }); - }); - }); - - describe('and no callback is specified', function () { - describe('with valid parameters', function () { - it('sets the value correctly', function (done) { - client.set(key, value); - client.get(key, helper.isString(value, done)); - }); - - it('sets the value correctly even if the callback is explicitly set to undefined', function (done) { - client.set(key, value, undefined); - client.get(key, helper.isString(value, done)); - }); - - it('sets the value correctly with the array syntax', function (done) { - client.set([key, value]); - client.get(key, helper.isString(value, done)); - }); - }); - - describe("with undefined 'key' and missing 'value' parameter", function () { - it('emits an error without callback', function (done) { - client.on('error', function (err) { - assert.equal(err.message, "ERR wrong number of arguments for 'set' command"); - assert.equal(err.command, 'SET'); - done(); - }); - client.set(undefined); - }); - }); - - it('errors if null value is passed', function (done) { - try { - client.set('foo', null); - assert(false); - } catch (error) { - assert(/The SET command contains a invalid argument type./.test(error.message)); - } - client.get('foo', helper.isNull(done)); - }); - - it('calls callback with error if null value is passed', function (done) { - client.set('foo', null, helper.isError(done)); - }); - - it('emit an error with only the key set', function (done) { - client.on('error', function (err) { - assert.equal(err.message, "ERR wrong number of arguments for 'set' command"); - done(); - }); - - client.set('foo'); - }); - - it('emit an error without any parameters', function (done) { - client.once('error', function (err) { - assert.equal(err.message, "ERR wrong number of arguments for 'set' command"); - assert.equal(err.command, 'SET'); - done(); - }); - client.set(); - }); - }); - }); - }); - }); -}); diff --git a/test/commands/setex.spec.js b/test/commands/setex.spec.js deleted file mode 100644 index a2126e6dbb6..00000000000 --- a/test/commands/setex.spec.js +++ /dev/null @@ -1,37 +0,0 @@ -'use strict'; - -var assert = require('assert'); -var config = require('../lib/config'); -var helper = require('../helper'); -var redis = config.redis; - -describe("The 'setex' method", function () { - - helper.allTests(function (ip, args) { - - describe('using ' + ip, function () { - var client; - - beforeEach(function (done) { - client = redis.createClient.apply(null, args); - client.once('ready', function () { - client.flushdb(done); - }); - }); - - it('sets a key with an expiry', function (done) { - client.setex(['setex key', '100', 'setex val'], helper.isString('OK')); - var buffering = client.exists(['setex key'], helper.isNumber(1)); - assert(typeof buffering === 'boolean'); - client.ttl(['setex key'], function (err, ttl) { - assert(ttl > 0); - return done(); - }); - }); - - afterEach(function () { - client.end(true); - }); - }); - }); -}); diff --git a/test/commands/setnx.spec.js b/test/commands/setnx.spec.js deleted file mode 100644 index 4b4688c0a68..00000000000 --- a/test/commands/setnx.spec.js +++ /dev/null @@ -1,37 +0,0 @@ -'use strict'; - -var config = require('../lib/config'); -var helper = require('../helper'); -var redis = config.redis; - -describe("The 'setnx' method", function () { - - helper.allTests(function (ip, args) { - - describe('using ' + ip, function () { - var client; - - beforeEach(function (done) { - client = redis.createClient.apply(null, args); - client.once('ready', function () { - client.flushdb(done); - }); - }); - - it('sets key if it does not have a value', function (done) { - client.SETNX('foo', 'banana', helper.isNumber(1)); - client.get('foo', helper.isString('banana', done)); - }); - - it('does not set key if it already has a value', function (done) { - client.set('foo', 'bar', helper.isString('OK')); - client.setnx('foo', 'banana', helper.isNumber(0)); - client.get('foo', helper.isString('bar', done)); - }); - - afterEach(function () { - client.end(true); - }); - }); - }); -}); diff --git a/test/commands/sinter.spec.js b/test/commands/sinter.spec.js deleted file mode 100644 index c4fc7759556..00000000000 --- a/test/commands/sinter.spec.js +++ /dev/null @@ -1,63 +0,0 @@ -'use strict'; - -var assert = require('assert'); -var config = require('../lib/config'); -var helper = require('../helper'); -var redis = config.redis; - -describe("The 'sinter' method", function () { - - helper.allTests(function (ip, args) { - - describe('using ' + ip, function () { - var client; - - beforeEach(function (done) { - client = redis.createClient.apply(null, args); - client.once('ready', function () { - client.flushdb(done); - }); - }); - - it('handles two sets being intersected', function (done) { - client.sadd('sa', 'a', helper.isNumber(1)); - client.sadd('sa', 'b', helper.isNumber(1)); - client.sadd('sa', 'c', helper.isNumber(1)); - - client.sadd('sb', 'b', helper.isNumber(1)); - client.sadd('sb', 'c', helper.isNumber(1)); - client.sadd('sb', 'd', helper.isNumber(1)); - - client.SINTER('sa', 'sb', function (err, intersection) { - assert.equal(intersection.length, 2); - assert.deepEqual(intersection.sort(), [ 'b', 'c' ]); - return done(err); - }); - }); - - it('handles three sets being intersected', function (done) { - client.sadd('sa', 'a', helper.isNumber(1)); - client.sadd('sa', 'b', helper.isNumber(1)); - client.sadd('sa', 'c', helper.isNumber(1)); - - client.sadd('sb', 'b', helper.isNumber(1)); - client.sadd('sb', 'c', helper.isNumber(1)); - client.sadd('sb', 'd', helper.isNumber(1)); - - client.sadd('sc', 'c', helper.isNumber(1)); - client.sadd('sc', 'd', helper.isNumber(1)); - client.sadd('sc', 'e', helper.isNumber(1)); - - client.sinter('sa', 'sb', 'sc', function (err, intersection) { - assert.equal(intersection.length, 1); - assert.equal(intersection[0], 'c'); - return done(err); - }); - }); - - afterEach(function () { - client.end(true); - }); - }); - }); -}); diff --git a/test/commands/sinterstore.spec.js b/test/commands/sinterstore.spec.js deleted file mode 100644 index 1ea4c4b109b..00000000000 --- a/test/commands/sinterstore.spec.js +++ /dev/null @@ -1,48 +0,0 @@ -'use strict'; - -var assert = require('assert'); -var config = require('../lib/config'); -var helper = require('../helper'); -var redis = config.redis; - -describe("The 'sinterstore' method", function () { - - helper.allTests(function (ip, args) { - - describe('using ' + ip, function () { - var client; - - beforeEach(function (done) { - client = redis.createClient.apply(null, args); - client.once('ready', function () { - client.flushdb(done); - }); - }); - - it('calculates set intersection and stores it in a key', function (done) { - client.sadd('sa', 'a', helper.isNumber(1)); - client.sadd('sa', 'b', helper.isNumber(1)); - client.sadd('sa', 'c', helper.isNumber(1)); - - client.sadd('sb', 'b', helper.isNumber(1)); - client.sadd('sb', 'c', helper.isNumber(1)); - client.sadd('sb', 'd', helper.isNumber(1)); - - client.sadd('sc', 'c', helper.isNumber(1)); - client.sadd('sc', 'd', helper.isNumber(1)); - client.sadd('sc', 'e', helper.isNumber(1)); - - client.sinterstore('foo', 'sa', 'sb', 'sc', helper.isNumber(1)); - - client.smembers('foo', function (err, members) { - assert.deepEqual(members, [ 'c' ]); - return done(err); - }); - }); - - afterEach(function () { - client.end(true); - }); - }); - }); -}); diff --git a/test/commands/sismember.spec.js b/test/commands/sismember.spec.js deleted file mode 100644 index 37ac1c466aa..00000000000 --- a/test/commands/sismember.spec.js +++ /dev/null @@ -1,35 +0,0 @@ -'use strict'; - -var config = require('../lib/config'); -var helper = require('../helper'); -var redis = config.redis; - -describe("The 'sismember' method", function () { - - helper.allTests(function (ip, args) { - - describe('using ' + ip, function () { - var client; - - beforeEach(function (done) { - client = redis.createClient.apply(null, args); - client.once('ready', function () { - client.flushdb(done); - }); - }); - - it('returns 0 if the value is not in the set', function (done) { - client.sismember('foo', 'banana', helper.isNumber(0, done)); - }); - - it('returns 1 if the value is in the set', function (done) { - client.sadd('foo', 'banana', helper.isNumber(1)); - client.SISMEMBER('foo', 'banana', helper.isNumber(1, done)); - }); - - afterEach(function () { - client.end(true); - }); - }); - }); -}); diff --git a/test/commands/slowlog.spec.js b/test/commands/slowlog.spec.js deleted file mode 100644 index 21f3f8007ac..00000000000 --- a/test/commands/slowlog.spec.js +++ /dev/null @@ -1,41 +0,0 @@ -'use strict'; - -var assert = require('assert'); -var config = require('../lib/config'); -var helper = require('../helper'); -var redis = config.redis; - -describe("The 'slowlog' method", function () { - - helper.allTests(function (ip, args) { - - describe('using ' + ip, function () { - var client; - - beforeEach(function (done) { - client = redis.createClient.apply(null, args); - client.once('ready', function () { - client.flushdb(done); - }); - }); - - it('logs operations in slowlog', function (done) { - client.config('set', 'slowlog-log-slower-than', 0, helper.isString('OK')); - client.slowlog('reset', helper.isString('OK')); - client.set('foo', 'bar', helper.isString('OK')); - client.get('foo', helper.isString('bar')); - client.SLOWLOG('get', function (err, res) { - assert.equal(res.length, 3); - assert.equal(res[0][3].length, 2); - assert.deepEqual(res[1][3], ['set', 'foo', 'bar']); - assert.deepEqual(res[2][3], ['slowlog', 'reset']); - return done(err); - }); - }); - - afterEach(function () { - client.end(true); - }); - }); - }); -}); diff --git a/test/commands/smembers.spec.js b/test/commands/smembers.spec.js deleted file mode 100644 index 0bc8143719f..00000000000 --- a/test/commands/smembers.spec.js +++ /dev/null @@ -1,38 +0,0 @@ -'use strict'; - -var assert = require('assert'); -var config = require('../lib/config'); -var helper = require('../helper'); -var redis = config.redis; - -describe("The 'smembers' method", function () { - - helper.allTests(function (ip, args) { - - describe('using ' + ip, function () { - var client; - - beforeEach(function (done) { - client = redis.createClient.apply(null, args); - client.once('ready', function () { - client.flushdb(done); - }); - }); - - it('returns all values in a set', function (done) { - client.sadd('foo', 'x', helper.isNumber(1)); - client.sadd('foo', 'y', helper.isNumber(1)); - client.smembers('foo', function (err, values) { - assert.equal(values.length, 2); - var members = values.sort(); - assert.deepEqual(members, [ 'x', 'y' ]); - return done(err); - }); - }); - - afterEach(function () { - client.end(true); - }); - }); - }); -}); diff --git a/test/commands/smove.spec.js b/test/commands/smove.spec.js deleted file mode 100644 index 969c264b756..00000000000 --- a/test/commands/smove.spec.js +++ /dev/null @@ -1,40 +0,0 @@ -'use strict'; - -var config = require('../lib/config'); -var helper = require('../helper'); -var redis = config.redis; - -describe("The 'smove' method", function () { - - helper.allTests(function (ip, args) { - - describe('using ' + ip, function () { - var client; - - beforeEach(function (done) { - client = redis.createClient.apply(null, args); - client.once('ready', function () { - client.flushdb(done); - }); - }); - - it('moves a value to a set that does not yet exist', function (done) { - client.sadd('foo', 'x', helper.isNumber(1)); - client.smove('foo', 'bar', 'x', helper.isNumber(1)); - client.sismember('foo', 'x', helper.isNumber(0)); - client.sismember('bar', 'x', helper.isNumber(1, done)); - }); - - it('does not move a value if it does not exist in the first set', function (done) { - client.sadd('foo', 'x', helper.isNumber(1)); - client.SMOVE('foo', 'bar', 'y', helper.isNumber(0)); - client.sismember('foo', 'y', helper.isNumber(0)); - client.sismember('bar', 'y', helper.isNumber(0, done)); - }); - - afterEach(function () { - client.end(true); - }); - }); - }); -}); diff --git a/test/commands/sort.spec.js b/test/commands/sort.spec.js deleted file mode 100644 index 2ee08c44e21..00000000000 --- a/test/commands/sort.spec.js +++ /dev/null @@ -1,130 +0,0 @@ -'use strict'; - -var assert = require('assert'); -var config = require('../lib/config'); -var helper = require('../helper'); -var redis = config.redis; - -function setupData (client, done) { - client.rpush('y', 'd'); - client.rpush('y', 'b'); - client.rpush('y', 'a'); - client.rpush('y', 'c'); - - client.rpush('x', '3'); - client.rpush('x', '9'); - client.rpush('x', '2'); - client.rpush('x', '4'); - - client.set('w3', '4'); - client.set('w9', '5'); - client.set('w2', '12'); - client.set('w4', '6'); - - client.set('o2', 'buz'); - client.set('o3', 'foo'); - client.set('o4', 'baz'); - client.set('o9', 'bar'); - - client.set('p2', 'qux'); - client.set('p3', 'bux'); - client.set('p4', 'lux'); - client.set('p9', 'tux', done); -} - -describe("The 'sort' method", function () { - - helper.allTests(function (ip, args) { - - describe('using ' + ip, function () { - var client; - - beforeEach(function (done) { - client = redis.createClient.apply(null, args); - client.once('error', done); - client.once('connect', function () { - client.flushdb(); - setupData(client, done); - }); - }); - - describe('alphabetical', function () { - it('sorts in ascending alphabetical order', function (done) { - client.sort('y', 'asc', 'alpha', function (err, sorted) { - assert.deepEqual(sorted, ['a', 'b', 'c', 'd']); - return done(err); - }); - }); - - it('sorts in descending alphabetical order', function (done) { - client.SORT('y', 'desc', 'alpha', function (err, sorted) { - assert.deepEqual(sorted, ['d', 'c', 'b', 'a']); - return done(err); - }); - }); - }); - - describe('numeric', function () { - it('sorts in ascending numeric order', function (done) { - client.sort('x', 'asc', function (err, sorted) { - assert.deepEqual(sorted, [2, 3, 4, 9]); - return done(err); - }); - }); - - it('sorts in descending numeric order', function (done) { - client.sort('x', 'desc', function (err, sorted) { - assert.deepEqual(sorted, [9, 4, 3, 2]); - return done(err); - }); - }); - }); - - describe('pattern', function () { - it('handles sorting with a pattern', function (done) { - client.sort('x', 'by', 'w*', 'asc', function (err, sorted) { - assert.deepEqual(sorted, [3, 9, 4, 2]); - return done(err); - }); - }); - - it("handles sorting with a 'by' pattern and 1 'get' pattern", function (done) { - client.sort('x', 'by', 'w*', 'asc', 'get', 'o*', function (err, sorted) { - assert.deepEqual(sorted, ['foo', 'bar', 'baz', 'buz']); - return done(err); - }); - }); - - it("handles sorting with a 'by' pattern and 2 'get' patterns", function (done) { - client.sort('x', 'by', 'w*', 'asc', 'get', 'o*', 'get', 'p*', function (err, sorted) { - assert.deepEqual(sorted, ['foo', 'bux', 'bar', 'tux', 'baz', 'lux', 'buz', 'qux']); - return done(err); - }); - }); - - it("handles sorting with a 'by' pattern and 2 'get' patterns with the array syntax", function (done) { - client.sort(['x', 'by', 'w*', 'asc', 'get', 'o*', 'get', 'p*'], function (err, sorted) { - assert.deepEqual(sorted, ['foo', 'bux', 'bar', 'tux', 'baz', 'lux', 'buz', 'qux']); - return done(err); - }); - }); - - it("sorting with a 'by' pattern and 2 'get' patterns and stores results", function (done) { - client.sort('x', 'by', 'w*', 'asc', 'get', 'o*', 'get', 'p*', 'store', 'bacon', function (err) { - if (err) return done(err); - }); - - client.lrange('bacon', 0, -1, function (err, values) { - assert.deepEqual(values, ['foo', 'bux', 'bar', 'tux', 'baz', 'lux', 'buz', 'qux']); - return done(err); - }); - }); - }); - - afterEach(function () { - client.end(true); - }); - }); - }); - -}); diff --git a/test/commands/spop.spec.js b/test/commands/spop.spec.js deleted file mode 100644 index ec3e93fda3f..00000000000 --- a/test/commands/spop.spec.js +++ /dev/null @@ -1,38 +0,0 @@ -'use strict'; - -var assert = require('assert'); -var config = require('../lib/config'); -var helper = require('../helper'); -var redis = config.redis; - -describe("The 'spop' method", function () { - - helper.allTests(function (ip, args) { - - describe('using ' + ip, function () { - var client; - - beforeEach(function (done) { - client = redis.createClient.apply(null, args); - client.once('ready', function () { - client.flushdb(done); - }); - }); - - it('returns a random element from the set', function (done) { - client.sadd('zzz', 'member0', helper.isNumber(1)); - client.scard('zzz', helper.isNumber(1)); - - client.spop('zzz', function (err, value) { - if (err) return done(err); - assert.equal(value, 'member0'); - client.scard('zzz', helper.isNumber(0, done)); - }); - }); - - afterEach(function () { - client.end(true); - }); - }); - }); -}); diff --git a/test/commands/srem.spec.js b/test/commands/srem.spec.js deleted file mode 100644 index d325cb57151..00000000000 --- a/test/commands/srem.spec.js +++ /dev/null @@ -1,69 +0,0 @@ -'use strict'; - -var assert = require('assert'); -var config = require('../lib/config'); -var helper = require('../helper'); -var redis = config.redis; - -describe("The 'srem' method", function () { - - helper.allTests(function (ip, args) { - - describe('using ' + ip, function () { - var client; - - beforeEach(function (done) { - client = redis.createClient.apply(null, args); - client.once('ready', function () { - client.flushdb(done); - }); - }); - - it('removes a value', function (done) { - client.sadd('set0', 'member0', helper.isNumber(1)); - client.srem('set0', 'member0', helper.isNumber(1)); - client.scard('set0', helper.isNumber(0, done)); - }); - - it('handles attempting to remove a missing value', function (done) { - client.SREM('set0', 'member0', helper.isNumber(0, done)); - }); - - it('allows multiple values to be removed', function (done) { - client.sadd('set0', ['member0', 'member1', 'member2'], helper.isNumber(3)); - client.SREM('set0', ['member1', 'member2'], helper.isNumber(2)); - client.smembers('set0', function (err, res) { - assert.strictEqual(res.length, 1); - assert.ok(~res.indexOf('member0')); - return done(err); - }); - }); - - it('allows multiple values to be removed with send_command', function (done) { - client.send_command('sadd', ['set0', 'member0', 'member1', 'member2'], helper.isNumber(3)); - client.send_command('srem', ['set0', 'member1', 'member2'], helper.isNumber(2)); - client.smembers('set0', function (err, res) { - assert.strictEqual(res.length, 1); - assert.ok(~res.indexOf('member0')); - return done(err); - }); - }); - - it('handles a value missing from the set of values being removed', function (done) { - client.sadd(['set0', 'member0', 'member1', 'member2'], helper.isNumber(3)); - client.SREM(['set0', 'member3', 'member4'], helper.isNumber(0)); - client.smembers('set0', function (err, res) { - assert.strictEqual(res.length, 3); - assert.ok(~res.indexOf('member0')); - assert.ok(~res.indexOf('member1')); - assert.ok(~res.indexOf('member2')); - return done(err); - }); - }); - - afterEach(function () { - client.end(true); - }); - }); - }); -}); diff --git a/test/commands/sunion.spec.js b/test/commands/sunion.spec.js deleted file mode 100644 index cc8eb624758..00000000000 --- a/test/commands/sunion.spec.js +++ /dev/null @@ -1,46 +0,0 @@ -'use strict'; - -var assert = require('assert'); -var config = require('../lib/config'); -var helper = require('../helper'); -var redis = config.redis; - -describe("The 'sunion' method", function () { - - helper.allTests(function (ip, args) { - - describe('using ' + ip, function () { - var client; - - beforeEach(function (done) { - client = redis.createClient.apply(null, args); - client.once('ready', function () { - client.flushdb(done); - }); - }); - - it('returns the union of a group of sets', function (done) { - client.sadd('sa', 'a', helper.isNumber(1)); - client.sadd('sa', 'b', helper.isNumber(1)); - client.sadd('sa', 'c', helper.isNumber(1)); - - client.sadd('sb', 'b', helper.isNumber(1)); - client.sadd('sb', 'c', helper.isNumber(1)); - client.sadd('sb', 'd', helper.isNumber(1)); - - client.sadd('sc', 'c', helper.isNumber(1)); - client.sadd('sc', 'd', helper.isNumber(1)); - client.sadd('sc', 'e', helper.isNumber(1)); - - client.sunion('sa', 'sb', 'sc', function (err, union) { - assert.deepEqual(union.sort(), ['a', 'b', 'c', 'd', 'e']); - return done(err); - }); - }); - - afterEach(function () { - client.end(true); - }); - }); - }); -}); diff --git a/test/commands/sunionstore.spec.js b/test/commands/sunionstore.spec.js deleted file mode 100644 index bd64c6f6b79..00000000000 --- a/test/commands/sunionstore.spec.js +++ /dev/null @@ -1,49 +0,0 @@ -'use strict'; - -var assert = require('assert'); -var config = require('../lib/config'); -var helper = require('../helper'); -var redis = config.redis; - -describe("The 'sunionstore' method", function () { - - helper.allTests(function (ip, args) { - - describe('using ' + ip, function () { - var client; - - beforeEach(function (done) { - client = redis.createClient.apply(null, args); - client.once('ready', function () { - client.flushdb(done); - }); - }); - - it('stores the result of a union', function (done) { - client.sadd('sa', 'a', helper.isNumber(1)); - client.sadd('sa', 'b', helper.isNumber(1)); - client.sadd('sa', 'c', helper.isNumber(1)); - - client.sadd('sb', 'b', helper.isNumber(1)); - client.sadd('sb', 'c', helper.isNumber(1)); - client.sadd('sb', 'd', helper.isNumber(1)); - - client.sadd('sc', 'c', helper.isNumber(1)); - client.sadd('sc', 'd', helper.isNumber(1)); - client.sadd('sc', 'e', helper.isNumber(1)); - - client.sunionstore('foo', 'sa', 'sb', 'sc', helper.isNumber(5)); - - client.smembers('foo', function (err, members) { - assert.equal(members.length, 5); - assert.deepEqual(members.sort(), ['a', 'b', 'c', 'd', 'e']); - return done(err); - }); - }); - - afterEach(function () { - client.end(true); - }); - }); - }); -}); diff --git a/test/commands/ttl.spec.js b/test/commands/ttl.spec.js deleted file mode 100644 index e176d41cb84..00000000000 --- a/test/commands/ttl.spec.js +++ /dev/null @@ -1,37 +0,0 @@ -'use strict'; - -var assert = require('assert'); -var config = require('../lib/config'); -var helper = require('../helper'); -var redis = config.redis; - -describe("The 'ttl' method", function () { - - helper.allTests(function (ip, args) { - - describe('using ' + ip, function () { - var client; - - beforeEach(function (done) { - client = redis.createClient.apply(null, args); - client.once('ready', function () { - client.flushdb(done); - }); - }); - - it('returns the current ttl on a key', function (done) { - client.set(['ttl key', 'ttl val'], helper.isString('OK')); - client.expire(['ttl key', '100'], helper.isNumber(1)); - client.TTL(['ttl key'], function (err, ttl) { - assert(ttl >= 99); - assert(ttl <= 100); - done(err); - }); - }); - - afterEach(function () { - client.end(true); - }); - }); - }); -}); diff --git a/test/commands/type.spec.js b/test/commands/type.spec.js deleted file mode 100644 index f70d79e9ef6..00000000000 --- a/test/commands/type.spec.js +++ /dev/null @@ -1,56 +0,0 @@ -'use strict'; - -var config = require('../lib/config'); -var helper = require('../helper'); -var redis = config.redis; - -describe("The 'type' method", function () { - - helper.allTests(function (ip, args) { - - describe('using ' + ip, function () { - var client; - - beforeEach(function (done) { - client = redis.createClient.apply(null, args); - client.once('ready', function () { - client.flushdb(done); - }); - }); - - it('reports string type', function (done) { - client.set(['string key', 'should be a string'], helper.isString('OK')); - client.TYPE(['string key'], helper.isString('string', done)); - }); - - it('reports list type', function (done) { - client.rpush(['list key', 'should be a list'], helper.isNumber(1)); - client.type(['list key'], helper.isString('list', done)); - }); - - it('reports set type', function (done) { - client.sadd(['set key', 'should be a set'], helper.isNumber(1)); - client.TYPE(['set key'], helper.isString('set', done)); - }); - - it('reports zset type', function (done) { - client.zadd('zset key', ['10.0', 'should be a zset'], helper.isNumber(1)); - client.TYPE(['zset key'], helper.isString('zset', done)); - }); - - it('reports hash type', function (done) { - client.hset('hash key', 'hashtest', 'should be a hash', helper.isNumber(1)); - client.TYPE(['hash key'], helper.isString('hash', done)); - }); - - it('reports none for null key', function (done) { - client.TYPE('not here yet', helper.isString('none', done)); - }); - - afterEach(function () { - client.end(true); - }); - }); - }); - -}); diff --git a/test/commands/watch.spec.js b/test/commands/watch.spec.js deleted file mode 100644 index 52a9b26f751..00000000000 --- a/test/commands/watch.spec.js +++ /dev/null @@ -1,54 +0,0 @@ -'use strict'; - -var assert = require('assert'); -var config = require('../lib/config'); -var helper = require('../helper'); -var redis = config.redis; - -describe("The 'watch' method", function () { - - helper.allTests(function (ip, args) { - - var watched = 'foobar'; - - describe('using ' + ip, function () { - var client; - - beforeEach(function (done) { - client = redis.createClient.apply(null, args); - client.once('ready', function () { - client.flushdb(done); - }); - }); - - afterEach(function () { - client.end(true); - }); - - it('does not execute transaction if watched key was modified prior to execution', function (done) { - client.WATCH(watched); - client.incr(watched); - var multi = client.multi(); - multi.incr(watched); - multi.exec(helper.isNull(done)); - }); - - it('successfully modifies other keys independently of transaction', function (done) { - client.set('unwatched', 200); - - client.set(watched, 0); - client.watch(watched); - client.incr(watched); - - client.multi().incr(watched).exec(function (err, replies) { - assert.strictEqual(replies, null, 'Aborted transaction multi-bulk reply should be null.'); - - client.get('unwatched', function (err, reply) { - assert.equal(reply, 200, 'Expected 200, got ' + reply); - return done(err); - }); - }); - }); - }); - }); -}); diff --git a/test/commands/zadd.spec.js b/test/commands/zadd.spec.js deleted file mode 100644 index f22963416c9..00000000000 --- a/test/commands/zadd.spec.js +++ /dev/null @@ -1,52 +0,0 @@ -'use strict'; - -var config = require('../lib/config'); -var helper = require('../helper'); -var assert = require('assert'); -var redis = config.redis; - -describe("The 'zadd' method", function () { - - helper.allTests(function (ip, args) { - - describe('using ' + ip, function () { - var client; - - beforeEach(function (done) { - client = redis.createClient.apply(null, args); - client.once('ready', function () { - client.flushdb(done); - }); - }); - - it('reports an error', function (done) { - if (helper.redisProcess().spawnFailed()) this.skip(); - client.zadd('infinity', [+'5t', 'should not be possible'], helper.isError(done)); - }); - - it('return inf / -inf', function (done) { - if (helper.redisProcess().spawnFailed()) this.skip(); - helper.serverVersionAtLeast.call(this, client, [3, 0, 2]); - client.zadd('infinity', [+Infinity, 'should be inf'], helper.isNumber(1)); - client.zadd('infinity', ['inf', 'should be also be inf'], helper.isNumber(1)); - client.zadd('infinity', -Infinity, 'should be negative inf', helper.isNumber(1)); - client.zadd('infinity', [99999999999999999999999, 'should not be inf'], helper.isNumber(1)); - client.zrange('infinity', 0, -1, 'WITHSCORES', function (err, res) { - assert.equal(res[5], 'inf'); - assert.equal(res[1], '-inf'); - if (process.platform !== 'win32') { - assert.equal(res[3], '9.9999999999999992e+22'); - } else { - assert.equal(res[3], '9.9999999999999992e+022'); - } - done(); - }); - }); - - afterEach(function () { - client.end(true); - }); - }); - }); - -}); diff --git a/test/commands/zscan.spec.js b/test/commands/zscan.spec.js deleted file mode 100644 index eb8acf44dbf..00000000000 --- a/test/commands/zscan.spec.js +++ /dev/null @@ -1,50 +0,0 @@ -'use strict'; - -var config = require('../lib/config'); -var helper = require('../helper'); -var assert = require('assert'); -var redis = config.redis; - -describe("The 'zscan' method", function () { - - helper.allTests(function (ip, args) { - - describe('using ' + ip, function () { - var client; - - beforeEach(function (done) { - client = redis.createClient.apply(null, args); - client.once('ready', function () { - client.flushdb(done); - }); - }); - - it('return values', function (done) { - if (helper.redisProcess().spawnFailed()) this.skip(); - helper.serverVersionAtLeast.call(this, client, [2, 8, 0]); - var hash = {}; - var set = []; - var zset = ['zset:1']; - for (var i = 0; i < 500; i++) { - hash['key_' + i] = 'value_' + i; - set.push('member_' + i); - zset.push(i, 'z_member_' + i); - } - client.hmset('hash:1', hash); - client.sadd('set:1', set); - client.zadd(zset); - client.zscan('zset:1', 0, 'MATCH', '*', 'COUNT', 500, function (err, res) { - assert(!err); - assert.strictEqual(res.length, 2); - assert.strictEqual(res[1].length, 1000); - done(); - }); - }); - - afterEach(function () { - client.end(true); - }); - }); - }); - -}); diff --git a/test/commands/zscore.spec.js b/test/commands/zscore.spec.js deleted file mode 100644 index 8b95e527641..00000000000 --- a/test/commands/zscore.spec.js +++ /dev/null @@ -1,35 +0,0 @@ -'use strict'; - -var config = require('../lib/config'); -var helper = require('../helper'); -var assert = require('assert'); -var redis = config.redis; - -describe("The 'zscore' method", function () { - - helper.allTests(function (ip, args) { - - describe('using ' + ip, function () { - var client; - - beforeEach(function (done) { - client = redis.createClient.apply(null, args); - client.once('ready', function () { - client.flushdb(done); - }); - }); - - it('should return the score of member in the sorted set at key', function (done) { - client.zadd('myzset', 1, 'one'); - client.zscore('myzset', 'one', function (err, res) { - assert.equal(res, 1); - done(); - }); - }); - - afterEach(function () { - client.end(true); - }); - }); - }); -}); diff --git a/test/conect.slave.spec.js b/test/conect.slave.spec.js deleted file mode 100644 index 5264a125e65..00000000000 --- a/test/conect.slave.spec.js +++ /dev/null @@ -1,99 +0,0 @@ -'use strict'; - -var assert = require('assert'); -var config = require('./lib/config'); -var helper = require('./helper'); -var RedisProcess = require('./lib/redis-process'); -var rp; -var path = require('path'); -var redis = config.redis; - -if (process.platform === 'win32') { - // TODO: Fix redis process spawn on windows - return; -} - -describe('master slave sync', function () { - var master = null; - var slave = null; - - before(function (done) { - helper.stopRedis(function () { - helper.startRedis('./conf/password.conf', done); - }); - }); - - before(function (done) { - if (helper.redisProcess().spawnFailed()) return done(); - master = redis.createClient({ - password: 'porkchopsandwiches' - }); - var multi = master.multi(); - var i = 0; - while (i < 1000) { - i++; - // Write some data in the redis instance, so there's something to sync - multi.set('foo' + i, 'bar' + new Array(500).join(Math.random())); - } - multi.exec(done); - }); - - it('sync process and no master should delay ready being emitted for slaves', function (done) { - if (helper.redisProcess().spawnFailed()) this.skip(); - - var port = 6381; - var firstInfo; - slave = redis.createClient({ - port: port, - retry_strategy: function (options) { - // Try to reconnect in very small intervals to catch the master_link_status down before the sync completes - return 10; - } - }); - - var tmp = slave.info.bind(slave); - var i = 0; - slave.info = function (err, res) { - i++; - tmp(err, res); - if (!firstInfo || Object.keys(firstInfo).length === 0) { - firstInfo = slave.server_info; - } - }; - - slave.on('connect', function () { - assert.strictEqual(i, 0); - }); - - var end = helper.callFuncAfter(done, 2); - - slave.on('ready', function () { - assert.strictEqual(this.server_info.master_link_status, 'up'); - assert.strictEqual(firstInfo.master_link_status, 'down'); - assert(i > 1); - this.get('foo300', function (err, res) { - assert.strictEqual(res.substr(0, 3), 'bar'); - end(err); - }); - }); - - RedisProcess.start(function (err, _rp) { - rp = _rp; - end(err); - }, path.resolve(__dirname, './conf/slave.conf'), port); - }); - - after(function (done) { - if (helper.redisProcess().spawnFailed()) return done(); - var end = helper.callFuncAfter(done, 3); - rp.stop(end); - slave.end(true); - master.flushdb(function (err) { - end(err); - master.end(true); - }); - helper.stopRedis(function () { - helper.startRedis('./conf/redis.conf', end); - }); - }); -}); diff --git a/test/conf/faulty.cert b/test/conf/faulty.cert deleted file mode 100644 index 30c98790061..00000000000 --- a/test/conf/faulty.cert +++ /dev/null @@ -1,19 +0,0 @@ ------BEGIN CERTIFICATE----- -MIIDATCCAemgAwIBAgIJALkMmVkQOERnMA0GCSqGSIb3DQEBBQUAMBcxFTATBgNV -BAMMDHJlZGlzLmpzLm9yZzAeFw0xNTEwMTkxMjIzMjRaFw0yNTEwMTYxMjIzMjRa -MBcxFTATBgNVBAMMDHJlZGlzLmpzLm9yZzCCASIwDQYJKoZIhvcNAQEBBQADggEP -ADCCAQoCggEBAJ/DmMTJHf7kyspxI1A/JmOc+KI9vxEcN5qn7IiZuGN7ghE43Q3q -XB2GUkMAuW1POkmM5yi3SuT1UXDR/4Gk7KlbHKMs37AV6PgJXX6oX0zu12LTAT7V -5byNrYtehSo42l1188dGEMCGaaf0cDntc7A3aW0ZtzrJt+2pu31Uatl2SEJCMra6 -+v6O0c9aHMF1cArKeawGqR+jHw6vXFZQbUd06nW5nQlUA6wVt1JjlLPwBwYsWLsi -YQxMC8NqpgAIg5tULSCpKwx5isL/CeotVVGDNZ/G8R1nTrxuygPlc3Qskj57hmV4 -tZK4JJxQFi7/9ehvjAvHohKrEPeqV5XL87cCAwEAAaNQME4wHQYDVR0OBBYEFCn/ -5hB+XY4pVOnaqvrmZMxrLFjLMB8GA1UdIwQYMBaAFCn/5hB+XY4pVOnaqvrmZMxr -LFjLMAwGA1UdEwQFMAMBAf8wDQYJKoZIhvcNAQEFBQADggEBAEduPyTHpXkCVZRQ -v6p+Ug4iVeXpxGCVr34y7EDUMgmuDdqsz1SrmqeDd0VmjZT8htbWw7QBKDPEBsbi -wl606aAn01iM+oUrwbtXxid1xfZj/j6pIhQVkGu7e/8A7Pr4QOP4OMdHB7EmqkAo -d/OLHa9LdKv2UtJHD6U7oVQbdBHrRV62125GMmotpQuSkEfZM6edKNzHPlqV/zJc -2kGCw3lZC21mTrsSMIC/FQiobPnig4kAvfh0of2rK/XAntlwT8ie1v1aK+jERsfm -uzMihl6XXBdzheq6KdIlf+5STHBIIRcvBoRKr5Va7EhnO03tTzeJowtqDv47yPC6 -w4kLcP8= ------END CERTIFICATE----- diff --git a/test/conf/password.conf b/test/conf/password.conf deleted file mode 100644 index 3b3c02f346e..00000000000 --- a/test/conf/password.conf +++ /dev/null @@ -1,6 +0,0 @@ -requirepass porkchopsandwiches -port 6379 -bind ::1 127.0.0.1 -unixsocket /tmp/redis.sock -unixsocketperm 700 -save "" diff --git a/test/conf/redis.conf b/test/conf/redis.conf deleted file mode 100644 index 9bf706c6543..00000000000 --- a/test/conf/redis.conf +++ /dev/null @@ -1,5 +0,0 @@ -port 6379 -bind ::1 127.0.0.1 -unixsocket /tmp/redis.sock -unixsocketperm 700 -save "" \ No newline at end of file diff --git a/test/conf/redis.js.org.cert b/test/conf/redis.js.org.cert deleted file mode 100644 index 6747b744ac3..00000000000 --- a/test/conf/redis.js.org.cert +++ /dev/null @@ -1,19 +0,0 @@ ------BEGIN CERTIFICATE----- -MIIDATCCAemgAwIBAgIJALkMmVkQOERnMA0GCSqGSIb3DQEBBQUAMBcxFTATBgNV -BAMMDHJlZGlzLmpzLm9yZzAeFw0xNTEwMTkxMjIzMjRaFw0yNTEwMTYxMjIzMjRa -MBcxFTATBgNVBAMMDHJlZGlzLmpzLm9yZzCCASIwDQYJKoZIhvcNAQEBBQADggEP -ADCCAQoCggEBAJ/DmMTJHf7kyspxI1A/JmOc+KI9vxEcN5qn7IiZuGN7ghE43Q3q -XB2GUkMAuW1POkmM5yi3SuT1UXDR/4Gk7KlbHKMs37AV6PgJXX6oX0zu12LTAT7V -5byNrYtehSo42l1188dGEMCGaaf0cDntc7A3aW0ZtzrJt+2pu31Uatl2SEJCMra6 -+v6O0c9aHMF1cArKeawGqR+jHw6vXFZQbUd05nW5nQlUA6wVt1JjlLPwBwYsWLsi -YQxMC8NqpgAIg5tULSCpKwx5isL/CeotVVGDNZ/G8R1nTrxuygPlc3Qskj57hmV4 -tZK4JJxQFi7/9ehvjAvHohKrEPeqV5XL87cCAwEAAaNQME4wHQYDVR0OBBYEFCn/ -5hB+XY4pVOnaqvrmZMxrLFjLMB8GA1UdIwQYMBaAFCn/5hB+XY4pVOnaqvrmZMxr -LFjLMAwGA1UdEwQFMAMBAf8wDQYJKoZIhvcNAQEFBQADggEBAEduPyTHpXkCVZRQ -v6p+Ug4iVeXpxGCVr34y7EDUMgmuDdqsz1SrmqeDd0VmjZT8htbWw7QBKDPEBsbi -wl606aAn01iM+oUrwbtXxid1xfZj/j6pIhQVkGu7e/8A7Pr4QOP4OMdHB7EmqkAo -d/OLHa9LdKv2UtJHD6U7oVQbdBHrRV62125GMmotpQuSkEfZM6edKNzHPlqV/zJc -2kGCw3lZC21mTrsSMIC/FQiobPnig4kAvfh0of2rK/XAntlwT8ie1v1aK+jERsfm -uzMihl6XXBdzheq6KdIlf+5STHBIIRcvBoRKr5Va7EhnO03tTzeJowtqDv47yPC6 -w4kLcP8= ------END CERTIFICATE----- diff --git a/test/conf/redis.js.org.key b/test/conf/redis.js.org.key deleted file mode 100644 index 9376fc1eee2..00000000000 --- a/test/conf/redis.js.org.key +++ /dev/null @@ -1,27 +0,0 @@ ------BEGIN RSA PRIVATE KEY----- -MIIEowIBAAKCAQEAn8OYxMkd/uTKynEjUD8mY5z4oj2/ERw3mqfsiJm4Y3uCETjd -DepcHYZSQwC5bU86SYznKLdK5PVRcNH/gaTsqVscoyzfsBXo+AldfqhfTO7XYtMB -PtXlvI2ti16FKjjaXXXzx0YQwIZpp/RwOe1zsDdpbRm3Osm37am7fVRq2XZIQkIy -trr6/o7Rz1ocwXVwCsp5rAapH6MfDq9cVlBtR3TmdbmdCVQDrBW3UmOUs/AHBixY -uyJhDEwLw2qmAAiDm1QtIKkrDHmKwv8J6i1VUYM1n8bxHWdOvG7KA+VzdCySPnuG -ZXi1krgknFAWLv/16G+MC8eiEqsQ96pXlcvztwIDAQABAoIBAGx4kLCLHCKDlGv+ -hMtnFNltKiJ9acxkLByFBsN4GwjwQk8PHIbmJ8Sj/hYf18WvlRN65zdtuxvYs4K2 -EZQkNcqGYdsoDHexaIt/UEs+ZfYF85bVTHMtJt3uE3Ycpq0UDK6H9wvFNnqAyBuQ -iuHJplJuTNYWL6Fqc8aZBwMA3crmwWTelgS+IXLH06E298+KIxbYrWSgrbcmV/Pj -Iwek4CPS0apoJnXxbZDDhAEYGOTxDNXGm+r7BaX/ePM2x1PPib2X9F2XqFV+A4T8 -Z91axKJwMrVuTrJkaLPDx9lNUskvvV6KgjZAtYRGpLQTN1AqXJZ09IoK9sNPE4rX -9fm4awECgYEAzMJkABL0UOoGJhdRf/R0aUOQMO7vYetX5SK9QXcEI04XYFieSaPm -71st+R/JlJ+LhrTrzGXvyU0tFAQaQZtwaGj/JhbptIpLlGrVf3mqSvxkNi/wzQnn -jBJrrf1ZkDiqtSy7AxGAefWblgK3R1ZU5+0a5jubDkmOltIlbULf0skCgYEAx76l -+5KhWOJPvrjNGB1a8oVXiFzoCpaVVZIhSdl0AtvkKollm5Ou+CKYpE3fKrejRXTD -zmr5bJFXT3VlmIa010cgXJ2btlFa1RiNzgretsOmMcHxLkpAu2/a0L4psHlCrWVK -fxbUW0BYEFVXBDe/4JhFw41YqohdPkFAyo5OUn8CgYBQZGYkzUxVVHzTicY66bym -85ryS217UY5x7WDHCjZ6shdlgYWsPgjWo0L6k+tuSfHbEr+dwcwSihWPzUiNx7yr -kcXTq51YgA/KluN6KEefJ1clG099AU2C5lyWtGjswgLsHULTopSBzdenXyuce53c -bXBpQq/PPTwZpSqCqoX8WQKBgGe+nsk+jGz1BoRBycyHmrAyD5e04ZR2R9PtFTsd -JYNCoIxzVoHqv8sDdRKJm6q9PKEbl4PDzg7UomuTxxPki1LxD17rQW/9a1cY7LYi -sTBuCAj5+YGYcWypGRaoXlDZeodC/+Fogx1uGw9Is+xt5EwL6tg5tt7D+uIV1Egg -h4+TAoGBAKYA/jn9v93bzPi+w1rlZrlPufRSr4k3mcHae165N/1PnjSguTFIF5DW -+1f5S+XioNyTcfx5gKI8f6wRn1j5zbB24GXgu8dXCzRHC2gzrwq2D9v1od4zP/o7 -xFxyiNGOMUJ7uW9d/nEL5Eg4CQKZEkZNmzHhuKNr8wDSr16DhXVK ------END RSA PRIVATE KEY----- diff --git a/test/conf/rename.conf b/test/conf/rename.conf deleted file mode 100644 index 207fe156221..00000000000 --- a/test/conf/rename.conf +++ /dev/null @@ -1,8 +0,0 @@ -port 6379 -bind ::1 127.0.0.1 -unixsocket /tmp/redis.sock -unixsocketperm 700 -save "" -rename-command SET 807081f5afa96845a02816a28b7258c3 -rename-command GET f397808a43ceca3963e22b4e13deb672 -rename-command GETRANGE 9e3102b15cf231c4e9e940f284744fe0 diff --git a/test/conf/slave.conf b/test/conf/slave.conf deleted file mode 100644 index f5632bbffcb..00000000000 --- a/test/conf/slave.conf +++ /dev/null @@ -1,7 +0,0 @@ -port 6381 -bind ::1 127.0.0.1 -unixsocket /tmp/redis6381.sock -unixsocketperm 700 -slaveof localhost 6379 -masterauth porkchopsandwiches -save "" diff --git a/test/conf/stunnel.conf.template b/test/conf/stunnel.conf.template deleted file mode 100644 index 17ee3e879c5..00000000000 --- a/test/conf/stunnel.conf.template +++ /dev/null @@ -1,11 +0,0 @@ -pid = __dirname/stunnel.pid -; output = __dirname/stunnel.log -CAfile = __dirname/redis.js.org.cert -cert = __dirname/redis.js.org.cert -key = __dirname/redis.js.org.key -client = no -foreground = yes -debug = 7 -[redis] -accept = 127.0.0.1:6380 -connect = 127.0.0.1:6379 diff --git a/test/connection.spec.js b/test/connection.spec.js deleted file mode 100644 index 827ff69c8aa..00000000000 --- a/test/connection.spec.js +++ /dev/null @@ -1,637 +0,0 @@ -'use strict'; - -var assert = require('assert'); -var config = require('./lib/config'); -var helper = require('./helper'); -var redis = config.redis; -var intercept = require('intercept-stdout'); -var net = require('net'); -var client; - -describe('connection tests', function () { - - beforeEach(function () { - client = null; - }); - afterEach(function () { - if (!client) return; - - client.end(true); - }); - - it('unofficially support for a private stream', function () { - // While using a private stream, reconnection and other features are not going to work properly. - // Besides that some functions also have to be monkey patched to be safe from errors in this case. - // Therefore this is not officially supported! - var socket = new net.Socket(); - client = new redis.RedisClient({ - prefix: 'test' - }, socket); - assert.strictEqual(client.stream, socket); - assert.strictEqual(client.stream.listeners('error').length, 1); - assert.strictEqual(client.address, '"Private stream"'); - // Pretent a reconnect event - client.create_stream(); - assert.strictEqual(client.stream, socket); - assert.strictEqual(client.stream.listeners('error').length, 1); - }); - - describe('quit on lost connections', function () { - - it('calling quit while the connection is down should not end in reconnecting version a', function (done) { - var called = 0; - client = redis.createClient({ - port: 9999, - retry_strategy: function (options) { - var bool = client.quit(function (err, res) { - assert.strictEqual(res, 'OK'); - assert.strictEqual(err, null); - assert.strictEqual(called++, -1); - setTimeout(done, 25); - }); - assert.strictEqual(bool, false); - assert.strictEqual(called++, 0); - return 5; - } - }); - client.set('foo', 'bar', function (err, res) { - assert.strictEqual(err.message, 'Stream connection ended and command aborted.'); - called = -1; - }); - }); - - it('calling quit while the connection is down should not end in reconnecting version b', function (done) { - var called = false; - client = redis.createClient(9999); - client.set('foo', 'bar', function (err, res) { - assert.strictEqual(err.message, 'Stream connection ended and command aborted.'); - called = true; - }); - var bool = client.quit(function (err, res) { - assert.strictEqual(res, 'OK'); - assert.strictEqual(err, null); - assert(called); - done(); - }); - assert.strictEqual(bool, false); - }); - - it('calling quit while the connection is down without offline queue should end the connection right away', function (done) { - var called = false; - client = redis.createClient(9999, { - enable_offline_queue: false - }); - client.set('foo', 'bar', function (err, res) { - assert.strictEqual(err.message, 'SET can\'t be processed. The connection is not yet established and the offline queue is deactivated.'); - called = true; - }); - var bool = client.quit(function (err, res) { - assert.strictEqual(res, 'OK'); - assert.strictEqual(err, null); - assert(called); - done(); - }); - // TODO: In v.3 the quit command would be fired right away, so bool should be true - assert.strictEqual(bool, false); - }); - - it('calling quit while connected without offline queue should end the connection when all commands have finished', function (done) { - var called = false; - client = redis.createClient({ - enable_offline_queue: false - }); - client.on('ready', function () { - client.set('foo', 'bar', function (err, res) { - assert.strictEqual(res, 'OK'); - called = true; - }); - var bool = client.quit(function (err, res) { - assert.strictEqual(res, 'OK'); - assert.strictEqual(err, null); - assert(called); - done(); - }); - // TODO: In v.3 the quit command would be fired right away, so bool should be true - assert.strictEqual(bool, true); - }); - }); - - it('do not quit before connected or a connection issue is detected', function (done) { - client = redis.createClient(); - client.set('foo', 'bar', helper.isString('OK')); - var bool = client.quit(done); - assert.strictEqual(bool, false); - }); - - it('quit "succeeds" even if the client connection is closed while doing so', function (done) { - client = redis.createClient(); - client.set('foo', 'bar', function (err, res) { - assert.strictEqual(res, 'OK'); - client.quit(function (err, res) { - assert.strictEqual(res, 'OK'); - done(err); - }); - client.end(true); // Flushing the quit command should result in a success - }); - }); - - it('quit right away if connection drops while quit command is on the fly', function (done) { - client = redis.createClient(); - client.once('ready', function () { - client.set('foo', 'bar', helper.isError()); - var bool = client.quit(done); - assert.strictEqual(bool, true); - process.nextTick(function () { - client.stream.destroy(); - }); - }); - }); - - }); - - helper.allTests(function (ip, args) { - - describe('using ' + ip, function () { - - describe('on lost connection', function () { - it('emit an error after max retry timeout and do not try to reconnect afterwards', function (done) { - // TODO: Investigate why this test fails with windows. Reconnect is only triggered once - if (process.platform === 'win32') this.skip(); - - var connect_timeout = 600; // in ms - client = redis.createClient({ - connect_timeout: connect_timeout - }); - var time = 0; - - client.once('ready', function () { - helper.killConnection(client); - }); - - client.on('reconnecting', function (params) { - time += params.delay; - }); - - client.on('error', function (err) { - if (/Redis connection in broken state: connection timeout.*?exceeded./.test(err.message)) { - process.nextTick(function () { // End is called after the error got emitted - assert.strictEqual(client.emitted_end, true); - assert.strictEqual(client.connected, false); - assert.strictEqual(client.ready, false); - assert.strictEqual(client.closing, true); - assert.strictEqual(client.retry_totaltime, connect_timeout); - assert.strictEqual(time, connect_timeout); - done(); - }); - } - }); - }); - - it('end connection while retry is still ongoing', function (done) { - var connect_timeout = 1000; // in ms - client = redis.createClient({ - connect_timeout: connect_timeout - }); - - client.once('ready', function () { - helper.killConnection(client); - }); - - client.on('reconnecting', function (params) { - client.end(true); - assert.strictEqual(params.times_connected, 1); - setTimeout(done, 5); - }); - }); - - it('can not connect with wrong host / port in the options object', function (done) { - var options = { - host: 'somewhere', - port: 6379, - family: ip, - max_attempts: 1 - }; - client = redis.createClient(options); - assert.strictEqual(client.connection_options.family, ip === 'IPv6' ? 6 : 4); - assert.strictEqual(Object.keys(options).length, 4); - var end = helper.callFuncAfter(done, 2); - - client.on('error', function (err) { - assert(/CONNECTION_BROKEN|ENOTFOUND|EAI_AGAIN/.test(err.code)); - end(); - }); - - }); - - it('emits error once if reconnecting after command has been executed but not yet returned without callback', function (done) { - client = redis.createClient.apply(null, args); - - client.on('ready', function () { - client.set('foo', 'bar', function (err) { - assert.strictEqual(err.code, 'UNCERTAIN_STATE'); - done(); - }); - // Abort connection before the value returned - client.stream.destroy(); - }); - }); - - it('retryStrategy used to reconnect with individual error', function (done) { - client = redis.createClient({ - retryStrategy: function (options) { - if (options.totalRetryTime > 150) { - client.set('foo', 'bar'); - client.once('error', function (err) { - assert.strictEqual(err.message, 'Redis connection in broken state: retry aborted.'); - assert.strictEqual(err.origin.message, 'Connection timeout'); - done(); - }); - // Pass a individual error message to the error handler - return new Error('Connection timeout'); - } - return Math.min(options.attempt * 25, 200); - }, - port: 9999 - }); - }); - - it('retry_strategy used to reconnect', function (done) { - client = redis.createClient({ - retry_strategy: function (options) { - if (options.total_retry_time > 150) { - client.set('foo', 'bar'); - client.once('error', function (err) { - assert.strictEqual(err.message, 'Redis connection in broken state: retry aborted.'); - assert.strictEqual(err.code, 'CONNECTION_BROKEN'); - assert.strictEqual(err.origin.code, 'ECONNREFUSED'); - done(); - }); - return false; - } - return Math.min(options.attempt * 25, 200); - }, - port: 9999 - }); - }); - - it('retryStrategy used to reconnect with defaults', function (done) { - var unhookIntercept = intercept(function () { - return ''; - }); - redis.debugMode = true; - client = redis.createClient({ - retryStrategy: function (options) { - client.set('foo', 'bar'); - assert(redis.debugMode); - return null; - } - }); - setTimeout(function () { - client.stream.destroy(); - }, 50); - client.on('error', function (err) { - if (err instanceof redis.AbortError) { - assert.strictEqual(err.message, 'Redis connection in broken state: retry aborted.'); - assert.strictEqual(err.code, 'CONNECTION_BROKEN'); - unhookIntercept(); - redis.debugMode = false; - done(); - } - }); - }); - }); - - describe('when not connected', function () { - - it('emit an error after the socket timeout exceeded the connect_timeout time', function (done) { - var connect_timeout = 500; // in ms - client = redis.createClient({ - // Auto detect ipv4 and use non routable ip to trigger the timeout - host: '10.255.255.1', - connect_timeout: connect_timeout - }); - process.nextTick(function () { - assert.strictEqual(client.stream.listeners('timeout').length, 1); - }); - assert.strictEqual(client.address, '10.255.255.1:6379'); - assert.strictEqual(client.connection_options.family, 4); - - client.on('reconnecting', function (params) { - throw new Error('No reconnect, since no connection was ever established'); - }); - - var time = Date.now(); - client.on('error', function (err) { - if (err.code === 'ENETUNREACH') { // The test is run without a internet connection. Pretent it works - return done(); - } - assert(/Redis connection in broken state: connection timeout.*?exceeded./.test(err.message), err.message); - // The code execution on windows is very slow at times - var add = process.platform !== 'win32' ? 15 : 200; - var now = Date.now(); - assert(now - time < connect_timeout + add, 'The real timeout time should be below ' + (connect_timeout + add) + 'ms but is: ' + (now - time)); - // Timers sometimes trigger early (e.g. 1ms to early) - assert(now - time >= connect_timeout - 5, 'The real timeout time should be above ' + connect_timeout + 'ms, but it is: ' + (now - time)); - done(); - }); - }); - - it('use the system socket timeout if the connect_timeout has not been provided', function (done) { - client = redis.createClient({ - host: '0:0:0:0:0:0:0:1', // auto detect ip v6 - no_ready_check: true - }); - assert.strictEqual(client.address, '0:0:0:0:0:0:0:1:6379'); - assert.strictEqual(client.connection_options.family, 6); - process.nextTick(function () { - assert.strictEqual(client.stream.listeners('timeout').length, 0); - done(); - }); - }); - - it('clears the socket timeout after a connection has been established', function (done) { - client = redis.createClient({ - connect_timeout: 1000 - }); - process.nextTick(function () { - // node > 6 - var timeout = client.stream.timeout; - // node <= 6 - if (timeout === undefined) timeout = client.stream._idleTimeout; - assert.strictEqual(timeout, 1000); - }); - client.on('connect', function () { - // node > 6 - var expected = 0; - var timeout = client.stream.timeout; - // node <= 6 - if (timeout === undefined) { - timeout = client.stream._idleTimeout; - expected = -1; - } - assert.strictEqual(timeout, expected); - assert.strictEqual(client.stream.listeners('timeout').length, 0); - client.on('ready', done); - }); - }); - - it('connect with host and port provided in the options object', function (done) { - client = redis.createClient({ - host: 'localhost', - port: '6379', - connect_timeout: 1000 - }); - - client.once('ready', done); - }); - - it('connect with path provided in the options object', function (done) { - if (process.platform === 'win32') { - this.skip(); - } - client = redis.createClient({ - path: '/tmp/redis.sock', - connect_timeout: 1000 - }); - - var end = helper.callFuncAfter(done, 2); - - client.once('ready', end); - client.set('foo', 'bar', end); - }); - - it('connects correctly with args', function (done) { - client = redis.createClient.apply(null, args); - client.on('error', done); - - client.once('ready', function () { - client.removeListener('error', done); - client.get('recon 1', done); - }); - }); - - it('connects correctly with default values', function (done) { - client = redis.createClient(); - client.on('error', done); - - client.once('ready', function () { - client.removeListener('error', done); - client.get('recon 1', done); - }); - }); - - it('connects with a port only', function (done) { - client = redis.createClient(6379); - assert.strictEqual(client.connection_options.family, 4); - client.on('error', done); - - client.once('ready', function () { - client.removeListener('error', done); - client.get('recon 1', done); - }); - }); - - it('connects correctly to localhost', function (done) { - client = redis.createClient(null, null); - client.on('error', done); - - client.once('ready', function () { - client.removeListener('error', done); - client.get('recon 1', done); - }); - }); - - it('connects correctly to the provided host with the port set to null', function (done) { - client = redis.createClient(null, 'localhost'); - client.on('error', done); - assert.strictEqual(client.address, 'localhost:6379'); - - client.once('ready', function () { - client.set('foo', 'bar'); - client.get('foo', function (err, res) { - assert.strictEqual(res, 'bar'); - done(err); - }); - }); - }); - - it('connects correctly to localhost and no ready check', function (done) { - client = redis.createClient(undefined, undefined, { - no_ready_check: true - }); - client.on('error', done); - - client.once('ready', function () { - client.set('foo', 'bar'); - client.get('foo', function (err, res) { - assert.strictEqual(res, 'bar'); - done(err); - }); - }); - }); - - it('connects correctly to the provided host with the port set to undefined', function (done) { - client = redis.createClient(undefined, 'localhost', { - no_ready_check: true - }); - client.on('error', done); - assert.strictEqual(client.address, 'localhost:6379'); - - client.once('ready', function () { - client.set('foo', 'bar'); - client.get('foo', function (err, res) { - assert.strictEqual(res, 'bar'); - done(err); - }); - }); - }); - - it('connects correctly even if the info command is not present on the redis server', function (done) { - client = redis.createClient.apply(null, args); - client.info = function (cb) { - // Mock the result - cb(new Error("ERR unknown command 'info'")); - }; - client.once('ready', function () { - assert.strictEqual(Object.keys(client.server_info).length, 0); - done(); - }); - }); - - it('fake the stream to mock redis', function () { - // This is needed for libraries that want to mock the stream like fakeredis - var temp = redis.RedisClient.prototype.create_stream; - var create_stream_string = String(temp); - redis.RedisClient.prototype.create_stream = function () { - this.connected = true; - this.ready = true; - }; - client = new redis.RedisClient(); - assert.strictEqual(client.stream, undefined); - assert.strictEqual(client.ready, true); - assert.strictEqual(client.connected, true); - client.end = function () {}; - assert(create_stream_string !== String(redis.RedisClient.prototype.create_stream)); - redis.RedisClient.prototype.create_stream = temp; - assert(create_stream_string === String(redis.RedisClient.prototype.create_stream)); - }); - - if (ip === 'IPv4') { - it('allows connecting with the redis url to the default host and port, select db 3 and warn about duplicate db option', function (done) { - client = redis.createClient('redis:///3?db=3'); - assert.strictEqual(client.selected_db, '3'); - client.on('ready', done); - }); - - it('allows connecting with the redis url and the default port and auth provided even though it is not required', function (done) { - client = redis.createClient('redis://:porkchopsandwiches@' + config.HOST[ip] + '/'); - var end = helper.callFuncAfter(done, 2); - client.on('warning', function (msg) { - assert.strictEqual(msg, 'Warning: Redis server does not require a password, but a password was supplied.'); - end(); - }); - client.on('ready', end); - }); - - it('allows connecting with the redis url as first parameter and the options as second parameter', function (done) { - client = redis.createClient('//127.0.0.1', { - connect_timeout: 1000 - }); - assert.strictEqual(client.options.connect_timeout, 1000); - client.on('ready', done); - }); - - it('allows connecting with the redis url in the options object and works with protocols other than the redis protocol (e.g. http)', function (done) { - client = redis.createClient({ - url: 'http://:porkchopsandwiches@' + config.HOST[ip] + '/3' - }); - assert.strictEqual(client.auth_pass, 'porkchopsandwiches'); - assert.strictEqual(+client.selected_db, 3); - assert(!client.options.port); - assert.strictEqual(client.options.host, config.HOST[ip]); - client.on('ready', done); - }); - - it('allows connecting with the redis url and no auth and options as second parameter', function (done) { - var options = { - detect_buffers: false - }; - client = redis.createClient('redis://' + config.HOST[ip] + ':' + config.PORT, options); - assert.strictEqual(Object.keys(options).length, 1); - client.on('ready', done); - }); - - it('allows connecting with the redis url and no auth and options as third parameter', function (done) { - client = redis.createClient('redis://' + config.HOST[ip] + ':' + config.PORT, null, { - detect_buffers: false - }); - client.on('ready', done); - }); - } - - it('redis still loading <= 500', function (done) { - client = redis.createClient.apply(null, args); - var tmp = client.info.bind(client); - var end = helper.callFuncAfter(done, 3); - var delayed = false; - var time; - // Mock original function and pretent redis is still loading - client.info = function (cb) { - tmp(function (err, res) { - if (!delayed) { - assert(!err); - client.server_info.loading = 1; - client.server_info.loading_eta_seconds = 0.5; - delayed = true; - time = Date.now(); - } - end(); - cb(err, res); - }); - }; - client.on('ready', function () { - var rest = Date.now() - time; - assert(rest >= 495, 'Rest should be equal or above 500 ms but is: ' + rest); // setTimeout might trigger early - // Be on the safe side and accept 200ms above the original value - assert(rest - 250 < 500, 'Rest - 250 should be below 500 ms but is: ' + (rest - 250)); - assert(delayed); - end(); - }); - }); - - it('redis still loading > 1000ms', function (done) { - client = redis.createClient.apply(null, args); - var tmp = client.info.bind(client); - var end = helper.callFuncAfter(done, 3); - var delayed = false; - var time; - // Mock original function and pretent redis is still loading - client.info = function (cb) { - tmp(function (err, res) { - if (!delayed) { - assert(!err); - // Try reconnecting after one second even if redis tells us the time needed is above one second - client.server_info.loading = 1; - client.server_info.loading_eta_seconds = 2.5; - delayed = true; - time = Date.now(); - } - end(); - cb(err, res); - }); - }; - client.on('ready', function () { - var rest = Date.now() - time; - assert(rest >= 998, '`rest` should be equal or above 1000 ms but is: ' + rest); // setTimeout might trigger early - // Be on the safe side and accept 200ms above the original value - assert(rest - 250 < 1000, '`rest` - 250 should be below 1000 ms but is: ' + (rest - 250)); - assert(delayed); - end(); - }); - }); - - }); - - }); - }); -}); diff --git a/test/custom_errors.spec.js b/test/custom_errors.spec.js deleted file mode 100644 index 90a5aaeb66a..00000000000 --- a/test/custom_errors.spec.js +++ /dev/null @@ -1,89 +0,0 @@ -'use strict'; - -var assert = require('assert'); -var errors = require('../lib/customErrors'); - -describe('errors', function () { - - describe('AbortError', function () { - it('should inherit from Error', function () { - var e = new errors.AbortError({}); - assert.strictEqual(e.message, ''); - assert.strictEqual(e.name, 'AbortError'); - assert.strictEqual(Object.keys(e).length, 0); - assert(e instanceof Error); - assert(e instanceof errors.AbortError); - }); - - it('should list options properties but not name and message', function () { - var e = new errors.AbortError({ - name: 'weird', - message: 'hello world', - property: true - }); - assert.strictEqual(e.message, 'hello world'); - assert.strictEqual(e.name, 'weird'); - assert.strictEqual(e.property, true); - assert.strictEqual(Object.keys(e).length, 2); - assert(e instanceof Error); - assert(e instanceof errors.AbortError); - assert(delete e.name); - assert.strictEqual(e.name, 'AbortError'); - }); - - it('should change name and message', function () { - var e = new errors.AbortError({ - message: 'hello world', - property: true - }); - assert.strictEqual(e.name, 'AbortError'); - assert.strictEqual(e.message, 'hello world'); - e.name = 'foo'; - e.message = 'foobar'; - assert.strictEqual(e.name, 'foo'); - assert.strictEqual(e.message, 'foobar'); - }); - }); - - describe('AggregateError', function () { - it('should inherit from Error and AbortError', function () { - var e = new errors.AggregateError({}); - assert.strictEqual(e.message, ''); - assert.strictEqual(e.name, 'AggregateError'); - assert.strictEqual(Object.keys(e).length, 0); - assert(e instanceof Error); - assert(e instanceof errors.AggregateError); - assert(e instanceof errors.AbortError); - }); - - it('should list options properties but not name and message', function () { - var e = new errors.AggregateError({ - name: 'weird', - message: 'hello world', - property: true - }); - assert.strictEqual(e.message, 'hello world'); - assert.strictEqual(e.name, 'weird'); - assert.strictEqual(e.property, true); - assert.strictEqual(Object.keys(e).length, 2); - assert(e instanceof Error); - assert(e instanceof errors.AggregateError); - assert(e instanceof errors.AbortError); - assert(delete e.name); - assert.strictEqual(e.name, 'AggregateError'); - }); - - it('should change name and message', function () { - var e = new errors.AggregateError({ - message: 'hello world', - property: true - }); - assert.strictEqual(e.name, 'AggregateError'); - assert.strictEqual(e.message, 'hello world'); - e.name = 'foo'; - e.message = 'foobar'; - assert.strictEqual(e.name, 'foo'); - assert.strictEqual(e.message, 'foobar'); - }); - }); -}); diff --git a/test/detect_buffers.spec.js b/test/detect_buffers.spec.js deleted file mode 100644 index faa63efb1f1..00000000000 --- a/test/detect_buffers.spec.js +++ /dev/null @@ -1,268 +0,0 @@ -'use strict'; - -var assert = require('assert'); -var config = require('./lib/config'); -var helper = require('./helper'); -var redis = config.redis; - -describe('detect_buffers', function () { - - var client; - var args = config.configureClient('localhost', { - detect_buffers: true - }); - - beforeEach(function (done) { - client = redis.createClient.apply(null, args); - client.once('error', done); - client.once('connect', function () { - client.flushdb(function (err) { - client.hmset('hash key 2', 'key 1', 'val 1', 'key 2', 'val 2'); - client.set('string key 1', 'string value'); - return done(err); - }); - }); - }); - - afterEach(function () { - client.end(true); - }); - - describe('get', function () { - describe('first argument is a string', function () { - it('returns a string', function (done) { - client.get('string key 1', helper.isString('string value', done)); - }); - - it('returns a string when executed as part of transaction', function (done) { - client.multi().get('string key 1').exec(function (err, res) { - helper.isString('string value', done)(err, res[0]); - }); - }); - }); - - describe('first argument is a buffer', function () { - it('returns a buffer', function (done) { - client.get(Buffer.from('string key 1'), function (err, reply) { - assert.strictEqual(true, Buffer.isBuffer(reply)); - assert.strictEqual('', reply.inspect()); - return done(err); - }); - }); - - it('returns a bufffer when executed as part of transaction', function (done) { - client.multi().get(Buffer.from('string key 1')).exec(function (err, reply) { - assert.strictEqual(1, reply.length); - assert.strictEqual(true, Buffer.isBuffer(reply[0])); - assert.strictEqual('', reply[0].inspect()); - return done(err); - }); - }); - }); - }); - - describe('multi.hget', function () { - it('can interleave string and buffer results', function (done) { - client.multi() - .hget('hash key 2', 'key 1') - .hget(Buffer.from('hash key 2'), 'key 1') - .hget('hash key 2', Buffer.from('key 2')) - .hget('hash key 2', 'key 2') - .exec(function (err, reply) { - assert.strictEqual(true, Array.isArray(reply)); - assert.strictEqual(4, reply.length); - assert.strictEqual('val 1', reply[0]); - assert.strictEqual(true, Buffer.isBuffer(reply[1])); - assert.strictEqual('', reply[1].inspect()); - assert.strictEqual(true, Buffer.isBuffer(reply[2])); - assert.strictEqual('', reply[2].inspect()); - assert.strictEqual('val 2', reply[3]); - return done(err); - }); - }); - }); - - describe('batch.hget', function () { - it('can interleave string and buffer results', function (done) { - client.batch() - .hget('hash key 2', 'key 1') - .hget(Buffer.from('hash key 2'), 'key 1') - .hget('hash key 2', Buffer.from('key 2')) - .hget('hash key 2', 'key 2') - .exec(function (err, reply) { - assert.strictEqual(true, Array.isArray(reply)); - assert.strictEqual(4, reply.length); - assert.strictEqual('val 1', reply[0]); - assert.strictEqual(true, Buffer.isBuffer(reply[1])); - assert.strictEqual('', reply[1].inspect()); - assert.strictEqual(true, Buffer.isBuffer(reply[2])); - assert.strictEqual('', reply[2].inspect()); - assert.strictEqual('val 2', reply[3]); - return done(err); - }); - }); - }); - - describe('hmget', function () { - describe('first argument is a string', function () { - it('returns strings for keys requested', function (done) { - client.hmget('hash key 2', 'key 1', 'key 2', function (err, reply) { - assert.strictEqual(true, Array.isArray(reply)); - assert.strictEqual(2, reply.length); - assert.strictEqual('val 1', reply[0]); - assert.strictEqual('val 2', reply[1]); - return done(err); - }); - }); - - it('returns strings for keys requested in transaction', function (done) { - client.multi().hmget('hash key 2', 'key 1', 'key 2').exec(function (err, reply) { - assert.strictEqual(true, Array.isArray(reply)); - assert.strictEqual(1, reply.length); - assert.strictEqual(2, reply[0].length); - assert.strictEqual('val 1', reply[0][0]); - assert.strictEqual('val 2', reply[0][1]); - return done(err); - }); - }); - - it('handles array of strings with undefined values (repro #344)', function (done) { - client.hmget('hash key 2', 'key 3', 'key 4', function (err, reply) { - assert.strictEqual(true, Array.isArray(reply)); - assert.strictEqual(2, reply.length); - assert.equal(null, reply[0]); - assert.equal(null, reply[1]); - return done(err); - }); - }); - - it('handles array of strings with undefined values in transaction (repro #344)', function (done) { - client.multi().hmget('hash key 2', 'key 3', 'key 4').exec(function (err, reply) { - assert.strictEqual(true, Array.isArray(reply)); - assert.strictEqual(1, reply.length); - assert.strictEqual(2, reply[0].length); - assert.equal(null, reply[0][0]); - assert.equal(null, reply[0][1]); - return done(err); - }); - }); - }); - - describe('first argument is a buffer', function () { - it('returns buffers for keys requested', function (done) { - client.hmget(Buffer.from('hash key 2'), 'key 1', 'key 2', function (err, reply) { - assert.strictEqual(true, Array.isArray(reply)); - assert.strictEqual(2, reply.length); - assert.strictEqual(true, Buffer.isBuffer(reply[0])); - assert.strictEqual(true, Buffer.isBuffer(reply[1])); - assert.strictEqual('', reply[0].inspect()); - assert.strictEqual('', reply[1].inspect()); - return done(err); - }); - }); - - it('returns buffers for keys requested in transaction', function (done) { - client.multi().hmget(Buffer.from('hash key 2'), 'key 1', 'key 2').exec(function (err, reply) { - assert.strictEqual(true, Array.isArray(reply)); - assert.strictEqual(1, reply.length); - assert.strictEqual(2, reply[0].length); - assert.strictEqual(true, Buffer.isBuffer(reply[0][0])); - assert.strictEqual(true, Buffer.isBuffer(reply[0][1])); - assert.strictEqual('', reply[0][0].inspect()); - assert.strictEqual('', reply[0][1].inspect()); - return done(err); - }); - }); - - it('returns buffers for keys requested in .batch', function (done) { - client.batch().hmget(Buffer.from('hash key 2'), 'key 1', 'key 2').exec(function (err, reply) { - assert.strictEqual(true, Array.isArray(reply)); - assert.strictEqual(1, reply.length); - assert.strictEqual(2, reply[0].length); - assert.strictEqual(true, Buffer.isBuffer(reply[0][0])); - assert.strictEqual(true, Buffer.isBuffer(reply[0][1])); - assert.strictEqual('', reply[0][0].inspect()); - assert.strictEqual('', reply[0][1].inspect()); - return done(err); - }); - }); - }); - }); - - describe('hgetall', function (done) { - describe('first argument is a string', function () { - it('returns string values', function (done) { - client.hgetall('hash key 2', function (err, reply) { - assert.strictEqual('object', typeof reply); - assert.strictEqual(2, Object.keys(reply).length); - assert.strictEqual('val 1', reply['key 1']); - assert.strictEqual('val 2', reply['key 2']); - return done(err); - }); - }); - - it('returns string values when executed in transaction', function (done) { - client.multi().hgetall('hash key 2').exec(function (err, reply) { - assert.strictEqual(1, reply.length); - assert.strictEqual('object', typeof reply[0]); - assert.strictEqual(2, Object.keys(reply[0]).length); - assert.strictEqual('val 1', reply[0]['key 1']); - assert.strictEqual('val 2', reply[0]['key 2']); - return done(err); - }); - }); - - it('returns string values when executed in .batch', function (done) { - client.batch().hgetall('hash key 2').exec(function (err, reply) { - assert.strictEqual(1, reply.length); - assert.strictEqual('object', typeof reply[0]); - assert.strictEqual(2, Object.keys(reply[0]).length); - assert.strictEqual('val 1', reply[0]['key 1']); - assert.strictEqual('val 2', reply[0]['key 2']); - return done(err); - }); - }); - }); - - describe('first argument is a buffer', function () { - it('returns buffer values', function (done) { - client.hgetall(Buffer.from('hash key 2'), function (err, reply) { - assert.strictEqual(null, err); - assert.strictEqual('object', typeof reply); - assert.strictEqual(2, Object.keys(reply).length); - assert.strictEqual(true, Buffer.isBuffer(reply['key 1'])); - assert.strictEqual(true, Buffer.isBuffer(reply['key 2'])); - assert.strictEqual('', reply['key 1'].inspect()); - assert.strictEqual('', reply['key 2'].inspect()); - return done(err); - }); - }); - - it('returns buffer values when executed in transaction', function (done) { - client.multi().hgetall(Buffer.from('hash key 2')).exec(function (err, reply) { - assert.strictEqual(1, reply.length); - assert.strictEqual('object', typeof reply[0]); - assert.strictEqual(2, Object.keys(reply[0]).length); - assert.strictEqual(true, Buffer.isBuffer(reply[0]['key 1'])); - assert.strictEqual(true, Buffer.isBuffer(reply[0]['key 2'])); - assert.strictEqual('', reply[0]['key 1'].inspect()); - assert.strictEqual('', reply[0]['key 2'].inspect()); - return done(err); - }); - }); - - it('returns buffer values when executed in .batch', function (done) { - client.batch().hgetall(Buffer.from('hash key 2')).exec(function (err, reply) { - assert.strictEqual(1, reply.length); - assert.strictEqual('object', typeof reply[0]); - assert.strictEqual(2, Object.keys(reply[0]).length); - assert.strictEqual(true, Buffer.isBuffer(reply[0]['key 1'])); - assert.strictEqual(true, Buffer.isBuffer(reply[0]['key 2'])); - assert.strictEqual('', reply[0]['key 1'].inspect()); - assert.strictEqual('', reply[0]['key 2'].inspect()); - return done(err); - }); - }); - }); - }); -}); diff --git a/test/errors.js b/test/errors.js deleted file mode 100644 index 060afab585a..00000000000 --- a/test/errors.js +++ /dev/null @@ -1,6 +0,0 @@ -'use strict'; - -module.exports = { - invalidPassword: /^(ERR invalid password|WRONGPASS invalid username-password pair)/, - subscribeUnsubscribeOnly: /^ERR( Can't execute 'get':)? only \(P\)SUBSCRIBE \/ \(P\)UNSUBSCRIBE/ -}; diff --git a/test/good_traces.spec.js b/test/good_traces.spec.js deleted file mode 100644 index d8759b0f9a1..00000000000 --- a/test/good_traces.spec.js +++ /dev/null @@ -1,59 +0,0 @@ -'use strict'; - -var assert = require('assert'); -var config = require('./lib/config'); -var fork = require('child_process').fork; -var redis = config.redis; - -describe('stack traces', function () { - - it('should return good traces with NODE_ENV=development set', function (done) { - var external = fork('./test/lib/good-traces.js', { - env: { - NODE_ENV: 'development' - } - }); - - var id = setTimeout(function () { - external.kill(); - done(new Error('Timeout')); - }, 6000); - - external.on('close', function (code) { - clearTimeout(id); - assert.strictEqual(code, 0); - done(); - }); - }); - - it('should return good traces with NODE_DEBUG=redis env set', function (done) { - var external = fork('./test/lib/good-traces.js', { - env: { - NODE_DEBUG: 'redis' - }, - silent: true - }); - - var id = setTimeout(function () { - external.kill(); - done(new Error('Timeout')); - }, 6000); - - external.on('close', function (code) { - clearTimeout(id); - assert.strictEqual(code, 0); - done(); - }); - }); - - // This is always going to return good stack traces - it('should always return good stack traces for rejected offline commands', function (done) { - var client = redis.createClient({ - enable_offline_queue: false - }); - client.set('foo', function (err, res) { - assert(/good_traces.spec.js/.test(err.stack)); - client.quit(done); - }); - }); -}); diff --git a/test/helper.js b/test/helper.js deleted file mode 100644 index 3e1437d14ab..00000000000 --- a/test/helper.js +++ /dev/null @@ -1,220 +0,0 @@ -'use strict'; - -var assert = require('assert'); -var path = require('path'); -var config = require('./lib/config'); -var RedisProcess = require('./lib/redis-process'); -var StunnelProcess = require('./lib/stunnel-process'); -var rp; -var stunnel_process; - -function startRedis (conf, done, port) { - RedisProcess.start(function (err, _rp) { - rp = _rp; - return done(err); - }, path.resolve(__dirname, conf), port); -} - -// don't start redis every time we -// include this helper file! -if (!process.env.REDIS_TESTS_STARTED) { - process.env.REDIS_TESTS_STARTED = true; - - before(function (done) { - startRedis('./conf/redis.conf', done); - }); - - after(function (done) { - if (rp) rp.stop(done); - }); -} - -function arrayHelper (results) { - if (results instanceof Array) { - assert.strictEqual(results.length, 1, 'The array length may only be one element'); - return results[0]; - } - return results; -} - -module.exports = { - redisProcess: function () { - return rp; - }, - stopRedis: function (done) { - rp.stop(done); - }, - startRedis: startRedis, - stopStunnel: function (done) { - if (stunnel_process) { - StunnelProcess.stop(stunnel_process, done); - } else { - done(); - } - }, - startStunnel: function (done) { - StunnelProcess.start(function (err, _stunnel_process) { - stunnel_process = _stunnel_process; - return done(err); - }, path.resolve(__dirname, './conf')); - }, - isNumber: function (expected, done) { - return function (err, results) { - assert.strictEqual(err, null, 'expected ' + expected + ', got error: ' + err); - results = arrayHelper(results); - assert.strictEqual(results, expected, expected + ' !== ' + results); - assert.strictEqual(typeof results, 'number', 'expected a number, got ' + typeof results); - if (done) done(); - }; - }, - isString: function (str, done) { - str = '' + str; // Make sure it's a string - return function (err, results) { - assert.strictEqual(err, null, "expected string '" + str + "', got error: " + err); - results = arrayHelper(results); - if (Buffer.isBuffer(results)) { // If options are passed to return either strings or buffers... - results = results.toString(); - } - assert.strictEqual(results, str, str + ' does not match ' + results); - if (done) done(); - }; - }, - isNull: function (done) { - return function (err, results) { - assert.strictEqual(err, null, 'expected null, got error: ' + err); - results = arrayHelper(results); - assert.strictEqual(results, null, results + ' is not null'); - if (done) done(); - }; - }, - isUndefined: function (done) { - return function (err, results) { - assert.strictEqual(err, null, 'expected null, got error: ' + err); - results = arrayHelper(results); - assert.strictEqual(results, undefined, results + ' is not undefined'); - if (done) done(); - }; - }, - isError: function (done) { - return function (err, results) { - assert(err instanceof Error, "err is not instance of 'Error', but an error is expected here."); - if (done) done(); - }; - }, - isNotError: function (done) { - return function (err, results) { - assert.strictEqual(err, null, 'expected success, got an error: ' + err); - if (done) done(); - }; - }, - isType: { - number: function (done) { - return function (err, results) { - assert.strictEqual(err, null, 'expected any number, got error: ' + err); - assert.strictEqual(typeof results, 'number', results + ' is not a number'); - if (done) done(); - }; - }, - string: function (done) { - return function (err, results) { - assert.strictEqual(err, null, 'expected any string, got error: ' + err); - assert.strictEqual(typeof results, 'string', results + ' is not a string'); - if (done) done(); - }; - }, - positiveNumber: function (done) { - return function (err, results) { - assert.strictEqual(err, null, 'expected positive number, got error: ' + err); - assert(results > 0, results + ' is not a positive number'); - if (done) done(); - }; - } - }, - match: function (pattern, done) { - return function (err, results) { - assert.strictEqual(err, null, 'expected ' + pattern.toString() + ', got error: ' + err); - results = arrayHelper(results); - assert(pattern.test(results), "expected string '" + results + "' to match " + pattern.toString()); - if (done) done(); - }; - }, - serverVersionAtLeast: function (connection, desired_version) { - // Wait until a connection has established (otherwise a timeout is going to be triggered at some point) - if (Object.keys(connection.server_info).length === 0) { - throw new Error('Version check not possible as the client is not yet ready or did not expose the version'); - } - // Return true if the server version >= desired_version - var version = connection.server_info.versions; - for (var i = 0; i < 3; i++) { - if (version[i] > desired_version[i]) { - return true; - } - if (version[i] < desired_version[i]) { - if (this.skip) this.skip(); - return false; - } - } - return true; - }, - allTests: function (opts, cb) { - if (!cb) { - cb = opts; - opts = {}; - } - var protocols = ['IPv4']; - if (process.platform !== 'win32') { - protocols.push('IPv6', '/tmp/redis.sock'); - } - var options = [{ - detect_buffers: true - }, { - detect_buffers: false - }]; - options.forEach(function (options) { - var strOptions = ''; - var key; - for (key in options) { - if (options.hasOwnProperty(key)) { - strOptions += key + ': ' + options[key] + '; '; - } - } - describe('using options: ' + strOptions, function () { - protocols.forEach(function (ip, i) { - if (i !== 0 && !opts.allConnections) { - return; - } - cb(ip, config.configureClient(ip, options)); - }); - }); - }); - }, - removeMochaListener: function () { - var mochaListener = process.listeners('uncaughtException').pop(); - process.removeListener('uncaughtException', mochaListener); - return mochaListener; - }, - callFuncAfter: function (func, max) { - var i = 0; - return function (err) { - if (err) { - throw err; - } - i++; - if (i >= max) { - func(); - return true; - } - return false; - }; - }, - killConnection: function (client) { - // Change the connection option to a non existing one and destroy the stream - client.connection_options = { - port: 65535, - host: '127.0.0.1', - family: 4 - }; - client.address = '127.0.0.1:65535'; - client.stream.destroy(); - } -}; diff --git a/test/lib/config.js b/test/lib/config.js deleted file mode 100644 index d363f83e04b..00000000000 --- a/test/lib/config.js +++ /dev/null @@ -1,37 +0,0 @@ -'use strict'; - -// helpers for configuring a redis client in -// its various modes, ipV6, ipV4, socket. -var redis = require('../../index'); -var bluebird = require('bluebird'); - -// Promisify everything -bluebird.promisifyAll(redis.RedisClient.prototype); -bluebird.promisifyAll(redis.Multi.prototype); - -var config = { - redis: redis, - PORT: 6379, - HOST: { - IPv4: '127.0.0.1', - IPv6: '::1' - }, - configureClient: function (ip, opts) { - var args = []; - // Do not manipulate the opts => copy them each time - opts = opts ? JSON.parse(JSON.stringify(opts)) : {}; - - if (ip.match(/\.sock/)) { - args.push(ip); - } else { - args.push(config.PORT); - args.push(config.HOST[ip]); - opts.family = ip; - } - args.push(opts); - - return args; - } -}; - -module.exports = config; diff --git a/test/lib/good-traces.js b/test/lib/good-traces.js deleted file mode 100644 index c583da2ae27..00000000000 --- a/test/lib/good-traces.js +++ /dev/null @@ -1,20 +0,0 @@ -// Spawned by the good_stacks.spec.js tests -'use strict'; - -var assert = require('assert'); -var redis = require('../../index'); -var client = redis.createClient(); - -// Both error cases would normally return bad stack traces -client.set('foo', function (err, res) { - assert(/good-traces.js:9:8/.test(err.stack)); - client.set('foo', 'bar', function (err, res) { - assert(/good-traces.js:11:12/.test(err.stack)); - client.quit(function () { - process.exit(0); - }); - }); - process.nextTick(function () { - client.stream.destroy(); - }); -}); diff --git a/test/lib/redis-process.js b/test/lib/redis-process.js deleted file mode 100644 index 23ff2e18153..00000000000 --- a/test/lib/redis-process.js +++ /dev/null @@ -1,100 +0,0 @@ -'use strict'; - -// helper to start and stop the redis process. -var config = require('./config'); -var fs = require('fs'); -var path = require('path'); -var spawn = require('cross-spawn'); -var tcpPortUsed = require('tcp-port-used'); -var bluebird = require('bluebird'); - -// wait for redis to be listening in -// all three modes (ipv4, ipv6, socket). -function waitForRedis (available, cb, port) { - if (process.platform === 'win32') return cb(); - - var time = Date.now(); - var running = false; - var socket = '/tmp/redis.sock'; - if (port) { - // We have to distinguish the redis sockets if we have more than a single redis instance running - socket = '/tmp/redis' + port + '.sock'; - } - port = port || config.PORT; - var id = setInterval(function () { - if (running) return; - running = true; - bluebird.join( - tcpPortUsed.check(port, '127.0.0.1'), - tcpPortUsed.check(port, '::1'), - function (ipV4, ipV6) { - if (ipV6 === available && ipV4 === available) { - if (fs.existsSync(socket) === available) { - clearInterval(id); - return cb(); - } - // The same message applies for can't stop but we ignore that case - throw new Error('Port ' + port + ' is already in use. Tests can\'t start.\n'); - } - if (Date.now() - time > 6000) { - throw new Error('Redis could not start on port ' + (port || config.PORT) + '\n'); - } - running = false; - } - ).catch(function (err) { - console.error('\x1b[31m' + err.stack + '\x1b[0m\n'); - process.exit(1); - }); - }, 100); -} - -module.exports = { - start: function (done, conf, port) { - var spawnFailed = false; - if (process.platform === 'win32') return done(null, { - spawnFailed: function () { - return spawnFailed; - }, - stop: function (done) { - return done(); - } - }); - // spawn redis with our testing configuration. - var confFile = conf || path.resolve(__dirname, '../conf/redis.conf'); - var rp = spawn('redis-server', [confFile], {}); - - // capture a failure booting redis, and give - // the user running the test some directions. - rp.once('exit', function (code) { - if (code !== 0) { - spawnFailed = true; - throw new Error('TESTS: Redis Spawn Failed'); - } - }); - - // wait for redis to become available, by - // checking the port we bind on. - waitForRedis(true, function () { - // return an object that can be used in - // an after() block to shutdown redis. - return done(null, { - spawnFailed: function () { - return spawnFailed; - }, - stop: function (done) { - if (spawnFailed) return done(); - rp.once('exit', function (code) { - var error = null; - if (code !== null && code !== 0) { - error = new Error('Redis shutdown failed with code ' + code); - } - waitForRedis(false, function () { - return done(error); - }, port); - }); - rp.kill('SIGTERM'); - } - }); - }, port); - } -}; diff --git a/test/lib/stunnel-process.js b/test/lib/stunnel-process.js deleted file mode 100644 index d773f4f1ded..00000000000 --- a/test/lib/stunnel-process.js +++ /dev/null @@ -1,88 +0,0 @@ -'use strict'; - -// helper to start and stop the stunnel process. -var spawn = require('child_process').spawn; -var EventEmitter = require('events'); -var fs = require('fs'); -var path = require('path'); -var util = require('util'); - -// Newer Node.js versions > 0.10 return the EventEmitter right away and using .EventEmitter was deprecated -if (typeof EventEmitter !== 'function') { - EventEmitter = EventEmitter.EventEmitter; -} - -function once (cb) { - var called = false; - return function () { - if (called) return; - called = true; - cb.apply(this, arguments); - }; -} - -function StunnelProcess (conf_dir) { - EventEmitter.call(this); - - // set up an stunnel to redis; edit the conf file to include required absolute paths - var conf_file = path.resolve(conf_dir, 'stunnel.conf'); - var conf_text = fs.readFileSync(conf_file + '.template').toString().replace(/__dirname/g, conf_dir); - - fs.writeFileSync(conf_file, conf_text); - var stunnel = this.stunnel = spawn('stunnel', [conf_file]); - - // handle child process events, and failure to set up tunnel - var self = this; - this.timer = setTimeout(function () { - self.emit('error', new Error('Timeout waiting for stunnel to start')); - }, 8000); - - stunnel.on('error', function (err) { - self.clear(); - self.emit('error', err); - }); - - stunnel.on('exit', function (code) { - self.clear(); - if (code === 0) { - self.emit('stopped'); - } else { - self.emit('error', new Error('Stunnel exited unexpectedly; code = ' + code)); - } - }); - - // wait to stunnel to start - stunnel.stderr.on('data', function (data) { - if (data.toString().match(/Service.+redis.+bound/)) { - clearTimeout(this.timer); - self.emit('started'); - } - }); -} -util.inherits(StunnelProcess, EventEmitter); - -StunnelProcess.prototype.clear = function () { - this.stunnel = null; - clearTimeout(this.timer); -}; - -StunnelProcess.prototype.stop = function (done) { - if (this.stunnel) { - this.stunnel.kill(); - } -}; - -module.exports = { - start: function (done, conf_dir) { - done = once(done); - var stunnel = new StunnelProcess(conf_dir); - stunnel.once('error', done.bind(done)); - stunnel.once('started', done.bind(done, null, stunnel)); - }, - stop: function (stunnel, done) { - stunnel.removeAllListeners(); - stunnel.stop(); - stunnel.once('error', done.bind(done)); - stunnel.once('stopped', done.bind(done, null)); - } -}; diff --git a/test/lib/unref.js b/test/lib/unref.js deleted file mode 100644 index 5ad176238e8..00000000000 --- a/test/lib/unref.js +++ /dev/null @@ -1,17 +0,0 @@ -// spawned by the unref tests in node_redis.spec.js. -// when configured, unref causes the client to exit -// as soon as there are no outstanding commands. -'use strict'; - -var redis = require('../../index'); -var HOST = process.argv[2] || '127.0.0.1'; -var PORT = process.argv[3]; -var args = PORT ? [PORT, HOST] : [HOST]; - -var c = redis.createClient.apply(redis, args); -c.info(function (err, reply) { - if (err) process.exit(-1); - if (!reply.length) process.exit(-1); - process.stdout.write(reply.length.toString()); -}); -c.unref(); diff --git a/test/multi.spec.js b/test/multi.spec.js deleted file mode 100644 index 5b0e801c875..00000000000 --- a/test/multi.spec.js +++ /dev/null @@ -1,737 +0,0 @@ -'use strict'; - -var assert = require('assert'); -var config = require('./lib/config'); -var helper = require('./helper'); -var utils = require('../lib/utils'); -var redis = config.redis; -var zlib = require('zlib'); -var client; - -describe("The 'multi' method", function () { - - afterEach(function () { - client.end(true); - }); - - describe('regression test', function () { - it('saved buffers with charsets different than utf-8 (issue #913)', function (done) { - this.timeout(12000); // Windows tests on 0.10 are slow - client = redis.createClient(); - - var end = helper.callFuncAfter(done, 100); - - // Some random object created from http://beta.json-generator.com/ - var test_obj = { - '_id': '5642c4c33d4667c4a1fefd99', 'index': 0, 'guid': '5baf1f1c-7621-41e7-ae7a-f8c6f3199b0f', 'isActive': true, - 'balance': '$1,028.63', 'picture': 'http://placehold.it/32x32', 'age': 31, 'eyeColor': 'green', 'name': {'first': 'Shana', 'last': 'Long'}, - 'company': 'MANGLO', 'email': 'shana.long@manglo.us', 'phone': '+1 (926) 405-3105', 'address': '747 Dank Court, Norfolk, Ohio, 1112', - 'about': 'Eu pariatur in nisi occaecat enim qui consequat nostrud cupidatat id. ' + - 'Commodo commodo dolore esse irure minim quis deserunt anim laborum aute deserunt et est. Quis nisi laborum deserunt nisi quis.', - 'registered': 'Friday, April 18, 2014 9:56 AM', 'latitude': '74.566613', 'longitude': '-11.660432', 'tags': [7, 'excepteur'], - 'range': [0, 1, 2, 3, 4, 5, 6, 7, 8, 9], 'friends': [3, {'id': 1, 'name': 'Schultz Dyer'}], - 'greeting': 'Hello, Shana! You have 5 unread messages.', 'favoriteFruit': 'strawberry' - }; - - function run () { - if (end() === true) { - return; - } - // To demonstrate a big payload for hash set field values, let's create a big array - var test_arr = []; - var i = 0; - for (; i < 80; i++) { - var new_obj = JSON.parse(JSON.stringify(test_obj)); - test_arr.push(new_obj); - } - - var json = JSON.stringify(test_arr); - zlib.deflate(Buffer.from(json), function (err, buffer) { - if (err) { - done(err); - return; - } - - var multi = client.multi(); - multi.del('SOME_KEY'); - - for (i = 0; i < 100; i++) { - multi.hset('SOME_KEY', 'SOME_FIELD' + i, buffer); - } - multi.exec(function (err, res) { - if (err) { - done(err); - return; - } - run(); - }); - }); - } - run(); - }); - }); - - describe('pipeline limit', function () { - - it('do not exceed maximum string size', function (done) { - this.timeout(process.platform !== 'win32' ? 10000 : 35000); // Windows tests are horribly slow - // Triggers a RangeError: Invalid string length if not handled properly - client = redis.createClient(); - var multi = client.multi(); - var i = Math.pow(2, 28); - while (i > 0) { - i -= 10230; - multi.set('foo' + i, 'bar' + new Array(1024).join('1234567890')); - } - client.on('ready', function () { - multi.exec(function (err, res) { - assert.strictEqual(res.length, 26241); - }); - client.flushdb(done); - }); - }); - - }); - - helper.allTests(function (ip, args) { - - describe('using ' + ip, function () { - - describe('when not connected', function () { - - beforeEach(function (done) { - var end = helper.callFuncAfter(done, 2); - client = redis.createClient.apply(null, args); - client.once('ready', function () { - client.quit(end); - }); - client.once('end', end); - }); - - it('reports an error', function (done) { - var multi = client.multi(); - var notBuffering = multi.exec(function (err, res) { - assert(err.message.match(/The connection is already closed/)); - done(); - }); - assert.strictEqual(notBuffering, false); - }); - - it('reports an error if promisified', function () { - return client.multi().execAsync().catch(function (err) { - assert(err.message.match(/The connection is already closed/)); - }); - }); - }); - - describe('when connected', function () { - - beforeEach(function () { - client = redis.createClient.apply(null, args); - }); - - describe('monitor and transactions do not work together', function () { - - it('results in a execabort', function (done) { - // Check that transactions in combination with monitor result in an error - client.monitor(function (e) { - client.on('error', function (err) { - assert.strictEqual(err.code, 'EXECABORT'); - client.end(false); - done(); - }); - var multi = client.multi(); - multi.set('hello', 'world'); - multi.exec(); - }); - }); - - it('results in a execabort #2', function (done) { - // Check that using monitor with a transactions results in an error - client.multi().set('foo', 'bar').monitor().exec(function (err, res) { - assert.strictEqual(err.code, 'EXECABORT'); - client.end(false); - done(); - }); - }); - - it('sanity check', function (done) { - // Remove the listener and add it back again after the error - var mochaListener = helper.removeMochaListener(); - process.on('uncaughtException', function (err) { - helper.removeMochaListener(); - process.on('uncaughtException', mochaListener); - done(); - }); - // Check if Redis still has the error - client.monitor(); - client.send_command('multi'); - client.send_command('set', ['foo', 'bar']); - client.send_command('get', ['foo']); - client.send_command('exec', function (err, res) { - // res[0] is going to be the monitor result of set - // res[1] is going to be the result of the set command - assert(utils.monitor_regex.test(res[0])); - assert.strictEqual(res[1], 'OK'); - assert.strictEqual(res.length, 2); - client.end(false); - }); - }); - }); - - it('executes a pipelined multi properly in combination with the offline queue', function (done) { - var multi1 = client.multi(); - multi1.set('m1', '123'); - multi1.get('m1'); - multi1.exec(done); - assert.strictEqual(client.offline_queue.length, 4); - }); - - it('executes a pipelined multi properly after a reconnect in combination with the offline queue', function (done) { - client.once('ready', function () { - client.stream.destroy(); - var called = false; - var multi1 = client.multi(); - multi1.set('m1', '123'); - multi1.get('m1'); - multi1.exec(function (err, res) { - assert(!err); - called = true; - }); - client.once('ready', function () { - var multi1 = client.multi(); - multi1.set('m2', '456'); - multi1.get('m2'); - multi1.exec(function (err, res) { - assert(called); - assert(!err); - assert.strictEqual(res[1], '456'); - done(); - }); - }); - }); - }); - }); - - describe('when connection is broken', function () { - - it('return an error even if connection is in broken mode if callback is present', function (done) { - client = redis.createClient({ - host: 'somewhere', - port: 6379, - retry_strategy: function (options) { - if (options.attempt > 1) { - // End reconnecting with built in error - return undefined; - } - } - }); - - client.on('error', function (err) { - if (/Redis connection to somewhere:6379 failed/.test(err.origin.message)) { - done(); - } - }); - - client.multi([['set', 'foo', 'bar'], ['get', 'foo']]).exec(function (err, res) { - assert(/Redis connection in broken state: retry aborted/.test(err.message)); - assert.strictEqual(err.errors.length, 2); - assert.strictEqual(err.errors[0].args.length, 2); - }); - }); - - it('does not emit an error twice if connection is in broken mode with no callback', function (done) { - client = redis.createClient({ - host: 'somewhere', - port: 6379, - retry_strategy: function (options) { - if (options.attempt > 1) { - // End reconnecting with built in error - return undefined; - } - } - }); - - client.on('error', function (err) { - // Results in multiple done calls if test fails - if (/Redis connection to somewhere:6379 failed/.test(err.origin.message)) { - done(); - } - }); - - client.multi([['set', 'foo', 'bar'], ['get', 'foo']]).exec(); - }); - }); - - describe('when ready', function () { - - beforeEach(function (done) { - client = redis.createClient.apply(null, args); - client.once('ready', function () { - client.flushdb(function (err) { - return done(err); - }); - }); - }); - - it('returns an empty result array', function (done) { - var multi = client.multi(); - var notBuffering = multi.exec(function (err, res) { - assert.strictEqual(err, null); - assert.strictEqual(res.length, 0); - done(); - }); - assert.strictEqual(notBuffering, true); - }); - - it('runs normal calls in-between multis', function (done) { - var multi1 = client.multi(); - multi1.set('m1', '123'); - client.set('m2', '456', done); - }); - - it('runs simultaneous multis with the same client', function (done) { - var end = helper.callFuncAfter(done, 2); - - var multi1 = client.multi(); - multi1.set('m1', '123'); - multi1.get('m1'); - - var multi2 = client.multi(); - multi2.set('m2', '456'); - multi2.get('m2'); - - multi1.exec(end); - multi2.exec(function (err, res) { - assert.strictEqual(res[1], '456'); - end(); - }); - }); - - it('runs simultaneous multis with the same client version 2', function (done) { - var end = helper.callFuncAfter(done, 2); - var multi2 = client.multi(); - var multi1 = client.multi(); - - multi2.set('m2', '456'); - multi1.set('m1', '123'); - multi1.get('m1'); - multi2.get('m1'); - multi2.ping(); - - multi1.exec(end); - multi2.exec(function (err, res) { - assert.strictEqual(res[1], '123'); - end(); - }); - }); - - it('roles back a transaction when one command in a sequence of commands fails', function (done) { - var multi1, multi2; - // Provoke an error at queue time - multi1 = client.MULTI(); - multi1.mset('multifoo', '10', 'multibar', '20', helper.isString('OK')); - - multi1.set('foo2', helper.isError()); - multi1.incr('multifoo'); - multi1.incr('multibar'); - multi1.exec(function () { - // Redis 2.6.5+ will abort transactions with errors - // see: http://redis.io/topics/transactions - var multibar_expected = 1; - var multifoo_expected = 1; - // Confirm that the previous command, while containing an error, still worked. - multi2 = client.multi(); - multi2.incr('multibar', helper.isNumber(multibar_expected)); - multi2.incr('multifoo', helper.isNumber(multifoo_expected)); - multi2.exec(function (err, replies) { - assert.strictEqual(multibar_expected, replies[0]); - assert.strictEqual(multifoo_expected, replies[1]); - return done(); - }); - }); - }); - - it('roles back a transaction when one command in an array of commands fails', function (done) { - // test nested multi-bulk replies - client.multi([ - ['mget', 'multifoo', 'multibar', function (err, res) { - assert.strictEqual(2, res.length); - assert.strictEqual(0, +res[0]); - assert.strictEqual(0, +res[1]); - }], - ['set', 'foo2', helper.isError()], - ['incr', 'multifoo'], - ['incr', 'multibar'] - ]).exec(function (err, replies) { - assert.notEqual(err, null); - assert.equal(replies, undefined); - return done(); - }); - }); - - it('handles multiple operations being applied to a set', function (done) { - client.sadd('some set', 'mem 1'); - client.sadd(['some set', 'mem 2']); - client.sadd('some set', 'mem 3'); - client.sadd('some set', 'mem 4'); - - // make sure empty mb reply works - client.del('some missing set'); - client.smembers('some missing set', function (err, reply) { - // make sure empty mb reply works - assert.strictEqual(0, reply.length); - }); - - // test nested multi-bulk replies with empty mb elements. - client.multi([ - ['smembers', ['some set']], - ['del', 'some set'], - ['smembers', 'some set'] - ]) - .scard('some set') - .exec(function (err, replies) { - assert.strictEqual(4, replies[0].length); - assert.strictEqual(0, replies[2].length); - return done(); - }); - }); - - it('allows multiple operations to be performed using constructor with all kinds of syntax', function (done) { - var now = Date.now(); - var arr = ['multihmset', 'multibar', 'multibaz']; - var arr2 = ['some manner of key', 'otherTypes']; - var arr3 = [5768, 'multibarx', 'multifoox']; - var arr4 = ['mset', [578, 'multibar'], helper.isString('OK')]; - var called = false; - client.multi([ - arr4, - [['mset', 'multifoo2', 'multibar2', 'multifoo3', 'multibar3'], helper.isString('OK')], - ['hmset', arr], - [['hmset', 'multihmset2', 'multibar2', 'multifoo3', 'multibar3', 'test'], helper.isString('OK')], - ['hmset', ['multihmset', 'multibar', 'multifoo'], helper.isString('OK')], - ['hmset', arr3, helper.isString('OK')], - ['hmset', now, {123456789: 'abcdefghij', 'some manner of key': 'a type of value', 'otherTypes': 555}], - ['hmset', 'key2', {'0123456789': 'abcdefghij', 'some manner of key': 'a type of value', 'otherTypes': 999}, helper.isString('OK')], - ['HMSET', 'multihmset', ['multibar', 'multibaz'], undefined], // undefined is used as a explicit not set callback variable - ['hmset', 'multihmset', ['multibar', 'multibaz'], helper.isString('OK')], - ]) - .hmget(now, 123456789, 'otherTypes') - .hmget('key2', arr2, function noop () {}) - .hmget(['multihmset2', 'some manner of key', 'multibar3']) - .mget('multifoo2', ['multifoo3', 'multifoo'], function (err, res) { - assert(res[0], 'multifoo3'); - assert(res[1], 'multifoo'); - called = true; - }) - .exec(function (err, replies) { - assert(called); - assert.equal(arr.length, 3); - assert.equal(arr2.length, 2); - assert.equal(arr3.length, 3); - assert.equal(arr4.length, 3); - assert.strictEqual(null, err); - assert.equal(replies[10][1], '555'); - assert.equal(replies[11][0], 'a type of value'); - assert.strictEqual(replies[12][0], null); - assert.equal(replies[12][1], 'test'); - assert.equal(replies[13][0], 'multibar2'); - assert.equal(replies[13].length, 3); - assert.equal(replies.length, 14); - return done(); - }); - }); - - it('converts a non string key to a string', function (done) { - // TODO: Converting the key might change soon again. - client.multi().hmset(true, { - test: 123, - bar: 'baz' - }).exec(done); - }); - - it('runs a multi without any further commands', function (done) { - var buffering = client.multi().exec(function (err, res) { - assert.strictEqual(err, null); - assert.strictEqual(res.length, 0); - done(); - }); - assert(typeof buffering === 'boolean'); - }); - - it('allows multiple operations to be performed using a chaining API', function (done) { - client.multi() - .mset('some', '10', 'keys', '20') - .incr('some') - .incr('keys') - .mget('some', ['keys']) - .exec(function (err, replies) { - assert.strictEqual(null, err); - assert.equal('OK', replies[0]); - assert.equal(11, replies[1]); - assert.equal(21, replies[2]); - assert.equal(11, replies[3][0].toString()); - assert.equal(21, replies[3][1].toString()); - return done(); - }); - }); - - it('allows multiple commands to work the same as normal to be performed using a chaining API', function (done) { - client.multi() - .mset(['some', '10', 'keys', '20']) - .incr('some', helper.isNumber(11)) - .incr(['keys'], helper.isNumber(21)) - .mget('some', 'keys') - .exec(function (err, replies) { - assert.strictEqual(null, err); - assert.equal('OK', replies[0]); - assert.equal(11, replies[1]); - assert.equal(21, replies[2]); - assert.equal(11, replies[3][0].toString()); - assert.equal(21, replies[3][1].toString()); - return done(); - }); - }); - - it('allows multiple commands to work the same as normal to be performed using a chaining API promisified', function () { - return client.multi() - .mset(['some', '10', 'keys', '20']) - .incr('some', helper.isNumber(11)) - .incr(['keys'], helper.isNumber(21)) - .mget('some', 'keys') - .execAsync() - .then(function (replies) { - assert.equal('OK', replies[0]); - assert.equal(11, replies[1]); - assert.equal(21, replies[2]); - assert.equal(11, replies[3][0].toString()); - assert.equal(21, replies[3][1].toString()); - }); - }); - - it('allows an array to be provided indicating multiple operations to perform', function (done) { - // test nested multi-bulk replies with nulls. - client.multi([ - ['mget', ['multifoo', 'some', 'random value', 'keys']], - ['incr', 'multifoo'] - ]) - .exec(function (err, replies) { - assert.strictEqual(replies.length, 2); - assert.strictEqual(replies[0].length, 4); - return done(); - }); - }); - - it('allows multiple operations to be performed on a hash', function (done) { - client.multi() - .hmset('multihash', 'a', 'foo', 'b', 1) - .hmset('multihash', { - extra: 'fancy', - things: 'here' - }) - .hgetall('multihash') - .exec(function (err, replies) { - assert.strictEqual(null, err); - assert.equal('OK', replies[0]); - assert.equal(Object.keys(replies[2]).length, 4); - assert.equal('foo', replies[2].a); - assert.equal('1', replies[2].b); - assert.equal('fancy', replies[2].extra); - assert.equal('here', replies[2].things); - return done(); - }); - }); - - it('reports EXECABORT exceptions when they occur (while queueing)', function (done) { - client.multi().config('bar').set('foo').set('bar').exec(function (err, reply) { - assert.equal(err.code, 'EXECABORT'); - assert.equal(reply, undefined, 'The reply should have been discarded'); - assert(err.message.match(/^EXECABORT/), 'Error message should begin with EXECABORT'); - assert.equal(err.errors.length, 2, 'err.errors should have 2 items'); - assert.strictEqual(err.errors[0].command, 'SET'); - assert.strictEqual(err.errors[0].code, 'ERR'); - assert.strictEqual(err.errors[0].position, 1); - assert(/^ERR/.test(err.errors[0].message), 'Actuall error message should begin with ERR'); - return done(); - }); - }); - - it('reports multiple exceptions when they occur (while EXEC is running)', function (done) { - client.multi().config('bar').debug('foo').eval("return {err='this is an error'}", 0).exec(function (err, reply) { - assert.strictEqual(reply.length, 3); - assert.equal(reply[0].code, 'ERR'); - assert.equal(reply[0].command, 'CONFIG'); - assert.equal(reply[2].code, undefined); - assert.equal(reply[2].command, 'EVAL'); - assert(/^this is an error/.test(reply[2].message)); - assert(/^ERR/.test(reply[0].message), 'Error message should begin with ERR'); - assert(/^ERR/.test(reply[1].message), 'Error message should begin with ERR'); - return done(); - }); - }); - - it('reports multiple exceptions when they occur (while EXEC is running) promisified', function () { - return client.multi().config('bar').debug('foo').eval("return {err='this is an error'}", 0).execAsync().then(function (reply) { - assert.strictEqual(reply.length, 3); - assert.equal(reply[0].code, 'ERR'); - assert.equal(reply[0].command, 'CONFIG'); - assert.equal(reply[2].code, undefined); - assert.equal(reply[2].command, 'EVAL'); - assert(/^this is an error/.test(reply[2].message)); - assert(/^ERR/.test(reply[0].message), 'Error message should begin with ERR'); - assert(/^ERR/.test(reply[1].message), 'Error message should begin with ERR'); - }); - }); - - it('reports multiple exceptions when they occur (while EXEC is running) and calls cb', function (done) { - var multi = client.multi(); - multi.config('bar', helper.isError()); - multi.set('foo', 'bar', helper.isString('OK')); - multi.debug('foo').exec(function (err, reply) { - assert.strictEqual(reply.length, 3); - assert.strictEqual(reply[0].code, 'ERR'); - assert(/^ERR/.test(reply[0].message), 'Error message should begin with ERR'); - assert(/^ERR/.test(reply[2].message), 'Error message should begin with ERR'); - assert.strictEqual(reply[1], 'OK'); - client.get('foo', helper.isString('bar', done)); - }); - }); - - it('emits an error if no callback has been provided and execabort error occured', function (done) { - var multi = client.multi(); - multi.config('bar'); - multi.set('foo'); - multi.exec(); - - client.on('error', function (err) { - assert.equal(err.code, 'EXECABORT'); - done(); - }); - }); - - it('should work without any callback', function (done) { - var multi = client.multi(); - multi.set('baz', 'binary'); - multi.set('foo', 'bar'); - multi.exec(); - - client.get('foo', helper.isString('bar', done)); - }); - - it('should not use a transaction with exec_atomic if no command is used', function () { - var multi = client.multi(); - var test = false; - multi.exec_batch = function () { - test = true; - }; - multi.exec_atomic(); - assert(test); - }); - - it('should not use a transaction with exec_atomic if only one command is used', function () { - var multi = client.multi(); - var test = false; - multi.exec_batch = function () { - test = true; - }; - multi.set('baz', 'binary'); - multi.EXEC_ATOMIC(); - assert(test); - }); - - it('should use transaction with exec_atomic and more than one command used', function (done) { - var multi = client.multi(); - var test = false; - multi.exec_batch = function () { - test = true; - }; - multi.set('baz', 'binary'); - multi.get('baz'); - multi.exec_atomic(done); - assert(!test); - }); - - it('do not mutate arguments in the multi constructor', function (done) { - var input = [['set', 'foo', 'bar'], ['get', 'foo']]; - client.multi(input).exec(function (err, res) { - assert.strictEqual(input.length, 2); - assert.strictEqual(input[0].length, 3); - assert.strictEqual(input[1].length, 2); - done(); - }); - }); - - it('works properly after a reconnect. issue #897', function (done) { - client.stream.destroy(); - client.on('error', function (err) { - assert.strictEqual(err.code, 'ECONNREFUSED'); - }); - client.on('ready', function () { - client.multi([['set', 'foo', 'bar'], ['get', 'foo']]).exec(function (err, res) { - assert(!err); - assert.strictEqual(res[1], 'bar'); - done(); - }); - }); - }); - - it('emits error once if reconnecting after multi has been executed but not yet returned without callback', function (done) { - // NOTE: If uncork is called async by postponing it to the next tick, this behavior is going to change. - // The command won't be processed anymore two errors are returned instead of one - client.on('error', function (err) { - assert.strictEqual(err.code, 'UNCERTAIN_STATE'); - client.get('foo', function (err, res) { - assert.strictEqual(res, 'bar'); - done(); - }); - }); - - // The commands should still be fired, no matter that the socket is destroyed on the same tick - client.multi().set('foo', 'bar').get('foo').exec(); - // Abort connection before the value returned - client.stream.destroy(); - }); - - it('indivdual commands work properly with multi', function (done) { - // Neither of the following work properly in a transactions: - // (This is due to Redis not returning the reply as expected / resulting in undefined behavior) - // (Likely there are more commands that do not work with a transaction) - // - // auth => can't be called after a multi command - // monitor => results in faulty return values e.g. multi().monitor().set('foo', 'bar').get('foo') - // returns ['OK, 'OK', 'monitor reply'] instead of ['OK', 'OK', 'bar'] - // quit => ends the connection before the exec - // client reply skip|off => results in weird return values. Not sure what exactly happens - // subscribe => enters subscribe mode and this does not work in combination with exec (the same for psubscribe, unsubscribe...) - // - - // Make sure send_command is not called - client.send_command = function () { - throw new Error('failed'); - }; - - assert.strictEqual(client.selected_db, undefined); - var multi = client.multi(); - multi.select(5, function (err, res) { - assert.strictEqual(client.selected_db, 5); - assert.strictEqual(res, 'OK'); - assert.notDeepEqual(client.server_info.db5, { avg_ttl: 0, expires: 0, keys: 1 }); - }); - // multi.client('reply', 'on', helper.isString('OK')); // Redis v.3.2 - multi.set('foo', 'bar', helper.isString('OK')); - multi.info(function (err, res) { - assert.strictEqual(res.indexOf('# Server\r\nredis_version:'), 0); - assert.deepEqual(client.server_info.db5, { avg_ttl: 0, expires: 0, keys: 1 }); - }); - multi.get('foo', helper.isString('bar')); - multi.exec(function (err, res) { - res[2] = res[2].substr(0, 10); - assert.deepEqual(res, ['OK', 'OK', '# Server\r\n', 'bar']); - client.flushdb(done); - }); - }); - - }); - }); - }); -}); diff --git a/test/node_redis.spec.js b/test/node_redis.spec.js deleted file mode 100644 index 12ad70d5ef6..00000000000 --- a/test/node_redis.spec.js +++ /dev/null @@ -1,1043 +0,0 @@ -'use strict'; - -var assert = require('assert'); -var fs = require('fs'); -var util = require('util'); -var path = require('path'); -var intercept = require('intercept-stdout'); -var config = require('./lib/config'); -var helper = require('./helper'); -var fork = require('child_process').fork; -var redis = config.redis; -var client; - -// Currently GitHub Actions on Windows (and event travis) builds hang after completing if -// any processes are still running, we shutdown redis-server after all tests complete (can't do this in a -// `after_script` hook as it hangs before the `after` life cycles) to workaround the issue. -after(function (done) { - if (process.platform !== 'win32' || !process.env.GITHUB_ACTION) { - return done(); - } - setTimeout(function () { - require('cross-spawn').sync('redis-server', ['--service-stop'], {}); - done(); - }, 2000); -}); - -describe('The node_redis client', function () { - - describe("The 'add_command' method", function () { - - var Redis = redis.RedisClient; - - it('camel case and snakeCase version exists', function () { - assert.strictEqual(typeof redis.addCommand, 'function'); - assert.strictEqual(typeof redis.add_command, 'function'); - }); - - it('converts special characters in functions names to lowercase', function () { - var command = 'really-new.command'; - assert.strictEqual(Redis.prototype[command], undefined); - redis.addCommand(command); - if (Redis.prototype[command].name !== '') { - assert.strictEqual(Redis.prototype[command].name, 'really_new_command'); - assert.strictEqual(Redis.prototype[command.toUpperCase()].name, 'really_new_command'); - assert.strictEqual(Redis.prototype.really_new_command.name, 'really_new_command'); - assert.strictEqual(Redis.prototype.REALLY_NEW_COMMAND.name, 'really_new_command'); - } - }); - }); - - it('individual commands sanity check', function (done) { - // All commands should work the same in multi context or without - // Therefor individual commands always have to be handled in both cases - fs.readFile(path.resolve(__dirname, '../lib/individualCommands.js'), 'utf8', function (err, data) { - var client_prototype = data.match(/(\n| = )RedisClient\.prototype.[a-zA-Z_]+/g); - var multi_prototype = data.match(/(\n| = )Multi\.prototype\.[a-zA-Z_]+/g); - // Check that every entry RedisClient entry has a correspondend Multi entry - assert.strictEqual(client_prototype.filter(function (entry) { - return multi_prototype.indexOf(entry.replace('RedisClient', 'Multi')) === -1; - }).length, 4); // multi and batch are included too - assert.strictEqual(client_prototype.length, multi_prototype.length + 4); - // Check that all entries exist in uppercase and in lowercase variants - assert.strictEqual(data.match(/(\n| = )RedisClient\.prototype.[a-z_]+/g).length * 2, client_prototype.length); - done(); - }); - }); - - it('convert minus to underscore in Redis function names', function (done) { - var names = Object.keys(redis.RedisClient.prototype); - client = redis.createClient(); - for (var i = 0; i < names.length; i++) { - assert(/^([a-zA-Z_][a-zA-Z_0-9]*)?$/.test(client[names[i]].name)); - } - client.quit(done); - }); - - it('reset the parser while reconnecting (See #1190)', function (done) { - var client = redis.createClient({ - retryStrategy: function () { - return 5; - } - }); - client.once('reconnecting', function () { - process.nextTick(function () { - assert.strictEqual(client.reply_parser.buffer, null); - client.end(true); - done(); - }); - }); - var partialInput = Buffer.from('$100\r\nabcdef'); - client.reply_parser.execute(partialInput); - assert.strictEqual(client.reply_parser.buffer.inspect(), partialInput.inspect()); - client.stream.destroy(); - }); - - helper.allTests(function (ip, args) { - - describe('using ' + ip, function () { - - afterEach(function () { - client.end(true); - }); - - describe('when connected', function () { - beforeEach(function (done) { - client = redis.createClient.apply(null, args); - client.once('connect', function () { - client.flushdb(done); - }); - }); - - describe('duplicate', function () { - it('check if all options got copied properly', function (done) { - client.selected_db = 2; - var client2 = client.duplicate(); - assert.strictEqual(client.connectionId + 1, client2.connection_id); - assert.strictEqual(client2.selected_db, 2); - assert(client.connected); - assert(!client2.connected); - for (var elem in client.options) { - if (client.options.hasOwnProperty(elem)) { - assert.strictEqual(client2.options[elem], client.options[elem]); - } - } - client2.on('error', function (err) { - assert.strictEqual(err.message, 'Connection forcefully ended and command aborted. It might have been processed.'); - assert.strictEqual(err.command, 'SELECT'); - assert(err instanceof Error); - assert.strictEqual(err.name, 'AbortError'); - }); - client2.on('ready', function () { - client2.end(true); - done(); - }); - }); - - it('check if all new options replaced the old ones', function (done) { - client.selected_db = 1; - var client2 = client.duplicate({ - db: 2, - no_ready_check: true - }); - assert(client.connected); - assert(!client2.connected); - assert.notEqual(client.selected_db, client2.selected_db); - assert.strictEqual(client.options.no_ready_check, undefined); - assert.strictEqual(client2.options.no_ready_check, true); - assert.notDeepEqual(client.options, client2.options); - for (var elem in client.options) { - if (client.options.hasOwnProperty(elem)) { - if (elem !== 'no_ready_check') { - assert.strictEqual(client2.options[elem], client.options[elem]); - } - } - } - client2.on('ready', function () { - client2.end(true); - done(); - }); - }); - - it('works with a callback', function (done) { - client.duplicate(function (err, client) { - assert(!err); - assert.strictEqual(client.ready, true); - client.quit(done); - }); - }); - - it('works with a callback and errors out', function (done) { - client.duplicate({ - port: '9999' - }, function (err, client) { - assert.strictEqual(err.code, 'ECONNREFUSED'); - done(client); - }); - }); - - it('works with a promises', function () { - return client.duplicateAsync().then(function (client) { - assert.strictEqual(client.ready, true); - return client.quitAsync(); - }); - }); - - it('works with a promises and errors', function () { - return client.duplicateAsync({ - port: 9999 - }).catch(function (err) { - assert.strictEqual(err.code, 'ECONNREFUSED'); - }); - }); - }); - - describe('big data', function () { - - // Check if the fast mode for big strings is working correct - it('safe strings that are bigger than 30000 characters', function (done) { - var str = 'foo ಠ_ಠ bar '; - while (str.length < 111111) { - str += str; - } - client.set('foo', str); - client.get('foo', function (err, res) { - assert.strictEqual(res, str); - done(); - }); - }); - - it('safe strings that are bigger than 30000 characters with multi', function (done) { - var str = 'foo ಠ_ಠ bar '; - while (str.length < 111111) { - str += str; - } - var called = false; - var temp = client.write_buffers.bind(client); - assert(client.fire_strings); - client.write_buffers = function (data) { - called = true; - // To increase write performance for strings the value is converted to a buffer - assert(!client.fire_strings); - temp(data); - }; - client.multi().set('foo', str).get('foo', function (err, res) { - assert.strictEqual(res, str); - }).exec(function (err, res) { - assert(called); - assert.strictEqual(res[1], str); - done(); - }); - assert(client.fire_strings); - }); - }); - - describe('send_command', function () { - - it('omitting args should be fine', function (done) { - client.server_info = {}; - client.send_command('info'); - client.send_command('ping', function (err, res) { - assert.strictEqual(res, 'PONG'); - // Check if the previous info command used the internal individual info command - assert.notDeepEqual(client.server_info, {}); - client.server_info = {}; - }); - client.send_command('info', null, undefined); - client.send_command('ping', null, function (err, res) { - assert.strictEqual(res, 'PONG'); - // Check if the previous info command used the internal individual info command - assert.notDeepEqual(client.server_info, {}); - client.server_info = {}; - }); - client.send_command('info', undefined, undefined); - client.send_command('ping', function (err, res) { - assert.strictEqual(res, 'PONG'); - // Check if the previous info command used the internal individual info command - assert.notDeepEqual(client.server_info, {}); - client.server_info = {}; - }); - client.send_command('info', undefined, function (err, res) { - assert(/redis_version/.test(res)); - // The individual info command should also be called by using send_command - // console.log(info, client.server_info); - assert.notDeepEqual(client.server_info, {}); - done(); - }); - }); - - it('using multi with sendCommand should work as individual command instead of using the internal multi', function (done) { - // This is necessary to keep backwards compatibility and it is the only way to handle multis as you want in node_redis - client.sendCommand('multi'); - client.sendCommand('set', ['foo', 'bar'], helper.isString('QUEUED')); - client.get('foo'); - client.exec(function (err, res) { // exec is not manipulated if not fired by the individual multi command - // As the multi command is handled individually by the user he also has to handle the return value - assert.strictEqual(res[0].toString(), 'OK'); - assert.strictEqual(res[1].toString(), 'bar'); - done(); - }); - }); - - it('multi should be handled special', function (done) { - client.send_command('multi', undefined, helper.isString('OK')); - var args = ['test', 'bla']; - client.send_command('set', args, helper.isString('QUEUED')); - assert.deepEqual(args, ['test', 'bla']); // Check args manipulation - client.get('test', helper.isString('QUEUED')); - client.exec(function (err, res) { - // As the multi command is handled individually by the user he also has to handle the return value - assert.strictEqual(res[0].toString(), 'OK'); - assert.strictEqual(res[1].toString(), 'bla'); - done(); - }); - }); - - it('using another type as cb should throw', function () { - try { - client.send_command('set', ['test', 'bla'], [true]); - throw new Error('failed'); - } catch (err) { - assert.strictEqual(err.message, 'Wrong input type "Array" for callback function'); - } - try { - client.send_command('set', ['test', 'bla'], null); - throw new Error('failed'); - } catch (err) { - assert.strictEqual(err.message, 'Wrong input type "null" for callback function'); - } - }); - - it('command argument has to be of type string', function () { - try { - client.send_command(true, ['test', 'bla'], function () {}); - throw new Error('failed'); - } catch (err) { - assert.strictEqual(err.message, 'Wrong input type "Boolean" for command name'); - } - try { - client.send_command(undefined, ['test', 'bla'], function () {}); - throw new Error('failed'); - } catch (err) { - assert.strictEqual(err.message, 'Wrong input type "undefined" for command name'); - } - try { - client.send_command(null, ['test', 'bla'], function () {}); - throw new Error('failed'); - } catch (err) { - assert.strictEqual(err.message, 'Wrong input type "null" for command name'); - } - }); - - it('args may only be of type Array or undefined', function () { - try { - client.send_command('info', 123); - throw new Error('failed'); - } catch (err) { - assert.strictEqual(err.message, 'Wrong input type "Number" for args'); - } - }); - - it('passing a callback as args and as callback should throw', function () { - try { - client.send_command('info', function a () {}, function b () {}); - throw new Error('failed'); - } catch (err) { - assert.strictEqual(err.message, 'Wrong input type "Function" for args'); - } - }); - - it('multi should be handled special', function (done) { - client.send_command('multi', undefined, helper.isString('OK')); - var args = ['test', 'bla']; - client.send_command('set', args, helper.isString('QUEUED')); - assert.deepEqual(args, ['test', 'bla']); // Check args manipulation - client.get('test', helper.isString('QUEUED')); - client.exec(function (err, res) { - // As the multi command is handled individually by the user he also has to handle the return value - assert.strictEqual(res[0].toString(), 'OK'); - assert.strictEqual(res[1].toString(), 'bla'); - done(); - }); - }); - - it('the args array may contain a arbitrary number of arguments', function (done) { - client.send_command('mset', ['foo', 1, 'bar', 2, 'baz', 3], helper.isString('OK')); - client.mget(['foo', 'bar', 'baz'], function (err, res) { - // As the multi command is handled individually by the user he also has to handle the return value - assert.strictEqual(res[0].toString(), '1'); - assert.strictEqual(res[1].toString(), '2'); - assert.strictEqual(res[2].toString(), '3'); - done(); - }); - }); - - it('send_command with callback as args', function (done) { - client.send_command('abcdef', function (err, res) { - if (process.platform === 'win32') { - assert.strictEqual(err.message, "ERR unknown command 'abcdef'"); - } else { - assert.strictEqual(err.message, 'ERR unknown command `abcdef`, with args beginning with: '); - } - done(); - }); - }); - - }); - - describe('retry_unfulfilled_commands', function () { - - it('should retry all commands instead of returning an error if a command did not yet return after a connection loss', function (done) { - var bclient = redis.createClient({ - retry_unfulfilled_commands: true - }); - bclient.blpop('blocking list 2', 5, function (err, value) { - assert.strictEqual(value[0], 'blocking list 2'); - assert.strictEqual(value[1], 'initial value'); - bclient.end(true); - done(err); - }); - bclient.once('ready', function () { - setTimeout(function () { - bclient.stream.destroy(); - client.rpush('blocking list 2', 'initial value', helper.isNumber(1)); - }, 100); - }); - }); - - it('should retry all commands even if the offline queue is disabled', function (done) { - var bclient = redis.createClient({ - enableOfflineQueue: false, - retryUnfulfilledCommands: true - }); - bclient.once('ready', function () { - bclient.blpop('blocking list 2', 5, function (err, value) { - assert.strictEqual(value[0], 'blocking list 2'); - assert.strictEqual(value[1], 'initial value'); - bclient.end(true); - done(err); - }); - setTimeout(function () { - bclient.stream.destroy(); - client.rpush('blocking list 2', 'initial value', helper.isNumber(1)); - }, 100); - }); - }); - - }); - - describe('.end', function () { - - it('used without flush / flush set to false', function (done) { - var finished = false; - var end = helper.callFuncAfter(function () { - if (!finished) { - done(new Error('failed')); - } - }, 20); - var cb = function (err, res) { - assert(/Connection forcefully ended|The connection is already closed./.test(err.message)); - assert.strictEqual(err.code, 'NR_CLOSED'); - end(); - }; - for (var i = 0; i < 20; i++) { - if (i === 10) { - client.end(); - } - client.set('foo', 'bar', cb); - } - client.on('warning', function () {}); // Ignore deprecation message - setTimeout(function () { - finished = true; - done(); - }, 25); - }); - - it('used with flush set to true', function (done) { - var end = helper.callFuncAfter(function () { - done(); - }, 20); - var cb = function (err, res) { - assert(/Connection forcefully ended|The connection is already closed./.test(err.message)); - end(); - }; - for (var i = 0; i < 20; i++) { - if (i === 10) { - client.end(true); - client.stream.write('foo'); // Trigger an error on the closed stream that we ignore - } - client.set('foo', 'bar', cb); - } - }); - - it('emits an aggregate error if no callback was present for multiple commands in debug_mode', function (done) { - redis.debug_mode = true; - var unhookIntercept = intercept(function (data) { - return ''; // Don't print the debug messages - }); - client.set('foo', 'bar'); - client.set('baz', 'hello world'); - client.on('error', function (err) { - assert(err instanceof Error); - assert(err instanceof redis.AbortError); - assert(err instanceof redis.AggregateError); - assert.strictEqual(err.name, 'AggregateError'); - assert.strictEqual(err.errors.length, 2); - assert.strictEqual(err.message, 'Connection forcefully ended and commands aborted.'); - assert.strictEqual(err.code, 'NR_CLOSED'); - assert.strictEqual(err.errors[0].message, 'Connection forcefully ended and command aborted. It might have been processed.'); - assert.strictEqual(err.errors[0].command, 'SET'); - assert.strictEqual(err.errors[0].code, 'NR_CLOSED'); - assert.deepEqual(err.errors[0].args, ['foo', 'bar']); - done(); - }); - client.end(true); - unhookIntercept(); - redis.debug_mode = false; - }); - - it('emits an abort error if no callback was present for a single commands', function (done) { - redis.debug_mode = true; - var unhookIntercept = intercept(function (data) { - return ''; // Don't print the debug messages - }); - client.set('foo', 'bar'); - client.on('error', function (err) { - assert(err instanceof Error); - assert(err instanceof redis.AbortError); - assert(!(err instanceof redis.AggregateError)); - assert.strictEqual(err.message, 'Connection forcefully ended and command aborted. It might have been processed.'); - assert.strictEqual(err.command, 'SET'); - assert.strictEqual(err.code, 'NR_CLOSED'); - assert.deepEqual(err.args, ['foo', 'bar']); - done(); - }); - client.end(true); - unhookIntercept(); - redis.debug_mode = false; - }); - - it('does not emit abort errors if no callback was present while not being in debug_mode ', function (done) { - client.set('foo', 'bar'); - client.end(true); - setTimeout(done, 100); - }); - - }); - - describe('commands after using .quit should fail', function () { - - it('return an error in the callback', function (done) { - if (helper.redisProcess().spawnFailed()) this.skip(); - - // TODO: Investigate why this test is failing hard and killing mocha if using '/tmp/redis.sock'. - // Seems like something is wrong with nyc while passing a socket connection to create client! - var client2 = redis.createClient(); - client2.quit(function () { - client2.get('foo', function (err, res) { - assert.strictEqual(err.message, 'Stream connection ended and command aborted. It might have been processed.'); - assert.strictEqual(client2.offline_queue.length, 0); - done(); - }); - }); - }); - - it('return an error in the callback version two', function (done) { - if (helper.redisProcess().spawnFailed()) this.skip(); - - client.quit(); - setTimeout(function () { - client.get('foo', function (err, res) { - assert.strictEqual(err.message, 'GET can\'t be processed. The connection is already closed.'); - assert.strictEqual(err.command, 'GET'); - assert.strictEqual(client.offline_queue.length, 0); - done(); - }); - }, 50); - }); - - it('emit an error', function (done) { - if (helper.redisProcess().spawnFailed()) this.skip(); - client.quit(); - client.on('error', function (err) { - assert.strictEqual(err.message, 'SET can\'t be processed. The connection is already closed.'); - assert.strictEqual(err.command, 'SET'); - assert.strictEqual(client.offline_queue_length, 0); - done(); - }); - setTimeout(function () { - client.set('foo', 'bar'); - }, 50); - }); - - }); - - describe('when redis closes unexpectedly', function () { - it('reconnects and can retrieve the pre-existing data', function (done) { - client.on('reconnecting', function on_recon (params) { - client.on('connect', function on_connect () { - var end = helper.callFuncAfter(function () { - client.removeListener('connect', on_connect); - client.removeListener('reconnecting', on_recon); - assert.strictEqual(client.server_info.db0.keys, 2); - assert.strictEqual(Object.keys(client.server_info.db0).length, 3); - done(); - }, 4); - client.get('recon 1', helper.isString('one', end)); - client.get('recon 1', helper.isString('one', end)); - client.get('recon 2', helper.isString('two', end)); - client.get('recon 2', helper.isString('two', end)); - }); - }); - - client.set('recon 1', 'one'); - client.set('recon 2', 'two', function (err, res) { - // Do not do this in normal programs. This is to simulate the server closing on us. - // For orderly shutdown in normal programs, do client.quit() - client.stream.destroy(); - }); - }); - - it('reconnects properly when monitoring', function (done) { - client.on('reconnecting', function on_recon (params) { - client.on('ready', function on_ready () { - assert.strictEqual(client.monitoring, true, 'monitoring after reconnect'); - client.removeListener('ready', on_ready); - client.removeListener('reconnecting', on_recon); - done(); - }); - }); - - assert.strictEqual(client.monitoring, false, 'monitoring off at start'); - client.set('recon 1', 'one'); - client.monitor(function (err, res) { - assert.strictEqual(client.monitoring, true, 'monitoring on after monitor()'); - client.set('recon 2', 'two', function (err, res) { - // Do not do this in normal programs. This is to simulate the server closing on us. - // For orderly shutdown in normal programs, do client.quit() - client.stream.destroy(); - }); - }); - }); - - describe("and it's subscribed to a channel", function () { - // "Connection in subscriber mode, only subscriber commands may be used" - it('reconnects, unsubscribes, and can retrieve the pre-existing data', function (done) { - client.on('ready', function on_connect () { - client.unsubscribe(helper.isNotError()); - - client.on('unsubscribe', function (channel, count) { - // we should now be out of subscriber mode. - assert.strictEqual(channel, 'recon channel'); - assert.strictEqual(count, 0); - client.set('foo', 'bar', helper.isString('OK', done)); - }); - }); - - client.set('recon 1', 'one'); - client.subscribe('recon channel', function (err, res) { - // Do not do this in normal programs. This is to simulate the server closing on us. - // For orderly shutdown in normal programs, do client.quit() - client.stream.destroy(); - }); - }); - - it('reconnects, unsubscribes, and can retrieve the pre-existing data of a explicit channel', function (done) { - client.on('ready', function on_connect () { - client.unsubscribe('recon channel', helper.isNotError()); - - client.on('unsubscribe', function (channel, count) { - // we should now be out of subscriber mode. - assert.strictEqual(channel, 'recon channel'); - assert.strictEqual(count, 0); - client.set('foo', 'bar', helper.isString('OK', done)); - }); - }); - - client.set('recon 1', 'one'); - client.subscribe('recon channel', function (err, res) { - // Do not do this in normal programs. This is to simulate the server closing on us. - // For orderly shutdown in normal programs, do client.quit() - client.stream.destroy(); - }); - }); - }); - - describe('domain', function () { - it('allows client to be executed from within domain', function (done) { - var domain = require('domain').create(); - - domain.run(function () { - client.set('domain', 'value', function (err, res) { - assert.ok(process.domain); - throw new Error('ohhhh noooo'); - }); - }); - - // this is the expected and desired behavior - domain.on('error', function (err) { - assert.strictEqual(err.message, 'ohhhh noooo'); - domain.exit(); - done(); - }); - }); - - it('keeps the same domain by using the offline queue', function (done) { - client.end(true); - client = redis.createClient(); - var testDomain = require('domain').create(); - testDomain.run(function () { - client.set('FOOBAR', 'def', function () { - assert.strictEqual(process.domain, testDomain); - done(); - }); - }); - }); - - it('catches all errors from within the domain', function (done) { - var domain = require('domain').create(); - - domain.run(function () { - if (process.versions.node.split('.')[0] >= 13) { - // Node >= 13 - // Recreate client in domain so error handlers run this domain - // Changed in: "error handler runs outside of its domain" - // https://github.com/nodejs/node/pull/26211 - client.end(true); // make sure to close current client - client = redis.createClient(); - } - client.end(true); - // Trigger an error within the domain - client.set('domain', 'value'); - }); - - domain.on('error', function (err) { - assert.strictEqual(err.message, 'SET can\'t be processed. The connection is already closed.'); - domain.exit(); - done(); - }); - }); - }); - }); - - describe('utf8', function () { - it('handles utf-8 keys', function (done) { - var utf8_sample = 'ಠ_ಠ'; - client.set(['utf8test', utf8_sample], helper.isString('OK')); - client.get(['utf8test'], function (err, obj) { - assert.strictEqual(utf8_sample, obj); - done(err); - }); - }); - }); - }); - - describe('unref', function () { - it('exits subprocess as soon as final command is processed', function (done) { - this.timeout(12000); - var args = config.HOST[ip] ? [config.HOST[ip], config.PORT] : [ip]; - var external = fork('./test/lib/unref.js', args); - - var id = setTimeout(function () { - external.kill(); - done(new Error('unref subprocess timed out')); - }, 8000); - - external.on('close', function (code) { - clearTimeout(id); - assert.strictEqual(code, 0); - done(); - }); - }); - }); - - describe('execution order / fire query while loading', function () { - it('keep execution order for commands that may fire while redis is still loading', function (done) { - client = redis.createClient.apply(null, args); - var fired = false; - client.set('foo', 'bar', function (err, res) { - assert(fired === false); - done(); - }); - client.info(function (err, res) { - fired = true; - }); - }); - - // TODO: consider allowing loading commands in v.4 - // it('should fire early', function (done) { - // client = redis.createClient.apply(null, args); - // var fired = false; - // client.info(function (err, res) { - // fired = true; - // }); - // client.set('foo', 'bar', function (err, res) { - // assert(fired); - // done(); - // }); - // assert.strictEqual(client.offline_queue.length, 1); - // assert.strictEqual(client.command_queue.length, 1); - // client.on('connect', function () { - // assert.strictEqual(client.offline_queue.length, 1); - // assert.strictEqual(client.command_queue.length, 1); - // }); - // client.on('ready', function () { - // assert.strictEqual(client.offline_queue.length, 0); - // }); - // }); - }); - - describe('protocol error', function () { - - it('should gracefully recover and only fail on the already send commands', function (done) { - client = redis.createClient.apply(null, args); - var error; - client.on('error', function (err) { - assert.strictEqual(err.message, 'Protocol error, got "a" as reply type byte. Please report this.'); - assert.strictEqual(err, error); - assert(err instanceof redis.ParserError); - // After the hard failure work properly again. The set should have been processed properly too - client.get('foo', function (err, res) { - assert.strictEqual(res, 'bar'); - done(); - }); - }); - client.once('ready', function () { - client.set('foo', 'bar', function (err, res) { - assert.strictEqual(err.message, 'Fatal error encountered. Command aborted. It might have been processed.'); - assert.strictEqual(err.code, 'NR_FATAL'); - assert(err instanceof redis.AbortError); - error = err.origin; - }); - // Make sure we call execute out of the reply - // ready is called in a reply - process.nextTick(function () { - // Fail the set answer. Has no corresponding command obj and will therefore land in the error handler and set - client.reply_parser.execute(Buffer.from('a*1\r*1\r$1`zasd\r\na')); - }); - }); - }); - }); - - describe('enable_offline_queue', function () { - describe('true', function () { - - it('does not return an error and enqueues operation', function (done) { - client = redis.createClient(9999, null); - var finished = false; - client.on('error', function (e) { - // ignore, b/c expecting a "can't connect" error - }); - - setTimeout(function () { - client.set('foo', 'bar', function (err, result) { - if (!finished) done(err); - assert.strictEqual(err.message, 'Connection forcefully ended and command aborted.'); - }); - - setTimeout(function () { - assert.strictEqual(client.offline_queue.length, 1); - finished = true; - done(); - }, 25); - }, 50); - }); - - it('enqueues operation and keep the queue while trying to reconnect', function (done) { - client = redis.createClient(9999, null, { - retry_strategy: function (options) { - if (options.attempt > 4) { - return undefined; - } - return 100; - }, - }); - var i = 0; - - client.on('error', function (err) { - if (err.code === 'CONNECTION_BROKEN') { - assert(i, 3); - assert.strictEqual(client.offline_queue.length, 0); - assert.strictEqual(err.origin.code, 'ECONNREFUSED'); - if (!(err instanceof redis.AbortError)) { - done(); - } else { - assert.strictEqual(err.command, 'SET'); - } - } else { - assert.equal(err.code, 'ECONNREFUSED'); - - if (typeof err.errno === 'number') { - // >= Node 13 - assert.equal(util.getSystemErrorName(err.errno), 'ECONNREFUSED'); - } else { - // < Node 13 - assert.equal(err.errno, 'ECONNREFUSED'); - } - assert.equal(err.syscall, 'connect'); - } - }); - - client.on('reconnecting', function (params) { - i++; - assert.equal(params.attempt, i); - assert.strictEqual(params.times_connected, 0); - assert(params.error instanceof Error); - assert(typeof params.total_retry_time === 'number'); - assert.strictEqual(client.offline_queue.length, 2); - }); - - // Should work with either a callback or without - client.set('baz', 13); - client.set('foo', 'bar', function (err, result) { - assert(i, 3); - assert(err); - assert.strictEqual(client.offline_queue.length, 0); - }); - }); - - it('flushes the command queue if connection is lost', function (done) { - client = redis.createClient(); - - client.once('ready', function () { - var multi = client.multi(); - multi.config('bar'); - var cb = function (err, reply) { - assert.equal(err.code, 'UNCERTAIN_STATE'); - }; - for (var i = 0; i < 12; i += 3) { - client.set('foo' + i, 'bar' + i); - multi.set('foo' + (i + 1), 'bar' + (i + 1), cb); - multi.set('foo' + (i + 2), 'bar' + (i + 2)); - } - multi.exec(); - assert.equal(client.command_queue_length, 15); - helper.killConnection(client); - }); - - var end = helper.callFuncAfter(done, 3); - client.on('error', function (err) { - if (err.command === 'EXEC') { - assert.strictEqual(client.command_queue.length, 0); - assert.strictEqual(err.errors.length, 9); - assert.strictEqual(err.errors[1].command, 'SET'); - assert.deepEqual(err.errors[1].args, ['foo1', 'bar1']); - end(); - } else if (err.code === 'UNCERTAIN_STATE') { - assert.strictEqual(client.command_queue.length, 0); - assert.strictEqual(err.errors.length, 4); - assert.strictEqual(err.errors[0].command, 'SET'); - assert.deepEqual(err.errors[0].args, ['foo0', 'bar0']); - end(); - } else { - assert.equal(err.code, 'ECONNREFUSED'); - if (typeof err.errno === 'number') { - // >= Node 13 - assert.equal(util.getSystemErrorName(err.errno), 'ECONNREFUSED'); - } else { - // < Node 13 - assert.equal(err.errno, 'ECONNREFUSED'); - } - assert.equal(err.syscall, 'connect'); - end(); - } - }); - }); - }); - - describe('false', function () { - - it('stream not writable', function (done) { - client = redis.createClient({ - enable_offline_queue: false - }); - client.on('ready', function () { - client.stream.destroy(); - client.set('foo', 'bar', function (err, res) { - assert.strictEqual(err.message, "SET can't be processed. Stream not writeable."); - done(); - }); - }); - }); - - it('emit an error and does not enqueues operation', function (done) { - client = redis.createClient(9999, null, { - max_attempts: 0, - enable_offline_queue: false - }); - var end = helper.callFuncAfter(done, 3); - - client.on('error', function (err) { - assert(/offline queue is deactivated|ECONNREFUSED/.test(err.message)); - assert.equal(client.command_queue.length, 0); - end(); - }); - - client.set('foo', 'bar'); - - assert.doesNotThrow(function () { - client.set('foo', 'bar', function (err) { - // should callback with an error - assert.ok(err); - setTimeout(end, 50); - }); - }); - }); - - it('flushes the command queue if connection is lost', function (done) { - client = redis.createClient({ - enable_offline_queue: false - }); - - redis.debug_mode = true; - var unhookIntercept = intercept(function () { - return ''; - }); - client.once('ready', function () { - var multi = client.multi(); - multi.config('bar'); - var cb = function (err, reply) { - assert.equal(err.code, 'UNCERTAIN_STATE'); - }; - for (var i = 0; i < 12; i += 3) { - client.set('foo' + i, 'bar' + i); - multi.set('foo' + (i + 1), 'bar' + (i + 1), cb); - multi.set('foo' + (i + 2), 'bar' + (i + 2)); - } - multi.exec(); - assert.equal(client.command_queue.length, 15); - helper.killConnection(client); - }); - - var end = helper.callFuncAfter(done, 3); - client.on('error', function (err) { - assert.equal(client.command_queue.length, 0); - if (err.command === 'EXEC') { - assert.equal(err.errors.length, 9); - end(); - } else if (err.code === 'UNCERTAIN_STATE') { - assert.equal(err.errors.length, 4); - end(); - } else { - assert.equal(err.code, 'ECONNREFUSED'); - if (typeof err.errno === 'number') { - // >= Node 13 - assert.equal(util.getSystemErrorName(err.errno), 'ECONNREFUSED'); - } else { - // < Node 13 - assert.equal(err.errno, 'ECONNREFUSED'); - } - assert.equal(err.syscall, 'connect'); - redis.debug_mode = false; - client.end(true); - unhookIntercept(); - end(); - } - }); - }); - }); - }); - - }); - }); -}); diff --git a/test/prefix.spec.js b/test/prefix.spec.js deleted file mode 100644 index 52fd39c1ccd..00000000000 --- a/test/prefix.spec.js +++ /dev/null @@ -1,118 +0,0 @@ -'use strict'; - -var assert = require('assert'); -var config = require('./lib/config'); -var helper = require('./helper'); -var redis = config.redis; - -describe('prefix key names', function () { - - helper.allTests(function (ip, args) { - - describe('using ' + ip, function () { - var client = null; - - beforeEach(function (done) { - client = redis.createClient({ - prefix: 'test:prefix:' - }); - client.on('ready', function () { - client.flushdb(function (err) { - done(err); - }); - }); - }); - - afterEach(function () { - client.end(true); - }); - - it('auto prefix set / get', function (done) { - client.set('key', 'value', function (err, reply) { - assert.strictEqual(reply, 'OK'); - }); - client.get('key', function (err, reply) { - assert.strictEqual(reply, 'value'); - }); - client.getrange('key', 1, -1, function (err, reply) { - assert.strictEqual(reply, 'alue'); - assert.strictEqual(err, null); - }); - client.exists('key', function (err, res) { - assert.strictEqual(res, 1); - }); - client.exists('test:prefix:key', function (err, res) { - // The key will be prefixed itself - assert.strictEqual(res, 0); - }); - client.mset('key2', 'value2', 'key3', 'value3'); - client.keys('*', function (err, res) { - assert.strictEqual(res.length, 3); - assert(res.indexOf('test:prefix:key') !== -1); - assert(res.indexOf('test:prefix:key2') !== -1); - assert(res.indexOf('test:prefix:key3') !== -1); - done(); - }); - }); - - it('auto prefix set / get with .batch', function (done) { - var batch = client.batch(); - batch.set('key', 'value', function (err, reply) { - assert.strictEqual(reply, 'OK'); - }); - batch.get('key', function (err, reply) { - assert.strictEqual(reply, 'value'); - }); - batch.getrange('key', 1, -1, function (err, reply) { - assert.strictEqual(reply, 'alue'); - assert.strictEqual(err, null); - }); - batch.exists('key', function (err, res) { - assert.strictEqual(res, 1); - }); - batch.exists('test:prefix:key', function (err, res) { - // The key will be prefixed itself - assert.strictEqual(res, 0); - }); - batch.mset('key2', 'value2', 'key3', 'value3'); - batch.keys('*', function (err, res) { - assert.strictEqual(res.length, 3); - assert(res.indexOf('test:prefix:key') !== -1); - assert(res.indexOf('test:prefix:key2') !== -1); - assert(res.indexOf('test:prefix:key3') !== -1); - }); - batch.exec(done); - }); - - it('auto prefix set / get with .multi', function (done) { - var multi = client.multi(); - multi.set('key', 'value', function (err, reply) { - assert.strictEqual(reply, 'OK'); - }); - multi.get('key', function (err, reply) { - assert.strictEqual(reply, 'value'); - }); - multi.getrange('key', 1, -1, function (err, reply) { - assert.strictEqual(reply, 'alue'); - assert.strictEqual(err, null); - }); - multi.exists('key', function (err, res) { - assert.strictEqual(res, 1); - }); - multi.exists('test:prefix:key', function (err, res) { - // The key will be prefixed itself - assert.strictEqual(res, 0); - }); - multi.mset('key2', 'value2', 'key3', 'value3'); - multi.keys('*', function (err, res) { - assert.strictEqual(res.length, 3); - assert(res.indexOf('test:prefix:key') !== -1); - assert(res.indexOf('test:prefix:key2') !== -1); - assert(res.indexOf('test:prefix:key3') !== -1); - }); - multi.exec(done); - }); - - }); - }); -}); diff --git a/test/pubsub.spec.js b/test/pubsub.spec.js deleted file mode 100644 index 34e93f37f2c..00000000000 --- a/test/pubsub.spec.js +++ /dev/null @@ -1,679 +0,0 @@ -'use strict'; - -var assert = require('assert'); -var config = require('./lib/config'); -var helper = require('./helper'); -var errors = require('./errors'); -var redis = config.redis; - -describe('publish/subscribe', function () { - - helper.allTests(function (ip, args) { - - describe('using ' + ip, function () { - var pub = null; - var sub = null; - var channel = 'test channel'; - var channel2 = 'test channel 2'; - var message = 'test message'; - - beforeEach(function (done) { - var end = helper.callFuncAfter(done, 2); - - pub = redis.createClient.apply(null, args); - sub = redis.createClient.apply(null, args); - pub.once('connect', function () { - pub.flushdb(function () { - end(); - }); - }); - sub.once('connect', function () { - end(); - }); - }); - - describe('disable resubscribe', function () { - beforeEach(function (done) { - sub.end(false); - sub = redis.createClient({ - disable_resubscribing: true - }); - sub.once('connect', function () { - done(); - }); - }); - - it('does not fire subscribe events after reconnecting', function (done) { - var a = false; - sub.on('subscribe', function (chnl, count) { - if (chnl === channel2) { - if (a) { - return done(new Error('Test failed')); - } - assert.equal(2, count); - sub.stream.destroy(); - } - }); - - sub.on('reconnecting', function () { - a = true; - sub.on('ready', function () { - assert.strictEqual(sub.command_queue.length, 0); - done(); - }); - }); - - sub.subscribe(channel, channel2); - }); - }); - - describe('string_numbers and pub sub', function () { - beforeEach(function (done) { - sub.end(false); - sub = redis.createClient({ - string_numbers: true - }); - sub.once('connect', function () { - done(); - }); - }); - - it('does not fire subscribe events after reconnecting', function (done) { - var i = 0; - var end = helper.callFuncAfter(done, 2); - sub.on('subscribe', function (chnl, count) { - assert.strictEqual(typeof count, 'number'); - assert.strictEqual(++i, count); - }); - sub.on('unsubscribe', function (chnl, count) { - assert.strictEqual(typeof count, 'number'); - assert.strictEqual(--i, count); - }); - sub.subscribe(channel, channel2); - sub.unsubscribe(function (err, res) { // Do not pass a channel here! - assert.strictEqual(sub.pub_sub_mode, 2); - assert.deepEqual(sub.subscription_set, {}); - end(); - }); - sub.set('foo', 'bar', helper.isString('OK')); - sub.subscribe(channel2, end); - }); - }); - - describe('subscribe', function () { - it('fires a subscribe event for each channel subscribed to even after reconnecting', function (done) { - var a = false; - sub.on('subscribe', function (chnl, count) { - if (chnl === channel2) { - assert.equal(2, count); - if (a) return done(); - sub.stream.destroy(); - } - }); - - sub.on('reconnecting', function () { - a = true; - }); - - sub.subscribe(channel, channel2); - }); - - it('fires a subscribe event for each channel as buffer subscribed to even after reconnecting', function (done) { - var a = false; - sub.end(true); - sub = redis.createClient({ - detect_buffers: true - }); - sub.on('subscribe', function (chnl, count) { - if (chnl.inspect() === Buffer.from([0xAA, 0xBB, 0x00, 0xF0]).inspect()) { - assert.equal(1, count); - if (a) { - return done(); - } - sub.stream.destroy(); - } - }); - - sub.on('reconnecting', function () { - a = true; - }); - - sub.subscribe(Buffer.from([0xAA, 0xBB, 0x00, 0xF0]), channel2); - }); - - it('receives messages on subscribed channel', function (done) { - var end = helper.callFuncAfter(done, 2); - sub.on('subscribe', function (chnl, count) { - pub.publish(channel, message, function (err, res) { - helper.isNumber(1)(err, res); - end(); - }); - }); - - sub.on('message', function (chnl, msg) { - assert.equal(chnl, channel); - assert.equal(msg, message); - end(); - }); - - sub.subscribe(channel); - }); - - it('receives messages if subscribe is called after unsubscribe', function (done) { - var end = helper.callFuncAfter(done, 2); - sub.once('subscribe', function (chnl, count) { - pub.publish(channel, message, function (err, res) { - helper.isNumber(1)(err, res); - end(); - }); - }); - - sub.on('message', function (chnl, msg) { - assert.equal(chnl, channel); - assert.equal(msg, message); - end(); - }); - - sub.subscribe(channel); - sub.unsubscribe(channel); - sub.subscribe(channel); - }); - - it('handles SUB_UNSUB_MSG_SUB', function (done) { - sub.subscribe('chan8'); - sub.subscribe('chan9'); - sub.unsubscribe('chan9'); - pub.publish('chan8', 'something'); - sub.subscribe('chan9', done); - }); - - it('handles SUB_UNSUB_MSG_SUB 2', function (done) { - sub.psubscribe('abc*', helper.isString('abc*')); - sub.subscribe('xyz'); - sub.unsubscribe('xyz'); - pub.publish('abcd', 'something'); - sub.subscribe('xyz', done); - }); - - it('emits end event if quit is called from within subscribe', function (done) { - sub.on('end', done); - sub.on('subscribe', function (chnl, count) { - sub.quit(); - }); - sub.subscribe(channel); - }); - - it('subscribe; close; resubscribe with prototype inherited property names', function (done) { - var count = 0; - var channels = ['channel 1', 'channel 2']; - var msg = ['hi from channel 1', 'hi from channel 2']; - - sub.on('message', function (channel, message) { - var n = Math.max(count - 1, 0); - assert.strictEqual(channel, channels[n]); - assert.strictEqual(message, msg[n]); - if (count === 2) return done(); - sub.stream.end(); - }); - - sub.select(3); - sub.subscribe(channels); - - sub.on('ready', function (err, results) { - pub.publish(channels[count], msg[count]); - count++; - }); - - pub.publish(channels[count], msg[count]); - }); - }); - - describe('multiple subscribe / unsubscribe commands', function () { - - it('reconnects properly with pub sub and select command', function (done) { - var end = helper.callFuncAfter(done, 2); - sub.select(3); - sub.set('foo', 'bar'); - sub.set('failure', helper.isError()); // Triggering a warning while subscribing should work - sub.mget('foo', 'bar', 'baz', 'hello', 'world', function (err, res) { - assert.deepEqual(res, ['bar', null, null, null, null]); - }); - sub.subscribe('somechannel', 'another channel', function (err, res) { - end(); - sub.stream.destroy(); - }); - assert(sub.ready); - sub.on('ready', function () { - sub.unsubscribe(); - sub.del('foo'); - sub.info(end); - }); - }); - - it('should not go into pubsub mode with unsubscribe commands', function (done) { - sub.on('unsubscribe', function (msg) { - // The unsubscribe should not be triggered, as there was no corresponding channel - throw new Error('Test failed'); - }); - sub.set('foo', 'bar'); - sub.unsubscribe(function (err, res) { - assert.strictEqual(res, null); - }); - sub.del('foo', done); - }); - - it('handles multiple channels with the same channel name properly, even with buffers', function (done) { - var channels = ['a', 'b', 'a', Buffer.from('a'), 'c', 'b']; - var subscribed_channels = [1, 2, 2, 2, 3, 3]; - var i = 0; - sub.subscribe(channels); - sub.on('subscribe', function (channel, count) { - if (Buffer.isBuffer(channel)) { - assert.strictEqual(channel.inspect(), Buffer.from(channels[i]).inspect()); - } else { - assert.strictEqual(channel, channels[i].toString()); - } - assert.strictEqual(count, subscribed_channels[i]); - i++; - }); - sub.unsubscribe('a', 'c', 'b'); - sub.get('foo', done); - }); - - it('should only resubscribe to channels not unsubscribed earlier on a reconnect', function (done) { - sub.subscribe('/foo', '/bar'); - sub.batch().unsubscribe(['/bar'], function () { - pub.pubsub('channels', function (err, res) { - assert.deepEqual(res, ['/foo']); - sub.stream.destroy(); - sub.once('ready', function () { - pub.pubsub('channels', function (err, res) { - assert.deepEqual(res, ['/foo']); - sub.unsubscribe('/foo', done); - }); - }); - }); - }).exec(); - }); - - it('unsubscribes, subscribes, unsubscribes... single and multiple entries mixed. Withouth callbacks', function (done) { - function subscribe (channels) { - sub.unsubscribe(helper.isNull); - sub.subscribe(channels, helper.isNull); - } - var all = false; - var subscribeMsg = ['1', '3', '2', '5', 'test', 'bla']; - sub.on('subscribe', function (msg, count) { - subscribeMsg.splice(subscribeMsg.indexOf(msg), 1); - if (subscribeMsg.length === 0 && all) { - assert.strictEqual(count, 3); - done(); - } - }); - var unsubscribeMsg = ['1', '3', '2']; - sub.on('unsubscribe', function (msg, count) { - unsubscribeMsg.splice(unsubscribeMsg.indexOf(msg), 1); - if (unsubscribeMsg.length === 0) { - assert.strictEqual(count, 0); - all = true; - } - }); - - subscribe(['1', '3']); - subscribe(['2']); - subscribe(['5', 'test', 'bla']); - }); - - it('unsubscribes, subscribes, unsubscribes... single and multiple entries mixed. Without callbacks', function (done) { - function subscribe (channels) { - sub.unsubscribe(); - sub.subscribe(channels); - } - var all = false; - var subscribeMsg = ['1', '3', '2', '5', 'test', 'bla']; - sub.on('subscribe', function (msg, count) { - subscribeMsg.splice(subscribeMsg.indexOf(msg), 1); - if (subscribeMsg.length === 0 && all) { - assert.strictEqual(count, 3); - done(); - } - }); - var unsubscribeMsg = ['1', '3', '2']; - sub.on('unsubscribe', function (msg, count) { - unsubscribeMsg.splice(unsubscribeMsg.indexOf(msg), 1); - if (unsubscribeMsg.length === 0) { - assert.strictEqual(count, 0); - all = true; - } - }); - - subscribe(['1', '3']); - subscribe(['2']); - subscribe(['5', 'test', 'bla']); - }); - - it('unsubscribes, subscribes, unsubscribes... single and multiple entries mixed. Without callback and concret channels', function (done) { - function subscribe (channels) { - sub.unsubscribe(channels); - sub.unsubscribe(channels); - sub.subscribe(channels); - } - var all = false; - var subscribeMsg = ['1', '3', '2', '5', 'test', 'bla']; - sub.on('subscribe', function (msg, count) { - subscribeMsg.splice(subscribeMsg.indexOf(msg), 1); - if (subscribeMsg.length === 0 && all) { - assert.strictEqual(count, 6); - done(); - } - }); - var unsubscribeMsg = ['1', '3', '2', '5', 'test', 'bla']; - sub.on('unsubscribe', function (msg, count) { - var pos = unsubscribeMsg.indexOf(msg); - if (pos !== -1) - unsubscribeMsg.splice(pos, 1); - if (unsubscribeMsg.length === 0) { - all = true; - } - }); - - subscribe(['1', '3']); - subscribe(['2']); - subscribe(['5', 'test', 'bla']); - }); - - it('unsubscribes, subscribes, unsubscribes... with pattern matching', function (done) { - function subscribe (channels, callback) { - sub.punsubscribe('prefix:*', helper.isNull); - sub.psubscribe(channels, function (err, res) { - helper.isNull(err); - if (callback) callback(err, res); - }); - } - var all = false; - var end = helper.callFuncAfter(done, 8); - var subscribeMsg = ['prefix:*', 'prefix:3', 'prefix:2', '5', 'test:a', 'bla']; - sub.on('psubscribe', function (msg, count) { - subscribeMsg.splice(subscribeMsg.indexOf(msg), 1); - if (subscribeMsg.length === 0) { - assert.strictEqual(count, 5); - all = true; - } - }); - var rest = 1; - var unsubscribeMsg = ['prefix:*', 'prefix:*', 'prefix:*', '*']; - sub.on('punsubscribe', function (msg, count) { - unsubscribeMsg.splice(unsubscribeMsg.indexOf(msg), 1); - if (all) { - assert.strictEqual(unsubscribeMsg.length, 0); - assert.strictEqual(count, rest--); // Print the remaining channels - end(); - } else { - assert.strictEqual(msg, 'prefix:*'); - assert.strictEqual(count, rest++ - 1); - } - }); - sub.on('pmessage', function (pattern, channel, msg) { - assert.strictEqual(msg, 'test'); - assert.strictEqual(pattern, 'prefix:*'); - assert.strictEqual(channel, 'prefix:1'); - end(); - }); - - subscribe(['prefix:*', 'prefix:3'], function () { - pub.publish('prefix:1', Buffer.from('test'), function () { - subscribe(['prefix:2']); - subscribe(['5', 'test:a', 'bla'], function () { - assert(all); - }); - sub.punsubscribe(function (err, res) { - assert(!err); - assert.strictEqual(res, 'bla'); - assert(all); - all = false; // Make sure the callback is actually after the emit - end(); - }); - sub.pubsub('channels', function (err, res) { - assert.strictEqual(res.length, 0); - end(); - }); - }); - }); - }); - }); - - describe('unsubscribe', function () { - it('fires an unsubscribe event', function (done) { - sub.on('subscribe', function (chnl, count) { - sub.unsubscribe(channel); - }); - - sub.subscribe(channel); - - sub.on('unsubscribe', function (chnl, count) { - assert.equal(chnl, channel); - assert.strictEqual(count, 0); - return done(); - }); - }); - - it('puts client back into write mode', function (done) { - sub.on('subscribe', function (chnl, count) { - sub.unsubscribe(channel); - }); - - sub.subscribe(channel); - - sub.on('unsubscribe', function (chnl, count) { - pub.incr('foo', helper.isNumber(1, done)); - }); - }); - - it('does not complain when unsubscribe is called and there are no subscriptions', function (done) { - sub.unsubscribe(function (err, res) { - assert.strictEqual(err, null); - assert.strictEqual(res, null); - done(); - }); - }); - - it('executes callback when unsubscribe is called and there are no subscriptions', function (done) { - pub.unsubscribe(function (err, results) { - assert.strictEqual(null, results); - done(err); - }); - }); - }); - - describe('psubscribe', function () { - it('allows all channels to be subscribed to using a * pattern', function (done) { - sub.subscribe('/foo'); - var sub2 = redis.createClient({ - return_buffers: true - }); - sub2.on('ready', function () { - sub2.batch().psubscribe('*', helper.isString('*')).exec(); - sub2.subscribe('/foo'); - sub2.on('pmessage', function (pattern, channel, message) { - assert.strictEqual(pattern.inspect(), Buffer.from('*').inspect()); - assert.strictEqual(channel.inspect(), Buffer.from('/foo').inspect()); - assert.strictEqual(message.inspect(), Buffer.from('hello world').inspect()); - sub2.quit(done); - }); - pub.pubsub('numsub', '/foo', function (err, res) { - assert.deepEqual(res, ['/foo', 2]); - }); - // sub2 is counted twice as it subscribed with psubscribe and subscribe - pub.publish('/foo', 'hello world', helper.isNumber(3)); - }); - }); - - it('allows to listen to pmessageBuffer and pmessage', function (done) { - var end = helper.callFuncAfter(done, 6); - var data = Array(10000).join('äüs^öéÉÉ`e'); - sub.set('foo', data, function () { - sub.get('foo'); - sub.stream.once('data', function () { - assert.strictEqual(sub.message_buffers, false); - assert.strictEqual(sub.shouldBuffer, false); - sub.on('pmessageBuffer', function (pattern, channel) { - if (typeof pattern === 'string') { - pattern = Buffer.from(pattern); - channel = Buffer.from(channel); - } - assert.strictEqual(pattern.inspect(), Buffer.from('*').inspect()); - assert.strictEqual(channel.inspect(), Buffer.from('/foo').inspect()); - sub.quit(end); - }); - // Either message_buffers or buffers has to be true, but not both at the same time - assert.notStrictEqual(sub.message_buffers, sub.buffers); - }); - var batch = sub.batch(); - batch.psubscribe('*'); - batch.subscribe('/foo'); - batch.unsubscribe('/foo'); - batch.unsubscribe(helper.isNull()); - batch.subscribe(['/foo'], helper.isString('/foo')); - batch.exec(function () { - pub.pubsub('numsub', '/foo', function (err, res) { - // There's one subscriber to this channel - assert.deepEqual(res, ['/foo', 1]); - end(); - }); - pub.pubsub('channels', function (err, res) { - // There's exactly one channel that is listened too - assert.deepEqual(res, ['/foo']); - end(); - }); - pub.pubsub('numpat', function (err, res) { - // One pattern is active - assert.strictEqual(res, 1); - end(); - }); - pub.publish('/foo', 'hello world', helper.isNumber(2)); - }); - // Either message_buffers or buffers has to be true, but not both at the same time - sub.on('pmessage', function (pattern, channel, message) { - assert.strictEqual(pattern, '*'); - assert.strictEqual(channel, '/foo'); - assert.strictEqual(message, 'hello world'); - end(); - }); - sub.on('message', function (channel, message) { - assert.strictEqual(channel, '/foo'); - assert.strictEqual(message, 'hello world'); - end(); - }); - }); - }); - }); - - describe('punsubscribe', function () { - it('does not complain when punsubscribe is called and there are no subscriptions', function () { - sub.punsubscribe(); - }); - - it('executes callback when punsubscribe is called and there are no subscriptions', function (done) { - pub.batch().punsubscribe(helper.isNull()).exec(done); - }); - }); - - describe('fail for other commands while in pub sub mode', function () { - it('return error if only pub sub commands are allowed', function (done) { - sub.subscribe('channel'); - // Ping is allowed even if not listed as such! - sub.ping(function (err, res) { - assert.strictEqual(err, null); - assert.strictEqual(res[0], 'pong'); - }); - // Get is forbidden - sub.get('foo', function (err, res) { - assert.ok(errors.subscribeUnsubscribeOnly.test(err.message)); - assert.strictEqual(err.command, 'GET'); - }); - // Quit is allowed - sub.quit(done); - }); - - it('emit error if only pub sub commands are allowed without callback', function (done) { - sub.subscribe('channel'); - sub.on('error', function (err) { - assert.ok(errors.subscribeUnsubscribeOnly.test(err.message)); - assert.strictEqual(err.command, 'GET'); - done(); - }); - sub.get('foo'); - }); - }); - - it('should not publish a message multiple times per command', function (done) { - var published = {}; - - function subscribe (message) { - sub.removeAllListeners('subscribe'); - sub.removeAllListeners('message'); - sub.removeAllListeners('unsubscribe'); - sub.on('subscribe', function () { - pub.publish('/foo', message); - }); - sub.on('message', function (channel, message) { - if (published[message]) { - done(new Error('Message published more than once.')); - } - published[message] = true; - }); - sub.on('unsubscribe', function (channel, count) { - assert.strictEqual(count, 0); - }); - sub.subscribe('/foo'); - } - - subscribe('hello'); - - setTimeout(function () { - sub.unsubscribe(); - setTimeout(function () { - subscribe('world'); - setTimeout(done, 50); - }, 40); - }, 40); - }); - - it('should not publish a message without any publish command', function (done) { - pub.set('foo', 'message'); - pub.set('bar', 'hello'); - pub.mget('foo', 'bar'); - pub.subscribe('channel', function () { - setTimeout(done, 50); - }); - pub.on('message', function (msg) { - done(new Error('This message should not have been published: ' + msg)); - }); - }); - - it('arguments variants', function (done) { - sub.batch() - .info(['stats']) - .info() - .client('KILL', ['type', 'pubsub']) - .client('KILL', ['type', 'pubsub'], function () {}) - .unsubscribe() - .psubscribe(['pattern:*']) - .punsubscribe('unkown*') - .punsubscribe(['pattern:*']) - .exec(function (err, res) { - sub.client('kill', ['type', 'pubsub']); - sub.psubscribe('*'); - sub.punsubscribe('pa*'); - sub.punsubscribe(['a', '*'], done); - }); - }); - - afterEach(function () { - // Explicitly ignore still running commands - pub.end(false); - sub.end(false); - }); - }); - }); -}); diff --git a/test/rename.spec.js b/test/rename.spec.js deleted file mode 100644 index 68adc5699f0..00000000000 --- a/test/rename.spec.js +++ /dev/null @@ -1,147 +0,0 @@ -'use strict'; - -var assert = require('assert'); -var config = require('./lib/config'); -var helper = require('./helper'); -var redis = config.redis; - -if (process.platform === 'win32') { - // TODO: Fix redis process spawn on windows - return; -} - -// TODO these tests are causing flakey tests - looks like redis-server is not -// being started with new configuration after or before these tests -xdescribe('rename commands', function () { - before(function (done) { - helper.stopRedis(function () { - helper.startRedis('./conf/rename.conf', done); - }); - }); - - helper.allTests(function (ip, args) { - - describe('using ' + ip, function () { - var client = null; - - beforeEach(function (done) { - if (helper.redisProcess().spawnFailed()) return done(); - client = redis.createClient({ - rename_commands: { - set: '807081f5afa96845a02816a28b7258c3', - GETRANGE: '9e3102b15cf231c4e9e940f284744fe0' - }, - }); - - client.on('ready', function () { - client.flushdb(done); - }); - }); - - afterEach(function () { - if (helper.redisProcess().spawnFailed()) return; - client.end(true); - }); - - it('allows to use renamed functions', function (done) { - if (helper.redisProcess().spawnFailed()) this.skip(); - - client.set('key', 'value', function (err, reply) { - assert.strictEqual(reply, 'OK'); - }); - - client.get('key', function (err, reply) { - assert.strictEqual(err.message, 'ERR unknown command `get`, with args beginning with: `key`, '); - assert.strictEqual(err.command, 'GET'); - assert.strictEqual(reply, undefined); - }); - - client.getrange('key', 1, -1, function (err, reply) { - assert.strictEqual(reply, 'alue'); - assert.strictEqual(err, null); - done(); - }); - }); - - it('should also work with batch', function (done) { - if (helper.redisProcess().spawnFailed()) this.skip(); - - client.batch([['set', 'key', 'value']]).exec(function (err, res) { - assert.strictEqual(res[0], 'OK'); - }); - - var batch = client.batch(); - batch.getrange('key', 1, -1); - batch.exec(function (err, res) { - assert(!err); - assert.strictEqual(res.length, 1); - assert.strictEqual(res[0], 'alue'); - done(); - }); - }); - - it('should also work with multi', function (done) { - if (helper.redisProcess().spawnFailed()) this.skip(); - - client.multi([['set', 'key', 'value']]).exec(function (err, res) { - assert.strictEqual(res[0], 'OK'); - }); - - var multi = client.multi(); - multi.getrange('key', 1, -1); - multi.exec(function (err, res) { - assert(!err); - assert.strictEqual(res.length, 1); - assert.strictEqual(res[0], 'alue'); - done(); - }); - }); - - it('should also work with multi and abort transaction', function (done) { - if (helper.redisProcess().spawnFailed()) this.skip(); - - var multi = client.multi(); - multi.get('key'); - multi.getrange('key', 1, -1, function (err, reply) { - assert.strictEqual(reply, 'alue'); - assert.strictEqual(err, null); - }); - multi.exec(function (err, res) { - assert(err); - assert.strictEqual(err.message, 'EXECABORT Transaction discarded because of previous errors.'); - assert.strictEqual(err.errors[0].message, 'ERR unknown command `get`, with args beginning with: `key`, '); - assert.strictEqual(err.errors[0].command, 'GET'); - assert.strictEqual(err.code, 'EXECABORT'); - assert.strictEqual(err.errors[0].code, 'ERR'); - done(); - }); - }); - - it('should also work prefixed commands', function (done) { - if (helper.redisProcess().spawnFailed()) this.skip(); - - client.end(true); - client = redis.createClient({ - rename_commands: { - set: '807081f5afa96845a02816a28b7258c3' - }, - prefix: 'baz' - }); - client.set('foo', 'bar'); - client.keys('*', function (err, reply) { - assert.strictEqual(reply[0], 'bazfoo'); - assert.strictEqual(err, null); - done(); - }); - }); - - }); - }); - - after(function (done) { - if (helper.redisProcess().spawnFailed()) return done(); - helper.stopRedis(function () { - helper.startRedis('./conf/redis.conf', done); - }); - }); -}); diff --git a/test/return_buffers.spec.js b/test/return_buffers.spec.js deleted file mode 100644 index 22efb31a04f..00000000000 --- a/test/return_buffers.spec.js +++ /dev/null @@ -1,297 +0,0 @@ -'use strict'; - -var assert = require('assert'); -var config = require('./lib/config'); -var helper = require('./helper'); -var redis = config.redis; - -describe('return_buffers', function () { - - helper.allTests(function (ip, basicArgs) { - - describe('using ' + ip, function () { - var client; - var args = config.configureClient(ip, { - return_buffers: true, - detect_buffers: true - }); - - beforeEach(function (done) { - client = redis.createClient.apply(null, args); - var i = 1; - if (args[2].detect_buffers) { - // Test if detect_buffer option was deactivated - assert.strictEqual(client.options.detect_buffers, false); - args[2].detect_buffers = false; - i++; - } - var end = helper.callFuncAfter(done, i); - client.on('warning', function (msg) { - assert.strictEqual(msg, 'WARNING: You activated return_buffers and detect_buffers at the same time. The return value is always going to be a buffer.'); - end(); - }); - client.once('error', done); - client.once('connect', function () { - client.flushdb(function (err) { - client.hmset('hash key 2', 'key 1', 'val 1', 'key 2', 'val 2'); - client.set('string key 1', 'string value'); - end(err); - }); - }); - }); - - afterEach(function () { - client.end(true); - }); - - describe('get', function () { - describe('first argument is a string', function () { - it('returns a buffer', function (done) { - client.get('string key 1', function (err, reply) { - assert.strictEqual(true, Buffer.isBuffer(reply)); - assert.strictEqual('', reply.inspect()); - return done(err); - }); - }); - - it('returns a bufffer when executed as part of transaction', function (done) { - client.multi().get('string key 1').exec(function (err, reply) { - assert.strictEqual(1, reply.length); - assert.strictEqual(true, Buffer.isBuffer(reply[0])); - assert.strictEqual('', reply[0].inspect()); - return done(err); - }); - }); - }); - }); - - describe('multi.hget', function () { - it('returns buffers', function (done) { - client.multi() - .hget('hash key 2', 'key 1') - .hget(Buffer.from('hash key 2'), 'key 1') - .hget('hash key 2', Buffer.from('key 2')) - .hget('hash key 2', 'key 2') - .exec(function (err, reply) { - assert.strictEqual(true, Array.isArray(reply)); - assert.strictEqual(4, reply.length); - assert.strictEqual('', reply[0].inspect()); - assert.strictEqual(true, Buffer.isBuffer(reply[1])); - assert.strictEqual('', reply[1].inspect()); - assert.strictEqual(true, Buffer.isBuffer(reply[2])); - assert.strictEqual('', reply[2].inspect()); - assert.strictEqual(true, Buffer.isBuffer(reply[3])); - assert.strictEqual('', reply[3].inspect()); - return done(err); - }); - }); - }); - - describe('batch.hget', function () { - it('returns buffers', function (done) { - client.batch() - .hget('hash key 2', 'key 1') - .hget(Buffer.from('hash key 2'), 'key 1') - .hget('hash key 2', Buffer.from('key 2')) - .hget('hash key 2', 'key 2') - .exec(function (err, reply) { - assert.strictEqual(true, Array.isArray(reply)); - assert.strictEqual(4, reply.length); - assert.strictEqual('', reply[0].inspect()); - assert.strictEqual(true, Buffer.isBuffer(reply[1])); - assert.strictEqual('', reply[1].inspect()); - assert.strictEqual(true, Buffer.isBuffer(reply[2])); - assert.strictEqual('', reply[2].inspect()); - assert.strictEqual(true, Buffer.isBuffer(reply[3])); - assert.strictEqual('', reply[3].inspect()); - return done(err); - }); - }); - }); - - describe('hmget', function () { - describe('first argument is a string', function () { - it('handles array of strings with undefined values in transaction (repro #344)', function (done) { - client.multi().hmget('hash key 2', 'key 3', 'key 4').exec(function (err, reply) { - assert.strictEqual(true, Array.isArray(reply)); - assert.strictEqual(1, reply.length); - assert.strictEqual(2, reply[0].length); - assert.equal(null, reply[0][0]); - assert.equal(null, reply[0][1]); - return done(err); - }); - }); - }); - - describe('first argument is a buffer', function () { - it('returns buffers for keys requested', function (done) { - client.hmget(Buffer.from('hash key 2'), 'key 1', 'key 2', function (err, reply) { - assert.strictEqual(true, Array.isArray(reply)); - assert.strictEqual(2, reply.length); - assert.strictEqual(true, Buffer.isBuffer(reply[0])); - assert.strictEqual(true, Buffer.isBuffer(reply[1])); - assert.strictEqual('', reply[0].inspect()); - assert.strictEqual('', reply[1].inspect()); - return done(err); - }); - }); - - it('returns buffers for keys requested in transaction', function (done) { - client.multi().hmget(Buffer.from('hash key 2'), 'key 1', 'key 2').exec(function (err, reply) { - assert.strictEqual(true, Array.isArray(reply)); - assert.strictEqual(1, reply.length); - assert.strictEqual(2, reply[0].length); - assert.strictEqual(true, Buffer.isBuffer(reply[0][0])); - assert.strictEqual(true, Buffer.isBuffer(reply[0][1])); - assert.strictEqual('', reply[0][0].inspect()); - assert.strictEqual('', reply[0][1].inspect()); - return done(err); - }); - }); - - it('returns buffers for keys requested in .batch', function (done) { - client.batch().hmget(Buffer.from('hash key 2'), 'key 1', 'key 2').exec(function (err, reply) { - assert.strictEqual(true, Array.isArray(reply)); - assert.strictEqual(1, reply.length); - assert.strictEqual(2, reply[0].length); - assert.strictEqual(true, Buffer.isBuffer(reply[0][0])); - assert.strictEqual(true, Buffer.isBuffer(reply[0][1])); - assert.strictEqual('', reply[0][0].inspect()); - assert.strictEqual('', reply[0][1].inspect()); - return done(err); - }); - }); - }); - }); - - describe('hgetall', function (done) { - describe('first argument is a string', function () { - it('returns buffer values', function (done) { - client.hgetall('hash key 2', function (err, reply) { - assert.strictEqual('object', typeof reply); - assert.strictEqual(2, Object.keys(reply).length); - assert.strictEqual('', reply['key 1'].inspect()); - assert.strictEqual('', reply['key 2'].inspect()); - return done(err); - }); - }); - - it('returns buffer values when executed in transaction', function (done) { - client.multi().hgetall('hash key 2').exec(function (err, reply) { - assert.strictEqual(1, reply.length); - assert.strictEqual('object', typeof reply[0]); - assert.strictEqual(2, Object.keys(reply[0]).length); - assert.strictEqual('', reply[0]['key 1'].inspect()); - assert.strictEqual('', reply[0]['key 2'].inspect()); - return done(err); - }); - }); - - it('returns buffer values when executed in .batch', function (done) { - client.batch().hgetall('hash key 2').exec(function (err, reply) { - assert.strictEqual(1, reply.length); - assert.strictEqual('object', typeof reply[0]); - assert.strictEqual(2, Object.keys(reply[0]).length); - assert.strictEqual('', reply[0]['key 1'].inspect()); - assert.strictEqual('', reply[0]['key 2'].inspect()); - return done(err); - }); - }); - }); - - describe('first argument is a buffer', function () { - it('returns buffer values', function (done) { - client.hgetall(Buffer.from('hash key 2'), function (err, reply) { - assert.strictEqual(null, err); - assert.strictEqual('object', typeof reply); - assert.strictEqual(2, Object.keys(reply).length); - assert.strictEqual(true, Buffer.isBuffer(reply['key 1'])); - assert.strictEqual(true, Buffer.isBuffer(reply['key 2'])); - assert.strictEqual('', reply['key 1'].inspect()); - assert.strictEqual('', reply['key 2'].inspect()); - return done(err); - }); - }); - - it('returns buffer values when executed in transaction', function (done) { - client.multi().hgetall(Buffer.from('hash key 2')).exec(function (err, reply) { - assert.strictEqual(1, reply.length); - assert.strictEqual('object', typeof reply[0]); - assert.strictEqual(2, Object.keys(reply[0]).length); - assert.strictEqual(true, Buffer.isBuffer(reply[0]['key 1'])); - assert.strictEqual(true, Buffer.isBuffer(reply[0]['key 2'])); - assert.strictEqual('', reply[0]['key 1'].inspect()); - assert.strictEqual('', reply[0]['key 2'].inspect()); - return done(err); - }); - }); - - it('returns buffer values when executed in .batch', function (done) { - client.batch().hgetall(Buffer.from('hash key 2')).exec(function (err, reply) { - assert.strictEqual(1, reply.length); - assert.strictEqual('object', typeof reply[0]); - assert.strictEqual(2, Object.keys(reply[0]).length); - assert.strictEqual(true, Buffer.isBuffer(reply[0]['key 1'])); - assert.strictEqual(true, Buffer.isBuffer(reply[0]['key 2'])); - assert.strictEqual('', reply[0]['key 1'].inspect()); - assert.strictEqual('', reply[0]['key 2'].inspect()); - return done(err); - }); - }); - }); - }); - - describe('publish/subscribe', function (done) { - var pub; - var sub; - var channel = 'test channel'; - var message = Buffer.from('test message'); - - var args = config.configureClient(ip, { - return_buffers: true - }); - - beforeEach(function (done) { - var pubConnected; - var subConnected; - - pub = redis.createClient.apply(redis.createClient, basicArgs); - sub = redis.createClient.apply(null, args); - pub.once('connect', function () { - pub.flushdb(function () { - pubConnected = true; - if (subConnected) { - done(); - } - }); - }); - sub.once('connect', function () { - subConnected = true; - if (pubConnected) { - done(); - } - }); - }); - - it('receives buffer messages', function (done) { - sub.on('subscribe', function (chnl, count) { - pub.publish(channel, message); - }); - - sub.on('message', function (chnl, msg) { - assert.strictEqual(true, Buffer.isBuffer(msg)); - assert.strictEqual('', msg.inspect()); - return done(); - }); - - sub.subscribe(channel); - }); - - afterEach(function () { - sub.end(true); - pub.end(true); - }); - }); - }); - }); -}); diff --git a/test/tls.spec.js b/test/tls.spec.js deleted file mode 100644 index 127a2cfb8de..00000000000 --- a/test/tls.spec.js +++ /dev/null @@ -1,151 +0,0 @@ -'use strict'; - -var assert = require('assert'); -var config = require('./lib/config'); -var fs = require('fs'); -var helper = require('./helper'); -var path = require('path'); -var redis = config.redis; -var utils = require('../lib/utils'); -var tls = require('tls'); - -var tls_options = { - servername: 'redis.js.org', - rejectUnauthorized: true, - ca: [ String(fs.readFileSync(path.resolve(__dirname, './conf/redis.js.org.cert'))) ] -}; - -var tls_port = 6380; -// Use skip instead of returning to indicate what tests really got skipped -var skip = false; - -describe('TLS connection tests', function () { - - before(function (done) { - // Print the warning when the tests run instead of while starting mocha - if (process.platform === 'win32') { - skip = true; - console.warn('\nStunnel tests do not work on windows atm. If you think you can fix that, it would be warmly welcome.\n'); - } - if (skip) return done(); - helper.stopStunnel(function () { - helper.startStunnel(done); - }); - }); - - after(function (done) { - if (skip) return done(); - helper.stopStunnel(done); - }); - - var client; - - afterEach(function () { - if (skip) return; - client.end(true); - }); - - describe('on lost connection', function () { - it('emit an error after max retry timeout and do not try to reconnect afterwards', function (done) { - if (skip) this.skip(); - var connect_timeout = 500; // in ms - client = redis.createClient({ - connect_timeout: connect_timeout, - port: tls_port, - tls: utils.clone(tls_options) - }); - var time = 0; - assert.strictEqual(client.address, '127.0.0.1:' + tls_port); - - client.once('ready', function () { - helper.killConnection(client); - }); - - client.on('reconnecting', function (params) { - time += params.delay; - }); - - client.on('error', function (err) { - if (/Redis connection in broken state: connection timeout.*?exceeded./.test(err.message)) { - process.nextTick(function () { - assert.strictEqual(time, connect_timeout); - assert.strictEqual(client.emitted_end, true); - assert.strictEqual(client.connected, false); - assert.strictEqual(client.ready, false); - assert.strictEqual(client.closing, true); - assert.strictEqual(time, connect_timeout); - done(); - }); - } - }); - }); - }); - - describe('when not connected', function () { - - it('connect with host and port provided in the tls object', function (done) { - if (skip) this.skip(); - var tls_opts = utils.clone(tls_options); - tls_opts.port = tls_port; - tls_opts.host = 'localhost'; - client = redis.createClient({ - connect_timeout: 1000, - tls: tls_opts - }); - - // verify connection is using TCP, not UNIX socket - assert.strictEqual(client.connection_options.host, 'localhost'); - assert.strictEqual(client.connection_options.port, tls_port); - assert.strictEqual(client.address, 'localhost:' + tls_port); - assert(client.stream.encrypted); - - client.set('foo', 'bar'); - client.get('foo', helper.isString('bar', done)); - }); - - describe('using rediss as url protocol', function () { - var tls_connect = tls.connect; - beforeEach(function () { - tls.connect = function (options) { - options = utils.clone(options); - options.ca = tls_options.ca; - options.servername = 'redis.js.org'; - options.rejectUnauthorized = true; - return tls_connect.call(tls, options); - }; - }); - afterEach(function () { - tls.connect = tls_connect; - }); - it('connect with tls when rediss is used as the protocol', function (done) { - if (skip) this.skip(); - client = redis.createClient('rediss://localhost:' + tls_port); - // verify connection is using TCP, not UNIX socket - assert(client.stream.encrypted); - client.set('foo', 'bar'); - client.get('foo', helper.isString('bar', done)); - }); - }); - - it('fails to connect because the cert is not correct', function (done) { - if (skip) this.skip(); - var faulty_cert = utils.clone(tls_options); - faulty_cert.ca = [ String(fs.readFileSync(path.resolve(__dirname, './conf/faulty.cert'))) ]; - client = redis.createClient({ - host: 'localhost', - connect_timeout: 1000, - port: tls_port, - tls: faulty_cert - }); - assert.strictEqual(client.address, 'localhost:' + tls_port); - client.on('error', function (err) { - assert(/DEPTH_ZERO_SELF_SIGNED_CERT/.test(err.code || err.message), err); - client.end(true); - }); - client.set('foo', 'bar', function (err, res) { - done(res); - }); - }); - - }); -}); diff --git a/test/unify_options.spec.js b/test/unify_options.spec.js deleted file mode 100644 index dcdd46d330b..00000000000 --- a/test/unify_options.spec.js +++ /dev/null @@ -1,241 +0,0 @@ -'use strict'; - -var assert = require('assert'); -var unifyOptions = require('../lib/createClient'); -var intercept = require('intercept-stdout'); - -describe('createClient options', function () { - - describe('port as first parameter', function () { - it('pass the options in the second parameter after a port', function () { - var options = unifyOptions(1234, { - option1: true, - option2: function () {} - }); - assert.strictEqual(Object.keys(options).length, 4); - assert(options.option1); - assert.strictEqual(options.port, 1234); - assert.strictEqual(options.host, undefined); - assert.strictEqual(typeof options.option2, 'function'); - }); - - it('pass the options in the third parameter after a port and host being set to null', function () { - var options = unifyOptions(1234, null, { - option1: true, - option2: function () {} - }); - assert.strictEqual(Object.keys(options).length, 4); - assert(options.option1); - assert.strictEqual(options.port, 1234); - assert.strictEqual(options.host, undefined); - assert.strictEqual(typeof options.option2, 'function'); - }); - - it('pass the options in the third parameter after a port and host being set to undefined', function () { - var options = unifyOptions(1234, undefined, { - option1: true, - option2: function () {} - }); - assert.strictEqual(Object.keys(options).length, 4); - assert(options.option1); - assert.strictEqual(options.port, 1234); - assert.strictEqual(options.host, undefined); - assert.strictEqual(typeof options.option2, 'function'); - }); - - it('pass the options in the third parameter after a port and host', function () { - var options = unifyOptions('1234', 'localhost', { - option1: true, - option2: function () {} - }); - assert.strictEqual(Object.keys(options).length, 4); - assert(options.option1); - assert.strictEqual(options.port, '1234'); - assert.strictEqual(options.host, 'localhost'); - assert.strictEqual(typeof options.option2, 'function'); - }); - - it('should throw with three parameters all set to a truthy value', function () { - try { - unifyOptions(1234, {}, {}); - throw new Error('failed'); - } catch (err) { - assert.strictEqual(err.message, 'Unknown type of connection in createClient()'); - } - }); - }); - - describe('unix socket as first parameter', function () { - it('pass the options in the second parameter after a port', function () { - var options = unifyOptions('/tmp/redis.sock', { - option1: true, - option2: function () {}, - option3: [1, 2, 3] - }); - assert.strictEqual(Object.keys(options).length, 4); - assert(options.option1); - assert.strictEqual(options.path, '/tmp/redis.sock'); - assert.strictEqual(typeof options.option2, 'function'); - assert.strictEqual(options.option3.length, 3); - }); - - it('pass the options in the third parameter after a port and host being set to null', function () { - var options = unifyOptions('/tmp/redis.sock', null, { - option1: true, - option2: function () {} - }); - assert.strictEqual(Object.keys(options).length, 3); - assert(options.option1); - assert.strictEqual(options.path, '/tmp/redis.sock'); - assert.strictEqual(typeof options.option2, 'function'); - }); - }); - - describe('redis url as first parameter', function () { - it('empty redis url including options as second parameter', function () { - var options = unifyOptions('redis://', { - option: [1, 2, 3] - }); - assert.strictEqual(Object.keys(options).length, 1); - assert.strictEqual(options.option.length, 3); - }); - - it('begin with two slashes including options as third parameter', function () { - var options = unifyOptions('//:abc@/3?port=123', { - option: [1, 2, 3] - }); - assert.strictEqual(Object.keys(options).length, 4); - assert.strictEqual(options.option.length, 3); - assert.strictEqual(options.port, '123'); - assert.strictEqual(options.db, '3'); - assert.strictEqual(options.password, 'abc'); - }); - - it('duplicated, identical query options including options obj', function () { - var text = ''; - var unhookIntercept = intercept(function (data) { - text += data; - return ''; - }); - var options = unifyOptions('//:abc@localhost:123/3?db=3&port=123&password=abc', null, { - option: [1, 2, 3] - }); - unhookIntercept(); - assert.strictEqual(text, - 'node_redis: WARNING: You passed the db option twice!\n' + - 'node_redis: WARNING: You passed the port option twice!\n' + - 'node_redis: WARNING: You passed the password option twice!\n' - ); - assert.strictEqual(Object.keys(options).length, 5); - assert.strictEqual(options.option.length, 3); - assert.strictEqual(options.host, 'localhost'); - assert.strictEqual(options.port, '123'); - assert.strictEqual(options.db, '3'); - assert.strictEqual(options.password, 'abc'); - }); - - it('should throw on duplicated, non-identical query options', function () { - try { - unifyOptions('//:abc@localhost:1234/3?port=123&password=abc'); - throw new Error('failed'); - } catch (err) { - assert.equal(err.message, 'The port option is added twice and does not match'); - } - }); - - it('should throw without protocol slashes', function () { - try { - unifyOptions('redis:abc@localhost:123/3?db=3&port=123&password=abc'); - throw new Error('failed'); - } catch (err) { - assert.equal(err.message, 'The redis url must begin with slashes "//" or contain slashes after the redis protocol'); - } - }); - - it('warns on protocol other than redis in the redis url', function () { - var text = ''; - var unhookIntercept = intercept(function (data) { - text += data; - return ''; - }); - var options = unifyOptions('http://abc'); - unhookIntercept(); - assert.strictEqual(Object.keys(options).length, 1); - assert.strictEqual(options.host, 'abc'); - assert.strictEqual(text, 'node_redis: WARNING: You passed "http" as protocol instead of the "redis" protocol!\n'); - }); - }); - - describe('no parameters or set to null / undefined', function () { - it('no parameters', function () { - var options = unifyOptions(); - assert.strictEqual(Object.keys(options).length, 1); - assert.strictEqual(options.host, undefined); - }); - - it('set to null', function () { - var options = unifyOptions(null, null); - assert.strictEqual(Object.keys(options).length, 1); - assert.strictEqual(options.host, null); - }); - - it('set to undefined', function () { - var options = unifyOptions(undefined, undefined); - assert.strictEqual(Object.keys(options).length, 1); - assert.strictEqual(options.host, undefined); - }); - }); - - describe('only an options object is passed', function () { - it('with options', function () { - var options = unifyOptions({ - option: true - }); - assert.strictEqual(Object.keys(options).length, 2); - assert.strictEqual(options.host, undefined); - assert.strictEqual(options.option, true); - }); - - it('without options', function () { - var options = unifyOptions({}); - assert.strictEqual(Object.keys(options).length, 1); - assert.strictEqual(options.host, undefined); - }); - - it('should throw with more parameters', function () { - try { - unifyOptions({ - option: true - }, undefined); - throw new Error('failed'); - } catch (err) { - assert.strictEqual(err.message, 'Too many arguments passed to createClient. Please only pass the options object'); - } - }); - - it('including url as option', function () { - var options = unifyOptions({ - option: [1, 2, 3], - url: '//hm:abc@localhost:123/3' - }); - assert.strictEqual(Object.keys(options).length, 7); - assert.strictEqual(options.option.length, 3); - assert.strictEqual(options.host, 'localhost'); - assert.strictEqual(options.port, '123'); - assert.strictEqual(options.db, '3'); - assert.strictEqual(options.url, '//hm:abc@localhost:123/3'); - assert.strictEqual(options.password, 'abc'); - }); - }); - - describe('faulty data', function () { - it('throws on strange connection info', function () { - try { - unifyOptions(true); - throw new Error('failed'); - } catch (err) { - assert.equal(err.message, 'Unknown type of connection in createClient()'); - } - }); - }); -}); diff --git a/test/utils.spec.js b/test/utils.spec.js deleted file mode 100644 index 592600fb6d8..00000000000 --- a/test/utils.spec.js +++ /dev/null @@ -1,185 +0,0 @@ -'use strict'; - -var assert = require('assert'); -var Queue = require('denque'); -var utils = require('../lib/utils'); -var intercept = require('intercept-stdout'); - -describe('utils.js', function () { - - describe('clone', function () { - it('ignore the object prototype and clone a nested array / object', function () { - var obj = { - a: [null, 'foo', ['bar'], { - "i'm special": true - }], - number: 5, - fn: function noop () {} - }; - var clone = utils.clone(obj); - assert.deepEqual(clone, obj); - assert.strictEqual(obj.fn, clone.fn); - assert(typeof clone.fn === 'function'); - }); - - it('replace falsy values with an empty object as return value', function () { - var a = utils.clone(); - var b = utils.clone(null); - assert.strictEqual(Object.keys(a).length, 0); - assert.strictEqual(Object.keys(b).length, 0); - }); - - it('transform camelCase options to snake_case and add the camel_case option', function () { - var a = utils.clone({ - optionOneTwo: true, - retryStrategy: false, - nested: { - onlyContainCamelCaseOnce: true - }, - tls: { - rejectUnauthorized: true - } - }); - assert.strictEqual(Object.keys(a).length, 5); - assert.strictEqual(a.option_one_two, true); - assert.strictEqual(a.retry_strategy, false); - assert.strictEqual(a.camel_case, true); - assert.strictEqual(a.tls.rejectUnauthorized, true); - assert.strictEqual(Object.keys(a.nested).length, 1); - }); - - it('throws on circular data', function () { - try { - var a = {}; - a.b = a; - utils.clone(a); - throw new Error('failed'); - } catch (e) { - assert(e.message !== 'failed'); - } - }); - }); - - describe('print helper', function () { - it('callback with reply', function () { - var text = ''; - var unhookIntercept = intercept(function (data) { - text += data; - return ''; - }); - utils.print(null, 'abc'); - unhookIntercept(); - assert.strictEqual(text, 'Reply: abc\n'); - }); - - it('callback with error', function () { - var text = ''; - var unhookIntercept = intercept(function (data) { - text += data; - return ''; - }); - utils.print(new Error('Wonderful exception')); - unhookIntercept(); - assert.strictEqual(text, 'Error: Wonderful exception\n'); - }); - }); - - describe('reply_in_order', function () { - - var err_count = 0; - var res_count = 0; - var emitted = false; - var clientMock = { - emit: function () { emitted = true; }, - offline_queue: new Queue(), - command_queue: new Queue() - }; - var create_command_obj = function () { - return { - callback: function (err, res) { - if (err) err_count++; - else res_count++; - } - }; - }; - - beforeEach(function () { - clientMock.offline_queue.clear(); - clientMock.command_queue.clear(); - err_count = 0; - res_count = 0; - emitted = false; - }); - - it('no elements in either queue. Reply in the next tick with callback', function (done) { - var called = false; - utils.reply_in_order(clientMock, function () { - called = true; - done(); - }, null, null); - assert(!called); - }); - - it('no elements in either queue. Reply in the next tick without callback', function (done) { - assert(!emitted); - utils.reply_in_order(clientMock, null, new Error('tada')); - assert(!emitted); - setTimeout(function () { - assert(emitted); - done(); - }, 1); - }); - - it('elements in the offline queue. Reply after the offline queue is empty and respect the command_obj callback', function (done) { - clientMock.offline_queue.push(create_command_obj()); - clientMock.offline_queue.push(create_command_obj()); - utils.reply_in_order(clientMock, function () { - assert.strictEqual(clientMock.offline_queue.length, 0); - assert.strictEqual(res_count, 2); - done(); - }, null, null); - while (clientMock.offline_queue.length) { - clientMock.offline_queue.shift().callback(null, 'foo'); - } - }); - - it('elements in the offline queue. Reply after the offline queue is empty and respect the command_obj error emit', function (done) { - clientMock.command_queue.push({}); - clientMock.command_queue.push(create_command_obj()); - clientMock.command_queue.push({}); - utils.reply_in_order(clientMock, function () { - assert.strictEqual(clientMock.command_queue.length, 0); - assert(emitted); - assert.strictEqual(err_count, 1); - assert.strictEqual(res_count, 0); - done(); - }, null, null); - while (clientMock.command_queue.length) { - var command_obj = clientMock.command_queue.shift(); - if (command_obj.callback) { - command_obj.callback(new Error('tada')); - } - } - }); - - it('elements in the offline queue and the command_queue. Reply all other commands got handled respect the command_obj', function (done) { - clientMock.command_queue.push(create_command_obj()); - clientMock.command_queue.push(create_command_obj()); - clientMock.command_queue.push(create_command_obj()); - clientMock.offline_queue.push({}); - utils.reply_in_order(clientMock, function (err, res) { - assert.strictEqual(clientMock.command_queue.length, 0); - assert.strictEqual(clientMock.offline_queue.length, 0); - assert(!emitted); - assert.strictEqual(res_count, 3); - done(); - }, null, null); - while (clientMock.offline_queue.length) { - clientMock.command_queue.push(clientMock.offline_queue.shift()); - } - while (clientMock.command_queue.length) { - clientMock.command_queue.shift().callback(null, 'hello world'); - } - }); - }); -}); diff --git a/tsconfig.json b/tsconfig.json new file mode 100644 index 00000000000..deebc9f1252 --- /dev/null +++ b/tsconfig.json @@ -0,0 +1,37 @@ +{ + "compilerOptions": { + "strict": true, + "target": "ES2019", + "lib": ["ES2019", "ES2020.BigInt", "ES2020.String", "ES2020.Symbol.WellKnown"], + "module": "CommonJS", + "moduleResolution": "Node", + "esModuleInterop": true, + "outDir": "./dist", + "declaration": true, + "useDefineForClassFields": true, + "allowJs": true + }, + "files": [ + "./lib/ts-declarations/cluster-key-slot.d.ts", + "./lib/ts-declarations/redis-parser.d.ts" + ], + "include": [ + "./index.ts", + "./lib/**/*.ts" + ], + "ts-node": { + "files": true + }, + "typedocOptions": { + "entryPoints": [ + "./index.ts", + "./lib" + ], + "exclude": [ + "./lib/ts-declarations", + "./lib/test-utils.ts" + ], + "theme": "./node_modules/typedoc-github-wiki-theme/dist", + "out": "documentation" + } +} From e9ebdbd1bb4a07dde9c33161880fc1e9e895bb51 Mon Sep 17 00:00:00 2001 From: Leibale Eidelman Date: Thu, 2 Sep 2021 10:24:51 -0400 Subject: [PATCH 0852/1748] Update package.json --- package.json | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/package.json b/package.json index be909a2197e..fd309d970f6 100644 --- a/package.json +++ b/package.json @@ -61,5 +61,5 @@ "bugs": { "url": "https://github.com/NodeRedis/node-redis/issues" }, - "homepage": "https://github.com/NodeRedis/node-redis/tree/v4" + "homepage": "https://github.com/NodeRedis/node-redis" } From 7983dd4c269997ff288de5a67896170be54997cc Mon Sep 17 00:00:00 2001 From: leibale Date: Thu, 2 Sep 2021 11:12:55 -0400 Subject: [PATCH 0853/1748] update workflows & README --- .github/workflows/benchmark.yml | 3 ++- .github/workflows/documentation.yml | 3 ++- .github/workflows/tests.yml | 3 ++- README.md | 4 ++-- 4 files changed, 8 insertions(+), 5 deletions(-) diff --git a/.github/workflows/benchmark.yml b/.github/workflows/benchmark.yml index b6e5802a914..2df438eb19c 100644 --- a/.github/workflows/benchmark.yml +++ b/.github/workflows/benchmark.yml @@ -3,7 +3,8 @@ name: Benchmark on: push: branches: - - v4 + - master + - v4.0 jobs: benchmark: diff --git a/.github/workflows/documentation.yml b/.github/workflows/documentation.yml index 16ca16b5608..9575d4639b9 100644 --- a/.github/workflows/documentation.yml +++ b/.github/workflows/documentation.yml @@ -3,7 +3,8 @@ name: Documentation on: push: branches: - - v4 + - master + - v4.0 jobs: documentation: diff --git a/.github/workflows/tests.yml b/.github/workflows/tests.yml index 028600f1a17..557d4f452dc 100644 --- a/.github/workflows/tests.yml +++ b/.github/workflows/tests.yml @@ -3,7 +3,8 @@ name: Tests on: push: branches: - - v4 + - master + - v4.0 jobs: tests: diff --git a/README.md b/README.md index acc229b69c2..db0fa71cc7f 100644 --- a/README.md +++ b/README.md @@ -6,8 +6,8 @@

- - Coverage Status + + Coverage Status Downloads From e421dc4bed75ae50eac927103b5254780e7d8994 Mon Sep 17 00:00:00 2001 From: leibale Date: Thu, 2 Sep 2021 11:20:57 -0400 Subject: [PATCH 0854/1748] add .deepsource.toml --- .deepsource.toml | 9 +++++++++ 1 file changed, 9 insertions(+) create mode 100644 .deepsource.toml diff --git a/.deepsource.toml b/.deepsource.toml new file mode 100644 index 00000000000..72aefc7b07a --- /dev/null +++ b/.deepsource.toml @@ -0,0 +1,9 @@ +version = 1 + +[[analyzers]] +name = "javascript" +enabled = true + + [analyzers.meta] + environment = ["nodejs"] + dialect = "typescript" From b80afc6346ab51a77123a5e3f59bd06524a0e02e Mon Sep 17 00:00:00 2001 From: leibale Date: Thu, 2 Sep 2021 14:00:52 -0400 Subject: [PATCH 0855/1748] fix client.quit, add error events on cluster, fix some "deepsource.io" warnings --- lib/client.ts | 5 +- lib/cluster-slots.ts | 43 ++++------- lib/cluster.ts | 7 +- lib/commander.ts | 4 +- lib/commands-queue.ts | 13 +++- lib/commands/MIGRATE.ts | 2 +- lib/commands/generic-transformers.spec.ts | 1 - package-lock.json | 94 +++++++++++------------ package.json | 2 +- 9 files changed, 86 insertions(+), 85 deletions(-) diff --git a/lib/client.ts b/lib/client.ts index a8da7f5ddd5..ed06317c14c 100644 --- a/lib/client.ts +++ b/lib/client.ts @@ -298,9 +298,10 @@ export default class RedisClient { - return this.#socket.quit(async () => { - this.#queue.addEncodedCommand(encodeCommand(['QUIT'])); + return this.#socket.quit(() => { + const promise = this.#queue.addEncodedCommand(encodeCommand(['QUIT'])); this.#tick(); + return promise; }); } diff --git a/lib/cluster-slots.ts b/lib/cluster-slots.ts index 3e255fc2a66..5fae5b92342 100644 --- a/lib/cluster-slots.ts +++ b/lib/cluster-slots.ts @@ -17,54 +17,40 @@ interface SlotNodes { clientIterator: IterableIterator> | undefined; } +type OnError = (err: unknown) => void; + export default class RedisClusterSlots { readonly #options: RedisClusterOptions; + readonly #onError: OnError; readonly #nodeByUrl = new Map>(); readonly #slots: Array> = []; - constructor(options: RedisClusterOptions) { + constructor(options: RedisClusterOptions, onError: OnError) { this.#options = options; + this.#onError = onError; } async connect(): Promise { for (const rootNode of this.#options.rootNodes) { - try { - await this.#discoverNodes(rootNode); - return; - } catch (err) { - console.error(err); - // this.emit('error', err); - } + if (await this.#discoverNodes(rootNode)) return; } throw new Error('None of the root nodes is available'); } async discover(startWith: RedisClientType): Promise { - try { - await this.#discoverNodes(startWith.options?.socket); - return; - } catch (err) { - console.error(err); - // this.emit('error', err); - } + if (await this.#discoverNodes(startWith.options?.socket)) return; for (const { client } of this.#nodeByUrl.values()) { if (client === startWith) continue; - - try { - await this.#discoverNodes(client.options?.socket); - return; - } catch (err) { - console.error(err); - // this.emit('error', err); - } + + if (await this.#discoverNodes(client.options?.socket)) return; } throw new Error('None of the cluster nodes is available'); } - async #discoverNodes(socketOptions?: RedisSocketOptions): Promise { + async #discoverNodes(socketOptions?: RedisSocketOptions): Promise { const client = RedisClient.create({ socket: socketOptions }); @@ -73,8 +59,14 @@ export default class RedisClusterSlots { rootNodes: Array; @@ -17,7 +18,7 @@ export interface RedisClusterOptions { export type RedisClusterType = WithPlugins & RedisCluster; -export default class RedisCluster { +export default class RedisCluster extends EventEmitter { static #extractFirstKey(command: RedisCommand, originalArgs: Array, redisArgs: Array): string | undefined { if (command.FIRST_KEY_INDEX === undefined) { return undefined; @@ -83,8 +84,10 @@ export default class RedisCluster) => RedisMultiCommandType; constructor(options: RedisClusterOptions) { + super(); + this.#options = options; - this.#slots = new RedisClusterSlots(options); + this.#slots = new RedisClusterSlots(options, err => this.emit('error', err)); this.#Multi = RedisMultiCommand.extend(options); } diff --git a/lib/commander.ts b/lib/commander.ts index 51adc417ba9..e8ff91cc7bf 100644 --- a/lib/commander.ts +++ b/lib/commander.ts @@ -97,12 +97,12 @@ export function transformCommandArguments( export function encodeCommand(args: Array): string { const encoded = [ `*${args.length}`, - `$${Buffer.byteLength(args[0])}`, + `$${Buffer.byteLength(args[0]).toString()}`, args[0] ]; for (let i = 1; i < args.length; i++) { - encoded.push(`$${Buffer.byteLength(args[i])}`, args[i]); + encoded.push(`$${Buffer.byteLength(args[i]).toString()}`, args[i]); } return encoded.join('\r\n') + '\r\n'; diff --git a/lib/commands-queue.ts b/lib/commands-queue.ts index 1890e0a00a9..cae3fd6130e 100644 --- a/lib/commands-queue.ts +++ b/lib/commands-queue.ts @@ -12,6 +12,7 @@ export interface QueueCommandOptions { interface CommandWaitingToBeSent extends CommandWaitingForReply { encodedCommand: string; + byteLength: number; chainId?: symbol; abort?: { signal: any; // TODO: `AbortSignal` type is incorrect @@ -130,6 +131,7 @@ export default class RedisCommandsQueue { return new Promise((resolve, reject) => { const node = new LinkedList.Node({ encodedCommand, + byteLength: Buffer.byteLength(encodedCommand), chainId: options?.chainId, resolve, reject @@ -156,7 +158,7 @@ export default class RedisCommandsQueue { this.#waitingToBeSent.pushNode(node); } - this.#waitingToBeSentCommandsLength += encodedCommand.length; + this.#waitingToBeSentCommandsLength += node.value.byteLength; }); } @@ -230,8 +232,12 @@ export default class RedisCommandsQueue { } this.#pubSubState[inProgressKey] += channelsCounter; + + const encodedCommand = encodeCommand(commandArgs), + byteLength = Buffer.byteLength(encodedCommand); this.#waitingToBeSent.push({ - encodedCommand: encodeCommand(commandArgs), + encodedCommand, + byteLength, channelsCounter, resolve: () => { this.#pubSubState[inProgressKey] -= channelsCounter; @@ -243,6 +249,7 @@ export default class RedisCommandsQueue { reject(); } }); + this.#waitingToBeSentCommandsLength += byteLength; }); } @@ -268,7 +275,7 @@ export default class RedisCommandsQueue { lastCommandChainId: symbol | undefined; for (const command of this.#waitingToBeSent) { encoded.push(command.encodedCommand); - size += command.encodedCommand.length; + size += command.byteLength; if (size > recommendedSize) { lastCommandChainId = command.chainId; break; diff --git a/lib/commands/MIGRATE.ts b/lib/commands/MIGRATE.ts index 1d2fc075efe..14dbe741be2 100644 --- a/lib/commands/MIGRATE.ts +++ b/lib/commands/MIGRATE.ts @@ -19,7 +19,7 @@ export function transformArguments( isKeyString = typeof key === 'string'; if (isKeyString) { - args.push(key as string); + args.push(key); } else { args.push('""'); } diff --git a/lib/commands/generic-transformers.spec.ts b/lib/commands/generic-transformers.spec.ts index 5335255f910..9ac72bb1b25 100644 --- a/lib/commands/generic-transformers.spec.ts +++ b/lib/commands/generic-transformers.spec.ts @@ -1,5 +1,4 @@ import { strict as assert } from 'assert'; -import { isObject } from 'util'; import { transformReplyBoolean, transformReplyBooleanArray, diff --git a/package-lock.json b/package-lock.json index 3b7397b61e4..cb1132ddb21 100644 --- a/package-lock.json +++ b/package-lock.json @@ -17,7 +17,7 @@ "devDependencies": { "@istanbuljs/nyc-config-typescript": "^1.0.1", "@types/mocha": "^9.0.0", - "@types/node": "^16.7.8", + "@types/node": "^16.7.10", "@types/sinon": "^10.0.2", "@types/which": "^2.0.1", "@types/yallist": "^4.0.1", @@ -642,9 +642,9 @@ } }, "node_modules/@octokit/graphql": { - "version": "4.7.0", - "resolved": "https://registry.npmjs.org/@octokit/graphql/-/graphql-4.7.0.tgz", - "integrity": "sha512-diY0qMPyQjfu4rDu3kDhJ9qIZadIm4IISO3RJSv9ajYUWJUCO0AykbgzLcg1xclxtXgzY583u3gAv66M6zz5SA==", + "version": "4.8.0", + "resolved": "https://registry.npmjs.org/@octokit/graphql/-/graphql-4.8.0.tgz", + "integrity": "sha512-0gv+qLSBLKF0z8TKaSKTsS39scVKF9dbMxJpj3U0vC7wjNWFuIpL/z76Qe2fiuCbDRcJSavkXsVtMS6/dtQQsg==", "dev": true, "dependencies": { "@octokit/request": "^5.6.0", @@ -653,18 +653,18 @@ } }, "node_modules/@octokit/openapi-types": { - "version": "9.7.0", - "resolved": "https://registry.npmjs.org/@octokit/openapi-types/-/openapi-types-9.7.0.tgz", - "integrity": "sha512-TUJ16DJU8mekne6+KVcMV5g6g/rJlrnIKn7aALG9QrNpnEipFc1xjoarh0PKaAWf2Hf+HwthRKYt+9mCm5RsRg==", + "version": "10.0.0", + "resolved": "https://registry.npmjs.org/@octokit/openapi-types/-/openapi-types-10.0.0.tgz", + "integrity": "sha512-k1iO2zKuEjjRS1EJb4FwSLk+iF6EGp+ZV0OMRViQoWhQ1fZTk9hg1xccZII5uyYoiqcbC73MRBmT45y1vp2PPg==", "dev": true }, "node_modules/@octokit/plugin-paginate-rest": { - "version": "2.15.1", - "resolved": "https://registry.npmjs.org/@octokit/plugin-paginate-rest/-/plugin-paginate-rest-2.15.1.tgz", - "integrity": "sha512-47r52KkhQDkmvUKZqXzA1lKvcyJEfYh3TKAIe5+EzMeyDM3d+/s5v11i2gTk8/n6No6DPi3k5Ind6wtDbo/AEg==", + "version": "2.16.0", + "resolved": "https://registry.npmjs.org/@octokit/plugin-paginate-rest/-/plugin-paginate-rest-2.16.0.tgz", + "integrity": "sha512-8YYzALPMvEZ35kgy5pdYvQ22Roz+BIuEaedO575GwE2vb/ACDqQn0xQrTJR4tnZCJn7pi8+AWPVjrFDaERIyXQ==", "dev": true, "dependencies": { - "@octokit/types": "^6.24.0" + "@octokit/types": "^6.26.0" }, "peerDependencies": { "@octokit/core": ">=2" @@ -730,12 +730,12 @@ } }, "node_modules/@octokit/types": { - "version": "6.25.0", - "resolved": "https://registry.npmjs.org/@octokit/types/-/types-6.25.0.tgz", - "integrity": "sha512-bNvyQKfngvAd/08COlYIN54nRgxskmejgywodizQNyiKoXmWRAjKup2/LYwm+T9V0gsKH6tuld1gM0PzmOiB4Q==", + "version": "6.26.0", + "resolved": "https://registry.npmjs.org/@octokit/types/-/types-6.26.0.tgz", + "integrity": "sha512-RDxZBAFMtqs1ZPnbUu1e7ohPNfoNhTiep4fErY7tZs995BeHu369Vsh5woMIaFbllRWEZBfvTCS4hvDnMPiHrA==", "dev": true, "dependencies": { - "@octokit/openapi-types": "^9.5.0" + "@octokit/openapi-types": "^10.0.0" } }, "node_modules/@sindresorhus/is": { @@ -855,9 +855,9 @@ "dev": true }, "node_modules/@types/node": { - "version": "16.7.8", - "resolved": "https://registry.npmjs.org/@types/node/-/node-16.7.8.tgz", - "integrity": "sha512-8upnoQU0OPzbIkm+ZMM0zCeFCkw2s3mS0IWdx0+AAaWqm4fkBb0UJp8Edl7FVKRamYbpJC/aVsHpKWBIbiC7Zg==", + "version": "16.7.10", + "resolved": "https://registry.npmjs.org/@types/node/-/node-16.7.10.tgz", + "integrity": "sha512-S63Dlv4zIPb8x6MMTgDq5WWRJQe56iBEY0O3SOFA9JrRienkOVDXSXBjjJw6HTNQYSE2JI6GMCR6LVbIMHJVvA==", "dev": true }, "node_modules/@types/parse-json": { @@ -1845,9 +1845,9 @@ "dev": true }, "node_modules/electron-to-chromium": { - "version": "1.3.822", - "resolved": "https://registry.npmjs.org/electron-to-chromium/-/electron-to-chromium-1.3.822.tgz", - "integrity": "sha512-k7jG5oYYHxF4jx6PcqwHX3JVME/OjzolqOZiIogi9xtsfsmTjTdie4x88OakYFPEa8euciTgCCzvVNwvmjHb1Q==", + "version": "1.3.827", + "resolved": "https://registry.npmjs.org/electron-to-chromium/-/electron-to-chromium-1.3.827.tgz", + "integrity": "sha512-ye+4uQOY/jbjRutMcE/EmOcNwUeo1qo9aKL2tPyb09cU3lmxNeyDF4RWiemmkknW+p29h7dyDqy02higTxc9/A==", "dev": true }, "node_modules/emoji-regex": { @@ -4773,9 +4773,9 @@ } }, "node_modules/shiki": { - "version": "0.9.8", - "resolved": "https://registry.npmjs.org/shiki/-/shiki-0.9.8.tgz", - "integrity": "sha512-499zQUTjcNTVwwiaPrWldUTXIV3T9HZWxDwE82bY+9GE7P2uD6hpHUTXNbTof3iOG6WT+/062+OMbl0lDoG8WQ==", + "version": "0.9.10", + "resolved": "https://registry.npmjs.org/shiki/-/shiki-0.9.10.tgz", + "integrity": "sha512-xeM7Oc6hY+6iW5O/T5hor8ul7mEprzyl5y4r5zthEHToQNw7MIhREMgU3r2gKDB0NaMLNrkcEQagudCdzE13Lg==", "dev": true, "dependencies": { "json5": "^2.2.0", @@ -6164,9 +6164,9 @@ } }, "@octokit/graphql": { - "version": "4.7.0", - "resolved": "https://registry.npmjs.org/@octokit/graphql/-/graphql-4.7.0.tgz", - "integrity": "sha512-diY0qMPyQjfu4rDu3kDhJ9qIZadIm4IISO3RJSv9ajYUWJUCO0AykbgzLcg1xclxtXgzY583u3gAv66M6zz5SA==", + "version": "4.8.0", + "resolved": "https://registry.npmjs.org/@octokit/graphql/-/graphql-4.8.0.tgz", + "integrity": "sha512-0gv+qLSBLKF0z8TKaSKTsS39scVKF9dbMxJpj3U0vC7wjNWFuIpL/z76Qe2fiuCbDRcJSavkXsVtMS6/dtQQsg==", "dev": true, "requires": { "@octokit/request": "^5.6.0", @@ -6175,18 +6175,18 @@ } }, "@octokit/openapi-types": { - "version": "9.7.0", - "resolved": "https://registry.npmjs.org/@octokit/openapi-types/-/openapi-types-9.7.0.tgz", - "integrity": "sha512-TUJ16DJU8mekne6+KVcMV5g6g/rJlrnIKn7aALG9QrNpnEipFc1xjoarh0PKaAWf2Hf+HwthRKYt+9mCm5RsRg==", + "version": "10.0.0", + "resolved": "https://registry.npmjs.org/@octokit/openapi-types/-/openapi-types-10.0.0.tgz", + "integrity": "sha512-k1iO2zKuEjjRS1EJb4FwSLk+iF6EGp+ZV0OMRViQoWhQ1fZTk9hg1xccZII5uyYoiqcbC73MRBmT45y1vp2PPg==", "dev": true }, "@octokit/plugin-paginate-rest": { - "version": "2.15.1", - "resolved": "https://registry.npmjs.org/@octokit/plugin-paginate-rest/-/plugin-paginate-rest-2.15.1.tgz", - "integrity": "sha512-47r52KkhQDkmvUKZqXzA1lKvcyJEfYh3TKAIe5+EzMeyDM3d+/s5v11i2gTk8/n6No6DPi3k5Ind6wtDbo/AEg==", + "version": "2.16.0", + "resolved": "https://registry.npmjs.org/@octokit/plugin-paginate-rest/-/plugin-paginate-rest-2.16.0.tgz", + "integrity": "sha512-8YYzALPMvEZ35kgy5pdYvQ22Roz+BIuEaedO575GwE2vb/ACDqQn0xQrTJR4tnZCJn7pi8+AWPVjrFDaERIyXQ==", "dev": true, "requires": { - "@octokit/types": "^6.24.0" + "@octokit/types": "^6.26.0" } }, "@octokit/plugin-request-log": { @@ -6244,12 +6244,12 @@ } }, "@octokit/types": { - "version": "6.25.0", - "resolved": "https://registry.npmjs.org/@octokit/types/-/types-6.25.0.tgz", - "integrity": "sha512-bNvyQKfngvAd/08COlYIN54nRgxskmejgywodizQNyiKoXmWRAjKup2/LYwm+T9V0gsKH6tuld1gM0PzmOiB4Q==", + "version": "6.26.0", + "resolved": "https://registry.npmjs.org/@octokit/types/-/types-6.26.0.tgz", + "integrity": "sha512-RDxZBAFMtqs1ZPnbUu1e7ohPNfoNhTiep4fErY7tZs995BeHu369Vsh5woMIaFbllRWEZBfvTCS4hvDnMPiHrA==", "dev": true, "requires": { - "@octokit/openapi-types": "^9.5.0" + "@octokit/openapi-types": "^10.0.0" } }, "@sindresorhus/is": { @@ -6360,9 +6360,9 @@ "dev": true }, "@types/node": { - "version": "16.7.8", - "resolved": "https://registry.npmjs.org/@types/node/-/node-16.7.8.tgz", - "integrity": "sha512-8upnoQU0OPzbIkm+ZMM0zCeFCkw2s3mS0IWdx0+AAaWqm4fkBb0UJp8Edl7FVKRamYbpJC/aVsHpKWBIbiC7Zg==", + "version": "16.7.10", + "resolved": "https://registry.npmjs.org/@types/node/-/node-16.7.10.tgz", + "integrity": "sha512-S63Dlv4zIPb8x6MMTgDq5WWRJQe56iBEY0O3SOFA9JrRienkOVDXSXBjjJw6HTNQYSE2JI6GMCR6LVbIMHJVvA==", "dev": true }, "@types/parse-json": { @@ -7108,9 +7108,9 @@ "dev": true }, "electron-to-chromium": { - "version": "1.3.822", - "resolved": "https://registry.npmjs.org/electron-to-chromium/-/electron-to-chromium-1.3.822.tgz", - "integrity": "sha512-k7jG5oYYHxF4jx6PcqwHX3JVME/OjzolqOZiIogi9xtsfsmTjTdie4x88OakYFPEa8euciTgCCzvVNwvmjHb1Q==", + "version": "1.3.827", + "resolved": "https://registry.npmjs.org/electron-to-chromium/-/electron-to-chromium-1.3.827.tgz", + "integrity": "sha512-ye+4uQOY/jbjRutMcE/EmOcNwUeo1qo9aKL2tPyb09cU3lmxNeyDF4RWiemmkknW+p29h7dyDqy02higTxc9/A==", "dev": true }, "emoji-regex": { @@ -9284,9 +9284,9 @@ } }, "shiki": { - "version": "0.9.8", - "resolved": "https://registry.npmjs.org/shiki/-/shiki-0.9.8.tgz", - "integrity": "sha512-499zQUTjcNTVwwiaPrWldUTXIV3T9HZWxDwE82bY+9GE7P2uD6hpHUTXNbTof3iOG6WT+/062+OMbl0lDoG8WQ==", + "version": "0.9.10", + "resolved": "https://registry.npmjs.org/shiki/-/shiki-0.9.10.tgz", + "integrity": "sha512-xeM7Oc6hY+6iW5O/T5hor8ul7mEprzyl5y4r5zthEHToQNw7MIhREMgU3r2gKDB0NaMLNrkcEQagudCdzE13Lg==", "dev": true, "requires": { "json5": "^2.2.0", diff --git a/package.json b/package.json index fd309d970f6..32cf674a4d2 100644 --- a/package.json +++ b/package.json @@ -35,7 +35,7 @@ "devDependencies": { "@istanbuljs/nyc-config-typescript": "^1.0.1", "@types/mocha": "^9.0.0", - "@types/node": "^16.7.8", + "@types/node": "^16.7.10", "@types/sinon": "^10.0.2", "@types/which": "^2.0.1", "@types/yallist": "^4.0.1", From 18ad329ccc2950b5481109367594d72ecaeecf27 Mon Sep 17 00:00:00 2001 From: leibale Date: Mon, 6 Sep 2021 15:59:52 -0400 Subject: [PATCH 0856/1748] Release 4.0.0-rc.1 --- package-lock.json | 4 ++-- package.json | 2 +- 2 files changed, 3 insertions(+), 3 deletions(-) diff --git a/package-lock.json b/package-lock.json index cb1132ddb21..ac623c60e6a 100644 --- a/package-lock.json +++ b/package-lock.json @@ -1,12 +1,12 @@ { "name": "redis", - "version": "4.0.0-rc.0", + "version": "4.0.0-rc.1", "lockfileVersion": 2, "requires": true, "packages": { "": { "name": "redis", - "version": "4.0.0-rc.0", + "version": "4.0.0-rc.1", "license": "MIT", "dependencies": { "cluster-key-slot": "1.1.0", diff --git a/package.json b/package.json index 32cf674a4d2..56a7ed38c65 100644 --- a/package.json +++ b/package.json @@ -1,6 +1,6 @@ { "name": "redis", - "version": "4.0.0-rc.0", + "version": "4.0.0-rc.1", "description": "A high performance Redis client.", "keywords": [ "database", From 77664c31ffcc3f3248860b1e7bd8f8daec9246c3 Mon Sep 17 00:00:00 2001 From: Leibale Eidelman Date: Mon, 6 Sep 2021 16:02:53 -0400 Subject: [PATCH 0857/1748] Release 4.0.0-rc.1 (#1648) * 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 --- .deepsource.toml | 9 +++ .github/workflows/benchmark.yml | 3 +- .github/workflows/documentation.yml | 3 +- .github/workflows/tests.yml | 3 +- README.md | 4 +- lib/client.ts | 5 +- lib/cluster-slots.ts | 43 ++++------ lib/cluster.ts | 7 +- lib/commander.ts | 4 +- lib/commands-queue.ts | 13 ++- lib/commands/MIGRATE.ts | 2 +- lib/commands/generic-transformers.spec.ts | 1 - package-lock.json | 98 +++++++++++------------ package.json | 4 +- 14 files changed, 106 insertions(+), 93 deletions(-) create mode 100644 .deepsource.toml diff --git a/.deepsource.toml b/.deepsource.toml new file mode 100644 index 00000000000..72aefc7b07a --- /dev/null +++ b/.deepsource.toml @@ -0,0 +1,9 @@ +version = 1 + +[[analyzers]] +name = "javascript" +enabled = true + + [analyzers.meta] + environment = ["nodejs"] + dialect = "typescript" diff --git a/.github/workflows/benchmark.yml b/.github/workflows/benchmark.yml index b6e5802a914..2df438eb19c 100644 --- a/.github/workflows/benchmark.yml +++ b/.github/workflows/benchmark.yml @@ -3,7 +3,8 @@ name: Benchmark on: push: branches: - - v4 + - master + - v4.0 jobs: benchmark: diff --git a/.github/workflows/documentation.yml b/.github/workflows/documentation.yml index 16ca16b5608..9575d4639b9 100644 --- a/.github/workflows/documentation.yml +++ b/.github/workflows/documentation.yml @@ -3,7 +3,8 @@ name: Documentation on: push: branches: - - v4 + - master + - v4.0 jobs: documentation: diff --git a/.github/workflows/tests.yml b/.github/workflows/tests.yml index 028600f1a17..557d4f452dc 100644 --- a/.github/workflows/tests.yml +++ b/.github/workflows/tests.yml @@ -3,7 +3,8 @@ name: Tests on: push: branches: - - v4 + - master + - v4.0 jobs: tests: diff --git a/README.md b/README.md index acc229b69c2..db0fa71cc7f 100644 --- a/README.md +++ b/README.md @@ -6,8 +6,8 @@

- - Coverage Status + + Coverage Status Downloads diff --git a/lib/client.ts b/lib/client.ts index a8da7f5ddd5..ed06317c14c 100644 --- a/lib/client.ts +++ b/lib/client.ts @@ -298,9 +298,10 @@ export default class RedisClient { - return this.#socket.quit(async () => { - this.#queue.addEncodedCommand(encodeCommand(['QUIT'])); + return this.#socket.quit(() => { + const promise = this.#queue.addEncodedCommand(encodeCommand(['QUIT'])); this.#tick(); + return promise; }); } diff --git a/lib/cluster-slots.ts b/lib/cluster-slots.ts index 3e255fc2a66..5fae5b92342 100644 --- a/lib/cluster-slots.ts +++ b/lib/cluster-slots.ts @@ -17,54 +17,40 @@ interface SlotNodes { clientIterator: IterableIterator> | undefined; } +type OnError = (err: unknown) => void; + export default class RedisClusterSlots { readonly #options: RedisClusterOptions; + readonly #onError: OnError; readonly #nodeByUrl = new Map>(); readonly #slots: Array> = []; - constructor(options: RedisClusterOptions) { + constructor(options: RedisClusterOptions, onError: OnError) { this.#options = options; + this.#onError = onError; } async connect(): Promise { for (const rootNode of this.#options.rootNodes) { - try { - await this.#discoverNodes(rootNode); - return; - } catch (err) { - console.error(err); - // this.emit('error', err); - } + if (await this.#discoverNodes(rootNode)) return; } throw new Error('None of the root nodes is available'); } async discover(startWith: RedisClientType): Promise { - try { - await this.#discoverNodes(startWith.options?.socket); - return; - } catch (err) { - console.error(err); - // this.emit('error', err); - } + if (await this.#discoverNodes(startWith.options?.socket)) return; for (const { client } of this.#nodeByUrl.values()) { if (client === startWith) continue; - - try { - await this.#discoverNodes(client.options?.socket); - return; - } catch (err) { - console.error(err); - // this.emit('error', err); - } + + if (await this.#discoverNodes(client.options?.socket)) return; } throw new Error('None of the cluster nodes is available'); } - async #discoverNodes(socketOptions?: RedisSocketOptions): Promise { + async #discoverNodes(socketOptions?: RedisSocketOptions): Promise { const client = RedisClient.create({ socket: socketOptions }); @@ -73,8 +59,14 @@ export default class RedisClusterSlots { rootNodes: Array; @@ -17,7 +18,7 @@ export interface RedisClusterOptions { export type RedisClusterType = WithPlugins & RedisCluster; -export default class RedisCluster { +export default class RedisCluster extends EventEmitter { static #extractFirstKey(command: RedisCommand, originalArgs: Array, redisArgs: Array): string | undefined { if (command.FIRST_KEY_INDEX === undefined) { return undefined; @@ -83,8 +84,10 @@ export default class RedisCluster) => RedisMultiCommandType; constructor(options: RedisClusterOptions) { + super(); + this.#options = options; - this.#slots = new RedisClusterSlots(options); + this.#slots = new RedisClusterSlots(options, err => this.emit('error', err)); this.#Multi = RedisMultiCommand.extend(options); } diff --git a/lib/commander.ts b/lib/commander.ts index 51adc417ba9..e8ff91cc7bf 100644 --- a/lib/commander.ts +++ b/lib/commander.ts @@ -97,12 +97,12 @@ export function transformCommandArguments( export function encodeCommand(args: Array): string { const encoded = [ `*${args.length}`, - `$${Buffer.byteLength(args[0])}`, + `$${Buffer.byteLength(args[0]).toString()}`, args[0] ]; for (let i = 1; i < args.length; i++) { - encoded.push(`$${Buffer.byteLength(args[i])}`, args[i]); + encoded.push(`$${Buffer.byteLength(args[i]).toString()}`, args[i]); } return encoded.join('\r\n') + '\r\n'; diff --git a/lib/commands-queue.ts b/lib/commands-queue.ts index 1890e0a00a9..cae3fd6130e 100644 --- a/lib/commands-queue.ts +++ b/lib/commands-queue.ts @@ -12,6 +12,7 @@ export interface QueueCommandOptions { interface CommandWaitingToBeSent extends CommandWaitingForReply { encodedCommand: string; + byteLength: number; chainId?: symbol; abort?: { signal: any; // TODO: `AbortSignal` type is incorrect @@ -130,6 +131,7 @@ export default class RedisCommandsQueue { return new Promise((resolve, reject) => { const node = new LinkedList.Node({ encodedCommand, + byteLength: Buffer.byteLength(encodedCommand), chainId: options?.chainId, resolve, reject @@ -156,7 +158,7 @@ export default class RedisCommandsQueue { this.#waitingToBeSent.pushNode(node); } - this.#waitingToBeSentCommandsLength += encodedCommand.length; + this.#waitingToBeSentCommandsLength += node.value.byteLength; }); } @@ -230,8 +232,12 @@ export default class RedisCommandsQueue { } this.#pubSubState[inProgressKey] += channelsCounter; + + const encodedCommand = encodeCommand(commandArgs), + byteLength = Buffer.byteLength(encodedCommand); this.#waitingToBeSent.push({ - encodedCommand: encodeCommand(commandArgs), + encodedCommand, + byteLength, channelsCounter, resolve: () => { this.#pubSubState[inProgressKey] -= channelsCounter; @@ -243,6 +249,7 @@ export default class RedisCommandsQueue { reject(); } }); + this.#waitingToBeSentCommandsLength += byteLength; }); } @@ -268,7 +275,7 @@ export default class RedisCommandsQueue { lastCommandChainId: symbol | undefined; for (const command of this.#waitingToBeSent) { encoded.push(command.encodedCommand); - size += command.encodedCommand.length; + size += command.byteLength; if (size > recommendedSize) { lastCommandChainId = command.chainId; break; diff --git a/lib/commands/MIGRATE.ts b/lib/commands/MIGRATE.ts index 1d2fc075efe..14dbe741be2 100644 --- a/lib/commands/MIGRATE.ts +++ b/lib/commands/MIGRATE.ts @@ -19,7 +19,7 @@ export function transformArguments( isKeyString = typeof key === 'string'; if (isKeyString) { - args.push(key as string); + args.push(key); } else { args.push('""'); } diff --git a/lib/commands/generic-transformers.spec.ts b/lib/commands/generic-transformers.spec.ts index 5335255f910..9ac72bb1b25 100644 --- a/lib/commands/generic-transformers.spec.ts +++ b/lib/commands/generic-transformers.spec.ts @@ -1,5 +1,4 @@ import { strict as assert } from 'assert'; -import { isObject } from 'util'; import { transformReplyBoolean, transformReplyBooleanArray, diff --git a/package-lock.json b/package-lock.json index 3b7397b61e4..ac623c60e6a 100644 --- a/package-lock.json +++ b/package-lock.json @@ -1,12 +1,12 @@ { "name": "redis", - "version": "4.0.0-rc.0", + "version": "4.0.0-rc.1", "lockfileVersion": 2, "requires": true, "packages": { "": { "name": "redis", - "version": "4.0.0-rc.0", + "version": "4.0.0-rc.1", "license": "MIT", "dependencies": { "cluster-key-slot": "1.1.0", @@ -17,7 +17,7 @@ "devDependencies": { "@istanbuljs/nyc-config-typescript": "^1.0.1", "@types/mocha": "^9.0.0", - "@types/node": "^16.7.8", + "@types/node": "^16.7.10", "@types/sinon": "^10.0.2", "@types/which": "^2.0.1", "@types/yallist": "^4.0.1", @@ -642,9 +642,9 @@ } }, "node_modules/@octokit/graphql": { - "version": "4.7.0", - "resolved": "https://registry.npmjs.org/@octokit/graphql/-/graphql-4.7.0.tgz", - "integrity": "sha512-diY0qMPyQjfu4rDu3kDhJ9qIZadIm4IISO3RJSv9ajYUWJUCO0AykbgzLcg1xclxtXgzY583u3gAv66M6zz5SA==", + "version": "4.8.0", + "resolved": "https://registry.npmjs.org/@octokit/graphql/-/graphql-4.8.0.tgz", + "integrity": "sha512-0gv+qLSBLKF0z8TKaSKTsS39scVKF9dbMxJpj3U0vC7wjNWFuIpL/z76Qe2fiuCbDRcJSavkXsVtMS6/dtQQsg==", "dev": true, "dependencies": { "@octokit/request": "^5.6.0", @@ -653,18 +653,18 @@ } }, "node_modules/@octokit/openapi-types": { - "version": "9.7.0", - "resolved": "https://registry.npmjs.org/@octokit/openapi-types/-/openapi-types-9.7.0.tgz", - "integrity": "sha512-TUJ16DJU8mekne6+KVcMV5g6g/rJlrnIKn7aALG9QrNpnEipFc1xjoarh0PKaAWf2Hf+HwthRKYt+9mCm5RsRg==", + "version": "10.0.0", + "resolved": "https://registry.npmjs.org/@octokit/openapi-types/-/openapi-types-10.0.0.tgz", + "integrity": "sha512-k1iO2zKuEjjRS1EJb4FwSLk+iF6EGp+ZV0OMRViQoWhQ1fZTk9hg1xccZII5uyYoiqcbC73MRBmT45y1vp2PPg==", "dev": true }, "node_modules/@octokit/plugin-paginate-rest": { - "version": "2.15.1", - "resolved": "https://registry.npmjs.org/@octokit/plugin-paginate-rest/-/plugin-paginate-rest-2.15.1.tgz", - "integrity": "sha512-47r52KkhQDkmvUKZqXzA1lKvcyJEfYh3TKAIe5+EzMeyDM3d+/s5v11i2gTk8/n6No6DPi3k5Ind6wtDbo/AEg==", + "version": "2.16.0", + "resolved": "https://registry.npmjs.org/@octokit/plugin-paginate-rest/-/plugin-paginate-rest-2.16.0.tgz", + "integrity": "sha512-8YYzALPMvEZ35kgy5pdYvQ22Roz+BIuEaedO575GwE2vb/ACDqQn0xQrTJR4tnZCJn7pi8+AWPVjrFDaERIyXQ==", "dev": true, "dependencies": { - "@octokit/types": "^6.24.0" + "@octokit/types": "^6.26.0" }, "peerDependencies": { "@octokit/core": ">=2" @@ -730,12 +730,12 @@ } }, "node_modules/@octokit/types": { - "version": "6.25.0", - "resolved": "https://registry.npmjs.org/@octokit/types/-/types-6.25.0.tgz", - "integrity": "sha512-bNvyQKfngvAd/08COlYIN54nRgxskmejgywodizQNyiKoXmWRAjKup2/LYwm+T9V0gsKH6tuld1gM0PzmOiB4Q==", + "version": "6.26.0", + "resolved": "https://registry.npmjs.org/@octokit/types/-/types-6.26.0.tgz", + "integrity": "sha512-RDxZBAFMtqs1ZPnbUu1e7ohPNfoNhTiep4fErY7tZs995BeHu369Vsh5woMIaFbllRWEZBfvTCS4hvDnMPiHrA==", "dev": true, "dependencies": { - "@octokit/openapi-types": "^9.5.0" + "@octokit/openapi-types": "^10.0.0" } }, "node_modules/@sindresorhus/is": { @@ -855,9 +855,9 @@ "dev": true }, "node_modules/@types/node": { - "version": "16.7.8", - "resolved": "https://registry.npmjs.org/@types/node/-/node-16.7.8.tgz", - "integrity": "sha512-8upnoQU0OPzbIkm+ZMM0zCeFCkw2s3mS0IWdx0+AAaWqm4fkBb0UJp8Edl7FVKRamYbpJC/aVsHpKWBIbiC7Zg==", + "version": "16.7.10", + "resolved": "https://registry.npmjs.org/@types/node/-/node-16.7.10.tgz", + "integrity": "sha512-S63Dlv4zIPb8x6MMTgDq5WWRJQe56iBEY0O3SOFA9JrRienkOVDXSXBjjJw6HTNQYSE2JI6GMCR6LVbIMHJVvA==", "dev": true }, "node_modules/@types/parse-json": { @@ -1845,9 +1845,9 @@ "dev": true }, "node_modules/electron-to-chromium": { - "version": "1.3.822", - "resolved": "https://registry.npmjs.org/electron-to-chromium/-/electron-to-chromium-1.3.822.tgz", - "integrity": "sha512-k7jG5oYYHxF4jx6PcqwHX3JVME/OjzolqOZiIogi9xtsfsmTjTdie4x88OakYFPEa8euciTgCCzvVNwvmjHb1Q==", + "version": "1.3.827", + "resolved": "https://registry.npmjs.org/electron-to-chromium/-/electron-to-chromium-1.3.827.tgz", + "integrity": "sha512-ye+4uQOY/jbjRutMcE/EmOcNwUeo1qo9aKL2tPyb09cU3lmxNeyDF4RWiemmkknW+p29h7dyDqy02higTxc9/A==", "dev": true }, "node_modules/emoji-regex": { @@ -4773,9 +4773,9 @@ } }, "node_modules/shiki": { - "version": "0.9.8", - "resolved": "https://registry.npmjs.org/shiki/-/shiki-0.9.8.tgz", - "integrity": "sha512-499zQUTjcNTVwwiaPrWldUTXIV3T9HZWxDwE82bY+9GE7P2uD6hpHUTXNbTof3iOG6WT+/062+OMbl0lDoG8WQ==", + "version": "0.9.10", + "resolved": "https://registry.npmjs.org/shiki/-/shiki-0.9.10.tgz", + "integrity": "sha512-xeM7Oc6hY+6iW5O/T5hor8ul7mEprzyl5y4r5zthEHToQNw7MIhREMgU3r2gKDB0NaMLNrkcEQagudCdzE13Lg==", "dev": true, "dependencies": { "json5": "^2.2.0", @@ -6164,9 +6164,9 @@ } }, "@octokit/graphql": { - "version": "4.7.0", - "resolved": "https://registry.npmjs.org/@octokit/graphql/-/graphql-4.7.0.tgz", - "integrity": "sha512-diY0qMPyQjfu4rDu3kDhJ9qIZadIm4IISO3RJSv9ajYUWJUCO0AykbgzLcg1xclxtXgzY583u3gAv66M6zz5SA==", + "version": "4.8.0", + "resolved": "https://registry.npmjs.org/@octokit/graphql/-/graphql-4.8.0.tgz", + "integrity": "sha512-0gv+qLSBLKF0z8TKaSKTsS39scVKF9dbMxJpj3U0vC7wjNWFuIpL/z76Qe2fiuCbDRcJSavkXsVtMS6/dtQQsg==", "dev": true, "requires": { "@octokit/request": "^5.6.0", @@ -6175,18 +6175,18 @@ } }, "@octokit/openapi-types": { - "version": "9.7.0", - "resolved": "https://registry.npmjs.org/@octokit/openapi-types/-/openapi-types-9.7.0.tgz", - "integrity": "sha512-TUJ16DJU8mekne6+KVcMV5g6g/rJlrnIKn7aALG9QrNpnEipFc1xjoarh0PKaAWf2Hf+HwthRKYt+9mCm5RsRg==", + "version": "10.0.0", + "resolved": "https://registry.npmjs.org/@octokit/openapi-types/-/openapi-types-10.0.0.tgz", + "integrity": "sha512-k1iO2zKuEjjRS1EJb4FwSLk+iF6EGp+ZV0OMRViQoWhQ1fZTk9hg1xccZII5uyYoiqcbC73MRBmT45y1vp2PPg==", "dev": true }, "@octokit/plugin-paginate-rest": { - "version": "2.15.1", - "resolved": "https://registry.npmjs.org/@octokit/plugin-paginate-rest/-/plugin-paginate-rest-2.15.1.tgz", - "integrity": "sha512-47r52KkhQDkmvUKZqXzA1lKvcyJEfYh3TKAIe5+EzMeyDM3d+/s5v11i2gTk8/n6No6DPi3k5Ind6wtDbo/AEg==", + "version": "2.16.0", + "resolved": "https://registry.npmjs.org/@octokit/plugin-paginate-rest/-/plugin-paginate-rest-2.16.0.tgz", + "integrity": "sha512-8YYzALPMvEZ35kgy5pdYvQ22Roz+BIuEaedO575GwE2vb/ACDqQn0xQrTJR4tnZCJn7pi8+AWPVjrFDaERIyXQ==", "dev": true, "requires": { - "@octokit/types": "^6.24.0" + "@octokit/types": "^6.26.0" } }, "@octokit/plugin-request-log": { @@ -6244,12 +6244,12 @@ } }, "@octokit/types": { - "version": "6.25.0", - "resolved": "https://registry.npmjs.org/@octokit/types/-/types-6.25.0.tgz", - "integrity": "sha512-bNvyQKfngvAd/08COlYIN54nRgxskmejgywodizQNyiKoXmWRAjKup2/LYwm+T9V0gsKH6tuld1gM0PzmOiB4Q==", + "version": "6.26.0", + "resolved": "https://registry.npmjs.org/@octokit/types/-/types-6.26.0.tgz", + "integrity": "sha512-RDxZBAFMtqs1ZPnbUu1e7ohPNfoNhTiep4fErY7tZs995BeHu369Vsh5woMIaFbllRWEZBfvTCS4hvDnMPiHrA==", "dev": true, "requires": { - "@octokit/openapi-types": "^9.5.0" + "@octokit/openapi-types": "^10.0.0" } }, "@sindresorhus/is": { @@ -6360,9 +6360,9 @@ "dev": true }, "@types/node": { - "version": "16.7.8", - "resolved": "https://registry.npmjs.org/@types/node/-/node-16.7.8.tgz", - "integrity": "sha512-8upnoQU0OPzbIkm+ZMM0zCeFCkw2s3mS0IWdx0+AAaWqm4fkBb0UJp8Edl7FVKRamYbpJC/aVsHpKWBIbiC7Zg==", + "version": "16.7.10", + "resolved": "https://registry.npmjs.org/@types/node/-/node-16.7.10.tgz", + "integrity": "sha512-S63Dlv4zIPb8x6MMTgDq5WWRJQe56iBEY0O3SOFA9JrRienkOVDXSXBjjJw6HTNQYSE2JI6GMCR6LVbIMHJVvA==", "dev": true }, "@types/parse-json": { @@ -7108,9 +7108,9 @@ "dev": true }, "electron-to-chromium": { - "version": "1.3.822", - "resolved": "https://registry.npmjs.org/electron-to-chromium/-/electron-to-chromium-1.3.822.tgz", - "integrity": "sha512-k7jG5oYYHxF4jx6PcqwHX3JVME/OjzolqOZiIogi9xtsfsmTjTdie4x88OakYFPEa8euciTgCCzvVNwvmjHb1Q==", + "version": "1.3.827", + "resolved": "https://registry.npmjs.org/electron-to-chromium/-/electron-to-chromium-1.3.827.tgz", + "integrity": "sha512-ye+4uQOY/jbjRutMcE/EmOcNwUeo1qo9aKL2tPyb09cU3lmxNeyDF4RWiemmkknW+p29h7dyDqy02higTxc9/A==", "dev": true }, "emoji-regex": { @@ -9284,9 +9284,9 @@ } }, "shiki": { - "version": "0.9.8", - "resolved": "https://registry.npmjs.org/shiki/-/shiki-0.9.8.tgz", - "integrity": "sha512-499zQUTjcNTVwwiaPrWldUTXIV3T9HZWxDwE82bY+9GE7P2uD6hpHUTXNbTof3iOG6WT+/062+OMbl0lDoG8WQ==", + "version": "0.9.10", + "resolved": "https://registry.npmjs.org/shiki/-/shiki-0.9.10.tgz", + "integrity": "sha512-xeM7Oc6hY+6iW5O/T5hor8ul7mEprzyl5y4r5zthEHToQNw7MIhREMgU3r2gKDB0NaMLNrkcEQagudCdzE13Lg==", "dev": true, "requires": { "json5": "^2.2.0", diff --git a/package.json b/package.json index fd309d970f6..56a7ed38c65 100644 --- a/package.json +++ b/package.json @@ -1,6 +1,6 @@ { "name": "redis", - "version": "4.0.0-rc.0", + "version": "4.0.0-rc.1", "description": "A high performance Redis client.", "keywords": [ "database", @@ -35,7 +35,7 @@ "devDependencies": { "@istanbuljs/nyc-config-typescript": "^1.0.1", "@types/mocha": "^9.0.0", - "@types/node": "^16.7.8", + "@types/node": "^16.7.10", "@types/sinon": "^10.0.2", "@types/which": "^2.0.1", "@types/yallist": "^4.0.1", From 1413a69a6b75253b606ffd211f7f119ec5337894 Mon Sep 17 00:00:00 2001 From: leibale Date: Thu, 9 Sep 2021 16:58:31 -0400 Subject: [PATCH 0858/1748] add cluster.duplicate, add some tests --- lib/client.ts | 2 +- lib/cluster.ts | 4 +++ lib/commands/GEOPOS.spec.ts | 50 +++++++++++++++++++++++++---- lib/commands/GEOSEARCHSTORE.spec.ts | 9 +++++- lib/commands/PUBSUB_NUMSUB.spec.ts | 2 +- 5 files changed, 58 insertions(+), 9 deletions(-) diff --git a/lib/client.ts b/lib/client.ts index ed06317c14c..139ec647fc3 100644 --- a/lib/client.ts +++ b/lib/client.ts @@ -184,7 +184,7 @@ export default class RedisClient this.#socket.write(encodedCommands) + encodedCommands => this.#socket.write(encodedCommands) ); } diff --git a/lib/cluster.ts b/lib/cluster.ts index 2c1b23465ee..3eeaed5009f 100644 --- a/lib/cluster.ts +++ b/lib/cluster.ts @@ -91,6 +91,10 @@ export default class RedisCluster { + return new (Object.getPrototypeOf(this).constructor)(this.#options); + } + async connect(): Promise { return this.#slots.connect(); } diff --git a/lib/commands/GEOPOS.spec.ts b/lib/commands/GEOPOS.spec.ts index 98cfa6aa2d3..e15abeff516 100644 --- a/lib/commands/GEOPOS.spec.ts +++ b/lib/commands/GEOPOS.spec.ts @@ -1,6 +1,6 @@ import { strict as assert } from 'assert'; import { TestRedisServers, itWithClient, TestRedisClusters, itWithCluster } from '../test-utils'; -import { transformArguments } from './GEOPOS'; +import { transformArguments, transformReply } from './GEOPOS'; describe('GEOPOS', () => { describe('transformArguments', () => { @@ -19,11 +19,49 @@ describe('GEOPOS', () => { }); }); - itWithClient(TestRedisServers.OPEN, 'client.geoPos', async client => { - assert.deepEqual( - await client.geoPos('key', 'member'), - [null] - ); + describe('transformReply', () => { + it('null', () => { + assert.deepEqual( + transformReply([null]), + [null] + ); + }); + + it('with member', () => { + assert.deepEqual( + transformReply([['1', '2']]), + [{ + longitude: '1', + latitude: '2' + }] + ); + }); + }); + + describe('client.geoPos', () => { + itWithClient(TestRedisServers.OPEN, 'null', async client => { + assert.deepEqual( + await client.geoPos('key', 'member'), + [null] + ); + }); + + itWithClient(TestRedisServers.OPEN, 'with member', async client => { + const coordinates = { + longitude: '-122.06429868936538696', + latitude: '37.37749628831998194' + }; + + await client.geoAdd('key', { + member: 'member', + ...coordinates + }); + + assert.deepEqual( + await client.geoPos('key', 'member'), + [coordinates] + ); + }); }); itWithCluster(TestRedisClusters.OPEN, 'cluster.geoPos', async cluster => { diff --git a/lib/commands/GEOSEARCHSTORE.spec.ts b/lib/commands/GEOSEARCHSTORE.spec.ts index 1983537077c..ad33c62b78c 100644 --- a/lib/commands/GEOSEARCHSTORE.spec.ts +++ b/lib/commands/GEOSEARCHSTORE.spec.ts @@ -1,6 +1,6 @@ import { strict as assert } from 'assert'; import { TestRedisServers, itWithClient, TestRedisClusters, itWithCluster, describeHandleMinimumRedisVersion } from '../test-utils'; -import { transformArguments } from './GEOSEARCHSTORE'; +import { transformArguments, transformReply } from './GEOSEARCHSTORE'; describe('GEOSEARCHSTORE', () => { describeHandleMinimumRedisVersion([6, 2]); @@ -40,6 +40,13 @@ describe('GEOSEARCHSTORE', () => { }); }); + it('transformReply with empty array (https://github.com/redis/redis/issues/9261)', () => { + assert.throws( + () => (transformReply as any)([]), + TypeError + ); + }); + itWithClient(TestRedisServers.OPEN, 'client.geoSearchStore', async client => { await client.geoAdd('source', { longitude: 1, diff --git a/lib/commands/PUBSUB_NUMSUB.spec.ts b/lib/commands/PUBSUB_NUMSUB.spec.ts index 74065dbb48f..403732f8f9d 100644 --- a/lib/commands/PUBSUB_NUMSUB.spec.ts +++ b/lib/commands/PUBSUB_NUMSUB.spec.ts @@ -33,7 +33,7 @@ describe('PUBSUB NUMSUB', () => { ); }); - itWithCluster(TestRedisClusters.OPEN, 'cluster.pubSubNumPat', async cluster => { + itWithCluster(TestRedisClusters.OPEN, 'cluster.pubSubNumSub', async cluster => { assert.deepEqual( await cluster.pubSubNumSub(), Object.create(null) From 08837c864801558ad8020278ad75a3b14a2ed560 Mon Sep 17 00:00:00 2001 From: leibale Date: Mon, 13 Sep 2021 19:49:39 -0400 Subject: [PATCH 0859/1748] fix #1650 - add support for Buffer in some commands, add GET_BUFFER command --- lib/client.spec.ts | 7 ++ lib/client.ts | 94 ++++++++++------------- lib/cluster-slots.ts | 2 +- lib/cluster.ts | 29 +++---- lib/commander.spec.ts | 22 +++++- lib/commander.ts | 20 ++--- lib/commands-queue.ts | 80 +++++-------------- lib/commands/ACL_DELUSER.ts | 3 +- lib/commands/ACL_SETUSER.ts | 3 +- lib/commands/BITOP.ts | 3 +- lib/commands/BLPOP.ts | 3 +- lib/commands/BRPOP.ts | 3 +- lib/commands/BZPOPMAX.ts | 3 +- lib/commands/BZPOPMIN.ts | 3 +- lib/commands/DEL.ts | 3 +- lib/commands/EXISTS.ts | 3 +- lib/commands/GEOHASH.ts | 3 +- lib/commands/GEOPOS.ts | 3 +- lib/commands/GET.ts | 3 +- lib/commands/GET_BUFFER.spec.ts | 22 ++++++ lib/commands/GET_BUFFER.ts | 7 ++ lib/commands/HDEL.ts | 3 +- lib/commands/HMGET.ts | 3 +- lib/commands/LPUSH.ts | 3 +- lib/commands/LPUSHX.ts | 3 +- lib/commands/PFADD.ts | 3 +- lib/commands/PFCOUNT.ts | 3 +- lib/commands/PFMERGE.ts | 3 +- lib/commands/RPUSH.ts | 3 +- lib/commands/RPUSHX.ts | 3 +- lib/commands/SADD.ts | 3 +- lib/commands/SCRIPT_EXISTS.ts | 3 +- lib/commands/SDIFF.ts | 3 +- lib/commands/SDIFFSTORE.ts | 3 +- lib/commands/SET.spec.ts | 2 +- lib/commands/SET.ts | 4 +- lib/commands/SETEX.ts | 3 +- lib/commands/SINTER.ts | 3 +- lib/commands/SINTERSTORE.ts | 3 +- lib/commands/SREM.ts | 3 +- lib/commands/SUNION.ts | 3 +- lib/commands/SUNIONSTORE.ts | 3 +- lib/commands/TOUCH.ts | 3 +- lib/commands/UNLINK.ts | 3 +- lib/commands/WATCH.ts | 3 +- lib/commands/XACK.ts | 3 +- lib/commands/XDEL.ts | 3 +- lib/commands/ZDIFF.ts | 3 +- lib/commands/ZDIFFSTORE.ts | 3 +- lib/commands/ZDIFF_WITHSCORES.ts | 3 +- lib/commands/ZINTER.ts | 3 +- lib/commands/ZINTERSTORE.ts | 3 +- lib/commands/ZINTER_WITHSCORES.ts | 3 +- lib/commands/ZMSCORE.ts | 3 +- lib/commands/ZREM.ts | 3 +- lib/commands/ZUNION.ts | 3 +- lib/commands/ZUNIONSTORE.ts | 3 +- lib/commands/ZUNION_WITHSCORES.ts | 3 +- lib/commands/generic-transformers.ts | 12 ++- lib/commands/index.ts | 10 ++- lib/multi-command.spec.ts | 23 +++--- lib/multi-command.ts | 20 +++-- lib/socket.ts | 28 +++++-- lib/ts-declarations/cluster-key-slot.d.ts | 2 +- lib/ts-declarations/redis-parser.d.ts | 2 + 65 files changed, 300 insertions(+), 227 deletions(-) create mode 100644 lib/commands/GET_BUFFER.spec.ts create mode 100644 lib/commands/GET_BUFFER.ts diff --git a/lib/client.spec.ts b/lib/client.spec.ts index f73049d2286..9f18e184c88 100644 --- a/lib/client.spec.ts +++ b/lib/client.spec.ts @@ -195,6 +195,13 @@ describe('Client', () => { assert.equal(await client.sendCommand(['PING']), 'PONG'); }); + itWithClient(TestRedisServers.OPEN, 'bufferMode', async client => { + assert.deepEqual( + await client.sendCommand(['PING'], undefined, true), + Buffer.from('PONG') + ); + }); + describe('AbortController', () => { before(function () { if (!global.AbortController) { diff --git a/lib/client.ts b/lib/client.ts index 139ec647fc3..aaa982da1cc 100644 --- a/lib/client.ts +++ b/lib/client.ts @@ -1,6 +1,6 @@ import RedisSocket, { RedisSocketOptions } from './socket'; import RedisCommandsQueue, { PubSubListener, PubSubSubscribeCommands, PubSubUnsubscribeCommands, QueueCommandOptions } from './commands-queue'; -import COMMANDS from './commands'; +import COMMANDS, { TransformArgumentsReply } from './commands'; import { RedisCommand, RedisModules, RedisReply } from './commands'; import RedisMultiCommand, { MultiQueuedCommand, RedisMultiCommandType } from './multi-command'; import EventEmitter from 'events'; @@ -62,12 +62,10 @@ export default class RedisClient> { const { args: redisArgs, options } = transformCommandArguments(command, args); - const reply = command.transformReply( - await this.#sendCommand(redisArgs, options), - redisArgs.preserve + return command.transformReply( + await this.#sendCommand(redisArgs, options, command.BUFFER_MODE), + redisArgs.preserve, ); - - return reply; } static async #scriptsExecutor( @@ -77,12 +75,10 @@ export default class RedisClient { const { args: redisArgs, options } = transformCommandArguments(script, args); - const reply = script.transformReply( - await this.executeScript(script, redisArgs, options), + return script.transformReply( + await this.executeScript(script, redisArgs, options, script.BUFFER_MODE), redisArgs.preserve ); - - return reply; } static create(options?: RedisClientOptions): RedisClientType { @@ -182,10 +178,7 @@ export default class RedisClient this.#socket.write(encodedCommands) - ); + return new RedisCommandsQueue(this.#options?.commandsQueueMaxLength); } #legacyMode(): void { @@ -299,7 +292,7 @@ export default class RedisClient { return this.#socket.quit(() => { - const promise = this.#queue.addEncodedCommand(encodeCommand(['QUIT'])); + const promise = this.#queue.addCommand(['QUIT']); this.#tick(); return promise; }); @@ -307,46 +300,64 @@ export default class RedisClient(args: Array, options?: ClientCommandOptions): Promise { - return this.#sendCommand(args, options); + sendCommand(args: TransformArgumentsReply, options?: ClientCommandOptions, bufferMode?: boolean): Promise { + return this.#sendCommand(args, options, bufferMode); } // using `#sendCommand` cause `sendCommand` is overwritten in legacy mode - #sendCommand(args: Array, options?: ClientCommandOptions): Promise { - return this.sendEncodedCommand(encodeCommand(args), options); - } - - async sendEncodedCommand(encodedCommand: string, options?: ClientCommandOptions): Promise { + async #sendCommand(args: TransformArgumentsReply, options?: ClientCommandOptions, bufferMode?: boolean): Promise { if (!this.#socket.isOpen) { throw new ClientClosedError(); } if (options?.isolated) { return this.executeIsolated(isolatedClient => - isolatedClient.sendEncodedCommand(encodedCommand, { + isolatedClient.sendCommand(args, { ...options, isolated: false }) ); } - const promise = this.#queue.addEncodedCommand(encodedCommand, options); + const promise = this.#queue.addCommand(args, options, bufferMode); this.#tick(); return await promise; } + #tick(): void { + if (!this.#socket.isSocketExists) { + return; + } + + this.#socket.cork(); + + while (true) { + const args = this.#queue.getCommandToSend(); + if (args === undefined) break; + + let writeResult; + for (const toWrite of encodeCommand(args)) { + writeResult = this.#socket.write(toWrite); + } + + if (!writeResult) { + break; + } + } + } + executeIsolated(fn: (client: RedisClientType) => T | Promise): Promise { return this.#isolationPool.use(fn); } - async executeScript(script: RedisLuaScript, args: Array, options?: ClientCommandOptions): Promise> { + async executeScript(script: RedisLuaScript, args: TransformArgumentsReply, options?: ClientCommandOptions, bufferMode?: boolean): Promise> { try { return await this.#sendCommand([ 'EVALSHA', script.SHA1, script.NUMBER_OF_KEYS.toString(), ...args - ], options); + ], options, bufferMode); } catch (err: any) { if (!err?.message?.startsWith?.('NOSCRIPT')) { throw err; @@ -357,14 +368,14 @@ export default class RedisClient, chainId?: symbol): Promise> { const promise = Promise.all( - commands.map(({encodedCommand}) => { - return this.#queue.addEncodedCommand(encodedCommand, RedisClient.commandOptions({ + commands.map(({ args }) => { + return this.#queue.addCommand(args, RedisClient.commandOptions({ chainId })); }) @@ -438,31 +449,6 @@ export default class RedisClient this.#tick()); - this.#isTickQueued = true; - return; - } - - const isBuffering = this.#queue.executeChunk(chunkRecommendedSize); - if (isBuffering === true) { - this.#socket.once('drain', () => this.#tick()); - } else if (isBuffering === false) { - this.#tick(); - return; - } - - this.#isTickQueued = false; - } } extendWithDefaultCommands(RedisClient, RedisClient.commandsExecutor); diff --git a/lib/cluster-slots.ts b/lib/cluster-slots.ts index 5fae5b92342..a5155cc53db 100644 --- a/lib/cluster-slots.ts +++ b/lib/cluster-slots.ts @@ -172,7 +172,7 @@ export default class RedisClusterSlots { + getClient(firstKey?: string | Buffer, isReadonly?: boolean): RedisClientType { if (!firstKey) { return this.#getRandomClient(); } diff --git a/lib/cluster.ts b/lib/cluster.ts index 3eeaed5009f..4f1b27cb05f 100644 --- a/lib/cluster.ts +++ b/lib/cluster.ts @@ -1,4 +1,4 @@ -import { RedisCommand, RedisModules } from './commands'; +import { RedisCommand, RedisModules, TransformArgumentsReply } from './commands'; import RedisClient, { ClientCommandOptions, RedisClientType, WithPlugins } from './client'; import { RedisSocketOptions } from './socket'; import RedisClusterSlots, { ClusterNode } from './cluster-slots'; @@ -6,6 +6,7 @@ import { RedisLuaScript, RedisLuaScripts } from './lua-script'; import { extendWithModulesAndScripts, extendWithDefaultCommands, transformCommandArguments } from './commander'; import RedisMultiCommand, { MultiQueuedCommand, RedisMultiCommandType } from './multi-command'; import { EventEmitter } from 'events'; +import cluster from 'cluster'; export interface RedisClusterOptions { rootNodes: Array; @@ -19,7 +20,7 @@ export type RedisClusterType WithPlugins & RedisCluster; export default class RedisCluster extends EventEmitter { - static #extractFirstKey(command: RedisCommand, originalArgs: Array, redisArgs: Array): string | undefined { + static #extractFirstKey(command: RedisCommand, originalArgs: Array, redisArgs: TransformArgumentsReply): string | Buffer | undefined { if (command.FIRST_KEY_INDEX === undefined) { return undefined; } else if (typeof command.FIRST_KEY_INDEX === 'number') { @@ -41,7 +42,8 @@ export default class RedisCluster( - firstKey: string | undefined, + firstKey: string | Buffer | undefined, isReadonly: boolean | undefined, - args: Array, + args: TransformArgumentsReply, options?: ClientCommandOptions, + bufferMode?: boolean, redirections = 0 ): Promise> { const client = this.#slots.getClient(firstKey, isReadonly); try { - return await client.sendCommand(args, options); + return await client.sendCommand(args, options, bufferMode); } catch (err: any) { const shouldRetry = await this.#handleCommandError(err, client, redirections); if (shouldRetry === true) { - return this.sendCommand(firstKey, isReadonly, args, options, redirections + 1); + return this.sendCommand(firstKey, isReadonly, args, options, bufferMode, redirections + 1); } else if (shouldRetry) { - return shouldRetry.sendCommand(args, options); + return shouldRetry.sendCommand(args, options, bufferMode); } throw err; @@ -125,7 +128,7 @@ export default class RedisCluster, - redisArgs: Array, + redisArgs: TransformArgumentsReply, options?: ClientCommandOptions, redirections = 0 ): Promise> { @@ -135,13 +138,13 @@ export default class RedisCluster { - return client.sendEncodedCommand(encodedCommand, RedisClient.commandOptions({ + commands.map(({ args }) => { + return client.sendCommand(args, RedisClient.commandOptions({ chainId })); }) diff --git a/lib/commander.spec.ts b/lib/commander.spec.ts index a38330abada..b6ec1004613 100644 --- a/lib/commander.spec.ts +++ b/lib/commander.spec.ts @@ -2,27 +2,43 @@ import { strict as assert } from 'assert'; import { describe } from 'mocha'; import { encodeCommand } from './commander'; +function encodeCommandToString(...args: Parameters): string { + const arr = []; + for (const item of encodeCommand(...args)) { + arr.push(item.toString()); + } + + return arr.join(''); +} + describe('Commander', () => { describe('encodeCommand (see #1628)', () => { it('1 byte', () => { assert.equal( - encodeCommand(['a', 'z']), + encodeCommandToString(['a', 'z']), '*2\r\n$1\r\na\r\n$1\r\nz\r\n' ); }); it('2 bytes', () => { assert.equal( - encodeCommand(['א', 'ת']), + encodeCommandToString(['א', 'ת']), '*2\r\n$2\r\nא\r\n$2\r\nת\r\n' ); }); it('4 bytes', () => { assert.equal( - encodeCommand(['🐣', '🐤']), + encodeCommandToString(['🐣', '🐤']), '*2\r\n$4\r\n🐣\r\n$4\r\n🐤\r\n' ); }); + + it('with a buffer', () => { + assert.equal( + encodeCommandToString([Buffer.from('string')]), + '*1\r\n$6\r\nstring\r\n' + ); + }); }); }); diff --git a/lib/commander.ts b/lib/commander.ts index e8ff91cc7bf..c2b1918709a 100644 --- a/lib/commander.ts +++ b/lib/commander.ts @@ -2,6 +2,7 @@ import COMMANDS, { RedisCommand, RedisModules, TransformArgumentsReply } from './commands'; import { RedisLuaScript, RedisLuaScripts } from './lua-script'; import { CommandOptions, isCommandOptions } from './command-options'; +import { off } from 'process'; type Instantiable = new(...args: Array) => T; @@ -94,16 +95,15 @@ export function transformCommandArguments( }; } -export function encodeCommand(args: Array): string { - const encoded = [ - `*${args.length}`, - `$${Buffer.byteLength(args[0]).toString()}`, - args[0] - ]; +const DELIMITER = '\r\n'; - for (let i = 1; i < args.length; i++) { - encoded.push(`$${Buffer.byteLength(args[i]).toString()}`, args[i]); - } +export function* encodeCommand(args: TransformArgumentsReply): IterableIterator { + yield `*${args.length}${DELIMITER}`; - return encoded.join('\r\n') + '\r\n'; + for (const arg of args) { + const byteLength = typeof arg === 'string' ? Buffer.byteLength(arg): arg.length; + yield `$${byteLength.toString()}${DELIMITER}`; + yield arg; + yield DELIMITER; + } } diff --git a/lib/commands-queue.ts b/lib/commands-queue.ts index cae3fd6130e..27c83965529 100644 --- a/lib/commands-queue.ts +++ b/lib/commands-queue.ts @@ -2,17 +2,15 @@ import LinkedList from 'yallist'; import RedisParser from 'redis-parser'; import { AbortError } from './errors'; import { RedisReply } from './commands'; -import { encodeCommand } from './commander'; export interface QueueCommandOptions { asap?: boolean; - signal?: any; // TODO: `AbortSignal` type is incorrect chainId?: symbol; + signal?: any; // TODO: `AbortSignal` type is incorrect } interface CommandWaitingToBeSent extends CommandWaitingForReply { - encodedCommand: string; - byteLength: number; + args: Array; chainId?: symbol; abort?: { signal: any; // TODO: `AbortSignal` type is incorrect @@ -24,10 +22,9 @@ interface CommandWaitingForReply { resolve(reply?: any): void; reject(err: Error): void; channelsCounter?: number; + bufferMode?: boolean; } -export type CommandsQueueExecutor = (encodedCommands: string) => boolean | undefined; - export enum PubSubSubscribeCommands { SUBSCRIBE = 'SUBSCRIBE', PSUBSCRIBE = 'PSUBSCRIBE' @@ -57,16 +54,8 @@ export default class RedisCommandsQueue { readonly #maxLength: number | null | undefined; - readonly #executor: CommandsQueueExecutor; - readonly #waitingToBeSent = new LinkedList(); - #waitingToBeSentCommandsLength = 0; - - get waitingToBeSentCommandsLength() { - return this.#waitingToBeSentCommandsLength; - } - readonly #waitingForReply = new LinkedList(); readonly #pubSubState = { @@ -114,12 +103,11 @@ export default class RedisCommandsQueue { #chainInExecution: symbol | undefined; - constructor(maxLength: number | null | undefined, executor: CommandsQueueExecutor) { + constructor(maxLength: number | null | undefined) { this.#maxLength = maxLength; - this.#executor = executor; } - addEncodedCommand(encodedCommand: string, options?: QueueCommandOptions): Promise { + addCommand(args: Array, options?: QueueCommandOptions, bufferMode?: boolean): Promise { if (this.#pubSubState.subscribing || this.#pubSubState.subscribed) { return Promise.reject(new Error('Cannot send commands in PubSub mode')); } else if (this.#maxLength && this.#waitingToBeSent.length + this.#waitingForReply.length >= this.#maxLength) { @@ -130,11 +118,11 @@ export default class RedisCommandsQueue { return new Promise((resolve, reject) => { const node = new LinkedList.Node({ - encodedCommand, - byteLength: Buffer.byteLength(encodedCommand), + args, chainId: options?.chainId, + bufferMode, resolve, - reject + reject, }); if (options?.signal) { @@ -157,8 +145,6 @@ export default class RedisCommandsQueue { } else { this.#waitingToBeSent.pushNode(node); } - - this.#waitingToBeSentCommandsLength += node.value.byteLength; }); } @@ -233,11 +219,8 @@ export default class RedisCommandsQueue { this.#pubSubState[inProgressKey] += channelsCounter; - const encodedCommand = encodeCommand(commandArgs), - byteLength = Buffer.byteLength(encodedCommand); this.#waitingToBeSent.push({ - encodedCommand, - byteLength, + args: commandArgs, channelsCounter, resolve: () => { this.#pubSubState[inProgressKey] -= channelsCounter; @@ -249,7 +232,6 @@ export default class RedisCommandsQueue { reject(); } }); - this.#waitingToBeSentCommandsLength += byteLength; }); } @@ -267,47 +249,25 @@ export default class RedisCommandsQueue { ]); } - executeChunk(recommendedSize: number): boolean | undefined { - if (!this.#waitingToBeSent.length) return; - - const encoded: Array = []; - let size = 0, - lastCommandChainId: symbol | undefined; - for (const command of this.#waitingToBeSent) { - encoded.push(command.encodedCommand); - size += command.byteLength; - if (size > recommendedSize) { - lastCommandChainId = command.chainId; - break; - } - } - - if (!lastCommandChainId && encoded.length === this.#waitingToBeSent.length) { - lastCommandChainId = this.#waitingToBeSent.tail!.value.chainId; - } - - lastCommandChainId ??= this.#waitingToBeSent.tail?.value.chainId; - - this.#executor(encoded.join('')); - - for (let i = 0; i < encoded.length; i++) { - const waitingToBeSent = this.#waitingToBeSent.shift()!; - if (waitingToBeSent.abort) { - waitingToBeSent.abort.signal.removeEventListener('abort', waitingToBeSent.abort.listener); - } + getCommandToSend(): Array | undefined { + const toSend = this.#waitingToBeSent.shift(); + if (toSend) { this.#waitingForReply.push({ - resolve: waitingToBeSent.resolve, - reject: waitingToBeSent.reject, - channelsCounter: waitingToBeSent.channelsCounter + resolve: toSend.resolve, + reject: toSend.reject, + channelsCounter: toSend.channelsCounter, + bufferMode: toSend.bufferMode }); } - this.#chainInExecution = lastCommandChainId; - this.#waitingToBeSentCommandsLength -= size; + this.#chainInExecution = toSend?.chainId; + + return toSend?.args; } parseResponse(data: Buffer): void { + this.#parser.setReturnBuffers(!!this.#waitingForReply.head?.value.bufferMode); this.#parser.execute(data); } diff --git a/lib/commands/ACL_DELUSER.ts b/lib/commands/ACL_DELUSER.ts index 7fb4904be41..85a916c4379 100644 --- a/lib/commands/ACL_DELUSER.ts +++ b/lib/commands/ACL_DELUSER.ts @@ -1,6 +1,7 @@ +import { TransformArgumentsReply } from '.'; import { pushVerdictArguments, transformReplyNumber } from './generic-transformers'; -export function transformArguments(username: string | Array): Array { +export function transformArguments(username: string | Array): TransformArgumentsReply { return pushVerdictArguments(['ACL', 'DELUSER'], username); } diff --git a/lib/commands/ACL_SETUSER.ts b/lib/commands/ACL_SETUSER.ts index b2829ca964f..e55a8942e02 100644 --- a/lib/commands/ACL_SETUSER.ts +++ b/lib/commands/ACL_SETUSER.ts @@ -1,6 +1,7 @@ +import { TransformArgumentsReply } from '.'; import { pushVerdictArguments, transformReplyString } from './generic-transformers'; -export function transformArguments(username: string, rule: string | Array): Array { +export function transformArguments(username: string, rule: string | Array): TransformArgumentsReply { return pushVerdictArguments(['ACL', 'SETUSER', username], rule); } diff --git a/lib/commands/BITOP.ts b/lib/commands/BITOP.ts index fe7d339f5d1..bb965da6dfa 100644 --- a/lib/commands/BITOP.ts +++ b/lib/commands/BITOP.ts @@ -1,10 +1,11 @@ +import { TransformArgumentsReply } from '.'; import { pushVerdictArguments, transformReplyNumber } from './generic-transformers'; export const FIRST_KEY_INDEX = 2; type BitOperations = 'AND' | 'OR' | 'XOR' | 'NOT'; -export function transformArguments(operation: BitOperations, destKey: string, key: string | Array): Array { +export function transformArguments(operation: BitOperations, destKey: string, key: string | Array): TransformArgumentsReply { return pushVerdictArguments(['BITOP', operation, destKey], key); } diff --git a/lib/commands/BLPOP.ts b/lib/commands/BLPOP.ts index 7c352951fb3..1061f5e113a 100644 --- a/lib/commands/BLPOP.ts +++ b/lib/commands/BLPOP.ts @@ -1,8 +1,9 @@ +import { TransformArgumentsReply } from '.'; import { pushVerdictArguments } from './generic-transformers'; export const FIRST_KEY_INDEX = 1; -export function transformArguments(keys: string | Array, timeout: number): Array { +export function transformArguments(keys: string | Buffer | Array, timeout: number): TransformArgumentsReply { const args = pushVerdictArguments(['BLPOP'], keys); args.push(timeout.toString()); diff --git a/lib/commands/BRPOP.ts b/lib/commands/BRPOP.ts index a03c278309a..93ded4dbf1a 100644 --- a/lib/commands/BRPOP.ts +++ b/lib/commands/BRPOP.ts @@ -1,8 +1,9 @@ +import { TransformArgumentsReply } from '.'; import { pushVerdictArguments } from './generic-transformers'; export const FIRST_KEY_INDEX = 1; -export function transformArguments(key: string | Array, timeout: number): Array { +export function transformArguments(key: string | Array, timeout: number): TransformArgumentsReply { const args = pushVerdictArguments(['BRPOP'], key); args.push(timeout.toString()); diff --git a/lib/commands/BZPOPMAX.ts b/lib/commands/BZPOPMAX.ts index ccd84272a50..3db9ca42cbb 100644 --- a/lib/commands/BZPOPMAX.ts +++ b/lib/commands/BZPOPMAX.ts @@ -1,8 +1,9 @@ +import { TransformArgumentsReply } from '.'; import { pushVerdictArguments, transformReplyNumberInfinity, ZMember } from './generic-transformers'; export const FIRST_KEY_INDEX = 1; -export function transformArguments(key: string | Array, timeout: number): Array { +export function transformArguments(key: string | Array, timeout: number): TransformArgumentsReply { const args = pushVerdictArguments(['BZPOPMAX'], key); args.push(timeout.toString()); diff --git a/lib/commands/BZPOPMIN.ts b/lib/commands/BZPOPMIN.ts index 0c299cdb9df..9106ae770da 100644 --- a/lib/commands/BZPOPMIN.ts +++ b/lib/commands/BZPOPMIN.ts @@ -1,8 +1,9 @@ +import { TransformArgumentsReply } from '.'; import { pushVerdictArguments, transformReplyNumberInfinity, ZMember } from './generic-transformers'; export const FIRST_KEY_INDEX = 1; -export function transformArguments(key: string | Array, timeout: number): Array { +export function transformArguments(key: string | Array, timeout: number): TransformArgumentsReply { const args = pushVerdictArguments(['BZPOPMIN'], key); args.push(timeout.toString()); diff --git a/lib/commands/DEL.ts b/lib/commands/DEL.ts index 3d9a78212f8..f96b6988f1c 100644 --- a/lib/commands/DEL.ts +++ b/lib/commands/DEL.ts @@ -1,6 +1,7 @@ +import { TransformArgumentsReply } from '.'; import { pushVerdictArguments, transformReplyNumber } from './generic-transformers'; -export function transformArguments(keys: string | Array): Array { +export function transformArguments(keys: string | Array): TransformArgumentsReply { return pushVerdictArguments(['DEL'], keys); } diff --git a/lib/commands/EXISTS.ts b/lib/commands/EXISTS.ts index 5a76ca833fb..00d10b9eebc 100644 --- a/lib/commands/EXISTS.ts +++ b/lib/commands/EXISTS.ts @@ -1,10 +1,11 @@ +import { TransformArgumentsReply } from '.'; import { pushVerdictArguments, transformReplyBoolean } from './generic-transformers'; export const FIRST_KEY_INDEX = 1; export const IS_READ_ONLY = true; -export function transformArguments(keys: string | Array): Array { +export function transformArguments(keys: string | Array): TransformArgumentsReply { return pushVerdictArguments(['EXISTS'], keys); } diff --git a/lib/commands/GEOHASH.ts b/lib/commands/GEOHASH.ts index a46738955d3..a95ae443408 100644 --- a/lib/commands/GEOHASH.ts +++ b/lib/commands/GEOHASH.ts @@ -1,10 +1,11 @@ +import { TransformArgumentsReply } from '.'; import { pushVerdictArguments, transformReplyStringArray } from './generic-transformers'; export const FIRST_KEY_INDEX = 1; export const IS_READ_ONLY = true; -export function transformArguments(key: string, member: string | Array): Array { +export function transformArguments(key: string, member: string | Array): TransformArgumentsReply { return pushVerdictArguments(['GEOHASH', key], member); } diff --git a/lib/commands/GEOPOS.ts b/lib/commands/GEOPOS.ts index 46b0a153ba9..893048cf6da 100644 --- a/lib/commands/GEOPOS.ts +++ b/lib/commands/GEOPOS.ts @@ -1,10 +1,11 @@ +import { TransformArgumentsReply } from '.'; import { pushVerdictArguments } from './generic-transformers'; export const FIRST_KEY_INDEX = 1; export const IS_READ_ONLY = true; -export function transformArguments(key: string, member: string | Array): Array { +export function transformArguments(key: string, member: string | Array): TransformArgumentsReply { return pushVerdictArguments(['GEOPOS', key], member); } diff --git a/lib/commands/GET.ts b/lib/commands/GET.ts index 714ad953d8e..6c6475a9d24 100644 --- a/lib/commands/GET.ts +++ b/lib/commands/GET.ts @@ -1,10 +1,11 @@ +import { TransformArgumentsReply } from '.'; import { transformReplyString } from './generic-transformers'; export const FIRST_KEY_INDEX = 1; export const IS_READ_ONLY = true; -export function transformArguments(key: string): Array { +export function transformArguments(key: string | Buffer): TransformArgumentsReply { return ['GET', key]; } diff --git a/lib/commands/GET_BUFFER.spec.ts b/lib/commands/GET_BUFFER.spec.ts new file mode 100644 index 00000000000..533eb808c49 --- /dev/null +++ b/lib/commands/GET_BUFFER.spec.ts @@ -0,0 +1,22 @@ +import { strict as assert } from 'assert'; +import { TestRedisServers, itWithClient, TestRedisClusters, itWithCluster } from '../test-utils'; + +describe('GET_BUFFER', () => { + itWithClient(TestRedisServers.OPEN, 'client.getBuffer', async client => { + const buffer = Buffer.from('string'); + await client.set('key', buffer); + assert.deepEqual( + buffer, + await client.getBuffer('key') + ); + }); + + itWithCluster(TestRedisClusters.OPEN, 'cluster.getBuffer', async cluster => { + const buffer = Buffer.from('string'); + await cluster.set('key', buffer); + assert.deepEqual( + buffer, + await cluster.getBuffer('key') + ); + }); +}); diff --git a/lib/commands/GET_BUFFER.ts b/lib/commands/GET_BUFFER.ts new file mode 100644 index 00000000000..3d6f454898b --- /dev/null +++ b/lib/commands/GET_BUFFER.ts @@ -0,0 +1,7 @@ +import { transformReplyBuffer } from './generic-transformers'; + +export { FIRST_KEY_INDEX, IS_READ_ONLY, transformArguments } from './GET'; + +export const BUFFER_MODE = true; + +export const transformReply = transformReplyBuffer; diff --git a/lib/commands/HDEL.ts b/lib/commands/HDEL.ts index ee961931449..4785b0e67f9 100644 --- a/lib/commands/HDEL.ts +++ b/lib/commands/HDEL.ts @@ -1,8 +1,9 @@ +import { TransformArgumentsReply } from '.'; import { pushVerdictArguments, transformReplyNumber } from './generic-transformers'; export const FIRST_KEY_INDEX = 1; -export function transformArguments(key: string, field: string | Array): Array { +export function transformArguments(key: string, field: string | Array): TransformArgumentsReply { return pushVerdictArguments(['HDEL', key], field); } diff --git a/lib/commands/HMGET.ts b/lib/commands/HMGET.ts index fc0f91d8224..9f26eeba640 100644 --- a/lib/commands/HMGET.ts +++ b/lib/commands/HMGET.ts @@ -1,10 +1,11 @@ +import { TransformArgumentsReply } from '.'; import { pushVerdictArguments, transformReplyStringArray } from './generic-transformers'; export const FIRST_KEY_INDEX = 1; export const IS_READ_ONLY = true; -export function transformArguments(key: string, fields: string | Array): Array { +export function transformArguments(key: string, fields: string | Array): TransformArgumentsReply { return pushVerdictArguments(['HMGET', key], fields); } diff --git a/lib/commands/LPUSH.ts b/lib/commands/LPUSH.ts index 434ad619cb7..7416d4946ea 100644 --- a/lib/commands/LPUSH.ts +++ b/lib/commands/LPUSH.ts @@ -1,8 +1,9 @@ +import { TransformArgumentsReply } from '.'; import { pushVerdictArguments, transformReplyNumber } from './generic-transformers'; export const FIRST_KEY_INDEX = 1; -export function transformArguments(key: string, elements: string | Array): Array { +export function transformArguments(key: string, elements: string | Array): TransformArgumentsReply { return pushVerdictArguments(['LPUSH', key], elements);} export const transformReply = transformReplyNumber; diff --git a/lib/commands/LPUSHX.ts b/lib/commands/LPUSHX.ts index f1a989d9625..f89623ace3a 100644 --- a/lib/commands/LPUSHX.ts +++ b/lib/commands/LPUSHX.ts @@ -1,8 +1,9 @@ +import { TransformArgumentsReply } from '.'; import { pushVerdictArguments, transformReplyNumber } from './generic-transformers'; export const FIRST_KEY_INDEX = 1; -export function transformArguments(key: string, element: string | Array): Array { +export function transformArguments(key: string, element: string | Array): TransformArgumentsReply { return pushVerdictArguments(['LPUSHX', key], element); } diff --git a/lib/commands/PFADD.ts b/lib/commands/PFADD.ts index 3348a98852a..cc99bed7f65 100644 --- a/lib/commands/PFADD.ts +++ b/lib/commands/PFADD.ts @@ -1,8 +1,9 @@ +import { TransformArgumentsReply } from '.'; import { pushVerdictArguments, transformReplyBoolean } from './generic-transformers'; export const FIRST_KEY_INDEX = 1; -export function transformArguments(key: string, element: string | Array): Array { +export function transformArguments(key: string, element: string | Array): TransformArgumentsReply { return pushVerdictArguments(['PFADD', key], element); } diff --git a/lib/commands/PFCOUNT.ts b/lib/commands/PFCOUNT.ts index eac710a3543..52963697adf 100644 --- a/lib/commands/PFCOUNT.ts +++ b/lib/commands/PFCOUNT.ts @@ -1,8 +1,9 @@ +import { TransformArgumentsReply } from '.'; import { pushVerdictArguments, transformReplyNumber } from './generic-transformers'; export const FIRST_KEY_INDEX = 1; -export function transformArguments(key: string | Array): Array { +export function transformArguments(key: string | Array): TransformArgumentsReply { return pushVerdictArguments(['PFCOUNT'], key); } diff --git a/lib/commands/PFMERGE.ts b/lib/commands/PFMERGE.ts index 73a4a2edb9a..c4ba11877f7 100644 --- a/lib/commands/PFMERGE.ts +++ b/lib/commands/PFMERGE.ts @@ -1,8 +1,9 @@ +import { TransformArgumentsReply } from '.'; import { pushVerdictArguments, transformReplyString } from './generic-transformers'; export const FIRST_KEY_INDEX = 1; -export function transformArguments(destination: string, source: string | Array): Array { +export function transformArguments(destination: string, source: string | Array): TransformArgumentsReply { return pushVerdictArguments(['PFMERGE', destination], source); } diff --git a/lib/commands/RPUSH.ts b/lib/commands/RPUSH.ts index 191d2704e09..665094f47a5 100644 --- a/lib/commands/RPUSH.ts +++ b/lib/commands/RPUSH.ts @@ -1,8 +1,9 @@ +import { TransformArgumentsReply } from '.'; import { pushVerdictArguments, transformReplyNumber } from './generic-transformers'; export const FIRST_KEY_INDEX = 1; -export function transformArguments(key: string, element: string | Array): Array { +export function transformArguments(key: string, element: string | Array): TransformArgumentsReply { return pushVerdictArguments(['RPUSH', key], element); } diff --git a/lib/commands/RPUSHX.ts b/lib/commands/RPUSHX.ts index a07615a58e0..fe1f969f3f6 100644 --- a/lib/commands/RPUSHX.ts +++ b/lib/commands/RPUSHX.ts @@ -1,8 +1,9 @@ +import { TransformArgumentsReply } from '.'; import { pushVerdictArguments, transformReplyNumber } from './generic-transformers'; export const FIRST_KEY_INDEX = 1; -export function transformArguments(key: string, element: string | Array): Array { +export function transformArguments(key: string, element: string | Array): TransformArgumentsReply { return pushVerdictArguments(['RPUSHX', key], element); } diff --git a/lib/commands/SADD.ts b/lib/commands/SADD.ts index a14ba1686c0..a432ccfef59 100644 --- a/lib/commands/SADD.ts +++ b/lib/commands/SADD.ts @@ -1,8 +1,9 @@ +import { TransformArgumentsReply } from '.'; import { pushVerdictArguments, transformReplyNumber } from './generic-transformers'; export const FIRST_KEY_INDEX = 1; -export function transformArguments(key: string, members: string | Array): Array { +export function transformArguments(key: string, members: string | Array): TransformArgumentsReply { return pushVerdictArguments(['SADD', key], members); } diff --git a/lib/commands/SCRIPT_EXISTS.ts b/lib/commands/SCRIPT_EXISTS.ts index b127a0b261b..47a7f456e9b 100644 --- a/lib/commands/SCRIPT_EXISTS.ts +++ b/lib/commands/SCRIPT_EXISTS.ts @@ -1,6 +1,7 @@ +import { TransformArgumentsReply } from '.'; import { pushVerdictArguments, transformReplyBooleanArray } from './generic-transformers'; -export function transformArguments(sha1: string | Array): Array { +export function transformArguments(sha1: string | Array): TransformArgumentsReply { return pushVerdictArguments(['SCRIPT', 'EXISTS'], sha1); } diff --git a/lib/commands/SDIFF.ts b/lib/commands/SDIFF.ts index 496ed593370..4d5aaea1a06 100644 --- a/lib/commands/SDIFF.ts +++ b/lib/commands/SDIFF.ts @@ -1,8 +1,9 @@ +import { TransformArgumentsReply } from '.'; import { pushVerdictArguments, transformReplyStringArray } from './generic-transformers'; export const FIRST_KEY_INDEX = 1; -export function transformArguments(keys: string | Array): Array { +export function transformArguments(keys: string | Array): TransformArgumentsReply { return pushVerdictArguments(['SDIFF'], keys); } diff --git a/lib/commands/SDIFFSTORE.ts b/lib/commands/SDIFFSTORE.ts index 295433602fb..69883d4124c 100644 --- a/lib/commands/SDIFFSTORE.ts +++ b/lib/commands/SDIFFSTORE.ts @@ -1,8 +1,9 @@ +import { TransformArgumentsReply } from '.'; import { pushVerdictArguments, transformReplyNumber } from './generic-transformers'; export const FIRST_KEY_INDEX = 1; -export function transformArguments(destination: string, keys: string | Array): Array { +export function transformArguments(destination: string, keys: string | Array): TransformArgumentsReply { return pushVerdictArguments(['SDIFFSTORE', destination], keys); } diff --git a/lib/commands/SET.spec.ts b/lib/commands/SET.spec.ts index a587f6c3120..32d138f2920 100644 --- a/lib/commands/SET.spec.ts +++ b/lib/commands/SET.spec.ts @@ -106,7 +106,7 @@ describe('SET', () => { 'OK' ); }); - + itWithClient(TestRedisServers.OPEN, 'with GET on empty key', async client => { assert.equal( await client.set('key', 'value', { diff --git a/lib/commands/SET.ts b/lib/commands/SET.ts index 4d5919cde21..03853b3f7d6 100644 --- a/lib/commands/SET.ts +++ b/lib/commands/SET.ts @@ -1,3 +1,5 @@ +import { TransformArgumentsReply } from '.'; + export const FIRST_KEY_INDEX = 1; interface EX { @@ -38,7 +40,7 @@ interface SetCommonOptions { type SetOptions = SetTTL & SetGuards & (SetCommonOptions | {}); -export function transformArguments(key: string, value: string, options?: SetOptions): Array { +export function transformArguments(key: string | Buffer, value: string | Buffer, options?: SetOptions): TransformArgumentsReply { const args = ['SET', key, value]; if (!options) { diff --git a/lib/commands/SETEX.ts b/lib/commands/SETEX.ts index 57c32db6ffe..320278c9264 100644 --- a/lib/commands/SETEX.ts +++ b/lib/commands/SETEX.ts @@ -1,8 +1,9 @@ +import { TransformArgumentsReply } from '.'; import { transformReplyString } from './generic-transformers'; export const FIRST_KEY_INDEX = 1; -export function transformArguments(key: string, seconds: number, value: string): Array { +export function transformArguments(key: string | Buffer, seconds: number, value: string): TransformArgumentsReply { return [ 'SETEX', key, diff --git a/lib/commands/SINTER.ts b/lib/commands/SINTER.ts index 104e81b9214..43869652370 100644 --- a/lib/commands/SINTER.ts +++ b/lib/commands/SINTER.ts @@ -1,8 +1,9 @@ +import { TransformArgumentsReply } from '.'; import { pushVerdictArguments, transformReplyStringArray } from './generic-transformers'; export const FIRST_KEY_INDEX = 1; -export function transformArguments(keys: string | Array): Array { +export function transformArguments(keys: string | Array): TransformArgumentsReply { return pushVerdictArguments(['SINTER'], keys); } diff --git a/lib/commands/SINTERSTORE.ts b/lib/commands/SINTERSTORE.ts index a7a4d4fd106..5ad1b11cbac 100644 --- a/lib/commands/SINTERSTORE.ts +++ b/lib/commands/SINTERSTORE.ts @@ -1,8 +1,9 @@ +import { TransformArgumentsReply } from '.'; import { pushVerdictArguments, transformReplyStringArray } from './generic-transformers'; export const FIRST_KEY_INDEX = 1; -export function transformArguments(destination: string, keys: string | Array): Array { +export function transformArguments(destination: string, keys: string | Array): TransformArgumentsReply { return pushVerdictArguments(['SINTERSTORE', destination], keys); } diff --git a/lib/commands/SREM.ts b/lib/commands/SREM.ts index d1021bb3a19..4ae33245d29 100644 --- a/lib/commands/SREM.ts +++ b/lib/commands/SREM.ts @@ -1,8 +1,9 @@ +import { TransformArgumentsReply } from '.'; import { pushVerdictArguments, transformReplyNumber } from './generic-transformers'; export const FIRST_KEY_INDEX = 1; -export function transformArguments(key: string, members: string | Array): Array { +export function transformArguments(key: string, members: string | Array): TransformArgumentsReply { return pushVerdictArguments(['SREM', key], members); } diff --git a/lib/commands/SUNION.ts b/lib/commands/SUNION.ts index 3f06138b1b6..705bff29927 100644 --- a/lib/commands/SUNION.ts +++ b/lib/commands/SUNION.ts @@ -1,10 +1,11 @@ +import { TransformArgumentsReply } from '.'; import { pushVerdictArguments, transformReplyStringArray } from './generic-transformers'; export const FIRST_KEY_INDEX = 1; export const IS_READ_ONLY = true; -export function transformArguments(keys: string | Array): Array { +export function transformArguments(keys: string | Array): TransformArgumentsReply { return pushVerdictArguments(['SUNION'], keys); } diff --git a/lib/commands/SUNIONSTORE.ts b/lib/commands/SUNIONSTORE.ts index 7a1aab80117..af717f627df 100644 --- a/lib/commands/SUNIONSTORE.ts +++ b/lib/commands/SUNIONSTORE.ts @@ -1,8 +1,9 @@ +import { TransformArgumentsReply } from '.'; import { pushVerdictArguments, transformReplyNumber } from './generic-transformers'; export const FIRST_KEY_INDEX = 1; -export function transformArguments(destination: string, keys: string | Array): Array { +export function transformArguments(destination: string, keys: string | Array): TransformArgumentsReply { return pushVerdictArguments(['SUNIONSTORE', destination], keys); } diff --git a/lib/commands/TOUCH.ts b/lib/commands/TOUCH.ts index f2fb0548970..abff4160392 100644 --- a/lib/commands/TOUCH.ts +++ b/lib/commands/TOUCH.ts @@ -1,8 +1,9 @@ +import { TransformArgumentsReply } from '.'; import { pushVerdictArguments, transformReplyNumber } from './generic-transformers'; export const FIRST_KEY_INDEX = 1; -export function transformArguments(key: string | Array): Array { +export function transformArguments(key: string | Array): TransformArgumentsReply { return pushVerdictArguments(['TOUCH'], key); } diff --git a/lib/commands/UNLINK.ts b/lib/commands/UNLINK.ts index 9dfe0ca48ea..4647a976e42 100644 --- a/lib/commands/UNLINK.ts +++ b/lib/commands/UNLINK.ts @@ -1,8 +1,9 @@ +import { TransformArgumentsReply } from '.'; import { pushVerdictArguments, transformReplyNumber } from './generic-transformers'; export const FIRST_KEY_INDEX = 1; -export function transformArguments(key: string | Array): Array { +export function transformArguments(key: string | Array): TransformArgumentsReply { return pushVerdictArguments(['UNLINK'], key); } diff --git a/lib/commands/WATCH.ts b/lib/commands/WATCH.ts index 5e24ca37952..e644ab0f462 100644 --- a/lib/commands/WATCH.ts +++ b/lib/commands/WATCH.ts @@ -1,6 +1,7 @@ +import { TransformArgumentsReply } from '.'; import { pushVerdictArguments, transformReplyString } from './generic-transformers'; -export function transformArguments(key: string | Array): Array { +export function transformArguments(key: string | Array): TransformArgumentsReply { return pushVerdictArguments(['WATCH'], key); } diff --git a/lib/commands/XACK.ts b/lib/commands/XACK.ts index 969f9b6a8b9..a6de28151eb 100644 --- a/lib/commands/XACK.ts +++ b/lib/commands/XACK.ts @@ -1,8 +1,9 @@ +import { TransformArgumentsReply } from '.'; import { pushVerdictArguments, transformReplyNumber } from './generic-transformers'; export const FIRST_KEY_INDEX = 1; -export function transformArguments(key: string, group: string, id: string | Array): Array { +export function transformArguments(key: string, group: string, id: string | Array): TransformArgumentsReply { return pushVerdictArguments(['XACK', key, group], id); } diff --git a/lib/commands/XDEL.ts b/lib/commands/XDEL.ts index 9d173271c28..083ea77ef0f 100644 --- a/lib/commands/XDEL.ts +++ b/lib/commands/XDEL.ts @@ -1,8 +1,9 @@ +import { TransformArgumentsReply } from '.'; import { pushVerdictArguments, transformReplyNumber } from './generic-transformers'; export const FIRST_KEY_INDEX = 1; -export function transformArguments(key: string, id: string | Array): Array { +export function transformArguments(key: string, id: string | Array): TransformArgumentsReply { return pushVerdictArguments(['XDEL', key], id); } diff --git a/lib/commands/ZDIFF.ts b/lib/commands/ZDIFF.ts index f557b597ec4..7154947fea7 100644 --- a/lib/commands/ZDIFF.ts +++ b/lib/commands/ZDIFF.ts @@ -1,10 +1,11 @@ +import { TransformArgumentsReply } from '.'; import { pushVerdictArgument, transformReplyStringArray } from './generic-transformers'; export const FIRST_KEY_INDEX = 2; export const IS_READ_ONLY = true; -export function transformArguments(keys: Array | string): Array { +export function transformArguments(keys: Array | string): TransformArgumentsReply { return pushVerdictArgument(['ZDIFF'], keys); } diff --git a/lib/commands/ZDIFFSTORE.ts b/lib/commands/ZDIFFSTORE.ts index de409c0939a..f91d4c869ba 100644 --- a/lib/commands/ZDIFFSTORE.ts +++ b/lib/commands/ZDIFFSTORE.ts @@ -1,8 +1,9 @@ +import { TransformArgumentsReply } from '.'; import { pushVerdictArgument, transformReplyNumber } from './generic-transformers'; export const FIRST_KEY_INDEX = 1; -export function transformArguments(destination: string, keys: Array | string): Array { +export function transformArguments(destination: string, keys: Array | string): TransformArgumentsReply { return pushVerdictArgument(['ZDIFFSTORE', destination], keys); } diff --git a/lib/commands/ZDIFF_WITHSCORES.ts b/lib/commands/ZDIFF_WITHSCORES.ts index 26effab7189..84126853361 100644 --- a/lib/commands/ZDIFF_WITHSCORES.ts +++ b/lib/commands/ZDIFF_WITHSCORES.ts @@ -1,9 +1,10 @@ +import { TransformArgumentsReply } from '.'; import { transformReplySortedSetWithScores } from './generic-transformers'; import { transformArguments as transformZDiffArguments } from './ZDIFF'; export { FIRST_KEY_INDEX, IS_READ_ONLY } from './ZDIFF'; -export function transformArguments(...args: Parameters): Array { +export function transformArguments(...args: Parameters): TransformArgumentsReply { return [ ...transformZDiffArguments(...args), 'WITHSCORES' diff --git a/lib/commands/ZINTER.ts b/lib/commands/ZINTER.ts index 90a42eda0d3..91d7982a8e7 100644 --- a/lib/commands/ZINTER.ts +++ b/lib/commands/ZINTER.ts @@ -1,3 +1,4 @@ +import { TransformArgumentsReply } from '.'; import { pushVerdictArgument, transformReplyStringArray } from './generic-transformers'; export const FIRST_KEY_INDEX = 2; @@ -9,7 +10,7 @@ interface ZInterOptions { AGGREGATE?: 'SUM' | 'MIN' | 'MAX'; } -export function transformArguments(keys: Array | string, options?: ZInterOptions): Array { +export function transformArguments(keys: Array | string, options?: ZInterOptions): TransformArgumentsReply { const args = pushVerdictArgument(['ZINTER'], keys); if (options?.WEIGHTS) { diff --git a/lib/commands/ZINTERSTORE.ts b/lib/commands/ZINTERSTORE.ts index a026916ce1f..6e79e423cb0 100644 --- a/lib/commands/ZINTERSTORE.ts +++ b/lib/commands/ZINTERSTORE.ts @@ -1,3 +1,4 @@ +import { TransformArgumentsReply } from '.'; import { pushVerdictArgument, transformReplyNumber } from './generic-transformers'; export const FIRST_KEY_INDEX = 1; @@ -7,7 +8,7 @@ interface ZInterStoreOptions { AGGREGATE?: 'SUM' | 'MIN' | 'MAX'; } -export function transformArguments(destination: string, keys: Array | string, options?: ZInterStoreOptions): Array { +export function transformArguments(destination: string, keys: Array | string, options?: ZInterStoreOptions): TransformArgumentsReply { const args = pushVerdictArgument(['ZINTERSTORE', destination], keys); if (options?.WEIGHTS) { diff --git a/lib/commands/ZINTER_WITHSCORES.ts b/lib/commands/ZINTER_WITHSCORES.ts index 0a82228fce9..f4287d1a684 100644 --- a/lib/commands/ZINTER_WITHSCORES.ts +++ b/lib/commands/ZINTER_WITHSCORES.ts @@ -1,9 +1,10 @@ +import { TransformArgumentsReply } from '.'; import { transformReplySortedSetWithScores } from './generic-transformers'; import { transformArguments as transformZInterArguments } from './ZINTER'; export { FIRST_KEY_INDEX, IS_READ_ONLY } from './ZINTER'; -export function transformArguments(...args: Parameters): Array { +export function transformArguments(...args: Parameters): TransformArgumentsReply { return [ ...transformZInterArguments(...args), 'WITHSCORES' diff --git a/lib/commands/ZMSCORE.ts b/lib/commands/ZMSCORE.ts index 8a6f73c7836..373adac3cf0 100644 --- a/lib/commands/ZMSCORE.ts +++ b/lib/commands/ZMSCORE.ts @@ -1,10 +1,11 @@ +import { TransformArgumentsReply } from '.'; import { pushVerdictArguments, transformReplyNumberInfinityNullArray } from './generic-transformers'; export const FIRST_KEY_INDEX = 1; export const IS_READ_ONLY = true; -export function transformArguments(key: string, member: string | Array): Array { +export function transformArguments(key: string, member: string | Array): TransformArgumentsReply { return pushVerdictArguments(['ZMSCORE', key], member); } diff --git a/lib/commands/ZREM.ts b/lib/commands/ZREM.ts index 089b6136afd..8419291f2fd 100644 --- a/lib/commands/ZREM.ts +++ b/lib/commands/ZREM.ts @@ -1,8 +1,9 @@ +import { TransformArgumentsReply } from '.'; import { pushVerdictArguments, transformReplyNumber } from './generic-transformers'; export const FIRST_KEY_INDEX = 1; -export function transformArguments(key: string, member: string | Array): Array { +export function transformArguments(key: string, member: string | Array): TransformArgumentsReply { return pushVerdictArguments(['ZREM', key], member); } diff --git a/lib/commands/ZUNION.ts b/lib/commands/ZUNION.ts index efdfccb1ff4..87158b8425a 100644 --- a/lib/commands/ZUNION.ts +++ b/lib/commands/ZUNION.ts @@ -1,3 +1,4 @@ +import { TransformArgumentsReply } from '.'; import { pushVerdictArgument, transformReplyStringArray } from './generic-transformers'; export const FIRST_KEY_INDEX = 2; @@ -9,7 +10,7 @@ interface ZUnionOptions { AGGREGATE?: 'SUM' | 'MIN' | 'MAX'; } -export function transformArguments(keys: Array | string, options?: ZUnionOptions): Array { +export function transformArguments(keys: Array | string, options?: ZUnionOptions): TransformArgumentsReply { const args = pushVerdictArgument(['ZUNION'], keys); if (options?.WEIGHTS) { diff --git a/lib/commands/ZUNIONSTORE.ts b/lib/commands/ZUNIONSTORE.ts index c03f1203706..4ebbdbd8591 100644 --- a/lib/commands/ZUNIONSTORE.ts +++ b/lib/commands/ZUNIONSTORE.ts @@ -1,3 +1,4 @@ +import { TransformArgumentsReply } from '.'; import { pushVerdictArgument, transformReplyNumber } from './generic-transformers'; export const FIRST_KEY_INDEX = 1; @@ -7,7 +8,7 @@ interface ZUnionOptions { AGGREGATE?: 'SUM' | 'MIN' | 'MAX'; } -export function transformArguments(destination: string, keys: Array | string, options?: ZUnionOptions): Array { +export function transformArguments(destination: string, keys: Array | string, options?: ZUnionOptions): TransformArgumentsReply { const args = pushVerdictArgument(['ZUNIONSTORE', destination], keys); if (options?.WEIGHTS) { diff --git a/lib/commands/ZUNION_WITHSCORES.ts b/lib/commands/ZUNION_WITHSCORES.ts index d0cef45cfb1..2215dad9749 100644 --- a/lib/commands/ZUNION_WITHSCORES.ts +++ b/lib/commands/ZUNION_WITHSCORES.ts @@ -1,9 +1,10 @@ +import { TransformArgumentsReply } from '.'; import { transformReplySortedSetWithScores } from './generic-transformers'; import { transformArguments as transformZUnionArguments } from './ZUNION'; export { FIRST_KEY_INDEX, IS_READ_ONLY } from './ZUNION'; -export function transformArguments(...args: Parameters): Array { +export function transformArguments(...args: Parameters): TransformArgumentsReply { return [ ...transformZUnionArguments(...args), 'WITHSCORES' diff --git a/lib/commands/generic-transformers.ts b/lib/commands/generic-transformers.ts index 8105bfe903f..496745cb1f1 100644 --- a/lib/commands/generic-transformers.ts +++ b/lib/commands/generic-transformers.ts @@ -20,6 +20,10 @@ export function transformReplyString(reply: string): string { return reply; } +export function transformReplyBuffer(reply: Buffer): Buffer { + return reply; +} + export function transformReplyStringNull(reply: string | null): string | null { return reply; } @@ -352,11 +356,11 @@ export function pushStringTuplesArguments(args: Array, tuples: StringTup return args; } -export function pushVerdictArguments(args: TransformArgumentsReply, value: string | Array): TransformArgumentsReply { - if (typeof value === 'string') { - args.push(value); - } else { +export function pushVerdictArguments(args: TransformArgumentsReply, value: string | Buffer | Array): TransformArgumentsReply { + if (Array.isArray(value)) { args.push(...value); + } else { + args.push(value); } return args; diff --git a/lib/commands/index.ts b/lib/commands/index.ts index cffb47c668a..dce28ac0937 100644 --- a/lib/commands/index.ts +++ b/lib/commands/index.ts @@ -61,6 +61,7 @@ import * as GEOPOS from './GEOPOS'; import * as GEOSEARCH_WITH from './GEOSEARCH_WITH'; import * as GEOSEARCH from './GEOSEARCH'; import * as GEOSEARCHSTORE from './GEOSEARCHSTORE'; +import * as GET_BUFFER from './GET_BUFFER'; import * as GET from './GET'; import * as GETBIT from './GETBIT'; import * as GETDEL from './GETDEL'; @@ -370,6 +371,8 @@ export default { geoSearch: GEOSEARCH, GEOSEARCHSTORE, geoSearchStore: GEOSEARCHSTORE, + GET_BUFFER, + getBuffer: GET_BUFFER, GET, get: GET, GETBIT, @@ -733,15 +736,16 @@ export default { zUnionStore: ZUNIONSTORE }; -export type RedisReply = string | number | Array | null | undefined; +export type RedisReply = string | number | Buffer | Array | null | undefined; -export type TransformArgumentsReply = Array & { preserve?: unknown }; +export type TransformArgumentsReply = Array & { preserve?: unknown }; export interface RedisCommand { FIRST_KEY_INDEX?: number | ((...args: Array) => string); IS_READ_ONLY?: boolean; transformArguments(...args: Array): TransformArgumentsReply; - transformReply(reply: RedisReply, preserved: unknown): any; + BUFFER_MODE?: boolean; + transformReply(reply: RedisReply, preserved?: unknown): any; } export interface RedisCommands { diff --git a/lib/multi-command.spec.ts b/lib/multi-command.spec.ts index a78cc8b2e08..52ecfb94b1c 100644 --- a/lib/multi-command.spec.ts +++ b/lib/multi-command.spec.ts @@ -1,6 +1,5 @@ import { strict as assert } from 'assert'; import RedisMultiCommand from './multi-command'; -import { encodeCommand } from './commander'; import { WatchError } from './errors'; import { spy } from 'sinon'; import { SQUARE_SCRIPT } from './client.spec'; @@ -10,11 +9,11 @@ describe('Multi Command', () => { it('simple', async () => { const multi = RedisMultiCommand.create((queue, symbol) => { assert.deepEqual( - queue.map(({encodedCommand}) => encodedCommand), + queue.map(({ args }) => args), [ - encodeCommand(['MULTI']), - encodeCommand(['PING']), - encodeCommand(['EXEC']), + ['MULTI'], + ['PING'], + ['EXEC'], ] ); @@ -55,8 +54,8 @@ describe('Multi Command', () => { it('execAsPipeline', async () => { const multi = RedisMultiCommand.create(queue => { assert.deepEqual( - queue.map(({encodedCommand}) => encodedCommand), - [encodeCommand(['PING'])] + queue.map(({ args }) => args), + [['PING']] ); return Promise.resolve(['PONG']); @@ -75,8 +74,8 @@ describe('Multi Command', () => { it('simple', async () => { const multi = RedisMultiCommand.create(queue => { assert.deepEqual( - queue.map(({encodedCommand}) => encodedCommand), - [encodeCommand(['PING'])] + queue.map(({ args }) => args), + [['PING']] ); return Promise.resolve(['PONG']); @@ -111,10 +110,10 @@ describe('Multi Command', () => { assert.deepEqual( await new MultiWithScript(queue => { assert.deepEqual( - queue.map(({encodedCommand}) => encodedCommand), + queue.map(({ args }) => args), [ - encodeCommand(['EVAL', SQUARE_SCRIPT.SCRIPT, '0', '2']), - encodeCommand(['EVALSHA', SQUARE_SCRIPT.SHA1, '0', '3']), + ['EVAL', SQUARE_SCRIPT.SCRIPT, '0', '2'], + ['EVALSHA', SQUARE_SCRIPT.SHA1, '0', '3'], ] ); diff --git a/lib/multi-command.ts b/lib/multi-command.ts index c8a50765967..53f439d8f36 100644 --- a/lib/multi-command.ts +++ b/lib/multi-command.ts @@ -2,7 +2,7 @@ import COMMANDS, { TransformArgumentsReply } from './commands'; import { RedisCommand, RedisModules, RedisReply } from './commands'; import { RedisLuaScript, RedisLuaScripts } from './lua-script'; import { RedisClientOptions } from './client'; -import { extendWithModulesAndScripts, extendWithDefaultCommands, encodeCommand } from './commander'; +import { extendWithModulesAndScripts, extendWithDefaultCommands } from './commander'; import { WatchError } from './errors'; type RedisMultiCommandSignature = (...args: Parameters) => RedisMultiCommandType; @@ -24,7 +24,7 @@ type WithScripts = { export type RedisMultiCommandType = RedisMultiCommand & WithCommands & WithModules & WithScripts; export interface MultiQueuedCommand { - encodedCommand: string; + args: TransformArgumentsReply; preservedArguments?: unknown; transformReply?: RedisCommand['transformReply']; } @@ -62,7 +62,9 @@ export default class RedisMultiCommand): this => { this.#queue.push({ - encodedCommand: encodeCommand(args.flat() as Array) + args: args.flat() as Array }); return this; } @@ -153,7 +155,7 @@ export default class RedisMultiCommand); diff --git a/lib/socket.ts b/lib/socket.ts index 66cd28d91d5..23daee14c37 100644 --- a/lib/socket.ts +++ b/lib/socket.ts @@ -91,10 +91,8 @@ export default class RedisSocket extends EventEmitter { return this.#isOpen; } - get chunkRecommendedSize(): number { - if (!this.#socket) return 0; - - return this.#socket.writableHighWaterMark - this.#socket.writableLength; + get isSocketExists(): boolean { + return !!this.#socket; } constructor(initiator?: RedisSocketInitiator, options?: RedisSocketOptions) { @@ -214,12 +212,12 @@ export default class RedisSocket extends EventEmitter { .catch(err => this.emit('error', err)); } - write(encodedCommands: string): boolean { + write(toWrite: string | Buffer): boolean { if (!this.#socket) { throw new ClientClosedError(); } - return this.#socket.write(encodedCommands); + return this.#socket.write(toWrite); } async disconnect(ignoreIsOpen = false): Promise { @@ -251,4 +249,22 @@ export default class RedisSocket extends EventEmitter { throw err; } } + + #isCorked = false; + + cork(): void { + if (!this.#socket) { + return; + } + + if (!this.#isCorked) { + this.#socket.cork(); + this.#isCorked = true; + + queueMicrotask(() => { + this.#socket?.uncork(); + this.#isCorked = false; + }); + } + } } diff --git a/lib/ts-declarations/cluster-key-slot.d.ts b/lib/ts-declarations/cluster-key-slot.d.ts index 5774c50fbd4..60421de296b 100644 --- a/lib/ts-declarations/cluster-key-slot.d.ts +++ b/lib/ts-declarations/cluster-key-slot.d.ts @@ -1,3 +1,3 @@ declare module 'cluster-key-slot' { - export default function calculateSlot(key: string): number; + export default function calculateSlot(key: string | Buffer): number; } diff --git a/lib/ts-declarations/redis-parser.d.ts b/lib/ts-declarations/redis-parser.d.ts index 68659616b93..7ec129ed8cd 100644 --- a/lib/ts-declarations/redis-parser.d.ts +++ b/lib/ts-declarations/redis-parser.d.ts @@ -8,6 +8,8 @@ declare module 'redis-parser' { export default class RedisParser { constructor(callbacks: RedisParserCallbacks); + setReturnBuffers(returnBuffers?: boolean): void; + execute(buffer: Buffer): void; } } From 9fc08d449ce357a8fd2f00187e993670be310cff Mon Sep 17 00:00:00 2001 From: leibale Date: Mon, 13 Sep 2021 19:55:37 -0400 Subject: [PATCH 0860/1748] fix GET and GET_BUFFER return type --- lib/commands/GET.ts | 4 ++-- lib/commands/GET_BUFFER.ts | 4 ++-- lib/commands/generic-transformers.ts | 8 ++++---- 3 files changed, 8 insertions(+), 8 deletions(-) diff --git a/lib/commands/GET.ts b/lib/commands/GET.ts index 6c6475a9d24..541790e54e4 100644 --- a/lib/commands/GET.ts +++ b/lib/commands/GET.ts @@ -1,5 +1,5 @@ import { TransformArgumentsReply } from '.'; -import { transformReplyString } from './generic-transformers'; +import { transformReplyStringNull } from './generic-transformers'; export const FIRST_KEY_INDEX = 1; @@ -9,4 +9,4 @@ export function transformArguments(key: string | Buffer): TransformArgumentsRepl return ['GET', key]; } -export const transformReply = transformReplyString; +export const transformReply = transformReplyStringNull; diff --git a/lib/commands/GET_BUFFER.ts b/lib/commands/GET_BUFFER.ts index 3d6f454898b..9d281961130 100644 --- a/lib/commands/GET_BUFFER.ts +++ b/lib/commands/GET_BUFFER.ts @@ -1,7 +1,7 @@ -import { transformReplyBuffer } from './generic-transformers'; +import { transformReplyBufferNull } from './generic-transformers'; export { FIRST_KEY_INDEX, IS_READ_ONLY, transformArguments } from './GET'; export const BUFFER_MODE = true; -export const transformReply = transformReplyBuffer; +export const transformReply = transformReplyBufferNull; diff --git a/lib/commands/generic-transformers.ts b/lib/commands/generic-transformers.ts index 496745cb1f1..bbc12ee113e 100644 --- a/lib/commands/generic-transformers.ts +++ b/lib/commands/generic-transformers.ts @@ -20,10 +20,6 @@ export function transformReplyString(reply: string): string { return reply; } -export function transformReplyBuffer(reply: Buffer): Buffer { - return reply; -} - export function transformReplyStringNull(reply: string | null): string | null { return reply; } @@ -54,6 +50,10 @@ export function transformReplyBit(reply: BitValue): BitValue { return reply; } +export function transformReplyBufferNull(reply: Buffer | null): Buffer | null { + return reply; +} + export function transformReplyVoid(): void {} export interface ScanOptions { From b91897acdbc3c38c63013aaca0a6cc8bc240c162 Mon Sep 17 00:00:00 2001 From: leibale Date: Mon, 13 Sep 2021 20:05:29 -0400 Subject: [PATCH 0861/1748] update FAQ --- docs/FAQ.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/docs/FAQ.md b/docs/FAQ.md index b5074e73025..cfdb2ecaf42 100644 --- a/docs/FAQ.md +++ b/docs/FAQ.md @@ -8,6 +8,6 @@ When a socket closed unexpectedly, all the commands that were already sent will ## How are commands batched? -Commands are pipelined using [`queueMicrotask`](https://nodejs.org/api/globals.html#globals_queuemicrotask_callback). Commands from the same "tick" will be sent in batches and respect the [`writableHighWaterMark`](https://nodejs.org/api/stream.html#stream_new_stream_writable_options). +Commands are pipelined using [`queueMicrotask`](https://nodejs.org/api/globals.html#globals_queuemicrotask_callback). If `socket.write()` returns `false`—meaning that ["all or part of the data was queued in user memory"](https://nodejs.org/api/net.html#net_socket_write_data_encoding_callback:~:text=all%20or%20part%20of%20the%20data%20was%20queued%20in%20user%20memory)—the commands will stack in memory until the [`drain`](https://nodejs.org/api/net.html#net_event_drain) event is fired. From 64f456767e489d2817ab58a5b86b6604d4eb339d Mon Sep 17 00:00:00 2001 From: Richard Samuelsson Date: Tue, 14 Sep 2021 04:08:11 +0200 Subject: [PATCH 0862/1748] Update invalid code example in README.md (#1654) * Update invalid code example in README.md * Update README.md Co-authored-by: Leibale Eidelman --- README.md | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/README.md b/README.md index db0fa71cc7f..bbcb7edc976 100644 --- a/README.md +++ b/README.md @@ -111,8 +111,7 @@ await client.set('another-key', 'another-value'); const [ setKeyReply, otherKeyValue ] = await client.multi() .set('key', 'value') .get('another-key') - .exec() -]); // ['OK', 'another-value'] + .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. From 0f5a2784974589e8ef54603b6dacaf72d2cc6335 Mon Sep 17 00:00:00 2001 From: leibale Date: Tue, 14 Sep 2021 11:09:31 -0400 Subject: [PATCH 0863/1748] fix #1652 --- lib/client.spec.ts | 3 +++ lib/commands-queue.ts | 17 +++++++---------- 2 files changed, 10 insertions(+), 10 deletions(-) diff --git a/lib/client.spec.ts b/lib/client.spec.ts index 9f18e184c88..7f1a534352c 100644 --- a/lib/client.spec.ts +++ b/lib/client.spec.ts @@ -516,6 +516,9 @@ describe('Client', () => { assert.ok(channelListener1.calledOnce); assert.ok(channelListener2.calledTwice); assert.ok(patternListener.calledThrice); + + // should be able to send commands when unsubsribed from all channels (see #1652) + await assert.doesNotReject(subscriber.ping()); } finally { await subscriber.disconnect(); } diff --git a/lib/commands-queue.ts b/lib/commands-queue.ts index 27c83965529..ef87184193f 100644 --- a/lib/commands-queue.ts +++ b/lib/commands-queue.ts @@ -171,8 +171,9 @@ export default class RedisCommandsQueue { unsubscribe(command: PubSubUnsubscribeCommands, channels?: string | Array, listener?: PubSubListener): Promise { const listeners = command === PubSubUnsubscribeCommands.UNSUBSCRIBE ? this.#pubSubListeners.channels : this.#pubSubListeners.patterns; if (!channels) { + const size = listeners.size; listeners.clear(); - return this.#pushPubSubCommand(command); + return this.#pushPubSubCommand(command, size); } const channelsToUnsubscribe = []; @@ -199,22 +200,18 @@ export default class RedisCommandsQueue { return this.#pushPubSubCommand(command, channelsToUnsubscribe); } - #pushPubSubCommand(command: PubSubSubscribeCommands | PubSubUnsubscribeCommands, channels?: Array): Promise { + #pushPubSubCommand(command: PubSubSubscribeCommands | PubSubUnsubscribeCommands, channels: number | Array): Promise { return new Promise((resolve, reject) => { const isSubscribe = command === PubSubSubscribeCommands.SUBSCRIBE || command === PubSubSubscribeCommands.PSUBSCRIBE, inProgressKey = isSubscribe ? 'subscribing' : 'unsubscribing', commandArgs: Array = [command]; + let channelsCounter: number; - if (channels?.length) { + if (typeof channels === 'number') { // unsubscribe only + channelsCounter = channels; + } else { commandArgs.push(...channels); channelsCounter = channels.length; - } else { - // unsubscribe only - channelsCounter = ( - command[0] === 'P' ? - this.#pubSubListeners.patterns : - this.#pubSubListeners.channels - ).size; } this.#pubSubState[inProgressKey] += channelsCounter; From 0ab224504961212fe44cf3972c7a2902346e1627 Mon Sep 17 00:00:00 2001 From: leibale Date: Sat, 18 Sep 2021 05:52:54 -0400 Subject: [PATCH 0864/1748] ref #1653 - better types --- lib/client.ts | 164 ++++++++++++++-------------- lib/cluster.ts | 89 +++++++-------- lib/commands/GEOSEARCHSTORE.spec.ts | 2 +- lib/commands/GETEX.ts | 3 +- lib/commands/index.ts | 7 +- lib/lua-script.ts | 4 +- lib/multi-command.ts | 89 ++++++++------- lib/test-utils.ts | 8 +- 8 files changed, 176 insertions(+), 190 deletions(-) diff --git a/lib/client.ts b/lib/client.ts index aaa982da1cc..c9e9cecf924 100644 --- a/lib/client.ts +++ b/lib/client.ts @@ -13,7 +13,7 @@ import { encodeCommand, extendWithDefaultCommands, extendWithModulesAndScripts, import { Pool, Options as PoolOptions, createPool } from 'generic-pool'; import { ClientClosedError } from './errors'; -export interface RedisClientOptions { +export interface RedisClientOptions { socket?: RedisSocketOptions; modules?: M; scripts?: S; @@ -43,51 +43,25 @@ type WithScripts = { export type WithPlugins = WithCommands & WithModules & WithScripts; -export type RedisClientType = +export type RedisClientType = WithPlugins & RedisClient; export interface ClientCommandOptions extends QueueCommandOptions { isolated?: boolean; } -export default class RedisClient extends EventEmitter { +export default class RedisClient extends EventEmitter { static commandOptions(options: ClientCommandOptions): CommandOptions { return commandOptions(options); } - static async commandsExecutor( - this: RedisClient, - command: RedisCommand, - args: Array - ): Promise> { - const { args: redisArgs, options } = transformCommandArguments(command, args); - - return command.transformReply( - await this.#sendCommand(redisArgs, options, command.BUFFER_MODE), - redisArgs.preserve, - ); - } - - static async #scriptsExecutor( - this: RedisClient, - script: RedisLuaScript, - args: Array - ): Promise { - const { args: redisArgs, options } = transformCommandArguments(script, args); - - return script.transformReply( - await this.executeScript(script, redisArgs, options, script.BUFFER_MODE), - redisArgs.preserve - ); - } - - static create(options?: RedisClientOptions): RedisClientType { + static create(options?: RedisClientOptions): RedisClientType { const Client = (extendWithModulesAndScripts({ BaseClass: RedisClient, modules: options?.modules, - modulesCommandsExecutor: RedisClient.commandsExecutor, + modulesCommandsExecutor: RedisClient.prototype.commandsExecutor, scripts: options?.scripts, - scriptsExecutor: RedisClient.#scriptsExecutor + scriptsExecutor: RedisClient.prototype.scriptsExecutor })); if (Client !== RedisClient) { @@ -104,7 +78,7 @@ export default class RedisClient = {}; #selectedDB = 0; - get options(): RedisClientOptions | null | undefined { + get options(): RedisClientOptions | undefined { return this.#options; } @@ -240,6 +214,72 @@ export default class RedisClient): Promise> { + const { args: redisArgs, options } = transformCommandArguments(command, args); + + return command.transformReply( + await this.#sendCommand(redisArgs, options, command.BUFFER_MODE), + redisArgs.preserve, + ); + } + + sendCommand(args: TransformArgumentsReply, options?: ClientCommandOptions, bufferMode?: boolean): Promise { + return this.#sendCommand(args, options, bufferMode); + } + + // using `#sendCommand` cause `sendCommand` is overwritten in legacy mode + async #sendCommand(args: TransformArgumentsReply, options?: ClientCommandOptions, bufferMode?: boolean): Promise { + if (!this.#socket.isOpen) { + throw new ClientClosedError(); + } + + if (options?.isolated) { + return this.executeIsolated(isolatedClient => + isolatedClient.sendCommand(args, { + ...options, + isolated: false + }) + ); + } + + const promise = this.#queue.addCommand(args, options, bufferMode); + this.#tick(); + return await promise; + } + + async scriptsExecutor(script: RedisLuaScript, args: Array): Promise> { + const { args: redisArgs, options } = transformCommandArguments(script, args); + + return script.transformReply( + await this.executeScript(script, redisArgs, options, script.BUFFER_MODE), + redisArgs.preserve + ); + } + + async executeScript(script: RedisLuaScript, args: TransformArgumentsReply, options?: ClientCommandOptions, bufferMode?: boolean): Promise> { + try { + return await this.#sendCommand([ + 'EVALSHA', + script.SHA1, + script.NUMBER_OF_KEYS.toString(), + ...args + ], options, bufferMode); + } catch (err: any) { + if (!err?.message?.startsWith?.('NOSCRIPT')) { + throw err; + } + + return await this.#sendCommand([ + 'EVAL', + script.SCRIPT, + script.NUMBER_OF_KEYS.toString(), + ...args + ], options, bufferMode); + } + } + + + async SELECT(db: number): Promise; async SELECT(options: CommandOptions, db: number): Promise; async SELECT(options?: any, db?: any): Promise { @@ -300,30 +340,6 @@ export default class RedisClient(args: TransformArgumentsReply, options?: ClientCommandOptions, bufferMode?: boolean): Promise { - return this.#sendCommand(args, options, bufferMode); - } - - // using `#sendCommand` cause `sendCommand` is overwritten in legacy mode - async #sendCommand(args: TransformArgumentsReply, options?: ClientCommandOptions, bufferMode?: boolean): Promise { - if (!this.#socket.isOpen) { - throw new ClientClosedError(); - } - - if (options?.isolated) { - return this.executeIsolated(isolatedClient => - isolatedClient.sendCommand(args, { - ...options, - isolated: false - }) - ); - } - - const promise = this.#queue.addCommand(args, options, bufferMode); - this.#tick(); - return await promise; - } - #tick(): void { if (!this.#socket.isSocketExists) { return; @@ -350,26 +366,11 @@ export default class RedisClient> { - try { - return await this.#sendCommand([ - 'EVALSHA', - script.SHA1, - script.NUMBER_OF_KEYS.toString(), - ...args - ], options, bufferMode); - } catch (err: any) { - if (!err?.message?.startsWith?.('NOSCRIPT')) { - throw err; - } - - return await this.#sendCommand([ - 'EVAL', - script.SCRIPT, - script.NUMBER_OF_KEYS.toString(), - ...args - ], options, bufferMode); - } + multi(): RedisMultiCommandType { + return new (this as any).Multi( + this.#multiExecutor.bind(this), + this.#options + ); } #multiExecutor(commands: Array, chainId?: symbol): Promise> { @@ -386,13 +387,6 @@ export default class RedisClient { - return new (this as any).Multi( - this.#multiExecutor.bind(this), - this.#options - ); - } - async* scanIterator(options?: ScanCommandOptions): AsyncIterable { let cursor = 0; do { @@ -451,5 +445,5 @@ export default class RedisClient { maxCommandRedirections?: number; } -export type RedisClusterType = - WithPlugins & RedisCluster; +export type RedisClusterType = + WithPlugins & RedisCluster; -export default class RedisCluster extends EventEmitter { +export default class RedisCluster extends EventEmitter { static #extractFirstKey(command: RedisCommand, originalArgs: Array, redisArgs: TransformArgumentsReply): string | Buffer | undefined { if (command.FIRST_KEY_INDEX === undefined) { return undefined; @@ -30,54 +30,13 @@ export default class RedisCluster - ): Promise> { - const { args: redisArgs, options } = transformCommandArguments(command, args); - - const reply = command.transformReply( - await this.sendCommand( - RedisCluster.#extractFirstKey(command, args, redisArgs), - command.IS_READ_ONLY, - redisArgs, - options, - command.BUFFER_MODE - ), - redisArgs.preserve - ); - - return reply; - } - - static async #scriptsExecutor( - this: RedisCluster, - script: RedisLuaScript, - args: Array - ): Promise { - const { args: redisArgs, options } = transformCommandArguments(script, args); - - const reply = script.transformReply( - await this.executeScript( - script, - args, - redisArgs, - options - ), - redisArgs.preserve - ); - - return reply; - } - - static create(options?: RedisClusterOptions): RedisClusterType { + static create(options?: RedisClusterOptions): RedisClusterType { return new (extendWithModulesAndScripts({ BaseClass: RedisCluster, modules: options?.modules, - modulesCommandsExecutor: RedisCluster.commandsExecutor, + modulesCommandsExecutor: RedisCluster.prototype.commandsExecutor, scripts: options?.scripts, - scriptsExecutor: RedisCluster.#scriptsExecutor + scriptsExecutor: RedisCluster.prototype.scriptsExecutor }))(options); } @@ -101,6 +60,23 @@ export default class RedisCluster): Promise> { + const { args: redisArgs, options } = transformCommandArguments(command, args); + + const reply = command.transformReply( + await this.sendCommand( + RedisCluster.#extractFirstKey(command, args, redisArgs), + command.IS_READ_ONLY, + redisArgs, + options, + command.BUFFER_MODE + ), + redisArgs.preserve + ); + + return reply; + } + async sendCommand( firstKey: string | Buffer | undefined, isReadonly: boolean | undefined, @@ -125,6 +101,22 @@ export default class RedisCluster): Promise> { + const { args: redisArgs, options } = transformCommandArguments(script, args); + + const reply = script.transformReply( + await this.executeScript( + script, + args, + redisArgs, + options + ), + redisArgs.preserve + ); + + return reply; + } + async executeScript( script: RedisLuaScript, originalArgs: Array, @@ -208,5 +200,4 @@ export default class RedisCluster { describe('transformArguments', () => { it('simple', () => { assert.deepEqual( - transformArguments('destination', 'source', 'member', { + transformArguments('destination', '/home/leibale/Workspace/node-redis/lib/commands/GEOSEARCHSTORE.spec.tssource', 'member', { radius: 1, unit: 'm' }, { diff --git a/lib/commands/GETEX.ts b/lib/commands/GETEX.ts index ca1465b7ee5..214dae5c7ab 100644 --- a/lib/commands/GETEX.ts +++ b/lib/commands/GETEX.ts @@ -1,3 +1,4 @@ +import { TransformArgumentsReply } from '.'; import { transformEXAT, transformPXAT, transformReplyStringNull } from './generic-transformers'; export const FIRST_KEY_INDEX = 1; @@ -14,7 +15,7 @@ type GetExModes = { PERSIST: true; }; -export function transformArguments(key: string, mode: GetExModes) { +export function transformArguments(key: string, mode: GetExModes): TransformArgumentsReply { const args = ['GETEX', key]; if ('EX' in mode) { diff --git a/lib/commands/index.ts b/lib/commands/index.ts index dce28ac0937..6e5310b811a 100644 --- a/lib/commands/index.ts +++ b/lib/commands/index.ts @@ -753,7 +753,10 @@ export interface RedisCommands { } export interface RedisModule { - [key: string]: RedisCommand; + [command: string]: RedisCommand; } -export type RedisModules = Record; +export interface RedisModules { + [module: string]: RedisModule; +} +// export type RedisModules = Record; diff --git a/lib/lua-script.ts b/lib/lua-script.ts index 183c42f219c..be16f9b9133 100644 --- a/lib/lua-script.ts +++ b/lib/lua-script.ts @@ -13,10 +13,10 @@ export interface SHA1 { export type RedisLuaScript = RedisLuaScriptConfig & SHA1; export interface RedisLuaScripts { - [key: string]: RedisLuaScript; + [script: string]: RedisLuaScript; } -export function defineScript(script: S): S & SHA1 { +export function defineScript(script: RedisLuaScriptConfig): typeof script & SHA1 { return { ...script, SHA1: scriptSha1(script.SCRIPT) diff --git a/lib/multi-command.ts b/lib/multi-command.ts index 53f439d8f36..a329a5dbf19 100644 --- a/lib/multi-command.ts +++ b/lib/multi-command.ts @@ -21,7 +21,8 @@ type WithScripts = { [P in keyof S]: RedisMultiCommandSignature }; -export type RedisMultiCommandType = RedisMultiCommand & WithCommands & WithModules & WithScripts; +export type RedisMultiCommandType = + RedisMultiCommand & WithCommands & WithModules & WithScripts; export interface MultiQueuedCommand { args: TransformArgumentsReply; @@ -31,60 +32,20 @@ export interface MultiQueuedCommand { export type RedisMultiExecutor = (queue: Array, chainId?: symbol) => Promise>; -export default class RedisMultiCommand { - static commandsExecutor(this: RedisMultiCommand, command: RedisCommand, args: Array): RedisMultiCommand { - return this.addCommand( - command.transformArguments(...args), - command.transformReply - ); - } - - static #scriptsExecutor( - this: RedisMultiCommand, - script: RedisLuaScript, - args: Array - ): RedisMultiCommand { - const transformedArguments: TransformArgumentsReply = []; - if (this.#scriptsInUse.has(script.SHA1)) { - transformedArguments.push( - 'EVALSHA', - script.SHA1 - ); - } else { - this.#scriptsInUse.add(script.SHA1); - transformedArguments.push( - 'EVAL', - script.SCRIPT - ); - } - - transformedArguments.push(script.NUMBER_OF_KEYS.toString()); - - const scriptArguments = script.transformArguments(...args); - transformedArguments.push(...scriptArguments); - if (scriptArguments.preserve) { - transformedArguments.preserve = scriptArguments.preserve; - } - - return this.addCommand( - transformedArguments, - script.transformReply - ); - } - +export default class RedisMultiCommand { static extend( clientOptions?: RedisClientOptions ): new (...args: ConstructorParameters) => RedisMultiCommandType { return extendWithModulesAndScripts({ BaseClass: RedisMultiCommand, modules: clientOptions?.modules, - modulesCommandsExecutor: RedisMultiCommand.commandsExecutor, + modulesCommandsExecutor: RedisMultiCommand.prototype.commandsExecutor, scripts: clientOptions?.scripts, - scriptsExecutor: RedisMultiCommand.#scriptsExecutor + scriptsExecutor: RedisMultiCommand.prototype.scriptsExecutor }); } - static create( + static create( executor: RedisMultiExecutor, clientOptions?: RedisClientOptions ): RedisMultiCommandType { @@ -153,6 +114,42 @@ export default class RedisMultiCommand): void => (this as any).addCommand(name, args); } + commandsExecutor(command: RedisCommand, args: Array): this { + return this.addCommand( + command.transformArguments(...args), + command.transformReply + ); + } + + scriptsExecutor(script: RedisLuaScript, args: Array): this { + const transformedArguments: TransformArgumentsReply = []; + if (this.#scriptsInUse.has(script.SHA1)) { + transformedArguments.push( + 'EVALSHA', + script.SHA1 + ); + } else { + this.#scriptsInUse.add(script.SHA1); + transformedArguments.push( + 'EVAL', + script.SCRIPT + ); + } + + transformedArguments.push(script.NUMBER_OF_KEYS.toString()); + + const scriptArguments = script.transformArguments(...args); + transformedArguments.push(...scriptArguments); + if (scriptArguments.preserve) { + transformedArguments.preserve = scriptArguments.preserve; + } + + return this.addCommand( + transformedArguments, + script.transformReply + ); + } + addCommand(args: TransformArgumentsReply, transformReply?: RedisCommand['transformReply']): this { this.#queue.push({ args, @@ -205,4 +202,4 @@ export default class RedisMultiCommand): Promise { const SLOTS = 16384; interface SpawnRedisClusterNodeResult extends SpawnRedisServerResult { - client: RedisClientType + client: RedisClientType } async function spawnRedisClusterNode( @@ -281,7 +281,7 @@ export function describeHandleMinimumRedisVersion(minimumVersion: PartialRedisVe export function itWithClient( type: TestRedisServers, title: string, - fn: (client: RedisClientType) => Promise, + fn: (client: RedisClientType) => Promise, options?: RedisTestOptions ): void { it(title, async function () { @@ -306,7 +306,7 @@ export function itWithClient( export function itWithCluster( type: TestRedisClusters, title: string, - fn: (cluster: RedisClusterType) => Promise, + fn: (cluster: RedisClusterType) => Promise, options?: RedisTestOptions ): void { it(title, async function () { @@ -328,7 +328,7 @@ export function itWithCluster( }); } -export function itWithDedicatedCluster(title: string, fn: (cluster: RedisClusterType) => Promise): void { +export function itWithDedicatedCluster(title: string, fn: (cluster: RedisClusterType) => Promise): void { it(title, async function () { this.timeout(10000); From 54124793ad1b633d39d372bdff487c9888c017a7 Mon Sep 17 00:00:00 2001 From: leibale Date: Sat, 18 Sep 2021 05:58:08 -0400 Subject: [PATCH 0865/1748] better types --- lib/commands/SETBIT.ts | 3 ++- lib/commands/index.ts | 4 ++-- lib/test-utils.ts | 4 +--- 3 files changed, 5 insertions(+), 6 deletions(-) diff --git a/lib/commands/SETBIT.ts b/lib/commands/SETBIT.ts index 0cd41d1b975..33b2ff1a838 100644 --- a/lib/commands/SETBIT.ts +++ b/lib/commands/SETBIT.ts @@ -1,8 +1,9 @@ +import { TransformArgumentsReply } from '.'; import { BitValue, transformReplyBit } from './generic-transformers'; export const FIRST_KEY_INDEX = 1; -export function transformArguments(key: string, offset: number, value: BitValue) { +export function transformArguments(key: string, offset: number, value: BitValue): TransformArgumentsReply { return ['SETBIT', key, offset.toString(), value.toString()]; } diff --git a/lib/commands/index.ts b/lib/commands/index.ts index 6e5310b811a..2c1a02d224f 100644 --- a/lib/commands/index.ts +++ b/lib/commands/index.ts @@ -743,9 +743,9 @@ export type TransformArgumentsReply = Array & { preserve?: unkn export interface RedisCommand { FIRST_KEY_INDEX?: number | ((...args: Array) => string); IS_READ_ONLY?: boolean; - transformArguments(...args: Array): TransformArgumentsReply; + transformArguments(this: void, zpte...args: Array): TransformArgumentsReply; BUFFER_MODE?: boolean; - transformReply(reply: RedisReply, preserved?: unknown): any; + transformReply(this: void, reply: RedisReply, preserved?: unknown): any; } export interface RedisCommands { diff --git a/lib/test-utils.ts b/lib/test-utils.ts index f68857d61e6..84685923693 100644 --- a/lib/test-utils.ts +++ b/lib/test-utils.ts @@ -1,7 +1,5 @@ import { strict as assert } from 'assert'; import RedisClient, { RedisClientType } from './client'; -import { RedisModules } from './commands'; -import { RedisLuaScripts } from './lua-script'; import { execSync, spawn } from 'child_process'; import { once } from 'events'; import { RedisSocketOptions } from './socket'; @@ -370,4 +368,4 @@ export async function waitTillBeenCalled(spy: SinonSpy): Promise { await promiseTimeout(1); } while (spy.callCount === calls) -} \ No newline at end of file +} From 10b9c59e0ffe2f2bfdd926080bf4c92a516cedd4 Mon Sep 17 00:00:00 2001 From: leibale Date: Sat, 18 Sep 2021 06:06:17 -0400 Subject: [PATCH 0866/1748] fix 54124793ad1b633d39d372bdff487c9888c017a7 --- lib/commands/index.ts | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/lib/commands/index.ts b/lib/commands/index.ts index 2c1a02d224f..93220630980 100644 --- a/lib/commands/index.ts +++ b/lib/commands/index.ts @@ -743,7 +743,7 @@ export type TransformArgumentsReply = Array & { preserve?: unkn export interface RedisCommand { FIRST_KEY_INDEX?: number | ((...args: Array) => string); IS_READ_ONLY?: boolean; - transformArguments(this: void, zpte...args: Array): TransformArgumentsReply; + transformArguments(this: void, ...args: Array): TransformArgumentsReply; BUFFER_MODE?: boolean; transformReply(this: void, reply: RedisReply, preserved?: unknown): any; } From 3cd31e37c2e9f6c763324b66ac71b3d55702e29e Mon Sep 17 00:00:00 2001 From: Leibale Eidelman Date: Sat, 18 Sep 2021 13:55:45 -0400 Subject: [PATCH 0867/1748] Update GEOSEARCHSTORE.spec.ts --- lib/commands/GEOSEARCHSTORE.spec.ts | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/lib/commands/GEOSEARCHSTORE.spec.ts b/lib/commands/GEOSEARCHSTORE.spec.ts index b39e94fc40c..ad33c62b78c 100644 --- a/lib/commands/GEOSEARCHSTORE.spec.ts +++ b/lib/commands/GEOSEARCHSTORE.spec.ts @@ -8,7 +8,7 @@ describe('GEOSEARCHSTORE', () => { describe('transformArguments', () => { it('simple', () => { assert.deepEqual( - transformArguments('destination', '/home/leibale/Workspace/node-redis/lib/commands/GEOSEARCHSTORE.spec.tssource', 'member', { + transformArguments('destination', 'source', 'member', { radius: 1, unit: 'm' }, { From 3a169d5e35b73b4c35f4df37781737d405699030 Mon Sep 17 00:00:00 2001 From: leibale Date: Mon, 20 Sep 2021 18:59:42 -0400 Subject: [PATCH 0868/1748] fix #1660 - add support for client.HSET('key', 'field', 'value') --- lib/commands/HSET.spec.ts | 9 ++++++++- lib/commands/HSET.ts | 13 +++++++++++-- 2 files changed, 19 insertions(+), 3 deletions(-) diff --git a/lib/commands/HSET.spec.ts b/lib/commands/HSET.spec.ts index af7bcb6eb20..601e7f967e1 100644 --- a/lib/commands/HSET.spec.ts +++ b/lib/commands/HSET.spec.ts @@ -4,6 +4,13 @@ import { TestRedisServers, itWithClient, TestRedisClusters, itWithCluster } from describe('HSET', () => { describe('transformArguments', () => { + it('field, value', () => { + assert.deepEqual( + transformArguments('key', 'field', 'value'), + ['HSET', 'key', 'field', 'value'] + ); + }); + it('Map', () => { assert.deepEqual( transformArguments('key', new Map([['field', 'value']])), @@ -30,7 +37,7 @@ describe('HSET', () => { itWithClient(TestRedisServers.OPEN, 'client.hSet', async client => { assert.equal( - await client.hSet('key', { field: 'value' }), + await client.hSet('key', 'field', 'value'), 1 ); }); diff --git a/lib/commands/HSET.ts b/lib/commands/HSET.ts index 3edaa64b4e8..cbd46061ad8 100644 --- a/lib/commands/HSET.ts +++ b/lib/commands/HSET.ts @@ -1,3 +1,4 @@ +import { TransformArgumentsReply } from '.'; import { transformReplyString } from './generic-transformers'; type HSETObject = Record; @@ -8,10 +9,18 @@ type HSETTuples = Array<[string, string]> | Array; export const FIRST_KEY_INDEX = 1; -export function transformArguments(key: string, value: HSETObject | HSETMap | HSETTuples): Array { +type GenericArguments = [key: string]; + +type SingleFieldArguments = [...generic: GenericArguments, field: string, value: string]; + +type MultipleFieldsArguments = [...generic: GenericArguments, value: HSETObject | HSETMap | HSETTuples]; + +export function transformArguments(...[ key, value, fieldValue ]: SingleFieldArguments | MultipleFieldsArguments): TransformArgumentsReply { const args = ['HSET', key]; - if (value instanceof Map) { + if (typeof value === 'string') { + args.push(value, fieldValue!); + } else if (value instanceof Map) { pushMap(args, value); } else if (Array.isArray(value)) { pushTuples(args, value); From d79bc55df6666c94cc44812aa861a6b208d56336 Mon Sep 17 00:00:00 2001 From: leibale Date: Mon, 20 Sep 2021 19:35:13 -0400 Subject: [PATCH 0869/1748] upgrade dependencies, update README --- README.md | 186 +++++----- package-lock.json | 856 ++++++++++++++++++++++++---------------------- package.json | 10 +- 3 files changed, 541 insertions(+), 511 deletions(-) diff --git a/README.md b/README.md index bbcb7edc976..2c465ae69fa 100644 --- a/README.md +++ b/README.md @@ -35,17 +35,17 @@ npm install redis@next ### Basic Example ```typescript -import { createClient } from 'redis'; +import { createClient } from "redis"; (async () => { - const client = createClient(); + const client = createClient(); - client.on('error', (err) => console.log('Redis Client Error', err)); + client.on("error", (err) => console.log("Redis Client Error", err)); - await client.connect(); + await client.connect(); - await client.set('key', 'value'); - const value = await client.get('key'); + await client.set("key", "value"); + const value = await client.get("key"); })(); ``` @@ -53,9 +53,9 @@ The above code connects to localhost on port 6379. To connect to a different hos ```typescript createClient({ - socket: { - url: 'redis://alice:foobared@awesome.redis.server:6380' - } + socket: { + url: "redis://alice:foobared@awesome.redis.server:6380", + }, }); ``` @@ -67,28 +67,28 @@ There is built-in support for all of the [out-of-the-box Redis commands](https:/ ```typescript // raw Redis commands -await client.HSET('key', 'field', 'value'); -await client.HGETALL('key'); +await client.HSET("key", "field", "value"); +await client.HGETALL("key"); // friendly JavaScript commands -await client.hSet('key', 'field', 'value'); -await client.hGetAll('key'); +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 +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'] +await client.hGetAll("key"); // { field1: 'value1', field2: 'value2' } +await client.hVals("key"); // ['value1', 'value2'] ``` ### Unsupported Redis Commands @@ -96,9 +96,9 @@ await client.hVals('key'); // ['value1', 'value2'] 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(["SET", "key", "value", "NX"]); // 'OK' -await client.sendCommand(['HGETALL', 'key']); // ['key1', 'field1', 'key2', 'field2'] +await client.sendCommand(["HGETALL", "key"]); // ['key1', 'field1', 'key2', 'field2'] ``` ### Transactions (Multi/Exec) @@ -106,12 +106,13 @@ await client.sendCommand(['HGETALL', 'key']); // ['key1', 'field1', 'key2', 'fie 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'); +await client.set("another-key", "another-value"); -const [ setKeyReply, otherKeyValue ] = await client.multi() - .set('key', 'value') - .get('another-key') - .exec(); // ['OK', '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. @@ -125,14 +126,11 @@ Any command can be run on a new connection by specifying the `isolated` option. This pattern works especially well for blocking commands—such as `BLPOP` and `BLMOVE`: ```typescript -import { commandOptions } from 'redis'; +import { commandOptions } from "redis"; -const blPopPromise = client.blPop( - commandOptions({ isolated: true }), - 'key' -); +const blPopPromise = client.blPop(commandOptions({ isolated: true }), "key"); -await client.lPush('key', ['1', '2']); +await client.lPush("key", ["1", "2"]); await blPopPromise; // '2' ``` @@ -152,23 +150,23 @@ 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.subscribe("channel", (message) => { + console.log(message); // 'message' }); -await subscriber.pSubscribe('channe*', (message, channel) => { - console.log(message, channel); // 'message', 'channel' +await subscriber.pSubscribe("channe*", (message, channel) => { + console.log(message, channel); // 'message', 'channel' }); -await subscriber.unsubscribe('channel'); +await subscriber.unsubscribe("channel"); -await subscriber.pUnsubscribe('channe*'); +await subscriber.pUnsubscribe("channe*"); ``` Publish a message on a channel: ```typescript -await publisher.publish('channel', 'message'); +await publisher.publish("channel", "message"); ``` ### Scan Iterator @@ -177,26 +175,29 @@ await publisher.publish('channel', 'message'); ```typescript for await (const key of client.scanIterator()) { - // use the key! - await client.get(key); + // use the key! + await client.get(key); } ``` This works with `HSCAN`, `SSCAN`, and `ZSCAN` too: ```typescript -for await (const member of client.hScanIterator('hash')) {} -for await (const { field, value } of client.sScanIterator('set')) {} -for await (const { member, score } of client.zScanIterator('sorted-set')) {} +for await (const member of client.hScanIterator("hash")) { +} +for await (const { field, value } of client.sScanIterator("set")) { +} +for await (const { member, score } 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 + TYPE: "string", // `SCAN` only + MATCH: "patter*", + COUNT: 100, }); ``` @@ -205,30 +206,29 @@ client.scanIterator({ Define new functions using [Lua scripts](https://redis.io/commands/eval) which execute on the Redis server: ```typescript -import { createClient, defineScript } from 'redis'; +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 { - return [key, number.toString()]; - }, - transformReply(reply: number): number { - return reply; - } - }) - } - }); - - await client.connect(); - - await client.set('key', '1'); - await client.add('key', 2); // 3 + 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 { + return [key, number.toString()]; + }, + transformReply(reply: number): number { + return reply; + }, + }), + }, + }); + + await client.connect(); + + await client.set("key", "1"); + await client.add("key", 2); // 3 })(); ``` @@ -237,25 +237,28 @@ import { createClient, defineScript } from 'redis'; Connecting to a cluster is a bit different. Create the client by specifying some (or all) of the nodes in your cluster and then use it like a non-clustered client: ```typescript -import { createCluster } from 'redis'; +import { createCluster } from "redis"; (async () => { - const cluster = createCluster({ - rootNodes: [{ - host: '10.0.0.1', - port: 30001 - }, { - host: '10.0.0.2', - port: 30002 - }] - }); - - cluster.on('error', (err) => console.log('Redis Cluster Error', err)); - - await cluster.connect(); - - await cluster.set('key', 'value'); - const value = await cluster.get('key'); + const cluster = createCluster({ + rootNodes: [ + { + host: "10.0.0.1", + port: 30001, + }, + { + host: "10.0.0.2", + port: 30002, + }, + ], + }); + + cluster.on("error", (err) => console.log("Redis Cluster Error", err)); + + await cluster.connect(); + + await cluster.set("key", "value"); + const value = await cluster.get("key"); })(); ``` @@ -264,16 +267,16 @@ import { createCluster } from 'redis'; 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=='); +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==') + client.set("Tm9kZSBSZWRpcw==", "users:1"), + client.sAdd("users:1:tokens", "Tm9kZSBSZWRpcw=="), ]); ``` @@ -283,8 +286,9 @@ 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! - - + + + ## License This repository is licensed under the "MIT" license. See [LICENSE](LICENSE). diff --git a/package-lock.json b/package-lock.json index ac623c60e6a..9986a955bf6 100644 --- a/package-lock.json +++ b/package-lock.json @@ -17,7 +17,7 @@ "devDependencies": { "@istanbuljs/nyc-config-typescript": "^1.0.1", "@types/mocha": "^9.0.0", - "@types/node": "^16.7.10", + "@types/node": "^16.9.4", "@types/sinon": "^10.0.2", "@types/which": "^2.0.1", "@types/yallist": "^4.0.1", @@ -25,12 +25,12 @@ "nyc": "^15.1.0", "release-it": "^14.11.5", "sinon": "^11.1.2", - "source-map-support": "^0.5.19", + "source-map-support": "^0.5.20", "ts-node": "^10.2.1", - "typedoc": "^0.21.9", + "typedoc": "^0.22.4", "typedoc-github-wiki-theme": "^0.5.1", - "typedoc-plugin-markdown": "^3.10.4", - "typescript": "^4.4.2", + "typedoc-plugin-markdown": "^3.11.0", + "typescript": "^4.4.3", "which": "^2.0.2" }, "engines": { @@ -59,20 +59,20 @@ } }, "node_modules/@babel/core": { - "version": "7.15.0", - "resolved": "https://registry.npmjs.org/@babel/core/-/core-7.15.0.tgz", - "integrity": "sha512-tXtmTminrze5HEUPn/a0JtOzzfp0nk+UEXQ/tqIJo3WDGypl/2OFQEMll/zSFU8f/lfmfLXvTaORHF3cfXIQMw==", + "version": "7.15.5", + "resolved": "https://registry.npmjs.org/@babel/core/-/core-7.15.5.tgz", + "integrity": "sha512-pYgXxiwAgQpgM1bNkZsDEq85f0ggXMA5L7c+o3tskGMh2BunCI9QUwB9Z4jpvXUOuMdyGKiGKQiRe11VS6Jzvg==", "dev": true, "dependencies": { "@babel/code-frame": "^7.14.5", - "@babel/generator": "^7.15.0", - "@babel/helper-compilation-targets": "^7.15.0", - "@babel/helper-module-transforms": "^7.15.0", - "@babel/helpers": "^7.14.8", - "@babel/parser": "^7.15.0", - "@babel/template": "^7.14.5", - "@babel/traverse": "^7.15.0", - "@babel/types": "^7.15.0", + "@babel/generator": "^7.15.4", + "@babel/helper-compilation-targets": "^7.15.4", + "@babel/helper-module-transforms": "^7.15.4", + "@babel/helpers": "^7.15.4", + "@babel/parser": "^7.15.5", + "@babel/template": "^7.15.4", + "@babel/traverse": "^7.15.4", + "@babel/types": "^7.15.4", "convert-source-map": "^1.7.0", "debug": "^4.1.0", "gensync": "^1.0.0-beta.2", @@ -89,12 +89,12 @@ } }, "node_modules/@babel/generator": { - "version": "7.15.0", - "resolved": "https://registry.npmjs.org/@babel/generator/-/generator-7.15.0.tgz", - "integrity": "sha512-eKl4XdMrbpYvuB505KTta4AV9g+wWzmVBW69tX0H2NwKVKd2YJbKgyK6M8j/rgLbmHOYJn6rUklV677nOyJrEQ==", + "version": "7.15.4", + "resolved": "https://registry.npmjs.org/@babel/generator/-/generator-7.15.4.tgz", + "integrity": "sha512-d3itta0tu+UayjEORPNz6e1T3FtvWlP5N4V5M+lhp/CxT4oAA7/NcScnpRyspUMLK6tu9MNHmQHxRykuN2R7hw==", "dev": true, "dependencies": { - "@babel/types": "^7.15.0", + "@babel/types": "^7.15.4", "jsesc": "^2.5.1", "source-map": "^0.5.0" }, @@ -103,9 +103,9 @@ } }, "node_modules/@babel/helper-compilation-targets": { - "version": "7.15.0", - "resolved": "https://registry.npmjs.org/@babel/helper-compilation-targets/-/helper-compilation-targets-7.15.0.tgz", - "integrity": "sha512-h+/9t0ncd4jfZ8wsdAsoIxSa61qhBYlycXiHWqJaQBCXAhDCMbPRSMTGnZIkkmt1u4ag+UQmuqcILwqKzZ4N2A==", + "version": "7.15.4", + "resolved": "https://registry.npmjs.org/@babel/helper-compilation-targets/-/helper-compilation-targets-7.15.4.tgz", + "integrity": "sha512-rMWPCirulnPSe4d+gwdWXLfAXTTBj8M3guAf5xFQJ0nvFY7tfNAFnWdqaHegHlgDZOCT4qvhF3BYlSJag8yhqQ==", "dev": true, "dependencies": { "@babel/compat-data": "^7.15.0", @@ -121,141 +121,141 @@ } }, "node_modules/@babel/helper-function-name": { - "version": "7.14.5", - "resolved": "https://registry.npmjs.org/@babel/helper-function-name/-/helper-function-name-7.14.5.tgz", - "integrity": "sha512-Gjna0AsXWfFvrAuX+VKcN/aNNWonizBj39yGwUzVDVTlMYJMK2Wp6xdpy72mfArFq5uK+NOuexfzZlzI1z9+AQ==", + "version": "7.15.4", + "resolved": "https://registry.npmjs.org/@babel/helper-function-name/-/helper-function-name-7.15.4.tgz", + "integrity": "sha512-Z91cOMM4DseLIGOnog+Z8OI6YseR9bua+HpvLAQ2XayUGU+neTtX+97caALaLdyu53I/fjhbeCnWnRH1O3jFOw==", "dev": true, "dependencies": { - "@babel/helper-get-function-arity": "^7.14.5", - "@babel/template": "^7.14.5", - "@babel/types": "^7.14.5" + "@babel/helper-get-function-arity": "^7.15.4", + "@babel/template": "^7.15.4", + "@babel/types": "^7.15.4" }, "engines": { "node": ">=6.9.0" } }, "node_modules/@babel/helper-get-function-arity": { - "version": "7.14.5", - "resolved": "https://registry.npmjs.org/@babel/helper-get-function-arity/-/helper-get-function-arity-7.14.5.tgz", - "integrity": "sha512-I1Db4Shst5lewOM4V+ZKJzQ0JGGaZ6VY1jYvMghRjqs6DWgxLCIyFt30GlnKkfUeFLpJt2vzbMVEXVSXlIFYUg==", + "version": "7.15.4", + "resolved": "https://registry.npmjs.org/@babel/helper-get-function-arity/-/helper-get-function-arity-7.15.4.tgz", + "integrity": "sha512-1/AlxSF92CmGZzHnC515hm4SirTxtpDnLEJ0UyEMgTMZN+6bxXKg04dKhiRx5Enel+SUA1G1t5Ed/yQia0efrA==", "dev": true, "dependencies": { - "@babel/types": "^7.14.5" + "@babel/types": "^7.15.4" }, "engines": { "node": ">=6.9.0" } }, "node_modules/@babel/helper-hoist-variables": { - "version": "7.14.5", - "resolved": "https://registry.npmjs.org/@babel/helper-hoist-variables/-/helper-hoist-variables-7.14.5.tgz", - "integrity": "sha512-R1PXiz31Uc0Vxy4OEOm07x0oSjKAdPPCh3tPivn/Eo8cvz6gveAeuyUUPB21Hoiif0uoPQSSdhIPS3352nvdyQ==", + "version": "7.15.4", + "resolved": "https://registry.npmjs.org/@babel/helper-hoist-variables/-/helper-hoist-variables-7.15.4.tgz", + "integrity": "sha512-VTy085egb3jUGVK9ycIxQiPbquesq0HUQ+tPO0uv5mPEBZipk+5FkRKiWq5apuyTE9FUrjENB0rCf8y+n+UuhA==", "dev": true, "dependencies": { - "@babel/types": "^7.14.5" + "@babel/types": "^7.15.4" }, "engines": { "node": ">=6.9.0" } }, "node_modules/@babel/helper-member-expression-to-functions": { - "version": "7.15.0", - "resolved": "https://registry.npmjs.org/@babel/helper-member-expression-to-functions/-/helper-member-expression-to-functions-7.15.0.tgz", - "integrity": "sha512-Jq8H8U2kYiafuj2xMTPQwkTBnEEdGKpT35lJEQsRRjnG0LW3neucsaMWLgKcwu3OHKNeYugfw+Z20BXBSEs2Lg==", + "version": "7.15.4", + "resolved": "https://registry.npmjs.org/@babel/helper-member-expression-to-functions/-/helper-member-expression-to-functions-7.15.4.tgz", + "integrity": "sha512-cokOMkxC/BTyNP1AlY25HuBWM32iCEsLPI4BHDpJCHHm1FU2E7dKWWIXJgQgSFiu4lp8q3bL1BIKwqkSUviqtA==", "dev": true, "dependencies": { - "@babel/types": "^7.15.0" + "@babel/types": "^7.15.4" }, "engines": { "node": ">=6.9.0" } }, "node_modules/@babel/helper-module-imports": { - "version": "7.14.5", - "resolved": "https://registry.npmjs.org/@babel/helper-module-imports/-/helper-module-imports-7.14.5.tgz", - "integrity": "sha512-SwrNHu5QWS84XlHwGYPDtCxcA0hrSlL2yhWYLgeOc0w7ccOl2qv4s/nARI0aYZW+bSwAL5CukeXA47B/1NKcnQ==", + "version": "7.15.4", + "resolved": "https://registry.npmjs.org/@babel/helper-module-imports/-/helper-module-imports-7.15.4.tgz", + "integrity": "sha512-jeAHZbzUwdW/xHgHQ3QmWR4Jg6j15q4w/gCfwZvtqOxoo5DKtLHk8Bsf4c5RZRC7NmLEs+ohkdq8jFefuvIxAA==", "dev": true, "dependencies": { - "@babel/types": "^7.14.5" + "@babel/types": "^7.15.4" }, "engines": { "node": ">=6.9.0" } }, "node_modules/@babel/helper-module-transforms": { - "version": "7.15.0", - "resolved": "https://registry.npmjs.org/@babel/helper-module-transforms/-/helper-module-transforms-7.15.0.tgz", - "integrity": "sha512-RkGiW5Rer7fpXv9m1B3iHIFDZdItnO2/BLfWVW/9q7+KqQSDY5kUfQEbzdXM1MVhJGcugKV7kRrNVzNxmk7NBg==", + "version": "7.15.7", + "resolved": "https://registry.npmjs.org/@babel/helper-module-transforms/-/helper-module-transforms-7.15.7.tgz", + "integrity": "sha512-ZNqjjQG/AuFfekFTY+7nY4RgBSklgTu970c7Rj3m/JOhIu5KPBUuTA9AY6zaKcUvk4g6EbDXdBnhi35FAssdSw==", "dev": true, "dependencies": { - "@babel/helper-module-imports": "^7.14.5", - "@babel/helper-replace-supers": "^7.15.0", - "@babel/helper-simple-access": "^7.14.8", - "@babel/helper-split-export-declaration": "^7.14.5", - "@babel/helper-validator-identifier": "^7.14.9", - "@babel/template": "^7.14.5", - "@babel/traverse": "^7.15.0", - "@babel/types": "^7.15.0" + "@babel/helper-module-imports": "^7.15.4", + "@babel/helper-replace-supers": "^7.15.4", + "@babel/helper-simple-access": "^7.15.4", + "@babel/helper-split-export-declaration": "^7.15.4", + "@babel/helper-validator-identifier": "^7.15.7", + "@babel/template": "^7.15.4", + "@babel/traverse": "^7.15.4", + "@babel/types": "^7.15.6" }, "engines": { "node": ">=6.9.0" } }, "node_modules/@babel/helper-optimise-call-expression": { - "version": "7.14.5", - "resolved": "https://registry.npmjs.org/@babel/helper-optimise-call-expression/-/helper-optimise-call-expression-7.14.5.tgz", - "integrity": "sha512-IqiLIrODUOdnPU9/F8ib1Fx2ohlgDhxnIDU7OEVi+kAbEZcyiF7BLU8W6PfvPi9LzztjS7kcbzbmL7oG8kD6VA==", + "version": "7.15.4", + "resolved": "https://registry.npmjs.org/@babel/helper-optimise-call-expression/-/helper-optimise-call-expression-7.15.4.tgz", + "integrity": "sha512-E/z9rfbAOt1vDW1DR7k4SzhzotVV5+qMciWV6LaG1g4jeFrkDlJedjtV4h0i4Q/ITnUu+Pk08M7fczsB9GXBDw==", "dev": true, "dependencies": { - "@babel/types": "^7.14.5" + "@babel/types": "^7.15.4" }, "engines": { "node": ">=6.9.0" } }, "node_modules/@babel/helper-replace-supers": { - "version": "7.15.0", - "resolved": "https://registry.npmjs.org/@babel/helper-replace-supers/-/helper-replace-supers-7.15.0.tgz", - "integrity": "sha512-6O+eWrhx+HEra/uJnifCwhwMd6Bp5+ZfZeJwbqUTuqkhIT6YcRhiZCOOFChRypOIe0cV46kFrRBlm+t5vHCEaA==", + "version": "7.15.4", + "resolved": "https://registry.npmjs.org/@babel/helper-replace-supers/-/helper-replace-supers-7.15.4.tgz", + "integrity": "sha512-/ztT6khaXF37MS47fufrKvIsiQkx1LBRvSJNzRqmbyeZnTwU9qBxXYLaaT/6KaxfKhjs2Wy8kG8ZdsFUuWBjzw==", "dev": true, "dependencies": { - "@babel/helper-member-expression-to-functions": "^7.15.0", - "@babel/helper-optimise-call-expression": "^7.14.5", - "@babel/traverse": "^7.15.0", - "@babel/types": "^7.15.0" + "@babel/helper-member-expression-to-functions": "^7.15.4", + "@babel/helper-optimise-call-expression": "^7.15.4", + "@babel/traverse": "^7.15.4", + "@babel/types": "^7.15.4" }, "engines": { "node": ">=6.9.0" } }, "node_modules/@babel/helper-simple-access": { - "version": "7.14.8", - "resolved": "https://registry.npmjs.org/@babel/helper-simple-access/-/helper-simple-access-7.14.8.tgz", - "integrity": "sha512-TrFN4RHh9gnWEU+s7JloIho2T76GPwRHhdzOWLqTrMnlas8T9O7ec+oEDNsRXndOmru9ymH9DFrEOxpzPoSbdg==", + "version": "7.15.4", + "resolved": "https://registry.npmjs.org/@babel/helper-simple-access/-/helper-simple-access-7.15.4.tgz", + "integrity": "sha512-UzazrDoIVOZZcTeHHEPYrr1MvTR/K+wgLg6MY6e1CJyaRhbibftF6fR2KU2sFRtI/nERUZR9fBd6aKgBlIBaPg==", "dev": true, "dependencies": { - "@babel/types": "^7.14.8" + "@babel/types": "^7.15.4" }, "engines": { "node": ">=6.9.0" } }, "node_modules/@babel/helper-split-export-declaration": { - "version": "7.14.5", - "resolved": "https://registry.npmjs.org/@babel/helper-split-export-declaration/-/helper-split-export-declaration-7.14.5.tgz", - "integrity": "sha512-hprxVPu6e5Kdp2puZUmvOGjaLv9TCe58E/Fl6hRq4YiVQxIcNvuq6uTM2r1mT/oPskuS9CgR+I94sqAYv0NGKA==", + "version": "7.15.4", + "resolved": "https://registry.npmjs.org/@babel/helper-split-export-declaration/-/helper-split-export-declaration-7.15.4.tgz", + "integrity": "sha512-HsFqhLDZ08DxCpBdEVtKmywj6PQbwnF6HHybur0MAnkAKnlS6uHkwnmRIkElB2Owpfb4xL4NwDmDLFubueDXsw==", "dev": true, "dependencies": { - "@babel/types": "^7.14.5" + "@babel/types": "^7.15.4" }, "engines": { "node": ">=6.9.0" } }, "node_modules/@babel/helper-validator-identifier": { - "version": "7.14.9", - "resolved": "https://registry.npmjs.org/@babel/helper-validator-identifier/-/helper-validator-identifier-7.14.9.tgz", - "integrity": "sha512-pQYxPY0UP6IHISRitNe8bsijHex4TWZXi2HwKVsjPiltzlhse2znVcm9Ace510VT1kxIHjGJCZZQBX2gJDbo0g==", + "version": "7.15.7", + "resolved": "https://registry.npmjs.org/@babel/helper-validator-identifier/-/helper-validator-identifier-7.15.7.tgz", + "integrity": "sha512-K4JvCtQqad9OY2+yTU8w+E82ywk/fe+ELNlt1G8z3bVGlZfn/hOcQQsUhGhW/N+tb3fxK800wLtKOE/aM0m72w==", "dev": true, "engines": { "node": ">=6.9.0" @@ -271,14 +271,14 @@ } }, "node_modules/@babel/helpers": { - "version": "7.15.3", - "resolved": "https://registry.npmjs.org/@babel/helpers/-/helpers-7.15.3.tgz", - "integrity": "sha512-HwJiz52XaS96lX+28Tnbu31VeFSQJGOeKHJeaEPQlTl7PnlhFElWPj8tUXtqFIzeN86XxXoBr+WFAyK2PPVz6g==", + "version": "7.15.4", + "resolved": "https://registry.npmjs.org/@babel/helpers/-/helpers-7.15.4.tgz", + "integrity": "sha512-V45u6dqEJ3w2rlryYYXf6i9rQ5YMNu4FLS6ngs8ikblhu2VdR1AqAd6aJjBzmf2Qzh6KOLqKHxEN9+TFbAkAVQ==", "dev": true, "dependencies": { - "@babel/template": "^7.14.5", - "@babel/traverse": "^7.15.0", - "@babel/types": "^7.15.0" + "@babel/template": "^7.15.4", + "@babel/traverse": "^7.15.4", + "@babel/types": "^7.15.4" }, "engines": { "node": ">=6.9.0" @@ -370,9 +370,9 @@ } }, "node_modules/@babel/parser": { - "version": "7.15.3", - "resolved": "https://registry.npmjs.org/@babel/parser/-/parser-7.15.3.tgz", - "integrity": "sha512-O0L6v/HvqbdJawj0iBEfVQMc3/6WP+AeOsovsIgBFyJaG+W2w7eqvZB7puddATmWuARlm1SX7DwxJ/JJUnDpEA==", + "version": "7.15.7", + "resolved": "https://registry.npmjs.org/@babel/parser/-/parser-7.15.7.tgz", + "integrity": "sha512-rycZXvQ+xS9QyIcJ9HXeDWf1uxqlbVFAUq0Rq0dbc50Zb/+wUe/ehyfzGfm9KZZF0kBejYgxltBXocP+gKdL2g==", "dev": true, "bin": { "parser": "bin/babel-parser.js" @@ -382,32 +382,32 @@ } }, "node_modules/@babel/template": { - "version": "7.14.5", - "resolved": "https://registry.npmjs.org/@babel/template/-/template-7.14.5.tgz", - "integrity": "sha512-6Z3Po85sfxRGachLULUhOmvAaOo7xCvqGQtxINai2mEGPFm6pQ4z5QInFnUrRpfoSV60BnjyF5F3c+15fxFV1g==", + "version": "7.15.4", + "resolved": "https://registry.npmjs.org/@babel/template/-/template-7.15.4.tgz", + "integrity": "sha512-UgBAfEa1oGuYgDIPM2G+aHa4Nlo9Lh6mGD2bDBGMTbYnc38vulXPuC1MGjYILIEmlwl6Rd+BPR9ee3gm20CBtg==", "dev": true, "dependencies": { "@babel/code-frame": "^7.14.5", - "@babel/parser": "^7.14.5", - "@babel/types": "^7.14.5" + "@babel/parser": "^7.15.4", + "@babel/types": "^7.15.4" }, "engines": { "node": ">=6.9.0" } }, "node_modules/@babel/traverse": { - "version": "7.15.0", - "resolved": "https://registry.npmjs.org/@babel/traverse/-/traverse-7.15.0.tgz", - "integrity": "sha512-392d8BN0C9eVxVWd8H6x9WfipgVH5IaIoLp23334Sc1vbKKWINnvwRpb4us0xtPaCumlwbTtIYNA0Dv/32sVFw==", + "version": "7.15.4", + "resolved": "https://registry.npmjs.org/@babel/traverse/-/traverse-7.15.4.tgz", + "integrity": "sha512-W6lQD8l4rUbQR/vYgSuCAE75ADyyQvOpFVsvPPdkhf6lATXAsQIG9YdtOcu8BB1dZ0LKu+Zo3c1wEcbKeuhdlA==", "dev": true, "dependencies": { "@babel/code-frame": "^7.14.5", - "@babel/generator": "^7.15.0", - "@babel/helper-function-name": "^7.14.5", - "@babel/helper-hoist-variables": "^7.14.5", - "@babel/helper-split-export-declaration": "^7.14.5", - "@babel/parser": "^7.15.0", - "@babel/types": "^7.15.0", + "@babel/generator": "^7.15.4", + "@babel/helper-function-name": "^7.15.4", + "@babel/helper-hoist-variables": "^7.15.4", + "@babel/helper-split-export-declaration": "^7.15.4", + "@babel/parser": "^7.15.4", + "@babel/types": "^7.15.4", "debug": "^4.1.0", "globals": "^11.1.0" }, @@ -416,9 +416,9 @@ } }, "node_modules/@babel/types": { - "version": "7.15.0", - "resolved": "https://registry.npmjs.org/@babel/types/-/types-7.15.0.tgz", - "integrity": "sha512-OBvfqnllOIdX4ojTHpwZbpvz4j3EWyjkZEdmjH0/cgsd6QOdSgU8rLSk6ard/pcW7rlmjdVSX/AWOaORR1uNOQ==", + "version": "7.15.6", + "resolved": "https://registry.npmjs.org/@babel/types/-/types-7.15.6.tgz", + "integrity": "sha512-BPU+7QhqNjmWyDO0/vitH/CuhpV8ZmK1wpKva8nuyNF5MJfuRNWMc+hc14+u9xT93kvykMdncrJT19h74uB1Ig==", "dev": true, "dependencies": { "@babel/helper-validator-identifier": "^7.14.9", @@ -607,9 +607,9 @@ } }, "node_modules/@octokit/auth-token": { - "version": "2.4.5", - "resolved": "https://registry.npmjs.org/@octokit/auth-token/-/auth-token-2.4.5.tgz", - "integrity": "sha512-BpGYsPgJt05M7/L/5FoE1PiAbdxXFZkX/3kDYcsvd1v6UhlnE5e96dTDr0ezX/EFwciQxf3cNV0loipsURU+WA==", + "version": "2.5.0", + "resolved": "https://registry.npmjs.org/@octokit/auth-token/-/auth-token-2.5.0.tgz", + "integrity": "sha512-r5FVUJCOLl19AxiuZD2VRZ/ORjp/4IN98Of6YJoJOkY75CIBuYfmiNHGrDwXr+aLGG55igl9QrxX3hbiXlLb+g==", "dev": true, "dependencies": { "@octokit/types": "^6.0.3" @@ -653,18 +653,18 @@ } }, "node_modules/@octokit/openapi-types": { - "version": "10.0.0", - "resolved": "https://registry.npmjs.org/@octokit/openapi-types/-/openapi-types-10.0.0.tgz", - "integrity": "sha512-k1iO2zKuEjjRS1EJb4FwSLk+iF6EGp+ZV0OMRViQoWhQ1fZTk9hg1xccZII5uyYoiqcbC73MRBmT45y1vp2PPg==", + "version": "10.2.2", + "resolved": "https://registry.npmjs.org/@octokit/openapi-types/-/openapi-types-10.2.2.tgz", + "integrity": "sha512-EVcXQ+ZrC04cg17AMg1ofocWMxHDn17cB66ZHgYc0eUwjFtxS0oBzkyw2VqIrHBwVgtfoYrq1WMQfQmMjUwthw==", "dev": true }, "node_modules/@octokit/plugin-paginate-rest": { - "version": "2.16.0", - "resolved": "https://registry.npmjs.org/@octokit/plugin-paginate-rest/-/plugin-paginate-rest-2.16.0.tgz", - "integrity": "sha512-8YYzALPMvEZ35kgy5pdYvQ22Roz+BIuEaedO575GwE2vb/ACDqQn0xQrTJR4tnZCJn7pi8+AWPVjrFDaERIyXQ==", + "version": "2.16.3", + "resolved": "https://registry.npmjs.org/@octokit/plugin-paginate-rest/-/plugin-paginate-rest-2.16.3.tgz", + "integrity": "sha512-kdc65UEsqze/9fCISq6BxLzeB9qf0vKvKojIfzgwf4tEF+Wy6c9dXnPFE6vgpoDFB1Z5Jek5WFVU6vL1w22+Iw==", "dev": true, "dependencies": { - "@octokit/types": "^6.26.0" + "@octokit/types": "^6.28.1" }, "peerDependencies": { "@octokit/core": ">=2" @@ -730,18 +730,18 @@ } }, "node_modules/@octokit/types": { - "version": "6.26.0", - "resolved": "https://registry.npmjs.org/@octokit/types/-/types-6.26.0.tgz", - "integrity": "sha512-RDxZBAFMtqs1ZPnbUu1e7ohPNfoNhTiep4fErY7tZs995BeHu369Vsh5woMIaFbllRWEZBfvTCS4hvDnMPiHrA==", + "version": "6.28.1", + "resolved": "https://registry.npmjs.org/@octokit/types/-/types-6.28.1.tgz", + "integrity": "sha512-XlxDoQLFO5JnFZgKVQTYTvXRsQFfr/GwDUU108NJ9R5yFPkA2qXhTJjYuul3vE4eLXP40FA2nysOu2zd6boE+w==", "dev": true, "dependencies": { - "@octokit/openapi-types": "^10.0.0" + "@octokit/openapi-types": "^10.2.2" } }, "node_modules/@sindresorhus/is": { - "version": "4.0.1", - "resolved": "https://registry.npmjs.org/@sindresorhus/is/-/is-4.0.1.tgz", - "integrity": "sha512-Qm9hBEBu18wt1PO2flE7LPb30BHMQt1eQgbV76YntdNk73XZGpn3izvGTYxbGgzXKgbCjiia0uxTd3aTNQrY/g==", + "version": "4.2.0", + "resolved": "https://registry.npmjs.org/@sindresorhus/is/-/is-4.2.0.tgz", + "integrity": "sha512-VkE3KLBmJwcCaVARtQpfuKcKv8gcBmUubrfHGF84dXuuW6jgsRYxPtzcIhPyK9WAPpRt2/xY6zkD9MnRaJzSyw==", "dev": true, "engines": { "node": ">=10" @@ -840,9 +840,9 @@ "dev": true }, "node_modules/@types/keyv": { - "version": "3.1.2", - "resolved": "https://registry.npmjs.org/@types/keyv/-/keyv-3.1.2.tgz", - "integrity": "sha512-/FvAK2p4jQOaJ6CGDHJTqZcUtbZe820qIeTg7o0Shg7drB4JHeL+V/dhSaly7NXx6u8eSee+r7coT+yuJEvDLg==", + "version": "3.1.3", + "resolved": "https://registry.npmjs.org/@types/keyv/-/keyv-3.1.3.tgz", + "integrity": "sha512-FXCJgyyN3ivVgRoml4h94G/p3kY+u/B86La+QptcqJaWtBWtmc6TtkNfS40n9bIvyLteHh7zXOtgbobORKPbDg==", "dev": true, "dependencies": { "@types/node": "*" @@ -855,9 +855,9 @@ "dev": true }, "node_modules/@types/node": { - "version": "16.7.10", - "resolved": "https://registry.npmjs.org/@types/node/-/node-16.7.10.tgz", - "integrity": "sha512-S63Dlv4zIPb8x6MMTgDq5WWRJQe56iBEY0O3SOFA9JrRienkOVDXSXBjjJw6HTNQYSE2JI6GMCR6LVbIMHJVvA==", + "version": "16.9.4", + "resolved": "https://registry.npmjs.org/@types/node/-/node-16.9.4.tgz", + "integrity": "sha512-KDazLNYAGIuJugdbULwFZULF9qQ13yNWEBFnfVpqlpgAAo6H/qnM9RjBgh0A0kmHf3XxAKLdN5mTIng9iUvVLA==", "dev": true }, "node_modules/@types/parse-json": { @@ -903,9 +903,9 @@ "dev": true }, "node_modules/acorn": { - "version": "8.4.1", - "resolved": "https://registry.npmjs.org/acorn/-/acorn-8.4.1.tgz", - "integrity": "sha512-asabaBSkEKosYKMITunzX177CXxQ4Q8BSSzMTKD+FefUhipQC70gfW5SiUDhYQ3vk8G+81HqQk7Fv9OXwwn9KA==", + "version": "8.5.0", + "resolved": "https://registry.npmjs.org/acorn/-/acorn-8.5.0.tgz", + "integrity": "sha512-yXbYeFy+jUuYd3/CDcg2NkIYE991XYX/bje7LmjJigUciaeO1JR4XxXgCIV1/Zc/dRuFEyw1L0pbA+qynJkW5Q==", "dev": true, "bin": { "acorn": "bin/acorn" @@ -915,9 +915,9 @@ } }, "node_modules/acorn-walk": { - "version": "8.1.1", - "resolved": "https://registry.npmjs.org/acorn-walk/-/acorn-walk-8.1.1.tgz", - "integrity": "sha512-FbJdceMlPHEAWJOILDk1fXD8lnTlEIWFkqtfk+MvmL5q/qlHfN7GEHcsFZWt/Tea9jRNPWUZG4G976nqAAmU9w==", + "version": "8.2.0", + "resolved": "https://registry.npmjs.org/acorn-walk/-/acorn-walk-8.2.0.tgz", + "integrity": "sha512-k+iyHEuPgSw6SbuDpGQM+06HQUa04DZ3o+F6CSzXMvvI5KMvnaEqXe+YVe555R9nn6GPt404fos4wcgpw12SDA==", "dev": true, "engines": { "node": ">=0.4.0" @@ -1032,9 +1032,9 @@ } }, "node_modules/ansi-regex": { - "version": "5.0.0", - "resolved": "https://registry.npmjs.org/ansi-regex/-/ansi-regex-5.0.0.tgz", - "integrity": "sha512-bY6fj56OUQ0hU1KjFNDQuJFezqKdrAyFdIevADiqrWHwSlbmBNMHp5ak2f40Pm8JTFyM2mqxkG6ngkHO11f/lg==", + "version": "5.0.1", + "resolved": "https://registry.npmjs.org/ansi-regex/-/ansi-regex-5.0.1.tgz", + "integrity": "sha512-quJQXlTSUGL2LH9SUXo8VwsY4soanhgo6LNSm84E1LBcE8s3O0wpdiRzyR9z/ZZJMlMWv37qOOb9pdJlMUEKFQ==", "dev": true, "engines": { "node": ">=8" @@ -1175,16 +1175,16 @@ } }, "node_modules/boxen": { - "version": "5.0.1", - "resolved": "https://registry.npmjs.org/boxen/-/boxen-5.0.1.tgz", - "integrity": "sha512-49VBlw+PrWEF51aCmy7QIteYPIFZxSpvqBdP/2itCPPlJ49kj9zg/XPRFrdkne2W+CfwXUls8exMvu1RysZpKA==", + "version": "5.1.2", + "resolved": "https://registry.npmjs.org/boxen/-/boxen-5.1.2.tgz", + "integrity": "sha512-9gYgQKXx+1nP8mP7CzFyaUARhg7D3n1dF/FnErWmu9l6JvGpNUN278h0aSb+QjoiKSWG+iZ3uHrcqk0qrY9RQQ==", "dev": true, "dependencies": { "ansi-align": "^3.0.0", "camelcase": "^6.2.0", "chalk": "^4.1.0", "cli-boxes": "^2.2.1", - "string-width": "^4.2.0", + "string-width": "^4.2.2", "type-fest": "^0.20.2", "widest-line": "^3.1.0", "wrap-ansi": "^7.0.0" @@ -1249,14 +1249,14 @@ "dev": true }, "node_modules/browserslist": { - "version": "4.16.8", - "resolved": "https://registry.npmjs.org/browserslist/-/browserslist-4.16.8.tgz", - "integrity": "sha512-sc2m9ohR/49sWEbPj14ZSSZqp+kbi16aLao42Hmn3Z8FpjuMaq2xCA2l4zl9ITfyzvnvyE0hcg62YkIGKxgaNQ==", + "version": "4.17.0", + "resolved": "https://registry.npmjs.org/browserslist/-/browserslist-4.17.0.tgz", + "integrity": "sha512-g2BJ2a0nEYvEFQC208q8mVAhfNwpZ5Mu8BwgtCdZKO3qx98HChmeg448fPdUzld8aFmfLgVh7yymqV+q1lJZ5g==", "dev": true, "dependencies": { - "caniuse-lite": "^1.0.30001251", + "caniuse-lite": "^1.0.30001254", "colorette": "^1.3.0", - "electron-to-chromium": "^1.3.811", + "electron-to-chromium": "^1.3.830", "escalade": "^3.1.1", "node-releases": "^1.1.75" }, @@ -1390,9 +1390,9 @@ } }, "node_modules/caniuse-lite": { - "version": "1.0.30001252", - "resolved": "https://registry.npmjs.org/caniuse-lite/-/caniuse-lite-1.0.30001252.tgz", - "integrity": "sha512-I56jhWDGMtdILQORdusxBOH+Nl/KgQSdDmpJezYddnAkVOmnoU8zwjTV9xAjMIYxr0iPreEAVylCGcmHCjfaOw==", + "version": "1.0.30001258", + "resolved": "https://registry.npmjs.org/caniuse-lite/-/caniuse-lite-1.0.30001258.tgz", + "integrity": "sha512-RBByOG6xWXUp0CR2/WU2amXz3stjKpSl5J1xU49F1n2OxD//uBZO4wCKUiG+QMGf7CHGfDDcqoKriomoGVxTeA==", "dev": true, "funding": { "type": "opencollective", @@ -1570,9 +1570,9 @@ "dev": true }, "node_modules/colorette": { - "version": "1.3.0", - "resolved": "https://registry.npmjs.org/colorette/-/colorette-1.3.0.tgz", - "integrity": "sha512-ecORCqbSFP7Wm8Y6lyqMJjexBQqXSF7SSeaTyGGphogUjBlFP9m9o08wy86HL2uB7fMTxtOUzLMk7ogKcxMg1w==", + "version": "1.4.0", + "resolved": "https://registry.npmjs.org/colorette/-/colorette-1.4.0.tgz", + "integrity": "sha512-Y2oEozpomLn7Q3HFP7dpww7AtMJplbM9lGZP6RDfHqmbeRjiwRg4n6VM6j4KLmRke85uWEI7JqF17f3pqdRA0g==", "dev": true }, "node_modules/combined-stream": { @@ -1845,9 +1845,9 @@ "dev": true }, "node_modules/electron-to-chromium": { - "version": "1.3.827", - "resolved": "https://registry.npmjs.org/electron-to-chromium/-/electron-to-chromium-1.3.827.tgz", - "integrity": "sha512-ye+4uQOY/jbjRutMcE/EmOcNwUeo1qo9aKL2tPyb09cU3lmxNeyDF4RWiemmkknW+p29h7dyDqy02higTxc9/A==", + "version": "1.3.844", + "resolved": "https://registry.npmjs.org/electron-to-chromium/-/electron-to-chromium-1.3.844.tgz", + "integrity": "sha512-7ES6GQVsbgsUA49/apqub51I9ij8E3QwGqq/IRvO6OPCly3how/YUSg1GPslRWq+BteT2h94iAIQdJbuVVH4Pg==", "dev": true }, "node_modules/emoji-regex": { @@ -1977,9 +1977,9 @@ } }, "node_modules/fastq": { - "version": "1.12.0", - "resolved": "https://registry.npmjs.org/fastq/-/fastq-1.12.0.tgz", - "integrity": "sha512-VNX0QkHK3RsXVKr9KrlUv/FoTa0NdbYoHHl7uXHv2rzyHSlxjdNAKug2twd9luJxpcyNeAgf5iPPMutJO67Dfg==", + "version": "1.13.0", + "resolved": "https://registry.npmjs.org/fastq/-/fastq-1.13.0.tgz", + "integrity": "sha512-YpkpUnK8od0o1hmeSc7UUs/eB/vIPWJYjKck2QKIzAf71Vm1AAQ3EbuZB3g2JIy+pg+ERD0vqI79KyZiB2e2Nw==", "dev": true, "dependencies": { "reusify": "^1.0.4" @@ -3068,6 +3068,12 @@ "node": ">=6" } }, + "node_modules/jsonc-parser": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/jsonc-parser/-/jsonc-parser-3.0.0.tgz", + "integrity": "sha512-fQzRfAbIBnR0IQvftw9FJveWiHp72Fg20giDrHz6TdfB12UH/uue0D3hm57UB5KgAVuniLMCaS8P1IMj9NR7cA==", + "dev": true + }, "node_modules/just-extend": { "version": "4.2.1", "resolved": "https://registry.npmjs.org/just-extend/-/just-extend-4.2.1.tgz", @@ -3214,9 +3220,9 @@ "dev": true }, "node_modules/marked": { - "version": "3.0.2", - "resolved": "https://registry.npmjs.org/marked/-/marked-3.0.2.tgz", - "integrity": "sha512-TMJQQ79Z0e3rJYazY0tIoMsFzteUGw9fB3FD+gzuIT3zLuG9L9ckIvUfF51apdJkcqc208jJN2KbtPbOvXtbjA==", + "version": "3.0.4", + "resolved": "https://registry.npmjs.org/marked/-/marked-3.0.4.tgz", + "integrity": "sha512-jBo8AOayNaEcvBhNobg6/BLhdsK3NvnKWJg33MAAPbvTWiG4QBn9gpW1+7RssrKu4K1dKlN+0goVQwV41xEfOA==", "dev": true, "bin": { "marked": "bin/marked" @@ -3419,10 +3425,13 @@ } }, "node_modules/node-fetch": { - "version": "2.6.1", - "resolved": "https://registry.npmjs.org/node-fetch/-/node-fetch-2.6.1.tgz", - "integrity": "sha512-V4aYg89jEoVRxRb2fJdAg8FHvI7cEyYdVAh94HH0UIK8oJxUfkjlDQN9RbMx+bEjP7+ggMiFRprSti032Oipxw==", + "version": "2.6.3", + "resolved": "https://registry.npmjs.org/node-fetch/-/node-fetch-2.6.3.tgz", + "integrity": "sha512-BXSmNTLLDHT0UjQDg5E23x+0n/hPDjySqc0ELE4NpCa2wE5qmmaEWFRP/+v8pfuocchR9l5vFLbSB7CPE2ahvQ==", "dev": true, + "dependencies": { + "whatwg-url": "^5.0.0" + }, "engines": { "node": "4.x || >=6.0.0" } @@ -3440,9 +3449,9 @@ } }, "node_modules/node-releases": { - "version": "1.1.75", - "resolved": "https://registry.npmjs.org/node-releases/-/node-releases-1.1.75.tgz", - "integrity": "sha512-Qe5OUajvqrqDSy6wrWFmMwfJ0jVgwiw4T3KqmbTcZ62qW0gQkheXYhcFM1+lOVcGUoRxcEcfyvFMAnDgaF1VWw==", + "version": "1.1.76", + "resolved": "https://registry.npmjs.org/node-releases/-/node-releases-1.1.76.tgz", + "integrity": "sha512-9/IECtNr8dXNmPWmFXepT0/7o5eolGesHUa3mtr0KlgnCvnZxwh2qensKL42JJY2vQKC3nIBXetFAqR+PW1CmA==", "dev": true }, "node_modules/normalize-path": { @@ -4201,15 +4210,6 @@ "node": ">=8" } }, - "node_modules/progress": { - "version": "2.0.3", - "resolved": "https://registry.npmjs.org/progress/-/progress-2.0.3.tgz", - "integrity": "sha512-7PiHtLll5LdnKIMw100I+8xJXR5gW2QwWYkT6iJva0bXitZKa/XMrSbdmg3r2Xnaidz9Qumd0VPaMrZlF9V9sA==", - "dev": true, - "engines": { - "node": ">=0.4.0" - } - }, "node_modules/protocols": { "version": "1.4.8", "resolved": "https://registry.npmjs.org/protocols/-/protocols-1.4.8.tgz", @@ -4773,12 +4773,12 @@ } }, "node_modules/shiki": { - "version": "0.9.10", - "resolved": "https://registry.npmjs.org/shiki/-/shiki-0.9.10.tgz", - "integrity": "sha512-xeM7Oc6hY+6iW5O/T5hor8ul7mEprzyl5y4r5zthEHToQNw7MIhREMgU3r2gKDB0NaMLNrkcEQagudCdzE13Lg==", + "version": "0.9.11", + "resolved": "https://registry.npmjs.org/shiki/-/shiki-0.9.11.tgz", + "integrity": "sha512-tjruNTLFhU0hruCPoJP0y+B9LKOmcqUhTpxn7pcJB3fa+04gFChuEmxmrUfOJ7ZO6Jd+HwMnDHgY3lv3Tqonuw==", "dev": true, "dependencies": { - "json5": "^2.2.0", + "jsonc-parser": "^3.0.0", "onigasm": "^2.2.5", "vscode-textmate": "5.2.0" } @@ -4798,9 +4798,9 @@ } }, "node_modules/signal-exit": { - "version": "3.0.3", - "resolved": "https://registry.npmjs.org/signal-exit/-/signal-exit-3.0.3.tgz", - "integrity": "sha512-VUJ49FC8U1OxwZLxIbTTrDvLnf/6TDgxZcK8wxR8zs13xpx7xbG60ndBlhNrFi2EMuFRoeDoJO7wthSLq42EjA==", + "version": "3.0.4", + "resolved": "https://registry.npmjs.org/signal-exit/-/signal-exit-3.0.4.tgz", + "integrity": "sha512-rqYhcAnZ6d/vTPGghdrw7iumdcbXpsk1b8IG/rz+VWV51DM0p7XCtMoJ3qhPLIbp3tvyt3pKRbaaEMZYpHto8Q==", "dev": true }, "node_modules/sinon": { @@ -4852,9 +4852,9 @@ } }, "node_modules/source-map-support": { - "version": "0.5.19", - "resolved": "https://registry.npmjs.org/source-map-support/-/source-map-support-0.5.19.tgz", - "integrity": "sha512-Wonm7zOCIJzBGQdB+thsPar0kYuCIzYvxZwlBa87yi/Mdjv7Tip2cyVbLj5o0cFPN4EVkuTwb3GDDyUx2DGnGw==", + "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==", "dev": true, "dependencies": { "buffer-from": "^1.0.0", @@ -5073,6 +5073,12 @@ "node": ">=8.0" } }, + "node_modules/tr46": { + "version": "0.0.3", + "resolved": "https://registry.npmjs.org/tr46/-/tr46-0.0.3.tgz", + "integrity": "sha1-gYT9NH2snNwYWZLzpmIuFLnZq2o=", + "dev": true + }, "node_modules/ts-node": { "version": "10.2.1", "resolved": "https://registry.npmjs.org/ts-node/-/ts-node-10.2.1.tgz", @@ -5160,19 +5166,16 @@ } }, "node_modules/typedoc": { - "version": "0.21.9", - "resolved": "https://registry.npmjs.org/typedoc/-/typedoc-0.21.9.tgz", - "integrity": "sha512-VRo7aII4bnYaBBM1lhw4bQFmUcDQV8m8tqgjtc7oXl87jc1Slbhfw2X5MccfcR2YnEClHDWgsiQGgNB8KJXocA==", + "version": "0.22.4", + "resolved": "https://registry.npmjs.org/typedoc/-/typedoc-0.22.4.tgz", + "integrity": "sha512-M/a8NnPxq3/iZNNVjzFCK5gu4m//HTJIPbSS0JQVbkHJPP9wyepR12agylWTSqeVZe0xsbidVtO26+PP7iD/jw==", "dev": true, "dependencies": { "glob": "^7.1.7", - "handlebars": "^4.7.7", "lunr": "^2.3.9", - "marked": "^3.0.2", - "minimatch": "^3.0.0", - "progress": "^2.0.3", - "shiki": "^0.9.8", - "typedoc-default-themes": "^0.12.10" + "marked": "^3.0.4", + "minimatch": "^3.0.4", + "shiki": "^0.9.11" }, "bin": { "typedoc": "bin/typedoc" @@ -5184,15 +5187,6 @@ "typescript": "4.0.x || 4.1.x || 4.2.x || 4.3.x || 4.4.x" } }, - "node_modules/typedoc-default-themes": { - "version": "0.12.10", - "resolved": "https://registry.npmjs.org/typedoc-default-themes/-/typedoc-default-themes-0.12.10.tgz", - "integrity": "sha512-fIS001cAYHkyQPidWXmHuhs8usjP5XVJjWB8oZGqkTowZaz3v7g3KDZeeqE82FBrmkAnIBOY3jgy7lnPnqATbA==", - "dev": true, - "engines": { - "node": ">= 8" - } - }, "node_modules/typedoc-github-wiki-theme": { "version": "0.5.1", "resolved": "https://registry.npmjs.org/typedoc-github-wiki-theme/-/typedoc-github-wiki-theme-0.5.1.tgz", @@ -5204,21 +5198,21 @@ } }, "node_modules/typedoc-plugin-markdown": { - "version": "3.10.4", - "resolved": "https://registry.npmjs.org/typedoc-plugin-markdown/-/typedoc-plugin-markdown-3.10.4.tgz", - "integrity": "sha512-if9w7S9fXLg73AYi/EoRSEhTOZlg3I8mIP8YEmvzSE33VrNXC9/hA0nVcLEwFVJeQY7ay6z67I6kW0KIv7LjeA==", + "version": "3.11.0", + "resolved": "https://registry.npmjs.org/typedoc-plugin-markdown/-/typedoc-plugin-markdown-3.11.0.tgz", + "integrity": "sha512-zewcbzOlMV9nbhLsJhKBpoRW4J32LgbfdqwYfEfzzeE+wGOaOfsM6g7QH+ZKj8n+knH4sLCtk6XMN1TI/a1UuQ==", "dev": true, "dependencies": { "handlebars": "^4.7.7" }, "peerDependencies": { - "typedoc": ">=0.21.2" + "typedoc": ">=0.22.0" } }, "node_modules/typescript": { - "version": "4.4.2", - "resolved": "https://registry.npmjs.org/typescript/-/typescript-4.4.2.tgz", - "integrity": "sha512-gzP+t5W4hdy4c+68bfcv0t400HVJMMd2+H9B7gae1nQlBzCqvrXX+6GL/b3GAgyTH966pzrZ70/fRjwAtZksSQ==", + "version": "4.4.3", + "resolved": "https://registry.npmjs.org/typescript/-/typescript-4.4.3.tgz", + "integrity": "sha512-4xfscpisVgqqDfPaJo5vkd+Qd/ItkoagnHpufr+i2QCHBsNYp+G7UAoyFl8aPtx879u38wPV65rZ8qbGZijalA==", "dev": true, "bin": { "tsc": "bin/tsc", @@ -5229,9 +5223,9 @@ } }, "node_modules/uglify-js": { - "version": "3.14.1", - "resolved": "https://registry.npmjs.org/uglify-js/-/uglify-js-3.14.1.tgz", - "integrity": "sha512-JhS3hmcVaXlp/xSo3PKY5R0JqKs5M3IV+exdLHW99qKvKivPO4Z8qbej6mte17SOPqAOVMjt/XGgWacnFSzM3g==", + "version": "3.14.2", + "resolved": "https://registry.npmjs.org/uglify-js/-/uglify-js-3.14.2.tgz", + "integrity": "sha512-rtPMlmcO4agTUfz10CbgJ1k6UAoXM2gWb3GoMPPZB/+/Ackf8lNWk11K4rYi2D0apgoFRLtQOZhb+/iGNJq26A==", "dev": true, "optional": true, "bin": { @@ -5381,6 +5375,22 @@ "defaults": "^1.0.3" } }, + "node_modules/webidl-conversions": { + "version": "3.0.1", + "resolved": "https://registry.npmjs.org/webidl-conversions/-/webidl-conversions-3.0.1.tgz", + "integrity": "sha1-JFNCdeKnvGvnvIZhHMFq4KVlSHE=", + "dev": true + }, + "node_modules/whatwg-url": { + "version": "5.0.0", + "resolved": "https://registry.npmjs.org/whatwg-url/-/whatwg-url-5.0.0.tgz", + "integrity": "sha1-lmRU6HZUYuN2RNNib2dCzotwll0=", + "dev": true, + "dependencies": { + "tr46": "~0.0.3", + "webidl-conversions": "^3.0.0" + } + }, "node_modules/which": { "version": "2.0.2", "resolved": "https://registry.npmjs.org/which/-/which-2.0.2.tgz", @@ -5712,20 +5722,20 @@ "dev": true }, "@babel/core": { - "version": "7.15.0", - "resolved": "https://registry.npmjs.org/@babel/core/-/core-7.15.0.tgz", - "integrity": "sha512-tXtmTminrze5HEUPn/a0JtOzzfp0nk+UEXQ/tqIJo3WDGypl/2OFQEMll/zSFU8f/lfmfLXvTaORHF3cfXIQMw==", + "version": "7.15.5", + "resolved": "https://registry.npmjs.org/@babel/core/-/core-7.15.5.tgz", + "integrity": "sha512-pYgXxiwAgQpgM1bNkZsDEq85f0ggXMA5L7c+o3tskGMh2BunCI9QUwB9Z4jpvXUOuMdyGKiGKQiRe11VS6Jzvg==", "dev": true, "requires": { "@babel/code-frame": "^7.14.5", - "@babel/generator": "^7.15.0", - "@babel/helper-compilation-targets": "^7.15.0", - "@babel/helper-module-transforms": "^7.15.0", - "@babel/helpers": "^7.14.8", - "@babel/parser": "^7.15.0", - "@babel/template": "^7.14.5", - "@babel/traverse": "^7.15.0", - "@babel/types": "^7.15.0", + "@babel/generator": "^7.15.4", + "@babel/helper-compilation-targets": "^7.15.4", + "@babel/helper-module-transforms": "^7.15.4", + "@babel/helpers": "^7.15.4", + "@babel/parser": "^7.15.5", + "@babel/template": "^7.15.4", + "@babel/traverse": "^7.15.4", + "@babel/types": "^7.15.4", "convert-source-map": "^1.7.0", "debug": "^4.1.0", "gensync": "^1.0.0-beta.2", @@ -5735,20 +5745,20 @@ } }, "@babel/generator": { - "version": "7.15.0", - "resolved": "https://registry.npmjs.org/@babel/generator/-/generator-7.15.0.tgz", - "integrity": "sha512-eKl4XdMrbpYvuB505KTta4AV9g+wWzmVBW69tX0H2NwKVKd2YJbKgyK6M8j/rgLbmHOYJn6rUklV677nOyJrEQ==", + "version": "7.15.4", + "resolved": "https://registry.npmjs.org/@babel/generator/-/generator-7.15.4.tgz", + "integrity": "sha512-d3itta0tu+UayjEORPNz6e1T3FtvWlP5N4V5M+lhp/CxT4oAA7/NcScnpRyspUMLK6tu9MNHmQHxRykuN2R7hw==", "dev": true, "requires": { - "@babel/types": "^7.15.0", + "@babel/types": "^7.15.4", "jsesc": "^2.5.1", "source-map": "^0.5.0" } }, "@babel/helper-compilation-targets": { - "version": "7.15.0", - "resolved": "https://registry.npmjs.org/@babel/helper-compilation-targets/-/helper-compilation-targets-7.15.0.tgz", - "integrity": "sha512-h+/9t0ncd4jfZ8wsdAsoIxSa61qhBYlycXiHWqJaQBCXAhDCMbPRSMTGnZIkkmt1u4ag+UQmuqcILwqKzZ4N2A==", + "version": "7.15.4", + "resolved": "https://registry.npmjs.org/@babel/helper-compilation-targets/-/helper-compilation-targets-7.15.4.tgz", + "integrity": "sha512-rMWPCirulnPSe4d+gwdWXLfAXTTBj8M3guAf5xFQJ0nvFY7tfNAFnWdqaHegHlgDZOCT4qvhF3BYlSJag8yhqQ==", "dev": true, "requires": { "@babel/compat-data": "^7.15.0", @@ -5758,111 +5768,111 @@ } }, "@babel/helper-function-name": { - "version": "7.14.5", - "resolved": "https://registry.npmjs.org/@babel/helper-function-name/-/helper-function-name-7.14.5.tgz", - "integrity": "sha512-Gjna0AsXWfFvrAuX+VKcN/aNNWonizBj39yGwUzVDVTlMYJMK2Wp6xdpy72mfArFq5uK+NOuexfzZlzI1z9+AQ==", + "version": "7.15.4", + "resolved": "https://registry.npmjs.org/@babel/helper-function-name/-/helper-function-name-7.15.4.tgz", + "integrity": "sha512-Z91cOMM4DseLIGOnog+Z8OI6YseR9bua+HpvLAQ2XayUGU+neTtX+97caALaLdyu53I/fjhbeCnWnRH1O3jFOw==", "dev": true, "requires": { - "@babel/helper-get-function-arity": "^7.14.5", - "@babel/template": "^7.14.5", - "@babel/types": "^7.14.5" + "@babel/helper-get-function-arity": "^7.15.4", + "@babel/template": "^7.15.4", + "@babel/types": "^7.15.4" } }, "@babel/helper-get-function-arity": { - "version": "7.14.5", - "resolved": "https://registry.npmjs.org/@babel/helper-get-function-arity/-/helper-get-function-arity-7.14.5.tgz", - "integrity": "sha512-I1Db4Shst5lewOM4V+ZKJzQ0JGGaZ6VY1jYvMghRjqs6DWgxLCIyFt30GlnKkfUeFLpJt2vzbMVEXVSXlIFYUg==", + "version": "7.15.4", + "resolved": "https://registry.npmjs.org/@babel/helper-get-function-arity/-/helper-get-function-arity-7.15.4.tgz", + "integrity": "sha512-1/AlxSF92CmGZzHnC515hm4SirTxtpDnLEJ0UyEMgTMZN+6bxXKg04dKhiRx5Enel+SUA1G1t5Ed/yQia0efrA==", "dev": true, "requires": { - "@babel/types": "^7.14.5" + "@babel/types": "^7.15.4" } }, "@babel/helper-hoist-variables": { - "version": "7.14.5", - "resolved": "https://registry.npmjs.org/@babel/helper-hoist-variables/-/helper-hoist-variables-7.14.5.tgz", - "integrity": "sha512-R1PXiz31Uc0Vxy4OEOm07x0oSjKAdPPCh3tPivn/Eo8cvz6gveAeuyUUPB21Hoiif0uoPQSSdhIPS3352nvdyQ==", + "version": "7.15.4", + "resolved": "https://registry.npmjs.org/@babel/helper-hoist-variables/-/helper-hoist-variables-7.15.4.tgz", + "integrity": "sha512-VTy085egb3jUGVK9ycIxQiPbquesq0HUQ+tPO0uv5mPEBZipk+5FkRKiWq5apuyTE9FUrjENB0rCf8y+n+UuhA==", "dev": true, "requires": { - "@babel/types": "^7.14.5" + "@babel/types": "^7.15.4" } }, "@babel/helper-member-expression-to-functions": { - "version": "7.15.0", - "resolved": "https://registry.npmjs.org/@babel/helper-member-expression-to-functions/-/helper-member-expression-to-functions-7.15.0.tgz", - "integrity": "sha512-Jq8H8U2kYiafuj2xMTPQwkTBnEEdGKpT35lJEQsRRjnG0LW3neucsaMWLgKcwu3OHKNeYugfw+Z20BXBSEs2Lg==", + "version": "7.15.4", + "resolved": "https://registry.npmjs.org/@babel/helper-member-expression-to-functions/-/helper-member-expression-to-functions-7.15.4.tgz", + "integrity": "sha512-cokOMkxC/BTyNP1AlY25HuBWM32iCEsLPI4BHDpJCHHm1FU2E7dKWWIXJgQgSFiu4lp8q3bL1BIKwqkSUviqtA==", "dev": true, "requires": { - "@babel/types": "^7.15.0" + "@babel/types": "^7.15.4" } }, "@babel/helper-module-imports": { - "version": "7.14.5", - "resolved": "https://registry.npmjs.org/@babel/helper-module-imports/-/helper-module-imports-7.14.5.tgz", - "integrity": "sha512-SwrNHu5QWS84XlHwGYPDtCxcA0hrSlL2yhWYLgeOc0w7ccOl2qv4s/nARI0aYZW+bSwAL5CukeXA47B/1NKcnQ==", + "version": "7.15.4", + "resolved": "https://registry.npmjs.org/@babel/helper-module-imports/-/helper-module-imports-7.15.4.tgz", + "integrity": "sha512-jeAHZbzUwdW/xHgHQ3QmWR4Jg6j15q4w/gCfwZvtqOxoo5DKtLHk8Bsf4c5RZRC7NmLEs+ohkdq8jFefuvIxAA==", "dev": true, "requires": { - "@babel/types": "^7.14.5" + "@babel/types": "^7.15.4" } }, "@babel/helper-module-transforms": { - "version": "7.15.0", - "resolved": "https://registry.npmjs.org/@babel/helper-module-transforms/-/helper-module-transforms-7.15.0.tgz", - "integrity": "sha512-RkGiW5Rer7fpXv9m1B3iHIFDZdItnO2/BLfWVW/9q7+KqQSDY5kUfQEbzdXM1MVhJGcugKV7kRrNVzNxmk7NBg==", + "version": "7.15.7", + "resolved": "https://registry.npmjs.org/@babel/helper-module-transforms/-/helper-module-transforms-7.15.7.tgz", + "integrity": "sha512-ZNqjjQG/AuFfekFTY+7nY4RgBSklgTu970c7Rj3m/JOhIu5KPBUuTA9AY6zaKcUvk4g6EbDXdBnhi35FAssdSw==", "dev": true, "requires": { - "@babel/helper-module-imports": "^7.14.5", - "@babel/helper-replace-supers": "^7.15.0", - "@babel/helper-simple-access": "^7.14.8", - "@babel/helper-split-export-declaration": "^7.14.5", - "@babel/helper-validator-identifier": "^7.14.9", - "@babel/template": "^7.14.5", - "@babel/traverse": "^7.15.0", - "@babel/types": "^7.15.0" + "@babel/helper-module-imports": "^7.15.4", + "@babel/helper-replace-supers": "^7.15.4", + "@babel/helper-simple-access": "^7.15.4", + "@babel/helper-split-export-declaration": "^7.15.4", + "@babel/helper-validator-identifier": "^7.15.7", + "@babel/template": "^7.15.4", + "@babel/traverse": "^7.15.4", + "@babel/types": "^7.15.6" } }, "@babel/helper-optimise-call-expression": { - "version": "7.14.5", - "resolved": "https://registry.npmjs.org/@babel/helper-optimise-call-expression/-/helper-optimise-call-expression-7.14.5.tgz", - "integrity": "sha512-IqiLIrODUOdnPU9/F8ib1Fx2ohlgDhxnIDU7OEVi+kAbEZcyiF7BLU8W6PfvPi9LzztjS7kcbzbmL7oG8kD6VA==", + "version": "7.15.4", + "resolved": "https://registry.npmjs.org/@babel/helper-optimise-call-expression/-/helper-optimise-call-expression-7.15.4.tgz", + "integrity": "sha512-E/z9rfbAOt1vDW1DR7k4SzhzotVV5+qMciWV6LaG1g4jeFrkDlJedjtV4h0i4Q/ITnUu+Pk08M7fczsB9GXBDw==", "dev": true, "requires": { - "@babel/types": "^7.14.5" + "@babel/types": "^7.15.4" } }, "@babel/helper-replace-supers": { - "version": "7.15.0", - "resolved": "https://registry.npmjs.org/@babel/helper-replace-supers/-/helper-replace-supers-7.15.0.tgz", - "integrity": "sha512-6O+eWrhx+HEra/uJnifCwhwMd6Bp5+ZfZeJwbqUTuqkhIT6YcRhiZCOOFChRypOIe0cV46kFrRBlm+t5vHCEaA==", + "version": "7.15.4", + "resolved": "https://registry.npmjs.org/@babel/helper-replace-supers/-/helper-replace-supers-7.15.4.tgz", + "integrity": "sha512-/ztT6khaXF37MS47fufrKvIsiQkx1LBRvSJNzRqmbyeZnTwU9qBxXYLaaT/6KaxfKhjs2Wy8kG8ZdsFUuWBjzw==", "dev": true, "requires": { - "@babel/helper-member-expression-to-functions": "^7.15.0", - "@babel/helper-optimise-call-expression": "^7.14.5", - "@babel/traverse": "^7.15.0", - "@babel/types": "^7.15.0" + "@babel/helper-member-expression-to-functions": "^7.15.4", + "@babel/helper-optimise-call-expression": "^7.15.4", + "@babel/traverse": "^7.15.4", + "@babel/types": "^7.15.4" } }, "@babel/helper-simple-access": { - "version": "7.14.8", - "resolved": "https://registry.npmjs.org/@babel/helper-simple-access/-/helper-simple-access-7.14.8.tgz", - "integrity": "sha512-TrFN4RHh9gnWEU+s7JloIho2T76GPwRHhdzOWLqTrMnlas8T9O7ec+oEDNsRXndOmru9ymH9DFrEOxpzPoSbdg==", + "version": "7.15.4", + "resolved": "https://registry.npmjs.org/@babel/helper-simple-access/-/helper-simple-access-7.15.4.tgz", + "integrity": "sha512-UzazrDoIVOZZcTeHHEPYrr1MvTR/K+wgLg6MY6e1CJyaRhbibftF6fR2KU2sFRtI/nERUZR9fBd6aKgBlIBaPg==", "dev": true, "requires": { - "@babel/types": "^7.14.8" + "@babel/types": "^7.15.4" } }, "@babel/helper-split-export-declaration": { - "version": "7.14.5", - "resolved": "https://registry.npmjs.org/@babel/helper-split-export-declaration/-/helper-split-export-declaration-7.14.5.tgz", - "integrity": "sha512-hprxVPu6e5Kdp2puZUmvOGjaLv9TCe58E/Fl6hRq4YiVQxIcNvuq6uTM2r1mT/oPskuS9CgR+I94sqAYv0NGKA==", + "version": "7.15.4", + "resolved": "https://registry.npmjs.org/@babel/helper-split-export-declaration/-/helper-split-export-declaration-7.15.4.tgz", + "integrity": "sha512-HsFqhLDZ08DxCpBdEVtKmywj6PQbwnF6HHybur0MAnkAKnlS6uHkwnmRIkElB2Owpfb4xL4NwDmDLFubueDXsw==", "dev": true, "requires": { - "@babel/types": "^7.14.5" + "@babel/types": "^7.15.4" } }, "@babel/helper-validator-identifier": { - "version": "7.14.9", - "resolved": "https://registry.npmjs.org/@babel/helper-validator-identifier/-/helper-validator-identifier-7.14.9.tgz", - "integrity": "sha512-pQYxPY0UP6IHISRitNe8bsijHex4TWZXi2HwKVsjPiltzlhse2znVcm9Ace510VT1kxIHjGJCZZQBX2gJDbo0g==", + "version": "7.15.7", + "resolved": "https://registry.npmjs.org/@babel/helper-validator-identifier/-/helper-validator-identifier-7.15.7.tgz", + "integrity": "sha512-K4JvCtQqad9OY2+yTU8w+E82ywk/fe+ELNlt1G8z3bVGlZfn/hOcQQsUhGhW/N+tb3fxK800wLtKOE/aM0m72w==", "dev": true }, "@babel/helper-validator-option": { @@ -5872,14 +5882,14 @@ "dev": true }, "@babel/helpers": { - "version": "7.15.3", - "resolved": "https://registry.npmjs.org/@babel/helpers/-/helpers-7.15.3.tgz", - "integrity": "sha512-HwJiz52XaS96lX+28Tnbu31VeFSQJGOeKHJeaEPQlTl7PnlhFElWPj8tUXtqFIzeN86XxXoBr+WFAyK2PPVz6g==", + "version": "7.15.4", + "resolved": "https://registry.npmjs.org/@babel/helpers/-/helpers-7.15.4.tgz", + "integrity": "sha512-V45u6dqEJ3w2rlryYYXf6i9rQ5YMNu4FLS6ngs8ikblhu2VdR1AqAd6aJjBzmf2Qzh6KOLqKHxEN9+TFbAkAVQ==", "dev": true, "requires": { - "@babel/template": "^7.14.5", - "@babel/traverse": "^7.15.0", - "@babel/types": "^7.15.0" + "@babel/template": "^7.15.4", + "@babel/traverse": "^7.15.4", + "@babel/types": "^7.15.4" } }, "@babel/highlight": { @@ -5952,43 +5962,43 @@ } }, "@babel/parser": { - "version": "7.15.3", - "resolved": "https://registry.npmjs.org/@babel/parser/-/parser-7.15.3.tgz", - "integrity": "sha512-O0L6v/HvqbdJawj0iBEfVQMc3/6WP+AeOsovsIgBFyJaG+W2w7eqvZB7puddATmWuARlm1SX7DwxJ/JJUnDpEA==", + "version": "7.15.7", + "resolved": "https://registry.npmjs.org/@babel/parser/-/parser-7.15.7.tgz", + "integrity": "sha512-rycZXvQ+xS9QyIcJ9HXeDWf1uxqlbVFAUq0Rq0dbc50Zb/+wUe/ehyfzGfm9KZZF0kBejYgxltBXocP+gKdL2g==", "dev": true }, "@babel/template": { - "version": "7.14.5", - "resolved": "https://registry.npmjs.org/@babel/template/-/template-7.14.5.tgz", - "integrity": "sha512-6Z3Po85sfxRGachLULUhOmvAaOo7xCvqGQtxINai2mEGPFm6pQ4z5QInFnUrRpfoSV60BnjyF5F3c+15fxFV1g==", + "version": "7.15.4", + "resolved": "https://registry.npmjs.org/@babel/template/-/template-7.15.4.tgz", + "integrity": "sha512-UgBAfEa1oGuYgDIPM2G+aHa4Nlo9Lh6mGD2bDBGMTbYnc38vulXPuC1MGjYILIEmlwl6Rd+BPR9ee3gm20CBtg==", "dev": true, "requires": { "@babel/code-frame": "^7.14.5", - "@babel/parser": "^7.14.5", - "@babel/types": "^7.14.5" + "@babel/parser": "^7.15.4", + "@babel/types": "^7.15.4" } }, "@babel/traverse": { - "version": "7.15.0", - "resolved": "https://registry.npmjs.org/@babel/traverse/-/traverse-7.15.0.tgz", - "integrity": "sha512-392d8BN0C9eVxVWd8H6x9WfipgVH5IaIoLp23334Sc1vbKKWINnvwRpb4us0xtPaCumlwbTtIYNA0Dv/32sVFw==", + "version": "7.15.4", + "resolved": "https://registry.npmjs.org/@babel/traverse/-/traverse-7.15.4.tgz", + "integrity": "sha512-W6lQD8l4rUbQR/vYgSuCAE75ADyyQvOpFVsvPPdkhf6lATXAsQIG9YdtOcu8BB1dZ0LKu+Zo3c1wEcbKeuhdlA==", "dev": true, "requires": { "@babel/code-frame": "^7.14.5", - "@babel/generator": "^7.15.0", - "@babel/helper-function-name": "^7.14.5", - "@babel/helper-hoist-variables": "^7.14.5", - "@babel/helper-split-export-declaration": "^7.14.5", - "@babel/parser": "^7.15.0", - "@babel/types": "^7.15.0", + "@babel/generator": "^7.15.4", + "@babel/helper-function-name": "^7.15.4", + "@babel/helper-hoist-variables": "^7.15.4", + "@babel/helper-split-export-declaration": "^7.15.4", + "@babel/parser": "^7.15.4", + "@babel/types": "^7.15.4", "debug": "^4.1.0", "globals": "^11.1.0" } }, "@babel/types": { - "version": "7.15.0", - "resolved": "https://registry.npmjs.org/@babel/types/-/types-7.15.0.tgz", - "integrity": "sha512-OBvfqnllOIdX4ojTHpwZbpvz4j3EWyjkZEdmjH0/cgsd6QOdSgU8rLSk6ard/pcW7rlmjdVSX/AWOaORR1uNOQ==", + "version": "7.15.6", + "resolved": "https://registry.npmjs.org/@babel/types/-/types-7.15.6.tgz", + "integrity": "sha512-BPU+7QhqNjmWyDO0/vitH/CuhpV8ZmK1wpKva8nuyNF5MJfuRNWMc+hc14+u9xT93kvykMdncrJT19h74uB1Ig==", "dev": true, "requires": { "@babel/helper-validator-identifier": "^7.14.9", @@ -6129,9 +6139,9 @@ } }, "@octokit/auth-token": { - "version": "2.4.5", - "resolved": "https://registry.npmjs.org/@octokit/auth-token/-/auth-token-2.4.5.tgz", - "integrity": "sha512-BpGYsPgJt05M7/L/5FoE1PiAbdxXFZkX/3kDYcsvd1v6UhlnE5e96dTDr0ezX/EFwciQxf3cNV0loipsURU+WA==", + "version": "2.5.0", + "resolved": "https://registry.npmjs.org/@octokit/auth-token/-/auth-token-2.5.0.tgz", + "integrity": "sha512-r5FVUJCOLl19AxiuZD2VRZ/ORjp/4IN98Of6YJoJOkY75CIBuYfmiNHGrDwXr+aLGG55igl9QrxX3hbiXlLb+g==", "dev": true, "requires": { "@octokit/types": "^6.0.3" @@ -6175,18 +6185,18 @@ } }, "@octokit/openapi-types": { - "version": "10.0.0", - "resolved": "https://registry.npmjs.org/@octokit/openapi-types/-/openapi-types-10.0.0.tgz", - "integrity": "sha512-k1iO2zKuEjjRS1EJb4FwSLk+iF6EGp+ZV0OMRViQoWhQ1fZTk9hg1xccZII5uyYoiqcbC73MRBmT45y1vp2PPg==", + "version": "10.2.2", + "resolved": "https://registry.npmjs.org/@octokit/openapi-types/-/openapi-types-10.2.2.tgz", + "integrity": "sha512-EVcXQ+ZrC04cg17AMg1ofocWMxHDn17cB66ZHgYc0eUwjFtxS0oBzkyw2VqIrHBwVgtfoYrq1WMQfQmMjUwthw==", "dev": true }, "@octokit/plugin-paginate-rest": { - "version": "2.16.0", - "resolved": "https://registry.npmjs.org/@octokit/plugin-paginate-rest/-/plugin-paginate-rest-2.16.0.tgz", - "integrity": "sha512-8YYzALPMvEZ35kgy5pdYvQ22Roz+BIuEaedO575GwE2vb/ACDqQn0xQrTJR4tnZCJn7pi8+AWPVjrFDaERIyXQ==", + "version": "2.16.3", + "resolved": "https://registry.npmjs.org/@octokit/plugin-paginate-rest/-/plugin-paginate-rest-2.16.3.tgz", + "integrity": "sha512-kdc65UEsqze/9fCISq6BxLzeB9qf0vKvKojIfzgwf4tEF+Wy6c9dXnPFE6vgpoDFB1Z5Jek5WFVU6vL1w22+Iw==", "dev": true, "requires": { - "@octokit/types": "^6.26.0" + "@octokit/types": "^6.28.1" } }, "@octokit/plugin-request-log": { @@ -6244,18 +6254,18 @@ } }, "@octokit/types": { - "version": "6.26.0", - "resolved": "https://registry.npmjs.org/@octokit/types/-/types-6.26.0.tgz", - "integrity": "sha512-RDxZBAFMtqs1ZPnbUu1e7ohPNfoNhTiep4fErY7tZs995BeHu369Vsh5woMIaFbllRWEZBfvTCS4hvDnMPiHrA==", + "version": "6.28.1", + "resolved": "https://registry.npmjs.org/@octokit/types/-/types-6.28.1.tgz", + "integrity": "sha512-XlxDoQLFO5JnFZgKVQTYTvXRsQFfr/GwDUU108NJ9R5yFPkA2qXhTJjYuul3vE4eLXP40FA2nysOu2zd6boE+w==", "dev": true, "requires": { - "@octokit/openapi-types": "^10.0.0" + "@octokit/openapi-types": "^10.2.2" } }, "@sindresorhus/is": { - "version": "4.0.1", - "resolved": "https://registry.npmjs.org/@sindresorhus/is/-/is-4.0.1.tgz", - "integrity": "sha512-Qm9hBEBu18wt1PO2flE7LPb30BHMQt1eQgbV76YntdNk73XZGpn3izvGTYxbGgzXKgbCjiia0uxTd3aTNQrY/g==", + "version": "4.2.0", + "resolved": "https://registry.npmjs.org/@sindresorhus/is/-/is-4.2.0.tgz", + "integrity": "sha512-VkE3KLBmJwcCaVARtQpfuKcKv8gcBmUubrfHGF84dXuuW6jgsRYxPtzcIhPyK9WAPpRt2/xY6zkD9MnRaJzSyw==", "dev": true }, "@sinonjs/commons": { @@ -6345,9 +6355,9 @@ "dev": true }, "@types/keyv": { - "version": "3.1.2", - "resolved": "https://registry.npmjs.org/@types/keyv/-/keyv-3.1.2.tgz", - "integrity": "sha512-/FvAK2p4jQOaJ6CGDHJTqZcUtbZe820qIeTg7o0Shg7drB4JHeL+V/dhSaly7NXx6u8eSee+r7coT+yuJEvDLg==", + "version": "3.1.3", + "resolved": "https://registry.npmjs.org/@types/keyv/-/keyv-3.1.3.tgz", + "integrity": "sha512-FXCJgyyN3ivVgRoml4h94G/p3kY+u/B86La+QptcqJaWtBWtmc6TtkNfS40n9bIvyLteHh7zXOtgbobORKPbDg==", "dev": true, "requires": { "@types/node": "*" @@ -6360,9 +6370,9 @@ "dev": true }, "@types/node": { - "version": "16.7.10", - "resolved": "https://registry.npmjs.org/@types/node/-/node-16.7.10.tgz", - "integrity": "sha512-S63Dlv4zIPb8x6MMTgDq5WWRJQe56iBEY0O3SOFA9JrRienkOVDXSXBjjJw6HTNQYSE2JI6GMCR6LVbIMHJVvA==", + "version": "16.9.4", + "resolved": "https://registry.npmjs.org/@types/node/-/node-16.9.4.tgz", + "integrity": "sha512-KDazLNYAGIuJugdbULwFZULF9qQ13yNWEBFnfVpqlpgAAo6H/qnM9RjBgh0A0kmHf3XxAKLdN5mTIng9iUvVLA==", "dev": true }, "@types/parse-json": { @@ -6408,15 +6418,15 @@ "dev": true }, "acorn": { - "version": "8.4.1", - "resolved": "https://registry.npmjs.org/acorn/-/acorn-8.4.1.tgz", - "integrity": "sha512-asabaBSkEKosYKMITunzX177CXxQ4Q8BSSzMTKD+FefUhipQC70gfW5SiUDhYQ3vk8G+81HqQk7Fv9OXwwn9KA==", + "version": "8.5.0", + "resolved": "https://registry.npmjs.org/acorn/-/acorn-8.5.0.tgz", + "integrity": "sha512-yXbYeFy+jUuYd3/CDcg2NkIYE991XYX/bje7LmjJigUciaeO1JR4XxXgCIV1/Zc/dRuFEyw1L0pbA+qynJkW5Q==", "dev": true }, "acorn-walk": { - "version": "8.1.1", - "resolved": "https://registry.npmjs.org/acorn-walk/-/acorn-walk-8.1.1.tgz", - "integrity": "sha512-FbJdceMlPHEAWJOILDk1fXD8lnTlEIWFkqtfk+MvmL5q/qlHfN7GEHcsFZWt/Tea9jRNPWUZG4G976nqAAmU9w==", + "version": "8.2.0", + "resolved": "https://registry.npmjs.org/acorn-walk/-/acorn-walk-8.2.0.tgz", + "integrity": "sha512-k+iyHEuPgSw6SbuDpGQM+06HQUa04DZ3o+F6CSzXMvvI5KMvnaEqXe+YVe555R9nn6GPt404fos4wcgpw12SDA==", "dev": true }, "aggregate-error": { @@ -6502,9 +6512,9 @@ } }, "ansi-regex": { - "version": "5.0.0", - "resolved": "https://registry.npmjs.org/ansi-regex/-/ansi-regex-5.0.0.tgz", - "integrity": "sha512-bY6fj56OUQ0hU1KjFNDQuJFezqKdrAyFdIevADiqrWHwSlbmBNMHp5ak2f40Pm8JTFyM2mqxkG6ngkHO11f/lg==", + "version": "5.0.1", + "resolved": "https://registry.npmjs.org/ansi-regex/-/ansi-regex-5.0.1.tgz", + "integrity": "sha512-quJQXlTSUGL2LH9SUXo8VwsY4soanhgo6LNSm84E1LBcE8s3O0wpdiRzyR9z/ZZJMlMWv37qOOb9pdJlMUEKFQ==", "dev": true }, "ansi-styles": { @@ -6610,16 +6620,16 @@ } }, "boxen": { - "version": "5.0.1", - "resolved": "https://registry.npmjs.org/boxen/-/boxen-5.0.1.tgz", - "integrity": "sha512-49VBlw+PrWEF51aCmy7QIteYPIFZxSpvqBdP/2itCPPlJ49kj9zg/XPRFrdkne2W+CfwXUls8exMvu1RysZpKA==", + "version": "5.1.2", + "resolved": "https://registry.npmjs.org/boxen/-/boxen-5.1.2.tgz", + "integrity": "sha512-9gYgQKXx+1nP8mP7CzFyaUARhg7D3n1dF/FnErWmu9l6JvGpNUN278h0aSb+QjoiKSWG+iZ3uHrcqk0qrY9RQQ==", "dev": true, "requires": { "ansi-align": "^3.0.0", "camelcase": "^6.2.0", "chalk": "^4.1.0", "cli-boxes": "^2.2.1", - "string-width": "^4.2.0", + "string-width": "^4.2.2", "type-fest": "^0.20.2", "widest-line": "^3.1.0", "wrap-ansi": "^7.0.0" @@ -6665,14 +6675,14 @@ "dev": true }, "browserslist": { - "version": "4.16.8", - "resolved": "https://registry.npmjs.org/browserslist/-/browserslist-4.16.8.tgz", - "integrity": "sha512-sc2m9ohR/49sWEbPj14ZSSZqp+kbi16aLao42Hmn3Z8FpjuMaq2xCA2l4zl9ITfyzvnvyE0hcg62YkIGKxgaNQ==", + "version": "4.17.0", + "resolved": "https://registry.npmjs.org/browserslist/-/browserslist-4.17.0.tgz", + "integrity": "sha512-g2BJ2a0nEYvEFQC208q8mVAhfNwpZ5Mu8BwgtCdZKO3qx98HChmeg448fPdUzld8aFmfLgVh7yymqV+q1lJZ5g==", "dev": true, "requires": { - "caniuse-lite": "^1.0.30001251", + "caniuse-lite": "^1.0.30001254", "colorette": "^1.3.0", - "electron-to-chromium": "^1.3.811", + "electron-to-chromium": "^1.3.830", "escalade": "^3.1.1", "node-releases": "^1.1.75" } @@ -6760,9 +6770,9 @@ "dev": true }, "caniuse-lite": { - "version": "1.0.30001252", - "resolved": "https://registry.npmjs.org/caniuse-lite/-/caniuse-lite-1.0.30001252.tgz", - "integrity": "sha512-I56jhWDGMtdILQORdusxBOH+Nl/KgQSdDmpJezYddnAkVOmnoU8zwjTV9xAjMIYxr0iPreEAVylCGcmHCjfaOw==", + "version": "1.0.30001258", + "resolved": "https://registry.npmjs.org/caniuse-lite/-/caniuse-lite-1.0.30001258.tgz", + "integrity": "sha512-RBByOG6xWXUp0CR2/WU2amXz3stjKpSl5J1xU49F1n2OxD//uBZO4wCKUiG+QMGf7CHGfDDcqoKriomoGVxTeA==", "dev": true }, "chalk": { @@ -6894,9 +6904,9 @@ "dev": true }, "colorette": { - "version": "1.3.0", - "resolved": "https://registry.npmjs.org/colorette/-/colorette-1.3.0.tgz", - "integrity": "sha512-ecORCqbSFP7Wm8Y6lyqMJjexBQqXSF7SSeaTyGGphogUjBlFP9m9o08wy86HL2uB7fMTxtOUzLMk7ogKcxMg1w==", + "version": "1.4.0", + "resolved": "https://registry.npmjs.org/colorette/-/colorette-1.4.0.tgz", + "integrity": "sha512-Y2oEozpomLn7Q3HFP7dpww7AtMJplbM9lGZP6RDfHqmbeRjiwRg4n6VM6j4KLmRke85uWEI7JqF17f3pqdRA0g==", "dev": true }, "combined-stream": { @@ -7108,9 +7118,9 @@ "dev": true }, "electron-to-chromium": { - "version": "1.3.827", - "resolved": "https://registry.npmjs.org/electron-to-chromium/-/electron-to-chromium-1.3.827.tgz", - "integrity": "sha512-ye+4uQOY/jbjRutMcE/EmOcNwUeo1qo9aKL2tPyb09cU3lmxNeyDF4RWiemmkknW+p29h7dyDqy02higTxc9/A==", + "version": "1.3.844", + "resolved": "https://registry.npmjs.org/electron-to-chromium/-/electron-to-chromium-1.3.844.tgz", + "integrity": "sha512-7ES6GQVsbgsUA49/apqub51I9ij8E3QwGqq/IRvO6OPCly3how/YUSg1GPslRWq+BteT2h94iAIQdJbuVVH4Pg==", "dev": true }, "emoji-regex": { @@ -7209,9 +7219,9 @@ } }, "fastq": { - "version": "1.12.0", - "resolved": "https://registry.npmjs.org/fastq/-/fastq-1.12.0.tgz", - "integrity": "sha512-VNX0QkHK3RsXVKr9KrlUv/FoTa0NdbYoHHl7uXHv2rzyHSlxjdNAKug2twd9luJxpcyNeAgf5iPPMutJO67Dfg==", + "version": "1.13.0", + "resolved": "https://registry.npmjs.org/fastq/-/fastq-1.13.0.tgz", + "integrity": "sha512-YpkpUnK8od0o1hmeSc7UUs/eB/vIPWJYjKck2QKIzAf71Vm1AAQ3EbuZB3g2JIy+pg+ERD0vqI79KyZiB2e2Nw==", "dev": true, "requires": { "reusify": "^1.0.4" @@ -7988,6 +7998,12 @@ "minimist": "^1.2.5" } }, + "jsonc-parser": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/jsonc-parser/-/jsonc-parser-3.0.0.tgz", + "integrity": "sha512-fQzRfAbIBnR0IQvftw9FJveWiHp72Fg20giDrHz6TdfB12UH/uue0D3hm57UB5KgAVuniLMCaS8P1IMj9NR7cA==", + "dev": true + }, "just-extend": { "version": "4.2.1", "resolved": "https://registry.npmjs.org/just-extend/-/just-extend-4.2.1.tgz", @@ -8106,9 +8122,9 @@ "dev": true }, "marked": { - "version": "3.0.2", - "resolved": "https://registry.npmjs.org/marked/-/marked-3.0.2.tgz", - "integrity": "sha512-TMJQQ79Z0e3rJYazY0tIoMsFzteUGw9fB3FD+gzuIT3zLuG9L9ckIvUfF51apdJkcqc208jJN2KbtPbOvXtbjA==", + "version": "3.0.4", + "resolved": "https://registry.npmjs.org/marked/-/marked-3.0.4.tgz", + "integrity": "sha512-jBo8AOayNaEcvBhNobg6/BLhdsK3NvnKWJg33MAAPbvTWiG4QBn9gpW1+7RssrKu4K1dKlN+0goVQwV41xEfOA==", "dev": true }, "merge-stream": { @@ -8263,10 +8279,13 @@ } }, "node-fetch": { - "version": "2.6.1", - "resolved": "https://registry.npmjs.org/node-fetch/-/node-fetch-2.6.1.tgz", - "integrity": "sha512-V4aYg89jEoVRxRb2fJdAg8FHvI7cEyYdVAh94HH0UIK8oJxUfkjlDQN9RbMx+bEjP7+ggMiFRprSti032Oipxw==", - "dev": true + "version": "2.6.3", + "resolved": "https://registry.npmjs.org/node-fetch/-/node-fetch-2.6.3.tgz", + "integrity": "sha512-BXSmNTLLDHT0UjQDg5E23x+0n/hPDjySqc0ELE4NpCa2wE5qmmaEWFRP/+v8pfuocchR9l5vFLbSB7CPE2ahvQ==", + "dev": true, + "requires": { + "whatwg-url": "^5.0.0" + } }, "node-preload": { "version": "0.2.1", @@ -8278,9 +8297,9 @@ } }, "node-releases": { - "version": "1.1.75", - "resolved": "https://registry.npmjs.org/node-releases/-/node-releases-1.1.75.tgz", - "integrity": "sha512-Qe5OUajvqrqDSy6wrWFmMwfJ0jVgwiw4T3KqmbTcZ62qW0gQkheXYhcFM1+lOVcGUoRxcEcfyvFMAnDgaF1VWw==", + "version": "1.1.76", + "resolved": "https://registry.npmjs.org/node-releases/-/node-releases-1.1.76.tgz", + "integrity": "sha512-9/IECtNr8dXNmPWmFXepT0/7o5eolGesHUa3mtr0KlgnCvnZxwh2qensKL42JJY2vQKC3nIBXetFAqR+PW1CmA==", "dev": true }, "normalize-path": { @@ -8865,12 +8884,6 @@ "fromentries": "^1.2.0" } }, - "progress": { - "version": "2.0.3", - "resolved": "https://registry.npmjs.org/progress/-/progress-2.0.3.tgz", - "integrity": "sha512-7PiHtLll5LdnKIMw100I+8xJXR5gW2QwWYkT6iJva0bXitZKa/XMrSbdmg3r2Xnaidz9Qumd0VPaMrZlF9V9sA==", - "dev": true - }, "protocols": { "version": "1.4.8", "resolved": "https://registry.npmjs.org/protocols/-/protocols-1.4.8.tgz", @@ -9284,12 +9297,12 @@ } }, "shiki": { - "version": "0.9.10", - "resolved": "https://registry.npmjs.org/shiki/-/shiki-0.9.10.tgz", - "integrity": "sha512-xeM7Oc6hY+6iW5O/T5hor8ul7mEprzyl5y4r5zthEHToQNw7MIhREMgU3r2gKDB0NaMLNrkcEQagudCdzE13Lg==", + "version": "0.9.11", + "resolved": "https://registry.npmjs.org/shiki/-/shiki-0.9.11.tgz", + "integrity": "sha512-tjruNTLFhU0hruCPoJP0y+B9LKOmcqUhTpxn7pcJB3fa+04gFChuEmxmrUfOJ7ZO6Jd+HwMnDHgY3lv3Tqonuw==", "dev": true, "requires": { - "json5": "^2.2.0", + "jsonc-parser": "^3.0.0", "onigasm": "^2.2.5", "vscode-textmate": "5.2.0" } @@ -9306,9 +9319,9 @@ } }, "signal-exit": { - "version": "3.0.3", - "resolved": "https://registry.npmjs.org/signal-exit/-/signal-exit-3.0.3.tgz", - "integrity": "sha512-VUJ49FC8U1OxwZLxIbTTrDvLnf/6TDgxZcK8wxR8zs13xpx7xbG60ndBlhNrFi2EMuFRoeDoJO7wthSLq42EjA==", + "version": "3.0.4", + "resolved": "https://registry.npmjs.org/signal-exit/-/signal-exit-3.0.4.tgz", + "integrity": "sha512-rqYhcAnZ6d/vTPGghdrw7iumdcbXpsk1b8IG/rz+VWV51DM0p7XCtMoJ3qhPLIbp3tvyt3pKRbaaEMZYpHto8Q==", "dev": true }, "sinon": { @@ -9349,9 +9362,9 @@ "dev": true }, "source-map-support": { - "version": "0.5.19", - "resolved": "https://registry.npmjs.org/source-map-support/-/source-map-support-0.5.19.tgz", - "integrity": "sha512-Wonm7zOCIJzBGQdB+thsPar0kYuCIzYvxZwlBa87yi/Mdjv7Tip2cyVbLj5o0cFPN4EVkuTwb3GDDyUx2DGnGw==", + "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==", "dev": true, "requires": { "buffer-from": "^1.0.0", @@ -9509,6 +9522,12 @@ "is-number": "^7.0.0" } }, + "tr46": { + "version": "0.0.3", + "resolved": "https://registry.npmjs.org/tr46/-/tr46-0.0.3.tgz", + "integrity": "sha1-gYT9NH2snNwYWZLzpmIuFLnZq2o=", + "dev": true + }, "ts-node": { "version": "10.2.1", "resolved": "https://registry.npmjs.org/ts-node/-/ts-node-10.2.1.tgz", @@ -9565,27 +9584,18 @@ } }, "typedoc": { - "version": "0.21.9", - "resolved": "https://registry.npmjs.org/typedoc/-/typedoc-0.21.9.tgz", - "integrity": "sha512-VRo7aII4bnYaBBM1lhw4bQFmUcDQV8m8tqgjtc7oXl87jc1Slbhfw2X5MccfcR2YnEClHDWgsiQGgNB8KJXocA==", + "version": "0.22.4", + "resolved": "https://registry.npmjs.org/typedoc/-/typedoc-0.22.4.tgz", + "integrity": "sha512-M/a8NnPxq3/iZNNVjzFCK5gu4m//HTJIPbSS0JQVbkHJPP9wyepR12agylWTSqeVZe0xsbidVtO26+PP7iD/jw==", "dev": true, "requires": { "glob": "^7.1.7", - "handlebars": "^4.7.7", "lunr": "^2.3.9", - "marked": "^3.0.2", - "minimatch": "^3.0.0", - "progress": "^2.0.3", - "shiki": "^0.9.8", - "typedoc-default-themes": "^0.12.10" + "marked": "^3.0.4", + "minimatch": "^3.0.4", + "shiki": "^0.9.11" } }, - "typedoc-default-themes": { - "version": "0.12.10", - "resolved": "https://registry.npmjs.org/typedoc-default-themes/-/typedoc-default-themes-0.12.10.tgz", - "integrity": "sha512-fIS001cAYHkyQPidWXmHuhs8usjP5XVJjWB8oZGqkTowZaz3v7g3KDZeeqE82FBrmkAnIBOY3jgy7lnPnqATbA==", - "dev": true - }, "typedoc-github-wiki-theme": { "version": "0.5.1", "resolved": "https://registry.npmjs.org/typedoc-github-wiki-theme/-/typedoc-github-wiki-theme-0.5.1.tgz", @@ -9594,24 +9604,24 @@ "requires": {} }, "typedoc-plugin-markdown": { - "version": "3.10.4", - "resolved": "https://registry.npmjs.org/typedoc-plugin-markdown/-/typedoc-plugin-markdown-3.10.4.tgz", - "integrity": "sha512-if9w7S9fXLg73AYi/EoRSEhTOZlg3I8mIP8YEmvzSE33VrNXC9/hA0nVcLEwFVJeQY7ay6z67I6kW0KIv7LjeA==", + "version": "3.11.0", + "resolved": "https://registry.npmjs.org/typedoc-plugin-markdown/-/typedoc-plugin-markdown-3.11.0.tgz", + "integrity": "sha512-zewcbzOlMV9nbhLsJhKBpoRW4J32LgbfdqwYfEfzzeE+wGOaOfsM6g7QH+ZKj8n+knH4sLCtk6XMN1TI/a1UuQ==", "dev": true, "requires": { "handlebars": "^4.7.7" } }, "typescript": { - "version": "4.4.2", - "resolved": "https://registry.npmjs.org/typescript/-/typescript-4.4.2.tgz", - "integrity": "sha512-gzP+t5W4hdy4c+68bfcv0t400HVJMMd2+H9B7gae1nQlBzCqvrXX+6GL/b3GAgyTH966pzrZ70/fRjwAtZksSQ==", + "version": "4.4.3", + "resolved": "https://registry.npmjs.org/typescript/-/typescript-4.4.3.tgz", + "integrity": "sha512-4xfscpisVgqqDfPaJo5vkd+Qd/ItkoagnHpufr+i2QCHBsNYp+G7UAoyFl8aPtx879u38wPV65rZ8qbGZijalA==", "dev": true }, "uglify-js": { - "version": "3.14.1", - "resolved": "https://registry.npmjs.org/uglify-js/-/uglify-js-3.14.1.tgz", - "integrity": "sha512-JhS3hmcVaXlp/xSo3PKY5R0JqKs5M3IV+exdLHW99qKvKivPO4Z8qbej6mte17SOPqAOVMjt/XGgWacnFSzM3g==", + "version": "3.14.2", + "resolved": "https://registry.npmjs.org/uglify-js/-/uglify-js-3.14.2.tgz", + "integrity": "sha512-rtPMlmcO4agTUfz10CbgJ1k6UAoXM2gWb3GoMPPZB/+/Ackf8lNWk11K4rYi2D0apgoFRLtQOZhb+/iGNJq26A==", "dev": true, "optional": true }, @@ -9729,6 +9739,22 @@ "defaults": "^1.0.3" } }, + "webidl-conversions": { + "version": "3.0.1", + "resolved": "https://registry.npmjs.org/webidl-conversions/-/webidl-conversions-3.0.1.tgz", + "integrity": "sha1-JFNCdeKnvGvnvIZhHMFq4KVlSHE=", + "dev": true + }, + "whatwg-url": { + "version": "5.0.0", + "resolved": "https://registry.npmjs.org/whatwg-url/-/whatwg-url-5.0.0.tgz", + "integrity": "sha1-lmRU6HZUYuN2RNNib2dCzotwll0=", + "dev": true, + "requires": { + "tr46": "~0.0.3", + "webidl-conversions": "^3.0.0" + } + }, "which": { "version": "2.0.2", "resolved": "https://registry.npmjs.org/which/-/which-2.0.2.tgz", diff --git a/package.json b/package.json index 56a7ed38c65..fc107833fc6 100644 --- a/package.json +++ b/package.json @@ -35,7 +35,7 @@ "devDependencies": { "@istanbuljs/nyc-config-typescript": "^1.0.1", "@types/mocha": "^9.0.0", - "@types/node": "^16.7.10", + "@types/node": "^16.9.4", "@types/sinon": "^10.0.2", "@types/which": "^2.0.1", "@types/yallist": "^4.0.1", @@ -43,12 +43,12 @@ "nyc": "^15.1.0", "release-it": "^14.11.5", "sinon": "^11.1.2", - "source-map-support": "^0.5.19", + "source-map-support": "^0.5.20", "ts-node": "^10.2.1", - "typedoc": "^0.21.9", + "typedoc": "^0.22.4", "typedoc-github-wiki-theme": "^0.5.1", - "typedoc-plugin-markdown": "^3.10.4", - "typescript": "^4.4.2", + "typedoc-plugin-markdown": "^3.11.0", + "typescript": "^4.4.3", "which": "^2.0.2" }, "engines": { From 1819b9c1c4a6ae48fd0597e6fa451c242b707e05 Mon Sep 17 00:00:00 2001 From: leibale Date: Tue, 21 Sep 2021 15:30:25 -0400 Subject: [PATCH 0870/1748] fix #1659 - add support for db-number in client options url --- docs/client-configuration.md | 39 ++++++++++---------- lib/client.spec.ts | 63 ++++++++++++++++++++++++++------ lib/client.ts | 69 +++++++++++++++++++++++++++++++++--- lib/socket.ts | 20 +---------- lib/test-utils.ts | 18 ++++++---- 5 files changed, 149 insertions(+), 60 deletions(-) diff --git a/docs/client-configuration.md b/docs/client-configuration.md index 4b93340ad8f..0c4c0c1ca8f 100644 --- a/docs/client-configuration.md +++ b/docs/client-configuration.md @@ -1,24 +1,25 @@ # `createClient` configuration -| Property | Default | Description | -|--------------------------|------------------------------------------|------------------------------------------------------------------------------------------------------------------------------| -| socket | | Object defining socket connection properties | -| socket.url | | `[redis[s]:]//[[username][:password]@][host][:port]` | -| socket.host | `'localhost'` | Hostname to connect to | -| socket.port | `6379` | Port to connect to | -| socket.username | | ACL username ([see ACL guide](https://redis.io/topics/acl)) | -| socket.password | | ACL password or the old "--requirepass" password | -| socket.connectTimeout | `5000` | The timeout for connecting to the Redis Server (in milliseconds) | -| socket.noDelay | `true` | Enable/disable the use of [`Nagle's algorithm`](https://nodejs.org/api/net.html#net_socket_setnodelay_nodelay) | -| socket.keepAlive | `5000` | Enable/disable the [`keep-alive`](https://nodejs.org/api/net.html#net_socket_setkeepalive_enable_initialdelay) functionality | -| socket.tls | | Set to `true` to enable [TLS Configuration](https://nodejs.org/api/tls.html#tls_tls_connect_options_callback) | -| socket.reconnectStrategy | `retries => Math.min(retries * 50, 500)` | A function containing the [Reconnect Strategy](#reconnect-strategy) logic | -| modules | | Object defining which [Redis Modules](https://redis.io/modules) to include (TODO - document) | -| 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)) | -| isolationPoolOptions | | See the [Isolated Execution Guide](./isolated-execution.md) | +| Property | Default | Description | +|--------------------------|------------------------------------------|-----------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------| +| url | | `redis[s]://[[username][:password]@][host][:port][/db-number]` (see [`redis`](https://www.iana.org/assignments/uri-schemes/prov/redis) and [`rediss`](https://www.iana.org/assignments/uri-schemes/prov/rediss) IANA registration for more details) | +| socket | | Object defining socket connection properties | +| socket.host | `'localhost'` | Hostname to connect to | +| socket.port | `6379` | Port to connect to | +| socket.connectTimeout | `5000` | The timeout for connecting to the Redis Server (in milliseconds) | +| socket.noDelay | `true` | Enable/disable the use of [`Nagle's algorithm`](https://nodejs.org/api/net.html#net_socket_setnodelay_nodelay) | +| socket.keepAlive | `5000` | Enable/disable the [`keep-alive`](https://nodejs.org/api/net.html#net_socket_setkeepalive_enable_initialdelay) functionality | +| socket.tls | | Set to `true` to enable [TLS Configuration](https://nodejs.org/api/tls.html#tls_tls_connect_options_callback) | +| socket.reconnectStrategy | `retries => Math.min(retries * 50, 500)` | A function containing the [Reconnect Strategy](#reconnect-strategy) logic | +| 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](https://redis.io/modules) to include (TODO - document) | +| 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)) | +| isolationPoolOptions | | See the [Isolated Execution Guide](./isolated-execution.md) | ## Reconnect Strategy diff --git a/lib/client.spec.ts b/lib/client.spec.ts index 7f1a534352c..06f8d2bb102 100644 --- a/lib/client.spec.ts +++ b/lib/client.spec.ts @@ -18,6 +18,53 @@ export const SQUARE_SCRIPT = defineScript({ }); describe('Client', () => { + describe('parseURL', () => { + it('redis://user:secret@localhost:6379/0', () => { + assert.deepEqual( + RedisClient.parseURL('redis://user:secret@localhost:6379/0'), + { + socket: { + host: 'localhost', + port: 6379 + }, + username: 'user', + password: 'secret', + database: 0 + } + ); + }); + + it('rediss://user:secret@localhost:6379/0', () => { + assert.deepEqual( + RedisClient.parseURL('rediss://user:secret@localhost:6379/0'), + { + socket: { + host: 'localhost', + port: 6379, + tls: true + }, + username: 'user', + password: 'secret', + database: 0 + } + ); + }); + + it('Invalid protocol', () => { + assert.throws( + () => RedisClient.parseURL('redi://user:secret@localhost:6379/0'), + TypeError + ); + }); + + it('Invalid pathname', () => { + assert.throws( + () => RedisClient.parseURL('redis://user:secret@localhost:6379/NaN'), + TypeError + ); + }); + }); + describe('authentication', () => { itWithClient(TestRedisServers.PASSWORD, 'Client should be authenticated', async client => { assert.equal( @@ -28,10 +75,8 @@ describe('Client', () => { it('should not retry connecting if failed due to wrong auth', async () => { const client = RedisClient.create({ - socket: { - ...TEST_REDIS_SERVERS[TestRedisServers.PASSWORD], - password: 'wrongpassword' - } + ...TEST_REDIS_SERVERS[TestRedisServers.PASSWORD], + password: 'wrongpassword' }); await assert.rejects( @@ -49,7 +94,7 @@ describe('Client', () => { describe('legacyMode', () => { const client = RedisClient.create({ - socket: TEST_REDIS_SERVERS[TestRedisServers.OPEN], + ...TEST_REDIS_SERVERS[TestRedisServers.OPEN], scripts: { square: SQUARE_SCRIPT }, @@ -173,9 +218,7 @@ describe('Client', () => { describe('events', () => { it('connect, ready, end', async () => { - const client = RedisClient.create({ - socket: TEST_REDIS_SERVERS[TestRedisServers.OPEN] - }); + const client = RedisClient.create(TEST_REDIS_SERVERS[TestRedisServers.OPEN]); await Promise.all([ client.connect(), @@ -550,9 +593,7 @@ describe('Client', () => { }); it('client.quit', async () => { - const client = RedisClient.create({ - socket: TEST_REDIS_SERVERS[TestRedisServers.OPEN] - }); + const client = RedisClient.create(TEST_REDIS_SERVERS[TestRedisServers.OPEN]); await client.connect(); diff --git a/lib/client.ts b/lib/client.ts index c9e9cecf924..93afee1ff1a 100644 --- a/lib/client.ts +++ b/lib/client.ts @@ -1,4 +1,4 @@ -import RedisSocket, { RedisSocketOptions } from './socket'; +import RedisSocket, { RedisSocketOptions, RedisNetSocketOptions, RedisTlsSocketOptions } from './socket'; import RedisCommandsQueue, { PubSubListener, PubSubSubscribeCommands, PubSubUnsubscribeCommands, QueueCommandOptions } from './commands-queue'; import COMMANDS, { TransformArgumentsReply } from './commands'; import { RedisCommand, RedisModules, RedisReply } from './commands'; @@ -12,9 +12,14 @@ import { HScanTuple } from './commands/HSCAN'; import { encodeCommand, extendWithDefaultCommands, extendWithModulesAndScripts, transformCommandArguments } from './commander'; import { Pool, Options as PoolOptions, createPool } from 'generic-pool'; import { ClientClosedError } from './errors'; +import { URL } from 'url'; export interface RedisClientOptions { + url?: string; socket?: RedisSocketOptions; + username?: string; + password?: string; + database?: number; modules?: M; scripts?: S; commandsQueueMaxLength?: number; @@ -71,6 +76,45 @@ export default class RedisClient { + // https://www.iana.org/assignments/uri-schemes/prov/redis + const { hostname, port, protocol, username, password, pathname } = new URL(url), + parsed: RedisClientOptions<{}, {}> = { + socket: { + host: hostname + } + }; + + if (protocol === 'rediss:') { + (parsed.socket as RedisTlsSocketOptions).tls = true; + } else if (protocol !== 'redis:') { + throw new TypeError('Invalid protocol'); + } + + if (port) { + (parsed.socket as RedisNetSocketOptions).port = Number(port); + } + + if (username) { + parsed.username = username; + } + + if (password) { + parsed.password = password; + } + + if (pathname.length > 1) { + const database = Number(pathname.substring(1)); + if (isNaN(database)) { + throw new TypeError('Invalid pathname'); + } + + parsed.database = database; + } + + return parsed; + } + readonly #options?: RedisClientOptions; readonly #socket: RedisSocket; readonly #queue: RedisCommandsQueue; @@ -96,7 +140,7 @@ export default class RedisClient) { super(); - this.#options = options; + this.#options = this.#initiateOptions(options); this.#socket = this.#initiateSocket(); this.#queue = this.#initiateQueue(); this.#isolationPool = createPool({ @@ -110,6 +154,23 @@ export default class RedisClient): RedisClientOptions | undefined { + if (options?.url) { + const parsed = RedisClient.parseURL(options.url); + if (options.socket) { + parsed.socket = Object.assign(options.socket, parsed.socket); + } + + Object.assign(options, parsed); + } + + if (options?.database) { + this.#selectedDB = options.database; + } + + return options; + } + #initiateSocket(): RedisSocket { const socketInitiator = async (): Promise => { const v4Commands = this.#options?.legacyMode ? this.#v4 : this, @@ -123,8 +184,8 @@ export default class RedisClient { connectEvent: string; @@ -44,14 +38,6 @@ export default class RedisSocket extends EventEmitter { static #initiateOptions(options?: RedisSocketOptions): RedisSocketOptions { options ??= {}; if (!RedisSocket.#isUnixSocket(options)) { - if (RedisSocket.#isUrlSocket(options)) { - const url = new URL(options.url); - (options as RedisNetSocketOptions).port = Number(url.port); - (options as RedisNetSocketOptions).host = url.hostname; - options.username = url.username; - options.password = url.password; - } - (options as RedisNetSocketOptions).port ??= 6379; (options as RedisNetSocketOptions).host ??= '127.0.0.1'; } @@ -67,10 +53,6 @@ export default class RedisSocket extends EventEmitter { return Math.min(retries * 50, 500); } - static #isUrlSocket(options: RedisSocketOptions): options is RedisUrlSocketOptions { - return Object.prototype.hasOwnProperty.call(options, 'url'); - } - static #isUnixSocket(options: RedisSocketOptions): options is RedisUnixSocketOptions { return Object.prototype.hasOwnProperty.call(options, 'path'); } diff --git a/lib/test-utils.ts b/lib/test-utils.ts index 84685923693..713a1a3434a 100644 --- a/lib/test-utils.ts +++ b/lib/test-utils.ts @@ -1,5 +1,5 @@ import { strict as assert } from 'assert'; -import RedisClient, { RedisClientType } from './client'; +import RedisClient, { RedisClientOptions, RedisClientType } from './client'; import { execSync, spawn } from 'child_process'; import { once } from 'events'; import { RedisSocketOptions } from './socket'; @@ -9,6 +9,8 @@ import RedisCluster, { RedisClusterType } from './cluster'; import { promises as fs } from 'fs'; import { Context as MochaContext } from 'mocha'; import { promiseTimeout } from './utils'; +import { RedisModules } from './commands'; +import { RedisLuaScripts } from './lua-script'; type RedisVersion = [major: number, minor: number, patch: number]; @@ -52,7 +54,7 @@ export enum TestRedisServers { PASSWORD } -export const TEST_REDIS_SERVERS: Record = {}; +export const TEST_REDIS_SERVERS: Record> = {}; export enum TestRedisClusters { OPEN @@ -226,13 +228,17 @@ export async function spawnGlobalRedisCluster(type: TestRedisClusters | null, nu async function spawnOpenServer(): Promise { TEST_REDIS_SERVERS[TestRedisServers.OPEN] = { - port: await spawnGlobalRedisServer() + socket: { + port: await spawnGlobalRedisServer() + } }; } async function spawnPasswordServer(): Promise { TEST_REDIS_SERVERS[TestRedisServers.PASSWORD] = { - port: await spawnGlobalRedisServer(['--requirepass', 'password']), + socket: { + port: await spawnGlobalRedisServer(['--requirepass', 'password']), + }, password: 'password' }; @@ -285,9 +291,7 @@ export function itWithClient( it(title, async function () { if (handleMinimumRedisVersion(this, options?.minimumRedisVersion)) return; - const client = RedisClient.create({ - socket: TEST_REDIS_SERVERS[type] - }); + const client = RedisClient.create(TEST_REDIS_SERVERS[type]); await client.connect(); From ad151cd10d2fb839888583fd8c12c45d2ac3b2f2 Mon Sep 17 00:00:00 2001 From: leibale Date: Tue, 21 Sep 2021 18:45:17 -0400 Subject: [PATCH 0871/1748] fix README, remove unused import, downgrade typedoc & typedoc-plugin-markdown --- README.md | 92 ++++++++------- lib/socket.ts | 1 - package-lock.json | 288 ++++++++++++++++++++++++++-------------------- package.json | 10 +- 4 files changed, 212 insertions(+), 179 deletions(-) diff --git a/README.md b/README.md index 2c465ae69fa..0ac104a2c21 100644 --- a/README.md +++ b/README.md @@ -35,27 +35,25 @@ npm install redis@next ### Basic Example ```typescript -import { createClient } from "redis"; +import { createClient } from 'redis'; (async () => { const client = createClient(); - client.on("error", (err) => console.log("Redis Client Error", err)); + client.on('error', (err) => console.log('Redis Client Error', err)); await client.connect(); - await client.set("key", "value"); - const value = await client.get("key"); + await client.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]`: +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({ - socket: { - url: "redis://alice:foobared@awesome.redis.server:6380", - }, + url: 'redis://alice:foobared@awesome.redis.server:6380', }); ``` @@ -67,18 +65,18 @@ There is built-in support for all of the [out-of-the-box Redis commands](https:/ ```typescript // raw Redis commands -await client.HSET("key", "field", "value"); -await client.HGETALL("key"); +await client.HSET('key', 'field', 'value'); +await client.HGETALL('key'); // friendly JavaScript commands -await client.hSet("key", "field", "value"); -await client.hGetAll("key"); +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", { +await client.set('key', 'value', { EX: 10, NX: true, }); @@ -87,8 +85,8 @@ await client.set("key", "value", { Replies will be transformed into useful data structures: ```typescript -await client.hGetAll("key"); // { field1: 'value1', field2: 'value2' } -await client.hVals("key"); // ['value1', 'value2'] +await client.hGetAll('key'); // { field1: 'value1', field2: 'value2' } +await client.hVals('key'); // ['value1', 'value2'] ``` ### Unsupported Redis Commands @@ -96,9 +94,9 @@ await client.hVals("key"); // ['value1', 'value2'] 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(['SET', 'key', 'value', 'NX']); // 'OK' -await client.sendCommand(["HGETALL", "key"]); // ['key1', 'field1', 'key2', 'field2'] +await client.sendCommand(['HGETALL', 'key']); // ['key1', 'field1', 'key2', 'field2'] ``` ### Transactions (Multi/Exec) @@ -106,12 +104,12 @@ await client.sendCommand(["HGETALL", "key"]); // ['key1', 'field1', 'key2', 'fie 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"); +await client.set('another-key', 'another-value'); const [setKeyReply, otherKeyValue] = await client .multi() - .set("key", "value") - .get("another-key") + .set('key', 'value') + .get('another-key') .exec(); // ['OK', 'another-value'] ``` @@ -126,11 +124,11 @@ Any command can be run on a new connection by specifying the `isolated` option. This pattern works especially well for blocking commands—such as `BLPOP` and `BLMOVE`: ```typescript -import { commandOptions } from "redis"; +import { commandOptions } from 'redis'; -const blPopPromise = client.blPop(commandOptions({ isolated: true }), "key"); +const blPopPromise = client.blPop(commandOptions({ isolated: true }), 'key'); -await client.lPush("key", ["1", "2"]); +await client.lPush('key', ['1', '2']); await blPopPromise; // '2' ``` @@ -150,23 +148,23 @@ await subscriber.connect(); Once you have one, simply subscribe and unsubscribe as needed: ```typescript -await subscriber.subscribe("channel", (message) => { +await subscriber.subscribe('channel', (message) => { console.log(message); // 'message' }); -await subscriber.pSubscribe("channe*", (message, channel) => { +await subscriber.pSubscribe('channe*', (message, channel) => { console.log(message, channel); // 'message', 'channel' }); -await subscriber.unsubscribe("channel"); +await subscriber.unsubscribe('channel'); -await subscriber.pUnsubscribe("channe*"); +await subscriber.pUnsubscribe('channe*'); ``` Publish a message on a channel: ```typescript -await publisher.publish("channel", "message"); +await publisher.publish('channel', 'message'); ``` ### Scan Iterator @@ -183,11 +181,11 @@ for await (const key of client.scanIterator()) { This works with `HSCAN`, `SSCAN`, and `ZSCAN` too: ```typescript -for await (const member of client.hScanIterator("hash")) { +for await (const member of client.hScanIterator('hash')) { } -for await (const { field, value } of client.sScanIterator("set")) { +for await (const { field, value } of client.sScanIterator('set')) { } -for await (const { member, score } of client.zScanIterator("sorted-set")) { +for await (const { member, score } of client.zScanIterator('sorted-set')) { } ``` @@ -195,8 +193,8 @@ You can override the default options by providing a configuration object: ```typescript client.scanIterator({ - TYPE: "string", // `SCAN` only - MATCH: "patter*", + TYPE: 'string', // `SCAN` only + MATCH: 'patter*', COUNT: 100, }); ``` @@ -206,7 +204,7 @@ client.scanIterator({ Define new functions using [Lua scripts](https://redis.io/commands/eval) which execute on the Redis server: ```typescript -import { createClient, defineScript } from "redis"; +import { createClient, defineScript } from 'redis'; (async () => { const client = createClient({ @@ -214,7 +212,7 @@ import { createClient, defineScript } from "redis"; add: defineScript({ NUMBER_OF_KEYS: 1, SCRIPT: - 'local val = redis.pcall("GET", KEYS[1]);' + "return val + ARGV[1];", + "local val = redis.pcall('GET', KEYS[1]);' + 'return val + ARGV[1];", transformArguments(key: string, toAdd: number): Array { return [key, number.toString()]; }, @@ -227,8 +225,8 @@ import { createClient, defineScript } from "redis"; await client.connect(); - await client.set("key", "1"); - await client.add("key", 2); // 3 + await client.set('key', '1'); + await client.add('key', 2); // 3 })(); ``` @@ -237,28 +235,28 @@ import { createClient, defineScript } from "redis"; Connecting to a cluster is a bit different. Create the client by specifying some (or all) of the nodes in your cluster and then use it like a non-clustered client: ```typescript -import { createCluster } from "redis"; +import { createCluster } from 'redis'; (async () => { const cluster = createCluster({ rootNodes: [ { - host: "10.0.0.1", + host: '10.0.0.1', port: 30001, }, { - host: "10.0.0.2", + host: '10.0.0.2', port: 30002, }, ], }); - cluster.on("error", (err) => console.log("Redis Cluster Error", err)); + cluster.on('error', (err) => console.log('Redis Cluster Error', err)); await cluster.connect(); - await cluster.set("key", "value"); - const value = await cluster.get("key"); + await cluster.set('key', 'value'); + const value = await cluster.get('key'); })(); ``` @@ -267,16 +265,16 @@ import { createCluster } from "redis"; 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=="); +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=="), + client.set('Tm9kZSBSZWRpcw==', 'users:1'), + client.sAdd('users:1:tokens', 'Tm9kZSBSZWRpcw=='), ]); ``` diff --git a/lib/socket.ts b/lib/socket.ts index 35673501876..8bc94c5ba07 100644 --- a/lib/socket.ts +++ b/lib/socket.ts @@ -1,7 +1,6 @@ import EventEmitter from 'events'; import net from 'net'; import tls from 'tls'; -import { URL } from 'url'; import { ConnectionTimeoutError, ClientClosedError } from './errors'; import { promiseTimeout } from './utils'; diff --git a/package-lock.json b/package-lock.json index 9986a955bf6..8d37f49d73b 100644 --- a/package-lock.json +++ b/package-lock.json @@ -17,19 +17,19 @@ "devDependencies": { "@istanbuljs/nyc-config-typescript": "^1.0.1", "@types/mocha": "^9.0.0", - "@types/node": "^16.9.4", - "@types/sinon": "^10.0.2", + "@types/node": "^16.9.6", + "@types/sinon": "^10.0.3", "@types/which": "^2.0.1", "@types/yallist": "^4.0.1", "mocha": "^9.1.1", "nyc": "^15.1.0", - "release-it": "^14.11.5", + "release-it": "^14.11.6", "sinon": "^11.1.2", "source-map-support": "^0.5.20", "ts-node": "^10.2.1", - "typedoc": "^0.22.4", + "typedoc": "0.21.9", "typedoc-github-wiki-theme": "^0.5.1", - "typedoc-plugin-markdown": "^3.11.0", + "typedoc-plugin-markdown": "3.10.4", "typescript": "^4.4.3", "which": "^2.0.2" }, @@ -680,12 +680,12 @@ } }, "node_modules/@octokit/plugin-rest-endpoint-methods": { - "version": "5.7.0", - "resolved": "https://registry.npmjs.org/@octokit/plugin-rest-endpoint-methods/-/plugin-rest-endpoint-methods-5.7.0.tgz", - "integrity": "sha512-G7sgccWRYQMwcHJXkDY/sDxbXeKiZkFQqUtzBCwmrzCNj2GQf3VygQ4T/BFL2crLVpIbenkE/c0ErhYOte2MPw==", + "version": "5.10.4", + "resolved": "https://registry.npmjs.org/@octokit/plugin-rest-endpoint-methods/-/plugin-rest-endpoint-methods-5.10.4.tgz", + "integrity": "sha512-Dh+EAMCYR9RUHwQChH94Skl0lM8Fh99auT8ggck/xTzjJrwVzvsd0YH68oRPqp/HxICzmUjLfaQ9sy1o1sfIiA==", "dev": true, "dependencies": { - "@octokit/types": "^6.24.0", + "@octokit/types": "^6.28.1", "deprecation": "^2.3.1" }, "peerDependencies": { @@ -718,15 +718,15 @@ } }, "node_modules/@octokit/rest": { - "version": "18.9.0", - "resolved": "https://registry.npmjs.org/@octokit/rest/-/rest-18.9.0.tgz", - "integrity": "sha512-VrmrE8gjpuOoDAGjrQq2j9ZhOE6LxaqxaQg0yMrrEnnQZy2ZcAnr5qbVfKsMF0up/48PRV/VFS/2GSMhA7nTdA==", + "version": "18.10.0", + "resolved": "https://registry.npmjs.org/@octokit/rest/-/rest-18.10.0.tgz", + "integrity": "sha512-esHR5OKy38bccL/sajHqZudZCvmv4yjovMJzyXlphaUo7xykmtOdILGJ3aAm0mFHmMLmPFmDMJXf39cAjNJsrw==", "dev": true, "dependencies": { - "@octokit/core": "^3.5.0", - "@octokit/plugin-paginate-rest": "^2.6.2", - "@octokit/plugin-request-log": "^1.0.2", - "@octokit/plugin-rest-endpoint-methods": "5.7.0" + "@octokit/core": "^3.5.1", + "@octokit/plugin-paginate-rest": "^2.16.0", + "@octokit/plugin-request-log": "^1.0.4", + "@octokit/plugin-rest-endpoint-methods": "^5.9.0" } }, "node_modules/@octokit/types": { @@ -855,9 +855,9 @@ "dev": true }, "node_modules/@types/node": { - "version": "16.9.4", - "resolved": "https://registry.npmjs.org/@types/node/-/node-16.9.4.tgz", - "integrity": "sha512-KDazLNYAGIuJugdbULwFZULF9qQ13yNWEBFnfVpqlpgAAo6H/qnM9RjBgh0A0kmHf3XxAKLdN5mTIng9iUvVLA==", + "version": "16.9.6", + "resolved": "https://registry.npmjs.org/@types/node/-/node-16.9.6.tgz", + "integrity": "sha512-YHUZhBOMTM3mjFkXVcK+WwAcYmyhe1wL4lfqNtzI0b3qAy7yuSetnM7QJazgE5PFmgVTNGiLOgRFfJMqW7XpSQ==", "dev": true }, "node_modules/@types/parse-json": { @@ -876,9 +876,9 @@ } }, "node_modules/@types/sinon": { - "version": "10.0.2", - "resolved": "https://registry.npmjs.org/@types/sinon/-/sinon-10.0.2.tgz", - "integrity": "sha512-BHn8Bpkapj8Wdfxvh2jWIUoaYB/9/XhsL0oOvBfRagJtKlSl9NWPcFOz2lRukI9szwGxFtYZCTejJSqsGDbdmw==", + "version": "10.0.3", + "resolved": "https://registry.npmjs.org/@types/sinon/-/sinon-10.0.3.tgz", + "integrity": "sha512-XUaFuUOQ3A/r6gS1qCU/USMleascaqGeQpGR1AZ5JdRtBPlzijRzKsik1TuGzvdtPA0mdq42JqaJmJ+Afg1LJg==", "dev": true, "dependencies": { "@sinonjs/fake-timers": "^7.1.0" @@ -1108,12 +1108,12 @@ } }, "node_modules/async-retry": { - "version": "1.3.1", - "resolved": "https://registry.npmjs.org/async-retry/-/async-retry-1.3.1.tgz", - "integrity": "sha512-aiieFW/7h3hY0Bq5d+ktDBejxuwR78vRu9hDUdR8rNhSaQ29VzPL4AoIRG7D/c7tdenwOcKvgPM6tIxB3cB6HA==", + "version": "1.3.3", + "resolved": "https://registry.npmjs.org/async-retry/-/async-retry-1.3.3.tgz", + "integrity": "sha512-wfr/jstw9xNi/0teMHrRW7dsz3Lt5ARhYNZ2ewpadnhaIp5mbALhOAP+EAdsC7t4Z6wqsDVv9+W6gm1Dk9mEyw==", "dev": true, "dependencies": { - "retry": "0.12.0" + "retry": "0.13.1" } }, "node_modules/asynckit": { @@ -1390,9 +1390,9 @@ } }, "node_modules/caniuse-lite": { - "version": "1.0.30001258", - "resolved": "https://registry.npmjs.org/caniuse-lite/-/caniuse-lite-1.0.30001258.tgz", - "integrity": "sha512-RBByOG6xWXUp0CR2/WU2amXz3stjKpSl5J1xU49F1n2OxD//uBZO4wCKUiG+QMGf7CHGfDDcqoKriomoGVxTeA==", + "version": "1.0.30001259", + "resolved": "https://registry.npmjs.org/caniuse-lite/-/caniuse-lite-1.0.30001259.tgz", + "integrity": "sha512-V7mQTFhjITxuk9zBpI6nYsiTXhcPe05l+364nZjK7MFK/E7ibvYBSAXr4YcA6oPR8j3ZLM/LN+lUqUVAQEUZFg==", "dev": true, "funding": { "type": "opencollective", @@ -1626,9 +1626,9 @@ } }, "node_modules/cosmiconfig": { - "version": "7.0.0", - "resolved": "https://registry.npmjs.org/cosmiconfig/-/cosmiconfig-7.0.0.tgz", - "integrity": "sha512-pondGvTuVYDk++upghXJabWzL6Kxu6f26ljFw64Swq9v6sQPUL3EUlVDV56diOjpCayKihL6hVe8exIACU4XcA==", + "version": "7.0.1", + "resolved": "https://registry.npmjs.org/cosmiconfig/-/cosmiconfig-7.0.1.tgz", + "integrity": "sha512-a1YWNUV2HwGimB7dU2s1wUMurNKjpx60HxBB6xUM8Re+2s1g1IIfJvFR0/iCF+XHdE0GMTKTuLR32UQff4TEyQ==", "dev": true, "dependencies": { "@types/parse-json": "^4.0.0", @@ -1845,9 +1845,9 @@ "dev": true }, "node_modules/electron-to-chromium": { - "version": "1.3.844", - "resolved": "https://registry.npmjs.org/electron-to-chromium/-/electron-to-chromium-1.3.844.tgz", - "integrity": "sha512-7ES6GQVsbgsUA49/apqub51I9ij8E3QwGqq/IRvO6OPCly3how/YUSg1GPslRWq+BteT2h94iAIQdJbuVVH4Pg==", + "version": "1.3.846", + "resolved": "https://registry.npmjs.org/electron-to-chromium/-/electron-to-chromium-1.3.846.tgz", + "integrity": "sha512-2jtSwgyiRzybHRxrc2nKI+39wH3AwQgn+sogQ+q814gv8hIFwrcZbV07Ea9f8AmK0ufPVZUvvAG1uZJ+obV4Jw==", "dev": true }, "node_modules/emoji-regex": { @@ -2217,9 +2217,9 @@ } }, "node_modules/git-url-parse": { - "version": "11.5.0", - "resolved": "https://registry.npmjs.org/git-url-parse/-/git-url-parse-11.5.0.tgz", - "integrity": "sha512-TZYSMDeM37r71Lqg1mbnMlOqlHd7BSij9qN7XwTkRqSAYFMihGLGhfHwgqQob3GUhEneKnV4nskN9rbQw2KGxA==", + "version": "11.6.0", + "resolved": "https://registry.npmjs.org/git-url-parse/-/git-url-parse-11.6.0.tgz", + "integrity": "sha512-WWUxvJs5HsyHL6L08wOusa/IXYtMuCAhrMmnTjQPpBU0TTHyDhnOATNH3xNQz7YOQUsqIIPTGr4xiVti1Hsk5g==", "dev": true, "dependencies": { "git-up": "^4.0.0" @@ -2615,9 +2615,9 @@ } }, "node_modules/inquirer": { - "version": "8.1.2", - "resolved": "https://registry.npmjs.org/inquirer/-/inquirer-8.1.2.tgz", - "integrity": "sha512-DHLKJwLPNgkfwNmsuEUKSejJFbkv0FMO9SMiQbjI3n5NQuCrSIBqP66ggqyz2a6t2qEolKrMjhQ3+W/xXgUQ+Q==", + "version": "8.1.5", + "resolved": "https://registry.npmjs.org/inquirer/-/inquirer-8.1.5.tgz", + "integrity": "sha512-G6/9xUqmt/r+UvufSyrPpt84NYwhKZ9jLsgMbQzlx804XErNupor8WQdBnBRrXmBfTPpuwf1sV+ss2ovjgdXIg==", "dev": true, "dependencies": { "ansi-escapes": "^4.2.1", @@ -2628,7 +2628,7 @@ "figures": "^3.0.0", "lodash": "^4.17.21", "mute-stream": "0.0.8", - "ora": "^5.3.0", + "ora": "^5.4.1", "run-async": "^2.4.0", "rxjs": "^7.2.0", "string-width": "^4.1.0", @@ -3425,9 +3425,9 @@ } }, "node_modules/node-fetch": { - "version": "2.6.3", - "resolved": "https://registry.npmjs.org/node-fetch/-/node-fetch-2.6.3.tgz", - "integrity": "sha512-BXSmNTLLDHT0UjQDg5E23x+0n/hPDjySqc0ELE4NpCa2wE5qmmaEWFRP/+v8pfuocchR9l5vFLbSB7CPE2ahvQ==", + "version": "2.6.4", + "resolved": "https://registry.npmjs.org/node-fetch/-/node-fetch-2.6.4.tgz", + "integrity": "sha512-aD1fO+xtLiSCc9vuD+sYMxpIuQyhHscGSkBEo2o5LTV/3bTEAYvdUii29n8LlO5uLCmWdGP7uVUVXFo5SRdkLA==", "dev": true, "dependencies": { "whatwg-url": "^5.0.0" @@ -4210,6 +4210,15 @@ "node": ">=8" } }, + "node_modules/progress": { + "version": "2.0.3", + "resolved": "https://registry.npmjs.org/progress/-/progress-2.0.3.tgz", + "integrity": "sha512-7PiHtLll5LdnKIMw100I+8xJXR5gW2QwWYkT6iJva0bXitZKa/XMrSbdmg3r2Xnaidz9Qumd0VPaMrZlF9V9sA==", + "dev": true, + "engines": { + "node": ">=0.4.0" + } + }, "node_modules/protocols": { "version": "1.4.8", "resolved": "https://registry.npmjs.org/protocols/-/protocols-1.4.8.tgz", @@ -4424,25 +4433,25 @@ } }, "node_modules/release-it": { - "version": "14.11.5", - "resolved": "https://registry.npmjs.org/release-it/-/release-it-14.11.5.tgz", - "integrity": "sha512-9BaPdq7ZKOwtzz3p1mRhg/tOH/cT/y2tUnPYzUwQiVdj42JaGI1Vo2l3WbgK8ICsbFyrhc0tri1+iqI8OvkI1A==", + "version": "14.11.6", + "resolved": "https://registry.npmjs.org/release-it/-/release-it-14.11.6.tgz", + "integrity": "sha512-6BNcuzFZHThBUBJ/xYw/bxZ+58CAwrwf1zgmjq2Ibl3nlDZbjphHG6iqxkJu7mZ8TIWs6NjloEAhqpjeXoN//Q==", "dev": true, "dependencies": { "@iarna/toml": "2.2.5", - "@octokit/rest": "18.9.0", - "async-retry": "1.3.1", + "@octokit/rest": "18.10.0", + "async-retry": "1.3.3", "chalk": "4.1.2", - "cosmiconfig": "7.0.0", + "cosmiconfig": "7.0.1", "debug": "4.3.2", "deprecated-obj": "2.0.0", "execa": "5.1.1", "form-data": "4.0.0", - "git-url-parse": "11.5.0", + "git-url-parse": "11.6.0", "globby": "11.0.4", "got": "11.8.2", "import-cwd": "3.0.0", - "inquirer": "8.1.2", + "inquirer": "8.1.5", "is-ci": "3.0.0", "lodash": "4.17.21", "mime-types": "2.1.32", @@ -4612,9 +4621,9 @@ } }, "node_modules/retry": { - "version": "0.12.0", - "resolved": "https://registry.npmjs.org/retry/-/retry-0.12.0.tgz", - "integrity": "sha1-G0KmJmoh8HQh0bC1S33BZ7AcATs=", + "version": "0.13.1", + "resolved": "https://registry.npmjs.org/retry/-/retry-0.13.1.tgz", + "integrity": "sha512-XQBQ3I8W1Cge0Seh+6gjj03LbmRFWuoszgK9ooCpwYIrhhoO80pfq4cUkU5DkknwfOfFteRwlZ56PYOGYyFWdg==", "dev": true, "engines": { "node": ">= 4" @@ -5166,16 +5175,19 @@ } }, "node_modules/typedoc": { - "version": "0.22.4", - "resolved": "https://registry.npmjs.org/typedoc/-/typedoc-0.22.4.tgz", - "integrity": "sha512-M/a8NnPxq3/iZNNVjzFCK5gu4m//HTJIPbSS0JQVbkHJPP9wyepR12agylWTSqeVZe0xsbidVtO26+PP7iD/jw==", + "version": "0.21.9", + "resolved": "https://registry.npmjs.org/typedoc/-/typedoc-0.21.9.tgz", + "integrity": "sha512-VRo7aII4bnYaBBM1lhw4bQFmUcDQV8m8tqgjtc7oXl87jc1Slbhfw2X5MccfcR2YnEClHDWgsiQGgNB8KJXocA==", "dev": true, "dependencies": { "glob": "^7.1.7", + "handlebars": "^4.7.7", "lunr": "^2.3.9", - "marked": "^3.0.4", - "minimatch": "^3.0.4", - "shiki": "^0.9.11" + "marked": "^3.0.2", + "minimatch": "^3.0.0", + "progress": "^2.0.3", + "shiki": "^0.9.8", + "typedoc-default-themes": "^0.12.10" }, "bin": { "typedoc": "bin/typedoc" @@ -5187,6 +5199,15 @@ "typescript": "4.0.x || 4.1.x || 4.2.x || 4.3.x || 4.4.x" } }, + "node_modules/typedoc-default-themes": { + "version": "0.12.10", + "resolved": "https://registry.npmjs.org/typedoc-default-themes/-/typedoc-default-themes-0.12.10.tgz", + "integrity": "sha512-fIS001cAYHkyQPidWXmHuhs8usjP5XVJjWB8oZGqkTowZaz3v7g3KDZeeqE82FBrmkAnIBOY3jgy7lnPnqATbA==", + "dev": true, + "engines": { + "node": ">= 8" + } + }, "node_modules/typedoc-github-wiki-theme": { "version": "0.5.1", "resolved": "https://registry.npmjs.org/typedoc-github-wiki-theme/-/typedoc-github-wiki-theme-0.5.1.tgz", @@ -5198,15 +5219,15 @@ } }, "node_modules/typedoc-plugin-markdown": { - "version": "3.11.0", - "resolved": "https://registry.npmjs.org/typedoc-plugin-markdown/-/typedoc-plugin-markdown-3.11.0.tgz", - "integrity": "sha512-zewcbzOlMV9nbhLsJhKBpoRW4J32LgbfdqwYfEfzzeE+wGOaOfsM6g7QH+ZKj8n+knH4sLCtk6XMN1TI/a1UuQ==", + "version": "3.10.4", + "resolved": "https://registry.npmjs.org/typedoc-plugin-markdown/-/typedoc-plugin-markdown-3.10.4.tgz", + "integrity": "sha512-if9w7S9fXLg73AYi/EoRSEhTOZlg3I8mIP8YEmvzSE33VrNXC9/hA0nVcLEwFVJeQY7ay6z67I6kW0KIv7LjeA==", "dev": true, "dependencies": { "handlebars": "^4.7.7" }, "peerDependencies": { - "typedoc": ">=0.22.0" + "typedoc": ">=0.21.2" } }, "node_modules/typescript": { @@ -6207,12 +6228,12 @@ "requires": {} }, "@octokit/plugin-rest-endpoint-methods": { - "version": "5.7.0", - "resolved": "https://registry.npmjs.org/@octokit/plugin-rest-endpoint-methods/-/plugin-rest-endpoint-methods-5.7.0.tgz", - "integrity": "sha512-G7sgccWRYQMwcHJXkDY/sDxbXeKiZkFQqUtzBCwmrzCNj2GQf3VygQ4T/BFL2crLVpIbenkE/c0ErhYOte2MPw==", + "version": "5.10.4", + "resolved": "https://registry.npmjs.org/@octokit/plugin-rest-endpoint-methods/-/plugin-rest-endpoint-methods-5.10.4.tgz", + "integrity": "sha512-Dh+EAMCYR9RUHwQChH94Skl0lM8Fh99auT8ggck/xTzjJrwVzvsd0YH68oRPqp/HxICzmUjLfaQ9sy1o1sfIiA==", "dev": true, "requires": { - "@octokit/types": "^6.24.0", + "@octokit/types": "^6.28.1", "deprecation": "^2.3.1" } }, @@ -6242,15 +6263,15 @@ } }, "@octokit/rest": { - "version": "18.9.0", - "resolved": "https://registry.npmjs.org/@octokit/rest/-/rest-18.9.0.tgz", - "integrity": "sha512-VrmrE8gjpuOoDAGjrQq2j9ZhOE6LxaqxaQg0yMrrEnnQZy2ZcAnr5qbVfKsMF0up/48PRV/VFS/2GSMhA7nTdA==", + "version": "18.10.0", + "resolved": "https://registry.npmjs.org/@octokit/rest/-/rest-18.10.0.tgz", + "integrity": "sha512-esHR5OKy38bccL/sajHqZudZCvmv4yjovMJzyXlphaUo7xykmtOdILGJ3aAm0mFHmMLmPFmDMJXf39cAjNJsrw==", "dev": true, "requires": { - "@octokit/core": "^3.5.0", - "@octokit/plugin-paginate-rest": "^2.6.2", - "@octokit/plugin-request-log": "^1.0.2", - "@octokit/plugin-rest-endpoint-methods": "5.7.0" + "@octokit/core": "^3.5.1", + "@octokit/plugin-paginate-rest": "^2.16.0", + "@octokit/plugin-request-log": "^1.0.4", + "@octokit/plugin-rest-endpoint-methods": "^5.9.0" } }, "@octokit/types": { @@ -6370,9 +6391,9 @@ "dev": true }, "@types/node": { - "version": "16.9.4", - "resolved": "https://registry.npmjs.org/@types/node/-/node-16.9.4.tgz", - "integrity": "sha512-KDazLNYAGIuJugdbULwFZULF9qQ13yNWEBFnfVpqlpgAAo6H/qnM9RjBgh0A0kmHf3XxAKLdN5mTIng9iUvVLA==", + "version": "16.9.6", + "resolved": "https://registry.npmjs.org/@types/node/-/node-16.9.6.tgz", + "integrity": "sha512-YHUZhBOMTM3mjFkXVcK+WwAcYmyhe1wL4lfqNtzI0b3qAy7yuSetnM7QJazgE5PFmgVTNGiLOgRFfJMqW7XpSQ==", "dev": true }, "@types/parse-json": { @@ -6391,9 +6412,9 @@ } }, "@types/sinon": { - "version": "10.0.2", - "resolved": "https://registry.npmjs.org/@types/sinon/-/sinon-10.0.2.tgz", - "integrity": "sha512-BHn8Bpkapj8Wdfxvh2jWIUoaYB/9/XhsL0oOvBfRagJtKlSl9NWPcFOz2lRukI9szwGxFtYZCTejJSqsGDbdmw==", + "version": "10.0.3", + "resolved": "https://registry.npmjs.org/@types/sinon/-/sinon-10.0.3.tgz", + "integrity": "sha512-XUaFuUOQ3A/r6gS1qCU/USMleascaqGeQpGR1AZ5JdRtBPlzijRzKsik1TuGzvdtPA0mdq42JqaJmJ+Afg1LJg==", "dev": true, "requires": { "@sinonjs/fake-timers": "^7.1.0" @@ -6570,12 +6591,12 @@ "dev": true }, "async-retry": { - "version": "1.3.1", - "resolved": "https://registry.npmjs.org/async-retry/-/async-retry-1.3.1.tgz", - "integrity": "sha512-aiieFW/7h3hY0Bq5d+ktDBejxuwR78vRu9hDUdR8rNhSaQ29VzPL4AoIRG7D/c7tdenwOcKvgPM6tIxB3cB6HA==", + "version": "1.3.3", + "resolved": "https://registry.npmjs.org/async-retry/-/async-retry-1.3.3.tgz", + "integrity": "sha512-wfr/jstw9xNi/0teMHrRW7dsz3Lt5ARhYNZ2ewpadnhaIp5mbALhOAP+EAdsC7t4Z6wqsDVv9+W6gm1Dk9mEyw==", "dev": true, "requires": { - "retry": "0.12.0" + "retry": "0.13.1" } }, "asynckit": { @@ -6770,9 +6791,9 @@ "dev": true }, "caniuse-lite": { - "version": "1.0.30001258", - "resolved": "https://registry.npmjs.org/caniuse-lite/-/caniuse-lite-1.0.30001258.tgz", - "integrity": "sha512-RBByOG6xWXUp0CR2/WU2amXz3stjKpSl5J1xU49F1n2OxD//uBZO4wCKUiG+QMGf7CHGfDDcqoKriomoGVxTeA==", + "version": "1.0.30001259", + "resolved": "https://registry.npmjs.org/caniuse-lite/-/caniuse-lite-1.0.30001259.tgz", + "integrity": "sha512-V7mQTFhjITxuk9zBpI6nYsiTXhcPe05l+364nZjK7MFK/E7ibvYBSAXr4YcA6oPR8j3ZLM/LN+lUqUVAQEUZFg==", "dev": true }, "chalk": { @@ -6954,9 +6975,9 @@ } }, "cosmiconfig": { - "version": "7.0.0", - "resolved": "https://registry.npmjs.org/cosmiconfig/-/cosmiconfig-7.0.0.tgz", - "integrity": "sha512-pondGvTuVYDk++upghXJabWzL6Kxu6f26ljFw64Swq9v6sQPUL3EUlVDV56diOjpCayKihL6hVe8exIACU4XcA==", + "version": "7.0.1", + "resolved": "https://registry.npmjs.org/cosmiconfig/-/cosmiconfig-7.0.1.tgz", + "integrity": "sha512-a1YWNUV2HwGimB7dU2s1wUMurNKjpx60HxBB6xUM8Re+2s1g1IIfJvFR0/iCF+XHdE0GMTKTuLR32UQff4TEyQ==", "dev": true, "requires": { "@types/parse-json": "^4.0.0", @@ -7118,9 +7139,9 @@ "dev": true }, "electron-to-chromium": { - "version": "1.3.844", - "resolved": "https://registry.npmjs.org/electron-to-chromium/-/electron-to-chromium-1.3.844.tgz", - "integrity": "sha512-7ES6GQVsbgsUA49/apqub51I9ij8E3QwGqq/IRvO6OPCly3how/YUSg1GPslRWq+BteT2h94iAIQdJbuVVH4Pg==", + "version": "1.3.846", + "resolved": "https://registry.npmjs.org/electron-to-chromium/-/electron-to-chromium-1.3.846.tgz", + "integrity": "sha512-2jtSwgyiRzybHRxrc2nKI+39wH3AwQgn+sogQ+q814gv8hIFwrcZbV07Ea9f8AmK0ufPVZUvvAG1uZJ+obV4Jw==", "dev": true }, "emoji-regex": { @@ -7383,9 +7404,9 @@ } }, "git-url-parse": { - "version": "11.5.0", - "resolved": "https://registry.npmjs.org/git-url-parse/-/git-url-parse-11.5.0.tgz", - "integrity": "sha512-TZYSMDeM37r71Lqg1mbnMlOqlHd7BSij9qN7XwTkRqSAYFMihGLGhfHwgqQob3GUhEneKnV4nskN9rbQw2KGxA==", + "version": "11.6.0", + "resolved": "https://registry.npmjs.org/git-url-parse/-/git-url-parse-11.6.0.tgz", + "integrity": "sha512-WWUxvJs5HsyHL6L08wOusa/IXYtMuCAhrMmnTjQPpBU0TTHyDhnOATNH3xNQz7YOQUsqIIPTGr4xiVti1Hsk5g==", "dev": true, "requires": { "git-up": "^4.0.0" @@ -7664,9 +7685,9 @@ "dev": true }, "inquirer": { - "version": "8.1.2", - "resolved": "https://registry.npmjs.org/inquirer/-/inquirer-8.1.2.tgz", - "integrity": "sha512-DHLKJwLPNgkfwNmsuEUKSejJFbkv0FMO9SMiQbjI3n5NQuCrSIBqP66ggqyz2a6t2qEolKrMjhQ3+W/xXgUQ+Q==", + "version": "8.1.5", + "resolved": "https://registry.npmjs.org/inquirer/-/inquirer-8.1.5.tgz", + "integrity": "sha512-G6/9xUqmt/r+UvufSyrPpt84NYwhKZ9jLsgMbQzlx804XErNupor8WQdBnBRrXmBfTPpuwf1sV+ss2ovjgdXIg==", "dev": true, "requires": { "ansi-escapes": "^4.2.1", @@ -7677,7 +7698,7 @@ "figures": "^3.0.0", "lodash": "^4.17.21", "mute-stream": "0.0.8", - "ora": "^5.3.0", + "ora": "^5.4.1", "run-async": "^2.4.0", "rxjs": "^7.2.0", "string-width": "^4.1.0", @@ -8279,9 +8300,9 @@ } }, "node-fetch": { - "version": "2.6.3", - "resolved": "https://registry.npmjs.org/node-fetch/-/node-fetch-2.6.3.tgz", - "integrity": "sha512-BXSmNTLLDHT0UjQDg5E23x+0n/hPDjySqc0ELE4NpCa2wE5qmmaEWFRP/+v8pfuocchR9l5vFLbSB7CPE2ahvQ==", + "version": "2.6.4", + "resolved": "https://registry.npmjs.org/node-fetch/-/node-fetch-2.6.4.tgz", + "integrity": "sha512-aD1fO+xtLiSCc9vuD+sYMxpIuQyhHscGSkBEo2o5LTV/3bTEAYvdUii29n8LlO5uLCmWdGP7uVUVXFo5SRdkLA==", "dev": true, "requires": { "whatwg-url": "^5.0.0" @@ -8884,6 +8905,12 @@ "fromentries": "^1.2.0" } }, + "progress": { + "version": "2.0.3", + "resolved": "https://registry.npmjs.org/progress/-/progress-2.0.3.tgz", + "integrity": "sha512-7PiHtLll5LdnKIMw100I+8xJXR5gW2QwWYkT6iJva0bXitZKa/XMrSbdmg3r2Xnaidz9Qumd0VPaMrZlF9V9sA==", + "dev": true + }, "protocols": { "version": "1.4.8", "resolved": "https://registry.npmjs.org/protocols/-/protocols-1.4.8.tgz", @@ -9038,25 +9065,25 @@ } }, "release-it": { - "version": "14.11.5", - "resolved": "https://registry.npmjs.org/release-it/-/release-it-14.11.5.tgz", - "integrity": "sha512-9BaPdq7ZKOwtzz3p1mRhg/tOH/cT/y2tUnPYzUwQiVdj42JaGI1Vo2l3WbgK8ICsbFyrhc0tri1+iqI8OvkI1A==", + "version": "14.11.6", + "resolved": "https://registry.npmjs.org/release-it/-/release-it-14.11.6.tgz", + "integrity": "sha512-6BNcuzFZHThBUBJ/xYw/bxZ+58CAwrwf1zgmjq2Ibl3nlDZbjphHG6iqxkJu7mZ8TIWs6NjloEAhqpjeXoN//Q==", "dev": true, "requires": { "@iarna/toml": "2.2.5", - "@octokit/rest": "18.9.0", - "async-retry": "1.3.1", + "@octokit/rest": "18.10.0", + "async-retry": "1.3.3", "chalk": "4.1.2", - "cosmiconfig": "7.0.0", + "cosmiconfig": "7.0.1", "debug": "4.3.2", "deprecated-obj": "2.0.0", "execa": "5.1.1", "form-data": "4.0.0", - "git-url-parse": "11.5.0", + "git-url-parse": "11.6.0", "globby": "11.0.4", "got": "11.8.2", "import-cwd": "3.0.0", - "inquirer": "8.1.2", + "inquirer": "8.1.5", "is-ci": "3.0.0", "lodash": "4.17.21", "mime-types": "2.1.32", @@ -9184,9 +9211,9 @@ } }, "retry": { - "version": "0.12.0", - "resolved": "https://registry.npmjs.org/retry/-/retry-0.12.0.tgz", - "integrity": "sha1-G0KmJmoh8HQh0bC1S33BZ7AcATs=", + "version": "0.13.1", + "resolved": "https://registry.npmjs.org/retry/-/retry-0.13.1.tgz", + "integrity": "sha512-XQBQ3I8W1Cge0Seh+6gjj03LbmRFWuoszgK9ooCpwYIrhhoO80pfq4cUkU5DkknwfOfFteRwlZ56PYOGYyFWdg==", "dev": true }, "reusify": { @@ -9584,18 +9611,27 @@ } }, "typedoc": { - "version": "0.22.4", - "resolved": "https://registry.npmjs.org/typedoc/-/typedoc-0.22.4.tgz", - "integrity": "sha512-M/a8NnPxq3/iZNNVjzFCK5gu4m//HTJIPbSS0JQVbkHJPP9wyepR12agylWTSqeVZe0xsbidVtO26+PP7iD/jw==", + "version": "0.21.9", + "resolved": "https://registry.npmjs.org/typedoc/-/typedoc-0.21.9.tgz", + "integrity": "sha512-VRo7aII4bnYaBBM1lhw4bQFmUcDQV8m8tqgjtc7oXl87jc1Slbhfw2X5MccfcR2YnEClHDWgsiQGgNB8KJXocA==", "dev": true, "requires": { "glob": "^7.1.7", + "handlebars": "^4.7.7", "lunr": "^2.3.9", - "marked": "^3.0.4", - "minimatch": "^3.0.4", - "shiki": "^0.9.11" + "marked": "^3.0.2", + "minimatch": "^3.0.0", + "progress": "^2.0.3", + "shiki": "^0.9.8", + "typedoc-default-themes": "^0.12.10" } }, + "typedoc-default-themes": { + "version": "0.12.10", + "resolved": "https://registry.npmjs.org/typedoc-default-themes/-/typedoc-default-themes-0.12.10.tgz", + "integrity": "sha512-fIS001cAYHkyQPidWXmHuhs8usjP5XVJjWB8oZGqkTowZaz3v7g3KDZeeqE82FBrmkAnIBOY3jgy7lnPnqATbA==", + "dev": true + }, "typedoc-github-wiki-theme": { "version": "0.5.1", "resolved": "https://registry.npmjs.org/typedoc-github-wiki-theme/-/typedoc-github-wiki-theme-0.5.1.tgz", @@ -9604,9 +9640,9 @@ "requires": {} }, "typedoc-plugin-markdown": { - "version": "3.11.0", - "resolved": "https://registry.npmjs.org/typedoc-plugin-markdown/-/typedoc-plugin-markdown-3.11.0.tgz", - "integrity": "sha512-zewcbzOlMV9nbhLsJhKBpoRW4J32LgbfdqwYfEfzzeE+wGOaOfsM6g7QH+ZKj8n+knH4sLCtk6XMN1TI/a1UuQ==", + "version": "3.10.4", + "resolved": "https://registry.npmjs.org/typedoc-plugin-markdown/-/typedoc-plugin-markdown-3.10.4.tgz", + "integrity": "sha512-if9w7S9fXLg73AYi/EoRSEhTOZlg3I8mIP8YEmvzSE33VrNXC9/hA0nVcLEwFVJeQY7ay6z67I6kW0KIv7LjeA==", "dev": true, "requires": { "handlebars": "^4.7.7" diff --git a/package.json b/package.json index fc107833fc6..ec77a93bb55 100644 --- a/package.json +++ b/package.json @@ -35,19 +35,19 @@ "devDependencies": { "@istanbuljs/nyc-config-typescript": "^1.0.1", "@types/mocha": "^9.0.0", - "@types/node": "^16.9.4", - "@types/sinon": "^10.0.2", + "@types/node": "^16.9.6", + "@types/sinon": "^10.0.3", "@types/which": "^2.0.1", "@types/yallist": "^4.0.1", "mocha": "^9.1.1", "nyc": "^15.1.0", - "release-it": "^14.11.5", + "release-it": "^14.11.6", "sinon": "^11.1.2", "source-map-support": "^0.5.20", "ts-node": "^10.2.1", - "typedoc": "^0.22.4", + "typedoc": "0.21.9", "typedoc-github-wiki-theme": "^0.5.1", - "typedoc-plugin-markdown": "^3.11.0", + "typedoc-plugin-markdown": "3.10.4", "typescript": "^4.4.3", "which": "^2.0.2" }, From 1c13a6575f1f68e6779c32bcbacf91db40058ec8 Mon Sep 17 00:00:00 2001 From: leibale Date: Tue, 21 Sep 2021 18:49:21 -0400 Subject: [PATCH 0872/1748] update client-configurations.md --- docs/client-configuration.md | 1 + 1 file changed, 1 insertion(+) diff --git a/docs/client-configuration.md b/docs/client-configuration.md index 0c4c0c1ca8f..11fdb0a6819 100644 --- a/docs/client-configuration.md +++ b/docs/client-configuration.md @@ -6,6 +6,7 @@ | socket | | Object defining socket connection properties | | socket.host | `'localhost'` | Hostname to connect to | | socket.port | `6379` | Port to connect to | +| socket.path | | UNIX Socket to connect to | | socket.connectTimeout | `5000` | The timeout for connecting to the Redis Server (in milliseconds) | | socket.noDelay | `true` | Enable/disable the use of [`Nagle's algorithm`](https://nodejs.org/api/net.html#net_socket_setnodelay_nodelay) | | socket.keepAlive | `5000` | Enable/disable the [`keep-alive`](https://nodejs.org/api/net.html#net_socket_setkeepalive_enable_initialdelay) functionality | From 9237a4e68aef4aae5af291cbb966a46d6715682f Mon Sep 17 00:00:00 2001 From: leibale Date: Thu, 23 Sep 2021 11:06:30 -0400 Subject: [PATCH 0873/1748] fix README --- README.md | 1 + 1 file changed, 1 insertion(+) diff --git a/README.md b/README.md index 0ac104a2c21..c5f0ea1a1c9 100644 --- a/README.md +++ b/README.md @@ -287,6 +287,7 @@ Thank you to all the people who already contributed to Node Redis! + ## License This repository is licensed under the "MIT" license. See [LICENSE](LICENSE). From 42dcf802b14ff5b1aeda3c87eba9dd3426107b31 Mon Sep 17 00:00:00 2001 From: leibale Date: Thu, 23 Sep 2021 16:17:00 -0400 Subject: [PATCH 0874/1748] add CLUSTER_SLOTS, add some tests --- lib/client.spec.ts | 29 ++++++++++++ lib/commands/CLUSTER_SLOTS.spec.ts | 76 ++++++++++++++++++++++++++++++ lib/commands/CLUSTER_SLOTS.ts | 41 ++++++++++++++++ lib/commands/index.ts | 3 ++ 4 files changed, 149 insertions(+) create mode 100644 lib/commands/CLUSTER_SLOTS.spec.ts create mode 100644 lib/commands/CLUSTER_SLOTS.ts diff --git a/lib/client.spec.ts b/lib/client.spec.ts index 06f8d2bb102..9f600c79185 100644 --- a/lib/client.spec.ts +++ b/lib/client.spec.ts @@ -5,6 +5,7 @@ import RedisClient from './client'; import { AbortError, ClientClosedError, ConnectionTimeoutError, WatchError } from './errors'; import { defineScript } from './lua-script'; import { spy } from 'sinon'; +import { RedisNetSocketOptions } from './socket'; export const SQUARE_SCRIPT = defineScript({ NUMBER_OF_KEYS: 0, @@ -63,6 +64,34 @@ describe('Client', () => { TypeError ); }); + + it('redis://localhost', () => { + assert.deepEqual( + RedisClient.parseURL('redis://localhost'), + { + socket: { + host: 'localhost', + } + } + ); + }); + + it('createClient with url', async () => { + const client = RedisClient.create({ + url: `redis://localhost:${(TEST_REDIS_SERVERS[TestRedisServers.OPEN].socket as RedisNetSocketOptions)!.port!.toString()}/1` + }); + + await client.connect(); + + try { + assert.equal( + (await client.clientInfo()).db, + 1 + ); + } finally { + await client.disconnect(); + } + }); }); describe('authentication', () => { diff --git a/lib/commands/CLUSTER_SLOTS.spec.ts b/lib/commands/CLUSTER_SLOTS.spec.ts new file mode 100644 index 00000000000..ec6773bcdd4 --- /dev/null +++ b/lib/commands/CLUSTER_SLOTS.spec.ts @@ -0,0 +1,76 @@ +import { strict as assert } from 'assert'; +import { transformArguments, transformReply } from './CLUSTER_SLOTS'; + +describe('CLUSTER SLOTS', () => { + it('transformArguments', () => { + assert.deepEqual( + transformArguments(), + ['CLUSTER', 'SLOTS'] + ); + }); + + it('transformReply', () => { + assert.deepEqual( + transformReply([ + [ + 0, + 5460, + ['127.0.0.1', 30001, '09dbe9720cda62f7865eabc5fd8857c5d2678366'], + ['127.0.0.1', 30004, '821d8ca00d7ccf931ed3ffc7e3db0599d2271abf'] + ], + [ + 5461, + 10922, + ['127.0.0.1', 30002, 'c9d93d9f2c0c524ff34cc11838c2003d8c29e013'], + ['127.0.0.1', 30005, 'faadb3eb99009de4ab72ad6b6ed87634c7ee410f'] + ], + [ + 10923, + 16383, + ['127.0.0.1', 30003, '044ec91f325b7595e76dbcb18cc688b6a5b434a1'], + ['127.0.0.1', 30006, '58e6e48d41228013e5d9c1c37c5060693925e97e'] + ] + ]), + [{ + from: 0, + to: 5460, + master: { + ip: '127.0.0.1', + port: 30001, + id: '09dbe9720cda62f7865eabc5fd8857c5d2678366' + }, + replicas: [{ + ip: '127.0.0.1', + port: 30004, + id: '821d8ca00d7ccf931ed3ffc7e3db0599d2271abf' + }] + }, { + from: 5461, + to: 10922, + master: { + ip: '127.0.0.1', + port: 30002, + id: 'c9d93d9f2c0c524ff34cc11838c2003d8c29e013' + }, + replicas: [{ + ip: '127.0.0.1', + port: 30005, + id: 'faadb3eb99009de4ab72ad6b6ed87634c7ee410f' + }] + }, { + from: 10923, + to: 16383, + master: { + ip: '127.0.0.1', + port: 30003, + id: '044ec91f325b7595e76dbcb18cc688b6a5b434a1' + }, + replicas: [{ + ip: '127.0.0.1', + port: 30006, + id: '58e6e48d41228013e5d9c1c37c5060693925e97e' + }] + }] + ) + }); +}); diff --git a/lib/commands/CLUSTER_SLOTS.ts b/lib/commands/CLUSTER_SLOTS.ts new file mode 100644 index 00000000000..b4672e731ac --- /dev/null +++ b/lib/commands/CLUSTER_SLOTS.ts @@ -0,0 +1,41 @@ +import { TransformArgumentsReply } from '.'; + +export function transformArguments(): TransformArgumentsReply { + return ['CLUSTER', 'SLOTS']; +} + +type ClusterSlotsRawNode = [ip: string, port: number, id: string]; + +type ClusterSlotsRawReply = Array<[from: number, to: number, master: ClusterSlotsRawNode, ...replicas: Array]>; + +type ClusterSlotsNode = { + ip: string; + port: number; + id: string; +}; + +export type ClusterSlotsReply = Array<{ + from: number; + to: number; + master: ClusterSlotsNode; + replicas: Array; +}>; + +export function transformReply(reply: ClusterSlotsRawReply): ClusterSlotsReply { + return reply.map(([from, to, master, ...replicas]) => { + return { + from, + to, + master: transformNode(master), + replicas: replicas.map(transformNode) + }; + }); +} + +function transformNode([ip, port, id]: ClusterSlotsRawNode): ClusterSlotsNode { + return { + ip, + port, + id + }; +} diff --git a/lib/commands/index.ts b/lib/commands/index.ts index 93220630980..89581090e58 100644 --- a/lib/commands/index.ts +++ b/lib/commands/index.ts @@ -34,6 +34,7 @@ import * as CLUSTER_NODES from './CLUSTER_NODES'; import * as CLUSTER_MEET from './CLUSTER_MEET'; import * as CLUSTER_RESET from './CLUSTER_RESET'; import * as CLUSTER_SETSLOT from './CLUSTER_SETSLOT'; +import * as CLUSTER_SLOTS from './CLUSTER_SLOTS'; import * as CONFIG_GET from './CONFIG_GET'; import * as CONFIG_RESETASTAT from './CONFIG_RESETSTAT'; import * as CONFIG_REWRITE from './CONFIG_REWRITE'; @@ -317,6 +318,8 @@ export default { clusterReset: CLUSTER_RESET, CLUSTER_SETSLOT, clusterSetSlot: CLUSTER_SETSLOT, + CLUSTER_SLOTS, + clusterSlots: CLUSTER_SLOTS, CONFIG_GET, configGet: CONFIG_GET, CONFIG_RESETASTAT, From 7d286e7ebe38c9a2f1f3161581f09410494bb041 Mon Sep 17 00:00:00 2001 From: leibale Date: Thu, 23 Sep 2021 16:33:47 -0400 Subject: [PATCH 0875/1748] fix "createClient with url" test with redis 5 --- lib/client.spec.ts | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/lib/client.spec.ts b/lib/client.spec.ts index 9f600c79185..2cf6ea4964e 100644 --- a/lib/client.spec.ts +++ b/lib/client.spec.ts @@ -85,13 +85,13 @@ describe('Client', () => { try { assert.equal( - (await client.clientInfo()).db, - 1 + await client.ping(), + 'PONG' ); } finally { await client.disconnect(); } - }); + }) }); describe('authentication', () => { From 06ee6af14a010e5ff9172c87fa694505d100a63b Mon Sep 17 00:00:00 2001 From: leibale Date: Thu, 23 Sep 2021 16:35:55 -0400 Subject: [PATCH 0876/1748] remove unused imports --- lib/cluster.ts | 1 - lib/commander.ts | 1 - 2 files changed, 2 deletions(-) diff --git a/lib/cluster.ts b/lib/cluster.ts index 97e87d3d88e..4be0e268207 100644 --- a/lib/cluster.ts +++ b/lib/cluster.ts @@ -6,7 +6,6 @@ import { RedisLuaScript, RedisLuaScripts } from './lua-script'; import { extendWithModulesAndScripts, extendWithDefaultCommands, transformCommandArguments } from './commander'; import RedisMultiCommand, { MultiQueuedCommand, RedisMultiCommandType } from './multi-command'; import { EventEmitter } from 'events'; -import cluster from 'cluster'; export interface RedisClusterOptions { rootNodes: Array; diff --git a/lib/commander.ts b/lib/commander.ts index c2b1918709a..78823516448 100644 --- a/lib/commander.ts +++ b/lib/commander.ts @@ -2,7 +2,6 @@ import COMMANDS, { RedisCommand, RedisModules, TransformArgumentsReply } from './commands'; import { RedisLuaScript, RedisLuaScripts } from './lua-script'; import { CommandOptions, isCommandOptions } from './command-options'; -import { off } from 'process'; type Instantiable = new(...args: Array) => T; From 01df65157a095c81afc02a54a5f98dbfab057737 Mon Sep 17 00:00:00 2001 From: leibale Date: Thu, 23 Sep 2021 16:36:27 -0400 Subject: [PATCH 0877/1748] Release 4.0.0-rc.2 --- package-lock.json | 4 ++-- package.json | 2 +- 2 files changed, 3 insertions(+), 3 deletions(-) diff --git a/package-lock.json b/package-lock.json index 36d4818943c..9fcd62b5996 100644 --- a/package-lock.json +++ b/package-lock.json @@ -1,12 +1,12 @@ { "name": "redis", - "version": "4.0.0-rc.1", + "version": "4.0.0-rc.2", "lockfileVersion": 2, "requires": true, "packages": { "": { "name": "redis", - "version": "4.0.0-rc.1", + "version": "4.0.0-rc.2", "license": "MIT", "dependencies": { "cluster-key-slot": "1.1.0", diff --git a/package.json b/package.json index ec77a93bb55..b2ceadbdd16 100644 --- a/package.json +++ b/package.json @@ -1,6 +1,6 @@ { "name": "redis", - "version": "4.0.0-rc.1", + "version": "4.0.0-rc.2", "description": "A high performance Redis client.", "keywords": [ "database", From e592d9403d6ac380be216a4b18a2ccd05197e50e Mon Sep 17 00:00:00 2001 From: Leibale Eidelman Date: Thu, 23 Sep 2021 16:36:40 -0400 Subject: [PATCH 0878/1748] v4.0.0-rc.2 (#1664) * 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 * fix #1652 * ref #1653 - better types * better types * fix 54124793ad1b633d39d372bdff487c9888c017a7 * 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 Co-authored-by: Richard Samuelsson --- README.md | 144 ++-- docs/FAQ.md | 2 +- docs/client-configuration.md | 40 +- lib/client.spec.ts | 102 ++- lib/client.ts | 289 ++++--- lib/cluster-slots.ts | 2 +- lib/cluster.ts | 117 ++- lib/commander.spec.ts | 22 +- lib/commander.ts | 21 +- lib/commands-queue.ts | 97 +-- lib/commands/ACL_DELUSER.ts | 3 +- lib/commands/ACL_SETUSER.ts | 3 +- lib/commands/BITOP.ts | 3 +- lib/commands/BLPOP.ts | 3 +- lib/commands/BRPOP.ts | 3 +- lib/commands/BZPOPMAX.ts | 3 +- lib/commands/BZPOPMIN.ts | 3 +- lib/commands/CLUSTER_SLOTS.spec.ts | 76 ++ lib/commands/CLUSTER_SLOTS.ts | 41 + lib/commands/DEL.ts | 3 +- lib/commands/EXISTS.ts | 3 +- lib/commands/GEOHASH.ts | 3 +- lib/commands/GEOPOS.spec.ts | 50 +- lib/commands/GEOPOS.ts | 3 +- lib/commands/GEOSEARCHSTORE.spec.ts | 9 +- lib/commands/GET.ts | 7 +- lib/commands/GETEX.ts | 3 +- lib/commands/GET_BUFFER.spec.ts | 22 + lib/commands/GET_BUFFER.ts | 7 + lib/commands/HDEL.ts | 3 +- lib/commands/HMGET.ts | 3 +- lib/commands/HSET.spec.ts | 9 +- lib/commands/HSET.ts | 13 +- lib/commands/LPUSH.ts | 3 +- lib/commands/LPUSHX.ts | 3 +- lib/commands/PFADD.ts | 3 +- lib/commands/PFCOUNT.ts | 3 +- lib/commands/PFMERGE.ts | 3 +- lib/commands/PUBSUB_NUMSUB.spec.ts | 2 +- lib/commands/RPUSH.ts | 3 +- lib/commands/RPUSHX.ts | 3 +- lib/commands/SADD.ts | 3 +- lib/commands/SCRIPT_EXISTS.ts | 3 +- lib/commands/SDIFF.ts | 3 +- lib/commands/SDIFFSTORE.ts | 3 +- lib/commands/SET.spec.ts | 2 +- lib/commands/SET.ts | 4 +- lib/commands/SETBIT.ts | 3 +- lib/commands/SETEX.ts | 3 +- lib/commands/SINTER.ts | 3 +- lib/commands/SINTERSTORE.ts | 3 +- lib/commands/SREM.ts | 3 +- lib/commands/SUNION.ts | 3 +- lib/commands/SUNIONSTORE.ts | 3 +- lib/commands/TOUCH.ts | 3 +- lib/commands/UNLINK.ts | 3 +- lib/commands/WATCH.ts | 3 +- lib/commands/XACK.ts | 3 +- lib/commands/XDEL.ts | 3 +- lib/commands/ZDIFF.ts | 3 +- lib/commands/ZDIFFSTORE.ts | 3 +- lib/commands/ZDIFF_WITHSCORES.ts | 3 +- lib/commands/ZINTER.ts | 3 +- lib/commands/ZINTERSTORE.ts | 3 +- lib/commands/ZINTER_WITHSCORES.ts | 3 +- lib/commands/ZMSCORE.ts | 3 +- lib/commands/ZREM.ts | 3 +- lib/commands/ZUNION.ts | 3 +- lib/commands/ZUNIONSTORE.ts | 3 +- lib/commands/ZUNION_WITHSCORES.ts | 3 +- lib/commands/generic-transformers.ts | 12 +- lib/commands/index.ts | 22 +- lib/lua-script.ts | 4 +- lib/multi-command.spec.ts | 23 +- lib/multi-command.ts | 103 ++- lib/socket.ts | 49 +- lib/test-utils.ts | 30 +- lib/ts-declarations/cluster-key-slot.d.ts | 2 +- lib/ts-declarations/redis-parser.d.ts | 2 + package-lock.json | 880 ++++++++++++---------- package.json | 16 +- 81 files changed, 1394 insertions(+), 971 deletions(-) create mode 100644 lib/commands/CLUSTER_SLOTS.spec.ts create mode 100644 lib/commands/CLUSTER_SLOTS.ts create mode 100644 lib/commands/GET_BUFFER.spec.ts create mode 100644 lib/commands/GET_BUFFER.ts diff --git a/README.md b/README.md index db0fa71cc7f..c5f0ea1a1c9 100644 --- a/README.md +++ b/README.md @@ -38,24 +38,22 @@ npm install redis@next import { createClient } from 'redis'; (async () => { - const client = createClient(); + const client = createClient(); - client.on('error', (err) => console.log('Redis Client Error', err)); + client.on('error', (err) => console.log('Redis Client Error', err)); - await client.connect(); + await client.connect(); - await client.set('key', 'value'); - const value = await client.get('key'); + 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]`: +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({ - socket: { - url: 'redis://alice:foobared@awesome.redis.server:6380' - } + url: 'redis://alice:foobared@awesome.redis.server:6380', }); ``` @@ -79,8 +77,8 @@ Modifiers to commands are specified using a JavaScript object: ```typescript await client.set('key', 'value', { - EX: 10, - NX: true + EX: 10, + NX: true, }); ``` @@ -108,11 +106,11 @@ Start a [transaction](https://redis.io/topics/transactions) by calling `.multi() ```typescript await client.set('another-key', 'another-value'); -const [ setKeyReply, otherKeyValue ] = await client.multi() - .set('key', 'value') - .get('another-key') - .exec() -]); // ['OK', '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. @@ -128,10 +126,7 @@ This pattern works especially well for blocking commands—such as `BLPOP` and ` ```typescript import { commandOptions } from 'redis'; -const blPopPromise = client.blPop( - commandOptions({ isolated: true }), - 'key' -); +const blPopPromise = client.blPop(commandOptions({ isolated: true }), 'key'); await client.lPush('key', ['1', '2']); @@ -153,12 +148,12 @@ 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.subscribe('channel', (message) => { + console.log(message); // 'message' }); await subscriber.pSubscribe('channe*', (message, channel) => { - console.log(message, channel); // 'message', 'channel' + console.log(message, channel); // 'message', 'channel' }); await subscriber.unsubscribe('channel'); @@ -178,26 +173,29 @@ await publisher.publish('channel', 'message'); ```typescript for await (const key of client.scanIterator()) { - // use the key! - await client.get(key); + // use the key! + await client.get(key); } ``` This works with `HSCAN`, `SSCAN`, and `ZSCAN` too: ```typescript -for await (const member of client.hScanIterator('hash')) {} -for await (const { field, value } of client.sScanIterator('set')) {} -for await (const { member, score } of client.zScanIterator('sorted-set')) {} +for await (const member of client.hScanIterator('hash')) { +} +for await (const { field, value } of client.sScanIterator('set')) { +} +for await (const { member, score } 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 + TYPE: 'string', // `SCAN` only + MATCH: 'patter*', + COUNT: 100, }); ``` @@ -209,27 +207,26 @@ Define new functions using [Lua scripts](https://redis.io/commands/eval) which e 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 { - return [key, number.toString()]; - }, - transformReply(reply: number): number { - return reply; - } - }) - } - }); - - await client.connect(); - - await client.set('key', '1'); - await client.add('key', 2); // 3 + 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 { + return [key, number.toString()]; + }, + transformReply(reply: number): number { + return reply; + }, + }), + }, + }); + + await client.connect(); + + await client.set('key', '1'); + await client.add('key', 2); // 3 })(); ``` @@ -241,22 +238,25 @@ Connecting to a cluster is a bit different. Create the client by specifying some import { createCluster } from 'redis'; (async () => { - const cluster = createCluster({ - rootNodes: [{ - host: '10.0.0.1', - port: 30001 - }, { - host: '10.0.0.2', - port: 30002 - }] - }); - - cluster.on('error', (err) => console.log('Redis Cluster Error', err)); - - await cluster.connect(); - - await cluster.set('key', 'value'); - const value = await cluster.get('key'); + const cluster = createCluster({ + rootNodes: [ + { + host: '10.0.0.1', + port: 30001, + }, + { + host: '10.0.0.2', + port: 30002, + }, + ], + }); + + cluster.on('error', (err) => console.log('Redis Cluster Error', err)); + + await cluster.connect(); + + await cluster.set('key', 'value'); + const value = await cluster.get('key'); })(); ``` @@ -273,8 +273,8 @@ Of course, if you don't do something with your Promises you're certain to get [u ```typescript await Promise.all([ - client.set('Tm9kZSBSZWRpcw==', 'users:1'), - client.sAdd('users:1:tokens', 'Tm9kZSBSZWRpcw==') + client.set('Tm9kZSBSZWRpcw==', 'users:1'), + client.sAdd('users:1:tokens', 'Tm9kZSBSZWRpcw=='), ]); ``` @@ -284,7 +284,9 @@ 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! - + + + ## License diff --git a/docs/FAQ.md b/docs/FAQ.md index b5074e73025..cfdb2ecaf42 100644 --- a/docs/FAQ.md +++ b/docs/FAQ.md @@ -8,6 +8,6 @@ When a socket closed unexpectedly, all the commands that were already sent will ## How are commands batched? -Commands are pipelined using [`queueMicrotask`](https://nodejs.org/api/globals.html#globals_queuemicrotask_callback). Commands from the same "tick" will be sent in batches and respect the [`writableHighWaterMark`](https://nodejs.org/api/stream.html#stream_new_stream_writable_options). +Commands are pipelined using [`queueMicrotask`](https://nodejs.org/api/globals.html#globals_queuemicrotask_callback). If `socket.write()` returns `false`—meaning that ["all or part of the data was queued in user memory"](https://nodejs.org/api/net.html#net_socket_write_data_encoding_callback:~:text=all%20or%20part%20of%20the%20data%20was%20queued%20in%20user%20memory)—the commands will stack in memory until the [`drain`](https://nodejs.org/api/net.html#net_event_drain) event is fired. diff --git a/docs/client-configuration.md b/docs/client-configuration.md index 4b93340ad8f..11fdb0a6819 100644 --- a/docs/client-configuration.md +++ b/docs/client-configuration.md @@ -1,24 +1,26 @@ # `createClient` configuration -| Property | Default | Description | -|--------------------------|------------------------------------------|------------------------------------------------------------------------------------------------------------------------------| -| socket | | Object defining socket connection properties | -| socket.url | | `[redis[s]:]//[[username][:password]@][host][:port]` | -| socket.host | `'localhost'` | Hostname to connect to | -| socket.port | `6379` | Port to connect to | -| socket.username | | ACL username ([see ACL guide](https://redis.io/topics/acl)) | -| socket.password | | ACL password or the old "--requirepass" password | -| socket.connectTimeout | `5000` | The timeout for connecting to the Redis Server (in milliseconds) | -| socket.noDelay | `true` | Enable/disable the use of [`Nagle's algorithm`](https://nodejs.org/api/net.html#net_socket_setnodelay_nodelay) | -| socket.keepAlive | `5000` | Enable/disable the [`keep-alive`](https://nodejs.org/api/net.html#net_socket_setkeepalive_enable_initialdelay) functionality | -| socket.tls | | Set to `true` to enable [TLS Configuration](https://nodejs.org/api/tls.html#tls_tls_connect_options_callback) | -| socket.reconnectStrategy | `retries => Math.min(retries * 50, 500)` | A function containing the [Reconnect Strategy](#reconnect-strategy) logic | -| modules | | Object defining which [Redis Modules](https://redis.io/modules) to include (TODO - document) | -| 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)) | -| isolationPoolOptions | | See the [Isolated Execution Guide](./isolated-execution.md) | +| Property | Default | Description | +|--------------------------|------------------------------------------|-----------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------| +| url | | `redis[s]://[[username][:password]@][host][:port][/db-number]` (see [`redis`](https://www.iana.org/assignments/uri-schemes/prov/redis) and [`rediss`](https://www.iana.org/assignments/uri-schemes/prov/rediss) IANA registration for more details) | +| socket | | Object defining socket connection properties | +| socket.host | `'localhost'` | Hostname to connect to | +| socket.port | `6379` | Port to connect to | +| socket.path | | UNIX Socket to connect to | +| socket.connectTimeout | `5000` | The timeout for connecting to the Redis Server (in milliseconds) | +| socket.noDelay | `true` | Enable/disable the use of [`Nagle's algorithm`](https://nodejs.org/api/net.html#net_socket_setnodelay_nodelay) | +| socket.keepAlive | `5000` | Enable/disable the [`keep-alive`](https://nodejs.org/api/net.html#net_socket_setkeepalive_enable_initialdelay) functionality | +| socket.tls | | Set to `true` to enable [TLS Configuration](https://nodejs.org/api/tls.html#tls_tls_connect_options_callback) | +| socket.reconnectStrategy | `retries => Math.min(retries * 50, 500)` | A function containing the [Reconnect Strategy](#reconnect-strategy) logic | +| 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](https://redis.io/modules) to include (TODO - document) | +| 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)) | +| isolationPoolOptions | | See the [Isolated Execution Guide](./isolated-execution.md) | ## Reconnect Strategy diff --git a/lib/client.spec.ts b/lib/client.spec.ts index f73049d2286..2cf6ea4964e 100644 --- a/lib/client.spec.ts +++ b/lib/client.spec.ts @@ -5,6 +5,7 @@ import RedisClient from './client'; import { AbortError, ClientClosedError, ConnectionTimeoutError, WatchError } from './errors'; import { defineScript } from './lua-script'; import { spy } from 'sinon'; +import { RedisNetSocketOptions } from './socket'; export const SQUARE_SCRIPT = defineScript({ NUMBER_OF_KEYS: 0, @@ -18,6 +19,81 @@ export const SQUARE_SCRIPT = defineScript({ }); describe('Client', () => { + describe('parseURL', () => { + it('redis://user:secret@localhost:6379/0', () => { + assert.deepEqual( + RedisClient.parseURL('redis://user:secret@localhost:6379/0'), + { + socket: { + host: 'localhost', + port: 6379 + }, + username: 'user', + password: 'secret', + database: 0 + } + ); + }); + + it('rediss://user:secret@localhost:6379/0', () => { + assert.deepEqual( + RedisClient.parseURL('rediss://user:secret@localhost:6379/0'), + { + socket: { + host: 'localhost', + port: 6379, + tls: true + }, + username: 'user', + password: 'secret', + database: 0 + } + ); + }); + + it('Invalid protocol', () => { + assert.throws( + () => RedisClient.parseURL('redi://user:secret@localhost:6379/0'), + TypeError + ); + }); + + it('Invalid pathname', () => { + assert.throws( + () => RedisClient.parseURL('redis://user:secret@localhost:6379/NaN'), + TypeError + ); + }); + + it('redis://localhost', () => { + assert.deepEqual( + RedisClient.parseURL('redis://localhost'), + { + socket: { + host: 'localhost', + } + } + ); + }); + + it('createClient with url', async () => { + const client = RedisClient.create({ + url: `redis://localhost:${(TEST_REDIS_SERVERS[TestRedisServers.OPEN].socket as RedisNetSocketOptions)!.port!.toString()}/1` + }); + + await client.connect(); + + try { + assert.equal( + await client.ping(), + 'PONG' + ); + } finally { + await client.disconnect(); + } + }) + }); + describe('authentication', () => { itWithClient(TestRedisServers.PASSWORD, 'Client should be authenticated', async client => { assert.equal( @@ -28,10 +104,8 @@ describe('Client', () => { it('should not retry connecting if failed due to wrong auth', async () => { const client = RedisClient.create({ - socket: { - ...TEST_REDIS_SERVERS[TestRedisServers.PASSWORD], - password: 'wrongpassword' - } + ...TEST_REDIS_SERVERS[TestRedisServers.PASSWORD], + password: 'wrongpassword' }); await assert.rejects( @@ -49,7 +123,7 @@ describe('Client', () => { describe('legacyMode', () => { const client = RedisClient.create({ - socket: TEST_REDIS_SERVERS[TestRedisServers.OPEN], + ...TEST_REDIS_SERVERS[TestRedisServers.OPEN], scripts: { square: SQUARE_SCRIPT }, @@ -173,9 +247,7 @@ describe('Client', () => { describe('events', () => { it('connect, ready, end', async () => { - const client = RedisClient.create({ - socket: TEST_REDIS_SERVERS[TestRedisServers.OPEN] - }); + const client = RedisClient.create(TEST_REDIS_SERVERS[TestRedisServers.OPEN]); await Promise.all([ client.connect(), @@ -195,6 +267,13 @@ describe('Client', () => { assert.equal(await client.sendCommand(['PING']), 'PONG'); }); + itWithClient(TestRedisServers.OPEN, 'bufferMode', async client => { + assert.deepEqual( + await client.sendCommand(['PING'], undefined, true), + Buffer.from('PONG') + ); + }); + describe('AbortController', () => { before(function () { if (!global.AbortController) { @@ -509,6 +588,9 @@ describe('Client', () => { assert.ok(channelListener1.calledOnce); assert.ok(channelListener2.calledTwice); assert.ok(patternListener.calledThrice); + + // should be able to send commands when unsubsribed from all channels (see #1652) + await assert.doesNotReject(subscriber.ping()); } finally { await subscriber.disconnect(); } @@ -540,9 +622,7 @@ describe('Client', () => { }); it('client.quit', async () => { - const client = RedisClient.create({ - socket: TEST_REDIS_SERVERS[TestRedisServers.OPEN] - }); + const client = RedisClient.create(TEST_REDIS_SERVERS[TestRedisServers.OPEN]); await client.connect(); diff --git a/lib/client.ts b/lib/client.ts index ed06317c14c..93afee1ff1a 100644 --- a/lib/client.ts +++ b/lib/client.ts @@ -1,6 +1,6 @@ -import RedisSocket, { RedisSocketOptions } from './socket'; +import RedisSocket, { RedisSocketOptions, RedisNetSocketOptions, RedisTlsSocketOptions } from './socket'; import RedisCommandsQueue, { PubSubListener, PubSubSubscribeCommands, PubSubUnsubscribeCommands, QueueCommandOptions } from './commands-queue'; -import COMMANDS from './commands'; +import COMMANDS, { TransformArgumentsReply } from './commands'; import { RedisCommand, RedisModules, RedisReply } from './commands'; import RedisMultiCommand, { MultiQueuedCommand, RedisMultiCommandType } from './multi-command'; import EventEmitter from 'events'; @@ -12,9 +12,14 @@ import { HScanTuple } from './commands/HSCAN'; import { encodeCommand, extendWithDefaultCommands, extendWithModulesAndScripts, transformCommandArguments } from './commander'; import { Pool, Options as PoolOptions, createPool } from 'generic-pool'; import { ClientClosedError } from './errors'; +import { URL } from 'url'; -export interface RedisClientOptions { +export interface RedisClientOptions { + url?: string; socket?: RedisSocketOptions; + username?: string; + password?: string; + database?: number; modules?: M; scripts?: S; commandsQueueMaxLength?: number; @@ -43,55 +48,25 @@ type WithScripts = { export type WithPlugins = WithCommands & WithModules & WithScripts; -export type RedisClientType = +export type RedisClientType = WithPlugins & RedisClient; export interface ClientCommandOptions extends QueueCommandOptions { isolated?: boolean; } -export default class RedisClient extends EventEmitter { +export default class RedisClient extends EventEmitter { static commandOptions(options: ClientCommandOptions): CommandOptions { return commandOptions(options); } - static async commandsExecutor( - this: RedisClient, - command: RedisCommand, - args: Array - ): Promise> { - const { args: redisArgs, options } = transformCommandArguments(command, args); - - const reply = command.transformReply( - await this.#sendCommand(redisArgs, options), - redisArgs.preserve - ); - - return reply; - } - - static async #scriptsExecutor( - this: RedisClient, - script: RedisLuaScript, - args: Array - ): Promise { - const { args: redisArgs, options } = transformCommandArguments(script, args); - - const reply = script.transformReply( - await this.executeScript(script, redisArgs, options), - redisArgs.preserve - ); - - return reply; - } - - static create(options?: RedisClientOptions): RedisClientType { + static create(options?: RedisClientOptions): RedisClientType { const Client = (extendWithModulesAndScripts({ BaseClass: RedisClient, modules: options?.modules, - modulesCommandsExecutor: RedisClient.commandsExecutor, + modulesCommandsExecutor: RedisClient.prototype.commandsExecutor, scripts: options?.scripts, - scriptsExecutor: RedisClient.#scriptsExecutor + scriptsExecutor: RedisClient.prototype.scriptsExecutor })); if (Client !== RedisClient) { @@ -101,6 +76,45 @@ export default class RedisClient { + // https://www.iana.org/assignments/uri-schemes/prov/redis + const { hostname, port, protocol, username, password, pathname } = new URL(url), + parsed: RedisClientOptions<{}, {}> = { + socket: { + host: hostname + } + }; + + if (protocol === 'rediss:') { + (parsed.socket as RedisTlsSocketOptions).tls = true; + } else if (protocol !== 'redis:') { + throw new TypeError('Invalid protocol'); + } + + if (port) { + (parsed.socket as RedisNetSocketOptions).port = Number(port); + } + + if (username) { + parsed.username = username; + } + + if (password) { + parsed.password = password; + } + + if (pathname.length > 1) { + const database = Number(pathname.substring(1)); + if (isNaN(database)) { + throw new TypeError('Invalid pathname'); + } + + parsed.database = database; + } + + return parsed; + } + readonly #options?: RedisClientOptions; readonly #socket: RedisSocket; readonly #queue: RedisCommandsQueue; @@ -108,7 +122,7 @@ export default class RedisClient = {}; #selectedDB = 0; - get options(): RedisClientOptions | null | undefined { + get options(): RedisClientOptions | undefined { return this.#options; } @@ -126,7 +140,7 @@ export default class RedisClient) { super(); - this.#options = options; + this.#options = this.#initiateOptions(options); this.#socket = this.#initiateSocket(); this.#queue = this.#initiateQueue(); this.#isolationPool = createPool({ @@ -140,6 +154,23 @@ export default class RedisClient): RedisClientOptions | undefined { + if (options?.url) { + const parsed = RedisClient.parseURL(options.url); + if (options.socket) { + parsed.socket = Object.assign(options.socket, parsed.socket); + } + + Object.assign(options, parsed); + } + + if (options?.database) { + this.#selectedDB = options.database; + } + + return options; + } + #initiateSocket(): RedisSocket { const socketInitiator = async (): Promise => { const v4Commands = this.#options?.legacyMode ? this.#v4 : this, @@ -153,8 +184,8 @@ export default class RedisClient this.#socket.write(encodedCommands) - ); + return new RedisCommandsQueue(this.#options?.commandsQueueMaxLength); } #legacyMode(): void { @@ -247,6 +275,72 @@ export default class RedisClient): Promise> { + const { args: redisArgs, options } = transformCommandArguments(command, args); + + return command.transformReply( + await this.#sendCommand(redisArgs, options, command.BUFFER_MODE), + redisArgs.preserve, + ); + } + + sendCommand(args: TransformArgumentsReply, options?: ClientCommandOptions, bufferMode?: boolean): Promise { + return this.#sendCommand(args, options, bufferMode); + } + + // using `#sendCommand` cause `sendCommand` is overwritten in legacy mode + async #sendCommand(args: TransformArgumentsReply, options?: ClientCommandOptions, bufferMode?: boolean): Promise { + if (!this.#socket.isOpen) { + throw new ClientClosedError(); + } + + if (options?.isolated) { + return this.executeIsolated(isolatedClient => + isolatedClient.sendCommand(args, { + ...options, + isolated: false + }) + ); + } + + const promise = this.#queue.addCommand(args, options, bufferMode); + this.#tick(); + return await promise; + } + + async scriptsExecutor(script: RedisLuaScript, args: Array): Promise> { + const { args: redisArgs, options } = transformCommandArguments(script, args); + + return script.transformReply( + await this.executeScript(script, redisArgs, options, script.BUFFER_MODE), + redisArgs.preserve + ); + } + + async executeScript(script: RedisLuaScript, args: TransformArgumentsReply, options?: ClientCommandOptions, bufferMode?: boolean): Promise> { + try { + return await this.#sendCommand([ + 'EVALSHA', + script.SHA1, + script.NUMBER_OF_KEYS.toString(), + ...args + ], options, bufferMode); + } catch (err: any) { + if (!err?.message?.startsWith?.('NOSCRIPT')) { + throw err; + } + + return await this.#sendCommand([ + 'EVAL', + script.SCRIPT, + script.NUMBER_OF_KEYS.toString(), + ...args + ], options, bufferMode); + } + } + + + async SELECT(db: number): Promise; async SELECT(options: CommandOptions, db: number): Promise; async SELECT(options?: any, db?: any): Promise { @@ -299,7 +393,7 @@ export default class RedisClient { return this.#socket.quit(() => { - const promise = this.#queue.addEncodedCommand(encodeCommand(['QUIT'])); + const promise = this.#queue.addCommand(['QUIT']); this.#tick(); return promise; }); @@ -307,64 +401,43 @@ export default class RedisClient(args: Array, options?: ClientCommandOptions): Promise { - return this.#sendCommand(args, options); - } + #tick(): void { + if (!this.#socket.isSocketExists) { + return; + } - // using `#sendCommand` cause `sendCommand` is overwritten in legacy mode - #sendCommand(args: Array, options?: ClientCommandOptions): Promise { - return this.sendEncodedCommand(encodeCommand(args), options); - } + this.#socket.cork(); - async sendEncodedCommand(encodedCommand: string, options?: ClientCommandOptions): Promise { - if (!this.#socket.isOpen) { - throw new ClientClosedError(); - } + while (true) { + const args = this.#queue.getCommandToSend(); + if (args === undefined) break; - if (options?.isolated) { - return this.executeIsolated(isolatedClient => - isolatedClient.sendEncodedCommand(encodedCommand, { - ...options, - isolated: false - }) - ); - } + let writeResult; + for (const toWrite of encodeCommand(args)) { + writeResult = this.#socket.write(toWrite); + } - const promise = this.#queue.addEncodedCommand(encodedCommand, options); - this.#tick(); - return await promise; + if (!writeResult) { + break; + } + } } executeIsolated(fn: (client: RedisClientType) => T | Promise): Promise { return this.#isolationPool.use(fn); } - async executeScript(script: RedisLuaScript, args: Array, options?: ClientCommandOptions): Promise> { - try { - return await this.#sendCommand([ - 'EVALSHA', - script.SHA1, - script.NUMBER_OF_KEYS.toString(), - ...args - ], options); - } catch (err: any) { - if (!err?.message?.startsWith?.('NOSCRIPT')) { - throw err; - } - - return await this.#sendCommand([ - 'EVAL', - script.SCRIPT, - script.NUMBER_OF_KEYS.toString(), - ...args - ], options); - } + multi(): RedisMultiCommandType { + return new (this as any).Multi( + this.#multiExecutor.bind(this), + this.#options + ); } #multiExecutor(commands: Array, chainId?: symbol): Promise> { const promise = Promise.all( - commands.map(({encodedCommand}) => { - return this.#queue.addEncodedCommand(encodedCommand, RedisClient.commandOptions({ + commands.map(({ args }) => { + return this.#queue.addCommand(args, RedisClient.commandOptions({ chainId })); }) @@ -375,13 +448,6 @@ export default class RedisClient { - return new (this as any).Multi( - this.#multiExecutor.bind(this), - this.#options - ); - } - async* scanIterator(options?: ScanCommandOptions): AsyncIterable { let cursor = 0; do { @@ -438,32 +504,7 @@ export default class RedisClient this.#tick()); - this.#isTickQueued = true; - return; - } - - const isBuffering = this.#queue.executeChunk(chunkRecommendedSize); - if (isBuffering === true) { - this.#socket.once('drain', () => this.#tick()); - } else if (isBuffering === false) { - this.#tick(); - return; - } - - this.#isTickQueued = false; - } } -extendWithDefaultCommands(RedisClient, RedisClient.commandsExecutor); +extendWithDefaultCommands(RedisClient, RedisClient.prototype.commandsExecutor); (RedisClient.prototype as any).Multi = RedisMultiCommand.extend(); diff --git a/lib/cluster-slots.ts b/lib/cluster-slots.ts index 5fae5b92342..a5155cc53db 100644 --- a/lib/cluster-slots.ts +++ b/lib/cluster-slots.ts @@ -172,7 +172,7 @@ export default class RedisClusterSlots { + getClient(firstKey?: string | Buffer, isReadonly?: boolean): RedisClientType { if (!firstKey) { return this.#getRandomClient(); } diff --git a/lib/cluster.ts b/lib/cluster.ts index 2c1b23465ee..4be0e268207 100644 --- a/lib/cluster.ts +++ b/lib/cluster.ts @@ -1,4 +1,4 @@ -import { RedisCommand, RedisModules } from './commands'; +import { RedisCommand, RedisModules, TransformArgumentsReply } from './commands'; import RedisClient, { ClientCommandOptions, RedisClientType, WithPlugins } from './client'; import { RedisSocketOptions } from './socket'; import RedisClusterSlots, { ClusterNode } from './cluster-slots'; @@ -15,11 +15,11 @@ export interface RedisClusterOptions { maxCommandRedirections?: number; } -export type RedisClusterType = - WithPlugins & RedisCluster; +export type RedisClusterType = + WithPlugins & RedisCluster; -export default class RedisCluster extends EventEmitter { - static #extractFirstKey(command: RedisCommand, originalArgs: Array, redisArgs: Array): string | undefined { +export default class RedisCluster extends EventEmitter { + static #extractFirstKey(command: RedisCommand, originalArgs: Array, redisArgs: TransformArgumentsReply): string | Buffer | undefined { if (command.FIRST_KEY_INDEX === undefined) { return undefined; } else if (typeof command.FIRST_KEY_INDEX === 'number') { @@ -29,53 +29,13 @@ export default class RedisCluster - ): Promise> { - const { args: redisArgs, options } = transformCommandArguments(command, args); - - const reply = command.transformReply( - await this.sendCommand( - RedisCluster.#extractFirstKey(command, args, redisArgs), - command.IS_READ_ONLY, - redisArgs, - options - ), - redisArgs.preserve - ); - - return reply; - } - - static async #scriptsExecutor( - this: RedisCluster, - script: RedisLuaScript, - args: Array - ): Promise { - const { args: redisArgs, options } = transformCommandArguments(script, args); - - const reply = script.transformReply( - await this.executeScript( - script, - args, - redisArgs, - options - ), - redisArgs.preserve - ); - - return reply; - } - - static create(options?: RedisClusterOptions): RedisClusterType { + static create(options?: RedisClusterOptions): RedisClusterType { return new (extendWithModulesAndScripts({ BaseClass: RedisCluster, modules: options?.modules, - modulesCommandsExecutor: RedisCluster.commandsExecutor, + modulesCommandsExecutor: RedisCluster.prototype.commandsExecutor, scripts: options?.scripts, - scriptsExecutor: RedisCluster.#scriptsExecutor + scriptsExecutor: RedisCluster.prototype.scriptsExecutor }))(options); } @@ -91,37 +51,75 @@ export default class RedisCluster { + return new (Object.getPrototypeOf(this).constructor)(this.#options); + } + async connect(): Promise { return this.#slots.connect(); } + async commandsExecutor(command: RedisCommand, args: Array): Promise> { + const { args: redisArgs, options } = transformCommandArguments(command, args); + + const reply = command.transformReply( + await this.sendCommand( + RedisCluster.#extractFirstKey(command, args, redisArgs), + command.IS_READ_ONLY, + redisArgs, + options, + command.BUFFER_MODE + ), + redisArgs.preserve + ); + + return reply; + } + async sendCommand( - firstKey: string | undefined, + firstKey: string | Buffer | undefined, isReadonly: boolean | undefined, - args: Array, + args: TransformArgumentsReply, options?: ClientCommandOptions, + bufferMode?: boolean, redirections = 0 ): Promise> { const client = this.#slots.getClient(firstKey, isReadonly); try { - return await client.sendCommand(args, options); + return await client.sendCommand(args, options, bufferMode); } catch (err: any) { const shouldRetry = await this.#handleCommandError(err, client, redirections); if (shouldRetry === true) { - return this.sendCommand(firstKey, isReadonly, args, options, redirections + 1); + return this.sendCommand(firstKey, isReadonly, args, options, bufferMode, redirections + 1); } else if (shouldRetry) { - return shouldRetry.sendCommand(args, options); + return shouldRetry.sendCommand(args, options, bufferMode); } throw err; } } + async scriptsExecutor(script: RedisLuaScript, args: Array): Promise> { + const { args: redisArgs, options } = transformCommandArguments(script, args); + + const reply = script.transformReply( + await this.executeScript( + script, + args, + redisArgs, + options + ), + redisArgs.preserve + ); + + return reply; + } + async executeScript( script: RedisLuaScript, originalArgs: Array, - redisArgs: Array, + redisArgs: TransformArgumentsReply, options?: ClientCommandOptions, redirections = 0 ): Promise> { @@ -131,13 +129,13 @@ export default class RedisCluster { - return client.sendEncodedCommand(encodedCommand, RedisClient.commandOptions({ + commands.map(({ args }) => { + return client.sendCommand(args, RedisClient.commandOptions({ chainId })); }) @@ -201,5 +199,4 @@ export default class RedisCluster): string { + const arr = []; + for (const item of encodeCommand(...args)) { + arr.push(item.toString()); + } + + return arr.join(''); +} + describe('Commander', () => { describe('encodeCommand (see #1628)', () => { it('1 byte', () => { assert.equal( - encodeCommand(['a', 'z']), + encodeCommandToString(['a', 'z']), '*2\r\n$1\r\na\r\n$1\r\nz\r\n' ); }); it('2 bytes', () => { assert.equal( - encodeCommand(['א', 'ת']), + encodeCommandToString(['א', 'ת']), '*2\r\n$2\r\nא\r\n$2\r\nת\r\n' ); }); it('4 bytes', () => { assert.equal( - encodeCommand(['🐣', '🐤']), + encodeCommandToString(['🐣', '🐤']), '*2\r\n$4\r\n🐣\r\n$4\r\n🐤\r\n' ); }); + + it('with a buffer', () => { + assert.equal( + encodeCommandToString([Buffer.from('string')]), + '*1\r\n$6\r\nstring\r\n' + ); + }); }); }); diff --git a/lib/commander.ts b/lib/commander.ts index e8ff91cc7bf..78823516448 100644 --- a/lib/commander.ts +++ b/lib/commander.ts @@ -94,16 +94,15 @@ export function transformCommandArguments( }; } -export function encodeCommand(args: Array): string { - const encoded = [ - `*${args.length}`, - `$${Buffer.byteLength(args[0]).toString()}`, - args[0] - ]; - - for (let i = 1; i < args.length; i++) { - encoded.push(`$${Buffer.byteLength(args[i]).toString()}`, args[i]); - } +const DELIMITER = '\r\n'; + +export function* encodeCommand(args: TransformArgumentsReply): IterableIterator { + yield `*${args.length}${DELIMITER}`; - return encoded.join('\r\n') + '\r\n'; + for (const arg of args) { + const byteLength = typeof arg === 'string' ? Buffer.byteLength(arg): arg.length; + yield `$${byteLength.toString()}${DELIMITER}`; + yield arg; + yield DELIMITER; + } } diff --git a/lib/commands-queue.ts b/lib/commands-queue.ts index cae3fd6130e..ef87184193f 100644 --- a/lib/commands-queue.ts +++ b/lib/commands-queue.ts @@ -2,17 +2,15 @@ import LinkedList from 'yallist'; import RedisParser from 'redis-parser'; import { AbortError } from './errors'; import { RedisReply } from './commands'; -import { encodeCommand } from './commander'; export interface QueueCommandOptions { asap?: boolean; - signal?: any; // TODO: `AbortSignal` type is incorrect chainId?: symbol; + signal?: any; // TODO: `AbortSignal` type is incorrect } interface CommandWaitingToBeSent extends CommandWaitingForReply { - encodedCommand: string; - byteLength: number; + args: Array; chainId?: symbol; abort?: { signal: any; // TODO: `AbortSignal` type is incorrect @@ -24,10 +22,9 @@ interface CommandWaitingForReply { resolve(reply?: any): void; reject(err: Error): void; channelsCounter?: number; + bufferMode?: boolean; } -export type CommandsQueueExecutor = (encodedCommands: string) => boolean | undefined; - export enum PubSubSubscribeCommands { SUBSCRIBE = 'SUBSCRIBE', PSUBSCRIBE = 'PSUBSCRIBE' @@ -57,16 +54,8 @@ export default class RedisCommandsQueue { readonly #maxLength: number | null | undefined; - readonly #executor: CommandsQueueExecutor; - readonly #waitingToBeSent = new LinkedList(); - #waitingToBeSentCommandsLength = 0; - - get waitingToBeSentCommandsLength() { - return this.#waitingToBeSentCommandsLength; - } - readonly #waitingForReply = new LinkedList(); readonly #pubSubState = { @@ -114,12 +103,11 @@ export default class RedisCommandsQueue { #chainInExecution: symbol | undefined; - constructor(maxLength: number | null | undefined, executor: CommandsQueueExecutor) { + constructor(maxLength: number | null | undefined) { this.#maxLength = maxLength; - this.#executor = executor; } - addEncodedCommand(encodedCommand: string, options?: QueueCommandOptions): Promise { + addCommand(args: Array, options?: QueueCommandOptions, bufferMode?: boolean): Promise { if (this.#pubSubState.subscribing || this.#pubSubState.subscribed) { return Promise.reject(new Error('Cannot send commands in PubSub mode')); } else if (this.#maxLength && this.#waitingToBeSent.length + this.#waitingForReply.length >= this.#maxLength) { @@ -130,11 +118,11 @@ export default class RedisCommandsQueue { return new Promise((resolve, reject) => { const node = new LinkedList.Node({ - encodedCommand, - byteLength: Buffer.byteLength(encodedCommand), + args, chainId: options?.chainId, + bufferMode, resolve, - reject + reject, }); if (options?.signal) { @@ -157,8 +145,6 @@ export default class RedisCommandsQueue { } else { this.#waitingToBeSent.pushNode(node); } - - this.#waitingToBeSentCommandsLength += node.value.byteLength; }); } @@ -185,8 +171,9 @@ export default class RedisCommandsQueue { unsubscribe(command: PubSubUnsubscribeCommands, channels?: string | Array, listener?: PubSubListener): Promise { const listeners = command === PubSubUnsubscribeCommands.UNSUBSCRIBE ? this.#pubSubListeners.channels : this.#pubSubListeners.patterns; if (!channels) { + const size = listeners.size; listeners.clear(); - return this.#pushPubSubCommand(command); + return this.#pushPubSubCommand(command, size); } const channelsToUnsubscribe = []; @@ -213,31 +200,24 @@ export default class RedisCommandsQueue { return this.#pushPubSubCommand(command, channelsToUnsubscribe); } - #pushPubSubCommand(command: PubSubSubscribeCommands | PubSubUnsubscribeCommands, channels?: Array): Promise { + #pushPubSubCommand(command: PubSubSubscribeCommands | PubSubUnsubscribeCommands, channels: number | Array): Promise { return new Promise((resolve, reject) => { const isSubscribe = command === PubSubSubscribeCommands.SUBSCRIBE || command === PubSubSubscribeCommands.PSUBSCRIBE, inProgressKey = isSubscribe ? 'subscribing' : 'unsubscribing', commandArgs: Array = [command]; + let channelsCounter: number; - if (channels?.length) { + if (typeof channels === 'number') { // unsubscribe only + channelsCounter = channels; + } else { commandArgs.push(...channels); channelsCounter = channels.length; - } else { - // unsubscribe only - channelsCounter = ( - command[0] === 'P' ? - this.#pubSubListeners.patterns : - this.#pubSubListeners.channels - ).size; } this.#pubSubState[inProgressKey] += channelsCounter; - const encodedCommand = encodeCommand(commandArgs), - byteLength = Buffer.byteLength(encodedCommand); this.#waitingToBeSent.push({ - encodedCommand, - byteLength, + args: commandArgs, channelsCounter, resolve: () => { this.#pubSubState[inProgressKey] -= channelsCounter; @@ -249,7 +229,6 @@ export default class RedisCommandsQueue { reject(); } }); - this.#waitingToBeSentCommandsLength += byteLength; }); } @@ -267,47 +246,25 @@ export default class RedisCommandsQueue { ]); } - executeChunk(recommendedSize: number): boolean | undefined { - if (!this.#waitingToBeSent.length) return; - - const encoded: Array = []; - let size = 0, - lastCommandChainId: symbol | undefined; - for (const command of this.#waitingToBeSent) { - encoded.push(command.encodedCommand); - size += command.byteLength; - if (size > recommendedSize) { - lastCommandChainId = command.chainId; - break; - } - } - - if (!lastCommandChainId && encoded.length === this.#waitingToBeSent.length) { - lastCommandChainId = this.#waitingToBeSent.tail!.value.chainId; - } - - lastCommandChainId ??= this.#waitingToBeSent.tail?.value.chainId; - - this.#executor(encoded.join('')); - - for (let i = 0; i < encoded.length; i++) { - const waitingToBeSent = this.#waitingToBeSent.shift()!; - if (waitingToBeSent.abort) { - waitingToBeSent.abort.signal.removeEventListener('abort', waitingToBeSent.abort.listener); - } + getCommandToSend(): Array | undefined { + const toSend = this.#waitingToBeSent.shift(); + if (toSend) { this.#waitingForReply.push({ - resolve: waitingToBeSent.resolve, - reject: waitingToBeSent.reject, - channelsCounter: waitingToBeSent.channelsCounter + resolve: toSend.resolve, + reject: toSend.reject, + channelsCounter: toSend.channelsCounter, + bufferMode: toSend.bufferMode }); } - this.#chainInExecution = lastCommandChainId; - this.#waitingToBeSentCommandsLength -= size; + this.#chainInExecution = toSend?.chainId; + + return toSend?.args; } parseResponse(data: Buffer): void { + this.#parser.setReturnBuffers(!!this.#waitingForReply.head?.value.bufferMode); this.#parser.execute(data); } diff --git a/lib/commands/ACL_DELUSER.ts b/lib/commands/ACL_DELUSER.ts index 7fb4904be41..85a916c4379 100644 --- a/lib/commands/ACL_DELUSER.ts +++ b/lib/commands/ACL_DELUSER.ts @@ -1,6 +1,7 @@ +import { TransformArgumentsReply } from '.'; import { pushVerdictArguments, transformReplyNumber } from './generic-transformers'; -export function transformArguments(username: string | Array): Array { +export function transformArguments(username: string | Array): TransformArgumentsReply { return pushVerdictArguments(['ACL', 'DELUSER'], username); } diff --git a/lib/commands/ACL_SETUSER.ts b/lib/commands/ACL_SETUSER.ts index b2829ca964f..e55a8942e02 100644 --- a/lib/commands/ACL_SETUSER.ts +++ b/lib/commands/ACL_SETUSER.ts @@ -1,6 +1,7 @@ +import { TransformArgumentsReply } from '.'; import { pushVerdictArguments, transformReplyString } from './generic-transformers'; -export function transformArguments(username: string, rule: string | Array): Array { +export function transformArguments(username: string, rule: string | Array): TransformArgumentsReply { return pushVerdictArguments(['ACL', 'SETUSER', username], rule); } diff --git a/lib/commands/BITOP.ts b/lib/commands/BITOP.ts index fe7d339f5d1..bb965da6dfa 100644 --- a/lib/commands/BITOP.ts +++ b/lib/commands/BITOP.ts @@ -1,10 +1,11 @@ +import { TransformArgumentsReply } from '.'; import { pushVerdictArguments, transformReplyNumber } from './generic-transformers'; export const FIRST_KEY_INDEX = 2; type BitOperations = 'AND' | 'OR' | 'XOR' | 'NOT'; -export function transformArguments(operation: BitOperations, destKey: string, key: string | Array): Array { +export function transformArguments(operation: BitOperations, destKey: string, key: string | Array): TransformArgumentsReply { return pushVerdictArguments(['BITOP', operation, destKey], key); } diff --git a/lib/commands/BLPOP.ts b/lib/commands/BLPOP.ts index 7c352951fb3..1061f5e113a 100644 --- a/lib/commands/BLPOP.ts +++ b/lib/commands/BLPOP.ts @@ -1,8 +1,9 @@ +import { TransformArgumentsReply } from '.'; import { pushVerdictArguments } from './generic-transformers'; export const FIRST_KEY_INDEX = 1; -export function transformArguments(keys: string | Array, timeout: number): Array { +export function transformArguments(keys: string | Buffer | Array, timeout: number): TransformArgumentsReply { const args = pushVerdictArguments(['BLPOP'], keys); args.push(timeout.toString()); diff --git a/lib/commands/BRPOP.ts b/lib/commands/BRPOP.ts index a03c278309a..93ded4dbf1a 100644 --- a/lib/commands/BRPOP.ts +++ b/lib/commands/BRPOP.ts @@ -1,8 +1,9 @@ +import { TransformArgumentsReply } from '.'; import { pushVerdictArguments } from './generic-transformers'; export const FIRST_KEY_INDEX = 1; -export function transformArguments(key: string | Array, timeout: number): Array { +export function transformArguments(key: string | Array, timeout: number): TransformArgumentsReply { const args = pushVerdictArguments(['BRPOP'], key); args.push(timeout.toString()); diff --git a/lib/commands/BZPOPMAX.ts b/lib/commands/BZPOPMAX.ts index ccd84272a50..3db9ca42cbb 100644 --- a/lib/commands/BZPOPMAX.ts +++ b/lib/commands/BZPOPMAX.ts @@ -1,8 +1,9 @@ +import { TransformArgumentsReply } from '.'; import { pushVerdictArguments, transformReplyNumberInfinity, ZMember } from './generic-transformers'; export const FIRST_KEY_INDEX = 1; -export function transformArguments(key: string | Array, timeout: number): Array { +export function transformArguments(key: string | Array, timeout: number): TransformArgumentsReply { const args = pushVerdictArguments(['BZPOPMAX'], key); args.push(timeout.toString()); diff --git a/lib/commands/BZPOPMIN.ts b/lib/commands/BZPOPMIN.ts index 0c299cdb9df..9106ae770da 100644 --- a/lib/commands/BZPOPMIN.ts +++ b/lib/commands/BZPOPMIN.ts @@ -1,8 +1,9 @@ +import { TransformArgumentsReply } from '.'; import { pushVerdictArguments, transformReplyNumberInfinity, ZMember } from './generic-transformers'; export const FIRST_KEY_INDEX = 1; -export function transformArguments(key: string | Array, timeout: number): Array { +export function transformArguments(key: string | Array, timeout: number): TransformArgumentsReply { const args = pushVerdictArguments(['BZPOPMIN'], key); args.push(timeout.toString()); diff --git a/lib/commands/CLUSTER_SLOTS.spec.ts b/lib/commands/CLUSTER_SLOTS.spec.ts new file mode 100644 index 00000000000..ec6773bcdd4 --- /dev/null +++ b/lib/commands/CLUSTER_SLOTS.spec.ts @@ -0,0 +1,76 @@ +import { strict as assert } from 'assert'; +import { transformArguments, transformReply } from './CLUSTER_SLOTS'; + +describe('CLUSTER SLOTS', () => { + it('transformArguments', () => { + assert.deepEqual( + transformArguments(), + ['CLUSTER', 'SLOTS'] + ); + }); + + it('transformReply', () => { + assert.deepEqual( + transformReply([ + [ + 0, + 5460, + ['127.0.0.1', 30001, '09dbe9720cda62f7865eabc5fd8857c5d2678366'], + ['127.0.0.1', 30004, '821d8ca00d7ccf931ed3ffc7e3db0599d2271abf'] + ], + [ + 5461, + 10922, + ['127.0.0.1', 30002, 'c9d93d9f2c0c524ff34cc11838c2003d8c29e013'], + ['127.0.0.1', 30005, 'faadb3eb99009de4ab72ad6b6ed87634c7ee410f'] + ], + [ + 10923, + 16383, + ['127.0.0.1', 30003, '044ec91f325b7595e76dbcb18cc688b6a5b434a1'], + ['127.0.0.1', 30006, '58e6e48d41228013e5d9c1c37c5060693925e97e'] + ] + ]), + [{ + from: 0, + to: 5460, + master: { + ip: '127.0.0.1', + port: 30001, + id: '09dbe9720cda62f7865eabc5fd8857c5d2678366' + }, + replicas: [{ + ip: '127.0.0.1', + port: 30004, + id: '821d8ca00d7ccf931ed3ffc7e3db0599d2271abf' + }] + }, { + from: 5461, + to: 10922, + master: { + ip: '127.0.0.1', + port: 30002, + id: 'c9d93d9f2c0c524ff34cc11838c2003d8c29e013' + }, + replicas: [{ + ip: '127.0.0.1', + port: 30005, + id: 'faadb3eb99009de4ab72ad6b6ed87634c7ee410f' + }] + }, { + from: 10923, + to: 16383, + master: { + ip: '127.0.0.1', + port: 30003, + id: '044ec91f325b7595e76dbcb18cc688b6a5b434a1' + }, + replicas: [{ + ip: '127.0.0.1', + port: 30006, + id: '58e6e48d41228013e5d9c1c37c5060693925e97e' + }] + }] + ) + }); +}); diff --git a/lib/commands/CLUSTER_SLOTS.ts b/lib/commands/CLUSTER_SLOTS.ts new file mode 100644 index 00000000000..b4672e731ac --- /dev/null +++ b/lib/commands/CLUSTER_SLOTS.ts @@ -0,0 +1,41 @@ +import { TransformArgumentsReply } from '.'; + +export function transformArguments(): TransformArgumentsReply { + return ['CLUSTER', 'SLOTS']; +} + +type ClusterSlotsRawNode = [ip: string, port: number, id: string]; + +type ClusterSlotsRawReply = Array<[from: number, to: number, master: ClusterSlotsRawNode, ...replicas: Array]>; + +type ClusterSlotsNode = { + ip: string; + port: number; + id: string; +}; + +export type ClusterSlotsReply = Array<{ + from: number; + to: number; + master: ClusterSlotsNode; + replicas: Array; +}>; + +export function transformReply(reply: ClusterSlotsRawReply): ClusterSlotsReply { + return reply.map(([from, to, master, ...replicas]) => { + return { + from, + to, + master: transformNode(master), + replicas: replicas.map(transformNode) + }; + }); +} + +function transformNode([ip, port, id]: ClusterSlotsRawNode): ClusterSlotsNode { + return { + ip, + port, + id + }; +} diff --git a/lib/commands/DEL.ts b/lib/commands/DEL.ts index 3d9a78212f8..f96b6988f1c 100644 --- a/lib/commands/DEL.ts +++ b/lib/commands/DEL.ts @@ -1,6 +1,7 @@ +import { TransformArgumentsReply } from '.'; import { pushVerdictArguments, transformReplyNumber } from './generic-transformers'; -export function transformArguments(keys: string | Array): Array { +export function transformArguments(keys: string | Array): TransformArgumentsReply { return pushVerdictArguments(['DEL'], keys); } diff --git a/lib/commands/EXISTS.ts b/lib/commands/EXISTS.ts index 5a76ca833fb..00d10b9eebc 100644 --- a/lib/commands/EXISTS.ts +++ b/lib/commands/EXISTS.ts @@ -1,10 +1,11 @@ +import { TransformArgumentsReply } from '.'; import { pushVerdictArguments, transformReplyBoolean } from './generic-transformers'; export const FIRST_KEY_INDEX = 1; export const IS_READ_ONLY = true; -export function transformArguments(keys: string | Array): Array { +export function transformArguments(keys: string | Array): TransformArgumentsReply { return pushVerdictArguments(['EXISTS'], keys); } diff --git a/lib/commands/GEOHASH.ts b/lib/commands/GEOHASH.ts index a46738955d3..a95ae443408 100644 --- a/lib/commands/GEOHASH.ts +++ b/lib/commands/GEOHASH.ts @@ -1,10 +1,11 @@ +import { TransformArgumentsReply } from '.'; import { pushVerdictArguments, transformReplyStringArray } from './generic-transformers'; export const FIRST_KEY_INDEX = 1; export const IS_READ_ONLY = true; -export function transformArguments(key: string, member: string | Array): Array { +export function transformArguments(key: string, member: string | Array): TransformArgumentsReply { return pushVerdictArguments(['GEOHASH', key], member); } diff --git a/lib/commands/GEOPOS.spec.ts b/lib/commands/GEOPOS.spec.ts index 98cfa6aa2d3..e15abeff516 100644 --- a/lib/commands/GEOPOS.spec.ts +++ b/lib/commands/GEOPOS.spec.ts @@ -1,6 +1,6 @@ import { strict as assert } from 'assert'; import { TestRedisServers, itWithClient, TestRedisClusters, itWithCluster } from '../test-utils'; -import { transformArguments } from './GEOPOS'; +import { transformArguments, transformReply } from './GEOPOS'; describe('GEOPOS', () => { describe('transformArguments', () => { @@ -19,11 +19,49 @@ describe('GEOPOS', () => { }); }); - itWithClient(TestRedisServers.OPEN, 'client.geoPos', async client => { - assert.deepEqual( - await client.geoPos('key', 'member'), - [null] - ); + describe('transformReply', () => { + it('null', () => { + assert.deepEqual( + transformReply([null]), + [null] + ); + }); + + it('with member', () => { + assert.deepEqual( + transformReply([['1', '2']]), + [{ + longitude: '1', + latitude: '2' + }] + ); + }); + }); + + describe('client.geoPos', () => { + itWithClient(TestRedisServers.OPEN, 'null', async client => { + assert.deepEqual( + await client.geoPos('key', 'member'), + [null] + ); + }); + + itWithClient(TestRedisServers.OPEN, 'with member', async client => { + const coordinates = { + longitude: '-122.06429868936538696', + latitude: '37.37749628831998194' + }; + + await client.geoAdd('key', { + member: 'member', + ...coordinates + }); + + assert.deepEqual( + await client.geoPos('key', 'member'), + [coordinates] + ); + }); }); itWithCluster(TestRedisClusters.OPEN, 'cluster.geoPos', async cluster => { diff --git a/lib/commands/GEOPOS.ts b/lib/commands/GEOPOS.ts index 46b0a153ba9..893048cf6da 100644 --- a/lib/commands/GEOPOS.ts +++ b/lib/commands/GEOPOS.ts @@ -1,10 +1,11 @@ +import { TransformArgumentsReply } from '.'; import { pushVerdictArguments } from './generic-transformers'; export const FIRST_KEY_INDEX = 1; export const IS_READ_ONLY = true; -export function transformArguments(key: string, member: string | Array): Array { +export function transformArguments(key: string, member: string | Array): TransformArgumentsReply { return pushVerdictArguments(['GEOPOS', key], member); } diff --git a/lib/commands/GEOSEARCHSTORE.spec.ts b/lib/commands/GEOSEARCHSTORE.spec.ts index 1983537077c..ad33c62b78c 100644 --- a/lib/commands/GEOSEARCHSTORE.spec.ts +++ b/lib/commands/GEOSEARCHSTORE.spec.ts @@ -1,6 +1,6 @@ import { strict as assert } from 'assert'; import { TestRedisServers, itWithClient, TestRedisClusters, itWithCluster, describeHandleMinimumRedisVersion } from '../test-utils'; -import { transformArguments } from './GEOSEARCHSTORE'; +import { transformArguments, transformReply } from './GEOSEARCHSTORE'; describe('GEOSEARCHSTORE', () => { describeHandleMinimumRedisVersion([6, 2]); @@ -40,6 +40,13 @@ describe('GEOSEARCHSTORE', () => { }); }); + it('transformReply with empty array (https://github.com/redis/redis/issues/9261)', () => { + assert.throws( + () => (transformReply as any)([]), + TypeError + ); + }); + itWithClient(TestRedisServers.OPEN, 'client.geoSearchStore', async client => { await client.geoAdd('source', { longitude: 1, diff --git a/lib/commands/GET.ts b/lib/commands/GET.ts index 714ad953d8e..541790e54e4 100644 --- a/lib/commands/GET.ts +++ b/lib/commands/GET.ts @@ -1,11 +1,12 @@ -import { transformReplyString } from './generic-transformers'; +import { TransformArgumentsReply } from '.'; +import { transformReplyStringNull } from './generic-transformers'; export const FIRST_KEY_INDEX = 1; export const IS_READ_ONLY = true; -export function transformArguments(key: string): Array { +export function transformArguments(key: string | Buffer): TransformArgumentsReply { return ['GET', key]; } -export const transformReply = transformReplyString; +export const transformReply = transformReplyStringNull; diff --git a/lib/commands/GETEX.ts b/lib/commands/GETEX.ts index ca1465b7ee5..214dae5c7ab 100644 --- a/lib/commands/GETEX.ts +++ b/lib/commands/GETEX.ts @@ -1,3 +1,4 @@ +import { TransformArgumentsReply } from '.'; import { transformEXAT, transformPXAT, transformReplyStringNull } from './generic-transformers'; export const FIRST_KEY_INDEX = 1; @@ -14,7 +15,7 @@ type GetExModes = { PERSIST: true; }; -export function transformArguments(key: string, mode: GetExModes) { +export function transformArguments(key: string, mode: GetExModes): TransformArgumentsReply { const args = ['GETEX', key]; if ('EX' in mode) { diff --git a/lib/commands/GET_BUFFER.spec.ts b/lib/commands/GET_BUFFER.spec.ts new file mode 100644 index 00000000000..533eb808c49 --- /dev/null +++ b/lib/commands/GET_BUFFER.spec.ts @@ -0,0 +1,22 @@ +import { strict as assert } from 'assert'; +import { TestRedisServers, itWithClient, TestRedisClusters, itWithCluster } from '../test-utils'; + +describe('GET_BUFFER', () => { + itWithClient(TestRedisServers.OPEN, 'client.getBuffer', async client => { + const buffer = Buffer.from('string'); + await client.set('key', buffer); + assert.deepEqual( + buffer, + await client.getBuffer('key') + ); + }); + + itWithCluster(TestRedisClusters.OPEN, 'cluster.getBuffer', async cluster => { + const buffer = Buffer.from('string'); + await cluster.set('key', buffer); + assert.deepEqual( + buffer, + await cluster.getBuffer('key') + ); + }); +}); diff --git a/lib/commands/GET_BUFFER.ts b/lib/commands/GET_BUFFER.ts new file mode 100644 index 00000000000..9d281961130 --- /dev/null +++ b/lib/commands/GET_BUFFER.ts @@ -0,0 +1,7 @@ +import { transformReplyBufferNull } from './generic-transformers'; + +export { FIRST_KEY_INDEX, IS_READ_ONLY, transformArguments } from './GET'; + +export const BUFFER_MODE = true; + +export const transformReply = transformReplyBufferNull; diff --git a/lib/commands/HDEL.ts b/lib/commands/HDEL.ts index ee961931449..4785b0e67f9 100644 --- a/lib/commands/HDEL.ts +++ b/lib/commands/HDEL.ts @@ -1,8 +1,9 @@ +import { TransformArgumentsReply } from '.'; import { pushVerdictArguments, transformReplyNumber } from './generic-transformers'; export const FIRST_KEY_INDEX = 1; -export function transformArguments(key: string, field: string | Array): Array { +export function transformArguments(key: string, field: string | Array): TransformArgumentsReply { return pushVerdictArguments(['HDEL', key], field); } diff --git a/lib/commands/HMGET.ts b/lib/commands/HMGET.ts index fc0f91d8224..9f26eeba640 100644 --- a/lib/commands/HMGET.ts +++ b/lib/commands/HMGET.ts @@ -1,10 +1,11 @@ +import { TransformArgumentsReply } from '.'; import { pushVerdictArguments, transformReplyStringArray } from './generic-transformers'; export const FIRST_KEY_INDEX = 1; export const IS_READ_ONLY = true; -export function transformArguments(key: string, fields: string | Array): Array { +export function transformArguments(key: string, fields: string | Array): TransformArgumentsReply { return pushVerdictArguments(['HMGET', key], fields); } diff --git a/lib/commands/HSET.spec.ts b/lib/commands/HSET.spec.ts index af7bcb6eb20..601e7f967e1 100644 --- a/lib/commands/HSET.spec.ts +++ b/lib/commands/HSET.spec.ts @@ -4,6 +4,13 @@ import { TestRedisServers, itWithClient, TestRedisClusters, itWithCluster } from describe('HSET', () => { describe('transformArguments', () => { + it('field, value', () => { + assert.deepEqual( + transformArguments('key', 'field', 'value'), + ['HSET', 'key', 'field', 'value'] + ); + }); + it('Map', () => { assert.deepEqual( transformArguments('key', new Map([['field', 'value']])), @@ -30,7 +37,7 @@ describe('HSET', () => { itWithClient(TestRedisServers.OPEN, 'client.hSet', async client => { assert.equal( - await client.hSet('key', { field: 'value' }), + await client.hSet('key', 'field', 'value'), 1 ); }); diff --git a/lib/commands/HSET.ts b/lib/commands/HSET.ts index 3edaa64b4e8..cbd46061ad8 100644 --- a/lib/commands/HSET.ts +++ b/lib/commands/HSET.ts @@ -1,3 +1,4 @@ +import { TransformArgumentsReply } from '.'; import { transformReplyString } from './generic-transformers'; type HSETObject = Record; @@ -8,10 +9,18 @@ type HSETTuples = Array<[string, string]> | Array; export const FIRST_KEY_INDEX = 1; -export function transformArguments(key: string, value: HSETObject | HSETMap | HSETTuples): Array { +type GenericArguments = [key: string]; + +type SingleFieldArguments = [...generic: GenericArguments, field: string, value: string]; + +type MultipleFieldsArguments = [...generic: GenericArguments, value: HSETObject | HSETMap | HSETTuples]; + +export function transformArguments(...[ key, value, fieldValue ]: SingleFieldArguments | MultipleFieldsArguments): TransformArgumentsReply { const args = ['HSET', key]; - if (value instanceof Map) { + if (typeof value === 'string') { + args.push(value, fieldValue!); + } else if (value instanceof Map) { pushMap(args, value); } else if (Array.isArray(value)) { pushTuples(args, value); diff --git a/lib/commands/LPUSH.ts b/lib/commands/LPUSH.ts index 434ad619cb7..7416d4946ea 100644 --- a/lib/commands/LPUSH.ts +++ b/lib/commands/LPUSH.ts @@ -1,8 +1,9 @@ +import { TransformArgumentsReply } from '.'; import { pushVerdictArguments, transformReplyNumber } from './generic-transformers'; export const FIRST_KEY_INDEX = 1; -export function transformArguments(key: string, elements: string | Array): Array { +export function transformArguments(key: string, elements: string | Array): TransformArgumentsReply { return pushVerdictArguments(['LPUSH', key], elements);} export const transformReply = transformReplyNumber; diff --git a/lib/commands/LPUSHX.ts b/lib/commands/LPUSHX.ts index f1a989d9625..f89623ace3a 100644 --- a/lib/commands/LPUSHX.ts +++ b/lib/commands/LPUSHX.ts @@ -1,8 +1,9 @@ +import { TransformArgumentsReply } from '.'; import { pushVerdictArguments, transformReplyNumber } from './generic-transformers'; export const FIRST_KEY_INDEX = 1; -export function transformArguments(key: string, element: string | Array): Array { +export function transformArguments(key: string, element: string | Array): TransformArgumentsReply { return pushVerdictArguments(['LPUSHX', key], element); } diff --git a/lib/commands/PFADD.ts b/lib/commands/PFADD.ts index 3348a98852a..cc99bed7f65 100644 --- a/lib/commands/PFADD.ts +++ b/lib/commands/PFADD.ts @@ -1,8 +1,9 @@ +import { TransformArgumentsReply } from '.'; import { pushVerdictArguments, transformReplyBoolean } from './generic-transformers'; export const FIRST_KEY_INDEX = 1; -export function transformArguments(key: string, element: string | Array): Array { +export function transformArguments(key: string, element: string | Array): TransformArgumentsReply { return pushVerdictArguments(['PFADD', key], element); } diff --git a/lib/commands/PFCOUNT.ts b/lib/commands/PFCOUNT.ts index eac710a3543..52963697adf 100644 --- a/lib/commands/PFCOUNT.ts +++ b/lib/commands/PFCOUNT.ts @@ -1,8 +1,9 @@ +import { TransformArgumentsReply } from '.'; import { pushVerdictArguments, transformReplyNumber } from './generic-transformers'; export const FIRST_KEY_INDEX = 1; -export function transformArguments(key: string | Array): Array { +export function transformArguments(key: string | Array): TransformArgumentsReply { return pushVerdictArguments(['PFCOUNT'], key); } diff --git a/lib/commands/PFMERGE.ts b/lib/commands/PFMERGE.ts index 73a4a2edb9a..c4ba11877f7 100644 --- a/lib/commands/PFMERGE.ts +++ b/lib/commands/PFMERGE.ts @@ -1,8 +1,9 @@ +import { TransformArgumentsReply } from '.'; import { pushVerdictArguments, transformReplyString } from './generic-transformers'; export const FIRST_KEY_INDEX = 1; -export function transformArguments(destination: string, source: string | Array): Array { +export function transformArguments(destination: string, source: string | Array): TransformArgumentsReply { return pushVerdictArguments(['PFMERGE', destination], source); } diff --git a/lib/commands/PUBSUB_NUMSUB.spec.ts b/lib/commands/PUBSUB_NUMSUB.spec.ts index 74065dbb48f..403732f8f9d 100644 --- a/lib/commands/PUBSUB_NUMSUB.spec.ts +++ b/lib/commands/PUBSUB_NUMSUB.spec.ts @@ -33,7 +33,7 @@ describe('PUBSUB NUMSUB', () => { ); }); - itWithCluster(TestRedisClusters.OPEN, 'cluster.pubSubNumPat', async cluster => { + itWithCluster(TestRedisClusters.OPEN, 'cluster.pubSubNumSub', async cluster => { assert.deepEqual( await cluster.pubSubNumSub(), Object.create(null) diff --git a/lib/commands/RPUSH.ts b/lib/commands/RPUSH.ts index 191d2704e09..665094f47a5 100644 --- a/lib/commands/RPUSH.ts +++ b/lib/commands/RPUSH.ts @@ -1,8 +1,9 @@ +import { TransformArgumentsReply } from '.'; import { pushVerdictArguments, transformReplyNumber } from './generic-transformers'; export const FIRST_KEY_INDEX = 1; -export function transformArguments(key: string, element: string | Array): Array { +export function transformArguments(key: string, element: string | Array): TransformArgumentsReply { return pushVerdictArguments(['RPUSH', key], element); } diff --git a/lib/commands/RPUSHX.ts b/lib/commands/RPUSHX.ts index a07615a58e0..fe1f969f3f6 100644 --- a/lib/commands/RPUSHX.ts +++ b/lib/commands/RPUSHX.ts @@ -1,8 +1,9 @@ +import { TransformArgumentsReply } from '.'; import { pushVerdictArguments, transformReplyNumber } from './generic-transformers'; export const FIRST_KEY_INDEX = 1; -export function transformArguments(key: string, element: string | Array): Array { +export function transformArguments(key: string, element: string | Array): TransformArgumentsReply { return pushVerdictArguments(['RPUSHX', key], element); } diff --git a/lib/commands/SADD.ts b/lib/commands/SADD.ts index a14ba1686c0..a432ccfef59 100644 --- a/lib/commands/SADD.ts +++ b/lib/commands/SADD.ts @@ -1,8 +1,9 @@ +import { TransformArgumentsReply } from '.'; import { pushVerdictArguments, transformReplyNumber } from './generic-transformers'; export const FIRST_KEY_INDEX = 1; -export function transformArguments(key: string, members: string | Array): Array { +export function transformArguments(key: string, members: string | Array): TransformArgumentsReply { return pushVerdictArguments(['SADD', key], members); } diff --git a/lib/commands/SCRIPT_EXISTS.ts b/lib/commands/SCRIPT_EXISTS.ts index b127a0b261b..47a7f456e9b 100644 --- a/lib/commands/SCRIPT_EXISTS.ts +++ b/lib/commands/SCRIPT_EXISTS.ts @@ -1,6 +1,7 @@ +import { TransformArgumentsReply } from '.'; import { pushVerdictArguments, transformReplyBooleanArray } from './generic-transformers'; -export function transformArguments(sha1: string | Array): Array { +export function transformArguments(sha1: string | Array): TransformArgumentsReply { return pushVerdictArguments(['SCRIPT', 'EXISTS'], sha1); } diff --git a/lib/commands/SDIFF.ts b/lib/commands/SDIFF.ts index 496ed593370..4d5aaea1a06 100644 --- a/lib/commands/SDIFF.ts +++ b/lib/commands/SDIFF.ts @@ -1,8 +1,9 @@ +import { TransformArgumentsReply } from '.'; import { pushVerdictArguments, transformReplyStringArray } from './generic-transformers'; export const FIRST_KEY_INDEX = 1; -export function transformArguments(keys: string | Array): Array { +export function transformArguments(keys: string | Array): TransformArgumentsReply { return pushVerdictArguments(['SDIFF'], keys); } diff --git a/lib/commands/SDIFFSTORE.ts b/lib/commands/SDIFFSTORE.ts index 295433602fb..69883d4124c 100644 --- a/lib/commands/SDIFFSTORE.ts +++ b/lib/commands/SDIFFSTORE.ts @@ -1,8 +1,9 @@ +import { TransformArgumentsReply } from '.'; import { pushVerdictArguments, transformReplyNumber } from './generic-transformers'; export const FIRST_KEY_INDEX = 1; -export function transformArguments(destination: string, keys: string | Array): Array { +export function transformArguments(destination: string, keys: string | Array): TransformArgumentsReply { return pushVerdictArguments(['SDIFFSTORE', destination], keys); } diff --git a/lib/commands/SET.spec.ts b/lib/commands/SET.spec.ts index a587f6c3120..32d138f2920 100644 --- a/lib/commands/SET.spec.ts +++ b/lib/commands/SET.spec.ts @@ -106,7 +106,7 @@ describe('SET', () => { 'OK' ); }); - + itWithClient(TestRedisServers.OPEN, 'with GET on empty key', async client => { assert.equal( await client.set('key', 'value', { diff --git a/lib/commands/SET.ts b/lib/commands/SET.ts index 4d5919cde21..03853b3f7d6 100644 --- a/lib/commands/SET.ts +++ b/lib/commands/SET.ts @@ -1,3 +1,5 @@ +import { TransformArgumentsReply } from '.'; + export const FIRST_KEY_INDEX = 1; interface EX { @@ -38,7 +40,7 @@ interface SetCommonOptions { type SetOptions = SetTTL & SetGuards & (SetCommonOptions | {}); -export function transformArguments(key: string, value: string, options?: SetOptions): Array { +export function transformArguments(key: string | Buffer, value: string | Buffer, options?: SetOptions): TransformArgumentsReply { const args = ['SET', key, value]; if (!options) { diff --git a/lib/commands/SETBIT.ts b/lib/commands/SETBIT.ts index 0cd41d1b975..33b2ff1a838 100644 --- a/lib/commands/SETBIT.ts +++ b/lib/commands/SETBIT.ts @@ -1,8 +1,9 @@ +import { TransformArgumentsReply } from '.'; import { BitValue, transformReplyBit } from './generic-transformers'; export const FIRST_KEY_INDEX = 1; -export function transformArguments(key: string, offset: number, value: BitValue) { +export function transformArguments(key: string, offset: number, value: BitValue): TransformArgumentsReply { return ['SETBIT', key, offset.toString(), value.toString()]; } diff --git a/lib/commands/SETEX.ts b/lib/commands/SETEX.ts index 57c32db6ffe..320278c9264 100644 --- a/lib/commands/SETEX.ts +++ b/lib/commands/SETEX.ts @@ -1,8 +1,9 @@ +import { TransformArgumentsReply } from '.'; import { transformReplyString } from './generic-transformers'; export const FIRST_KEY_INDEX = 1; -export function transformArguments(key: string, seconds: number, value: string): Array { +export function transformArguments(key: string | Buffer, seconds: number, value: string): TransformArgumentsReply { return [ 'SETEX', key, diff --git a/lib/commands/SINTER.ts b/lib/commands/SINTER.ts index 104e81b9214..43869652370 100644 --- a/lib/commands/SINTER.ts +++ b/lib/commands/SINTER.ts @@ -1,8 +1,9 @@ +import { TransformArgumentsReply } from '.'; import { pushVerdictArguments, transformReplyStringArray } from './generic-transformers'; export const FIRST_KEY_INDEX = 1; -export function transformArguments(keys: string | Array): Array { +export function transformArguments(keys: string | Array): TransformArgumentsReply { return pushVerdictArguments(['SINTER'], keys); } diff --git a/lib/commands/SINTERSTORE.ts b/lib/commands/SINTERSTORE.ts index a7a4d4fd106..5ad1b11cbac 100644 --- a/lib/commands/SINTERSTORE.ts +++ b/lib/commands/SINTERSTORE.ts @@ -1,8 +1,9 @@ +import { TransformArgumentsReply } from '.'; import { pushVerdictArguments, transformReplyStringArray } from './generic-transformers'; export const FIRST_KEY_INDEX = 1; -export function transformArguments(destination: string, keys: string | Array): Array { +export function transformArguments(destination: string, keys: string | Array): TransformArgumentsReply { return pushVerdictArguments(['SINTERSTORE', destination], keys); } diff --git a/lib/commands/SREM.ts b/lib/commands/SREM.ts index d1021bb3a19..4ae33245d29 100644 --- a/lib/commands/SREM.ts +++ b/lib/commands/SREM.ts @@ -1,8 +1,9 @@ +import { TransformArgumentsReply } from '.'; import { pushVerdictArguments, transformReplyNumber } from './generic-transformers'; export const FIRST_KEY_INDEX = 1; -export function transformArguments(key: string, members: string | Array): Array { +export function transformArguments(key: string, members: string | Array): TransformArgumentsReply { return pushVerdictArguments(['SREM', key], members); } diff --git a/lib/commands/SUNION.ts b/lib/commands/SUNION.ts index 3f06138b1b6..705bff29927 100644 --- a/lib/commands/SUNION.ts +++ b/lib/commands/SUNION.ts @@ -1,10 +1,11 @@ +import { TransformArgumentsReply } from '.'; import { pushVerdictArguments, transformReplyStringArray } from './generic-transformers'; export const FIRST_KEY_INDEX = 1; export const IS_READ_ONLY = true; -export function transformArguments(keys: string | Array): Array { +export function transformArguments(keys: string | Array): TransformArgumentsReply { return pushVerdictArguments(['SUNION'], keys); } diff --git a/lib/commands/SUNIONSTORE.ts b/lib/commands/SUNIONSTORE.ts index 7a1aab80117..af717f627df 100644 --- a/lib/commands/SUNIONSTORE.ts +++ b/lib/commands/SUNIONSTORE.ts @@ -1,8 +1,9 @@ +import { TransformArgumentsReply } from '.'; import { pushVerdictArguments, transformReplyNumber } from './generic-transformers'; export const FIRST_KEY_INDEX = 1; -export function transformArguments(destination: string, keys: string | Array): Array { +export function transformArguments(destination: string, keys: string | Array): TransformArgumentsReply { return pushVerdictArguments(['SUNIONSTORE', destination], keys); } diff --git a/lib/commands/TOUCH.ts b/lib/commands/TOUCH.ts index f2fb0548970..abff4160392 100644 --- a/lib/commands/TOUCH.ts +++ b/lib/commands/TOUCH.ts @@ -1,8 +1,9 @@ +import { TransformArgumentsReply } from '.'; import { pushVerdictArguments, transformReplyNumber } from './generic-transformers'; export const FIRST_KEY_INDEX = 1; -export function transformArguments(key: string | Array): Array { +export function transformArguments(key: string | Array): TransformArgumentsReply { return pushVerdictArguments(['TOUCH'], key); } diff --git a/lib/commands/UNLINK.ts b/lib/commands/UNLINK.ts index 9dfe0ca48ea..4647a976e42 100644 --- a/lib/commands/UNLINK.ts +++ b/lib/commands/UNLINK.ts @@ -1,8 +1,9 @@ +import { TransformArgumentsReply } from '.'; import { pushVerdictArguments, transformReplyNumber } from './generic-transformers'; export const FIRST_KEY_INDEX = 1; -export function transformArguments(key: string | Array): Array { +export function transformArguments(key: string | Array): TransformArgumentsReply { return pushVerdictArguments(['UNLINK'], key); } diff --git a/lib/commands/WATCH.ts b/lib/commands/WATCH.ts index 5e24ca37952..e644ab0f462 100644 --- a/lib/commands/WATCH.ts +++ b/lib/commands/WATCH.ts @@ -1,6 +1,7 @@ +import { TransformArgumentsReply } from '.'; import { pushVerdictArguments, transformReplyString } from './generic-transformers'; -export function transformArguments(key: string | Array): Array { +export function transformArguments(key: string | Array): TransformArgumentsReply { return pushVerdictArguments(['WATCH'], key); } diff --git a/lib/commands/XACK.ts b/lib/commands/XACK.ts index 969f9b6a8b9..a6de28151eb 100644 --- a/lib/commands/XACK.ts +++ b/lib/commands/XACK.ts @@ -1,8 +1,9 @@ +import { TransformArgumentsReply } from '.'; import { pushVerdictArguments, transformReplyNumber } from './generic-transformers'; export const FIRST_KEY_INDEX = 1; -export function transformArguments(key: string, group: string, id: string | Array): Array { +export function transformArguments(key: string, group: string, id: string | Array): TransformArgumentsReply { return pushVerdictArguments(['XACK', key, group], id); } diff --git a/lib/commands/XDEL.ts b/lib/commands/XDEL.ts index 9d173271c28..083ea77ef0f 100644 --- a/lib/commands/XDEL.ts +++ b/lib/commands/XDEL.ts @@ -1,8 +1,9 @@ +import { TransformArgumentsReply } from '.'; import { pushVerdictArguments, transformReplyNumber } from './generic-transformers'; export const FIRST_KEY_INDEX = 1; -export function transformArguments(key: string, id: string | Array): Array { +export function transformArguments(key: string, id: string | Array): TransformArgumentsReply { return pushVerdictArguments(['XDEL', key], id); } diff --git a/lib/commands/ZDIFF.ts b/lib/commands/ZDIFF.ts index f557b597ec4..7154947fea7 100644 --- a/lib/commands/ZDIFF.ts +++ b/lib/commands/ZDIFF.ts @@ -1,10 +1,11 @@ +import { TransformArgumentsReply } from '.'; import { pushVerdictArgument, transformReplyStringArray } from './generic-transformers'; export const FIRST_KEY_INDEX = 2; export const IS_READ_ONLY = true; -export function transformArguments(keys: Array | string): Array { +export function transformArguments(keys: Array | string): TransformArgumentsReply { return pushVerdictArgument(['ZDIFF'], keys); } diff --git a/lib/commands/ZDIFFSTORE.ts b/lib/commands/ZDIFFSTORE.ts index de409c0939a..f91d4c869ba 100644 --- a/lib/commands/ZDIFFSTORE.ts +++ b/lib/commands/ZDIFFSTORE.ts @@ -1,8 +1,9 @@ +import { TransformArgumentsReply } from '.'; import { pushVerdictArgument, transformReplyNumber } from './generic-transformers'; export const FIRST_KEY_INDEX = 1; -export function transformArguments(destination: string, keys: Array | string): Array { +export function transformArguments(destination: string, keys: Array | string): TransformArgumentsReply { return pushVerdictArgument(['ZDIFFSTORE', destination], keys); } diff --git a/lib/commands/ZDIFF_WITHSCORES.ts b/lib/commands/ZDIFF_WITHSCORES.ts index 26effab7189..84126853361 100644 --- a/lib/commands/ZDIFF_WITHSCORES.ts +++ b/lib/commands/ZDIFF_WITHSCORES.ts @@ -1,9 +1,10 @@ +import { TransformArgumentsReply } from '.'; import { transformReplySortedSetWithScores } from './generic-transformers'; import { transformArguments as transformZDiffArguments } from './ZDIFF'; export { FIRST_KEY_INDEX, IS_READ_ONLY } from './ZDIFF'; -export function transformArguments(...args: Parameters): Array { +export function transformArguments(...args: Parameters): TransformArgumentsReply { return [ ...transformZDiffArguments(...args), 'WITHSCORES' diff --git a/lib/commands/ZINTER.ts b/lib/commands/ZINTER.ts index 90a42eda0d3..91d7982a8e7 100644 --- a/lib/commands/ZINTER.ts +++ b/lib/commands/ZINTER.ts @@ -1,3 +1,4 @@ +import { TransformArgumentsReply } from '.'; import { pushVerdictArgument, transformReplyStringArray } from './generic-transformers'; export const FIRST_KEY_INDEX = 2; @@ -9,7 +10,7 @@ interface ZInterOptions { AGGREGATE?: 'SUM' | 'MIN' | 'MAX'; } -export function transformArguments(keys: Array | string, options?: ZInterOptions): Array { +export function transformArguments(keys: Array | string, options?: ZInterOptions): TransformArgumentsReply { const args = pushVerdictArgument(['ZINTER'], keys); if (options?.WEIGHTS) { diff --git a/lib/commands/ZINTERSTORE.ts b/lib/commands/ZINTERSTORE.ts index a026916ce1f..6e79e423cb0 100644 --- a/lib/commands/ZINTERSTORE.ts +++ b/lib/commands/ZINTERSTORE.ts @@ -1,3 +1,4 @@ +import { TransformArgumentsReply } from '.'; import { pushVerdictArgument, transformReplyNumber } from './generic-transformers'; export const FIRST_KEY_INDEX = 1; @@ -7,7 +8,7 @@ interface ZInterStoreOptions { AGGREGATE?: 'SUM' | 'MIN' | 'MAX'; } -export function transformArguments(destination: string, keys: Array | string, options?: ZInterStoreOptions): Array { +export function transformArguments(destination: string, keys: Array | string, options?: ZInterStoreOptions): TransformArgumentsReply { const args = pushVerdictArgument(['ZINTERSTORE', destination], keys); if (options?.WEIGHTS) { diff --git a/lib/commands/ZINTER_WITHSCORES.ts b/lib/commands/ZINTER_WITHSCORES.ts index 0a82228fce9..f4287d1a684 100644 --- a/lib/commands/ZINTER_WITHSCORES.ts +++ b/lib/commands/ZINTER_WITHSCORES.ts @@ -1,9 +1,10 @@ +import { TransformArgumentsReply } from '.'; import { transformReplySortedSetWithScores } from './generic-transformers'; import { transformArguments as transformZInterArguments } from './ZINTER'; export { FIRST_KEY_INDEX, IS_READ_ONLY } from './ZINTER'; -export function transformArguments(...args: Parameters): Array { +export function transformArguments(...args: Parameters): TransformArgumentsReply { return [ ...transformZInterArguments(...args), 'WITHSCORES' diff --git a/lib/commands/ZMSCORE.ts b/lib/commands/ZMSCORE.ts index 8a6f73c7836..373adac3cf0 100644 --- a/lib/commands/ZMSCORE.ts +++ b/lib/commands/ZMSCORE.ts @@ -1,10 +1,11 @@ +import { TransformArgumentsReply } from '.'; import { pushVerdictArguments, transformReplyNumberInfinityNullArray } from './generic-transformers'; export const FIRST_KEY_INDEX = 1; export const IS_READ_ONLY = true; -export function transformArguments(key: string, member: string | Array): Array { +export function transformArguments(key: string, member: string | Array): TransformArgumentsReply { return pushVerdictArguments(['ZMSCORE', key], member); } diff --git a/lib/commands/ZREM.ts b/lib/commands/ZREM.ts index 089b6136afd..8419291f2fd 100644 --- a/lib/commands/ZREM.ts +++ b/lib/commands/ZREM.ts @@ -1,8 +1,9 @@ +import { TransformArgumentsReply } from '.'; import { pushVerdictArguments, transformReplyNumber } from './generic-transformers'; export const FIRST_KEY_INDEX = 1; -export function transformArguments(key: string, member: string | Array): Array { +export function transformArguments(key: string, member: string | Array): TransformArgumentsReply { return pushVerdictArguments(['ZREM', key], member); } diff --git a/lib/commands/ZUNION.ts b/lib/commands/ZUNION.ts index efdfccb1ff4..87158b8425a 100644 --- a/lib/commands/ZUNION.ts +++ b/lib/commands/ZUNION.ts @@ -1,3 +1,4 @@ +import { TransformArgumentsReply } from '.'; import { pushVerdictArgument, transformReplyStringArray } from './generic-transformers'; export const FIRST_KEY_INDEX = 2; @@ -9,7 +10,7 @@ interface ZUnionOptions { AGGREGATE?: 'SUM' | 'MIN' | 'MAX'; } -export function transformArguments(keys: Array | string, options?: ZUnionOptions): Array { +export function transformArguments(keys: Array | string, options?: ZUnionOptions): TransformArgumentsReply { const args = pushVerdictArgument(['ZUNION'], keys); if (options?.WEIGHTS) { diff --git a/lib/commands/ZUNIONSTORE.ts b/lib/commands/ZUNIONSTORE.ts index c03f1203706..4ebbdbd8591 100644 --- a/lib/commands/ZUNIONSTORE.ts +++ b/lib/commands/ZUNIONSTORE.ts @@ -1,3 +1,4 @@ +import { TransformArgumentsReply } from '.'; import { pushVerdictArgument, transformReplyNumber } from './generic-transformers'; export const FIRST_KEY_INDEX = 1; @@ -7,7 +8,7 @@ interface ZUnionOptions { AGGREGATE?: 'SUM' | 'MIN' | 'MAX'; } -export function transformArguments(destination: string, keys: Array | string, options?: ZUnionOptions): Array { +export function transformArguments(destination: string, keys: Array | string, options?: ZUnionOptions): TransformArgumentsReply { const args = pushVerdictArgument(['ZUNIONSTORE', destination], keys); if (options?.WEIGHTS) { diff --git a/lib/commands/ZUNION_WITHSCORES.ts b/lib/commands/ZUNION_WITHSCORES.ts index d0cef45cfb1..2215dad9749 100644 --- a/lib/commands/ZUNION_WITHSCORES.ts +++ b/lib/commands/ZUNION_WITHSCORES.ts @@ -1,9 +1,10 @@ +import { TransformArgumentsReply } from '.'; import { transformReplySortedSetWithScores } from './generic-transformers'; import { transformArguments as transformZUnionArguments } from './ZUNION'; export { FIRST_KEY_INDEX, IS_READ_ONLY } from './ZUNION'; -export function transformArguments(...args: Parameters): Array { +export function transformArguments(...args: Parameters): TransformArgumentsReply { return [ ...transformZUnionArguments(...args), 'WITHSCORES' diff --git a/lib/commands/generic-transformers.ts b/lib/commands/generic-transformers.ts index 8105bfe903f..bbc12ee113e 100644 --- a/lib/commands/generic-transformers.ts +++ b/lib/commands/generic-transformers.ts @@ -50,6 +50,10 @@ export function transformReplyBit(reply: BitValue): BitValue { return reply; } +export function transformReplyBufferNull(reply: Buffer | null): Buffer | null { + return reply; +} + export function transformReplyVoid(): void {} export interface ScanOptions { @@ -352,11 +356,11 @@ export function pushStringTuplesArguments(args: Array, tuples: StringTup return args; } -export function pushVerdictArguments(args: TransformArgumentsReply, value: string | Array): TransformArgumentsReply { - if (typeof value === 'string') { - args.push(value); - } else { +export function pushVerdictArguments(args: TransformArgumentsReply, value: string | Buffer | Array): TransformArgumentsReply { + if (Array.isArray(value)) { args.push(...value); + } else { + args.push(value); } return args; diff --git a/lib/commands/index.ts b/lib/commands/index.ts index cffb47c668a..89581090e58 100644 --- a/lib/commands/index.ts +++ b/lib/commands/index.ts @@ -34,6 +34,7 @@ import * as CLUSTER_NODES from './CLUSTER_NODES'; import * as CLUSTER_MEET from './CLUSTER_MEET'; import * as CLUSTER_RESET from './CLUSTER_RESET'; import * as CLUSTER_SETSLOT from './CLUSTER_SETSLOT'; +import * as CLUSTER_SLOTS from './CLUSTER_SLOTS'; import * as CONFIG_GET from './CONFIG_GET'; import * as CONFIG_RESETASTAT from './CONFIG_RESETSTAT'; import * as CONFIG_REWRITE from './CONFIG_REWRITE'; @@ -61,6 +62,7 @@ import * as GEOPOS from './GEOPOS'; import * as GEOSEARCH_WITH from './GEOSEARCH_WITH'; import * as GEOSEARCH from './GEOSEARCH'; import * as GEOSEARCHSTORE from './GEOSEARCHSTORE'; +import * as GET_BUFFER from './GET_BUFFER'; import * as GET from './GET'; import * as GETBIT from './GETBIT'; import * as GETDEL from './GETDEL'; @@ -316,6 +318,8 @@ export default { clusterReset: CLUSTER_RESET, CLUSTER_SETSLOT, clusterSetSlot: CLUSTER_SETSLOT, + CLUSTER_SLOTS, + clusterSlots: CLUSTER_SLOTS, CONFIG_GET, configGet: CONFIG_GET, CONFIG_RESETASTAT, @@ -370,6 +374,8 @@ export default { geoSearch: GEOSEARCH, GEOSEARCHSTORE, geoSearchStore: GEOSEARCHSTORE, + GET_BUFFER, + getBuffer: GET_BUFFER, GET, get: GET, GETBIT, @@ -733,15 +739,16 @@ export default { zUnionStore: ZUNIONSTORE }; -export type RedisReply = string | number | Array | null | undefined; +export type RedisReply = string | number | Buffer | Array | null | undefined; -export type TransformArgumentsReply = Array & { preserve?: unknown }; +export type TransformArgumentsReply = Array & { preserve?: unknown }; export interface RedisCommand { FIRST_KEY_INDEX?: number | ((...args: Array) => string); IS_READ_ONLY?: boolean; - transformArguments(...args: Array): TransformArgumentsReply; - transformReply(reply: RedisReply, preserved: unknown): any; + transformArguments(this: void, ...args: Array): TransformArgumentsReply; + BUFFER_MODE?: boolean; + transformReply(this: void, reply: RedisReply, preserved?: unknown): any; } export interface RedisCommands { @@ -749,7 +756,10 @@ export interface RedisCommands { } export interface RedisModule { - [key: string]: RedisCommand; + [command: string]: RedisCommand; } -export type RedisModules = Record; +export interface RedisModules { + [module: string]: RedisModule; +} +// export type RedisModules = Record; diff --git a/lib/lua-script.ts b/lib/lua-script.ts index 183c42f219c..be16f9b9133 100644 --- a/lib/lua-script.ts +++ b/lib/lua-script.ts @@ -13,10 +13,10 @@ export interface SHA1 { export type RedisLuaScript = RedisLuaScriptConfig & SHA1; export interface RedisLuaScripts { - [key: string]: RedisLuaScript; + [script: string]: RedisLuaScript; } -export function defineScript(script: S): S & SHA1 { +export function defineScript(script: RedisLuaScriptConfig): typeof script & SHA1 { return { ...script, SHA1: scriptSha1(script.SCRIPT) diff --git a/lib/multi-command.spec.ts b/lib/multi-command.spec.ts index a78cc8b2e08..52ecfb94b1c 100644 --- a/lib/multi-command.spec.ts +++ b/lib/multi-command.spec.ts @@ -1,6 +1,5 @@ import { strict as assert } from 'assert'; import RedisMultiCommand from './multi-command'; -import { encodeCommand } from './commander'; import { WatchError } from './errors'; import { spy } from 'sinon'; import { SQUARE_SCRIPT } from './client.spec'; @@ -10,11 +9,11 @@ describe('Multi Command', () => { it('simple', async () => { const multi = RedisMultiCommand.create((queue, symbol) => { assert.deepEqual( - queue.map(({encodedCommand}) => encodedCommand), + queue.map(({ args }) => args), [ - encodeCommand(['MULTI']), - encodeCommand(['PING']), - encodeCommand(['EXEC']), + ['MULTI'], + ['PING'], + ['EXEC'], ] ); @@ -55,8 +54,8 @@ describe('Multi Command', () => { it('execAsPipeline', async () => { const multi = RedisMultiCommand.create(queue => { assert.deepEqual( - queue.map(({encodedCommand}) => encodedCommand), - [encodeCommand(['PING'])] + queue.map(({ args }) => args), + [['PING']] ); return Promise.resolve(['PONG']); @@ -75,8 +74,8 @@ describe('Multi Command', () => { it('simple', async () => { const multi = RedisMultiCommand.create(queue => { assert.deepEqual( - queue.map(({encodedCommand}) => encodedCommand), - [encodeCommand(['PING'])] + queue.map(({ args }) => args), + [['PING']] ); return Promise.resolve(['PONG']); @@ -111,10 +110,10 @@ describe('Multi Command', () => { assert.deepEqual( await new MultiWithScript(queue => { assert.deepEqual( - queue.map(({encodedCommand}) => encodedCommand), + queue.map(({ args }) => args), [ - encodeCommand(['EVAL', SQUARE_SCRIPT.SCRIPT, '0', '2']), - encodeCommand(['EVALSHA', SQUARE_SCRIPT.SHA1, '0', '3']), + ['EVAL', SQUARE_SCRIPT.SCRIPT, '0', '2'], + ['EVALSHA', SQUARE_SCRIPT.SHA1, '0', '3'], ] ); diff --git a/lib/multi-command.ts b/lib/multi-command.ts index c8a50765967..a329a5dbf19 100644 --- a/lib/multi-command.ts +++ b/lib/multi-command.ts @@ -2,7 +2,7 @@ import COMMANDS, { TransformArgumentsReply } from './commands'; import { RedisCommand, RedisModules, RedisReply } from './commands'; import { RedisLuaScript, RedisLuaScripts } from './lua-script'; import { RedisClientOptions } from './client'; -import { extendWithModulesAndScripts, extendWithDefaultCommands, encodeCommand } from './commander'; +import { extendWithModulesAndScripts, extendWithDefaultCommands } from './commander'; import { WatchError } from './errors'; type RedisMultiCommandSignature = (...args: Parameters) => RedisMultiCommandType; @@ -21,68 +21,31 @@ type WithScripts = { [P in keyof S]: RedisMultiCommandSignature }; -export type RedisMultiCommandType = RedisMultiCommand & WithCommands & WithModules & WithScripts; +export type RedisMultiCommandType = + RedisMultiCommand & WithCommands & WithModules & WithScripts; export interface MultiQueuedCommand { - encodedCommand: string; + args: TransformArgumentsReply; preservedArguments?: unknown; transformReply?: RedisCommand['transformReply']; } export type RedisMultiExecutor = (queue: Array, chainId?: symbol) => Promise>; -export default class RedisMultiCommand { - static commandsExecutor(this: RedisMultiCommand, command: RedisCommand, args: Array): RedisMultiCommand { - return this.addCommand( - command.transformArguments(...args), - command.transformReply - ); - } - - static #scriptsExecutor( - this: RedisMultiCommand, - script: RedisLuaScript, - args: Array - ): RedisMultiCommand { - const transformedArguments: TransformArgumentsReply = []; - if (this.#scriptsInUse.has(script.SHA1)) { - transformedArguments.push( - 'EVALSHA', - script.SHA1 - ); - } else { - this.#scriptsInUse.add(script.SHA1); - transformedArguments.push( - 'EVAL', - script.SCRIPT - ); - } - - transformedArguments.push(script.NUMBER_OF_KEYS.toString()); - - const scriptArguments = script.transformArguments(...args); - transformedArguments.push(...scriptArguments); - transformedArguments.preserve = scriptArguments.preserve; - - return this.addCommand( - transformedArguments, - script.transformReply - ); - } - +export default class RedisMultiCommand { static extend( clientOptions?: RedisClientOptions ): new (...args: ConstructorParameters) => RedisMultiCommandType { return extendWithModulesAndScripts({ BaseClass: RedisMultiCommand, modules: clientOptions?.modules, - modulesCommandsExecutor: RedisMultiCommand.commandsExecutor, + modulesCommandsExecutor: RedisMultiCommand.prototype.commandsExecutor, scripts: clientOptions?.scripts, - scriptsExecutor: RedisMultiCommand.#scriptsExecutor + scriptsExecutor: RedisMultiCommand.prototype.scriptsExecutor }); } - static create( + static create( executor: RedisMultiExecutor, clientOptions?: RedisClientOptions ): RedisMultiCommandType { @@ -119,7 +82,7 @@ export default class RedisMultiCommand): this => { this.#queue.push({ - encodedCommand: encodeCommand(args.flat() as Array) + args: args.flat() as Array }); return this; } @@ -151,9 +114,45 @@ export default class RedisMultiCommand): void => (this as any).addCommand(name, args); } + commandsExecutor(command: RedisCommand, args: Array): this { + return this.addCommand( + command.transformArguments(...args), + command.transformReply + ); + } + + scriptsExecutor(script: RedisLuaScript, args: Array): this { + const transformedArguments: TransformArgumentsReply = []; + if (this.#scriptsInUse.has(script.SHA1)) { + transformedArguments.push( + 'EVALSHA', + script.SHA1 + ); + } else { + this.#scriptsInUse.add(script.SHA1); + transformedArguments.push( + 'EVAL', + script.SCRIPT + ); + } + + transformedArguments.push(script.NUMBER_OF_KEYS.toString()); + + const scriptArguments = script.transformArguments(...args); + transformedArguments.push(...scriptArguments); + if (scriptArguments.preserve) { + transformedArguments.preserve = scriptArguments.preserve; + } + + return this.addCommand( + transformedArguments, + script.transformReply + ); + } + addCommand(args: TransformArgumentsReply, transformReply?: RedisCommand['transformReply']): this { this.#queue.push({ - encodedCommand: encodeCommand(args), + args, preservedArguments: args.preserve, transformReply }); @@ -170,13 +169,9 @@ export default class RedisMultiCommand); @@ -207,4 +202,4 @@ export default class RedisMultiCommand { connectEvent: string; @@ -44,14 +37,6 @@ export default class RedisSocket extends EventEmitter { static #initiateOptions(options?: RedisSocketOptions): RedisSocketOptions { options ??= {}; if (!RedisSocket.#isUnixSocket(options)) { - if (RedisSocket.#isUrlSocket(options)) { - const url = new URL(options.url); - (options as RedisNetSocketOptions).port = Number(url.port); - (options as RedisNetSocketOptions).host = url.hostname; - options.username = url.username; - options.password = url.password; - } - (options as RedisNetSocketOptions).port ??= 6379; (options as RedisNetSocketOptions).host ??= '127.0.0.1'; } @@ -67,10 +52,6 @@ export default class RedisSocket extends EventEmitter { return Math.min(retries * 50, 500); } - static #isUrlSocket(options: RedisSocketOptions): options is RedisUrlSocketOptions { - return Object.prototype.hasOwnProperty.call(options, 'url'); - } - static #isUnixSocket(options: RedisSocketOptions): options is RedisUnixSocketOptions { return Object.prototype.hasOwnProperty.call(options, 'path'); } @@ -91,10 +72,8 @@ export default class RedisSocket extends EventEmitter { return this.#isOpen; } - get chunkRecommendedSize(): number { - if (!this.#socket) return 0; - - return this.#socket.writableHighWaterMark - this.#socket.writableLength; + get isSocketExists(): boolean { + return !!this.#socket; } constructor(initiator?: RedisSocketInitiator, options?: RedisSocketOptions) { @@ -214,12 +193,12 @@ export default class RedisSocket extends EventEmitter { .catch(err => this.emit('error', err)); } - write(encodedCommands: string): boolean { + write(toWrite: string | Buffer): boolean { if (!this.#socket) { throw new ClientClosedError(); } - return this.#socket.write(encodedCommands); + return this.#socket.write(toWrite); } async disconnect(ignoreIsOpen = false): Promise { @@ -251,4 +230,22 @@ export default class RedisSocket extends EventEmitter { throw err; } } + + #isCorked = false; + + cork(): void { + if (!this.#socket) { + return; + } + + if (!this.#isCorked) { + this.#socket.cork(); + this.#isCorked = true; + + queueMicrotask(() => { + this.#socket?.uncork(); + this.#isCorked = false; + }); + } + } } diff --git a/lib/test-utils.ts b/lib/test-utils.ts index e23d90d47ea..713a1a3434a 100644 --- a/lib/test-utils.ts +++ b/lib/test-utils.ts @@ -1,7 +1,5 @@ import { strict as assert } from 'assert'; -import RedisClient, { RedisClientType } from './client'; -import { RedisModules } from './commands'; -import { RedisLuaScripts } from './lua-script'; +import RedisClient, { RedisClientOptions, RedisClientType } from './client'; import { execSync, spawn } from 'child_process'; import { once } from 'events'; import { RedisSocketOptions } from './socket'; @@ -11,6 +9,8 @@ import RedisCluster, { RedisClusterType } from './cluster'; import { promises as fs } from 'fs'; import { Context as MochaContext } from 'mocha'; import { promiseTimeout } from './utils'; +import { RedisModules } from './commands'; +import { RedisLuaScripts } from './lua-script'; type RedisVersion = [major: number, minor: number, patch: number]; @@ -54,7 +54,7 @@ export enum TestRedisServers { PASSWORD } -export const TEST_REDIS_SERVERS: Record = {}; +export const TEST_REDIS_SERVERS: Record> = {}; export enum TestRedisClusters { OPEN @@ -112,7 +112,7 @@ async function spawnGlobalRedisServer(args?: Array): Promise { const SLOTS = 16384; interface SpawnRedisClusterNodeResult extends SpawnRedisServerResult { - client: RedisClientType + client: RedisClientType } async function spawnRedisClusterNode( @@ -228,13 +228,17 @@ export async function spawnGlobalRedisCluster(type: TestRedisClusters | null, nu async function spawnOpenServer(): Promise { TEST_REDIS_SERVERS[TestRedisServers.OPEN] = { - port: await spawnGlobalRedisServer() + socket: { + port: await spawnGlobalRedisServer() + } }; } async function spawnPasswordServer(): Promise { TEST_REDIS_SERVERS[TestRedisServers.PASSWORD] = { - port: await spawnGlobalRedisServer(['--requirepass', 'password']), + socket: { + port: await spawnGlobalRedisServer(['--requirepass', 'password']), + }, password: 'password' }; @@ -281,15 +285,13 @@ export function describeHandleMinimumRedisVersion(minimumVersion: PartialRedisVe export function itWithClient( type: TestRedisServers, title: string, - fn: (client: RedisClientType) => Promise, + fn: (client: RedisClientType) => Promise, options?: RedisTestOptions ): void { it(title, async function () { if (handleMinimumRedisVersion(this, options?.minimumRedisVersion)) return; - const client = RedisClient.create({ - socket: TEST_REDIS_SERVERS[type] - }); + const client = RedisClient.create(TEST_REDIS_SERVERS[type]); await client.connect(); @@ -306,7 +308,7 @@ export function itWithClient( export function itWithCluster( type: TestRedisClusters, title: string, - fn: (cluster: RedisClusterType) => Promise, + fn: (cluster: RedisClusterType) => Promise, options?: RedisTestOptions ): void { it(title, async function () { @@ -328,7 +330,7 @@ export function itWithCluster( }); } -export function itWithDedicatedCluster(title: string, fn: (cluster: RedisClusterType) => Promise): void { +export function itWithDedicatedCluster(title: string, fn: (cluster: RedisClusterType) => Promise): void { it(title, async function () { this.timeout(10000); @@ -370,4 +372,4 @@ export async function waitTillBeenCalled(spy: SinonSpy): Promise { await promiseTimeout(1); } while (spy.callCount === calls) -} \ No newline at end of file +} diff --git a/lib/ts-declarations/cluster-key-slot.d.ts b/lib/ts-declarations/cluster-key-slot.d.ts index 5774c50fbd4..60421de296b 100644 --- a/lib/ts-declarations/cluster-key-slot.d.ts +++ b/lib/ts-declarations/cluster-key-slot.d.ts @@ -1,3 +1,3 @@ declare module 'cluster-key-slot' { - export default function calculateSlot(key: string): number; + export default function calculateSlot(key: string | Buffer): number; } diff --git a/lib/ts-declarations/redis-parser.d.ts b/lib/ts-declarations/redis-parser.d.ts index 68659616b93..7ec129ed8cd 100644 --- a/lib/ts-declarations/redis-parser.d.ts +++ b/lib/ts-declarations/redis-parser.d.ts @@ -8,6 +8,8 @@ declare module 'redis-parser' { export default class RedisParser { constructor(callbacks: RedisParserCallbacks); + setReturnBuffers(returnBuffers?: boolean): void; + execute(buffer: Buffer): void; } } diff --git a/package-lock.json b/package-lock.json index ac623c60e6a..9fcd62b5996 100644 --- a/package-lock.json +++ b/package-lock.json @@ -1,12 +1,12 @@ { "name": "redis", - "version": "4.0.0-rc.1", + "version": "4.0.0-rc.2", "lockfileVersion": 2, "requires": true, "packages": { "": { "name": "redis", - "version": "4.0.0-rc.1", + "version": "4.0.0-rc.2", "license": "MIT", "dependencies": { "cluster-key-slot": "1.1.0", @@ -17,20 +17,20 @@ "devDependencies": { "@istanbuljs/nyc-config-typescript": "^1.0.1", "@types/mocha": "^9.0.0", - "@types/node": "^16.7.10", - "@types/sinon": "^10.0.2", + "@types/node": "^16.9.6", + "@types/sinon": "^10.0.3", "@types/which": "^2.0.1", "@types/yallist": "^4.0.1", "mocha": "^9.1.1", "nyc": "^15.1.0", - "release-it": "^14.11.5", + "release-it": "^14.11.6", "sinon": "^11.1.2", - "source-map-support": "^0.5.19", + "source-map-support": "^0.5.20", "ts-node": "^10.2.1", - "typedoc": "^0.21.9", + "typedoc": "0.21.9", "typedoc-github-wiki-theme": "^0.5.1", - "typedoc-plugin-markdown": "^3.10.4", - "typescript": "^4.4.2", + "typedoc-plugin-markdown": "3.10.4", + "typescript": "^4.4.3", "which": "^2.0.2" }, "engines": { @@ -59,20 +59,20 @@ } }, "node_modules/@babel/core": { - "version": "7.15.0", - "resolved": "https://registry.npmjs.org/@babel/core/-/core-7.15.0.tgz", - "integrity": "sha512-tXtmTminrze5HEUPn/a0JtOzzfp0nk+UEXQ/tqIJo3WDGypl/2OFQEMll/zSFU8f/lfmfLXvTaORHF3cfXIQMw==", + "version": "7.15.5", + "resolved": "https://registry.npmjs.org/@babel/core/-/core-7.15.5.tgz", + "integrity": "sha512-pYgXxiwAgQpgM1bNkZsDEq85f0ggXMA5L7c+o3tskGMh2BunCI9QUwB9Z4jpvXUOuMdyGKiGKQiRe11VS6Jzvg==", "dev": true, "dependencies": { "@babel/code-frame": "^7.14.5", - "@babel/generator": "^7.15.0", - "@babel/helper-compilation-targets": "^7.15.0", - "@babel/helper-module-transforms": "^7.15.0", - "@babel/helpers": "^7.14.8", - "@babel/parser": "^7.15.0", - "@babel/template": "^7.14.5", - "@babel/traverse": "^7.15.0", - "@babel/types": "^7.15.0", + "@babel/generator": "^7.15.4", + "@babel/helper-compilation-targets": "^7.15.4", + "@babel/helper-module-transforms": "^7.15.4", + "@babel/helpers": "^7.15.4", + "@babel/parser": "^7.15.5", + "@babel/template": "^7.15.4", + "@babel/traverse": "^7.15.4", + "@babel/types": "^7.15.4", "convert-source-map": "^1.7.0", "debug": "^4.1.0", "gensync": "^1.0.0-beta.2", @@ -89,12 +89,12 @@ } }, "node_modules/@babel/generator": { - "version": "7.15.0", - "resolved": "https://registry.npmjs.org/@babel/generator/-/generator-7.15.0.tgz", - "integrity": "sha512-eKl4XdMrbpYvuB505KTta4AV9g+wWzmVBW69tX0H2NwKVKd2YJbKgyK6M8j/rgLbmHOYJn6rUklV677nOyJrEQ==", + "version": "7.15.4", + "resolved": "https://registry.npmjs.org/@babel/generator/-/generator-7.15.4.tgz", + "integrity": "sha512-d3itta0tu+UayjEORPNz6e1T3FtvWlP5N4V5M+lhp/CxT4oAA7/NcScnpRyspUMLK6tu9MNHmQHxRykuN2R7hw==", "dev": true, "dependencies": { - "@babel/types": "^7.15.0", + "@babel/types": "^7.15.4", "jsesc": "^2.5.1", "source-map": "^0.5.0" }, @@ -103,9 +103,9 @@ } }, "node_modules/@babel/helper-compilation-targets": { - "version": "7.15.0", - "resolved": "https://registry.npmjs.org/@babel/helper-compilation-targets/-/helper-compilation-targets-7.15.0.tgz", - "integrity": "sha512-h+/9t0ncd4jfZ8wsdAsoIxSa61qhBYlycXiHWqJaQBCXAhDCMbPRSMTGnZIkkmt1u4ag+UQmuqcILwqKzZ4N2A==", + "version": "7.15.4", + "resolved": "https://registry.npmjs.org/@babel/helper-compilation-targets/-/helper-compilation-targets-7.15.4.tgz", + "integrity": "sha512-rMWPCirulnPSe4d+gwdWXLfAXTTBj8M3guAf5xFQJ0nvFY7tfNAFnWdqaHegHlgDZOCT4qvhF3BYlSJag8yhqQ==", "dev": true, "dependencies": { "@babel/compat-data": "^7.15.0", @@ -121,141 +121,141 @@ } }, "node_modules/@babel/helper-function-name": { - "version": "7.14.5", - "resolved": "https://registry.npmjs.org/@babel/helper-function-name/-/helper-function-name-7.14.5.tgz", - "integrity": "sha512-Gjna0AsXWfFvrAuX+VKcN/aNNWonizBj39yGwUzVDVTlMYJMK2Wp6xdpy72mfArFq5uK+NOuexfzZlzI1z9+AQ==", + "version": "7.15.4", + "resolved": "https://registry.npmjs.org/@babel/helper-function-name/-/helper-function-name-7.15.4.tgz", + "integrity": "sha512-Z91cOMM4DseLIGOnog+Z8OI6YseR9bua+HpvLAQ2XayUGU+neTtX+97caALaLdyu53I/fjhbeCnWnRH1O3jFOw==", "dev": true, "dependencies": { - "@babel/helper-get-function-arity": "^7.14.5", - "@babel/template": "^7.14.5", - "@babel/types": "^7.14.5" + "@babel/helper-get-function-arity": "^7.15.4", + "@babel/template": "^7.15.4", + "@babel/types": "^7.15.4" }, "engines": { "node": ">=6.9.0" } }, "node_modules/@babel/helper-get-function-arity": { - "version": "7.14.5", - "resolved": "https://registry.npmjs.org/@babel/helper-get-function-arity/-/helper-get-function-arity-7.14.5.tgz", - "integrity": "sha512-I1Db4Shst5lewOM4V+ZKJzQ0JGGaZ6VY1jYvMghRjqs6DWgxLCIyFt30GlnKkfUeFLpJt2vzbMVEXVSXlIFYUg==", + "version": "7.15.4", + "resolved": "https://registry.npmjs.org/@babel/helper-get-function-arity/-/helper-get-function-arity-7.15.4.tgz", + "integrity": "sha512-1/AlxSF92CmGZzHnC515hm4SirTxtpDnLEJ0UyEMgTMZN+6bxXKg04dKhiRx5Enel+SUA1G1t5Ed/yQia0efrA==", "dev": true, "dependencies": { - "@babel/types": "^7.14.5" + "@babel/types": "^7.15.4" }, "engines": { "node": ">=6.9.0" } }, "node_modules/@babel/helper-hoist-variables": { - "version": "7.14.5", - "resolved": "https://registry.npmjs.org/@babel/helper-hoist-variables/-/helper-hoist-variables-7.14.5.tgz", - "integrity": "sha512-R1PXiz31Uc0Vxy4OEOm07x0oSjKAdPPCh3tPivn/Eo8cvz6gveAeuyUUPB21Hoiif0uoPQSSdhIPS3352nvdyQ==", + "version": "7.15.4", + "resolved": "https://registry.npmjs.org/@babel/helper-hoist-variables/-/helper-hoist-variables-7.15.4.tgz", + "integrity": "sha512-VTy085egb3jUGVK9ycIxQiPbquesq0HUQ+tPO0uv5mPEBZipk+5FkRKiWq5apuyTE9FUrjENB0rCf8y+n+UuhA==", "dev": true, "dependencies": { - "@babel/types": "^7.14.5" + "@babel/types": "^7.15.4" }, "engines": { "node": ">=6.9.0" } }, "node_modules/@babel/helper-member-expression-to-functions": { - "version": "7.15.0", - "resolved": "https://registry.npmjs.org/@babel/helper-member-expression-to-functions/-/helper-member-expression-to-functions-7.15.0.tgz", - "integrity": "sha512-Jq8H8U2kYiafuj2xMTPQwkTBnEEdGKpT35lJEQsRRjnG0LW3neucsaMWLgKcwu3OHKNeYugfw+Z20BXBSEs2Lg==", + "version": "7.15.4", + "resolved": "https://registry.npmjs.org/@babel/helper-member-expression-to-functions/-/helper-member-expression-to-functions-7.15.4.tgz", + "integrity": "sha512-cokOMkxC/BTyNP1AlY25HuBWM32iCEsLPI4BHDpJCHHm1FU2E7dKWWIXJgQgSFiu4lp8q3bL1BIKwqkSUviqtA==", "dev": true, "dependencies": { - "@babel/types": "^7.15.0" + "@babel/types": "^7.15.4" }, "engines": { "node": ">=6.9.0" } }, "node_modules/@babel/helper-module-imports": { - "version": "7.14.5", - "resolved": "https://registry.npmjs.org/@babel/helper-module-imports/-/helper-module-imports-7.14.5.tgz", - "integrity": "sha512-SwrNHu5QWS84XlHwGYPDtCxcA0hrSlL2yhWYLgeOc0w7ccOl2qv4s/nARI0aYZW+bSwAL5CukeXA47B/1NKcnQ==", + "version": "7.15.4", + "resolved": "https://registry.npmjs.org/@babel/helper-module-imports/-/helper-module-imports-7.15.4.tgz", + "integrity": "sha512-jeAHZbzUwdW/xHgHQ3QmWR4Jg6j15q4w/gCfwZvtqOxoo5DKtLHk8Bsf4c5RZRC7NmLEs+ohkdq8jFefuvIxAA==", "dev": true, "dependencies": { - "@babel/types": "^7.14.5" + "@babel/types": "^7.15.4" }, "engines": { "node": ">=6.9.0" } }, "node_modules/@babel/helper-module-transforms": { - "version": "7.15.0", - "resolved": "https://registry.npmjs.org/@babel/helper-module-transforms/-/helper-module-transforms-7.15.0.tgz", - "integrity": "sha512-RkGiW5Rer7fpXv9m1B3iHIFDZdItnO2/BLfWVW/9q7+KqQSDY5kUfQEbzdXM1MVhJGcugKV7kRrNVzNxmk7NBg==", + "version": "7.15.7", + "resolved": "https://registry.npmjs.org/@babel/helper-module-transforms/-/helper-module-transforms-7.15.7.tgz", + "integrity": "sha512-ZNqjjQG/AuFfekFTY+7nY4RgBSklgTu970c7Rj3m/JOhIu5KPBUuTA9AY6zaKcUvk4g6EbDXdBnhi35FAssdSw==", "dev": true, "dependencies": { - "@babel/helper-module-imports": "^7.14.5", - "@babel/helper-replace-supers": "^7.15.0", - "@babel/helper-simple-access": "^7.14.8", - "@babel/helper-split-export-declaration": "^7.14.5", - "@babel/helper-validator-identifier": "^7.14.9", - "@babel/template": "^7.14.5", - "@babel/traverse": "^7.15.0", - "@babel/types": "^7.15.0" + "@babel/helper-module-imports": "^7.15.4", + "@babel/helper-replace-supers": "^7.15.4", + "@babel/helper-simple-access": "^7.15.4", + "@babel/helper-split-export-declaration": "^7.15.4", + "@babel/helper-validator-identifier": "^7.15.7", + "@babel/template": "^7.15.4", + "@babel/traverse": "^7.15.4", + "@babel/types": "^7.15.6" }, "engines": { "node": ">=6.9.0" } }, "node_modules/@babel/helper-optimise-call-expression": { - "version": "7.14.5", - "resolved": "https://registry.npmjs.org/@babel/helper-optimise-call-expression/-/helper-optimise-call-expression-7.14.5.tgz", - "integrity": "sha512-IqiLIrODUOdnPU9/F8ib1Fx2ohlgDhxnIDU7OEVi+kAbEZcyiF7BLU8W6PfvPi9LzztjS7kcbzbmL7oG8kD6VA==", + "version": "7.15.4", + "resolved": "https://registry.npmjs.org/@babel/helper-optimise-call-expression/-/helper-optimise-call-expression-7.15.4.tgz", + "integrity": "sha512-E/z9rfbAOt1vDW1DR7k4SzhzotVV5+qMciWV6LaG1g4jeFrkDlJedjtV4h0i4Q/ITnUu+Pk08M7fczsB9GXBDw==", "dev": true, "dependencies": { - "@babel/types": "^7.14.5" + "@babel/types": "^7.15.4" }, "engines": { "node": ">=6.9.0" } }, "node_modules/@babel/helper-replace-supers": { - "version": "7.15.0", - "resolved": "https://registry.npmjs.org/@babel/helper-replace-supers/-/helper-replace-supers-7.15.0.tgz", - "integrity": "sha512-6O+eWrhx+HEra/uJnifCwhwMd6Bp5+ZfZeJwbqUTuqkhIT6YcRhiZCOOFChRypOIe0cV46kFrRBlm+t5vHCEaA==", + "version": "7.15.4", + "resolved": "https://registry.npmjs.org/@babel/helper-replace-supers/-/helper-replace-supers-7.15.4.tgz", + "integrity": "sha512-/ztT6khaXF37MS47fufrKvIsiQkx1LBRvSJNzRqmbyeZnTwU9qBxXYLaaT/6KaxfKhjs2Wy8kG8ZdsFUuWBjzw==", "dev": true, "dependencies": { - "@babel/helper-member-expression-to-functions": "^7.15.0", - "@babel/helper-optimise-call-expression": "^7.14.5", - "@babel/traverse": "^7.15.0", - "@babel/types": "^7.15.0" + "@babel/helper-member-expression-to-functions": "^7.15.4", + "@babel/helper-optimise-call-expression": "^7.15.4", + "@babel/traverse": "^7.15.4", + "@babel/types": "^7.15.4" }, "engines": { "node": ">=6.9.0" } }, "node_modules/@babel/helper-simple-access": { - "version": "7.14.8", - "resolved": "https://registry.npmjs.org/@babel/helper-simple-access/-/helper-simple-access-7.14.8.tgz", - "integrity": "sha512-TrFN4RHh9gnWEU+s7JloIho2T76GPwRHhdzOWLqTrMnlas8T9O7ec+oEDNsRXndOmru9ymH9DFrEOxpzPoSbdg==", + "version": "7.15.4", + "resolved": "https://registry.npmjs.org/@babel/helper-simple-access/-/helper-simple-access-7.15.4.tgz", + "integrity": "sha512-UzazrDoIVOZZcTeHHEPYrr1MvTR/K+wgLg6MY6e1CJyaRhbibftF6fR2KU2sFRtI/nERUZR9fBd6aKgBlIBaPg==", "dev": true, "dependencies": { - "@babel/types": "^7.14.8" + "@babel/types": "^7.15.4" }, "engines": { "node": ">=6.9.0" } }, "node_modules/@babel/helper-split-export-declaration": { - "version": "7.14.5", - "resolved": "https://registry.npmjs.org/@babel/helper-split-export-declaration/-/helper-split-export-declaration-7.14.5.tgz", - "integrity": "sha512-hprxVPu6e5Kdp2puZUmvOGjaLv9TCe58E/Fl6hRq4YiVQxIcNvuq6uTM2r1mT/oPskuS9CgR+I94sqAYv0NGKA==", + "version": "7.15.4", + "resolved": "https://registry.npmjs.org/@babel/helper-split-export-declaration/-/helper-split-export-declaration-7.15.4.tgz", + "integrity": "sha512-HsFqhLDZ08DxCpBdEVtKmywj6PQbwnF6HHybur0MAnkAKnlS6uHkwnmRIkElB2Owpfb4xL4NwDmDLFubueDXsw==", "dev": true, "dependencies": { - "@babel/types": "^7.14.5" + "@babel/types": "^7.15.4" }, "engines": { "node": ">=6.9.0" } }, "node_modules/@babel/helper-validator-identifier": { - "version": "7.14.9", - "resolved": "https://registry.npmjs.org/@babel/helper-validator-identifier/-/helper-validator-identifier-7.14.9.tgz", - "integrity": "sha512-pQYxPY0UP6IHISRitNe8bsijHex4TWZXi2HwKVsjPiltzlhse2znVcm9Ace510VT1kxIHjGJCZZQBX2gJDbo0g==", + "version": "7.15.7", + "resolved": "https://registry.npmjs.org/@babel/helper-validator-identifier/-/helper-validator-identifier-7.15.7.tgz", + "integrity": "sha512-K4JvCtQqad9OY2+yTU8w+E82ywk/fe+ELNlt1G8z3bVGlZfn/hOcQQsUhGhW/N+tb3fxK800wLtKOE/aM0m72w==", "dev": true, "engines": { "node": ">=6.9.0" @@ -271,14 +271,14 @@ } }, "node_modules/@babel/helpers": { - "version": "7.15.3", - "resolved": "https://registry.npmjs.org/@babel/helpers/-/helpers-7.15.3.tgz", - "integrity": "sha512-HwJiz52XaS96lX+28Tnbu31VeFSQJGOeKHJeaEPQlTl7PnlhFElWPj8tUXtqFIzeN86XxXoBr+WFAyK2PPVz6g==", + "version": "7.15.4", + "resolved": "https://registry.npmjs.org/@babel/helpers/-/helpers-7.15.4.tgz", + "integrity": "sha512-V45u6dqEJ3w2rlryYYXf6i9rQ5YMNu4FLS6ngs8ikblhu2VdR1AqAd6aJjBzmf2Qzh6KOLqKHxEN9+TFbAkAVQ==", "dev": true, "dependencies": { - "@babel/template": "^7.14.5", - "@babel/traverse": "^7.15.0", - "@babel/types": "^7.15.0" + "@babel/template": "^7.15.4", + "@babel/traverse": "^7.15.4", + "@babel/types": "^7.15.4" }, "engines": { "node": ">=6.9.0" @@ -370,9 +370,9 @@ } }, "node_modules/@babel/parser": { - "version": "7.15.3", - "resolved": "https://registry.npmjs.org/@babel/parser/-/parser-7.15.3.tgz", - "integrity": "sha512-O0L6v/HvqbdJawj0iBEfVQMc3/6WP+AeOsovsIgBFyJaG+W2w7eqvZB7puddATmWuARlm1SX7DwxJ/JJUnDpEA==", + "version": "7.15.7", + "resolved": "https://registry.npmjs.org/@babel/parser/-/parser-7.15.7.tgz", + "integrity": "sha512-rycZXvQ+xS9QyIcJ9HXeDWf1uxqlbVFAUq0Rq0dbc50Zb/+wUe/ehyfzGfm9KZZF0kBejYgxltBXocP+gKdL2g==", "dev": true, "bin": { "parser": "bin/babel-parser.js" @@ -382,32 +382,32 @@ } }, "node_modules/@babel/template": { - "version": "7.14.5", - "resolved": "https://registry.npmjs.org/@babel/template/-/template-7.14.5.tgz", - "integrity": "sha512-6Z3Po85sfxRGachLULUhOmvAaOo7xCvqGQtxINai2mEGPFm6pQ4z5QInFnUrRpfoSV60BnjyF5F3c+15fxFV1g==", + "version": "7.15.4", + "resolved": "https://registry.npmjs.org/@babel/template/-/template-7.15.4.tgz", + "integrity": "sha512-UgBAfEa1oGuYgDIPM2G+aHa4Nlo9Lh6mGD2bDBGMTbYnc38vulXPuC1MGjYILIEmlwl6Rd+BPR9ee3gm20CBtg==", "dev": true, "dependencies": { "@babel/code-frame": "^7.14.5", - "@babel/parser": "^7.14.5", - "@babel/types": "^7.14.5" + "@babel/parser": "^7.15.4", + "@babel/types": "^7.15.4" }, "engines": { "node": ">=6.9.0" } }, "node_modules/@babel/traverse": { - "version": "7.15.0", - "resolved": "https://registry.npmjs.org/@babel/traverse/-/traverse-7.15.0.tgz", - "integrity": "sha512-392d8BN0C9eVxVWd8H6x9WfipgVH5IaIoLp23334Sc1vbKKWINnvwRpb4us0xtPaCumlwbTtIYNA0Dv/32sVFw==", + "version": "7.15.4", + "resolved": "https://registry.npmjs.org/@babel/traverse/-/traverse-7.15.4.tgz", + "integrity": "sha512-W6lQD8l4rUbQR/vYgSuCAE75ADyyQvOpFVsvPPdkhf6lATXAsQIG9YdtOcu8BB1dZ0LKu+Zo3c1wEcbKeuhdlA==", "dev": true, "dependencies": { "@babel/code-frame": "^7.14.5", - "@babel/generator": "^7.15.0", - "@babel/helper-function-name": "^7.14.5", - "@babel/helper-hoist-variables": "^7.14.5", - "@babel/helper-split-export-declaration": "^7.14.5", - "@babel/parser": "^7.15.0", - "@babel/types": "^7.15.0", + "@babel/generator": "^7.15.4", + "@babel/helper-function-name": "^7.15.4", + "@babel/helper-hoist-variables": "^7.15.4", + "@babel/helper-split-export-declaration": "^7.15.4", + "@babel/parser": "^7.15.4", + "@babel/types": "^7.15.4", "debug": "^4.1.0", "globals": "^11.1.0" }, @@ -416,9 +416,9 @@ } }, "node_modules/@babel/types": { - "version": "7.15.0", - "resolved": "https://registry.npmjs.org/@babel/types/-/types-7.15.0.tgz", - "integrity": "sha512-OBvfqnllOIdX4ojTHpwZbpvz4j3EWyjkZEdmjH0/cgsd6QOdSgU8rLSk6ard/pcW7rlmjdVSX/AWOaORR1uNOQ==", + "version": "7.15.6", + "resolved": "https://registry.npmjs.org/@babel/types/-/types-7.15.6.tgz", + "integrity": "sha512-BPU+7QhqNjmWyDO0/vitH/CuhpV8ZmK1wpKva8nuyNF5MJfuRNWMc+hc14+u9xT93kvykMdncrJT19h74uB1Ig==", "dev": true, "dependencies": { "@babel/helper-validator-identifier": "^7.14.9", @@ -607,9 +607,9 @@ } }, "node_modules/@octokit/auth-token": { - "version": "2.4.5", - "resolved": "https://registry.npmjs.org/@octokit/auth-token/-/auth-token-2.4.5.tgz", - "integrity": "sha512-BpGYsPgJt05M7/L/5FoE1PiAbdxXFZkX/3kDYcsvd1v6UhlnE5e96dTDr0ezX/EFwciQxf3cNV0loipsURU+WA==", + "version": "2.5.0", + "resolved": "https://registry.npmjs.org/@octokit/auth-token/-/auth-token-2.5.0.tgz", + "integrity": "sha512-r5FVUJCOLl19AxiuZD2VRZ/ORjp/4IN98Of6YJoJOkY75CIBuYfmiNHGrDwXr+aLGG55igl9QrxX3hbiXlLb+g==", "dev": true, "dependencies": { "@octokit/types": "^6.0.3" @@ -680,12 +680,12 @@ } }, "node_modules/@octokit/plugin-rest-endpoint-methods": { - "version": "5.7.0", - "resolved": "https://registry.npmjs.org/@octokit/plugin-rest-endpoint-methods/-/plugin-rest-endpoint-methods-5.7.0.tgz", - "integrity": "sha512-G7sgccWRYQMwcHJXkDY/sDxbXeKiZkFQqUtzBCwmrzCNj2GQf3VygQ4T/BFL2crLVpIbenkE/c0ErhYOte2MPw==", + "version": "5.10.4", + "resolved": "https://registry.npmjs.org/@octokit/plugin-rest-endpoint-methods/-/plugin-rest-endpoint-methods-5.10.4.tgz", + "integrity": "sha512-Dh+EAMCYR9RUHwQChH94Skl0lM8Fh99auT8ggck/xTzjJrwVzvsd0YH68oRPqp/HxICzmUjLfaQ9sy1o1sfIiA==", "dev": true, "dependencies": { - "@octokit/types": "^6.24.0", + "@octokit/types": "^6.28.1", "deprecation": "^2.3.1" }, "peerDependencies": { @@ -718,15 +718,15 @@ } }, "node_modules/@octokit/rest": { - "version": "18.9.0", - "resolved": "https://registry.npmjs.org/@octokit/rest/-/rest-18.9.0.tgz", - "integrity": "sha512-VrmrE8gjpuOoDAGjrQq2j9ZhOE6LxaqxaQg0yMrrEnnQZy2ZcAnr5qbVfKsMF0up/48PRV/VFS/2GSMhA7nTdA==", + "version": "18.10.0", + "resolved": "https://registry.npmjs.org/@octokit/rest/-/rest-18.10.0.tgz", + "integrity": "sha512-esHR5OKy38bccL/sajHqZudZCvmv4yjovMJzyXlphaUo7xykmtOdILGJ3aAm0mFHmMLmPFmDMJXf39cAjNJsrw==", "dev": true, "dependencies": { - "@octokit/core": "^3.5.0", - "@octokit/plugin-paginate-rest": "^2.6.2", - "@octokit/plugin-request-log": "^1.0.2", - "@octokit/plugin-rest-endpoint-methods": "5.7.0" + "@octokit/core": "^3.5.1", + "@octokit/plugin-paginate-rest": "^2.16.0", + "@octokit/plugin-request-log": "^1.0.4", + "@octokit/plugin-rest-endpoint-methods": "^5.9.0" } }, "node_modules/@octokit/types": { @@ -739,9 +739,9 @@ } }, "node_modules/@sindresorhus/is": { - "version": "4.0.1", - "resolved": "https://registry.npmjs.org/@sindresorhus/is/-/is-4.0.1.tgz", - "integrity": "sha512-Qm9hBEBu18wt1PO2flE7LPb30BHMQt1eQgbV76YntdNk73XZGpn3izvGTYxbGgzXKgbCjiia0uxTd3aTNQrY/g==", + "version": "4.2.0", + "resolved": "https://registry.npmjs.org/@sindresorhus/is/-/is-4.2.0.tgz", + "integrity": "sha512-VkE3KLBmJwcCaVARtQpfuKcKv8gcBmUubrfHGF84dXuuW6jgsRYxPtzcIhPyK9WAPpRt2/xY6zkD9MnRaJzSyw==", "dev": true, "engines": { "node": ">=10" @@ -840,9 +840,9 @@ "dev": true }, "node_modules/@types/keyv": { - "version": "3.1.2", - "resolved": "https://registry.npmjs.org/@types/keyv/-/keyv-3.1.2.tgz", - "integrity": "sha512-/FvAK2p4jQOaJ6CGDHJTqZcUtbZe820qIeTg7o0Shg7drB4JHeL+V/dhSaly7NXx6u8eSee+r7coT+yuJEvDLg==", + "version": "3.1.3", + "resolved": "https://registry.npmjs.org/@types/keyv/-/keyv-3.1.3.tgz", + "integrity": "sha512-FXCJgyyN3ivVgRoml4h94G/p3kY+u/B86La+QptcqJaWtBWtmc6TtkNfS40n9bIvyLteHh7zXOtgbobORKPbDg==", "dev": true, "dependencies": { "@types/node": "*" @@ -876,9 +876,9 @@ } }, "node_modules/@types/sinon": { - "version": "10.0.2", - "resolved": "https://registry.npmjs.org/@types/sinon/-/sinon-10.0.2.tgz", - "integrity": "sha512-BHn8Bpkapj8Wdfxvh2jWIUoaYB/9/XhsL0oOvBfRagJtKlSl9NWPcFOz2lRukI9szwGxFtYZCTejJSqsGDbdmw==", + "version": "10.0.3", + "resolved": "https://registry.npmjs.org/@types/sinon/-/sinon-10.0.3.tgz", + "integrity": "sha512-XUaFuUOQ3A/r6gS1qCU/USMleascaqGeQpGR1AZ5JdRtBPlzijRzKsik1TuGzvdtPA0mdq42JqaJmJ+Afg1LJg==", "dev": true, "dependencies": { "@sinonjs/fake-timers": "^7.1.0" @@ -903,9 +903,9 @@ "dev": true }, "node_modules/acorn": { - "version": "8.4.1", - "resolved": "https://registry.npmjs.org/acorn/-/acorn-8.4.1.tgz", - "integrity": "sha512-asabaBSkEKosYKMITunzX177CXxQ4Q8BSSzMTKD+FefUhipQC70gfW5SiUDhYQ3vk8G+81HqQk7Fv9OXwwn9KA==", + "version": "8.5.0", + "resolved": "https://registry.npmjs.org/acorn/-/acorn-8.5.0.tgz", + "integrity": "sha512-yXbYeFy+jUuYd3/CDcg2NkIYE991XYX/bje7LmjJigUciaeO1JR4XxXgCIV1/Zc/dRuFEyw1L0pbA+qynJkW5Q==", "dev": true, "bin": { "acorn": "bin/acorn" @@ -915,9 +915,9 @@ } }, "node_modules/acorn-walk": { - "version": "8.1.1", - "resolved": "https://registry.npmjs.org/acorn-walk/-/acorn-walk-8.1.1.tgz", - "integrity": "sha512-FbJdceMlPHEAWJOILDk1fXD8lnTlEIWFkqtfk+MvmL5q/qlHfN7GEHcsFZWt/Tea9jRNPWUZG4G976nqAAmU9w==", + "version": "8.2.0", + "resolved": "https://registry.npmjs.org/acorn-walk/-/acorn-walk-8.2.0.tgz", + "integrity": "sha512-k+iyHEuPgSw6SbuDpGQM+06HQUa04DZ3o+F6CSzXMvvI5KMvnaEqXe+YVe555R9nn6GPt404fos4wcgpw12SDA==", "dev": true, "engines": { "node": ">=0.4.0" @@ -1032,9 +1032,9 @@ } }, "node_modules/ansi-regex": { - "version": "5.0.0", - "resolved": "https://registry.npmjs.org/ansi-regex/-/ansi-regex-5.0.0.tgz", - "integrity": "sha512-bY6fj56OUQ0hU1KjFNDQuJFezqKdrAyFdIevADiqrWHwSlbmBNMHp5ak2f40Pm8JTFyM2mqxkG6ngkHO11f/lg==", + "version": "5.0.1", + "resolved": "https://registry.npmjs.org/ansi-regex/-/ansi-regex-5.0.1.tgz", + "integrity": "sha512-quJQXlTSUGL2LH9SUXo8VwsY4soanhgo6LNSm84E1LBcE8s3O0wpdiRzyR9z/ZZJMlMWv37qOOb9pdJlMUEKFQ==", "dev": true, "engines": { "node": ">=8" @@ -1108,12 +1108,12 @@ } }, "node_modules/async-retry": { - "version": "1.3.1", - "resolved": "https://registry.npmjs.org/async-retry/-/async-retry-1.3.1.tgz", - "integrity": "sha512-aiieFW/7h3hY0Bq5d+ktDBejxuwR78vRu9hDUdR8rNhSaQ29VzPL4AoIRG7D/c7tdenwOcKvgPM6tIxB3cB6HA==", + "version": "1.3.3", + "resolved": "https://registry.npmjs.org/async-retry/-/async-retry-1.3.3.tgz", + "integrity": "sha512-wfr/jstw9xNi/0teMHrRW7dsz3Lt5ARhYNZ2ewpadnhaIp5mbALhOAP+EAdsC7t4Z6wqsDVv9+W6gm1Dk9mEyw==", "dev": true, "dependencies": { - "retry": "0.12.0" + "retry": "0.13.1" } }, "node_modules/asynckit": { @@ -1175,16 +1175,16 @@ } }, "node_modules/boxen": { - "version": "5.0.1", - "resolved": "https://registry.npmjs.org/boxen/-/boxen-5.0.1.tgz", - "integrity": "sha512-49VBlw+PrWEF51aCmy7QIteYPIFZxSpvqBdP/2itCPPlJ49kj9zg/XPRFrdkne2W+CfwXUls8exMvu1RysZpKA==", + "version": "5.1.2", + "resolved": "https://registry.npmjs.org/boxen/-/boxen-5.1.2.tgz", + "integrity": "sha512-9gYgQKXx+1nP8mP7CzFyaUARhg7D3n1dF/FnErWmu9l6JvGpNUN278h0aSb+QjoiKSWG+iZ3uHrcqk0qrY9RQQ==", "dev": true, "dependencies": { "ansi-align": "^3.0.0", "camelcase": "^6.2.0", "chalk": "^4.1.0", "cli-boxes": "^2.2.1", - "string-width": "^4.2.0", + "string-width": "^4.2.2", "type-fest": "^0.20.2", "widest-line": "^3.1.0", "wrap-ansi": "^7.0.0" @@ -1249,14 +1249,14 @@ "dev": true }, "node_modules/browserslist": { - "version": "4.16.8", - "resolved": "https://registry.npmjs.org/browserslist/-/browserslist-4.16.8.tgz", - "integrity": "sha512-sc2m9ohR/49sWEbPj14ZSSZqp+kbi16aLao42Hmn3Z8FpjuMaq2xCA2l4zl9ITfyzvnvyE0hcg62YkIGKxgaNQ==", + "version": "4.17.0", + "resolved": "https://registry.npmjs.org/browserslist/-/browserslist-4.17.0.tgz", + "integrity": "sha512-g2BJ2a0nEYvEFQC208q8mVAhfNwpZ5Mu8BwgtCdZKO3qx98HChmeg448fPdUzld8aFmfLgVh7yymqV+q1lJZ5g==", "dev": true, "dependencies": { - "caniuse-lite": "^1.0.30001251", + "caniuse-lite": "^1.0.30001254", "colorette": "^1.3.0", - "electron-to-chromium": "^1.3.811", + "electron-to-chromium": "^1.3.830", "escalade": "^3.1.1", "node-releases": "^1.1.75" }, @@ -1390,9 +1390,9 @@ } }, "node_modules/caniuse-lite": { - "version": "1.0.30001252", - "resolved": "https://registry.npmjs.org/caniuse-lite/-/caniuse-lite-1.0.30001252.tgz", - "integrity": "sha512-I56jhWDGMtdILQORdusxBOH+Nl/KgQSdDmpJezYddnAkVOmnoU8zwjTV9xAjMIYxr0iPreEAVylCGcmHCjfaOw==", + "version": "1.0.30001259", + "resolved": "https://registry.npmjs.org/caniuse-lite/-/caniuse-lite-1.0.30001259.tgz", + "integrity": "sha512-V7mQTFhjITxuk9zBpI6nYsiTXhcPe05l+364nZjK7MFK/E7ibvYBSAXr4YcA6oPR8j3ZLM/LN+lUqUVAQEUZFg==", "dev": true, "funding": { "type": "opencollective", @@ -1570,9 +1570,9 @@ "dev": true }, "node_modules/colorette": { - "version": "1.3.0", - "resolved": "https://registry.npmjs.org/colorette/-/colorette-1.3.0.tgz", - "integrity": "sha512-ecORCqbSFP7Wm8Y6lyqMJjexBQqXSF7SSeaTyGGphogUjBlFP9m9o08wy86HL2uB7fMTxtOUzLMk7ogKcxMg1w==", + "version": "1.4.0", + "resolved": "https://registry.npmjs.org/colorette/-/colorette-1.4.0.tgz", + "integrity": "sha512-Y2oEozpomLn7Q3HFP7dpww7AtMJplbM9lGZP6RDfHqmbeRjiwRg4n6VM6j4KLmRke85uWEI7JqF17f3pqdRA0g==", "dev": true }, "node_modules/combined-stream": { @@ -1626,9 +1626,9 @@ } }, "node_modules/cosmiconfig": { - "version": "7.0.0", - "resolved": "https://registry.npmjs.org/cosmiconfig/-/cosmiconfig-7.0.0.tgz", - "integrity": "sha512-pondGvTuVYDk++upghXJabWzL6Kxu6f26ljFw64Swq9v6sQPUL3EUlVDV56diOjpCayKihL6hVe8exIACU4XcA==", + "version": "7.0.1", + "resolved": "https://registry.npmjs.org/cosmiconfig/-/cosmiconfig-7.0.1.tgz", + "integrity": "sha512-a1YWNUV2HwGimB7dU2s1wUMurNKjpx60HxBB6xUM8Re+2s1g1IIfJvFR0/iCF+XHdE0GMTKTuLR32UQff4TEyQ==", "dev": true, "dependencies": { "@types/parse-json": "^4.0.0", @@ -1977,9 +1977,9 @@ } }, "node_modules/fastq": { - "version": "1.12.0", - "resolved": "https://registry.npmjs.org/fastq/-/fastq-1.12.0.tgz", - "integrity": "sha512-VNX0QkHK3RsXVKr9KrlUv/FoTa0NdbYoHHl7uXHv2rzyHSlxjdNAKug2twd9luJxpcyNeAgf5iPPMutJO67Dfg==", + "version": "1.13.0", + "resolved": "https://registry.npmjs.org/fastq/-/fastq-1.13.0.tgz", + "integrity": "sha512-YpkpUnK8od0o1hmeSc7UUs/eB/vIPWJYjKck2QKIzAf71Vm1AAQ3EbuZB3g2JIy+pg+ERD0vqI79KyZiB2e2Nw==", "dev": true, "dependencies": { "reusify": "^1.0.4" @@ -2217,9 +2217,9 @@ } }, "node_modules/git-url-parse": { - "version": "11.5.0", - "resolved": "https://registry.npmjs.org/git-url-parse/-/git-url-parse-11.5.0.tgz", - "integrity": "sha512-TZYSMDeM37r71Lqg1mbnMlOqlHd7BSij9qN7XwTkRqSAYFMihGLGhfHwgqQob3GUhEneKnV4nskN9rbQw2KGxA==", + "version": "11.6.0", + "resolved": "https://registry.npmjs.org/git-url-parse/-/git-url-parse-11.6.0.tgz", + "integrity": "sha512-WWUxvJs5HsyHL6L08wOusa/IXYtMuCAhrMmnTjQPpBU0TTHyDhnOATNH3xNQz7YOQUsqIIPTGr4xiVti1Hsk5g==", "dev": true, "dependencies": { "git-up": "^4.0.0" @@ -2615,9 +2615,9 @@ } }, "node_modules/inquirer": { - "version": "8.1.2", - "resolved": "https://registry.npmjs.org/inquirer/-/inquirer-8.1.2.tgz", - "integrity": "sha512-DHLKJwLPNgkfwNmsuEUKSejJFbkv0FMO9SMiQbjI3n5NQuCrSIBqP66ggqyz2a6t2qEolKrMjhQ3+W/xXgUQ+Q==", + "version": "8.1.5", + "resolved": "https://registry.npmjs.org/inquirer/-/inquirer-8.1.5.tgz", + "integrity": "sha512-G6/9xUqmt/r+UvufSyrPpt84NYwhKZ9jLsgMbQzlx804XErNupor8WQdBnBRrXmBfTPpuwf1sV+ss2ovjgdXIg==", "dev": true, "dependencies": { "ansi-escapes": "^4.2.1", @@ -2628,7 +2628,7 @@ "figures": "^3.0.0", "lodash": "^4.17.21", "mute-stream": "0.0.8", - "ora": "^5.3.0", + "ora": "^5.4.1", "run-async": "^2.4.0", "rxjs": "^7.2.0", "string-width": "^4.1.0", @@ -3068,6 +3068,12 @@ "node": ">=6" } }, + "node_modules/jsonc-parser": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/jsonc-parser/-/jsonc-parser-3.0.0.tgz", + "integrity": "sha512-fQzRfAbIBnR0IQvftw9FJveWiHp72Fg20giDrHz6TdfB12UH/uue0D3hm57UB5KgAVuniLMCaS8P1IMj9NR7cA==", + "dev": true + }, "node_modules/just-extend": { "version": "4.2.1", "resolved": "https://registry.npmjs.org/just-extend/-/just-extend-4.2.1.tgz", @@ -3214,9 +3220,9 @@ "dev": true }, "node_modules/marked": { - "version": "3.0.2", - "resolved": "https://registry.npmjs.org/marked/-/marked-3.0.2.tgz", - "integrity": "sha512-TMJQQ79Z0e3rJYazY0tIoMsFzteUGw9fB3FD+gzuIT3zLuG9L9ckIvUfF51apdJkcqc208jJN2KbtPbOvXtbjA==", + "version": "3.0.4", + "resolved": "https://registry.npmjs.org/marked/-/marked-3.0.4.tgz", + "integrity": "sha512-jBo8AOayNaEcvBhNobg6/BLhdsK3NvnKWJg33MAAPbvTWiG4QBn9gpW1+7RssrKu4K1dKlN+0goVQwV41xEfOA==", "dev": true, "bin": { "marked": "bin/marked" @@ -3419,10 +3425,13 @@ } }, "node_modules/node-fetch": { - "version": "2.6.1", - "resolved": "https://registry.npmjs.org/node-fetch/-/node-fetch-2.6.1.tgz", - "integrity": "sha512-V4aYg89jEoVRxRb2fJdAg8FHvI7cEyYdVAh94HH0UIK8oJxUfkjlDQN9RbMx+bEjP7+ggMiFRprSti032Oipxw==", + "version": "2.6.4", + "resolved": "https://registry.npmjs.org/node-fetch/-/node-fetch-2.6.4.tgz", + "integrity": "sha512-aD1fO+xtLiSCc9vuD+sYMxpIuQyhHscGSkBEo2o5LTV/3bTEAYvdUii29n8LlO5uLCmWdGP7uVUVXFo5SRdkLA==", "dev": true, + "dependencies": { + "whatwg-url": "^5.0.0" + }, "engines": { "node": "4.x || >=6.0.0" } @@ -3440,9 +3449,9 @@ } }, "node_modules/node-releases": { - "version": "1.1.75", - "resolved": "https://registry.npmjs.org/node-releases/-/node-releases-1.1.75.tgz", - "integrity": "sha512-Qe5OUajvqrqDSy6wrWFmMwfJ0jVgwiw4T3KqmbTcZ62qW0gQkheXYhcFM1+lOVcGUoRxcEcfyvFMAnDgaF1VWw==", + "version": "1.1.76", + "resolved": "https://registry.npmjs.org/node-releases/-/node-releases-1.1.76.tgz", + "integrity": "sha512-9/IECtNr8dXNmPWmFXepT0/7o5eolGesHUa3mtr0KlgnCvnZxwh2qensKL42JJY2vQKC3nIBXetFAqR+PW1CmA==", "dev": true }, "node_modules/normalize-path": { @@ -4424,25 +4433,25 @@ } }, "node_modules/release-it": { - "version": "14.11.5", - "resolved": "https://registry.npmjs.org/release-it/-/release-it-14.11.5.tgz", - "integrity": "sha512-9BaPdq7ZKOwtzz3p1mRhg/tOH/cT/y2tUnPYzUwQiVdj42JaGI1Vo2l3WbgK8ICsbFyrhc0tri1+iqI8OvkI1A==", + "version": "14.11.6", + "resolved": "https://registry.npmjs.org/release-it/-/release-it-14.11.6.tgz", + "integrity": "sha512-6BNcuzFZHThBUBJ/xYw/bxZ+58CAwrwf1zgmjq2Ibl3nlDZbjphHG6iqxkJu7mZ8TIWs6NjloEAhqpjeXoN//Q==", "dev": true, "dependencies": { "@iarna/toml": "2.2.5", - "@octokit/rest": "18.9.0", - "async-retry": "1.3.1", + "@octokit/rest": "18.10.0", + "async-retry": "1.3.3", "chalk": "4.1.2", - "cosmiconfig": "7.0.0", + "cosmiconfig": "7.0.1", "debug": "4.3.2", "deprecated-obj": "2.0.0", "execa": "5.1.1", "form-data": "4.0.0", - "git-url-parse": "11.5.0", + "git-url-parse": "11.6.0", "globby": "11.0.4", "got": "11.8.2", "import-cwd": "3.0.0", - "inquirer": "8.1.2", + "inquirer": "8.1.5", "is-ci": "3.0.0", "lodash": "4.17.21", "mime-types": "2.1.32", @@ -4612,9 +4621,9 @@ } }, "node_modules/retry": { - "version": "0.12.0", - "resolved": "https://registry.npmjs.org/retry/-/retry-0.12.0.tgz", - "integrity": "sha1-G0KmJmoh8HQh0bC1S33BZ7AcATs=", + "version": "0.13.1", + "resolved": "https://registry.npmjs.org/retry/-/retry-0.13.1.tgz", + "integrity": "sha512-XQBQ3I8W1Cge0Seh+6gjj03LbmRFWuoszgK9ooCpwYIrhhoO80pfq4cUkU5DkknwfOfFteRwlZ56PYOGYyFWdg==", "dev": true, "engines": { "node": ">= 4" @@ -4778,7 +4787,7 @@ "integrity": "sha512-xeM7Oc6hY+6iW5O/T5hor8ul7mEprzyl5y4r5zthEHToQNw7MIhREMgU3r2gKDB0NaMLNrkcEQagudCdzE13Lg==", "dev": true, "dependencies": { - "json5": "^2.2.0", + "jsonc-parser": "^3.0.0", "onigasm": "^2.2.5", "vscode-textmate": "5.2.0" } @@ -4798,9 +4807,9 @@ } }, "node_modules/signal-exit": { - "version": "3.0.3", - "resolved": "https://registry.npmjs.org/signal-exit/-/signal-exit-3.0.3.tgz", - "integrity": "sha512-VUJ49FC8U1OxwZLxIbTTrDvLnf/6TDgxZcK8wxR8zs13xpx7xbG60ndBlhNrFi2EMuFRoeDoJO7wthSLq42EjA==", + "version": "3.0.4", + "resolved": "https://registry.npmjs.org/signal-exit/-/signal-exit-3.0.4.tgz", + "integrity": "sha512-rqYhcAnZ6d/vTPGghdrw7iumdcbXpsk1b8IG/rz+VWV51DM0p7XCtMoJ3qhPLIbp3tvyt3pKRbaaEMZYpHto8Q==", "dev": true }, "node_modules/sinon": { @@ -4852,9 +4861,9 @@ } }, "node_modules/source-map-support": { - "version": "0.5.19", - "resolved": "https://registry.npmjs.org/source-map-support/-/source-map-support-0.5.19.tgz", - "integrity": "sha512-Wonm7zOCIJzBGQdB+thsPar0kYuCIzYvxZwlBa87yi/Mdjv7Tip2cyVbLj5o0cFPN4EVkuTwb3GDDyUx2DGnGw==", + "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==", "dev": true, "dependencies": { "buffer-from": "^1.0.0", @@ -5073,6 +5082,12 @@ "node": ">=8.0" } }, + "node_modules/tr46": { + "version": "0.0.3", + "resolved": "https://registry.npmjs.org/tr46/-/tr46-0.0.3.tgz", + "integrity": "sha1-gYT9NH2snNwYWZLzpmIuFLnZq2o=", + "dev": true + }, "node_modules/ts-node": { "version": "10.2.1", "resolved": "https://registry.npmjs.org/ts-node/-/ts-node-10.2.1.tgz", @@ -5216,9 +5231,9 @@ } }, "node_modules/typescript": { - "version": "4.4.2", - "resolved": "https://registry.npmjs.org/typescript/-/typescript-4.4.2.tgz", - "integrity": "sha512-gzP+t5W4hdy4c+68bfcv0t400HVJMMd2+H9B7gae1nQlBzCqvrXX+6GL/b3GAgyTH966pzrZ70/fRjwAtZksSQ==", + "version": "4.4.3", + "resolved": "https://registry.npmjs.org/typescript/-/typescript-4.4.3.tgz", + "integrity": "sha512-4xfscpisVgqqDfPaJo5vkd+Qd/ItkoagnHpufr+i2QCHBsNYp+G7UAoyFl8aPtx879u38wPV65rZ8qbGZijalA==", "dev": true, "bin": { "tsc": "bin/tsc", @@ -5229,9 +5244,9 @@ } }, "node_modules/uglify-js": { - "version": "3.14.1", - "resolved": "https://registry.npmjs.org/uglify-js/-/uglify-js-3.14.1.tgz", - "integrity": "sha512-JhS3hmcVaXlp/xSo3PKY5R0JqKs5M3IV+exdLHW99qKvKivPO4Z8qbej6mte17SOPqAOVMjt/XGgWacnFSzM3g==", + "version": "3.14.2", + "resolved": "https://registry.npmjs.org/uglify-js/-/uglify-js-3.14.2.tgz", + "integrity": "sha512-rtPMlmcO4agTUfz10CbgJ1k6UAoXM2gWb3GoMPPZB/+/Ackf8lNWk11K4rYi2D0apgoFRLtQOZhb+/iGNJq26A==", "dev": true, "optional": true, "bin": { @@ -5381,6 +5396,22 @@ "defaults": "^1.0.3" } }, + "node_modules/webidl-conversions": { + "version": "3.0.1", + "resolved": "https://registry.npmjs.org/webidl-conversions/-/webidl-conversions-3.0.1.tgz", + "integrity": "sha1-JFNCdeKnvGvnvIZhHMFq4KVlSHE=", + "dev": true + }, + "node_modules/whatwg-url": { + "version": "5.0.0", + "resolved": "https://registry.npmjs.org/whatwg-url/-/whatwg-url-5.0.0.tgz", + "integrity": "sha1-lmRU6HZUYuN2RNNib2dCzotwll0=", + "dev": true, + "dependencies": { + "tr46": "~0.0.3", + "webidl-conversions": "^3.0.0" + } + }, "node_modules/which": { "version": "2.0.2", "resolved": "https://registry.npmjs.org/which/-/which-2.0.2.tgz", @@ -5712,20 +5743,20 @@ "dev": true }, "@babel/core": { - "version": "7.15.0", - "resolved": "https://registry.npmjs.org/@babel/core/-/core-7.15.0.tgz", - "integrity": "sha512-tXtmTminrze5HEUPn/a0JtOzzfp0nk+UEXQ/tqIJo3WDGypl/2OFQEMll/zSFU8f/lfmfLXvTaORHF3cfXIQMw==", + "version": "7.15.5", + "resolved": "https://registry.npmjs.org/@babel/core/-/core-7.15.5.tgz", + "integrity": "sha512-pYgXxiwAgQpgM1bNkZsDEq85f0ggXMA5L7c+o3tskGMh2BunCI9QUwB9Z4jpvXUOuMdyGKiGKQiRe11VS6Jzvg==", "dev": true, "requires": { "@babel/code-frame": "^7.14.5", - "@babel/generator": "^7.15.0", - "@babel/helper-compilation-targets": "^7.15.0", - "@babel/helper-module-transforms": "^7.15.0", - "@babel/helpers": "^7.14.8", - "@babel/parser": "^7.15.0", - "@babel/template": "^7.14.5", - "@babel/traverse": "^7.15.0", - "@babel/types": "^7.15.0", + "@babel/generator": "^7.15.4", + "@babel/helper-compilation-targets": "^7.15.4", + "@babel/helper-module-transforms": "^7.15.4", + "@babel/helpers": "^7.15.4", + "@babel/parser": "^7.15.5", + "@babel/template": "^7.15.4", + "@babel/traverse": "^7.15.4", + "@babel/types": "^7.15.4", "convert-source-map": "^1.7.0", "debug": "^4.1.0", "gensync": "^1.0.0-beta.2", @@ -5735,20 +5766,20 @@ } }, "@babel/generator": { - "version": "7.15.0", - "resolved": "https://registry.npmjs.org/@babel/generator/-/generator-7.15.0.tgz", - "integrity": "sha512-eKl4XdMrbpYvuB505KTta4AV9g+wWzmVBW69tX0H2NwKVKd2YJbKgyK6M8j/rgLbmHOYJn6rUklV677nOyJrEQ==", + "version": "7.15.4", + "resolved": "https://registry.npmjs.org/@babel/generator/-/generator-7.15.4.tgz", + "integrity": "sha512-d3itta0tu+UayjEORPNz6e1T3FtvWlP5N4V5M+lhp/CxT4oAA7/NcScnpRyspUMLK6tu9MNHmQHxRykuN2R7hw==", "dev": true, "requires": { - "@babel/types": "^7.15.0", + "@babel/types": "^7.15.4", "jsesc": "^2.5.1", "source-map": "^0.5.0" } }, "@babel/helper-compilation-targets": { - "version": "7.15.0", - "resolved": "https://registry.npmjs.org/@babel/helper-compilation-targets/-/helper-compilation-targets-7.15.0.tgz", - "integrity": "sha512-h+/9t0ncd4jfZ8wsdAsoIxSa61qhBYlycXiHWqJaQBCXAhDCMbPRSMTGnZIkkmt1u4ag+UQmuqcILwqKzZ4N2A==", + "version": "7.15.4", + "resolved": "https://registry.npmjs.org/@babel/helper-compilation-targets/-/helper-compilation-targets-7.15.4.tgz", + "integrity": "sha512-rMWPCirulnPSe4d+gwdWXLfAXTTBj8M3guAf5xFQJ0nvFY7tfNAFnWdqaHegHlgDZOCT4qvhF3BYlSJag8yhqQ==", "dev": true, "requires": { "@babel/compat-data": "^7.15.0", @@ -5758,111 +5789,111 @@ } }, "@babel/helper-function-name": { - "version": "7.14.5", - "resolved": "https://registry.npmjs.org/@babel/helper-function-name/-/helper-function-name-7.14.5.tgz", - "integrity": "sha512-Gjna0AsXWfFvrAuX+VKcN/aNNWonizBj39yGwUzVDVTlMYJMK2Wp6xdpy72mfArFq5uK+NOuexfzZlzI1z9+AQ==", + "version": "7.15.4", + "resolved": "https://registry.npmjs.org/@babel/helper-function-name/-/helper-function-name-7.15.4.tgz", + "integrity": "sha512-Z91cOMM4DseLIGOnog+Z8OI6YseR9bua+HpvLAQ2XayUGU+neTtX+97caALaLdyu53I/fjhbeCnWnRH1O3jFOw==", "dev": true, "requires": { - "@babel/helper-get-function-arity": "^7.14.5", - "@babel/template": "^7.14.5", - "@babel/types": "^7.14.5" + "@babel/helper-get-function-arity": "^7.15.4", + "@babel/template": "^7.15.4", + "@babel/types": "^7.15.4" } }, "@babel/helper-get-function-arity": { - "version": "7.14.5", - "resolved": "https://registry.npmjs.org/@babel/helper-get-function-arity/-/helper-get-function-arity-7.14.5.tgz", - "integrity": "sha512-I1Db4Shst5lewOM4V+ZKJzQ0JGGaZ6VY1jYvMghRjqs6DWgxLCIyFt30GlnKkfUeFLpJt2vzbMVEXVSXlIFYUg==", + "version": "7.15.4", + "resolved": "https://registry.npmjs.org/@babel/helper-get-function-arity/-/helper-get-function-arity-7.15.4.tgz", + "integrity": "sha512-1/AlxSF92CmGZzHnC515hm4SirTxtpDnLEJ0UyEMgTMZN+6bxXKg04dKhiRx5Enel+SUA1G1t5Ed/yQia0efrA==", "dev": true, "requires": { - "@babel/types": "^7.14.5" + "@babel/types": "^7.15.4" } }, "@babel/helper-hoist-variables": { - "version": "7.14.5", - "resolved": "https://registry.npmjs.org/@babel/helper-hoist-variables/-/helper-hoist-variables-7.14.5.tgz", - "integrity": "sha512-R1PXiz31Uc0Vxy4OEOm07x0oSjKAdPPCh3tPivn/Eo8cvz6gveAeuyUUPB21Hoiif0uoPQSSdhIPS3352nvdyQ==", + "version": "7.15.4", + "resolved": "https://registry.npmjs.org/@babel/helper-hoist-variables/-/helper-hoist-variables-7.15.4.tgz", + "integrity": "sha512-VTy085egb3jUGVK9ycIxQiPbquesq0HUQ+tPO0uv5mPEBZipk+5FkRKiWq5apuyTE9FUrjENB0rCf8y+n+UuhA==", "dev": true, "requires": { - "@babel/types": "^7.14.5" + "@babel/types": "^7.15.4" } }, "@babel/helper-member-expression-to-functions": { - "version": "7.15.0", - "resolved": "https://registry.npmjs.org/@babel/helper-member-expression-to-functions/-/helper-member-expression-to-functions-7.15.0.tgz", - "integrity": "sha512-Jq8H8U2kYiafuj2xMTPQwkTBnEEdGKpT35lJEQsRRjnG0LW3neucsaMWLgKcwu3OHKNeYugfw+Z20BXBSEs2Lg==", + "version": "7.15.4", + "resolved": "https://registry.npmjs.org/@babel/helper-member-expression-to-functions/-/helper-member-expression-to-functions-7.15.4.tgz", + "integrity": "sha512-cokOMkxC/BTyNP1AlY25HuBWM32iCEsLPI4BHDpJCHHm1FU2E7dKWWIXJgQgSFiu4lp8q3bL1BIKwqkSUviqtA==", "dev": true, "requires": { - "@babel/types": "^7.15.0" + "@babel/types": "^7.15.4" } }, "@babel/helper-module-imports": { - "version": "7.14.5", - "resolved": "https://registry.npmjs.org/@babel/helper-module-imports/-/helper-module-imports-7.14.5.tgz", - "integrity": "sha512-SwrNHu5QWS84XlHwGYPDtCxcA0hrSlL2yhWYLgeOc0w7ccOl2qv4s/nARI0aYZW+bSwAL5CukeXA47B/1NKcnQ==", + "version": "7.15.4", + "resolved": "https://registry.npmjs.org/@babel/helper-module-imports/-/helper-module-imports-7.15.4.tgz", + "integrity": "sha512-jeAHZbzUwdW/xHgHQ3QmWR4Jg6j15q4w/gCfwZvtqOxoo5DKtLHk8Bsf4c5RZRC7NmLEs+ohkdq8jFefuvIxAA==", "dev": true, "requires": { - "@babel/types": "^7.14.5" + "@babel/types": "^7.15.4" } }, "@babel/helper-module-transforms": { - "version": "7.15.0", - "resolved": "https://registry.npmjs.org/@babel/helper-module-transforms/-/helper-module-transforms-7.15.0.tgz", - "integrity": "sha512-RkGiW5Rer7fpXv9m1B3iHIFDZdItnO2/BLfWVW/9q7+KqQSDY5kUfQEbzdXM1MVhJGcugKV7kRrNVzNxmk7NBg==", + "version": "7.15.7", + "resolved": "https://registry.npmjs.org/@babel/helper-module-transforms/-/helper-module-transforms-7.15.7.tgz", + "integrity": "sha512-ZNqjjQG/AuFfekFTY+7nY4RgBSklgTu970c7Rj3m/JOhIu5KPBUuTA9AY6zaKcUvk4g6EbDXdBnhi35FAssdSw==", "dev": true, "requires": { - "@babel/helper-module-imports": "^7.14.5", - "@babel/helper-replace-supers": "^7.15.0", - "@babel/helper-simple-access": "^7.14.8", - "@babel/helper-split-export-declaration": "^7.14.5", - "@babel/helper-validator-identifier": "^7.14.9", - "@babel/template": "^7.14.5", - "@babel/traverse": "^7.15.0", - "@babel/types": "^7.15.0" + "@babel/helper-module-imports": "^7.15.4", + "@babel/helper-replace-supers": "^7.15.4", + "@babel/helper-simple-access": "^7.15.4", + "@babel/helper-split-export-declaration": "^7.15.4", + "@babel/helper-validator-identifier": "^7.15.7", + "@babel/template": "^7.15.4", + "@babel/traverse": "^7.15.4", + "@babel/types": "^7.15.6" } }, "@babel/helper-optimise-call-expression": { - "version": "7.14.5", - "resolved": "https://registry.npmjs.org/@babel/helper-optimise-call-expression/-/helper-optimise-call-expression-7.14.5.tgz", - "integrity": "sha512-IqiLIrODUOdnPU9/F8ib1Fx2ohlgDhxnIDU7OEVi+kAbEZcyiF7BLU8W6PfvPi9LzztjS7kcbzbmL7oG8kD6VA==", + "version": "7.15.4", + "resolved": "https://registry.npmjs.org/@babel/helper-optimise-call-expression/-/helper-optimise-call-expression-7.15.4.tgz", + "integrity": "sha512-E/z9rfbAOt1vDW1DR7k4SzhzotVV5+qMciWV6LaG1g4jeFrkDlJedjtV4h0i4Q/ITnUu+Pk08M7fczsB9GXBDw==", "dev": true, "requires": { - "@babel/types": "^7.14.5" + "@babel/types": "^7.15.4" } }, "@babel/helper-replace-supers": { - "version": "7.15.0", - "resolved": "https://registry.npmjs.org/@babel/helper-replace-supers/-/helper-replace-supers-7.15.0.tgz", - "integrity": "sha512-6O+eWrhx+HEra/uJnifCwhwMd6Bp5+ZfZeJwbqUTuqkhIT6YcRhiZCOOFChRypOIe0cV46kFrRBlm+t5vHCEaA==", + "version": "7.15.4", + "resolved": "https://registry.npmjs.org/@babel/helper-replace-supers/-/helper-replace-supers-7.15.4.tgz", + "integrity": "sha512-/ztT6khaXF37MS47fufrKvIsiQkx1LBRvSJNzRqmbyeZnTwU9qBxXYLaaT/6KaxfKhjs2Wy8kG8ZdsFUuWBjzw==", "dev": true, "requires": { - "@babel/helper-member-expression-to-functions": "^7.15.0", - "@babel/helper-optimise-call-expression": "^7.14.5", - "@babel/traverse": "^7.15.0", - "@babel/types": "^7.15.0" + "@babel/helper-member-expression-to-functions": "^7.15.4", + "@babel/helper-optimise-call-expression": "^7.15.4", + "@babel/traverse": "^7.15.4", + "@babel/types": "^7.15.4" } }, "@babel/helper-simple-access": { - "version": "7.14.8", - "resolved": "https://registry.npmjs.org/@babel/helper-simple-access/-/helper-simple-access-7.14.8.tgz", - "integrity": "sha512-TrFN4RHh9gnWEU+s7JloIho2T76GPwRHhdzOWLqTrMnlas8T9O7ec+oEDNsRXndOmru9ymH9DFrEOxpzPoSbdg==", + "version": "7.15.4", + "resolved": "https://registry.npmjs.org/@babel/helper-simple-access/-/helper-simple-access-7.15.4.tgz", + "integrity": "sha512-UzazrDoIVOZZcTeHHEPYrr1MvTR/K+wgLg6MY6e1CJyaRhbibftF6fR2KU2sFRtI/nERUZR9fBd6aKgBlIBaPg==", "dev": true, "requires": { - "@babel/types": "^7.14.8" + "@babel/types": "^7.15.4" } }, "@babel/helper-split-export-declaration": { - "version": "7.14.5", - "resolved": "https://registry.npmjs.org/@babel/helper-split-export-declaration/-/helper-split-export-declaration-7.14.5.tgz", - "integrity": "sha512-hprxVPu6e5Kdp2puZUmvOGjaLv9TCe58E/Fl6hRq4YiVQxIcNvuq6uTM2r1mT/oPskuS9CgR+I94sqAYv0NGKA==", + "version": "7.15.4", + "resolved": "https://registry.npmjs.org/@babel/helper-split-export-declaration/-/helper-split-export-declaration-7.15.4.tgz", + "integrity": "sha512-HsFqhLDZ08DxCpBdEVtKmywj6PQbwnF6HHybur0MAnkAKnlS6uHkwnmRIkElB2Owpfb4xL4NwDmDLFubueDXsw==", "dev": true, "requires": { - "@babel/types": "^7.14.5" + "@babel/types": "^7.15.4" } }, "@babel/helper-validator-identifier": { - "version": "7.14.9", - "resolved": "https://registry.npmjs.org/@babel/helper-validator-identifier/-/helper-validator-identifier-7.14.9.tgz", - "integrity": "sha512-pQYxPY0UP6IHISRitNe8bsijHex4TWZXi2HwKVsjPiltzlhse2znVcm9Ace510VT1kxIHjGJCZZQBX2gJDbo0g==", + "version": "7.15.7", + "resolved": "https://registry.npmjs.org/@babel/helper-validator-identifier/-/helper-validator-identifier-7.15.7.tgz", + "integrity": "sha512-K4JvCtQqad9OY2+yTU8w+E82ywk/fe+ELNlt1G8z3bVGlZfn/hOcQQsUhGhW/N+tb3fxK800wLtKOE/aM0m72w==", "dev": true }, "@babel/helper-validator-option": { @@ -5872,14 +5903,14 @@ "dev": true }, "@babel/helpers": { - "version": "7.15.3", - "resolved": "https://registry.npmjs.org/@babel/helpers/-/helpers-7.15.3.tgz", - "integrity": "sha512-HwJiz52XaS96lX+28Tnbu31VeFSQJGOeKHJeaEPQlTl7PnlhFElWPj8tUXtqFIzeN86XxXoBr+WFAyK2PPVz6g==", + "version": "7.15.4", + "resolved": "https://registry.npmjs.org/@babel/helpers/-/helpers-7.15.4.tgz", + "integrity": "sha512-V45u6dqEJ3w2rlryYYXf6i9rQ5YMNu4FLS6ngs8ikblhu2VdR1AqAd6aJjBzmf2Qzh6KOLqKHxEN9+TFbAkAVQ==", "dev": true, "requires": { - "@babel/template": "^7.14.5", - "@babel/traverse": "^7.15.0", - "@babel/types": "^7.15.0" + "@babel/template": "^7.15.4", + "@babel/traverse": "^7.15.4", + "@babel/types": "^7.15.4" } }, "@babel/highlight": { @@ -5952,43 +5983,43 @@ } }, "@babel/parser": { - "version": "7.15.3", - "resolved": "https://registry.npmjs.org/@babel/parser/-/parser-7.15.3.tgz", - "integrity": "sha512-O0L6v/HvqbdJawj0iBEfVQMc3/6WP+AeOsovsIgBFyJaG+W2w7eqvZB7puddATmWuARlm1SX7DwxJ/JJUnDpEA==", + "version": "7.15.7", + "resolved": "https://registry.npmjs.org/@babel/parser/-/parser-7.15.7.tgz", + "integrity": "sha512-rycZXvQ+xS9QyIcJ9HXeDWf1uxqlbVFAUq0Rq0dbc50Zb/+wUe/ehyfzGfm9KZZF0kBejYgxltBXocP+gKdL2g==", "dev": true }, "@babel/template": { - "version": "7.14.5", - "resolved": "https://registry.npmjs.org/@babel/template/-/template-7.14.5.tgz", - "integrity": "sha512-6Z3Po85sfxRGachLULUhOmvAaOo7xCvqGQtxINai2mEGPFm6pQ4z5QInFnUrRpfoSV60BnjyF5F3c+15fxFV1g==", + "version": "7.15.4", + "resolved": "https://registry.npmjs.org/@babel/template/-/template-7.15.4.tgz", + "integrity": "sha512-UgBAfEa1oGuYgDIPM2G+aHa4Nlo9Lh6mGD2bDBGMTbYnc38vulXPuC1MGjYILIEmlwl6Rd+BPR9ee3gm20CBtg==", "dev": true, "requires": { "@babel/code-frame": "^7.14.5", - "@babel/parser": "^7.14.5", - "@babel/types": "^7.14.5" + "@babel/parser": "^7.15.4", + "@babel/types": "^7.15.4" } }, "@babel/traverse": { - "version": "7.15.0", - "resolved": "https://registry.npmjs.org/@babel/traverse/-/traverse-7.15.0.tgz", - "integrity": "sha512-392d8BN0C9eVxVWd8H6x9WfipgVH5IaIoLp23334Sc1vbKKWINnvwRpb4us0xtPaCumlwbTtIYNA0Dv/32sVFw==", + "version": "7.15.4", + "resolved": "https://registry.npmjs.org/@babel/traverse/-/traverse-7.15.4.tgz", + "integrity": "sha512-W6lQD8l4rUbQR/vYgSuCAE75ADyyQvOpFVsvPPdkhf6lATXAsQIG9YdtOcu8BB1dZ0LKu+Zo3c1wEcbKeuhdlA==", "dev": true, "requires": { "@babel/code-frame": "^7.14.5", - "@babel/generator": "^7.15.0", - "@babel/helper-function-name": "^7.14.5", - "@babel/helper-hoist-variables": "^7.14.5", - "@babel/helper-split-export-declaration": "^7.14.5", - "@babel/parser": "^7.15.0", - "@babel/types": "^7.15.0", + "@babel/generator": "^7.15.4", + "@babel/helper-function-name": "^7.15.4", + "@babel/helper-hoist-variables": "^7.15.4", + "@babel/helper-split-export-declaration": "^7.15.4", + "@babel/parser": "^7.15.4", + "@babel/types": "^7.15.4", "debug": "^4.1.0", "globals": "^11.1.0" } }, "@babel/types": { - "version": "7.15.0", - "resolved": "https://registry.npmjs.org/@babel/types/-/types-7.15.0.tgz", - "integrity": "sha512-OBvfqnllOIdX4ojTHpwZbpvz4j3EWyjkZEdmjH0/cgsd6QOdSgU8rLSk6ard/pcW7rlmjdVSX/AWOaORR1uNOQ==", + "version": "7.15.6", + "resolved": "https://registry.npmjs.org/@babel/types/-/types-7.15.6.tgz", + "integrity": "sha512-BPU+7QhqNjmWyDO0/vitH/CuhpV8ZmK1wpKva8nuyNF5MJfuRNWMc+hc14+u9xT93kvykMdncrJT19h74uB1Ig==", "dev": true, "requires": { "@babel/helper-validator-identifier": "^7.14.9", @@ -6129,9 +6160,9 @@ } }, "@octokit/auth-token": { - "version": "2.4.5", - "resolved": "https://registry.npmjs.org/@octokit/auth-token/-/auth-token-2.4.5.tgz", - "integrity": "sha512-BpGYsPgJt05M7/L/5FoE1PiAbdxXFZkX/3kDYcsvd1v6UhlnE5e96dTDr0ezX/EFwciQxf3cNV0loipsURU+WA==", + "version": "2.5.0", + "resolved": "https://registry.npmjs.org/@octokit/auth-token/-/auth-token-2.5.0.tgz", + "integrity": "sha512-r5FVUJCOLl19AxiuZD2VRZ/ORjp/4IN98Of6YJoJOkY75CIBuYfmiNHGrDwXr+aLGG55igl9QrxX3hbiXlLb+g==", "dev": true, "requires": { "@octokit/types": "^6.0.3" @@ -6197,12 +6228,12 @@ "requires": {} }, "@octokit/plugin-rest-endpoint-methods": { - "version": "5.7.0", - "resolved": "https://registry.npmjs.org/@octokit/plugin-rest-endpoint-methods/-/plugin-rest-endpoint-methods-5.7.0.tgz", - "integrity": "sha512-G7sgccWRYQMwcHJXkDY/sDxbXeKiZkFQqUtzBCwmrzCNj2GQf3VygQ4T/BFL2crLVpIbenkE/c0ErhYOte2MPw==", + "version": "5.10.4", + "resolved": "https://registry.npmjs.org/@octokit/plugin-rest-endpoint-methods/-/plugin-rest-endpoint-methods-5.10.4.tgz", + "integrity": "sha512-Dh+EAMCYR9RUHwQChH94Skl0lM8Fh99auT8ggck/xTzjJrwVzvsd0YH68oRPqp/HxICzmUjLfaQ9sy1o1sfIiA==", "dev": true, "requires": { - "@octokit/types": "^6.24.0", + "@octokit/types": "^6.28.1", "deprecation": "^2.3.1" } }, @@ -6232,30 +6263,30 @@ } }, "@octokit/rest": { - "version": "18.9.0", - "resolved": "https://registry.npmjs.org/@octokit/rest/-/rest-18.9.0.tgz", - "integrity": "sha512-VrmrE8gjpuOoDAGjrQq2j9ZhOE6LxaqxaQg0yMrrEnnQZy2ZcAnr5qbVfKsMF0up/48PRV/VFS/2GSMhA7nTdA==", + "version": "18.10.0", + "resolved": "https://registry.npmjs.org/@octokit/rest/-/rest-18.10.0.tgz", + "integrity": "sha512-esHR5OKy38bccL/sajHqZudZCvmv4yjovMJzyXlphaUo7xykmtOdILGJ3aAm0mFHmMLmPFmDMJXf39cAjNJsrw==", "dev": true, "requires": { - "@octokit/core": "^3.5.0", - "@octokit/plugin-paginate-rest": "^2.6.2", - "@octokit/plugin-request-log": "^1.0.2", - "@octokit/plugin-rest-endpoint-methods": "5.7.0" + "@octokit/core": "^3.5.1", + "@octokit/plugin-paginate-rest": "^2.16.0", + "@octokit/plugin-request-log": "^1.0.4", + "@octokit/plugin-rest-endpoint-methods": "^5.9.0" } }, "@octokit/types": { - "version": "6.26.0", - "resolved": "https://registry.npmjs.org/@octokit/types/-/types-6.26.0.tgz", - "integrity": "sha512-RDxZBAFMtqs1ZPnbUu1e7ohPNfoNhTiep4fErY7tZs995BeHu369Vsh5woMIaFbllRWEZBfvTCS4hvDnMPiHrA==", + "version": "6.28.1", + "resolved": "https://registry.npmjs.org/@octokit/types/-/types-6.28.1.tgz", + "integrity": "sha512-XlxDoQLFO5JnFZgKVQTYTvXRsQFfr/GwDUU108NJ9R5yFPkA2qXhTJjYuul3vE4eLXP40FA2nysOu2zd6boE+w==", "dev": true, "requires": { - "@octokit/openapi-types": "^10.0.0" + "@octokit/openapi-types": "^10.2.2" } }, "@sindresorhus/is": { - "version": "4.0.1", - "resolved": "https://registry.npmjs.org/@sindresorhus/is/-/is-4.0.1.tgz", - "integrity": "sha512-Qm9hBEBu18wt1PO2flE7LPb30BHMQt1eQgbV76YntdNk73XZGpn3izvGTYxbGgzXKgbCjiia0uxTd3aTNQrY/g==", + "version": "4.2.0", + "resolved": "https://registry.npmjs.org/@sindresorhus/is/-/is-4.2.0.tgz", + "integrity": "sha512-VkE3KLBmJwcCaVARtQpfuKcKv8gcBmUubrfHGF84dXuuW6jgsRYxPtzcIhPyK9WAPpRt2/xY6zkD9MnRaJzSyw==", "dev": true }, "@sinonjs/commons": { @@ -6345,9 +6376,9 @@ "dev": true }, "@types/keyv": { - "version": "3.1.2", - "resolved": "https://registry.npmjs.org/@types/keyv/-/keyv-3.1.2.tgz", - "integrity": "sha512-/FvAK2p4jQOaJ6CGDHJTqZcUtbZe820qIeTg7o0Shg7drB4JHeL+V/dhSaly7NXx6u8eSee+r7coT+yuJEvDLg==", + "version": "3.1.3", + "resolved": "https://registry.npmjs.org/@types/keyv/-/keyv-3.1.3.tgz", + "integrity": "sha512-FXCJgyyN3ivVgRoml4h94G/p3kY+u/B86La+QptcqJaWtBWtmc6TtkNfS40n9bIvyLteHh7zXOtgbobORKPbDg==", "dev": true, "requires": { "@types/node": "*" @@ -6360,9 +6391,9 @@ "dev": true }, "@types/node": { - "version": "16.7.10", - "resolved": "https://registry.npmjs.org/@types/node/-/node-16.7.10.tgz", - "integrity": "sha512-S63Dlv4zIPb8x6MMTgDq5WWRJQe56iBEY0O3SOFA9JrRienkOVDXSXBjjJw6HTNQYSE2JI6GMCR6LVbIMHJVvA==", + "version": "16.9.6", + "resolved": "https://registry.npmjs.org/@types/node/-/node-16.9.6.tgz", + "integrity": "sha512-YHUZhBOMTM3mjFkXVcK+WwAcYmyhe1wL4lfqNtzI0b3qAy7yuSetnM7QJazgE5PFmgVTNGiLOgRFfJMqW7XpSQ==", "dev": true }, "@types/parse-json": { @@ -6381,9 +6412,9 @@ } }, "@types/sinon": { - "version": "10.0.2", - "resolved": "https://registry.npmjs.org/@types/sinon/-/sinon-10.0.2.tgz", - "integrity": "sha512-BHn8Bpkapj8Wdfxvh2jWIUoaYB/9/XhsL0oOvBfRagJtKlSl9NWPcFOz2lRukI9szwGxFtYZCTejJSqsGDbdmw==", + "version": "10.0.3", + "resolved": "https://registry.npmjs.org/@types/sinon/-/sinon-10.0.3.tgz", + "integrity": "sha512-XUaFuUOQ3A/r6gS1qCU/USMleascaqGeQpGR1AZ5JdRtBPlzijRzKsik1TuGzvdtPA0mdq42JqaJmJ+Afg1LJg==", "dev": true, "requires": { "@sinonjs/fake-timers": "^7.1.0" @@ -6408,15 +6439,15 @@ "dev": true }, "acorn": { - "version": "8.4.1", - "resolved": "https://registry.npmjs.org/acorn/-/acorn-8.4.1.tgz", - "integrity": "sha512-asabaBSkEKosYKMITunzX177CXxQ4Q8BSSzMTKD+FefUhipQC70gfW5SiUDhYQ3vk8G+81HqQk7Fv9OXwwn9KA==", + "version": "8.5.0", + "resolved": "https://registry.npmjs.org/acorn/-/acorn-8.5.0.tgz", + "integrity": "sha512-yXbYeFy+jUuYd3/CDcg2NkIYE991XYX/bje7LmjJigUciaeO1JR4XxXgCIV1/Zc/dRuFEyw1L0pbA+qynJkW5Q==", "dev": true }, "acorn-walk": { - "version": "8.1.1", - "resolved": "https://registry.npmjs.org/acorn-walk/-/acorn-walk-8.1.1.tgz", - "integrity": "sha512-FbJdceMlPHEAWJOILDk1fXD8lnTlEIWFkqtfk+MvmL5q/qlHfN7GEHcsFZWt/Tea9jRNPWUZG4G976nqAAmU9w==", + "version": "8.2.0", + "resolved": "https://registry.npmjs.org/acorn-walk/-/acorn-walk-8.2.0.tgz", + "integrity": "sha512-k+iyHEuPgSw6SbuDpGQM+06HQUa04DZ3o+F6CSzXMvvI5KMvnaEqXe+YVe555R9nn6GPt404fos4wcgpw12SDA==", "dev": true }, "aggregate-error": { @@ -6502,9 +6533,9 @@ } }, "ansi-regex": { - "version": "5.0.0", - "resolved": "https://registry.npmjs.org/ansi-regex/-/ansi-regex-5.0.0.tgz", - "integrity": "sha512-bY6fj56OUQ0hU1KjFNDQuJFezqKdrAyFdIevADiqrWHwSlbmBNMHp5ak2f40Pm8JTFyM2mqxkG6ngkHO11f/lg==", + "version": "5.0.1", + "resolved": "https://registry.npmjs.org/ansi-regex/-/ansi-regex-5.0.1.tgz", + "integrity": "sha512-quJQXlTSUGL2LH9SUXo8VwsY4soanhgo6LNSm84E1LBcE8s3O0wpdiRzyR9z/ZZJMlMWv37qOOb9pdJlMUEKFQ==", "dev": true }, "ansi-styles": { @@ -6560,12 +6591,12 @@ "dev": true }, "async-retry": { - "version": "1.3.1", - "resolved": "https://registry.npmjs.org/async-retry/-/async-retry-1.3.1.tgz", - "integrity": "sha512-aiieFW/7h3hY0Bq5d+ktDBejxuwR78vRu9hDUdR8rNhSaQ29VzPL4AoIRG7D/c7tdenwOcKvgPM6tIxB3cB6HA==", + "version": "1.3.3", + "resolved": "https://registry.npmjs.org/async-retry/-/async-retry-1.3.3.tgz", + "integrity": "sha512-wfr/jstw9xNi/0teMHrRW7dsz3Lt5ARhYNZ2ewpadnhaIp5mbALhOAP+EAdsC7t4Z6wqsDVv9+W6gm1Dk9mEyw==", "dev": true, "requires": { - "retry": "0.12.0" + "retry": "0.13.1" } }, "asynckit": { @@ -6610,16 +6641,16 @@ } }, "boxen": { - "version": "5.0.1", - "resolved": "https://registry.npmjs.org/boxen/-/boxen-5.0.1.tgz", - "integrity": "sha512-49VBlw+PrWEF51aCmy7QIteYPIFZxSpvqBdP/2itCPPlJ49kj9zg/XPRFrdkne2W+CfwXUls8exMvu1RysZpKA==", + "version": "5.1.2", + "resolved": "https://registry.npmjs.org/boxen/-/boxen-5.1.2.tgz", + "integrity": "sha512-9gYgQKXx+1nP8mP7CzFyaUARhg7D3n1dF/FnErWmu9l6JvGpNUN278h0aSb+QjoiKSWG+iZ3uHrcqk0qrY9RQQ==", "dev": true, "requires": { "ansi-align": "^3.0.0", "camelcase": "^6.2.0", "chalk": "^4.1.0", "cli-boxes": "^2.2.1", - "string-width": "^4.2.0", + "string-width": "^4.2.2", "type-fest": "^0.20.2", "widest-line": "^3.1.0", "wrap-ansi": "^7.0.0" @@ -6665,14 +6696,14 @@ "dev": true }, "browserslist": { - "version": "4.16.8", - "resolved": "https://registry.npmjs.org/browserslist/-/browserslist-4.16.8.tgz", - "integrity": "sha512-sc2m9ohR/49sWEbPj14ZSSZqp+kbi16aLao42Hmn3Z8FpjuMaq2xCA2l4zl9ITfyzvnvyE0hcg62YkIGKxgaNQ==", + "version": "4.17.0", + "resolved": "https://registry.npmjs.org/browserslist/-/browserslist-4.17.0.tgz", + "integrity": "sha512-g2BJ2a0nEYvEFQC208q8mVAhfNwpZ5Mu8BwgtCdZKO3qx98HChmeg448fPdUzld8aFmfLgVh7yymqV+q1lJZ5g==", "dev": true, "requires": { - "caniuse-lite": "^1.0.30001251", + "caniuse-lite": "^1.0.30001254", "colorette": "^1.3.0", - "electron-to-chromium": "^1.3.811", + "electron-to-chromium": "^1.3.830", "escalade": "^3.1.1", "node-releases": "^1.1.75" } @@ -6760,9 +6791,9 @@ "dev": true }, "caniuse-lite": { - "version": "1.0.30001252", - "resolved": "https://registry.npmjs.org/caniuse-lite/-/caniuse-lite-1.0.30001252.tgz", - "integrity": "sha512-I56jhWDGMtdILQORdusxBOH+Nl/KgQSdDmpJezYddnAkVOmnoU8zwjTV9xAjMIYxr0iPreEAVylCGcmHCjfaOw==", + "version": "1.0.30001259", + "resolved": "https://registry.npmjs.org/caniuse-lite/-/caniuse-lite-1.0.30001259.tgz", + "integrity": "sha512-V7mQTFhjITxuk9zBpI6nYsiTXhcPe05l+364nZjK7MFK/E7ibvYBSAXr4YcA6oPR8j3ZLM/LN+lUqUVAQEUZFg==", "dev": true }, "chalk": { @@ -6894,9 +6925,9 @@ "dev": true }, "colorette": { - "version": "1.3.0", - "resolved": "https://registry.npmjs.org/colorette/-/colorette-1.3.0.tgz", - "integrity": "sha512-ecORCqbSFP7Wm8Y6lyqMJjexBQqXSF7SSeaTyGGphogUjBlFP9m9o08wy86HL2uB7fMTxtOUzLMk7ogKcxMg1w==", + "version": "1.4.0", + "resolved": "https://registry.npmjs.org/colorette/-/colorette-1.4.0.tgz", + "integrity": "sha512-Y2oEozpomLn7Q3HFP7dpww7AtMJplbM9lGZP6RDfHqmbeRjiwRg4n6VM6j4KLmRke85uWEI7JqF17f3pqdRA0g==", "dev": true }, "combined-stream": { @@ -6944,9 +6975,9 @@ } }, "cosmiconfig": { - "version": "7.0.0", - "resolved": "https://registry.npmjs.org/cosmiconfig/-/cosmiconfig-7.0.0.tgz", - "integrity": "sha512-pondGvTuVYDk++upghXJabWzL6Kxu6f26ljFw64Swq9v6sQPUL3EUlVDV56diOjpCayKihL6hVe8exIACU4XcA==", + "version": "7.0.1", + "resolved": "https://registry.npmjs.org/cosmiconfig/-/cosmiconfig-7.0.1.tgz", + "integrity": "sha512-a1YWNUV2HwGimB7dU2s1wUMurNKjpx60HxBB6xUM8Re+2s1g1IIfJvFR0/iCF+XHdE0GMTKTuLR32UQff4TEyQ==", "dev": true, "requires": { "@types/parse-json": "^4.0.0", @@ -7209,9 +7240,9 @@ } }, "fastq": { - "version": "1.12.0", - "resolved": "https://registry.npmjs.org/fastq/-/fastq-1.12.0.tgz", - "integrity": "sha512-VNX0QkHK3RsXVKr9KrlUv/FoTa0NdbYoHHl7uXHv2rzyHSlxjdNAKug2twd9luJxpcyNeAgf5iPPMutJO67Dfg==", + "version": "1.13.0", + "resolved": "https://registry.npmjs.org/fastq/-/fastq-1.13.0.tgz", + "integrity": "sha512-YpkpUnK8od0o1hmeSc7UUs/eB/vIPWJYjKck2QKIzAf71Vm1AAQ3EbuZB3g2JIy+pg+ERD0vqI79KyZiB2e2Nw==", "dev": true, "requires": { "reusify": "^1.0.4" @@ -7373,9 +7404,9 @@ } }, "git-url-parse": { - "version": "11.5.0", - "resolved": "https://registry.npmjs.org/git-url-parse/-/git-url-parse-11.5.0.tgz", - "integrity": "sha512-TZYSMDeM37r71Lqg1mbnMlOqlHd7BSij9qN7XwTkRqSAYFMihGLGhfHwgqQob3GUhEneKnV4nskN9rbQw2KGxA==", + "version": "11.6.0", + "resolved": "https://registry.npmjs.org/git-url-parse/-/git-url-parse-11.6.0.tgz", + "integrity": "sha512-WWUxvJs5HsyHL6L08wOusa/IXYtMuCAhrMmnTjQPpBU0TTHyDhnOATNH3xNQz7YOQUsqIIPTGr4xiVti1Hsk5g==", "dev": true, "requires": { "git-up": "^4.0.0" @@ -7654,9 +7685,9 @@ "dev": true }, "inquirer": { - "version": "8.1.2", - "resolved": "https://registry.npmjs.org/inquirer/-/inquirer-8.1.2.tgz", - "integrity": "sha512-DHLKJwLPNgkfwNmsuEUKSejJFbkv0FMO9SMiQbjI3n5NQuCrSIBqP66ggqyz2a6t2qEolKrMjhQ3+W/xXgUQ+Q==", + "version": "8.1.5", + "resolved": "https://registry.npmjs.org/inquirer/-/inquirer-8.1.5.tgz", + "integrity": "sha512-G6/9xUqmt/r+UvufSyrPpt84NYwhKZ9jLsgMbQzlx804XErNupor8WQdBnBRrXmBfTPpuwf1sV+ss2ovjgdXIg==", "dev": true, "requires": { "ansi-escapes": "^4.2.1", @@ -7667,7 +7698,7 @@ "figures": "^3.0.0", "lodash": "^4.17.21", "mute-stream": "0.0.8", - "ora": "^5.3.0", + "ora": "^5.4.1", "run-async": "^2.4.0", "rxjs": "^7.2.0", "string-width": "^4.1.0", @@ -7988,6 +8019,12 @@ "minimist": "^1.2.5" } }, + "jsonc-parser": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/jsonc-parser/-/jsonc-parser-3.0.0.tgz", + "integrity": "sha512-fQzRfAbIBnR0IQvftw9FJveWiHp72Fg20giDrHz6TdfB12UH/uue0D3hm57UB5KgAVuniLMCaS8P1IMj9NR7cA==", + "dev": true + }, "just-extend": { "version": "4.2.1", "resolved": "https://registry.npmjs.org/just-extend/-/just-extend-4.2.1.tgz", @@ -8106,9 +8143,9 @@ "dev": true }, "marked": { - "version": "3.0.2", - "resolved": "https://registry.npmjs.org/marked/-/marked-3.0.2.tgz", - "integrity": "sha512-TMJQQ79Z0e3rJYazY0tIoMsFzteUGw9fB3FD+gzuIT3zLuG9L9ckIvUfF51apdJkcqc208jJN2KbtPbOvXtbjA==", + "version": "3.0.4", + "resolved": "https://registry.npmjs.org/marked/-/marked-3.0.4.tgz", + "integrity": "sha512-jBo8AOayNaEcvBhNobg6/BLhdsK3NvnKWJg33MAAPbvTWiG4QBn9gpW1+7RssrKu4K1dKlN+0goVQwV41xEfOA==", "dev": true }, "merge-stream": { @@ -8263,10 +8300,13 @@ } }, "node-fetch": { - "version": "2.6.1", - "resolved": "https://registry.npmjs.org/node-fetch/-/node-fetch-2.6.1.tgz", - "integrity": "sha512-V4aYg89jEoVRxRb2fJdAg8FHvI7cEyYdVAh94HH0UIK8oJxUfkjlDQN9RbMx+bEjP7+ggMiFRprSti032Oipxw==", - "dev": true + "version": "2.6.4", + "resolved": "https://registry.npmjs.org/node-fetch/-/node-fetch-2.6.4.tgz", + "integrity": "sha512-aD1fO+xtLiSCc9vuD+sYMxpIuQyhHscGSkBEo2o5LTV/3bTEAYvdUii29n8LlO5uLCmWdGP7uVUVXFo5SRdkLA==", + "dev": true, + "requires": { + "whatwg-url": "^5.0.0" + } }, "node-preload": { "version": "0.2.1", @@ -8278,9 +8318,9 @@ } }, "node-releases": { - "version": "1.1.75", - "resolved": "https://registry.npmjs.org/node-releases/-/node-releases-1.1.75.tgz", - "integrity": "sha512-Qe5OUajvqrqDSy6wrWFmMwfJ0jVgwiw4T3KqmbTcZ62qW0gQkheXYhcFM1+lOVcGUoRxcEcfyvFMAnDgaF1VWw==", + "version": "1.1.76", + "resolved": "https://registry.npmjs.org/node-releases/-/node-releases-1.1.76.tgz", + "integrity": "sha512-9/IECtNr8dXNmPWmFXepT0/7o5eolGesHUa3mtr0KlgnCvnZxwh2qensKL42JJY2vQKC3nIBXetFAqR+PW1CmA==", "dev": true }, "normalize-path": { @@ -9025,25 +9065,25 @@ } }, "release-it": { - "version": "14.11.5", - "resolved": "https://registry.npmjs.org/release-it/-/release-it-14.11.5.tgz", - "integrity": "sha512-9BaPdq7ZKOwtzz3p1mRhg/tOH/cT/y2tUnPYzUwQiVdj42JaGI1Vo2l3WbgK8ICsbFyrhc0tri1+iqI8OvkI1A==", + "version": "14.11.6", + "resolved": "https://registry.npmjs.org/release-it/-/release-it-14.11.6.tgz", + "integrity": "sha512-6BNcuzFZHThBUBJ/xYw/bxZ+58CAwrwf1zgmjq2Ibl3nlDZbjphHG6iqxkJu7mZ8TIWs6NjloEAhqpjeXoN//Q==", "dev": true, "requires": { "@iarna/toml": "2.2.5", - "@octokit/rest": "18.9.0", - "async-retry": "1.3.1", + "@octokit/rest": "18.10.0", + "async-retry": "1.3.3", "chalk": "4.1.2", - "cosmiconfig": "7.0.0", + "cosmiconfig": "7.0.1", "debug": "4.3.2", "deprecated-obj": "2.0.0", "execa": "5.1.1", "form-data": "4.0.0", - "git-url-parse": "11.5.0", + "git-url-parse": "11.6.0", "globby": "11.0.4", "got": "11.8.2", "import-cwd": "3.0.0", - "inquirer": "8.1.2", + "inquirer": "8.1.5", "is-ci": "3.0.0", "lodash": "4.17.21", "mime-types": "2.1.32", @@ -9171,9 +9211,9 @@ } }, "retry": { - "version": "0.12.0", - "resolved": "https://registry.npmjs.org/retry/-/retry-0.12.0.tgz", - "integrity": "sha1-G0KmJmoh8HQh0bC1S33BZ7AcATs=", + "version": "0.13.1", + "resolved": "https://registry.npmjs.org/retry/-/retry-0.13.1.tgz", + "integrity": "sha512-XQBQ3I8W1Cge0Seh+6gjj03LbmRFWuoszgK9ooCpwYIrhhoO80pfq4cUkU5DkknwfOfFteRwlZ56PYOGYyFWdg==", "dev": true }, "reusify": { @@ -9289,7 +9329,7 @@ "integrity": "sha512-xeM7Oc6hY+6iW5O/T5hor8ul7mEprzyl5y4r5zthEHToQNw7MIhREMgU3r2gKDB0NaMLNrkcEQagudCdzE13Lg==", "dev": true, "requires": { - "json5": "^2.2.0", + "jsonc-parser": "^3.0.0", "onigasm": "^2.2.5", "vscode-textmate": "5.2.0" } @@ -9306,9 +9346,9 @@ } }, "signal-exit": { - "version": "3.0.3", - "resolved": "https://registry.npmjs.org/signal-exit/-/signal-exit-3.0.3.tgz", - "integrity": "sha512-VUJ49FC8U1OxwZLxIbTTrDvLnf/6TDgxZcK8wxR8zs13xpx7xbG60ndBlhNrFi2EMuFRoeDoJO7wthSLq42EjA==", + "version": "3.0.4", + "resolved": "https://registry.npmjs.org/signal-exit/-/signal-exit-3.0.4.tgz", + "integrity": "sha512-rqYhcAnZ6d/vTPGghdrw7iumdcbXpsk1b8IG/rz+VWV51DM0p7XCtMoJ3qhPLIbp3tvyt3pKRbaaEMZYpHto8Q==", "dev": true }, "sinon": { @@ -9349,9 +9389,9 @@ "dev": true }, "source-map-support": { - "version": "0.5.19", - "resolved": "https://registry.npmjs.org/source-map-support/-/source-map-support-0.5.19.tgz", - "integrity": "sha512-Wonm7zOCIJzBGQdB+thsPar0kYuCIzYvxZwlBa87yi/Mdjv7Tip2cyVbLj5o0cFPN4EVkuTwb3GDDyUx2DGnGw==", + "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==", "dev": true, "requires": { "buffer-from": "^1.0.0", @@ -9509,6 +9549,12 @@ "is-number": "^7.0.0" } }, + "tr46": { + "version": "0.0.3", + "resolved": "https://registry.npmjs.org/tr46/-/tr46-0.0.3.tgz", + "integrity": "sha1-gYT9NH2snNwYWZLzpmIuFLnZq2o=", + "dev": true + }, "ts-node": { "version": "10.2.1", "resolved": "https://registry.npmjs.org/ts-node/-/ts-node-10.2.1.tgz", @@ -9603,15 +9649,15 @@ } }, "typescript": { - "version": "4.4.2", - "resolved": "https://registry.npmjs.org/typescript/-/typescript-4.4.2.tgz", - "integrity": "sha512-gzP+t5W4hdy4c+68bfcv0t400HVJMMd2+H9B7gae1nQlBzCqvrXX+6GL/b3GAgyTH966pzrZ70/fRjwAtZksSQ==", + "version": "4.4.3", + "resolved": "https://registry.npmjs.org/typescript/-/typescript-4.4.3.tgz", + "integrity": "sha512-4xfscpisVgqqDfPaJo5vkd+Qd/ItkoagnHpufr+i2QCHBsNYp+G7UAoyFl8aPtx879u38wPV65rZ8qbGZijalA==", "dev": true }, "uglify-js": { - "version": "3.14.1", - "resolved": "https://registry.npmjs.org/uglify-js/-/uglify-js-3.14.1.tgz", - "integrity": "sha512-JhS3hmcVaXlp/xSo3PKY5R0JqKs5M3IV+exdLHW99qKvKivPO4Z8qbej6mte17SOPqAOVMjt/XGgWacnFSzM3g==", + "version": "3.14.2", + "resolved": "https://registry.npmjs.org/uglify-js/-/uglify-js-3.14.2.tgz", + "integrity": "sha512-rtPMlmcO4agTUfz10CbgJ1k6UAoXM2gWb3GoMPPZB/+/Ackf8lNWk11K4rYi2D0apgoFRLtQOZhb+/iGNJq26A==", "dev": true, "optional": true }, @@ -9729,6 +9775,22 @@ "defaults": "^1.0.3" } }, + "webidl-conversions": { + "version": "3.0.1", + "resolved": "https://registry.npmjs.org/webidl-conversions/-/webidl-conversions-3.0.1.tgz", + "integrity": "sha1-JFNCdeKnvGvnvIZhHMFq4KVlSHE=", + "dev": true + }, + "whatwg-url": { + "version": "5.0.0", + "resolved": "https://registry.npmjs.org/whatwg-url/-/whatwg-url-5.0.0.tgz", + "integrity": "sha1-lmRU6HZUYuN2RNNib2dCzotwll0=", + "dev": true, + "requires": { + "tr46": "~0.0.3", + "webidl-conversions": "^3.0.0" + } + }, "which": { "version": "2.0.2", "resolved": "https://registry.npmjs.org/which/-/which-2.0.2.tgz", diff --git a/package.json b/package.json index 56a7ed38c65..b2ceadbdd16 100644 --- a/package.json +++ b/package.json @@ -1,6 +1,6 @@ { "name": "redis", - "version": "4.0.0-rc.1", + "version": "4.0.0-rc.2", "description": "A high performance Redis client.", "keywords": [ "database", @@ -35,20 +35,20 @@ "devDependencies": { "@istanbuljs/nyc-config-typescript": "^1.0.1", "@types/mocha": "^9.0.0", - "@types/node": "^16.7.10", - "@types/sinon": "^10.0.2", + "@types/node": "^16.9.6", + "@types/sinon": "^10.0.3", "@types/which": "^2.0.1", "@types/yallist": "^4.0.1", "mocha": "^9.1.1", "nyc": "^15.1.0", - "release-it": "^14.11.5", + "release-it": "^14.11.6", "sinon": "^11.1.2", - "source-map-support": "^0.5.19", + "source-map-support": "^0.5.20", "ts-node": "^10.2.1", - "typedoc": "^0.21.9", + "typedoc": "0.21.9", "typedoc-github-wiki-theme": "^0.5.1", - "typedoc-plugin-markdown": "^3.10.4", - "typescript": "^4.4.2", + "typedoc-plugin-markdown": "3.10.4", + "typescript": "^4.4.3", "which": "^2.0.2" }, "engines": { From 9a1beedda7ef2d07599e061d6f542fa9a2e1e965 Mon Sep 17 00:00:00 2001 From: leibale Date: Thu, 23 Sep 2021 16:52:34 -0400 Subject: [PATCH 0879/1748] add missing semicolon --- lib/commands/BITFIELD.ts | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/lib/commands/BITFIELD.ts b/lib/commands/BITFIELD.ts index 445c26e28a3..90adb969814 100644 --- a/lib/commands/BITFIELD.ts +++ b/lib/commands/BITFIELD.ts @@ -66,7 +66,7 @@ export function transformArguments(key: string, operations: BitFieldOperations): options.type, options.offset.toString(), options.increment.toString() - ) + ); break; case 'OVERFLOW': From c19d200b91c3c32fde7bf75c859daf9b416a5540 Mon Sep 17 00:00:00 2001 From: leibale Date: Wed, 29 Sep 2021 17:58:16 -0400 Subject: [PATCH 0880/1748] replace empty "transformReply" functions with typescript "declare" --- benchmark/package-lock.json | 215 ++++------ lib/client.ts | 20 +- lib/cluster.ts | 22 +- lib/commander.ts | 10 +- lib/commands/ACL_CAT.ts | 4 +- lib/commands/ACL_DELUSER.ts | 4 +- lib/commands/ACL_GENPASS.ts | 4 +- lib/commands/ACL_LIST.ts | 4 +- lib/commands/ACL_LOAD.ts | 4 +- lib/commands/ACL_LOG_RESET.ts | 4 +- lib/commands/ACL_SAVE.ts | 4 +- lib/commands/ACL_SETUSER.ts | 4 +- lib/commands/ACL_USERS.ts | 4 +- lib/commands/ACL_WHOAMI.ts | 4 +- lib/commands/APPEND.ts | 4 +- lib/commands/ASKING.ts | 4 +- lib/commands/AUTH.ts | 4 +- lib/commands/BGREWRITEAOF.ts | 4 +- lib/commands/BGSAVE.ts | 4 +- lib/commands/BITCOUNT.ts | 4 +- lib/commands/BITFIELD.ts | 4 +- lib/commands/BITOP.ts | 4 +- lib/commands/BITPOS.ts | 4 +- lib/commands/BLMOVE.ts | 3 +- lib/commands/BRPOPLPUSH.ts | 4 +- lib/commands/CLIENT_ID.ts | 4 +- lib/commands/CLUSTER_ADDSLOTS.ts | 4 +- lib/commands/CLUSTER_FLUSHSLOTS.ts | 4 +- lib/commands/CLUSTER_GETKEYSINSLOT.ts | 4 +- lib/commands/CLUSTER_MEET.ts | 4 +- lib/commands/CLUSTER_RESET.ts | 4 +- lib/commands/CLUSTER_SETSLOT.ts | 4 +- lib/commands/CONFIG_RESETSTAT.ts | 4 +- lib/commands/CONFIG_REWRITE.ts | 4 +- lib/commands/CONFIG_SET.ts | 4 +- lib/commands/DBSIZE.ts | 4 +- lib/commands/DECR.ts | 4 +- lib/commands/DECRBY.ts | 4 +- lib/commands/DEL.ts | 4 +- lib/commands/DISCARD.ts | 4 +- lib/commands/DUMP.ts | 4 +- lib/commands/ECHO.ts | 4 +- lib/commands/FAILOVER.ts | 4 +- lib/commands/FLUSHALL.ts | 4 +- lib/commands/FLUSHDB.ts | 3 +- lib/commands/GEOADD.ts | 4 +- lib/commands/GEOHASH.ts | 4 +- lib/commands/GEOSEARCH.ts | 4 +- lib/commands/GET.ts | 3 +- lib/commands/GETBIT.ts | 4 +- lib/commands/GETDEL.ts | 4 +- lib/commands/GETEX.ts | 4 +- lib/commands/GETRANGE.ts | 4 +- lib/commands/GETSET.ts | 4 +- lib/commands/GET_BUFFER.ts | 4 +- lib/commands/HDEL.ts | 4 +- lib/commands/HINCRBY.ts | 4 +- lib/commands/HINCRBYFLOAT.ts | 4 +- lib/commands/HKEYS.ts | 4 +- lib/commands/HLEN.ts | 4 +- lib/commands/HMGET.ts | 4 +- lib/commands/HRANDFIELD.ts | 4 +- lib/commands/HRANDFIELD_COUNT.ts | 3 +- lib/commands/HSET.ts | 3 +- lib/commands/HSTRLEN.ts | 4 +- lib/commands/HVALS.ts | 4 +- lib/commands/INCR.ts | 4 +- lib/commands/INCRBY.ts | 4 +- lib/commands/INCRBYFLOAT.ts | 4 +- lib/commands/INFO.ts | 4 +- lib/commands/LINDEX.ts | 4 +- lib/commands/LINSERT.ts | 4 +- lib/commands/LLEN.ts | 4 +- lib/commands/LMOVE.ts | 4 +- lib/commands/LOLWUT.ts | 4 +- lib/commands/LPOP.ts | 4 +- lib/commands/LPOP_COUNT.ts | 4 +- lib/commands/LPOS.ts | 4 +- lib/commands/LPOS_COUNT.ts | 3 +- lib/commands/LPUSH.ts | 4 +- lib/commands/LPUSHX.ts | 4 +- lib/commands/LRANGE.ts | 4 +- lib/commands/LREM.ts | 4 +- lib/commands/LSET.ts | 4 +- lib/commands/LTRIM.ts | 4 +- lib/commands/MEMORY_DOCTOR.ts | 4 +- lib/commands/MEMORY_MALLOC-STATS.ts | 4 +- lib/commands/MEMORY_PURGE.ts | 4 +- lib/commands/MEMORY_USAGE.ts | 4 +- lib/commands/MGET.ts | 4 +- lib/commands/MIGRATE.ts | 3 +- lib/commands/MODULE_LIST.ts | 4 +- lib/commands/MODULE_LOAD.ts | 4 +- lib/commands/MODULE_UNLOAD.ts | 4 +- lib/commands/MSET.ts | 4 +- lib/commands/PFCOUNT.ts | 4 +- lib/commands/PFMERGE.ts | 4 +- lib/commands/PING.ts | 4 +- lib/commands/PSETEX.ts | 4 +- lib/commands/PTTL.ts | 4 +- lib/commands/PUBLISH.ts | 4 +- lib/commands/PUBSUB_CHANNELS.ts | 4 +- lib/commands/PUBSUB_NUMPAT.ts | 4 +- lib/commands/READONLY.ts | 4 +- lib/commands/READWRITE.ts | 4 +- lib/commands/RENAME.ts | 4 +- lib/commands/REPLICAOF.ts | 4 +- lib/commands/RESTORE-ASKING.ts | 4 +- lib/commands/RPOP.ts | 4 +- lib/commands/RPOPLPUSH.ts | 4 +- lib/commands/RPOP_COUNT.ts | 4 +- lib/commands/RPUSH.ts | 4 +- lib/commands/RPUSHX.ts | 4 +- lib/commands/SADD.ts | 4 +- lib/commands/SAVE.ts | 4 +- lib/commands/SCARD.ts | 4 +- lib/commands/SCRIPT_DEBUG.ts | 4 +- lib/commands/SCRIPT_FLUSH.ts | 4 +- lib/commands/SCRIPT_KILL.ts | 4 +- lib/commands/SCRIPT_LOAD.ts | 4 +- lib/commands/SDIFF.ts | 4 +- lib/commands/SDIFFSTORE.ts | 4 +- lib/commands/SETBIT.ts | 4 +- lib/commands/SETEX.ts | 3 +- lib/commands/SETRANGE.ts | 4 +- lib/commands/SHUTDOWN.ts | 4 +- lib/commands/SINTER.ts | 4 +- lib/commands/SINTERSTORE.ts | 4 +- lib/commands/SMEMBERS.ts | 4 +- lib/commands/SPOP.ts | 4 +- lib/commands/SRANDMEMBER.ts | 4 +- lib/commands/SRANDMEMBER_COUNT.ts | 3 +- lib/commands/SREM.ts | 4 +- lib/commands/STRLEN.ts | 4 +- lib/commands/SUNION.ts | 4 +- lib/commands/SUNIONSTORE.ts | 4 +- lib/commands/SWAPDB.ts | 4 +- lib/commands/TOUCH.ts | 4 +- lib/commands/TTL.ts | 4 +- lib/commands/TYPE.ts | 4 +- lib/commands/UNLINK.ts | 4 +- lib/commands/UNWATCH.ts | 4 +- lib/commands/WAIT.ts | 4 +- lib/commands/WATCH.ts | 4 +- lib/commands/XACK.ts | 4 +- lib/commands/XADD.ts | 5 +- lib/commands/XCLAIM_JUSTID.ts | 3 +- lib/commands/XDEL.ts | 4 +- lib/commands/XGROUP_CREATE.ts | 4 +- lib/commands/XGROUP_DELCONSUMER.ts | 4 +- lib/commands/XGROUP_SETID.ts | 4 +- lib/commands/XLEN.ts | 4 +- lib/commands/XTRIM.ts | 4 +- lib/commands/ZCARD.ts | 4 +- lib/commands/ZCOUNT.ts | 4 +- lib/commands/ZDIFF.ts | 4 +- lib/commands/ZDIFFSTORE.ts | 4 +- lib/commands/ZINTER.ts | 4 +- lib/commands/ZINTERSTORE.ts | 4 +- lib/commands/ZLEXCOUNT.ts | 4 +- lib/commands/ZRANDMEMBER.ts | 4 +- lib/commands/ZRANDMEMBER_COUNT.ts | 3 +- lib/commands/ZRANGE.ts | 4 +- lib/commands/ZRANK.ts | 4 +- lib/commands/ZREM.ts | 4 +- lib/commands/ZREMRANGEBYLEX.ts | 4 +- lib/commands/ZREMRANGEBYRANK.ts | 4 +- lib/commands/ZREMRANGEBYSCORE.ts | 4 +- lib/commands/ZREVRANK.ts | 4 +- lib/commands/ZUNION.ts | 4 +- lib/commands/ZUNIONSTORE.ts | 4 +- lib/commands/generic-transformers.ts | 46 --- lib/commands/index.ts | 4 +- package-lock.json | 548 ++++++++------------------ 174 files changed, 472 insertions(+), 1051 deletions(-) diff --git a/benchmark/package-lock.json b/benchmark/package-lock.json index a16a420f8cb..767d6870184 100644 --- a/benchmark/package-lock.json +++ b/benchmark/package-lock.json @@ -4,9 +4,9 @@ "requires": true, "packages": { "": { + "name": "benchmark", "license": "ISC", "dependencies": { - "@probe.gl/bench": "^3.4.0", "benny": "^3.6.15", "v3": "npm:redis@3.1.2", "v4": "file:../" @@ -14,30 +14,31 @@ }, "..": { "name": "redis", - "version": "4.0.0-next.5", + "version": "4.0.0-rc.2", "license": "MIT", "dependencies": { "cluster-key-slot": "1.1.0", + "generic-pool": "3.8.2", "redis-parser": "3.0.0", "yallist": "4.0.0" }, "devDependencies": { "@istanbuljs/nyc-config-typescript": "^1.0.1", "@types/mocha": "^9.0.0", - "@types/node": "^16.4.5", - "@types/sinon": "^10.0.2", + "@types/node": "^16.9.6", + "@types/sinon": "^10.0.3", "@types/which": "^2.0.1", "@types/yallist": "^4.0.1", - "mocha": "^9.0.3", + "mocha": "^9.1.1", "nyc": "^15.1.0", - "release-it": "^14.10.1", + "release-it": "^14.11.6", "sinon": "^11.1.2", - "source-map-support": "^0.5.19", - "ts-node": "^10.1.0", - "typedoc": "^0.21.4", + "source-map-support": "^0.5.20", + "ts-node": "^10.2.1", + "typedoc": "0.21.9", "typedoc-github-wiki-theme": "^0.5.1", - "typedoc-plugin-markdown": "^3.10.4", - "typescript": "^4.3.5", + "typedoc-plugin-markdown": "3.10.4", + "typescript": "^4.4.3", "which": "^2.0.2" }, "engines": { @@ -81,34 +82,6 @@ "fast-deep-equal": "^3.1.1" } }, - "node_modules/@babel/runtime": { - "version": "7.14.8", - "resolved": "https://registry.npmjs.org/@babel/runtime/-/runtime-7.14.8.tgz", - "integrity": "sha512-twj3L8Og5SaCRCErB4x4ajbvBIVV77CGeFglHpeg5WC5FF8TZzBWXtTJ4MqaD9QszLYTtr+IsaAL2rEUevb+eg==", - "dependencies": { - "regenerator-runtime": "^0.13.4" - }, - "engines": { - "node": ">=6.9.0" - } - }, - "node_modules/@probe.gl/bench": { - "version": "3.4.0", - "resolved": "https://registry.npmjs.org/@probe.gl/bench/-/bench-3.4.0.tgz", - "integrity": "sha512-S7iNPz5G3zEfEP0S4SAMvtj+dwP7EWfVBaA8Cy5CVIgM1lnpUbXvqoAJxlVEedNC32Icxwq65XQheufy1Zzmug==", - "dependencies": { - "@babel/runtime": "^7.0.0", - "probe.gl": "3.4.0" - } - }, - "node_modules/@probe.gl/stats": { - "version": "3.4.0", - "resolved": "https://registry.npmjs.org/@probe.gl/stats/-/stats-3.4.0.tgz", - "integrity": "sha512-Gl37r9qGuiKadIvTZdSZvzCNOttJYw6RcY1oT0oDuB8r2uhuZAdSMQRQTy9FTinp6MY6O9wngGnV6EpQ8wSBAw==", - "dependencies": { - "@babel/runtime": "^7.0.0" - } - }, "node_modules/ansi-escapes": { "version": "4.3.2", "resolved": "https://registry.npmjs.org/ansi-escapes/-/ansi-escapes-4.3.2.tgz", @@ -124,9 +97,9 @@ } }, "node_modules/ansi-regex": { - "version": "5.0.0", - "resolved": "https://registry.npmjs.org/ansi-regex/-/ansi-regex-5.0.0.tgz", - "integrity": "sha512-bY6fj56OUQ0hU1KjFNDQuJFezqKdrAyFdIevADiqrWHwSlbmBNMHp5ak2f40Pm8JTFyM2mqxkG6ngkHO11f/lg==", + "version": "5.0.1", + "resolved": "https://registry.npmjs.org/ansi-regex/-/ansi-regex-5.0.1.tgz", + "integrity": "sha512-quJQXlTSUGL2LH9SUXo8VwsY4soanhgo6LNSm84E1LBcE8s3O0wpdiRzyR9z/ZZJMlMWv37qOOb9pdJlMUEKFQ==", "engines": { "node": ">=8" } @@ -223,9 +196,9 @@ } }, "node_modules/denque": { - "version": "1.5.0", - "resolved": "https://registry.npmjs.org/denque/-/denque-1.5.0.tgz", - "integrity": "sha512-CYiCSgIF1p6EUByQPlGkKnP1M9g0ZV3qMIrqMqZqdwazygIA/YP2vrbcyl1h/WppKJTdl1F85cXIle+394iDAQ==", + "version": "1.5.1", + "resolved": "https://registry.npmjs.org/denque/-/denque-1.5.1.tgz", + "integrity": "sha512-XwE+iZ4D6ZUB7mfYRMb5wByE8L74HCn30FBN7sWnXksWc1LO1bPDl67pBR9o/kC4z/xSNAwkMYcGgqDV3BE3Hw==", "engines": { "node": ">=0.10" } @@ -255,9 +228,9 @@ } }, "node_modules/graceful-fs": { - "version": "4.2.6", - "resolved": "https://registry.npmjs.org/graceful-fs/-/graceful-fs-4.2.6.tgz", - "integrity": "sha512-nTnJ528pbqxYanhpDYsi4Rd8MAeaBA67+RZ10CM1m3bTAVFEDcd5AuA4a6W5YkGZ1iNXHzZz8T6TBKLeBuNriQ==" + "version": "4.2.8", + "resolved": "https://registry.npmjs.org/graceful-fs/-/graceful-fs-4.2.8.tgz", + "integrity": "sha512-qkIilPUYcNhJpd33n0GBXTB1MMPp14TxEsEs0pTrsSVucApsYzW5V+Q8Qxhik6KU3evy+qkAAowTByymK0avdg==" }, "node_modules/is-fullwidth-code-point": { "version": "3.0.0", @@ -366,9 +339,9 @@ "integrity": "sha512-fnWVljUchTro6RiCFvCXBbNhJc2NijN7oIQxbwsyL0buWJPG85v81ehlHI9fXrJsMNgTofEoWIQeClKpgxFLrg==" }, "node_modules/prettier": { - "version": "2.3.2", - "resolved": "https://registry.npmjs.org/prettier/-/prettier-2.3.2.tgz", - "integrity": "sha512-lnJzDfJ66zkMy58OL5/NY5zp70S7Nz6KqcKkXYzn2tMVrNxvbqaBpg7H3qHaLxCJ5lNMsGuM8+ohS7cZrthdLQ==", + "version": "2.4.1", + "resolved": "https://registry.npmjs.org/prettier/-/prettier-2.4.1.tgz", + "integrity": "sha512-9fbDAXSBcc6Bs1mZrDYb3XKzDLm4EXXL9sC1LqKP5rZkT6KRr/rf9amVUcODVXgguK/isJz0d0hP72WeaKWsvA==", "bin": { "prettier": "bin-prettier.js" }, @@ -376,15 +349,6 @@ "node": ">=10.13.0" } }, - "node_modules/probe.gl": { - "version": "3.4.0", - "resolved": "https://registry.npmjs.org/probe.gl/-/probe.gl-3.4.0.tgz", - "integrity": "sha512-9CLByZATuhuG/Viq3ckfWU+dAhb7dMmjzsyCy4s7ds9ueTejcVRENxL197/XacOK/AN61YrEERB0QnouB0Qc0Q==", - "dependencies": { - "@babel/runtime": "^7.0.0", - "@probe.gl/stats": "3.4.0" - } - }, "node_modules/redis-commands": { "version": "1.7.0", "resolved": "https://registry.npmjs.org/redis-commands/-/redis-commands-1.7.0.tgz", @@ -409,11 +373,6 @@ "node": ">=4" } }, - "node_modules/regenerator-runtime": { - "version": "0.13.9", - "resolved": "https://registry.npmjs.org/regenerator-runtime/-/regenerator-runtime-0.13.9.tgz", - "integrity": "sha512-p3VT+cOEgxFsRRA9X4lkI1E+k2/CtnKtU4gcxyaCUreilL/vqI6CdZ3wxVUx3UOUg+gnUOQQcRI7BmSI656MYA==" - }, "node_modules/restore-cursor": { "version": "3.1.0", "resolved": "https://registry.npmjs.org/restore-cursor/-/restore-cursor-3.1.0.tgz", @@ -427,9 +386,9 @@ } }, "node_modules/signal-exit": { - "version": "3.0.3", - "resolved": "https://registry.npmjs.org/signal-exit/-/signal-exit-3.0.3.tgz", - "integrity": "sha512-VUJ49FC8U1OxwZLxIbTTrDvLnf/6TDgxZcK8wxR8zs13xpx7xbG60ndBlhNrFi2EMuFRoeDoJO7wthSLq42EjA==" + "version": "3.0.5", + "resolved": "https://registry.npmjs.org/signal-exit/-/signal-exit-3.0.5.tgz", + "integrity": "sha512-KWcOiKeQj6ZyXx7zq4YxSMgHRlod4czeBQZrPb8OKcohcqAXShm7E20kEMle9WBt26hFcAf0qLOcp5zmY7kOqQ==" }, "node_modules/slice-ansi": { "version": "4.0.0", @@ -453,24 +412,24 @@ "integrity": "sha512-IYsheLg6dasD3zT/w9+8Iq9tcIQqqu91ZIpJOnIEM25C3X/g4Tl8mhXwW2ZQpbrsJISr9+wizEYgsibN5/b32Q==" }, "node_modules/string-width": { - "version": "4.2.2", - "resolved": "https://registry.npmjs.org/string-width/-/string-width-4.2.2.tgz", - "integrity": "sha512-XBJbT3N4JhVumXE0eoLU9DCjcaF92KLNqTmFCnG1pf8duUxFGwtP6AD6nkjw9a3IdiRtL3E2w3JDiE/xi3vOeA==", + "version": "4.2.3", + "resolved": "https://registry.npmjs.org/string-width/-/string-width-4.2.3.tgz", + "integrity": "sha512-wKyQRQpjJ0sIp62ErSZdGsjMJWsap5oRNihHhu6G7JVO/9jIB6UyevL+tXuOqrng8j/cxKTWyWUwvSTriiZz/g==", "dependencies": { "emoji-regex": "^8.0.0", "is-fullwidth-code-point": "^3.0.0", - "strip-ansi": "^6.0.0" + "strip-ansi": "^6.0.1" }, "engines": { "node": ">=8" } }, "node_modules/strip-ansi": { - "version": "6.0.0", - "resolved": "https://registry.npmjs.org/strip-ansi/-/strip-ansi-6.0.0.tgz", - "integrity": "sha512-AuvKTrTfQNYNIctbR1K/YGTR1756GycPsg7b9bdV9Duqur4gv6aKqHXah67Z8ImS7WEz5QVcOtlfW2rZEugt6w==", + "version": "6.0.1", + "resolved": "https://registry.npmjs.org/strip-ansi/-/strip-ansi-6.0.1.tgz", + "integrity": "sha512-Y38VPSHcqkFrCpFnQ9vuSXmquuv5oXOKpGeT6aGrr3o3Gc9AlVa6JBfUSOCnbxGGZF+/0ooI7KrPuUSztUdU5A==", "dependencies": { - "ansi-regex": "^5.0.0" + "ansi-regex": "^5.0.1" }, "engines": { "node": ">=8" @@ -570,31 +529,6 @@ "fast-deep-equal": "^3.1.1" } }, - "@babel/runtime": { - "version": "7.14.8", - "resolved": "https://registry.npmjs.org/@babel/runtime/-/runtime-7.14.8.tgz", - "integrity": "sha512-twj3L8Og5SaCRCErB4x4ajbvBIVV77CGeFglHpeg5WC5FF8TZzBWXtTJ4MqaD9QszLYTtr+IsaAL2rEUevb+eg==", - "requires": { - "regenerator-runtime": "^0.13.4" - } - }, - "@probe.gl/bench": { - "version": "3.4.0", - "resolved": "https://registry.npmjs.org/@probe.gl/bench/-/bench-3.4.0.tgz", - "integrity": "sha512-S7iNPz5G3zEfEP0S4SAMvtj+dwP7EWfVBaA8Cy5CVIgM1lnpUbXvqoAJxlVEedNC32Icxwq65XQheufy1Zzmug==", - "requires": { - "@babel/runtime": "^7.0.0", - "probe.gl": "3.4.0" - } - }, - "@probe.gl/stats": { - "version": "3.4.0", - "resolved": "https://registry.npmjs.org/@probe.gl/stats/-/stats-3.4.0.tgz", - "integrity": "sha512-Gl37r9qGuiKadIvTZdSZvzCNOttJYw6RcY1oT0oDuB8r2uhuZAdSMQRQTy9FTinp6MY6O9wngGnV6EpQ8wSBAw==", - "requires": { - "@babel/runtime": "^7.0.0" - } - }, "ansi-escapes": { "version": "4.3.2", "resolved": "https://registry.npmjs.org/ansi-escapes/-/ansi-escapes-4.3.2.tgz", @@ -604,9 +538,9 @@ } }, "ansi-regex": { - "version": "5.0.0", - "resolved": "https://registry.npmjs.org/ansi-regex/-/ansi-regex-5.0.0.tgz", - "integrity": "sha512-bY6fj56OUQ0hU1KjFNDQuJFezqKdrAyFdIevADiqrWHwSlbmBNMHp5ak2f40Pm8JTFyM2mqxkG6ngkHO11f/lg==" + "version": "5.0.1", + "resolved": "https://registry.npmjs.org/ansi-regex/-/ansi-regex-5.0.1.tgz", + "integrity": "sha512-quJQXlTSUGL2LH9SUXo8VwsY4soanhgo6LNSm84E1LBcE8s3O0wpdiRzyR9z/ZZJMlMWv37qOOb9pdJlMUEKFQ==" }, "ansi-styles": { "version": "4.3.0", @@ -679,9 +613,9 @@ "integrity": "sha512-U7VdrJFnJgo4xjrHpTzu0yrHPGImdsmD95ZlgYSEajAn2JKzDhDTPG9kBTefmObL2w/ngeZnilk+OV9CG3d7UA==" }, "denque": { - "version": "1.5.0", - "resolved": "https://registry.npmjs.org/denque/-/denque-1.5.0.tgz", - "integrity": "sha512-CYiCSgIF1p6EUByQPlGkKnP1M9g0ZV3qMIrqMqZqdwazygIA/YP2vrbcyl1h/WppKJTdl1F85cXIle+394iDAQ==" + "version": "1.5.1", + "resolved": "https://registry.npmjs.org/denque/-/denque-1.5.1.tgz", + "integrity": "sha512-XwE+iZ4D6ZUB7mfYRMb5wByE8L74HCn30FBN7sWnXksWc1LO1bPDl67pBR9o/kC4z/xSNAwkMYcGgqDV3BE3Hw==" }, "emoji-regex": { "version": "8.0.0", @@ -705,9 +639,9 @@ } }, "graceful-fs": { - "version": "4.2.6", - "resolved": "https://registry.npmjs.org/graceful-fs/-/graceful-fs-4.2.6.tgz", - "integrity": "sha512-nTnJ528pbqxYanhpDYsi4Rd8MAeaBA67+RZ10CM1m3bTAVFEDcd5AuA4a6W5YkGZ1iNXHzZz8T6TBKLeBuNriQ==" + "version": "4.2.8", + "resolved": "https://registry.npmjs.org/graceful-fs/-/graceful-fs-4.2.8.tgz", + "integrity": "sha512-qkIilPUYcNhJpd33n0GBXTB1MMPp14TxEsEs0pTrsSVucApsYzW5V+Q8Qxhik6KU3evy+qkAAowTByymK0avdg==" }, "is-fullwidth-code-point": { "version": "3.0.0", @@ -783,18 +717,9 @@ "integrity": "sha512-fnWVljUchTro6RiCFvCXBbNhJc2NijN7oIQxbwsyL0buWJPG85v81ehlHI9fXrJsMNgTofEoWIQeClKpgxFLrg==" }, "prettier": { - "version": "2.3.2", - "resolved": "https://registry.npmjs.org/prettier/-/prettier-2.3.2.tgz", - "integrity": "sha512-lnJzDfJ66zkMy58OL5/NY5zp70S7Nz6KqcKkXYzn2tMVrNxvbqaBpg7H3qHaLxCJ5lNMsGuM8+ohS7cZrthdLQ==" - }, - "probe.gl": { - "version": "3.4.0", - "resolved": "https://registry.npmjs.org/probe.gl/-/probe.gl-3.4.0.tgz", - "integrity": "sha512-9CLByZATuhuG/Viq3ckfWU+dAhb7dMmjzsyCy4s7ds9ueTejcVRENxL197/XacOK/AN61YrEERB0QnouB0Qc0Q==", - "requires": { - "@babel/runtime": "^7.0.0", - "@probe.gl/stats": "3.4.0" - } + "version": "2.4.1", + "resolved": "https://registry.npmjs.org/prettier/-/prettier-2.4.1.tgz", + "integrity": "sha512-9fbDAXSBcc6Bs1mZrDYb3XKzDLm4EXXL9sC1LqKP5rZkT6KRr/rf9amVUcODVXgguK/isJz0d0hP72WeaKWsvA==" }, "redis-commands": { "version": "1.7.0", @@ -814,11 +739,6 @@ "redis-errors": "^1.0.0" } }, - "regenerator-runtime": { - "version": "0.13.9", - "resolved": "https://registry.npmjs.org/regenerator-runtime/-/regenerator-runtime-0.13.9.tgz", - "integrity": "sha512-p3VT+cOEgxFsRRA9X4lkI1E+k2/CtnKtU4gcxyaCUreilL/vqI6CdZ3wxVUx3UOUg+gnUOQQcRI7BmSI656MYA==" - }, "restore-cursor": { "version": "3.1.0", "resolved": "https://registry.npmjs.org/restore-cursor/-/restore-cursor-3.1.0.tgz", @@ -829,9 +749,9 @@ } }, "signal-exit": { - "version": "3.0.3", - "resolved": "https://registry.npmjs.org/signal-exit/-/signal-exit-3.0.3.tgz", - "integrity": "sha512-VUJ49FC8U1OxwZLxIbTTrDvLnf/6TDgxZcK8wxR8zs13xpx7xbG60ndBlhNrFi2EMuFRoeDoJO7wthSLq42EjA==" + "version": "3.0.5", + "resolved": "https://registry.npmjs.org/signal-exit/-/signal-exit-3.0.5.tgz", + "integrity": "sha512-KWcOiKeQj6ZyXx7zq4YxSMgHRlod4czeBQZrPb8OKcohcqAXShm7E20kEMle9WBt26hFcAf0qLOcp5zmY7kOqQ==" }, "slice-ansi": { "version": "4.0.0", @@ -849,21 +769,21 @@ "integrity": "sha512-IYsheLg6dasD3zT/w9+8Iq9tcIQqqu91ZIpJOnIEM25C3X/g4Tl8mhXwW2ZQpbrsJISr9+wizEYgsibN5/b32Q==" }, "string-width": { - "version": "4.2.2", - "resolved": "https://registry.npmjs.org/string-width/-/string-width-4.2.2.tgz", - "integrity": "sha512-XBJbT3N4JhVumXE0eoLU9DCjcaF92KLNqTmFCnG1pf8duUxFGwtP6AD6nkjw9a3IdiRtL3E2w3JDiE/xi3vOeA==", + "version": "4.2.3", + "resolved": "https://registry.npmjs.org/string-width/-/string-width-4.2.3.tgz", + "integrity": "sha512-wKyQRQpjJ0sIp62ErSZdGsjMJWsap5oRNihHhu6G7JVO/9jIB6UyevL+tXuOqrng8j/cxKTWyWUwvSTriiZz/g==", "requires": { "emoji-regex": "^8.0.0", "is-fullwidth-code-point": "^3.0.0", - "strip-ansi": "^6.0.0" + "strip-ansi": "^6.0.1" } }, "strip-ansi": { - "version": "6.0.0", - "resolved": "https://registry.npmjs.org/strip-ansi/-/strip-ansi-6.0.0.tgz", - "integrity": "sha512-AuvKTrTfQNYNIctbR1K/YGTR1756GycPsg7b9bdV9Duqur4gv6aKqHXah67Z8ImS7WEz5QVcOtlfW2rZEugt6w==", + "version": "6.0.1", + "resolved": "https://registry.npmjs.org/strip-ansi/-/strip-ansi-6.0.1.tgz", + "integrity": "sha512-Y38VPSHcqkFrCpFnQ9vuSXmquuv5oXOKpGeT6aGrr3o3Gc9AlVa6JBfUSOCnbxGGZF+/0ooI7KrPuUSztUdU5A==", "requires": { - "ansi-regex": "^5.0.0" + "ansi-regex": "^5.0.1" } }, "type-fest": { @@ -892,22 +812,23 @@ "requires": { "@istanbuljs/nyc-config-typescript": "^1.0.1", "@types/mocha": "^9.0.0", - "@types/node": "^16.4.5", - "@types/sinon": "^10.0.2", + "@types/node": "^16.9.6", + "@types/sinon": "^10.0.3", "@types/which": "^2.0.1", "@types/yallist": "^4.0.1", "cluster-key-slot": "1.1.0", - "mocha": "^9.0.3", + "generic-pool": "3.8.2", + "mocha": "^9.1.1", "nyc": "^15.1.0", "redis-parser": "3.0.0", - "release-it": "^14.10.1", + "release-it": "^14.11.6", "sinon": "^11.1.2", - "source-map-support": "^0.5.19", - "ts-node": "^10.1.0", - "typedoc": "^0.21.4", + "source-map-support": "^0.5.20", + "ts-node": "^10.2.1", + "typedoc": "0.21.9", "typedoc-github-wiki-theme": "^0.5.1", - "typedoc-plugin-markdown": "^3.10.4", - "typescript": "^4.3.5", + "typedoc-plugin-markdown": "3.10.4", + "typescript": "^4.4.3", "which": "^2.0.2", "yallist": "4.0.0" } diff --git a/lib/client.ts b/lib/client.ts index 93afee1ff1a..98169bc302e 100644 --- a/lib/client.ts +++ b/lib/client.ts @@ -1,6 +1,6 @@ import RedisSocket, { RedisSocketOptions, RedisNetSocketOptions, RedisTlsSocketOptions } from './socket'; import RedisCommandsQueue, { PubSubListener, PubSubSubscribeCommands, PubSubUnsubscribeCommands, QueueCommandOptions } from './commands-queue'; -import COMMANDS, { TransformArgumentsReply } from './commands'; +import COMMANDS, { RedisCommandReply, TransformArgumentsReply } from './commands'; import { RedisCommand, RedisModules, RedisReply } from './commands'; import RedisMultiCommand, { MultiQueuedCommand, RedisMultiCommandType } from './multi-command'; import EventEmitter from 'events'; @@ -9,7 +9,7 @@ import { RedisLuaScript, RedisLuaScripts } from './lua-script'; import { ScanOptions, ZMember } from './commands/generic-transformers'; import { ScanCommandOptions } from './commands/SCAN'; import { HScanTuple } from './commands/HSCAN'; -import { encodeCommand, extendWithDefaultCommands, extendWithModulesAndScripts, transformCommandArguments } from './commander'; +import { encodeCommand, extendWithDefaultCommands, extendWithModulesAndScripts, transformCommandArguments, transformCommandReply } from './commander'; import { Pool, Options as PoolOptions, createPool } from 'generic-pool'; import { ClientClosedError } from './errors'; import { URL } from 'url'; @@ -29,7 +29,7 @@ export interface RedisClientOptions { } export type RedisCommandSignature = - (...args: Parameters | [options: CommandOptions, ...rest: Parameters]) => Promise>; + (...args: Parameters | [options: CommandOptions, ...rest: Parameters]) => Promise>; type WithCommands = { [P in keyof typeof COMMANDS]: RedisCommandSignature<(typeof COMMANDS)[P]>; @@ -275,12 +275,13 @@ export default class RedisClient): Promise> { + async commandsExecutor(command: RedisCommand, args: Array): Promise> { const { args: redisArgs, options } = transformCommandArguments(command, args); - return command.transformReply( + return transformCommandReply( + command, await this.#sendCommand(redisArgs, options, command.BUFFER_MODE), - redisArgs.preserve, + redisArgs.preserve ); } @@ -308,16 +309,17 @@ export default class RedisClient): Promise> { + async scriptsExecutor(script: RedisLuaScript, args: Array): Promise> { const { args: redisArgs, options } = transformCommandArguments(script, args); - return script.transformReply( + return transformCommandReply( + script, await this.executeScript(script, redisArgs, options, script.BUFFER_MODE), redisArgs.preserve ); } - async executeScript(script: RedisLuaScript, args: TransformArgumentsReply, options?: ClientCommandOptions, bufferMode?: boolean): Promise> { + async executeScript(script: RedisLuaScript, args: TransformArgumentsReply, options?: ClientCommandOptions, bufferMode?: boolean): Promise> { try { return await this.#sendCommand([ 'EVALSHA', diff --git a/lib/cluster.ts b/lib/cluster.ts index 4be0e268207..87dcec17b71 100644 --- a/lib/cluster.ts +++ b/lib/cluster.ts @@ -1,9 +1,9 @@ -import { RedisCommand, RedisModules, TransformArgumentsReply } from './commands'; +import { RedisCommand, RedisCommandReply, RedisModules, TransformArgumentsReply } from './commands'; import RedisClient, { ClientCommandOptions, RedisClientType, WithPlugins } from './client'; import { RedisSocketOptions } from './socket'; import RedisClusterSlots, { ClusterNode } from './cluster-slots'; import { RedisLuaScript, RedisLuaScripts } from './lua-script'; -import { extendWithModulesAndScripts, extendWithDefaultCommands, transformCommandArguments } from './commander'; +import { extendWithModulesAndScripts, extendWithDefaultCommands, transformCommandArguments, transformCommandReply } from './commander'; import RedisMultiCommand, { MultiQueuedCommand, RedisMultiCommandType } from './multi-command'; import { EventEmitter } from 'events'; @@ -59,10 +59,11 @@ export default class RedisCluster): Promise> { + async commandsExecutor(command: RedisCommand, args: Array): Promise> { const { args: redisArgs, options } = transformCommandArguments(command, args); - const reply = command.transformReply( + return transformCommandReply( + command, await this.sendCommand( RedisCluster.#extractFirstKey(command, args, redisArgs), command.IS_READ_ONLY, @@ -72,8 +73,6 @@ export default class RedisCluster( @@ -83,7 +82,7 @@ export default class RedisCluster> { + ): Promise> { const client = this.#slots.getClient(firstKey, isReadonly); try { @@ -100,10 +99,11 @@ export default class RedisCluster): Promise> { + async scriptsExecutor(script: RedisLuaScript, args: Array): Promise> { const { args: redisArgs, options } = transformCommandArguments(script, args); - const reply = script.transformReply( + return transformCommandReply( + script, await this.executeScript( script, args, @@ -112,8 +112,6 @@ export default class RedisCluster> { + ): Promise> { const client = this.#slots.getClient( RedisCluster.#extractFirstKey(script, originalArgs, redisArgs), script.IS_READ_ONLY diff --git a/lib/commander.ts b/lib/commander.ts index 78823516448..4e542bde74a 100644 --- a/lib/commander.ts +++ b/lib/commander.ts @@ -1,5 +1,5 @@ -import COMMANDS, { RedisCommand, RedisModules, TransformArgumentsReply } from './commands'; +import COMMANDS, { RedisCommand, RedisCommandReply, RedisModules, RedisReply, TransformArgumentsReply } from './commands'; import { RedisLuaScript, RedisLuaScripts } from './lua-script'; import { CommandOptions, isCommandOptions } from './command-options'; @@ -106,3 +106,11 @@ export function* encodeCommand(args: TransformArgumentsReply): IterableIterator< yield DELIMITER; } } + +export function transformCommandReply(command: RedisCommand, rawReply: RedisReply, preserved: unknown): RedisCommandReply { + if (!command.transformReply) { + return rawReply; + } + + return command.transformReply(rawReply, preserved); +} diff --git a/lib/commands/ACL_CAT.ts b/lib/commands/ACL_CAT.ts index d1620ef1584..f11be873961 100644 --- a/lib/commands/ACL_CAT.ts +++ b/lib/commands/ACL_CAT.ts @@ -1,5 +1,3 @@ -import { transformReplyStringArray } from './generic-transformers'; - export function transformArguments(categoryName?: string): Array { const args = ['ACL', 'CAT']; @@ -10,4 +8,4 @@ export function transformArguments(categoryName?: string): Array { return args; } -export const transformReply = transformReplyStringArray; +export declare function transformReply(): Array; diff --git a/lib/commands/ACL_DELUSER.ts b/lib/commands/ACL_DELUSER.ts index 85a916c4379..e83e4d7d0ab 100644 --- a/lib/commands/ACL_DELUSER.ts +++ b/lib/commands/ACL_DELUSER.ts @@ -1,8 +1,8 @@ import { TransformArgumentsReply } from '.'; -import { pushVerdictArguments, transformReplyNumber } from './generic-transformers'; +import { pushVerdictArguments } from './generic-transformers'; export function transformArguments(username: string | Array): TransformArgumentsReply { return pushVerdictArguments(['ACL', 'DELUSER'], username); } -export const transformReply = transformReplyNumber; +export declare const transformReply: (reply: number) => number; diff --git a/lib/commands/ACL_GENPASS.ts b/lib/commands/ACL_GENPASS.ts index ec55aebdb07..6a36c396619 100644 --- a/lib/commands/ACL_GENPASS.ts +++ b/lib/commands/ACL_GENPASS.ts @@ -1,5 +1,3 @@ -import { transformReplyString } from './generic-transformers'; - export function transformArguments(bits?: number): Array { const args = ['ACL', 'GENPASS']; @@ -10,4 +8,4 @@ export function transformArguments(bits?: number): Array { return args; } -export const transformReply = transformReplyString; +export declare function transformReply(): string; diff --git a/lib/commands/ACL_LIST.ts b/lib/commands/ACL_LIST.ts index 3f2845b907a..a2caae82c46 100644 --- a/lib/commands/ACL_LIST.ts +++ b/lib/commands/ACL_LIST.ts @@ -1,7 +1,5 @@ -import { transformReplyStringArray } from './generic-transformers'; - export function transformArguments(): Array { return ['ACL', 'LIST']; } -export const transformReply = transformReplyStringArray; +export declare function transformReply(): Array; diff --git a/lib/commands/ACL_LOAD.ts b/lib/commands/ACL_LOAD.ts index 59418614ed3..0a5ea85101d 100644 --- a/lib/commands/ACL_LOAD.ts +++ b/lib/commands/ACL_LOAD.ts @@ -1,7 +1,5 @@ -import { transformReplyString } from './generic-transformers'; - export function transformArguments(): Array { return ['ACL', 'LOAD']; } -export const transformReply = transformReplyString; +export declare function transformReply(): string; diff --git a/lib/commands/ACL_LOG_RESET.ts b/lib/commands/ACL_LOG_RESET.ts index 30b8ccb20c7..5bfd7ae9392 100644 --- a/lib/commands/ACL_LOG_RESET.ts +++ b/lib/commands/ACL_LOG_RESET.ts @@ -1,7 +1,5 @@ -import { transformReplyString } from './generic-transformers'; - export function transformArguments(): Array { return ['ACL', 'LOG', 'RESET']; } -export const transformReply = transformReplyString; +export declare function transformReply(): string; diff --git a/lib/commands/ACL_SAVE.ts b/lib/commands/ACL_SAVE.ts index 5b9c7b84cc3..af9abeb1d5c 100644 --- a/lib/commands/ACL_SAVE.ts +++ b/lib/commands/ACL_SAVE.ts @@ -1,7 +1,5 @@ -import { transformReplyString } from './generic-transformers'; - export function transformArguments(): Array { return ['ACL', 'SAVE']; } -export const transformReply = transformReplyString; +export declare function transformReply(): string; diff --git a/lib/commands/ACL_SETUSER.ts b/lib/commands/ACL_SETUSER.ts index e55a8942e02..a590376ab84 100644 --- a/lib/commands/ACL_SETUSER.ts +++ b/lib/commands/ACL_SETUSER.ts @@ -1,8 +1,8 @@ import { TransformArgumentsReply } from '.'; -import { pushVerdictArguments, transformReplyString } from './generic-transformers'; +import { pushVerdictArguments } from './generic-transformers'; export function transformArguments(username: string, rule: string | Array): TransformArgumentsReply { return pushVerdictArguments(['ACL', 'SETUSER', username], rule); } -export const transformReply = transformReplyString; +export declare function transformReply(): string; diff --git a/lib/commands/ACL_USERS.ts b/lib/commands/ACL_USERS.ts index f9e837a4347..91f391a5b73 100644 --- a/lib/commands/ACL_USERS.ts +++ b/lib/commands/ACL_USERS.ts @@ -1,7 +1,5 @@ -import { transformReplyStringArray } from './generic-transformers'; - export function transformArguments(): Array { return ['ACL', 'USERS']; } -export const transformReply = transformReplyStringArray; +export declare function transformReply(): Array; diff --git a/lib/commands/ACL_WHOAMI.ts b/lib/commands/ACL_WHOAMI.ts index 3fc70649f87..c855eeb1aa7 100644 --- a/lib/commands/ACL_WHOAMI.ts +++ b/lib/commands/ACL_WHOAMI.ts @@ -1,7 +1,5 @@ -import { transformReplyString } from './generic-transformers'; - export function transformArguments(): Array { return ['ACL', 'WHOAMI']; } -export const transformReply = transformReplyString; +export declare function transformReply(): string; diff --git a/lib/commands/APPEND.ts b/lib/commands/APPEND.ts index f64e835113c..a162dc1381c 100644 --- a/lib/commands/APPEND.ts +++ b/lib/commands/APPEND.ts @@ -1,9 +1,7 @@ -import { transformReplyString } from './generic-transformers'; - export const FIRST_KEY_INDEX = 1; export function transformArguments(key: string, value: string): Array { return ['APPEND', key, value]; } -export const transformReply = transformReplyString; +export declare function transformReply(): string; diff --git a/lib/commands/ASKING.ts b/lib/commands/ASKING.ts index 3f836131ac1..8071ec20180 100644 --- a/lib/commands/ASKING.ts +++ b/lib/commands/ASKING.ts @@ -1,7 +1,5 @@ -import { transformReplyString } from './generic-transformers'; - export function transformArguments(): Array { return ['ASKING']; } -export const transformReply = transformReplyString; +export declare function transformReply(): string; diff --git a/lib/commands/AUTH.ts b/lib/commands/AUTH.ts index 750f0f54354..eb2449208a8 100644 --- a/lib/commands/AUTH.ts +++ b/lib/commands/AUTH.ts @@ -1,5 +1,3 @@ -import { transformReplyString } from './generic-transformers'; - export interface AuthOptions { username?: string; password: string; @@ -13,4 +11,4 @@ export function transformArguments({username, password}: AuthOptions): Array { return ['BGREWRITEAOF']; } -export const transformReply = transformReplyString; +export declare function transformReply(): string; diff --git a/lib/commands/BGSAVE.ts b/lib/commands/BGSAVE.ts index f09f906ade7..fba9e37ed76 100644 --- a/lib/commands/BGSAVE.ts +++ b/lib/commands/BGSAVE.ts @@ -1,5 +1,3 @@ -import { transformReplyString } from './generic-transformers'; - interface BgSaveOptions { SCHEDULE?: true; } @@ -14,4 +12,4 @@ export function transformArguments(options?: BgSaveOptions): Array { return args; } -export const transformReply = transformReplyString; \ No newline at end of file +export declare function transformReply(): string; diff --git a/lib/commands/BITCOUNT.ts b/lib/commands/BITCOUNT.ts index 1aececc377e..320d8f3acb7 100644 --- a/lib/commands/BITCOUNT.ts +++ b/lib/commands/BITCOUNT.ts @@ -1,5 +1,3 @@ -import { transformReplyNumber } from './generic-transformers'; - export const FIRST_KEY_INDEX = 1; export const IS_READ_ONLY = true; @@ -22,4 +20,4 @@ export function transformArguments(key: string, range?: BitCountRange): Array; diff --git a/lib/commands/BITOP.ts b/lib/commands/BITOP.ts index bb965da6dfa..1a750f811ca 100644 --- a/lib/commands/BITOP.ts +++ b/lib/commands/BITOP.ts @@ -1,5 +1,5 @@ import { TransformArgumentsReply } from '.'; -import { pushVerdictArguments, transformReplyNumber } from './generic-transformers'; +import { pushVerdictArguments } from './generic-transformers'; export const FIRST_KEY_INDEX = 2; @@ -9,4 +9,4 @@ export function transformArguments(operation: BitOperations, destKey: string, ke return pushVerdictArguments(['BITOP', operation, destKey], key); } -export const transformReply = transformReplyNumber; +export declare function transformReply(): number; diff --git a/lib/commands/BITPOS.ts b/lib/commands/BITPOS.ts index bebc45b03c3..86f539f2dda 100644 --- a/lib/commands/BITPOS.ts +++ b/lib/commands/BITPOS.ts @@ -1,4 +1,4 @@ -import { BitValue, transformReplyNumber } from './generic-transformers'; +import { BitValue } from './generic-transformers'; export const FIRST_KEY_INDEX = 1; @@ -18,4 +18,4 @@ export function transformArguments(key: string, bit: BitValue, start?: number, e return args; } -export const transformReply = transformReplyNumber; \ No newline at end of file +export declare function transformReply(): number; diff --git a/lib/commands/BLMOVE.ts b/lib/commands/BLMOVE.ts index 74a2eed4aa5..461a146e7a3 100644 --- a/lib/commands/BLMOVE.ts +++ b/lib/commands/BLMOVE.ts @@ -1,4 +1,3 @@ -import { transformReplyStringNull } from './generic-transformers'; import { LMoveSide } from './LMOVE'; export const FIRST_KEY_INDEX = 1; @@ -20,4 +19,4 @@ export function transformArguments( ]; } -export const transformReply = transformReplyStringNull; +export declare function transformReply(): string | null; diff --git a/lib/commands/BRPOPLPUSH.ts b/lib/commands/BRPOPLPUSH.ts index f6a3bc1b8a6..3f6aed35be1 100644 --- a/lib/commands/BRPOPLPUSH.ts +++ b/lib/commands/BRPOPLPUSH.ts @@ -1,9 +1,7 @@ -import { transformReplyNumberNull } from './generic-transformers'; - export const FIRST_KEY_INDEX = 1; export function transformArguments(source: string, destination: string, timeout: number): Array { return ['BRPOPLPUSH', source, destination, timeout.toString()]; } -export const transformReply = transformReplyNumberNull; +export declare function transformReply(): number | null; diff --git a/lib/commands/CLIENT_ID.ts b/lib/commands/CLIENT_ID.ts index baeab148eba..a57e392ade6 100644 --- a/lib/commands/CLIENT_ID.ts +++ b/lib/commands/CLIENT_ID.ts @@ -1,9 +1,7 @@ -import { transformReplyNumber } from './generic-transformers'; - export const IS_READ_ONLY = true; export function transformArguments(): Array { return ['CLIENT', 'ID']; } -export const transformReply = transformReplyNumber; +export declare function transformReply(): number; diff --git a/lib/commands/CLUSTER_ADDSLOTS.ts b/lib/commands/CLUSTER_ADDSLOTS.ts index 594eae77c75..e458b8aab91 100644 --- a/lib/commands/CLUSTER_ADDSLOTS.ts +++ b/lib/commands/CLUSTER_ADDSLOTS.ts @@ -1,5 +1,3 @@ -import { transformReplyString } from './generic-transformers'; - export function transformArguments(slots: number | Array): Array { const args = ['CLUSTER', 'ADDSLOTS']; @@ -12,4 +10,4 @@ export function transformArguments(slots: number | Array): Array return args; } -export const transformReply = transformReplyString; +export declare function transformReply(): string; diff --git a/lib/commands/CLUSTER_FLUSHSLOTS.ts b/lib/commands/CLUSTER_FLUSHSLOTS.ts index 28fbcc1fab1..285c9fd26fe 100644 --- a/lib/commands/CLUSTER_FLUSHSLOTS.ts +++ b/lib/commands/CLUSTER_FLUSHSLOTS.ts @@ -1,7 +1,5 @@ -import { transformReplyString } from './generic-transformers'; - export function transformArguments(): Array { return ['CLUSTER', 'FLUSHSLOTS']; } -export const transformReply = transformReplyString; +export declare function transformReply(): string; diff --git a/lib/commands/CLUSTER_GETKEYSINSLOT.ts b/lib/commands/CLUSTER_GETKEYSINSLOT.ts index c5719848cf5..67c5cbafb77 100644 --- a/lib/commands/CLUSTER_GETKEYSINSLOT.ts +++ b/lib/commands/CLUSTER_GETKEYSINSLOT.ts @@ -1,7 +1,5 @@ -import { transformReplyString } from './generic-transformers'; - export function transformArguments(slot: number, count: number): Array { return ['CLUSTER', 'GETKEYSINSLOT', slot.toString(), count.toString()]; } -export const transformReply = transformReplyString; +export declare function transformReply(): string; diff --git a/lib/commands/CLUSTER_MEET.ts b/lib/commands/CLUSTER_MEET.ts index 19da150356c..54a0bf708a8 100644 --- a/lib/commands/CLUSTER_MEET.ts +++ b/lib/commands/CLUSTER_MEET.ts @@ -1,7 +1,5 @@ -import { transformReplyString } from './generic-transformers'; - export function transformArguments(ip: string, port: number): Array { return ['CLUSTER', 'MEET', ip, port.toString()]; } -export const transformReply = transformReplyString; +export declare function transformReply(): string; diff --git a/lib/commands/CLUSTER_RESET.ts b/lib/commands/CLUSTER_RESET.ts index ec27b45eeb7..3e7c5bb52fb 100644 --- a/lib/commands/CLUSTER_RESET.ts +++ b/lib/commands/CLUSTER_RESET.ts @@ -1,5 +1,3 @@ -import { transformReplyString } from './generic-transformers'; - export type ClusterResetModes = 'HARD' | 'SOFT'; export function transformArguments(mode?: ClusterResetModes): Array { @@ -12,4 +10,4 @@ export function transformArguments(mode?: ClusterResetModes): Array { return args; } -export const transformReply = transformReplyString; +export declare function transformReply(): string; diff --git a/lib/commands/CLUSTER_SETSLOT.ts b/lib/commands/CLUSTER_SETSLOT.ts index c665b349622..591b5fb9632 100644 --- a/lib/commands/CLUSTER_SETSLOT.ts +++ b/lib/commands/CLUSTER_SETSLOT.ts @@ -1,5 +1,3 @@ -import { transformReplyString } from './generic-transformers'; - export enum ClusterSlotStates { IMPORTING = 'IMPORTING', MIGRATING = 'MIGRATING', @@ -17,4 +15,4 @@ export function transformArguments(slot: number, state: ClusterSlotStates, nodeI return args; } -export const transformReply = transformReplyString; +export declare function transformReply(): string; diff --git a/lib/commands/CONFIG_RESETSTAT.ts b/lib/commands/CONFIG_RESETSTAT.ts index 3c87b08d88b..aba54bc3c7b 100644 --- a/lib/commands/CONFIG_RESETSTAT.ts +++ b/lib/commands/CONFIG_RESETSTAT.ts @@ -1,7 +1,5 @@ -import { transformReplyString } from './generic-transformers'; - export function transformArguments(): Array { return ['CONFIG', 'RESETSTAT']; } -export const transformReply = transformReplyString; +export declare function transformReply(): string; diff --git a/lib/commands/CONFIG_REWRITE.ts b/lib/commands/CONFIG_REWRITE.ts index 06247517128..67984adf300 100644 --- a/lib/commands/CONFIG_REWRITE.ts +++ b/lib/commands/CONFIG_REWRITE.ts @@ -1,7 +1,5 @@ -import { transformReplyString } from './generic-transformers'; - export function transformArguments(): Array { return ['CONFIG', 'REWRITE']; } -export const transformReply = transformReplyString; +export declare function transformReply(): string; diff --git a/lib/commands/CONFIG_SET.ts b/lib/commands/CONFIG_SET.ts index 894a95cb1cc..ec09e4469fa 100644 --- a/lib/commands/CONFIG_SET.ts +++ b/lib/commands/CONFIG_SET.ts @@ -1,7 +1,5 @@ -import { transformReplyString } from './generic-transformers'; - export function transformArguments(parameter: string, value: string): Array { return ['CONFIG', 'SET', parameter, value]; } -export const transformReply = transformReplyString; +export declare function transformReply(): string; diff --git a/lib/commands/DBSIZE.ts b/lib/commands/DBSIZE.ts index 72933930f70..6b442ec33a2 100644 --- a/lib/commands/DBSIZE.ts +++ b/lib/commands/DBSIZE.ts @@ -1,9 +1,7 @@ -import { transformReplyNumber } from './generic-transformers'; - export const IS_READ_ONLY = true; export function transformArguments(): Array { return ['DBSIZE']; } -export const transformReply = transformReplyNumber; +export declare function transformReply(): number; diff --git a/lib/commands/DECR.ts b/lib/commands/DECR.ts index cac6e07f053..e30d2aaf29c 100644 --- a/lib/commands/DECR.ts +++ b/lib/commands/DECR.ts @@ -1,9 +1,7 @@ -import { transformReplyNumber } from './generic-transformers'; - export const FIRST_KEY_INDEX = 1; export function transformArguments(key: string): Array { return ['DECR', key]; } -export const transformReply = transformReplyNumber; +export declare function transformReply(): number; diff --git a/lib/commands/DECRBY.ts b/lib/commands/DECRBY.ts index cc163cbe824..561eb9491c4 100644 --- a/lib/commands/DECRBY.ts +++ b/lib/commands/DECRBY.ts @@ -1,9 +1,7 @@ -import { transformReplyNumber } from './generic-transformers'; - export const FIRST_KEY_INDEX = 1; export function transformArguments(key: string, decrement: number): Array { return ['DECRBY', key, decrement.toString()]; } -export const transformReply = transformReplyNumber; +export declare function transformReply(): number; diff --git a/lib/commands/DEL.ts b/lib/commands/DEL.ts index f96b6988f1c..b815258df1b 100644 --- a/lib/commands/DEL.ts +++ b/lib/commands/DEL.ts @@ -1,8 +1,8 @@ import { TransformArgumentsReply } from '.'; -import { pushVerdictArguments, transformReplyNumber } from './generic-transformers'; +import { pushVerdictArguments } from './generic-transformers'; export function transformArguments(keys: string | Array): TransformArgumentsReply { return pushVerdictArguments(['DEL'], keys); } -export const transformReply = transformReplyNumber; +export declare function transformReply(): number; diff --git a/lib/commands/DISCARD.ts b/lib/commands/DISCARD.ts index b5aaf45cc8d..444f800db80 100644 --- a/lib/commands/DISCARD.ts +++ b/lib/commands/DISCARD.ts @@ -1,7 +1,5 @@ -import { transformReplyString } from './generic-transformers'; - export function transformArguments(): Array { return ['DISCARD']; } -export const transformReply = transformReplyString; +export declare function transformReply(): string; diff --git a/lib/commands/DUMP.ts b/lib/commands/DUMP.ts index 1c72110f21f..de85b889bb9 100644 --- a/lib/commands/DUMP.ts +++ b/lib/commands/DUMP.ts @@ -1,7 +1,5 @@ -import { transformReplyString } from './generic-transformers'; - export function transformArguments(key: string): Array { return ['DUMP', key]; } -export const transformReply = transformReplyString; +export declare function transformReply(): string; diff --git a/lib/commands/ECHO.ts b/lib/commands/ECHO.ts index 007b8f27646..75a91d4ac91 100644 --- a/lib/commands/ECHO.ts +++ b/lib/commands/ECHO.ts @@ -1,9 +1,7 @@ -import { transformReplyString } from './generic-transformers'; - export const IS_READ_ONLY = true; export function transformArguments(message: string): Array { return ['ECHO', message]; } -export const transformReply = transformReplyString; +export declare function transformReply(): string; diff --git a/lib/commands/FAILOVER.ts b/lib/commands/FAILOVER.ts index 11ccb32a5cf..c31dbc063de 100644 --- a/lib/commands/FAILOVER.ts +++ b/lib/commands/FAILOVER.ts @@ -1,5 +1,3 @@ -import { transformReplyString } from './generic-transformers'; - interface FailoverOptions { TO?: { host: string; @@ -32,4 +30,4 @@ export function transformArguments(options?: FailoverOptions): Array { return args; } -export const transformReply = transformReplyString; +export declare function transformReply(): string; diff --git a/lib/commands/FLUSHALL.ts b/lib/commands/FLUSHALL.ts index 4be3474f7e2..967096bb9bd 100644 --- a/lib/commands/FLUSHALL.ts +++ b/lib/commands/FLUSHALL.ts @@ -1,5 +1,3 @@ -import { transformReplyString } from './generic-transformers'; - export enum RedisFlushModes { ASYNC = 'ASYNC', SYNC = 'SYNC' @@ -15,4 +13,4 @@ export function transformArguments(mode?: RedisFlushModes): Array { return args; } -export const transformReply = transformReplyString; +export declare function transformReply(): string; diff --git a/lib/commands/FLUSHDB.ts b/lib/commands/FLUSHDB.ts index a85c0933c4c..5b8060df9eb 100644 --- a/lib/commands/FLUSHDB.ts +++ b/lib/commands/FLUSHDB.ts @@ -1,5 +1,4 @@ import { RedisFlushModes } from './FLUSHALL'; -import { transformReplyString } from './generic-transformers'; export function transformArguments(mode?: RedisFlushModes): Array { const args = ['FLUSHDB']; @@ -11,4 +10,4 @@ export function transformArguments(mode?: RedisFlushModes): Array { return args; } -export const transformReply = transformReplyString; +export declare function transformReply(): string; diff --git a/lib/commands/GEOADD.ts b/lib/commands/GEOADD.ts index 1236563d541..7f5ac5533e3 100644 --- a/lib/commands/GEOADD.ts +++ b/lib/commands/GEOADD.ts @@ -1,4 +1,4 @@ -import { GeoCoordinates, transformReplyNumber } from './generic-transformers'; +import { GeoCoordinates } from './generic-transformers'; interface GeoMember extends GeoCoordinates { member: string; @@ -46,4 +46,4 @@ export function transformArguments(key: string, toAdd: GeoMember | Array): return pushVerdictArguments(['GEOHASH', key], member); } -export const transformReply = transformReplyStringArray; +export declare function transformReply(): Array; diff --git a/lib/commands/GEOSEARCH.ts b/lib/commands/GEOSEARCH.ts index 3872f11c6cb..5453a2ae1b1 100644 --- a/lib/commands/GEOSEARCH.ts +++ b/lib/commands/GEOSEARCH.ts @@ -1,4 +1,4 @@ -import { GeoSearchFrom, GeoSearchBy, GeoSearchOptions, pushGeoSearchArguments, transformReplyStringArray } from './generic-transformers'; +import { GeoSearchFrom, GeoSearchBy, GeoSearchOptions, pushGeoSearchArguments } from './generic-transformers'; export const FIRST_KEY_INDEX = 1; @@ -13,4 +13,4 @@ export function transformArguments( return pushGeoSearchArguments(['GEOSEARCH'], key, from, by, options); } -export const transformReply = transformReplyStringArray; +export declare function transformReply(): Array; diff --git a/lib/commands/GET.ts b/lib/commands/GET.ts index 541790e54e4..dbd303d1c65 100644 --- a/lib/commands/GET.ts +++ b/lib/commands/GET.ts @@ -1,5 +1,4 @@ import { TransformArgumentsReply } from '.'; -import { transformReplyStringNull } from './generic-transformers'; export const FIRST_KEY_INDEX = 1; @@ -9,4 +8,4 @@ export function transformArguments(key: string | Buffer): TransformArgumentsRepl return ['GET', key]; } -export const transformReply = transformReplyStringNull; +export declare function transformReply(): string | null; diff --git a/lib/commands/GETBIT.ts b/lib/commands/GETBIT.ts index c7e878f75a8..1e5fd884251 100644 --- a/lib/commands/GETBIT.ts +++ b/lib/commands/GETBIT.ts @@ -1,4 +1,4 @@ -import { transformReplyBit } from './generic-transformers'; +import { BitValue } from './generic-transformers'; export const FIRST_KEY_INDEX = 1; @@ -8,4 +8,4 @@ export function transformArguments(key: string, offset: number): Array { return ['GETBIT', key, offset.toString()]; } -export const transformReply = transformReplyBit; +export declare function transformReply(): BitValue; diff --git a/lib/commands/GETDEL.ts b/lib/commands/GETDEL.ts index 218e057637d..de99cc6357b 100644 --- a/lib/commands/GETDEL.ts +++ b/lib/commands/GETDEL.ts @@ -1,9 +1,7 @@ -import { transformReplyStringNull } from './generic-transformers'; - export const FIRST_KEY_INDEX = 1; export function transformArguments(key: string): Array { return ['GETDEL', key]; } -export const transformReply = transformReplyStringNull; +export declare function transformReply(): string | null; diff --git a/lib/commands/GETEX.ts b/lib/commands/GETEX.ts index 214dae5c7ab..2c6a4f243f6 100644 --- a/lib/commands/GETEX.ts +++ b/lib/commands/GETEX.ts @@ -1,5 +1,5 @@ import { TransformArgumentsReply } from '.'; -import { transformEXAT, transformPXAT, transformReplyStringNull } from './generic-transformers'; +import { transformEXAT, transformPXAT } from './generic-transformers'; export const FIRST_KEY_INDEX = 1; @@ -33,4 +33,4 @@ export function transformArguments(key: string, mode: GetExModes): TransformArgu return args; } -export const transformReply = transformReplyStringNull; +export declare function transformReply(): string | null; diff --git a/lib/commands/GETRANGE.ts b/lib/commands/GETRANGE.ts index 9488dd53d5e..babb0a6a7c2 100644 --- a/lib/commands/GETRANGE.ts +++ b/lib/commands/GETRANGE.ts @@ -1,5 +1,3 @@ -import { transformReplyString } from './generic-transformers'; - export const FIRST_KEY_INDEX = 1; export const IS_READ_ONLY = true; @@ -8,4 +6,4 @@ export function transformArguments(key: string, start: number, end: number): Arr return ['GETRANGE', key, start.toString(), end.toString()]; } -export const transformReply = transformReplyString; +export declare function transformReply(): string; diff --git a/lib/commands/GETSET.ts b/lib/commands/GETSET.ts index 1b9b9d6bc75..4d3516866fb 100644 --- a/lib/commands/GETSET.ts +++ b/lib/commands/GETSET.ts @@ -1,9 +1,7 @@ -import { transformReplyStringNull } from './generic-transformers'; - export const FIRST_KEY_INDEX = 1; export function transformArguments(key: string, value: string): Array { return ['GETSET', key, value]; } -export const transformReply = transformReplyStringNull; +export declare function transformReply(): string | null; diff --git a/lib/commands/GET_BUFFER.ts b/lib/commands/GET_BUFFER.ts index 9d281961130..2f08ecb708a 100644 --- a/lib/commands/GET_BUFFER.ts +++ b/lib/commands/GET_BUFFER.ts @@ -1,7 +1,5 @@ -import { transformReplyBufferNull } from './generic-transformers'; - export { FIRST_KEY_INDEX, IS_READ_ONLY, transformArguments } from './GET'; export const BUFFER_MODE = true; -export const transformReply = transformReplyBufferNull; +export declare function transformReply(): Buffer | null; diff --git a/lib/commands/HDEL.ts b/lib/commands/HDEL.ts index 4785b0e67f9..75130c87239 100644 --- a/lib/commands/HDEL.ts +++ b/lib/commands/HDEL.ts @@ -1,5 +1,5 @@ import { TransformArgumentsReply } from '.'; -import { pushVerdictArguments, transformReplyNumber } from './generic-transformers'; +import { pushVerdictArguments } from './generic-transformers'; export const FIRST_KEY_INDEX = 1; @@ -7,4 +7,4 @@ export function transformArguments(key: string, field: string | Array): return pushVerdictArguments(['HDEL', key], field); } -export const transformReply = transformReplyNumber; +export declare function transformReply(): number; diff --git a/lib/commands/HINCRBY.ts b/lib/commands/HINCRBY.ts index 192dac456e1..8f0e99d41f4 100644 --- a/lib/commands/HINCRBY.ts +++ b/lib/commands/HINCRBY.ts @@ -1,9 +1,7 @@ -import { transformReplyNumber } from './generic-transformers'; - export const FIRST_KEY_INDEX = 1; export function transformArguments(key: string, field: string, increment: number): Array { return ['HINCRBY', key, field, increment.toString()]; } -export const transformReply = transformReplyNumber; +export declare function transformReply(): number; diff --git a/lib/commands/HINCRBYFLOAT.ts b/lib/commands/HINCRBYFLOAT.ts index 10c949b8d93..262a7d5d6b0 100644 --- a/lib/commands/HINCRBYFLOAT.ts +++ b/lib/commands/HINCRBYFLOAT.ts @@ -1,9 +1,7 @@ -import { transformReplyNumber } from './generic-transformers'; - export const FIRST_KEY_INDEX = 1; export function transformArguments(key: string, field: string, increment: number): Array { return ['HINCRBYFLOAT', key, field, increment.toString()]; } -export const transformReply = transformReplyNumber; +export declare function transformReply(): number; diff --git a/lib/commands/HKEYS.ts b/lib/commands/HKEYS.ts index d79d2c1d134..358f08fc762 100644 --- a/lib/commands/HKEYS.ts +++ b/lib/commands/HKEYS.ts @@ -1,9 +1,7 @@ -import { transformReplyStringArray } from './generic-transformers'; - export const FIRST_KEY_INDEX = 1; export function transformArguments(key: string): Array { return ['HKEYS', key]; } -export const transformReply = transformReplyStringArray; +export declare function transformReply(): Array; diff --git a/lib/commands/HLEN.ts b/lib/commands/HLEN.ts index ba7ccc3aed9..5c717ad7c54 100644 --- a/lib/commands/HLEN.ts +++ b/lib/commands/HLEN.ts @@ -1,9 +1,7 @@ -import { transformReplyNumber } from './generic-transformers'; - export const FIRST_KEY_INDEX = 1; export function transformArguments(key: string): Array { return ['HLEN', key]; } -export const transformReply = transformReplyNumber; +export declare function transformReply(): number; diff --git a/lib/commands/HMGET.ts b/lib/commands/HMGET.ts index 9f26eeba640..420102d2b27 100644 --- a/lib/commands/HMGET.ts +++ b/lib/commands/HMGET.ts @@ -1,5 +1,5 @@ import { TransformArgumentsReply } from '.'; -import { pushVerdictArguments, transformReplyStringArray } from './generic-transformers'; +import { pushVerdictArguments } from './generic-transformers'; export const FIRST_KEY_INDEX = 1; @@ -9,4 +9,4 @@ export function transformArguments(key: string, fields: string | Array): return pushVerdictArguments(['HMGET', key], fields); } -export const transformReply = transformReplyStringArray; +export declare function transformReply(): Array; diff --git a/lib/commands/HRANDFIELD.ts b/lib/commands/HRANDFIELD.ts index e0c6ee392d5..24ca9b83d56 100644 --- a/lib/commands/HRANDFIELD.ts +++ b/lib/commands/HRANDFIELD.ts @@ -1,9 +1,7 @@ -import { transformReplyStringNull } from './generic-transformers'; - export const FIRST_KEY_INDEX = 1; export function transformArguments(key: string): Array { return ['HRANDFIELD', key]; } -export const transformReply = transformReplyStringNull; \ No newline at end of file +export declare function transformReply(): string | null; diff --git a/lib/commands/HRANDFIELD_COUNT.ts b/lib/commands/HRANDFIELD_COUNT.ts index d615b86ee8b..c0a8b1d449f 100644 --- a/lib/commands/HRANDFIELD_COUNT.ts +++ b/lib/commands/HRANDFIELD_COUNT.ts @@ -1,4 +1,3 @@ -import { transformReplyStringArray } from './generic-transformers'; import { transformArguments as transformHRandFieldArguments } from './HRANDFIELD'; export { FIRST_KEY_INDEX } from './HRANDFIELD'; @@ -10,4 +9,4 @@ export function transformArguments(key: string, count: number): Array { ]; } -export const transformReply = transformReplyStringArray; +export declare function transformReply(): Array; diff --git a/lib/commands/HSET.ts b/lib/commands/HSET.ts index cbd46061ad8..1aecd50c0d0 100644 --- a/lib/commands/HSET.ts +++ b/lib/commands/HSET.ts @@ -1,5 +1,4 @@ import { TransformArgumentsReply } from '.'; -import { transformReplyString } from './generic-transformers'; type HSETObject = Record; @@ -47,4 +46,4 @@ function pushObject(args: Array, object: HSETObject): void { } } -export const transformReply = transformReplyString; +export declare function transformReply(): string; diff --git a/lib/commands/HSTRLEN.ts b/lib/commands/HSTRLEN.ts index 4181cde8517..d0138eb3ec9 100644 --- a/lib/commands/HSTRLEN.ts +++ b/lib/commands/HSTRLEN.ts @@ -1,9 +1,7 @@ -import { transformReplyNumber } from './generic-transformers'; - export const FIRST_KEY_INDEX = 1; export function transformArguments(key: string, field: string): Array { return ['HSTRLEN', key, field]; } -export const transformReply = transformReplyNumber; +export declare function transformReply(): number; diff --git a/lib/commands/HVALS.ts b/lib/commands/HVALS.ts index 7f924623cf3..cb17fdb29be 100644 --- a/lib/commands/HVALS.ts +++ b/lib/commands/HVALS.ts @@ -1,9 +1,7 @@ -import { transformReplyStringArray } from './generic-transformers'; - export const FIRST_KEY_INDEX = 1; export function transformArguments(key: string): Array { return ['HVALS', key]; } -export const transformReply = transformReplyStringArray; +export declare function transformReply(): Array; diff --git a/lib/commands/INCR.ts b/lib/commands/INCR.ts index 00747f0f7e2..f7b81013258 100644 --- a/lib/commands/INCR.ts +++ b/lib/commands/INCR.ts @@ -1,9 +1,7 @@ -import { transformReplyNumber } from './generic-transformers'; - export const FIRST_KEY_INDEX = 1; export function transformArguments(key: string): Array { return ['INCR', key]; } -export const transformReply = transformReplyNumber; +export declare function transformReply(): number; diff --git a/lib/commands/INCRBY.ts b/lib/commands/INCRBY.ts index 8fd31d03380..8f2a4406bf9 100644 --- a/lib/commands/INCRBY.ts +++ b/lib/commands/INCRBY.ts @@ -1,9 +1,7 @@ -import { transformReplyNumber } from './generic-transformers'; - export const FIRST_KEY_INDEX = 1; export function transformArguments(key: string, increment: number): Array { return ['INCRBY', key, increment.toString()]; } -export const transformReply = transformReplyNumber; +export declare function transformReply(): number; diff --git a/lib/commands/INCRBYFLOAT.ts b/lib/commands/INCRBYFLOAT.ts index 38912cbdc94..a5f99820cb3 100644 --- a/lib/commands/INCRBYFLOAT.ts +++ b/lib/commands/INCRBYFLOAT.ts @@ -1,9 +1,7 @@ -import { transformReplyString } from './generic-transformers'; - export const FIRST_KEY_INDEX = 1; export function transformArguments(key: string, increment: number): Array { return ['INCRBYFLOAT', key, increment.toString()]; } -export const transformReply = transformReplyString; +export declare function transformReply(): string; diff --git a/lib/commands/INFO.ts b/lib/commands/INFO.ts index 437b5e5b83b..8ab24221b26 100644 --- a/lib/commands/INFO.ts +++ b/lib/commands/INFO.ts @@ -1,5 +1,3 @@ -import { transformReplyString } from './generic-transformers'; - export const IS_READ_ONLY = true; export function transformArguments(section?: string): Array { @@ -12,4 +10,4 @@ export function transformArguments(section?: string): Array { return args; } -export const transformReply = transformReplyString; +export declare function transformReply(): string; diff --git a/lib/commands/LINDEX.ts b/lib/commands/LINDEX.ts index 0237a4705b1..9a89b41da55 100644 --- a/lib/commands/LINDEX.ts +++ b/lib/commands/LINDEX.ts @@ -1,5 +1,3 @@ -import { transformReplyNumberNull } from './generic-transformers'; - export const FIRST_KEY_INDEX = 1; export const IS_READ_ONLY = true; @@ -8,4 +6,4 @@ export function transformArguments(key: string, element: string): Array return ['LINDEX', key, element]; } -export const transformReply = transformReplyNumberNull; +export declare function transformReply(): number | null; diff --git a/lib/commands/LINSERT.ts b/lib/commands/LINSERT.ts index 40bd4e3d4df..b1d377f92aa 100644 --- a/lib/commands/LINSERT.ts +++ b/lib/commands/LINSERT.ts @@ -1,5 +1,3 @@ -import { transformReplyNumber } from './generic-transformers'; - export const FIRST_KEY_INDEX = 1; type LInsertPosition = 'BEFORE' | 'AFTER'; @@ -19,4 +17,4 @@ export function transformArguments( ]; } -export const transformReply = transformReplyNumber; +export declare function transformReply(): number; diff --git a/lib/commands/LLEN.ts b/lib/commands/LLEN.ts index 61aae604c97..49ac1d1916c 100644 --- a/lib/commands/LLEN.ts +++ b/lib/commands/LLEN.ts @@ -1,5 +1,3 @@ -import { transformReplyNumber } from './generic-transformers'; - export const FIRST_KEY_INDEX = 1; export const IS_READ_ONLY = true; @@ -8,4 +6,4 @@ export function transformArguments(key: string): Array { return ['LLEN', key]; } -export const transformReply = transformReplyNumber; \ No newline at end of file +export declare function transformReply(): number; diff --git a/lib/commands/LMOVE.ts b/lib/commands/LMOVE.ts index 1e99297d812..111e758a0a4 100644 --- a/lib/commands/LMOVE.ts +++ b/lib/commands/LMOVE.ts @@ -1,5 +1,3 @@ -import { transformReplyStringNull } from './generic-transformers'; - export type LMoveSide = 'LEFT' | 'RIGHT'; export const FIRST_KEY_INDEX = 1; @@ -19,4 +17,4 @@ export function transformArguments( ]; } -export const transformReply = transformReplyStringNull; +export declare function transformReply(): string | null; diff --git a/lib/commands/LOLWUT.ts b/lib/commands/LOLWUT.ts index f0cd20d4471..cfe01adbcf2 100644 --- a/lib/commands/LOLWUT.ts +++ b/lib/commands/LOLWUT.ts @@ -1,5 +1,3 @@ -import { transformReplyString } from './generic-transformers'; - export const IS_READ_ONLY = true; export function transformArguments(version?: number, ...optionalArguments: Array): Array { @@ -16,4 +14,4 @@ export function transformArguments(version?: number, ...optionalArguments: Array return args; } -export const transformReply = transformReplyString; +export declare function transformReply(): string; diff --git a/lib/commands/LPOP.ts b/lib/commands/LPOP.ts index 30595a5491a..9bf340e07c9 100644 --- a/lib/commands/LPOP.ts +++ b/lib/commands/LPOP.ts @@ -1,9 +1,7 @@ -import { transformReplyStringNull } from './generic-transformers'; - export const FIRST_KEY_INDEX = 1; export function transformArguments(key: string): Array { return ['LPOP', key]; } -export const transformReply = transformReplyStringNull; +export declare function transformReply(): string | null; diff --git a/lib/commands/LPOP_COUNT.ts b/lib/commands/LPOP_COUNT.ts index 432d2c47c09..828b0251ff6 100644 --- a/lib/commands/LPOP_COUNT.ts +++ b/lib/commands/LPOP_COUNT.ts @@ -1,9 +1,7 @@ -import { transformReplyStringArrayNull } from './generic-transformers'; - export const FIRST_KEY_INDEX = 1; export function transformArguments(key: string, count: number): Array { return ['LPOP', key, count.toString()]; } -export const transformReply = transformReplyStringArrayNull; +export declare function transformReply(): Array | null; diff --git a/lib/commands/LPOS.ts b/lib/commands/LPOS.ts index fc160dbcbb8..3371f01d67e 100644 --- a/lib/commands/LPOS.ts +++ b/lib/commands/LPOS.ts @@ -1,5 +1,3 @@ -import { transformReplyNumberNull } from './generic-transformers'; - export const FIRST_KEY_INDEX = 1; export const IS_READ_ONLY = true; @@ -23,4 +21,4 @@ export function transformArguments(key: string, element: string, options?: LPosO return args; } -export const transformReply = transformReplyNumberNull; +export declare function transformReply(): number | null; diff --git a/lib/commands/LPOS_COUNT.ts b/lib/commands/LPOS_COUNT.ts index 2a1d3068583..b5a60317eae 100644 --- a/lib/commands/LPOS_COUNT.ts +++ b/lib/commands/LPOS_COUNT.ts @@ -1,4 +1,3 @@ -import { transformReplyNumberArray } from './generic-transformers'; import { LPosOptions } from './LPOS'; export { FIRST_KEY_INDEX, IS_READ_ONLY } from './LPOS'; @@ -19,4 +18,4 @@ export function transformArguments(key: string, element: string, count: number, return args; } -export const transformReply = transformReplyNumberArray; +export declare function transformReply(): Array; diff --git a/lib/commands/LPUSH.ts b/lib/commands/LPUSH.ts index 7416d4946ea..349affb5e09 100644 --- a/lib/commands/LPUSH.ts +++ b/lib/commands/LPUSH.ts @@ -1,9 +1,9 @@ import { TransformArgumentsReply } from '.'; -import { pushVerdictArguments, transformReplyNumber } from './generic-transformers'; +import { pushVerdictArguments } from './generic-transformers'; export const FIRST_KEY_INDEX = 1; export function transformArguments(key: string, elements: string | Array): TransformArgumentsReply { return pushVerdictArguments(['LPUSH', key], elements);} -export const transformReply = transformReplyNumber; +export declare function transformReply(): number; diff --git a/lib/commands/LPUSHX.ts b/lib/commands/LPUSHX.ts index f89623ace3a..23b8bd9b91b 100644 --- a/lib/commands/LPUSHX.ts +++ b/lib/commands/LPUSHX.ts @@ -1,5 +1,5 @@ import { TransformArgumentsReply } from '.'; -import { pushVerdictArguments, transformReplyNumber } from './generic-transformers'; +import { pushVerdictArguments } from './generic-transformers'; export const FIRST_KEY_INDEX = 1; @@ -7,4 +7,4 @@ export function transformArguments(key: string, element: string | Array) return pushVerdictArguments(['LPUSHX', key], element); } -export const transformReply = transformReplyNumber; +export declare function transformReply(): number; diff --git a/lib/commands/LRANGE.ts b/lib/commands/LRANGE.ts index cbed9a75ded..7ea97ec4f43 100644 --- a/lib/commands/LRANGE.ts +++ b/lib/commands/LRANGE.ts @@ -1,5 +1,3 @@ -import { transformReplyStringArray } from './generic-transformers'; - export const FIRST_KEY_INDEX = 1; export const IS_READ_ONLY = true; @@ -13,4 +11,4 @@ export function transformArguments(key: string, start: number, stop: number): Ar ]; } -export const transformReply = transformReplyStringArray; +export declare function transformReply(): Array; diff --git a/lib/commands/LREM.ts b/lib/commands/LREM.ts index 5eabbc9194e..b43f8a28427 100644 --- a/lib/commands/LREM.ts +++ b/lib/commands/LREM.ts @@ -1,5 +1,3 @@ -import { transformReplyNumber } from './generic-transformers'; - export const FIRST_KEY_INDEX = 1; export function transformArguments(key: string, count: number, element: string): Array { @@ -11,4 +9,4 @@ export function transformArguments(key: string, count: number, element: string): ]; } -export const transformReply = transformReplyNumber; +export declare function transformReply(): number; diff --git a/lib/commands/LSET.ts b/lib/commands/LSET.ts index 0e910dd6a1c..1511fb9751e 100644 --- a/lib/commands/LSET.ts +++ b/lib/commands/LSET.ts @@ -1,5 +1,3 @@ -import { transformReplyString } from './generic-transformers'; - export const FIRST_KEY_INDEX = 1; export function transformArguments(key: string, index: number, element: string): Array { @@ -11,4 +9,4 @@ export function transformArguments(key: string, index: number, element: string): ]; } -export const transformReply = transformReplyString; +export declare function transformReply(): string; diff --git a/lib/commands/LTRIM.ts b/lib/commands/LTRIM.ts index 3ccfa751af6..018afd90a15 100644 --- a/lib/commands/LTRIM.ts +++ b/lib/commands/LTRIM.ts @@ -1,5 +1,3 @@ -import { transformReplyString } from './generic-transformers'; - export const FIRST_KEY_INDEX = 1; export function transformArguments(key: string, start: number, stop: number): Array { @@ -11,4 +9,4 @@ export function transformArguments(key: string, start: number, stop: number): Ar ] } -export const transformReply = transformReplyString; +export declare function transformReply(): string; diff --git a/lib/commands/MEMORY_DOCTOR.ts b/lib/commands/MEMORY_DOCTOR.ts index 0d02bf93360..95a37246ffa 100644 --- a/lib/commands/MEMORY_DOCTOR.ts +++ b/lib/commands/MEMORY_DOCTOR.ts @@ -1,7 +1,5 @@ -import { transformReplyString } from './generic-transformers'; - export function transformArguments(): Array { return ['MEMORY', 'DOCTOR']; } -export const transformReply = transformReplyString; +export declare function transformReply(): string; diff --git a/lib/commands/MEMORY_MALLOC-STATS.ts b/lib/commands/MEMORY_MALLOC-STATS.ts index 7dd997c48b1..3977e3a1de4 100644 --- a/lib/commands/MEMORY_MALLOC-STATS.ts +++ b/lib/commands/MEMORY_MALLOC-STATS.ts @@ -1,7 +1,5 @@ -import { transformReplyString } from './generic-transformers'; - export function transformArguments(): Array { return ['MEMORY', 'MALLOC-STATS']; } -export const transformReply = transformReplyString; +export declare function transformReply(): string; diff --git a/lib/commands/MEMORY_PURGE.ts b/lib/commands/MEMORY_PURGE.ts index 7aaeee7e6aa..cfa38179273 100644 --- a/lib/commands/MEMORY_PURGE.ts +++ b/lib/commands/MEMORY_PURGE.ts @@ -1,7 +1,5 @@ -import { transformReplyString } from './generic-transformers'; - export function transformArguments(): Array { return ['MEMORY', 'PURGE']; } -export const transformReply = transformReplyString; +export declare function transformReply(): string; diff --git a/lib/commands/MEMORY_USAGE.ts b/lib/commands/MEMORY_USAGE.ts index 0868b162268..959cdb0a0c4 100644 --- a/lib/commands/MEMORY_USAGE.ts +++ b/lib/commands/MEMORY_USAGE.ts @@ -1,5 +1,3 @@ -import { transformReplyNumberNull } from './generic-transformers'; - export const FIRST_KEY_INDEX = 1; export const IS_READ_ONLY = true; @@ -18,4 +16,4 @@ export function transformArguments(key: string, options?: MemoryUsageOptions): A return args; } -export const transformReply = transformReplyNumberNull; +export declare function transformReply(): number | null; diff --git a/lib/commands/MGET.ts b/lib/commands/MGET.ts index fdf5b3dde85..6d5b9073d49 100644 --- a/lib/commands/MGET.ts +++ b/lib/commands/MGET.ts @@ -1,5 +1,3 @@ -import { transformReplyStringNullArray } from './generic-transformers'; - export const FIRST_KEY_INDEX = 1; export const IS_READ_ONLY = true; @@ -8,4 +6,4 @@ export function transformArguments(keys: Array): Array { return ['MGET', ...keys]; } -export const transformReply = transformReplyStringNullArray; +export declare function transformReply(): Array; diff --git a/lib/commands/MIGRATE.ts b/lib/commands/MIGRATE.ts index 14dbe741be2..4d2795123a8 100644 --- a/lib/commands/MIGRATE.ts +++ b/lib/commands/MIGRATE.ts @@ -1,5 +1,4 @@ import { AuthOptions } from './AUTH'; -import { transformReplyString } from './generic-transformers'; interface MigrateOptions { COPY?: true; @@ -62,4 +61,4 @@ export function transformArguments( return args; } -export const transformReply = transformReplyString; +export declare function transformReply(): string; diff --git a/lib/commands/MODULE_LIST.ts b/lib/commands/MODULE_LIST.ts index 53ad14b68eb..d75b2428308 100644 --- a/lib/commands/MODULE_LIST.ts +++ b/lib/commands/MODULE_LIST.ts @@ -1,7 +1,5 @@ -import { transformReplyString } from './generic-transformers'; - export function transformArguments(): Array { return ['MODULE', 'LIST']; } -export const transformReply = transformReplyString; +export declare function transformReply(): string; diff --git a/lib/commands/MODULE_LOAD.ts b/lib/commands/MODULE_LOAD.ts index cd2347af24c..b44b4b57ce6 100644 --- a/lib/commands/MODULE_LOAD.ts +++ b/lib/commands/MODULE_LOAD.ts @@ -1,5 +1,3 @@ -import { transformReplyString } from './generic-transformers'; - export function transformArguments(path: string, moduleArgs?: Array): Array { const args = ['MODULE', 'LOAD', path]; @@ -10,4 +8,4 @@ export function transformArguments(path: string, moduleArgs?: Array): Ar return args; } -export const transformReply = transformReplyString; +export declare function transformReply(): string; diff --git a/lib/commands/MODULE_UNLOAD.ts b/lib/commands/MODULE_UNLOAD.ts index 3737784f000..d5927778fe6 100644 --- a/lib/commands/MODULE_UNLOAD.ts +++ b/lib/commands/MODULE_UNLOAD.ts @@ -1,7 +1,5 @@ -import { transformReplyString } from './generic-transformers'; - export function transformArguments(name: string): Array { return ['MODULE', 'UNLOAD', name]; } -export const transformReply = transformReplyString; +export declare function transformReply(): string; diff --git a/lib/commands/MSET.ts b/lib/commands/MSET.ts index d51790caeed..d3e290df70f 100644 --- a/lib/commands/MSET.ts +++ b/lib/commands/MSET.ts @@ -1,5 +1,3 @@ -import { transformReplyString } from './generic-transformers'; - export const FIRST_KEY_INDEX = 1; export function transformArguments(toSet: Array<[string, string]> | Array | Record): Array { @@ -16,4 +14,4 @@ export function transformArguments(toSet: Array<[string, string]> | Array): TransformArgume return pushVerdictArguments(['PFCOUNT'], key); } -export const transformReply = transformReplyNumber; +export declare function transformReply(): number; diff --git a/lib/commands/PFMERGE.ts b/lib/commands/PFMERGE.ts index c4ba11877f7..86bef6c4d7f 100644 --- a/lib/commands/PFMERGE.ts +++ b/lib/commands/PFMERGE.ts @@ -1,5 +1,5 @@ import { TransformArgumentsReply } from '.'; -import { pushVerdictArguments, transformReplyString } from './generic-transformers'; +import { pushVerdictArguments } from './generic-transformers'; export const FIRST_KEY_INDEX = 1; @@ -7,4 +7,4 @@ export function transformArguments(destination: string, source: string | Array { return ['PING']; } -export const transformReply = transformReplyString; +export declare function transformReply(): string; diff --git a/lib/commands/PSETEX.ts b/lib/commands/PSETEX.ts index 101030d2e63..a0bd4f5c229 100644 --- a/lib/commands/PSETEX.ts +++ b/lib/commands/PSETEX.ts @@ -1,5 +1,3 @@ -import { transformReplyString } from './generic-transformers'; - export const FIRST_KEY_INDEX = 1; export function transformArguments(key: string, milliseconds: number, value: string): Array { @@ -11,4 +9,4 @@ export function transformArguments(key: string, milliseconds: number, value: str ]; } -export const transformReply = transformReplyString; +export declare function transformReply(): string; diff --git a/lib/commands/PTTL.ts b/lib/commands/PTTL.ts index 8356c75bbd9..c1bc18323d5 100644 --- a/lib/commands/PTTL.ts +++ b/lib/commands/PTTL.ts @@ -1,5 +1,3 @@ -import { transformReplyNumber } from './generic-transformers'; - export const FIRST_KEY_INDEX = 1; export const IS_READ_ONLY = true; @@ -8,4 +6,4 @@ export function transformArguments(key: string): Array { return ['PTTL', key]; } -export const transformReply = transformReplyNumber; +export declare function transformReply(): number; diff --git a/lib/commands/PUBLISH.ts b/lib/commands/PUBLISH.ts index 51387a6803f..eda5234df20 100644 --- a/lib/commands/PUBLISH.ts +++ b/lib/commands/PUBLISH.ts @@ -1,7 +1,5 @@ -import { transformReplyNumber } from './generic-transformers'; - export function transformArguments(channel: string, message: string): Array { return ['PUBLISH', channel, message]; } -export const transformReply = transformReplyNumber; +export declare function transformReply(): number; diff --git a/lib/commands/PUBSUB_CHANNELS.ts b/lib/commands/PUBSUB_CHANNELS.ts index aa7a0749fc6..86a144ede8e 100644 --- a/lib/commands/PUBSUB_CHANNELS.ts +++ b/lib/commands/PUBSUB_CHANNELS.ts @@ -1,5 +1,3 @@ -import { transformReplyStringArray } from './generic-transformers'; - export const IS_READ_ONLY = true; export function transformArguments(pattern?: string): Array { @@ -12,4 +10,4 @@ export function transformArguments(pattern?: string): Array { return args; } -export const transformReply = transformReplyStringArray; +export declare function transformReply(): Array; diff --git a/lib/commands/PUBSUB_NUMPAT.ts b/lib/commands/PUBSUB_NUMPAT.ts index 966a8d237c7..15be6aa1b18 100644 --- a/lib/commands/PUBSUB_NUMPAT.ts +++ b/lib/commands/PUBSUB_NUMPAT.ts @@ -1,9 +1,7 @@ -import { transformReplyString } from './generic-transformers'; - export const IS_READ_ONLY = true; export function transformArguments(): Array { return ['PUBSUB', 'NUMPAT']; } -export const transformReply = transformReplyString; +export declare function transformReply(): string; diff --git a/lib/commands/READONLY.ts b/lib/commands/READONLY.ts index 00fbe4e4351..db7db881628 100644 --- a/lib/commands/READONLY.ts +++ b/lib/commands/READONLY.ts @@ -1,7 +1,5 @@ -import { transformReplyString } from './generic-transformers'; - export function transformArguments(): Array { return ['READONLY']; } -export const transformReply = transformReplyString; +export declare function transformReply(): string; diff --git a/lib/commands/READWRITE.ts b/lib/commands/READWRITE.ts index 16f95604407..60dc865e89e 100644 --- a/lib/commands/READWRITE.ts +++ b/lib/commands/READWRITE.ts @@ -1,7 +1,5 @@ -import { transformReplyString } from './generic-transformers'; - export function transformArguments(): Array { return ['READWRITE']; } -export const transformReply = transformReplyString; +export declare function transformReply(): string; diff --git a/lib/commands/RENAME.ts b/lib/commands/RENAME.ts index 0f9582677f1..f2affada60b 100644 --- a/lib/commands/RENAME.ts +++ b/lib/commands/RENAME.ts @@ -1,9 +1,7 @@ -import { transformReplyString } from './generic-transformers'; - export const FIRST_KEY_INDEX = 1; export function transformArguments(key: string, newKey: string): Array { return ['RENAME', key, newKey]; } -export const transformReply = transformReplyString; +export declare function transformReply(): string; diff --git a/lib/commands/REPLICAOF.ts b/lib/commands/REPLICAOF.ts index 0b56bd74dc6..bd452e0f371 100644 --- a/lib/commands/REPLICAOF.ts +++ b/lib/commands/REPLICAOF.ts @@ -1,7 +1,5 @@ -import { transformReplyString } from './generic-transformers'; - export function transformArguments(host: string, port: number): Array { return ['REPLICAOF', host, port.toString()]; } -export const transformReply = transformReplyString; +export declare function transformReply(): string; diff --git a/lib/commands/RESTORE-ASKING.ts b/lib/commands/RESTORE-ASKING.ts index 4d178cb1f0b..d53d8541cd7 100644 --- a/lib/commands/RESTORE-ASKING.ts +++ b/lib/commands/RESTORE-ASKING.ts @@ -1,7 +1,5 @@ -import { transformReplyString } from './generic-transformers'; - export function transformArguments(): Array { return ['RESTORE-ASKING']; } -export const transformReply = transformReplyString; +export declare function transformReply(): string; diff --git a/lib/commands/RPOP.ts b/lib/commands/RPOP.ts index daccbf5d42d..96735dea8b9 100644 --- a/lib/commands/RPOP.ts +++ b/lib/commands/RPOP.ts @@ -1,9 +1,7 @@ -import { transformReplyStringNull } from './generic-transformers'; - export const FIRST_KEY_INDEX = 1; export function transformArguments(key: string): Array { return ['RPOP', key]; } -export const transformReply = transformReplyStringNull; +export declare function transformReply(): string | null; diff --git a/lib/commands/RPOPLPUSH.ts b/lib/commands/RPOPLPUSH.ts index db388906d3e..23f1ff08766 100644 --- a/lib/commands/RPOPLPUSH.ts +++ b/lib/commands/RPOPLPUSH.ts @@ -1,9 +1,7 @@ -import { transformReplyNumberNull } from './generic-transformers'; - export const FIRST_KEY_INDEX = 1; export function transformArguments(source: string, destination: string): Array { return ['RPOPLPUSH', source, destination]; } -export const transformReply = transformReplyNumberNull; +export declare function transformReply(): number | null; diff --git a/lib/commands/RPOP_COUNT.ts b/lib/commands/RPOP_COUNT.ts index 205704274f7..f7f3463a3ee 100644 --- a/lib/commands/RPOP_COUNT.ts +++ b/lib/commands/RPOP_COUNT.ts @@ -1,9 +1,7 @@ -import { transformReplyStringArrayNull } from './generic-transformers'; - export const FIRST_KEY_INDEX = 1; export function transformArguments(key: string, count: number): Array { return ['RPOP', key, count.toString()]; } -export const transformReply = transformReplyStringArrayNull; +export declare function transformReply(): Array | null; diff --git a/lib/commands/RPUSH.ts b/lib/commands/RPUSH.ts index 665094f47a5..92df52f03ff 100644 --- a/lib/commands/RPUSH.ts +++ b/lib/commands/RPUSH.ts @@ -1,5 +1,5 @@ import { TransformArgumentsReply } from '.'; -import { pushVerdictArguments, transformReplyNumber } from './generic-transformers'; +import { pushVerdictArguments } from './generic-transformers'; export const FIRST_KEY_INDEX = 1; @@ -7,4 +7,4 @@ export function transformArguments(key: string, element: string | Array) return pushVerdictArguments(['RPUSH', key], element); } -export const transformReply = transformReplyNumber; +export declare function transformReply(): number; diff --git a/lib/commands/RPUSHX.ts b/lib/commands/RPUSHX.ts index fe1f969f3f6..14ad9dc9d50 100644 --- a/lib/commands/RPUSHX.ts +++ b/lib/commands/RPUSHX.ts @@ -1,5 +1,5 @@ import { TransformArgumentsReply } from '.'; -import { pushVerdictArguments, transformReplyNumber } from './generic-transformers'; +import { pushVerdictArguments } from './generic-transformers'; export const FIRST_KEY_INDEX = 1; @@ -7,4 +7,4 @@ export function transformArguments(key: string, element: string | Array) return pushVerdictArguments(['RPUSHX', key], element); } -export const transformReply = transformReplyNumber; +export declare function transformReply(): number; diff --git a/lib/commands/SADD.ts b/lib/commands/SADD.ts index a432ccfef59..31623c435ce 100644 --- a/lib/commands/SADD.ts +++ b/lib/commands/SADD.ts @@ -1,5 +1,5 @@ import { TransformArgumentsReply } from '.'; -import { pushVerdictArguments, transformReplyNumber } from './generic-transformers'; +import { pushVerdictArguments } from './generic-transformers'; export const FIRST_KEY_INDEX = 1; @@ -7,4 +7,4 @@ export function transformArguments(key: string, members: string | Array) return pushVerdictArguments(['SADD', key], members); } -export const transformReply = transformReplyNumber; +export declare function transformReply(): number; diff --git a/lib/commands/SAVE.ts b/lib/commands/SAVE.ts index 38a397892f8..e88575f0a5c 100644 --- a/lib/commands/SAVE.ts +++ b/lib/commands/SAVE.ts @@ -1,7 +1,5 @@ -import { transformReplyString } from './generic-transformers'; - export function transformArguments(): Array { return ['SAVE']; } -export const transformReply = transformReplyString; +export declare function transformReply(): string; diff --git a/lib/commands/SCARD.ts b/lib/commands/SCARD.ts index 8a90bd3b029..0d3ce49b6b2 100644 --- a/lib/commands/SCARD.ts +++ b/lib/commands/SCARD.ts @@ -1,9 +1,7 @@ -import { transformReplyNumber } from './generic-transformers'; - export const FIRST_KEY_INDEX = 1; export function transformArguments(key: string): Array { return ['SCARD', key]; } -export const transformReply = transformReplyNumber; +export declare function transformReply(): number; diff --git a/lib/commands/SCRIPT_DEBUG.ts b/lib/commands/SCRIPT_DEBUG.ts index e93443a5860..e9e1e909d59 100644 --- a/lib/commands/SCRIPT_DEBUG.ts +++ b/lib/commands/SCRIPT_DEBUG.ts @@ -1,7 +1,5 @@ -import { transformReplyString } from './generic-transformers'; - export function transformArguments(mode: 'YES' | 'SYNC' | 'NO'): Array { return ['SCRIPT', 'DEBUG', mode]; } -export const transformReply = transformReplyString; +export declare function transformReply(): string; diff --git a/lib/commands/SCRIPT_FLUSH.ts b/lib/commands/SCRIPT_FLUSH.ts index 83bc9e2b5d8..2c220e9e3d1 100644 --- a/lib/commands/SCRIPT_FLUSH.ts +++ b/lib/commands/SCRIPT_FLUSH.ts @@ -1,5 +1,3 @@ -import { transformReplyString } from './generic-transformers'; - export function transformArguments(mode?: 'ASYNC' | 'SYNC'): Array { const args = ['SCRIPT', 'FLUSH']; @@ -10,4 +8,4 @@ export function transformArguments(mode?: 'ASYNC' | 'SYNC'): Array { return args; } -export const transformReply = transformReplyString; +export declare function transformReply(): string; diff --git a/lib/commands/SCRIPT_KILL.ts b/lib/commands/SCRIPT_KILL.ts index 5c175b74d6c..c0a53da8681 100644 --- a/lib/commands/SCRIPT_KILL.ts +++ b/lib/commands/SCRIPT_KILL.ts @@ -1,7 +1,5 @@ -import { transformReplyString } from './generic-transformers'; - export function transformArguments(): Array { return ['SCRIPT', 'KILL']; } -export const transformReply = transformReplyString; +export declare function transformReply(): string; diff --git a/lib/commands/SCRIPT_LOAD.ts b/lib/commands/SCRIPT_LOAD.ts index 378fbf1e76a..7cb28c1ec7f 100644 --- a/lib/commands/SCRIPT_LOAD.ts +++ b/lib/commands/SCRIPT_LOAD.ts @@ -1,7 +1,5 @@ -import { transformReplyString } from './generic-transformers'; - export function transformArguments(script: string): Array { return ['SCRIPT', 'LOAD', script]; } -export const transformReply = transformReplyString; +export declare function transformReply(): string; diff --git a/lib/commands/SDIFF.ts b/lib/commands/SDIFF.ts index 4d5aaea1a06..134bd66ef85 100644 --- a/lib/commands/SDIFF.ts +++ b/lib/commands/SDIFF.ts @@ -1,5 +1,5 @@ import { TransformArgumentsReply } from '.'; -import { pushVerdictArguments, transformReplyStringArray } from './generic-transformers'; +import { pushVerdictArguments } from './generic-transformers'; export const FIRST_KEY_INDEX = 1; @@ -7,4 +7,4 @@ export function transformArguments(keys: string | Array): TransformArgum return pushVerdictArguments(['SDIFF'], keys); } -export const transformReply = transformReplyStringArray; +export declare function transformReply(): Array; diff --git a/lib/commands/SDIFFSTORE.ts b/lib/commands/SDIFFSTORE.ts index 69883d4124c..1c437087c5e 100644 --- a/lib/commands/SDIFFSTORE.ts +++ b/lib/commands/SDIFFSTORE.ts @@ -1,5 +1,5 @@ import { TransformArgumentsReply } from '.'; -import { pushVerdictArguments, transformReplyNumber } from './generic-transformers'; +import { pushVerdictArguments } from './generic-transformers'; export const FIRST_KEY_INDEX = 1; @@ -7,4 +7,4 @@ export function transformArguments(destination: string, keys: string | Array { return ['SETRANGE', key, offset.toString(), value]; } -export const transformReply = transformReplyNumber; +export declare function transformReply(): number; diff --git a/lib/commands/SHUTDOWN.ts b/lib/commands/SHUTDOWN.ts index 0dd2cf3a5b3..1990d05a2ed 100644 --- a/lib/commands/SHUTDOWN.ts +++ b/lib/commands/SHUTDOWN.ts @@ -1,5 +1,3 @@ -import { transformReplyVoid } from './generic-transformers'; - export function transformArguments(mode?: 'NOSAVE' | 'SAVE'): Array { const args = ['SHUTDOWN']; @@ -10,4 +8,4 @@ export function transformArguments(mode?: 'NOSAVE' | 'SAVE'): Array { return args; } -export const transformReply = transformReplyVoid; +export declare function transformReply(): void; diff --git a/lib/commands/SINTER.ts b/lib/commands/SINTER.ts index 43869652370..6348bbc7ab9 100644 --- a/lib/commands/SINTER.ts +++ b/lib/commands/SINTER.ts @@ -1,5 +1,5 @@ import { TransformArgumentsReply } from '.'; -import { pushVerdictArguments, transformReplyStringArray } from './generic-transformers'; +import { pushVerdictArguments } from './generic-transformers'; export const FIRST_KEY_INDEX = 1; @@ -7,4 +7,4 @@ export function transformArguments(keys: string | Array): TransformArgum return pushVerdictArguments(['SINTER'], keys); } -export const transformReply = transformReplyStringArray; +export declare function transformReply(): Array; diff --git a/lib/commands/SINTERSTORE.ts b/lib/commands/SINTERSTORE.ts index 5ad1b11cbac..0d31c409da8 100644 --- a/lib/commands/SINTERSTORE.ts +++ b/lib/commands/SINTERSTORE.ts @@ -1,5 +1,5 @@ import { TransformArgumentsReply } from '.'; -import { pushVerdictArguments, transformReplyStringArray } from './generic-transformers'; +import { pushVerdictArguments } from './generic-transformers'; export const FIRST_KEY_INDEX = 1; @@ -7,4 +7,4 @@ export function transformArguments(destination: string, keys: string | Array; diff --git a/lib/commands/SMEMBERS.ts b/lib/commands/SMEMBERS.ts index d7e75daaa3e..71b479f9d8e 100644 --- a/lib/commands/SMEMBERS.ts +++ b/lib/commands/SMEMBERS.ts @@ -1,9 +1,7 @@ -import { transformReplyStringArray } from './generic-transformers'; - export const FIRST_KEY_INDEX = 1; export function transformArguments(key: string): Array { return ['SMEMBERS', key]; } -export const transformReply = transformReplyStringArray; +export declare function transformReply(): Array; diff --git a/lib/commands/SPOP.ts b/lib/commands/SPOP.ts index a389fed5cc5..84845230d41 100644 --- a/lib/commands/SPOP.ts +++ b/lib/commands/SPOP.ts @@ -1,5 +1,3 @@ -import { transformReplyStringArray } from './generic-transformers'; - export const FIRST_KEY_INDEX = 1; export function transformArguments(key: string, count?: number): Array { @@ -12,4 +10,4 @@ export function transformArguments(key: string, count?: number): Array { return args; } -export const transformReply = transformReplyStringArray; +export declare function transformReply(): Array; diff --git a/lib/commands/SRANDMEMBER.ts b/lib/commands/SRANDMEMBER.ts index 2e8cd539273..c477a5691d7 100644 --- a/lib/commands/SRANDMEMBER.ts +++ b/lib/commands/SRANDMEMBER.ts @@ -1,9 +1,7 @@ -import { transformReplyStringNull } from './generic-transformers'; - export const FIRST_KEY_INDEX = 1; export function transformArguments(key: string): Array { return ['SRANDMEMBER', key]; } -export const transformReply = transformReplyStringNull; +export declare function transformReply(): string | null; diff --git a/lib/commands/SRANDMEMBER_COUNT.ts b/lib/commands/SRANDMEMBER_COUNT.ts index b7fa8ebeb3a..89d9b8c4aef 100644 --- a/lib/commands/SRANDMEMBER_COUNT.ts +++ b/lib/commands/SRANDMEMBER_COUNT.ts @@ -1,4 +1,3 @@ -import { transformReplyStringArray } from './generic-transformers'; import { transformArguments as transformSRandMemberArguments } from './SRANDMEMBER'; export { FIRST_KEY_INDEX } from './SRANDMEMBER'; @@ -10,4 +9,4 @@ export function transformArguments(key: string, count: number): Array { ]; } -export const transformReply = transformReplyStringArray; +export declare function transformReply(): Array; diff --git a/lib/commands/SREM.ts b/lib/commands/SREM.ts index 4ae33245d29..534de170540 100644 --- a/lib/commands/SREM.ts +++ b/lib/commands/SREM.ts @@ -1,5 +1,5 @@ import { TransformArgumentsReply } from '.'; -import { pushVerdictArguments, transformReplyNumber } from './generic-transformers'; +import { pushVerdictArguments } from './generic-transformers'; export const FIRST_KEY_INDEX = 1; @@ -7,4 +7,4 @@ export function transformArguments(key: string, members: string | Array) return pushVerdictArguments(['SREM', key], members); } -export const transformReply = transformReplyNumber; +export declare function transformReply(): number; diff --git a/lib/commands/STRLEN.ts b/lib/commands/STRLEN.ts index d8112ce7d1f..208d9d73fb8 100644 --- a/lib/commands/STRLEN.ts +++ b/lib/commands/STRLEN.ts @@ -1,5 +1,3 @@ -import { transformReplyNumber } from './generic-transformers'; - export const FIRST_KEY_INDEX = 1; export const IS_READ_ONLY = true; @@ -8,4 +6,4 @@ export function transformArguments(key: string): Array { return ['STRLEN', key]; } -export const transformReply = transformReplyNumber; +export declare function transformReply(): number; diff --git a/lib/commands/SUNION.ts b/lib/commands/SUNION.ts index 705bff29927..07a46e38efa 100644 --- a/lib/commands/SUNION.ts +++ b/lib/commands/SUNION.ts @@ -1,5 +1,5 @@ import { TransformArgumentsReply } from '.'; -import { pushVerdictArguments, transformReplyStringArray } from './generic-transformers'; +import { pushVerdictArguments } from './generic-transformers'; export const FIRST_KEY_INDEX = 1; @@ -9,4 +9,4 @@ export function transformArguments(keys: string | Array): TransformArgum return pushVerdictArguments(['SUNION'], keys); } -export const transformReply = transformReplyStringArray; +export declare function transformReply(): Array; diff --git a/lib/commands/SUNIONSTORE.ts b/lib/commands/SUNIONSTORE.ts index af717f627df..8745c516433 100644 --- a/lib/commands/SUNIONSTORE.ts +++ b/lib/commands/SUNIONSTORE.ts @@ -1,5 +1,5 @@ import { TransformArgumentsReply } from '.'; -import { pushVerdictArguments, transformReplyNumber } from './generic-transformers'; +import { pushVerdictArguments } from './generic-transformers'; export const FIRST_KEY_INDEX = 1; @@ -7,4 +7,4 @@ export function transformArguments(destination: string, keys: string | Array { return ['SWAPDB', index1.toString(), index2.toString()]; } -export const transformReply = transformReplyString; +export declare function transformReply(): string; diff --git a/lib/commands/TOUCH.ts b/lib/commands/TOUCH.ts index abff4160392..8632d848dbf 100644 --- a/lib/commands/TOUCH.ts +++ b/lib/commands/TOUCH.ts @@ -1,5 +1,5 @@ import { TransformArgumentsReply } from '.'; -import { pushVerdictArguments, transformReplyNumber } from './generic-transformers'; +import { pushVerdictArguments } from './generic-transformers'; export const FIRST_KEY_INDEX = 1; @@ -7,4 +7,4 @@ export function transformArguments(key: string | Array): TransformArgume return pushVerdictArguments(['TOUCH'], key); } -export const transformReply = transformReplyNumber; +export declare function transformReply(): number; diff --git a/lib/commands/TTL.ts b/lib/commands/TTL.ts index aa8462dfea3..4ae31245aa0 100644 --- a/lib/commands/TTL.ts +++ b/lib/commands/TTL.ts @@ -1,5 +1,3 @@ -import { transformReplyNumber } from './generic-transformers'; - export const FIRST_KEY_INDEX = 1; export const IS_READ_ONLY = true; @@ -8,4 +6,4 @@ export function transformArguments(key: string): Array { return ['TTL', key]; } -export const transformReply = transformReplyNumber; +export declare function transformReply(): number; diff --git a/lib/commands/TYPE.ts b/lib/commands/TYPE.ts index 4f27b29d2b6..283701b6e9f 100644 --- a/lib/commands/TYPE.ts +++ b/lib/commands/TYPE.ts @@ -1,5 +1,3 @@ -import { transformReplyString } from './generic-transformers'; - export const FIRST_KEY_INDEX = 1; export const IS_READ_ONLY = true; @@ -8,4 +6,4 @@ export function transformArguments(key: string): Array { return ['TYPE', key]; } -export const transformReply = transformReplyString; +export declare function transformReply(): string; diff --git a/lib/commands/UNLINK.ts b/lib/commands/UNLINK.ts index 4647a976e42..2ff87974cfb 100644 --- a/lib/commands/UNLINK.ts +++ b/lib/commands/UNLINK.ts @@ -1,5 +1,5 @@ import { TransformArgumentsReply } from '.'; -import { pushVerdictArguments, transformReplyNumber } from './generic-transformers'; +import { pushVerdictArguments } from './generic-transformers'; export const FIRST_KEY_INDEX = 1; @@ -7,4 +7,4 @@ export function transformArguments(key: string | Array): TransformArgume return pushVerdictArguments(['UNLINK'], key); } -export const transformReply = transformReplyNumber; +export declare function transformReply(): number; diff --git a/lib/commands/UNWATCH.ts b/lib/commands/UNWATCH.ts index d0ede556f3a..ce42e7697bf 100644 --- a/lib/commands/UNWATCH.ts +++ b/lib/commands/UNWATCH.ts @@ -1,7 +1,5 @@ -import { transformReplyString } from './generic-transformers'; - export function transformArguments(): Array { return ['UNWATCH']; } -export const transformReply = transformReplyString; +export declare function transformReply(): string; diff --git a/lib/commands/WAIT.ts b/lib/commands/WAIT.ts index 214fb356688..dff51ed9680 100644 --- a/lib/commands/WAIT.ts +++ b/lib/commands/WAIT.ts @@ -1,9 +1,7 @@ -import { transformReplyNumber } from './generic-transformers'; - export const FIRST_KEY_INDEX = 1; export function transformArguments(numberOfReplicas: number, timeout: number): Array { return ['WAIT', numberOfReplicas.toString(), timeout.toString()]; } -export const transformReply = transformReplyNumber; +export declare function transformReply(): number; diff --git a/lib/commands/WATCH.ts b/lib/commands/WATCH.ts index e644ab0f462..5ca42c0eb95 100644 --- a/lib/commands/WATCH.ts +++ b/lib/commands/WATCH.ts @@ -1,8 +1,8 @@ import { TransformArgumentsReply } from '.'; -import { pushVerdictArguments, transformReplyString } from './generic-transformers'; +import { pushVerdictArguments } from './generic-transformers'; export function transformArguments(key: string | Array): TransformArgumentsReply { return pushVerdictArguments(['WATCH'], key); } -export const transformReply = transformReplyString; +export declare function transformReply(): string; diff --git a/lib/commands/XACK.ts b/lib/commands/XACK.ts index a6de28151eb..4573b7f3d25 100644 --- a/lib/commands/XACK.ts +++ b/lib/commands/XACK.ts @@ -1,5 +1,5 @@ import { TransformArgumentsReply } from '.'; -import { pushVerdictArguments, transformReplyNumber } from './generic-transformers'; +import { pushVerdictArguments } from './generic-transformers'; export const FIRST_KEY_INDEX = 1; @@ -7,4 +7,4 @@ export function transformArguments(key: string, group: string, id: string | Arra return pushVerdictArguments(['XACK', key, group], id); } -export const transformReply = transformReplyNumber; +export declare function transformReply(): number; diff --git a/lib/commands/XADD.ts b/lib/commands/XADD.ts index 0500a2fde65..7bc263b2400 100644 --- a/lib/commands/XADD.ts +++ b/lib/commands/XADD.ts @@ -1,8 +1,7 @@ -import { TuplesObject, transformReplyString } from './generic-transformers'; +import { TuplesObject } from './generic-transformers'; export const FIRST_KEY_INDEX = 1; - interface XAddOptions { NOMKSTREAM?: true; TRIM?: { @@ -45,4 +44,4 @@ export function transformArguments(key: string, id: string, message: TuplesObjec return args; } -export const transformReply = transformReplyString; +export declare function transformReply(): string; diff --git a/lib/commands/XCLAIM_JUSTID.ts b/lib/commands/XCLAIM_JUSTID.ts index dcf274ed821..ab74c89420c 100644 --- a/lib/commands/XCLAIM_JUSTID.ts +++ b/lib/commands/XCLAIM_JUSTID.ts @@ -1,4 +1,3 @@ -import { transformReplyStringArray } from './generic-transformers'; import { transformArguments as transformArgumentsXClaim } from './XCLAIM'; export { FIRST_KEY_INDEX } from './XCLAIM'; @@ -10,4 +9,4 @@ export function transformArguments(...args: Parameters; diff --git a/lib/commands/XDEL.ts b/lib/commands/XDEL.ts index 083ea77ef0f..187e5b4e73a 100644 --- a/lib/commands/XDEL.ts +++ b/lib/commands/XDEL.ts @@ -1,5 +1,5 @@ import { TransformArgumentsReply } from '.'; -import { pushVerdictArguments, transformReplyNumber } from './generic-transformers'; +import { pushVerdictArguments } from './generic-transformers'; export const FIRST_KEY_INDEX = 1; @@ -7,4 +7,4 @@ export function transformArguments(key: string, id: string | Array): Tra return pushVerdictArguments(['XDEL', key], id); } -export const transformReply = transformReplyNumber; +export declare function transformReply(): number; diff --git a/lib/commands/XGROUP_CREATE.ts b/lib/commands/XGROUP_CREATE.ts index 167197b263c..c2d7f157615 100644 --- a/lib/commands/XGROUP_CREATE.ts +++ b/lib/commands/XGROUP_CREATE.ts @@ -1,5 +1,3 @@ -import { transformReplyString } from './generic-transformers'; - export const FIRST_KEY_INDEX = 2; interface XGroupCreateOptions { @@ -16,4 +14,4 @@ export function transformArguments(key: string, group: string, id: string, optio return args; } -export const transformReply = transformReplyString; +export declare function transformReply(): string; diff --git a/lib/commands/XGROUP_DELCONSUMER.ts b/lib/commands/XGROUP_DELCONSUMER.ts index 91a36f91a30..dc9de7bb577 100644 --- a/lib/commands/XGROUP_DELCONSUMER.ts +++ b/lib/commands/XGROUP_DELCONSUMER.ts @@ -1,9 +1,7 @@ -import { transformReplyNumber } from './generic-transformers'; - export const FIRST_KEY_INDEX = 2; export function transformArguments(key: string, group: string, consumer: string): Array { return ['XGROUP', 'DELCONSUMER', key, group, consumer]; } -export const transformReply = transformReplyNumber; +export declare function transformReply(): number; diff --git a/lib/commands/XGROUP_SETID.ts b/lib/commands/XGROUP_SETID.ts index ce4a13086d3..2d4e30d9c94 100644 --- a/lib/commands/XGROUP_SETID.ts +++ b/lib/commands/XGROUP_SETID.ts @@ -1,9 +1,7 @@ -import { transformReplyString } from './generic-transformers'; - export const FIRST_KEY_INDEX = 2; export function transformArguments(key: string, group: string, id: string): Array { return ['XGROUP', 'SETID', key, group, id]; } -export const transformReply = transformReplyString; +export declare function transformReply(): string; diff --git a/lib/commands/XLEN.ts b/lib/commands/XLEN.ts index d7ba033e612..1cadd61952b 100644 --- a/lib/commands/XLEN.ts +++ b/lib/commands/XLEN.ts @@ -1,5 +1,3 @@ -import { transformReplyNumber } from './generic-transformers'; - export const FIRST_KEY_INDEX = 1; export const IS_READ_ONLY = true; @@ -8,4 +6,4 @@ export function transformArguments(key: string): Array { return ['XLEN', key]; } -export const transformReply = transformReplyNumber; +export declare function transformReply(): number; diff --git a/lib/commands/XTRIM.ts b/lib/commands/XTRIM.ts index 8175ba70df3..520c38847b8 100644 --- a/lib/commands/XTRIM.ts +++ b/lib/commands/XTRIM.ts @@ -1,5 +1,3 @@ -import { transformReplyNumber } from './generic-transformers'; - export const FIRST_KEY_INDEX = 1; interface XTrimOptions { @@ -23,4 +21,4 @@ export function transformArguments(key: string, strategy: 'MAXLEN' | 'MINID', th return args; } -export const transformReply = transformReplyNumber; +export declare function transformReply(): number; diff --git a/lib/commands/ZCARD.ts b/lib/commands/ZCARD.ts index f6e4ea5f6cd..9c76c485bf9 100644 --- a/lib/commands/ZCARD.ts +++ b/lib/commands/ZCARD.ts @@ -1,5 +1,3 @@ -import { transformReplyNumber } from './generic-transformers'; - export const FIRST_KEY_INDEX = 1; export const IS_READ_ONLY = true; @@ -8,4 +6,4 @@ export function transformArguments(key: string): Array { return ['ZCARD', key]; } -export const transformReply = transformReplyNumber; +export declare function transformReply(): number; diff --git a/lib/commands/ZCOUNT.ts b/lib/commands/ZCOUNT.ts index fd73c384489..a18ba0fd790 100644 --- a/lib/commands/ZCOUNT.ts +++ b/lib/commands/ZCOUNT.ts @@ -1,4 +1,4 @@ -import { transformArgumentNumberInfinity, transformReplyNumber } from './generic-transformers'; +import { transformArgumentNumberInfinity } from './generic-transformers'; export const FIRST_KEY_INDEX = 1; @@ -13,4 +13,4 @@ export function transformArguments(key: string, min: number, max: number): Array ]; } -export const transformReply = transformReplyNumber; +export declare function transformReply(): number; diff --git a/lib/commands/ZDIFF.ts b/lib/commands/ZDIFF.ts index 7154947fea7..4c5b722131e 100644 --- a/lib/commands/ZDIFF.ts +++ b/lib/commands/ZDIFF.ts @@ -1,5 +1,5 @@ import { TransformArgumentsReply } from '.'; -import { pushVerdictArgument, transformReplyStringArray } from './generic-transformers'; +import { pushVerdictArgument } from './generic-transformers'; export const FIRST_KEY_INDEX = 2; @@ -9,4 +9,4 @@ export function transformArguments(keys: Array | string): TransformArgum return pushVerdictArgument(['ZDIFF'], keys); } -export const transformReply = transformReplyStringArray; +export declare function transformReply(): Array; diff --git a/lib/commands/ZDIFFSTORE.ts b/lib/commands/ZDIFFSTORE.ts index f91d4c869ba..95c58c66b9e 100644 --- a/lib/commands/ZDIFFSTORE.ts +++ b/lib/commands/ZDIFFSTORE.ts @@ -1,5 +1,5 @@ import { TransformArgumentsReply } from '.'; -import { pushVerdictArgument, transformReplyNumber } from './generic-transformers'; +import { pushVerdictArgument } from './generic-transformers'; export const FIRST_KEY_INDEX = 1; @@ -7,4 +7,4 @@ export function transformArguments(destination: string, keys: Array | st return pushVerdictArgument(['ZDIFFSTORE', destination], keys); } -export const transformReply = transformReplyNumber; +export declare function transformReply(): number; diff --git a/lib/commands/ZINTER.ts b/lib/commands/ZINTER.ts index 91d7982a8e7..ae1b27a6c8d 100644 --- a/lib/commands/ZINTER.ts +++ b/lib/commands/ZINTER.ts @@ -1,5 +1,5 @@ import { TransformArgumentsReply } from '.'; -import { pushVerdictArgument, transformReplyStringArray } from './generic-transformers'; +import { pushVerdictArgument } from './generic-transformers'; export const FIRST_KEY_INDEX = 2; @@ -27,4 +27,4 @@ export function transformArguments(keys: Array | string, options?: ZInte return args; } -export const transformReply = transformReplyStringArray; +export declare function transformReply(): Array; diff --git a/lib/commands/ZINTERSTORE.ts b/lib/commands/ZINTERSTORE.ts index 6e79e423cb0..496729a774e 100644 --- a/lib/commands/ZINTERSTORE.ts +++ b/lib/commands/ZINTERSTORE.ts @@ -1,5 +1,5 @@ import { TransformArgumentsReply } from '.'; -import { pushVerdictArgument, transformReplyNumber } from './generic-transformers'; +import { pushVerdictArgument } from './generic-transformers'; export const FIRST_KEY_INDEX = 1; @@ -25,4 +25,4 @@ export function transformArguments(destination: string, keys: Array | st return args; } -export const transformReply = transformReplyNumber; +export declare function transformReply(): number; diff --git a/lib/commands/ZLEXCOUNT.ts b/lib/commands/ZLEXCOUNT.ts index 2ba50dda73e..2e70fdee91d 100644 --- a/lib/commands/ZLEXCOUNT.ts +++ b/lib/commands/ZLEXCOUNT.ts @@ -1,5 +1,3 @@ -import { transformReplyNumber } from './generic-transformers'; - export const FIRST_KEY_INDEX = 1; export const IS_READ_ONLY = true; @@ -13,4 +11,4 @@ export function transformArguments(key: string, min: string, max: string): Array ]; } -export const transformReply = transformReplyNumber; +export declare function transformReply(): number; diff --git a/lib/commands/ZRANDMEMBER.ts b/lib/commands/ZRANDMEMBER.ts index 27bb7cefa50..13bb05598b7 100644 --- a/lib/commands/ZRANDMEMBER.ts +++ b/lib/commands/ZRANDMEMBER.ts @@ -1,5 +1,3 @@ -import { transformReplyStringNull } from './generic-transformers'; - export const FIRST_KEY_INDEX = 1; export const IS_READ_ONLY = true; @@ -8,4 +6,4 @@ export function transformArguments(key: string): Array { return ['ZRANDMEMBER', key]; } -export const transformReply = transformReplyStringNull; +export declare function transformReply(): string | null; diff --git a/lib/commands/ZRANDMEMBER_COUNT.ts b/lib/commands/ZRANDMEMBER_COUNT.ts index f7eef456d05..1b7b8fea994 100644 --- a/lib/commands/ZRANDMEMBER_COUNT.ts +++ b/lib/commands/ZRANDMEMBER_COUNT.ts @@ -1,4 +1,3 @@ -import { transformReplyStringArray } from './generic-transformers'; import { transformArguments as transformZRandMemberArguments } from './ZRANDMEMBER'; export { FIRST_KEY_INDEX, IS_READ_ONLY } from './ZRANDMEMBER'; @@ -10,4 +9,4 @@ export function transformArguments(key: string, count: number): Array { ]; } -export const transformReply = transformReplyStringArray; +export declare function transformReply(): Array; diff --git a/lib/commands/ZRANGE.ts b/lib/commands/ZRANGE.ts index 9037210d69f..391c5ca893e 100644 --- a/lib/commands/ZRANGE.ts +++ b/lib/commands/ZRANGE.ts @@ -1,4 +1,4 @@ -import { transformArgumentNumberInfinity, transformReplyStringArray } from './generic-transformers'; +import { transformArgumentNumberInfinity } from './generic-transformers'; export const FIRST_KEY_INDEX = 1; @@ -42,4 +42,4 @@ export function transformArguments(key: string, min: string | number, max: strin return args; } -export const transformReply = transformReplyStringArray; +export declare function transformReply(): Array; diff --git a/lib/commands/ZRANK.ts b/lib/commands/ZRANK.ts index 84f9c7d4a9e..e060ccf6f8c 100644 --- a/lib/commands/ZRANK.ts +++ b/lib/commands/ZRANK.ts @@ -1,5 +1,3 @@ -import { transformReplyNumberNull } from './generic-transformers'; - export const FIRST_KEY_INDEX = 1; export const IS_READ_ONLY = true; @@ -8,4 +6,4 @@ export function transformArguments(key: string, member: string): Array { return ['ZRANK', key, member]; } -export const transformReply = transformReplyNumberNull; +export declare function transformReply(): number | null; diff --git a/lib/commands/ZREM.ts b/lib/commands/ZREM.ts index 8419291f2fd..550a41e8b5c 100644 --- a/lib/commands/ZREM.ts +++ b/lib/commands/ZREM.ts @@ -1,5 +1,5 @@ import { TransformArgumentsReply } from '.'; -import { pushVerdictArguments, transformReplyNumber } from './generic-transformers'; +import { pushVerdictArguments } from './generic-transformers'; export const FIRST_KEY_INDEX = 1; @@ -7,4 +7,4 @@ export function transformArguments(key: string, member: string | Array): return pushVerdictArguments(['ZREM', key], member); } -export const transformReply = transformReplyNumber; +export declare function transformReply(): number; diff --git a/lib/commands/ZREMRANGEBYLEX.ts b/lib/commands/ZREMRANGEBYLEX.ts index aaf92992f98..1f17d6b986e 100644 --- a/lib/commands/ZREMRANGEBYLEX.ts +++ b/lib/commands/ZREMRANGEBYLEX.ts @@ -1,9 +1,7 @@ -import { transformReplyNumber } from './generic-transformers'; - export const FIRST_KEY_INDEX = 1; export function transformArguments(key: string, min: string, max: string): Array { return ['ZREMRANGEBYLEX', key, min, max]; } -export const transformReply = transformReplyNumber; +export declare function transformReply(): number; diff --git a/lib/commands/ZREMRANGEBYRANK.ts b/lib/commands/ZREMRANGEBYRANK.ts index 89bf63d8e34..550a56e8a85 100644 --- a/lib/commands/ZREMRANGEBYRANK.ts +++ b/lib/commands/ZREMRANGEBYRANK.ts @@ -1,9 +1,7 @@ -import { transformReplyNumber } from './generic-transformers'; - export const FIRST_KEY_INDEX = 1; export function transformArguments(key: string, start: number, stop: number): Array { return ['ZREMRANGEBYRANK', key, start.toString(), stop.toString()]; } -export const transformReply = transformReplyNumber; +export declare function transformReply(): number; diff --git a/lib/commands/ZREMRANGEBYSCORE.ts b/lib/commands/ZREMRANGEBYSCORE.ts index 64d14a4eb41..e0186fcb64e 100644 --- a/lib/commands/ZREMRANGEBYSCORE.ts +++ b/lib/commands/ZREMRANGEBYSCORE.ts @@ -1,9 +1,7 @@ -import { transformReplyNumber } from './generic-transformers'; - export const FIRST_KEY_INDEX = 1; export function transformArguments(key: string, min: number, max: number): Array { return ['ZREMRANGEBYSCORE', key, min.toString(), max.toString()]; } -export const transformReply = transformReplyNumber; +export declare function transformReply(): number; diff --git a/lib/commands/ZREVRANK.ts b/lib/commands/ZREVRANK.ts index 7d4c4ce2ab5..808d9e45dc3 100644 --- a/lib/commands/ZREVRANK.ts +++ b/lib/commands/ZREVRANK.ts @@ -1,5 +1,3 @@ -import { transformReplyNumberNull } from './generic-transformers'; - export const FIRST_KEY_INDEX = 1; export const IS_READ_ONLY = true; @@ -8,4 +6,4 @@ export function transformArguments(key: string, member: string): Array { return ['ZREVRANK', key, member]; } -export const transformReply = transformReplyNumberNull; +export declare function transformReply(): number | null; diff --git a/lib/commands/ZUNION.ts b/lib/commands/ZUNION.ts index 87158b8425a..1f0723eb021 100644 --- a/lib/commands/ZUNION.ts +++ b/lib/commands/ZUNION.ts @@ -1,5 +1,5 @@ import { TransformArgumentsReply } from '.'; -import { pushVerdictArgument, transformReplyStringArray } from './generic-transformers'; +import { pushVerdictArgument } from './generic-transformers'; export const FIRST_KEY_INDEX = 2; @@ -24,4 +24,4 @@ export function transformArguments(keys: Array | string, options?: ZUnio return args; } -export const transformReply = transformReplyStringArray; +export declare function transformReply(): Array; diff --git a/lib/commands/ZUNIONSTORE.ts b/lib/commands/ZUNIONSTORE.ts index 4ebbdbd8591..d0a92b20a60 100644 --- a/lib/commands/ZUNIONSTORE.ts +++ b/lib/commands/ZUNIONSTORE.ts @@ -1,5 +1,5 @@ import { TransformArgumentsReply } from '.'; -import { pushVerdictArgument, transformReplyNumber } from './generic-transformers'; +import { pushVerdictArgument } from './generic-transformers'; export const FIRST_KEY_INDEX = 1; @@ -22,4 +22,4 @@ export function transformArguments(destination: string, keys: Array | st return args; } -export const transformReply = transformReplyNumber; +export declare function transformReply(): number; diff --git a/lib/commands/generic-transformers.ts b/lib/commands/generic-transformers.ts index bbc12ee113e..87325e61d72 100644 --- a/lib/commands/generic-transformers.ts +++ b/lib/commands/generic-transformers.ts @@ -1,41 +1,5 @@ import { TransformArgumentsReply } from '.'; -export function transformReplyNumber(reply: number): number { - return reply; -} - -export function transformReplyNumberNull(reply: number | null): number | null { - return reply; -} - -export function transformReplyNumberArray(reply: Array): Array { - return reply; -} - -export function transformReplyNumberNullArray(reply: Array): Array { - return reply; -} - -export function transformReplyString(reply: string): string { - return reply; -} - -export function transformReplyStringNull(reply: string | null): string | null { - return reply; -} - -export function transformReplyStringArray(reply: Array): Array { - return reply; -} - -export function transformReplyStringArrayNull(reply: Array | null): Array | null { - return reply; -} - -export function transformReplyStringNullArray(reply: Array): Array { - return reply; -} - export function transformReplyBoolean(reply: number): boolean { return reply === 1; } @@ -46,16 +10,6 @@ export function transformReplyBooleanArray(reply: Array): Array export type BitValue = 0 | 1; -export function transformReplyBit(reply: BitValue): BitValue { - return reply; -} - -export function transformReplyBufferNull(reply: Buffer | null): Buffer | null { - return reply; -} - -export function transformReplyVoid(): void {} - export interface ScanOptions { MATCH?: string; COUNT?: number; diff --git a/lib/commands/index.ts b/lib/commands/index.ts index 89581090e58..192968cb2ae 100644 --- a/lib/commands/index.ts +++ b/lib/commands/index.ts @@ -748,9 +748,11 @@ export interface RedisCommand { IS_READ_ONLY?: boolean; transformArguments(this: void, ...args: Array): TransformArgumentsReply; BUFFER_MODE?: boolean; - transformReply(this: void, reply: RedisReply, preserved?: unknown): any; + transformReply?(this: void, reply: RedisReply, preserved?: unknown): any; } +export type RedisCommandReply = C['transformReply'] extends (...args: any) => infer T ? T : RedisReply; + export interface RedisCommands { [command: string]: RedisCommand; } diff --git a/package-lock.json b/package-lock.json index 9fcd62b5996..e4625a07401 100644 --- a/package-lock.json +++ b/package-lock.json @@ -653,18 +653,18 @@ } }, "node_modules/@octokit/openapi-types": { - "version": "10.0.0", - "resolved": "https://registry.npmjs.org/@octokit/openapi-types/-/openapi-types-10.0.0.tgz", - "integrity": "sha512-k1iO2zKuEjjRS1EJb4FwSLk+iF6EGp+ZV0OMRViQoWhQ1fZTk9hg1xccZII5uyYoiqcbC73MRBmT45y1vp2PPg==", + "version": "10.6.1", + "resolved": "https://registry.npmjs.org/@octokit/openapi-types/-/openapi-types-10.6.1.tgz", + "integrity": "sha512-53YKy8w8+sHQhUONhTiYt6MqNqPolejYr6rK/3VOevpORAIYGQEX2pmXnnhgdSsjHy176e5ZBgVt0ppOGziS7g==", "dev": true }, "node_modules/@octokit/plugin-paginate-rest": { - "version": "2.16.0", - "resolved": "https://registry.npmjs.org/@octokit/plugin-paginate-rest/-/plugin-paginate-rest-2.16.0.tgz", - "integrity": "sha512-8YYzALPMvEZ35kgy5pdYvQ22Roz+BIuEaedO575GwE2vb/ACDqQn0xQrTJR4tnZCJn7pi8+AWPVjrFDaERIyXQ==", + "version": "2.16.5", + "resolved": "https://registry.npmjs.org/@octokit/plugin-paginate-rest/-/plugin-paginate-rest-2.16.5.tgz", + "integrity": "sha512-2PfRGymdBypqRes4Xelu0BAZZRCV/Qg0xgo8UB10UKoghCM+zg640+T5WkRsRD0edwfLBPP3VsJgDyDTG4EIYg==", "dev": true, "dependencies": { - "@octokit/types": "^6.26.0" + "@octokit/types": "^6.31.0" }, "peerDependencies": { "@octokit/core": ">=2" @@ -680,12 +680,12 @@ } }, "node_modules/@octokit/plugin-rest-endpoint-methods": { - "version": "5.10.4", - "resolved": "https://registry.npmjs.org/@octokit/plugin-rest-endpoint-methods/-/plugin-rest-endpoint-methods-5.10.4.tgz", - "integrity": "sha512-Dh+EAMCYR9RUHwQChH94Skl0lM8Fh99auT8ggck/xTzjJrwVzvsd0YH68oRPqp/HxICzmUjLfaQ9sy1o1sfIiA==", + "version": "5.11.3", + "resolved": "https://registry.npmjs.org/@octokit/plugin-rest-endpoint-methods/-/plugin-rest-endpoint-methods-5.11.3.tgz", + "integrity": "sha512-E19gqHqfP3uJa2/hx6Abhx2NrVP5tsNbst2/AeqGxlGM+eL4N8fRbzhd+NEIsGAB4y3R7e9kVE0y8OOghlXUXw==", "dev": true, "dependencies": { - "@octokit/types": "^6.28.1", + "@octokit/types": "^6.31.1", "deprecation": "^2.3.1" }, "peerDependencies": { @@ -730,12 +730,12 @@ } }, "node_modules/@octokit/types": { - "version": "6.26.0", - "resolved": "https://registry.npmjs.org/@octokit/types/-/types-6.26.0.tgz", - "integrity": "sha512-RDxZBAFMtqs1ZPnbUu1e7ohPNfoNhTiep4fErY7tZs995BeHu369Vsh5woMIaFbllRWEZBfvTCS4hvDnMPiHrA==", + "version": "6.31.1", + "resolved": "https://registry.npmjs.org/@octokit/types/-/types-6.31.1.tgz", + "integrity": "sha512-xkF46eaYcpT8ieO78mZWhMq3bt37zIsP5BUkN+zWgX+mTYDB7jOtUP1MOxcSF8hhJhsjjlB1YDgQAhX0z0oqPw==", "dev": true, "dependencies": { - "@octokit/openapi-types": "^10.0.0" + "@octokit/openapi-types": "^10.6.1" } }, "node_modules/@sindresorhus/is": { @@ -855,9 +855,9 @@ "dev": true }, "node_modules/@types/node": { - "version": "16.7.10", - "resolved": "https://registry.npmjs.org/@types/node/-/node-16.7.10.tgz", - "integrity": "sha512-S63Dlv4zIPb8x6MMTgDq5WWRJQe56iBEY0O3SOFA9JrRienkOVDXSXBjjJw6HTNQYSE2JI6GMCR6LVbIMHJVvA==", + "version": "16.10.2", + "resolved": "https://registry.npmjs.org/@types/node/-/node-16.10.2.tgz", + "integrity": "sha512-zCclL4/rx+W5SQTzFs9wyvvyCwoK9QtBpratqz2IYJ3O8Umrn0m3nsTv0wQBk9sRGpvUe9CwPDrQFB10f1FIjQ==", "dev": true }, "node_modules/@types/parse-json": { @@ -876,9 +876,9 @@ } }, "node_modules/@types/sinon": { - "version": "10.0.3", - "resolved": "https://registry.npmjs.org/@types/sinon/-/sinon-10.0.3.tgz", - "integrity": "sha512-XUaFuUOQ3A/r6gS1qCU/USMleascaqGeQpGR1AZ5JdRtBPlzijRzKsik1TuGzvdtPA0mdq42JqaJmJ+Afg1LJg==", + "version": "10.0.4", + "resolved": "https://registry.npmjs.org/@types/sinon/-/sinon-10.0.4.tgz", + "integrity": "sha512-fOYjrxQv8zJsqOY6V6ecP4eZhQBxtY80X0er1VVnUIAIZo74jHm8e1vguG5Yt4Iv8W2Wr7TgibB8MfRe32k9pA==", "dev": true, "dependencies": { "@sinonjs/fake-timers": "^7.1.0" @@ -937,62 +937,12 @@ } }, "node_modules/ansi-align": { - "version": "3.0.0", - "resolved": "https://registry.npmjs.org/ansi-align/-/ansi-align-3.0.0.tgz", - "integrity": "sha512-ZpClVKqXN3RGBmKibdfWzqCY4lnjEuoNzU5T0oEFpfd/z5qJHVarukridD4juLO2FXMiwUQxr9WqQtaYa8XRYw==", - "dev": true, - "dependencies": { - "string-width": "^3.0.0" - } - }, - "node_modules/ansi-align/node_modules/ansi-regex": { - "version": "4.1.0", - "resolved": "https://registry.npmjs.org/ansi-regex/-/ansi-regex-4.1.0.tgz", - "integrity": "sha512-1apePfXM1UOSqw0o9IiFAovVz9M5S1Dg+4TrDwfMewQ6p/rmMueb7tWZjQ1rx4Loy1ArBggoqGpfqqdI4rondg==", - "dev": true, - "engines": { - "node": ">=6" - } - }, - "node_modules/ansi-align/node_modules/emoji-regex": { - "version": "7.0.3", - "resolved": "https://registry.npmjs.org/emoji-regex/-/emoji-regex-7.0.3.tgz", - "integrity": "sha512-CwBLREIQ7LvYFB0WyRvwhq5N5qPhc6PMjD6bYggFlI5YyDgl+0vxq5VHbMOFqLg7hfWzmu8T5Z1QofhmTIhItA==", - "dev": true - }, - "node_modules/ansi-align/node_modules/is-fullwidth-code-point": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/is-fullwidth-code-point/-/is-fullwidth-code-point-2.0.0.tgz", - "integrity": "sha1-o7MKXE8ZkYMWeqq5O+764937ZU8=", - "dev": true, - "engines": { - "node": ">=4" - } - }, - "node_modules/ansi-align/node_modules/string-width": { - "version": "3.1.0", - "resolved": "https://registry.npmjs.org/string-width/-/string-width-3.1.0.tgz", - "integrity": "sha512-vafcv6KjVZKSgz06oM/H6GDBrAtz8vdhQakGjFIvNrHA6y3HCF1CInLy+QLq8dTJPQ1b+KDUqDFctkdRW44e1w==", - "dev": true, - "dependencies": { - "emoji-regex": "^7.0.1", - "is-fullwidth-code-point": "^2.0.0", - "strip-ansi": "^5.1.0" - }, - "engines": { - "node": ">=6" - } - }, - "node_modules/ansi-align/node_modules/strip-ansi": { - "version": "5.2.0", - "resolved": "https://registry.npmjs.org/strip-ansi/-/strip-ansi-5.2.0.tgz", - "integrity": "sha512-DuRs1gKbBqsMKIZlrffwlug8MHkcnpjs5VPmL1PAh+mA30U0DTotfDZ0d2UUsXpPmPmMMJ6W773MaA3J+lbiWA==", + "version": "3.0.1", + "resolved": "https://registry.npmjs.org/ansi-align/-/ansi-align-3.0.1.tgz", + "integrity": "sha512-IOfwwBF5iczOjp/WeY4YxyjqAFMQoZufdQWDd19SEExbVLNXqvpzSJ/M7Za4/sCPmQ0+GRquoA7bGcINcxew6w==", "dev": true, "dependencies": { - "ansi-regex": "^4.1.0" - }, - "engines": { - "node": ">=6" + "string-width": "^4.1.0" } }, "node_modules/ansi-colors": { @@ -1249,16 +1199,16 @@ "dev": true }, "node_modules/browserslist": { - "version": "4.17.0", - "resolved": "https://registry.npmjs.org/browserslist/-/browserslist-4.17.0.tgz", - "integrity": "sha512-g2BJ2a0nEYvEFQC208q8mVAhfNwpZ5Mu8BwgtCdZKO3qx98HChmeg448fPdUzld8aFmfLgVh7yymqV+q1lJZ5g==", + "version": "4.17.1", + "resolved": "https://registry.npmjs.org/browserslist/-/browserslist-4.17.1.tgz", + "integrity": "sha512-aLD0ZMDSnF4lUt4ZDNgqi5BUn9BZ7YdQdI/cYlILrhdSSZJLU9aNZoD5/NBmM4SK34APB2e83MOsRt1EnkuyaQ==", "dev": true, "dependencies": { - "caniuse-lite": "^1.0.30001254", - "colorette": "^1.3.0", - "electron-to-chromium": "^1.3.830", + "caniuse-lite": "^1.0.30001259", + "electron-to-chromium": "^1.3.846", "escalade": "^3.1.1", - "node-releases": "^1.1.75" + "nanocolors": "^0.1.5", + "node-releases": "^1.1.76" }, "bin": { "browserslist": "cli.js" @@ -1390,9 +1340,9 @@ } }, "node_modules/caniuse-lite": { - "version": "1.0.30001259", - "resolved": "https://registry.npmjs.org/caniuse-lite/-/caniuse-lite-1.0.30001259.tgz", - "integrity": "sha512-V7mQTFhjITxuk9zBpI6nYsiTXhcPe05l+364nZjK7MFK/E7ibvYBSAXr4YcA6oPR8j3ZLM/LN+lUqUVAQEUZFg==", + "version": "1.0.30001261", + "resolved": "https://registry.npmjs.org/caniuse-lite/-/caniuse-lite-1.0.30001261.tgz", + "integrity": "sha512-vM8D9Uvp7bHIN0fZ2KQ4wnmYFpJo/Etb4Vwsuc+ka0tfGDHvOPrFm6S/7CCNLSOkAUjenT2HnUPESdOIL91FaA==", "dev": true, "funding": { "type": "opencollective", @@ -1569,12 +1519,6 @@ "integrity": "sha512-dOy+3AuW3a2wNbZHIuMZpTcgjGuLU/uBL/ubcZF9OXbDo8ff4O8yVp5Bf0efS8uEoYo5q4Fx7dY9OgQGXgAsQA==", "dev": true }, - "node_modules/colorette": { - "version": "1.4.0", - "resolved": "https://registry.npmjs.org/colorette/-/colorette-1.4.0.tgz", - "integrity": "sha512-Y2oEozpomLn7Q3HFP7dpww7AtMJplbM9lGZP6RDfHqmbeRjiwRg4n6VM6j4KLmRke85uWEI7JqF17f3pqdRA0g==", - "dev": true - }, "node_modules/combined-stream": { "version": "1.0.8", "resolved": "https://registry.npmjs.org/combined-stream/-/combined-stream-1.0.8.tgz", @@ -1671,9 +1615,9 @@ } }, "node_modules/debug": { - "version": "4.3.1", - "resolved": "https://registry.npmjs.org/debug/-/debug-4.3.1.tgz", - "integrity": "sha512-doEwdvm4PCeK4K3RQN2ZC2BYUBaxwLARCqZmMjtF8a51J2Rb0xpVloFRnCODwqjpwnAoao4pelN8l3RJdv3gRQ==", + "version": "4.3.2", + "resolved": "https://registry.npmjs.org/debug/-/debug-4.3.2.tgz", + "integrity": "sha512-mOp8wKcvj7XxC78zLgw/ZA+6TSgkoE2C/ienthhRD298T7UNwAg9diBpLRxC0mOezLl4B0xV7M0cCO6P/O0Xhw==", "dev": true, "dependencies": { "ms": "2.1.2" @@ -1845,9 +1789,9 @@ "dev": true }, "node_modules/electron-to-chromium": { - "version": "1.3.827", - "resolved": "https://registry.npmjs.org/electron-to-chromium/-/electron-to-chromium-1.3.827.tgz", - "integrity": "sha512-ye+4uQOY/jbjRutMcE/EmOcNwUeo1qo9aKL2tPyb09cU3lmxNeyDF4RWiemmkknW+p29h7dyDqy02higTxc9/A==", + "version": "1.3.853", + "resolved": "https://registry.npmjs.org/electron-to-chromium/-/electron-to-chromium-1.3.853.tgz", + "integrity": "sha512-W4U8n+U8I5/SUaFcqZgbKRmYZwcyEIQVBDf+j5QQK6xChjXnQD+wj248eGR9X4u+dDmDR//8vIfbu4PrdBBIoQ==", "dev": true }, "node_modules/emoji-regex": { @@ -2679,9 +2623,9 @@ } }, "node_modules/is-core-module": { - "version": "2.6.0", - "resolved": "https://registry.npmjs.org/is-core-module/-/is-core-module-2.6.0.tgz", - "integrity": "sha512-wShG8vs60jKfPWpF2KZRaAtvt3a20OAn7+IJ6hLPECpSABLcKtFKTTI4ZtH5QcBruBHlq+WsdHWyz0BCZW7svQ==", + "version": "2.7.0", + "resolved": "https://registry.npmjs.org/is-core-module/-/is-core-module-2.7.0.tgz", + "integrity": "sha512-ByY+tjCciCr+9nLryBYcSD50EOGWt95c7tIsKTG1J2ixKKXPvF7Ej3AVd+UfDydAJom3biBGDBALaO79ktwgEQ==", "dev": true, "dependencies": { "has": "^1.0.3" @@ -2724,9 +2668,9 @@ } }, "node_modules/is-glob": { - "version": "4.0.1", - "resolved": "https://registry.npmjs.org/is-glob/-/is-glob-4.0.1.tgz", - "integrity": "sha512-5G0tKtBTFImOqDnLB2hG6Bp2qcKEFduo4tZu9MT/H6NQv/ghhy30o55ufafxJ/LdH79LLs2Kfrn85TLKyA7BUg==", + "version": "4.0.3", + "resolved": "https://registry.npmjs.org/is-glob/-/is-glob-4.0.3.tgz", + "integrity": "sha512-xelSayHH36ZgE7ZWhli7pW34hNbNl8Ojv5KVmkJD4hBdD3th8Tfk9vYasLM+mXWOZhFkgZfxhLSnrwRr4elSSg==", "dev": true, "dependencies": { "is-extglob": "^2.1.1" @@ -2896,9 +2840,9 @@ "dev": true }, "node_modules/istanbul-lib-coverage": { - "version": "3.0.0", - "resolved": "https://registry.npmjs.org/istanbul-lib-coverage/-/istanbul-lib-coverage-3.0.0.tgz", - "integrity": "sha512-UiUIqxMgRDET6eR+o5HbfRYP1l0hqkWOs7vNxC/mggutCMUIhWMm8gAHb8tHlyfD3/l6rlgNA5cKdDzEAf6hEg==", + "version": "3.0.1", + "resolved": "https://registry.npmjs.org/istanbul-lib-coverage/-/istanbul-lib-coverage-3.0.1.tgz", + "integrity": "sha512-GvCYYTxaCPqwMjobtVcVKvSHtAGe48MNhGjpK8LtVF8K0ISX7hCKl85LgtuaSneWVyQmaGcW3iXVV3GaZSLpmQ==", "dev": true, "engines": { "node": ">=8" @@ -3317,16 +3261,16 @@ "dev": true }, "node_modules/mocha": { - "version": "9.1.1", - "resolved": "https://registry.npmjs.org/mocha/-/mocha-9.1.1.tgz", - "integrity": "sha512-0wE74YMgOkCgBUj8VyIDwmLUjTsS13WV1Pg7l0SHea2qzZzlq7MDnfbPsHKcELBRk3+izEVkRofjmClpycudCA==", + "version": "9.1.2", + "resolved": "https://registry.npmjs.org/mocha/-/mocha-9.1.2.tgz", + "integrity": "sha512-ta3LtJ+63RIBP03VBjMGtSqbe6cWXRejF9SyM9Zyli1CKZJZ+vfCTj3oW24V7wAphMJdpOFLoMI3hjJ1LWbs0w==", "dev": true, "dependencies": { "@ungap/promise-all-settled": "1.1.2", "ansi-colors": "4.1.1", "browser-stdout": "1.3.1", "chokidar": "3.5.2", - "debug": "4.3.1", + "debug": "4.3.2", "diff": "5.0.0", "escape-string-regexp": "4.0.0", "find-up": "5.0.0", @@ -3337,12 +3281,11 @@ "log-symbols": "4.1.0", "minimatch": "3.0.4", "ms": "2.1.3", - "nanoid": "3.1.23", + "nanoid": "3.1.25", "serialize-javascript": "6.0.0", "strip-json-comments": "3.1.1", "supports-color": "8.1.1", "which": "2.0.2", - "wide-align": "1.1.3", "workerpool": "6.1.5", "yargs": "16.2.0", "yargs-parser": "20.2.4", @@ -3372,10 +3315,16 @@ "integrity": "sha512-nnbWWOkoWyUsTjKrhgD0dcz22mdkSnpYqbEjIm2nhwhuxlSkpywJmBo8h0ZqJdkp73mb90SssHkN4rsRaBAfAA==", "dev": true }, + "node_modules/nanocolors": { + "version": "0.1.12", + "resolved": "https://registry.npmjs.org/nanocolors/-/nanocolors-0.1.12.tgz", + "integrity": "sha512-2nMHqg1x5PU+unxX7PGY7AuYxl2qDx7PSrTRjizr8sxdd3l/3hBuWWaki62qmtYm2U5i4Z5E7GbjlyDFhs9/EQ==", + "dev": true + }, "node_modules/nanoid": { - "version": "3.1.23", - "resolved": "https://registry.npmjs.org/nanoid/-/nanoid-3.1.23.tgz", - "integrity": "sha512-FiB0kzdP0FFVGDKlRLEQ1BgDzU87dy5NnzjeW9YZNt+/c3+q82EQDUwniSAUxp/F0gFNI1ZhKU1FqYsMuqZVnw==", + "version": "3.1.25", + "resolved": "https://registry.npmjs.org/nanoid/-/nanoid-3.1.25.tgz", + "integrity": "sha512-rdwtIXaXCLFAQbnfqDRnI6jaRHp9fTcYBjtFKE8eezcZ7LuLjhUaQGNeMXf1HmRoCH32CLz6XwX0TtxEOS/A3Q==", "dev": true, "bin": { "nanoid": "bin/nanoid.cjs" @@ -3425,9 +3374,9 @@ } }, "node_modules/node-fetch": { - "version": "2.6.4", - "resolved": "https://registry.npmjs.org/node-fetch/-/node-fetch-2.6.4.tgz", - "integrity": "sha512-aD1fO+xtLiSCc9vuD+sYMxpIuQyhHscGSkBEo2o5LTV/3bTEAYvdUii29n8LlO5uLCmWdGP7uVUVXFo5SRdkLA==", + "version": "2.6.5", + "resolved": "https://registry.npmjs.org/node-fetch/-/node-fetch-2.6.5.tgz", + "integrity": "sha512-mmlIVHJEu5rnIxgEgez6b9GgWXbkZj5YZ7fx+2r94a2E+Uirsp6HsPTPlomfdHtpt/B0cdKviwkoaM6pyvUOpQ==", "dev": true, "dependencies": { "whatwg-url": "^5.0.0" @@ -4475,23 +4424,6 @@ "node": ">=10" } }, - "node_modules/release-it/node_modules/debug": { - "version": "4.3.2", - "resolved": "https://registry.npmjs.org/debug/-/debug-4.3.2.tgz", - "integrity": "sha512-mOp8wKcvj7XxC78zLgw/ZA+6TSgkoE2C/ienthhRD298T7UNwAg9diBpLRxC0mOezLl4B0xV7M0cCO6P/O0Xhw==", - "dev": true, - "dependencies": { - "ms": "2.1.2" - }, - "engines": { - "node": ">=6.0" - }, - "peerDependenciesMeta": { - "supports-color": { - "optional": true - } - } - }, "node_modules/release-it/node_modules/lru-cache": { "version": "6.0.0", "resolved": "https://registry.npmjs.org/lru-cache/-/lru-cache-6.0.0.tgz", @@ -4504,12 +4436,6 @@ "node": ">=10" } }, - "node_modules/release-it/node_modules/ms": { - "version": "2.1.2", - "resolved": "https://registry.npmjs.org/ms/-/ms-2.1.2.tgz", - "integrity": "sha512-sGkPx+VjMtmA6MX27oA4FBFELFCZZ4S4XqeGOXCv68tT+jb3vk/RyaKWP0PTKyWtmLSM0b+adUTEvbs1PEaH2w==", - "dev": true - }, "node_modules/release-it/node_modules/semver": { "version": "7.3.5", "resolved": "https://registry.npmjs.org/semver/-/semver-7.3.5.tgz", @@ -4782,9 +4708,9 @@ } }, "node_modules/shiki": { - "version": "0.9.10", - "resolved": "https://registry.npmjs.org/shiki/-/shiki-0.9.10.tgz", - "integrity": "sha512-xeM7Oc6hY+6iW5O/T5hor8ul7mEprzyl5y4r5zthEHToQNw7MIhREMgU3r2gKDB0NaMLNrkcEQagudCdzE13Lg==", + "version": "0.9.11", + "resolved": "https://registry.npmjs.org/shiki/-/shiki-0.9.11.tgz", + "integrity": "sha512-tjruNTLFhU0hruCPoJP0y+B9LKOmcqUhTpxn7pcJB3fa+04gFChuEmxmrUfOJ7ZO6Jd+HwMnDHgY3lv3Tqonuw==", "dev": true, "dependencies": { "jsonc-parser": "^3.0.0", @@ -4807,9 +4733,9 @@ } }, "node_modules/signal-exit": { - "version": "3.0.4", - "resolved": "https://registry.npmjs.org/signal-exit/-/signal-exit-3.0.4.tgz", - "integrity": "sha512-rqYhcAnZ6d/vTPGghdrw7iumdcbXpsk1b8IG/rz+VWV51DM0p7XCtMoJ3qhPLIbp3tvyt3pKRbaaEMZYpHto8Q==", + "version": "3.0.5", + "resolved": "https://registry.npmjs.org/signal-exit/-/signal-exit-3.0.5.tgz", + "integrity": "sha512-KWcOiKeQj6ZyXx7zq4YxSMgHRlod4czeBQZrPb8OKcohcqAXShm7E20kEMle9WBt26hFcAf0qLOcp5zmY7kOqQ==", "dev": true }, "node_modules/sinon": { @@ -4950,26 +4876,26 @@ ] }, "node_modules/string-width": { - "version": "4.2.2", - "resolved": "https://registry.npmjs.org/string-width/-/string-width-4.2.2.tgz", - "integrity": "sha512-XBJbT3N4JhVumXE0eoLU9DCjcaF92KLNqTmFCnG1pf8duUxFGwtP6AD6nkjw9a3IdiRtL3E2w3JDiE/xi3vOeA==", + "version": "4.2.3", + "resolved": "https://registry.npmjs.org/string-width/-/string-width-4.2.3.tgz", + "integrity": "sha512-wKyQRQpjJ0sIp62ErSZdGsjMJWsap5oRNihHhu6G7JVO/9jIB6UyevL+tXuOqrng8j/cxKTWyWUwvSTriiZz/g==", "dev": true, "dependencies": { "emoji-regex": "^8.0.0", "is-fullwidth-code-point": "^3.0.0", - "strip-ansi": "^6.0.0" + "strip-ansi": "^6.0.1" }, "engines": { "node": ">=8" } }, "node_modules/strip-ansi": { - "version": "6.0.0", - "resolved": "https://registry.npmjs.org/strip-ansi/-/strip-ansi-6.0.0.tgz", - "integrity": "sha512-AuvKTrTfQNYNIctbR1K/YGTR1756GycPsg7b9bdV9Duqur4gv6aKqHXah67Z8ImS7WEz5QVcOtlfW2rZEugt6w==", + "version": "6.0.1", + "resolved": "https://registry.npmjs.org/strip-ansi/-/strip-ansi-6.0.1.tgz", + "integrity": "sha512-Y38VPSHcqkFrCpFnQ9vuSXmquuv5oXOKpGeT6aGrr3o3Gc9AlVa6JBfUSOCnbxGGZF+/0ooI7KrPuUSztUdU5A==", "dev": true, "dependencies": { - "ansi-regex": "^5.0.0" + "ansi-regex": "^5.0.1" }, "engines": { "node": ">=8" @@ -5433,58 +5359,6 @@ "integrity": "sha1-2e8H3Od7mQK4o6j6SzHD4/fm6Ho=", "dev": true }, - "node_modules/wide-align": { - "version": "1.1.3", - "resolved": "https://registry.npmjs.org/wide-align/-/wide-align-1.1.3.tgz", - "integrity": "sha512-QGkOQc8XL6Bt5PwnsExKBPuMKBxnGxWWW3fU55Xt4feHozMUhdUMaBCk290qpm/wG5u/RSKzwdAC4i51YigihA==", - "dev": true, - "dependencies": { - "string-width": "^1.0.2 || 2" - } - }, - "node_modules/wide-align/node_modules/ansi-regex": { - "version": "3.0.0", - "resolved": "https://registry.npmjs.org/ansi-regex/-/ansi-regex-3.0.0.tgz", - "integrity": "sha1-7QMXwyIGT3lGbAKWa922Bas32Zg=", - "dev": true, - "engines": { - "node": ">=4" - } - }, - "node_modules/wide-align/node_modules/is-fullwidth-code-point": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/is-fullwidth-code-point/-/is-fullwidth-code-point-2.0.0.tgz", - "integrity": "sha1-o7MKXE8ZkYMWeqq5O+764937ZU8=", - "dev": true, - "engines": { - "node": ">=4" - } - }, - "node_modules/wide-align/node_modules/string-width": { - "version": "2.1.1", - "resolved": "https://registry.npmjs.org/string-width/-/string-width-2.1.1.tgz", - "integrity": "sha512-nOqH59deCq9SRHlxq1Aw85Jnt4w6KvLKqWVik6oA9ZklXLNIOlqg4F2yrT1MVaTjAqvVwdfeZ7w7aCvJD7ugkw==", - "dev": true, - "dependencies": { - "is-fullwidth-code-point": "^2.0.0", - "strip-ansi": "^4.0.0" - }, - "engines": { - "node": ">=4" - } - }, - "node_modules/wide-align/node_modules/strip-ansi": { - "version": "4.0.0", - "resolved": "https://registry.npmjs.org/strip-ansi/-/strip-ansi-4.0.0.tgz", - "integrity": "sha1-qEeQIusaw2iocTibY1JixQXuNo8=", - "dev": true, - "dependencies": { - "ansi-regex": "^3.0.0" - }, - "engines": { - "node": ">=4" - } - }, "node_modules/widest-line": { "version": "3.1.0", "resolved": "https://registry.npmjs.org/widest-line/-/widest-line-3.1.0.tgz", @@ -6206,18 +6080,18 @@ } }, "@octokit/openapi-types": { - "version": "10.0.0", - "resolved": "https://registry.npmjs.org/@octokit/openapi-types/-/openapi-types-10.0.0.tgz", - "integrity": "sha512-k1iO2zKuEjjRS1EJb4FwSLk+iF6EGp+ZV0OMRViQoWhQ1fZTk9hg1xccZII5uyYoiqcbC73MRBmT45y1vp2PPg==", + "version": "10.6.1", + "resolved": "https://registry.npmjs.org/@octokit/openapi-types/-/openapi-types-10.6.1.tgz", + "integrity": "sha512-53YKy8w8+sHQhUONhTiYt6MqNqPolejYr6rK/3VOevpORAIYGQEX2pmXnnhgdSsjHy176e5ZBgVt0ppOGziS7g==", "dev": true }, "@octokit/plugin-paginate-rest": { - "version": "2.16.0", - "resolved": "https://registry.npmjs.org/@octokit/plugin-paginate-rest/-/plugin-paginate-rest-2.16.0.tgz", - "integrity": "sha512-8YYzALPMvEZ35kgy5pdYvQ22Roz+BIuEaedO575GwE2vb/ACDqQn0xQrTJR4tnZCJn7pi8+AWPVjrFDaERIyXQ==", + "version": "2.16.5", + "resolved": "https://registry.npmjs.org/@octokit/plugin-paginate-rest/-/plugin-paginate-rest-2.16.5.tgz", + "integrity": "sha512-2PfRGymdBypqRes4Xelu0BAZZRCV/Qg0xgo8UB10UKoghCM+zg640+T5WkRsRD0edwfLBPP3VsJgDyDTG4EIYg==", "dev": true, "requires": { - "@octokit/types": "^6.26.0" + "@octokit/types": "^6.31.0" } }, "@octokit/plugin-request-log": { @@ -6228,12 +6102,12 @@ "requires": {} }, "@octokit/plugin-rest-endpoint-methods": { - "version": "5.10.4", - "resolved": "https://registry.npmjs.org/@octokit/plugin-rest-endpoint-methods/-/plugin-rest-endpoint-methods-5.10.4.tgz", - "integrity": "sha512-Dh+EAMCYR9RUHwQChH94Skl0lM8Fh99auT8ggck/xTzjJrwVzvsd0YH68oRPqp/HxICzmUjLfaQ9sy1o1sfIiA==", + "version": "5.11.3", + "resolved": "https://registry.npmjs.org/@octokit/plugin-rest-endpoint-methods/-/plugin-rest-endpoint-methods-5.11.3.tgz", + "integrity": "sha512-E19gqHqfP3uJa2/hx6Abhx2NrVP5tsNbst2/AeqGxlGM+eL4N8fRbzhd+NEIsGAB4y3R7e9kVE0y8OOghlXUXw==", "dev": true, "requires": { - "@octokit/types": "^6.28.1", + "@octokit/types": "^6.31.1", "deprecation": "^2.3.1" } }, @@ -6275,12 +6149,12 @@ } }, "@octokit/types": { - "version": "6.28.1", - "resolved": "https://registry.npmjs.org/@octokit/types/-/types-6.28.1.tgz", - "integrity": "sha512-XlxDoQLFO5JnFZgKVQTYTvXRsQFfr/GwDUU108NJ9R5yFPkA2qXhTJjYuul3vE4eLXP40FA2nysOu2zd6boE+w==", + "version": "6.31.1", + "resolved": "https://registry.npmjs.org/@octokit/types/-/types-6.31.1.tgz", + "integrity": "sha512-xkF46eaYcpT8ieO78mZWhMq3bt37zIsP5BUkN+zWgX+mTYDB7jOtUP1MOxcSF8hhJhsjjlB1YDgQAhX0z0oqPw==", "dev": true, "requires": { - "@octokit/openapi-types": "^10.2.2" + "@octokit/openapi-types": "^10.6.1" } }, "@sindresorhus/is": { @@ -6391,9 +6265,9 @@ "dev": true }, "@types/node": { - "version": "16.9.6", - "resolved": "https://registry.npmjs.org/@types/node/-/node-16.9.6.tgz", - "integrity": "sha512-YHUZhBOMTM3mjFkXVcK+WwAcYmyhe1wL4lfqNtzI0b3qAy7yuSetnM7QJazgE5PFmgVTNGiLOgRFfJMqW7XpSQ==", + "version": "16.10.2", + "resolved": "https://registry.npmjs.org/@types/node/-/node-16.10.2.tgz", + "integrity": "sha512-zCclL4/rx+W5SQTzFs9wyvvyCwoK9QtBpratqz2IYJ3O8Umrn0m3nsTv0wQBk9sRGpvUe9CwPDrQFB10f1FIjQ==", "dev": true }, "@types/parse-json": { @@ -6412,9 +6286,9 @@ } }, "@types/sinon": { - "version": "10.0.3", - "resolved": "https://registry.npmjs.org/@types/sinon/-/sinon-10.0.3.tgz", - "integrity": "sha512-XUaFuUOQ3A/r6gS1qCU/USMleascaqGeQpGR1AZ5JdRtBPlzijRzKsik1TuGzvdtPA0mdq42JqaJmJ+Afg1LJg==", + "version": "10.0.4", + "resolved": "https://registry.npmjs.org/@types/sinon/-/sinon-10.0.4.tgz", + "integrity": "sha512-fOYjrxQv8zJsqOY6V6ecP4eZhQBxtY80X0er1VVnUIAIZo74jHm8e1vguG5Yt4Iv8W2Wr7TgibB8MfRe32k9pA==", "dev": true, "requires": { "@sinonjs/fake-timers": "^7.1.0" @@ -6461,52 +6335,12 @@ } }, "ansi-align": { - "version": "3.0.0", - "resolved": "https://registry.npmjs.org/ansi-align/-/ansi-align-3.0.0.tgz", - "integrity": "sha512-ZpClVKqXN3RGBmKibdfWzqCY4lnjEuoNzU5T0oEFpfd/z5qJHVarukridD4juLO2FXMiwUQxr9WqQtaYa8XRYw==", + "version": "3.0.1", + "resolved": "https://registry.npmjs.org/ansi-align/-/ansi-align-3.0.1.tgz", + "integrity": "sha512-IOfwwBF5iczOjp/WeY4YxyjqAFMQoZufdQWDd19SEExbVLNXqvpzSJ/M7Za4/sCPmQ0+GRquoA7bGcINcxew6w==", "dev": true, "requires": { - "string-width": "^3.0.0" - }, - "dependencies": { - "ansi-regex": { - "version": "4.1.0", - "resolved": "https://registry.npmjs.org/ansi-regex/-/ansi-regex-4.1.0.tgz", - "integrity": "sha512-1apePfXM1UOSqw0o9IiFAovVz9M5S1Dg+4TrDwfMewQ6p/rmMueb7tWZjQ1rx4Loy1ArBggoqGpfqqdI4rondg==", - "dev": true - }, - "emoji-regex": { - "version": "7.0.3", - "resolved": "https://registry.npmjs.org/emoji-regex/-/emoji-regex-7.0.3.tgz", - "integrity": "sha512-CwBLREIQ7LvYFB0WyRvwhq5N5qPhc6PMjD6bYggFlI5YyDgl+0vxq5VHbMOFqLg7hfWzmu8T5Z1QofhmTIhItA==", - "dev": true - }, - "is-fullwidth-code-point": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/is-fullwidth-code-point/-/is-fullwidth-code-point-2.0.0.tgz", - "integrity": "sha1-o7MKXE8ZkYMWeqq5O+764937ZU8=", - "dev": true - }, - "string-width": { - "version": "3.1.0", - "resolved": "https://registry.npmjs.org/string-width/-/string-width-3.1.0.tgz", - "integrity": "sha512-vafcv6KjVZKSgz06oM/H6GDBrAtz8vdhQakGjFIvNrHA6y3HCF1CInLy+QLq8dTJPQ1b+KDUqDFctkdRW44e1w==", - "dev": true, - "requires": { - "emoji-regex": "^7.0.1", - "is-fullwidth-code-point": "^2.0.0", - "strip-ansi": "^5.1.0" - } - }, - "strip-ansi": { - "version": "5.2.0", - "resolved": "https://registry.npmjs.org/strip-ansi/-/strip-ansi-5.2.0.tgz", - "integrity": "sha512-DuRs1gKbBqsMKIZlrffwlug8MHkcnpjs5VPmL1PAh+mA30U0DTotfDZ0d2UUsXpPmPmMMJ6W773MaA3J+lbiWA==", - "dev": true, - "requires": { - "ansi-regex": "^4.1.0" - } - } + "string-width": "^4.1.0" } }, "ansi-colors": { @@ -6696,16 +6530,16 @@ "dev": true }, "browserslist": { - "version": "4.17.0", - "resolved": "https://registry.npmjs.org/browserslist/-/browserslist-4.17.0.tgz", - "integrity": "sha512-g2BJ2a0nEYvEFQC208q8mVAhfNwpZ5Mu8BwgtCdZKO3qx98HChmeg448fPdUzld8aFmfLgVh7yymqV+q1lJZ5g==", + "version": "4.17.1", + "resolved": "https://registry.npmjs.org/browserslist/-/browserslist-4.17.1.tgz", + "integrity": "sha512-aLD0ZMDSnF4lUt4ZDNgqi5BUn9BZ7YdQdI/cYlILrhdSSZJLU9aNZoD5/NBmM4SK34APB2e83MOsRt1EnkuyaQ==", "dev": true, "requires": { - "caniuse-lite": "^1.0.30001254", - "colorette": "^1.3.0", - "electron-to-chromium": "^1.3.830", + "caniuse-lite": "^1.0.30001259", + "electron-to-chromium": "^1.3.846", "escalade": "^3.1.1", - "node-releases": "^1.1.75" + "nanocolors": "^0.1.5", + "node-releases": "^1.1.76" } }, "buffer": { @@ -6791,9 +6625,9 @@ "dev": true }, "caniuse-lite": { - "version": "1.0.30001259", - "resolved": "https://registry.npmjs.org/caniuse-lite/-/caniuse-lite-1.0.30001259.tgz", - "integrity": "sha512-V7mQTFhjITxuk9zBpI6nYsiTXhcPe05l+364nZjK7MFK/E7ibvYBSAXr4YcA6oPR8j3ZLM/LN+lUqUVAQEUZFg==", + "version": "1.0.30001261", + "resolved": "https://registry.npmjs.org/caniuse-lite/-/caniuse-lite-1.0.30001261.tgz", + "integrity": "sha512-vM8D9Uvp7bHIN0fZ2KQ4wnmYFpJo/Etb4Vwsuc+ka0tfGDHvOPrFm6S/7CCNLSOkAUjenT2HnUPESdOIL91FaA==", "dev": true }, "chalk": { @@ -6924,12 +6758,6 @@ "integrity": "sha512-dOy+3AuW3a2wNbZHIuMZpTcgjGuLU/uBL/ubcZF9OXbDo8ff4O8yVp5Bf0efS8uEoYo5q4Fx7dY9OgQGXgAsQA==", "dev": true }, - "colorette": { - "version": "1.4.0", - "resolved": "https://registry.npmjs.org/colorette/-/colorette-1.4.0.tgz", - "integrity": "sha512-Y2oEozpomLn7Q3HFP7dpww7AtMJplbM9lGZP6RDfHqmbeRjiwRg4n6VM6j4KLmRke85uWEI7JqF17f3pqdRA0g==", - "dev": true - }, "combined-stream": { "version": "1.0.8", "resolved": "https://registry.npmjs.org/combined-stream/-/combined-stream-1.0.8.tgz", @@ -7011,9 +6839,9 @@ "dev": true }, "debug": { - "version": "4.3.1", - "resolved": "https://registry.npmjs.org/debug/-/debug-4.3.1.tgz", - "integrity": "sha512-doEwdvm4PCeK4K3RQN2ZC2BYUBaxwLARCqZmMjtF8a51J2Rb0xpVloFRnCODwqjpwnAoao4pelN8l3RJdv3gRQ==", + "version": "4.3.2", + "resolved": "https://registry.npmjs.org/debug/-/debug-4.3.2.tgz", + "integrity": "sha512-mOp8wKcvj7XxC78zLgw/ZA+6TSgkoE2C/ienthhRD298T7UNwAg9diBpLRxC0mOezLl4B0xV7M0cCO6P/O0Xhw==", "dev": true, "requires": { "ms": "2.1.2" @@ -7139,9 +6967,9 @@ "dev": true }, "electron-to-chromium": { - "version": "1.3.827", - "resolved": "https://registry.npmjs.org/electron-to-chromium/-/electron-to-chromium-1.3.827.tgz", - "integrity": "sha512-ye+4uQOY/jbjRutMcE/EmOcNwUeo1qo9aKL2tPyb09cU3lmxNeyDF4RWiemmkknW+p29h7dyDqy02higTxc9/A==", + "version": "1.3.853", + "resolved": "https://registry.npmjs.org/electron-to-chromium/-/electron-to-chromium-1.3.853.tgz", + "integrity": "sha512-W4U8n+U8I5/SUaFcqZgbKRmYZwcyEIQVBDf+j5QQK6xChjXnQD+wj248eGR9X4u+dDmDR//8vIfbu4PrdBBIoQ==", "dev": true }, "emoji-regex": { @@ -7737,9 +7565,9 @@ } }, "is-core-module": { - "version": "2.6.0", - "resolved": "https://registry.npmjs.org/is-core-module/-/is-core-module-2.6.0.tgz", - "integrity": "sha512-wShG8vs60jKfPWpF2KZRaAtvt3a20OAn7+IJ6hLPECpSABLcKtFKTTI4ZtH5QcBruBHlq+WsdHWyz0BCZW7svQ==", + "version": "2.7.0", + "resolved": "https://registry.npmjs.org/is-core-module/-/is-core-module-2.7.0.tgz", + "integrity": "sha512-ByY+tjCciCr+9nLryBYcSD50EOGWt95c7tIsKTG1J2ixKKXPvF7Ej3AVd+UfDydAJom3biBGDBALaO79ktwgEQ==", "dev": true, "requires": { "has": "^1.0.3" @@ -7764,9 +7592,9 @@ "dev": true }, "is-glob": { - "version": "4.0.1", - "resolved": "https://registry.npmjs.org/is-glob/-/is-glob-4.0.1.tgz", - "integrity": "sha512-5G0tKtBTFImOqDnLB2hG6Bp2qcKEFduo4tZu9MT/H6NQv/ghhy30o55ufafxJ/LdH79LLs2Kfrn85TLKyA7BUg==", + "version": "4.0.3", + "resolved": "https://registry.npmjs.org/is-glob/-/is-glob-4.0.3.tgz", + "integrity": "sha512-xelSayHH36ZgE7ZWhli7pW34hNbNl8Ojv5KVmkJD4hBdD3th8Tfk9vYasLM+mXWOZhFkgZfxhLSnrwRr4elSSg==", "dev": true, "requires": { "is-extglob": "^2.1.1" @@ -7885,9 +7713,9 @@ "dev": true }, "istanbul-lib-coverage": { - "version": "3.0.0", - "resolved": "https://registry.npmjs.org/istanbul-lib-coverage/-/istanbul-lib-coverage-3.0.0.tgz", - "integrity": "sha512-UiUIqxMgRDET6eR+o5HbfRYP1l0hqkWOs7vNxC/mggutCMUIhWMm8gAHb8tHlyfD3/l6rlgNA5cKdDzEAf6hEg==", + "version": "3.0.1", + "resolved": "https://registry.npmjs.org/istanbul-lib-coverage/-/istanbul-lib-coverage-3.0.1.tgz", + "integrity": "sha512-GvCYYTxaCPqwMjobtVcVKvSHtAGe48MNhGjpK8LtVF8K0ISX7hCKl85LgtuaSneWVyQmaGcW3iXVV3GaZSLpmQ==", "dev": true }, "istanbul-lib-hook": { @@ -8213,16 +8041,16 @@ "dev": true }, "mocha": { - "version": "9.1.1", - "resolved": "https://registry.npmjs.org/mocha/-/mocha-9.1.1.tgz", - "integrity": "sha512-0wE74YMgOkCgBUj8VyIDwmLUjTsS13WV1Pg7l0SHea2qzZzlq7MDnfbPsHKcELBRk3+izEVkRofjmClpycudCA==", + "version": "9.1.2", + "resolved": "https://registry.npmjs.org/mocha/-/mocha-9.1.2.tgz", + "integrity": "sha512-ta3LtJ+63RIBP03VBjMGtSqbe6cWXRejF9SyM9Zyli1CKZJZ+vfCTj3oW24V7wAphMJdpOFLoMI3hjJ1LWbs0w==", "dev": true, "requires": { "@ungap/promise-all-settled": "1.1.2", "ansi-colors": "4.1.1", "browser-stdout": "1.3.1", "chokidar": "3.5.2", - "debug": "4.3.1", + "debug": "4.3.2", "diff": "5.0.0", "escape-string-regexp": "4.0.0", "find-up": "5.0.0", @@ -8233,12 +8061,11 @@ "log-symbols": "4.1.0", "minimatch": "3.0.4", "ms": "2.1.3", - "nanoid": "3.1.23", + "nanoid": "3.1.25", "serialize-javascript": "6.0.0", "strip-json-comments": "3.1.1", "supports-color": "8.1.1", "which": "2.0.2", - "wide-align": "1.1.3", "workerpool": "6.1.5", "yargs": "16.2.0", "yargs-parser": "20.2.4", @@ -8257,10 +8084,16 @@ "integrity": "sha512-nnbWWOkoWyUsTjKrhgD0dcz22mdkSnpYqbEjIm2nhwhuxlSkpywJmBo8h0ZqJdkp73mb90SssHkN4rsRaBAfAA==", "dev": true }, + "nanocolors": { + "version": "0.1.12", + "resolved": "https://registry.npmjs.org/nanocolors/-/nanocolors-0.1.12.tgz", + "integrity": "sha512-2nMHqg1x5PU+unxX7PGY7AuYxl2qDx7PSrTRjizr8sxdd3l/3hBuWWaki62qmtYm2U5i4Z5E7GbjlyDFhs9/EQ==", + "dev": true + }, "nanoid": { - "version": "3.1.23", - "resolved": "https://registry.npmjs.org/nanoid/-/nanoid-3.1.23.tgz", - "integrity": "sha512-FiB0kzdP0FFVGDKlRLEQ1BgDzU87dy5NnzjeW9YZNt+/c3+q82EQDUwniSAUxp/F0gFNI1ZhKU1FqYsMuqZVnw==", + "version": "3.1.25", + "resolved": "https://registry.npmjs.org/nanoid/-/nanoid-3.1.25.tgz", + "integrity": "sha512-rdwtIXaXCLFAQbnfqDRnI6jaRHp9fTcYBjtFKE8eezcZ7LuLjhUaQGNeMXf1HmRoCH32CLz6XwX0TtxEOS/A3Q==", "dev": true }, "neo-async": { @@ -8300,9 +8133,9 @@ } }, "node-fetch": { - "version": "2.6.4", - "resolved": "https://registry.npmjs.org/node-fetch/-/node-fetch-2.6.4.tgz", - "integrity": "sha512-aD1fO+xtLiSCc9vuD+sYMxpIuQyhHscGSkBEo2o5LTV/3bTEAYvdUii29n8LlO5uLCmWdGP7uVUVXFo5SRdkLA==", + "version": "2.6.5", + "resolved": "https://registry.npmjs.org/node-fetch/-/node-fetch-2.6.5.tgz", + "integrity": "sha512-mmlIVHJEu5rnIxgEgez6b9GgWXbkZj5YZ7fx+2r94a2E+Uirsp6HsPTPlomfdHtpt/B0cdKviwkoaM6pyvUOpQ==", "dev": true, "requires": { "whatwg-url": "^5.0.0" @@ -9101,15 +8934,6 @@ "yargs-parser": "20.2.9" }, "dependencies": { - "debug": { - "version": "4.3.2", - "resolved": "https://registry.npmjs.org/debug/-/debug-4.3.2.tgz", - "integrity": "sha512-mOp8wKcvj7XxC78zLgw/ZA+6TSgkoE2C/ienthhRD298T7UNwAg9diBpLRxC0mOezLl4B0xV7M0cCO6P/O0Xhw==", - "dev": true, - "requires": { - "ms": "2.1.2" - } - }, "lru-cache": { "version": "6.0.0", "resolved": "https://registry.npmjs.org/lru-cache/-/lru-cache-6.0.0.tgz", @@ -9119,12 +8943,6 @@ "yallist": "^4.0.0" } }, - "ms": { - "version": "2.1.2", - "resolved": "https://registry.npmjs.org/ms/-/ms-2.1.2.tgz", - "integrity": "sha512-sGkPx+VjMtmA6MX27oA4FBFELFCZZ4S4XqeGOXCv68tT+jb3vk/RyaKWP0PTKyWtmLSM0b+adUTEvbs1PEaH2w==", - "dev": true - }, "semver": { "version": "7.3.5", "resolved": "https://registry.npmjs.org/semver/-/semver-7.3.5.tgz", @@ -9324,9 +9142,9 @@ } }, "shiki": { - "version": "0.9.10", - "resolved": "https://registry.npmjs.org/shiki/-/shiki-0.9.10.tgz", - "integrity": "sha512-xeM7Oc6hY+6iW5O/T5hor8ul7mEprzyl5y4r5zthEHToQNw7MIhREMgU3r2gKDB0NaMLNrkcEQagudCdzE13Lg==", + "version": "0.9.11", + "resolved": "https://registry.npmjs.org/shiki/-/shiki-0.9.11.tgz", + "integrity": "sha512-tjruNTLFhU0hruCPoJP0y+B9LKOmcqUhTpxn7pcJB3fa+04gFChuEmxmrUfOJ7ZO6Jd+HwMnDHgY3lv3Tqonuw==", "dev": true, "requires": { "jsonc-parser": "^3.0.0", @@ -9346,9 +9164,9 @@ } }, "signal-exit": { - "version": "3.0.4", - "resolved": "https://registry.npmjs.org/signal-exit/-/signal-exit-3.0.4.tgz", - "integrity": "sha512-rqYhcAnZ6d/vTPGghdrw7iumdcbXpsk1b8IG/rz+VWV51DM0p7XCtMoJ3qhPLIbp3tvyt3pKRbaaEMZYpHto8Q==", + "version": "3.0.5", + "resolved": "https://registry.npmjs.org/signal-exit/-/signal-exit-3.0.5.tgz", + "integrity": "sha512-KWcOiKeQj6ZyXx7zq4YxSMgHRlod4czeBQZrPb8OKcohcqAXShm7E20kEMle9WBt26hFcAf0qLOcp5zmY7kOqQ==", "dev": true }, "sinon": { @@ -9456,23 +9274,23 @@ } }, "string-width": { - "version": "4.2.2", - "resolved": "https://registry.npmjs.org/string-width/-/string-width-4.2.2.tgz", - "integrity": "sha512-XBJbT3N4JhVumXE0eoLU9DCjcaF92KLNqTmFCnG1pf8duUxFGwtP6AD6nkjw9a3IdiRtL3E2w3JDiE/xi3vOeA==", + "version": "4.2.3", + "resolved": "https://registry.npmjs.org/string-width/-/string-width-4.2.3.tgz", + "integrity": "sha512-wKyQRQpjJ0sIp62ErSZdGsjMJWsap5oRNihHhu6G7JVO/9jIB6UyevL+tXuOqrng8j/cxKTWyWUwvSTriiZz/g==", "dev": true, "requires": { "emoji-regex": "^8.0.0", "is-fullwidth-code-point": "^3.0.0", - "strip-ansi": "^6.0.0" + "strip-ansi": "^6.0.1" } }, "strip-ansi": { - "version": "6.0.0", - "resolved": "https://registry.npmjs.org/strip-ansi/-/strip-ansi-6.0.0.tgz", - "integrity": "sha512-AuvKTrTfQNYNIctbR1K/YGTR1756GycPsg7b9bdV9Duqur4gv6aKqHXah67Z8ImS7WEz5QVcOtlfW2rZEugt6w==", + "version": "6.0.1", + "resolved": "https://registry.npmjs.org/strip-ansi/-/strip-ansi-6.0.1.tgz", + "integrity": "sha512-Y38VPSHcqkFrCpFnQ9vuSXmquuv5oXOKpGeT6aGrr3o3Gc9AlVa6JBfUSOCnbxGGZF+/0ooI7KrPuUSztUdU5A==", "dev": true, "requires": { - "ansi-regex": "^5.0.0" + "ansi-regex": "^5.0.1" } }, "strip-bom": { @@ -9806,48 +9624,6 @@ "integrity": "sha1-2e8H3Od7mQK4o6j6SzHD4/fm6Ho=", "dev": true }, - "wide-align": { - "version": "1.1.3", - "resolved": "https://registry.npmjs.org/wide-align/-/wide-align-1.1.3.tgz", - "integrity": "sha512-QGkOQc8XL6Bt5PwnsExKBPuMKBxnGxWWW3fU55Xt4feHozMUhdUMaBCk290qpm/wG5u/RSKzwdAC4i51YigihA==", - "dev": true, - "requires": { - "string-width": "^1.0.2 || 2" - }, - "dependencies": { - "ansi-regex": { - "version": "3.0.0", - "resolved": "https://registry.npmjs.org/ansi-regex/-/ansi-regex-3.0.0.tgz", - "integrity": "sha1-7QMXwyIGT3lGbAKWa922Bas32Zg=", - "dev": true - }, - "is-fullwidth-code-point": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/is-fullwidth-code-point/-/is-fullwidth-code-point-2.0.0.tgz", - "integrity": "sha1-o7MKXE8ZkYMWeqq5O+764937ZU8=", - "dev": true - }, - "string-width": { - "version": "2.1.1", - "resolved": "https://registry.npmjs.org/string-width/-/string-width-2.1.1.tgz", - "integrity": "sha512-nOqH59deCq9SRHlxq1Aw85Jnt4w6KvLKqWVik6oA9ZklXLNIOlqg4F2yrT1MVaTjAqvVwdfeZ7w7aCvJD7ugkw==", - "dev": true, - "requires": { - "is-fullwidth-code-point": "^2.0.0", - "strip-ansi": "^4.0.0" - } - }, - "strip-ansi": { - "version": "4.0.0", - "resolved": "https://registry.npmjs.org/strip-ansi/-/strip-ansi-4.0.0.tgz", - "integrity": "sha1-qEeQIusaw2iocTibY1JixQXuNo8=", - "dev": true, - "requires": { - "ansi-regex": "^3.0.0" - } - } - } - }, "widest-line": { "version": "3.1.0", "resolved": "https://registry.npmjs.org/widest-line/-/widest-line-3.1.0.tgz", From 068f7f49bf4db822c603697e99b5f906c78ab850 Mon Sep 17 00:00:00 2001 From: leibale Date: Wed, 29 Sep 2021 18:20:50 -0400 Subject: [PATCH 0881/1748] fix EVAL & EVALSHA, add some tests, npm update --- lib/commands/EVAL.ts | 4 ---- lib/commands/EVALSHA.ts | 4 ---- lib/commands/ZRANGESTORE.spec.ts | 11 ++++++++++- lib/commands/generic-transformers.spec.ts | 7 +++++++ lib/commands/generic-transformers.ts | 6 ++---- package-lock.json | 12 ++++++------ 6 files changed, 25 insertions(+), 19 deletions(-) diff --git a/lib/commands/EVAL.ts b/lib/commands/EVAL.ts index 89645df9f3e..f269815b7ec 100644 --- a/lib/commands/EVAL.ts +++ b/lib/commands/EVAL.ts @@ -3,7 +3,3 @@ import { EvalOptions, pushEvalArguments } from './generic-transformers'; export function transformArguments(script: string, options?: EvalOptions): Array { return pushEvalArguments(['EVAL', script], options); } - -export function transformReply(reply: unknown): unknown { - return reply; -} diff --git a/lib/commands/EVALSHA.ts b/lib/commands/EVALSHA.ts index a81595bc4c0..105784cf5f8 100644 --- a/lib/commands/EVALSHA.ts +++ b/lib/commands/EVALSHA.ts @@ -3,7 +3,3 @@ import { EvalOptions, pushEvalArguments } from './generic-transformers'; export function transformArguments(sha1: string, options?: EvalOptions): Array { return pushEvalArguments(['EVALSHA', sha1], options); } - -export function transformReply(reply: unknown): unknown { - return reply; -} diff --git a/lib/commands/ZRANGESTORE.spec.ts b/lib/commands/ZRANGESTORE.spec.ts index 30dee7c0b5b..54055656409 100644 --- a/lib/commands/ZRANGESTORE.spec.ts +++ b/lib/commands/ZRANGESTORE.spec.ts @@ -1,6 +1,6 @@ import { strict as assert } from 'assert'; import { TestRedisServers, itWithClient, describeHandleMinimumRedisVersion } from '../test-utils'; -import { transformArguments } from './ZRANGESTORE'; +import { transformArguments, transformReply } from './ZRANGESTORE'; describe('ZRANGESTORE', () => { describeHandleMinimumRedisVersion([6, 2]); @@ -68,6 +68,15 @@ describe('ZRANGESTORE', () => { }); }); + describe('transformReply', () => { + it('should throw TypeError when reply is not a number', () => { + assert.throws( + () => (transformReply as any)([]), + TypeError + ); + }); + }); + itWithClient(TestRedisServers.OPEN, 'client.zRangeStore', async client => { await client.zAdd('src', { score: 0.5, diff --git a/lib/commands/generic-transformers.spec.ts b/lib/commands/generic-transformers.spec.ts index 9ac72bb1b25..9bde6ebb3af 100644 --- a/lib/commands/generic-transformers.spec.ts +++ b/lib/commands/generic-transformers.spec.ts @@ -300,6 +300,13 @@ describe('Generic Transformers', () => { ); }); + it('with COUNT', () => { + assert.deepEqual( + pushGeoCountArgument([], 1), + ['COUNT', '1'] + ); + }); + it('with ANY', () => { assert.deepEqual( pushGeoCountArgument([], { diff --git a/lib/commands/generic-transformers.ts b/lib/commands/generic-transformers.ts index 87325e61d72..84a6c97b81e 100644 --- a/lib/commands/generic-transformers.ts +++ b/lib/commands/generic-transformers.ts @@ -204,12 +204,10 @@ export function pushGeoSearchArguments( args.push('BYBOX', by.width.toString(), by.height.toString()); } - if (by.unit) { - args.push(by.unit); - } + args.push(by.unit); if (options?.SORT) { - args.push(options?.SORT); + args.push(options.SORT); } pushGeoCountArgument(args, options?.COUNT); diff --git a/package-lock.json b/package-lock.json index e4625a07401..bb840cc6dde 100644 --- a/package-lock.json +++ b/package-lock.json @@ -1789,9 +1789,9 @@ "dev": true }, "node_modules/electron-to-chromium": { - "version": "1.3.853", - "resolved": "https://registry.npmjs.org/electron-to-chromium/-/electron-to-chromium-1.3.853.tgz", - "integrity": "sha512-W4U8n+U8I5/SUaFcqZgbKRmYZwcyEIQVBDf+j5QQK6xChjXnQD+wj248eGR9X4u+dDmDR//8vIfbu4PrdBBIoQ==", + "version": "1.3.854", + "resolved": "https://registry.npmjs.org/electron-to-chromium/-/electron-to-chromium-1.3.854.tgz", + "integrity": "sha512-00/IIC1mFPkq32MhUJyLdcTp7+wsKK2G3Sb65GSas9FKJQGYkDcZ4GwJkkxf5YyM3ETvl6n+toV8OmtXl4IA/g==", "dev": true }, "node_modules/emoji-regex": { @@ -6967,9 +6967,9 @@ "dev": true }, "electron-to-chromium": { - "version": "1.3.853", - "resolved": "https://registry.npmjs.org/electron-to-chromium/-/electron-to-chromium-1.3.853.tgz", - "integrity": "sha512-W4U8n+U8I5/SUaFcqZgbKRmYZwcyEIQVBDf+j5QQK6xChjXnQD+wj248eGR9X4u+dDmDR//8vIfbu4PrdBBIoQ==", + "version": "1.3.854", + "resolved": "https://registry.npmjs.org/electron-to-chromium/-/electron-to-chromium-1.3.854.tgz", + "integrity": "sha512-00/IIC1mFPkq32MhUJyLdcTp7+wsKK2G3Sb65GSas9FKJQGYkDcZ4GwJkkxf5YyM3ETvl6n+toV8OmtXl4IA/g==", "dev": true }, "emoji-regex": { From 43e546df987b58655383030edb570a7034694ee1 Mon Sep 17 00:00:00 2001 From: leibale Date: Fri, 1 Oct 2021 15:31:00 -0400 Subject: [PATCH 0882/1748] fix #1665 - add ZRANGEBYLEX, ZRANGEBYSCORE, ZRANGEBYSCORE_WITHSCORES --- lib/commands/ZRANGEBYLEX.spec.ts | 33 +++++++++++++++++ lib/commands/ZRANGEBYLEX.ts | 35 +++++++++++++++++++ lib/commands/ZRANGEBYSCORE.spec.ts | 33 +++++++++++++++++ lib/commands/ZRANGEBYSCORE.ts | 35 +++++++++++++++++++ lib/commands/ZRANGEBYSCORE_WITHSCORES.spec.ts | 33 +++++++++++++++++ lib/commands/ZRANGEBYSCORE_WITHSCORES.ts | 19 ++++++++++ lib/commands/index.ts | 9 +++++ 7 files changed, 197 insertions(+) create mode 100644 lib/commands/ZRANGEBYLEX.spec.ts create mode 100644 lib/commands/ZRANGEBYLEX.ts create mode 100644 lib/commands/ZRANGEBYSCORE.spec.ts create mode 100644 lib/commands/ZRANGEBYSCORE.ts create mode 100644 lib/commands/ZRANGEBYSCORE_WITHSCORES.spec.ts create mode 100644 lib/commands/ZRANGEBYSCORE_WITHSCORES.ts diff --git a/lib/commands/ZRANGEBYLEX.spec.ts b/lib/commands/ZRANGEBYLEX.spec.ts new file mode 100644 index 00000000000..7f687509548 --- /dev/null +++ b/lib/commands/ZRANGEBYLEX.spec.ts @@ -0,0 +1,33 @@ +import { strict as assert } from 'assert'; +import { TestRedisServers, itWithClient } from '../test-utils'; +import { transformArguments } from './ZRANGEBYLEX'; + +describe('ZRANGEBYLEX', () => { + describe('transformArguments', () => { + it('simple', () => { + assert.deepEqual( + transformArguments('src', '-', '+'), + ['ZRANGEBYLEX', 'src', '-', '+'] + ); + }); + + it('with LIMIT', () => { + assert.deepEqual( + transformArguments('src', '-', '+', { + LIMIT: { + offset: 0, + count: 1 + } + }), + ['ZRANGEBYLEX', 'src', '-', '+', 'LIMIT', '0', '1'] + ); + }); + }); + + itWithClient(TestRedisServers.OPEN, 'client.zRangeByLex', async client => { + assert.deepEqual( + await client.zRangeByLex('src', '-', '+'), + [] + ); + }); +}); diff --git a/lib/commands/ZRANGEBYLEX.ts b/lib/commands/ZRANGEBYLEX.ts new file mode 100644 index 00000000000..5e4475800c2 --- /dev/null +++ b/lib/commands/ZRANGEBYLEX.ts @@ -0,0 +1,35 @@ +import { TransformArgumentsReply } from '.'; +import { transformArgumentNumberInfinity } from './generic-transformers'; + +export const FIRST_KEY_INDEX = 1; + +export const IS_READ_ONLY = true; + +export interface ZRangeByLexOptions { + LIMIT?: { + offset: number; + count: number; + }; +} + +export function transformArguments( + key: string, + min: number | string, + max: number | string, + options?: ZRangeByLexOptions +): TransformArgumentsReply { + const args = [ + 'ZRANGEBYLEX', + key, + typeof min === 'string' ? min : transformArgumentNumberInfinity(min), + typeof max === 'string' ? max : transformArgumentNumberInfinity(max) + ]; + + if (options?.LIMIT) { + args.push('LIMIT', options.LIMIT.offset.toString(), options.LIMIT.count.toString()); + } + + return args; +} + +export declare function transformReply(): Array; diff --git a/lib/commands/ZRANGEBYSCORE.spec.ts b/lib/commands/ZRANGEBYSCORE.spec.ts new file mode 100644 index 00000000000..0419b232563 --- /dev/null +++ b/lib/commands/ZRANGEBYSCORE.spec.ts @@ -0,0 +1,33 @@ +import { strict as assert } from 'assert'; +import { TestRedisServers, itWithClient } from '../test-utils'; +import { transformArguments } from './ZRANGEBYSCORE'; + +describe('ZRANGEBYSCORE', () => { + describe('transformArguments', () => { + it('simple', () => { + assert.deepEqual( + transformArguments('src', 0, 1), + ['ZRANGEBYSCORE', 'src', '0', '1'] + ); + }); + + it('with LIMIT', () => { + assert.deepEqual( + transformArguments('src', 0, 1, { + LIMIT: { + offset: 0, + count: 1 + } + }), + ['ZRANGEBYSCORE', 'src', '0', '1', 'LIMIT', '0', '1'] + ); + }); + }); + + itWithClient(TestRedisServers.OPEN, 'client.zRangeByScore', async client => { + assert.deepEqual( + await client.zRangeByScore('src', 0, 1), + [] + ); + }); +}); diff --git a/lib/commands/ZRANGEBYSCORE.ts b/lib/commands/ZRANGEBYSCORE.ts new file mode 100644 index 00000000000..1932683f955 --- /dev/null +++ b/lib/commands/ZRANGEBYSCORE.ts @@ -0,0 +1,35 @@ +import { TransformArgumentsReply } from '.'; +import { transformArgumentNumberInfinity } from './generic-transformers'; + +export const FIRST_KEY_INDEX = 1; + +export const IS_READ_ONLY = true; + +export interface ZRangeByScoreOptions { + LIMIT?: { + offset: number; + count: number; + }; +} + +export function transformArguments( + key: string, + min: number | string, + max: number | string, + options?: ZRangeByScoreOptions +): TransformArgumentsReply { + const args = [ + 'ZRANGEBYSCORE', + key, + typeof min === 'string' ? min : transformArgumentNumberInfinity(min), + typeof max === 'string' ? max : transformArgumentNumberInfinity(max) + ]; + + if (options?.LIMIT) { + args.push('LIMIT', options.LIMIT.offset.toString(), options.LIMIT.count.toString()); + } + + return args; +} + +export declare function transformReply(): Array; diff --git a/lib/commands/ZRANGEBYSCORE_WITHSCORES.spec.ts b/lib/commands/ZRANGEBYSCORE_WITHSCORES.spec.ts new file mode 100644 index 00000000000..84d1aeb0aad --- /dev/null +++ b/lib/commands/ZRANGEBYSCORE_WITHSCORES.spec.ts @@ -0,0 +1,33 @@ +import { strict as assert } from 'assert'; +import { TestRedisServers, itWithClient } from '../test-utils'; +import { transformArguments } from './ZRANGEBYSCORE_WITHSCORES'; + +describe('ZRANGEBYSCORE WITHSCORES', () => { + describe('transformArguments', () => { + it('simple', () => { + assert.deepEqual( + transformArguments('src', 0, 1), + ['ZRANGEBYSCORE', 'src', '0', '1', 'WITHSCORES'] + ); + }); + + it('with LIMIT', () => { + assert.deepEqual( + transformArguments('src', 0, 1, { + LIMIT: { + offset: 0, + count: 1 + } + }), + ['ZRANGEBYSCORE', 'src', '0', '1', 'LIMIT', '0', '1', 'WITHSCORES'] + ); + }); + }); + + itWithClient(TestRedisServers.OPEN, 'client.zRangeByScoreWithScores', async client => { + assert.deepEqual( + await client.zRangeByScoreWithScores('src', 0, 1), + [] + ); + }); +}); diff --git a/lib/commands/ZRANGEBYSCORE_WITHSCORES.ts b/lib/commands/ZRANGEBYSCORE_WITHSCORES.ts new file mode 100644 index 00000000000..050ebf58936 --- /dev/null +++ b/lib/commands/ZRANGEBYSCORE_WITHSCORES.ts @@ -0,0 +1,19 @@ +import { TransformArgumentsReply } from '.'; +import { transformReplySortedSetWithScores } from './generic-transformers'; +import { ZRangeByScoreOptions, transformArguments as transformZRangeByScoreArguments } from './ZRANGEBYSCORE'; + +export { FIRST_KEY_INDEX, IS_READ_ONLY } from './ZRANGEBYSCORE'; + +export function transformArguments( + key: string, + min: number | string, + max: number | string, + options?: ZRangeByScoreOptions +): TransformArgumentsReply { + return [ + ...transformZRangeByScoreArguments(key, min, max, options), + 'WITHSCORES' + ]; +} + +export const transformReply = transformReplySortedSetWithScores; diff --git a/lib/commands/index.ts b/lib/commands/index.ts index 192968cb2ae..03a44ad7947 100644 --- a/lib/commands/index.ts +++ b/lib/commands/index.ts @@ -232,6 +232,9 @@ import * as ZRANDMEMBER_COUNT from './ZRANDMEMBER_COUNT'; import * as ZRANDMEMBER from './ZRANDMEMBER'; import * as ZRANGE_WITHSCORES from './ZRANGE_WITHSCORES'; import * as ZRANGE from './ZRANGE'; +import * as ZRANGEBYLEX from './ZRANGEBYLEX'; +import * as ZRANGEBYSCORE_WITHSCORES from './ZRANGEBYSCORE_WITHSCORES'; +import * as ZRANGEBYSCORE from './ZRANGEBYSCORE'; import * as ZRANGESTORE from './ZRANGESTORE'; import * as ZRANK from './ZRANK'; import * as ZREM from './ZREM'; @@ -713,6 +716,12 @@ export default { zRangeWithScores: ZRANGE_WITHSCORES, ZRANGE, zRange: ZRANGE, + ZRANGEBYLEX, + zRangeByLex: ZRANGEBYLEX, + ZRANGEBYSCORE_WITHSCORES, + zRangeByScoreWithScores: ZRANGEBYSCORE_WITHSCORES, + ZRANGEBYSCORE, + zRangeByScore: ZRANGEBYSCORE, ZRANGESTORE, zRangeStore: ZRANGESTORE, ZRANK, From 5eb06bcaa344ae531088677eef55496a8c3b3b94 Mon Sep 17 00:00:00 2001 From: leibale Date: Fri, 1 Oct 2021 15:51:45 -0400 Subject: [PATCH 0883/1748] new issue templates --- .github/ISSUE_TEMPLATE.md | 21 --------------------- .github/ISSUE_TEMPLATE/bug-report.md | 15 +++++++++++++++ .github/ISSUE_TEMPLATE/feature-request.md | 7 +++++++ 3 files changed, 22 insertions(+), 21 deletions(-) delete mode 100644 .github/ISSUE_TEMPLATE.md create mode 100644 .github/ISSUE_TEMPLATE/bug-report.md create mode 100644 .github/ISSUE_TEMPLATE/feature-request.md diff --git a/.github/ISSUE_TEMPLATE.md b/.github/ISSUE_TEMPLATE.md deleted file mode 100644 index 9b181d43149..00000000000 --- a/.github/ISSUE_TEMPLATE.md +++ /dev/null @@ -1,21 +0,0 @@ ---- -title: ⚠️ Bug report -labels: needs-triage ---- - -### Issue - -> Describe your issue here - ---- - -### Environment - - - - **Node.js Version**: `VERSION_HERE` - - - - **Redis Server Version**: `VERSION_HERE` - - - - **Platform**: `PLATFORM_HERE` diff --git a/.github/ISSUE_TEMPLATE/bug-report.md b/.github/ISSUE_TEMPLATE/bug-report.md new file mode 100644 index 00000000000..a7fae8eeb11 --- /dev/null +++ b/.github/ISSUE_TEMPLATE/bug-report.md @@ -0,0 +1,15 @@ +--- +name: Bug report +about: Create a report to help us improve +title: '' +labels: Bug +assignees: '' +--- + + + +**Environment:** + - **Node.js Version**: + - **Redis Server Version**: + - **Node Redis Version**: + - **Platform**: diff --git a/.github/ISSUE_TEMPLATE/feature-request.md b/.github/ISSUE_TEMPLATE/feature-request.md new file mode 100644 index 00000000000..0645d6e1a83 --- /dev/null +++ b/.github/ISSUE_TEMPLATE/feature-request.md @@ -0,0 +1,7 @@ +--- +name: Feature request +about: Suggest an idea for this project +title: '' +labels: Bug +assignees: '' +--- From 6e7844eca0c0dcb164bb29406d2612fa441b408b Mon Sep 17 00:00:00 2001 From: leibale Date: Mon, 4 Oct 2021 17:30:18 -0400 Subject: [PATCH 0884/1748] add all COMMAND commands --- lib/commands/COMMAND.spec.ts | 28 +++++++++ lib/commands/COMMAND.ts | 12 ++++ lib/commands/COMMAND_COUNT.spec.ts | 26 ++++++++ lib/commands/COMMAND_COUNT.ts | 9 +++ lib/commands/COMMAND_GETKEYS.spec.ts | 26 ++++++++ lib/commands/COMMAND_GETKEYS.ts | 9 +++ lib/commands/COMMAND_INFO.spec.ts | 28 +++++++++ lib/commands/COMMAND_INFO.ts | 12 ++++ lib/commands/generic-transformers.spec.ts | 28 ++++++++- lib/commands/generic-transformers.ts | 76 +++++++++++++++++++++++ lib/commands/index.ts | 12 ++++ 11 files changed, 265 insertions(+), 1 deletion(-) create mode 100644 lib/commands/COMMAND.spec.ts create mode 100644 lib/commands/COMMAND.ts create mode 100644 lib/commands/COMMAND_COUNT.spec.ts create mode 100644 lib/commands/COMMAND_COUNT.ts create mode 100644 lib/commands/COMMAND_GETKEYS.spec.ts create mode 100644 lib/commands/COMMAND_GETKEYS.ts create mode 100644 lib/commands/COMMAND_INFO.spec.ts create mode 100644 lib/commands/COMMAND_INFO.ts diff --git a/lib/commands/COMMAND.spec.ts b/lib/commands/COMMAND.spec.ts new file mode 100644 index 00000000000..e2c563862fb --- /dev/null +++ b/lib/commands/COMMAND.spec.ts @@ -0,0 +1,28 @@ +import { strict as assert } from 'assert'; +import { itWithClient, TestRedisServers } from '../test-utils'; +import { transformArguments } from './COMMAND'; +import { CommandCategories, CommandFlags } from './generic-transformers'; + +describe('COMMAND', () => { + it('transformArguments', () => { + assert.deepEqual( + transformArguments(), + ['COMMAND'] + ); + }); + + itWithClient(TestRedisServers.OPEN, 'client.command', async client => { + assert.deepEqual( + (await client.command()).find(command => command.name === 'ping'), + { + name: 'ping', + arity: -1, + flags: new Set([CommandFlags.STALE, CommandFlags.FAST]), + firstKeyIndex: 0, + lastKeyIndex: 0, + step: 0, + categories: new Set([CommandCategories.FAST, CommandCategories.CONNECTION]) + } + ); + }); +}); diff --git a/lib/commands/COMMAND.ts b/lib/commands/COMMAND.ts new file mode 100644 index 00000000000..f72cc3f37dd --- /dev/null +++ b/lib/commands/COMMAND.ts @@ -0,0 +1,12 @@ +import { TransformArgumentsReply } from '.'; +import { CommandRawReply, CommandReply, transformCommandReply } from './generic-transformers'; + +export const IS_READ_ONLY = true; + +export function transformArguments(): TransformArgumentsReply { + return ['COMMAND']; +} + +export function transformReply(reply: Array): Array { + return reply.map(transformCommandReply); +} diff --git a/lib/commands/COMMAND_COUNT.spec.ts b/lib/commands/COMMAND_COUNT.spec.ts new file mode 100644 index 00000000000..61ba1bf2540 --- /dev/null +++ b/lib/commands/COMMAND_COUNT.spec.ts @@ -0,0 +1,26 @@ +import { strict as assert } from 'assert'; +import { itWithClient, itWithCluster, TestRedisClusters, TestRedisServers } from '../test-utils'; +import { transformArguments } from './COMMAND_COUNT'; + +describe('COMMAND COUNT', () => { + it('transformArguments', () => { + assert.deepEqual( + transformArguments(), + ['COMMAND', 'COUNT'] + ); + }); + + itWithClient(TestRedisServers.OPEN, 'client.commandCount', async client => { + assert.equal( + typeof await client.commandCount(), + 'number' + ); + }); + + itWithCluster(TestRedisClusters.OPEN, 'cluster.commandCount', async cluster => { + assert.equal( + typeof await cluster.commandCount(), + 'number' + ); + }); +}); diff --git a/lib/commands/COMMAND_COUNT.ts b/lib/commands/COMMAND_COUNT.ts new file mode 100644 index 00000000000..4cdec7bebf1 --- /dev/null +++ b/lib/commands/COMMAND_COUNT.ts @@ -0,0 +1,9 @@ +import { TransformArgumentsReply } from '.'; + +export const IS_READ_ONLY = true; + +export function transformArguments(): TransformArgumentsReply { + return ['COMMAND', 'COUNT']; +} + +declare function transformReply(): number; diff --git a/lib/commands/COMMAND_GETKEYS.spec.ts b/lib/commands/COMMAND_GETKEYS.spec.ts new file mode 100644 index 00000000000..37e91781589 --- /dev/null +++ b/lib/commands/COMMAND_GETKEYS.spec.ts @@ -0,0 +1,26 @@ +import { strict as assert } from 'assert'; +import { itWithClient, itWithCluster, TestRedisClusters, TestRedisServers } from '../test-utils'; +import { transformArguments } from './COMMAND_GETKEYS'; + +describe('COMMAND GETKEYS', () => { + it('transformArguments', () => { + assert.deepEqual( + transformArguments(['GET', 'key']), + ['COMMAND', 'GETKEYS', 'GET', 'key'] + ); + }); + + itWithClient(TestRedisServers.OPEN, 'client.commandGetKeys', async client => { + assert.deepEqual( + await client.commandGetKeys(['GET', 'key']), + ['key'] + ); + }); + + itWithCluster(TestRedisClusters.OPEN, 'cluster.commandGetKeys', async cluster => { + assert.deepEqual( + await cluster.commandGetKeys(['GET', 'key']), + ['key'] + ); + }); +}); diff --git a/lib/commands/COMMAND_GETKEYS.ts b/lib/commands/COMMAND_GETKEYS.ts new file mode 100644 index 00000000000..0b8f38e3d08 --- /dev/null +++ b/lib/commands/COMMAND_GETKEYS.ts @@ -0,0 +1,9 @@ +import { TransformArgumentsReply } from '.'; + +export const IS_READ_ONLY = true; + +export function transformArguments(args: Array): TransformArgumentsReply { + return ['COMMAND', 'GETKEYS', ...args]; +} + +declare function transformReply(): Array; diff --git a/lib/commands/COMMAND_INFO.spec.ts b/lib/commands/COMMAND_INFO.spec.ts new file mode 100644 index 00000000000..c4deec0d220 --- /dev/null +++ b/lib/commands/COMMAND_INFO.spec.ts @@ -0,0 +1,28 @@ +import { strict as assert } from 'assert'; +import { itWithClient, TestRedisServers } from '../test-utils'; +import { transformArguments } from './COMMAND_INFO'; +import { CommandCategories, CommandFlags } from './generic-transformers'; + +describe('COMMAND INFO', () => { + it('transformArguments', () => { + assert.deepEqual( + transformArguments(['PING']), + ['COMMAND', 'INFO', 'PING'] + ); + }); + + itWithClient(TestRedisServers.OPEN, 'client.commandInfo', async client => { + assert.deepEqual( + await client.commandInfo(['PING']), + [{ + name: 'ping', + arity: -1, + flags: new Set([CommandFlags.STALE, CommandFlags.FAST]), + firstKeyIndex: 0, + lastKeyIndex: 0, + step: 0, + categories: new Set([CommandCategories.FAST, CommandCategories.CONNECTION]) + }] + ); + }); +}); diff --git a/lib/commands/COMMAND_INFO.ts b/lib/commands/COMMAND_INFO.ts new file mode 100644 index 00000000000..274c57d6aef --- /dev/null +++ b/lib/commands/COMMAND_INFO.ts @@ -0,0 +1,12 @@ +import { TransformArgumentsReply } from '.'; +import { CommandRawReply, CommandReply, transformCommandReply } from './generic-transformers'; + +export const IS_READ_ONLY = true; + +export function transformArguments(commands: Array): TransformArgumentsReply { + return ['COMMAND', 'INFO', ...commands]; +} + +export function transformReply(reply: Array): Array { + return reply.map(command => command ? transformCommandReply(command) : null); +} diff --git a/lib/commands/generic-transformers.spec.ts b/lib/commands/generic-transformers.spec.ts index 9bde6ebb3af..bdc3ee938cd 100644 --- a/lib/commands/generic-transformers.spec.ts +++ b/lib/commands/generic-transformers.spec.ts @@ -21,7 +21,10 @@ import { pushStringTuplesArguments, pushVerdictArguments, pushVerdictArgument, - pushOptionalVerdictArgument + pushOptionalVerdictArgument, + transformCommandReply, + CommandFlags, + CommandCategories } from './generic-transformers'; describe('Generic Transformers', () => { @@ -626,4 +629,27 @@ describe('Generic Transformers', () => { ); }); }); + + it('transformCommandReply', () => { + assert.deepEqual( + transformCommandReply([ + 'ping', + -1, + [CommandFlags.STALE, CommandFlags.FAST], + 0, + 0, + 0, + [CommandCategories.FAST, CommandCategories.CONNECTION] + ]), + { + name: 'ping', + arity: -1, + flags: new Set([CommandFlags.STALE, CommandFlags.FAST]), + firstKeyIndex: 0, + lastKeyIndex: 0, + step: 0, + categories: new Set([CommandCategories.FAST, CommandCategories.CONNECTION]) + } + ); + }); }); diff --git a/lib/commands/generic-transformers.ts b/lib/commands/generic-transformers.ts index 84a6c97b81e..98e6750f765 100644 --- a/lib/commands/generic-transformers.ts +++ b/lib/commands/generic-transformers.ts @@ -335,3 +335,79 @@ export function pushOptionalVerdictArgument(args: TransformArgumentsReply, name: return pushVerdictArgument(args, value); } + +export enum CommandFlags { + WRITE = 'write', // command may result in modifications + READONLY = 'readonly', // command will never modify keys + DENYOOM = 'denyoom', // reject command if currently out of memory + ADMIN = 'admin', // server admin command + PUBSUB = 'pubsub', // pubsub-related command + NOSCRIPT = 'noscript', // deny this command from scripts + RANDOM = 'random', // command has random results, dangerous for scripts + SORT_FOR_SCRIPT = 'sort_for_script', // if called from script, sort output + LOADING = 'loading', // allow command while database is loading + STALE = 'stale', // allow command while replica has stale data + SKIP_MONITOR = 'skip_monitor', // do not show this command in MONITOR + ASKING = 'asking', // cluster related - accept even if importing + FAST = 'fast', // command operates in constant or log(N) time. Used for latency monitoring. + MOVABLEKEYS = 'movablekeys' // keys have no pre-determined position. You must discover keys yourself. +} + +export enum CommandCategories { + KEYSPACE = '@keyspace', + READ = '@read', + WRITE = '@write', + SET = '@set', + SORTEDSET = '@sortedset', + LIST = '@list', + HASH = '@hash', + STRING = '@string', + BITMAP = '@bitmap', + HYPERLOGLOG = '@hyperloglog', + GEO = '@geo', + STREAM = '@stream', + PUBSUB = '@pubsub', + ADMIN = '@admin', + FAST = '@fast', + SLOW = '@slow', + BLOCKING = '@blocking', + DANGEROUS = '@dangerous', + CONNECTION = '@connection', + TRANSACTION = '@transaction', + SCRIPTING = '@scripting' +} + +export type CommandRawReply = [ + name: string, + arity: number, + flags: Array, + firstKeyIndex: number, + lastKeyIndex: number, + step: number, + categories: Array +]; + +export type CommandReply = { + name: string, + arity: number, + flags: Set, + firstKeyIndex: number, + lastKeyIndex: number, + step: number, + categories: Set +}; + +export function transformCommandReply( + this: void, + [name, arity, flags, firstKeyIndex, lastKeyIndex, step, categories]: CommandRawReply +): CommandReply { + return { + name, + arity, + flags: new Set(flags), + firstKeyIndex, + lastKeyIndex, + step, + categories: new Set(categories) + }; +} diff --git a/lib/commands/index.ts b/lib/commands/index.ts index 03a44ad7947..014aff9e3a6 100644 --- a/lib/commands/index.ts +++ b/lib/commands/index.ts @@ -35,6 +35,10 @@ import * as CLUSTER_MEET from './CLUSTER_MEET'; import * as CLUSTER_RESET from './CLUSTER_RESET'; import * as CLUSTER_SETSLOT from './CLUSTER_SETSLOT'; import * as CLUSTER_SLOTS from './CLUSTER_SLOTS'; +import * as COMMAND_COUNT from './COMMAND_COUNT'; +import * as COMMAND_GETKEYS from './COMMAND_GETKEYS'; +import * as COMMAND_INFO from './COMMAND_INFO'; +import * as COMMAND from './COMMAND'; import * as CONFIG_GET from './CONFIG_GET'; import * as CONFIG_RESETASTAT from './CONFIG_RESETSTAT'; import * as CONFIG_REWRITE from './CONFIG_REWRITE'; @@ -323,6 +327,14 @@ export default { clusterSetSlot: CLUSTER_SETSLOT, CLUSTER_SLOTS, clusterSlots: CLUSTER_SLOTS, + COMMAND_COUNT, + commandCount: COMMAND_COUNT, + COMMAND_GETKEYS, + commandGetKeys: COMMAND_GETKEYS, + COMMAND_INFO, + commandInfo: COMMAND_INFO, + COMMAND, + command: COMMAND, CONFIG_GET, configGet: CONFIG_GET, CONFIG_RESETASTAT, From 833416c4e90a4f301ce257c30ce4309f300ab31e Mon Sep 17 00:00:00 2001 From: leibale Date: Mon, 4 Oct 2021 18:27:32 -0400 Subject: [PATCH 0885/1748] run COMMAND & COMMAND INFO tests only on redis >6 --- lib/commands/COMMAND.spec.ts | 2 ++ lib/commands/COMMAND_INFO.spec.ts | 2 ++ 2 files changed, 4 insertions(+) diff --git a/lib/commands/COMMAND.spec.ts b/lib/commands/COMMAND.spec.ts index e2c563862fb..1f036dadc17 100644 --- a/lib/commands/COMMAND.spec.ts +++ b/lib/commands/COMMAND.spec.ts @@ -24,5 +24,7 @@ describe('COMMAND', () => { categories: new Set([CommandCategories.FAST, CommandCategories.CONNECTION]) } ); + }, { + minimumRedisVersion: [6] }); }); diff --git a/lib/commands/COMMAND_INFO.spec.ts b/lib/commands/COMMAND_INFO.spec.ts index c4deec0d220..59a61f6680a 100644 --- a/lib/commands/COMMAND_INFO.spec.ts +++ b/lib/commands/COMMAND_INFO.spec.ts @@ -24,5 +24,7 @@ describe('COMMAND INFO', () => { categories: new Set([CommandCategories.FAST, CommandCategories.CONNECTION]) }] ); + }, { + minimumRedisVersion: [6] }); }); From ef93bb192d04372d130aca5510c347a866990685 Mon Sep 17 00:00:00 2001 From: Leibale Eidelman Date: Tue, 5 Oct 2021 16:53:32 -0400 Subject: [PATCH 0886/1748] Create SECURITY.md --- SECURITY.md | 16 ++++++++++++++++ 1 file changed, 16 insertions(+) create mode 100644 SECURITY.md diff --git a/SECURITY.md b/SECURITY.md new file mode 100644 index 00000000000..0839a123c96 --- /dev/null +++ b/SECURITY.md @@ -0,0 +1,16 @@ +# Security Policy + +## Supported Versions + +Node Redis is generally backwards compatible with very few exceptions, so we recommend users to always use the latest version to experience stability, performance and security. + +| Version | Supported | +| ------- | ------------------ | +| 4.0.x | :white_check_mark: | +| 3.1.x | :white_check_mark: | +| < 3.1 | :x: | + +## Reporting a Vulnerability + +If you believe you’ve discovered a serious vulnerability, please contact the Node Redis core team at redis@redis.io. We will evaluate your report and if necessary issue a fix and an advisory. If the issue was previously undisclosed, +we’ll also mention your name in the credits. From cc83cee22ce5cc835d54ea2281c5debb2e6cd3ff Mon Sep 17 00:00:00 2001 From: leibale Date: Thu, 7 Oct 2021 10:20:21 -0400 Subject: [PATCH 0887/1748] fix #1671 - add support for all client configurations in cluster --- README.md | 34 +++++++++++++++------------------- index.ts | 2 -- lib/cluster-slots.ts | 12 +++++------- lib/cluster.spec.ts | 4 ++-- lib/cluster.ts | 10 ++++++---- lib/commands/BZPOPMAX.spec.ts | 1 - lib/test-utils.ts | 22 +++++++++++++--------- 7 files changed, 41 insertions(+), 44 deletions(-) diff --git a/README.md b/README.md index c5f0ea1a1c9..c768b691d71 100644 --- a/README.md +++ b/README.md @@ -53,7 +53,7 @@ The above code connects to localhost on port 6379. To connect to a different hos ```typescript createClient({ - url: 'redis://alice:foobared@awesome.redis.server:6380', + url: 'redis://alice:foobared@awesome.redis.server:6380' }); ``` @@ -78,7 +78,7 @@ Modifiers to commands are specified using a JavaScript object: ```typescript await client.set('key', 'value', { EX: 10, - NX: true, + NX: true }); ``` @@ -181,12 +181,9 @@ for await (const key of client.scanIterator()) { This works with `HSCAN`, `SSCAN`, and `ZSCAN` too: ```typescript -for await (const member of client.hScanIterator('hash')) { -} -for await (const { field, value } of client.sScanIterator('set')) { -} -for await (const { member, score } of client.zScanIterator('sorted-set')) { -} +for await (const member of client.hScanIterator('hash')) {} +for await (const { field, value } of client.sScanIterator('set')) {} +for await (const { member, score } of client.zScanIterator('sorted-set')) {} ``` You can override the default options by providing a configuration object: @@ -204,7 +201,8 @@ client.scanIterator({ Define new functions using [Lua scripts](https://redis.io/commands/eval) which execute on the Redis server: ```typescript -import { createClient, defineScript } from 'redis'; +import { createClient } from 'redis'; +import { defineScript } from 'redis/lua-script'; (async () => { const client = createClient({ @@ -218,9 +216,9 @@ import { createClient, defineScript } from 'redis'; }, transformReply(reply: number): number { return reply; - }, - }), - }, + } + }) + } }); await client.connect(); @@ -241,14 +239,12 @@ import { createCluster } from 'redis'; const cluster = createCluster({ rootNodes: [ { - host: '10.0.0.1', - port: 30001, + url: 'redis://10.0.0.1:30001' }, { - host: '10.0.0.2', - port: 30002, - }, - ], + url: 'redis://10.0.0.2:30002' + } + ] }); cluster.on('error', (err) => console.log('Redis Cluster Error', err)); @@ -274,7 +270,7 @@ Of course, if you don't do something with your Promises you're certain to get [u ```typescript await Promise.all([ client.set('Tm9kZSBSZWRpcw==', 'users:1'), - client.sAdd('users:1:tokens', 'Tm9kZSBSZWRpcw=='), + client.sAdd('users:1:tokens', 'Tm9kZSBSZWRpcw==') ]); ``` diff --git a/index.ts b/index.ts index 408cbe3b996..fb448f989c7 100644 --- a/index.ts +++ b/index.ts @@ -6,5 +6,3 @@ export const createClient = RedisClient.create; export const commandOptions = RedisClient.commandOptions; export const createCluster = RedisCluster.create; - -export { defineScript } from './lib/lua-script'; diff --git a/lib/cluster-slots.ts b/lib/cluster-slots.ts index a5155cc53db..4c33f2e0f79 100644 --- a/lib/cluster-slots.ts +++ b/lib/cluster-slots.ts @@ -2,7 +2,7 @@ import calculateSlot from 'cluster-key-slot'; import RedisClient, { RedisClientType } from './client'; import { RedisSocketOptions } from './socket'; import { RedisClusterMasterNode, RedisClusterReplicaNode } from './commands/CLUSTER_NODES'; -import { RedisClusterOptions } from './cluster'; +import { RedisClusterClientOptions, RedisClusterOptions } from './cluster'; import { RedisModules } from './commands'; import { RedisLuaScripts } from './lua-script'; @@ -39,21 +39,19 @@ export default class RedisClusterSlots): Promise { - if (await this.#discoverNodes(startWith.options?.socket)) return; + if (await this.#discoverNodes(startWith.options)) return; for (const { client } of this.#nodeByUrl.values()) { if (client === startWith) continue; - if (await this.#discoverNodes(client.options?.socket)) return; + if (await this.#discoverNodes(client.options)) return; } throw new Error('None of the cluster nodes is available'); } - async #discoverNodes(socketOptions?: RedisSocketOptions): Promise { - const client = RedisClient.create({ - socket: socketOptions - }); + async #discoverNodes(clientOptions?: RedisClusterClientOptions): Promise { + const client = RedisClient.create(clientOptions); await client.connect(); diff --git a/lib/cluster.spec.ts b/lib/cluster.spec.ts index b7dbe50c908..22ae204f9aa 100644 --- a/lib/cluster.spec.ts +++ b/lib/cluster.spec.ts @@ -8,7 +8,7 @@ import { ClusterSlotStates } from './commands/CLUSTER_SETSLOT'; describe('Cluster', () => { it('sendCommand', async () => { const cluster = RedisCluster.create({ - rootNodes: TEST_REDIS_CLUSTERES[TestRedisClusters.OPEN], + ...TEST_REDIS_CLUSTERES[TestRedisClusters.OPEN], useReplicas: true }); @@ -42,7 +42,7 @@ describe('Cluster', () => { it('scripts', async () => { const cluster = RedisCluster.create({ - rootNodes: TEST_REDIS_CLUSTERES[TestRedisClusters.OPEN], + ...TEST_REDIS_CLUSTERES[TestRedisClusters.OPEN], scripts: { add: defineScript({ NUMBER_OF_KEYS: 0, diff --git a/lib/cluster.ts b/lib/cluster.ts index 87dcec17b71..565bb859b84 100644 --- a/lib/cluster.ts +++ b/lib/cluster.ts @@ -1,14 +1,16 @@ import { RedisCommand, RedisCommandReply, RedisModules, TransformArgumentsReply } from './commands'; -import RedisClient, { ClientCommandOptions, RedisClientType, WithPlugins } from './client'; -import { RedisSocketOptions } from './socket'; +import RedisClient, { ClientCommandOptions, RedisClientOptions, RedisClientType, WithPlugins } from './client'; import RedisClusterSlots, { ClusterNode } from './cluster-slots'; import { RedisLuaScript, RedisLuaScripts } from './lua-script'; import { extendWithModulesAndScripts, extendWithDefaultCommands, transformCommandArguments, transformCommandReply } from './commander'; import RedisMultiCommand, { MultiQueuedCommand, RedisMultiCommandType } from './multi-command'; import { EventEmitter } from 'events'; -export interface RedisClusterOptions { - rootNodes: Array; +export type RedisClusterClientOptions = Omit, 'modules' | 'scripts'>; + +export interface RedisClusterOptions { + rootNodes: Array; + defaults?: RedisClusterClientOptions; modules?: M; scripts?: S; useReplicas?: boolean; diff --git a/lib/commands/BZPOPMAX.spec.ts b/lib/commands/BZPOPMAX.spec.ts index c4bcc321b29..090dfba096d 100644 --- a/lib/commands/BZPOPMAX.spec.ts +++ b/lib/commands/BZPOPMAX.spec.ts @@ -2,7 +2,6 @@ import { strict as assert } from 'assert'; import { TestRedisServers, itWithClient } from '../test-utils'; import { transformArguments, transformReply } from './BZPOPMAX'; import { commandOptions } from '../../index'; -import { describe } from 'mocha'; describe('BZPOPMAX', () => { describe('transformArguments', () => { diff --git a/lib/test-utils.ts b/lib/test-utils.ts index 713a1a3434a..bc3c0514606 100644 --- a/lib/test-utils.ts +++ b/lib/test-utils.ts @@ -5,7 +5,7 @@ import { once } from 'events'; import { RedisSocketOptions } from './socket'; import which from 'which'; import { SinonSpy } from 'sinon'; -import RedisCluster, { RedisClusterType } from './cluster'; +import RedisCluster, { RedisClusterOptions, RedisClusterType } from './cluster'; import { promises as fs } from 'fs'; import { Context as MochaContext } from 'mocha'; import { promiseTimeout } from './utils'; @@ -60,7 +60,7 @@ export enum TestRedisClusters { OPEN } -export const TEST_REDIS_CLUSTERES: Record> = {}; +export const TEST_REDIS_CLUSTERES: Record> = {}; let port = 6379; @@ -248,9 +248,13 @@ async function spawnPasswordServer(): Promise { } async function spawnOpenCluster(): Promise { - TEST_REDIS_CLUSTERES[TestRedisClusters.OPEN] = (await spawnGlobalRedisCluster(TestRedisClusters.OPEN, 3)).map(port => ({ - port - })); + TEST_REDIS_CLUSTERES[TestRedisClusters.OPEN] = { + rootNodes: (await spawnGlobalRedisCluster(TestRedisClusters.OPEN, 3)).map(port => ({ + socket: { + port + } + })) + }; } before(function () { @@ -314,9 +318,7 @@ export function itWithCluster( it(title, async function () { if (handleMinimumRedisVersion(this, options?.minimumRedisVersion)) return; - const cluster = RedisCluster.create({ - rootNodes: TEST_REDIS_CLUSTERES[type] - }); + const cluster = RedisCluster.create(TEST_REDIS_CLUSTERES[type]); await cluster.connect(); @@ -337,7 +339,9 @@ export function itWithDedicatedCluster(title: string, fn: (cluster: RedisCluster const spawnResults = await spawnRedisCluster(null, 3), cluster = RedisCluster.create({ rootNodes: [{ - port: spawnResults[0].port + socket: { + port: spawnResults[0].port + } }] }); From d7026f619e66eadb186c15589cb9bc5cc7d03de4 Mon Sep 17 00:00:00 2001 From: leibale Date: Thu, 7 Oct 2021 11:44:45 -0400 Subject: [PATCH 0888/1748] ref #1671 - add support for defaults --- lib/cluster-slots.ts | 31 ++++++++++++++++++++++--------- lib/cluster.ts | 2 +- 2 files changed, 23 insertions(+), 10 deletions(-) diff --git a/lib/cluster-slots.ts b/lib/cluster-slots.ts index 4c33f2e0f79..29730bf753d 100644 --- a/lib/cluster-slots.ts +++ b/lib/cluster-slots.ts @@ -1,6 +1,5 @@ import calculateSlot from 'cluster-key-slot'; import RedisClient, { RedisClientType } from './client'; -import { RedisSocketOptions } from './socket'; import { RedisClusterMasterNode, RedisClusterReplicaNode } from './commands/CLUSTER_NODES'; import { RedisClusterClientOptions, RedisClusterOptions } from './cluster'; import { RedisModules } from './commands'; @@ -32,7 +31,7 @@ export default class RedisClusterSlots { for (const rootNode of this.#options.rootNodes) { - if (await this.#discoverNodes(rootNode)) return; + if (await this.#discoverNodes(this.#clientOptionsDefaults(rootNode))) return; } throw new Error('None of the root nodes is available'); @@ -99,6 +98,18 @@ export default class RedisClusterSlots, promises: Array>): ClusterNode { const url = `${nodeData.host}:${nodeData.port}`; clientsInUse.add(url); @@ -107,13 +118,15 @@ export default class RedisClusterSlots, 'module export interface RedisClusterOptions { rootNodes: Array; - defaults?: RedisClusterClientOptions; + defaults?: Partial; modules?: M; scripts?: S; useReplicas?: boolean; From 13d44147db4e4f718c27c7c34f7ab4eb3b009e4c Mon Sep 17 00:00:00 2001 From: leibale Date: Mon, 11 Oct 2021 17:18:41 -0400 Subject: [PATCH 0889/1748] remove some commands from cluster, npm update, clean code, --- benchmark/package-lock.json | 122 +-- benchmark/package.json | 2 +- lib/{ => client}/commands-queue.ts | 6 +- lib/client/commands.ts | 233 ++++++ lib/{client.spec.ts => client/index.spec.ts} | 19 +- lib/{client.ts => client/index.ts} | 90 +- lib/client/multi-command.ts | 132 +++ lib/{ => client}/socket.spec.ts | 0 lib/{ => client}/socket.ts | 4 +- lib/{ => cluster}/cluster-slots.ts | 25 +- lib/cluster/commands.ts | 535 ++++++++++++ .../index.spec.ts} | 13 +- lib/{cluster.ts => cluster/index.ts} | 80 +- lib/cluster/multi-command.ts | 112 +++ lib/commander.ts | 45 +- lib/commands/ACL_DELUSER.ts | 4 +- lib/commands/ACL_SETUSER.ts | 4 +- lib/commands/BITOP.ts | 4 +- lib/commands/BLPOP.ts | 4 +- lib/commands/BRPOP.ts | 4 +- lib/commands/BZPOPMAX.ts | 4 +- lib/commands/BZPOPMIN.ts | 4 +- lib/commands/CLUSTER_INFO.spec.ts | 18 - lib/commands/CLUSTER_NODES.spec.ts | 21 - lib/commands/CLUSTER_SLOTS.ts | 4 +- lib/commands/COMMAND.ts | 4 +- lib/commands/COMMAND_COUNT.spec.ts | 9 +- lib/commands/COMMAND_COUNT.ts | 4 +- lib/commands/COMMAND_GETKEYS.spec.ts | 9 +- lib/commands/COMMAND_GETKEYS.ts | 4 +- lib/commands/COMMAND_INFO.ts | 4 +- lib/commands/DBSIZE.spec.ts | 9 +- lib/commands/DEL.ts | 4 +- lib/commands/ECHO.spec.ts | 9 +- lib/commands/EXISTS.ts | 4 +- lib/commands/GEOHASH.ts | 4 +- lib/commands/GEOPOS.ts | 4 +- lib/commands/GEOSEARCH_WITH.spec.ts | 4 +- lib/commands/GEOSEARCH_WITH.ts | 6 +- lib/commands/GET.ts | 4 +- lib/commands/GETEX.ts | 4 +- lib/commands/HDEL.ts | 4 +- lib/commands/HMGET.ts | 4 +- lib/commands/HSET.ts | 4 +- lib/commands/LASTSAVE.spec.ts | 6 +- lib/commands/LOLWUT.spec.ts | 11 +- lib/commands/LPUSH.ts | 4 +- lib/commands/LPUSHX.ts | 4 +- lib/commands/MEMORY_DOCTOR.spec.ts | 9 +- lib/commands/MEMORY_MALLOC-STATS.spec.ts | 9 +- lib/commands/MEMORY_PURGE.spec.ts | 9 +- lib/commands/MEMORY_USAGE.spec.ts | 9 +- lib/commands/PFADD.ts | 4 +- lib/commands/PFCOUNT.ts | 4 +- lib/commands/PFMERGE.ts | 4 +- lib/commands/PING.spec.ts | 9 +- lib/commands/PUBSUB_CHANNELS.spec.ts | 9 +- lib/commands/PUBSUB_NUMPAT.spec.ts | 9 +- lib/commands/PUBSUB_NUMSUB.spec.ts | 9 +- lib/commands/RPUSH.ts | 4 +- lib/commands/RPUSHX.ts | 4 +- lib/commands/SADD.ts | 4 +- lib/commands/SCRIPT_DEBUG.spec.ts | 9 +- lib/commands/SCRIPT_EXISTS.spec.ts | 9 +- lib/commands/SCRIPT_EXISTS.ts | 4 +- lib/commands/SCRIPT_FLUSH.spec.ts | 9 +- lib/commands/SCRIPT_LOAD.spec.ts | 9 +- lib/commands/SDIFF.ts | 4 +- lib/commands/SDIFFSTORE.ts | 4 +- lib/commands/SET.ts | 4 +- lib/commands/SETBIT.ts | 4 +- lib/commands/SETEX.ts | 4 +- lib/commands/SINTER.ts | 4 +- lib/commands/SINTERSTORE.ts | 4 +- lib/commands/SREM.ts | 4 +- lib/commands/SUNION.ts | 4 +- lib/commands/SUNIONSTORE.ts | 4 +- lib/commands/TOUCH.ts | 4 +- lib/commands/UNLINK.ts | 4 +- lib/commands/UNWATCH.spec.ts | 9 +- lib/commands/WATCH.ts | 4 +- lib/commands/XACK.ts | 4 +- lib/commands/XDEL.ts | 4 +- lib/commands/ZDIFF.ts | 4 +- lib/commands/ZDIFFSTORE.ts | 4 +- lib/commands/ZDIFF_WITHSCORES.ts | 4 +- lib/commands/ZINTER.ts | 4 +- lib/commands/ZINTERSTORE.ts | 4 +- lib/commands/ZINTER_WITHSCORES.ts | 4 +- lib/commands/ZMSCORE.ts | 4 +- lib/commands/ZRANGEBYLEX.ts | 4 +- lib/commands/ZRANGEBYSCORE.ts | 4 +- lib/commands/ZRANGEBYSCORE_WITHSCORES.ts | 4 +- lib/commands/ZREM.ts | 4 +- lib/commands/ZUNION.ts | 4 +- lib/commands/ZUNIONSTORE.ts | 4 +- lib/commands/ZUNION_WITHSCORES.ts | 4 +- lib/commands/generic-transformers.ts | 8 +- lib/commands/index.ts | 784 +----------------- lib/lua-script.ts | 10 +- lib/multi-command.spec.ts | 163 ++-- lib/multi-command.ts | 190 +---- lib/test-utils.ts | 8 +- package-lock.json | 492 ++++++----- package.json | 15 +- tsconfig.json | 8 +- 106 files changed, 1729 insertions(+), 1822 deletions(-) rename lib/{ => client}/commands-queue.ts (97%) create mode 100644 lib/client/commands.ts rename lib/{client.spec.ts => client/index.spec.ts} (97%) rename lib/{client.ts => client/index.ts} (82%) create mode 100644 lib/client/multi-command.ts rename lib/{ => client}/socket.spec.ts (100%) rename lib/{ => client}/socket.ts (98%) rename lib/{ => cluster}/cluster-slots.ts (91%) create mode 100644 lib/cluster/commands.ts rename lib/{cluster.spec.ts => cluster/index.spec.ts} (91%) rename lib/{cluster.ts => cluster/index.ts} (68%) create mode 100644 lib/cluster/multi-command.ts diff --git a/benchmark/package-lock.json b/benchmark/package-lock.json index 767d6870184..49d77a57787 100644 --- a/benchmark/package-lock.json +++ b/benchmark/package-lock.json @@ -7,7 +7,7 @@ "name": "benchmark", "license": "ISC", "dependencies": { - "benny": "^3.6.15", + "benny": "^3.7.0", "v3": "npm:redis@3.1.2", "v4": "file:../" } @@ -24,20 +24,21 @@ }, "devDependencies": { "@istanbuljs/nyc-config-typescript": "^1.0.1", + "@tsconfig/node12": "^1.0.9", "@types/mocha": "^9.0.0", - "@types/node": "^16.9.6", - "@types/sinon": "^10.0.3", + "@types/node": "^16.10.3", + "@types/sinon": "^10.0.4", "@types/which": "^2.0.1", "@types/yallist": "^4.0.1", - "mocha": "^9.1.1", + "mocha": "^9.1.2", "nyc": "^15.1.0", "release-it": "^14.11.6", "sinon": "^11.1.2", "source-map-support": "^0.5.20", - "ts-node": "^10.2.1", - "typedoc": "0.21.9", - "typedoc-github-wiki-theme": "^0.5.1", - "typedoc-plugin-markdown": "3.10.4", + "ts-node": "^10.3.0", + "typedoc": "^0.22.5", + "typedoc-github-wiki-theme": "^0.6.0", + "typedoc-plugin-markdown": "^3.11.3", "typescript": "^4.4.3", "which": "^2.0.2" }, @@ -126,14 +127,6 @@ "node": ">=8" } }, - "node_modules/at-least-node": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/at-least-node/-/at-least-node-1.0.0.tgz", - "integrity": "sha512-+q/t7Ekv1EDY2l6Gda6LLiX14rU9TV20Wa3ofeQmwPFZbOMo9DXrLbOjFaaclkXKWidIaopwAObQDqwWtGUjqg==", - "engines": { - "node": ">= 4.0.0" - } - }, "node_modules/benchmark": { "version": "2.1.4", "resolved": "https://registry.npmjs.org/benchmark/-/benchmark-2.1.4.tgz", @@ -144,20 +137,21 @@ } }, "node_modules/benny": { - "version": "3.6.15", - "resolved": "https://registry.npmjs.org/benny/-/benny-3.6.15.tgz", - "integrity": "sha512-kq6XVGGYVou3Y8KNPs3SEF881vi5fJ8sIf9w69D2rreiNfRicWVWK6u6/mObMw6BiexoHHumtipn5gcu0Tngng==", + "version": "3.7.0", + "resolved": "https://registry.npmjs.org/benny/-/benny-3.7.0.tgz", + "integrity": "sha512-tfQ8s4qwjfyMvFqe7wSGGel2Pzgl5ytwztS72Xh/ynPKJeXFLQ0sSVL95X9wHpqpXqW13pTHsEMCggrNKgeAoA==", "dependencies": { "@arrows/composition": "^1.0.0", "@arrows/dispatch": "^1.0.2", "@arrows/multimethod": "^1.1.6", "benchmark": "^2.1.4", - "fs-extra": "^9.0.1", - "json2csv": "^5.0.4", - "kleur": "^4.1.3", - "log-update": "^4.0.0", - "prettier": "^2.1.2", - "stats-median": "^1.0.1" + "fs-extra": "^10.0.0", + "json2csv": "^5.0.6", + "kleur": "^4.1.4", + "log-update": "^4.0.0" + }, + "engines": { + "node": ">=12" } }, "node_modules/cli-cursor": { @@ -214,17 +208,16 @@ "integrity": "sha512-f3qQ9oQy9j2AhBe/H9VC91wLmKBCCU/gDOnKNAYG5hswO7BLKj09Hc5HYNz9cGI++xlpDCIgDaitVs03ATR84Q==" }, "node_modules/fs-extra": { - "version": "9.1.0", - "resolved": "https://registry.npmjs.org/fs-extra/-/fs-extra-9.1.0.tgz", - "integrity": "sha512-hcg3ZmepS30/7BSFqRvoo3DOMQu7IjqxO5nCDt+zM9XWjb33Wg7ziNT+Qvqbuc3+gWpzO02JubVyk2G4Zvo1OQ==", + "version": "10.0.0", + "resolved": "https://registry.npmjs.org/fs-extra/-/fs-extra-10.0.0.tgz", + "integrity": "sha512-C5owb14u9eJwizKGdchcDUQeFtlSHHthBk8pbX9Vc1PFZrLombudjDnNns88aYslCyF6IY5SUw3Roz6xShcEIQ==", "dependencies": { - "at-least-node": "^1.0.0", "graceful-fs": "^4.2.0", "jsonfile": "^6.0.1", "universalify": "^2.0.0" }, "engines": { - "node": ">=10" + "node": ">=12" } }, "node_modules/graceful-fs": { @@ -338,17 +331,6 @@ "resolved": "https://registry.npmjs.org/platform/-/platform-1.3.6.tgz", "integrity": "sha512-fnWVljUchTro6RiCFvCXBbNhJc2NijN7oIQxbwsyL0buWJPG85v81ehlHI9fXrJsMNgTofEoWIQeClKpgxFLrg==" }, - "node_modules/prettier": { - "version": "2.4.1", - "resolved": "https://registry.npmjs.org/prettier/-/prettier-2.4.1.tgz", - "integrity": "sha512-9fbDAXSBcc6Bs1mZrDYb3XKzDLm4EXXL9sC1LqKP5rZkT6KRr/rf9amVUcODVXgguK/isJz0d0hP72WeaKWsvA==", - "bin": { - "prettier": "bin-prettier.js" - }, - "engines": { - "node": ">=10.13.0" - } - }, "node_modules/redis-commands": { "version": "1.7.0", "resolved": "https://registry.npmjs.org/redis-commands/-/redis-commands-1.7.0.tgz", @@ -406,11 +388,6 @@ "url": "https://github.com/chalk/slice-ansi?sponsor=1" } }, - "node_modules/stats-median": { - "version": "1.0.1", - "resolved": "https://registry.npmjs.org/stats-median/-/stats-median-1.0.1.tgz", - "integrity": "sha512-IYsheLg6dasD3zT/w9+8Iq9tcIQqqu91ZIpJOnIEM25C3X/g4Tl8mhXwW2ZQpbrsJISr9+wizEYgsibN5/b32Q==" - }, "node_modules/string-width": { "version": "4.2.3", "resolved": "https://registry.npmjs.org/string-width/-/string-width-4.2.3.tgz", @@ -555,11 +532,6 @@ "resolved": "https://registry.npmjs.org/astral-regex/-/astral-regex-2.0.0.tgz", "integrity": "sha512-Z7tMw1ytTXt5jqMcOP+OQteU1VuNK9Y02uuJtKQ1Sv69jXQKKg5cibLwGJow8yzZP+eAc18EmLGPal0bp36rvQ==" }, - "at-least-node": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/at-least-node/-/at-least-node-1.0.0.tgz", - "integrity": "sha512-+q/t7Ekv1EDY2l6Gda6LLiX14rU9TV20Wa3ofeQmwPFZbOMo9DXrLbOjFaaclkXKWidIaopwAObQDqwWtGUjqg==" - }, "benchmark": { "version": "2.1.4", "resolved": "https://registry.npmjs.org/benchmark/-/benchmark-2.1.4.tgz", @@ -570,20 +542,18 @@ } }, "benny": { - "version": "3.6.15", - "resolved": "https://registry.npmjs.org/benny/-/benny-3.6.15.tgz", - "integrity": "sha512-kq6XVGGYVou3Y8KNPs3SEF881vi5fJ8sIf9w69D2rreiNfRicWVWK6u6/mObMw6BiexoHHumtipn5gcu0Tngng==", + "version": "3.7.0", + "resolved": "https://registry.npmjs.org/benny/-/benny-3.7.0.tgz", + "integrity": "sha512-tfQ8s4qwjfyMvFqe7wSGGel2Pzgl5ytwztS72Xh/ynPKJeXFLQ0sSVL95X9wHpqpXqW13pTHsEMCggrNKgeAoA==", "requires": { "@arrows/composition": "^1.0.0", "@arrows/dispatch": "^1.0.2", "@arrows/multimethod": "^1.1.6", "benchmark": "^2.1.4", - "fs-extra": "^9.0.1", - "json2csv": "^5.0.4", - "kleur": "^4.1.3", - "log-update": "^4.0.0", - "prettier": "^2.1.2", - "stats-median": "^1.0.1" + "fs-extra": "^10.0.0", + "json2csv": "^5.0.6", + "kleur": "^4.1.4", + "log-update": "^4.0.0" } }, "cli-cursor": { @@ -628,11 +598,10 @@ "integrity": "sha512-f3qQ9oQy9j2AhBe/H9VC91wLmKBCCU/gDOnKNAYG5hswO7BLKj09Hc5HYNz9cGI++xlpDCIgDaitVs03ATR84Q==" }, "fs-extra": { - "version": "9.1.0", - "resolved": "https://registry.npmjs.org/fs-extra/-/fs-extra-9.1.0.tgz", - "integrity": "sha512-hcg3ZmepS30/7BSFqRvoo3DOMQu7IjqxO5nCDt+zM9XWjb33Wg7ziNT+Qvqbuc3+gWpzO02JubVyk2G4Zvo1OQ==", + "version": "10.0.0", + "resolved": "https://registry.npmjs.org/fs-extra/-/fs-extra-10.0.0.tgz", + "integrity": "sha512-C5owb14u9eJwizKGdchcDUQeFtlSHHthBk8pbX9Vc1PFZrLombudjDnNns88aYslCyF6IY5SUw3Roz6xShcEIQ==", "requires": { - "at-least-node": "^1.0.0", "graceful-fs": "^4.2.0", "jsonfile": "^6.0.1", "universalify": "^2.0.0" @@ -716,11 +685,6 @@ "resolved": "https://registry.npmjs.org/platform/-/platform-1.3.6.tgz", "integrity": "sha512-fnWVljUchTro6RiCFvCXBbNhJc2NijN7oIQxbwsyL0buWJPG85v81ehlHI9fXrJsMNgTofEoWIQeClKpgxFLrg==" }, - "prettier": { - "version": "2.4.1", - "resolved": "https://registry.npmjs.org/prettier/-/prettier-2.4.1.tgz", - "integrity": "sha512-9fbDAXSBcc6Bs1mZrDYb3XKzDLm4EXXL9sC1LqKP5rZkT6KRr/rf9amVUcODVXgguK/isJz0d0hP72WeaKWsvA==" - }, "redis-commands": { "version": "1.7.0", "resolved": "https://registry.npmjs.org/redis-commands/-/redis-commands-1.7.0.tgz", @@ -763,11 +727,6 @@ "is-fullwidth-code-point": "^3.0.0" } }, - "stats-median": { - "version": "1.0.1", - "resolved": "https://registry.npmjs.org/stats-median/-/stats-median-1.0.1.tgz", - "integrity": "sha512-IYsheLg6dasD3zT/w9+8Iq9tcIQqqu91ZIpJOnIEM25C3X/g4Tl8mhXwW2ZQpbrsJISr9+wizEYgsibN5/b32Q==" - }, "string-width": { "version": "4.2.3", "resolved": "https://registry.npmjs.org/string-width/-/string-width-4.2.3.tgz", @@ -811,23 +770,24 @@ "version": "file:..", "requires": { "@istanbuljs/nyc-config-typescript": "^1.0.1", + "@tsconfig/node12": "^1.0.9", "@types/mocha": "^9.0.0", - "@types/node": "^16.9.6", - "@types/sinon": "^10.0.3", + "@types/node": "^16.10.3", + "@types/sinon": "^10.0.4", "@types/which": "^2.0.1", "@types/yallist": "^4.0.1", "cluster-key-slot": "1.1.0", "generic-pool": "3.8.2", - "mocha": "^9.1.1", + "mocha": "^9.1.2", "nyc": "^15.1.0", "redis-parser": "3.0.0", "release-it": "^14.11.6", "sinon": "^11.1.2", "source-map-support": "^0.5.20", - "ts-node": "^10.2.1", - "typedoc": "0.21.9", - "typedoc-github-wiki-theme": "^0.5.1", - "typedoc-plugin-markdown": "3.10.4", + "ts-node": "^10.3.0", + "typedoc": "^0.22.5", + "typedoc-github-wiki-theme": "^0.6.0", + "typedoc-plugin-markdown": "^3.11.3", "typescript": "^4.4.3", "which": "^2.0.2", "yallist": "4.0.0" diff --git a/benchmark/package.json b/benchmark/package.json index 5226a5b0c89..f56ad2f23ce 100644 --- a/benchmark/package.json +++ b/benchmark/package.json @@ -10,7 +10,7 @@ "author": "", "license": "ISC", "dependencies": { - "benny": "^3.6.15", + "benny": "^3.7.0", "v3": "npm:redis@3.1.2", "v4": "file:../" } diff --git a/lib/commands-queue.ts b/lib/client/commands-queue.ts similarity index 97% rename from lib/commands-queue.ts rename to lib/client/commands-queue.ts index ef87184193f..791c7638bad 100644 --- a/lib/commands-queue.ts +++ b/lib/client/commands-queue.ts @@ -1,7 +1,7 @@ import LinkedList from 'yallist'; import RedisParser from 'redis-parser'; -import { AbortError } from './errors'; -import { RedisReply } from './commands'; +import { AbortError } from '../errors'; +import { RedisCommandRawReply } from '../commands'; export interface QueueCommandOptions { asap?: boolean; @@ -107,7 +107,7 @@ export default class RedisCommandsQueue { this.#maxLength = maxLength; } - addCommand(args: Array, options?: QueueCommandOptions, bufferMode?: boolean): Promise { + addCommand(args: Array, options?: QueueCommandOptions, bufferMode?: boolean): Promise { if (this.#pubSubState.subscribing || this.#pubSubState.subscribed) { return Promise.reject(new Error('Cannot send commands in PubSub mode')); } else if (this.#maxLength && this.#waitingToBeSent.length + this.#waitingForReply.length >= this.#maxLength) { diff --git a/lib/client/commands.ts b/lib/client/commands.ts new file mode 100644 index 00000000000..c34f34be4fa --- /dev/null +++ b/lib/client/commands.ts @@ -0,0 +1,233 @@ +import CLUSTER_COMMANDS from '../cluster/commands'; +import * as ACL_CAT from '../commands/ACL_CAT'; +import * as ACL_DELUSER from '../commands/ACL_DELUSER'; +import * as ACL_GENPASS from '../commands/ACL_GENPASS'; +import * as ACL_GETUSER from '../commands/ACL_GETUSER'; +import * as ACL_LIST from '../commands/ACL_LIST'; +import * as ACL_LOAD from '../commands/ACL_LOAD'; +import * as ACL_LOG_RESET from '../commands/ACL_LOG_RESET'; +import * as ACL_LOG from '../commands/ACL_LOG'; +import * as ACL_SAVE from '../commands/ACL_SAVE'; +import * as ACL_SETUSER from '../commands/ACL_SETUSER'; +import * as ACL_USERS from '../commands/ACL_USERS'; +import * as ACL_WHOAMI from '../commands/ACL_WHOAMI'; +import * as ASKING from '../commands/ASKING'; +import * as AUTH from '../commands/AUTH'; +import * as BGREWRITEAOF from '../commands/BGREWRITEAOF'; +import * as BGSAVE from '../commands/BGSAVE'; +import * as CLIENT_ID from '../commands/CLIENT_ID'; +import * as CLIENT_INFO from '../commands/CLIENT_INFO'; +import * as CLUSTER_ADDSLOTS from '../commands/CLUSTER_ADDSLOTS'; +import * as CLUSTER_FLUSHSLOTS from '../commands/CLUSTER_FLUSHSLOTS'; +import * as CLUSTER_INFO from '../commands/CLUSTER_INFO'; +import * as CLUSTER_NODES from '../commands/CLUSTER_NODES'; +import * as CLUSTER_MEET from '../commands/CLUSTER_MEET'; +import * as CLUSTER_RESET from '../commands/CLUSTER_RESET'; +import * as CLUSTER_SETSLOT from '../commands/CLUSTER_SETSLOT'; +import * as CLUSTER_SLOTS from '../commands/CLUSTER_SLOTS'; +import * as COMMAND_COUNT from '../commands/COMMAND_COUNT'; +import * as COMMAND_GETKEYS from '../commands/COMMAND_GETKEYS'; +import * as COMMAND_INFO from '../commands/COMMAND_INFO'; +import * as COMMAND from '../commands/COMMAND'; +import * as CONFIG_GET from '../commands/CONFIG_GET'; +import * as CONFIG_RESETASTAT from '../commands/CONFIG_RESETSTAT'; +import * as CONFIG_REWRITE from '../commands/CONFIG_REWRITE'; +import * as CONFIG_SET from '../commands/CONFIG_SET'; +import * as DBSIZE from '../commands/DBSIZE'; +import * as DISCARD from '../commands/DISCARD'; +import * as ECHO from '../commands/ECHO'; +import * as FAILOVER from '../commands/FAILOVER'; +import * as FLUSHALL from '../commands/FLUSHALL'; +import * as FLUSHDB from '../commands/FLUSHDB'; +import * as HELLO from '../commands/HELLO'; +import * as INFO from '../commands/INFO'; +import * as KEYS from '../commands/KEYS'; +import * as LASTSAVE from '../commands/LASTSAVE'; +import * as LOLWUT from '../commands/LOLWUT'; +import * as MEMOERY_DOCTOR from '../commands/MEMORY_DOCTOR'; +import * as MEMORY_MALLOC_STATS from '../commands/MEMORY_MALLOC-STATS'; +import * as MEMORY_PURGE from '../commands/MEMORY_PURGE'; +import * as MEMORY_STATS from '../commands/MEMORY_STATS'; +import * as MEMORY_USAGE from '../commands/MEMORY_USAGE'; +import * as MODULE_LIST from '../commands/MODULE_LIST'; +import * as MODULE_LOAD from '../commands/MODULE_LOAD'; +import * as MODULE_UNLOAD from '../commands/MODULE_UNLOAD'; +import * as MOVE from '../commands/MOVE'; +import * as PING from '../commands/PING'; +import * as PUBSUB_CHANNELS from '../commands/PUBSUB_CHANNELS'; +import * as PUBSUB_NUMPAT from '../commands/PUBSUB_NUMPAT'; +import * as PUBSUB_NUMSUB from '../commands/PUBSUB_NUMSUB'; +import * as RANDOMKEY from '../commands/RANDOMKEY'; +import * as READONLY from '../commands/READONLY'; +import * as READWRITE from '../commands/READWRITE'; +import * as REPLICAOF from '../commands/REPLICAOF'; +import * as RESTORE_ASKING from '../commands/RESTORE-ASKING'; +import * as ROLE from '../commands/ROLE'; +import * as SAVE from '../commands/SAVE'; +import * as SCAN from '../commands/SCAN'; +import * as SCRIPT_DEBUG from '../commands/SCRIPT_DEBUG'; +import * as SCRIPT_EXISTS from '../commands/SCRIPT_EXISTS'; +import * as SCRIPT_FLUSH from '../commands/SCRIPT_FLUSH'; +import * as SCRIPT_KILL from '../commands/SCRIPT_KILL'; +import * as SCRIPT_LOAD from '../commands/SCRIPT_LOAD'; +import * as SHUTDOWN from '../commands/SHUTDOWN'; +import * as SWAPDB from '../commands/SWAPDB'; +import * as TIME from '../commands/TIME'; +import * as UNWATCH from '../commands/UNWATCH'; +import * as WAIT from '../commands/WAIT'; + +export default { + ...CLUSTER_COMMANDS, + ACL_CAT, + aclCat: ACL_CAT, + ACL_DELUSER, + aclDelUser: ACL_DELUSER, + ACL_GENPASS, + aclGenPass: ACL_GENPASS, + ACL_GETUSER, + aclGetUser: ACL_GETUSER, + ACL_LIST, + aclList: ACL_LIST, + ACL_LOAD, + aclLoad: ACL_LOAD, + ACL_LOG_RESET, + aclLogReset: ACL_LOG_RESET, + ACL_LOG, + aclLog: ACL_LOG, + ACL_SAVE, + aclSave: ACL_SAVE, + ACL_SETUSER, + aclSetUser: ACL_SETUSER, + ACL_USERS, + aclUsers: ACL_USERS, + ACL_WHOAMI, + aclWhoAmI: ACL_WHOAMI, + ASKING, + asking: ASKING, + AUTH, + auth: AUTH, + BGREWRITEAOF, + bgRewriteAof: BGREWRITEAOF, + BGSAVE, + bgSave: BGSAVE, + CLIENT_ID, + clientId: CLIENT_ID, + CLIENT_INFO, + clientInfo: CLIENT_INFO, + CLUSTER_ADDSLOTS, + clusterAddSlots: CLUSTER_ADDSLOTS, + CLUSTER_FLUSHSLOTS, + clusterFlushSlots: CLUSTER_FLUSHSLOTS, + CLUSTER_INFO, + clusterInfo: CLUSTER_INFO, + CLUSTER_NODES, + clusterNodes: CLUSTER_NODES, + CLUSTER_MEET, + clusterMeet: CLUSTER_MEET, + CLUSTER_RESET, + clusterReset: CLUSTER_RESET, + CLUSTER_SETSLOT, + clusterSetSlot: CLUSTER_SETSLOT, + CLUSTER_SLOTS, + clusterSlots: CLUSTER_SLOTS, + COMMAND_COUNT, + commandCount: COMMAND_COUNT, + COMMAND_GETKEYS, + commandGetKeys: COMMAND_GETKEYS, + COMMAND_INFO, + commandInfo: COMMAND_INFO, + COMMAND, + command: COMMAND, + CONFIG_GET, + configGet: CONFIG_GET, + CONFIG_RESETASTAT, + configResetStat: CONFIG_RESETASTAT, + CONFIG_REWRITE, + configRewrite: CONFIG_REWRITE, + CONFIG_SET, + configSet: CONFIG_SET, + DBSIZE, + dbSize: DBSIZE, + DISCARD, + discard: DISCARD, + ECHO, + echo: ECHO, + FAILOVER, + failover: FAILOVER, + FLUSHALL, + flushAll: FLUSHALL, + FLUSHDB, + flushDb: FLUSHDB, + HELLO, + hello: HELLO, + INFO, + info: INFO, + KEYS, + keys: KEYS, + LASTSAVE, + lastSave: LASTSAVE, + LOLWUT, + lolwut: LOLWUT, + MEMOERY_DOCTOR, + memoryDoctor: MEMOERY_DOCTOR, + 'MEMORY_MALLOC-STATS': MEMORY_MALLOC_STATS, + memoryMallocStats: MEMORY_MALLOC_STATS, + MEMORY_PURGE, + memoryPurge: MEMORY_PURGE, + MEMORY_STATS, + memoryStats: MEMORY_STATS, + MEMORY_USAGE, + memoryUsage: MEMORY_USAGE, + MODULE_LIST, + moduleList: MODULE_LIST, + MODULE_LOAD, + moduleLoad: MODULE_LOAD, + MODULE_UNLOAD, + moduleUnload: MODULE_UNLOAD, + MOVE, + move: MOVE, + PING, + ping: PING, + PUBSUB_CHANNELS, + pubSubChannels: PUBSUB_CHANNELS, + PUBSUB_NUMPAT, + pubSubNumPat: PUBSUB_NUMPAT, + PUBSUB_NUMSUB, + pubSubNumSub: PUBSUB_NUMSUB, + RANDOMKEY, + randomKey: RANDOMKEY, + READONLY, + readonly: READONLY, + READWRITE, + readwrite: READWRITE, + REPLICAOF, + replicaOf: REPLICAOF, + 'RESTORE-ASKING': RESTORE_ASKING, + restoreAsking: RESTORE_ASKING, + ROLE, + role: ROLE, + SAVE, + save: SAVE, + SCAN, + scan: SCAN, + SCRIPT_DEBUG, + scriptDebug: SCRIPT_DEBUG, + SCRIPT_EXISTS, + scriptExists: SCRIPT_EXISTS, + SCRIPT_FLUSH, + scriptFlush: SCRIPT_FLUSH, + SCRIPT_KILL, + scriptKill: SCRIPT_KILL, + SCRIPT_LOAD, + scriptLoad: SCRIPT_LOAD, + SHUTDOWN, + shutdown: SHUTDOWN, + SWAPDB, + swapDb: SWAPDB, + TIME, + time: TIME, + UNWATCH, + unwatch: UNWATCH, + WAIT, + wait: WAIT, +}; diff --git a/lib/client.spec.ts b/lib/client/index.spec.ts similarity index 97% rename from lib/client.spec.ts rename to lib/client/index.spec.ts index 2cf6ea4964e..e98814d0582 100644 --- a/lib/client.spec.ts +++ b/lib/client/index.spec.ts @@ -1,11 +1,11 @@ import { strict as assert, AssertionError } from 'assert'; import { once } from 'events'; -import { itWithClient, TEST_REDIS_SERVERS, TestRedisServers, waitTillBeenCalled, isRedisVersionGreaterThan } from './test-utils'; -import RedisClient from './client'; -import { AbortError, ClientClosedError, ConnectionTimeoutError, WatchError } from './errors'; -import { defineScript } from './lua-script'; +import { itWithClient, TEST_REDIS_SERVERS, TestRedisServers, waitTillBeenCalled, isRedisVersionGreaterThan } from '../test-utils'; +import RedisClient from '.'; +import { AbortError, ClientClosedError, ConnectionTimeoutError, WatchError } from '../errors'; +import { defineScript } from '../lua-script'; import { spy } from 'sinon'; -import { RedisNetSocketOptions } from './socket'; +import { RedisNetSocketOptions } from '../client/socket'; export const SQUARE_SCRIPT = defineScript({ NUMBER_OF_KEYS: 0, @@ -366,6 +366,15 @@ describe('Client', () => { WatchError ); }); + + itWithClient(TestRedisServers.OPEN, 'execAsPipeline', async client => { + assert.deepEqual( + await client.multi() + .ping() + .exec(true), + ['PONG'] + ); + }); }); it('scripts', async () => { diff --git a/lib/client.ts b/lib/client/index.ts similarity index 82% rename from lib/client.ts rename to lib/client/index.ts index 98169bc302e..5aeffd365da 100644 --- a/lib/client.ts +++ b/lib/client/index.ts @@ -1,79 +1,81 @@ +import COMMANDS from './commands'; +import { RedisCommand, RedisCommandArguments, RedisCommandRawReply, RedisCommandReply, RedisModules, RedisPlugins, RedisScript, RedisScripts } from '../commands'; import RedisSocket, { RedisSocketOptions, RedisNetSocketOptions, RedisTlsSocketOptions } from './socket'; import RedisCommandsQueue, { PubSubListener, PubSubSubscribeCommands, PubSubUnsubscribeCommands, QueueCommandOptions } from './commands-queue'; -import COMMANDS, { RedisCommandReply, TransformArgumentsReply } from './commands'; -import { RedisCommand, RedisModules, RedisReply } from './commands'; -import RedisMultiCommand, { MultiQueuedCommand, RedisMultiCommandType } from './multi-command'; +import RedisClientMultiCommand, { RedisClientMultiCommandType } from './multi-command'; +import { RedisMultiQueuedCommand } from '../multi-command'; import EventEmitter from 'events'; -import { CommandOptions, commandOptions, isCommandOptions } from './command-options'; -import { RedisLuaScript, RedisLuaScripts } from './lua-script'; -import { ScanOptions, ZMember } from './commands/generic-transformers'; -import { ScanCommandOptions } from './commands/SCAN'; -import { HScanTuple } from './commands/HSCAN'; -import { encodeCommand, extendWithDefaultCommands, extendWithModulesAndScripts, transformCommandArguments, transformCommandReply } from './commander'; +import { CommandOptions, commandOptions, isCommandOptions } from '../command-options'; +import { ScanOptions, ZMember } from '../commands/generic-transformers'; +import { ScanCommandOptions } from '../commands/SCAN'; +import { HScanTuple } from '../commands/HSCAN'; +import { encodeCommand, extendWithCommands, extendWithModulesAndScripts, transformCommandArguments, transformCommandReply } from '../commander'; import { Pool, Options as PoolOptions, createPool } from 'generic-pool'; -import { ClientClosedError } from './errors'; +import { ClientClosedError } from '../errors'; import { URL } from 'url'; -export interface RedisClientOptions { +export interface RedisClientOptions extends RedisPlugins { url?: string; socket?: RedisSocketOptions; username?: string; password?: string; database?: number; - modules?: M; - scripts?: S; commandsQueueMaxLength?: number; readonly?: boolean; legacyMode?: boolean; isolationPoolOptions?: PoolOptions; } -export type RedisCommandSignature = +export type RedisClientCommandSignature = (...args: Parameters | [options: CommandOptions, ...rest: Parameters]) => Promise>; type WithCommands = { - [P in keyof typeof COMMANDS]: RedisCommandSignature<(typeof COMMANDS)[P]>; + [P in keyof typeof COMMANDS]: RedisClientCommandSignature<(typeof COMMANDS)[P]>; }; -type WithModules = { +export type WithModules = { [P in keyof M]: { - [C in keyof M[P]]: RedisCommandSignature; + [C in keyof M[P]]: RedisClientCommandSignature; }; }; -type WithScripts = { - [P in keyof S]: RedisCommandSignature; +export type WithScripts = { + [P in keyof S]: RedisClientCommandSignature; }; -export type WithPlugins = - WithCommands & WithModules & WithScripts; +export type RedisClientType = + RedisClient & WithCommands & WithModules & WithScripts; -export type RedisClientType = - WithPlugins & RedisClient; +export type InstantiableRedisClient = + new (...args: ConstructorParameters) => RedisClientType; export interface ClientCommandOptions extends QueueCommandOptions { isolated?: boolean; } -export default class RedisClient extends EventEmitter { +export default class RedisClient extends EventEmitter { static commandOptions(options: ClientCommandOptions): CommandOptions { return commandOptions(options); } - static create(options?: RedisClientOptions): RedisClientType { - const Client = (extendWithModulesAndScripts({ + static extend(plugins?: RedisPlugins): InstantiableRedisClient { + const Client = extendWithModulesAndScripts({ BaseClass: RedisClient, - modules: options?.modules, + modules: plugins?.modules, modulesCommandsExecutor: RedisClient.prototype.commandsExecutor, - scripts: options?.scripts, + scripts: plugins?.scripts, scriptsExecutor: RedisClient.prototype.scriptsExecutor - })); + }); if (Client !== RedisClient) { - Client.prototype.Multi = RedisMultiCommand.extend(options); + Client.prototype.Multi = RedisClientMultiCommand.extend(plugins); } - return new Client(options); + return Client; + } + + static create(options?: RedisClientOptions): RedisClientType { + return new (RedisClient.extend(options))(options); } static parseURL(url: string): RedisClientOptions<{}, {}> { @@ -285,12 +287,12 @@ export default class RedisClient(args: TransformArgumentsReply, options?: ClientCommandOptions, bufferMode?: boolean): Promise { + sendCommand(args: RedisCommandArguments, options?: ClientCommandOptions, bufferMode?: boolean): Promise { return this.#sendCommand(args, options, bufferMode); } // using `#sendCommand` cause `sendCommand` is overwritten in legacy mode - async #sendCommand(args: TransformArgumentsReply, options?: ClientCommandOptions, bufferMode?: boolean): Promise { + async #sendCommand(args: RedisCommandArguments, options?: ClientCommandOptions, bufferMode?: boolean): Promise { if (!this.#socket.isOpen) { throw new ClientClosedError(); } @@ -309,7 +311,7 @@ export default class RedisClient): Promise> { + async scriptsExecutor(script: RedisScript, args: Array): Promise> { const { args: redisArgs, options } = transformCommandArguments(script, args); return transformCommandReply( @@ -319,7 +321,7 @@ export default class RedisClient> { + async executeScript(script: RedisScript, args: RedisCommandArguments, options?: ClientCommandOptions, bufferMode?: boolean): Promise> { try { return await this.#sendCommand([ 'EVALSHA', @@ -341,8 +343,6 @@ export default class RedisClient; async SELECT(options: CommandOptions, db: number): Promise; async SELECT(options?: any, db?: any): Promise { @@ -429,14 +429,14 @@ export default class RedisClient { + multi(): RedisClientMultiCommandType { return new (this as any).Multi( - this.#multiExecutor.bind(this), - this.#options + this.multiExecutor.bind(this), + this.#options?.legacyMode ); } - #multiExecutor(commands: Array, chainId?: symbol): Promise> { + multiExecutor(commands: Array, chainId?: symbol): Promise> { const promise = Promise.all( commands.map(({ args }) => { return this.#queue.addCommand(args, RedisClient.commandOptions({ @@ -508,5 +508,9 @@ export default class RedisClient = + (...args: Parameters) => RedisClientMultiCommandType; + +type WithCommands = { + [P in keyof typeof COMMANDS]: RedisClientMultiCommandSignature<(typeof COMMANDS)[P], M, S> +}; + +type WithModules = { + [P in keyof M]: { + [C in keyof M[P]]: RedisClientMultiCommandSignature; + }; +}; + +type WithScripts = { + [P in keyof S]: RedisClientMultiCommandSignature +}; + +export type RedisClientMultiCommandType = + RedisClientMultiCommand & WithCommands & WithModules & WithScripts; + +export type RedisClientMultiExecutor = (queue: Array, chainId?: symbol) => Promise>; + +export default class RedisClientMultiCommand { + readonly #multi = new RedisMultiCommand(); + readonly #executor: RedisClientMultiExecutor; + + static extend( + plugins?: RedisPlugins + ): new (...args: ConstructorParameters) => RedisClientMultiCommandType { + return extendWithModulesAndScripts({ + BaseClass: RedisClientMultiCommand, + modules: plugins?.modules, + modulesCommandsExecutor: RedisClientMultiCommand.prototype.commandsExecutor, + scripts: plugins?.scripts, + scriptsExecutor: RedisClientMultiCommand.prototype.scriptsExecutor + }); + } + + readonly v4: Record = {}; + + constructor(executor: RedisClientMultiExecutor, legacyMode = false) { + this.#executor = executor; + if (legacyMode) { + this.#legacyMode(); + } + } + + #legacyMode(): void { + this.v4.addCommand = this.addCommand.bind(this); + (this as any).addCommand = (...args: Array>): this => { + this.#multi.addCommand(args.flat()); + return this; + }; + this.v4.exec = this.exec.bind(this); + (this as any).exec = (callback?: (err: Error | null, replies?: Array) => unknown): void => { + this.v4.exec() + .then((reply: Array) => { + if (!callback) return; + + callback(null, reply); + }) + .catch((err: Error) => { + if (!callback) { + // this.emit('error', err); + return; + } + + callback(err); + }); + }; + + for (const name of Object.keys(COMMANDS)) { + this.#defineLegacyCommand(name); + } + } + + #defineLegacyCommand(name: string): void { + (this as any).v4[name] = (this as any)[name].bind(this.v4); + (this as any)[name] = (...args: Array): void => (this as any).addCommand(name, args); + } + + commandsExecutor(command: RedisCommand, args: Array): this { + return this.addCommand( + command.transformArguments(...args), + command.transformReply + ); + } + + addCommand(args: RedisCommandArguments, transformReply?: RedisCommand['transformReply']): this { + this.#multi.addCommand(args, transformReply); + return this; + } + + scriptsExecutor(script: RedisScript, args: Array): this { + this.#multi.addScript(script, args); + return this; + } + + async exec(execAsPipeline = false): Promise> { + if (execAsPipeline) { + return this.execAsPipeline(); + } + + const commands = this.#multi.exec(); + if (!commands) return []; + + return this.#multi.handleExecReplies( + await this.#executor(commands, RedisMultiCommand.generateChainId()) + ); + } + + EXEC = this.exec; + + async execAsPipeline(): Promise> { + if (!this.#multi.queue.length) return []; + + return this.#multi.transformReplies( + await this.#executor(this.#multi.queue) + ); + } +} + +extendWithCommands({ + BaseClass: RedisClientMultiCommand, + commands: COMMANDS, + executor: RedisClientMultiCommand.prototype.commandsExecutor +}); diff --git a/lib/socket.spec.ts b/lib/client/socket.spec.ts similarity index 100% rename from lib/socket.spec.ts rename to lib/client/socket.spec.ts diff --git a/lib/socket.ts b/lib/client/socket.ts similarity index 98% rename from lib/socket.ts rename to lib/client/socket.ts index 8bc94c5ba07..ca48ad4d542 100644 --- a/lib/socket.ts +++ b/lib/client/socket.ts @@ -1,8 +1,8 @@ import EventEmitter from 'events'; import net from 'net'; import tls from 'tls'; -import { ConnectionTimeoutError, ClientClosedError } from './errors'; -import { promiseTimeout } from './utils'; +import { ConnectionTimeoutError, ClientClosedError } from '../errors'; +import { promiseTimeout } from '../utils'; export interface RedisSocketCommonOptions { connectTimeout?: number; diff --git a/lib/cluster-slots.ts b/lib/cluster/cluster-slots.ts similarity index 91% rename from lib/cluster-slots.ts rename to lib/cluster/cluster-slots.ts index 29730bf753d..63834d4b4ca 100644 --- a/lib/cluster-slots.ts +++ b/lib/cluster/cluster-slots.ts @@ -1,16 +1,15 @@ import calculateSlot from 'cluster-key-slot'; -import RedisClient, { RedisClientType } from './client'; -import { RedisClusterMasterNode, RedisClusterReplicaNode } from './commands/CLUSTER_NODES'; -import { RedisClusterClientOptions, RedisClusterOptions } from './cluster'; -import { RedisModules } from './commands'; -import { RedisLuaScripts } from './lua-script'; +import RedisClient, { InstantiableRedisClient, RedisClientType } from '../client'; +import { RedisClusterMasterNode, RedisClusterReplicaNode } from '../commands/CLUSTER_NODES'; +import { RedisClusterClientOptions, RedisClusterOptions } from '.'; +import { RedisModules, RedisScripts } from '../commands'; -export interface ClusterNode { +export interface ClusterNode { id: string; client: RedisClientType; } -interface SlotNodes { +interface SlotNodes { master: ClusterNode; replicas: Array>; clientIterator: IterableIterator> | undefined; @@ -18,14 +17,16 @@ interface SlotNodes { type OnError = (err: unknown) => void; -export default class RedisClusterSlots { - readonly #options: RedisClusterOptions; +export default class RedisClusterSlots { + readonly #options: RedisClusterOptions; + readonly #Client: InstantiableRedisClient; readonly #onError: OnError; readonly #nodeByUrl = new Map>(); readonly #slots: Array> = []; - constructor(options: RedisClusterOptions, onError: OnError) { + constructor(options: RedisClusterOptions, onError: OnError) { this.#options = options; + this.#Client = RedisClient.extend(options); this.#onError = onError; } @@ -50,7 +51,7 @@ export default class RedisClusterSlots { - const client = RedisClient.create(clientOptions); + const client = new this.#Client(clientOptions); await client.connect(); @@ -118,7 +119,7 @@ export default class RedisClusterSlots { it('sendCommand', async () => { @@ -15,7 +15,7 @@ describe('Cluster', () => { await cluster.connect(); try { - await cluster.ping(); + await cluster.publish('channel', 'message'); await cluster.set('a', 'b'); await cluster.set('a{a}', 'bb'); await cluster.set('aa', 'bb'); @@ -32,11 +32,10 @@ describe('Cluster', () => { const key = 'key'; assert.deepEqual( await cluster.multi(key) - .ping() .set(key, 'value') .get(key) .exec(), - ['PONG', 'OK', 'value'] + ['OK', 'value'] ); }); diff --git a/lib/cluster.ts b/lib/cluster/index.ts similarity index 68% rename from lib/cluster.ts rename to lib/cluster/index.ts index 295e193cbc9..4b55a93d4ab 100644 --- a/lib/cluster.ts +++ b/lib/cluster/index.ts @@ -1,27 +1,35 @@ -import { RedisCommand, RedisCommandReply, RedisModules, TransformArgumentsReply } from './commands'; -import RedisClient, { ClientCommandOptions, RedisClientOptions, RedisClientType, WithPlugins } from './client'; +import COMMANDS from './commands'; +import { RedisCommand, RedisCommandArguments, RedisCommandReply, RedisModules, RedisScript, RedisScripts } from '../commands'; +import { ClientCommandOptions, RedisClientCommandSignature, RedisClientOptions, RedisClientType, WithModules, WithScripts } from '../client'; import RedisClusterSlots, { ClusterNode } from './cluster-slots'; -import { RedisLuaScript, RedisLuaScripts } from './lua-script'; -import { extendWithModulesAndScripts, extendWithDefaultCommands, transformCommandArguments, transformCommandReply } from './commander'; -import RedisMultiCommand, { MultiQueuedCommand, RedisMultiCommandType } from './multi-command'; +import { extendWithModulesAndScripts, transformCommandArguments, transformCommandReply, extendWithCommands } from '../commander'; import { EventEmitter } from 'events'; +import RedisClusterMultiCommand, { RedisClusterMultiCommandType } from './multi-command'; +import { RedisMultiQueuedCommand } from '../multi-command'; export type RedisClusterClientOptions = Omit, 'modules' | 'scripts'>; -export interface RedisClusterOptions { - rootNodes: Array; - defaults?: Partial; +export interface RedisClusterPlugins { modules?: M; scripts?: S; +} + +export interface RedisClusterOptions extends RedisClusterPlugins { + rootNodes: Array; + defaults?: Partial; useReplicas?: boolean; maxCommandRedirections?: number; } -export type RedisClusterType = - WithPlugins & RedisCluster; +type WithCommands = { + [P in keyof typeof COMMANDS]: RedisClientCommandSignature<(typeof COMMANDS)[P]>; +}; + +export type RedisClusterType = + RedisCluster & WithCommands & WithModules & WithScripts; -export default class RedisCluster extends EventEmitter { - static #extractFirstKey(command: RedisCommand, originalArgs: Array, redisArgs: TransformArgumentsReply): string | Buffer | undefined { +export default class RedisCluster extends EventEmitter { + static extractFirstKey(command: RedisCommand, originalArgs: Array, redisArgs: RedisCommandArguments): string | Buffer | undefined { if (command.FIRST_KEY_INDEX === undefined) { return undefined; } else if (typeof command.FIRST_KEY_INDEX === 'number') { @@ -31,7 +39,7 @@ export default class RedisCluster(options?: RedisClusterOptions): RedisClusterType { + static create(options?: RedisClusterOptions): RedisClusterType { return new (extendWithModulesAndScripts({ BaseClass: RedisCluster, modules: options?.modules, @@ -41,20 +49,20 @@ export default class RedisCluster; readonly #slots: RedisClusterSlots; - readonly #Multi: new (...args: ConstructorParameters) => RedisMultiCommandType; + readonly #Multi: new (...args: ConstructorParameters) => RedisClusterMultiCommandType; constructor(options: RedisClusterOptions) { super(); this.#options = options; this.#slots = new RedisClusterSlots(options, err => this.emit('error', err)); - this.#Multi = RedisMultiCommand.extend(options); + this.#Multi = RedisClusterMultiCommand.extend(options); } - duplicate(): RedisClusterOptions { - return new (Object.getPrototypeOf(this).constructor)(this.#options); + duplicate(): RedisClusterType { + return new (Object.getPrototypeOf(this).constructor)(); } async connect(): Promise { @@ -67,7 +75,7 @@ export default class RedisCluster( firstKey: string | Buffer | undefined, isReadonly: boolean | undefined, - args: TransformArgumentsReply, + args: RedisCommandArguments, options?: ClientCommandOptions, bufferMode?: boolean, redirections = 0 @@ -101,7 +109,7 @@ export default class RedisCluster): Promise> { + async scriptsExecutor(script: RedisScript, args: Array): Promise> { const { args: redisArgs, options } = transformCommandArguments(script, args); return transformCommandReply( @@ -117,14 +125,14 @@ export default class RedisCluster, - redisArgs: TransformArgumentsReply, + redisArgs: RedisCommandArguments, options?: ClientCommandOptions, redirections = 0 ): Promise> { const client = this.#slots.getClient( - RedisCluster.#extractFirstKey(script, originalArgs, redisArgs), + RedisCluster.extractFirstKey(script, originalArgs, redisArgs), script.IS_READ_ONLY ); @@ -169,20 +177,14 @@ export default class RedisCluster { + multi(routing?: string | Buffer): RedisClusterMultiCommandType { return new this.#Multi( - async (commands: Array, chainId?: symbol) => { - const client = this.#slots.getClient(routing); - - return Promise.all( - commands.map(({ args }) => { - return client.sendCommand(args, RedisClient.commandOptions({ - chainId - })); - }) - ); + async (commands: Array, firstKey?: string | Buffer, chainId?: symbol) => { + return this.#slots + .getClient(firstKey) + .multiExecutor(commands, chainId); }, - this.#options + routing ); } @@ -199,4 +201,8 @@ export default class RedisCluster = + (...args: Parameters) => RedisClusterMultiCommandType; + +type WithCommands = { + [P in keyof typeof COMMANDS]: RedisClusterMultiCommandSignature<(typeof COMMANDS)[P], M, S> +}; + +type WithModules = { + [P in keyof M]: { + [C in keyof M[P]]: RedisClusterMultiCommandSignature; + }; +}; + +type WithScripts = { + [P in keyof S]: RedisClusterMultiCommandSignature +}; + +export type RedisClusterMultiCommandType = + RedisClusterMultiCommand & WithCommands & WithModules & WithScripts; + +export type RedisClusterMultiExecutor = (queue: Array, firstKey?: string | Buffer, chainId?: symbol) => Promise>; + +export default class RedisClusterMultiCommand { + readonly #multi = new RedisMultiCommand(); + readonly #executor: RedisClusterMultiExecutor; + #firstKey: string | Buffer | undefined; + + static extend( + plugins?: RedisPlugins + ): new (...args: ConstructorParameters) => RedisClusterMultiCommandType { + return extendWithModulesAndScripts({ + BaseClass: RedisClusterMultiCommand, + modules: plugins?.modules, + modulesCommandsExecutor: RedisClusterMultiCommand.prototype.commandsExecutor, + scripts: plugins?.scripts, + scriptsExecutor: RedisClusterMultiCommand.prototype.scriptsExecutor + }); + } + + constructor(executor: RedisClusterMultiExecutor, firstKey?: string | Buffer) { + this.#executor = executor; + this.#firstKey = firstKey; + } + + commandsExecutor(command: RedisCommand, args: Array): this { + const transformedArguments = command.transformArguments(...args); + if (!this.#firstKey) { + this.#firstKey = RedisCluster.extractFirstKey(command, args, transformedArguments); + } + + return this.addCommand( + undefined, + transformedArguments, + command.transformReply + ); + } + + addCommand( + firstKey: string | Buffer | undefined, + args: RedisCommandArguments, + transformReply?: RedisCommand['transformReply'] + ): this { + if (!this.#firstKey) { + this.#firstKey = firstKey; + } + + this.#multi.addCommand(args, transformReply); + return this; + } + + scriptsExecutor(script: RedisScript, args: Array): this { + const transformedArguments = this.#multi.addScript(script, args); + if (!this.#firstKey) { + this.#firstKey = RedisCluster.extractFirstKey(script, args, transformedArguments); + } + + return this.addCommand(undefined, transformedArguments); + } + + async exec(execAsPipeline = false): Promise> { + if (execAsPipeline) { + return this.execAsPipeline(); + } + + const commands = this.#multi.exec(); + if (!commands) return []; + + return this.#multi.handleExecReplies( + await this.#executor(commands, this.#firstKey, RedisMultiCommand.generateChainId()) + ); + } + + EXEC = this.exec; + + async execAsPipeline(): Promise> { + return this.#multi.transformReplies( + await this.#executor(this.#multi.queue, this.#firstKey) + ); + } +} + +extendWithCommands({ + BaseClass: RedisClusterMultiCommand, + commands: COMMANDS, + executor: RedisClusterMultiCommand.prototype.commandsExecutor +}); diff --git a/lib/commander.ts b/lib/commander.ts index 4e542bde74a..5871c2f235b 100644 --- a/lib/commander.ts +++ b/lib/commander.ts @@ -1,37 +1,32 @@ -import COMMANDS, { RedisCommand, RedisCommandReply, RedisModules, RedisReply, TransformArgumentsReply } from './commands'; -import { RedisLuaScript, RedisLuaScripts } from './lua-script'; import { CommandOptions, isCommandOptions } from './command-options'; +import { RedisCommand, RedisCommandArguments, RedisCommandRawReply, RedisCommandReply, RedisCommands, RedisModules, RedisScript, RedisScripts } from './commands'; type Instantiable = new(...args: Array) => T; -type CommandExecutor = (this: InstanceType, command: RedisCommand, args: Array) => unknown; +interface ExtendWithCommandsConfig { + BaseClass: T; + commands: RedisCommands; + executor(command: RedisCommand, args: Array): unknown; +} -export function extendWithDefaultCommands(BaseClass: T, executor: CommandExecutor): void { - for (const [name, command] of Object.entries(COMMANDS)) { +export function extendWithCommands({ BaseClass, commands, executor }: ExtendWithCommandsConfig): void { + for (const [name, command] of Object.entries(commands)) { BaseClass.prototype[name] = function (...args: Array): unknown { return executor.call(this, command, args); }; } } -interface ExtendWithModulesAndScriptsConfig< - T extends Instantiable, - M extends RedisModules, - S extends RedisLuaScripts -> { +interface ExtendWithModulesAndScriptsConfig { BaseClass: T; - modules: M | undefined; - modulesCommandsExecutor: CommandExecutor; - scripts: S | undefined; - scriptsExecutor(this: InstanceType, script: RedisLuaScript, args: Array): unknown; + modules?: RedisModules; + modulesCommandsExecutor(this: InstanceType, command: RedisCommand, args: Array): unknown; + scripts?: RedisScripts; + scriptsExecutor(this: InstanceType, script: RedisScript, args: Array): unknown; } -export function extendWithModulesAndScripts< - T extends Instantiable, - M extends RedisModules, - S extends RedisLuaScripts, ->(config: ExtendWithModulesAndScriptsConfig): T { +export function extendWithModulesAndScripts(config: ExtendWithModulesAndScriptsConfig): T { let Commander: T | undefined; if (config.modules) { @@ -39,7 +34,7 @@ export function extendWithModulesAndScripts< constructor(...args: Array) { super(...args); - for (const module of Object.keys(config.modules as RedisModules)) { + for (const module of Object.keys(config.modules!)) { this[module] = new this[module](this); } } @@ -79,7 +74,7 @@ export function transformCommandArguments( command: RedisCommand, args: Array ): { - args: TransformArgumentsReply; + args: RedisCommandArguments; options: CommandOptions | undefined; } { let options; @@ -96,7 +91,7 @@ export function transformCommandArguments( const DELIMITER = '\r\n'; -export function* encodeCommand(args: TransformArgumentsReply): IterableIterator { +export function* encodeCommand(args: RedisCommandArguments): IterableIterator { yield `*${args.length}${DELIMITER}`; for (const arg of args) { @@ -107,7 +102,11 @@ export function* encodeCommand(args: TransformArgumentsReply): IterableIterator< } } -export function transformCommandReply(command: RedisCommand, rawReply: RedisReply, preserved: unknown): RedisCommandReply { +export function transformCommandReply( + command: RedisCommand, + rawReply: RedisCommandRawReply, + preserved: unknown +): RedisCommandReply { if (!command.transformReply) { return rawReply; } diff --git a/lib/commands/ACL_DELUSER.ts b/lib/commands/ACL_DELUSER.ts index e83e4d7d0ab..97f50d48945 100644 --- a/lib/commands/ACL_DELUSER.ts +++ b/lib/commands/ACL_DELUSER.ts @@ -1,7 +1,7 @@ -import { TransformArgumentsReply } from '.'; +import { RedisCommandArguments } from '.'; import { pushVerdictArguments } from './generic-transformers'; -export function transformArguments(username: string | Array): TransformArgumentsReply { +export function transformArguments(username: string | Array): RedisCommandArguments { return pushVerdictArguments(['ACL', 'DELUSER'], username); } diff --git a/lib/commands/ACL_SETUSER.ts b/lib/commands/ACL_SETUSER.ts index a590376ab84..d8734f0a1ca 100644 --- a/lib/commands/ACL_SETUSER.ts +++ b/lib/commands/ACL_SETUSER.ts @@ -1,7 +1,7 @@ -import { TransformArgumentsReply } from '.'; +import { RedisCommandArguments } from '.'; import { pushVerdictArguments } from './generic-transformers'; -export function transformArguments(username: string, rule: string | Array): TransformArgumentsReply { +export function transformArguments(username: string, rule: string | Array): RedisCommandArguments { return pushVerdictArguments(['ACL', 'SETUSER', username], rule); } diff --git a/lib/commands/BITOP.ts b/lib/commands/BITOP.ts index 1a750f811ca..af31f42f1dc 100644 --- a/lib/commands/BITOP.ts +++ b/lib/commands/BITOP.ts @@ -1,11 +1,11 @@ -import { TransformArgumentsReply } from '.'; +import { RedisCommandArguments } from '.'; import { pushVerdictArguments } from './generic-transformers'; export const FIRST_KEY_INDEX = 2; type BitOperations = 'AND' | 'OR' | 'XOR' | 'NOT'; -export function transformArguments(operation: BitOperations, destKey: string, key: string | Array): TransformArgumentsReply { +export function transformArguments(operation: BitOperations, destKey: string, key: string | Array): RedisCommandArguments { return pushVerdictArguments(['BITOP', operation, destKey], key); } diff --git a/lib/commands/BLPOP.ts b/lib/commands/BLPOP.ts index 1061f5e113a..15c52722941 100644 --- a/lib/commands/BLPOP.ts +++ b/lib/commands/BLPOP.ts @@ -1,9 +1,9 @@ -import { TransformArgumentsReply } from '.'; +import { RedisCommandArguments } from '.'; import { pushVerdictArguments } from './generic-transformers'; export const FIRST_KEY_INDEX = 1; -export function transformArguments(keys: string | Buffer | Array, timeout: number): TransformArgumentsReply { +export function transformArguments(keys: string | Buffer | Array, timeout: number): RedisCommandArguments { const args = pushVerdictArguments(['BLPOP'], keys); args.push(timeout.toString()); diff --git a/lib/commands/BRPOP.ts b/lib/commands/BRPOP.ts index 93ded4dbf1a..602ce9c7913 100644 --- a/lib/commands/BRPOP.ts +++ b/lib/commands/BRPOP.ts @@ -1,9 +1,9 @@ -import { TransformArgumentsReply } from '.'; +import { RedisCommandArguments } from '.'; import { pushVerdictArguments } from './generic-transformers'; export const FIRST_KEY_INDEX = 1; -export function transformArguments(key: string | Array, timeout: number): TransformArgumentsReply { +export function transformArguments(key: string | Array, timeout: number): RedisCommandArguments { const args = pushVerdictArguments(['BRPOP'], key); args.push(timeout.toString()); diff --git a/lib/commands/BZPOPMAX.ts b/lib/commands/BZPOPMAX.ts index 3db9ca42cbb..eb6647ce9e3 100644 --- a/lib/commands/BZPOPMAX.ts +++ b/lib/commands/BZPOPMAX.ts @@ -1,9 +1,9 @@ -import { TransformArgumentsReply } from '.'; +import { RedisCommandArguments } from '.'; import { pushVerdictArguments, transformReplyNumberInfinity, ZMember } from './generic-transformers'; export const FIRST_KEY_INDEX = 1; -export function transformArguments(key: string | Array, timeout: number): TransformArgumentsReply { +export function transformArguments(key: string | Array, timeout: number): RedisCommandArguments { const args = pushVerdictArguments(['BZPOPMAX'], key); args.push(timeout.toString()); diff --git a/lib/commands/BZPOPMIN.ts b/lib/commands/BZPOPMIN.ts index 9106ae770da..75b092e543b 100644 --- a/lib/commands/BZPOPMIN.ts +++ b/lib/commands/BZPOPMIN.ts @@ -1,9 +1,9 @@ -import { TransformArgumentsReply } from '.'; +import { RedisCommandArguments } from '.'; import { pushVerdictArguments, transformReplyNumberInfinity, ZMember } from './generic-transformers'; export const FIRST_KEY_INDEX = 1; -export function transformArguments(key: string | Array, timeout: number): TransformArgumentsReply { +export function transformArguments(key: string | Array, timeout: number): RedisCommandArguments { const args = pushVerdictArguments(['BZPOPMIN'], key); args.push(timeout.toString()); diff --git a/lib/commands/CLUSTER_INFO.spec.ts b/lib/commands/CLUSTER_INFO.spec.ts index ce41151b67f..a4def45cb79 100644 --- a/lib/commands/CLUSTER_INFO.spec.ts +++ b/lib/commands/CLUSTER_INFO.spec.ts @@ -1,5 +1,4 @@ import { strict as assert } from 'assert'; -import { itWithCluster, TestRedisClusters } from '../test-utils'; import { transformArguments, transformReply } from './CLUSTER_INFO'; describe('CLUSTER INFO', () => { @@ -44,21 +43,4 @@ describe('CLUSTER INFO', () => { } ); }); - - itWithCluster(TestRedisClusters.OPEN, 'cluster.clusterInfo', async cluster => { - const info = await cluster.clusterInfo(); - assert.equal(info.state, 'ok'); - assert.deepEqual(info.slots, { - assigned: 16384, - ok: 16384, - pfail: 0, - fail: 0 - }); - assert.equal(info.knownNodes, 3); - assert.equal(info.size, 3); - assert.equal(typeof info.currentEpoch, 'number'); - assert.equal(typeof info.myEpoch, 'number'); - assert.equal(typeof info.stats.messagesReceived, 'number'); - assert.equal(typeof info.stats.messagesSent, 'number'); - }); }); diff --git a/lib/commands/CLUSTER_NODES.spec.ts b/lib/commands/CLUSTER_NODES.spec.ts index 1f0e9dd425a..2b3881d8cd0 100644 --- a/lib/commands/CLUSTER_NODES.spec.ts +++ b/lib/commands/CLUSTER_NODES.spec.ts @@ -1,5 +1,4 @@ import { strict as assert } from 'assert'; -import { itWithCluster, TestRedisClusters } from '../test-utils'; import { RedisClusterNodeLinkStates, transformArguments, transformReply } from './CLUSTER_NODES'; describe('CLUSTER NODES', () => { @@ -93,24 +92,4 @@ describe('CLUSTER NODES', () => { ); }); }); - - itWithCluster(TestRedisClusters.OPEN, 'cluster.clusterNodes', async cluster => { - for (const node of (await cluster.clusterNodes())) { - assert.equal(typeof node.id, 'string'); - assert.equal(typeof node.url, 'string'); - assert.equal(typeof node.host, 'string'); - assert.equal(typeof node.port, 'number'); - assert.equal(typeof node.cport, 'number'); - assert.ok(Array.isArray(node.flags)); - assert.equal(typeof node.pingSent, 'number'); - assert.equal(typeof node.pongRecv, 'number'); - assert.equal(typeof node.configEpoch, 'number'); - assert.equal(typeof node.linkState, 'string'); - - for (const slot of node.slots) { - assert.equal(typeof slot.from, 'number'); - assert.equal(typeof slot.to, 'number'); - } - } - }); }); diff --git a/lib/commands/CLUSTER_SLOTS.ts b/lib/commands/CLUSTER_SLOTS.ts index b4672e731ac..afe1ebc83dd 100644 --- a/lib/commands/CLUSTER_SLOTS.ts +++ b/lib/commands/CLUSTER_SLOTS.ts @@ -1,6 +1,6 @@ -import { TransformArgumentsReply } from '.'; +import { RedisCommandArguments } from '.'; -export function transformArguments(): TransformArgumentsReply { +export function transformArguments(): RedisCommandArguments { return ['CLUSTER', 'SLOTS']; } diff --git a/lib/commands/COMMAND.ts b/lib/commands/COMMAND.ts index f72cc3f37dd..b6ee50b2f4c 100644 --- a/lib/commands/COMMAND.ts +++ b/lib/commands/COMMAND.ts @@ -1,9 +1,9 @@ -import { TransformArgumentsReply } from '.'; +import { RedisCommandArguments } from '.'; import { CommandRawReply, CommandReply, transformCommandReply } from './generic-transformers'; export const IS_READ_ONLY = true; -export function transformArguments(): TransformArgumentsReply { +export function transformArguments(): RedisCommandArguments { return ['COMMAND']; } diff --git a/lib/commands/COMMAND_COUNT.spec.ts b/lib/commands/COMMAND_COUNT.spec.ts index 61ba1bf2540..23e83c71cec 100644 --- a/lib/commands/COMMAND_COUNT.spec.ts +++ b/lib/commands/COMMAND_COUNT.spec.ts @@ -1,5 +1,5 @@ import { strict as assert } from 'assert'; -import { itWithClient, itWithCluster, TestRedisClusters, TestRedisServers } from '../test-utils'; +import { TestRedisServers, itWithClient } from '../test-utils'; import { transformArguments } from './COMMAND_COUNT'; describe('COMMAND COUNT', () => { @@ -16,11 +16,4 @@ describe('COMMAND COUNT', () => { 'number' ); }); - - itWithCluster(TestRedisClusters.OPEN, 'cluster.commandCount', async cluster => { - assert.equal( - typeof await cluster.commandCount(), - 'number' - ); - }); }); diff --git a/lib/commands/COMMAND_COUNT.ts b/lib/commands/COMMAND_COUNT.ts index 4cdec7bebf1..5b8283bcc66 100644 --- a/lib/commands/COMMAND_COUNT.ts +++ b/lib/commands/COMMAND_COUNT.ts @@ -1,8 +1,8 @@ -import { TransformArgumentsReply } from '.'; +import { RedisCommandArguments } from '.'; export const IS_READ_ONLY = true; -export function transformArguments(): TransformArgumentsReply { +export function transformArguments(): RedisCommandArguments { return ['COMMAND', 'COUNT']; } diff --git a/lib/commands/COMMAND_GETKEYS.spec.ts b/lib/commands/COMMAND_GETKEYS.spec.ts index 37e91781589..f2630db9afa 100644 --- a/lib/commands/COMMAND_GETKEYS.spec.ts +++ b/lib/commands/COMMAND_GETKEYS.spec.ts @@ -1,5 +1,5 @@ import { strict as assert } from 'assert'; -import { itWithClient, itWithCluster, TestRedisClusters, TestRedisServers } from '../test-utils'; +import { TestRedisServers, itWithClient } from '../test-utils'; import { transformArguments } from './COMMAND_GETKEYS'; describe('COMMAND GETKEYS', () => { @@ -16,11 +16,4 @@ describe('COMMAND GETKEYS', () => { ['key'] ); }); - - itWithCluster(TestRedisClusters.OPEN, 'cluster.commandGetKeys', async cluster => { - assert.deepEqual( - await cluster.commandGetKeys(['GET', 'key']), - ['key'] - ); - }); }); diff --git a/lib/commands/COMMAND_GETKEYS.ts b/lib/commands/COMMAND_GETKEYS.ts index 0b8f38e3d08..caf342088fb 100644 --- a/lib/commands/COMMAND_GETKEYS.ts +++ b/lib/commands/COMMAND_GETKEYS.ts @@ -1,8 +1,8 @@ -import { TransformArgumentsReply } from '.'; +import { RedisCommandArguments } from '.'; export const IS_READ_ONLY = true; -export function transformArguments(args: Array): TransformArgumentsReply { +export function transformArguments(args: Array): RedisCommandArguments { return ['COMMAND', 'GETKEYS', ...args]; } diff --git a/lib/commands/COMMAND_INFO.ts b/lib/commands/COMMAND_INFO.ts index 274c57d6aef..6f84d0edaf9 100644 --- a/lib/commands/COMMAND_INFO.ts +++ b/lib/commands/COMMAND_INFO.ts @@ -1,9 +1,9 @@ -import { TransformArgumentsReply } from '.'; +import { RedisCommandArguments } from '.'; import { CommandRawReply, CommandReply, transformCommandReply } from './generic-transformers'; export const IS_READ_ONLY = true; -export function transformArguments(commands: Array): TransformArgumentsReply { +export function transformArguments(commands: Array): RedisCommandArguments { return ['COMMAND', 'INFO', ...commands]; } diff --git a/lib/commands/DBSIZE.spec.ts b/lib/commands/DBSIZE.spec.ts index 87e3c154534..36f591dbd29 100644 --- a/lib/commands/DBSIZE.spec.ts +++ b/lib/commands/DBSIZE.spec.ts @@ -1,5 +1,5 @@ import { strict as assert } from 'assert'; -import { TestRedisServers, itWithClient, TestRedisClusters, itWithCluster } from '../test-utils'; +import { TestRedisServers, itWithClient } from '../test-utils'; import { transformArguments } from './DBSIZE'; describe('DBSIZE', () => { @@ -16,11 +16,4 @@ describe('DBSIZE', () => { 0 ); }); - - itWithCluster(TestRedisClusters.OPEN, 'cluster.dbSize', async cluster => { - assert.equal( - await cluster.dbSize(), - 0 - ); - }); }); diff --git a/lib/commands/DEL.ts b/lib/commands/DEL.ts index b815258df1b..02ef553f647 100644 --- a/lib/commands/DEL.ts +++ b/lib/commands/DEL.ts @@ -1,7 +1,7 @@ -import { TransformArgumentsReply } from '.'; +import { RedisCommandArguments } from '.'; import { pushVerdictArguments } from './generic-transformers'; -export function transformArguments(keys: string | Array): TransformArgumentsReply { +export function transformArguments(keys: string | Array): RedisCommandArguments { return pushVerdictArguments(['DEL'], keys); } diff --git a/lib/commands/ECHO.spec.ts b/lib/commands/ECHO.spec.ts index 4a1bf8fe378..d91b7373950 100644 --- a/lib/commands/ECHO.spec.ts +++ b/lib/commands/ECHO.spec.ts @@ -1,5 +1,5 @@ import { strict as assert } from 'assert'; -import { TestRedisServers, itWithClient, TestRedisClusters, itWithCluster } from '../test-utils'; +import { TestRedisServers, itWithClient } from '../test-utils'; import { transformArguments } from './ECHO'; describe('ECHO', () => { @@ -16,11 +16,4 @@ describe('ECHO', () => { 'message' ); }); - - itWithCluster(TestRedisClusters.OPEN, 'cluster.echo', async cluster => { - assert.equal( - await cluster.echo('message'), - 'message' - ); - }); }); diff --git a/lib/commands/EXISTS.ts b/lib/commands/EXISTS.ts index 00d10b9eebc..aac164fc953 100644 --- a/lib/commands/EXISTS.ts +++ b/lib/commands/EXISTS.ts @@ -1,11 +1,11 @@ -import { TransformArgumentsReply } from '.'; +import { RedisCommandArguments } from '.'; import { pushVerdictArguments, transformReplyBoolean } from './generic-transformers'; export const FIRST_KEY_INDEX = 1; export const IS_READ_ONLY = true; -export function transformArguments(keys: string | Array): TransformArgumentsReply { +export function transformArguments(keys: string | Array): RedisCommandArguments { return pushVerdictArguments(['EXISTS'], keys); } diff --git a/lib/commands/GEOHASH.ts b/lib/commands/GEOHASH.ts index 2ee2c6a6689..8613f37fa43 100644 --- a/lib/commands/GEOHASH.ts +++ b/lib/commands/GEOHASH.ts @@ -1,11 +1,11 @@ -import { TransformArgumentsReply } from '.'; +import { RedisCommandArguments } from '.'; import { pushVerdictArguments } from './generic-transformers'; export const FIRST_KEY_INDEX = 1; export const IS_READ_ONLY = true; -export function transformArguments(key: string, member: string | Array): TransformArgumentsReply { +export function transformArguments(key: string, member: string | Array): RedisCommandArguments { return pushVerdictArguments(['GEOHASH', key], member); } diff --git a/lib/commands/GEOPOS.ts b/lib/commands/GEOPOS.ts index 893048cf6da..95f33d9e3a8 100644 --- a/lib/commands/GEOPOS.ts +++ b/lib/commands/GEOPOS.ts @@ -1,11 +1,11 @@ -import { TransformArgumentsReply } from '.'; +import { RedisCommandArguments } from '.'; import { pushVerdictArguments } from './generic-transformers'; export const FIRST_KEY_INDEX = 1; export const IS_READ_ONLY = true; -export function transformArguments(key: string, member: string | Array): TransformArgumentsReply { +export function transformArguments(key: string, member: string | Array): RedisCommandArguments { return pushVerdictArguments(['GEOPOS', key], member); } diff --git a/lib/commands/GEOSEARCH_WITH.spec.ts b/lib/commands/GEOSEARCH_WITH.spec.ts index a400fb965c6..922c00d7194 100644 --- a/lib/commands/GEOSEARCH_WITH.spec.ts +++ b/lib/commands/GEOSEARCH_WITH.spec.ts @@ -1,5 +1,5 @@ import { strict as assert } from 'assert'; -import { TransformArgumentsReply } from '.'; +import { RedisCommandArguments } from '.'; import { TestRedisServers, itWithClient, TestRedisClusters, itWithCluster, describeHandleMinimumRedisVersion } from '../test-utils'; import { GeoReplyWith } from './generic-transformers'; import { transformArguments } from './GEOSEARCH_WITH'; @@ -8,7 +8,7 @@ describe('GEOSEARCH WITH', () => { describeHandleMinimumRedisVersion([6, 2]); it('transformArguments', () => { - const expectedReply: TransformArgumentsReply = ['GEOSEARCH', 'key', 'FROMMEMBER', 'member', 'BYRADIUS', '1', 'm', 'WITHDIST'] + const expectedReply: RedisCommandArguments = ['GEOSEARCH', 'key', 'FROMMEMBER', 'member', 'BYRADIUS', '1', 'm', 'WITHDIST'] expectedReply.preserve = ['WITHDIST']; assert.deepEqual( diff --git a/lib/commands/GEOSEARCH_WITH.ts b/lib/commands/GEOSEARCH_WITH.ts index ef19ca5dfc9..cd461c9677f 100644 --- a/lib/commands/GEOSEARCH_WITH.ts +++ b/lib/commands/GEOSEARCH_WITH.ts @@ -1,4 +1,4 @@ -import { TransformArgumentsReply } from '.'; +import { RedisCommandArguments } from '.'; import { GeoSearchFrom, GeoSearchBy, GeoReplyWith, GeoSearchOptions, transformGeoMembersWithReply } from './generic-transformers'; import { transformArguments as geoSearchTransformArguments } from './GEOSEARCH'; @@ -10,8 +10,8 @@ export function transformArguments( by: GeoSearchBy, replyWith: Array, options?: GeoSearchOptions -): TransformArgumentsReply { - const args: TransformArgumentsReply = geoSearchTransformArguments(key, from, by, options); +): RedisCommandArguments { + const args: RedisCommandArguments = geoSearchTransformArguments(key, from, by, options); args.push(...replyWith); diff --git a/lib/commands/GET.ts b/lib/commands/GET.ts index dbd303d1c65..0e89a4e6a74 100644 --- a/lib/commands/GET.ts +++ b/lib/commands/GET.ts @@ -1,10 +1,10 @@ -import { TransformArgumentsReply } from '.'; +import { RedisCommandArguments } from '.'; export const FIRST_KEY_INDEX = 1; export const IS_READ_ONLY = true; -export function transformArguments(key: string | Buffer): TransformArgumentsReply { +export function transformArguments(key: string | Buffer): RedisCommandArguments { return ['GET', key]; } diff --git a/lib/commands/GETEX.ts b/lib/commands/GETEX.ts index 2c6a4f243f6..cd4f283eee3 100644 --- a/lib/commands/GETEX.ts +++ b/lib/commands/GETEX.ts @@ -1,4 +1,4 @@ -import { TransformArgumentsReply } from '.'; +import { RedisCommandArguments } from '.'; import { transformEXAT, transformPXAT } from './generic-transformers'; export const FIRST_KEY_INDEX = 1; @@ -15,7 +15,7 @@ type GetExModes = { PERSIST: true; }; -export function transformArguments(key: string, mode: GetExModes): TransformArgumentsReply { +export function transformArguments(key: string, mode: GetExModes): RedisCommandArguments { const args = ['GETEX', key]; if ('EX' in mode) { diff --git a/lib/commands/HDEL.ts b/lib/commands/HDEL.ts index 75130c87239..58d057ebf10 100644 --- a/lib/commands/HDEL.ts +++ b/lib/commands/HDEL.ts @@ -1,9 +1,9 @@ -import { TransformArgumentsReply } from '.'; +import { RedisCommandArguments } from '.'; import { pushVerdictArguments } from './generic-transformers'; export const FIRST_KEY_INDEX = 1; -export function transformArguments(key: string, field: string | Array): TransformArgumentsReply { +export function transformArguments(key: string, field: string | Array): RedisCommandArguments { return pushVerdictArguments(['HDEL', key], field); } diff --git a/lib/commands/HMGET.ts b/lib/commands/HMGET.ts index 420102d2b27..7ca3a55b69c 100644 --- a/lib/commands/HMGET.ts +++ b/lib/commands/HMGET.ts @@ -1,11 +1,11 @@ -import { TransformArgumentsReply } from '.'; +import { RedisCommandArguments } from '.'; import { pushVerdictArguments } from './generic-transformers'; export const FIRST_KEY_INDEX = 1; export const IS_READ_ONLY = true; -export function transformArguments(key: string, fields: string | Array): TransformArgumentsReply { +export function transformArguments(key: string, fields: string | Array): RedisCommandArguments { return pushVerdictArguments(['HMGET', key], fields); } diff --git a/lib/commands/HSET.ts b/lib/commands/HSET.ts index 1aecd50c0d0..1d4acd6c018 100644 --- a/lib/commands/HSET.ts +++ b/lib/commands/HSET.ts @@ -1,4 +1,4 @@ -import { TransformArgumentsReply } from '.'; +import { RedisCommandArguments } from '.'; type HSETObject = Record; @@ -14,7 +14,7 @@ type SingleFieldArguments = [...generic: GenericArguments, field: string, value: type MultipleFieldsArguments = [...generic: GenericArguments, value: HSETObject | HSETMap | HSETTuples]; -export function transformArguments(...[ key, value, fieldValue ]: SingleFieldArguments | MultipleFieldsArguments): TransformArgumentsReply { +export function transformArguments(...[ key, value, fieldValue ]: SingleFieldArguments | MultipleFieldsArguments): RedisCommandArguments { const args = ['HSET', key]; if (typeof value === 'string') { diff --git a/lib/commands/LASTSAVE.spec.ts b/lib/commands/LASTSAVE.spec.ts index 1b13bed5d20..b8d801f70b5 100644 --- a/lib/commands/LASTSAVE.spec.ts +++ b/lib/commands/LASTSAVE.spec.ts @@ -1,5 +1,5 @@ import { strict as assert } from 'assert'; -import { TestRedisServers, itWithClient, itWithCluster, TestRedisClusters } from '../test-utils'; +import { TestRedisServers, itWithClient } from '../test-utils'; import { transformArguments } from './LASTSAVE'; describe('LASTSAVE', () => { @@ -13,8 +13,4 @@ describe('LASTSAVE', () => { itWithClient(TestRedisServers.OPEN, 'client.lastSave', async client => { assert.ok((await client.lastSave()) instanceof Date); }); - - itWithCluster(TestRedisClusters.OPEN, 'cluster.lastSave', async cluster => { - assert.ok((await cluster.lastSave()) instanceof Date); - }); }); diff --git a/lib/commands/LOLWUT.spec.ts b/lib/commands/LOLWUT.spec.ts index 8e77b85b599..8f4478aecc7 100644 --- a/lib/commands/LOLWUT.spec.ts +++ b/lib/commands/LOLWUT.spec.ts @@ -1,5 +1,5 @@ import { strict as assert } from 'assert'; -import { TestRedisServers, itWithClient, itWithCluster, TestRedisClusters } from '../test-utils'; +import { TestRedisServers, itWithClient } from '../test-utils'; import { transformArguments } from './LOLWUT'; describe('LOLWUT', () => { @@ -25,7 +25,7 @@ describe('LOLWUT', () => { ); }); }); - + itWithClient(TestRedisServers.OPEN, 'client.LOLWUT', async client => { assert.equal( @@ -33,11 +33,4 @@ describe('LOLWUT', () => { 'string' ); }); - - itWithCluster(TestRedisClusters.OPEN, 'cluster.LOLWUT', async cluster => { - assert.equal( - typeof (await cluster.LOLWUT()), - 'string' - ); - }); }); diff --git a/lib/commands/LPUSH.ts b/lib/commands/LPUSH.ts index 349affb5e09..b9644344772 100644 --- a/lib/commands/LPUSH.ts +++ b/lib/commands/LPUSH.ts @@ -1,9 +1,9 @@ -import { TransformArgumentsReply } from '.'; +import { RedisCommandArguments } from '.'; import { pushVerdictArguments } from './generic-transformers'; export const FIRST_KEY_INDEX = 1; -export function transformArguments(key: string, elements: string | Array): TransformArgumentsReply { +export function transformArguments(key: string, elements: string | Array): RedisCommandArguments { return pushVerdictArguments(['LPUSH', key], elements);} export declare function transformReply(): number; diff --git a/lib/commands/LPUSHX.ts b/lib/commands/LPUSHX.ts index 23b8bd9b91b..5f92d84d3b0 100644 --- a/lib/commands/LPUSHX.ts +++ b/lib/commands/LPUSHX.ts @@ -1,9 +1,9 @@ -import { TransformArgumentsReply } from '.'; +import { RedisCommandArguments } from '.'; import { pushVerdictArguments } from './generic-transformers'; export const FIRST_KEY_INDEX = 1; -export function transformArguments(key: string, element: string | Array): TransformArgumentsReply { +export function transformArguments(key: string, element: string | Array): RedisCommandArguments { return pushVerdictArguments(['LPUSHX', key], element); } diff --git a/lib/commands/MEMORY_DOCTOR.spec.ts b/lib/commands/MEMORY_DOCTOR.spec.ts index da883deeb77..1b4d16fa0db 100644 --- a/lib/commands/MEMORY_DOCTOR.spec.ts +++ b/lib/commands/MEMORY_DOCTOR.spec.ts @@ -1,5 +1,5 @@ import { strict as assert } from 'assert'; -import { TestRedisServers, itWithClient, TestRedisClusters, itWithCluster } from '../test-utils'; +import { TestRedisServers, itWithClient } from '../test-utils'; import { transformArguments } from './MEMORY_DOCTOR'; describe('MEMORY DOCTOR', () => { @@ -16,11 +16,4 @@ describe('MEMORY DOCTOR', () => { 'string' ); }); - - itWithCluster(TestRedisClusters.OPEN, 'cluster.memoryDoctor', async cluster => { - assert.equal( - typeof (await cluster.memoryDoctor()), - 'string' - ); - }); }); diff --git a/lib/commands/MEMORY_MALLOC-STATS.spec.ts b/lib/commands/MEMORY_MALLOC-STATS.spec.ts index 2750ebdf7a0..034b94f7c66 100644 --- a/lib/commands/MEMORY_MALLOC-STATS.spec.ts +++ b/lib/commands/MEMORY_MALLOC-STATS.spec.ts @@ -1,5 +1,5 @@ import { strict as assert } from 'assert'; -import { TestRedisServers, itWithClient, TestRedisClusters, itWithCluster } from '../test-utils'; +import { TestRedisServers, itWithClient } from '../test-utils'; import { transformArguments } from './MEMORY_MALLOC-STATS'; describe('MEMORY MALLOC-STATS', () => { @@ -16,11 +16,4 @@ describe('MEMORY MALLOC-STATS', () => { 'string' ); }); - - itWithCluster(TestRedisClusters.OPEN, 'cluster.memoryDoctor', async cluster => { - assert.equal( - typeof (await cluster.memoryDoctor()), - 'string' - ); - }); }); diff --git a/lib/commands/MEMORY_PURGE.spec.ts b/lib/commands/MEMORY_PURGE.spec.ts index ac9198ccfc8..97ca6feebf6 100644 --- a/lib/commands/MEMORY_PURGE.spec.ts +++ b/lib/commands/MEMORY_PURGE.spec.ts @@ -1,5 +1,5 @@ import { strict as assert } from 'assert'; -import { TestRedisServers, itWithClient, TestRedisClusters, itWithCluster } from '../test-utils'; +import { TestRedisServers, itWithClient } from '../test-utils'; import { transformArguments } from './MEMORY_PURGE'; describe('MEMORY PURGE', () => { @@ -16,11 +16,4 @@ describe('MEMORY PURGE', () => { 'OK' ); }); - - itWithCluster(TestRedisClusters.OPEN, 'cluster.memoryPurge', async cluster => { - assert.equal( - await cluster.memoryPurge(), - 'OK' - ); - }); }); diff --git a/lib/commands/MEMORY_USAGE.spec.ts b/lib/commands/MEMORY_USAGE.spec.ts index 7487e7e4ffc..90dff62c674 100644 --- a/lib/commands/MEMORY_USAGE.spec.ts +++ b/lib/commands/MEMORY_USAGE.spec.ts @@ -1,5 +1,5 @@ import { strict as assert } from 'assert'; -import { TestRedisServers, itWithClient, TestRedisClusters, itWithCluster } from '../test-utils'; +import { TestRedisServers, itWithClient } from '../test-utils'; import { transformArguments } from './MEMORY_USAGE'; describe('MEMORY USAGE', () => { @@ -27,11 +27,4 @@ describe('MEMORY USAGE', () => { null ); }); - - itWithCluster(TestRedisClusters.OPEN, 'cluster.memoryUsage', async cluster => { - assert.equal( - await cluster.memoryUsage('key'), - null - ); - }); }); diff --git a/lib/commands/PFADD.ts b/lib/commands/PFADD.ts index cc99bed7f65..4328a18dfe5 100644 --- a/lib/commands/PFADD.ts +++ b/lib/commands/PFADD.ts @@ -1,9 +1,9 @@ -import { TransformArgumentsReply } from '.'; +import { RedisCommandArguments } from '.'; import { pushVerdictArguments, transformReplyBoolean } from './generic-transformers'; export const FIRST_KEY_INDEX = 1; -export function transformArguments(key: string, element: string | Array): TransformArgumentsReply { +export function transformArguments(key: string, element: string | Array): RedisCommandArguments { return pushVerdictArguments(['PFADD', key], element); } diff --git a/lib/commands/PFCOUNT.ts b/lib/commands/PFCOUNT.ts index 1fe3b1ee5d2..ec6c0906041 100644 --- a/lib/commands/PFCOUNT.ts +++ b/lib/commands/PFCOUNT.ts @@ -1,9 +1,9 @@ -import { TransformArgumentsReply } from '.'; +import { RedisCommandArguments } from '.'; import { pushVerdictArguments } from './generic-transformers'; export const FIRST_KEY_INDEX = 1; -export function transformArguments(key: string | Array): TransformArgumentsReply { +export function transformArguments(key: string | Array): RedisCommandArguments { return pushVerdictArguments(['PFCOUNT'], key); } diff --git a/lib/commands/PFMERGE.ts b/lib/commands/PFMERGE.ts index 86bef6c4d7f..e934062b3fe 100644 --- a/lib/commands/PFMERGE.ts +++ b/lib/commands/PFMERGE.ts @@ -1,9 +1,9 @@ -import { TransformArgumentsReply } from '.'; +import { RedisCommandArguments } from '.'; import { pushVerdictArguments } from './generic-transformers'; export const FIRST_KEY_INDEX = 1; -export function transformArguments(destination: string, source: string | Array): TransformArgumentsReply { +export function transformArguments(destination: string, source: string | Array): RedisCommandArguments { return pushVerdictArguments(['PFMERGE', destination], source); } diff --git a/lib/commands/PING.spec.ts b/lib/commands/PING.spec.ts index 87d9359a36d..43b683f192d 100644 --- a/lib/commands/PING.spec.ts +++ b/lib/commands/PING.spec.ts @@ -1,5 +1,5 @@ import { strict as assert } from 'assert'; -import { TestRedisServers, itWithClient, itWithCluster, TestRedisClusters } from '../test-utils'; +import { TestRedisServers, itWithClient } from '../test-utils'; describe('PING', () => { itWithClient(TestRedisServers.OPEN, 'client.ping', async client => { @@ -8,11 +8,4 @@ describe('PING', () => { 'PONG' ); }); - - itWithCluster(TestRedisClusters.OPEN, 'cluster.ping', async cluster => { - assert.equal( - await cluster.ping(), - 'PONG' - ); - }); }); diff --git a/lib/commands/PUBSUB_CHANNELS.spec.ts b/lib/commands/PUBSUB_CHANNELS.spec.ts index 5ff9db60df8..9e148bc7fda 100644 --- a/lib/commands/PUBSUB_CHANNELS.spec.ts +++ b/lib/commands/PUBSUB_CHANNELS.spec.ts @@ -1,5 +1,5 @@ import { strict as assert } from 'assert'; -import { TestRedisServers, itWithClient, TestRedisClusters, itWithCluster } from '../test-utils'; +import { TestRedisServers, itWithClient } from '../test-utils'; import { transformArguments } from './PUBSUB_CHANNELS'; describe('PUBSUB CHANNELS', () => { @@ -25,11 +25,4 @@ describe('PUBSUB CHANNELS', () => { [] ); }); - - itWithCluster(TestRedisClusters.OPEN, 'cluster.pubSubChannels', async cluster => { - assert.deepEqual( - await cluster.pubSubChannels(), - [] - ); - }); }); diff --git a/lib/commands/PUBSUB_NUMPAT.spec.ts b/lib/commands/PUBSUB_NUMPAT.spec.ts index 49a39eedae0..55eef6a97de 100644 --- a/lib/commands/PUBSUB_NUMPAT.spec.ts +++ b/lib/commands/PUBSUB_NUMPAT.spec.ts @@ -1,5 +1,5 @@ import { strict as assert } from 'assert'; -import { TestRedisServers, itWithClient, TestRedisClusters, itWithCluster } from '../test-utils'; +import { TestRedisServers, itWithClient } from '../test-utils'; import { transformArguments } from './PUBSUB_NUMPAT'; describe('PUBSUB NUMPAT', () => { @@ -16,11 +16,4 @@ describe('PUBSUB NUMPAT', () => { 0 ); }); - - itWithCluster(TestRedisClusters.OPEN, 'cluster.pubSubNumPat', async cluster => { - assert.equal( - await cluster.pubSubNumPat(), - 0 - ); - }); }); diff --git a/lib/commands/PUBSUB_NUMSUB.spec.ts b/lib/commands/PUBSUB_NUMSUB.spec.ts index 403732f8f9d..ef44faf2c0b 100644 --- a/lib/commands/PUBSUB_NUMSUB.spec.ts +++ b/lib/commands/PUBSUB_NUMSUB.spec.ts @@ -1,5 +1,5 @@ import { strict as assert } from 'assert'; -import { TestRedisServers, itWithClient, TestRedisClusters, itWithCluster } from '../test-utils'; +import { TestRedisServers, itWithClient } from '../test-utils'; import { transformArguments } from './PUBSUB_NUMSUB'; describe('PUBSUB NUMSUB', () => { @@ -32,11 +32,4 @@ describe('PUBSUB NUMSUB', () => { Object.create(null) ); }); - - itWithCluster(TestRedisClusters.OPEN, 'cluster.pubSubNumSub', async cluster => { - assert.deepEqual( - await cluster.pubSubNumSub(), - Object.create(null) - ); - }); }); diff --git a/lib/commands/RPUSH.ts b/lib/commands/RPUSH.ts index 92df52f03ff..575177755cc 100644 --- a/lib/commands/RPUSH.ts +++ b/lib/commands/RPUSH.ts @@ -1,9 +1,9 @@ -import { TransformArgumentsReply } from '.'; +import { RedisCommandArguments } from '.'; import { pushVerdictArguments } from './generic-transformers'; export const FIRST_KEY_INDEX = 1; -export function transformArguments(key: string, element: string | Array): TransformArgumentsReply { +export function transformArguments(key: string, element: string | Array): RedisCommandArguments { return pushVerdictArguments(['RPUSH', key], element); } diff --git a/lib/commands/RPUSHX.ts b/lib/commands/RPUSHX.ts index 14ad9dc9d50..bacc60d404b 100644 --- a/lib/commands/RPUSHX.ts +++ b/lib/commands/RPUSHX.ts @@ -1,9 +1,9 @@ -import { TransformArgumentsReply } from '.'; +import { RedisCommandArguments } from '.'; import { pushVerdictArguments } from './generic-transformers'; export const FIRST_KEY_INDEX = 1; -export function transformArguments(key: string, element: string | Array): TransformArgumentsReply { +export function transformArguments(key: string, element: string | Array): RedisCommandArguments { return pushVerdictArguments(['RPUSHX', key], element); } diff --git a/lib/commands/SADD.ts b/lib/commands/SADD.ts index 31623c435ce..05e5a6858f7 100644 --- a/lib/commands/SADD.ts +++ b/lib/commands/SADD.ts @@ -1,9 +1,9 @@ -import { TransformArgumentsReply } from '.'; +import { RedisCommandArguments } from '.'; import { pushVerdictArguments } from './generic-transformers'; export const FIRST_KEY_INDEX = 1; -export function transformArguments(key: string, members: string | Array): TransformArgumentsReply { +export function transformArguments(key: string, members: string | Array): RedisCommandArguments { return pushVerdictArguments(['SADD', key], members); } diff --git a/lib/commands/SCRIPT_DEBUG.spec.ts b/lib/commands/SCRIPT_DEBUG.spec.ts index 9096605143a..9d2ad1af266 100644 --- a/lib/commands/SCRIPT_DEBUG.spec.ts +++ b/lib/commands/SCRIPT_DEBUG.spec.ts @@ -1,5 +1,5 @@ import { strict as assert } from 'assert'; -import { TestRedisServers, itWithClient, TestRedisClusters, itWithCluster } from '../test-utils'; +import { TestRedisServers, itWithClient } from '../test-utils'; import { transformArguments } from './SCRIPT_DEBUG'; describe('SCRIPT DEBUG', () => { @@ -16,11 +16,4 @@ describe('SCRIPT DEBUG', () => { 'OK' ); }); - - itWithCluster(TestRedisClusters.OPEN, 'cluster.scriptDebug', async cluster => { - assert.equal( - await cluster.scriptDebug('NO'), - 'OK' - ); - }); }); diff --git a/lib/commands/SCRIPT_EXISTS.spec.ts b/lib/commands/SCRIPT_EXISTS.spec.ts index d03521a5c60..b23380c7579 100644 --- a/lib/commands/SCRIPT_EXISTS.spec.ts +++ b/lib/commands/SCRIPT_EXISTS.spec.ts @@ -1,5 +1,5 @@ import { strict as assert } from 'assert'; -import { TestRedisServers, itWithClient, TestRedisClusters, itWithCluster } from '../test-utils'; +import { TestRedisServers, itWithClient } from '../test-utils'; import { transformArguments } from './SCRIPT_EXISTS'; describe('SCRIPT EXISTS', () => { @@ -25,11 +25,4 @@ describe('SCRIPT EXISTS', () => { [false] ); }); - - itWithCluster(TestRedisClusters.OPEN, 'cluster.scriptExists', async cluster => { - assert.deepEqual( - await cluster.scriptExists('sha1'), - [false] - ); - }); }); diff --git a/lib/commands/SCRIPT_EXISTS.ts b/lib/commands/SCRIPT_EXISTS.ts index 47a7f456e9b..ee89f955e50 100644 --- a/lib/commands/SCRIPT_EXISTS.ts +++ b/lib/commands/SCRIPT_EXISTS.ts @@ -1,7 +1,7 @@ -import { TransformArgumentsReply } from '.'; +import { RedisCommandArguments } from '.'; import { pushVerdictArguments, transformReplyBooleanArray } from './generic-transformers'; -export function transformArguments(sha1: string | Array): TransformArgumentsReply { +export function transformArguments(sha1: string | Array): RedisCommandArguments { return pushVerdictArguments(['SCRIPT', 'EXISTS'], sha1); } diff --git a/lib/commands/SCRIPT_FLUSH.spec.ts b/lib/commands/SCRIPT_FLUSH.spec.ts index c1321676ebe..c77accb50a9 100644 --- a/lib/commands/SCRIPT_FLUSH.spec.ts +++ b/lib/commands/SCRIPT_FLUSH.spec.ts @@ -1,5 +1,5 @@ import { strict as assert } from 'assert'; -import { TestRedisServers, itWithClient, TestRedisClusters, itWithCluster } from '../test-utils'; +import { TestRedisServers, itWithClient } from '../test-utils'; import { transformArguments } from './SCRIPT_FLUSH'; describe('SCRIPT FLUSH', () => { @@ -25,11 +25,4 @@ describe('SCRIPT FLUSH', () => { 'OK' ); }); - - itWithCluster(TestRedisClusters.OPEN, 'cluster.scriptFlush', async cluster => { - assert.equal( - await cluster.scriptFlush(), - 'OK' - ); - }); }); diff --git a/lib/commands/SCRIPT_LOAD.spec.ts b/lib/commands/SCRIPT_LOAD.spec.ts index 46490f35c8a..1d7da3e9c2d 100644 --- a/lib/commands/SCRIPT_LOAD.spec.ts +++ b/lib/commands/SCRIPT_LOAD.spec.ts @@ -1,6 +1,6 @@ import { strict as assert } from 'assert'; import { scriptSha1 } from '../lua-script'; -import { TestRedisServers, itWithClient, TestRedisClusters, itWithCluster } from '../test-utils'; +import { TestRedisServers, itWithClient } from '../test-utils'; import { transformArguments } from './SCRIPT_LOAD'; describe('SCRIPT LOAD', () => { @@ -20,11 +20,4 @@ describe('SCRIPT LOAD', () => { SCRIPT_SHA1 ); }); - - itWithCluster(TestRedisClusters.OPEN, 'cluster.scriptLoad', async cluster => { - assert.equal( - await cluster.scriptLoad(SCRIPT), - SCRIPT_SHA1 - ); - }); }); diff --git a/lib/commands/SDIFF.ts b/lib/commands/SDIFF.ts index 134bd66ef85..7c1e4fc34a9 100644 --- a/lib/commands/SDIFF.ts +++ b/lib/commands/SDIFF.ts @@ -1,9 +1,9 @@ -import { TransformArgumentsReply } from '.'; +import { RedisCommandArguments } from '.'; import { pushVerdictArguments } from './generic-transformers'; export const FIRST_KEY_INDEX = 1; -export function transformArguments(keys: string | Array): TransformArgumentsReply { +export function transformArguments(keys: string | Array): RedisCommandArguments { return pushVerdictArguments(['SDIFF'], keys); } diff --git a/lib/commands/SDIFFSTORE.ts b/lib/commands/SDIFFSTORE.ts index 1c437087c5e..9cca24beb61 100644 --- a/lib/commands/SDIFFSTORE.ts +++ b/lib/commands/SDIFFSTORE.ts @@ -1,9 +1,9 @@ -import { TransformArgumentsReply } from '.'; +import { RedisCommandArguments } from '.'; import { pushVerdictArguments } from './generic-transformers'; export const FIRST_KEY_INDEX = 1; -export function transformArguments(destination: string, keys: string | Array): TransformArgumentsReply { +export function transformArguments(destination: string, keys: string | Array): RedisCommandArguments { return pushVerdictArguments(['SDIFFSTORE', destination], keys); } diff --git a/lib/commands/SET.ts b/lib/commands/SET.ts index 03853b3f7d6..b19a1b2c5c2 100644 --- a/lib/commands/SET.ts +++ b/lib/commands/SET.ts @@ -1,4 +1,4 @@ -import { TransformArgumentsReply } from '.'; +import { RedisCommandArguments } from '.'; export const FIRST_KEY_INDEX = 1; @@ -40,7 +40,7 @@ interface SetCommonOptions { type SetOptions = SetTTL & SetGuards & (SetCommonOptions | {}); -export function transformArguments(key: string | Buffer, value: string | Buffer, options?: SetOptions): TransformArgumentsReply { +export function transformArguments(key: string | Buffer, value: string | Buffer, options?: SetOptions): RedisCommandArguments { const args = ['SET', key, value]; if (!options) { diff --git a/lib/commands/SETBIT.ts b/lib/commands/SETBIT.ts index 7b96907dfeb..7b0812a55ba 100644 --- a/lib/commands/SETBIT.ts +++ b/lib/commands/SETBIT.ts @@ -1,9 +1,9 @@ -import { TransformArgumentsReply } from '.'; +import { RedisCommandArguments } from '.'; import { BitValue } from './generic-transformers'; export const FIRST_KEY_INDEX = 1; -export function transformArguments(key: string, offset: number, value: BitValue): TransformArgumentsReply { +export function transformArguments(key: string, offset: number, value: BitValue): RedisCommandArguments { return ['SETBIT', key, offset.toString(), value.toString()]; } diff --git a/lib/commands/SETEX.ts b/lib/commands/SETEX.ts index 0d21479bdc4..89c6e06a3f2 100644 --- a/lib/commands/SETEX.ts +++ b/lib/commands/SETEX.ts @@ -1,8 +1,8 @@ -import { TransformArgumentsReply } from '.'; +import { RedisCommandArguments } from '.'; export const FIRST_KEY_INDEX = 1; -export function transformArguments(key: string | Buffer, seconds: number, value: string): TransformArgumentsReply { +export function transformArguments(key: string | Buffer, seconds: number, value: string): RedisCommandArguments { return [ 'SETEX', key, diff --git a/lib/commands/SINTER.ts b/lib/commands/SINTER.ts index 6348bbc7ab9..5d74e761f0e 100644 --- a/lib/commands/SINTER.ts +++ b/lib/commands/SINTER.ts @@ -1,9 +1,9 @@ -import { TransformArgumentsReply } from '.'; +import { RedisCommandArguments } from '.'; import { pushVerdictArguments } from './generic-transformers'; export const FIRST_KEY_INDEX = 1; -export function transformArguments(keys: string | Array): TransformArgumentsReply { +export function transformArguments(keys: string | Array): RedisCommandArguments { return pushVerdictArguments(['SINTER'], keys); } diff --git a/lib/commands/SINTERSTORE.ts b/lib/commands/SINTERSTORE.ts index 0d31c409da8..40f31a8b7a3 100644 --- a/lib/commands/SINTERSTORE.ts +++ b/lib/commands/SINTERSTORE.ts @@ -1,9 +1,9 @@ -import { TransformArgumentsReply } from '.'; +import { RedisCommandArguments } from '.'; import { pushVerdictArguments } from './generic-transformers'; export const FIRST_KEY_INDEX = 1; -export function transformArguments(destination: string, keys: string | Array): TransformArgumentsReply { +export function transformArguments(destination: string, keys: string | Array): RedisCommandArguments { return pushVerdictArguments(['SINTERSTORE', destination], keys); } diff --git a/lib/commands/SREM.ts b/lib/commands/SREM.ts index 534de170540..9a37ac9bf99 100644 --- a/lib/commands/SREM.ts +++ b/lib/commands/SREM.ts @@ -1,9 +1,9 @@ -import { TransformArgumentsReply } from '.'; +import { RedisCommandArguments } from '.'; import { pushVerdictArguments } from './generic-transformers'; export const FIRST_KEY_INDEX = 1; -export function transformArguments(key: string, members: string | Array): TransformArgumentsReply { +export function transformArguments(key: string, members: string | Array): RedisCommandArguments { return pushVerdictArguments(['SREM', key], members); } diff --git a/lib/commands/SUNION.ts b/lib/commands/SUNION.ts index 07a46e38efa..ae8b02b481d 100644 --- a/lib/commands/SUNION.ts +++ b/lib/commands/SUNION.ts @@ -1,11 +1,11 @@ -import { TransformArgumentsReply } from '.'; +import { RedisCommandArguments } from '.'; import { pushVerdictArguments } from './generic-transformers'; export const FIRST_KEY_INDEX = 1; export const IS_READ_ONLY = true; -export function transformArguments(keys: string | Array): TransformArgumentsReply { +export function transformArguments(keys: string | Array): RedisCommandArguments { return pushVerdictArguments(['SUNION'], keys); } diff --git a/lib/commands/SUNIONSTORE.ts b/lib/commands/SUNIONSTORE.ts index 8745c516433..f259769f49a 100644 --- a/lib/commands/SUNIONSTORE.ts +++ b/lib/commands/SUNIONSTORE.ts @@ -1,9 +1,9 @@ -import { TransformArgumentsReply } from '.'; +import { RedisCommandArguments } from '.'; import { pushVerdictArguments } from './generic-transformers'; export const FIRST_KEY_INDEX = 1; -export function transformArguments(destination: string, keys: string | Array): TransformArgumentsReply { +export function transformArguments(destination: string, keys: string | Array): RedisCommandArguments { return pushVerdictArguments(['SUNIONSTORE', destination], keys); } diff --git a/lib/commands/TOUCH.ts b/lib/commands/TOUCH.ts index 8632d848dbf..a3dc31e8568 100644 --- a/lib/commands/TOUCH.ts +++ b/lib/commands/TOUCH.ts @@ -1,9 +1,9 @@ -import { TransformArgumentsReply } from '.'; +import { RedisCommandArguments } from '.'; import { pushVerdictArguments } from './generic-transformers'; export const FIRST_KEY_INDEX = 1; -export function transformArguments(key: string | Array): TransformArgumentsReply { +export function transformArguments(key: string | Array): RedisCommandArguments { return pushVerdictArguments(['TOUCH'], key); } diff --git a/lib/commands/UNLINK.ts b/lib/commands/UNLINK.ts index 2ff87974cfb..467b4172e0f 100644 --- a/lib/commands/UNLINK.ts +++ b/lib/commands/UNLINK.ts @@ -1,9 +1,9 @@ -import { TransformArgumentsReply } from '.'; +import { RedisCommandArguments } from '.'; import { pushVerdictArguments } from './generic-transformers'; export const FIRST_KEY_INDEX = 1; -export function transformArguments(key: string | Array): TransformArgumentsReply { +export function transformArguments(key: string | Array): RedisCommandArguments { return pushVerdictArguments(['UNLINK'], key); } diff --git a/lib/commands/UNWATCH.spec.ts b/lib/commands/UNWATCH.spec.ts index 238ffdc59b3..07059310cbc 100644 --- a/lib/commands/UNWATCH.spec.ts +++ b/lib/commands/UNWATCH.spec.ts @@ -1,5 +1,5 @@ import { strict as assert } from 'assert'; -import { TestRedisServers, itWithClient, TestRedisClusters, itWithCluster } from '../test-utils'; +import { TestRedisServers, itWithClient } from '../test-utils'; import { transformArguments } from './UNWATCH'; describe('UNWATCH', () => { @@ -16,11 +16,4 @@ describe('UNWATCH', () => { 'OK' ); }); - - itWithCluster(TestRedisClusters.OPEN, 'cluster.unwatch', async cluster => { - assert.equal( - await cluster.unwatch(), - 'OK' - ); - }); }); diff --git a/lib/commands/WATCH.ts b/lib/commands/WATCH.ts index 5ca42c0eb95..f66429b507e 100644 --- a/lib/commands/WATCH.ts +++ b/lib/commands/WATCH.ts @@ -1,7 +1,7 @@ -import { TransformArgumentsReply } from '.'; +import { RedisCommandArguments } from '.'; import { pushVerdictArguments } from './generic-transformers'; -export function transformArguments(key: string | Array): TransformArgumentsReply { +export function transformArguments(key: string | Array): RedisCommandArguments { return pushVerdictArguments(['WATCH'], key); } diff --git a/lib/commands/XACK.ts b/lib/commands/XACK.ts index 4573b7f3d25..0d21a0ec085 100644 --- a/lib/commands/XACK.ts +++ b/lib/commands/XACK.ts @@ -1,9 +1,9 @@ -import { TransformArgumentsReply } from '.'; +import { RedisCommandArguments } from '.'; import { pushVerdictArguments } from './generic-transformers'; export const FIRST_KEY_INDEX = 1; -export function transformArguments(key: string, group: string, id: string | Array): TransformArgumentsReply { +export function transformArguments(key: string, group: string, id: string | Array): RedisCommandArguments { return pushVerdictArguments(['XACK', key, group], id); } diff --git a/lib/commands/XDEL.ts b/lib/commands/XDEL.ts index 187e5b4e73a..892b9a0de5b 100644 --- a/lib/commands/XDEL.ts +++ b/lib/commands/XDEL.ts @@ -1,9 +1,9 @@ -import { TransformArgumentsReply } from '.'; +import { RedisCommandArguments } from '.'; import { pushVerdictArguments } from './generic-transformers'; export const FIRST_KEY_INDEX = 1; -export function transformArguments(key: string, id: string | Array): TransformArgumentsReply { +export function transformArguments(key: string, id: string | Array): RedisCommandArguments { return pushVerdictArguments(['XDEL', key], id); } diff --git a/lib/commands/ZDIFF.ts b/lib/commands/ZDIFF.ts index 4c5b722131e..a45bf01a526 100644 --- a/lib/commands/ZDIFF.ts +++ b/lib/commands/ZDIFF.ts @@ -1,11 +1,11 @@ -import { TransformArgumentsReply } from '.'; +import { RedisCommandArguments } from '.'; import { pushVerdictArgument } from './generic-transformers'; export const FIRST_KEY_INDEX = 2; export const IS_READ_ONLY = true; -export function transformArguments(keys: Array | string): TransformArgumentsReply { +export function transformArguments(keys: Array | string): RedisCommandArguments { return pushVerdictArgument(['ZDIFF'], keys); } diff --git a/lib/commands/ZDIFFSTORE.ts b/lib/commands/ZDIFFSTORE.ts index 95c58c66b9e..9c782b8a5ab 100644 --- a/lib/commands/ZDIFFSTORE.ts +++ b/lib/commands/ZDIFFSTORE.ts @@ -1,9 +1,9 @@ -import { TransformArgumentsReply } from '.'; +import { RedisCommandArguments } from '.'; import { pushVerdictArgument } from './generic-transformers'; export const FIRST_KEY_INDEX = 1; -export function transformArguments(destination: string, keys: Array | string): TransformArgumentsReply { +export function transformArguments(destination: string, keys: Array | string): RedisCommandArguments { return pushVerdictArgument(['ZDIFFSTORE', destination], keys); } diff --git a/lib/commands/ZDIFF_WITHSCORES.ts b/lib/commands/ZDIFF_WITHSCORES.ts index 84126853361..49707563546 100644 --- a/lib/commands/ZDIFF_WITHSCORES.ts +++ b/lib/commands/ZDIFF_WITHSCORES.ts @@ -1,10 +1,10 @@ -import { TransformArgumentsReply } from '.'; +import { RedisCommandArguments } from '.'; import { transformReplySortedSetWithScores } from './generic-transformers'; import { transformArguments as transformZDiffArguments } from './ZDIFF'; export { FIRST_KEY_INDEX, IS_READ_ONLY } from './ZDIFF'; -export function transformArguments(...args: Parameters): TransformArgumentsReply { +export function transformArguments(...args: Parameters): RedisCommandArguments { return [ ...transformZDiffArguments(...args), 'WITHSCORES' diff --git a/lib/commands/ZINTER.ts b/lib/commands/ZINTER.ts index ae1b27a6c8d..eee49837386 100644 --- a/lib/commands/ZINTER.ts +++ b/lib/commands/ZINTER.ts @@ -1,4 +1,4 @@ -import { TransformArgumentsReply } from '.'; +import { RedisCommandArguments } from '.'; import { pushVerdictArgument } from './generic-transformers'; export const FIRST_KEY_INDEX = 2; @@ -10,7 +10,7 @@ interface ZInterOptions { AGGREGATE?: 'SUM' | 'MIN' | 'MAX'; } -export function transformArguments(keys: Array | string, options?: ZInterOptions): TransformArgumentsReply { +export function transformArguments(keys: Array | string, options?: ZInterOptions): RedisCommandArguments { const args = pushVerdictArgument(['ZINTER'], keys); if (options?.WEIGHTS) { diff --git a/lib/commands/ZINTERSTORE.ts b/lib/commands/ZINTERSTORE.ts index 496729a774e..59a27e11c51 100644 --- a/lib/commands/ZINTERSTORE.ts +++ b/lib/commands/ZINTERSTORE.ts @@ -1,4 +1,4 @@ -import { TransformArgumentsReply } from '.'; +import { RedisCommandArguments } from '.'; import { pushVerdictArgument } from './generic-transformers'; export const FIRST_KEY_INDEX = 1; @@ -8,7 +8,7 @@ interface ZInterStoreOptions { AGGREGATE?: 'SUM' | 'MIN' | 'MAX'; } -export function transformArguments(destination: string, keys: Array | string, options?: ZInterStoreOptions): TransformArgumentsReply { +export function transformArguments(destination: string, keys: Array | string, options?: ZInterStoreOptions): RedisCommandArguments { const args = pushVerdictArgument(['ZINTERSTORE', destination], keys); if (options?.WEIGHTS) { diff --git a/lib/commands/ZINTER_WITHSCORES.ts b/lib/commands/ZINTER_WITHSCORES.ts index f4287d1a684..f75a506de73 100644 --- a/lib/commands/ZINTER_WITHSCORES.ts +++ b/lib/commands/ZINTER_WITHSCORES.ts @@ -1,10 +1,10 @@ -import { TransformArgumentsReply } from '.'; +import { RedisCommandArguments } from '.'; import { transformReplySortedSetWithScores } from './generic-transformers'; import { transformArguments as transformZInterArguments } from './ZINTER'; export { FIRST_KEY_INDEX, IS_READ_ONLY } from './ZINTER'; -export function transformArguments(...args: Parameters): TransformArgumentsReply { +export function transformArguments(...args: Parameters): RedisCommandArguments { return [ ...transformZInterArguments(...args), 'WITHSCORES' diff --git a/lib/commands/ZMSCORE.ts b/lib/commands/ZMSCORE.ts index 373adac3cf0..2790f712316 100644 --- a/lib/commands/ZMSCORE.ts +++ b/lib/commands/ZMSCORE.ts @@ -1,11 +1,11 @@ -import { TransformArgumentsReply } from '.'; +import { RedisCommandArguments } from '.'; import { pushVerdictArguments, transformReplyNumberInfinityNullArray } from './generic-transformers'; export const FIRST_KEY_INDEX = 1; export const IS_READ_ONLY = true; -export function transformArguments(key: string, member: string | Array): TransformArgumentsReply { +export function transformArguments(key: string, member: string | Array): RedisCommandArguments { return pushVerdictArguments(['ZMSCORE', key], member); } diff --git a/lib/commands/ZRANGEBYLEX.ts b/lib/commands/ZRANGEBYLEX.ts index 5e4475800c2..7e2e7613b00 100644 --- a/lib/commands/ZRANGEBYLEX.ts +++ b/lib/commands/ZRANGEBYLEX.ts @@ -1,4 +1,4 @@ -import { TransformArgumentsReply } from '.'; +import { RedisCommandArguments } from '.'; import { transformArgumentNumberInfinity } from './generic-transformers'; export const FIRST_KEY_INDEX = 1; @@ -17,7 +17,7 @@ export function transformArguments( min: number | string, max: number | string, options?: ZRangeByLexOptions -): TransformArgumentsReply { +): RedisCommandArguments { const args = [ 'ZRANGEBYLEX', key, diff --git a/lib/commands/ZRANGEBYSCORE.ts b/lib/commands/ZRANGEBYSCORE.ts index 1932683f955..48dd8a415c6 100644 --- a/lib/commands/ZRANGEBYSCORE.ts +++ b/lib/commands/ZRANGEBYSCORE.ts @@ -1,4 +1,4 @@ -import { TransformArgumentsReply } from '.'; +import { RedisCommandArguments } from '.'; import { transformArgumentNumberInfinity } from './generic-transformers'; export const FIRST_KEY_INDEX = 1; @@ -17,7 +17,7 @@ export function transformArguments( min: number | string, max: number | string, options?: ZRangeByScoreOptions -): TransformArgumentsReply { +): RedisCommandArguments { const args = [ 'ZRANGEBYSCORE', key, diff --git a/lib/commands/ZRANGEBYSCORE_WITHSCORES.ts b/lib/commands/ZRANGEBYSCORE_WITHSCORES.ts index 050ebf58936..f6f7f993cbc 100644 --- a/lib/commands/ZRANGEBYSCORE_WITHSCORES.ts +++ b/lib/commands/ZRANGEBYSCORE_WITHSCORES.ts @@ -1,4 +1,4 @@ -import { TransformArgumentsReply } from '.'; +import { RedisCommandArguments } from '.'; import { transformReplySortedSetWithScores } from './generic-transformers'; import { ZRangeByScoreOptions, transformArguments as transformZRangeByScoreArguments } from './ZRANGEBYSCORE'; @@ -9,7 +9,7 @@ export function transformArguments( min: number | string, max: number | string, options?: ZRangeByScoreOptions -): TransformArgumentsReply { +): RedisCommandArguments { return [ ...transformZRangeByScoreArguments(key, min, max, options), 'WITHSCORES' diff --git a/lib/commands/ZREM.ts b/lib/commands/ZREM.ts index 550a41e8b5c..332289b3fdb 100644 --- a/lib/commands/ZREM.ts +++ b/lib/commands/ZREM.ts @@ -1,9 +1,9 @@ -import { TransformArgumentsReply } from '.'; +import { RedisCommandArguments } from '.'; import { pushVerdictArguments } from './generic-transformers'; export const FIRST_KEY_INDEX = 1; -export function transformArguments(key: string, member: string | Array): TransformArgumentsReply { +export function transformArguments(key: string, member: string | Array): RedisCommandArguments { return pushVerdictArguments(['ZREM', key], member); } diff --git a/lib/commands/ZUNION.ts b/lib/commands/ZUNION.ts index 1f0723eb021..2163978470c 100644 --- a/lib/commands/ZUNION.ts +++ b/lib/commands/ZUNION.ts @@ -1,4 +1,4 @@ -import { TransformArgumentsReply } from '.'; +import { RedisCommandArguments } from '.'; import { pushVerdictArgument } from './generic-transformers'; export const FIRST_KEY_INDEX = 2; @@ -10,7 +10,7 @@ interface ZUnionOptions { AGGREGATE?: 'SUM' | 'MIN' | 'MAX'; } -export function transformArguments(keys: Array | string, options?: ZUnionOptions): TransformArgumentsReply { +export function transformArguments(keys: Array | string, options?: ZUnionOptions): RedisCommandArguments { const args = pushVerdictArgument(['ZUNION'], keys); if (options?.WEIGHTS) { diff --git a/lib/commands/ZUNIONSTORE.ts b/lib/commands/ZUNIONSTORE.ts index d0a92b20a60..406f0430c52 100644 --- a/lib/commands/ZUNIONSTORE.ts +++ b/lib/commands/ZUNIONSTORE.ts @@ -1,4 +1,4 @@ -import { TransformArgumentsReply } from '.'; +import { RedisCommandArguments } from '.'; import { pushVerdictArgument } from './generic-transformers'; export const FIRST_KEY_INDEX = 1; @@ -8,7 +8,7 @@ interface ZUnionOptions { AGGREGATE?: 'SUM' | 'MIN' | 'MAX'; } -export function transformArguments(destination: string, keys: Array | string, options?: ZUnionOptions): TransformArgumentsReply { +export function transformArguments(destination: string, keys: Array | string, options?: ZUnionOptions): RedisCommandArguments { const args = pushVerdictArgument(['ZUNIONSTORE', destination], keys); if (options?.WEIGHTS) { diff --git a/lib/commands/ZUNION_WITHSCORES.ts b/lib/commands/ZUNION_WITHSCORES.ts index 2215dad9749..d361fd432b0 100644 --- a/lib/commands/ZUNION_WITHSCORES.ts +++ b/lib/commands/ZUNION_WITHSCORES.ts @@ -1,10 +1,10 @@ -import { TransformArgumentsReply } from '.'; +import { RedisCommandArguments } from '.'; import { transformReplySortedSetWithScores } from './generic-transformers'; import { transformArguments as transformZUnionArguments } from './ZUNION'; export { FIRST_KEY_INDEX, IS_READ_ONLY } from './ZUNION'; -export function transformArguments(...args: Parameters): TransformArgumentsReply { +export function transformArguments(...args: Parameters): RedisCommandArguments { return [ ...transformZUnionArguments(...args), 'WITHSCORES' diff --git a/lib/commands/generic-transformers.ts b/lib/commands/generic-transformers.ts index 98e6750f765..a531e86b432 100644 --- a/lib/commands/generic-transformers.ts +++ b/lib/commands/generic-transformers.ts @@ -1,4 +1,4 @@ -import { TransformArgumentsReply } from '.'; +import { RedisCommandArguments } from '.'; export function transformReplyBoolean(reply: number): boolean { return reply === 1; @@ -308,7 +308,7 @@ export function pushStringTuplesArguments(args: Array, tuples: StringTup return args; } -export function pushVerdictArguments(args: TransformArgumentsReply, value: string | Buffer | Array): TransformArgumentsReply { +export function pushVerdictArguments(args: RedisCommandArguments, value: string | Buffer | Array): RedisCommandArguments { if (Array.isArray(value)) { args.push(...value); } else { @@ -318,7 +318,7 @@ export function pushVerdictArguments(args: TransformArgumentsReply, value: strin return args; } -export function pushVerdictArgument(args: TransformArgumentsReply, value: string | Array): TransformArgumentsReply { +export function pushVerdictArgument(args: RedisCommandArguments, value: string | Array): RedisCommandArguments { if (typeof value === 'string') { args.push('1', value); } else { @@ -328,7 +328,7 @@ export function pushVerdictArgument(args: TransformArgumentsReply, value: string return args; } -export function pushOptionalVerdictArgument(args: TransformArgumentsReply, name: string, value: undefined | string | Array): TransformArgumentsReply { +export function pushOptionalVerdictArgument(args: RedisCommandArguments, name: string, value: undefined | string | Array): RedisCommandArguments { if (value === undefined) return args; args.push(name); diff --git a/lib/commands/index.ts b/lib/commands/index.ts index 014aff9e3a6..f88d777c892 100644 --- a/lib/commands/index.ts +++ b/lib/commands/index.ts @@ -1,778 +1,18 @@ -import * as ACL_CAT from './ACL_CAT'; -import * as ACL_DELUSER from './ACL_DELUSER'; -import * as ACL_GENPASS from './ACL_GENPASS'; -import * as ACL_GETUSER from './ACL_GETUSER'; -import * as ACL_LIST from './ACL_LIST'; -import * as ACL_LOAD from './ACL_LOAD'; -import * as ACL_LOG_RESET from './ACL_LOG_RESET'; -import * as ACL_LOG from './ACL_LOG'; -import * as ACL_SAVE from './ACL_SAVE'; -import * as ACL_SETUSER from './ACL_SETUSER'; -import * as ACL_USERS from './ACL_USERS'; -import * as ACL_WHOAMI from './ACL_WHOAMI'; -import * as APPEND from './APPEND'; -import * as ASKING from './ASKING'; -import * as AUTH from './AUTH'; -import * as BGREWRITEAOF from './BGREWRITEAOF'; -import * as BGSAVE from './BGSAVE'; -import * as BITCOUNT from './BITCOUNT'; -import * as BITFIELD from './BITFIELD'; -import * as BITOP from './BITOP'; -import * as BITPOS from './BITPOS'; -import * as BLMOVE from './BLMOVE'; -import * as BLPOP from './BLPOP'; -import * as BRPOP from './BRPOP'; -import * as BRPOPLPUSH from './BRPOPLPUSH'; -import * as BZPOPMAX from './BZPOPMAX'; -import * as BZPOPMIN from './BZPOPMIN'; -import * as CLIENT_ID from './CLIENT_ID'; -import * as CLIENT_INFO from './CLIENT_INFO'; -import * as CLUSTER_ADDSLOTS from './CLUSTER_ADDSLOTS'; -import * as CLUSTER_FLUSHSLOTS from './CLUSTER_FLUSHSLOTS'; -import * as CLUSTER_INFO from './CLUSTER_INFO'; -import * as CLUSTER_NODES from './CLUSTER_NODES'; -import * as CLUSTER_MEET from './CLUSTER_MEET'; -import * as CLUSTER_RESET from './CLUSTER_RESET'; -import * as CLUSTER_SETSLOT from './CLUSTER_SETSLOT'; -import * as CLUSTER_SLOTS from './CLUSTER_SLOTS'; -import * as COMMAND_COUNT from './COMMAND_COUNT'; -import * as COMMAND_GETKEYS from './COMMAND_GETKEYS'; -import * as COMMAND_INFO from './COMMAND_INFO'; -import * as COMMAND from './COMMAND'; -import * as CONFIG_GET from './CONFIG_GET'; -import * as CONFIG_RESETASTAT from './CONFIG_RESETSTAT'; -import * as CONFIG_REWRITE from './CONFIG_REWRITE'; -import * as CONFIG_SET from './CONFIG_SET'; -import * as COPY from './COPY'; -import * as DBSIZE from './DBSIZE'; -import * as DECR from './DECR'; -import * as DECRBY from './DECRBY'; -import * as DEL from './DEL'; -import * as DISCARD from './DISCARD'; -import * as DUMP from './DUMP'; -import * as ECHO from './ECHO'; -import * as EVAL from './EVAL'; -import * as EVALSHA from './EVALSHA'; -import * as EXISTS from './EXISTS'; -import * as EXPIRE from './EXPIRE'; -import * as EXPIREAT from './EXPIREAT'; -import * as FAILOVER from './FAILOVER'; -import * as FLUSHALL from './FLUSHALL'; -import * as FLUSHDB from './FLUSHDB'; -import * as GEOADD from './GEOADD'; -import * as GEODIST from './GEODIST'; -import * as GEOHASH from './GEOHASH'; -import * as GEOPOS from './GEOPOS'; -import * as GEOSEARCH_WITH from './GEOSEARCH_WITH'; -import * as GEOSEARCH from './GEOSEARCH'; -import * as GEOSEARCHSTORE from './GEOSEARCHSTORE'; -import * as GET_BUFFER from './GET_BUFFER'; -import * as GET from './GET'; -import * as GETBIT from './GETBIT'; -import * as GETDEL from './GETDEL'; -import * as GETEX from './GETEX'; -import * as GETRANGE from './GETRANGE'; -import * as GETSET from './GETSET'; -import * as HDEL from './HDEL'; -import * as HELLO from './HELLO'; -import * as HEXISTS from './HEXISTS'; -import * as HGET from './HGET'; -import * as HGETALL from './HGETALL'; -import * as HINCRBY from './HINCRBY'; -import * as HINCRBYFLOAT from './HINCRBYFLOAT'; -import * as HKEYS from './HKEYS'; -import * as HLEN from './HLEN'; -import * as HMGET from './HMGET'; -import * as HRANDFIELD_COUNT_WITHVALUES from './HRANDFIELD_COUNT_WITHVALUES'; -import * as HRANDFIELD_COUNT from './HRANDFIELD_COUNT'; -import * as HRANDFIELD from './HRANDFIELD'; -import * as HSCAN from './HSCAN'; -import * as HSET from './HSET'; -import * as HSETNX from './HSETNX'; -import * as HSTRLEN from './HSTRLEN'; -import * as HVALS from './HVALS'; -import * as INCR from './INCR'; -import * as INCRBY from './INCRBY'; -import * as INCRBYFLOAT from './INCRBYFLOAT'; -import * as INFO from './INFO'; -import * as KEYS from './KEYS'; -import * as LASTSAVE from './LASTSAVE'; -import * as LINDEX from './LINDEX'; -import * as LINSERT from './LINSERT'; -import * as LLEN from './LLEN'; -import * as LMOVE from './LMOVE'; -import * as LOLWUT from './LOLWUT'; -import * as LPOP_COUNT from './LPOP_COUNT'; -import * as LPOP from './LPOP'; -import * as LPOS_COUNT from './LPOS_COUNT'; -import * as LPOS from './LPOS'; -import * as LPUSH from './LPUSH'; -import * as LPUSHX from './LPUSHX'; -import * as LRANGE from './LRANGE'; -import * as LREM from './LREM'; -import * as LSET from './LSET'; -import * as LTRIM from './LTRIM'; -import * as MEMOERY_DOCTOR from './MEMORY_DOCTOR'; -import * as MEMORY_MALLOC_STATS from './MEMORY_MALLOC-STATS'; -import * as MEMORY_PURGE from './MEMORY_PURGE'; -import * as MEMORY_STATS from './MEMORY_STATS'; -import * as MEMORY_USAGE from './MEMORY_USAGE'; -import * as MGET from './MGET'; -import * as MIGRATE from './MIGRATE'; -import * as MODULE_LIST from './MODULE_LIST'; -import * as MODULE_LOAD from './MODULE_LOAD'; -import * as MODULE_UNLOAD from './MODULE_UNLOAD'; -import * as MOVE from './MOVE'; -import * as MSET from './MSET'; -import * as MSETNX from './MSETNX'; -import * as PERSIST from './PERSIST'; -import * as PEXPIRE from './PEXPIRE'; -import * as PEXPIREAT from './PEXPIREAT'; -import * as PFADD from './PFADD'; -import * as PFCOUNT from './PFCOUNT'; -import * as PFMERGE from './PFMERGE'; -import * as PING from './PING'; -import * as PSETEX from './PSETEX'; -import * as PTTL from './PTTL'; -import * as PUBLISH from './PUBLISH'; -import * as PUBSUB_CHANNELS from './PUBSUB_CHANNELS'; -import * as PUBSUB_NUMPAT from './PUBSUB_NUMPAT'; -import * as PUBSUB_NUMSUB from './PUBSUB_NUMSUB'; -import * as RANDOMKEY from './RANDOMKEY'; -import * as READONLY from './READONLY'; -import * as READWRITE from './READWRITE'; -import * as RENAME from './RENAME'; -import * as RENAMENX from './RENAMENX'; -import * as REPLICAOF from './REPLICAOF'; -import * as RESTORE_ASKING from './RESTORE-ASKING'; -import * as ROLE from './ROLE'; -import * as RPOP_COUNT from './RPOP_COUNT'; -import * as RPOP from './RPOP'; -import * as RPOPLPUSH from './RPOPLPUSH'; -import * as RPUSH from './RPUSH'; -import * as RPUSHX from './RPUSHX'; -import * as SADD from './SADD'; -import * as SAVE from './SAVE'; -import * as SCAN from './SCAN'; -import * as SCARD from './SCARD'; -import * as SCRIPT_DEBUG from './SCRIPT_DEBUG'; -import * as SCRIPT_EXISTS from './SCRIPT_EXISTS'; -import * as SCRIPT_FLUSH from './SCRIPT_FLUSH'; -import * as SCRIPT_KILL from './SCRIPT_KILL'; -import * as SCRIPT_LOAD from './SCRIPT_LOAD'; -import * as SDIFF from './SDIFF'; -import * as SDIFFSTORE from './SDIFFSTORE'; -import * as SET from './SET'; -import * as SETBIT from './SETBIT'; -import * as SETEX from './SETEX'; -import * as SETNX from './SETNX'; -import * as SETRANGE from './SETRANGE'; -import * as SHUTDOWN from './SHUTDOWN'; -import * as SINTER from './SINTER'; -import * as SINTERSTORE from './SINTERSTORE'; -import * as SISMEMBER from './SISMEMBER'; -import * as SMEMBERS from './SMEMBERS'; -import * as SMISMEMBER from './SMISMEMBER'; -import * as SMOVE from './SMOVE'; -import * as SORT from './SORT'; -import * as SPOP from './SPOP'; -import * as SRANDMEMBER_COUNT from './SRANDMEMBER_COUNT'; -import * as SRANDMEMBER from './SRANDMEMBER'; -import * as SREM from './SREM'; -import * as SSCAN from './SSCAN'; -import * as STRLEN from './STRLEN'; -import * as SUNION from './SUNION'; -import * as SUNIONSTORE from './SUNIONSTORE'; -import * as SWAPDB from './SWAPDB'; -import * as TIME from './TIME'; -import * as TOUCH from './TOUCH'; -import * as TTL from './TTL'; -import * as TYPE from './TYPE'; -import * as UNLINK from './UNLINK'; -import * as UNWATCH from './UNWATCH'; -import * as WAIT from './WAIT'; -import * as WATCH from './WATCH'; -import * as XACK from './XACK'; -import * as XADD from './XADD'; -import * as XAUTOCLAIM_JUSTID from './XAUTOCLAIM_JUSTID'; -import * as XAUTOCLAIM from './XAUTOCLAIM'; -import * as XCLAIM from './XCLAIM'; -import * as XCLAIM_JUSTID from './XCLAIM_JUSTID'; -import * as XDEL from './XDEL'; -import * as XGROUP_CREATE from './XGROUP_CREATE'; -import * as XGROUP_CREATECONSUMER from './XGROUP_CREATECONSUMER'; -import * as XGROUP_DELCONSUMER from './XGROUP_DELCONSUMER'; -import * as XGROUP_DESTROY from './XGROUP_DESTROY'; -import * as XGROUP_SETID from './XGROUP_SETID'; -import * as XINFO_CONSUMERS from './XINFO_CONSUMERS'; -import * as XINFO_GROUPS from './XINFO_GROUPS'; -import * as XINFO_STREAM from './XINFO_STREAM'; -import * as XLEN from './XLEN'; -import * as XPENDING_RANGE from './XPENDING_RANGE'; -import * as XPENDING from './XPENDING'; -import * as XRANGE from './XRANGE'; -import * as XREAD from './XREAD'; -import * as XREADGROUP from './XREADGROUP'; -import * as XREVRANGE from './XREVRANGE'; -import * as XTRIM from './XTRIM'; -import * as ZADD from './ZADD'; -import * as ZCARD from './ZCARD'; -import * as ZCOUNT from './ZCOUNT'; -import * as ZDIFF_WITHSCORES from './ZDIFF_WITHSCORES'; -import * as ZDIFF from './ZDIFF'; -import * as ZDIFFSTORE from './ZDIFFSTORE'; -import * as ZINCRBY from './ZINCRBY'; -import * as ZINTER_WITHSCORES from './ZINTER_WITHSCORES'; -import * as ZINTER from './ZINTER'; -import * as ZINTERSTORE from './ZINTERSTORE'; -import * as ZLEXCOUNT from './ZLEXCOUNT'; -import * as ZMSCORE from './ZMSCORE'; -import * as ZPOPMAX_COUNT from './ZPOPMAX_COUNT'; -import * as ZPOPMAX from './ZPOPMAX'; -import * as ZPOPMIN_COUNT from './ZPOPMIN_COUNT'; -import * as ZPOPMIN from './ZPOPMIN'; -import * as ZRANDMEMBER_COUNT_WITHSCORES from './ZRANDMEMBER_COUNT_WITHSCORES'; -import * as ZRANDMEMBER_COUNT from './ZRANDMEMBER_COUNT'; -import * as ZRANDMEMBER from './ZRANDMEMBER'; -import * as ZRANGE_WITHSCORES from './ZRANGE_WITHSCORES'; -import * as ZRANGE from './ZRANGE'; -import * as ZRANGEBYLEX from './ZRANGEBYLEX'; -import * as ZRANGEBYSCORE_WITHSCORES from './ZRANGEBYSCORE_WITHSCORES'; -import * as ZRANGEBYSCORE from './ZRANGEBYSCORE'; -import * as ZRANGESTORE from './ZRANGESTORE'; -import * as ZRANK from './ZRANK'; -import * as ZREM from './ZREM'; -import * as ZREMRANGEBYLEX from './ZREMRANGEBYLEX'; -import * as ZREMRANGEBYRANK from './ZREMRANGEBYRANK'; -import * as ZREMRANGEBYSCORE from './ZREMRANGEBYSCORE'; -import * as ZREVRANK from './ZREVRANK'; -import * as ZSCAN from './ZSCAN'; -import * as ZSCORE from './ZSCORE'; -import * as ZUNION_WITHSCORES from './ZUNION_WITHSCORES'; -import * as ZUNION from './ZUNION'; -import * as ZUNIONSTORE from './ZUNIONSTORE'; +import { RedisScriptConfig, SHA1 } from '../lua-script'; -export default { - ACL_CAT, - aclCat: ACL_CAT, - ACL_DELUSER, - aclDelUser: ACL_DELUSER, - ACL_GENPASS, - aclGenPass: ACL_GENPASS, - ACL_GETUSER, - aclGetUser: ACL_GETUSER, - ACL_LIST, - aclList: ACL_LIST, - ACL_LOAD, - aclLoad: ACL_LOAD, - ACL_LOG_RESET, - aclLogReset: ACL_LOG_RESET, - ACL_LOG, - aclLog: ACL_LOG, - ACL_SAVE, - aclSave: ACL_SAVE, - ACL_SETUSER, - aclSetUser: ACL_SETUSER, - ACL_USERS, - aclUsers: ACL_USERS, - ACL_WHOAMI, - aclWhoAmI: ACL_WHOAMI, - APPEND, - append: APPEND, - ASKING, - asking: ASKING, - AUTH, - auth: AUTH, - BGREWRITEAOF, - bgRewriteAof: BGREWRITEAOF, - BGSAVE, - bgSave: BGSAVE, - BITCOUNT, - bitCount: BITCOUNT, - BITFIELD, - bitField: BITFIELD, - BITOP, - bitOp: BITOP, - BITPOS, - bitPos: BITPOS, - BLMOVE, - blMove: BLMOVE, - BLPOP, - blPop: BLPOP, - BRPOP, - brPop: BRPOP, - BRPOPLPUSH, - brPopLPush: BRPOPLPUSH, - BZPOPMAX, - bzPopMax: BZPOPMAX, - BZPOPMIN, - bzPopMin: BZPOPMIN, - CLIENT_ID, - clientId: CLIENT_ID, - CLIENT_INFO, - clientInfo: CLIENT_INFO, - CLUSTER_ADDSLOTS, - clusterAddSlots: CLUSTER_ADDSLOTS, - CLUSTER_FLUSHSLOTS, - clusterFlushSlots: CLUSTER_FLUSHSLOTS, - CLUSTER_INFO, - clusterInfo: CLUSTER_INFO, - CLUSTER_NODES, - clusterNodes: CLUSTER_NODES, - CLUSTER_MEET, - clusterMeet: CLUSTER_MEET, - CLUSTER_RESET, - clusterReset: CLUSTER_RESET, - CLUSTER_SETSLOT, - clusterSetSlot: CLUSTER_SETSLOT, - CLUSTER_SLOTS, - clusterSlots: CLUSTER_SLOTS, - COMMAND_COUNT, - commandCount: COMMAND_COUNT, - COMMAND_GETKEYS, - commandGetKeys: COMMAND_GETKEYS, - COMMAND_INFO, - commandInfo: COMMAND_INFO, - COMMAND, - command: COMMAND, - CONFIG_GET, - configGet: CONFIG_GET, - CONFIG_RESETASTAT, - configResetStat: CONFIG_RESETASTAT, - CONFIG_REWRITE, - configRewrite: CONFIG_REWRITE, - CONFIG_SET, - configSet: CONFIG_SET, - COPY, - copy: COPY, - DBSIZE, - dbSize: DBSIZE, - DECR, - decr: DECR, - DECRBY, - decrBy: DECRBY, - DEL, - del: DEL, - DISCARD, - discard: DISCARD, - DUMP, - dump: DUMP, - ECHO, - echo: ECHO, - EVAL, - eval: EVAL, - EVALSHA, - evalSha: EVALSHA, - EXISTS, - exists: EXISTS, - EXPIRE, - expire: EXPIRE, - EXPIREAT, - expireAt: EXPIREAT, - FAILOVER, - failover: FAILOVER, - FLUSHALL, - flushAll: FLUSHALL, - FLUSHDB, - flushDb: FLUSHDB, - GEOADD, - geoAdd: GEOADD, - GEODIST, - geoDist: GEODIST, - GEOHASH, - geoHash: GEOHASH, - GEOPOS, - geoPos: GEOPOS, - GEOSEARCH_WITH, - geoSearchWith: GEOSEARCH_WITH, - GEOSEARCH, - geoSearch: GEOSEARCH, - GEOSEARCHSTORE, - geoSearchStore: GEOSEARCHSTORE, - GET_BUFFER, - getBuffer: GET_BUFFER, - GET, - get: GET, - GETBIT, - getBit: GETBIT, - GETDEL, - getDel: GETDEL, - GETEX, - getEx: GETEX, - GETRANGE, - getRange: GETRANGE, - GETSET, - getSet: GETSET, - HDEL, - hDel: HDEL, - HELLO, - hello: HELLO, - HEXISTS, - hExists: HEXISTS, - HGET, - hGet: HGET, - HGETALL, - hGetAll: HGETALL, - HINCRBY, - hIncrBy: HINCRBY, - HINCRBYFLOAT, - hIncrByFloat: HINCRBYFLOAT, - HKEYS, - hKeys: HKEYS, - HLEN, - hLen: HLEN, - HMGET, - hmGet: HMGET, - HRANDFIELD_COUNT_WITHVALUES, - hRandFieldCountWithValues: HRANDFIELD_COUNT_WITHVALUES, - HRANDFIELD_COUNT, - hRandFieldCount: HRANDFIELD_COUNT, - HRANDFIELD, - hRandField: HRANDFIELD, - HSCAN, - hScan: HSCAN, - HSET, - hSet: HSET, - HSETNX, - hSetNX: HSETNX, - HSTRLEN, - hStrLen: HSTRLEN, - HVALS, - hVals: HVALS, - INCR, - incr: INCR, - INCRBY, - incrBy: INCRBY, - INCRBYFLOAT, - incrByFloat: INCRBYFLOAT, - INFO, - info: INFO, - KEYS, - keys: KEYS, - LASTSAVE, - lastSave: LASTSAVE, - LINDEX, - lIndex: LINDEX, - LINSERT, - lInsert: LINSERT, - LLEN, - lLen: LLEN, - LMOVE, - lMove: LMOVE, - LOLWUT, - LPOP_COUNT, - lPopCount: LPOP_COUNT, - LPOP, - lPop: LPOP, - LPOS_COUNT, - lPosCount: LPOS_COUNT, - LPOS, - lPos: LPOS, - LPUSH, - lPush: LPUSH, - LPUSHX, - lPushX: LPUSHX, - LRANGE, - lRange: LRANGE, - LREM, - lRem: LREM, - LSET, - lSet: LSET, - LTRIM, - lTrim: LTRIM, - MEMOERY_DOCTOR, - memoryDoctor: MEMOERY_DOCTOR, - 'MEMORY_MALLOC-STATS': MEMORY_MALLOC_STATS, - memoryMallocStats: MEMORY_MALLOC_STATS, - MEMORY_PURGE, - memoryPurge: MEMORY_PURGE, - MEMORY_STATS, - memoryStats: MEMORY_STATS, - MEMORY_USAGE, - memoryUsage: MEMORY_USAGE, - MGET, - mGet: MGET, - MIGRATE, - migrate: MIGRATE, - MODULE_LIST, - moduleList: MODULE_LIST, - MODULE_LOAD, - moduleLoad: MODULE_LOAD, - MODULE_UNLOAD, - moduleUnload: MODULE_UNLOAD, - MOVE, - move: MOVE, - MSET, - mSet: MSET, - MSETNX, - mSetNX: MSETNX, - PERSIST, - persist: PERSIST, - PEXPIRE, - pExpire: PEXPIRE, - PEXPIREAT, - pExpireAt: PEXPIREAT, - PFADD, - pfAdd: PFADD, - PFCOUNT, - pfCount: PFCOUNT, - PFMERGE, - pfMerge: PFMERGE, - PING, - ping: PING, - PSETEX, - pSetEx: PSETEX, - PTTL, - pTTL: PTTL, - PUBLISH, - publish: PUBLISH, - PUBSUB_CHANNELS, - pubSubChannels: PUBSUB_CHANNELS, - PUBSUB_NUMPAT, - pubSubNumPat: PUBSUB_NUMPAT, - PUBSUB_NUMSUB, - pubSubNumSub: PUBSUB_NUMSUB, - RANDOMKEY, - randomKey: RANDOMKEY, - READONLY, - readonly: READONLY, - READWRITE, - readwrite: READWRITE, - RENAME, - rename: RENAME, - RENAMENX, - renameNX: RENAMENX, - REPLICAOF, - replicaOf: REPLICAOF, - 'RESTORE-ASKING': RESTORE_ASKING, - restoreAsking: RESTORE_ASKING, - ROLE, - role: ROLE, - RPOP_COUNT, - rPopCount: RPOP_COUNT, - RPOP, - rPop: RPOP, - RPOPLPUSH, - rPopLPush: RPOPLPUSH, - RPUSH, - rPush: RPUSH, - RPUSHX, - rPushX: RPUSHX, - SADD, - sAdd: SADD, - SAVE, - save: SAVE, - SCAN, - scan: SCAN, - SCARD, - sCard: SCARD, - SCRIPT_DEBUG, - scriptDebug: SCRIPT_DEBUG, - SCRIPT_EXISTS, - scriptExists: SCRIPT_EXISTS, - SCRIPT_FLUSH, - scriptFlush: SCRIPT_FLUSH, - SCRIPT_KILL, - scriptKill: SCRIPT_KILL, - SCRIPT_LOAD, - scriptLoad: SCRIPT_LOAD, - SDIFF, - sDiff: SDIFF, - SDIFFSTORE, - sDiffStore: SDIFFSTORE, - SINTER, - sInter: SINTER, - SINTERSTORE, - sInterStore: SINTERSTORE, - SET, - set: SET, - SETBIT, - setBit: SETBIT, - SETEX, - setEx: SETEX, - SETNX, - setNX: SETNX, - SETRANGE, - setRange: SETRANGE, - SHUTDOWN, - shutdown: SHUTDOWN, - SISMEMBER, - sIsMember: SISMEMBER, - SMEMBERS, - sMembers: SMEMBERS, - SMISMEMBER, - smIsMember: SMISMEMBER, - SMOVE, - sMove: SMOVE, - SORT, - sort: SORT, - SPOP, - sPop: SPOP, - SRANDMEMBER_COUNT, - sRandMemberCount: SRANDMEMBER_COUNT, - SRANDMEMBER, - sRandMember: SRANDMEMBER, - SREM, - sRem: SREM, - SSCAN, - sScan: SSCAN, - STRLEN, - strLen: STRLEN, - SUNION, - sUnion: SUNION, - SUNIONSTORE, - sUnionStore: SUNIONSTORE, - SWAPDB, - swapDb: SWAPDB, - TIME, - time: TIME, - TOUCH, - touch: TOUCH, - TTL, - ttl: TTL, - TYPE, - type: TYPE, - UNLINK, - unlink: UNLINK, - UNWATCH, - unwatch: UNWATCH, - WAIT, - wait: WAIT, - WATCH, - watch: WATCH, - XACK, - xAck: XACK, - XADD, - xAdd: XADD, - XAUTOCLAIM_JUSTID, - xAutoClaimJustId: XAUTOCLAIM_JUSTID, - XAUTOCLAIM, - xAutoClaim: XAUTOCLAIM, - XCLAIM, - xClaim: XCLAIM, - XCLAIM_JUSTID, - xClaimJustId: XCLAIM_JUSTID, - XDEL, - xDel: XDEL, - XGROUP_CREATE, - xGroupCreate: XGROUP_CREATE, - XGROUP_CREATECONSUMER, - xGroupCreateConsumer: XGROUP_CREATECONSUMER, - XGROUP_DELCONSUMER, - xGroupDelConsumer: XGROUP_DELCONSUMER, - XGROUP_DESTROY, - xGroupDestroy: XGROUP_DESTROY, - XGROUP_SETID, - xGroupSetId: XGROUP_SETID, - XINFO_CONSUMERS, - xInfoConsumers: XINFO_CONSUMERS, - XINFO_GROUPS, - xInfoGroups: XINFO_GROUPS, - XINFO_STREAM, - xInfoStream: XINFO_STREAM, - XLEN, - xLen: XLEN, - XPENDING_RANGE, - xPendingRange: XPENDING_RANGE, - XPENDING, - xPending: XPENDING, - XRANGE, - xRange: XRANGE, - XREAD, - xRead: XREAD, - XREADGROUP, - xReadGroup: XREADGROUP, - XREVRANGE, - xRevRange: XREVRANGE, - XTRIM, - xTrim: XTRIM, - ZADD, - zAdd: ZADD, - ZCARD, - zCard: ZCARD, - ZCOUNT, - zCount: ZCOUNT, - ZDIFF_WITHSCORES, - zDiffWithScores: ZDIFF_WITHSCORES, - ZDIFF, - zDiff: ZDIFF, - ZDIFFSTORE, - zDiffStore: ZDIFFSTORE, - ZINCRBY, - zIncrBy: ZINCRBY, - ZINTER_WITHSCORES, - zInterWithScores: ZINTER_WITHSCORES, - ZINTER, - zInter: ZINTER, - ZINTERSTORE, - zInterStore: ZINTERSTORE, - ZLEXCOUNT, - zLexCount: ZLEXCOUNT, - ZMSCORE, - zmScore: ZMSCORE, - ZPOPMAX_COUNT, - zPopMaxCount: ZPOPMAX_COUNT, - ZPOPMAX, - zPopMax: ZPOPMAX, - ZPOPMIN_COUNT, - zPopMinCount: ZPOPMIN_COUNT, - ZPOPMIN, - zPopMin: ZPOPMIN, - ZRANDMEMBER_COUNT_WITHSCORES, - zRandMemberCountWithScores: ZRANDMEMBER_COUNT_WITHSCORES, - ZRANDMEMBER_COUNT, - zRandMemberCount: ZRANDMEMBER_COUNT, - ZRANDMEMBER, - zRandMember: ZRANDMEMBER, - ZRANGE_WITHSCORES, - zRangeWithScores: ZRANGE_WITHSCORES, - ZRANGE, - zRange: ZRANGE, - ZRANGEBYLEX, - zRangeByLex: ZRANGEBYLEX, - ZRANGEBYSCORE_WITHSCORES, - zRangeByScoreWithScores: ZRANGEBYSCORE_WITHSCORES, - ZRANGEBYSCORE, - zRangeByScore: ZRANGEBYSCORE, - ZRANGESTORE, - zRangeStore: ZRANGESTORE, - ZRANK, - zRank: ZRANK, - ZREM, - zRem: ZREM, - ZREMRANGEBYLEX, - zRemRangeByLex: ZREMRANGEBYLEX, - ZREMRANGEBYRANK, - zRemRangeByRank: ZREMRANGEBYRANK, - ZREMRANGEBYSCORE, - zRemRangeByScore: ZREMRANGEBYSCORE, - ZREVRANK, - zRevRank: ZREVRANK, - ZSCAN, - zScan: ZSCAN, - ZSCORE, - zScore: ZSCORE, - ZUNION_WITHSCORES, - zUnionWithScores: ZUNION_WITHSCORES, - ZUNION, - zUnion: ZUNION, - ZUNIONSTORE, - zUnionStore: ZUNIONSTORE -}; +export type RedisCommandRawReply = string | number | Buffer | Array | null | undefined; -export type RedisReply = string | number | Buffer | Array | null | undefined; - -export type TransformArgumentsReply = Array & { preserve?: unknown }; +export type RedisCommandArguments = Array & { preserve?: unknown }; export interface RedisCommand { FIRST_KEY_INDEX?: number | ((...args: Array) => string); IS_READ_ONLY?: boolean; - transformArguments(this: void, ...args: Array): TransformArgumentsReply; + transformArguments(this: void, ...args: Array): RedisCommandArguments; BUFFER_MODE?: boolean; - transformReply?(this: void, reply: RedisReply, preserved?: unknown): any; + transformReply?(this: void, reply: RedisCommandRawReply, preserved?: unknown): any; } -export type RedisCommandReply = C['transformReply'] extends (...args: any) => infer T ? T : RedisReply; +export type RedisCommandReply = C['transformReply'] extends (...args: any) => infer T ? T : RedisCommandRawReply; export interface RedisCommands { [command: string]: RedisCommand; @@ -785,4 +25,14 @@ export interface RedisModule { export interface RedisModules { [module: string]: RedisModule; } -// export type RedisModules = Record; + +export type RedisScript = RedisScriptConfig & SHA1; + +export interface RedisScripts { + [script: string]: RedisScript; +} + +export interface RedisPlugins { + modules?: M; + scripts?: S; +} diff --git a/lib/lua-script.ts b/lib/lua-script.ts index be16f9b9133..3089d468d65 100644 --- a/lib/lua-script.ts +++ b/lib/lua-script.ts @@ -1,7 +1,7 @@ import { createHash } from 'crypto'; import { RedisCommand } from './commands'; -export interface RedisLuaScriptConfig extends RedisCommand { +export interface RedisScriptConfig extends RedisCommand { SCRIPT: string; NUMBER_OF_KEYS: number; } @@ -10,13 +10,7 @@ export interface SHA1 { SHA1: string; } -export type RedisLuaScript = RedisLuaScriptConfig & SHA1; - -export interface RedisLuaScripts { - [script: string]: RedisLuaScript; -} - -export function defineScript(script: RedisLuaScriptConfig): typeof script & SHA1 { +export function defineScript(script: RedisScriptConfig): typeof script & SHA1 { return { ...script, SHA1: scriptSha1(script.SCRIPT) diff --git a/lib/multi-command.spec.ts b/lib/multi-command.spec.ts index 52ecfb94b1c..7e9667cd518 100644 --- a/lib/multi-command.spec.ts +++ b/lib/multi-command.spec.ts @@ -1,126 +1,97 @@ import { strict as assert } from 'assert'; import RedisMultiCommand from './multi-command'; import { WatchError } from './errors'; -import { spy } from 'sinon'; -import { SQUARE_SCRIPT } from './client.spec'; +import { SQUARE_SCRIPT } from './client/index.spec'; describe('Multi Command', () => { - describe('exec', () => { - it('simple', async () => { - const multi = RedisMultiCommand.create((queue, symbol) => { - assert.deepEqual( - queue.map(({ args }) => args), - [ - ['MULTI'], - ['PING'], - ['EXEC'], - ] - ); + it('generateChainId', () => { + assert.equal( + typeof RedisMultiCommand.generateChainId(), + 'symbol' + ); + }); - assert.equal( - typeof symbol, - 'symbol' - ); + it('addCommand', () => { + const multi = new RedisMultiCommand(); + multi.addCommand(['PING']); - return Promise.resolve(['QUEUED', 'QUEUED', ['PONG']]); - }); + assert.deepEqual( + multi.queue[0].args, + ['PING'] + ); + }); - multi.ping(); + it('addScript', () => { + const multi = new RedisMultiCommand(); + + multi.addScript(SQUARE_SCRIPT, ['1']); + assert.equal( + multi.scriptsInUse.has(SQUARE_SCRIPT.SHA1), + true + ); + assert.deepEqual( + multi.queue[0].args, + ['EVAL', SQUARE_SCRIPT.SCRIPT, '0', '1'] + ); + + multi.addScript(SQUARE_SCRIPT, ['2']); + assert.equal( + multi.scriptsInUse.has(SQUARE_SCRIPT.SHA1), + true + ); + assert.deepEqual( + multi.queue[1].args, + ['EVALSHA', SQUARE_SCRIPT.SHA1, '0', '2'] + ); + }); - assert.deepEqual( - await multi.exec(), - ['PONG'] + describe('exec', () => { + it('undefined', () => { + assert.equal( + new RedisMultiCommand().exec(), + undefined ); }); - it('executing an empty queue should resolve without executing on the server', async () => { - const executor = spy(); + it('Array', () => { + const multi = new RedisMultiCommand(); + multi.addCommand(['PING']); assert.deepEqual( - await RedisMultiCommand.create(executor).exec(), - [] + multi.exec(), + [ + { args: ['MULTI'] }, + { args: ['PING'], transformReply: undefined }, + { args: ['EXEC'] } + ] ); - - assert.ok(executor.notCalled); }); + }); + describe('handleExecReplies', () => { it('WatchError', () => { - return assert.rejects( - RedisMultiCommand.create(() => Promise.resolve([null])).ping().exec(), + assert.throws( + () => new RedisMultiCommand().handleExecReplies([null]), WatchError ); }); - it('execAsPipeline', async () => { - const multi = RedisMultiCommand.create(queue => { - assert.deepEqual( - queue.map(({ args }) => args), - [['PING']] - ); - - return Promise.resolve(['PONG']); - }); - - multi.ping(); - + it('with replies', () => { + const multi = new RedisMultiCommand(); + multi.addCommand(['PING']); assert.deepEqual( - await multi.exec(true), + multi.handleExecReplies(['OK', 'QUEUED', ['PONG']]), ['PONG'] ); }); }); - describe('execAsPipeline', () => { - it('simple', async () => { - const multi = RedisMultiCommand.create(queue => { - assert.deepEqual( - queue.map(({ args }) => args), - [['PING']] - ); - - return Promise.resolve(['PONG']); - }); - - multi.ping(); - - assert.deepEqual( - await multi.execAsPipeline(), - ['PONG'] - ); - }); - - it('executing an empty queue should resolve without executing on the server', async () => { - const executor = spy(); - - assert.deepEqual( - await RedisMultiCommand.create(executor).execAsPipeline(), - [] - ); - - assert.ok(executor.notCalled); - }); - - it('with scripts', async () => { - const MultiWithScript = RedisMultiCommand.extend({ - scripts: { - square: SQUARE_SCRIPT - } - }); - - assert.deepEqual( - await new MultiWithScript(queue => { - assert.deepEqual( - queue.map(({ args }) => args), - [ - ['EVAL', SQUARE_SCRIPT.SCRIPT, '0', '2'], - ['EVALSHA', SQUARE_SCRIPT.SHA1, '0', '3'], - ] - ); - - return Promise.resolve([4, 9]); - }).square(2).square(3).execAsPipeline(), - [4, 9] - ); - }); + it('transformReplies', () => { + const multi = new RedisMultiCommand(); + multi.addCommand(['PING'], (reply: string) => reply.substring(0, 2)); + assert.deepEqual( + multi.transformReplies(['PONG']), + ['PO'] + ); }); }); diff --git a/lib/multi-command.ts b/lib/multi-command.ts index a329a5dbf19..d66974a5a21 100644 --- a/lib/multi-command.ts +++ b/lib/multi-command.ts @@ -1,135 +1,36 @@ -import COMMANDS, { TransformArgumentsReply } from './commands'; -import { RedisCommand, RedisModules, RedisReply } from './commands'; -import { RedisLuaScript, RedisLuaScripts } from './lua-script'; -import { RedisClientOptions } from './client'; -import { extendWithModulesAndScripts, extendWithDefaultCommands } from './commander'; +import { RedisCommand, RedisCommandArguments, RedisCommandRawReply, RedisScript } from './commands'; import { WatchError } from './errors'; -type RedisMultiCommandSignature = (...args: Parameters) => RedisMultiCommandType; - -type WithCommands = { - [P in keyof typeof COMMANDS]: RedisMultiCommandSignature<(typeof COMMANDS)[P], M, S> -}; - -type WithModules = { - [P in keyof M]: { - [C in keyof M[P]]: RedisMultiCommandSignature; - }; -}; - -type WithScripts = { - [P in keyof S]: RedisMultiCommandSignature -}; - -export type RedisMultiCommandType = - RedisMultiCommand & WithCommands & WithModules & WithScripts; - -export interface MultiQueuedCommand { - args: TransformArgumentsReply; - preservedArguments?: unknown; +export interface RedisMultiQueuedCommand { + args: RedisCommandArguments; transformReply?: RedisCommand['transformReply']; } -export type RedisMultiExecutor = (queue: Array, chainId?: symbol) => Promise>; - -export default class RedisMultiCommand { - static extend( - clientOptions?: RedisClientOptions - ): new (...args: ConstructorParameters) => RedisMultiCommandType { - return extendWithModulesAndScripts({ - BaseClass: RedisMultiCommand, - modules: clientOptions?.modules, - modulesCommandsExecutor: RedisMultiCommand.prototype.commandsExecutor, - scripts: clientOptions?.scripts, - scriptsExecutor: RedisMultiCommand.prototype.scriptsExecutor - }); - } - - static create( - executor: RedisMultiExecutor, - clientOptions?: RedisClientOptions - ): RedisMultiCommandType { - return new this(executor, clientOptions); - } - - readonly #executor: RedisMultiExecutor; - - readonly #clientOptions: RedisClientOptions | undefined; - - readonly #queue: Array = []; - - readonly #scriptsInUse = new Set(); - - readonly #v4: Record = {}; - - get v4(): Record { - if (!this.#clientOptions?.legacyMode) { - throw new Error('client is not in "legacy mode"'); - } - - return this.#v4; - } - - constructor(executor: RedisMultiExecutor, clientOptions?: RedisClientOptions) { - this.#executor = executor; - this.#clientOptions = clientOptions; - this.#legacyMode(); +export default class RedisMultiCommand { + static generateChainId(): symbol { + return Symbol('RedisMultiCommand Chain Id'); } - #legacyMode(): void { - if (!this.#clientOptions?.legacyMode) return; - - this.#v4.addCommand = this.addCommand.bind(this); - (this as any).addCommand = (...args: Array): this => { - this.#queue.push({ - args: args.flat() as Array - }); - return this; - } - this.#v4.exec = this.exec.bind(this); - (this as any).exec = (callback?: (err: Error | null, replies?: Array) => unknown): void => { - this.#v4.exec() - .then((reply: Array) => { - if (!callback) return; - - callback(null, reply); - }) - .catch((err: Error) => { - if (!callback) { - // this.emit('error', err); - return; - } - - callback(err); - }); - }; - - for (const name of Object.keys(COMMANDS)) { - this.#defineLegacyCommand(name); - } - } + readonly queue: Array = []; - #defineLegacyCommand(name: string): void { - (this as any).#v4[name] = (this as any)[name].bind(this.#v4); - (this as any)[name] = (...args: Array): void => (this as any).addCommand(name, args); - } + readonly scriptsInUse = new Set(); - commandsExecutor(command: RedisCommand, args: Array): this { - return this.addCommand( - command.transformArguments(...args), - command.transformReply - ); + addCommand(args: RedisCommandArguments, transformReply?: RedisCommand['transformReply']): void { + this.queue.push({ + args, + transformReply + }); } - scriptsExecutor(script: RedisLuaScript, args: Array): this { - const transformedArguments: TransformArgumentsReply = []; - if (this.#scriptsInUse.has(script.SHA1)) { + addScript(script: RedisScript, args: Array): RedisCommandArguments { + const transformedArguments: RedisCommandArguments = []; + if (this.scriptsInUse.has(script.SHA1)) { transformedArguments.push( 'EVALSHA', script.SHA1 ); } else { - this.#scriptsInUse.add(script.SHA1); + this.scriptsInUse.add(script.SHA1); transformedArguments.push( 'EVAL', script.SCRIPT @@ -144,62 +45,39 @@ export default class RedisMultiCommand> { - if (execAsPipeline) { - return this.execAsPipeline(); - } else if (!this.#queue.length) { - return []; + exec(): undefined | Array { + if (!this.queue.length) { + return; } - const queue = this.#queue.splice(0), - rawReplies = await this.#executor([ - { args: ['MULTI'] }, - ...queue, - { args: ['EXEC'] } - ], Symbol('[RedisMultiCommand] Chain ID')), - execReply = rawReplies[rawReplies.length - 1] as (null | Array); + return [ + { args: ['MULTI'] }, + ...this.queue, + { args: ['EXEC'] } + ]; + } + handleExecReplies(rawReplies: Array): Array { + const execReply = rawReplies[rawReplies.length - 1] as (null | Array); if (execReply === null) { throw new WatchError(); } - return this.#transformReplies(execReply, queue); + return this.transformReplies(execReply); } - async execAsPipeline(): Promise> { - if (!this.#queue.length) { - return []; - } - - const queue = this.#queue.splice(0); - return this.#transformReplies( - await this.#executor(queue), - queue - ); - } - - #transformReplies(rawReplies: Array, queue: Array): Array { + transformReplies(rawReplies: Array): Array { return rawReplies.map((reply, i) => { - const { transformReply, preservedArguments } = queue[i]; - return transformReply ? transformReply(reply, preservedArguments) : reply; + const { transformReply, args } = this.queue[i]; + return transformReply ? transformReply(reply, args.preserve) : reply; }); } } - -extendWithDefaultCommands(RedisMultiCommand, RedisMultiCommand.prototype.commandsExecutor); diff --git a/lib/test-utils.ts b/lib/test-utils.ts index bc3c0514606..978940ff93d 100644 --- a/lib/test-utils.ts +++ b/lib/test-utils.ts @@ -2,15 +2,13 @@ import { strict as assert } from 'assert'; import RedisClient, { RedisClientOptions, RedisClientType } from './client'; import { execSync, spawn } from 'child_process'; import { once } from 'events'; -import { RedisSocketOptions } from './socket'; import which from 'which'; import { SinonSpy } from 'sinon'; import RedisCluster, { RedisClusterOptions, RedisClusterType } from './cluster'; import { promises as fs } from 'fs'; import { Context as MochaContext } from 'mocha'; import { promiseTimeout } from './utils'; -import { RedisModules } from './commands'; -import { RedisLuaScripts } from './lua-script'; +import { RedisModules, RedisScripts } from './commands'; type RedisVersion = [major: number, minor: number, patch: number]; @@ -54,13 +52,13 @@ export enum TestRedisServers { PASSWORD } -export const TEST_REDIS_SERVERS: Record> = {}; +export const TEST_REDIS_SERVERS: Record> = {}; export enum TestRedisClusters { OPEN } -export const TEST_REDIS_CLUSTERES: Record> = {}; +export const TEST_REDIS_CLUSTERES: Record> = {}; let port = 6379; diff --git a/package-lock.json b/package-lock.json index bb840cc6dde..6a7fa380af7 100644 --- a/package-lock.json +++ b/package-lock.json @@ -16,20 +16,21 @@ }, "devDependencies": { "@istanbuljs/nyc-config-typescript": "^1.0.1", + "@tsconfig/node12": "^1.0.9", "@types/mocha": "^9.0.0", - "@types/node": "^16.9.6", - "@types/sinon": "^10.0.3", + "@types/node": "^16.10.3", + "@types/sinon": "^10.0.4", "@types/which": "^2.0.1", "@types/yallist": "^4.0.1", - "mocha": "^9.1.1", + "mocha": "^9.1.2", "nyc": "^15.1.0", "release-it": "^14.11.6", "sinon": "^11.1.2", "source-map-support": "^0.5.20", - "ts-node": "^10.2.1", - "typedoc": "0.21.9", - "typedoc-github-wiki-theme": "^0.5.1", - "typedoc-plugin-markdown": "3.10.4", + "ts-node": "^10.3.0", + "typedoc": "^0.22.5", + "typedoc-github-wiki-theme": "^0.6.0", + "typedoc-plugin-markdown": "^3.11.3", "typescript": "^4.4.3", "which": "^2.0.2" }, @@ -38,9 +39,9 @@ } }, "node_modules/@babel/code-frame": { - "version": "7.14.5", - "resolved": "https://registry.npmjs.org/@babel/code-frame/-/code-frame-7.14.5.tgz", - "integrity": "sha512-9pzDqyc6OLDaqe+zbACgFkb6fKMNG6CObKpnYXChRsvYGyEdc7CA2BaqeOM+vOtCS5ndmJicPJhKAwYRI6UfFw==", + "version": "7.15.8", + "resolved": "https://registry.npmjs.org/@babel/code-frame/-/code-frame-7.15.8.tgz", + "integrity": "sha512-2IAnmn8zbvC/jKYhq5Ki9I+DwjlrtMPUCH/CpHvqI4dNnlwHwsxoIhlc8WcYY5LSYknXQtAlFYuHfqAFCvQ4Wg==", "dev": true, "dependencies": { "@babel/highlight": "^7.14.5" @@ -59,20 +60,20 @@ } }, "node_modules/@babel/core": { - "version": "7.15.5", - "resolved": "https://registry.npmjs.org/@babel/core/-/core-7.15.5.tgz", - "integrity": "sha512-pYgXxiwAgQpgM1bNkZsDEq85f0ggXMA5L7c+o3tskGMh2BunCI9QUwB9Z4jpvXUOuMdyGKiGKQiRe11VS6Jzvg==", + "version": "7.15.8", + "resolved": "https://registry.npmjs.org/@babel/core/-/core-7.15.8.tgz", + "integrity": "sha512-3UG9dsxvYBMYwRv+gS41WKHno4K60/9GPy1CJaH6xy3Elq8CTtvtjT5R5jmNhXfCYLX2mTw+7/aq5ak/gOE0og==", "dev": true, "dependencies": { - "@babel/code-frame": "^7.14.5", - "@babel/generator": "^7.15.4", + "@babel/code-frame": "^7.15.8", + "@babel/generator": "^7.15.8", "@babel/helper-compilation-targets": "^7.15.4", - "@babel/helper-module-transforms": "^7.15.4", + "@babel/helper-module-transforms": "^7.15.8", "@babel/helpers": "^7.15.4", - "@babel/parser": "^7.15.5", + "@babel/parser": "^7.15.8", "@babel/template": "^7.15.4", "@babel/traverse": "^7.15.4", - "@babel/types": "^7.15.4", + "@babel/types": "^7.15.6", "convert-source-map": "^1.7.0", "debug": "^4.1.0", "gensync": "^1.0.0-beta.2", @@ -89,12 +90,12 @@ } }, "node_modules/@babel/generator": { - "version": "7.15.4", - "resolved": "https://registry.npmjs.org/@babel/generator/-/generator-7.15.4.tgz", - "integrity": "sha512-d3itta0tu+UayjEORPNz6e1T3FtvWlP5N4V5M+lhp/CxT4oAA7/NcScnpRyspUMLK6tu9MNHmQHxRykuN2R7hw==", + "version": "7.15.8", + "resolved": "https://registry.npmjs.org/@babel/generator/-/generator-7.15.8.tgz", + "integrity": "sha512-ECmAKstXbp1cvpTTZciZCgfOt6iN64lR0d+euv3UZisU5awfRawOvg07Utn/qBGuH4bRIEZKrA/4LzZyXhZr8g==", "dev": true, "dependencies": { - "@babel/types": "^7.15.4", + "@babel/types": "^7.15.6", "jsesc": "^2.5.1", "source-map": "^0.5.0" }, @@ -183,9 +184,9 @@ } }, "node_modules/@babel/helper-module-transforms": { - "version": "7.15.7", - "resolved": "https://registry.npmjs.org/@babel/helper-module-transforms/-/helper-module-transforms-7.15.7.tgz", - "integrity": "sha512-ZNqjjQG/AuFfekFTY+7nY4RgBSklgTu970c7Rj3m/JOhIu5KPBUuTA9AY6zaKcUvk4g6EbDXdBnhi35FAssdSw==", + "version": "7.15.8", + "resolved": "https://registry.npmjs.org/@babel/helper-module-transforms/-/helper-module-transforms-7.15.8.tgz", + "integrity": "sha512-DfAfA6PfpG8t4S6npwzLvTUpp0sS7JrcuaMiy1Y5645laRJIp/LiLGIBbQKaXSInK8tiGNI7FL7L8UvB8gdUZg==", "dev": true, "dependencies": { "@babel/helper-module-imports": "^7.15.4", @@ -370,9 +371,9 @@ } }, "node_modules/@babel/parser": { - "version": "7.15.7", - "resolved": "https://registry.npmjs.org/@babel/parser/-/parser-7.15.7.tgz", - "integrity": "sha512-rycZXvQ+xS9QyIcJ9HXeDWf1uxqlbVFAUq0Rq0dbc50Zb/+wUe/ehyfzGfm9KZZF0kBejYgxltBXocP+gKdL2g==", + "version": "7.15.8", + "resolved": "https://registry.npmjs.org/@babel/parser/-/parser-7.15.8.tgz", + "integrity": "sha512-BRYa3wcQnjS/nqI8Ac94pYYpJfojHVvVXJ97+IDCImX4Jc8W8Xv1+47enbruk+q1etOpsQNwnfFcNGw+gtPGxA==", "dev": true, "bin": { "parser": "bin/babel-parser.js" @@ -438,9 +439,9 @@ } }, "node_modules/@cspotcode/source-map-support": { - "version": "0.6.1", - "resolved": "https://registry.npmjs.org/@cspotcode/source-map-support/-/source-map-support-0.6.1.tgz", - "integrity": "sha512-DX3Z+T5dt1ockmPdobJS/FAsQPW4V4SrWEhD2iYQT2Cb2tQsiMnYxrcUH9By/Z3B+v0S5LMBkQtV/XOBbpLEOg==", + "version": "0.7.0", + "resolved": "https://registry.npmjs.org/@cspotcode/source-map-support/-/source-map-support-0.7.0.tgz", + "integrity": "sha512-X4xqRHqN8ACt2aHVe51OxeA2HjbcL4MqFqXkrmQszJ1NOUuUu5u6Vqx/0lZSVNku7velL5FC/s5uEAj1lsBMhA==", "dev": true, "dependencies": { "@cspotcode/source-map-consumer": "0.8.0" @@ -653,18 +654,18 @@ } }, "node_modules/@octokit/openapi-types": { - "version": "10.6.1", - "resolved": "https://registry.npmjs.org/@octokit/openapi-types/-/openapi-types-10.6.1.tgz", - "integrity": "sha512-53YKy8w8+sHQhUONhTiYt6MqNqPolejYr6rK/3VOevpORAIYGQEX2pmXnnhgdSsjHy176e5ZBgVt0ppOGziS7g==", + "version": "11.2.0", + "resolved": "https://registry.npmjs.org/@octokit/openapi-types/-/openapi-types-11.2.0.tgz", + "integrity": "sha512-PBsVO+15KSlGmiI8QAzaqvsNlZlrDlyAJYcrXBCvVUxCp7VnXjkwPoFHgjEJXx3WF9BAwkA6nfCUA7i9sODzKA==", "dev": true }, "node_modules/@octokit/plugin-paginate-rest": { - "version": "2.16.5", - "resolved": "https://registry.npmjs.org/@octokit/plugin-paginate-rest/-/plugin-paginate-rest-2.16.5.tgz", - "integrity": "sha512-2PfRGymdBypqRes4Xelu0BAZZRCV/Qg0xgo8UB10UKoghCM+zg640+T5WkRsRD0edwfLBPP3VsJgDyDTG4EIYg==", + "version": "2.17.0", + "resolved": "https://registry.npmjs.org/@octokit/plugin-paginate-rest/-/plugin-paginate-rest-2.17.0.tgz", + "integrity": "sha512-tzMbrbnam2Mt4AhuyCHvpRkS0oZ5MvwwcQPYGtMv4tUa5kkzG58SVB0fcsLulOZQeRnOgdkZWkRUiyBlh0Bkyw==", "dev": true, "dependencies": { - "@octokit/types": "^6.31.0" + "@octokit/types": "^6.34.0" }, "peerDependencies": { "@octokit/core": ">=2" @@ -680,12 +681,12 @@ } }, "node_modules/@octokit/plugin-rest-endpoint-methods": { - "version": "5.11.3", - "resolved": "https://registry.npmjs.org/@octokit/plugin-rest-endpoint-methods/-/plugin-rest-endpoint-methods-5.11.3.tgz", - "integrity": "sha512-E19gqHqfP3uJa2/hx6Abhx2NrVP5tsNbst2/AeqGxlGM+eL4N8fRbzhd+NEIsGAB4y3R7e9kVE0y8OOghlXUXw==", + "version": "5.13.0", + "resolved": "https://registry.npmjs.org/@octokit/plugin-rest-endpoint-methods/-/plugin-rest-endpoint-methods-5.13.0.tgz", + "integrity": "sha512-uJjMTkN1KaOIgNtUPMtIXDOjx6dGYysdIFhgA52x4xSadQCz3b/zJexvITDVpANnfKPW/+E0xkOvLntqMYpviA==", "dev": true, "dependencies": { - "@octokit/types": "^6.31.1", + "@octokit/types": "^6.34.0", "deprecation": "^2.3.1" }, "peerDependencies": { @@ -693,9 +694,9 @@ } }, "node_modules/@octokit/request": { - "version": "5.6.1", - "resolved": "https://registry.npmjs.org/@octokit/request/-/request-5.6.1.tgz", - "integrity": "sha512-Ls2cfs1OfXaOKzkcxnqw5MR6drMA/zWX/LIS/p8Yjdz7QKTPQLMsB3R+OvoxE6XnXeXEE2X7xe4G4l4X0gRiKQ==", + "version": "5.6.2", + "resolved": "https://registry.npmjs.org/@octokit/request/-/request-5.6.2.tgz", + "integrity": "sha512-je66CvSEVf0jCpRISxkUcCa0UkxmFs6eGDRSbfJtAVwbLH5ceqF+YEyC8lj8ystKyZTy8adWr0qmkY52EfOeLA==", "dev": true, "dependencies": { "@octokit/endpoint": "^6.0.1", @@ -730,12 +731,12 @@ } }, "node_modules/@octokit/types": { - "version": "6.31.1", - "resolved": "https://registry.npmjs.org/@octokit/types/-/types-6.31.1.tgz", - "integrity": "sha512-xkF46eaYcpT8ieO78mZWhMq3bt37zIsP5BUkN+zWgX+mTYDB7jOtUP1MOxcSF8hhJhsjjlB1YDgQAhX0z0oqPw==", + "version": "6.34.0", + "resolved": "https://registry.npmjs.org/@octokit/types/-/types-6.34.0.tgz", + "integrity": "sha512-s1zLBjWhdEI2zwaoSgyOFoKSl109CUcVBCc7biPJ3aAf6LGLU6szDvi31JPU7bxfla2lqfhjbbg/5DdFNxOwHw==", "dev": true, "dependencies": { - "@octokit/openapi-types": "^10.6.1" + "@octokit/openapi-types": "^11.2.0" } }, "node_modules/@sindresorhus/is": { @@ -855,9 +856,9 @@ "dev": true }, "node_modules/@types/node": { - "version": "16.10.2", - "resolved": "https://registry.npmjs.org/@types/node/-/node-16.10.2.tgz", - "integrity": "sha512-zCclL4/rx+W5SQTzFs9wyvvyCwoK9QtBpratqz2IYJ3O8Umrn0m3nsTv0wQBk9sRGpvUe9CwPDrQFB10f1FIjQ==", + "version": "16.10.3", + "resolved": "https://registry.npmjs.org/@types/node/-/node-16.10.3.tgz", + "integrity": "sha512-ho3Ruq+fFnBrZhUYI46n/bV2GjwzSkwuT4dTf0GkuNFmnb8nq4ny2z9JEVemFi6bdEJanHLlYfy9c6FN9B9McQ==", "dev": true }, "node_modules/@types/parse-json": { @@ -1199,16 +1200,16 @@ "dev": true }, "node_modules/browserslist": { - "version": "4.17.1", - "resolved": "https://registry.npmjs.org/browserslist/-/browserslist-4.17.1.tgz", - "integrity": "sha512-aLD0ZMDSnF4lUt4ZDNgqi5BUn9BZ7YdQdI/cYlILrhdSSZJLU9aNZoD5/NBmM4SK34APB2e83MOsRt1EnkuyaQ==", + "version": "4.17.3", + "resolved": "https://registry.npmjs.org/browserslist/-/browserslist-4.17.3.tgz", + "integrity": "sha512-59IqHJV5VGdcJZ+GZ2hU5n4Kv3YiASzW6Xk5g9tf5a/MAzGeFwgGWU39fVzNIOVcgB3+Gp+kiQu0HEfTVU/3VQ==", "dev": true, "dependencies": { - "caniuse-lite": "^1.0.30001259", - "electron-to-chromium": "^1.3.846", + "caniuse-lite": "^1.0.30001264", + "electron-to-chromium": "^1.3.857", "escalade": "^3.1.1", - "nanocolors": "^0.1.5", - "node-releases": "^1.1.76" + "node-releases": "^1.1.77", + "picocolors": "^0.2.1" }, "bin": { "browserslist": "cli.js" @@ -1340,9 +1341,9 @@ } }, "node_modules/caniuse-lite": { - "version": "1.0.30001261", - "resolved": "https://registry.npmjs.org/caniuse-lite/-/caniuse-lite-1.0.30001261.tgz", - "integrity": "sha512-vM8D9Uvp7bHIN0fZ2KQ4wnmYFpJo/Etb4Vwsuc+ka0tfGDHvOPrFm6S/7CCNLSOkAUjenT2HnUPESdOIL91FaA==", + "version": "1.0.30001265", + "resolved": "https://registry.npmjs.org/caniuse-lite/-/caniuse-lite-1.0.30001265.tgz", + "integrity": "sha512-YzBnspggWV5hep1m9Z6sZVLOt7vrju8xWooFAgN6BA5qvy98qPAPb7vNUzypFaoh2pb3vlfzbDO8tB57UPGbtw==", "dev": true, "funding": { "type": "opencollective", @@ -1444,9 +1445,9 @@ } }, "node_modules/cli-spinners": { - "version": "2.6.0", - "resolved": "https://registry.npmjs.org/cli-spinners/-/cli-spinners-2.6.0.tgz", - "integrity": "sha512-t+4/y50K/+4xcCRosKkA7W4gTr1MySvLV0q+PxmG7FJ5g+66ChKurYjxBCjHggHH3HA5Hh9cy+lcUGWDqVH+4Q==", + "version": "2.6.1", + "resolved": "https://registry.npmjs.org/cli-spinners/-/cli-spinners-2.6.1.tgz", + "integrity": "sha512-x/5fWmGMnbKQAaNwN+UZlV79qBLM9JFnJuJ03gIi5whrob0xV0ofNVHy9DhwGdsMJQc2OKv0oGmLzvaqvAVv+g==", "dev": true, "engines": { "node": ">=6" @@ -1789,9 +1790,9 @@ "dev": true }, "node_modules/electron-to-chromium": { - "version": "1.3.854", - "resolved": "https://registry.npmjs.org/electron-to-chromium/-/electron-to-chromium-1.3.854.tgz", - "integrity": "sha512-00/IIC1mFPkq32MhUJyLdcTp7+wsKK2G3Sb65GSas9FKJQGYkDcZ4GwJkkxf5YyM3ETvl6n+toV8OmtXl4IA/g==", + "version": "1.3.864", + "resolved": "https://registry.npmjs.org/electron-to-chromium/-/electron-to-chromium-1.3.864.tgz", + "integrity": "sha512-v4rbad8GO6/yVI92WOeU9Wgxc4NA0n4f6P1FvZTY+jyY7JHEhw3bduYu60v3Q1h81Cg6eo4ApZrFPuycwd5hGw==", "dev": true }, "node_modules/emoji-regex": { @@ -2840,9 +2841,9 @@ "dev": true }, "node_modules/istanbul-lib-coverage": { - "version": "3.0.1", - "resolved": "https://registry.npmjs.org/istanbul-lib-coverage/-/istanbul-lib-coverage-3.0.1.tgz", - "integrity": "sha512-GvCYYTxaCPqwMjobtVcVKvSHtAGe48MNhGjpK8LtVF8K0ISX7hCKl85LgtuaSneWVyQmaGcW3iXVV3GaZSLpmQ==", + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/istanbul-lib-coverage/-/istanbul-lib-coverage-3.0.0.tgz", + "integrity": "sha512-UiUIqxMgRDET6eR+o5HbfRYP1l0hqkWOs7vNxC/mggutCMUIhWMm8gAHb8tHlyfD3/l6rlgNA5cKdDzEAf6hEg==", "dev": true, "engines": { "node": ">=8" @@ -2943,9 +2944,9 @@ } }, "node_modules/istanbul-reports": { - "version": "3.0.2", - "resolved": "https://registry.npmjs.org/istanbul-reports/-/istanbul-reports-3.0.2.tgz", - "integrity": "sha512-9tZvz7AiR3PEDNGiV9vIouQ/EAcqMXFmkcA1CDFTwOB98OZVDL0PH9glHotf5Ugp6GCOTypfzGWI/OqjWNCRUw==", + "version": "3.0.3", + "resolved": "https://registry.npmjs.org/istanbul-reports/-/istanbul-reports-3.0.3.tgz", + "integrity": "sha512-0i77ZFLsb9U3DHi22WzmIngVzfoyxxbQcZRqlF3KoKmCJGq9nhFHoGi8FqBztN2rE8w6hURnZghetn0xpkVb6A==", "dev": true, "dependencies": { "html-escaper": "^2.0.0", @@ -3164,9 +3165,9 @@ "dev": true }, "node_modules/marked": { - "version": "3.0.4", - "resolved": "https://registry.npmjs.org/marked/-/marked-3.0.4.tgz", - "integrity": "sha512-jBo8AOayNaEcvBhNobg6/BLhdsK3NvnKWJg33MAAPbvTWiG4QBn9gpW1+7RssrKu4K1dKlN+0goVQwV41xEfOA==", + "version": "3.0.7", + "resolved": "https://registry.npmjs.org/marked/-/marked-3.0.7.tgz", + "integrity": "sha512-ctKqbnLuNbsHbI26cfMyOlKgXGfl1orOv1AvWWDX7AkgfMOwCWvmuYc+mVLeWhQ9W6hdWVBynOs96VkcscKo0Q==", "dev": true, "bin": { "marked": "bin/marked" @@ -3315,12 +3316,6 @@ "integrity": "sha512-nnbWWOkoWyUsTjKrhgD0dcz22mdkSnpYqbEjIm2nhwhuxlSkpywJmBo8h0ZqJdkp73mb90SssHkN4rsRaBAfAA==", "dev": true }, - "node_modules/nanocolors": { - "version": "0.1.12", - "resolved": "https://registry.npmjs.org/nanocolors/-/nanocolors-0.1.12.tgz", - "integrity": "sha512-2nMHqg1x5PU+unxX7PGY7AuYxl2qDx7PSrTRjizr8sxdd3l/3hBuWWaki62qmtYm2U5i4Z5E7GbjlyDFhs9/EQ==", - "dev": true - }, "node_modules/nanoid": { "version": "3.1.25", "resolved": "https://registry.npmjs.org/nanoid/-/nanoid-3.1.25.tgz", @@ -3398,9 +3393,9 @@ } }, "node_modules/node-releases": { - "version": "1.1.76", - "resolved": "https://registry.npmjs.org/node-releases/-/node-releases-1.1.76.tgz", - "integrity": "sha512-9/IECtNr8dXNmPWmFXepT0/7o5eolGesHUa3mtr0KlgnCvnZxwh2qensKL42JJY2vQKC3nIBXetFAqR+PW1CmA==", + "version": "1.1.77", + "resolved": "https://registry.npmjs.org/node-releases/-/node-releases-1.1.77.tgz", + "integrity": "sha512-rB1DUFUNAN4Gn9keO2K1efO35IDK7yKHCdCaIMvFO7yUYmmZYeDjnGKle26G4rwj+LKRQpjyUUvMkPglwGCYNQ==", "dev": true }, "node_modules/normalize-path": { @@ -4062,6 +4057,12 @@ "node": ">=8" } }, + "node_modules/picocolors": { + "version": "0.2.1", + "resolved": "https://registry.npmjs.org/picocolors/-/picocolors-0.2.1.tgz", + "integrity": "sha512-cMlDqaLEqfSaW8Z7N5Jw+lyIW869EzT73/F5lhtY9cLGoVxSXznfgfXMO0Z5K0o0Q2TkTXq+0KFsdnSe3jDViA==", + "dev": true + }, "node_modules/picomatch": { "version": "2.3.0", "resolved": "https://registry.npmjs.org/picomatch/-/picomatch-2.3.0.tgz", @@ -4159,15 +4160,6 @@ "node": ">=8" } }, - "node_modules/progress": { - "version": "2.0.3", - "resolved": "https://registry.npmjs.org/progress/-/progress-2.0.3.tgz", - "integrity": "sha512-7PiHtLll5LdnKIMw100I+8xJXR5gW2QwWYkT6iJva0bXitZKa/XMrSbdmg3r2Xnaidz9Qumd0VPaMrZlF9V9sA==", - "dev": true, - "engines": { - "node": ">=0.4.0" - } - }, "node_modules/protocols": { "version": "1.4.8", "resolved": "https://registry.npmjs.org/protocols/-/protocols-1.4.8.tgz", @@ -4613,9 +4605,9 @@ } }, "node_modules/rxjs": { - "version": "7.3.0", - "resolved": "https://registry.npmjs.org/rxjs/-/rxjs-7.3.0.tgz", - "integrity": "sha512-p2yuGIg9S1epc3vrjKf6iVb3RCaAYjYskkO+jHIaV0IjOPlJop4UnodOoFb2xeNwlguqLYvGw1b1McillYb5Gw==", + "version": "7.4.0", + "resolved": "https://registry.npmjs.org/rxjs/-/rxjs-7.4.0.tgz", + "integrity": "sha512-7SQDi7xeTMCJpqViXh8gL/lebcwlp3d831F05+9B44A4B0WfsEwUQHR64gsH1kvJ+Ep/J9K2+n1hVl1CsGN23w==", "dev": true, "dependencies": { "tslib": "~2.1.0" @@ -5015,12 +5007,12 @@ "dev": true }, "node_modules/ts-node": { - "version": "10.2.1", - "resolved": "https://registry.npmjs.org/ts-node/-/ts-node-10.2.1.tgz", - "integrity": "sha512-hCnyOyuGmD5wHleOQX6NIjJtYVIO8bPP8F2acWkB4W06wdlkgyvJtubO/I9NkI88hCFECbsEgoLc0VNkYmcSfw==", + "version": "10.3.0", + "resolved": "https://registry.npmjs.org/ts-node/-/ts-node-10.3.0.tgz", + "integrity": "sha512-RYIy3i8IgpFH45AX4fQHExrT8BxDeKTdC83QFJkNzkvt8uFB6QJ8XMyhynYiKMLxt9a7yuXaDBZNOYS3XjDcYw==", "dev": true, "dependencies": { - "@cspotcode/source-map-support": "0.6.1", + "@cspotcode/source-map-support": "0.7.0", "@tsconfig/node10": "^1.0.7", "@tsconfig/node12": "^1.0.7", "@tsconfig/node14": "^1.0.0", @@ -5040,9 +5032,6 @@ "ts-node-transpile-only": "dist/bin-transpile.js", "ts-script": "dist/bin-script-deprecated.js" }, - "engines": { - "node": ">=12.0.0" - }, "peerDependencies": { "@swc/core": ">=1.2.50", "@swc/wasm": ">=1.2.50", @@ -5101,19 +5090,16 @@ } }, "node_modules/typedoc": { - "version": "0.21.9", - "resolved": "https://registry.npmjs.org/typedoc/-/typedoc-0.21.9.tgz", - "integrity": "sha512-VRo7aII4bnYaBBM1lhw4bQFmUcDQV8m8tqgjtc7oXl87jc1Slbhfw2X5MccfcR2YnEClHDWgsiQGgNB8KJXocA==", + "version": "0.22.5", + "resolved": "https://registry.npmjs.org/typedoc/-/typedoc-0.22.5.tgz", + "integrity": "sha512-KFrWGU1iKiTGw0RcyjLNYDmhd7uICU14HgBNPmFKY/sT4Pm/fraaLyWyisst9vGTUAKxqibqoDITR7+ZcAkhHg==", "dev": true, "dependencies": { - "glob": "^7.1.7", - "handlebars": "^4.7.7", + "glob": "^7.2.0", "lunr": "^2.3.9", - "marked": "^3.0.2", - "minimatch": "^3.0.0", - "progress": "^2.0.3", - "shiki": "^0.9.8", - "typedoc-default-themes": "^0.12.10" + "marked": "^3.0.4", + "minimatch": "^3.0.4", + "shiki": "^0.9.11" }, "bin": { "typedoc": "bin/typedoc" @@ -5125,35 +5111,46 @@ "typescript": "4.0.x || 4.1.x || 4.2.x || 4.3.x || 4.4.x" } }, - "node_modules/typedoc-default-themes": { - "version": "0.12.10", - "resolved": "https://registry.npmjs.org/typedoc-default-themes/-/typedoc-default-themes-0.12.10.tgz", - "integrity": "sha512-fIS001cAYHkyQPidWXmHuhs8usjP5XVJjWB8oZGqkTowZaz3v7g3KDZeeqE82FBrmkAnIBOY3jgy7lnPnqATbA==", - "dev": true, - "engines": { - "node": ">= 8" - } - }, "node_modules/typedoc-github-wiki-theme": { - "version": "0.5.1", - "resolved": "https://registry.npmjs.org/typedoc-github-wiki-theme/-/typedoc-github-wiki-theme-0.5.1.tgz", - "integrity": "sha512-ED2Uc3WUjbv6xIdCkpMz3yBtcEciJnAhDQdRWLYgw4K+FKY0T3PzbI+ssNsBVgwDnYQP/XuaqfZkeQ3EQsOm9g==", + "version": "0.6.0", + "resolved": "https://registry.npmjs.org/typedoc-github-wiki-theme/-/typedoc-github-wiki-theme-0.6.0.tgz", + "integrity": "sha512-uHhR7PwAZ4JgO0KzlLocWSvMqKsSZ/wxUQYGKskIepzsotPAQcAWnSSnGi6gdkSw8UAfIIppdD7H1AmI39962w==", "dev": true, "peerDependencies": { - "typedoc": ">=0.20.0", - "typedoc-plugin-markdown": ">=3.4.0" + "typedoc": ">=0.22.0", + "typedoc-plugin-markdown": ">=3.11.0" } }, "node_modules/typedoc-plugin-markdown": { - "version": "3.10.4", - "resolved": "https://registry.npmjs.org/typedoc-plugin-markdown/-/typedoc-plugin-markdown-3.10.4.tgz", - "integrity": "sha512-if9w7S9fXLg73AYi/EoRSEhTOZlg3I8mIP8YEmvzSE33VrNXC9/hA0nVcLEwFVJeQY7ay6z67I6kW0KIv7LjeA==", + "version": "3.11.3", + "resolved": "https://registry.npmjs.org/typedoc-plugin-markdown/-/typedoc-plugin-markdown-3.11.3.tgz", + "integrity": "sha512-rWiHbEIe0oZetDIsBR24XJVxGOJ91kDcHoj2KhFKxCLoJGX659EKBQkHne9QJ4W2stGhu1fRgFyQaouSBnxukA==", "dev": true, "dependencies": { "handlebars": "^4.7.7" }, "peerDependencies": { - "typedoc": ">=0.21.2" + "typedoc": ">=0.22.0" + } + }, + "node_modules/typedoc/node_modules/glob": { + "version": "7.2.0", + "resolved": "https://registry.npmjs.org/glob/-/glob-7.2.0.tgz", + "integrity": "sha512-lmLf6gtyrPq8tTjSmrO94wBeQbFR3HbLHbuyD69wuyQkImp2hWqMGB47OX65FBkPffO641IP9jWa1z4ivqG26Q==", + "dev": true, + "dependencies": { + "fs.realpath": "^1.0.0", + "inflight": "^1.0.4", + "inherits": "2", + "minimatch": "^3.0.4", + "once": "^1.3.0", + "path-is-absolute": "^1.0.0" + }, + "engines": { + "node": "*" + }, + "funding": { + "url": "https://github.com/sponsors/isaacs" } }, "node_modules/typescript": { @@ -5602,9 +5599,9 @@ }, "dependencies": { "@babel/code-frame": { - "version": "7.14.5", - "resolved": "https://registry.npmjs.org/@babel/code-frame/-/code-frame-7.14.5.tgz", - "integrity": "sha512-9pzDqyc6OLDaqe+zbACgFkb6fKMNG6CObKpnYXChRsvYGyEdc7CA2BaqeOM+vOtCS5ndmJicPJhKAwYRI6UfFw==", + "version": "7.15.8", + "resolved": "https://registry.npmjs.org/@babel/code-frame/-/code-frame-7.15.8.tgz", + "integrity": "sha512-2IAnmn8zbvC/jKYhq5Ki9I+DwjlrtMPUCH/CpHvqI4dNnlwHwsxoIhlc8WcYY5LSYknXQtAlFYuHfqAFCvQ4Wg==", "dev": true, "requires": { "@babel/highlight": "^7.14.5" @@ -5617,20 +5614,20 @@ "dev": true }, "@babel/core": { - "version": "7.15.5", - "resolved": "https://registry.npmjs.org/@babel/core/-/core-7.15.5.tgz", - "integrity": "sha512-pYgXxiwAgQpgM1bNkZsDEq85f0ggXMA5L7c+o3tskGMh2BunCI9QUwB9Z4jpvXUOuMdyGKiGKQiRe11VS6Jzvg==", + "version": "7.15.8", + "resolved": "https://registry.npmjs.org/@babel/core/-/core-7.15.8.tgz", + "integrity": "sha512-3UG9dsxvYBMYwRv+gS41WKHno4K60/9GPy1CJaH6xy3Elq8CTtvtjT5R5jmNhXfCYLX2mTw+7/aq5ak/gOE0og==", "dev": true, "requires": { - "@babel/code-frame": "^7.14.5", - "@babel/generator": "^7.15.4", + "@babel/code-frame": "^7.15.8", + "@babel/generator": "^7.15.8", "@babel/helper-compilation-targets": "^7.15.4", - "@babel/helper-module-transforms": "^7.15.4", + "@babel/helper-module-transforms": "^7.15.8", "@babel/helpers": "^7.15.4", - "@babel/parser": "^7.15.5", + "@babel/parser": "^7.15.8", "@babel/template": "^7.15.4", "@babel/traverse": "^7.15.4", - "@babel/types": "^7.15.4", + "@babel/types": "^7.15.6", "convert-source-map": "^1.7.0", "debug": "^4.1.0", "gensync": "^1.0.0-beta.2", @@ -5640,12 +5637,12 @@ } }, "@babel/generator": { - "version": "7.15.4", - "resolved": "https://registry.npmjs.org/@babel/generator/-/generator-7.15.4.tgz", - "integrity": "sha512-d3itta0tu+UayjEORPNz6e1T3FtvWlP5N4V5M+lhp/CxT4oAA7/NcScnpRyspUMLK6tu9MNHmQHxRykuN2R7hw==", + "version": "7.15.8", + "resolved": "https://registry.npmjs.org/@babel/generator/-/generator-7.15.8.tgz", + "integrity": "sha512-ECmAKstXbp1cvpTTZciZCgfOt6iN64lR0d+euv3UZisU5awfRawOvg07Utn/qBGuH4bRIEZKrA/4LzZyXhZr8g==", "dev": true, "requires": { - "@babel/types": "^7.15.4", + "@babel/types": "^7.15.6", "jsesc": "^2.5.1", "source-map": "^0.5.0" } @@ -5710,9 +5707,9 @@ } }, "@babel/helper-module-transforms": { - "version": "7.15.7", - "resolved": "https://registry.npmjs.org/@babel/helper-module-transforms/-/helper-module-transforms-7.15.7.tgz", - "integrity": "sha512-ZNqjjQG/AuFfekFTY+7nY4RgBSklgTu970c7Rj3m/JOhIu5KPBUuTA9AY6zaKcUvk4g6EbDXdBnhi35FAssdSw==", + "version": "7.15.8", + "resolved": "https://registry.npmjs.org/@babel/helper-module-transforms/-/helper-module-transforms-7.15.8.tgz", + "integrity": "sha512-DfAfA6PfpG8t4S6npwzLvTUpp0sS7JrcuaMiy1Y5645laRJIp/LiLGIBbQKaXSInK8tiGNI7FL7L8UvB8gdUZg==", "dev": true, "requires": { "@babel/helper-module-imports": "^7.15.4", @@ -5857,9 +5854,9 @@ } }, "@babel/parser": { - "version": "7.15.7", - "resolved": "https://registry.npmjs.org/@babel/parser/-/parser-7.15.7.tgz", - "integrity": "sha512-rycZXvQ+xS9QyIcJ9HXeDWf1uxqlbVFAUq0Rq0dbc50Zb/+wUe/ehyfzGfm9KZZF0kBejYgxltBXocP+gKdL2g==", + "version": "7.15.8", + "resolved": "https://registry.npmjs.org/@babel/parser/-/parser-7.15.8.tgz", + "integrity": "sha512-BRYa3wcQnjS/nqI8Ac94pYYpJfojHVvVXJ97+IDCImX4Jc8W8Xv1+47enbruk+q1etOpsQNwnfFcNGw+gtPGxA==", "dev": true }, "@babel/template": { @@ -5907,9 +5904,9 @@ "dev": true }, "@cspotcode/source-map-support": { - "version": "0.6.1", - "resolved": "https://registry.npmjs.org/@cspotcode/source-map-support/-/source-map-support-0.6.1.tgz", - "integrity": "sha512-DX3Z+T5dt1ockmPdobJS/FAsQPW4V4SrWEhD2iYQT2Cb2tQsiMnYxrcUH9By/Z3B+v0S5LMBkQtV/XOBbpLEOg==", + "version": "0.7.0", + "resolved": "https://registry.npmjs.org/@cspotcode/source-map-support/-/source-map-support-0.7.0.tgz", + "integrity": "sha512-X4xqRHqN8ACt2aHVe51OxeA2HjbcL4MqFqXkrmQszJ1NOUuUu5u6Vqx/0lZSVNku7velL5FC/s5uEAj1lsBMhA==", "dev": true, "requires": { "@cspotcode/source-map-consumer": "0.8.0" @@ -6080,18 +6077,18 @@ } }, "@octokit/openapi-types": { - "version": "10.6.1", - "resolved": "https://registry.npmjs.org/@octokit/openapi-types/-/openapi-types-10.6.1.tgz", - "integrity": "sha512-53YKy8w8+sHQhUONhTiYt6MqNqPolejYr6rK/3VOevpORAIYGQEX2pmXnnhgdSsjHy176e5ZBgVt0ppOGziS7g==", + "version": "11.2.0", + "resolved": "https://registry.npmjs.org/@octokit/openapi-types/-/openapi-types-11.2.0.tgz", + "integrity": "sha512-PBsVO+15KSlGmiI8QAzaqvsNlZlrDlyAJYcrXBCvVUxCp7VnXjkwPoFHgjEJXx3WF9BAwkA6nfCUA7i9sODzKA==", "dev": true }, "@octokit/plugin-paginate-rest": { - "version": "2.16.5", - "resolved": "https://registry.npmjs.org/@octokit/plugin-paginate-rest/-/plugin-paginate-rest-2.16.5.tgz", - "integrity": "sha512-2PfRGymdBypqRes4Xelu0BAZZRCV/Qg0xgo8UB10UKoghCM+zg640+T5WkRsRD0edwfLBPP3VsJgDyDTG4EIYg==", + "version": "2.17.0", + "resolved": "https://registry.npmjs.org/@octokit/plugin-paginate-rest/-/plugin-paginate-rest-2.17.0.tgz", + "integrity": "sha512-tzMbrbnam2Mt4AhuyCHvpRkS0oZ5MvwwcQPYGtMv4tUa5kkzG58SVB0fcsLulOZQeRnOgdkZWkRUiyBlh0Bkyw==", "dev": true, "requires": { - "@octokit/types": "^6.31.0" + "@octokit/types": "^6.34.0" } }, "@octokit/plugin-request-log": { @@ -6102,19 +6099,19 @@ "requires": {} }, "@octokit/plugin-rest-endpoint-methods": { - "version": "5.11.3", - "resolved": "https://registry.npmjs.org/@octokit/plugin-rest-endpoint-methods/-/plugin-rest-endpoint-methods-5.11.3.tgz", - "integrity": "sha512-E19gqHqfP3uJa2/hx6Abhx2NrVP5tsNbst2/AeqGxlGM+eL4N8fRbzhd+NEIsGAB4y3R7e9kVE0y8OOghlXUXw==", + "version": "5.13.0", + "resolved": "https://registry.npmjs.org/@octokit/plugin-rest-endpoint-methods/-/plugin-rest-endpoint-methods-5.13.0.tgz", + "integrity": "sha512-uJjMTkN1KaOIgNtUPMtIXDOjx6dGYysdIFhgA52x4xSadQCz3b/zJexvITDVpANnfKPW/+E0xkOvLntqMYpviA==", "dev": true, "requires": { - "@octokit/types": "^6.31.1", + "@octokit/types": "^6.34.0", "deprecation": "^2.3.1" } }, "@octokit/request": { - "version": "5.6.1", - "resolved": "https://registry.npmjs.org/@octokit/request/-/request-5.6.1.tgz", - "integrity": "sha512-Ls2cfs1OfXaOKzkcxnqw5MR6drMA/zWX/LIS/p8Yjdz7QKTPQLMsB3R+OvoxE6XnXeXEE2X7xe4G4l4X0gRiKQ==", + "version": "5.6.2", + "resolved": "https://registry.npmjs.org/@octokit/request/-/request-5.6.2.tgz", + "integrity": "sha512-je66CvSEVf0jCpRISxkUcCa0UkxmFs6eGDRSbfJtAVwbLH5ceqF+YEyC8lj8ystKyZTy8adWr0qmkY52EfOeLA==", "dev": true, "requires": { "@octokit/endpoint": "^6.0.1", @@ -6149,12 +6146,12 @@ } }, "@octokit/types": { - "version": "6.31.1", - "resolved": "https://registry.npmjs.org/@octokit/types/-/types-6.31.1.tgz", - "integrity": "sha512-xkF46eaYcpT8ieO78mZWhMq3bt37zIsP5BUkN+zWgX+mTYDB7jOtUP1MOxcSF8hhJhsjjlB1YDgQAhX0z0oqPw==", + "version": "6.34.0", + "resolved": "https://registry.npmjs.org/@octokit/types/-/types-6.34.0.tgz", + "integrity": "sha512-s1zLBjWhdEI2zwaoSgyOFoKSl109CUcVBCc7biPJ3aAf6LGLU6szDvi31JPU7bxfla2lqfhjbbg/5DdFNxOwHw==", "dev": true, "requires": { - "@octokit/openapi-types": "^10.6.1" + "@octokit/openapi-types": "^11.2.0" } }, "@sindresorhus/is": { @@ -6265,9 +6262,9 @@ "dev": true }, "@types/node": { - "version": "16.10.2", - "resolved": "https://registry.npmjs.org/@types/node/-/node-16.10.2.tgz", - "integrity": "sha512-zCclL4/rx+W5SQTzFs9wyvvyCwoK9QtBpratqz2IYJ3O8Umrn0m3nsTv0wQBk9sRGpvUe9CwPDrQFB10f1FIjQ==", + "version": "16.10.3", + "resolved": "https://registry.npmjs.org/@types/node/-/node-16.10.3.tgz", + "integrity": "sha512-ho3Ruq+fFnBrZhUYI46n/bV2GjwzSkwuT4dTf0GkuNFmnb8nq4ny2z9JEVemFi6bdEJanHLlYfy9c6FN9B9McQ==", "dev": true }, "@types/parse-json": { @@ -6530,16 +6527,16 @@ "dev": true }, "browserslist": { - "version": "4.17.1", - "resolved": "https://registry.npmjs.org/browserslist/-/browserslist-4.17.1.tgz", - "integrity": "sha512-aLD0ZMDSnF4lUt4ZDNgqi5BUn9BZ7YdQdI/cYlILrhdSSZJLU9aNZoD5/NBmM4SK34APB2e83MOsRt1EnkuyaQ==", + "version": "4.17.3", + "resolved": "https://registry.npmjs.org/browserslist/-/browserslist-4.17.3.tgz", + "integrity": "sha512-59IqHJV5VGdcJZ+GZ2hU5n4Kv3YiASzW6Xk5g9tf5a/MAzGeFwgGWU39fVzNIOVcgB3+Gp+kiQu0HEfTVU/3VQ==", "dev": true, "requires": { - "caniuse-lite": "^1.0.30001259", - "electron-to-chromium": "^1.3.846", + "caniuse-lite": "^1.0.30001264", + "electron-to-chromium": "^1.3.857", "escalade": "^3.1.1", - "nanocolors": "^0.1.5", - "node-releases": "^1.1.76" + "node-releases": "^1.1.77", + "picocolors": "^0.2.1" } }, "buffer": { @@ -6625,9 +6622,9 @@ "dev": true }, "caniuse-lite": { - "version": "1.0.30001261", - "resolved": "https://registry.npmjs.org/caniuse-lite/-/caniuse-lite-1.0.30001261.tgz", - "integrity": "sha512-vM8D9Uvp7bHIN0fZ2KQ4wnmYFpJo/Etb4Vwsuc+ka0tfGDHvOPrFm6S/7CCNLSOkAUjenT2HnUPESdOIL91FaA==", + "version": "1.0.30001265", + "resolved": "https://registry.npmjs.org/caniuse-lite/-/caniuse-lite-1.0.30001265.tgz", + "integrity": "sha512-YzBnspggWV5hep1m9Z6sZVLOt7vrju8xWooFAgN6BA5qvy98qPAPb7vNUzypFaoh2pb3vlfzbDO8tB57UPGbtw==", "dev": true }, "chalk": { @@ -6701,9 +6698,9 @@ } }, "cli-spinners": { - "version": "2.6.0", - "resolved": "https://registry.npmjs.org/cli-spinners/-/cli-spinners-2.6.0.tgz", - "integrity": "sha512-t+4/y50K/+4xcCRosKkA7W4gTr1MySvLV0q+PxmG7FJ5g+66ChKurYjxBCjHggHH3HA5Hh9cy+lcUGWDqVH+4Q==", + "version": "2.6.1", + "resolved": "https://registry.npmjs.org/cli-spinners/-/cli-spinners-2.6.1.tgz", + "integrity": "sha512-x/5fWmGMnbKQAaNwN+UZlV79qBLM9JFnJuJ03gIi5whrob0xV0ofNVHy9DhwGdsMJQc2OKv0oGmLzvaqvAVv+g==", "dev": true }, "cli-width": { @@ -6967,9 +6964,9 @@ "dev": true }, "electron-to-chromium": { - "version": "1.3.854", - "resolved": "https://registry.npmjs.org/electron-to-chromium/-/electron-to-chromium-1.3.854.tgz", - "integrity": "sha512-00/IIC1mFPkq32MhUJyLdcTp7+wsKK2G3Sb65GSas9FKJQGYkDcZ4GwJkkxf5YyM3ETvl6n+toV8OmtXl4IA/g==", + "version": "1.3.864", + "resolved": "https://registry.npmjs.org/electron-to-chromium/-/electron-to-chromium-1.3.864.tgz", + "integrity": "sha512-v4rbad8GO6/yVI92WOeU9Wgxc4NA0n4f6P1FvZTY+jyY7JHEhw3bduYu60v3Q1h81Cg6eo4ApZrFPuycwd5hGw==", "dev": true }, "emoji-regex": { @@ -7713,9 +7710,9 @@ "dev": true }, "istanbul-lib-coverage": { - "version": "3.0.1", - "resolved": "https://registry.npmjs.org/istanbul-lib-coverage/-/istanbul-lib-coverage-3.0.1.tgz", - "integrity": "sha512-GvCYYTxaCPqwMjobtVcVKvSHtAGe48MNhGjpK8LtVF8K0ISX7hCKl85LgtuaSneWVyQmaGcW3iXVV3GaZSLpmQ==", + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/istanbul-lib-coverage/-/istanbul-lib-coverage-3.0.0.tgz", + "integrity": "sha512-UiUIqxMgRDET6eR+o5HbfRYP1l0hqkWOs7vNxC/mggutCMUIhWMm8gAHb8tHlyfD3/l6rlgNA5cKdDzEAf6hEg==", "dev": true }, "istanbul-lib-hook": { @@ -7796,9 +7793,9 @@ } }, "istanbul-reports": { - "version": "3.0.2", - "resolved": "https://registry.npmjs.org/istanbul-reports/-/istanbul-reports-3.0.2.tgz", - "integrity": "sha512-9tZvz7AiR3PEDNGiV9vIouQ/EAcqMXFmkcA1CDFTwOB98OZVDL0PH9glHotf5Ugp6GCOTypfzGWI/OqjWNCRUw==", + "version": "3.0.3", + "resolved": "https://registry.npmjs.org/istanbul-reports/-/istanbul-reports-3.0.3.tgz", + "integrity": "sha512-0i77ZFLsb9U3DHi22WzmIngVzfoyxxbQcZRqlF3KoKmCJGq9nhFHoGi8FqBztN2rE8w6hURnZghetn0xpkVb6A==", "dev": true, "requires": { "html-escaper": "^2.0.0", @@ -7971,9 +7968,9 @@ "dev": true }, "marked": { - "version": "3.0.4", - "resolved": "https://registry.npmjs.org/marked/-/marked-3.0.4.tgz", - "integrity": "sha512-jBo8AOayNaEcvBhNobg6/BLhdsK3NvnKWJg33MAAPbvTWiG4QBn9gpW1+7RssrKu4K1dKlN+0goVQwV41xEfOA==", + "version": "3.0.7", + "resolved": "https://registry.npmjs.org/marked/-/marked-3.0.7.tgz", + "integrity": "sha512-ctKqbnLuNbsHbI26cfMyOlKgXGfl1orOv1AvWWDX7AkgfMOwCWvmuYc+mVLeWhQ9W6hdWVBynOs96VkcscKo0Q==", "dev": true }, "merge-stream": { @@ -8084,12 +8081,6 @@ "integrity": "sha512-nnbWWOkoWyUsTjKrhgD0dcz22mdkSnpYqbEjIm2nhwhuxlSkpywJmBo8h0ZqJdkp73mb90SssHkN4rsRaBAfAA==", "dev": true }, - "nanocolors": { - "version": "0.1.12", - "resolved": "https://registry.npmjs.org/nanocolors/-/nanocolors-0.1.12.tgz", - "integrity": "sha512-2nMHqg1x5PU+unxX7PGY7AuYxl2qDx7PSrTRjizr8sxdd3l/3hBuWWaki62qmtYm2U5i4Z5E7GbjlyDFhs9/EQ==", - "dev": true - }, "nanoid": { "version": "3.1.25", "resolved": "https://registry.npmjs.org/nanoid/-/nanoid-3.1.25.tgz", @@ -8151,9 +8142,9 @@ } }, "node-releases": { - "version": "1.1.76", - "resolved": "https://registry.npmjs.org/node-releases/-/node-releases-1.1.76.tgz", - "integrity": "sha512-9/IECtNr8dXNmPWmFXepT0/7o5eolGesHUa3mtr0KlgnCvnZxwh2qensKL42JJY2vQKC3nIBXetFAqR+PW1CmA==", + "version": "1.1.77", + "resolved": "https://registry.npmjs.org/node-releases/-/node-releases-1.1.77.tgz", + "integrity": "sha512-rB1DUFUNAN4Gn9keO2K1efO35IDK7yKHCdCaIMvFO7yUYmmZYeDjnGKle26G4rwj+LKRQpjyUUvMkPglwGCYNQ==", "dev": true }, "normalize-path": { @@ -8669,6 +8660,12 @@ "integrity": "sha512-gDKb8aZMDeD/tZWs9P6+q0J9Mwkdl6xMV8TjnGP3qJVJ06bdMgkbBlLU8IdfOsIsFz2BW1rNVT3XuNEl8zPAvw==", "dev": true }, + "picocolors": { + "version": "0.2.1", + "resolved": "https://registry.npmjs.org/picocolors/-/picocolors-0.2.1.tgz", + "integrity": "sha512-cMlDqaLEqfSaW8Z7N5Jw+lyIW869EzT73/F5lhtY9cLGoVxSXznfgfXMO0Z5K0o0Q2TkTXq+0KFsdnSe3jDViA==", + "dev": true + }, "picomatch": { "version": "2.3.0", "resolved": "https://registry.npmjs.org/picomatch/-/picomatch-2.3.0.tgz", @@ -8738,12 +8735,6 @@ "fromentries": "^1.2.0" } }, - "progress": { - "version": "2.0.3", - "resolved": "https://registry.npmjs.org/progress/-/progress-2.0.3.tgz", - "integrity": "sha512-7PiHtLll5LdnKIMw100I+8xJXR5gW2QwWYkT6iJva0bXitZKa/XMrSbdmg3r2Xnaidz9Qumd0VPaMrZlF9V9sA==", - "dev": true - }, "protocols": { "version": "1.4.8", "resolved": "https://registry.npmjs.org/protocols/-/protocols-1.4.8.tgz", @@ -9065,9 +9056,9 @@ } }, "rxjs": { - "version": "7.3.0", - "resolved": "https://registry.npmjs.org/rxjs/-/rxjs-7.3.0.tgz", - "integrity": "sha512-p2yuGIg9S1epc3vrjKf6iVb3RCaAYjYskkO+jHIaV0IjOPlJop4UnodOoFb2xeNwlguqLYvGw1b1McillYb5Gw==", + "version": "7.4.0", + "resolved": "https://registry.npmjs.org/rxjs/-/rxjs-7.4.0.tgz", + "integrity": "sha512-7SQDi7xeTMCJpqViXh8gL/lebcwlp3d831F05+9B44A4B0WfsEwUQHR64gsH1kvJ+Ep/J9K2+n1hVl1CsGN23w==", "dev": true, "requires": { "tslib": "~2.1.0" @@ -9374,12 +9365,12 @@ "dev": true }, "ts-node": { - "version": "10.2.1", - "resolved": "https://registry.npmjs.org/ts-node/-/ts-node-10.2.1.tgz", - "integrity": "sha512-hCnyOyuGmD5wHleOQX6NIjJtYVIO8bPP8F2acWkB4W06wdlkgyvJtubO/I9NkI88hCFECbsEgoLc0VNkYmcSfw==", + "version": "10.3.0", + "resolved": "https://registry.npmjs.org/ts-node/-/ts-node-10.3.0.tgz", + "integrity": "sha512-RYIy3i8IgpFH45AX4fQHExrT8BxDeKTdC83QFJkNzkvt8uFB6QJ8XMyhynYiKMLxt9a7yuXaDBZNOYS3XjDcYw==", "dev": true, "requires": { - "@cspotcode/source-map-support": "0.6.1", + "@cspotcode/source-map-support": "0.7.0", "@tsconfig/node10": "^1.0.7", "@tsconfig/node12": "^1.0.7", "@tsconfig/node14": "^1.0.0", @@ -9429,38 +9420,45 @@ } }, "typedoc": { - "version": "0.21.9", - "resolved": "https://registry.npmjs.org/typedoc/-/typedoc-0.21.9.tgz", - "integrity": "sha512-VRo7aII4bnYaBBM1lhw4bQFmUcDQV8m8tqgjtc7oXl87jc1Slbhfw2X5MccfcR2YnEClHDWgsiQGgNB8KJXocA==", + "version": "0.22.5", + "resolved": "https://registry.npmjs.org/typedoc/-/typedoc-0.22.5.tgz", + "integrity": "sha512-KFrWGU1iKiTGw0RcyjLNYDmhd7uICU14HgBNPmFKY/sT4Pm/fraaLyWyisst9vGTUAKxqibqoDITR7+ZcAkhHg==", "dev": true, "requires": { - "glob": "^7.1.7", - "handlebars": "^4.7.7", + "glob": "^7.2.0", "lunr": "^2.3.9", - "marked": "^3.0.2", - "minimatch": "^3.0.0", - "progress": "^2.0.3", - "shiki": "^0.9.8", - "typedoc-default-themes": "^0.12.10" + "marked": "^3.0.4", + "minimatch": "^3.0.4", + "shiki": "^0.9.11" + }, + "dependencies": { + "glob": { + "version": "7.2.0", + "resolved": "https://registry.npmjs.org/glob/-/glob-7.2.0.tgz", + "integrity": "sha512-lmLf6gtyrPq8tTjSmrO94wBeQbFR3HbLHbuyD69wuyQkImp2hWqMGB47OX65FBkPffO641IP9jWa1z4ivqG26Q==", + "dev": true, + "requires": { + "fs.realpath": "^1.0.0", + "inflight": "^1.0.4", + "inherits": "2", + "minimatch": "^3.0.4", + "once": "^1.3.0", + "path-is-absolute": "^1.0.0" + } + } } }, - "typedoc-default-themes": { - "version": "0.12.10", - "resolved": "https://registry.npmjs.org/typedoc-default-themes/-/typedoc-default-themes-0.12.10.tgz", - "integrity": "sha512-fIS001cAYHkyQPidWXmHuhs8usjP5XVJjWB8oZGqkTowZaz3v7g3KDZeeqE82FBrmkAnIBOY3jgy7lnPnqATbA==", - "dev": true - }, "typedoc-github-wiki-theme": { - "version": "0.5.1", - "resolved": "https://registry.npmjs.org/typedoc-github-wiki-theme/-/typedoc-github-wiki-theme-0.5.1.tgz", - "integrity": "sha512-ED2Uc3WUjbv6xIdCkpMz3yBtcEciJnAhDQdRWLYgw4K+FKY0T3PzbI+ssNsBVgwDnYQP/XuaqfZkeQ3EQsOm9g==", + "version": "0.6.0", + "resolved": "https://registry.npmjs.org/typedoc-github-wiki-theme/-/typedoc-github-wiki-theme-0.6.0.tgz", + "integrity": "sha512-uHhR7PwAZ4JgO0KzlLocWSvMqKsSZ/wxUQYGKskIepzsotPAQcAWnSSnGi6gdkSw8UAfIIppdD7H1AmI39962w==", "dev": true, "requires": {} }, "typedoc-plugin-markdown": { - "version": "3.10.4", - "resolved": "https://registry.npmjs.org/typedoc-plugin-markdown/-/typedoc-plugin-markdown-3.10.4.tgz", - "integrity": "sha512-if9w7S9fXLg73AYi/EoRSEhTOZlg3I8mIP8YEmvzSE33VrNXC9/hA0nVcLEwFVJeQY7ay6z67I6kW0KIv7LjeA==", + "version": "3.11.3", + "resolved": "https://registry.npmjs.org/typedoc-plugin-markdown/-/typedoc-plugin-markdown-3.11.3.tgz", + "integrity": "sha512-rWiHbEIe0oZetDIsBR24XJVxGOJ91kDcHoj2KhFKxCLoJGX659EKBQkHne9QJ4W2stGhu1fRgFyQaouSBnxukA==", "dev": true, "requires": { "handlebars": "^4.7.7" diff --git a/package.json b/package.json index b2ceadbdd16..48c5018c13f 100644 --- a/package.json +++ b/package.json @@ -34,20 +34,21 @@ }, "devDependencies": { "@istanbuljs/nyc-config-typescript": "^1.0.1", + "@tsconfig/node12": "^1.0.9", "@types/mocha": "^9.0.0", - "@types/node": "^16.9.6", - "@types/sinon": "^10.0.3", + "@types/node": "^16.10.3", + "@types/sinon": "^10.0.4", "@types/which": "^2.0.1", "@types/yallist": "^4.0.1", - "mocha": "^9.1.1", + "mocha": "^9.1.2", "nyc": "^15.1.0", "release-it": "^14.11.6", "sinon": "^11.1.2", "source-map-support": "^0.5.20", - "ts-node": "^10.2.1", - "typedoc": "0.21.9", - "typedoc-github-wiki-theme": "^0.5.1", - "typedoc-plugin-markdown": "3.10.4", + "ts-node": "^10.3.0", + "typedoc": "^0.22.5", + "typedoc-github-wiki-theme": "^0.6.0", + "typedoc-plugin-markdown": "^3.11.3", "typescript": "^4.4.3", "which": "^2.0.2" }, diff --git a/tsconfig.json b/tsconfig.json index deebc9f1252..1f76310034d 100644 --- a/tsconfig.json +++ b/tsconfig.json @@ -1,11 +1,6 @@ { + "extends": "@tsconfig/node12/tsconfig.json", "compilerOptions": { - "strict": true, - "target": "ES2019", - "lib": ["ES2019", "ES2020.BigInt", "ES2020.String", "ES2020.Symbol.WellKnown"], - "module": "CommonJS", - "moduleResolution": "Node", - "esModuleInterop": true, "outDir": "./dist", "declaration": true, "useDefineForClassFields": true, @@ -27,6 +22,7 @@ "./index.ts", "./lib" ], + "entryPointStrategy": "expand", "exclude": [ "./lib/ts-declarations", "./lib/test-utils.ts" From c650d864d1fc2e48c243ed0cdec50b24b583eb55 Mon Sep 17 00:00:00 2001 From: leibale Date: Mon, 11 Oct 2021 17:32:15 -0400 Subject: [PATCH 0890/1748] lock benny version --- benchmark/package-lock.json | 92 +++++++++++++++++++++++++++---------- benchmark/package.json | 2 +- 2 files changed, 68 insertions(+), 26 deletions(-) diff --git a/benchmark/package-lock.json b/benchmark/package-lock.json index 49d77a57787..4afaf8c305c 100644 --- a/benchmark/package-lock.json +++ b/benchmark/package-lock.json @@ -7,7 +7,7 @@ "name": "benchmark", "license": "ISC", "dependencies": { - "benny": "^3.7.0", + "benny": "3.6.15", "v3": "npm:redis@3.1.2", "v4": "file:../" } @@ -127,6 +127,14 @@ "node": ">=8" } }, + "node_modules/at-least-node": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/at-least-node/-/at-least-node-1.0.0.tgz", + "integrity": "sha512-+q/t7Ekv1EDY2l6Gda6LLiX14rU9TV20Wa3ofeQmwPFZbOMo9DXrLbOjFaaclkXKWidIaopwAObQDqwWtGUjqg==", + "engines": { + "node": ">= 4.0.0" + } + }, "node_modules/benchmark": { "version": "2.1.4", "resolved": "https://registry.npmjs.org/benchmark/-/benchmark-2.1.4.tgz", @@ -137,21 +145,20 @@ } }, "node_modules/benny": { - "version": "3.7.0", - "resolved": "https://registry.npmjs.org/benny/-/benny-3.7.0.tgz", - "integrity": "sha512-tfQ8s4qwjfyMvFqe7wSGGel2Pzgl5ytwztS72Xh/ynPKJeXFLQ0sSVL95X9wHpqpXqW13pTHsEMCggrNKgeAoA==", + "version": "3.6.15", + "resolved": "https://registry.npmjs.org/benny/-/benny-3.6.15.tgz", + "integrity": "sha512-kq6XVGGYVou3Y8KNPs3SEF881vi5fJ8sIf9w69D2rreiNfRicWVWK6u6/mObMw6BiexoHHumtipn5gcu0Tngng==", "dependencies": { "@arrows/composition": "^1.0.0", "@arrows/dispatch": "^1.0.2", "@arrows/multimethod": "^1.1.6", "benchmark": "^2.1.4", - "fs-extra": "^10.0.0", - "json2csv": "^5.0.6", - "kleur": "^4.1.4", - "log-update": "^4.0.0" - }, - "engines": { - "node": ">=12" + "fs-extra": "^9.0.1", + "json2csv": "^5.0.4", + "kleur": "^4.1.3", + "log-update": "^4.0.0", + "prettier": "^2.1.2", + "stats-median": "^1.0.1" } }, "node_modules/cli-cursor": { @@ -208,16 +215,17 @@ "integrity": "sha512-f3qQ9oQy9j2AhBe/H9VC91wLmKBCCU/gDOnKNAYG5hswO7BLKj09Hc5HYNz9cGI++xlpDCIgDaitVs03ATR84Q==" }, "node_modules/fs-extra": { - "version": "10.0.0", - "resolved": "https://registry.npmjs.org/fs-extra/-/fs-extra-10.0.0.tgz", - "integrity": "sha512-C5owb14u9eJwizKGdchcDUQeFtlSHHthBk8pbX9Vc1PFZrLombudjDnNns88aYslCyF6IY5SUw3Roz6xShcEIQ==", + "version": "9.1.0", + "resolved": "https://registry.npmjs.org/fs-extra/-/fs-extra-9.1.0.tgz", + "integrity": "sha512-hcg3ZmepS30/7BSFqRvoo3DOMQu7IjqxO5nCDt+zM9XWjb33Wg7ziNT+Qvqbuc3+gWpzO02JubVyk2G4Zvo1OQ==", "dependencies": { + "at-least-node": "^1.0.0", "graceful-fs": "^4.2.0", "jsonfile": "^6.0.1", "universalify": "^2.0.0" }, "engines": { - "node": ">=12" + "node": ">=10" } }, "node_modules/graceful-fs": { @@ -331,6 +339,17 @@ "resolved": "https://registry.npmjs.org/platform/-/platform-1.3.6.tgz", "integrity": "sha512-fnWVljUchTro6RiCFvCXBbNhJc2NijN7oIQxbwsyL0buWJPG85v81ehlHI9fXrJsMNgTofEoWIQeClKpgxFLrg==" }, + "node_modules/prettier": { + "version": "2.4.1", + "resolved": "https://registry.npmjs.org/prettier/-/prettier-2.4.1.tgz", + "integrity": "sha512-9fbDAXSBcc6Bs1mZrDYb3XKzDLm4EXXL9sC1LqKP5rZkT6KRr/rf9amVUcODVXgguK/isJz0d0hP72WeaKWsvA==", + "bin": { + "prettier": "bin-prettier.js" + }, + "engines": { + "node": ">=10.13.0" + } + }, "node_modules/redis-commands": { "version": "1.7.0", "resolved": "https://registry.npmjs.org/redis-commands/-/redis-commands-1.7.0.tgz", @@ -388,6 +407,11 @@ "url": "https://github.com/chalk/slice-ansi?sponsor=1" } }, + "node_modules/stats-median": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/stats-median/-/stats-median-1.0.1.tgz", + "integrity": "sha512-IYsheLg6dasD3zT/w9+8Iq9tcIQqqu91ZIpJOnIEM25C3X/g4Tl8mhXwW2ZQpbrsJISr9+wizEYgsibN5/b32Q==" + }, "node_modules/string-width": { "version": "4.2.3", "resolved": "https://registry.npmjs.org/string-width/-/string-width-4.2.3.tgz", @@ -532,6 +556,11 @@ "resolved": "https://registry.npmjs.org/astral-regex/-/astral-regex-2.0.0.tgz", "integrity": "sha512-Z7tMw1ytTXt5jqMcOP+OQteU1VuNK9Y02uuJtKQ1Sv69jXQKKg5cibLwGJow8yzZP+eAc18EmLGPal0bp36rvQ==" }, + "at-least-node": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/at-least-node/-/at-least-node-1.0.0.tgz", + "integrity": "sha512-+q/t7Ekv1EDY2l6Gda6LLiX14rU9TV20Wa3ofeQmwPFZbOMo9DXrLbOjFaaclkXKWidIaopwAObQDqwWtGUjqg==" + }, "benchmark": { "version": "2.1.4", "resolved": "https://registry.npmjs.org/benchmark/-/benchmark-2.1.4.tgz", @@ -542,18 +571,20 @@ } }, "benny": { - "version": "3.7.0", - "resolved": "https://registry.npmjs.org/benny/-/benny-3.7.0.tgz", - "integrity": "sha512-tfQ8s4qwjfyMvFqe7wSGGel2Pzgl5ytwztS72Xh/ynPKJeXFLQ0sSVL95X9wHpqpXqW13pTHsEMCggrNKgeAoA==", + "version": "3.6.15", + "resolved": "https://registry.npmjs.org/benny/-/benny-3.6.15.tgz", + "integrity": "sha512-kq6XVGGYVou3Y8KNPs3SEF881vi5fJ8sIf9w69D2rreiNfRicWVWK6u6/mObMw6BiexoHHumtipn5gcu0Tngng==", "requires": { "@arrows/composition": "^1.0.0", "@arrows/dispatch": "^1.0.2", "@arrows/multimethod": "^1.1.6", "benchmark": "^2.1.4", - "fs-extra": "^10.0.0", - "json2csv": "^5.0.6", - "kleur": "^4.1.4", - "log-update": "^4.0.0" + "fs-extra": "^9.0.1", + "json2csv": "^5.0.4", + "kleur": "^4.1.3", + "log-update": "^4.0.0", + "prettier": "^2.1.2", + "stats-median": "^1.0.1" } }, "cli-cursor": { @@ -598,10 +629,11 @@ "integrity": "sha512-f3qQ9oQy9j2AhBe/H9VC91wLmKBCCU/gDOnKNAYG5hswO7BLKj09Hc5HYNz9cGI++xlpDCIgDaitVs03ATR84Q==" }, "fs-extra": { - "version": "10.0.0", - "resolved": "https://registry.npmjs.org/fs-extra/-/fs-extra-10.0.0.tgz", - "integrity": "sha512-C5owb14u9eJwizKGdchcDUQeFtlSHHthBk8pbX9Vc1PFZrLombudjDnNns88aYslCyF6IY5SUw3Roz6xShcEIQ==", + "version": "9.1.0", + "resolved": "https://registry.npmjs.org/fs-extra/-/fs-extra-9.1.0.tgz", + "integrity": "sha512-hcg3ZmepS30/7BSFqRvoo3DOMQu7IjqxO5nCDt+zM9XWjb33Wg7ziNT+Qvqbuc3+gWpzO02JubVyk2G4Zvo1OQ==", "requires": { + "at-least-node": "^1.0.0", "graceful-fs": "^4.2.0", "jsonfile": "^6.0.1", "universalify": "^2.0.0" @@ -685,6 +717,11 @@ "resolved": "https://registry.npmjs.org/platform/-/platform-1.3.6.tgz", "integrity": "sha512-fnWVljUchTro6RiCFvCXBbNhJc2NijN7oIQxbwsyL0buWJPG85v81ehlHI9fXrJsMNgTofEoWIQeClKpgxFLrg==" }, + "prettier": { + "version": "2.4.1", + "resolved": "https://registry.npmjs.org/prettier/-/prettier-2.4.1.tgz", + "integrity": "sha512-9fbDAXSBcc6Bs1mZrDYb3XKzDLm4EXXL9sC1LqKP5rZkT6KRr/rf9amVUcODVXgguK/isJz0d0hP72WeaKWsvA==" + }, "redis-commands": { "version": "1.7.0", "resolved": "https://registry.npmjs.org/redis-commands/-/redis-commands-1.7.0.tgz", @@ -727,6 +764,11 @@ "is-fullwidth-code-point": "^3.0.0" } }, + "stats-median": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/stats-median/-/stats-median-1.0.1.tgz", + "integrity": "sha512-IYsheLg6dasD3zT/w9+8Iq9tcIQqqu91ZIpJOnIEM25C3X/g4Tl8mhXwW2ZQpbrsJISr9+wizEYgsibN5/b32Q==" + }, "string-width": { "version": "4.2.3", "resolved": "https://registry.npmjs.org/string-width/-/string-width-4.2.3.tgz", diff --git a/benchmark/package.json b/benchmark/package.json index f56ad2f23ce..ab874090c4b 100644 --- a/benchmark/package.json +++ b/benchmark/package.json @@ -10,7 +10,7 @@ "author": "", "license": "ISC", "dependencies": { - "benny": "^3.7.0", + "benny": "3.6.15", "v3": "npm:redis@3.1.2", "v4": "file:../" } From 40972582f02acb9b5ebf8ac5c897e2eefa9e9474 Mon Sep 17 00:00:00 2001 From: leibale Date: Mon, 11 Oct 2021 17:32:49 -0400 Subject: [PATCH 0891/1748] fix #1674 - remove `isolationPoolOptions` when creating isolated connection --- lib/client/index.ts | 11 ++++++++--- lib/cluster/index.ts | 7 +++++-- 2 files changed, 13 insertions(+), 5 deletions(-) diff --git a/lib/client/index.ts b/lib/client/index.ts index 5aeffd365da..1a5384f509f 100644 --- a/lib/client/index.ts +++ b/lib/client/index.ts @@ -147,7 +147,9 @@ export default class RedisClient this.#queue = this.#initiateQueue(); this.#isolationPool = createPool({ create: async () => { - const duplicate = this.duplicate(); + const duplicate = this.duplicate({ + isolationPoolOptions: undefined + }); await duplicate.connect(); return duplicate; }, @@ -269,8 +271,11 @@ export default class RedisClient }; } - duplicate(): RedisClientType { - return new (Object.getPrototypeOf(this).constructor)(this.#options); + duplicate(overrides?: Partial>): RedisClientType { + return new (Object.getPrototypeOf(this).constructor)({ + ...this.#options, + ...overrides + }); } async connect(): Promise { diff --git a/lib/cluster/index.ts b/lib/cluster/index.ts index 4b55a93d4ab..aeaabecae35 100644 --- a/lib/cluster/index.ts +++ b/lib/cluster/index.ts @@ -61,8 +61,11 @@ export default class RedisCluster(): RedisClusterType { - return new (Object.getPrototypeOf(this).constructor)(); + duplicate(overrides?: Partial>): RedisClusterType { + return new (Object.getPrototypeOf(this).constructor)({ + ...this.#options, + ...overrides + }); } async connect(): Promise { From 61cec9d06cc6806fd097bbc94d6d096b000e269a Mon Sep 17 00:00:00 2001 From: leibale Date: Mon, 11 Oct 2021 19:15:02 -0400 Subject: [PATCH 0892/1748] increase test coverage --- lib/cluster/index.spec.ts | 2 +- lib/commands/GEODIST.spec.ts | 32 ++++++++++++++++--- lib/commands/HELLO.ts | 2 +- lib/commands/HSET.spec.ts | 10 +++--- lib/commands/XREAD.spec.ts | 22 +++++++++++-- lib/commands/XREADGROUP.spec.ts | 56 +++++++++++++++++++++------------ lib/commands/ZINTER.ts | 2 +- lib/commands/ZINTERSTORE.ts | 2 +- lib/commands/ZRANGE.spec.ts | 7 +++++ 9 files changed, 97 insertions(+), 38 deletions(-) diff --git a/lib/cluster/index.spec.ts b/lib/cluster/index.spec.ts index f3bf4d8bacb..f2227395385 100644 --- a/lib/cluster/index.spec.ts +++ b/lib/cluster/index.spec.ts @@ -31,7 +31,7 @@ describe('Cluster', () => { itWithCluster(TestRedisClusters.OPEN, 'multi', async cluster => { const key = 'key'; assert.deepEqual( - await cluster.multi(key) + await cluster.multi() .set(key, 'value') .get(key) .exec(), diff --git a/lib/commands/GEODIST.spec.ts b/lib/commands/GEODIST.spec.ts index c1168259312..2cff8ecbd82 100644 --- a/lib/commands/GEODIST.spec.ts +++ b/lib/commands/GEODIST.spec.ts @@ -19,11 +19,33 @@ describe('GEODIST', () => { }); }); - itWithClient(TestRedisServers.OPEN, 'client.geoDist', async client => { - assert.equal( - await client.geoDist('key', '1', '2'), - null - ); + describe('client.geoDist', () => { + itWithClient(TestRedisServers.OPEN, 'null', async client => { + assert.equal( + await client.geoDist('key', '1', '2'), + null + ); + }); + + itWithClient(TestRedisServers.OPEN, 'with value', async client => { + const [, dist] = await Promise.all([ + client.geoAdd('key', [{ + member: '1', + longitude: 1, + latitude: 1 + }, { + member: '2', + longitude: 2, + latitude: 2 + }]), + client.geoDist('key', '1', '2') + ]); + + assert.equal( + dist, + 157270.0561 + ); + }); }); itWithCluster(TestRedisClusters.OPEN, 'cluster.geoDist', async cluster => { diff --git a/lib/commands/HELLO.ts b/lib/commands/HELLO.ts index efb96890fcd..86dae2a1d71 100644 --- a/lib/commands/HELLO.ts +++ b/lib/commands/HELLO.ts @@ -12,7 +12,7 @@ export function transformArguments(options?: HelloOptions): Array { if (options) { args.push(options.protover.toString()); - if (options?.auth) { + if (options.auth) { args.push('AUTH', options.auth.username, options.auth.password); } diff --git a/lib/commands/HSET.spec.ts b/lib/commands/HSET.spec.ts index 601e7f967e1..e8dfe7865d3 100644 --- a/lib/commands/HSET.spec.ts +++ b/lib/commands/HSET.spec.ts @@ -26,12 +26,10 @@ describe('HSET', () => { }); it('Object', () => { - it('Array', () => { - assert.deepEqual( - transformArguments('key', { field: 'value' }), - ['HSET', 'key', 'field', 'value'] - ); - }); + assert.deepEqual( + transformArguments('key', { field: 'value' }), + ['HSET', 'key', 'field', 'value'] + ); }); }); diff --git a/lib/commands/XREAD.spec.ts b/lib/commands/XREAD.spec.ts index e7bf127a90a..501571bfbeb 100644 --- a/lib/commands/XREAD.spec.ts +++ b/lib/commands/XREAD.spec.ts @@ -1,8 +1,24 @@ import { strict as assert } from 'assert'; import { TestRedisServers, itWithClient, itWithCluster, TestRedisClusters } from '../test-utils'; -import { transformArguments } from './XREAD'; +import { FIRST_KEY_INDEX, transformArguments } from './XREAD'; describe('XREAD', () => { + describe('FIRST_KEY_INDEX', () => { + it('single stream', () => { + assert.equal( + FIRST_KEY_INDEX({ key: 'key', id: '' }), + 'key' + ); + }); + + it('multiple streams', () => { + assert.equal( + FIRST_KEY_INDEX([{ key: '1', id: '' }, { key: '2', id: '' }]), + '1' + ); + }); + }); + describe('transformArguments', () => { it('single stream', () => { assert.deepEqual( @@ -13,13 +29,13 @@ describe('XREAD', () => { ['XREAD', 'STREAMS', 'key', '0'] ); }); - + it('multiple streams', () => { assert.deepEqual( transformArguments([{ key: '1', id: '0' - }, { + }, { key: '2', id: '0' }]), diff --git a/lib/commands/XREADGROUP.spec.ts b/lib/commands/XREADGROUP.spec.ts index 8f7693c333b..8cb3147bfe7 100644 --- a/lib/commands/XREADGROUP.spec.ts +++ b/lib/commands/XREADGROUP.spec.ts @@ -1,8 +1,24 @@ import { strict as assert } from 'assert'; import { TestRedisServers, itWithClient, itWithCluster, TestRedisClusters } from '../test-utils'; -import { transformArguments } from './XREADGROUP'; +import { FIRST_KEY_INDEX, transformArguments } from './XREADGROUP'; describe('XREADGROUP', () => { + describe('FIRST_KEY_INDEX', () => { + it('single stream', () => { + assert.equal( + FIRST_KEY_INDEX('', '', { key: 'key', id: '' }), + 'key' + ); + }); + + it('multiple streams', () => { + assert.equal( + FIRST_KEY_INDEX('', '', [{ key: '1', id: '' }, { key: '2', id: '' }]), + '1' + ); + }); + }); + describe('transformArguments', () => { it('single stream', () => { assert.deepEqual( @@ -78,13 +94,27 @@ describe('XREADGROUP', () => { }); }); - describe('client.xReadGroup', () => { - itWithClient(TestRedisServers.OPEN, 'null', async client => { + itWithClient(TestRedisServers.OPEN, 'null', async client => { + const [, readGroupReply] = await Promise.all([ + client.xGroupCreate('key', 'group', '$', { + MKSTREAM: true + }), + client.xReadGroup('group', 'consumer', { + key: 'key', + id: '>' + }) + ]); + + assert.equal(readGroupReply, null); + }); + + describe('cluster.xReadGroup', () => { + itWithCluster(TestRedisClusters.OPEN, 'null', async cluster => { const [, readGroupReply] = await Promise.all([ - client.xGroupCreate('key', 'group', '$', { + cluster.xGroupCreate('key', 'group', '$', { MKSTREAM: true }), - client.xReadGroup('group', 'consumer', { + cluster.xReadGroup('group', 'consumer', { key: 'key', id: '>' }) @@ -93,7 +123,7 @@ describe('XREADGROUP', () => { assert.equal(readGroupReply, null); }); - itWithClient(TestRedisServers.OPEN, 'with a message', async client => { + itWithCluster(TestRedisClusters.OPEN, 'with a message', async client => { const [, id, readGroupReply] = await Promise.all([ client.xGroupCreate('key', 'group', '$', { MKSTREAM: true @@ -120,18 +150,4 @@ describe('XREADGROUP', () => { }]); }); }); - - itWithCluster(TestRedisClusters.OPEN, 'cluster.xReadGroup', async cluster => { - const [, readGroupReply] = await Promise.all([ - cluster.xGroupCreate('key', 'group', '$', { - MKSTREAM: true - }), - cluster.xReadGroup('group', 'consumer', { - key: 'key', - id: '>' - }) - ]); - - assert.equal(readGroupReply, null); - }); }); diff --git a/lib/commands/ZINTER.ts b/lib/commands/ZINTER.ts index eee49837386..629515b57f9 100644 --- a/lib/commands/ZINTER.ts +++ b/lib/commands/ZINTER.ts @@ -21,7 +21,7 @@ export function transformArguments(keys: Array | string, options?: ZInte } if (options?.AGGREGATE) { - args.push('AGGREGATE', options?.AGGREGATE); + args.push('AGGREGATE', options.AGGREGATE); } return args; diff --git a/lib/commands/ZINTERSTORE.ts b/lib/commands/ZINTERSTORE.ts index 59a27e11c51..e0916e5b14a 100644 --- a/lib/commands/ZINTERSTORE.ts +++ b/lib/commands/ZINTERSTORE.ts @@ -19,7 +19,7 @@ export function transformArguments(destination: string, keys: Array | st } if (options?.AGGREGATE) { - args.push('AGGREGATE', options?.AGGREGATE); + args.push('AGGREGATE', options.AGGREGATE); } return args; diff --git a/lib/commands/ZRANGE.spec.ts b/lib/commands/ZRANGE.spec.ts index 72d83931ff4..7347ed0ad09 100644 --- a/lib/commands/ZRANGE.spec.ts +++ b/lib/commands/ZRANGE.spec.ts @@ -11,6 +11,13 @@ describe('ZRANGE', () => { ); }); + it('using strings', () => { + assert.deepEqual( + transformArguments('src', '0', '1'), + ['ZRANGE', 'src', '0', '1'] + ); + }); + it('with BYSCORE', () => { assert.deepEqual( transformArguments('src', 0, 1, { From b0f657c74762a2f5c87bbe27ed3d160b0dd8fbd9 Mon Sep 17 00:00:00 2001 From: leibale Date: Mon, 11 Oct 2021 19:34:23 -0400 Subject: [PATCH 0893/1748] update .npmignore --- .npmignore | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/.npmignore b/.npmignore index fa2d8841124..e3fc1a7731d 100644 --- a/.npmignore +++ b/.npmignore @@ -1,12 +1,13 @@ .vscode/ .idea/ node_modules/ -.nyc_output +.nyc_output/ coverage/ dump.rdb documentation/ CONTRIBUTING.md tsconfig.json +.deepsource.toml .nycrc.json benchmark/ .github/ From 235db19f795d18ca620def083e2230a36b889858 Mon Sep 17 00:00:00 2001 From: leibale Date: Mon, 11 Oct 2021 19:36:46 -0400 Subject: [PATCH 0894/1748] Release 4.0.0-rc.3 --- package-lock.json | 4 ++-- package.json | 2 +- 2 files changed, 3 insertions(+), 3 deletions(-) diff --git a/package-lock.json b/package-lock.json index 6a7fa380af7..f47208d6c9d 100644 --- a/package-lock.json +++ b/package-lock.json @@ -1,12 +1,12 @@ { "name": "redis", - "version": "4.0.0-rc.2", + "version": "4.0.0-rc.3", "lockfileVersion": 2, "requires": true, "packages": { "": { "name": "redis", - "version": "4.0.0-rc.2", + "version": "4.0.0-rc.3", "license": "MIT", "dependencies": { "cluster-key-slot": "1.1.0", diff --git a/package.json b/package.json index 48c5018c13f..7f8720f47bf 100644 --- a/package.json +++ b/package.json @@ -1,6 +1,6 @@ { "name": "redis", - "version": "4.0.0-rc.2", + "version": "4.0.0-rc.3", "description": "A high performance Redis client.", "keywords": [ "database", From 199285aa71fd1adb020acd36f54a780b4e570267 Mon Sep 17 00:00:00 2001 From: Leibale Eidelman Date: Mon, 11 Oct 2021 19:40:14 -0400 Subject: [PATCH 0895/1748] Release 4.0.0-rc.3 (#1678) * 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 * fix #1652 * ref #1653 - better types * better types * fix 54124793ad1b633d39d372bdff487c9888c017a7 * 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 Co-authored-by: Richard Samuelsson --- .github/ISSUE_TEMPLATE.md | 21 - .github/ISSUE_TEMPLATE/bug-report.md | 15 + .github/ISSUE_TEMPLATE/feature-request.md | 7 + .npmignore | 3 +- README.md | 34 +- SECURITY.md | 16 + benchmark/package-lock.json | 223 ++--- benchmark/package.json | 2 +- index.ts | 2 - lib/{ => client}/commands-queue.ts | 6 +- lib/client/commands.ts | 233 +++++ lib/{client.spec.ts => client/index.spec.ts} | 19 +- lib/{client.ts => client/index.ts} | 113 ++- lib/client/multi-command.ts | 132 +++ lib/{ => client}/socket.spec.ts | 0 lib/{ => client}/socket.ts | 4 +- lib/{ => cluster}/cluster-slots.ts | 64 +- lib/cluster/commands.ts | 535 +++++++++++ .../index.spec.ts} | 19 +- lib/{cluster.ts => cluster/index.ts} | 101 +- lib/cluster/multi-command.ts | 112 +++ lib/commander.ts | 51 +- lib/commands/ACL_CAT.ts | 4 +- lib/commands/ACL_DELUSER.ts | 8 +- lib/commands/ACL_GENPASS.ts | 4 +- lib/commands/ACL_LIST.ts | 4 +- lib/commands/ACL_LOAD.ts | 4 +- lib/commands/ACL_LOG_RESET.ts | 4 +- lib/commands/ACL_SAVE.ts | 4 +- lib/commands/ACL_SETUSER.ts | 8 +- lib/commands/ACL_USERS.ts | 4 +- lib/commands/ACL_WHOAMI.ts | 4 +- lib/commands/APPEND.ts | 4 +- lib/commands/ASKING.ts | 4 +- lib/commands/AUTH.ts | 4 +- lib/commands/BGREWRITEAOF.ts | 4 +- lib/commands/BGSAVE.ts | 4 +- lib/commands/BITCOUNT.ts | 4 +- lib/commands/BITFIELD.ts | 6 +- lib/commands/BITOP.ts | 8 +- lib/commands/BITPOS.ts | 4 +- lib/commands/BLMOVE.ts | 3 +- lib/commands/BLPOP.ts | 4 +- lib/commands/BRPOP.ts | 4 +- lib/commands/BRPOPLPUSH.ts | 4 +- lib/commands/BZPOPMAX.spec.ts | 1 - lib/commands/BZPOPMAX.ts | 4 +- lib/commands/BZPOPMIN.ts | 4 +- lib/commands/CLIENT_ID.ts | 4 +- lib/commands/CLUSTER_ADDSLOTS.ts | 4 +- lib/commands/CLUSTER_FLUSHSLOTS.ts | 4 +- lib/commands/CLUSTER_GETKEYSINSLOT.ts | 4 +- lib/commands/CLUSTER_INFO.spec.ts | 18 - lib/commands/CLUSTER_MEET.ts | 4 +- lib/commands/CLUSTER_NODES.spec.ts | 21 - lib/commands/CLUSTER_RESET.ts | 4 +- lib/commands/CLUSTER_SETSLOT.ts | 4 +- lib/commands/CLUSTER_SLOTS.ts | 4 +- lib/commands/COMMAND.spec.ts | 30 + lib/commands/COMMAND.ts | 12 + lib/commands/COMMAND_COUNT.spec.ts | 19 + lib/commands/COMMAND_COUNT.ts | 9 + lib/commands/COMMAND_GETKEYS.spec.ts | 19 + lib/commands/COMMAND_GETKEYS.ts | 9 + lib/commands/COMMAND_INFO.spec.ts | 30 + lib/commands/COMMAND_INFO.ts | 12 + lib/commands/CONFIG_RESETSTAT.ts | 4 +- lib/commands/CONFIG_REWRITE.ts | 4 +- lib/commands/CONFIG_SET.ts | 4 +- lib/commands/DBSIZE.spec.ts | 9 +- lib/commands/DBSIZE.ts | 4 +- lib/commands/DECR.ts | 4 +- lib/commands/DECRBY.ts | 4 +- lib/commands/DEL.ts | 8 +- lib/commands/DISCARD.ts | 4 +- lib/commands/DUMP.ts | 4 +- lib/commands/ECHO.spec.ts | 9 +- lib/commands/ECHO.ts | 4 +- lib/commands/EVAL.ts | 4 - lib/commands/EVALSHA.ts | 4 - lib/commands/EXISTS.ts | 4 +- lib/commands/FAILOVER.ts | 4 +- lib/commands/FLUSHALL.ts | 4 +- lib/commands/FLUSHDB.ts | 3 +- lib/commands/GEOADD.ts | 4 +- lib/commands/GEODIST.spec.ts | 32 +- lib/commands/GEOHASH.ts | 8 +- lib/commands/GEOPOS.ts | 4 +- lib/commands/GEOSEARCH.ts | 4 +- lib/commands/GEOSEARCH_WITH.spec.ts | 4 +- lib/commands/GEOSEARCH_WITH.ts | 6 +- lib/commands/GET.ts | 7 +- lib/commands/GETBIT.ts | 4 +- lib/commands/GETDEL.ts | 4 +- lib/commands/GETEX.ts | 8 +- lib/commands/GETRANGE.ts | 4 +- lib/commands/GETSET.ts | 4 +- lib/commands/GET_BUFFER.ts | 4 +- lib/commands/HDEL.ts | 8 +- lib/commands/HELLO.ts | 2 +- lib/commands/HINCRBY.ts | 4 +- lib/commands/HINCRBYFLOAT.ts | 4 +- lib/commands/HKEYS.ts | 4 +- lib/commands/HLEN.ts | 4 +- lib/commands/HMGET.ts | 8 +- lib/commands/HRANDFIELD.ts | 4 +- lib/commands/HRANDFIELD_COUNT.ts | 3 +- lib/commands/HSET.spec.ts | 10 +- lib/commands/HSET.ts | 7 +- lib/commands/HSTRLEN.ts | 4 +- lib/commands/HVALS.ts | 4 +- lib/commands/INCR.ts | 4 +- lib/commands/INCRBY.ts | 4 +- lib/commands/INCRBYFLOAT.ts | 4 +- lib/commands/INFO.ts | 4 +- lib/commands/LASTSAVE.spec.ts | 6 +- lib/commands/LINDEX.ts | 4 +- lib/commands/LINSERT.ts | 4 +- lib/commands/LLEN.ts | 4 +- lib/commands/LMOVE.ts | 4 +- lib/commands/LOLWUT.spec.ts | 11 +- lib/commands/LOLWUT.ts | 4 +- lib/commands/LPOP.ts | 4 +- lib/commands/LPOP_COUNT.ts | 4 +- lib/commands/LPOS.ts | 4 +- lib/commands/LPOS_COUNT.ts | 3 +- lib/commands/LPUSH.ts | 8 +- lib/commands/LPUSHX.ts | 8 +- lib/commands/LRANGE.ts | 4 +- lib/commands/LREM.ts | 4 +- lib/commands/LSET.ts | 4 +- lib/commands/LTRIM.ts | 4 +- lib/commands/MEMORY_DOCTOR.spec.ts | 9 +- lib/commands/MEMORY_DOCTOR.ts | 4 +- lib/commands/MEMORY_MALLOC-STATS.spec.ts | 9 +- lib/commands/MEMORY_MALLOC-STATS.ts | 4 +- lib/commands/MEMORY_PURGE.spec.ts | 9 +- lib/commands/MEMORY_PURGE.ts | 4 +- lib/commands/MEMORY_USAGE.spec.ts | 9 +- lib/commands/MEMORY_USAGE.ts | 4 +- lib/commands/MGET.ts | 4 +- lib/commands/MIGRATE.ts | 3 +- lib/commands/MODULE_LIST.ts | 4 +- lib/commands/MODULE_LOAD.ts | 4 +- lib/commands/MODULE_UNLOAD.ts | 4 +- lib/commands/MSET.ts | 4 +- lib/commands/PFADD.ts | 4 +- lib/commands/PFCOUNT.ts | 8 +- lib/commands/PFMERGE.ts | 8 +- lib/commands/PING.spec.ts | 9 +- lib/commands/PING.ts | 4 +- lib/commands/PSETEX.ts | 4 +- lib/commands/PTTL.ts | 4 +- lib/commands/PUBLISH.ts | 4 +- lib/commands/PUBSUB_CHANNELS.spec.ts | 9 +- lib/commands/PUBSUB_CHANNELS.ts | 4 +- lib/commands/PUBSUB_NUMPAT.spec.ts | 9 +- lib/commands/PUBSUB_NUMPAT.ts | 4 +- lib/commands/PUBSUB_NUMSUB.spec.ts | 9 +- lib/commands/READONLY.ts | 4 +- lib/commands/READWRITE.ts | 4 +- lib/commands/RENAME.ts | 4 +- lib/commands/REPLICAOF.ts | 4 +- lib/commands/RESTORE-ASKING.ts | 4 +- lib/commands/RPOP.ts | 4 +- lib/commands/RPOPLPUSH.ts | 4 +- lib/commands/RPOP_COUNT.ts | 4 +- lib/commands/RPUSH.ts | 8 +- lib/commands/RPUSHX.ts | 8 +- lib/commands/SADD.ts | 8 +- lib/commands/SAVE.ts | 4 +- lib/commands/SCARD.ts | 4 +- lib/commands/SCRIPT_DEBUG.spec.ts | 9 +- lib/commands/SCRIPT_DEBUG.ts | 4 +- lib/commands/SCRIPT_EXISTS.spec.ts | 9 +- lib/commands/SCRIPT_EXISTS.ts | 4 +- lib/commands/SCRIPT_FLUSH.spec.ts | 9 +- lib/commands/SCRIPT_FLUSH.ts | 4 +- lib/commands/SCRIPT_KILL.ts | 4 +- lib/commands/SCRIPT_LOAD.spec.ts | 9 +- lib/commands/SCRIPT_LOAD.ts | 4 +- lib/commands/SDIFF.ts | 8 +- lib/commands/SDIFFSTORE.ts | 8 +- lib/commands/SET.ts | 4 +- lib/commands/SETBIT.ts | 8 +- lib/commands/SETEX.ts | 7 +- lib/commands/SETRANGE.ts | 4 +- lib/commands/SHUTDOWN.ts | 4 +- lib/commands/SINTER.ts | 8 +- lib/commands/SINTERSTORE.ts | 8 +- lib/commands/SMEMBERS.ts | 4 +- lib/commands/SPOP.ts | 4 +- lib/commands/SRANDMEMBER.ts | 4 +- lib/commands/SRANDMEMBER_COUNT.ts | 3 +- lib/commands/SREM.ts | 8 +- lib/commands/STRLEN.ts | 4 +- lib/commands/SUNION.ts | 8 +- lib/commands/SUNIONSTORE.ts | 8 +- lib/commands/SWAPDB.ts | 4 +- lib/commands/TOUCH.ts | 8 +- lib/commands/TTL.ts | 4 +- lib/commands/TYPE.ts | 4 +- lib/commands/UNLINK.ts | 8 +- lib/commands/UNWATCH.spec.ts | 9 +- lib/commands/UNWATCH.ts | 4 +- lib/commands/WAIT.ts | 4 +- lib/commands/WATCH.ts | 8 +- lib/commands/XACK.ts | 8 +- lib/commands/XADD.ts | 5 +- lib/commands/XCLAIM_JUSTID.ts | 3 +- lib/commands/XDEL.ts | 8 +- lib/commands/XGROUP_CREATE.ts | 4 +- lib/commands/XGROUP_DELCONSUMER.ts | 4 +- lib/commands/XGROUP_SETID.ts | 4 +- lib/commands/XLEN.ts | 4 +- lib/commands/XREAD.spec.ts | 22 +- lib/commands/XREADGROUP.spec.ts | 56 +- lib/commands/XTRIM.ts | 4 +- lib/commands/ZCARD.ts | 4 +- lib/commands/ZCOUNT.ts | 4 +- lib/commands/ZDIFF.ts | 8 +- lib/commands/ZDIFFSTORE.ts | 8 +- lib/commands/ZDIFF_WITHSCORES.ts | 4 +- lib/commands/ZINTER.ts | 10 +- lib/commands/ZINTERSTORE.ts | 10 +- lib/commands/ZINTER_WITHSCORES.ts | 4 +- lib/commands/ZLEXCOUNT.ts | 4 +- lib/commands/ZMSCORE.ts | 4 +- lib/commands/ZRANDMEMBER.ts | 4 +- lib/commands/ZRANDMEMBER_COUNT.ts | 3 +- lib/commands/ZRANGE.spec.ts | 7 + lib/commands/ZRANGE.ts | 4 +- lib/commands/ZRANGEBYLEX.spec.ts | 33 + lib/commands/ZRANGEBYLEX.ts | 35 + lib/commands/ZRANGEBYSCORE.spec.ts | 33 + lib/commands/ZRANGEBYSCORE.ts | 35 + lib/commands/ZRANGEBYSCORE_WITHSCORES.spec.ts | 33 + lib/commands/ZRANGEBYSCORE_WITHSCORES.ts | 19 + lib/commands/ZRANGESTORE.spec.ts | 11 +- lib/commands/ZRANK.ts | 4 +- lib/commands/ZREM.ts | 8 +- lib/commands/ZREMRANGEBYLEX.ts | 4 +- lib/commands/ZREMRANGEBYRANK.ts | 4 +- lib/commands/ZREMRANGEBYSCORE.ts | 4 +- lib/commands/ZREVRANK.ts | 4 +- lib/commands/ZUNION.ts | 8 +- lib/commands/ZUNIONSTORE.ts | 8 +- lib/commands/ZUNION_WITHSCORES.ts | 4 +- lib/commands/generic-transformers.spec.ts | 35 +- lib/commands/generic-transformers.ts | 136 +-- lib/commands/index.ts | 763 +-------------- lib/lua-script.ts | 10 +- lib/multi-command.spec.ts | 163 ++-- lib/multi-command.ts | 190 +--- lib/test-utils.ts | 28 +- package-lock.json | 872 +++++++----------- package.json | 17 +- tsconfig.json | 8 +- 258 files changed, 2710 insertions(+), 2774 deletions(-) delete mode 100644 .github/ISSUE_TEMPLATE.md create mode 100644 .github/ISSUE_TEMPLATE/bug-report.md create mode 100644 .github/ISSUE_TEMPLATE/feature-request.md create mode 100644 SECURITY.md rename lib/{ => client}/commands-queue.ts (97%) create mode 100644 lib/client/commands.ts rename lib/{client.spec.ts => client/index.spec.ts} (97%) rename lib/{client.ts => client/index.ts} (79%) create mode 100644 lib/client/multi-command.ts rename lib/{ => client}/socket.spec.ts (100%) rename lib/{ => client}/socket.ts (98%) rename lib/{ => cluster}/cluster-slots.ts (77%) create mode 100644 lib/cluster/commands.ts rename lib/{cluster.spec.ts => cluster/index.spec.ts} (86%) rename lib/{cluster.ts => cluster/index.ts} (61%) create mode 100644 lib/cluster/multi-command.ts create mode 100644 lib/commands/COMMAND.spec.ts create mode 100644 lib/commands/COMMAND.ts create mode 100644 lib/commands/COMMAND_COUNT.spec.ts create mode 100644 lib/commands/COMMAND_COUNT.ts create mode 100644 lib/commands/COMMAND_GETKEYS.spec.ts create mode 100644 lib/commands/COMMAND_GETKEYS.ts create mode 100644 lib/commands/COMMAND_INFO.spec.ts create mode 100644 lib/commands/COMMAND_INFO.ts create mode 100644 lib/commands/ZRANGEBYLEX.spec.ts create mode 100644 lib/commands/ZRANGEBYLEX.ts create mode 100644 lib/commands/ZRANGEBYSCORE.spec.ts create mode 100644 lib/commands/ZRANGEBYSCORE.ts create mode 100644 lib/commands/ZRANGEBYSCORE_WITHSCORES.spec.ts create mode 100644 lib/commands/ZRANGEBYSCORE_WITHSCORES.ts diff --git a/.github/ISSUE_TEMPLATE.md b/.github/ISSUE_TEMPLATE.md deleted file mode 100644 index 9b181d43149..00000000000 --- a/.github/ISSUE_TEMPLATE.md +++ /dev/null @@ -1,21 +0,0 @@ ---- -title: ⚠️ Bug report -labels: needs-triage ---- - -### Issue - -> Describe your issue here - ---- - -### Environment - - - - **Node.js Version**: `VERSION_HERE` - - - - **Redis Server Version**: `VERSION_HERE` - - - - **Platform**: `PLATFORM_HERE` diff --git a/.github/ISSUE_TEMPLATE/bug-report.md b/.github/ISSUE_TEMPLATE/bug-report.md new file mode 100644 index 00000000000..a7fae8eeb11 --- /dev/null +++ b/.github/ISSUE_TEMPLATE/bug-report.md @@ -0,0 +1,15 @@ +--- +name: Bug report +about: Create a report to help us improve +title: '' +labels: Bug +assignees: '' +--- + + + +**Environment:** + - **Node.js Version**: + - **Redis Server Version**: + - **Node Redis Version**: + - **Platform**: diff --git a/.github/ISSUE_TEMPLATE/feature-request.md b/.github/ISSUE_TEMPLATE/feature-request.md new file mode 100644 index 00000000000..0645d6e1a83 --- /dev/null +++ b/.github/ISSUE_TEMPLATE/feature-request.md @@ -0,0 +1,7 @@ +--- +name: Feature request +about: Suggest an idea for this project +title: '' +labels: Bug +assignees: '' +--- diff --git a/.npmignore b/.npmignore index fa2d8841124..e3fc1a7731d 100644 --- a/.npmignore +++ b/.npmignore @@ -1,12 +1,13 @@ .vscode/ .idea/ node_modules/ -.nyc_output +.nyc_output/ coverage/ dump.rdb documentation/ CONTRIBUTING.md tsconfig.json +.deepsource.toml .nycrc.json benchmark/ .github/ diff --git a/README.md b/README.md index c5f0ea1a1c9..c768b691d71 100644 --- a/README.md +++ b/README.md @@ -53,7 +53,7 @@ The above code connects to localhost on port 6379. To connect to a different hos ```typescript createClient({ - url: 'redis://alice:foobared@awesome.redis.server:6380', + url: 'redis://alice:foobared@awesome.redis.server:6380' }); ``` @@ -78,7 +78,7 @@ Modifiers to commands are specified using a JavaScript object: ```typescript await client.set('key', 'value', { EX: 10, - NX: true, + NX: true }); ``` @@ -181,12 +181,9 @@ for await (const key of client.scanIterator()) { This works with `HSCAN`, `SSCAN`, and `ZSCAN` too: ```typescript -for await (const member of client.hScanIterator('hash')) { -} -for await (const { field, value } of client.sScanIterator('set')) { -} -for await (const { member, score } of client.zScanIterator('sorted-set')) { -} +for await (const member of client.hScanIterator('hash')) {} +for await (const { field, value } of client.sScanIterator('set')) {} +for await (const { member, score } of client.zScanIterator('sorted-set')) {} ``` You can override the default options by providing a configuration object: @@ -204,7 +201,8 @@ client.scanIterator({ Define new functions using [Lua scripts](https://redis.io/commands/eval) which execute on the Redis server: ```typescript -import { createClient, defineScript } from 'redis'; +import { createClient } from 'redis'; +import { defineScript } from 'redis/lua-script'; (async () => { const client = createClient({ @@ -218,9 +216,9 @@ import { createClient, defineScript } from 'redis'; }, transformReply(reply: number): number { return reply; - }, - }), - }, + } + }) + } }); await client.connect(); @@ -241,14 +239,12 @@ import { createCluster } from 'redis'; const cluster = createCluster({ rootNodes: [ { - host: '10.0.0.1', - port: 30001, + url: 'redis://10.0.0.1:30001' }, { - host: '10.0.0.2', - port: 30002, - }, - ], + url: 'redis://10.0.0.2:30002' + } + ] }); cluster.on('error', (err) => console.log('Redis Cluster Error', err)); @@ -274,7 +270,7 @@ Of course, if you don't do something with your Promises you're certain to get [u ```typescript await Promise.all([ client.set('Tm9kZSBSZWRpcw==', 'users:1'), - client.sAdd('users:1:tokens', 'Tm9kZSBSZWRpcw=='), + client.sAdd('users:1:tokens', 'Tm9kZSBSZWRpcw==') ]); ``` diff --git a/SECURITY.md b/SECURITY.md new file mode 100644 index 00000000000..0839a123c96 --- /dev/null +++ b/SECURITY.md @@ -0,0 +1,16 @@ +# Security Policy + +## Supported Versions + +Node Redis is generally backwards compatible with very few exceptions, so we recommend users to always use the latest version to experience stability, performance and security. + +| Version | Supported | +| ------- | ------------------ | +| 4.0.x | :white_check_mark: | +| 3.1.x | :white_check_mark: | +| < 3.1 | :x: | + +## Reporting a Vulnerability + +If you believe you’ve discovered a serious vulnerability, please contact the Node Redis core team at redis@redis.io. We will evaluate your report and if necessary issue a fix and an advisory. If the issue was previously undisclosed, +we’ll also mention your name in the credits. diff --git a/benchmark/package-lock.json b/benchmark/package-lock.json index a16a420f8cb..4afaf8c305c 100644 --- a/benchmark/package-lock.json +++ b/benchmark/package-lock.json @@ -4,40 +4,42 @@ "requires": true, "packages": { "": { + "name": "benchmark", "license": "ISC", "dependencies": { - "@probe.gl/bench": "^3.4.0", - "benny": "^3.6.15", + "benny": "3.6.15", "v3": "npm:redis@3.1.2", "v4": "file:../" } }, "..": { "name": "redis", - "version": "4.0.0-next.5", + "version": "4.0.0-rc.2", "license": "MIT", "dependencies": { "cluster-key-slot": "1.1.0", + "generic-pool": "3.8.2", "redis-parser": "3.0.0", "yallist": "4.0.0" }, "devDependencies": { "@istanbuljs/nyc-config-typescript": "^1.0.1", + "@tsconfig/node12": "^1.0.9", "@types/mocha": "^9.0.0", - "@types/node": "^16.4.5", - "@types/sinon": "^10.0.2", + "@types/node": "^16.10.3", + "@types/sinon": "^10.0.4", "@types/which": "^2.0.1", "@types/yallist": "^4.0.1", - "mocha": "^9.0.3", + "mocha": "^9.1.2", "nyc": "^15.1.0", - "release-it": "^14.10.1", + "release-it": "^14.11.6", "sinon": "^11.1.2", - "source-map-support": "^0.5.19", - "ts-node": "^10.1.0", - "typedoc": "^0.21.4", - "typedoc-github-wiki-theme": "^0.5.1", - "typedoc-plugin-markdown": "^3.10.4", - "typescript": "^4.3.5", + "source-map-support": "^0.5.20", + "ts-node": "^10.3.0", + "typedoc": "^0.22.5", + "typedoc-github-wiki-theme": "^0.6.0", + "typedoc-plugin-markdown": "^3.11.3", + "typescript": "^4.4.3", "which": "^2.0.2" }, "engines": { @@ -81,34 +83,6 @@ "fast-deep-equal": "^3.1.1" } }, - "node_modules/@babel/runtime": { - "version": "7.14.8", - "resolved": "https://registry.npmjs.org/@babel/runtime/-/runtime-7.14.8.tgz", - "integrity": "sha512-twj3L8Og5SaCRCErB4x4ajbvBIVV77CGeFglHpeg5WC5FF8TZzBWXtTJ4MqaD9QszLYTtr+IsaAL2rEUevb+eg==", - "dependencies": { - "regenerator-runtime": "^0.13.4" - }, - "engines": { - "node": ">=6.9.0" - } - }, - "node_modules/@probe.gl/bench": { - "version": "3.4.0", - "resolved": "https://registry.npmjs.org/@probe.gl/bench/-/bench-3.4.0.tgz", - "integrity": "sha512-S7iNPz5G3zEfEP0S4SAMvtj+dwP7EWfVBaA8Cy5CVIgM1lnpUbXvqoAJxlVEedNC32Icxwq65XQheufy1Zzmug==", - "dependencies": { - "@babel/runtime": "^7.0.0", - "probe.gl": "3.4.0" - } - }, - "node_modules/@probe.gl/stats": { - "version": "3.4.0", - "resolved": "https://registry.npmjs.org/@probe.gl/stats/-/stats-3.4.0.tgz", - "integrity": "sha512-Gl37r9qGuiKadIvTZdSZvzCNOttJYw6RcY1oT0oDuB8r2uhuZAdSMQRQTy9FTinp6MY6O9wngGnV6EpQ8wSBAw==", - "dependencies": { - "@babel/runtime": "^7.0.0" - } - }, "node_modules/ansi-escapes": { "version": "4.3.2", "resolved": "https://registry.npmjs.org/ansi-escapes/-/ansi-escapes-4.3.2.tgz", @@ -124,9 +98,9 @@ } }, "node_modules/ansi-regex": { - "version": "5.0.0", - "resolved": "https://registry.npmjs.org/ansi-regex/-/ansi-regex-5.0.0.tgz", - "integrity": "sha512-bY6fj56OUQ0hU1KjFNDQuJFezqKdrAyFdIevADiqrWHwSlbmBNMHp5ak2f40Pm8JTFyM2mqxkG6ngkHO11f/lg==", + "version": "5.0.1", + "resolved": "https://registry.npmjs.org/ansi-regex/-/ansi-regex-5.0.1.tgz", + "integrity": "sha512-quJQXlTSUGL2LH9SUXo8VwsY4soanhgo6LNSm84E1LBcE8s3O0wpdiRzyR9z/ZZJMlMWv37qOOb9pdJlMUEKFQ==", "engines": { "node": ">=8" } @@ -223,9 +197,9 @@ } }, "node_modules/denque": { - "version": "1.5.0", - "resolved": "https://registry.npmjs.org/denque/-/denque-1.5.0.tgz", - "integrity": "sha512-CYiCSgIF1p6EUByQPlGkKnP1M9g0ZV3qMIrqMqZqdwazygIA/YP2vrbcyl1h/WppKJTdl1F85cXIle+394iDAQ==", + "version": "1.5.1", + "resolved": "https://registry.npmjs.org/denque/-/denque-1.5.1.tgz", + "integrity": "sha512-XwE+iZ4D6ZUB7mfYRMb5wByE8L74HCn30FBN7sWnXksWc1LO1bPDl67pBR9o/kC4z/xSNAwkMYcGgqDV3BE3Hw==", "engines": { "node": ">=0.10" } @@ -255,9 +229,9 @@ } }, "node_modules/graceful-fs": { - "version": "4.2.6", - "resolved": "https://registry.npmjs.org/graceful-fs/-/graceful-fs-4.2.6.tgz", - "integrity": "sha512-nTnJ528pbqxYanhpDYsi4Rd8MAeaBA67+RZ10CM1m3bTAVFEDcd5AuA4a6W5YkGZ1iNXHzZz8T6TBKLeBuNriQ==" + "version": "4.2.8", + "resolved": "https://registry.npmjs.org/graceful-fs/-/graceful-fs-4.2.8.tgz", + "integrity": "sha512-qkIilPUYcNhJpd33n0GBXTB1MMPp14TxEsEs0pTrsSVucApsYzW5V+Q8Qxhik6KU3evy+qkAAowTByymK0avdg==" }, "node_modules/is-fullwidth-code-point": { "version": "3.0.0", @@ -366,9 +340,9 @@ "integrity": "sha512-fnWVljUchTro6RiCFvCXBbNhJc2NijN7oIQxbwsyL0buWJPG85v81ehlHI9fXrJsMNgTofEoWIQeClKpgxFLrg==" }, "node_modules/prettier": { - "version": "2.3.2", - "resolved": "https://registry.npmjs.org/prettier/-/prettier-2.3.2.tgz", - "integrity": "sha512-lnJzDfJ66zkMy58OL5/NY5zp70S7Nz6KqcKkXYzn2tMVrNxvbqaBpg7H3qHaLxCJ5lNMsGuM8+ohS7cZrthdLQ==", + "version": "2.4.1", + "resolved": "https://registry.npmjs.org/prettier/-/prettier-2.4.1.tgz", + "integrity": "sha512-9fbDAXSBcc6Bs1mZrDYb3XKzDLm4EXXL9sC1LqKP5rZkT6KRr/rf9amVUcODVXgguK/isJz0d0hP72WeaKWsvA==", "bin": { "prettier": "bin-prettier.js" }, @@ -376,15 +350,6 @@ "node": ">=10.13.0" } }, - "node_modules/probe.gl": { - "version": "3.4.0", - "resolved": "https://registry.npmjs.org/probe.gl/-/probe.gl-3.4.0.tgz", - "integrity": "sha512-9CLByZATuhuG/Viq3ckfWU+dAhb7dMmjzsyCy4s7ds9ueTejcVRENxL197/XacOK/AN61YrEERB0QnouB0Qc0Q==", - "dependencies": { - "@babel/runtime": "^7.0.0", - "@probe.gl/stats": "3.4.0" - } - }, "node_modules/redis-commands": { "version": "1.7.0", "resolved": "https://registry.npmjs.org/redis-commands/-/redis-commands-1.7.0.tgz", @@ -409,11 +374,6 @@ "node": ">=4" } }, - "node_modules/regenerator-runtime": { - "version": "0.13.9", - "resolved": "https://registry.npmjs.org/regenerator-runtime/-/regenerator-runtime-0.13.9.tgz", - "integrity": "sha512-p3VT+cOEgxFsRRA9X4lkI1E+k2/CtnKtU4gcxyaCUreilL/vqI6CdZ3wxVUx3UOUg+gnUOQQcRI7BmSI656MYA==" - }, "node_modules/restore-cursor": { "version": "3.1.0", "resolved": "https://registry.npmjs.org/restore-cursor/-/restore-cursor-3.1.0.tgz", @@ -427,9 +387,9 @@ } }, "node_modules/signal-exit": { - "version": "3.0.3", - "resolved": "https://registry.npmjs.org/signal-exit/-/signal-exit-3.0.3.tgz", - "integrity": "sha512-VUJ49FC8U1OxwZLxIbTTrDvLnf/6TDgxZcK8wxR8zs13xpx7xbG60ndBlhNrFi2EMuFRoeDoJO7wthSLq42EjA==" + "version": "3.0.5", + "resolved": "https://registry.npmjs.org/signal-exit/-/signal-exit-3.0.5.tgz", + "integrity": "sha512-KWcOiKeQj6ZyXx7zq4YxSMgHRlod4czeBQZrPb8OKcohcqAXShm7E20kEMle9WBt26hFcAf0qLOcp5zmY7kOqQ==" }, "node_modules/slice-ansi": { "version": "4.0.0", @@ -453,24 +413,24 @@ "integrity": "sha512-IYsheLg6dasD3zT/w9+8Iq9tcIQqqu91ZIpJOnIEM25C3X/g4Tl8mhXwW2ZQpbrsJISr9+wizEYgsibN5/b32Q==" }, "node_modules/string-width": { - "version": "4.2.2", - "resolved": "https://registry.npmjs.org/string-width/-/string-width-4.2.2.tgz", - "integrity": "sha512-XBJbT3N4JhVumXE0eoLU9DCjcaF92KLNqTmFCnG1pf8duUxFGwtP6AD6nkjw9a3IdiRtL3E2w3JDiE/xi3vOeA==", + "version": "4.2.3", + "resolved": "https://registry.npmjs.org/string-width/-/string-width-4.2.3.tgz", + "integrity": "sha512-wKyQRQpjJ0sIp62ErSZdGsjMJWsap5oRNihHhu6G7JVO/9jIB6UyevL+tXuOqrng8j/cxKTWyWUwvSTriiZz/g==", "dependencies": { "emoji-regex": "^8.0.0", "is-fullwidth-code-point": "^3.0.0", - "strip-ansi": "^6.0.0" + "strip-ansi": "^6.0.1" }, "engines": { "node": ">=8" } }, "node_modules/strip-ansi": { - "version": "6.0.0", - "resolved": "https://registry.npmjs.org/strip-ansi/-/strip-ansi-6.0.0.tgz", - "integrity": "sha512-AuvKTrTfQNYNIctbR1K/YGTR1756GycPsg7b9bdV9Duqur4gv6aKqHXah67Z8ImS7WEz5QVcOtlfW2rZEugt6w==", + "version": "6.0.1", + "resolved": "https://registry.npmjs.org/strip-ansi/-/strip-ansi-6.0.1.tgz", + "integrity": "sha512-Y38VPSHcqkFrCpFnQ9vuSXmquuv5oXOKpGeT6aGrr3o3Gc9AlVa6JBfUSOCnbxGGZF+/0ooI7KrPuUSztUdU5A==", "dependencies": { - "ansi-regex": "^5.0.0" + "ansi-regex": "^5.0.1" }, "engines": { "node": ">=8" @@ -570,31 +530,6 @@ "fast-deep-equal": "^3.1.1" } }, - "@babel/runtime": { - "version": "7.14.8", - "resolved": "https://registry.npmjs.org/@babel/runtime/-/runtime-7.14.8.tgz", - "integrity": "sha512-twj3L8Og5SaCRCErB4x4ajbvBIVV77CGeFglHpeg5WC5FF8TZzBWXtTJ4MqaD9QszLYTtr+IsaAL2rEUevb+eg==", - "requires": { - "regenerator-runtime": "^0.13.4" - } - }, - "@probe.gl/bench": { - "version": "3.4.0", - "resolved": "https://registry.npmjs.org/@probe.gl/bench/-/bench-3.4.0.tgz", - "integrity": "sha512-S7iNPz5G3zEfEP0S4SAMvtj+dwP7EWfVBaA8Cy5CVIgM1lnpUbXvqoAJxlVEedNC32Icxwq65XQheufy1Zzmug==", - "requires": { - "@babel/runtime": "^7.0.0", - "probe.gl": "3.4.0" - } - }, - "@probe.gl/stats": { - "version": "3.4.0", - "resolved": "https://registry.npmjs.org/@probe.gl/stats/-/stats-3.4.0.tgz", - "integrity": "sha512-Gl37r9qGuiKadIvTZdSZvzCNOttJYw6RcY1oT0oDuB8r2uhuZAdSMQRQTy9FTinp6MY6O9wngGnV6EpQ8wSBAw==", - "requires": { - "@babel/runtime": "^7.0.0" - } - }, "ansi-escapes": { "version": "4.3.2", "resolved": "https://registry.npmjs.org/ansi-escapes/-/ansi-escapes-4.3.2.tgz", @@ -604,9 +539,9 @@ } }, "ansi-regex": { - "version": "5.0.0", - "resolved": "https://registry.npmjs.org/ansi-regex/-/ansi-regex-5.0.0.tgz", - "integrity": "sha512-bY6fj56OUQ0hU1KjFNDQuJFezqKdrAyFdIevADiqrWHwSlbmBNMHp5ak2f40Pm8JTFyM2mqxkG6ngkHO11f/lg==" + "version": "5.0.1", + "resolved": "https://registry.npmjs.org/ansi-regex/-/ansi-regex-5.0.1.tgz", + "integrity": "sha512-quJQXlTSUGL2LH9SUXo8VwsY4soanhgo6LNSm84E1LBcE8s3O0wpdiRzyR9z/ZZJMlMWv37qOOb9pdJlMUEKFQ==" }, "ansi-styles": { "version": "4.3.0", @@ -679,9 +614,9 @@ "integrity": "sha512-U7VdrJFnJgo4xjrHpTzu0yrHPGImdsmD95ZlgYSEajAn2JKzDhDTPG9kBTefmObL2w/ngeZnilk+OV9CG3d7UA==" }, "denque": { - "version": "1.5.0", - "resolved": "https://registry.npmjs.org/denque/-/denque-1.5.0.tgz", - "integrity": "sha512-CYiCSgIF1p6EUByQPlGkKnP1M9g0ZV3qMIrqMqZqdwazygIA/YP2vrbcyl1h/WppKJTdl1F85cXIle+394iDAQ==" + "version": "1.5.1", + "resolved": "https://registry.npmjs.org/denque/-/denque-1.5.1.tgz", + "integrity": "sha512-XwE+iZ4D6ZUB7mfYRMb5wByE8L74HCn30FBN7sWnXksWc1LO1bPDl67pBR9o/kC4z/xSNAwkMYcGgqDV3BE3Hw==" }, "emoji-regex": { "version": "8.0.0", @@ -705,9 +640,9 @@ } }, "graceful-fs": { - "version": "4.2.6", - "resolved": "https://registry.npmjs.org/graceful-fs/-/graceful-fs-4.2.6.tgz", - "integrity": "sha512-nTnJ528pbqxYanhpDYsi4Rd8MAeaBA67+RZ10CM1m3bTAVFEDcd5AuA4a6W5YkGZ1iNXHzZz8T6TBKLeBuNriQ==" + "version": "4.2.8", + "resolved": "https://registry.npmjs.org/graceful-fs/-/graceful-fs-4.2.8.tgz", + "integrity": "sha512-qkIilPUYcNhJpd33n0GBXTB1MMPp14TxEsEs0pTrsSVucApsYzW5V+Q8Qxhik6KU3evy+qkAAowTByymK0avdg==" }, "is-fullwidth-code-point": { "version": "3.0.0", @@ -783,18 +718,9 @@ "integrity": "sha512-fnWVljUchTro6RiCFvCXBbNhJc2NijN7oIQxbwsyL0buWJPG85v81ehlHI9fXrJsMNgTofEoWIQeClKpgxFLrg==" }, "prettier": { - "version": "2.3.2", - "resolved": "https://registry.npmjs.org/prettier/-/prettier-2.3.2.tgz", - "integrity": "sha512-lnJzDfJ66zkMy58OL5/NY5zp70S7Nz6KqcKkXYzn2tMVrNxvbqaBpg7H3qHaLxCJ5lNMsGuM8+ohS7cZrthdLQ==" - }, - "probe.gl": { - "version": "3.4.0", - "resolved": "https://registry.npmjs.org/probe.gl/-/probe.gl-3.4.0.tgz", - "integrity": "sha512-9CLByZATuhuG/Viq3ckfWU+dAhb7dMmjzsyCy4s7ds9ueTejcVRENxL197/XacOK/AN61YrEERB0QnouB0Qc0Q==", - "requires": { - "@babel/runtime": "^7.0.0", - "@probe.gl/stats": "3.4.0" - } + "version": "2.4.1", + "resolved": "https://registry.npmjs.org/prettier/-/prettier-2.4.1.tgz", + "integrity": "sha512-9fbDAXSBcc6Bs1mZrDYb3XKzDLm4EXXL9sC1LqKP5rZkT6KRr/rf9amVUcODVXgguK/isJz0d0hP72WeaKWsvA==" }, "redis-commands": { "version": "1.7.0", @@ -814,11 +740,6 @@ "redis-errors": "^1.0.0" } }, - "regenerator-runtime": { - "version": "0.13.9", - "resolved": "https://registry.npmjs.org/regenerator-runtime/-/regenerator-runtime-0.13.9.tgz", - "integrity": "sha512-p3VT+cOEgxFsRRA9X4lkI1E+k2/CtnKtU4gcxyaCUreilL/vqI6CdZ3wxVUx3UOUg+gnUOQQcRI7BmSI656MYA==" - }, "restore-cursor": { "version": "3.1.0", "resolved": "https://registry.npmjs.org/restore-cursor/-/restore-cursor-3.1.0.tgz", @@ -829,9 +750,9 @@ } }, "signal-exit": { - "version": "3.0.3", - "resolved": "https://registry.npmjs.org/signal-exit/-/signal-exit-3.0.3.tgz", - "integrity": "sha512-VUJ49FC8U1OxwZLxIbTTrDvLnf/6TDgxZcK8wxR8zs13xpx7xbG60ndBlhNrFi2EMuFRoeDoJO7wthSLq42EjA==" + "version": "3.0.5", + "resolved": "https://registry.npmjs.org/signal-exit/-/signal-exit-3.0.5.tgz", + "integrity": "sha512-KWcOiKeQj6ZyXx7zq4YxSMgHRlod4czeBQZrPb8OKcohcqAXShm7E20kEMle9WBt26hFcAf0qLOcp5zmY7kOqQ==" }, "slice-ansi": { "version": "4.0.0", @@ -849,21 +770,21 @@ "integrity": "sha512-IYsheLg6dasD3zT/w9+8Iq9tcIQqqu91ZIpJOnIEM25C3X/g4Tl8mhXwW2ZQpbrsJISr9+wizEYgsibN5/b32Q==" }, "string-width": { - "version": "4.2.2", - "resolved": "https://registry.npmjs.org/string-width/-/string-width-4.2.2.tgz", - "integrity": "sha512-XBJbT3N4JhVumXE0eoLU9DCjcaF92KLNqTmFCnG1pf8duUxFGwtP6AD6nkjw9a3IdiRtL3E2w3JDiE/xi3vOeA==", + "version": "4.2.3", + "resolved": "https://registry.npmjs.org/string-width/-/string-width-4.2.3.tgz", + "integrity": "sha512-wKyQRQpjJ0sIp62ErSZdGsjMJWsap5oRNihHhu6G7JVO/9jIB6UyevL+tXuOqrng8j/cxKTWyWUwvSTriiZz/g==", "requires": { "emoji-regex": "^8.0.0", "is-fullwidth-code-point": "^3.0.0", - "strip-ansi": "^6.0.0" + "strip-ansi": "^6.0.1" } }, "strip-ansi": { - "version": "6.0.0", - "resolved": "https://registry.npmjs.org/strip-ansi/-/strip-ansi-6.0.0.tgz", - "integrity": "sha512-AuvKTrTfQNYNIctbR1K/YGTR1756GycPsg7b9bdV9Duqur4gv6aKqHXah67Z8ImS7WEz5QVcOtlfW2rZEugt6w==", + "version": "6.0.1", + "resolved": "https://registry.npmjs.org/strip-ansi/-/strip-ansi-6.0.1.tgz", + "integrity": "sha512-Y38VPSHcqkFrCpFnQ9vuSXmquuv5oXOKpGeT6aGrr3o3Gc9AlVa6JBfUSOCnbxGGZF+/0ooI7KrPuUSztUdU5A==", "requires": { - "ansi-regex": "^5.0.0" + "ansi-regex": "^5.0.1" } }, "type-fest": { @@ -891,23 +812,25 @@ "version": "file:..", "requires": { "@istanbuljs/nyc-config-typescript": "^1.0.1", + "@tsconfig/node12": "^1.0.9", "@types/mocha": "^9.0.0", - "@types/node": "^16.4.5", - "@types/sinon": "^10.0.2", + "@types/node": "^16.10.3", + "@types/sinon": "^10.0.4", "@types/which": "^2.0.1", "@types/yallist": "^4.0.1", "cluster-key-slot": "1.1.0", - "mocha": "^9.0.3", + "generic-pool": "3.8.2", + "mocha": "^9.1.2", "nyc": "^15.1.0", "redis-parser": "3.0.0", - "release-it": "^14.10.1", + "release-it": "^14.11.6", "sinon": "^11.1.2", - "source-map-support": "^0.5.19", - "ts-node": "^10.1.0", - "typedoc": "^0.21.4", - "typedoc-github-wiki-theme": "^0.5.1", - "typedoc-plugin-markdown": "^3.10.4", - "typescript": "^4.3.5", + "source-map-support": "^0.5.20", + "ts-node": "^10.3.0", + "typedoc": "^0.22.5", + "typedoc-github-wiki-theme": "^0.6.0", + "typedoc-plugin-markdown": "^3.11.3", + "typescript": "^4.4.3", "which": "^2.0.2", "yallist": "4.0.0" } diff --git a/benchmark/package.json b/benchmark/package.json index 5226a5b0c89..ab874090c4b 100644 --- a/benchmark/package.json +++ b/benchmark/package.json @@ -10,7 +10,7 @@ "author": "", "license": "ISC", "dependencies": { - "benny": "^3.6.15", + "benny": "3.6.15", "v3": "npm:redis@3.1.2", "v4": "file:../" } diff --git a/index.ts b/index.ts index 408cbe3b996..fb448f989c7 100644 --- a/index.ts +++ b/index.ts @@ -6,5 +6,3 @@ export const createClient = RedisClient.create; export const commandOptions = RedisClient.commandOptions; export const createCluster = RedisCluster.create; - -export { defineScript } from './lib/lua-script'; diff --git a/lib/commands-queue.ts b/lib/client/commands-queue.ts similarity index 97% rename from lib/commands-queue.ts rename to lib/client/commands-queue.ts index ef87184193f..791c7638bad 100644 --- a/lib/commands-queue.ts +++ b/lib/client/commands-queue.ts @@ -1,7 +1,7 @@ import LinkedList from 'yallist'; import RedisParser from 'redis-parser'; -import { AbortError } from './errors'; -import { RedisReply } from './commands'; +import { AbortError } from '../errors'; +import { RedisCommandRawReply } from '../commands'; export interface QueueCommandOptions { asap?: boolean; @@ -107,7 +107,7 @@ export default class RedisCommandsQueue { this.#maxLength = maxLength; } - addCommand(args: Array, options?: QueueCommandOptions, bufferMode?: boolean): Promise { + addCommand(args: Array, options?: QueueCommandOptions, bufferMode?: boolean): Promise { if (this.#pubSubState.subscribing || this.#pubSubState.subscribed) { return Promise.reject(new Error('Cannot send commands in PubSub mode')); } else if (this.#maxLength && this.#waitingToBeSent.length + this.#waitingForReply.length >= this.#maxLength) { diff --git a/lib/client/commands.ts b/lib/client/commands.ts new file mode 100644 index 00000000000..c34f34be4fa --- /dev/null +++ b/lib/client/commands.ts @@ -0,0 +1,233 @@ +import CLUSTER_COMMANDS from '../cluster/commands'; +import * as ACL_CAT from '../commands/ACL_CAT'; +import * as ACL_DELUSER from '../commands/ACL_DELUSER'; +import * as ACL_GENPASS from '../commands/ACL_GENPASS'; +import * as ACL_GETUSER from '../commands/ACL_GETUSER'; +import * as ACL_LIST from '../commands/ACL_LIST'; +import * as ACL_LOAD from '../commands/ACL_LOAD'; +import * as ACL_LOG_RESET from '../commands/ACL_LOG_RESET'; +import * as ACL_LOG from '../commands/ACL_LOG'; +import * as ACL_SAVE from '../commands/ACL_SAVE'; +import * as ACL_SETUSER from '../commands/ACL_SETUSER'; +import * as ACL_USERS from '../commands/ACL_USERS'; +import * as ACL_WHOAMI from '../commands/ACL_WHOAMI'; +import * as ASKING from '../commands/ASKING'; +import * as AUTH from '../commands/AUTH'; +import * as BGREWRITEAOF from '../commands/BGREWRITEAOF'; +import * as BGSAVE from '../commands/BGSAVE'; +import * as CLIENT_ID from '../commands/CLIENT_ID'; +import * as CLIENT_INFO from '../commands/CLIENT_INFO'; +import * as CLUSTER_ADDSLOTS from '../commands/CLUSTER_ADDSLOTS'; +import * as CLUSTER_FLUSHSLOTS from '../commands/CLUSTER_FLUSHSLOTS'; +import * as CLUSTER_INFO from '../commands/CLUSTER_INFO'; +import * as CLUSTER_NODES from '../commands/CLUSTER_NODES'; +import * as CLUSTER_MEET from '../commands/CLUSTER_MEET'; +import * as CLUSTER_RESET from '../commands/CLUSTER_RESET'; +import * as CLUSTER_SETSLOT from '../commands/CLUSTER_SETSLOT'; +import * as CLUSTER_SLOTS from '../commands/CLUSTER_SLOTS'; +import * as COMMAND_COUNT from '../commands/COMMAND_COUNT'; +import * as COMMAND_GETKEYS from '../commands/COMMAND_GETKEYS'; +import * as COMMAND_INFO from '../commands/COMMAND_INFO'; +import * as COMMAND from '../commands/COMMAND'; +import * as CONFIG_GET from '../commands/CONFIG_GET'; +import * as CONFIG_RESETASTAT from '../commands/CONFIG_RESETSTAT'; +import * as CONFIG_REWRITE from '../commands/CONFIG_REWRITE'; +import * as CONFIG_SET from '../commands/CONFIG_SET'; +import * as DBSIZE from '../commands/DBSIZE'; +import * as DISCARD from '../commands/DISCARD'; +import * as ECHO from '../commands/ECHO'; +import * as FAILOVER from '../commands/FAILOVER'; +import * as FLUSHALL from '../commands/FLUSHALL'; +import * as FLUSHDB from '../commands/FLUSHDB'; +import * as HELLO from '../commands/HELLO'; +import * as INFO from '../commands/INFO'; +import * as KEYS from '../commands/KEYS'; +import * as LASTSAVE from '../commands/LASTSAVE'; +import * as LOLWUT from '../commands/LOLWUT'; +import * as MEMOERY_DOCTOR from '../commands/MEMORY_DOCTOR'; +import * as MEMORY_MALLOC_STATS from '../commands/MEMORY_MALLOC-STATS'; +import * as MEMORY_PURGE from '../commands/MEMORY_PURGE'; +import * as MEMORY_STATS from '../commands/MEMORY_STATS'; +import * as MEMORY_USAGE from '../commands/MEMORY_USAGE'; +import * as MODULE_LIST from '../commands/MODULE_LIST'; +import * as MODULE_LOAD from '../commands/MODULE_LOAD'; +import * as MODULE_UNLOAD from '../commands/MODULE_UNLOAD'; +import * as MOVE from '../commands/MOVE'; +import * as PING from '../commands/PING'; +import * as PUBSUB_CHANNELS from '../commands/PUBSUB_CHANNELS'; +import * as PUBSUB_NUMPAT from '../commands/PUBSUB_NUMPAT'; +import * as PUBSUB_NUMSUB from '../commands/PUBSUB_NUMSUB'; +import * as RANDOMKEY from '../commands/RANDOMKEY'; +import * as READONLY from '../commands/READONLY'; +import * as READWRITE from '../commands/READWRITE'; +import * as REPLICAOF from '../commands/REPLICAOF'; +import * as RESTORE_ASKING from '../commands/RESTORE-ASKING'; +import * as ROLE from '../commands/ROLE'; +import * as SAVE from '../commands/SAVE'; +import * as SCAN from '../commands/SCAN'; +import * as SCRIPT_DEBUG from '../commands/SCRIPT_DEBUG'; +import * as SCRIPT_EXISTS from '../commands/SCRIPT_EXISTS'; +import * as SCRIPT_FLUSH from '../commands/SCRIPT_FLUSH'; +import * as SCRIPT_KILL from '../commands/SCRIPT_KILL'; +import * as SCRIPT_LOAD from '../commands/SCRIPT_LOAD'; +import * as SHUTDOWN from '../commands/SHUTDOWN'; +import * as SWAPDB from '../commands/SWAPDB'; +import * as TIME from '../commands/TIME'; +import * as UNWATCH from '../commands/UNWATCH'; +import * as WAIT from '../commands/WAIT'; + +export default { + ...CLUSTER_COMMANDS, + ACL_CAT, + aclCat: ACL_CAT, + ACL_DELUSER, + aclDelUser: ACL_DELUSER, + ACL_GENPASS, + aclGenPass: ACL_GENPASS, + ACL_GETUSER, + aclGetUser: ACL_GETUSER, + ACL_LIST, + aclList: ACL_LIST, + ACL_LOAD, + aclLoad: ACL_LOAD, + ACL_LOG_RESET, + aclLogReset: ACL_LOG_RESET, + ACL_LOG, + aclLog: ACL_LOG, + ACL_SAVE, + aclSave: ACL_SAVE, + ACL_SETUSER, + aclSetUser: ACL_SETUSER, + ACL_USERS, + aclUsers: ACL_USERS, + ACL_WHOAMI, + aclWhoAmI: ACL_WHOAMI, + ASKING, + asking: ASKING, + AUTH, + auth: AUTH, + BGREWRITEAOF, + bgRewriteAof: BGREWRITEAOF, + BGSAVE, + bgSave: BGSAVE, + CLIENT_ID, + clientId: CLIENT_ID, + CLIENT_INFO, + clientInfo: CLIENT_INFO, + CLUSTER_ADDSLOTS, + clusterAddSlots: CLUSTER_ADDSLOTS, + CLUSTER_FLUSHSLOTS, + clusterFlushSlots: CLUSTER_FLUSHSLOTS, + CLUSTER_INFO, + clusterInfo: CLUSTER_INFO, + CLUSTER_NODES, + clusterNodes: CLUSTER_NODES, + CLUSTER_MEET, + clusterMeet: CLUSTER_MEET, + CLUSTER_RESET, + clusterReset: CLUSTER_RESET, + CLUSTER_SETSLOT, + clusterSetSlot: CLUSTER_SETSLOT, + CLUSTER_SLOTS, + clusterSlots: CLUSTER_SLOTS, + COMMAND_COUNT, + commandCount: COMMAND_COUNT, + COMMAND_GETKEYS, + commandGetKeys: COMMAND_GETKEYS, + COMMAND_INFO, + commandInfo: COMMAND_INFO, + COMMAND, + command: COMMAND, + CONFIG_GET, + configGet: CONFIG_GET, + CONFIG_RESETASTAT, + configResetStat: CONFIG_RESETASTAT, + CONFIG_REWRITE, + configRewrite: CONFIG_REWRITE, + CONFIG_SET, + configSet: CONFIG_SET, + DBSIZE, + dbSize: DBSIZE, + DISCARD, + discard: DISCARD, + ECHO, + echo: ECHO, + FAILOVER, + failover: FAILOVER, + FLUSHALL, + flushAll: FLUSHALL, + FLUSHDB, + flushDb: FLUSHDB, + HELLO, + hello: HELLO, + INFO, + info: INFO, + KEYS, + keys: KEYS, + LASTSAVE, + lastSave: LASTSAVE, + LOLWUT, + lolwut: LOLWUT, + MEMOERY_DOCTOR, + memoryDoctor: MEMOERY_DOCTOR, + 'MEMORY_MALLOC-STATS': MEMORY_MALLOC_STATS, + memoryMallocStats: MEMORY_MALLOC_STATS, + MEMORY_PURGE, + memoryPurge: MEMORY_PURGE, + MEMORY_STATS, + memoryStats: MEMORY_STATS, + MEMORY_USAGE, + memoryUsage: MEMORY_USAGE, + MODULE_LIST, + moduleList: MODULE_LIST, + MODULE_LOAD, + moduleLoad: MODULE_LOAD, + MODULE_UNLOAD, + moduleUnload: MODULE_UNLOAD, + MOVE, + move: MOVE, + PING, + ping: PING, + PUBSUB_CHANNELS, + pubSubChannels: PUBSUB_CHANNELS, + PUBSUB_NUMPAT, + pubSubNumPat: PUBSUB_NUMPAT, + PUBSUB_NUMSUB, + pubSubNumSub: PUBSUB_NUMSUB, + RANDOMKEY, + randomKey: RANDOMKEY, + READONLY, + readonly: READONLY, + READWRITE, + readwrite: READWRITE, + REPLICAOF, + replicaOf: REPLICAOF, + 'RESTORE-ASKING': RESTORE_ASKING, + restoreAsking: RESTORE_ASKING, + ROLE, + role: ROLE, + SAVE, + save: SAVE, + SCAN, + scan: SCAN, + SCRIPT_DEBUG, + scriptDebug: SCRIPT_DEBUG, + SCRIPT_EXISTS, + scriptExists: SCRIPT_EXISTS, + SCRIPT_FLUSH, + scriptFlush: SCRIPT_FLUSH, + SCRIPT_KILL, + scriptKill: SCRIPT_KILL, + SCRIPT_LOAD, + scriptLoad: SCRIPT_LOAD, + SHUTDOWN, + shutdown: SHUTDOWN, + SWAPDB, + swapDb: SWAPDB, + TIME, + time: TIME, + UNWATCH, + unwatch: UNWATCH, + WAIT, + wait: WAIT, +}; diff --git a/lib/client.spec.ts b/lib/client/index.spec.ts similarity index 97% rename from lib/client.spec.ts rename to lib/client/index.spec.ts index 2cf6ea4964e..e98814d0582 100644 --- a/lib/client.spec.ts +++ b/lib/client/index.spec.ts @@ -1,11 +1,11 @@ import { strict as assert, AssertionError } from 'assert'; import { once } from 'events'; -import { itWithClient, TEST_REDIS_SERVERS, TestRedisServers, waitTillBeenCalled, isRedisVersionGreaterThan } from './test-utils'; -import RedisClient from './client'; -import { AbortError, ClientClosedError, ConnectionTimeoutError, WatchError } from './errors'; -import { defineScript } from './lua-script'; +import { itWithClient, TEST_REDIS_SERVERS, TestRedisServers, waitTillBeenCalled, isRedisVersionGreaterThan } from '../test-utils'; +import RedisClient from '.'; +import { AbortError, ClientClosedError, ConnectionTimeoutError, WatchError } from '../errors'; +import { defineScript } from '../lua-script'; import { spy } from 'sinon'; -import { RedisNetSocketOptions } from './socket'; +import { RedisNetSocketOptions } from '../client/socket'; export const SQUARE_SCRIPT = defineScript({ NUMBER_OF_KEYS: 0, @@ -366,6 +366,15 @@ describe('Client', () => { WatchError ); }); + + itWithClient(TestRedisServers.OPEN, 'execAsPipeline', async client => { + assert.deepEqual( + await client.multi() + .ping() + .exec(true), + ['PONG'] + ); + }); }); it('scripts', async () => { diff --git a/lib/client.ts b/lib/client/index.ts similarity index 79% rename from lib/client.ts rename to lib/client/index.ts index 93afee1ff1a..1a5384f509f 100644 --- a/lib/client.ts +++ b/lib/client/index.ts @@ -1,79 +1,81 @@ +import COMMANDS from './commands'; +import { RedisCommand, RedisCommandArguments, RedisCommandRawReply, RedisCommandReply, RedisModules, RedisPlugins, RedisScript, RedisScripts } from '../commands'; import RedisSocket, { RedisSocketOptions, RedisNetSocketOptions, RedisTlsSocketOptions } from './socket'; import RedisCommandsQueue, { PubSubListener, PubSubSubscribeCommands, PubSubUnsubscribeCommands, QueueCommandOptions } from './commands-queue'; -import COMMANDS, { TransformArgumentsReply } from './commands'; -import { RedisCommand, RedisModules, RedisReply } from './commands'; -import RedisMultiCommand, { MultiQueuedCommand, RedisMultiCommandType } from './multi-command'; +import RedisClientMultiCommand, { RedisClientMultiCommandType } from './multi-command'; +import { RedisMultiQueuedCommand } from '../multi-command'; import EventEmitter from 'events'; -import { CommandOptions, commandOptions, isCommandOptions } from './command-options'; -import { RedisLuaScript, RedisLuaScripts } from './lua-script'; -import { ScanOptions, ZMember } from './commands/generic-transformers'; -import { ScanCommandOptions } from './commands/SCAN'; -import { HScanTuple } from './commands/HSCAN'; -import { encodeCommand, extendWithDefaultCommands, extendWithModulesAndScripts, transformCommandArguments } from './commander'; +import { CommandOptions, commandOptions, isCommandOptions } from '../command-options'; +import { ScanOptions, ZMember } from '../commands/generic-transformers'; +import { ScanCommandOptions } from '../commands/SCAN'; +import { HScanTuple } from '../commands/HSCAN'; +import { encodeCommand, extendWithCommands, extendWithModulesAndScripts, transformCommandArguments, transformCommandReply } from '../commander'; import { Pool, Options as PoolOptions, createPool } from 'generic-pool'; -import { ClientClosedError } from './errors'; +import { ClientClosedError } from '../errors'; import { URL } from 'url'; -export interface RedisClientOptions { +export interface RedisClientOptions extends RedisPlugins { url?: string; socket?: RedisSocketOptions; username?: string; password?: string; database?: number; - modules?: M; - scripts?: S; commandsQueueMaxLength?: number; readonly?: boolean; legacyMode?: boolean; isolationPoolOptions?: PoolOptions; } -export type RedisCommandSignature = - (...args: Parameters | [options: CommandOptions, ...rest: Parameters]) => Promise>; +export type RedisClientCommandSignature = + (...args: Parameters | [options: CommandOptions, ...rest: Parameters]) => Promise>; type WithCommands = { - [P in keyof typeof COMMANDS]: RedisCommandSignature<(typeof COMMANDS)[P]>; + [P in keyof typeof COMMANDS]: RedisClientCommandSignature<(typeof COMMANDS)[P]>; }; -type WithModules = { +export type WithModules = { [P in keyof M]: { - [C in keyof M[P]]: RedisCommandSignature; + [C in keyof M[P]]: RedisClientCommandSignature; }; }; -type WithScripts = { - [P in keyof S]: RedisCommandSignature; +export type WithScripts = { + [P in keyof S]: RedisClientCommandSignature; }; -export type WithPlugins = - WithCommands & WithModules & WithScripts; +export type RedisClientType = + RedisClient & WithCommands & WithModules & WithScripts; -export type RedisClientType = - WithPlugins & RedisClient; +export type InstantiableRedisClient = + new (...args: ConstructorParameters) => RedisClientType; export interface ClientCommandOptions extends QueueCommandOptions { isolated?: boolean; } -export default class RedisClient extends EventEmitter { +export default class RedisClient extends EventEmitter { static commandOptions(options: ClientCommandOptions): CommandOptions { return commandOptions(options); } - static create(options?: RedisClientOptions): RedisClientType { - const Client = (extendWithModulesAndScripts({ + static extend(plugins?: RedisPlugins): InstantiableRedisClient { + const Client = extendWithModulesAndScripts({ BaseClass: RedisClient, - modules: options?.modules, + modules: plugins?.modules, modulesCommandsExecutor: RedisClient.prototype.commandsExecutor, - scripts: options?.scripts, + scripts: plugins?.scripts, scriptsExecutor: RedisClient.prototype.scriptsExecutor - })); + }); if (Client !== RedisClient) { - Client.prototype.Multi = RedisMultiCommand.extend(options); + Client.prototype.Multi = RedisClientMultiCommand.extend(plugins); } - return new Client(options); + return Client; + } + + static create(options?: RedisClientOptions): RedisClientType { + return new (RedisClient.extend(options))(options); } static parseURL(url: string): RedisClientOptions<{}, {}> { @@ -145,7 +147,9 @@ export default class RedisClient { - const duplicate = this.duplicate(); + const duplicate = this.duplicate({ + isolationPoolOptions: undefined + }); await duplicate.connect(); return duplicate; }, @@ -267,29 +271,33 @@ export default class RedisClient { - return new (Object.getPrototypeOf(this).constructor)(this.#options); + duplicate(overrides?: Partial>): RedisClientType { + return new (Object.getPrototypeOf(this).constructor)({ + ...this.#options, + ...overrides + }); } async connect(): Promise { await this.#socket.connect(); } - async commandsExecutor(command: RedisCommand, args: Array): Promise> { + async commandsExecutor(command: RedisCommand, args: Array): Promise> { const { args: redisArgs, options } = transformCommandArguments(command, args); - return command.transformReply( + return transformCommandReply( + command, await this.#sendCommand(redisArgs, options, command.BUFFER_MODE), - redisArgs.preserve, + redisArgs.preserve ); } - sendCommand(args: TransformArgumentsReply, options?: ClientCommandOptions, bufferMode?: boolean): Promise { + sendCommand(args: RedisCommandArguments, options?: ClientCommandOptions, bufferMode?: boolean): Promise { return this.#sendCommand(args, options, bufferMode); } // using `#sendCommand` cause `sendCommand` is overwritten in legacy mode - async #sendCommand(args: TransformArgumentsReply, options?: ClientCommandOptions, bufferMode?: boolean): Promise { + async #sendCommand(args: RedisCommandArguments, options?: ClientCommandOptions, bufferMode?: boolean): Promise { if (!this.#socket.isOpen) { throw new ClientClosedError(); } @@ -308,16 +316,17 @@ export default class RedisClient): Promise> { + async scriptsExecutor(script: RedisScript, args: Array): Promise> { const { args: redisArgs, options } = transformCommandArguments(script, args); - return script.transformReply( + return transformCommandReply( + script, await this.executeScript(script, redisArgs, options, script.BUFFER_MODE), redisArgs.preserve ); } - async executeScript(script: RedisLuaScript, args: TransformArgumentsReply, options?: ClientCommandOptions, bufferMode?: boolean): Promise> { + async executeScript(script: RedisScript, args: RedisCommandArguments, options?: ClientCommandOptions, bufferMode?: boolean): Promise> { try { return await this.#sendCommand([ 'EVALSHA', @@ -339,8 +348,6 @@ export default class RedisClient; async SELECT(options: CommandOptions, db: number): Promise; async SELECT(options?: any, db?: any): Promise { @@ -427,14 +434,14 @@ export default class RedisClient { + multi(): RedisClientMultiCommandType { return new (this as any).Multi( - this.#multiExecutor.bind(this), - this.#options + this.multiExecutor.bind(this), + this.#options?.legacyMode ); } - #multiExecutor(commands: Array, chainId?: symbol): Promise> { + multiExecutor(commands: Array, chainId?: symbol): Promise> { const promise = Promise.all( commands.map(({ args }) => { return this.#queue.addCommand(args, RedisClient.commandOptions({ @@ -506,5 +513,9 @@ export default class RedisClient = + (...args: Parameters) => RedisClientMultiCommandType; + +type WithCommands = { + [P in keyof typeof COMMANDS]: RedisClientMultiCommandSignature<(typeof COMMANDS)[P], M, S> +}; + +type WithModules = { + [P in keyof M]: { + [C in keyof M[P]]: RedisClientMultiCommandSignature; + }; +}; + +type WithScripts = { + [P in keyof S]: RedisClientMultiCommandSignature +}; + +export type RedisClientMultiCommandType = + RedisClientMultiCommand & WithCommands & WithModules & WithScripts; + +export type RedisClientMultiExecutor = (queue: Array, chainId?: symbol) => Promise>; + +export default class RedisClientMultiCommand { + readonly #multi = new RedisMultiCommand(); + readonly #executor: RedisClientMultiExecutor; + + static extend( + plugins?: RedisPlugins + ): new (...args: ConstructorParameters) => RedisClientMultiCommandType { + return extendWithModulesAndScripts({ + BaseClass: RedisClientMultiCommand, + modules: plugins?.modules, + modulesCommandsExecutor: RedisClientMultiCommand.prototype.commandsExecutor, + scripts: plugins?.scripts, + scriptsExecutor: RedisClientMultiCommand.prototype.scriptsExecutor + }); + } + + readonly v4: Record = {}; + + constructor(executor: RedisClientMultiExecutor, legacyMode = false) { + this.#executor = executor; + if (legacyMode) { + this.#legacyMode(); + } + } + + #legacyMode(): void { + this.v4.addCommand = this.addCommand.bind(this); + (this as any).addCommand = (...args: Array>): this => { + this.#multi.addCommand(args.flat()); + return this; + }; + this.v4.exec = this.exec.bind(this); + (this as any).exec = (callback?: (err: Error | null, replies?: Array) => unknown): void => { + this.v4.exec() + .then((reply: Array) => { + if (!callback) return; + + callback(null, reply); + }) + .catch((err: Error) => { + if (!callback) { + // this.emit('error', err); + return; + } + + callback(err); + }); + }; + + for (const name of Object.keys(COMMANDS)) { + this.#defineLegacyCommand(name); + } + } + + #defineLegacyCommand(name: string): void { + (this as any).v4[name] = (this as any)[name].bind(this.v4); + (this as any)[name] = (...args: Array): void => (this as any).addCommand(name, args); + } + + commandsExecutor(command: RedisCommand, args: Array): this { + return this.addCommand( + command.transformArguments(...args), + command.transformReply + ); + } + + addCommand(args: RedisCommandArguments, transformReply?: RedisCommand['transformReply']): this { + this.#multi.addCommand(args, transformReply); + return this; + } + + scriptsExecutor(script: RedisScript, args: Array): this { + this.#multi.addScript(script, args); + return this; + } + + async exec(execAsPipeline = false): Promise> { + if (execAsPipeline) { + return this.execAsPipeline(); + } + + const commands = this.#multi.exec(); + if (!commands) return []; + + return this.#multi.handleExecReplies( + await this.#executor(commands, RedisMultiCommand.generateChainId()) + ); + } + + EXEC = this.exec; + + async execAsPipeline(): Promise> { + if (!this.#multi.queue.length) return []; + + return this.#multi.transformReplies( + await this.#executor(this.#multi.queue) + ); + } +} + +extendWithCommands({ + BaseClass: RedisClientMultiCommand, + commands: COMMANDS, + executor: RedisClientMultiCommand.prototype.commandsExecutor +}); diff --git a/lib/socket.spec.ts b/lib/client/socket.spec.ts similarity index 100% rename from lib/socket.spec.ts rename to lib/client/socket.spec.ts diff --git a/lib/socket.ts b/lib/client/socket.ts similarity index 98% rename from lib/socket.ts rename to lib/client/socket.ts index 8bc94c5ba07..ca48ad4d542 100644 --- a/lib/socket.ts +++ b/lib/client/socket.ts @@ -1,8 +1,8 @@ import EventEmitter from 'events'; import net from 'net'; import tls from 'tls'; -import { ConnectionTimeoutError, ClientClosedError } from './errors'; -import { promiseTimeout } from './utils'; +import { ConnectionTimeoutError, ClientClosedError } from '../errors'; +import { promiseTimeout } from '../utils'; export interface RedisSocketCommonOptions { connectTimeout?: number; diff --git a/lib/cluster-slots.ts b/lib/cluster/cluster-slots.ts similarity index 77% rename from lib/cluster-slots.ts rename to lib/cluster/cluster-slots.ts index a5155cc53db..63834d4b4ca 100644 --- a/lib/cluster-slots.ts +++ b/lib/cluster/cluster-slots.ts @@ -1,17 +1,15 @@ import calculateSlot from 'cluster-key-slot'; -import RedisClient, { RedisClientType } from './client'; -import { RedisSocketOptions } from './socket'; -import { RedisClusterMasterNode, RedisClusterReplicaNode } from './commands/CLUSTER_NODES'; -import { RedisClusterOptions } from './cluster'; -import { RedisModules } from './commands'; -import { RedisLuaScripts } from './lua-script'; - -export interface ClusterNode { +import RedisClient, { InstantiableRedisClient, RedisClientType } from '../client'; +import { RedisClusterMasterNode, RedisClusterReplicaNode } from '../commands/CLUSTER_NODES'; +import { RedisClusterClientOptions, RedisClusterOptions } from '.'; +import { RedisModules, RedisScripts } from '../commands'; + +export interface ClusterNode { id: string; client: RedisClientType; } -interface SlotNodes { +interface SlotNodes { master: ClusterNode; replicas: Array>; clientIterator: IterableIterator> | undefined; @@ -19,41 +17,41 @@ interface SlotNodes { type OnError = (err: unknown) => void; -export default class RedisClusterSlots { - readonly #options: RedisClusterOptions; +export default class RedisClusterSlots { + readonly #options: RedisClusterOptions; + readonly #Client: InstantiableRedisClient; readonly #onError: OnError; readonly #nodeByUrl = new Map>(); readonly #slots: Array> = []; - constructor(options: RedisClusterOptions, onError: OnError) { + constructor(options: RedisClusterOptions, onError: OnError) { this.#options = options; + this.#Client = RedisClient.extend(options); this.#onError = onError; } async connect(): Promise { for (const rootNode of this.#options.rootNodes) { - if (await this.#discoverNodes(rootNode)) return; + if (await this.#discoverNodes(this.#clientOptionsDefaults(rootNode))) return; } throw new Error('None of the root nodes is available'); } async discover(startWith: RedisClientType): Promise { - if (await this.#discoverNodes(startWith.options?.socket)) return; + if (await this.#discoverNodes(startWith.options)) return; for (const { client } of this.#nodeByUrl.values()) { if (client === startWith) continue; - if (await this.#discoverNodes(client.options?.socket)) return; + if (await this.#discoverNodes(client.options)) return; } throw new Error('None of the cluster nodes is available'); } - async #discoverNodes(socketOptions?: RedisSocketOptions): Promise { - const client = RedisClient.create({ - socket: socketOptions - }); + async #discoverNodes(clientOptions?: RedisClusterClientOptions): Promise { + const client = new this.#Client(clientOptions); await client.connect(); @@ -101,6 +99,18 @@ export default class RedisClusterSlots, promises: Array>): ClusterNode { const url = `${nodeData.host}:${nodeData.port}`; clientsInUse.add(url); @@ -109,13 +119,15 @@ export default class RedisClusterSlots { it('sendCommand', async () => { const cluster = RedisCluster.create({ - rootNodes: TEST_REDIS_CLUSTERES[TestRedisClusters.OPEN], + ...TEST_REDIS_CLUSTERES[TestRedisClusters.OPEN], useReplicas: true }); await cluster.connect(); try { - await cluster.ping(); + await cluster.publish('channel', 'message'); await cluster.set('a', 'b'); await cluster.set('a{a}', 'bb'); await cluster.set('aa', 'bb'); @@ -31,18 +31,17 @@ describe('Cluster', () => { itWithCluster(TestRedisClusters.OPEN, 'multi', async cluster => { const key = 'key'; assert.deepEqual( - await cluster.multi(key) - .ping() + await cluster.multi() .set(key, 'value') .get(key) .exec(), - ['PONG', 'OK', 'value'] + ['OK', 'value'] ); }); it('scripts', async () => { const cluster = RedisCluster.create({ - rootNodes: TEST_REDIS_CLUSTERES[TestRedisClusters.OPEN], + ...TEST_REDIS_CLUSTERES[TestRedisClusters.OPEN], scripts: { add: defineScript({ NUMBER_OF_KEYS: 0, diff --git a/lib/cluster.ts b/lib/cluster/index.ts similarity index 61% rename from lib/cluster.ts rename to lib/cluster/index.ts index 4be0e268207..aeaabecae35 100644 --- a/lib/cluster.ts +++ b/lib/cluster/index.ts @@ -1,25 +1,35 @@ -import { RedisCommand, RedisModules, TransformArgumentsReply } from './commands'; -import RedisClient, { ClientCommandOptions, RedisClientType, WithPlugins } from './client'; -import { RedisSocketOptions } from './socket'; +import COMMANDS from './commands'; +import { RedisCommand, RedisCommandArguments, RedisCommandReply, RedisModules, RedisScript, RedisScripts } from '../commands'; +import { ClientCommandOptions, RedisClientCommandSignature, RedisClientOptions, RedisClientType, WithModules, WithScripts } from '../client'; import RedisClusterSlots, { ClusterNode } from './cluster-slots'; -import { RedisLuaScript, RedisLuaScripts } from './lua-script'; -import { extendWithModulesAndScripts, extendWithDefaultCommands, transformCommandArguments } from './commander'; -import RedisMultiCommand, { MultiQueuedCommand, RedisMultiCommandType } from './multi-command'; +import { extendWithModulesAndScripts, transformCommandArguments, transformCommandReply, extendWithCommands } from '../commander'; import { EventEmitter } from 'events'; +import RedisClusterMultiCommand, { RedisClusterMultiCommandType } from './multi-command'; +import { RedisMultiQueuedCommand } from '../multi-command'; -export interface RedisClusterOptions { - rootNodes: Array; +export type RedisClusterClientOptions = Omit, 'modules' | 'scripts'>; + +export interface RedisClusterPlugins { modules?: M; scripts?: S; +} + +export interface RedisClusterOptions extends RedisClusterPlugins { + rootNodes: Array; + defaults?: Partial; useReplicas?: boolean; maxCommandRedirections?: number; } -export type RedisClusterType = - WithPlugins & RedisCluster; +type WithCommands = { + [P in keyof typeof COMMANDS]: RedisClientCommandSignature<(typeof COMMANDS)[P]>; +}; -export default class RedisCluster extends EventEmitter { - static #extractFirstKey(command: RedisCommand, originalArgs: Array, redisArgs: TransformArgumentsReply): string | Buffer | undefined { +export type RedisClusterType = + RedisCluster & WithCommands & WithModules & WithScripts; + +export default class RedisCluster extends EventEmitter { + static extractFirstKey(command: RedisCommand, originalArgs: Array, redisArgs: RedisCommandArguments): string | Buffer | undefined { if (command.FIRST_KEY_INDEX === undefined) { return undefined; } else if (typeof command.FIRST_KEY_INDEX === 'number') { @@ -29,7 +39,7 @@ export default class RedisCluster(options?: RedisClusterOptions): RedisClusterType { + static create(options?: RedisClusterOptions): RedisClusterType { return new (extendWithModulesAndScripts({ BaseClass: RedisCluster, modules: options?.modules, @@ -39,32 +49,36 @@ export default class RedisCluster; readonly #slots: RedisClusterSlots; - readonly #Multi: new (...args: ConstructorParameters) => RedisMultiCommandType; + readonly #Multi: new (...args: ConstructorParameters) => RedisClusterMultiCommandType; constructor(options: RedisClusterOptions) { super(); this.#options = options; this.#slots = new RedisClusterSlots(options, err => this.emit('error', err)); - this.#Multi = RedisMultiCommand.extend(options); + this.#Multi = RedisClusterMultiCommand.extend(options); } - duplicate(): RedisClusterOptions { - return new (Object.getPrototypeOf(this).constructor)(this.#options); + duplicate(overrides?: Partial>): RedisClusterType { + return new (Object.getPrototypeOf(this).constructor)({ + ...this.#options, + ...overrides + }); } async connect(): Promise { return this.#slots.connect(); } - async commandsExecutor(command: RedisCommand, args: Array): Promise> { + async commandsExecutor(command: RedisCommand, args: Array): Promise> { const { args: redisArgs, options } = transformCommandArguments(command, args); - const reply = command.transformReply( + return transformCommandReply( + command, await this.sendCommand( - RedisCluster.#extractFirstKey(command, args, redisArgs), + RedisCluster.extractFirstKey(command, args, redisArgs), command.IS_READ_ONLY, redisArgs, options, @@ -72,18 +86,16 @@ export default class RedisCluster( firstKey: string | Buffer | undefined, isReadonly: boolean | undefined, - args: TransformArgumentsReply, + args: RedisCommandArguments, options?: ClientCommandOptions, bufferMode?: boolean, redirections = 0 - ): Promise> { + ): Promise> { const client = this.#slots.getClient(firstKey, isReadonly); try { @@ -100,10 +112,11 @@ export default class RedisCluster): Promise> { + async scriptsExecutor(script: RedisScript, args: Array): Promise> { const { args: redisArgs, options } = transformCommandArguments(script, args); - const reply = script.transformReply( + return transformCommandReply( + script, await this.executeScript( script, args, @@ -112,19 +125,17 @@ export default class RedisCluster, - redisArgs: TransformArgumentsReply, + redisArgs: RedisCommandArguments, options?: ClientCommandOptions, redirections = 0 - ): Promise> { + ): Promise> { const client = this.#slots.getClient( - RedisCluster.#extractFirstKey(script, originalArgs, redisArgs), + RedisCluster.extractFirstKey(script, originalArgs, redisArgs), script.IS_READ_ONLY ); @@ -169,20 +180,14 @@ export default class RedisCluster { + multi(routing?: string | Buffer): RedisClusterMultiCommandType { return new this.#Multi( - async (commands: Array, chainId?: symbol) => { - const client = this.#slots.getClient(routing); - - return Promise.all( - commands.map(({ args }) => { - return client.sendCommand(args, RedisClient.commandOptions({ - chainId - })); - }) - ); + async (commands: Array, firstKey?: string | Buffer, chainId?: symbol) => { + return this.#slots + .getClient(firstKey) + .multiExecutor(commands, chainId); }, - this.#options + routing ); } @@ -199,4 +204,8 @@ export default class RedisCluster = + (...args: Parameters) => RedisClusterMultiCommandType; + +type WithCommands = { + [P in keyof typeof COMMANDS]: RedisClusterMultiCommandSignature<(typeof COMMANDS)[P], M, S> +}; + +type WithModules = { + [P in keyof M]: { + [C in keyof M[P]]: RedisClusterMultiCommandSignature; + }; +}; + +type WithScripts = { + [P in keyof S]: RedisClusterMultiCommandSignature +}; + +export type RedisClusterMultiCommandType = + RedisClusterMultiCommand & WithCommands & WithModules & WithScripts; + +export type RedisClusterMultiExecutor = (queue: Array, firstKey?: string | Buffer, chainId?: symbol) => Promise>; + +export default class RedisClusterMultiCommand { + readonly #multi = new RedisMultiCommand(); + readonly #executor: RedisClusterMultiExecutor; + #firstKey: string | Buffer | undefined; + + static extend( + plugins?: RedisPlugins + ): new (...args: ConstructorParameters) => RedisClusterMultiCommandType { + return extendWithModulesAndScripts({ + BaseClass: RedisClusterMultiCommand, + modules: plugins?.modules, + modulesCommandsExecutor: RedisClusterMultiCommand.prototype.commandsExecutor, + scripts: plugins?.scripts, + scriptsExecutor: RedisClusterMultiCommand.prototype.scriptsExecutor + }); + } + + constructor(executor: RedisClusterMultiExecutor, firstKey?: string | Buffer) { + this.#executor = executor; + this.#firstKey = firstKey; + } + + commandsExecutor(command: RedisCommand, args: Array): this { + const transformedArguments = command.transformArguments(...args); + if (!this.#firstKey) { + this.#firstKey = RedisCluster.extractFirstKey(command, args, transformedArguments); + } + + return this.addCommand( + undefined, + transformedArguments, + command.transformReply + ); + } + + addCommand( + firstKey: string | Buffer | undefined, + args: RedisCommandArguments, + transformReply?: RedisCommand['transformReply'] + ): this { + if (!this.#firstKey) { + this.#firstKey = firstKey; + } + + this.#multi.addCommand(args, transformReply); + return this; + } + + scriptsExecutor(script: RedisScript, args: Array): this { + const transformedArguments = this.#multi.addScript(script, args); + if (!this.#firstKey) { + this.#firstKey = RedisCluster.extractFirstKey(script, args, transformedArguments); + } + + return this.addCommand(undefined, transformedArguments); + } + + async exec(execAsPipeline = false): Promise> { + if (execAsPipeline) { + return this.execAsPipeline(); + } + + const commands = this.#multi.exec(); + if (!commands) return []; + + return this.#multi.handleExecReplies( + await this.#executor(commands, this.#firstKey, RedisMultiCommand.generateChainId()) + ); + } + + EXEC = this.exec; + + async execAsPipeline(): Promise> { + return this.#multi.transformReplies( + await this.#executor(this.#multi.queue, this.#firstKey) + ); + } +} + +extendWithCommands({ + BaseClass: RedisClusterMultiCommand, + commands: COMMANDS, + executor: RedisClusterMultiCommand.prototype.commandsExecutor +}); diff --git a/lib/commander.ts b/lib/commander.ts index 78823516448..5871c2f235b 100644 --- a/lib/commander.ts +++ b/lib/commander.ts @@ -1,37 +1,32 @@ -import COMMANDS, { RedisCommand, RedisModules, TransformArgumentsReply } from './commands'; -import { RedisLuaScript, RedisLuaScripts } from './lua-script'; import { CommandOptions, isCommandOptions } from './command-options'; +import { RedisCommand, RedisCommandArguments, RedisCommandRawReply, RedisCommandReply, RedisCommands, RedisModules, RedisScript, RedisScripts } from './commands'; type Instantiable = new(...args: Array) => T; -type CommandExecutor = (this: InstanceType, command: RedisCommand, args: Array) => unknown; +interface ExtendWithCommandsConfig { + BaseClass: T; + commands: RedisCommands; + executor(command: RedisCommand, args: Array): unknown; +} -export function extendWithDefaultCommands(BaseClass: T, executor: CommandExecutor): void { - for (const [name, command] of Object.entries(COMMANDS)) { +export function extendWithCommands({ BaseClass, commands, executor }: ExtendWithCommandsConfig): void { + for (const [name, command] of Object.entries(commands)) { BaseClass.prototype[name] = function (...args: Array): unknown { return executor.call(this, command, args); }; } } -interface ExtendWithModulesAndScriptsConfig< - T extends Instantiable, - M extends RedisModules, - S extends RedisLuaScripts -> { +interface ExtendWithModulesAndScriptsConfig { BaseClass: T; - modules: M | undefined; - modulesCommandsExecutor: CommandExecutor; - scripts: S | undefined; - scriptsExecutor(this: InstanceType, script: RedisLuaScript, args: Array): unknown; + modules?: RedisModules; + modulesCommandsExecutor(this: InstanceType, command: RedisCommand, args: Array): unknown; + scripts?: RedisScripts; + scriptsExecutor(this: InstanceType, script: RedisScript, args: Array): unknown; } -export function extendWithModulesAndScripts< - T extends Instantiable, - M extends RedisModules, - S extends RedisLuaScripts, ->(config: ExtendWithModulesAndScriptsConfig): T { +export function extendWithModulesAndScripts(config: ExtendWithModulesAndScriptsConfig): T { let Commander: T | undefined; if (config.modules) { @@ -39,7 +34,7 @@ export function extendWithModulesAndScripts< constructor(...args: Array) { super(...args); - for (const module of Object.keys(config.modules as RedisModules)) { + for (const module of Object.keys(config.modules!)) { this[module] = new this[module](this); } } @@ -79,7 +74,7 @@ export function transformCommandArguments( command: RedisCommand, args: Array ): { - args: TransformArgumentsReply; + args: RedisCommandArguments; options: CommandOptions | undefined; } { let options; @@ -96,7 +91,7 @@ export function transformCommandArguments( const DELIMITER = '\r\n'; -export function* encodeCommand(args: TransformArgumentsReply): IterableIterator { +export function* encodeCommand(args: RedisCommandArguments): IterableIterator { yield `*${args.length}${DELIMITER}`; for (const arg of args) { @@ -106,3 +101,15 @@ export function* encodeCommand(args: TransformArgumentsReply): IterableIterator< yield DELIMITER; } } + +export function transformCommandReply( + command: RedisCommand, + rawReply: RedisCommandRawReply, + preserved: unknown +): RedisCommandReply { + if (!command.transformReply) { + return rawReply; + } + + return command.transformReply(rawReply, preserved); +} diff --git a/lib/commands/ACL_CAT.ts b/lib/commands/ACL_CAT.ts index d1620ef1584..f11be873961 100644 --- a/lib/commands/ACL_CAT.ts +++ b/lib/commands/ACL_CAT.ts @@ -1,5 +1,3 @@ -import { transformReplyStringArray } from './generic-transformers'; - export function transformArguments(categoryName?: string): Array { const args = ['ACL', 'CAT']; @@ -10,4 +8,4 @@ export function transformArguments(categoryName?: string): Array { return args; } -export const transformReply = transformReplyStringArray; +export declare function transformReply(): Array; diff --git a/lib/commands/ACL_DELUSER.ts b/lib/commands/ACL_DELUSER.ts index 85a916c4379..97f50d48945 100644 --- a/lib/commands/ACL_DELUSER.ts +++ b/lib/commands/ACL_DELUSER.ts @@ -1,8 +1,8 @@ -import { TransformArgumentsReply } from '.'; -import { pushVerdictArguments, transformReplyNumber } from './generic-transformers'; +import { RedisCommandArguments } from '.'; +import { pushVerdictArguments } from './generic-transformers'; -export function transformArguments(username: string | Array): TransformArgumentsReply { +export function transformArguments(username: string | Array): RedisCommandArguments { return pushVerdictArguments(['ACL', 'DELUSER'], username); } -export const transformReply = transformReplyNumber; +export declare const transformReply: (reply: number) => number; diff --git a/lib/commands/ACL_GENPASS.ts b/lib/commands/ACL_GENPASS.ts index ec55aebdb07..6a36c396619 100644 --- a/lib/commands/ACL_GENPASS.ts +++ b/lib/commands/ACL_GENPASS.ts @@ -1,5 +1,3 @@ -import { transformReplyString } from './generic-transformers'; - export function transformArguments(bits?: number): Array { const args = ['ACL', 'GENPASS']; @@ -10,4 +8,4 @@ export function transformArguments(bits?: number): Array { return args; } -export const transformReply = transformReplyString; +export declare function transformReply(): string; diff --git a/lib/commands/ACL_LIST.ts b/lib/commands/ACL_LIST.ts index 3f2845b907a..a2caae82c46 100644 --- a/lib/commands/ACL_LIST.ts +++ b/lib/commands/ACL_LIST.ts @@ -1,7 +1,5 @@ -import { transformReplyStringArray } from './generic-transformers'; - export function transformArguments(): Array { return ['ACL', 'LIST']; } -export const transformReply = transformReplyStringArray; +export declare function transformReply(): Array; diff --git a/lib/commands/ACL_LOAD.ts b/lib/commands/ACL_LOAD.ts index 59418614ed3..0a5ea85101d 100644 --- a/lib/commands/ACL_LOAD.ts +++ b/lib/commands/ACL_LOAD.ts @@ -1,7 +1,5 @@ -import { transformReplyString } from './generic-transformers'; - export function transformArguments(): Array { return ['ACL', 'LOAD']; } -export const transformReply = transformReplyString; +export declare function transformReply(): string; diff --git a/lib/commands/ACL_LOG_RESET.ts b/lib/commands/ACL_LOG_RESET.ts index 30b8ccb20c7..5bfd7ae9392 100644 --- a/lib/commands/ACL_LOG_RESET.ts +++ b/lib/commands/ACL_LOG_RESET.ts @@ -1,7 +1,5 @@ -import { transformReplyString } from './generic-transformers'; - export function transformArguments(): Array { return ['ACL', 'LOG', 'RESET']; } -export const transformReply = transformReplyString; +export declare function transformReply(): string; diff --git a/lib/commands/ACL_SAVE.ts b/lib/commands/ACL_SAVE.ts index 5b9c7b84cc3..af9abeb1d5c 100644 --- a/lib/commands/ACL_SAVE.ts +++ b/lib/commands/ACL_SAVE.ts @@ -1,7 +1,5 @@ -import { transformReplyString } from './generic-transformers'; - export function transformArguments(): Array { return ['ACL', 'SAVE']; } -export const transformReply = transformReplyString; +export declare function transformReply(): string; diff --git a/lib/commands/ACL_SETUSER.ts b/lib/commands/ACL_SETUSER.ts index e55a8942e02..d8734f0a1ca 100644 --- a/lib/commands/ACL_SETUSER.ts +++ b/lib/commands/ACL_SETUSER.ts @@ -1,8 +1,8 @@ -import { TransformArgumentsReply } from '.'; -import { pushVerdictArguments, transformReplyString } from './generic-transformers'; +import { RedisCommandArguments } from '.'; +import { pushVerdictArguments } from './generic-transformers'; -export function transformArguments(username: string, rule: string | Array): TransformArgumentsReply { +export function transformArguments(username: string, rule: string | Array): RedisCommandArguments { return pushVerdictArguments(['ACL', 'SETUSER', username], rule); } -export const transformReply = transformReplyString; +export declare function transformReply(): string; diff --git a/lib/commands/ACL_USERS.ts b/lib/commands/ACL_USERS.ts index f9e837a4347..91f391a5b73 100644 --- a/lib/commands/ACL_USERS.ts +++ b/lib/commands/ACL_USERS.ts @@ -1,7 +1,5 @@ -import { transformReplyStringArray } from './generic-transformers'; - export function transformArguments(): Array { return ['ACL', 'USERS']; } -export const transformReply = transformReplyStringArray; +export declare function transformReply(): Array; diff --git a/lib/commands/ACL_WHOAMI.ts b/lib/commands/ACL_WHOAMI.ts index 3fc70649f87..c855eeb1aa7 100644 --- a/lib/commands/ACL_WHOAMI.ts +++ b/lib/commands/ACL_WHOAMI.ts @@ -1,7 +1,5 @@ -import { transformReplyString } from './generic-transformers'; - export function transformArguments(): Array { return ['ACL', 'WHOAMI']; } -export const transformReply = transformReplyString; +export declare function transformReply(): string; diff --git a/lib/commands/APPEND.ts b/lib/commands/APPEND.ts index f64e835113c..a162dc1381c 100644 --- a/lib/commands/APPEND.ts +++ b/lib/commands/APPEND.ts @@ -1,9 +1,7 @@ -import { transformReplyString } from './generic-transformers'; - export const FIRST_KEY_INDEX = 1; export function transformArguments(key: string, value: string): Array { return ['APPEND', key, value]; } -export const transformReply = transformReplyString; +export declare function transformReply(): string; diff --git a/lib/commands/ASKING.ts b/lib/commands/ASKING.ts index 3f836131ac1..8071ec20180 100644 --- a/lib/commands/ASKING.ts +++ b/lib/commands/ASKING.ts @@ -1,7 +1,5 @@ -import { transformReplyString } from './generic-transformers'; - export function transformArguments(): Array { return ['ASKING']; } -export const transformReply = transformReplyString; +export declare function transformReply(): string; diff --git a/lib/commands/AUTH.ts b/lib/commands/AUTH.ts index 750f0f54354..eb2449208a8 100644 --- a/lib/commands/AUTH.ts +++ b/lib/commands/AUTH.ts @@ -1,5 +1,3 @@ -import { transformReplyString } from './generic-transformers'; - export interface AuthOptions { username?: string; password: string; @@ -13,4 +11,4 @@ export function transformArguments({username, password}: AuthOptions): Array { return ['BGREWRITEAOF']; } -export const transformReply = transformReplyString; +export declare function transformReply(): string; diff --git a/lib/commands/BGSAVE.ts b/lib/commands/BGSAVE.ts index f09f906ade7..fba9e37ed76 100644 --- a/lib/commands/BGSAVE.ts +++ b/lib/commands/BGSAVE.ts @@ -1,5 +1,3 @@ -import { transformReplyString } from './generic-transformers'; - interface BgSaveOptions { SCHEDULE?: true; } @@ -14,4 +12,4 @@ export function transformArguments(options?: BgSaveOptions): Array { return args; } -export const transformReply = transformReplyString; \ No newline at end of file +export declare function transformReply(): string; diff --git a/lib/commands/BITCOUNT.ts b/lib/commands/BITCOUNT.ts index 1aececc377e..320d8f3acb7 100644 --- a/lib/commands/BITCOUNT.ts +++ b/lib/commands/BITCOUNT.ts @@ -1,5 +1,3 @@ -import { transformReplyNumber } from './generic-transformers'; - export const FIRST_KEY_INDEX = 1; export const IS_READ_ONLY = true; @@ -22,4 +20,4 @@ export function transformArguments(key: string, range?: BitCountRange): Array; diff --git a/lib/commands/BITOP.ts b/lib/commands/BITOP.ts index bb965da6dfa..af31f42f1dc 100644 --- a/lib/commands/BITOP.ts +++ b/lib/commands/BITOP.ts @@ -1,12 +1,12 @@ -import { TransformArgumentsReply } from '.'; -import { pushVerdictArguments, transformReplyNumber } from './generic-transformers'; +import { RedisCommandArguments } from '.'; +import { pushVerdictArguments } from './generic-transformers'; export const FIRST_KEY_INDEX = 2; type BitOperations = 'AND' | 'OR' | 'XOR' | 'NOT'; -export function transformArguments(operation: BitOperations, destKey: string, key: string | Array): TransformArgumentsReply { +export function transformArguments(operation: BitOperations, destKey: string, key: string | Array): RedisCommandArguments { return pushVerdictArguments(['BITOP', operation, destKey], key); } -export const transformReply = transformReplyNumber; +export declare function transformReply(): number; diff --git a/lib/commands/BITPOS.ts b/lib/commands/BITPOS.ts index bebc45b03c3..86f539f2dda 100644 --- a/lib/commands/BITPOS.ts +++ b/lib/commands/BITPOS.ts @@ -1,4 +1,4 @@ -import { BitValue, transformReplyNumber } from './generic-transformers'; +import { BitValue } from './generic-transformers'; export const FIRST_KEY_INDEX = 1; @@ -18,4 +18,4 @@ export function transformArguments(key: string, bit: BitValue, start?: number, e return args; } -export const transformReply = transformReplyNumber; \ No newline at end of file +export declare function transformReply(): number; diff --git a/lib/commands/BLMOVE.ts b/lib/commands/BLMOVE.ts index 74a2eed4aa5..461a146e7a3 100644 --- a/lib/commands/BLMOVE.ts +++ b/lib/commands/BLMOVE.ts @@ -1,4 +1,3 @@ -import { transformReplyStringNull } from './generic-transformers'; import { LMoveSide } from './LMOVE'; export const FIRST_KEY_INDEX = 1; @@ -20,4 +19,4 @@ export function transformArguments( ]; } -export const transformReply = transformReplyStringNull; +export declare function transformReply(): string | null; diff --git a/lib/commands/BLPOP.ts b/lib/commands/BLPOP.ts index 1061f5e113a..15c52722941 100644 --- a/lib/commands/BLPOP.ts +++ b/lib/commands/BLPOP.ts @@ -1,9 +1,9 @@ -import { TransformArgumentsReply } from '.'; +import { RedisCommandArguments } from '.'; import { pushVerdictArguments } from './generic-transformers'; export const FIRST_KEY_INDEX = 1; -export function transformArguments(keys: string | Buffer | Array, timeout: number): TransformArgumentsReply { +export function transformArguments(keys: string | Buffer | Array, timeout: number): RedisCommandArguments { const args = pushVerdictArguments(['BLPOP'], keys); args.push(timeout.toString()); diff --git a/lib/commands/BRPOP.ts b/lib/commands/BRPOP.ts index 93ded4dbf1a..602ce9c7913 100644 --- a/lib/commands/BRPOP.ts +++ b/lib/commands/BRPOP.ts @@ -1,9 +1,9 @@ -import { TransformArgumentsReply } from '.'; +import { RedisCommandArguments } from '.'; import { pushVerdictArguments } from './generic-transformers'; export const FIRST_KEY_INDEX = 1; -export function transformArguments(key: string | Array, timeout: number): TransformArgumentsReply { +export function transformArguments(key: string | Array, timeout: number): RedisCommandArguments { const args = pushVerdictArguments(['BRPOP'], key); args.push(timeout.toString()); diff --git a/lib/commands/BRPOPLPUSH.ts b/lib/commands/BRPOPLPUSH.ts index f6a3bc1b8a6..3f6aed35be1 100644 --- a/lib/commands/BRPOPLPUSH.ts +++ b/lib/commands/BRPOPLPUSH.ts @@ -1,9 +1,7 @@ -import { transformReplyNumberNull } from './generic-transformers'; - export const FIRST_KEY_INDEX = 1; export function transformArguments(source: string, destination: string, timeout: number): Array { return ['BRPOPLPUSH', source, destination, timeout.toString()]; } -export const transformReply = transformReplyNumberNull; +export declare function transformReply(): number | null; diff --git a/lib/commands/BZPOPMAX.spec.ts b/lib/commands/BZPOPMAX.spec.ts index c4bcc321b29..090dfba096d 100644 --- a/lib/commands/BZPOPMAX.spec.ts +++ b/lib/commands/BZPOPMAX.spec.ts @@ -2,7 +2,6 @@ import { strict as assert } from 'assert'; import { TestRedisServers, itWithClient } from '../test-utils'; import { transformArguments, transformReply } from './BZPOPMAX'; import { commandOptions } from '../../index'; -import { describe } from 'mocha'; describe('BZPOPMAX', () => { describe('transformArguments', () => { diff --git a/lib/commands/BZPOPMAX.ts b/lib/commands/BZPOPMAX.ts index 3db9ca42cbb..eb6647ce9e3 100644 --- a/lib/commands/BZPOPMAX.ts +++ b/lib/commands/BZPOPMAX.ts @@ -1,9 +1,9 @@ -import { TransformArgumentsReply } from '.'; +import { RedisCommandArguments } from '.'; import { pushVerdictArguments, transformReplyNumberInfinity, ZMember } from './generic-transformers'; export const FIRST_KEY_INDEX = 1; -export function transformArguments(key: string | Array, timeout: number): TransformArgumentsReply { +export function transformArguments(key: string | Array, timeout: number): RedisCommandArguments { const args = pushVerdictArguments(['BZPOPMAX'], key); args.push(timeout.toString()); diff --git a/lib/commands/BZPOPMIN.ts b/lib/commands/BZPOPMIN.ts index 9106ae770da..75b092e543b 100644 --- a/lib/commands/BZPOPMIN.ts +++ b/lib/commands/BZPOPMIN.ts @@ -1,9 +1,9 @@ -import { TransformArgumentsReply } from '.'; +import { RedisCommandArguments } from '.'; import { pushVerdictArguments, transformReplyNumberInfinity, ZMember } from './generic-transformers'; export const FIRST_KEY_INDEX = 1; -export function transformArguments(key: string | Array, timeout: number): TransformArgumentsReply { +export function transformArguments(key: string | Array, timeout: number): RedisCommandArguments { const args = pushVerdictArguments(['BZPOPMIN'], key); args.push(timeout.toString()); diff --git a/lib/commands/CLIENT_ID.ts b/lib/commands/CLIENT_ID.ts index baeab148eba..a57e392ade6 100644 --- a/lib/commands/CLIENT_ID.ts +++ b/lib/commands/CLIENT_ID.ts @@ -1,9 +1,7 @@ -import { transformReplyNumber } from './generic-transformers'; - export const IS_READ_ONLY = true; export function transformArguments(): Array { return ['CLIENT', 'ID']; } -export const transformReply = transformReplyNumber; +export declare function transformReply(): number; diff --git a/lib/commands/CLUSTER_ADDSLOTS.ts b/lib/commands/CLUSTER_ADDSLOTS.ts index 594eae77c75..e458b8aab91 100644 --- a/lib/commands/CLUSTER_ADDSLOTS.ts +++ b/lib/commands/CLUSTER_ADDSLOTS.ts @@ -1,5 +1,3 @@ -import { transformReplyString } from './generic-transformers'; - export function transformArguments(slots: number | Array): Array { const args = ['CLUSTER', 'ADDSLOTS']; @@ -12,4 +10,4 @@ export function transformArguments(slots: number | Array): Array return args; } -export const transformReply = transformReplyString; +export declare function transformReply(): string; diff --git a/lib/commands/CLUSTER_FLUSHSLOTS.ts b/lib/commands/CLUSTER_FLUSHSLOTS.ts index 28fbcc1fab1..285c9fd26fe 100644 --- a/lib/commands/CLUSTER_FLUSHSLOTS.ts +++ b/lib/commands/CLUSTER_FLUSHSLOTS.ts @@ -1,7 +1,5 @@ -import { transformReplyString } from './generic-transformers'; - export function transformArguments(): Array { return ['CLUSTER', 'FLUSHSLOTS']; } -export const transformReply = transformReplyString; +export declare function transformReply(): string; diff --git a/lib/commands/CLUSTER_GETKEYSINSLOT.ts b/lib/commands/CLUSTER_GETKEYSINSLOT.ts index c5719848cf5..67c5cbafb77 100644 --- a/lib/commands/CLUSTER_GETKEYSINSLOT.ts +++ b/lib/commands/CLUSTER_GETKEYSINSLOT.ts @@ -1,7 +1,5 @@ -import { transformReplyString } from './generic-transformers'; - export function transformArguments(slot: number, count: number): Array { return ['CLUSTER', 'GETKEYSINSLOT', slot.toString(), count.toString()]; } -export const transformReply = transformReplyString; +export declare function transformReply(): string; diff --git a/lib/commands/CLUSTER_INFO.spec.ts b/lib/commands/CLUSTER_INFO.spec.ts index ce41151b67f..a4def45cb79 100644 --- a/lib/commands/CLUSTER_INFO.spec.ts +++ b/lib/commands/CLUSTER_INFO.spec.ts @@ -1,5 +1,4 @@ import { strict as assert } from 'assert'; -import { itWithCluster, TestRedisClusters } from '../test-utils'; import { transformArguments, transformReply } from './CLUSTER_INFO'; describe('CLUSTER INFO', () => { @@ -44,21 +43,4 @@ describe('CLUSTER INFO', () => { } ); }); - - itWithCluster(TestRedisClusters.OPEN, 'cluster.clusterInfo', async cluster => { - const info = await cluster.clusterInfo(); - assert.equal(info.state, 'ok'); - assert.deepEqual(info.slots, { - assigned: 16384, - ok: 16384, - pfail: 0, - fail: 0 - }); - assert.equal(info.knownNodes, 3); - assert.equal(info.size, 3); - assert.equal(typeof info.currentEpoch, 'number'); - assert.equal(typeof info.myEpoch, 'number'); - assert.equal(typeof info.stats.messagesReceived, 'number'); - assert.equal(typeof info.stats.messagesSent, 'number'); - }); }); diff --git a/lib/commands/CLUSTER_MEET.ts b/lib/commands/CLUSTER_MEET.ts index 19da150356c..54a0bf708a8 100644 --- a/lib/commands/CLUSTER_MEET.ts +++ b/lib/commands/CLUSTER_MEET.ts @@ -1,7 +1,5 @@ -import { transformReplyString } from './generic-transformers'; - export function transformArguments(ip: string, port: number): Array { return ['CLUSTER', 'MEET', ip, port.toString()]; } -export const transformReply = transformReplyString; +export declare function transformReply(): string; diff --git a/lib/commands/CLUSTER_NODES.spec.ts b/lib/commands/CLUSTER_NODES.spec.ts index 1f0e9dd425a..2b3881d8cd0 100644 --- a/lib/commands/CLUSTER_NODES.spec.ts +++ b/lib/commands/CLUSTER_NODES.spec.ts @@ -1,5 +1,4 @@ import { strict as assert } from 'assert'; -import { itWithCluster, TestRedisClusters } from '../test-utils'; import { RedisClusterNodeLinkStates, transformArguments, transformReply } from './CLUSTER_NODES'; describe('CLUSTER NODES', () => { @@ -93,24 +92,4 @@ describe('CLUSTER NODES', () => { ); }); }); - - itWithCluster(TestRedisClusters.OPEN, 'cluster.clusterNodes', async cluster => { - for (const node of (await cluster.clusterNodes())) { - assert.equal(typeof node.id, 'string'); - assert.equal(typeof node.url, 'string'); - assert.equal(typeof node.host, 'string'); - assert.equal(typeof node.port, 'number'); - assert.equal(typeof node.cport, 'number'); - assert.ok(Array.isArray(node.flags)); - assert.equal(typeof node.pingSent, 'number'); - assert.equal(typeof node.pongRecv, 'number'); - assert.equal(typeof node.configEpoch, 'number'); - assert.equal(typeof node.linkState, 'string'); - - for (const slot of node.slots) { - assert.equal(typeof slot.from, 'number'); - assert.equal(typeof slot.to, 'number'); - } - } - }); }); diff --git a/lib/commands/CLUSTER_RESET.ts b/lib/commands/CLUSTER_RESET.ts index ec27b45eeb7..3e7c5bb52fb 100644 --- a/lib/commands/CLUSTER_RESET.ts +++ b/lib/commands/CLUSTER_RESET.ts @@ -1,5 +1,3 @@ -import { transformReplyString } from './generic-transformers'; - export type ClusterResetModes = 'HARD' | 'SOFT'; export function transformArguments(mode?: ClusterResetModes): Array { @@ -12,4 +10,4 @@ export function transformArguments(mode?: ClusterResetModes): Array { return args; } -export const transformReply = transformReplyString; +export declare function transformReply(): string; diff --git a/lib/commands/CLUSTER_SETSLOT.ts b/lib/commands/CLUSTER_SETSLOT.ts index c665b349622..591b5fb9632 100644 --- a/lib/commands/CLUSTER_SETSLOT.ts +++ b/lib/commands/CLUSTER_SETSLOT.ts @@ -1,5 +1,3 @@ -import { transformReplyString } from './generic-transformers'; - export enum ClusterSlotStates { IMPORTING = 'IMPORTING', MIGRATING = 'MIGRATING', @@ -17,4 +15,4 @@ export function transformArguments(slot: number, state: ClusterSlotStates, nodeI return args; } -export const transformReply = transformReplyString; +export declare function transformReply(): string; diff --git a/lib/commands/CLUSTER_SLOTS.ts b/lib/commands/CLUSTER_SLOTS.ts index b4672e731ac..afe1ebc83dd 100644 --- a/lib/commands/CLUSTER_SLOTS.ts +++ b/lib/commands/CLUSTER_SLOTS.ts @@ -1,6 +1,6 @@ -import { TransformArgumentsReply } from '.'; +import { RedisCommandArguments } from '.'; -export function transformArguments(): TransformArgumentsReply { +export function transformArguments(): RedisCommandArguments { return ['CLUSTER', 'SLOTS']; } diff --git a/lib/commands/COMMAND.spec.ts b/lib/commands/COMMAND.spec.ts new file mode 100644 index 00000000000..1f036dadc17 --- /dev/null +++ b/lib/commands/COMMAND.spec.ts @@ -0,0 +1,30 @@ +import { strict as assert } from 'assert'; +import { itWithClient, TestRedisServers } from '../test-utils'; +import { transformArguments } from './COMMAND'; +import { CommandCategories, CommandFlags } from './generic-transformers'; + +describe('COMMAND', () => { + it('transformArguments', () => { + assert.deepEqual( + transformArguments(), + ['COMMAND'] + ); + }); + + itWithClient(TestRedisServers.OPEN, 'client.command', async client => { + assert.deepEqual( + (await client.command()).find(command => command.name === 'ping'), + { + name: 'ping', + arity: -1, + flags: new Set([CommandFlags.STALE, CommandFlags.FAST]), + firstKeyIndex: 0, + lastKeyIndex: 0, + step: 0, + categories: new Set([CommandCategories.FAST, CommandCategories.CONNECTION]) + } + ); + }, { + minimumRedisVersion: [6] + }); +}); diff --git a/lib/commands/COMMAND.ts b/lib/commands/COMMAND.ts new file mode 100644 index 00000000000..b6ee50b2f4c --- /dev/null +++ b/lib/commands/COMMAND.ts @@ -0,0 +1,12 @@ +import { RedisCommandArguments } from '.'; +import { CommandRawReply, CommandReply, transformCommandReply } from './generic-transformers'; + +export const IS_READ_ONLY = true; + +export function transformArguments(): RedisCommandArguments { + return ['COMMAND']; +} + +export function transformReply(reply: Array): Array { + return reply.map(transformCommandReply); +} diff --git a/lib/commands/COMMAND_COUNT.spec.ts b/lib/commands/COMMAND_COUNT.spec.ts new file mode 100644 index 00000000000..23e83c71cec --- /dev/null +++ b/lib/commands/COMMAND_COUNT.spec.ts @@ -0,0 +1,19 @@ +import { strict as assert } from 'assert'; +import { TestRedisServers, itWithClient } from '../test-utils'; +import { transformArguments } from './COMMAND_COUNT'; + +describe('COMMAND COUNT', () => { + it('transformArguments', () => { + assert.deepEqual( + transformArguments(), + ['COMMAND', 'COUNT'] + ); + }); + + itWithClient(TestRedisServers.OPEN, 'client.commandCount', async client => { + assert.equal( + typeof await client.commandCount(), + 'number' + ); + }); +}); diff --git a/lib/commands/COMMAND_COUNT.ts b/lib/commands/COMMAND_COUNT.ts new file mode 100644 index 00000000000..5b8283bcc66 --- /dev/null +++ b/lib/commands/COMMAND_COUNT.ts @@ -0,0 +1,9 @@ +import { RedisCommandArguments } from '.'; + +export const IS_READ_ONLY = true; + +export function transformArguments(): RedisCommandArguments { + return ['COMMAND', 'COUNT']; +} + +declare function transformReply(): number; diff --git a/lib/commands/COMMAND_GETKEYS.spec.ts b/lib/commands/COMMAND_GETKEYS.spec.ts new file mode 100644 index 00000000000..f2630db9afa --- /dev/null +++ b/lib/commands/COMMAND_GETKEYS.spec.ts @@ -0,0 +1,19 @@ +import { strict as assert } from 'assert'; +import { TestRedisServers, itWithClient } from '../test-utils'; +import { transformArguments } from './COMMAND_GETKEYS'; + +describe('COMMAND GETKEYS', () => { + it('transformArguments', () => { + assert.deepEqual( + transformArguments(['GET', 'key']), + ['COMMAND', 'GETKEYS', 'GET', 'key'] + ); + }); + + itWithClient(TestRedisServers.OPEN, 'client.commandGetKeys', async client => { + assert.deepEqual( + await client.commandGetKeys(['GET', 'key']), + ['key'] + ); + }); +}); diff --git a/lib/commands/COMMAND_GETKEYS.ts b/lib/commands/COMMAND_GETKEYS.ts new file mode 100644 index 00000000000..caf342088fb --- /dev/null +++ b/lib/commands/COMMAND_GETKEYS.ts @@ -0,0 +1,9 @@ +import { RedisCommandArguments } from '.'; + +export const IS_READ_ONLY = true; + +export function transformArguments(args: Array): RedisCommandArguments { + return ['COMMAND', 'GETKEYS', ...args]; +} + +declare function transformReply(): Array; diff --git a/lib/commands/COMMAND_INFO.spec.ts b/lib/commands/COMMAND_INFO.spec.ts new file mode 100644 index 00000000000..59a61f6680a --- /dev/null +++ b/lib/commands/COMMAND_INFO.spec.ts @@ -0,0 +1,30 @@ +import { strict as assert } from 'assert'; +import { itWithClient, TestRedisServers } from '../test-utils'; +import { transformArguments } from './COMMAND_INFO'; +import { CommandCategories, CommandFlags } from './generic-transformers'; + +describe('COMMAND INFO', () => { + it('transformArguments', () => { + assert.deepEqual( + transformArguments(['PING']), + ['COMMAND', 'INFO', 'PING'] + ); + }); + + itWithClient(TestRedisServers.OPEN, 'client.commandInfo', async client => { + assert.deepEqual( + await client.commandInfo(['PING']), + [{ + name: 'ping', + arity: -1, + flags: new Set([CommandFlags.STALE, CommandFlags.FAST]), + firstKeyIndex: 0, + lastKeyIndex: 0, + step: 0, + categories: new Set([CommandCategories.FAST, CommandCategories.CONNECTION]) + }] + ); + }, { + minimumRedisVersion: [6] + }); +}); diff --git a/lib/commands/COMMAND_INFO.ts b/lib/commands/COMMAND_INFO.ts new file mode 100644 index 00000000000..6f84d0edaf9 --- /dev/null +++ b/lib/commands/COMMAND_INFO.ts @@ -0,0 +1,12 @@ +import { RedisCommandArguments } from '.'; +import { CommandRawReply, CommandReply, transformCommandReply } from './generic-transformers'; + +export const IS_READ_ONLY = true; + +export function transformArguments(commands: Array): RedisCommandArguments { + return ['COMMAND', 'INFO', ...commands]; +} + +export function transformReply(reply: Array): Array { + return reply.map(command => command ? transformCommandReply(command) : null); +} diff --git a/lib/commands/CONFIG_RESETSTAT.ts b/lib/commands/CONFIG_RESETSTAT.ts index 3c87b08d88b..aba54bc3c7b 100644 --- a/lib/commands/CONFIG_RESETSTAT.ts +++ b/lib/commands/CONFIG_RESETSTAT.ts @@ -1,7 +1,5 @@ -import { transformReplyString } from './generic-transformers'; - export function transformArguments(): Array { return ['CONFIG', 'RESETSTAT']; } -export const transformReply = transformReplyString; +export declare function transformReply(): string; diff --git a/lib/commands/CONFIG_REWRITE.ts b/lib/commands/CONFIG_REWRITE.ts index 06247517128..67984adf300 100644 --- a/lib/commands/CONFIG_REWRITE.ts +++ b/lib/commands/CONFIG_REWRITE.ts @@ -1,7 +1,5 @@ -import { transformReplyString } from './generic-transformers'; - export function transformArguments(): Array { return ['CONFIG', 'REWRITE']; } -export const transformReply = transformReplyString; +export declare function transformReply(): string; diff --git a/lib/commands/CONFIG_SET.ts b/lib/commands/CONFIG_SET.ts index 894a95cb1cc..ec09e4469fa 100644 --- a/lib/commands/CONFIG_SET.ts +++ b/lib/commands/CONFIG_SET.ts @@ -1,7 +1,5 @@ -import { transformReplyString } from './generic-transformers'; - export function transformArguments(parameter: string, value: string): Array { return ['CONFIG', 'SET', parameter, value]; } -export const transformReply = transformReplyString; +export declare function transformReply(): string; diff --git a/lib/commands/DBSIZE.spec.ts b/lib/commands/DBSIZE.spec.ts index 87e3c154534..36f591dbd29 100644 --- a/lib/commands/DBSIZE.spec.ts +++ b/lib/commands/DBSIZE.spec.ts @@ -1,5 +1,5 @@ import { strict as assert } from 'assert'; -import { TestRedisServers, itWithClient, TestRedisClusters, itWithCluster } from '../test-utils'; +import { TestRedisServers, itWithClient } from '../test-utils'; import { transformArguments } from './DBSIZE'; describe('DBSIZE', () => { @@ -16,11 +16,4 @@ describe('DBSIZE', () => { 0 ); }); - - itWithCluster(TestRedisClusters.OPEN, 'cluster.dbSize', async cluster => { - assert.equal( - await cluster.dbSize(), - 0 - ); - }); }); diff --git a/lib/commands/DBSIZE.ts b/lib/commands/DBSIZE.ts index 72933930f70..6b442ec33a2 100644 --- a/lib/commands/DBSIZE.ts +++ b/lib/commands/DBSIZE.ts @@ -1,9 +1,7 @@ -import { transformReplyNumber } from './generic-transformers'; - export const IS_READ_ONLY = true; export function transformArguments(): Array { return ['DBSIZE']; } -export const transformReply = transformReplyNumber; +export declare function transformReply(): number; diff --git a/lib/commands/DECR.ts b/lib/commands/DECR.ts index cac6e07f053..e30d2aaf29c 100644 --- a/lib/commands/DECR.ts +++ b/lib/commands/DECR.ts @@ -1,9 +1,7 @@ -import { transformReplyNumber } from './generic-transformers'; - export const FIRST_KEY_INDEX = 1; export function transformArguments(key: string): Array { return ['DECR', key]; } -export const transformReply = transformReplyNumber; +export declare function transformReply(): number; diff --git a/lib/commands/DECRBY.ts b/lib/commands/DECRBY.ts index cc163cbe824..561eb9491c4 100644 --- a/lib/commands/DECRBY.ts +++ b/lib/commands/DECRBY.ts @@ -1,9 +1,7 @@ -import { transformReplyNumber } from './generic-transformers'; - export const FIRST_KEY_INDEX = 1; export function transformArguments(key: string, decrement: number): Array { return ['DECRBY', key, decrement.toString()]; } -export const transformReply = transformReplyNumber; +export declare function transformReply(): number; diff --git a/lib/commands/DEL.ts b/lib/commands/DEL.ts index f96b6988f1c..02ef553f647 100644 --- a/lib/commands/DEL.ts +++ b/lib/commands/DEL.ts @@ -1,8 +1,8 @@ -import { TransformArgumentsReply } from '.'; -import { pushVerdictArguments, transformReplyNumber } from './generic-transformers'; +import { RedisCommandArguments } from '.'; +import { pushVerdictArguments } from './generic-transformers'; -export function transformArguments(keys: string | Array): TransformArgumentsReply { +export function transformArguments(keys: string | Array): RedisCommandArguments { return pushVerdictArguments(['DEL'], keys); } -export const transformReply = transformReplyNumber; +export declare function transformReply(): number; diff --git a/lib/commands/DISCARD.ts b/lib/commands/DISCARD.ts index b5aaf45cc8d..444f800db80 100644 --- a/lib/commands/DISCARD.ts +++ b/lib/commands/DISCARD.ts @@ -1,7 +1,5 @@ -import { transformReplyString } from './generic-transformers'; - export function transformArguments(): Array { return ['DISCARD']; } -export const transformReply = transformReplyString; +export declare function transformReply(): string; diff --git a/lib/commands/DUMP.ts b/lib/commands/DUMP.ts index 1c72110f21f..de85b889bb9 100644 --- a/lib/commands/DUMP.ts +++ b/lib/commands/DUMP.ts @@ -1,7 +1,5 @@ -import { transformReplyString } from './generic-transformers'; - export function transformArguments(key: string): Array { return ['DUMP', key]; } -export const transformReply = transformReplyString; +export declare function transformReply(): string; diff --git a/lib/commands/ECHO.spec.ts b/lib/commands/ECHO.spec.ts index 4a1bf8fe378..d91b7373950 100644 --- a/lib/commands/ECHO.spec.ts +++ b/lib/commands/ECHO.spec.ts @@ -1,5 +1,5 @@ import { strict as assert } from 'assert'; -import { TestRedisServers, itWithClient, TestRedisClusters, itWithCluster } from '../test-utils'; +import { TestRedisServers, itWithClient } from '../test-utils'; import { transformArguments } from './ECHO'; describe('ECHO', () => { @@ -16,11 +16,4 @@ describe('ECHO', () => { 'message' ); }); - - itWithCluster(TestRedisClusters.OPEN, 'cluster.echo', async cluster => { - assert.equal( - await cluster.echo('message'), - 'message' - ); - }); }); diff --git a/lib/commands/ECHO.ts b/lib/commands/ECHO.ts index 007b8f27646..75a91d4ac91 100644 --- a/lib/commands/ECHO.ts +++ b/lib/commands/ECHO.ts @@ -1,9 +1,7 @@ -import { transformReplyString } from './generic-transformers'; - export const IS_READ_ONLY = true; export function transformArguments(message: string): Array { return ['ECHO', message]; } -export const transformReply = transformReplyString; +export declare function transformReply(): string; diff --git a/lib/commands/EVAL.ts b/lib/commands/EVAL.ts index 89645df9f3e..f269815b7ec 100644 --- a/lib/commands/EVAL.ts +++ b/lib/commands/EVAL.ts @@ -3,7 +3,3 @@ import { EvalOptions, pushEvalArguments } from './generic-transformers'; export function transformArguments(script: string, options?: EvalOptions): Array { return pushEvalArguments(['EVAL', script], options); } - -export function transformReply(reply: unknown): unknown { - return reply; -} diff --git a/lib/commands/EVALSHA.ts b/lib/commands/EVALSHA.ts index a81595bc4c0..105784cf5f8 100644 --- a/lib/commands/EVALSHA.ts +++ b/lib/commands/EVALSHA.ts @@ -3,7 +3,3 @@ import { EvalOptions, pushEvalArguments } from './generic-transformers'; export function transformArguments(sha1: string, options?: EvalOptions): Array { return pushEvalArguments(['EVALSHA', sha1], options); } - -export function transformReply(reply: unknown): unknown { - return reply; -} diff --git a/lib/commands/EXISTS.ts b/lib/commands/EXISTS.ts index 00d10b9eebc..aac164fc953 100644 --- a/lib/commands/EXISTS.ts +++ b/lib/commands/EXISTS.ts @@ -1,11 +1,11 @@ -import { TransformArgumentsReply } from '.'; +import { RedisCommandArguments } from '.'; import { pushVerdictArguments, transformReplyBoolean } from './generic-transformers'; export const FIRST_KEY_INDEX = 1; export const IS_READ_ONLY = true; -export function transformArguments(keys: string | Array): TransformArgumentsReply { +export function transformArguments(keys: string | Array): RedisCommandArguments { return pushVerdictArguments(['EXISTS'], keys); } diff --git a/lib/commands/FAILOVER.ts b/lib/commands/FAILOVER.ts index 11ccb32a5cf..c31dbc063de 100644 --- a/lib/commands/FAILOVER.ts +++ b/lib/commands/FAILOVER.ts @@ -1,5 +1,3 @@ -import { transformReplyString } from './generic-transformers'; - interface FailoverOptions { TO?: { host: string; @@ -32,4 +30,4 @@ export function transformArguments(options?: FailoverOptions): Array { return args; } -export const transformReply = transformReplyString; +export declare function transformReply(): string; diff --git a/lib/commands/FLUSHALL.ts b/lib/commands/FLUSHALL.ts index 4be3474f7e2..967096bb9bd 100644 --- a/lib/commands/FLUSHALL.ts +++ b/lib/commands/FLUSHALL.ts @@ -1,5 +1,3 @@ -import { transformReplyString } from './generic-transformers'; - export enum RedisFlushModes { ASYNC = 'ASYNC', SYNC = 'SYNC' @@ -15,4 +13,4 @@ export function transformArguments(mode?: RedisFlushModes): Array { return args; } -export const transformReply = transformReplyString; +export declare function transformReply(): string; diff --git a/lib/commands/FLUSHDB.ts b/lib/commands/FLUSHDB.ts index a85c0933c4c..5b8060df9eb 100644 --- a/lib/commands/FLUSHDB.ts +++ b/lib/commands/FLUSHDB.ts @@ -1,5 +1,4 @@ import { RedisFlushModes } from './FLUSHALL'; -import { transformReplyString } from './generic-transformers'; export function transformArguments(mode?: RedisFlushModes): Array { const args = ['FLUSHDB']; @@ -11,4 +10,4 @@ export function transformArguments(mode?: RedisFlushModes): Array { return args; } -export const transformReply = transformReplyString; +export declare function transformReply(): string; diff --git a/lib/commands/GEOADD.ts b/lib/commands/GEOADD.ts index 1236563d541..7f5ac5533e3 100644 --- a/lib/commands/GEOADD.ts +++ b/lib/commands/GEOADD.ts @@ -1,4 +1,4 @@ -import { GeoCoordinates, transformReplyNumber } from './generic-transformers'; +import { GeoCoordinates } from './generic-transformers'; interface GeoMember extends GeoCoordinates { member: string; @@ -46,4 +46,4 @@ export function transformArguments(key: string, toAdd: GeoMember | Array { }); }); - itWithClient(TestRedisServers.OPEN, 'client.geoDist', async client => { - assert.equal( - await client.geoDist('key', '1', '2'), - null - ); + describe('client.geoDist', () => { + itWithClient(TestRedisServers.OPEN, 'null', async client => { + assert.equal( + await client.geoDist('key', '1', '2'), + null + ); + }); + + itWithClient(TestRedisServers.OPEN, 'with value', async client => { + const [, dist] = await Promise.all([ + client.geoAdd('key', [{ + member: '1', + longitude: 1, + latitude: 1 + }, { + member: '2', + longitude: 2, + latitude: 2 + }]), + client.geoDist('key', '1', '2') + ]); + + assert.equal( + dist, + 157270.0561 + ); + }); }); itWithCluster(TestRedisClusters.OPEN, 'cluster.geoDist', async cluster => { diff --git a/lib/commands/GEOHASH.ts b/lib/commands/GEOHASH.ts index a95ae443408..8613f37fa43 100644 --- a/lib/commands/GEOHASH.ts +++ b/lib/commands/GEOHASH.ts @@ -1,12 +1,12 @@ -import { TransformArgumentsReply } from '.'; -import { pushVerdictArguments, transformReplyStringArray } from './generic-transformers'; +import { RedisCommandArguments } from '.'; +import { pushVerdictArguments } from './generic-transformers'; export const FIRST_KEY_INDEX = 1; export const IS_READ_ONLY = true; -export function transformArguments(key: string, member: string | Array): TransformArgumentsReply { +export function transformArguments(key: string, member: string | Array): RedisCommandArguments { return pushVerdictArguments(['GEOHASH', key], member); } -export const transformReply = transformReplyStringArray; +export declare function transformReply(): Array; diff --git a/lib/commands/GEOPOS.ts b/lib/commands/GEOPOS.ts index 893048cf6da..95f33d9e3a8 100644 --- a/lib/commands/GEOPOS.ts +++ b/lib/commands/GEOPOS.ts @@ -1,11 +1,11 @@ -import { TransformArgumentsReply } from '.'; +import { RedisCommandArguments } from '.'; import { pushVerdictArguments } from './generic-transformers'; export const FIRST_KEY_INDEX = 1; export const IS_READ_ONLY = true; -export function transformArguments(key: string, member: string | Array): TransformArgumentsReply { +export function transformArguments(key: string, member: string | Array): RedisCommandArguments { return pushVerdictArguments(['GEOPOS', key], member); } diff --git a/lib/commands/GEOSEARCH.ts b/lib/commands/GEOSEARCH.ts index 3872f11c6cb..5453a2ae1b1 100644 --- a/lib/commands/GEOSEARCH.ts +++ b/lib/commands/GEOSEARCH.ts @@ -1,4 +1,4 @@ -import { GeoSearchFrom, GeoSearchBy, GeoSearchOptions, pushGeoSearchArguments, transformReplyStringArray } from './generic-transformers'; +import { GeoSearchFrom, GeoSearchBy, GeoSearchOptions, pushGeoSearchArguments } from './generic-transformers'; export const FIRST_KEY_INDEX = 1; @@ -13,4 +13,4 @@ export function transformArguments( return pushGeoSearchArguments(['GEOSEARCH'], key, from, by, options); } -export const transformReply = transformReplyStringArray; +export declare function transformReply(): Array; diff --git a/lib/commands/GEOSEARCH_WITH.spec.ts b/lib/commands/GEOSEARCH_WITH.spec.ts index a400fb965c6..922c00d7194 100644 --- a/lib/commands/GEOSEARCH_WITH.spec.ts +++ b/lib/commands/GEOSEARCH_WITH.spec.ts @@ -1,5 +1,5 @@ import { strict as assert } from 'assert'; -import { TransformArgumentsReply } from '.'; +import { RedisCommandArguments } from '.'; import { TestRedisServers, itWithClient, TestRedisClusters, itWithCluster, describeHandleMinimumRedisVersion } from '../test-utils'; import { GeoReplyWith } from './generic-transformers'; import { transformArguments } from './GEOSEARCH_WITH'; @@ -8,7 +8,7 @@ describe('GEOSEARCH WITH', () => { describeHandleMinimumRedisVersion([6, 2]); it('transformArguments', () => { - const expectedReply: TransformArgumentsReply = ['GEOSEARCH', 'key', 'FROMMEMBER', 'member', 'BYRADIUS', '1', 'm', 'WITHDIST'] + const expectedReply: RedisCommandArguments = ['GEOSEARCH', 'key', 'FROMMEMBER', 'member', 'BYRADIUS', '1', 'm', 'WITHDIST'] expectedReply.preserve = ['WITHDIST']; assert.deepEqual( diff --git a/lib/commands/GEOSEARCH_WITH.ts b/lib/commands/GEOSEARCH_WITH.ts index ef19ca5dfc9..cd461c9677f 100644 --- a/lib/commands/GEOSEARCH_WITH.ts +++ b/lib/commands/GEOSEARCH_WITH.ts @@ -1,4 +1,4 @@ -import { TransformArgumentsReply } from '.'; +import { RedisCommandArguments } from '.'; import { GeoSearchFrom, GeoSearchBy, GeoReplyWith, GeoSearchOptions, transformGeoMembersWithReply } from './generic-transformers'; import { transformArguments as geoSearchTransformArguments } from './GEOSEARCH'; @@ -10,8 +10,8 @@ export function transformArguments( by: GeoSearchBy, replyWith: Array, options?: GeoSearchOptions -): TransformArgumentsReply { - const args: TransformArgumentsReply = geoSearchTransformArguments(key, from, by, options); +): RedisCommandArguments { + const args: RedisCommandArguments = geoSearchTransformArguments(key, from, by, options); args.push(...replyWith); diff --git a/lib/commands/GET.ts b/lib/commands/GET.ts index 541790e54e4..0e89a4e6a74 100644 --- a/lib/commands/GET.ts +++ b/lib/commands/GET.ts @@ -1,12 +1,11 @@ -import { TransformArgumentsReply } from '.'; -import { transformReplyStringNull } from './generic-transformers'; +import { RedisCommandArguments } from '.'; export const FIRST_KEY_INDEX = 1; export const IS_READ_ONLY = true; -export function transformArguments(key: string | Buffer): TransformArgumentsReply { +export function transformArguments(key: string | Buffer): RedisCommandArguments { return ['GET', key]; } -export const transformReply = transformReplyStringNull; +export declare function transformReply(): string | null; diff --git a/lib/commands/GETBIT.ts b/lib/commands/GETBIT.ts index c7e878f75a8..1e5fd884251 100644 --- a/lib/commands/GETBIT.ts +++ b/lib/commands/GETBIT.ts @@ -1,4 +1,4 @@ -import { transformReplyBit } from './generic-transformers'; +import { BitValue } from './generic-transformers'; export const FIRST_KEY_INDEX = 1; @@ -8,4 +8,4 @@ export function transformArguments(key: string, offset: number): Array { return ['GETBIT', key, offset.toString()]; } -export const transformReply = transformReplyBit; +export declare function transformReply(): BitValue; diff --git a/lib/commands/GETDEL.ts b/lib/commands/GETDEL.ts index 218e057637d..de99cc6357b 100644 --- a/lib/commands/GETDEL.ts +++ b/lib/commands/GETDEL.ts @@ -1,9 +1,7 @@ -import { transformReplyStringNull } from './generic-transformers'; - export const FIRST_KEY_INDEX = 1; export function transformArguments(key: string): Array { return ['GETDEL', key]; } -export const transformReply = transformReplyStringNull; +export declare function transformReply(): string | null; diff --git a/lib/commands/GETEX.ts b/lib/commands/GETEX.ts index 214dae5c7ab..cd4f283eee3 100644 --- a/lib/commands/GETEX.ts +++ b/lib/commands/GETEX.ts @@ -1,5 +1,5 @@ -import { TransformArgumentsReply } from '.'; -import { transformEXAT, transformPXAT, transformReplyStringNull } from './generic-transformers'; +import { RedisCommandArguments } from '.'; +import { transformEXAT, transformPXAT } from './generic-transformers'; export const FIRST_KEY_INDEX = 1; @@ -15,7 +15,7 @@ type GetExModes = { PERSIST: true; }; -export function transformArguments(key: string, mode: GetExModes): TransformArgumentsReply { +export function transformArguments(key: string, mode: GetExModes): RedisCommandArguments { const args = ['GETEX', key]; if ('EX' in mode) { @@ -33,4 +33,4 @@ export function transformArguments(key: string, mode: GetExModes): TransformArgu return args; } -export const transformReply = transformReplyStringNull; +export declare function transformReply(): string | null; diff --git a/lib/commands/GETRANGE.ts b/lib/commands/GETRANGE.ts index 9488dd53d5e..babb0a6a7c2 100644 --- a/lib/commands/GETRANGE.ts +++ b/lib/commands/GETRANGE.ts @@ -1,5 +1,3 @@ -import { transformReplyString } from './generic-transformers'; - export const FIRST_KEY_INDEX = 1; export const IS_READ_ONLY = true; @@ -8,4 +6,4 @@ export function transformArguments(key: string, start: number, end: number): Arr return ['GETRANGE', key, start.toString(), end.toString()]; } -export const transformReply = transformReplyString; +export declare function transformReply(): string; diff --git a/lib/commands/GETSET.ts b/lib/commands/GETSET.ts index 1b9b9d6bc75..4d3516866fb 100644 --- a/lib/commands/GETSET.ts +++ b/lib/commands/GETSET.ts @@ -1,9 +1,7 @@ -import { transformReplyStringNull } from './generic-transformers'; - export const FIRST_KEY_INDEX = 1; export function transformArguments(key: string, value: string): Array { return ['GETSET', key, value]; } -export const transformReply = transformReplyStringNull; +export declare function transformReply(): string | null; diff --git a/lib/commands/GET_BUFFER.ts b/lib/commands/GET_BUFFER.ts index 9d281961130..2f08ecb708a 100644 --- a/lib/commands/GET_BUFFER.ts +++ b/lib/commands/GET_BUFFER.ts @@ -1,7 +1,5 @@ -import { transformReplyBufferNull } from './generic-transformers'; - export { FIRST_KEY_INDEX, IS_READ_ONLY, transformArguments } from './GET'; export const BUFFER_MODE = true; -export const transformReply = transformReplyBufferNull; +export declare function transformReply(): Buffer | null; diff --git a/lib/commands/HDEL.ts b/lib/commands/HDEL.ts index 4785b0e67f9..58d057ebf10 100644 --- a/lib/commands/HDEL.ts +++ b/lib/commands/HDEL.ts @@ -1,10 +1,10 @@ -import { TransformArgumentsReply } from '.'; -import { pushVerdictArguments, transformReplyNumber } from './generic-transformers'; +import { RedisCommandArguments } from '.'; +import { pushVerdictArguments } from './generic-transformers'; export const FIRST_KEY_INDEX = 1; -export function transformArguments(key: string, field: string | Array): TransformArgumentsReply { +export function transformArguments(key: string, field: string | Array): RedisCommandArguments { return pushVerdictArguments(['HDEL', key], field); } -export const transformReply = transformReplyNumber; +export declare function transformReply(): number; diff --git a/lib/commands/HELLO.ts b/lib/commands/HELLO.ts index efb96890fcd..86dae2a1d71 100644 --- a/lib/commands/HELLO.ts +++ b/lib/commands/HELLO.ts @@ -12,7 +12,7 @@ export function transformArguments(options?: HelloOptions): Array { if (options) { args.push(options.protover.toString()); - if (options?.auth) { + if (options.auth) { args.push('AUTH', options.auth.username, options.auth.password); } diff --git a/lib/commands/HINCRBY.ts b/lib/commands/HINCRBY.ts index 192dac456e1..8f0e99d41f4 100644 --- a/lib/commands/HINCRBY.ts +++ b/lib/commands/HINCRBY.ts @@ -1,9 +1,7 @@ -import { transformReplyNumber } from './generic-transformers'; - export const FIRST_KEY_INDEX = 1; export function transformArguments(key: string, field: string, increment: number): Array { return ['HINCRBY', key, field, increment.toString()]; } -export const transformReply = transformReplyNumber; +export declare function transformReply(): number; diff --git a/lib/commands/HINCRBYFLOAT.ts b/lib/commands/HINCRBYFLOAT.ts index 10c949b8d93..262a7d5d6b0 100644 --- a/lib/commands/HINCRBYFLOAT.ts +++ b/lib/commands/HINCRBYFLOAT.ts @@ -1,9 +1,7 @@ -import { transformReplyNumber } from './generic-transformers'; - export const FIRST_KEY_INDEX = 1; export function transformArguments(key: string, field: string, increment: number): Array { return ['HINCRBYFLOAT', key, field, increment.toString()]; } -export const transformReply = transformReplyNumber; +export declare function transformReply(): number; diff --git a/lib/commands/HKEYS.ts b/lib/commands/HKEYS.ts index d79d2c1d134..358f08fc762 100644 --- a/lib/commands/HKEYS.ts +++ b/lib/commands/HKEYS.ts @@ -1,9 +1,7 @@ -import { transformReplyStringArray } from './generic-transformers'; - export const FIRST_KEY_INDEX = 1; export function transformArguments(key: string): Array { return ['HKEYS', key]; } -export const transformReply = transformReplyStringArray; +export declare function transformReply(): Array; diff --git a/lib/commands/HLEN.ts b/lib/commands/HLEN.ts index ba7ccc3aed9..5c717ad7c54 100644 --- a/lib/commands/HLEN.ts +++ b/lib/commands/HLEN.ts @@ -1,9 +1,7 @@ -import { transformReplyNumber } from './generic-transformers'; - export const FIRST_KEY_INDEX = 1; export function transformArguments(key: string): Array { return ['HLEN', key]; } -export const transformReply = transformReplyNumber; +export declare function transformReply(): number; diff --git a/lib/commands/HMGET.ts b/lib/commands/HMGET.ts index 9f26eeba640..7ca3a55b69c 100644 --- a/lib/commands/HMGET.ts +++ b/lib/commands/HMGET.ts @@ -1,12 +1,12 @@ -import { TransformArgumentsReply } from '.'; -import { pushVerdictArguments, transformReplyStringArray } from './generic-transformers'; +import { RedisCommandArguments } from '.'; +import { pushVerdictArguments } from './generic-transformers'; export const FIRST_KEY_INDEX = 1; export const IS_READ_ONLY = true; -export function transformArguments(key: string, fields: string | Array): TransformArgumentsReply { +export function transformArguments(key: string, fields: string | Array): RedisCommandArguments { return pushVerdictArguments(['HMGET', key], fields); } -export const transformReply = transformReplyStringArray; +export declare function transformReply(): Array; diff --git a/lib/commands/HRANDFIELD.ts b/lib/commands/HRANDFIELD.ts index e0c6ee392d5..24ca9b83d56 100644 --- a/lib/commands/HRANDFIELD.ts +++ b/lib/commands/HRANDFIELD.ts @@ -1,9 +1,7 @@ -import { transformReplyStringNull } from './generic-transformers'; - export const FIRST_KEY_INDEX = 1; export function transformArguments(key: string): Array { return ['HRANDFIELD', key]; } -export const transformReply = transformReplyStringNull; \ No newline at end of file +export declare function transformReply(): string | null; diff --git a/lib/commands/HRANDFIELD_COUNT.ts b/lib/commands/HRANDFIELD_COUNT.ts index d615b86ee8b..c0a8b1d449f 100644 --- a/lib/commands/HRANDFIELD_COUNT.ts +++ b/lib/commands/HRANDFIELD_COUNT.ts @@ -1,4 +1,3 @@ -import { transformReplyStringArray } from './generic-transformers'; import { transformArguments as transformHRandFieldArguments } from './HRANDFIELD'; export { FIRST_KEY_INDEX } from './HRANDFIELD'; @@ -10,4 +9,4 @@ export function transformArguments(key: string, count: number): Array { ]; } -export const transformReply = transformReplyStringArray; +export declare function transformReply(): Array; diff --git a/lib/commands/HSET.spec.ts b/lib/commands/HSET.spec.ts index 601e7f967e1..e8dfe7865d3 100644 --- a/lib/commands/HSET.spec.ts +++ b/lib/commands/HSET.spec.ts @@ -26,12 +26,10 @@ describe('HSET', () => { }); it('Object', () => { - it('Array', () => { - assert.deepEqual( - transformArguments('key', { field: 'value' }), - ['HSET', 'key', 'field', 'value'] - ); - }); + assert.deepEqual( + transformArguments('key', { field: 'value' }), + ['HSET', 'key', 'field', 'value'] + ); }); }); diff --git a/lib/commands/HSET.ts b/lib/commands/HSET.ts index cbd46061ad8..1d4acd6c018 100644 --- a/lib/commands/HSET.ts +++ b/lib/commands/HSET.ts @@ -1,5 +1,4 @@ -import { TransformArgumentsReply } from '.'; -import { transformReplyString } from './generic-transformers'; +import { RedisCommandArguments } from '.'; type HSETObject = Record; @@ -15,7 +14,7 @@ type SingleFieldArguments = [...generic: GenericArguments, field: string, value: type MultipleFieldsArguments = [...generic: GenericArguments, value: HSETObject | HSETMap | HSETTuples]; -export function transformArguments(...[ key, value, fieldValue ]: SingleFieldArguments | MultipleFieldsArguments): TransformArgumentsReply { +export function transformArguments(...[ key, value, fieldValue ]: SingleFieldArguments | MultipleFieldsArguments): RedisCommandArguments { const args = ['HSET', key]; if (typeof value === 'string') { @@ -47,4 +46,4 @@ function pushObject(args: Array, object: HSETObject): void { } } -export const transformReply = transformReplyString; +export declare function transformReply(): string; diff --git a/lib/commands/HSTRLEN.ts b/lib/commands/HSTRLEN.ts index 4181cde8517..d0138eb3ec9 100644 --- a/lib/commands/HSTRLEN.ts +++ b/lib/commands/HSTRLEN.ts @@ -1,9 +1,7 @@ -import { transformReplyNumber } from './generic-transformers'; - export const FIRST_KEY_INDEX = 1; export function transformArguments(key: string, field: string): Array { return ['HSTRLEN', key, field]; } -export const transformReply = transformReplyNumber; +export declare function transformReply(): number; diff --git a/lib/commands/HVALS.ts b/lib/commands/HVALS.ts index 7f924623cf3..cb17fdb29be 100644 --- a/lib/commands/HVALS.ts +++ b/lib/commands/HVALS.ts @@ -1,9 +1,7 @@ -import { transformReplyStringArray } from './generic-transformers'; - export const FIRST_KEY_INDEX = 1; export function transformArguments(key: string): Array { return ['HVALS', key]; } -export const transformReply = transformReplyStringArray; +export declare function transformReply(): Array; diff --git a/lib/commands/INCR.ts b/lib/commands/INCR.ts index 00747f0f7e2..f7b81013258 100644 --- a/lib/commands/INCR.ts +++ b/lib/commands/INCR.ts @@ -1,9 +1,7 @@ -import { transformReplyNumber } from './generic-transformers'; - export const FIRST_KEY_INDEX = 1; export function transformArguments(key: string): Array { return ['INCR', key]; } -export const transformReply = transformReplyNumber; +export declare function transformReply(): number; diff --git a/lib/commands/INCRBY.ts b/lib/commands/INCRBY.ts index 8fd31d03380..8f2a4406bf9 100644 --- a/lib/commands/INCRBY.ts +++ b/lib/commands/INCRBY.ts @@ -1,9 +1,7 @@ -import { transformReplyNumber } from './generic-transformers'; - export const FIRST_KEY_INDEX = 1; export function transformArguments(key: string, increment: number): Array { return ['INCRBY', key, increment.toString()]; } -export const transformReply = transformReplyNumber; +export declare function transformReply(): number; diff --git a/lib/commands/INCRBYFLOAT.ts b/lib/commands/INCRBYFLOAT.ts index 38912cbdc94..a5f99820cb3 100644 --- a/lib/commands/INCRBYFLOAT.ts +++ b/lib/commands/INCRBYFLOAT.ts @@ -1,9 +1,7 @@ -import { transformReplyString } from './generic-transformers'; - export const FIRST_KEY_INDEX = 1; export function transformArguments(key: string, increment: number): Array { return ['INCRBYFLOAT', key, increment.toString()]; } -export const transformReply = transformReplyString; +export declare function transformReply(): string; diff --git a/lib/commands/INFO.ts b/lib/commands/INFO.ts index 437b5e5b83b..8ab24221b26 100644 --- a/lib/commands/INFO.ts +++ b/lib/commands/INFO.ts @@ -1,5 +1,3 @@ -import { transformReplyString } from './generic-transformers'; - export const IS_READ_ONLY = true; export function transformArguments(section?: string): Array { @@ -12,4 +10,4 @@ export function transformArguments(section?: string): Array { return args; } -export const transformReply = transformReplyString; +export declare function transformReply(): string; diff --git a/lib/commands/LASTSAVE.spec.ts b/lib/commands/LASTSAVE.spec.ts index 1b13bed5d20..b8d801f70b5 100644 --- a/lib/commands/LASTSAVE.spec.ts +++ b/lib/commands/LASTSAVE.spec.ts @@ -1,5 +1,5 @@ import { strict as assert } from 'assert'; -import { TestRedisServers, itWithClient, itWithCluster, TestRedisClusters } from '../test-utils'; +import { TestRedisServers, itWithClient } from '../test-utils'; import { transformArguments } from './LASTSAVE'; describe('LASTSAVE', () => { @@ -13,8 +13,4 @@ describe('LASTSAVE', () => { itWithClient(TestRedisServers.OPEN, 'client.lastSave', async client => { assert.ok((await client.lastSave()) instanceof Date); }); - - itWithCluster(TestRedisClusters.OPEN, 'cluster.lastSave', async cluster => { - assert.ok((await cluster.lastSave()) instanceof Date); - }); }); diff --git a/lib/commands/LINDEX.ts b/lib/commands/LINDEX.ts index 0237a4705b1..9a89b41da55 100644 --- a/lib/commands/LINDEX.ts +++ b/lib/commands/LINDEX.ts @@ -1,5 +1,3 @@ -import { transformReplyNumberNull } from './generic-transformers'; - export const FIRST_KEY_INDEX = 1; export const IS_READ_ONLY = true; @@ -8,4 +6,4 @@ export function transformArguments(key: string, element: string): Array return ['LINDEX', key, element]; } -export const transformReply = transformReplyNumberNull; +export declare function transformReply(): number | null; diff --git a/lib/commands/LINSERT.ts b/lib/commands/LINSERT.ts index 40bd4e3d4df..b1d377f92aa 100644 --- a/lib/commands/LINSERT.ts +++ b/lib/commands/LINSERT.ts @@ -1,5 +1,3 @@ -import { transformReplyNumber } from './generic-transformers'; - export const FIRST_KEY_INDEX = 1; type LInsertPosition = 'BEFORE' | 'AFTER'; @@ -19,4 +17,4 @@ export function transformArguments( ]; } -export const transformReply = transformReplyNumber; +export declare function transformReply(): number; diff --git a/lib/commands/LLEN.ts b/lib/commands/LLEN.ts index 61aae604c97..49ac1d1916c 100644 --- a/lib/commands/LLEN.ts +++ b/lib/commands/LLEN.ts @@ -1,5 +1,3 @@ -import { transformReplyNumber } from './generic-transformers'; - export const FIRST_KEY_INDEX = 1; export const IS_READ_ONLY = true; @@ -8,4 +6,4 @@ export function transformArguments(key: string): Array { return ['LLEN', key]; } -export const transformReply = transformReplyNumber; \ No newline at end of file +export declare function transformReply(): number; diff --git a/lib/commands/LMOVE.ts b/lib/commands/LMOVE.ts index 1e99297d812..111e758a0a4 100644 --- a/lib/commands/LMOVE.ts +++ b/lib/commands/LMOVE.ts @@ -1,5 +1,3 @@ -import { transformReplyStringNull } from './generic-transformers'; - export type LMoveSide = 'LEFT' | 'RIGHT'; export const FIRST_KEY_INDEX = 1; @@ -19,4 +17,4 @@ export function transformArguments( ]; } -export const transformReply = transformReplyStringNull; +export declare function transformReply(): string | null; diff --git a/lib/commands/LOLWUT.spec.ts b/lib/commands/LOLWUT.spec.ts index 8e77b85b599..8f4478aecc7 100644 --- a/lib/commands/LOLWUT.spec.ts +++ b/lib/commands/LOLWUT.spec.ts @@ -1,5 +1,5 @@ import { strict as assert } from 'assert'; -import { TestRedisServers, itWithClient, itWithCluster, TestRedisClusters } from '../test-utils'; +import { TestRedisServers, itWithClient } from '../test-utils'; import { transformArguments } from './LOLWUT'; describe('LOLWUT', () => { @@ -25,7 +25,7 @@ describe('LOLWUT', () => { ); }); }); - + itWithClient(TestRedisServers.OPEN, 'client.LOLWUT', async client => { assert.equal( @@ -33,11 +33,4 @@ describe('LOLWUT', () => { 'string' ); }); - - itWithCluster(TestRedisClusters.OPEN, 'cluster.LOLWUT', async cluster => { - assert.equal( - typeof (await cluster.LOLWUT()), - 'string' - ); - }); }); diff --git a/lib/commands/LOLWUT.ts b/lib/commands/LOLWUT.ts index f0cd20d4471..cfe01adbcf2 100644 --- a/lib/commands/LOLWUT.ts +++ b/lib/commands/LOLWUT.ts @@ -1,5 +1,3 @@ -import { transformReplyString } from './generic-transformers'; - export const IS_READ_ONLY = true; export function transformArguments(version?: number, ...optionalArguments: Array): Array { @@ -16,4 +14,4 @@ export function transformArguments(version?: number, ...optionalArguments: Array return args; } -export const transformReply = transformReplyString; +export declare function transformReply(): string; diff --git a/lib/commands/LPOP.ts b/lib/commands/LPOP.ts index 30595a5491a..9bf340e07c9 100644 --- a/lib/commands/LPOP.ts +++ b/lib/commands/LPOP.ts @@ -1,9 +1,7 @@ -import { transformReplyStringNull } from './generic-transformers'; - export const FIRST_KEY_INDEX = 1; export function transformArguments(key: string): Array { return ['LPOP', key]; } -export const transformReply = transformReplyStringNull; +export declare function transformReply(): string | null; diff --git a/lib/commands/LPOP_COUNT.ts b/lib/commands/LPOP_COUNT.ts index 432d2c47c09..828b0251ff6 100644 --- a/lib/commands/LPOP_COUNT.ts +++ b/lib/commands/LPOP_COUNT.ts @@ -1,9 +1,7 @@ -import { transformReplyStringArrayNull } from './generic-transformers'; - export const FIRST_KEY_INDEX = 1; export function transformArguments(key: string, count: number): Array { return ['LPOP', key, count.toString()]; } -export const transformReply = transformReplyStringArrayNull; +export declare function transformReply(): Array | null; diff --git a/lib/commands/LPOS.ts b/lib/commands/LPOS.ts index fc160dbcbb8..3371f01d67e 100644 --- a/lib/commands/LPOS.ts +++ b/lib/commands/LPOS.ts @@ -1,5 +1,3 @@ -import { transformReplyNumberNull } from './generic-transformers'; - export const FIRST_KEY_INDEX = 1; export const IS_READ_ONLY = true; @@ -23,4 +21,4 @@ export function transformArguments(key: string, element: string, options?: LPosO return args; } -export const transformReply = transformReplyNumberNull; +export declare function transformReply(): number | null; diff --git a/lib/commands/LPOS_COUNT.ts b/lib/commands/LPOS_COUNT.ts index 2a1d3068583..b5a60317eae 100644 --- a/lib/commands/LPOS_COUNT.ts +++ b/lib/commands/LPOS_COUNT.ts @@ -1,4 +1,3 @@ -import { transformReplyNumberArray } from './generic-transformers'; import { LPosOptions } from './LPOS'; export { FIRST_KEY_INDEX, IS_READ_ONLY } from './LPOS'; @@ -19,4 +18,4 @@ export function transformArguments(key: string, element: string, count: number, return args; } -export const transformReply = transformReplyNumberArray; +export declare function transformReply(): Array; diff --git a/lib/commands/LPUSH.ts b/lib/commands/LPUSH.ts index 7416d4946ea..b9644344772 100644 --- a/lib/commands/LPUSH.ts +++ b/lib/commands/LPUSH.ts @@ -1,9 +1,9 @@ -import { TransformArgumentsReply } from '.'; -import { pushVerdictArguments, transformReplyNumber } from './generic-transformers'; +import { RedisCommandArguments } from '.'; +import { pushVerdictArguments } from './generic-transformers'; export const FIRST_KEY_INDEX = 1; -export function transformArguments(key: string, elements: string | Array): TransformArgumentsReply { +export function transformArguments(key: string, elements: string | Array): RedisCommandArguments { return pushVerdictArguments(['LPUSH', key], elements);} -export const transformReply = transformReplyNumber; +export declare function transformReply(): number; diff --git a/lib/commands/LPUSHX.ts b/lib/commands/LPUSHX.ts index f89623ace3a..5f92d84d3b0 100644 --- a/lib/commands/LPUSHX.ts +++ b/lib/commands/LPUSHX.ts @@ -1,10 +1,10 @@ -import { TransformArgumentsReply } from '.'; -import { pushVerdictArguments, transformReplyNumber } from './generic-transformers'; +import { RedisCommandArguments } from '.'; +import { pushVerdictArguments } from './generic-transformers'; export const FIRST_KEY_INDEX = 1; -export function transformArguments(key: string, element: string | Array): TransformArgumentsReply { +export function transformArguments(key: string, element: string | Array): RedisCommandArguments { return pushVerdictArguments(['LPUSHX', key], element); } -export const transformReply = transformReplyNumber; +export declare function transformReply(): number; diff --git a/lib/commands/LRANGE.ts b/lib/commands/LRANGE.ts index cbed9a75ded..7ea97ec4f43 100644 --- a/lib/commands/LRANGE.ts +++ b/lib/commands/LRANGE.ts @@ -1,5 +1,3 @@ -import { transformReplyStringArray } from './generic-transformers'; - export const FIRST_KEY_INDEX = 1; export const IS_READ_ONLY = true; @@ -13,4 +11,4 @@ export function transformArguments(key: string, start: number, stop: number): Ar ]; } -export const transformReply = transformReplyStringArray; +export declare function transformReply(): Array; diff --git a/lib/commands/LREM.ts b/lib/commands/LREM.ts index 5eabbc9194e..b43f8a28427 100644 --- a/lib/commands/LREM.ts +++ b/lib/commands/LREM.ts @@ -1,5 +1,3 @@ -import { transformReplyNumber } from './generic-transformers'; - export const FIRST_KEY_INDEX = 1; export function transformArguments(key: string, count: number, element: string): Array { @@ -11,4 +9,4 @@ export function transformArguments(key: string, count: number, element: string): ]; } -export const transformReply = transformReplyNumber; +export declare function transformReply(): number; diff --git a/lib/commands/LSET.ts b/lib/commands/LSET.ts index 0e910dd6a1c..1511fb9751e 100644 --- a/lib/commands/LSET.ts +++ b/lib/commands/LSET.ts @@ -1,5 +1,3 @@ -import { transformReplyString } from './generic-transformers'; - export const FIRST_KEY_INDEX = 1; export function transformArguments(key: string, index: number, element: string): Array { @@ -11,4 +9,4 @@ export function transformArguments(key: string, index: number, element: string): ]; } -export const transformReply = transformReplyString; +export declare function transformReply(): string; diff --git a/lib/commands/LTRIM.ts b/lib/commands/LTRIM.ts index 3ccfa751af6..018afd90a15 100644 --- a/lib/commands/LTRIM.ts +++ b/lib/commands/LTRIM.ts @@ -1,5 +1,3 @@ -import { transformReplyString } from './generic-transformers'; - export const FIRST_KEY_INDEX = 1; export function transformArguments(key: string, start: number, stop: number): Array { @@ -11,4 +9,4 @@ export function transformArguments(key: string, start: number, stop: number): Ar ] } -export const transformReply = transformReplyString; +export declare function transformReply(): string; diff --git a/lib/commands/MEMORY_DOCTOR.spec.ts b/lib/commands/MEMORY_DOCTOR.spec.ts index da883deeb77..1b4d16fa0db 100644 --- a/lib/commands/MEMORY_DOCTOR.spec.ts +++ b/lib/commands/MEMORY_DOCTOR.spec.ts @@ -1,5 +1,5 @@ import { strict as assert } from 'assert'; -import { TestRedisServers, itWithClient, TestRedisClusters, itWithCluster } from '../test-utils'; +import { TestRedisServers, itWithClient } from '../test-utils'; import { transformArguments } from './MEMORY_DOCTOR'; describe('MEMORY DOCTOR', () => { @@ -16,11 +16,4 @@ describe('MEMORY DOCTOR', () => { 'string' ); }); - - itWithCluster(TestRedisClusters.OPEN, 'cluster.memoryDoctor', async cluster => { - assert.equal( - typeof (await cluster.memoryDoctor()), - 'string' - ); - }); }); diff --git a/lib/commands/MEMORY_DOCTOR.ts b/lib/commands/MEMORY_DOCTOR.ts index 0d02bf93360..95a37246ffa 100644 --- a/lib/commands/MEMORY_DOCTOR.ts +++ b/lib/commands/MEMORY_DOCTOR.ts @@ -1,7 +1,5 @@ -import { transformReplyString } from './generic-transformers'; - export function transformArguments(): Array { return ['MEMORY', 'DOCTOR']; } -export const transformReply = transformReplyString; +export declare function transformReply(): string; diff --git a/lib/commands/MEMORY_MALLOC-STATS.spec.ts b/lib/commands/MEMORY_MALLOC-STATS.spec.ts index 2750ebdf7a0..034b94f7c66 100644 --- a/lib/commands/MEMORY_MALLOC-STATS.spec.ts +++ b/lib/commands/MEMORY_MALLOC-STATS.spec.ts @@ -1,5 +1,5 @@ import { strict as assert } from 'assert'; -import { TestRedisServers, itWithClient, TestRedisClusters, itWithCluster } from '../test-utils'; +import { TestRedisServers, itWithClient } from '../test-utils'; import { transformArguments } from './MEMORY_MALLOC-STATS'; describe('MEMORY MALLOC-STATS', () => { @@ -16,11 +16,4 @@ describe('MEMORY MALLOC-STATS', () => { 'string' ); }); - - itWithCluster(TestRedisClusters.OPEN, 'cluster.memoryDoctor', async cluster => { - assert.equal( - typeof (await cluster.memoryDoctor()), - 'string' - ); - }); }); diff --git a/lib/commands/MEMORY_MALLOC-STATS.ts b/lib/commands/MEMORY_MALLOC-STATS.ts index 7dd997c48b1..3977e3a1de4 100644 --- a/lib/commands/MEMORY_MALLOC-STATS.ts +++ b/lib/commands/MEMORY_MALLOC-STATS.ts @@ -1,7 +1,5 @@ -import { transformReplyString } from './generic-transformers'; - export function transformArguments(): Array { return ['MEMORY', 'MALLOC-STATS']; } -export const transformReply = transformReplyString; +export declare function transformReply(): string; diff --git a/lib/commands/MEMORY_PURGE.spec.ts b/lib/commands/MEMORY_PURGE.spec.ts index ac9198ccfc8..97ca6feebf6 100644 --- a/lib/commands/MEMORY_PURGE.spec.ts +++ b/lib/commands/MEMORY_PURGE.spec.ts @@ -1,5 +1,5 @@ import { strict as assert } from 'assert'; -import { TestRedisServers, itWithClient, TestRedisClusters, itWithCluster } from '../test-utils'; +import { TestRedisServers, itWithClient } from '../test-utils'; import { transformArguments } from './MEMORY_PURGE'; describe('MEMORY PURGE', () => { @@ -16,11 +16,4 @@ describe('MEMORY PURGE', () => { 'OK' ); }); - - itWithCluster(TestRedisClusters.OPEN, 'cluster.memoryPurge', async cluster => { - assert.equal( - await cluster.memoryPurge(), - 'OK' - ); - }); }); diff --git a/lib/commands/MEMORY_PURGE.ts b/lib/commands/MEMORY_PURGE.ts index 7aaeee7e6aa..cfa38179273 100644 --- a/lib/commands/MEMORY_PURGE.ts +++ b/lib/commands/MEMORY_PURGE.ts @@ -1,7 +1,5 @@ -import { transformReplyString } from './generic-transformers'; - export function transformArguments(): Array { return ['MEMORY', 'PURGE']; } -export const transformReply = transformReplyString; +export declare function transformReply(): string; diff --git a/lib/commands/MEMORY_USAGE.spec.ts b/lib/commands/MEMORY_USAGE.spec.ts index 7487e7e4ffc..90dff62c674 100644 --- a/lib/commands/MEMORY_USAGE.spec.ts +++ b/lib/commands/MEMORY_USAGE.spec.ts @@ -1,5 +1,5 @@ import { strict as assert } from 'assert'; -import { TestRedisServers, itWithClient, TestRedisClusters, itWithCluster } from '../test-utils'; +import { TestRedisServers, itWithClient } from '../test-utils'; import { transformArguments } from './MEMORY_USAGE'; describe('MEMORY USAGE', () => { @@ -27,11 +27,4 @@ describe('MEMORY USAGE', () => { null ); }); - - itWithCluster(TestRedisClusters.OPEN, 'cluster.memoryUsage', async cluster => { - assert.equal( - await cluster.memoryUsage('key'), - null - ); - }); }); diff --git a/lib/commands/MEMORY_USAGE.ts b/lib/commands/MEMORY_USAGE.ts index 0868b162268..959cdb0a0c4 100644 --- a/lib/commands/MEMORY_USAGE.ts +++ b/lib/commands/MEMORY_USAGE.ts @@ -1,5 +1,3 @@ -import { transformReplyNumberNull } from './generic-transformers'; - export const FIRST_KEY_INDEX = 1; export const IS_READ_ONLY = true; @@ -18,4 +16,4 @@ export function transformArguments(key: string, options?: MemoryUsageOptions): A return args; } -export const transformReply = transformReplyNumberNull; +export declare function transformReply(): number | null; diff --git a/lib/commands/MGET.ts b/lib/commands/MGET.ts index fdf5b3dde85..6d5b9073d49 100644 --- a/lib/commands/MGET.ts +++ b/lib/commands/MGET.ts @@ -1,5 +1,3 @@ -import { transformReplyStringNullArray } from './generic-transformers'; - export const FIRST_KEY_INDEX = 1; export const IS_READ_ONLY = true; @@ -8,4 +6,4 @@ export function transformArguments(keys: Array): Array { return ['MGET', ...keys]; } -export const transformReply = transformReplyStringNullArray; +export declare function transformReply(): Array; diff --git a/lib/commands/MIGRATE.ts b/lib/commands/MIGRATE.ts index 14dbe741be2..4d2795123a8 100644 --- a/lib/commands/MIGRATE.ts +++ b/lib/commands/MIGRATE.ts @@ -1,5 +1,4 @@ import { AuthOptions } from './AUTH'; -import { transformReplyString } from './generic-transformers'; interface MigrateOptions { COPY?: true; @@ -62,4 +61,4 @@ export function transformArguments( return args; } -export const transformReply = transformReplyString; +export declare function transformReply(): string; diff --git a/lib/commands/MODULE_LIST.ts b/lib/commands/MODULE_LIST.ts index 53ad14b68eb..d75b2428308 100644 --- a/lib/commands/MODULE_LIST.ts +++ b/lib/commands/MODULE_LIST.ts @@ -1,7 +1,5 @@ -import { transformReplyString } from './generic-transformers'; - export function transformArguments(): Array { return ['MODULE', 'LIST']; } -export const transformReply = transformReplyString; +export declare function transformReply(): string; diff --git a/lib/commands/MODULE_LOAD.ts b/lib/commands/MODULE_LOAD.ts index cd2347af24c..b44b4b57ce6 100644 --- a/lib/commands/MODULE_LOAD.ts +++ b/lib/commands/MODULE_LOAD.ts @@ -1,5 +1,3 @@ -import { transformReplyString } from './generic-transformers'; - export function transformArguments(path: string, moduleArgs?: Array): Array { const args = ['MODULE', 'LOAD', path]; @@ -10,4 +8,4 @@ export function transformArguments(path: string, moduleArgs?: Array): Ar return args; } -export const transformReply = transformReplyString; +export declare function transformReply(): string; diff --git a/lib/commands/MODULE_UNLOAD.ts b/lib/commands/MODULE_UNLOAD.ts index 3737784f000..d5927778fe6 100644 --- a/lib/commands/MODULE_UNLOAD.ts +++ b/lib/commands/MODULE_UNLOAD.ts @@ -1,7 +1,5 @@ -import { transformReplyString } from './generic-transformers'; - export function transformArguments(name: string): Array { return ['MODULE', 'UNLOAD', name]; } -export const transformReply = transformReplyString; +export declare function transformReply(): string; diff --git a/lib/commands/MSET.ts b/lib/commands/MSET.ts index d51790caeed..d3e290df70f 100644 --- a/lib/commands/MSET.ts +++ b/lib/commands/MSET.ts @@ -1,5 +1,3 @@ -import { transformReplyString } from './generic-transformers'; - export const FIRST_KEY_INDEX = 1; export function transformArguments(toSet: Array<[string, string]> | Array | Record): Array { @@ -16,4 +14,4 @@ export function transformArguments(toSet: Array<[string, string]> | Array): TransformArgumentsReply { +export function transformArguments(key: string, element: string | Array): RedisCommandArguments { return pushVerdictArguments(['PFADD', key], element); } diff --git a/lib/commands/PFCOUNT.ts b/lib/commands/PFCOUNT.ts index 52963697adf..ec6c0906041 100644 --- a/lib/commands/PFCOUNT.ts +++ b/lib/commands/PFCOUNT.ts @@ -1,10 +1,10 @@ -import { TransformArgumentsReply } from '.'; -import { pushVerdictArguments, transformReplyNumber } from './generic-transformers'; +import { RedisCommandArguments } from '.'; +import { pushVerdictArguments } from './generic-transformers'; export const FIRST_KEY_INDEX = 1; -export function transformArguments(key: string | Array): TransformArgumentsReply { +export function transformArguments(key: string | Array): RedisCommandArguments { return pushVerdictArguments(['PFCOUNT'], key); } -export const transformReply = transformReplyNumber; +export declare function transformReply(): number; diff --git a/lib/commands/PFMERGE.ts b/lib/commands/PFMERGE.ts index c4ba11877f7..e934062b3fe 100644 --- a/lib/commands/PFMERGE.ts +++ b/lib/commands/PFMERGE.ts @@ -1,10 +1,10 @@ -import { TransformArgumentsReply } from '.'; -import { pushVerdictArguments, transformReplyString } from './generic-transformers'; +import { RedisCommandArguments } from '.'; +import { pushVerdictArguments } from './generic-transformers'; export const FIRST_KEY_INDEX = 1; -export function transformArguments(destination: string, source: string | Array): TransformArgumentsReply { +export function transformArguments(destination: string, source: string | Array): RedisCommandArguments { return pushVerdictArguments(['PFMERGE', destination], source); } -export const transformReply = transformReplyString; +export declare function transformReply(): string; diff --git a/lib/commands/PING.spec.ts b/lib/commands/PING.spec.ts index 87d9359a36d..43b683f192d 100644 --- a/lib/commands/PING.spec.ts +++ b/lib/commands/PING.spec.ts @@ -1,5 +1,5 @@ import { strict as assert } from 'assert'; -import { TestRedisServers, itWithClient, itWithCluster, TestRedisClusters } from '../test-utils'; +import { TestRedisServers, itWithClient } from '../test-utils'; describe('PING', () => { itWithClient(TestRedisServers.OPEN, 'client.ping', async client => { @@ -8,11 +8,4 @@ describe('PING', () => { 'PONG' ); }); - - itWithCluster(TestRedisClusters.OPEN, 'cluster.ping', async cluster => { - assert.equal( - await cluster.ping(), - 'PONG' - ); - }); }); diff --git a/lib/commands/PING.ts b/lib/commands/PING.ts index 36e92a08cfe..1e9aa957bf3 100644 --- a/lib/commands/PING.ts +++ b/lib/commands/PING.ts @@ -1,7 +1,5 @@ -import { transformReplyString } from './generic-transformers'; - export function transformArguments(): Array { return ['PING']; } -export const transformReply = transformReplyString; +export declare function transformReply(): string; diff --git a/lib/commands/PSETEX.ts b/lib/commands/PSETEX.ts index 101030d2e63..a0bd4f5c229 100644 --- a/lib/commands/PSETEX.ts +++ b/lib/commands/PSETEX.ts @@ -1,5 +1,3 @@ -import { transformReplyString } from './generic-transformers'; - export const FIRST_KEY_INDEX = 1; export function transformArguments(key: string, milliseconds: number, value: string): Array { @@ -11,4 +9,4 @@ export function transformArguments(key: string, milliseconds: number, value: str ]; } -export const transformReply = transformReplyString; +export declare function transformReply(): string; diff --git a/lib/commands/PTTL.ts b/lib/commands/PTTL.ts index 8356c75bbd9..c1bc18323d5 100644 --- a/lib/commands/PTTL.ts +++ b/lib/commands/PTTL.ts @@ -1,5 +1,3 @@ -import { transformReplyNumber } from './generic-transformers'; - export const FIRST_KEY_INDEX = 1; export const IS_READ_ONLY = true; @@ -8,4 +6,4 @@ export function transformArguments(key: string): Array { return ['PTTL', key]; } -export const transformReply = transformReplyNumber; +export declare function transformReply(): number; diff --git a/lib/commands/PUBLISH.ts b/lib/commands/PUBLISH.ts index 51387a6803f..eda5234df20 100644 --- a/lib/commands/PUBLISH.ts +++ b/lib/commands/PUBLISH.ts @@ -1,7 +1,5 @@ -import { transformReplyNumber } from './generic-transformers'; - export function transformArguments(channel: string, message: string): Array { return ['PUBLISH', channel, message]; } -export const transformReply = transformReplyNumber; +export declare function transformReply(): number; diff --git a/lib/commands/PUBSUB_CHANNELS.spec.ts b/lib/commands/PUBSUB_CHANNELS.spec.ts index 5ff9db60df8..9e148bc7fda 100644 --- a/lib/commands/PUBSUB_CHANNELS.spec.ts +++ b/lib/commands/PUBSUB_CHANNELS.spec.ts @@ -1,5 +1,5 @@ import { strict as assert } from 'assert'; -import { TestRedisServers, itWithClient, TestRedisClusters, itWithCluster } from '../test-utils'; +import { TestRedisServers, itWithClient } from '../test-utils'; import { transformArguments } from './PUBSUB_CHANNELS'; describe('PUBSUB CHANNELS', () => { @@ -25,11 +25,4 @@ describe('PUBSUB CHANNELS', () => { [] ); }); - - itWithCluster(TestRedisClusters.OPEN, 'cluster.pubSubChannels', async cluster => { - assert.deepEqual( - await cluster.pubSubChannels(), - [] - ); - }); }); diff --git a/lib/commands/PUBSUB_CHANNELS.ts b/lib/commands/PUBSUB_CHANNELS.ts index aa7a0749fc6..86a144ede8e 100644 --- a/lib/commands/PUBSUB_CHANNELS.ts +++ b/lib/commands/PUBSUB_CHANNELS.ts @@ -1,5 +1,3 @@ -import { transformReplyStringArray } from './generic-transformers'; - export const IS_READ_ONLY = true; export function transformArguments(pattern?: string): Array { @@ -12,4 +10,4 @@ export function transformArguments(pattern?: string): Array { return args; } -export const transformReply = transformReplyStringArray; +export declare function transformReply(): Array; diff --git a/lib/commands/PUBSUB_NUMPAT.spec.ts b/lib/commands/PUBSUB_NUMPAT.spec.ts index 49a39eedae0..55eef6a97de 100644 --- a/lib/commands/PUBSUB_NUMPAT.spec.ts +++ b/lib/commands/PUBSUB_NUMPAT.spec.ts @@ -1,5 +1,5 @@ import { strict as assert } from 'assert'; -import { TestRedisServers, itWithClient, TestRedisClusters, itWithCluster } from '../test-utils'; +import { TestRedisServers, itWithClient } from '../test-utils'; import { transformArguments } from './PUBSUB_NUMPAT'; describe('PUBSUB NUMPAT', () => { @@ -16,11 +16,4 @@ describe('PUBSUB NUMPAT', () => { 0 ); }); - - itWithCluster(TestRedisClusters.OPEN, 'cluster.pubSubNumPat', async cluster => { - assert.equal( - await cluster.pubSubNumPat(), - 0 - ); - }); }); diff --git a/lib/commands/PUBSUB_NUMPAT.ts b/lib/commands/PUBSUB_NUMPAT.ts index 966a8d237c7..15be6aa1b18 100644 --- a/lib/commands/PUBSUB_NUMPAT.ts +++ b/lib/commands/PUBSUB_NUMPAT.ts @@ -1,9 +1,7 @@ -import { transformReplyString } from './generic-transformers'; - export const IS_READ_ONLY = true; export function transformArguments(): Array { return ['PUBSUB', 'NUMPAT']; } -export const transformReply = transformReplyString; +export declare function transformReply(): string; diff --git a/lib/commands/PUBSUB_NUMSUB.spec.ts b/lib/commands/PUBSUB_NUMSUB.spec.ts index 403732f8f9d..ef44faf2c0b 100644 --- a/lib/commands/PUBSUB_NUMSUB.spec.ts +++ b/lib/commands/PUBSUB_NUMSUB.spec.ts @@ -1,5 +1,5 @@ import { strict as assert } from 'assert'; -import { TestRedisServers, itWithClient, TestRedisClusters, itWithCluster } from '../test-utils'; +import { TestRedisServers, itWithClient } from '../test-utils'; import { transformArguments } from './PUBSUB_NUMSUB'; describe('PUBSUB NUMSUB', () => { @@ -32,11 +32,4 @@ describe('PUBSUB NUMSUB', () => { Object.create(null) ); }); - - itWithCluster(TestRedisClusters.OPEN, 'cluster.pubSubNumSub', async cluster => { - assert.deepEqual( - await cluster.pubSubNumSub(), - Object.create(null) - ); - }); }); diff --git a/lib/commands/READONLY.ts b/lib/commands/READONLY.ts index 00fbe4e4351..db7db881628 100644 --- a/lib/commands/READONLY.ts +++ b/lib/commands/READONLY.ts @@ -1,7 +1,5 @@ -import { transformReplyString } from './generic-transformers'; - export function transformArguments(): Array { return ['READONLY']; } -export const transformReply = transformReplyString; +export declare function transformReply(): string; diff --git a/lib/commands/READWRITE.ts b/lib/commands/READWRITE.ts index 16f95604407..60dc865e89e 100644 --- a/lib/commands/READWRITE.ts +++ b/lib/commands/READWRITE.ts @@ -1,7 +1,5 @@ -import { transformReplyString } from './generic-transformers'; - export function transformArguments(): Array { return ['READWRITE']; } -export const transformReply = transformReplyString; +export declare function transformReply(): string; diff --git a/lib/commands/RENAME.ts b/lib/commands/RENAME.ts index 0f9582677f1..f2affada60b 100644 --- a/lib/commands/RENAME.ts +++ b/lib/commands/RENAME.ts @@ -1,9 +1,7 @@ -import { transformReplyString } from './generic-transformers'; - export const FIRST_KEY_INDEX = 1; export function transformArguments(key: string, newKey: string): Array { return ['RENAME', key, newKey]; } -export const transformReply = transformReplyString; +export declare function transformReply(): string; diff --git a/lib/commands/REPLICAOF.ts b/lib/commands/REPLICAOF.ts index 0b56bd74dc6..bd452e0f371 100644 --- a/lib/commands/REPLICAOF.ts +++ b/lib/commands/REPLICAOF.ts @@ -1,7 +1,5 @@ -import { transformReplyString } from './generic-transformers'; - export function transformArguments(host: string, port: number): Array { return ['REPLICAOF', host, port.toString()]; } -export const transformReply = transformReplyString; +export declare function transformReply(): string; diff --git a/lib/commands/RESTORE-ASKING.ts b/lib/commands/RESTORE-ASKING.ts index 4d178cb1f0b..d53d8541cd7 100644 --- a/lib/commands/RESTORE-ASKING.ts +++ b/lib/commands/RESTORE-ASKING.ts @@ -1,7 +1,5 @@ -import { transformReplyString } from './generic-transformers'; - export function transformArguments(): Array { return ['RESTORE-ASKING']; } -export const transformReply = transformReplyString; +export declare function transformReply(): string; diff --git a/lib/commands/RPOP.ts b/lib/commands/RPOP.ts index daccbf5d42d..96735dea8b9 100644 --- a/lib/commands/RPOP.ts +++ b/lib/commands/RPOP.ts @@ -1,9 +1,7 @@ -import { transformReplyStringNull } from './generic-transformers'; - export const FIRST_KEY_INDEX = 1; export function transformArguments(key: string): Array { return ['RPOP', key]; } -export const transformReply = transformReplyStringNull; +export declare function transformReply(): string | null; diff --git a/lib/commands/RPOPLPUSH.ts b/lib/commands/RPOPLPUSH.ts index db388906d3e..23f1ff08766 100644 --- a/lib/commands/RPOPLPUSH.ts +++ b/lib/commands/RPOPLPUSH.ts @@ -1,9 +1,7 @@ -import { transformReplyNumberNull } from './generic-transformers'; - export const FIRST_KEY_INDEX = 1; export function transformArguments(source: string, destination: string): Array { return ['RPOPLPUSH', source, destination]; } -export const transformReply = transformReplyNumberNull; +export declare function transformReply(): number | null; diff --git a/lib/commands/RPOP_COUNT.ts b/lib/commands/RPOP_COUNT.ts index 205704274f7..f7f3463a3ee 100644 --- a/lib/commands/RPOP_COUNT.ts +++ b/lib/commands/RPOP_COUNT.ts @@ -1,9 +1,7 @@ -import { transformReplyStringArrayNull } from './generic-transformers'; - export const FIRST_KEY_INDEX = 1; export function transformArguments(key: string, count: number): Array { return ['RPOP', key, count.toString()]; } -export const transformReply = transformReplyStringArrayNull; +export declare function transformReply(): Array | null; diff --git a/lib/commands/RPUSH.ts b/lib/commands/RPUSH.ts index 665094f47a5..575177755cc 100644 --- a/lib/commands/RPUSH.ts +++ b/lib/commands/RPUSH.ts @@ -1,10 +1,10 @@ -import { TransformArgumentsReply } from '.'; -import { pushVerdictArguments, transformReplyNumber } from './generic-transformers'; +import { RedisCommandArguments } from '.'; +import { pushVerdictArguments } from './generic-transformers'; export const FIRST_KEY_INDEX = 1; -export function transformArguments(key: string, element: string | Array): TransformArgumentsReply { +export function transformArguments(key: string, element: string | Array): RedisCommandArguments { return pushVerdictArguments(['RPUSH', key], element); } -export const transformReply = transformReplyNumber; +export declare function transformReply(): number; diff --git a/lib/commands/RPUSHX.ts b/lib/commands/RPUSHX.ts index fe1f969f3f6..bacc60d404b 100644 --- a/lib/commands/RPUSHX.ts +++ b/lib/commands/RPUSHX.ts @@ -1,10 +1,10 @@ -import { TransformArgumentsReply } from '.'; -import { pushVerdictArguments, transformReplyNumber } from './generic-transformers'; +import { RedisCommandArguments } from '.'; +import { pushVerdictArguments } from './generic-transformers'; export const FIRST_KEY_INDEX = 1; -export function transformArguments(key: string, element: string | Array): TransformArgumentsReply { +export function transformArguments(key: string, element: string | Array): RedisCommandArguments { return pushVerdictArguments(['RPUSHX', key], element); } -export const transformReply = transformReplyNumber; +export declare function transformReply(): number; diff --git a/lib/commands/SADD.ts b/lib/commands/SADD.ts index a432ccfef59..05e5a6858f7 100644 --- a/lib/commands/SADD.ts +++ b/lib/commands/SADD.ts @@ -1,10 +1,10 @@ -import { TransformArgumentsReply } from '.'; -import { pushVerdictArguments, transformReplyNumber } from './generic-transformers'; +import { RedisCommandArguments } from '.'; +import { pushVerdictArguments } from './generic-transformers'; export const FIRST_KEY_INDEX = 1; -export function transformArguments(key: string, members: string | Array): TransformArgumentsReply { +export function transformArguments(key: string, members: string | Array): RedisCommandArguments { return pushVerdictArguments(['SADD', key], members); } -export const transformReply = transformReplyNumber; +export declare function transformReply(): number; diff --git a/lib/commands/SAVE.ts b/lib/commands/SAVE.ts index 38a397892f8..e88575f0a5c 100644 --- a/lib/commands/SAVE.ts +++ b/lib/commands/SAVE.ts @@ -1,7 +1,5 @@ -import { transformReplyString } from './generic-transformers'; - export function transformArguments(): Array { return ['SAVE']; } -export const transformReply = transformReplyString; +export declare function transformReply(): string; diff --git a/lib/commands/SCARD.ts b/lib/commands/SCARD.ts index 8a90bd3b029..0d3ce49b6b2 100644 --- a/lib/commands/SCARD.ts +++ b/lib/commands/SCARD.ts @@ -1,9 +1,7 @@ -import { transformReplyNumber } from './generic-transformers'; - export const FIRST_KEY_INDEX = 1; export function transformArguments(key: string): Array { return ['SCARD', key]; } -export const transformReply = transformReplyNumber; +export declare function transformReply(): number; diff --git a/lib/commands/SCRIPT_DEBUG.spec.ts b/lib/commands/SCRIPT_DEBUG.spec.ts index 9096605143a..9d2ad1af266 100644 --- a/lib/commands/SCRIPT_DEBUG.spec.ts +++ b/lib/commands/SCRIPT_DEBUG.spec.ts @@ -1,5 +1,5 @@ import { strict as assert } from 'assert'; -import { TestRedisServers, itWithClient, TestRedisClusters, itWithCluster } from '../test-utils'; +import { TestRedisServers, itWithClient } from '../test-utils'; import { transformArguments } from './SCRIPT_DEBUG'; describe('SCRIPT DEBUG', () => { @@ -16,11 +16,4 @@ describe('SCRIPT DEBUG', () => { 'OK' ); }); - - itWithCluster(TestRedisClusters.OPEN, 'cluster.scriptDebug', async cluster => { - assert.equal( - await cluster.scriptDebug('NO'), - 'OK' - ); - }); }); diff --git a/lib/commands/SCRIPT_DEBUG.ts b/lib/commands/SCRIPT_DEBUG.ts index e93443a5860..e9e1e909d59 100644 --- a/lib/commands/SCRIPT_DEBUG.ts +++ b/lib/commands/SCRIPT_DEBUG.ts @@ -1,7 +1,5 @@ -import { transformReplyString } from './generic-transformers'; - export function transformArguments(mode: 'YES' | 'SYNC' | 'NO'): Array { return ['SCRIPT', 'DEBUG', mode]; } -export const transformReply = transformReplyString; +export declare function transformReply(): string; diff --git a/lib/commands/SCRIPT_EXISTS.spec.ts b/lib/commands/SCRIPT_EXISTS.spec.ts index d03521a5c60..b23380c7579 100644 --- a/lib/commands/SCRIPT_EXISTS.spec.ts +++ b/lib/commands/SCRIPT_EXISTS.spec.ts @@ -1,5 +1,5 @@ import { strict as assert } from 'assert'; -import { TestRedisServers, itWithClient, TestRedisClusters, itWithCluster } from '../test-utils'; +import { TestRedisServers, itWithClient } from '../test-utils'; import { transformArguments } from './SCRIPT_EXISTS'; describe('SCRIPT EXISTS', () => { @@ -25,11 +25,4 @@ describe('SCRIPT EXISTS', () => { [false] ); }); - - itWithCluster(TestRedisClusters.OPEN, 'cluster.scriptExists', async cluster => { - assert.deepEqual( - await cluster.scriptExists('sha1'), - [false] - ); - }); }); diff --git a/lib/commands/SCRIPT_EXISTS.ts b/lib/commands/SCRIPT_EXISTS.ts index 47a7f456e9b..ee89f955e50 100644 --- a/lib/commands/SCRIPT_EXISTS.ts +++ b/lib/commands/SCRIPT_EXISTS.ts @@ -1,7 +1,7 @@ -import { TransformArgumentsReply } from '.'; +import { RedisCommandArguments } from '.'; import { pushVerdictArguments, transformReplyBooleanArray } from './generic-transformers'; -export function transformArguments(sha1: string | Array): TransformArgumentsReply { +export function transformArguments(sha1: string | Array): RedisCommandArguments { return pushVerdictArguments(['SCRIPT', 'EXISTS'], sha1); } diff --git a/lib/commands/SCRIPT_FLUSH.spec.ts b/lib/commands/SCRIPT_FLUSH.spec.ts index c1321676ebe..c77accb50a9 100644 --- a/lib/commands/SCRIPT_FLUSH.spec.ts +++ b/lib/commands/SCRIPT_FLUSH.spec.ts @@ -1,5 +1,5 @@ import { strict as assert } from 'assert'; -import { TestRedisServers, itWithClient, TestRedisClusters, itWithCluster } from '../test-utils'; +import { TestRedisServers, itWithClient } from '../test-utils'; import { transformArguments } from './SCRIPT_FLUSH'; describe('SCRIPT FLUSH', () => { @@ -25,11 +25,4 @@ describe('SCRIPT FLUSH', () => { 'OK' ); }); - - itWithCluster(TestRedisClusters.OPEN, 'cluster.scriptFlush', async cluster => { - assert.equal( - await cluster.scriptFlush(), - 'OK' - ); - }); }); diff --git a/lib/commands/SCRIPT_FLUSH.ts b/lib/commands/SCRIPT_FLUSH.ts index 83bc9e2b5d8..2c220e9e3d1 100644 --- a/lib/commands/SCRIPT_FLUSH.ts +++ b/lib/commands/SCRIPT_FLUSH.ts @@ -1,5 +1,3 @@ -import { transformReplyString } from './generic-transformers'; - export function transformArguments(mode?: 'ASYNC' | 'SYNC'): Array { const args = ['SCRIPT', 'FLUSH']; @@ -10,4 +8,4 @@ export function transformArguments(mode?: 'ASYNC' | 'SYNC'): Array { return args; } -export const transformReply = transformReplyString; +export declare function transformReply(): string; diff --git a/lib/commands/SCRIPT_KILL.ts b/lib/commands/SCRIPT_KILL.ts index 5c175b74d6c..c0a53da8681 100644 --- a/lib/commands/SCRIPT_KILL.ts +++ b/lib/commands/SCRIPT_KILL.ts @@ -1,7 +1,5 @@ -import { transformReplyString } from './generic-transformers'; - export function transformArguments(): Array { return ['SCRIPT', 'KILL']; } -export const transformReply = transformReplyString; +export declare function transformReply(): string; diff --git a/lib/commands/SCRIPT_LOAD.spec.ts b/lib/commands/SCRIPT_LOAD.spec.ts index 46490f35c8a..1d7da3e9c2d 100644 --- a/lib/commands/SCRIPT_LOAD.spec.ts +++ b/lib/commands/SCRIPT_LOAD.spec.ts @@ -1,6 +1,6 @@ import { strict as assert } from 'assert'; import { scriptSha1 } from '../lua-script'; -import { TestRedisServers, itWithClient, TestRedisClusters, itWithCluster } from '../test-utils'; +import { TestRedisServers, itWithClient } from '../test-utils'; import { transformArguments } from './SCRIPT_LOAD'; describe('SCRIPT LOAD', () => { @@ -20,11 +20,4 @@ describe('SCRIPT LOAD', () => { SCRIPT_SHA1 ); }); - - itWithCluster(TestRedisClusters.OPEN, 'cluster.scriptLoad', async cluster => { - assert.equal( - await cluster.scriptLoad(SCRIPT), - SCRIPT_SHA1 - ); - }); }); diff --git a/lib/commands/SCRIPT_LOAD.ts b/lib/commands/SCRIPT_LOAD.ts index 378fbf1e76a..7cb28c1ec7f 100644 --- a/lib/commands/SCRIPT_LOAD.ts +++ b/lib/commands/SCRIPT_LOAD.ts @@ -1,7 +1,5 @@ -import { transformReplyString } from './generic-transformers'; - export function transformArguments(script: string): Array { return ['SCRIPT', 'LOAD', script]; } -export const transformReply = transformReplyString; +export declare function transformReply(): string; diff --git a/lib/commands/SDIFF.ts b/lib/commands/SDIFF.ts index 4d5aaea1a06..7c1e4fc34a9 100644 --- a/lib/commands/SDIFF.ts +++ b/lib/commands/SDIFF.ts @@ -1,10 +1,10 @@ -import { TransformArgumentsReply } from '.'; -import { pushVerdictArguments, transformReplyStringArray } from './generic-transformers'; +import { RedisCommandArguments } from '.'; +import { pushVerdictArguments } from './generic-transformers'; export const FIRST_KEY_INDEX = 1; -export function transformArguments(keys: string | Array): TransformArgumentsReply { +export function transformArguments(keys: string | Array): RedisCommandArguments { return pushVerdictArguments(['SDIFF'], keys); } -export const transformReply = transformReplyStringArray; +export declare function transformReply(): Array; diff --git a/lib/commands/SDIFFSTORE.ts b/lib/commands/SDIFFSTORE.ts index 69883d4124c..9cca24beb61 100644 --- a/lib/commands/SDIFFSTORE.ts +++ b/lib/commands/SDIFFSTORE.ts @@ -1,10 +1,10 @@ -import { TransformArgumentsReply } from '.'; -import { pushVerdictArguments, transformReplyNumber } from './generic-transformers'; +import { RedisCommandArguments } from '.'; +import { pushVerdictArguments } from './generic-transformers'; export const FIRST_KEY_INDEX = 1; -export function transformArguments(destination: string, keys: string | Array): TransformArgumentsReply { +export function transformArguments(destination: string, keys: string | Array): RedisCommandArguments { return pushVerdictArguments(['SDIFFSTORE', destination], keys); } -export const transformReply = transformReplyNumber; +export declare function transformReply(): number; diff --git a/lib/commands/SET.ts b/lib/commands/SET.ts index 03853b3f7d6..b19a1b2c5c2 100644 --- a/lib/commands/SET.ts +++ b/lib/commands/SET.ts @@ -1,4 +1,4 @@ -import { TransformArgumentsReply } from '.'; +import { RedisCommandArguments } from '.'; export const FIRST_KEY_INDEX = 1; @@ -40,7 +40,7 @@ interface SetCommonOptions { type SetOptions = SetTTL & SetGuards & (SetCommonOptions | {}); -export function transformArguments(key: string | Buffer, value: string | Buffer, options?: SetOptions): TransformArgumentsReply { +export function transformArguments(key: string | Buffer, value: string | Buffer, options?: SetOptions): RedisCommandArguments { const args = ['SET', key, value]; if (!options) { diff --git a/lib/commands/SETBIT.ts b/lib/commands/SETBIT.ts index 33b2ff1a838..7b0812a55ba 100644 --- a/lib/commands/SETBIT.ts +++ b/lib/commands/SETBIT.ts @@ -1,10 +1,10 @@ -import { TransformArgumentsReply } from '.'; -import { BitValue, transformReplyBit } from './generic-transformers'; +import { RedisCommandArguments } from '.'; +import { BitValue } from './generic-transformers'; export const FIRST_KEY_INDEX = 1; -export function transformArguments(key: string, offset: number, value: BitValue): TransformArgumentsReply { +export function transformArguments(key: string, offset: number, value: BitValue): RedisCommandArguments { return ['SETBIT', key, offset.toString(), value.toString()]; } -export const transformReply = transformReplyBit; +export declare function transformReply(): BitValue; diff --git a/lib/commands/SETEX.ts b/lib/commands/SETEX.ts index 320278c9264..89c6e06a3f2 100644 --- a/lib/commands/SETEX.ts +++ b/lib/commands/SETEX.ts @@ -1,9 +1,8 @@ -import { TransformArgumentsReply } from '.'; -import { transformReplyString } from './generic-transformers'; +import { RedisCommandArguments } from '.'; export const FIRST_KEY_INDEX = 1; -export function transformArguments(key: string | Buffer, seconds: number, value: string): TransformArgumentsReply { +export function transformArguments(key: string | Buffer, seconds: number, value: string): RedisCommandArguments { return [ 'SETEX', key, @@ -12,4 +11,4 @@ export function transformArguments(key: string | Buffer, seconds: number, value: ]; } -export const transformReply = transformReplyString; +export declare function transformReply(): string; diff --git a/lib/commands/SETRANGE.ts b/lib/commands/SETRANGE.ts index a303487ddd4..fa15c41db9d 100644 --- a/lib/commands/SETRANGE.ts +++ b/lib/commands/SETRANGE.ts @@ -1,9 +1,7 @@ -import { transformReplyNumber } from './generic-transformers'; - export const FIRST_KEY_INDEX = 1; export function transformArguments(key: string, offset: number, value: string): Array { return ['SETRANGE', key, offset.toString(), value]; } -export const transformReply = transformReplyNumber; +export declare function transformReply(): number; diff --git a/lib/commands/SHUTDOWN.ts b/lib/commands/SHUTDOWN.ts index 0dd2cf3a5b3..1990d05a2ed 100644 --- a/lib/commands/SHUTDOWN.ts +++ b/lib/commands/SHUTDOWN.ts @@ -1,5 +1,3 @@ -import { transformReplyVoid } from './generic-transformers'; - export function transformArguments(mode?: 'NOSAVE' | 'SAVE'): Array { const args = ['SHUTDOWN']; @@ -10,4 +8,4 @@ export function transformArguments(mode?: 'NOSAVE' | 'SAVE'): Array { return args; } -export const transformReply = transformReplyVoid; +export declare function transformReply(): void; diff --git a/lib/commands/SINTER.ts b/lib/commands/SINTER.ts index 43869652370..5d74e761f0e 100644 --- a/lib/commands/SINTER.ts +++ b/lib/commands/SINTER.ts @@ -1,10 +1,10 @@ -import { TransformArgumentsReply } from '.'; -import { pushVerdictArguments, transformReplyStringArray } from './generic-transformers'; +import { RedisCommandArguments } from '.'; +import { pushVerdictArguments } from './generic-transformers'; export const FIRST_KEY_INDEX = 1; -export function transformArguments(keys: string | Array): TransformArgumentsReply { +export function transformArguments(keys: string | Array): RedisCommandArguments { return pushVerdictArguments(['SINTER'], keys); } -export const transformReply = transformReplyStringArray; +export declare function transformReply(): Array; diff --git a/lib/commands/SINTERSTORE.ts b/lib/commands/SINTERSTORE.ts index 5ad1b11cbac..40f31a8b7a3 100644 --- a/lib/commands/SINTERSTORE.ts +++ b/lib/commands/SINTERSTORE.ts @@ -1,10 +1,10 @@ -import { TransformArgumentsReply } from '.'; -import { pushVerdictArguments, transformReplyStringArray } from './generic-transformers'; +import { RedisCommandArguments } from '.'; +import { pushVerdictArguments } from './generic-transformers'; export const FIRST_KEY_INDEX = 1; -export function transformArguments(destination: string, keys: string | Array): TransformArgumentsReply { +export function transformArguments(destination: string, keys: string | Array): RedisCommandArguments { return pushVerdictArguments(['SINTERSTORE', destination], keys); } -export const transformReply = transformReplyStringArray; +export declare function transformReply(): Array; diff --git a/lib/commands/SMEMBERS.ts b/lib/commands/SMEMBERS.ts index d7e75daaa3e..71b479f9d8e 100644 --- a/lib/commands/SMEMBERS.ts +++ b/lib/commands/SMEMBERS.ts @@ -1,9 +1,7 @@ -import { transformReplyStringArray } from './generic-transformers'; - export const FIRST_KEY_INDEX = 1; export function transformArguments(key: string): Array { return ['SMEMBERS', key]; } -export const transformReply = transformReplyStringArray; +export declare function transformReply(): Array; diff --git a/lib/commands/SPOP.ts b/lib/commands/SPOP.ts index a389fed5cc5..84845230d41 100644 --- a/lib/commands/SPOP.ts +++ b/lib/commands/SPOP.ts @@ -1,5 +1,3 @@ -import { transformReplyStringArray } from './generic-transformers'; - export const FIRST_KEY_INDEX = 1; export function transformArguments(key: string, count?: number): Array { @@ -12,4 +10,4 @@ export function transformArguments(key: string, count?: number): Array { return args; } -export const transformReply = transformReplyStringArray; +export declare function transformReply(): Array; diff --git a/lib/commands/SRANDMEMBER.ts b/lib/commands/SRANDMEMBER.ts index 2e8cd539273..c477a5691d7 100644 --- a/lib/commands/SRANDMEMBER.ts +++ b/lib/commands/SRANDMEMBER.ts @@ -1,9 +1,7 @@ -import { transformReplyStringNull } from './generic-transformers'; - export const FIRST_KEY_INDEX = 1; export function transformArguments(key: string): Array { return ['SRANDMEMBER', key]; } -export const transformReply = transformReplyStringNull; +export declare function transformReply(): string | null; diff --git a/lib/commands/SRANDMEMBER_COUNT.ts b/lib/commands/SRANDMEMBER_COUNT.ts index b7fa8ebeb3a..89d9b8c4aef 100644 --- a/lib/commands/SRANDMEMBER_COUNT.ts +++ b/lib/commands/SRANDMEMBER_COUNT.ts @@ -1,4 +1,3 @@ -import { transformReplyStringArray } from './generic-transformers'; import { transformArguments as transformSRandMemberArguments } from './SRANDMEMBER'; export { FIRST_KEY_INDEX } from './SRANDMEMBER'; @@ -10,4 +9,4 @@ export function transformArguments(key: string, count: number): Array { ]; } -export const transformReply = transformReplyStringArray; +export declare function transformReply(): Array; diff --git a/lib/commands/SREM.ts b/lib/commands/SREM.ts index 4ae33245d29..9a37ac9bf99 100644 --- a/lib/commands/SREM.ts +++ b/lib/commands/SREM.ts @@ -1,10 +1,10 @@ -import { TransformArgumentsReply } from '.'; -import { pushVerdictArguments, transformReplyNumber } from './generic-transformers'; +import { RedisCommandArguments } from '.'; +import { pushVerdictArguments } from './generic-transformers'; export const FIRST_KEY_INDEX = 1; -export function transformArguments(key: string, members: string | Array): TransformArgumentsReply { +export function transformArguments(key: string, members: string | Array): RedisCommandArguments { return pushVerdictArguments(['SREM', key], members); } -export const transformReply = transformReplyNumber; +export declare function transformReply(): number; diff --git a/lib/commands/STRLEN.ts b/lib/commands/STRLEN.ts index d8112ce7d1f..208d9d73fb8 100644 --- a/lib/commands/STRLEN.ts +++ b/lib/commands/STRLEN.ts @@ -1,5 +1,3 @@ -import { transformReplyNumber } from './generic-transformers'; - export const FIRST_KEY_INDEX = 1; export const IS_READ_ONLY = true; @@ -8,4 +6,4 @@ export function transformArguments(key: string): Array { return ['STRLEN', key]; } -export const transformReply = transformReplyNumber; +export declare function transformReply(): number; diff --git a/lib/commands/SUNION.ts b/lib/commands/SUNION.ts index 705bff29927..ae8b02b481d 100644 --- a/lib/commands/SUNION.ts +++ b/lib/commands/SUNION.ts @@ -1,12 +1,12 @@ -import { TransformArgumentsReply } from '.'; -import { pushVerdictArguments, transformReplyStringArray } from './generic-transformers'; +import { RedisCommandArguments } from '.'; +import { pushVerdictArguments } from './generic-transformers'; export const FIRST_KEY_INDEX = 1; export const IS_READ_ONLY = true; -export function transformArguments(keys: string | Array): TransformArgumentsReply { +export function transformArguments(keys: string | Array): RedisCommandArguments { return pushVerdictArguments(['SUNION'], keys); } -export const transformReply = transformReplyStringArray; +export declare function transformReply(): Array; diff --git a/lib/commands/SUNIONSTORE.ts b/lib/commands/SUNIONSTORE.ts index af717f627df..f259769f49a 100644 --- a/lib/commands/SUNIONSTORE.ts +++ b/lib/commands/SUNIONSTORE.ts @@ -1,10 +1,10 @@ -import { TransformArgumentsReply } from '.'; -import { pushVerdictArguments, transformReplyNumber } from './generic-transformers'; +import { RedisCommandArguments } from '.'; +import { pushVerdictArguments } from './generic-transformers'; export const FIRST_KEY_INDEX = 1; -export function transformArguments(destination: string, keys: string | Array): TransformArgumentsReply { +export function transformArguments(destination: string, keys: string | Array): RedisCommandArguments { return pushVerdictArguments(['SUNIONSTORE', destination], keys); } -export const transformReply = transformReplyNumber; +export declare function transformReply(): number; diff --git a/lib/commands/SWAPDB.ts b/lib/commands/SWAPDB.ts index f0d4dacf3fc..7f13d6b008e 100644 --- a/lib/commands/SWAPDB.ts +++ b/lib/commands/SWAPDB.ts @@ -1,7 +1,5 @@ -import { transformReplyString } from './generic-transformers'; - export function transformArguments(index1: number, index2: number): Array { return ['SWAPDB', index1.toString(), index2.toString()]; } -export const transformReply = transformReplyString; +export declare function transformReply(): string; diff --git a/lib/commands/TOUCH.ts b/lib/commands/TOUCH.ts index abff4160392..a3dc31e8568 100644 --- a/lib/commands/TOUCH.ts +++ b/lib/commands/TOUCH.ts @@ -1,10 +1,10 @@ -import { TransformArgumentsReply } from '.'; -import { pushVerdictArguments, transformReplyNumber } from './generic-transformers'; +import { RedisCommandArguments } from '.'; +import { pushVerdictArguments } from './generic-transformers'; export const FIRST_KEY_INDEX = 1; -export function transformArguments(key: string | Array): TransformArgumentsReply { +export function transformArguments(key: string | Array): RedisCommandArguments { return pushVerdictArguments(['TOUCH'], key); } -export const transformReply = transformReplyNumber; +export declare function transformReply(): number; diff --git a/lib/commands/TTL.ts b/lib/commands/TTL.ts index aa8462dfea3..4ae31245aa0 100644 --- a/lib/commands/TTL.ts +++ b/lib/commands/TTL.ts @@ -1,5 +1,3 @@ -import { transformReplyNumber } from './generic-transformers'; - export const FIRST_KEY_INDEX = 1; export const IS_READ_ONLY = true; @@ -8,4 +6,4 @@ export function transformArguments(key: string): Array { return ['TTL', key]; } -export const transformReply = transformReplyNumber; +export declare function transformReply(): number; diff --git a/lib/commands/TYPE.ts b/lib/commands/TYPE.ts index 4f27b29d2b6..283701b6e9f 100644 --- a/lib/commands/TYPE.ts +++ b/lib/commands/TYPE.ts @@ -1,5 +1,3 @@ -import { transformReplyString } from './generic-transformers'; - export const FIRST_KEY_INDEX = 1; export const IS_READ_ONLY = true; @@ -8,4 +6,4 @@ export function transformArguments(key: string): Array { return ['TYPE', key]; } -export const transformReply = transformReplyString; +export declare function transformReply(): string; diff --git a/lib/commands/UNLINK.ts b/lib/commands/UNLINK.ts index 4647a976e42..467b4172e0f 100644 --- a/lib/commands/UNLINK.ts +++ b/lib/commands/UNLINK.ts @@ -1,10 +1,10 @@ -import { TransformArgumentsReply } from '.'; -import { pushVerdictArguments, transformReplyNumber } from './generic-transformers'; +import { RedisCommandArguments } from '.'; +import { pushVerdictArguments } from './generic-transformers'; export const FIRST_KEY_INDEX = 1; -export function transformArguments(key: string | Array): TransformArgumentsReply { +export function transformArguments(key: string | Array): RedisCommandArguments { return pushVerdictArguments(['UNLINK'], key); } -export const transformReply = transformReplyNumber; +export declare function transformReply(): number; diff --git a/lib/commands/UNWATCH.spec.ts b/lib/commands/UNWATCH.spec.ts index 238ffdc59b3..07059310cbc 100644 --- a/lib/commands/UNWATCH.spec.ts +++ b/lib/commands/UNWATCH.spec.ts @@ -1,5 +1,5 @@ import { strict as assert } from 'assert'; -import { TestRedisServers, itWithClient, TestRedisClusters, itWithCluster } from '../test-utils'; +import { TestRedisServers, itWithClient } from '../test-utils'; import { transformArguments } from './UNWATCH'; describe('UNWATCH', () => { @@ -16,11 +16,4 @@ describe('UNWATCH', () => { 'OK' ); }); - - itWithCluster(TestRedisClusters.OPEN, 'cluster.unwatch', async cluster => { - assert.equal( - await cluster.unwatch(), - 'OK' - ); - }); }); diff --git a/lib/commands/UNWATCH.ts b/lib/commands/UNWATCH.ts index d0ede556f3a..ce42e7697bf 100644 --- a/lib/commands/UNWATCH.ts +++ b/lib/commands/UNWATCH.ts @@ -1,7 +1,5 @@ -import { transformReplyString } from './generic-transformers'; - export function transformArguments(): Array { return ['UNWATCH']; } -export const transformReply = transformReplyString; +export declare function transformReply(): string; diff --git a/lib/commands/WAIT.ts b/lib/commands/WAIT.ts index 214fb356688..dff51ed9680 100644 --- a/lib/commands/WAIT.ts +++ b/lib/commands/WAIT.ts @@ -1,9 +1,7 @@ -import { transformReplyNumber } from './generic-transformers'; - export const FIRST_KEY_INDEX = 1; export function transformArguments(numberOfReplicas: number, timeout: number): Array { return ['WAIT', numberOfReplicas.toString(), timeout.toString()]; } -export const transformReply = transformReplyNumber; +export declare function transformReply(): number; diff --git a/lib/commands/WATCH.ts b/lib/commands/WATCH.ts index e644ab0f462..f66429b507e 100644 --- a/lib/commands/WATCH.ts +++ b/lib/commands/WATCH.ts @@ -1,8 +1,8 @@ -import { TransformArgumentsReply } from '.'; -import { pushVerdictArguments, transformReplyString } from './generic-transformers'; +import { RedisCommandArguments } from '.'; +import { pushVerdictArguments } from './generic-transformers'; -export function transformArguments(key: string | Array): TransformArgumentsReply { +export function transformArguments(key: string | Array): RedisCommandArguments { return pushVerdictArguments(['WATCH'], key); } -export const transformReply = transformReplyString; +export declare function transformReply(): string; diff --git a/lib/commands/XACK.ts b/lib/commands/XACK.ts index a6de28151eb..0d21a0ec085 100644 --- a/lib/commands/XACK.ts +++ b/lib/commands/XACK.ts @@ -1,10 +1,10 @@ -import { TransformArgumentsReply } from '.'; -import { pushVerdictArguments, transformReplyNumber } from './generic-transformers'; +import { RedisCommandArguments } from '.'; +import { pushVerdictArguments } from './generic-transformers'; export const FIRST_KEY_INDEX = 1; -export function transformArguments(key: string, group: string, id: string | Array): TransformArgumentsReply { +export function transformArguments(key: string, group: string, id: string | Array): RedisCommandArguments { return pushVerdictArguments(['XACK', key, group], id); } -export const transformReply = transformReplyNumber; +export declare function transformReply(): number; diff --git a/lib/commands/XADD.ts b/lib/commands/XADD.ts index 0500a2fde65..7bc263b2400 100644 --- a/lib/commands/XADD.ts +++ b/lib/commands/XADD.ts @@ -1,8 +1,7 @@ -import { TuplesObject, transformReplyString } from './generic-transformers'; +import { TuplesObject } from './generic-transformers'; export const FIRST_KEY_INDEX = 1; - interface XAddOptions { NOMKSTREAM?: true; TRIM?: { @@ -45,4 +44,4 @@ export function transformArguments(key: string, id: string, message: TuplesObjec return args; } -export const transformReply = transformReplyString; +export declare function transformReply(): string; diff --git a/lib/commands/XCLAIM_JUSTID.ts b/lib/commands/XCLAIM_JUSTID.ts index dcf274ed821..ab74c89420c 100644 --- a/lib/commands/XCLAIM_JUSTID.ts +++ b/lib/commands/XCLAIM_JUSTID.ts @@ -1,4 +1,3 @@ -import { transformReplyStringArray } from './generic-transformers'; import { transformArguments as transformArgumentsXClaim } from './XCLAIM'; export { FIRST_KEY_INDEX } from './XCLAIM'; @@ -10,4 +9,4 @@ export function transformArguments(...args: Parameters; diff --git a/lib/commands/XDEL.ts b/lib/commands/XDEL.ts index 083ea77ef0f..892b9a0de5b 100644 --- a/lib/commands/XDEL.ts +++ b/lib/commands/XDEL.ts @@ -1,10 +1,10 @@ -import { TransformArgumentsReply } from '.'; -import { pushVerdictArguments, transformReplyNumber } from './generic-transformers'; +import { RedisCommandArguments } from '.'; +import { pushVerdictArguments } from './generic-transformers'; export const FIRST_KEY_INDEX = 1; -export function transformArguments(key: string, id: string | Array): TransformArgumentsReply { +export function transformArguments(key: string, id: string | Array): RedisCommandArguments { return pushVerdictArguments(['XDEL', key], id); } -export const transformReply = transformReplyNumber; +export declare function transformReply(): number; diff --git a/lib/commands/XGROUP_CREATE.ts b/lib/commands/XGROUP_CREATE.ts index 167197b263c..c2d7f157615 100644 --- a/lib/commands/XGROUP_CREATE.ts +++ b/lib/commands/XGROUP_CREATE.ts @@ -1,5 +1,3 @@ -import { transformReplyString } from './generic-transformers'; - export const FIRST_KEY_INDEX = 2; interface XGroupCreateOptions { @@ -16,4 +14,4 @@ export function transformArguments(key: string, group: string, id: string, optio return args; } -export const transformReply = transformReplyString; +export declare function transformReply(): string; diff --git a/lib/commands/XGROUP_DELCONSUMER.ts b/lib/commands/XGROUP_DELCONSUMER.ts index 91a36f91a30..dc9de7bb577 100644 --- a/lib/commands/XGROUP_DELCONSUMER.ts +++ b/lib/commands/XGROUP_DELCONSUMER.ts @@ -1,9 +1,7 @@ -import { transformReplyNumber } from './generic-transformers'; - export const FIRST_KEY_INDEX = 2; export function transformArguments(key: string, group: string, consumer: string): Array { return ['XGROUP', 'DELCONSUMER', key, group, consumer]; } -export const transformReply = transformReplyNumber; +export declare function transformReply(): number; diff --git a/lib/commands/XGROUP_SETID.ts b/lib/commands/XGROUP_SETID.ts index ce4a13086d3..2d4e30d9c94 100644 --- a/lib/commands/XGROUP_SETID.ts +++ b/lib/commands/XGROUP_SETID.ts @@ -1,9 +1,7 @@ -import { transformReplyString } from './generic-transformers'; - export const FIRST_KEY_INDEX = 2; export function transformArguments(key: string, group: string, id: string): Array { return ['XGROUP', 'SETID', key, group, id]; } -export const transformReply = transformReplyString; +export declare function transformReply(): string; diff --git a/lib/commands/XLEN.ts b/lib/commands/XLEN.ts index d7ba033e612..1cadd61952b 100644 --- a/lib/commands/XLEN.ts +++ b/lib/commands/XLEN.ts @@ -1,5 +1,3 @@ -import { transformReplyNumber } from './generic-transformers'; - export const FIRST_KEY_INDEX = 1; export const IS_READ_ONLY = true; @@ -8,4 +6,4 @@ export function transformArguments(key: string): Array { return ['XLEN', key]; } -export const transformReply = transformReplyNumber; +export declare function transformReply(): number; diff --git a/lib/commands/XREAD.spec.ts b/lib/commands/XREAD.spec.ts index e7bf127a90a..501571bfbeb 100644 --- a/lib/commands/XREAD.spec.ts +++ b/lib/commands/XREAD.spec.ts @@ -1,8 +1,24 @@ import { strict as assert } from 'assert'; import { TestRedisServers, itWithClient, itWithCluster, TestRedisClusters } from '../test-utils'; -import { transformArguments } from './XREAD'; +import { FIRST_KEY_INDEX, transformArguments } from './XREAD'; describe('XREAD', () => { + describe('FIRST_KEY_INDEX', () => { + it('single stream', () => { + assert.equal( + FIRST_KEY_INDEX({ key: 'key', id: '' }), + 'key' + ); + }); + + it('multiple streams', () => { + assert.equal( + FIRST_KEY_INDEX([{ key: '1', id: '' }, { key: '2', id: '' }]), + '1' + ); + }); + }); + describe('transformArguments', () => { it('single stream', () => { assert.deepEqual( @@ -13,13 +29,13 @@ describe('XREAD', () => { ['XREAD', 'STREAMS', 'key', '0'] ); }); - + it('multiple streams', () => { assert.deepEqual( transformArguments([{ key: '1', id: '0' - }, { + }, { key: '2', id: '0' }]), diff --git a/lib/commands/XREADGROUP.spec.ts b/lib/commands/XREADGROUP.spec.ts index 8f7693c333b..8cb3147bfe7 100644 --- a/lib/commands/XREADGROUP.spec.ts +++ b/lib/commands/XREADGROUP.spec.ts @@ -1,8 +1,24 @@ import { strict as assert } from 'assert'; import { TestRedisServers, itWithClient, itWithCluster, TestRedisClusters } from '../test-utils'; -import { transformArguments } from './XREADGROUP'; +import { FIRST_KEY_INDEX, transformArguments } from './XREADGROUP'; describe('XREADGROUP', () => { + describe('FIRST_KEY_INDEX', () => { + it('single stream', () => { + assert.equal( + FIRST_KEY_INDEX('', '', { key: 'key', id: '' }), + 'key' + ); + }); + + it('multiple streams', () => { + assert.equal( + FIRST_KEY_INDEX('', '', [{ key: '1', id: '' }, { key: '2', id: '' }]), + '1' + ); + }); + }); + describe('transformArguments', () => { it('single stream', () => { assert.deepEqual( @@ -78,13 +94,27 @@ describe('XREADGROUP', () => { }); }); - describe('client.xReadGroup', () => { - itWithClient(TestRedisServers.OPEN, 'null', async client => { + itWithClient(TestRedisServers.OPEN, 'null', async client => { + const [, readGroupReply] = await Promise.all([ + client.xGroupCreate('key', 'group', '$', { + MKSTREAM: true + }), + client.xReadGroup('group', 'consumer', { + key: 'key', + id: '>' + }) + ]); + + assert.equal(readGroupReply, null); + }); + + describe('cluster.xReadGroup', () => { + itWithCluster(TestRedisClusters.OPEN, 'null', async cluster => { const [, readGroupReply] = await Promise.all([ - client.xGroupCreate('key', 'group', '$', { + cluster.xGroupCreate('key', 'group', '$', { MKSTREAM: true }), - client.xReadGroup('group', 'consumer', { + cluster.xReadGroup('group', 'consumer', { key: 'key', id: '>' }) @@ -93,7 +123,7 @@ describe('XREADGROUP', () => { assert.equal(readGroupReply, null); }); - itWithClient(TestRedisServers.OPEN, 'with a message', async client => { + itWithCluster(TestRedisClusters.OPEN, 'with a message', async client => { const [, id, readGroupReply] = await Promise.all([ client.xGroupCreate('key', 'group', '$', { MKSTREAM: true @@ -120,18 +150,4 @@ describe('XREADGROUP', () => { }]); }); }); - - itWithCluster(TestRedisClusters.OPEN, 'cluster.xReadGroup', async cluster => { - const [, readGroupReply] = await Promise.all([ - cluster.xGroupCreate('key', 'group', '$', { - MKSTREAM: true - }), - cluster.xReadGroup('group', 'consumer', { - key: 'key', - id: '>' - }) - ]); - - assert.equal(readGroupReply, null); - }); }); diff --git a/lib/commands/XTRIM.ts b/lib/commands/XTRIM.ts index 8175ba70df3..520c38847b8 100644 --- a/lib/commands/XTRIM.ts +++ b/lib/commands/XTRIM.ts @@ -1,5 +1,3 @@ -import { transformReplyNumber } from './generic-transformers'; - export const FIRST_KEY_INDEX = 1; interface XTrimOptions { @@ -23,4 +21,4 @@ export function transformArguments(key: string, strategy: 'MAXLEN' | 'MINID', th return args; } -export const transformReply = transformReplyNumber; +export declare function transformReply(): number; diff --git a/lib/commands/ZCARD.ts b/lib/commands/ZCARD.ts index f6e4ea5f6cd..9c76c485bf9 100644 --- a/lib/commands/ZCARD.ts +++ b/lib/commands/ZCARD.ts @@ -1,5 +1,3 @@ -import { transformReplyNumber } from './generic-transformers'; - export const FIRST_KEY_INDEX = 1; export const IS_READ_ONLY = true; @@ -8,4 +6,4 @@ export function transformArguments(key: string): Array { return ['ZCARD', key]; } -export const transformReply = transformReplyNumber; +export declare function transformReply(): number; diff --git a/lib/commands/ZCOUNT.ts b/lib/commands/ZCOUNT.ts index fd73c384489..a18ba0fd790 100644 --- a/lib/commands/ZCOUNT.ts +++ b/lib/commands/ZCOUNT.ts @@ -1,4 +1,4 @@ -import { transformArgumentNumberInfinity, transformReplyNumber } from './generic-transformers'; +import { transformArgumentNumberInfinity } from './generic-transformers'; export const FIRST_KEY_INDEX = 1; @@ -13,4 +13,4 @@ export function transformArguments(key: string, min: number, max: number): Array ]; } -export const transformReply = transformReplyNumber; +export declare function transformReply(): number; diff --git a/lib/commands/ZDIFF.ts b/lib/commands/ZDIFF.ts index 7154947fea7..a45bf01a526 100644 --- a/lib/commands/ZDIFF.ts +++ b/lib/commands/ZDIFF.ts @@ -1,12 +1,12 @@ -import { TransformArgumentsReply } from '.'; -import { pushVerdictArgument, transformReplyStringArray } from './generic-transformers'; +import { RedisCommandArguments } from '.'; +import { pushVerdictArgument } from './generic-transformers'; export const FIRST_KEY_INDEX = 2; export const IS_READ_ONLY = true; -export function transformArguments(keys: Array | string): TransformArgumentsReply { +export function transformArguments(keys: Array | string): RedisCommandArguments { return pushVerdictArgument(['ZDIFF'], keys); } -export const transformReply = transformReplyStringArray; +export declare function transformReply(): Array; diff --git a/lib/commands/ZDIFFSTORE.ts b/lib/commands/ZDIFFSTORE.ts index f91d4c869ba..9c782b8a5ab 100644 --- a/lib/commands/ZDIFFSTORE.ts +++ b/lib/commands/ZDIFFSTORE.ts @@ -1,10 +1,10 @@ -import { TransformArgumentsReply } from '.'; -import { pushVerdictArgument, transformReplyNumber } from './generic-transformers'; +import { RedisCommandArguments } from '.'; +import { pushVerdictArgument } from './generic-transformers'; export const FIRST_KEY_INDEX = 1; -export function transformArguments(destination: string, keys: Array | string): TransformArgumentsReply { +export function transformArguments(destination: string, keys: Array | string): RedisCommandArguments { return pushVerdictArgument(['ZDIFFSTORE', destination], keys); } -export const transformReply = transformReplyNumber; +export declare function transformReply(): number; diff --git a/lib/commands/ZDIFF_WITHSCORES.ts b/lib/commands/ZDIFF_WITHSCORES.ts index 84126853361..49707563546 100644 --- a/lib/commands/ZDIFF_WITHSCORES.ts +++ b/lib/commands/ZDIFF_WITHSCORES.ts @@ -1,10 +1,10 @@ -import { TransformArgumentsReply } from '.'; +import { RedisCommandArguments } from '.'; import { transformReplySortedSetWithScores } from './generic-transformers'; import { transformArguments as transformZDiffArguments } from './ZDIFF'; export { FIRST_KEY_INDEX, IS_READ_ONLY } from './ZDIFF'; -export function transformArguments(...args: Parameters): TransformArgumentsReply { +export function transformArguments(...args: Parameters): RedisCommandArguments { return [ ...transformZDiffArguments(...args), 'WITHSCORES' diff --git a/lib/commands/ZINTER.ts b/lib/commands/ZINTER.ts index 91d7982a8e7..629515b57f9 100644 --- a/lib/commands/ZINTER.ts +++ b/lib/commands/ZINTER.ts @@ -1,5 +1,5 @@ -import { TransformArgumentsReply } from '.'; -import { pushVerdictArgument, transformReplyStringArray } from './generic-transformers'; +import { RedisCommandArguments } from '.'; +import { pushVerdictArgument } from './generic-transformers'; export const FIRST_KEY_INDEX = 2; @@ -10,7 +10,7 @@ interface ZInterOptions { AGGREGATE?: 'SUM' | 'MIN' | 'MAX'; } -export function transformArguments(keys: Array | string, options?: ZInterOptions): TransformArgumentsReply { +export function transformArguments(keys: Array | string, options?: ZInterOptions): RedisCommandArguments { const args = pushVerdictArgument(['ZINTER'], keys); if (options?.WEIGHTS) { @@ -21,10 +21,10 @@ export function transformArguments(keys: Array | string, options?: ZInte } if (options?.AGGREGATE) { - args.push('AGGREGATE', options?.AGGREGATE); + args.push('AGGREGATE', options.AGGREGATE); } return args; } -export const transformReply = transformReplyStringArray; +export declare function transformReply(): Array; diff --git a/lib/commands/ZINTERSTORE.ts b/lib/commands/ZINTERSTORE.ts index 6e79e423cb0..e0916e5b14a 100644 --- a/lib/commands/ZINTERSTORE.ts +++ b/lib/commands/ZINTERSTORE.ts @@ -1,5 +1,5 @@ -import { TransformArgumentsReply } from '.'; -import { pushVerdictArgument, transformReplyNumber } from './generic-transformers'; +import { RedisCommandArguments } from '.'; +import { pushVerdictArgument } from './generic-transformers'; export const FIRST_KEY_INDEX = 1; @@ -8,7 +8,7 @@ interface ZInterStoreOptions { AGGREGATE?: 'SUM' | 'MIN' | 'MAX'; } -export function transformArguments(destination: string, keys: Array | string, options?: ZInterStoreOptions): TransformArgumentsReply { +export function transformArguments(destination: string, keys: Array | string, options?: ZInterStoreOptions): RedisCommandArguments { const args = pushVerdictArgument(['ZINTERSTORE', destination], keys); if (options?.WEIGHTS) { @@ -19,10 +19,10 @@ export function transformArguments(destination: string, keys: Array | st } if (options?.AGGREGATE) { - args.push('AGGREGATE', options?.AGGREGATE); + args.push('AGGREGATE', options.AGGREGATE); } return args; } -export const transformReply = transformReplyNumber; +export declare function transformReply(): number; diff --git a/lib/commands/ZINTER_WITHSCORES.ts b/lib/commands/ZINTER_WITHSCORES.ts index f4287d1a684..f75a506de73 100644 --- a/lib/commands/ZINTER_WITHSCORES.ts +++ b/lib/commands/ZINTER_WITHSCORES.ts @@ -1,10 +1,10 @@ -import { TransformArgumentsReply } from '.'; +import { RedisCommandArguments } from '.'; import { transformReplySortedSetWithScores } from './generic-transformers'; import { transformArguments as transformZInterArguments } from './ZINTER'; export { FIRST_KEY_INDEX, IS_READ_ONLY } from './ZINTER'; -export function transformArguments(...args: Parameters): TransformArgumentsReply { +export function transformArguments(...args: Parameters): RedisCommandArguments { return [ ...transformZInterArguments(...args), 'WITHSCORES' diff --git a/lib/commands/ZLEXCOUNT.ts b/lib/commands/ZLEXCOUNT.ts index 2ba50dda73e..2e70fdee91d 100644 --- a/lib/commands/ZLEXCOUNT.ts +++ b/lib/commands/ZLEXCOUNT.ts @@ -1,5 +1,3 @@ -import { transformReplyNumber } from './generic-transformers'; - export const FIRST_KEY_INDEX = 1; export const IS_READ_ONLY = true; @@ -13,4 +11,4 @@ export function transformArguments(key: string, min: string, max: string): Array ]; } -export const transformReply = transformReplyNumber; +export declare function transformReply(): number; diff --git a/lib/commands/ZMSCORE.ts b/lib/commands/ZMSCORE.ts index 373adac3cf0..2790f712316 100644 --- a/lib/commands/ZMSCORE.ts +++ b/lib/commands/ZMSCORE.ts @@ -1,11 +1,11 @@ -import { TransformArgumentsReply } from '.'; +import { RedisCommandArguments } from '.'; import { pushVerdictArguments, transformReplyNumberInfinityNullArray } from './generic-transformers'; export const FIRST_KEY_INDEX = 1; export const IS_READ_ONLY = true; -export function transformArguments(key: string, member: string | Array): TransformArgumentsReply { +export function transformArguments(key: string, member: string | Array): RedisCommandArguments { return pushVerdictArguments(['ZMSCORE', key], member); } diff --git a/lib/commands/ZRANDMEMBER.ts b/lib/commands/ZRANDMEMBER.ts index 27bb7cefa50..13bb05598b7 100644 --- a/lib/commands/ZRANDMEMBER.ts +++ b/lib/commands/ZRANDMEMBER.ts @@ -1,5 +1,3 @@ -import { transformReplyStringNull } from './generic-transformers'; - export const FIRST_KEY_INDEX = 1; export const IS_READ_ONLY = true; @@ -8,4 +6,4 @@ export function transformArguments(key: string): Array { return ['ZRANDMEMBER', key]; } -export const transformReply = transformReplyStringNull; +export declare function transformReply(): string | null; diff --git a/lib/commands/ZRANDMEMBER_COUNT.ts b/lib/commands/ZRANDMEMBER_COUNT.ts index f7eef456d05..1b7b8fea994 100644 --- a/lib/commands/ZRANDMEMBER_COUNT.ts +++ b/lib/commands/ZRANDMEMBER_COUNT.ts @@ -1,4 +1,3 @@ -import { transformReplyStringArray } from './generic-transformers'; import { transformArguments as transformZRandMemberArguments } from './ZRANDMEMBER'; export { FIRST_KEY_INDEX, IS_READ_ONLY } from './ZRANDMEMBER'; @@ -10,4 +9,4 @@ export function transformArguments(key: string, count: number): Array { ]; } -export const transformReply = transformReplyStringArray; +export declare function transformReply(): Array; diff --git a/lib/commands/ZRANGE.spec.ts b/lib/commands/ZRANGE.spec.ts index 72d83931ff4..7347ed0ad09 100644 --- a/lib/commands/ZRANGE.spec.ts +++ b/lib/commands/ZRANGE.spec.ts @@ -11,6 +11,13 @@ describe('ZRANGE', () => { ); }); + it('using strings', () => { + assert.deepEqual( + transformArguments('src', '0', '1'), + ['ZRANGE', 'src', '0', '1'] + ); + }); + it('with BYSCORE', () => { assert.deepEqual( transformArguments('src', 0, 1, { diff --git a/lib/commands/ZRANGE.ts b/lib/commands/ZRANGE.ts index 9037210d69f..391c5ca893e 100644 --- a/lib/commands/ZRANGE.ts +++ b/lib/commands/ZRANGE.ts @@ -1,4 +1,4 @@ -import { transformArgumentNumberInfinity, transformReplyStringArray } from './generic-transformers'; +import { transformArgumentNumberInfinity } from './generic-transformers'; export const FIRST_KEY_INDEX = 1; @@ -42,4 +42,4 @@ export function transformArguments(key: string, min: string | number, max: strin return args; } -export const transformReply = transformReplyStringArray; +export declare function transformReply(): Array; diff --git a/lib/commands/ZRANGEBYLEX.spec.ts b/lib/commands/ZRANGEBYLEX.spec.ts new file mode 100644 index 00000000000..7f687509548 --- /dev/null +++ b/lib/commands/ZRANGEBYLEX.spec.ts @@ -0,0 +1,33 @@ +import { strict as assert } from 'assert'; +import { TestRedisServers, itWithClient } from '../test-utils'; +import { transformArguments } from './ZRANGEBYLEX'; + +describe('ZRANGEBYLEX', () => { + describe('transformArguments', () => { + it('simple', () => { + assert.deepEqual( + transformArguments('src', '-', '+'), + ['ZRANGEBYLEX', 'src', '-', '+'] + ); + }); + + it('with LIMIT', () => { + assert.deepEqual( + transformArguments('src', '-', '+', { + LIMIT: { + offset: 0, + count: 1 + } + }), + ['ZRANGEBYLEX', 'src', '-', '+', 'LIMIT', '0', '1'] + ); + }); + }); + + itWithClient(TestRedisServers.OPEN, 'client.zRangeByLex', async client => { + assert.deepEqual( + await client.zRangeByLex('src', '-', '+'), + [] + ); + }); +}); diff --git a/lib/commands/ZRANGEBYLEX.ts b/lib/commands/ZRANGEBYLEX.ts new file mode 100644 index 00000000000..7e2e7613b00 --- /dev/null +++ b/lib/commands/ZRANGEBYLEX.ts @@ -0,0 +1,35 @@ +import { RedisCommandArguments } from '.'; +import { transformArgumentNumberInfinity } from './generic-transformers'; + +export const FIRST_KEY_INDEX = 1; + +export const IS_READ_ONLY = true; + +export interface ZRangeByLexOptions { + LIMIT?: { + offset: number; + count: number; + }; +} + +export function transformArguments( + key: string, + min: number | string, + max: number | string, + options?: ZRangeByLexOptions +): RedisCommandArguments { + const args = [ + 'ZRANGEBYLEX', + key, + typeof min === 'string' ? min : transformArgumentNumberInfinity(min), + typeof max === 'string' ? max : transformArgumentNumberInfinity(max) + ]; + + if (options?.LIMIT) { + args.push('LIMIT', options.LIMIT.offset.toString(), options.LIMIT.count.toString()); + } + + return args; +} + +export declare function transformReply(): Array; diff --git a/lib/commands/ZRANGEBYSCORE.spec.ts b/lib/commands/ZRANGEBYSCORE.spec.ts new file mode 100644 index 00000000000..0419b232563 --- /dev/null +++ b/lib/commands/ZRANGEBYSCORE.spec.ts @@ -0,0 +1,33 @@ +import { strict as assert } from 'assert'; +import { TestRedisServers, itWithClient } from '../test-utils'; +import { transformArguments } from './ZRANGEBYSCORE'; + +describe('ZRANGEBYSCORE', () => { + describe('transformArguments', () => { + it('simple', () => { + assert.deepEqual( + transformArguments('src', 0, 1), + ['ZRANGEBYSCORE', 'src', '0', '1'] + ); + }); + + it('with LIMIT', () => { + assert.deepEqual( + transformArguments('src', 0, 1, { + LIMIT: { + offset: 0, + count: 1 + } + }), + ['ZRANGEBYSCORE', 'src', '0', '1', 'LIMIT', '0', '1'] + ); + }); + }); + + itWithClient(TestRedisServers.OPEN, 'client.zRangeByScore', async client => { + assert.deepEqual( + await client.zRangeByScore('src', 0, 1), + [] + ); + }); +}); diff --git a/lib/commands/ZRANGEBYSCORE.ts b/lib/commands/ZRANGEBYSCORE.ts new file mode 100644 index 00000000000..48dd8a415c6 --- /dev/null +++ b/lib/commands/ZRANGEBYSCORE.ts @@ -0,0 +1,35 @@ +import { RedisCommandArguments } from '.'; +import { transformArgumentNumberInfinity } from './generic-transformers'; + +export const FIRST_KEY_INDEX = 1; + +export const IS_READ_ONLY = true; + +export interface ZRangeByScoreOptions { + LIMIT?: { + offset: number; + count: number; + }; +} + +export function transformArguments( + key: string, + min: number | string, + max: number | string, + options?: ZRangeByScoreOptions +): RedisCommandArguments { + const args = [ + 'ZRANGEBYSCORE', + key, + typeof min === 'string' ? min : transformArgumentNumberInfinity(min), + typeof max === 'string' ? max : transformArgumentNumberInfinity(max) + ]; + + if (options?.LIMIT) { + args.push('LIMIT', options.LIMIT.offset.toString(), options.LIMIT.count.toString()); + } + + return args; +} + +export declare function transformReply(): Array; diff --git a/lib/commands/ZRANGEBYSCORE_WITHSCORES.spec.ts b/lib/commands/ZRANGEBYSCORE_WITHSCORES.spec.ts new file mode 100644 index 00000000000..84d1aeb0aad --- /dev/null +++ b/lib/commands/ZRANGEBYSCORE_WITHSCORES.spec.ts @@ -0,0 +1,33 @@ +import { strict as assert } from 'assert'; +import { TestRedisServers, itWithClient } from '../test-utils'; +import { transformArguments } from './ZRANGEBYSCORE_WITHSCORES'; + +describe('ZRANGEBYSCORE WITHSCORES', () => { + describe('transformArguments', () => { + it('simple', () => { + assert.deepEqual( + transformArguments('src', 0, 1), + ['ZRANGEBYSCORE', 'src', '0', '1', 'WITHSCORES'] + ); + }); + + it('with LIMIT', () => { + assert.deepEqual( + transformArguments('src', 0, 1, { + LIMIT: { + offset: 0, + count: 1 + } + }), + ['ZRANGEBYSCORE', 'src', '0', '1', 'LIMIT', '0', '1', 'WITHSCORES'] + ); + }); + }); + + itWithClient(TestRedisServers.OPEN, 'client.zRangeByScoreWithScores', async client => { + assert.deepEqual( + await client.zRangeByScoreWithScores('src', 0, 1), + [] + ); + }); +}); diff --git a/lib/commands/ZRANGEBYSCORE_WITHSCORES.ts b/lib/commands/ZRANGEBYSCORE_WITHSCORES.ts new file mode 100644 index 00000000000..f6f7f993cbc --- /dev/null +++ b/lib/commands/ZRANGEBYSCORE_WITHSCORES.ts @@ -0,0 +1,19 @@ +import { RedisCommandArguments } from '.'; +import { transformReplySortedSetWithScores } from './generic-transformers'; +import { ZRangeByScoreOptions, transformArguments as transformZRangeByScoreArguments } from './ZRANGEBYSCORE'; + +export { FIRST_KEY_INDEX, IS_READ_ONLY } from './ZRANGEBYSCORE'; + +export function transformArguments( + key: string, + min: number | string, + max: number | string, + options?: ZRangeByScoreOptions +): RedisCommandArguments { + return [ + ...transformZRangeByScoreArguments(key, min, max, options), + 'WITHSCORES' + ]; +} + +export const transformReply = transformReplySortedSetWithScores; diff --git a/lib/commands/ZRANGESTORE.spec.ts b/lib/commands/ZRANGESTORE.spec.ts index 30dee7c0b5b..54055656409 100644 --- a/lib/commands/ZRANGESTORE.spec.ts +++ b/lib/commands/ZRANGESTORE.spec.ts @@ -1,6 +1,6 @@ import { strict as assert } from 'assert'; import { TestRedisServers, itWithClient, describeHandleMinimumRedisVersion } from '../test-utils'; -import { transformArguments } from './ZRANGESTORE'; +import { transformArguments, transformReply } from './ZRANGESTORE'; describe('ZRANGESTORE', () => { describeHandleMinimumRedisVersion([6, 2]); @@ -68,6 +68,15 @@ describe('ZRANGESTORE', () => { }); }); + describe('transformReply', () => { + it('should throw TypeError when reply is not a number', () => { + assert.throws( + () => (transformReply as any)([]), + TypeError + ); + }); + }); + itWithClient(TestRedisServers.OPEN, 'client.zRangeStore', async client => { await client.zAdd('src', { score: 0.5, diff --git a/lib/commands/ZRANK.ts b/lib/commands/ZRANK.ts index 84f9c7d4a9e..e060ccf6f8c 100644 --- a/lib/commands/ZRANK.ts +++ b/lib/commands/ZRANK.ts @@ -1,5 +1,3 @@ -import { transformReplyNumberNull } from './generic-transformers'; - export const FIRST_KEY_INDEX = 1; export const IS_READ_ONLY = true; @@ -8,4 +6,4 @@ export function transformArguments(key: string, member: string): Array { return ['ZRANK', key, member]; } -export const transformReply = transformReplyNumberNull; +export declare function transformReply(): number | null; diff --git a/lib/commands/ZREM.ts b/lib/commands/ZREM.ts index 8419291f2fd..332289b3fdb 100644 --- a/lib/commands/ZREM.ts +++ b/lib/commands/ZREM.ts @@ -1,10 +1,10 @@ -import { TransformArgumentsReply } from '.'; -import { pushVerdictArguments, transformReplyNumber } from './generic-transformers'; +import { RedisCommandArguments } from '.'; +import { pushVerdictArguments } from './generic-transformers'; export const FIRST_KEY_INDEX = 1; -export function transformArguments(key: string, member: string | Array): TransformArgumentsReply { +export function transformArguments(key: string, member: string | Array): RedisCommandArguments { return pushVerdictArguments(['ZREM', key], member); } -export const transformReply = transformReplyNumber; +export declare function transformReply(): number; diff --git a/lib/commands/ZREMRANGEBYLEX.ts b/lib/commands/ZREMRANGEBYLEX.ts index aaf92992f98..1f17d6b986e 100644 --- a/lib/commands/ZREMRANGEBYLEX.ts +++ b/lib/commands/ZREMRANGEBYLEX.ts @@ -1,9 +1,7 @@ -import { transformReplyNumber } from './generic-transformers'; - export const FIRST_KEY_INDEX = 1; export function transformArguments(key: string, min: string, max: string): Array { return ['ZREMRANGEBYLEX', key, min, max]; } -export const transformReply = transformReplyNumber; +export declare function transformReply(): number; diff --git a/lib/commands/ZREMRANGEBYRANK.ts b/lib/commands/ZREMRANGEBYRANK.ts index 89bf63d8e34..550a56e8a85 100644 --- a/lib/commands/ZREMRANGEBYRANK.ts +++ b/lib/commands/ZREMRANGEBYRANK.ts @@ -1,9 +1,7 @@ -import { transformReplyNumber } from './generic-transformers'; - export const FIRST_KEY_INDEX = 1; export function transformArguments(key: string, start: number, stop: number): Array { return ['ZREMRANGEBYRANK', key, start.toString(), stop.toString()]; } -export const transformReply = transformReplyNumber; +export declare function transformReply(): number; diff --git a/lib/commands/ZREMRANGEBYSCORE.ts b/lib/commands/ZREMRANGEBYSCORE.ts index 64d14a4eb41..e0186fcb64e 100644 --- a/lib/commands/ZREMRANGEBYSCORE.ts +++ b/lib/commands/ZREMRANGEBYSCORE.ts @@ -1,9 +1,7 @@ -import { transformReplyNumber } from './generic-transformers'; - export const FIRST_KEY_INDEX = 1; export function transformArguments(key: string, min: number, max: number): Array { return ['ZREMRANGEBYSCORE', key, min.toString(), max.toString()]; } -export const transformReply = transformReplyNumber; +export declare function transformReply(): number; diff --git a/lib/commands/ZREVRANK.ts b/lib/commands/ZREVRANK.ts index 7d4c4ce2ab5..808d9e45dc3 100644 --- a/lib/commands/ZREVRANK.ts +++ b/lib/commands/ZREVRANK.ts @@ -1,5 +1,3 @@ -import { transformReplyNumberNull } from './generic-transformers'; - export const FIRST_KEY_INDEX = 1; export const IS_READ_ONLY = true; @@ -8,4 +6,4 @@ export function transformArguments(key: string, member: string): Array { return ['ZREVRANK', key, member]; } -export const transformReply = transformReplyNumberNull; +export declare function transformReply(): number | null; diff --git a/lib/commands/ZUNION.ts b/lib/commands/ZUNION.ts index 87158b8425a..2163978470c 100644 --- a/lib/commands/ZUNION.ts +++ b/lib/commands/ZUNION.ts @@ -1,5 +1,5 @@ -import { TransformArgumentsReply } from '.'; -import { pushVerdictArgument, transformReplyStringArray } from './generic-transformers'; +import { RedisCommandArguments } from '.'; +import { pushVerdictArgument } from './generic-transformers'; export const FIRST_KEY_INDEX = 2; @@ -10,7 +10,7 @@ interface ZUnionOptions { AGGREGATE?: 'SUM' | 'MIN' | 'MAX'; } -export function transformArguments(keys: Array | string, options?: ZUnionOptions): TransformArgumentsReply { +export function transformArguments(keys: Array | string, options?: ZUnionOptions): RedisCommandArguments { const args = pushVerdictArgument(['ZUNION'], keys); if (options?.WEIGHTS) { @@ -24,4 +24,4 @@ export function transformArguments(keys: Array | string, options?: ZUnio return args; } -export const transformReply = transformReplyStringArray; +export declare function transformReply(): Array; diff --git a/lib/commands/ZUNIONSTORE.ts b/lib/commands/ZUNIONSTORE.ts index 4ebbdbd8591..406f0430c52 100644 --- a/lib/commands/ZUNIONSTORE.ts +++ b/lib/commands/ZUNIONSTORE.ts @@ -1,5 +1,5 @@ -import { TransformArgumentsReply } from '.'; -import { pushVerdictArgument, transformReplyNumber } from './generic-transformers'; +import { RedisCommandArguments } from '.'; +import { pushVerdictArgument } from './generic-transformers'; export const FIRST_KEY_INDEX = 1; @@ -8,7 +8,7 @@ interface ZUnionOptions { AGGREGATE?: 'SUM' | 'MIN' | 'MAX'; } -export function transformArguments(destination: string, keys: Array | string, options?: ZUnionOptions): TransformArgumentsReply { +export function transformArguments(destination: string, keys: Array | string, options?: ZUnionOptions): RedisCommandArguments { const args = pushVerdictArgument(['ZUNIONSTORE', destination], keys); if (options?.WEIGHTS) { @@ -22,4 +22,4 @@ export function transformArguments(destination: string, keys: Array | st return args; } -export const transformReply = transformReplyNumber; +export declare function transformReply(): number; diff --git a/lib/commands/ZUNION_WITHSCORES.ts b/lib/commands/ZUNION_WITHSCORES.ts index 2215dad9749..d361fd432b0 100644 --- a/lib/commands/ZUNION_WITHSCORES.ts +++ b/lib/commands/ZUNION_WITHSCORES.ts @@ -1,10 +1,10 @@ -import { TransformArgumentsReply } from '.'; +import { RedisCommandArguments } from '.'; import { transformReplySortedSetWithScores } from './generic-transformers'; import { transformArguments as transformZUnionArguments } from './ZUNION'; export { FIRST_KEY_INDEX, IS_READ_ONLY } from './ZUNION'; -export function transformArguments(...args: Parameters): TransformArgumentsReply { +export function transformArguments(...args: Parameters): RedisCommandArguments { return [ ...transformZUnionArguments(...args), 'WITHSCORES' diff --git a/lib/commands/generic-transformers.spec.ts b/lib/commands/generic-transformers.spec.ts index 9ac72bb1b25..bdc3ee938cd 100644 --- a/lib/commands/generic-transformers.spec.ts +++ b/lib/commands/generic-transformers.spec.ts @@ -21,7 +21,10 @@ import { pushStringTuplesArguments, pushVerdictArguments, pushVerdictArgument, - pushOptionalVerdictArgument + pushOptionalVerdictArgument, + transformCommandReply, + CommandFlags, + CommandCategories } from './generic-transformers'; describe('Generic Transformers', () => { @@ -300,6 +303,13 @@ describe('Generic Transformers', () => { ); }); + it('with COUNT', () => { + assert.deepEqual( + pushGeoCountArgument([], 1), + ['COUNT', '1'] + ); + }); + it('with ANY', () => { assert.deepEqual( pushGeoCountArgument([], { @@ -619,4 +629,27 @@ describe('Generic Transformers', () => { ); }); }); + + it('transformCommandReply', () => { + assert.deepEqual( + transformCommandReply([ + 'ping', + -1, + [CommandFlags.STALE, CommandFlags.FAST], + 0, + 0, + 0, + [CommandCategories.FAST, CommandCategories.CONNECTION] + ]), + { + name: 'ping', + arity: -1, + flags: new Set([CommandFlags.STALE, CommandFlags.FAST]), + firstKeyIndex: 0, + lastKeyIndex: 0, + step: 0, + categories: new Set([CommandCategories.FAST, CommandCategories.CONNECTION]) + } + ); + }); }); diff --git a/lib/commands/generic-transformers.ts b/lib/commands/generic-transformers.ts index bbc12ee113e..a531e86b432 100644 --- a/lib/commands/generic-transformers.ts +++ b/lib/commands/generic-transformers.ts @@ -1,40 +1,4 @@ -import { TransformArgumentsReply } from '.'; - -export function transformReplyNumber(reply: number): number { - return reply; -} - -export function transformReplyNumberNull(reply: number | null): number | null { - return reply; -} - -export function transformReplyNumberArray(reply: Array): Array { - return reply; -} - -export function transformReplyNumberNullArray(reply: Array): Array { - return reply; -} - -export function transformReplyString(reply: string): string { - return reply; -} - -export function transformReplyStringNull(reply: string | null): string | null { - return reply; -} - -export function transformReplyStringArray(reply: Array): Array { - return reply; -} - -export function transformReplyStringArrayNull(reply: Array | null): Array | null { - return reply; -} - -export function transformReplyStringNullArray(reply: Array): Array { - return reply; -} +import { RedisCommandArguments } from '.'; export function transformReplyBoolean(reply: number): boolean { return reply === 1; @@ -46,16 +10,6 @@ export function transformReplyBooleanArray(reply: Array): Array export type BitValue = 0 | 1; -export function transformReplyBit(reply: BitValue): BitValue { - return reply; -} - -export function transformReplyBufferNull(reply: Buffer | null): Buffer | null { - return reply; -} - -export function transformReplyVoid(): void {} - export interface ScanOptions { MATCH?: string; COUNT?: number; @@ -250,12 +204,10 @@ export function pushGeoSearchArguments( args.push('BYBOX', by.width.toString(), by.height.toString()); } - if (by.unit) { - args.push(by.unit); - } + args.push(by.unit); if (options?.SORT) { - args.push(options?.SORT); + args.push(options.SORT); } pushGeoCountArgument(args, options?.COUNT); @@ -356,7 +308,7 @@ export function pushStringTuplesArguments(args: Array, tuples: StringTup return args; } -export function pushVerdictArguments(args: TransformArgumentsReply, value: string | Buffer | Array): TransformArgumentsReply { +export function pushVerdictArguments(args: RedisCommandArguments, value: string | Buffer | Array): RedisCommandArguments { if (Array.isArray(value)) { args.push(...value); } else { @@ -366,7 +318,7 @@ export function pushVerdictArguments(args: TransformArgumentsReply, value: strin return args; } -export function pushVerdictArgument(args: TransformArgumentsReply, value: string | Array): TransformArgumentsReply { +export function pushVerdictArgument(args: RedisCommandArguments, value: string | Array): RedisCommandArguments { if (typeof value === 'string') { args.push('1', value); } else { @@ -376,10 +328,86 @@ export function pushVerdictArgument(args: TransformArgumentsReply, value: string return args; } -export function pushOptionalVerdictArgument(args: TransformArgumentsReply, name: string, value: undefined | string | Array): TransformArgumentsReply { +export function pushOptionalVerdictArgument(args: RedisCommandArguments, name: string, value: undefined | string | Array): RedisCommandArguments { if (value === undefined) return args; args.push(name); return pushVerdictArgument(args, value); } + +export enum CommandFlags { + WRITE = 'write', // command may result in modifications + READONLY = 'readonly', // command will never modify keys + DENYOOM = 'denyoom', // reject command if currently out of memory + ADMIN = 'admin', // server admin command + PUBSUB = 'pubsub', // pubsub-related command + NOSCRIPT = 'noscript', // deny this command from scripts + RANDOM = 'random', // command has random results, dangerous for scripts + SORT_FOR_SCRIPT = 'sort_for_script', // if called from script, sort output + LOADING = 'loading', // allow command while database is loading + STALE = 'stale', // allow command while replica has stale data + SKIP_MONITOR = 'skip_monitor', // do not show this command in MONITOR + ASKING = 'asking', // cluster related - accept even if importing + FAST = 'fast', // command operates in constant or log(N) time. Used for latency monitoring. + MOVABLEKEYS = 'movablekeys' // keys have no pre-determined position. You must discover keys yourself. +} + +export enum CommandCategories { + KEYSPACE = '@keyspace', + READ = '@read', + WRITE = '@write', + SET = '@set', + SORTEDSET = '@sortedset', + LIST = '@list', + HASH = '@hash', + STRING = '@string', + BITMAP = '@bitmap', + HYPERLOGLOG = '@hyperloglog', + GEO = '@geo', + STREAM = '@stream', + PUBSUB = '@pubsub', + ADMIN = '@admin', + FAST = '@fast', + SLOW = '@slow', + BLOCKING = '@blocking', + DANGEROUS = '@dangerous', + CONNECTION = '@connection', + TRANSACTION = '@transaction', + SCRIPTING = '@scripting' +} + +export type CommandRawReply = [ + name: string, + arity: number, + flags: Array, + firstKeyIndex: number, + lastKeyIndex: number, + step: number, + categories: Array +]; + +export type CommandReply = { + name: string, + arity: number, + flags: Set, + firstKeyIndex: number, + lastKeyIndex: number, + step: number, + categories: Set +}; + +export function transformCommandReply( + this: void, + [name, arity, flags, firstKeyIndex, lastKeyIndex, step, categories]: CommandRawReply +): CommandReply { + return { + name, + arity, + flags: new Set(flags), + firstKeyIndex, + lastKeyIndex, + step, + categories: new Set(categories) + }; +} diff --git a/lib/commands/index.ts b/lib/commands/index.ts index 89581090e58..f88d777c892 100644 --- a/lib/commands/index.ts +++ b/lib/commands/index.ts @@ -1,756 +1,19 @@ -import * as ACL_CAT from './ACL_CAT'; -import * as ACL_DELUSER from './ACL_DELUSER'; -import * as ACL_GENPASS from './ACL_GENPASS'; -import * as ACL_GETUSER from './ACL_GETUSER'; -import * as ACL_LIST from './ACL_LIST'; -import * as ACL_LOAD from './ACL_LOAD'; -import * as ACL_LOG_RESET from './ACL_LOG_RESET'; -import * as ACL_LOG from './ACL_LOG'; -import * as ACL_SAVE from './ACL_SAVE'; -import * as ACL_SETUSER from './ACL_SETUSER'; -import * as ACL_USERS from './ACL_USERS'; -import * as ACL_WHOAMI from './ACL_WHOAMI'; -import * as APPEND from './APPEND'; -import * as ASKING from './ASKING'; -import * as AUTH from './AUTH'; -import * as BGREWRITEAOF from './BGREWRITEAOF'; -import * as BGSAVE from './BGSAVE'; -import * as BITCOUNT from './BITCOUNT'; -import * as BITFIELD from './BITFIELD'; -import * as BITOP from './BITOP'; -import * as BITPOS from './BITPOS'; -import * as BLMOVE from './BLMOVE'; -import * as BLPOP from './BLPOP'; -import * as BRPOP from './BRPOP'; -import * as BRPOPLPUSH from './BRPOPLPUSH'; -import * as BZPOPMAX from './BZPOPMAX'; -import * as BZPOPMIN from './BZPOPMIN'; -import * as CLIENT_ID from './CLIENT_ID'; -import * as CLIENT_INFO from './CLIENT_INFO'; -import * as CLUSTER_ADDSLOTS from './CLUSTER_ADDSLOTS'; -import * as CLUSTER_FLUSHSLOTS from './CLUSTER_FLUSHSLOTS'; -import * as CLUSTER_INFO from './CLUSTER_INFO'; -import * as CLUSTER_NODES from './CLUSTER_NODES'; -import * as CLUSTER_MEET from './CLUSTER_MEET'; -import * as CLUSTER_RESET from './CLUSTER_RESET'; -import * as CLUSTER_SETSLOT from './CLUSTER_SETSLOT'; -import * as CLUSTER_SLOTS from './CLUSTER_SLOTS'; -import * as CONFIG_GET from './CONFIG_GET'; -import * as CONFIG_RESETASTAT from './CONFIG_RESETSTAT'; -import * as CONFIG_REWRITE from './CONFIG_REWRITE'; -import * as CONFIG_SET from './CONFIG_SET'; -import * as COPY from './COPY'; -import * as DBSIZE from './DBSIZE'; -import * as DECR from './DECR'; -import * as DECRBY from './DECRBY'; -import * as DEL from './DEL'; -import * as DISCARD from './DISCARD'; -import * as DUMP from './DUMP'; -import * as ECHO from './ECHO'; -import * as EVAL from './EVAL'; -import * as EVALSHA from './EVALSHA'; -import * as EXISTS from './EXISTS'; -import * as EXPIRE from './EXPIRE'; -import * as EXPIREAT from './EXPIREAT'; -import * as FAILOVER from './FAILOVER'; -import * as FLUSHALL from './FLUSHALL'; -import * as FLUSHDB from './FLUSHDB'; -import * as GEOADD from './GEOADD'; -import * as GEODIST from './GEODIST'; -import * as GEOHASH from './GEOHASH'; -import * as GEOPOS from './GEOPOS'; -import * as GEOSEARCH_WITH from './GEOSEARCH_WITH'; -import * as GEOSEARCH from './GEOSEARCH'; -import * as GEOSEARCHSTORE from './GEOSEARCHSTORE'; -import * as GET_BUFFER from './GET_BUFFER'; -import * as GET from './GET'; -import * as GETBIT from './GETBIT'; -import * as GETDEL from './GETDEL'; -import * as GETEX from './GETEX'; -import * as GETRANGE from './GETRANGE'; -import * as GETSET from './GETSET'; -import * as HDEL from './HDEL'; -import * as HELLO from './HELLO'; -import * as HEXISTS from './HEXISTS'; -import * as HGET from './HGET'; -import * as HGETALL from './HGETALL'; -import * as HINCRBY from './HINCRBY'; -import * as HINCRBYFLOAT from './HINCRBYFLOAT'; -import * as HKEYS from './HKEYS'; -import * as HLEN from './HLEN'; -import * as HMGET from './HMGET'; -import * as HRANDFIELD_COUNT_WITHVALUES from './HRANDFIELD_COUNT_WITHVALUES'; -import * as HRANDFIELD_COUNT from './HRANDFIELD_COUNT'; -import * as HRANDFIELD from './HRANDFIELD'; -import * as HSCAN from './HSCAN'; -import * as HSET from './HSET'; -import * as HSETNX from './HSETNX'; -import * as HSTRLEN from './HSTRLEN'; -import * as HVALS from './HVALS'; -import * as INCR from './INCR'; -import * as INCRBY from './INCRBY'; -import * as INCRBYFLOAT from './INCRBYFLOAT'; -import * as INFO from './INFO'; -import * as KEYS from './KEYS'; -import * as LASTSAVE from './LASTSAVE'; -import * as LINDEX from './LINDEX'; -import * as LINSERT from './LINSERT'; -import * as LLEN from './LLEN'; -import * as LMOVE from './LMOVE'; -import * as LOLWUT from './LOLWUT'; -import * as LPOP_COUNT from './LPOP_COUNT'; -import * as LPOP from './LPOP'; -import * as LPOS_COUNT from './LPOS_COUNT'; -import * as LPOS from './LPOS'; -import * as LPUSH from './LPUSH'; -import * as LPUSHX from './LPUSHX'; -import * as LRANGE from './LRANGE'; -import * as LREM from './LREM'; -import * as LSET from './LSET'; -import * as LTRIM from './LTRIM'; -import * as MEMOERY_DOCTOR from './MEMORY_DOCTOR'; -import * as MEMORY_MALLOC_STATS from './MEMORY_MALLOC-STATS'; -import * as MEMORY_PURGE from './MEMORY_PURGE'; -import * as MEMORY_STATS from './MEMORY_STATS'; -import * as MEMORY_USAGE from './MEMORY_USAGE'; -import * as MGET from './MGET'; -import * as MIGRATE from './MIGRATE'; -import * as MODULE_LIST from './MODULE_LIST'; -import * as MODULE_LOAD from './MODULE_LOAD'; -import * as MODULE_UNLOAD from './MODULE_UNLOAD'; -import * as MOVE from './MOVE'; -import * as MSET from './MSET'; -import * as MSETNX from './MSETNX'; -import * as PERSIST from './PERSIST'; -import * as PEXPIRE from './PEXPIRE'; -import * as PEXPIREAT from './PEXPIREAT'; -import * as PFADD from './PFADD'; -import * as PFCOUNT from './PFCOUNT'; -import * as PFMERGE from './PFMERGE'; -import * as PING from './PING'; -import * as PSETEX from './PSETEX'; -import * as PTTL from './PTTL'; -import * as PUBLISH from './PUBLISH'; -import * as PUBSUB_CHANNELS from './PUBSUB_CHANNELS'; -import * as PUBSUB_NUMPAT from './PUBSUB_NUMPAT'; -import * as PUBSUB_NUMSUB from './PUBSUB_NUMSUB'; -import * as RANDOMKEY from './RANDOMKEY'; -import * as READONLY from './READONLY'; -import * as READWRITE from './READWRITE'; -import * as RENAME from './RENAME'; -import * as RENAMENX from './RENAMENX'; -import * as REPLICAOF from './REPLICAOF'; -import * as RESTORE_ASKING from './RESTORE-ASKING'; -import * as ROLE from './ROLE'; -import * as RPOP_COUNT from './RPOP_COUNT'; -import * as RPOP from './RPOP'; -import * as RPOPLPUSH from './RPOPLPUSH'; -import * as RPUSH from './RPUSH'; -import * as RPUSHX from './RPUSHX'; -import * as SADD from './SADD'; -import * as SAVE from './SAVE'; -import * as SCAN from './SCAN'; -import * as SCARD from './SCARD'; -import * as SCRIPT_DEBUG from './SCRIPT_DEBUG'; -import * as SCRIPT_EXISTS from './SCRIPT_EXISTS'; -import * as SCRIPT_FLUSH from './SCRIPT_FLUSH'; -import * as SCRIPT_KILL from './SCRIPT_KILL'; -import * as SCRIPT_LOAD from './SCRIPT_LOAD'; -import * as SDIFF from './SDIFF'; -import * as SDIFFSTORE from './SDIFFSTORE'; -import * as SET from './SET'; -import * as SETBIT from './SETBIT'; -import * as SETEX from './SETEX'; -import * as SETNX from './SETNX'; -import * as SETRANGE from './SETRANGE'; -import * as SHUTDOWN from './SHUTDOWN'; -import * as SINTER from './SINTER'; -import * as SINTERSTORE from './SINTERSTORE'; -import * as SISMEMBER from './SISMEMBER'; -import * as SMEMBERS from './SMEMBERS'; -import * as SMISMEMBER from './SMISMEMBER'; -import * as SMOVE from './SMOVE'; -import * as SORT from './SORT'; -import * as SPOP from './SPOP'; -import * as SRANDMEMBER_COUNT from './SRANDMEMBER_COUNT'; -import * as SRANDMEMBER from './SRANDMEMBER'; -import * as SREM from './SREM'; -import * as SSCAN from './SSCAN'; -import * as STRLEN from './STRLEN'; -import * as SUNION from './SUNION'; -import * as SUNIONSTORE from './SUNIONSTORE'; -import * as SWAPDB from './SWAPDB'; -import * as TIME from './TIME'; -import * as TOUCH from './TOUCH'; -import * as TTL from './TTL'; -import * as TYPE from './TYPE'; -import * as UNLINK from './UNLINK'; -import * as UNWATCH from './UNWATCH'; -import * as WAIT from './WAIT'; -import * as WATCH from './WATCH'; -import * as XACK from './XACK'; -import * as XADD from './XADD'; -import * as XAUTOCLAIM_JUSTID from './XAUTOCLAIM_JUSTID'; -import * as XAUTOCLAIM from './XAUTOCLAIM'; -import * as XCLAIM from './XCLAIM'; -import * as XCLAIM_JUSTID from './XCLAIM_JUSTID'; -import * as XDEL from './XDEL'; -import * as XGROUP_CREATE from './XGROUP_CREATE'; -import * as XGROUP_CREATECONSUMER from './XGROUP_CREATECONSUMER'; -import * as XGROUP_DELCONSUMER from './XGROUP_DELCONSUMER'; -import * as XGROUP_DESTROY from './XGROUP_DESTROY'; -import * as XGROUP_SETID from './XGROUP_SETID'; -import * as XINFO_CONSUMERS from './XINFO_CONSUMERS'; -import * as XINFO_GROUPS from './XINFO_GROUPS'; -import * as XINFO_STREAM from './XINFO_STREAM'; -import * as XLEN from './XLEN'; -import * as XPENDING_RANGE from './XPENDING_RANGE'; -import * as XPENDING from './XPENDING'; -import * as XRANGE from './XRANGE'; -import * as XREAD from './XREAD'; -import * as XREADGROUP from './XREADGROUP'; -import * as XREVRANGE from './XREVRANGE'; -import * as XTRIM from './XTRIM'; -import * as ZADD from './ZADD'; -import * as ZCARD from './ZCARD'; -import * as ZCOUNT from './ZCOUNT'; -import * as ZDIFF_WITHSCORES from './ZDIFF_WITHSCORES'; -import * as ZDIFF from './ZDIFF'; -import * as ZDIFFSTORE from './ZDIFFSTORE'; -import * as ZINCRBY from './ZINCRBY'; -import * as ZINTER_WITHSCORES from './ZINTER_WITHSCORES'; -import * as ZINTER from './ZINTER'; -import * as ZINTERSTORE from './ZINTERSTORE'; -import * as ZLEXCOUNT from './ZLEXCOUNT'; -import * as ZMSCORE from './ZMSCORE'; -import * as ZPOPMAX_COUNT from './ZPOPMAX_COUNT'; -import * as ZPOPMAX from './ZPOPMAX'; -import * as ZPOPMIN_COUNT from './ZPOPMIN_COUNT'; -import * as ZPOPMIN from './ZPOPMIN'; -import * as ZRANDMEMBER_COUNT_WITHSCORES from './ZRANDMEMBER_COUNT_WITHSCORES'; -import * as ZRANDMEMBER_COUNT from './ZRANDMEMBER_COUNT'; -import * as ZRANDMEMBER from './ZRANDMEMBER'; -import * as ZRANGE_WITHSCORES from './ZRANGE_WITHSCORES'; -import * as ZRANGE from './ZRANGE'; -import * as ZRANGESTORE from './ZRANGESTORE'; -import * as ZRANK from './ZRANK'; -import * as ZREM from './ZREM'; -import * as ZREMRANGEBYLEX from './ZREMRANGEBYLEX'; -import * as ZREMRANGEBYRANK from './ZREMRANGEBYRANK'; -import * as ZREMRANGEBYSCORE from './ZREMRANGEBYSCORE'; -import * as ZREVRANK from './ZREVRANK'; -import * as ZSCAN from './ZSCAN'; -import * as ZSCORE from './ZSCORE'; -import * as ZUNION_WITHSCORES from './ZUNION_WITHSCORES'; -import * as ZUNION from './ZUNION'; -import * as ZUNIONSTORE from './ZUNIONSTORE'; +import { RedisScriptConfig, SHA1 } from '../lua-script'; -export default { - ACL_CAT, - aclCat: ACL_CAT, - ACL_DELUSER, - aclDelUser: ACL_DELUSER, - ACL_GENPASS, - aclGenPass: ACL_GENPASS, - ACL_GETUSER, - aclGetUser: ACL_GETUSER, - ACL_LIST, - aclList: ACL_LIST, - ACL_LOAD, - aclLoad: ACL_LOAD, - ACL_LOG_RESET, - aclLogReset: ACL_LOG_RESET, - ACL_LOG, - aclLog: ACL_LOG, - ACL_SAVE, - aclSave: ACL_SAVE, - ACL_SETUSER, - aclSetUser: ACL_SETUSER, - ACL_USERS, - aclUsers: ACL_USERS, - ACL_WHOAMI, - aclWhoAmI: ACL_WHOAMI, - APPEND, - append: APPEND, - ASKING, - asking: ASKING, - AUTH, - auth: AUTH, - BGREWRITEAOF, - bgRewriteAof: BGREWRITEAOF, - BGSAVE, - bgSave: BGSAVE, - BITCOUNT, - bitCount: BITCOUNT, - BITFIELD, - bitField: BITFIELD, - BITOP, - bitOp: BITOP, - BITPOS, - bitPos: BITPOS, - BLMOVE, - blMove: BLMOVE, - BLPOP, - blPop: BLPOP, - BRPOP, - brPop: BRPOP, - BRPOPLPUSH, - brPopLPush: BRPOPLPUSH, - BZPOPMAX, - bzPopMax: BZPOPMAX, - BZPOPMIN, - bzPopMin: BZPOPMIN, - CLIENT_ID, - clientId: CLIENT_ID, - CLIENT_INFO, - clientInfo: CLIENT_INFO, - CLUSTER_ADDSLOTS, - clusterAddSlots: CLUSTER_ADDSLOTS, - CLUSTER_FLUSHSLOTS, - clusterFlushSlots: CLUSTER_FLUSHSLOTS, - CLUSTER_INFO, - clusterInfo: CLUSTER_INFO, - CLUSTER_NODES, - clusterNodes: CLUSTER_NODES, - CLUSTER_MEET, - clusterMeet: CLUSTER_MEET, - CLUSTER_RESET, - clusterReset: CLUSTER_RESET, - CLUSTER_SETSLOT, - clusterSetSlot: CLUSTER_SETSLOT, - CLUSTER_SLOTS, - clusterSlots: CLUSTER_SLOTS, - CONFIG_GET, - configGet: CONFIG_GET, - CONFIG_RESETASTAT, - configResetStat: CONFIG_RESETASTAT, - CONFIG_REWRITE, - configRewrite: CONFIG_REWRITE, - CONFIG_SET, - configSet: CONFIG_SET, - COPY, - copy: COPY, - DBSIZE, - dbSize: DBSIZE, - DECR, - decr: DECR, - DECRBY, - decrBy: DECRBY, - DEL, - del: DEL, - DISCARD, - discard: DISCARD, - DUMP, - dump: DUMP, - ECHO, - echo: ECHO, - EVAL, - eval: EVAL, - EVALSHA, - evalSha: EVALSHA, - EXISTS, - exists: EXISTS, - EXPIRE, - expire: EXPIRE, - EXPIREAT, - expireAt: EXPIREAT, - FAILOVER, - failover: FAILOVER, - FLUSHALL, - flushAll: FLUSHALL, - FLUSHDB, - flushDb: FLUSHDB, - GEOADD, - geoAdd: GEOADD, - GEODIST, - geoDist: GEODIST, - GEOHASH, - geoHash: GEOHASH, - GEOPOS, - geoPos: GEOPOS, - GEOSEARCH_WITH, - geoSearchWith: GEOSEARCH_WITH, - GEOSEARCH, - geoSearch: GEOSEARCH, - GEOSEARCHSTORE, - geoSearchStore: GEOSEARCHSTORE, - GET_BUFFER, - getBuffer: GET_BUFFER, - GET, - get: GET, - GETBIT, - getBit: GETBIT, - GETDEL, - getDel: GETDEL, - GETEX, - getEx: GETEX, - GETRANGE, - getRange: GETRANGE, - GETSET, - getSet: GETSET, - HDEL, - hDel: HDEL, - HELLO, - hello: HELLO, - HEXISTS, - hExists: HEXISTS, - HGET, - hGet: HGET, - HGETALL, - hGetAll: HGETALL, - HINCRBY, - hIncrBy: HINCRBY, - HINCRBYFLOAT, - hIncrByFloat: HINCRBYFLOAT, - HKEYS, - hKeys: HKEYS, - HLEN, - hLen: HLEN, - HMGET, - hmGet: HMGET, - HRANDFIELD_COUNT_WITHVALUES, - hRandFieldCountWithValues: HRANDFIELD_COUNT_WITHVALUES, - HRANDFIELD_COUNT, - hRandFieldCount: HRANDFIELD_COUNT, - HRANDFIELD, - hRandField: HRANDFIELD, - HSCAN, - hScan: HSCAN, - HSET, - hSet: HSET, - HSETNX, - hSetNX: HSETNX, - HSTRLEN, - hStrLen: HSTRLEN, - HVALS, - hVals: HVALS, - INCR, - incr: INCR, - INCRBY, - incrBy: INCRBY, - INCRBYFLOAT, - incrByFloat: INCRBYFLOAT, - INFO, - info: INFO, - KEYS, - keys: KEYS, - LASTSAVE, - lastSave: LASTSAVE, - LINDEX, - lIndex: LINDEX, - LINSERT, - lInsert: LINSERT, - LLEN, - lLen: LLEN, - LMOVE, - lMove: LMOVE, - LOLWUT, - LPOP_COUNT, - lPopCount: LPOP_COUNT, - LPOP, - lPop: LPOP, - LPOS_COUNT, - lPosCount: LPOS_COUNT, - LPOS, - lPos: LPOS, - LPUSH, - lPush: LPUSH, - LPUSHX, - lPushX: LPUSHX, - LRANGE, - lRange: LRANGE, - LREM, - lRem: LREM, - LSET, - lSet: LSET, - LTRIM, - lTrim: LTRIM, - MEMOERY_DOCTOR, - memoryDoctor: MEMOERY_DOCTOR, - 'MEMORY_MALLOC-STATS': MEMORY_MALLOC_STATS, - memoryMallocStats: MEMORY_MALLOC_STATS, - MEMORY_PURGE, - memoryPurge: MEMORY_PURGE, - MEMORY_STATS, - memoryStats: MEMORY_STATS, - MEMORY_USAGE, - memoryUsage: MEMORY_USAGE, - MGET, - mGet: MGET, - MIGRATE, - migrate: MIGRATE, - MODULE_LIST, - moduleList: MODULE_LIST, - MODULE_LOAD, - moduleLoad: MODULE_LOAD, - MODULE_UNLOAD, - moduleUnload: MODULE_UNLOAD, - MOVE, - move: MOVE, - MSET, - mSet: MSET, - MSETNX, - mSetNX: MSETNX, - PERSIST, - persist: PERSIST, - PEXPIRE, - pExpire: PEXPIRE, - PEXPIREAT, - pExpireAt: PEXPIREAT, - PFADD, - pfAdd: PFADD, - PFCOUNT, - pfCount: PFCOUNT, - PFMERGE, - pfMerge: PFMERGE, - PING, - ping: PING, - PSETEX, - pSetEx: PSETEX, - PTTL, - pTTL: PTTL, - PUBLISH, - publish: PUBLISH, - PUBSUB_CHANNELS, - pubSubChannels: PUBSUB_CHANNELS, - PUBSUB_NUMPAT, - pubSubNumPat: PUBSUB_NUMPAT, - PUBSUB_NUMSUB, - pubSubNumSub: PUBSUB_NUMSUB, - RANDOMKEY, - randomKey: RANDOMKEY, - READONLY, - readonly: READONLY, - READWRITE, - readwrite: READWRITE, - RENAME, - rename: RENAME, - RENAMENX, - renameNX: RENAMENX, - REPLICAOF, - replicaOf: REPLICAOF, - 'RESTORE-ASKING': RESTORE_ASKING, - restoreAsking: RESTORE_ASKING, - ROLE, - role: ROLE, - RPOP_COUNT, - rPopCount: RPOP_COUNT, - RPOP, - rPop: RPOP, - RPOPLPUSH, - rPopLPush: RPOPLPUSH, - RPUSH, - rPush: RPUSH, - RPUSHX, - rPushX: RPUSHX, - SADD, - sAdd: SADD, - SAVE, - save: SAVE, - SCAN, - scan: SCAN, - SCARD, - sCard: SCARD, - SCRIPT_DEBUG, - scriptDebug: SCRIPT_DEBUG, - SCRIPT_EXISTS, - scriptExists: SCRIPT_EXISTS, - SCRIPT_FLUSH, - scriptFlush: SCRIPT_FLUSH, - SCRIPT_KILL, - scriptKill: SCRIPT_KILL, - SCRIPT_LOAD, - scriptLoad: SCRIPT_LOAD, - SDIFF, - sDiff: SDIFF, - SDIFFSTORE, - sDiffStore: SDIFFSTORE, - SINTER, - sInter: SINTER, - SINTERSTORE, - sInterStore: SINTERSTORE, - SET, - set: SET, - SETBIT, - setBit: SETBIT, - SETEX, - setEx: SETEX, - SETNX, - setNX: SETNX, - SETRANGE, - setRange: SETRANGE, - SHUTDOWN, - shutdown: SHUTDOWN, - SISMEMBER, - sIsMember: SISMEMBER, - SMEMBERS, - sMembers: SMEMBERS, - SMISMEMBER, - smIsMember: SMISMEMBER, - SMOVE, - sMove: SMOVE, - SORT, - sort: SORT, - SPOP, - sPop: SPOP, - SRANDMEMBER_COUNT, - sRandMemberCount: SRANDMEMBER_COUNT, - SRANDMEMBER, - sRandMember: SRANDMEMBER, - SREM, - sRem: SREM, - SSCAN, - sScan: SSCAN, - STRLEN, - strLen: STRLEN, - SUNION, - sUnion: SUNION, - SUNIONSTORE, - sUnionStore: SUNIONSTORE, - SWAPDB, - swapDb: SWAPDB, - TIME, - time: TIME, - TOUCH, - touch: TOUCH, - TTL, - ttl: TTL, - TYPE, - type: TYPE, - UNLINK, - unlink: UNLINK, - UNWATCH, - unwatch: UNWATCH, - WAIT, - wait: WAIT, - WATCH, - watch: WATCH, - XACK, - xAck: XACK, - XADD, - xAdd: XADD, - XAUTOCLAIM_JUSTID, - xAutoClaimJustId: XAUTOCLAIM_JUSTID, - XAUTOCLAIM, - xAutoClaim: XAUTOCLAIM, - XCLAIM, - xClaim: XCLAIM, - XCLAIM_JUSTID, - xClaimJustId: XCLAIM_JUSTID, - XDEL, - xDel: XDEL, - XGROUP_CREATE, - xGroupCreate: XGROUP_CREATE, - XGROUP_CREATECONSUMER, - xGroupCreateConsumer: XGROUP_CREATECONSUMER, - XGROUP_DELCONSUMER, - xGroupDelConsumer: XGROUP_DELCONSUMER, - XGROUP_DESTROY, - xGroupDestroy: XGROUP_DESTROY, - XGROUP_SETID, - xGroupSetId: XGROUP_SETID, - XINFO_CONSUMERS, - xInfoConsumers: XINFO_CONSUMERS, - XINFO_GROUPS, - xInfoGroups: XINFO_GROUPS, - XINFO_STREAM, - xInfoStream: XINFO_STREAM, - XLEN, - xLen: XLEN, - XPENDING_RANGE, - xPendingRange: XPENDING_RANGE, - XPENDING, - xPending: XPENDING, - XRANGE, - xRange: XRANGE, - XREAD, - xRead: XREAD, - XREADGROUP, - xReadGroup: XREADGROUP, - XREVRANGE, - xRevRange: XREVRANGE, - XTRIM, - xTrim: XTRIM, - ZADD, - zAdd: ZADD, - ZCARD, - zCard: ZCARD, - ZCOUNT, - zCount: ZCOUNT, - ZDIFF_WITHSCORES, - zDiffWithScores: ZDIFF_WITHSCORES, - ZDIFF, - zDiff: ZDIFF, - ZDIFFSTORE, - zDiffStore: ZDIFFSTORE, - ZINCRBY, - zIncrBy: ZINCRBY, - ZINTER_WITHSCORES, - zInterWithScores: ZINTER_WITHSCORES, - ZINTER, - zInter: ZINTER, - ZINTERSTORE, - zInterStore: ZINTERSTORE, - ZLEXCOUNT, - zLexCount: ZLEXCOUNT, - ZMSCORE, - zmScore: ZMSCORE, - ZPOPMAX_COUNT, - zPopMaxCount: ZPOPMAX_COUNT, - ZPOPMAX, - zPopMax: ZPOPMAX, - ZPOPMIN_COUNT, - zPopMinCount: ZPOPMIN_COUNT, - ZPOPMIN, - zPopMin: ZPOPMIN, - ZRANDMEMBER_COUNT_WITHSCORES, - zRandMemberCountWithScores: ZRANDMEMBER_COUNT_WITHSCORES, - ZRANDMEMBER_COUNT, - zRandMemberCount: ZRANDMEMBER_COUNT, - ZRANDMEMBER, - zRandMember: ZRANDMEMBER, - ZRANGE_WITHSCORES, - zRangeWithScores: ZRANGE_WITHSCORES, - ZRANGE, - zRange: ZRANGE, - ZRANGESTORE, - zRangeStore: ZRANGESTORE, - ZRANK, - zRank: ZRANK, - ZREM, - zRem: ZREM, - ZREMRANGEBYLEX, - zRemRangeByLex: ZREMRANGEBYLEX, - ZREMRANGEBYRANK, - zRemRangeByRank: ZREMRANGEBYRANK, - ZREMRANGEBYSCORE, - zRemRangeByScore: ZREMRANGEBYSCORE, - ZREVRANK, - zRevRank: ZREVRANK, - ZSCAN, - zScan: ZSCAN, - ZSCORE, - zScore: ZSCORE, - ZUNION_WITHSCORES, - zUnionWithScores: ZUNION_WITHSCORES, - ZUNION, - zUnion: ZUNION, - ZUNIONSTORE, - zUnionStore: ZUNIONSTORE -}; +export type RedisCommandRawReply = string | number | Buffer | Array | null | undefined; -export type RedisReply = string | number | Buffer | Array | null | undefined; - -export type TransformArgumentsReply = Array & { preserve?: unknown }; +export type RedisCommandArguments = Array & { preserve?: unknown }; export interface RedisCommand { FIRST_KEY_INDEX?: number | ((...args: Array) => string); IS_READ_ONLY?: boolean; - transformArguments(this: void, ...args: Array): TransformArgumentsReply; + transformArguments(this: void, ...args: Array): RedisCommandArguments; BUFFER_MODE?: boolean; - transformReply(this: void, reply: RedisReply, preserved?: unknown): any; + transformReply?(this: void, reply: RedisCommandRawReply, preserved?: unknown): any; } +export type RedisCommandReply = C['transformReply'] extends (...args: any) => infer T ? T : RedisCommandRawReply; + export interface RedisCommands { [command: string]: RedisCommand; } @@ -762,4 +25,14 @@ export interface RedisModule { export interface RedisModules { [module: string]: RedisModule; } -// export type RedisModules = Record; + +export type RedisScript = RedisScriptConfig & SHA1; + +export interface RedisScripts { + [script: string]: RedisScript; +} + +export interface RedisPlugins { + modules?: M; + scripts?: S; +} diff --git a/lib/lua-script.ts b/lib/lua-script.ts index be16f9b9133..3089d468d65 100644 --- a/lib/lua-script.ts +++ b/lib/lua-script.ts @@ -1,7 +1,7 @@ import { createHash } from 'crypto'; import { RedisCommand } from './commands'; -export interface RedisLuaScriptConfig extends RedisCommand { +export interface RedisScriptConfig extends RedisCommand { SCRIPT: string; NUMBER_OF_KEYS: number; } @@ -10,13 +10,7 @@ export interface SHA1 { SHA1: string; } -export type RedisLuaScript = RedisLuaScriptConfig & SHA1; - -export interface RedisLuaScripts { - [script: string]: RedisLuaScript; -} - -export function defineScript(script: RedisLuaScriptConfig): typeof script & SHA1 { +export function defineScript(script: RedisScriptConfig): typeof script & SHA1 { return { ...script, SHA1: scriptSha1(script.SCRIPT) diff --git a/lib/multi-command.spec.ts b/lib/multi-command.spec.ts index 52ecfb94b1c..7e9667cd518 100644 --- a/lib/multi-command.spec.ts +++ b/lib/multi-command.spec.ts @@ -1,126 +1,97 @@ import { strict as assert } from 'assert'; import RedisMultiCommand from './multi-command'; import { WatchError } from './errors'; -import { spy } from 'sinon'; -import { SQUARE_SCRIPT } from './client.spec'; +import { SQUARE_SCRIPT } from './client/index.spec'; describe('Multi Command', () => { - describe('exec', () => { - it('simple', async () => { - const multi = RedisMultiCommand.create((queue, symbol) => { - assert.deepEqual( - queue.map(({ args }) => args), - [ - ['MULTI'], - ['PING'], - ['EXEC'], - ] - ); + it('generateChainId', () => { + assert.equal( + typeof RedisMultiCommand.generateChainId(), + 'symbol' + ); + }); - assert.equal( - typeof symbol, - 'symbol' - ); + it('addCommand', () => { + const multi = new RedisMultiCommand(); + multi.addCommand(['PING']); - return Promise.resolve(['QUEUED', 'QUEUED', ['PONG']]); - }); + assert.deepEqual( + multi.queue[0].args, + ['PING'] + ); + }); - multi.ping(); + it('addScript', () => { + const multi = new RedisMultiCommand(); + + multi.addScript(SQUARE_SCRIPT, ['1']); + assert.equal( + multi.scriptsInUse.has(SQUARE_SCRIPT.SHA1), + true + ); + assert.deepEqual( + multi.queue[0].args, + ['EVAL', SQUARE_SCRIPT.SCRIPT, '0', '1'] + ); + + multi.addScript(SQUARE_SCRIPT, ['2']); + assert.equal( + multi.scriptsInUse.has(SQUARE_SCRIPT.SHA1), + true + ); + assert.deepEqual( + multi.queue[1].args, + ['EVALSHA', SQUARE_SCRIPT.SHA1, '0', '2'] + ); + }); - assert.deepEqual( - await multi.exec(), - ['PONG'] + describe('exec', () => { + it('undefined', () => { + assert.equal( + new RedisMultiCommand().exec(), + undefined ); }); - it('executing an empty queue should resolve without executing on the server', async () => { - const executor = spy(); + it('Array', () => { + const multi = new RedisMultiCommand(); + multi.addCommand(['PING']); assert.deepEqual( - await RedisMultiCommand.create(executor).exec(), - [] + multi.exec(), + [ + { args: ['MULTI'] }, + { args: ['PING'], transformReply: undefined }, + { args: ['EXEC'] } + ] ); - - assert.ok(executor.notCalled); }); + }); + describe('handleExecReplies', () => { it('WatchError', () => { - return assert.rejects( - RedisMultiCommand.create(() => Promise.resolve([null])).ping().exec(), + assert.throws( + () => new RedisMultiCommand().handleExecReplies([null]), WatchError ); }); - it('execAsPipeline', async () => { - const multi = RedisMultiCommand.create(queue => { - assert.deepEqual( - queue.map(({ args }) => args), - [['PING']] - ); - - return Promise.resolve(['PONG']); - }); - - multi.ping(); - + it('with replies', () => { + const multi = new RedisMultiCommand(); + multi.addCommand(['PING']); assert.deepEqual( - await multi.exec(true), + multi.handleExecReplies(['OK', 'QUEUED', ['PONG']]), ['PONG'] ); }); }); - describe('execAsPipeline', () => { - it('simple', async () => { - const multi = RedisMultiCommand.create(queue => { - assert.deepEqual( - queue.map(({ args }) => args), - [['PING']] - ); - - return Promise.resolve(['PONG']); - }); - - multi.ping(); - - assert.deepEqual( - await multi.execAsPipeline(), - ['PONG'] - ); - }); - - it('executing an empty queue should resolve without executing on the server', async () => { - const executor = spy(); - - assert.deepEqual( - await RedisMultiCommand.create(executor).execAsPipeline(), - [] - ); - - assert.ok(executor.notCalled); - }); - - it('with scripts', async () => { - const MultiWithScript = RedisMultiCommand.extend({ - scripts: { - square: SQUARE_SCRIPT - } - }); - - assert.deepEqual( - await new MultiWithScript(queue => { - assert.deepEqual( - queue.map(({ args }) => args), - [ - ['EVAL', SQUARE_SCRIPT.SCRIPT, '0', '2'], - ['EVALSHA', SQUARE_SCRIPT.SHA1, '0', '3'], - ] - ); - - return Promise.resolve([4, 9]); - }).square(2).square(3).execAsPipeline(), - [4, 9] - ); - }); + it('transformReplies', () => { + const multi = new RedisMultiCommand(); + multi.addCommand(['PING'], (reply: string) => reply.substring(0, 2)); + assert.deepEqual( + multi.transformReplies(['PONG']), + ['PO'] + ); }); }); diff --git a/lib/multi-command.ts b/lib/multi-command.ts index a329a5dbf19..d66974a5a21 100644 --- a/lib/multi-command.ts +++ b/lib/multi-command.ts @@ -1,135 +1,36 @@ -import COMMANDS, { TransformArgumentsReply } from './commands'; -import { RedisCommand, RedisModules, RedisReply } from './commands'; -import { RedisLuaScript, RedisLuaScripts } from './lua-script'; -import { RedisClientOptions } from './client'; -import { extendWithModulesAndScripts, extendWithDefaultCommands } from './commander'; +import { RedisCommand, RedisCommandArguments, RedisCommandRawReply, RedisScript } from './commands'; import { WatchError } from './errors'; -type RedisMultiCommandSignature = (...args: Parameters) => RedisMultiCommandType; - -type WithCommands = { - [P in keyof typeof COMMANDS]: RedisMultiCommandSignature<(typeof COMMANDS)[P], M, S> -}; - -type WithModules = { - [P in keyof M]: { - [C in keyof M[P]]: RedisMultiCommandSignature; - }; -}; - -type WithScripts = { - [P in keyof S]: RedisMultiCommandSignature -}; - -export type RedisMultiCommandType = - RedisMultiCommand & WithCommands & WithModules & WithScripts; - -export interface MultiQueuedCommand { - args: TransformArgumentsReply; - preservedArguments?: unknown; +export interface RedisMultiQueuedCommand { + args: RedisCommandArguments; transformReply?: RedisCommand['transformReply']; } -export type RedisMultiExecutor = (queue: Array, chainId?: symbol) => Promise>; - -export default class RedisMultiCommand { - static extend( - clientOptions?: RedisClientOptions - ): new (...args: ConstructorParameters) => RedisMultiCommandType { - return extendWithModulesAndScripts({ - BaseClass: RedisMultiCommand, - modules: clientOptions?.modules, - modulesCommandsExecutor: RedisMultiCommand.prototype.commandsExecutor, - scripts: clientOptions?.scripts, - scriptsExecutor: RedisMultiCommand.prototype.scriptsExecutor - }); - } - - static create( - executor: RedisMultiExecutor, - clientOptions?: RedisClientOptions - ): RedisMultiCommandType { - return new this(executor, clientOptions); - } - - readonly #executor: RedisMultiExecutor; - - readonly #clientOptions: RedisClientOptions | undefined; - - readonly #queue: Array = []; - - readonly #scriptsInUse = new Set(); - - readonly #v4: Record = {}; - - get v4(): Record { - if (!this.#clientOptions?.legacyMode) { - throw new Error('client is not in "legacy mode"'); - } - - return this.#v4; - } - - constructor(executor: RedisMultiExecutor, clientOptions?: RedisClientOptions) { - this.#executor = executor; - this.#clientOptions = clientOptions; - this.#legacyMode(); +export default class RedisMultiCommand { + static generateChainId(): symbol { + return Symbol('RedisMultiCommand Chain Id'); } - #legacyMode(): void { - if (!this.#clientOptions?.legacyMode) return; - - this.#v4.addCommand = this.addCommand.bind(this); - (this as any).addCommand = (...args: Array): this => { - this.#queue.push({ - args: args.flat() as Array - }); - return this; - } - this.#v4.exec = this.exec.bind(this); - (this as any).exec = (callback?: (err: Error | null, replies?: Array) => unknown): void => { - this.#v4.exec() - .then((reply: Array) => { - if (!callback) return; - - callback(null, reply); - }) - .catch((err: Error) => { - if (!callback) { - // this.emit('error', err); - return; - } - - callback(err); - }); - }; - - for (const name of Object.keys(COMMANDS)) { - this.#defineLegacyCommand(name); - } - } + readonly queue: Array = []; - #defineLegacyCommand(name: string): void { - (this as any).#v4[name] = (this as any)[name].bind(this.#v4); - (this as any)[name] = (...args: Array): void => (this as any).addCommand(name, args); - } + readonly scriptsInUse = new Set(); - commandsExecutor(command: RedisCommand, args: Array): this { - return this.addCommand( - command.transformArguments(...args), - command.transformReply - ); + addCommand(args: RedisCommandArguments, transformReply?: RedisCommand['transformReply']): void { + this.queue.push({ + args, + transformReply + }); } - scriptsExecutor(script: RedisLuaScript, args: Array): this { - const transformedArguments: TransformArgumentsReply = []; - if (this.#scriptsInUse.has(script.SHA1)) { + addScript(script: RedisScript, args: Array): RedisCommandArguments { + const transformedArguments: RedisCommandArguments = []; + if (this.scriptsInUse.has(script.SHA1)) { transformedArguments.push( 'EVALSHA', script.SHA1 ); } else { - this.#scriptsInUse.add(script.SHA1); + this.scriptsInUse.add(script.SHA1); transformedArguments.push( 'EVAL', script.SCRIPT @@ -144,62 +45,39 @@ export default class RedisMultiCommand> { - if (execAsPipeline) { - return this.execAsPipeline(); - } else if (!this.#queue.length) { - return []; + exec(): undefined | Array { + if (!this.queue.length) { + return; } - const queue = this.#queue.splice(0), - rawReplies = await this.#executor([ - { args: ['MULTI'] }, - ...queue, - { args: ['EXEC'] } - ], Symbol('[RedisMultiCommand] Chain ID')), - execReply = rawReplies[rawReplies.length - 1] as (null | Array); + return [ + { args: ['MULTI'] }, + ...this.queue, + { args: ['EXEC'] } + ]; + } + handleExecReplies(rawReplies: Array): Array { + const execReply = rawReplies[rawReplies.length - 1] as (null | Array); if (execReply === null) { throw new WatchError(); } - return this.#transformReplies(execReply, queue); + return this.transformReplies(execReply); } - async execAsPipeline(): Promise> { - if (!this.#queue.length) { - return []; - } - - const queue = this.#queue.splice(0); - return this.#transformReplies( - await this.#executor(queue), - queue - ); - } - - #transformReplies(rawReplies: Array, queue: Array): Array { + transformReplies(rawReplies: Array): Array { return rawReplies.map((reply, i) => { - const { transformReply, preservedArguments } = queue[i]; - return transformReply ? transformReply(reply, preservedArguments) : reply; + const { transformReply, args } = this.queue[i]; + return transformReply ? transformReply(reply, args.preserve) : reply; }); } } - -extendWithDefaultCommands(RedisMultiCommand, RedisMultiCommand.prototype.commandsExecutor); diff --git a/lib/test-utils.ts b/lib/test-utils.ts index 713a1a3434a..978940ff93d 100644 --- a/lib/test-utils.ts +++ b/lib/test-utils.ts @@ -2,15 +2,13 @@ import { strict as assert } from 'assert'; import RedisClient, { RedisClientOptions, RedisClientType } from './client'; import { execSync, spawn } from 'child_process'; import { once } from 'events'; -import { RedisSocketOptions } from './socket'; import which from 'which'; import { SinonSpy } from 'sinon'; -import RedisCluster, { RedisClusterType } from './cluster'; +import RedisCluster, { RedisClusterOptions, RedisClusterType } from './cluster'; import { promises as fs } from 'fs'; import { Context as MochaContext } from 'mocha'; import { promiseTimeout } from './utils'; -import { RedisModules } from './commands'; -import { RedisLuaScripts } from './lua-script'; +import { RedisModules, RedisScripts } from './commands'; type RedisVersion = [major: number, minor: number, patch: number]; @@ -54,13 +52,13 @@ export enum TestRedisServers { PASSWORD } -export const TEST_REDIS_SERVERS: Record> = {}; +export const TEST_REDIS_SERVERS: Record> = {}; export enum TestRedisClusters { OPEN } -export const TEST_REDIS_CLUSTERES: Record> = {}; +export const TEST_REDIS_CLUSTERES: Record> = {}; let port = 6379; @@ -248,9 +246,13 @@ async function spawnPasswordServer(): Promise { } async function spawnOpenCluster(): Promise { - TEST_REDIS_CLUSTERES[TestRedisClusters.OPEN] = (await spawnGlobalRedisCluster(TestRedisClusters.OPEN, 3)).map(port => ({ - port - })); + TEST_REDIS_CLUSTERES[TestRedisClusters.OPEN] = { + rootNodes: (await spawnGlobalRedisCluster(TestRedisClusters.OPEN, 3)).map(port => ({ + socket: { + port + } + })) + }; } before(function () { @@ -314,9 +316,7 @@ export function itWithCluster( it(title, async function () { if (handleMinimumRedisVersion(this, options?.minimumRedisVersion)) return; - const cluster = RedisCluster.create({ - rootNodes: TEST_REDIS_CLUSTERES[type] - }); + const cluster = RedisCluster.create(TEST_REDIS_CLUSTERES[type]); await cluster.connect(); @@ -337,7 +337,9 @@ export function itWithDedicatedCluster(title: string, fn: (cluster: RedisCluster const spawnResults = await spawnRedisCluster(null, 3), cluster = RedisCluster.create({ rootNodes: [{ - port: spawnResults[0].port + socket: { + port: spawnResults[0].port + } }] }); diff --git a/package-lock.json b/package-lock.json index 9fcd62b5996..f47208d6c9d 100644 --- a/package-lock.json +++ b/package-lock.json @@ -1,12 +1,12 @@ { "name": "redis", - "version": "4.0.0-rc.2", + "version": "4.0.0-rc.3", "lockfileVersion": 2, "requires": true, "packages": { "": { "name": "redis", - "version": "4.0.0-rc.2", + "version": "4.0.0-rc.3", "license": "MIT", "dependencies": { "cluster-key-slot": "1.1.0", @@ -16,20 +16,21 @@ }, "devDependencies": { "@istanbuljs/nyc-config-typescript": "^1.0.1", + "@tsconfig/node12": "^1.0.9", "@types/mocha": "^9.0.0", - "@types/node": "^16.9.6", - "@types/sinon": "^10.0.3", + "@types/node": "^16.10.3", + "@types/sinon": "^10.0.4", "@types/which": "^2.0.1", "@types/yallist": "^4.0.1", - "mocha": "^9.1.1", + "mocha": "^9.1.2", "nyc": "^15.1.0", "release-it": "^14.11.6", "sinon": "^11.1.2", "source-map-support": "^0.5.20", - "ts-node": "^10.2.1", - "typedoc": "0.21.9", - "typedoc-github-wiki-theme": "^0.5.1", - "typedoc-plugin-markdown": "3.10.4", + "ts-node": "^10.3.0", + "typedoc": "^0.22.5", + "typedoc-github-wiki-theme": "^0.6.0", + "typedoc-plugin-markdown": "^3.11.3", "typescript": "^4.4.3", "which": "^2.0.2" }, @@ -38,9 +39,9 @@ } }, "node_modules/@babel/code-frame": { - "version": "7.14.5", - "resolved": "https://registry.npmjs.org/@babel/code-frame/-/code-frame-7.14.5.tgz", - "integrity": "sha512-9pzDqyc6OLDaqe+zbACgFkb6fKMNG6CObKpnYXChRsvYGyEdc7CA2BaqeOM+vOtCS5ndmJicPJhKAwYRI6UfFw==", + "version": "7.15.8", + "resolved": "https://registry.npmjs.org/@babel/code-frame/-/code-frame-7.15.8.tgz", + "integrity": "sha512-2IAnmn8zbvC/jKYhq5Ki9I+DwjlrtMPUCH/CpHvqI4dNnlwHwsxoIhlc8WcYY5LSYknXQtAlFYuHfqAFCvQ4Wg==", "dev": true, "dependencies": { "@babel/highlight": "^7.14.5" @@ -59,20 +60,20 @@ } }, "node_modules/@babel/core": { - "version": "7.15.5", - "resolved": "https://registry.npmjs.org/@babel/core/-/core-7.15.5.tgz", - "integrity": "sha512-pYgXxiwAgQpgM1bNkZsDEq85f0ggXMA5L7c+o3tskGMh2BunCI9QUwB9Z4jpvXUOuMdyGKiGKQiRe11VS6Jzvg==", + "version": "7.15.8", + "resolved": "https://registry.npmjs.org/@babel/core/-/core-7.15.8.tgz", + "integrity": "sha512-3UG9dsxvYBMYwRv+gS41WKHno4K60/9GPy1CJaH6xy3Elq8CTtvtjT5R5jmNhXfCYLX2mTw+7/aq5ak/gOE0og==", "dev": true, "dependencies": { - "@babel/code-frame": "^7.14.5", - "@babel/generator": "^7.15.4", + "@babel/code-frame": "^7.15.8", + "@babel/generator": "^7.15.8", "@babel/helper-compilation-targets": "^7.15.4", - "@babel/helper-module-transforms": "^7.15.4", + "@babel/helper-module-transforms": "^7.15.8", "@babel/helpers": "^7.15.4", - "@babel/parser": "^7.15.5", + "@babel/parser": "^7.15.8", "@babel/template": "^7.15.4", "@babel/traverse": "^7.15.4", - "@babel/types": "^7.15.4", + "@babel/types": "^7.15.6", "convert-source-map": "^1.7.0", "debug": "^4.1.0", "gensync": "^1.0.0-beta.2", @@ -89,12 +90,12 @@ } }, "node_modules/@babel/generator": { - "version": "7.15.4", - "resolved": "https://registry.npmjs.org/@babel/generator/-/generator-7.15.4.tgz", - "integrity": "sha512-d3itta0tu+UayjEORPNz6e1T3FtvWlP5N4V5M+lhp/CxT4oAA7/NcScnpRyspUMLK6tu9MNHmQHxRykuN2R7hw==", + "version": "7.15.8", + "resolved": "https://registry.npmjs.org/@babel/generator/-/generator-7.15.8.tgz", + "integrity": "sha512-ECmAKstXbp1cvpTTZciZCgfOt6iN64lR0d+euv3UZisU5awfRawOvg07Utn/qBGuH4bRIEZKrA/4LzZyXhZr8g==", "dev": true, "dependencies": { - "@babel/types": "^7.15.4", + "@babel/types": "^7.15.6", "jsesc": "^2.5.1", "source-map": "^0.5.0" }, @@ -183,9 +184,9 @@ } }, "node_modules/@babel/helper-module-transforms": { - "version": "7.15.7", - "resolved": "https://registry.npmjs.org/@babel/helper-module-transforms/-/helper-module-transforms-7.15.7.tgz", - "integrity": "sha512-ZNqjjQG/AuFfekFTY+7nY4RgBSklgTu970c7Rj3m/JOhIu5KPBUuTA9AY6zaKcUvk4g6EbDXdBnhi35FAssdSw==", + "version": "7.15.8", + "resolved": "https://registry.npmjs.org/@babel/helper-module-transforms/-/helper-module-transforms-7.15.8.tgz", + "integrity": "sha512-DfAfA6PfpG8t4S6npwzLvTUpp0sS7JrcuaMiy1Y5645laRJIp/LiLGIBbQKaXSInK8tiGNI7FL7L8UvB8gdUZg==", "dev": true, "dependencies": { "@babel/helper-module-imports": "^7.15.4", @@ -370,9 +371,9 @@ } }, "node_modules/@babel/parser": { - "version": "7.15.7", - "resolved": "https://registry.npmjs.org/@babel/parser/-/parser-7.15.7.tgz", - "integrity": "sha512-rycZXvQ+xS9QyIcJ9HXeDWf1uxqlbVFAUq0Rq0dbc50Zb/+wUe/ehyfzGfm9KZZF0kBejYgxltBXocP+gKdL2g==", + "version": "7.15.8", + "resolved": "https://registry.npmjs.org/@babel/parser/-/parser-7.15.8.tgz", + "integrity": "sha512-BRYa3wcQnjS/nqI8Ac94pYYpJfojHVvVXJ97+IDCImX4Jc8W8Xv1+47enbruk+q1etOpsQNwnfFcNGw+gtPGxA==", "dev": true, "bin": { "parser": "bin/babel-parser.js" @@ -438,9 +439,9 @@ } }, "node_modules/@cspotcode/source-map-support": { - "version": "0.6.1", - "resolved": "https://registry.npmjs.org/@cspotcode/source-map-support/-/source-map-support-0.6.1.tgz", - "integrity": "sha512-DX3Z+T5dt1ockmPdobJS/FAsQPW4V4SrWEhD2iYQT2Cb2tQsiMnYxrcUH9By/Z3B+v0S5LMBkQtV/XOBbpLEOg==", + "version": "0.7.0", + "resolved": "https://registry.npmjs.org/@cspotcode/source-map-support/-/source-map-support-0.7.0.tgz", + "integrity": "sha512-X4xqRHqN8ACt2aHVe51OxeA2HjbcL4MqFqXkrmQszJ1NOUuUu5u6Vqx/0lZSVNku7velL5FC/s5uEAj1lsBMhA==", "dev": true, "dependencies": { "@cspotcode/source-map-consumer": "0.8.0" @@ -653,18 +654,18 @@ } }, "node_modules/@octokit/openapi-types": { - "version": "10.0.0", - "resolved": "https://registry.npmjs.org/@octokit/openapi-types/-/openapi-types-10.0.0.tgz", - "integrity": "sha512-k1iO2zKuEjjRS1EJb4FwSLk+iF6EGp+ZV0OMRViQoWhQ1fZTk9hg1xccZII5uyYoiqcbC73MRBmT45y1vp2PPg==", + "version": "11.2.0", + "resolved": "https://registry.npmjs.org/@octokit/openapi-types/-/openapi-types-11.2.0.tgz", + "integrity": "sha512-PBsVO+15KSlGmiI8QAzaqvsNlZlrDlyAJYcrXBCvVUxCp7VnXjkwPoFHgjEJXx3WF9BAwkA6nfCUA7i9sODzKA==", "dev": true }, "node_modules/@octokit/plugin-paginate-rest": { - "version": "2.16.0", - "resolved": "https://registry.npmjs.org/@octokit/plugin-paginate-rest/-/plugin-paginate-rest-2.16.0.tgz", - "integrity": "sha512-8YYzALPMvEZ35kgy5pdYvQ22Roz+BIuEaedO575GwE2vb/ACDqQn0xQrTJR4tnZCJn7pi8+AWPVjrFDaERIyXQ==", + "version": "2.17.0", + "resolved": "https://registry.npmjs.org/@octokit/plugin-paginate-rest/-/plugin-paginate-rest-2.17.0.tgz", + "integrity": "sha512-tzMbrbnam2Mt4AhuyCHvpRkS0oZ5MvwwcQPYGtMv4tUa5kkzG58SVB0fcsLulOZQeRnOgdkZWkRUiyBlh0Bkyw==", "dev": true, "dependencies": { - "@octokit/types": "^6.26.0" + "@octokit/types": "^6.34.0" }, "peerDependencies": { "@octokit/core": ">=2" @@ -680,12 +681,12 @@ } }, "node_modules/@octokit/plugin-rest-endpoint-methods": { - "version": "5.10.4", - "resolved": "https://registry.npmjs.org/@octokit/plugin-rest-endpoint-methods/-/plugin-rest-endpoint-methods-5.10.4.tgz", - "integrity": "sha512-Dh+EAMCYR9RUHwQChH94Skl0lM8Fh99auT8ggck/xTzjJrwVzvsd0YH68oRPqp/HxICzmUjLfaQ9sy1o1sfIiA==", + "version": "5.13.0", + "resolved": "https://registry.npmjs.org/@octokit/plugin-rest-endpoint-methods/-/plugin-rest-endpoint-methods-5.13.0.tgz", + "integrity": "sha512-uJjMTkN1KaOIgNtUPMtIXDOjx6dGYysdIFhgA52x4xSadQCz3b/zJexvITDVpANnfKPW/+E0xkOvLntqMYpviA==", "dev": true, "dependencies": { - "@octokit/types": "^6.28.1", + "@octokit/types": "^6.34.0", "deprecation": "^2.3.1" }, "peerDependencies": { @@ -693,9 +694,9 @@ } }, "node_modules/@octokit/request": { - "version": "5.6.1", - "resolved": "https://registry.npmjs.org/@octokit/request/-/request-5.6.1.tgz", - "integrity": "sha512-Ls2cfs1OfXaOKzkcxnqw5MR6drMA/zWX/LIS/p8Yjdz7QKTPQLMsB3R+OvoxE6XnXeXEE2X7xe4G4l4X0gRiKQ==", + "version": "5.6.2", + "resolved": "https://registry.npmjs.org/@octokit/request/-/request-5.6.2.tgz", + "integrity": "sha512-je66CvSEVf0jCpRISxkUcCa0UkxmFs6eGDRSbfJtAVwbLH5ceqF+YEyC8lj8ystKyZTy8adWr0qmkY52EfOeLA==", "dev": true, "dependencies": { "@octokit/endpoint": "^6.0.1", @@ -730,12 +731,12 @@ } }, "node_modules/@octokit/types": { - "version": "6.26.0", - "resolved": "https://registry.npmjs.org/@octokit/types/-/types-6.26.0.tgz", - "integrity": "sha512-RDxZBAFMtqs1ZPnbUu1e7ohPNfoNhTiep4fErY7tZs995BeHu369Vsh5woMIaFbllRWEZBfvTCS4hvDnMPiHrA==", + "version": "6.34.0", + "resolved": "https://registry.npmjs.org/@octokit/types/-/types-6.34.0.tgz", + "integrity": "sha512-s1zLBjWhdEI2zwaoSgyOFoKSl109CUcVBCc7biPJ3aAf6LGLU6szDvi31JPU7bxfla2lqfhjbbg/5DdFNxOwHw==", "dev": true, "dependencies": { - "@octokit/openapi-types": "^10.0.0" + "@octokit/openapi-types": "^11.2.0" } }, "node_modules/@sindresorhus/is": { @@ -855,9 +856,9 @@ "dev": true }, "node_modules/@types/node": { - "version": "16.7.10", - "resolved": "https://registry.npmjs.org/@types/node/-/node-16.7.10.tgz", - "integrity": "sha512-S63Dlv4zIPb8x6MMTgDq5WWRJQe56iBEY0O3SOFA9JrRienkOVDXSXBjjJw6HTNQYSE2JI6GMCR6LVbIMHJVvA==", + "version": "16.10.3", + "resolved": "https://registry.npmjs.org/@types/node/-/node-16.10.3.tgz", + "integrity": "sha512-ho3Ruq+fFnBrZhUYI46n/bV2GjwzSkwuT4dTf0GkuNFmnb8nq4ny2z9JEVemFi6bdEJanHLlYfy9c6FN9B9McQ==", "dev": true }, "node_modules/@types/parse-json": { @@ -876,9 +877,9 @@ } }, "node_modules/@types/sinon": { - "version": "10.0.3", - "resolved": "https://registry.npmjs.org/@types/sinon/-/sinon-10.0.3.tgz", - "integrity": "sha512-XUaFuUOQ3A/r6gS1qCU/USMleascaqGeQpGR1AZ5JdRtBPlzijRzKsik1TuGzvdtPA0mdq42JqaJmJ+Afg1LJg==", + "version": "10.0.4", + "resolved": "https://registry.npmjs.org/@types/sinon/-/sinon-10.0.4.tgz", + "integrity": "sha512-fOYjrxQv8zJsqOY6V6ecP4eZhQBxtY80X0er1VVnUIAIZo74jHm8e1vguG5Yt4Iv8W2Wr7TgibB8MfRe32k9pA==", "dev": true, "dependencies": { "@sinonjs/fake-timers": "^7.1.0" @@ -937,62 +938,12 @@ } }, "node_modules/ansi-align": { - "version": "3.0.0", - "resolved": "https://registry.npmjs.org/ansi-align/-/ansi-align-3.0.0.tgz", - "integrity": "sha512-ZpClVKqXN3RGBmKibdfWzqCY4lnjEuoNzU5T0oEFpfd/z5qJHVarukridD4juLO2FXMiwUQxr9WqQtaYa8XRYw==", - "dev": true, - "dependencies": { - "string-width": "^3.0.0" - } - }, - "node_modules/ansi-align/node_modules/ansi-regex": { - "version": "4.1.0", - "resolved": "https://registry.npmjs.org/ansi-regex/-/ansi-regex-4.1.0.tgz", - "integrity": "sha512-1apePfXM1UOSqw0o9IiFAovVz9M5S1Dg+4TrDwfMewQ6p/rmMueb7tWZjQ1rx4Loy1ArBggoqGpfqqdI4rondg==", - "dev": true, - "engines": { - "node": ">=6" - } - }, - "node_modules/ansi-align/node_modules/emoji-regex": { - "version": "7.0.3", - "resolved": "https://registry.npmjs.org/emoji-regex/-/emoji-regex-7.0.3.tgz", - "integrity": "sha512-CwBLREIQ7LvYFB0WyRvwhq5N5qPhc6PMjD6bYggFlI5YyDgl+0vxq5VHbMOFqLg7hfWzmu8T5Z1QofhmTIhItA==", - "dev": true - }, - "node_modules/ansi-align/node_modules/is-fullwidth-code-point": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/is-fullwidth-code-point/-/is-fullwidth-code-point-2.0.0.tgz", - "integrity": "sha1-o7MKXE8ZkYMWeqq5O+764937ZU8=", - "dev": true, - "engines": { - "node": ">=4" - } - }, - "node_modules/ansi-align/node_modules/string-width": { - "version": "3.1.0", - "resolved": "https://registry.npmjs.org/string-width/-/string-width-3.1.0.tgz", - "integrity": "sha512-vafcv6KjVZKSgz06oM/H6GDBrAtz8vdhQakGjFIvNrHA6y3HCF1CInLy+QLq8dTJPQ1b+KDUqDFctkdRW44e1w==", - "dev": true, - "dependencies": { - "emoji-regex": "^7.0.1", - "is-fullwidth-code-point": "^2.0.0", - "strip-ansi": "^5.1.0" - }, - "engines": { - "node": ">=6" - } - }, - "node_modules/ansi-align/node_modules/strip-ansi": { - "version": "5.2.0", - "resolved": "https://registry.npmjs.org/strip-ansi/-/strip-ansi-5.2.0.tgz", - "integrity": "sha512-DuRs1gKbBqsMKIZlrffwlug8MHkcnpjs5VPmL1PAh+mA30U0DTotfDZ0d2UUsXpPmPmMMJ6W773MaA3J+lbiWA==", + "version": "3.0.1", + "resolved": "https://registry.npmjs.org/ansi-align/-/ansi-align-3.0.1.tgz", + "integrity": "sha512-IOfwwBF5iczOjp/WeY4YxyjqAFMQoZufdQWDd19SEExbVLNXqvpzSJ/M7Za4/sCPmQ0+GRquoA7bGcINcxew6w==", "dev": true, "dependencies": { - "ansi-regex": "^4.1.0" - }, - "engines": { - "node": ">=6" + "string-width": "^4.1.0" } }, "node_modules/ansi-colors": { @@ -1249,16 +1200,16 @@ "dev": true }, "node_modules/browserslist": { - "version": "4.17.0", - "resolved": "https://registry.npmjs.org/browserslist/-/browserslist-4.17.0.tgz", - "integrity": "sha512-g2BJ2a0nEYvEFQC208q8mVAhfNwpZ5Mu8BwgtCdZKO3qx98HChmeg448fPdUzld8aFmfLgVh7yymqV+q1lJZ5g==", + "version": "4.17.3", + "resolved": "https://registry.npmjs.org/browserslist/-/browserslist-4.17.3.tgz", + "integrity": "sha512-59IqHJV5VGdcJZ+GZ2hU5n4Kv3YiASzW6Xk5g9tf5a/MAzGeFwgGWU39fVzNIOVcgB3+Gp+kiQu0HEfTVU/3VQ==", "dev": true, "dependencies": { - "caniuse-lite": "^1.0.30001254", - "colorette": "^1.3.0", - "electron-to-chromium": "^1.3.830", + "caniuse-lite": "^1.0.30001264", + "electron-to-chromium": "^1.3.857", "escalade": "^3.1.1", - "node-releases": "^1.1.75" + "node-releases": "^1.1.77", + "picocolors": "^0.2.1" }, "bin": { "browserslist": "cli.js" @@ -1390,9 +1341,9 @@ } }, "node_modules/caniuse-lite": { - "version": "1.0.30001259", - "resolved": "https://registry.npmjs.org/caniuse-lite/-/caniuse-lite-1.0.30001259.tgz", - "integrity": "sha512-V7mQTFhjITxuk9zBpI6nYsiTXhcPe05l+364nZjK7MFK/E7ibvYBSAXr4YcA6oPR8j3ZLM/LN+lUqUVAQEUZFg==", + "version": "1.0.30001265", + "resolved": "https://registry.npmjs.org/caniuse-lite/-/caniuse-lite-1.0.30001265.tgz", + "integrity": "sha512-YzBnspggWV5hep1m9Z6sZVLOt7vrju8xWooFAgN6BA5qvy98qPAPb7vNUzypFaoh2pb3vlfzbDO8tB57UPGbtw==", "dev": true, "funding": { "type": "opencollective", @@ -1494,9 +1445,9 @@ } }, "node_modules/cli-spinners": { - "version": "2.6.0", - "resolved": "https://registry.npmjs.org/cli-spinners/-/cli-spinners-2.6.0.tgz", - "integrity": "sha512-t+4/y50K/+4xcCRosKkA7W4gTr1MySvLV0q+PxmG7FJ5g+66ChKurYjxBCjHggHH3HA5Hh9cy+lcUGWDqVH+4Q==", + "version": "2.6.1", + "resolved": "https://registry.npmjs.org/cli-spinners/-/cli-spinners-2.6.1.tgz", + "integrity": "sha512-x/5fWmGMnbKQAaNwN+UZlV79qBLM9JFnJuJ03gIi5whrob0xV0ofNVHy9DhwGdsMJQc2OKv0oGmLzvaqvAVv+g==", "dev": true, "engines": { "node": ">=6" @@ -1569,12 +1520,6 @@ "integrity": "sha512-dOy+3AuW3a2wNbZHIuMZpTcgjGuLU/uBL/ubcZF9OXbDo8ff4O8yVp5Bf0efS8uEoYo5q4Fx7dY9OgQGXgAsQA==", "dev": true }, - "node_modules/colorette": { - "version": "1.4.0", - "resolved": "https://registry.npmjs.org/colorette/-/colorette-1.4.0.tgz", - "integrity": "sha512-Y2oEozpomLn7Q3HFP7dpww7AtMJplbM9lGZP6RDfHqmbeRjiwRg4n6VM6j4KLmRke85uWEI7JqF17f3pqdRA0g==", - "dev": true - }, "node_modules/combined-stream": { "version": "1.0.8", "resolved": "https://registry.npmjs.org/combined-stream/-/combined-stream-1.0.8.tgz", @@ -1671,9 +1616,9 @@ } }, "node_modules/debug": { - "version": "4.3.1", - "resolved": "https://registry.npmjs.org/debug/-/debug-4.3.1.tgz", - "integrity": "sha512-doEwdvm4PCeK4K3RQN2ZC2BYUBaxwLARCqZmMjtF8a51J2Rb0xpVloFRnCODwqjpwnAoao4pelN8l3RJdv3gRQ==", + "version": "4.3.2", + "resolved": "https://registry.npmjs.org/debug/-/debug-4.3.2.tgz", + "integrity": "sha512-mOp8wKcvj7XxC78zLgw/ZA+6TSgkoE2C/ienthhRD298T7UNwAg9diBpLRxC0mOezLl4B0xV7M0cCO6P/O0Xhw==", "dev": true, "dependencies": { "ms": "2.1.2" @@ -1845,9 +1790,9 @@ "dev": true }, "node_modules/electron-to-chromium": { - "version": "1.3.827", - "resolved": "https://registry.npmjs.org/electron-to-chromium/-/electron-to-chromium-1.3.827.tgz", - "integrity": "sha512-ye+4uQOY/jbjRutMcE/EmOcNwUeo1qo9aKL2tPyb09cU3lmxNeyDF4RWiemmkknW+p29h7dyDqy02higTxc9/A==", + "version": "1.3.864", + "resolved": "https://registry.npmjs.org/electron-to-chromium/-/electron-to-chromium-1.3.864.tgz", + "integrity": "sha512-v4rbad8GO6/yVI92WOeU9Wgxc4NA0n4f6P1FvZTY+jyY7JHEhw3bduYu60v3Q1h81Cg6eo4ApZrFPuycwd5hGw==", "dev": true }, "node_modules/emoji-regex": { @@ -2679,9 +2624,9 @@ } }, "node_modules/is-core-module": { - "version": "2.6.0", - "resolved": "https://registry.npmjs.org/is-core-module/-/is-core-module-2.6.0.tgz", - "integrity": "sha512-wShG8vs60jKfPWpF2KZRaAtvt3a20OAn7+IJ6hLPECpSABLcKtFKTTI4ZtH5QcBruBHlq+WsdHWyz0BCZW7svQ==", + "version": "2.7.0", + "resolved": "https://registry.npmjs.org/is-core-module/-/is-core-module-2.7.0.tgz", + "integrity": "sha512-ByY+tjCciCr+9nLryBYcSD50EOGWt95c7tIsKTG1J2ixKKXPvF7Ej3AVd+UfDydAJom3biBGDBALaO79ktwgEQ==", "dev": true, "dependencies": { "has": "^1.0.3" @@ -2724,9 +2669,9 @@ } }, "node_modules/is-glob": { - "version": "4.0.1", - "resolved": "https://registry.npmjs.org/is-glob/-/is-glob-4.0.1.tgz", - "integrity": "sha512-5G0tKtBTFImOqDnLB2hG6Bp2qcKEFduo4tZu9MT/H6NQv/ghhy30o55ufafxJ/LdH79LLs2Kfrn85TLKyA7BUg==", + "version": "4.0.3", + "resolved": "https://registry.npmjs.org/is-glob/-/is-glob-4.0.3.tgz", + "integrity": "sha512-xelSayHH36ZgE7ZWhli7pW34hNbNl8Ojv5KVmkJD4hBdD3th8Tfk9vYasLM+mXWOZhFkgZfxhLSnrwRr4elSSg==", "dev": true, "dependencies": { "is-extglob": "^2.1.1" @@ -2999,9 +2944,9 @@ } }, "node_modules/istanbul-reports": { - "version": "3.0.2", - "resolved": "https://registry.npmjs.org/istanbul-reports/-/istanbul-reports-3.0.2.tgz", - "integrity": "sha512-9tZvz7AiR3PEDNGiV9vIouQ/EAcqMXFmkcA1CDFTwOB98OZVDL0PH9glHotf5Ugp6GCOTypfzGWI/OqjWNCRUw==", + "version": "3.0.3", + "resolved": "https://registry.npmjs.org/istanbul-reports/-/istanbul-reports-3.0.3.tgz", + "integrity": "sha512-0i77ZFLsb9U3DHi22WzmIngVzfoyxxbQcZRqlF3KoKmCJGq9nhFHoGi8FqBztN2rE8w6hURnZghetn0xpkVb6A==", "dev": true, "dependencies": { "html-escaper": "^2.0.0", @@ -3220,9 +3165,9 @@ "dev": true }, "node_modules/marked": { - "version": "3.0.4", - "resolved": "https://registry.npmjs.org/marked/-/marked-3.0.4.tgz", - "integrity": "sha512-jBo8AOayNaEcvBhNobg6/BLhdsK3NvnKWJg33MAAPbvTWiG4QBn9gpW1+7RssrKu4K1dKlN+0goVQwV41xEfOA==", + "version": "3.0.7", + "resolved": "https://registry.npmjs.org/marked/-/marked-3.0.7.tgz", + "integrity": "sha512-ctKqbnLuNbsHbI26cfMyOlKgXGfl1orOv1AvWWDX7AkgfMOwCWvmuYc+mVLeWhQ9W6hdWVBynOs96VkcscKo0Q==", "dev": true, "bin": { "marked": "bin/marked" @@ -3317,16 +3262,16 @@ "dev": true }, "node_modules/mocha": { - "version": "9.1.1", - "resolved": "https://registry.npmjs.org/mocha/-/mocha-9.1.1.tgz", - "integrity": "sha512-0wE74YMgOkCgBUj8VyIDwmLUjTsS13WV1Pg7l0SHea2qzZzlq7MDnfbPsHKcELBRk3+izEVkRofjmClpycudCA==", + "version": "9.1.2", + "resolved": "https://registry.npmjs.org/mocha/-/mocha-9.1.2.tgz", + "integrity": "sha512-ta3LtJ+63RIBP03VBjMGtSqbe6cWXRejF9SyM9Zyli1CKZJZ+vfCTj3oW24V7wAphMJdpOFLoMI3hjJ1LWbs0w==", "dev": true, "dependencies": { "@ungap/promise-all-settled": "1.1.2", "ansi-colors": "4.1.1", "browser-stdout": "1.3.1", "chokidar": "3.5.2", - "debug": "4.3.1", + "debug": "4.3.2", "diff": "5.0.0", "escape-string-regexp": "4.0.0", "find-up": "5.0.0", @@ -3337,12 +3282,11 @@ "log-symbols": "4.1.0", "minimatch": "3.0.4", "ms": "2.1.3", - "nanoid": "3.1.23", + "nanoid": "3.1.25", "serialize-javascript": "6.0.0", "strip-json-comments": "3.1.1", "supports-color": "8.1.1", "which": "2.0.2", - "wide-align": "1.1.3", "workerpool": "6.1.5", "yargs": "16.2.0", "yargs-parser": "20.2.4", @@ -3373,9 +3317,9 @@ "dev": true }, "node_modules/nanoid": { - "version": "3.1.23", - "resolved": "https://registry.npmjs.org/nanoid/-/nanoid-3.1.23.tgz", - "integrity": "sha512-FiB0kzdP0FFVGDKlRLEQ1BgDzU87dy5NnzjeW9YZNt+/c3+q82EQDUwniSAUxp/F0gFNI1ZhKU1FqYsMuqZVnw==", + "version": "3.1.25", + "resolved": "https://registry.npmjs.org/nanoid/-/nanoid-3.1.25.tgz", + "integrity": "sha512-rdwtIXaXCLFAQbnfqDRnI6jaRHp9fTcYBjtFKE8eezcZ7LuLjhUaQGNeMXf1HmRoCH32CLz6XwX0TtxEOS/A3Q==", "dev": true, "bin": { "nanoid": "bin/nanoid.cjs" @@ -3425,9 +3369,9 @@ } }, "node_modules/node-fetch": { - "version": "2.6.4", - "resolved": "https://registry.npmjs.org/node-fetch/-/node-fetch-2.6.4.tgz", - "integrity": "sha512-aD1fO+xtLiSCc9vuD+sYMxpIuQyhHscGSkBEo2o5LTV/3bTEAYvdUii29n8LlO5uLCmWdGP7uVUVXFo5SRdkLA==", + "version": "2.6.5", + "resolved": "https://registry.npmjs.org/node-fetch/-/node-fetch-2.6.5.tgz", + "integrity": "sha512-mmlIVHJEu5rnIxgEgez6b9GgWXbkZj5YZ7fx+2r94a2E+Uirsp6HsPTPlomfdHtpt/B0cdKviwkoaM6pyvUOpQ==", "dev": true, "dependencies": { "whatwg-url": "^5.0.0" @@ -3449,9 +3393,9 @@ } }, "node_modules/node-releases": { - "version": "1.1.76", - "resolved": "https://registry.npmjs.org/node-releases/-/node-releases-1.1.76.tgz", - "integrity": "sha512-9/IECtNr8dXNmPWmFXepT0/7o5eolGesHUa3mtr0KlgnCvnZxwh2qensKL42JJY2vQKC3nIBXetFAqR+PW1CmA==", + "version": "1.1.77", + "resolved": "https://registry.npmjs.org/node-releases/-/node-releases-1.1.77.tgz", + "integrity": "sha512-rB1DUFUNAN4Gn9keO2K1efO35IDK7yKHCdCaIMvFO7yUYmmZYeDjnGKle26G4rwj+LKRQpjyUUvMkPglwGCYNQ==", "dev": true }, "node_modules/normalize-path": { @@ -4113,6 +4057,12 @@ "node": ">=8" } }, + "node_modules/picocolors": { + "version": "0.2.1", + "resolved": "https://registry.npmjs.org/picocolors/-/picocolors-0.2.1.tgz", + "integrity": "sha512-cMlDqaLEqfSaW8Z7N5Jw+lyIW869EzT73/F5lhtY9cLGoVxSXznfgfXMO0Z5K0o0Q2TkTXq+0KFsdnSe3jDViA==", + "dev": true + }, "node_modules/picomatch": { "version": "2.3.0", "resolved": "https://registry.npmjs.org/picomatch/-/picomatch-2.3.0.tgz", @@ -4210,15 +4160,6 @@ "node": ">=8" } }, - "node_modules/progress": { - "version": "2.0.3", - "resolved": "https://registry.npmjs.org/progress/-/progress-2.0.3.tgz", - "integrity": "sha512-7PiHtLll5LdnKIMw100I+8xJXR5gW2QwWYkT6iJva0bXitZKa/XMrSbdmg3r2Xnaidz9Qumd0VPaMrZlF9V9sA==", - "dev": true, - "engines": { - "node": ">=0.4.0" - } - }, "node_modules/protocols": { "version": "1.4.8", "resolved": "https://registry.npmjs.org/protocols/-/protocols-1.4.8.tgz", @@ -4475,23 +4416,6 @@ "node": ">=10" } }, - "node_modules/release-it/node_modules/debug": { - "version": "4.3.2", - "resolved": "https://registry.npmjs.org/debug/-/debug-4.3.2.tgz", - "integrity": "sha512-mOp8wKcvj7XxC78zLgw/ZA+6TSgkoE2C/ienthhRD298T7UNwAg9diBpLRxC0mOezLl4B0xV7M0cCO6P/O0Xhw==", - "dev": true, - "dependencies": { - "ms": "2.1.2" - }, - "engines": { - "node": ">=6.0" - }, - "peerDependenciesMeta": { - "supports-color": { - "optional": true - } - } - }, "node_modules/release-it/node_modules/lru-cache": { "version": "6.0.0", "resolved": "https://registry.npmjs.org/lru-cache/-/lru-cache-6.0.0.tgz", @@ -4504,12 +4428,6 @@ "node": ">=10" } }, - "node_modules/release-it/node_modules/ms": { - "version": "2.1.2", - "resolved": "https://registry.npmjs.org/ms/-/ms-2.1.2.tgz", - "integrity": "sha512-sGkPx+VjMtmA6MX27oA4FBFELFCZZ4S4XqeGOXCv68tT+jb3vk/RyaKWP0PTKyWtmLSM0b+adUTEvbs1PEaH2w==", - "dev": true - }, "node_modules/release-it/node_modules/semver": { "version": "7.3.5", "resolved": "https://registry.npmjs.org/semver/-/semver-7.3.5.tgz", @@ -4687,9 +4605,9 @@ } }, "node_modules/rxjs": { - "version": "7.3.0", - "resolved": "https://registry.npmjs.org/rxjs/-/rxjs-7.3.0.tgz", - "integrity": "sha512-p2yuGIg9S1epc3vrjKf6iVb3RCaAYjYskkO+jHIaV0IjOPlJop4UnodOoFb2xeNwlguqLYvGw1b1McillYb5Gw==", + "version": "7.4.0", + "resolved": "https://registry.npmjs.org/rxjs/-/rxjs-7.4.0.tgz", + "integrity": "sha512-7SQDi7xeTMCJpqViXh8gL/lebcwlp3d831F05+9B44A4B0WfsEwUQHR64gsH1kvJ+Ep/J9K2+n1hVl1CsGN23w==", "dev": true, "dependencies": { "tslib": "~2.1.0" @@ -4782,9 +4700,9 @@ } }, "node_modules/shiki": { - "version": "0.9.10", - "resolved": "https://registry.npmjs.org/shiki/-/shiki-0.9.10.tgz", - "integrity": "sha512-xeM7Oc6hY+6iW5O/T5hor8ul7mEprzyl5y4r5zthEHToQNw7MIhREMgU3r2gKDB0NaMLNrkcEQagudCdzE13Lg==", + "version": "0.9.11", + "resolved": "https://registry.npmjs.org/shiki/-/shiki-0.9.11.tgz", + "integrity": "sha512-tjruNTLFhU0hruCPoJP0y+B9LKOmcqUhTpxn7pcJB3fa+04gFChuEmxmrUfOJ7ZO6Jd+HwMnDHgY3lv3Tqonuw==", "dev": true, "dependencies": { "jsonc-parser": "^3.0.0", @@ -4807,9 +4725,9 @@ } }, "node_modules/signal-exit": { - "version": "3.0.4", - "resolved": "https://registry.npmjs.org/signal-exit/-/signal-exit-3.0.4.tgz", - "integrity": "sha512-rqYhcAnZ6d/vTPGghdrw7iumdcbXpsk1b8IG/rz+VWV51DM0p7XCtMoJ3qhPLIbp3tvyt3pKRbaaEMZYpHto8Q==", + "version": "3.0.5", + "resolved": "https://registry.npmjs.org/signal-exit/-/signal-exit-3.0.5.tgz", + "integrity": "sha512-KWcOiKeQj6ZyXx7zq4YxSMgHRlod4czeBQZrPb8OKcohcqAXShm7E20kEMle9WBt26hFcAf0qLOcp5zmY7kOqQ==", "dev": true }, "node_modules/sinon": { @@ -4950,26 +4868,26 @@ ] }, "node_modules/string-width": { - "version": "4.2.2", - "resolved": "https://registry.npmjs.org/string-width/-/string-width-4.2.2.tgz", - "integrity": "sha512-XBJbT3N4JhVumXE0eoLU9DCjcaF92KLNqTmFCnG1pf8duUxFGwtP6AD6nkjw9a3IdiRtL3E2w3JDiE/xi3vOeA==", + "version": "4.2.3", + "resolved": "https://registry.npmjs.org/string-width/-/string-width-4.2.3.tgz", + "integrity": "sha512-wKyQRQpjJ0sIp62ErSZdGsjMJWsap5oRNihHhu6G7JVO/9jIB6UyevL+tXuOqrng8j/cxKTWyWUwvSTriiZz/g==", "dev": true, "dependencies": { "emoji-regex": "^8.0.0", "is-fullwidth-code-point": "^3.0.0", - "strip-ansi": "^6.0.0" + "strip-ansi": "^6.0.1" }, "engines": { "node": ">=8" } }, "node_modules/strip-ansi": { - "version": "6.0.0", - "resolved": "https://registry.npmjs.org/strip-ansi/-/strip-ansi-6.0.0.tgz", - "integrity": "sha512-AuvKTrTfQNYNIctbR1K/YGTR1756GycPsg7b9bdV9Duqur4gv6aKqHXah67Z8ImS7WEz5QVcOtlfW2rZEugt6w==", + "version": "6.0.1", + "resolved": "https://registry.npmjs.org/strip-ansi/-/strip-ansi-6.0.1.tgz", + "integrity": "sha512-Y38VPSHcqkFrCpFnQ9vuSXmquuv5oXOKpGeT6aGrr3o3Gc9AlVa6JBfUSOCnbxGGZF+/0ooI7KrPuUSztUdU5A==", "dev": true, "dependencies": { - "ansi-regex": "^5.0.0" + "ansi-regex": "^5.0.1" }, "engines": { "node": ">=8" @@ -5089,12 +5007,12 @@ "dev": true }, "node_modules/ts-node": { - "version": "10.2.1", - "resolved": "https://registry.npmjs.org/ts-node/-/ts-node-10.2.1.tgz", - "integrity": "sha512-hCnyOyuGmD5wHleOQX6NIjJtYVIO8bPP8F2acWkB4W06wdlkgyvJtubO/I9NkI88hCFECbsEgoLc0VNkYmcSfw==", + "version": "10.3.0", + "resolved": "https://registry.npmjs.org/ts-node/-/ts-node-10.3.0.tgz", + "integrity": "sha512-RYIy3i8IgpFH45AX4fQHExrT8BxDeKTdC83QFJkNzkvt8uFB6QJ8XMyhynYiKMLxt9a7yuXaDBZNOYS3XjDcYw==", "dev": true, "dependencies": { - "@cspotcode/source-map-support": "0.6.1", + "@cspotcode/source-map-support": "0.7.0", "@tsconfig/node10": "^1.0.7", "@tsconfig/node12": "^1.0.7", "@tsconfig/node14": "^1.0.0", @@ -5114,9 +5032,6 @@ "ts-node-transpile-only": "dist/bin-transpile.js", "ts-script": "dist/bin-script-deprecated.js" }, - "engines": { - "node": ">=12.0.0" - }, "peerDependencies": { "@swc/core": ">=1.2.50", "@swc/wasm": ">=1.2.50", @@ -5175,19 +5090,16 @@ } }, "node_modules/typedoc": { - "version": "0.21.9", - "resolved": "https://registry.npmjs.org/typedoc/-/typedoc-0.21.9.tgz", - "integrity": "sha512-VRo7aII4bnYaBBM1lhw4bQFmUcDQV8m8tqgjtc7oXl87jc1Slbhfw2X5MccfcR2YnEClHDWgsiQGgNB8KJXocA==", + "version": "0.22.5", + "resolved": "https://registry.npmjs.org/typedoc/-/typedoc-0.22.5.tgz", + "integrity": "sha512-KFrWGU1iKiTGw0RcyjLNYDmhd7uICU14HgBNPmFKY/sT4Pm/fraaLyWyisst9vGTUAKxqibqoDITR7+ZcAkhHg==", "dev": true, "dependencies": { - "glob": "^7.1.7", - "handlebars": "^4.7.7", + "glob": "^7.2.0", "lunr": "^2.3.9", - "marked": "^3.0.2", - "minimatch": "^3.0.0", - "progress": "^2.0.3", - "shiki": "^0.9.8", - "typedoc-default-themes": "^0.12.10" + "marked": "^3.0.4", + "minimatch": "^3.0.4", + "shiki": "^0.9.11" }, "bin": { "typedoc": "bin/typedoc" @@ -5199,35 +5111,46 @@ "typescript": "4.0.x || 4.1.x || 4.2.x || 4.3.x || 4.4.x" } }, - "node_modules/typedoc-default-themes": { - "version": "0.12.10", - "resolved": "https://registry.npmjs.org/typedoc-default-themes/-/typedoc-default-themes-0.12.10.tgz", - "integrity": "sha512-fIS001cAYHkyQPidWXmHuhs8usjP5XVJjWB8oZGqkTowZaz3v7g3KDZeeqE82FBrmkAnIBOY3jgy7lnPnqATbA==", - "dev": true, - "engines": { - "node": ">= 8" - } - }, "node_modules/typedoc-github-wiki-theme": { - "version": "0.5.1", - "resolved": "https://registry.npmjs.org/typedoc-github-wiki-theme/-/typedoc-github-wiki-theme-0.5.1.tgz", - "integrity": "sha512-ED2Uc3WUjbv6xIdCkpMz3yBtcEciJnAhDQdRWLYgw4K+FKY0T3PzbI+ssNsBVgwDnYQP/XuaqfZkeQ3EQsOm9g==", + "version": "0.6.0", + "resolved": "https://registry.npmjs.org/typedoc-github-wiki-theme/-/typedoc-github-wiki-theme-0.6.0.tgz", + "integrity": "sha512-uHhR7PwAZ4JgO0KzlLocWSvMqKsSZ/wxUQYGKskIepzsotPAQcAWnSSnGi6gdkSw8UAfIIppdD7H1AmI39962w==", "dev": true, "peerDependencies": { - "typedoc": ">=0.20.0", - "typedoc-plugin-markdown": ">=3.4.0" + "typedoc": ">=0.22.0", + "typedoc-plugin-markdown": ">=3.11.0" } }, "node_modules/typedoc-plugin-markdown": { - "version": "3.10.4", - "resolved": "https://registry.npmjs.org/typedoc-plugin-markdown/-/typedoc-plugin-markdown-3.10.4.tgz", - "integrity": "sha512-if9w7S9fXLg73AYi/EoRSEhTOZlg3I8mIP8YEmvzSE33VrNXC9/hA0nVcLEwFVJeQY7ay6z67I6kW0KIv7LjeA==", + "version": "3.11.3", + "resolved": "https://registry.npmjs.org/typedoc-plugin-markdown/-/typedoc-plugin-markdown-3.11.3.tgz", + "integrity": "sha512-rWiHbEIe0oZetDIsBR24XJVxGOJ91kDcHoj2KhFKxCLoJGX659EKBQkHne9QJ4W2stGhu1fRgFyQaouSBnxukA==", "dev": true, "dependencies": { "handlebars": "^4.7.7" }, "peerDependencies": { - "typedoc": ">=0.21.2" + "typedoc": ">=0.22.0" + } + }, + "node_modules/typedoc/node_modules/glob": { + "version": "7.2.0", + "resolved": "https://registry.npmjs.org/glob/-/glob-7.2.0.tgz", + "integrity": "sha512-lmLf6gtyrPq8tTjSmrO94wBeQbFR3HbLHbuyD69wuyQkImp2hWqMGB47OX65FBkPffO641IP9jWa1z4ivqG26Q==", + "dev": true, + "dependencies": { + "fs.realpath": "^1.0.0", + "inflight": "^1.0.4", + "inherits": "2", + "minimatch": "^3.0.4", + "once": "^1.3.0", + "path-is-absolute": "^1.0.0" + }, + "engines": { + "node": "*" + }, + "funding": { + "url": "https://github.com/sponsors/isaacs" } }, "node_modules/typescript": { @@ -5433,58 +5356,6 @@ "integrity": "sha1-2e8H3Od7mQK4o6j6SzHD4/fm6Ho=", "dev": true }, - "node_modules/wide-align": { - "version": "1.1.3", - "resolved": "https://registry.npmjs.org/wide-align/-/wide-align-1.1.3.tgz", - "integrity": "sha512-QGkOQc8XL6Bt5PwnsExKBPuMKBxnGxWWW3fU55Xt4feHozMUhdUMaBCk290qpm/wG5u/RSKzwdAC4i51YigihA==", - "dev": true, - "dependencies": { - "string-width": "^1.0.2 || 2" - } - }, - "node_modules/wide-align/node_modules/ansi-regex": { - "version": "3.0.0", - "resolved": "https://registry.npmjs.org/ansi-regex/-/ansi-regex-3.0.0.tgz", - "integrity": "sha1-7QMXwyIGT3lGbAKWa922Bas32Zg=", - "dev": true, - "engines": { - "node": ">=4" - } - }, - "node_modules/wide-align/node_modules/is-fullwidth-code-point": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/is-fullwidth-code-point/-/is-fullwidth-code-point-2.0.0.tgz", - "integrity": "sha1-o7MKXE8ZkYMWeqq5O+764937ZU8=", - "dev": true, - "engines": { - "node": ">=4" - } - }, - "node_modules/wide-align/node_modules/string-width": { - "version": "2.1.1", - "resolved": "https://registry.npmjs.org/string-width/-/string-width-2.1.1.tgz", - "integrity": "sha512-nOqH59deCq9SRHlxq1Aw85Jnt4w6KvLKqWVik6oA9ZklXLNIOlqg4F2yrT1MVaTjAqvVwdfeZ7w7aCvJD7ugkw==", - "dev": true, - "dependencies": { - "is-fullwidth-code-point": "^2.0.0", - "strip-ansi": "^4.0.0" - }, - "engines": { - "node": ">=4" - } - }, - "node_modules/wide-align/node_modules/strip-ansi": { - "version": "4.0.0", - "resolved": "https://registry.npmjs.org/strip-ansi/-/strip-ansi-4.0.0.tgz", - "integrity": "sha1-qEeQIusaw2iocTibY1JixQXuNo8=", - "dev": true, - "dependencies": { - "ansi-regex": "^3.0.0" - }, - "engines": { - "node": ">=4" - } - }, "node_modules/widest-line": { "version": "3.1.0", "resolved": "https://registry.npmjs.org/widest-line/-/widest-line-3.1.0.tgz", @@ -5728,9 +5599,9 @@ }, "dependencies": { "@babel/code-frame": { - "version": "7.14.5", - "resolved": "https://registry.npmjs.org/@babel/code-frame/-/code-frame-7.14.5.tgz", - "integrity": "sha512-9pzDqyc6OLDaqe+zbACgFkb6fKMNG6CObKpnYXChRsvYGyEdc7CA2BaqeOM+vOtCS5ndmJicPJhKAwYRI6UfFw==", + "version": "7.15.8", + "resolved": "https://registry.npmjs.org/@babel/code-frame/-/code-frame-7.15.8.tgz", + "integrity": "sha512-2IAnmn8zbvC/jKYhq5Ki9I+DwjlrtMPUCH/CpHvqI4dNnlwHwsxoIhlc8WcYY5LSYknXQtAlFYuHfqAFCvQ4Wg==", "dev": true, "requires": { "@babel/highlight": "^7.14.5" @@ -5743,20 +5614,20 @@ "dev": true }, "@babel/core": { - "version": "7.15.5", - "resolved": "https://registry.npmjs.org/@babel/core/-/core-7.15.5.tgz", - "integrity": "sha512-pYgXxiwAgQpgM1bNkZsDEq85f0ggXMA5L7c+o3tskGMh2BunCI9QUwB9Z4jpvXUOuMdyGKiGKQiRe11VS6Jzvg==", + "version": "7.15.8", + "resolved": "https://registry.npmjs.org/@babel/core/-/core-7.15.8.tgz", + "integrity": "sha512-3UG9dsxvYBMYwRv+gS41WKHno4K60/9GPy1CJaH6xy3Elq8CTtvtjT5R5jmNhXfCYLX2mTw+7/aq5ak/gOE0og==", "dev": true, "requires": { - "@babel/code-frame": "^7.14.5", - "@babel/generator": "^7.15.4", + "@babel/code-frame": "^7.15.8", + "@babel/generator": "^7.15.8", "@babel/helper-compilation-targets": "^7.15.4", - "@babel/helper-module-transforms": "^7.15.4", + "@babel/helper-module-transforms": "^7.15.8", "@babel/helpers": "^7.15.4", - "@babel/parser": "^7.15.5", + "@babel/parser": "^7.15.8", "@babel/template": "^7.15.4", "@babel/traverse": "^7.15.4", - "@babel/types": "^7.15.4", + "@babel/types": "^7.15.6", "convert-source-map": "^1.7.0", "debug": "^4.1.0", "gensync": "^1.0.0-beta.2", @@ -5766,12 +5637,12 @@ } }, "@babel/generator": { - "version": "7.15.4", - "resolved": "https://registry.npmjs.org/@babel/generator/-/generator-7.15.4.tgz", - "integrity": "sha512-d3itta0tu+UayjEORPNz6e1T3FtvWlP5N4V5M+lhp/CxT4oAA7/NcScnpRyspUMLK6tu9MNHmQHxRykuN2R7hw==", + "version": "7.15.8", + "resolved": "https://registry.npmjs.org/@babel/generator/-/generator-7.15.8.tgz", + "integrity": "sha512-ECmAKstXbp1cvpTTZciZCgfOt6iN64lR0d+euv3UZisU5awfRawOvg07Utn/qBGuH4bRIEZKrA/4LzZyXhZr8g==", "dev": true, "requires": { - "@babel/types": "^7.15.4", + "@babel/types": "^7.15.6", "jsesc": "^2.5.1", "source-map": "^0.5.0" } @@ -5836,9 +5707,9 @@ } }, "@babel/helper-module-transforms": { - "version": "7.15.7", - "resolved": "https://registry.npmjs.org/@babel/helper-module-transforms/-/helper-module-transforms-7.15.7.tgz", - "integrity": "sha512-ZNqjjQG/AuFfekFTY+7nY4RgBSklgTu970c7Rj3m/JOhIu5KPBUuTA9AY6zaKcUvk4g6EbDXdBnhi35FAssdSw==", + "version": "7.15.8", + "resolved": "https://registry.npmjs.org/@babel/helper-module-transforms/-/helper-module-transforms-7.15.8.tgz", + "integrity": "sha512-DfAfA6PfpG8t4S6npwzLvTUpp0sS7JrcuaMiy1Y5645laRJIp/LiLGIBbQKaXSInK8tiGNI7FL7L8UvB8gdUZg==", "dev": true, "requires": { "@babel/helper-module-imports": "^7.15.4", @@ -5983,9 +5854,9 @@ } }, "@babel/parser": { - "version": "7.15.7", - "resolved": "https://registry.npmjs.org/@babel/parser/-/parser-7.15.7.tgz", - "integrity": "sha512-rycZXvQ+xS9QyIcJ9HXeDWf1uxqlbVFAUq0Rq0dbc50Zb/+wUe/ehyfzGfm9KZZF0kBejYgxltBXocP+gKdL2g==", + "version": "7.15.8", + "resolved": "https://registry.npmjs.org/@babel/parser/-/parser-7.15.8.tgz", + "integrity": "sha512-BRYa3wcQnjS/nqI8Ac94pYYpJfojHVvVXJ97+IDCImX4Jc8W8Xv1+47enbruk+q1etOpsQNwnfFcNGw+gtPGxA==", "dev": true }, "@babel/template": { @@ -6033,9 +5904,9 @@ "dev": true }, "@cspotcode/source-map-support": { - "version": "0.6.1", - "resolved": "https://registry.npmjs.org/@cspotcode/source-map-support/-/source-map-support-0.6.1.tgz", - "integrity": "sha512-DX3Z+T5dt1ockmPdobJS/FAsQPW4V4SrWEhD2iYQT2Cb2tQsiMnYxrcUH9By/Z3B+v0S5LMBkQtV/XOBbpLEOg==", + "version": "0.7.0", + "resolved": "https://registry.npmjs.org/@cspotcode/source-map-support/-/source-map-support-0.7.0.tgz", + "integrity": "sha512-X4xqRHqN8ACt2aHVe51OxeA2HjbcL4MqFqXkrmQszJ1NOUuUu5u6Vqx/0lZSVNku7velL5FC/s5uEAj1lsBMhA==", "dev": true, "requires": { "@cspotcode/source-map-consumer": "0.8.0" @@ -6206,18 +6077,18 @@ } }, "@octokit/openapi-types": { - "version": "10.0.0", - "resolved": "https://registry.npmjs.org/@octokit/openapi-types/-/openapi-types-10.0.0.tgz", - "integrity": "sha512-k1iO2zKuEjjRS1EJb4FwSLk+iF6EGp+ZV0OMRViQoWhQ1fZTk9hg1xccZII5uyYoiqcbC73MRBmT45y1vp2PPg==", + "version": "11.2.0", + "resolved": "https://registry.npmjs.org/@octokit/openapi-types/-/openapi-types-11.2.0.tgz", + "integrity": "sha512-PBsVO+15KSlGmiI8QAzaqvsNlZlrDlyAJYcrXBCvVUxCp7VnXjkwPoFHgjEJXx3WF9BAwkA6nfCUA7i9sODzKA==", "dev": true }, "@octokit/plugin-paginate-rest": { - "version": "2.16.0", - "resolved": "https://registry.npmjs.org/@octokit/plugin-paginate-rest/-/plugin-paginate-rest-2.16.0.tgz", - "integrity": "sha512-8YYzALPMvEZ35kgy5pdYvQ22Roz+BIuEaedO575GwE2vb/ACDqQn0xQrTJR4tnZCJn7pi8+AWPVjrFDaERIyXQ==", + "version": "2.17.0", + "resolved": "https://registry.npmjs.org/@octokit/plugin-paginate-rest/-/plugin-paginate-rest-2.17.0.tgz", + "integrity": "sha512-tzMbrbnam2Mt4AhuyCHvpRkS0oZ5MvwwcQPYGtMv4tUa5kkzG58SVB0fcsLulOZQeRnOgdkZWkRUiyBlh0Bkyw==", "dev": true, "requires": { - "@octokit/types": "^6.26.0" + "@octokit/types": "^6.34.0" } }, "@octokit/plugin-request-log": { @@ -6228,19 +6099,19 @@ "requires": {} }, "@octokit/plugin-rest-endpoint-methods": { - "version": "5.10.4", - "resolved": "https://registry.npmjs.org/@octokit/plugin-rest-endpoint-methods/-/plugin-rest-endpoint-methods-5.10.4.tgz", - "integrity": "sha512-Dh+EAMCYR9RUHwQChH94Skl0lM8Fh99auT8ggck/xTzjJrwVzvsd0YH68oRPqp/HxICzmUjLfaQ9sy1o1sfIiA==", + "version": "5.13.0", + "resolved": "https://registry.npmjs.org/@octokit/plugin-rest-endpoint-methods/-/plugin-rest-endpoint-methods-5.13.0.tgz", + "integrity": "sha512-uJjMTkN1KaOIgNtUPMtIXDOjx6dGYysdIFhgA52x4xSadQCz3b/zJexvITDVpANnfKPW/+E0xkOvLntqMYpviA==", "dev": true, "requires": { - "@octokit/types": "^6.28.1", + "@octokit/types": "^6.34.0", "deprecation": "^2.3.1" } }, "@octokit/request": { - "version": "5.6.1", - "resolved": "https://registry.npmjs.org/@octokit/request/-/request-5.6.1.tgz", - "integrity": "sha512-Ls2cfs1OfXaOKzkcxnqw5MR6drMA/zWX/LIS/p8Yjdz7QKTPQLMsB3R+OvoxE6XnXeXEE2X7xe4G4l4X0gRiKQ==", + "version": "5.6.2", + "resolved": "https://registry.npmjs.org/@octokit/request/-/request-5.6.2.tgz", + "integrity": "sha512-je66CvSEVf0jCpRISxkUcCa0UkxmFs6eGDRSbfJtAVwbLH5ceqF+YEyC8lj8ystKyZTy8adWr0qmkY52EfOeLA==", "dev": true, "requires": { "@octokit/endpoint": "^6.0.1", @@ -6275,12 +6146,12 @@ } }, "@octokit/types": { - "version": "6.28.1", - "resolved": "https://registry.npmjs.org/@octokit/types/-/types-6.28.1.tgz", - "integrity": "sha512-XlxDoQLFO5JnFZgKVQTYTvXRsQFfr/GwDUU108NJ9R5yFPkA2qXhTJjYuul3vE4eLXP40FA2nysOu2zd6boE+w==", + "version": "6.34.0", + "resolved": "https://registry.npmjs.org/@octokit/types/-/types-6.34.0.tgz", + "integrity": "sha512-s1zLBjWhdEI2zwaoSgyOFoKSl109CUcVBCc7biPJ3aAf6LGLU6szDvi31JPU7bxfla2lqfhjbbg/5DdFNxOwHw==", "dev": true, "requires": { - "@octokit/openapi-types": "^10.2.2" + "@octokit/openapi-types": "^11.2.0" } }, "@sindresorhus/is": { @@ -6391,9 +6262,9 @@ "dev": true }, "@types/node": { - "version": "16.9.6", - "resolved": "https://registry.npmjs.org/@types/node/-/node-16.9.6.tgz", - "integrity": "sha512-YHUZhBOMTM3mjFkXVcK+WwAcYmyhe1wL4lfqNtzI0b3qAy7yuSetnM7QJazgE5PFmgVTNGiLOgRFfJMqW7XpSQ==", + "version": "16.10.3", + "resolved": "https://registry.npmjs.org/@types/node/-/node-16.10.3.tgz", + "integrity": "sha512-ho3Ruq+fFnBrZhUYI46n/bV2GjwzSkwuT4dTf0GkuNFmnb8nq4ny2z9JEVemFi6bdEJanHLlYfy9c6FN9B9McQ==", "dev": true }, "@types/parse-json": { @@ -6412,9 +6283,9 @@ } }, "@types/sinon": { - "version": "10.0.3", - "resolved": "https://registry.npmjs.org/@types/sinon/-/sinon-10.0.3.tgz", - "integrity": "sha512-XUaFuUOQ3A/r6gS1qCU/USMleascaqGeQpGR1AZ5JdRtBPlzijRzKsik1TuGzvdtPA0mdq42JqaJmJ+Afg1LJg==", + "version": "10.0.4", + "resolved": "https://registry.npmjs.org/@types/sinon/-/sinon-10.0.4.tgz", + "integrity": "sha512-fOYjrxQv8zJsqOY6V6ecP4eZhQBxtY80X0er1VVnUIAIZo74jHm8e1vguG5Yt4Iv8W2Wr7TgibB8MfRe32k9pA==", "dev": true, "requires": { "@sinonjs/fake-timers": "^7.1.0" @@ -6461,52 +6332,12 @@ } }, "ansi-align": { - "version": "3.0.0", - "resolved": "https://registry.npmjs.org/ansi-align/-/ansi-align-3.0.0.tgz", - "integrity": "sha512-ZpClVKqXN3RGBmKibdfWzqCY4lnjEuoNzU5T0oEFpfd/z5qJHVarukridD4juLO2FXMiwUQxr9WqQtaYa8XRYw==", + "version": "3.0.1", + "resolved": "https://registry.npmjs.org/ansi-align/-/ansi-align-3.0.1.tgz", + "integrity": "sha512-IOfwwBF5iczOjp/WeY4YxyjqAFMQoZufdQWDd19SEExbVLNXqvpzSJ/M7Za4/sCPmQ0+GRquoA7bGcINcxew6w==", "dev": true, "requires": { - "string-width": "^3.0.0" - }, - "dependencies": { - "ansi-regex": { - "version": "4.1.0", - "resolved": "https://registry.npmjs.org/ansi-regex/-/ansi-regex-4.1.0.tgz", - "integrity": "sha512-1apePfXM1UOSqw0o9IiFAovVz9M5S1Dg+4TrDwfMewQ6p/rmMueb7tWZjQ1rx4Loy1ArBggoqGpfqqdI4rondg==", - "dev": true - }, - "emoji-regex": { - "version": "7.0.3", - "resolved": "https://registry.npmjs.org/emoji-regex/-/emoji-regex-7.0.3.tgz", - "integrity": "sha512-CwBLREIQ7LvYFB0WyRvwhq5N5qPhc6PMjD6bYggFlI5YyDgl+0vxq5VHbMOFqLg7hfWzmu8T5Z1QofhmTIhItA==", - "dev": true - }, - "is-fullwidth-code-point": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/is-fullwidth-code-point/-/is-fullwidth-code-point-2.0.0.tgz", - "integrity": "sha1-o7MKXE8ZkYMWeqq5O+764937ZU8=", - "dev": true - }, - "string-width": { - "version": "3.1.0", - "resolved": "https://registry.npmjs.org/string-width/-/string-width-3.1.0.tgz", - "integrity": "sha512-vafcv6KjVZKSgz06oM/H6GDBrAtz8vdhQakGjFIvNrHA6y3HCF1CInLy+QLq8dTJPQ1b+KDUqDFctkdRW44e1w==", - "dev": true, - "requires": { - "emoji-regex": "^7.0.1", - "is-fullwidth-code-point": "^2.0.0", - "strip-ansi": "^5.1.0" - } - }, - "strip-ansi": { - "version": "5.2.0", - "resolved": "https://registry.npmjs.org/strip-ansi/-/strip-ansi-5.2.0.tgz", - "integrity": "sha512-DuRs1gKbBqsMKIZlrffwlug8MHkcnpjs5VPmL1PAh+mA30U0DTotfDZ0d2UUsXpPmPmMMJ6W773MaA3J+lbiWA==", - "dev": true, - "requires": { - "ansi-regex": "^4.1.0" - } - } + "string-width": "^4.1.0" } }, "ansi-colors": { @@ -6696,16 +6527,16 @@ "dev": true }, "browserslist": { - "version": "4.17.0", - "resolved": "https://registry.npmjs.org/browserslist/-/browserslist-4.17.0.tgz", - "integrity": "sha512-g2BJ2a0nEYvEFQC208q8mVAhfNwpZ5Mu8BwgtCdZKO3qx98HChmeg448fPdUzld8aFmfLgVh7yymqV+q1lJZ5g==", + "version": "4.17.3", + "resolved": "https://registry.npmjs.org/browserslist/-/browserslist-4.17.3.tgz", + "integrity": "sha512-59IqHJV5VGdcJZ+GZ2hU5n4Kv3YiASzW6Xk5g9tf5a/MAzGeFwgGWU39fVzNIOVcgB3+Gp+kiQu0HEfTVU/3VQ==", "dev": true, "requires": { - "caniuse-lite": "^1.0.30001254", - "colorette": "^1.3.0", - "electron-to-chromium": "^1.3.830", + "caniuse-lite": "^1.0.30001264", + "electron-to-chromium": "^1.3.857", "escalade": "^3.1.1", - "node-releases": "^1.1.75" + "node-releases": "^1.1.77", + "picocolors": "^0.2.1" } }, "buffer": { @@ -6791,9 +6622,9 @@ "dev": true }, "caniuse-lite": { - "version": "1.0.30001259", - "resolved": "https://registry.npmjs.org/caniuse-lite/-/caniuse-lite-1.0.30001259.tgz", - "integrity": "sha512-V7mQTFhjITxuk9zBpI6nYsiTXhcPe05l+364nZjK7MFK/E7ibvYBSAXr4YcA6oPR8j3ZLM/LN+lUqUVAQEUZFg==", + "version": "1.0.30001265", + "resolved": "https://registry.npmjs.org/caniuse-lite/-/caniuse-lite-1.0.30001265.tgz", + "integrity": "sha512-YzBnspggWV5hep1m9Z6sZVLOt7vrju8xWooFAgN6BA5qvy98qPAPb7vNUzypFaoh2pb3vlfzbDO8tB57UPGbtw==", "dev": true }, "chalk": { @@ -6867,9 +6698,9 @@ } }, "cli-spinners": { - "version": "2.6.0", - "resolved": "https://registry.npmjs.org/cli-spinners/-/cli-spinners-2.6.0.tgz", - "integrity": "sha512-t+4/y50K/+4xcCRosKkA7W4gTr1MySvLV0q+PxmG7FJ5g+66ChKurYjxBCjHggHH3HA5Hh9cy+lcUGWDqVH+4Q==", + "version": "2.6.1", + "resolved": "https://registry.npmjs.org/cli-spinners/-/cli-spinners-2.6.1.tgz", + "integrity": "sha512-x/5fWmGMnbKQAaNwN+UZlV79qBLM9JFnJuJ03gIi5whrob0xV0ofNVHy9DhwGdsMJQc2OKv0oGmLzvaqvAVv+g==", "dev": true }, "cli-width": { @@ -6924,12 +6755,6 @@ "integrity": "sha512-dOy+3AuW3a2wNbZHIuMZpTcgjGuLU/uBL/ubcZF9OXbDo8ff4O8yVp5Bf0efS8uEoYo5q4Fx7dY9OgQGXgAsQA==", "dev": true }, - "colorette": { - "version": "1.4.0", - "resolved": "https://registry.npmjs.org/colorette/-/colorette-1.4.0.tgz", - "integrity": "sha512-Y2oEozpomLn7Q3HFP7dpww7AtMJplbM9lGZP6RDfHqmbeRjiwRg4n6VM6j4KLmRke85uWEI7JqF17f3pqdRA0g==", - "dev": true - }, "combined-stream": { "version": "1.0.8", "resolved": "https://registry.npmjs.org/combined-stream/-/combined-stream-1.0.8.tgz", @@ -7011,9 +6836,9 @@ "dev": true }, "debug": { - "version": "4.3.1", - "resolved": "https://registry.npmjs.org/debug/-/debug-4.3.1.tgz", - "integrity": "sha512-doEwdvm4PCeK4K3RQN2ZC2BYUBaxwLARCqZmMjtF8a51J2Rb0xpVloFRnCODwqjpwnAoao4pelN8l3RJdv3gRQ==", + "version": "4.3.2", + "resolved": "https://registry.npmjs.org/debug/-/debug-4.3.2.tgz", + "integrity": "sha512-mOp8wKcvj7XxC78zLgw/ZA+6TSgkoE2C/ienthhRD298T7UNwAg9diBpLRxC0mOezLl4B0xV7M0cCO6P/O0Xhw==", "dev": true, "requires": { "ms": "2.1.2" @@ -7139,9 +6964,9 @@ "dev": true }, "electron-to-chromium": { - "version": "1.3.827", - "resolved": "https://registry.npmjs.org/electron-to-chromium/-/electron-to-chromium-1.3.827.tgz", - "integrity": "sha512-ye+4uQOY/jbjRutMcE/EmOcNwUeo1qo9aKL2tPyb09cU3lmxNeyDF4RWiemmkknW+p29h7dyDqy02higTxc9/A==", + "version": "1.3.864", + "resolved": "https://registry.npmjs.org/electron-to-chromium/-/electron-to-chromium-1.3.864.tgz", + "integrity": "sha512-v4rbad8GO6/yVI92WOeU9Wgxc4NA0n4f6P1FvZTY+jyY7JHEhw3bduYu60v3Q1h81Cg6eo4ApZrFPuycwd5hGw==", "dev": true }, "emoji-regex": { @@ -7737,9 +7562,9 @@ } }, "is-core-module": { - "version": "2.6.0", - "resolved": "https://registry.npmjs.org/is-core-module/-/is-core-module-2.6.0.tgz", - "integrity": "sha512-wShG8vs60jKfPWpF2KZRaAtvt3a20OAn7+IJ6hLPECpSABLcKtFKTTI4ZtH5QcBruBHlq+WsdHWyz0BCZW7svQ==", + "version": "2.7.0", + "resolved": "https://registry.npmjs.org/is-core-module/-/is-core-module-2.7.0.tgz", + "integrity": "sha512-ByY+tjCciCr+9nLryBYcSD50EOGWt95c7tIsKTG1J2ixKKXPvF7Ej3AVd+UfDydAJom3biBGDBALaO79ktwgEQ==", "dev": true, "requires": { "has": "^1.0.3" @@ -7764,9 +7589,9 @@ "dev": true }, "is-glob": { - "version": "4.0.1", - "resolved": "https://registry.npmjs.org/is-glob/-/is-glob-4.0.1.tgz", - "integrity": "sha512-5G0tKtBTFImOqDnLB2hG6Bp2qcKEFduo4tZu9MT/H6NQv/ghhy30o55ufafxJ/LdH79LLs2Kfrn85TLKyA7BUg==", + "version": "4.0.3", + "resolved": "https://registry.npmjs.org/is-glob/-/is-glob-4.0.3.tgz", + "integrity": "sha512-xelSayHH36ZgE7ZWhli7pW34hNbNl8Ojv5KVmkJD4hBdD3th8Tfk9vYasLM+mXWOZhFkgZfxhLSnrwRr4elSSg==", "dev": true, "requires": { "is-extglob": "^2.1.1" @@ -7968,9 +7793,9 @@ } }, "istanbul-reports": { - "version": "3.0.2", - "resolved": "https://registry.npmjs.org/istanbul-reports/-/istanbul-reports-3.0.2.tgz", - "integrity": "sha512-9tZvz7AiR3PEDNGiV9vIouQ/EAcqMXFmkcA1CDFTwOB98OZVDL0PH9glHotf5Ugp6GCOTypfzGWI/OqjWNCRUw==", + "version": "3.0.3", + "resolved": "https://registry.npmjs.org/istanbul-reports/-/istanbul-reports-3.0.3.tgz", + "integrity": "sha512-0i77ZFLsb9U3DHi22WzmIngVzfoyxxbQcZRqlF3KoKmCJGq9nhFHoGi8FqBztN2rE8w6hURnZghetn0xpkVb6A==", "dev": true, "requires": { "html-escaper": "^2.0.0", @@ -8143,9 +7968,9 @@ "dev": true }, "marked": { - "version": "3.0.4", - "resolved": "https://registry.npmjs.org/marked/-/marked-3.0.4.tgz", - "integrity": "sha512-jBo8AOayNaEcvBhNobg6/BLhdsK3NvnKWJg33MAAPbvTWiG4QBn9gpW1+7RssrKu4K1dKlN+0goVQwV41xEfOA==", + "version": "3.0.7", + "resolved": "https://registry.npmjs.org/marked/-/marked-3.0.7.tgz", + "integrity": "sha512-ctKqbnLuNbsHbI26cfMyOlKgXGfl1orOv1AvWWDX7AkgfMOwCWvmuYc+mVLeWhQ9W6hdWVBynOs96VkcscKo0Q==", "dev": true }, "merge-stream": { @@ -8213,16 +8038,16 @@ "dev": true }, "mocha": { - "version": "9.1.1", - "resolved": "https://registry.npmjs.org/mocha/-/mocha-9.1.1.tgz", - "integrity": "sha512-0wE74YMgOkCgBUj8VyIDwmLUjTsS13WV1Pg7l0SHea2qzZzlq7MDnfbPsHKcELBRk3+izEVkRofjmClpycudCA==", + "version": "9.1.2", + "resolved": "https://registry.npmjs.org/mocha/-/mocha-9.1.2.tgz", + "integrity": "sha512-ta3LtJ+63RIBP03VBjMGtSqbe6cWXRejF9SyM9Zyli1CKZJZ+vfCTj3oW24V7wAphMJdpOFLoMI3hjJ1LWbs0w==", "dev": true, "requires": { "@ungap/promise-all-settled": "1.1.2", "ansi-colors": "4.1.1", "browser-stdout": "1.3.1", "chokidar": "3.5.2", - "debug": "4.3.1", + "debug": "4.3.2", "diff": "5.0.0", "escape-string-regexp": "4.0.0", "find-up": "5.0.0", @@ -8233,12 +8058,11 @@ "log-symbols": "4.1.0", "minimatch": "3.0.4", "ms": "2.1.3", - "nanoid": "3.1.23", + "nanoid": "3.1.25", "serialize-javascript": "6.0.0", "strip-json-comments": "3.1.1", "supports-color": "8.1.1", "which": "2.0.2", - "wide-align": "1.1.3", "workerpool": "6.1.5", "yargs": "16.2.0", "yargs-parser": "20.2.4", @@ -8258,9 +8082,9 @@ "dev": true }, "nanoid": { - "version": "3.1.23", - "resolved": "https://registry.npmjs.org/nanoid/-/nanoid-3.1.23.tgz", - "integrity": "sha512-FiB0kzdP0FFVGDKlRLEQ1BgDzU87dy5NnzjeW9YZNt+/c3+q82EQDUwniSAUxp/F0gFNI1ZhKU1FqYsMuqZVnw==", + "version": "3.1.25", + "resolved": "https://registry.npmjs.org/nanoid/-/nanoid-3.1.25.tgz", + "integrity": "sha512-rdwtIXaXCLFAQbnfqDRnI6jaRHp9fTcYBjtFKE8eezcZ7LuLjhUaQGNeMXf1HmRoCH32CLz6XwX0TtxEOS/A3Q==", "dev": true }, "neo-async": { @@ -8300,9 +8124,9 @@ } }, "node-fetch": { - "version": "2.6.4", - "resolved": "https://registry.npmjs.org/node-fetch/-/node-fetch-2.6.4.tgz", - "integrity": "sha512-aD1fO+xtLiSCc9vuD+sYMxpIuQyhHscGSkBEo2o5LTV/3bTEAYvdUii29n8LlO5uLCmWdGP7uVUVXFo5SRdkLA==", + "version": "2.6.5", + "resolved": "https://registry.npmjs.org/node-fetch/-/node-fetch-2.6.5.tgz", + "integrity": "sha512-mmlIVHJEu5rnIxgEgez6b9GgWXbkZj5YZ7fx+2r94a2E+Uirsp6HsPTPlomfdHtpt/B0cdKviwkoaM6pyvUOpQ==", "dev": true, "requires": { "whatwg-url": "^5.0.0" @@ -8318,9 +8142,9 @@ } }, "node-releases": { - "version": "1.1.76", - "resolved": "https://registry.npmjs.org/node-releases/-/node-releases-1.1.76.tgz", - "integrity": "sha512-9/IECtNr8dXNmPWmFXepT0/7o5eolGesHUa3mtr0KlgnCvnZxwh2qensKL42JJY2vQKC3nIBXetFAqR+PW1CmA==", + "version": "1.1.77", + "resolved": "https://registry.npmjs.org/node-releases/-/node-releases-1.1.77.tgz", + "integrity": "sha512-rB1DUFUNAN4Gn9keO2K1efO35IDK7yKHCdCaIMvFO7yUYmmZYeDjnGKle26G4rwj+LKRQpjyUUvMkPglwGCYNQ==", "dev": true }, "normalize-path": { @@ -8836,6 +8660,12 @@ "integrity": "sha512-gDKb8aZMDeD/tZWs9P6+q0J9Mwkdl6xMV8TjnGP3qJVJ06bdMgkbBlLU8IdfOsIsFz2BW1rNVT3XuNEl8zPAvw==", "dev": true }, + "picocolors": { + "version": "0.2.1", + "resolved": "https://registry.npmjs.org/picocolors/-/picocolors-0.2.1.tgz", + "integrity": "sha512-cMlDqaLEqfSaW8Z7N5Jw+lyIW869EzT73/F5lhtY9cLGoVxSXznfgfXMO0Z5K0o0Q2TkTXq+0KFsdnSe3jDViA==", + "dev": true + }, "picomatch": { "version": "2.3.0", "resolved": "https://registry.npmjs.org/picomatch/-/picomatch-2.3.0.tgz", @@ -8905,12 +8735,6 @@ "fromentries": "^1.2.0" } }, - "progress": { - "version": "2.0.3", - "resolved": "https://registry.npmjs.org/progress/-/progress-2.0.3.tgz", - "integrity": "sha512-7PiHtLll5LdnKIMw100I+8xJXR5gW2QwWYkT6iJva0bXitZKa/XMrSbdmg3r2Xnaidz9Qumd0VPaMrZlF9V9sA==", - "dev": true - }, "protocols": { "version": "1.4.8", "resolved": "https://registry.npmjs.org/protocols/-/protocols-1.4.8.tgz", @@ -9101,15 +8925,6 @@ "yargs-parser": "20.2.9" }, "dependencies": { - "debug": { - "version": "4.3.2", - "resolved": "https://registry.npmjs.org/debug/-/debug-4.3.2.tgz", - "integrity": "sha512-mOp8wKcvj7XxC78zLgw/ZA+6TSgkoE2C/ienthhRD298T7UNwAg9diBpLRxC0mOezLl4B0xV7M0cCO6P/O0Xhw==", - "dev": true, - "requires": { - "ms": "2.1.2" - } - }, "lru-cache": { "version": "6.0.0", "resolved": "https://registry.npmjs.org/lru-cache/-/lru-cache-6.0.0.tgz", @@ -9119,12 +8934,6 @@ "yallist": "^4.0.0" } }, - "ms": { - "version": "2.1.2", - "resolved": "https://registry.npmjs.org/ms/-/ms-2.1.2.tgz", - "integrity": "sha512-sGkPx+VjMtmA6MX27oA4FBFELFCZZ4S4XqeGOXCv68tT+jb3vk/RyaKWP0PTKyWtmLSM0b+adUTEvbs1PEaH2w==", - "dev": true - }, "semver": { "version": "7.3.5", "resolved": "https://registry.npmjs.org/semver/-/semver-7.3.5.tgz", @@ -9247,9 +9056,9 @@ } }, "rxjs": { - "version": "7.3.0", - "resolved": "https://registry.npmjs.org/rxjs/-/rxjs-7.3.0.tgz", - "integrity": "sha512-p2yuGIg9S1epc3vrjKf6iVb3RCaAYjYskkO+jHIaV0IjOPlJop4UnodOoFb2xeNwlguqLYvGw1b1McillYb5Gw==", + "version": "7.4.0", + "resolved": "https://registry.npmjs.org/rxjs/-/rxjs-7.4.0.tgz", + "integrity": "sha512-7SQDi7xeTMCJpqViXh8gL/lebcwlp3d831F05+9B44A4B0WfsEwUQHR64gsH1kvJ+Ep/J9K2+n1hVl1CsGN23w==", "dev": true, "requires": { "tslib": "~2.1.0" @@ -9324,9 +9133,9 @@ } }, "shiki": { - "version": "0.9.10", - "resolved": "https://registry.npmjs.org/shiki/-/shiki-0.9.10.tgz", - "integrity": "sha512-xeM7Oc6hY+6iW5O/T5hor8ul7mEprzyl5y4r5zthEHToQNw7MIhREMgU3r2gKDB0NaMLNrkcEQagudCdzE13Lg==", + "version": "0.9.11", + "resolved": "https://registry.npmjs.org/shiki/-/shiki-0.9.11.tgz", + "integrity": "sha512-tjruNTLFhU0hruCPoJP0y+B9LKOmcqUhTpxn7pcJB3fa+04gFChuEmxmrUfOJ7ZO6Jd+HwMnDHgY3lv3Tqonuw==", "dev": true, "requires": { "jsonc-parser": "^3.0.0", @@ -9346,9 +9155,9 @@ } }, "signal-exit": { - "version": "3.0.4", - "resolved": "https://registry.npmjs.org/signal-exit/-/signal-exit-3.0.4.tgz", - "integrity": "sha512-rqYhcAnZ6d/vTPGghdrw7iumdcbXpsk1b8IG/rz+VWV51DM0p7XCtMoJ3qhPLIbp3tvyt3pKRbaaEMZYpHto8Q==", + "version": "3.0.5", + "resolved": "https://registry.npmjs.org/signal-exit/-/signal-exit-3.0.5.tgz", + "integrity": "sha512-KWcOiKeQj6ZyXx7zq4YxSMgHRlod4czeBQZrPb8OKcohcqAXShm7E20kEMle9WBt26hFcAf0qLOcp5zmY7kOqQ==", "dev": true }, "sinon": { @@ -9456,23 +9265,23 @@ } }, "string-width": { - "version": "4.2.2", - "resolved": "https://registry.npmjs.org/string-width/-/string-width-4.2.2.tgz", - "integrity": "sha512-XBJbT3N4JhVumXE0eoLU9DCjcaF92KLNqTmFCnG1pf8duUxFGwtP6AD6nkjw9a3IdiRtL3E2w3JDiE/xi3vOeA==", + "version": "4.2.3", + "resolved": "https://registry.npmjs.org/string-width/-/string-width-4.2.3.tgz", + "integrity": "sha512-wKyQRQpjJ0sIp62ErSZdGsjMJWsap5oRNihHhu6G7JVO/9jIB6UyevL+tXuOqrng8j/cxKTWyWUwvSTriiZz/g==", "dev": true, "requires": { "emoji-regex": "^8.0.0", "is-fullwidth-code-point": "^3.0.0", - "strip-ansi": "^6.0.0" + "strip-ansi": "^6.0.1" } }, "strip-ansi": { - "version": "6.0.0", - "resolved": "https://registry.npmjs.org/strip-ansi/-/strip-ansi-6.0.0.tgz", - "integrity": "sha512-AuvKTrTfQNYNIctbR1K/YGTR1756GycPsg7b9bdV9Duqur4gv6aKqHXah67Z8ImS7WEz5QVcOtlfW2rZEugt6w==", + "version": "6.0.1", + "resolved": "https://registry.npmjs.org/strip-ansi/-/strip-ansi-6.0.1.tgz", + "integrity": "sha512-Y38VPSHcqkFrCpFnQ9vuSXmquuv5oXOKpGeT6aGrr3o3Gc9AlVa6JBfUSOCnbxGGZF+/0ooI7KrPuUSztUdU5A==", "dev": true, "requires": { - "ansi-regex": "^5.0.0" + "ansi-regex": "^5.0.1" } }, "strip-bom": { @@ -9556,12 +9365,12 @@ "dev": true }, "ts-node": { - "version": "10.2.1", - "resolved": "https://registry.npmjs.org/ts-node/-/ts-node-10.2.1.tgz", - "integrity": "sha512-hCnyOyuGmD5wHleOQX6NIjJtYVIO8bPP8F2acWkB4W06wdlkgyvJtubO/I9NkI88hCFECbsEgoLc0VNkYmcSfw==", + "version": "10.3.0", + "resolved": "https://registry.npmjs.org/ts-node/-/ts-node-10.3.0.tgz", + "integrity": "sha512-RYIy3i8IgpFH45AX4fQHExrT8BxDeKTdC83QFJkNzkvt8uFB6QJ8XMyhynYiKMLxt9a7yuXaDBZNOYS3XjDcYw==", "dev": true, "requires": { - "@cspotcode/source-map-support": "0.6.1", + "@cspotcode/source-map-support": "0.7.0", "@tsconfig/node10": "^1.0.7", "@tsconfig/node12": "^1.0.7", "@tsconfig/node14": "^1.0.0", @@ -9611,38 +9420,45 @@ } }, "typedoc": { - "version": "0.21.9", - "resolved": "https://registry.npmjs.org/typedoc/-/typedoc-0.21.9.tgz", - "integrity": "sha512-VRo7aII4bnYaBBM1lhw4bQFmUcDQV8m8tqgjtc7oXl87jc1Slbhfw2X5MccfcR2YnEClHDWgsiQGgNB8KJXocA==", + "version": "0.22.5", + "resolved": "https://registry.npmjs.org/typedoc/-/typedoc-0.22.5.tgz", + "integrity": "sha512-KFrWGU1iKiTGw0RcyjLNYDmhd7uICU14HgBNPmFKY/sT4Pm/fraaLyWyisst9vGTUAKxqibqoDITR7+ZcAkhHg==", "dev": true, "requires": { - "glob": "^7.1.7", - "handlebars": "^4.7.7", + "glob": "^7.2.0", "lunr": "^2.3.9", - "marked": "^3.0.2", - "minimatch": "^3.0.0", - "progress": "^2.0.3", - "shiki": "^0.9.8", - "typedoc-default-themes": "^0.12.10" + "marked": "^3.0.4", + "minimatch": "^3.0.4", + "shiki": "^0.9.11" + }, + "dependencies": { + "glob": { + "version": "7.2.0", + "resolved": "https://registry.npmjs.org/glob/-/glob-7.2.0.tgz", + "integrity": "sha512-lmLf6gtyrPq8tTjSmrO94wBeQbFR3HbLHbuyD69wuyQkImp2hWqMGB47OX65FBkPffO641IP9jWa1z4ivqG26Q==", + "dev": true, + "requires": { + "fs.realpath": "^1.0.0", + "inflight": "^1.0.4", + "inherits": "2", + "minimatch": "^3.0.4", + "once": "^1.3.0", + "path-is-absolute": "^1.0.0" + } + } } }, - "typedoc-default-themes": { - "version": "0.12.10", - "resolved": "https://registry.npmjs.org/typedoc-default-themes/-/typedoc-default-themes-0.12.10.tgz", - "integrity": "sha512-fIS001cAYHkyQPidWXmHuhs8usjP5XVJjWB8oZGqkTowZaz3v7g3KDZeeqE82FBrmkAnIBOY3jgy7lnPnqATbA==", - "dev": true - }, "typedoc-github-wiki-theme": { - "version": "0.5.1", - "resolved": "https://registry.npmjs.org/typedoc-github-wiki-theme/-/typedoc-github-wiki-theme-0.5.1.tgz", - "integrity": "sha512-ED2Uc3WUjbv6xIdCkpMz3yBtcEciJnAhDQdRWLYgw4K+FKY0T3PzbI+ssNsBVgwDnYQP/XuaqfZkeQ3EQsOm9g==", + "version": "0.6.0", + "resolved": "https://registry.npmjs.org/typedoc-github-wiki-theme/-/typedoc-github-wiki-theme-0.6.0.tgz", + "integrity": "sha512-uHhR7PwAZ4JgO0KzlLocWSvMqKsSZ/wxUQYGKskIepzsotPAQcAWnSSnGi6gdkSw8UAfIIppdD7H1AmI39962w==", "dev": true, "requires": {} }, "typedoc-plugin-markdown": { - "version": "3.10.4", - "resolved": "https://registry.npmjs.org/typedoc-plugin-markdown/-/typedoc-plugin-markdown-3.10.4.tgz", - "integrity": "sha512-if9w7S9fXLg73AYi/EoRSEhTOZlg3I8mIP8YEmvzSE33VrNXC9/hA0nVcLEwFVJeQY7ay6z67I6kW0KIv7LjeA==", + "version": "3.11.3", + "resolved": "https://registry.npmjs.org/typedoc-plugin-markdown/-/typedoc-plugin-markdown-3.11.3.tgz", + "integrity": "sha512-rWiHbEIe0oZetDIsBR24XJVxGOJ91kDcHoj2KhFKxCLoJGX659EKBQkHne9QJ4W2stGhu1fRgFyQaouSBnxukA==", "dev": true, "requires": { "handlebars": "^4.7.7" @@ -9806,48 +9622,6 @@ "integrity": "sha1-2e8H3Od7mQK4o6j6SzHD4/fm6Ho=", "dev": true }, - "wide-align": { - "version": "1.1.3", - "resolved": "https://registry.npmjs.org/wide-align/-/wide-align-1.1.3.tgz", - "integrity": "sha512-QGkOQc8XL6Bt5PwnsExKBPuMKBxnGxWWW3fU55Xt4feHozMUhdUMaBCk290qpm/wG5u/RSKzwdAC4i51YigihA==", - "dev": true, - "requires": { - "string-width": "^1.0.2 || 2" - }, - "dependencies": { - "ansi-regex": { - "version": "3.0.0", - "resolved": "https://registry.npmjs.org/ansi-regex/-/ansi-regex-3.0.0.tgz", - "integrity": "sha1-7QMXwyIGT3lGbAKWa922Bas32Zg=", - "dev": true - }, - "is-fullwidth-code-point": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/is-fullwidth-code-point/-/is-fullwidth-code-point-2.0.0.tgz", - "integrity": "sha1-o7MKXE8ZkYMWeqq5O+764937ZU8=", - "dev": true - }, - "string-width": { - "version": "2.1.1", - "resolved": "https://registry.npmjs.org/string-width/-/string-width-2.1.1.tgz", - "integrity": "sha512-nOqH59deCq9SRHlxq1Aw85Jnt4w6KvLKqWVik6oA9ZklXLNIOlqg4F2yrT1MVaTjAqvVwdfeZ7w7aCvJD7ugkw==", - "dev": true, - "requires": { - "is-fullwidth-code-point": "^2.0.0", - "strip-ansi": "^4.0.0" - } - }, - "strip-ansi": { - "version": "4.0.0", - "resolved": "https://registry.npmjs.org/strip-ansi/-/strip-ansi-4.0.0.tgz", - "integrity": "sha1-qEeQIusaw2iocTibY1JixQXuNo8=", - "dev": true, - "requires": { - "ansi-regex": "^3.0.0" - } - } - } - }, "widest-line": { "version": "3.1.0", "resolved": "https://registry.npmjs.org/widest-line/-/widest-line-3.1.0.tgz", diff --git a/package.json b/package.json index b2ceadbdd16..7f8720f47bf 100644 --- a/package.json +++ b/package.json @@ -1,6 +1,6 @@ { "name": "redis", - "version": "4.0.0-rc.2", + "version": "4.0.0-rc.3", "description": "A high performance Redis client.", "keywords": [ "database", @@ -34,20 +34,21 @@ }, "devDependencies": { "@istanbuljs/nyc-config-typescript": "^1.0.1", + "@tsconfig/node12": "^1.0.9", "@types/mocha": "^9.0.0", - "@types/node": "^16.9.6", - "@types/sinon": "^10.0.3", + "@types/node": "^16.10.3", + "@types/sinon": "^10.0.4", "@types/which": "^2.0.1", "@types/yallist": "^4.0.1", - "mocha": "^9.1.1", + "mocha": "^9.1.2", "nyc": "^15.1.0", "release-it": "^14.11.6", "sinon": "^11.1.2", "source-map-support": "^0.5.20", - "ts-node": "^10.2.1", - "typedoc": "0.21.9", - "typedoc-github-wiki-theme": "^0.5.1", - "typedoc-plugin-markdown": "3.10.4", + "ts-node": "^10.3.0", + "typedoc": "^0.22.5", + "typedoc-github-wiki-theme": "^0.6.0", + "typedoc-plugin-markdown": "^3.11.3", "typescript": "^4.4.3", "which": "^2.0.2" }, diff --git a/tsconfig.json b/tsconfig.json index deebc9f1252..1f76310034d 100644 --- a/tsconfig.json +++ b/tsconfig.json @@ -1,11 +1,6 @@ { + "extends": "@tsconfig/node12/tsconfig.json", "compilerOptions": { - "strict": true, - "target": "ES2019", - "lib": ["ES2019", "ES2020.BigInt", "ES2020.String", "ES2020.Symbol.WellKnown"], - "module": "CommonJS", - "moduleResolution": "Node", - "esModuleInterop": true, "outDir": "./dist", "declaration": true, "useDefineForClassFields": true, @@ -27,6 +22,7 @@ "./index.ts", "./lib" ], + "entryPointStrategy": "expand", "exclude": [ "./lib/ts-declarations", "./lib/test-utils.ts" From eb11f8386ad36503d165c23927876f9226cbd115 Mon Sep 17 00:00:00 2001 From: leibale Date: Tue, 12 Oct 2021 11:43:53 -0400 Subject: [PATCH 0896/1748] fix README --- README.md | 5 ++--- index.ts | 2 ++ 2 files changed, 4 insertions(+), 3 deletions(-) diff --git a/README.md b/README.md index c768b691d71..073776535e6 100644 --- a/README.md +++ b/README.md @@ -192,7 +192,7 @@ You can override the default options by providing a configuration object: client.scanIterator({ TYPE: 'string', // `SCAN` only MATCH: 'patter*', - COUNT: 100, + COUNT: 100 }); ``` @@ -201,8 +201,7 @@ client.scanIterator({ Define new functions using [Lua scripts](https://redis.io/commands/eval) which execute on the Redis server: ```typescript -import { createClient } from 'redis'; -import { defineScript } from 'redis/lua-script'; +import { createClient, defineScript } from 'redis'; (async () => { const client = createClient({ diff --git a/index.ts b/index.ts index fb448f989c7..408cbe3b996 100644 --- a/index.ts +++ b/index.ts @@ -6,3 +6,5 @@ export const createClient = RedisClient.create; export const commandOptions = RedisClient.commandOptions; export const createCluster = RedisCluster.create; + +export { defineScript } from './lib/lua-script'; From c8f3327ab7408139f7651e6fe83e26d069e082d0 Mon Sep 17 00:00:00 2001 From: leibale Date: Tue, 12 Oct 2021 11:44:16 -0400 Subject: [PATCH 0897/1748] remove whitespace from LICENSE --- LICENSE | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/LICENSE b/LICENSE index 5cb3bb4180b..db86cc4de7f 100644 --- a/LICENSE +++ b/LICENSE @@ -1,4 +1,4 @@ - MIT License +MIT License Copyright (c) 2016-present Node Redis contributors. From bc1ffbc5db92cb4e6d8c972258fcfe877bfb79f1 Mon Sep 17 00:00:00 2001 From: leibale Date: Tue, 12 Oct 2021 13:19:30 -0400 Subject: [PATCH 0898/1748] use "export { x as y }" instead of import & const --- lib/commands/CONFIG_GET.ts | 4 +--- lib/commands/COPY.ts | 4 +--- lib/commands/EXISTS.ts | 4 ++-- lib/commands/EXPIRE.ts | 4 +--- lib/commands/EXPIREAT.ts | 4 ++-- lib/commands/GEOSEARCH_WITH.ts | 4 ++-- lib/commands/HEXISTS.ts | 4 +--- lib/commands/HGETALL.ts | 4 +--- lib/commands/HRANDFIELD_COUNT_WITHVALUES.ts | 3 +-- lib/commands/HSETNX.ts | 4 +--- lib/commands/MOVE.ts | 4 +--- lib/commands/MSETNX.ts | 4 +--- lib/commands/PERSIST.ts | 4 +--- lib/commands/PEXPIRE.ts | 4 +--- lib/commands/PEXPIREAT.ts | 4 ++-- lib/commands/PFADD.ts | 4 ++-- lib/commands/RENAMENX.ts | 4 +--- lib/commands/SCRIPT_EXISTS.ts | 4 ++-- lib/commands/SETNX.ts | 4 +--- lib/commands/SISMEMBER.ts | 4 +--- lib/commands/SMISMEMBER.ts | 4 +--- lib/commands/SMOVE.ts | 4 +--- lib/commands/XCLAIM.ts | 4 ++-- lib/commands/XGROUP_CREATECONSUMER.ts | 4 +--- lib/commands/XGROUP_DESTROY.ts | 4 +--- lib/commands/XPENDING_RANGE.ts | 6 ++---- lib/commands/XRANGE.ts | 4 +--- lib/commands/XREAD.ts | 4 +--- lib/commands/XREADGROUP.ts | 4 +--- lib/commands/XREVRANGE.ts | 4 +--- lib/commands/ZADD.ts | 6 +++--- lib/commands/ZDIFF_WITHSCORES.ts | 3 +-- lib/commands/ZINCRBY.ts | 4 ++-- lib/commands/ZINTER_WITHSCORES.ts | 3 +-- lib/commands/ZMSCORE.ts | 4 ++-- lib/commands/ZPOPMAX_COUNT.ts | 3 +-- lib/commands/ZPOPMIN_COUNT.ts | 3 +-- lib/commands/ZRANDMEMBER_COUNT_WITHSCORES.ts | 3 +-- lib/commands/ZRANGEBYSCORE_WITHSCORES.ts | 3 +-- lib/commands/ZRANGE_WITHSCORES.ts | 3 +-- lib/commands/ZSCORE.ts | 4 +--- lib/commands/ZUNION_WITHSCORES.ts | 3 +-- 42 files changed, 54 insertions(+), 109 deletions(-) diff --git a/lib/commands/CONFIG_GET.ts b/lib/commands/CONFIG_GET.ts index 423683c13a4..35907742e25 100644 --- a/lib/commands/CONFIG_GET.ts +++ b/lib/commands/CONFIG_GET.ts @@ -1,7 +1,5 @@ -import { transformReplyTuples } from './generic-transformers'; - export function transformArguments(parameter: string): Array { return ['CONFIG', 'GET', parameter]; } -export const transformReply = transformReplyTuples; +export { transformReplyTuples as transformReply } from './generic-transformers'; diff --git a/lib/commands/COPY.ts b/lib/commands/COPY.ts index 534b0d9c48c..c7a44e45c3c 100644 --- a/lib/commands/COPY.ts +++ b/lib/commands/COPY.ts @@ -1,5 +1,3 @@ -import { transformReplyBoolean } from './generic-transformers'; - interface CopyCommandOptions { destinationDb?: number; replace?: boolean; @@ -21,4 +19,4 @@ export function transformArguments(source: string, destination: string, options? return args; } -export const transformReply = transformReplyBoolean; +export { transformReplyBoolean as transformReply } from './generic-transformers'; diff --git a/lib/commands/EXISTS.ts b/lib/commands/EXISTS.ts index aac164fc953..5a9c0e3be66 100644 --- a/lib/commands/EXISTS.ts +++ b/lib/commands/EXISTS.ts @@ -1,5 +1,5 @@ import { RedisCommandArguments } from '.'; -import { pushVerdictArguments, transformReplyBoolean } from './generic-transformers'; +import { pushVerdictArguments } from './generic-transformers'; export const FIRST_KEY_INDEX = 1; @@ -9,4 +9,4 @@ export function transformArguments(keys: string | Array): RedisCommandAr return pushVerdictArguments(['EXISTS'], keys); } -export const transformReply = transformReplyBoolean; +export { transformReplyBoolean as transformReply } from './generic-transformers'; diff --git a/lib/commands/EXPIRE.ts b/lib/commands/EXPIRE.ts index 04b0504a6f7..36bcf8b882d 100644 --- a/lib/commands/EXPIRE.ts +++ b/lib/commands/EXPIRE.ts @@ -1,7 +1,5 @@ -import { transformReplyBoolean } from './generic-transformers'; - export function transformArguments(key: string, seconds: number): Array { return ['EXPIRE', key, seconds.toString()]; } -export const transformReply = transformReplyBoolean; +export { transformReplyBoolean as transformReply } from './generic-transformers'; diff --git a/lib/commands/EXPIREAT.ts b/lib/commands/EXPIREAT.ts index b7bfdcaa422..72142e4cb79 100644 --- a/lib/commands/EXPIREAT.ts +++ b/lib/commands/EXPIREAT.ts @@ -1,4 +1,4 @@ -import { transformEXAT, transformReplyBoolean } from './generic-transformers'; +import { transformEXAT } from './generic-transformers'; export function transformArguments(key: string, timestamp: number | Date): Array { return [ @@ -8,4 +8,4 @@ export function transformArguments(key: string, timestamp: number | Date): Array ]; } -export const transformReply = transformReplyBoolean; +export { transformReplyBoolean as transformReply } from './generic-transformers'; diff --git a/lib/commands/GEOSEARCH_WITH.ts b/lib/commands/GEOSEARCH_WITH.ts index cd461c9677f..64c6d88e33c 100644 --- a/lib/commands/GEOSEARCH_WITH.ts +++ b/lib/commands/GEOSEARCH_WITH.ts @@ -1,5 +1,5 @@ import { RedisCommandArguments } from '.'; -import { GeoSearchFrom, GeoSearchBy, GeoReplyWith, GeoSearchOptions, transformGeoMembersWithReply } from './generic-transformers'; +import { GeoSearchFrom, GeoSearchBy, GeoReplyWith, GeoSearchOptions } from './generic-transformers'; import { transformArguments as geoSearchTransformArguments } from './GEOSEARCH'; export { FIRST_KEY_INDEX, IS_READ_ONLY } from './GEOSEARCH'; @@ -20,4 +20,4 @@ export function transformArguments( return args; } -export const transformReply = transformGeoMembersWithReply; +export { transformGeoMembersWithReply as transformReply } from './generic-transformers'; diff --git a/lib/commands/HEXISTS.ts b/lib/commands/HEXISTS.ts index 7cf0b158d92..0eae5e03e52 100644 --- a/lib/commands/HEXISTS.ts +++ b/lib/commands/HEXISTS.ts @@ -1,9 +1,7 @@ -import { transformReplyBoolean } from './generic-transformers'; - export const FIRST_KEY_INDEX = 1; export function transformArguments(key: string, field: string): Array { return ['HEXISTS', key, field]; } -export const transformReply = transformReplyBoolean; +export { transformReplyBoolean as transformReply } from './generic-transformers'; diff --git a/lib/commands/HGETALL.ts b/lib/commands/HGETALL.ts index 8ac14ec4963..55c6d3456a1 100644 --- a/lib/commands/HGETALL.ts +++ b/lib/commands/HGETALL.ts @@ -1,9 +1,7 @@ -import { transformReplyTuples } from './generic-transformers'; - export const FIRST_KEY_INDEX = 1; export function transformArguments(key: string): Array { return ['HGETALL', key]; } -export const transformReply = transformReplyTuples; +export { transformReplyTuples as transformReply } from './generic-transformers'; diff --git a/lib/commands/HRANDFIELD_COUNT_WITHVALUES.ts b/lib/commands/HRANDFIELD_COUNT_WITHVALUES.ts index 53856c1984e..1769cae0b76 100644 --- a/lib/commands/HRANDFIELD_COUNT_WITHVALUES.ts +++ b/lib/commands/HRANDFIELD_COUNT_WITHVALUES.ts @@ -1,4 +1,3 @@ -import { transformReplyTuples } from './generic-transformers'; import { transformArguments as transformHRandFieldCountArguments } from './HRANDFIELD_COUNT'; export { FIRST_KEY_INDEX } from './HRANDFIELD_COUNT'; @@ -10,4 +9,4 @@ export function transformArguments(key: string, count: number): Array { ]; } -export const transformReply = transformReplyTuples; +export { transformReplyTuples as transformReply } from './generic-transformers'; diff --git a/lib/commands/HSETNX.ts b/lib/commands/HSETNX.ts index 0eef8752529..83112f8945e 100644 --- a/lib/commands/HSETNX.ts +++ b/lib/commands/HSETNX.ts @@ -1,9 +1,7 @@ -import { transformReplyBoolean } from './generic-transformers'; - export const FIRST_KEY_INDEX = 1; export function transformArguments(key: string, field: string, value: string): Array { return ['HSETNX', key, field, value]; } -export const transformReply = transformReplyBoolean; +export { transformReplyBoolean as transformReply } from './generic-transformers'; diff --git a/lib/commands/MOVE.ts b/lib/commands/MOVE.ts index 74bb88c5e74..93896a63054 100644 --- a/lib/commands/MOVE.ts +++ b/lib/commands/MOVE.ts @@ -1,7 +1,5 @@ -import { transformReplyBoolean } from './generic-transformers'; - export function transformArguments(key: string, db: number): Array { return ['MOVE', key, db.toString()]; } -export const transformReply = transformReplyBoolean; \ No newline at end of file +export { transformReplyBoolean as transformReply } from './generic-transformers'; diff --git a/lib/commands/MSETNX.ts b/lib/commands/MSETNX.ts index c9c8c840374..fb7b028bc25 100644 --- a/lib/commands/MSETNX.ts +++ b/lib/commands/MSETNX.ts @@ -1,5 +1,3 @@ -import { transformReplyBoolean } from './generic-transformers'; - export const FIRST_KEY_INDEX = 1; export function transformArguments(toSet: Array<[string, string]> | Array | Record): Array { @@ -16,4 +14,4 @@ export function transformArguments(toSet: Array<[string, string]> | Array { return ['PERSIST', key]; } -export const transformReply = transformReplyBoolean; +export { transformReplyBoolean as transformReply } from './generic-transformers'; diff --git a/lib/commands/PEXPIRE.ts b/lib/commands/PEXPIRE.ts index d795f2fc0d0..f3b70279aa7 100644 --- a/lib/commands/PEXPIRE.ts +++ b/lib/commands/PEXPIRE.ts @@ -1,9 +1,7 @@ -import { transformReplyBoolean } from './generic-transformers'; - export const FIRST_KEY_INDEX = 1; export function transformArguments(key: string, milliseconds: number): Array { return ['PEXPIRE', key, milliseconds.toString()]; } -export const transformReply = transformReplyBoolean; +export { transformReplyBoolean as transformReply } from './generic-transformers'; diff --git a/lib/commands/PEXPIREAT.ts b/lib/commands/PEXPIREAT.ts index 91f38f88946..5dded48d278 100644 --- a/lib/commands/PEXPIREAT.ts +++ b/lib/commands/PEXPIREAT.ts @@ -1,4 +1,4 @@ -import { transformPXAT, transformReplyBoolean } from './generic-transformers'; +import { transformPXAT } from './generic-transformers'; export const FIRST_KEY_INDEX = 1; @@ -10,4 +10,4 @@ export function transformArguments(key: string, millisecondsTimestamp: number | ]; } -export const transformReply = transformReplyBoolean; +export { transformReplyBoolean as transformReply } from './generic-transformers'; diff --git a/lib/commands/PFADD.ts b/lib/commands/PFADD.ts index 4328a18dfe5..e45e83c3ae0 100644 --- a/lib/commands/PFADD.ts +++ b/lib/commands/PFADD.ts @@ -1,5 +1,5 @@ import { RedisCommandArguments } from '.'; -import { pushVerdictArguments, transformReplyBoolean } from './generic-transformers'; +import { pushVerdictArguments } from './generic-transformers'; export const FIRST_KEY_INDEX = 1; @@ -7,4 +7,4 @@ export function transformArguments(key: string, element: string | Array) return pushVerdictArguments(['PFADD', key], element); } -export const transformReply = transformReplyBoolean; +export { transformReplyBoolean as transformReply } from './generic-transformers'; diff --git a/lib/commands/RENAMENX.ts b/lib/commands/RENAMENX.ts index 883d2ca2961..2cfec007741 100644 --- a/lib/commands/RENAMENX.ts +++ b/lib/commands/RENAMENX.ts @@ -1,9 +1,7 @@ -import { transformReplyBoolean } from './generic-transformers'; - export const FIRST_KEY_INDEX = 1; export function transformArguments(key: string, newKey: string): Array { return ['RENAMENX', key, newKey]; } -export const transformReply = transformReplyBoolean; +export { transformReplyBoolean as transformReply } from './generic-transformers'; diff --git a/lib/commands/SCRIPT_EXISTS.ts b/lib/commands/SCRIPT_EXISTS.ts index ee89f955e50..d4f65cfd72b 100644 --- a/lib/commands/SCRIPT_EXISTS.ts +++ b/lib/commands/SCRIPT_EXISTS.ts @@ -1,8 +1,8 @@ import { RedisCommandArguments } from '.'; -import { pushVerdictArguments, transformReplyBooleanArray } from './generic-transformers'; +import { pushVerdictArguments } from './generic-transformers'; export function transformArguments(sha1: string | Array): RedisCommandArguments { return pushVerdictArguments(['SCRIPT', 'EXISTS'], sha1); } -export const transformReply = transformReplyBooleanArray; +export { transformReplyBooleanArray as transformReply } from './generic-transformers'; diff --git a/lib/commands/SETNX.ts b/lib/commands/SETNX.ts index f0097836581..b45e93b0f7d 100644 --- a/lib/commands/SETNX.ts +++ b/lib/commands/SETNX.ts @@ -1,9 +1,7 @@ -import { transformReplyBoolean } from './generic-transformers'; - export const FIRST_KEY_INDEX = 1; export function transformArguments(key: string, value: string): Array { return ['SETNX', key, value]; } -export const transformReply = transformReplyBoolean; +export { transformReplyBoolean as transformReply } from './generic-transformers'; diff --git a/lib/commands/SISMEMBER.ts b/lib/commands/SISMEMBER.ts index 661410fce0d..d8c47a76a69 100644 --- a/lib/commands/SISMEMBER.ts +++ b/lib/commands/SISMEMBER.ts @@ -1,9 +1,7 @@ -import { transformReplyBoolean } from './generic-transformers'; - export const FIRST_KEY_INDEX = 1; export function transformArguments(key: string, member: string): Array { return ['SISMEMBER', key, member]; } -export const transformReply = transformReplyBoolean; +export { transformReplyBoolean as transformReply } from './generic-transformers'; diff --git a/lib/commands/SMISMEMBER.ts b/lib/commands/SMISMEMBER.ts index 07637a689b6..85b954bc3d7 100644 --- a/lib/commands/SMISMEMBER.ts +++ b/lib/commands/SMISMEMBER.ts @@ -1,9 +1,7 @@ -import { transformReplyBooleanArray } from './generic-transformers'; - export const FIRST_KEY_INDEX = 1; export function transformArguments(key: string, members: Array): Array { return ['SMISMEMBER', key, ...members]; } -export const transformReply = transformReplyBooleanArray; +export { transformReplyBooleanArray as transformReply } from './generic-transformers'; diff --git a/lib/commands/SMOVE.ts b/lib/commands/SMOVE.ts index f8922f6ca3f..7850b8f9ddc 100644 --- a/lib/commands/SMOVE.ts +++ b/lib/commands/SMOVE.ts @@ -1,9 +1,7 @@ -import { transformReplyBoolean } from './generic-transformers'; - export const FIRST_KEY_INDEX = 1; export function transformArguments(source: string, destination: string, member: string): Array { return ['SMOVE', source, destination, member]; } -export const transformReply = transformReplyBoolean; +export { transformReplyBoolean as transformReply } from './generic-transformers'; diff --git a/lib/commands/XCLAIM.ts b/lib/commands/XCLAIM.ts index c5890a75797..c87d1551e86 100644 --- a/lib/commands/XCLAIM.ts +++ b/lib/commands/XCLAIM.ts @@ -1,4 +1,4 @@ -import { pushVerdictArguments, transformReplyStreamMessages } from './generic-transformers'; +import { pushVerdictArguments } from './generic-transformers'; export const FIRST_KEY_INDEX = 1; @@ -43,4 +43,4 @@ export function transformArguments( return args; } -export const transformReply = transformReplyStreamMessages; +export { transformReplyStreamMessages as transformReply } from './generic-transformers'; diff --git a/lib/commands/XGROUP_CREATECONSUMER.ts b/lib/commands/XGROUP_CREATECONSUMER.ts index 395688706e2..f1a57e6fc42 100644 --- a/lib/commands/XGROUP_CREATECONSUMER.ts +++ b/lib/commands/XGROUP_CREATECONSUMER.ts @@ -1,9 +1,7 @@ -import { transformReplyBoolean } from './generic-transformers'; - export const FIRST_KEY_INDEX = 2; export function transformArguments(key: string, group: string, consumer: string): Array { return ['XGROUP', 'CREATECONSUMER', key, group, consumer]; } -export const transformReply = transformReplyBoolean; +export { transformReplyBoolean as transformReply } from './generic-transformers'; diff --git a/lib/commands/XGROUP_DESTROY.ts b/lib/commands/XGROUP_DESTROY.ts index 1fd25550c33..a4d67e5f4e5 100644 --- a/lib/commands/XGROUP_DESTROY.ts +++ b/lib/commands/XGROUP_DESTROY.ts @@ -1,9 +1,7 @@ -import { transformReplyBoolean } from './generic-transformers'; - export const FIRST_KEY_INDEX = 2; export function transformArguments(key: string, group: string): Array { return ['XGROUP', 'DESTROY', key, group]; } -export const transformReply = transformReplyBoolean; +export { transformReplyBoolean as transformReply } from './generic-transformers'; diff --git a/lib/commands/XPENDING_RANGE.ts b/lib/commands/XPENDING_RANGE.ts index c9e8d898e86..d0b45f0fabb 100644 --- a/lib/commands/XPENDING_RANGE.ts +++ b/lib/commands/XPENDING_RANGE.ts @@ -1,5 +1,3 @@ -import { transformReplyStreamMessages } from './generic-transformers'; - export const FIRST_KEY_INDEX = 1; export const IS_READ_ONLY = true; @@ -24,7 +22,7 @@ export function transformArguments( } args.push(start, end, count.toString()); - + if (options?.consumer) { args.push(options.consumer); } @@ -32,4 +30,4 @@ export function transformArguments( return args; } -export const transformReply = transformReplyStreamMessages; +export { transformReplyStreamMessages as transformReply } from './generic-transformers'; diff --git a/lib/commands/XRANGE.ts b/lib/commands/XRANGE.ts index 2902d0743db..b357266c950 100644 --- a/lib/commands/XRANGE.ts +++ b/lib/commands/XRANGE.ts @@ -1,5 +1,3 @@ -import { transformReplyStreamMessages } from './generic-transformers'; - export const FIRST_KEY_INDEX = 1; export const IS_READ_ONLY = true; @@ -18,4 +16,4 @@ export function transformArguments(key: string, start: string, end: string, opti return args; } -export const transformReply = transformReplyStreamMessages; +export { transformReplyStreamMessages as transformReply } from './generic-transformers'; diff --git a/lib/commands/XREAD.ts b/lib/commands/XREAD.ts index 1f4932837a2..00d8aa959ec 100644 --- a/lib/commands/XREAD.ts +++ b/lib/commands/XREAD.ts @@ -1,5 +1,3 @@ -import { transformReplyStreamsMessages } from './generic-transformers'; - export const FIRST_KEY_INDEX = (streams: Array | XReadStream): string => { return Array.isArray(streams) ? streams[0].key : streams.key; }; @@ -40,4 +38,4 @@ export function transformArguments(streams: Array | XReadStream, op return args; } -export const transformReply = transformReplyStreamsMessages; +export { transformReplyStreamsMessages as transformReply } from './generic-transformers'; diff --git a/lib/commands/XREADGROUP.ts b/lib/commands/XREADGROUP.ts index b01385e7c2f..6d329d377ff 100644 --- a/lib/commands/XREADGROUP.ts +++ b/lib/commands/XREADGROUP.ts @@ -1,5 +1,3 @@ -import { transformReplyStreamsMessages } from './generic-transformers'; - export interface XReadGroupStream { key: string; id: string; @@ -54,4 +52,4 @@ export function transformArguments( return args; } -export const transformReply = transformReplyStreamsMessages; +export { transformReplyStreamsMessages as transformReply } from './generic-transformers'; diff --git a/lib/commands/XREVRANGE.ts b/lib/commands/XREVRANGE.ts index a1fbbbc128a..db48856cdcc 100644 --- a/lib/commands/XREVRANGE.ts +++ b/lib/commands/XREVRANGE.ts @@ -1,5 +1,3 @@ -import { transformReplyStreamMessages } from './generic-transformers'; - interface XRangeRevOptions { COUNT?: number; } @@ -14,4 +12,4 @@ export function transformArguments(key: string, start: string, end: string, opti return args; } -export const transformReply = transformReplyStreamMessages; +export { transformReplyStreamMessages as transformReply } from './generic-transformers'; diff --git a/lib/commands/ZADD.ts b/lib/commands/ZADD.ts index 0e43ecaf508..69f01c8d534 100644 --- a/lib/commands/ZADD.ts +++ b/lib/commands/ZADD.ts @@ -1,4 +1,4 @@ -import { transformArgumentNumberInfinity, transformReplyNumberInfinity, ZMember } from './generic-transformers'; +import { transformArgumentNumberInfinity, ZMember } from './generic-transformers'; export const FIRST_KEY_INDEX = 1; @@ -56,11 +56,11 @@ export function transformArguments(key: string, members: ZMember | Array): return pushVerdictArguments(['ZMSCORE', key], member); } -export const transformReply = transformReplyNumberInfinityNullArray; +export { transformReplyNumberInfinityNullArray as transformReply } from './generic-transformers'; diff --git a/lib/commands/ZPOPMAX_COUNT.ts b/lib/commands/ZPOPMAX_COUNT.ts index abfa8494ec4..abef32cca56 100644 --- a/lib/commands/ZPOPMAX_COUNT.ts +++ b/lib/commands/ZPOPMAX_COUNT.ts @@ -1,4 +1,3 @@ -import { transformReplySortedSetWithScores } from './generic-transformers'; import { transformArguments as transformZPopMaxArguments } from './ZPOPMAX'; export { FIRST_KEY_INDEX } from './ZPOPMAX'; @@ -10,4 +9,4 @@ export function transformArguments(key: string, count: number): Array { ]; } -export const transformReply = transformReplySortedSetWithScores; +export { transformReplySortedSetWithScores as transformReply } from './generic-transformers'; diff --git a/lib/commands/ZPOPMIN_COUNT.ts b/lib/commands/ZPOPMIN_COUNT.ts index e313b32dc8a..7d290ebfe37 100644 --- a/lib/commands/ZPOPMIN_COUNT.ts +++ b/lib/commands/ZPOPMIN_COUNT.ts @@ -1,4 +1,3 @@ -import { transformReplySortedSetWithScores } from './generic-transformers'; import { transformArguments as transformZPopMinArguments } from './ZPOPMIN'; export { FIRST_KEY_INDEX } from './ZPOPMIN'; @@ -10,4 +9,4 @@ export function transformArguments(key: string, count: number): Array { ]; } -export const transformReply = transformReplySortedSetWithScores; +export { transformReplySortedSetWithScores as transformReply } from './generic-transformers'; diff --git a/lib/commands/ZRANDMEMBER_COUNT_WITHSCORES.ts b/lib/commands/ZRANDMEMBER_COUNT_WITHSCORES.ts index 6d79f41c955..8f49b326ad1 100644 --- a/lib/commands/ZRANDMEMBER_COUNT_WITHSCORES.ts +++ b/lib/commands/ZRANDMEMBER_COUNT_WITHSCORES.ts @@ -1,4 +1,3 @@ -import { transformReplySortedSetWithScores } from './generic-transformers'; import { transformArguments as transformZRandMemberCountArguments } from './ZRANDMEMBER_COUNT'; export { FIRST_KEY_INDEX, IS_READ_ONLY } from './ZRANDMEMBER_COUNT'; @@ -10,4 +9,4 @@ export function transformArguments(...args: Parameters { return ['ZSCORE', key, member]; } -export const transformReply = transformReplyNumberInfinityNull; +export { transformReplyNumberInfinityNull as transformReply } from './generic-transformers'; diff --git a/lib/commands/ZUNION_WITHSCORES.ts b/lib/commands/ZUNION_WITHSCORES.ts index d361fd432b0..3b937341921 100644 --- a/lib/commands/ZUNION_WITHSCORES.ts +++ b/lib/commands/ZUNION_WITHSCORES.ts @@ -1,5 +1,4 @@ import { RedisCommandArguments } from '.'; -import { transformReplySortedSetWithScores } from './generic-transformers'; import { transformArguments as transformZUnionArguments } from './ZUNION'; export { FIRST_KEY_INDEX, IS_READ_ONLY } from './ZUNION'; @@ -11,4 +10,4 @@ export function transformArguments(...args: Parameters Date: Wed, 13 Oct 2021 13:37:57 -0400 Subject: [PATCH 0899/1748] move from "NodeRedis" to "Redis" --- README.md | 12 ++++++------ package.json | 16 +++------------- 2 files changed, 9 insertions(+), 19 deletions(-) diff --git a/README.md b/README.md index 073776535e6..23b183f540b 100644 --- a/README.md +++ b/README.md @@ -1,13 +1,13 @@

- +

Node Redis

- - Coverage Status + + Coverage Status Downloads @@ -57,7 +57,7 @@ createClient({ }); ``` -You can also use discrete parameters, UNIX sockets, and even TLS to connect. Details can be found in in the [Wiki](https://github.com/NodeRedis/node-redis/wiki/lib.socket#RedisSocketOptions). +You can also use discrete parameters, UNIX sockets, and even TLS to connect. Details can be found in in the [Wiki](https://github.com/redis/node-redis/wiki/lib.socket#RedisSocketOptions). ### Redis Commands @@ -279,8 +279,8 @@ 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! - - + + ## License diff --git a/package.json b/package.json index 7f8720f47bf..80bf7aa05a6 100644 --- a/package.json +++ b/package.json @@ -8,16 +8,6 @@ "pubsub" ], "author": "Matt Ranney ", - "contributors": [ - { - "name": "Mike Diarmid (Salakar)", - "url": "https://github.com/salakar" - }, - { - "name": "Ruben Bridgewater (BridgeAR)", - "url": "https://github.com/BridgeAR" - } - ], "license": "MIT", "main": "./dist/index.js", "types": "./dist/index.d.ts", @@ -57,10 +47,10 @@ }, "repository": { "type": "git", - "url": "git://github.com/NodeRedis/node-redis.git" + "url": "git://github.com/redis/node-redis.git" }, "bugs": { - "url": "https://github.com/NodeRedis/node-redis/issues" + "url": "https://github.com/redis/node-redis/issues" }, - "homepage": "https://github.com/NodeRedis/node-redis" + "homepage": "https://github.com/redis/node-redis" } From 4b2ad9561646e3db4edfcb407d9be6ace281105b Mon Sep 17 00:00:00 2001 From: leibale Date: Thu, 14 Oct 2021 16:50:39 -0400 Subject: [PATCH 0900/1748] fix #1676 --- lib/client/index.ts | 1 + 1 file changed, 1 insertion(+) diff --git a/lib/client/index.ts b/lib/client/index.ts index 1a5384f509f..7c094e154f3 100644 --- a/lib/client/index.ts +++ b/lib/client/index.ts @@ -213,6 +213,7 @@ export default class RedisClient this.#tick(); }) .on('reconnecting', () => this.emit('reconnecting')) + .on('drain', () => this.#tick()) .on('end', () => this.emit('end')); } From 83ddbbfc0ac94ac65355ff6a2f34645528c9b864 Mon Sep 17 00:00:00 2001 From: leibale Date: Thu, 14 Oct 2021 16:50:48 -0400 Subject: [PATCH 0901/1748] update comments --- lib/cluster/cluster-slots.ts | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/lib/cluster/cluster-slots.ts b/lib/cluster/cluster-slots.ts index 63834d4b4ca..f65411d7974 100644 --- a/lib/cluster/cluster-slots.ts +++ b/lib/cluster/cluster-slots.ts @@ -69,7 +69,7 @@ export default class RedisClusterSlots): Promise { - // Override this.#slots and add not existing clients to this.#clientByKey + // Override this.#slots and add not existing clients to this.#nodeByUrl const promises: Array> = [], clientsInUse = new Set(); for (const master of masters) { @@ -88,7 +88,7 @@ export default class RedisClusterSlots Date: Tue, 19 Oct 2021 05:53:43 +0800 Subject: [PATCH 0902/1748] Auth before select database (#1679) * Auth before select database * fix #1681 Co-authored-by: leibale --- lib/client/index.spec.ts | 12 ++++++++++++ lib/client/index.ts | 32 ++++++++++++++++++++++++++------ lib/client/socket.ts | 17 +++++++++++++++-- lib/test-utils.ts | 11 +++++++++-- 4 files changed, 62 insertions(+), 10 deletions(-) diff --git a/lib/client/index.spec.ts b/lib/client/index.spec.ts index e98814d0582..4d30e9be60b 100644 --- a/lib/client/index.spec.ts +++ b/lib/client/index.spec.ts @@ -119,6 +119,18 @@ describe('Client', () => { assert.equal(client.isOpen, false); }); + + itWithClient(TestRedisServers.PASSWORD, 'should execute AUTH before SELECT', async client => { + assert.equal( + (await client.clientInfo()).db, + 2 + ); + }, { + minimumRedisVersion: [6, 2], + clientOptions: { + database: 2 + } + }); }); describe('legacyMode', () => { diff --git a/lib/client/index.ts b/lib/client/index.ts index 7c094e154f3..8850574e716 100644 --- a/lib/client/index.ts +++ b/lib/client/index.ts @@ -177,24 +177,44 @@ export default class RedisClient #initiateSocket(): RedisSocket { const socketInitiator = async (): Promise => { - const v4Commands = this.#options?.legacyMode ? this.#v4 : this, - promises = []; + const promises = []; if (this.#selectedDB !== 0) { - promises.push(v4Commands.select(RedisClient.commandOptions({ asap: true }), this.#selectedDB)); + promises.push( + this.#queue.addCommand( + ['SELECT', this.#selectedDB.toString()], + { asap: true } + ) + ); } if (this.#options?.readonly) { - promises.push(v4Commands.readonly(RedisClient.commandOptions({ asap: true }))); + promises.push( + this.#queue.addCommand( + COMMANDS.READONLY.transformArguments(), + { asap: true } + ) + ); } if (this.#options?.username || this.#options?.password) { - promises.push(v4Commands.auth(RedisClient.commandOptions({ asap: true }), this.#options)); + promises.push( + this.#queue.addCommand( + COMMANDS.AUTH.transformArguments({ + username: this.#options.username, + password: this.#options.password ?? '' + }), + { asap: true } + ) + ); } const resubscribePromise = this.#queue.resubscribe(); if (resubscribePromise) { promises.push(resubscribePromise); + } + + if (promises.length) { this.#tick(); } @@ -410,7 +430,7 @@ export default class RedisClient quit = this.QUIT; #tick(): void { - if (!this.#socket.isSocketExists) { + if (!this.#socket.isSocketExists || this.#socket.writableNeedDrain) { return; } diff --git a/lib/client/socket.ts b/lib/client/socket.ts index ca48ad4d542..88ae03003aa 100644 --- a/lib/client/socket.ts +++ b/lib/client/socket.ts @@ -76,6 +76,14 @@ export default class RedisSocket extends EventEmitter { return !!this.#socket; } + // `writable.writableNeedDrain` was added in v15.2.0 and therefore can't be used + // https://nodejs.org/api/stream.html#stream_writable_writableneeddrain + #writableNeedDrain = false; + + get writableNeedDrain(): boolean { + return this.#writableNeedDrain; + } + constructor(initiator?: RedisSocketInitiator, options?: RedisSocketOptions) { super(); @@ -163,7 +171,10 @@ export default class RedisSocket extends EventEmitter { this.#onSocketError(new Error('Socket closed unexpectedly')); } }) - .on('drain', () => this.emit('drain')) + .on('drain', () => { + this.#writableNeedDrain = false; + this.emit('drain'); + }) .on('data', (data: Buffer) => this.emit('data', data)); resolve(socket); @@ -198,7 +209,9 @@ export default class RedisSocket extends EventEmitter { throw new ClientClosedError(); } - return this.#socket.write(toWrite); + const wasFullyWritten = this.#socket.write(toWrite); + this.#writableNeedDrain = !wasFullyWritten; + return wasFullyWritten; } async disconnect(ignoreIsOpen = false): Promise { diff --git a/lib/test-utils.ts b/lib/test-utils.ts index 978940ff93d..3b823ac6eed 100644 --- a/lib/test-utils.ts +++ b/lib/test-utils.ts @@ -284,16 +284,23 @@ export function describeHandleMinimumRedisVersion(minimumVersion: PartialRedisVe }); } +interface RedisClientTestOptions extends RedisTestOptions { + clientOptions?: RedisClientOptions<{}, {}>; +} + export function itWithClient( type: TestRedisServers, title: string, fn: (client: RedisClientType) => Promise, - options?: RedisTestOptions + options?: RedisClientTestOptions ): void { it(title, async function () { if (handleMinimumRedisVersion(this, options?.minimumRedisVersion)) return; - const client = RedisClient.create(TEST_REDIS_SERVERS[type]); + const client = RedisClient.create({ + ...TEST_REDIS_SERVERS[type], + ...options?.clientOptions + }); await client.connect(); From 26774f8fa3700623699d7f30361f4c5b7a699e1d Mon Sep 17 00:00:00 2001 From: Simon Prickett Date: Mon, 18 Oct 2021 23:20:00 +0100 Subject: [PATCH 0903/1748] 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. --- .npmignore | 1 + .../connect-as-acl-user.js | 32 +++++ examples/package-lock.json | 110 ++++++++++++++++++ examples/package.json | 14 +++ 4 files changed, 157 insertions(+) create mode 100644 examples/connect-as-acl-user/connect-as-acl-user.js create mode 100644 examples/package-lock.json create mode 100644 examples/package.json diff --git a/.npmignore b/.npmignore index e3fc1a7731d..7e7bb32b111 100644 --- a/.npmignore +++ b/.npmignore @@ -16,3 +16,4 @@ lib/ index.ts *.spec.* dist/lib/test-utils.* +examples/ diff --git a/examples/connect-as-acl-user/connect-as-acl-user.js b/examples/connect-as-acl-user/connect-as-acl-user.js new file mode 100644 index 00000000000..20e3c4c3caf --- /dev/null +++ b/examples/connect-as-acl-user/connect-as-acl-user.js @@ -0,0 +1,32 @@ +// Connect to Redis 6.x as an ACL user. Attempt to run a command +// that the user is allowed to execute, and a command that the +// user is not allowed to execute. + +// Create the test user in redis-cli with this command: +// acl setuser testuser on >testpassword +ping + +import { createClient } from 'redis'; + +const connectWithACLUser = async () => { + const client = createClient({ + url: 'redis://testuser:testpassword@127.0.0.1:6379' + }); + + await client.connect(); + + // Returns PONG + console.log(`Response from PING command: ${await client.ping()}`); + + try { + // This will error as this user is not allowed to run this command... + console.log(`Response from GET command: ${await client.get('somekey')}`); + } catch (e) { + console.log(`GET command failed: ${e.message}`); + } + + await client.quit(); +} + + +connectWithACLUser(); + diff --git a/examples/package-lock.json b/examples/package-lock.json new file mode 100644 index 00000000000..07eff138d1f --- /dev/null +++ b/examples/package-lock.json @@ -0,0 +1,110 @@ +{ + "name": "node-redis-examples", + "version": "1.0.0", + "lockfileVersion": 2, + "requires": true, + "packages": { + "": { + "name": "node-redis-examples", + "version": "1.0.0", + "dependencies": { + "redis": "next" + } + }, + "node_modules/cluster-key-slot": { + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/cluster-key-slot/-/cluster-key-slot-1.1.0.tgz", + "integrity": "sha512-2Nii8p3RwAPiFwsnZvukotvow2rIHM+yQ6ZcBXGHdniadkYGZYiGmkHJIbZPIV9nfv7m/U1IPMVVcAhoWFeklw==", + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/generic-pool": { + "version": "3.8.2", + "resolved": "https://registry.npmjs.org/generic-pool/-/generic-pool-3.8.2.tgz", + "integrity": "sha512-nGToKy6p3PAbYQ7p1UlWl6vSPwfwU6TMSWK7TTu+WUY4ZjyZQGniGGt2oNVvyNSpyZYSB43zMXVLcBm08MTMkg==", + "engines": { + "node": ">= 4" + } + }, + "node_modules/redis": { + "version": "4.0.0-rc.3", + "resolved": "https://registry.npmjs.org/redis/-/redis-4.0.0-rc.3.tgz", + "integrity": "sha512-yvijGYWp3aOvqpFgqovUWLWSdHgjUEtScqJmjTfUXj/4kEHuSW2TERFQelIBnrGeKh8//eYlLpCFKCjDYT4NQw==", + "dependencies": { + "cluster-key-slot": "1.1.0", + "generic-pool": "3.8.2", + "redis-parser": "3.0.0", + "yallist": "4.0.0" + }, + "engines": { + "node": ">=12" + } + }, + "node_modules/redis-errors": { + "version": "1.2.0", + "resolved": "https://registry.npmjs.org/redis-errors/-/redis-errors-1.2.0.tgz", + "integrity": "sha1-62LSrbFeTq9GEMBK/hUpOEJQq60=", + "engines": { + "node": ">=4" + } + }, + "node_modules/redis-parser": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/redis-parser/-/redis-parser-3.0.0.tgz", + "integrity": "sha1-tm2CjNyv5rS4pCin3vTGvKwxyLQ=", + "dependencies": { + "redis-errors": "^1.0.0" + }, + "engines": { + "node": ">=4" + } + }, + "node_modules/yallist": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/yallist/-/yallist-4.0.0.tgz", + "integrity": "sha512-3wdGidZyq5PB084XLES5TpOSRA3wjXAlIWMhum2kRcv/41Sn2emQ0dycQW4uZXLejwKvg6EsvbdlVL+FYEct7A==" + } + }, + "dependencies": { + "cluster-key-slot": { + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/cluster-key-slot/-/cluster-key-slot-1.1.0.tgz", + "integrity": "sha512-2Nii8p3RwAPiFwsnZvukotvow2rIHM+yQ6ZcBXGHdniadkYGZYiGmkHJIbZPIV9nfv7m/U1IPMVVcAhoWFeklw==" + }, + "generic-pool": { + "version": "3.8.2", + "resolved": "https://registry.npmjs.org/generic-pool/-/generic-pool-3.8.2.tgz", + "integrity": "sha512-nGToKy6p3PAbYQ7p1UlWl6vSPwfwU6TMSWK7TTu+WUY4ZjyZQGniGGt2oNVvyNSpyZYSB43zMXVLcBm08MTMkg==" + }, + "redis": { + "version": "4.0.0-rc.3", + "resolved": "https://registry.npmjs.org/redis/-/redis-4.0.0-rc.3.tgz", + "integrity": "sha512-yvijGYWp3aOvqpFgqovUWLWSdHgjUEtScqJmjTfUXj/4kEHuSW2TERFQelIBnrGeKh8//eYlLpCFKCjDYT4NQw==", + "requires": { + "cluster-key-slot": "1.1.0", + "generic-pool": "3.8.2", + "redis-parser": "3.0.0", + "yallist": "4.0.0" + } + }, + "redis-errors": { + "version": "1.2.0", + "resolved": "https://registry.npmjs.org/redis-errors/-/redis-errors-1.2.0.tgz", + "integrity": "sha1-62LSrbFeTq9GEMBK/hUpOEJQq60=" + }, + "redis-parser": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/redis-parser/-/redis-parser-3.0.0.tgz", + "integrity": "sha1-tm2CjNyv5rS4pCin3vTGvKwxyLQ=", + "requires": { + "redis-errors": "^1.0.0" + } + }, + "yallist": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/yallist/-/yallist-4.0.0.tgz", + "integrity": "sha512-3wdGidZyq5PB084XLES5TpOSRA3wjXAlIWMhum2kRcv/41Sn2emQ0dycQW4uZXLejwKvg6EsvbdlVL+FYEct7A==" + } + } +} diff --git a/examples/package.json b/examples/package.json new file mode 100644 index 00000000000..69fff5b5413 --- /dev/null +++ b/examples/package.json @@ -0,0 +1,14 @@ +{ + "name": "node-redis-examples", + "version": "1.0.0", + "description": "node-redis 4 example script", + "main": "index.js", + "private": true, + "scripts": { + }, + "type": "module", + "dependencies": { + "redis": "next" + } +} + From 293a0288b597a31522e63877a2f402ae6a4ab348 Mon Sep 17 00:00:00 2001 From: Simon Prickett Date: Mon, 18 Oct 2021 23:22:01 +0100 Subject: [PATCH 0904/1748] Adds Apple .DS_Store file to .gitignore (#1685) * Adds Apple .DS_Store file. * Add .DS_Store to .npmignore too Co-authored-by: Leibale Eidelman --- .gitignore | 1 + .npmignore | 1 + 2 files changed, 2 insertions(+) diff --git a/.gitignore b/.gitignore index 0bdff14c7ff..8d752019aa0 100644 --- a/.gitignore +++ b/.gitignore @@ -6,3 +6,4 @@ dist/ coverage/ dump.rdb documentation/ +.DS_Store diff --git a/.npmignore b/.npmignore index 7e7bb32b111..115395e3fbb 100644 --- a/.npmignore +++ b/.npmignore @@ -16,4 +16,5 @@ lib/ index.ts *.spec.* dist/lib/test-utils.* +.DS_Store examples/ From 55cfee5d323cbbb594965fb1bc52138a2fae3804 Mon Sep 17 00:00:00 2001 From: leibale Date: Mon, 18 Oct 2021 18:23:50 -0400 Subject: [PATCH 0905/1748] move examples --- examples/{connect-as-acl-user => }/connect-as-acl-user.js | 0 1 file changed, 0 insertions(+), 0 deletions(-) rename examples/{connect-as-acl-user => }/connect-as-acl-user.js (100%) diff --git a/examples/connect-as-acl-user/connect-as-acl-user.js b/examples/connect-as-acl-user.js similarity index 100% rename from examples/connect-as-acl-user/connect-as-acl-user.js rename to examples/connect-as-acl-user.js From ae264ffb0f59a08759761d4a2fe7d711d7d57fe6 Mon Sep 17 00:00:00 2001 From: leibale Date: Mon, 18 Oct 2021 20:22:00 -0400 Subject: [PATCH 0906/1748] clean some tests --- lib/client/index.spec.ts | 64 +++++++++++++++------------------------- lib/test-utils.ts | 14 ++++----- 2 files changed, 30 insertions(+), 48 deletions(-) diff --git a/lib/client/index.spec.ts b/lib/client/index.spec.ts index 4d30e9be60b..2f14e8eecd7 100644 --- a/lib/client/index.spec.ts +++ b/lib/client/index.spec.ts @@ -339,24 +339,18 @@ describe('Client', () => { ); }); - it('with script', async () => { - const client = RedisClient.create({ + itWithClient(TestRedisServers.OPEN, 'with script', async client => { + assert.deepEqual( + await client.multi() + .square(2) + .exec(), + [4] + ); + }, { + clientOptions: { scripts: { square: SQUARE_SCRIPT } - }); - - await client.connect(); - - try { - assert.deepEqual( - await client.multi() - .square(2) - .exec(), - [4] - ); - } finally { - await client.disconnect(); } }); @@ -389,27 +383,26 @@ describe('Client', () => { }); }); - it('scripts', async () => { - const client = RedisClient.create({ + itWithClient(TestRedisServers.OPEN, 'scripts', async client => { + assert.equal( + await client.square(2), + 4 + ); + }, { + clientOptions: { scripts: { square: SQUARE_SCRIPT } - }); - - await client.connect(); - - try { - assert.equal( - await client.square(2), - 4 - ); - } finally { - await client.disconnect(); } }); - it('modules', async () => { - const client = RedisClient.create({ + itWithClient(TestRedisServers.OPEN, 'modules', async client => { + assert.equal( + await client.module.echo('message'), + 'message' + ); + }, { + clientOptions: { modules: { module: { echo: { @@ -422,17 +415,6 @@ describe('Client', () => { } } } - }); - - await client.connect(); - - try { - assert.equal( - await client.module.echo('message'), - 'message' - ); - } finally { - await client.disconnect(); } }); diff --git a/lib/test-utils.ts b/lib/test-utils.ts index 3b823ac6eed..42eb9df500b 100644 --- a/lib/test-utils.ts +++ b/lib/test-utils.ts @@ -52,13 +52,13 @@ export enum TestRedisServers { PASSWORD } -export const TEST_REDIS_SERVERS: Record> = {}; +export const TEST_REDIS_SERVERS: Record, 'modules' | 'scripts'>> = {}; export enum TestRedisClusters { OPEN } -export const TEST_REDIS_CLUSTERES: Record> = {}; +export const TEST_REDIS_CLUSTERES: Record, 'modules' | 'scripts'>> = {}; let port = 6379; @@ -284,15 +284,15 @@ export function describeHandleMinimumRedisVersion(minimumVersion: PartialRedisVe }); } -interface RedisClientTestOptions extends RedisTestOptions { - clientOptions?: RedisClientOptions<{}, {}>; +interface RedisClientTestOptions extends RedisTestOptions { + clientOptions?: RedisClientOptions; } -export function itWithClient( +export function itWithClient( type: TestRedisServers, title: string, - fn: (client: RedisClientType) => Promise, - options?: RedisClientTestOptions + fn: (client: RedisClientType) => Promise, + options?: RedisClientTestOptions ): void { it(title, async function () { if (handleMinimumRedisVersion(this, options?.minimumRedisVersion)) return; From e3ebda1041c90df3840601115e78d7a00b2131e0 Mon Sep 17 00:00:00 2001 From: leibale Date: Mon, 18 Oct 2021 20:34:00 -0400 Subject: [PATCH 0907/1748] clean code --- lib/client/index.ts | 9 ++++----- 1 file changed, 4 insertions(+), 5 deletions(-) diff --git a/lib/client/index.ts b/lib/client/index.ts index 8850574e716..7026142c2fe 100644 --- a/lib/client/index.ts +++ b/lib/client/index.ts @@ -216,9 +216,8 @@ export default class RedisClient if (promises.length) { this.#tick(); + await Promise.all(promises); } - - await Promise.all(promises); }; return new RedisSocket(socketInitiator, this.#options?.socket) @@ -318,9 +317,9 @@ export default class RedisClient } // using `#sendCommand` cause `sendCommand` is overwritten in legacy mode - async #sendCommand(args: RedisCommandArguments, options?: ClientCommandOptions, bufferMode?: boolean): Promise { + #sendCommand(args: RedisCommandArguments, options?: ClientCommandOptions, bufferMode?: boolean): Promise { if (!this.#socket.isOpen) { - throw new ClientClosedError(); + return Promise.reject(new ClientClosedError()); } if (options?.isolated) { @@ -334,7 +333,7 @@ export default class RedisClient const promise = this.#queue.addCommand(args, options, bufferMode); this.#tick(); - return await promise; + return promise; } async scriptsExecutor(script: RedisScript, args: Array): Promise> { From a5eb16595f44781db9d99e99ad0a42b6cecd459b Mon Sep 17 00:00:00 2001 From: Simon Prickett Date: Tue, 19 Oct 2021 14:33:00 +0100 Subject: [PATCH 0908/1748] Adds examples table of contents and contribution guidelines. (#1686) --- examples/README.md | 60 ++++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 60 insertions(+) create mode 100644 examples/README.md diff --git a/examples/README.md b/examples/README.md new file mode 100644 index 00000000000..d1e3b73a054 --- /dev/null +++ b/examples/README.md @@ -0,0 +1,60 @@ +# Node Redis: Examples + +This folder contains example scripts showing how to use Node Redis in different scenarios. + +|File Name |Description | +|------------------------|------------------------------------| +|`connect-as-acl-user.js`|Connect to Redis 6 using an ACL user| + +## Contributing + +We'd love to see more examples here. If you have an idea that you'd like to see included here, submit a Pull Request and we'll be sure to review it! Don't forget to check out our [contributing guide](../CONTRIBUTING.md). + +### Coding Guidelines for Examples + +When adding a new example, please follow these guidelines: + +* Add your code in a single JavaScript file per example, directly in the `examples` folder +* Do not introduce other dependencies in your example +* Give your `.js` file a meaningful name using `-` separators e.g. `adding-to-a-stream.js` +* Indent your code using 2 spaces +* Use the single line `//` comment style and comment your code +* Add a comment at the top of your `.js` file describing what your example does +* Add a comment at the top of your `.js` file describing any Redis commands that need to be run to set up data for your example (try and keep this minimal) +* Use semicolons +* Use `async` and `await` +* Use single quotes, `'hello'` not `"hello"` +* Place your example code in a single `async` function where possible, named according to the file name e.g. `add-to-stream.js` would contain `const addtoStream = async () => { ... };`, and call this function at the end of the file e.g. `addToStream();` +* Unless your example requires a connection string, assume Redis is on the default localhost port 6379 with no password +* Use meaningful example data, let's not use `foo`, `bar`, `baz` etc! +* Leave on empty line at the end of your `.js` file +* Update this `README.md` file to add your example to the table + +Use [connect-as-acl-user.js](connect-as-acl-user.js) as a guide to develop a well formatted example script. + +### Example Template + +Here's a starter template for adding a new example, imagine this is stored in `do-something.js`: + +```javascript +// This comment should describe what the example does +// and can extend to multiple lines. + +// Set up the data in redis-cli using these commands: +// + +import { createClient } from 'redis'; + +const doSomething = async () => { + const client = createClient(); + + await client.connect(); + + // Add your example code here... + + await client.quit(); +} + + +doSomething(); +``` \ No newline at end of file From 46aad787ebc8635d43f96564aade2d0cbc84a804 Mon Sep 17 00:00:00 2001 From: Simon Prickett Date: Tue, 19 Oct 2021 21:13:35 +0100 Subject: [PATCH 0909/1748] Updated examples to use named functions. (#1687) * Updated examples to user named functions. * Update README.md Co-authored-by: Leibale Eidelman --- examples/README.md | 11 +++++------ examples/connect-as-acl-user.js | 4 +--- 2 files changed, 6 insertions(+), 9 deletions(-) diff --git a/examples/README.md b/examples/README.md index d1e3b73a054..25b87683330 100644 --- a/examples/README.md +++ b/examples/README.md @@ -2,9 +2,9 @@ This folder contains example scripts showing how to use Node Redis in different scenarios. -|File Name |Description | -|------------------------|------------------------------------| -|`connect-as-acl-user.js`|Connect to Redis 6 using an ACL user| +| File Name | Description | +|--------------------------|--------------------------------------| +| `connect-as-acl-user.js` | Connect to Redis 6 using an ACL user | ## Contributing @@ -45,7 +45,7 @@ Here's a starter template for adding a new example, imagine this is stored in `d import { createClient } from 'redis'; -const doSomething = async () => { +async function doSomething() { const client = createClient(); await client.connect(); @@ -55,6 +55,5 @@ const doSomething = async () => { await client.quit(); } - doSomething(); -``` \ No newline at end of file +``` diff --git a/examples/connect-as-acl-user.js b/examples/connect-as-acl-user.js index 20e3c4c3caf..26e1e443b0a 100644 --- a/examples/connect-as-acl-user.js +++ b/examples/connect-as-acl-user.js @@ -7,7 +7,7 @@ import { createClient } from 'redis'; -const connectWithACLUser = async () => { +async function connectWithACLUser() { const client = createClient({ url: 'redis://testuser:testpassword@127.0.0.1:6379' }); @@ -27,6 +27,4 @@ const connectWithACLUser = async () => { await client.quit(); } - connectWithACLUser(); - From 2a7a7c1c2e484950ceb57497f786658dacf19127 Mon Sep 17 00:00:00 2001 From: leibale Date: Tue, 19 Oct 2021 16:20:02 -0400 Subject: [PATCH 0910/1748] 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 --- .eslintrc.json | 12 + .github/workflows/tests.yml | 2 +- README.md | 61 +- SECURITY.md | 6 +- docs/clustering.md | 54 + lib/client/commands-queue.ts | 9 +- lib/client/commands.ts | 2 +- lib/client/index.spec.ts | 5 +- lib/client/index.ts | 31 +- lib/client/multi-command.ts | 6 +- lib/client/socket.ts | 1 - lib/cluster/index.ts | 17 +- lib/cluster/multi-command.ts | 6 +- lib/commands/COMMAND_COUNT.ts | 2 +- lib/commands/COMMAND_GETKEYS.ts | 2 +- lib/commands/SET.ts | 52 +- package-lock.json | 2105 +++++++++++++++++++++++++------ package.json | 12 +- 18 files changed, 1894 insertions(+), 491 deletions(-) create mode 100644 .eslintrc.json create mode 100644 docs/clustering.md diff --git a/.eslintrc.json b/.eslintrc.json new file mode 100644 index 00000000000..9378001252c --- /dev/null +++ b/.eslintrc.json @@ -0,0 +1,12 @@ +{ + "root": true, + "parser": "@typescript-eslint/parser", + "plugins": [ + "@typescript-eslint" + ], + "extends": [ + "eslint:recommended", + "plugin:@typescript-eslint/eslint-recommended", + "plugin:@typescript-eslint/recommended" + ] + } diff --git a/.github/workflows/tests.yml b/.github/workflows/tests.yml index 557d4f452dc..7ef80685d56 100644 --- a/.github/workflows/tests.yml +++ b/.github/workflows/tests.yml @@ -13,7 +13,7 @@ jobs: fail-fast: false matrix: node-version: [12.x, 14.x, 16.x] - redis-version: [5.x, 6.x] + redis-version: [5.x, 6.0.x, 6.2.x] steps: - uses: actions/checkout@v2.3.4 diff --git a/README.md b/README.md index 23b183f540b..63fe9d2a522 100644 --- a/README.md +++ b/README.md @@ -57,7 +57,7 @@ createClient({ }); ``` -You can also use discrete parameters, UNIX sockets, and even TLS to connect. Details can be found in in the [Wiki](https://github.com/redis/node-redis/wiki/lib.socket#RedisSocketOptions). +You can also use discrete parameters, UNIX sockets, and even TLS to connect. Details can be found in the [client configuration guide](./docs/client-configuration.md). ### Redis Commands @@ -227,32 +227,34 @@ import { createClient, defineScript } from 'redis'; })(); ``` -### Cluster +### Disconnecting -Connecting to a cluster is a bit different. Create the client by specifying some (or all) of the nodes in your cluster and then use it like a non-clustered client: +There are two functions that disconnect a client from the Redis server. In most scenarios you should use `.quit()` to ensure that pending commands are sent to Redis before closing a connection. -```typescript -import { createCluster } from 'redis'; +#### `.QUIT()`/`.quit()` -(async () => { - const cluster = createCluster({ - rootNodes: [ - { - url: 'redis://10.0.0.1:30001' - }, - { - url: 'redis://10.0.0.2:30002' - } - ] - }); +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 +} +``` - cluster.on('error', (err) => console.log('Redis Cluster Error', err)); +#### `.disconnect()` - await cluster.connect(); +Forcibly close a client's connection to Redis immediately. Calling `disconnect` will not send further pending commands to the Redis server, or wait for or parse outstanding responses. - await cluster.set('key', 'value'); - const value = await cluster.get('key'); -})(); +```typescript +await client.disconnect(); ``` ### Auto-Pipelining @@ -273,6 +275,23 @@ await Promise.all([ ]); ``` +### Clustering + +Check out the [Clustering Guide](./docs/clustering.md) when using Node Redis to connect to a Redis Cluster. + +## Supported Redis versions + +Node Redis is supported with the following versions of Redis: + +| Version | Supported | +|---------|--------------------| +| 6.2.z | :heavy_check_mark: | +| 6.0.z | :heavy_check_mark: | +| 5.y.z | :heavy_check_mark: | +| < 5.0 | :x: | + +> Node Redis should work with older versions of Redis, but it is not fully tested and we cannot offer support. + ## Contributing If you'd like to contribute, check out the [contributing guide](CONTRIBUTING.md). diff --git a/SECURITY.md b/SECURITY.md index 0839a123c96..f96aa68dc12 100644 --- a/SECURITY.md +++ b/SECURITY.md @@ -5,9 +5,9 @@ Node Redis is generally backwards compatible with very few exceptions, so we recommend users to always use the latest version to experience stability, performance and security. | Version | Supported | -| ------- | ------------------ | -| 4.0.x | :white_check_mark: | -| 3.1.x | :white_check_mark: | +|---------|--------------------| +| 4.0.z | :heavy_check_mark: | +| 3.1.z | :heavy_check_mark: | | < 3.1 | :x: | ## Reporting a Vulnerability diff --git a/docs/clustering.md b/docs/clustering.md new file mode 100644 index 00000000000..028ebea81d5 --- /dev/null +++ b/docs/clustering.md @@ -0,0 +1,54 @@ +# Clustering + +## Basic Example + +Connecting to a cluster is a bit different. Create the client by specifying some (or all) of the nodes in your cluster and then use it like a regular client instance: + +```typescript +import { createCluster } from 'redis'; + +(async () => { + const cluster = createCluster({ + rootNodes: [ + { + url: 'redis://10.0.0.1:30001' + }, + { + url: 'redis://10.0.0.2:30002' + } + ] + }); + + cluster.on('error', (err) => console.log('Redis Cluster Error', err)); + + await cluster.connect(); + + await cluster.set('key', 'value'); + const value = await cluster.get('key'); +})(); +``` + +## `createCluster` configuration + +> See the [client configuration](./client-configuration.md) page for the `rootNodes` and `defaults` configuration schemas. + +| Property | Default | Description | +|------------------------|---------|---------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------| +| rootNodes | | An array of root nodes that are part of the cluster, which will be used to get the cluster topology. Each element in the array is a client configuration object. There is no need to specify every node in the cluster, 3 should be enough to reliably connect and obtain the cluster configuration from the server | +| defaults | | The default configuration values for every client in the cluster. Use this for example when specifying an ACL user to connect with | +| useReplicas | `false` | When `true`, distribute load by executing readonly commands (such as `GET`, `GEOSEARCH`, etc.) across all cluster nodes. When `false`, only use master nodes | +| maxCommandRedirections | `16` | The maximum number of times a command will be redirected due to `MOVED` or `ASK` errors | | + +## Command Routing + +### Commands that operate on Redis Keys + +Commands such as `GET`, `SET`, etc. will be routed by the first key, for instance `MGET 1 2 3` will be routed by the key `1`. + +### [Server Commands][https://redis.io/commands#server] + +Admin commands such as `MEMORY STATS`, `FLUSHALL`, etc. are not attached to the cluster, and should be executed on a specific node using `.getSlot()` or `.getAllMasters()`. + +### "Forwarded Commands" + +Some commands (e.g. `PUBLISH`) are forwarded to other cluster nodes by the Redis server. The client will send these commands to a random node in order to spread the load across the cluster. diff --git a/lib/client/commands-queue.ts b/lib/client/commands-queue.ts index 791c7638bad..c07e582238c 100644 --- a/lib/client/commands-queue.ts +++ b/lib/client/commands-queue.ts @@ -6,20 +6,20 @@ import { RedisCommandRawReply } from '../commands'; export interface QueueCommandOptions { asap?: boolean; chainId?: symbol; - signal?: any; // TODO: `AbortSignal` type is incorrect + signal?: AbortSignal; } interface CommandWaitingToBeSent extends CommandWaitingForReply { args: Array; chainId?: symbol; abort?: { - signal: any; // TODO: `AbortSignal` type is incorrect + signal: AbortSignal; listener(): void; }; } interface CommandWaitingForReply { - resolve(reply?: any): void; + resolve(reply?: unknown): void; reject(err: Error): void; channelsCounter?: number; bufferMode?: boolean; @@ -135,7 +135,8 @@ export default class RedisCommandsQueue { signal: options.signal, listener }; - options.signal.addEventListener('abort', listener, { + // AbortSignal type is incorrent + (options.signal as any).addEventListener('abort', listener, { once: true }); } diff --git a/lib/client/commands.ts b/lib/client/commands.ts index c34f34be4fa..de901152f11 100644 --- a/lib/client/commands.ts +++ b/lib/client/commands.ts @@ -229,5 +229,5 @@ export default { UNWATCH, unwatch: UNWATCH, WAIT, - wait: WAIT, + wait: WAIT }; diff --git a/lib/client/index.spec.ts b/lib/client/index.spec.ts index 2f14e8eecd7..3e1bbd6aad4 100644 --- a/lib/client/index.spec.ts +++ b/lib/client/index.spec.ts @@ -611,8 +611,9 @@ describe('Client', () => { const promise = assert.rejects(client.connect(), ConnectionTimeoutError), start = process.hrtime.bigint(); - // block the event loop for 1ms, to make sure the connection will timeout - while (process.hrtime.bigint() - start < 1_000_000) {} + while (process.hrtime.bigint() - start < 1_000_000) { + // block the event loop for 1ms, to make sure the connection will timeout + } await promise; } catch (err) { diff --git a/lib/client/index.ts b/lib/client/index.ts index 7026142c2fe..123c3f543f6 100644 --- a/lib/client/index.ts +++ b/lib/client/index.ts @@ -34,16 +34,16 @@ type WithCommands = { }; export type WithModules = { - [P in keyof M]: { + [P in keyof M as M[P] extends never ? never : P]: { [C in keyof M[P]]: RedisClientCommandSignature; }; }; export type WithScripts = { - [P in keyof S]: RedisClientCommandSignature; + [P in keyof S as S[P] extends never ? never : P]: RedisClientCommandSignature; }; -export type RedisClientType = +export type RedisClientType, S extends RedisScripts = Record> = RedisClient & WithCommands & WithModules & WithScripts; export type InstantiableRedisClient = @@ -53,12 +53,14 @@ export interface ClientCommandOptions extends QueueCommandOptions { isolated?: boolean; } +type ClientLegacyCallback = (err: Error | null, reply?: RedisCommandRawReply) => void; + export default class RedisClient extends EventEmitter { static commandOptions(options: ClientCommandOptions): CommandOptions { return commandOptions(options); } - static extend(plugins?: RedisPlugins): InstantiableRedisClient { + static extend, S extends RedisScripts = Record>(plugins?: RedisPlugins): InstantiableRedisClient { const Client = extendWithModulesAndScripts({ BaseClass: RedisClient, modules: plugins?.modules, @@ -74,14 +76,14 @@ export default class RedisClient return Client; } - static create(options?: RedisClientOptions): RedisClientType { + static create, S extends RedisScripts = Record>(options?: RedisClientOptions): RedisClientType { return new (RedisClient.extend(options))(options); } - static parseURL(url: string): RedisClientOptions<{}, {}> { + static parseURL(url: string): RedisClientOptions, Record> { // https://www.iana.org/assignments/uri-schemes/prov/redis const { hostname, port, protocol, username, password, pathname } = new URL(url), - parsed: RedisClientOptions<{}, {}> = { + parsed: RedisClientOptions, Record> = { socket: { host: hostname } @@ -245,10 +247,12 @@ export default class RedisClient (this as any).#v4.sendCommand = this.#sendCommand.bind(this); (this as any).sendCommand = (...args: Array): void => { - const callback = typeof args[args.length - 1] === 'function' ? args[args.length - 1] as Function : undefined, + const callback = typeof args[args.length - 1] === 'function' ? + args[args.length - 1] as ClientLegacyCallback : + undefined, actualArgs = !callback ? args : args.slice(0, -1); this.#sendCommand(actualArgs.flat() as Array) - .then((reply: unknown) => { + .then((reply: RedisCommandRawReply) => { if (!callback) return; // https://github.com/NodeRedis/node-redis#commands:~:text=minimal%20parsing @@ -435,17 +439,12 @@ export default class RedisClient this.#socket.cork(); - while (true) { + while (!this.#socket.writableNeedDrain) { const args = this.#queue.getCommandToSend(); if (args === undefined) break; - let writeResult; for (const toWrite of encodeCommand(args)) { - writeResult = this.#socket.write(toWrite); - } - - if (!writeResult) { - break; + this.#socket.write(toWrite); } } } diff --git a/lib/client/multi-command.ts b/lib/client/multi-command.ts index ba02c2b9aa8..e4b2c165c8f 100644 --- a/lib/client/multi-command.ts +++ b/lib/client/multi-command.ts @@ -11,16 +11,16 @@ type WithCommands = { }; type WithModules = { - [P in keyof M]: { + [P in keyof M as M[P] extends never ? never : P]: { [C in keyof M[P]]: RedisClientMultiCommandSignature; }; }; type WithScripts = { - [P in keyof S]: RedisClientMultiCommandSignature + [P in keyof S as S[P] extends never ? never : P]: RedisClientMultiCommandSignature }; -export type RedisClientMultiCommandType = +export type RedisClientMultiCommandType, S extends RedisScripts = Record> = RedisClientMultiCommand & WithCommands & WithModules & WithScripts; export type RedisClientMultiExecutor = (queue: Array, chainId?: symbol) => Promise>; diff --git a/lib/client/socket.ts b/lib/client/socket.ts index 88ae03003aa..d5309483979 100644 --- a/lib/client/socket.ts +++ b/lib/client/socket.ts @@ -234,7 +234,6 @@ export default class RedisSocket extends EventEmitter { this.#isOpen = false; - try { await fn(); await this.disconnect(true); diff --git a/lib/cluster/index.ts b/lib/cluster/index.ts index aeaabecae35..fcf6d4754bb 100644 --- a/lib/cluster/index.ts +++ b/lib/cluster/index.ts @@ -1,5 +1,5 @@ import COMMANDS from './commands'; -import { RedisCommand, RedisCommandArguments, RedisCommandReply, RedisModules, RedisScript, RedisScripts } from '../commands'; +import { RedisCommand, RedisCommandArguments, RedisCommandReply, RedisModules, RedisPlugins, RedisScript, RedisScripts } from '../commands'; import { ClientCommandOptions, RedisClientCommandSignature, RedisClientOptions, RedisClientType, WithModules, WithScripts } from '../client'; import RedisClusterSlots, { ClusterNode } from './cluster-slots'; import { extendWithModulesAndScripts, transformCommandArguments, transformCommandReply, extendWithCommands } from '../commander'; @@ -7,14 +7,9 @@ import { EventEmitter } from 'events'; import RedisClusterMultiCommand, { RedisClusterMultiCommandType } from './multi-command'; import { RedisMultiQueuedCommand } from '../multi-command'; -export type RedisClusterClientOptions = Omit, 'modules' | 'scripts'>; +export type RedisClusterClientOptions = Omit, Record>, 'modules' | 'scripts'>; -export interface RedisClusterPlugins { - modules?: M; - scripts?: S; -} - -export interface RedisClusterOptions extends RedisClusterPlugins { +export interface RedisClusterOptions extends RedisPlugins { rootNodes: Array; defaults?: Partial; useReplicas?: boolean; @@ -25,10 +20,10 @@ type WithCommands = { [P in keyof typeof COMMANDS]: RedisClientCommandSignature<(typeof COMMANDS)[P]>; }; -export type RedisClusterType = +export type RedisClusterType, S extends RedisScripts = Record> = RedisCluster & WithCommands & WithModules & WithScripts; -export default class RedisCluster extends EventEmitter { +export default class RedisCluster, S extends RedisScripts = Record> extends EventEmitter { static extractFirstKey(command: RedisCommand, originalArgs: Array, redisArgs: RedisCommandArguments): string | Buffer | undefined { if (command.FIRST_KEY_INDEX === undefined) { return undefined; @@ -39,7 +34,7 @@ export default class RedisCluster(options?: RedisClusterOptions): RedisClusterType { + static create, S extends RedisScripts = Record>(options?: RedisClusterOptions): RedisClusterType { return new (extendWithModulesAndScripts({ BaseClass: RedisCluster, modules: options?.modules, diff --git a/lib/cluster/multi-command.ts b/lib/cluster/multi-command.ts index 8ffd39a90e0..8a798ca9092 100644 --- a/lib/cluster/multi-command.ts +++ b/lib/cluster/multi-command.ts @@ -12,16 +12,16 @@ type WithCommands = { }; type WithModules = { - [P in keyof M]: { + [P in keyof M as M[P] extends never ? never : P]: { [C in keyof M[P]]: RedisClusterMultiCommandSignature; }; }; type WithScripts = { - [P in keyof S]: RedisClusterMultiCommandSignature + [P in keyof S as S[P] extends never ? never : P]: RedisClusterMultiCommandSignature }; -export type RedisClusterMultiCommandType = +export type RedisClusterMultiCommandType, S extends RedisScripts = Record> = RedisClusterMultiCommand & WithCommands & WithModules & WithScripts; export type RedisClusterMultiExecutor = (queue: Array, firstKey?: string | Buffer, chainId?: symbol) => Promise>; diff --git a/lib/commands/COMMAND_COUNT.ts b/lib/commands/COMMAND_COUNT.ts index 5b8283bcc66..34c6a088da6 100644 --- a/lib/commands/COMMAND_COUNT.ts +++ b/lib/commands/COMMAND_COUNT.ts @@ -6,4 +6,4 @@ export function transformArguments(): RedisCommandArguments { return ['COMMAND', 'COUNT']; } -declare function transformReply(): number; +export declare function transformReply(): number; diff --git a/lib/commands/COMMAND_GETKEYS.ts b/lib/commands/COMMAND_GETKEYS.ts index caf342088fb..1c38515aef9 100644 --- a/lib/commands/COMMAND_GETKEYS.ts +++ b/lib/commands/COMMAND_GETKEYS.ts @@ -6,4 +6,4 @@ export function transformArguments(args: Array): RedisCommandArguments { return ['COMMAND', 'GETKEYS', ...args]; } -declare function transformReply(): Array; +export declare function transformReply(): Array; diff --git a/lib/commands/SET.ts b/lib/commands/SET.ts index b19a1b2c5c2..fdc7eef1986 100644 --- a/lib/commands/SET.ts +++ b/lib/commands/SET.ts @@ -2,43 +2,27 @@ import { RedisCommandArguments } from '.'; export const FIRST_KEY_INDEX = 1; -interface EX { - EX: number; -} - -interface PX { - PX: number -} +type MaximumOneOf = + K extends keyof T ? { [P in K]?: T[K] } & Partial, never>> : never; -interface EXAT { +type SetTTL = MaximumOneOf<{ + EX: number; + PX: number; EXAT: number; -} - -interface PXAT { PXAT: number; -} - -interface KEEPTTL { KEEPTTL: true; -} +}>; -type SetTTL = EX | PX | EXAT | PXAT | KEEPTTL | {}; - -interface NX { +type SetGuards = MaximumOneOf<{ NX: true; -} - -interface XX { XX: true; -} - -type SetGuards = NX | XX | {}; +}>; interface SetCommonOptions { - GET: true + GET?: true; } -type SetOptions = SetTTL & SetGuards & (SetCommonOptions | {}); +type SetOptions = SetTTL & SetGuards & SetCommonOptions; export function transformArguments(key: string | Buffer, value: string | Buffer, options?: SetOptions): RedisCommandArguments { const args = ['SET', key, value]; @@ -47,25 +31,25 @@ export function transformArguments(key: string | Buffer, value: string | Buffer, return args; } - if ('EX' in options) { + if (options.EX) { args.push('EX', options.EX.toString()); - } else if ('PX' in options) { + } else if (options.PX) { args.push('PX', options.PX.toString()); - } else if ('EXAT' in options) { + } else if (options.EXAT) { args.push('EXAT', options.EXAT.toString()); - } else if ('PXAT' in options) { + } else if (options.PXAT) { args.push('PXAT', options.PXAT.toString()); - } else if ((options).KEEPTTL) { + } else if (options.KEEPTTL) { args.push('KEEPTTL'); } - if ((options).NX) { + if (options.NX) { args.push('NX'); - } else if ((options).XX) { + } else if (options.XX) { args.push('XX'); } - if ((options).GET) { + if (options.GET) { args.push('GET'); } diff --git a/package-lock.json b/package-lock.json index f47208d6c9d..520aedc3278 100644 --- a/package-lock.json +++ b/package-lock.json @@ -18,20 +18,23 @@ "@istanbuljs/nyc-config-typescript": "^1.0.1", "@tsconfig/node12": "^1.0.9", "@types/mocha": "^9.0.0", - "@types/node": "^16.10.3", + "@types/node": "^16.11.1", "@types/sinon": "^10.0.4", "@types/which": "^2.0.1", "@types/yallist": "^4.0.1", - "mocha": "^9.1.2", + "@typescript-eslint/eslint-plugin": "^5.1.0", + "@typescript-eslint/parser": "^5.1.0", + "eslint": "^8.0.1", + "mocha": "^9.1.3", "nyc": "^15.1.0", "release-it": "^14.11.6", "sinon": "^11.1.2", "source-map-support": "^0.5.20", "ts-node": "^10.3.0", - "typedoc": "^0.22.5", + "typedoc": "^0.22.6", "typedoc-github-wiki-theme": "^0.6.0", "typedoc-plugin-markdown": "^3.11.3", - "typescript": "^4.4.3", + "typescript": "^4.4.4", "which": "^2.0.2" }, "engines": { @@ -89,6 +92,15 @@ "url": "https://opencollective.com/babel" } }, + "node_modules/@babel/core/node_modules/semver": { + "version": "6.3.0", + "resolved": "https://registry.npmjs.org/semver/-/semver-6.3.0.tgz", + "integrity": "sha512-b39TBaTSfV6yBrapU89p5fKekE2m/NwnDocOVruQFS1/veMgdzuPcnOM34M6CwxW8jH/lxEa5rBoDeUwu5HHTw==", + "dev": true, + "bin": { + "semver": "bin/semver.js" + } + }, "node_modules/@babel/generator": { "version": "7.15.8", "resolved": "https://registry.npmjs.org/@babel/generator/-/generator-7.15.8.tgz", @@ -121,6 +133,15 @@ "@babel/core": "^7.0.0" } }, + "node_modules/@babel/helper-compilation-targets/node_modules/semver": { + "version": "6.3.0", + "resolved": "https://registry.npmjs.org/semver/-/semver-6.3.0.tgz", + "integrity": "sha512-b39TBaTSfV6yBrapU89p5fKekE2m/NwnDocOVruQFS1/veMgdzuPcnOM34M6CwxW8jH/lxEa5rBoDeUwu5HHTw==", + "dev": true, + "bin": { + "semver": "bin/semver.js" + } + }, "node_modules/@babel/helper-function-name": { "version": "7.15.4", "resolved": "https://registry.npmjs.org/@babel/helper-function-name/-/helper-function-name-7.15.4.tgz", @@ -416,6 +437,15 @@ "node": ">=6.9.0" } }, + "node_modules/@babel/traverse/node_modules/globals": { + "version": "11.12.0", + "resolved": "https://registry.npmjs.org/globals/-/globals-11.12.0.tgz", + "integrity": "sha512-WOBp/EEGUiIsJSp7wcv/y6MO+lV9UoncWqxuFfm8eBwzWNgyfBd6Gz+IeKQ9jCmyhoH99g15M3T+QaVHFjizVA==", + "dev": true, + "engines": { + "node": ">=4" + } + }, "node_modules/@babel/types": { "version": "7.15.6", "resolved": "https://registry.npmjs.org/@babel/types/-/types-7.15.6.tgz", @@ -450,6 +480,77 @@ "node": ">=12" } }, + "node_modules/@eslint/eslintrc": { + "version": "1.0.3", + "resolved": "https://registry.npmjs.org/@eslint/eslintrc/-/eslintrc-1.0.3.tgz", + "integrity": "sha512-DHI1wDPoKCBPoLZA3qDR91+3te/wDSc1YhKg3jR8NxKKRJq2hwHwcWv31cSwSYvIBrmbENoYMWcenW8uproQqg==", + "dev": true, + "dependencies": { + "ajv": "^6.12.4", + "debug": "^4.3.2", + "espree": "^9.0.0", + "globals": "^13.9.0", + "ignore": "^4.0.6", + "import-fresh": "^3.2.1", + "js-yaml": "^3.13.1", + "minimatch": "^3.0.4", + "strip-json-comments": "^3.1.1" + }, + "engines": { + "node": "^12.22.0 || ^14.17.0 || >=16.0.0" + } + }, + "node_modules/@eslint/eslintrc/node_modules/argparse": { + "version": "1.0.10", + "resolved": "https://registry.npmjs.org/argparse/-/argparse-1.0.10.tgz", + "integrity": "sha512-o5Roy6tNG4SL/FOkCAN6RzjiakZS25RLYFrcMttJqbdd8BWrnA+fGz57iN5Pb06pvBGvl5gQ0B48dJlslXvoTg==", + "dev": true, + "dependencies": { + "sprintf-js": "~1.0.2" + } + }, + "node_modules/@eslint/eslintrc/node_modules/ignore": { + "version": "4.0.6", + "resolved": "https://registry.npmjs.org/ignore/-/ignore-4.0.6.tgz", + "integrity": "sha512-cyFDKrqc/YdcWFniJhzI42+AzS+gNwmUzOSFcRCQYwySuBBBy/KjuxWLZ/FHEH6Moq1NizMOBWyTcv8O4OZIMg==", + "dev": true, + "engines": { + "node": ">= 4" + } + }, + "node_modules/@eslint/eslintrc/node_modules/js-yaml": { + "version": "3.14.1", + "resolved": "https://registry.npmjs.org/js-yaml/-/js-yaml-3.14.1.tgz", + "integrity": "sha512-okMH7OXXJ7YrN9Ok3/SXrnu4iX9yOk+25nqX4imS2npuvTYDmo/QEZoqwZkYaIDk3jVvBOTOIEgEhaLOynBS9g==", + "dev": true, + "dependencies": { + "argparse": "^1.0.7", + "esprima": "^4.0.0" + }, + "bin": { + "js-yaml": "bin/js-yaml.js" + } + }, + "node_modules/@humanwhocodes/config-array": { + "version": "0.6.0", + "resolved": "https://registry.npmjs.org/@humanwhocodes/config-array/-/config-array-0.6.0.tgz", + "integrity": "sha512-JQlEKbcgEUjBFhLIF4iqM7u/9lwgHRBcpHrmUNCALK0Q3amXN6lxdoXLnF0sm11E9VqTmBALR87IlUg1bZ8A9A==", + "dev": true, + "dependencies": { + "@humanwhocodes/object-schema": "^1.2.0", + "debug": "^4.1.1", + "minimatch": "^3.0.4" + }, + "engines": { + "node": ">=10.10.0" + } + }, + "node_modules/@humanwhocodes/object-schema": { + "version": "1.2.0", + "resolved": "https://registry.npmjs.org/@humanwhocodes/object-schema/-/object-schema-1.2.0.tgz", + "integrity": "sha512-wdppn25U8z/2yiaT6YGquE6X8sSv7hNMWSXYSSU1jGv/yd6XqjXgTDJ8KP4NgjTXfJ3GbRjeeb8RTV7a/VpM+w==", + "dev": true + }, "node_modules/@iarna/toml": { "version": "2.2.5", "resolved": "https://registry.npmjs.org/@iarna/toml/-/toml-2.2.5.tgz", @@ -546,6 +647,15 @@ "node": ">=8" } }, + "node_modules/@istanbuljs/load-nyc-config/node_modules/resolve-from": { + "version": "5.0.0", + "resolved": "https://registry.npmjs.org/resolve-from/-/resolve-from-5.0.0.tgz", + "integrity": "sha512-qYg9KP24dD5qka9J47d0aVky0N+b4fTU89LN9iDnjB5waksiC49rvMB0PrUJQGoTmH50XPiqOvAjDfaijGxYZw==", + "dev": true, + "engines": { + "node": ">=8" + } + }, "node_modules/@istanbuljs/nyc-config-typescript": { "version": "1.0.1", "resolved": "https://registry.npmjs.org/@istanbuljs/nyc-config-typescript/-/nyc-config-typescript-1.0.1.tgz", @@ -840,6 +950,12 @@ "integrity": "sha512-SZs7ekbP8CN0txVG2xVRH6EgKmEm31BOxA07vkFaETzZz1xh+cbt8BcI0slpymvwhx5dlFnQG2rTlPVQn+iRPQ==", "dev": true }, + "node_modules/@types/json-schema": { + "version": "7.0.9", + "resolved": "https://registry.npmjs.org/@types/json-schema/-/json-schema-7.0.9.tgz", + "integrity": "sha512-qcUXuemtEu+E5wZSJHNxUXeCZhAfXKQ41D+duX+VYPde7xyEVZci+/oXKJL13tnRs9lR2pr4fod59GT6/X1/yQ==", + "dev": true + }, "node_modules/@types/keyv": { "version": "3.1.3", "resolved": "https://registry.npmjs.org/@types/keyv/-/keyv-3.1.3.tgz", @@ -856,9 +972,9 @@ "dev": true }, "node_modules/@types/node": { - "version": "16.10.3", - "resolved": "https://registry.npmjs.org/@types/node/-/node-16.10.3.tgz", - "integrity": "sha512-ho3Ruq+fFnBrZhUYI46n/bV2GjwzSkwuT4dTf0GkuNFmnb8nq4ny2z9JEVemFi6bdEJanHLlYfy9c6FN9B9McQ==", + "version": "16.11.1", + "resolved": "https://registry.npmjs.org/@types/node/-/node-16.11.1.tgz", + "integrity": "sha512-PYGcJHL9mwl1Ek3PLiYgyEKtwTMmkMw4vbiyz/ps3pfdRYLVv+SN7qHVAImrjdAXxgluDEw6Ph4lyv+m9UpRmA==", "dev": true }, "node_modules/@types/parse-json": { @@ -897,6 +1013,163 @@ "integrity": "sha512-G3FNJfaYtN8URU6wd6+uwFI62KO79j7n3XTYcwcFncP8gkfoi0b821GoVVt0oqKVnCqKYOMNKIGpakPoFhzAGA==", "dev": true }, + "node_modules/@typescript-eslint/eslint-plugin": { + "version": "5.1.0", + "resolved": "https://registry.npmjs.org/@typescript-eslint/eslint-plugin/-/eslint-plugin-5.1.0.tgz", + "integrity": "sha512-bekODL3Tqf36Yz8u+ilha4zGxL9mdB6LIsIoMAvvC5FAuWo4NpZYXtCbv7B2CeR1LhI/lLtLk+q4tbtxuoVuCg==", + "dev": true, + "dependencies": { + "@typescript-eslint/experimental-utils": "5.1.0", + "@typescript-eslint/scope-manager": "5.1.0", + "debug": "^4.3.2", + "functional-red-black-tree": "^1.0.1", + "ignore": "^5.1.8", + "regexpp": "^3.2.0", + "semver": "^7.3.5", + "tsutils": "^3.21.0" + }, + "engines": { + "node": "^12.22.0 || ^14.17.0 || >=16.0.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/typescript-eslint" + }, + "peerDependencies": { + "@typescript-eslint/parser": "^5.0.0", + "eslint": "^6.0.0 || ^7.0.0 || ^8.0.0" + }, + "peerDependenciesMeta": { + "typescript": { + "optional": true + } + } + }, + "node_modules/@typescript-eslint/experimental-utils": { + "version": "5.1.0", + "resolved": "https://registry.npmjs.org/@typescript-eslint/experimental-utils/-/experimental-utils-5.1.0.tgz", + "integrity": "sha512-ovE9qUiZMOMgxQAESZsdBT+EXIfx/YUYAbwGUI6V03amFdOOxI9c6kitkgRvLkJaLusgMZ2xBhss+tQ0Y1HWxA==", + "dev": true, + "dependencies": { + "@types/json-schema": "^7.0.9", + "@typescript-eslint/scope-manager": "5.1.0", + "@typescript-eslint/types": "5.1.0", + "@typescript-eslint/typescript-estree": "5.1.0", + "eslint-scope": "^5.1.1", + "eslint-utils": "^3.0.0" + }, + "engines": { + "node": "^12.22.0 || ^14.17.0 || >=16.0.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/typescript-eslint" + }, + "peerDependencies": { + "eslint": "*" + } + }, + "node_modules/@typescript-eslint/parser": { + "version": "5.1.0", + "resolved": "https://registry.npmjs.org/@typescript-eslint/parser/-/parser-5.1.0.tgz", + "integrity": "sha512-vx1P+mhCtYw3+bRHmbalq/VKP2Y3gnzNgxGxfEWc6OFpuEL7iQdAeq11Ke3Rhy8NjgB+AHsIWEwni3e+Y7djKA==", + "dev": true, + "dependencies": { + "@typescript-eslint/scope-manager": "5.1.0", + "@typescript-eslint/types": "5.1.0", + "@typescript-eslint/typescript-estree": "5.1.0", + "debug": "^4.3.2" + }, + "engines": { + "node": "^12.22.0 || ^14.17.0 || >=16.0.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/typescript-eslint" + }, + "peerDependencies": { + "eslint": "^6.0.0 || ^7.0.0 || ^8.0.0" + }, + "peerDependenciesMeta": { + "typescript": { + "optional": true + } + } + }, + "node_modules/@typescript-eslint/scope-manager": { + "version": "5.1.0", + "resolved": "https://registry.npmjs.org/@typescript-eslint/scope-manager/-/scope-manager-5.1.0.tgz", + "integrity": "sha512-yYlyVjvn5lvwCL37i4hPsa1s0ORsjkauhTqbb8MnpvUs7xykmcjGqwlNZ2Q5QpoqkJ1odlM2bqHqJwa28qV6Tw==", + "dev": true, + "dependencies": { + "@typescript-eslint/types": "5.1.0", + "@typescript-eslint/visitor-keys": "5.1.0" + }, + "engines": { + "node": "^12.22.0 || ^14.17.0 || >=16.0.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/typescript-eslint" + } + }, + "node_modules/@typescript-eslint/types": { + "version": "5.1.0", + "resolved": "https://registry.npmjs.org/@typescript-eslint/types/-/types-5.1.0.tgz", + "integrity": "sha512-sEwNINVxcB4ZgC6Fe6rUyMlvsB2jvVdgxjZEjQUQVlaSPMNamDOwO6/TB98kFt4sYYfNhdhTPBEQqNQZjMMswA==", + "dev": true, + "engines": { + "node": "^12.22.0 || ^14.17.0 || >=16.0.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/typescript-eslint" + } + }, + "node_modules/@typescript-eslint/typescript-estree": { + "version": "5.1.0", + "resolved": "https://registry.npmjs.org/@typescript-eslint/typescript-estree/-/typescript-estree-5.1.0.tgz", + "integrity": "sha512-SSz+l9YrIIsW4s0ZqaEfnjl156XQ4VRmJsbA0ZE1XkXrD3cRpzuZSVCyqeCMR3EBjF27IisWakbBDGhGNIOvfQ==", + "dev": true, + "dependencies": { + "@typescript-eslint/types": "5.1.0", + "@typescript-eslint/visitor-keys": "5.1.0", + "debug": "^4.3.2", + "globby": "^11.0.4", + "is-glob": "^4.0.3", + "semver": "^7.3.5", + "tsutils": "^3.21.0" + }, + "engines": { + "node": "^12.22.0 || ^14.17.0 || >=16.0.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/typescript-eslint" + }, + "peerDependenciesMeta": { + "typescript": { + "optional": true + } + } + }, + "node_modules/@typescript-eslint/visitor-keys": { + "version": "5.1.0", + "resolved": "https://registry.npmjs.org/@typescript-eslint/visitor-keys/-/visitor-keys-5.1.0.tgz", + "integrity": "sha512-uqNXepKBg81JVwjuqAxYrXa1Ql/YDzM+8g/pS+TCPxba0wZttl8m5DkrasbfnmJGHs4lQ2jTbcZ5azGhI7kK+w==", + "dev": true, + "dependencies": { + "@typescript-eslint/types": "5.1.0", + "eslint-visitor-keys": "^3.0.0" + }, + "engines": { + "node": "^12.22.0 || ^14.17.0 || >=16.0.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/typescript-eslint" + } + }, "node_modules/@ungap/promise-all-settled": { "version": "1.1.2", "resolved": "https://registry.npmjs.org/@ungap/promise-all-settled/-/promise-all-settled-1.1.2.tgz", @@ -915,6 +1188,15 @@ "node": ">=0.4.0" } }, + "node_modules/acorn-jsx": { + "version": "5.3.2", + "resolved": "https://registry.npmjs.org/acorn-jsx/-/acorn-jsx-5.3.2.tgz", + "integrity": "sha512-rq9s+JNhf0IChjtDXxllJ7g41oZk5SlXtp0LHwyA5cejwn7vKmKp4pPri6YEePv2PU65sAsegbXtIinmDFDXgQ==", + "dev": true, + "peerDependencies": { + "acorn": "^6.0.0 || ^7.0.0 || ^8.0.0" + } + }, "node_modules/acorn-walk": { "version": "8.2.0", "resolved": "https://registry.npmjs.org/acorn-walk/-/acorn-walk-8.2.0.tgz", @@ -937,6 +1219,22 @@ "node": ">=8" } }, + "node_modules/ajv": { + "version": "6.12.6", + "resolved": "https://registry.npmjs.org/ajv/-/ajv-6.12.6.tgz", + "integrity": "sha512-j3fVLgvTo527anyYyJOGTYJbG+vnnQYvE0m5mmkc1TK+nxAppkCLMIL0aZ4dblVCNoGShhm+kzE4ZUykBoMg4g==", + "dev": true, + "dependencies": { + "fast-deep-equal": "^3.1.1", + "fast-json-stable-stringify": "^2.0.0", + "json-schema-traverse": "^0.4.1", + "uri-js": "^4.2.2" + }, + "funding": { + "type": "github", + "url": "https://github.com/sponsors/epoberezkin" + } + }, "node_modules/ansi-align": { "version": "3.0.1", "resolved": "https://registry.npmjs.org/ansi-align/-/ansi-align-3.0.1.tgz", @@ -1159,18 +1457,6 @@ "url": "https://github.com/sponsors/sindresorhus" } }, - "node_modules/boxen/node_modules/type-fest": { - "version": "0.20.2", - "resolved": "https://registry.npmjs.org/type-fest/-/type-fest-0.20.2.tgz", - "integrity": "sha512-Ne+eE4r0/iWnpAxD852z3A+N0Bt5RN//NjJwRd2VFHEmrywxf5vsZlh4R6lixl6B+wz/8d+maTSAkN1FIkI3LQ==", - "dev": true, - "engines": { - "node": ">=10" - }, - "funding": { - "url": "https://github.com/sponsors/sindresorhus" - } - }, "node_modules/brace-expansion": { "version": "1.1.11", "resolved": "https://registry.npmjs.org/brace-expansion/-/brace-expansion-1.1.11.tgz", @@ -1200,16 +1486,16 @@ "dev": true }, "node_modules/browserslist": { - "version": "4.17.3", - "resolved": "https://registry.npmjs.org/browserslist/-/browserslist-4.17.3.tgz", - "integrity": "sha512-59IqHJV5VGdcJZ+GZ2hU5n4Kv3YiASzW6Xk5g9tf5a/MAzGeFwgGWU39fVzNIOVcgB3+Gp+kiQu0HEfTVU/3VQ==", + "version": "4.17.4", + "resolved": "https://registry.npmjs.org/browserslist/-/browserslist-4.17.4.tgz", + "integrity": "sha512-Zg7RpbZpIJRW3am9Lyckue7PLytvVxxhJj1CaJVlCWENsGEAOlnlt8X0ZxGRPp7Bt9o8tIRM5SEXy4BCPMJjLQ==", "dev": true, "dependencies": { - "caniuse-lite": "^1.0.30001264", - "electron-to-chromium": "^1.3.857", + "caniuse-lite": "^1.0.30001265", + "electron-to-chromium": "^1.3.867", "escalade": "^3.1.1", - "node-releases": "^1.1.77", - "picocolors": "^0.2.1" + "node-releases": "^2.0.0", + "picocolors": "^1.0.0" }, "bin": { "browserslist": "cli.js" @@ -1341,9 +1627,9 @@ } }, "node_modules/caniuse-lite": { - "version": "1.0.30001265", - "resolved": "https://registry.npmjs.org/caniuse-lite/-/caniuse-lite-1.0.30001265.tgz", - "integrity": "sha512-YzBnspggWV5hep1m9Z6sZVLOt7vrju8xWooFAgN6BA5qvy98qPAPb7vNUzypFaoh2pb3vlfzbDO8tB57UPGbtw==", + "version": "1.0.30001269", + "resolved": "https://registry.npmjs.org/caniuse-lite/-/caniuse-lite-1.0.30001269.tgz", + "integrity": "sha512-UOy8okEVs48MyHYgV+RdW1Oiudl1H6KolybD6ZquD0VcrPSgj25omXO1S7rDydjpqaISCwA8Pyx+jUQKZwWO5w==", "dev": true, "funding": { "type": "opencollective", @@ -1366,18 +1652,6 @@ "url": "https://github.com/chalk/chalk?sponsor=1" } }, - "node_modules/chalk/node_modules/supports-color": { - "version": "7.2.0", - "resolved": "https://registry.npmjs.org/supports-color/-/supports-color-7.2.0.tgz", - "integrity": "sha512-qpCAvRl9stuOHveKsn7HncJRvv501qIacKzQlO/+Lwxc9+0q2wLyv4Dfvt80/DPn2pqOBsJdDiogXGR9+OvwRw==", - "dev": true, - "dependencies": { - "has-flag": "^4.0.0" - }, - "engines": { - "node": ">=8" - } - }, "node_modules/chardet": { "version": "0.7.0", "resolved": "https://registry.npmjs.org/chardet/-/chardet-0.7.0.tgz", @@ -1405,6 +1679,18 @@ "fsevents": "~2.3.2" } }, + "node_modules/chokidar/node_modules/glob-parent": { + "version": "5.1.2", + "resolved": "https://registry.npmjs.org/glob-parent/-/glob-parent-5.1.2.tgz", + "integrity": "sha512-AOIgSQCepiJYwP3ARnGx+5VnTu2HBYdzbGP45eLw1vr3zB3vZLeyed1sC9hnbcOc9/SrMyM5RPQrkGz4aS9Zow==", + "dev": true, + "dependencies": { + "is-glob": "^4.0.1" + }, + "engines": { + "node": ">= 6" + } + }, "node_modules/ci-info": { "version": "3.2.0", "resolved": "https://registry.npmjs.org/ci-info/-/ci-info-3.2.0.tgz", @@ -1632,12 +1918,6 @@ } } }, - "node_modules/debug/node_modules/ms": { - "version": "2.1.2", - "resolved": "https://registry.npmjs.org/ms/-/ms-2.1.2.tgz", - "integrity": "sha512-sGkPx+VjMtmA6MX27oA4FBFELFCZZ4S4XqeGOXCv68tT+jb3vk/RyaKWP0PTKyWtmLSM0b+adUTEvbs1PEaH2w==", - "dev": true - }, "node_modules/decamelize": { "version": "1.2.0", "resolved": "https://registry.npmjs.org/decamelize/-/decamelize-1.2.0.tgz", @@ -1692,6 +1972,12 @@ "node": ">=4.0.0" } }, + "node_modules/deep-is": { + "version": "0.1.4", + "resolved": "https://registry.npmjs.org/deep-is/-/deep-is-0.1.4.tgz", + "integrity": "sha512-oIPzksmTg4/MriiaYGO+okXDT7ztn/w3Eptv/+gSIdMdKsJo0u4CfYNFJPy+4SKMuCqGw2wxnA+URMg3t8a/bQ==", + "dev": true + }, "node_modules/default-require-extensions": { "version": "3.0.0", "resolved": "https://registry.npmjs.org/default-require-extensions/-/default-require-extensions-3.0.0.tgz", @@ -1771,6 +2057,18 @@ "node": ">=8" } }, + "node_modules/doctrine": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/doctrine/-/doctrine-3.0.0.tgz", + "integrity": "sha512-yS+Q5i3hBf7GBkd4KG8a7eBNNWNGLTaEwwYWUijIYM7zrlYDM0BFXHjjPWlWZ1Rg7UaddZeIDmi9jF3HmqiQ2w==", + "dev": true, + "dependencies": { + "esutils": "^2.0.2" + }, + "engines": { + "node": ">=6.0.0" + } + }, "node_modules/dot-prop": { "version": "5.3.0", "resolved": "https://registry.npmjs.org/dot-prop/-/dot-prop-5.3.0.tgz", @@ -1790,9 +2088,9 @@ "dev": true }, "node_modules/electron-to-chromium": { - "version": "1.3.864", - "resolved": "https://registry.npmjs.org/electron-to-chromium/-/electron-to-chromium-1.3.864.tgz", - "integrity": "sha512-v4rbad8GO6/yVI92WOeU9Wgxc4NA0n4f6P1FvZTY+jyY7JHEhw3bduYu60v3Q1h81Cg6eo4ApZrFPuycwd5hGw==", + "version": "1.3.872", + "resolved": "https://registry.npmjs.org/electron-to-chromium/-/electron-to-chromium-1.3.872.tgz", + "integrity": "sha512-qG96atLFY0agKyEETiBFNhpRLSXGSXOBuhXWpbkYqrLKKASpRyRBUtfkn0ZjIf/yXfA7FA4nScVOMpXSHFlUCQ==", "dev": true }, "node_modules/emoji-regex": { @@ -1810,6 +2108,18 @@ "once": "^1.4.0" } }, + "node_modules/enquirer": { + "version": "2.3.6", + "resolved": "https://registry.npmjs.org/enquirer/-/enquirer-2.3.6.tgz", + "integrity": "sha512-yjNnPr315/FjS4zIsUxYguYUPP2e1NK4d7E7ZOLiyYCcbFBiTMyID+2wvm2w6+pZ/odMA7cRkjhsPbltwBOrLg==", + "dev": true, + "dependencies": { + "ansi-colors": "^4.1.1" + }, + "engines": { + "node": ">=8.6" + } + }, "node_modules/error-ex": { "version": "1.3.2", "resolved": "https://registry.npmjs.org/error-ex/-/error-ex-1.3.2.tgz", @@ -1855,6 +2165,155 @@ "url": "https://github.com/sponsors/sindresorhus" } }, + "node_modules/eslint": { + "version": "8.0.1", + "resolved": "https://registry.npmjs.org/eslint/-/eslint-8.0.1.tgz", + "integrity": "sha512-LsgcwZgQ72vZ+SMp4K6pAnk2yFDWL7Ti4pJaRvsZ0Hsw2h8ZjUIW38a9AFn2cZXdBMlScMFYYgsSp4ttFI/0bA==", + "dev": true, + "dependencies": { + "@eslint/eslintrc": "^1.0.3", + "@humanwhocodes/config-array": "^0.6.0", + "ajv": "^6.10.0", + "chalk": "^4.0.0", + "cross-spawn": "^7.0.2", + "debug": "^4.3.2", + "doctrine": "^3.0.0", + "enquirer": "^2.3.5", + "escape-string-regexp": "^4.0.0", + "eslint-scope": "^6.0.0", + "eslint-utils": "^3.0.0", + "eslint-visitor-keys": "^3.0.0", + "espree": "^9.0.0", + "esquery": "^1.4.0", + "esutils": "^2.0.2", + "fast-deep-equal": "^3.1.3", + "file-entry-cache": "^6.0.1", + "functional-red-black-tree": "^1.0.1", + "glob-parent": "^6.0.1", + "globals": "^13.6.0", + "ignore": "^4.0.6", + "import-fresh": "^3.0.0", + "imurmurhash": "^0.1.4", + "is-glob": "^4.0.0", + "js-yaml": "^4.1.0", + "json-stable-stringify-without-jsonify": "^1.0.1", + "levn": "^0.4.1", + "lodash.merge": "^4.6.2", + "minimatch": "^3.0.4", + "natural-compare": "^1.4.0", + "optionator": "^0.9.1", + "progress": "^2.0.0", + "regexpp": "^3.2.0", + "semver": "^7.2.1", + "strip-ansi": "^6.0.0", + "strip-json-comments": "^3.1.0", + "text-table": "^0.2.0", + "v8-compile-cache": "^2.0.3" + }, + "bin": { + "eslint": "bin/eslint.js" + }, + "engines": { + "node": "^12.22.0 || ^14.17.0 || >=16.0.0" + }, + "funding": { + "url": "https://opencollective.com/eslint" + } + }, + "node_modules/eslint-scope": { + "version": "5.1.1", + "resolved": "https://registry.npmjs.org/eslint-scope/-/eslint-scope-5.1.1.tgz", + "integrity": "sha512-2NxwbF/hZ0KpepYN0cNbo+FN6XoK7GaHlQhgx/hIZl6Va0bF45RQOOwhLIy8lQDbuCiadSLCBnH2CFYquit5bw==", + "dev": true, + "dependencies": { + "esrecurse": "^4.3.0", + "estraverse": "^4.1.1" + }, + "engines": { + "node": ">=8.0.0" + } + }, + "node_modules/eslint-utils": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/eslint-utils/-/eslint-utils-3.0.0.tgz", + "integrity": "sha512-uuQC43IGctw68pJA1RgbQS8/NP7rch6Cwd4j3ZBtgo4/8Flj4eGE7ZYSZRN3iq5pVUv6GPdW5Z1RFleo84uLDA==", + "dev": true, + "dependencies": { + "eslint-visitor-keys": "^2.0.0" + }, + "engines": { + "node": "^10.0.0 || ^12.0.0 || >= 14.0.0" + }, + "funding": { + "url": "https://github.com/sponsors/mysticatea" + }, + "peerDependencies": { + "eslint": ">=5" + } + }, + "node_modules/eslint-utils/node_modules/eslint-visitor-keys": { + "version": "2.1.0", + "resolved": "https://registry.npmjs.org/eslint-visitor-keys/-/eslint-visitor-keys-2.1.0.tgz", + "integrity": "sha512-0rSmRBzXgDzIsD6mGdJgevzgezI534Cer5L/vyMX0kHzT/jiB43jRhd9YUlMGYLQy2zprNmoT8qasCGtY+QaKw==", + "dev": true, + "engines": { + "node": ">=10" + } + }, + "node_modules/eslint-visitor-keys": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/eslint-visitor-keys/-/eslint-visitor-keys-3.0.0.tgz", + "integrity": "sha512-mJOZa35trBTb3IyRmo8xmKBZlxf+N7OnUl4+ZhJHs/r+0770Wh/LEACE2pqMGMe27G/4y8P2bYGk4J70IC5k1Q==", + "dev": true, + "engines": { + "node": "^12.22.0 || ^14.17.0 || >=16.0.0" + } + }, + "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==", + "dev": true, + "dependencies": { + "esrecurse": "^4.3.0", + "estraverse": "^5.2.0" + }, + "engines": { + "node": "^12.22.0 || ^14.17.0 || >=16.0.0" + } + }, + "node_modules/eslint/node_modules/estraverse": { + "version": "5.2.0", + "resolved": "https://registry.npmjs.org/estraverse/-/estraverse-5.2.0.tgz", + "integrity": "sha512-BxbNGGNm0RyRYvUdHpIwv9IWzeM9XClbOxwoATuFdOE7ZE6wHL+HQ5T8hoPM+zHvmKzzsEqhgy0GrQ5X13afiQ==", + "dev": true, + "engines": { + "node": ">=4.0" + } + }, + "node_modules/eslint/node_modules/ignore": { + "version": "4.0.6", + "resolved": "https://registry.npmjs.org/ignore/-/ignore-4.0.6.tgz", + "integrity": "sha512-cyFDKrqc/YdcWFniJhzI42+AzS+gNwmUzOSFcRCQYwySuBBBy/KjuxWLZ/FHEH6Moq1NizMOBWyTcv8O4OZIMg==", + "dev": true, + "engines": { + "node": ">= 4" + } + }, + "node_modules/espree": { + "version": "9.0.0", + "resolved": "https://registry.npmjs.org/espree/-/espree-9.0.0.tgz", + "integrity": "sha512-r5EQJcYZ2oaGbeR0jR0fFVijGOcwai07/690YRXLINuhmVeRY4UKSAsQPe/0BNuDgwP7Ophoc1PRsr2E3tkbdQ==", + "dev": true, + "dependencies": { + "acorn": "^8.5.0", + "acorn-jsx": "^5.3.1", + "eslint-visitor-keys": "^3.0.0" + }, + "engines": { + "node": "^12.22.0 || ^14.17.0 || >=16.0.0" + } + }, "node_modules/esprima": { "version": "4.0.1", "resolved": "https://registry.npmjs.org/esprima/-/esprima-4.0.1.tgz", @@ -1868,6 +2327,66 @@ "node": ">=4" } }, + "node_modules/esquery": { + "version": "1.4.0", + "resolved": "https://registry.npmjs.org/esquery/-/esquery-1.4.0.tgz", + "integrity": "sha512-cCDispWt5vHHtwMY2YrAQ4ibFkAL8RbH5YGBnZBc90MolvvfkkQcJro/aZiAQUlQ3qgrYS6D6v8Gc5G5CQsc9w==", + "dev": true, + "dependencies": { + "estraverse": "^5.1.0" + }, + "engines": { + "node": ">=0.10" + } + }, + "node_modules/esquery/node_modules/estraverse": { + "version": "5.2.0", + "resolved": "https://registry.npmjs.org/estraverse/-/estraverse-5.2.0.tgz", + "integrity": "sha512-BxbNGGNm0RyRYvUdHpIwv9IWzeM9XClbOxwoATuFdOE7ZE6wHL+HQ5T8hoPM+zHvmKzzsEqhgy0GrQ5X13afiQ==", + "dev": true, + "engines": { + "node": ">=4.0" + } + }, + "node_modules/esrecurse": { + "version": "4.3.0", + "resolved": "https://registry.npmjs.org/esrecurse/-/esrecurse-4.3.0.tgz", + "integrity": "sha512-KmfKL3b6G+RXvP8N1vr3Tq1kL/oCFgn2NYXEtqP8/L3pKapUA4G8cFVaoF3SU323CD4XypR/ffioHmkti6/Tag==", + "dev": true, + "dependencies": { + "estraverse": "^5.2.0" + }, + "engines": { + "node": ">=4.0" + } + }, + "node_modules/esrecurse/node_modules/estraverse": { + "version": "5.2.0", + "resolved": "https://registry.npmjs.org/estraverse/-/estraverse-5.2.0.tgz", + "integrity": "sha512-BxbNGGNm0RyRYvUdHpIwv9IWzeM9XClbOxwoATuFdOE7ZE6wHL+HQ5T8hoPM+zHvmKzzsEqhgy0GrQ5X13afiQ==", + "dev": true, + "engines": { + "node": ">=4.0" + } + }, + "node_modules/estraverse": { + "version": "4.3.0", + "resolved": "https://registry.npmjs.org/estraverse/-/estraverse-4.3.0.tgz", + "integrity": "sha512-39nnKffWz8xN1BU/2c79n9nB9HDzo0niYUqx6xyqUnyoAnQyyWpOTdZEeiCch8BBu515t4wp9ZmgVfVhn9EBpw==", + "dev": true, + "engines": { + "node": ">=4.0" + } + }, + "node_modules/esutils": { + "version": "2.0.3", + "resolved": "https://registry.npmjs.org/esutils/-/esutils-2.0.3.tgz", + "integrity": "sha512-kVscqXk4OCp68SZ0dkgEKVi6/8ij300KBWTJq32P/dYeWTSwK41WyTxalN1eRmA5Z9UU/LX9D7FWSmV9SAYx6g==", + "dev": true, + "engines": { + "node": ">=0.10.0" + } + }, "node_modules/execa": { "version": "5.1.1", "resolved": "https://registry.npmjs.org/execa/-/execa-5.1.1.tgz", @@ -1905,6 +2424,12 @@ "node": ">=4" } }, + "node_modules/fast-deep-equal": { + "version": "3.1.3", + "resolved": "https://registry.npmjs.org/fast-deep-equal/-/fast-deep-equal-3.1.3.tgz", + "integrity": "sha512-f3qQ9oQy9j2AhBe/H9VC91wLmKBCCU/gDOnKNAYG5hswO7BLKj09Hc5HYNz9cGI++xlpDCIgDaitVs03ATR84Q==", + "dev": true + }, "node_modules/fast-glob": { "version": "3.2.7", "resolved": "https://registry.npmjs.org/fast-glob/-/fast-glob-3.2.7.tgz", @@ -1921,6 +2446,30 @@ "node": ">=8" } }, + "node_modules/fast-glob/node_modules/glob-parent": { + "version": "5.1.2", + "resolved": "https://registry.npmjs.org/glob-parent/-/glob-parent-5.1.2.tgz", + "integrity": "sha512-AOIgSQCepiJYwP3ARnGx+5VnTu2HBYdzbGP45eLw1vr3zB3vZLeyed1sC9hnbcOc9/SrMyM5RPQrkGz4aS9Zow==", + "dev": true, + "dependencies": { + "is-glob": "^4.0.1" + }, + "engines": { + "node": ">= 6" + } + }, + "node_modules/fast-json-stable-stringify": { + "version": "2.1.0", + "resolved": "https://registry.npmjs.org/fast-json-stable-stringify/-/fast-json-stable-stringify-2.1.0.tgz", + "integrity": "sha512-lhd/wF+Lk98HZoTCtlVraHtfh5XYijIjalXck7saUtuanSDyLMxnHhSXEDJqHxD7msR8D0uCmqlkwjCV8xvwHw==", + "dev": true + }, + "node_modules/fast-levenshtein": { + "version": "2.0.6", + "resolved": "https://registry.npmjs.org/fast-levenshtein/-/fast-levenshtein-2.0.6.tgz", + "integrity": "sha1-PYpcZog6FqMMqGQ+hR8Zuqd5eRc=", + "dev": true + }, "node_modules/fastq": { "version": "1.13.0", "resolved": "https://registry.npmjs.org/fastq/-/fastq-1.13.0.tgz", @@ -1954,6 +2503,18 @@ "node": ">=0.8.0" } }, + "node_modules/file-entry-cache": { + "version": "6.0.1", + "resolved": "https://registry.npmjs.org/file-entry-cache/-/file-entry-cache-6.0.1.tgz", + "integrity": "sha512-7Gps/XWymbLk2QLYK4NzpMOrYjMhdIxXuIvy2QBsLE6ljuodKvdkWs/cpyJJ3CVIVpH0Oi1Hvg1ovbMzLdFBBg==", + "dev": true, + "dependencies": { + "flat-cache": "^3.0.4" + }, + "engines": { + "node": "^10.12.0 || >=12.0.0" + } + }, "node_modules/fill-range": { "version": "7.0.1", "resolved": "https://registry.npmjs.org/fill-range/-/fill-range-7.0.1.tgz", @@ -2017,6 +2578,25 @@ "flat": "cli.js" } }, + "node_modules/flat-cache": { + "version": "3.0.4", + "resolved": "https://registry.npmjs.org/flat-cache/-/flat-cache-3.0.4.tgz", + "integrity": "sha512-dm9s5Pw7Jc0GvMYbshN6zchCA9RgQlzzEZX3vylR9IqFfS8XciblUXOKfW6SiuJ0e13eDYZoZV5wdrev7P3Nwg==", + "dev": true, + "dependencies": { + "flatted": "^3.1.0", + "rimraf": "^3.0.2" + }, + "engines": { + "node": "^10.12.0 || >=12.0.0" + } + }, + "node_modules/flatted": { + "version": "3.2.2", + "resolved": "https://registry.npmjs.org/flatted/-/flatted-3.2.2.tgz", + "integrity": "sha512-JaTY/wtrcSyvXJl4IMFHPKyFur1sE9AUqc0QnhOaJ0CxHtAoIV8pYDzeEfAaNEtGkOfq4gr3LBFmdXW5mOQFnA==", + "dev": true + }, "node_modules/foreground-child": { "version": "2.0.0", "resolved": "https://registry.npmjs.org/foreground-child/-/foreground-child-2.0.0.tgz", @@ -2090,6 +2670,12 @@ "integrity": "sha512-yIovAzMX49sF8Yl58fSCWJ5svSLuaibPxXQJFLmBObTuCr0Mf1KiPopGM9NiFjiYBCbfaa2Fh6breQ6ANVTI0A==", "dev": true }, + "node_modules/functional-red-black-tree": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/functional-red-black-tree/-/functional-red-black-tree-1.0.1.tgz", + "integrity": "sha1-GwqzvVU7Kg1jmdKcDj6gslIHgyc=", + "dev": true + }, "node_modules/generic-pool": { "version": "3.8.2", "resolved": "https://registry.npmjs.org/generic-pool/-/generic-pool-3.8.2.tgz", @@ -2191,15 +2777,15 @@ } }, "node_modules/glob-parent": { - "version": "5.1.2", - "resolved": "https://registry.npmjs.org/glob-parent/-/glob-parent-5.1.2.tgz", - "integrity": "sha512-AOIgSQCepiJYwP3ARnGx+5VnTu2HBYdzbGP45eLw1vr3zB3vZLeyed1sC9hnbcOc9/SrMyM5RPQrkGz4aS9Zow==", + "version": "6.0.2", + "resolved": "https://registry.npmjs.org/glob-parent/-/glob-parent-6.0.2.tgz", + "integrity": "sha512-XxwI8EOhVQgWp6iDL+3b0r86f4d6AX6zSU55HfB4ydCEuXLXc5FcYeOu+nnGftS4TEju/11rt4KJPTMgbfmv4A==", "dev": true, "dependencies": { - "is-glob": "^4.0.1" + "is-glob": "^4.0.3" }, "engines": { - "node": ">= 6" + "node": ">=10.13.0" } }, "node_modules/global-dirs": { @@ -2218,12 +2804,18 @@ } }, "node_modules/globals": { - "version": "11.12.0", - "resolved": "https://registry.npmjs.org/globals/-/globals-11.12.0.tgz", - "integrity": "sha512-WOBp/EEGUiIsJSp7wcv/y6MO+lV9UoncWqxuFfm8eBwzWNgyfBd6Gz+IeKQ9jCmyhoH99g15M3T+QaVHFjizVA==", + "version": "13.11.0", + "resolved": "https://registry.npmjs.org/globals/-/globals-13.11.0.tgz", + "integrity": "sha512-08/xrJ7wQjK9kkkRoI3OFUBbLx4f+6x3SGwcPvQ0QH6goFDrOU2oyAWrmh3dJezu65buo+HBMzAMQy6rovVC3g==", "dev": true, + "dependencies": { + "type-fest": "^0.20.2" + }, "engines": { - "node": ">=4" + "node": ">=8" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" } }, "node_modules/globby": { @@ -2374,6 +2966,15 @@ "url": "https://github.com/sponsors/sindresorhus" } }, + "node_modules/hasha/node_modules/type-fest": { + "version": "0.8.1", + "resolved": "https://registry.npmjs.org/type-fest/-/type-fest-0.8.1.tgz", + "integrity": "sha512-4dbzIzqvjtgiM5rw1k5rEHtBANKmdudhGyBEajN01fEyhaAIhsoKNy6y7+IN93IfpFtwY9iqi7kD+xwKhQsNJA==", + "dev": true, + "engines": { + "node": ">=8" + } + }, "node_modules/he": { "version": "1.2.0", "resolved": "https://registry.npmjs.org/he/-/he-1.2.0.tgz", @@ -2486,23 +3087,23 @@ "url": "https://github.com/sponsors/sindresorhus" } }, - "node_modules/import-fresh/node_modules/resolve-from": { - "version": "4.0.0", - "resolved": "https://registry.npmjs.org/resolve-from/-/resolve-from-4.0.0.tgz", - "integrity": "sha512-pb/MYmXstAkysRFx8piNI1tGFNQIFA3vkE3Gq4EuA1dF6gHp/+vgZqsCGJapvy8N3Q+4o7FwvquPJcnZ7RYy4g==", - "dev": true, - "engines": { - "node": ">=4" - } - }, "node_modules/import-from": { "version": "3.0.0", "resolved": "https://registry.npmjs.org/import-from/-/import-from-3.0.0.tgz", "integrity": "sha512-CiuXOFFSzkU5x/CR0+z7T91Iht4CXgfCxVOFRhh2Zyhg5wOpWvvDLQUsWl+gcN+QscYBjez8hDCt85O7RLDttQ==", "dev": true, - "dependencies": { - "resolve-from": "^5.0.0" - }, + "dependencies": { + "resolve-from": "^5.0.0" + }, + "engines": { + "node": ">=8" + } + }, + "node_modules/import-from/node_modules/resolve-from": { + "version": "5.0.0", + "resolved": "https://registry.npmjs.org/resolve-from/-/resolve-from-5.0.0.tgz", + "integrity": "sha512-qYg9KP24dD5qka9J47d0aVky0N+b4fTU89LN9iDnjB5waksiC49rvMB0PrUJQGoTmH50XPiqOvAjDfaijGxYZw==", + "dev": true, "engines": { "node": ">=8" } @@ -2624,9 +3225,9 @@ } }, "node_modules/is-core-module": { - "version": "2.7.0", - "resolved": "https://registry.npmjs.org/is-core-module/-/is-core-module-2.7.0.tgz", - "integrity": "sha512-ByY+tjCciCr+9nLryBYcSD50EOGWt95c7tIsKTG1J2ixKKXPvF7Ej3AVd+UfDydAJom3biBGDBALaO79ktwgEQ==", + "version": "2.8.0", + "resolved": "https://registry.npmjs.org/is-core-module/-/is-core-module-2.8.0.tgz", + "integrity": "sha512-vd15qHsaqrRL7dtH6QNuy0ndJmRDrS9HAM1CAiSifNUFv4x1a0CCVsj18hJ1mShxIG6T2i1sO78MkP56r0nYRw==", "dev": true, "dependencies": { "has": "^1.0.3" @@ -2841,9 +3442,9 @@ "dev": true }, "node_modules/istanbul-lib-coverage": { - "version": "3.0.0", - "resolved": "https://registry.npmjs.org/istanbul-lib-coverage/-/istanbul-lib-coverage-3.0.0.tgz", - "integrity": "sha512-UiUIqxMgRDET6eR+o5HbfRYP1l0hqkWOs7vNxC/mggutCMUIhWMm8gAHb8tHlyfD3/l6rlgNA5cKdDzEAf6hEg==", + "version": "3.2.0", + "resolved": "https://registry.npmjs.org/istanbul-lib-coverage/-/istanbul-lib-coverage-3.2.0.tgz", + "integrity": "sha512-eOeJ5BHCmHYvQK7xt9GkdHuzuCGS1Y6g9Gvnx3Ym33fz/HpLRYxiS0wHNr+m/MBC8B647Xt608vCDEvhl9c6Mw==", "dev": true, "engines": { "node": ">=8" @@ -2876,6 +3477,15 @@ "node": ">=8" } }, + "node_modules/istanbul-lib-instrument/node_modules/semver": { + "version": "6.3.0", + "resolved": "https://registry.npmjs.org/semver/-/semver-6.3.0.tgz", + "integrity": "sha512-b39TBaTSfV6yBrapU89p5fKekE2m/NwnDocOVruQFS1/veMgdzuPcnOM34M6CwxW8jH/lxEa5rBoDeUwu5HHTw==", + "dev": true, + "bin": { + "semver": "bin/semver.js" + } + }, "node_modules/istanbul-lib-processinfo": { "version": "2.0.2", "resolved": "https://registry.npmjs.org/istanbul-lib-processinfo/-/istanbul-lib-processinfo-2.0.2.tgz", @@ -2908,22 +3518,10 @@ "node": ">=8" } }, - "node_modules/istanbul-lib-report/node_modules/supports-color": { - "version": "7.2.0", - "resolved": "https://registry.npmjs.org/supports-color/-/supports-color-7.2.0.tgz", - "integrity": "sha512-qpCAvRl9stuOHveKsn7HncJRvv501qIacKzQlO/+Lwxc9+0q2wLyv4Dfvt80/DPn2pqOBsJdDiogXGR9+OvwRw==", - "dev": true, - "dependencies": { - "has-flag": "^4.0.0" - }, - "engines": { - "node": ">=8" - } - }, "node_modules/istanbul-lib-source-maps": { - "version": "4.0.0", - "resolved": "https://registry.npmjs.org/istanbul-lib-source-maps/-/istanbul-lib-source-maps-4.0.0.tgz", - "integrity": "sha512-c16LpFRkR8vQXyHZ5nLpY35JZtzj1PQY1iZmesUbf1FZHbIupcWfjgOXBY9YHkLEQ6puz1u4Dgj6qmU/DisrZg==", + "version": "4.0.1", + "resolved": "https://registry.npmjs.org/istanbul-lib-source-maps/-/istanbul-lib-source-maps-4.0.1.tgz", + "integrity": "sha512-n3s8EwkdFIJCG3BPKBYvskgXGoy88ARzvegkitk60NxRdwltLOTaH7CUiMRXvwYorl0Q712iEjcWB+fK/MrWVw==", "dev": true, "dependencies": { "debug": "^4.1.1", @@ -2931,7 +3529,7 @@ "source-map": "^0.6.1" }, "engines": { - "node": ">=8" + "node": ">=10" } }, "node_modules/istanbul-lib-source-maps/node_modules/source-map": { @@ -2944,9 +3542,9 @@ } }, "node_modules/istanbul-reports": { - "version": "3.0.3", - "resolved": "https://registry.npmjs.org/istanbul-reports/-/istanbul-reports-3.0.3.tgz", - "integrity": "sha512-0i77ZFLsb9U3DHi22WzmIngVzfoyxxbQcZRqlF3KoKmCJGq9nhFHoGi8FqBztN2rE8w6hURnZghetn0xpkVb6A==", + "version": "3.0.5", + "resolved": "https://registry.npmjs.org/istanbul-reports/-/istanbul-reports-3.0.5.tgz", + "integrity": "sha512-5+19PlhnGabNWB7kOFnuxT8H3T/iIyQzIbQMxXsURmmvKg86P2sbkrGOT77VnHw0Qr0gc2XzRaRfMZYYbSQCJQ==", "dev": true, "dependencies": { "html-escaper": "^2.0.0", @@ -2998,6 +3596,18 @@ "integrity": "sha512-xyFwyhro/JEof6Ghe2iz2NcXoj2sloNsWr/XsERDK/oiPCfaNhl5ONfp+jQdAZRQQ0IJWNzH9zIZF7li91kh2w==", "dev": true }, + "node_modules/json-schema-traverse": { + "version": "0.4.1", + "resolved": "https://registry.npmjs.org/json-schema-traverse/-/json-schema-traverse-0.4.1.tgz", + "integrity": "sha512-xbbCH5dCYU5T8LcEhhuh7HJ88HXuW3qsI3Y0zOZFKfZEHcpWiHU/Jxzk629Brsab/mMiHQti9wMP+845RPe3Vg==", + "dev": true + }, + "node_modules/json-stable-stringify-without-jsonify": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/json-stable-stringify-without-jsonify/-/json-stable-stringify-without-jsonify-1.0.1.tgz", + "integrity": "sha1-nbe1lJatPzz+8wp1FC0tkwrXJlE=", + "dev": true + }, "node_modules/json5": { "version": "2.2.0", "resolved": "https://registry.npmjs.org/json5/-/json5-2.2.0.tgz", @@ -3046,6 +3656,19 @@ "node": ">=8" } }, + "node_modules/levn": { + "version": "0.4.1", + "resolved": "https://registry.npmjs.org/levn/-/levn-0.4.1.tgz", + "integrity": "sha512-+bT2uH4E5LGE7h/n3evcS/sQlJXCpIp6ym8OWJ5eV6+67Dsql/LaaT7qJBAt2rzfoa/5QBGBhxDix1dMt2kQKQ==", + "dev": true, + "dependencies": { + "prelude-ls": "^1.2.1", + "type-check": "~0.4.0" + }, + "engines": { + "node": ">= 0.8.0" + } + }, "node_modules/lines-and-columns": { "version": "1.1.6", "resolved": "https://registry.npmjs.org/lines-and-columns/-/lines-and-columns-1.1.6.tgz", @@ -3085,6 +3708,12 @@ "integrity": "sha1-LRd/ZS+jHpObRDjVNBSZ36OCXpk=", "dev": true }, + "node_modules/lodash.merge": { + "version": "4.6.2", + "resolved": "https://registry.npmjs.org/lodash.merge/-/lodash.merge-4.6.2.tgz", + "integrity": "sha512-0KpjqXRVvrYyCsX1swR/XTK0va6VQkQM6MNo7PqW77ByjAhoARA8EfrP1N4+KlKj8YS0ZUCtRT/YUuhyYDujIQ==", + "dev": true + }, "node_modules/log-symbols": { "version": "4.1.0", "resolved": "https://registry.npmjs.org/log-symbols/-/log-symbols-4.1.0.tgz", @@ -3111,20 +3740,17 @@ } }, "node_modules/lru-cache": { - "version": "5.1.1", - "resolved": "https://registry.npmjs.org/lru-cache/-/lru-cache-5.1.1.tgz", - "integrity": "sha512-KpNARQA3Iwv+jTA0utUVVbrh+Jlrr1Fv0e56GGzAFOXN7dk/FviaDW8LHmK52DlcH4WP2n6gI8vN1aesBFgo9w==", + "version": "6.0.0", + "resolved": "https://registry.npmjs.org/lru-cache/-/lru-cache-6.0.0.tgz", + "integrity": "sha512-Jo6dJ04CmSjuznwJSS3pUeWmd/H0ffTlkXXgwZi+eq1UCmqQwCh+eLsYOYCwY991i2Fah4h1BEMCx4qThGbsiA==", "dev": true, "dependencies": { - "yallist": "^3.0.2" + "yallist": "^4.0.0" + }, + "engines": { + "node": ">=10" } }, - "node_modules/lru-cache/node_modules/yallist": { - "version": "3.1.1", - "resolved": "https://registry.npmjs.org/yallist/-/yallist-3.1.1.tgz", - "integrity": "sha512-a4UGQaWPH59mOXUYnAG2ewncQS4i4F43Tv3JoAM+s2VDAmS9NsK8GpDMLrCHPksFT7h3K6TOoUNn2pb7RoXx4g==", - "dev": true - }, "node_modules/lunr": { "version": "2.3.9", "resolved": "https://registry.npmjs.org/lunr/-/lunr-2.3.9.tgz", @@ -3158,6 +3784,15 @@ "url": "https://github.com/sponsors/sindresorhus" } }, + "node_modules/make-dir/node_modules/semver": { + "version": "6.3.0", + "resolved": "https://registry.npmjs.org/semver/-/semver-6.3.0.tgz", + "integrity": "sha512-b39TBaTSfV6yBrapU89p5fKekE2m/NwnDocOVruQFS1/veMgdzuPcnOM34M6CwxW8jH/lxEa5rBoDeUwu5HHTw==", + "dev": true, + "bin": { + "semver": "bin/semver.js" + } + }, "node_modules/make-error": { "version": "1.3.6", "resolved": "https://registry.npmjs.org/make-error/-/make-error-1.3.6.tgz", @@ -3262,9 +3897,9 @@ "dev": true }, "node_modules/mocha": { - "version": "9.1.2", - "resolved": "https://registry.npmjs.org/mocha/-/mocha-9.1.2.tgz", - "integrity": "sha512-ta3LtJ+63RIBP03VBjMGtSqbe6cWXRejF9SyM9Zyli1CKZJZ+vfCTj3oW24V7wAphMJdpOFLoMI3hjJ1LWbs0w==", + "version": "9.1.3", + "resolved": "https://registry.npmjs.org/mocha/-/mocha-9.1.3.tgz", + "integrity": "sha512-Xcpl9FqXOAYqI3j79pEtHBBnQgVXIhpULjGQa7DVb0Po+VzmSIK9kanAiWLHoRR/dbZ2qpdPshuXr8l1VaHCzw==", "dev": true, "dependencies": { "@ungap/promise-all-settled": "1.1.2", @@ -3304,12 +3939,33 @@ "url": "https://opencollective.com/mochajs" } }, - "node_modules/ms": { + "node_modules/mocha/node_modules/ms": { "version": "2.1.3", "resolved": "https://registry.npmjs.org/ms/-/ms-2.1.3.tgz", "integrity": "sha512-6FlzubTLZG3J2a/NVCAleEhjzq5oxgHyaCU9yYXvcLsvoVaHJq/s5xXI6/XXP6tz7R9xAOtHnSO/tXtF3WRTlA==", "dev": true }, + "node_modules/mocha/node_modules/supports-color": { + "version": "8.1.1", + "resolved": "https://registry.npmjs.org/supports-color/-/supports-color-8.1.1.tgz", + "integrity": "sha512-MpUEN2OodtUzxvKQl72cUF7RQ5EiHsGvSsVG0ia9c5RbWGL2CI4C7EpPS8UTBIplnlzZiNuV56w+FuNxy3ty2Q==", + "dev": true, + "dependencies": { + "has-flag": "^4.0.0" + }, + "engines": { + "node": ">=10" + }, + "funding": { + "url": "https://github.com/chalk/supports-color?sponsor=1" + } + }, + "node_modules/ms": { + "version": "2.1.2", + "resolved": "https://registry.npmjs.org/ms/-/ms-2.1.2.tgz", + "integrity": "sha512-sGkPx+VjMtmA6MX27oA4FBFELFCZZ4S4XqeGOXCv68tT+jb3vk/RyaKWP0PTKyWtmLSM0b+adUTEvbs1PEaH2w==", + "dev": true + }, "node_modules/mute-stream": { "version": "0.0.8", "resolved": "https://registry.npmjs.org/mute-stream/-/mute-stream-0.0.8.tgz", @@ -3328,6 +3984,12 @@ "node": "^10 || ^12 || ^13.7 || ^14 || >=15.0.1" } }, + "node_modules/natural-compare": { + "version": "1.4.0", + "resolved": "https://registry.npmjs.org/natural-compare/-/natural-compare-1.4.0.tgz", + "integrity": "sha1-Sr6/7tdUHywnrPspvbvRXI1bpPc=", + "dev": true + }, "node_modules/neo-async": { "version": "2.6.2", "resolved": "https://registry.npmjs.org/neo-async/-/neo-async-2.6.2.tgz", @@ -3393,9 +4055,9 @@ } }, "node_modules/node-releases": { - "version": "1.1.77", - "resolved": "https://registry.npmjs.org/node-releases/-/node-releases-1.1.77.tgz", - "integrity": "sha512-rB1DUFUNAN4Gn9keO2K1efO35IDK7yKHCdCaIMvFO7yUYmmZYeDjnGKle26G4rwj+LKRQpjyUUvMkPglwGCYNQ==", + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/node-releases/-/node-releases-2.0.0.tgz", + "integrity": "sha512-aA87l0flFYMzCHpTM3DERFSYxc6lv/BltdbRTOMZuxZ0cwZCD3mejE5n9vLhSJCN++/eOqr77G1IO5uXxlQYWA==", "dev": true }, "node_modules/normalize-path": { @@ -3535,6 +4197,15 @@ "node": ">=8" } }, + "node_modules/nyc/node_modules/resolve-from": { + "version": "5.0.0", + "resolved": "https://registry.npmjs.org/resolve-from/-/resolve-from-5.0.0.tgz", + "integrity": "sha512-qYg9KP24dD5qka9J47d0aVky0N+b4fTU89LN9iDnjB5waksiC49rvMB0PrUJQGoTmH50XPiqOvAjDfaijGxYZw==", + "dev": true, + "engines": { + "node": ">=8" + } + }, "node_modules/nyc/node_modules/wrap-ansi": { "version": "6.2.0", "resolved": "https://registry.npmjs.org/wrap-ansi/-/wrap-ansi-6.2.0.tgz", @@ -3632,6 +4303,21 @@ "lru-cache": "^5.1.1" } }, + "node_modules/onigasm/node_modules/lru-cache": { + "version": "5.1.1", + "resolved": "https://registry.npmjs.org/lru-cache/-/lru-cache-5.1.1.tgz", + "integrity": "sha512-KpNARQA3Iwv+jTA0utUVVbrh+Jlrr1Fv0e56GGzAFOXN7dk/FviaDW8LHmK52DlcH4WP2n6gI8vN1aesBFgo9w==", + "dev": true, + "dependencies": { + "yallist": "^3.0.2" + } + }, + "node_modules/onigasm/node_modules/yallist": { + "version": "3.1.1", + "resolved": "https://registry.npmjs.org/yallist/-/yallist-3.1.1.tgz", + "integrity": "sha512-a4UGQaWPH59mOXUYnAG2ewncQS4i4F43Tv3JoAM+s2VDAmS9NsK8GpDMLrCHPksFT7h3K6TOoUNn2pb7RoXx4g==", + "dev": true + }, "node_modules/open": { "version": "7.4.2", "resolved": "https://registry.npmjs.org/open/-/open-7.4.2.tgz", @@ -3648,6 +4334,23 @@ "url": "https://github.com/sponsors/sindresorhus" } }, + "node_modules/optionator": { + "version": "0.9.1", + "resolved": "https://registry.npmjs.org/optionator/-/optionator-0.9.1.tgz", + "integrity": "sha512-74RlY5FCnhq4jRxVUPKDaRwrVNXMqsGsiW6AJw4XK8hmtm10wC0ypZBLw5IIp85NZMr91+qd1RvvENwg7jjRFw==", + "dev": true, + "dependencies": { + "deep-is": "^0.1.3", + "fast-levenshtein": "^2.0.6", + "levn": "^0.4.1", + "prelude-ls": "^1.2.1", + "type-check": "^0.4.0", + "word-wrap": "^1.2.3" + }, + "engines": { + "node": ">= 0.8.0" + } + }, "node_modules/ora": { "version": "5.4.1", "resolved": "https://registry.npmjs.org/ora/-/ora-5.4.1.tgz", @@ -3952,6 +4655,15 @@ "node": ">=0.10.0" } }, + "node_modules/package-json/node_modules/semver": { + "version": "6.3.0", + "resolved": "https://registry.npmjs.org/semver/-/semver-6.3.0.tgz", + "integrity": "sha512-b39TBaTSfV6yBrapU89p5fKekE2m/NwnDocOVruQFS1/veMgdzuPcnOM34M6CwxW8jH/lxEa5rBoDeUwu5HHTw==", + "dev": true, + "bin": { + "semver": "bin/semver.js" + } + }, "node_modules/parent-module": { "version": "1.0.1", "resolved": "https://registry.npmjs.org/parent-module/-/parent-module-1.0.1.tgz", @@ -4058,9 +4770,9 @@ } }, "node_modules/picocolors": { - "version": "0.2.1", - "resolved": "https://registry.npmjs.org/picocolors/-/picocolors-0.2.1.tgz", - "integrity": "sha512-cMlDqaLEqfSaW8Z7N5Jw+lyIW869EzT73/F5lhtY9cLGoVxSXznfgfXMO0Z5K0o0Q2TkTXq+0KFsdnSe3jDViA==", + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/picocolors/-/picocolors-1.0.0.tgz", + "integrity": "sha512-1fygroTLlHu66zi26VoTDv8yRgm0Fccecssto+MhsZ0D/DGW2sm8E8AjW7NU5VVTRt5GxbeZ5qBuJr+HyLYkjQ==", "dev": true }, "node_modules/picomatch": { @@ -4139,6 +4851,15 @@ "node": ">=8" } }, + "node_modules/prelude-ls": { + "version": "1.2.1", + "resolved": "https://registry.npmjs.org/prelude-ls/-/prelude-ls-1.2.1.tgz", + "integrity": "sha512-vkcDPrRZo1QZLbn5RLGPpg/WmIQ65qoWWhcGKf/b5eplkkarX0m9z8ppCat4mlOqUsWpyNuYgO3VRyrYHSzX5g==", + "dev": true, + "engines": { + "node": ">= 0.8.0" + } + }, "node_modules/prepend-http": { "version": "2.0.0", "resolved": "https://registry.npmjs.org/prepend-http/-/prepend-http-2.0.0.tgz", @@ -4160,6 +4881,15 @@ "node": ">=8" } }, + "node_modules/progress": { + "version": "2.0.3", + "resolved": "https://registry.npmjs.org/progress/-/progress-2.0.3.tgz", + "integrity": "sha512-7PiHtLll5LdnKIMw100I+8xJXR5gW2QwWYkT6iJva0bXitZKa/XMrSbdmg3r2Xnaidz9Qumd0VPaMrZlF9V9sA==", + "dev": true, + "engines": { + "node": ">=0.4.0" + } + }, "node_modules/protocols": { "version": "1.4.8", "resolved": "https://registry.npmjs.org/protocols/-/protocols-1.4.8.tgz", @@ -4176,6 +4906,15 @@ "once": "^1.3.1" } }, + "node_modules/punycode": { + "version": "2.1.1", + "resolved": "https://registry.npmjs.org/punycode/-/punycode-2.1.1.tgz", + "integrity": "sha512-XRsRjdf+j5ml+y/6GKHPZbrF/8p2Yga0JPtdqTIY2Xe5ohJPD9saDJJLPvp9+NSBprVvevdXZybnj2cv8OEd0A==", + "dev": true, + "engines": { + "node": ">=6" + } + }, "node_modules/pupa": { "version": "2.1.1", "resolved": "https://registry.npmjs.org/pupa/-/pupa-2.1.1.tgz", @@ -4349,6 +5088,18 @@ "node": ">=4" } }, + "node_modules/regexpp": { + "version": "3.2.0", + "resolved": "https://registry.npmjs.org/regexpp/-/regexpp-3.2.0.tgz", + "integrity": "sha512-pq2bWo9mVD43nbts2wGv17XLiNLya+GklZ8kaDLV2Z08gDCsGpnKn9BFMepvWuHCbyVvY7J5o5+BVvoQbmlJLg==", + "dev": true, + "engines": { + "node": ">=8" + }, + "funding": { + "url": "https://github.com/sponsors/mysticatea" + } + }, "node_modules/registry-auth-token": { "version": "4.2.1", "resolved": "https://registry.npmjs.org/registry-auth-token/-/registry-auth-token-4.2.1.tgz", @@ -4416,33 +5167,6 @@ "node": ">=10" } }, - "node_modules/release-it/node_modules/lru-cache": { - "version": "6.0.0", - "resolved": "https://registry.npmjs.org/lru-cache/-/lru-cache-6.0.0.tgz", - "integrity": "sha512-Jo6dJ04CmSjuznwJSS3pUeWmd/H0ffTlkXXgwZi+eq1UCmqQwCh+eLsYOYCwY991i2Fah4h1BEMCx4qThGbsiA==", - "dev": true, - "dependencies": { - "yallist": "^4.0.0" - }, - "engines": { - "node": ">=10" - } - }, - "node_modules/release-it/node_modules/semver": { - "version": "7.3.5", - "resolved": "https://registry.npmjs.org/semver/-/semver-7.3.5.tgz", - "integrity": "sha512-PoeGJYh8HK4BTO/a9Tf6ZG3veo/A7ZVsYrSA6J8ny9nb3B1VrpkuN+z9OE5wfE5p6H4LchYZsegiQgbJD94ZFQ==", - "dev": true, - "dependencies": { - "lru-cache": "^6.0.0" - }, - "bin": { - "semver": "bin/semver.js" - }, - "engines": { - "node": ">=10" - } - }, "node_modules/release-it/node_modules/uuid": { "version": "8.3.2", "resolved": "https://registry.npmjs.org/uuid/-/uuid-8.3.2.tgz", @@ -4508,12 +5232,12 @@ "dev": true }, "node_modules/resolve-from": { - "version": "5.0.0", - "resolved": "https://registry.npmjs.org/resolve-from/-/resolve-from-5.0.0.tgz", - "integrity": "sha512-qYg9KP24dD5qka9J47d0aVky0N+b4fTU89LN9iDnjB5waksiC49rvMB0PrUJQGoTmH50XPiqOvAjDfaijGxYZw==", + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/resolve-from/-/resolve-from-4.0.0.tgz", + "integrity": "sha512-pb/MYmXstAkysRFx8piNI1tGFNQIFA3vkE3Gq4EuA1dF6gHp/+vgZqsCGJapvy8N3Q+4o7FwvquPJcnZ7RYy4g==", "dev": true, "engines": { - "node": ">=8" + "node": ">=4" } }, "node_modules/responselike": { @@ -4626,12 +5350,18 @@ "dev": true }, "node_modules/semver": { - "version": "6.3.0", - "resolved": "https://registry.npmjs.org/semver/-/semver-6.3.0.tgz", - "integrity": "sha512-b39TBaTSfV6yBrapU89p5fKekE2m/NwnDocOVruQFS1/veMgdzuPcnOM34M6CwxW8jH/lxEa5rBoDeUwu5HHTw==", + "version": "7.3.5", + "resolved": "https://registry.npmjs.org/semver/-/semver-7.3.5.tgz", + "integrity": "sha512-PoeGJYh8HK4BTO/a9Tf6ZG3veo/A7ZVsYrSA6J8ny9nb3B1VrpkuN+z9OE5wfE5p6H4LchYZsegiQgbJD94ZFQ==", "dev": true, + "dependencies": { + "lru-cache": "^6.0.0" + }, "bin": { "semver": "bin/semver.js" + }, + "engines": { + "node": ">=10" } }, "node_modules/semver-diff": { @@ -4646,6 +5376,15 @@ "node": ">=8" } }, + "node_modules/semver-diff/node_modules/semver": { + "version": "6.3.0", + "resolved": "https://registry.npmjs.org/semver/-/semver-6.3.0.tgz", + "integrity": "sha512-b39TBaTSfV6yBrapU89p5fKekE2m/NwnDocOVruQFS1/veMgdzuPcnOM34M6CwxW8jH/lxEa5rBoDeUwu5HHTw==", + "dev": true, + "bin": { + "semver": "bin/semver.js" + } + }, "node_modules/serialize-javascript": { "version": "6.0.0", "resolved": "https://registry.npmjs.org/serialize-javascript/-/serialize-javascript-6.0.0.tgz", @@ -4700,9 +5439,9 @@ } }, "node_modules/shiki": { - "version": "0.9.11", - "resolved": "https://registry.npmjs.org/shiki/-/shiki-0.9.11.tgz", - "integrity": "sha512-tjruNTLFhU0hruCPoJP0y+B9LKOmcqUhTpxn7pcJB3fa+04gFChuEmxmrUfOJ7ZO6Jd+HwMnDHgY3lv3Tqonuw==", + "version": "0.9.12", + "resolved": "https://registry.npmjs.org/shiki/-/shiki-0.9.12.tgz", + "integrity": "sha512-VXcROdldv0/Qu0w2XvzU4IrvTeBNs/Kj/FCmtcEXGz7Tic/veQzliJj6tEiAgoKianhQstpYmbPDStHU5Opqcw==", "dev": true, "dependencies": { "jsonc-parser": "^3.0.0", @@ -4748,18 +5487,6 @@ "url": "https://opencollective.com/sinon" } }, - "node_modules/sinon/node_modules/supports-color": { - "version": "7.2.0", - "resolved": "https://registry.npmjs.org/supports-color/-/supports-color-7.2.0.tgz", - "integrity": "sha512-qpCAvRl9stuOHveKsn7HncJRvv501qIacKzQlO/+Lwxc9+0q2wLyv4Dfvt80/DPn2pqOBsJdDiogXGR9+OvwRw==", - "dev": true, - "dependencies": { - "has-flag": "^4.0.0" - }, - "engines": { - "node": ">=8" - } - }, "node_modules/slash": { "version": "3.0.0", "resolved": "https://registry.npmjs.org/slash/-/slash-3.0.0.tgz", @@ -4924,18 +5651,15 @@ } }, "node_modules/supports-color": { - "version": "8.1.1", - "resolved": "https://registry.npmjs.org/supports-color/-/supports-color-8.1.1.tgz", - "integrity": "sha512-MpUEN2OodtUzxvKQl72cUF7RQ5EiHsGvSsVG0ia9c5RbWGL2CI4C7EpPS8UTBIplnlzZiNuV56w+FuNxy3ty2Q==", + "version": "7.2.0", + "resolved": "https://registry.npmjs.org/supports-color/-/supports-color-7.2.0.tgz", + "integrity": "sha512-qpCAvRl9stuOHveKsn7HncJRvv501qIacKzQlO/+Lwxc9+0q2wLyv4Dfvt80/DPn2pqOBsJdDiogXGR9+OvwRw==", "dev": true, "dependencies": { "has-flag": "^4.0.0" }, "engines": { - "node": ">=10" - }, - "funding": { - "url": "https://github.com/chalk/supports-color?sponsor=1" + "node": ">=8" } }, "node_modules/test-exclude": { @@ -4952,6 +5676,12 @@ "node": ">=8" } }, + "node_modules/text-table": { + "version": "0.2.0", + "resolved": "https://registry.npmjs.org/text-table/-/text-table-0.2.0.tgz", + "integrity": "sha1-f17oI66AUgfACvLfSoTsP8+lcLQ=", + "dev": true + }, "node_modules/through": { "version": "2.3.8", "resolved": "https://registry.npmjs.org/through/-/through-2.3.8.tgz", @@ -5053,15 +5783,48 @@ "integrity": "sha512-58lmxKSA4BNyLz+HHMUzlOEpg09FV+ev6ZMe3vJihgdxzgcwZ8VoEEPmALCZG9LmqfVoNMMKpttIYTVG6uDY7A==", "dev": true, "engines": { - "node": ">=0.3.1" + "node": ">=0.3.1" + } + }, + "node_modules/tslib": { + "version": "2.1.0", + "resolved": "https://registry.npmjs.org/tslib/-/tslib-2.1.0.tgz", + "integrity": "sha512-hcVC3wYEziELGGmEEXue7D75zbwIIVUMWAVbHItGPx0ziyXxrOMQx4rQEVEV45Ut/1IotuEvwqPopzIOkDMf0A==", + "dev": true + }, + "node_modules/tsutils": { + "version": "3.21.0", + "resolved": "https://registry.npmjs.org/tsutils/-/tsutils-3.21.0.tgz", + "integrity": "sha512-mHKK3iUXL+3UF6xL5k0PEhKRUBKPBCv/+RkEOpjRWxxx27KKRBmmA60A9pgOUvMi8GKhRMPEmjBRPzs2W7O1OA==", + "dev": true, + "dependencies": { + "tslib": "^1.8.1" + }, + "engines": { + "node": ">= 6" + }, + "peerDependencies": { + "typescript": ">=2.8.0 || >= 3.2.0-dev || >= 3.3.0-dev || >= 3.4.0-dev || >= 3.5.0-dev || >= 3.6.0-dev || >= 3.6.0-beta || >= 3.7.0-dev || >= 3.7.0-beta" + } + }, + "node_modules/tsutils/node_modules/tslib": { + "version": "1.14.1", + "resolved": "https://registry.npmjs.org/tslib/-/tslib-1.14.1.tgz", + "integrity": "sha512-Xni35NKzjgMrwevysHTCArtLDpPvye8zV/0E4EyYn43P7/7qvQwPh9BGkHewbMulVntbigmcT7rdX3BNo9wRJg==", + "dev": true + }, + "node_modules/type-check": { + "version": "0.4.0", + "resolved": "https://registry.npmjs.org/type-check/-/type-check-0.4.0.tgz", + "integrity": "sha512-XleUoc9uwGXqjWwXaUTZAmzMcFZ5858QA2vvx1Ur5xIcixXIP+8LnFDgRplU30us6teqdlskFfu+ae4K79Ooew==", + "dev": true, + "dependencies": { + "prelude-ls": "^1.2.1" + }, + "engines": { + "node": ">= 0.8.0" } }, - "node_modules/tslib": { - "version": "2.1.0", - "resolved": "https://registry.npmjs.org/tslib/-/tslib-2.1.0.tgz", - "integrity": "sha512-hcVC3wYEziELGGmEEXue7D75zbwIIVUMWAVbHItGPx0ziyXxrOMQx4rQEVEV45Ut/1IotuEvwqPopzIOkDMf0A==", - "dev": true - }, "node_modules/type-detect": { "version": "4.0.8", "resolved": "https://registry.npmjs.org/type-detect/-/type-detect-4.0.8.tgz", @@ -5072,12 +5835,15 @@ } }, "node_modules/type-fest": { - "version": "0.8.1", - "resolved": "https://registry.npmjs.org/type-fest/-/type-fest-0.8.1.tgz", - "integrity": "sha512-4dbzIzqvjtgiM5rw1k5rEHtBANKmdudhGyBEajN01fEyhaAIhsoKNy6y7+IN93IfpFtwY9iqi7kD+xwKhQsNJA==", + "version": "0.20.2", + "resolved": "https://registry.npmjs.org/type-fest/-/type-fest-0.20.2.tgz", + "integrity": "sha512-Ne+eE4r0/iWnpAxD852z3A+N0Bt5RN//NjJwRd2VFHEmrywxf5vsZlh4R6lixl6B+wz/8d+maTSAkN1FIkI3LQ==", "dev": true, "engines": { - "node": ">=8" + "node": ">=10" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" } }, "node_modules/typedarray-to-buffer": { @@ -5090,9 +5856,9 @@ } }, "node_modules/typedoc": { - "version": "0.22.5", - "resolved": "https://registry.npmjs.org/typedoc/-/typedoc-0.22.5.tgz", - "integrity": "sha512-KFrWGU1iKiTGw0RcyjLNYDmhd7uICU14HgBNPmFKY/sT4Pm/fraaLyWyisst9vGTUAKxqibqoDITR7+ZcAkhHg==", + "version": "0.22.6", + "resolved": "https://registry.npmjs.org/typedoc/-/typedoc-0.22.6.tgz", + "integrity": "sha512-ePbJqOaz0GNkU2ehRwFwBpLD4Gp6m7jbJfHysXmDdjVKc1g8DFJ83r/LOZ9TZrkC661vgpoIY3FjSPEtUilHNA==", "dev": true, "dependencies": { "glob": "^7.2.0", @@ -5154,9 +5920,9 @@ } }, "node_modules/typescript": { - "version": "4.4.3", - "resolved": "https://registry.npmjs.org/typescript/-/typescript-4.4.3.tgz", - "integrity": "sha512-4xfscpisVgqqDfPaJo5vkd+Qd/ItkoagnHpufr+i2QCHBsNYp+G7UAoyFl8aPtx879u38wPV65rZ8qbGZijalA==", + "version": "4.4.4", + "resolved": "https://registry.npmjs.org/typescript/-/typescript-4.4.4.tgz", + "integrity": "sha512-DqGhF5IKoBl8WNf8C1gu8q0xZSInh9j1kJJMqT3a94w1JzVaBU4EXOSMrz9yDqMT0xt3selp83fuFMQ0uzv6qA==", "dev": true, "bin": { "tsc": "bin/tsc", @@ -5243,31 +6009,13 @@ "is-ci": "bin.js" } }, - "node_modules/update-notifier/node_modules/lru-cache": { - "version": "6.0.0", - "resolved": "https://registry.npmjs.org/lru-cache/-/lru-cache-6.0.0.tgz", - "integrity": "sha512-Jo6dJ04CmSjuznwJSS3pUeWmd/H0ffTlkXXgwZi+eq1UCmqQwCh+eLsYOYCwY991i2Fah4h1BEMCx4qThGbsiA==", - "dev": true, - "dependencies": { - "yallist": "^4.0.0" - }, - "engines": { - "node": ">=10" - } - }, - "node_modules/update-notifier/node_modules/semver": { - "version": "7.3.5", - "resolved": "https://registry.npmjs.org/semver/-/semver-7.3.5.tgz", - "integrity": "sha512-PoeGJYh8HK4BTO/a9Tf6ZG3veo/A7ZVsYrSA6J8ny9nb3B1VrpkuN+z9OE5wfE5p6H4LchYZsegiQgbJD94ZFQ==", + "node_modules/uri-js": { + "version": "4.4.1", + "resolved": "https://registry.npmjs.org/uri-js/-/uri-js-4.4.1.tgz", + "integrity": "sha512-7rKUyy33Q1yc98pQ1DAmLtwX109F7TIfWlW1Ydo8Wl1ii1SeHieeh0HHfPeL2fMXK6z0s8ecKs9frCuLJvndBg==", "dev": true, "dependencies": { - "lru-cache": "^6.0.0" - }, - "bin": { - "semver": "bin/semver.js" - }, - "engines": { - "node": ">=10" + "punycode": "^2.1.0" } }, "node_modules/url-join": { @@ -5304,6 +6052,12 @@ "uuid": "bin/uuid" } }, + "node_modules/v8-compile-cache": { + "version": "2.3.0", + "resolved": "https://registry.npmjs.org/v8-compile-cache/-/v8-compile-cache-2.3.0.tgz", + "integrity": "sha512-l8lCEmLcLYZh4nbunNZvQCJc5pv7+RCwa8q/LdUx8u7lsWvPDKmpodJAJNwkAhJC//dFY48KuIEmjtd4RViDrA==", + "dev": true + }, "node_modules/vscode-textmate": { "version": "5.2.0", "resolved": "https://registry.npmjs.org/vscode-textmate/-/vscode-textmate-5.2.0.tgz", @@ -5430,6 +6184,15 @@ "node": ">=8.12.0" } }, + "node_modules/word-wrap": { + "version": "1.2.3", + "resolved": "https://registry.npmjs.org/word-wrap/-/word-wrap-1.2.3.tgz", + "integrity": "sha512-Hz/mrNwitNRh/HUAtM/VT/5VH+ygD6DV7mYKZAtHOrbs8U7lvPS6xf7EJKMF0uW1KJCl0H701g3ZGus+muE5vQ==", + "dev": true, + "engines": { + "node": ">=0.10.0" + } + }, "node_modules/wordwrap": { "version": "1.0.0", "resolved": "https://registry.npmjs.org/wordwrap/-/wordwrap-1.0.0.tgz", @@ -5634,6 +6397,14 @@ "json5": "^2.1.2", "semver": "^6.3.0", "source-map": "^0.5.0" + }, + "dependencies": { + "semver": { + "version": "6.3.0", + "resolved": "https://registry.npmjs.org/semver/-/semver-6.3.0.tgz", + "integrity": "sha512-b39TBaTSfV6yBrapU89p5fKekE2m/NwnDocOVruQFS1/veMgdzuPcnOM34M6CwxW8jH/lxEa5rBoDeUwu5HHTw==", + "dev": true + } } }, "@babel/generator": { @@ -5657,6 +6428,14 @@ "@babel/helper-validator-option": "^7.14.5", "browserslist": "^4.16.6", "semver": "^6.3.0" + }, + "dependencies": { + "semver": { + "version": "6.3.0", + "resolved": "https://registry.npmjs.org/semver/-/semver-6.3.0.tgz", + "integrity": "sha512-b39TBaTSfV6yBrapU89p5fKekE2m/NwnDocOVruQFS1/veMgdzuPcnOM34M6CwxW8jH/lxEa5rBoDeUwu5HHTw==", + "dev": true + } } }, "@babel/helper-function-name": { @@ -5885,6 +6664,14 @@ "@babel/types": "^7.15.4", "debug": "^4.1.0", "globals": "^11.1.0" + }, + "dependencies": { + "globals": { + "version": "11.12.0", + "resolved": "https://registry.npmjs.org/globals/-/globals-11.12.0.tgz", + "integrity": "sha512-WOBp/EEGUiIsJSp7wcv/y6MO+lV9UoncWqxuFfm8eBwzWNgyfBd6Gz+IeKQ9jCmyhoH99g15M3T+QaVHFjizVA==", + "dev": true + } } }, "@babel/types": { @@ -5912,6 +6699,67 @@ "@cspotcode/source-map-consumer": "0.8.0" } }, + "@eslint/eslintrc": { + "version": "1.0.3", + "resolved": "https://registry.npmjs.org/@eslint/eslintrc/-/eslintrc-1.0.3.tgz", + "integrity": "sha512-DHI1wDPoKCBPoLZA3qDR91+3te/wDSc1YhKg3jR8NxKKRJq2hwHwcWv31cSwSYvIBrmbENoYMWcenW8uproQqg==", + "dev": true, + "requires": { + "ajv": "^6.12.4", + "debug": "^4.3.2", + "espree": "^9.0.0", + "globals": "^13.9.0", + "ignore": "^4.0.6", + "import-fresh": "^3.2.1", + "js-yaml": "^3.13.1", + "minimatch": "^3.0.4", + "strip-json-comments": "^3.1.1" + }, + "dependencies": { + "argparse": { + "version": "1.0.10", + "resolved": "https://registry.npmjs.org/argparse/-/argparse-1.0.10.tgz", + "integrity": "sha512-o5Roy6tNG4SL/FOkCAN6RzjiakZS25RLYFrcMttJqbdd8BWrnA+fGz57iN5Pb06pvBGvl5gQ0B48dJlslXvoTg==", + "dev": true, + "requires": { + "sprintf-js": "~1.0.2" + } + }, + "ignore": { + "version": "4.0.6", + "resolved": "https://registry.npmjs.org/ignore/-/ignore-4.0.6.tgz", + "integrity": "sha512-cyFDKrqc/YdcWFniJhzI42+AzS+gNwmUzOSFcRCQYwySuBBBy/KjuxWLZ/FHEH6Moq1NizMOBWyTcv8O4OZIMg==", + "dev": true + }, + "js-yaml": { + "version": "3.14.1", + "resolved": "https://registry.npmjs.org/js-yaml/-/js-yaml-3.14.1.tgz", + "integrity": "sha512-okMH7OXXJ7YrN9Ok3/SXrnu4iX9yOk+25nqX4imS2npuvTYDmo/QEZoqwZkYaIDk3jVvBOTOIEgEhaLOynBS9g==", + "dev": true, + "requires": { + "argparse": "^1.0.7", + "esprima": "^4.0.0" + } + } + } + }, + "@humanwhocodes/config-array": { + "version": "0.6.0", + "resolved": "https://registry.npmjs.org/@humanwhocodes/config-array/-/config-array-0.6.0.tgz", + "integrity": "sha512-JQlEKbcgEUjBFhLIF4iqM7u/9lwgHRBcpHrmUNCALK0Q3amXN6lxdoXLnF0sm11E9VqTmBALR87IlUg1bZ8A9A==", + "dev": true, + "requires": { + "@humanwhocodes/object-schema": "^1.2.0", + "debug": "^4.1.1", + "minimatch": "^3.0.4" + } + }, + "@humanwhocodes/object-schema": { + "version": "1.2.0", + "resolved": "https://registry.npmjs.org/@humanwhocodes/object-schema/-/object-schema-1.2.0.tgz", + "integrity": "sha512-wdppn25U8z/2yiaT6YGquE6X8sSv7hNMWSXYSSU1jGv/yd6XqjXgTDJ8KP4NgjTXfJ3GbRjeeb8RTV7a/VpM+w==", + "dev": true + }, "@iarna/toml": { "version": "2.2.5", "resolved": "https://registry.npmjs.org/@iarna/toml/-/toml-2.2.5.tgz", @@ -5986,6 +6834,12 @@ "requires": { "p-limit": "^2.2.0" } + }, + "resolve-from": { + "version": "5.0.0", + "resolved": "https://registry.npmjs.org/resolve-from/-/resolve-from-5.0.0.tgz", + "integrity": "sha512-qYg9KP24dD5qka9J47d0aVky0N+b4fTU89LN9iDnjB5waksiC49rvMB0PrUJQGoTmH50XPiqOvAjDfaijGxYZw==", + "dev": true } } }, @@ -6246,6 +7100,12 @@ "integrity": "sha512-SZs7ekbP8CN0txVG2xVRH6EgKmEm31BOxA07vkFaETzZz1xh+cbt8BcI0slpymvwhx5dlFnQG2rTlPVQn+iRPQ==", "dev": true }, + "@types/json-schema": { + "version": "7.0.9", + "resolved": "https://registry.npmjs.org/@types/json-schema/-/json-schema-7.0.9.tgz", + "integrity": "sha512-qcUXuemtEu+E5wZSJHNxUXeCZhAfXKQ41D+duX+VYPde7xyEVZci+/oXKJL13tnRs9lR2pr4fod59GT6/X1/yQ==", + "dev": true + }, "@types/keyv": { "version": "3.1.3", "resolved": "https://registry.npmjs.org/@types/keyv/-/keyv-3.1.3.tgz", @@ -6262,9 +7122,9 @@ "dev": true }, "@types/node": { - "version": "16.10.3", - "resolved": "https://registry.npmjs.org/@types/node/-/node-16.10.3.tgz", - "integrity": "sha512-ho3Ruq+fFnBrZhUYI46n/bV2GjwzSkwuT4dTf0GkuNFmnb8nq4ny2z9JEVemFi6bdEJanHLlYfy9c6FN9B9McQ==", + "version": "16.11.1", + "resolved": "https://registry.npmjs.org/@types/node/-/node-16.11.1.tgz", + "integrity": "sha512-PYGcJHL9mwl1Ek3PLiYgyEKtwTMmkMw4vbiyz/ps3pfdRYLVv+SN7qHVAImrjdAXxgluDEw6Ph4lyv+m9UpRmA==", "dev": true }, "@types/parse-json": { @@ -6303,6 +7163,89 @@ "integrity": "sha512-G3FNJfaYtN8URU6wd6+uwFI62KO79j7n3XTYcwcFncP8gkfoi0b821GoVVt0oqKVnCqKYOMNKIGpakPoFhzAGA==", "dev": true }, + "@typescript-eslint/eslint-plugin": { + "version": "5.1.0", + "resolved": "https://registry.npmjs.org/@typescript-eslint/eslint-plugin/-/eslint-plugin-5.1.0.tgz", + "integrity": "sha512-bekODL3Tqf36Yz8u+ilha4zGxL9mdB6LIsIoMAvvC5FAuWo4NpZYXtCbv7B2CeR1LhI/lLtLk+q4tbtxuoVuCg==", + "dev": true, + "requires": { + "@typescript-eslint/experimental-utils": "5.1.0", + "@typescript-eslint/scope-manager": "5.1.0", + "debug": "^4.3.2", + "functional-red-black-tree": "^1.0.1", + "ignore": "^5.1.8", + "regexpp": "^3.2.0", + "semver": "^7.3.5", + "tsutils": "^3.21.0" + } + }, + "@typescript-eslint/experimental-utils": { + "version": "5.1.0", + "resolved": "https://registry.npmjs.org/@typescript-eslint/experimental-utils/-/experimental-utils-5.1.0.tgz", + "integrity": "sha512-ovE9qUiZMOMgxQAESZsdBT+EXIfx/YUYAbwGUI6V03amFdOOxI9c6kitkgRvLkJaLusgMZ2xBhss+tQ0Y1HWxA==", + "dev": true, + "requires": { + "@types/json-schema": "^7.0.9", + "@typescript-eslint/scope-manager": "5.1.0", + "@typescript-eslint/types": "5.1.0", + "@typescript-eslint/typescript-estree": "5.1.0", + "eslint-scope": "^5.1.1", + "eslint-utils": "^3.0.0" + } + }, + "@typescript-eslint/parser": { + "version": "5.1.0", + "resolved": "https://registry.npmjs.org/@typescript-eslint/parser/-/parser-5.1.0.tgz", + "integrity": "sha512-vx1P+mhCtYw3+bRHmbalq/VKP2Y3gnzNgxGxfEWc6OFpuEL7iQdAeq11Ke3Rhy8NjgB+AHsIWEwni3e+Y7djKA==", + "dev": true, + "requires": { + "@typescript-eslint/scope-manager": "5.1.0", + "@typescript-eslint/types": "5.1.0", + "@typescript-eslint/typescript-estree": "5.1.0", + "debug": "^4.3.2" + } + }, + "@typescript-eslint/scope-manager": { + "version": "5.1.0", + "resolved": "https://registry.npmjs.org/@typescript-eslint/scope-manager/-/scope-manager-5.1.0.tgz", + "integrity": "sha512-yYlyVjvn5lvwCL37i4hPsa1s0ORsjkauhTqbb8MnpvUs7xykmcjGqwlNZ2Q5QpoqkJ1odlM2bqHqJwa28qV6Tw==", + "dev": true, + "requires": { + "@typescript-eslint/types": "5.1.0", + "@typescript-eslint/visitor-keys": "5.1.0" + } + }, + "@typescript-eslint/types": { + "version": "5.1.0", + "resolved": "https://registry.npmjs.org/@typescript-eslint/types/-/types-5.1.0.tgz", + "integrity": "sha512-sEwNINVxcB4ZgC6Fe6rUyMlvsB2jvVdgxjZEjQUQVlaSPMNamDOwO6/TB98kFt4sYYfNhdhTPBEQqNQZjMMswA==", + "dev": true + }, + "@typescript-eslint/typescript-estree": { + "version": "5.1.0", + "resolved": "https://registry.npmjs.org/@typescript-eslint/typescript-estree/-/typescript-estree-5.1.0.tgz", + "integrity": "sha512-SSz+l9YrIIsW4s0ZqaEfnjl156XQ4VRmJsbA0ZE1XkXrD3cRpzuZSVCyqeCMR3EBjF27IisWakbBDGhGNIOvfQ==", + "dev": true, + "requires": { + "@typescript-eslint/types": "5.1.0", + "@typescript-eslint/visitor-keys": "5.1.0", + "debug": "^4.3.2", + "globby": "^11.0.4", + "is-glob": "^4.0.3", + "semver": "^7.3.5", + "tsutils": "^3.21.0" + } + }, + "@typescript-eslint/visitor-keys": { + "version": "5.1.0", + "resolved": "https://registry.npmjs.org/@typescript-eslint/visitor-keys/-/visitor-keys-5.1.0.tgz", + "integrity": "sha512-uqNXepKBg81JVwjuqAxYrXa1Ql/YDzM+8g/pS+TCPxba0wZttl8m5DkrasbfnmJGHs4lQ2jTbcZ5azGhI7kK+w==", + "dev": true, + "requires": { + "@typescript-eslint/types": "5.1.0", + "eslint-visitor-keys": "^3.0.0" + } + }, "@ungap/promise-all-settled": { "version": "1.1.2", "resolved": "https://registry.npmjs.org/@ungap/promise-all-settled/-/promise-all-settled-1.1.2.tgz", @@ -6315,6 +7258,13 @@ "integrity": "sha512-yXbYeFy+jUuYd3/CDcg2NkIYE991XYX/bje7LmjJigUciaeO1JR4XxXgCIV1/Zc/dRuFEyw1L0pbA+qynJkW5Q==", "dev": true }, + "acorn-jsx": { + "version": "5.3.2", + "resolved": "https://registry.npmjs.org/acorn-jsx/-/acorn-jsx-5.3.2.tgz", + "integrity": "sha512-rq9s+JNhf0IChjtDXxllJ7g41oZk5SlXtp0LHwyA5cejwn7vKmKp4pPri6YEePv2PU65sAsegbXtIinmDFDXgQ==", + "dev": true, + "requires": {} + }, "acorn-walk": { "version": "8.2.0", "resolved": "https://registry.npmjs.org/acorn-walk/-/acorn-walk-8.2.0.tgz", @@ -6331,6 +7281,18 @@ "indent-string": "^4.0.0" } }, + "ajv": { + "version": "6.12.6", + "resolved": "https://registry.npmjs.org/ajv/-/ajv-6.12.6.tgz", + "integrity": "sha512-j3fVLgvTo527anyYyJOGTYJbG+vnnQYvE0m5mmkc1TK+nxAppkCLMIL0aZ4dblVCNoGShhm+kzE4ZUykBoMg4g==", + "dev": true, + "requires": { + "fast-deep-equal": "^3.1.1", + "fast-json-stable-stringify": "^2.0.0", + "json-schema-traverse": "^0.4.1", + "uri-js": "^4.2.2" + } + }, "ansi-align": { "version": "3.0.1", "resolved": "https://registry.npmjs.org/ansi-align/-/ansi-align-3.0.1.tgz", @@ -6492,12 +7454,6 @@ "resolved": "https://registry.npmjs.org/camelcase/-/camelcase-6.2.0.tgz", "integrity": "sha512-c7wVvbw3f37nuobQNtgsgG9POC9qMbNuMQmTCqZv23b6MIz0fcYpBiOlv9gEN/hdLdnZTDQhg6e9Dq5M1vKvfg==", "dev": true - }, - "type-fest": { - "version": "0.20.2", - "resolved": "https://registry.npmjs.org/type-fest/-/type-fest-0.20.2.tgz", - "integrity": "sha512-Ne+eE4r0/iWnpAxD852z3A+N0Bt5RN//NjJwRd2VFHEmrywxf5vsZlh4R6lixl6B+wz/8d+maTSAkN1FIkI3LQ==", - "dev": true } } }, @@ -6527,16 +7483,16 @@ "dev": true }, "browserslist": { - "version": "4.17.3", - "resolved": "https://registry.npmjs.org/browserslist/-/browserslist-4.17.3.tgz", - "integrity": "sha512-59IqHJV5VGdcJZ+GZ2hU5n4Kv3YiASzW6Xk5g9tf5a/MAzGeFwgGWU39fVzNIOVcgB3+Gp+kiQu0HEfTVU/3VQ==", + "version": "4.17.4", + "resolved": "https://registry.npmjs.org/browserslist/-/browserslist-4.17.4.tgz", + "integrity": "sha512-Zg7RpbZpIJRW3am9Lyckue7PLytvVxxhJj1CaJVlCWENsGEAOlnlt8X0ZxGRPp7Bt9o8tIRM5SEXy4BCPMJjLQ==", "dev": true, "requires": { - "caniuse-lite": "^1.0.30001264", - "electron-to-chromium": "^1.3.857", + "caniuse-lite": "^1.0.30001265", + "electron-to-chromium": "^1.3.867", "escalade": "^3.1.1", - "node-releases": "^1.1.77", - "picocolors": "^0.2.1" + "node-releases": "^2.0.0", + "picocolors": "^1.0.0" } }, "buffer": { @@ -6622,9 +7578,9 @@ "dev": true }, "caniuse-lite": { - "version": "1.0.30001265", - "resolved": "https://registry.npmjs.org/caniuse-lite/-/caniuse-lite-1.0.30001265.tgz", - "integrity": "sha512-YzBnspggWV5hep1m9Z6sZVLOt7vrju8xWooFAgN6BA5qvy98qPAPb7vNUzypFaoh2pb3vlfzbDO8tB57UPGbtw==", + "version": "1.0.30001269", + "resolved": "https://registry.npmjs.org/caniuse-lite/-/caniuse-lite-1.0.30001269.tgz", + "integrity": "sha512-UOy8okEVs48MyHYgV+RdW1Oiudl1H6KolybD6ZquD0VcrPSgj25omXO1S7rDydjpqaISCwA8Pyx+jUQKZwWO5w==", "dev": true }, "chalk": { @@ -6635,17 +7591,6 @@ "requires": { "ansi-styles": "^4.1.0", "supports-color": "^7.1.0" - }, - "dependencies": { - "supports-color": { - "version": "7.2.0", - "resolved": "https://registry.npmjs.org/supports-color/-/supports-color-7.2.0.tgz", - "integrity": "sha512-qpCAvRl9stuOHveKsn7HncJRvv501qIacKzQlO/+Lwxc9+0q2wLyv4Dfvt80/DPn2pqOBsJdDiogXGR9+OvwRw==", - "dev": true, - "requires": { - "has-flag": "^4.0.0" - } - } } }, "chardet": { @@ -6668,6 +7613,17 @@ "is-glob": "~4.0.1", "normalize-path": "~3.0.0", "readdirp": "~3.6.0" + }, + "dependencies": { + "glob-parent": { + "version": "5.1.2", + "resolved": "https://registry.npmjs.org/glob-parent/-/glob-parent-5.1.2.tgz", + "integrity": "sha512-AOIgSQCepiJYwP3ARnGx+5VnTu2HBYdzbGP45eLw1vr3zB3vZLeyed1sC9hnbcOc9/SrMyM5RPQrkGz4aS9Zow==", + "dev": true, + "requires": { + "is-glob": "^4.0.1" + } + } } }, "ci-info": { @@ -6842,14 +7798,6 @@ "dev": true, "requires": { "ms": "2.1.2" - }, - "dependencies": { - "ms": { - "version": "2.1.2", - "resolved": "https://registry.npmjs.org/ms/-/ms-2.1.2.tgz", - "integrity": "sha512-sGkPx+VjMtmA6MX27oA4FBFELFCZZ4S4XqeGOXCv68tT+jb3vk/RyaKWP0PTKyWtmLSM0b+adUTEvbs1PEaH2w==", - "dev": true - } } }, "decamelize": { @@ -6887,6 +7835,12 @@ "integrity": "sha512-LOHxIOaPYdHlJRtCQfDIVZtfw/ufM8+rVj649RIHzcm/vGwQRXFt6OPqIFWsm2XEMrNIEtWR64sY1LEKD2vAOA==", "dev": true }, + "deep-is": { + "version": "0.1.4", + "resolved": "https://registry.npmjs.org/deep-is/-/deep-is-0.1.4.tgz", + "integrity": "sha512-oIPzksmTg4/MriiaYGO+okXDT7ztn/w3Eptv/+gSIdMdKsJo0u4CfYNFJPy+4SKMuCqGw2wxnA+URMg3t8a/bQ==", + "dev": true + }, "default-require-extensions": { "version": "3.0.0", "resolved": "https://registry.npmjs.org/default-require-extensions/-/default-require-extensions-3.0.0.tgz", @@ -6948,6 +7902,15 @@ "path-type": "^4.0.0" } }, + "doctrine": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/doctrine/-/doctrine-3.0.0.tgz", + "integrity": "sha512-yS+Q5i3hBf7GBkd4KG8a7eBNNWNGLTaEwwYWUijIYM7zrlYDM0BFXHjjPWlWZ1Rg7UaddZeIDmi9jF3HmqiQ2w==", + "dev": true, + "requires": { + "esutils": "^2.0.2" + } + }, "dot-prop": { "version": "5.3.0", "resolved": "https://registry.npmjs.org/dot-prop/-/dot-prop-5.3.0.tgz", @@ -6964,9 +7927,9 @@ "dev": true }, "electron-to-chromium": { - "version": "1.3.864", - "resolved": "https://registry.npmjs.org/electron-to-chromium/-/electron-to-chromium-1.3.864.tgz", - "integrity": "sha512-v4rbad8GO6/yVI92WOeU9Wgxc4NA0n4f6P1FvZTY+jyY7JHEhw3bduYu60v3Q1h81Cg6eo4ApZrFPuycwd5hGw==", + "version": "1.3.872", + "resolved": "https://registry.npmjs.org/electron-to-chromium/-/electron-to-chromium-1.3.872.tgz", + "integrity": "sha512-qG96atLFY0agKyEETiBFNhpRLSXGSXOBuhXWpbkYqrLKKASpRyRBUtfkn0ZjIf/yXfA7FA4nScVOMpXSHFlUCQ==", "dev": true }, "emoji-regex": { @@ -6984,6 +7947,15 @@ "once": "^1.4.0" } }, + "enquirer": { + "version": "2.3.6", + "resolved": "https://registry.npmjs.org/enquirer/-/enquirer-2.3.6.tgz", + "integrity": "sha512-yjNnPr315/FjS4zIsUxYguYUPP2e1NK4d7E7ZOLiyYCcbFBiTMyID+2wvm2w6+pZ/odMA7cRkjhsPbltwBOrLg==", + "dev": true, + "requires": { + "ansi-colors": "^4.1.1" + } + }, "error-ex": { "version": "1.3.2", "resolved": "https://registry.npmjs.org/error-ex/-/error-ex-1.3.2.tgz", @@ -7017,12 +7989,172 @@ "integrity": "sha512-TtpcNJ3XAzx3Gq8sWRzJaVajRs0uVxA2YAkdb1jm2YkPz4G6egUFAyA3n5vtEIZefPk5Wa4UXbKuS5fKkJWdgA==", "dev": true }, + "eslint": { + "version": "8.0.1", + "resolved": "https://registry.npmjs.org/eslint/-/eslint-8.0.1.tgz", + "integrity": "sha512-LsgcwZgQ72vZ+SMp4K6pAnk2yFDWL7Ti4pJaRvsZ0Hsw2h8ZjUIW38a9AFn2cZXdBMlScMFYYgsSp4ttFI/0bA==", + "dev": true, + "requires": { + "@eslint/eslintrc": "^1.0.3", + "@humanwhocodes/config-array": "^0.6.0", + "ajv": "^6.10.0", + "chalk": "^4.0.0", + "cross-spawn": "^7.0.2", + "debug": "^4.3.2", + "doctrine": "^3.0.0", + "enquirer": "^2.3.5", + "escape-string-regexp": "^4.0.0", + "eslint-scope": "^6.0.0", + "eslint-utils": "^3.0.0", + "eslint-visitor-keys": "^3.0.0", + "espree": "^9.0.0", + "esquery": "^1.4.0", + "esutils": "^2.0.2", + "fast-deep-equal": "^3.1.3", + "file-entry-cache": "^6.0.1", + "functional-red-black-tree": "^1.0.1", + "glob-parent": "^6.0.1", + "globals": "^13.6.0", + "ignore": "^4.0.6", + "import-fresh": "^3.0.0", + "imurmurhash": "^0.1.4", + "is-glob": "^4.0.0", + "js-yaml": "^4.1.0", + "json-stable-stringify-without-jsonify": "^1.0.1", + "levn": "^0.4.1", + "lodash.merge": "^4.6.2", + "minimatch": "^3.0.4", + "natural-compare": "^1.4.0", + "optionator": "^0.9.1", + "progress": "^2.0.0", + "regexpp": "^3.2.0", + "semver": "^7.2.1", + "strip-ansi": "^6.0.0", + "strip-json-comments": "^3.1.0", + "text-table": "^0.2.0", + "v8-compile-cache": "^2.0.3" + }, + "dependencies": { + "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==", + "dev": true, + "requires": { + "esrecurse": "^4.3.0", + "estraverse": "^5.2.0" + } + }, + "estraverse": { + "version": "5.2.0", + "resolved": "https://registry.npmjs.org/estraverse/-/estraverse-5.2.0.tgz", + "integrity": "sha512-BxbNGGNm0RyRYvUdHpIwv9IWzeM9XClbOxwoATuFdOE7ZE6wHL+HQ5T8hoPM+zHvmKzzsEqhgy0GrQ5X13afiQ==", + "dev": true + }, + "ignore": { + "version": "4.0.6", + "resolved": "https://registry.npmjs.org/ignore/-/ignore-4.0.6.tgz", + "integrity": "sha512-cyFDKrqc/YdcWFniJhzI42+AzS+gNwmUzOSFcRCQYwySuBBBy/KjuxWLZ/FHEH6Moq1NizMOBWyTcv8O4OZIMg==", + "dev": true + } + } + }, + "eslint-scope": { + "version": "5.1.1", + "resolved": "https://registry.npmjs.org/eslint-scope/-/eslint-scope-5.1.1.tgz", + "integrity": "sha512-2NxwbF/hZ0KpepYN0cNbo+FN6XoK7GaHlQhgx/hIZl6Va0bF45RQOOwhLIy8lQDbuCiadSLCBnH2CFYquit5bw==", + "dev": true, + "requires": { + "esrecurse": "^4.3.0", + "estraverse": "^4.1.1" + } + }, + "eslint-utils": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/eslint-utils/-/eslint-utils-3.0.0.tgz", + "integrity": "sha512-uuQC43IGctw68pJA1RgbQS8/NP7rch6Cwd4j3ZBtgo4/8Flj4eGE7ZYSZRN3iq5pVUv6GPdW5Z1RFleo84uLDA==", + "dev": true, + "requires": { + "eslint-visitor-keys": "^2.0.0" + }, + "dependencies": { + "eslint-visitor-keys": { + "version": "2.1.0", + "resolved": "https://registry.npmjs.org/eslint-visitor-keys/-/eslint-visitor-keys-2.1.0.tgz", + "integrity": "sha512-0rSmRBzXgDzIsD6mGdJgevzgezI534Cer5L/vyMX0kHzT/jiB43jRhd9YUlMGYLQy2zprNmoT8qasCGtY+QaKw==", + "dev": true + } + } + }, + "eslint-visitor-keys": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/eslint-visitor-keys/-/eslint-visitor-keys-3.0.0.tgz", + "integrity": "sha512-mJOZa35trBTb3IyRmo8xmKBZlxf+N7OnUl4+ZhJHs/r+0770Wh/LEACE2pqMGMe27G/4y8P2bYGk4J70IC5k1Q==", + "dev": true + }, + "espree": { + "version": "9.0.0", + "resolved": "https://registry.npmjs.org/espree/-/espree-9.0.0.tgz", + "integrity": "sha512-r5EQJcYZ2oaGbeR0jR0fFVijGOcwai07/690YRXLINuhmVeRY4UKSAsQPe/0BNuDgwP7Ophoc1PRsr2E3tkbdQ==", + "dev": true, + "requires": { + "acorn": "^8.5.0", + "acorn-jsx": "^5.3.1", + "eslint-visitor-keys": "^3.0.0" + } + }, "esprima": { "version": "4.0.1", "resolved": "https://registry.npmjs.org/esprima/-/esprima-4.0.1.tgz", "integrity": "sha512-eGuFFw7Upda+g4p+QHvnW0RyTX/SVeJBDM/gCtMARO0cLuT2HcEKnTPvhjV6aGeqrCB/sbNop0Kszm0jsaWU4A==", "dev": true }, + "esquery": { + "version": "1.4.0", + "resolved": "https://registry.npmjs.org/esquery/-/esquery-1.4.0.tgz", + "integrity": "sha512-cCDispWt5vHHtwMY2YrAQ4ibFkAL8RbH5YGBnZBc90MolvvfkkQcJro/aZiAQUlQ3qgrYS6D6v8Gc5G5CQsc9w==", + "dev": true, + "requires": { + "estraverse": "^5.1.0" + }, + "dependencies": { + "estraverse": { + "version": "5.2.0", + "resolved": "https://registry.npmjs.org/estraverse/-/estraverse-5.2.0.tgz", + "integrity": "sha512-BxbNGGNm0RyRYvUdHpIwv9IWzeM9XClbOxwoATuFdOE7ZE6wHL+HQ5T8hoPM+zHvmKzzsEqhgy0GrQ5X13afiQ==", + "dev": true + } + } + }, + "esrecurse": { + "version": "4.3.0", + "resolved": "https://registry.npmjs.org/esrecurse/-/esrecurse-4.3.0.tgz", + "integrity": "sha512-KmfKL3b6G+RXvP8N1vr3Tq1kL/oCFgn2NYXEtqP8/L3pKapUA4G8cFVaoF3SU323CD4XypR/ffioHmkti6/Tag==", + "dev": true, + "requires": { + "estraverse": "^5.2.0" + }, + "dependencies": { + "estraverse": { + "version": "5.2.0", + "resolved": "https://registry.npmjs.org/estraverse/-/estraverse-5.2.0.tgz", + "integrity": "sha512-BxbNGGNm0RyRYvUdHpIwv9IWzeM9XClbOxwoATuFdOE7ZE6wHL+HQ5T8hoPM+zHvmKzzsEqhgy0GrQ5X13afiQ==", + "dev": true + } + } + }, + "estraverse": { + "version": "4.3.0", + "resolved": "https://registry.npmjs.org/estraverse/-/estraverse-4.3.0.tgz", + "integrity": "sha512-39nnKffWz8xN1BU/2c79n9nB9HDzo0niYUqx6xyqUnyoAnQyyWpOTdZEeiCch8BBu515t4wp9ZmgVfVhn9EBpw==", + "dev": true + }, + "esutils": { + "version": "2.0.3", + "resolved": "https://registry.npmjs.org/esutils/-/esutils-2.0.3.tgz", + "integrity": "sha512-kVscqXk4OCp68SZ0dkgEKVi6/8ij300KBWTJq32P/dYeWTSwK41WyTxalN1eRmA5Z9UU/LX9D7FWSmV9SAYx6g==", + "dev": true + }, "execa": { "version": "5.1.1", "resolved": "https://registry.npmjs.org/execa/-/execa-5.1.1.tgz", @@ -7051,6 +8183,12 @@ "tmp": "^0.0.33" } }, + "fast-deep-equal": { + "version": "3.1.3", + "resolved": "https://registry.npmjs.org/fast-deep-equal/-/fast-deep-equal-3.1.3.tgz", + "integrity": "sha512-f3qQ9oQy9j2AhBe/H9VC91wLmKBCCU/gDOnKNAYG5hswO7BLKj09Hc5HYNz9cGI++xlpDCIgDaitVs03ATR84Q==", + "dev": true + }, "fast-glob": { "version": "3.2.7", "resolved": "https://registry.npmjs.org/fast-glob/-/fast-glob-3.2.7.tgz", @@ -7062,8 +8200,31 @@ "glob-parent": "^5.1.2", "merge2": "^1.3.0", "micromatch": "^4.0.4" + }, + "dependencies": { + "glob-parent": { + "version": "5.1.2", + "resolved": "https://registry.npmjs.org/glob-parent/-/glob-parent-5.1.2.tgz", + "integrity": "sha512-AOIgSQCepiJYwP3ARnGx+5VnTu2HBYdzbGP45eLw1vr3zB3vZLeyed1sC9hnbcOc9/SrMyM5RPQrkGz4aS9Zow==", + "dev": true, + "requires": { + "is-glob": "^4.0.1" + } + } } }, + "fast-json-stable-stringify": { + "version": "2.1.0", + "resolved": "https://registry.npmjs.org/fast-json-stable-stringify/-/fast-json-stable-stringify-2.1.0.tgz", + "integrity": "sha512-lhd/wF+Lk98HZoTCtlVraHtfh5XYijIjalXck7saUtuanSDyLMxnHhSXEDJqHxD7msR8D0uCmqlkwjCV8xvwHw==", + "dev": true + }, + "fast-levenshtein": { + "version": "2.0.6", + "resolved": "https://registry.npmjs.org/fast-levenshtein/-/fast-levenshtein-2.0.6.tgz", + "integrity": "sha1-PYpcZog6FqMMqGQ+hR8Zuqd5eRc=", + "dev": true + }, "fastq": { "version": "1.13.0", "resolved": "https://registry.npmjs.org/fastq/-/fastq-1.13.0.tgz", @@ -7090,6 +8251,15 @@ } } }, + "file-entry-cache": { + "version": "6.0.1", + "resolved": "https://registry.npmjs.org/file-entry-cache/-/file-entry-cache-6.0.1.tgz", + "integrity": "sha512-7Gps/XWymbLk2QLYK4NzpMOrYjMhdIxXuIvy2QBsLE6ljuodKvdkWs/cpyJJ3CVIVpH0Oi1Hvg1ovbMzLdFBBg==", + "dev": true, + "requires": { + "flat-cache": "^3.0.4" + } + }, "fill-range": { "version": "7.0.1", "resolved": "https://registry.npmjs.org/fill-range/-/fill-range-7.0.1.tgz", @@ -7132,6 +8302,22 @@ "integrity": "sha512-b6suED+5/3rTpUBdG1gupIl8MPFCAMA0QXwmljLhvCUKcUvdE4gWky9zpuGCcXHOsz4J9wPGNWq6OKpmIzz3hQ==", "dev": true }, + "flat-cache": { + "version": "3.0.4", + "resolved": "https://registry.npmjs.org/flat-cache/-/flat-cache-3.0.4.tgz", + "integrity": "sha512-dm9s5Pw7Jc0GvMYbshN6zchCA9RgQlzzEZX3vylR9IqFfS8XciblUXOKfW6SiuJ0e13eDYZoZV5wdrev7P3Nwg==", + "dev": true, + "requires": { + "flatted": "^3.1.0", + "rimraf": "^3.0.2" + } + }, + "flatted": { + "version": "3.2.2", + "resolved": "https://registry.npmjs.org/flatted/-/flatted-3.2.2.tgz", + "integrity": "sha512-JaTY/wtrcSyvXJl4IMFHPKyFur1sE9AUqc0QnhOaJ0CxHtAoIV8pYDzeEfAaNEtGkOfq4gr3LBFmdXW5mOQFnA==", + "dev": true + }, "foreground-child": { "version": "2.0.0", "resolved": "https://registry.npmjs.org/foreground-child/-/foreground-child-2.0.0.tgz", @@ -7178,6 +8364,12 @@ "integrity": "sha512-yIovAzMX49sF8Yl58fSCWJ5svSLuaibPxXQJFLmBObTuCr0Mf1KiPopGM9NiFjiYBCbfaa2Fh6breQ6ANVTI0A==", "dev": true }, + "functional-red-black-tree": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/functional-red-black-tree/-/functional-red-black-tree-1.0.1.tgz", + "integrity": "sha1-GwqzvVU7Kg1jmdKcDj6gslIHgyc=", + "dev": true + }, "generic-pool": { "version": "3.8.2", "resolved": "https://registry.npmjs.org/generic-pool/-/generic-pool-3.8.2.tgz", @@ -7252,12 +8444,12 @@ } }, "glob-parent": { - "version": "5.1.2", - "resolved": "https://registry.npmjs.org/glob-parent/-/glob-parent-5.1.2.tgz", - "integrity": "sha512-AOIgSQCepiJYwP3ARnGx+5VnTu2HBYdzbGP45eLw1vr3zB3vZLeyed1sC9hnbcOc9/SrMyM5RPQrkGz4aS9Zow==", + "version": "6.0.2", + "resolved": "https://registry.npmjs.org/glob-parent/-/glob-parent-6.0.2.tgz", + "integrity": "sha512-XxwI8EOhVQgWp6iDL+3b0r86f4d6AX6zSU55HfB4ydCEuXLXc5FcYeOu+nnGftS4TEju/11rt4KJPTMgbfmv4A==", "dev": true, "requires": { - "is-glob": "^4.0.1" + "is-glob": "^4.0.3" } }, "global-dirs": { @@ -7270,10 +8462,13 @@ } }, "globals": { - "version": "11.12.0", - "resolved": "https://registry.npmjs.org/globals/-/globals-11.12.0.tgz", - "integrity": "sha512-WOBp/EEGUiIsJSp7wcv/y6MO+lV9UoncWqxuFfm8eBwzWNgyfBd6Gz+IeKQ9jCmyhoH99g15M3T+QaVHFjizVA==", - "dev": true + "version": "13.11.0", + "resolved": "https://registry.npmjs.org/globals/-/globals-13.11.0.tgz", + "integrity": "sha512-08/xrJ7wQjK9kkkRoI3OFUBbLx4f+6x3SGwcPvQ0QH6goFDrOU2oyAWrmh3dJezu65buo+HBMzAMQy6rovVC3g==", + "dev": true, + "requires": { + "type-fest": "^0.20.2" + } }, "globby": { "version": "11.0.4", @@ -7376,6 +8571,14 @@ "requires": { "is-stream": "^2.0.0", "type-fest": "^0.8.0" + }, + "dependencies": { + "type-fest": { + "version": "0.8.1", + "resolved": "https://registry.npmjs.org/type-fest/-/type-fest-0.8.1.tgz", + "integrity": "sha512-4dbzIzqvjtgiM5rw1k5rEHtBANKmdudhGyBEajN01fEyhaAIhsoKNy6y7+IN93IfpFtwY9iqi7kD+xwKhQsNJA==", + "dev": true + } } }, "he": { @@ -7450,14 +8653,6 @@ "requires": { "parent-module": "^1.0.0", "resolve-from": "^4.0.0" - }, - "dependencies": { - "resolve-from": { - "version": "4.0.0", - "resolved": "https://registry.npmjs.org/resolve-from/-/resolve-from-4.0.0.tgz", - "integrity": "sha512-pb/MYmXstAkysRFx8piNI1tGFNQIFA3vkE3Gq4EuA1dF6gHp/+vgZqsCGJapvy8N3Q+4o7FwvquPJcnZ7RYy4g==", - "dev": true - } } }, "import-from": { @@ -7467,6 +8662,14 @@ "dev": true, "requires": { "resolve-from": "^5.0.0" + }, + "dependencies": { + "resolve-from": { + "version": "5.0.0", + "resolved": "https://registry.npmjs.org/resolve-from/-/resolve-from-5.0.0.tgz", + "integrity": "sha512-qYg9KP24dD5qka9J47d0aVky0N+b4fTU89LN9iDnjB5waksiC49rvMB0PrUJQGoTmH50XPiqOvAjDfaijGxYZw==", + "dev": true + } } }, "import-lazy": { @@ -7562,9 +8765,9 @@ } }, "is-core-module": { - "version": "2.7.0", - "resolved": "https://registry.npmjs.org/is-core-module/-/is-core-module-2.7.0.tgz", - "integrity": "sha512-ByY+tjCciCr+9nLryBYcSD50EOGWt95c7tIsKTG1J2ixKKXPvF7Ej3AVd+UfDydAJom3biBGDBALaO79ktwgEQ==", + "version": "2.8.0", + "resolved": "https://registry.npmjs.org/is-core-module/-/is-core-module-2.8.0.tgz", + "integrity": "sha512-vd15qHsaqrRL7dtH6QNuy0ndJmRDrS9HAM1CAiSifNUFv4x1a0CCVsj18hJ1mShxIG6T2i1sO78MkP56r0nYRw==", "dev": true, "requires": { "has": "^1.0.3" @@ -7710,9 +8913,9 @@ "dev": true }, "istanbul-lib-coverage": { - "version": "3.0.0", - "resolved": "https://registry.npmjs.org/istanbul-lib-coverage/-/istanbul-lib-coverage-3.0.0.tgz", - "integrity": "sha512-UiUIqxMgRDET6eR+o5HbfRYP1l0hqkWOs7vNxC/mggutCMUIhWMm8gAHb8tHlyfD3/l6rlgNA5cKdDzEAf6hEg==", + "version": "3.2.0", + "resolved": "https://registry.npmjs.org/istanbul-lib-coverage/-/istanbul-lib-coverage-3.2.0.tgz", + "integrity": "sha512-eOeJ5BHCmHYvQK7xt9GkdHuzuCGS1Y6g9Gvnx3Ym33fz/HpLRYxiS0wHNr+m/MBC8B647Xt608vCDEvhl9c6Mw==", "dev": true }, "istanbul-lib-hook": { @@ -7734,6 +8937,14 @@ "@istanbuljs/schema": "^0.1.2", "istanbul-lib-coverage": "^3.0.0", "semver": "^6.3.0" + }, + "dependencies": { + "semver": { + "version": "6.3.0", + "resolved": "https://registry.npmjs.org/semver/-/semver-6.3.0.tgz", + "integrity": "sha512-b39TBaTSfV6yBrapU89p5fKekE2m/NwnDocOVruQFS1/veMgdzuPcnOM34M6CwxW8jH/lxEa5rBoDeUwu5HHTw==", + "dev": true + } } }, "istanbul-lib-processinfo": { @@ -7760,23 +8971,12 @@ "istanbul-lib-coverage": "^3.0.0", "make-dir": "^3.0.0", "supports-color": "^7.1.0" - }, - "dependencies": { - "supports-color": { - "version": "7.2.0", - "resolved": "https://registry.npmjs.org/supports-color/-/supports-color-7.2.0.tgz", - "integrity": "sha512-qpCAvRl9stuOHveKsn7HncJRvv501qIacKzQlO/+Lwxc9+0q2wLyv4Dfvt80/DPn2pqOBsJdDiogXGR9+OvwRw==", - "dev": true, - "requires": { - "has-flag": "^4.0.0" - } - } } }, "istanbul-lib-source-maps": { - "version": "4.0.0", - "resolved": "https://registry.npmjs.org/istanbul-lib-source-maps/-/istanbul-lib-source-maps-4.0.0.tgz", - "integrity": "sha512-c16LpFRkR8vQXyHZ5nLpY35JZtzj1PQY1iZmesUbf1FZHbIupcWfjgOXBY9YHkLEQ6puz1u4Dgj6qmU/DisrZg==", + "version": "4.0.1", + "resolved": "https://registry.npmjs.org/istanbul-lib-source-maps/-/istanbul-lib-source-maps-4.0.1.tgz", + "integrity": "sha512-n3s8EwkdFIJCG3BPKBYvskgXGoy88ARzvegkitk60NxRdwltLOTaH7CUiMRXvwYorl0Q712iEjcWB+fK/MrWVw==", "dev": true, "requires": { "debug": "^4.1.1", @@ -7793,9 +8993,9 @@ } }, "istanbul-reports": { - "version": "3.0.3", - "resolved": "https://registry.npmjs.org/istanbul-reports/-/istanbul-reports-3.0.3.tgz", - "integrity": "sha512-0i77ZFLsb9U3DHi22WzmIngVzfoyxxbQcZRqlF3KoKmCJGq9nhFHoGi8FqBztN2rE8w6hURnZghetn0xpkVb6A==", + "version": "3.0.5", + "resolved": "https://registry.npmjs.org/istanbul-reports/-/istanbul-reports-3.0.5.tgz", + "integrity": "sha512-5+19PlhnGabNWB7kOFnuxT8H3T/iIyQzIbQMxXsURmmvKg86P2sbkrGOT77VnHw0Qr0gc2XzRaRfMZYYbSQCJQ==", "dev": true, "requires": { "html-escaper": "^2.0.0", @@ -7835,6 +9035,18 @@ "integrity": "sha512-xyFwyhro/JEof6Ghe2iz2NcXoj2sloNsWr/XsERDK/oiPCfaNhl5ONfp+jQdAZRQQ0IJWNzH9zIZF7li91kh2w==", "dev": true }, + "json-schema-traverse": { + "version": "0.4.1", + "resolved": "https://registry.npmjs.org/json-schema-traverse/-/json-schema-traverse-0.4.1.tgz", + "integrity": "sha512-xbbCH5dCYU5T8LcEhhuh7HJ88HXuW3qsI3Y0zOZFKfZEHcpWiHU/Jxzk629Brsab/mMiHQti9wMP+845RPe3Vg==", + "dev": true + }, + "json-stable-stringify-without-jsonify": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/json-stable-stringify-without-jsonify/-/json-stable-stringify-without-jsonify-1.0.1.tgz", + "integrity": "sha1-nbe1lJatPzz+8wp1FC0tkwrXJlE=", + "dev": true + }, "json5": { "version": "2.2.0", "resolved": "https://registry.npmjs.org/json5/-/json5-2.2.0.tgz", @@ -7874,6 +9086,16 @@ "package-json": "^6.3.0" } }, + "levn": { + "version": "0.4.1", + "resolved": "https://registry.npmjs.org/levn/-/levn-0.4.1.tgz", + "integrity": "sha512-+bT2uH4E5LGE7h/n3evcS/sQlJXCpIp6ym8OWJ5eV6+67Dsql/LaaT7qJBAt2rzfoa/5QBGBhxDix1dMt2kQKQ==", + "dev": true, + "requires": { + "prelude-ls": "^1.2.1", + "type-check": "~0.4.0" + } + }, "lines-and-columns": { "version": "1.1.6", "resolved": "https://registry.npmjs.org/lines-and-columns/-/lines-and-columns-1.1.6.tgz", @@ -7907,6 +9129,12 @@ "integrity": "sha1-LRd/ZS+jHpObRDjVNBSZ36OCXpk=", "dev": true }, + "lodash.merge": { + "version": "4.6.2", + "resolved": "https://registry.npmjs.org/lodash.merge/-/lodash.merge-4.6.2.tgz", + "integrity": "sha512-0KpjqXRVvrYyCsX1swR/XTK0va6VQkQM6MNo7PqW77ByjAhoARA8EfrP1N4+KlKj8YS0ZUCtRT/YUuhyYDujIQ==", + "dev": true + }, "log-symbols": { "version": "4.1.0", "resolved": "https://registry.npmjs.org/log-symbols/-/log-symbols-4.1.0.tgz", @@ -7924,20 +9152,12 @@ "dev": true }, "lru-cache": { - "version": "5.1.1", - "resolved": "https://registry.npmjs.org/lru-cache/-/lru-cache-5.1.1.tgz", - "integrity": "sha512-KpNARQA3Iwv+jTA0utUVVbrh+Jlrr1Fv0e56GGzAFOXN7dk/FviaDW8LHmK52DlcH4WP2n6gI8vN1aesBFgo9w==", + "version": "6.0.0", + "resolved": "https://registry.npmjs.org/lru-cache/-/lru-cache-6.0.0.tgz", + "integrity": "sha512-Jo6dJ04CmSjuznwJSS3pUeWmd/H0ffTlkXXgwZi+eq1UCmqQwCh+eLsYOYCwY991i2Fah4h1BEMCx4qThGbsiA==", "dev": true, "requires": { - "yallist": "^3.0.2" - }, - "dependencies": { - "yallist": { - "version": "3.1.1", - "resolved": "https://registry.npmjs.org/yallist/-/yallist-3.1.1.tgz", - "integrity": "sha512-a4UGQaWPH59mOXUYnAG2ewncQS4i4F43Tv3JoAM+s2VDAmS9NsK8GpDMLrCHPksFT7h3K6TOoUNn2pb7RoXx4g==", - "dev": true - } + "yallist": "^4.0.0" } }, "lunr": { @@ -7959,6 +9179,14 @@ "dev": true, "requires": { "semver": "^6.0.0" + }, + "dependencies": { + "semver": { + "version": "6.3.0", + "resolved": "https://registry.npmjs.org/semver/-/semver-6.3.0.tgz", + "integrity": "sha512-b39TBaTSfV6yBrapU89p5fKekE2m/NwnDocOVruQFS1/veMgdzuPcnOM34M6CwxW8jH/lxEa5rBoDeUwu5HHTw==", + "dev": true + } } }, "make-error": { @@ -8038,9 +9266,9 @@ "dev": true }, "mocha": { - "version": "9.1.2", - "resolved": "https://registry.npmjs.org/mocha/-/mocha-9.1.2.tgz", - "integrity": "sha512-ta3LtJ+63RIBP03VBjMGtSqbe6cWXRejF9SyM9Zyli1CKZJZ+vfCTj3oW24V7wAphMJdpOFLoMI3hjJ1LWbs0w==", + "version": "9.1.3", + "resolved": "https://registry.npmjs.org/mocha/-/mocha-9.1.3.tgz", + "integrity": "sha512-Xcpl9FqXOAYqI3j79pEtHBBnQgVXIhpULjGQa7DVb0Po+VzmSIK9kanAiWLHoRR/dbZ2qpdPshuXr8l1VaHCzw==", "dev": true, "requires": { "@ungap/promise-all-settled": "1.1.2", @@ -8067,12 +9295,29 @@ "yargs": "16.2.0", "yargs-parser": "20.2.4", "yargs-unparser": "2.0.0" + }, + "dependencies": { + "ms": { + "version": "2.1.3", + "resolved": "https://registry.npmjs.org/ms/-/ms-2.1.3.tgz", + "integrity": "sha512-6FlzubTLZG3J2a/NVCAleEhjzq5oxgHyaCU9yYXvcLsvoVaHJq/s5xXI6/XXP6tz7R9xAOtHnSO/tXtF3WRTlA==", + "dev": true + }, + "supports-color": { + "version": "8.1.1", + "resolved": "https://registry.npmjs.org/supports-color/-/supports-color-8.1.1.tgz", + "integrity": "sha512-MpUEN2OodtUzxvKQl72cUF7RQ5EiHsGvSsVG0ia9c5RbWGL2CI4C7EpPS8UTBIplnlzZiNuV56w+FuNxy3ty2Q==", + "dev": true, + "requires": { + "has-flag": "^4.0.0" + } + } } }, "ms": { - "version": "2.1.3", - "resolved": "https://registry.npmjs.org/ms/-/ms-2.1.3.tgz", - "integrity": "sha512-6FlzubTLZG3J2a/NVCAleEhjzq5oxgHyaCU9yYXvcLsvoVaHJq/s5xXI6/XXP6tz7R9xAOtHnSO/tXtF3WRTlA==", + "version": "2.1.2", + "resolved": "https://registry.npmjs.org/ms/-/ms-2.1.2.tgz", + "integrity": "sha512-sGkPx+VjMtmA6MX27oA4FBFELFCZZ4S4XqeGOXCv68tT+jb3vk/RyaKWP0PTKyWtmLSM0b+adUTEvbs1PEaH2w==", "dev": true }, "mute-stream": { @@ -8087,6 +9332,12 @@ "integrity": "sha512-rdwtIXaXCLFAQbnfqDRnI6jaRHp9fTcYBjtFKE8eezcZ7LuLjhUaQGNeMXf1HmRoCH32CLz6XwX0TtxEOS/A3Q==", "dev": true }, + "natural-compare": { + "version": "1.4.0", + "resolved": "https://registry.npmjs.org/natural-compare/-/natural-compare-1.4.0.tgz", + "integrity": "sha1-Sr6/7tdUHywnrPspvbvRXI1bpPc=", + "dev": true + }, "neo-async": { "version": "2.6.2", "resolved": "https://registry.npmjs.org/neo-async/-/neo-async-2.6.2.tgz", @@ -8142,9 +9393,9 @@ } }, "node-releases": { - "version": "1.1.77", - "resolved": "https://registry.npmjs.org/node-releases/-/node-releases-1.1.77.tgz", - "integrity": "sha512-rB1DUFUNAN4Gn9keO2K1efO35IDK7yKHCdCaIMvFO7yUYmmZYeDjnGKle26G4rwj+LKRQpjyUUvMkPglwGCYNQ==", + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/node-releases/-/node-releases-2.0.0.tgz", + "integrity": "sha512-aA87l0flFYMzCHpTM3DERFSYxc6lv/BltdbRTOMZuxZ0cwZCD3mejE5n9vLhSJCN++/eOqr77G1IO5uXxlQYWA==", "dev": true }, "normalize-path": { @@ -8251,6 +9502,12 @@ "p-limit": "^2.2.0" } }, + "resolve-from": { + "version": "5.0.0", + "resolved": "https://registry.npmjs.org/resolve-from/-/resolve-from-5.0.0.tgz", + "integrity": "sha512-qYg9KP24dD5qka9J47d0aVky0N+b4fTU89LN9iDnjB5waksiC49rvMB0PrUJQGoTmH50XPiqOvAjDfaijGxYZw==", + "dev": true + }, "wrap-ansi": { "version": "6.2.0", "resolved": "https://registry.npmjs.org/wrap-ansi/-/wrap-ansi-6.2.0.tgz", @@ -8330,6 +9587,23 @@ "dev": true, "requires": { "lru-cache": "^5.1.1" + }, + "dependencies": { + "lru-cache": { + "version": "5.1.1", + "resolved": "https://registry.npmjs.org/lru-cache/-/lru-cache-5.1.1.tgz", + "integrity": "sha512-KpNARQA3Iwv+jTA0utUVVbrh+Jlrr1Fv0e56GGzAFOXN7dk/FviaDW8LHmK52DlcH4WP2n6gI8vN1aesBFgo9w==", + "dev": true, + "requires": { + "yallist": "^3.0.2" + } + }, + "yallist": { + "version": "3.1.1", + "resolved": "https://registry.npmjs.org/yallist/-/yallist-3.1.1.tgz", + "integrity": "sha512-a4UGQaWPH59mOXUYnAG2ewncQS4i4F43Tv3JoAM+s2VDAmS9NsK8GpDMLrCHPksFT7h3K6TOoUNn2pb7RoXx4g==", + "dev": true + } } }, "open": { @@ -8342,6 +9616,20 @@ "is-wsl": "^2.1.1" } }, + "optionator": { + "version": "0.9.1", + "resolved": "https://registry.npmjs.org/optionator/-/optionator-0.9.1.tgz", + "integrity": "sha512-74RlY5FCnhq4jRxVUPKDaRwrVNXMqsGsiW6AJw4XK8hmtm10wC0ypZBLw5IIp85NZMr91+qd1RvvENwg7jjRFw==", + "dev": true, + "requires": { + "deep-is": "^0.1.3", + "fast-levenshtein": "^2.0.6", + "levn": "^0.4.1", + "prelude-ls": "^1.2.1", + "type-check": "^0.4.0", + "word-wrap": "^1.2.3" + } + }, "ora": { "version": "5.4.1", "resolved": "https://registry.npmjs.org/ora/-/ora-5.4.1.tgz", @@ -8573,6 +9861,12 @@ "dev": true } } + }, + "semver": { + "version": "6.3.0", + "resolved": "https://registry.npmjs.org/semver/-/semver-6.3.0.tgz", + "integrity": "sha512-b39TBaTSfV6yBrapU89p5fKekE2m/NwnDocOVruQFS1/veMgdzuPcnOM34M6CwxW8jH/lxEa5rBoDeUwu5HHTw==", + "dev": true } } }, @@ -8661,9 +9955,9 @@ "dev": true }, "picocolors": { - "version": "0.2.1", - "resolved": "https://registry.npmjs.org/picocolors/-/picocolors-0.2.1.tgz", - "integrity": "sha512-cMlDqaLEqfSaW8Z7N5Jw+lyIW869EzT73/F5lhtY9cLGoVxSXznfgfXMO0Z5K0o0Q2TkTXq+0KFsdnSe3jDViA==", + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/picocolors/-/picocolors-1.0.0.tgz", + "integrity": "sha512-1fygroTLlHu66zi26VoTDv8yRgm0Fccecssto+MhsZ0D/DGW2sm8E8AjW7NU5VVTRt5GxbeZ5qBuJr+HyLYkjQ==", "dev": true }, "picomatch": { @@ -8720,6 +10014,12 @@ } } }, + "prelude-ls": { + "version": "1.2.1", + "resolved": "https://registry.npmjs.org/prelude-ls/-/prelude-ls-1.2.1.tgz", + "integrity": "sha512-vkcDPrRZo1QZLbn5RLGPpg/WmIQ65qoWWhcGKf/b5eplkkarX0m9z8ppCat4mlOqUsWpyNuYgO3VRyrYHSzX5g==", + "dev": true + }, "prepend-http": { "version": "2.0.0", "resolved": "https://registry.npmjs.org/prepend-http/-/prepend-http-2.0.0.tgz", @@ -8735,6 +10035,12 @@ "fromentries": "^1.2.0" } }, + "progress": { + "version": "2.0.3", + "resolved": "https://registry.npmjs.org/progress/-/progress-2.0.3.tgz", + "integrity": "sha512-7PiHtLll5LdnKIMw100I+8xJXR5gW2QwWYkT6iJva0bXitZKa/XMrSbdmg3r2Xnaidz9Qumd0VPaMrZlF9V9sA==", + "dev": true + }, "protocols": { "version": "1.4.8", "resolved": "https://registry.npmjs.org/protocols/-/protocols-1.4.8.tgz", @@ -8751,6 +10057,12 @@ "once": "^1.3.1" } }, + "punycode": { + "version": "2.1.1", + "resolved": "https://registry.npmjs.org/punycode/-/punycode-2.1.1.tgz", + "integrity": "sha512-XRsRjdf+j5ml+y/6GKHPZbrF/8p2Yga0JPtdqTIY2Xe5ohJPD9saDJJLPvp9+NSBprVvevdXZybnj2cv8OEd0A==", + "dev": true + }, "pupa": { "version": "2.1.1", "resolved": "https://registry.npmjs.org/pupa/-/pupa-2.1.1.tgz", @@ -8870,6 +10182,12 @@ "redis-errors": "^1.0.0" } }, + "regexpp": { + "version": "3.2.0", + "resolved": "https://registry.npmjs.org/regexpp/-/regexpp-3.2.0.tgz", + "integrity": "sha512-pq2bWo9mVD43nbts2wGv17XLiNLya+GklZ8kaDLV2Z08gDCsGpnKn9BFMepvWuHCbyVvY7J5o5+BVvoQbmlJLg==", + "dev": true + }, "registry-auth-token": { "version": "4.2.1", "resolved": "https://registry.npmjs.org/registry-auth-token/-/registry-auth-token-4.2.1.tgz", @@ -8925,24 +10243,6 @@ "yargs-parser": "20.2.9" }, "dependencies": { - "lru-cache": { - "version": "6.0.0", - "resolved": "https://registry.npmjs.org/lru-cache/-/lru-cache-6.0.0.tgz", - "integrity": "sha512-Jo6dJ04CmSjuznwJSS3pUeWmd/H0ffTlkXXgwZi+eq1UCmqQwCh+eLsYOYCwY991i2Fah4h1BEMCx4qThGbsiA==", - "dev": true, - "requires": { - "yallist": "^4.0.0" - } - }, - "semver": { - "version": "7.3.5", - "resolved": "https://registry.npmjs.org/semver/-/semver-7.3.5.tgz", - "integrity": "sha512-PoeGJYh8HK4BTO/a9Tf6ZG3veo/A7ZVsYrSA6J8ny9nb3B1VrpkuN+z9OE5wfE5p6H4LchYZsegiQgbJD94ZFQ==", - "dev": true, - "requires": { - "lru-cache": "^6.0.0" - } - }, "uuid": { "version": "8.3.2", "resolved": "https://registry.npmjs.org/uuid/-/uuid-8.3.2.tgz", @@ -8995,9 +10295,9 @@ "dev": true }, "resolve-from": { - "version": "5.0.0", - "resolved": "https://registry.npmjs.org/resolve-from/-/resolve-from-5.0.0.tgz", - "integrity": "sha512-qYg9KP24dD5qka9J47d0aVky0N+b4fTU89LN9iDnjB5waksiC49rvMB0PrUJQGoTmH50XPiqOvAjDfaijGxYZw==", + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/resolve-from/-/resolve-from-4.0.0.tgz", + "integrity": "sha512-pb/MYmXstAkysRFx8piNI1tGFNQIFA3vkE3Gq4EuA1dF6gHp/+vgZqsCGJapvy8N3Q+4o7FwvquPJcnZ7RYy4g==", "dev": true }, "responselike": { @@ -9077,10 +10377,13 @@ "dev": true }, "semver": { - "version": "6.3.0", - "resolved": "https://registry.npmjs.org/semver/-/semver-6.3.0.tgz", - "integrity": "sha512-b39TBaTSfV6yBrapU89p5fKekE2m/NwnDocOVruQFS1/veMgdzuPcnOM34M6CwxW8jH/lxEa5rBoDeUwu5HHTw==", - "dev": true + "version": "7.3.5", + "resolved": "https://registry.npmjs.org/semver/-/semver-7.3.5.tgz", + "integrity": "sha512-PoeGJYh8HK4BTO/a9Tf6ZG3veo/A7ZVsYrSA6J8ny9nb3B1VrpkuN+z9OE5wfE5p6H4LchYZsegiQgbJD94ZFQ==", + "dev": true, + "requires": { + "lru-cache": "^6.0.0" + } }, "semver-diff": { "version": "3.1.1", @@ -9089,6 +10392,14 @@ "dev": true, "requires": { "semver": "^6.3.0" + }, + "dependencies": { + "semver": { + "version": "6.3.0", + "resolved": "https://registry.npmjs.org/semver/-/semver-6.3.0.tgz", + "integrity": "sha512-b39TBaTSfV6yBrapU89p5fKekE2m/NwnDocOVruQFS1/veMgdzuPcnOM34M6CwxW8jH/lxEa5rBoDeUwu5HHTw==", + "dev": true + } } }, "serialize-javascript": { @@ -9133,9 +10444,9 @@ } }, "shiki": { - "version": "0.9.11", - "resolved": "https://registry.npmjs.org/shiki/-/shiki-0.9.11.tgz", - "integrity": "sha512-tjruNTLFhU0hruCPoJP0y+B9LKOmcqUhTpxn7pcJB3fa+04gFChuEmxmrUfOJ7ZO6Jd+HwMnDHgY3lv3Tqonuw==", + "version": "0.9.12", + "resolved": "https://registry.npmjs.org/shiki/-/shiki-0.9.12.tgz", + "integrity": "sha512-VXcROdldv0/Qu0w2XvzU4IrvTeBNs/Kj/FCmtcEXGz7Tic/veQzliJj6tEiAgoKianhQstpYmbPDStHU5Opqcw==", "dev": true, "requires": { "jsonc-parser": "^3.0.0", @@ -9172,17 +10483,6 @@ "diff": "^5.0.0", "nise": "^5.1.0", "supports-color": "^7.2.0" - }, - "dependencies": { - "supports-color": { - "version": "7.2.0", - "resolved": "https://registry.npmjs.org/supports-color/-/supports-color-7.2.0.tgz", - "integrity": "sha512-qpCAvRl9stuOHveKsn7HncJRvv501qIacKzQlO/+Lwxc9+0q2wLyv4Dfvt80/DPn2pqOBsJdDiogXGR9+OvwRw==", - "dev": true, - "requires": { - "has-flag": "^4.0.0" - } - } } }, "slash": { @@ -9303,9 +10603,9 @@ "dev": true }, "supports-color": { - "version": "8.1.1", - "resolved": "https://registry.npmjs.org/supports-color/-/supports-color-8.1.1.tgz", - "integrity": "sha512-MpUEN2OodtUzxvKQl72cUF7RQ5EiHsGvSsVG0ia9c5RbWGL2CI4C7EpPS8UTBIplnlzZiNuV56w+FuNxy3ty2Q==", + "version": "7.2.0", + "resolved": "https://registry.npmjs.org/supports-color/-/supports-color-7.2.0.tgz", + "integrity": "sha512-qpCAvRl9stuOHveKsn7HncJRvv501qIacKzQlO/+Lwxc9+0q2wLyv4Dfvt80/DPn2pqOBsJdDiogXGR9+OvwRw==", "dev": true, "requires": { "has-flag": "^4.0.0" @@ -9322,6 +10622,12 @@ "minimatch": "^3.0.4" } }, + "text-table": { + "version": "0.2.0", + "resolved": "https://registry.npmjs.org/text-table/-/text-table-0.2.0.tgz", + "integrity": "sha1-f17oI66AUgfACvLfSoTsP8+lcLQ=", + "dev": true + }, "through": { "version": "2.3.8", "resolved": "https://registry.npmjs.org/through/-/through-2.3.8.tgz", @@ -9398,6 +10704,32 @@ "integrity": "sha512-hcVC3wYEziELGGmEEXue7D75zbwIIVUMWAVbHItGPx0ziyXxrOMQx4rQEVEV45Ut/1IotuEvwqPopzIOkDMf0A==", "dev": true }, + "tsutils": { + "version": "3.21.0", + "resolved": "https://registry.npmjs.org/tsutils/-/tsutils-3.21.0.tgz", + "integrity": "sha512-mHKK3iUXL+3UF6xL5k0PEhKRUBKPBCv/+RkEOpjRWxxx27KKRBmmA60A9pgOUvMi8GKhRMPEmjBRPzs2W7O1OA==", + "dev": true, + "requires": { + "tslib": "^1.8.1" + }, + "dependencies": { + "tslib": { + "version": "1.14.1", + "resolved": "https://registry.npmjs.org/tslib/-/tslib-1.14.1.tgz", + "integrity": "sha512-Xni35NKzjgMrwevysHTCArtLDpPvye8zV/0E4EyYn43P7/7qvQwPh9BGkHewbMulVntbigmcT7rdX3BNo9wRJg==", + "dev": true + } + } + }, + "type-check": { + "version": "0.4.0", + "resolved": "https://registry.npmjs.org/type-check/-/type-check-0.4.0.tgz", + "integrity": "sha512-XleUoc9uwGXqjWwXaUTZAmzMcFZ5858QA2vvx1Ur5xIcixXIP+8LnFDgRplU30us6teqdlskFfu+ae4K79Ooew==", + "dev": true, + "requires": { + "prelude-ls": "^1.2.1" + } + }, "type-detect": { "version": "4.0.8", "resolved": "https://registry.npmjs.org/type-detect/-/type-detect-4.0.8.tgz", @@ -9405,9 +10737,9 @@ "dev": true }, "type-fest": { - "version": "0.8.1", - "resolved": "https://registry.npmjs.org/type-fest/-/type-fest-0.8.1.tgz", - "integrity": "sha512-4dbzIzqvjtgiM5rw1k5rEHtBANKmdudhGyBEajN01fEyhaAIhsoKNy6y7+IN93IfpFtwY9iqi7kD+xwKhQsNJA==", + "version": "0.20.2", + "resolved": "https://registry.npmjs.org/type-fest/-/type-fest-0.20.2.tgz", + "integrity": "sha512-Ne+eE4r0/iWnpAxD852z3A+N0Bt5RN//NjJwRd2VFHEmrywxf5vsZlh4R6lixl6B+wz/8d+maTSAkN1FIkI3LQ==", "dev": true }, "typedarray-to-buffer": { @@ -9420,9 +10752,9 @@ } }, "typedoc": { - "version": "0.22.5", - "resolved": "https://registry.npmjs.org/typedoc/-/typedoc-0.22.5.tgz", - "integrity": "sha512-KFrWGU1iKiTGw0RcyjLNYDmhd7uICU14HgBNPmFKY/sT4Pm/fraaLyWyisst9vGTUAKxqibqoDITR7+ZcAkhHg==", + "version": "0.22.6", + "resolved": "https://registry.npmjs.org/typedoc/-/typedoc-0.22.6.tgz", + "integrity": "sha512-ePbJqOaz0GNkU2ehRwFwBpLD4Gp6m7jbJfHysXmDdjVKc1g8DFJ83r/LOZ9TZrkC661vgpoIY3FjSPEtUilHNA==", "dev": true, "requires": { "glob": "^7.2.0", @@ -9465,9 +10797,9 @@ } }, "typescript": { - "version": "4.4.3", - "resolved": "https://registry.npmjs.org/typescript/-/typescript-4.4.3.tgz", - "integrity": "sha512-4xfscpisVgqqDfPaJo5vkd+Qd/ItkoagnHpufr+i2QCHBsNYp+G7UAoyFl8aPtx879u38wPV65rZ8qbGZijalA==", + "version": "4.4.4", + "resolved": "https://registry.npmjs.org/typescript/-/typescript-4.4.4.tgz", + "integrity": "sha512-DqGhF5IKoBl8WNf8C1gu8q0xZSInh9j1kJJMqT3a94w1JzVaBU4EXOSMrz9yDqMT0xt3selp83fuFMQ0uzv6qA==", "dev": true }, "uglify-js": { @@ -9528,27 +10860,18 @@ "requires": { "ci-info": "^2.0.0" } - }, - "lru-cache": { - "version": "6.0.0", - "resolved": "https://registry.npmjs.org/lru-cache/-/lru-cache-6.0.0.tgz", - "integrity": "sha512-Jo6dJ04CmSjuznwJSS3pUeWmd/H0ffTlkXXgwZi+eq1UCmqQwCh+eLsYOYCwY991i2Fah4h1BEMCx4qThGbsiA==", - "dev": true, - "requires": { - "yallist": "^4.0.0" - } - }, - "semver": { - "version": "7.3.5", - "resolved": "https://registry.npmjs.org/semver/-/semver-7.3.5.tgz", - "integrity": "sha512-PoeGJYh8HK4BTO/a9Tf6ZG3veo/A7ZVsYrSA6J8ny9nb3B1VrpkuN+z9OE5wfE5p6H4LchYZsegiQgbJD94ZFQ==", - "dev": true, - "requires": { - "lru-cache": "^6.0.0" - } } } }, + "uri-js": { + "version": "4.4.1", + "resolved": "https://registry.npmjs.org/uri-js/-/uri-js-4.4.1.tgz", + "integrity": "sha512-7rKUyy33Q1yc98pQ1DAmLtwX109F7TIfWlW1Ydo8Wl1ii1SeHieeh0HHfPeL2fMXK6z0s8ecKs9frCuLJvndBg==", + "dev": true, + "requires": { + "punycode": "^2.1.0" + } + }, "url-join": { "version": "4.0.1", "resolved": "https://registry.npmjs.org/url-join/-/url-join-4.0.1.tgz", @@ -9576,6 +10899,12 @@ "integrity": "sha512-HjSDRw6gZE5JMggctHBcjVak08+KEVhSIiDzFnT9S9aegmp85S/bReBVTb4QTFaRNptJ9kuYaNhnbNEOkbKb/A==", "dev": true }, + "v8-compile-cache": { + "version": "2.3.0", + "resolved": "https://registry.npmjs.org/v8-compile-cache/-/v8-compile-cache-2.3.0.tgz", + "integrity": "sha512-l8lCEmLcLYZh4nbunNZvQCJc5pv7+RCwa8q/LdUx8u7lsWvPDKmpodJAJNwkAhJC//dFY48KuIEmjtd4RViDrA==", + "dev": true + }, "vscode-textmate": { "version": "5.2.0", "resolved": "https://registry.npmjs.org/vscode-textmate/-/vscode-textmate-5.2.0.tgz", @@ -9674,6 +11003,12 @@ } } }, + "word-wrap": { + "version": "1.2.3", + "resolved": "https://registry.npmjs.org/word-wrap/-/word-wrap-1.2.3.tgz", + "integrity": "sha512-Hz/mrNwitNRh/HUAtM/VT/5VH+ygD6DV7mYKZAtHOrbs8U7lvPS6xf7EJKMF0uW1KJCl0H701g3ZGus+muE5vQ==", + "dev": true + }, "wordwrap": { "version": "1.0.0", "resolved": "https://registry.npmjs.org/wordwrap/-/wordwrap-1.0.0.tgz", diff --git a/package.json b/package.json index 80bf7aa05a6..beddb1f86df 100644 --- a/package.json +++ b/package.json @@ -14,6 +14,7 @@ "scripts": { "test": "nyc -r text-summary -r html mocha -r source-map-support/register -r ts-node/register './lib/**/*.spec.ts'", "build": "tsc", + "lint": "eslint ./*.ts ./lib/**/*.ts", "documentation": "typedoc" }, "dependencies": { @@ -26,20 +27,23 @@ "@istanbuljs/nyc-config-typescript": "^1.0.1", "@tsconfig/node12": "^1.0.9", "@types/mocha": "^9.0.0", - "@types/node": "^16.10.3", + "@types/node": "^16.11.1", "@types/sinon": "^10.0.4", "@types/which": "^2.0.1", "@types/yallist": "^4.0.1", - "mocha": "^9.1.2", + "@typescript-eslint/eslint-plugin": "^5.1.0", + "@typescript-eslint/parser": "^5.1.0", + "eslint": "^8.0.1", + "mocha": "^9.1.3", "nyc": "^15.1.0", "release-it": "^14.11.6", "sinon": "^11.1.2", "source-map-support": "^0.5.20", "ts-node": "^10.3.0", - "typedoc": "^0.22.5", + "typedoc": "^0.22.6", "typedoc-github-wiki-theme": "^0.6.0", "typedoc-plugin-markdown": "^3.11.3", - "typescript": "^4.4.3", + "typescript": "^4.4.4", "which": "^2.0.2" }, "engines": { From 5be091f7a1c6725a560d75ca705212bce5fe806a Mon Sep 17 00:00:00 2001 From: leibale Date: Tue, 19 Oct 2021 16:29:42 -0400 Subject: [PATCH 0911/1748] fix tests with redis 6.0.x --- lib/client/index.spec.ts | 15 ++++++++++----- lib/commands/ACL_GETUSER.spec.ts | 4 ++-- lib/commands/HELLO.spec.ts | 2 ++ 3 files changed, 14 insertions(+), 7 deletions(-) diff --git a/lib/client/index.spec.ts b/lib/client/index.spec.ts index 3e1bbd6aad4..3e57ff3dcf8 100644 --- a/lib/client/index.spec.ts +++ b/lib/client/index.spec.ts @@ -108,13 +108,18 @@ describe('Client', () => { password: 'wrongpassword' }); + let message; + if (isRedisVersionGreaterThan([6, 2])) { + message = 'WRONGPASS invalid username-password pair or user is disabled.'; + } else if (isRedisVersionGreaterThan([6])) { + message = 'WRONGPASS invalid username-password pair'; + } else { + message = 'ERR invalid password'; + } + await assert.rejects( client.connect(), - { - message: isRedisVersionGreaterThan([6]) ? - 'WRONGPASS invalid username-password pair or user is disabled.' : - 'ERR invalid password' - } + { message } ); assert.equal(client.isOpen, false); diff --git a/lib/commands/ACL_GETUSER.spec.ts b/lib/commands/ACL_GETUSER.spec.ts index c43cdc364ae..6bd04033836 100644 --- a/lib/commands/ACL_GETUSER.spec.ts +++ b/lib/commands/ACL_GETUSER.spec.ts @@ -1,5 +1,5 @@ import { strict as assert } from 'assert'; -import { describeHandleMinimumRedisVersion, itWithClient, TestRedisServers } from '../test-utils'; +import { describeHandleMinimumRedisVersion, isRedisVersionGreaterThan, itWithClient, TestRedisServers } from '../test-utils'; import { transformArguments } from './ACL_GETUSER'; describe('ACL GETUSER', () => { @@ -20,7 +20,7 @@ describe('ACL GETUSER', () => { passwords: [], commands: '+@all', keys: ['*'], - channels: ['*'] + channels: isRedisVersionGreaterThan([6, 2]) ? ['*'] : undefined } ); }); diff --git a/lib/commands/HELLO.spec.ts b/lib/commands/HELLO.spec.ts index 7642f739d92..db4604afead 100644 --- a/lib/commands/HELLO.spec.ts +++ b/lib/commands/HELLO.spec.ts @@ -73,5 +73,7 @@ describe('HELLO', () => { modules: [] } ); + }, { + minimumRedisVersion: [6, 2] }); }); From 9f32ae728d803aff6e1bacfb198b069a863d3f52 Mon Sep 17 00:00:00 2001 From: leibale Date: Tue, 19 Oct 2021 16:32:49 -0400 Subject: [PATCH 0912/1748] fix ACL GETUSER test --- lib/commands/ACL_GETUSER.spec.ts | 8 ++++++-- 1 file changed, 6 insertions(+), 2 deletions(-) diff --git a/lib/commands/ACL_GETUSER.spec.ts b/lib/commands/ACL_GETUSER.spec.ts index 6bd04033836..35997610f5c 100644 --- a/lib/commands/ACL_GETUSER.spec.ts +++ b/lib/commands/ACL_GETUSER.spec.ts @@ -16,11 +16,15 @@ describe('ACL GETUSER', () => { assert.deepEqual( await client.aclGetUser('default'), { - flags: ['on', 'allkeys', 'allchannels', 'allcommands', 'nopass'], passwords: [], commands: '+@all', keys: ['*'], - channels: isRedisVersionGreaterThan([6, 2]) ? ['*'] : undefined + ...(isRedisVersionGreaterThan([6, 2]) ? { + flags: ['on', 'allkeys', 'allchannels', 'allcommands', 'nopass'], + channels: ['*'] + } : { + flags: ['on', 'allkeys', 'allcommands', 'nopass'] + }) } ); }); From dd85537c5875ec20342ce03b84dcf2629c70c2d5 Mon Sep 17 00:00:00 2001 From: leibale Date: Tue, 19 Oct 2021 17:14:31 -0400 Subject: [PATCH 0913/1748] fix client.quit and client.disconnect --- lib/client/index.spec.ts | 32 +++++++++++++++++++++++++++++--- lib/client/index.ts | 11 +++++++---- lib/client/socket.ts | 1 + lib/errors.ts | 6 ++++++ 4 files changed, 43 insertions(+), 7 deletions(-) diff --git a/lib/client/index.spec.ts b/lib/client/index.spec.ts index 3e57ff3dcf8..bdfebed5758 100644 --- a/lib/client/index.spec.ts +++ b/lib/client/index.spec.ts @@ -2,7 +2,7 @@ import { strict as assert, AssertionError } from 'assert'; import { once } from 'events'; import { itWithClient, TEST_REDIS_SERVERS, TestRedisServers, waitTillBeenCalled, isRedisVersionGreaterThan } from '../test-utils'; import RedisClient from '.'; -import { AbortError, ClientClosedError, ConnectionTimeoutError, WatchError } from '../errors'; +import { AbortError, ClientClosedError, ConnectionTimeoutError, DisconnectsClientError, WatchError } from '../errors'; import { defineScript } from '../lua-script'; import { spy } from 'sinon'; import { RedisNetSocketOptions } from '../client/socket'; @@ -636,10 +636,36 @@ describe('Client', () => { await client.connect(); try { - const quitPromise = client.quit(); + const pingPromise = client.ping(), + quitPromise = client.quit(); + assert.equal(client.isOpen, false); + + const [ping] = await Promise.all([ + pingPromise, + assert.doesNotReject(quitPromise), + assert.rejects(client.ping(), ClientClosedError) + ]); + + assert.equal(ping, 'PONG'); + } finally { + if (client.isOpen) { + await client.disconnect(); + } + } + }); + + it('client.disconnect', async () => { + const client = RedisClient.create(TEST_REDIS_SERVERS[TestRedisServers.OPEN]); + + await client.connect(); + + try { + const pingPromise = client.ping(), + disconnectPromise = client.disconnect(); assert.equal(client.isOpen, false); await Promise.all([ - quitPromise, + assert.rejects(pingPromise, DisconnectsClientError), + assert.doesNotReject(disconnectPromise), assert.rejects(client.ping(), ClientClosedError) ]); } finally { diff --git a/lib/client/index.ts b/lib/client/index.ts index 123c3f543f6..a76a56ace79 100644 --- a/lib/client/index.ts +++ b/lib/client/index.ts @@ -11,7 +11,7 @@ import { ScanCommandOptions } from '../commands/SCAN'; import { HScanTuple } from '../commands/HSCAN'; import { encodeCommand, extendWithCommands, extendWithModulesAndScripts, transformCommandArguments, transformCommandReply } from '../commander'; import { Pool, Options as PoolOptions, createPool } from 'generic-pool'; -import { ClientClosedError } from '../errors'; +import { ClientClosedError, DisconnectsClientError } from '../errors'; import { URL } from 'url'; export interface RedisClientOptions extends RedisPlugins { @@ -424,9 +424,12 @@ export default class RedisClient QUIT(): Promise { return this.#socket.quit(() => { - const promise = this.#queue.addCommand(['QUIT']); + const quitPromise = this.#queue.addCommand(['QUIT']); this.#tick(); - return promise; + return Promise.all([ + quitPromise, + this.#destroyIsolationPool() + ]); }); } @@ -519,7 +522,7 @@ export default class RedisClient } async disconnect(): Promise { - this.#queue.flushAll(new Error('Disconnecting')); + this.#queue.flushAll(new DisconnectsClientError()); await Promise.all([ this.#socket.disconnect(), this.#destroyIsolationPool() diff --git a/lib/client/socket.ts b/lib/client/socket.ts index d5309483979..923d14dffd5 100644 --- a/lib/client/socket.ts +++ b/lib/client/socket.ts @@ -222,6 +222,7 @@ export default class RedisSocket extends EventEmitter { } this.#socket.end(); + this.#socket.removeAllListeners('data'); await EventEmitter.once(this.#socket, 'end'); this.#socket = undefined; this.emit('end'); diff --git a/lib/errors.ts b/lib/errors.ts index 86a65587cf5..3f5fe40c201 100644 --- a/lib/errors.ts +++ b/lib/errors.ts @@ -21,3 +21,9 @@ export class ClientClosedError extends Error { super('The client is closed'); } } + +export class DisconnectsClientError extends Error { + constructor() { + super('Disconnects client'); + } +} From 6fc631781a97e5d3a53af580b24d4a38dff1e99b Mon Sep 17 00:00:00 2001 From: leibale Date: Tue, 19 Oct 2021 17:16:51 -0400 Subject: [PATCH 0914/1748] fix ACL GETUSER --- lib/commands/ACL_GETUSER.spec.ts | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/lib/commands/ACL_GETUSER.spec.ts b/lib/commands/ACL_GETUSER.spec.ts index 35997610f5c..e3446162da9 100644 --- a/lib/commands/ACL_GETUSER.spec.ts +++ b/lib/commands/ACL_GETUSER.spec.ts @@ -23,7 +23,8 @@ describe('ACL GETUSER', () => { flags: ['on', 'allkeys', 'allchannels', 'allcommands', 'nopass'], channels: ['*'] } : { - flags: ['on', 'allkeys', 'allcommands', 'nopass'] + flags: ['on', 'allkeys', 'allcommands', 'nopass'], + channels: undefined }) } ); From dec504851eb26d234bcd4237914c40e099ae2083 Mon Sep 17 00:00:00 2001 From: Simon Prickett Date: Thu, 21 Oct 2021 21:48:29 +0100 Subject: [PATCH 0915/1748] Adds TypeScript note and corrects a typo. --- examples/README.md | 10 +++++----- 1 file changed, 5 insertions(+), 5 deletions(-) diff --git a/examples/README.md b/examples/README.md index 25b87683330..ecb84a69f80 100644 --- a/examples/README.md +++ b/examples/README.md @@ -14,20 +14,20 @@ We'd love to see more examples here. If you have an idea that you'd like to see When adding a new example, please follow these guidelines: -* Add your code in a single JavaScript file per example, directly in the `examples` folder +* Add your code in a single JavaScript or TypeScript file per example, directly in the `examples` folder * Do not introduce other dependencies in your example -* Give your `.js` file a meaningful name using `-` separators e.g. `adding-to-a-stream.js` +* Give your `.js` file a meaningful name using `-` separators e.g. `adding-to-a-stream.js` / `adding-to-a-stream.ts` * Indent your code using 2 spaces * Use the single line `//` comment style and comment your code -* Add a comment at the top of your `.js` file describing what your example does -* Add a comment at the top of your `.js` file describing any Redis commands that need to be run to set up data for your example (try and keep this minimal) +* Add a comment at the top of your `.js` / `.ts` file describing what your example does +* Add a comment at the top of your `.js` / `.ts` file describing any Redis commands that need to be run to set up data for your example (try and keep this minimal) * Use semicolons * Use `async` and `await` * Use single quotes, `'hello'` not `"hello"` * Place your example code in a single `async` function where possible, named according to the file name e.g. `add-to-stream.js` would contain `const addtoStream = async () => { ... };`, and call this function at the end of the file e.g. `addToStream();` * Unless your example requires a connection string, assume Redis is on the default localhost port 6379 with no password * Use meaningful example data, let's not use `foo`, `bar`, `baz` etc! -* Leave on empty line at the end of your `.js` file +* Leave an empty line at the end of your `.js` file * Update this `README.md` file to add your example to the table Use [connect-as-acl-user.js](connect-as-acl-user.js) as a guide to develop a well formatted example script. From d06ecc77435b69fa544bd918f585a202e2a0714d Mon Sep 17 00:00:00 2001 From: Suze Shardlow Date: Fri, 22 Oct 2021 19:57:02 +0100 Subject: [PATCH 0916/1748] Fixes a bug in the Scan Iterator section. (#1694) --- README.md | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/README.md b/README.md index 63fe9d2a522..3b73d543c34 100644 --- a/README.md +++ b/README.md @@ -181,9 +181,9 @@ for await (const key of client.scanIterator()) { This works with `HSCAN`, `SSCAN`, and `ZSCAN` too: ```typescript -for await (const member of client.hScanIterator('hash')) {} -for await (const { field, value } of client.sScanIterator('set')) {} -for await (const { member, score } of client.zScanIterator('sorted-set')) {} +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: From 55391557ca8cac4de420cf9c854b2a9c22d8c074 Mon Sep 17 00:00:00 2001 From: Simon Prickett Date: Mon, 25 Oct 2021 22:53:22 +0100 Subject: [PATCH 0917/1748] Made examples use local version. --- examples/README.md | 12 ++++ examples/package-lock.json | 125 +++++++++++++++---------------------- examples/package.json | 2 +- 3 files changed, 64 insertions(+), 75 deletions(-) diff --git a/examples/README.md b/examples/README.md index ecb84a69f80..6d5d2a1f2e8 100644 --- a/examples/README.md +++ b/examples/README.md @@ -10,6 +10,18 @@ This folder contains example scripts showing how to use Node Redis in different We'd love to see more examples here. If you have an idea that you'd like to see included here, submit a Pull Request and we'll be sure to review it! Don't forget to check out our [contributing guide](../CONTRIBUTING.md). +## Setup + +To set up the examples folder so that you can run an example / develop one of your own: + +``` +$ git clone https://github.com/redis/node-redis.git +$ cd node-redis +$ npm install && npm run build +$ cd examples +$ npm install +``` + ### Coding Guidelines for Examples When adding a new example, please follow these guidelines: diff --git a/examples/package-lock.json b/examples/package-lock.json index 07eff138d1f..e99b7b2184c 100644 --- a/examples/package-lock.json +++ b/examples/package-lock.json @@ -8,103 +8,80 @@ "name": "node-redis-examples", "version": "1.0.0", "dependencies": { - "redis": "next" + "redis": "../" } }, - "node_modules/cluster-key-slot": { - "version": "1.1.0", - "resolved": "https://registry.npmjs.org/cluster-key-slot/-/cluster-key-slot-1.1.0.tgz", - "integrity": "sha512-2Nii8p3RwAPiFwsnZvukotvow2rIHM+yQ6ZcBXGHdniadkYGZYiGmkHJIbZPIV9nfv7m/U1IPMVVcAhoWFeklw==", - "engines": { - "node": ">=0.10.0" - } - }, - "node_modules/generic-pool": { - "version": "3.8.2", - "resolved": "https://registry.npmjs.org/generic-pool/-/generic-pool-3.8.2.tgz", - "integrity": "sha512-nGToKy6p3PAbYQ7p1UlWl6vSPwfwU6TMSWK7TTu+WUY4ZjyZQGniGGt2oNVvyNSpyZYSB43zMXVLcBm08MTMkg==", - "engines": { - "node": ">= 4" - } - }, - "node_modules/redis": { + "..": { "version": "4.0.0-rc.3", - "resolved": "https://registry.npmjs.org/redis/-/redis-4.0.0-rc.3.tgz", - "integrity": "sha512-yvijGYWp3aOvqpFgqovUWLWSdHgjUEtScqJmjTfUXj/4kEHuSW2TERFQelIBnrGeKh8//eYlLpCFKCjDYT4NQw==", + "license": "MIT", "dependencies": { "cluster-key-slot": "1.1.0", "generic-pool": "3.8.2", "redis-parser": "3.0.0", "yallist": "4.0.0" }, - "engines": { - "node": ">=12" - } - }, - "node_modules/redis-errors": { - "version": "1.2.0", - "resolved": "https://registry.npmjs.org/redis-errors/-/redis-errors-1.2.0.tgz", - "integrity": "sha1-62LSrbFeTq9GEMBK/hUpOEJQq60=", - "engines": { - "node": ">=4" - } - }, - "node_modules/redis-parser": { - "version": "3.0.0", - "resolved": "https://registry.npmjs.org/redis-parser/-/redis-parser-3.0.0.tgz", - "integrity": "sha1-tm2CjNyv5rS4pCin3vTGvKwxyLQ=", - "dependencies": { - "redis-errors": "^1.0.0" + "devDependencies": { + "@istanbuljs/nyc-config-typescript": "^1.0.1", + "@tsconfig/node12": "^1.0.9", + "@types/mocha": "^9.0.0", + "@types/node": "^16.11.1", + "@types/sinon": "^10.0.4", + "@types/which": "^2.0.1", + "@types/yallist": "^4.0.1", + "@typescript-eslint/eslint-plugin": "^5.1.0", + "@typescript-eslint/parser": "^5.1.0", + "eslint": "^8.0.1", + "mocha": "^9.1.3", + "nyc": "^15.1.0", + "release-it": "^14.11.6", + "sinon": "^11.1.2", + "source-map-support": "^0.5.20", + "ts-node": "^10.3.0", + "typedoc": "^0.22.6", + "typedoc-github-wiki-theme": "^0.6.0", + "typedoc-plugin-markdown": "^3.11.3", + "typescript": "^4.4.4", + "which": "^2.0.2" }, "engines": { - "node": ">=4" + "node": ">=12" } }, - "node_modules/yallist": { - "version": "4.0.0", - "resolved": "https://registry.npmjs.org/yallist/-/yallist-4.0.0.tgz", - "integrity": "sha512-3wdGidZyq5PB084XLES5TpOSRA3wjXAlIWMhum2kRcv/41Sn2emQ0dycQW4uZXLejwKvg6EsvbdlVL+FYEct7A==" + "node_modules/redis": { + "resolved": "..", + "link": true } }, "dependencies": { - "cluster-key-slot": { - "version": "1.1.0", - "resolved": "https://registry.npmjs.org/cluster-key-slot/-/cluster-key-slot-1.1.0.tgz", - "integrity": "sha512-2Nii8p3RwAPiFwsnZvukotvow2rIHM+yQ6ZcBXGHdniadkYGZYiGmkHJIbZPIV9nfv7m/U1IPMVVcAhoWFeklw==" - }, - "generic-pool": { - "version": "3.8.2", - "resolved": "https://registry.npmjs.org/generic-pool/-/generic-pool-3.8.2.tgz", - "integrity": "sha512-nGToKy6p3PAbYQ7p1UlWl6vSPwfwU6TMSWK7TTu+WUY4ZjyZQGniGGt2oNVvyNSpyZYSB43zMXVLcBm08MTMkg==" - }, "redis": { - "version": "4.0.0-rc.3", - "resolved": "https://registry.npmjs.org/redis/-/redis-4.0.0-rc.3.tgz", - "integrity": "sha512-yvijGYWp3aOvqpFgqovUWLWSdHgjUEtScqJmjTfUXj/4kEHuSW2TERFQelIBnrGeKh8//eYlLpCFKCjDYT4NQw==", + "version": "file:..", "requires": { + "@istanbuljs/nyc-config-typescript": "^1.0.1", + "@tsconfig/node12": "^1.0.9", + "@types/mocha": "^9.0.0", + "@types/node": "^16.11.1", + "@types/sinon": "^10.0.4", + "@types/which": "^2.0.1", + "@types/yallist": "^4.0.1", + "@typescript-eslint/eslint-plugin": "^5.1.0", + "@typescript-eslint/parser": "^5.1.0", "cluster-key-slot": "1.1.0", + "eslint": "^8.0.1", "generic-pool": "3.8.2", + "mocha": "^9.1.3", + "nyc": "^15.1.0", "redis-parser": "3.0.0", + "release-it": "^14.11.6", + "sinon": "^11.1.2", + "source-map-support": "^0.5.20", + "ts-node": "^10.3.0", + "typedoc": "^0.22.6", + "typedoc-github-wiki-theme": "^0.6.0", + "typedoc-plugin-markdown": "^3.11.3", + "typescript": "^4.4.4", + "which": "^2.0.2", "yallist": "4.0.0" } - }, - "redis-errors": { - "version": "1.2.0", - "resolved": "https://registry.npmjs.org/redis-errors/-/redis-errors-1.2.0.tgz", - "integrity": "sha1-62LSrbFeTq9GEMBK/hUpOEJQq60=" - }, - "redis-parser": { - "version": "3.0.0", - "resolved": "https://registry.npmjs.org/redis-parser/-/redis-parser-3.0.0.tgz", - "integrity": "sha1-tm2CjNyv5rS4pCin3vTGvKwxyLQ=", - "requires": { - "redis-errors": "^1.0.0" - } - }, - "yallist": { - "version": "4.0.0", - "resolved": "https://registry.npmjs.org/yallist/-/yallist-4.0.0.tgz", - "integrity": "sha512-3wdGidZyq5PB084XLES5TpOSRA3wjXAlIWMhum2kRcv/41Sn2emQ0dycQW4uZXLejwKvg6EsvbdlVL+FYEct7A==" } } } diff --git a/examples/package.json b/examples/package.json index 69fff5b5413..4963094b0ba 100644 --- a/examples/package.json +++ b/examples/package.json @@ -8,7 +8,7 @@ }, "type": "module", "dependencies": { - "redis": "next" + "redis": "../" } } From 9a4ac34f185f91c275c071637742c70bf5c0a56b Mon Sep 17 00:00:00 2001 From: Joshua T Date: Tue, 26 Oct 2021 04:01:19 +0530 Subject: [PATCH 0918/1748] Add `lua-multi-incr.js` example (#1692) Also fix syntax error in the lua example in the README Closes #1689. --- README.md | 4 ++-- examples/README.md | 7 ++++--- examples/lua-multi-incr.js | 31 +++++++++++++++++++++++++++++++ 3 files changed, 37 insertions(+), 5 deletions(-) create mode 100644 examples/lua-multi-incr.js diff --git a/README.md b/README.md index 3b73d543c34..97ad01df834 100644 --- a/README.md +++ b/README.md @@ -209,9 +209,9 @@ import { createClient, defineScript } from 'redis'; add: defineScript({ NUMBER_OF_KEYS: 1, SCRIPT: - "local val = redis.pcall('GET', KEYS[1]);' + 'return val + ARGV[1];", + "local val = redis.pcall('GET', KEYS[1]);" + "return val + ARGV[1];", transformArguments(key: string, toAdd: number): Array { - return [key, number.toString()]; + return [key, toAdd.toString()]; }, transformReply(reply: number): number { return reply; diff --git a/examples/README.md b/examples/README.md index 6d5d2a1f2e8..92f31d4468e 100644 --- a/examples/README.md +++ b/examples/README.md @@ -2,9 +2,10 @@ This folder contains example scripts showing how to use Node Redis in different scenarios. -| File Name | Description | -|--------------------------|--------------------------------------| -| `connect-as-acl-user.js` | Connect to Redis 6 using an ACL user | +| File Name | Description | +| ------------------------ | ----------------------------------------------------------------------------- | +| `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 | ## Contributing diff --git a/examples/lua-multi-incr.js b/examples/lua-multi-incr.js new file mode 100644 index 00000000000..ec433c27ea2 --- /dev/null +++ b/examples/lua-multi-incr.js @@ -0,0 +1,31 @@ +// Define a custome lua script that accepts two keys and an amount to +// increment each of them by + +import { createClient, defineScript } from 'redis'; + +async function luaMultiIncr() { + const client = createClient({ + scripts: { + mincr: defineScript({ + NUMBER_OF_KEYS: 2, + SCRIPT: + 'return {' + + 'redis.pcall("INCRBY", KEYS[1], ARGV[1]),' + + 'redis.pcall("INCRBY", KEYS[2], ARGV[1])' + + '}', + transformArguments(key1, key2, increment) { + return [key1, key2, increment.toString()]; + }, + }), + }, + }); + + await client.connect(); + + await client.set('mykey', '5'); + console.log(await client.mincr('mykey', 'myotherkey', 10)); // [ 15, 10 ] + + await client.quit(); +} + +luaMultiIncr(); From 9c3c42f11372047fe9ebac0f26e13bf08ca9d563 Mon Sep 17 00:00:00 2001 From: Aditya Rastogi Date: Tue, 26 Oct 2021 04:13:47 +0530 Subject: [PATCH 0919/1748] Add(examples): Create an example for blPop & lPush (#1696) * Add(examples): Create an example for blPop & lPush Signed-off-by: Aditya Rastogi * Update(examples): fix case, add timeout, update readme Signed-off-by: Aditya Rastogi Closes #1693. --- examples/README.md | 7 ++++--- examples/blocking-list-pop.js | 32 ++++++++++++++++++++++++++++++++ 2 files changed, 36 insertions(+), 3 deletions(-) create mode 100644 examples/blocking-list-pop.js diff --git a/examples/README.md b/examples/README.md index 92f31d4468e..353fa552192 100644 --- a/examples/README.md +++ b/examples/README.md @@ -2,9 +2,10 @@ This folder contains example scripts showing how to use Node Redis in different scenarios. -| File Name | Description | -| ------------------------ | ----------------------------------------------------------------------------- | -| `connect-as-acl-user.js` | Connect to Redis 6 using an ACL user | +| File Name | Description | +|--------------------------|--------------------------------------| +| `connect-as-acl-user.js` | Connect to Redis 6 using an ACL user | +| `blocking-list-pop.js` | Block until an element is pushed to a list | | `lua-multi-incr.js` | Define a custom lua script that allows you to perform INCRBY on multiple keys | ## Contributing diff --git a/examples/blocking-list-pop.js b/examples/blocking-list-pop.js new file mode 100644 index 00000000000..59dde3274a3 --- /dev/null +++ b/examples/blocking-list-pop.js @@ -0,0 +1,32 @@ +// This example shows how to use the blocking LPUSH command. + +// This code shows how to run with isolation the blPop Command to block the script while waiting for a value to be pushed to the list. +// The script will be blocked until the LPUSH command is executed. +// After which we log the list and quit the client. + +import { createClient, commandOptions } from 'redis'; + +async function blockingListPop() { + const client = createClient(); + + await client.connect(); + + const keyName = 'keyName'; + + const blpopPromise = client.blPop( + commandOptions({ isolated: true }), + keyName, + 0 + ); + + await client.lPush(keyName, 'value'); + + await blpopPromise; + + console.log('blpopPromise resolved'); + console.log(keyName); + + await client.quit(); +} + +blockingListPop(); From fdffa2383fb74671fe96491ecd83eaf7c64e956c Mon Sep 17 00:00:00 2001 From: Rohan Kumar Date: Wed, 27 Oct 2021 04:52:19 +0530 Subject: [PATCH 0920/1748] 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 Closes #1688. --- examples/README.md | 11 +++++----- examples/command-with-modifiers.js | 32 ++++++++++++++++++++++++++++++ 2 files changed, 38 insertions(+), 5 deletions(-) create mode 100644 examples/command-with-modifiers.js diff --git a/examples/README.md b/examples/README.md index 353fa552192..07cbf8b3737 100644 --- a/examples/README.md +++ b/examples/README.md @@ -2,11 +2,12 @@ This folder contains example scripts showing how to use Node Redis in different scenarios. -| File Name | Description | -|--------------------------|--------------------------------------| -| `connect-as-acl-user.js` | Connect to Redis 6 using an ACL user | -| `blocking-list-pop.js` | Block until an element is pushed to a list | -| `lua-multi-incr.js` | Define a custom lua script that allows you to perform INCRBY on multiple keys | +| File Name | Description | +|-----------------------------|--------------------------------------| +| `connect-as-acl-user.js` | Connect to Redis 6 using an ACL user | +| `blocking-list-pop.js` | Block until an element is pushed to a list | +| `lua-multi-incr.js` | Define a custom lua script that allows you to perform INCRBY on multiple keys | +| `command-with-modifiers.js` | Define a script that allows to run a command with several modifiers | ## Contributing diff --git a/examples/command-with-modifiers.js b/examples/command-with-modifiers.js new file mode 100644 index 00000000000..2932aec0d64 --- /dev/null +++ b/examples/command-with-modifiers.js @@ -0,0 +1,32 @@ +// Define a custom script that shows example of SET command +// with several modifiers. + +import { createClient } from 'redis'; + +async function commandWithModifiers() { + const client = createClient(); + + await client.connect(); + await client.del('mykey'); + + let result = await client.set('mykey', 'myvalue', { + EX: 60, + GET: true + } + ); + + console.log(result); //nil + + result = await client.set('mykey', 'newvalue', { + EX: 60, + GET: true + } + ); + + console.log(result); //myvalue + + await client.quit(); +} + +commandWithModifiers(); + From d409120e5be6e96a3d5d6efef1e4532f48b45182 Mon Sep 17 00:00:00 2001 From: Kalki Date: Thu, 28 Oct 2021 02:24:12 +0530 Subject: [PATCH 0921/1748] 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 Co-authored-by: Simon Prickett Closes #1697. --- examples/README.md | 1 + examples/set-scan.js | 19 +++++++++++++++++++ 2 files changed, 20 insertions(+) create mode 100644 examples/set-scan.js diff --git a/examples/README.md b/examples/README.md index 07cbf8b3737..2b8fbec9277 100644 --- a/examples/README.md +++ b/examples/README.md @@ -8,6 +8,7 @@ This folder contains example scripts showing how to use Node Redis in different | `blocking-list-pop.js` | Block until an element is pushed to a list | | `lua-multi-incr.js` | Define a custom lua script that allows you to perform INCRBY on multiple keys | | `command-with-modifiers.js` | Define a script that allows to run a command with several modifiers | +| `set-scan.js` | An example script that shows how to use the SSCAN iterator functionality | ## Contributing diff --git a/examples/set-scan.js b/examples/set-scan.js new file mode 100644 index 00000000000..3cca05b152d --- /dev/null +++ b/examples/set-scan.js @@ -0,0 +1,19 @@ +// An example script that shows how to use the SSCAN iterator functionality to retrieve the contents of a Redis set. +// Create the set in redis-cli with this command: +// sadd setName a b c d e f g h i j k l m n o p q + +import { createClient } from 'redis'; + +async function setScan() { + const client = createClient(); + await client.connect(); + + const setName = 'setName'; + for await (const member of client.sScanIterator(setName)) { + console.log(member); + } + + await client.quit(); +} + +setScan(); From f6f9b3dccd4152d221f0d9bba4bbe512f4a30629 Mon Sep 17 00:00:00 2001 From: Leibale Eidelman Date: Sun, 31 Oct 2021 21:09:51 -0400 Subject: [PATCH 0922/1748] fix #1706 - HSET return type should be number --- lib/commands/HSET.ts | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/lib/commands/HSET.ts b/lib/commands/HSET.ts index 1d4acd6c018..4c60b91ad89 100644 --- a/lib/commands/HSET.ts +++ b/lib/commands/HSET.ts @@ -46,4 +46,4 @@ function pushObject(args: Array, object: HSETObject): void { } } -export declare function transformReply(): string; +export declare function transformReply(): number; From 5cff3320d2213c504695f694d7b742df3f5e11c9 Mon Sep 17 00:00:00 2001 From: leibale Date: Mon, 1 Nov 2021 11:30:25 -0400 Subject: [PATCH 0923/1748] use dockers for tests, fix some bugs --- .eslintrc.json | 5 +- .github/workflows/tests.yml | 10 +- .nycrc.json | 2 +- README.md | 2 +- docs/clustering.md | 2 +- lib/client/commands-queue.ts | 8 +- lib/client/index.spec.ts | 474 ++++----- lib/client/index.ts | 26 +- lib/client/socket.spec.ts | 2 +- lib/client/socket.ts | 73 +- lib/cluster/index.spec.ts | 57 +- lib/commands/ACL_CAT.spec.ts | 4 +- lib/commands/ACL_DELUSER.spec.ts | 8 +- lib/commands/ACL_GENPASS.spec.ts | 4 +- lib/commands/ACL_GETUSER.spec.ts | 10 +- lib/commands/ACL_LIST.spec.ts | 4 +- lib/commands/ACL_LOAD.spec.ts | 4 +- lib/commands/ACL_LOG.spec.ts | 4 +- lib/commands/ACL_LOG_RESET.spec.ts | 4 +- lib/commands/ACL_SAVE.spec.ts | 4 +- lib/commands/ACL_SETUSER.spec.ts | 4 +- lib/commands/ACL_USERS.spec.ts | 4 +- lib/commands/ACL_WHOAMI.spec.ts | 4 +- lib/commands/APPEND.spec.ts | 2 +- lib/commands/BITCOUNT.spec.ts | 6 +- lib/commands/BITFIELD.spec.ts | 6 +- lib/commands/BITOP.spec.ts | 10 +- lib/commands/BITPOS.spec.ts | 10 +- lib/commands/BLMOVE.spec.ts | 12 +- lib/commands/BLPOP.spec.ts | 10 +- lib/commands/BRPOP.spec.ts | 10 +- lib/commands/BRPOPLPUSH.spec.ts | 10 +- lib/commands/BZPOPMAX.spec.ts | 6 +- lib/commands/BZPOPMIN.spec.ts | 6 +- lib/commands/CLIENT_ID.spec.ts | 6 +- lib/commands/CLUSTER_SLOTS.spec.ts | 2 +- lib/commands/COMMAND.spec.ts | 23 +- lib/commands/COMMAND_COUNT.spec.ts | 6 +- lib/commands/COMMAND_GETKEYS.spec.ts | 6 +- lib/commands/COMMAND_INFO.spec.ts | 42 +- lib/commands/COPY.spec.ts | 8 +- lib/commands/DBSIZE.spec.ts | 6 +- lib/commands/DECR.spec.ts | 6 +- lib/commands/DECRBY.spec.ts | 6 +- lib/commands/DEL.spec.ts | 6 +- lib/commands/DUMP.spec.ts | 6 +- lib/commands/ECHO.spec.ts | 6 +- lib/commands/EVAL.spec.ts | 10 +- lib/commands/EXISTS.spec.ts | 6 +- lib/commands/EXPIRE.spec.ts | 6 +- lib/commands/EXPIREAT.spec.ts | 8 +- lib/commands/FLUSHALL.spec.ts | 6 +- lib/commands/FLUSHDB.spec.ts | 6 +- lib/commands/GEOADD.spec.ts | 10 +- lib/commands/GEODIST.spec.ts | 14 +- lib/commands/GEOHASH.spec.ts | 10 +- lib/commands/GEOPOS.spec.ts | 14 +- lib/commands/GEOSEARCH.spec.ts | 12 +- lib/commands/GEOSEARCHSTORE.spec.ts | 12 +- lib/commands/GEOSEARCH_WITH.spec.ts | 14 +- lib/commands/GET.spec.ts | 10 +- lib/commands/GETBIT.spec.ts | 10 +- lib/commands/GETDEL.spec.ts | 13 +- lib/commands/GETEX.spec.ts | 12 +- lib/commands/GETRANGE.spec.ts | 11 +- lib/commands/GETSET.spec.ts | 10 +- lib/commands/GET_BUFFER.spec.ts | 10 +- lib/commands/HDEL.spec.ts | 6 +- lib/commands/HELLO.spec.ts | 29 +- lib/commands/HEXISTS.spec.ts | 6 +- lib/commands/HGET.spec.ts | 6 +- lib/commands/HGETALL.spec.ts | 6 +- lib/commands/HINCRBY.spec.ts | 6 +- lib/commands/HINCRBYFLOAT.spec.ts | 6 +- lib/commands/HKEYS.spec.ts | 6 +- lib/commands/HLEN.spec.ts | 6 +- lib/commands/HMGET.spec.ts | 6 +- lib/commands/HRANDFIELD.spec.ts | 8 +- lib/commands/HRANDFIELD_COUNT.spec.ts | 8 +- .../HRANDFIELD_COUNT_WITHVALUES.spec.ts | 8 +- lib/commands/HSCAN.spec.ts | 6 +- lib/commands/HSET.spec.ts | 10 +- lib/commands/HSETNX.spec.ts | 6 +- lib/commands/HSTRLEN.spec.ts | 6 +- lib/commands/HVALS.spec.ts | 6 +- lib/commands/INCR.spec.ts | 6 +- lib/commands/INCRBY.spec.ts | 6 +- lib/commands/INCRBYFLOAT.spec.ts | 6 +- lib/commands/KEYS.spec.ts | 6 +- lib/commands/LASTSAVE.spec.ts | 6 +- lib/commands/LINDEX.spec.ts | 10 +- lib/commands/LINSERT.spec.ts | 10 +- lib/commands/LLEN.spec.ts | 10 +- lib/commands/LMOVE.spec.ts | 12 +- lib/commands/LOLWUT.spec.ts | 7 +- lib/commands/LPOP.spec.ts | 10 +- lib/commands/LPOP_COUNT.spec.ts | 12 +- lib/commands/LPOS.spec.ts | 12 +- lib/commands/LPOS_COUNT.spec.ts | 12 +- lib/commands/LPUSH.spec.ts | 10 +- lib/commands/LPUSHX.spec.ts | 10 +- lib/commands/LRANGE.spec.ts | 10 +- lib/commands/LREM.spec.ts | 10 +- lib/commands/LSET.spec.ts | 10 +- lib/commands/LTRIM.spec.ts | 10 +- lib/commands/LTRIM.ts | 2 +- lib/commands/MEMORY_DOCTOR.spec.ts | 6 +- lib/commands/MEMORY_MALLOC-STATS.spec.ts | 6 +- lib/commands/MEMORY_PURGE.spec.ts | 6 +- lib/commands/MEMORY_USAGE.spec.ts | 6 +- lib/commands/MGET.spec.ts | 10 +- lib/commands/MOVE.spec.ts | 6 +- lib/commands/MSET.spec.ts | 10 +- lib/commands/MSETNX.spec.ts | 10 +- lib/commands/PERSIST.spec.ts | 6 +- lib/commands/PEXPIRE.spec.ts | 6 +- lib/commands/PEXPIREAT.spec.ts | 8 +- lib/commands/PFADD.spec.ts | 6 +- lib/commands/PFCOUNT.spec.ts | 6 +- lib/commands/PFMERGE.spec.ts | 6 +- lib/commands/PING.spec.ts | 6 +- lib/commands/PSETEX.spec.ts | 10 +- lib/commands/PTTL.spec.ts | 6 +- lib/commands/PUBLISH.spec.ts | 6 +- lib/commands/PUBSUB_CHANNELS.spec.ts | 6 +- lib/commands/PUBSUB_NUMPAT.spec.ts | 6 +- lib/commands/PUBSUB_NUMSUB.spec.ts | 6 +- lib/commands/RANDOMKEY.spec.ts | 6 +- lib/commands/RENAME.spec.ts | 6 +- lib/commands/RENAMENX.spec.ts | 6 +- lib/commands/ROLE.spec.ts | 6 +- lib/commands/RPOP.spec.ts | 10 +- lib/commands/RPOPLPUSH.spec.ts | 10 +- lib/commands/RPOP_COUNT.spec.ts | 12 +- lib/commands/RPUSH.spec.ts | 10 +- lib/commands/RPUSHX.spec.ts | 10 +- lib/commands/SADD.spec.ts | 6 +- lib/commands/SCAN.spec.ts | 6 +- lib/commands/SCARD.spec.ts | 6 +- lib/commands/SCRIPT_DEBUG.spec.ts | 6 +- lib/commands/SCRIPT_EXISTS.spec.ts | 6 +- lib/commands/SCRIPT_FLUSH.spec.ts | 6 +- lib/commands/SCRIPT_LOAD.spec.ts | 6 +- lib/commands/SDIFF.spec.ts | 6 +- lib/commands/SDIFFSTORE.spec.ts | 6 +- lib/commands/SET.spec.ts | 13 +- lib/commands/SETBIT.spec.ts | 10 +- lib/commands/SETEX.spec.ts | 10 +- lib/commands/SETNX .spec.ts | 10 +- lib/commands/SETRANGE.spec.ts | 10 +- lib/commands/SINTER.spec.ts | 6 +- lib/commands/SINTERSTORE.spec.ts | 6 +- lib/commands/SISMEMBER.spec.ts | 8 +- lib/commands/SMEMBERS.spec.ts | 6 +- lib/commands/SMISMEMBER.spec.ts | 8 +- lib/commands/SMOVE.spec.ts | 6 +- lib/commands/SORT.spec.ts | 8 +- lib/commands/SPOP.spec.ts | 6 +- lib/commands/SRANDMEMBER.spec.ts | 6 +- lib/commands/SRANDMEMBER_COUNT.spec.ts | 6 +- lib/commands/SREM.spec.ts | 6 +- lib/commands/SSCAN.spec.ts | 6 +- lib/commands/STRLEN.spec.ts | 10 +- lib/commands/SUNION.spec.ts | 6 +- lib/commands/SUNIONSTORE.spec.ts | 6 +- lib/commands/SWAPDB.spec.ts | 6 +- lib/commands/TIME.spec.ts | 6 +- lib/commands/TOUCH.spec.ts | 6 +- lib/commands/TTL.spec.ts | 6 +- lib/commands/TYPE.spec.ts | 6 +- lib/commands/UNLINK.spec.ts | 6 +- lib/commands/UNWATCH.spec.ts | 6 +- lib/commands/WAIT.spec.ts | 6 +- lib/commands/XACK.spec.ts | 6 +- lib/commands/XADD.spec.ts | 8 +- lib/commands/XAUTOCLAIM.spec.ts | 10 +- lib/commands/XAUTOCLAIM_JUSTID.spec.ts | 8 +- lib/commands/XCLAIM.spec.ts | 8 +- lib/commands/XCLAIM_JUSTID.spec.ts | 8 +- lib/commands/XDEL.spec.ts | 6 +- lib/commands/XGROUP_CREATE.spec.ts | 6 +- lib/commands/XGROUP_CREATECONSUMER.spec.ts | 10 +- lib/commands/XGROUP_DELCONSUMER.spec.ts | 8 +- lib/commands/XGROUP_DESTROY.spec.ts | 8 +- lib/commands/XGROUP_SETID.spec.ts | 6 +- lib/commands/XINFO_CONSUMERS.spec.ts | 8 +- lib/commands/XINFO_GROUPS.spec.ts | 8 +- lib/commands/XINFO_STREAM.spec.ts | 6 +- lib/commands/XLEN.spec.ts | 6 +- lib/commands/XPENDING.spec.ts | 6 +- lib/commands/XPENDING_RANGE.spec.ts | 10 +- lib/commands/XRANGE.spec.ts | 8 +- lib/commands/XREAD.spec.ts | 10 +- lib/commands/XREADGROUP.spec.ts | 44 +- lib/commands/XREVRANGE.spec.ts | 8 +- lib/commands/XTRIM.spec.ts | 6 +- lib/commands/ZADD.spec.ts | 6 +- lib/commands/ZCARD.spec.ts | 6 +- lib/commands/ZCOUNT.spec.ts | 6 +- lib/commands/ZDIFF.spec.ts | 8 +- lib/commands/ZDIFFSTORE.spec.ts | 8 +- lib/commands/ZDIFF_WITHSCORES.spec.ts | 8 +- lib/commands/ZINCRBY.spec.ts | 6 +- lib/commands/ZINTER.spec.ts | 10 +- lib/commands/ZINTERSTORE.spec.ts | 6 +- lib/commands/ZINTER_WITHSCORES.spec.ts | 8 +- lib/commands/ZLEXCOUNT.spec.ts | 6 +- lib/commands/ZMSCORE.spec.ts | 8 +- lib/commands/ZPOPMAX.spec.ts | 10 +- lib/commands/ZPOPMAX_COUNT.spec.ts | 6 +- lib/commands/ZPOPMIN.spec.ts | 10 +- lib/commands/ZPOPMIN_COUNT.spec.ts | 6 +- lib/commands/ZRANDMEMBER.spec.ts | 8 +- lib/commands/ZRANDMEMBER_COUNT.spec.ts | 8 +- .../ZRANDMEMBER_COUNT_WITHSCORES.spec.ts | 8 +- lib/commands/ZRANGE.spec.ts | 6 +- lib/commands/ZRANGEBYLEX.spec.ts | 6 +- lib/commands/ZRANGEBYSCORE.spec.ts | 6 +- lib/commands/ZRANGEBYSCORE_WITHSCORES.spec.ts | 6 +- lib/commands/ZRANGESTORE.spec.ts | 9 +- lib/commands/ZRANGE_WITHSCORES.spec.ts | 6 +- lib/commands/ZRANK.spec.ts | 6 +- lib/commands/ZREM.spec.ts | 6 +- lib/commands/ZREMRANGEBYLEX.spec.ts | 6 +- lib/commands/ZREMRANGEBYRANK.spec.ts | 6 +- lib/commands/ZREMRANGEBYSCORE.spec.ts | 6 +- lib/commands/ZREVRANK.spec.ts | 6 +- lib/commands/ZSCAN.spec.ts | 6 +- lib/commands/ZSCORE.spec.ts | 6 +- lib/commands/ZUNION.spec.ts | 8 +- lib/commands/ZUNIONSTORE.spec.ts | 6 +- lib/commands/ZUNION_WITHSCORES.spec.ts | 8 +- lib/commands/generic-transformers.spec.ts | 2 +- lib/errors.ts | 6 + lib/test-utils.ts | 384 -------- lib/test-utils/dockers.ts | 215 ++++ lib/test-utils/index.ts | 49 + lib/test-utils/test-utils.ts | 175 ++++ package-lock.json | 915 +++++++++--------- package.json | 18 +- 240 files changed, 2124 insertions(+), 2046 deletions(-) delete mode 100644 lib/test-utils.ts create mode 100644 lib/test-utils/dockers.ts create mode 100644 lib/test-utils/index.ts create mode 100644 lib/test-utils/test-utils.ts diff --git a/.eslintrc.json b/.eslintrc.json index 9378001252c..4536bc31338 100644 --- a/.eslintrc.json +++ b/.eslintrc.json @@ -8,5 +8,8 @@ "eslint:recommended", "plugin:@typescript-eslint/eslint-recommended", "plugin:@typescript-eslint/recommended" - ] + ], + "rules": { + "semi": [2, "always"] + } } diff --git a/.github/workflows/tests.yml b/.github/workflows/tests.yml index 7ef80685d56..b6b2dd050eb 100644 --- a/.github/workflows/tests.yml +++ b/.github/workflows/tests.yml @@ -13,7 +13,7 @@ jobs: fail-fast: false matrix: node-version: [12.x, 14.x, 16.x] - redis-version: [5.x, 6.0.x, 6.2.x] + redis-version: [5, 6.0, 6.2] steps: - uses: actions/checkout@v2.3.4 @@ -25,17 +25,11 @@ jobs: with: node-version: ${{ matrix.node-version }} - - name: Setup Redis - uses: shogo82148/actions-setup-redis@v1.12.0 - with: - redis-version: ${{ matrix.redis-version }} - auto-start: "false" - - name: Install Packages run: npm ci - name: Run Tests - run: npm run test + run: npm run test -- --forbid-only --redis-version=${{ matrix.redis-version }} - name: Generate lcov run: ./node_modules/.bin/nyc report -r lcov diff --git a/.nycrc.json b/.nycrc.json index 925d954248c..918b308a898 100644 --- a/.nycrc.json +++ b/.nycrc.json @@ -1,4 +1,4 @@ { "extends": "@istanbuljs/nyc-config-typescript", - "exclude": ["**/*.spec.ts", "lib/test-utils.ts"] + "exclude": ["**/*.spec.ts", "lib/test-utils/**/*.ts"] } \ No newline at end of file diff --git a/README.md b/README.md index 97ad01df834..9fe9e70a253 100644 --- a/README.md +++ b/README.md @@ -126,7 +126,7 @@ This pattern works especially well for blocking commands—such as `BLPOP` and ` ```typescript import { commandOptions } from 'redis'; -const blPopPromise = client.blPop(commandOptions({ isolated: true }), 'key'); +const blPopPromise = client.blPop(commandOptions({ isolated: true }), 'key', 0); await client.lPush('key', ['1', '2']); diff --git a/docs/clustering.md b/docs/clustering.md index 028ebea81d5..a84dc3b1aa4 100644 --- a/docs/clustering.md +++ b/docs/clustering.md @@ -45,7 +45,7 @@ import { createCluster } from 'redis'; Commands such as `GET`, `SET`, etc. will be routed by the first key, for instance `MGET 1 2 3` will be routed by the key `1`. -### [Server Commands][https://redis.io/commands#server] +### [Server Commands](https://redis.io/commands#server) Admin commands such as `MEMORY STATS`, `FLUSHALL`, etc. are not attached to the cluster, and should be executed on a specific node using `.getSlot()` or `.getAllMasters()`. diff --git a/lib/client/commands-queue.ts b/lib/client/commands-queue.ts index c07e582238c..4e2a66c606e 100644 --- a/lib/client/commands-queue.ts +++ b/lib/client/commands-queue.ts @@ -1,7 +1,7 @@ import LinkedList from 'yallist'; import RedisParser from 'redis-parser'; import { AbortError } from '../errors'; -import { RedisCommandRawReply } from '../commands'; +import { RedisCommandArguments, RedisCommandRawReply } from '../commands'; export interface QueueCommandOptions { asap?: boolean; @@ -10,7 +10,7 @@ export interface QueueCommandOptions { } interface CommandWaitingToBeSent extends CommandWaitingForReply { - args: Array; + args: RedisCommandArguments; chainId?: symbol; abort?: { signal: AbortSignal; @@ -107,7 +107,7 @@ export default class RedisCommandsQueue { this.#maxLength = maxLength; } - addCommand(args: Array, options?: QueueCommandOptions, bufferMode?: boolean): Promise { + addCommand(args: RedisCommandArguments, options?: QueueCommandOptions, bufferMode?: boolean): Promise { if (this.#pubSubState.subscribing || this.#pubSubState.subscribed) { return Promise.reject(new Error('Cannot send commands in PubSub mode')); } else if (this.#maxLength && this.#waitingToBeSent.length + this.#waitingForReply.length >= this.#maxLength) { @@ -247,7 +247,7 @@ export default class RedisCommandsQueue { ]); } - getCommandToSend(): Array | undefined { + getCommandToSend(): RedisCommandArguments | undefined { const toSend = this.#waitingToBeSent.shift(); if (toSend) { diff --git a/lib/client/index.spec.ts b/lib/client/index.spec.ts index bdfebed5758..51dded18b1f 100644 --- a/lib/client/index.spec.ts +++ b/lib/client/index.spec.ts @@ -1,11 +1,12 @@ -import { strict as assert, AssertionError } from 'assert'; -import { once } from 'events'; -import { itWithClient, TEST_REDIS_SERVERS, TestRedisServers, waitTillBeenCalled, isRedisVersionGreaterThan } from '../test-utils'; -import RedisClient from '.'; -import { AbortError, ClientClosedError, ConnectionTimeoutError, DisconnectsClientError, WatchError } from '../errors'; +import { strict as assert } from 'assert'; +import testUtils, { GLOBAL, waitTillBeenCalled } from '../test-utils'; +import RedisClient, { RedisClientType } from '.'; +import { RedisClientMultiCommandType } from './multi-command'; +import { RedisCommandArguments, RedisCommandRawReply, RedisModules, RedisScripts } from '../commands'; +import { AbortError, ClientClosedError, ConnectionTimeoutError, DisconnectsClientError, SocketClosedUnexpectedlyError, WatchError } from '../errors'; import { defineScript } from '../lua-script'; import { spy } from 'sinon'; -import { RedisNetSocketOptions } from '../client/socket'; +import { once } from 'events'; export const SQUARE_SCRIPT = defineScript({ NUMBER_OF_KEYS: 0, @@ -75,43 +76,21 @@ describe('Client', () => { } ); }); - - it('createClient with url', async () => { - const client = RedisClient.create({ - url: `redis://localhost:${(TEST_REDIS_SERVERS[TestRedisServers.OPEN].socket as RedisNetSocketOptions)!.port!.toString()}/1` - }); - - await client.connect(); - - try { - assert.equal( - await client.ping(), - 'PONG' - ); - } finally { - await client.disconnect(); - } - }) }); describe('authentication', () => { - itWithClient(TestRedisServers.PASSWORD, 'Client should be authenticated', async client => { + testUtils.testWithClient('Client should be authenticated', async client => { assert.equal( await client.ping(), 'PONG' ); - }); - - it('should not retry connecting if failed due to wrong auth', async () => { - const client = RedisClient.create({ - ...TEST_REDIS_SERVERS[TestRedisServers.PASSWORD], - password: 'wrongpassword' - }); + }, GLOBAL.SERVERS.PASSWORD); + testUtils.testWithClient('should not retry connecting if failed due to wrong auth', async client => { let message; - if (isRedisVersionGreaterThan([6, 2])) { + if (testUtils.isVersionGreaterThan([6, 2])) { message = 'WRONGPASS invalid username-password pair or user is disabled.'; - } else if (isRedisVersionGreaterThan([6])) { + } else if (testUtils.isVersionGreaterThan([6])) { message = 'WRONGPASS invalid username-password pair'; } else { message = 'ERR invalid password'; @@ -123,173 +102,216 @@ describe('Client', () => { ); assert.equal(client.isOpen, false); + }, { + ...GLOBAL.SERVERS.PASSWORD, + clientOptions: { + password: 'wrongpassword' + }, + disableClientSetup: true }); - itWithClient(TestRedisServers.PASSWORD, 'should execute AUTH before SELECT', async client => { + testUtils.testWithClient('should execute AUTH before SELECT', async client => { assert.equal( (await client.clientInfo()).db, 2 ); }, { - minimumRedisVersion: [6, 2], + ...GLOBAL.SERVERS.PASSWORD, clientOptions: { + ...GLOBAL.SERVERS.PASSWORD.clientOptions, database: 2 - } + }, + minimumDockerVersion: [6, 2] }); }); describe('legacyMode', () => { - const client = RedisClient.create({ - ...TEST_REDIS_SERVERS[TestRedisServers.OPEN], - scripts: { - square: SQUARE_SCRIPT - }, - legacyMode: true - }); - - before(() => client.connect()); - afterEach(() => client.v4.flushAll()); - after(() => client.disconnect()); - - it('client.sendCommand should call the callback', done => { - (client as any).sendCommand('PING', (err?: Error, reply?: string) => { - if (err) { - return done(err); - } + function sendCommandAsync(client: RedisClientType, args: RedisCommandArguments): Promise { + return new Promise((resolve, reject) => { + (client as any).sendCommand(args, (err: Error | undefined, reply: RedisCommandRawReply) => { + if (err) return reject(err); - try { - assert.equal(reply, 'PONG'); - done(); - } catch (err) { - done(err); - } + resolve(reply); + }); }); + } + + testUtils.testWithClient('client.sendCommand should call the callback', async client => { + assert.equal( + await sendCommandAsync(client, ['PING']), + 'PONG' + ); + }, { + ...GLOBAL.SERVERS.OPEN, + clientOptions: { + legacyMode: true + } }); - it('client.sendCommand should work without callback', async () => { - (client as any).sendCommand('PING'); + testUtils.testWithClient('client.sendCommand should work without callback', async client => { + client.sendCommand(['PING']); await client.v4.ping(); // make sure the first command was replied + }, { + ...GLOBAL.SERVERS.OPEN, + clientOptions: { + legacyMode: true + } }); - it('client.v4.sendCommand should return a promise', async () => { + testUtils.testWithClient('client.v4.sendCommand should return a promise', async client => { assert.equal( await client.v4.sendCommand(['PING']), 'PONG' ); + }, { + ...GLOBAL.SERVERS.OPEN, + clientOptions: { + legacyMode: true + } }); - it('client.{command} should accept vardict arguments', done => { - (client as any).set('a', 'b', (err?: Error, reply?: string) => { - if (err) { - return done(err); - } + function setAsync(client: RedisClientType, ...args: Array): Promise { + return new Promise((resolve, reject) => { + (client as any).set(...args, (err: Error | undefined, reply: RedisCommandRawReply) => { + if (err) return reject(err); - try { - assert.equal(reply, 'OK'); - done(); - } catch (err) { - done(err); - } + resolve(reply); + }); }); - }); - - it('client.{command} should accept arguments array', done => { - (client as any).set(['a', 'b'], (err?: Error, reply?: string) => { - if (err) { - return done(err); - } + } - try { - assert.equal(reply, 'OK'); - done(); - } catch (err) { - done(err); - } - }); + testUtils.testWithClient('client.{command} should accept vardict arguments', async client => { + assert.equal( + await setAsync(client, 'a', 'b'), + 'OK' + ); + }, { + ...GLOBAL.SERVERS.OPEN, + clientOptions: { + legacyMode: true + } }); - it('client.{command} should accept mix of strings and array of strings', done => { - (client as any).set(['a'], 'b', ['XX'], (err?: Error, reply?: string) => { - if (err) { - return done(err); - } + testUtils.testWithClient('client.{command} should accept arguments array', async client => { + assert.equal( + await setAsync(client, ['a', 'b']), + 'OK' + ); + }, { + ...GLOBAL.SERVERS.OPEN, + clientOptions: { + legacyMode: true + } + }); - try { - assert.equal(reply, null); - done(); - } catch (err) { - done(err); - } - }); + testUtils.testWithClient('client.{command} should accept mix of strings and array of strings', async client => { + assert.equal( + await setAsync(client, ['a'], 'b', ['XX']), + null + ); + }, { + ...GLOBAL.SERVERS.OPEN, + clientOptions: { + legacyMode: true + } }); - it('client.multi.ping.exec should call the callback', done => { - (client as any).multi() - .ping() - .exec((err?: Error, reply?: string) => { - if (err) { - return done(err); - } + function multiExecAsync(multi: RedisClientMultiCommandType): Promise> { + return new Promise((resolve, reject) => { + (multi as any).exec((err: Error | undefined, replies: Array) => { + if (err) return reject(err); - try { - assert.deepEqual(reply, ['PONG']); - done(); - } catch (err) { - done(err); - } + resolve(replies); }); + }); + } + + testUtils.testWithClient('client.multi.ping.exec should call the callback', async client => { + assert.deepEqual( + await multiExecAsync( + client.multi().ping() + ), + ['PONG'] + ); + }, { + ...GLOBAL.SERVERS.OPEN, + clientOptions: { + legacyMode: true + } }); - it('client.multi.ping.exec should work without callback', async () => { - (client as any).multi() + testUtils.testWithClient('client.multi.ping.exec should call the callback', async client => { + client.multi() .ping() .exec(); await client.v4.ping(); // make sure the first command was replied + }, { + ...GLOBAL.SERVERS.OPEN, + clientOptions: { + legacyMode: true + } }); - it('client.multi.ping.v4.ping.v4.exec should return a promise', async () => { + testUtils.testWithClient('client.multi.ping.v4.ping.v4.exec should return a promise', async client => { assert.deepEqual( - await ((client as any).multi() + await client.multi() .ping() .v4.ping() - .v4.exec()), + .v4.exec(), ['PONG', 'PONG'] ); + }, { + ...GLOBAL.SERVERS.OPEN, + clientOptions: { + legacyMode: true + } }); - it('client.{script} should return a promise', async () => { - assert.equal(await client.square(2), 4); + testUtils.testWithClient('client.{script} should return a promise', async client => { + assert.equal( + await client.square(2), + 4 + ); + }, { + ...GLOBAL.SERVERS.OPEN, + clientOptions: { + legacyMode: true, + scripts: { + square: SQUARE_SCRIPT + } + } }); }); describe('events', () => { - it('connect, ready, end', async () => { - const client = RedisClient.create(TEST_REDIS_SERVERS[TestRedisServers.OPEN]); - + testUtils.testWithClient('connect, ready, end', async client => { await Promise.all([ - client.connect(), once(client, 'connect'), - once(client, 'ready') + once(client, 'ready'), + client.connect() ]); await Promise.all([ - client.disconnect(), - once(client, 'end') + once(client, 'end'), + client.disconnect() ]); + }, { + ...GLOBAL.SERVERS.OPEN, + disableClientSetup: true }); }); describe('sendCommand', () => { - itWithClient(TestRedisServers.OPEN, 'PING', async client => { + testUtils.testWithClient('PING', async client => { assert.equal(await client.sendCommand(['PING']), 'PONG'); - }); + }, GLOBAL.SERVERS.OPEN); - itWithClient(TestRedisServers.OPEN, 'bufferMode', async client => { + testUtils.testWithClient('bufferMode', async client => { assert.deepEqual( await client.sendCommand(['PING'], undefined, true), Buffer.from('PONG') ); - }); + }, GLOBAL.SERVERS.OPEN); describe('AbortController', () => { before(function () { @@ -298,13 +320,13 @@ describe('Client', () => { } }); - itWithClient(TestRedisServers.OPEN, 'success', async client => { + testUtils.testWithClient('success', async client => { await client.sendCommand(['PING'], { signal: new AbortController().signal }); - }); + }, GLOBAL.SERVERS.OPEN); - itWithClient(TestRedisServers.OPEN, 'AbortError', client => { + testUtils.testWithClient('AbortError', client => { const controller = new AbortController(); controller.abort(); @@ -314,12 +336,12 @@ describe('Client', () => { }), AbortError ); - }); + }, GLOBAL.SERVERS.OPEN); }); }); describe('multi', () => { - itWithClient(TestRedisServers.OPEN, 'simple', async client => { + testUtils.testWithClient('simple', async client => { assert.deepEqual( await client.multi() .ping() @@ -328,23 +350,19 @@ describe('Client', () => { .exec(), ['PONG', 'OK', 'value'] ); - }); - - itWithClient(TestRedisServers.OPEN, 'should reject the whole chain on error', client => { - client.on('error', () => { - // ignore errors - }); + }, GLOBAL.SERVERS.OPEN); + testUtils.testWithClient('should reject the whole chain on error', client => { return assert.rejects( client.multi() .ping() - .addCommand(['DEBUG', 'RESTART']) + .addCommand(['INVALID COMMAND']) .ping() .exec() ); - }); + }, GLOBAL.SERVERS.OPEN); - itWithClient(TestRedisServers.OPEN, 'with script', async client => { + testUtils.testWithClient('with script', async client => { assert.deepEqual( await client.multi() .square(2) @@ -352,6 +370,7 @@ describe('Client', () => { [4] ); }, { + ...GLOBAL.SERVERS.OPEN, clientOptions: { scripts: { square: SQUARE_SCRIPT @@ -359,7 +378,7 @@ describe('Client', () => { } }); - itWithClient(TestRedisServers.OPEN, 'WatchError', async client => { + testUtils.testWithClient('WatchError', async client => { await client.watch('key'); await client.set( @@ -376,24 +395,25 @@ describe('Client', () => { .exec(), WatchError ); - }); + }, GLOBAL.SERVERS.OPEN); - itWithClient(TestRedisServers.OPEN, 'execAsPipeline', async client => { + testUtils.testWithClient('execAsPipeline', async client => { assert.deepEqual( await client.multi() .ping() .exec(true), ['PONG'] ); - }); + }, GLOBAL.SERVERS.OPEN); }); - itWithClient(TestRedisServers.OPEN, 'scripts', async client => { + testUtils.testWithClient('scripts', async client => { assert.equal( await client.square(2), 4 ); }, { + ...GLOBAL.SERVERS.OPEN, clientOptions: { scripts: { square: SQUARE_SCRIPT @@ -401,12 +421,13 @@ describe('Client', () => { } }); - itWithClient(TestRedisServers.OPEN, 'modules', async client => { + testUtils.testWithClient('modules', async client => { assert.equal( await client.module.echo('message'), 'message' ); }, { + ...GLOBAL.SERVERS.OPEN, clientOptions: { modules: { module: { @@ -423,7 +444,7 @@ describe('Client', () => { } }); - itWithClient(TestRedisServers.OPEN, 'executeIsolated', async client => { + testUtils.testWithClient('executeIsolated', async client => { await client.sendCommand(['CLIENT', 'SETNAME', 'client']); assert.equal( @@ -432,35 +453,35 @@ describe('Client', () => { ), null ); - }); - - itWithClient(TestRedisServers.OPEN, 'should reconnect after DEBUG RESTART', async client => { - client.on('error', () => { - // ignore errors - }); - - await client.sendCommand(['CLIENT', 'SETNAME', 'client']); - await assert.rejects(client.sendCommand(['DEBUG', 'RESTART'])); - assert.ok(await client.sendCommand(['CLIENT', 'GETNAME']) === null); - }); + }, GLOBAL.SERVERS.OPEN); + + async function killClient(client: RedisClientType): Promise { + const onceErrorPromise = once(client, 'error'); + await client.sendCommand(['QUIT']); + await Promise.all([ + onceErrorPromise, + assert.rejects(client.ping(), SocketClosedUnexpectedlyError) + ]); + } - itWithClient(TestRedisServers.OPEN, 'should SELECT db after reconnection', async client => { - client.on('error', () => { - // ignore errors - }); + testUtils.testWithClient('should reconnect when socket disconnects', async client => { + await killClient(client); + await assert.doesNotReject(client.ping()); + }, GLOBAL.SERVERS.OPEN); + testUtils.testWithClient('should remember selected db', async client => { await client.select(1); - await assert.rejects(client.sendCommand(['DEBUG', 'RESTART'])); + await killClient(client); assert.equal( (await client.clientInfo()).db, 1 ); }, { - // because of CLIENT INFO - minimumRedisVersion: [6, 2] + ...GLOBAL.SERVERS.OPEN, + minimumDockerVersion: [6, 2] // CLIENT INFO }); - itWithClient(TestRedisServers.OPEN, 'scanIterator', async client => { + testUtils.testWithClient('scanIterator', async client => { const promises = [], keys = new Set(); for (let i = 0; i < 100; i++) { @@ -477,9 +498,9 @@ describe('Client', () => { } assert.deepEqual(keys, results); - }); + }, GLOBAL.SERVERS.OPEN); - itWithClient(TestRedisServers.OPEN, 'hScanIterator', async client => { + testUtils.testWithClient('hScanIterator', async client => { const hash: Record = {}; for (let i = 0; i < 100; i++) { hash[i.toString()] = i.toString(); @@ -493,9 +514,9 @@ describe('Client', () => { } assert.deepEqual(hash, results); - }); + }, GLOBAL.SERVERS.OPEN); - itWithClient(TestRedisServers.OPEN, 'sScanIterator', async client => { + testUtils.testWithClient('sScanIterator', async client => { const members = new Set(); for (let i = 0; i < 100; i++) { members.add(i.toString()); @@ -509,9 +530,9 @@ describe('Client', () => { } assert.deepEqual(members, results); - }); + }, GLOBAL.SERVERS.OPEN); - itWithClient(TestRedisServers.OPEN, 'zScanIterator', async client => { + testUtils.testWithClient('zScanIterator', async client => { const members = []; for (let i = 0; i < 100; i++) { members.push({ @@ -537,9 +558,9 @@ describe('Client', () => { [...map.entries()].sort(sort), members.map(member => [member.value, member.score]).sort(sort) ); - }); + }, GLOBAL.SERVERS.OPEN); - itWithClient(TestRedisServers.OPEN, 'PubSub', async publisher => { + testUtils.testWithClient('PubSub', async publisher => { const subscriber = publisher.duplicate(); await subscriber.connect(); @@ -602,76 +623,59 @@ describe('Client', () => { } finally { await subscriber.disconnect(); } - }); + }, GLOBAL.SERVERS.OPEN); - it('ConnectionTimeoutError', async () => { - const client = RedisClient.create({ - socket: { - ...TEST_REDIS_SERVERS[TestRedisServers.OPEN], - connectTimeout: 1 - } - }); - - try { - const promise = assert.rejects(client.connect(), ConnectionTimeoutError), - start = process.hrtime.bigint(); + testUtils.testWithClient('ConnectionTimeoutError', async client => { + const promise = assert.rejects(client.connect(), ConnectionTimeoutError), + start = process.hrtime.bigint(); - while (process.hrtime.bigint() - start < 1_000_000) { - // block the event loop for 1ms, to make sure the connection will timeout - } + while (process.hrtime.bigint() - start < 1_000_000) { + // block the event loop for 1ms, to make sure the connection will timeout + } - await promise; - } catch (err) { - if (err instanceof AssertionError) { - await client.disconnect(); + await promise; + }, { + ...GLOBAL.SERVERS.OPEN, + clientOptions: { + socket: { + connectTimeout: 1 } - - throw err; - } + }, + disableClientSetup: true }); - it('client.quit', async () => { - const client = RedisClient.create(TEST_REDIS_SERVERS[TestRedisServers.OPEN]); - + testUtils.testWithClient('client.quit', async client => { await client.connect(); - try { - const pingPromise = client.ping(), - quitPromise = client.quit(); - assert.equal(client.isOpen, false); + const pingPromise = client.ping(), + quitPromise = client.quit(); + assert.equal(client.isOpen, false); - const [ping] = await Promise.all([ - pingPromise, - assert.doesNotReject(quitPromise), - assert.rejects(client.ping(), ClientClosedError) - ]); + const [ping] = await Promise.all([ + pingPromise, + assert.doesNotReject(quitPromise), + assert.rejects(client.ping(), ClientClosedError) + ]); - assert.equal(ping, 'PONG'); - } finally { - if (client.isOpen) { - await client.disconnect(); - } - } + assert.equal(ping, 'PONG'); + }, { + ...GLOBAL.SERVERS.OPEN, + disableClientSetup: true }); - it('client.disconnect', async () => { - const client = RedisClient.create(TEST_REDIS_SERVERS[TestRedisServers.OPEN]); - + testUtils.testWithClient('client.disconnect', async client => { await client.connect(); - try { - const pingPromise = client.ping(), - disconnectPromise = client.disconnect(); - assert.equal(client.isOpen, false); - await Promise.all([ - assert.rejects(pingPromise, DisconnectsClientError), - assert.doesNotReject(disconnectPromise), - assert.rejects(client.ping(), ClientClosedError) - ]); - } finally { - if (client.isOpen) { - await client.disconnect(); - } - } + const pingPromise = client.ping(), + disconnectPromise = client.disconnect(); + assert.equal(client.isOpen, false); + await Promise.all([ + assert.rejects(pingPromise, DisconnectsClientError), + assert.doesNotReject(disconnectPromise), + assert.rejects(client.ping(), ClientClosedError) + ]); + }, { + ...GLOBAL.SERVERS.OPEN, + disableClientSetup: true }); }); diff --git a/lib/client/index.ts b/lib/client/index.ts index a76a56ace79..40499cbf7c0 100644 --- a/lib/client/index.ts +++ b/lib/client/index.ts @@ -9,7 +9,7 @@ import { CommandOptions, commandOptions, isCommandOptions } from '../command-opt import { ScanOptions, ZMember } from '../commands/generic-transformers'; import { ScanCommandOptions } from '../commands/SCAN'; import { HScanTuple } from '../commands/HSCAN'; -import { encodeCommand, extendWithCommands, extendWithModulesAndScripts, transformCommandArguments, transformCommandReply } from '../commander'; +import { extendWithCommands, extendWithModulesAndScripts, transformCommandArguments, transformCommandReply } from '../commander'; import { Pool, Options as PoolOptions, createPool } from 'generic-pool'; import { ClientClosedError, DisconnectsClientError } from '../errors'; import { URL } from 'url'; @@ -217,7 +217,7 @@ export default class RedisClient } if (promises.length) { - this.#tick(); + this.#tick(true); await Promise.all(promises); } }; @@ -435,8 +435,8 @@ export default class RedisClient quit = this.QUIT; - #tick(): void { - if (!this.#socket.isSocketExists || this.#socket.writableNeedDrain) { + #tick(force = false): void { + if (this.#socket.writableNeedDrain || (!force && !this.#socket.isReady)) { return; } @@ -446,9 +446,7 @@ export default class RedisClient const args = this.#queue.getCommandToSend(); if (args === undefined) break; - for (const toWrite of encodeCommand(args)) { - this.#socket.write(toWrite); - } + this.#socket.writeCommand(args); } } @@ -485,7 +483,7 @@ export default class RedisClient for (const key of reply.keys) { yield key; } - } while (cursor !== 0) + } while (cursor !== 0); } async* hScanIterator(key: string, options?: ScanOptions): AsyncIterable { @@ -496,7 +494,7 @@ export default class RedisClient for (const tuple of reply.tuples) { yield tuple; } - } while (cursor !== 0) + } while (cursor !== 0); } async* sScanIterator(key: string, options?: ScanOptions): AsyncIterable { @@ -507,7 +505,7 @@ export default class RedisClient for (const member of reply.members) { yield member; } - } while (cursor !== 0) + } while (cursor !== 0); } async* zScanIterator(key: string, options?: ScanOptions): AsyncIterable { @@ -518,15 +516,13 @@ export default class RedisClient for (const member of reply.members) { yield member; } - } while (cursor !== 0) + } while (cursor !== 0); } async disconnect(): Promise { this.#queue.flushAll(new DisconnectsClientError()); - await Promise.all([ - this.#socket.disconnect(), - this.#destroyIsolationPool() - ]); + this.#socket.disconnect(); + await this.#destroyIsolationPool(); } async #destroyIsolationPool(): Promise { diff --git a/lib/client/socket.spec.ts b/lib/client/socket.spec.ts index 11c02d0885c..263320bbf72 100644 --- a/lib/client/socket.spec.ts +++ b/lib/client/socket.spec.ts @@ -33,6 +33,6 @@ describe('Socket', () => { return assert.rejects(socket.connect(), { message: '50' }); - }) + }); }); }); diff --git a/lib/client/socket.ts b/lib/client/socket.ts index 923d14dffd5..3a816604718 100644 --- a/lib/client/socket.ts +++ b/lib/client/socket.ts @@ -1,7 +1,9 @@ import EventEmitter from 'events'; import net from 'net'; import tls from 'tls'; -import { ConnectionTimeoutError, ClientClosedError } from '../errors'; +import { encodeCommand } from '../commander'; +import { RedisCommandArguments } from '../commands'; +import { ConnectionTimeoutError, ClientClosedError, SocketClosedUnexpectedlyError } from '../errors'; import { promiseTimeout } from '../utils'; export interface RedisSocketCommonOptions { @@ -72,8 +74,10 @@ export default class RedisSocket extends EventEmitter { return this.#isOpen; } - get isSocketExists(): boolean { - return !!this.#socket; + #isReady = false; + + get isReady(): boolean { + return this.#isReady; } // `writable.writableNeedDrain` was added in v15.2.0 and therefore can't be used @@ -93,33 +97,39 @@ export default class RedisSocket extends EventEmitter { async connect(): Promise { if (this.#isOpen) { - throw new Error('Socket is connection/connecting'); + throw new Error('Socket already opened'); } - this.#isOpen = true; - - try { - await this.#connect(); - } catch (err) { - this.#isOpen = false; - throw err; - } + return this.#connect(); } async #connect(hadError?: boolean): Promise { + this.#isOpen = true; this.#socket = await this.#retryConnection(0, hadError); + this.#writableNeedDrain = false; + + if (!this.#isOpen) { + this.disconnect(); + return; + } + this.emit('connect'); if (this.#initiator) { try { await this.#initiator(); } catch (err) { - this.#socket.end(); + this.#socket.destroy(); this.#socket = undefined; + this.#isOpen = false; throw err; } + + if (!this.#isOpen) return; } + this.#isReady = true; + this.emit('ready'); } @@ -168,7 +178,7 @@ export default class RedisSocket extends EventEmitter { .once('error', (err: Error) => this.#onSocketError(err)) .once('close', hadError => { if (!hadError && this.#isOpen) { - this.#onSocketError(new Error('Socket closed unexpectedly')); + this.#onSocketError(new SocketClosedUnexpectedlyError()); } }) .on('drain', () => { @@ -197,33 +207,32 @@ export default class RedisSocket extends EventEmitter { } #onSocketError(err: Error): void { - this.#socket = undefined; + this.#isReady = false; this.emit('error', err); - this.#connect(true) - .catch(err => this.emit('error', err)); + this.#connect(true).catch(() => { + // the error was already emitted, silently ignore it + }); } - write(toWrite: string | Buffer): boolean { + writeCommand(args: RedisCommandArguments): void { if (!this.#socket) { throw new ClientClosedError(); } - const wasFullyWritten = this.#socket.write(toWrite); - this.#writableNeedDrain = !wasFullyWritten; - return wasFullyWritten; + for (const toWrite of encodeCommand(args)) { + this.#writableNeedDrain = !this.#socket.write(toWrite); + } } - async disconnect(ignoreIsOpen = false): Promise { - if ((!ignoreIsOpen && !this.#isOpen) || !this.#socket) { + disconnect(): void { + if (!this.#socket) { throw new ClientClosedError(); } else { - this.#isOpen = false; + this.#isOpen = this.#isReady = false; } - this.#socket.end(); - this.#socket.removeAllListeners('data'); - await EventEmitter.once(this.#socket, 'end'); + this.#socket.destroy(); this.#socket = undefined; this.emit('end'); } @@ -234,14 +243,8 @@ export default class RedisSocket extends EventEmitter { } this.#isOpen = false; - - try { - await fn(); - await this.disconnect(true); - } catch (err) { - this.#isOpen = true; - throw err; - } + await fn(); + this.disconnect(); } #isCorked = false; diff --git a/lib/cluster/index.spec.ts b/lib/cluster/index.spec.ts index f2227395385..66319460f63 100644 --- a/lib/cluster/index.spec.ts +++ b/lib/cluster/index.spec.ts @@ -1,17 +1,11 @@ import { strict as assert } from 'assert'; -import RedisCluster from '.'; -import { defineScript } from '../lua-script'; -import { itWithCluster, itWithDedicatedCluster, TestRedisClusters, TEST_REDIS_CLUSTERES } from '../test-utils'; +import testUtils, { GLOBAL } from '../test-utils'; import calculateSlot from 'cluster-key-slot'; import { ClusterSlotStates } from '../commands/CLUSTER_SETSLOT'; +import { SQUARE_SCRIPT } from '../client/index.spec'; describe('Cluster', () => { - it('sendCommand', async () => { - const cluster = RedisCluster.create({ - ...TEST_REDIS_CLUSTERES[TestRedisClusters.OPEN], - useReplicas: true - }); - + testUtils.testWithCluster('sendCommand', async cluster => { await cluster.connect(); try { @@ -26,9 +20,9 @@ describe('Cluster', () => { } finally { await cluster.disconnect(); } - }); + }, GLOBAL.CLUSTERS.OPEN); - itWithCluster(TestRedisClusters.OPEN, 'multi', async cluster => { + testUtils.testWithCluster('multi', async cluster => { const key = 'key'; assert.deepEqual( await cluster.multi() @@ -37,40 +31,23 @@ describe('Cluster', () => { .exec(), ['OK', 'value'] ); - }); + }, GLOBAL.CLUSTERS.OPEN); - it('scripts', async () => { - const cluster = RedisCluster.create({ - ...TEST_REDIS_CLUSTERES[TestRedisClusters.OPEN], + testUtils.testWithCluster('scripts', async cluster => { + assert.equal( + await cluster.square(2), + 4 + ); + }, { + ...GLOBAL.CLUSTERS.OPEN, + clusterConfiguration: { scripts: { - add: defineScript({ - NUMBER_OF_KEYS: 0, - SCRIPT: 'return ARGV[1] + 1;', - transformArguments(number: number): Array { - assert.equal(number, 1); - return [number.toString()]; - }, - transformReply(reply: number): number { - assert.equal(reply, 2); - return reply; - } - }) + square: SQUARE_SCRIPT } - }); - - await cluster.connect(); - - try { - assert.equal( - await cluster.add(1), - 2 - ); - } finally { - await cluster.disconnect(); } }); - itWithDedicatedCluster('should handle live resharding', async cluster => { + testUtils.testWithCluster('should handle live resharding', async cluster => { const key = 'key', value = 'value'; await cluster.set(key, value); @@ -110,5 +87,7 @@ describe('Cluster', () => { await cluster.get(key), value ); + }, { + serverArguments: [] }); }); diff --git a/lib/commands/ACL_CAT.spec.ts b/lib/commands/ACL_CAT.spec.ts index 77ed1cb7a07..521871a1c6b 100644 --- a/lib/commands/ACL_CAT.spec.ts +++ b/lib/commands/ACL_CAT.spec.ts @@ -1,9 +1,9 @@ import { strict as assert } from 'assert'; -import { describeHandleMinimumRedisVersion } from '../test-utils'; +import testUtils from '../test-utils'; import { transformArguments } from './ACL_CAT'; describe('ACL CAT', () => { - describeHandleMinimumRedisVersion([6]); + testUtils.isVersionGreaterThanHook([6]); describe('transformArguments', () => { it('simple', () => { diff --git a/lib/commands/ACL_DELUSER.spec.ts b/lib/commands/ACL_DELUSER.spec.ts index c64e8db1965..5c5ea2fa2a3 100644 --- a/lib/commands/ACL_DELUSER.spec.ts +++ b/lib/commands/ACL_DELUSER.spec.ts @@ -1,9 +1,9 @@ import { strict as assert } from 'assert'; -import { describeHandleMinimumRedisVersion, itWithClient, TestRedisServers } from '../test-utils'; +import testUtils, { GLOBAL } from '../test-utils'; import { transformArguments } from './ACL_DELUSER'; describe('ACL DELUSER', () => { - describeHandleMinimumRedisVersion([6]); + testUtils.isVersionGreaterThanHook([6]); describe('transformArguments', () => { it('string', () => { @@ -21,10 +21,10 @@ describe('ACL DELUSER', () => { }); }); - itWithClient(TestRedisServers.OPEN, 'client.aclDelUser', async client => { + testUtils.testWithClient('client.aclDelUser', async client => { assert.equal( await client.aclDelUser('dosenotexists'), 0 ); - }); + }, GLOBAL.SERVERS.OPEN); }); diff --git a/lib/commands/ACL_GENPASS.spec.ts b/lib/commands/ACL_GENPASS.spec.ts index a288a4f7142..3b2a022f972 100644 --- a/lib/commands/ACL_GENPASS.spec.ts +++ b/lib/commands/ACL_GENPASS.spec.ts @@ -1,9 +1,9 @@ import { strict as assert } from 'assert'; -import { describeHandleMinimumRedisVersion } from '../test-utils'; +import testUtils from '../test-utils'; import { transformArguments } from './ACL_GENPASS'; describe('ACL GENPASS', () => { - describeHandleMinimumRedisVersion([6]); + testUtils.isVersionGreaterThanHook([6]); describe('transformArguments', () => { it('simple', () => { diff --git a/lib/commands/ACL_GETUSER.spec.ts b/lib/commands/ACL_GETUSER.spec.ts index e3446162da9..fcc10768e61 100644 --- a/lib/commands/ACL_GETUSER.spec.ts +++ b/lib/commands/ACL_GETUSER.spec.ts @@ -1,9 +1,9 @@ import { strict as assert } from 'assert'; -import { describeHandleMinimumRedisVersion, isRedisVersionGreaterThan, itWithClient, TestRedisServers } from '../test-utils'; +import testUtils, { GLOBAL } from '../test-utils'; import { transformArguments } from './ACL_GETUSER'; describe('ACL GETUSER', () => { - describeHandleMinimumRedisVersion([6]); + testUtils.isVersionGreaterThanHook([6]); it('transformArguments', () => { assert.deepEqual( @@ -12,14 +12,14 @@ describe('ACL GETUSER', () => { ); }); - itWithClient(TestRedisServers.OPEN, 'client.aclGetUser', async client => { + testUtils.testWithClient('client.aclGetUser', async client => { assert.deepEqual( await client.aclGetUser('default'), { passwords: [], commands: '+@all', keys: ['*'], - ...(isRedisVersionGreaterThan([6, 2]) ? { + ...(testUtils.isVersionGreaterThan([6, 2]) ? { flags: ['on', 'allkeys', 'allchannels', 'allcommands', 'nopass'], channels: ['*'] } : { @@ -28,5 +28,5 @@ describe('ACL GETUSER', () => { }) } ); - }); + }, GLOBAL.SERVERS.OPEN); }); diff --git a/lib/commands/ACL_LIST.spec.ts b/lib/commands/ACL_LIST.spec.ts index ab6bae762f1..9f9156db7a2 100644 --- a/lib/commands/ACL_LIST.spec.ts +++ b/lib/commands/ACL_LIST.spec.ts @@ -1,9 +1,9 @@ import { strict as assert } from 'assert'; -import { describeHandleMinimumRedisVersion } from '../test-utils'; +import testUtils from '../test-utils'; import { transformArguments } from './ACL_LIST'; describe('ACL LIST', () => { - describeHandleMinimumRedisVersion([6]); + testUtils.isVersionGreaterThanHook([6]); it('transformArguments', () => { assert.deepEqual( diff --git a/lib/commands/ACL_LOAD.spec.ts b/lib/commands/ACL_LOAD.spec.ts index d173d7f1355..703d5eeb252 100644 --- a/lib/commands/ACL_LOAD.spec.ts +++ b/lib/commands/ACL_LOAD.spec.ts @@ -1,9 +1,9 @@ import { strict as assert } from 'assert'; -import { describeHandleMinimumRedisVersion } from '../test-utils'; +import testUtils from '../test-utils'; import { transformArguments } from './ACL_SAVE'; describe('ACL SAVE', () => { - describeHandleMinimumRedisVersion([6]); + testUtils.isVersionGreaterThanHook([6]); it('transformArguments', () => { assert.deepEqual( diff --git a/lib/commands/ACL_LOG.spec.ts b/lib/commands/ACL_LOG.spec.ts index 3ce76ce4563..a8296d31da6 100644 --- a/lib/commands/ACL_LOG.spec.ts +++ b/lib/commands/ACL_LOG.spec.ts @@ -1,9 +1,9 @@ import { strict as assert } from 'assert'; -import { describeHandleMinimumRedisVersion } from '../test-utils'; +import testUtils from '../test-utils'; import { transformArguments, transformReply } from './ACL_LOG'; describe('ACL LOG', () => { - describeHandleMinimumRedisVersion([6]); + testUtils.isVersionGreaterThanHook([6]); describe('transformArguments', () => { it('simple', () => { diff --git a/lib/commands/ACL_LOG_RESET.spec.ts b/lib/commands/ACL_LOG_RESET.spec.ts index 3f0e628d9f0..5d26e45d04f 100644 --- a/lib/commands/ACL_LOG_RESET.spec.ts +++ b/lib/commands/ACL_LOG_RESET.spec.ts @@ -1,9 +1,9 @@ import { strict as assert } from 'assert'; -import { describeHandleMinimumRedisVersion } from '../test-utils'; +import testUtils from '../test-utils'; import { transformArguments } from './ACL_LOG_RESET'; describe('ACL LOG RESET', () => { - describeHandleMinimumRedisVersion([6]); + testUtils.isVersionGreaterThanHook([6]); it('transformArguments', () => { assert.deepEqual( diff --git a/lib/commands/ACL_SAVE.spec.ts b/lib/commands/ACL_SAVE.spec.ts index b34c7bb0e6f..f4de312bb7a 100644 --- a/lib/commands/ACL_SAVE.spec.ts +++ b/lib/commands/ACL_SAVE.spec.ts @@ -1,9 +1,9 @@ import { strict as assert } from 'assert'; -import { describeHandleMinimumRedisVersion } from '../test-utils'; +import testUtils from '../test-utils'; import { transformArguments } from './ACL_LOAD'; describe('ACL LOAD', () => { - describeHandleMinimumRedisVersion([6]); + testUtils.isVersionGreaterThanHook([6]); it('transformArguments', () => { assert.deepEqual( diff --git a/lib/commands/ACL_SETUSER.spec.ts b/lib/commands/ACL_SETUSER.spec.ts index f3badfcdca8..9c8ea8a59e0 100644 --- a/lib/commands/ACL_SETUSER.spec.ts +++ b/lib/commands/ACL_SETUSER.spec.ts @@ -1,9 +1,9 @@ import { strict as assert } from 'assert'; -import { describeHandleMinimumRedisVersion } from '../test-utils'; +import testUtils from '../test-utils'; import { transformArguments } from './ACL_SETUSER'; describe('ACL SETUSER', () => { - describeHandleMinimumRedisVersion([6]); + testUtils.isVersionGreaterThanHook([6]); describe('transformArguments', () => { it('string', () => { diff --git a/lib/commands/ACL_USERS.spec.ts b/lib/commands/ACL_USERS.spec.ts index 14b76725fcd..35e06ce8494 100644 --- a/lib/commands/ACL_USERS.spec.ts +++ b/lib/commands/ACL_USERS.spec.ts @@ -1,9 +1,9 @@ import { strict as assert } from 'assert'; -import { describeHandleMinimumRedisVersion } from '../test-utils'; +import testUtils from '../test-utils'; import { transformArguments } from './ACL_USERS'; describe('ACL USERS', () => { - describeHandleMinimumRedisVersion([6]); + testUtils.isVersionGreaterThanHook([6]); it('transformArguments', () => { assert.deepEqual( diff --git a/lib/commands/ACL_WHOAMI.spec.ts b/lib/commands/ACL_WHOAMI.spec.ts index a933057ea9d..32eb327beea 100644 --- a/lib/commands/ACL_WHOAMI.spec.ts +++ b/lib/commands/ACL_WHOAMI.spec.ts @@ -1,9 +1,9 @@ import { strict as assert } from 'assert'; -import { describeHandleMinimumRedisVersion } from '../test-utils'; +import testUtils from '../test-utils'; import { transformArguments } from './ACL_WHOAMI'; describe('ACL WHOAMI', () => { - describeHandleMinimumRedisVersion([6]); + testUtils.isVersionGreaterThanHook([6]); it('transformArguments', () => { assert.deepEqual( diff --git a/lib/commands/APPEND.spec.ts b/lib/commands/APPEND.spec.ts index 283ab807956..23353866843 100644 --- a/lib/commands/APPEND.spec.ts +++ b/lib/commands/APPEND.spec.ts @@ -1,7 +1,7 @@ import { strict as assert } from 'assert'; import { transformArguments } from './APPEND'; -describe('AUTH', () => { +describe('APPEND', () => { it('transformArguments', () => { assert.deepEqual( transformArguments('key', 'value'), diff --git a/lib/commands/BITCOUNT.spec.ts b/lib/commands/BITCOUNT.spec.ts index bf4cf39cab6..8919957cf92 100644 --- a/lib/commands/BITCOUNT.spec.ts +++ b/lib/commands/BITCOUNT.spec.ts @@ -1,5 +1,5 @@ import { strict as assert } from 'assert'; -import { TestRedisServers, itWithClient } from '../test-utils'; +import testUtils, { GLOBAL } from '../test-utils'; import { transformArguments } from './BITCOUNT'; describe('BITCOUNT', () => { @@ -22,10 +22,10 @@ describe('BITCOUNT', () => { }); }); - itWithClient(TestRedisServers.OPEN, 'client.bitCount', async client => { + testUtils.testWithClient('client.bitCount', async client => { assert.equal( await client.bitCount('key'), 0 ); - }); + }, GLOBAL.SERVERS.OPEN); }); diff --git a/lib/commands/BITFIELD.spec.ts b/lib/commands/BITFIELD.spec.ts index 4d6d9d11c1a..93a5cb08a63 100644 --- a/lib/commands/BITFIELD.spec.ts +++ b/lib/commands/BITFIELD.spec.ts @@ -1,5 +1,5 @@ import { strict as assert } from 'assert'; -import { TestRedisServers, itWithClient } from '../test-utils'; +import testUtils, { GLOBAL } from '../test-utils'; import { transformArguments } from './BITFIELD'; describe('BITFIELD', () => { @@ -33,10 +33,10 @@ describe('BITFIELD', () => { ); }); - itWithClient(TestRedisServers.OPEN, 'client.bitField', async client => { + testUtils.testWithClient('client.bitField', async client => { assert.deepEqual( await client.bitField('key', []), [] ); - }); + }, GLOBAL.SERVERS.OPEN); }); diff --git a/lib/commands/BITOP.spec.ts b/lib/commands/BITOP.spec.ts index aa863e5f2d2..554530d56f4 100644 --- a/lib/commands/BITOP.spec.ts +++ b/lib/commands/BITOP.spec.ts @@ -1,5 +1,5 @@ import { strict as assert } from 'assert'; -import { TestRedisServers, itWithClient, TestRedisClusters, itWithCluster } from '../test-utils'; +import testUtils, { GLOBAL } from '../test-utils'; import { transformArguments } from './BITOP'; describe('BITOP', () => { @@ -19,17 +19,17 @@ describe('BITOP', () => { }); }); - itWithClient(TestRedisServers.OPEN, 'client.bitOp', async client => { + testUtils.testWithClient('client.bitOp', async client => { assert.equal( await client.bitOp('AND', 'destKey', 'key'), 0 ); - }); + }, GLOBAL.SERVERS.OPEN); - itWithCluster(TestRedisClusters.OPEN, 'cluster.bitOp', async cluster => { + testUtils.testWithCluster('cluster.bitOp', async cluster => { assert.equal( await cluster.bitOp('AND', '{tag}destKey', '{tag}key'), 0 ); - }); + }, GLOBAL.CLUSTERS.OPEN); }); diff --git a/lib/commands/BITPOS.spec.ts b/lib/commands/BITPOS.spec.ts index ad08e708c54..354deea6195 100644 --- a/lib/commands/BITPOS.spec.ts +++ b/lib/commands/BITPOS.spec.ts @@ -1,5 +1,5 @@ import { strict as assert } from 'assert'; -import { TestRedisServers, itWithClient, TestRedisClusters, itWithCluster } from '../test-utils'; +import testUtils, { GLOBAL } from '../test-utils'; import { transformArguments } from './BITPOS'; describe('BITPOS', () => { @@ -26,17 +26,17 @@ describe('BITPOS', () => { }); }); - itWithClient(TestRedisServers.OPEN, 'client.bitPos', async client => { + testUtils.testWithClient('client.bitPos', async client => { assert.equal( await client.bitPos('key', 1, 1), -1 ); - }); + }, GLOBAL.SERVERS.OPEN); - itWithCluster(TestRedisClusters.OPEN, 'cluster.bitPos', async cluster => { + testUtils.testWithCluster('cluster.bitPos', async cluster => { assert.equal( await cluster.bitPos('key', 1, 1), -1 ); - }); + }, GLOBAL.CLUSTERS.OPEN); }); diff --git a/lib/commands/BLMOVE.spec.ts b/lib/commands/BLMOVE.spec.ts index b942864758f..3b86c1ec91e 100644 --- a/lib/commands/BLMOVE.spec.ts +++ b/lib/commands/BLMOVE.spec.ts @@ -1,10 +1,10 @@ import { strict as assert } from 'assert'; -import { TestRedisServers, itWithClient, itWithCluster, TestRedisClusters, describeHandleMinimumRedisVersion } from '../test-utils'; +import testUtils, { GLOBAL } from '../test-utils'; import { transformArguments } from './BLMOVE'; import { commandOptions } from '../../index'; describe('BLMOVE', () => { - describeHandleMinimumRedisVersion([6, 2]); + testUtils.isVersionGreaterThanHook([6, 2]); it('transformArguments', () => { assert.deepEqual( @@ -13,7 +13,7 @@ describe('BLMOVE', () => { ); }); - itWithClient(TestRedisServers.OPEN, 'client.blMove', async client => { + testUtils.testWithClient('client.blMove', async client => { const [blMoveReply] = await Promise.all([ client.blMove(commandOptions({ isolated: true @@ -25,9 +25,9 @@ describe('BLMOVE', () => { blMoveReply, 'element' ); - }); + }, GLOBAL.SERVERS.OPEN); - itWithCluster(TestRedisClusters.OPEN, 'cluster.blMove', async cluster => { + testUtils.testWithCluster('cluster.blMove', async cluster => { const [blMoveReply] = await Promise.all([ cluster.blMove(commandOptions({ isolated: true @@ -39,5 +39,5 @@ describe('BLMOVE', () => { blMoveReply, 'element' ); - }); + }, GLOBAL.CLUSTERS.OPEN); }); diff --git a/lib/commands/BLPOP.spec.ts b/lib/commands/BLPOP.spec.ts index 651dd09eaf3..4b93c0b43b8 100644 --- a/lib/commands/BLPOP.spec.ts +++ b/lib/commands/BLPOP.spec.ts @@ -1,5 +1,5 @@ import { strict as assert } from 'assert'; -import { TestRedisServers, itWithClient, itWithCluster, TestRedisClusters } from '../test-utils'; +import testUtils, { GLOBAL } from '../test-utils'; import { transformArguments, transformReply } from './BLPOP'; import { commandOptions } from '../../index'; @@ -39,7 +39,7 @@ describe('BLPOP', () => { }); }); - itWithClient(TestRedisServers.OPEN, 'client.blPop', async client => { + testUtils.testWithClient('client.blPop', async client => { const [ blPopReply ] = await Promise.all([ client.blPop( commandOptions({ isolated: true }), @@ -56,9 +56,9 @@ describe('BLPOP', () => { element: 'element' } ); - }); + }, GLOBAL.SERVERS.OPEN); - itWithCluster(TestRedisClusters.OPEN, 'cluster.blPop', async cluster => { + testUtils.testWithCluster('cluster.blPop', async cluster => { const [ blPopReply ] = await Promise.all([ cluster.blPop( commandOptions({ isolated: true }), @@ -75,5 +75,5 @@ describe('BLPOP', () => { element: 'element' } ); - }); + }, GLOBAL.CLUSTERS.OPEN); }); diff --git a/lib/commands/BRPOP.spec.ts b/lib/commands/BRPOP.spec.ts index 9a7d0bbc37d..fc203e1abdf 100644 --- a/lib/commands/BRPOP.spec.ts +++ b/lib/commands/BRPOP.spec.ts @@ -1,5 +1,5 @@ import { strict as assert } from 'assert'; -import { TestRedisServers, itWithClient, itWithCluster, TestRedisClusters } from '../test-utils'; +import testUtils, { GLOBAL } from '../test-utils'; import { transformArguments, transformReply } from './BRPOP'; import { commandOptions } from '../../index'; @@ -39,7 +39,7 @@ describe('BRPOP', () => { }); }); - itWithClient(TestRedisServers.OPEN, 'client.brPop', async client => { + testUtils.testWithClient('client.brPop', async client => { const [ brPopReply ] = await Promise.all([ client.brPop( commandOptions({ isolated: true }), @@ -56,9 +56,9 @@ describe('BRPOP', () => { element: 'element' } ); - }); + }, GLOBAL.SERVERS.OPEN); - itWithCluster(TestRedisClusters.OPEN, 'cluster.brPop', async cluster => { + testUtils.testWithCluster('cluster.brPop', async cluster => { const [ brPopReply ] = await Promise.all([ cluster.brPop( commandOptions({ isolated: true }), @@ -75,5 +75,5 @@ describe('BRPOP', () => { element: 'element' } ); - }); + }, GLOBAL.CLUSTERS.OPEN); }); diff --git a/lib/commands/BRPOPLPUSH.spec.ts b/lib/commands/BRPOPLPUSH.spec.ts index 08bcf5e4d94..214af4553ac 100644 --- a/lib/commands/BRPOPLPUSH.spec.ts +++ b/lib/commands/BRPOPLPUSH.spec.ts @@ -1,5 +1,5 @@ import { strict as assert } from 'assert'; -import { TestRedisServers, itWithClient, itWithCluster, TestRedisClusters } from '../test-utils'; +import testUtils, { GLOBAL } from '../test-utils'; import { transformArguments } from './BRPOPLPUSH'; import { commandOptions } from '../../index'; @@ -11,7 +11,7 @@ describe('BRPOPLPUSH', () => { ); }); - itWithClient(TestRedisServers.OPEN, 'client.brPopLPush', async client => { + testUtils.testWithClient('client.brPopLPush', async client => { const [ popReply ] = await Promise.all([ client.brPopLPush( commandOptions({ isolated: true }), @@ -26,9 +26,9 @@ describe('BRPOPLPUSH', () => { popReply, 'element' ); - }); + }, GLOBAL.SERVERS.OPEN); - itWithCluster(TestRedisClusters.OPEN, 'cluster.brPopLPush', async cluster => { + testUtils.testWithCluster('cluster.brPopLPush', async cluster => { const [ popReply ] = await Promise.all([ cluster.brPopLPush( commandOptions({ isolated: true }), @@ -43,5 +43,5 @@ describe('BRPOPLPUSH', () => { popReply, 'element' ); - }); + }, GLOBAL.CLUSTERS.OPEN); }); diff --git a/lib/commands/BZPOPMAX.spec.ts b/lib/commands/BZPOPMAX.spec.ts index 090dfba096d..e1c37478469 100644 --- a/lib/commands/BZPOPMAX.spec.ts +++ b/lib/commands/BZPOPMAX.spec.ts @@ -1,5 +1,5 @@ import { strict as assert } from 'assert'; -import { TestRedisServers, itWithClient } from '../test-utils'; +import testUtils, { GLOBAL } from '../test-utils'; import { transformArguments, transformReply } from './BZPOPMAX'; import { commandOptions } from '../../index'; @@ -40,7 +40,7 @@ describe('BZPOPMAX', () => { }); }); - itWithClient(TestRedisServers.OPEN, 'client.bzPopMax', async client => { + testUtils.testWithClient('client.bzPopMax', async client => { const [ bzPopMaxReply ] = await Promise.all([ client.bzPopMax( commandOptions({ isolated: true }), @@ -61,5 +61,5 @@ describe('BZPOPMAX', () => { score: 1 } ); - }); + }, GLOBAL.SERVERS.OPEN); }); diff --git a/lib/commands/BZPOPMIN.spec.ts b/lib/commands/BZPOPMIN.spec.ts index 8b8977f9b3a..4cd1ec1b220 100644 --- a/lib/commands/BZPOPMIN.spec.ts +++ b/lib/commands/BZPOPMIN.spec.ts @@ -1,5 +1,5 @@ import { strict as assert } from 'assert'; -import { TestRedisServers, itWithClient } from '../test-utils'; +import testUtils, { GLOBAL } from '../test-utils'; import { transformArguments, transformReply } from './BZPOPMIN'; import { commandOptions } from '../../index'; @@ -40,7 +40,7 @@ describe('BZPOPMIN', () => { }); }); - itWithClient(TestRedisServers.OPEN, 'client.bzPopMin', async client => { + testUtils.testWithClient('client.bzPopMin', async client => { const [ bzPopMinReply ] = await Promise.all([ client.bzPopMin( commandOptions({ isolated: true }), @@ -61,5 +61,5 @@ describe('BZPOPMIN', () => { score: 1 } ); - }); + }, GLOBAL.SERVERS.OPEN); }); diff --git a/lib/commands/CLIENT_ID.spec.ts b/lib/commands/CLIENT_ID.spec.ts index cb7dfd9f730..6792a8c31be 100644 --- a/lib/commands/CLIENT_ID.spec.ts +++ b/lib/commands/CLIENT_ID.spec.ts @@ -1,5 +1,5 @@ import { strict as assert } from 'assert'; -import { TestRedisServers, itWithClient } from '../test-utils'; +import testUtils, { GLOBAL } from '../test-utils'; import { transformArguments } from './CLIENT_ID'; describe('CLIENT ID', () => { @@ -10,10 +10,10 @@ describe('CLIENT ID', () => { ); }); - itWithClient(TestRedisServers.OPEN, 'client.clientId', async client => { + testUtils.testWithClient('client.clientId', async client => { assert.equal( typeof (await client.clientId()), 'number' ); - }); + }, GLOBAL.SERVERS.OPEN); }); diff --git a/lib/commands/CLUSTER_SLOTS.spec.ts b/lib/commands/CLUSTER_SLOTS.spec.ts index ec6773bcdd4..6efbfe13ce1 100644 --- a/lib/commands/CLUSTER_SLOTS.spec.ts +++ b/lib/commands/CLUSTER_SLOTS.spec.ts @@ -71,6 +71,6 @@ describe('CLUSTER SLOTS', () => { id: '58e6e48d41228013e5d9c1c37c5060693925e97e' }] }] - ) + ); }); }); diff --git a/lib/commands/COMMAND.spec.ts b/lib/commands/COMMAND.spec.ts index 1f036dadc17..baad79845ab 100644 --- a/lib/commands/COMMAND.spec.ts +++ b/lib/commands/COMMAND.spec.ts @@ -1,7 +1,7 @@ import { strict as assert } from 'assert'; -import { itWithClient, TestRedisServers } from '../test-utils'; +import testUtils, { GLOBAL } from '../test-utils'; import { transformArguments } from './COMMAND'; -import { CommandCategories, CommandFlags } from './generic-transformers'; +import { assertPingCommand } from './COMMAND_INFO.spec'; describe('COMMAND', () => { it('transformArguments', () => { @@ -11,20 +11,7 @@ describe('COMMAND', () => { ); }); - itWithClient(TestRedisServers.OPEN, 'client.command', async client => { - assert.deepEqual( - (await client.command()).find(command => command.name === 'ping'), - { - name: 'ping', - arity: -1, - flags: new Set([CommandFlags.STALE, CommandFlags.FAST]), - firstKeyIndex: 0, - lastKeyIndex: 0, - step: 0, - categories: new Set([CommandCategories.FAST, CommandCategories.CONNECTION]) - } - ); - }, { - minimumRedisVersion: [6] - }); + testUtils.testWithClient('client.command', async client => { + assertPingCommand((await client.command()).find(command => command.name === 'ping')); + }, GLOBAL.SERVERS.OPEN); }); diff --git a/lib/commands/COMMAND_COUNT.spec.ts b/lib/commands/COMMAND_COUNT.spec.ts index 23e83c71cec..71482382f67 100644 --- a/lib/commands/COMMAND_COUNT.spec.ts +++ b/lib/commands/COMMAND_COUNT.spec.ts @@ -1,5 +1,5 @@ import { strict as assert } from 'assert'; -import { TestRedisServers, itWithClient } from '../test-utils'; +import testUtils, { GLOBAL } from '../test-utils'; import { transformArguments } from './COMMAND_COUNT'; describe('COMMAND COUNT', () => { @@ -10,10 +10,10 @@ describe('COMMAND COUNT', () => { ); }); - itWithClient(TestRedisServers.OPEN, 'client.commandCount', async client => { + testUtils.testWithClient('client.commandCount', async client => { assert.equal( typeof await client.commandCount(), 'number' ); - }); + }, GLOBAL.SERVERS.OPEN); }); diff --git a/lib/commands/COMMAND_GETKEYS.spec.ts b/lib/commands/COMMAND_GETKEYS.spec.ts index f2630db9afa..a92d032c5d6 100644 --- a/lib/commands/COMMAND_GETKEYS.spec.ts +++ b/lib/commands/COMMAND_GETKEYS.spec.ts @@ -1,5 +1,5 @@ import { strict as assert } from 'assert'; -import { TestRedisServers, itWithClient } from '../test-utils'; +import testUtils, { GLOBAL } from '../test-utils'; import { transformArguments } from './COMMAND_GETKEYS'; describe('COMMAND GETKEYS', () => { @@ -10,10 +10,10 @@ describe('COMMAND GETKEYS', () => { ); }); - itWithClient(TestRedisServers.OPEN, 'client.commandGetKeys', async client => { + testUtils.testWithClient('client.commandGetKeys', async client => { assert.deepEqual( await client.commandGetKeys(['GET', 'key']), ['key'] ); - }); + }, GLOBAL.SERVERS.OPEN); }); diff --git a/lib/commands/COMMAND_INFO.spec.ts b/lib/commands/COMMAND_INFO.spec.ts index 59a61f6680a..3b2672dcb6b 100644 --- a/lib/commands/COMMAND_INFO.spec.ts +++ b/lib/commands/COMMAND_INFO.spec.ts @@ -1,7 +1,26 @@ import { strict as assert } from 'assert'; -import { itWithClient, TestRedisServers } from '../test-utils'; +import testUtils, { GLOBAL } from '../test-utils'; import { transformArguments } from './COMMAND_INFO'; -import { CommandCategories, CommandFlags } from './generic-transformers'; +import { CommandCategories, CommandFlags, CommandReply } from './generic-transformers'; + +export function assertPingCommand(commandInfo: CommandReply | null | undefined): void { + assert.deepEqual( + commandInfo, + { + name: 'ping', + arity: -1, + flags: new Set([CommandFlags.STALE, CommandFlags.FAST]), + firstKeyIndex: 0, + lastKeyIndex: 0, + step: 0, + categories: new Set( + testUtils.isVersionGreaterThan([6]) ? + [CommandCategories.FAST, CommandCategories.CONNECTION] : + [] + ) + } + ); +} describe('COMMAND INFO', () => { it('transformArguments', () => { @@ -11,20 +30,7 @@ describe('COMMAND INFO', () => { ); }); - itWithClient(TestRedisServers.OPEN, 'client.commandInfo', async client => { - assert.deepEqual( - await client.commandInfo(['PING']), - [{ - name: 'ping', - arity: -1, - flags: new Set([CommandFlags.STALE, CommandFlags.FAST]), - firstKeyIndex: 0, - lastKeyIndex: 0, - step: 0, - categories: new Set([CommandCategories.FAST, CommandCategories.CONNECTION]) - }] - ); - }, { - minimumRedisVersion: [6] - }); + testUtils.testWithClient('client.commandInfo', async client => { + assertPingCommand((await client.commandInfo(['PING']))[0]); + }, GLOBAL.SERVERS.OPEN); }); diff --git a/lib/commands/COPY.spec.ts b/lib/commands/COPY.spec.ts index fb35be863ab..0d68e969cdb 100644 --- a/lib/commands/COPY.spec.ts +++ b/lib/commands/COPY.spec.ts @@ -1,9 +1,9 @@ import { strict as assert } from 'assert'; -import { TestRedisServers, itWithClient, describeHandleMinimumRedisVersion } from '../test-utils'; +import testUtils, { GLOBAL } from '../test-utils'; import { transformArguments, transformReply } from './COPY'; describe('COPY', () => { - describeHandleMinimumRedisVersion([6, 2]); + testUtils.isVersionGreaterThanHook([6, 2]); describe('transformArguments', () => { it('simple', () => { @@ -58,10 +58,10 @@ describe('COPY', () => { }); }); - itWithClient(TestRedisServers.OPEN, 'client.copy', async client => { + testUtils.testWithClient('client.copy', async client => { assert.equal( await client.copy('source', 'destination'), false ); - }); + }, GLOBAL.SERVERS.OPEN); }); diff --git a/lib/commands/DBSIZE.spec.ts b/lib/commands/DBSIZE.spec.ts index 36f591dbd29..a014a46e6e2 100644 --- a/lib/commands/DBSIZE.spec.ts +++ b/lib/commands/DBSIZE.spec.ts @@ -1,5 +1,5 @@ import { strict as assert } from 'assert'; -import { TestRedisServers, itWithClient } from '../test-utils'; +import testUtils, { GLOBAL } from '../test-utils'; import { transformArguments } from './DBSIZE'; describe('DBSIZE', () => { @@ -10,10 +10,10 @@ describe('DBSIZE', () => { ); }); - itWithClient(TestRedisServers.OPEN, 'client.dbSize', async client => { + testUtils.testWithClient('client.dbSize', async client => { assert.equal( await client.dbSize(), 0 ); - }); + }, GLOBAL.SERVERS.OPEN); }); diff --git a/lib/commands/DECR.spec.ts b/lib/commands/DECR.spec.ts index 5b4b4f0fd33..75e1205feda 100644 --- a/lib/commands/DECR.spec.ts +++ b/lib/commands/DECR.spec.ts @@ -1,5 +1,5 @@ import { strict as assert } from 'assert'; -import { TestRedisServers, itWithClient } from '../test-utils'; +import testUtils, { GLOBAL } from '../test-utils'; import { transformArguments } from './DECR'; describe('DECR', () => { @@ -10,10 +10,10 @@ describe('DECR', () => { ); }); - itWithClient(TestRedisServers.OPEN, 'client.decr', async client => { + testUtils.testWithClient('client.decr', async client => { assert.equal( await client.decr('key'), -1 ); - }); + }, GLOBAL.SERVERS.OPEN); }); diff --git a/lib/commands/DECRBY.spec.ts b/lib/commands/DECRBY.spec.ts index 1c9ac69bb96..d2c23e94728 100644 --- a/lib/commands/DECRBY.spec.ts +++ b/lib/commands/DECRBY.spec.ts @@ -1,5 +1,5 @@ import { strict as assert } from 'assert'; -import { TestRedisServers, itWithClient } from '../test-utils'; +import testUtils, { GLOBAL } from '../test-utils'; import { transformArguments } from './DECRBY'; describe('DECRBY', () => { @@ -10,10 +10,10 @@ describe('DECRBY', () => { ); }); - itWithClient(TestRedisServers.OPEN, 'client.decrBy', async client => { + testUtils.testWithClient('client.decrBy', async client => { assert.equal( await client.decrBy('key', 2), -2 ); - }); + }, GLOBAL.SERVERS.OPEN); }); diff --git a/lib/commands/DEL.spec.ts b/lib/commands/DEL.spec.ts index ec780de67a0..75a29a8f641 100644 --- a/lib/commands/DEL.spec.ts +++ b/lib/commands/DEL.spec.ts @@ -1,5 +1,5 @@ import { strict as assert } from 'assert'; -import { TestRedisServers, itWithClient } from '../test-utils'; +import testUtils, { GLOBAL } from '../test-utils'; import { transformArguments } from './DEL'; describe('DEL', () => { @@ -19,10 +19,10 @@ describe('DEL', () => { }); }); - itWithClient(TestRedisServers.OPEN, 'client.del', async client => { + testUtils.testWithClient('client.del', async client => { assert.equal( await client.del('key'), 0 ); - }); + }, GLOBAL.SERVERS.OPEN); }); diff --git a/lib/commands/DUMP.spec.ts b/lib/commands/DUMP.spec.ts index e3f42c57578..aebbf4f3f7c 100644 --- a/lib/commands/DUMP.spec.ts +++ b/lib/commands/DUMP.spec.ts @@ -1,11 +1,11 @@ import { strict as assert } from 'assert'; -import { TestRedisServers, itWithClient } from '../test-utils'; +import testUtils, { GLOBAL } from '../test-utils'; describe('DUMP', () => { - itWithClient(TestRedisServers.OPEN, 'client.dump', async client => { + testUtils.testWithClient('client.dump', async client => { assert.equal( await client.dump('key'), null ); - }); + }, GLOBAL.SERVERS.OPEN); }); diff --git a/lib/commands/ECHO.spec.ts b/lib/commands/ECHO.spec.ts index d91b7373950..27f6b2a17d3 100644 --- a/lib/commands/ECHO.spec.ts +++ b/lib/commands/ECHO.spec.ts @@ -1,5 +1,5 @@ import { strict as assert } from 'assert'; -import { TestRedisServers, itWithClient } from '../test-utils'; +import testUtils, { GLOBAL } from '../test-utils'; import { transformArguments } from './ECHO'; describe('ECHO', () => { @@ -10,10 +10,10 @@ describe('ECHO', () => { ); }); - itWithClient(TestRedisServers.OPEN, 'client.echo', async client => { + testUtils.testWithClient('client.echo', async client => { assert.equal( await client.echo('message'), 'message' ); - }); + }, GLOBAL.SERVERS.OPEN); }); diff --git a/lib/commands/EVAL.spec.ts b/lib/commands/EVAL.spec.ts index 2be1aedf08a..7aa029362fd 100644 --- a/lib/commands/EVAL.spec.ts +++ b/lib/commands/EVAL.spec.ts @@ -1,5 +1,5 @@ import { strict as assert } from 'assert'; -import { TestRedisServers, itWithClient, TestRedisClusters, itWithCluster } from '../test-utils'; +import testUtils, { GLOBAL } from '../test-utils'; import { transformArguments } from './EVAL'; describe('EVAL', () => { @@ -13,17 +13,17 @@ describe('EVAL', () => { ); }); - itWithClient(TestRedisServers.OPEN, 'client.eval', async client => { + testUtils.testWithClient('client.eval', async client => { assert.equal( await client.eval('return 1'), 1 ); - }); + }, GLOBAL.SERVERS.OPEN); - itWithCluster(TestRedisClusters.OPEN, 'cluster.eval', async cluster => { + testUtils.testWithCluster('cluster.eval', async cluster => { assert.equal( await cluster.eval('return 1'), 1 ); - }); + }, GLOBAL.CLUSTERS.OPEN); }); diff --git a/lib/commands/EXISTS.spec.ts b/lib/commands/EXISTS.spec.ts index 3cba44b563f..241a97c362e 100644 --- a/lib/commands/EXISTS.spec.ts +++ b/lib/commands/EXISTS.spec.ts @@ -1,5 +1,5 @@ import { strict as assert } from 'assert'; -import { TestRedisServers, itWithClient } from '../test-utils'; +import testUtils, { GLOBAL } from '../test-utils'; import { transformArguments } from './EXISTS'; describe('EXISTS', () => { @@ -19,10 +19,10 @@ describe('EXISTS', () => { }); }); - itWithClient(TestRedisServers.OPEN, 'client.exists', async client => { + testUtils.testWithClient('client.exists', async client => { assert.equal( await client.exists('key'), false ); - }); + }, GLOBAL.SERVERS.OPEN); }); diff --git a/lib/commands/EXPIRE.spec.ts b/lib/commands/EXPIRE.spec.ts index 6550532cab2..e2dc6e03123 100644 --- a/lib/commands/EXPIRE.spec.ts +++ b/lib/commands/EXPIRE.spec.ts @@ -1,5 +1,5 @@ import { strict as assert } from 'assert'; -import { TestRedisServers, itWithClient } from '../test-utils'; +import testUtils, { GLOBAL } from '../test-utils'; import { transformArguments } from './EXPIRE'; describe('EXPIRE', () => { @@ -10,10 +10,10 @@ describe('EXPIRE', () => { ); }); - itWithClient(TestRedisServers.OPEN, 'client.expire', async client => { + testUtils.testWithClient('client.expire', async client => { assert.equal( await client.expire('key', 0), false ); - }); + }, GLOBAL.SERVERS.OPEN); }); diff --git a/lib/commands/EXPIREAT.spec.ts b/lib/commands/EXPIREAT.spec.ts index cefe9fa9b8b..1a11af1a735 100644 --- a/lib/commands/EXPIREAT.spec.ts +++ b/lib/commands/EXPIREAT.spec.ts @@ -1,5 +1,5 @@ import { strict as assert } from 'assert'; -import { TestRedisServers, itWithClient } from '../test-utils'; +import testUtils, { GLOBAL } from '../test-utils'; import { transformArguments } from './EXPIREAT'; describe('EXPIREAT', () => { @@ -10,7 +10,7 @@ describe('EXPIREAT', () => { ['EXPIREAT', 'key', '1'] ); }); - + it('date', () => { const d = new Date(); assert.deepEqual( @@ -20,10 +20,10 @@ describe('EXPIREAT', () => { }); }); - itWithClient(TestRedisServers.OPEN, 'client.expireAt', async client => { + testUtils.testWithClient('client.expireAt', async client => { assert.equal( await client.expireAt('key', 1), false ); - }); + }, GLOBAL.SERVERS.OPEN); }); diff --git a/lib/commands/FLUSHALL.spec.ts b/lib/commands/FLUSHALL.spec.ts index 7f1c5ffd282..db5bb72e9cb 100644 --- a/lib/commands/FLUSHALL.spec.ts +++ b/lib/commands/FLUSHALL.spec.ts @@ -1,5 +1,5 @@ import { strict as assert } from 'assert'; -import { TestRedisServers, itWithClient } from '../test-utils'; +import testUtils, { GLOBAL } from '../test-utils'; import { RedisFlushModes, transformArguments } from './FLUSHALL'; describe('FLUSHALL', () => { @@ -26,10 +26,10 @@ describe('FLUSHALL', () => { }); }); - itWithClient(TestRedisServers.OPEN, 'client.flushAll', async client => { + testUtils.testWithClient('client.flushAll', async client => { assert.equal( await client.flushAll(), 'OK' ); - }); + }, GLOBAL.SERVERS.OPEN); }); diff --git a/lib/commands/FLUSHDB.spec.ts b/lib/commands/FLUSHDB.spec.ts index e237e527680..bf460e9e7a8 100644 --- a/lib/commands/FLUSHDB.spec.ts +++ b/lib/commands/FLUSHDB.spec.ts @@ -1,5 +1,5 @@ import { strict as assert } from 'assert'; -import { TestRedisServers, itWithClient } from '../test-utils'; +import testUtils, { GLOBAL } from '../test-utils'; import { RedisFlushModes } from './FLUSHALL'; import { transformArguments } from './FLUSHDB'; @@ -27,10 +27,10 @@ describe('FLUSHDB', () => { }); }); - itWithClient(TestRedisServers.OPEN, 'client.flushDb', async client => { + testUtils.testWithClient('client.flushDb', async client => { assert.equal( await client.flushDb(), 'OK' ); - }); + }, GLOBAL.SERVERS.OPEN); }); diff --git a/lib/commands/GEOADD.spec.ts b/lib/commands/GEOADD.spec.ts index 673e962093f..6425c881c9d 100644 --- a/lib/commands/GEOADD.spec.ts +++ b/lib/commands/GEOADD.spec.ts @@ -1,5 +1,5 @@ import { strict as assert } from 'assert'; -import { TestRedisServers, itWithClient, TestRedisClusters, itWithCluster } from '../test-utils'; +import testUtils, { GLOBAL } from '../test-utils'; import { transformArguments } from './GEOADD'; describe('GEOADD', () => { @@ -71,7 +71,7 @@ describe('GEOADD', () => { }); }); - itWithClient(TestRedisServers.OPEN, 'client.geoAdd', async client => { + testUtils.testWithClient('client.geoAdd', async client => { assert.equal( await client.geoAdd('key', { member: 'member', @@ -80,9 +80,9 @@ describe('GEOADD', () => { }), 1 ); - }); + }, GLOBAL.SERVERS.OPEN); - itWithCluster(TestRedisClusters.OPEN, 'cluster.geoAdd', async cluster => { + testUtils.testWithCluster('cluster.geoAdd', async cluster => { assert.equal( await cluster.geoAdd('key', { member: 'member', @@ -91,5 +91,5 @@ describe('GEOADD', () => { }), 1 ); - }); + }, GLOBAL.CLUSTERS.OPEN); }); diff --git a/lib/commands/GEODIST.spec.ts b/lib/commands/GEODIST.spec.ts index 2cff8ecbd82..bbc62480ee1 100644 --- a/lib/commands/GEODIST.spec.ts +++ b/lib/commands/GEODIST.spec.ts @@ -1,5 +1,5 @@ import { strict as assert } from 'assert'; -import { TestRedisServers, itWithClient, TestRedisClusters, itWithCluster } from '../test-utils'; +import testUtils, { GLOBAL } from '../test-utils'; import { transformArguments } from './GEODIST'; describe('GEODIST', () => { @@ -20,14 +20,14 @@ describe('GEODIST', () => { }); describe('client.geoDist', () => { - itWithClient(TestRedisServers.OPEN, 'null', async client => { + testUtils.testWithClient('null', async client => { assert.equal( await client.geoDist('key', '1', '2'), null ); - }); + }, GLOBAL.SERVERS.OPEN); - itWithClient(TestRedisServers.OPEN, 'with value', async client => { + testUtils.testWithClient('with value', async client => { const [, dist] = await Promise.all([ client.geoAdd('key', [{ member: '1', @@ -45,13 +45,13 @@ describe('GEODIST', () => { dist, 157270.0561 ); - }); + }, GLOBAL.SERVERS.OPEN); }); - itWithCluster(TestRedisClusters.OPEN, 'cluster.geoDist', async cluster => { + testUtils.testWithCluster('cluster.geoDist', async cluster => { assert.equal( await cluster.geoDist('key', '1', '2'), null ); - }); + }, GLOBAL.CLUSTERS.OPEN); }); diff --git a/lib/commands/GEOHASH.spec.ts b/lib/commands/GEOHASH.spec.ts index b79de235557..c421c148f43 100644 --- a/lib/commands/GEOHASH.spec.ts +++ b/lib/commands/GEOHASH.spec.ts @@ -1,5 +1,5 @@ import { strict as assert } from 'assert'; -import { TestRedisServers, itWithClient, TestRedisClusters, itWithCluster } from '../test-utils'; +import testUtils, { GLOBAL } from '../test-utils'; import { transformArguments } from './GEOHASH'; describe('GEOHASH', () => { @@ -19,17 +19,17 @@ describe('GEOHASH', () => { }); }); - itWithClient(TestRedisServers.OPEN, 'client.geoHash', async client => { + testUtils.testWithClient('client.geoHash', async client => { assert.deepEqual( await client.geoHash('key', 'member'), [null] ); - }); + }, GLOBAL.SERVERS.OPEN); - itWithCluster(TestRedisClusters.OPEN, 'cluster.geoHash', async cluster => { + testUtils.testWithCluster('cluster.geoHash', async cluster => { assert.deepEqual( await cluster.geoHash('key', 'member'), [null] ); - }); + }, GLOBAL.CLUSTERS.OPEN); }); diff --git a/lib/commands/GEOPOS.spec.ts b/lib/commands/GEOPOS.spec.ts index e15abeff516..9c08ccd08f5 100644 --- a/lib/commands/GEOPOS.spec.ts +++ b/lib/commands/GEOPOS.spec.ts @@ -1,5 +1,5 @@ import { strict as assert } from 'assert'; -import { TestRedisServers, itWithClient, TestRedisClusters, itWithCluster } from '../test-utils'; +import testUtils, { GLOBAL } from '../test-utils'; import { transformArguments, transformReply } from './GEOPOS'; describe('GEOPOS', () => { @@ -39,14 +39,14 @@ describe('GEOPOS', () => { }); describe('client.geoPos', () => { - itWithClient(TestRedisServers.OPEN, 'null', async client => { + testUtils.testWithClient('null', async client => { assert.deepEqual( await client.geoPos('key', 'member'), [null] ); - }); + }, GLOBAL.SERVERS.OPEN); - itWithClient(TestRedisServers.OPEN, 'with member', async client => { + testUtils.testWithClient('with member', async client => { const coordinates = { longitude: '-122.06429868936538696', latitude: '37.37749628831998194' @@ -61,13 +61,13 @@ describe('GEOPOS', () => { await client.geoPos('key', 'member'), [coordinates] ); - }); + }, GLOBAL.SERVERS.OPEN); }); - itWithCluster(TestRedisClusters.OPEN, 'cluster.geoPos', async cluster => { + testUtils.testWithCluster('cluster.geoPos', async cluster => { assert.deepEqual( await cluster.geoPos('key', 'member'), [null] ); - }); + }, GLOBAL.CLUSTERS.OPEN); }); diff --git a/lib/commands/GEOSEARCH.spec.ts b/lib/commands/GEOSEARCH.spec.ts index a8606b3f74c..ec0d4bcc4f8 100644 --- a/lib/commands/GEOSEARCH.spec.ts +++ b/lib/commands/GEOSEARCH.spec.ts @@ -1,9 +1,9 @@ import { strict as assert } from 'assert'; -import { TestRedisServers, itWithClient, TestRedisClusters, itWithCluster, describeHandleMinimumRedisVersion } from '../test-utils'; +import testUtils, { GLOBAL } from '../test-utils'; import { transformArguments } from './GEOSEARCH'; describe('GEOSEARCH', () => { - describeHandleMinimumRedisVersion([6, 2]); + testUtils.isVersionGreaterThanHook([6, 2]); it('transformArguments', () => { assert.deepEqual( @@ -15,7 +15,7 @@ describe('GEOSEARCH', () => { ); }); - itWithClient(TestRedisServers.OPEN, 'client.geoSearch', async client => { + testUtils.testWithClient('client.geoSearch', async client => { assert.deepEqual( await client.geoSearch('key', 'member', { radius: 1, @@ -23,9 +23,9 @@ describe('GEOSEARCH', () => { }), [] ); - }); + }, GLOBAL.SERVERS.OPEN); - itWithCluster(TestRedisClusters.OPEN, 'cluster.geoSearch', async cluster => { + testUtils.testWithCluster('cluster.geoSearch', async cluster => { assert.deepEqual( await cluster.geoSearch('key', 'member', { radius: 1, @@ -33,5 +33,5 @@ describe('GEOSEARCH', () => { }), [] ); - }); + }, GLOBAL.CLUSTERS.OPEN); }); diff --git a/lib/commands/GEOSEARCHSTORE.spec.ts b/lib/commands/GEOSEARCHSTORE.spec.ts index ad33c62b78c..eb32fa134e4 100644 --- a/lib/commands/GEOSEARCHSTORE.spec.ts +++ b/lib/commands/GEOSEARCHSTORE.spec.ts @@ -1,9 +1,9 @@ import { strict as assert } from 'assert'; -import { TestRedisServers, itWithClient, TestRedisClusters, itWithCluster, describeHandleMinimumRedisVersion } from '../test-utils'; +import testUtils, { GLOBAL } from '../test-utils'; import { transformArguments, transformReply } from './GEOSEARCHSTORE'; describe('GEOSEARCHSTORE', () => { - describeHandleMinimumRedisVersion([6, 2]); + testUtils.isVersionGreaterThanHook([6, 2]); describe('transformArguments', () => { it('simple', () => { @@ -47,7 +47,7 @@ describe('GEOSEARCHSTORE', () => { ); }); - itWithClient(TestRedisServers.OPEN, 'client.geoSearchStore', async client => { + testUtils.testWithClient('client.geoSearchStore', async client => { await client.geoAdd('source', { longitude: 1, latitude: 1, @@ -61,9 +61,9 @@ describe('GEOSEARCHSTORE', () => { }), 1 ); - }); + }, GLOBAL.SERVERS.OPEN); - itWithCluster(TestRedisClusters.OPEN, 'cluster.geoSearchStore', async cluster => { + testUtils.testWithCluster('cluster.geoSearchStore', async cluster => { await cluster.geoAdd('{tag}source', { longitude: 1, latitude: 1, @@ -77,5 +77,5 @@ describe('GEOSEARCHSTORE', () => { }), 1 ); - }); + }, GLOBAL.CLUSTERS.OPEN); }); diff --git a/lib/commands/GEOSEARCH_WITH.spec.ts b/lib/commands/GEOSEARCH_WITH.spec.ts index 922c00d7194..c1f5213775a 100644 --- a/lib/commands/GEOSEARCH_WITH.spec.ts +++ b/lib/commands/GEOSEARCH_WITH.spec.ts @@ -1,14 +1,14 @@ import { strict as assert } from 'assert'; +import testUtils, { GLOBAL } from '../test-utils'; import { RedisCommandArguments } from '.'; -import { TestRedisServers, itWithClient, TestRedisClusters, itWithCluster, describeHandleMinimumRedisVersion } from '../test-utils'; import { GeoReplyWith } from './generic-transformers'; import { transformArguments } from './GEOSEARCH_WITH'; describe('GEOSEARCH WITH', () => { - describeHandleMinimumRedisVersion([6, 2]); + testUtils.isVersionGreaterThanHook([6, 2]); it('transformArguments', () => { - const expectedReply: RedisCommandArguments = ['GEOSEARCH', 'key', 'FROMMEMBER', 'member', 'BYRADIUS', '1', 'm', 'WITHDIST'] + const expectedReply: RedisCommandArguments = ['GEOSEARCH', 'key', 'FROMMEMBER', 'member', 'BYRADIUS', '1', 'm', 'WITHDIST']; expectedReply.preserve = ['WITHDIST']; assert.deepEqual( @@ -20,7 +20,7 @@ describe('GEOSEARCH WITH', () => { ); }); - itWithClient(TestRedisServers.OPEN, 'client.geoSearchWith', async client => { + testUtils.testWithClient('client.geoSearchWith', async client => { assert.deepEqual( await client.geoSearchWith('key', 'member', { radius: 1, @@ -28,9 +28,9 @@ describe('GEOSEARCH WITH', () => { }, [GeoReplyWith.DISTANCE]), [] ); - }); + }, GLOBAL.SERVERS.OPEN); - itWithCluster(TestRedisClusters.OPEN, 'cluster.geoSearchWith', async cluster => { + testUtils.testWithCluster('cluster.geoSearchWith', async cluster => { assert.deepEqual( await cluster.geoSearchWith('key', 'member', { radius: 1, @@ -38,5 +38,5 @@ describe('GEOSEARCH WITH', () => { }, [GeoReplyWith.DISTANCE]), [] ); - }); + }, GLOBAL.CLUSTERS.OPEN); }); diff --git a/lib/commands/GET.spec.ts b/lib/commands/GET.spec.ts index 303be60fe99..4c197f99a4e 100644 --- a/lib/commands/GET.spec.ts +++ b/lib/commands/GET.spec.ts @@ -1,5 +1,5 @@ import { strict as assert } from 'assert'; -import { TestRedisServers, itWithClient, TestRedisClusters, itWithCluster } from '../test-utils'; +import testUtils, { GLOBAL } from '../test-utils'; import { transformArguments } from './GET'; describe('GET', () => { @@ -10,17 +10,17 @@ describe('GET', () => { ); }); - itWithClient(TestRedisServers.OPEN, 'client.get', async client => { + testUtils.testWithClient('client.get', async client => { assert.equal( await client.get('key'), null ); - }); + }, GLOBAL.SERVERS.OPEN); - itWithCluster(TestRedisClusters.OPEN, 'cluster.get', async cluster => { + testUtils.testWithCluster('cluster.get', async cluster => { assert.equal( await cluster.get('key'), null ); - }); + }, GLOBAL.CLUSTERS.OPEN); }); diff --git a/lib/commands/GETBIT.spec.ts b/lib/commands/GETBIT.spec.ts index 7163b4ba255..4206084eced 100644 --- a/lib/commands/GETBIT.spec.ts +++ b/lib/commands/GETBIT.spec.ts @@ -1,5 +1,5 @@ import { strict as assert } from 'assert'; -import { TestRedisServers, itWithClient, TestRedisClusters, itWithCluster } from '../test-utils'; +import testUtils, { GLOBAL } from '../test-utils'; import { transformArguments } from './GETBIT'; describe('GETBIT', () => { @@ -10,17 +10,17 @@ describe('GETBIT', () => { ); }); - itWithClient(TestRedisServers.OPEN, 'client.getBit', async client => { + testUtils.testWithClient('client.getBit', async client => { assert.equal( await client.getBit('key', 0), 0 ); - }); + }, GLOBAL.SERVERS.OPEN); - itWithCluster(TestRedisClusters.OPEN, 'cluster.getBit', async cluster => { + testUtils.testWithCluster('cluster.getBit', async cluster => { assert.equal( await cluster.getBit('key', 0), 0 ); - }); + }, GLOBAL.CLUSTERS.OPEN); }); diff --git a/lib/commands/GETDEL.spec.ts b/lib/commands/GETDEL.spec.ts index 232c08b9500..db3a486696a 100644 --- a/lib/commands/GETDEL.spec.ts +++ b/lib/commands/GETDEL.spec.ts @@ -1,9 +1,9 @@ import { strict as assert } from 'assert'; -import { TestRedisServers, itWithClient, TestRedisClusters, itWithCluster, describeHandleMinimumRedisVersion } from '../test-utils'; +import testUtils, { GLOBAL } from '../test-utils'; import { transformArguments } from './GETDEL'; describe('GETDEL', () => { - describeHandleMinimumRedisVersion([6, 2]); + testUtils.isVersionGreaterThanHook([6, 2]); it('transformArguments', () => { assert.deepEqual( @@ -12,18 +12,17 @@ describe('GETDEL', () => { ); }); - itWithClient(TestRedisServers.OPEN, 'client.getDel', async client => { + testUtils.testWithClient('client.getDel', async client => { assert.equal( await client.getDel('key'), null ); - }); - + }, GLOBAL.SERVERS.OPEN); - itWithCluster(TestRedisClusters.OPEN, 'cluster.getDel', async cluster => { + testUtils.testWithCluster('cluster.getDel', async cluster => { assert.equal( await cluster.getDel('key'), null ); - }); + }, GLOBAL.CLUSTERS.OPEN); }); diff --git a/lib/commands/GETEX.spec.ts b/lib/commands/GETEX.spec.ts index 830f12cedf8..1bf86089da1 100644 --- a/lib/commands/GETEX.spec.ts +++ b/lib/commands/GETEX.spec.ts @@ -1,9 +1,9 @@ import { strict as assert } from 'assert'; -import { TestRedisServers, itWithClient, TestRedisClusters, itWithCluster, describeHandleMinimumRedisVersion } from '../test-utils'; +import testUtils, { GLOBAL } from '../test-utils'; import { transformArguments } from './GETEX'; describe('GETEX', () => { - describeHandleMinimumRedisVersion([6, 2]); + testUtils.isVersionGreaterThanHook([6, 2]); describe('transformArguments', () => { it('EX', () => { @@ -76,21 +76,21 @@ describe('GETEX', () => { }); }); - itWithClient(TestRedisServers.OPEN, 'client.getEx', async client => { + testUtils.testWithClient('client.getEx', async client => { assert.equal( await client.getEx('key', { PERSIST: true }), null ); - }); + }, GLOBAL.SERVERS.OPEN); - itWithCluster(TestRedisClusters.OPEN, 'cluster.getEx', async cluster => { + testUtils.testWithCluster('cluster.getEx', async cluster => { assert.equal( await cluster.getEx('key', { PERSIST: true }), null ); - }); + }, GLOBAL.CLUSTERS.OPEN); }); diff --git a/lib/commands/GETRANGE.spec.ts b/lib/commands/GETRANGE.spec.ts index 726311e6844..0c9dbc2c70f 100644 --- a/lib/commands/GETRANGE.spec.ts +++ b/lib/commands/GETRANGE.spec.ts @@ -1,5 +1,5 @@ import { strict as assert } from 'assert'; -import { TestRedisServers, itWithClient, TestRedisClusters, itWithCluster } from '../test-utils'; +import testUtils, { GLOBAL } from '../test-utils'; import { transformArguments } from './GETRANGE'; describe('GETRANGE', () => { @@ -10,18 +10,17 @@ describe('GETRANGE', () => { ); }); - itWithClient(TestRedisServers.OPEN, 'client.getRange', async client => { + testUtils.testWithClient('client.getRange', async client => { assert.equal( await client.getRange('key', 0, -1), '' ); - }); - + }, GLOBAL.SERVERS.OPEN); - itWithCluster(TestRedisClusters.OPEN, 'cluster.lTrim', async cluster => { + testUtils.testWithCluster('cluster.lTrim', async cluster => { assert.equal( await cluster.getRange('key', 0, -1), '' ); - }); + }, GLOBAL.CLUSTERS.OPEN); }); diff --git a/lib/commands/GETSET.spec.ts b/lib/commands/GETSET.spec.ts index 4af5ab39ca2..73fbcec57ea 100644 --- a/lib/commands/GETSET.spec.ts +++ b/lib/commands/GETSET.spec.ts @@ -1,5 +1,5 @@ import { strict as assert } from 'assert'; -import { TestRedisServers, itWithClient, TestRedisClusters, itWithCluster } from '../test-utils'; +import testUtils, { GLOBAL } from '../test-utils'; import { transformArguments } from './GETSET'; describe('GETSET', () => { @@ -10,17 +10,17 @@ describe('GETSET', () => { ); }); - itWithClient(TestRedisServers.OPEN, 'client.getSet', async client => { + testUtils.testWithClient('client.getSet', async client => { assert.equal( await client.getSet('key', 'value'), null ); - }); + }, GLOBAL.SERVERS.OPEN); - itWithCluster(TestRedisClusters.OPEN, 'cluster.getSet', async cluster => { + testUtils.testWithCluster('cluster.getSet', async cluster => { assert.equal( await cluster.getSet('key', 'value'), null ); - }); + }, GLOBAL.CLUSTERS.OPEN); }); diff --git a/lib/commands/GET_BUFFER.spec.ts b/lib/commands/GET_BUFFER.spec.ts index 533eb808c49..1f1a86799f4 100644 --- a/lib/commands/GET_BUFFER.spec.ts +++ b/lib/commands/GET_BUFFER.spec.ts @@ -1,22 +1,22 @@ import { strict as assert } from 'assert'; -import { TestRedisServers, itWithClient, TestRedisClusters, itWithCluster } from '../test-utils'; +import testUtils, { GLOBAL } from '../test-utils'; describe('GET_BUFFER', () => { - itWithClient(TestRedisServers.OPEN, 'client.getBuffer', async client => { + testUtils.testWithClient('client.getBuffer', async client => { const buffer = Buffer.from('string'); await client.set('key', buffer); assert.deepEqual( buffer, await client.getBuffer('key') ); - }); + }, GLOBAL.SERVERS.OPEN); - itWithCluster(TestRedisClusters.OPEN, 'cluster.getBuffer', async cluster => { + testUtils.testWithCluster('cluster.getBuffer', async cluster => { const buffer = Buffer.from('string'); await cluster.set('key', buffer); assert.deepEqual( buffer, await cluster.getBuffer('key') ); - }); + }, GLOBAL.CLUSTERS.OPEN); }); diff --git a/lib/commands/HDEL.spec.ts b/lib/commands/HDEL.spec.ts index 04191f51ada..eb24bcfacbd 100644 --- a/lib/commands/HDEL.spec.ts +++ b/lib/commands/HDEL.spec.ts @@ -1,5 +1,5 @@ import { strict as assert } from 'assert'; -import { TestRedisServers, itWithClient } from '../test-utils'; +import testUtils, { GLOBAL } from '../test-utils'; import { transformArguments } from './HDEL'; describe('HDEL', () => { @@ -19,10 +19,10 @@ describe('HDEL', () => { }); }); - itWithClient(TestRedisServers.OPEN, 'client.hDel', async client => { + testUtils.testWithClient('client.hDel', async client => { assert.equal( await client.hDel('key', 'field'), 0 ); - }); + }, GLOBAL.SERVERS.OPEN); }); diff --git a/lib/commands/HELLO.spec.ts b/lib/commands/HELLO.spec.ts index db4604afead..12d6d98c7c9 100644 --- a/lib/commands/HELLO.spec.ts +++ b/lib/commands/HELLO.spec.ts @@ -1,9 +1,9 @@ import { strict as assert } from 'assert'; -import { REDIS_VERSION, TestRedisServers, itWithClient, describeHandleMinimumRedisVersion } from '../test-utils'; +import testUtils, { GLOBAL } from '../test-utils'; import { transformArguments } from './HELLO'; describe('HELLO', () => { - describeHandleMinimumRedisVersion([6]); + testUtils.isVersionGreaterThanHook([6]); describe('transformArguments', () => { it('simple', () => { @@ -60,20 +60,17 @@ describe('HELLO', () => { }); }); - itWithClient(TestRedisServers.OPEN, 'client.hello', async client => { - assert.deepEqual( - await client.hello(), - { - server: 'redis', - version: REDIS_VERSION.join('.'), - proto: 2, - id: await client.clientId(), - mode: 'standalone', - role: 'master', - modules: [] - } - ); + testUtils.testWithClient('client.hello', async client => { + const reply = await client.hello(); + assert.equal(reply.server, 'redis'); + assert.equal(typeof reply.version, 'string'); + assert.equal(reply.proto, 2); + assert.equal(typeof reply.id, 'number'); + assert.equal(reply.mode, 'standalone'); + assert.equal(reply.role, 'master'); + assert.deepEqual(reply.modules, []); }, { - minimumRedisVersion: [6, 2] + ...GLOBAL.SERVERS.OPEN, + minimumDockerVersion: [6, 2] }); }); diff --git a/lib/commands/HEXISTS.spec.ts b/lib/commands/HEXISTS.spec.ts index 26c411c432d..3764319c123 100644 --- a/lib/commands/HEXISTS.spec.ts +++ b/lib/commands/HEXISTS.spec.ts @@ -1,5 +1,5 @@ import { strict as assert } from 'assert'; -import { TestRedisServers, itWithClient } from '../test-utils'; +import testUtils, { GLOBAL } from '../test-utils'; import { transformArguments } from './HEXISTS'; describe('HEXISTS', () => { @@ -10,10 +10,10 @@ describe('HEXISTS', () => { ); }); - itWithClient(TestRedisServers.OPEN, 'client.hExists', async client => { + testUtils.testWithClient('client.hExists', async client => { assert.equal( await client.hExists('key', 'field'), false ); - }); + }, GLOBAL.SERVERS.OPEN); }); diff --git a/lib/commands/HGET.spec.ts b/lib/commands/HGET.spec.ts index c78550c5179..6b6d0a3ee22 100644 --- a/lib/commands/HGET.spec.ts +++ b/lib/commands/HGET.spec.ts @@ -1,5 +1,5 @@ import { strict as assert } from 'assert'; -import { TestRedisServers, itWithClient } from '../test-utils'; +import testUtils, { GLOBAL } from '../test-utils'; import { transformArguments } from './HGET'; describe('HGET', () => { @@ -10,10 +10,10 @@ describe('HGET', () => { ); }); - itWithClient(TestRedisServers.OPEN, 'client.hGet', async client => { + testUtils.testWithClient('client.hGet', async client => { assert.equal( await client.hGet('key', 'field'), null ); - }); + }, GLOBAL.SERVERS.OPEN); }); diff --git a/lib/commands/HGETALL.spec.ts b/lib/commands/HGETALL.spec.ts index 68b51a2902b..fcd1a30457c 100644 --- a/lib/commands/HGETALL.spec.ts +++ b/lib/commands/HGETALL.spec.ts @@ -1,5 +1,5 @@ import { strict as assert } from 'assert'; -import { TestRedisServers, itWithClient } from '../test-utils'; +import testUtils, { GLOBAL } from '../test-utils'; import { transformReply } from './HGETALL'; describe('HGETALL', () => { @@ -32,10 +32,10 @@ describe('HGETALL', () => { }); }); - itWithClient(TestRedisServers.OPEN, 'client.hGetAll', async client => { + testUtils.testWithClient('client.hGetAll', async client => { assert.deepEqual( await client.hGetAll('key'), Object.create(null) ); - }); + }, GLOBAL.SERVERS.OPEN); }); diff --git a/lib/commands/HINCRBY.spec.ts b/lib/commands/HINCRBY.spec.ts index 898dfd1172f..de406217921 100644 --- a/lib/commands/HINCRBY.spec.ts +++ b/lib/commands/HINCRBY.spec.ts @@ -1,5 +1,5 @@ import { strict as assert } from 'assert'; -import { TestRedisServers, itWithClient } from '../test-utils'; +import testUtils, { GLOBAL } from '../test-utils'; import { transformArguments } from './HINCRBY'; describe('HINCRBY', () => { @@ -10,10 +10,10 @@ describe('HINCRBY', () => { ); }); - itWithClient(TestRedisServers.OPEN, 'client.hIncrBy', async client => { + testUtils.testWithClient('client.hIncrBy', async client => { assert.equal( await client.hIncrBy('key', 'field', 1), 1 ); - }); + }, GLOBAL.SERVERS.OPEN); }); diff --git a/lib/commands/HINCRBYFLOAT.spec.ts b/lib/commands/HINCRBYFLOAT.spec.ts index 83e87538c54..bd0147a3481 100644 --- a/lib/commands/HINCRBYFLOAT.spec.ts +++ b/lib/commands/HINCRBYFLOAT.spec.ts @@ -1,5 +1,5 @@ import { strict as assert } from 'assert'; -import { TestRedisServers, itWithClient } from '../test-utils'; +import testUtils, { GLOBAL } from '../test-utils'; import { transformArguments } from './HINCRBYFLOAT'; describe('HINCRBYFLOAT', () => { @@ -10,10 +10,10 @@ describe('HINCRBYFLOAT', () => { ); }); - itWithClient(TestRedisServers.OPEN, 'client.hIncrByFloat', async client => { + testUtils.testWithClient('client.hIncrByFloat', async client => { assert.equal( await client.hIncrByFloat('key', 'field', 1.5), '1.5' ); - }); + }, GLOBAL.SERVERS.OPEN); }); diff --git a/lib/commands/HKEYS.spec.ts b/lib/commands/HKEYS.spec.ts index 12190668b0b..f94538f67d3 100644 --- a/lib/commands/HKEYS.spec.ts +++ b/lib/commands/HKEYS.spec.ts @@ -1,5 +1,5 @@ import { strict as assert } from 'assert'; -import { TestRedisServers, itWithClient } from '../test-utils'; +import testUtils, { GLOBAL } from '../test-utils'; import { transformArguments } from './HKEYS'; describe('HKEYS', () => { @@ -10,10 +10,10 @@ describe('HKEYS', () => { ); }); - itWithClient(TestRedisServers.OPEN, 'client.hKeys', async client => { + testUtils.testWithClient('client.hKeys', async client => { assert.deepEqual( await client.hKeys('key'), [] ); - }); + }, GLOBAL.SERVERS.OPEN); }); diff --git a/lib/commands/HLEN.spec.ts b/lib/commands/HLEN.spec.ts index e9aaa64e6e5..be9d4b13a7d 100644 --- a/lib/commands/HLEN.spec.ts +++ b/lib/commands/HLEN.spec.ts @@ -1,5 +1,5 @@ import { strict as assert } from 'assert'; -import { TestRedisServers, itWithClient } from '../test-utils'; +import testUtils, { GLOBAL } from '../test-utils'; import { transformArguments } from './HLEN'; describe('HLEN', () => { @@ -10,10 +10,10 @@ describe('HLEN', () => { ); }); - itWithClient(TestRedisServers.OPEN, 'client.hLen', async client => { + testUtils.testWithClient('client.hLen', async client => { assert.equal( await client.hLen('key'), 0 ); - }); + }, GLOBAL.SERVERS.OPEN); }); diff --git a/lib/commands/HMGET.spec.ts b/lib/commands/HMGET.spec.ts index 3b1c286e748..a7c934b760d 100644 --- a/lib/commands/HMGET.spec.ts +++ b/lib/commands/HMGET.spec.ts @@ -1,5 +1,5 @@ import { strict as assert } from 'assert'; -import { TestRedisServers, itWithClient } from '../test-utils'; +import testUtils, { GLOBAL } from '../test-utils'; import { transformArguments } from './HMGET'; describe('HMGET', () => { @@ -19,10 +19,10 @@ describe('HMGET', () => { }); }); - itWithClient(TestRedisServers.OPEN, 'client.hmGet', async client => { + testUtils.testWithClient('client.hmGet', async client => { assert.deepEqual( await client.hmGet('key', 'field'), [null] ); - }); + }, GLOBAL.SERVERS.OPEN); }); diff --git a/lib/commands/HRANDFIELD.spec.ts b/lib/commands/HRANDFIELD.spec.ts index 70e2585cf93..df0a4fc7a1d 100644 --- a/lib/commands/HRANDFIELD.spec.ts +++ b/lib/commands/HRANDFIELD.spec.ts @@ -1,9 +1,9 @@ import { strict as assert } from 'assert'; -import { TestRedisServers, itWithClient, describeHandleMinimumRedisVersion } from '../test-utils'; +import testUtils, { GLOBAL } from '../test-utils'; import { transformArguments } from './HRANDFIELD'; describe('HRANDFIELD', () => { - describeHandleMinimumRedisVersion([6, 2]); + testUtils.isVersionGreaterThanHook([6, 2]); it('transformArguments', () => { assert.deepEqual( @@ -12,10 +12,10 @@ describe('HRANDFIELD', () => { ); }); - itWithClient(TestRedisServers.OPEN, 'client.hRandField', async client => { + testUtils.testWithClient('client.hRandField', async client => { assert.equal( await client.hRandField('key'), null ); - }); + }, GLOBAL.SERVERS.OPEN); }); diff --git a/lib/commands/HRANDFIELD_COUNT.spec.ts b/lib/commands/HRANDFIELD_COUNT.spec.ts index 6954bd484af..4bfce0f7e23 100644 --- a/lib/commands/HRANDFIELD_COUNT.spec.ts +++ b/lib/commands/HRANDFIELD_COUNT.spec.ts @@ -1,9 +1,9 @@ import { strict as assert } from 'assert'; -import { TestRedisServers, itWithClient, describeHandleMinimumRedisVersion } from '../test-utils'; +import testUtils, { GLOBAL } from '../test-utils'; import { transformArguments } from './HRANDFIELD_COUNT'; describe('HRANDFIELD COUNT', () => { - describeHandleMinimumRedisVersion([6, 2, 5]); + testUtils.isVersionGreaterThanHook([6, 2, 5]); it('transformArguments', () => { assert.deepEqual( @@ -12,10 +12,10 @@ describe('HRANDFIELD COUNT', () => { ); }); - itWithClient(TestRedisServers.OPEN, 'client.hRandFieldCount', async client => { + testUtils.testWithClient('client.hRandFieldCount', async client => { assert.deepEqual( await client.hRandFieldCount('key', 1), [] ); - }); + }, GLOBAL.SERVERS.OPEN); }); diff --git a/lib/commands/HRANDFIELD_COUNT_WITHVALUES.spec.ts b/lib/commands/HRANDFIELD_COUNT_WITHVALUES.spec.ts index 0c26cbc7938..c4e6409a726 100644 --- a/lib/commands/HRANDFIELD_COUNT_WITHVALUES.spec.ts +++ b/lib/commands/HRANDFIELD_COUNT_WITHVALUES.spec.ts @@ -1,9 +1,9 @@ import { strict as assert } from 'assert'; -import { TestRedisServers, itWithClient, describeHandleMinimumRedisVersion } from '../test-utils'; +import testUtils, { GLOBAL } from '../test-utils'; import { transformArguments } from './HRANDFIELD_COUNT_WITHVALUES'; describe('HRANDFIELD COUNT WITHVALUES', () => { - describeHandleMinimumRedisVersion([6, 2, 5]); + testUtils.isVersionGreaterThanHook([6, 2, 5]); it('transformArguments', () => { assert.deepEqual( @@ -12,10 +12,10 @@ describe('HRANDFIELD COUNT WITHVALUES', () => { ); }); - itWithClient(TestRedisServers.OPEN, 'client.hRandFieldCountWithValues', async client => { + testUtils.testWithClient('client.hRandFieldCountWithValues', async client => { assert.deepEqual( await client.hRandFieldCountWithValues('key', 1), Object.create(null) ); - }); + }, GLOBAL.SERVERS.OPEN); }); diff --git a/lib/commands/HSCAN.spec.ts b/lib/commands/HSCAN.spec.ts index 7441dd48d52..b426763b99b 100644 --- a/lib/commands/HSCAN.spec.ts +++ b/lib/commands/HSCAN.spec.ts @@ -1,5 +1,5 @@ import { strict as assert } from 'assert'; -import { TestRedisServers, itWithClient } from '../test-utils'; +import testUtils, { GLOBAL } from '../test-utils'; import { transformArguments, transformReply } from './HSCAN'; describe('HSCAN', () => { @@ -65,7 +65,7 @@ describe('HSCAN', () => { }); }); - itWithClient(TestRedisServers.OPEN, 'client.hScan', async client => { + testUtils.testWithClient('client.hScan', async client => { assert.deepEqual( await client.hScan('key', 0), { @@ -73,5 +73,5 @@ describe('HSCAN', () => { tuples: [] } ); - }); + }, GLOBAL.SERVERS.OPEN); }); diff --git a/lib/commands/HSET.spec.ts b/lib/commands/HSET.spec.ts index e8dfe7865d3..507c7bbbf74 100644 --- a/lib/commands/HSET.spec.ts +++ b/lib/commands/HSET.spec.ts @@ -1,6 +1,6 @@ import { strict as assert } from 'assert'; import { transformArguments } from './HSET'; -import { TestRedisServers, itWithClient, TestRedisClusters, itWithCluster } from '../test-utils'; +import testUtils, { GLOBAL } from '../test-utils'; describe('HSET', () => { describe('transformArguments', () => { @@ -33,17 +33,17 @@ describe('HSET', () => { }); }); - itWithClient(TestRedisServers.OPEN, 'client.hSet', async client => { + testUtils.testWithClient('client.hSet', async client => { assert.equal( await client.hSet('key', 'field', 'value'), 1 ); - }); + }, GLOBAL.SERVERS.OPEN); - itWithCluster(TestRedisClusters.OPEN, 'cluster.hSet', async cluster => { + testUtils.testWithCluster('cluster.hSet', async cluster => { assert.equal( await cluster.hSet('key', { field: 'value' }), 1 ); - }); + }, GLOBAL.CLUSTERS.OPEN); }); \ No newline at end of file diff --git a/lib/commands/HSETNX.spec.ts b/lib/commands/HSETNX.spec.ts index f810c5e2b9a..190fa50ae97 100644 --- a/lib/commands/HSETNX.spec.ts +++ b/lib/commands/HSETNX.spec.ts @@ -1,5 +1,5 @@ import { strict as assert } from 'assert'; -import { TestRedisServers, itWithClient } from '../test-utils'; +import testUtils, { GLOBAL } from '../test-utils'; import { transformArguments } from './HSETNX'; describe('HSETNX', () => { @@ -10,10 +10,10 @@ describe('HSETNX', () => { ); }); - itWithClient(TestRedisServers.OPEN, 'client.hSetNX', async client => { + testUtils.testWithClient('client.hSetNX', async client => { assert.equal( await client.hSetNX('key', 'field', 'value'), true ); - }); + }, GLOBAL.SERVERS.OPEN); }); diff --git a/lib/commands/HSTRLEN.spec.ts b/lib/commands/HSTRLEN.spec.ts index 35bf08d54c4..79c3150211e 100644 --- a/lib/commands/HSTRLEN.spec.ts +++ b/lib/commands/HSTRLEN.spec.ts @@ -1,5 +1,5 @@ import { strict as assert } from 'assert'; -import { TestRedisServers, itWithClient } from '../test-utils'; +import testUtils, { GLOBAL } from '../test-utils'; import { transformArguments } from './HSTRLEN'; describe('HSTRLEN', () => { @@ -10,10 +10,10 @@ describe('HSTRLEN', () => { ); }); - itWithClient(TestRedisServers.OPEN, 'client.hStrLen', async client => { + testUtils.testWithClient('client.hStrLen', async client => { assert.equal( await client.hStrLen('key', 'field'), 0 ); - }); + }, GLOBAL.SERVERS.OPEN); }); diff --git a/lib/commands/HVALS.spec.ts b/lib/commands/HVALS.spec.ts index 9e6451f500e..d0a6c39ce5f 100644 --- a/lib/commands/HVALS.spec.ts +++ b/lib/commands/HVALS.spec.ts @@ -1,5 +1,5 @@ import { strict as assert } from 'assert'; -import { TestRedisServers, itWithClient } from '../test-utils'; +import testUtils, { GLOBAL } from '../test-utils'; import { transformArguments } from './HVALS'; describe('HVALS', () => { @@ -10,10 +10,10 @@ describe('HVALS', () => { ); }); - itWithClient(TestRedisServers.OPEN, 'client.hVals', async client => { + testUtils.testWithClient('client.hVals', async client => { assert.deepEqual( await client.hVals('key'), [] ); - }); + }, GLOBAL.SERVERS.OPEN); }); diff --git a/lib/commands/INCR.spec.ts b/lib/commands/INCR.spec.ts index d64c3696af4..321d83edc54 100644 --- a/lib/commands/INCR.spec.ts +++ b/lib/commands/INCR.spec.ts @@ -1,5 +1,5 @@ import { strict as assert } from 'assert'; -import { TestRedisServers, itWithClient } from '../test-utils'; +import testUtils, { GLOBAL } from '../test-utils'; import { transformArguments } from './INCR'; describe('INCR', () => { @@ -10,10 +10,10 @@ describe('INCR', () => { ); }); - itWithClient(TestRedisServers.OPEN, 'client.incr', async client => { + testUtils.testWithClient('client.incr', async client => { assert.equal( await client.incr('key'), 1 ); - }); + }, GLOBAL.SERVERS.OPEN); }); diff --git a/lib/commands/INCRBY.spec.ts b/lib/commands/INCRBY.spec.ts index 875277570cb..a671d0ec259 100644 --- a/lib/commands/INCRBY.spec.ts +++ b/lib/commands/INCRBY.spec.ts @@ -1,5 +1,5 @@ import { strict as assert } from 'assert'; -import { TestRedisServers, itWithClient } from '../test-utils'; +import testUtils, { GLOBAL } from '../test-utils'; import { transformArguments } from './INCRBY'; describe('INCR', () => { @@ -10,10 +10,10 @@ describe('INCR', () => { ); }); - itWithClient(TestRedisServers.OPEN, 'client.incrBy', async client => { + testUtils.testWithClient('client.incrBy', async client => { assert.equal( await client.incrBy('key', 1), 1 ); - }); + }, GLOBAL.SERVERS.OPEN); }); diff --git a/lib/commands/INCRBYFLOAT.spec.ts b/lib/commands/INCRBYFLOAT.spec.ts index fe062b62905..b2dd5aa5da9 100644 --- a/lib/commands/INCRBYFLOAT.spec.ts +++ b/lib/commands/INCRBYFLOAT.spec.ts @@ -1,5 +1,5 @@ import { strict as assert } from 'assert'; -import { TestRedisServers, itWithClient } from '../test-utils'; +import testUtils, { GLOBAL } from '../test-utils'; import { transformArguments } from './INCRBYFLOAT'; describe('INCRBYFLOAT', () => { @@ -10,10 +10,10 @@ describe('INCRBYFLOAT', () => { ); }); - itWithClient(TestRedisServers.OPEN, 'client.incrByFloat', async client => { + testUtils.testWithClient('client.incrByFloat', async client => { assert.equal( await client.incrByFloat('key', 1.5), '1.5' ); - }); + }, GLOBAL.SERVERS.OPEN); }); diff --git a/lib/commands/KEYS.spec.ts b/lib/commands/KEYS.spec.ts index d11e8a0f58b..c066331ea7c 100644 --- a/lib/commands/KEYS.spec.ts +++ b/lib/commands/KEYS.spec.ts @@ -1,11 +1,11 @@ import { strict as assert } from 'assert'; -import { TestRedisServers, itWithClient } from '../test-utils'; +import testUtils, { GLOBAL } from '../test-utils'; describe('KEYS', () => { - itWithClient(TestRedisServers.OPEN, 'client.keys', async client => { + testUtils.testWithClient('client.keys', async client => { assert.deepEqual( await client.keys('pattern'), [] ); - }); + }, GLOBAL.SERVERS.OPEN); }); diff --git a/lib/commands/LASTSAVE.spec.ts b/lib/commands/LASTSAVE.spec.ts index b8d801f70b5..a6b4863f39e 100644 --- a/lib/commands/LASTSAVE.spec.ts +++ b/lib/commands/LASTSAVE.spec.ts @@ -1,5 +1,5 @@ import { strict as assert } from 'assert'; -import { TestRedisServers, itWithClient } from '../test-utils'; +import testUtils, { GLOBAL } from '../test-utils'; import { transformArguments } from './LASTSAVE'; describe('LASTSAVE', () => { @@ -10,7 +10,7 @@ describe('LASTSAVE', () => { ); }); - itWithClient(TestRedisServers.OPEN, 'client.lastSave', async client => { + testUtils.testWithClient('client.lastSave', async client => { assert.ok((await client.lastSave()) instanceof Date); - }); + }, GLOBAL.SERVERS.OPEN); }); diff --git a/lib/commands/LINDEX.spec.ts b/lib/commands/LINDEX.spec.ts index 74a6706ecdc..5e0b1473ec4 100644 --- a/lib/commands/LINDEX.spec.ts +++ b/lib/commands/LINDEX.spec.ts @@ -1,5 +1,5 @@ import { strict as assert } from 'assert'; -import { TestRedisServers, itWithClient, itWithCluster, TestRedisClusters } from '../test-utils'; +import testUtils, { GLOBAL } from '../test-utils'; import { transformArguments } from './LINDEX'; describe('LINDEX', () => { @@ -10,17 +10,17 @@ describe('LINDEX', () => { ); }); - itWithClient(TestRedisServers.OPEN, 'client.lIndex', async client => { + testUtils.testWithClient('client.lIndex', async client => { assert.equal( await client.lIndex('key', 'element'), null ); - }); + }, GLOBAL.SERVERS.OPEN); - itWithCluster(TestRedisClusters.OPEN, 'cluster.lIndex', async cluster => { + testUtils.testWithCluster('cluster.lIndex', async cluster => { assert.equal( await cluster.lIndex('key', 'element'), null ); - }); + }, GLOBAL.CLUSTERS.OPEN); }); diff --git a/lib/commands/LINSERT.spec.ts b/lib/commands/LINSERT.spec.ts index 286e61d06d6..6cc429d6a2c 100644 --- a/lib/commands/LINSERT.spec.ts +++ b/lib/commands/LINSERT.spec.ts @@ -1,5 +1,5 @@ import { strict as assert } from 'assert'; -import { TestRedisServers, itWithClient, itWithCluster, TestRedisClusters } from '../test-utils'; +import testUtils, { GLOBAL } from '../test-utils'; import { transformArguments } from './LINSERT'; describe('LINSERT', () => { @@ -10,17 +10,17 @@ describe('LINSERT', () => { ); }); - itWithClient(TestRedisServers.OPEN, 'client.lInsert', async client => { + testUtils.testWithClient('client.lInsert', async client => { assert.equal( await client.lInsert('key', 'BEFORE', 'pivot', 'element'), 0 ); - }); + }, GLOBAL.SERVERS.OPEN); - itWithCluster(TestRedisClusters.OPEN, 'cluster.lLen', async cluster => { + testUtils.testWithCluster('cluster.lLen', async cluster => { assert.equal( await cluster.lInsert('key', 'BEFORE', 'pivot', 'element'), 0 ); - }); + }, GLOBAL.CLUSTERS.OPEN); }); diff --git a/lib/commands/LLEN.spec.ts b/lib/commands/LLEN.spec.ts index 6e4581ddd1f..fb126ddad55 100644 --- a/lib/commands/LLEN.spec.ts +++ b/lib/commands/LLEN.spec.ts @@ -1,5 +1,5 @@ import { strict as assert } from 'assert'; -import { TestRedisServers, itWithClient, itWithCluster, TestRedisClusters } from '../test-utils'; +import testUtils, { GLOBAL } from '../test-utils'; import { transformArguments } from './LLEN'; describe('LLEN', () => { @@ -10,17 +10,17 @@ describe('LLEN', () => { ); }); - itWithClient(TestRedisServers.OPEN, 'client.lLen', async client => { + testUtils.testWithClient('client.lLen', async client => { assert.equal( await client.lLen('key'), 0 ); - }); + }, GLOBAL.SERVERS.OPEN); - itWithCluster(TestRedisClusters.OPEN, 'cluster.lLen', async cluster => { + testUtils.testWithCluster('cluster.lLen', async cluster => { assert.equal( await cluster.lLen('key'), 0 ); - }); + }, GLOBAL.CLUSTERS.OPEN); }); diff --git a/lib/commands/LMOVE.spec.ts b/lib/commands/LMOVE.spec.ts index bcb897f76ac..f1d418c394e 100644 --- a/lib/commands/LMOVE.spec.ts +++ b/lib/commands/LMOVE.spec.ts @@ -1,9 +1,9 @@ import { strict as assert } from 'assert'; -import { TestRedisServers, itWithClient, itWithCluster, TestRedisClusters, describeHandleMinimumRedisVersion } from '../test-utils'; +import testUtils, { GLOBAL } from '../test-utils'; import { transformArguments } from './LMOVE'; describe('LMOVE', () => { - describeHandleMinimumRedisVersion([6, 2]); + testUtils.isVersionGreaterThanHook([6, 2]); it('transformArguments', () => { assert.deepEqual( @@ -12,17 +12,17 @@ describe('LMOVE', () => { ); }); - itWithClient(TestRedisServers.OPEN, 'client.lMove', async client => { + testUtils.testWithClient('client.lMove', async client => { assert.equal( await client.lMove('source', 'destination', 'LEFT', 'RIGHT'), null ); - }); + }, GLOBAL.SERVERS.OPEN); - itWithCluster(TestRedisClusters.OPEN, 'cluster.lMove', async cluster => { + testUtils.testWithCluster('cluster.lMove', async cluster => { assert.equal( await cluster.lMove('{tag}source', '{tag}destination', 'LEFT', 'RIGHT'), null ); - }); + }, GLOBAL.CLUSTERS.OPEN); }); diff --git a/lib/commands/LOLWUT.spec.ts b/lib/commands/LOLWUT.spec.ts index 8f4478aecc7..db335893302 100644 --- a/lib/commands/LOLWUT.spec.ts +++ b/lib/commands/LOLWUT.spec.ts @@ -1,5 +1,5 @@ import { strict as assert } from 'assert'; -import { TestRedisServers, itWithClient } from '../test-utils'; +import testUtils, { GLOBAL } from '../test-utils'; import { transformArguments } from './LOLWUT'; describe('LOLWUT', () => { @@ -26,11 +26,10 @@ describe('LOLWUT', () => { }); }); - - itWithClient(TestRedisServers.OPEN, 'client.LOLWUT', async client => { + testUtils.testWithClient('client.LOLWUT', async client => { assert.equal( typeof (await client.LOLWUT()), 'string' ); - }); + }, GLOBAL.SERVERS.OPEN); }); diff --git a/lib/commands/LPOP.spec.ts b/lib/commands/LPOP.spec.ts index b593f657427..d694fb10588 100644 --- a/lib/commands/LPOP.spec.ts +++ b/lib/commands/LPOP.spec.ts @@ -1,5 +1,5 @@ import { strict as assert } from 'assert'; -import { TestRedisServers, itWithClient, itWithCluster, TestRedisClusters } from '../test-utils'; +import testUtils, { GLOBAL } from '../test-utils'; import { transformArguments } from './LPOP'; describe('LPOP', () => { @@ -10,17 +10,17 @@ describe('LPOP', () => { ); }); - itWithClient(TestRedisServers.OPEN, 'client.lPop', async client => { + testUtils.testWithClient('client.lPop', async client => { assert.equal( await client.lPop('key'), null ); - }); + }, GLOBAL.SERVERS.OPEN); - itWithCluster(TestRedisClusters.OPEN, 'cluster.lPop', async cluster => { + testUtils.testWithCluster('cluster.lPop', async cluster => { assert.equal( await cluster.lPop('key'), null ); - }); + }, GLOBAL.CLUSTERS.OPEN); }); diff --git a/lib/commands/LPOP_COUNT.spec.ts b/lib/commands/LPOP_COUNT.spec.ts index 89150dbf4de..9d87fad3862 100644 --- a/lib/commands/LPOP_COUNT.spec.ts +++ b/lib/commands/LPOP_COUNT.spec.ts @@ -1,9 +1,9 @@ import { strict as assert } from 'assert'; -import { TestRedisServers, itWithClient, itWithCluster, TestRedisClusters, describeHandleMinimumRedisVersion } from '../test-utils'; +import testUtils, { GLOBAL } from '../test-utils'; import { transformArguments } from './LPOP_COUNT'; describe('LPOP COUNT', () => { - describeHandleMinimumRedisVersion([6, 2]); + testUtils.isVersionGreaterThanHook([6, 2]); it('transformArguments', () => { assert.deepEqual( @@ -12,17 +12,17 @@ describe('LPOP COUNT', () => { ); }); - itWithClient(TestRedisServers.OPEN, 'client.lPopCount', async client => { + testUtils.testWithClient('client.lPopCount', async client => { assert.equal( await client.lPopCount('key', 1), null ); - }); + }, GLOBAL.SERVERS.OPEN); - itWithCluster(TestRedisClusters.OPEN, 'cluster.lPop', async cluster => { + testUtils.testWithCluster('cluster.lPopCount', async cluster => { assert.equal( await cluster.lPopCount('key', 1), null ); - }); + }, GLOBAL.CLUSTERS.OPEN); }); diff --git a/lib/commands/LPOS.spec.ts b/lib/commands/LPOS.spec.ts index 1cf9e35209d..6b6050f2c3b 100644 --- a/lib/commands/LPOS.spec.ts +++ b/lib/commands/LPOS.spec.ts @@ -1,9 +1,9 @@ import { strict as assert } from 'assert'; -import { TestRedisServers, itWithClient, TestRedisClusters, itWithCluster, describeHandleMinimumRedisVersion } from '../test-utils'; +import testUtils, { GLOBAL } from '../test-utils'; import { transformArguments } from './LPOS'; describe('LPOS', () => { - describeHandleMinimumRedisVersion([6, 0, 6]); + testUtils.isVersionGreaterThanHook([6, 0, 6]); describe('transformArguments', () => { it('simple', () => { @@ -42,17 +42,17 @@ describe('LPOS', () => { }); }); - itWithClient(TestRedisServers.OPEN, 'client.lPos', async client => { + testUtils.testWithClient('client.lPos', async client => { assert.equal( await client.lPos('key', 'element'), null ); - }); + }, GLOBAL.SERVERS.OPEN); - itWithCluster(TestRedisClusters.OPEN, 'cluster.lPos', async cluster => { + testUtils.testWithCluster('cluster.lPos', async cluster => { assert.equal( await cluster.lPos('key', 'element'), null ); - }); + }, GLOBAL.CLUSTERS.OPEN); }); diff --git a/lib/commands/LPOS_COUNT.spec.ts b/lib/commands/LPOS_COUNT.spec.ts index 1d80bd45c3c..4b01f2f59b9 100644 --- a/lib/commands/LPOS_COUNT.spec.ts +++ b/lib/commands/LPOS_COUNT.spec.ts @@ -1,9 +1,9 @@ import { strict as assert } from 'assert'; -import { TestRedisServers, itWithClient, itWithCluster, TestRedisClusters, describeHandleMinimumRedisVersion } from '../test-utils'; +import testUtils, { GLOBAL } from '../test-utils'; import { transformArguments } from './LPOS_COUNT'; describe('LPOS COUNT', () => { - describeHandleMinimumRedisVersion([6, 0, 6]); + testUtils.isVersionGreaterThanHook([6, 0, 6]); describe('transformArguments', () => { it('simple', () => { @@ -42,17 +42,17 @@ describe('LPOS COUNT', () => { }); }); - itWithClient(TestRedisServers.OPEN, 'client.lPosCount', async client => { + testUtils.testWithClient('client.lPosCount', async client => { assert.deepEqual( await client.lPosCount('key', 'element', 0), [] ); - }); + }, GLOBAL.SERVERS.OPEN); - itWithCluster(TestRedisClusters.OPEN, 'cluster.lPosCount', async cluster => { + testUtils.testWithCluster('cluster.lPosCount', async cluster => { assert.deepEqual( await cluster.lPosCount('key', 'element', 0), [] ); - }); + }, GLOBAL.CLUSTERS.OPEN); }); diff --git a/lib/commands/LPUSH.spec.ts b/lib/commands/LPUSH.spec.ts index 44cf8c12d5f..b5b1f5084eb 100644 --- a/lib/commands/LPUSH.spec.ts +++ b/lib/commands/LPUSH.spec.ts @@ -1,5 +1,5 @@ import { strict as assert } from 'assert'; -import { TestRedisServers, itWithClient, itWithCluster, TestRedisClusters } from '../test-utils'; +import testUtils, { GLOBAL } from '../test-utils'; import { transformArguments } from './LPUSH'; describe('LPUSH', () => { @@ -19,17 +19,17 @@ describe('LPUSH', () => { }); }); - itWithClient(TestRedisServers.OPEN, 'client.lPush', async client => { + testUtils.testWithClient('client.lPush', async client => { assert.equal( await client.lPush('key', 'field'), 1 ); - }); + }, GLOBAL.SERVERS.OPEN); - itWithCluster(TestRedisClusters.OPEN, 'cluster.lPush', async cluster => { + testUtils.testWithCluster('cluster.lPush', async cluster => { assert.equal( await cluster.lPush('key', 'field'), 1 ); - }); + }, GLOBAL.CLUSTERS.OPEN); }); diff --git a/lib/commands/LPUSHX.spec.ts b/lib/commands/LPUSHX.spec.ts index 1150c4d64dd..d978e5a588f 100644 --- a/lib/commands/LPUSHX.spec.ts +++ b/lib/commands/LPUSHX.spec.ts @@ -1,5 +1,5 @@ import { strict as assert } from 'assert'; -import { TestRedisServers, itWithClient, itWithCluster, TestRedisClusters } from '../test-utils'; +import testUtils, { GLOBAL } from '../test-utils'; import { transformArguments } from './LPUSHX'; describe('LPUSHX', () => { @@ -19,17 +19,17 @@ describe('LPUSHX', () => { }); }); - itWithClient(TestRedisServers.OPEN, 'client.lPushX', async client => { + testUtils.testWithClient('client.lPushX', async client => { assert.equal( await client.lPushX('key', 'element'), 0 ); - }); + }, GLOBAL.SERVERS.OPEN); - itWithCluster(TestRedisClusters.OPEN, 'cluster.lPushX', async cluster => { + testUtils.testWithCluster('cluster.lPushX', async cluster => { assert.equal( await cluster.lPushX('key', 'element'), 0 ); - }); + }, GLOBAL.CLUSTERS.OPEN); }); diff --git a/lib/commands/LRANGE.spec.ts b/lib/commands/LRANGE.spec.ts index 843b7b8815e..dffe6087b80 100644 --- a/lib/commands/LRANGE.spec.ts +++ b/lib/commands/LRANGE.spec.ts @@ -1,6 +1,6 @@ import { strict as assert } from 'assert'; -import { TestRedisServers, itWithClient, itWithCluster, TestRedisClusters } from '../test-utils'; +import testUtils, { GLOBAL } from '../test-utils'; import { transformArguments } from './LRANGE'; describe('LRANGE', () => { @@ -11,17 +11,17 @@ describe('LRANGE', () => { ); }); - itWithClient(TestRedisServers.OPEN, 'client.lRange', async client => { + testUtils.testWithClient('client.lRange', async client => { assert.deepEqual( await client.lRange('key', 0, -1), [] ); - }); + }, GLOBAL.SERVERS.OPEN); - itWithCluster(TestRedisClusters.OPEN, 'cluster.lRange', async cluster => { + testUtils.testWithCluster('cluster.lRange', async cluster => { assert.deepEqual( await cluster.lRange('key', 0, -1), [] ); - }); + }, GLOBAL.CLUSTERS.OPEN); }); diff --git a/lib/commands/LREM.spec.ts b/lib/commands/LREM.spec.ts index e2f027ffeb8..3405f4beb07 100644 --- a/lib/commands/LREM.spec.ts +++ b/lib/commands/LREM.spec.ts @@ -1,6 +1,6 @@ import { strict as assert } from 'assert'; -import { TestRedisServers, itWithClient, itWithCluster, TestRedisClusters } from '../test-utils'; +import testUtils, { GLOBAL } from '../test-utils'; import { transformArguments } from './LREM'; describe('LREM', () => { @@ -11,17 +11,17 @@ describe('LREM', () => { ); }); - itWithClient(TestRedisServers.OPEN, 'client.lRem', async client => { + testUtils.testWithClient('client.lRem', async client => { assert.equal( await client.lRem('key', 0, 'element'), 0 ); - }); + }, GLOBAL.SERVERS.OPEN); - itWithCluster(TestRedisClusters.OPEN, 'cluster.lRem', async cluster => { + testUtils.testWithCluster('cluster.lRem', async cluster => { assert.equal( await cluster.lRem('key', 0, 'element'), 0 ); - }); + }, GLOBAL.CLUSTERS.OPEN); }); diff --git a/lib/commands/LSET.spec.ts b/lib/commands/LSET.spec.ts index a5fe78cf4c3..d7241032cc6 100644 --- a/lib/commands/LSET.spec.ts +++ b/lib/commands/LSET.spec.ts @@ -1,5 +1,5 @@ import { strict as assert } from 'assert'; -import { TestRedisServers, itWithClient, itWithCluster, TestRedisClusters } from '../test-utils'; +import testUtils, { GLOBAL } from '../test-utils'; import { transformArguments } from './LSET'; describe('LSET', () => { @@ -10,19 +10,19 @@ describe('LSET', () => { ); }); - itWithClient(TestRedisServers.OPEN, 'client.lSet', async client => { + testUtils.testWithClient('client.lSet', async client => { await client.lPush('key', 'element'); assert.equal( await client.lSet('key', 0, 'element'), 'OK' ); - }); + }, GLOBAL.SERVERS.OPEN); - itWithCluster(TestRedisClusters.OPEN, 'cluster.lSet', async cluster => { + testUtils.testWithCluster('cluster.lSet', async cluster => { await cluster.lPush('key', 'element'); assert.equal( await cluster.lSet('key', 0, 'element'), 'OK' ); - }); + }, GLOBAL.CLUSTERS.OPEN); }); diff --git a/lib/commands/LTRIM.spec.ts b/lib/commands/LTRIM.spec.ts index 8092ba6af1e..5b6ac5d3660 100644 --- a/lib/commands/LTRIM.spec.ts +++ b/lib/commands/LTRIM.spec.ts @@ -1,5 +1,5 @@ import { strict as assert } from 'assert'; -import { TestRedisServers, itWithClient, itWithCluster, TestRedisClusters } from '../test-utils'; +import testUtils, { GLOBAL } from '../test-utils'; import { transformArguments } from './LTRIM'; describe('LTRIM', () => { @@ -10,17 +10,17 @@ describe('LTRIM', () => { ); }); - itWithClient(TestRedisServers.OPEN, 'client.lTrim', async client => { + testUtils.testWithClient('client.lTrim', async client => { assert.equal( await client.lTrim('key', 0, -1), 'OK' ); - }); + }, GLOBAL.SERVERS.OPEN); - itWithCluster(TestRedisClusters.OPEN, 'cluster.lTrim', async cluster => { + testUtils.testWithCluster('cluster.lTrim', async cluster => { assert.equal( await cluster.lTrim('key', 0, -1), 'OK' ); - }); + }, GLOBAL.CLUSTERS.OPEN); }); diff --git a/lib/commands/LTRIM.ts b/lib/commands/LTRIM.ts index 018afd90a15..ab78e5305aa 100644 --- a/lib/commands/LTRIM.ts +++ b/lib/commands/LTRIM.ts @@ -6,7 +6,7 @@ export function transformArguments(key: string, start: number, stop: number): Ar key, start.toString(), stop.toString() - ] + ]; } export declare function transformReply(): string; diff --git a/lib/commands/MEMORY_DOCTOR.spec.ts b/lib/commands/MEMORY_DOCTOR.spec.ts index 1b4d16fa0db..ad97047606c 100644 --- a/lib/commands/MEMORY_DOCTOR.spec.ts +++ b/lib/commands/MEMORY_DOCTOR.spec.ts @@ -1,5 +1,5 @@ import { strict as assert } from 'assert'; -import { TestRedisServers, itWithClient } from '../test-utils'; +import testUtils, { GLOBAL } from '../test-utils'; import { transformArguments } from './MEMORY_DOCTOR'; describe('MEMORY DOCTOR', () => { @@ -10,10 +10,10 @@ describe('MEMORY DOCTOR', () => { ); }); - itWithClient(TestRedisServers.OPEN, 'client.memoryDoctor', async client => { + testUtils.testWithClient('client.memoryDoctor', async client => { assert.equal( typeof (await client.memoryDoctor()), 'string' ); - }); + }, GLOBAL.SERVERS.OPEN); }); diff --git a/lib/commands/MEMORY_MALLOC-STATS.spec.ts b/lib/commands/MEMORY_MALLOC-STATS.spec.ts index 034b94f7c66..ce866f1e116 100644 --- a/lib/commands/MEMORY_MALLOC-STATS.spec.ts +++ b/lib/commands/MEMORY_MALLOC-STATS.spec.ts @@ -1,5 +1,5 @@ import { strict as assert } from 'assert'; -import { TestRedisServers, itWithClient } from '../test-utils'; +import testUtils, { GLOBAL } from '../test-utils'; import { transformArguments } from './MEMORY_MALLOC-STATS'; describe('MEMORY MALLOC-STATS', () => { @@ -10,10 +10,10 @@ describe('MEMORY MALLOC-STATS', () => { ); }); - itWithClient(TestRedisServers.OPEN, 'client.memoryMallocStats', async client => { + testUtils.testWithClient('client.memoryMallocStats', async client => { assert.equal( typeof (await client.memoryDoctor()), 'string' ); - }); + }, GLOBAL.SERVERS.OPEN); }); diff --git a/lib/commands/MEMORY_PURGE.spec.ts b/lib/commands/MEMORY_PURGE.spec.ts index 97ca6feebf6..5d34331feb6 100644 --- a/lib/commands/MEMORY_PURGE.spec.ts +++ b/lib/commands/MEMORY_PURGE.spec.ts @@ -1,5 +1,5 @@ import { strict as assert } from 'assert'; -import { TestRedisServers, itWithClient } from '../test-utils'; +import testUtils, { GLOBAL } from '../test-utils'; import { transformArguments } from './MEMORY_PURGE'; describe('MEMORY PURGE', () => { @@ -10,10 +10,10 @@ describe('MEMORY PURGE', () => { ); }); - itWithClient(TestRedisServers.OPEN, 'client.memoryPurge', async client => { + testUtils.testWithClient('client.memoryPurge', async client => { assert.equal( await client.memoryPurge(), 'OK' ); - }); + }, GLOBAL.SERVERS.OPEN); }); diff --git a/lib/commands/MEMORY_USAGE.spec.ts b/lib/commands/MEMORY_USAGE.spec.ts index 90dff62c674..fe5ff404d93 100644 --- a/lib/commands/MEMORY_USAGE.spec.ts +++ b/lib/commands/MEMORY_USAGE.spec.ts @@ -1,5 +1,5 @@ import { strict as assert } from 'assert'; -import { TestRedisServers, itWithClient } from '../test-utils'; +import testUtils, { GLOBAL } from '../test-utils'; import { transformArguments } from './MEMORY_USAGE'; describe('MEMORY USAGE', () => { @@ -21,10 +21,10 @@ describe('MEMORY USAGE', () => { }); }); - itWithClient(TestRedisServers.OPEN, 'client.memoryUsage', async client => { + testUtils.testWithClient('client.memoryUsage', async client => { assert.equal( await client.memoryUsage('key'), null ); - }); + }, GLOBAL.SERVERS.OPEN); }); diff --git a/lib/commands/MGET.spec.ts b/lib/commands/MGET.spec.ts index c8c020fe433..9ff47895f4e 100644 --- a/lib/commands/MGET.spec.ts +++ b/lib/commands/MGET.spec.ts @@ -1,5 +1,5 @@ import { strict as assert } from 'assert'; -import { TestRedisServers, itWithClient, TestRedisClusters, itWithCluster } from '../test-utils'; +import testUtils, { GLOBAL } from '../test-utils'; import { transformArguments } from './MGET'; describe('MGET', () => { @@ -10,17 +10,17 @@ describe('MGET', () => { ); }); - itWithClient(TestRedisServers.OPEN, 'client.mGet', async client => { + testUtils.testWithClient('client.mGet', async client => { assert.deepEqual( await client.mGet(['key']), [null] ); - }); + }, GLOBAL.SERVERS.OPEN); - itWithCluster(TestRedisClusters.OPEN, 'cluster.mGet', async cluster => { + testUtils.testWithCluster('cluster.mGet', async cluster => { assert.deepEqual( await cluster.mGet(['key']), [null] ); - }); + }, GLOBAL.CLUSTERS.OPEN); }); diff --git a/lib/commands/MOVE.spec.ts b/lib/commands/MOVE.spec.ts index a05ca4613e9..f7fdc481cbf 100644 --- a/lib/commands/MOVE.spec.ts +++ b/lib/commands/MOVE.spec.ts @@ -1,5 +1,5 @@ import { strict as assert } from 'assert'; -import { TestRedisServers, itWithClient } from '../test-utils'; +import testUtils, { GLOBAL } from '../test-utils'; import { transformArguments } from './MOVE'; describe('MOVE', () => { @@ -10,10 +10,10 @@ describe('MOVE', () => { ); }); - itWithClient(TestRedisServers.OPEN, 'client.move', async client => { + testUtils.testWithClient('client.move', async client => { assert.equal( await client.move('key', 1), false ); - }); + }, GLOBAL.SERVERS.OPEN); }); diff --git a/lib/commands/MSET.spec.ts b/lib/commands/MSET.spec.ts index 4445f4a7281..0568f38487e 100644 --- a/lib/commands/MSET.spec.ts +++ b/lib/commands/MSET.spec.ts @@ -1,5 +1,5 @@ import { strict as assert } from 'assert'; -import { TestRedisServers, itWithClient, TestRedisClusters, itWithCluster } from '../test-utils'; +import testUtils, { GLOBAL } from '../test-utils'; import { transformArguments } from './MSET'; describe('MSET', () => { @@ -26,17 +26,17 @@ describe('MSET', () => { }); }); - itWithClient(TestRedisServers.OPEN, 'client.mSet', async client => { + testUtils.testWithClient('client.mSet', async client => { assert.equal( await client.mSet(['key1', 'value1', 'key2', 'value2']), 'OK' ); - }); + }, GLOBAL.SERVERS.OPEN); - itWithCluster(TestRedisClusters.OPEN, 'cluster.mSet', async cluster => { + testUtils.testWithCluster('cluster.mSet', async cluster => { assert.equal( await cluster.mSet(['{key}1', 'value1', '{key}2', 'value2']), 'OK' ); - }); + }, GLOBAL.CLUSTERS.OPEN); }); diff --git a/lib/commands/MSETNX.spec.ts b/lib/commands/MSETNX.spec.ts index 7f61a43e8d0..854a9affd8a 100644 --- a/lib/commands/MSETNX.spec.ts +++ b/lib/commands/MSETNX.spec.ts @@ -1,5 +1,5 @@ import { strict as assert } from 'assert'; -import { TestRedisServers, itWithClient, TestRedisClusters, itWithCluster } from '../test-utils'; +import testUtils, { GLOBAL } from '../test-utils'; import { transformArguments } from './MSETNX'; describe('MSETNX', () => { @@ -26,17 +26,17 @@ describe('MSETNX', () => { }); }); - itWithClient(TestRedisServers.OPEN, 'client.mSetNX', async client => { + testUtils.testWithClient('client.mSetNX', async client => { assert.equal( await client.mSetNX(['key1', 'value1', 'key2', 'value2']), true ); - }); + }, GLOBAL.SERVERS.OPEN); - itWithCluster(TestRedisClusters.OPEN, 'cluster.mSetNX', async cluster => { + testUtils.testWithCluster('cluster.mSetNX', async cluster => { assert.equal( await cluster.mSetNX(['{key}1', 'value1', '{key}2', 'value2']), true ); - }); + }, GLOBAL.CLUSTERS.OPEN); }); diff --git a/lib/commands/PERSIST.spec.ts b/lib/commands/PERSIST.spec.ts index 05c0e7aed8e..4e53bd85a6c 100644 --- a/lib/commands/PERSIST.spec.ts +++ b/lib/commands/PERSIST.spec.ts @@ -1,5 +1,5 @@ import { strict as assert } from 'assert'; -import { TestRedisServers, itWithClient } from '../test-utils'; +import testUtils, { GLOBAL } from '../test-utils'; import { transformArguments } from './PERSIST'; describe('PERSIST', () => { @@ -10,10 +10,10 @@ describe('PERSIST', () => { ); }); - itWithClient(TestRedisServers.OPEN, 'client.persist', async client => { + testUtils.testWithClient('client.persist', async client => { assert.equal( await client.persist('key'), false ); - }); + }, GLOBAL.SERVERS.OPEN); }); diff --git a/lib/commands/PEXPIRE.spec.ts b/lib/commands/PEXPIRE.spec.ts index b7c4e1df461..4738edcf8f0 100644 --- a/lib/commands/PEXPIRE.spec.ts +++ b/lib/commands/PEXPIRE.spec.ts @@ -1,5 +1,5 @@ import { strict as assert } from 'assert'; -import { TestRedisServers, itWithClient } from '../test-utils'; +import testUtils, { GLOBAL } from '../test-utils'; import { transformArguments } from './PEXPIRE'; describe('PEXPIRE', () => { @@ -10,10 +10,10 @@ describe('PEXPIRE', () => { ); }); - itWithClient(TestRedisServers.OPEN, 'client.pExpire', async client => { + testUtils.testWithClient('client.pExpire', async client => { assert.equal( await client.pExpire('key', 1), false ); - }); + }, GLOBAL.SERVERS.OPEN); }); diff --git a/lib/commands/PEXPIREAT.spec.ts b/lib/commands/PEXPIREAT.spec.ts index 6e5fc37ed5c..19fc3b888d7 100644 --- a/lib/commands/PEXPIREAT.spec.ts +++ b/lib/commands/PEXPIREAT.spec.ts @@ -1,5 +1,5 @@ import { strict as assert } from 'assert'; -import { TestRedisServers, itWithClient } from '../test-utils'; +import testUtils, { GLOBAL } from '../test-utils'; import { transformArguments } from './PEXPIREAT'; describe('PEXPIREAT', () => { @@ -10,7 +10,7 @@ describe('PEXPIREAT', () => { ['PEXPIREAT', 'key', '1'] ); }); - + it('date', () => { const d = new Date(); assert.deepEqual( @@ -20,10 +20,10 @@ describe('PEXPIREAT', () => { }); }); - itWithClient(TestRedisServers.OPEN, 'client.pExpireAt', async client => { + testUtils.testWithClient('client.pExpireAt', async client => { assert.equal( await client.pExpireAt('key', 1), false ); - }); + }, GLOBAL.SERVERS.OPEN); }); diff --git a/lib/commands/PFADD.spec.ts b/lib/commands/PFADD.spec.ts index 74f03ea3ce2..8c0e752fd50 100644 --- a/lib/commands/PFADD.spec.ts +++ b/lib/commands/PFADD.spec.ts @@ -1,5 +1,5 @@ import { strict as assert } from 'assert'; -import { TestRedisServers, itWithClient } from '../test-utils'; +import testUtils, { GLOBAL } from '../test-utils'; import { transformArguments } from './PFADD'; describe('PFADD', () => { @@ -19,10 +19,10 @@ describe('PFADD', () => { }); }); - itWithClient(TestRedisServers.OPEN, 'client.pfAdd', async client => { + testUtils.testWithClient('client.pfAdd', async client => { assert.equal( await client.pfAdd('key', '1'), true ); - }); + }, GLOBAL.SERVERS.OPEN); }); diff --git a/lib/commands/PFCOUNT.spec.ts b/lib/commands/PFCOUNT.spec.ts index 049fa2c200c..a1ea06c4494 100644 --- a/lib/commands/PFCOUNT.spec.ts +++ b/lib/commands/PFCOUNT.spec.ts @@ -1,5 +1,5 @@ import { strict as assert } from 'assert'; -import { TestRedisServers, itWithClient } from '../test-utils'; +import testUtils, { GLOBAL } from '../test-utils'; import { transformArguments } from './PFCOUNT'; describe('PFCOUNT', () => { @@ -19,10 +19,10 @@ describe('PFCOUNT', () => { }); }); - itWithClient(TestRedisServers.OPEN, 'client.pfCount', async client => { + testUtils.testWithClient('client.pfCount', async client => { assert.equal( await client.pfCount('key'), 0 ); - }); + }, GLOBAL.SERVERS.OPEN); }); diff --git a/lib/commands/PFMERGE.spec.ts b/lib/commands/PFMERGE.spec.ts index 1f6ed24bcd1..881fc5f5439 100644 --- a/lib/commands/PFMERGE.spec.ts +++ b/lib/commands/PFMERGE.spec.ts @@ -1,5 +1,5 @@ import { strict as assert } from 'assert'; -import { TestRedisServers, itWithClient } from '../test-utils'; +import testUtils, { GLOBAL } from '../test-utils'; import { transformArguments } from './PFMERGE'; describe('PFMERGE', () => { @@ -19,10 +19,10 @@ describe('PFMERGE', () => { }); }); - itWithClient(TestRedisServers.OPEN, 'client.pfMerge', async client => { + testUtils.testWithClient('client.pfMerge', async client => { assert.equal( await client.pfMerge('destination', 'source'), 'OK' ); - }); + }, GLOBAL.SERVERS.OPEN); }); diff --git a/lib/commands/PING.spec.ts b/lib/commands/PING.spec.ts index 43b683f192d..85b48fec6b5 100644 --- a/lib/commands/PING.spec.ts +++ b/lib/commands/PING.spec.ts @@ -1,11 +1,11 @@ import { strict as assert } from 'assert'; -import { TestRedisServers, itWithClient } from '../test-utils'; +import testUtils, { GLOBAL } from '../test-utils'; describe('PING', () => { - itWithClient(TestRedisServers.OPEN, 'client.ping', async client => { + testUtils.testWithClient('client.ping', async client => { assert.equal( await client.ping(), 'PONG' ); - }); + }, GLOBAL.SERVERS.OPEN); }); diff --git a/lib/commands/PSETEX.spec.ts b/lib/commands/PSETEX.spec.ts index c98142effa9..61a6e682b08 100644 --- a/lib/commands/PSETEX.spec.ts +++ b/lib/commands/PSETEX.spec.ts @@ -1,5 +1,5 @@ import { strict as assert } from 'assert'; -import { TestRedisServers, itWithClient, TestRedisClusters, itWithCluster } from '../test-utils'; +import testUtils, { GLOBAL } from '../test-utils'; import { transformArguments } from './PSETEX'; describe('PSETEX', () => { @@ -10,17 +10,17 @@ describe('PSETEX', () => { ); }); - itWithClient(TestRedisServers.OPEN, 'client.pSetEx', async client => { + testUtils.testWithClient('client.pSetEx', async client => { assert.equal( await client.pSetEx('key', 1, 'value'), 'OK' ); - }); + }, GLOBAL.SERVERS.OPEN); - itWithCluster(TestRedisClusters.OPEN, 'cluster.pSetEx', async cluster => { + testUtils.testWithCluster('cluster.pSetEx', async cluster => { assert.equal( await cluster.pSetEx('key', 1, 'value'), 'OK' ); - }); + }, GLOBAL.CLUSTERS.OPEN); }); diff --git a/lib/commands/PTTL.spec.ts b/lib/commands/PTTL.spec.ts index 35f48c2cc3e..e65421de590 100644 --- a/lib/commands/PTTL.spec.ts +++ b/lib/commands/PTTL.spec.ts @@ -1,5 +1,5 @@ import { strict as assert } from 'assert'; -import { TestRedisServers, itWithClient } from '../test-utils'; +import testUtils, { GLOBAL } from '../test-utils'; import { transformArguments } from './PTTL'; describe('PTTL', () => { @@ -10,10 +10,10 @@ describe('PTTL', () => { ); }); - itWithClient(TestRedisServers.OPEN, 'client.pTTL', async client => { + testUtils.testWithClient('client.pTTL', async client => { assert.equal( await client.pTTL('key'), -2 ); - }); + }, GLOBAL.SERVERS.OPEN); }); diff --git a/lib/commands/PUBLISH.spec.ts b/lib/commands/PUBLISH.spec.ts index e746b9490e0..b2084e668ba 100644 --- a/lib/commands/PUBLISH.spec.ts +++ b/lib/commands/PUBLISH.spec.ts @@ -1,5 +1,5 @@ import { strict as assert } from 'assert'; -import { TestRedisServers, itWithClient } from '../test-utils'; +import testUtils, { GLOBAL } from '../test-utils'; import { transformArguments } from './PUBLISH'; describe('PUBLISH', () => { @@ -10,10 +10,10 @@ describe('PUBLISH', () => { ); }); - itWithClient(TestRedisServers.OPEN, 'client.publish', async client => { + testUtils.testWithClient('client.publish', async client => { assert.equal( await client.publish('channel', 'message'), 0 ); - }); + }, GLOBAL.SERVERS.OPEN); }); diff --git a/lib/commands/PUBSUB_CHANNELS.spec.ts b/lib/commands/PUBSUB_CHANNELS.spec.ts index 9e148bc7fda..c427eab4850 100644 --- a/lib/commands/PUBSUB_CHANNELS.spec.ts +++ b/lib/commands/PUBSUB_CHANNELS.spec.ts @@ -1,5 +1,5 @@ import { strict as assert } from 'assert'; -import { TestRedisServers, itWithClient } from '../test-utils'; +import testUtils, { GLOBAL } from '../test-utils'; import { transformArguments } from './PUBSUB_CHANNELS'; describe('PUBSUB CHANNELS', () => { @@ -19,10 +19,10 @@ describe('PUBSUB CHANNELS', () => { }); }); - itWithClient(TestRedisServers.OPEN, 'client.pubSubChannels', async client => { + testUtils.testWithClient('client.pubSubChannels', async client => { assert.deepEqual( await client.pubSubChannels(), [] ); - }); + }, GLOBAL.SERVERS.OPEN); }); diff --git a/lib/commands/PUBSUB_NUMPAT.spec.ts b/lib/commands/PUBSUB_NUMPAT.spec.ts index 55eef6a97de..d738b916c60 100644 --- a/lib/commands/PUBSUB_NUMPAT.spec.ts +++ b/lib/commands/PUBSUB_NUMPAT.spec.ts @@ -1,5 +1,5 @@ import { strict as assert } from 'assert'; -import { TestRedisServers, itWithClient } from '../test-utils'; +import testUtils, { GLOBAL } from '../test-utils'; import { transformArguments } from './PUBSUB_NUMPAT'; describe('PUBSUB NUMPAT', () => { @@ -10,10 +10,10 @@ describe('PUBSUB NUMPAT', () => { ); }); - itWithClient(TestRedisServers.OPEN, 'client.pubSubNumPat', async client => { + testUtils.testWithClient('client.pubSubNumPat', async client => { assert.equal( await client.pubSubNumPat(), 0 ); - }); + }, GLOBAL.SERVERS.OPEN); }); diff --git a/lib/commands/PUBSUB_NUMSUB.spec.ts b/lib/commands/PUBSUB_NUMSUB.spec.ts index ef44faf2c0b..e35558ef865 100644 --- a/lib/commands/PUBSUB_NUMSUB.spec.ts +++ b/lib/commands/PUBSUB_NUMSUB.spec.ts @@ -1,5 +1,5 @@ import { strict as assert } from 'assert'; -import { TestRedisServers, itWithClient } from '../test-utils'; +import testUtils, { GLOBAL } from '../test-utils'; import { transformArguments } from './PUBSUB_NUMSUB'; describe('PUBSUB NUMSUB', () => { @@ -26,10 +26,10 @@ describe('PUBSUB NUMSUB', () => { }); }); - itWithClient(TestRedisServers.OPEN, 'client.pubSubNumSub', async client => { + testUtils.testWithClient('client.pubSubNumSub', async client => { assert.deepEqual( await client.pubSubNumSub(), Object.create(null) ); - }); + }, GLOBAL.SERVERS.OPEN); }); diff --git a/lib/commands/RANDOMKEY.spec.ts b/lib/commands/RANDOMKEY.spec.ts index 171c42be116..81c42b2fd83 100644 --- a/lib/commands/RANDOMKEY.spec.ts +++ b/lib/commands/RANDOMKEY.spec.ts @@ -1,5 +1,5 @@ import { strict as assert } from 'assert'; -import { TestRedisServers, itWithClient } from '../test-utils'; +import testUtils, { GLOBAL } from '../test-utils'; import { transformArguments } from './RANDOMKEY'; describe('RANDOMKEY', () => { @@ -10,10 +10,10 @@ describe('RANDOMKEY', () => { ); }); - itWithClient(TestRedisServers.OPEN, 'client.randomKey', async client => { + testUtils.testWithClient('client.randomKey', async client => { assert.equal( await client.randomKey(), null ); - }); + }, GLOBAL.SERVERS.OPEN); }); diff --git a/lib/commands/RENAME.spec.ts b/lib/commands/RENAME.spec.ts index 9d447c600b6..49e0af600f6 100644 --- a/lib/commands/RENAME.spec.ts +++ b/lib/commands/RENAME.spec.ts @@ -1,5 +1,5 @@ import { strict as assert } from 'assert'; -import { TestRedisServers, itWithClient } from '../test-utils'; +import testUtils, { GLOBAL } from '../test-utils'; import { transformArguments } from './RENAME'; describe('RENAME', () => { @@ -10,12 +10,12 @@ describe('RENAME', () => { ); }); - itWithClient(TestRedisServers.OPEN, 'client.rename', async client => { + testUtils.testWithClient('client.rename', async client => { await client.set('from', 'value'); assert.equal( await client.rename('from', 'to'), 'OK' ); - }); + }, GLOBAL.SERVERS.OPEN); }); diff --git a/lib/commands/RENAMENX.spec.ts b/lib/commands/RENAMENX.spec.ts index f438834b90e..6345eb5bd09 100644 --- a/lib/commands/RENAMENX.spec.ts +++ b/lib/commands/RENAMENX.spec.ts @@ -1,5 +1,5 @@ import { strict as assert } from 'assert'; -import { TestRedisServers, itWithClient } from '../test-utils'; +import testUtils, { GLOBAL } from '../test-utils'; import { transformArguments } from './RENAMENX'; describe('RENAMENX', () => { @@ -10,12 +10,12 @@ describe('RENAMENX', () => { ); }); - itWithClient(TestRedisServers.OPEN, 'client.renameNX', async client => { + testUtils.testWithClient('client.renameNX', async client => { await client.set('from', 'value'); assert.equal( await client.renameNX('from', 'to'), true ); - }); + }, GLOBAL.SERVERS.OPEN); }); diff --git a/lib/commands/ROLE.spec.ts b/lib/commands/ROLE.spec.ts index 5b647e07ca6..2e6d9b163ae 100644 --- a/lib/commands/ROLE.spec.ts +++ b/lib/commands/ROLE.spec.ts @@ -1,5 +1,5 @@ import { strict as assert } from 'assert'; -import { itWithClient, TestRedisServers } from '../test-utils'; +import testUtils, { GLOBAL } from '../test-utils'; import { transformArguments, transformReply } from './ROLE'; describe('ROLE', () => { @@ -56,7 +56,7 @@ describe('ROLE', () => { }); }); - itWithClient(TestRedisServers.OPEN, 'client.role', async client => { + testUtils.testWithClient('client.role', async client => { assert.deepEqual( await client.role(), { @@ -65,5 +65,5 @@ describe('ROLE', () => { replicas: [] } ); - }); + }, GLOBAL.SERVERS.OPEN); }); diff --git a/lib/commands/RPOP.spec.ts b/lib/commands/RPOP.spec.ts index 2a753ff1a66..6e57afa3216 100644 --- a/lib/commands/RPOP.spec.ts +++ b/lib/commands/RPOP.spec.ts @@ -1,5 +1,5 @@ import { strict as assert } from 'assert'; -import { TestRedisServers, itWithClient, itWithCluster, TestRedisClusters } from '../test-utils'; +import testUtils, { GLOBAL } from '../test-utils'; import { transformArguments } from './RPOP'; describe('RPOP', () => { @@ -10,17 +10,17 @@ describe('RPOP', () => { ); }); - itWithClient(TestRedisServers.OPEN, 'client.rPop', async client => { + testUtils.testWithClient('client.rPop', async client => { assert.equal( await client.rPop('key'), null ); - }); + }, GLOBAL.SERVERS.OPEN); - itWithCluster(TestRedisClusters.OPEN, 'cluster.rPop', async cluster => { + testUtils.testWithCluster('cluster.rPop', async cluster => { assert.equal( await cluster.rPop('key'), null ); - }); + }, GLOBAL.CLUSTERS.OPEN); }); diff --git a/lib/commands/RPOPLPUSH.spec.ts b/lib/commands/RPOPLPUSH.spec.ts index 75b5f2e18f9..cef3049bd91 100644 --- a/lib/commands/RPOPLPUSH.spec.ts +++ b/lib/commands/RPOPLPUSH.spec.ts @@ -1,5 +1,5 @@ import { strict as assert } from 'assert'; -import { TestRedisServers, itWithClient, itWithCluster, TestRedisClusters } from '../test-utils'; +import testUtils, { GLOBAL } from '../test-utils'; import { transformArguments } from './RPOPLPUSH'; describe('RPOPLPUSH', () => { @@ -10,17 +10,17 @@ describe('RPOPLPUSH', () => { ); }); - itWithClient(TestRedisServers.OPEN, 'client.rPopLPush', async client => { + testUtils.testWithClient('client.rPopLPush', async client => { assert.equal( await client.rPopLPush('source', 'destination'), null ); - }); + }, GLOBAL.SERVERS.OPEN); - itWithCluster(TestRedisClusters.OPEN, 'cluster.rPopLPush', async cluster => { + testUtils.testWithCluster('cluster.rPopLPush', async cluster => { assert.equal( await cluster.rPopLPush('{tag}source', '{tag}destination'), null ); - }); + }, GLOBAL.CLUSTERS.OPEN); }); diff --git a/lib/commands/RPOP_COUNT.spec.ts b/lib/commands/RPOP_COUNT.spec.ts index 2624540f124..3657a608039 100644 --- a/lib/commands/RPOP_COUNT.spec.ts +++ b/lib/commands/RPOP_COUNT.spec.ts @@ -1,9 +1,9 @@ import { strict as assert } from 'assert'; -import { TestRedisServers, itWithClient, itWithCluster, TestRedisClusters, describeHandleMinimumRedisVersion } from '../test-utils'; +import testUtils, { GLOBAL } from '../test-utils'; import { transformArguments } from './RPOP_COUNT'; describe('RPOP COUNT', () => { - describeHandleMinimumRedisVersion([6, 2]); + testUtils.isVersionGreaterThanHook([6, 2]); it('transformArguments', () => { assert.deepEqual( @@ -12,17 +12,17 @@ describe('RPOP COUNT', () => { ); }); - itWithClient(TestRedisServers.OPEN, 'client.rPopCount', async client => { + testUtils.testWithClient('client.rPopCount', async client => { assert.equal( await client.rPopCount('key', 1), null ); - }); + }, GLOBAL.SERVERS.OPEN); - itWithCluster(TestRedisClusters.OPEN, 'cluster.rPopCount', async cluster => { + testUtils.testWithCluster('cluster.rPopCount', async cluster => { assert.equal( await cluster.rPopCount('key', 1), null ); - }); + }, GLOBAL.CLUSTERS.OPEN); }); diff --git a/lib/commands/RPUSH.spec.ts b/lib/commands/RPUSH.spec.ts index 4336d10c9a3..afa5c1c6400 100644 --- a/lib/commands/RPUSH.spec.ts +++ b/lib/commands/RPUSH.spec.ts @@ -1,5 +1,5 @@ import { strict as assert } from 'assert'; -import { TestRedisServers, itWithClient, itWithCluster, TestRedisClusters } from '../test-utils'; +import testUtils, { GLOBAL } from '../test-utils'; import { transformArguments } from './RPUSH'; describe('RPUSH', () => { @@ -19,17 +19,17 @@ describe('RPUSH', () => { }); }); - itWithClient(TestRedisServers.OPEN, 'client.rPush', async client => { + testUtils.testWithClient('client.rPush', async client => { assert.equal( await client.rPush('key', 'element'), 1 ); - }); + }, GLOBAL.SERVERS.OPEN); - itWithCluster(TestRedisClusters.OPEN, 'cluster.rPush', async cluster => { + testUtils.testWithCluster('cluster.rPush', async cluster => { assert.equal( await cluster.rPush('key', 'element'), 1 ); - }); + }, GLOBAL.CLUSTERS.OPEN); }); diff --git a/lib/commands/RPUSHX.spec.ts b/lib/commands/RPUSHX.spec.ts index 18f91e8bef6..ee2041de6f2 100644 --- a/lib/commands/RPUSHX.spec.ts +++ b/lib/commands/RPUSHX.spec.ts @@ -1,5 +1,5 @@ import { strict as assert } from 'assert'; -import { TestRedisServers, itWithClient, itWithCluster, TestRedisClusters } from '../test-utils'; +import testUtils, { GLOBAL } from '../test-utils'; import { transformArguments } from './RPUSHX'; describe('RPUSHX', () => { @@ -19,17 +19,17 @@ describe('RPUSHX', () => { }); }); - itWithClient(TestRedisServers.OPEN, 'client.rPushX', async client => { + testUtils.testWithClient('client.rPushX', async client => { assert.equal( await client.rPushX('key', 'element'), 0 ); - }); + }, GLOBAL.SERVERS.OPEN); - itWithCluster(TestRedisClusters.OPEN, 'cluster.rPushX', async cluster => { + testUtils.testWithCluster('cluster.rPushX', async cluster => { assert.equal( await cluster.rPushX('key', 'element'), 0 ); - }); + }, GLOBAL.CLUSTERS.OPEN); }); diff --git a/lib/commands/SADD.spec.ts b/lib/commands/SADD.spec.ts index bf1ee48fe7f..4533f6f9ad5 100644 --- a/lib/commands/SADD.spec.ts +++ b/lib/commands/SADD.spec.ts @@ -1,5 +1,5 @@ import { strict as assert } from 'assert'; -import { TestRedisServers, itWithClient } from '../test-utils'; +import testUtils, { GLOBAL } from '../test-utils'; import { transformArguments } from './SADD'; describe('SADD', () => { @@ -19,10 +19,10 @@ describe('SADD', () => { }); }); - itWithClient(TestRedisServers.OPEN, 'client.sAdd', async client => { + testUtils.testWithClient('client.sAdd', async client => { assert.equal( await client.sAdd('key', 'member'), 1 ); - }); + }, GLOBAL.SERVERS.OPEN); }); diff --git a/lib/commands/SCAN.spec.ts b/lib/commands/SCAN.spec.ts index 975c4cb6d2f..7657b744e02 100644 --- a/lib/commands/SCAN.spec.ts +++ b/lib/commands/SCAN.spec.ts @@ -1,5 +1,5 @@ import { strict as assert } from 'assert'; -import { TestRedisServers, itWithClient } from '../test-utils'; +import testUtils, { GLOBAL } from '../test-utils'; import { transformArguments, transformReply } from './SCAN'; describe('SCAN', () => { @@ -72,7 +72,7 @@ describe('SCAN', () => { }); }); - itWithClient(TestRedisServers.OPEN, 'client.scan', async client => { + testUtils.testWithClient('client.scan', async client => { assert.deepEqual( await client.scan(0), { @@ -80,5 +80,5 @@ describe('SCAN', () => { keys: [] } ); - }); + }, GLOBAL.SERVERS.OPEN); }); diff --git a/lib/commands/SCARD.spec.ts b/lib/commands/SCARD.spec.ts index b6681693814..afc21c6b00c 100644 --- a/lib/commands/SCARD.spec.ts +++ b/lib/commands/SCARD.spec.ts @@ -1,5 +1,5 @@ import { strict as assert } from 'assert'; -import { TestRedisServers, itWithClient } from '../test-utils'; +import testUtils, { GLOBAL } from '../test-utils'; import { transformArguments } from './SCARD'; describe('SCARD', () => { @@ -10,10 +10,10 @@ describe('SCARD', () => { ); }); - itWithClient(TestRedisServers.OPEN, 'client.sCard', async client => { + testUtils.testWithClient('client.sCard', async client => { assert.equal( await client.sCard('key'), 0 ); - }); + }, GLOBAL.SERVERS.OPEN); }); diff --git a/lib/commands/SCRIPT_DEBUG.spec.ts b/lib/commands/SCRIPT_DEBUG.spec.ts index 9d2ad1af266..192f90f75a5 100644 --- a/lib/commands/SCRIPT_DEBUG.spec.ts +++ b/lib/commands/SCRIPT_DEBUG.spec.ts @@ -1,5 +1,5 @@ import { strict as assert } from 'assert'; -import { TestRedisServers, itWithClient } from '../test-utils'; +import testUtils, { GLOBAL } from '../test-utils'; import { transformArguments } from './SCRIPT_DEBUG'; describe('SCRIPT DEBUG', () => { @@ -10,10 +10,10 @@ describe('SCRIPT DEBUG', () => { ); }); - itWithClient(TestRedisServers.OPEN, 'client.scriptDebug', async client => { + testUtils.testWithClient('client.scriptDebug', async client => { assert.equal( await client.scriptDebug('NO'), 'OK' ); - }); + }, GLOBAL.SERVERS.OPEN); }); diff --git a/lib/commands/SCRIPT_EXISTS.spec.ts b/lib/commands/SCRIPT_EXISTS.spec.ts index b23380c7579..e0fbbcc5537 100644 --- a/lib/commands/SCRIPT_EXISTS.spec.ts +++ b/lib/commands/SCRIPT_EXISTS.spec.ts @@ -1,5 +1,5 @@ import { strict as assert } from 'assert'; -import { TestRedisServers, itWithClient } from '../test-utils'; +import testUtils, { GLOBAL } from '../test-utils'; import { transformArguments } from './SCRIPT_EXISTS'; describe('SCRIPT EXISTS', () => { @@ -19,10 +19,10 @@ describe('SCRIPT EXISTS', () => { }); }); - itWithClient(TestRedisServers.OPEN, 'client.scriptExists', async client => { + testUtils.testWithClient('client.scriptExists', async client => { assert.deepEqual( await client.scriptExists('sha1'), [false] ); - }); + }, GLOBAL.SERVERS.OPEN); }); diff --git a/lib/commands/SCRIPT_FLUSH.spec.ts b/lib/commands/SCRIPT_FLUSH.spec.ts index c77accb50a9..ae156e937d1 100644 --- a/lib/commands/SCRIPT_FLUSH.spec.ts +++ b/lib/commands/SCRIPT_FLUSH.spec.ts @@ -1,5 +1,5 @@ import { strict as assert } from 'assert'; -import { TestRedisServers, itWithClient } from '../test-utils'; +import testUtils, { GLOBAL } from '../test-utils'; import { transformArguments } from './SCRIPT_FLUSH'; describe('SCRIPT FLUSH', () => { @@ -19,10 +19,10 @@ describe('SCRIPT FLUSH', () => { }); }); - itWithClient(TestRedisServers.OPEN, 'client.scriptFlush', async client => { + testUtils.testWithClient('client.scriptFlush', async client => { assert.equal( await client.scriptFlush(), 'OK' ); - }); + }, GLOBAL.SERVERS.OPEN); }); diff --git a/lib/commands/SCRIPT_LOAD.spec.ts b/lib/commands/SCRIPT_LOAD.spec.ts index 1d7da3e9c2d..062f3c201e1 100644 --- a/lib/commands/SCRIPT_LOAD.spec.ts +++ b/lib/commands/SCRIPT_LOAD.spec.ts @@ -1,6 +1,6 @@ import { strict as assert } from 'assert'; import { scriptSha1 } from '../lua-script'; -import { TestRedisServers, itWithClient } from '../test-utils'; +import testUtils, { GLOBAL } from '../test-utils'; import { transformArguments } from './SCRIPT_LOAD'; describe('SCRIPT LOAD', () => { @@ -14,10 +14,10 @@ describe('SCRIPT LOAD', () => { ); }); - itWithClient(TestRedisServers.OPEN, 'client.scriptLoad', async client => { + testUtils.testWithClient('client.scriptLoad', async client => { assert.equal( await client.scriptLoad(SCRIPT), SCRIPT_SHA1 ); - }); + }, GLOBAL.SERVERS.OPEN); }); diff --git a/lib/commands/SDIFF.spec.ts b/lib/commands/SDIFF.spec.ts index 82ef2dac6fc..340906e9350 100644 --- a/lib/commands/SDIFF.spec.ts +++ b/lib/commands/SDIFF.spec.ts @@ -1,5 +1,5 @@ import { strict as assert } from 'assert'; -import { TestRedisServers, itWithClient } from '../test-utils'; +import testUtils, { GLOBAL } from '../test-utils'; import { transformArguments } from './SDIFF'; describe('SDIFF', () => { @@ -19,10 +19,10 @@ describe('SDIFF', () => { }); }); - itWithClient(TestRedisServers.OPEN, 'client.sDiff', async client => { + testUtils.testWithClient('client.sDiff', async client => { assert.deepEqual( await client.sDiff('key'), [] ); - }); + }, GLOBAL.SERVERS.OPEN); }); diff --git a/lib/commands/SDIFFSTORE.spec.ts b/lib/commands/SDIFFSTORE.spec.ts index 1e7f5f6f32c..263b4f43f64 100644 --- a/lib/commands/SDIFFSTORE.spec.ts +++ b/lib/commands/SDIFFSTORE.spec.ts @@ -1,5 +1,5 @@ import { strict as assert } from 'assert'; -import { TestRedisServers, itWithClient } from '../test-utils'; +import testUtils, { GLOBAL } from '../test-utils'; import { transformArguments } from './SDIFFSTORE'; describe('SDIFFSTORE', () => { @@ -19,10 +19,10 @@ describe('SDIFFSTORE', () => { }); }); - itWithClient(TestRedisServers.OPEN, 'client.sDiffStore', async client => { + testUtils.testWithClient('client.sDiffStore', async client => { assert.equal( await client.sDiffStore('destination', 'key'), 0 ); - }); + }, GLOBAL.SERVERS.OPEN); }); diff --git a/lib/commands/SET.spec.ts b/lib/commands/SET.spec.ts index 32d138f2920..353885a3097 100644 --- a/lib/commands/SET.spec.ts +++ b/lib/commands/SET.spec.ts @@ -1,5 +1,5 @@ import { strict as assert } from 'assert'; -import { TestRedisServers, itWithClient } from '../test-utils'; +import testUtils, { GLOBAL } from '../test-utils'; import { transformArguments } from './SET'; describe('SET', () => { @@ -100,14 +100,14 @@ describe('SET', () => { }); describe('client.set', () => { - itWithClient(TestRedisServers.OPEN, 'simple', async client => { + testUtils.testWithClient('simple', async client => { assert.equal( await client.set('key', 'value'), 'OK' ); - }); - - itWithClient(TestRedisServers.OPEN, 'with GET on empty key', async client => { + }, GLOBAL.SERVERS.OPEN); + + testUtils.testWithClient('with GET on empty key', async client => { assert.equal( await client.set('key', 'value', { GET: true @@ -115,7 +115,8 @@ describe('SET', () => { null ); }, { - minimumRedisVersion: [6, 2] + ...GLOBAL.SERVERS.OPEN, + minimumDockerVersion: [6, 2] }); }); }); diff --git a/lib/commands/SETBIT.spec.ts b/lib/commands/SETBIT.spec.ts index 7347913f293..43fbff7c2d9 100644 --- a/lib/commands/SETBIT.spec.ts +++ b/lib/commands/SETBIT.spec.ts @@ -1,5 +1,5 @@ import { strict as assert } from 'assert'; -import { TestRedisServers, itWithClient, TestRedisClusters, itWithCluster } from '../test-utils'; +import testUtils, { GLOBAL } from '../test-utils'; import { transformArguments } from './SETBIT'; describe('SETBIT', () => { @@ -10,17 +10,17 @@ describe('SETBIT', () => { ); }); - itWithClient(TestRedisServers.OPEN, 'client.setBit', async client => { + testUtils.testWithClient('client.setBit', async client => { assert.equal( await client.setBit('key', 0, 1), 0 ); - }); + }, GLOBAL.SERVERS.OPEN); - itWithCluster(TestRedisClusters.OPEN, 'cluster.setBit', async cluster => { + testUtils.testWithCluster('cluster.setBit', async cluster => { assert.equal( await cluster.setBit('key', 0, 1), 0 ); - }); + }, GLOBAL.CLUSTERS.OPEN); }); diff --git a/lib/commands/SETEX.spec.ts b/lib/commands/SETEX.spec.ts index 7ea55eba83c..bca298c6c04 100644 --- a/lib/commands/SETEX.spec.ts +++ b/lib/commands/SETEX.spec.ts @@ -1,5 +1,5 @@ import { strict as assert } from 'assert'; -import { TestRedisServers, itWithClient, TestRedisClusters, itWithCluster } from '../test-utils'; +import testUtils, { GLOBAL } from '../test-utils'; import { transformArguments } from './SETEX'; describe('SETEX', () => { @@ -10,17 +10,17 @@ describe('SETEX', () => { ); }); - itWithClient(TestRedisServers.OPEN, 'client.setEx', async client => { + testUtils.testWithClient('client.setEx', async client => { assert.equal( await client.setEx('key', 1, 'value'), 'OK' ); - }); + }, GLOBAL.SERVERS.OPEN); - itWithCluster(TestRedisClusters.OPEN, 'cluster.setEx', async cluster => { + testUtils.testWithCluster('cluster.setEx', async cluster => { assert.equal( await cluster.setEx('key', 1, 'value'), 'OK' ); - }); + }, GLOBAL.CLUSTERS.OPEN); }); diff --git a/lib/commands/SETNX .spec.ts b/lib/commands/SETNX .spec.ts index daf3ca6e76a..c5bdfcffa2c 100644 --- a/lib/commands/SETNX .spec.ts +++ b/lib/commands/SETNX .spec.ts @@ -1,5 +1,5 @@ import { strict as assert } from 'assert'; -import { TestRedisServers, itWithClient, TestRedisClusters, itWithCluster } from '../test-utils'; +import testUtils, { GLOBAL } from '../test-utils'; import { transformArguments } from './SETNX'; describe('SETNX', () => { @@ -10,17 +10,17 @@ describe('SETNX', () => { ); }); - itWithClient(TestRedisServers.OPEN, 'client.setNX', async client => { + testUtils.testWithClient('client.setNX', async client => { assert.equal( await client.setNX('key', 'value'), true ); - }); + }, GLOBAL.SERVERS.OPEN); - itWithCluster(TestRedisClusters.OPEN, 'cluster.setNX', async cluster => { + testUtils.testWithCluster('cluster.setNX', async cluster => { assert.equal( await cluster.setNX('key', 'value'), true ); - }); + }, GLOBAL.CLUSTERS.OPEN); }); diff --git a/lib/commands/SETRANGE.spec.ts b/lib/commands/SETRANGE.spec.ts index 766c56c5ff1..398b7730404 100644 --- a/lib/commands/SETRANGE.spec.ts +++ b/lib/commands/SETRANGE.spec.ts @@ -1,5 +1,5 @@ import { strict as assert } from 'assert'; -import { TestRedisServers, itWithClient, itWithCluster, TestRedisClusters } from '../test-utils'; +import testUtils, { GLOBAL } from '../test-utils'; import { transformArguments } from './SETRANGE'; describe('SETRANGE', () => { @@ -10,17 +10,17 @@ describe('SETRANGE', () => { ); }); - itWithClient(TestRedisServers.OPEN, 'client.setRange', async client => { + testUtils.testWithClient('client.setRange', async client => { assert.equal( await client.setRange('key', 0, 'value'), 5 ); - }); + }, GLOBAL.SERVERS.OPEN); - itWithCluster(TestRedisClusters.OPEN, 'cluster.setRange', async cluster => { + testUtils.testWithCluster('cluster.setRange', async cluster => { assert.equal( await cluster.setRange('key', 0, 'value'), 5 ); - }); + }, GLOBAL.CLUSTERS.OPEN); }); diff --git a/lib/commands/SINTER.spec.ts b/lib/commands/SINTER.spec.ts index 8fee35427cf..2324eac3ee8 100644 --- a/lib/commands/SINTER.spec.ts +++ b/lib/commands/SINTER.spec.ts @@ -1,5 +1,5 @@ import { strict as assert } from 'assert'; -import { TestRedisServers, itWithClient } from '../test-utils'; +import testUtils, { GLOBAL } from '../test-utils'; import { transformArguments } from './SINTER'; describe('SINTER', () => { @@ -19,10 +19,10 @@ describe('SINTER', () => { }); }); - itWithClient(TestRedisServers.OPEN, 'client.sInter', async client => { + testUtils.testWithClient('client.sInter', async client => { assert.deepEqual( await client.sInter('key'), [] ); - }); + }, GLOBAL.SERVERS.OPEN); }); diff --git a/lib/commands/SINTERSTORE.spec.ts b/lib/commands/SINTERSTORE.spec.ts index 013931d2312..c4a6a095e7d 100644 --- a/lib/commands/SINTERSTORE.spec.ts +++ b/lib/commands/SINTERSTORE.spec.ts @@ -1,5 +1,5 @@ import { strict as assert } from 'assert'; -import { TestRedisServers, itWithClient } from '../test-utils'; +import testUtils, { GLOBAL } from '../test-utils'; import { transformArguments } from './SINTERSTORE'; describe('SINTERSTORE', () => { @@ -19,10 +19,10 @@ describe('SINTERSTORE', () => { }); }); - itWithClient(TestRedisServers.OPEN, 'client.sInterStore', async client => { + testUtils.testWithClient('client.sInterStore', async client => { assert.equal( await client.sInterStore('destination', 'key'), 0 ); - }); + }, GLOBAL.SERVERS.OPEN); }); diff --git a/lib/commands/SISMEMBER.spec.ts b/lib/commands/SISMEMBER.spec.ts index fec4ebfc57d..8d18c83697a 100644 --- a/lib/commands/SISMEMBER.spec.ts +++ b/lib/commands/SISMEMBER.spec.ts @@ -1,10 +1,8 @@ import { strict as assert } from 'assert'; -import { TestRedisServers, itWithClient, describeHandleMinimumRedisVersion } from '../test-utils'; +import testUtils, { GLOBAL } from '../test-utils'; import { transformArguments } from './SISMEMBER'; describe('SISMEMBER', () => { - describeHandleMinimumRedisVersion([6, 2]); - it('transformArguments', () => { assert.deepEqual( transformArguments('key', 'member'), @@ -12,10 +10,10 @@ describe('SISMEMBER', () => { ); }); - itWithClient(TestRedisServers.OPEN, 'client.sIsMember', async client => { + testUtils.testWithClient('client.sIsMember', async client => { assert.equal( await client.sIsMember('key', 'member'), false ); - }); + }, GLOBAL.SERVERS.OPEN); }); diff --git a/lib/commands/SMEMBERS.spec.ts b/lib/commands/SMEMBERS.spec.ts index 2398dbaa8c6..b9c58c9eebb 100644 --- a/lib/commands/SMEMBERS.spec.ts +++ b/lib/commands/SMEMBERS.spec.ts @@ -1,5 +1,5 @@ import { strict as assert } from 'assert'; -import { TestRedisServers, itWithClient } from '../test-utils'; +import testUtils, { GLOBAL } from '../test-utils'; import { transformArguments } from './SMEMBERS'; describe('SMEMBERS', () => { @@ -10,10 +10,10 @@ describe('SMEMBERS', () => { ); }); - itWithClient(TestRedisServers.OPEN, 'client.sMembers', async client => { + testUtils.testWithClient('client.sMembers', async client => { assert.deepEqual( await client.sMembers('key'), [] ); - }); + }, GLOBAL.SERVERS.OPEN); }); diff --git a/lib/commands/SMISMEMBER.spec.ts b/lib/commands/SMISMEMBER.spec.ts index 320f60d4ba2..e3728134029 100644 --- a/lib/commands/SMISMEMBER.spec.ts +++ b/lib/commands/SMISMEMBER.spec.ts @@ -1,9 +1,9 @@ import { strict as assert } from 'assert'; -import { TestRedisServers, itWithClient, describeHandleMinimumRedisVersion } from '../test-utils'; +import testUtils, { GLOBAL } from '../test-utils'; import { transformArguments } from './SMISMEMBER'; describe('SMISMEMBER', () => { - describeHandleMinimumRedisVersion([6, 2]); + testUtils.isVersionGreaterThanHook([6, 2]); it('transformArguments', () => { assert.deepEqual( @@ -12,10 +12,10 @@ describe('SMISMEMBER', () => { ); }); - itWithClient(TestRedisServers.OPEN, 'client.smIsMember', async client => { + testUtils.testWithClient('client.smIsMember', async client => { assert.deepEqual( await client.smIsMember('key', ['1', '2']), [false, false] ); - }); + }, GLOBAL.SERVERS.OPEN); }); diff --git a/lib/commands/SMOVE.spec.ts b/lib/commands/SMOVE.spec.ts index 97e938a46bb..e3308ee8143 100644 --- a/lib/commands/SMOVE.spec.ts +++ b/lib/commands/SMOVE.spec.ts @@ -1,5 +1,5 @@ import { strict as assert } from 'assert'; -import { TestRedisServers, itWithClient } from '../test-utils'; +import testUtils, { GLOBAL } from '../test-utils'; import { transformArguments } from './SMOVE'; describe('SMOVE', () => { @@ -10,10 +10,10 @@ describe('SMOVE', () => { ); }); - itWithClient(TestRedisServers.OPEN, 'client.sMove', async client => { + testUtils.testWithClient('client.sMove', async client => { assert.equal( await client.sMove('source', 'destination', 'member'), false ); - }); + }, GLOBAL.SERVERS.OPEN); }); diff --git a/lib/commands/SORT.spec.ts b/lib/commands/SORT.spec.ts index c449e0511f0..637f48876dc 100644 --- a/lib/commands/SORT.spec.ts +++ b/lib/commands/SORT.spec.ts @@ -1,5 +1,5 @@ import { strict as assert } from 'assert'; -import { TestRedisServers, itWithClient } from '../test-utils'; +import testUtils, { GLOBAL } from '../test-utils'; import { transformArguments } from './SORT'; describe('SORT', () => { @@ -60,7 +60,7 @@ describe('SORT', () => { ['SORT', 'key', 'ASC'] ); }); - + it('with ALPHA', () => { assert.deepEqual( transformArguments('key', { @@ -97,10 +97,10 @@ describe('SORT', () => { }); }); - itWithClient(TestRedisServers.OPEN, 'client.sort', async client => { + testUtils.testWithClient('client.sort', async client => { assert.deepEqual( await client.sort('key'), [] ); - }); + }, GLOBAL.SERVERS.OPEN); }); diff --git a/lib/commands/SPOP.spec.ts b/lib/commands/SPOP.spec.ts index 238c58f4796..6a384d181fc 100644 --- a/lib/commands/SPOP.spec.ts +++ b/lib/commands/SPOP.spec.ts @@ -1,5 +1,5 @@ import { strict as assert } from 'assert'; -import { TestRedisServers, itWithClient } from '../test-utils'; +import testUtils, { GLOBAL } from '../test-utils'; import { transformArguments } from './SPOP'; describe('SPOP', () => { @@ -19,10 +19,10 @@ describe('SPOP', () => { }); }); - itWithClient(TestRedisServers.OPEN, 'client.sPop', async client => { + testUtils.testWithClient('client.sPop', async client => { assert.equal( await client.sPop('key'), null ); - }); + }, GLOBAL.SERVERS.OPEN); }); diff --git a/lib/commands/SRANDMEMBER.spec.ts b/lib/commands/SRANDMEMBER.spec.ts index 5c359f73f96..291271540be 100644 --- a/lib/commands/SRANDMEMBER.spec.ts +++ b/lib/commands/SRANDMEMBER.spec.ts @@ -1,5 +1,5 @@ import { strict as assert } from 'assert'; -import { TestRedisServers, itWithClient } from '../test-utils'; +import testUtils, { GLOBAL } from '../test-utils'; import { transformArguments } from './SRANDMEMBER'; describe('SRANDMEMBER', () => { @@ -10,10 +10,10 @@ describe('SRANDMEMBER', () => { ); }); - itWithClient(TestRedisServers.OPEN, 'client.sRandMember', async client => { + testUtils.testWithClient('client.sRandMember', async client => { assert.equal( await client.sRandMember('key'), null ); - }); + }, GLOBAL.SERVERS.OPEN); }); diff --git a/lib/commands/SRANDMEMBER_COUNT.spec.ts b/lib/commands/SRANDMEMBER_COUNT.spec.ts index 81a4fd45f31..d3d787b3e63 100644 --- a/lib/commands/SRANDMEMBER_COUNT.spec.ts +++ b/lib/commands/SRANDMEMBER_COUNT.spec.ts @@ -1,5 +1,5 @@ import { strict as assert } from 'assert'; -import { TestRedisServers, itWithClient } from '../test-utils'; +import testUtils, { GLOBAL } from '../test-utils'; import { transformArguments } from './SRANDMEMBER_COUNT'; describe('SRANDMEMBER COUNT', () => { @@ -10,10 +10,10 @@ describe('SRANDMEMBER COUNT', () => { ); }); - itWithClient(TestRedisServers.OPEN, 'client.sRandMemberCount', async client => { + testUtils.testWithClient('client.sRandMemberCount', async client => { assert.deepEqual( await client.sRandMemberCount('key', 1), [] ); - }); + }, GLOBAL.SERVERS.OPEN); }); diff --git a/lib/commands/SREM.spec.ts b/lib/commands/SREM.spec.ts index c9270624ae9..d53d7b0334d 100644 --- a/lib/commands/SREM.spec.ts +++ b/lib/commands/SREM.spec.ts @@ -1,5 +1,5 @@ import { strict as assert } from 'assert'; -import { TestRedisServers, itWithClient } from '../test-utils'; +import testUtils, { GLOBAL } from '../test-utils'; import { transformArguments } from './SREM'; describe('SREM', () => { @@ -19,10 +19,10 @@ describe('SREM', () => { }); }); - itWithClient(TestRedisServers.OPEN, 'client.sRem', async client => { + testUtils.testWithClient('client.sRem', async client => { assert.equal( await client.sRem('key', 'member'), 0 ); - }); + }, GLOBAL.SERVERS.OPEN); }); diff --git a/lib/commands/SSCAN.spec.ts b/lib/commands/SSCAN.spec.ts index 9b203ffb83e..71a90bf81d8 100644 --- a/lib/commands/SSCAN.spec.ts +++ b/lib/commands/SSCAN.spec.ts @@ -1,5 +1,5 @@ import { strict as assert } from 'assert'; -import { TestRedisServers, itWithClient } from '../test-utils'; +import testUtils, { GLOBAL } from '../test-utils'; import { transformArguments, transformReply } from './SSCAN'; describe('SSCAN', () => { @@ -62,7 +62,7 @@ describe('SSCAN', () => { }); }); - itWithClient(TestRedisServers.OPEN, 'client.sScan', async client => { + testUtils.testWithClient('client.sScan', async client => { assert.deepEqual( await client.sScan('key', 0), { @@ -70,5 +70,5 @@ describe('SSCAN', () => { members: [] } ); - }); + }, GLOBAL.SERVERS.OPEN); }); diff --git a/lib/commands/STRLEN.spec.ts b/lib/commands/STRLEN.spec.ts index 3d24e360372..519c68d3e5d 100644 --- a/lib/commands/STRLEN.spec.ts +++ b/lib/commands/STRLEN.spec.ts @@ -1,5 +1,5 @@ import { strict as assert } from 'assert'; -import { TestRedisServers, itWithClient, itWithCluster, TestRedisClusters } from '../test-utils'; +import testUtils, { GLOBAL } from '../test-utils'; import { transformArguments } from './STRLEN'; describe('STRLEN', () => { @@ -10,17 +10,17 @@ describe('STRLEN', () => { ); }); - itWithClient(TestRedisServers.OPEN, 'client.strLen', async client => { + testUtils.testWithClient('client.strLen', async client => { assert.equal( await client.strLen('key'), 0 ); - }); + }, GLOBAL.SERVERS.OPEN); - itWithCluster(TestRedisClusters.OPEN, 'cluster.strLen', async cluster => { + testUtils.testWithCluster('cluster.strLen', async cluster => { assert.equal( await cluster.strLen('key'), 0 ); - }); + }, GLOBAL.CLUSTERS.OPEN); }); diff --git a/lib/commands/SUNION.spec.ts b/lib/commands/SUNION.spec.ts index fdf97668971..2918607c1d6 100644 --- a/lib/commands/SUNION.spec.ts +++ b/lib/commands/SUNION.spec.ts @@ -1,5 +1,5 @@ import { strict as assert } from 'assert'; -import { TestRedisServers, itWithClient } from '../test-utils'; +import testUtils, { GLOBAL } from '../test-utils'; import { transformArguments } from './SUNION'; describe('SUNION', () => { @@ -19,10 +19,10 @@ describe('SUNION', () => { }); }); - itWithClient(TestRedisServers.OPEN, 'client.sUnion', async client => { + testUtils.testWithClient('client.sUnion', async client => { assert.deepEqual( await client.sUnion('key'), [] ); - }); + }, GLOBAL.SERVERS.OPEN); }); diff --git a/lib/commands/SUNIONSTORE.spec.ts b/lib/commands/SUNIONSTORE.spec.ts index 82c9a03a0b8..142533eea2b 100644 --- a/lib/commands/SUNIONSTORE.spec.ts +++ b/lib/commands/SUNIONSTORE.spec.ts @@ -1,5 +1,5 @@ import { strict as assert } from 'assert'; -import { TestRedisServers, itWithClient } from '../test-utils'; +import testUtils, { GLOBAL } from '../test-utils'; import { transformArguments } from './SUNIONSTORE'; describe('SUNIONSTORE', () => { @@ -19,10 +19,10 @@ describe('SUNIONSTORE', () => { }); }); - itWithClient(TestRedisServers.OPEN, 'client.sUnionStore', async client => { + testUtils.testWithClient('client.sUnionStore', async client => { assert.equal( await client.sUnionStore('destination', 'key'), 0 ); - }); + }, GLOBAL.SERVERS.OPEN); }); diff --git a/lib/commands/SWAPDB.spec.ts b/lib/commands/SWAPDB.spec.ts index 1a5637ae43d..add87512a64 100644 --- a/lib/commands/SWAPDB.spec.ts +++ b/lib/commands/SWAPDB.spec.ts @@ -1,5 +1,5 @@ import { strict as assert } from 'assert'; -import { itWithClient, TestRedisServers } from '../test-utils'; +import testUtils, { GLOBAL } from '../test-utils'; import { transformArguments } from './SWAPDB'; describe('SWAPDB', () => { @@ -10,10 +10,10 @@ describe('SWAPDB', () => { ); }); - itWithClient(TestRedisServers.OPEN, 'client.swapDb', async client => { + testUtils.testWithClient('client.swapDb', async client => { assert.equal( await client.swapDb(0, 1), 'OK' ); - }); + }, GLOBAL.SERVERS.OPEN); }); diff --git a/lib/commands/TIME.spec.ts b/lib/commands/TIME.spec.ts index 1a07114af4b..bbaa7942db0 100644 --- a/lib/commands/TIME.spec.ts +++ b/lib/commands/TIME.spec.ts @@ -1,5 +1,5 @@ import { strict as assert } from 'assert'; -import { TestRedisServers, itWithClient } from '../test-utils'; +import testUtils, { GLOBAL } from '../test-utils'; import { transformArguments } from './TIME'; describe('TIME', () => { @@ -10,9 +10,9 @@ describe('TIME', () => { ); }); - itWithClient(TestRedisServers.OPEN, 'client.time', async client => { + testUtils.testWithClient('client.time', async client => { const reply = await client.time(); assert.ok(reply instanceof Date); assert.ok(typeof reply.microseconds === 'number'); - }); + }, GLOBAL.SERVERS.OPEN); }); diff --git a/lib/commands/TOUCH.spec.ts b/lib/commands/TOUCH.spec.ts index c4cb4356291..578c49587d7 100644 --- a/lib/commands/TOUCH.spec.ts +++ b/lib/commands/TOUCH.spec.ts @@ -1,5 +1,5 @@ import { strict as assert } from 'assert'; -import { TestRedisServers, itWithClient } from '../test-utils'; +import testUtils, { GLOBAL } from '../test-utils'; import { transformArguments } from './TOUCH'; describe('TOUCH', () => { @@ -19,10 +19,10 @@ describe('TOUCH', () => { }); }); - itWithClient(TestRedisServers.OPEN, 'client.touch', async client => { + testUtils.testWithClient('client.touch', async client => { assert.equal( await client.touch('key'), 0 ); - }); + }, GLOBAL.SERVERS.OPEN); }); diff --git a/lib/commands/TTL.spec.ts b/lib/commands/TTL.spec.ts index bcabe8d39e5..e37a6ab714b 100644 --- a/lib/commands/TTL.spec.ts +++ b/lib/commands/TTL.spec.ts @@ -1,5 +1,5 @@ import { strict as assert } from 'assert'; -import { TestRedisServers, itWithClient } from '../test-utils'; +import testUtils, { GLOBAL } from '../test-utils'; import { transformArguments } from './TTL'; describe('TTL', () => { @@ -10,10 +10,10 @@ describe('TTL', () => { ); }); - itWithClient(TestRedisServers.OPEN, 'client.ttl', async client => { + testUtils.testWithClient('client.ttl', async client => { assert.equal( await client.ttl('key'), -2 ); - }); + }, GLOBAL.SERVERS.OPEN); }); diff --git a/lib/commands/TYPE.spec.ts b/lib/commands/TYPE.spec.ts index d40f724242d..1040bf979b3 100644 --- a/lib/commands/TYPE.spec.ts +++ b/lib/commands/TYPE.spec.ts @@ -1,5 +1,5 @@ import { strict as assert } from 'assert'; -import { TestRedisServers, itWithClient } from '../test-utils'; +import testUtils, { GLOBAL } from '../test-utils'; import { transformArguments } from './TYPE'; describe('TYPE', () => { @@ -10,10 +10,10 @@ describe('TYPE', () => { ); }); - itWithClient(TestRedisServers.OPEN, 'client.type', async client => { + testUtils.testWithClient('client.type', async client => { assert.equal( await client.type('key'), 'none' ); - }); + }, GLOBAL.SERVERS.OPEN); }); diff --git a/lib/commands/UNLINK.spec.ts b/lib/commands/UNLINK.spec.ts index a0dddf54f25..e8355407d8f 100644 --- a/lib/commands/UNLINK.spec.ts +++ b/lib/commands/UNLINK.spec.ts @@ -1,5 +1,5 @@ import { strict as assert } from 'assert'; -import { TestRedisServers, itWithClient } from '../test-utils'; +import testUtils, { GLOBAL } from '../test-utils'; import { transformArguments } from './UNLINK'; describe('UNLINK', () => { @@ -19,10 +19,10 @@ describe('UNLINK', () => { }); }); - itWithClient(TestRedisServers.OPEN, 'client.unlink', async client => { + testUtils.testWithClient('client.unlink', async client => { assert.equal( await client.unlink('key'), 0 ); - }); + }, GLOBAL.SERVERS.OPEN); }); diff --git a/lib/commands/UNWATCH.spec.ts b/lib/commands/UNWATCH.spec.ts index 07059310cbc..109ed0fa7c0 100644 --- a/lib/commands/UNWATCH.spec.ts +++ b/lib/commands/UNWATCH.spec.ts @@ -1,5 +1,5 @@ import { strict as assert } from 'assert'; -import { TestRedisServers, itWithClient } from '../test-utils'; +import testUtils, { GLOBAL } from '../test-utils'; import { transformArguments } from './UNWATCH'; describe('UNWATCH', () => { @@ -10,10 +10,10 @@ describe('UNWATCH', () => { ); }); - itWithClient(TestRedisServers.OPEN, 'client.unwatch', async client => { + testUtils.testWithClient('client.unwatch', async client => { assert.equal( await client.unwatch(), 'OK' ); - }); + }, GLOBAL.SERVERS.OPEN); }); diff --git a/lib/commands/WAIT.spec.ts b/lib/commands/WAIT.spec.ts index c3f53b7db70..c85ef598612 100644 --- a/lib/commands/WAIT.spec.ts +++ b/lib/commands/WAIT.spec.ts @@ -1,5 +1,5 @@ import { strict as assert } from 'assert'; -import { TestRedisServers, itWithClient } from '../test-utils'; +import testUtils, { GLOBAL } from '../test-utils'; import { transformArguments } from './WAIT'; describe('WAIT', () => { @@ -10,10 +10,10 @@ describe('WAIT', () => { ); }); - itWithClient(TestRedisServers.OPEN, 'client.wait', async client => { + testUtils.testWithClient('client.wait', async client => { assert.equal( await client.wait(0, 1), 0 ); - }); + }, GLOBAL.SERVERS.OPEN); }); diff --git a/lib/commands/XACK.spec.ts b/lib/commands/XACK.spec.ts index fb267c355eb..0586a5921fd 100644 --- a/lib/commands/XACK.spec.ts +++ b/lib/commands/XACK.spec.ts @@ -1,5 +1,5 @@ import { strict as assert } from 'assert'; -import { TestRedisServers, itWithClient } from '../test-utils'; +import testUtils, { GLOBAL } from '../test-utils'; import { transformArguments } from './XACK'; describe('XACK', () => { @@ -19,10 +19,10 @@ describe('XACK', () => { }); }); - itWithClient(TestRedisServers.OPEN, 'client.xAck', async client => { + testUtils.testWithClient('client.xAck', async client => { assert.equal( await client.xAck('key', 'group', '1-0'), 0 ); - }); + }, GLOBAL.SERVERS.OPEN); }); diff --git a/lib/commands/XADD.spec.ts b/lib/commands/XADD.spec.ts index 02e6888051c..4b556ecc27c 100644 --- a/lib/commands/XADD.spec.ts +++ b/lib/commands/XADD.spec.ts @@ -1,5 +1,5 @@ import { strict as assert } from 'assert'; -import { TestRedisServers, itWithClient } from '../test-utils'; +import testUtils, { GLOBAL } from '../test-utils'; import { transformArguments } from './XADD'; describe('XADD', () => { @@ -60,7 +60,7 @@ describe('XADD', () => { ['XADD', 'key', 'MAXLEN', '1000', '*','field', 'value'] ); }); - + it('with TRIM.strategyModifier', () => { assert.deepEqual( transformArguments('key', '*', { @@ -107,12 +107,12 @@ describe('XADD', () => { }); }); - itWithClient(TestRedisServers.OPEN, 'client.xAdd', async client => { + testUtils.testWithClient('client.xAdd', async client => { assert.equal( typeof await client.xAdd('key', '*', { field: 'value' }), 'string' ); - }); + }, GLOBAL.SERVERS.OPEN); }); diff --git a/lib/commands/XAUTOCLAIM.spec.ts b/lib/commands/XAUTOCLAIM.spec.ts index a0818d5c2c3..4447a06d773 100644 --- a/lib/commands/XAUTOCLAIM.spec.ts +++ b/lib/commands/XAUTOCLAIM.spec.ts @@ -1,9 +1,9 @@ import { strict as assert } from 'assert'; -import { TestRedisServers, itWithClient, describeHandleMinimumRedisVersion } from '../test-utils'; +import testUtils, { GLOBAL } from '../test-utils'; import { transformArguments } from './XAUTOCLAIM'; describe('XAUTOCLAIM', () => { - describeHandleMinimumRedisVersion([6, 2]); + testUtils.isVersionGreaterThanHook([6, 2]); describe('transformArguments', () => { it('simple', () => { @@ -23,14 +23,14 @@ describe('XAUTOCLAIM', () => { }); }); - itWithClient(TestRedisServers.OPEN, 'client.xAutoClaim', async client => { + testUtils.testWithClient('client.xAutoClaim', async client => { await Promise.all([ client.xGroupCreate('key', 'group', '$', { MKSTREAM: true }), client.xGroupCreateConsumer('key', 'group', 'consumer'), ]); - + assert.deepEqual( await client.xAutoClaim('key', 'group', 'consumer', 1, '0-0'), { @@ -38,5 +38,5 @@ describe('XAUTOCLAIM', () => { messages: [] } ); - }); + }, GLOBAL.SERVERS.OPEN); }); diff --git a/lib/commands/XAUTOCLAIM_JUSTID.spec.ts b/lib/commands/XAUTOCLAIM_JUSTID.spec.ts index d076f28751a..9aa24cd04a4 100644 --- a/lib/commands/XAUTOCLAIM_JUSTID.spec.ts +++ b/lib/commands/XAUTOCLAIM_JUSTID.spec.ts @@ -1,9 +1,9 @@ import { strict as assert } from 'assert'; -import { TestRedisServers, itWithClient, describeHandleMinimumRedisVersion } from '../test-utils'; +import testUtils, { GLOBAL } from '../test-utils'; import { transformArguments } from './XAUTOCLAIM_JUSTID'; describe('XAUTOCLAIM JUSTID', () => { - describeHandleMinimumRedisVersion([6, 2]); + testUtils.isVersionGreaterThanHook([6, 2]); it('transformArguments', () => { assert.deepEqual( @@ -12,7 +12,7 @@ describe('XAUTOCLAIM JUSTID', () => { ); }); - itWithClient(TestRedisServers.OPEN, 'client.xAutoClaimJustId', async client => { + testUtils.testWithClient('client.xAutoClaimJustId', async client => { await Promise.all([ client.xGroupCreate('key', 'group', '$', { MKSTREAM: true @@ -27,5 +27,5 @@ describe('XAUTOCLAIM JUSTID', () => { messages: [] } ); - }); + }, GLOBAL.SERVERS.OPEN); }); diff --git a/lib/commands/XCLAIM.spec.ts b/lib/commands/XCLAIM.spec.ts index ff4b445dcf3..141a62ab77a 100644 --- a/lib/commands/XCLAIM.spec.ts +++ b/lib/commands/XCLAIM.spec.ts @@ -1,5 +1,5 @@ import { strict as assert } from 'assert'; -import { TestRedisServers, itWithClient } from '../test-utils'; +import testUtils, { GLOBAL } from '../test-utils'; import { transformArguments } from './XCLAIM'; describe('XCLAIM', () => { @@ -77,14 +77,14 @@ describe('XCLAIM', () => { }); }); - itWithClient(TestRedisServers.OPEN, 'client.xClaim', async client => { + testUtils.testWithClient('client.xClaim', async client => { await client.xGroupCreate('key', 'group', '$', { MKSTREAM: true }); - + assert.deepEqual( await client.xClaim('key', 'group', 'consumer', 1, '0-0'), [] ); - }); + }, GLOBAL.SERVERS.OPEN); }); diff --git a/lib/commands/XCLAIM_JUSTID.spec.ts b/lib/commands/XCLAIM_JUSTID.spec.ts index bb31f2c4532..619f876d53d 100644 --- a/lib/commands/XCLAIM_JUSTID.spec.ts +++ b/lib/commands/XCLAIM_JUSTID.spec.ts @@ -1,5 +1,5 @@ import { strict as assert } from 'assert'; -import { TestRedisServers, itWithClient } from '../test-utils'; +import testUtils, { GLOBAL } from '../test-utils'; import { transformArguments } from './XCLAIM_JUSTID'; describe('XCLAIM JUSTID', () => { @@ -10,14 +10,14 @@ describe('XCLAIM JUSTID', () => { ); }); - itWithClient(TestRedisServers.OPEN, 'client.xClaimJustId', async client => { + testUtils.testWithClient('client.xClaimJustId', async client => { await client.xGroupCreate('key', 'group', '$', { MKSTREAM: true }); - + assert.deepEqual( await client.xClaimJustId('key', 'group', 'consumer', 1, '0-0'), [] ); - }); + }, GLOBAL.SERVERS.OPEN); }); diff --git a/lib/commands/XDEL.spec.ts b/lib/commands/XDEL.spec.ts index 1a3015538f4..00f9e2f9c67 100644 --- a/lib/commands/XDEL.spec.ts +++ b/lib/commands/XDEL.spec.ts @@ -1,5 +1,5 @@ import { strict as assert } from 'assert'; -import { TestRedisServers, itWithClient } from '../test-utils'; +import testUtils, { GLOBAL } from '../test-utils'; import { transformArguments } from './XDEL'; describe('XDEL', () => { @@ -19,10 +19,10 @@ describe('XDEL', () => { }); }); - itWithClient(TestRedisServers.OPEN, 'client.xDel', async client => { + testUtils.testWithClient('client.xDel', async client => { assert.equal( await client.xDel('key', '0-0'), 0 ); - }); + }, GLOBAL.SERVERS.OPEN); }); diff --git a/lib/commands/XGROUP_CREATE.spec.ts b/lib/commands/XGROUP_CREATE.spec.ts index fdbb796f107..57516e44cc8 100644 --- a/lib/commands/XGROUP_CREATE.spec.ts +++ b/lib/commands/XGROUP_CREATE.spec.ts @@ -1,5 +1,5 @@ import { strict as assert } from 'assert'; -import { TestRedisServers, itWithClient } from '../test-utils'; +import testUtils, { GLOBAL } from '../test-utils'; import { transformArguments } from './XGROUP_CREATE'; describe('XGROUP CREATE', () => { @@ -21,12 +21,12 @@ describe('XGROUP CREATE', () => { }); }); - itWithClient(TestRedisServers.OPEN, 'client.xGroupCreate', async client => { + testUtils.testWithClient('client.xGroupCreate', async client => { assert.equal( await client.xGroupCreate('key', 'group', '$', { MKSTREAM: true }), 'OK' ); - }); + }, GLOBAL.SERVERS.OPEN); }); diff --git a/lib/commands/XGROUP_CREATECONSUMER.spec.ts b/lib/commands/XGROUP_CREATECONSUMER.spec.ts index 5b06188e302..62443345188 100644 --- a/lib/commands/XGROUP_CREATECONSUMER.spec.ts +++ b/lib/commands/XGROUP_CREATECONSUMER.spec.ts @@ -1,9 +1,9 @@ import { strict as assert } from 'assert'; -import { TestRedisServers, itWithClient, describeHandleMinimumRedisVersion } from '../test-utils'; +import testUtils, { GLOBAL } from '../test-utils'; import { transformArguments } from './XGROUP_CREATECONSUMER'; describe('XGROUP CREATECONSUMER', () => { - describeHandleMinimumRedisVersion([6, 2]); + testUtils.isVersionGreaterThanHook([6, 2]); it('transformArguments', () => { assert.deepEqual( @@ -12,14 +12,14 @@ describe('XGROUP CREATECONSUMER', () => { ); }); - itWithClient(TestRedisServers.OPEN, 'client.xGroupCreateConsumer', async client => { + testUtils.testWithClient('client.xGroupCreateConsumer', async client => { await client.xGroupCreate('key', 'group', '$', { MKSTREAM: true }); - + assert.equal( await client.xGroupCreateConsumer('key', 'group', 'consumer'), true ); - }); + }, GLOBAL.SERVERS.OPEN); }); diff --git a/lib/commands/XGROUP_DELCONSUMER.spec.ts b/lib/commands/XGROUP_DELCONSUMER.spec.ts index c3cf3c2378a..d071aedf64f 100644 --- a/lib/commands/XGROUP_DELCONSUMER.spec.ts +++ b/lib/commands/XGROUP_DELCONSUMER.spec.ts @@ -1,5 +1,5 @@ import { strict as assert } from 'assert'; -import { TestRedisServers, itWithClient } from '../test-utils'; +import testUtils, { GLOBAL } from '../test-utils'; import { transformArguments } from './XGROUP_DELCONSUMER'; describe('XGROUP DELCONSUMER', () => { @@ -10,14 +10,14 @@ describe('XGROUP DELCONSUMER', () => { ); }); - itWithClient(TestRedisServers.OPEN, 'client.xGroupDelConsumer', async client => { + testUtils.testWithClient('client.xGroupDelConsumer', async client => { await client.xGroupCreate('key', 'group', '$', { MKSTREAM: true }); - + assert.equal( await client.xGroupDelConsumer('key', 'group', 'consumer'), 0 ); - }); + }, GLOBAL.SERVERS.OPEN); }); diff --git a/lib/commands/XGROUP_DESTROY.spec.ts b/lib/commands/XGROUP_DESTROY.spec.ts index e991bc0d667..ea8e7b7be98 100644 --- a/lib/commands/XGROUP_DESTROY.spec.ts +++ b/lib/commands/XGROUP_DESTROY.spec.ts @@ -1,5 +1,5 @@ import { strict as assert } from 'assert'; -import { TestRedisServers, itWithClient } from '../test-utils'; +import testUtils, { GLOBAL } from '../test-utils'; import { transformArguments } from './XGROUP_DESTROY'; describe('XGROUP DESTROY', () => { @@ -10,14 +10,14 @@ describe('XGROUP DESTROY', () => { ); }); - itWithClient(TestRedisServers.OPEN, 'client.xGroupDestroy', async client => { + testUtils.testWithClient('client.xGroupDestroy', async client => { await client.xGroupCreate('key', 'group', '$', { MKSTREAM: true }); - + assert.equal( await client.xGroupDestroy('key', 'group'), true ); - }); + }, GLOBAL.SERVERS.OPEN); }); diff --git a/lib/commands/XGROUP_SETID.spec.ts b/lib/commands/XGROUP_SETID.spec.ts index 0fa10cdb0b7..8df51f5401d 100644 --- a/lib/commands/XGROUP_SETID.spec.ts +++ b/lib/commands/XGROUP_SETID.spec.ts @@ -1,5 +1,5 @@ import { strict as assert } from 'assert'; -import { TestRedisServers, itWithClient } from '../test-utils'; +import testUtils, { GLOBAL } from '../test-utils'; import { transformArguments } from './XGROUP_SETID'; describe('XGROUP SETID', () => { @@ -10,7 +10,7 @@ describe('XGROUP SETID', () => { ); }); - itWithClient(TestRedisServers.OPEN, 'client.xGroupSetId', async client => { + testUtils.testWithClient('client.xGroupSetId', async client => { await client.xGroupCreate('key', 'group', '$', { MKSTREAM: true }); @@ -19,5 +19,5 @@ describe('XGROUP SETID', () => { await client.xGroupSetId('key', 'group', '0'), 'OK' ); - }); + }, GLOBAL.SERVERS.OPEN); }); diff --git a/lib/commands/XINFO_CONSUMERS.spec.ts b/lib/commands/XINFO_CONSUMERS.spec.ts index 08ef17e51aa..87c82b34f29 100644 --- a/lib/commands/XINFO_CONSUMERS.spec.ts +++ b/lib/commands/XINFO_CONSUMERS.spec.ts @@ -1,5 +1,5 @@ import { strict as assert } from 'assert'; -import { TestRedisServers, itWithClient } from '../test-utils'; +import testUtils, { GLOBAL } from '../test-utils'; import { transformArguments, transformReply } from './XINFO_CONSUMERS'; describe('XINFO CONSUMERS', () => { @@ -26,9 +26,9 @@ describe('XINFO CONSUMERS', () => { idle: 83841983 }] ); - }) + }); - itWithClient(TestRedisServers.OPEN, 'client.xInfoConsumers', async client => { + testUtils.testWithClient('client.xInfoConsumers', async client => { await client.xGroupCreate('key', 'group', '$', { MKSTREAM: true }); @@ -37,5 +37,5 @@ describe('XINFO CONSUMERS', () => { await client.xInfoConsumers('key', 'group'), [] ); - }); + }, GLOBAL.SERVERS.OPEN); }); diff --git a/lib/commands/XINFO_GROUPS.spec.ts b/lib/commands/XINFO_GROUPS.spec.ts index 8fbd86ee3ee..dea8ac58d9c 100644 --- a/lib/commands/XINFO_GROUPS.spec.ts +++ b/lib/commands/XINFO_GROUPS.spec.ts @@ -1,5 +1,5 @@ import { strict as assert } from 'assert'; -import { TestRedisServers, itWithClient } from '../test-utils'; +import testUtils, { GLOBAL } from '../test-utils'; import { transformArguments, transformReply } from './XINFO_GROUPS'; describe('XINFO GROUPS', () => { @@ -28,9 +28,9 @@ describe('XINFO GROUPS', () => { lastDeliveredId: '1588152498034-0' }] ); - }) + }); - itWithClient(TestRedisServers.OPEN, 'client.xInfoGroups', async client => { + testUtils.testWithClient('client.xInfoGroups', async client => { await client.xGroupCreate('key', 'group', '$', { MKSTREAM: true }); @@ -44,5 +44,5 @@ describe('XINFO GROUPS', () => { lastDeliveredId: '0-0' }] ); - }); + }, GLOBAL.SERVERS.OPEN); }); diff --git a/lib/commands/XINFO_STREAM.spec.ts b/lib/commands/XINFO_STREAM.spec.ts index ecab605e4e3..ca8d44f2875 100644 --- a/lib/commands/XINFO_STREAM.spec.ts +++ b/lib/commands/XINFO_STREAM.spec.ts @@ -1,5 +1,5 @@ import { strict as assert } from 'assert'; -import { TestRedisServers, itWithClient } from '../test-utils'; +import testUtils, { GLOBAL } from '../test-utils'; import { transformArguments, transformReply } from './XINFO_STREAM'; describe('XINFO STREAM', () => { @@ -51,7 +51,7 @@ describe('XINFO STREAM', () => { ); }); - itWithClient(TestRedisServers.OPEN, 'client.xInfoStream', async client => { + testUtils.testWithClient('client.xInfoStream', async client => { await client.xGroupCreate('key', 'group', '$', { MKSTREAM: true }); @@ -68,5 +68,5 @@ describe('XINFO STREAM', () => { lastEntry: null } ); - }); + }, GLOBAL.SERVERS.OPEN); }); diff --git a/lib/commands/XLEN.spec.ts b/lib/commands/XLEN.spec.ts index c4f62dbc4f2..178024ba89e 100644 --- a/lib/commands/XLEN.spec.ts +++ b/lib/commands/XLEN.spec.ts @@ -1,5 +1,5 @@ import { strict as assert } from 'assert'; -import { TestRedisServers, itWithClient } from '../test-utils'; +import testUtils, { GLOBAL } from '../test-utils'; import { transformArguments } from './XLEN'; describe('XLEN', () => { @@ -10,10 +10,10 @@ describe('XLEN', () => { ); }); - itWithClient(TestRedisServers.OPEN, 'client.xLen', async client => { + testUtils.testWithClient('client.xLen', async client => { assert.equal( await client.xLen('key'), 0 ); - }); + }, GLOBAL.SERVERS.OPEN); }); diff --git a/lib/commands/XPENDING.spec.ts b/lib/commands/XPENDING.spec.ts index 31ffeeb4230..7eb12b40efe 100644 --- a/lib/commands/XPENDING.spec.ts +++ b/lib/commands/XPENDING.spec.ts @@ -1,5 +1,5 @@ import { strict as assert } from 'assert'; -import { TestRedisServers, itWithClient } from '../test-utils'; +import testUtils, { GLOBAL } from '../test-utils'; import { transformArguments } from './XPENDING'; describe('XPENDING', () => { @@ -12,7 +12,7 @@ describe('XPENDING', () => { }); }); - itWithClient(TestRedisServers.OPEN, 'client.xPending', async client => { + testUtils.testWithClient('client.xPending', async client => { await client.xGroupCreate('key', 'group', '$', { MKSTREAM: true }); @@ -26,5 +26,5 @@ describe('XPENDING', () => { consumers: null } ); - }); + }, GLOBAL.SERVERS.OPEN); }); diff --git a/lib/commands/XPENDING_RANGE.spec.ts b/lib/commands/XPENDING_RANGE.spec.ts index 76a582d3db5..0b57c704bb0 100644 --- a/lib/commands/XPENDING_RANGE.spec.ts +++ b/lib/commands/XPENDING_RANGE.spec.ts @@ -1,5 +1,5 @@ import { strict as assert } from 'assert'; -import { TestRedisServers, itWithClient } from '../test-utils'; +import testUtils, { GLOBAL } from '../test-utils'; import { transformArguments } from './XPENDING_RANGE'; describe('XPENDING RANGE', () => { @@ -10,7 +10,7 @@ describe('XPENDING RANGE', () => { ['XPENDING', 'key', 'group', '-', '+', '1'] ); }); - + it('with IDLE', () => { assert.deepEqual( transformArguments('key', 'group', '-', '+', 1, { @@ -40,14 +40,14 @@ describe('XPENDING RANGE', () => { }); }); - itWithClient(TestRedisServers.OPEN, 'client.xPendingRange', async client => { + testUtils.testWithClient('client.xPendingRange', async client => { await client.xGroupCreate('key', 'group', '$', { MKSTREAM: true }); - + assert.deepEqual( await client.xPendingRange('key', 'group', '-', '+', 1), [] ); - }); + }, GLOBAL.SERVERS.OPEN); }); diff --git a/lib/commands/XRANGE.spec.ts b/lib/commands/XRANGE.spec.ts index 55efa9d7729..01c713e9595 100644 --- a/lib/commands/XRANGE.spec.ts +++ b/lib/commands/XRANGE.spec.ts @@ -1,5 +1,5 @@ import { strict as assert } from 'assert'; -import { TestRedisServers, itWithClient } from '../test-utils'; +import testUtils, { GLOBAL } from '../test-utils'; import { transformArguments } from './XRANGE'; describe('XRANGE', () => { @@ -10,7 +10,7 @@ describe('XRANGE', () => { ['XRANGE', 'key', '-', '+'] ); }); - + it('with COUNT', () => { assert.deepEqual( transformArguments('key', '-', '+', { @@ -21,10 +21,10 @@ describe('XRANGE', () => { }); }); - itWithClient(TestRedisServers.OPEN, 'client.xRange', async client => { + testUtils.testWithClient('client.xRange', async client => { assert.deepEqual( await client.xRange('key', '+', '-'), [] ); - }); + }, GLOBAL.SERVERS.OPEN); }); diff --git a/lib/commands/XREAD.spec.ts b/lib/commands/XREAD.spec.ts index 501571bfbeb..b607f53532e 100644 --- a/lib/commands/XREAD.spec.ts +++ b/lib/commands/XREAD.spec.ts @@ -1,5 +1,5 @@ import { strict as assert } from 'assert'; -import { TestRedisServers, itWithClient, itWithCluster, TestRedisClusters } from '../test-utils'; +import testUtils, { GLOBAL } from '../test-utils'; import { FIRST_KEY_INDEX, transformArguments } from './XREAD'; describe('XREAD', () => { @@ -81,7 +81,7 @@ describe('XREAD', () => { }); }); - itWithClient(TestRedisServers.OPEN, 'client.xRead', async client => { + testUtils.testWithClient('client.xRead', async client => { assert.equal( await client.xRead({ key: 'key', @@ -89,9 +89,9 @@ describe('XREAD', () => { }), null ); - }); + }, GLOBAL.SERVERS.OPEN); - itWithCluster(TestRedisClusters.OPEN, 'cluster.xRead', async cluster => { + testUtils.testWithCluster('cluster.xRead', async cluster => { assert.equal( await cluster.xRead({ key: 'key', @@ -99,5 +99,5 @@ describe('XREAD', () => { }), null ); - }); + }, GLOBAL.CLUSTERS.OPEN); }); diff --git a/lib/commands/XREADGROUP.spec.ts b/lib/commands/XREADGROUP.spec.ts index 8cb3147bfe7..fa196d504ad 100644 --- a/lib/commands/XREADGROUP.spec.ts +++ b/lib/commands/XREADGROUP.spec.ts @@ -1,5 +1,5 @@ import { strict as assert } from 'assert'; -import { TestRedisServers, itWithClient, itWithCluster, TestRedisClusters } from '../test-utils'; +import testUtils, { GLOBAL } from '../test-utils'; import { FIRST_KEY_INDEX, transformArguments } from './XREADGROUP'; describe('XREADGROUP', () => { @@ -94,36 +94,22 @@ describe('XREADGROUP', () => { }); }); - itWithClient(TestRedisServers.OPEN, 'null', async client => { - const [, readGroupReply] = await Promise.all([ - client.xGroupCreate('key', 'group', '$', { - MKSTREAM: true - }), - client.xReadGroup('group', 'consumer', { - key: 'key', - id: '>' - }) - ]); - - assert.equal(readGroupReply, null); - }); - - describe('cluster.xReadGroup', () => { - itWithCluster(TestRedisClusters.OPEN, 'null', async cluster => { + describe('client.xReadGroup', () => { + testUtils.testWithClient('null', async client => { const [, readGroupReply] = await Promise.all([ - cluster.xGroupCreate('key', 'group', '$', { + client.xGroupCreate('key', 'group', '$', { MKSTREAM: true }), - cluster.xReadGroup('group', 'consumer', { + client.xReadGroup('group', 'consumer', { key: 'key', id: '>' }) ]); assert.equal(readGroupReply, null); - }); + }, GLOBAL.SERVERS.OPEN); - itWithCluster(TestRedisClusters.OPEN, 'with a message', async client => { + testUtils.testWithClient('with a message', async client => { const [, id, readGroupReply] = await Promise.all([ client.xGroupCreate('key', 'group', '$', { MKSTREAM: true @@ -148,6 +134,20 @@ describe('XREADGROUP', () => { }) }] }]); - }); + }, GLOBAL.SERVERS.OPEN); }); + + testUtils.testWithCluster('cluster.xReadGroup', async cluster => { + const [, readGroupReply] = await Promise.all([ + cluster.xGroupCreate('key', 'group', '$', { + MKSTREAM: true + }), + cluster.xReadGroup('group', 'consumer', { + key: 'key', + id: '>' + }) + ]); + + assert.equal(readGroupReply, null); + }, GLOBAL.CLUSTERS.OPEN); }); diff --git a/lib/commands/XREVRANGE.spec.ts b/lib/commands/XREVRANGE.spec.ts index ba009cc2bbe..fd6e1a3adfe 100644 --- a/lib/commands/XREVRANGE.spec.ts +++ b/lib/commands/XREVRANGE.spec.ts @@ -1,5 +1,5 @@ import { strict as assert } from 'assert'; -import { TestRedisServers, itWithClient } from '../test-utils'; +import testUtils, { GLOBAL } from '../test-utils'; import { transformArguments } from './XREVRANGE'; describe('XREVRANGE', () => { @@ -10,7 +10,7 @@ describe('XREVRANGE', () => { ['XREVRANGE', 'key', '-', '+'] ); }); - + it('with COUNT', () => { assert.deepEqual( transformArguments('key', '-', '+', { @@ -21,10 +21,10 @@ describe('XREVRANGE', () => { }); }); - itWithClient(TestRedisServers.OPEN, 'client.xRevRange', async client => { + testUtils.testWithClient('client.xRevRange', async client => { assert.deepEqual( await client.xRevRange('key', '+', '-'), [] ); - }); + }, GLOBAL.SERVERS.OPEN); }); diff --git a/lib/commands/XTRIM.spec.ts b/lib/commands/XTRIM.spec.ts index 0b48fd6a2d6..a8f8078eb28 100644 --- a/lib/commands/XTRIM.spec.ts +++ b/lib/commands/XTRIM.spec.ts @@ -1,5 +1,5 @@ import { strict as assert } from 'assert'; -import { TestRedisServers, itWithClient } from '../test-utils'; +import testUtils, { GLOBAL } from '../test-utils'; import { transformArguments } from './XTRIM'; describe('XTRIM', () => { @@ -40,10 +40,10 @@ describe('XTRIM', () => { }); }); - itWithClient(TestRedisServers.OPEN, 'client.xTrim', async client => { + testUtils.testWithClient('client.xTrim', async client => { assert.equal( await client.xTrim('key', 'MAXLEN', 1), 0 ); - }); + }, GLOBAL.SERVERS.OPEN); }); diff --git a/lib/commands/ZADD.spec.ts b/lib/commands/ZADD.spec.ts index 7c017e45410..4f497bdca90 100644 --- a/lib/commands/ZADD.spec.ts +++ b/lib/commands/ZADD.spec.ts @@ -1,5 +1,5 @@ import { strict as assert } from 'assert'; -import { TestRedisServers, itWithClient } from '../test-utils'; +import testUtils, { GLOBAL } from '../test-utils'; import { transformArguments } from './ZADD'; describe('ZADD', () => { @@ -115,7 +115,7 @@ describe('ZADD', () => { }); }); - itWithClient(TestRedisServers.OPEN, 'client.zAdd', async client => { + testUtils.testWithClient('client.zAdd', async client => { assert.equal( await client.zAdd('key', { value: '1', @@ -123,5 +123,5 @@ describe('ZADD', () => { }), 1 ); - }); + }, GLOBAL.SERVERS.OPEN); }); diff --git a/lib/commands/ZCARD.spec.ts b/lib/commands/ZCARD.spec.ts index 03bfe59cfc3..2e90da772b6 100644 --- a/lib/commands/ZCARD.spec.ts +++ b/lib/commands/ZCARD.spec.ts @@ -1,5 +1,5 @@ import { strict as assert } from 'assert'; -import { TestRedisServers, itWithClient } from '../test-utils'; +import testUtils, { GLOBAL } from '../test-utils'; import { transformArguments } from './ZCARD'; describe('ZCARD', () => { @@ -10,10 +10,10 @@ describe('ZCARD', () => { ); }); - itWithClient(TestRedisServers.OPEN, 'client.zCard', async client => { + testUtils.testWithClient('client.zCard', async client => { assert.equal( await client.zCard('key'), 0 ); - }); + }, GLOBAL.SERVERS.OPEN); }); diff --git a/lib/commands/ZCOUNT.spec.ts b/lib/commands/ZCOUNT.spec.ts index e461241ce1c..e185ed3cd45 100644 --- a/lib/commands/ZCOUNT.spec.ts +++ b/lib/commands/ZCOUNT.spec.ts @@ -1,5 +1,5 @@ import { strict as assert } from 'assert'; -import { TestRedisServers, itWithClient } from '../test-utils'; +import testUtils, { GLOBAL } from '../test-utils'; import { transformArguments } from './ZCOUNT'; describe('ZCOUNT', () => { @@ -10,10 +10,10 @@ describe('ZCOUNT', () => { ); }); - itWithClient(TestRedisServers.OPEN, 'client.zCount', async client => { + testUtils.testWithClient('client.zCount', async client => { assert.equal( await client.zCount('key', 0, 1), 0 ); - }); + }, GLOBAL.SERVERS.OPEN); }); diff --git a/lib/commands/ZDIFF.spec.ts b/lib/commands/ZDIFF.spec.ts index f45b2af7edc..8bb1a101f53 100644 --- a/lib/commands/ZDIFF.spec.ts +++ b/lib/commands/ZDIFF.spec.ts @@ -1,9 +1,9 @@ import { strict as assert } from 'assert'; -import { TestRedisServers, itWithClient, describeHandleMinimumRedisVersion } from '../test-utils'; +import testUtils, { GLOBAL } from '../test-utils'; import { transformArguments } from './ZDIFF'; describe('ZDIFF', () => { - describeHandleMinimumRedisVersion([6, 2]); + testUtils.isVersionGreaterThanHook([6, 2]); describe('transformArguments', () => { it('string', () => { @@ -21,10 +21,10 @@ describe('ZDIFF', () => { }); }); - itWithClient(TestRedisServers.OPEN, 'client.zDiff', async client => { + testUtils.testWithClient('client.zDiff', async client => { assert.deepEqual( await client.zDiff('key'), [] ); - }); + }, GLOBAL.SERVERS.OPEN); }); diff --git a/lib/commands/ZDIFFSTORE.spec.ts b/lib/commands/ZDIFFSTORE.spec.ts index 5fbeebaf502..c63902b2666 100644 --- a/lib/commands/ZDIFFSTORE.spec.ts +++ b/lib/commands/ZDIFFSTORE.spec.ts @@ -1,9 +1,9 @@ import { strict as assert } from 'assert'; -import { TestRedisServers, itWithClient, describeHandleMinimumRedisVersion } from '../test-utils'; +import testUtils, { GLOBAL } from '../test-utils'; import { transformArguments } from './ZDIFFSTORE'; describe('ZDIFFSTORE', () => { - describeHandleMinimumRedisVersion([6, 2]); + testUtils.isVersionGreaterThanHook([6, 2]); describe('transformArguments', () => { it('string', () => { @@ -21,10 +21,10 @@ describe('ZDIFFSTORE', () => { }); }); - itWithClient(TestRedisServers.OPEN, 'client.zDiffStore', async client => { + testUtils.testWithClient('client.zDiffStore', async client => { assert.equal( await client.zDiffStore('destination', 'key'), 0 ); - }); + }, GLOBAL.SERVERS.OPEN); }); diff --git a/lib/commands/ZDIFF_WITHSCORES.spec.ts b/lib/commands/ZDIFF_WITHSCORES.spec.ts index 99c23108292..3b9cb725aaa 100644 --- a/lib/commands/ZDIFF_WITHSCORES.spec.ts +++ b/lib/commands/ZDIFF_WITHSCORES.spec.ts @@ -1,9 +1,9 @@ import { strict as assert } from 'assert'; -import { TestRedisServers, itWithClient, describeHandleMinimumRedisVersion } from '../test-utils'; +import testUtils, { GLOBAL } from '../test-utils'; import { transformArguments } from './ZDIFF_WITHSCORES'; describe('ZDIFF WITHSCORES', () => { - describeHandleMinimumRedisVersion([6, 2]); + testUtils.isVersionGreaterThanHook([6, 2]); describe('transformArguments', () => { it('string', () => { @@ -21,10 +21,10 @@ describe('ZDIFF WITHSCORES', () => { }); }); - itWithClient(TestRedisServers.OPEN, 'client.zDiffWithScores', async client => { + testUtils.testWithClient('client.zDiffWithScores', async client => { assert.deepEqual( await client.zDiffWithScores('key'), [] ); - }); + }, GLOBAL.SERVERS.OPEN); }); diff --git a/lib/commands/ZINCRBY.spec.ts b/lib/commands/ZINCRBY.spec.ts index 2196c63ec06..bf2a34b0965 100644 --- a/lib/commands/ZINCRBY.spec.ts +++ b/lib/commands/ZINCRBY.spec.ts @@ -1,5 +1,5 @@ import { strict as assert } from 'assert'; -import { TestRedisServers, itWithClient } from '../test-utils'; +import testUtils, { GLOBAL } from '../test-utils'; import { transformArguments } from './ZINCRBY'; describe('ZINCRBY', () => { @@ -10,10 +10,10 @@ describe('ZINCRBY', () => { ); }); - itWithClient(TestRedisServers.OPEN, 'client.zIncrBy', async client => { + testUtils.testWithClient('client.zIncrBy', async client => { assert.equal( await client.zIncrBy('destination', 1, 'member'), 1 ); - }); + }, GLOBAL.SERVERS.OPEN); }); diff --git a/lib/commands/ZINTER.spec.ts b/lib/commands/ZINTER.spec.ts index 998c46fd3e0..4d2d86c8869 100644 --- a/lib/commands/ZINTER.spec.ts +++ b/lib/commands/ZINTER.spec.ts @@ -1,10 +1,10 @@ import { strict as assert } from 'assert'; -import { TestRedisServers, itWithClient, describeHandleMinimumRedisVersion } from '../test-utils'; +import testUtils, { GLOBAL } from '../test-utils'; import { transformArguments } from './ZINTER'; describe('ZINTER', () => { - describeHandleMinimumRedisVersion([6, 2]); - + testUtils.isVersionGreaterThanHook([6, 2]); + describe('transformArguments', () => { it('key (string)', () => { assert.deepEqual( @@ -49,10 +49,10 @@ describe('ZINTER', () => { }); }); - itWithClient(TestRedisServers.OPEN, 'client.zInter', async client => { + testUtils.testWithClient('client.zInter', async client => { assert.deepEqual( await client.zInter('key'), [] ); - }); + }, GLOBAL.SERVERS.OPEN); }); diff --git a/lib/commands/ZINTERSTORE.spec.ts b/lib/commands/ZINTERSTORE.spec.ts index fca03157cb2..224961f0786 100644 --- a/lib/commands/ZINTERSTORE.spec.ts +++ b/lib/commands/ZINTERSTORE.spec.ts @@ -1,5 +1,5 @@ import { strict as assert } from 'assert'; -import { TestRedisServers, itWithClient } from '../test-utils'; +import testUtils, { GLOBAL } from '../test-utils'; import { transformArguments } from './ZINTERSTORE'; describe('ZINTERSTORE', () => { @@ -47,10 +47,10 @@ describe('ZINTERSTORE', () => { }); }); - itWithClient(TestRedisServers.OPEN, 'client.zInterStore', async client => { + testUtils.testWithClient('client.zInterStore', async client => { assert.equal( await client.zInterStore('destination', 'key'), 0 ); - }); + }, GLOBAL.SERVERS.OPEN); }); diff --git a/lib/commands/ZINTER_WITHSCORES.spec.ts b/lib/commands/ZINTER_WITHSCORES.spec.ts index f66787e3def..0eaeb26a244 100644 --- a/lib/commands/ZINTER_WITHSCORES.spec.ts +++ b/lib/commands/ZINTER_WITHSCORES.spec.ts @@ -1,9 +1,9 @@ import { strict as assert } from 'assert'; -import { TestRedisServers, itWithClient, describeHandleMinimumRedisVersion } from '../test-utils'; +import testUtils, { GLOBAL } from '../test-utils'; import { transformArguments } from './ZINTER_WITHSCORES'; describe('ZINTER WITHSCORES', () => { - describeHandleMinimumRedisVersion([6, 2]); + testUtils.isVersionGreaterThanHook([6, 2]); describe('transformArguments', () => { it('key (string)', () => { @@ -49,10 +49,10 @@ describe('ZINTER WITHSCORES', () => { }); }); - itWithClient(TestRedisServers.OPEN, 'client.zInterWithScores', async client => { + testUtils.testWithClient('client.zInterWithScores', async client => { assert.deepEqual( await client.zInterWithScores('key'), [] ); - }); + }, GLOBAL.SERVERS.OPEN); }); diff --git a/lib/commands/ZLEXCOUNT.spec.ts b/lib/commands/ZLEXCOUNT.spec.ts index b106ba0cdc5..85809f1a9a9 100644 --- a/lib/commands/ZLEXCOUNT.spec.ts +++ b/lib/commands/ZLEXCOUNT.spec.ts @@ -1,5 +1,5 @@ import { strict as assert } from 'assert'; -import { TestRedisServers, itWithClient } from '../test-utils'; +import testUtils, { GLOBAL } from '../test-utils'; import { transformArguments } from './ZLEXCOUNT'; describe('ZLEXCOUNT', () => { @@ -10,10 +10,10 @@ describe('ZLEXCOUNT', () => { ); }); - itWithClient(TestRedisServers.OPEN, 'client.zLexCount', async client => { + testUtils.testWithClient('client.zLexCount', async client => { assert.equal( await client.zLexCount('key', '[a', '[b'), 0 ); - }); + }, GLOBAL.SERVERS.OPEN); }); diff --git a/lib/commands/ZMSCORE.spec.ts b/lib/commands/ZMSCORE.spec.ts index 3cf3845392d..228c8e9d6f6 100644 --- a/lib/commands/ZMSCORE.spec.ts +++ b/lib/commands/ZMSCORE.spec.ts @@ -1,9 +1,9 @@ import { strict as assert } from 'assert'; -import { TestRedisServers, itWithClient, describeHandleMinimumRedisVersion } from '../test-utils'; +import testUtils, { GLOBAL } from '../test-utils'; import { transformArguments } from './ZMSCORE'; describe('ZMSCORE', () => { - describeHandleMinimumRedisVersion([6, 2]); + testUtils.isVersionGreaterThanHook([6, 2]); describe('transformArguments', () => { it('string', () => { @@ -21,10 +21,10 @@ describe('ZMSCORE', () => { }); }); - itWithClient(TestRedisServers.OPEN, 'client.zmScore', async client => { + testUtils.testWithClient('client.zmScore', async client => { assert.deepEqual( await client.zmScore('key', 'member'), [null] ); - }); + }, GLOBAL.SERVERS.OPEN); }); diff --git a/lib/commands/ZPOPMAX.spec.ts b/lib/commands/ZPOPMAX.spec.ts index ceab3cad1d0..18fba23a3e9 100644 --- a/lib/commands/ZPOPMAX.spec.ts +++ b/lib/commands/ZPOPMAX.spec.ts @@ -1,5 +1,5 @@ import { strict as assert } from 'assert'; -import { TestRedisServers, itWithClient } from '../test-utils'; +import testUtils, { GLOBAL } from '../test-utils'; import { transformArguments, transformReply } from './ZPOPMAX'; describe('ZPOPMAX', () => { @@ -21,14 +21,14 @@ describe('ZPOPMAX', () => { }); describe('client.zPopMax', () => { - itWithClient(TestRedisServers.OPEN, 'null', async client => { + testUtils.testWithClient('null', async client => { assert.equal( await client.zPopMax('key'), null ); - }); + }, GLOBAL.SERVERS.OPEN); - itWithClient(TestRedisServers.OPEN, 'member', async client => { + testUtils.testWithClient('member', async client => { const member = { score: 1, value: 'value' }, [, zPopMaxReply] = await Promise.all([ client.zAdd('key', member), @@ -36,6 +36,6 @@ describe('ZPOPMAX', () => { ]); assert.deepEqual(zPopMaxReply, member); - }); + }, GLOBAL.SERVERS.OPEN); }); }); diff --git a/lib/commands/ZPOPMAX_COUNT.spec.ts b/lib/commands/ZPOPMAX_COUNT.spec.ts index c0e71977ee8..b282d0d3199 100644 --- a/lib/commands/ZPOPMAX_COUNT.spec.ts +++ b/lib/commands/ZPOPMAX_COUNT.spec.ts @@ -1,5 +1,5 @@ import { strict as assert } from 'assert'; -import { TestRedisServers, itWithClient } from '../test-utils'; +import testUtils, { GLOBAL } from '../test-utils'; import { transformArguments } from './ZPOPMAX_COUNT'; describe('ZPOPMAX COUNT', () => { @@ -10,10 +10,10 @@ describe('ZPOPMAX COUNT', () => { ); }); - itWithClient(TestRedisServers.OPEN, 'client.zPopMaxCount', async client => { + testUtils.testWithClient('client.zPopMaxCount', async client => { assert.deepEqual( await client.zPopMaxCount('key', 1), [] ); - }); + }, GLOBAL.SERVERS.OPEN); }); diff --git a/lib/commands/ZPOPMIN.spec.ts b/lib/commands/ZPOPMIN.spec.ts index c69ca7c27f7..624b7054404 100644 --- a/lib/commands/ZPOPMIN.spec.ts +++ b/lib/commands/ZPOPMIN.spec.ts @@ -1,5 +1,5 @@ import { strict as assert } from 'assert'; -import { TestRedisServers, itWithClient } from '../test-utils'; +import testUtils, { GLOBAL } from '../test-utils'; import { transformArguments, transformReply } from './ZPOPMIN'; describe('ZPOPMIN', () => { @@ -21,14 +21,14 @@ describe('ZPOPMIN', () => { }); describe('client.zPopMin', () => { - itWithClient(TestRedisServers.OPEN, 'null', async client => { + testUtils.testWithClient('null', async client => { assert.equal( await client.zPopMin('key'), null ); - }); + }, GLOBAL.SERVERS.OPEN); - itWithClient(TestRedisServers.OPEN, 'member', async client => { + testUtils.testWithClient('member', async client => { const member = { score: 1, value: 'value' }, [, zPopMinReply] = await Promise.all([ client.zAdd('key', member), @@ -36,6 +36,6 @@ describe('ZPOPMIN', () => { ]); assert.deepEqual(zPopMinReply, member); - }); + }, GLOBAL.SERVERS.OPEN); }); }); diff --git a/lib/commands/ZPOPMIN_COUNT.spec.ts b/lib/commands/ZPOPMIN_COUNT.spec.ts index 1c2745a0fdf..6d40002ab72 100644 --- a/lib/commands/ZPOPMIN_COUNT.spec.ts +++ b/lib/commands/ZPOPMIN_COUNT.spec.ts @@ -1,5 +1,5 @@ import { strict as assert } from 'assert'; -import { TestRedisServers, itWithClient } from '../test-utils'; +import testUtils, { GLOBAL } from '../test-utils'; import { transformArguments } from './ZPOPMIN_COUNT'; describe('ZPOPMIN COUNT', () => { @@ -10,10 +10,10 @@ describe('ZPOPMIN COUNT', () => { ); }); - itWithClient(TestRedisServers.OPEN, 'client.zPopMinCount', async client => { + testUtils.testWithClient('client.zPopMinCount', async client => { assert.deepEqual( await client.zPopMinCount('key', 1), [] ); - }); + }, GLOBAL.SERVERS.OPEN); }); diff --git a/lib/commands/ZRANDMEMBER.spec.ts b/lib/commands/ZRANDMEMBER.spec.ts index da31641a18c..c57d26f830e 100644 --- a/lib/commands/ZRANDMEMBER.spec.ts +++ b/lib/commands/ZRANDMEMBER.spec.ts @@ -1,9 +1,9 @@ import { strict as assert } from 'assert'; -import { TestRedisServers, itWithClient, describeHandleMinimumRedisVersion } from '../test-utils'; +import testUtils, { GLOBAL } from '../test-utils'; import { transformArguments } from './ZRANDMEMBER'; describe('ZRANDMEMBER', () => { - describeHandleMinimumRedisVersion([6, 2]); + testUtils.isVersionGreaterThanHook([6, 2]); it('transformArguments', () => { assert.deepEqual( @@ -12,10 +12,10 @@ describe('ZRANDMEMBER', () => { ); }); - itWithClient(TestRedisServers.OPEN, 'client.zRandMember', async client => { + testUtils.testWithClient('client.zRandMember', async client => { assert.equal( await client.zRandMember('key'), null ); - }); + }, GLOBAL.SERVERS.OPEN); }); diff --git a/lib/commands/ZRANDMEMBER_COUNT.spec.ts b/lib/commands/ZRANDMEMBER_COUNT.spec.ts index 4c873c82d90..10db0727b23 100644 --- a/lib/commands/ZRANDMEMBER_COUNT.spec.ts +++ b/lib/commands/ZRANDMEMBER_COUNT.spec.ts @@ -1,9 +1,9 @@ import { strict as assert } from 'assert'; -import { TestRedisServers, itWithClient, describeHandleMinimumRedisVersion } from '../test-utils'; +import testUtils, { GLOBAL } from '../test-utils'; import { transformArguments } from './ZRANDMEMBER_COUNT'; describe('ZRANDMEMBER COUNT', () => { - describeHandleMinimumRedisVersion([6, 2, 5]); + testUtils.isVersionGreaterThanHook([6, 2, 5]); it('transformArguments', () => { assert.deepEqual( @@ -12,10 +12,10 @@ describe('ZRANDMEMBER COUNT', () => { ); }); - itWithClient(TestRedisServers.OPEN, 'client.zRandMemberCount', async client => { + testUtils.testWithClient('client.zRandMemberCount', async client => { assert.deepEqual( await client.zRandMemberCount('key', 1), [] ); - }); + }, GLOBAL.SERVERS.OPEN); }); diff --git a/lib/commands/ZRANDMEMBER_COUNT_WITHSCORES.spec.ts b/lib/commands/ZRANDMEMBER_COUNT_WITHSCORES.spec.ts index 55624361fb6..5b5ec1f500f 100644 --- a/lib/commands/ZRANDMEMBER_COUNT_WITHSCORES.spec.ts +++ b/lib/commands/ZRANDMEMBER_COUNT_WITHSCORES.spec.ts @@ -1,9 +1,9 @@ import { strict as assert } from 'assert'; -import { TestRedisServers, itWithClient, describeHandleMinimumRedisVersion } from '../test-utils'; +import testUtils, { GLOBAL } from '../test-utils'; import { transformArguments } from './ZRANDMEMBER_COUNT_WITHSCORES'; describe('ZRANDMEMBER COUNT WITHSCORES', () => { - describeHandleMinimumRedisVersion([6, 2, 5]); + testUtils.isVersionGreaterThanHook([6, 2, 5]); it('transformArguments', () => { assert.deepEqual( @@ -12,10 +12,10 @@ describe('ZRANDMEMBER COUNT WITHSCORES', () => { ); }); - itWithClient(TestRedisServers.OPEN, 'client.zRandMemberCountWithScores', async client => { + testUtils.testWithClient('client.zRandMemberCountWithScores', async client => { assert.deepEqual( await client.zRandMemberCountWithScores('key', 1), [] ); - }); + }, GLOBAL.SERVERS.OPEN); }); diff --git a/lib/commands/ZRANGE.spec.ts b/lib/commands/ZRANGE.spec.ts index 7347ed0ad09..03687c2ba9d 100644 --- a/lib/commands/ZRANGE.spec.ts +++ b/lib/commands/ZRANGE.spec.ts @@ -1,5 +1,5 @@ import { strict as assert } from 'assert'; -import { TestRedisServers, itWithClient } from '../test-utils'; +import testUtils, { GLOBAL } from '../test-utils'; import { transformArguments } from './ZRANGE'; describe('ZRANGE', () => { @@ -72,10 +72,10 @@ describe('ZRANGE', () => { }); }); - itWithClient(TestRedisServers.OPEN, 'client.zRange', async client => { + testUtils.testWithClient('client.zRange', async client => { assert.deepEqual( await client.zRange('src', 0, 1), [] ); - }); + }, GLOBAL.SERVERS.OPEN); }); diff --git a/lib/commands/ZRANGEBYLEX.spec.ts b/lib/commands/ZRANGEBYLEX.spec.ts index 7f687509548..fe7b7d5a16e 100644 --- a/lib/commands/ZRANGEBYLEX.spec.ts +++ b/lib/commands/ZRANGEBYLEX.spec.ts @@ -1,5 +1,5 @@ import { strict as assert } from 'assert'; -import { TestRedisServers, itWithClient } from '../test-utils'; +import testUtils, { GLOBAL } from '../test-utils'; import { transformArguments } from './ZRANGEBYLEX'; describe('ZRANGEBYLEX', () => { @@ -24,10 +24,10 @@ describe('ZRANGEBYLEX', () => { }); }); - itWithClient(TestRedisServers.OPEN, 'client.zRangeByLex', async client => { + testUtils.testWithClient('client.zRangeByLex', async client => { assert.deepEqual( await client.zRangeByLex('src', '-', '+'), [] ); - }); + }, GLOBAL.SERVERS.OPEN); }); diff --git a/lib/commands/ZRANGEBYSCORE.spec.ts b/lib/commands/ZRANGEBYSCORE.spec.ts index 0419b232563..a3484326306 100644 --- a/lib/commands/ZRANGEBYSCORE.spec.ts +++ b/lib/commands/ZRANGEBYSCORE.spec.ts @@ -1,5 +1,5 @@ import { strict as assert } from 'assert'; -import { TestRedisServers, itWithClient } from '../test-utils'; +import testUtils, { GLOBAL } from '../test-utils'; import { transformArguments } from './ZRANGEBYSCORE'; describe('ZRANGEBYSCORE', () => { @@ -24,10 +24,10 @@ describe('ZRANGEBYSCORE', () => { }); }); - itWithClient(TestRedisServers.OPEN, 'client.zRangeByScore', async client => { + testUtils.testWithClient('client.zRangeByScore', async client => { assert.deepEqual( await client.zRangeByScore('src', 0, 1), [] ); - }); + }, GLOBAL.SERVERS.OPEN); }); diff --git a/lib/commands/ZRANGEBYSCORE_WITHSCORES.spec.ts b/lib/commands/ZRANGEBYSCORE_WITHSCORES.spec.ts index 84d1aeb0aad..3552d3e2535 100644 --- a/lib/commands/ZRANGEBYSCORE_WITHSCORES.spec.ts +++ b/lib/commands/ZRANGEBYSCORE_WITHSCORES.spec.ts @@ -1,5 +1,5 @@ import { strict as assert } from 'assert'; -import { TestRedisServers, itWithClient } from '../test-utils'; +import testUtils, { GLOBAL } from '../test-utils'; import { transformArguments } from './ZRANGEBYSCORE_WITHSCORES'; describe('ZRANGEBYSCORE WITHSCORES', () => { @@ -24,10 +24,10 @@ describe('ZRANGEBYSCORE WITHSCORES', () => { }); }); - itWithClient(TestRedisServers.OPEN, 'client.zRangeByScoreWithScores', async client => { + testUtils.testWithClient('client.zRangeByScoreWithScores', async client => { assert.deepEqual( await client.zRangeByScoreWithScores('src', 0, 1), [] ); - }); + }, GLOBAL.SERVERS.OPEN); }); diff --git a/lib/commands/ZRANGESTORE.spec.ts b/lib/commands/ZRANGESTORE.spec.ts index 54055656409..7af253e539f 100644 --- a/lib/commands/ZRANGESTORE.spec.ts +++ b/lib/commands/ZRANGESTORE.spec.ts @@ -1,9 +1,9 @@ import { strict as assert } from 'assert'; -import { TestRedisServers, itWithClient, describeHandleMinimumRedisVersion } from '../test-utils'; +import testUtils, { GLOBAL } from '../test-utils'; import { transformArguments, transformReply } from './ZRANGESTORE'; describe('ZRANGESTORE', () => { - describeHandleMinimumRedisVersion([6, 2]); + testUtils.isVersionGreaterThanHook([6, 2]); describe('transformArguments', () => { it('simple', () => { @@ -71,13 +71,14 @@ describe('ZRANGESTORE', () => { describe('transformReply', () => { it('should throw TypeError when reply is not a number', () => { assert.throws( + // eslint-disable-next-line @typescript-eslint/no-explicit-any () => (transformReply as any)([]), TypeError ); }); }); - itWithClient(TestRedisServers.OPEN, 'client.zRangeStore', async client => { + testUtils.testWithClient('client.zRangeStore', async client => { await client.zAdd('src', { score: 0.5, value: 'value' @@ -87,5 +88,5 @@ describe('ZRANGESTORE', () => { await client.zRangeStore('dst', 'src', 0, 1), 1 ); - }); + }, GLOBAL.SERVERS.OPEN); }); diff --git a/lib/commands/ZRANGE_WITHSCORES.spec.ts b/lib/commands/ZRANGE_WITHSCORES.spec.ts index 4c739b3d3b3..d9b07e19dda 100644 --- a/lib/commands/ZRANGE_WITHSCORES.spec.ts +++ b/lib/commands/ZRANGE_WITHSCORES.spec.ts @@ -1,5 +1,5 @@ import { strict as assert } from 'assert'; -import { TestRedisServers, itWithClient } from '../test-utils'; +import testUtils, { GLOBAL } from '../test-utils'; import { transformArguments } from './ZRANGE_WITHSCORES'; describe('ZRANGE WITHSCORES', () => { @@ -56,10 +56,10 @@ describe('ZRANGE WITHSCORES', () => { }); }); - itWithClient(TestRedisServers.OPEN, 'client.zRangeWithScores', async client => { + testUtils.testWithClient('client.zRangeWithScores', async client => { assert.deepEqual( await client.zRangeWithScores('src', 0, 1), [] ); - }); + }, GLOBAL.SERVERS.OPEN); }); diff --git a/lib/commands/ZRANK.spec.ts b/lib/commands/ZRANK.spec.ts index 8dd9c924ccc..0c81517a7d6 100644 --- a/lib/commands/ZRANK.spec.ts +++ b/lib/commands/ZRANK.spec.ts @@ -1,5 +1,5 @@ import { strict as assert } from 'assert'; -import { TestRedisServers, itWithClient } from '../test-utils'; +import testUtils, { GLOBAL } from '../test-utils'; import { transformArguments } from './ZRANK'; describe('ZRANK', () => { @@ -10,10 +10,10 @@ describe('ZRANK', () => { ); }); - itWithClient(TestRedisServers.OPEN, 'client.zRank', async client => { + testUtils.testWithClient('client.zRank', async client => { assert.equal( await client.zRank('key', 'member'), null ); - }); + }, GLOBAL.SERVERS.OPEN); }); diff --git a/lib/commands/ZREM.spec.ts b/lib/commands/ZREM.spec.ts index d613832035d..3ac001708a0 100644 --- a/lib/commands/ZREM.spec.ts +++ b/lib/commands/ZREM.spec.ts @@ -1,5 +1,5 @@ import { strict as assert } from 'assert'; -import { TestRedisServers, itWithClient } from '../test-utils'; +import testUtils, { GLOBAL } from '../test-utils'; import { transformArguments } from './ZREM'; describe('ZREM', () => { @@ -19,10 +19,10 @@ describe('ZREM', () => { }); }); - itWithClient(TestRedisServers.OPEN, 'client.zRem', async client => { + testUtils.testWithClient('client.zRem', async client => { assert.equal( await client.zRem('key', 'member'), 0 ); - }); + }, GLOBAL.SERVERS.OPEN); }); diff --git a/lib/commands/ZREMRANGEBYLEX.spec.ts b/lib/commands/ZREMRANGEBYLEX.spec.ts index 7aae059480c..b59c9e9f3b0 100644 --- a/lib/commands/ZREMRANGEBYLEX.spec.ts +++ b/lib/commands/ZREMRANGEBYLEX.spec.ts @@ -1,5 +1,5 @@ import { strict as assert } from 'assert'; -import { TestRedisServers, itWithClient } from '../test-utils'; +import testUtils, { GLOBAL } from '../test-utils'; import { transformArguments } from './ZREMRANGEBYLEX'; describe('ZREMRANGEBYLEX', () => { @@ -10,10 +10,10 @@ describe('ZREMRANGEBYLEX', () => { ); }); - itWithClient(TestRedisServers.OPEN, 'client.zRemRangeByLex', async client => { + testUtils.testWithClient('client.zRemRangeByLex', async client => { assert.equal( await client.zRemRangeByLex('key', '[a', '[b'), 0 ); - }); + }, GLOBAL.SERVERS.OPEN); }); diff --git a/lib/commands/ZREMRANGEBYRANK.spec.ts b/lib/commands/ZREMRANGEBYRANK.spec.ts index 401b57c8e2a..c659dadb790 100644 --- a/lib/commands/ZREMRANGEBYRANK.spec.ts +++ b/lib/commands/ZREMRANGEBYRANK.spec.ts @@ -1,5 +1,5 @@ import { strict as assert } from 'assert'; -import { TestRedisServers, itWithClient } from '../test-utils'; +import testUtils, { GLOBAL } from '../test-utils'; import { transformArguments } from './ZREMRANGEBYRANK'; describe('ZREMRANGEBYRANK', () => { @@ -10,10 +10,10 @@ describe('ZREMRANGEBYRANK', () => { ); }); - itWithClient(TestRedisServers.OPEN, 'client.zRemRangeByRank', async client => { + testUtils.testWithClient('client.zRemRangeByRank', async client => { assert.equal( await client.zRemRangeByRank('key', 0, 1), 0 ); - }); + }, GLOBAL.SERVERS.OPEN); }); diff --git a/lib/commands/ZREMRANGEBYSCORE.spec.ts b/lib/commands/ZREMRANGEBYSCORE.spec.ts index 141392e772b..988fd7690c9 100644 --- a/lib/commands/ZREMRANGEBYSCORE.spec.ts +++ b/lib/commands/ZREMRANGEBYSCORE.spec.ts @@ -1,5 +1,5 @@ import { strict as assert } from 'assert'; -import { TestRedisServers, itWithClient } from '../test-utils'; +import testUtils, { GLOBAL } from '../test-utils'; import { transformArguments } from './ZREMRANGEBYSCORE'; describe('ZREMRANGEBYSCORE', () => { @@ -10,10 +10,10 @@ describe('ZREMRANGEBYSCORE', () => { ); }); - itWithClient(TestRedisServers.OPEN, 'client.zRemRangeByScore', async client => { + testUtils.testWithClient('client.zRemRangeByScore', async client => { assert.equal( await client.zRemRangeByScore('key', 0, 1), 0 ); - }); + }, GLOBAL.SERVERS.OPEN); }); diff --git a/lib/commands/ZREVRANK.spec.ts b/lib/commands/ZREVRANK.spec.ts index 727a61a35a6..d9fef0d70a4 100644 --- a/lib/commands/ZREVRANK.spec.ts +++ b/lib/commands/ZREVRANK.spec.ts @@ -1,5 +1,5 @@ import { strict as assert } from 'assert'; -import { TestRedisServers, itWithClient } from '../test-utils'; +import testUtils, { GLOBAL } from '../test-utils'; import { transformArguments } from './ZREVRANK'; describe('ZREVRANK', () => { @@ -10,10 +10,10 @@ describe('ZREVRANK', () => { ); }); - itWithClient(TestRedisServers.OPEN, 'client.zRevRank', async client => { + testUtils.testWithClient('client.zRevRank', async client => { assert.equal( await client.zRevRank('key', 'member'), null ); - }); + }, GLOBAL.SERVERS.OPEN); }); diff --git a/lib/commands/ZSCAN.spec.ts b/lib/commands/ZSCAN.spec.ts index 3ff0c0a52b2..afa221a1ef3 100644 --- a/lib/commands/ZSCAN.spec.ts +++ b/lib/commands/ZSCAN.spec.ts @@ -1,5 +1,5 @@ import { strict as assert } from 'assert'; -import { TestRedisServers, itWithClient } from '../test-utils'; +import testUtils, { GLOBAL } from '../test-utils'; import { transformArguments, transformReply } from './ZSCAN'; describe('ZSCAN', () => { @@ -65,7 +65,7 @@ describe('ZSCAN', () => { }); }); - itWithClient(TestRedisServers.OPEN, 'client.zScan', async client => { + testUtils.testWithClient('client.zScan', async client => { assert.deepEqual( await client.zScan('key', 0), { @@ -73,5 +73,5 @@ describe('ZSCAN', () => { members: [] } ); - }); + }, GLOBAL.SERVERS.OPEN); }); diff --git a/lib/commands/ZSCORE.spec.ts b/lib/commands/ZSCORE.spec.ts index d346a2e2c81..fe2a1c6a7c5 100644 --- a/lib/commands/ZSCORE.spec.ts +++ b/lib/commands/ZSCORE.spec.ts @@ -1,5 +1,5 @@ import { strict as assert } from 'assert'; -import { TestRedisServers, itWithClient } from '../test-utils'; +import testUtils, { GLOBAL } from '../test-utils'; import { transformArguments } from './ZSCORE'; describe('ZSCORE', () => { @@ -10,10 +10,10 @@ describe('ZSCORE', () => { ); }); - itWithClient(TestRedisServers.OPEN, 'client.zScore', async client => { + testUtils.testWithClient('client.zScore', async client => { assert.equal( await client.zScore('key', 'member'), null ); - }); + }, GLOBAL.SERVERS.OPEN); }); diff --git a/lib/commands/ZUNION.spec.ts b/lib/commands/ZUNION.spec.ts index 12e92833931..c53498cbf65 100644 --- a/lib/commands/ZUNION.spec.ts +++ b/lib/commands/ZUNION.spec.ts @@ -1,9 +1,9 @@ import { strict as assert } from 'assert'; -import { TestRedisServers, itWithClient, describeHandleMinimumRedisVersion } from '../test-utils'; +import testUtils, { GLOBAL } from '../test-utils'; import { transformArguments } from './ZUNION'; describe('ZUNION', () => { - describeHandleMinimumRedisVersion([6, 2]); + testUtils.isVersionGreaterThanHook([6, 2]); describe('transformArguments', () => { it('key (string)', () => { @@ -39,10 +39,10 @@ describe('ZUNION', () => { }); }); - itWithClient(TestRedisServers.OPEN, 'client.zUnion', async client => { + testUtils.testWithClient('client.zUnion', async client => { assert.deepEqual( await client.zUnion('key'), [] ); - }); + }, GLOBAL.SERVERS.OPEN); }); diff --git a/lib/commands/ZUNIONSTORE.spec.ts b/lib/commands/ZUNIONSTORE.spec.ts index 0c4d7a3006b..8f11828b221 100644 --- a/lib/commands/ZUNIONSTORE.spec.ts +++ b/lib/commands/ZUNIONSTORE.spec.ts @@ -1,5 +1,5 @@ import { strict as assert } from 'assert'; -import { TestRedisServers, itWithClient } from '../test-utils'; +import testUtils, { GLOBAL } from '../test-utils'; import { transformArguments } from './ZUNIONSTORE'; describe('ZUNIONSTORE', () => { @@ -47,10 +47,10 @@ describe('ZUNIONSTORE', () => { }); }); - itWithClient(TestRedisServers.OPEN, 'client.zUnionStore', async client => { + testUtils.testWithClient('client.zUnionStore', async client => { assert.equal( await client.zUnionStore('destination', 'key'), 0 ); - }); + }, GLOBAL.SERVERS.OPEN); }); diff --git a/lib/commands/ZUNION_WITHSCORES.spec.ts b/lib/commands/ZUNION_WITHSCORES.spec.ts index d9c65ba5e4b..3786a97963d 100644 --- a/lib/commands/ZUNION_WITHSCORES.spec.ts +++ b/lib/commands/ZUNION_WITHSCORES.spec.ts @@ -1,9 +1,9 @@ import { strict as assert } from 'assert'; -import { TestRedisServers, itWithClient, describeHandleMinimumRedisVersion } from '../test-utils'; +import testUtils, { GLOBAL } from '../test-utils'; import { transformArguments } from './ZUNION_WITHSCORES'; describe('ZUNION WITHSCORES', () => { - describeHandleMinimumRedisVersion([6, 2]); + testUtils.isVersionGreaterThanHook([6, 2]); describe('transformArguments', () => { it('key (string)', () => { @@ -39,10 +39,10 @@ describe('ZUNION WITHSCORES', () => { }); }); - itWithClient(TestRedisServers.OPEN, 'client.zUnionWithScores', async client => { + testUtils.testWithClient('client.zUnionWithScores', async client => { assert.deepEqual( await client.zUnionWithScores('key'), [] ); - }); + }, GLOBAL.SERVERS.OPEN); }); diff --git a/lib/commands/generic-transformers.spec.ts b/lib/commands/generic-transformers.spec.ts index bdc3ee938cd..887d8720def 100644 --- a/lib/commands/generic-transformers.spec.ts +++ b/lib/commands/generic-transformers.spec.ts @@ -268,7 +268,7 @@ describe('Generic Transformers', () => { }) }] }] - ) + ); }); }); diff --git a/lib/errors.ts b/lib/errors.ts index 3f5fe40c201..79a438aa1d8 100644 --- a/lib/errors.ts +++ b/lib/errors.ts @@ -27,3 +27,9 @@ export class DisconnectsClientError extends Error { super('Disconnects client'); } } + +export class SocketClosedUnexpectedlyError extends Error { + constructor() { + super('Socket closed unexpectedly'); + } +} diff --git a/lib/test-utils.ts b/lib/test-utils.ts deleted file mode 100644 index 42eb9df500b..00000000000 --- a/lib/test-utils.ts +++ /dev/null @@ -1,384 +0,0 @@ -import { strict as assert } from 'assert'; -import RedisClient, { RedisClientOptions, RedisClientType } from './client'; -import { execSync, spawn } from 'child_process'; -import { once } from 'events'; -import which from 'which'; -import { SinonSpy } from 'sinon'; -import RedisCluster, { RedisClusterOptions, RedisClusterType } from './cluster'; -import { promises as fs } from 'fs'; -import { Context as MochaContext } from 'mocha'; -import { promiseTimeout } from './utils'; -import { RedisModules, RedisScripts } from './commands'; - -type RedisVersion = [major: number, minor: number, patch: number]; - -type PartialRedisVersion = RedisVersion | [major: number, minor: number] | [major: number]; - -const REDIS_PATH = which.sync('redis-server'); -export const REDIS_VERSION = getRedisVersion(); - -function getRedisVersion(): RedisVersion { - const raw = execSync(`${REDIS_PATH} -v`).toString(), - indexOfVersion = raw.indexOf('v='); - - if (indexOfVersion === -1) { - throw new Error('Unknown redis version'); - } - - const start = indexOfVersion + 2; - return raw.substring( - start, - raw.indexOf(' ', start) - ).split('.', 3).map(Number) as RedisVersion; -} - -export function isRedisVersionGreaterThan(minimumVersion: PartialRedisVersion | undefined): boolean { - if (minimumVersion === undefined) return true; - - const lastIndex = minimumVersion.length - 1; - for (let i = 0; i < lastIndex; i++) { - if (REDIS_VERSION[i] > minimumVersion[i]) { - return true; - } else if (minimumVersion[i] > REDIS_VERSION[i]) { - return false; - } - } - - return REDIS_VERSION[lastIndex] >= minimumVersion[lastIndex]; -} - -export enum TestRedisServers { - OPEN, - PASSWORD -} - -export const TEST_REDIS_SERVERS: Record, 'modules' | 'scripts'>> = {}; - -export enum TestRedisClusters { - OPEN -} - -export const TEST_REDIS_CLUSTERES: Record, 'modules' | 'scripts'>> = {}; - -let port = 6379; - -interface SpawnRedisServerResult { - port: number; - cleanup: () => Promise; -} - -async function spawnRedisServer(args?: Array): Promise { - const currentPort = port++, - process = spawn(REDIS_PATH, [ - '--save', - '', - '--port', - currentPort.toString(), - ...(args ?? []) - ]); - - process - .once('error', err => console.error('Redis process error', err)) - .once('close', code => console.error(`Redis process closed unexpectedly with code ${code}`)); - - for await (const chunk of process.stdout) { - if (chunk.toString().includes('Ready to accept connections')) { - break; - } - } - - if (process.exitCode !== null) { - throw new Error('Error while spawning redis server'); - } - - return { - port: currentPort, - async cleanup(): Promise { - process.removeAllListeners('close'); - assert.ok(process.kill()); - await once(process, 'close'); - } - }; -} - -async function spawnGlobalRedisServer(args?: Array): Promise { - const { port, cleanup } = await spawnRedisServer(args); - after(cleanup); - return port; -} - -const SLOTS = 16384; - -interface SpawnRedisClusterNodeResult extends SpawnRedisServerResult { - client: RedisClientType -} - -async function spawnRedisClusterNode( - type: TestRedisClusters | null, - nodeIndex: number, - fromSlot: number, - toSlot: number, - args?: Array -): Promise { - const clusterConfigFile = `/tmp/${type}-${nodeIndex}.conf`, - { port, cleanup: originalCleanup } = await spawnRedisServer([ - '--cluster-enabled', - 'yes', - '--cluster-node-timeout', - '5000', - '--cluster-config-file', - clusterConfigFile, - ...(args ?? []) - ]); - - const client = RedisClient.create({ - socket: { - port - } - }); - - await client.connect(); - - const range = []; - for (let i = fromSlot; i < toSlot; i++) { - range.push(i); - } - - await Promise.all([ - client.clusterFlushSlots(), - client.clusterAddSlots(range) - ]); - - return { - port, - async cleanup(): Promise { - await originalCleanup(); - - try { - await fs.unlink(clusterConfigFile); - } catch (err: any) { - if (err.code === 'ENOENT') return; - - throw err; - } - }, - client - }; -} - -export async function spawnRedisCluster(type: TestRedisClusters | null, numberOfNodes: number, args?: Array): Promise> { - const spawnPromises = [], - slotsPerNode = Math.floor(SLOTS / numberOfNodes); - for (let i = 0; i < numberOfNodes; i++) { - const fromSlot = i * slotsPerNode; - spawnPromises.push( - spawnRedisClusterNode( - type, - i, - fromSlot, - i === numberOfNodes - 1 ? SLOTS : fromSlot + slotsPerNode, - args - ) - ); - } - - const spawnResults = await Promise.all(spawnPromises), - meetPromises = []; - for (let i = 1; i < spawnResults.length; i++) { - meetPromises.push( - spawnResults[i].client.clusterMeet( - '127.0.0.1', - spawnResults[i - 1].port - ) - ); - } - - await Promise.all(meetPromises); - - while (!(await clusterIsReady(spawnResults))) { - await promiseTimeout(100); - } - - await Promise.all( - spawnResults.map(result => result.client.disconnect()) - ); - - return spawnResults; -} - -async function clusterIsReady(spawnResults: Array): Promise { - const nodesClusetrInfo = await Promise.all( - spawnResults.map(result => result.client.clusterInfo()) - ); - - return nodesClusetrInfo.every(({ state }) => state === 'ok'); -} - -export async function spawnGlobalRedisCluster(type: TestRedisClusters | null, numberOfNodes: number, args?: Array): Promise> { - const results = await spawnRedisCluster(type, numberOfNodes, args); - - after(() => Promise.all( - results.map(({ cleanup }) => cleanup()) - )); - - return results.map(({ port }) => port); -} - -async function spawnOpenServer(): Promise { - TEST_REDIS_SERVERS[TestRedisServers.OPEN] = { - socket: { - port: await spawnGlobalRedisServer() - } - }; -} - -async function spawnPasswordServer(): Promise { - TEST_REDIS_SERVERS[TestRedisServers.PASSWORD] = { - socket: { - port: await spawnGlobalRedisServer(['--requirepass', 'password']), - }, - password: 'password' - }; - - if (isRedisVersionGreaterThan([6])) { - TEST_REDIS_SERVERS[TestRedisServers.PASSWORD].username = 'default'; - } -} - -async function spawnOpenCluster(): Promise { - TEST_REDIS_CLUSTERES[TestRedisClusters.OPEN] = { - rootNodes: (await spawnGlobalRedisCluster(TestRedisClusters.OPEN, 3)).map(port => ({ - socket: { - port - } - })) - }; -} - -before(function () { - this.timeout(10000); - - return Promise.all([ - spawnOpenServer(), - spawnPasswordServer(), - spawnOpenCluster() - ]); -}); - -interface RedisTestOptions { - minimumRedisVersion?: PartialRedisVersion; -} - -export function handleMinimumRedisVersion(mochaContext: MochaContext, minimumVersion: PartialRedisVersion | undefined): boolean { - if (isRedisVersionGreaterThan(minimumVersion)) { - return false; - } - - mochaContext.skip(); - return true; -} - -export function describeHandleMinimumRedisVersion(minimumVersion: PartialRedisVersion): void { - before(function () { - handleMinimumRedisVersion(this, minimumVersion); - }); -} - -interface RedisClientTestOptions extends RedisTestOptions { - clientOptions?: RedisClientOptions; -} - -export function itWithClient( - type: TestRedisServers, - title: string, - fn: (client: RedisClientType) => Promise, - options?: RedisClientTestOptions -): void { - it(title, async function () { - if (handleMinimumRedisVersion(this, options?.minimumRedisVersion)) return; - - const client = RedisClient.create({ - ...TEST_REDIS_SERVERS[type], - ...options?.clientOptions - }); - - await client.connect(); - - try { - await client.flushAll(); - await fn(client); - } finally { - await client.flushAll(); - await client.disconnect(); - } - }); -} - -export function itWithCluster( - type: TestRedisClusters, - title: string, - fn: (cluster: RedisClusterType) => Promise, - options?: RedisTestOptions -): void { - it(title, async function () { - if (handleMinimumRedisVersion(this, options?.minimumRedisVersion)) return; - - const cluster = RedisCluster.create(TEST_REDIS_CLUSTERES[type]); - - await cluster.connect(); - - try { - await clusterFlushAll(cluster); - await fn(cluster); - } finally { - await clusterFlushAll(cluster); - await cluster.disconnect(); - } - }); -} - -export function itWithDedicatedCluster(title: string, fn: (cluster: RedisClusterType) => Promise): void { - it(title, async function () { - this.timeout(10000); - - const spawnResults = await spawnRedisCluster(null, 3), - cluster = RedisCluster.create({ - rootNodes: [{ - socket: { - port: spawnResults[0].port - } - }] - }); - - await cluster.connect(); - - try { - await fn(cluster); - } finally { - await cluster.disconnect(); - - for (const { cleanup } of spawnResults) { - await cleanup(); - } - } - }); -} - -async function clusterFlushAll(cluster: RedisCluster): Promise { - await Promise.all( - cluster.getMasters().map(({ client }) => client.flushAll()) - ); -} - -export async function waitTillBeenCalled(spy: SinonSpy): Promise { - const start = process.hrtime.bigint(), - calls = spy.callCount; - - do { - if (process.hrtime.bigint() - start > 1_000_000_000) { - throw new Error('Waiting for more than 1 second'); - } - - await promiseTimeout(1); - } while (spy.callCount === calls) -} diff --git a/lib/test-utils/dockers.ts b/lib/test-utils/dockers.ts new file mode 100644 index 00000000000..3e48618ee46 --- /dev/null +++ b/lib/test-utils/dockers.ts @@ -0,0 +1,215 @@ +import { createConnection } from 'net'; +import { once } from 'events'; +import { RedisModules, RedisScripts } from '../commands'; +import RedisClient, { RedisClientType } from '../client'; +import { promiseTimeout } from '../utils'; +import { promisify } from 'util'; +import { exec } from 'child_process'; +const execAsync = promisify(exec); + +interface ErrorWithCode extends Error { + code: string; +} + +async function isPortAvailable(port: number): Promise { + try { + const socket = createConnection({ port }); + await once(socket, 'connect'); + socket.end(); + } catch (err) { + if (err instanceof Error && (err as ErrorWithCode).code === 'ECONNREFUSED') { + return true; + } + } + + return false; +} + +const portIterator = (async function*(): AsyncIterableIterator { + for (let i = 6379; i < 65535; i++) { + if (await isPortAvailable(i)) { + yield i; + } + } + + throw new Error('All ports are in use'); +})(); + +export interface RedisServerDockerConfig { + image: string; + version: Array; +} + +export interface RedisServerDocker { + port: number; + dockerId: string; +} + +async function spawnRedisServerDocker({ image, version }: RedisServerDockerConfig, serverArguments: Array): Promise { + const port = (await portIterator.next()).value, + { stdout, stderr } = await execAsync( + `docker run -d --network host ${image}:${version.join('.')} ` + + `--save --port ${port.toString()} ${serverArguments.join(' ')}` + ); + + if (!stdout) { + throw new Error(`docker run error - ${stderr}`); + } + + while (await isPortAvailable(port)) { + await promiseTimeout(500); + } + + return { + port, + dockerId: stdout.trim() + }; +} + +const RUNNING_SERVERS = new Map, ReturnType>(); + +export function spawnRedisServer(dockerConfig: RedisServerDockerConfig, serverArguments: Array): Promise { + const runningServer = RUNNING_SERVERS.get(serverArguments); + if (runningServer) { + return runningServer; + } + + const dockerPromise = spawnRedisServerDocker(dockerConfig, serverArguments); + RUNNING_SERVERS.set(serverArguments, dockerPromise); + return dockerPromise; +} + +async function dockerRemove(dockerId: string): Promise { + const { stderr } = await execAsync(`docker rm -f ${dockerId}`); + if (stderr) { + throw new Error(`docker rm error - ${stderr}`); + } +} + +after(() => { + return Promise.all( + [...RUNNING_SERVERS.values()].map(async dockerPromise => + await dockerRemove((await dockerPromise).dockerId) + ) + ); +}); + +export interface RedisClusterDockersConfig extends RedisServerDockerConfig { + numberOfNodes?: number; +} + +async function spawnRedisClusterNodeDocker( + dockersConfig: RedisClusterDockersConfig, + serverArguments: Array, + fromSlot: number, + toSlot: number, + waitForState: boolean, + meetPort?: number +): Promise { + const docker = await spawnRedisServerDocker(dockersConfig, [ + ...serverArguments, + '--cluster-enabled', + 'yes', + '--cluster-node-timeout', + '5000' + ]), + client = RedisClient.create({ + socket: { + port: docker.port + } + }); + + await client.connect(); + + try { + const range = []; + for (let i = fromSlot; i < toSlot; i++) { + range.push(i); + } + + const promises: Array> = [client.clusterAddSlots(range)]; + + if (meetPort) { + promises.push(client.clusterMeet('127.0.0.1', meetPort)); + } + + if (waitForState) { + promises.push(waitForClusterState(client)); + } + + await Promise.all(promises); + + return docker; + } finally { + await client.disconnect(); + } +} + +async function waitForClusterState(client: RedisClientType): Promise { + while ((await client.clusterInfo()).state !== 'ok') { + await promiseTimeout(500); + } +} + +const SLOTS = 16384; + +async function spawnRedisClusterDockers(dockersConfig: RedisClusterDockersConfig, serverArguments: Array): Promise> { + const numberOfNodes = dockersConfig.numberOfNodes ?? 3, + slotsPerNode = Math.floor(SLOTS / numberOfNodes), + dockers: Array = []; + for (let i = 0; i < numberOfNodes; i++) { + const fromSlot = i * slotsPerNode, + [ toSlot, waitForState ] = i === numberOfNodes - 1 ? [SLOTS, true] : [fromSlot + slotsPerNode, false]; + dockers.push( + await spawnRedisClusterNodeDocker( + dockersConfig, + serverArguments, + fromSlot, + toSlot, + waitForState, + i === 0 ? undefined : dockers[i - 1].port + ) + ); + } + + const client = RedisClient.create({ + socket: { + port: dockers[0].port + } + }); + + await client.connect(); + + try { + while ((await client.clusterInfo()).state !== 'ok') { + await promiseTimeout(500); + } + } finally { + await client.disconnect(); + } + + return dockers; +} + +const RUNNING_CLUSTERS = new Map, ReturnType>(); + +export function spawnRedisCluster(dockersConfig: RedisClusterDockersConfig, serverArguments: Array): Promise> { + const runningCluster = RUNNING_CLUSTERS.get(serverArguments); + if (runningCluster) { + return runningCluster; + } + + const dockersPromise = spawnRedisClusterDockers(dockersConfig, serverArguments); + RUNNING_CLUSTERS.set(serverArguments, dockersPromise); + return dockersPromise; +} + +after(() => { + return Promise.all( + [...RUNNING_CLUSTERS.values()].map(async dockersPromise => { + return Promise.all( + (await dockersPromise).map(({ dockerId }) => dockerRemove(dockerId)) + ); + }) + ); +}); diff --git a/lib/test-utils/index.ts b/lib/test-utils/index.ts new file mode 100644 index 00000000000..69bfc440c91 --- /dev/null +++ b/lib/test-utils/index.ts @@ -0,0 +1,49 @@ +import TestUtils from './test-utils'; +import { SinonSpy } from 'sinon'; +import { promiseTimeout } from '../utils'; + +export default new TestUtils({ + defaultDockerVersion: '6.2', + dockerImageName: 'redis', + dockerImageVersionArgument: 'redis-version' +}); + +export const GLOBAL = { + SERVERS: { + OPEN: { + serverArguments: [] + }, + PASSWORD: { + serverArguments: ['--requirepass', 'password'], + clientOptions: { + password: 'password' + } + } + }, + CLUSTERS: { + OPEN: { + serverArguments: [] + }, + PASSWORD: { + serverArguments: ['--requirepass', 'password'], + clusterConfiguration: { + defaults: { + password: 'password' + } + } + } + } +}; + +export async function waitTillBeenCalled(spy: SinonSpy): Promise { + const start = process.hrtime.bigint(), + calls = spy.callCount; + + do { + if (process.hrtime.bigint() - start > 1_000_000_000) { + throw new Error('Waiting for more than 1 second'); + } + + await promiseTimeout(1); + } while (spy.callCount === calls); +} diff --git a/lib/test-utils/test-utils.ts b/lib/test-utils/test-utils.ts new file mode 100644 index 00000000000..7e488702711 --- /dev/null +++ b/lib/test-utils/test-utils.ts @@ -0,0 +1,175 @@ +import { RedisModules, RedisScripts } from '../commands'; +import RedisClient, { RedisClientOptions, RedisClientType } from '../client'; +import RedisCluster, { RedisClusterOptions, RedisClusterType } from '../cluster'; +import { RedisServerDockerConfig, spawnRedisServer, spawnRedisCluster } from './dockers'; +import yargs from 'yargs'; +import { hideBin } from 'yargs/helpers'; + +interface CommonTestOptions { + minimumDockerVersion?: Array; +} + +interface ClientTestOptions extends CommonTestOptions { + serverArguments: Array; + clientOptions?: Partial>; + disableClientSetup?: boolean; +} + +interface ClusterTestOptions extends CommonTestOptions { + serverArguments: Array; + clusterConfiguration?: Partial>; +} + +interface TestsUtilsConfig { + dockerImageName: string; + dockerImageVersionArgument: string; + defaultDockerVersion: string; +} + +export default class TestUtils { + static #getVersion(config: TestsUtilsConfig): Array { + return yargs(hideBin(process.argv)) + .option('redis-version', { + type: 'string', + default: config.defaultDockerVersion + }) + .coerce(config.dockerImageVersionArgument, (arg: string) => { + return arg.split('.').map(x => { + const value = Number(x); + if (Number.isNaN(value)) { + throw new TypeError(`${arg} is not a valid redis version`); + } + + return value; + }); + }) + .demandOption(config.dockerImageVersionArgument) + .parseSync()[config.dockerImageVersionArgument]; + } + + readonly #DOCKER_IMAGE: RedisServerDockerConfig; + + constructor(config: TestsUtilsConfig) { + this.#DOCKER_IMAGE = { + image: config.dockerImageName, + version: TestUtils.#getVersion(config) + }; + } + + isVersionGreaterThan(minimumVersion: Array | undefined): boolean { + if (minimumVersion === undefined) return true; + + const lastIndex = Math.min(this.#DOCKER_IMAGE.version.length, minimumVersion.length) - 1; + for (let i = 0; i < lastIndex; i++) { + if (this.#DOCKER_IMAGE.version[i] > minimumVersion[i]) { + return true; + } else if (minimumVersion[i] > this.#DOCKER_IMAGE.version[i]) { + return false; + } + } + + return this.#DOCKER_IMAGE.version[lastIndex] >= minimumVersion[lastIndex]; + } + + isVersionGreaterThanHook(minimumVersion: Array | undefined): void { + const isVersionGreaterThan = this.isVersionGreaterThan.bind(this); + before(function () { + if (!isVersionGreaterThan(minimumVersion)) { + return this.skip(); + } + }); + } + + testWithClient( + title: string, + fn: (client: RedisClientType) => Promise, + options: ClientTestOptions + ): Mocha.Test { + let dockerPromise: ReturnType; + if (this.isVersionGreaterThan(options.minimumDockerVersion)) { + const dockerImage = this.#DOCKER_IMAGE; + before(function () { + this.timeout(5000); + + dockerPromise = spawnRedisServer(dockerImage, options.serverArguments); + return dockerPromise; + }); + } + + return it(title, async function() { + if (!dockerPromise) return this.skip(); + + const client = RedisClient.create({ + ...options?.clientOptions, + socket: { + ...options?.clientOptions?.socket, + port: (await dockerPromise).port + } + }); + + if (options.disableClientSetup) { + return fn(client); + } + + try { + await client.connect(); + await client.flushAll(); + + await fn(client); + } finally { + if (client.isOpen) { + await client.flushAll(); + await client.disconnect(); + } + } + }); + } + + static async #clusterFlushAll(cluster: RedisClusterType): Promise { + await Promise.all( + cluster.getMasters().map(({ client }) => client.flushAll()) + ); + } + + testWithCluster( + title: string, + fn: (cluster: RedisClusterType) => Promise, + options: ClusterTestOptions + ): Mocha.Test { + let dockersPromise: ReturnType; + if (this.isVersionGreaterThan(options.minimumDockerVersion)) { + const dockerImage = this.#DOCKER_IMAGE; + before(function () { + this.timeout(10000); + + dockersPromise = spawnRedisCluster(dockerImage, options.serverArguments); + return dockersPromise; + }); + } + + return it(title, async function () { + if (!dockersPromise) return this.skip(); + + const dockers = await dockersPromise, + cluster = RedisCluster.create({ + ...options.clusterConfiguration, + rootNodes: dockers.map(({ port }) => ({ + socket: { + port + } + })) + }); + + + await cluster.connect(); + + try { + await TestUtils.#clusterFlushAll(cluster); + await fn(cluster); + } finally { + await TestUtils.#clusterFlushAll(cluster); + await cluster.disconnect(); + } + }); + } +} diff --git a/package-lock.json b/package-lock.json index 520aedc3278..ff1b2e6e569 100644 --- a/package-lock.json +++ b/package-lock.json @@ -18,65 +18,65 @@ "@istanbuljs/nyc-config-typescript": "^1.0.1", "@tsconfig/node12": "^1.0.9", "@types/mocha": "^9.0.0", - "@types/node": "^16.11.1", - "@types/sinon": "^10.0.4", - "@types/which": "^2.0.1", + "@types/node": "^16.11.6", + "@types/sinon": "^10.0.6", "@types/yallist": "^4.0.1", - "@typescript-eslint/eslint-plugin": "^5.1.0", - "@typescript-eslint/parser": "^5.1.0", - "eslint": "^8.0.1", + "@types/yargs": "^17.0.5", + "@typescript-eslint/eslint-plugin": "^5.2.0", + "@typescript-eslint/parser": "^5.2.0", + "eslint": "^8.1.0", "mocha": "^9.1.3", "nyc": "^15.1.0", "release-it": "^14.11.6", "sinon": "^11.1.2", "source-map-support": "^0.5.20", - "ts-node": "^10.3.0", - "typedoc": "^0.22.6", + "ts-node": "^10.4.0", + "typedoc": "^0.22.7", "typedoc-github-wiki-theme": "^0.6.0", "typedoc-plugin-markdown": "^3.11.3", "typescript": "^4.4.4", - "which": "^2.0.2" + "yargs": "^17.2.1" }, "engines": { "node": ">=12" } }, "node_modules/@babel/code-frame": { - "version": "7.15.8", - "resolved": "https://registry.npmjs.org/@babel/code-frame/-/code-frame-7.15.8.tgz", - "integrity": "sha512-2IAnmn8zbvC/jKYhq5Ki9I+DwjlrtMPUCH/CpHvqI4dNnlwHwsxoIhlc8WcYY5LSYknXQtAlFYuHfqAFCvQ4Wg==", + "version": "7.16.0", + "resolved": "https://registry.npmjs.org/@babel/code-frame/-/code-frame-7.16.0.tgz", + "integrity": "sha512-IF4EOMEV+bfYwOmNxGzSnjR2EmQod7f1UXOpZM3l4i4o4QNwzjtJAu/HxdjHq0aYBvdqMuQEY1eg0nqW9ZPORA==", "dev": true, "dependencies": { - "@babel/highlight": "^7.14.5" + "@babel/highlight": "^7.16.0" }, "engines": { "node": ">=6.9.0" } }, "node_modules/@babel/compat-data": { - "version": "7.15.0", - "resolved": "https://registry.npmjs.org/@babel/compat-data/-/compat-data-7.15.0.tgz", - "integrity": "sha512-0NqAC1IJE0S0+lL1SWFMxMkz1pKCNCjI4tr2Zx4LJSXxCLAdr6KyArnY+sno5m3yH9g737ygOyPABDsnXkpxiA==", + "version": "7.16.0", + "resolved": "https://registry.npmjs.org/@babel/compat-data/-/compat-data-7.16.0.tgz", + "integrity": "sha512-DGjt2QZse5SGd9nfOSqO4WLJ8NN/oHkijbXbPrxuoJO3oIPJL3TciZs9FX+cOHNiY9E9l0opL8g7BmLe3T+9ew==", "dev": true, "engines": { "node": ">=6.9.0" } }, "node_modules/@babel/core": { - "version": "7.15.8", - "resolved": "https://registry.npmjs.org/@babel/core/-/core-7.15.8.tgz", - "integrity": "sha512-3UG9dsxvYBMYwRv+gS41WKHno4K60/9GPy1CJaH6xy3Elq8CTtvtjT5R5jmNhXfCYLX2mTw+7/aq5ak/gOE0og==", - "dev": true, - "dependencies": { - "@babel/code-frame": "^7.15.8", - "@babel/generator": "^7.15.8", - "@babel/helper-compilation-targets": "^7.15.4", - "@babel/helper-module-transforms": "^7.15.8", - "@babel/helpers": "^7.15.4", - "@babel/parser": "^7.15.8", - "@babel/template": "^7.15.4", - "@babel/traverse": "^7.15.4", - "@babel/types": "^7.15.6", + "version": "7.16.0", + "resolved": "https://registry.npmjs.org/@babel/core/-/core-7.16.0.tgz", + "integrity": "sha512-mYZEvshBRHGsIAiyH5PzCFTCfbWfoYbO/jcSdXQSUQu1/pW0xDZAUP7KEc32heqWTAfAHhV9j1vH8Sav7l+JNQ==", + "dev": true, + "dependencies": { + "@babel/code-frame": "^7.16.0", + "@babel/generator": "^7.16.0", + "@babel/helper-compilation-targets": "^7.16.0", + "@babel/helper-module-transforms": "^7.16.0", + "@babel/helpers": "^7.16.0", + "@babel/parser": "^7.16.0", + "@babel/template": "^7.16.0", + "@babel/traverse": "^7.16.0", + "@babel/types": "^7.16.0", "convert-source-map": "^1.7.0", "debug": "^4.1.0", "gensync": "^1.0.0-beta.2", @@ -102,12 +102,12 @@ } }, "node_modules/@babel/generator": { - "version": "7.15.8", - "resolved": "https://registry.npmjs.org/@babel/generator/-/generator-7.15.8.tgz", - "integrity": "sha512-ECmAKstXbp1cvpTTZciZCgfOt6iN64lR0d+euv3UZisU5awfRawOvg07Utn/qBGuH4bRIEZKrA/4LzZyXhZr8g==", + "version": "7.16.0", + "resolved": "https://registry.npmjs.org/@babel/generator/-/generator-7.16.0.tgz", + "integrity": "sha512-RR8hUCfRQn9j9RPKEVXo9LiwoxLPYn6hNZlvUOR8tSnaxlD0p0+la00ZP9/SnRt6HchKr+X0fO2r8vrETiJGew==", "dev": true, "dependencies": { - "@babel/types": "^7.15.6", + "@babel/types": "^7.16.0", "jsesc": "^2.5.1", "source-map": "^0.5.0" }, @@ -116,12 +116,12 @@ } }, "node_modules/@babel/helper-compilation-targets": { - "version": "7.15.4", - "resolved": "https://registry.npmjs.org/@babel/helper-compilation-targets/-/helper-compilation-targets-7.15.4.tgz", - "integrity": "sha512-rMWPCirulnPSe4d+gwdWXLfAXTTBj8M3guAf5xFQJ0nvFY7tfNAFnWdqaHegHlgDZOCT4qvhF3BYlSJag8yhqQ==", + "version": "7.16.0", + "resolved": "https://registry.npmjs.org/@babel/helper-compilation-targets/-/helper-compilation-targets-7.16.0.tgz", + "integrity": "sha512-S7iaOT1SYlqK0sQaCi21RX4+13hmdmnxIEAnQUB/eh7GeAnRjOUgTYpLkUOiRXzD+yog1JxP0qyAQZ7ZxVxLVg==", "dev": true, "dependencies": { - "@babel/compat-data": "^7.15.0", + "@babel/compat-data": "^7.16.0", "@babel/helper-validator-option": "^7.14.5", "browserslist": "^4.16.6", "semver": "^6.3.0" @@ -143,132 +143,132 @@ } }, "node_modules/@babel/helper-function-name": { - "version": "7.15.4", - "resolved": "https://registry.npmjs.org/@babel/helper-function-name/-/helper-function-name-7.15.4.tgz", - "integrity": "sha512-Z91cOMM4DseLIGOnog+Z8OI6YseR9bua+HpvLAQ2XayUGU+neTtX+97caALaLdyu53I/fjhbeCnWnRH1O3jFOw==", + "version": "7.16.0", + "resolved": "https://registry.npmjs.org/@babel/helper-function-name/-/helper-function-name-7.16.0.tgz", + "integrity": "sha512-BZh4mEk1xi2h4HFjWUXRQX5AEx4rvaZxHgax9gcjdLWdkjsY7MKt5p0otjsg5noXw+pB+clMCjw+aEVYADMjog==", "dev": true, "dependencies": { - "@babel/helper-get-function-arity": "^7.15.4", - "@babel/template": "^7.15.4", - "@babel/types": "^7.15.4" + "@babel/helper-get-function-arity": "^7.16.0", + "@babel/template": "^7.16.0", + "@babel/types": "^7.16.0" }, "engines": { "node": ">=6.9.0" } }, "node_modules/@babel/helper-get-function-arity": { - "version": "7.15.4", - "resolved": "https://registry.npmjs.org/@babel/helper-get-function-arity/-/helper-get-function-arity-7.15.4.tgz", - "integrity": "sha512-1/AlxSF92CmGZzHnC515hm4SirTxtpDnLEJ0UyEMgTMZN+6bxXKg04dKhiRx5Enel+SUA1G1t5Ed/yQia0efrA==", + "version": "7.16.0", + "resolved": "https://registry.npmjs.org/@babel/helper-get-function-arity/-/helper-get-function-arity-7.16.0.tgz", + "integrity": "sha512-ASCquNcywC1NkYh/z7Cgp3w31YW8aojjYIlNg4VeJiHkqyP4AzIvr4qx7pYDb4/s8YcsZWqqOSxgkvjUz1kpDQ==", "dev": true, "dependencies": { - "@babel/types": "^7.15.4" + "@babel/types": "^7.16.0" }, "engines": { "node": ">=6.9.0" } }, "node_modules/@babel/helper-hoist-variables": { - "version": "7.15.4", - "resolved": "https://registry.npmjs.org/@babel/helper-hoist-variables/-/helper-hoist-variables-7.15.4.tgz", - "integrity": "sha512-VTy085egb3jUGVK9ycIxQiPbquesq0HUQ+tPO0uv5mPEBZipk+5FkRKiWq5apuyTE9FUrjENB0rCf8y+n+UuhA==", + "version": "7.16.0", + "resolved": "https://registry.npmjs.org/@babel/helper-hoist-variables/-/helper-hoist-variables-7.16.0.tgz", + "integrity": "sha512-1AZlpazjUR0EQZQv3sgRNfM9mEVWPK3M6vlalczA+EECcPz3XPh6VplbErL5UoMpChhSck5wAJHthlj1bYpcmg==", "dev": true, "dependencies": { - "@babel/types": "^7.15.4" + "@babel/types": "^7.16.0" }, "engines": { "node": ">=6.9.0" } }, "node_modules/@babel/helper-member-expression-to-functions": { - "version": "7.15.4", - "resolved": "https://registry.npmjs.org/@babel/helper-member-expression-to-functions/-/helper-member-expression-to-functions-7.15.4.tgz", - "integrity": "sha512-cokOMkxC/BTyNP1AlY25HuBWM32iCEsLPI4BHDpJCHHm1FU2E7dKWWIXJgQgSFiu4lp8q3bL1BIKwqkSUviqtA==", + "version": "7.16.0", + "resolved": "https://registry.npmjs.org/@babel/helper-member-expression-to-functions/-/helper-member-expression-to-functions-7.16.0.tgz", + "integrity": "sha512-bsjlBFPuWT6IWhl28EdrQ+gTvSvj5tqVP5Xeftp07SEuz5pLnsXZuDkDD3Rfcxy0IsHmbZ+7B2/9SHzxO0T+sQ==", "dev": true, "dependencies": { - "@babel/types": "^7.15.4" + "@babel/types": "^7.16.0" }, "engines": { "node": ">=6.9.0" } }, "node_modules/@babel/helper-module-imports": { - "version": "7.15.4", - "resolved": "https://registry.npmjs.org/@babel/helper-module-imports/-/helper-module-imports-7.15.4.tgz", - "integrity": "sha512-jeAHZbzUwdW/xHgHQ3QmWR4Jg6j15q4w/gCfwZvtqOxoo5DKtLHk8Bsf4c5RZRC7NmLEs+ohkdq8jFefuvIxAA==", + "version": "7.16.0", + "resolved": "https://registry.npmjs.org/@babel/helper-module-imports/-/helper-module-imports-7.16.0.tgz", + "integrity": "sha512-kkH7sWzKPq0xt3H1n+ghb4xEMP8k0U7XV3kkB+ZGy69kDk2ySFW1qPi06sjKzFY3t1j6XbJSqr4mF9L7CYVyhg==", "dev": true, "dependencies": { - "@babel/types": "^7.15.4" + "@babel/types": "^7.16.0" }, "engines": { "node": ">=6.9.0" } }, "node_modules/@babel/helper-module-transforms": { - "version": "7.15.8", - "resolved": "https://registry.npmjs.org/@babel/helper-module-transforms/-/helper-module-transforms-7.15.8.tgz", - "integrity": "sha512-DfAfA6PfpG8t4S6npwzLvTUpp0sS7JrcuaMiy1Y5645laRJIp/LiLGIBbQKaXSInK8tiGNI7FL7L8UvB8gdUZg==", + "version": "7.16.0", + "resolved": "https://registry.npmjs.org/@babel/helper-module-transforms/-/helper-module-transforms-7.16.0.tgz", + "integrity": "sha512-My4cr9ATcaBbmaEa8M0dZNA74cfI6gitvUAskgDtAFmAqyFKDSHQo5YstxPbN+lzHl2D9l/YOEFqb2mtUh4gfA==", "dev": true, "dependencies": { - "@babel/helper-module-imports": "^7.15.4", - "@babel/helper-replace-supers": "^7.15.4", - "@babel/helper-simple-access": "^7.15.4", - "@babel/helper-split-export-declaration": "^7.15.4", + "@babel/helper-module-imports": "^7.16.0", + "@babel/helper-replace-supers": "^7.16.0", + "@babel/helper-simple-access": "^7.16.0", + "@babel/helper-split-export-declaration": "^7.16.0", "@babel/helper-validator-identifier": "^7.15.7", - "@babel/template": "^7.15.4", - "@babel/traverse": "^7.15.4", - "@babel/types": "^7.15.6" + "@babel/template": "^7.16.0", + "@babel/traverse": "^7.16.0", + "@babel/types": "^7.16.0" }, "engines": { "node": ">=6.9.0" } }, "node_modules/@babel/helper-optimise-call-expression": { - "version": "7.15.4", - "resolved": "https://registry.npmjs.org/@babel/helper-optimise-call-expression/-/helper-optimise-call-expression-7.15.4.tgz", - "integrity": "sha512-E/z9rfbAOt1vDW1DR7k4SzhzotVV5+qMciWV6LaG1g4jeFrkDlJedjtV4h0i4Q/ITnUu+Pk08M7fczsB9GXBDw==", + "version": "7.16.0", + "resolved": "https://registry.npmjs.org/@babel/helper-optimise-call-expression/-/helper-optimise-call-expression-7.16.0.tgz", + "integrity": "sha512-SuI467Gi2V8fkofm2JPnZzB/SUuXoJA5zXe/xzyPP2M04686RzFKFHPK6HDVN6JvWBIEW8tt9hPR7fXdn2Lgpw==", "dev": true, "dependencies": { - "@babel/types": "^7.15.4" + "@babel/types": "^7.16.0" }, "engines": { "node": ">=6.9.0" } }, "node_modules/@babel/helper-replace-supers": { - "version": "7.15.4", - "resolved": "https://registry.npmjs.org/@babel/helper-replace-supers/-/helper-replace-supers-7.15.4.tgz", - "integrity": "sha512-/ztT6khaXF37MS47fufrKvIsiQkx1LBRvSJNzRqmbyeZnTwU9qBxXYLaaT/6KaxfKhjs2Wy8kG8ZdsFUuWBjzw==", + "version": "7.16.0", + "resolved": "https://registry.npmjs.org/@babel/helper-replace-supers/-/helper-replace-supers-7.16.0.tgz", + "integrity": "sha512-TQxuQfSCdoha7cpRNJvfaYxxxzmbxXw/+6cS7V02eeDYyhxderSoMVALvwupA54/pZcOTtVeJ0xccp1nGWladA==", "dev": true, "dependencies": { - "@babel/helper-member-expression-to-functions": "^7.15.4", - "@babel/helper-optimise-call-expression": "^7.15.4", - "@babel/traverse": "^7.15.4", - "@babel/types": "^7.15.4" + "@babel/helper-member-expression-to-functions": "^7.16.0", + "@babel/helper-optimise-call-expression": "^7.16.0", + "@babel/traverse": "^7.16.0", + "@babel/types": "^7.16.0" }, "engines": { "node": ">=6.9.0" } }, "node_modules/@babel/helper-simple-access": { - "version": "7.15.4", - "resolved": "https://registry.npmjs.org/@babel/helper-simple-access/-/helper-simple-access-7.15.4.tgz", - "integrity": "sha512-UzazrDoIVOZZcTeHHEPYrr1MvTR/K+wgLg6MY6e1CJyaRhbibftF6fR2KU2sFRtI/nERUZR9fBd6aKgBlIBaPg==", + "version": "7.16.0", + "resolved": "https://registry.npmjs.org/@babel/helper-simple-access/-/helper-simple-access-7.16.0.tgz", + "integrity": "sha512-o1rjBT/gppAqKsYfUdfHq5Rk03lMQrkPHG1OWzHWpLgVXRH4HnMM9Et9CVdIqwkCQlobnGHEJMsgWP/jE1zUiw==", "dev": true, "dependencies": { - "@babel/types": "^7.15.4" + "@babel/types": "^7.16.0" }, "engines": { "node": ">=6.9.0" } }, "node_modules/@babel/helper-split-export-declaration": { - "version": "7.15.4", - "resolved": "https://registry.npmjs.org/@babel/helper-split-export-declaration/-/helper-split-export-declaration-7.15.4.tgz", - "integrity": "sha512-HsFqhLDZ08DxCpBdEVtKmywj6PQbwnF6HHybur0MAnkAKnlS6uHkwnmRIkElB2Owpfb4xL4NwDmDLFubueDXsw==", + "version": "7.16.0", + "resolved": "https://registry.npmjs.org/@babel/helper-split-export-declaration/-/helper-split-export-declaration-7.16.0.tgz", + "integrity": "sha512-0YMMRpuDFNGTHNRiiqJX19GjNXA4H0E8jZ2ibccfSxaCogbm3am5WN/2nQNj0YnQwGWM1J06GOcQ2qnh3+0paw==", "dev": true, "dependencies": { - "@babel/types": "^7.15.4" + "@babel/types": "^7.16.0" }, "engines": { "node": ">=6.9.0" @@ -293,26 +293,26 @@ } }, "node_modules/@babel/helpers": { - "version": "7.15.4", - "resolved": "https://registry.npmjs.org/@babel/helpers/-/helpers-7.15.4.tgz", - "integrity": "sha512-V45u6dqEJ3w2rlryYYXf6i9rQ5YMNu4FLS6ngs8ikblhu2VdR1AqAd6aJjBzmf2Qzh6KOLqKHxEN9+TFbAkAVQ==", + "version": "7.16.0", + "resolved": "https://registry.npmjs.org/@babel/helpers/-/helpers-7.16.0.tgz", + "integrity": "sha512-dVRM0StFMdKlkt7cVcGgwD8UMaBfWJHl3A83Yfs8GQ3MO0LHIIIMvK7Fa0RGOGUQ10qikLaX6D7o5htcQWgTMQ==", "dev": true, "dependencies": { - "@babel/template": "^7.15.4", - "@babel/traverse": "^7.15.4", - "@babel/types": "^7.15.4" + "@babel/template": "^7.16.0", + "@babel/traverse": "^7.16.0", + "@babel/types": "^7.16.0" }, "engines": { "node": ">=6.9.0" } }, "node_modules/@babel/highlight": { - "version": "7.14.5", - "resolved": "https://registry.npmjs.org/@babel/highlight/-/highlight-7.14.5.tgz", - "integrity": "sha512-qf9u2WFWVV0MppaL877j2dBtQIDgmidgjGk5VIMw3OadXvYaXn66U1BFlH2t4+t3i+8PhedppRv+i40ABzd+gg==", + "version": "7.16.0", + "resolved": "https://registry.npmjs.org/@babel/highlight/-/highlight-7.16.0.tgz", + "integrity": "sha512-t8MH41kUQylBtu2+4IQA3atqevA2lRgqA2wyVB/YiWmsDSuylZZuXOUy9ric30hfzauEFfdsuk/eXTRrGrfd0g==", "dev": true, "dependencies": { - "@babel/helper-validator-identifier": "^7.14.5", + "@babel/helper-validator-identifier": "^7.15.7", "chalk": "^2.0.0", "js-tokens": "^4.0.0" }, @@ -392,9 +392,9 @@ } }, "node_modules/@babel/parser": { - "version": "7.15.8", - "resolved": "https://registry.npmjs.org/@babel/parser/-/parser-7.15.8.tgz", - "integrity": "sha512-BRYa3wcQnjS/nqI8Ac94pYYpJfojHVvVXJ97+IDCImX4Jc8W8Xv1+47enbruk+q1etOpsQNwnfFcNGw+gtPGxA==", + "version": "7.16.0", + "resolved": "https://registry.npmjs.org/@babel/parser/-/parser-7.16.0.tgz", + "integrity": "sha512-TEHWXf0xxpi9wKVyBCmRcSSDjbJ/cl6LUdlbYUHEaNQUJGhreJbZrXT6sR4+fZLxVUJqNRB4KyOvjuy/D9009A==", "dev": true, "bin": { "parser": "bin/babel-parser.js" @@ -404,32 +404,32 @@ } }, "node_modules/@babel/template": { - "version": "7.15.4", - "resolved": "https://registry.npmjs.org/@babel/template/-/template-7.15.4.tgz", - "integrity": "sha512-UgBAfEa1oGuYgDIPM2G+aHa4Nlo9Lh6mGD2bDBGMTbYnc38vulXPuC1MGjYILIEmlwl6Rd+BPR9ee3gm20CBtg==", + "version": "7.16.0", + "resolved": "https://registry.npmjs.org/@babel/template/-/template-7.16.0.tgz", + "integrity": "sha512-MnZdpFD/ZdYhXwiunMqqgyZyucaYsbL0IrjoGjaVhGilz+x8YB++kRfygSOIj1yOtWKPlx7NBp+9I1RQSgsd5A==", "dev": true, "dependencies": { - "@babel/code-frame": "^7.14.5", - "@babel/parser": "^7.15.4", - "@babel/types": "^7.15.4" + "@babel/code-frame": "^7.16.0", + "@babel/parser": "^7.16.0", + "@babel/types": "^7.16.0" }, "engines": { "node": ">=6.9.0" } }, "node_modules/@babel/traverse": { - "version": "7.15.4", - "resolved": "https://registry.npmjs.org/@babel/traverse/-/traverse-7.15.4.tgz", - "integrity": "sha512-W6lQD8l4rUbQR/vYgSuCAE75ADyyQvOpFVsvPPdkhf6lATXAsQIG9YdtOcu8BB1dZ0LKu+Zo3c1wEcbKeuhdlA==", - "dev": true, - "dependencies": { - "@babel/code-frame": "^7.14.5", - "@babel/generator": "^7.15.4", - "@babel/helper-function-name": "^7.15.4", - "@babel/helper-hoist-variables": "^7.15.4", - "@babel/helper-split-export-declaration": "^7.15.4", - "@babel/parser": "^7.15.4", - "@babel/types": "^7.15.4", + "version": "7.16.0", + "resolved": "https://registry.npmjs.org/@babel/traverse/-/traverse-7.16.0.tgz", + "integrity": "sha512-qQ84jIs1aRQxaGaxSysII9TuDaguZ5yVrEuC0BN2vcPlalwfLovVmCjbFDPECPXcYM/wLvNFfp8uDOliLxIoUQ==", + "dev": true, + "dependencies": { + "@babel/code-frame": "^7.16.0", + "@babel/generator": "^7.16.0", + "@babel/helper-function-name": "^7.16.0", + "@babel/helper-hoist-variables": "^7.16.0", + "@babel/helper-split-export-declaration": "^7.16.0", + "@babel/parser": "^7.16.0", + "@babel/types": "^7.16.0", "debug": "^4.1.0", "globals": "^11.1.0" }, @@ -447,12 +447,12 @@ } }, "node_modules/@babel/types": { - "version": "7.15.6", - "resolved": "https://registry.npmjs.org/@babel/types/-/types-7.15.6.tgz", - "integrity": "sha512-BPU+7QhqNjmWyDO0/vitH/CuhpV8ZmK1wpKva8nuyNF5MJfuRNWMc+hc14+u9xT93kvykMdncrJT19h74uB1Ig==", + "version": "7.16.0", + "resolved": "https://registry.npmjs.org/@babel/types/-/types-7.16.0.tgz", + "integrity": "sha512-PJgg/k3SdLsGb3hhisFvtLOw5ts113klrpLuIPtCJIU+BB24fqq6lf8RWqKJEjzqXR9AEH1rIb5XTqwBHB+kQg==", "dev": true, "dependencies": { - "@babel/helper-validator-identifier": "^7.14.9", + "@babel/helper-validator-identifier": "^7.15.7", "to-fast-properties": "^2.0.0" }, "engines": { @@ -972,9 +972,9 @@ "dev": true }, "node_modules/@types/node": { - "version": "16.11.1", - "resolved": "https://registry.npmjs.org/@types/node/-/node-16.11.1.tgz", - "integrity": "sha512-PYGcJHL9mwl1Ek3PLiYgyEKtwTMmkMw4vbiyz/ps3pfdRYLVv+SN7qHVAImrjdAXxgluDEw6Ph4lyv+m9UpRmA==", + "version": "16.11.6", + "resolved": "https://registry.npmjs.org/@types/node/-/node-16.11.6.tgz", + "integrity": "sha512-ua7PgUoeQFjmWPcoo9khiPum3Pd60k4/2ZGXt18sm2Slk0W0xZTqt5Y0Ny1NyBiN1EVQ/+FaF9NcY4Qe6rwk5w==", "dev": true }, "node_modules/@types/parse-json": { @@ -993,34 +993,43 @@ } }, "node_modules/@types/sinon": { - "version": "10.0.4", - "resolved": "https://registry.npmjs.org/@types/sinon/-/sinon-10.0.4.tgz", - "integrity": "sha512-fOYjrxQv8zJsqOY6V6ecP4eZhQBxtY80X0er1VVnUIAIZo74jHm8e1vguG5Yt4Iv8W2Wr7TgibB8MfRe32k9pA==", + "version": "10.0.6", + "resolved": "https://registry.npmjs.org/@types/sinon/-/sinon-10.0.6.tgz", + "integrity": "sha512-6EF+wzMWvBNeGrfP3Nx60hhx+FfwSg1JJBLAAP/IdIUq0EYkqCYf70VT3PhuhPX9eLD+Dp+lNdpb/ZeHG8Yezg==", "dev": true, "dependencies": { "@sinonjs/fake-timers": "^7.1.0" } }, - "node_modules/@types/which": { - "version": "2.0.1", - "resolved": "https://registry.npmjs.org/@types/which/-/which-2.0.1.tgz", - "integrity": "sha512-Jjakcv8Roqtio6w1gr0D7y6twbhx6gGgFGF5BLwajPpnOIOxFkakFhCq+LmyyeAz7BX6ULrjBOxdKaCDy+4+dQ==", - "dev": true - }, "node_modules/@types/yallist": { "version": "4.0.1", "resolved": "https://registry.npmjs.org/@types/yallist/-/yallist-4.0.1.tgz", "integrity": "sha512-G3FNJfaYtN8URU6wd6+uwFI62KO79j7n3XTYcwcFncP8gkfoi0b821GoVVt0oqKVnCqKYOMNKIGpakPoFhzAGA==", "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==", + "dev": true, + "dependencies": { + "@types/yargs-parser": "*" + } + }, + "node_modules/@types/yargs-parser": { + "version": "20.2.1", + "resolved": "https://registry.npmjs.org/@types/yargs-parser/-/yargs-parser-20.2.1.tgz", + "integrity": "sha512-7tFImggNeNBVMsn0vLrpn1H1uPrUBdnARPTpZoitY37ZrdJREzf7I16tMrlK3hen349gr1NYh8CmZQa7CTG6Aw==", + "dev": true + }, "node_modules/@typescript-eslint/eslint-plugin": { - "version": "5.1.0", - "resolved": "https://registry.npmjs.org/@typescript-eslint/eslint-plugin/-/eslint-plugin-5.1.0.tgz", - "integrity": "sha512-bekODL3Tqf36Yz8u+ilha4zGxL9mdB6LIsIoMAvvC5FAuWo4NpZYXtCbv7B2CeR1LhI/lLtLk+q4tbtxuoVuCg==", + "version": "5.2.0", + "resolved": "https://registry.npmjs.org/@typescript-eslint/eslint-plugin/-/eslint-plugin-5.2.0.tgz", + "integrity": "sha512-qQwg7sqYkBF4CIQSyRQyqsYvP+g/J0To9ZPVNJpfxfekl5RmdvQnFFTVVwpRtaUDFNvjfe/34TgY/dpc3MgNTw==", "dev": true, "dependencies": { - "@typescript-eslint/experimental-utils": "5.1.0", - "@typescript-eslint/scope-manager": "5.1.0", + "@typescript-eslint/experimental-utils": "5.2.0", + "@typescript-eslint/scope-manager": "5.2.0", "debug": "^4.3.2", "functional-red-black-tree": "^1.0.1", "ignore": "^5.1.8", @@ -1046,15 +1055,15 @@ } }, "node_modules/@typescript-eslint/experimental-utils": { - "version": "5.1.0", - "resolved": "https://registry.npmjs.org/@typescript-eslint/experimental-utils/-/experimental-utils-5.1.0.tgz", - "integrity": "sha512-ovE9qUiZMOMgxQAESZsdBT+EXIfx/YUYAbwGUI6V03amFdOOxI9c6kitkgRvLkJaLusgMZ2xBhss+tQ0Y1HWxA==", + "version": "5.2.0", + "resolved": "https://registry.npmjs.org/@typescript-eslint/experimental-utils/-/experimental-utils-5.2.0.tgz", + "integrity": "sha512-fWyT3Agf7n7HuZZRpvUYdFYbPk3iDCq6fgu3ulia4c7yxmPnwVBovdSOX7RL+k8u6hLbrXcdAehlWUVpGh6IEw==", "dev": true, "dependencies": { "@types/json-schema": "^7.0.9", - "@typescript-eslint/scope-manager": "5.1.0", - "@typescript-eslint/types": "5.1.0", - "@typescript-eslint/typescript-estree": "5.1.0", + "@typescript-eslint/scope-manager": "5.2.0", + "@typescript-eslint/types": "5.2.0", + "@typescript-eslint/typescript-estree": "5.2.0", "eslint-scope": "^5.1.1", "eslint-utils": "^3.0.0" }, @@ -1070,14 +1079,14 @@ } }, "node_modules/@typescript-eslint/parser": { - "version": "5.1.0", - "resolved": "https://registry.npmjs.org/@typescript-eslint/parser/-/parser-5.1.0.tgz", - "integrity": "sha512-vx1P+mhCtYw3+bRHmbalq/VKP2Y3gnzNgxGxfEWc6OFpuEL7iQdAeq11Ke3Rhy8NjgB+AHsIWEwni3e+Y7djKA==", + "version": "5.2.0", + "resolved": "https://registry.npmjs.org/@typescript-eslint/parser/-/parser-5.2.0.tgz", + "integrity": "sha512-Uyy4TjJBlh3NuA8/4yIQptyJb95Qz5PX//6p8n7zG0QnN4o3NF9Je3JHbVU7fxf5ncSXTmnvMtd/LDQWDk0YqA==", "dev": true, "dependencies": { - "@typescript-eslint/scope-manager": "5.1.0", - "@typescript-eslint/types": "5.1.0", - "@typescript-eslint/typescript-estree": "5.1.0", + "@typescript-eslint/scope-manager": "5.2.0", + "@typescript-eslint/types": "5.2.0", + "@typescript-eslint/typescript-estree": "5.2.0", "debug": "^4.3.2" }, "engines": { @@ -1097,13 +1106,13 @@ } }, "node_modules/@typescript-eslint/scope-manager": { - "version": "5.1.0", - "resolved": "https://registry.npmjs.org/@typescript-eslint/scope-manager/-/scope-manager-5.1.0.tgz", - "integrity": "sha512-yYlyVjvn5lvwCL37i4hPsa1s0ORsjkauhTqbb8MnpvUs7xykmcjGqwlNZ2Q5QpoqkJ1odlM2bqHqJwa28qV6Tw==", + "version": "5.2.0", + "resolved": "https://registry.npmjs.org/@typescript-eslint/scope-manager/-/scope-manager-5.2.0.tgz", + "integrity": "sha512-RW+wowZqPzQw8MUFltfKYZfKXqA2qgyi6oi/31J1zfXJRpOn6tCaZtd9b5u9ubnDG2n/EMvQLeZrsLNPpaUiFQ==", "dev": true, "dependencies": { - "@typescript-eslint/types": "5.1.0", - "@typescript-eslint/visitor-keys": "5.1.0" + "@typescript-eslint/types": "5.2.0", + "@typescript-eslint/visitor-keys": "5.2.0" }, "engines": { "node": "^12.22.0 || ^14.17.0 || >=16.0.0" @@ -1114,9 +1123,9 @@ } }, "node_modules/@typescript-eslint/types": { - "version": "5.1.0", - "resolved": "https://registry.npmjs.org/@typescript-eslint/types/-/types-5.1.0.tgz", - "integrity": "sha512-sEwNINVxcB4ZgC6Fe6rUyMlvsB2jvVdgxjZEjQUQVlaSPMNamDOwO6/TB98kFt4sYYfNhdhTPBEQqNQZjMMswA==", + "version": "5.2.0", + "resolved": "https://registry.npmjs.org/@typescript-eslint/types/-/types-5.2.0.tgz", + "integrity": "sha512-cTk6x08qqosps6sPyP2j7NxyFPlCNsJwSDasqPNjEQ8JMD5xxj2NHxcLin5AJQ8pAVwpQ8BMI3bTxR0zxmK9qQ==", "dev": true, "engines": { "node": "^12.22.0 || ^14.17.0 || >=16.0.0" @@ -1127,13 +1136,13 @@ } }, "node_modules/@typescript-eslint/typescript-estree": { - "version": "5.1.0", - "resolved": "https://registry.npmjs.org/@typescript-eslint/typescript-estree/-/typescript-estree-5.1.0.tgz", - "integrity": "sha512-SSz+l9YrIIsW4s0ZqaEfnjl156XQ4VRmJsbA0ZE1XkXrD3cRpzuZSVCyqeCMR3EBjF27IisWakbBDGhGNIOvfQ==", + "version": "5.2.0", + "resolved": "https://registry.npmjs.org/@typescript-eslint/typescript-estree/-/typescript-estree-5.2.0.tgz", + "integrity": "sha512-RsdXq2XmVgKbm9nLsE3mjNUM7BTr/K4DYR9WfFVMUuozHWtH5gMpiNZmtrMG8GR385EOSQ3kC9HiEMJWimxd/g==", "dev": true, "dependencies": { - "@typescript-eslint/types": "5.1.0", - "@typescript-eslint/visitor-keys": "5.1.0", + "@typescript-eslint/types": "5.2.0", + "@typescript-eslint/visitor-keys": "5.2.0", "debug": "^4.3.2", "globby": "^11.0.4", "is-glob": "^4.0.3", @@ -1154,12 +1163,12 @@ } }, "node_modules/@typescript-eslint/visitor-keys": { - "version": "5.1.0", - "resolved": "https://registry.npmjs.org/@typescript-eslint/visitor-keys/-/visitor-keys-5.1.0.tgz", - "integrity": "sha512-uqNXepKBg81JVwjuqAxYrXa1Ql/YDzM+8g/pS+TCPxba0wZttl8m5DkrasbfnmJGHs4lQ2jTbcZ5azGhI7kK+w==", + "version": "5.2.0", + "resolved": "https://registry.npmjs.org/@typescript-eslint/visitor-keys/-/visitor-keys-5.2.0.tgz", + "integrity": "sha512-Nk7HizaXWWCUBfLA/rPNKMzXzWS8Wg9qHMuGtT+v2/YpPij4nVXrVJc24N/r5WrrmqK31jCrZxeHqIgqRzs0Xg==", "dev": true, "dependencies": { - "@typescript-eslint/types": "5.1.0", + "@typescript-eslint/types": "5.2.0", "eslint-visitor-keys": "^3.0.0" }, "engines": { @@ -1486,15 +1495,15 @@ "dev": true }, "node_modules/browserslist": { - "version": "4.17.4", - "resolved": "https://registry.npmjs.org/browserslist/-/browserslist-4.17.4.tgz", - "integrity": "sha512-Zg7RpbZpIJRW3am9Lyckue7PLytvVxxhJj1CaJVlCWENsGEAOlnlt8X0ZxGRPp7Bt9o8tIRM5SEXy4BCPMJjLQ==", + "version": "4.17.5", + "resolved": "https://registry.npmjs.org/browserslist/-/browserslist-4.17.5.tgz", + "integrity": "sha512-I3ekeB92mmpctWBoLXe0d5wPS2cBuRvvW0JyyJHMrk9/HmP2ZjrTboNAZ8iuGqaEIlKguljbQY32OkOJIRrgoA==", "dev": true, "dependencies": { - "caniuse-lite": "^1.0.30001265", - "electron-to-chromium": "^1.3.867", + "caniuse-lite": "^1.0.30001271", + "electron-to-chromium": "^1.3.878", "escalade": "^3.1.1", - "node-releases": "^2.0.0", + "node-releases": "^2.0.1", "picocolors": "^1.0.0" }, "bin": { @@ -1627,9 +1636,9 @@ } }, "node_modules/caniuse-lite": { - "version": "1.0.30001269", - "resolved": "https://registry.npmjs.org/caniuse-lite/-/caniuse-lite-1.0.30001269.tgz", - "integrity": "sha512-UOy8okEVs48MyHYgV+RdW1Oiudl1H6KolybD6ZquD0VcrPSgj25omXO1S7rDydjpqaISCwA8Pyx+jUQKZwWO5w==", + "version": "1.0.30001274", + "resolved": "https://registry.npmjs.org/caniuse-lite/-/caniuse-lite-1.0.30001274.tgz", + "integrity": "sha512-+Nkvv0fHyhISkiMIjnyjmf5YJcQ1IQHZN6U9TLUMroWR38FNwpsC51Gb68yueafX1V6ifOisInSgP9WJFS13ew==", "dev": true, "funding": { "type": "opencollective", @@ -2088,9 +2097,9 @@ "dev": true }, "node_modules/electron-to-chromium": { - "version": "1.3.872", - "resolved": "https://registry.npmjs.org/electron-to-chromium/-/electron-to-chromium-1.3.872.tgz", - "integrity": "sha512-qG96atLFY0agKyEETiBFNhpRLSXGSXOBuhXWpbkYqrLKKASpRyRBUtfkn0ZjIf/yXfA7FA4nScVOMpXSHFlUCQ==", + "version": "1.3.885", + "resolved": "https://registry.npmjs.org/electron-to-chromium/-/electron-to-chromium-1.3.885.tgz", + "integrity": "sha512-JXKFJcVWrdHa09n4CNZYfYaK6EW5aAew7/wr3L1OnsD1L+JHL+RCtd7QgIsxUbFPeTwPlvnpqNNTOLkoefmtXg==", "dev": true }, "node_modules/emoji-regex": { @@ -2166,9 +2175,9 @@ } }, "node_modules/eslint": { - "version": "8.0.1", - "resolved": "https://registry.npmjs.org/eslint/-/eslint-8.0.1.tgz", - "integrity": "sha512-LsgcwZgQ72vZ+SMp4K6pAnk2yFDWL7Ti4pJaRvsZ0Hsw2h8ZjUIW38a9AFn2cZXdBMlScMFYYgsSp4ttFI/0bA==", + "version": "8.1.0", + "resolved": "https://registry.npmjs.org/eslint/-/eslint-8.1.0.tgz", + "integrity": "sha512-JZvNneArGSUsluHWJ8g8MMs3CfIEzwaLx9KyH4tZ2i+R2/rPWzL8c0zg3rHdwYVpN/1sB9gqnjHwz9HoeJpGHw==", "dev": true, "dependencies": { "@eslint/eslintrc": "^1.0.3", @@ -2283,9 +2292,9 @@ } }, "node_modules/eslint/node_modules/estraverse": { - "version": "5.2.0", - "resolved": "https://registry.npmjs.org/estraverse/-/estraverse-5.2.0.tgz", - "integrity": "sha512-BxbNGGNm0RyRYvUdHpIwv9IWzeM9XClbOxwoATuFdOE7ZE6wHL+HQ5T8hoPM+zHvmKzzsEqhgy0GrQ5X13afiQ==", + "version": "5.3.0", + "resolved": "https://registry.npmjs.org/estraverse/-/estraverse-5.3.0.tgz", + "integrity": "sha512-MMdARuVEQziNTeJD8DgMqmhwR11BRQ/cBP+pLtYdSTnf3MIO8fFeiINEbX36ZdNlfU/7A9f3gUw49B3oQsvwBA==", "dev": true, "engines": { "node": ">=4.0" @@ -2340,9 +2349,9 @@ } }, "node_modules/esquery/node_modules/estraverse": { - "version": "5.2.0", - "resolved": "https://registry.npmjs.org/estraverse/-/estraverse-5.2.0.tgz", - "integrity": "sha512-BxbNGGNm0RyRYvUdHpIwv9IWzeM9XClbOxwoATuFdOE7ZE6wHL+HQ5T8hoPM+zHvmKzzsEqhgy0GrQ5X13afiQ==", + "version": "5.3.0", + "resolved": "https://registry.npmjs.org/estraverse/-/estraverse-5.3.0.tgz", + "integrity": "sha512-MMdARuVEQziNTeJD8DgMqmhwR11BRQ/cBP+pLtYdSTnf3MIO8fFeiINEbX36ZdNlfU/7A9f3gUw49B3oQsvwBA==", "dev": true, "engines": { "node": ">=4.0" @@ -2361,9 +2370,9 @@ } }, "node_modules/esrecurse/node_modules/estraverse": { - "version": "5.2.0", - "resolved": "https://registry.npmjs.org/estraverse/-/estraverse-5.2.0.tgz", - "integrity": "sha512-BxbNGGNm0RyRYvUdHpIwv9IWzeM9XClbOxwoATuFdOE7ZE6wHL+HQ5T8hoPM+zHvmKzzsEqhgy0GrQ5X13afiQ==", + "version": "5.3.0", + "resolved": "https://registry.npmjs.org/estraverse/-/estraverse-5.3.0.tgz", + "integrity": "sha512-MMdARuVEQziNTeJD8DgMqmhwR11BRQ/cBP+pLtYdSTnf3MIO8fFeiINEbX36ZdNlfU/7A9f3gUw49B3oQsvwBA==", "dev": true, "engines": { "node": ">=4.0" @@ -2804,9 +2813,9 @@ } }, "node_modules/globals": { - "version": "13.11.0", - "resolved": "https://registry.npmjs.org/globals/-/globals-13.11.0.tgz", - "integrity": "sha512-08/xrJ7wQjK9kkkRoI3OFUBbLx4f+6x3SGwcPvQ0QH6goFDrOU2oyAWrmh3dJezu65buo+HBMzAMQy6rovVC3g==", + "version": "13.12.0", + "resolved": "https://registry.npmjs.org/globals/-/globals-13.12.0.tgz", + "integrity": "sha512-uS8X6lSKN2JumVoXrbUz+uG4BYG+eiawqm3qFcT7ammfbUHeCBoJMlHcec/S3krSk73/AE/f0szYFmgAA3kYZg==", "dev": true, "dependencies": { "type-fest": "^0.20.2" @@ -3636,9 +3645,9 @@ "dev": true }, "node_modules/keyv": { - "version": "4.0.3", - "resolved": "https://registry.npmjs.org/keyv/-/keyv-4.0.3.tgz", - "integrity": "sha512-zdGa2TOpSZPq5mU6iowDARnMBZgtCqJ11dJROFi6tg6kTn4nuUdU09lFyLFSaHrWqpIJ+EBq4E8/Dc0Vx5vLdA==", + "version": "4.0.4", + "resolved": "https://registry.npmjs.org/keyv/-/keyv-4.0.4.tgz", + "integrity": "sha512-vqNHbAc8BBsxk+7QBYLW0Y219rWcClspR6WSeoHYKG5mnsSoOH+BL1pWq02DDCVdvvuUny5rkBlzMRzoqc+GIg==", "dev": true, "dependencies": { "json-buffer": "3.0.1" @@ -3800,9 +3809,9 @@ "dev": true }, "node_modules/marked": { - "version": "3.0.7", - "resolved": "https://registry.npmjs.org/marked/-/marked-3.0.7.tgz", - "integrity": "sha512-ctKqbnLuNbsHbI26cfMyOlKgXGfl1orOv1AvWWDX7AkgfMOwCWvmuYc+mVLeWhQ9W6hdWVBynOs96VkcscKo0Q==", + "version": "3.0.8", + "resolved": "https://registry.npmjs.org/marked/-/marked-3.0.8.tgz", + "integrity": "sha512-0gVrAjo5m0VZSJb4rpL59K1unJAMb/hm8HRXqasD8VeC8m91ytDPMritgFSlKonfdt+rRYYpP/JfLxgIX8yoSw==", "dev": true, "bin": { "marked": "bin/marked" @@ -3960,6 +3969,24 @@ "url": "https://github.com/chalk/supports-color?sponsor=1" } }, + "node_modules/mocha/node_modules/yargs": { + "version": "16.2.0", + "resolved": "https://registry.npmjs.org/yargs/-/yargs-16.2.0.tgz", + "integrity": "sha512-D1mvvtDG0L5ft/jGWkLpG1+m0eQxOfaBvTNELraWj22wSVUMWxZUvYgJYcKh6jGGIkJFhH4IZPQhR4TKpc8mBw==", + "dev": true, + "dependencies": { + "cliui": "^7.0.2", + "escalade": "^3.1.1", + "get-caller-file": "^2.0.5", + "require-directory": "^2.1.1", + "string-width": "^4.2.0", + "y18n": "^5.0.5", + "yargs-parser": "^20.2.2" + }, + "engines": { + "node": ">=10" + } + }, "node_modules/ms": { "version": "2.1.2", "resolved": "https://registry.npmjs.org/ms/-/ms-2.1.2.tgz", @@ -4031,9 +4058,9 @@ } }, "node_modules/node-fetch": { - "version": "2.6.5", - "resolved": "https://registry.npmjs.org/node-fetch/-/node-fetch-2.6.5.tgz", - "integrity": "sha512-mmlIVHJEu5rnIxgEgez6b9GgWXbkZj5YZ7fx+2r94a2E+Uirsp6HsPTPlomfdHtpt/B0cdKviwkoaM6pyvUOpQ==", + "version": "2.6.6", + "resolved": "https://registry.npmjs.org/node-fetch/-/node-fetch-2.6.6.tgz", + "integrity": "sha512-Z8/6vRlTUChSdIgMa51jxQ4lrw/Jy5SOW10ObaA47/RElsAN2c5Pn8bTgFGWn/ibwzXTE8qwr1Yzx28vsecXEA==", "dev": true, "dependencies": { "whatwg-url": "^5.0.0" @@ -4055,9 +4082,9 @@ } }, "node_modules/node-releases": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/node-releases/-/node-releases-2.0.0.tgz", - "integrity": "sha512-aA87l0flFYMzCHpTM3DERFSYxc6lv/BltdbRTOMZuxZ0cwZCD3mejE5n9vLhSJCN++/eOqr77G1IO5uXxlQYWA==", + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/node-releases/-/node-releases-2.0.1.tgz", + "integrity": "sha512-CqyzN6z7Q6aMeF/ktcMVTzhAHCEpf8SOarwpzpf8pNBY2k5/oM34UHldUwp8VKI7uxct2HxSRdJjBaZeESzcxA==", "dev": true }, "node_modules/normalize-path": { @@ -5737,9 +5764,9 @@ "dev": true }, "node_modules/ts-node": { - "version": "10.3.0", - "resolved": "https://registry.npmjs.org/ts-node/-/ts-node-10.3.0.tgz", - "integrity": "sha512-RYIy3i8IgpFH45AX4fQHExrT8BxDeKTdC83QFJkNzkvt8uFB6QJ8XMyhynYiKMLxt9a7yuXaDBZNOYS3XjDcYw==", + "version": "10.4.0", + "resolved": "https://registry.npmjs.org/ts-node/-/ts-node-10.4.0.tgz", + "integrity": "sha512-g0FlPvvCXSIO1JDF6S232P5jPYqBkRL9qly81ZgAOSU7rwI0stphCgd2kLiCrU9DjQCrJMWEqcNSjQL02s6d8A==", "dev": true, "dependencies": { "@cspotcode/source-map-support": "0.7.0", @@ -5856,16 +5883,16 @@ } }, "node_modules/typedoc": { - "version": "0.22.6", - "resolved": "https://registry.npmjs.org/typedoc/-/typedoc-0.22.6.tgz", - "integrity": "sha512-ePbJqOaz0GNkU2ehRwFwBpLD4Gp6m7jbJfHysXmDdjVKc1g8DFJ83r/LOZ9TZrkC661vgpoIY3FjSPEtUilHNA==", + "version": "0.22.7", + "resolved": "https://registry.npmjs.org/typedoc/-/typedoc-0.22.7.tgz", + "integrity": "sha512-ndxxp+tU1Wczvdxp4u2/PvT1qjD6hdFdSdehpORHjE+JXmMkl2bftXCR0upHmsnesBG7VCcr8vfgloGHIH8glQ==", "dev": true, "dependencies": { "glob": "^7.2.0", "lunr": "^2.3.9", - "marked": "^3.0.4", + "marked": "^3.0.8", "minimatch": "^3.0.4", - "shiki": "^0.9.11" + "shiki": "^0.9.12" }, "bin": { "typedoc": "bin/typedoc" @@ -6273,9 +6300,9 @@ } }, "node_modules/yargs": { - "version": "16.2.0", - "resolved": "https://registry.npmjs.org/yargs/-/yargs-16.2.0.tgz", - "integrity": "sha512-D1mvvtDG0L5ft/jGWkLpG1+m0eQxOfaBvTNELraWj22wSVUMWxZUvYgJYcKh6jGGIkJFhH4IZPQhR4TKpc8mBw==", + "version": "17.2.1", + "resolved": "https://registry.npmjs.org/yargs/-/yargs-17.2.1.tgz", + "integrity": "sha512-XfR8du6ua4K6uLGm5S6fA+FIJom/MdJcFNVY8geLlp2v8GYbOXD4EB1tPNZsRn4vBzKGMgb5DRZMeWuFc2GO8Q==", "dev": true, "dependencies": { "cliui": "^7.0.2", @@ -6287,7 +6314,7 @@ "yargs-parser": "^20.2.2" }, "engines": { - "node": ">=10" + "node": ">=12" } }, "node_modules/yargs-parser": { @@ -6362,35 +6389,35 @@ }, "dependencies": { "@babel/code-frame": { - "version": "7.15.8", - "resolved": "https://registry.npmjs.org/@babel/code-frame/-/code-frame-7.15.8.tgz", - "integrity": "sha512-2IAnmn8zbvC/jKYhq5Ki9I+DwjlrtMPUCH/CpHvqI4dNnlwHwsxoIhlc8WcYY5LSYknXQtAlFYuHfqAFCvQ4Wg==", + "version": "7.16.0", + "resolved": "https://registry.npmjs.org/@babel/code-frame/-/code-frame-7.16.0.tgz", + "integrity": "sha512-IF4EOMEV+bfYwOmNxGzSnjR2EmQod7f1UXOpZM3l4i4o4QNwzjtJAu/HxdjHq0aYBvdqMuQEY1eg0nqW9ZPORA==", "dev": true, "requires": { - "@babel/highlight": "^7.14.5" + "@babel/highlight": "^7.16.0" } }, "@babel/compat-data": { - "version": "7.15.0", - "resolved": "https://registry.npmjs.org/@babel/compat-data/-/compat-data-7.15.0.tgz", - "integrity": "sha512-0NqAC1IJE0S0+lL1SWFMxMkz1pKCNCjI4tr2Zx4LJSXxCLAdr6KyArnY+sno5m3yH9g737ygOyPABDsnXkpxiA==", + "version": "7.16.0", + "resolved": "https://registry.npmjs.org/@babel/compat-data/-/compat-data-7.16.0.tgz", + "integrity": "sha512-DGjt2QZse5SGd9nfOSqO4WLJ8NN/oHkijbXbPrxuoJO3oIPJL3TciZs9FX+cOHNiY9E9l0opL8g7BmLe3T+9ew==", "dev": true }, "@babel/core": { - "version": "7.15.8", - "resolved": "https://registry.npmjs.org/@babel/core/-/core-7.15.8.tgz", - "integrity": "sha512-3UG9dsxvYBMYwRv+gS41WKHno4K60/9GPy1CJaH6xy3Elq8CTtvtjT5R5jmNhXfCYLX2mTw+7/aq5ak/gOE0og==", - "dev": true, - "requires": { - "@babel/code-frame": "^7.15.8", - "@babel/generator": "^7.15.8", - "@babel/helper-compilation-targets": "^7.15.4", - "@babel/helper-module-transforms": "^7.15.8", - "@babel/helpers": "^7.15.4", - "@babel/parser": "^7.15.8", - "@babel/template": "^7.15.4", - "@babel/traverse": "^7.15.4", - "@babel/types": "^7.15.6", + "version": "7.16.0", + "resolved": "https://registry.npmjs.org/@babel/core/-/core-7.16.0.tgz", + "integrity": "sha512-mYZEvshBRHGsIAiyH5PzCFTCfbWfoYbO/jcSdXQSUQu1/pW0xDZAUP7KEc32heqWTAfAHhV9j1vH8Sav7l+JNQ==", + "dev": true, + "requires": { + "@babel/code-frame": "^7.16.0", + "@babel/generator": "^7.16.0", + "@babel/helper-compilation-targets": "^7.16.0", + "@babel/helper-module-transforms": "^7.16.0", + "@babel/helpers": "^7.16.0", + "@babel/parser": "^7.16.0", + "@babel/template": "^7.16.0", + "@babel/traverse": "^7.16.0", + "@babel/types": "^7.16.0", "convert-source-map": "^1.7.0", "debug": "^4.1.0", "gensync": "^1.0.0-beta.2", @@ -6408,23 +6435,23 @@ } }, "@babel/generator": { - "version": "7.15.8", - "resolved": "https://registry.npmjs.org/@babel/generator/-/generator-7.15.8.tgz", - "integrity": "sha512-ECmAKstXbp1cvpTTZciZCgfOt6iN64lR0d+euv3UZisU5awfRawOvg07Utn/qBGuH4bRIEZKrA/4LzZyXhZr8g==", + "version": "7.16.0", + "resolved": "https://registry.npmjs.org/@babel/generator/-/generator-7.16.0.tgz", + "integrity": "sha512-RR8hUCfRQn9j9RPKEVXo9LiwoxLPYn6hNZlvUOR8tSnaxlD0p0+la00ZP9/SnRt6HchKr+X0fO2r8vrETiJGew==", "dev": true, "requires": { - "@babel/types": "^7.15.6", + "@babel/types": "^7.16.0", "jsesc": "^2.5.1", "source-map": "^0.5.0" } }, "@babel/helper-compilation-targets": { - "version": "7.15.4", - "resolved": "https://registry.npmjs.org/@babel/helper-compilation-targets/-/helper-compilation-targets-7.15.4.tgz", - "integrity": "sha512-rMWPCirulnPSe4d+gwdWXLfAXTTBj8M3guAf5xFQJ0nvFY7tfNAFnWdqaHegHlgDZOCT4qvhF3BYlSJag8yhqQ==", + "version": "7.16.0", + "resolved": "https://registry.npmjs.org/@babel/helper-compilation-targets/-/helper-compilation-targets-7.16.0.tgz", + "integrity": "sha512-S7iaOT1SYlqK0sQaCi21RX4+13hmdmnxIEAnQUB/eh7GeAnRjOUgTYpLkUOiRXzD+yog1JxP0qyAQZ7ZxVxLVg==", "dev": true, "requires": { - "@babel/compat-data": "^7.15.0", + "@babel/compat-data": "^7.16.0", "@babel/helper-validator-option": "^7.14.5", "browserslist": "^4.16.6", "semver": "^6.3.0" @@ -6439,105 +6466,105 @@ } }, "@babel/helper-function-name": { - "version": "7.15.4", - "resolved": "https://registry.npmjs.org/@babel/helper-function-name/-/helper-function-name-7.15.4.tgz", - "integrity": "sha512-Z91cOMM4DseLIGOnog+Z8OI6YseR9bua+HpvLAQ2XayUGU+neTtX+97caALaLdyu53I/fjhbeCnWnRH1O3jFOw==", + "version": "7.16.0", + "resolved": "https://registry.npmjs.org/@babel/helper-function-name/-/helper-function-name-7.16.0.tgz", + "integrity": "sha512-BZh4mEk1xi2h4HFjWUXRQX5AEx4rvaZxHgax9gcjdLWdkjsY7MKt5p0otjsg5noXw+pB+clMCjw+aEVYADMjog==", "dev": true, "requires": { - "@babel/helper-get-function-arity": "^7.15.4", - "@babel/template": "^7.15.4", - "@babel/types": "^7.15.4" + "@babel/helper-get-function-arity": "^7.16.0", + "@babel/template": "^7.16.0", + "@babel/types": "^7.16.0" } }, "@babel/helper-get-function-arity": { - "version": "7.15.4", - "resolved": "https://registry.npmjs.org/@babel/helper-get-function-arity/-/helper-get-function-arity-7.15.4.tgz", - "integrity": "sha512-1/AlxSF92CmGZzHnC515hm4SirTxtpDnLEJ0UyEMgTMZN+6bxXKg04dKhiRx5Enel+SUA1G1t5Ed/yQia0efrA==", + "version": "7.16.0", + "resolved": "https://registry.npmjs.org/@babel/helper-get-function-arity/-/helper-get-function-arity-7.16.0.tgz", + "integrity": "sha512-ASCquNcywC1NkYh/z7Cgp3w31YW8aojjYIlNg4VeJiHkqyP4AzIvr4qx7pYDb4/s8YcsZWqqOSxgkvjUz1kpDQ==", "dev": true, "requires": { - "@babel/types": "^7.15.4" + "@babel/types": "^7.16.0" } }, "@babel/helper-hoist-variables": { - "version": "7.15.4", - "resolved": "https://registry.npmjs.org/@babel/helper-hoist-variables/-/helper-hoist-variables-7.15.4.tgz", - "integrity": "sha512-VTy085egb3jUGVK9ycIxQiPbquesq0HUQ+tPO0uv5mPEBZipk+5FkRKiWq5apuyTE9FUrjENB0rCf8y+n+UuhA==", + "version": "7.16.0", + "resolved": "https://registry.npmjs.org/@babel/helper-hoist-variables/-/helper-hoist-variables-7.16.0.tgz", + "integrity": "sha512-1AZlpazjUR0EQZQv3sgRNfM9mEVWPK3M6vlalczA+EECcPz3XPh6VplbErL5UoMpChhSck5wAJHthlj1bYpcmg==", "dev": true, "requires": { - "@babel/types": "^7.15.4" + "@babel/types": "^7.16.0" } }, "@babel/helper-member-expression-to-functions": { - "version": "7.15.4", - "resolved": "https://registry.npmjs.org/@babel/helper-member-expression-to-functions/-/helper-member-expression-to-functions-7.15.4.tgz", - "integrity": "sha512-cokOMkxC/BTyNP1AlY25HuBWM32iCEsLPI4BHDpJCHHm1FU2E7dKWWIXJgQgSFiu4lp8q3bL1BIKwqkSUviqtA==", + "version": "7.16.0", + "resolved": "https://registry.npmjs.org/@babel/helper-member-expression-to-functions/-/helper-member-expression-to-functions-7.16.0.tgz", + "integrity": "sha512-bsjlBFPuWT6IWhl28EdrQ+gTvSvj5tqVP5Xeftp07SEuz5pLnsXZuDkDD3Rfcxy0IsHmbZ+7B2/9SHzxO0T+sQ==", "dev": true, "requires": { - "@babel/types": "^7.15.4" + "@babel/types": "^7.16.0" } }, "@babel/helper-module-imports": { - "version": "7.15.4", - "resolved": "https://registry.npmjs.org/@babel/helper-module-imports/-/helper-module-imports-7.15.4.tgz", - "integrity": "sha512-jeAHZbzUwdW/xHgHQ3QmWR4Jg6j15q4w/gCfwZvtqOxoo5DKtLHk8Bsf4c5RZRC7NmLEs+ohkdq8jFefuvIxAA==", + "version": "7.16.0", + "resolved": "https://registry.npmjs.org/@babel/helper-module-imports/-/helper-module-imports-7.16.0.tgz", + "integrity": "sha512-kkH7sWzKPq0xt3H1n+ghb4xEMP8k0U7XV3kkB+ZGy69kDk2ySFW1qPi06sjKzFY3t1j6XbJSqr4mF9L7CYVyhg==", "dev": true, "requires": { - "@babel/types": "^7.15.4" + "@babel/types": "^7.16.0" } }, "@babel/helper-module-transforms": { - "version": "7.15.8", - "resolved": "https://registry.npmjs.org/@babel/helper-module-transforms/-/helper-module-transforms-7.15.8.tgz", - "integrity": "sha512-DfAfA6PfpG8t4S6npwzLvTUpp0sS7JrcuaMiy1Y5645laRJIp/LiLGIBbQKaXSInK8tiGNI7FL7L8UvB8gdUZg==", + "version": "7.16.0", + "resolved": "https://registry.npmjs.org/@babel/helper-module-transforms/-/helper-module-transforms-7.16.0.tgz", + "integrity": "sha512-My4cr9ATcaBbmaEa8M0dZNA74cfI6gitvUAskgDtAFmAqyFKDSHQo5YstxPbN+lzHl2D9l/YOEFqb2mtUh4gfA==", "dev": true, "requires": { - "@babel/helper-module-imports": "^7.15.4", - "@babel/helper-replace-supers": "^7.15.4", - "@babel/helper-simple-access": "^7.15.4", - "@babel/helper-split-export-declaration": "^7.15.4", + "@babel/helper-module-imports": "^7.16.0", + "@babel/helper-replace-supers": "^7.16.0", + "@babel/helper-simple-access": "^7.16.0", + "@babel/helper-split-export-declaration": "^7.16.0", "@babel/helper-validator-identifier": "^7.15.7", - "@babel/template": "^7.15.4", - "@babel/traverse": "^7.15.4", - "@babel/types": "^7.15.6" + "@babel/template": "^7.16.0", + "@babel/traverse": "^7.16.0", + "@babel/types": "^7.16.0" } }, "@babel/helper-optimise-call-expression": { - "version": "7.15.4", - "resolved": "https://registry.npmjs.org/@babel/helper-optimise-call-expression/-/helper-optimise-call-expression-7.15.4.tgz", - "integrity": "sha512-E/z9rfbAOt1vDW1DR7k4SzhzotVV5+qMciWV6LaG1g4jeFrkDlJedjtV4h0i4Q/ITnUu+Pk08M7fczsB9GXBDw==", + "version": "7.16.0", + "resolved": "https://registry.npmjs.org/@babel/helper-optimise-call-expression/-/helper-optimise-call-expression-7.16.0.tgz", + "integrity": "sha512-SuI467Gi2V8fkofm2JPnZzB/SUuXoJA5zXe/xzyPP2M04686RzFKFHPK6HDVN6JvWBIEW8tt9hPR7fXdn2Lgpw==", "dev": true, "requires": { - "@babel/types": "^7.15.4" + "@babel/types": "^7.16.0" } }, "@babel/helper-replace-supers": { - "version": "7.15.4", - "resolved": "https://registry.npmjs.org/@babel/helper-replace-supers/-/helper-replace-supers-7.15.4.tgz", - "integrity": "sha512-/ztT6khaXF37MS47fufrKvIsiQkx1LBRvSJNzRqmbyeZnTwU9qBxXYLaaT/6KaxfKhjs2Wy8kG8ZdsFUuWBjzw==", + "version": "7.16.0", + "resolved": "https://registry.npmjs.org/@babel/helper-replace-supers/-/helper-replace-supers-7.16.0.tgz", + "integrity": "sha512-TQxuQfSCdoha7cpRNJvfaYxxxzmbxXw/+6cS7V02eeDYyhxderSoMVALvwupA54/pZcOTtVeJ0xccp1nGWladA==", "dev": true, "requires": { - "@babel/helper-member-expression-to-functions": "^7.15.4", - "@babel/helper-optimise-call-expression": "^7.15.4", - "@babel/traverse": "^7.15.4", - "@babel/types": "^7.15.4" + "@babel/helper-member-expression-to-functions": "^7.16.0", + "@babel/helper-optimise-call-expression": "^7.16.0", + "@babel/traverse": "^7.16.0", + "@babel/types": "^7.16.0" } }, "@babel/helper-simple-access": { - "version": "7.15.4", - "resolved": "https://registry.npmjs.org/@babel/helper-simple-access/-/helper-simple-access-7.15.4.tgz", - "integrity": "sha512-UzazrDoIVOZZcTeHHEPYrr1MvTR/K+wgLg6MY6e1CJyaRhbibftF6fR2KU2sFRtI/nERUZR9fBd6aKgBlIBaPg==", + "version": "7.16.0", + "resolved": "https://registry.npmjs.org/@babel/helper-simple-access/-/helper-simple-access-7.16.0.tgz", + "integrity": "sha512-o1rjBT/gppAqKsYfUdfHq5Rk03lMQrkPHG1OWzHWpLgVXRH4HnMM9Et9CVdIqwkCQlobnGHEJMsgWP/jE1zUiw==", "dev": true, "requires": { - "@babel/types": "^7.15.4" + "@babel/types": "^7.16.0" } }, "@babel/helper-split-export-declaration": { - "version": "7.15.4", - "resolved": "https://registry.npmjs.org/@babel/helper-split-export-declaration/-/helper-split-export-declaration-7.15.4.tgz", - "integrity": "sha512-HsFqhLDZ08DxCpBdEVtKmywj6PQbwnF6HHybur0MAnkAKnlS6uHkwnmRIkElB2Owpfb4xL4NwDmDLFubueDXsw==", + "version": "7.16.0", + "resolved": "https://registry.npmjs.org/@babel/helper-split-export-declaration/-/helper-split-export-declaration-7.16.0.tgz", + "integrity": "sha512-0YMMRpuDFNGTHNRiiqJX19GjNXA4H0E8jZ2ibccfSxaCogbm3am5WN/2nQNj0YnQwGWM1J06GOcQ2qnh3+0paw==", "dev": true, "requires": { - "@babel/types": "^7.15.4" + "@babel/types": "^7.16.0" } }, "@babel/helper-validator-identifier": { @@ -6553,23 +6580,23 @@ "dev": true }, "@babel/helpers": { - "version": "7.15.4", - "resolved": "https://registry.npmjs.org/@babel/helpers/-/helpers-7.15.4.tgz", - "integrity": "sha512-V45u6dqEJ3w2rlryYYXf6i9rQ5YMNu4FLS6ngs8ikblhu2VdR1AqAd6aJjBzmf2Qzh6KOLqKHxEN9+TFbAkAVQ==", + "version": "7.16.0", + "resolved": "https://registry.npmjs.org/@babel/helpers/-/helpers-7.16.0.tgz", + "integrity": "sha512-dVRM0StFMdKlkt7cVcGgwD8UMaBfWJHl3A83Yfs8GQ3MO0LHIIIMvK7Fa0RGOGUQ10qikLaX6D7o5htcQWgTMQ==", "dev": true, "requires": { - "@babel/template": "^7.15.4", - "@babel/traverse": "^7.15.4", - "@babel/types": "^7.15.4" + "@babel/template": "^7.16.0", + "@babel/traverse": "^7.16.0", + "@babel/types": "^7.16.0" } }, "@babel/highlight": { - "version": "7.14.5", - "resolved": "https://registry.npmjs.org/@babel/highlight/-/highlight-7.14.5.tgz", - "integrity": "sha512-qf9u2WFWVV0MppaL877j2dBtQIDgmidgjGk5VIMw3OadXvYaXn66U1BFlH2t4+t3i+8PhedppRv+i40ABzd+gg==", + "version": "7.16.0", + "resolved": "https://registry.npmjs.org/@babel/highlight/-/highlight-7.16.0.tgz", + "integrity": "sha512-t8MH41kUQylBtu2+4IQA3atqevA2lRgqA2wyVB/YiWmsDSuylZZuXOUy9ric30hfzauEFfdsuk/eXTRrGrfd0g==", "dev": true, "requires": { - "@babel/helper-validator-identifier": "^7.14.5", + "@babel/helper-validator-identifier": "^7.15.7", "chalk": "^2.0.0", "js-tokens": "^4.0.0" }, @@ -6633,35 +6660,35 @@ } }, "@babel/parser": { - "version": "7.15.8", - "resolved": "https://registry.npmjs.org/@babel/parser/-/parser-7.15.8.tgz", - "integrity": "sha512-BRYa3wcQnjS/nqI8Ac94pYYpJfojHVvVXJ97+IDCImX4Jc8W8Xv1+47enbruk+q1etOpsQNwnfFcNGw+gtPGxA==", + "version": "7.16.0", + "resolved": "https://registry.npmjs.org/@babel/parser/-/parser-7.16.0.tgz", + "integrity": "sha512-TEHWXf0xxpi9wKVyBCmRcSSDjbJ/cl6LUdlbYUHEaNQUJGhreJbZrXT6sR4+fZLxVUJqNRB4KyOvjuy/D9009A==", "dev": true }, "@babel/template": { - "version": "7.15.4", - "resolved": "https://registry.npmjs.org/@babel/template/-/template-7.15.4.tgz", - "integrity": "sha512-UgBAfEa1oGuYgDIPM2G+aHa4Nlo9Lh6mGD2bDBGMTbYnc38vulXPuC1MGjYILIEmlwl6Rd+BPR9ee3gm20CBtg==", + "version": "7.16.0", + "resolved": "https://registry.npmjs.org/@babel/template/-/template-7.16.0.tgz", + "integrity": "sha512-MnZdpFD/ZdYhXwiunMqqgyZyucaYsbL0IrjoGjaVhGilz+x8YB++kRfygSOIj1yOtWKPlx7NBp+9I1RQSgsd5A==", "dev": true, "requires": { - "@babel/code-frame": "^7.14.5", - "@babel/parser": "^7.15.4", - "@babel/types": "^7.15.4" + "@babel/code-frame": "^7.16.0", + "@babel/parser": "^7.16.0", + "@babel/types": "^7.16.0" } }, "@babel/traverse": { - "version": "7.15.4", - "resolved": "https://registry.npmjs.org/@babel/traverse/-/traverse-7.15.4.tgz", - "integrity": "sha512-W6lQD8l4rUbQR/vYgSuCAE75ADyyQvOpFVsvPPdkhf6lATXAsQIG9YdtOcu8BB1dZ0LKu+Zo3c1wEcbKeuhdlA==", - "dev": true, - "requires": { - "@babel/code-frame": "^7.14.5", - "@babel/generator": "^7.15.4", - "@babel/helper-function-name": "^7.15.4", - "@babel/helper-hoist-variables": "^7.15.4", - "@babel/helper-split-export-declaration": "^7.15.4", - "@babel/parser": "^7.15.4", - "@babel/types": "^7.15.4", + "version": "7.16.0", + "resolved": "https://registry.npmjs.org/@babel/traverse/-/traverse-7.16.0.tgz", + "integrity": "sha512-qQ84jIs1aRQxaGaxSysII9TuDaguZ5yVrEuC0BN2vcPlalwfLovVmCjbFDPECPXcYM/wLvNFfp8uDOliLxIoUQ==", + "dev": true, + "requires": { + "@babel/code-frame": "^7.16.0", + "@babel/generator": "^7.16.0", + "@babel/helper-function-name": "^7.16.0", + "@babel/helper-hoist-variables": "^7.16.0", + "@babel/helper-split-export-declaration": "^7.16.0", + "@babel/parser": "^7.16.0", + "@babel/types": "^7.16.0", "debug": "^4.1.0", "globals": "^11.1.0" }, @@ -6675,12 +6702,12 @@ } }, "@babel/types": { - "version": "7.15.6", - "resolved": "https://registry.npmjs.org/@babel/types/-/types-7.15.6.tgz", - "integrity": "sha512-BPU+7QhqNjmWyDO0/vitH/CuhpV8ZmK1wpKva8nuyNF5MJfuRNWMc+hc14+u9xT93kvykMdncrJT19h74uB1Ig==", + "version": "7.16.0", + "resolved": "https://registry.npmjs.org/@babel/types/-/types-7.16.0.tgz", + "integrity": "sha512-PJgg/k3SdLsGb3hhisFvtLOw5ts113klrpLuIPtCJIU+BB24fqq6lf8RWqKJEjzqXR9AEH1rIb5XTqwBHB+kQg==", "dev": true, "requires": { - "@babel/helper-validator-identifier": "^7.14.9", + "@babel/helper-validator-identifier": "^7.15.7", "to-fast-properties": "^2.0.0" } }, @@ -7122,9 +7149,9 @@ "dev": true }, "@types/node": { - "version": "16.11.1", - "resolved": "https://registry.npmjs.org/@types/node/-/node-16.11.1.tgz", - "integrity": "sha512-PYGcJHL9mwl1Ek3PLiYgyEKtwTMmkMw4vbiyz/ps3pfdRYLVv+SN7qHVAImrjdAXxgluDEw6Ph4lyv+m9UpRmA==", + "version": "16.11.6", + "resolved": "https://registry.npmjs.org/@types/node/-/node-16.11.6.tgz", + "integrity": "sha512-ua7PgUoeQFjmWPcoo9khiPum3Pd60k4/2ZGXt18sm2Slk0W0xZTqt5Y0Ny1NyBiN1EVQ/+FaF9NcY4Qe6rwk5w==", "dev": true }, "@types/parse-json": { @@ -7143,34 +7170,43 @@ } }, "@types/sinon": { - "version": "10.0.4", - "resolved": "https://registry.npmjs.org/@types/sinon/-/sinon-10.0.4.tgz", - "integrity": "sha512-fOYjrxQv8zJsqOY6V6ecP4eZhQBxtY80X0er1VVnUIAIZo74jHm8e1vguG5Yt4Iv8W2Wr7TgibB8MfRe32k9pA==", + "version": "10.0.6", + "resolved": "https://registry.npmjs.org/@types/sinon/-/sinon-10.0.6.tgz", + "integrity": "sha512-6EF+wzMWvBNeGrfP3Nx60hhx+FfwSg1JJBLAAP/IdIUq0EYkqCYf70VT3PhuhPX9eLD+Dp+lNdpb/ZeHG8Yezg==", "dev": true, "requires": { "@sinonjs/fake-timers": "^7.1.0" } }, - "@types/which": { - "version": "2.0.1", - "resolved": "https://registry.npmjs.org/@types/which/-/which-2.0.1.tgz", - "integrity": "sha512-Jjakcv8Roqtio6w1gr0D7y6twbhx6gGgFGF5BLwajPpnOIOxFkakFhCq+LmyyeAz7BX6ULrjBOxdKaCDy+4+dQ==", - "dev": true - }, "@types/yallist": { "version": "4.0.1", "resolved": "https://registry.npmjs.org/@types/yallist/-/yallist-4.0.1.tgz", "integrity": "sha512-G3FNJfaYtN8URU6wd6+uwFI62KO79j7n3XTYcwcFncP8gkfoi0b821GoVVt0oqKVnCqKYOMNKIGpakPoFhzAGA==", "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==", + "dev": true, + "requires": { + "@types/yargs-parser": "*" + } + }, + "@types/yargs-parser": { + "version": "20.2.1", + "resolved": "https://registry.npmjs.org/@types/yargs-parser/-/yargs-parser-20.2.1.tgz", + "integrity": "sha512-7tFImggNeNBVMsn0vLrpn1H1uPrUBdnARPTpZoitY37ZrdJREzf7I16tMrlK3hen349gr1NYh8CmZQa7CTG6Aw==", + "dev": true + }, "@typescript-eslint/eslint-plugin": { - "version": "5.1.0", - "resolved": "https://registry.npmjs.org/@typescript-eslint/eslint-plugin/-/eslint-plugin-5.1.0.tgz", - "integrity": "sha512-bekODL3Tqf36Yz8u+ilha4zGxL9mdB6LIsIoMAvvC5FAuWo4NpZYXtCbv7B2CeR1LhI/lLtLk+q4tbtxuoVuCg==", + "version": "5.2.0", + "resolved": "https://registry.npmjs.org/@typescript-eslint/eslint-plugin/-/eslint-plugin-5.2.0.tgz", + "integrity": "sha512-qQwg7sqYkBF4CIQSyRQyqsYvP+g/J0To9ZPVNJpfxfekl5RmdvQnFFTVVwpRtaUDFNvjfe/34TgY/dpc3MgNTw==", "dev": true, "requires": { - "@typescript-eslint/experimental-utils": "5.1.0", - "@typescript-eslint/scope-manager": "5.1.0", + "@typescript-eslint/experimental-utils": "5.2.0", + "@typescript-eslint/scope-manager": "5.2.0", "debug": "^4.3.2", "functional-red-black-tree": "^1.0.1", "ignore": "^5.1.8", @@ -7180,55 +7216,55 @@ } }, "@typescript-eslint/experimental-utils": { - "version": "5.1.0", - "resolved": "https://registry.npmjs.org/@typescript-eslint/experimental-utils/-/experimental-utils-5.1.0.tgz", - "integrity": "sha512-ovE9qUiZMOMgxQAESZsdBT+EXIfx/YUYAbwGUI6V03amFdOOxI9c6kitkgRvLkJaLusgMZ2xBhss+tQ0Y1HWxA==", + "version": "5.2.0", + "resolved": "https://registry.npmjs.org/@typescript-eslint/experimental-utils/-/experimental-utils-5.2.0.tgz", + "integrity": "sha512-fWyT3Agf7n7HuZZRpvUYdFYbPk3iDCq6fgu3ulia4c7yxmPnwVBovdSOX7RL+k8u6hLbrXcdAehlWUVpGh6IEw==", "dev": true, "requires": { "@types/json-schema": "^7.0.9", - "@typescript-eslint/scope-manager": "5.1.0", - "@typescript-eslint/types": "5.1.0", - "@typescript-eslint/typescript-estree": "5.1.0", + "@typescript-eslint/scope-manager": "5.2.0", + "@typescript-eslint/types": "5.2.0", + "@typescript-eslint/typescript-estree": "5.2.0", "eslint-scope": "^5.1.1", "eslint-utils": "^3.0.0" } }, "@typescript-eslint/parser": { - "version": "5.1.0", - "resolved": "https://registry.npmjs.org/@typescript-eslint/parser/-/parser-5.1.0.tgz", - "integrity": "sha512-vx1P+mhCtYw3+bRHmbalq/VKP2Y3gnzNgxGxfEWc6OFpuEL7iQdAeq11Ke3Rhy8NjgB+AHsIWEwni3e+Y7djKA==", + "version": "5.2.0", + "resolved": "https://registry.npmjs.org/@typescript-eslint/parser/-/parser-5.2.0.tgz", + "integrity": "sha512-Uyy4TjJBlh3NuA8/4yIQptyJb95Qz5PX//6p8n7zG0QnN4o3NF9Je3JHbVU7fxf5ncSXTmnvMtd/LDQWDk0YqA==", "dev": true, "requires": { - "@typescript-eslint/scope-manager": "5.1.0", - "@typescript-eslint/types": "5.1.0", - "@typescript-eslint/typescript-estree": "5.1.0", + "@typescript-eslint/scope-manager": "5.2.0", + "@typescript-eslint/types": "5.2.0", + "@typescript-eslint/typescript-estree": "5.2.0", "debug": "^4.3.2" } }, "@typescript-eslint/scope-manager": { - "version": "5.1.0", - "resolved": "https://registry.npmjs.org/@typescript-eslint/scope-manager/-/scope-manager-5.1.0.tgz", - "integrity": "sha512-yYlyVjvn5lvwCL37i4hPsa1s0ORsjkauhTqbb8MnpvUs7xykmcjGqwlNZ2Q5QpoqkJ1odlM2bqHqJwa28qV6Tw==", + "version": "5.2.0", + "resolved": "https://registry.npmjs.org/@typescript-eslint/scope-manager/-/scope-manager-5.2.0.tgz", + "integrity": "sha512-RW+wowZqPzQw8MUFltfKYZfKXqA2qgyi6oi/31J1zfXJRpOn6tCaZtd9b5u9ubnDG2n/EMvQLeZrsLNPpaUiFQ==", "dev": true, "requires": { - "@typescript-eslint/types": "5.1.0", - "@typescript-eslint/visitor-keys": "5.1.0" + "@typescript-eslint/types": "5.2.0", + "@typescript-eslint/visitor-keys": "5.2.0" } }, "@typescript-eslint/types": { - "version": "5.1.0", - "resolved": "https://registry.npmjs.org/@typescript-eslint/types/-/types-5.1.0.tgz", - "integrity": "sha512-sEwNINVxcB4ZgC6Fe6rUyMlvsB2jvVdgxjZEjQUQVlaSPMNamDOwO6/TB98kFt4sYYfNhdhTPBEQqNQZjMMswA==", + "version": "5.2.0", + "resolved": "https://registry.npmjs.org/@typescript-eslint/types/-/types-5.2.0.tgz", + "integrity": "sha512-cTk6x08qqosps6sPyP2j7NxyFPlCNsJwSDasqPNjEQ8JMD5xxj2NHxcLin5AJQ8pAVwpQ8BMI3bTxR0zxmK9qQ==", "dev": true }, "@typescript-eslint/typescript-estree": { - "version": "5.1.0", - "resolved": "https://registry.npmjs.org/@typescript-eslint/typescript-estree/-/typescript-estree-5.1.0.tgz", - "integrity": "sha512-SSz+l9YrIIsW4s0ZqaEfnjl156XQ4VRmJsbA0ZE1XkXrD3cRpzuZSVCyqeCMR3EBjF27IisWakbBDGhGNIOvfQ==", + "version": "5.2.0", + "resolved": "https://registry.npmjs.org/@typescript-eslint/typescript-estree/-/typescript-estree-5.2.0.tgz", + "integrity": "sha512-RsdXq2XmVgKbm9nLsE3mjNUM7BTr/K4DYR9WfFVMUuozHWtH5gMpiNZmtrMG8GR385EOSQ3kC9HiEMJWimxd/g==", "dev": true, "requires": { - "@typescript-eslint/types": "5.1.0", - "@typescript-eslint/visitor-keys": "5.1.0", + "@typescript-eslint/types": "5.2.0", + "@typescript-eslint/visitor-keys": "5.2.0", "debug": "^4.3.2", "globby": "^11.0.4", "is-glob": "^4.0.3", @@ -7237,12 +7273,12 @@ } }, "@typescript-eslint/visitor-keys": { - "version": "5.1.0", - "resolved": "https://registry.npmjs.org/@typescript-eslint/visitor-keys/-/visitor-keys-5.1.0.tgz", - "integrity": "sha512-uqNXepKBg81JVwjuqAxYrXa1Ql/YDzM+8g/pS+TCPxba0wZttl8m5DkrasbfnmJGHs4lQ2jTbcZ5azGhI7kK+w==", + "version": "5.2.0", + "resolved": "https://registry.npmjs.org/@typescript-eslint/visitor-keys/-/visitor-keys-5.2.0.tgz", + "integrity": "sha512-Nk7HizaXWWCUBfLA/rPNKMzXzWS8Wg9qHMuGtT+v2/YpPij4nVXrVJc24N/r5WrrmqK31jCrZxeHqIgqRzs0Xg==", "dev": true, "requires": { - "@typescript-eslint/types": "5.1.0", + "@typescript-eslint/types": "5.2.0", "eslint-visitor-keys": "^3.0.0" } }, @@ -7483,15 +7519,15 @@ "dev": true }, "browserslist": { - "version": "4.17.4", - "resolved": "https://registry.npmjs.org/browserslist/-/browserslist-4.17.4.tgz", - "integrity": "sha512-Zg7RpbZpIJRW3am9Lyckue7PLytvVxxhJj1CaJVlCWENsGEAOlnlt8X0ZxGRPp7Bt9o8tIRM5SEXy4BCPMJjLQ==", + "version": "4.17.5", + "resolved": "https://registry.npmjs.org/browserslist/-/browserslist-4.17.5.tgz", + "integrity": "sha512-I3ekeB92mmpctWBoLXe0d5wPS2cBuRvvW0JyyJHMrk9/HmP2ZjrTboNAZ8iuGqaEIlKguljbQY32OkOJIRrgoA==", "dev": true, "requires": { - "caniuse-lite": "^1.0.30001265", - "electron-to-chromium": "^1.3.867", + "caniuse-lite": "^1.0.30001271", + "electron-to-chromium": "^1.3.878", "escalade": "^3.1.1", - "node-releases": "^2.0.0", + "node-releases": "^2.0.1", "picocolors": "^1.0.0" } }, @@ -7578,9 +7614,9 @@ "dev": true }, "caniuse-lite": { - "version": "1.0.30001269", - "resolved": "https://registry.npmjs.org/caniuse-lite/-/caniuse-lite-1.0.30001269.tgz", - "integrity": "sha512-UOy8okEVs48MyHYgV+RdW1Oiudl1H6KolybD6ZquD0VcrPSgj25omXO1S7rDydjpqaISCwA8Pyx+jUQKZwWO5w==", + "version": "1.0.30001274", + "resolved": "https://registry.npmjs.org/caniuse-lite/-/caniuse-lite-1.0.30001274.tgz", + "integrity": "sha512-+Nkvv0fHyhISkiMIjnyjmf5YJcQ1IQHZN6U9TLUMroWR38FNwpsC51Gb68yueafX1V6ifOisInSgP9WJFS13ew==", "dev": true }, "chalk": { @@ -7927,9 +7963,9 @@ "dev": true }, "electron-to-chromium": { - "version": "1.3.872", - "resolved": "https://registry.npmjs.org/electron-to-chromium/-/electron-to-chromium-1.3.872.tgz", - "integrity": "sha512-qG96atLFY0agKyEETiBFNhpRLSXGSXOBuhXWpbkYqrLKKASpRyRBUtfkn0ZjIf/yXfA7FA4nScVOMpXSHFlUCQ==", + "version": "1.3.885", + "resolved": "https://registry.npmjs.org/electron-to-chromium/-/electron-to-chromium-1.3.885.tgz", + "integrity": "sha512-JXKFJcVWrdHa09n4CNZYfYaK6EW5aAew7/wr3L1OnsD1L+JHL+RCtd7QgIsxUbFPeTwPlvnpqNNTOLkoefmtXg==", "dev": true }, "emoji-regex": { @@ -7990,9 +8026,9 @@ "dev": true }, "eslint": { - "version": "8.0.1", - "resolved": "https://registry.npmjs.org/eslint/-/eslint-8.0.1.tgz", - "integrity": "sha512-LsgcwZgQ72vZ+SMp4K6pAnk2yFDWL7Ti4pJaRvsZ0Hsw2h8ZjUIW38a9AFn2cZXdBMlScMFYYgsSp4ttFI/0bA==", + "version": "8.1.0", + "resolved": "https://registry.npmjs.org/eslint/-/eslint-8.1.0.tgz", + "integrity": "sha512-JZvNneArGSUsluHWJ8g8MMs3CfIEzwaLx9KyH4tZ2i+R2/rPWzL8c0zg3rHdwYVpN/1sB9gqnjHwz9HoeJpGHw==", "dev": true, "requires": { "@eslint/eslintrc": "^1.0.3", @@ -8046,9 +8082,9 @@ } }, "estraverse": { - "version": "5.2.0", - "resolved": "https://registry.npmjs.org/estraverse/-/estraverse-5.2.0.tgz", - "integrity": "sha512-BxbNGGNm0RyRYvUdHpIwv9IWzeM9XClbOxwoATuFdOE7ZE6wHL+HQ5T8hoPM+zHvmKzzsEqhgy0GrQ5X13afiQ==", + "version": "5.3.0", + "resolved": "https://registry.npmjs.org/estraverse/-/estraverse-5.3.0.tgz", + "integrity": "sha512-MMdARuVEQziNTeJD8DgMqmhwR11BRQ/cBP+pLtYdSTnf3MIO8fFeiINEbX36ZdNlfU/7A9f3gUw49B3oQsvwBA==", "dev": true }, "ignore": { @@ -8119,9 +8155,9 @@ }, "dependencies": { "estraverse": { - "version": "5.2.0", - "resolved": "https://registry.npmjs.org/estraverse/-/estraverse-5.2.0.tgz", - "integrity": "sha512-BxbNGGNm0RyRYvUdHpIwv9IWzeM9XClbOxwoATuFdOE7ZE6wHL+HQ5T8hoPM+zHvmKzzsEqhgy0GrQ5X13afiQ==", + "version": "5.3.0", + "resolved": "https://registry.npmjs.org/estraverse/-/estraverse-5.3.0.tgz", + "integrity": "sha512-MMdARuVEQziNTeJD8DgMqmhwR11BRQ/cBP+pLtYdSTnf3MIO8fFeiINEbX36ZdNlfU/7A9f3gUw49B3oQsvwBA==", "dev": true } } @@ -8136,9 +8172,9 @@ }, "dependencies": { "estraverse": { - "version": "5.2.0", - "resolved": "https://registry.npmjs.org/estraverse/-/estraverse-5.2.0.tgz", - "integrity": "sha512-BxbNGGNm0RyRYvUdHpIwv9IWzeM9XClbOxwoATuFdOE7ZE6wHL+HQ5T8hoPM+zHvmKzzsEqhgy0GrQ5X13afiQ==", + "version": "5.3.0", + "resolved": "https://registry.npmjs.org/estraverse/-/estraverse-5.3.0.tgz", + "integrity": "sha512-MMdARuVEQziNTeJD8DgMqmhwR11BRQ/cBP+pLtYdSTnf3MIO8fFeiINEbX36ZdNlfU/7A9f3gUw49B3oQsvwBA==", "dev": true } } @@ -8462,9 +8498,9 @@ } }, "globals": { - "version": "13.11.0", - "resolved": "https://registry.npmjs.org/globals/-/globals-13.11.0.tgz", - "integrity": "sha512-08/xrJ7wQjK9kkkRoI3OFUBbLx4f+6x3SGwcPvQ0QH6goFDrOU2oyAWrmh3dJezu65buo+HBMzAMQy6rovVC3g==", + "version": "13.12.0", + "resolved": "https://registry.npmjs.org/globals/-/globals-13.12.0.tgz", + "integrity": "sha512-uS8X6lSKN2JumVoXrbUz+uG4BYG+eiawqm3qFcT7ammfbUHeCBoJMlHcec/S3krSk73/AE/f0szYFmgAA3kYZg==", "dev": true, "requires": { "type-fest": "^0.20.2" @@ -9069,9 +9105,9 @@ "dev": true }, "keyv": { - "version": "4.0.3", - "resolved": "https://registry.npmjs.org/keyv/-/keyv-4.0.3.tgz", - "integrity": "sha512-zdGa2TOpSZPq5mU6iowDARnMBZgtCqJ11dJROFi6tg6kTn4nuUdU09lFyLFSaHrWqpIJ+EBq4E8/Dc0Vx5vLdA==", + "version": "4.0.4", + "resolved": "https://registry.npmjs.org/keyv/-/keyv-4.0.4.tgz", + "integrity": "sha512-vqNHbAc8BBsxk+7QBYLW0Y219rWcClspR6WSeoHYKG5mnsSoOH+BL1pWq02DDCVdvvuUny5rkBlzMRzoqc+GIg==", "dev": true, "requires": { "json-buffer": "3.0.1" @@ -9196,9 +9232,9 @@ "dev": true }, "marked": { - "version": "3.0.7", - "resolved": "https://registry.npmjs.org/marked/-/marked-3.0.7.tgz", - "integrity": "sha512-ctKqbnLuNbsHbI26cfMyOlKgXGfl1orOv1AvWWDX7AkgfMOwCWvmuYc+mVLeWhQ9W6hdWVBynOs96VkcscKo0Q==", + "version": "3.0.8", + "resolved": "https://registry.npmjs.org/marked/-/marked-3.0.8.tgz", + "integrity": "sha512-0gVrAjo5m0VZSJb4rpL59K1unJAMb/hm8HRXqasD8VeC8m91ytDPMritgFSlKonfdt+rRYYpP/JfLxgIX8yoSw==", "dev": true }, "merge-stream": { @@ -9311,6 +9347,21 @@ "requires": { "has-flag": "^4.0.0" } + }, + "yargs": { + "version": "16.2.0", + "resolved": "https://registry.npmjs.org/yargs/-/yargs-16.2.0.tgz", + "integrity": "sha512-D1mvvtDG0L5ft/jGWkLpG1+m0eQxOfaBvTNELraWj22wSVUMWxZUvYgJYcKh6jGGIkJFhH4IZPQhR4TKpc8mBw==", + "dev": true, + "requires": { + "cliui": "^7.0.2", + "escalade": "^3.1.1", + "get-caller-file": "^2.0.5", + "require-directory": "^2.1.1", + "string-width": "^4.2.0", + "y18n": "^5.0.5", + "yargs-parser": "^20.2.2" + } } } }, @@ -9375,9 +9426,9 @@ } }, "node-fetch": { - "version": "2.6.5", - "resolved": "https://registry.npmjs.org/node-fetch/-/node-fetch-2.6.5.tgz", - "integrity": "sha512-mmlIVHJEu5rnIxgEgez6b9GgWXbkZj5YZ7fx+2r94a2E+Uirsp6HsPTPlomfdHtpt/B0cdKviwkoaM6pyvUOpQ==", + "version": "2.6.6", + "resolved": "https://registry.npmjs.org/node-fetch/-/node-fetch-2.6.6.tgz", + "integrity": "sha512-Z8/6vRlTUChSdIgMa51jxQ4lrw/Jy5SOW10ObaA47/RElsAN2c5Pn8bTgFGWn/ibwzXTE8qwr1Yzx28vsecXEA==", "dev": true, "requires": { "whatwg-url": "^5.0.0" @@ -9393,9 +9444,9 @@ } }, "node-releases": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/node-releases/-/node-releases-2.0.0.tgz", - "integrity": "sha512-aA87l0flFYMzCHpTM3DERFSYxc6lv/BltdbRTOMZuxZ0cwZCD3mejE5n9vLhSJCN++/eOqr77G1IO5uXxlQYWA==", + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/node-releases/-/node-releases-2.0.1.tgz", + "integrity": "sha512-CqyzN6z7Q6aMeF/ktcMVTzhAHCEpf8SOarwpzpf8pNBY2k5/oM34UHldUwp8VKI7uxct2HxSRdJjBaZeESzcxA==", "dev": true }, "normalize-path": { @@ -10671,9 +10722,9 @@ "dev": true }, "ts-node": { - "version": "10.3.0", - "resolved": "https://registry.npmjs.org/ts-node/-/ts-node-10.3.0.tgz", - "integrity": "sha512-RYIy3i8IgpFH45AX4fQHExrT8BxDeKTdC83QFJkNzkvt8uFB6QJ8XMyhynYiKMLxt9a7yuXaDBZNOYS3XjDcYw==", + "version": "10.4.0", + "resolved": "https://registry.npmjs.org/ts-node/-/ts-node-10.4.0.tgz", + "integrity": "sha512-g0FlPvvCXSIO1JDF6S232P5jPYqBkRL9qly81ZgAOSU7rwI0stphCgd2kLiCrU9DjQCrJMWEqcNSjQL02s6d8A==", "dev": true, "requires": { "@cspotcode/source-map-support": "0.7.0", @@ -10752,16 +10803,16 @@ } }, "typedoc": { - "version": "0.22.6", - "resolved": "https://registry.npmjs.org/typedoc/-/typedoc-0.22.6.tgz", - "integrity": "sha512-ePbJqOaz0GNkU2ehRwFwBpLD4Gp6m7jbJfHysXmDdjVKc1g8DFJ83r/LOZ9TZrkC661vgpoIY3FjSPEtUilHNA==", + "version": "0.22.7", + "resolved": "https://registry.npmjs.org/typedoc/-/typedoc-0.22.7.tgz", + "integrity": "sha512-ndxxp+tU1Wczvdxp4u2/PvT1qjD6hdFdSdehpORHjE+JXmMkl2bftXCR0upHmsnesBG7VCcr8vfgloGHIH8glQ==", "dev": true, "requires": { "glob": "^7.2.0", "lunr": "^2.3.9", - "marked": "^3.0.4", + "marked": "^3.0.8", "minimatch": "^3.0.4", - "shiki": "^0.9.11" + "shiki": "^0.9.12" }, "dependencies": { "glob": { @@ -11074,9 +11125,9 @@ "dev": true }, "yargs": { - "version": "16.2.0", - "resolved": "https://registry.npmjs.org/yargs/-/yargs-16.2.0.tgz", - "integrity": "sha512-D1mvvtDG0L5ft/jGWkLpG1+m0eQxOfaBvTNELraWj22wSVUMWxZUvYgJYcKh6jGGIkJFhH4IZPQhR4TKpc8mBw==", + "version": "17.2.1", + "resolved": "https://registry.npmjs.org/yargs/-/yargs-17.2.1.tgz", + "integrity": "sha512-XfR8du6ua4K6uLGm5S6fA+FIJom/MdJcFNVY8geLlp2v8GYbOXD4EB1tPNZsRn4vBzKGMgb5DRZMeWuFc2GO8Q==", "dev": true, "requires": { "cliui": "^7.0.2", diff --git a/package.json b/package.json index beddb1f86df..f252e4a0fa3 100644 --- a/package.json +++ b/package.json @@ -27,24 +27,24 @@ "@istanbuljs/nyc-config-typescript": "^1.0.1", "@tsconfig/node12": "^1.0.9", "@types/mocha": "^9.0.0", - "@types/node": "^16.11.1", - "@types/sinon": "^10.0.4", - "@types/which": "^2.0.1", + "@types/node": "^16.11.6", + "@types/sinon": "^10.0.6", "@types/yallist": "^4.0.1", - "@typescript-eslint/eslint-plugin": "^5.1.0", - "@typescript-eslint/parser": "^5.1.0", - "eslint": "^8.0.1", + "@types/yargs": "^17.0.5", + "@typescript-eslint/eslint-plugin": "^5.2.0", + "@typescript-eslint/parser": "^5.2.0", + "eslint": "^8.1.0", "mocha": "^9.1.3", "nyc": "^15.1.0", "release-it": "^14.11.6", "sinon": "^11.1.2", "source-map-support": "^0.5.20", - "ts-node": "^10.3.0", - "typedoc": "^0.22.6", + "ts-node": "^10.4.0", + "typedoc": "^0.22.7", "typedoc-github-wiki-theme": "^0.6.0", "typedoc-plugin-markdown": "^3.11.3", "typescript": "^4.4.4", - "which": "^2.0.2" + "yargs": "^17.2.1" }, "engines": { "node": ">=12" From 97dd75a9e5ca260208711c6be45d376a904e51cc Mon Sep 17 00:00:00 2001 From: leibale Date: Mon, 1 Nov 2021 11:33:45 -0400 Subject: [PATCH 0924/1748] increase dockers timeout to 30s --- lib/test-utils/test-utils.ts | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/lib/test-utils/test-utils.ts b/lib/test-utils/test-utils.ts index 7e488702711..a550e3f707d 100644 --- a/lib/test-utils/test-utils.ts +++ b/lib/test-utils/test-utils.ts @@ -89,7 +89,7 @@ export default class TestUtils { if (this.isVersionGreaterThan(options.minimumDockerVersion)) { const dockerImage = this.#DOCKER_IMAGE; before(function () { - this.timeout(5000); + this.timeout(30000); dockerPromise = spawnRedisServer(dockerImage, options.serverArguments); return dockerPromise; @@ -140,7 +140,7 @@ export default class TestUtils { if (this.isVersionGreaterThan(options.minimumDockerVersion)) { const dockerImage = this.#DOCKER_IMAGE; before(function () { - this.timeout(10000); + this.timeout(30000); dockersPromise = spawnRedisCluster(dockerImage, options.serverArguments); return dockersPromise; From ecbd5b696828b70f06d44d3bf29062a820956797 Mon Sep 17 00:00:00 2001 From: Chayim Date: Thu, 4 Nov 2021 18:15:52 +0200 Subject: [PATCH 0925/1748] release drafter (#1683) * release drafter * fixing contributors --- .github/release-drafter-config.yml | 44 +++++++++++++++++++++++++++ .github/workflows/release-drafter.yml | 19 ++++++++++++ 2 files changed, 63 insertions(+) create mode 100644 .github/release-drafter-config.yml create mode 100644 .github/workflows/release-drafter.yml diff --git a/.github/release-drafter-config.yml b/.github/release-drafter-config.yml new file mode 100644 index 00000000000..f17a2992fad --- /dev/null +++ b/.github/release-drafter-config.yml @@ -0,0 +1,44 @@ +name-template: 'Version $NEXT_PATCH_VERSION' +tag-template: 'v$NEXT_PATCH_VERSION' +autolabeler: + - label: 'chore' + files: + - '*.md' + - '.github/*' + - label: 'bug' + branch: + - '/bug-.+' + - label: 'chore' + branch: + - '/chore-.+' + - label: 'feature' + branch: + - '/feature-.+' +categories: + - title: 'Breaking Changes' + labels: + - 'breakingchange' + - title: '🚀 New Features' + labels: + - 'feature' + - 'enhancement' + - title: '🐛 Bug Fixes' + labels: + - 'fix' + - 'bugfix' + - 'bug' + - title: '🧰 Maintenance' + label: 'chore' +change-template: '- $TITLE (#$NUMBER)' +exclude-labels: + - 'skip-changelog' +template: | + ## Changes + + $CHANGES + + ## Contributors + We'd like to thank all the contributors who worked on this release! + + $CONTRIBUTORS + diff --git a/.github/workflows/release-drafter.yml b/.github/workflows/release-drafter.yml new file mode 100644 index 00000000000..ec2d88bf6e7 --- /dev/null +++ b/.github/workflows/release-drafter.yml @@ -0,0 +1,19 @@ +name: Release Drafter + +on: + push: + # branches to consider in the event; optional, defaults to all + branches: + - master + +jobs: + update_release_draft: + runs-on: ubuntu-latest + steps: + # Drafts your next Release notes as Pull Requests are merged into "master" + - uses: release-drafter/release-drafter@v5 + with: + # (Optional) specify config name to use, relative to .github/. Default: release-drafter.yml + config-name: release-drafter-config.yml + env: + GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }} From 3eb99dbe8378a347dd42a1ff87c89327e989443f Mon Sep 17 00:00:00 2001 From: leibale Date: Mon, 8 Nov 2021 19:21:15 -0500 Subject: [PATCH 0926/1748] use dockers for tests, use npm workspaces, add rejson & redisearch modules, fix some bugs --- .github/release-drafter-config.yml | 1 - .github/workflows/benchmark.yml | 46 - .github/workflows/tests.yml | 3 + .gitignore | 9 +- .npmignore | 20 - .nycrc.json | 4 - CONTRIBUTING.md | 5 +- README.md | 300 +------ benchmark/.gitignore | 1 - benchmark/index.js | 81 -- benchmark/package-lock.json | 849 ------------------ benchmark/package.json | 17 - package-lock.json | 616 ++++++++----- package.json | 67 +- packages/all-in-one/index.ts | 19 + packages/all-in-one/package.json | 27 + packages/all-in-one/tsconfig.json | 9 + .../client/.eslintrc.json | 0 packages/client/.gitignore | 1 + packages/client/.npmignore | 9 + packages/client/.nycrc.json | 4 + CHANGELOG.md => packages/client/CHANGELOG.md | 0 packages/client/LICENSE | 24 + packages/client/README.md | 294 ++++++ {docs => packages/client/docs}/FAQ.md | 0 .../client/docs}/client-configuration.md | 2 +- {docs => packages/client/docs}/clustering.md | 4 +- .../client/docs}/isolated-execution.md | 0 {docs => packages/client/docs}/v3-to-v4.md | 0 .../client/examples}/README.md | 0 .../client/examples}/blocking-list-pop.js | 0 .../examples}/command-with-modifiers.js | 0 .../client/examples}/connect-as-acl-user.js | 0 .../client/examples}/lua-multi-incr.js | 0 .../client/examples}/package-lock.json | 0 .../client/examples}/package.json | 7 +- .../client/examples}/set-scan.js | 0 index.ts => packages/client/index.ts | 0 .../client/lib}/client/commands-queue.ts | 0 .../client/lib}/client/commands.ts | 0 .../client/lib}/client/index.spec.ts | 20 +- {lib => packages/client/lib}/client/index.ts | 0 .../client/lib}/client/multi-command.ts | 0 .../client/lib}/client/socket.spec.ts | 0 {lib => packages/client/lib}/client/socket.ts | 0 .../client/lib}/cluster/cluster-slots.ts | 0 .../client/lib}/cluster/commands.ts | 0 .../client/lib}/cluster/index.spec.ts | 70 +- {lib => packages/client/lib}/cluster/index.ts | 0 .../client/lib}/cluster/multi-command.ts | 0 .../client/lib}/command-options.ts | 0 .../client/lib}/commander.spec.ts | 0 {lib => packages/client/lib}/commander.ts | 0 .../client/lib}/commands/ACL_CAT.spec.ts | 0 .../client/lib}/commands/ACL_CAT.ts | 0 .../client/lib}/commands/ACL_DELUSER.spec.ts | 0 .../client/lib}/commands/ACL_DELUSER.ts | 0 .../client/lib}/commands/ACL_GENPASS.spec.ts | 0 .../client/lib}/commands/ACL_GENPASS.ts | 0 .../client/lib}/commands/ACL_GETUSER.spec.ts | 0 .../client/lib}/commands/ACL_GETUSER.ts | 0 .../client/lib}/commands/ACL_LIST.spec.ts | 0 .../client/lib}/commands/ACL_LIST.ts | 0 .../client/lib}/commands/ACL_LOAD.spec.ts | 0 .../client/lib}/commands/ACL_LOAD.ts | 0 .../client/lib}/commands/ACL_LOG.spec.ts | 0 .../client/lib}/commands/ACL_LOG.ts | 0 .../lib}/commands/ACL_LOG_RESET.spec.ts | 0 .../client/lib}/commands/ACL_LOG_RESET.ts | 0 .../client/lib}/commands/ACL_SAVE.spec.ts | 0 .../client/lib}/commands/ACL_SAVE.ts | 0 .../client/lib}/commands/ACL_SETUSER.spec.ts | 0 .../client/lib}/commands/ACL_SETUSER.ts | 0 .../client/lib}/commands/ACL_USERS.spec.ts | 0 .../client/lib}/commands/ACL_USERS.ts | 0 .../client/lib}/commands/ACL_WHOAMI.spec.ts | 0 .../client/lib}/commands/ACL_WHOAMI.ts | 0 .../client/lib}/commands/APPEND.spec.ts | 0 .../client/lib}/commands/APPEND.ts | 0 .../client/lib}/commands/ASKING.spec.ts | 0 .../client/lib}/commands/ASKING.ts | 0 .../client/lib}/commands/AUTH.spec.ts | 0 {lib => packages/client/lib}/commands/AUTH.ts | 0 .../client/lib}/commands/BGREWRITEAOF.spec.ts | 0 .../client/lib}/commands/BGREWRITEAOF.ts | 0 .../client/lib}/commands/BGSAVE.spec.ts | 0 .../client/lib}/commands/BGSAVE.ts | 0 .../client/lib}/commands/BITCOUNT.spec.ts | 0 .../client/lib}/commands/BITCOUNT.ts | 0 .../client/lib}/commands/BITFIELD.spec.ts | 0 .../client/lib}/commands/BITFIELD.ts | 0 .../client/lib}/commands/BITOP.spec.ts | 0 .../client/lib}/commands/BITOP.ts | 0 .../client/lib}/commands/BITPOS.spec.ts | 0 .../client/lib}/commands/BITPOS.ts | 0 .../client/lib}/commands/BLMOVE.spec.ts | 0 .../client/lib}/commands/BLMOVE.ts | 0 .../client/lib}/commands/BLPOP.spec.ts | 0 .../client/lib}/commands/BLPOP.ts | 0 .../client/lib}/commands/BRPOP.spec.ts | 0 .../client/lib}/commands/BRPOP.ts | 0 .../client/lib}/commands/BRPOPLPUSH.spec.ts | 0 .../client/lib}/commands/BRPOPLPUSH.ts | 0 .../client/lib}/commands/BZPOPMAX.spec.ts | 0 .../client/lib}/commands/BZPOPMAX.ts | 0 .../client/lib}/commands/BZPOPMIN.spec.ts | 0 .../client/lib}/commands/BZPOPMIN.ts | 0 .../client/lib}/commands/CLIENT_ID.spec.ts | 0 .../client/lib}/commands/CLIENT_ID.ts | 0 .../client/lib}/commands/CLIENT_INFO.spec.ts | 0 .../client/lib}/commands/CLIENT_INFO.ts | 0 .../lib}/commands/CLUSTER_ADDSLOTS.spec.ts | 0 .../client/lib}/commands/CLUSTER_ADDSLOTS.ts | 0 .../lib}/commands/CLUSTER_FLUSHSLOTS.spec.ts | 0 .../lib}/commands/CLUSTER_FLUSHSLOTS.ts | 0 .../commands/CLUSTER_GETKEYSINSLOT.spec.ts | 0 .../lib}/commands/CLUSTER_GETKEYSINSLOT.ts | 0 .../client/lib}/commands/CLUSTER_INFO.spec.ts | 0 .../client/lib}/commands/CLUSTER_INFO.ts | 0 .../client/lib}/commands/CLUSTER_MEET.spec.ts | 0 .../client/lib}/commands/CLUSTER_MEET.ts | 0 .../lib}/commands/CLUSTER_NODES.spec.ts | 0 .../client/lib}/commands/CLUSTER_NODES.ts | 0 .../lib}/commands/CLUSTER_RESET.spec.ts | 0 .../client/lib}/commands/CLUSTER_RESET.ts | 0 .../lib}/commands/CLUSTER_SETSLOT.spec.ts | 0 .../client/lib}/commands/CLUSTER_SETSLOT.ts | 0 .../lib}/commands/CLUSTER_SLOTS.spec.ts | 0 .../client/lib}/commands/CLUSTER_SLOTS.ts | 0 .../client/lib}/commands/COMMAND.spec.ts | 6 +- .../client/lib}/commands/COMMAND.ts | 0 .../lib}/commands/COMMAND_COUNT.spec.ts | 0 .../client/lib}/commands/COMMAND_COUNT.ts | 0 .../lib}/commands/COMMAND_GETKEYS.spec.ts | 0 .../client/lib}/commands/COMMAND_GETKEYS.ts | 0 .../client/lib}/commands/COMMAND_INFO.spec.ts | 0 .../client/lib}/commands/COMMAND_INFO.ts | 0 .../client/lib}/commands/CONFIG_GET.spec.ts | 0 .../client/lib}/commands/CONFIG_GET.ts | 0 .../lib}/commands/CONFIG_RESETSTAT.spec.ts | 0 .../client/lib}/commands/CONFIG_RESETSTAT.ts | 0 .../lib}/commands/CONFIG_REWRITE.spec.ts | 0 .../client/lib}/commands/CONFIG_REWRITE.ts | 0 .../client/lib}/commands/CONFIG_SET.spec.ts | 0 .../client/lib}/commands/CONFIG_SET.ts | 0 .../client/lib}/commands/COPY.spec.ts | 0 {lib => packages/client/lib}/commands/COPY.ts | 0 .../client/lib}/commands/DBSIZE.spec.ts | 0 .../client/lib}/commands/DBSIZE.ts | 0 .../client/lib}/commands/DECR.spec.ts | 0 {lib => packages/client/lib}/commands/DECR.ts | 0 .../client/lib}/commands/DECRBY.spec.ts | 0 .../client/lib}/commands/DECRBY.ts | 0 .../client/lib}/commands/DEL.spec.ts | 0 {lib => packages/client/lib}/commands/DEL.ts | 0 .../client/lib}/commands/DISCARD.spec.ts | 0 .../client/lib}/commands/DISCARD.ts | 0 .../client/lib}/commands/DUMP.spec.ts | 0 {lib => packages/client/lib}/commands/DUMP.ts | 0 .../client/lib}/commands/ECHO.spec.ts | 0 {lib => packages/client/lib}/commands/ECHO.ts | 0 .../client/lib}/commands/EVAL.spec.ts | 0 {lib => packages/client/lib}/commands/EVAL.ts | 0 .../client/lib}/commands/EVALSHA.spec.ts | 0 .../client/lib}/commands/EVALSHA.ts | 0 .../client/lib}/commands/EXISTS.spec.ts | 0 .../client/lib}/commands/EXISTS.ts | 0 .../client/lib}/commands/EXPIRE.spec.ts | 0 .../client/lib}/commands/EXPIRE.ts | 0 .../client/lib}/commands/EXPIREAT.spec.ts | 0 .../client/lib}/commands/EXPIREAT.ts | 0 .../client/lib}/commands/FAILOVER.spec.ts | 0 .../client/lib}/commands/FAILOVER.ts | 0 .../client/lib}/commands/FLUSHALL.spec.ts | 0 .../client/lib}/commands/FLUSHALL.ts | 0 .../client/lib}/commands/FLUSHDB.spec.ts | 0 .../client/lib}/commands/FLUSHDB.ts | 0 .../client/lib}/commands/GEOADD.spec.ts | 0 .../client/lib}/commands/GEOADD.ts | 0 .../client/lib}/commands/GEODIST.spec.ts | 0 .../client/lib}/commands/GEODIST.ts | 0 .../client/lib}/commands/GEOHASH.spec.ts | 0 .../client/lib}/commands/GEOHASH.ts | 0 .../client/lib}/commands/GEOPOS.spec.ts | 0 .../client/lib}/commands/GEOPOS.ts | 0 .../client/lib}/commands/GEOSEARCH.spec.ts | 0 .../client/lib}/commands/GEOSEARCH.ts | 0 .../lib}/commands/GEOSEARCHSTORE.spec.ts | 0 .../client/lib}/commands/GEOSEARCHSTORE.ts | 0 .../lib}/commands/GEOSEARCH_WITH.spec.ts | 0 .../client/lib}/commands/GEOSEARCH_WITH.ts | 0 .../client/lib}/commands/GET.spec.ts | 0 {lib => packages/client/lib}/commands/GET.ts | 0 .../client/lib}/commands/GETBIT.spec.ts | 0 .../client/lib}/commands/GETBIT.ts | 0 .../client/lib}/commands/GETDEL.spec.ts | 0 .../client/lib}/commands/GETDEL.ts | 0 .../client/lib}/commands/GETEX.spec.ts | 0 .../client/lib}/commands/GETEX.ts | 0 .../client/lib}/commands/GETRANGE.spec.ts | 0 .../client/lib}/commands/GETRANGE.ts | 0 .../client/lib}/commands/GETSET.spec.ts | 0 .../client/lib}/commands/GETSET.ts | 0 .../client/lib}/commands/GET_BUFFER.spec.ts | 0 .../client/lib}/commands/GET_BUFFER.ts | 0 .../client/lib}/commands/HDEL.spec.ts | 0 {lib => packages/client/lib}/commands/HDEL.ts | 0 .../client/lib}/commands/HELLO.spec.ts | 0 .../client/lib}/commands/HELLO.ts | 0 .../client/lib}/commands/HEXISTS.spec.ts | 0 .../client/lib}/commands/HEXISTS.ts | 0 .../client/lib}/commands/HGET.spec.ts | 0 {lib => packages/client/lib}/commands/HGET.ts | 0 .../client/lib}/commands/HGETALL.spec.ts | 0 .../client/lib}/commands/HGETALL.ts | 0 .../client/lib}/commands/HINCRBY.spec.ts | 0 .../client/lib}/commands/HINCRBY.ts | 0 .../client/lib}/commands/HINCRBYFLOAT.spec.ts | 0 .../client/lib}/commands/HINCRBYFLOAT.ts | 0 .../client/lib}/commands/HKEYS.spec.ts | 0 .../client/lib}/commands/HKEYS.ts | 0 .../client/lib}/commands/HLEN.spec.ts | 0 {lib => packages/client/lib}/commands/HLEN.ts | 0 .../client/lib}/commands/HMGET.spec.ts | 0 .../client/lib}/commands/HMGET.ts | 0 .../client/lib}/commands/HRANDFIELD.spec.ts | 0 .../client/lib}/commands/HRANDFIELD.ts | 0 .../lib}/commands/HRANDFIELD_COUNT.spec.ts | 0 .../client/lib}/commands/HRANDFIELD_COUNT.ts | 0 .../HRANDFIELD_COUNT_WITHVALUES.spec.ts | 0 .../commands/HRANDFIELD_COUNT_WITHVALUES.ts | 0 .../client/lib}/commands/HSCAN.spec.ts | 0 .../client/lib}/commands/HSCAN.ts | 0 .../client/lib}/commands/HSET.spec.ts | 0 {lib => packages/client/lib}/commands/HSET.ts | 0 .../client/lib}/commands/HSETNX.spec.ts | 0 .../client/lib}/commands/HSETNX.ts | 0 .../client/lib}/commands/HSTRLEN.spec.ts | 0 .../client/lib}/commands/HSTRLEN.ts | 0 .../client/lib}/commands/HVALS.spec.ts | 0 .../client/lib}/commands/HVALS.ts | 0 .../client/lib}/commands/INCR.spec.ts | 0 {lib => packages/client/lib}/commands/INCR.ts | 0 .../client/lib}/commands/INCRBY.spec.ts | 0 .../client/lib}/commands/INCRBY.ts | 0 .../client/lib}/commands/INCRBYFLOAT.spec.ts | 0 .../client/lib}/commands/INCRBYFLOAT.ts | 0 .../client/lib}/commands/INFO.spec.ts | 0 {lib => packages/client/lib}/commands/INFO.ts | 0 .../client/lib}/commands/KEYS.spec.ts | 0 {lib => packages/client/lib}/commands/KEYS.ts | 0 .../client/lib}/commands/LASTSAVE.spec.ts | 0 .../client/lib}/commands/LASTSAVE.ts | 0 .../client/lib}/commands/LINDEX.spec.ts | 0 .../client/lib}/commands/LINDEX.ts | 0 .../client/lib}/commands/LINSERT.spec.ts | 0 .../client/lib}/commands/LINSERT.ts | 0 .../client/lib}/commands/LLEN.spec.ts | 0 {lib => packages/client/lib}/commands/LLEN.ts | 0 .../client/lib}/commands/LMOVE.spec.ts | 0 .../client/lib}/commands/LMOVE.ts | 0 .../client/lib}/commands/LOLWUT.spec.ts | 0 .../client/lib}/commands/LOLWUT.ts | 0 .../client/lib}/commands/LPOP.spec.ts | 0 {lib => packages/client/lib}/commands/LPOP.ts | 0 .../client/lib}/commands/LPOP_COUNT.spec.ts | 0 .../client/lib}/commands/LPOP_COUNT.ts | 0 .../client/lib}/commands/LPOS.spec.ts | 0 {lib => packages/client/lib}/commands/LPOS.ts | 0 .../client/lib}/commands/LPOS_COUNT.spec.ts | 0 .../client/lib}/commands/LPOS_COUNT.ts | 0 .../client/lib}/commands/LPUSH.spec.ts | 0 .../client/lib}/commands/LPUSH.ts | 0 .../client/lib}/commands/LPUSHX.spec.ts | 0 .../client/lib}/commands/LPUSHX.ts | 0 .../client/lib}/commands/LRANGE.spec.ts | 0 .../client/lib}/commands/LRANGE.ts | 0 .../client/lib}/commands/LREM.spec.ts | 0 {lib => packages/client/lib}/commands/LREM.ts | 0 .../client/lib}/commands/LSET.spec.ts | 0 {lib => packages/client/lib}/commands/LSET.ts | 0 .../client/lib}/commands/LTRIM.spec.ts | 0 .../client/lib}/commands/LTRIM.ts | 0 .../lib}/commands/MEMORY_DOCTOR.spec.ts | 0 .../client/lib}/commands/MEMORY_DOCTOR.ts | 0 .../lib}/commands/MEMORY_MALLOC-STATS.spec.ts | 0 .../lib}/commands/MEMORY_MALLOC-STATS.ts | 0 .../client/lib}/commands/MEMORY_PURGE.spec.ts | 0 .../client/lib}/commands/MEMORY_PURGE.ts | 0 .../client/lib}/commands/MEMORY_STATS.spec.ts | 0 .../client/lib}/commands/MEMORY_STATS.ts | 0 .../client/lib}/commands/MEMORY_USAGE.spec.ts | 0 .../client/lib}/commands/MEMORY_USAGE.ts | 0 .../client/lib}/commands/MGET.spec.ts | 0 {lib => packages/client/lib}/commands/MGET.ts | 0 .../client/lib}/commands/MIGRATE.spec.ts | 0 .../client/lib}/commands/MIGRATE.ts | 0 .../client/lib}/commands/MODULE_LIST.spec.ts | 0 .../client/lib}/commands/MODULE_LIST.ts | 0 .../client/lib}/commands/MODULE_LOAD.spec.ts | 0 .../client/lib}/commands/MODULE_LOAD.ts | 0 .../lib}/commands/MODULE_UNLOAD.spec.ts | 0 .../client/lib}/commands/MODULE_UNLOAD.ts | 0 .../client/lib}/commands/MOVE.spec.ts | 0 {lib => packages/client/lib}/commands/MOVE.ts | 0 .../client/lib}/commands/MSET.spec.ts | 0 {lib => packages/client/lib}/commands/MSET.ts | 0 .../client/lib}/commands/MSETNX.spec.ts | 0 .../client/lib}/commands/MSETNX.ts | 0 .../client/lib}/commands/PERSIST.spec.ts | 0 .../client/lib}/commands/PERSIST.ts | 0 .../client/lib}/commands/PEXPIRE.spec.ts | 0 .../client/lib}/commands/PEXPIRE.ts | 0 .../client/lib}/commands/PEXPIREAT.spec.ts | 0 .../client/lib}/commands/PEXPIREAT.ts | 0 .../client/lib}/commands/PFADD.spec.ts | 0 .../client/lib}/commands/PFADD.ts | 0 .../client/lib}/commands/PFCOUNT.spec.ts | 0 .../client/lib}/commands/PFCOUNT.ts | 0 .../client/lib}/commands/PFMERGE.spec.ts | 0 .../client/lib}/commands/PFMERGE.ts | 0 .../client/lib}/commands/PING.spec.ts | 0 {lib => packages/client/lib}/commands/PING.ts | 0 .../client/lib}/commands/PSETEX.spec.ts | 0 .../client/lib}/commands/PSETEX.ts | 0 .../client/lib}/commands/PTTL.spec.ts | 0 {lib => packages/client/lib}/commands/PTTL.ts | 0 .../client/lib}/commands/PUBLISH.spec.ts | 0 .../client/lib}/commands/PUBLISH.ts | 0 .../lib}/commands/PUBSUB_CHANNELS.spec.ts | 0 .../client/lib}/commands/PUBSUB_CHANNELS.ts | 0 .../lib}/commands/PUBSUB_NUMPAT.spec.ts | 0 .../client/lib}/commands/PUBSUB_NUMPAT.ts | 0 .../lib}/commands/PUBSUB_NUMSUB.spec.ts | 0 .../client/lib}/commands/PUBSUB_NUMSUB.ts | 0 .../client/lib}/commands/RANDOMKEY.spec.ts | 0 .../client/lib}/commands/RANDOMKEY.ts | 0 .../client/lib}/commands/READONLY.spec.ts | 0 .../client/lib}/commands/READONLY.ts | 0 .../client/lib}/commands/READWRITE.spec.ts | 0 .../client/lib}/commands/READWRITE.ts | 0 .../client/lib}/commands/RENAME.spec.ts | 0 .../client/lib}/commands/RENAME.ts | 0 .../client/lib}/commands/RENAMENX.spec.ts | 0 .../client/lib}/commands/RENAMENX.ts | 0 .../client/lib}/commands/REPLICAOF.spec.ts | 0 .../client/lib}/commands/REPLICAOF.ts | 0 .../lib}/commands/RESTORE-ASKING.spec.ts | 0 .../client/lib}/commands/RESTORE-ASKING.ts | 0 .../client/lib}/commands/ROLE.spec.ts | 0 {lib => packages/client/lib}/commands/ROLE.ts | 0 .../client/lib}/commands/RPOP.spec.ts | 0 {lib => packages/client/lib}/commands/RPOP.ts | 0 .../client/lib}/commands/RPOPLPUSH.spec.ts | 0 .../client/lib}/commands/RPOPLPUSH.ts | 0 .../client/lib}/commands/RPOP_COUNT.spec.ts | 0 .../client/lib}/commands/RPOP_COUNT.ts | 0 .../client/lib}/commands/RPUSH.spec.ts | 0 .../client/lib}/commands/RPUSH.ts | 0 .../client/lib}/commands/RPUSHX.spec.ts | 0 .../client/lib}/commands/RPUSHX.ts | 0 .../client/lib}/commands/SADD.spec.ts | 0 {lib => packages/client/lib}/commands/SADD.ts | 0 .../client/lib}/commands/SAVE.spec.ts | 0 {lib => packages/client/lib}/commands/SAVE.ts | 0 .../client/lib}/commands/SCAN.spec.ts | 0 {lib => packages/client/lib}/commands/SCAN.ts | 0 .../client/lib}/commands/SCARD.spec.ts | 0 .../client/lib}/commands/SCARD.ts | 0 .../client/lib}/commands/SCRIPT_DEBUG.spec.ts | 0 .../client/lib}/commands/SCRIPT_DEBUG.ts | 0 .../lib}/commands/SCRIPT_EXISTS.spec.ts | 0 .../client/lib}/commands/SCRIPT_EXISTS.ts | 0 .../client/lib}/commands/SCRIPT_FLUSH.spec.ts | 0 .../client/lib}/commands/SCRIPT_FLUSH.ts | 0 .../client/lib}/commands/SCRIPT_KILL.spec.ts | 0 .../client/lib}/commands/SCRIPT_KILL.ts | 0 .../client/lib}/commands/SCRIPT_LOAD.spec.ts | 0 .../client/lib}/commands/SCRIPT_LOAD.ts | 0 .../client/lib}/commands/SDIFF.spec.ts | 0 .../client/lib}/commands/SDIFF.ts | 0 .../client/lib}/commands/SDIFFSTORE.spec.ts | 0 .../client/lib}/commands/SDIFFSTORE.ts | 0 .../client/lib}/commands/SET.spec.ts | 0 {lib => packages/client/lib}/commands/SET.ts | 0 .../client/lib}/commands/SETBIT.spec.ts | 0 .../client/lib}/commands/SETBIT.ts | 0 .../client/lib}/commands/SETEX.spec.ts | 0 .../client/lib}/commands/SETEX.ts | 0 .../client/lib}/commands/SETNX .spec.ts | 0 .../client/lib}/commands/SETNX.ts | 0 .../client/lib}/commands/SETRANGE.spec.ts | 0 .../client/lib}/commands/SETRANGE.ts | 0 .../client/lib}/commands/SHUTDOWN.spec.ts | 0 .../client/lib}/commands/SHUTDOWN.ts | 0 .../client/lib}/commands/SINTER.spec.ts | 0 .../client/lib}/commands/SINTER.ts | 0 .../client/lib}/commands/SINTERSTORE.spec.ts | 0 .../client/lib}/commands/SINTERSTORE.ts | 0 .../client/lib}/commands/SISMEMBER.spec.ts | 0 .../client/lib}/commands/SISMEMBER.ts | 0 .../client/lib}/commands/SMEMBERS.spec.ts | 0 .../client/lib}/commands/SMEMBERS.ts | 0 .../client/lib}/commands/SMISMEMBER.spec.ts | 0 .../client/lib}/commands/SMISMEMBER.ts | 0 .../client/lib}/commands/SMOVE.spec.ts | 0 .../client/lib}/commands/SMOVE.ts | 0 .../client/lib}/commands/SORT.spec.ts | 0 {lib => packages/client/lib}/commands/SORT.ts | 0 .../client/lib}/commands/SPOP.spec.ts | 0 {lib => packages/client/lib}/commands/SPOP.ts | 0 .../client/lib}/commands/SRANDMEMBER.spec.ts | 0 .../client/lib}/commands/SRANDMEMBER.ts | 0 .../lib}/commands/SRANDMEMBER_COUNT.spec.ts | 0 .../client/lib}/commands/SRANDMEMBER_COUNT.ts | 0 .../client/lib}/commands/SREM.spec.ts | 0 {lib => packages/client/lib}/commands/SREM.ts | 0 .../client/lib}/commands/SSCAN.spec.ts | 0 .../client/lib}/commands/SSCAN.ts | 0 .../client/lib}/commands/STRLEN.spec.ts | 0 .../client/lib}/commands/STRLEN.ts | 0 .../client/lib}/commands/SUNION.spec.ts | 0 .../client/lib}/commands/SUNION.ts | 0 .../client/lib}/commands/SUNIONSTORE.spec.ts | 0 .../client/lib}/commands/SUNIONSTORE.ts | 0 .../client/lib}/commands/SWAPDB.spec.ts | 0 .../client/lib}/commands/SWAPDB.ts | 0 .../client/lib}/commands/TIME.spec.ts | 10 +- {lib => packages/client/lib}/commands/TIME.ts | 0 .../client/lib}/commands/TOUCH.spec.ts | 0 .../client/lib}/commands/TOUCH.ts | 0 .../client/lib}/commands/TTL.spec.ts | 0 {lib => packages/client/lib}/commands/TTL.ts | 0 .../client/lib}/commands/TYPE.spec.ts | 0 {lib => packages/client/lib}/commands/TYPE.ts | 0 .../client/lib}/commands/UNLINK.spec.ts | 0 .../client/lib}/commands/UNLINK.ts | 0 .../client/lib}/commands/UNWATCH.spec.ts | 0 .../client/lib}/commands/UNWATCH.ts | 0 .../client/lib}/commands/WAIT.spec.ts | 0 {lib => packages/client/lib}/commands/WAIT.ts | 0 .../client/lib}/commands/WATCH.spec.ts | 0 .../client/lib}/commands/WATCH.ts | 0 .../client/lib}/commands/XACK.spec.ts | 0 {lib => packages/client/lib}/commands/XACK.ts | 0 .../client/lib}/commands/XADD.spec.ts | 0 {lib => packages/client/lib}/commands/XADD.ts | 0 .../client/lib}/commands/XAUTOCLAIM.spec.ts | 0 .../client/lib}/commands/XAUTOCLAIM.ts | 0 .../lib}/commands/XAUTOCLAIM_JUSTID.spec.ts | 0 .../client/lib}/commands/XAUTOCLAIM_JUSTID.ts | 0 .../client/lib}/commands/XCLAIM.spec.ts | 0 .../client/lib}/commands/XCLAIM.ts | 0 .../lib}/commands/XCLAIM_JUSTID.spec.ts | 0 .../client/lib}/commands/XCLAIM_JUSTID.ts | 0 .../client/lib}/commands/XDEL.spec.ts | 0 {lib => packages/client/lib}/commands/XDEL.ts | 0 .../lib}/commands/XGROUP_CREATE.spec.ts | 0 .../client/lib}/commands/XGROUP_CREATE.ts | 0 .../commands/XGROUP_CREATECONSUMER.spec.ts | 0 .../lib}/commands/XGROUP_CREATECONSUMER.ts | 0 .../lib}/commands/XGROUP_DELCONSUMER.spec.ts | 0 .../lib}/commands/XGROUP_DELCONSUMER.ts | 0 .../lib}/commands/XGROUP_DESTROY.spec.ts | 0 .../client/lib}/commands/XGROUP_DESTROY.ts | 0 .../client/lib}/commands/XGROUP_SETID.spec.ts | 0 .../client/lib}/commands/XGROUP_SETID.ts | 0 .../lib}/commands/XINFO_CONSUMERS.spec.ts | 0 .../client/lib}/commands/XINFO_CONSUMERS.ts | 0 .../client/lib}/commands/XINFO_GROUPS.spec.ts | 0 .../client/lib}/commands/XINFO_GROUPS.ts | 0 .../client/lib}/commands/XINFO_STREAM.spec.ts | 0 .../client/lib}/commands/XINFO_STREAM.ts | 0 .../client/lib}/commands/XLEN.spec.ts | 0 {lib => packages/client/lib}/commands/XLEN.ts | 0 .../client/lib}/commands/XPENDING.spec.ts | 0 .../client/lib}/commands/XPENDING.ts | 0 .../lib}/commands/XPENDING_RANGE.spec.ts | 0 .../client/lib}/commands/XPENDING_RANGE.ts | 0 .../client/lib}/commands/XRANGE.spec.ts | 0 .../client/lib}/commands/XRANGE.ts | 0 .../client/lib}/commands/XREAD.spec.ts | 0 .../client/lib}/commands/XREAD.ts | 0 .../client/lib}/commands/XREADGROUP.spec.ts | 0 .../client/lib}/commands/XREADGROUP.ts | 0 .../client/lib}/commands/XREVRANGE.spec.ts | 0 .../client/lib}/commands/XREVRANGE.ts | 0 .../client/lib}/commands/XTRIM.spec.ts | 0 .../client/lib}/commands/XTRIM.ts | 0 .../client/lib}/commands/ZADD.spec.ts | 0 {lib => packages/client/lib}/commands/ZADD.ts | 0 .../client/lib}/commands/ZCARD.spec.ts | 0 .../client/lib}/commands/ZCARD.ts | 0 .../client/lib}/commands/ZCOUNT.spec.ts | 0 .../client/lib}/commands/ZCOUNT.ts | 0 .../client/lib}/commands/ZDIFF.spec.ts | 0 .../client/lib}/commands/ZDIFF.ts | 0 .../client/lib}/commands/ZDIFFSTORE.spec.ts | 0 .../client/lib}/commands/ZDIFFSTORE.ts | 0 .../lib}/commands/ZDIFF_WITHSCORES.spec.ts | 0 .../client/lib}/commands/ZDIFF_WITHSCORES.ts | 0 .../client/lib}/commands/ZINCRBY.spec.ts | 0 .../client/lib}/commands/ZINCRBY.ts | 0 .../client/lib}/commands/ZINTER.spec.ts | 0 .../client/lib}/commands/ZINTER.ts | 0 .../client/lib}/commands/ZINTERSTORE.spec.ts | 0 .../client/lib}/commands/ZINTERSTORE.ts | 0 .../lib}/commands/ZINTER_WITHSCORES.spec.ts | 0 .../client/lib}/commands/ZINTER_WITHSCORES.ts | 0 .../client/lib}/commands/ZLEXCOUNT.spec.ts | 0 .../client/lib}/commands/ZLEXCOUNT.ts | 0 .../client/lib}/commands/ZMSCORE.spec.ts | 0 .../client/lib}/commands/ZMSCORE.ts | 0 .../client/lib}/commands/ZPOPMAX.spec.ts | 0 .../client/lib}/commands/ZPOPMAX.ts | 0 .../lib}/commands/ZPOPMAX_COUNT.spec.ts | 0 .../client/lib}/commands/ZPOPMAX_COUNT.ts | 0 .../client/lib}/commands/ZPOPMIN.spec.ts | 0 .../client/lib}/commands/ZPOPMIN.ts | 0 .../lib}/commands/ZPOPMIN_COUNT.spec.ts | 0 .../client/lib}/commands/ZPOPMIN_COUNT.ts | 0 .../client/lib}/commands/ZRANDMEMBER.spec.ts | 0 .../client/lib}/commands/ZRANDMEMBER.ts | 0 .../lib}/commands/ZRANDMEMBER_COUNT.spec.ts | 0 .../client/lib}/commands/ZRANDMEMBER_COUNT.ts | 0 .../ZRANDMEMBER_COUNT_WITHSCORES.spec.ts | 0 .../commands/ZRANDMEMBER_COUNT_WITHSCORES.ts | 0 .../client/lib}/commands/ZRANGE.spec.ts | 0 .../client/lib}/commands/ZRANGE.ts | 0 .../client/lib}/commands/ZRANGEBYLEX.spec.ts | 0 .../client/lib}/commands/ZRANGEBYLEX.ts | 0 .../lib}/commands/ZRANGEBYSCORE.spec.ts | 0 .../client/lib}/commands/ZRANGEBYSCORE.ts | 0 .../commands/ZRANGEBYSCORE_WITHSCORES.spec.ts | 0 .../lib}/commands/ZRANGEBYSCORE_WITHSCORES.ts | 0 .../client/lib}/commands/ZRANGESTORE.spec.ts | 0 .../client/lib}/commands/ZRANGESTORE.ts | 0 .../lib}/commands/ZRANGE_WITHSCORES.spec.ts | 0 .../client/lib}/commands/ZRANGE_WITHSCORES.ts | 0 .../client/lib}/commands/ZRANK.spec.ts | 0 .../client/lib}/commands/ZRANK.ts | 0 .../client/lib}/commands/ZREM.spec.ts | 0 {lib => packages/client/lib}/commands/ZREM.ts | 0 .../lib}/commands/ZREMRANGEBYLEX.spec.ts | 0 .../client/lib}/commands/ZREMRANGEBYLEX.ts | 0 .../lib}/commands/ZREMRANGEBYRANK.spec.ts | 0 .../client/lib}/commands/ZREMRANGEBYRANK.ts | 0 .../lib}/commands/ZREMRANGEBYSCORE.spec.ts | 0 .../client/lib}/commands/ZREMRANGEBYSCORE.ts | 0 .../client/lib}/commands/ZREVRANK.spec.ts | 0 .../client/lib}/commands/ZREVRANK.ts | 0 .../client/lib}/commands/ZSCAN.spec.ts | 0 .../client/lib}/commands/ZSCAN.ts | 0 .../client/lib}/commands/ZSCORE.spec.ts | 0 .../client/lib}/commands/ZSCORE.ts | 0 .../client/lib}/commands/ZUNION.spec.ts | 0 .../client/lib}/commands/ZUNION.ts | 0 .../client/lib}/commands/ZUNIONSTORE.spec.ts | 0 .../client/lib}/commands/ZUNIONSTORE.ts | 0 .../lib}/commands/ZUNION_WITHSCORES.spec.ts | 0 .../client/lib}/commands/ZUNION_WITHSCORES.ts | 0 .../commands/generic-transformers.spec.ts | 0 .../lib}/commands/generic-transformers.ts | 0 .../client/lib}/commands/index.ts | 0 {lib => packages/client/lib}/errors.ts | 0 {lib => packages/client/lib}/lua-script.ts | 0 .../client/lib}/multi-command.spec.ts | 0 {lib => packages/client/lib}/multi-command.ts | 0 .../client/lib/test-utils.ts | 4 +- .../ts-declarations/cluster-key-slot.d.ts | 0 .../lib}/ts-declarations/redis-parser.d.ts | 0 {lib => packages/client/lib}/utils.ts | 0 packages/client/package.json | 49 + packages/client/tsconfig.json | 27 + packages/json/.nycrc.json | 4 + packages/json/lib/commands/ARRAPPEND.spec.ts | 30 + packages/json/lib/commands/ARRAPPEND.ts | 15 + packages/json/lib/commands/ARRINDEX.spec.ts | 37 + packages/json/lib/commands/ARRINDEX.ts | 21 + packages/json/lib/commands/ARRINSERT.spec.ts | 30 + packages/json/lib/commands/ARRINSERT.ts | 15 + packages/json/lib/commands/ARRLEN.spec.ts | 30 + packages/json/lib/commands/ARRLEN.ts | 15 + packages/json/lib/commands/ARRPOP.spec.ts | 37 + packages/json/lib/commands/ARRPOP.ts | 17 + packages/json/lib/commands/ARRTRIM.spec.ts | 21 + packages/json/lib/commands/ARRTRIM.ts | 7 + .../json/lib/commands/DEBUG_MEMORY.spec.ts | 28 + packages/json/lib/commands/DEBUG_MEMORY.ts | 13 + packages/json/lib/commands/DEL.spec.ts | 28 + packages/json/lib/commands/DEL.ts | 13 + packages/json/lib/commands/FORGET.spec.ts | 28 + packages/json/lib/commands/FORGET.ts | 13 + packages/json/lib/commands/GET.spec.ts | 78 ++ packages/json/lib/commands/GET.ts | 41 + packages/json/lib/commands/MGET.spec.ts | 19 + packages/json/lib/commands/MGET.ts | 15 + packages/json/lib/commands/NUMINCRBY.spec.ts | 21 + packages/json/lib/commands/NUMINCRBY.ts | 7 + packages/json/lib/commands/NUMMULTBY.spec.ts | 21 + packages/json/lib/commands/NUMMULTBY.ts | 7 + packages/json/lib/commands/OBJKEYS.spec.ts | 28 + packages/json/lib/commands/OBJKEYS.ts | 13 + packages/json/lib/commands/OBJLEN.spec.ts | 28 + packages/json/lib/commands/OBJLEN.ts | 13 + packages/json/lib/commands/RESP.spec.ts | 28 + packages/json/lib/commands/RESP.ts | 15 + packages/json/lib/commands/SET.spec.ts | 35 + packages/json/lib/commands/SET.ts | 25 + packages/json/lib/commands/STRAPPEND.spec.ts | 30 + packages/json/lib/commands/STRAPPEND.ts | 21 + packages/json/lib/commands/STRLEN.spec.ts | 30 + packages/json/lib/commands/STRLEN.ts | 15 + packages/json/lib/commands/TYPE.spec.ts | 28 + packages/json/lib/commands/TYPE.ts | 13 + packages/json/lib/commands/index.ts | 94 ++ packages/json/lib/index.ts | 1 + packages/json/lib/test-utils.ts | 21 + packages/json/package.json | 24 + packages/json/tsconfig.json | 9 + packages/search/.nycrc.json | 4 + .../search/lib/commands/AGGREGATE.spec.ts | 482 ++++++++++ packages/search/lib/commands/AGGREGATE.ts | 283 ++++++ packages/search/lib/commands/ALIASADD.spec.ts | 11 + packages/search/lib/commands/ALIASADD.ts | 5 + packages/search/lib/commands/ALIASDEL.spec.ts | 11 + packages/search/lib/commands/ALIASDEL.ts | 5 + .../search/lib/commands/ALIASUPDATE.spec.ts | 11 + packages/search/lib/commands/ALIASUPDATE.ts | 5 + .../search/lib/commands/CONFIG_GET.spec.ts | 25 + packages/search/lib/commands/CONFIG_GET.ts | 16 + .../search/lib/commands/CONFIG_SET.spec.ts | 12 + packages/search/lib/commands/CONFIG_SET.ts | 5 + packages/search/lib/commands/CREATE.spec.ts | 347 +++++++ packages/search/lib/commands/CREATE.ts | 222 +++++ packages/search/lib/commands/DICTADD.spec.ts | 28 + packages/search/lib/commands/DICTADD.ts | 8 + packages/search/lib/commands/DICTDEL.spec.ts | 28 + packages/search/lib/commands/DICTDEL.ts | 8 + packages/search/lib/commands/DICTDUMP.spec.ts | 21 + packages/search/lib/commands/DICTDUMP.ts | 5 + .../search/lib/commands/DROPINDEX.spec.ts | 33 + packages/search/lib/commands/DROPINDEX.ts | 15 + packages/search/lib/commands/EXPLAIN.spec.ts | 11 + packages/search/lib/commands/EXPLAIN.ts | 7 + .../search/lib/commands/EXPLAINCLI.spec.ts | 11 + packages/search/lib/commands/EXPLAINCLI.ts | 7 + packages/search/lib/commands/INFO.spec.ts | 65 ++ packages/search/lib/commands/INFO.ts | 171 ++++ packages/search/lib/commands/PROFILE.ts | 26 + packages/search/lib/commands/SEARCH.spec.ts | 243 +++++ packages/search/lib/commands/SEARCH.ts | 202 +++++ .../search/lib/commands/SPELLCHECK.spec.ts | 71 ++ packages/search/lib/commands/SPELLCHECK.ts | 57 ++ packages/search/lib/commands/SUGADD.spec.ts | 35 + packages/search/lib/commands/SUGADD.ts | 20 + packages/search/lib/commands/SUGDEL.spec.ts | 19 + packages/search/lib/commands/SUGDEL.ts | 5 + packages/search/lib/commands/SUGGET.spec.ts | 46 + packages/search/lib/commands/SUGGET.ts | 22 + .../lib/commands/SUGGET_WITHPAYLOADS.spec.ts | 33 + .../lib/commands/SUGGET_WITHPAYLOADS.ts | 29 + .../lib/commands/SUGGET_WITHSCORES.spec.ts | 33 + .../search/lib/commands/SUGGET_WITHSCORES.ts | 29 + .../SUGGET_WITHSCORES_WITHPAYLOADS.spec.ts | 34 + .../SUGGET_WITHSCORES_WITHPAYLOADS.ts | 30 + packages/search/lib/commands/SUGLEN.spec.ts | 19 + packages/search/lib/commands/SUGLEN.ts | 7 + packages/search/lib/commands/SYNDUMP.spec.ts | 23 + packages/search/lib/commands/SYNDUMP.ts | 5 + .../search/lib/commands/SYNUPDATE.spec.ts | 39 + packages/search/lib/commands/SYNUPDATE.ts | 23 + packages/search/lib/commands/TAGVALS.spec.ts | 24 + packages/search/lib/commands/TAGVALS.ts | 5 + packages/search/lib/commands/_LIST.spec.ts | 19 + packages/search/lib/commands/_LIST.ts | 5 + packages/search/lib/commands/index.spec.ts | 46 + packages/search/lib/commands/index.ts | 133 +++ packages/search/lib/index.ts | 1 + packages/search/lib/test-utils.ts | 21 + packages/search/package.json | 24 + packages/search/tsconfig.json | 9 + packages/test-utils/docker/Dockerfile | 9 + packages/test-utils/docker/entrypoint.sh | 7 + .../test-utils/lib}/dockers.ts | 17 +- .../test-utils/lib/index.ts | 50 +- packages/test-utils/package.json | 27 + packages/test-utils/tsconfig.json | 9 + tsconfig.json | 28 +- 689 files changed, 5321 insertions(+), 1712 deletions(-) delete mode 100644 .github/workflows/benchmark.yml delete mode 100644 .npmignore delete mode 100644 .nycrc.json delete mode 100644 benchmark/.gitignore delete mode 100644 benchmark/index.js delete mode 100644 benchmark/package-lock.json delete mode 100644 benchmark/package.json create mode 100644 packages/all-in-one/index.ts create mode 100644 packages/all-in-one/package.json create mode 100644 packages/all-in-one/tsconfig.json rename .eslintrc.json => packages/client/.eslintrc.json (100%) create mode 100644 packages/client/.gitignore create mode 100644 packages/client/.npmignore create mode 100644 packages/client/.nycrc.json rename CHANGELOG.md => packages/client/CHANGELOG.md (100%) create mode 100644 packages/client/LICENSE create mode 100644 packages/client/README.md rename {docs => packages/client/docs}/FAQ.md (100%) rename {docs => packages/client/docs}/client-configuration.md (98%) rename {docs => packages/client/docs}/clustering.md (82%) rename {docs => packages/client/docs}/isolated-execution.md (100%) rename {docs => packages/client/docs}/v3-to-v4.md (100%) rename {examples => packages/client/examples}/README.md (100%) rename {examples => packages/client/examples}/blocking-list-pop.js (100%) rename {examples => packages/client/examples}/command-with-modifiers.js (100%) rename {examples => packages/client/examples}/connect-as-acl-user.js (100%) rename {examples => packages/client/examples}/lua-multi-incr.js (100%) rename {examples => packages/client/examples}/package-lock.json (100%) rename {examples => packages/client/examples}/package.json (64%) rename {examples => packages/client/examples}/set-scan.js (100%) rename index.ts => packages/client/index.ts (100%) rename {lib => packages/client/lib}/client/commands-queue.ts (100%) rename {lib => packages/client/lib}/client/commands.ts (100%) rename {lib => packages/client/lib}/client/index.spec.ts (98%) rename {lib => packages/client/lib}/client/index.ts (100%) rename {lib => packages/client/lib}/client/multi-command.ts (100%) rename {lib => packages/client/lib}/client/socket.spec.ts (100%) rename {lib => packages/client/lib}/client/socket.ts (100%) rename {lib => packages/client/lib}/cluster/cluster-slots.ts (100%) rename {lib => packages/client/lib}/cluster/commands.ts (100%) rename {lib => packages/client/lib}/cluster/index.spec.ts (51%) rename {lib => packages/client/lib}/cluster/index.ts (100%) rename {lib => packages/client/lib}/cluster/multi-command.ts (100%) rename {lib => packages/client/lib}/command-options.ts (100%) rename {lib => packages/client/lib}/commander.spec.ts (100%) rename {lib => packages/client/lib}/commander.ts (100%) rename {lib => packages/client/lib}/commands/ACL_CAT.spec.ts (100%) rename {lib => packages/client/lib}/commands/ACL_CAT.ts (100%) rename {lib => packages/client/lib}/commands/ACL_DELUSER.spec.ts (100%) rename {lib => packages/client/lib}/commands/ACL_DELUSER.ts (100%) rename {lib => packages/client/lib}/commands/ACL_GENPASS.spec.ts (100%) rename {lib => packages/client/lib}/commands/ACL_GENPASS.ts (100%) rename {lib => packages/client/lib}/commands/ACL_GETUSER.spec.ts (100%) rename {lib => packages/client/lib}/commands/ACL_GETUSER.ts (100%) rename {lib => packages/client/lib}/commands/ACL_LIST.spec.ts (100%) rename {lib => packages/client/lib}/commands/ACL_LIST.ts (100%) rename {lib => packages/client/lib}/commands/ACL_LOAD.spec.ts (100%) rename {lib => packages/client/lib}/commands/ACL_LOAD.ts (100%) rename {lib => packages/client/lib}/commands/ACL_LOG.spec.ts (100%) rename {lib => packages/client/lib}/commands/ACL_LOG.ts (100%) rename {lib => packages/client/lib}/commands/ACL_LOG_RESET.spec.ts (100%) rename {lib => packages/client/lib}/commands/ACL_LOG_RESET.ts (100%) rename {lib => packages/client/lib}/commands/ACL_SAVE.spec.ts (100%) rename {lib => packages/client/lib}/commands/ACL_SAVE.ts (100%) rename {lib => packages/client/lib}/commands/ACL_SETUSER.spec.ts (100%) rename {lib => packages/client/lib}/commands/ACL_SETUSER.ts (100%) rename {lib => packages/client/lib}/commands/ACL_USERS.spec.ts (100%) rename {lib => packages/client/lib}/commands/ACL_USERS.ts (100%) rename {lib => packages/client/lib}/commands/ACL_WHOAMI.spec.ts (100%) rename {lib => packages/client/lib}/commands/ACL_WHOAMI.ts (100%) rename {lib => packages/client/lib}/commands/APPEND.spec.ts (100%) rename {lib => packages/client/lib}/commands/APPEND.ts (100%) rename {lib => packages/client/lib}/commands/ASKING.spec.ts (100%) rename {lib => packages/client/lib}/commands/ASKING.ts (100%) rename {lib => packages/client/lib}/commands/AUTH.spec.ts (100%) rename {lib => packages/client/lib}/commands/AUTH.ts (100%) rename {lib => packages/client/lib}/commands/BGREWRITEAOF.spec.ts (100%) rename {lib => packages/client/lib}/commands/BGREWRITEAOF.ts (100%) rename {lib => packages/client/lib}/commands/BGSAVE.spec.ts (100%) rename {lib => packages/client/lib}/commands/BGSAVE.ts (100%) rename {lib => packages/client/lib}/commands/BITCOUNT.spec.ts (100%) rename {lib => packages/client/lib}/commands/BITCOUNT.ts (100%) rename {lib => packages/client/lib}/commands/BITFIELD.spec.ts (100%) rename {lib => packages/client/lib}/commands/BITFIELD.ts (100%) rename {lib => packages/client/lib}/commands/BITOP.spec.ts (100%) rename {lib => packages/client/lib}/commands/BITOP.ts (100%) rename {lib => packages/client/lib}/commands/BITPOS.spec.ts (100%) rename {lib => packages/client/lib}/commands/BITPOS.ts (100%) rename {lib => packages/client/lib}/commands/BLMOVE.spec.ts (100%) rename {lib => packages/client/lib}/commands/BLMOVE.ts (100%) rename {lib => packages/client/lib}/commands/BLPOP.spec.ts (100%) rename {lib => packages/client/lib}/commands/BLPOP.ts (100%) rename {lib => packages/client/lib}/commands/BRPOP.spec.ts (100%) rename {lib => packages/client/lib}/commands/BRPOP.ts (100%) rename {lib => packages/client/lib}/commands/BRPOPLPUSH.spec.ts (100%) rename {lib => packages/client/lib}/commands/BRPOPLPUSH.ts (100%) rename {lib => packages/client/lib}/commands/BZPOPMAX.spec.ts (100%) rename {lib => packages/client/lib}/commands/BZPOPMAX.ts (100%) rename {lib => packages/client/lib}/commands/BZPOPMIN.spec.ts (100%) rename {lib => packages/client/lib}/commands/BZPOPMIN.ts (100%) rename {lib => packages/client/lib}/commands/CLIENT_ID.spec.ts (100%) rename {lib => packages/client/lib}/commands/CLIENT_ID.ts (100%) rename {lib => packages/client/lib}/commands/CLIENT_INFO.spec.ts (100%) rename {lib => packages/client/lib}/commands/CLIENT_INFO.ts (100%) rename {lib => packages/client/lib}/commands/CLUSTER_ADDSLOTS.spec.ts (100%) rename {lib => packages/client/lib}/commands/CLUSTER_ADDSLOTS.ts (100%) rename {lib => packages/client/lib}/commands/CLUSTER_FLUSHSLOTS.spec.ts (100%) rename {lib => packages/client/lib}/commands/CLUSTER_FLUSHSLOTS.ts (100%) rename {lib => packages/client/lib}/commands/CLUSTER_GETKEYSINSLOT.spec.ts (100%) rename {lib => packages/client/lib}/commands/CLUSTER_GETKEYSINSLOT.ts (100%) rename {lib => packages/client/lib}/commands/CLUSTER_INFO.spec.ts (100%) rename {lib => packages/client/lib}/commands/CLUSTER_INFO.ts (100%) rename {lib => packages/client/lib}/commands/CLUSTER_MEET.spec.ts (100%) rename {lib => packages/client/lib}/commands/CLUSTER_MEET.ts (100%) rename {lib => packages/client/lib}/commands/CLUSTER_NODES.spec.ts (100%) rename {lib => packages/client/lib}/commands/CLUSTER_NODES.ts (100%) rename {lib => packages/client/lib}/commands/CLUSTER_RESET.spec.ts (100%) rename {lib => packages/client/lib}/commands/CLUSTER_RESET.ts (100%) rename {lib => packages/client/lib}/commands/CLUSTER_SETSLOT.spec.ts (100%) rename {lib => packages/client/lib}/commands/CLUSTER_SETSLOT.ts (100%) rename {lib => packages/client/lib}/commands/CLUSTER_SLOTS.spec.ts (100%) rename {lib => packages/client/lib}/commands/CLUSTER_SLOTS.ts (100%) rename {lib => packages/client/lib}/commands/COMMAND.spec.ts (65%) rename {lib => packages/client/lib}/commands/COMMAND.ts (100%) rename {lib => packages/client/lib}/commands/COMMAND_COUNT.spec.ts (100%) rename {lib => packages/client/lib}/commands/COMMAND_COUNT.ts (100%) rename {lib => packages/client/lib}/commands/COMMAND_GETKEYS.spec.ts (100%) rename {lib => packages/client/lib}/commands/COMMAND_GETKEYS.ts (100%) rename {lib => packages/client/lib}/commands/COMMAND_INFO.spec.ts (100%) rename {lib => packages/client/lib}/commands/COMMAND_INFO.ts (100%) rename {lib => packages/client/lib}/commands/CONFIG_GET.spec.ts (100%) rename {lib => packages/client/lib}/commands/CONFIG_GET.ts (100%) rename {lib => packages/client/lib}/commands/CONFIG_RESETSTAT.spec.ts (100%) rename {lib => packages/client/lib}/commands/CONFIG_RESETSTAT.ts (100%) rename {lib => packages/client/lib}/commands/CONFIG_REWRITE.spec.ts (100%) rename {lib => packages/client/lib}/commands/CONFIG_REWRITE.ts (100%) rename {lib => packages/client/lib}/commands/CONFIG_SET.spec.ts (100%) rename {lib => packages/client/lib}/commands/CONFIG_SET.ts (100%) rename {lib => packages/client/lib}/commands/COPY.spec.ts (100%) rename {lib => packages/client/lib}/commands/COPY.ts (100%) rename {lib => packages/client/lib}/commands/DBSIZE.spec.ts (100%) rename {lib => packages/client/lib}/commands/DBSIZE.ts (100%) rename {lib => packages/client/lib}/commands/DECR.spec.ts (100%) rename {lib => packages/client/lib}/commands/DECR.ts (100%) rename {lib => packages/client/lib}/commands/DECRBY.spec.ts (100%) rename {lib => packages/client/lib}/commands/DECRBY.ts (100%) rename {lib => packages/client/lib}/commands/DEL.spec.ts (100%) rename {lib => packages/client/lib}/commands/DEL.ts (100%) rename {lib => packages/client/lib}/commands/DISCARD.spec.ts (100%) rename {lib => packages/client/lib}/commands/DISCARD.ts (100%) rename {lib => packages/client/lib}/commands/DUMP.spec.ts (100%) rename {lib => packages/client/lib}/commands/DUMP.ts (100%) rename {lib => packages/client/lib}/commands/ECHO.spec.ts (100%) rename {lib => packages/client/lib}/commands/ECHO.ts (100%) rename {lib => packages/client/lib}/commands/EVAL.spec.ts (100%) rename {lib => packages/client/lib}/commands/EVAL.ts (100%) rename {lib => packages/client/lib}/commands/EVALSHA.spec.ts (100%) rename {lib => packages/client/lib}/commands/EVALSHA.ts (100%) rename {lib => packages/client/lib}/commands/EXISTS.spec.ts (100%) rename {lib => packages/client/lib}/commands/EXISTS.ts (100%) rename {lib => packages/client/lib}/commands/EXPIRE.spec.ts (100%) rename {lib => packages/client/lib}/commands/EXPIRE.ts (100%) rename {lib => packages/client/lib}/commands/EXPIREAT.spec.ts (100%) rename {lib => packages/client/lib}/commands/EXPIREAT.ts (100%) rename {lib => packages/client/lib}/commands/FAILOVER.spec.ts (100%) rename {lib => packages/client/lib}/commands/FAILOVER.ts (100%) rename {lib => packages/client/lib}/commands/FLUSHALL.spec.ts (100%) rename {lib => packages/client/lib}/commands/FLUSHALL.ts (100%) rename {lib => packages/client/lib}/commands/FLUSHDB.spec.ts (100%) rename {lib => packages/client/lib}/commands/FLUSHDB.ts (100%) rename {lib => packages/client/lib}/commands/GEOADD.spec.ts (100%) rename {lib => packages/client/lib}/commands/GEOADD.ts (100%) rename {lib => packages/client/lib}/commands/GEODIST.spec.ts (100%) rename {lib => packages/client/lib}/commands/GEODIST.ts (100%) rename {lib => packages/client/lib}/commands/GEOHASH.spec.ts (100%) rename {lib => packages/client/lib}/commands/GEOHASH.ts (100%) rename {lib => packages/client/lib}/commands/GEOPOS.spec.ts (100%) rename {lib => packages/client/lib}/commands/GEOPOS.ts (100%) rename {lib => packages/client/lib}/commands/GEOSEARCH.spec.ts (100%) rename {lib => packages/client/lib}/commands/GEOSEARCH.ts (100%) rename {lib => packages/client/lib}/commands/GEOSEARCHSTORE.spec.ts (100%) rename {lib => packages/client/lib}/commands/GEOSEARCHSTORE.ts (100%) rename {lib => packages/client/lib}/commands/GEOSEARCH_WITH.spec.ts (100%) rename {lib => packages/client/lib}/commands/GEOSEARCH_WITH.ts (100%) rename {lib => packages/client/lib}/commands/GET.spec.ts (100%) rename {lib => packages/client/lib}/commands/GET.ts (100%) rename {lib => packages/client/lib}/commands/GETBIT.spec.ts (100%) rename {lib => packages/client/lib}/commands/GETBIT.ts (100%) rename {lib => packages/client/lib}/commands/GETDEL.spec.ts (100%) rename {lib => packages/client/lib}/commands/GETDEL.ts (100%) rename {lib => packages/client/lib}/commands/GETEX.spec.ts (100%) rename {lib => packages/client/lib}/commands/GETEX.ts (100%) rename {lib => packages/client/lib}/commands/GETRANGE.spec.ts (100%) rename {lib => packages/client/lib}/commands/GETRANGE.ts (100%) rename {lib => packages/client/lib}/commands/GETSET.spec.ts (100%) rename {lib => packages/client/lib}/commands/GETSET.ts (100%) rename {lib => packages/client/lib}/commands/GET_BUFFER.spec.ts (100%) rename {lib => packages/client/lib}/commands/GET_BUFFER.ts (100%) rename {lib => packages/client/lib}/commands/HDEL.spec.ts (100%) rename {lib => packages/client/lib}/commands/HDEL.ts (100%) rename {lib => packages/client/lib}/commands/HELLO.spec.ts (100%) rename {lib => packages/client/lib}/commands/HELLO.ts (100%) rename {lib => packages/client/lib}/commands/HEXISTS.spec.ts (100%) rename {lib => packages/client/lib}/commands/HEXISTS.ts (100%) rename {lib => packages/client/lib}/commands/HGET.spec.ts (100%) rename {lib => packages/client/lib}/commands/HGET.ts (100%) rename {lib => packages/client/lib}/commands/HGETALL.spec.ts (100%) rename {lib => packages/client/lib}/commands/HGETALL.ts (100%) rename {lib => packages/client/lib}/commands/HINCRBY.spec.ts (100%) rename {lib => packages/client/lib}/commands/HINCRBY.ts (100%) rename {lib => packages/client/lib}/commands/HINCRBYFLOAT.spec.ts (100%) rename {lib => packages/client/lib}/commands/HINCRBYFLOAT.ts (100%) rename {lib => packages/client/lib}/commands/HKEYS.spec.ts (100%) rename {lib => packages/client/lib}/commands/HKEYS.ts (100%) rename {lib => packages/client/lib}/commands/HLEN.spec.ts (100%) rename {lib => packages/client/lib}/commands/HLEN.ts (100%) rename {lib => packages/client/lib}/commands/HMGET.spec.ts (100%) rename {lib => packages/client/lib}/commands/HMGET.ts (100%) rename {lib => packages/client/lib}/commands/HRANDFIELD.spec.ts (100%) rename {lib => packages/client/lib}/commands/HRANDFIELD.ts (100%) rename {lib => packages/client/lib}/commands/HRANDFIELD_COUNT.spec.ts (100%) rename {lib => packages/client/lib}/commands/HRANDFIELD_COUNT.ts (100%) rename {lib => packages/client/lib}/commands/HRANDFIELD_COUNT_WITHVALUES.spec.ts (100%) rename {lib => packages/client/lib}/commands/HRANDFIELD_COUNT_WITHVALUES.ts (100%) rename {lib => packages/client/lib}/commands/HSCAN.spec.ts (100%) rename {lib => packages/client/lib}/commands/HSCAN.ts (100%) rename {lib => packages/client/lib}/commands/HSET.spec.ts (100%) rename {lib => packages/client/lib}/commands/HSET.ts (100%) rename {lib => packages/client/lib}/commands/HSETNX.spec.ts (100%) rename {lib => packages/client/lib}/commands/HSETNX.ts (100%) rename {lib => packages/client/lib}/commands/HSTRLEN.spec.ts (100%) rename {lib => packages/client/lib}/commands/HSTRLEN.ts (100%) rename {lib => packages/client/lib}/commands/HVALS.spec.ts (100%) rename {lib => packages/client/lib}/commands/HVALS.ts (100%) rename {lib => packages/client/lib}/commands/INCR.spec.ts (100%) rename {lib => packages/client/lib}/commands/INCR.ts (100%) rename {lib => packages/client/lib}/commands/INCRBY.spec.ts (100%) rename {lib => packages/client/lib}/commands/INCRBY.ts (100%) rename {lib => packages/client/lib}/commands/INCRBYFLOAT.spec.ts (100%) rename {lib => packages/client/lib}/commands/INCRBYFLOAT.ts (100%) rename {lib => packages/client/lib}/commands/INFO.spec.ts (100%) rename {lib => packages/client/lib}/commands/INFO.ts (100%) rename {lib => packages/client/lib}/commands/KEYS.spec.ts (100%) rename {lib => packages/client/lib}/commands/KEYS.ts (100%) rename {lib => packages/client/lib}/commands/LASTSAVE.spec.ts (100%) rename {lib => packages/client/lib}/commands/LASTSAVE.ts (100%) rename {lib => packages/client/lib}/commands/LINDEX.spec.ts (100%) rename {lib => packages/client/lib}/commands/LINDEX.ts (100%) rename {lib => packages/client/lib}/commands/LINSERT.spec.ts (100%) rename {lib => packages/client/lib}/commands/LINSERT.ts (100%) rename {lib => packages/client/lib}/commands/LLEN.spec.ts (100%) rename {lib => packages/client/lib}/commands/LLEN.ts (100%) rename {lib => packages/client/lib}/commands/LMOVE.spec.ts (100%) rename {lib => packages/client/lib}/commands/LMOVE.ts (100%) rename {lib => packages/client/lib}/commands/LOLWUT.spec.ts (100%) rename {lib => packages/client/lib}/commands/LOLWUT.ts (100%) rename {lib => packages/client/lib}/commands/LPOP.spec.ts (100%) rename {lib => packages/client/lib}/commands/LPOP.ts (100%) rename {lib => packages/client/lib}/commands/LPOP_COUNT.spec.ts (100%) rename {lib => packages/client/lib}/commands/LPOP_COUNT.ts (100%) rename {lib => packages/client/lib}/commands/LPOS.spec.ts (100%) rename {lib => packages/client/lib}/commands/LPOS.ts (100%) rename {lib => packages/client/lib}/commands/LPOS_COUNT.spec.ts (100%) rename {lib => packages/client/lib}/commands/LPOS_COUNT.ts (100%) rename {lib => packages/client/lib}/commands/LPUSH.spec.ts (100%) rename {lib => packages/client/lib}/commands/LPUSH.ts (100%) rename {lib => packages/client/lib}/commands/LPUSHX.spec.ts (100%) rename {lib => packages/client/lib}/commands/LPUSHX.ts (100%) rename {lib => packages/client/lib}/commands/LRANGE.spec.ts (100%) rename {lib => packages/client/lib}/commands/LRANGE.ts (100%) rename {lib => packages/client/lib}/commands/LREM.spec.ts (100%) rename {lib => packages/client/lib}/commands/LREM.ts (100%) rename {lib => packages/client/lib}/commands/LSET.spec.ts (100%) rename {lib => packages/client/lib}/commands/LSET.ts (100%) rename {lib => packages/client/lib}/commands/LTRIM.spec.ts (100%) rename {lib => packages/client/lib}/commands/LTRIM.ts (100%) rename {lib => packages/client/lib}/commands/MEMORY_DOCTOR.spec.ts (100%) rename {lib => packages/client/lib}/commands/MEMORY_DOCTOR.ts (100%) rename {lib => packages/client/lib}/commands/MEMORY_MALLOC-STATS.spec.ts (100%) rename {lib => packages/client/lib}/commands/MEMORY_MALLOC-STATS.ts (100%) rename {lib => packages/client/lib}/commands/MEMORY_PURGE.spec.ts (100%) rename {lib => packages/client/lib}/commands/MEMORY_PURGE.ts (100%) rename {lib => packages/client/lib}/commands/MEMORY_STATS.spec.ts (100%) rename {lib => packages/client/lib}/commands/MEMORY_STATS.ts (100%) rename {lib => packages/client/lib}/commands/MEMORY_USAGE.spec.ts (100%) rename {lib => packages/client/lib}/commands/MEMORY_USAGE.ts (100%) rename {lib => packages/client/lib}/commands/MGET.spec.ts (100%) rename {lib => packages/client/lib}/commands/MGET.ts (100%) rename {lib => packages/client/lib}/commands/MIGRATE.spec.ts (100%) rename {lib => packages/client/lib}/commands/MIGRATE.ts (100%) rename {lib => packages/client/lib}/commands/MODULE_LIST.spec.ts (100%) rename {lib => packages/client/lib}/commands/MODULE_LIST.ts (100%) rename {lib => packages/client/lib}/commands/MODULE_LOAD.spec.ts (100%) rename {lib => packages/client/lib}/commands/MODULE_LOAD.ts (100%) rename {lib => packages/client/lib}/commands/MODULE_UNLOAD.spec.ts (100%) rename {lib => packages/client/lib}/commands/MODULE_UNLOAD.ts (100%) rename {lib => packages/client/lib}/commands/MOVE.spec.ts (100%) rename {lib => packages/client/lib}/commands/MOVE.ts (100%) rename {lib => packages/client/lib}/commands/MSET.spec.ts (100%) rename {lib => packages/client/lib}/commands/MSET.ts (100%) rename {lib => packages/client/lib}/commands/MSETNX.spec.ts (100%) rename {lib => packages/client/lib}/commands/MSETNX.ts (100%) rename {lib => packages/client/lib}/commands/PERSIST.spec.ts (100%) rename {lib => packages/client/lib}/commands/PERSIST.ts (100%) rename {lib => packages/client/lib}/commands/PEXPIRE.spec.ts (100%) rename {lib => packages/client/lib}/commands/PEXPIRE.ts (100%) rename {lib => packages/client/lib}/commands/PEXPIREAT.spec.ts (100%) rename {lib => packages/client/lib}/commands/PEXPIREAT.ts (100%) rename {lib => packages/client/lib}/commands/PFADD.spec.ts (100%) rename {lib => packages/client/lib}/commands/PFADD.ts (100%) rename {lib => packages/client/lib}/commands/PFCOUNT.spec.ts (100%) rename {lib => packages/client/lib}/commands/PFCOUNT.ts (100%) rename {lib => packages/client/lib}/commands/PFMERGE.spec.ts (100%) rename {lib => packages/client/lib}/commands/PFMERGE.ts (100%) rename {lib => packages/client/lib}/commands/PING.spec.ts (100%) rename {lib => packages/client/lib}/commands/PING.ts (100%) rename {lib => packages/client/lib}/commands/PSETEX.spec.ts (100%) rename {lib => packages/client/lib}/commands/PSETEX.ts (100%) rename {lib => packages/client/lib}/commands/PTTL.spec.ts (100%) rename {lib => packages/client/lib}/commands/PTTL.ts (100%) rename {lib => packages/client/lib}/commands/PUBLISH.spec.ts (100%) rename {lib => packages/client/lib}/commands/PUBLISH.ts (100%) rename {lib => packages/client/lib}/commands/PUBSUB_CHANNELS.spec.ts (100%) rename {lib => packages/client/lib}/commands/PUBSUB_CHANNELS.ts (100%) rename {lib => packages/client/lib}/commands/PUBSUB_NUMPAT.spec.ts (100%) rename {lib => packages/client/lib}/commands/PUBSUB_NUMPAT.ts (100%) rename {lib => packages/client/lib}/commands/PUBSUB_NUMSUB.spec.ts (100%) rename {lib => packages/client/lib}/commands/PUBSUB_NUMSUB.ts (100%) rename {lib => packages/client/lib}/commands/RANDOMKEY.spec.ts (100%) rename {lib => packages/client/lib}/commands/RANDOMKEY.ts (100%) rename {lib => packages/client/lib}/commands/READONLY.spec.ts (100%) rename {lib => packages/client/lib}/commands/READONLY.ts (100%) rename {lib => packages/client/lib}/commands/READWRITE.spec.ts (100%) rename {lib => packages/client/lib}/commands/READWRITE.ts (100%) rename {lib => packages/client/lib}/commands/RENAME.spec.ts (100%) rename {lib => packages/client/lib}/commands/RENAME.ts (100%) rename {lib => packages/client/lib}/commands/RENAMENX.spec.ts (100%) rename {lib => packages/client/lib}/commands/RENAMENX.ts (100%) rename {lib => packages/client/lib}/commands/REPLICAOF.spec.ts (100%) rename {lib => packages/client/lib}/commands/REPLICAOF.ts (100%) rename {lib => packages/client/lib}/commands/RESTORE-ASKING.spec.ts (100%) rename {lib => packages/client/lib}/commands/RESTORE-ASKING.ts (100%) rename {lib => packages/client/lib}/commands/ROLE.spec.ts (100%) rename {lib => packages/client/lib}/commands/ROLE.ts (100%) rename {lib => packages/client/lib}/commands/RPOP.spec.ts (100%) rename {lib => packages/client/lib}/commands/RPOP.ts (100%) rename {lib => packages/client/lib}/commands/RPOPLPUSH.spec.ts (100%) rename {lib => packages/client/lib}/commands/RPOPLPUSH.ts (100%) rename {lib => packages/client/lib}/commands/RPOP_COUNT.spec.ts (100%) rename {lib => packages/client/lib}/commands/RPOP_COUNT.ts (100%) rename {lib => packages/client/lib}/commands/RPUSH.spec.ts (100%) rename {lib => packages/client/lib}/commands/RPUSH.ts (100%) rename {lib => packages/client/lib}/commands/RPUSHX.spec.ts (100%) rename {lib => packages/client/lib}/commands/RPUSHX.ts (100%) rename {lib => packages/client/lib}/commands/SADD.spec.ts (100%) rename {lib => packages/client/lib}/commands/SADD.ts (100%) rename {lib => packages/client/lib}/commands/SAVE.spec.ts (100%) rename {lib => packages/client/lib}/commands/SAVE.ts (100%) rename {lib => packages/client/lib}/commands/SCAN.spec.ts (100%) rename {lib => packages/client/lib}/commands/SCAN.ts (100%) rename {lib => packages/client/lib}/commands/SCARD.spec.ts (100%) rename {lib => packages/client/lib}/commands/SCARD.ts (100%) rename {lib => packages/client/lib}/commands/SCRIPT_DEBUG.spec.ts (100%) rename {lib => packages/client/lib}/commands/SCRIPT_DEBUG.ts (100%) rename {lib => packages/client/lib}/commands/SCRIPT_EXISTS.spec.ts (100%) rename {lib => packages/client/lib}/commands/SCRIPT_EXISTS.ts (100%) rename {lib => packages/client/lib}/commands/SCRIPT_FLUSH.spec.ts (100%) rename {lib => packages/client/lib}/commands/SCRIPT_FLUSH.ts (100%) rename {lib => packages/client/lib}/commands/SCRIPT_KILL.spec.ts (100%) rename {lib => packages/client/lib}/commands/SCRIPT_KILL.ts (100%) rename {lib => packages/client/lib}/commands/SCRIPT_LOAD.spec.ts (100%) rename {lib => packages/client/lib}/commands/SCRIPT_LOAD.ts (100%) rename {lib => packages/client/lib}/commands/SDIFF.spec.ts (100%) rename {lib => packages/client/lib}/commands/SDIFF.ts (100%) rename {lib => packages/client/lib}/commands/SDIFFSTORE.spec.ts (100%) rename {lib => packages/client/lib}/commands/SDIFFSTORE.ts (100%) rename {lib => packages/client/lib}/commands/SET.spec.ts (100%) rename {lib => packages/client/lib}/commands/SET.ts (100%) rename {lib => packages/client/lib}/commands/SETBIT.spec.ts (100%) rename {lib => packages/client/lib}/commands/SETBIT.ts (100%) rename {lib => packages/client/lib}/commands/SETEX.spec.ts (100%) rename {lib => packages/client/lib}/commands/SETEX.ts (100%) rename {lib => packages/client/lib}/commands/SETNX .spec.ts (100%) rename {lib => packages/client/lib}/commands/SETNX.ts (100%) rename {lib => packages/client/lib}/commands/SETRANGE.spec.ts (100%) rename {lib => packages/client/lib}/commands/SETRANGE.ts (100%) rename {lib => packages/client/lib}/commands/SHUTDOWN.spec.ts (100%) rename {lib => packages/client/lib}/commands/SHUTDOWN.ts (100%) rename {lib => packages/client/lib}/commands/SINTER.spec.ts (100%) rename {lib => packages/client/lib}/commands/SINTER.ts (100%) rename {lib => packages/client/lib}/commands/SINTERSTORE.spec.ts (100%) rename {lib => packages/client/lib}/commands/SINTERSTORE.ts (100%) rename {lib => packages/client/lib}/commands/SISMEMBER.spec.ts (100%) rename {lib => packages/client/lib}/commands/SISMEMBER.ts (100%) rename {lib => packages/client/lib}/commands/SMEMBERS.spec.ts (100%) rename {lib => packages/client/lib}/commands/SMEMBERS.ts (100%) rename {lib => packages/client/lib}/commands/SMISMEMBER.spec.ts (100%) rename {lib => packages/client/lib}/commands/SMISMEMBER.ts (100%) rename {lib => packages/client/lib}/commands/SMOVE.spec.ts (100%) rename {lib => packages/client/lib}/commands/SMOVE.ts (100%) rename {lib => packages/client/lib}/commands/SORT.spec.ts (100%) rename {lib => packages/client/lib}/commands/SORT.ts (100%) rename {lib => packages/client/lib}/commands/SPOP.spec.ts (100%) rename {lib => packages/client/lib}/commands/SPOP.ts (100%) rename {lib => packages/client/lib}/commands/SRANDMEMBER.spec.ts (100%) rename {lib => packages/client/lib}/commands/SRANDMEMBER.ts (100%) rename {lib => packages/client/lib}/commands/SRANDMEMBER_COUNT.spec.ts (100%) rename {lib => packages/client/lib}/commands/SRANDMEMBER_COUNT.ts (100%) rename {lib => packages/client/lib}/commands/SREM.spec.ts (100%) rename {lib => packages/client/lib}/commands/SREM.ts (100%) rename {lib => packages/client/lib}/commands/SSCAN.spec.ts (100%) rename {lib => packages/client/lib}/commands/SSCAN.ts (100%) rename {lib => packages/client/lib}/commands/STRLEN.spec.ts (100%) rename {lib => packages/client/lib}/commands/STRLEN.ts (100%) rename {lib => packages/client/lib}/commands/SUNION.spec.ts (100%) rename {lib => packages/client/lib}/commands/SUNION.ts (100%) rename {lib => packages/client/lib}/commands/SUNIONSTORE.spec.ts (100%) rename {lib => packages/client/lib}/commands/SUNIONSTORE.ts (100%) rename {lib => packages/client/lib}/commands/SWAPDB.spec.ts (100%) rename {lib => packages/client/lib}/commands/SWAPDB.ts (100%) rename {lib => packages/client/lib}/commands/TIME.spec.ts (55%) rename {lib => packages/client/lib}/commands/TIME.ts (100%) rename {lib => packages/client/lib}/commands/TOUCH.spec.ts (100%) rename {lib => packages/client/lib}/commands/TOUCH.ts (100%) rename {lib => packages/client/lib}/commands/TTL.spec.ts (100%) rename {lib => packages/client/lib}/commands/TTL.ts (100%) rename {lib => packages/client/lib}/commands/TYPE.spec.ts (100%) rename {lib => packages/client/lib}/commands/TYPE.ts (100%) rename {lib => packages/client/lib}/commands/UNLINK.spec.ts (100%) rename {lib => packages/client/lib}/commands/UNLINK.ts (100%) rename {lib => packages/client/lib}/commands/UNWATCH.spec.ts (100%) rename {lib => packages/client/lib}/commands/UNWATCH.ts (100%) rename {lib => packages/client/lib}/commands/WAIT.spec.ts (100%) rename {lib => packages/client/lib}/commands/WAIT.ts (100%) rename {lib => packages/client/lib}/commands/WATCH.spec.ts (100%) rename {lib => packages/client/lib}/commands/WATCH.ts (100%) rename {lib => packages/client/lib}/commands/XACK.spec.ts (100%) rename {lib => packages/client/lib}/commands/XACK.ts (100%) rename {lib => packages/client/lib}/commands/XADD.spec.ts (100%) rename {lib => packages/client/lib}/commands/XADD.ts (100%) rename {lib => packages/client/lib}/commands/XAUTOCLAIM.spec.ts (100%) rename {lib => packages/client/lib}/commands/XAUTOCLAIM.ts (100%) rename {lib => packages/client/lib}/commands/XAUTOCLAIM_JUSTID.spec.ts (100%) rename {lib => packages/client/lib}/commands/XAUTOCLAIM_JUSTID.ts (100%) rename {lib => packages/client/lib}/commands/XCLAIM.spec.ts (100%) rename {lib => packages/client/lib}/commands/XCLAIM.ts (100%) rename {lib => packages/client/lib}/commands/XCLAIM_JUSTID.spec.ts (100%) rename {lib => packages/client/lib}/commands/XCLAIM_JUSTID.ts (100%) rename {lib => packages/client/lib}/commands/XDEL.spec.ts (100%) rename {lib => packages/client/lib}/commands/XDEL.ts (100%) rename {lib => packages/client/lib}/commands/XGROUP_CREATE.spec.ts (100%) rename {lib => packages/client/lib}/commands/XGROUP_CREATE.ts (100%) rename {lib => packages/client/lib}/commands/XGROUP_CREATECONSUMER.spec.ts (100%) rename {lib => packages/client/lib}/commands/XGROUP_CREATECONSUMER.ts (100%) rename {lib => packages/client/lib}/commands/XGROUP_DELCONSUMER.spec.ts (100%) rename {lib => packages/client/lib}/commands/XGROUP_DELCONSUMER.ts (100%) rename {lib => packages/client/lib}/commands/XGROUP_DESTROY.spec.ts (100%) rename {lib => packages/client/lib}/commands/XGROUP_DESTROY.ts (100%) rename {lib => packages/client/lib}/commands/XGROUP_SETID.spec.ts (100%) rename {lib => packages/client/lib}/commands/XGROUP_SETID.ts (100%) rename {lib => packages/client/lib}/commands/XINFO_CONSUMERS.spec.ts (100%) rename {lib => packages/client/lib}/commands/XINFO_CONSUMERS.ts (100%) rename {lib => packages/client/lib}/commands/XINFO_GROUPS.spec.ts (100%) rename {lib => packages/client/lib}/commands/XINFO_GROUPS.ts (100%) rename {lib => packages/client/lib}/commands/XINFO_STREAM.spec.ts (100%) rename {lib => packages/client/lib}/commands/XINFO_STREAM.ts (100%) rename {lib => packages/client/lib}/commands/XLEN.spec.ts (100%) rename {lib => packages/client/lib}/commands/XLEN.ts (100%) rename {lib => packages/client/lib}/commands/XPENDING.spec.ts (100%) rename {lib => packages/client/lib}/commands/XPENDING.ts (100%) rename {lib => packages/client/lib}/commands/XPENDING_RANGE.spec.ts (100%) rename {lib => packages/client/lib}/commands/XPENDING_RANGE.ts (100%) rename {lib => packages/client/lib}/commands/XRANGE.spec.ts (100%) rename {lib => packages/client/lib}/commands/XRANGE.ts (100%) rename {lib => packages/client/lib}/commands/XREAD.spec.ts (100%) rename {lib => packages/client/lib}/commands/XREAD.ts (100%) rename {lib => packages/client/lib}/commands/XREADGROUP.spec.ts (100%) rename {lib => packages/client/lib}/commands/XREADGROUP.ts (100%) rename {lib => packages/client/lib}/commands/XREVRANGE.spec.ts (100%) rename {lib => packages/client/lib}/commands/XREVRANGE.ts (100%) rename {lib => packages/client/lib}/commands/XTRIM.spec.ts (100%) rename {lib => packages/client/lib}/commands/XTRIM.ts (100%) rename {lib => packages/client/lib}/commands/ZADD.spec.ts (100%) rename {lib => packages/client/lib}/commands/ZADD.ts (100%) rename {lib => packages/client/lib}/commands/ZCARD.spec.ts (100%) rename {lib => packages/client/lib}/commands/ZCARD.ts (100%) rename {lib => packages/client/lib}/commands/ZCOUNT.spec.ts (100%) rename {lib => packages/client/lib}/commands/ZCOUNT.ts (100%) rename {lib => packages/client/lib}/commands/ZDIFF.spec.ts (100%) rename {lib => packages/client/lib}/commands/ZDIFF.ts (100%) rename {lib => packages/client/lib}/commands/ZDIFFSTORE.spec.ts (100%) rename {lib => packages/client/lib}/commands/ZDIFFSTORE.ts (100%) rename {lib => packages/client/lib}/commands/ZDIFF_WITHSCORES.spec.ts (100%) rename {lib => packages/client/lib}/commands/ZDIFF_WITHSCORES.ts (100%) rename {lib => packages/client/lib}/commands/ZINCRBY.spec.ts (100%) rename {lib => packages/client/lib}/commands/ZINCRBY.ts (100%) rename {lib => packages/client/lib}/commands/ZINTER.spec.ts (100%) rename {lib => packages/client/lib}/commands/ZINTER.ts (100%) rename {lib => packages/client/lib}/commands/ZINTERSTORE.spec.ts (100%) rename {lib => packages/client/lib}/commands/ZINTERSTORE.ts (100%) rename {lib => packages/client/lib}/commands/ZINTER_WITHSCORES.spec.ts (100%) rename {lib => packages/client/lib}/commands/ZINTER_WITHSCORES.ts (100%) rename {lib => packages/client/lib}/commands/ZLEXCOUNT.spec.ts (100%) rename {lib => packages/client/lib}/commands/ZLEXCOUNT.ts (100%) rename {lib => packages/client/lib}/commands/ZMSCORE.spec.ts (100%) rename {lib => packages/client/lib}/commands/ZMSCORE.ts (100%) rename {lib => packages/client/lib}/commands/ZPOPMAX.spec.ts (100%) rename {lib => packages/client/lib}/commands/ZPOPMAX.ts (100%) rename {lib => packages/client/lib}/commands/ZPOPMAX_COUNT.spec.ts (100%) rename {lib => packages/client/lib}/commands/ZPOPMAX_COUNT.ts (100%) rename {lib => packages/client/lib}/commands/ZPOPMIN.spec.ts (100%) rename {lib => packages/client/lib}/commands/ZPOPMIN.ts (100%) rename {lib => packages/client/lib}/commands/ZPOPMIN_COUNT.spec.ts (100%) rename {lib => packages/client/lib}/commands/ZPOPMIN_COUNT.ts (100%) rename {lib => packages/client/lib}/commands/ZRANDMEMBER.spec.ts (100%) rename {lib => packages/client/lib}/commands/ZRANDMEMBER.ts (100%) rename {lib => packages/client/lib}/commands/ZRANDMEMBER_COUNT.spec.ts (100%) rename {lib => packages/client/lib}/commands/ZRANDMEMBER_COUNT.ts (100%) rename {lib => packages/client/lib}/commands/ZRANDMEMBER_COUNT_WITHSCORES.spec.ts (100%) rename {lib => packages/client/lib}/commands/ZRANDMEMBER_COUNT_WITHSCORES.ts (100%) rename {lib => packages/client/lib}/commands/ZRANGE.spec.ts (100%) rename {lib => packages/client/lib}/commands/ZRANGE.ts (100%) rename {lib => packages/client/lib}/commands/ZRANGEBYLEX.spec.ts (100%) rename {lib => packages/client/lib}/commands/ZRANGEBYLEX.ts (100%) rename {lib => packages/client/lib}/commands/ZRANGEBYSCORE.spec.ts (100%) rename {lib => packages/client/lib}/commands/ZRANGEBYSCORE.ts (100%) rename {lib => packages/client/lib}/commands/ZRANGEBYSCORE_WITHSCORES.spec.ts (100%) rename {lib => packages/client/lib}/commands/ZRANGEBYSCORE_WITHSCORES.ts (100%) rename {lib => packages/client/lib}/commands/ZRANGESTORE.spec.ts (100%) rename {lib => packages/client/lib}/commands/ZRANGESTORE.ts (100%) rename {lib => packages/client/lib}/commands/ZRANGE_WITHSCORES.spec.ts (100%) rename {lib => packages/client/lib}/commands/ZRANGE_WITHSCORES.ts (100%) rename {lib => packages/client/lib}/commands/ZRANK.spec.ts (100%) rename {lib => packages/client/lib}/commands/ZRANK.ts (100%) rename {lib => packages/client/lib}/commands/ZREM.spec.ts (100%) rename {lib => packages/client/lib}/commands/ZREM.ts (100%) rename {lib => packages/client/lib}/commands/ZREMRANGEBYLEX.spec.ts (100%) rename {lib => packages/client/lib}/commands/ZREMRANGEBYLEX.ts (100%) rename {lib => packages/client/lib}/commands/ZREMRANGEBYRANK.spec.ts (100%) rename {lib => packages/client/lib}/commands/ZREMRANGEBYRANK.ts (100%) rename {lib => packages/client/lib}/commands/ZREMRANGEBYSCORE.spec.ts (100%) rename {lib => packages/client/lib}/commands/ZREMRANGEBYSCORE.ts (100%) rename {lib => packages/client/lib}/commands/ZREVRANK.spec.ts (100%) rename {lib => packages/client/lib}/commands/ZREVRANK.ts (100%) rename {lib => packages/client/lib}/commands/ZSCAN.spec.ts (100%) rename {lib => packages/client/lib}/commands/ZSCAN.ts (100%) rename {lib => packages/client/lib}/commands/ZSCORE.spec.ts (100%) rename {lib => packages/client/lib}/commands/ZSCORE.ts (100%) rename {lib => packages/client/lib}/commands/ZUNION.spec.ts (100%) rename {lib => packages/client/lib}/commands/ZUNION.ts (100%) rename {lib => packages/client/lib}/commands/ZUNIONSTORE.spec.ts (100%) rename {lib => packages/client/lib}/commands/ZUNIONSTORE.ts (100%) rename {lib => packages/client/lib}/commands/ZUNION_WITHSCORES.spec.ts (100%) rename {lib => packages/client/lib}/commands/ZUNION_WITHSCORES.ts (100%) rename {lib => packages/client/lib}/commands/generic-transformers.spec.ts (100%) rename {lib => packages/client/lib}/commands/generic-transformers.ts (100%) rename {lib => packages/client/lib}/commands/index.ts (100%) rename {lib => packages/client/lib}/errors.ts (100%) rename {lib => packages/client/lib}/lua-script.ts (100%) rename {lib => packages/client/lib}/multi-command.spec.ts (100%) rename {lib => packages/client/lib}/multi-command.ts (100%) rename lib/test-utils/index.ts => packages/client/lib/test-utils.ts (93%) rename {lib => packages/client/lib}/ts-declarations/cluster-key-slot.d.ts (100%) rename {lib => packages/client/lib}/ts-declarations/redis-parser.d.ts (100%) rename {lib => packages/client/lib}/utils.ts (100%) create mode 100644 packages/client/package.json create mode 100644 packages/client/tsconfig.json create mode 100644 packages/json/.nycrc.json create mode 100644 packages/json/lib/commands/ARRAPPEND.spec.ts create mode 100644 packages/json/lib/commands/ARRAPPEND.ts create mode 100644 packages/json/lib/commands/ARRINDEX.spec.ts create mode 100644 packages/json/lib/commands/ARRINDEX.ts create mode 100644 packages/json/lib/commands/ARRINSERT.spec.ts create mode 100644 packages/json/lib/commands/ARRINSERT.ts create mode 100644 packages/json/lib/commands/ARRLEN.spec.ts create mode 100644 packages/json/lib/commands/ARRLEN.ts create mode 100644 packages/json/lib/commands/ARRPOP.spec.ts create mode 100644 packages/json/lib/commands/ARRPOP.ts create mode 100644 packages/json/lib/commands/ARRTRIM.spec.ts create mode 100644 packages/json/lib/commands/ARRTRIM.ts create mode 100644 packages/json/lib/commands/DEBUG_MEMORY.spec.ts create mode 100644 packages/json/lib/commands/DEBUG_MEMORY.ts create mode 100644 packages/json/lib/commands/DEL.spec.ts create mode 100644 packages/json/lib/commands/DEL.ts create mode 100644 packages/json/lib/commands/FORGET.spec.ts create mode 100644 packages/json/lib/commands/FORGET.ts create mode 100644 packages/json/lib/commands/GET.spec.ts create mode 100644 packages/json/lib/commands/GET.ts create mode 100644 packages/json/lib/commands/MGET.spec.ts create mode 100644 packages/json/lib/commands/MGET.ts create mode 100644 packages/json/lib/commands/NUMINCRBY.spec.ts create mode 100644 packages/json/lib/commands/NUMINCRBY.ts create mode 100644 packages/json/lib/commands/NUMMULTBY.spec.ts create mode 100644 packages/json/lib/commands/NUMMULTBY.ts create mode 100644 packages/json/lib/commands/OBJKEYS.spec.ts create mode 100644 packages/json/lib/commands/OBJKEYS.ts create mode 100644 packages/json/lib/commands/OBJLEN.spec.ts create mode 100644 packages/json/lib/commands/OBJLEN.ts create mode 100644 packages/json/lib/commands/RESP.spec.ts create mode 100644 packages/json/lib/commands/RESP.ts create mode 100644 packages/json/lib/commands/SET.spec.ts create mode 100644 packages/json/lib/commands/SET.ts create mode 100644 packages/json/lib/commands/STRAPPEND.spec.ts create mode 100644 packages/json/lib/commands/STRAPPEND.ts create mode 100644 packages/json/lib/commands/STRLEN.spec.ts create mode 100644 packages/json/lib/commands/STRLEN.ts create mode 100644 packages/json/lib/commands/TYPE.spec.ts create mode 100644 packages/json/lib/commands/TYPE.ts create mode 100644 packages/json/lib/commands/index.ts create mode 100644 packages/json/lib/index.ts create mode 100644 packages/json/lib/test-utils.ts create mode 100644 packages/json/package.json create mode 100644 packages/json/tsconfig.json create mode 100644 packages/search/.nycrc.json create mode 100644 packages/search/lib/commands/AGGREGATE.spec.ts create mode 100644 packages/search/lib/commands/AGGREGATE.ts create mode 100644 packages/search/lib/commands/ALIASADD.spec.ts create mode 100644 packages/search/lib/commands/ALIASADD.ts create mode 100644 packages/search/lib/commands/ALIASDEL.spec.ts create mode 100644 packages/search/lib/commands/ALIASDEL.ts create mode 100644 packages/search/lib/commands/ALIASUPDATE.spec.ts create mode 100644 packages/search/lib/commands/ALIASUPDATE.ts create mode 100644 packages/search/lib/commands/CONFIG_GET.spec.ts create mode 100644 packages/search/lib/commands/CONFIG_GET.ts create mode 100644 packages/search/lib/commands/CONFIG_SET.spec.ts create mode 100644 packages/search/lib/commands/CONFIG_SET.ts create mode 100644 packages/search/lib/commands/CREATE.spec.ts create mode 100644 packages/search/lib/commands/CREATE.ts create mode 100644 packages/search/lib/commands/DICTADD.spec.ts create mode 100644 packages/search/lib/commands/DICTADD.ts create mode 100644 packages/search/lib/commands/DICTDEL.spec.ts create mode 100644 packages/search/lib/commands/DICTDEL.ts create mode 100644 packages/search/lib/commands/DICTDUMP.spec.ts create mode 100644 packages/search/lib/commands/DICTDUMP.ts create mode 100644 packages/search/lib/commands/DROPINDEX.spec.ts create mode 100644 packages/search/lib/commands/DROPINDEX.ts create mode 100644 packages/search/lib/commands/EXPLAIN.spec.ts create mode 100644 packages/search/lib/commands/EXPLAIN.ts create mode 100644 packages/search/lib/commands/EXPLAINCLI.spec.ts create mode 100644 packages/search/lib/commands/EXPLAINCLI.ts create mode 100644 packages/search/lib/commands/INFO.spec.ts create mode 100644 packages/search/lib/commands/INFO.ts create mode 100644 packages/search/lib/commands/PROFILE.ts create mode 100644 packages/search/lib/commands/SEARCH.spec.ts create mode 100644 packages/search/lib/commands/SEARCH.ts create mode 100644 packages/search/lib/commands/SPELLCHECK.spec.ts create mode 100644 packages/search/lib/commands/SPELLCHECK.ts create mode 100644 packages/search/lib/commands/SUGADD.spec.ts create mode 100644 packages/search/lib/commands/SUGADD.ts create mode 100644 packages/search/lib/commands/SUGDEL.spec.ts create mode 100644 packages/search/lib/commands/SUGDEL.ts create mode 100644 packages/search/lib/commands/SUGGET.spec.ts create mode 100644 packages/search/lib/commands/SUGGET.ts create mode 100644 packages/search/lib/commands/SUGGET_WITHPAYLOADS.spec.ts create mode 100644 packages/search/lib/commands/SUGGET_WITHPAYLOADS.ts create mode 100644 packages/search/lib/commands/SUGGET_WITHSCORES.spec.ts create mode 100644 packages/search/lib/commands/SUGGET_WITHSCORES.ts create mode 100644 packages/search/lib/commands/SUGGET_WITHSCORES_WITHPAYLOADS.spec.ts create mode 100644 packages/search/lib/commands/SUGGET_WITHSCORES_WITHPAYLOADS.ts create mode 100644 packages/search/lib/commands/SUGLEN.spec.ts create mode 100644 packages/search/lib/commands/SUGLEN.ts create mode 100644 packages/search/lib/commands/SYNDUMP.spec.ts create mode 100644 packages/search/lib/commands/SYNDUMP.ts create mode 100644 packages/search/lib/commands/SYNUPDATE.spec.ts create mode 100644 packages/search/lib/commands/SYNUPDATE.ts create mode 100644 packages/search/lib/commands/TAGVALS.spec.ts create mode 100644 packages/search/lib/commands/TAGVALS.ts create mode 100644 packages/search/lib/commands/_LIST.spec.ts create mode 100644 packages/search/lib/commands/_LIST.ts create mode 100644 packages/search/lib/commands/index.spec.ts create mode 100644 packages/search/lib/commands/index.ts create mode 100644 packages/search/lib/index.ts create mode 100644 packages/search/lib/test-utils.ts create mode 100644 packages/search/package.json create mode 100644 packages/search/tsconfig.json create mode 100644 packages/test-utils/docker/Dockerfile create mode 100755 packages/test-utils/docker/entrypoint.sh rename {lib/test-utils => packages/test-utils/lib}/dockers.ts (90%) rename lib/test-utils/test-utils.ts => packages/test-utils/lib/index.ts (83%) create mode 100644 packages/test-utils/package.json create mode 100644 packages/test-utils/tsconfig.json diff --git a/.github/release-drafter-config.yml b/.github/release-drafter-config.yml index f17a2992fad..9a98b1c08a3 100644 --- a/.github/release-drafter-config.yml +++ b/.github/release-drafter-config.yml @@ -41,4 +41,3 @@ template: | We'd like to thank all the contributors who worked on this release! $CONTRIBUTORS - diff --git a/.github/workflows/benchmark.yml b/.github/workflows/benchmark.yml deleted file mode 100644 index 2df438eb19c..00000000000 --- a/.github/workflows/benchmark.yml +++ /dev/null @@ -1,46 +0,0 @@ -name: Benchmark - -on: - push: - branches: - - master - - v4.0 - -jobs: - benchmark: - name: Benchmark - runs-on: ubuntu-latest - strategy: - fail-fast: false - matrix: - node-version: [16.x] - redis-version: [6.x] - - steps: - - uses: actions/checkout@v2.3.4 - with: - fetch-depth: 1 - - - name: Use Node.js ${{ matrix.node-version }} - uses: actions/setup-node@v2.3.0 - with: - node-version: ${{ matrix.node-version }} - - - name: Setup Redis - uses: shogo82148/actions-setup-redis@v1.12.0 - with: - redis-version: ${{ matrix.redis-version }} - - - name: Install Packages - run: npm ci - - - name: Build - run: npm run build - - - name: Install Benchmark Packages - run: npm ci - working-directory: ./benchmark - - - name: Benchmark - run: npm run start - working-directory: ./benchmark diff --git a/.github/workflows/tests.yml b/.github/workflows/tests.yml index b6b2dd050eb..59421208396 100644 --- a/.github/workflows/tests.yml +++ b/.github/workflows/tests.yml @@ -28,6 +28,9 @@ jobs: - name: Install Packages run: npm ci + - name: Build tests tools + run: npm run build:tests-tools + - name: Run Tests run: npm run test -- --forbid-only --redis-version=${{ matrix.redis-version }} diff --git a/.gitignore b/.gitignore index 8d752019aa0..9ee58bfbd30 100644 --- a/.gitignore +++ b/.gitignore @@ -1,9 +1,8 @@ -.vscode/ .idea/ -node_modules/ -dist/ .nyc_output/ +.vscode/ coverage/ -dump.rdb -documentation/ +dist/ +node_modules/ .DS_Store +dump.rdb diff --git a/.npmignore b/.npmignore deleted file mode 100644 index 115395e3fbb..00000000000 --- a/.npmignore +++ /dev/null @@ -1,20 +0,0 @@ -.vscode/ -.idea/ -node_modules/ -.nyc_output/ -coverage/ -dump.rdb -documentation/ -CONTRIBUTING.md -tsconfig.json -.deepsource.toml -.nycrc.json -benchmark/ -.github/ -scripts/ -lib/ -index.ts -*.spec.* -dist/lib/test-utils.* -.DS_Store -examples/ diff --git a/.nycrc.json b/.nycrc.json deleted file mode 100644 index 918b308a898..00000000000 --- a/.nycrc.json +++ /dev/null @@ -1,4 +0,0 @@ -{ - "extends": "@istanbuljs/nyc-config-typescript", - "exclude": ["**/*.spec.ts", "lib/test-utils/**/*.ts"] -} \ No newline at end of file diff --git a/CONTRIBUTING.md b/CONTRIBUTING.md index fbad5205081..a041584e6fd 100644 --- a/CONTRIBUTING.md +++ b/CONTRIBUTING.md @@ -47,10 +47,7 @@ Node Redis has a full test suite with coverage setup. To run the tests, run `npm install` to install dependencies, then run `npm test`. -Note that the test suite assumes that a few tools are installed in your environment, such as: - -- redis (make sure redis-server is not running when starting the tests, it's part of the test-suite to start it and you'll end up with a "port already in use" error) -- stunnel (for TLS tests) +Note that the test suite assumes that [`docker`](https://www.docker.com/) is installed in your environment. ### Submitting Code for Review diff --git a/README.md b/README.md index 9fe9e70a253..35648bd72ef 100644 --- a/README.md +++ b/README.md @@ -1,296 +1,18 @@ -

- - - -

Node Redis

-

+# Node-Redis monorpo - +### Clients ---- +| Name | Description | +|------------------------------------|-------------| +| [redis](./packages/all-in-one) | | +| [@redis/client](./packages/client) | | -## Installation +### [Modules](https://redis.io/modules) -```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 { - 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. +| Name | Description | +|------------------------------------|------------------------------------------------------------| +| [@redis/json](./packages/json) | [Redis JSON](https://oss.redis.com/redisjson/) commands | +| [@redis/search](./packages/search) | [Redis Search](https://oss.redis.com/redisearch/) commands | ## Contributing diff --git a/benchmark/.gitignore b/benchmark/.gitignore deleted file mode 100644 index 3c3629e647f..00000000000 --- a/benchmark/.gitignore +++ /dev/null @@ -1 +0,0 @@ -node_modules diff --git a/benchmark/index.js b/benchmark/index.js deleted file mode 100644 index 37f88176653..00000000000 --- a/benchmark/index.js +++ /dev/null @@ -1,81 +0,0 @@ -import { add, suite, cycle, complete } from 'benny'; -import v4 from 'v4'; -import v3 from 'v3'; -import { once } from 'events'; - -const v4Client = v4.createClient(), - v4LegacyClient = v4.createClient({ - legacyMode: true - }), - v3Client = v3.createClient(); - -await Promise.all([ - v4Client.connect(), - v4LegacyClient.connect(), - once(v3Client, 'connect') -]); - -const key = random(100), - value = random(100); - -function random(size) { - const result = []; - - for (let i = 0; i < size; i++) { - result.push(Math.floor(Math.random() * 10)); - } - - return result.join(''); -} - -suite( - 'SET GET', - add('v4', async () => { - await Promise.all([ - v4Client.set(key, value), - v4Client.get(key) - ]); - }), - add('v4 - legacy mode', () => { - return new Promise((resolve, reject) => { - v4LegacyClient.set(key, value); - v4LegacyClient.get(key, (err, reply) => { - if (err) { - reject(err); - } else { - resolve(reply); - } - }); - }); - }), - add('v3', () => { - return new Promise((resolve, reject) => { - v3Client.set(key, value); - v3Client.get(key, (err, reply) => { - if (err) { - reject(err); - } else { - resolve(reply); - } - }); - }); - }), - cycle(), - complete(), - complete(() => { - return Promise.all([ - v4Client.disconnect(), - v4LegacyClient.disconnect(), - new Promise((resolve, reject) => { - v3Client.quit((err) => { - if (err) { - reject(err); - } else { - resolve(err); - } - }); - }) - ]); - }) -); - diff --git a/benchmark/package-lock.json b/benchmark/package-lock.json deleted file mode 100644 index 4afaf8c305c..00000000000 --- a/benchmark/package-lock.json +++ /dev/null @@ -1,849 +0,0 @@ -{ - "name": "benchmark", - "lockfileVersion": 2, - "requires": true, - "packages": { - "": { - "name": "benchmark", - "license": "ISC", - "dependencies": { - "benny": "3.6.15", - "v3": "npm:redis@3.1.2", - "v4": "file:../" - } - }, - "..": { - "name": "redis", - "version": "4.0.0-rc.2", - "license": "MIT", - "dependencies": { - "cluster-key-slot": "1.1.0", - "generic-pool": "3.8.2", - "redis-parser": "3.0.0", - "yallist": "4.0.0" - }, - "devDependencies": { - "@istanbuljs/nyc-config-typescript": "^1.0.1", - "@tsconfig/node12": "^1.0.9", - "@types/mocha": "^9.0.0", - "@types/node": "^16.10.3", - "@types/sinon": "^10.0.4", - "@types/which": "^2.0.1", - "@types/yallist": "^4.0.1", - "mocha": "^9.1.2", - "nyc": "^15.1.0", - "release-it": "^14.11.6", - "sinon": "^11.1.2", - "source-map-support": "^0.5.20", - "ts-node": "^10.3.0", - "typedoc": "^0.22.5", - "typedoc-github-wiki-theme": "^0.6.0", - "typedoc-plugin-markdown": "^3.11.3", - "typescript": "^4.4.3", - "which": "^2.0.2" - }, - "engines": { - "node": ">=12" - } - }, - "node_modules/@arrows/array": { - "version": "1.4.1", - "resolved": "https://registry.npmjs.org/@arrows/array/-/array-1.4.1.tgz", - "integrity": "sha512-MGYS8xi3c4tTy1ivhrVntFvufoNzje0PchjEz6G/SsWRgUKxL4tKwS6iPdO8vsaJYldagAeWMd5KRD0aX3Q39g==", - "dependencies": { - "@arrows/composition": "^1.2.2" - } - }, - "node_modules/@arrows/composition": { - "version": "1.2.2", - "resolved": "https://registry.npmjs.org/@arrows/composition/-/composition-1.2.2.tgz", - "integrity": "sha512-9fh1yHwrx32lundiB3SlZ/VwuStPB4QakPsSLrGJFH6rCXvdrd060ivAZ7/2vlqPnEjBkPRRXOcG1YOu19p2GQ==" - }, - "node_modules/@arrows/dispatch": { - "version": "1.0.3", - "resolved": "https://registry.npmjs.org/@arrows/dispatch/-/dispatch-1.0.3.tgz", - "integrity": "sha512-v/HwvrFonitYZM2PmBlAlCqVqxrkIIoiEuy5bQgn0BdfvlL0ooSBzcPzTMrtzY8eYktPyYcHg8fLbSgyybXEqw==", - "dependencies": { - "@arrows/composition": "^1.2.2" - } - }, - "node_modules/@arrows/error": { - "version": "1.0.2", - "resolved": "https://registry.npmjs.org/@arrows/error/-/error-1.0.2.tgz", - "integrity": "sha512-yvkiv1ay4Z3+Z6oQsUkedsQm5aFdyPpkBUQs8vejazU/RmANABx6bMMcBPPHI4aW43VPQmXFfBzr/4FExwWTEA==" - }, - "node_modules/@arrows/multimethod": { - "version": "1.1.7", - "resolved": "https://registry.npmjs.org/@arrows/multimethod/-/multimethod-1.1.7.tgz", - "integrity": "sha512-EjHD3XuGAV4G28rm7mu8k7zQJh/EOizh104/p9i2ofGcnL5mgKONFH/Bq6H3SJjM+WDAlKcR9WBpNhaAKCnH2g==", - "dependencies": { - "@arrows/array": "^1.4.0", - "@arrows/composition": "^1.2.2", - "@arrows/error": "^1.0.2", - "fast-deep-equal": "^3.1.1" - } - }, - "node_modules/ansi-escapes": { - "version": "4.3.2", - "resolved": "https://registry.npmjs.org/ansi-escapes/-/ansi-escapes-4.3.2.tgz", - "integrity": "sha512-gKXj5ALrKWQLsYG9jlTRmR/xKluxHV+Z9QEwNIgCfM1/uwPMCuzVVnh5mwTd+OuBZcwSIMbqssNWRm1lE51QaQ==", - "dependencies": { - "type-fest": "^0.21.3" - }, - "engines": { - "node": ">=8" - }, - "funding": { - "url": "https://github.com/sponsors/sindresorhus" - } - }, - "node_modules/ansi-regex": { - "version": "5.0.1", - "resolved": "https://registry.npmjs.org/ansi-regex/-/ansi-regex-5.0.1.tgz", - "integrity": "sha512-quJQXlTSUGL2LH9SUXo8VwsY4soanhgo6LNSm84E1LBcE8s3O0wpdiRzyR9z/ZZJMlMWv37qOOb9pdJlMUEKFQ==", - "engines": { - "node": ">=8" - } - }, - "node_modules/ansi-styles": { - "version": "4.3.0", - "resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-4.3.0.tgz", - "integrity": "sha512-zbB9rCJAT1rbjiVDb2hqKFHNYLxgtk8NURxZ3IZwD3F6NtxbXZQCnnSi1Lkx+IDohdPlFp222wVALIheZJQSEg==", - "dependencies": { - "color-convert": "^2.0.1" - }, - "engines": { - "node": ">=8" - }, - "funding": { - "url": "https://github.com/chalk/ansi-styles?sponsor=1" - } - }, - "node_modules/astral-regex": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/astral-regex/-/astral-regex-2.0.0.tgz", - "integrity": "sha512-Z7tMw1ytTXt5jqMcOP+OQteU1VuNK9Y02uuJtKQ1Sv69jXQKKg5cibLwGJow8yzZP+eAc18EmLGPal0bp36rvQ==", - "engines": { - "node": ">=8" - } - }, - "node_modules/at-least-node": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/at-least-node/-/at-least-node-1.0.0.tgz", - "integrity": "sha512-+q/t7Ekv1EDY2l6Gda6LLiX14rU9TV20Wa3ofeQmwPFZbOMo9DXrLbOjFaaclkXKWidIaopwAObQDqwWtGUjqg==", - "engines": { - "node": ">= 4.0.0" - } - }, - "node_modules/benchmark": { - "version": "2.1.4", - "resolved": "https://registry.npmjs.org/benchmark/-/benchmark-2.1.4.tgz", - "integrity": "sha1-CfPeMckWQl1JjMLuVloOvzwqVik=", - "dependencies": { - "lodash": "^4.17.4", - "platform": "^1.3.3" - } - }, - "node_modules/benny": { - "version": "3.6.15", - "resolved": "https://registry.npmjs.org/benny/-/benny-3.6.15.tgz", - "integrity": "sha512-kq6XVGGYVou3Y8KNPs3SEF881vi5fJ8sIf9w69D2rreiNfRicWVWK6u6/mObMw6BiexoHHumtipn5gcu0Tngng==", - "dependencies": { - "@arrows/composition": "^1.0.0", - "@arrows/dispatch": "^1.0.2", - "@arrows/multimethod": "^1.1.6", - "benchmark": "^2.1.4", - "fs-extra": "^9.0.1", - "json2csv": "^5.0.4", - "kleur": "^4.1.3", - "log-update": "^4.0.0", - "prettier": "^2.1.2", - "stats-median": "^1.0.1" - } - }, - "node_modules/cli-cursor": { - "version": "3.1.0", - "resolved": "https://registry.npmjs.org/cli-cursor/-/cli-cursor-3.1.0.tgz", - "integrity": "sha512-I/zHAwsKf9FqGoXM4WWRACob9+SNukZTd94DWF57E4toouRulbCxcUh6RKUEOQlYTHJnzkPMySvPNaaSLNfLZw==", - "dependencies": { - "restore-cursor": "^3.1.0" - }, - "engines": { - "node": ">=8" - } - }, - "node_modules/color-convert": { - "version": "2.0.1", - "resolved": "https://registry.npmjs.org/color-convert/-/color-convert-2.0.1.tgz", - "integrity": "sha512-RRECPsj7iu/xb5oKYcsFHSppFNnsj/52OVTRKb4zP5onXwVF3zVmmToNcOfGC+CRDpfK/U584fMg38ZHCaElKQ==", - "dependencies": { - "color-name": "~1.1.4" - }, - "engines": { - "node": ">=7.0.0" - } - }, - "node_modules/color-name": { - "version": "1.1.4", - "resolved": "https://registry.npmjs.org/color-name/-/color-name-1.1.4.tgz", - "integrity": "sha512-dOy+3AuW3a2wNbZHIuMZpTcgjGuLU/uBL/ubcZF9OXbDo8ff4O8yVp5Bf0efS8uEoYo5q4Fx7dY9OgQGXgAsQA==" - }, - "node_modules/commander": { - "version": "6.2.1", - "resolved": "https://registry.npmjs.org/commander/-/commander-6.2.1.tgz", - "integrity": "sha512-U7VdrJFnJgo4xjrHpTzu0yrHPGImdsmD95ZlgYSEajAn2JKzDhDTPG9kBTefmObL2w/ngeZnilk+OV9CG3d7UA==", - "engines": { - "node": ">= 6" - } - }, - "node_modules/denque": { - "version": "1.5.1", - "resolved": "https://registry.npmjs.org/denque/-/denque-1.5.1.tgz", - "integrity": "sha512-XwE+iZ4D6ZUB7mfYRMb5wByE8L74HCn30FBN7sWnXksWc1LO1bPDl67pBR9o/kC4z/xSNAwkMYcGgqDV3BE3Hw==", - "engines": { - "node": ">=0.10" - } - }, - "node_modules/emoji-regex": { - "version": "8.0.0", - "resolved": "https://registry.npmjs.org/emoji-regex/-/emoji-regex-8.0.0.tgz", - "integrity": "sha512-MSjYzcWNOA0ewAHpz0MxpYFvwg6yjy1NG3xteoqz644VCo/RPgnr1/GGt+ic3iJTzQ8Eu3TdM14SawnVUmGE6A==" - }, - "node_modules/fast-deep-equal": { - "version": "3.1.3", - "resolved": "https://registry.npmjs.org/fast-deep-equal/-/fast-deep-equal-3.1.3.tgz", - "integrity": "sha512-f3qQ9oQy9j2AhBe/H9VC91wLmKBCCU/gDOnKNAYG5hswO7BLKj09Hc5HYNz9cGI++xlpDCIgDaitVs03ATR84Q==" - }, - "node_modules/fs-extra": { - "version": "9.1.0", - "resolved": "https://registry.npmjs.org/fs-extra/-/fs-extra-9.1.0.tgz", - "integrity": "sha512-hcg3ZmepS30/7BSFqRvoo3DOMQu7IjqxO5nCDt+zM9XWjb33Wg7ziNT+Qvqbuc3+gWpzO02JubVyk2G4Zvo1OQ==", - "dependencies": { - "at-least-node": "^1.0.0", - "graceful-fs": "^4.2.0", - "jsonfile": "^6.0.1", - "universalify": "^2.0.0" - }, - "engines": { - "node": ">=10" - } - }, - "node_modules/graceful-fs": { - "version": "4.2.8", - "resolved": "https://registry.npmjs.org/graceful-fs/-/graceful-fs-4.2.8.tgz", - "integrity": "sha512-qkIilPUYcNhJpd33n0GBXTB1MMPp14TxEsEs0pTrsSVucApsYzW5V+Q8Qxhik6KU3evy+qkAAowTByymK0avdg==" - }, - "node_modules/is-fullwidth-code-point": { - "version": "3.0.0", - "resolved": "https://registry.npmjs.org/is-fullwidth-code-point/-/is-fullwidth-code-point-3.0.0.tgz", - "integrity": "sha512-zymm5+u+sCsSWyD9qNaejV3DFvhCKclKdizYaJUuHA83RLjb7nSuGnddCHGv0hk+KY7BMAlsWeK4Ueg6EV6XQg==", - "engines": { - "node": ">=8" - } - }, - "node_modules/json2csv": { - "version": "5.0.6", - "resolved": "https://registry.npmjs.org/json2csv/-/json2csv-5.0.6.tgz", - "integrity": "sha512-0/4Lv6IenJV0qj2oBdgPIAmFiKKnh8qh7bmLFJ+/ZZHLjSeiL3fKKGX3UryvKPbxFbhV+JcYo9KUC19GJ/Z/4A==", - "dependencies": { - "commander": "^6.1.0", - "jsonparse": "^1.3.1", - "lodash.get": "^4.4.2" - }, - "bin": { - "json2csv": "bin/json2csv.js" - }, - "engines": { - "node": ">= 10", - "npm": ">= 6.13.0" - } - }, - "node_modules/jsonfile": { - "version": "6.1.0", - "resolved": "https://registry.npmjs.org/jsonfile/-/jsonfile-6.1.0.tgz", - "integrity": "sha512-5dgndWOriYSm5cnYaJNhalLNDKOqFwyDB/rr1E9ZsGciGvKPs8R2xYGCacuf3z6K1YKDz182fd+fY3cn3pMqXQ==", - "dependencies": { - "universalify": "^2.0.0" - }, - "optionalDependencies": { - "graceful-fs": "^4.1.6" - } - }, - "node_modules/jsonparse": { - "version": "1.3.1", - "resolved": "https://registry.npmjs.org/jsonparse/-/jsonparse-1.3.1.tgz", - "integrity": "sha1-P02uSpH6wxX3EGL4UhzCOfE2YoA=", - "engines": [ - "node >= 0.2.0" - ] - }, - "node_modules/kleur": { - "version": "4.1.4", - "resolved": "https://registry.npmjs.org/kleur/-/kleur-4.1.4.tgz", - "integrity": "sha512-8QADVssbrFjivHWQU7KkMgptGTl6WAcSdlbBPY4uNF+mWr6DGcKrvY2w4FQJoXch7+fKMjj0dRrL75vk3k23OA==", - "engines": { - "node": ">=6" - } - }, - "node_modules/lodash": { - "version": "4.17.21", - "resolved": "https://registry.npmjs.org/lodash/-/lodash-4.17.21.tgz", - "integrity": "sha512-v2kDEe57lecTulaDIuNTPy3Ry4gLGJ6Z1O3vE1krgXZNrsQ+LFTGHVxVjcXPs17LhbZVGedAJv8XZ1tvj5FvSg==" - }, - "node_modules/lodash.get": { - "version": "4.4.2", - "resolved": "https://registry.npmjs.org/lodash.get/-/lodash.get-4.4.2.tgz", - "integrity": "sha1-LRd/ZS+jHpObRDjVNBSZ36OCXpk=" - }, - "node_modules/log-update": { - "version": "4.0.0", - "resolved": "https://registry.npmjs.org/log-update/-/log-update-4.0.0.tgz", - "integrity": "sha512-9fkkDevMefjg0mmzWFBW8YkFP91OrizzkW3diF7CpG+S2EYdy4+TVfGwz1zeF8x7hCx1ovSPTOE9Ngib74qqUg==", - "dependencies": { - "ansi-escapes": "^4.3.0", - "cli-cursor": "^3.1.0", - "slice-ansi": "^4.0.0", - "wrap-ansi": "^6.2.0" - }, - "engines": { - "node": ">=10" - }, - "funding": { - "url": "https://github.com/sponsors/sindresorhus" - } - }, - "node_modules/mimic-fn": { - "version": "2.1.0", - "resolved": "https://registry.npmjs.org/mimic-fn/-/mimic-fn-2.1.0.tgz", - "integrity": "sha512-OqbOk5oEQeAZ8WXWydlu9HJjz9WVdEIvamMCcXmuqUYjTknH/sqsWvhQ3vgwKFRR1HpjvNBKQ37nbJgYzGqGcg==", - "engines": { - "node": ">=6" - } - }, - "node_modules/onetime": { - "version": "5.1.2", - "resolved": "https://registry.npmjs.org/onetime/-/onetime-5.1.2.tgz", - "integrity": "sha512-kbpaSSGJTWdAY5KPVeMOKXSrPtr8C8C7wodJbcsd51jRnmD+GZu8Y0VoU6Dm5Z4vWr0Ig/1NKuWRKf7j5aaYSg==", - "dependencies": { - "mimic-fn": "^2.1.0" - }, - "engines": { - "node": ">=6" - }, - "funding": { - "url": "https://github.com/sponsors/sindresorhus" - } - }, - "node_modules/platform": { - "version": "1.3.6", - "resolved": "https://registry.npmjs.org/platform/-/platform-1.3.6.tgz", - "integrity": "sha512-fnWVljUchTro6RiCFvCXBbNhJc2NijN7oIQxbwsyL0buWJPG85v81ehlHI9fXrJsMNgTofEoWIQeClKpgxFLrg==" - }, - "node_modules/prettier": { - "version": "2.4.1", - "resolved": "https://registry.npmjs.org/prettier/-/prettier-2.4.1.tgz", - "integrity": "sha512-9fbDAXSBcc6Bs1mZrDYb3XKzDLm4EXXL9sC1LqKP5rZkT6KRr/rf9amVUcODVXgguK/isJz0d0hP72WeaKWsvA==", - "bin": { - "prettier": "bin-prettier.js" - }, - "engines": { - "node": ">=10.13.0" - } - }, - "node_modules/redis-commands": { - "version": "1.7.0", - "resolved": "https://registry.npmjs.org/redis-commands/-/redis-commands-1.7.0.tgz", - "integrity": "sha512-nJWqw3bTFy21hX/CPKHth6sfhZbdiHP6bTawSgQBlKOVRG7EZkfHbbHwQJnrE4vsQf0CMNE+3gJ4Fmm16vdVlQ==" - }, - "node_modules/redis-errors": { - "version": "1.2.0", - "resolved": "https://registry.npmjs.org/redis-errors/-/redis-errors-1.2.0.tgz", - "integrity": "sha1-62LSrbFeTq9GEMBK/hUpOEJQq60=", - "engines": { - "node": ">=4" - } - }, - "node_modules/redis-parser": { - "version": "3.0.0", - "resolved": "https://registry.npmjs.org/redis-parser/-/redis-parser-3.0.0.tgz", - "integrity": "sha1-tm2CjNyv5rS4pCin3vTGvKwxyLQ=", - "dependencies": { - "redis-errors": "^1.0.0" - }, - "engines": { - "node": ">=4" - } - }, - "node_modules/restore-cursor": { - "version": "3.1.0", - "resolved": "https://registry.npmjs.org/restore-cursor/-/restore-cursor-3.1.0.tgz", - "integrity": "sha512-l+sSefzHpj5qimhFSE5a8nufZYAM3sBSVMAPtYkmC+4EH2anSGaEMXSD0izRQbu9nfyQ9y5JrVmp7E8oZrUjvA==", - "dependencies": { - "onetime": "^5.1.0", - "signal-exit": "^3.0.2" - }, - "engines": { - "node": ">=8" - } - }, - "node_modules/signal-exit": { - "version": "3.0.5", - "resolved": "https://registry.npmjs.org/signal-exit/-/signal-exit-3.0.5.tgz", - "integrity": "sha512-KWcOiKeQj6ZyXx7zq4YxSMgHRlod4czeBQZrPb8OKcohcqAXShm7E20kEMle9WBt26hFcAf0qLOcp5zmY7kOqQ==" - }, - "node_modules/slice-ansi": { - "version": "4.0.0", - "resolved": "https://registry.npmjs.org/slice-ansi/-/slice-ansi-4.0.0.tgz", - "integrity": "sha512-qMCMfhY040cVHT43K9BFygqYbUPFZKHOg7K73mtTWJRb8pyP3fzf4Ixd5SzdEJQ6MRUg/WBnOLxghZtKKurENQ==", - "dependencies": { - "ansi-styles": "^4.0.0", - "astral-regex": "^2.0.0", - "is-fullwidth-code-point": "^3.0.0" - }, - "engines": { - "node": ">=10" - }, - "funding": { - "url": "https://github.com/chalk/slice-ansi?sponsor=1" - } - }, - "node_modules/stats-median": { - "version": "1.0.1", - "resolved": "https://registry.npmjs.org/stats-median/-/stats-median-1.0.1.tgz", - "integrity": "sha512-IYsheLg6dasD3zT/w9+8Iq9tcIQqqu91ZIpJOnIEM25C3X/g4Tl8mhXwW2ZQpbrsJISr9+wizEYgsibN5/b32Q==" - }, - "node_modules/string-width": { - "version": "4.2.3", - "resolved": "https://registry.npmjs.org/string-width/-/string-width-4.2.3.tgz", - "integrity": "sha512-wKyQRQpjJ0sIp62ErSZdGsjMJWsap5oRNihHhu6G7JVO/9jIB6UyevL+tXuOqrng8j/cxKTWyWUwvSTriiZz/g==", - "dependencies": { - "emoji-regex": "^8.0.0", - "is-fullwidth-code-point": "^3.0.0", - "strip-ansi": "^6.0.1" - }, - "engines": { - "node": ">=8" - } - }, - "node_modules/strip-ansi": { - "version": "6.0.1", - "resolved": "https://registry.npmjs.org/strip-ansi/-/strip-ansi-6.0.1.tgz", - "integrity": "sha512-Y38VPSHcqkFrCpFnQ9vuSXmquuv5oXOKpGeT6aGrr3o3Gc9AlVa6JBfUSOCnbxGGZF+/0ooI7KrPuUSztUdU5A==", - "dependencies": { - "ansi-regex": "^5.0.1" - }, - "engines": { - "node": ">=8" - } - }, - "node_modules/type-fest": { - "version": "0.21.3", - "resolved": "https://registry.npmjs.org/type-fest/-/type-fest-0.21.3.tgz", - "integrity": "sha512-t0rzBq87m3fVcduHDUFhKmyyX+9eo6WQjZvf51Ea/M0Q7+T374Jp1aUiyUl0GKxp8M/OETVHSDvmkyPgvX+X2w==", - "engines": { - "node": ">=10" - }, - "funding": { - "url": "https://github.com/sponsors/sindresorhus" - } - }, - "node_modules/universalify": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/universalify/-/universalify-2.0.0.tgz", - "integrity": "sha512-hAZsKq7Yy11Zu1DE0OzWjw7nnLZmJZYTDZZyEFHZdUhV8FkH5MCfoU1XMaxXovpyW5nq5scPqq0ZDP9Zyl04oQ==", - "engines": { - "node": ">= 10.0.0" - } - }, - "node_modules/v3": { - "name": "redis", - "version": "3.1.2", - "resolved": "https://registry.npmjs.org/redis/-/redis-3.1.2.tgz", - "integrity": "sha512-grn5KoZLr/qrRQVwoSkmzdbw6pwF+/rwODtrOr6vuBRiR/f3rjSTGupbF90Zpqm2oenix8Do6RV7pYEkGwlKkw==", - "dependencies": { - "denque": "^1.5.0", - "redis-commands": "^1.7.0", - "redis-errors": "^1.2.0", - "redis-parser": "^3.0.0" - }, - "engines": { - "node": ">=10" - }, - "funding": { - "type": "opencollective", - "url": "https://opencollective.com/node-redis" - } - }, - "node_modules/v4": { - "resolved": "..", - "link": true - }, - "node_modules/wrap-ansi": { - "version": "6.2.0", - "resolved": "https://registry.npmjs.org/wrap-ansi/-/wrap-ansi-6.2.0.tgz", - "integrity": "sha512-r6lPcBGxZXlIcymEu7InxDMhdW0KDxpLgoFLcguasxCaJ/SOIZwINatK9KY/tf+ZrlywOKU0UDj3ATXUBfxJXA==", - "dependencies": { - "ansi-styles": "^4.0.0", - "string-width": "^4.1.0", - "strip-ansi": "^6.0.0" - }, - "engines": { - "node": ">=8" - } - } - }, - "dependencies": { - "@arrows/array": { - "version": "1.4.1", - "resolved": "https://registry.npmjs.org/@arrows/array/-/array-1.4.1.tgz", - "integrity": "sha512-MGYS8xi3c4tTy1ivhrVntFvufoNzje0PchjEz6G/SsWRgUKxL4tKwS6iPdO8vsaJYldagAeWMd5KRD0aX3Q39g==", - "requires": { - "@arrows/composition": "^1.2.2" - } - }, - "@arrows/composition": { - "version": "1.2.2", - "resolved": "https://registry.npmjs.org/@arrows/composition/-/composition-1.2.2.tgz", - "integrity": "sha512-9fh1yHwrx32lundiB3SlZ/VwuStPB4QakPsSLrGJFH6rCXvdrd060ivAZ7/2vlqPnEjBkPRRXOcG1YOu19p2GQ==" - }, - "@arrows/dispatch": { - "version": "1.0.3", - "resolved": "https://registry.npmjs.org/@arrows/dispatch/-/dispatch-1.0.3.tgz", - "integrity": "sha512-v/HwvrFonitYZM2PmBlAlCqVqxrkIIoiEuy5bQgn0BdfvlL0ooSBzcPzTMrtzY8eYktPyYcHg8fLbSgyybXEqw==", - "requires": { - "@arrows/composition": "^1.2.2" - } - }, - "@arrows/error": { - "version": "1.0.2", - "resolved": "https://registry.npmjs.org/@arrows/error/-/error-1.0.2.tgz", - "integrity": "sha512-yvkiv1ay4Z3+Z6oQsUkedsQm5aFdyPpkBUQs8vejazU/RmANABx6bMMcBPPHI4aW43VPQmXFfBzr/4FExwWTEA==" - }, - "@arrows/multimethod": { - "version": "1.1.7", - "resolved": "https://registry.npmjs.org/@arrows/multimethod/-/multimethod-1.1.7.tgz", - "integrity": "sha512-EjHD3XuGAV4G28rm7mu8k7zQJh/EOizh104/p9i2ofGcnL5mgKONFH/Bq6H3SJjM+WDAlKcR9WBpNhaAKCnH2g==", - "requires": { - "@arrows/array": "^1.4.0", - "@arrows/composition": "^1.2.2", - "@arrows/error": "^1.0.2", - "fast-deep-equal": "^3.1.1" - } - }, - "ansi-escapes": { - "version": "4.3.2", - "resolved": "https://registry.npmjs.org/ansi-escapes/-/ansi-escapes-4.3.2.tgz", - "integrity": "sha512-gKXj5ALrKWQLsYG9jlTRmR/xKluxHV+Z9QEwNIgCfM1/uwPMCuzVVnh5mwTd+OuBZcwSIMbqssNWRm1lE51QaQ==", - "requires": { - "type-fest": "^0.21.3" - } - }, - "ansi-regex": { - "version": "5.0.1", - "resolved": "https://registry.npmjs.org/ansi-regex/-/ansi-regex-5.0.1.tgz", - "integrity": "sha512-quJQXlTSUGL2LH9SUXo8VwsY4soanhgo6LNSm84E1LBcE8s3O0wpdiRzyR9z/ZZJMlMWv37qOOb9pdJlMUEKFQ==" - }, - "ansi-styles": { - "version": "4.3.0", - "resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-4.3.0.tgz", - "integrity": "sha512-zbB9rCJAT1rbjiVDb2hqKFHNYLxgtk8NURxZ3IZwD3F6NtxbXZQCnnSi1Lkx+IDohdPlFp222wVALIheZJQSEg==", - "requires": { - "color-convert": "^2.0.1" - } - }, - "astral-regex": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/astral-regex/-/astral-regex-2.0.0.tgz", - "integrity": "sha512-Z7tMw1ytTXt5jqMcOP+OQteU1VuNK9Y02uuJtKQ1Sv69jXQKKg5cibLwGJow8yzZP+eAc18EmLGPal0bp36rvQ==" - }, - "at-least-node": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/at-least-node/-/at-least-node-1.0.0.tgz", - "integrity": "sha512-+q/t7Ekv1EDY2l6Gda6LLiX14rU9TV20Wa3ofeQmwPFZbOMo9DXrLbOjFaaclkXKWidIaopwAObQDqwWtGUjqg==" - }, - "benchmark": { - "version": "2.1.4", - "resolved": "https://registry.npmjs.org/benchmark/-/benchmark-2.1.4.tgz", - "integrity": "sha1-CfPeMckWQl1JjMLuVloOvzwqVik=", - "requires": { - "lodash": "^4.17.4", - "platform": "^1.3.3" - } - }, - "benny": { - "version": "3.6.15", - "resolved": "https://registry.npmjs.org/benny/-/benny-3.6.15.tgz", - "integrity": "sha512-kq6XVGGYVou3Y8KNPs3SEF881vi5fJ8sIf9w69D2rreiNfRicWVWK6u6/mObMw6BiexoHHumtipn5gcu0Tngng==", - "requires": { - "@arrows/composition": "^1.0.0", - "@arrows/dispatch": "^1.0.2", - "@arrows/multimethod": "^1.1.6", - "benchmark": "^2.1.4", - "fs-extra": "^9.0.1", - "json2csv": "^5.0.4", - "kleur": "^4.1.3", - "log-update": "^4.0.0", - "prettier": "^2.1.2", - "stats-median": "^1.0.1" - } - }, - "cli-cursor": { - "version": "3.1.0", - "resolved": "https://registry.npmjs.org/cli-cursor/-/cli-cursor-3.1.0.tgz", - "integrity": "sha512-I/zHAwsKf9FqGoXM4WWRACob9+SNukZTd94DWF57E4toouRulbCxcUh6RKUEOQlYTHJnzkPMySvPNaaSLNfLZw==", - "requires": { - "restore-cursor": "^3.1.0" - } - }, - "color-convert": { - "version": "2.0.1", - "resolved": "https://registry.npmjs.org/color-convert/-/color-convert-2.0.1.tgz", - "integrity": "sha512-RRECPsj7iu/xb5oKYcsFHSppFNnsj/52OVTRKb4zP5onXwVF3zVmmToNcOfGC+CRDpfK/U584fMg38ZHCaElKQ==", - "requires": { - "color-name": "~1.1.4" - } - }, - "color-name": { - "version": "1.1.4", - "resolved": "https://registry.npmjs.org/color-name/-/color-name-1.1.4.tgz", - "integrity": "sha512-dOy+3AuW3a2wNbZHIuMZpTcgjGuLU/uBL/ubcZF9OXbDo8ff4O8yVp5Bf0efS8uEoYo5q4Fx7dY9OgQGXgAsQA==" - }, - "commander": { - "version": "6.2.1", - "resolved": "https://registry.npmjs.org/commander/-/commander-6.2.1.tgz", - "integrity": "sha512-U7VdrJFnJgo4xjrHpTzu0yrHPGImdsmD95ZlgYSEajAn2JKzDhDTPG9kBTefmObL2w/ngeZnilk+OV9CG3d7UA==" - }, - "denque": { - "version": "1.5.1", - "resolved": "https://registry.npmjs.org/denque/-/denque-1.5.1.tgz", - "integrity": "sha512-XwE+iZ4D6ZUB7mfYRMb5wByE8L74HCn30FBN7sWnXksWc1LO1bPDl67pBR9o/kC4z/xSNAwkMYcGgqDV3BE3Hw==" - }, - "emoji-regex": { - "version": "8.0.0", - "resolved": "https://registry.npmjs.org/emoji-regex/-/emoji-regex-8.0.0.tgz", - "integrity": "sha512-MSjYzcWNOA0ewAHpz0MxpYFvwg6yjy1NG3xteoqz644VCo/RPgnr1/GGt+ic3iJTzQ8Eu3TdM14SawnVUmGE6A==" - }, - "fast-deep-equal": { - "version": "3.1.3", - "resolved": "https://registry.npmjs.org/fast-deep-equal/-/fast-deep-equal-3.1.3.tgz", - "integrity": "sha512-f3qQ9oQy9j2AhBe/H9VC91wLmKBCCU/gDOnKNAYG5hswO7BLKj09Hc5HYNz9cGI++xlpDCIgDaitVs03ATR84Q==" - }, - "fs-extra": { - "version": "9.1.0", - "resolved": "https://registry.npmjs.org/fs-extra/-/fs-extra-9.1.0.tgz", - "integrity": "sha512-hcg3ZmepS30/7BSFqRvoo3DOMQu7IjqxO5nCDt+zM9XWjb33Wg7ziNT+Qvqbuc3+gWpzO02JubVyk2G4Zvo1OQ==", - "requires": { - "at-least-node": "^1.0.0", - "graceful-fs": "^4.2.0", - "jsonfile": "^6.0.1", - "universalify": "^2.0.0" - } - }, - "graceful-fs": { - "version": "4.2.8", - "resolved": "https://registry.npmjs.org/graceful-fs/-/graceful-fs-4.2.8.tgz", - "integrity": "sha512-qkIilPUYcNhJpd33n0GBXTB1MMPp14TxEsEs0pTrsSVucApsYzW5V+Q8Qxhik6KU3evy+qkAAowTByymK0avdg==" - }, - "is-fullwidth-code-point": { - "version": "3.0.0", - "resolved": "https://registry.npmjs.org/is-fullwidth-code-point/-/is-fullwidth-code-point-3.0.0.tgz", - "integrity": "sha512-zymm5+u+sCsSWyD9qNaejV3DFvhCKclKdizYaJUuHA83RLjb7nSuGnddCHGv0hk+KY7BMAlsWeK4Ueg6EV6XQg==" - }, - "json2csv": { - "version": "5.0.6", - "resolved": "https://registry.npmjs.org/json2csv/-/json2csv-5.0.6.tgz", - "integrity": "sha512-0/4Lv6IenJV0qj2oBdgPIAmFiKKnh8qh7bmLFJ+/ZZHLjSeiL3fKKGX3UryvKPbxFbhV+JcYo9KUC19GJ/Z/4A==", - "requires": { - "commander": "^6.1.0", - "jsonparse": "^1.3.1", - "lodash.get": "^4.4.2" - } - }, - "jsonfile": { - "version": "6.1.0", - "resolved": "https://registry.npmjs.org/jsonfile/-/jsonfile-6.1.0.tgz", - "integrity": "sha512-5dgndWOriYSm5cnYaJNhalLNDKOqFwyDB/rr1E9ZsGciGvKPs8R2xYGCacuf3z6K1YKDz182fd+fY3cn3pMqXQ==", - "requires": { - "graceful-fs": "^4.1.6", - "universalify": "^2.0.0" - } - }, - "jsonparse": { - "version": "1.3.1", - "resolved": "https://registry.npmjs.org/jsonparse/-/jsonparse-1.3.1.tgz", - "integrity": "sha1-P02uSpH6wxX3EGL4UhzCOfE2YoA=" - }, - "kleur": { - "version": "4.1.4", - "resolved": "https://registry.npmjs.org/kleur/-/kleur-4.1.4.tgz", - "integrity": "sha512-8QADVssbrFjivHWQU7KkMgptGTl6WAcSdlbBPY4uNF+mWr6DGcKrvY2w4FQJoXch7+fKMjj0dRrL75vk3k23OA==" - }, - "lodash": { - "version": "4.17.21", - "resolved": "https://registry.npmjs.org/lodash/-/lodash-4.17.21.tgz", - "integrity": "sha512-v2kDEe57lecTulaDIuNTPy3Ry4gLGJ6Z1O3vE1krgXZNrsQ+LFTGHVxVjcXPs17LhbZVGedAJv8XZ1tvj5FvSg==" - }, - "lodash.get": { - "version": "4.4.2", - "resolved": "https://registry.npmjs.org/lodash.get/-/lodash.get-4.4.2.tgz", - "integrity": "sha1-LRd/ZS+jHpObRDjVNBSZ36OCXpk=" - }, - "log-update": { - "version": "4.0.0", - "resolved": "https://registry.npmjs.org/log-update/-/log-update-4.0.0.tgz", - "integrity": "sha512-9fkkDevMefjg0mmzWFBW8YkFP91OrizzkW3diF7CpG+S2EYdy4+TVfGwz1zeF8x7hCx1ovSPTOE9Ngib74qqUg==", - "requires": { - "ansi-escapes": "^4.3.0", - "cli-cursor": "^3.1.0", - "slice-ansi": "^4.0.0", - "wrap-ansi": "^6.2.0" - } - }, - "mimic-fn": { - "version": "2.1.0", - "resolved": "https://registry.npmjs.org/mimic-fn/-/mimic-fn-2.1.0.tgz", - "integrity": "sha512-OqbOk5oEQeAZ8WXWydlu9HJjz9WVdEIvamMCcXmuqUYjTknH/sqsWvhQ3vgwKFRR1HpjvNBKQ37nbJgYzGqGcg==" - }, - "onetime": { - "version": "5.1.2", - "resolved": "https://registry.npmjs.org/onetime/-/onetime-5.1.2.tgz", - "integrity": "sha512-kbpaSSGJTWdAY5KPVeMOKXSrPtr8C8C7wodJbcsd51jRnmD+GZu8Y0VoU6Dm5Z4vWr0Ig/1NKuWRKf7j5aaYSg==", - "requires": { - "mimic-fn": "^2.1.0" - } - }, - "platform": { - "version": "1.3.6", - "resolved": "https://registry.npmjs.org/platform/-/platform-1.3.6.tgz", - "integrity": "sha512-fnWVljUchTro6RiCFvCXBbNhJc2NijN7oIQxbwsyL0buWJPG85v81ehlHI9fXrJsMNgTofEoWIQeClKpgxFLrg==" - }, - "prettier": { - "version": "2.4.1", - "resolved": "https://registry.npmjs.org/prettier/-/prettier-2.4.1.tgz", - "integrity": "sha512-9fbDAXSBcc6Bs1mZrDYb3XKzDLm4EXXL9sC1LqKP5rZkT6KRr/rf9amVUcODVXgguK/isJz0d0hP72WeaKWsvA==" - }, - "redis-commands": { - "version": "1.7.0", - "resolved": "https://registry.npmjs.org/redis-commands/-/redis-commands-1.7.0.tgz", - "integrity": "sha512-nJWqw3bTFy21hX/CPKHth6sfhZbdiHP6bTawSgQBlKOVRG7EZkfHbbHwQJnrE4vsQf0CMNE+3gJ4Fmm16vdVlQ==" - }, - "redis-errors": { - "version": "1.2.0", - "resolved": "https://registry.npmjs.org/redis-errors/-/redis-errors-1.2.0.tgz", - "integrity": "sha1-62LSrbFeTq9GEMBK/hUpOEJQq60=" - }, - "redis-parser": { - "version": "3.0.0", - "resolved": "https://registry.npmjs.org/redis-parser/-/redis-parser-3.0.0.tgz", - "integrity": "sha1-tm2CjNyv5rS4pCin3vTGvKwxyLQ=", - "requires": { - "redis-errors": "^1.0.0" - } - }, - "restore-cursor": { - "version": "3.1.0", - "resolved": "https://registry.npmjs.org/restore-cursor/-/restore-cursor-3.1.0.tgz", - "integrity": "sha512-l+sSefzHpj5qimhFSE5a8nufZYAM3sBSVMAPtYkmC+4EH2anSGaEMXSD0izRQbu9nfyQ9y5JrVmp7E8oZrUjvA==", - "requires": { - "onetime": "^5.1.0", - "signal-exit": "^3.0.2" - } - }, - "signal-exit": { - "version": "3.0.5", - "resolved": "https://registry.npmjs.org/signal-exit/-/signal-exit-3.0.5.tgz", - "integrity": "sha512-KWcOiKeQj6ZyXx7zq4YxSMgHRlod4czeBQZrPb8OKcohcqAXShm7E20kEMle9WBt26hFcAf0qLOcp5zmY7kOqQ==" - }, - "slice-ansi": { - "version": "4.0.0", - "resolved": "https://registry.npmjs.org/slice-ansi/-/slice-ansi-4.0.0.tgz", - "integrity": "sha512-qMCMfhY040cVHT43K9BFygqYbUPFZKHOg7K73mtTWJRb8pyP3fzf4Ixd5SzdEJQ6MRUg/WBnOLxghZtKKurENQ==", - "requires": { - "ansi-styles": "^4.0.0", - "astral-regex": "^2.0.0", - "is-fullwidth-code-point": "^3.0.0" - } - }, - "stats-median": { - "version": "1.0.1", - "resolved": "https://registry.npmjs.org/stats-median/-/stats-median-1.0.1.tgz", - "integrity": "sha512-IYsheLg6dasD3zT/w9+8Iq9tcIQqqu91ZIpJOnIEM25C3X/g4Tl8mhXwW2ZQpbrsJISr9+wizEYgsibN5/b32Q==" - }, - "string-width": { - "version": "4.2.3", - "resolved": "https://registry.npmjs.org/string-width/-/string-width-4.2.3.tgz", - "integrity": "sha512-wKyQRQpjJ0sIp62ErSZdGsjMJWsap5oRNihHhu6G7JVO/9jIB6UyevL+tXuOqrng8j/cxKTWyWUwvSTriiZz/g==", - "requires": { - "emoji-regex": "^8.0.0", - "is-fullwidth-code-point": "^3.0.0", - "strip-ansi": "^6.0.1" - } - }, - "strip-ansi": { - "version": "6.0.1", - "resolved": "https://registry.npmjs.org/strip-ansi/-/strip-ansi-6.0.1.tgz", - "integrity": "sha512-Y38VPSHcqkFrCpFnQ9vuSXmquuv5oXOKpGeT6aGrr3o3Gc9AlVa6JBfUSOCnbxGGZF+/0ooI7KrPuUSztUdU5A==", - "requires": { - "ansi-regex": "^5.0.1" - } - }, - "type-fest": { - "version": "0.21.3", - "resolved": "https://registry.npmjs.org/type-fest/-/type-fest-0.21.3.tgz", - "integrity": "sha512-t0rzBq87m3fVcduHDUFhKmyyX+9eo6WQjZvf51Ea/M0Q7+T374Jp1aUiyUl0GKxp8M/OETVHSDvmkyPgvX+X2w==" - }, - "universalify": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/universalify/-/universalify-2.0.0.tgz", - "integrity": "sha512-hAZsKq7Yy11Zu1DE0OzWjw7nnLZmJZYTDZZyEFHZdUhV8FkH5MCfoU1XMaxXovpyW5nq5scPqq0ZDP9Zyl04oQ==" - }, - "v3": { - "version": "npm:redis@3.1.2", - "resolved": "https://registry.npmjs.org/redis/-/redis-3.1.2.tgz", - "integrity": "sha512-grn5KoZLr/qrRQVwoSkmzdbw6pwF+/rwODtrOr6vuBRiR/f3rjSTGupbF90Zpqm2oenix8Do6RV7pYEkGwlKkw==", - "requires": { - "denque": "^1.5.0", - "redis-commands": "^1.7.0", - "redis-errors": "^1.2.0", - "redis-parser": "^3.0.0" - } - }, - "v4": { - "version": "file:..", - "requires": { - "@istanbuljs/nyc-config-typescript": "^1.0.1", - "@tsconfig/node12": "^1.0.9", - "@types/mocha": "^9.0.0", - "@types/node": "^16.10.3", - "@types/sinon": "^10.0.4", - "@types/which": "^2.0.1", - "@types/yallist": "^4.0.1", - "cluster-key-slot": "1.1.0", - "generic-pool": "3.8.2", - "mocha": "^9.1.2", - "nyc": "^15.1.0", - "redis-parser": "3.0.0", - "release-it": "^14.11.6", - "sinon": "^11.1.2", - "source-map-support": "^0.5.20", - "ts-node": "^10.3.0", - "typedoc": "^0.22.5", - "typedoc-github-wiki-theme": "^0.6.0", - "typedoc-plugin-markdown": "^3.11.3", - "typescript": "^4.4.3", - "which": "^2.0.2", - "yallist": "4.0.0" - } - }, - "wrap-ansi": { - "version": "6.2.0", - "resolved": "https://registry.npmjs.org/wrap-ansi/-/wrap-ansi-6.2.0.tgz", - "integrity": "sha512-r6lPcBGxZXlIcymEu7InxDMhdW0KDxpLgoFLcguasxCaJ/SOIZwINatK9KY/tf+ZrlywOKU0UDj3ATXUBfxJXA==", - "requires": { - "ansi-styles": "^4.0.0", - "string-width": "^4.1.0", - "strip-ansi": "^6.0.0" - } - } - } -} diff --git a/benchmark/package.json b/benchmark/package.json deleted file mode 100644 index ab874090c4b..00000000000 --- a/benchmark/package.json +++ /dev/null @@ -1,17 +0,0 @@ -{ - "name": "benchmark", - "private": true, - "description": "", - "main": "index.js", - "type": "module", - "scripts": { - "start": "node ./" - }, - "author": "", - "license": "ISC", - "dependencies": { - "benny": "3.6.15", - "v3": "npm:redis@3.1.2", - "v4": "file:../" - } -} diff --git a/package-lock.json b/package-lock.json index ff1b2e6e569..87ae980524c 100644 --- a/package-lock.json +++ b/package-lock.json @@ -1,44 +1,15 @@ { - "name": "redis", - "version": "4.0.0-rc.3", + "name": "redis-monorepo", "lockfileVersion": 2, "requires": true, "packages": { "": { - "name": "redis", - "version": "4.0.0-rc.3", - "license": "MIT", - "dependencies": { - "cluster-key-slot": "1.1.0", - "generic-pool": "3.8.2", - "redis-parser": "3.0.0", - "yallist": "4.0.0" - }, + "name": "redis-monorepo", + "workspaces": [ + "./packages/*" + ], "devDependencies": { - "@istanbuljs/nyc-config-typescript": "^1.0.1", - "@tsconfig/node12": "^1.0.9", - "@types/mocha": "^9.0.0", - "@types/node": "^16.11.6", - "@types/sinon": "^10.0.6", - "@types/yallist": "^4.0.1", - "@types/yargs": "^17.0.5", - "@typescript-eslint/eslint-plugin": "^5.2.0", - "@typescript-eslint/parser": "^5.2.0", - "eslint": "^8.1.0", - "mocha": "^9.1.3", - "nyc": "^15.1.0", - "release-it": "^14.11.6", - "sinon": "^11.1.2", - "source-map-support": "^0.5.20", - "ts-node": "^10.4.0", - "typedoc": "^0.22.7", - "typedoc-github-wiki-theme": "^0.6.0", - "typedoc-plugin-markdown": "^3.11.3", - "typescript": "^4.4.4", - "yargs": "^17.2.1" - }, - "engines": { - "node": ">=12" + "@tsconfig/node12": "^1.0.9" } }, "node_modules/@babel/code-frame": { @@ -392,9 +363,9 @@ } }, "node_modules/@babel/parser": { - "version": "7.16.0", - "resolved": "https://registry.npmjs.org/@babel/parser/-/parser-7.16.0.tgz", - "integrity": "sha512-TEHWXf0xxpi9wKVyBCmRcSSDjbJ/cl6LUdlbYUHEaNQUJGhreJbZrXT6sR4+fZLxVUJqNRB4KyOvjuy/D9009A==", + "version": "7.16.2", + "resolved": "https://registry.npmjs.org/@babel/parser/-/parser-7.16.2.tgz", + "integrity": "sha512-RUVpT0G2h6rOZwqLDTrKk7ksNv7YpAilTnYe1/Q+eDjxEceRMKVWbCsX7t8h6C1qCFi/1Y8WZjcEPBAFG27GPw==", "dev": true, "bin": { "parser": "bin/babel-parser.js" @@ -481,9 +452,9 @@ } }, "node_modules/@eslint/eslintrc": { - "version": "1.0.3", - "resolved": "https://registry.npmjs.org/@eslint/eslintrc/-/eslintrc-1.0.3.tgz", - "integrity": "sha512-DHI1wDPoKCBPoLZA3qDR91+3te/wDSc1YhKg3jR8NxKKRJq2hwHwcWv31cSwSYvIBrmbENoYMWcenW8uproQqg==", + "version": "1.0.4", + "resolved": "https://registry.npmjs.org/@eslint/eslintrc/-/eslintrc-1.0.4.tgz", + "integrity": "sha512-h8Vx6MdxwWI2WM8/zREHMoqdgLNXEL4QX3MWSVMdyNJGvXVOs+6lp+m2hc3FnuMHDc4poxFNI20vCk0OmI4G0Q==", "dev": true, "dependencies": { "ajv": "^6.12.4", @@ -492,7 +463,7 @@ "globals": "^13.9.0", "ignore": "^4.0.6", "import-fresh": "^3.2.1", - "js-yaml": "^3.13.1", + "js-yaml": "^4.1.0", "minimatch": "^3.0.4", "strip-json-comments": "^3.1.1" }, @@ -500,15 +471,6 @@ "node": "^12.22.0 || ^14.17.0 || >=16.0.0" } }, - "node_modules/@eslint/eslintrc/node_modules/argparse": { - "version": "1.0.10", - "resolved": "https://registry.npmjs.org/argparse/-/argparse-1.0.10.tgz", - "integrity": "sha512-o5Roy6tNG4SL/FOkCAN6RzjiakZS25RLYFrcMttJqbdd8BWrnA+fGz57iN5Pb06pvBGvl5gQ0B48dJlslXvoTg==", - "dev": true, - "dependencies": { - "sprintf-js": "~1.0.2" - } - }, "node_modules/@eslint/eslintrc/node_modules/ignore": { "version": "4.0.6", "resolved": "https://registry.npmjs.org/ignore/-/ignore-4.0.6.tgz", @@ -518,19 +480,6 @@ "node": ">= 4" } }, - "node_modules/@eslint/eslintrc/node_modules/js-yaml": { - "version": "3.14.1", - "resolved": "https://registry.npmjs.org/js-yaml/-/js-yaml-3.14.1.tgz", - "integrity": "sha512-okMH7OXXJ7YrN9Ok3/SXrnu4iX9yOk+25nqX4imS2npuvTYDmo/QEZoqwZkYaIDk3jVvBOTOIEgEhaLOynBS9g==", - "dev": true, - "dependencies": { - "argparse": "^1.0.7", - "esprima": "^4.0.0" - }, - "bin": { - "js-yaml": "bin/js-yaml.js" - } - }, "node_modules/@humanwhocodes/config-array": { "version": "0.6.0", "resolved": "https://registry.npmjs.org/@humanwhocodes/config-array/-/config-array-0.6.0.tgz", @@ -546,9 +495,9 @@ } }, "node_modules/@humanwhocodes/object-schema": { - "version": "1.2.0", - "resolved": "https://registry.npmjs.org/@humanwhocodes/object-schema/-/object-schema-1.2.0.tgz", - "integrity": "sha512-wdppn25U8z/2yiaT6YGquE6X8sSv7hNMWSXYSSU1jGv/yd6XqjXgTDJ8KP4NgjTXfJ3GbRjeeb8RTV7a/VpM+w==", + "version": "1.2.1", + "resolved": "https://registry.npmjs.org/@humanwhocodes/object-schema/-/object-schema-1.2.1.tgz", + "integrity": "sha512-ZnQMnLV4e7hDlUvw8H+U8ASL02SS2Gn6+9Ac3wGGLIe7+je2AeAOxPY+izIPJDfFDb7eDjev0Us8MO1iFRN8hA==", "dev": true }, "node_modules/@iarna/toml": { @@ -849,6 +798,26 @@ "@octokit/openapi-types": "^11.2.0" } }, + "node_modules/@redis/client": { + "resolved": "packages/client", + "link": true + }, + "node_modules/@redis/json": { + "resolved": "packages/json", + "link": true + }, + "node_modules/@redis/search": { + "resolved": "packages/search", + "link": true + }, + "node_modules/@redis/test-utils": { + "resolved": "packages/test-utils", + "link": true + }, + "node_modules/@redis/time-series": { + "resolved": "packages/time-series", + "link": true + }, "node_modules/@sindresorhus/is": { "version": "4.2.0", "resolved": "https://registry.npmjs.org/@sindresorhus/is/-/is-4.2.0.tgz", @@ -972,9 +941,9 @@ "dev": true }, "node_modules/@types/node": { - "version": "16.11.6", - "resolved": "https://registry.npmjs.org/@types/node/-/node-16.11.6.tgz", - "integrity": "sha512-ua7PgUoeQFjmWPcoo9khiPum3Pd60k4/2ZGXt18sm2Slk0W0xZTqt5Y0Ny1NyBiN1EVQ/+FaF9NcY4Qe6rwk5w==", + "version": "16.11.7", + "resolved": "https://registry.npmjs.org/@types/node/-/node-16.11.7.tgz", + "integrity": "sha512-QB5D2sqfSjCmTuWcBWyJ+/44bcjO7VbjSbOE0ucoVbAsSNQc4Lt6QkgkVXkTDwkL4z/beecZNDvVX15D4P8Jbw==", "dev": true }, "node_modules/@types/parse-json": { @@ -1023,13 +992,13 @@ "dev": true }, "node_modules/@typescript-eslint/eslint-plugin": { - "version": "5.2.0", - "resolved": "https://registry.npmjs.org/@typescript-eslint/eslint-plugin/-/eslint-plugin-5.2.0.tgz", - "integrity": "sha512-qQwg7sqYkBF4CIQSyRQyqsYvP+g/J0To9ZPVNJpfxfekl5RmdvQnFFTVVwpRtaUDFNvjfe/34TgY/dpc3MgNTw==", + "version": "5.3.1", + "resolved": "https://registry.npmjs.org/@typescript-eslint/eslint-plugin/-/eslint-plugin-5.3.1.tgz", + "integrity": "sha512-cFImaoIr5Ojj358xI/SDhjog57OK2NqlpxwdcgyxDA3bJlZcJq5CPzUXtpD7CxI2Hm6ATU7w5fQnnkVnmwpHqw==", "dev": true, "dependencies": { - "@typescript-eslint/experimental-utils": "5.2.0", - "@typescript-eslint/scope-manager": "5.2.0", + "@typescript-eslint/experimental-utils": "5.3.1", + "@typescript-eslint/scope-manager": "5.3.1", "debug": "^4.3.2", "functional-red-black-tree": "^1.0.1", "ignore": "^5.1.8", @@ -1055,15 +1024,15 @@ } }, "node_modules/@typescript-eslint/experimental-utils": { - "version": "5.2.0", - "resolved": "https://registry.npmjs.org/@typescript-eslint/experimental-utils/-/experimental-utils-5.2.0.tgz", - "integrity": "sha512-fWyT3Agf7n7HuZZRpvUYdFYbPk3iDCq6fgu3ulia4c7yxmPnwVBovdSOX7RL+k8u6hLbrXcdAehlWUVpGh6IEw==", + "version": "5.3.1", + "resolved": "https://registry.npmjs.org/@typescript-eslint/experimental-utils/-/experimental-utils-5.3.1.tgz", + "integrity": "sha512-RgFn5asjZ5daUhbK5Sp0peq0SSMytqcrkNfU4pnDma2D8P3ElZ6JbYjY8IMSFfZAJ0f3x3tnO3vXHweYg0g59w==", "dev": true, "dependencies": { "@types/json-schema": "^7.0.9", - "@typescript-eslint/scope-manager": "5.2.0", - "@typescript-eslint/types": "5.2.0", - "@typescript-eslint/typescript-estree": "5.2.0", + "@typescript-eslint/scope-manager": "5.3.1", + "@typescript-eslint/types": "5.3.1", + "@typescript-eslint/typescript-estree": "5.3.1", "eslint-scope": "^5.1.1", "eslint-utils": "^3.0.0" }, @@ -1079,14 +1048,14 @@ } }, "node_modules/@typescript-eslint/parser": { - "version": "5.2.0", - "resolved": "https://registry.npmjs.org/@typescript-eslint/parser/-/parser-5.2.0.tgz", - "integrity": "sha512-Uyy4TjJBlh3NuA8/4yIQptyJb95Qz5PX//6p8n7zG0QnN4o3NF9Je3JHbVU7fxf5ncSXTmnvMtd/LDQWDk0YqA==", + "version": "5.3.1", + "resolved": "https://registry.npmjs.org/@typescript-eslint/parser/-/parser-5.3.1.tgz", + "integrity": "sha512-TD+ONlx5c+Qhk21x9gsJAMRohWAUMavSOmJgv3JGy9dgPhuBd5Wok0lmMClZDyJNLLZK1JRKiATzCKZNUmoyfw==", "dev": true, "dependencies": { - "@typescript-eslint/scope-manager": "5.2.0", - "@typescript-eslint/types": "5.2.0", - "@typescript-eslint/typescript-estree": "5.2.0", + "@typescript-eslint/scope-manager": "5.3.1", + "@typescript-eslint/types": "5.3.1", + "@typescript-eslint/typescript-estree": "5.3.1", "debug": "^4.3.2" }, "engines": { @@ -1106,13 +1075,13 @@ } }, "node_modules/@typescript-eslint/scope-manager": { - "version": "5.2.0", - "resolved": "https://registry.npmjs.org/@typescript-eslint/scope-manager/-/scope-manager-5.2.0.tgz", - "integrity": "sha512-RW+wowZqPzQw8MUFltfKYZfKXqA2qgyi6oi/31J1zfXJRpOn6tCaZtd9b5u9ubnDG2n/EMvQLeZrsLNPpaUiFQ==", + "version": "5.3.1", + "resolved": "https://registry.npmjs.org/@typescript-eslint/scope-manager/-/scope-manager-5.3.1.tgz", + "integrity": "sha512-XksFVBgAq0Y9H40BDbuPOTUIp7dn4u8oOuhcgGq7EoDP50eqcafkMVGrypyVGvDYHzjhdUCUwuwVUK4JhkMAMg==", "dev": true, "dependencies": { - "@typescript-eslint/types": "5.2.0", - "@typescript-eslint/visitor-keys": "5.2.0" + "@typescript-eslint/types": "5.3.1", + "@typescript-eslint/visitor-keys": "5.3.1" }, "engines": { "node": "^12.22.0 || ^14.17.0 || >=16.0.0" @@ -1123,9 +1092,9 @@ } }, "node_modules/@typescript-eslint/types": { - "version": "5.2.0", - "resolved": "https://registry.npmjs.org/@typescript-eslint/types/-/types-5.2.0.tgz", - "integrity": "sha512-cTk6x08qqosps6sPyP2j7NxyFPlCNsJwSDasqPNjEQ8JMD5xxj2NHxcLin5AJQ8pAVwpQ8BMI3bTxR0zxmK9qQ==", + "version": "5.3.1", + "resolved": "https://registry.npmjs.org/@typescript-eslint/types/-/types-5.3.1.tgz", + "integrity": "sha512-bG7HeBLolxKHtdHG54Uac750eXuQQPpdJfCYuw4ZI3bZ7+GgKClMWM8jExBtp7NSP4m8PmLRM8+lhzkYnSmSxQ==", "dev": true, "engines": { "node": "^12.22.0 || ^14.17.0 || >=16.0.0" @@ -1136,13 +1105,13 @@ } }, "node_modules/@typescript-eslint/typescript-estree": { - "version": "5.2.0", - "resolved": "https://registry.npmjs.org/@typescript-eslint/typescript-estree/-/typescript-estree-5.2.0.tgz", - "integrity": "sha512-RsdXq2XmVgKbm9nLsE3mjNUM7BTr/K4DYR9WfFVMUuozHWtH5gMpiNZmtrMG8GR385EOSQ3kC9HiEMJWimxd/g==", + "version": "5.3.1", + "resolved": "https://registry.npmjs.org/@typescript-eslint/typescript-estree/-/typescript-estree-5.3.1.tgz", + "integrity": "sha512-PwFbh/PKDVo/Wct6N3w+E4rLZxUDgsoII/GrWM2A62ETOzJd4M6s0Mu7w4CWsZraTbaC5UQI+dLeyOIFF1PquQ==", "dev": true, "dependencies": { - "@typescript-eslint/types": "5.2.0", - "@typescript-eslint/visitor-keys": "5.2.0", + "@typescript-eslint/types": "5.3.1", + "@typescript-eslint/visitor-keys": "5.3.1", "debug": "^4.3.2", "globby": "^11.0.4", "is-glob": "^4.0.3", @@ -1163,12 +1132,12 @@ } }, "node_modules/@typescript-eslint/visitor-keys": { - "version": "5.2.0", - "resolved": "https://registry.npmjs.org/@typescript-eslint/visitor-keys/-/visitor-keys-5.2.0.tgz", - "integrity": "sha512-Nk7HizaXWWCUBfLA/rPNKMzXzWS8Wg9qHMuGtT+v2/YpPij4nVXrVJc24N/r5WrrmqK31jCrZxeHqIgqRzs0Xg==", + "version": "5.3.1", + "resolved": "https://registry.npmjs.org/@typescript-eslint/visitor-keys/-/visitor-keys-5.3.1.tgz", + "integrity": "sha512-3cHUzUuVTuNHx0Gjjt5pEHa87+lzyqOiHXy/Gz+SJOCW1mpw9xQHIIEwnKn+Thph1mgWyZ90nboOcSuZr/jTTQ==", "dev": true, "dependencies": { - "@typescript-eslint/types": "5.2.0", + "@typescript-eslint/types": "5.3.1", "eslint-visitor-keys": "^3.0.0" }, "engines": { @@ -1495,13 +1464,13 @@ "dev": true }, "node_modules/browserslist": { - "version": "4.17.5", - "resolved": "https://registry.npmjs.org/browserslist/-/browserslist-4.17.5.tgz", - "integrity": "sha512-I3ekeB92mmpctWBoLXe0d5wPS2cBuRvvW0JyyJHMrk9/HmP2ZjrTboNAZ8iuGqaEIlKguljbQY32OkOJIRrgoA==", + "version": "4.17.6", + "resolved": "https://registry.npmjs.org/browserslist/-/browserslist-4.17.6.tgz", + "integrity": "sha512-uPgz3vyRTlEiCv4ee9KlsKgo2V6qPk7Jsn0KAn2OBqbqKo3iNcPEC1Ti6J4dwnz+aIRfEEEuOzC9IBk8tXUomw==", "dev": true, "dependencies": { - "caniuse-lite": "^1.0.30001271", - "electron-to-chromium": "^1.3.878", + "caniuse-lite": "^1.0.30001274", + "electron-to-chromium": "^1.3.886", "escalade": "^3.1.1", "node-releases": "^2.0.1", "picocolors": "^1.0.0" @@ -1636,9 +1605,9 @@ } }, "node_modules/caniuse-lite": { - "version": "1.0.30001274", - "resolved": "https://registry.npmjs.org/caniuse-lite/-/caniuse-lite-1.0.30001274.tgz", - "integrity": "sha512-+Nkvv0fHyhISkiMIjnyjmf5YJcQ1IQHZN6U9TLUMroWR38FNwpsC51Gb68yueafX1V6ifOisInSgP9WJFS13ew==", + "version": "1.0.30001278", + "resolved": "https://registry.npmjs.org/caniuse-lite/-/caniuse-lite-1.0.30001278.tgz", + "integrity": "sha512-mpF9KeH8u5cMoEmIic/cr7PNS+F5LWBk0t2ekGT60lFf0Wq+n9LspAj0g3P+o7DQhD3sUdlMln4YFAWhFYn9jg==", "dev": true, "funding": { "type": "opencollective", @@ -2097,9 +2066,9 @@ "dev": true }, "node_modules/electron-to-chromium": { - "version": "1.3.885", - "resolved": "https://registry.npmjs.org/electron-to-chromium/-/electron-to-chromium-1.3.885.tgz", - "integrity": "sha512-JXKFJcVWrdHa09n4CNZYfYaK6EW5aAew7/wr3L1OnsD1L+JHL+RCtd7QgIsxUbFPeTwPlvnpqNNTOLkoefmtXg==", + "version": "1.3.891", + "resolved": "https://registry.npmjs.org/electron-to-chromium/-/electron-to-chromium-1.3.891.tgz", + "integrity": "sha512-3cpwR82QkIS01CN/dup/4Yr3BiOiRLlZlcAFn/5FbNCunMO9ojqDgEP9JEo1QNLflu3pEnPWve50gHOEKc7r6w==", "dev": true }, "node_modules/emoji-regex": { @@ -2175,12 +2144,12 @@ } }, "node_modules/eslint": { - "version": "8.1.0", - "resolved": "https://registry.npmjs.org/eslint/-/eslint-8.1.0.tgz", - "integrity": "sha512-JZvNneArGSUsluHWJ8g8MMs3CfIEzwaLx9KyH4tZ2i+R2/rPWzL8c0zg3rHdwYVpN/1sB9gqnjHwz9HoeJpGHw==", + "version": "8.2.0", + "resolved": "https://registry.npmjs.org/eslint/-/eslint-8.2.0.tgz", + "integrity": "sha512-erw7XmM+CLxTOickrimJ1SiF55jiNlVSp2qqm0NuBWPtHYQCegD5ZMaW0c3i5ytPqL+SSLaCxdvQXFPLJn+ABw==", "dev": true, "dependencies": { - "@eslint/eslintrc": "^1.0.3", + "@eslint/eslintrc": "^1.0.4", "@humanwhocodes/config-array": "^0.6.0", "ajv": "^6.10.0", "chalk": "^4.0.0", @@ -2214,7 +2183,7 @@ "progress": "^2.0.0", "regexpp": "^3.2.0", "semver": "^7.2.1", - "strip-ansi": "^6.0.0", + "strip-ansi": "^6.0.1", "strip-json-comments": "^3.1.0", "text-table": "^0.2.0", "v8-compile-cache": "^2.0.3" @@ -2270,9 +2239,9 @@ } }, "node_modules/eslint-visitor-keys": { - "version": "3.0.0", - "resolved": "https://registry.npmjs.org/eslint-visitor-keys/-/eslint-visitor-keys-3.0.0.tgz", - "integrity": "sha512-mJOZa35trBTb3IyRmo8xmKBZlxf+N7OnUl4+ZhJHs/r+0770Wh/LEACE2pqMGMe27G/4y8P2bYGk4J70IC5k1Q==", + "version": "3.1.0", + "resolved": "https://registry.npmjs.org/eslint-visitor-keys/-/eslint-visitor-keys-3.1.0.tgz", + "integrity": "sha512-yWJFpu4DtjsWKkt5GeNBBuZMlNcYVs6vRCLoCVEJrTjaSB6LC98gFipNK/erM2Heg/E8mIK+hXG/pJMLK+eRZA==", "dev": true, "engines": { "node": "^12.22.0 || ^14.17.0 || >=16.0.0" @@ -3060,9 +3029,9 @@ ] }, "node_modules/ignore": { - "version": "5.1.8", - "resolved": "https://registry.npmjs.org/ignore/-/ignore-5.1.8.tgz", - "integrity": "sha512-BMpfD7PpiETpBl/A6S498BaIJ6Y/ABT93ETbby2fP00v4EbvPBXWEoaR1UBPKs3iR53pJY7EtZk5KACI57i1Uw==", + "version": "5.1.9", + "resolved": "https://registry.npmjs.org/ignore/-/ignore-5.1.9.tgz", + "integrity": "sha512-2zeMQpbKz5dhZ9IwL0gbxSW5w0NK/MSAMtNuhgIHEPmaU3vPdKPL0UdvUCXs5SS4JAwsBxysK5sFMW8ocFiVjQ==", "dev": true, "engines": { "node": ">= 4" @@ -5096,6 +5065,10 @@ "node": ">= 0.10" } }, + "node_modules/redis": { + "resolved": "packages/all-in-one", + "link": true + }, "node_modules/redis-errors": { "version": "1.2.0", "resolved": "https://registry.npmjs.org/redis-errors/-/redis-errors-1.2.0.tgz", @@ -5883,9 +5856,9 @@ } }, "node_modules/typedoc": { - "version": "0.22.7", - "resolved": "https://registry.npmjs.org/typedoc/-/typedoc-0.22.7.tgz", - "integrity": "sha512-ndxxp+tU1Wczvdxp4u2/PvT1qjD6hdFdSdehpORHjE+JXmMkl2bftXCR0upHmsnesBG7VCcr8vfgloGHIH8glQ==", + "version": "0.22.8", + "resolved": "https://registry.npmjs.org/typedoc/-/typedoc-0.22.8.tgz", + "integrity": "sha512-92S+YzyhospdXN5rnkYUTgirdTYqNWY7NP9vco+IqQQoiSXzVSUsawVro+tMyEEsWUS7EMaJ2YOjB9uE0CBi6A==", "dev": true, "dependencies": { "glob": "^7.2.0", @@ -5960,9 +5933,9 @@ } }, "node_modules/uglify-js": { - "version": "3.14.2", - "resolved": "https://registry.npmjs.org/uglify-js/-/uglify-js-3.14.2.tgz", - "integrity": "sha512-rtPMlmcO4agTUfz10CbgJ1k6UAoXM2gWb3GoMPPZB/+/Ackf8lNWk11K4rYi2D0apgoFRLtQOZhb+/iGNJq26A==", + "version": "3.14.3", + "resolved": "https://registry.npmjs.org/uglify-js/-/uglify-js-3.14.3.tgz", + "integrity": "sha512-mic3aOdiq01DuSVx0TseaEzMIVqebMZ0Z3vaeDhFEh9bsc24hV1TFvN74reA2vs08D0ZWfNjAcJ3UbVLaBss+g==", "dev": true, "optional": true, "bin": { @@ -6385,6 +6358,124 @@ "funding": { "url": "https://github.com/sponsors/sindresorhus" } + }, + "packages/all-in-one": { + "name": "redis", + "version": "4.0.0-rc.3", + "license": "MIT", + "dependencies": { + "@redis/client": "^4.0.0-rc", + "@redis/json": "^1.0.0-rc", + "@redis/search": "^1.0.0-rc" + }, + "devDependencies": { + "release-it": "^14.11.6", + "typescript": "^4.4.4" + } + }, + "packages/client": { + "name": "@redis/client", + "version": "4.0.0-rc.3", + "license": "MIT", + "dependencies": { + "cluster-key-slot": "1.1.0", + "generic-pool": "3.8.2", + "redis-parser": "3.0.0", + "yallist": "4.0.0" + }, + "devDependencies": { + "@istanbuljs/nyc-config-typescript": "^1.0.1", + "@redis/test-utils": "*", + "@types/node": "^16.11.6", + "@types/sinon": "^10.0.6", + "@types/yallist": "^4.0.1", + "@typescript-eslint/eslint-plugin": "^5.2.0", + "@typescript-eslint/parser": "^5.2.0", + "eslint": "^8.1.0", + "nyc": "^15.1.0", + "release-it": "^14.11.6", + "sinon": "^11.1.2", + "source-map-support": "^0.5.20", + "ts-node": "^10.4.0", + "typedoc": "^0.22.7", + "typedoc-github-wiki-theme": "^0.6.0", + "typedoc-plugin-markdown": "^3.11.3", + "typescript": "^4.4.4" + }, + "engines": { + "node": ">=12" + } + }, + "packages/json": { + "name": "@redis/json", + "version": "1.0.0-rc.0", + "license": "MIT", + "devDependencies": { + "@istanbuljs/nyc-config-typescript": "^1.0.1", + "@redis/test-utils": "*", + "@types/node": "^16.11.6", + "nyc": "^15.1.0", + "release-it": "^14.11.6", + "source-map-support": "^0.5.20", + "ts-node": "^10.4.0", + "typescript": "^4.4.4" + }, + "peerDependencies": { + "@redis/client": "^4.0.0-rc" + } + }, + "packages/search": { + "name": "@redis/search", + "version": "1.0.0-rc.0", + "license": "MIT", + "devDependencies": { + "@istanbuljs/nyc-config-typescript": "^1.0.1", + "@redis/test-utils": "*", + "@types/node": "^16.11.6", + "nyc": "^15.1.0", + "release-it": "^14.11.6", + "source-map-support": "^0.5.20", + "ts-node": "^10.4.0", + "typescript": "^4.4.4" + }, + "peerDependencies": { + "@redis/client": "^4.0.0-rc" + } + }, + "packages/test-utils": { + "name": "@redis/test-utils", + "devDependencies": { + "@istanbuljs/nyc-config-typescript": "^1.0.1", + "@tsconfig/node12": "^1.0.9", + "@types/mocha": "^9.0.0", + "@types/node": "^16.11.6", + "@types/yargs": "^17.0.5", + "mocha": "^9.1.3", + "nyc": "^15.1.0", + "release-it": "^14.11.6", + "source-map-support": "^0.5.20", + "ts-node": "^10.4.0", + "typescript": "^4.4.4", + "yargs": "^17.2.1" + }, + "peerDependencies": { + "@redis/client": "^4.0.0-rc" + } + }, + "packages/time-series": { + "name": "@redis/time-series", + "version": "1.0.0", + "license": "ISC", + "devDependencies": { + "@istanbuljs/nyc-config-typescript": "^1.0.1", + "@redis/test-utils": "*", + "@types/node": "^16.11.6", + "nyc": "^15.1.0", + "release-it": "^14.11.6", + "source-map-support": "^0.5.20", + "ts-node": "^10.4.0", + "typescript": "^4.4.4" + } } }, "dependencies": { @@ -6660,9 +6751,9 @@ } }, "@babel/parser": { - "version": "7.16.0", - "resolved": "https://registry.npmjs.org/@babel/parser/-/parser-7.16.0.tgz", - "integrity": "sha512-TEHWXf0xxpi9wKVyBCmRcSSDjbJ/cl6LUdlbYUHEaNQUJGhreJbZrXT6sR4+fZLxVUJqNRB4KyOvjuy/D9009A==", + "version": "7.16.2", + "resolved": "https://registry.npmjs.org/@babel/parser/-/parser-7.16.2.tgz", + "integrity": "sha512-RUVpT0G2h6rOZwqLDTrKk7ksNv7YpAilTnYe1/Q+eDjxEceRMKVWbCsX7t8h6C1qCFi/1Y8WZjcEPBAFG27GPw==", "dev": true }, "@babel/template": { @@ -6727,9 +6818,9 @@ } }, "@eslint/eslintrc": { - "version": "1.0.3", - "resolved": "https://registry.npmjs.org/@eslint/eslintrc/-/eslintrc-1.0.3.tgz", - "integrity": "sha512-DHI1wDPoKCBPoLZA3qDR91+3te/wDSc1YhKg3jR8NxKKRJq2hwHwcWv31cSwSYvIBrmbENoYMWcenW8uproQqg==", + "version": "1.0.4", + "resolved": "https://registry.npmjs.org/@eslint/eslintrc/-/eslintrc-1.0.4.tgz", + "integrity": "sha512-h8Vx6MdxwWI2WM8/zREHMoqdgLNXEL4QX3MWSVMdyNJGvXVOs+6lp+m2hc3FnuMHDc4poxFNI20vCk0OmI4G0Q==", "dev": true, "requires": { "ajv": "^6.12.4", @@ -6738,35 +6829,16 @@ "globals": "^13.9.0", "ignore": "^4.0.6", "import-fresh": "^3.2.1", - "js-yaml": "^3.13.1", + "js-yaml": "^4.1.0", "minimatch": "^3.0.4", "strip-json-comments": "^3.1.1" }, "dependencies": { - "argparse": { - "version": "1.0.10", - "resolved": "https://registry.npmjs.org/argparse/-/argparse-1.0.10.tgz", - "integrity": "sha512-o5Roy6tNG4SL/FOkCAN6RzjiakZS25RLYFrcMttJqbdd8BWrnA+fGz57iN5Pb06pvBGvl5gQ0B48dJlslXvoTg==", - "dev": true, - "requires": { - "sprintf-js": "~1.0.2" - } - }, "ignore": { "version": "4.0.6", "resolved": "https://registry.npmjs.org/ignore/-/ignore-4.0.6.tgz", "integrity": "sha512-cyFDKrqc/YdcWFniJhzI42+AzS+gNwmUzOSFcRCQYwySuBBBy/KjuxWLZ/FHEH6Moq1NizMOBWyTcv8O4OZIMg==", "dev": true - }, - "js-yaml": { - "version": "3.14.1", - "resolved": "https://registry.npmjs.org/js-yaml/-/js-yaml-3.14.1.tgz", - "integrity": "sha512-okMH7OXXJ7YrN9Ok3/SXrnu4iX9yOk+25nqX4imS2npuvTYDmo/QEZoqwZkYaIDk3jVvBOTOIEgEhaLOynBS9g==", - "dev": true, - "requires": { - "argparse": "^1.0.7", - "esprima": "^4.0.0" - } } } }, @@ -6782,9 +6854,9 @@ } }, "@humanwhocodes/object-schema": { - "version": "1.2.0", - "resolved": "https://registry.npmjs.org/@humanwhocodes/object-schema/-/object-schema-1.2.0.tgz", - "integrity": "sha512-wdppn25U8z/2yiaT6YGquE6X8sSv7hNMWSXYSSU1jGv/yd6XqjXgTDJ8KP4NgjTXfJ3GbRjeeb8RTV7a/VpM+w==", + "version": "1.2.1", + "resolved": "https://registry.npmjs.org/@humanwhocodes/object-schema/-/object-schema-1.2.1.tgz", + "integrity": "sha512-ZnQMnLV4e7hDlUvw8H+U8ASL02SS2Gn6+9Ac3wGGLIe7+je2AeAOxPY+izIPJDfFDb7eDjev0Us8MO1iFRN8hA==", "dev": true }, "@iarna/toml": { @@ -7035,6 +7107,88 @@ "@octokit/openapi-types": "^11.2.0" } }, + "@redis/client": { + "version": "file:packages/client", + "requires": { + "@istanbuljs/nyc-config-typescript": "^1.0.1", + "@redis/test-utils": "*", + "@types/node": "^16.11.6", + "@types/sinon": "^10.0.6", + "@types/yallist": "^4.0.1", + "@typescript-eslint/eslint-plugin": "^5.2.0", + "@typescript-eslint/parser": "^5.2.0", + "cluster-key-slot": "1.1.0", + "eslint": "^8.1.0", + "generic-pool": "3.8.2", + "nyc": "^15.1.0", + "redis-parser": "3.0.0", + "release-it": "^14.11.6", + "sinon": "^11.1.2", + "source-map-support": "^0.5.20", + "ts-node": "^10.4.0", + "typedoc": "^0.22.7", + "typedoc-github-wiki-theme": "^0.6.0", + "typedoc-plugin-markdown": "^3.11.3", + "typescript": "^4.4.4", + "yallist": "4.0.0" + } + }, + "@redis/json": { + "version": "file:packages/json", + "requires": { + "@istanbuljs/nyc-config-typescript": "^1.0.1", + "@redis/test-utils": "*", + "@types/node": "^16.11.6", + "nyc": "^15.1.0", + "release-it": "^14.11.6", + "source-map-support": "^0.5.20", + "ts-node": "^10.4.0", + "typescript": "^4.4.4" + } + }, + "@redis/search": { + "version": "file:packages/search", + "requires": { + "@istanbuljs/nyc-config-typescript": "^1.0.1", + "@redis/test-utils": "*", + "@types/node": "^16.11.6", + "nyc": "^15.1.0", + "release-it": "^14.11.6", + "source-map-support": "^0.5.20", + "ts-node": "^10.4.0", + "typescript": "^4.4.4" + } + }, + "@redis/test-utils": { + "version": "file:packages/test-utils", + "requires": { + "@istanbuljs/nyc-config-typescript": "^1.0.1", + "@tsconfig/node12": "^1.0.9", + "@types/mocha": "^9.0.0", + "@types/node": "^16.11.6", + "@types/yargs": "^17.0.5", + "mocha": "^9.1.3", + "nyc": "^15.1.0", + "release-it": "^14.11.6", + "source-map-support": "^0.5.20", + "ts-node": "^10.4.0", + "typescript": "^4.4.4", + "yargs": "^17.2.1" + } + }, + "@redis/time-series": { + "version": "file:packages/time-series", + "requires": { + "@istanbuljs/nyc-config-typescript": "^1.0.1", + "@redis/test-utils": "*", + "@types/node": "^16.11.6", + "nyc": "^15.1.0", + "release-it": "^14.11.6", + "source-map-support": "^0.5.20", + "ts-node": "^10.4.0", + "typescript": "^4.4.4" + } + }, "@sindresorhus/is": { "version": "4.2.0", "resolved": "https://registry.npmjs.org/@sindresorhus/is/-/is-4.2.0.tgz", @@ -7149,9 +7303,9 @@ "dev": true }, "@types/node": { - "version": "16.11.6", - "resolved": "https://registry.npmjs.org/@types/node/-/node-16.11.6.tgz", - "integrity": "sha512-ua7PgUoeQFjmWPcoo9khiPum3Pd60k4/2ZGXt18sm2Slk0W0xZTqt5Y0Ny1NyBiN1EVQ/+FaF9NcY4Qe6rwk5w==", + "version": "16.11.7", + "resolved": "https://registry.npmjs.org/@types/node/-/node-16.11.7.tgz", + "integrity": "sha512-QB5D2sqfSjCmTuWcBWyJ+/44bcjO7VbjSbOE0ucoVbAsSNQc4Lt6QkgkVXkTDwkL4z/beecZNDvVX15D4P8Jbw==", "dev": true }, "@types/parse-json": { @@ -7200,13 +7354,13 @@ "dev": true }, "@typescript-eslint/eslint-plugin": { - "version": "5.2.0", - "resolved": "https://registry.npmjs.org/@typescript-eslint/eslint-plugin/-/eslint-plugin-5.2.0.tgz", - "integrity": "sha512-qQwg7sqYkBF4CIQSyRQyqsYvP+g/J0To9ZPVNJpfxfekl5RmdvQnFFTVVwpRtaUDFNvjfe/34TgY/dpc3MgNTw==", + "version": "5.3.1", + "resolved": "https://registry.npmjs.org/@typescript-eslint/eslint-plugin/-/eslint-plugin-5.3.1.tgz", + "integrity": "sha512-cFImaoIr5Ojj358xI/SDhjog57OK2NqlpxwdcgyxDA3bJlZcJq5CPzUXtpD7CxI2Hm6ATU7w5fQnnkVnmwpHqw==", "dev": true, "requires": { - "@typescript-eslint/experimental-utils": "5.2.0", - "@typescript-eslint/scope-manager": "5.2.0", + "@typescript-eslint/experimental-utils": "5.3.1", + "@typescript-eslint/scope-manager": "5.3.1", "debug": "^4.3.2", "functional-red-black-tree": "^1.0.1", "ignore": "^5.1.8", @@ -7216,55 +7370,55 @@ } }, "@typescript-eslint/experimental-utils": { - "version": "5.2.0", - "resolved": "https://registry.npmjs.org/@typescript-eslint/experimental-utils/-/experimental-utils-5.2.0.tgz", - "integrity": "sha512-fWyT3Agf7n7HuZZRpvUYdFYbPk3iDCq6fgu3ulia4c7yxmPnwVBovdSOX7RL+k8u6hLbrXcdAehlWUVpGh6IEw==", + "version": "5.3.1", + "resolved": "https://registry.npmjs.org/@typescript-eslint/experimental-utils/-/experimental-utils-5.3.1.tgz", + "integrity": "sha512-RgFn5asjZ5daUhbK5Sp0peq0SSMytqcrkNfU4pnDma2D8P3ElZ6JbYjY8IMSFfZAJ0f3x3tnO3vXHweYg0g59w==", "dev": true, "requires": { "@types/json-schema": "^7.0.9", - "@typescript-eslint/scope-manager": "5.2.0", - "@typescript-eslint/types": "5.2.0", - "@typescript-eslint/typescript-estree": "5.2.0", + "@typescript-eslint/scope-manager": "5.3.1", + "@typescript-eslint/types": "5.3.1", + "@typescript-eslint/typescript-estree": "5.3.1", "eslint-scope": "^5.1.1", "eslint-utils": "^3.0.0" } }, "@typescript-eslint/parser": { - "version": "5.2.0", - "resolved": "https://registry.npmjs.org/@typescript-eslint/parser/-/parser-5.2.0.tgz", - "integrity": "sha512-Uyy4TjJBlh3NuA8/4yIQptyJb95Qz5PX//6p8n7zG0QnN4o3NF9Je3JHbVU7fxf5ncSXTmnvMtd/LDQWDk0YqA==", + "version": "5.3.1", + "resolved": "https://registry.npmjs.org/@typescript-eslint/parser/-/parser-5.3.1.tgz", + "integrity": "sha512-TD+ONlx5c+Qhk21x9gsJAMRohWAUMavSOmJgv3JGy9dgPhuBd5Wok0lmMClZDyJNLLZK1JRKiATzCKZNUmoyfw==", "dev": true, "requires": { - "@typescript-eslint/scope-manager": "5.2.0", - "@typescript-eslint/types": "5.2.0", - "@typescript-eslint/typescript-estree": "5.2.0", + "@typescript-eslint/scope-manager": "5.3.1", + "@typescript-eslint/types": "5.3.1", + "@typescript-eslint/typescript-estree": "5.3.1", "debug": "^4.3.2" } }, "@typescript-eslint/scope-manager": { - "version": "5.2.0", - "resolved": "https://registry.npmjs.org/@typescript-eslint/scope-manager/-/scope-manager-5.2.0.tgz", - "integrity": "sha512-RW+wowZqPzQw8MUFltfKYZfKXqA2qgyi6oi/31J1zfXJRpOn6tCaZtd9b5u9ubnDG2n/EMvQLeZrsLNPpaUiFQ==", + "version": "5.3.1", + "resolved": "https://registry.npmjs.org/@typescript-eslint/scope-manager/-/scope-manager-5.3.1.tgz", + "integrity": "sha512-XksFVBgAq0Y9H40BDbuPOTUIp7dn4u8oOuhcgGq7EoDP50eqcafkMVGrypyVGvDYHzjhdUCUwuwVUK4JhkMAMg==", "dev": true, "requires": { - "@typescript-eslint/types": "5.2.0", - "@typescript-eslint/visitor-keys": "5.2.0" + "@typescript-eslint/types": "5.3.1", + "@typescript-eslint/visitor-keys": "5.3.1" } }, "@typescript-eslint/types": { - "version": "5.2.0", - "resolved": "https://registry.npmjs.org/@typescript-eslint/types/-/types-5.2.0.tgz", - "integrity": "sha512-cTk6x08qqosps6sPyP2j7NxyFPlCNsJwSDasqPNjEQ8JMD5xxj2NHxcLin5AJQ8pAVwpQ8BMI3bTxR0zxmK9qQ==", + "version": "5.3.1", + "resolved": "https://registry.npmjs.org/@typescript-eslint/types/-/types-5.3.1.tgz", + "integrity": "sha512-bG7HeBLolxKHtdHG54Uac750eXuQQPpdJfCYuw4ZI3bZ7+GgKClMWM8jExBtp7NSP4m8PmLRM8+lhzkYnSmSxQ==", "dev": true }, "@typescript-eslint/typescript-estree": { - "version": "5.2.0", - "resolved": "https://registry.npmjs.org/@typescript-eslint/typescript-estree/-/typescript-estree-5.2.0.tgz", - "integrity": "sha512-RsdXq2XmVgKbm9nLsE3mjNUM7BTr/K4DYR9WfFVMUuozHWtH5gMpiNZmtrMG8GR385EOSQ3kC9HiEMJWimxd/g==", + "version": "5.3.1", + "resolved": "https://registry.npmjs.org/@typescript-eslint/typescript-estree/-/typescript-estree-5.3.1.tgz", + "integrity": "sha512-PwFbh/PKDVo/Wct6N3w+E4rLZxUDgsoII/GrWM2A62ETOzJd4M6s0Mu7w4CWsZraTbaC5UQI+dLeyOIFF1PquQ==", "dev": true, "requires": { - "@typescript-eslint/types": "5.2.0", - "@typescript-eslint/visitor-keys": "5.2.0", + "@typescript-eslint/types": "5.3.1", + "@typescript-eslint/visitor-keys": "5.3.1", "debug": "^4.3.2", "globby": "^11.0.4", "is-glob": "^4.0.3", @@ -7273,12 +7427,12 @@ } }, "@typescript-eslint/visitor-keys": { - "version": "5.2.0", - "resolved": "https://registry.npmjs.org/@typescript-eslint/visitor-keys/-/visitor-keys-5.2.0.tgz", - "integrity": "sha512-Nk7HizaXWWCUBfLA/rPNKMzXzWS8Wg9qHMuGtT+v2/YpPij4nVXrVJc24N/r5WrrmqK31jCrZxeHqIgqRzs0Xg==", + "version": "5.3.1", + "resolved": "https://registry.npmjs.org/@typescript-eslint/visitor-keys/-/visitor-keys-5.3.1.tgz", + "integrity": "sha512-3cHUzUuVTuNHx0Gjjt5pEHa87+lzyqOiHXy/Gz+SJOCW1mpw9xQHIIEwnKn+Thph1mgWyZ90nboOcSuZr/jTTQ==", "dev": true, "requires": { - "@typescript-eslint/types": "5.2.0", + "@typescript-eslint/types": "5.3.1", "eslint-visitor-keys": "^3.0.0" } }, @@ -7519,13 +7673,13 @@ "dev": true }, "browserslist": { - "version": "4.17.5", - "resolved": "https://registry.npmjs.org/browserslist/-/browserslist-4.17.5.tgz", - "integrity": "sha512-I3ekeB92mmpctWBoLXe0d5wPS2cBuRvvW0JyyJHMrk9/HmP2ZjrTboNAZ8iuGqaEIlKguljbQY32OkOJIRrgoA==", + "version": "4.17.6", + "resolved": "https://registry.npmjs.org/browserslist/-/browserslist-4.17.6.tgz", + "integrity": "sha512-uPgz3vyRTlEiCv4ee9KlsKgo2V6qPk7Jsn0KAn2OBqbqKo3iNcPEC1Ti6J4dwnz+aIRfEEEuOzC9IBk8tXUomw==", "dev": true, "requires": { - "caniuse-lite": "^1.0.30001271", - "electron-to-chromium": "^1.3.878", + "caniuse-lite": "^1.0.30001274", + "electron-to-chromium": "^1.3.886", "escalade": "^3.1.1", "node-releases": "^2.0.1", "picocolors": "^1.0.0" @@ -7614,9 +7768,9 @@ "dev": true }, "caniuse-lite": { - "version": "1.0.30001274", - "resolved": "https://registry.npmjs.org/caniuse-lite/-/caniuse-lite-1.0.30001274.tgz", - "integrity": "sha512-+Nkvv0fHyhISkiMIjnyjmf5YJcQ1IQHZN6U9TLUMroWR38FNwpsC51Gb68yueafX1V6ifOisInSgP9WJFS13ew==", + "version": "1.0.30001278", + "resolved": "https://registry.npmjs.org/caniuse-lite/-/caniuse-lite-1.0.30001278.tgz", + "integrity": "sha512-mpF9KeH8u5cMoEmIic/cr7PNS+F5LWBk0t2ekGT60lFf0Wq+n9LspAj0g3P+o7DQhD3sUdlMln4YFAWhFYn9jg==", "dev": true }, "chalk": { @@ -7963,9 +8117,9 @@ "dev": true }, "electron-to-chromium": { - "version": "1.3.885", - "resolved": "https://registry.npmjs.org/electron-to-chromium/-/electron-to-chromium-1.3.885.tgz", - "integrity": "sha512-JXKFJcVWrdHa09n4CNZYfYaK6EW5aAew7/wr3L1OnsD1L+JHL+RCtd7QgIsxUbFPeTwPlvnpqNNTOLkoefmtXg==", + "version": "1.3.891", + "resolved": "https://registry.npmjs.org/electron-to-chromium/-/electron-to-chromium-1.3.891.tgz", + "integrity": "sha512-3cpwR82QkIS01CN/dup/4Yr3BiOiRLlZlcAFn/5FbNCunMO9ojqDgEP9JEo1QNLflu3pEnPWve50gHOEKc7r6w==", "dev": true }, "emoji-regex": { @@ -8026,12 +8180,12 @@ "dev": true }, "eslint": { - "version": "8.1.0", - "resolved": "https://registry.npmjs.org/eslint/-/eslint-8.1.0.tgz", - "integrity": "sha512-JZvNneArGSUsluHWJ8g8MMs3CfIEzwaLx9KyH4tZ2i+R2/rPWzL8c0zg3rHdwYVpN/1sB9gqnjHwz9HoeJpGHw==", + "version": "8.2.0", + "resolved": "https://registry.npmjs.org/eslint/-/eslint-8.2.0.tgz", + "integrity": "sha512-erw7XmM+CLxTOickrimJ1SiF55jiNlVSp2qqm0NuBWPtHYQCegD5ZMaW0c3i5ytPqL+SSLaCxdvQXFPLJn+ABw==", "dev": true, "requires": { - "@eslint/eslintrc": "^1.0.3", + "@eslint/eslintrc": "^1.0.4", "@humanwhocodes/config-array": "^0.6.0", "ajv": "^6.10.0", "chalk": "^4.0.0", @@ -8065,7 +8219,7 @@ "progress": "^2.0.0", "regexpp": "^3.2.0", "semver": "^7.2.1", - "strip-ansi": "^6.0.0", + "strip-ansi": "^6.0.1", "strip-json-comments": "^3.1.0", "text-table": "^0.2.0", "v8-compile-cache": "^2.0.3" @@ -8123,9 +8277,9 @@ } }, "eslint-visitor-keys": { - "version": "3.0.0", - "resolved": "https://registry.npmjs.org/eslint-visitor-keys/-/eslint-visitor-keys-3.0.0.tgz", - "integrity": "sha512-mJOZa35trBTb3IyRmo8xmKBZlxf+N7OnUl4+ZhJHs/r+0770Wh/LEACE2pqMGMe27G/4y8P2bYGk4J70IC5k1Q==", + "version": "3.1.0", + "resolved": "https://registry.npmjs.org/eslint-visitor-keys/-/eslint-visitor-keys-3.1.0.tgz", + "integrity": "sha512-yWJFpu4DtjsWKkt5GeNBBuZMlNcYVs6vRCLoCVEJrTjaSB6LC98gFipNK/erM2Heg/E8mIK+hXG/pJMLK+eRZA==", "dev": true }, "espree": { @@ -8667,9 +8821,9 @@ "dev": true }, "ignore": { - "version": "5.1.8", - "resolved": "https://registry.npmjs.org/ignore/-/ignore-5.1.8.tgz", - "integrity": "sha512-BMpfD7PpiETpBl/A6S498BaIJ6Y/ABT93ETbby2fP00v4EbvPBXWEoaR1UBPKs3iR53pJY7EtZk5KACI57i1Uw==", + "version": "5.1.9", + "resolved": "https://registry.npmjs.org/ignore/-/ignore-5.1.9.tgz", + "integrity": "sha512-2zeMQpbKz5dhZ9IwL0gbxSW5w0NK/MSAMtNuhgIHEPmaU3vPdKPL0UdvUCXs5SS4JAwsBxysK5sFMW8ocFiVjQ==", "dev": true }, "import-cwd": { @@ -10220,6 +10374,16 @@ "resolve": "^1.1.6" } }, + "redis": { + "version": "file:packages/all-in-one", + "requires": { + "@redis/client": "^4.0.0-rc", + "@redis/json": "^1.0.0-rc", + "@redis/search": "^1.0.0-rc", + "release-it": "^14.11.6", + "typescript": "^4.4.4" + } + }, "redis-errors": { "version": "1.2.0", "resolved": "https://registry.npmjs.org/redis-errors/-/redis-errors-1.2.0.tgz", @@ -10803,9 +10967,9 @@ } }, "typedoc": { - "version": "0.22.7", - "resolved": "https://registry.npmjs.org/typedoc/-/typedoc-0.22.7.tgz", - "integrity": "sha512-ndxxp+tU1Wczvdxp4u2/PvT1qjD6hdFdSdehpORHjE+JXmMkl2bftXCR0upHmsnesBG7VCcr8vfgloGHIH8glQ==", + "version": "0.22.8", + "resolved": "https://registry.npmjs.org/typedoc/-/typedoc-0.22.8.tgz", + "integrity": "sha512-92S+YzyhospdXN5rnkYUTgirdTYqNWY7NP9vco+IqQQoiSXzVSUsawVro+tMyEEsWUS7EMaJ2YOjB9uE0CBi6A==", "dev": true, "requires": { "glob": "^7.2.0", @@ -10854,9 +11018,9 @@ "dev": true }, "uglify-js": { - "version": "3.14.2", - "resolved": "https://registry.npmjs.org/uglify-js/-/uglify-js-3.14.2.tgz", - "integrity": "sha512-rtPMlmcO4agTUfz10CbgJ1k6UAoXM2gWb3GoMPPZB/+/Ackf8lNWk11K4rYi2D0apgoFRLtQOZhb+/iGNJq26A==", + "version": "3.14.3", + "resolved": "https://registry.npmjs.org/uglify-js/-/uglify-js-3.14.3.tgz", + "integrity": "sha512-mic3aOdiq01DuSVx0TseaEzMIVqebMZ0Z3vaeDhFEh9bsc24hV1TFvN74reA2vs08D0ZWfNjAcJ3UbVLaBss+g==", "dev": true, "optional": true }, diff --git a/package.json b/package.json index f252e4a0fa3..e7de3fc8674 100644 --- a/package.json +++ b/package.json @@ -1,60 +1,19 @@ { - "name": "redis", - "version": "4.0.0-rc.3", - "description": "A high performance Redis client.", - "keywords": [ - "database", - "redis", - "pubsub" + "name": "redis-monorepo", + "private": true, + "workspaces": [ + "./packages/*" ], - "author": "Matt Ranney ", - "license": "MIT", - "main": "./dist/index.js", - "types": "./dist/index.d.ts", "scripts": { - "test": "nyc -r text-summary -r html mocha -r source-map-support/register -r ts-node/register './lib/**/*.spec.ts'", - "build": "tsc", - "lint": "eslint ./*.ts ./lib/**/*.ts", - "documentation": "typedoc" - }, - "dependencies": { - "cluster-key-slot": "1.1.0", - "generic-pool": "3.8.2", - "redis-parser": "3.0.0", - "yallist": "4.0.0" + "test": "npm run test -ws --if-present", + "build:client": "npm run build -w ./packages/client", + "build:test-utils": "npm run build -w ./packages/test-utils", + "build:tests-tools": "npm run build:client && npm run build:test-utils", + "build:modules": "find ./packages -mindepth 1 -maxdepth 1 -type d ! -name 'client' ! -name 'test-utils' ! -name 'all-in-one' -exec npm run build -w {} \\;", + "build:all-in-one": "npm run build -w ./packages/all-in-one", + "build": "npm run build:client && npm run build:test-utils && npm run build:modules && npm run build:all-in-one" }, "devDependencies": { - "@istanbuljs/nyc-config-typescript": "^1.0.1", - "@tsconfig/node12": "^1.0.9", - "@types/mocha": "^9.0.0", - "@types/node": "^16.11.6", - "@types/sinon": "^10.0.6", - "@types/yallist": "^4.0.1", - "@types/yargs": "^17.0.5", - "@typescript-eslint/eslint-plugin": "^5.2.0", - "@typescript-eslint/parser": "^5.2.0", - "eslint": "^8.1.0", - "mocha": "^9.1.3", - "nyc": "^15.1.0", - "release-it": "^14.11.6", - "sinon": "^11.1.2", - "source-map-support": "^0.5.20", - "ts-node": "^10.4.0", - "typedoc": "^0.22.7", - "typedoc-github-wiki-theme": "^0.6.0", - "typedoc-plugin-markdown": "^3.11.3", - "typescript": "^4.4.4", - "yargs": "^17.2.1" - }, - "engines": { - "node": ">=12" - }, - "repository": { - "type": "git", - "url": "git://github.com/redis/node-redis.git" - }, - "bugs": { - "url": "https://github.com/redis/node-redis/issues" - }, - "homepage": "https://github.com/redis/node-redis" + "@tsconfig/node12": "^1.0.9" + } } diff --git a/packages/all-in-one/index.ts b/packages/all-in-one/index.ts new file mode 100644 index 00000000000..b0cd5c1f8f7 --- /dev/null +++ b/packages/all-in-one/index.ts @@ -0,0 +1,19 @@ +import { createClient as _createClient } from '@redis/client'; +import { RedisScripts } from '@redis/client/dist/lib/commands'; +import { RedisClientOptions, RedisClientType } from '@redis/client/dist/lib/client'; +import RedisJSON from '@redis/json'; +import RediSearch from '@redis/search'; + +const modules = { + json: RedisJSON, + ft: RediSearch +}; + +export function createClient>( + options?: Omit, 'modules'> +): RedisClientType { + return _createClient({ + ...options, + modules + }); +} diff --git a/packages/all-in-one/package.json b/packages/all-in-one/package.json new file mode 100644 index 00000000000..8fdb7149717 --- /dev/null +++ b/packages/all-in-one/package.json @@ -0,0 +1,27 @@ +{ + "name": "redis", + "version": "4.0.0-rc.3", + "license": "MIT", + "main": "./dist/index.js", + "types": "./dist/index.ts", + "scripts": { + "build": "tsc" + }, + "dependencies": { + "@redis/client": "^4.0.0-rc", + "@redis/json": "^1.0.0-rc", + "@redis/search": "^1.0.0-rc" + }, + "devDependencies": { + "release-it": "^14.11.6", + "typescript": "^4.4.4" + }, + "repository": { + "type": "git", + "url": "git://github.com/redis/node-redis.git" + }, + "bugs": { + "url": "https://github.com/redis/node-redis/issues" + }, + "homepage": "https://github.com/redis/node-redis" +} diff --git a/packages/all-in-one/tsconfig.json b/packages/all-in-one/tsconfig.json new file mode 100644 index 00000000000..103760b874b --- /dev/null +++ b/packages/all-in-one/tsconfig.json @@ -0,0 +1,9 @@ +{ + "extends": "../../tsconfig", + "compilerOptions": { + "outDir": "./dist" + }, + "include": [ + "./index.ts" + ] + } diff --git a/.eslintrc.json b/packages/client/.eslintrc.json similarity index 100% rename from .eslintrc.json rename to packages/client/.eslintrc.json diff --git a/packages/client/.gitignore b/packages/client/.gitignore new file mode 100644 index 00000000000..2d7ddbc106e --- /dev/null +++ b/packages/client/.gitignore @@ -0,0 +1 @@ +documentation/ diff --git a/packages/client/.npmignore b/packages/client/.npmignore new file mode 100644 index 00000000000..b7310e15767 --- /dev/null +++ b/packages/client/.npmignore @@ -0,0 +1,9 @@ +.nyc_output/ +coverage/ +documentation/ +examples/ +lib/ +.nycrc.json +dump.rdb +index.ts +tsconfig.json diff --git a/packages/client/.nycrc.json b/packages/client/.nycrc.json new file mode 100644 index 00000000000..b4e671e178f --- /dev/null +++ b/packages/client/.nycrc.json @@ -0,0 +1,4 @@ +{ + "extends": "@istanbuljs/nyc-config-typescript", + "exclude": ["**/*.spec.ts", "lib/test-utils.ts"] +} diff --git a/CHANGELOG.md b/packages/client/CHANGELOG.md similarity index 100% rename from CHANGELOG.md rename to packages/client/CHANGELOG.md diff --git a/packages/client/LICENSE b/packages/client/LICENSE new file mode 100644 index 00000000000..db86cc4de7f --- /dev/null +++ b/packages/client/LICENSE @@ -0,0 +1,24 @@ +MIT License + +Copyright (c) 2016-present Node Redis contributors. + +Permission is hereby granted, free of charge, to any person +obtaining a copy of this software and associated documentation +files (the "Software"), to deal in the Software without +restriction, including without limitation the rights to use, +copy, modify, merge, publish, distribute, sublicense, and/or sell +copies of the Software, and to permit persons to whom the +Software is furnished to do so, subject to the following +conditions: + +The above copyright notice and this permission notice shall be +included in all copies or substantial portions of the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, +EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES +OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND +NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT +HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, +WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING +FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR +OTHER DEALINGS IN THE SOFTWARE. diff --git a/packages/client/README.md b/packages/client/README.md new file mode 100644 index 00000000000..813f9830efd --- /dev/null +++ b/packages/client/README.md @@ -0,0 +1,294 @@ +

+ + + +

Node Redis

+

+ + + +--- + +## 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 { + 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. diff --git a/docs/FAQ.md b/packages/client/docs/FAQ.md similarity index 100% rename from docs/FAQ.md rename to packages/client/docs/FAQ.md diff --git a/docs/client-configuration.md b/packages/client/docs/client-configuration.md similarity index 98% rename from docs/client-configuration.md rename to packages/client/docs/client-configuration.md index 11fdb0a6819..3b6b7dcad73 100644 --- a/docs/client-configuration.md +++ b/packages/client/docs/client-configuration.md @@ -15,7 +15,7 @@ | 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](https://redis.io/modules) to include (TODO - document) | +| 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)) | | commandsQueueMaxLength | | Maximum length of the client's internal command queue | | readonly | `false` | Connect in [`READONLY`](https://redis.io/commands/readonly) mode | diff --git a/docs/clustering.md b/packages/client/docs/clustering.md similarity index 82% rename from docs/clustering.md rename to packages/client/docs/clustering.md index a84dc3b1aa4..3b5ef94a5c7 100644 --- a/docs/clustering.md +++ b/packages/client/docs/clustering.md @@ -37,7 +37,9 @@ import { createCluster } from 'redis'; | rootNodes | | An array of root nodes that are part of the cluster, which will be used to get the cluster topology. Each element in the array is a client configuration object. There is no need to specify every node in the cluster, 3 should be enough to reliably connect and obtain the cluster configuration from the server | | defaults | | The default configuration values for every client in the cluster. Use this for example when specifying an ACL user to connect with | | useReplicas | `false` | When `true`, distribute load by executing readonly commands (such as `GET`, `GEOSEARCH`, etc.) across all cluster nodes. When `false`, only use master nodes | -| maxCommandRedirections | `16` | The maximum number of times a command will be redirected due to `MOVED` or `ASK` errors | | +| 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 | +| scripts | | Object defining Lua Scripts to use with this client (see [Lua Scripts](../README.md#lua-scripts)) | ## Command Routing diff --git a/docs/isolated-execution.md b/packages/client/docs/isolated-execution.md similarity index 100% rename from docs/isolated-execution.md rename to packages/client/docs/isolated-execution.md diff --git a/docs/v3-to-v4.md b/packages/client/docs/v3-to-v4.md similarity index 100% rename from docs/v3-to-v4.md rename to packages/client/docs/v3-to-v4.md diff --git a/examples/README.md b/packages/client/examples/README.md similarity index 100% rename from examples/README.md rename to packages/client/examples/README.md diff --git a/examples/blocking-list-pop.js b/packages/client/examples/blocking-list-pop.js similarity index 100% rename from examples/blocking-list-pop.js rename to packages/client/examples/blocking-list-pop.js diff --git a/examples/command-with-modifiers.js b/packages/client/examples/command-with-modifiers.js similarity index 100% rename from examples/command-with-modifiers.js rename to packages/client/examples/command-with-modifiers.js diff --git a/examples/connect-as-acl-user.js b/packages/client/examples/connect-as-acl-user.js similarity index 100% rename from examples/connect-as-acl-user.js rename to packages/client/examples/connect-as-acl-user.js diff --git a/examples/lua-multi-incr.js b/packages/client/examples/lua-multi-incr.js similarity index 100% rename from examples/lua-multi-incr.js rename to packages/client/examples/lua-multi-incr.js diff --git a/examples/package-lock.json b/packages/client/examples/package-lock.json similarity index 100% rename from examples/package-lock.json rename to packages/client/examples/package-lock.json diff --git a/examples/package.json b/packages/client/examples/package.json similarity index 64% rename from examples/package.json rename to packages/client/examples/package.json index 4963094b0ba..edb8cdacdb2 100644 --- a/examples/package.json +++ b/packages/client/examples/package.json @@ -4,11 +4,6 @@ "description": "node-redis 4 example script", "main": "index.js", "private": true, - "scripts": { - }, - "type": "module", - "dependencies": { - "redis": "../" - } + "type": "module" } diff --git a/examples/set-scan.js b/packages/client/examples/set-scan.js similarity index 100% rename from examples/set-scan.js rename to packages/client/examples/set-scan.js diff --git a/index.ts b/packages/client/index.ts similarity index 100% rename from index.ts rename to packages/client/index.ts diff --git a/lib/client/commands-queue.ts b/packages/client/lib/client/commands-queue.ts similarity index 100% rename from lib/client/commands-queue.ts rename to packages/client/lib/client/commands-queue.ts diff --git a/lib/client/commands.ts b/packages/client/lib/client/commands.ts similarity index 100% rename from lib/client/commands.ts rename to packages/client/lib/client/commands.ts diff --git a/lib/client/index.spec.ts b/packages/client/lib/client/index.spec.ts similarity index 98% rename from lib/client/index.spec.ts rename to packages/client/lib/client/index.spec.ts index 51dded18b1f..21abe8a25f1 100644 --- a/lib/client/index.spec.ts +++ b/packages/client/lib/client/index.spec.ts @@ -444,16 +444,16 @@ describe('Client', () => { } }); - testUtils.testWithClient('executeIsolated', async client => { - await client.sendCommand(['CLIENT', 'SETNAME', 'client']); - - assert.equal( - await client.executeIsolated(isolatedClient => - isolatedClient.sendCommand(['CLIENT', 'GETNAME']) - ), - null - ); - }, GLOBAL.SERVERS.OPEN); + // testUtils.testWithClient('executeIsolated', async client => { + // await client.sendCommand(['CLIENT', 'SETNAME', 'client']); + + // assert.equal( + // await client.executeIsolated(isolatedClient => + // isolatedClient.sendCommand(['CLIENT', 'GETNAME']) + // ), + // null + // ); + // }, GLOBAL.SERVERS.OPEN); async function killClient(client: RedisClientType): Promise { const onceErrorPromise = once(client, 'error'); diff --git a/lib/client/index.ts b/packages/client/lib/client/index.ts similarity index 100% rename from lib/client/index.ts rename to packages/client/lib/client/index.ts diff --git a/lib/client/multi-command.ts b/packages/client/lib/client/multi-command.ts similarity index 100% rename from lib/client/multi-command.ts rename to packages/client/lib/client/multi-command.ts diff --git a/lib/client/socket.spec.ts b/packages/client/lib/client/socket.spec.ts similarity index 100% rename from lib/client/socket.spec.ts rename to packages/client/lib/client/socket.spec.ts diff --git a/lib/client/socket.ts b/packages/client/lib/client/socket.ts similarity index 100% rename from lib/client/socket.ts rename to packages/client/lib/client/socket.ts diff --git a/lib/cluster/cluster-slots.ts b/packages/client/lib/cluster/cluster-slots.ts similarity index 100% rename from lib/cluster/cluster-slots.ts rename to packages/client/lib/cluster/cluster-slots.ts diff --git a/lib/cluster/commands.ts b/packages/client/lib/cluster/commands.ts similarity index 100% rename from lib/cluster/commands.ts rename to packages/client/lib/cluster/commands.ts diff --git a/lib/cluster/index.spec.ts b/packages/client/lib/cluster/index.spec.ts similarity index 51% rename from lib/cluster/index.spec.ts rename to packages/client/lib/cluster/index.spec.ts index 66319460f63..43492a6500f 100644 --- a/lib/cluster/index.spec.ts +++ b/packages/client/lib/cluster/index.spec.ts @@ -47,47 +47,47 @@ describe('Cluster', () => { } }); - testUtils.testWithCluster('should handle live resharding', async cluster => { - const key = 'key', - value = 'value'; - await cluster.set(key, value); + // testUtils.testWithCluster('should handle live resharding', async cluster => { + // const key = 'key', + // value = 'value'; + // await cluster.set(key, value); - const slot = calculateSlot(key), - from = cluster.getSlotMaster(slot), - to = cluster.getMasters().find(node => node.id !== from.id); + // const slot = calculateSlot(key), + // from = cluster.getSlotMaster(slot), + // to = cluster.getMasters().find(node => node.id !== from.id); - await to!.client.clusterSetSlot(slot, ClusterSlotStates.IMPORTING, from.id); + // await to!.client.clusterSetSlot(slot, ClusterSlotStates.IMPORTING, from.id); - // should be able to get the key from the original node before it was migrated - assert.equal( - await cluster.get(key), - value - ); + // // should be able to get the key from the original node before it was migrated + // assert.equal( + // await cluster.get(key), + // value + // ); - await from.client.clusterSetSlot(slot, ClusterSlotStates.MIGRATING, to!.id); + // await from.client.clusterSetSlot(slot, ClusterSlotStates.MIGRATING, to!.id); - // should be able to get the key from the original node using the "ASKING" command - assert.equal( - await cluster.get(key), - value - ); + // // should be able to get the key from the original node using the "ASKING" command + // assert.equal( + // await cluster.get(key), + // value + // ); - const { port: toPort } = to!.client.options!.socket; + // const { port: toPort } = to!.client.options!.socket; - await from.client.migrate( - '127.0.0.1', - toPort, - key, - 0, - 10 - ); + // await from.client.migrate( + // '127.0.0.1', + // toPort, + // key, + // 0, + // 10 + // ); - // should be able to get the key from the new node - assert.equal( - await cluster.get(key), - value - ); - }, { - serverArguments: [] - }); + // // should be able to get the key from the new node + // assert.equal( + // await cluster.get(key), + // value + // ); + // }, { + // serverArguments: [] + // }); }); diff --git a/lib/cluster/index.ts b/packages/client/lib/cluster/index.ts similarity index 100% rename from lib/cluster/index.ts rename to packages/client/lib/cluster/index.ts diff --git a/lib/cluster/multi-command.ts b/packages/client/lib/cluster/multi-command.ts similarity index 100% rename from lib/cluster/multi-command.ts rename to packages/client/lib/cluster/multi-command.ts diff --git a/lib/command-options.ts b/packages/client/lib/command-options.ts similarity index 100% rename from lib/command-options.ts rename to packages/client/lib/command-options.ts diff --git a/lib/commander.spec.ts b/packages/client/lib/commander.spec.ts similarity index 100% rename from lib/commander.spec.ts rename to packages/client/lib/commander.spec.ts diff --git a/lib/commander.ts b/packages/client/lib/commander.ts similarity index 100% rename from lib/commander.ts rename to packages/client/lib/commander.ts diff --git a/lib/commands/ACL_CAT.spec.ts b/packages/client/lib/commands/ACL_CAT.spec.ts similarity index 100% rename from lib/commands/ACL_CAT.spec.ts rename to packages/client/lib/commands/ACL_CAT.spec.ts diff --git a/lib/commands/ACL_CAT.ts b/packages/client/lib/commands/ACL_CAT.ts similarity index 100% rename from lib/commands/ACL_CAT.ts rename to packages/client/lib/commands/ACL_CAT.ts diff --git a/lib/commands/ACL_DELUSER.spec.ts b/packages/client/lib/commands/ACL_DELUSER.spec.ts similarity index 100% rename from lib/commands/ACL_DELUSER.spec.ts rename to packages/client/lib/commands/ACL_DELUSER.spec.ts diff --git a/lib/commands/ACL_DELUSER.ts b/packages/client/lib/commands/ACL_DELUSER.ts similarity index 100% rename from lib/commands/ACL_DELUSER.ts rename to packages/client/lib/commands/ACL_DELUSER.ts diff --git a/lib/commands/ACL_GENPASS.spec.ts b/packages/client/lib/commands/ACL_GENPASS.spec.ts similarity index 100% rename from lib/commands/ACL_GENPASS.spec.ts rename to packages/client/lib/commands/ACL_GENPASS.spec.ts diff --git a/lib/commands/ACL_GENPASS.ts b/packages/client/lib/commands/ACL_GENPASS.ts similarity index 100% rename from lib/commands/ACL_GENPASS.ts rename to packages/client/lib/commands/ACL_GENPASS.ts diff --git a/lib/commands/ACL_GETUSER.spec.ts b/packages/client/lib/commands/ACL_GETUSER.spec.ts similarity index 100% rename from lib/commands/ACL_GETUSER.spec.ts rename to packages/client/lib/commands/ACL_GETUSER.spec.ts diff --git a/lib/commands/ACL_GETUSER.ts b/packages/client/lib/commands/ACL_GETUSER.ts similarity index 100% rename from lib/commands/ACL_GETUSER.ts rename to packages/client/lib/commands/ACL_GETUSER.ts diff --git a/lib/commands/ACL_LIST.spec.ts b/packages/client/lib/commands/ACL_LIST.spec.ts similarity index 100% rename from lib/commands/ACL_LIST.spec.ts rename to packages/client/lib/commands/ACL_LIST.spec.ts diff --git a/lib/commands/ACL_LIST.ts b/packages/client/lib/commands/ACL_LIST.ts similarity index 100% rename from lib/commands/ACL_LIST.ts rename to packages/client/lib/commands/ACL_LIST.ts diff --git a/lib/commands/ACL_LOAD.spec.ts b/packages/client/lib/commands/ACL_LOAD.spec.ts similarity index 100% rename from lib/commands/ACL_LOAD.spec.ts rename to packages/client/lib/commands/ACL_LOAD.spec.ts diff --git a/lib/commands/ACL_LOAD.ts b/packages/client/lib/commands/ACL_LOAD.ts similarity index 100% rename from lib/commands/ACL_LOAD.ts rename to packages/client/lib/commands/ACL_LOAD.ts diff --git a/lib/commands/ACL_LOG.spec.ts b/packages/client/lib/commands/ACL_LOG.spec.ts similarity index 100% rename from lib/commands/ACL_LOG.spec.ts rename to packages/client/lib/commands/ACL_LOG.spec.ts diff --git a/lib/commands/ACL_LOG.ts b/packages/client/lib/commands/ACL_LOG.ts similarity index 100% rename from lib/commands/ACL_LOG.ts rename to packages/client/lib/commands/ACL_LOG.ts diff --git a/lib/commands/ACL_LOG_RESET.spec.ts b/packages/client/lib/commands/ACL_LOG_RESET.spec.ts similarity index 100% rename from lib/commands/ACL_LOG_RESET.spec.ts rename to packages/client/lib/commands/ACL_LOG_RESET.spec.ts diff --git a/lib/commands/ACL_LOG_RESET.ts b/packages/client/lib/commands/ACL_LOG_RESET.ts similarity index 100% rename from lib/commands/ACL_LOG_RESET.ts rename to packages/client/lib/commands/ACL_LOG_RESET.ts diff --git a/lib/commands/ACL_SAVE.spec.ts b/packages/client/lib/commands/ACL_SAVE.spec.ts similarity index 100% rename from lib/commands/ACL_SAVE.spec.ts rename to packages/client/lib/commands/ACL_SAVE.spec.ts diff --git a/lib/commands/ACL_SAVE.ts b/packages/client/lib/commands/ACL_SAVE.ts similarity index 100% rename from lib/commands/ACL_SAVE.ts rename to packages/client/lib/commands/ACL_SAVE.ts diff --git a/lib/commands/ACL_SETUSER.spec.ts b/packages/client/lib/commands/ACL_SETUSER.spec.ts similarity index 100% rename from lib/commands/ACL_SETUSER.spec.ts rename to packages/client/lib/commands/ACL_SETUSER.spec.ts diff --git a/lib/commands/ACL_SETUSER.ts b/packages/client/lib/commands/ACL_SETUSER.ts similarity index 100% rename from lib/commands/ACL_SETUSER.ts rename to packages/client/lib/commands/ACL_SETUSER.ts diff --git a/lib/commands/ACL_USERS.spec.ts b/packages/client/lib/commands/ACL_USERS.spec.ts similarity index 100% rename from lib/commands/ACL_USERS.spec.ts rename to packages/client/lib/commands/ACL_USERS.spec.ts diff --git a/lib/commands/ACL_USERS.ts b/packages/client/lib/commands/ACL_USERS.ts similarity index 100% rename from lib/commands/ACL_USERS.ts rename to packages/client/lib/commands/ACL_USERS.ts diff --git a/lib/commands/ACL_WHOAMI.spec.ts b/packages/client/lib/commands/ACL_WHOAMI.spec.ts similarity index 100% rename from lib/commands/ACL_WHOAMI.spec.ts rename to packages/client/lib/commands/ACL_WHOAMI.spec.ts diff --git a/lib/commands/ACL_WHOAMI.ts b/packages/client/lib/commands/ACL_WHOAMI.ts similarity index 100% rename from lib/commands/ACL_WHOAMI.ts rename to packages/client/lib/commands/ACL_WHOAMI.ts diff --git a/lib/commands/APPEND.spec.ts b/packages/client/lib/commands/APPEND.spec.ts similarity index 100% rename from lib/commands/APPEND.spec.ts rename to packages/client/lib/commands/APPEND.spec.ts diff --git a/lib/commands/APPEND.ts b/packages/client/lib/commands/APPEND.ts similarity index 100% rename from lib/commands/APPEND.ts rename to packages/client/lib/commands/APPEND.ts diff --git a/lib/commands/ASKING.spec.ts b/packages/client/lib/commands/ASKING.spec.ts similarity index 100% rename from lib/commands/ASKING.spec.ts rename to packages/client/lib/commands/ASKING.spec.ts diff --git a/lib/commands/ASKING.ts b/packages/client/lib/commands/ASKING.ts similarity index 100% rename from lib/commands/ASKING.ts rename to packages/client/lib/commands/ASKING.ts diff --git a/lib/commands/AUTH.spec.ts b/packages/client/lib/commands/AUTH.spec.ts similarity index 100% rename from lib/commands/AUTH.spec.ts rename to packages/client/lib/commands/AUTH.spec.ts diff --git a/lib/commands/AUTH.ts b/packages/client/lib/commands/AUTH.ts similarity index 100% rename from lib/commands/AUTH.ts rename to packages/client/lib/commands/AUTH.ts diff --git a/lib/commands/BGREWRITEAOF.spec.ts b/packages/client/lib/commands/BGREWRITEAOF.spec.ts similarity index 100% rename from lib/commands/BGREWRITEAOF.spec.ts rename to packages/client/lib/commands/BGREWRITEAOF.spec.ts diff --git a/lib/commands/BGREWRITEAOF.ts b/packages/client/lib/commands/BGREWRITEAOF.ts similarity index 100% rename from lib/commands/BGREWRITEAOF.ts rename to packages/client/lib/commands/BGREWRITEAOF.ts diff --git a/lib/commands/BGSAVE.spec.ts b/packages/client/lib/commands/BGSAVE.spec.ts similarity index 100% rename from lib/commands/BGSAVE.spec.ts rename to packages/client/lib/commands/BGSAVE.spec.ts diff --git a/lib/commands/BGSAVE.ts b/packages/client/lib/commands/BGSAVE.ts similarity index 100% rename from lib/commands/BGSAVE.ts rename to packages/client/lib/commands/BGSAVE.ts diff --git a/lib/commands/BITCOUNT.spec.ts b/packages/client/lib/commands/BITCOUNT.spec.ts similarity index 100% rename from lib/commands/BITCOUNT.spec.ts rename to packages/client/lib/commands/BITCOUNT.spec.ts diff --git a/lib/commands/BITCOUNT.ts b/packages/client/lib/commands/BITCOUNT.ts similarity index 100% rename from lib/commands/BITCOUNT.ts rename to packages/client/lib/commands/BITCOUNT.ts diff --git a/lib/commands/BITFIELD.spec.ts b/packages/client/lib/commands/BITFIELD.spec.ts similarity index 100% rename from lib/commands/BITFIELD.spec.ts rename to packages/client/lib/commands/BITFIELD.spec.ts diff --git a/lib/commands/BITFIELD.ts b/packages/client/lib/commands/BITFIELD.ts similarity index 100% rename from lib/commands/BITFIELD.ts rename to packages/client/lib/commands/BITFIELD.ts diff --git a/lib/commands/BITOP.spec.ts b/packages/client/lib/commands/BITOP.spec.ts similarity index 100% rename from lib/commands/BITOP.spec.ts rename to packages/client/lib/commands/BITOP.spec.ts diff --git a/lib/commands/BITOP.ts b/packages/client/lib/commands/BITOP.ts similarity index 100% rename from lib/commands/BITOP.ts rename to packages/client/lib/commands/BITOP.ts diff --git a/lib/commands/BITPOS.spec.ts b/packages/client/lib/commands/BITPOS.spec.ts similarity index 100% rename from lib/commands/BITPOS.spec.ts rename to packages/client/lib/commands/BITPOS.spec.ts diff --git a/lib/commands/BITPOS.ts b/packages/client/lib/commands/BITPOS.ts similarity index 100% rename from lib/commands/BITPOS.ts rename to packages/client/lib/commands/BITPOS.ts diff --git a/lib/commands/BLMOVE.spec.ts b/packages/client/lib/commands/BLMOVE.spec.ts similarity index 100% rename from lib/commands/BLMOVE.spec.ts rename to packages/client/lib/commands/BLMOVE.spec.ts diff --git a/lib/commands/BLMOVE.ts b/packages/client/lib/commands/BLMOVE.ts similarity index 100% rename from lib/commands/BLMOVE.ts rename to packages/client/lib/commands/BLMOVE.ts diff --git a/lib/commands/BLPOP.spec.ts b/packages/client/lib/commands/BLPOP.spec.ts similarity index 100% rename from lib/commands/BLPOP.spec.ts rename to packages/client/lib/commands/BLPOP.spec.ts diff --git a/lib/commands/BLPOP.ts b/packages/client/lib/commands/BLPOP.ts similarity index 100% rename from lib/commands/BLPOP.ts rename to packages/client/lib/commands/BLPOP.ts diff --git a/lib/commands/BRPOP.spec.ts b/packages/client/lib/commands/BRPOP.spec.ts similarity index 100% rename from lib/commands/BRPOP.spec.ts rename to packages/client/lib/commands/BRPOP.spec.ts diff --git a/lib/commands/BRPOP.ts b/packages/client/lib/commands/BRPOP.ts similarity index 100% rename from lib/commands/BRPOP.ts rename to packages/client/lib/commands/BRPOP.ts diff --git a/lib/commands/BRPOPLPUSH.spec.ts b/packages/client/lib/commands/BRPOPLPUSH.spec.ts similarity index 100% rename from lib/commands/BRPOPLPUSH.spec.ts rename to packages/client/lib/commands/BRPOPLPUSH.spec.ts diff --git a/lib/commands/BRPOPLPUSH.ts b/packages/client/lib/commands/BRPOPLPUSH.ts similarity index 100% rename from lib/commands/BRPOPLPUSH.ts rename to packages/client/lib/commands/BRPOPLPUSH.ts diff --git a/lib/commands/BZPOPMAX.spec.ts b/packages/client/lib/commands/BZPOPMAX.spec.ts similarity index 100% rename from lib/commands/BZPOPMAX.spec.ts rename to packages/client/lib/commands/BZPOPMAX.spec.ts diff --git a/lib/commands/BZPOPMAX.ts b/packages/client/lib/commands/BZPOPMAX.ts similarity index 100% rename from lib/commands/BZPOPMAX.ts rename to packages/client/lib/commands/BZPOPMAX.ts diff --git a/lib/commands/BZPOPMIN.spec.ts b/packages/client/lib/commands/BZPOPMIN.spec.ts similarity index 100% rename from lib/commands/BZPOPMIN.spec.ts rename to packages/client/lib/commands/BZPOPMIN.spec.ts diff --git a/lib/commands/BZPOPMIN.ts b/packages/client/lib/commands/BZPOPMIN.ts similarity index 100% rename from lib/commands/BZPOPMIN.ts rename to packages/client/lib/commands/BZPOPMIN.ts diff --git a/lib/commands/CLIENT_ID.spec.ts b/packages/client/lib/commands/CLIENT_ID.spec.ts similarity index 100% rename from lib/commands/CLIENT_ID.spec.ts rename to packages/client/lib/commands/CLIENT_ID.spec.ts diff --git a/lib/commands/CLIENT_ID.ts b/packages/client/lib/commands/CLIENT_ID.ts similarity index 100% rename from lib/commands/CLIENT_ID.ts rename to packages/client/lib/commands/CLIENT_ID.ts diff --git a/lib/commands/CLIENT_INFO.spec.ts b/packages/client/lib/commands/CLIENT_INFO.spec.ts similarity index 100% rename from lib/commands/CLIENT_INFO.spec.ts rename to packages/client/lib/commands/CLIENT_INFO.spec.ts diff --git a/lib/commands/CLIENT_INFO.ts b/packages/client/lib/commands/CLIENT_INFO.ts similarity index 100% rename from lib/commands/CLIENT_INFO.ts rename to packages/client/lib/commands/CLIENT_INFO.ts diff --git a/lib/commands/CLUSTER_ADDSLOTS.spec.ts b/packages/client/lib/commands/CLUSTER_ADDSLOTS.spec.ts similarity index 100% rename from lib/commands/CLUSTER_ADDSLOTS.spec.ts rename to packages/client/lib/commands/CLUSTER_ADDSLOTS.spec.ts diff --git a/lib/commands/CLUSTER_ADDSLOTS.ts b/packages/client/lib/commands/CLUSTER_ADDSLOTS.ts similarity index 100% rename from lib/commands/CLUSTER_ADDSLOTS.ts rename to packages/client/lib/commands/CLUSTER_ADDSLOTS.ts diff --git a/lib/commands/CLUSTER_FLUSHSLOTS.spec.ts b/packages/client/lib/commands/CLUSTER_FLUSHSLOTS.spec.ts similarity index 100% rename from lib/commands/CLUSTER_FLUSHSLOTS.spec.ts rename to packages/client/lib/commands/CLUSTER_FLUSHSLOTS.spec.ts diff --git a/lib/commands/CLUSTER_FLUSHSLOTS.ts b/packages/client/lib/commands/CLUSTER_FLUSHSLOTS.ts similarity index 100% rename from lib/commands/CLUSTER_FLUSHSLOTS.ts rename to packages/client/lib/commands/CLUSTER_FLUSHSLOTS.ts diff --git a/lib/commands/CLUSTER_GETKEYSINSLOT.spec.ts b/packages/client/lib/commands/CLUSTER_GETKEYSINSLOT.spec.ts similarity index 100% rename from lib/commands/CLUSTER_GETKEYSINSLOT.spec.ts rename to packages/client/lib/commands/CLUSTER_GETKEYSINSLOT.spec.ts diff --git a/lib/commands/CLUSTER_GETKEYSINSLOT.ts b/packages/client/lib/commands/CLUSTER_GETKEYSINSLOT.ts similarity index 100% rename from lib/commands/CLUSTER_GETKEYSINSLOT.ts rename to packages/client/lib/commands/CLUSTER_GETKEYSINSLOT.ts diff --git a/lib/commands/CLUSTER_INFO.spec.ts b/packages/client/lib/commands/CLUSTER_INFO.spec.ts similarity index 100% rename from lib/commands/CLUSTER_INFO.spec.ts rename to packages/client/lib/commands/CLUSTER_INFO.spec.ts diff --git a/lib/commands/CLUSTER_INFO.ts b/packages/client/lib/commands/CLUSTER_INFO.ts similarity index 100% rename from lib/commands/CLUSTER_INFO.ts rename to packages/client/lib/commands/CLUSTER_INFO.ts diff --git a/lib/commands/CLUSTER_MEET.spec.ts b/packages/client/lib/commands/CLUSTER_MEET.spec.ts similarity index 100% rename from lib/commands/CLUSTER_MEET.spec.ts rename to packages/client/lib/commands/CLUSTER_MEET.spec.ts diff --git a/lib/commands/CLUSTER_MEET.ts b/packages/client/lib/commands/CLUSTER_MEET.ts similarity index 100% rename from lib/commands/CLUSTER_MEET.ts rename to packages/client/lib/commands/CLUSTER_MEET.ts diff --git a/lib/commands/CLUSTER_NODES.spec.ts b/packages/client/lib/commands/CLUSTER_NODES.spec.ts similarity index 100% rename from lib/commands/CLUSTER_NODES.spec.ts rename to packages/client/lib/commands/CLUSTER_NODES.spec.ts diff --git a/lib/commands/CLUSTER_NODES.ts b/packages/client/lib/commands/CLUSTER_NODES.ts similarity index 100% rename from lib/commands/CLUSTER_NODES.ts rename to packages/client/lib/commands/CLUSTER_NODES.ts diff --git a/lib/commands/CLUSTER_RESET.spec.ts b/packages/client/lib/commands/CLUSTER_RESET.spec.ts similarity index 100% rename from lib/commands/CLUSTER_RESET.spec.ts rename to packages/client/lib/commands/CLUSTER_RESET.spec.ts diff --git a/lib/commands/CLUSTER_RESET.ts b/packages/client/lib/commands/CLUSTER_RESET.ts similarity index 100% rename from lib/commands/CLUSTER_RESET.ts rename to packages/client/lib/commands/CLUSTER_RESET.ts diff --git a/lib/commands/CLUSTER_SETSLOT.spec.ts b/packages/client/lib/commands/CLUSTER_SETSLOT.spec.ts similarity index 100% rename from lib/commands/CLUSTER_SETSLOT.spec.ts rename to packages/client/lib/commands/CLUSTER_SETSLOT.spec.ts diff --git a/lib/commands/CLUSTER_SETSLOT.ts b/packages/client/lib/commands/CLUSTER_SETSLOT.ts similarity index 100% rename from lib/commands/CLUSTER_SETSLOT.ts rename to packages/client/lib/commands/CLUSTER_SETSLOT.ts diff --git a/lib/commands/CLUSTER_SLOTS.spec.ts b/packages/client/lib/commands/CLUSTER_SLOTS.spec.ts similarity index 100% rename from lib/commands/CLUSTER_SLOTS.spec.ts rename to packages/client/lib/commands/CLUSTER_SLOTS.spec.ts diff --git a/lib/commands/CLUSTER_SLOTS.ts b/packages/client/lib/commands/CLUSTER_SLOTS.ts similarity index 100% rename from lib/commands/CLUSTER_SLOTS.ts rename to packages/client/lib/commands/CLUSTER_SLOTS.ts diff --git a/lib/commands/COMMAND.spec.ts b/packages/client/lib/commands/COMMAND.spec.ts similarity index 65% rename from lib/commands/COMMAND.spec.ts rename to packages/client/lib/commands/COMMAND.spec.ts index baad79845ab..04ceab2a07c 100644 --- a/lib/commands/COMMAND.spec.ts +++ b/packages/client/lib/commands/COMMAND.spec.ts @@ -11,7 +11,7 @@ describe('COMMAND', () => { ); }); - testUtils.testWithClient('client.command', async client => { - assertPingCommand((await client.command()).find(command => command.name === 'ping')); - }, GLOBAL.SERVERS.OPEN); + // testUtils.testWithClient('client.command', async client => { + // assertPingCommand((await client.command()).find(command => command.name === 'ping')); + // }, GLOBAL.SERVERS.OPEN); }); diff --git a/lib/commands/COMMAND.ts b/packages/client/lib/commands/COMMAND.ts similarity index 100% rename from lib/commands/COMMAND.ts rename to packages/client/lib/commands/COMMAND.ts diff --git a/lib/commands/COMMAND_COUNT.spec.ts b/packages/client/lib/commands/COMMAND_COUNT.spec.ts similarity index 100% rename from lib/commands/COMMAND_COUNT.spec.ts rename to packages/client/lib/commands/COMMAND_COUNT.spec.ts diff --git a/lib/commands/COMMAND_COUNT.ts b/packages/client/lib/commands/COMMAND_COUNT.ts similarity index 100% rename from lib/commands/COMMAND_COUNT.ts rename to packages/client/lib/commands/COMMAND_COUNT.ts diff --git a/lib/commands/COMMAND_GETKEYS.spec.ts b/packages/client/lib/commands/COMMAND_GETKEYS.spec.ts similarity index 100% rename from lib/commands/COMMAND_GETKEYS.spec.ts rename to packages/client/lib/commands/COMMAND_GETKEYS.spec.ts diff --git a/lib/commands/COMMAND_GETKEYS.ts b/packages/client/lib/commands/COMMAND_GETKEYS.ts similarity index 100% rename from lib/commands/COMMAND_GETKEYS.ts rename to packages/client/lib/commands/COMMAND_GETKEYS.ts diff --git a/lib/commands/COMMAND_INFO.spec.ts b/packages/client/lib/commands/COMMAND_INFO.spec.ts similarity index 100% rename from lib/commands/COMMAND_INFO.spec.ts rename to packages/client/lib/commands/COMMAND_INFO.spec.ts diff --git a/lib/commands/COMMAND_INFO.ts b/packages/client/lib/commands/COMMAND_INFO.ts similarity index 100% rename from lib/commands/COMMAND_INFO.ts rename to packages/client/lib/commands/COMMAND_INFO.ts diff --git a/lib/commands/CONFIG_GET.spec.ts b/packages/client/lib/commands/CONFIG_GET.spec.ts similarity index 100% rename from lib/commands/CONFIG_GET.spec.ts rename to packages/client/lib/commands/CONFIG_GET.spec.ts diff --git a/lib/commands/CONFIG_GET.ts b/packages/client/lib/commands/CONFIG_GET.ts similarity index 100% rename from lib/commands/CONFIG_GET.ts rename to packages/client/lib/commands/CONFIG_GET.ts diff --git a/lib/commands/CONFIG_RESETSTAT.spec.ts b/packages/client/lib/commands/CONFIG_RESETSTAT.spec.ts similarity index 100% rename from lib/commands/CONFIG_RESETSTAT.spec.ts rename to packages/client/lib/commands/CONFIG_RESETSTAT.spec.ts diff --git a/lib/commands/CONFIG_RESETSTAT.ts b/packages/client/lib/commands/CONFIG_RESETSTAT.ts similarity index 100% rename from lib/commands/CONFIG_RESETSTAT.ts rename to packages/client/lib/commands/CONFIG_RESETSTAT.ts diff --git a/lib/commands/CONFIG_REWRITE.spec.ts b/packages/client/lib/commands/CONFIG_REWRITE.spec.ts similarity index 100% rename from lib/commands/CONFIG_REWRITE.spec.ts rename to packages/client/lib/commands/CONFIG_REWRITE.spec.ts diff --git a/lib/commands/CONFIG_REWRITE.ts b/packages/client/lib/commands/CONFIG_REWRITE.ts similarity index 100% rename from lib/commands/CONFIG_REWRITE.ts rename to packages/client/lib/commands/CONFIG_REWRITE.ts diff --git a/lib/commands/CONFIG_SET.spec.ts b/packages/client/lib/commands/CONFIG_SET.spec.ts similarity index 100% rename from lib/commands/CONFIG_SET.spec.ts rename to packages/client/lib/commands/CONFIG_SET.spec.ts diff --git a/lib/commands/CONFIG_SET.ts b/packages/client/lib/commands/CONFIG_SET.ts similarity index 100% rename from lib/commands/CONFIG_SET.ts rename to packages/client/lib/commands/CONFIG_SET.ts diff --git a/lib/commands/COPY.spec.ts b/packages/client/lib/commands/COPY.spec.ts similarity index 100% rename from lib/commands/COPY.spec.ts rename to packages/client/lib/commands/COPY.spec.ts diff --git a/lib/commands/COPY.ts b/packages/client/lib/commands/COPY.ts similarity index 100% rename from lib/commands/COPY.ts rename to packages/client/lib/commands/COPY.ts diff --git a/lib/commands/DBSIZE.spec.ts b/packages/client/lib/commands/DBSIZE.spec.ts similarity index 100% rename from lib/commands/DBSIZE.spec.ts rename to packages/client/lib/commands/DBSIZE.spec.ts diff --git a/lib/commands/DBSIZE.ts b/packages/client/lib/commands/DBSIZE.ts similarity index 100% rename from lib/commands/DBSIZE.ts rename to packages/client/lib/commands/DBSIZE.ts diff --git a/lib/commands/DECR.spec.ts b/packages/client/lib/commands/DECR.spec.ts similarity index 100% rename from lib/commands/DECR.spec.ts rename to packages/client/lib/commands/DECR.spec.ts diff --git a/lib/commands/DECR.ts b/packages/client/lib/commands/DECR.ts similarity index 100% rename from lib/commands/DECR.ts rename to packages/client/lib/commands/DECR.ts diff --git a/lib/commands/DECRBY.spec.ts b/packages/client/lib/commands/DECRBY.spec.ts similarity index 100% rename from lib/commands/DECRBY.spec.ts rename to packages/client/lib/commands/DECRBY.spec.ts diff --git a/lib/commands/DECRBY.ts b/packages/client/lib/commands/DECRBY.ts similarity index 100% rename from lib/commands/DECRBY.ts rename to packages/client/lib/commands/DECRBY.ts diff --git a/lib/commands/DEL.spec.ts b/packages/client/lib/commands/DEL.spec.ts similarity index 100% rename from lib/commands/DEL.spec.ts rename to packages/client/lib/commands/DEL.spec.ts diff --git a/lib/commands/DEL.ts b/packages/client/lib/commands/DEL.ts similarity index 100% rename from lib/commands/DEL.ts rename to packages/client/lib/commands/DEL.ts diff --git a/lib/commands/DISCARD.spec.ts b/packages/client/lib/commands/DISCARD.spec.ts similarity index 100% rename from lib/commands/DISCARD.spec.ts rename to packages/client/lib/commands/DISCARD.spec.ts diff --git a/lib/commands/DISCARD.ts b/packages/client/lib/commands/DISCARD.ts similarity index 100% rename from lib/commands/DISCARD.ts rename to packages/client/lib/commands/DISCARD.ts diff --git a/lib/commands/DUMP.spec.ts b/packages/client/lib/commands/DUMP.spec.ts similarity index 100% rename from lib/commands/DUMP.spec.ts rename to packages/client/lib/commands/DUMP.spec.ts diff --git a/lib/commands/DUMP.ts b/packages/client/lib/commands/DUMP.ts similarity index 100% rename from lib/commands/DUMP.ts rename to packages/client/lib/commands/DUMP.ts diff --git a/lib/commands/ECHO.spec.ts b/packages/client/lib/commands/ECHO.spec.ts similarity index 100% rename from lib/commands/ECHO.spec.ts rename to packages/client/lib/commands/ECHO.spec.ts diff --git a/lib/commands/ECHO.ts b/packages/client/lib/commands/ECHO.ts similarity index 100% rename from lib/commands/ECHO.ts rename to packages/client/lib/commands/ECHO.ts diff --git a/lib/commands/EVAL.spec.ts b/packages/client/lib/commands/EVAL.spec.ts similarity index 100% rename from lib/commands/EVAL.spec.ts rename to packages/client/lib/commands/EVAL.spec.ts diff --git a/lib/commands/EVAL.ts b/packages/client/lib/commands/EVAL.ts similarity index 100% rename from lib/commands/EVAL.ts rename to packages/client/lib/commands/EVAL.ts diff --git a/lib/commands/EVALSHA.spec.ts b/packages/client/lib/commands/EVALSHA.spec.ts similarity index 100% rename from lib/commands/EVALSHA.spec.ts rename to packages/client/lib/commands/EVALSHA.spec.ts diff --git a/lib/commands/EVALSHA.ts b/packages/client/lib/commands/EVALSHA.ts similarity index 100% rename from lib/commands/EVALSHA.ts rename to packages/client/lib/commands/EVALSHA.ts diff --git a/lib/commands/EXISTS.spec.ts b/packages/client/lib/commands/EXISTS.spec.ts similarity index 100% rename from lib/commands/EXISTS.spec.ts rename to packages/client/lib/commands/EXISTS.spec.ts diff --git a/lib/commands/EXISTS.ts b/packages/client/lib/commands/EXISTS.ts similarity index 100% rename from lib/commands/EXISTS.ts rename to packages/client/lib/commands/EXISTS.ts diff --git a/lib/commands/EXPIRE.spec.ts b/packages/client/lib/commands/EXPIRE.spec.ts similarity index 100% rename from lib/commands/EXPIRE.spec.ts rename to packages/client/lib/commands/EXPIRE.spec.ts diff --git a/lib/commands/EXPIRE.ts b/packages/client/lib/commands/EXPIRE.ts similarity index 100% rename from lib/commands/EXPIRE.ts rename to packages/client/lib/commands/EXPIRE.ts diff --git a/lib/commands/EXPIREAT.spec.ts b/packages/client/lib/commands/EXPIREAT.spec.ts similarity index 100% rename from lib/commands/EXPIREAT.spec.ts rename to packages/client/lib/commands/EXPIREAT.spec.ts diff --git a/lib/commands/EXPIREAT.ts b/packages/client/lib/commands/EXPIREAT.ts similarity index 100% rename from lib/commands/EXPIREAT.ts rename to packages/client/lib/commands/EXPIREAT.ts diff --git a/lib/commands/FAILOVER.spec.ts b/packages/client/lib/commands/FAILOVER.spec.ts similarity index 100% rename from lib/commands/FAILOVER.spec.ts rename to packages/client/lib/commands/FAILOVER.spec.ts diff --git a/lib/commands/FAILOVER.ts b/packages/client/lib/commands/FAILOVER.ts similarity index 100% rename from lib/commands/FAILOVER.ts rename to packages/client/lib/commands/FAILOVER.ts diff --git a/lib/commands/FLUSHALL.spec.ts b/packages/client/lib/commands/FLUSHALL.spec.ts similarity index 100% rename from lib/commands/FLUSHALL.spec.ts rename to packages/client/lib/commands/FLUSHALL.spec.ts diff --git a/lib/commands/FLUSHALL.ts b/packages/client/lib/commands/FLUSHALL.ts similarity index 100% rename from lib/commands/FLUSHALL.ts rename to packages/client/lib/commands/FLUSHALL.ts diff --git a/lib/commands/FLUSHDB.spec.ts b/packages/client/lib/commands/FLUSHDB.spec.ts similarity index 100% rename from lib/commands/FLUSHDB.spec.ts rename to packages/client/lib/commands/FLUSHDB.spec.ts diff --git a/lib/commands/FLUSHDB.ts b/packages/client/lib/commands/FLUSHDB.ts similarity index 100% rename from lib/commands/FLUSHDB.ts rename to packages/client/lib/commands/FLUSHDB.ts diff --git a/lib/commands/GEOADD.spec.ts b/packages/client/lib/commands/GEOADD.spec.ts similarity index 100% rename from lib/commands/GEOADD.spec.ts rename to packages/client/lib/commands/GEOADD.spec.ts diff --git a/lib/commands/GEOADD.ts b/packages/client/lib/commands/GEOADD.ts similarity index 100% rename from lib/commands/GEOADD.ts rename to packages/client/lib/commands/GEOADD.ts diff --git a/lib/commands/GEODIST.spec.ts b/packages/client/lib/commands/GEODIST.spec.ts similarity index 100% rename from lib/commands/GEODIST.spec.ts rename to packages/client/lib/commands/GEODIST.spec.ts diff --git a/lib/commands/GEODIST.ts b/packages/client/lib/commands/GEODIST.ts similarity index 100% rename from lib/commands/GEODIST.ts rename to packages/client/lib/commands/GEODIST.ts diff --git a/lib/commands/GEOHASH.spec.ts b/packages/client/lib/commands/GEOHASH.spec.ts similarity index 100% rename from lib/commands/GEOHASH.spec.ts rename to packages/client/lib/commands/GEOHASH.spec.ts diff --git a/lib/commands/GEOHASH.ts b/packages/client/lib/commands/GEOHASH.ts similarity index 100% rename from lib/commands/GEOHASH.ts rename to packages/client/lib/commands/GEOHASH.ts diff --git a/lib/commands/GEOPOS.spec.ts b/packages/client/lib/commands/GEOPOS.spec.ts similarity index 100% rename from lib/commands/GEOPOS.spec.ts rename to packages/client/lib/commands/GEOPOS.spec.ts diff --git a/lib/commands/GEOPOS.ts b/packages/client/lib/commands/GEOPOS.ts similarity index 100% rename from lib/commands/GEOPOS.ts rename to packages/client/lib/commands/GEOPOS.ts diff --git a/lib/commands/GEOSEARCH.spec.ts b/packages/client/lib/commands/GEOSEARCH.spec.ts similarity index 100% rename from lib/commands/GEOSEARCH.spec.ts rename to packages/client/lib/commands/GEOSEARCH.spec.ts diff --git a/lib/commands/GEOSEARCH.ts b/packages/client/lib/commands/GEOSEARCH.ts similarity index 100% rename from lib/commands/GEOSEARCH.ts rename to packages/client/lib/commands/GEOSEARCH.ts diff --git a/lib/commands/GEOSEARCHSTORE.spec.ts b/packages/client/lib/commands/GEOSEARCHSTORE.spec.ts similarity index 100% rename from lib/commands/GEOSEARCHSTORE.spec.ts rename to packages/client/lib/commands/GEOSEARCHSTORE.spec.ts diff --git a/lib/commands/GEOSEARCHSTORE.ts b/packages/client/lib/commands/GEOSEARCHSTORE.ts similarity index 100% rename from lib/commands/GEOSEARCHSTORE.ts rename to packages/client/lib/commands/GEOSEARCHSTORE.ts diff --git a/lib/commands/GEOSEARCH_WITH.spec.ts b/packages/client/lib/commands/GEOSEARCH_WITH.spec.ts similarity index 100% rename from lib/commands/GEOSEARCH_WITH.spec.ts rename to packages/client/lib/commands/GEOSEARCH_WITH.spec.ts diff --git a/lib/commands/GEOSEARCH_WITH.ts b/packages/client/lib/commands/GEOSEARCH_WITH.ts similarity index 100% rename from lib/commands/GEOSEARCH_WITH.ts rename to packages/client/lib/commands/GEOSEARCH_WITH.ts diff --git a/lib/commands/GET.spec.ts b/packages/client/lib/commands/GET.spec.ts similarity index 100% rename from lib/commands/GET.spec.ts rename to packages/client/lib/commands/GET.spec.ts diff --git a/lib/commands/GET.ts b/packages/client/lib/commands/GET.ts similarity index 100% rename from lib/commands/GET.ts rename to packages/client/lib/commands/GET.ts diff --git a/lib/commands/GETBIT.spec.ts b/packages/client/lib/commands/GETBIT.spec.ts similarity index 100% rename from lib/commands/GETBIT.spec.ts rename to packages/client/lib/commands/GETBIT.spec.ts diff --git a/lib/commands/GETBIT.ts b/packages/client/lib/commands/GETBIT.ts similarity index 100% rename from lib/commands/GETBIT.ts rename to packages/client/lib/commands/GETBIT.ts diff --git a/lib/commands/GETDEL.spec.ts b/packages/client/lib/commands/GETDEL.spec.ts similarity index 100% rename from lib/commands/GETDEL.spec.ts rename to packages/client/lib/commands/GETDEL.spec.ts diff --git a/lib/commands/GETDEL.ts b/packages/client/lib/commands/GETDEL.ts similarity index 100% rename from lib/commands/GETDEL.ts rename to packages/client/lib/commands/GETDEL.ts diff --git a/lib/commands/GETEX.spec.ts b/packages/client/lib/commands/GETEX.spec.ts similarity index 100% rename from lib/commands/GETEX.spec.ts rename to packages/client/lib/commands/GETEX.spec.ts diff --git a/lib/commands/GETEX.ts b/packages/client/lib/commands/GETEX.ts similarity index 100% rename from lib/commands/GETEX.ts rename to packages/client/lib/commands/GETEX.ts diff --git a/lib/commands/GETRANGE.spec.ts b/packages/client/lib/commands/GETRANGE.spec.ts similarity index 100% rename from lib/commands/GETRANGE.spec.ts rename to packages/client/lib/commands/GETRANGE.spec.ts diff --git a/lib/commands/GETRANGE.ts b/packages/client/lib/commands/GETRANGE.ts similarity index 100% rename from lib/commands/GETRANGE.ts rename to packages/client/lib/commands/GETRANGE.ts diff --git a/lib/commands/GETSET.spec.ts b/packages/client/lib/commands/GETSET.spec.ts similarity index 100% rename from lib/commands/GETSET.spec.ts rename to packages/client/lib/commands/GETSET.spec.ts diff --git a/lib/commands/GETSET.ts b/packages/client/lib/commands/GETSET.ts similarity index 100% rename from lib/commands/GETSET.ts rename to packages/client/lib/commands/GETSET.ts diff --git a/lib/commands/GET_BUFFER.spec.ts b/packages/client/lib/commands/GET_BUFFER.spec.ts similarity index 100% rename from lib/commands/GET_BUFFER.spec.ts rename to packages/client/lib/commands/GET_BUFFER.spec.ts diff --git a/lib/commands/GET_BUFFER.ts b/packages/client/lib/commands/GET_BUFFER.ts similarity index 100% rename from lib/commands/GET_BUFFER.ts rename to packages/client/lib/commands/GET_BUFFER.ts diff --git a/lib/commands/HDEL.spec.ts b/packages/client/lib/commands/HDEL.spec.ts similarity index 100% rename from lib/commands/HDEL.spec.ts rename to packages/client/lib/commands/HDEL.spec.ts diff --git a/lib/commands/HDEL.ts b/packages/client/lib/commands/HDEL.ts similarity index 100% rename from lib/commands/HDEL.ts rename to packages/client/lib/commands/HDEL.ts diff --git a/lib/commands/HELLO.spec.ts b/packages/client/lib/commands/HELLO.spec.ts similarity index 100% rename from lib/commands/HELLO.spec.ts rename to packages/client/lib/commands/HELLO.spec.ts diff --git a/lib/commands/HELLO.ts b/packages/client/lib/commands/HELLO.ts similarity index 100% rename from lib/commands/HELLO.ts rename to packages/client/lib/commands/HELLO.ts diff --git a/lib/commands/HEXISTS.spec.ts b/packages/client/lib/commands/HEXISTS.spec.ts similarity index 100% rename from lib/commands/HEXISTS.spec.ts rename to packages/client/lib/commands/HEXISTS.spec.ts diff --git a/lib/commands/HEXISTS.ts b/packages/client/lib/commands/HEXISTS.ts similarity index 100% rename from lib/commands/HEXISTS.ts rename to packages/client/lib/commands/HEXISTS.ts diff --git a/lib/commands/HGET.spec.ts b/packages/client/lib/commands/HGET.spec.ts similarity index 100% rename from lib/commands/HGET.spec.ts rename to packages/client/lib/commands/HGET.spec.ts diff --git a/lib/commands/HGET.ts b/packages/client/lib/commands/HGET.ts similarity index 100% rename from lib/commands/HGET.ts rename to packages/client/lib/commands/HGET.ts diff --git a/lib/commands/HGETALL.spec.ts b/packages/client/lib/commands/HGETALL.spec.ts similarity index 100% rename from lib/commands/HGETALL.spec.ts rename to packages/client/lib/commands/HGETALL.spec.ts diff --git a/lib/commands/HGETALL.ts b/packages/client/lib/commands/HGETALL.ts similarity index 100% rename from lib/commands/HGETALL.ts rename to packages/client/lib/commands/HGETALL.ts diff --git a/lib/commands/HINCRBY.spec.ts b/packages/client/lib/commands/HINCRBY.spec.ts similarity index 100% rename from lib/commands/HINCRBY.spec.ts rename to packages/client/lib/commands/HINCRBY.spec.ts diff --git a/lib/commands/HINCRBY.ts b/packages/client/lib/commands/HINCRBY.ts similarity index 100% rename from lib/commands/HINCRBY.ts rename to packages/client/lib/commands/HINCRBY.ts diff --git a/lib/commands/HINCRBYFLOAT.spec.ts b/packages/client/lib/commands/HINCRBYFLOAT.spec.ts similarity index 100% rename from lib/commands/HINCRBYFLOAT.spec.ts rename to packages/client/lib/commands/HINCRBYFLOAT.spec.ts diff --git a/lib/commands/HINCRBYFLOAT.ts b/packages/client/lib/commands/HINCRBYFLOAT.ts similarity index 100% rename from lib/commands/HINCRBYFLOAT.ts rename to packages/client/lib/commands/HINCRBYFLOAT.ts diff --git a/lib/commands/HKEYS.spec.ts b/packages/client/lib/commands/HKEYS.spec.ts similarity index 100% rename from lib/commands/HKEYS.spec.ts rename to packages/client/lib/commands/HKEYS.spec.ts diff --git a/lib/commands/HKEYS.ts b/packages/client/lib/commands/HKEYS.ts similarity index 100% rename from lib/commands/HKEYS.ts rename to packages/client/lib/commands/HKEYS.ts diff --git a/lib/commands/HLEN.spec.ts b/packages/client/lib/commands/HLEN.spec.ts similarity index 100% rename from lib/commands/HLEN.spec.ts rename to packages/client/lib/commands/HLEN.spec.ts diff --git a/lib/commands/HLEN.ts b/packages/client/lib/commands/HLEN.ts similarity index 100% rename from lib/commands/HLEN.ts rename to packages/client/lib/commands/HLEN.ts diff --git a/lib/commands/HMGET.spec.ts b/packages/client/lib/commands/HMGET.spec.ts similarity index 100% rename from lib/commands/HMGET.spec.ts rename to packages/client/lib/commands/HMGET.spec.ts diff --git a/lib/commands/HMGET.ts b/packages/client/lib/commands/HMGET.ts similarity index 100% rename from lib/commands/HMGET.ts rename to packages/client/lib/commands/HMGET.ts diff --git a/lib/commands/HRANDFIELD.spec.ts b/packages/client/lib/commands/HRANDFIELD.spec.ts similarity index 100% rename from lib/commands/HRANDFIELD.spec.ts rename to packages/client/lib/commands/HRANDFIELD.spec.ts diff --git a/lib/commands/HRANDFIELD.ts b/packages/client/lib/commands/HRANDFIELD.ts similarity index 100% rename from lib/commands/HRANDFIELD.ts rename to packages/client/lib/commands/HRANDFIELD.ts diff --git a/lib/commands/HRANDFIELD_COUNT.spec.ts b/packages/client/lib/commands/HRANDFIELD_COUNT.spec.ts similarity index 100% rename from lib/commands/HRANDFIELD_COUNT.spec.ts rename to packages/client/lib/commands/HRANDFIELD_COUNT.spec.ts diff --git a/lib/commands/HRANDFIELD_COUNT.ts b/packages/client/lib/commands/HRANDFIELD_COUNT.ts similarity index 100% rename from lib/commands/HRANDFIELD_COUNT.ts rename to packages/client/lib/commands/HRANDFIELD_COUNT.ts diff --git a/lib/commands/HRANDFIELD_COUNT_WITHVALUES.spec.ts b/packages/client/lib/commands/HRANDFIELD_COUNT_WITHVALUES.spec.ts similarity index 100% rename from lib/commands/HRANDFIELD_COUNT_WITHVALUES.spec.ts rename to packages/client/lib/commands/HRANDFIELD_COUNT_WITHVALUES.spec.ts diff --git a/lib/commands/HRANDFIELD_COUNT_WITHVALUES.ts b/packages/client/lib/commands/HRANDFIELD_COUNT_WITHVALUES.ts similarity index 100% rename from lib/commands/HRANDFIELD_COUNT_WITHVALUES.ts rename to packages/client/lib/commands/HRANDFIELD_COUNT_WITHVALUES.ts diff --git a/lib/commands/HSCAN.spec.ts b/packages/client/lib/commands/HSCAN.spec.ts similarity index 100% rename from lib/commands/HSCAN.spec.ts rename to packages/client/lib/commands/HSCAN.spec.ts diff --git a/lib/commands/HSCAN.ts b/packages/client/lib/commands/HSCAN.ts similarity index 100% rename from lib/commands/HSCAN.ts rename to packages/client/lib/commands/HSCAN.ts diff --git a/lib/commands/HSET.spec.ts b/packages/client/lib/commands/HSET.spec.ts similarity index 100% rename from lib/commands/HSET.spec.ts rename to packages/client/lib/commands/HSET.spec.ts diff --git a/lib/commands/HSET.ts b/packages/client/lib/commands/HSET.ts similarity index 100% rename from lib/commands/HSET.ts rename to packages/client/lib/commands/HSET.ts diff --git a/lib/commands/HSETNX.spec.ts b/packages/client/lib/commands/HSETNX.spec.ts similarity index 100% rename from lib/commands/HSETNX.spec.ts rename to packages/client/lib/commands/HSETNX.spec.ts diff --git a/lib/commands/HSETNX.ts b/packages/client/lib/commands/HSETNX.ts similarity index 100% rename from lib/commands/HSETNX.ts rename to packages/client/lib/commands/HSETNX.ts diff --git a/lib/commands/HSTRLEN.spec.ts b/packages/client/lib/commands/HSTRLEN.spec.ts similarity index 100% rename from lib/commands/HSTRLEN.spec.ts rename to packages/client/lib/commands/HSTRLEN.spec.ts diff --git a/lib/commands/HSTRLEN.ts b/packages/client/lib/commands/HSTRLEN.ts similarity index 100% rename from lib/commands/HSTRLEN.ts rename to packages/client/lib/commands/HSTRLEN.ts diff --git a/lib/commands/HVALS.spec.ts b/packages/client/lib/commands/HVALS.spec.ts similarity index 100% rename from lib/commands/HVALS.spec.ts rename to packages/client/lib/commands/HVALS.spec.ts diff --git a/lib/commands/HVALS.ts b/packages/client/lib/commands/HVALS.ts similarity index 100% rename from lib/commands/HVALS.ts rename to packages/client/lib/commands/HVALS.ts diff --git a/lib/commands/INCR.spec.ts b/packages/client/lib/commands/INCR.spec.ts similarity index 100% rename from lib/commands/INCR.spec.ts rename to packages/client/lib/commands/INCR.spec.ts diff --git a/lib/commands/INCR.ts b/packages/client/lib/commands/INCR.ts similarity index 100% rename from lib/commands/INCR.ts rename to packages/client/lib/commands/INCR.ts diff --git a/lib/commands/INCRBY.spec.ts b/packages/client/lib/commands/INCRBY.spec.ts similarity index 100% rename from lib/commands/INCRBY.spec.ts rename to packages/client/lib/commands/INCRBY.spec.ts diff --git a/lib/commands/INCRBY.ts b/packages/client/lib/commands/INCRBY.ts similarity index 100% rename from lib/commands/INCRBY.ts rename to packages/client/lib/commands/INCRBY.ts diff --git a/lib/commands/INCRBYFLOAT.spec.ts b/packages/client/lib/commands/INCRBYFLOAT.spec.ts similarity index 100% rename from lib/commands/INCRBYFLOAT.spec.ts rename to packages/client/lib/commands/INCRBYFLOAT.spec.ts diff --git a/lib/commands/INCRBYFLOAT.ts b/packages/client/lib/commands/INCRBYFLOAT.ts similarity index 100% rename from lib/commands/INCRBYFLOAT.ts rename to packages/client/lib/commands/INCRBYFLOAT.ts diff --git a/lib/commands/INFO.spec.ts b/packages/client/lib/commands/INFO.spec.ts similarity index 100% rename from lib/commands/INFO.spec.ts rename to packages/client/lib/commands/INFO.spec.ts diff --git a/lib/commands/INFO.ts b/packages/client/lib/commands/INFO.ts similarity index 100% rename from lib/commands/INFO.ts rename to packages/client/lib/commands/INFO.ts diff --git a/lib/commands/KEYS.spec.ts b/packages/client/lib/commands/KEYS.spec.ts similarity index 100% rename from lib/commands/KEYS.spec.ts rename to packages/client/lib/commands/KEYS.spec.ts diff --git a/lib/commands/KEYS.ts b/packages/client/lib/commands/KEYS.ts similarity index 100% rename from lib/commands/KEYS.ts rename to packages/client/lib/commands/KEYS.ts diff --git a/lib/commands/LASTSAVE.spec.ts b/packages/client/lib/commands/LASTSAVE.spec.ts similarity index 100% rename from lib/commands/LASTSAVE.spec.ts rename to packages/client/lib/commands/LASTSAVE.spec.ts diff --git a/lib/commands/LASTSAVE.ts b/packages/client/lib/commands/LASTSAVE.ts similarity index 100% rename from lib/commands/LASTSAVE.ts rename to packages/client/lib/commands/LASTSAVE.ts diff --git a/lib/commands/LINDEX.spec.ts b/packages/client/lib/commands/LINDEX.spec.ts similarity index 100% rename from lib/commands/LINDEX.spec.ts rename to packages/client/lib/commands/LINDEX.spec.ts diff --git a/lib/commands/LINDEX.ts b/packages/client/lib/commands/LINDEX.ts similarity index 100% rename from lib/commands/LINDEX.ts rename to packages/client/lib/commands/LINDEX.ts diff --git a/lib/commands/LINSERT.spec.ts b/packages/client/lib/commands/LINSERT.spec.ts similarity index 100% rename from lib/commands/LINSERT.spec.ts rename to packages/client/lib/commands/LINSERT.spec.ts diff --git a/lib/commands/LINSERT.ts b/packages/client/lib/commands/LINSERT.ts similarity index 100% rename from lib/commands/LINSERT.ts rename to packages/client/lib/commands/LINSERT.ts diff --git a/lib/commands/LLEN.spec.ts b/packages/client/lib/commands/LLEN.spec.ts similarity index 100% rename from lib/commands/LLEN.spec.ts rename to packages/client/lib/commands/LLEN.spec.ts diff --git a/lib/commands/LLEN.ts b/packages/client/lib/commands/LLEN.ts similarity index 100% rename from lib/commands/LLEN.ts rename to packages/client/lib/commands/LLEN.ts diff --git a/lib/commands/LMOVE.spec.ts b/packages/client/lib/commands/LMOVE.spec.ts similarity index 100% rename from lib/commands/LMOVE.spec.ts rename to packages/client/lib/commands/LMOVE.spec.ts diff --git a/lib/commands/LMOVE.ts b/packages/client/lib/commands/LMOVE.ts similarity index 100% rename from lib/commands/LMOVE.ts rename to packages/client/lib/commands/LMOVE.ts diff --git a/lib/commands/LOLWUT.spec.ts b/packages/client/lib/commands/LOLWUT.spec.ts similarity index 100% rename from lib/commands/LOLWUT.spec.ts rename to packages/client/lib/commands/LOLWUT.spec.ts diff --git a/lib/commands/LOLWUT.ts b/packages/client/lib/commands/LOLWUT.ts similarity index 100% rename from lib/commands/LOLWUT.ts rename to packages/client/lib/commands/LOLWUT.ts diff --git a/lib/commands/LPOP.spec.ts b/packages/client/lib/commands/LPOP.spec.ts similarity index 100% rename from lib/commands/LPOP.spec.ts rename to packages/client/lib/commands/LPOP.spec.ts diff --git a/lib/commands/LPOP.ts b/packages/client/lib/commands/LPOP.ts similarity index 100% rename from lib/commands/LPOP.ts rename to packages/client/lib/commands/LPOP.ts diff --git a/lib/commands/LPOP_COUNT.spec.ts b/packages/client/lib/commands/LPOP_COUNT.spec.ts similarity index 100% rename from lib/commands/LPOP_COUNT.spec.ts rename to packages/client/lib/commands/LPOP_COUNT.spec.ts diff --git a/lib/commands/LPOP_COUNT.ts b/packages/client/lib/commands/LPOP_COUNT.ts similarity index 100% rename from lib/commands/LPOP_COUNT.ts rename to packages/client/lib/commands/LPOP_COUNT.ts diff --git a/lib/commands/LPOS.spec.ts b/packages/client/lib/commands/LPOS.spec.ts similarity index 100% rename from lib/commands/LPOS.spec.ts rename to packages/client/lib/commands/LPOS.spec.ts diff --git a/lib/commands/LPOS.ts b/packages/client/lib/commands/LPOS.ts similarity index 100% rename from lib/commands/LPOS.ts rename to packages/client/lib/commands/LPOS.ts diff --git a/lib/commands/LPOS_COUNT.spec.ts b/packages/client/lib/commands/LPOS_COUNT.spec.ts similarity index 100% rename from lib/commands/LPOS_COUNT.spec.ts rename to packages/client/lib/commands/LPOS_COUNT.spec.ts diff --git a/lib/commands/LPOS_COUNT.ts b/packages/client/lib/commands/LPOS_COUNT.ts similarity index 100% rename from lib/commands/LPOS_COUNT.ts rename to packages/client/lib/commands/LPOS_COUNT.ts diff --git a/lib/commands/LPUSH.spec.ts b/packages/client/lib/commands/LPUSH.spec.ts similarity index 100% rename from lib/commands/LPUSH.spec.ts rename to packages/client/lib/commands/LPUSH.spec.ts diff --git a/lib/commands/LPUSH.ts b/packages/client/lib/commands/LPUSH.ts similarity index 100% rename from lib/commands/LPUSH.ts rename to packages/client/lib/commands/LPUSH.ts diff --git a/lib/commands/LPUSHX.spec.ts b/packages/client/lib/commands/LPUSHX.spec.ts similarity index 100% rename from lib/commands/LPUSHX.spec.ts rename to packages/client/lib/commands/LPUSHX.spec.ts diff --git a/lib/commands/LPUSHX.ts b/packages/client/lib/commands/LPUSHX.ts similarity index 100% rename from lib/commands/LPUSHX.ts rename to packages/client/lib/commands/LPUSHX.ts diff --git a/lib/commands/LRANGE.spec.ts b/packages/client/lib/commands/LRANGE.spec.ts similarity index 100% rename from lib/commands/LRANGE.spec.ts rename to packages/client/lib/commands/LRANGE.spec.ts diff --git a/lib/commands/LRANGE.ts b/packages/client/lib/commands/LRANGE.ts similarity index 100% rename from lib/commands/LRANGE.ts rename to packages/client/lib/commands/LRANGE.ts diff --git a/lib/commands/LREM.spec.ts b/packages/client/lib/commands/LREM.spec.ts similarity index 100% rename from lib/commands/LREM.spec.ts rename to packages/client/lib/commands/LREM.spec.ts diff --git a/lib/commands/LREM.ts b/packages/client/lib/commands/LREM.ts similarity index 100% rename from lib/commands/LREM.ts rename to packages/client/lib/commands/LREM.ts diff --git a/lib/commands/LSET.spec.ts b/packages/client/lib/commands/LSET.spec.ts similarity index 100% rename from lib/commands/LSET.spec.ts rename to packages/client/lib/commands/LSET.spec.ts diff --git a/lib/commands/LSET.ts b/packages/client/lib/commands/LSET.ts similarity index 100% rename from lib/commands/LSET.ts rename to packages/client/lib/commands/LSET.ts diff --git a/lib/commands/LTRIM.spec.ts b/packages/client/lib/commands/LTRIM.spec.ts similarity index 100% rename from lib/commands/LTRIM.spec.ts rename to packages/client/lib/commands/LTRIM.spec.ts diff --git a/lib/commands/LTRIM.ts b/packages/client/lib/commands/LTRIM.ts similarity index 100% rename from lib/commands/LTRIM.ts rename to packages/client/lib/commands/LTRIM.ts diff --git a/lib/commands/MEMORY_DOCTOR.spec.ts b/packages/client/lib/commands/MEMORY_DOCTOR.spec.ts similarity index 100% rename from lib/commands/MEMORY_DOCTOR.spec.ts rename to packages/client/lib/commands/MEMORY_DOCTOR.spec.ts diff --git a/lib/commands/MEMORY_DOCTOR.ts b/packages/client/lib/commands/MEMORY_DOCTOR.ts similarity index 100% rename from lib/commands/MEMORY_DOCTOR.ts rename to packages/client/lib/commands/MEMORY_DOCTOR.ts diff --git a/lib/commands/MEMORY_MALLOC-STATS.spec.ts b/packages/client/lib/commands/MEMORY_MALLOC-STATS.spec.ts similarity index 100% rename from lib/commands/MEMORY_MALLOC-STATS.spec.ts rename to packages/client/lib/commands/MEMORY_MALLOC-STATS.spec.ts diff --git a/lib/commands/MEMORY_MALLOC-STATS.ts b/packages/client/lib/commands/MEMORY_MALLOC-STATS.ts similarity index 100% rename from lib/commands/MEMORY_MALLOC-STATS.ts rename to packages/client/lib/commands/MEMORY_MALLOC-STATS.ts diff --git a/lib/commands/MEMORY_PURGE.spec.ts b/packages/client/lib/commands/MEMORY_PURGE.spec.ts similarity index 100% rename from lib/commands/MEMORY_PURGE.spec.ts rename to packages/client/lib/commands/MEMORY_PURGE.spec.ts diff --git a/lib/commands/MEMORY_PURGE.ts b/packages/client/lib/commands/MEMORY_PURGE.ts similarity index 100% rename from lib/commands/MEMORY_PURGE.ts rename to packages/client/lib/commands/MEMORY_PURGE.ts diff --git a/lib/commands/MEMORY_STATS.spec.ts b/packages/client/lib/commands/MEMORY_STATS.spec.ts similarity index 100% rename from lib/commands/MEMORY_STATS.spec.ts rename to packages/client/lib/commands/MEMORY_STATS.spec.ts diff --git a/lib/commands/MEMORY_STATS.ts b/packages/client/lib/commands/MEMORY_STATS.ts similarity index 100% rename from lib/commands/MEMORY_STATS.ts rename to packages/client/lib/commands/MEMORY_STATS.ts diff --git a/lib/commands/MEMORY_USAGE.spec.ts b/packages/client/lib/commands/MEMORY_USAGE.spec.ts similarity index 100% rename from lib/commands/MEMORY_USAGE.spec.ts rename to packages/client/lib/commands/MEMORY_USAGE.spec.ts diff --git a/lib/commands/MEMORY_USAGE.ts b/packages/client/lib/commands/MEMORY_USAGE.ts similarity index 100% rename from lib/commands/MEMORY_USAGE.ts rename to packages/client/lib/commands/MEMORY_USAGE.ts diff --git a/lib/commands/MGET.spec.ts b/packages/client/lib/commands/MGET.spec.ts similarity index 100% rename from lib/commands/MGET.spec.ts rename to packages/client/lib/commands/MGET.spec.ts diff --git a/lib/commands/MGET.ts b/packages/client/lib/commands/MGET.ts similarity index 100% rename from lib/commands/MGET.ts rename to packages/client/lib/commands/MGET.ts diff --git a/lib/commands/MIGRATE.spec.ts b/packages/client/lib/commands/MIGRATE.spec.ts similarity index 100% rename from lib/commands/MIGRATE.spec.ts rename to packages/client/lib/commands/MIGRATE.spec.ts diff --git a/lib/commands/MIGRATE.ts b/packages/client/lib/commands/MIGRATE.ts similarity index 100% rename from lib/commands/MIGRATE.ts rename to packages/client/lib/commands/MIGRATE.ts diff --git a/lib/commands/MODULE_LIST.spec.ts b/packages/client/lib/commands/MODULE_LIST.spec.ts similarity index 100% rename from lib/commands/MODULE_LIST.spec.ts rename to packages/client/lib/commands/MODULE_LIST.spec.ts diff --git a/lib/commands/MODULE_LIST.ts b/packages/client/lib/commands/MODULE_LIST.ts similarity index 100% rename from lib/commands/MODULE_LIST.ts rename to packages/client/lib/commands/MODULE_LIST.ts diff --git a/lib/commands/MODULE_LOAD.spec.ts b/packages/client/lib/commands/MODULE_LOAD.spec.ts similarity index 100% rename from lib/commands/MODULE_LOAD.spec.ts rename to packages/client/lib/commands/MODULE_LOAD.spec.ts diff --git a/lib/commands/MODULE_LOAD.ts b/packages/client/lib/commands/MODULE_LOAD.ts similarity index 100% rename from lib/commands/MODULE_LOAD.ts rename to packages/client/lib/commands/MODULE_LOAD.ts diff --git a/lib/commands/MODULE_UNLOAD.spec.ts b/packages/client/lib/commands/MODULE_UNLOAD.spec.ts similarity index 100% rename from lib/commands/MODULE_UNLOAD.spec.ts rename to packages/client/lib/commands/MODULE_UNLOAD.spec.ts diff --git a/lib/commands/MODULE_UNLOAD.ts b/packages/client/lib/commands/MODULE_UNLOAD.ts similarity index 100% rename from lib/commands/MODULE_UNLOAD.ts rename to packages/client/lib/commands/MODULE_UNLOAD.ts diff --git a/lib/commands/MOVE.spec.ts b/packages/client/lib/commands/MOVE.spec.ts similarity index 100% rename from lib/commands/MOVE.spec.ts rename to packages/client/lib/commands/MOVE.spec.ts diff --git a/lib/commands/MOVE.ts b/packages/client/lib/commands/MOVE.ts similarity index 100% rename from lib/commands/MOVE.ts rename to packages/client/lib/commands/MOVE.ts diff --git a/lib/commands/MSET.spec.ts b/packages/client/lib/commands/MSET.spec.ts similarity index 100% rename from lib/commands/MSET.spec.ts rename to packages/client/lib/commands/MSET.spec.ts diff --git a/lib/commands/MSET.ts b/packages/client/lib/commands/MSET.ts similarity index 100% rename from lib/commands/MSET.ts rename to packages/client/lib/commands/MSET.ts diff --git a/lib/commands/MSETNX.spec.ts b/packages/client/lib/commands/MSETNX.spec.ts similarity index 100% rename from lib/commands/MSETNX.spec.ts rename to packages/client/lib/commands/MSETNX.spec.ts diff --git a/lib/commands/MSETNX.ts b/packages/client/lib/commands/MSETNX.ts similarity index 100% rename from lib/commands/MSETNX.ts rename to packages/client/lib/commands/MSETNX.ts diff --git a/lib/commands/PERSIST.spec.ts b/packages/client/lib/commands/PERSIST.spec.ts similarity index 100% rename from lib/commands/PERSIST.spec.ts rename to packages/client/lib/commands/PERSIST.spec.ts diff --git a/lib/commands/PERSIST.ts b/packages/client/lib/commands/PERSIST.ts similarity index 100% rename from lib/commands/PERSIST.ts rename to packages/client/lib/commands/PERSIST.ts diff --git a/lib/commands/PEXPIRE.spec.ts b/packages/client/lib/commands/PEXPIRE.spec.ts similarity index 100% rename from lib/commands/PEXPIRE.spec.ts rename to packages/client/lib/commands/PEXPIRE.spec.ts diff --git a/lib/commands/PEXPIRE.ts b/packages/client/lib/commands/PEXPIRE.ts similarity index 100% rename from lib/commands/PEXPIRE.ts rename to packages/client/lib/commands/PEXPIRE.ts diff --git a/lib/commands/PEXPIREAT.spec.ts b/packages/client/lib/commands/PEXPIREAT.spec.ts similarity index 100% rename from lib/commands/PEXPIREAT.spec.ts rename to packages/client/lib/commands/PEXPIREAT.spec.ts diff --git a/lib/commands/PEXPIREAT.ts b/packages/client/lib/commands/PEXPIREAT.ts similarity index 100% rename from lib/commands/PEXPIREAT.ts rename to packages/client/lib/commands/PEXPIREAT.ts diff --git a/lib/commands/PFADD.spec.ts b/packages/client/lib/commands/PFADD.spec.ts similarity index 100% rename from lib/commands/PFADD.spec.ts rename to packages/client/lib/commands/PFADD.spec.ts diff --git a/lib/commands/PFADD.ts b/packages/client/lib/commands/PFADD.ts similarity index 100% rename from lib/commands/PFADD.ts rename to packages/client/lib/commands/PFADD.ts diff --git a/lib/commands/PFCOUNT.spec.ts b/packages/client/lib/commands/PFCOUNT.spec.ts similarity index 100% rename from lib/commands/PFCOUNT.spec.ts rename to packages/client/lib/commands/PFCOUNT.spec.ts diff --git a/lib/commands/PFCOUNT.ts b/packages/client/lib/commands/PFCOUNT.ts similarity index 100% rename from lib/commands/PFCOUNT.ts rename to packages/client/lib/commands/PFCOUNT.ts diff --git a/lib/commands/PFMERGE.spec.ts b/packages/client/lib/commands/PFMERGE.spec.ts similarity index 100% rename from lib/commands/PFMERGE.spec.ts rename to packages/client/lib/commands/PFMERGE.spec.ts diff --git a/lib/commands/PFMERGE.ts b/packages/client/lib/commands/PFMERGE.ts similarity index 100% rename from lib/commands/PFMERGE.ts rename to packages/client/lib/commands/PFMERGE.ts diff --git a/lib/commands/PING.spec.ts b/packages/client/lib/commands/PING.spec.ts similarity index 100% rename from lib/commands/PING.spec.ts rename to packages/client/lib/commands/PING.spec.ts diff --git a/lib/commands/PING.ts b/packages/client/lib/commands/PING.ts similarity index 100% rename from lib/commands/PING.ts rename to packages/client/lib/commands/PING.ts diff --git a/lib/commands/PSETEX.spec.ts b/packages/client/lib/commands/PSETEX.spec.ts similarity index 100% rename from lib/commands/PSETEX.spec.ts rename to packages/client/lib/commands/PSETEX.spec.ts diff --git a/lib/commands/PSETEX.ts b/packages/client/lib/commands/PSETEX.ts similarity index 100% rename from lib/commands/PSETEX.ts rename to packages/client/lib/commands/PSETEX.ts diff --git a/lib/commands/PTTL.spec.ts b/packages/client/lib/commands/PTTL.spec.ts similarity index 100% rename from lib/commands/PTTL.spec.ts rename to packages/client/lib/commands/PTTL.spec.ts diff --git a/lib/commands/PTTL.ts b/packages/client/lib/commands/PTTL.ts similarity index 100% rename from lib/commands/PTTL.ts rename to packages/client/lib/commands/PTTL.ts diff --git a/lib/commands/PUBLISH.spec.ts b/packages/client/lib/commands/PUBLISH.spec.ts similarity index 100% rename from lib/commands/PUBLISH.spec.ts rename to packages/client/lib/commands/PUBLISH.spec.ts diff --git a/lib/commands/PUBLISH.ts b/packages/client/lib/commands/PUBLISH.ts similarity index 100% rename from lib/commands/PUBLISH.ts rename to packages/client/lib/commands/PUBLISH.ts diff --git a/lib/commands/PUBSUB_CHANNELS.spec.ts b/packages/client/lib/commands/PUBSUB_CHANNELS.spec.ts similarity index 100% rename from lib/commands/PUBSUB_CHANNELS.spec.ts rename to packages/client/lib/commands/PUBSUB_CHANNELS.spec.ts diff --git a/lib/commands/PUBSUB_CHANNELS.ts b/packages/client/lib/commands/PUBSUB_CHANNELS.ts similarity index 100% rename from lib/commands/PUBSUB_CHANNELS.ts rename to packages/client/lib/commands/PUBSUB_CHANNELS.ts diff --git a/lib/commands/PUBSUB_NUMPAT.spec.ts b/packages/client/lib/commands/PUBSUB_NUMPAT.spec.ts similarity index 100% rename from lib/commands/PUBSUB_NUMPAT.spec.ts rename to packages/client/lib/commands/PUBSUB_NUMPAT.spec.ts diff --git a/lib/commands/PUBSUB_NUMPAT.ts b/packages/client/lib/commands/PUBSUB_NUMPAT.ts similarity index 100% rename from lib/commands/PUBSUB_NUMPAT.ts rename to packages/client/lib/commands/PUBSUB_NUMPAT.ts diff --git a/lib/commands/PUBSUB_NUMSUB.spec.ts b/packages/client/lib/commands/PUBSUB_NUMSUB.spec.ts similarity index 100% rename from lib/commands/PUBSUB_NUMSUB.spec.ts rename to packages/client/lib/commands/PUBSUB_NUMSUB.spec.ts diff --git a/lib/commands/PUBSUB_NUMSUB.ts b/packages/client/lib/commands/PUBSUB_NUMSUB.ts similarity index 100% rename from lib/commands/PUBSUB_NUMSUB.ts rename to packages/client/lib/commands/PUBSUB_NUMSUB.ts diff --git a/lib/commands/RANDOMKEY.spec.ts b/packages/client/lib/commands/RANDOMKEY.spec.ts similarity index 100% rename from lib/commands/RANDOMKEY.spec.ts rename to packages/client/lib/commands/RANDOMKEY.spec.ts diff --git a/lib/commands/RANDOMKEY.ts b/packages/client/lib/commands/RANDOMKEY.ts similarity index 100% rename from lib/commands/RANDOMKEY.ts rename to packages/client/lib/commands/RANDOMKEY.ts diff --git a/lib/commands/READONLY.spec.ts b/packages/client/lib/commands/READONLY.spec.ts similarity index 100% rename from lib/commands/READONLY.spec.ts rename to packages/client/lib/commands/READONLY.spec.ts diff --git a/lib/commands/READONLY.ts b/packages/client/lib/commands/READONLY.ts similarity index 100% rename from lib/commands/READONLY.ts rename to packages/client/lib/commands/READONLY.ts diff --git a/lib/commands/READWRITE.spec.ts b/packages/client/lib/commands/READWRITE.spec.ts similarity index 100% rename from lib/commands/READWRITE.spec.ts rename to packages/client/lib/commands/READWRITE.spec.ts diff --git a/lib/commands/READWRITE.ts b/packages/client/lib/commands/READWRITE.ts similarity index 100% rename from lib/commands/READWRITE.ts rename to packages/client/lib/commands/READWRITE.ts diff --git a/lib/commands/RENAME.spec.ts b/packages/client/lib/commands/RENAME.spec.ts similarity index 100% rename from lib/commands/RENAME.spec.ts rename to packages/client/lib/commands/RENAME.spec.ts diff --git a/lib/commands/RENAME.ts b/packages/client/lib/commands/RENAME.ts similarity index 100% rename from lib/commands/RENAME.ts rename to packages/client/lib/commands/RENAME.ts diff --git a/lib/commands/RENAMENX.spec.ts b/packages/client/lib/commands/RENAMENX.spec.ts similarity index 100% rename from lib/commands/RENAMENX.spec.ts rename to packages/client/lib/commands/RENAMENX.spec.ts diff --git a/lib/commands/RENAMENX.ts b/packages/client/lib/commands/RENAMENX.ts similarity index 100% rename from lib/commands/RENAMENX.ts rename to packages/client/lib/commands/RENAMENX.ts diff --git a/lib/commands/REPLICAOF.spec.ts b/packages/client/lib/commands/REPLICAOF.spec.ts similarity index 100% rename from lib/commands/REPLICAOF.spec.ts rename to packages/client/lib/commands/REPLICAOF.spec.ts diff --git a/lib/commands/REPLICAOF.ts b/packages/client/lib/commands/REPLICAOF.ts similarity index 100% rename from lib/commands/REPLICAOF.ts rename to packages/client/lib/commands/REPLICAOF.ts diff --git a/lib/commands/RESTORE-ASKING.spec.ts b/packages/client/lib/commands/RESTORE-ASKING.spec.ts similarity index 100% rename from lib/commands/RESTORE-ASKING.spec.ts rename to packages/client/lib/commands/RESTORE-ASKING.spec.ts diff --git a/lib/commands/RESTORE-ASKING.ts b/packages/client/lib/commands/RESTORE-ASKING.ts similarity index 100% rename from lib/commands/RESTORE-ASKING.ts rename to packages/client/lib/commands/RESTORE-ASKING.ts diff --git a/lib/commands/ROLE.spec.ts b/packages/client/lib/commands/ROLE.spec.ts similarity index 100% rename from lib/commands/ROLE.spec.ts rename to packages/client/lib/commands/ROLE.spec.ts diff --git a/lib/commands/ROLE.ts b/packages/client/lib/commands/ROLE.ts similarity index 100% rename from lib/commands/ROLE.ts rename to packages/client/lib/commands/ROLE.ts diff --git a/lib/commands/RPOP.spec.ts b/packages/client/lib/commands/RPOP.spec.ts similarity index 100% rename from lib/commands/RPOP.spec.ts rename to packages/client/lib/commands/RPOP.spec.ts diff --git a/lib/commands/RPOP.ts b/packages/client/lib/commands/RPOP.ts similarity index 100% rename from lib/commands/RPOP.ts rename to packages/client/lib/commands/RPOP.ts diff --git a/lib/commands/RPOPLPUSH.spec.ts b/packages/client/lib/commands/RPOPLPUSH.spec.ts similarity index 100% rename from lib/commands/RPOPLPUSH.spec.ts rename to packages/client/lib/commands/RPOPLPUSH.spec.ts diff --git a/lib/commands/RPOPLPUSH.ts b/packages/client/lib/commands/RPOPLPUSH.ts similarity index 100% rename from lib/commands/RPOPLPUSH.ts rename to packages/client/lib/commands/RPOPLPUSH.ts diff --git a/lib/commands/RPOP_COUNT.spec.ts b/packages/client/lib/commands/RPOP_COUNT.spec.ts similarity index 100% rename from lib/commands/RPOP_COUNT.spec.ts rename to packages/client/lib/commands/RPOP_COUNT.spec.ts diff --git a/lib/commands/RPOP_COUNT.ts b/packages/client/lib/commands/RPOP_COUNT.ts similarity index 100% rename from lib/commands/RPOP_COUNT.ts rename to packages/client/lib/commands/RPOP_COUNT.ts diff --git a/lib/commands/RPUSH.spec.ts b/packages/client/lib/commands/RPUSH.spec.ts similarity index 100% rename from lib/commands/RPUSH.spec.ts rename to packages/client/lib/commands/RPUSH.spec.ts diff --git a/lib/commands/RPUSH.ts b/packages/client/lib/commands/RPUSH.ts similarity index 100% rename from lib/commands/RPUSH.ts rename to packages/client/lib/commands/RPUSH.ts diff --git a/lib/commands/RPUSHX.spec.ts b/packages/client/lib/commands/RPUSHX.spec.ts similarity index 100% rename from lib/commands/RPUSHX.spec.ts rename to packages/client/lib/commands/RPUSHX.spec.ts diff --git a/lib/commands/RPUSHX.ts b/packages/client/lib/commands/RPUSHX.ts similarity index 100% rename from lib/commands/RPUSHX.ts rename to packages/client/lib/commands/RPUSHX.ts diff --git a/lib/commands/SADD.spec.ts b/packages/client/lib/commands/SADD.spec.ts similarity index 100% rename from lib/commands/SADD.spec.ts rename to packages/client/lib/commands/SADD.spec.ts diff --git a/lib/commands/SADD.ts b/packages/client/lib/commands/SADD.ts similarity index 100% rename from lib/commands/SADD.ts rename to packages/client/lib/commands/SADD.ts diff --git a/lib/commands/SAVE.spec.ts b/packages/client/lib/commands/SAVE.spec.ts similarity index 100% rename from lib/commands/SAVE.spec.ts rename to packages/client/lib/commands/SAVE.spec.ts diff --git a/lib/commands/SAVE.ts b/packages/client/lib/commands/SAVE.ts similarity index 100% rename from lib/commands/SAVE.ts rename to packages/client/lib/commands/SAVE.ts diff --git a/lib/commands/SCAN.spec.ts b/packages/client/lib/commands/SCAN.spec.ts similarity index 100% rename from lib/commands/SCAN.spec.ts rename to packages/client/lib/commands/SCAN.spec.ts diff --git a/lib/commands/SCAN.ts b/packages/client/lib/commands/SCAN.ts similarity index 100% rename from lib/commands/SCAN.ts rename to packages/client/lib/commands/SCAN.ts diff --git a/lib/commands/SCARD.spec.ts b/packages/client/lib/commands/SCARD.spec.ts similarity index 100% rename from lib/commands/SCARD.spec.ts rename to packages/client/lib/commands/SCARD.spec.ts diff --git a/lib/commands/SCARD.ts b/packages/client/lib/commands/SCARD.ts similarity index 100% rename from lib/commands/SCARD.ts rename to packages/client/lib/commands/SCARD.ts diff --git a/lib/commands/SCRIPT_DEBUG.spec.ts b/packages/client/lib/commands/SCRIPT_DEBUG.spec.ts similarity index 100% rename from lib/commands/SCRIPT_DEBUG.spec.ts rename to packages/client/lib/commands/SCRIPT_DEBUG.spec.ts diff --git a/lib/commands/SCRIPT_DEBUG.ts b/packages/client/lib/commands/SCRIPT_DEBUG.ts similarity index 100% rename from lib/commands/SCRIPT_DEBUG.ts rename to packages/client/lib/commands/SCRIPT_DEBUG.ts diff --git a/lib/commands/SCRIPT_EXISTS.spec.ts b/packages/client/lib/commands/SCRIPT_EXISTS.spec.ts similarity index 100% rename from lib/commands/SCRIPT_EXISTS.spec.ts rename to packages/client/lib/commands/SCRIPT_EXISTS.spec.ts diff --git a/lib/commands/SCRIPT_EXISTS.ts b/packages/client/lib/commands/SCRIPT_EXISTS.ts similarity index 100% rename from lib/commands/SCRIPT_EXISTS.ts rename to packages/client/lib/commands/SCRIPT_EXISTS.ts diff --git a/lib/commands/SCRIPT_FLUSH.spec.ts b/packages/client/lib/commands/SCRIPT_FLUSH.spec.ts similarity index 100% rename from lib/commands/SCRIPT_FLUSH.spec.ts rename to packages/client/lib/commands/SCRIPT_FLUSH.spec.ts diff --git a/lib/commands/SCRIPT_FLUSH.ts b/packages/client/lib/commands/SCRIPT_FLUSH.ts similarity index 100% rename from lib/commands/SCRIPT_FLUSH.ts rename to packages/client/lib/commands/SCRIPT_FLUSH.ts diff --git a/lib/commands/SCRIPT_KILL.spec.ts b/packages/client/lib/commands/SCRIPT_KILL.spec.ts similarity index 100% rename from lib/commands/SCRIPT_KILL.spec.ts rename to packages/client/lib/commands/SCRIPT_KILL.spec.ts diff --git a/lib/commands/SCRIPT_KILL.ts b/packages/client/lib/commands/SCRIPT_KILL.ts similarity index 100% rename from lib/commands/SCRIPT_KILL.ts rename to packages/client/lib/commands/SCRIPT_KILL.ts diff --git a/lib/commands/SCRIPT_LOAD.spec.ts b/packages/client/lib/commands/SCRIPT_LOAD.spec.ts similarity index 100% rename from lib/commands/SCRIPT_LOAD.spec.ts rename to packages/client/lib/commands/SCRIPT_LOAD.spec.ts diff --git a/lib/commands/SCRIPT_LOAD.ts b/packages/client/lib/commands/SCRIPT_LOAD.ts similarity index 100% rename from lib/commands/SCRIPT_LOAD.ts rename to packages/client/lib/commands/SCRIPT_LOAD.ts diff --git a/lib/commands/SDIFF.spec.ts b/packages/client/lib/commands/SDIFF.spec.ts similarity index 100% rename from lib/commands/SDIFF.spec.ts rename to packages/client/lib/commands/SDIFF.spec.ts diff --git a/lib/commands/SDIFF.ts b/packages/client/lib/commands/SDIFF.ts similarity index 100% rename from lib/commands/SDIFF.ts rename to packages/client/lib/commands/SDIFF.ts diff --git a/lib/commands/SDIFFSTORE.spec.ts b/packages/client/lib/commands/SDIFFSTORE.spec.ts similarity index 100% rename from lib/commands/SDIFFSTORE.spec.ts rename to packages/client/lib/commands/SDIFFSTORE.spec.ts diff --git a/lib/commands/SDIFFSTORE.ts b/packages/client/lib/commands/SDIFFSTORE.ts similarity index 100% rename from lib/commands/SDIFFSTORE.ts rename to packages/client/lib/commands/SDIFFSTORE.ts diff --git a/lib/commands/SET.spec.ts b/packages/client/lib/commands/SET.spec.ts similarity index 100% rename from lib/commands/SET.spec.ts rename to packages/client/lib/commands/SET.spec.ts diff --git a/lib/commands/SET.ts b/packages/client/lib/commands/SET.ts similarity index 100% rename from lib/commands/SET.ts rename to packages/client/lib/commands/SET.ts diff --git a/lib/commands/SETBIT.spec.ts b/packages/client/lib/commands/SETBIT.spec.ts similarity index 100% rename from lib/commands/SETBIT.spec.ts rename to packages/client/lib/commands/SETBIT.spec.ts diff --git a/lib/commands/SETBIT.ts b/packages/client/lib/commands/SETBIT.ts similarity index 100% rename from lib/commands/SETBIT.ts rename to packages/client/lib/commands/SETBIT.ts diff --git a/lib/commands/SETEX.spec.ts b/packages/client/lib/commands/SETEX.spec.ts similarity index 100% rename from lib/commands/SETEX.spec.ts rename to packages/client/lib/commands/SETEX.spec.ts diff --git a/lib/commands/SETEX.ts b/packages/client/lib/commands/SETEX.ts similarity index 100% rename from lib/commands/SETEX.ts rename to packages/client/lib/commands/SETEX.ts diff --git a/lib/commands/SETNX .spec.ts b/packages/client/lib/commands/SETNX .spec.ts similarity index 100% rename from lib/commands/SETNX .spec.ts rename to packages/client/lib/commands/SETNX .spec.ts diff --git a/lib/commands/SETNX.ts b/packages/client/lib/commands/SETNX.ts similarity index 100% rename from lib/commands/SETNX.ts rename to packages/client/lib/commands/SETNX.ts diff --git a/lib/commands/SETRANGE.spec.ts b/packages/client/lib/commands/SETRANGE.spec.ts similarity index 100% rename from lib/commands/SETRANGE.spec.ts rename to packages/client/lib/commands/SETRANGE.spec.ts diff --git a/lib/commands/SETRANGE.ts b/packages/client/lib/commands/SETRANGE.ts similarity index 100% rename from lib/commands/SETRANGE.ts rename to packages/client/lib/commands/SETRANGE.ts diff --git a/lib/commands/SHUTDOWN.spec.ts b/packages/client/lib/commands/SHUTDOWN.spec.ts similarity index 100% rename from lib/commands/SHUTDOWN.spec.ts rename to packages/client/lib/commands/SHUTDOWN.spec.ts diff --git a/lib/commands/SHUTDOWN.ts b/packages/client/lib/commands/SHUTDOWN.ts similarity index 100% rename from lib/commands/SHUTDOWN.ts rename to packages/client/lib/commands/SHUTDOWN.ts diff --git a/lib/commands/SINTER.spec.ts b/packages/client/lib/commands/SINTER.spec.ts similarity index 100% rename from lib/commands/SINTER.spec.ts rename to packages/client/lib/commands/SINTER.spec.ts diff --git a/lib/commands/SINTER.ts b/packages/client/lib/commands/SINTER.ts similarity index 100% rename from lib/commands/SINTER.ts rename to packages/client/lib/commands/SINTER.ts diff --git a/lib/commands/SINTERSTORE.spec.ts b/packages/client/lib/commands/SINTERSTORE.spec.ts similarity index 100% rename from lib/commands/SINTERSTORE.spec.ts rename to packages/client/lib/commands/SINTERSTORE.spec.ts diff --git a/lib/commands/SINTERSTORE.ts b/packages/client/lib/commands/SINTERSTORE.ts similarity index 100% rename from lib/commands/SINTERSTORE.ts rename to packages/client/lib/commands/SINTERSTORE.ts diff --git a/lib/commands/SISMEMBER.spec.ts b/packages/client/lib/commands/SISMEMBER.spec.ts similarity index 100% rename from lib/commands/SISMEMBER.spec.ts rename to packages/client/lib/commands/SISMEMBER.spec.ts diff --git a/lib/commands/SISMEMBER.ts b/packages/client/lib/commands/SISMEMBER.ts similarity index 100% rename from lib/commands/SISMEMBER.ts rename to packages/client/lib/commands/SISMEMBER.ts diff --git a/lib/commands/SMEMBERS.spec.ts b/packages/client/lib/commands/SMEMBERS.spec.ts similarity index 100% rename from lib/commands/SMEMBERS.spec.ts rename to packages/client/lib/commands/SMEMBERS.spec.ts diff --git a/lib/commands/SMEMBERS.ts b/packages/client/lib/commands/SMEMBERS.ts similarity index 100% rename from lib/commands/SMEMBERS.ts rename to packages/client/lib/commands/SMEMBERS.ts diff --git a/lib/commands/SMISMEMBER.spec.ts b/packages/client/lib/commands/SMISMEMBER.spec.ts similarity index 100% rename from lib/commands/SMISMEMBER.spec.ts rename to packages/client/lib/commands/SMISMEMBER.spec.ts diff --git a/lib/commands/SMISMEMBER.ts b/packages/client/lib/commands/SMISMEMBER.ts similarity index 100% rename from lib/commands/SMISMEMBER.ts rename to packages/client/lib/commands/SMISMEMBER.ts diff --git a/lib/commands/SMOVE.spec.ts b/packages/client/lib/commands/SMOVE.spec.ts similarity index 100% rename from lib/commands/SMOVE.spec.ts rename to packages/client/lib/commands/SMOVE.spec.ts diff --git a/lib/commands/SMOVE.ts b/packages/client/lib/commands/SMOVE.ts similarity index 100% rename from lib/commands/SMOVE.ts rename to packages/client/lib/commands/SMOVE.ts diff --git a/lib/commands/SORT.spec.ts b/packages/client/lib/commands/SORT.spec.ts similarity index 100% rename from lib/commands/SORT.spec.ts rename to packages/client/lib/commands/SORT.spec.ts diff --git a/lib/commands/SORT.ts b/packages/client/lib/commands/SORT.ts similarity index 100% rename from lib/commands/SORT.ts rename to packages/client/lib/commands/SORT.ts diff --git a/lib/commands/SPOP.spec.ts b/packages/client/lib/commands/SPOP.spec.ts similarity index 100% rename from lib/commands/SPOP.spec.ts rename to packages/client/lib/commands/SPOP.spec.ts diff --git a/lib/commands/SPOP.ts b/packages/client/lib/commands/SPOP.ts similarity index 100% rename from lib/commands/SPOP.ts rename to packages/client/lib/commands/SPOP.ts diff --git a/lib/commands/SRANDMEMBER.spec.ts b/packages/client/lib/commands/SRANDMEMBER.spec.ts similarity index 100% rename from lib/commands/SRANDMEMBER.spec.ts rename to packages/client/lib/commands/SRANDMEMBER.spec.ts diff --git a/lib/commands/SRANDMEMBER.ts b/packages/client/lib/commands/SRANDMEMBER.ts similarity index 100% rename from lib/commands/SRANDMEMBER.ts rename to packages/client/lib/commands/SRANDMEMBER.ts diff --git a/lib/commands/SRANDMEMBER_COUNT.spec.ts b/packages/client/lib/commands/SRANDMEMBER_COUNT.spec.ts similarity index 100% rename from lib/commands/SRANDMEMBER_COUNT.spec.ts rename to packages/client/lib/commands/SRANDMEMBER_COUNT.spec.ts diff --git a/lib/commands/SRANDMEMBER_COUNT.ts b/packages/client/lib/commands/SRANDMEMBER_COUNT.ts similarity index 100% rename from lib/commands/SRANDMEMBER_COUNT.ts rename to packages/client/lib/commands/SRANDMEMBER_COUNT.ts diff --git a/lib/commands/SREM.spec.ts b/packages/client/lib/commands/SREM.spec.ts similarity index 100% rename from lib/commands/SREM.spec.ts rename to packages/client/lib/commands/SREM.spec.ts diff --git a/lib/commands/SREM.ts b/packages/client/lib/commands/SREM.ts similarity index 100% rename from lib/commands/SREM.ts rename to packages/client/lib/commands/SREM.ts diff --git a/lib/commands/SSCAN.spec.ts b/packages/client/lib/commands/SSCAN.spec.ts similarity index 100% rename from lib/commands/SSCAN.spec.ts rename to packages/client/lib/commands/SSCAN.spec.ts diff --git a/lib/commands/SSCAN.ts b/packages/client/lib/commands/SSCAN.ts similarity index 100% rename from lib/commands/SSCAN.ts rename to packages/client/lib/commands/SSCAN.ts diff --git a/lib/commands/STRLEN.spec.ts b/packages/client/lib/commands/STRLEN.spec.ts similarity index 100% rename from lib/commands/STRLEN.spec.ts rename to packages/client/lib/commands/STRLEN.spec.ts diff --git a/lib/commands/STRLEN.ts b/packages/client/lib/commands/STRLEN.ts similarity index 100% rename from lib/commands/STRLEN.ts rename to packages/client/lib/commands/STRLEN.ts diff --git a/lib/commands/SUNION.spec.ts b/packages/client/lib/commands/SUNION.spec.ts similarity index 100% rename from lib/commands/SUNION.spec.ts rename to packages/client/lib/commands/SUNION.spec.ts diff --git a/lib/commands/SUNION.ts b/packages/client/lib/commands/SUNION.ts similarity index 100% rename from lib/commands/SUNION.ts rename to packages/client/lib/commands/SUNION.ts diff --git a/lib/commands/SUNIONSTORE.spec.ts b/packages/client/lib/commands/SUNIONSTORE.spec.ts similarity index 100% rename from lib/commands/SUNIONSTORE.spec.ts rename to packages/client/lib/commands/SUNIONSTORE.spec.ts diff --git a/lib/commands/SUNIONSTORE.ts b/packages/client/lib/commands/SUNIONSTORE.ts similarity index 100% rename from lib/commands/SUNIONSTORE.ts rename to packages/client/lib/commands/SUNIONSTORE.ts diff --git a/lib/commands/SWAPDB.spec.ts b/packages/client/lib/commands/SWAPDB.spec.ts similarity index 100% rename from lib/commands/SWAPDB.spec.ts rename to packages/client/lib/commands/SWAPDB.spec.ts diff --git a/lib/commands/SWAPDB.ts b/packages/client/lib/commands/SWAPDB.ts similarity index 100% rename from lib/commands/SWAPDB.ts rename to packages/client/lib/commands/SWAPDB.ts diff --git a/lib/commands/TIME.spec.ts b/packages/client/lib/commands/TIME.spec.ts similarity index 55% rename from lib/commands/TIME.spec.ts rename to packages/client/lib/commands/TIME.spec.ts index bbaa7942db0..1139d18d537 100644 --- a/lib/commands/TIME.spec.ts +++ b/packages/client/lib/commands/TIME.spec.ts @@ -10,9 +10,9 @@ describe('TIME', () => { ); }); - testUtils.testWithClient('client.time', async client => { - const reply = await client.time(); - assert.ok(reply instanceof Date); - assert.ok(typeof reply.microseconds === 'number'); - }, GLOBAL.SERVERS.OPEN); + // testUtils.testWithClient('client.time', async client => { + // const reply = await client.time(); + // assert.ok(reply instanceof Date); + // assert.ok(typeof reply.microseconds === 'number'); + // }, GLOBAL.SERVERS.OPEN); }); diff --git a/lib/commands/TIME.ts b/packages/client/lib/commands/TIME.ts similarity index 100% rename from lib/commands/TIME.ts rename to packages/client/lib/commands/TIME.ts diff --git a/lib/commands/TOUCH.spec.ts b/packages/client/lib/commands/TOUCH.spec.ts similarity index 100% rename from lib/commands/TOUCH.spec.ts rename to packages/client/lib/commands/TOUCH.spec.ts diff --git a/lib/commands/TOUCH.ts b/packages/client/lib/commands/TOUCH.ts similarity index 100% rename from lib/commands/TOUCH.ts rename to packages/client/lib/commands/TOUCH.ts diff --git a/lib/commands/TTL.spec.ts b/packages/client/lib/commands/TTL.spec.ts similarity index 100% rename from lib/commands/TTL.spec.ts rename to packages/client/lib/commands/TTL.spec.ts diff --git a/lib/commands/TTL.ts b/packages/client/lib/commands/TTL.ts similarity index 100% rename from lib/commands/TTL.ts rename to packages/client/lib/commands/TTL.ts diff --git a/lib/commands/TYPE.spec.ts b/packages/client/lib/commands/TYPE.spec.ts similarity index 100% rename from lib/commands/TYPE.spec.ts rename to packages/client/lib/commands/TYPE.spec.ts diff --git a/lib/commands/TYPE.ts b/packages/client/lib/commands/TYPE.ts similarity index 100% rename from lib/commands/TYPE.ts rename to packages/client/lib/commands/TYPE.ts diff --git a/lib/commands/UNLINK.spec.ts b/packages/client/lib/commands/UNLINK.spec.ts similarity index 100% rename from lib/commands/UNLINK.spec.ts rename to packages/client/lib/commands/UNLINK.spec.ts diff --git a/lib/commands/UNLINK.ts b/packages/client/lib/commands/UNLINK.ts similarity index 100% rename from lib/commands/UNLINK.ts rename to packages/client/lib/commands/UNLINK.ts diff --git a/lib/commands/UNWATCH.spec.ts b/packages/client/lib/commands/UNWATCH.spec.ts similarity index 100% rename from lib/commands/UNWATCH.spec.ts rename to packages/client/lib/commands/UNWATCH.spec.ts diff --git a/lib/commands/UNWATCH.ts b/packages/client/lib/commands/UNWATCH.ts similarity index 100% rename from lib/commands/UNWATCH.ts rename to packages/client/lib/commands/UNWATCH.ts diff --git a/lib/commands/WAIT.spec.ts b/packages/client/lib/commands/WAIT.spec.ts similarity index 100% rename from lib/commands/WAIT.spec.ts rename to packages/client/lib/commands/WAIT.spec.ts diff --git a/lib/commands/WAIT.ts b/packages/client/lib/commands/WAIT.ts similarity index 100% rename from lib/commands/WAIT.ts rename to packages/client/lib/commands/WAIT.ts diff --git a/lib/commands/WATCH.spec.ts b/packages/client/lib/commands/WATCH.spec.ts similarity index 100% rename from lib/commands/WATCH.spec.ts rename to packages/client/lib/commands/WATCH.spec.ts diff --git a/lib/commands/WATCH.ts b/packages/client/lib/commands/WATCH.ts similarity index 100% rename from lib/commands/WATCH.ts rename to packages/client/lib/commands/WATCH.ts diff --git a/lib/commands/XACK.spec.ts b/packages/client/lib/commands/XACK.spec.ts similarity index 100% rename from lib/commands/XACK.spec.ts rename to packages/client/lib/commands/XACK.spec.ts diff --git a/lib/commands/XACK.ts b/packages/client/lib/commands/XACK.ts similarity index 100% rename from lib/commands/XACK.ts rename to packages/client/lib/commands/XACK.ts diff --git a/lib/commands/XADD.spec.ts b/packages/client/lib/commands/XADD.spec.ts similarity index 100% rename from lib/commands/XADD.spec.ts rename to packages/client/lib/commands/XADD.spec.ts diff --git a/lib/commands/XADD.ts b/packages/client/lib/commands/XADD.ts similarity index 100% rename from lib/commands/XADD.ts rename to packages/client/lib/commands/XADD.ts diff --git a/lib/commands/XAUTOCLAIM.spec.ts b/packages/client/lib/commands/XAUTOCLAIM.spec.ts similarity index 100% rename from lib/commands/XAUTOCLAIM.spec.ts rename to packages/client/lib/commands/XAUTOCLAIM.spec.ts diff --git a/lib/commands/XAUTOCLAIM.ts b/packages/client/lib/commands/XAUTOCLAIM.ts similarity index 100% rename from lib/commands/XAUTOCLAIM.ts rename to packages/client/lib/commands/XAUTOCLAIM.ts diff --git a/lib/commands/XAUTOCLAIM_JUSTID.spec.ts b/packages/client/lib/commands/XAUTOCLAIM_JUSTID.spec.ts similarity index 100% rename from lib/commands/XAUTOCLAIM_JUSTID.spec.ts rename to packages/client/lib/commands/XAUTOCLAIM_JUSTID.spec.ts diff --git a/lib/commands/XAUTOCLAIM_JUSTID.ts b/packages/client/lib/commands/XAUTOCLAIM_JUSTID.ts similarity index 100% rename from lib/commands/XAUTOCLAIM_JUSTID.ts rename to packages/client/lib/commands/XAUTOCLAIM_JUSTID.ts diff --git a/lib/commands/XCLAIM.spec.ts b/packages/client/lib/commands/XCLAIM.spec.ts similarity index 100% rename from lib/commands/XCLAIM.spec.ts rename to packages/client/lib/commands/XCLAIM.spec.ts diff --git a/lib/commands/XCLAIM.ts b/packages/client/lib/commands/XCLAIM.ts similarity index 100% rename from lib/commands/XCLAIM.ts rename to packages/client/lib/commands/XCLAIM.ts diff --git a/lib/commands/XCLAIM_JUSTID.spec.ts b/packages/client/lib/commands/XCLAIM_JUSTID.spec.ts similarity index 100% rename from lib/commands/XCLAIM_JUSTID.spec.ts rename to packages/client/lib/commands/XCLAIM_JUSTID.spec.ts diff --git a/lib/commands/XCLAIM_JUSTID.ts b/packages/client/lib/commands/XCLAIM_JUSTID.ts similarity index 100% rename from lib/commands/XCLAIM_JUSTID.ts rename to packages/client/lib/commands/XCLAIM_JUSTID.ts diff --git a/lib/commands/XDEL.spec.ts b/packages/client/lib/commands/XDEL.spec.ts similarity index 100% rename from lib/commands/XDEL.spec.ts rename to packages/client/lib/commands/XDEL.spec.ts diff --git a/lib/commands/XDEL.ts b/packages/client/lib/commands/XDEL.ts similarity index 100% rename from lib/commands/XDEL.ts rename to packages/client/lib/commands/XDEL.ts diff --git a/lib/commands/XGROUP_CREATE.spec.ts b/packages/client/lib/commands/XGROUP_CREATE.spec.ts similarity index 100% rename from lib/commands/XGROUP_CREATE.spec.ts rename to packages/client/lib/commands/XGROUP_CREATE.spec.ts diff --git a/lib/commands/XGROUP_CREATE.ts b/packages/client/lib/commands/XGROUP_CREATE.ts similarity index 100% rename from lib/commands/XGROUP_CREATE.ts rename to packages/client/lib/commands/XGROUP_CREATE.ts diff --git a/lib/commands/XGROUP_CREATECONSUMER.spec.ts b/packages/client/lib/commands/XGROUP_CREATECONSUMER.spec.ts similarity index 100% rename from lib/commands/XGROUP_CREATECONSUMER.spec.ts rename to packages/client/lib/commands/XGROUP_CREATECONSUMER.spec.ts diff --git a/lib/commands/XGROUP_CREATECONSUMER.ts b/packages/client/lib/commands/XGROUP_CREATECONSUMER.ts similarity index 100% rename from lib/commands/XGROUP_CREATECONSUMER.ts rename to packages/client/lib/commands/XGROUP_CREATECONSUMER.ts diff --git a/lib/commands/XGROUP_DELCONSUMER.spec.ts b/packages/client/lib/commands/XGROUP_DELCONSUMER.spec.ts similarity index 100% rename from lib/commands/XGROUP_DELCONSUMER.spec.ts rename to packages/client/lib/commands/XGROUP_DELCONSUMER.spec.ts diff --git a/lib/commands/XGROUP_DELCONSUMER.ts b/packages/client/lib/commands/XGROUP_DELCONSUMER.ts similarity index 100% rename from lib/commands/XGROUP_DELCONSUMER.ts rename to packages/client/lib/commands/XGROUP_DELCONSUMER.ts diff --git a/lib/commands/XGROUP_DESTROY.spec.ts b/packages/client/lib/commands/XGROUP_DESTROY.spec.ts similarity index 100% rename from lib/commands/XGROUP_DESTROY.spec.ts rename to packages/client/lib/commands/XGROUP_DESTROY.spec.ts diff --git a/lib/commands/XGROUP_DESTROY.ts b/packages/client/lib/commands/XGROUP_DESTROY.ts similarity index 100% rename from lib/commands/XGROUP_DESTROY.ts rename to packages/client/lib/commands/XGROUP_DESTROY.ts diff --git a/lib/commands/XGROUP_SETID.spec.ts b/packages/client/lib/commands/XGROUP_SETID.spec.ts similarity index 100% rename from lib/commands/XGROUP_SETID.spec.ts rename to packages/client/lib/commands/XGROUP_SETID.spec.ts diff --git a/lib/commands/XGROUP_SETID.ts b/packages/client/lib/commands/XGROUP_SETID.ts similarity index 100% rename from lib/commands/XGROUP_SETID.ts rename to packages/client/lib/commands/XGROUP_SETID.ts diff --git a/lib/commands/XINFO_CONSUMERS.spec.ts b/packages/client/lib/commands/XINFO_CONSUMERS.spec.ts similarity index 100% rename from lib/commands/XINFO_CONSUMERS.spec.ts rename to packages/client/lib/commands/XINFO_CONSUMERS.spec.ts diff --git a/lib/commands/XINFO_CONSUMERS.ts b/packages/client/lib/commands/XINFO_CONSUMERS.ts similarity index 100% rename from lib/commands/XINFO_CONSUMERS.ts rename to packages/client/lib/commands/XINFO_CONSUMERS.ts diff --git a/lib/commands/XINFO_GROUPS.spec.ts b/packages/client/lib/commands/XINFO_GROUPS.spec.ts similarity index 100% rename from lib/commands/XINFO_GROUPS.spec.ts rename to packages/client/lib/commands/XINFO_GROUPS.spec.ts diff --git a/lib/commands/XINFO_GROUPS.ts b/packages/client/lib/commands/XINFO_GROUPS.ts similarity index 100% rename from lib/commands/XINFO_GROUPS.ts rename to packages/client/lib/commands/XINFO_GROUPS.ts diff --git a/lib/commands/XINFO_STREAM.spec.ts b/packages/client/lib/commands/XINFO_STREAM.spec.ts similarity index 100% rename from lib/commands/XINFO_STREAM.spec.ts rename to packages/client/lib/commands/XINFO_STREAM.spec.ts diff --git a/lib/commands/XINFO_STREAM.ts b/packages/client/lib/commands/XINFO_STREAM.ts similarity index 100% rename from lib/commands/XINFO_STREAM.ts rename to packages/client/lib/commands/XINFO_STREAM.ts diff --git a/lib/commands/XLEN.spec.ts b/packages/client/lib/commands/XLEN.spec.ts similarity index 100% rename from lib/commands/XLEN.spec.ts rename to packages/client/lib/commands/XLEN.spec.ts diff --git a/lib/commands/XLEN.ts b/packages/client/lib/commands/XLEN.ts similarity index 100% rename from lib/commands/XLEN.ts rename to packages/client/lib/commands/XLEN.ts diff --git a/lib/commands/XPENDING.spec.ts b/packages/client/lib/commands/XPENDING.spec.ts similarity index 100% rename from lib/commands/XPENDING.spec.ts rename to packages/client/lib/commands/XPENDING.spec.ts diff --git a/lib/commands/XPENDING.ts b/packages/client/lib/commands/XPENDING.ts similarity index 100% rename from lib/commands/XPENDING.ts rename to packages/client/lib/commands/XPENDING.ts diff --git a/lib/commands/XPENDING_RANGE.spec.ts b/packages/client/lib/commands/XPENDING_RANGE.spec.ts similarity index 100% rename from lib/commands/XPENDING_RANGE.spec.ts rename to packages/client/lib/commands/XPENDING_RANGE.spec.ts diff --git a/lib/commands/XPENDING_RANGE.ts b/packages/client/lib/commands/XPENDING_RANGE.ts similarity index 100% rename from lib/commands/XPENDING_RANGE.ts rename to packages/client/lib/commands/XPENDING_RANGE.ts diff --git a/lib/commands/XRANGE.spec.ts b/packages/client/lib/commands/XRANGE.spec.ts similarity index 100% rename from lib/commands/XRANGE.spec.ts rename to packages/client/lib/commands/XRANGE.spec.ts diff --git a/lib/commands/XRANGE.ts b/packages/client/lib/commands/XRANGE.ts similarity index 100% rename from lib/commands/XRANGE.ts rename to packages/client/lib/commands/XRANGE.ts diff --git a/lib/commands/XREAD.spec.ts b/packages/client/lib/commands/XREAD.spec.ts similarity index 100% rename from lib/commands/XREAD.spec.ts rename to packages/client/lib/commands/XREAD.spec.ts diff --git a/lib/commands/XREAD.ts b/packages/client/lib/commands/XREAD.ts similarity index 100% rename from lib/commands/XREAD.ts rename to packages/client/lib/commands/XREAD.ts diff --git a/lib/commands/XREADGROUP.spec.ts b/packages/client/lib/commands/XREADGROUP.spec.ts similarity index 100% rename from lib/commands/XREADGROUP.spec.ts rename to packages/client/lib/commands/XREADGROUP.spec.ts diff --git a/lib/commands/XREADGROUP.ts b/packages/client/lib/commands/XREADGROUP.ts similarity index 100% rename from lib/commands/XREADGROUP.ts rename to packages/client/lib/commands/XREADGROUP.ts diff --git a/lib/commands/XREVRANGE.spec.ts b/packages/client/lib/commands/XREVRANGE.spec.ts similarity index 100% rename from lib/commands/XREVRANGE.spec.ts rename to packages/client/lib/commands/XREVRANGE.spec.ts diff --git a/lib/commands/XREVRANGE.ts b/packages/client/lib/commands/XREVRANGE.ts similarity index 100% rename from lib/commands/XREVRANGE.ts rename to packages/client/lib/commands/XREVRANGE.ts diff --git a/lib/commands/XTRIM.spec.ts b/packages/client/lib/commands/XTRIM.spec.ts similarity index 100% rename from lib/commands/XTRIM.spec.ts rename to packages/client/lib/commands/XTRIM.spec.ts diff --git a/lib/commands/XTRIM.ts b/packages/client/lib/commands/XTRIM.ts similarity index 100% rename from lib/commands/XTRIM.ts rename to packages/client/lib/commands/XTRIM.ts diff --git a/lib/commands/ZADD.spec.ts b/packages/client/lib/commands/ZADD.spec.ts similarity index 100% rename from lib/commands/ZADD.spec.ts rename to packages/client/lib/commands/ZADD.spec.ts diff --git a/lib/commands/ZADD.ts b/packages/client/lib/commands/ZADD.ts similarity index 100% rename from lib/commands/ZADD.ts rename to packages/client/lib/commands/ZADD.ts diff --git a/lib/commands/ZCARD.spec.ts b/packages/client/lib/commands/ZCARD.spec.ts similarity index 100% rename from lib/commands/ZCARD.spec.ts rename to packages/client/lib/commands/ZCARD.spec.ts diff --git a/lib/commands/ZCARD.ts b/packages/client/lib/commands/ZCARD.ts similarity index 100% rename from lib/commands/ZCARD.ts rename to packages/client/lib/commands/ZCARD.ts diff --git a/lib/commands/ZCOUNT.spec.ts b/packages/client/lib/commands/ZCOUNT.spec.ts similarity index 100% rename from lib/commands/ZCOUNT.spec.ts rename to packages/client/lib/commands/ZCOUNT.spec.ts diff --git a/lib/commands/ZCOUNT.ts b/packages/client/lib/commands/ZCOUNT.ts similarity index 100% rename from lib/commands/ZCOUNT.ts rename to packages/client/lib/commands/ZCOUNT.ts diff --git a/lib/commands/ZDIFF.spec.ts b/packages/client/lib/commands/ZDIFF.spec.ts similarity index 100% rename from lib/commands/ZDIFF.spec.ts rename to packages/client/lib/commands/ZDIFF.spec.ts diff --git a/lib/commands/ZDIFF.ts b/packages/client/lib/commands/ZDIFF.ts similarity index 100% rename from lib/commands/ZDIFF.ts rename to packages/client/lib/commands/ZDIFF.ts diff --git a/lib/commands/ZDIFFSTORE.spec.ts b/packages/client/lib/commands/ZDIFFSTORE.spec.ts similarity index 100% rename from lib/commands/ZDIFFSTORE.spec.ts rename to packages/client/lib/commands/ZDIFFSTORE.spec.ts diff --git a/lib/commands/ZDIFFSTORE.ts b/packages/client/lib/commands/ZDIFFSTORE.ts similarity index 100% rename from lib/commands/ZDIFFSTORE.ts rename to packages/client/lib/commands/ZDIFFSTORE.ts diff --git a/lib/commands/ZDIFF_WITHSCORES.spec.ts b/packages/client/lib/commands/ZDIFF_WITHSCORES.spec.ts similarity index 100% rename from lib/commands/ZDIFF_WITHSCORES.spec.ts rename to packages/client/lib/commands/ZDIFF_WITHSCORES.spec.ts diff --git a/lib/commands/ZDIFF_WITHSCORES.ts b/packages/client/lib/commands/ZDIFF_WITHSCORES.ts similarity index 100% rename from lib/commands/ZDIFF_WITHSCORES.ts rename to packages/client/lib/commands/ZDIFF_WITHSCORES.ts diff --git a/lib/commands/ZINCRBY.spec.ts b/packages/client/lib/commands/ZINCRBY.spec.ts similarity index 100% rename from lib/commands/ZINCRBY.spec.ts rename to packages/client/lib/commands/ZINCRBY.spec.ts diff --git a/lib/commands/ZINCRBY.ts b/packages/client/lib/commands/ZINCRBY.ts similarity index 100% rename from lib/commands/ZINCRBY.ts rename to packages/client/lib/commands/ZINCRBY.ts diff --git a/lib/commands/ZINTER.spec.ts b/packages/client/lib/commands/ZINTER.spec.ts similarity index 100% rename from lib/commands/ZINTER.spec.ts rename to packages/client/lib/commands/ZINTER.spec.ts diff --git a/lib/commands/ZINTER.ts b/packages/client/lib/commands/ZINTER.ts similarity index 100% rename from lib/commands/ZINTER.ts rename to packages/client/lib/commands/ZINTER.ts diff --git a/lib/commands/ZINTERSTORE.spec.ts b/packages/client/lib/commands/ZINTERSTORE.spec.ts similarity index 100% rename from lib/commands/ZINTERSTORE.spec.ts rename to packages/client/lib/commands/ZINTERSTORE.spec.ts diff --git a/lib/commands/ZINTERSTORE.ts b/packages/client/lib/commands/ZINTERSTORE.ts similarity index 100% rename from lib/commands/ZINTERSTORE.ts rename to packages/client/lib/commands/ZINTERSTORE.ts diff --git a/lib/commands/ZINTER_WITHSCORES.spec.ts b/packages/client/lib/commands/ZINTER_WITHSCORES.spec.ts similarity index 100% rename from lib/commands/ZINTER_WITHSCORES.spec.ts rename to packages/client/lib/commands/ZINTER_WITHSCORES.spec.ts diff --git a/lib/commands/ZINTER_WITHSCORES.ts b/packages/client/lib/commands/ZINTER_WITHSCORES.ts similarity index 100% rename from lib/commands/ZINTER_WITHSCORES.ts rename to packages/client/lib/commands/ZINTER_WITHSCORES.ts diff --git a/lib/commands/ZLEXCOUNT.spec.ts b/packages/client/lib/commands/ZLEXCOUNT.spec.ts similarity index 100% rename from lib/commands/ZLEXCOUNT.spec.ts rename to packages/client/lib/commands/ZLEXCOUNT.spec.ts diff --git a/lib/commands/ZLEXCOUNT.ts b/packages/client/lib/commands/ZLEXCOUNT.ts similarity index 100% rename from lib/commands/ZLEXCOUNT.ts rename to packages/client/lib/commands/ZLEXCOUNT.ts diff --git a/lib/commands/ZMSCORE.spec.ts b/packages/client/lib/commands/ZMSCORE.spec.ts similarity index 100% rename from lib/commands/ZMSCORE.spec.ts rename to packages/client/lib/commands/ZMSCORE.spec.ts diff --git a/lib/commands/ZMSCORE.ts b/packages/client/lib/commands/ZMSCORE.ts similarity index 100% rename from lib/commands/ZMSCORE.ts rename to packages/client/lib/commands/ZMSCORE.ts diff --git a/lib/commands/ZPOPMAX.spec.ts b/packages/client/lib/commands/ZPOPMAX.spec.ts similarity index 100% rename from lib/commands/ZPOPMAX.spec.ts rename to packages/client/lib/commands/ZPOPMAX.spec.ts diff --git a/lib/commands/ZPOPMAX.ts b/packages/client/lib/commands/ZPOPMAX.ts similarity index 100% rename from lib/commands/ZPOPMAX.ts rename to packages/client/lib/commands/ZPOPMAX.ts diff --git a/lib/commands/ZPOPMAX_COUNT.spec.ts b/packages/client/lib/commands/ZPOPMAX_COUNT.spec.ts similarity index 100% rename from lib/commands/ZPOPMAX_COUNT.spec.ts rename to packages/client/lib/commands/ZPOPMAX_COUNT.spec.ts diff --git a/lib/commands/ZPOPMAX_COUNT.ts b/packages/client/lib/commands/ZPOPMAX_COUNT.ts similarity index 100% rename from lib/commands/ZPOPMAX_COUNT.ts rename to packages/client/lib/commands/ZPOPMAX_COUNT.ts diff --git a/lib/commands/ZPOPMIN.spec.ts b/packages/client/lib/commands/ZPOPMIN.spec.ts similarity index 100% rename from lib/commands/ZPOPMIN.spec.ts rename to packages/client/lib/commands/ZPOPMIN.spec.ts diff --git a/lib/commands/ZPOPMIN.ts b/packages/client/lib/commands/ZPOPMIN.ts similarity index 100% rename from lib/commands/ZPOPMIN.ts rename to packages/client/lib/commands/ZPOPMIN.ts diff --git a/lib/commands/ZPOPMIN_COUNT.spec.ts b/packages/client/lib/commands/ZPOPMIN_COUNT.spec.ts similarity index 100% rename from lib/commands/ZPOPMIN_COUNT.spec.ts rename to packages/client/lib/commands/ZPOPMIN_COUNT.spec.ts diff --git a/lib/commands/ZPOPMIN_COUNT.ts b/packages/client/lib/commands/ZPOPMIN_COUNT.ts similarity index 100% rename from lib/commands/ZPOPMIN_COUNT.ts rename to packages/client/lib/commands/ZPOPMIN_COUNT.ts diff --git a/lib/commands/ZRANDMEMBER.spec.ts b/packages/client/lib/commands/ZRANDMEMBER.spec.ts similarity index 100% rename from lib/commands/ZRANDMEMBER.spec.ts rename to packages/client/lib/commands/ZRANDMEMBER.spec.ts diff --git a/lib/commands/ZRANDMEMBER.ts b/packages/client/lib/commands/ZRANDMEMBER.ts similarity index 100% rename from lib/commands/ZRANDMEMBER.ts rename to packages/client/lib/commands/ZRANDMEMBER.ts diff --git a/lib/commands/ZRANDMEMBER_COUNT.spec.ts b/packages/client/lib/commands/ZRANDMEMBER_COUNT.spec.ts similarity index 100% rename from lib/commands/ZRANDMEMBER_COUNT.spec.ts rename to packages/client/lib/commands/ZRANDMEMBER_COUNT.spec.ts diff --git a/lib/commands/ZRANDMEMBER_COUNT.ts b/packages/client/lib/commands/ZRANDMEMBER_COUNT.ts similarity index 100% rename from lib/commands/ZRANDMEMBER_COUNT.ts rename to packages/client/lib/commands/ZRANDMEMBER_COUNT.ts diff --git a/lib/commands/ZRANDMEMBER_COUNT_WITHSCORES.spec.ts b/packages/client/lib/commands/ZRANDMEMBER_COUNT_WITHSCORES.spec.ts similarity index 100% rename from lib/commands/ZRANDMEMBER_COUNT_WITHSCORES.spec.ts rename to packages/client/lib/commands/ZRANDMEMBER_COUNT_WITHSCORES.spec.ts diff --git a/lib/commands/ZRANDMEMBER_COUNT_WITHSCORES.ts b/packages/client/lib/commands/ZRANDMEMBER_COUNT_WITHSCORES.ts similarity index 100% rename from lib/commands/ZRANDMEMBER_COUNT_WITHSCORES.ts rename to packages/client/lib/commands/ZRANDMEMBER_COUNT_WITHSCORES.ts diff --git a/lib/commands/ZRANGE.spec.ts b/packages/client/lib/commands/ZRANGE.spec.ts similarity index 100% rename from lib/commands/ZRANGE.spec.ts rename to packages/client/lib/commands/ZRANGE.spec.ts diff --git a/lib/commands/ZRANGE.ts b/packages/client/lib/commands/ZRANGE.ts similarity index 100% rename from lib/commands/ZRANGE.ts rename to packages/client/lib/commands/ZRANGE.ts diff --git a/lib/commands/ZRANGEBYLEX.spec.ts b/packages/client/lib/commands/ZRANGEBYLEX.spec.ts similarity index 100% rename from lib/commands/ZRANGEBYLEX.spec.ts rename to packages/client/lib/commands/ZRANGEBYLEX.spec.ts diff --git a/lib/commands/ZRANGEBYLEX.ts b/packages/client/lib/commands/ZRANGEBYLEX.ts similarity index 100% rename from lib/commands/ZRANGEBYLEX.ts rename to packages/client/lib/commands/ZRANGEBYLEX.ts diff --git a/lib/commands/ZRANGEBYSCORE.spec.ts b/packages/client/lib/commands/ZRANGEBYSCORE.spec.ts similarity index 100% rename from lib/commands/ZRANGEBYSCORE.spec.ts rename to packages/client/lib/commands/ZRANGEBYSCORE.spec.ts diff --git a/lib/commands/ZRANGEBYSCORE.ts b/packages/client/lib/commands/ZRANGEBYSCORE.ts similarity index 100% rename from lib/commands/ZRANGEBYSCORE.ts rename to packages/client/lib/commands/ZRANGEBYSCORE.ts diff --git a/lib/commands/ZRANGEBYSCORE_WITHSCORES.spec.ts b/packages/client/lib/commands/ZRANGEBYSCORE_WITHSCORES.spec.ts similarity index 100% rename from lib/commands/ZRANGEBYSCORE_WITHSCORES.spec.ts rename to packages/client/lib/commands/ZRANGEBYSCORE_WITHSCORES.spec.ts diff --git a/lib/commands/ZRANGEBYSCORE_WITHSCORES.ts b/packages/client/lib/commands/ZRANGEBYSCORE_WITHSCORES.ts similarity index 100% rename from lib/commands/ZRANGEBYSCORE_WITHSCORES.ts rename to packages/client/lib/commands/ZRANGEBYSCORE_WITHSCORES.ts diff --git a/lib/commands/ZRANGESTORE.spec.ts b/packages/client/lib/commands/ZRANGESTORE.spec.ts similarity index 100% rename from lib/commands/ZRANGESTORE.spec.ts rename to packages/client/lib/commands/ZRANGESTORE.spec.ts diff --git a/lib/commands/ZRANGESTORE.ts b/packages/client/lib/commands/ZRANGESTORE.ts similarity index 100% rename from lib/commands/ZRANGESTORE.ts rename to packages/client/lib/commands/ZRANGESTORE.ts diff --git a/lib/commands/ZRANGE_WITHSCORES.spec.ts b/packages/client/lib/commands/ZRANGE_WITHSCORES.spec.ts similarity index 100% rename from lib/commands/ZRANGE_WITHSCORES.spec.ts rename to packages/client/lib/commands/ZRANGE_WITHSCORES.spec.ts diff --git a/lib/commands/ZRANGE_WITHSCORES.ts b/packages/client/lib/commands/ZRANGE_WITHSCORES.ts similarity index 100% rename from lib/commands/ZRANGE_WITHSCORES.ts rename to packages/client/lib/commands/ZRANGE_WITHSCORES.ts diff --git a/lib/commands/ZRANK.spec.ts b/packages/client/lib/commands/ZRANK.spec.ts similarity index 100% rename from lib/commands/ZRANK.spec.ts rename to packages/client/lib/commands/ZRANK.spec.ts diff --git a/lib/commands/ZRANK.ts b/packages/client/lib/commands/ZRANK.ts similarity index 100% rename from lib/commands/ZRANK.ts rename to packages/client/lib/commands/ZRANK.ts diff --git a/lib/commands/ZREM.spec.ts b/packages/client/lib/commands/ZREM.spec.ts similarity index 100% rename from lib/commands/ZREM.spec.ts rename to packages/client/lib/commands/ZREM.spec.ts diff --git a/lib/commands/ZREM.ts b/packages/client/lib/commands/ZREM.ts similarity index 100% rename from lib/commands/ZREM.ts rename to packages/client/lib/commands/ZREM.ts diff --git a/lib/commands/ZREMRANGEBYLEX.spec.ts b/packages/client/lib/commands/ZREMRANGEBYLEX.spec.ts similarity index 100% rename from lib/commands/ZREMRANGEBYLEX.spec.ts rename to packages/client/lib/commands/ZREMRANGEBYLEX.spec.ts diff --git a/lib/commands/ZREMRANGEBYLEX.ts b/packages/client/lib/commands/ZREMRANGEBYLEX.ts similarity index 100% rename from lib/commands/ZREMRANGEBYLEX.ts rename to packages/client/lib/commands/ZREMRANGEBYLEX.ts diff --git a/lib/commands/ZREMRANGEBYRANK.spec.ts b/packages/client/lib/commands/ZREMRANGEBYRANK.spec.ts similarity index 100% rename from lib/commands/ZREMRANGEBYRANK.spec.ts rename to packages/client/lib/commands/ZREMRANGEBYRANK.spec.ts diff --git a/lib/commands/ZREMRANGEBYRANK.ts b/packages/client/lib/commands/ZREMRANGEBYRANK.ts similarity index 100% rename from lib/commands/ZREMRANGEBYRANK.ts rename to packages/client/lib/commands/ZREMRANGEBYRANK.ts diff --git a/lib/commands/ZREMRANGEBYSCORE.spec.ts b/packages/client/lib/commands/ZREMRANGEBYSCORE.spec.ts similarity index 100% rename from lib/commands/ZREMRANGEBYSCORE.spec.ts rename to packages/client/lib/commands/ZREMRANGEBYSCORE.spec.ts diff --git a/lib/commands/ZREMRANGEBYSCORE.ts b/packages/client/lib/commands/ZREMRANGEBYSCORE.ts similarity index 100% rename from lib/commands/ZREMRANGEBYSCORE.ts rename to packages/client/lib/commands/ZREMRANGEBYSCORE.ts diff --git a/lib/commands/ZREVRANK.spec.ts b/packages/client/lib/commands/ZREVRANK.spec.ts similarity index 100% rename from lib/commands/ZREVRANK.spec.ts rename to packages/client/lib/commands/ZREVRANK.spec.ts diff --git a/lib/commands/ZREVRANK.ts b/packages/client/lib/commands/ZREVRANK.ts similarity index 100% rename from lib/commands/ZREVRANK.ts rename to packages/client/lib/commands/ZREVRANK.ts diff --git a/lib/commands/ZSCAN.spec.ts b/packages/client/lib/commands/ZSCAN.spec.ts similarity index 100% rename from lib/commands/ZSCAN.spec.ts rename to packages/client/lib/commands/ZSCAN.spec.ts diff --git a/lib/commands/ZSCAN.ts b/packages/client/lib/commands/ZSCAN.ts similarity index 100% rename from lib/commands/ZSCAN.ts rename to packages/client/lib/commands/ZSCAN.ts diff --git a/lib/commands/ZSCORE.spec.ts b/packages/client/lib/commands/ZSCORE.spec.ts similarity index 100% rename from lib/commands/ZSCORE.spec.ts rename to packages/client/lib/commands/ZSCORE.spec.ts diff --git a/lib/commands/ZSCORE.ts b/packages/client/lib/commands/ZSCORE.ts similarity index 100% rename from lib/commands/ZSCORE.ts rename to packages/client/lib/commands/ZSCORE.ts diff --git a/lib/commands/ZUNION.spec.ts b/packages/client/lib/commands/ZUNION.spec.ts similarity index 100% rename from lib/commands/ZUNION.spec.ts rename to packages/client/lib/commands/ZUNION.spec.ts diff --git a/lib/commands/ZUNION.ts b/packages/client/lib/commands/ZUNION.ts similarity index 100% rename from lib/commands/ZUNION.ts rename to packages/client/lib/commands/ZUNION.ts diff --git a/lib/commands/ZUNIONSTORE.spec.ts b/packages/client/lib/commands/ZUNIONSTORE.spec.ts similarity index 100% rename from lib/commands/ZUNIONSTORE.spec.ts rename to packages/client/lib/commands/ZUNIONSTORE.spec.ts diff --git a/lib/commands/ZUNIONSTORE.ts b/packages/client/lib/commands/ZUNIONSTORE.ts similarity index 100% rename from lib/commands/ZUNIONSTORE.ts rename to packages/client/lib/commands/ZUNIONSTORE.ts diff --git a/lib/commands/ZUNION_WITHSCORES.spec.ts b/packages/client/lib/commands/ZUNION_WITHSCORES.spec.ts similarity index 100% rename from lib/commands/ZUNION_WITHSCORES.spec.ts rename to packages/client/lib/commands/ZUNION_WITHSCORES.spec.ts diff --git a/lib/commands/ZUNION_WITHSCORES.ts b/packages/client/lib/commands/ZUNION_WITHSCORES.ts similarity index 100% rename from lib/commands/ZUNION_WITHSCORES.ts rename to packages/client/lib/commands/ZUNION_WITHSCORES.ts diff --git a/lib/commands/generic-transformers.spec.ts b/packages/client/lib/commands/generic-transformers.spec.ts similarity index 100% rename from lib/commands/generic-transformers.spec.ts rename to packages/client/lib/commands/generic-transformers.spec.ts diff --git a/lib/commands/generic-transformers.ts b/packages/client/lib/commands/generic-transformers.ts similarity index 100% rename from lib/commands/generic-transformers.ts rename to packages/client/lib/commands/generic-transformers.ts diff --git a/lib/commands/index.ts b/packages/client/lib/commands/index.ts similarity index 100% rename from lib/commands/index.ts rename to packages/client/lib/commands/index.ts diff --git a/lib/errors.ts b/packages/client/lib/errors.ts similarity index 100% rename from lib/errors.ts rename to packages/client/lib/errors.ts diff --git a/lib/lua-script.ts b/packages/client/lib/lua-script.ts similarity index 100% rename from lib/lua-script.ts rename to packages/client/lib/lua-script.ts diff --git a/lib/multi-command.spec.ts b/packages/client/lib/multi-command.spec.ts similarity index 100% rename from lib/multi-command.spec.ts rename to packages/client/lib/multi-command.spec.ts diff --git a/lib/multi-command.ts b/packages/client/lib/multi-command.ts similarity index 100% rename from lib/multi-command.ts rename to packages/client/lib/multi-command.ts diff --git a/lib/test-utils/index.ts b/packages/client/lib/test-utils.ts similarity index 93% rename from lib/test-utils/index.ts rename to packages/client/lib/test-utils.ts index 69bfc440c91..85057da841e 100644 --- a/lib/test-utils/index.ts +++ b/packages/client/lib/test-utils.ts @@ -1,6 +1,6 @@ -import TestUtils from './test-utils'; +import TestUtils from '@redis/test-utils'; import { SinonSpy } from 'sinon'; -import { promiseTimeout } from '../utils'; +import { promiseTimeout } from './utils'; export default new TestUtils({ defaultDockerVersion: '6.2', diff --git a/lib/ts-declarations/cluster-key-slot.d.ts b/packages/client/lib/ts-declarations/cluster-key-slot.d.ts similarity index 100% rename from lib/ts-declarations/cluster-key-slot.d.ts rename to packages/client/lib/ts-declarations/cluster-key-slot.d.ts diff --git a/lib/ts-declarations/redis-parser.d.ts b/packages/client/lib/ts-declarations/redis-parser.d.ts similarity index 100% rename from lib/ts-declarations/redis-parser.d.ts rename to packages/client/lib/ts-declarations/redis-parser.d.ts diff --git a/lib/utils.ts b/packages/client/lib/utils.ts similarity index 100% rename from lib/utils.ts rename to packages/client/lib/utils.ts diff --git a/packages/client/package.json b/packages/client/package.json new file mode 100644 index 00000000000..30be25d26af --- /dev/null +++ b/packages/client/package.json @@ -0,0 +1,49 @@ +{ + "name": "@redis/client", + "version": "4.0.0-rc.3", + "license": "MIT", + "main": "./dist/index.js", + "types": "./dist/index.d.ts", + "scripts": { + "test": "nyc -r text-summary -r html mocha -r source-map-support/register -r ts-node/register './lib/**/*.spec.ts'", + "build": "tsc", + "lint": "eslint ./*.ts ./lib/**/*.ts", + "documentation": "typedoc" + }, + "dependencies": { + "cluster-key-slot": "1.1.0", + "generic-pool": "3.8.2", + "redis-parser": "3.0.0", + "yallist": "4.0.0" + }, + "devDependencies": { + "@istanbuljs/nyc-config-typescript": "^1.0.1", + "@redis/test-utils": "*", + "@types/node": "^16.11.6", + "@types/sinon": "^10.0.6", + "@types/yallist": "^4.0.1", + "@typescript-eslint/eslint-plugin": "^5.2.0", + "@typescript-eslint/parser": "^5.2.0", + "eslint": "^8.1.0", + "nyc": "^15.1.0", + "release-it": "^14.11.6", + "sinon": "^11.1.2", + "source-map-support": "^0.5.20", + "ts-node": "^10.4.0", + "typedoc": "^0.22.7", + "typedoc-github-wiki-theme": "^0.6.0", + "typedoc-plugin-markdown": "^3.11.3", + "typescript": "^4.4.4" + }, + "engines": { + "node": ">=12" + }, + "repository": { + "type": "git", + "url": "git://github.com/redis/node-redis.git" + }, + "bugs": { + "url": "https://github.com/redis/node-redis/issues" + }, + "homepage": "https://github.com/redis/node-redis" +} diff --git a/packages/client/tsconfig.json b/packages/client/tsconfig.json new file mode 100644 index 00000000000..1700c9097bf --- /dev/null +++ b/packages/client/tsconfig.json @@ -0,0 +1,27 @@ +{ + "extends": "../../tsconfig.json", + "compilerOptions": { + "outDir": "./dist" + }, + "include": [ + "./index.ts", + "./lib/**/*.ts" + ], + "exclude": [ + "./lib/test-utils.ts", + "./lib/**/*.spec.ts" + ], + "typedocOptions": { + "entryPoints": [ + "./index.ts", + "./lib" + ], + "entryPointStrategy": "expand", + "exclude": [ + "./lib/ts-declarations", + "./lib/test-utils.ts" + ], + "theme": "./node_modules/typedoc-github-wiki-theme/dist", + "out": "documentation" + } +} diff --git a/packages/json/.nycrc.json b/packages/json/.nycrc.json new file mode 100644 index 00000000000..b4e671e178f --- /dev/null +++ b/packages/json/.nycrc.json @@ -0,0 +1,4 @@ +{ + "extends": "@istanbuljs/nyc-config-typescript", + "exclude": ["**/*.spec.ts", "lib/test-utils.ts"] +} diff --git a/packages/json/lib/commands/ARRAPPEND.spec.ts b/packages/json/lib/commands/ARRAPPEND.spec.ts new file mode 100644 index 00000000000..ab53837a000 --- /dev/null +++ b/packages/json/lib/commands/ARRAPPEND.spec.ts @@ -0,0 +1,30 @@ +import { strict as assert } from 'assert'; +import testUtils, { GLOBAL } from '../test-utils'; +import { transformArguments } from './ARRAPPEND'; + +describe('ARRAPPEND', () => { + describe('transformArguments', () => { + it('single JSON', () => { + assert.deepEqual( + transformArguments('key', '$', 1), + ['JSON.ARRAPPEND', 'key', '$', '1'] + ); + }); + + it('multiple JSONs', () => { + assert.deepEqual( + transformArguments('key', '$', 1, 2), + ['JSON.ARRAPPEND', 'key', '$', '1', '2'] + ); + }); + }); + + testUtils.testWithClient('client.json.arrAppend', async client => { + await client.json.set('key', '$', []); + + assert.deepEqual( + await client.json.arrAppend('key', '$', 1), + [1] + ); + }, GLOBAL.SERVERS.OPEN); +}); diff --git a/packages/json/lib/commands/ARRAPPEND.ts b/packages/json/lib/commands/ARRAPPEND.ts new file mode 100644 index 00000000000..2935d192996 --- /dev/null +++ b/packages/json/lib/commands/ARRAPPEND.ts @@ -0,0 +1,15 @@ +import { RedisJSON, transformRedisJsonArgument } from '.'; + +export const FIRST_KEY_INDEX = 1; + +export function transformArguments(key: string, path: string, ...jsons: Array): Array { + const args = ['JSON.ARRAPPEND', key, path]; + + for (const json of jsons) { + args.push(transformRedisJsonArgument(json)); + } + + return args; +} + +export declare function transformReply(): number | Array; diff --git a/packages/json/lib/commands/ARRINDEX.spec.ts b/packages/json/lib/commands/ARRINDEX.spec.ts new file mode 100644 index 00000000000..7a47d67126a --- /dev/null +++ b/packages/json/lib/commands/ARRINDEX.spec.ts @@ -0,0 +1,37 @@ +import { strict as assert } from 'assert'; +import testUtils, { GLOBAL } from '../test-utils'; +import { transformArguments } from './ARRINDEX'; + +describe('ARRINDEX', () => { + describe('transformArguments', () => { + it('simple', () => { + assert.deepEqual( + transformArguments('key', '$', 'json'), + ['JSON.ARRINDEX', 'key', '$', '"json"'] + ); + }); + + it('with start', () => { + assert.deepEqual( + transformArguments('key', '$', 'json', 1), + ['JSON.ARRINDEX', 'key', '$', '"json"', '1'] + ); + }); + + it('with start, end', () => { + assert.deepEqual( + transformArguments('key', '$', 'json', 1, 2), + ['JSON.ARRINDEX', 'key', '$', '"json"', '1', '2'] + ); + }); + }); + + testUtils.testWithClient('client.json.arrIndex', async client => { + await client.json.set('key', '$', []); + + assert.deepEqual( + await client.json.arrIndex('key', '$', 'json'), + [-1] + ); + }, GLOBAL.SERVERS.OPEN); +}); diff --git a/packages/json/lib/commands/ARRINDEX.ts b/packages/json/lib/commands/ARRINDEX.ts new file mode 100644 index 00000000000..5860b59cb3c --- /dev/null +++ b/packages/json/lib/commands/ARRINDEX.ts @@ -0,0 +1,21 @@ +import { RedisJSON, transformRedisJsonArgument } from '.'; + +export const FIRST_KEY_INDEX = 1; + +export const IS_READ_ONLY = true; + +export function transformArguments(key: string, path: string, json: RedisJSON, start?: number, stop?: number): Array { + const args = ['JSON.ARRINDEX', key, path, transformRedisJsonArgument(json)]; + + if (start !== undefined && start !== null) { + args.push(start.toString()); + + if (stop !== undefined && stop !== null) { + args.push(stop.toString()); + } + } + + return args; +} + +export declare function transformReply(): number | Array; diff --git a/packages/json/lib/commands/ARRINSERT.spec.ts b/packages/json/lib/commands/ARRINSERT.spec.ts new file mode 100644 index 00000000000..4b9d58b2cac --- /dev/null +++ b/packages/json/lib/commands/ARRINSERT.spec.ts @@ -0,0 +1,30 @@ +import { strict as assert } from 'assert'; +import testUtils, { GLOBAL } from '../test-utils'; +import { transformArguments } from './ARRINSERT'; + +describe('ARRINSERT', () => { + describe('transformArguments', () => { + it('single JSON', () => { + assert.deepEqual( + transformArguments('key', '$', 0, 'json'), + ['JSON.ARRINSERT', 'key', '$', '0', '"json"'] + ); + }); + + it('multiple JSONs', () => { + assert.deepEqual( + transformArguments('key', '$', 0, '1', '2'), + ['JSON.ARRINSERT', 'key', '$', '0', '"1"', '"2"'] + ); + }); + }); + + testUtils.testWithClient('client.json.arrInsert', async client => { + await client.json.set('key', '$', []); + + assert.deepEqual( + await client.json.arrInsert('key', '$', 0, 'json'), + [1] + ); + }, GLOBAL.SERVERS.OPEN); +}); diff --git a/packages/json/lib/commands/ARRINSERT.ts b/packages/json/lib/commands/ARRINSERT.ts new file mode 100644 index 00000000000..85857657019 --- /dev/null +++ b/packages/json/lib/commands/ARRINSERT.ts @@ -0,0 +1,15 @@ +import { RedisJSON, transformRedisJsonArgument } from '.'; + +export const FIRST_KEY_INDEX = 1; + +export function transformArguments(key: string, path: string, index: number, ...jsons: Array): Array { + const args = ['JSON.ARRINSERT', key, path, index.toString()]; + + for (const json of jsons) { + args.push(transformRedisJsonArgument(json)); + } + + return args; +} + +export declare function transformReply(): number | Array; diff --git a/packages/json/lib/commands/ARRLEN.spec.ts b/packages/json/lib/commands/ARRLEN.spec.ts new file mode 100644 index 00000000000..f0a3ec40a4c --- /dev/null +++ b/packages/json/lib/commands/ARRLEN.spec.ts @@ -0,0 +1,30 @@ +import { strict as assert } from 'assert'; +import testUtils, { GLOBAL } from '../test-utils'; +import { transformArguments } from './ARRLEN'; + +describe('ARRLEN', () => { + describe('transformArguments', () => { + it('without path', () => { + assert.deepEqual( + transformArguments('key'), + ['JSON.ARRLEN', 'key'] + ); + }); + + it('with path', () => { + assert.deepEqual( + transformArguments('key', '$'), + ['JSON.ARRLEN', 'key', '$'] + ); + }); + }); + + testUtils.testWithClient('client.json.arrLen', async client => { + await client.json.set('key', '$', []); + + assert.deepEqual( + await client.json.arrLen('key', '$'), + [0] + ); + }, GLOBAL.SERVERS.OPEN); +}); diff --git a/packages/json/lib/commands/ARRLEN.ts b/packages/json/lib/commands/ARRLEN.ts new file mode 100644 index 00000000000..818397b7f8d --- /dev/null +++ b/packages/json/lib/commands/ARRLEN.ts @@ -0,0 +1,15 @@ +export const FIRST_KEY_INDEX = 1; + +export const IS_READ_ONLY = true; + +export function transformArguments(key: string, path?: string): Array { + const args = ['JSON.ARRLEN', key]; + + if (path) { + args.push(path); + } + + return args; +} + +export declare function transformReply(): number | Array; diff --git a/packages/json/lib/commands/ARRPOP.spec.ts b/packages/json/lib/commands/ARRPOP.spec.ts new file mode 100644 index 00000000000..a80b8c3cbc5 --- /dev/null +++ b/packages/json/lib/commands/ARRPOP.spec.ts @@ -0,0 +1,37 @@ +import { strict as assert } from 'assert'; +import testUtils, { GLOBAL } from '../test-utils'; +import { transformArguments } from './ARRPOP'; + +describe('ARRPOP', () => { + describe('transformArguments', () => { + it('key', () => { + assert.deepEqual( + transformArguments('key'), + ['JSON.ARRPOP', 'key'] + ); + }); + + it('key, path', () => { + assert.deepEqual( + transformArguments('key', '$'), + ['JSON.ARRPOP', 'key', '$'] + ); + }); + + it('key, path, index', () => { + assert.deepEqual( + transformArguments('key', '$', 0), + ['JSON.ARRPOP', 'key', '$', '0'] + ); + }); + }); + + testUtils.testWithClient('client.json.arrPop', async client => { + await client.json.set('key', '$', []); + + assert.deepEqual( + await client.json.arrPop('key', '$'), + [null] + ); + }, GLOBAL.SERVERS.OPEN); +}); diff --git a/packages/json/lib/commands/ARRPOP.ts b/packages/json/lib/commands/ARRPOP.ts new file mode 100644 index 00000000000..5d8785a8d94 --- /dev/null +++ b/packages/json/lib/commands/ARRPOP.ts @@ -0,0 +1,17 @@ +export const FIRST_KEY_INDEX = 1; + +export function transformArguments(key: string, path?: string, index?: number): Array { + const args = ['JSON.ARRPOP', key]; + + if (path) { + args.push(path); + + if (index !== undefined && index !== null) { + args.push(index.toString()); + } + } + + return args; +} + +export { transformRedisJsonNullArrayReply as transformReply } from '.'; diff --git a/packages/json/lib/commands/ARRTRIM.spec.ts b/packages/json/lib/commands/ARRTRIM.spec.ts new file mode 100644 index 00000000000..c254e1b6a03 --- /dev/null +++ b/packages/json/lib/commands/ARRTRIM.spec.ts @@ -0,0 +1,21 @@ +import { strict as assert } from 'assert'; +import testUtils, { GLOBAL } from '../test-utils'; +import { transformArguments } from './ARRTRIM'; + +describe('ARRTRIM', () => { + it('transformArguments', () => { + assert.deepEqual( + transformArguments('key', '$', 0, 1), + ['JSON.ARRTRIM', 'key', '$', '0', '1'] + ); + }); + + testUtils.testWithClient('client.json.arrTrim', async client => { + await client.json.set('key', '$', []); + + assert.deepEqual( + await client.json.arrTrim('key', '$', 0, 1), + [0] + ); + }, GLOBAL.SERVERS.OPEN); +}); diff --git a/packages/json/lib/commands/ARRTRIM.ts b/packages/json/lib/commands/ARRTRIM.ts new file mode 100644 index 00000000000..2de444eeebd --- /dev/null +++ b/packages/json/lib/commands/ARRTRIM.ts @@ -0,0 +1,7 @@ +export const FIRST_KEY_INDEX = 1; + +export function transformArguments(key: string, path: string, start: number, stop: number): Array { + return ['JSON.ARRTRIM', key, path, start.toString(), stop.toString()]; +} + +export declare function transformReply(): number | Array; diff --git a/packages/json/lib/commands/DEBUG_MEMORY.spec.ts b/packages/json/lib/commands/DEBUG_MEMORY.spec.ts new file mode 100644 index 00000000000..468c994f2f5 --- /dev/null +++ b/packages/json/lib/commands/DEBUG_MEMORY.spec.ts @@ -0,0 +1,28 @@ +import { strict as assert } from 'assert'; +import testUtils, { GLOBAL } from '../test-utils'; +import { transformArguments } from './DEBUG_MEMORY'; + +describe('DEBUG MEMORY', () => { + describe('transformArguments', () => { + it('without path', () => { + assert.deepEqual( + transformArguments('key'), + ['JSON.DEBUG', 'MEMORY', 'key'] + ); + }); + + it('with path', () => { + assert.deepEqual( + transformArguments('key', '$'), + ['JSON.DEBUG', 'MEMORY', 'key', '$'] + ); + }); + }); + + testUtils.testWithClient('client.json.arrTrim', async client => { + assert.deepEqual( + await client.json.debugMemory('key', '$'), + [] + ); + }, GLOBAL.SERVERS.OPEN); +}); diff --git a/packages/json/lib/commands/DEBUG_MEMORY.ts b/packages/json/lib/commands/DEBUG_MEMORY.ts new file mode 100644 index 00000000000..da60b1d9529 --- /dev/null +++ b/packages/json/lib/commands/DEBUG_MEMORY.ts @@ -0,0 +1,13 @@ +export const FIRST_KEY_INDEX = 2; + +export function transformArguments(key: string, path?: string): Array { + const args = ['JSON.DEBUG', 'MEMORY', key]; + + if (path) { + args.push(path); + } + + return args; +} + +export declare function transformReply(): number; diff --git a/packages/json/lib/commands/DEL.spec.ts b/packages/json/lib/commands/DEL.spec.ts new file mode 100644 index 00000000000..a957b9584ac --- /dev/null +++ b/packages/json/lib/commands/DEL.spec.ts @@ -0,0 +1,28 @@ +import { strict as assert } from 'assert'; +import testUtils, { GLOBAL } from '../test-utils'; +import { transformArguments } from './DEL'; + +describe('DEL', () => { + describe('transformArguments', () => { + it('key', () => { + assert.deepEqual( + transformArguments('key'), + ['JSON.DEL', 'key'] + ); + }); + + it('key, path', () => { + assert.deepEqual( + transformArguments('key', '$.path'), + ['JSON.DEL', 'key', '$.path'] + ); + }); + }); + + testUtils.testWithClient('client.json.del', async client => { + assert.deepEqual( + await client.json.del('key'), + 0 + ); + }, GLOBAL.SERVERS.OPEN); +}); diff --git a/packages/json/lib/commands/DEL.ts b/packages/json/lib/commands/DEL.ts new file mode 100644 index 00000000000..090d4dbe853 --- /dev/null +++ b/packages/json/lib/commands/DEL.ts @@ -0,0 +1,13 @@ +export const FIRST_KEY_INDEX = 1; + +export function transformArguments(key: string, path?: string): Array { + const args = ['JSON.DEL', key]; + + if (path) { + args.push(path); + } + + return args; +} + +export declare function transformReply(): number; diff --git a/packages/json/lib/commands/FORGET.spec.ts b/packages/json/lib/commands/FORGET.spec.ts new file mode 100644 index 00000000000..923bb997fc8 --- /dev/null +++ b/packages/json/lib/commands/FORGET.spec.ts @@ -0,0 +1,28 @@ +import { strict as assert } from 'assert'; +import testUtils, { GLOBAL } from '../test-utils'; +import { transformArguments } from './FORGET'; + +describe('FORGET', () => { + describe('transformArguments', () => { + it('key', () => { + assert.deepEqual( + transformArguments('key'), + ['JSON.FORGET', 'key'] + ); + }); + + it('key, path', () => { + assert.deepEqual( + transformArguments('key', '$.path'), + ['JSON.FORGET', 'key', '$.path'] + ); + }); + }); + + testUtils.testWithClient('client.json.forget', async client => { + assert.deepEqual( + await client.json.forget('key'), + 0 + ); + }, GLOBAL.SERVERS.OPEN); +}); diff --git a/packages/json/lib/commands/FORGET.ts b/packages/json/lib/commands/FORGET.ts new file mode 100644 index 00000000000..cb2df3d605d --- /dev/null +++ b/packages/json/lib/commands/FORGET.ts @@ -0,0 +1,13 @@ +export const FIRST_KEY_INDEX = 1; + +export function transformArguments(key: string, path?: string): Array { + const args = ['JSON.FORGET', key]; + + if (path) { + args.push(path); + } + + return args; +} + +export declare function transformReply(): number; diff --git a/packages/json/lib/commands/GET.spec.ts b/packages/json/lib/commands/GET.spec.ts new file mode 100644 index 00000000000..ed831689a93 --- /dev/null +++ b/packages/json/lib/commands/GET.spec.ts @@ -0,0 +1,78 @@ +import { strict as assert } from 'assert'; +import testUtils, { GLOBAL } from '../test-utils'; +import { transformArguments } from './GET'; + +describe('GET', () => { + describe('transformArguments', () => { + describe('path', () => { + it('string', () => { + assert.deepEqual( + transformArguments('key', { path: '$' }), + ['JSON.GET', 'key', '$'] + ); + }); + + it('array', () => { + assert.deepEqual( + transformArguments('key', { path: ['$.1', '$.2'] }), + ['JSON.GET', 'key', '$.1', '$.2'] + ); + }); + }); + + it('key', () => { + assert.deepEqual( + transformArguments('key'), + ['JSON.GET', 'key'] + ); + }); + + it('INDENT', () => { + assert.deepEqual( + transformArguments('key', { INDENT: 'indent' }), + ['JSON.GET', 'key', 'INDENT', 'indent'] + ); + }); + + it('NEWLINE', () => { + assert.deepEqual( + transformArguments('key', { NEWLINE: 'newline' }), + ['JSON.GET', 'key', 'NEWLINE', 'newline'] + ); + }); + + it('SPACE', () => { + assert.deepEqual( + transformArguments('key', { SPACE: 'space' }), + ['JSON.GET', 'key', 'SPACE', 'space'] + ); + }); + + it('NOESCAPE', () => { + assert.deepEqual( + transformArguments('key', { NOESCAPE: true }), + ['JSON.GET', 'key', 'NOESCAPE'] + ); + }); + + it('INDENT, NEWLINE, SPACE, NOESCAPE, path', () => { + assert.deepEqual( + transformArguments('key', { + path: '$.path', + INDENT: 'indent', + NEWLINE: 'newline', + SPACE: 'space', + NOESCAPE: true + }), + ['JSON.GET', 'key', '$.path', 'INDENT', 'indent', 'NEWLINE', 'newline', 'SPACE', 'space', 'NOESCAPE'] + ); + }); + }); + + testUtils.testWithClient('client.json.get', async client => { + assert.equal( + await client.json.get('key'), + null + ); + }, GLOBAL.SERVERS.OPEN); +}); diff --git a/packages/json/lib/commands/GET.ts b/packages/json/lib/commands/GET.ts new file mode 100644 index 00000000000..8b69dc80533 --- /dev/null +++ b/packages/json/lib/commands/GET.ts @@ -0,0 +1,41 @@ +import { pushVerdictArguments } from '@redis/client/lib/commands/generic-transformers'; + +export const FIRST_KEY_INDEX = 1; + +export const IS_READ_ONLY = true; + +interface GetOptions { + path?: string | Array; + INDENT?: string; + NEWLINE?: string; + SPACE?: string; + NOESCAPE?: true; +} + +export function transformArguments(key: string, options?: GetOptions): Array { + const args = ['JSON.GET', key]; + + if (options?.path) { + pushVerdictArguments(args, options.path); + } + + if (options?.INDENT) { + args.push('INDENT', options.INDENT); + } + + if (options?.NEWLINE) { + args.push('NEWLINE', options.NEWLINE); + } + + if (options?.SPACE) { + args.push('SPACE', options.SPACE); + } + + if (options?.NOESCAPE) { + args.push('NOESCAPE'); + } + + return args; +} + +export { transformRedisJsonNullReply as transformReply } from '.'; diff --git a/packages/json/lib/commands/MGET.spec.ts b/packages/json/lib/commands/MGET.spec.ts new file mode 100644 index 00000000000..456e160dd50 --- /dev/null +++ b/packages/json/lib/commands/MGET.spec.ts @@ -0,0 +1,19 @@ +import { strict as assert } from 'assert'; +import testUtils, { GLOBAL } from '../test-utils'; +import { transformArguments } from './MGET'; + +describe('MGET', () => { + it('transformArguments', () => { + assert.deepEqual( + transformArguments(['1', '2'], '$'), + ['JSON.MGET', '1', '2', '$'] + ); + }); + + testUtils.testWithClient('client.json.mGet', async client => { + assert.deepEqual( + await client.json.mGet(['1', '2'], '$'), + [null, null] + ); + }, GLOBAL.SERVERS.OPEN); +}); diff --git a/packages/json/lib/commands/MGET.ts b/packages/json/lib/commands/MGET.ts new file mode 100644 index 00000000000..582b73bf85a --- /dev/null +++ b/packages/json/lib/commands/MGET.ts @@ -0,0 +1,15 @@ +import { RedisJSON, transformRedisJsonNullReply } from '.'; + +export const FIRST_KEY_INDEX = 1; + +export function transformArguments(keys: Array, path: string): Array { + return [ + 'JSON.MGET', + ...keys, + path + ]; +} + +export function transformReply(reply: Array): Array { + return reply.map(transformRedisJsonNullReply); +} diff --git a/packages/json/lib/commands/NUMINCRBY.spec.ts b/packages/json/lib/commands/NUMINCRBY.spec.ts new file mode 100644 index 00000000000..56dede68bde --- /dev/null +++ b/packages/json/lib/commands/NUMINCRBY.spec.ts @@ -0,0 +1,21 @@ +import { strict as assert } from 'assert'; +import testUtils, { GLOBAL } from '../test-utils'; +import { transformArguments } from './NUMINCRBY'; + +describe('NUMINCRBY', () => { + it('transformArguments', () => { + assert.deepEqual( + transformArguments('key', '$', 1), + ['JSON.NUMINCRBY', 'key', '$', '1'] + ); + }); + + testUtils.testWithClient('client.json.numIncrBy', async client => { + await client.json.set('key', '$', 0); + + assert.deepEqual( + await client.json.numIncrBy('key', '$', 1), + [1] + ); + }, GLOBAL.SERVERS.OPEN); +}); diff --git a/packages/json/lib/commands/NUMINCRBY.ts b/packages/json/lib/commands/NUMINCRBY.ts new file mode 100644 index 00000000000..e3d8887ea3d --- /dev/null +++ b/packages/json/lib/commands/NUMINCRBY.ts @@ -0,0 +1,7 @@ +export const FIRST_KEY_INDEX = 1; + +export function transformArguments(key: string, path: string, by: number): Array { + return ['JSON.NUMINCRBY', key, path, by.toString()]; +} + +export { transformNumbersReply as transformReply } from '.'; diff --git a/packages/json/lib/commands/NUMMULTBY.spec.ts b/packages/json/lib/commands/NUMMULTBY.spec.ts new file mode 100644 index 00000000000..3e2581a3cd8 --- /dev/null +++ b/packages/json/lib/commands/NUMMULTBY.spec.ts @@ -0,0 +1,21 @@ +import { strict as assert } from 'assert'; +import testUtils, { GLOBAL } from '../test-utils'; +import { transformArguments } from './NUMMULTBY'; + +describe('NUMMULTBY', () => { + it('transformArguments', () => { + assert.deepEqual( + transformArguments('key', '$', 2), + ['JSON.NUMMULTBY', 'key', '$', '2'] + ); + }); + + testUtils.testWithClient('client.json.numMultBy', async client => { + await client.json.set('key', '$', 1); + + assert.deepEqual( + await client.json.numMultBy('key', '$', 2), + [2] + ); + }, GLOBAL.SERVERS.OPEN); +}); diff --git a/packages/json/lib/commands/NUMMULTBY.ts b/packages/json/lib/commands/NUMMULTBY.ts new file mode 100644 index 00000000000..2082916619a --- /dev/null +++ b/packages/json/lib/commands/NUMMULTBY.ts @@ -0,0 +1,7 @@ +export const FIRST_KEY_INDEX = 1; + +export function transformArguments(key: string, path: string, by: number): Array { + return ['JSON.NUMMULTBY', key, path, by.toString()]; +} + +export { transformNumbersReply as transformReply } from '.'; diff --git a/packages/json/lib/commands/OBJKEYS.spec.ts b/packages/json/lib/commands/OBJKEYS.spec.ts new file mode 100644 index 00000000000..6288c112392 --- /dev/null +++ b/packages/json/lib/commands/OBJKEYS.spec.ts @@ -0,0 +1,28 @@ +import { strict as assert } from 'assert'; +import testUtils, { GLOBAL } from '../test-utils'; +import { transformArguments } from './OBJKEYS'; + +describe('OBJKEYS', () => { + describe('transformArguments', () => { + it('without path', () => { + assert.deepEqual( + transformArguments('key'), + ['JSON.OBJKEYS', 'key'] + ); + }); + + it('with path', () => { + assert.deepEqual( + transformArguments('key', '$'), + ['JSON.OBJKEYS', 'key', '$'] + ); + }); + }); + + // testUtils.testWithClient('client.json.objKeys', async client => { + // assert.deepEqual( + // await client.json.objKeys('key', '$'), + // [null] + // ); + // }, GLOBAL.SERVERS.OPEN); +}); diff --git a/packages/json/lib/commands/OBJKEYS.ts b/packages/json/lib/commands/OBJKEYS.ts new file mode 100644 index 00000000000..a9465c9160c --- /dev/null +++ b/packages/json/lib/commands/OBJKEYS.ts @@ -0,0 +1,13 @@ +export const FIRST_KEY_INDEX = 1; + +export function transformArguments(key: string, path?: string): Array { + const args = ['JSON.OBJKEYS', key]; + + if (path) { + args.push(path); + } + + return args; +} + +export declare function transformReply(): Array | null | Array | null>; diff --git a/packages/json/lib/commands/OBJLEN.spec.ts b/packages/json/lib/commands/OBJLEN.spec.ts new file mode 100644 index 00000000000..35b6589c875 --- /dev/null +++ b/packages/json/lib/commands/OBJLEN.spec.ts @@ -0,0 +1,28 @@ +import { strict as assert } from 'assert'; +import testUtils, { GLOBAL } from '../test-utils'; +import { transformArguments } from './OBJLEN'; + +describe('OBJLEN', () => { + describe('transformArguments', () => { + it('without path', () => { + assert.deepEqual( + transformArguments('key'), + ['JSON.OBJLEN', 'key'] + ); + }); + + it('with path', () => { + assert.deepEqual( + transformArguments('key', '$'), + ['JSON.OBJLEN', 'key', '$'] + ); + }); + }); + + // testUtils.testWithClient('client.json.objLen', async client => { + // assert.equal( + // await client.json.objLen('key', '$'), + // [null] + // ); + // }, GLOBAL.SERVERS.OPEN); +}); diff --git a/packages/json/lib/commands/OBJLEN.ts b/packages/json/lib/commands/OBJLEN.ts new file mode 100644 index 00000000000..aa800e97f71 --- /dev/null +++ b/packages/json/lib/commands/OBJLEN.ts @@ -0,0 +1,13 @@ +export const FIRST_KEY_INDEX = 1; + +export function transformArguments(key: string, path?: string): Array { + const args = ['JSON.OBJLEN', key]; + + if (path) { + args.push(path); + } + + return args; +} + +export declare function transformReply(): number | null | Array; diff --git a/packages/json/lib/commands/RESP.spec.ts b/packages/json/lib/commands/RESP.spec.ts new file mode 100644 index 00000000000..8b70962d1c5 --- /dev/null +++ b/packages/json/lib/commands/RESP.spec.ts @@ -0,0 +1,28 @@ +import { strict as assert } from 'assert'; +import testUtils, { GLOBAL } from '../test-utils'; +import { transformArguments } from './RESP'; + +describe('RESP', () => { + describe('transformArguments', () => { + it('without path', () => { + assert.deepEqual( + transformArguments('key'), + ['JSON.RESP', 'key'] + ); + }); + + it('with path', () => { + assert.deepEqual( + transformArguments('key', '$'), + ['JSON.RESP', 'key', '$'] + ); + }); + }); + + // testUtils.testWithClient('client.json.resp', async client => { + // assert.deepEqual( + // await client.json.resp('key', '$'), + // [null] + // ); + // }, GLOBAL.SERVERS.OPEN); +}); diff --git a/packages/json/lib/commands/RESP.ts b/packages/json/lib/commands/RESP.ts new file mode 100644 index 00000000000..2b56bf1f059 --- /dev/null +++ b/packages/json/lib/commands/RESP.ts @@ -0,0 +1,15 @@ +export const FIRST_KEY_INDEX = 1; + +export function transformArguments(key: string, path?: string): Array { + const args = ['JSON.RESP', key]; + + if (path) { + args.push(path); + } + + return args; +} + +type RESPReply = Array; + +export declare function transfromReply(): RESPReply; diff --git a/packages/json/lib/commands/SET.spec.ts b/packages/json/lib/commands/SET.spec.ts new file mode 100644 index 00000000000..8f8586a2047 --- /dev/null +++ b/packages/json/lib/commands/SET.spec.ts @@ -0,0 +1,35 @@ +import { strict as assert } from 'assert'; +import testUtils, { GLOBAL } from '../test-utils'; +import { transformArguments } from './SET'; + +describe('SET', () => { + describe('transformArguments', () => { + it('transformArguments', () => { + assert.deepEqual( + transformArguments('key', '$', 'json'), + ['JSON.SET', 'key', '$', '"json"'] + ); + }); + + it('NX', () => { + assert.deepEqual( + transformArguments('key', '$', 'json', { NX: true }), + ['JSON.SET', 'key', '$', '"json"', 'NX'] + ); + }); + + it('XX', () => { + assert.deepEqual( + transformArguments('key', '$', 'json', { XX: true }), + ['JSON.SET', 'key', '$', '"json"', 'XX'] + ); + }); + }); + + testUtils.testWithClient('client.json.mGet', async client => { + assert.equal( + await client.json.set('key', '$', 'json'), + 'OK' + ); + }, GLOBAL.SERVERS.OPEN); +}); diff --git a/packages/json/lib/commands/SET.ts b/packages/json/lib/commands/SET.ts new file mode 100644 index 00000000000..f50a42bf5db --- /dev/null +++ b/packages/json/lib/commands/SET.ts @@ -0,0 +1,25 @@ +import { RedisJSON, transformRedisJsonArgument } from '.'; + +export const FIRST_KEY_INDEX = 1; + +interface NX { + NX: true; +} + +interface XX { + XX: true; +} + +export function transformArguments(key: string, path: string, json: RedisJSON, options?: NX | XX): Array { + const args = ['JSON.SET', key, path, transformRedisJsonArgument(json)]; + + if ((options)?.NX) { + args.push('NX'); + } else if ((options)?.XX) { + args.push('XX'); + } + + return args; +} + +export declare function transformReply(): 'OK' | null; diff --git a/packages/json/lib/commands/STRAPPEND.spec.ts b/packages/json/lib/commands/STRAPPEND.spec.ts new file mode 100644 index 00000000000..a37eaa1d91c --- /dev/null +++ b/packages/json/lib/commands/STRAPPEND.spec.ts @@ -0,0 +1,30 @@ +import { strict as assert } from 'assert'; +import testUtils, { GLOBAL } from '../test-utils'; +import { transformArguments } from './STRAPPEND'; + +describe('STRAPPEND', () => { + describe('transformArguments', () => { + it('without path', () => { + assert.deepEqual( + transformArguments('key', 'append'), + ['JSON.STRAPPEND', 'key', '"append"'] + ); + }); + + it('with path', () => { + assert.deepEqual( + transformArguments('key', '$', 'append'), + ['JSON.STRAPPEND', 'key', '$', '"append"'] + ); + }); + }); + + testUtils.testWithClient('client.json.strAppend', async client => { + await client.json.set('key', '$', ''); + + assert.deepEqual( + await client.json.strAppend('key', '$', 'append'), + [6] + ); + }, GLOBAL.SERVERS.OPEN); +}); diff --git a/packages/json/lib/commands/STRAPPEND.ts b/packages/json/lib/commands/STRAPPEND.ts new file mode 100644 index 00000000000..eea384c93fd --- /dev/null +++ b/packages/json/lib/commands/STRAPPEND.ts @@ -0,0 +1,21 @@ +import { transformRedisJsonArgument } from '.'; + +export const FIRST_KEY_INDEX = 1; + +type AppendArguments = [key: string, append: string]; + +type AppendWithPathArguments = [key: string, path: string, append: string]; + +export function transformArguments(...[key, pathOrAppend, append]: AppendArguments | AppendWithPathArguments): Array { + const args = ['JSON.STRAPPEND', key]; + + if (append !== undefined && append !== null) { + args.push(pathOrAppend, transformRedisJsonArgument(append)); + } else { + args.push(transformRedisJsonArgument(pathOrAppend)); + } + + return args; +} + +export declare function transformReply(): number | Array; diff --git a/packages/json/lib/commands/STRLEN.spec.ts b/packages/json/lib/commands/STRLEN.spec.ts new file mode 100644 index 00000000000..cf163d3c19e --- /dev/null +++ b/packages/json/lib/commands/STRLEN.spec.ts @@ -0,0 +1,30 @@ +import { strict as assert } from 'assert'; +import testUtils, { GLOBAL } from '../test-utils'; +import { transformArguments } from './STRLEN'; + +describe('STRLEN', () => { + describe('transformArguments', () => { + it('without path', () => { + assert.deepEqual( + transformArguments('key'), + ['JSON.STRLEN', 'key'] + ); + }); + + it('with path', () => { + assert.deepEqual( + transformArguments('key', '$'), + ['JSON.STRLEN', 'key', '$'] + ); + }); + }); + + testUtils.testWithClient('client.json.strLen', async client => { + await client.json.set('key', '$', ''); + + assert.deepEqual( + await client.json.strLen('key', '$'), + [0] + ); + }, GLOBAL.SERVERS.OPEN); +}); diff --git a/packages/json/lib/commands/STRLEN.ts b/packages/json/lib/commands/STRLEN.ts new file mode 100644 index 00000000000..93f5d563baf --- /dev/null +++ b/packages/json/lib/commands/STRLEN.ts @@ -0,0 +1,15 @@ +export const FIRST_KEY_INDEX = 1; + +export const IS_READ_ONLY = true; + +export function transformArguments(key: string, path?: string): Array { + const args = ['JSON.STRLEN', key]; + + if (path) { + args.push(path); + } + + return args; +} + +export declare function transformReply(): number; diff --git a/packages/json/lib/commands/TYPE.spec.ts b/packages/json/lib/commands/TYPE.spec.ts new file mode 100644 index 00000000000..5cecfb827a7 --- /dev/null +++ b/packages/json/lib/commands/TYPE.spec.ts @@ -0,0 +1,28 @@ +import { strict as assert } from 'assert'; +import testUtils, { GLOBAL } from '../test-utils'; +import { transformArguments } from './TYPE'; + +describe('TYPE', () => { + describe('transformArguments', () => { + it('without path', () => { + assert.deepEqual( + transformArguments('key'), + ['JSON.TYPE', 'key'] + ); + }); + + it('with path', () => { + assert.deepEqual( + transformArguments('key', '$'), + ['JSON.TYPE', 'key', '$'] + ); + }); + }); + + // testUtils.testWithClient('client.json.type', async client => { + // assert.deepEqual( + // await client.json.type('key', '$'), + // [null] + // ); + // }, GLOBAL.SERVERS.OPEN); +}); diff --git a/packages/json/lib/commands/TYPE.ts b/packages/json/lib/commands/TYPE.ts new file mode 100644 index 00000000000..7fd55f625dc --- /dev/null +++ b/packages/json/lib/commands/TYPE.ts @@ -0,0 +1,13 @@ +export const FIRST_KEY_INDEX = 1; + +export function transformArguments(key: string, path?: string): Array { + const args = ['JSON.TYPE', key]; + + if (path) { + args.push(path); + } + + return args; +} + +export declare function transformReply(): string | null | Array; diff --git a/packages/json/lib/commands/index.ts b/packages/json/lib/commands/index.ts new file mode 100644 index 00000000000..91b4f7dc4b5 --- /dev/null +++ b/packages/json/lib/commands/index.ts @@ -0,0 +1,94 @@ +import * as ARRAPPEND from './ARRAPPEND'; +import * as ARRINDEX from './ARRINDEX'; +import * as ARRINSERT from './ARRINSERT'; +import * as ARRLEN from './ARRLEN'; +import * as ARRPOP from './ARRPOP'; +import * as ARRTRIM from './ARRTRIM'; +import * as DEBUG_MEMORY from './DEBUG_MEMORY'; +import * as DEL from './DEL'; +import * as FORGET from './FORGET'; +import * as GET from './GET'; +import * as MGET from './MGET'; +import * as NUMINCRBY from './NUMINCRBY'; +import * as NUMMULTBY from './NUMMULTBY'; +import * as OBJKEYS from './OBJKEYS'; +import * as OBJLEN from './OBJLEN'; +import * as RESP from './RESP'; +import * as SET from './SET'; +import * as STRAPPEND from './STRAPPEND'; +import * as STRLEN from './STRLEN'; +import * as TYPE from './TYPE'; + +export default { + ARRAPPEND, + arrAppend: ARRAPPEND, + ARRINDEX, + arrIndex: ARRINDEX, + ARRINSERT, + arrInsert: ARRINSERT, + ARRLEN, + arrLen: ARRLEN, + ARRPOP, + arrPop: ARRPOP, + ARRTRIM, + arrTrim: ARRTRIM, + DEBUG_MEMORY, + debugMemory: DEBUG_MEMORY, + DEL, + del: DEL, + FORGET, + forget: FORGET, + GET, + get: GET, + MGET, + mGet: MGET, + NUMINCRBY, + numIncrBy: NUMINCRBY, + NUMMULTBY, + numMultBy: NUMMULTBY, + OBJKEYS, + objKeys: OBJKEYS, + OBJLEN, + objLen: OBJLEN, + RESP, + resp: RESP, + SET, + set: SET, + STRAPPEND, + strAppend: STRAPPEND, + STRLEN, + strLen: STRLEN, + TYPE, + type: TYPE +}; + +// using two "objects" and not `Record` cause of: +// https://github.com/microsoft/TypeScript/issues/14174 +export type RedisJSON = null | boolean | number | string | Date | Array | { [key: string]: RedisJSON } | { [key: number]: RedisJSON }; + +export function transformRedisJsonArgument(json: RedisJSON): string { + return JSON.stringify(json); +} + +export function transformRedisJsonReply(json: string): RedisJSON { + return JSON.parse(json); +} + +export function transformRedisJsonArrayReply(jsons: Array): Array { + return jsons.map(transformRedisJsonReply) +} + +export function transformRedisJsonNullReply(json: string | null): RedisJSON | null { + if (json === null) return null; + + return transformRedisJsonReply(json); +} + + +export function transformRedisJsonNullArrayReply(jsons: Array): Array { + return jsons.map(transformRedisJsonNullReply); +} + +export function transformNumbersReply(reply: string): number | Array { + return JSON.parse(reply); +} diff --git a/packages/json/lib/index.ts b/packages/json/lib/index.ts new file mode 100644 index 00000000000..bc0e103e8c8 --- /dev/null +++ b/packages/json/lib/index.ts @@ -0,0 +1 @@ +export { default } from './commands'; diff --git a/packages/json/lib/test-utils.ts b/packages/json/lib/test-utils.ts new file mode 100644 index 00000000000..140e781d91e --- /dev/null +++ b/packages/json/lib/test-utils.ts @@ -0,0 +1,21 @@ +import TestUtils from '@redis/test-utils'; +import RedisJSON from '.'; + +export default new TestUtils({ + dockerImageName: 'redislabs/rejson', + dockerImageVersionArgument: 'rejson-version', + defaultDockerVersion: '2.0.2' +}); + +export const GLOBAL = { + SERVERS: { + OPEN: { + serverArguments: ['--loadmodule /usr/lib/redis/modules/rejson.so'], + clientOptions: { + modules: { + json: RedisJSON + } + } + } + } +}; diff --git a/packages/json/package.json b/packages/json/package.json new file mode 100644 index 00000000000..ebd7f8f76fa --- /dev/null +++ b/packages/json/package.json @@ -0,0 +1,24 @@ +{ + "name": "@redis/json", + "version": "1.0.0-rc.0", + "license": "MIT", + "main": "./dist/index.js", + "types": "./dist/index.d.ts", + "scripts": { + "test": "nyc -r text-summary -r html mocha -r source-map-support/register -r ts-node/register './lib/**/*.spec.ts'", + "build": "tsc" + }, + "peerDependencies": { + "@redis/client": "^4.0.0-rc" + }, + "devDependencies": { + "@istanbuljs/nyc-config-typescript": "^1.0.1", + "@redis/test-utils": "*", + "@types/node": "^16.11.6", + "nyc": "^15.1.0", + "release-it": "^14.11.6", + "source-map-support": "^0.5.20", + "ts-node": "^10.4.0", + "typescript": "^4.4.4" + } +} diff --git a/packages/json/tsconfig.json b/packages/json/tsconfig.json new file mode 100644 index 00000000000..fdb86c004cc --- /dev/null +++ b/packages/json/tsconfig.json @@ -0,0 +1,9 @@ +{ + "extends": "../../tsconfig", + "compilerOptions": { + "outDir": "./dist" + }, + "include": [ + "./lib/**/*.ts" + ] +} diff --git a/packages/search/.nycrc.json b/packages/search/.nycrc.json new file mode 100644 index 00000000000..b4e671e178f --- /dev/null +++ b/packages/search/.nycrc.json @@ -0,0 +1,4 @@ +{ + "extends": "@istanbuljs/nyc-config-typescript", + "exclude": ["**/*.spec.ts", "lib/test-utils.ts"] +} diff --git a/packages/search/lib/commands/AGGREGATE.spec.ts b/packages/search/lib/commands/AGGREGATE.spec.ts new file mode 100644 index 00000000000..2a6647c97a4 --- /dev/null +++ b/packages/search/lib/commands/AGGREGATE.spec.ts @@ -0,0 +1,482 @@ +import { strict as assert } from 'assert'; +import testUtils, { GLOBAL } from '../test-utils'; +import { AggregateGroupByReducers, AggregateSteps, transformArguments } from './AGGREGATE'; +import { SchemaFieldTypes } from './CREATE'; + +describe('AGGREGATE', () => { + describe('transformArguments', () => { + it('without options', () => { + assert.deepEqual( + transformArguments('index', '*'), + ['FT.AGGREGATE', 'index', '*'] + ); + }); + + it('with VERBATIM', () => { + assert.deepEqual( + transformArguments('index', '*', { VERBATIM: true }), + ['FT.AGGREGATE', 'index', '*', 'VERBATIM'] + ); + }); + + describe('with LOAD', () => { + describe('single', () => { + describe('without alias', () => { + it('string', () => { + assert.deepEqual( + transformArguments('index', '*', { LOAD: '@property' }), + ['FT.AGGREGATE', 'index', '*', 'LOAD', '1', '@property'] + ); + }); + + it('{ identifier: string }', () => { + assert.deepEqual( + transformArguments('index', '*', { + LOAD: { + identifier: '@property' + } + }), + ['FT.AGGREGATE', 'index', '*', 'LOAD', '1', '@property'] + ); + }); + }); + + it('with alias', () => { + assert.deepEqual( + transformArguments('index', '*', { + LOAD: { + identifier: '@property', + AS: 'alias' + } + }), + ['FT.AGGREGATE', 'index', '*', 'LOAD', '3', '@property', 'AS', 'alias'] + ); + }); + }); + + it('multiple', () => { + assert.deepEqual( + transformArguments('index', '*', { LOAD: ['@1', '@2'] }), + ['FT.AGGREGATE', 'index', '*', 'LOAD', '2', '@1', '@2'] + ); + }); + }); + + describe('with STEPS', () => { + describe('GROUPBY', () => { + describe('COUNT', () => { + describe('without properties', () => { + it('without alias', () => { + assert.deepEqual( + transformArguments('index', '*', { + STEPS: [{ + type: AggregateSteps.GROUPBY, + REDUCE: { + type: AggregateGroupByReducers.COUNT + } + }] + }), + ['FT.AGGREGATE', 'index', '*', 'GROUPBY', '0', 'REDUCE', 'COUNT', '0'] + ); + }); + + it('with alias', () => { + assert.deepEqual( + transformArguments('index', '*', { + STEPS: [{ + type: AggregateSteps.GROUPBY, + REDUCE: { + type: AggregateGroupByReducers.COUNT, + AS: 'count' + } + }] + }), + ['FT.AGGREGATE', 'index', '*', 'GROUPBY', '0', 'REDUCE', 'COUNT', '0', 'AS', 'count'] + ); + }); + }); + + describe('with properties', () => { + it('single', () => { + assert.deepEqual( + transformArguments('index', '*', { + STEPS: [{ + type: AggregateSteps.GROUPBY, + properties: '@property', + REDUCE: { + type: AggregateGroupByReducers.COUNT + } + }] + }), + ['FT.AGGREGATE', 'index', '*', 'GROUPBY', '1', '@property', 'REDUCE', 'COUNT', '0'] + ); + }); + + it('multiple', () => { + assert.deepEqual( + transformArguments('index', '*', { + STEPS: [{ + type: AggregateSteps.GROUPBY, + properties: ['@1', '@2'], + REDUCE: { + type: AggregateGroupByReducers.COUNT + } + }] + }), + ['FT.AGGREGATE', 'index', '*', 'GROUPBY', '2', '@1', '@2', 'REDUCE', 'COUNT', '0'] + ); + }); + }); + }); + + it('COUNT_DISTINCT', () => { + assert.deepEqual( + transformArguments('index', '*', { + STEPS: [{ + type: AggregateSteps.GROUPBY, + REDUCE: { + type: AggregateGroupByReducers.COUNT_DISTINCT, + property: '@property' + } + }] + }), + ['FT.AGGREGATE', 'index', '*', 'GROUPBY', '0', 'REDUCE', 'COUNT_DISTINCT', '1', '@property'] + ); + }); + + it('COUNT_DISTINCTISH', () => { + assert.deepEqual( + transformArguments('index', '*', { + STEPS: [{ + type: AggregateSteps.GROUPBY, + REDUCE: { + type: AggregateGroupByReducers.COUNT_DISTINCTISH, + property: '@property' + } + }] + }), + ['FT.AGGREGATE', 'index', '*', 'GROUPBY', '0', 'REDUCE', 'COUNT_DISTINCTISH', '1', '@property'] + ); + }); + + it('SUM', () => { + assert.deepEqual( + transformArguments('index', '*', { + STEPS: [{ + type: AggregateSteps.GROUPBY, + REDUCE: { + type: AggregateGroupByReducers.SUM, + property: '@property' + } + }] + }), + ['FT.AGGREGATE', 'index', '*', 'GROUPBY', '0', 'REDUCE', 'SUM', '1', '@property'] + ); + }); + + it('MIN', () => { + assert.deepEqual( + transformArguments('index', '*', { + STEPS: [{ + type: AggregateSteps.GROUPBY, + REDUCE: { + type: AggregateGroupByReducers.MIN, + property: '@property' + } + }] + }), + ['FT.AGGREGATE', 'index', '*', 'GROUPBY', '0', 'REDUCE', 'MIN', '1', '@property'] + ); + }); + + it('MAX', () => { + assert.deepEqual( + transformArguments('index', '*', { + STEPS: [{ + type: AggregateSteps.GROUPBY, + REDUCE: { + type: AggregateGroupByReducers.MAX, + property: '@property' + } + }] + }), + ['FT.AGGREGATE', 'index', '*', 'GROUPBY', '0', 'REDUCE', 'MAX', '1', '@property'] + ); + }); + + it('AVG', () => { + assert.deepEqual( + transformArguments('index', '*', { + STEPS: [{ + type: AggregateSteps.GROUPBY, + REDUCE: { + type: AggregateGroupByReducers.AVG, + property: '@property' + } + }] + }), + ['FT.AGGREGATE', 'index', '*', 'GROUPBY', '0', 'REDUCE', 'AVG', '1', '@property'] + ); + }); + + it('STDDEV', () => { + assert.deepEqual( + transformArguments('index', '*', { + STEPS: [{ + type: AggregateSteps.GROUPBY, + REDUCE: { + type: AggregateGroupByReducers.STDDEV, + property: '@property' + } + }] + }), + ['FT.AGGREGATE', 'index', '*', 'GROUPBY', '0', 'REDUCE', 'STDDEV', '1', '@property'] + ); + }); + + it('QUANTILE', () => { + assert.deepEqual( + transformArguments('index', '*', { + STEPS: [{ + type: AggregateSteps.GROUPBY, + REDUCE: { + type: AggregateGroupByReducers.QUANTILE, + property: '@property', + quantile: 0.5 + } + }] + }), + ['FT.AGGREGATE', 'index', '*', 'GROUPBY', '0', 'REDUCE', 'QUANTILE', '2', '@property', '0.5'] + ); + }); + + it('TO_LIST', () => { + assert.deepEqual( + transformArguments('index', '*', { + STEPS: [{ + type: AggregateSteps.GROUPBY, + REDUCE: { + type: AggregateGroupByReducers.TO_LIST, + property: '@property' + } + }] + }), + ['FT.AGGREGATE', 'index', '*', 'GROUPBY', '0', 'REDUCE', 'TOLIST', '1', '@property'] + ); + }); + + describe('FIRST_VALUE', () => { + it('simple', () => { + assert.deepEqual( + transformArguments('index', '*', { + STEPS: [{ + type: AggregateSteps.GROUPBY, + REDUCE: { + type: AggregateGroupByReducers.FIRST_VALUE, + property: '@property' + } + }] + }), + ['FT.AGGREGATE', 'index', '*', 'GROUPBY', '0', 'REDUCE', 'FIRST_VALUE', '1', '@property'] + ); + }); + + describe('with BY', () => { + describe('without direction', () => { + it('string', () => { + assert.deepEqual( + transformArguments('index', '*', { + STEPS: [{ + type: AggregateSteps.GROUPBY, + REDUCE: { + type: AggregateGroupByReducers.FIRST_VALUE, + property: '@property', + BY: '@by' + } + }] + }), + ['FT.AGGREGATE', 'index', '*', 'GROUPBY', '0', 'REDUCE', 'FIRST_VALUE', '3', '@property', 'BY', '@by'] + ); + }); + + + it('{ property: string }', () => { + assert.deepEqual( + transformArguments('index', '*', { + STEPS: [{ + type: AggregateSteps.GROUPBY, + REDUCE: { + type: AggregateGroupByReducers.FIRST_VALUE, + property: '@property', + BY: { + property: '@by' + } + } + }] + }), + ['FT.AGGREGATE', 'index', '*', 'GROUPBY', '0', 'REDUCE', 'FIRST_VALUE', '3', '@property', 'BY', '@by'] + ); + }); + }); + + it('with direction', () => { + assert.deepEqual( + transformArguments('index', '*', { + STEPS: [{ + type: AggregateSteps.GROUPBY, + REDUCE: { + type: AggregateGroupByReducers.FIRST_VALUE, + property: '@property', + BY: { + property: '@by', + direction: 'ASC' + } + } + }] + }), + ['FT.AGGREGATE', 'index', '*', 'GROUPBY', '0', 'REDUCE', 'FIRST_VALUE', '4', '@property', 'BY', '@by', 'ASC'] + ); + }); + }); + }); + + it('RANDOM_SAMPLE', () => { + assert.deepEqual( + transformArguments('index', '*', { + STEPS: [{ + type: AggregateSteps.GROUPBY, + REDUCE: { + type: AggregateGroupByReducers.RANDOM_SAMPLE, + property: '@property', + sampleSize: 1 + } + }] + }), + ['FT.AGGREGATE', 'index', '*', 'GROUPBY', '0', 'REDUCE', 'RANDOM_SAMPLE', '2', '@property', '1'] + ); + }); + }); + + describe('SORTBY', () => { + it('string', () => { + assert.deepEqual( + transformArguments('index', '*', { + STEPS: [{ + type: AggregateSteps.SORTBY, + BY: '@by' + }] + }), + ['FT.AGGREGATE', 'index', '*', 'SORTBY', '1', '@by'] + ); + }); + + it('Array', () => { + assert.deepEqual( + transformArguments('index', '*', { + STEPS: [{ + type: AggregateSteps.SORTBY, + BY: ['@1', '@2'] + }] + }), + ['FT.AGGREGATE', 'index', '*', 'SORTBY', '2', '@1', '@2'] + ); + }); + + it('with MAX', () => { + assert.deepEqual( + transformArguments('index', '*', { + STEPS: [{ + type: AggregateSteps.SORTBY, + BY: '@by', + MAX: 1 + }] + }), + ['FT.AGGREGATE', 'index', '*', 'SORTBY', '1', '@by', 'MAX', '1'] + ); + }); + }); + + describe('APPLY', () => { + assert.deepEqual( + transformArguments('index', '*', { + STEPS: [{ + type: AggregateSteps.APPLY, + expression: '@field + 1', + AS: 'as' + }] + }), + ['FT.AGGREGATE', 'index', '*', 'APPLY', '@field + 1', 'AS', 'as'] + ); + }); + + describe('LIMIT', () => { + assert.deepEqual( + transformArguments('index', '*', { + STEPS: [{ + type: AggregateSteps.LIMIT, + from: 0, + size: 1 + }] + }), + ['FT.AGGREGATE', 'index', '*', 'LIMIT', '0', '1'] + ); + }); + + describe('FILTER', () => { + assert.deepEqual( + transformArguments('index', '*', { + STEPS: [{ + type: AggregateSteps.FILTER, + expression: '@field != ""' + }] + }), + ['FT.AGGREGATE', 'index', '*', 'FILTER', '@field != ""'] + ); + }); + }); + }); + + testUtils.testWithClient('client.ft.aggregate', async client => { + await Promise.all([ + client.ft.create('index', { + field: SchemaFieldTypes.NUMERIC + }), + client.hSet('1', 'field', '1'), + client.hSet('2', 'field', '2') + ]); + + assert.deepEqual( + await client.ft.aggregate('index', '*', { + STEPS: [{ + type: AggregateSteps.GROUPBY, + REDUCE: [{ + type: AggregateGroupByReducers.SUM, + property: '@field', + AS: 'sum' + }, { + type: AggregateGroupByReducers.AVG, + property: '@field', + AS: 'avg' + }] + }] + }), + { + total: 1, + results: [ + Object.create(null, { + sum: { + value: '3', + configurable: true, + enumerable: true + }, + avg: { + value: '1.5', + configurable: true, + enumerable: true + } + }) + ] + } + ); + }, GLOBAL.SERVERS.OPEN); +}); diff --git a/packages/search/lib/commands/AGGREGATE.ts b/packages/search/lib/commands/AGGREGATE.ts new file mode 100644 index 00000000000..affc98b51d7 --- /dev/null +++ b/packages/search/lib/commands/AGGREGATE.ts @@ -0,0 +1,283 @@ +import { RedisCommandArguments } from '@redis/client/dist/lib/commands'; +import { pushVerdictArgument, transformReplyTuples, TuplesObject } from '@redis/client/dist/lib/commands/generic-transformers'; +import { PropertyName, pushArgumentsWithLength, pushSortByArguments, SortByOptions } from '.'; + +export enum AggregateSteps { + GROUPBY = 'GROUPBY', + SORTBY = 'SORTBY', + APPLY = 'APPLY', + LIMIT = 'LIMIT', + FILTER = 'FILTER' +} + +interface AggregateStep { + type: T; +} + +export enum AggregateGroupByReducers { + COUNT = 'COUNT', + COUNT_DISTINCT = 'COUNT_DISTINCT', + COUNT_DISTINCTISH = 'COUNT_DISTINCTISH', + SUM = 'SUM', + MIN = 'MIN', + MAX = 'MAX', + AVG = 'AVG', + STDDEV = 'STDDEV', + QUANTILE = 'QUANTILE', + TOLIST = 'TOLIST', + TO_LIST = 'TOLIST', + FIRST_VALUE = 'FIRST_VALUE', + RANDOM_SAMPLE = 'RANDOM_SAMPLE' +} + +interface GroupByReducer { + type: T; + AS?: string; +} + +type CountReducer = GroupByReducer; + +interface CountDistinctReducer extends GroupByReducer { + property: PropertyName; +} + +interface CountDistinctishReducer extends GroupByReducer { + property: PropertyName; +} + +interface SumReducer extends GroupByReducer { + property: PropertyName; +} + +interface MinReducer extends GroupByReducer { + property: PropertyName; +} + +interface MaxReducer extends GroupByReducer { + property: PropertyName; +} + +interface AvgReducer extends GroupByReducer { + property: PropertyName; +} + +interface StdDevReducer extends GroupByReducer { + property: PropertyName; +} + +interface QuantileReducer extends GroupByReducer { + property: PropertyName; + quantile: number; +} + +interface ToListReducer extends GroupByReducer { + property: PropertyName; +} + +interface FirstValueReducer extends GroupByReducer { + property: PropertyName; + BY?: PropertyName | { + property: PropertyName; + direction?: 'ASC' | 'DESC'; + }; +} + +interface RandomSampleReducer extends GroupByReducer { + property: PropertyName; + sampleSize: number; +} + +type GroupByReducers = CountReducer | CountDistinctReducer | CountDistinctishReducer | SumReducer | MinReducer | MaxReducer | AvgReducer | StdDevReducer | QuantileReducer | ToListReducer | FirstValueReducer | RandomSampleReducer; + +interface GroupByStep extends AggregateStep { + properties?: PropertyName | Array; + REDUCE: GroupByReducers | Array; +} + +interface SortStep extends AggregateStep { + BY: SortByOptions | Array; + MAX?: number; +} + +interface ApplyStep extends AggregateStep { + expression: string; + AS: string; +} + +interface LimitStep extends AggregateStep { + from: number; + size: number; +} + +interface FilterStep extends AggregateStep { + expression: string; +} + +type LoadField = PropertyName | { + identifier: PropertyName; + AS?: string; +} + +interface AggregateOptions { + VERBATIM?: true; + LOAD?: LoadField | Array; + STEPS?: Array; +} + +export function transformArguments(index: string, query: string, options?: AggregateOptions): RedisCommandArguments { + const args = ['FT.AGGREGATE', index, query]; + + if (options?.VERBATIM) { + args.push('VERBATIM'); + } + + if (options?.LOAD) { + args.push('LOAD'); + pushArgumentsWithLength(args, () => { + if (Array.isArray(options.LOAD)) { + for (const load of options.LOAD) { + pushLoadField(args, load); + } + } else { + pushLoadField(args, options.LOAD!); + } + }); + } + + if (options?.STEPS) { + for (const step of options.STEPS) { + switch (step.type) { + case AggregateSteps.GROUPBY: + args.push('GROUPBY'); + if (!step.properties) { + args.push('0'); + } else { + pushVerdictArgument(args, step.properties); + } + + if (Array.isArray(step.REDUCE)) { + for (const reducer of step.REDUCE) { + pushGroupByReducer(args, reducer); + } + } else { + pushGroupByReducer(args, step.REDUCE); + } + + break; + + case AggregateSteps.SORTBY: + pushSortByArguments(args, 'SORTBY', step.BY); + + if (step.MAX) { + args.push('MAX', step.MAX.toString()); + } + + break; + + case AggregateSteps.APPLY: + args.push('APPLY', step.expression, 'AS', step.AS); + break; + + case AggregateSteps.LIMIT: + args.push('LIMIT', step.from.toString(), step.size.toString()); + break; + + case AggregateSteps.FILTER: + args.push('FILTER', step.expression); + break; + } + } + } + + return args; +} + +function pushLoadField(args: RedisCommandArguments, toLoad: LoadField): void { + if (typeof toLoad === 'string') { + args.push(toLoad); + } else { + args.push(toLoad.identifier); + + if (toLoad.AS) { + args.push('AS', toLoad.AS); + } + } +} + +function pushGroupByReducer(args: RedisCommandArguments, reducer: GroupByReducers): void { + args.push('REDUCE', reducer.type); + + switch (reducer.type) { + case AggregateGroupByReducers.COUNT: + args.push('0'); + break; + + case AggregateGroupByReducers.COUNT_DISTINCT: + case AggregateGroupByReducers.COUNT_DISTINCTISH: + case AggregateGroupByReducers.SUM: + case AggregateGroupByReducers.MIN: + case AggregateGroupByReducers.MAX: + case AggregateGroupByReducers.AVG: + case AggregateGroupByReducers.STDDEV: + case AggregateGroupByReducers.TOLIST: + args.push('1', reducer.property); + break; + + case AggregateGroupByReducers.QUANTILE: + args.push('2', reducer.property, reducer.quantile.toString()); + break; + + case AggregateGroupByReducers.FIRST_VALUE: { + pushArgumentsWithLength(args, () => { + args.push(reducer.property); + + if (reducer.BY) { + args.push('BY'); + if (typeof reducer.BY === 'string') { + args.push(reducer.BY); + } else { + args.push(reducer.BY.property); + + if (reducer.BY.direction) { + args.push(reducer.BY.direction); + } + } + } + }); + + break; + } + + case AggregateGroupByReducers.RANDOM_SAMPLE: + args.push('2', reducer.property, reducer.sampleSize.toString()); + break; + } + + if (reducer.AS) { + args.push('AS', reducer.AS); + } +} + +type AggregateRawReply = [ + total: number, + ...results: Array> +]; + +interface AggregateReply { + total: number; + results: Array; +} + +export function transformReply(rawReply: AggregateRawReply): AggregateReply { + const results: Array = []; + for (let i = 1; i < rawReply.length; i++) { + results.push( + transformReplyTuples(rawReply[i] as Array) + ); + } + + return { + total: rawReply[0], + results + }; +} \ No newline at end of file diff --git a/packages/search/lib/commands/ALIASADD.spec.ts b/packages/search/lib/commands/ALIASADD.spec.ts new file mode 100644 index 00000000000..7bb2452838b --- /dev/null +++ b/packages/search/lib/commands/ALIASADD.spec.ts @@ -0,0 +1,11 @@ +import { strict as assert } from 'assert'; +import { transformArguments } from './ALIASADD'; + +describe('ALIASADD', () => { + it('transformArguments', () => { + assert.deepEqual( + transformArguments('alias', 'index'), + ['FT.ALIASADD', 'alias', 'index'] + ); + }); +}); diff --git a/packages/search/lib/commands/ALIASADD.ts b/packages/search/lib/commands/ALIASADD.ts new file mode 100644 index 00000000000..552c1add695 --- /dev/null +++ b/packages/search/lib/commands/ALIASADD.ts @@ -0,0 +1,5 @@ +export function transformArguments(name: string, index: string): Array { + return ['FT.ALIASADD', name, index]; +} + +export declare function transformReply(): 'OK'; diff --git a/packages/search/lib/commands/ALIASDEL.spec.ts b/packages/search/lib/commands/ALIASDEL.spec.ts new file mode 100644 index 00000000000..5255ba835db --- /dev/null +++ b/packages/search/lib/commands/ALIASDEL.spec.ts @@ -0,0 +1,11 @@ +import { strict as assert } from 'assert'; +import { transformArguments } from './ALIASDEL'; + +describe('ALIASDEL', () => { + it('transformArguments', () => { + assert.deepEqual( + transformArguments('alias', 'index'), + ['FT.ALIASDEL', 'alias', 'index'] + ); + }); +}); diff --git a/packages/search/lib/commands/ALIASDEL.ts b/packages/search/lib/commands/ALIASDEL.ts new file mode 100644 index 00000000000..434b4df3dea --- /dev/null +++ b/packages/search/lib/commands/ALIASDEL.ts @@ -0,0 +1,5 @@ +export function transformArguments(name: string, index: string): Array { + return ['FT.ALIASDEL', name, index]; +} + +export declare function transformReply(): 'OK'; diff --git a/packages/search/lib/commands/ALIASUPDATE.spec.ts b/packages/search/lib/commands/ALIASUPDATE.spec.ts new file mode 100644 index 00000000000..79421b1a20d --- /dev/null +++ b/packages/search/lib/commands/ALIASUPDATE.spec.ts @@ -0,0 +1,11 @@ +import { strict as assert } from 'assert'; +import { transformArguments } from './ALIASUPDATE'; + +describe('ALIASUPDATE', () => { + it('transformArguments', () => { + assert.deepEqual( + transformArguments('alias', 'index'), + ['FT.ALIASUPDATE', 'alias', 'index'] + ); + }); +}); diff --git a/packages/search/lib/commands/ALIASUPDATE.ts b/packages/search/lib/commands/ALIASUPDATE.ts new file mode 100644 index 00000000000..ac64ef57c3f --- /dev/null +++ b/packages/search/lib/commands/ALIASUPDATE.ts @@ -0,0 +1,5 @@ +export function transformArguments(name: string, index: string): Array { + return ['FT.ALIASUPDATE', name, index]; +} + +export declare function transformReply(): 'OK'; diff --git a/packages/search/lib/commands/CONFIG_GET.spec.ts b/packages/search/lib/commands/CONFIG_GET.spec.ts new file mode 100644 index 00000000000..8614f443426 --- /dev/null +++ b/packages/search/lib/commands/CONFIG_GET.spec.ts @@ -0,0 +1,25 @@ +import { strict as assert } from 'assert'; +import testUtils, { GLOBAL } from '../test-utils'; +import { transformArguments } from './CONFIG_GET'; + +describe('CONFIG GET', () => { + it('transformArguments', () => { + assert.deepEqual( + transformArguments('TIMEOUT'), + ['FT.CONFIG', 'GET', 'TIMEOUT'] + ); + }); + + testUtils.testWithClient('client.ft.configGet', async client => { + assert.deepEqual( + await client.ft.configGet('TIMEOUT'), + Object.create(null, { + TIMEOUT: { + value: '500', + configurable: true, + enumerable: true + } + }) + ); + }, GLOBAL.SERVERS.OPEN); +}); diff --git a/packages/search/lib/commands/CONFIG_GET.ts b/packages/search/lib/commands/CONFIG_GET.ts new file mode 100644 index 00000000000..fbf1f1164b9 --- /dev/null +++ b/packages/search/lib/commands/CONFIG_GET.ts @@ -0,0 +1,16 @@ +export function transformArguments(option: string) { + return ['FT.CONFIG', 'GET', option]; +} + +interface ConfigGetReply { + [option: string]: string | null; +} + +export function transformReply(rawReply: Array<[string, string | null]>): ConfigGetReply { + const transformedReply: ConfigGetReply = Object.create(null); + for (const [key, value] of rawReply) { + transformedReply[key] = value; + } + + return transformedReply; +} diff --git a/packages/search/lib/commands/CONFIG_SET.spec.ts b/packages/search/lib/commands/CONFIG_SET.spec.ts new file mode 100644 index 00000000000..59cb63a3d8e --- /dev/null +++ b/packages/search/lib/commands/CONFIG_SET.spec.ts @@ -0,0 +1,12 @@ +import { strict as assert } from 'assert'; +import testUtils, { GLOBAL } from '../test-utils'; +import { transformArguments } from './CONFIG_SET'; + +describe('CONFIG SET', () => { + it('transformArguments', () => { + assert.deepEqual( + transformArguments('TIMEOUT', '500'), + ['FT.CONFIG', 'SET', 'TIMEOUT', '500'] + ); + }); +}); diff --git a/packages/search/lib/commands/CONFIG_SET.ts b/packages/search/lib/commands/CONFIG_SET.ts new file mode 100644 index 00000000000..93b76d79edf --- /dev/null +++ b/packages/search/lib/commands/CONFIG_SET.ts @@ -0,0 +1,5 @@ +export function transformArguments(option: string, value: string): Array { + return ['FT.CONFIG', 'SET', option, value]; +} + +export declare function transformReply(): 'OK'; diff --git a/packages/search/lib/commands/CREATE.spec.ts b/packages/search/lib/commands/CREATE.spec.ts new file mode 100644 index 00000000000..2ac68cc9710 --- /dev/null +++ b/packages/search/lib/commands/CREATE.spec.ts @@ -0,0 +1,347 @@ +import { strict as assert } from 'assert'; +import testUtils, { GLOBAL } from '../test-utils'; +import { RedisSearchLanguages, SchemaFieldTypes, SchemaTextFieldPhonetics, transformArguments } from './CREATE'; + +describe('CREATE', () => { + describe('transformArguments', () => { + it('simple', () => { + assert.deepEqual( + transformArguments('index', {}), + ['FT.CREATE', 'index', 'SCHEMA'] + ); + }); + + describe('with fields', () => { + describe('TEXT', () => { + it('without options', () => { + assert.deepEqual( + transformArguments('index', { + field: SchemaFieldTypes.TEXT + }), + ['FT.CREATE', 'index', 'SCHEMA', 'field', 'TEXT'] + ); + }); + + it('with NOSTEM', () => { + assert.deepEqual( + transformArguments('index', { + field: { + type: SchemaFieldTypes.TEXT, + NOSTEM: true + } + }), + ['FT.CREATE', 'index', 'SCHEMA', 'field', 'TEXT', 'NOSTEM'] + ); + }); + + it('with WEIGHT', () => { + assert.deepEqual( + transformArguments('index', { + field: { + type: SchemaFieldTypes.TEXT, + WEIGHT: 1 + } + }), + ['FT.CREATE', 'index', 'SCHEMA', 'field', 'TEXT', 'WEIGHT', '1'] + ); + }); + + it('with PHONETIC', () => { + assert.deepEqual( + transformArguments('index', { + field: { + type: SchemaFieldTypes.TEXT, + PHONETIC: SchemaTextFieldPhonetics.DM_EN + } + }), + ['FT.CREATE', 'index', 'SCHEMA', 'field', 'TEXT', 'PHONETIC', SchemaTextFieldPhonetics.DM_EN] + ); + }); + }); + + it('NUMERIC', () => { + assert.deepEqual( + transformArguments('index', { + field: SchemaFieldTypes.NUMERIC + }), + ['FT.CREATE', 'index', 'SCHEMA', 'field', 'NUMERIC'] + ); + }); + + it('GEO', () => { + assert.deepEqual( + transformArguments('index', { + field: SchemaFieldTypes.GEO + }), + ['FT.CREATE', 'index', 'SCHEMA', 'field', 'GEO'] + ); + }); + + describe('TAG', () => { + describe('without options', () => { + it('SchemaFieldTypes.TAG', () => { + assert.deepEqual( + transformArguments('index', { + field: SchemaFieldTypes.TAG + }), + ['FT.CREATE', 'index', 'SCHEMA', 'field', 'TAG'] + ); + }); + + it('{ type: SchemaFieldTypes.TAG }', () => { + assert.deepEqual( + transformArguments('index', { + field: { + type: SchemaFieldTypes.TAG + } + }), + ['FT.CREATE', 'index', 'SCHEMA', 'field', 'TAG'] + ); + }); + }); + + it('with SEPERATOR', () => { + assert.deepEqual( + transformArguments('index', { + field: { + type: SchemaFieldTypes.TAG, + SEPERATOR: 'seperator' + } + }), + ['FT.CREATE', 'index', 'SCHEMA', 'field', 'TAG', 'SEPERATOR', 'seperator'] + ); + }); + }); + + describe('with generic options', () => { + it('with AS', () => { + assert.deepEqual( + transformArguments('index', { + field: { + type: SchemaFieldTypes.TEXT, + AS: 'as' + } + }), + ['FT.CREATE', 'index', 'SCHEMA', 'field', 'AS', 'as', 'TEXT'] + ); + }); + + it('with CASESENSITIVE', () => { + assert.deepEqual( + transformArguments('index', { + field: { + type: SchemaFieldTypes.TEXT, + CASESENSITIVE: true + } + }), + ['FT.CREATE', 'index', 'SCHEMA', 'field', 'TEXT', 'CASESENSITIVE'] + ); + }); + + describe('with SORTABLE', () => { + it('true', () => { + assert.deepEqual( + transformArguments('index', { + field: { + type: SchemaFieldTypes.TEXT, + SORTABLE: true + } + }), + ['FT.CREATE', 'index', 'SCHEMA', 'field', 'TEXT', 'SORTABLE'] + ); + }); + + it('UNF', () => { + assert.deepEqual( + transformArguments('index', { + field: { + type: SchemaFieldTypes.TEXT, + SORTABLE: 'UNF' + } + }), + ['FT.CREATE', 'index', 'SCHEMA', 'field', 'TEXT', 'SORTABLE', 'UNF'] + ); + }); + }); + + it('with NOINDEX', () => { + assert.deepEqual( + transformArguments('index', { + field: { + type: SchemaFieldTypes.TEXT, + NOINDEX: true + } + }), + ['FT.CREATE', 'index', 'SCHEMA', 'field', 'TEXT', 'NOINDEX'] + ); + }); + }); + }); + + it('with ON', () => { + assert.deepEqual( + transformArguments('index', {}, { + ON: 'HASH' + }), + ['FT.CREATE', 'index', 'ON', 'HASH', 'SCHEMA'] + ); + }); + + describe('with PREFIX', () => { + it('string', () => { + assert.deepEqual( + transformArguments('index', {}, { + PREFIX: 'prefix' + }), + ['FT.CREATE', 'index', 'PREFIX', '1', 'prefix', 'SCHEMA'] + ); + }); + + it('Array', () => { + assert.deepEqual( + transformArguments('index', {}, { + PREFIX: ['1', '2'] + }), + ['FT.CREATE', 'index', 'PREFIX', '2', '1', '2', 'SCHEMA'] + ); + }); + }); + + it('with FILTER', () => { + assert.deepEqual( + transformArguments('index', {}, { + FILTER: '@field != ""' + }), + ['FT.CREATE', 'index', 'FILTER', '@field != ""', 'SCHEMA'] + ); + }); + + it('with LANGUAGE', () => { + assert.deepEqual( + transformArguments('index', {}, { + LANGUAGE: RedisSearchLanguages.ARABIC + }), + ['FT.CREATE', 'index', 'LANGUAGE', RedisSearchLanguages.ARABIC, 'SCHEMA'] + ); + }); + + it('with LANGUAGE_FIELD', () => { + assert.deepEqual( + transformArguments('index', {}, { + LANGUAGE_FIELD: '@field' + }), + ['FT.CREATE', 'index', 'LANGUAGE_FIELD', '@field', 'SCHEMA'] + ); + }); + + it('with SCORE', () => { + assert.deepEqual( + transformArguments('index', {}, { + SCORE: 1 + }), + ['FT.CREATE', 'index', 'SCORE', '1', 'SCHEMA'] + ); + }); + + it('with SCORE_FIELD', () => { + assert.deepEqual( + transformArguments('index', {}, { + SCORE_FIELD: '@field' + }), + ['FT.CREATE', 'index', 'SCORE_FIELD', '@field', 'SCHEMA'] + ); + }); + + it('with MAXTEXTFIELDS', () => { + assert.deepEqual( + transformArguments('index', {}, { + MAXTEXTFIELDS: true + }), + ['FT.CREATE', 'index', 'MAXTEXTFIELDS', 'SCHEMA'] + ); + }); + + it('with TEMPORARY', () => { + assert.deepEqual( + transformArguments('index', {}, { + TEMPORARY: 1 + }), + ['FT.CREATE', 'index', 'TEMPORARY', '1', 'SCHEMA'] + ); + }); + + it('with NOOFFSETS', () => { + assert.deepEqual( + transformArguments('index', {}, { + NOOFFSETS: true + }), + ['FT.CREATE', 'index', 'NOOFFSETS', 'SCHEMA'] + ); + }); + + it('with NOHL', () => { + assert.deepEqual( + transformArguments('index', {}, { + NOHL: true + }), + ['FT.CREATE', 'index', 'NOHL', 'SCHEMA'] + ); + }); + + it('with NOFIELDS', () => { + assert.deepEqual( + transformArguments('index', {}, { + NOFIELDS: true + }), + ['FT.CREATE', 'index', 'NOFIELDS', 'SCHEMA'] + ); + }); + + it('with NOFREQS', () => { + assert.deepEqual( + transformArguments('index', {}, { + NOFREQS: true + }), + ['FT.CREATE', 'index', 'NOFREQS', 'SCHEMA'] + ); + }); + + it('with SKIPINITIALSCAN', () => { + assert.deepEqual( + transformArguments('index', {}, { + SKIPINITIALSCAN: true + }), + ['FT.CREATE', 'index', 'SKIPINITIALSCAN', 'SCHEMA'] + ); + }); + + describe('with STOPWORDS', () => { + it('string', () => { + assert.deepEqual( + transformArguments('index', {}, { + STOPWORDS: 'stopword' + }), + ['FT.CREATE', 'index', 'STOPWORDS', '1', 'stopword', 'SCHEMA'] + ); + }); + + it('Array', () => { + assert.deepEqual( + transformArguments('index', {}, { + STOPWORDS: ['1', '2'] + }), + ['FT.CREATE', 'index', 'STOPWORDS', '2', '1', '2', 'SCHEMA'] + ); + }); + }); + }); + + testUtils.testWithClient('client.ft.create', async client => { + assert.equal( + await client.ft.create('index', { + field: SchemaFieldTypes.TEXT // TODO: shouldn't be mandatory + }), + 'OK' + ); + }, GLOBAL.SERVERS.OPEN); +}); diff --git a/packages/search/lib/commands/CREATE.ts b/packages/search/lib/commands/CREATE.ts new file mode 100644 index 00000000000..94c063ec503 --- /dev/null +++ b/packages/search/lib/commands/CREATE.ts @@ -0,0 +1,222 @@ +import { pushOptionalVerdictArgument } from '@redis/client/dist/lib/commands/generic-transformers'; +import { PropertyName } from '.'; + +export enum SchemaFieldTypes { + TEXT = 'TEXT', + NUMERIC = 'NUMERIC', + GEO = 'GEO', + TAG = 'TAG', +} + +type CreateSchemaField> = T | ({ + type: T; + AS?: string; + CASESENSITIVE?: true; + SORTABLE?: true | 'UNF'; + NOINDEX?: true; +} & E); + +export enum SchemaTextFieldPhonetics { + DM_EN = 'dm:en', + DM_FR = 'dm:fr', + FM_PT = 'dm:pt', + DM_ES = 'dm:es' +} + +type CreateSchemaTextField = CreateSchemaField; + +type CreateSchemaNumericField = CreateSchemaField; + +type CreateSchemaGeoField = CreateSchemaField; + +type CreateSchemaTagField = CreateSchemaField; + +interface CreateSchema { + [field: string]: + CreateSchemaTextField | + CreateSchemaNumericField | + CreateSchemaGeoField | + CreateSchemaTagField +} + +export enum RedisSearchLanguages { + ARABIC = 'Arabic', + BASQUE = 'Basque', + CATALANA = 'Catalan', + DANISH = 'Danish', + DUTCH = 'Dutch', + ENGLISH = 'English', + FINNISH = 'Finnish', + FRENCH = 'French', + GERMAN = 'German', + GREEK = 'Greek', + HUNGARIAN = 'Hungarian', + INDONESAIN = 'Indonesian', + IRISH = 'Irish', + ITALIAN = 'Italian', + LITHUANIAN = 'Lithuanian', + NEPALI = 'Nepali', + NORWEIGAN = 'Norwegian', + PORTUGUESE = 'Portuguese', + ROMANIAN = 'Romanian', + RUSSIAN = 'Russian', + SPANISH = 'Spanish', + SWEDISH = 'Swedish', + TAMIL = 'Tamil', + TURKISH = 'Turkish', + CHINESE = 'Chinese' +} + +interface CreateOptions { + ON?: 'HASH' | 'JSON'; + PREFIX?: string | Array; + FILTER?: string; + LANGUAGE?: RedisSearchLanguages; + LANGUAGE_FIELD?: PropertyName; + SCORE?: number; + SCORE_FIELD?: PropertyName; + // PAYLOAD_FIELD?: string; + MAXTEXTFIELDS?: true; + TEMPORARY?: number; + NOOFFSETS?: true; + NOHL?: true; + NOFIELDS?: true; + NOFREQS?: true; + SKIPINITIALSCAN?: true; + STOPWORDS?: string | Array; +} + +export function transformArguments(index: string, schema: CreateSchema, options?: CreateOptions): Array { + const args = ['FT.CREATE', index]; + + if (options?.ON) { + args.push('ON', options.ON); + } + + pushOptionalVerdictArgument(args, 'PREFIX', options?.PREFIX); + + if (options?.FILTER) { + args.push('FILTER', options.FILTER); + } + + if (options?.LANGUAGE) { + args.push('LANGUAGE', options.LANGUAGE); + } + + if (options?.LANGUAGE_FIELD) { + args.push('LANGUAGE_FIELD', options.LANGUAGE_FIELD); + } + + if (options?.SCORE) { + args.push('SCORE', options.SCORE.toString()); + } + + if (options?.SCORE_FIELD) { + args.push('SCORE_FIELD', options.SCORE_FIELD); + } + + // if (options?.PAYLOAD_FIELD) { + // args.push('PAYLOAD_FIELD', options.PAYLOAD_FIELD); + // } + + if (options?.MAXTEXTFIELDS) { + args.push('MAXTEXTFIELDS'); + } + + if (options?.TEMPORARY) { + args.push('TEMPORARY', options.TEMPORARY.toString()); + } + + if (options?.NOOFFSETS) { + args.push('NOOFFSETS'); + } + + if (options?.NOHL) { + args.push('NOHL'); + } + + if (options?.NOFIELDS) { + args.push('NOFIELDS'); + } + + if (options?.NOFREQS) { + args.push('NOFREQS'); + } + + if (options?.SKIPINITIALSCAN) { + args.push('SKIPINITIALSCAN'); + } + + pushOptionalVerdictArgument(args, 'STOPWORDS', options?.STOPWORDS); + + args.push('SCHEMA'); + + for (const [field, fieldOptions] of Object.entries(schema)) { + args.push(field); + + if (typeof fieldOptions === 'string') { + args.push(fieldOptions); + continue; + } + + if (fieldOptions.AS) { + args.push('AS', fieldOptions.AS); + } + + args.push(fieldOptions.type); + + switch (fieldOptions.type) { + case 'TEXT': + if (fieldOptions.NOSTEM) { + args.push('NOSTEM'); + } + + if (fieldOptions.WEIGHT) { + args.push('WEIGHT', fieldOptions.WEIGHT.toString()); + } + + if (fieldOptions.PHONETIC) { + args.push('PHONETIC', fieldOptions.PHONETIC); + } + + break; + + // case 'NUMERIC': + // case 'GEO': + // break; + + case 'TAG': + if (fieldOptions.SEPERATOR) { + args.push('SEPERATOR', fieldOptions.SEPERATOR); + } + + break; + } + + if (fieldOptions.CASESENSITIVE) { + args.push('CASESENSITIVE'); + } + + if (fieldOptions.SORTABLE) { + args.push('SORTABLE'); + + if (fieldOptions.SORTABLE === 'UNF') { + args.push('UNF'); + } + } + + if (fieldOptions.NOINDEX) { + args.push('NOINDEX'); + } + } + + return args; +} + +export declare function transformReply(): 'OK'; diff --git a/packages/search/lib/commands/DICTADD.spec.ts b/packages/search/lib/commands/DICTADD.spec.ts new file mode 100644 index 00000000000..b5f29dd4083 --- /dev/null +++ b/packages/search/lib/commands/DICTADD.spec.ts @@ -0,0 +1,28 @@ +import { strict as assert } from 'assert'; +import testUtils, { GLOBAL } from '../test-utils'; +import { transformArguments } from './DICTADD'; + +describe('DICTADD', () => { + describe('transformArguments', () => { + it('string', () => { + assert.deepEqual( + transformArguments('dictionary', 'term'), + ['FT.DICTADD', 'dictionary', 'term'] + ); + }); + + it('Array', () => { + assert.deepEqual( + transformArguments('dictionary', ['1', '2']), + ['FT.DICTADD', 'dictionary', '1', '2'] + ); + }); + }); + + testUtils.testWithClient('client.ft.dictAdd', async client => { + assert.equal( + await client.ft.dictAdd('dictionary', 'term'), + 1 + ); + }, GLOBAL.SERVERS.OPEN); +}); diff --git a/packages/search/lib/commands/DICTADD.ts b/packages/search/lib/commands/DICTADD.ts new file mode 100644 index 00000000000..60af11fd41f --- /dev/null +++ b/packages/search/lib/commands/DICTADD.ts @@ -0,0 +1,8 @@ +import { RedisCommandArguments } from '@redis/client/dist/lib/commands'; +import { pushVerdictArguments } from '@redis/client/dist/lib/commands/generic-transformers'; + +export function transformArguments(dictionary: string, term: string | Array): RedisCommandArguments { + return pushVerdictArguments(['FT.DICTADD', dictionary], term); +} + +export declare function transformReply(): number; diff --git a/packages/search/lib/commands/DICTDEL.spec.ts b/packages/search/lib/commands/DICTDEL.spec.ts new file mode 100644 index 00000000000..5ffa6b6b84f --- /dev/null +++ b/packages/search/lib/commands/DICTDEL.spec.ts @@ -0,0 +1,28 @@ +import { strict as assert } from 'assert'; +import testUtils, { GLOBAL } from '../test-utils'; +import { transformArguments } from './DICTDEL'; + +describe('DICTDEL', () => { + describe('transformArguments', () => { + it('string', () => { + assert.deepEqual( + transformArguments('dictionary', 'term'), + ['FT.DICTDEL', 'dictionary', 'term'] + ); + }); + + it('Array', () => { + assert.deepEqual( + transformArguments('dictionary', ['1', '2']), + ['FT.DICTDEL', 'dictionary', '1', '2'] + ); + }); + }); + + testUtils.testWithClient('client.ft.dictDel', async client => { + assert.equal( + await client.ft.dictDel('dictionary', 'term'), + 0 + ); + }, GLOBAL.SERVERS.OPEN); +}); diff --git a/packages/search/lib/commands/DICTDEL.ts b/packages/search/lib/commands/DICTDEL.ts new file mode 100644 index 00000000000..a1b728f1926 --- /dev/null +++ b/packages/search/lib/commands/DICTDEL.ts @@ -0,0 +1,8 @@ +import { RedisCommandArguments } from '@redis/client/dist/lib/commands'; +import { pushVerdictArguments } from '@redis/client/dist/lib/commands/generic-transformers'; + +export function transformArguments(dictionary: string, term: string | Array): RedisCommandArguments { + return pushVerdictArguments(['FT.DICTDEL', dictionary], term); +} + +export declare function transformReply(): number; diff --git a/packages/search/lib/commands/DICTDUMP.spec.ts b/packages/search/lib/commands/DICTDUMP.spec.ts new file mode 100644 index 00000000000..9896fb9440d --- /dev/null +++ b/packages/search/lib/commands/DICTDUMP.spec.ts @@ -0,0 +1,21 @@ +import { strict as assert } from 'assert'; +import testUtils, { GLOBAL } from '../test-utils'; +import { transformArguments } from './DICTDUMP'; + +describe('DICTDUMP', () => { + it('transformArguments', () => { + assert.deepEqual( + transformArguments('dictionary'), + ['FT.DICTDUMP', 'dictionary'] + ); + }); + + testUtils.testWithClient('client.ft.dictDump', async client => { + await client.ft.dictAdd('dictionary', 'string') + + assert.deepEqual( + await client.ft.dictDump('dictionary'), + ['string'] + ); + }, GLOBAL.SERVERS.OPEN); +}); diff --git a/packages/search/lib/commands/DICTDUMP.ts b/packages/search/lib/commands/DICTDUMP.ts new file mode 100644 index 00000000000..1427bb42cb7 --- /dev/null +++ b/packages/search/lib/commands/DICTDUMP.ts @@ -0,0 +1,5 @@ +export function transformArguments(dictionary: string): Array { + return ['FT.DICTDUMP', dictionary]; +} + +export declare function transformReply(): Array; diff --git a/packages/search/lib/commands/DROPINDEX.spec.ts b/packages/search/lib/commands/DROPINDEX.spec.ts new file mode 100644 index 00000000000..751e274ba60 --- /dev/null +++ b/packages/search/lib/commands/DROPINDEX.spec.ts @@ -0,0 +1,33 @@ +import { strict as assert } from 'assert'; +import testUtils, { GLOBAL } from '../test-utils'; +import { SchemaFieldTypes } from './CREATE'; +import { transformArguments } from './DROPINDEX'; + +describe('DROPINDEX', () => { + describe('transformArguments', () => { + it('without options', () => { + assert.deepEqual( + transformArguments('index'), + ['FT.DROPINDEX', 'index'] + ); + }); + + it('with DD', () => { + assert.deepEqual( + transformArguments('index', { DD: true }), + ['FT.DROPINDEX', 'index', 'DD'] + ); + }); + }); + + testUtils.testWithClient('client.ft.dropIndex', async client => { + await client.ft.create('index', { + field: SchemaFieldTypes.TEXT // TODO: shouldn't be mandatory + }); + + assert.equal( + await client.ft.dropIndex('index'), + 'OK' + ); + }, GLOBAL.SERVERS.OPEN); +}); diff --git a/packages/search/lib/commands/DROPINDEX.ts b/packages/search/lib/commands/DROPINDEX.ts new file mode 100644 index 00000000000..7897a9dd82e --- /dev/null +++ b/packages/search/lib/commands/DROPINDEX.ts @@ -0,0 +1,15 @@ +interface DropIndexOptions { + DD?: true; +} + +export function transformArguments(index: string, options?: DropIndexOptions): Array { + const args = ['FT.DROPINDEX', index]; + + if (options?.DD) { + args.push('DD'); + } + + return args; +} + +export declare function transformReply(): 'OK'; diff --git a/packages/search/lib/commands/EXPLAIN.spec.ts b/packages/search/lib/commands/EXPLAIN.spec.ts new file mode 100644 index 00000000000..dd55e038710 --- /dev/null +++ b/packages/search/lib/commands/EXPLAIN.spec.ts @@ -0,0 +1,11 @@ +import { strict as assert } from 'assert'; +import { transformArguments } from './EXPLAIN'; + +describe('EXPLAIN', () => { + it('transformArguments', () => { + assert.deepEqual( + transformArguments('index', '*'), + ['FT.EXPLAIN', 'index', '*'] + ); + }); +}); diff --git a/packages/search/lib/commands/EXPLAIN.ts b/packages/search/lib/commands/EXPLAIN.ts new file mode 100644 index 00000000000..c41cd9a4aac --- /dev/null +++ b/packages/search/lib/commands/EXPLAIN.ts @@ -0,0 +1,7 @@ +export const IS_READ_ONLY = true; + +export function transformArguments(index: string, query: string): Array { + return ['FT.EXPLAIN', index, query]; +} + +export declare function transformReply(): string; diff --git a/packages/search/lib/commands/EXPLAINCLI.spec.ts b/packages/search/lib/commands/EXPLAINCLI.spec.ts new file mode 100644 index 00000000000..238ef44eaaa --- /dev/null +++ b/packages/search/lib/commands/EXPLAINCLI.spec.ts @@ -0,0 +1,11 @@ +import { strict as assert } from 'assert'; +import { transformArguments } from './EXPLAINCLI'; + +describe('EXPLAINCLI', () => { + it('transformArguments', () => { + assert.deepEqual( + transformArguments('index', '*'), + ['FT.EXPLAINCLI', 'index', '*'] + ); + }); +}); diff --git a/packages/search/lib/commands/EXPLAINCLI.ts b/packages/search/lib/commands/EXPLAINCLI.ts new file mode 100644 index 00000000000..db97fb9c8da --- /dev/null +++ b/packages/search/lib/commands/EXPLAINCLI.ts @@ -0,0 +1,7 @@ +export const IS_READ_ONLY = true; + +export function transformArguments(index: string, query: string): Array { + return ['FT.EXPLAINCLI', index, query]; +} + +export declare function transformReply(): Array; diff --git a/packages/search/lib/commands/INFO.spec.ts b/packages/search/lib/commands/INFO.spec.ts new file mode 100644 index 00000000000..fa50a4b0cd8 --- /dev/null +++ b/packages/search/lib/commands/INFO.spec.ts @@ -0,0 +1,65 @@ +import { strict as assert } from 'assert'; +import testUtils, { GLOBAL } from '../test-utils'; +import { SchemaFieldTypes } from './CREATE'; +import { transformArguments } from './INFO'; + +describe('INFO', () => { + it('transformArguments', () => { + assert.deepEqual( + transformArguments('index'), + ['FT.INFO', 'index'] + ); + }); + + testUtils.testWithClient('client.ft.info', async client => { + await client.ft.create('index', {}, { + ON: 'HASH' // TODO: shouldn't be mandatory + }); + + assert.deepEqual( + await client.ft.info('index'), + { + indexName: 'index', + indexOptions: [], + indexDefinition: { + defaultScore: '1', + keyType: 'HASH', + prefixes: [''] + }, + attributes: [], + numDocs: '0', + maxDocId: '0', + numTerms: '0', + numRecords: '0', + invertedSzMb: '0', + totalInvertedIndexBlocks: '0', + offsetVectorsSzMb: '0', + docTableSizeMb: '0', + sortableValuesSizeMb: '0', + keyTableSizeMb: '0', + recordsPerDocAvg: '-nan', + bytesPerRecordAvg: '-nan', + offsetsPerTermAvg: '-nan', + offsetBitsPerRecordAvg: '-nan', + hashIndexingFailures: '0', + indexing: '0', + percentIndexed: '1', + gcStats: { + bytesCollected: '0', + totalMsRun: '0', + totalCycles: '0', + averageCycleTimeMs: '-nan', + lastRunTimeMs: '0', + gcNumericTreesMissed: '0', + gcBlocksDenied: '0' + }, + cursorStats: { + globalIdle: 0, + globalTotal: 0, + indexCapacity: 128, + idnexTotal: 0 + } + } + ); + }, GLOBAL.SERVERS.OPEN); +}); diff --git a/packages/search/lib/commands/INFO.ts b/packages/search/lib/commands/INFO.ts new file mode 100644 index 00000000000..42451114c89 --- /dev/null +++ b/packages/search/lib/commands/INFO.ts @@ -0,0 +1,171 @@ +export function transformArguments(index: string): Array { + return ['FT.INFO', index]; +} + +type InfoRawReply = [ + _: string, + indexName: string, + _: string, + indexOptions: Array, + _: string, + indexDefinition: [ + _: string, + keyType: string, + _: string, + prefixes: Array, + _: string, + defaultScore: string + ], + _: string, + attributes: Array>, + _: string, + numDocs: string, + _: string, + maxDocId: string, + _: string, + numTerms: string, + _: string, + numRecords: string, + _: string, + invertedSzMb: string, + _: string, + totalInvertedIndexBlocks: string, + _: string, + offsetVectorsSzMb: string, + _: string, + docTableSizeMb: string, + _: string, + sortableValuesSizeMb: string, + _: string, + keyTableSizeMb: string, + _: string, + recordsPerDocAvg: string, + _: string, + bytesPerRecordAvg: string, + _: string, + offsetsPerTermAvg: string, + _: string, + offsetBitsPerRecordAvg: string, + _: string, + hashIndexingFailures: string, + _: string, + indexing: string, + _: string, + percentIndexed: string, + _: string, + gcStats: [ + _: string, + bytesCollected: string, + _: string, + totalMsRun: string, + _: string, + totalCycles: string, + _: string, + averageCycleTimeMs: string, + _: string, + lastRunTimeMs: string, + _: string, + gcNumericTreesMissed: string, + _: string, + gcBlocksDenied: string + ], + _: string, + cursorStats: [ + _: string, + globalIdle: number, + _: string, + globalTotal: number, + _: string, + indexCapacity: number, + _: string, + idnexTotal: number + ] +]; + +interface InfoReply { + indexName: string; + indexOptions: Array; + indexDefinition: { + keyType: string; + prefixes: Array; + defaultScore: string; + }; + attributes: Array>; + numDocs: string; + maxDocId: string; + numTerms: string; + numRecords: string; + invertedSzMb: string; + totalInvertedIndexBlocks: string; + offsetVectorsSzMb: string; + docTableSizeMb: string; + sortableValuesSizeMb: string; + keyTableSizeMb: string; + recordsPerDocAvg: string; + bytesPerRecordAvg: string; + offsetsPerTermAvg: string; + offsetBitsPerRecordAvg: string; + hashIndexingFailures: string; + indexing: string; + percentIndexed: string; + gcStats: { + bytesCollected: string; + totalMsRun: string; + totalCycles: string; + averageCycleTimeMs: string; + lastRunTimeMs: string; + gcNumericTreesMissed: string; + gcBlocksDenied: string; + }; + cursorStats: { + globalIdle: number; + globalTotal: number; + indexCapacity: number; + idnexTotal: number; + }; +} + +export function transformReply(rawReply: InfoRawReply): InfoReply { + return { + indexName: rawReply[1], + indexOptions: rawReply[3], + indexDefinition: { + keyType: rawReply[5][1], + prefixes: rawReply[5][3], + defaultScore: rawReply[5][5] + }, + attributes: rawReply[7], + numDocs: rawReply[9], + maxDocId: rawReply[11], + numTerms: rawReply[13], + numRecords: rawReply[15], + invertedSzMb: rawReply[17], + totalInvertedIndexBlocks: rawReply[19], + offsetVectorsSzMb: rawReply[21], + docTableSizeMb: rawReply[23], + sortableValuesSizeMb: rawReply[25], + keyTableSizeMb: rawReply[27], + recordsPerDocAvg: rawReply[29], + bytesPerRecordAvg: rawReply[31], + offsetsPerTermAvg: rawReply[33], + offsetBitsPerRecordAvg: rawReply[35], + hashIndexingFailures: rawReply[37], + indexing: rawReply[39], + percentIndexed: rawReply[41], + gcStats: { + bytesCollected: rawReply[43][1], + totalMsRun: rawReply[43][3], + totalCycles: rawReply[43][5], + averageCycleTimeMs: rawReply[43][7], + lastRunTimeMs: rawReply[43][9], + gcNumericTreesMissed: rawReply[43][11], + gcBlocksDenied: rawReply[43][13] + }, + cursorStats: { + globalIdle: rawReply[45][1], + globalTotal: rawReply[45][3], + indexCapacity: rawReply[45][5], + idnexTotal: rawReply[45][7] + } + }; +} diff --git a/packages/search/lib/commands/PROFILE.ts b/packages/search/lib/commands/PROFILE.ts new file mode 100644 index 00000000000..e315ea52304 --- /dev/null +++ b/packages/search/lib/commands/PROFILE.ts @@ -0,0 +1,26 @@ +export const IS_READ_ONLY = true; + +interface ProfileOptions { + LIMITED?: true; +} + +export function transformArguments( + index: string, + type: 'SEARCH' | 'AGGREGATE', + query: string, + options?: ProfileOptions +): Array { + const args = ['FT.PROFILE', index, type]; + + if (options?.LIMITED) { + args.push('LIMITED'); + } + + args.push('QUERY', query); + + return args; +} + +export function transformReply() { + +} diff --git a/packages/search/lib/commands/SEARCH.spec.ts b/packages/search/lib/commands/SEARCH.spec.ts new file mode 100644 index 00000000000..efe9c899ac2 --- /dev/null +++ b/packages/search/lib/commands/SEARCH.spec.ts @@ -0,0 +1,243 @@ +import { strict as assert } from 'assert'; +import testUtils, { GLOBAL } from '../test-utils'; +import { RedisSearchLanguages, SchemaFieldTypes } from './CREATE'; +import { transformArguments } from './SEARCH'; + +describe('SEARCH', () => { + describe('transformArguments', () => { + it('without options', () => { + assert.deepEqual( + transformArguments('index', 'query'), + ['FT.SEARCH', 'index', 'query'] + ); + }); + + it('with VERBATIM', () => { + assert.deepEqual( + transformArguments('index', 'query', { VERBATIM: true }), + ['FT.SEARCH', 'index', 'query', 'VERBATIM'] + ); + }); + + it('with NOSTOPWORDS', () => { + assert.deepEqual( + transformArguments('index', 'query', { NOSTOPWORDS: true }), + ['FT.SEARCH', 'index', 'query', 'NOSTOPWORDS'] + ); + }); + + it('with INKEYS', () => { + assert.deepEqual( + transformArguments('index', 'query', { INKEYS: 'key' }), + ['FT.SEARCH', 'index', 'query', 'INKEYS', '1', 'key'] + ); + }); + + it('with INFIELDS', () => { + assert.deepEqual( + transformArguments('index', 'query', { INFIELDS: 'field' }), + ['FT.SEARCH', 'index', 'query', 'INFIELDS', '1', 'field'] + ); + }); + + it('with RETURN', () => { + assert.deepEqual( + transformArguments('index', 'query', { RETURN: 'return' }), + ['FT.SEARCH', 'index', 'query', 'RETURN', '1', 'return'] + ); + }); + + describe('with SUMMARIZE', () => { + it('true', () => { + assert.deepEqual( + transformArguments('index', 'query', { SUMMARIZE: true }), + ['FT.SEARCH', 'index', 'query', 'SUMMARIZE'] + ); + }); + + describe('with FIELDS', () => { + it('string', () => { + assert.deepEqual( + transformArguments('index', 'query', { + SUMMARIZE: { + FIELDS: ['@field'] + } + }), + ['FT.SEARCH', 'index', 'query', 'SUMMARIZE', 'FIELDS', '1', '@field'] + ); + }); + + it('Array', () => { + assert.deepEqual( + transformArguments('index', 'query', { + SUMMARIZE: { + FIELDS: ['@1', '@2'] + } + }), + ['FT.SEARCH', 'index', 'query', 'SUMMARIZE', 'FIELDS', '2', '@1', '@2'] + ); + }); + }); + + it('with FRAGS', () => { + assert.deepEqual( + transformArguments('index', 'query', { + SUMMARIZE: { + FRAGS: 1 + } + }), + ['FT.SEARCH', 'index', 'query', 'SUMMARIZE', 'FRAGS', '1'] + ); + }); + + it('with LEN', () => { + assert.deepEqual( + transformArguments('index', 'query', { + SUMMARIZE: { + LEN: 1 + } + }), + ['FT.SEARCH', 'index', 'query', 'SUMMARIZE', 'LEN', '1'] + ); + }); + + it('with SEPARATOR', () => { + assert.deepEqual( + transformArguments('index', 'query', { + SUMMARIZE: { + SEPARATOR: 'separator' + } + }), + ['FT.SEARCH', 'index', 'query', 'SUMMARIZE', 'SEPARATOR', 'separator'] + ); + }); + }); + + describe('with HIGHLIGHT', () => { + it('true', () => { + assert.deepEqual( + transformArguments('index', 'query', { HIGHLIGHT: true }), + ['FT.SEARCH', 'index', 'query', 'HIGHLIGHT'] + ); + }); + + describe('with FIELDS', () => { + it('string', () => { + assert.deepEqual( + transformArguments('index', 'query', { + HIGHLIGHT: { + FIELDS: ['@field'] + } + }), + ['FT.SEARCH', 'index', 'query', 'HIGHLIGHT', 'FIELDS', '1', '@field'] + ); + }); + + it('Array', () => { + assert.deepEqual( + transformArguments('index', 'query', { + HIGHLIGHT: { + FIELDS: ['@1', '@2'] + } + }), + ['FT.SEARCH', 'index', 'query', 'HIGHLIGHT', 'FIELDS', '2', '@1', '@2'] + ); + }); + }); + + it('with TAGS', () => { + assert.deepEqual( + transformArguments('index', 'query', { + HIGHLIGHT: { + TAGS: { + open: 'open', + close: 'close' + } + } + }), + ['FT.SEARCH', 'index', 'query', 'HIGHLIGHT', 'TAGS', 'open', 'close'] + ); + }); + }); + + it('with SLOP', () => { + assert.deepEqual( + transformArguments('index', 'query', { SLOP: 1 }), + ['FT.SEARCH', 'index', 'query', 'SLOP', '1'] + ); + }); + + it('with INORDER', () => { + assert.deepEqual( + transformArguments('index', 'query', { INORDER: true }), + ['FT.SEARCH', 'index', 'query', 'INORDER'] + ); + }); + + it('with LANGUAGE', () => { + assert.deepEqual( + transformArguments('index', 'query', { LANGUAGE: RedisSearchLanguages.ARABIC }), + ['FT.SEARCH', 'index', 'query', 'LANGUAGE', RedisSearchLanguages.ARABIC] + ); + }); + + it('with EXPANDER', () => { + assert.deepEqual( + transformArguments('index', 'query', { EXPANDER: 'expender' }), + ['FT.SEARCH', 'index', 'query', 'EXPANDER', 'expender'] + ); + }); + + it('with SCORER', () => { + assert.deepEqual( + transformArguments('index', 'query', { SCORER: 'scorer' }), + ['FT.SEARCH', 'index', 'query', 'SCORER', 'scorer'] + ); + }); + + it('with MSORTBY', () => { + assert.deepEqual( + transformArguments('index', 'query', { MSORTBY: '@by' }), + ['FT.SEARCH', 'index', 'query', 'MSORTBY', '1', '@by'] + ); + }); + + it('with LIMIT', () => { + assert.deepEqual( + transformArguments('index', 'query', { + LIMIT: { + from: 0, + size: 1 + } + }), + ['FT.SEARCH', 'index', 'query', 'LIMIT', '0', '1'] + ); + }); + }); + + testUtils.testWithClient('client.ft.search', async client => { + await Promise.all([ + client.ft.create('index', { + field: SchemaFieldTypes.NUMERIC + }), + client.hSet('1', 'field', '1') + ]); + + assert.deepEqual( + await client.ft.search('index', '*'), + { + total: 1, + documents: [{ + id: '1', + value: Object.create(null, { + field: { + value: '1', + configurable: true, + enumerable: true + } + }) + }] + } + ); + }, GLOBAL.SERVERS.OPEN); +}); diff --git a/packages/search/lib/commands/SEARCH.ts b/packages/search/lib/commands/SEARCH.ts new file mode 100644 index 00000000000..6b14a0a18fd --- /dev/null +++ b/packages/search/lib/commands/SEARCH.ts @@ -0,0 +1,202 @@ +import { RedisCommandArguments } from '@redis/client/dist/lib/commands'; +import { pushOptionalVerdictArgument, pushVerdictArgument, transformReplyTuples } from '@redis/client/dist/lib/commands/generic-transformers'; +import { type } from 'os'; +import { PropertyName, pushSortByArguments, SortByOptions } from '.'; +import { RedisSearchLanguages } from './CREATE'; + +export const FIRST_KEY_INDEX = 1; + +export const IS_READ_ONLY = true; + +interface SearchOptions { + // NOCONTENT?: true; TODO + VERBATIM?: true; + NOSTOPWORDS?: true; + // WITHSCORES?: true; + // WITHPAYLOADS?: true; + WITHSORTKEYS?: true; + // FILTER?: { + // field: string; + // min: number | string; + // max: number | string; + // }; + // GEOFILTER?: { + // field: string; + // lon: number; + // lat: number; + // radius: number; + // unit: 'm' | 'km' | 'mi' | 'ft'; + // }; + INKEYS?: string | Array; + INFIELDS?: string | Array; + RETURN?: string | Array; + SUMMARIZE?: true | { + FIELDS?: PropertyName | Array; + FRAGS?: number; + LEN?: number; + SEPARATOR?: string; + }; + HIGHLIGHT?: true | { + FIELDS?: PropertyName | Array; + TAGS?: { + open: string; + close: string; + } + }; + SLOP?: number; + INORDER?: true; + LANGUAGE?: RedisSearchLanguages; + EXPANDER?: string; + SCORER?: string; + // EXPLAINSCORE?: true; // TODO: WITHSCORES + // PAYLOAD?: ; + // SORTBY?: SortByOptions; + MSORTBY?: SortByOptions | Array; + LIMIT?: { + from: number | string; + size: number | string; + }; +} + +export function transformArguments( + index: string, + query: string, + options?: SearchOptions +): RedisCommandArguments { + const args: RedisCommandArguments = ['FT.SEARCH', index, query]; + + if (options?.VERBATIM) { + args.push('VERBATIM'); + } + + if (options?.NOSTOPWORDS) { + args.push('NOSTOPWORDS'); + } + + // if (options?.WITHSCORES) { + // args.push('WITHSCORES'); + // } + + // if (options?.WITHPAYLOADS) { + // args.push('WITHPAYLOADS'); + // } + + pushOptionalVerdictArgument(args, 'INKEYS', options?.INKEYS); + pushOptionalVerdictArgument(args, 'INFIELDS', options?.INFIELDS); + pushOptionalVerdictArgument(args, 'RETURN', options?.RETURN); + + if (options?.SUMMARIZE) { + args.push('SUMMARIZE'); + + if (typeof options.SUMMARIZE === 'object') { + if (options.SUMMARIZE.FIELDS) { + args.push('FIELDS'); + pushVerdictArgument(args, options.SUMMARIZE.FIELDS); + } + + if (options.SUMMARIZE.FRAGS) { + args.push('FRAGS', options.SUMMARIZE.FRAGS.toString()); + } + + if (options.SUMMARIZE.LEN) { + args.push('LEN', options.SUMMARIZE.LEN.toString()); + } + + if (options.SUMMARIZE.SEPARATOR) { + args.push('SEPARATOR', options.SUMMARIZE.SEPARATOR); + } + } + } + + if (options?.HIGHLIGHT) { + args.push('HIGHLIGHT'); + + if (typeof options.HIGHLIGHT === 'object') { + if (options.HIGHLIGHT.FIELDS) { + args.push('FIELDS'); + pushVerdictArgument(args, options.HIGHLIGHT.FIELDS); + } + + if (options.HIGHLIGHT.TAGS) { + args.push('TAGS', options.HIGHLIGHT.TAGS.open, options.HIGHLIGHT.TAGS.close); + } + } + } + + if (options?.SLOP) { + args.push('SLOP', options.SLOP.toString()); + } + + if (options?.INORDER) { + args.push('INORDER'); + } + + if (options?.LANGUAGE) { + args.push('LANGUAGE', options.LANGUAGE); + } + + if (options?.EXPANDER) { + args.push('EXPANDER', options.EXPANDER); + } + + if (options?.SCORER) { + args.push('SCORER', options.SCORER); + } + + // if (options?.EXPLAINSCORE) { + // args.push('EXPLAINSCORE'); + // } + + // if (options?.PAYLOAD) { + // args.push('PAYLOAD', options.PAYLOAD); + // } + + // if (options?.SORTBY) { + // args.push('SORTBY'); + // pushSortByArguments(args, options.SORTBY); + // } + + if (options?.MSORTBY) { + pushSortByArguments(args, 'MSORTBY', options.MSORTBY); + } + + if (options?.LIMIT) { + args.push( + 'LIMIT', + options.LIMIT.from.toString(), + options.LIMIT.size.toString() + ); + } + + return args; +} + +interface SearchDocumentValue { + [key: string]: string | number | null | Array | SearchDocumentValue; +} + +interface SearchReply { + total: number; + documents: Array<{ + id: string; + value: SearchDocumentValue; + }>; +} + +export function transformReply(reply: Array): SearchReply { + const documents = []; + for (let i = 1; i < reply.length; i += 2) { + const tuples = reply[i + 1]; + documents.push({ + id: reply[i], + value: tuples.length === 2 && tuples[0] === '$' ? + JSON.parse(tuples[1]) : + transformReplyTuples(tuples) + }); + } + + return { + total: reply[0], + documents + }; +} diff --git a/packages/search/lib/commands/SPELLCHECK.spec.ts b/packages/search/lib/commands/SPELLCHECK.spec.ts new file mode 100644 index 00000000000..bacbe118b38 --- /dev/null +++ b/packages/search/lib/commands/SPELLCHECK.spec.ts @@ -0,0 +1,71 @@ +import { strict as assert } from 'assert'; +import testUtils, { GLOBAL } from '../test-utils'; +import { SchemaFieldTypes } from './CREATE'; +import { transformArguments } from './SPELLCHECK'; + +describe('SPELLCHECK', () => { + describe('transformArguments', () => { + it('without options', () => { + assert.deepEqual( + transformArguments('index', 'query'), + ['FT.SPELLCHECK', 'index', 'query'] + ); + }); + + it('with DISTANCE', () => { + assert.deepEqual( + transformArguments('index', 'query', { DISTANCE: 2 }), + ['FT.SPELLCHECK', 'index', 'query', 'DISTANCE', '2'] + ); + }); + + describe('with TERMS', () => { + it('single', () => { + assert.deepEqual( + transformArguments('index', 'query', { + TERMS: { + mode: 'INCLUDE', + dictionary: 'dictionary' + } + }), + ['FT.SPELLCHECK', 'index', 'query', 'TERMS', 'INCLUDE', 'dictionary'] + ); + }); + + it('multiple', () => { + assert.deepEqual( + transformArguments('index', 'query', { + TERMS: [{ + mode: 'INCLUDE', + dictionary: 'include' + }, { + mode: 'EXCLUDE', + dictionary: 'exclude' + }] + }), + ['FT.SPELLCHECK', 'index', 'query', 'TERMS', 'INCLUDE', 'include', 'TERMS', 'EXCLUDE', 'exclude'] + ); + }); + }); + }); + + testUtils.testWithClient('client.ft.spellCheck', async client => { + await Promise.all([ + client.ft.create('index', { + field: SchemaFieldTypes.TEXT + }), + client.hSet('key', 'field', 'query') + ]); + + assert.deepEqual( + await client.ft.spellCheck('index', 'quer'), + [{ + term: 'quer', + suggestions: [{ + score: 1, + suggestion: 'query' + }] + }] + ); + }, GLOBAL.SERVERS.OPEN); +}); diff --git a/packages/search/lib/commands/SPELLCHECK.ts b/packages/search/lib/commands/SPELLCHECK.ts new file mode 100644 index 00000000000..ae4cb3cdce1 --- /dev/null +++ b/packages/search/lib/commands/SPELLCHECK.ts @@ -0,0 +1,57 @@ +interface SpellCheckTerms { + mode: 'INCLUDE' | 'EXCLUDE'; + dictionary: string; +} + +interface SpellCheckOptions { + DISTANCE?: number; + TERMS?: SpellCheckTerms | Array; +} + +export function transformArguments(index: string, query: string, options?: SpellCheckOptions): Array { + const args = ['FT.SPELLCHECK', index, query]; + + if (options?.DISTANCE) { + args.push('DISTANCE', options.DISTANCE.toString()); + } + + if (options?.TERMS) { + if (Array.isArray(options.TERMS)) { + for (const term of options.TERMS) { + pushTerms(args, term); + } + } else { + pushTerms(args, options.TERMS); + } + } + + return args; +} + +function pushTerms(args: Array, { mode, dictionary }: SpellCheckTerms): void { + args.push('TERMS', mode, dictionary); +} + +type SpellCheckRawReply = Array<[ + _: string, + term: string, + suggestions: Array<[score: string, suggestion: string]> +]>; + +type SpellCheckReply = Array<{ + term: string, + suggestions: Array<{ + score: number, + suggestion: string + }> +}>; + +export function transformReply(rawReply: SpellCheckRawReply): SpellCheckReply { + return rawReply.map(([, term, suggestions]) => ({ + term, + suggestions: suggestions.map(([score, suggestion]) => ({ + score: Number(score), + suggestion + })) + })); +} diff --git a/packages/search/lib/commands/SUGADD.spec.ts b/packages/search/lib/commands/SUGADD.spec.ts new file mode 100644 index 00000000000..23294eb4abd --- /dev/null +++ b/packages/search/lib/commands/SUGADD.spec.ts @@ -0,0 +1,35 @@ +import { strict as assert } from 'assert'; +import testUtils, { GLOBAL } from '../test-utils'; +import { transformArguments } from './SUGADD'; + +describe('SUGADD', () => { + describe('transformArguments', () => { + it('without options', () => { + assert.deepEqual( + transformArguments('key', 'string', 1), + ['FT.SUGADD', 'key', 'string', '1'] + ); + }); + + it('with INCR', () => { + assert.deepEqual( + transformArguments('key', 'string', 1, { INCR: true }), + ['FT.SUGADD', 'key', 'string', '1', 'INCR'] + ); + }); + + it('with PAYLOAD', () => { + assert.deepEqual( + transformArguments('key', 'string', 1, { PAYLOAD: 'payload' }), + ['FT.SUGADD', 'key', 'string', '1', 'PAYLOAD', 'payload'] + ); + }); + }); + + testUtils.testWithClient('client.ft.sugAdd', async client => { + assert.equal( + await client.ft.sugAdd('key', 'string', 1), + 1 + ); + }, GLOBAL.SERVERS.OPEN); +}); diff --git a/packages/search/lib/commands/SUGADD.ts b/packages/search/lib/commands/SUGADD.ts new file mode 100644 index 00000000000..d68f0d98841 --- /dev/null +++ b/packages/search/lib/commands/SUGADD.ts @@ -0,0 +1,20 @@ +interface SugAddOptions { + INCR?: true; + PAYLOAD?: string; +} + +export function transformArguments(key: string, string: string, score: number, options?: SugAddOptions): Array { + const args = ['FT.SUGADD', key, string, score.toString()]; + + if (options?.INCR) { + args.push('INCR'); + } + + if (options?.PAYLOAD) { + args.push('PAYLOAD', options.PAYLOAD); + } + + return args; +} + +export declare function transformReply(): number; diff --git a/packages/search/lib/commands/SUGDEL.spec.ts b/packages/search/lib/commands/SUGDEL.spec.ts new file mode 100644 index 00000000000..3d89e3b9a72 --- /dev/null +++ b/packages/search/lib/commands/SUGDEL.spec.ts @@ -0,0 +1,19 @@ +import { strict as assert } from 'assert'; +import testUtils, { GLOBAL } from '../test-utils'; +import { transformArguments } from './SUGDEL'; + +describe('SUGDEL', () => { + it('transformArguments', () => { + assert.deepEqual( + transformArguments('key', 'string'), + ['FT.SUGDEL', 'key', 'string'] + ); + }); + + testUtils.testWithClient('client.ft.sugDel', async client => { + assert.equal( + await client.ft.sugDel('key', 'string'), + false + ); + }, GLOBAL.SERVERS.OPEN); +}); diff --git a/packages/search/lib/commands/SUGDEL.ts b/packages/search/lib/commands/SUGDEL.ts new file mode 100644 index 00000000000..be2d4262caa --- /dev/null +++ b/packages/search/lib/commands/SUGDEL.ts @@ -0,0 +1,5 @@ +export function transformArguments(key: string, string: string): Array { + return ['FT.SUGDEL', key, string]; +} + +export { transformReplyBoolean as transformReply } from '@redis/client/dist/lib/commands/generic-transformers'; diff --git a/packages/search/lib/commands/SUGGET.spec.ts b/packages/search/lib/commands/SUGGET.spec.ts new file mode 100644 index 00000000000..c24c2ff0863 --- /dev/null +++ b/packages/search/lib/commands/SUGGET.spec.ts @@ -0,0 +1,46 @@ +import { strict as assert } from 'assert'; +import testUtils, { GLOBAL } from '../test-utils'; +import { transformArguments } from './SUGGET'; + +describe('SUGGET', () => { + describe('transformArguments', () => { + it('without options', () => { + assert.deepEqual( + transformArguments('key', 'prefix'), + ['FT.SUGGET', 'key', 'prefix'] + ); + }); + + it('with FUZZY', () => { + assert.deepEqual( + transformArguments('key', 'prefix', { FUZZY: true }), + ['FT.SUGGET', 'key', 'prefix', 'FUZZY'] + ); + }); + + it('with MAX', () => { + assert.deepEqual( + transformArguments('key', 'prefix', { MAX: 10 }), + ['FT.SUGGET', 'key', 'prefix', 'MAX', '10'] + ); + }); + }); + + describe('client.ft.sugGet', () => { + testUtils.testWithClient('null', async client => { + assert.equal( + await client.ft.sugGet('key', 'prefix'), + null + ); + }, GLOBAL.SERVERS.OPEN); + + testUtils.testWithClient('with suggestions', async client => { + await client.ft.sugAdd('key', 'string', 1); + + assert.deepEqual( + await client.ft.sugGet('key', 'string'), + ['string'] + ); + }, GLOBAL.SERVERS.OPEN); + }); +}); diff --git a/packages/search/lib/commands/SUGGET.ts b/packages/search/lib/commands/SUGGET.ts new file mode 100644 index 00000000000..558cedeaa08 --- /dev/null +++ b/packages/search/lib/commands/SUGGET.ts @@ -0,0 +1,22 @@ +export const IS_READ_ONLY = true; + +export interface SugGetOptions { + FUZZY?: true; + MAX?: number; +} + +export function transformArguments(key: string, prefix: string, options?: SugGetOptions): Array { + const args = ['FT.SUGGET', key, prefix]; + + if (options?.FUZZY) { + args.push('FUZZY'); + } + + if (options?.MAX) { + args.push('MAX', options.MAX.toString()); + } + + return args; +} + +export declare function transformReply(): null | Array; diff --git a/packages/search/lib/commands/SUGGET_WITHPAYLOADS.spec.ts b/packages/search/lib/commands/SUGGET_WITHPAYLOADS.spec.ts new file mode 100644 index 00000000000..a4a87ebe895 --- /dev/null +++ b/packages/search/lib/commands/SUGGET_WITHPAYLOADS.spec.ts @@ -0,0 +1,33 @@ +import { strict as assert } from 'assert'; +import testUtils, { GLOBAL } from '../test-utils'; +import { transformArguments } from './SUGGET_WITHPAYLOADS'; + +describe('SUGGET WITHPAYLOADS', () => { + it('transformArguments', () => { + assert.deepEqual( + transformArguments('key', 'prefix'), + ['FT.SUGGET', 'key', 'prefix', 'WITHPAYLOADS'] + ); + }); + + describe('client.ft.sugGetWithPayloads', () => { + testUtils.testWithClient('null', async client => { + assert.equal( + await client.ft.sugGetWithPayloads('key', 'prefix'), + null + ); + }, GLOBAL.SERVERS.OPEN); + + testUtils.testWithClient('with suggestions', async client => { + await client.ft.sugAdd('key', 'string', 1, { PAYLOAD: 'payload' }); + + assert.deepEqual( + await client.ft.sugGetWithPayloads('key', 'string'), + [{ + suggestion: 'string', + payload: 'payload' + }] + ); + }, GLOBAL.SERVERS.OPEN); + }); +}); diff --git a/packages/search/lib/commands/SUGGET_WITHPAYLOADS.ts b/packages/search/lib/commands/SUGGET_WITHPAYLOADS.ts new file mode 100644 index 00000000000..7eaff4697e1 --- /dev/null +++ b/packages/search/lib/commands/SUGGET_WITHPAYLOADS.ts @@ -0,0 +1,29 @@ +import { SugGetOptions, transformArguments as transformSugGetArguments } from './SUGGET'; + +export { IS_READ_ONLY } from './SUGGET'; + +export function transformArguments(key: string, prefix: string, options?: SugGetOptions): Array { + return [ + ...transformSugGetArguments(key, prefix, options), + 'WITHPAYLOADS' + ]; +} + +export interface SuggestionWithPayload { + suggestion: string; + payload: string | null; +} + +export function transformReply(rawReply: Array | null): Array | null { + if (rawReply === null) return null; + + const transformedReply = []; + for (let i = 0; i < rawReply.length; i += 2) { + transformedReply.push({ + suggestion: rawReply[i]!, + payload: rawReply[i + 1] + }); + } + + return transformedReply; +} diff --git a/packages/search/lib/commands/SUGGET_WITHSCORES.spec.ts b/packages/search/lib/commands/SUGGET_WITHSCORES.spec.ts new file mode 100644 index 00000000000..e60daa917a9 --- /dev/null +++ b/packages/search/lib/commands/SUGGET_WITHSCORES.spec.ts @@ -0,0 +1,33 @@ +import { strict as assert } from 'assert'; +import testUtils, { GLOBAL } from '../test-utils'; +import { transformArguments } from './SUGGET_WITHSCORES'; + +describe('SUGGET WITHSCORES', () => { + it('transformArguments', () => { + assert.deepEqual( + transformArguments('key', 'prefix'), + ['FT.SUGGET', 'key', 'prefix', 'WITHSCORES'] + ); + }); + + describe('client.ft.sugGetWithScores', () => { + testUtils.testWithClient('null', async client => { + assert.equal( + await client.ft.sugGetWithScores('key', 'prefix'), + null + ); + }, GLOBAL.SERVERS.OPEN); + + testUtils.testWithClient('with suggestions', async client => { + await client.ft.sugAdd('key', 'string', 1); + + assert.deepEqual( + await client.ft.sugGetWithScores('key', 'string'), + [{ + suggestion: 'string', + score: 2147483648 + }] + ); + }, GLOBAL.SERVERS.OPEN); + }); +}); diff --git a/packages/search/lib/commands/SUGGET_WITHSCORES.ts b/packages/search/lib/commands/SUGGET_WITHSCORES.ts new file mode 100644 index 00000000000..bad5bff2999 --- /dev/null +++ b/packages/search/lib/commands/SUGGET_WITHSCORES.ts @@ -0,0 +1,29 @@ +import { SugGetOptions, transformArguments as transformSugGetArguments } from './SUGGET'; + +export { IS_READ_ONLY } from './SUGGET'; + +export function transformArguments(key: string, prefix: string, options?: SugGetOptions): Array { + return [ + ...transformSugGetArguments(key, prefix, options), + 'WITHSCORES' + ]; +} + +export interface SuggestionWithScores { + suggestion: string; + score: number; +} + +export function transformReply(rawReply: Array | null): Array | null { + if (rawReply === null) return null; + + const transformedReply = []; + for (let i = 0; i < rawReply.length; i += 2) { + transformedReply.push({ + suggestion: rawReply[i], + score: Number(rawReply[i + 1]) + }); + } + + return transformedReply; +} diff --git a/packages/search/lib/commands/SUGGET_WITHSCORES_WITHPAYLOADS.spec.ts b/packages/search/lib/commands/SUGGET_WITHSCORES_WITHPAYLOADS.spec.ts new file mode 100644 index 00000000000..0900d91b8d9 --- /dev/null +++ b/packages/search/lib/commands/SUGGET_WITHSCORES_WITHPAYLOADS.spec.ts @@ -0,0 +1,34 @@ +import { strict as assert } from 'assert'; +import testUtils, { GLOBAL } from '../test-utils'; +import { transformArguments } from './SUGGET_WITHSCORES_WITHPAYLOADS'; + +describe('SUGGET WITHSCORES WITHPAYLOADS', () => { + it('transformArguments', () => { + assert.deepEqual( + transformArguments('key', 'prefix'), + ['FT.SUGGET', 'key', 'prefix', 'WITHSCORES', 'WITHPAYLOADS'] + ); + }); + + describe('client.ft.sugGetWithScoresWithPayloads', () => { + testUtils.testWithClient('null', async client => { + assert.equal( + await client.ft.sugGetWithScoresWithPayloads('key', 'prefix'), + null + ); + }, GLOBAL.SERVERS.OPEN); + + testUtils.testWithClient('with suggestions', async client => { + await client.ft.sugAdd('key', 'string', 1, { PAYLOAD: 'payload' }); + + assert.deepEqual( + await client.ft.sugGetWithScoresWithPayloads('key', 'string'), + [{ + suggestion: 'string', + score: 2147483648, + payload: 'payload' + }] + ); + }, GLOBAL.SERVERS.OPEN); + }); +}); diff --git a/packages/search/lib/commands/SUGGET_WITHSCORES_WITHPAYLOADS.ts b/packages/search/lib/commands/SUGGET_WITHSCORES_WITHPAYLOADS.ts new file mode 100644 index 00000000000..3b2fe7667b7 --- /dev/null +++ b/packages/search/lib/commands/SUGGET_WITHSCORES_WITHPAYLOADS.ts @@ -0,0 +1,30 @@ +import { SugGetOptions, transformArguments as transformSugGetArguments } from './SUGGET'; +import { SuggestionWithPayload } from './SUGGET_WITHPAYLOADS'; +import { SuggestionWithScores } from './SUGGET_WITHSCORES'; + +export { IS_READ_ONLY } from './SUGGET'; + +export function transformArguments(key: string, prefix: string, options?: SugGetOptions): Array { + return [ + ...transformSugGetArguments(key, prefix, options), + 'WITHSCORES', + 'WITHPAYLOADS' + ]; +} + +type SuggestionWithScoresAndPayloads = SuggestionWithScores & SuggestionWithPayload; + +export function transformReply(rawReply: Array | null): Array | null { + if (rawReply === null) return null; + + const transformedReply = []; + for (let i = 0; i < rawReply.length; i += 3) { + transformedReply.push({ + suggestion: rawReply[i]!, + score: Number(rawReply[i + 1]!), + payload: rawReply[i + 2] + }); + } + + return transformedReply; +} diff --git a/packages/search/lib/commands/SUGLEN.spec.ts b/packages/search/lib/commands/SUGLEN.spec.ts new file mode 100644 index 00000000000..2ea680df953 --- /dev/null +++ b/packages/search/lib/commands/SUGLEN.spec.ts @@ -0,0 +1,19 @@ +import { strict as assert } from 'assert'; +import testUtils, { GLOBAL } from '../test-utils'; +import { transformArguments } from './SUGLEN'; + +describe('SUGLEN', () => { + it('transformArguments', () => { + assert.deepEqual( + transformArguments('key'), + ['FT.SUGLEN', 'key'] + ); + }); + + testUtils.testWithClient('client.ft.sugLen', async client => { + assert.equal( + await client.ft.sugLen('key'), + 0 + ); + }, GLOBAL.SERVERS.OPEN); +}); diff --git a/packages/search/lib/commands/SUGLEN.ts b/packages/search/lib/commands/SUGLEN.ts new file mode 100644 index 00000000000..15b3da61261 --- /dev/null +++ b/packages/search/lib/commands/SUGLEN.ts @@ -0,0 +1,7 @@ +export const IS_READ_ONLY = true; + +export function transformArguments(key: string): Array { + return ['FT.SUGLEN', key]; +} + +export declare function transformReply(): number; diff --git a/packages/search/lib/commands/SYNDUMP.spec.ts b/packages/search/lib/commands/SYNDUMP.spec.ts new file mode 100644 index 00000000000..4b0cb0c8b3a --- /dev/null +++ b/packages/search/lib/commands/SYNDUMP.spec.ts @@ -0,0 +1,23 @@ +import { strict as assert } from 'assert'; +import testUtils, { GLOBAL } from '../test-utils'; +import { transformArguments } from './SYNDUMP'; + +describe('SYNDUMP', () => { + it('transformArguments', () => { + assert.deepEqual( + transformArguments('index'), + ['FT.SYNDUMP', 'index'] + ); + }); + + testUtils.testWithClient('client.ft.synDump', async client => { + await client.ft.create('index', {}, { + ON: 'HASH' // TODO: shouldn't be mandatory + }); + + assert.deepEqual( + await client.ft.synDump('index'), + [] + ); + }, GLOBAL.SERVERS.OPEN); +}); diff --git a/packages/search/lib/commands/SYNDUMP.ts b/packages/search/lib/commands/SYNDUMP.ts new file mode 100644 index 00000000000..5f1e71aaf78 --- /dev/null +++ b/packages/search/lib/commands/SYNDUMP.ts @@ -0,0 +1,5 @@ +export function transformArguments(index: string): Array { + return ['FT.SYNDUMP', index]; +} + +export declare function transformReply(): Array; diff --git a/packages/search/lib/commands/SYNUPDATE.spec.ts b/packages/search/lib/commands/SYNUPDATE.spec.ts new file mode 100644 index 00000000000..bf7fed84934 --- /dev/null +++ b/packages/search/lib/commands/SYNUPDATE.spec.ts @@ -0,0 +1,39 @@ +import { strict as assert } from 'assert'; +import testUtils, { GLOBAL } from '../test-utils'; +import { transformArguments } from './SYNUPDATE'; + +describe('SYNUPDATE', () => { + describe('transformArguments', () => { + it('single term', () => { + assert.deepEqual( + transformArguments('index', 'groupId', 'term'), + ['FT.SYNUPDATE', 'index', 'groupId', 'term'] + ); + }); + + it('multiple terms', () => { + assert.deepEqual( + transformArguments('index', 'groupId', ['1', '2']), + ['FT.SYNUPDATE', 'index', 'groupId', '1', '2'] + ); + }); + + it('with SKIPINITIALSCAN', () => { + assert.deepEqual( + transformArguments('index', 'groupId', 'term', { SKIPINITIALSCAN: true }), + ['FT.SYNUPDATE', 'index', 'groupId', 'SKIPINITIALSCAN', 'term'] + ); + }); + }); + + testUtils.testWithClient('client.ft.synUpdate', async client => { + await client.ft.create('index', {}, { + ON: 'HASH' // TODO: shouldn't be mandatory + }); + + assert.equal( + await client.ft.synUpdate('index', 'groupId', 'term'), + 'OK' + ); + }, GLOBAL.SERVERS.OPEN); +}); diff --git a/packages/search/lib/commands/SYNUPDATE.ts b/packages/search/lib/commands/SYNUPDATE.ts new file mode 100644 index 00000000000..3384ea59d94 --- /dev/null +++ b/packages/search/lib/commands/SYNUPDATE.ts @@ -0,0 +1,23 @@ +import { pushVerdictArguments } from '@redis/client/dist/lib/commands/generic-transformers'; +import { RedisCommandArguments } from '@redis/client/dist/lib/commands'; + +interface SynUpdateOptions { + SKIPINITIALSCAN?: true; +} + +export function transformArguments( + index: string, + groupId: string, + terms: string | Array, + options?: SynUpdateOptions +): RedisCommandArguments { + const args = ['FT.SYNUPDATE', index, groupId]; + + if (options?.SKIPINITIALSCAN) { + args.push('SKIPINITIALSCAN'); + } + + return pushVerdictArguments(args, terms); +} + +export declare function transformReply(): 'OK'; diff --git a/packages/search/lib/commands/TAGVALS.spec.ts b/packages/search/lib/commands/TAGVALS.spec.ts new file mode 100644 index 00000000000..1f90939bb0d --- /dev/null +++ b/packages/search/lib/commands/TAGVALS.spec.ts @@ -0,0 +1,24 @@ +import { strict as assert } from 'assert'; +import testUtils, { GLOBAL } from '../test-utils'; +import { SchemaFieldTypes } from './CREATE'; +import { transformArguments } from './TAGVALS'; + +describe('TAGVALS', () => { + it('transformArguments', () => { + assert.deepEqual( + transformArguments('index', '@field'), + ['FT.TAGVALS', 'index', '@field'] + ); + }); + + testUtils.testWithClient('client.ft.tagVals', async client => { + await client.ft.create('index', { + field: SchemaFieldTypes.TAG + }); + + assert.deepEqual( + await client.ft.tagVals('index', 'field'), + [] + ); + }, GLOBAL.SERVERS.OPEN); +}); diff --git a/packages/search/lib/commands/TAGVALS.ts b/packages/search/lib/commands/TAGVALS.ts new file mode 100644 index 00000000000..54342f0c9e5 --- /dev/null +++ b/packages/search/lib/commands/TAGVALS.ts @@ -0,0 +1,5 @@ +export function transformArguments(index: string, fieldName: string): Array { + return ['FT.TAGVALS', index, fieldName]; +} + +export declare function transformReply(): Array; diff --git a/packages/search/lib/commands/_LIST.spec.ts b/packages/search/lib/commands/_LIST.spec.ts new file mode 100644 index 00000000000..602c29975f2 --- /dev/null +++ b/packages/search/lib/commands/_LIST.spec.ts @@ -0,0 +1,19 @@ +import { strict as assert } from 'assert'; +import testUtils, { GLOBAL } from '../test-utils'; +import { transformArguments } from './_LIST'; + +describe('_LIST', () => { + it('transformArguments', () => { + assert.deepEqual( + transformArguments(), + ['FT._LIST'] + ); + }); + + testUtils.testWithClient('client.ft._list', async client => { + assert.deepEqual( + await client.ft._list(), + [] + ); + }, GLOBAL.SERVERS.OPEN); +}); diff --git a/packages/search/lib/commands/_LIST.ts b/packages/search/lib/commands/_LIST.ts new file mode 100644 index 00000000000..588ec837c3b --- /dev/null +++ b/packages/search/lib/commands/_LIST.ts @@ -0,0 +1,5 @@ +export function transformArguments(): Array { + return ['FT._LIST']; +} + +export declare function transformReply(): Array; diff --git a/packages/search/lib/commands/index.spec.ts b/packages/search/lib/commands/index.spec.ts new file mode 100644 index 00000000000..f0afa304a92 --- /dev/null +++ b/packages/search/lib/commands/index.spec.ts @@ -0,0 +1,46 @@ +import { strict as assert } from 'assert'; +import { pushArgumentsWithLength, pushSortByArguments } from '.'; + +describe('pushSortByArguments', () => { + describe('single', () => { + it('string', () => { + assert.deepEqual( + pushSortByArguments([], 'SORTBT', '@property'), + ['SORTBT', '1', '@property'] + ); + }); + + it('.BY', () => { + assert.deepEqual( + pushSortByArguments([], 'SORTBT', { BY: '@property' }), + ['SORTBT', '1', '@property'] + ); + }); + + it('with DIRECTION', () => { + assert.deepEqual( + pushSortByArguments([], 'SORTBY', { + BY: '@property', + DIRECTION: 'ASC' + }), + ['SORTBY', '2', '@property', 'ASC'] + ); + }); + }); + + it('multiple', () => { + assert.deepEqual( + pushSortByArguments([], 'SORTBY', ['@1', '@2']), + ['SORTBY', '2', '@1', '@2'] + ); + }); +}); + +it('pushArgumentsWithLength', () => { + assert.deepEqual( + pushArgumentsWithLength(['a'], args => { + args.push('b', 'c'); + }), + ['a', '2', 'b', 'c'] + ); +}); diff --git a/packages/search/lib/commands/index.ts b/packages/search/lib/commands/index.ts new file mode 100644 index 00000000000..f54a71e0e0b --- /dev/null +++ b/packages/search/lib/commands/index.ts @@ -0,0 +1,133 @@ +import * as _LIST from './_LIST'; +import * as AGGREGATE from './AGGREGATE'; +import * as ALIASADD from './ALIASADD'; +import * as ALIASDEL from './ALIASDEL'; +import * as ALIASUPDATE from './ALIASUPDATE'; +import * as CONFIG_GET from './CONFIG_GET'; +import * as CONFIG_SET from './CONFIG_SET'; +import * as CREATE from './CREATE'; +import * as DICTADD from './DICTADD'; +import * as DICTDEL from './DICTDEL'; +import * as DICTDUMP from './DICTDUMP'; +import * as DROPINDEX from './DROPINDEX'; +import * as EXPLAIN from './EXPLAIN'; +import * as EXPLAINCLI from './EXPLAINCLI'; +import * as INFO from './INFO'; +// import * as PROFILE from './PROFILE'; +import * as SEARCH from './SEARCH'; +import * as SPELLCHECK from './SPELLCHECK'; +import * as SUGADD from './SUGADD'; +import * as SUGDEL from './SUGDEL'; +import * as SUGGET_WITHPAYLOADS from './SUGGET_WITHPAYLOADS'; +import * as SUGGET_WITHSCORES_WITHPAYLOADS from './SUGGET_WITHSCORES_WITHPAYLOADS'; +import * as SUGGET_WITHSCORES from './SUGGET_WITHSCORES'; +import * as SUGGET from './SUGGET'; +import * as SUGLEN from './SUGLEN'; +import * as SYNDUMP from './SYNDUMP'; +import * as SYNUPDATE from './SYNUPDATE'; +import * as TAGVALS from './TAGVALS'; +import { RedisCommandArguments } from '@redis/client/dist/lib/commands'; + +export default { + _LIST, + _list: _LIST, + AGGREGATE, + aggregate: AGGREGATE, + ALIASADD, + aliasAdd: ALIASADD, + ALIASDEL, + aliasDel: ALIASDEL, + ALIASUPDATE, + aliasUpdate: ALIASUPDATE, + CONFIG_GET, + configGet: CONFIG_GET, + CONFIG_SET, + configSet: CONFIG_SET, + CREATE, + create: CREATE, + DICTADD, + dictAdd: DICTADD, + DICTDEL, + dictDel: DICTDEL, + DICTDUMP, + dictDump: DICTDUMP, + DROPINDEX, + dropIndex: DROPINDEX, + EXPLAIN, + explain: EXPLAIN, + EXPLAINCLI, + explainCli: EXPLAINCLI, + INFO, + info: INFO, + // PROFILE, + // profile: PROFILE, + SEARCH, + search: SEARCH, + SPELLCHECK, + spellCheck: SPELLCHECK, + SUGADD, + sugAdd: SUGADD, + SUGDEL, + sugDel: SUGDEL, + SUGGET_WITHPAYLOADS, + sugGetWithPayloads: SUGGET_WITHPAYLOADS, + SUGGET_WITHSCORES_WITHPAYLOADS, + sugGetWithScoresWithPayloads: SUGGET_WITHSCORES_WITHPAYLOADS, + SUGGET_WITHSCORES, + sugGetWithScores: SUGGET_WITHSCORES, + SUGGET, + sugGet: SUGGET, + SUGLEN, + sugLen: SUGLEN, + SYNDUMP, + synDump: SYNDUMP, + SYNUPDATE, + synUpdate: SYNUPDATE, + TAGVALS, + tagVals: TAGVALS +}; + +export type PropertyName = `${'@' | '$.'}${string}`; + +export type SortByOptions = PropertyName | { + BY: PropertyName; + DIRECTION?: 'ASC' | 'DESC'; +}; + +function pushSortByProperty(args: RedisCommandArguments, sortBy: SortByOptions): void { + if (typeof sortBy === 'string') { + args.push(sortBy); + } else { + args.push(sortBy.BY); + + if (sortBy.DIRECTION) { + args.push(sortBy.DIRECTION); + } + } +} + +export function pushSortByArguments(args: RedisCommandArguments, name: string, sortBy: SortByOptions | Array): RedisCommandArguments { + const lengthBefore = args.push( + name, + '' // will be overwritten + ); + + if (Array.isArray(sortBy)) { + for (const field of sortBy) { + pushSortByProperty(args, field); + } + } else { + pushSortByProperty(args, sortBy); + } + + args[lengthBefore - 1] = (args.length - lengthBefore).toString(); + + return args; +} + +export function pushArgumentsWithLength(args: RedisCommandArguments, fn: (args: RedisCommandArguments) => void): RedisCommandArguments { + const lengthIndex = args.push('') - 1; + fn(args); + args[lengthIndex] = (args.length - lengthIndex - 1).toString(); + return args; +} diff --git a/packages/search/lib/index.ts b/packages/search/lib/index.ts new file mode 100644 index 00000000000..bc0e103e8c8 --- /dev/null +++ b/packages/search/lib/index.ts @@ -0,0 +1 @@ +export { default } from './commands'; diff --git a/packages/search/lib/test-utils.ts b/packages/search/lib/test-utils.ts new file mode 100644 index 00000000000..c68504c21d7 --- /dev/null +++ b/packages/search/lib/test-utils.ts @@ -0,0 +1,21 @@ +import TestUtils from '@redis/test-utils'; +import RediSearch from '.'; + +export default new TestUtils({ + dockerImageName: 'redislabs/redisearch', + dockerImageVersionArgument: 'redisearch-version', + defaultDockerVersion: '2.2.1' +}); + +export const GLOBAL = { + SERVERS: { + OPEN: { + serverArguments: ['--loadmodule /usr/lib/redis/modules/redisearch.so'], + clientOptions: { + modules: { + ft: RediSearch + } + } + } + } +}; diff --git a/packages/search/package.json b/packages/search/package.json new file mode 100644 index 00000000000..dc435cbd364 --- /dev/null +++ b/packages/search/package.json @@ -0,0 +1,24 @@ +{ + "name": "@redis/search", + "version": "1.0.0-rc.0", + "license": "MIT", + "main": "./dist/index.js", + "types": "./dist/index.d.ts", + "scripts": { + "test": "nyc -r text-summary -r html mocha -r source-map-support/register -r ts-node/register './lib/**/*.spec.ts'", + "build": "tsc" + }, + "peerDependencies": { + "@redis/client": "^4.0.0-rc" + }, + "devDependencies": { + "@istanbuljs/nyc-config-typescript": "^1.0.1", + "@redis/test-utils": "*", + "@types/node": "^16.11.6", + "nyc": "^15.1.0", + "release-it": "^14.11.6", + "source-map-support": "^0.5.20", + "ts-node": "^10.4.0", + "typescript": "^4.4.4" + } +} diff --git a/packages/search/tsconfig.json b/packages/search/tsconfig.json new file mode 100644 index 00000000000..fdb86c004cc --- /dev/null +++ b/packages/search/tsconfig.json @@ -0,0 +1,9 @@ +{ + "extends": "../../tsconfig", + "compilerOptions": { + "outDir": "./dist" + }, + "include": [ + "./lib/**/*.ts" + ] +} diff --git a/packages/test-utils/docker/Dockerfile b/packages/test-utils/docker/Dockerfile new file mode 100644 index 00000000000..23fc0b3a517 --- /dev/null +++ b/packages/test-utils/docker/Dockerfile @@ -0,0 +1,9 @@ +ARG IMAGE +FROM ${IMAGE} + +ARG REDIS_ARGUMENTS +ENV REDIS_ARGUMENTS=${REDIS_ARGUMENTS} + +COPY ./entrypoint.sh / + +ENTRYPOINT ["/entrypoint.sh"] diff --git a/packages/test-utils/docker/entrypoint.sh b/packages/test-utils/docker/entrypoint.sh new file mode 100755 index 00000000000..244977e83c4 --- /dev/null +++ b/packages/test-utils/docker/entrypoint.sh @@ -0,0 +1,7 @@ +#!/bin/bash + +echo testststealkshdfklhasdf + +echo $REDIS_ARGUMENTS + +redis-server $REDIS_ARGUMENTS diff --git a/lib/test-utils/dockers.ts b/packages/test-utils/lib/dockers.ts similarity index 90% rename from lib/test-utils/dockers.ts rename to packages/test-utils/lib/dockers.ts index 3e48618ee46..28bd1e49057 100644 --- a/lib/test-utils/dockers.ts +++ b/packages/test-utils/lib/dockers.ts @@ -1,8 +1,9 @@ import { createConnection } from 'net'; import { once } from 'events'; -import { RedisModules, RedisScripts } from '../commands'; -import RedisClient, { RedisClientType } from '../client'; -import { promiseTimeout } from '../utils'; +import { RedisModules, RedisScripts } from '@redis/client/lib/commands'; +import RedisClient, { RedisClientType } from '@redis/client/lib/client'; +import { promiseTimeout } from '@redis/client/lib/utils'; +import path from 'path'; import { promisify } from 'util'; import { exec } from 'child_process'; const execAsync = promisify(exec); @@ -45,11 +46,17 @@ export interface RedisServerDocker { dockerId: string; } +// ".." cause it'll be in `./dist` +const DOCKER_FODLER_PATH = path.join(__dirname, '../docker'); + async function spawnRedisServerDocker({ image, version }: RedisServerDockerConfig, serverArguments: Array): Promise { const port = (await portIterator.next()).value, { stdout, stderr } = await execAsync( - `docker run -d --network host ${image}:${version.join('.')} ` + - `--save --port ${port.toString()} ${serverArguments.join(' ')}` + 'docker run -d --network host $(' + + `docker build ${DOCKER_FODLER_PATH} -q ` + + `--build-arg IMAGE=${image}:${version.join('.')} ` + + `--build-arg REDIS_ARGUMENTS="--save --port ${port.toString()} ${serverArguments.join(' ')}"` + + ')' ); if (!stdout) { diff --git a/lib/test-utils/test-utils.ts b/packages/test-utils/lib/index.ts similarity index 83% rename from lib/test-utils/test-utils.ts rename to packages/test-utils/lib/index.ts index a550e3f707d..d6692cace66 100644 --- a/lib/test-utils/test-utils.ts +++ b/packages/test-utils/lib/index.ts @@ -1,10 +1,18 @@ -import { RedisModules, RedisScripts } from '../commands'; -import RedisClient, { RedisClientOptions, RedisClientType } from '../client'; -import RedisCluster, { RedisClusterOptions, RedisClusterType } from '../cluster'; +import { RedisModules, RedisScripts } from '@redis/client/lib/commands'; +import RedisClient, { RedisClientOptions, RedisClientType } from '@redis/client/lib/client'; +import RedisCluster, { RedisClusterOptions, RedisClusterType } from '@redis/client/lib/cluster'; import { RedisServerDockerConfig, spawnRedisServer, spawnRedisCluster } from './dockers'; import yargs from 'yargs'; import { hideBin } from 'yargs/helpers'; +interface TestUtilsConfig { + dockerImageName: string; + dockerImageVersionArgument: string; + defaultDockerVersion: string; + defaultClientOptions?: Partial>; + defaultClusterOptions?: Partial>; +} + interface CommonTestOptions { minimumDockerVersion?: Array; } @@ -20,20 +28,14 @@ interface ClusterTestOptions ext clusterConfiguration?: Partial>; } -interface TestsUtilsConfig { - dockerImageName: string; - dockerImageVersionArgument: string; - defaultDockerVersion: string; -} - -export default class TestUtils { - static #getVersion(config: TestsUtilsConfig): Array { +export default class TestUtils { + static #getVersion(argumentName: string, defaultVersion: string): Array { return yargs(hideBin(process.argv)) - .option('redis-version', { + .option(argumentName, { type: 'string', - default: config.defaultDockerVersion + default: defaultVersion }) - .coerce(config.dockerImageVersionArgument, (arg: string) => { + .coerce(argumentName, (arg: string) => { return arg.split('.').map(x => { const value = Number(x); if (Number.isNaN(value)) { @@ -43,16 +45,16 @@ export default class TestUtils { return value; }); }) - .demandOption(config.dockerImageVersionArgument) - .parseSync()[config.dockerImageVersionArgument]; + .demandOption(argumentName) + .parseSync()[argumentName]; } readonly #DOCKER_IMAGE: RedisServerDockerConfig; - constructor(config: TestsUtilsConfig) { + constructor(config: TestUtilsConfig) { this.#DOCKER_IMAGE = { image: config.dockerImageName, - version: TestUtils.#getVersion(config) + version: TestUtils.#getVersion(config.dockerImageVersionArgument, config.defaultDockerVersion) }; } @@ -84,7 +86,7 @@ export default class TestUtils { title: string, fn: (client: RedisClientType) => Promise, options: ClientTestOptions - ): Mocha.Test { + ): void { let dockerPromise: ReturnType; if (this.isVersionGreaterThan(options.minimumDockerVersion)) { const dockerImage = this.#DOCKER_IMAGE; @@ -96,7 +98,7 @@ export default class TestUtils { }); } - return it(title, async function() { + it(title, async function() { if (!dockerPromise) return this.skip(); const client = RedisClient.create({ @@ -111,10 +113,10 @@ export default class TestUtils { return fn(client); } + await client.connect(); + try { - await client.connect(); await client.flushAll(); - await fn(client); } finally { if (client.isOpen) { @@ -135,7 +137,7 @@ export default class TestUtils { title: string, fn: (cluster: RedisClusterType) => Promise, options: ClusterTestOptions - ): Mocha.Test { + ): void { let dockersPromise: ReturnType; if (this.isVersionGreaterThan(options.minimumDockerVersion)) { const dockerImage = this.#DOCKER_IMAGE; @@ -147,7 +149,7 @@ export default class TestUtils { }); } - return it(title, async function () { + it(title, async function () { if (!dockersPromise) return this.skip(); const dockers = await dockersPromise, diff --git a/packages/test-utils/package.json b/packages/test-utils/package.json new file mode 100644 index 00000000000..0c934ace9fb --- /dev/null +++ b/packages/test-utils/package.json @@ -0,0 +1,27 @@ +{ + "name": "@redis/test-utils", + "private": true, + "main": "./dist/index.js", + "types": "./dist/index.d.ts", + "scripts": { + "build": "tsc", + "test": "echo \"TODO\"" + }, + "peerDependencies": { + "@redis/client": "^4.0.0-rc" + }, + "devDependencies": { + "@istanbuljs/nyc-config-typescript": "^1.0.1", + "@tsconfig/node12": "^1.0.9", + "@types/mocha": "^9.0.0", + "@types/node": "^16.11.6", + "@types/yargs": "^17.0.5", + "mocha": "^9.1.3", + "nyc": "^15.1.0", + "release-it": "^14.11.6", + "source-map-support": "^0.5.20", + "ts-node": "^10.4.0", + "typescript": "^4.4.4", + "yargs": "^17.2.1" + } +} diff --git a/packages/test-utils/tsconfig.json b/packages/test-utils/tsconfig.json new file mode 100644 index 00000000000..fdb86c004cc --- /dev/null +++ b/packages/test-utils/tsconfig.json @@ -0,0 +1,9 @@ +{ + "extends": "../../tsconfig", + "compilerOptions": { + "outDir": "./dist" + }, + "include": [ + "./lib/**/*.ts" + ] +} diff --git a/tsconfig.json b/tsconfig.json index 1f76310034d..fa46effbdcc 100644 --- a/tsconfig.json +++ b/tsconfig.json @@ -1,33 +1,15 @@ { - "extends": "@tsconfig/node12/tsconfig.json", + "extends": "@tsconfig/node12/tsconfig", "compilerOptions": { - "outDir": "./dist", "declaration": true, - "useDefineForClassFields": true, - "allowJs": true + "allowJs": true, + "useDefineForClassFields": true }, "files": [ - "./lib/ts-declarations/cluster-key-slot.d.ts", - "./lib/ts-declarations/redis-parser.d.ts" - ], - "include": [ - "./index.ts", - "./lib/**/*.ts" + "./packages/client/lib/ts-declarations/cluster-key-slot.d.ts", + "./packages/client/lib/ts-declarations/redis-parser.d.ts" ], "ts-node": { "files": true - }, - "typedocOptions": { - "entryPoints": [ - "./index.ts", - "./lib" - ], - "entryPointStrategy": "expand", - "exclude": [ - "./lib/ts-declarations", - "./lib/test-utils.ts" - ], - "theme": "./node_modules/typedoc-github-wiki-theme/dist", - "out": "documentation" } } From 69d98bf39cec2c9b137db30994956a54d6a25dfd Mon Sep 17 00:00:00 2001 From: leibale Date: Tue, 9 Nov 2021 10:39:07 -0500 Subject: [PATCH 0927/1748] fix #1712 - fix LINDEX return type --- packages/client/lib/commands/LINDEX.ts | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/packages/client/lib/commands/LINDEX.ts b/packages/client/lib/commands/LINDEX.ts index 9a89b41da55..4c283f0912c 100644 --- a/packages/client/lib/commands/LINDEX.ts +++ b/packages/client/lib/commands/LINDEX.ts @@ -6,4 +6,4 @@ export function transformArguments(key: string, element: string): Array return ['LINDEX', key, element]; } -export declare function transformReply(): number | null; +export declare function transformReply(): string | null; From 64df5dc76e94d72188619e4457995df5bc3b96a0 Mon Sep 17 00:00:00 2001 From: leibale Date: Tue, 9 Nov 2021 12:32:21 -0500 Subject: [PATCH 0928/1748] uncomment TIME tests --- packages/client/lib/commands/TIME.spec.ts | 10 +++++----- 1 file changed, 5 insertions(+), 5 deletions(-) diff --git a/packages/client/lib/commands/TIME.spec.ts b/packages/client/lib/commands/TIME.spec.ts index 1139d18d537..bbaa7942db0 100644 --- a/packages/client/lib/commands/TIME.spec.ts +++ b/packages/client/lib/commands/TIME.spec.ts @@ -10,9 +10,9 @@ describe('TIME', () => { ); }); - // testUtils.testWithClient('client.time', async client => { - // const reply = await client.time(); - // assert.ok(reply instanceof Date); - // assert.ok(typeof reply.microseconds === 'number'); - // }, GLOBAL.SERVERS.OPEN); + testUtils.testWithClient('client.time', async client => { + const reply = await client.time(); + assert.ok(reply instanceof Date); + assert.ok(typeof reply.microseconds === 'number'); + }, GLOBAL.SERVERS.OPEN); }); From cd74f9632eb056ff18afd5e5cc21690990307275 Mon Sep 17 00:00:00 2001 From: leibale Date: Tue, 9 Nov 2021 13:07:53 -0500 Subject: [PATCH 0929/1748] use codecov --- .github/workflows/tests.yml | 32 +++++++++++++------------------- packages/client/.nycrc.json | 2 +- packages/client/README.md | 4 ++-- packages/client/package.json | 2 +- packages/json/package.json | 2 +- packages/search/package.json | 2 +- 6 files changed, 19 insertions(+), 25 deletions(-) diff --git a/.github/workflows/tests.yml b/.github/workflows/tests.yml index 59421208396..2d03fa563f8 100644 --- a/.github/workflows/tests.yml +++ b/.github/workflows/tests.yml @@ -25,6 +25,9 @@ jobs: with: node-version: ${{ matrix.node-version }} + - name: Update npm + run: npm i -g + - name: Install Packages run: npm ci @@ -34,22 +37,13 @@ jobs: - name: Run Tests run: npm run test -- --forbid-only --redis-version=${{ matrix.redis-version }} - - name: Generate lcov - run: ./node_modules/.bin/nyc report -r lcov - - - name: Coveralls - uses: coverallsapp/github-action@1.1.3 - with: - github-token: ${{ secrets.GITHUB_TOKEN }} - flag-name: Node ${{ matrix.node-version }} Redis ${{ matrix.redis-version }} - parallel: true - - finish: - needs: tests - runs-on: ubuntu-latest - steps: - - name: Coveralls Finished - uses: coverallsapp/github-action@1.1.3 - with: - github-token: ${{ secrets.github_token }} - parallel-finished: true + - name: Upload to Codecov + run: | + curl https://keybase.io/codecovsecurity/pgp_keys.asc | gpg --no-default-keyring --keyring trustedkeys.gpg --import + curl -Os https://uploader.codecov.io/latest/linux/codecov + curl -Os https://uploader.codecov.io/latest/linux/codecov.SHA256SUM + curl -Os https://uploader.codecov.io/latest/linux/codecov.SHA256SUM.sig + gpgv codecov.SHA256SUM.sig codecov.SHA256SUM + shasum -a 256 -c codecov.SHA256SUM + chmod +x codecov + ./codecov diff --git a/packages/client/.nycrc.json b/packages/client/.nycrc.json index b4e671e178f..dd42463d9cb 100644 --- a/packages/client/.nycrc.json +++ b/packages/client/.nycrc.json @@ -1,4 +1,4 @@ { "extends": "@istanbuljs/nyc-config-typescript", - "exclude": ["**/*.spec.ts", "lib/test-utils.ts"] + "exclude": ["**/*.spec.ts", "lib/test-utils.ts", "examples/*"] } diff --git a/packages/client/README.md b/packages/client/README.md index 813f9830efd..6b136bf48a5 100644 --- a/packages/client/README.md +++ b/packages/client/README.md @@ -6,8 +6,8 @@

- - Coverage Status + + Coverage Downloads diff --git a/packages/client/package.json b/packages/client/package.json index 30be25d26af..1b4124c2eb3 100644 --- a/packages/client/package.json +++ b/packages/client/package.json @@ -5,7 +5,7 @@ "main": "./dist/index.js", "types": "./dist/index.d.ts", "scripts": { - "test": "nyc -r text-summary -r html mocha -r source-map-support/register -r ts-node/register './lib/**/*.spec.ts'", + "test": "nyc -r text-summary -r lcov mocha -r source-map-support/register -r ts-node/register './lib/**/*.spec.ts'", "build": "tsc", "lint": "eslint ./*.ts ./lib/**/*.ts", "documentation": "typedoc" diff --git a/packages/json/package.json b/packages/json/package.json index ebd7f8f76fa..7f53cc0b807 100644 --- a/packages/json/package.json +++ b/packages/json/package.json @@ -5,7 +5,7 @@ "main": "./dist/index.js", "types": "./dist/index.d.ts", "scripts": { - "test": "nyc -r text-summary -r html mocha -r source-map-support/register -r ts-node/register './lib/**/*.spec.ts'", + "test": "nyc -r text-summary -r lcov mocha -r source-map-support/register -r ts-node/register './lib/**/*.spec.ts'", "build": "tsc" }, "peerDependencies": { diff --git a/packages/search/package.json b/packages/search/package.json index dc435cbd364..b44dc8fb528 100644 --- a/packages/search/package.json +++ b/packages/search/package.json @@ -5,7 +5,7 @@ "main": "./dist/index.js", "types": "./dist/index.d.ts", "scripts": { - "test": "nyc -r text-summary -r html mocha -r source-map-support/register -r ts-node/register './lib/**/*.spec.ts'", + "test": "nyc -r text-summary -r lcov mocha -r source-map-support/register -r ts-node/register './lib/**/*.spec.ts'", "build": "tsc" }, "peerDependencies": { From 28018caf48998eb2470f57a0bebd26f632d96ce4 Mon Sep 17 00:00:00 2001 From: leibale Date: Tue, 9 Nov 2021 13:09:35 -0500 Subject: [PATCH 0930/1748] fix tests.yml --- .github/workflows/tests.yml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.github/workflows/tests.yml b/.github/workflows/tests.yml index 2d03fa563f8..e85f7e1fd56 100644 --- a/.github/workflows/tests.yml +++ b/.github/workflows/tests.yml @@ -26,7 +26,7 @@ jobs: node-version: ${{ matrix.node-version }} - name: Update npm - run: npm i -g + run: npm i -g npm - name: Install Packages run: npm ci From 8b3daad88a70bf5e31c2a8e80d5f3000d0d2660c Mon Sep 17 00:00:00 2001 From: leibale Date: Tue, 9 Nov 2021 13:38:08 -0500 Subject: [PATCH 0931/1748] uncomment "should handle live resharding" test --- packages/client/lib/cluster/index.spec.ts | 71 ++++++++++++----------- packages/test-utils/lib/index.ts | 6 +- 2 files changed, 41 insertions(+), 36 deletions(-) diff --git a/packages/client/lib/cluster/index.spec.ts b/packages/client/lib/cluster/index.spec.ts index 43492a6500f..a4c4489ee7b 100644 --- a/packages/client/lib/cluster/index.spec.ts +++ b/packages/client/lib/cluster/index.spec.ts @@ -47,47 +47,48 @@ describe('Cluster', () => { } }); - // testUtils.testWithCluster('should handle live resharding', async cluster => { - // const key = 'key', - // value = 'value'; - // await cluster.set(key, value); + testUtils.testWithCluster('should handle live resharding', async cluster => { + const key = 'key', + value = 'value'; + await cluster.set(key, value); - // const slot = calculateSlot(key), - // from = cluster.getSlotMaster(slot), - // to = cluster.getMasters().find(node => node.id !== from.id); + const slot = calculateSlot(key), + from = cluster.getSlotMaster(slot), + to = cluster.getMasters().find(node => node.id !== from.id); - // await to!.client.clusterSetSlot(slot, ClusterSlotStates.IMPORTING, from.id); + await to!.client.clusterSetSlot(slot, ClusterSlotStates.IMPORTING, from.id); - // // should be able to get the key from the original node before it was migrated - // assert.equal( - // await cluster.get(key), - // value - // ); + // should be able to get the key from the original node before it was migrated + assert.equal( + await cluster.get(key), + value + ); - // await from.client.clusterSetSlot(slot, ClusterSlotStates.MIGRATING, to!.id); + await from.client.clusterSetSlot(slot, ClusterSlotStates.MIGRATING, to!.id); - // // should be able to get the key from the original node using the "ASKING" command - // assert.equal( - // await cluster.get(key), - // value - // ); + // should be able to get the key from the original node using the "ASKING" command + assert.equal( + await cluster.get(key), + value + ); - // const { port: toPort } = to!.client.options!.socket; + const { port: toPort } = to!.client.options!.socket; - // await from.client.migrate( - // '127.0.0.1', - // toPort, - // key, - // 0, - // 10 - // ); + await from.client.migrate( + '127.0.0.1', + toPort, + key, + 0, + 10 + ); - // // should be able to get the key from the new node - // assert.equal( - // await cluster.get(key), - // value - // ); - // }, { - // serverArguments: [] - // }); + // should be able to get the key from the new node + assert.equal( + await cluster.get(key), + value + ); + }, { + serverArguments: [], + numberOfNodes: 2 + }); }); diff --git a/packages/test-utils/lib/index.ts b/packages/test-utils/lib/index.ts index d6692cace66..f9a1fc1dbd9 100644 --- a/packages/test-utils/lib/index.ts +++ b/packages/test-utils/lib/index.ts @@ -26,6 +26,7 @@ interface ClientTestOptions exte interface ClusterTestOptions extends CommonTestOptions { serverArguments: Array; clusterConfiguration?: Partial>; + numberOfNodes?: number; } export default class TestUtils { @@ -144,7 +145,10 @@ export default class TestUtils { before(function () { this.timeout(30000); - dockersPromise = spawnRedisCluster(dockerImage, options.serverArguments); + dockersPromise = spawnRedisCluster({ + ...dockerImage, + numberOfNodes: options?.numberOfNodes + }, options.serverArguments); return dockersPromise; }); } From ab339d396a32e988a80f5ba71d999530d8ad064d Mon Sep 17 00:00:00 2001 From: leibale Date: Tue, 9 Nov 2021 18:07:26 -0500 Subject: [PATCH 0932/1748] fix #1714 - update README(s) --- README.md | 302 +- {packages/client/docs => docs}/FAQ.md | 0 .../docs => docs}/client-configuration.md | 0 {packages/client/docs => docs}/clustering.md | 0 .../docs => docs}/isolated-execution.md | 0 {packages/client/docs => docs}/v3-to-v4.md | 0 examples/.gitignore | 1 + .../client/examples => examples}/README.md | 10 +- .../blocking-list-pop.js | 0 .../command-with-modifiers.js | 19 +- .../connect-as-acl-user.js | 0 .../examples => examples}/lua-multi-incr.js | 0 .../client/examples => examples}/package.json | 5 +- examples/search+json.js | 76 + .../client/examples => examples}/set-scan.js | 0 package-lock.json | 11351 ---------------- packages/all-in-one/index.ts | 3 + packages/all-in-one/package.json | 4 +- packages/client/LICENSE | 24 - packages/client/README.md | 294 - packages/client/examples/package-lock.json | 87 - packages/client/package.json | 2 +- packages/json/lib/commands/GET.ts | 2 +- packages/json/package.json | 2 +- packages/search/lib/commands/CREATE.spec.ts | 23 +- packages/search/lib/commands/CREATE.ts | 42 +- packages/search/lib/commands/SEARCH.spec.ts | 3 +- packages/search/lib/commands/SEARCH.ts | 6 +- packages/search/lib/commands/index.ts | 28 + packages/search/lib/index.ts | 3 + packages/search/package.json | 2 +- packages/test-utils/lib/dockers.ts | 6 +- packages/test-utils/lib/index.ts | 6 +- packages/test-utils/package.json | 2 +- 34 files changed, 461 insertions(+), 11842 deletions(-) rename {packages/client/docs => docs}/FAQ.md (100%) rename {packages/client/docs => docs}/client-configuration.md (100%) rename {packages/client/docs => docs}/clustering.md (100%) rename {packages/client/docs => docs}/isolated-execution.md (100%) rename {packages/client/docs => docs}/v3-to-v4.md (100%) create mode 100644 examples/.gitignore rename {packages/client/examples => examples}/README.md (95%) rename {packages/client/examples => examples}/blocking-list-pop.js (100%) rename {packages/client/examples => examples}/command-with-modifiers.js (80%) rename {packages/client/examples => examples}/connect-as-acl-user.js (100%) rename {packages/client/examples => examples}/lua-multi-incr.js (100%) rename {packages/client/examples => examples}/package.json (64%) create mode 100644 examples/search+json.js rename {packages/client/examples => examples}/set-scan.js (100%) delete mode 100644 package-lock.json delete mode 100644 packages/client/LICENSE delete mode 100644 packages/client/README.md delete mode 100644 packages/client/examples/package-lock.json diff --git a/README.md b/README.md index 35648bd72ef..b5894f6f708 100644 --- a/README.md +++ b/README.md @@ -1,16 +1,304 @@ -# Node-Redis monorpo +

+ + + +

Node Redis

+

-### Clients + -| Name | Description | -|------------------------------------|-------------| -| [redis](./packages/all-in-one) | | -| [@redis/client](./packages/client) | | +--- -### [Modules](https://redis.io/modules) +## 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 { + 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](./packages/all-in-one) | | +| [@redis/client](./packages/client) | | | [@redis/json](./packages/json) | [Redis JSON](https://oss.redis.com/redisjson/) commands | | [@redis/search](./packages/search) | [Redis Search](https://oss.redis.com/redisearch/) commands | diff --git a/packages/client/docs/FAQ.md b/docs/FAQ.md similarity index 100% rename from packages/client/docs/FAQ.md rename to docs/FAQ.md diff --git a/packages/client/docs/client-configuration.md b/docs/client-configuration.md similarity index 100% rename from packages/client/docs/client-configuration.md rename to docs/client-configuration.md diff --git a/packages/client/docs/clustering.md b/docs/clustering.md similarity index 100% rename from packages/client/docs/clustering.md rename to docs/clustering.md diff --git a/packages/client/docs/isolated-execution.md b/docs/isolated-execution.md similarity index 100% rename from packages/client/docs/isolated-execution.md rename to docs/isolated-execution.md diff --git a/packages/client/docs/v3-to-v4.md b/docs/v3-to-v4.md similarity index 100% rename from packages/client/docs/v3-to-v4.md rename to docs/v3-to-v4.md diff --git a/examples/.gitignore b/examples/.gitignore new file mode 100644 index 00000000000..d8b83df9cdb --- /dev/null +++ b/examples/.gitignore @@ -0,0 +1 @@ +package-lock.json diff --git a/packages/client/examples/README.md b/examples/README.md similarity index 95% rename from packages/client/examples/README.md rename to examples/README.md index 2b8fbec9277..9b285315219 100644 --- a/packages/client/examples/README.md +++ b/examples/README.md @@ -21,7 +21,7 @@ To set up the examples folder so that you can run an example / develop one of yo ``` $ git clone https://github.com/redis/node-redis.git $ cd node-redis -$ npm install && npm run build +$ npm install -ws && npm run build $ cd examples $ npm install ``` @@ -62,13 +62,13 @@ Here's a starter template for adding a new example, imagine this is stored in `d import { createClient } from 'redis'; async function doSomething() { - const client = createClient(); + const client = createClient(); - await client.connect(); + await client.connect(); - // Add your example code here... + // Add your example code here... - await client.quit(); + await client.quit(); } doSomething(); diff --git a/packages/client/examples/blocking-list-pop.js b/examples/blocking-list-pop.js similarity index 100% rename from packages/client/examples/blocking-list-pop.js rename to examples/blocking-list-pop.js diff --git a/packages/client/examples/command-with-modifiers.js b/examples/command-with-modifiers.js similarity index 80% rename from packages/client/examples/command-with-modifiers.js rename to examples/command-with-modifiers.js index 2932aec0d64..78b1d2e3d2f 100644 --- a/packages/client/examples/command-with-modifiers.js +++ b/examples/command-with-modifiers.js @@ -10,19 +10,18 @@ async function commandWithModifiers() { await client.del('mykey'); let result = await client.set('mykey', 'myvalue', { - EX: 60, - GET: true - } - ); - + EX: 60, + GET: true + }); + console.log(result); //nil result = await client.set('mykey', 'newvalue', { - EX: 60, - GET: true - } - ); - + EX: 60, + GET: true + } + ); + console.log(result); //myvalue await client.quit(); diff --git a/packages/client/examples/connect-as-acl-user.js b/examples/connect-as-acl-user.js similarity index 100% rename from packages/client/examples/connect-as-acl-user.js rename to examples/connect-as-acl-user.js diff --git a/packages/client/examples/lua-multi-incr.js b/examples/lua-multi-incr.js similarity index 100% rename from packages/client/examples/lua-multi-incr.js rename to examples/lua-multi-incr.js diff --git a/packages/client/examples/package.json b/examples/package.json similarity index 64% rename from packages/client/examples/package.json rename to examples/package.json index edb8cdacdb2..452d88cbe5d 100644 --- a/packages/client/examples/package.json +++ b/examples/package.json @@ -4,6 +4,9 @@ "description": "node-redis 4 example script", "main": "index.js", "private": true, - "type": "module" + "type": "module", + "dependencies": { + "redis": "../packages/all-in-one" + } } diff --git a/examples/search+json.js b/examples/search+json.js new file mode 100644 index 00000000000..43eaa2a158d --- /dev/null +++ b/examples/search+json.js @@ -0,0 +1,76 @@ +// RediSearch & ReJSON example + +import { createClient, SchemaFieldTypes, AggregateGroupByReducers, AggregateSteps } from 'redis'; + +async function searchPlusJson() { + const client = createClient(); + + await client.connect(); + + await client.flushAll(); + + // 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(); diff --git a/packages/client/examples/set-scan.js b/examples/set-scan.js similarity index 100% rename from packages/client/examples/set-scan.js rename to examples/set-scan.js diff --git a/package-lock.json b/package-lock.json deleted file mode 100644 index 87ae980524c..00000000000 --- a/package-lock.json +++ /dev/null @@ -1,11351 +0,0 @@ -{ - "name": "redis-monorepo", - "lockfileVersion": 2, - "requires": true, - "packages": { - "": { - "name": "redis-monorepo", - "workspaces": [ - "./packages/*" - ], - "devDependencies": { - "@tsconfig/node12": "^1.0.9" - } - }, - "node_modules/@babel/code-frame": { - "version": "7.16.0", - "resolved": "https://registry.npmjs.org/@babel/code-frame/-/code-frame-7.16.0.tgz", - "integrity": "sha512-IF4EOMEV+bfYwOmNxGzSnjR2EmQod7f1UXOpZM3l4i4o4QNwzjtJAu/HxdjHq0aYBvdqMuQEY1eg0nqW9ZPORA==", - "dev": true, - "dependencies": { - "@babel/highlight": "^7.16.0" - }, - "engines": { - "node": ">=6.9.0" - } - }, - "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==", - "dev": true, - "engines": { - "node": ">=6.9.0" - } - }, - "node_modules/@babel/core": { - "version": "7.16.0", - "resolved": "https://registry.npmjs.org/@babel/core/-/core-7.16.0.tgz", - "integrity": "sha512-mYZEvshBRHGsIAiyH5PzCFTCfbWfoYbO/jcSdXQSUQu1/pW0xDZAUP7KEc32heqWTAfAHhV9j1vH8Sav7l+JNQ==", - "dev": true, - "dependencies": { - "@babel/code-frame": "^7.16.0", - "@babel/generator": "^7.16.0", - "@babel/helper-compilation-targets": "^7.16.0", - "@babel/helper-module-transforms": "^7.16.0", - "@babel/helpers": "^7.16.0", - "@babel/parser": "^7.16.0", - "@babel/template": "^7.16.0", - "@babel/traverse": "^7.16.0", - "@babel/types": "^7.16.0", - "convert-source-map": "^1.7.0", - "debug": "^4.1.0", - "gensync": "^1.0.0-beta.2", - "json5": "^2.1.2", - "semver": "^6.3.0", - "source-map": "^0.5.0" - }, - "engines": { - "node": ">=6.9.0" - }, - "funding": { - "type": "opencollective", - "url": "https://opencollective.com/babel" - } - }, - "node_modules/@babel/core/node_modules/semver": { - "version": "6.3.0", - "resolved": "https://registry.npmjs.org/semver/-/semver-6.3.0.tgz", - "integrity": "sha512-b39TBaTSfV6yBrapU89p5fKekE2m/NwnDocOVruQFS1/veMgdzuPcnOM34M6CwxW8jH/lxEa5rBoDeUwu5HHTw==", - "dev": true, - "bin": { - "semver": "bin/semver.js" - } - }, - "node_modules/@babel/generator": { - "version": "7.16.0", - "resolved": "https://registry.npmjs.org/@babel/generator/-/generator-7.16.0.tgz", - "integrity": "sha512-RR8hUCfRQn9j9RPKEVXo9LiwoxLPYn6hNZlvUOR8tSnaxlD0p0+la00ZP9/SnRt6HchKr+X0fO2r8vrETiJGew==", - "dev": true, - "dependencies": { - "@babel/types": "^7.16.0", - "jsesc": "^2.5.1", - "source-map": "^0.5.0" - }, - "engines": { - "node": ">=6.9.0" - } - }, - "node_modules/@babel/helper-compilation-targets": { - "version": "7.16.0", - "resolved": "https://registry.npmjs.org/@babel/helper-compilation-targets/-/helper-compilation-targets-7.16.0.tgz", - "integrity": "sha512-S7iaOT1SYlqK0sQaCi21RX4+13hmdmnxIEAnQUB/eh7GeAnRjOUgTYpLkUOiRXzD+yog1JxP0qyAQZ7ZxVxLVg==", - "dev": true, - "dependencies": { - "@babel/compat-data": "^7.16.0", - "@babel/helper-validator-option": "^7.14.5", - "browserslist": "^4.16.6", - "semver": "^6.3.0" - }, - "engines": { - "node": ">=6.9.0" - }, - "peerDependencies": { - "@babel/core": "^7.0.0" - } - }, - "node_modules/@babel/helper-compilation-targets/node_modules/semver": { - "version": "6.3.0", - "resolved": "https://registry.npmjs.org/semver/-/semver-6.3.0.tgz", - "integrity": "sha512-b39TBaTSfV6yBrapU89p5fKekE2m/NwnDocOVruQFS1/veMgdzuPcnOM34M6CwxW8jH/lxEa5rBoDeUwu5HHTw==", - "dev": true, - "bin": { - "semver": "bin/semver.js" - } - }, - "node_modules/@babel/helper-function-name": { - "version": "7.16.0", - "resolved": "https://registry.npmjs.org/@babel/helper-function-name/-/helper-function-name-7.16.0.tgz", - "integrity": "sha512-BZh4mEk1xi2h4HFjWUXRQX5AEx4rvaZxHgax9gcjdLWdkjsY7MKt5p0otjsg5noXw+pB+clMCjw+aEVYADMjog==", - "dev": true, - "dependencies": { - "@babel/helper-get-function-arity": "^7.16.0", - "@babel/template": "^7.16.0", - "@babel/types": "^7.16.0" - }, - "engines": { - "node": ">=6.9.0" - } - }, - "node_modules/@babel/helper-get-function-arity": { - "version": "7.16.0", - "resolved": "https://registry.npmjs.org/@babel/helper-get-function-arity/-/helper-get-function-arity-7.16.0.tgz", - "integrity": "sha512-ASCquNcywC1NkYh/z7Cgp3w31YW8aojjYIlNg4VeJiHkqyP4AzIvr4qx7pYDb4/s8YcsZWqqOSxgkvjUz1kpDQ==", - "dev": true, - "dependencies": { - "@babel/types": "^7.16.0" - }, - "engines": { - "node": ">=6.9.0" - } - }, - "node_modules/@babel/helper-hoist-variables": { - "version": "7.16.0", - "resolved": "https://registry.npmjs.org/@babel/helper-hoist-variables/-/helper-hoist-variables-7.16.0.tgz", - "integrity": "sha512-1AZlpazjUR0EQZQv3sgRNfM9mEVWPK3M6vlalczA+EECcPz3XPh6VplbErL5UoMpChhSck5wAJHthlj1bYpcmg==", - "dev": true, - "dependencies": { - "@babel/types": "^7.16.0" - }, - "engines": { - "node": ">=6.9.0" - } - }, - "node_modules/@babel/helper-member-expression-to-functions": { - "version": "7.16.0", - "resolved": "https://registry.npmjs.org/@babel/helper-member-expression-to-functions/-/helper-member-expression-to-functions-7.16.0.tgz", - "integrity": "sha512-bsjlBFPuWT6IWhl28EdrQ+gTvSvj5tqVP5Xeftp07SEuz5pLnsXZuDkDD3Rfcxy0IsHmbZ+7B2/9SHzxO0T+sQ==", - "dev": true, - "dependencies": { - "@babel/types": "^7.16.0" - }, - "engines": { - "node": ">=6.9.0" - } - }, - "node_modules/@babel/helper-module-imports": { - "version": "7.16.0", - "resolved": "https://registry.npmjs.org/@babel/helper-module-imports/-/helper-module-imports-7.16.0.tgz", - "integrity": "sha512-kkH7sWzKPq0xt3H1n+ghb4xEMP8k0U7XV3kkB+ZGy69kDk2ySFW1qPi06sjKzFY3t1j6XbJSqr4mF9L7CYVyhg==", - "dev": true, - "dependencies": { - "@babel/types": "^7.16.0" - }, - "engines": { - "node": ">=6.9.0" - } - }, - "node_modules/@babel/helper-module-transforms": { - "version": "7.16.0", - "resolved": "https://registry.npmjs.org/@babel/helper-module-transforms/-/helper-module-transforms-7.16.0.tgz", - "integrity": "sha512-My4cr9ATcaBbmaEa8M0dZNA74cfI6gitvUAskgDtAFmAqyFKDSHQo5YstxPbN+lzHl2D9l/YOEFqb2mtUh4gfA==", - "dev": true, - "dependencies": { - "@babel/helper-module-imports": "^7.16.0", - "@babel/helper-replace-supers": "^7.16.0", - "@babel/helper-simple-access": "^7.16.0", - "@babel/helper-split-export-declaration": "^7.16.0", - "@babel/helper-validator-identifier": "^7.15.7", - "@babel/template": "^7.16.0", - "@babel/traverse": "^7.16.0", - "@babel/types": "^7.16.0" - }, - "engines": { - "node": ">=6.9.0" - } - }, - "node_modules/@babel/helper-optimise-call-expression": { - "version": "7.16.0", - "resolved": "https://registry.npmjs.org/@babel/helper-optimise-call-expression/-/helper-optimise-call-expression-7.16.0.tgz", - "integrity": "sha512-SuI467Gi2V8fkofm2JPnZzB/SUuXoJA5zXe/xzyPP2M04686RzFKFHPK6HDVN6JvWBIEW8tt9hPR7fXdn2Lgpw==", - "dev": true, - "dependencies": { - "@babel/types": "^7.16.0" - }, - "engines": { - "node": ">=6.9.0" - } - }, - "node_modules/@babel/helper-replace-supers": { - "version": "7.16.0", - "resolved": "https://registry.npmjs.org/@babel/helper-replace-supers/-/helper-replace-supers-7.16.0.tgz", - "integrity": "sha512-TQxuQfSCdoha7cpRNJvfaYxxxzmbxXw/+6cS7V02eeDYyhxderSoMVALvwupA54/pZcOTtVeJ0xccp1nGWladA==", - "dev": true, - "dependencies": { - "@babel/helper-member-expression-to-functions": "^7.16.0", - "@babel/helper-optimise-call-expression": "^7.16.0", - "@babel/traverse": "^7.16.0", - "@babel/types": "^7.16.0" - }, - "engines": { - "node": ">=6.9.0" - } - }, - "node_modules/@babel/helper-simple-access": { - "version": "7.16.0", - "resolved": "https://registry.npmjs.org/@babel/helper-simple-access/-/helper-simple-access-7.16.0.tgz", - "integrity": "sha512-o1rjBT/gppAqKsYfUdfHq5Rk03lMQrkPHG1OWzHWpLgVXRH4HnMM9Et9CVdIqwkCQlobnGHEJMsgWP/jE1zUiw==", - "dev": true, - "dependencies": { - "@babel/types": "^7.16.0" - }, - "engines": { - "node": ">=6.9.0" - } - }, - "node_modules/@babel/helper-split-export-declaration": { - "version": "7.16.0", - "resolved": "https://registry.npmjs.org/@babel/helper-split-export-declaration/-/helper-split-export-declaration-7.16.0.tgz", - "integrity": "sha512-0YMMRpuDFNGTHNRiiqJX19GjNXA4H0E8jZ2ibccfSxaCogbm3am5WN/2nQNj0YnQwGWM1J06GOcQ2qnh3+0paw==", - "dev": true, - "dependencies": { - "@babel/types": "^7.16.0" - }, - "engines": { - "node": ">=6.9.0" - } - }, - "node_modules/@babel/helper-validator-identifier": { - "version": "7.15.7", - "resolved": "https://registry.npmjs.org/@babel/helper-validator-identifier/-/helper-validator-identifier-7.15.7.tgz", - "integrity": "sha512-K4JvCtQqad9OY2+yTU8w+E82ywk/fe+ELNlt1G8z3bVGlZfn/hOcQQsUhGhW/N+tb3fxK800wLtKOE/aM0m72w==", - "dev": true, - "engines": { - "node": ">=6.9.0" - } - }, - "node_modules/@babel/helper-validator-option": { - "version": "7.14.5", - "resolved": "https://registry.npmjs.org/@babel/helper-validator-option/-/helper-validator-option-7.14.5.tgz", - "integrity": "sha512-OX8D5eeX4XwcroVW45NMvoYaIuFI+GQpA2a8Gi+X/U/cDUIRsV37qQfF905F0htTRCREQIB4KqPeaveRJUl3Ow==", - "dev": true, - "engines": { - "node": ">=6.9.0" - } - }, - "node_modules/@babel/helpers": { - "version": "7.16.0", - "resolved": "https://registry.npmjs.org/@babel/helpers/-/helpers-7.16.0.tgz", - "integrity": "sha512-dVRM0StFMdKlkt7cVcGgwD8UMaBfWJHl3A83Yfs8GQ3MO0LHIIIMvK7Fa0RGOGUQ10qikLaX6D7o5htcQWgTMQ==", - "dev": true, - "dependencies": { - "@babel/template": "^7.16.0", - "@babel/traverse": "^7.16.0", - "@babel/types": "^7.16.0" - }, - "engines": { - "node": ">=6.9.0" - } - }, - "node_modules/@babel/highlight": { - "version": "7.16.0", - "resolved": "https://registry.npmjs.org/@babel/highlight/-/highlight-7.16.0.tgz", - "integrity": "sha512-t8MH41kUQylBtu2+4IQA3atqevA2lRgqA2wyVB/YiWmsDSuylZZuXOUy9ric30hfzauEFfdsuk/eXTRrGrfd0g==", - "dev": true, - "dependencies": { - "@babel/helper-validator-identifier": "^7.15.7", - "chalk": "^2.0.0", - "js-tokens": "^4.0.0" - }, - "engines": { - "node": ">=6.9.0" - } - }, - "node_modules/@babel/highlight/node_modules/ansi-styles": { - "version": "3.2.1", - "resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-3.2.1.tgz", - "integrity": "sha512-VT0ZI6kZRdTh8YyJw3SMbYm/u+NqfsAxEpWO0Pf9sq8/e94WxxOpPKx9FR1FlyCtOVDNOQ+8ntlqFxiRc+r5qA==", - "dev": true, - "dependencies": { - "color-convert": "^1.9.0" - }, - "engines": { - "node": ">=4" - } - }, - "node_modules/@babel/highlight/node_modules/chalk": { - "version": "2.4.2", - "resolved": "https://registry.npmjs.org/chalk/-/chalk-2.4.2.tgz", - "integrity": "sha512-Mti+f9lpJNcwF4tWV8/OrTTtF1gZi+f8FqlyAdouralcFWFQWF2+NgCHShjkCb+IFBLq9buZwE1xckQU4peSuQ==", - "dev": true, - "dependencies": { - "ansi-styles": "^3.2.1", - "escape-string-regexp": "^1.0.5", - "supports-color": "^5.3.0" - }, - "engines": { - "node": ">=4" - } - }, - "node_modules/@babel/highlight/node_modules/color-convert": { - "version": "1.9.3", - "resolved": "https://registry.npmjs.org/color-convert/-/color-convert-1.9.3.tgz", - "integrity": "sha512-QfAUtd+vFdAtFQcC8CCyYt1fYWxSqAiK2cSD6zDB8N3cpsEBAvRxp9zOGg6G/SHHJYAT88/az/IuDGALsNVbGg==", - "dev": true, - "dependencies": { - "color-name": "1.1.3" - } - }, - "node_modules/@babel/highlight/node_modules/color-name": { - "version": "1.1.3", - "resolved": "https://registry.npmjs.org/color-name/-/color-name-1.1.3.tgz", - "integrity": "sha1-p9BVi9icQveV3UIyj3QIMcpTvCU=", - "dev": true - }, - "node_modules/@babel/highlight/node_modules/escape-string-regexp": { - "version": "1.0.5", - "resolved": "https://registry.npmjs.org/escape-string-regexp/-/escape-string-regexp-1.0.5.tgz", - "integrity": "sha1-G2HAViGQqN/2rjuyzwIAyhMLhtQ=", - "dev": true, - "engines": { - "node": ">=0.8.0" - } - }, - "node_modules/@babel/highlight/node_modules/has-flag": { - "version": "3.0.0", - "resolved": "https://registry.npmjs.org/has-flag/-/has-flag-3.0.0.tgz", - "integrity": "sha1-tdRU3CGZriJWmfNGfloH87lVuv0=", - "dev": true, - "engines": { - "node": ">=4" - } - }, - "node_modules/@babel/highlight/node_modules/supports-color": { - "version": "5.5.0", - "resolved": "https://registry.npmjs.org/supports-color/-/supports-color-5.5.0.tgz", - "integrity": "sha512-QjVjwdXIt408MIiAqCX4oUKsgU2EqAGzs2Ppkm4aQYbjm+ZEWEcW4SfFNTr4uMNZma0ey4f5lgLrkB0aX0QMow==", - "dev": true, - "dependencies": { - "has-flag": "^3.0.0" - }, - "engines": { - "node": ">=4" - } - }, - "node_modules/@babel/parser": { - "version": "7.16.2", - "resolved": "https://registry.npmjs.org/@babel/parser/-/parser-7.16.2.tgz", - "integrity": "sha512-RUVpT0G2h6rOZwqLDTrKk7ksNv7YpAilTnYe1/Q+eDjxEceRMKVWbCsX7t8h6C1qCFi/1Y8WZjcEPBAFG27GPw==", - "dev": true, - "bin": { - "parser": "bin/babel-parser.js" - }, - "engines": { - "node": ">=6.0.0" - } - }, - "node_modules/@babel/template": { - "version": "7.16.0", - "resolved": "https://registry.npmjs.org/@babel/template/-/template-7.16.0.tgz", - "integrity": "sha512-MnZdpFD/ZdYhXwiunMqqgyZyucaYsbL0IrjoGjaVhGilz+x8YB++kRfygSOIj1yOtWKPlx7NBp+9I1RQSgsd5A==", - "dev": true, - "dependencies": { - "@babel/code-frame": "^7.16.0", - "@babel/parser": "^7.16.0", - "@babel/types": "^7.16.0" - }, - "engines": { - "node": ">=6.9.0" - } - }, - "node_modules/@babel/traverse": { - "version": "7.16.0", - "resolved": "https://registry.npmjs.org/@babel/traverse/-/traverse-7.16.0.tgz", - "integrity": "sha512-qQ84jIs1aRQxaGaxSysII9TuDaguZ5yVrEuC0BN2vcPlalwfLovVmCjbFDPECPXcYM/wLvNFfp8uDOliLxIoUQ==", - "dev": true, - "dependencies": { - "@babel/code-frame": "^7.16.0", - "@babel/generator": "^7.16.0", - "@babel/helper-function-name": "^7.16.0", - "@babel/helper-hoist-variables": "^7.16.0", - "@babel/helper-split-export-declaration": "^7.16.0", - "@babel/parser": "^7.16.0", - "@babel/types": "^7.16.0", - "debug": "^4.1.0", - "globals": "^11.1.0" - }, - "engines": { - "node": ">=6.9.0" - } - }, - "node_modules/@babel/traverse/node_modules/globals": { - "version": "11.12.0", - "resolved": "https://registry.npmjs.org/globals/-/globals-11.12.0.tgz", - "integrity": "sha512-WOBp/EEGUiIsJSp7wcv/y6MO+lV9UoncWqxuFfm8eBwzWNgyfBd6Gz+IeKQ9jCmyhoH99g15M3T+QaVHFjizVA==", - "dev": true, - "engines": { - "node": ">=4" - } - }, - "node_modules/@babel/types": { - "version": "7.16.0", - "resolved": "https://registry.npmjs.org/@babel/types/-/types-7.16.0.tgz", - "integrity": "sha512-PJgg/k3SdLsGb3hhisFvtLOw5ts113klrpLuIPtCJIU+BB24fqq6lf8RWqKJEjzqXR9AEH1rIb5XTqwBHB+kQg==", - "dev": true, - "dependencies": { - "@babel/helper-validator-identifier": "^7.15.7", - "to-fast-properties": "^2.0.0" - }, - "engines": { - "node": ">=6.9.0" - } - }, - "node_modules/@cspotcode/source-map-consumer": { - "version": "0.8.0", - "resolved": "https://registry.npmjs.org/@cspotcode/source-map-consumer/-/source-map-consumer-0.8.0.tgz", - "integrity": "sha512-41qniHzTU8yAGbCp04ohlmSrZf8bkf/iJsl3V0dRGsQN/5GFfx+LbCSsCpp2gqrqjTVg/K6O8ycoV35JIwAzAg==", - "dev": true, - "engines": { - "node": ">= 12" - } - }, - "node_modules/@cspotcode/source-map-support": { - "version": "0.7.0", - "resolved": "https://registry.npmjs.org/@cspotcode/source-map-support/-/source-map-support-0.7.0.tgz", - "integrity": "sha512-X4xqRHqN8ACt2aHVe51OxeA2HjbcL4MqFqXkrmQszJ1NOUuUu5u6Vqx/0lZSVNku7velL5FC/s5uEAj1lsBMhA==", - "dev": true, - "dependencies": { - "@cspotcode/source-map-consumer": "0.8.0" - }, - "engines": { - "node": ">=12" - } - }, - "node_modules/@eslint/eslintrc": { - "version": "1.0.4", - "resolved": "https://registry.npmjs.org/@eslint/eslintrc/-/eslintrc-1.0.4.tgz", - "integrity": "sha512-h8Vx6MdxwWI2WM8/zREHMoqdgLNXEL4QX3MWSVMdyNJGvXVOs+6lp+m2hc3FnuMHDc4poxFNI20vCk0OmI4G0Q==", - "dev": true, - "dependencies": { - "ajv": "^6.12.4", - "debug": "^4.3.2", - "espree": "^9.0.0", - "globals": "^13.9.0", - "ignore": "^4.0.6", - "import-fresh": "^3.2.1", - "js-yaml": "^4.1.0", - "minimatch": "^3.0.4", - "strip-json-comments": "^3.1.1" - }, - "engines": { - "node": "^12.22.0 || ^14.17.0 || >=16.0.0" - } - }, - "node_modules/@eslint/eslintrc/node_modules/ignore": { - "version": "4.0.6", - "resolved": "https://registry.npmjs.org/ignore/-/ignore-4.0.6.tgz", - "integrity": "sha512-cyFDKrqc/YdcWFniJhzI42+AzS+gNwmUzOSFcRCQYwySuBBBy/KjuxWLZ/FHEH6Moq1NizMOBWyTcv8O4OZIMg==", - "dev": true, - "engines": { - "node": ">= 4" - } - }, - "node_modules/@humanwhocodes/config-array": { - "version": "0.6.0", - "resolved": "https://registry.npmjs.org/@humanwhocodes/config-array/-/config-array-0.6.0.tgz", - "integrity": "sha512-JQlEKbcgEUjBFhLIF4iqM7u/9lwgHRBcpHrmUNCALK0Q3amXN6lxdoXLnF0sm11E9VqTmBALR87IlUg1bZ8A9A==", - "dev": true, - "dependencies": { - "@humanwhocodes/object-schema": "^1.2.0", - "debug": "^4.1.1", - "minimatch": "^3.0.4" - }, - "engines": { - "node": ">=10.10.0" - } - }, - "node_modules/@humanwhocodes/object-schema": { - "version": "1.2.1", - "resolved": "https://registry.npmjs.org/@humanwhocodes/object-schema/-/object-schema-1.2.1.tgz", - "integrity": "sha512-ZnQMnLV4e7hDlUvw8H+U8ASL02SS2Gn6+9Ac3wGGLIe7+je2AeAOxPY+izIPJDfFDb7eDjev0Us8MO1iFRN8hA==", - "dev": true - }, - "node_modules/@iarna/toml": { - "version": "2.2.5", - "resolved": "https://registry.npmjs.org/@iarna/toml/-/toml-2.2.5.tgz", - "integrity": "sha512-trnsAYxU3xnS1gPHPyU961coFyLkh4gAD/0zQ5mymY4yOZ+CYvsPqUbOFSw0aDM4y0tV7tiFxL/1XfXPNC6IPg==", - "dev": true - }, - "node_modules/@istanbuljs/load-nyc-config": { - "version": "1.1.0", - "resolved": "https://registry.npmjs.org/@istanbuljs/load-nyc-config/-/load-nyc-config-1.1.0.tgz", - "integrity": "sha512-VjeHSlIzpv/NyD3N0YuHfXOPDIixcA1q2ZV98wsMqcYlPmv2n3Yb2lYP9XMElnaFVXg5A7YLTeLu6V84uQDjmQ==", - "dev": true, - "dependencies": { - "camelcase": "^5.3.1", - "find-up": "^4.1.0", - "get-package-type": "^0.1.0", - "js-yaml": "^3.13.1", - "resolve-from": "^5.0.0" - }, - "engines": { - "node": ">=8" - } - }, - "node_modules/@istanbuljs/load-nyc-config/node_modules/argparse": { - "version": "1.0.10", - "resolved": "https://registry.npmjs.org/argparse/-/argparse-1.0.10.tgz", - "integrity": "sha512-o5Roy6tNG4SL/FOkCAN6RzjiakZS25RLYFrcMttJqbdd8BWrnA+fGz57iN5Pb06pvBGvl5gQ0B48dJlslXvoTg==", - "dev": true, - "dependencies": { - "sprintf-js": "~1.0.2" - } - }, - "node_modules/@istanbuljs/load-nyc-config/node_modules/find-up": { - "version": "4.1.0", - "resolved": "https://registry.npmjs.org/find-up/-/find-up-4.1.0.tgz", - "integrity": "sha512-PpOwAdQ/YlXQ2vj8a3h8IipDuYRi3wceVQQGYWxNINccq40Anw7BlsEXCMbt1Zt+OLA6Fq9suIpIWD0OsnISlw==", - "dev": true, - "dependencies": { - "locate-path": "^5.0.0", - "path-exists": "^4.0.0" - }, - "engines": { - "node": ">=8" - } - }, - "node_modules/@istanbuljs/load-nyc-config/node_modules/js-yaml": { - "version": "3.14.1", - "resolved": "https://registry.npmjs.org/js-yaml/-/js-yaml-3.14.1.tgz", - "integrity": "sha512-okMH7OXXJ7YrN9Ok3/SXrnu4iX9yOk+25nqX4imS2npuvTYDmo/QEZoqwZkYaIDk3jVvBOTOIEgEhaLOynBS9g==", - "dev": true, - "dependencies": { - "argparse": "^1.0.7", - "esprima": "^4.0.0" - }, - "bin": { - "js-yaml": "bin/js-yaml.js" - } - }, - "node_modules/@istanbuljs/load-nyc-config/node_modules/locate-path": { - "version": "5.0.0", - "resolved": "https://registry.npmjs.org/locate-path/-/locate-path-5.0.0.tgz", - "integrity": "sha512-t7hw9pI+WvuwNJXwk5zVHpyhIqzg2qTlklJOf0mVxGSbe3Fp2VieZcduNYjaLDoy6p9uGpQEGWG87WpMKlNq8g==", - "dev": true, - "dependencies": { - "p-locate": "^4.1.0" - }, - "engines": { - "node": ">=8" - } - }, - "node_modules/@istanbuljs/load-nyc-config/node_modules/p-limit": { - "version": "2.3.0", - "resolved": "https://registry.npmjs.org/p-limit/-/p-limit-2.3.0.tgz", - "integrity": "sha512-//88mFWSJx8lxCzwdAABTJL2MyWB12+eIY7MDL2SqLmAkeKU9qxRvWuSyTjm3FUmpBEMuFfckAIqEaVGUDxb6w==", - "dev": true, - "dependencies": { - "p-try": "^2.0.0" - }, - "engines": { - "node": ">=6" - }, - "funding": { - "url": "https://github.com/sponsors/sindresorhus" - } - }, - "node_modules/@istanbuljs/load-nyc-config/node_modules/p-locate": { - "version": "4.1.0", - "resolved": "https://registry.npmjs.org/p-locate/-/p-locate-4.1.0.tgz", - "integrity": "sha512-R79ZZ/0wAxKGu3oYMlz8jy/kbhsNrS7SKZ7PxEHBgJ5+F2mtFW2fK2cOtBh1cHYkQsbzFV7I+EoRKe6Yt0oK7A==", - "dev": true, - "dependencies": { - "p-limit": "^2.2.0" - }, - "engines": { - "node": ">=8" - } - }, - "node_modules/@istanbuljs/load-nyc-config/node_modules/resolve-from": { - "version": "5.0.0", - "resolved": "https://registry.npmjs.org/resolve-from/-/resolve-from-5.0.0.tgz", - "integrity": "sha512-qYg9KP24dD5qka9J47d0aVky0N+b4fTU89LN9iDnjB5waksiC49rvMB0PrUJQGoTmH50XPiqOvAjDfaijGxYZw==", - "dev": true, - "engines": { - "node": ">=8" - } - }, - "node_modules/@istanbuljs/nyc-config-typescript": { - "version": "1.0.1", - "resolved": "https://registry.npmjs.org/@istanbuljs/nyc-config-typescript/-/nyc-config-typescript-1.0.1.tgz", - "integrity": "sha512-/gz6LgVpky205LuoOfwEZmnUtaSmdk0QIMcNFj9OvxhiMhPpKftMgZmGN7jNj7jR+lr8IB1Yks3QSSSNSxfoaQ==", - "dev": true, - "dependencies": { - "@istanbuljs/schema": "^0.1.2" - }, - "engines": { - "node": ">=8" - }, - "peerDependencies": { - "nyc": ">=15", - "source-map-support": "*", - "ts-node": "*" - } - }, - "node_modules/@istanbuljs/schema": { - "version": "0.1.3", - "resolved": "https://registry.npmjs.org/@istanbuljs/schema/-/schema-0.1.3.tgz", - "integrity": "sha512-ZXRY4jNvVgSVQ8DL3LTcakaAtXwTVUxE81hslsyD2AtoXW/wVob10HkOJ1X/pAlcI7D+2YoZKg5do8G/w6RYgA==", - "dev": true, - "engines": { - "node": ">=8" - } - }, - "node_modules/@nodelib/fs.scandir": { - "version": "2.1.5", - "resolved": "https://registry.npmjs.org/@nodelib/fs.scandir/-/fs.scandir-2.1.5.tgz", - "integrity": "sha512-vq24Bq3ym5HEQm2NKCr3yXDwjc7vTsEThRDnkp2DK9p1uqLR+DHurm/NOTo0KG7HYHU7eppKZj3MyqYuMBf62g==", - "dev": true, - "dependencies": { - "@nodelib/fs.stat": "2.0.5", - "run-parallel": "^1.1.9" - }, - "engines": { - "node": ">= 8" - } - }, - "node_modules/@nodelib/fs.stat": { - "version": "2.0.5", - "resolved": "https://registry.npmjs.org/@nodelib/fs.stat/-/fs.stat-2.0.5.tgz", - "integrity": "sha512-RkhPPp2zrqDAQA/2jNhnztcPAlv64XdhIp7a7454A5ovI7Bukxgt7MX7udwAu3zg1DcpPU0rz3VV1SeaqvY4+A==", - "dev": true, - "engines": { - "node": ">= 8" - } - }, - "node_modules/@nodelib/fs.walk": { - "version": "1.2.8", - "resolved": "https://registry.npmjs.org/@nodelib/fs.walk/-/fs.walk-1.2.8.tgz", - "integrity": "sha512-oGB+UxlgWcgQkgwo8GcEGwemoTFt3FIO9ababBmaGwXIoBKZ+GTy0pP185beGg7Llih/NSHSV2XAs1lnznocSg==", - "dev": true, - "dependencies": { - "@nodelib/fs.scandir": "2.1.5", - "fastq": "^1.6.0" - }, - "engines": { - "node": ">= 8" - } - }, - "node_modules/@octokit/auth-token": { - "version": "2.5.0", - "resolved": "https://registry.npmjs.org/@octokit/auth-token/-/auth-token-2.5.0.tgz", - "integrity": "sha512-r5FVUJCOLl19AxiuZD2VRZ/ORjp/4IN98Of6YJoJOkY75CIBuYfmiNHGrDwXr+aLGG55igl9QrxX3hbiXlLb+g==", - "dev": true, - "dependencies": { - "@octokit/types": "^6.0.3" - } - }, - "node_modules/@octokit/core": { - "version": "3.5.1", - "resolved": "https://registry.npmjs.org/@octokit/core/-/core-3.5.1.tgz", - "integrity": "sha512-omncwpLVxMP+GLpLPgeGJBF6IWJFjXDS5flY5VbppePYX9XehevbDykRH9PdCdvqt9TS5AOTiDide7h0qrkHjw==", - "dev": true, - "dependencies": { - "@octokit/auth-token": "^2.4.4", - "@octokit/graphql": "^4.5.8", - "@octokit/request": "^5.6.0", - "@octokit/request-error": "^2.0.5", - "@octokit/types": "^6.0.3", - "before-after-hook": "^2.2.0", - "universal-user-agent": "^6.0.0" - } - }, - "node_modules/@octokit/endpoint": { - "version": "6.0.12", - "resolved": "https://registry.npmjs.org/@octokit/endpoint/-/endpoint-6.0.12.tgz", - "integrity": "sha512-lF3puPwkQWGfkMClXb4k/eUT/nZKQfxinRWJrdZaJO85Dqwo/G0yOC434Jr2ojwafWJMYqFGFa5ms4jJUgujdA==", - "dev": true, - "dependencies": { - "@octokit/types": "^6.0.3", - "is-plain-object": "^5.0.0", - "universal-user-agent": "^6.0.0" - } - }, - "node_modules/@octokit/graphql": { - "version": "4.8.0", - "resolved": "https://registry.npmjs.org/@octokit/graphql/-/graphql-4.8.0.tgz", - "integrity": "sha512-0gv+qLSBLKF0z8TKaSKTsS39scVKF9dbMxJpj3U0vC7wjNWFuIpL/z76Qe2fiuCbDRcJSavkXsVtMS6/dtQQsg==", - "dev": true, - "dependencies": { - "@octokit/request": "^5.6.0", - "@octokit/types": "^6.0.3", - "universal-user-agent": "^6.0.0" - } - }, - "node_modules/@octokit/openapi-types": { - "version": "11.2.0", - "resolved": "https://registry.npmjs.org/@octokit/openapi-types/-/openapi-types-11.2.0.tgz", - "integrity": "sha512-PBsVO+15KSlGmiI8QAzaqvsNlZlrDlyAJYcrXBCvVUxCp7VnXjkwPoFHgjEJXx3WF9BAwkA6nfCUA7i9sODzKA==", - "dev": true - }, - "node_modules/@octokit/plugin-paginate-rest": { - "version": "2.17.0", - "resolved": "https://registry.npmjs.org/@octokit/plugin-paginate-rest/-/plugin-paginate-rest-2.17.0.tgz", - "integrity": "sha512-tzMbrbnam2Mt4AhuyCHvpRkS0oZ5MvwwcQPYGtMv4tUa5kkzG58SVB0fcsLulOZQeRnOgdkZWkRUiyBlh0Bkyw==", - "dev": true, - "dependencies": { - "@octokit/types": "^6.34.0" - }, - "peerDependencies": { - "@octokit/core": ">=2" - } - }, - "node_modules/@octokit/plugin-request-log": { - "version": "1.0.4", - "resolved": "https://registry.npmjs.org/@octokit/plugin-request-log/-/plugin-request-log-1.0.4.tgz", - "integrity": "sha512-mLUsMkgP7K/cnFEw07kWqXGF5LKrOkD+lhCrKvPHXWDywAwuDUeDwWBpc69XK3pNX0uKiVt8g5z96PJ6z9xCFA==", - "dev": true, - "peerDependencies": { - "@octokit/core": ">=3" - } - }, - "node_modules/@octokit/plugin-rest-endpoint-methods": { - "version": "5.13.0", - "resolved": "https://registry.npmjs.org/@octokit/plugin-rest-endpoint-methods/-/plugin-rest-endpoint-methods-5.13.0.tgz", - "integrity": "sha512-uJjMTkN1KaOIgNtUPMtIXDOjx6dGYysdIFhgA52x4xSadQCz3b/zJexvITDVpANnfKPW/+E0xkOvLntqMYpviA==", - "dev": true, - "dependencies": { - "@octokit/types": "^6.34.0", - "deprecation": "^2.3.1" - }, - "peerDependencies": { - "@octokit/core": ">=3" - } - }, - "node_modules/@octokit/request": { - "version": "5.6.2", - "resolved": "https://registry.npmjs.org/@octokit/request/-/request-5.6.2.tgz", - "integrity": "sha512-je66CvSEVf0jCpRISxkUcCa0UkxmFs6eGDRSbfJtAVwbLH5ceqF+YEyC8lj8ystKyZTy8adWr0qmkY52EfOeLA==", - "dev": true, - "dependencies": { - "@octokit/endpoint": "^6.0.1", - "@octokit/request-error": "^2.1.0", - "@octokit/types": "^6.16.1", - "is-plain-object": "^5.0.0", - "node-fetch": "^2.6.1", - "universal-user-agent": "^6.0.0" - } - }, - "node_modules/@octokit/request-error": { - "version": "2.1.0", - "resolved": "https://registry.npmjs.org/@octokit/request-error/-/request-error-2.1.0.tgz", - "integrity": "sha512-1VIvgXxs9WHSjicsRwq8PlR2LR2x6DwsJAaFgzdi0JfJoGSO8mYI/cHJQ+9FbN21aa+DrgNLnwObmyeSC8Rmpg==", - "dev": true, - "dependencies": { - "@octokit/types": "^6.0.3", - "deprecation": "^2.0.0", - "once": "^1.4.0" - } - }, - "node_modules/@octokit/rest": { - "version": "18.10.0", - "resolved": "https://registry.npmjs.org/@octokit/rest/-/rest-18.10.0.tgz", - "integrity": "sha512-esHR5OKy38bccL/sajHqZudZCvmv4yjovMJzyXlphaUo7xykmtOdILGJ3aAm0mFHmMLmPFmDMJXf39cAjNJsrw==", - "dev": true, - "dependencies": { - "@octokit/core": "^3.5.1", - "@octokit/plugin-paginate-rest": "^2.16.0", - "@octokit/plugin-request-log": "^1.0.4", - "@octokit/plugin-rest-endpoint-methods": "^5.9.0" - } - }, - "node_modules/@octokit/types": { - "version": "6.34.0", - "resolved": "https://registry.npmjs.org/@octokit/types/-/types-6.34.0.tgz", - "integrity": "sha512-s1zLBjWhdEI2zwaoSgyOFoKSl109CUcVBCc7biPJ3aAf6LGLU6szDvi31JPU7bxfla2lqfhjbbg/5DdFNxOwHw==", - "dev": true, - "dependencies": { - "@octokit/openapi-types": "^11.2.0" - } - }, - "node_modules/@redis/client": { - "resolved": "packages/client", - "link": true - }, - "node_modules/@redis/json": { - "resolved": "packages/json", - "link": true - }, - "node_modules/@redis/search": { - "resolved": "packages/search", - "link": true - }, - "node_modules/@redis/test-utils": { - "resolved": "packages/test-utils", - "link": true - }, - "node_modules/@redis/time-series": { - "resolved": "packages/time-series", - "link": true - }, - "node_modules/@sindresorhus/is": { - "version": "4.2.0", - "resolved": "https://registry.npmjs.org/@sindresorhus/is/-/is-4.2.0.tgz", - "integrity": "sha512-VkE3KLBmJwcCaVARtQpfuKcKv8gcBmUubrfHGF84dXuuW6jgsRYxPtzcIhPyK9WAPpRt2/xY6zkD9MnRaJzSyw==", - "dev": true, - "engines": { - "node": ">=10" - }, - "funding": { - "url": "https://github.com/sindresorhus/is?sponsor=1" - } - }, - "node_modules/@sinonjs/commons": { - "version": "1.8.3", - "resolved": "https://registry.npmjs.org/@sinonjs/commons/-/commons-1.8.3.tgz", - "integrity": "sha512-xkNcLAn/wZaX14RPlwizcKicDk9G3F8m2nU3L7Ukm5zBgTwiT0wsoFAHx9Jq56fJA1z/7uKGtCRu16sOUCLIHQ==", - "dev": true, - "dependencies": { - "type-detect": "4.0.8" - } - }, - "node_modules/@sinonjs/fake-timers": { - "version": "7.1.2", - "resolved": "https://registry.npmjs.org/@sinonjs/fake-timers/-/fake-timers-7.1.2.tgz", - "integrity": "sha512-iQADsW4LBMISqZ6Ci1dupJL9pprqwcVFTcOsEmQOEhW+KLCVn/Y4Jrvg2k19fIHCp+iFprriYPTdRcQR8NbUPg==", - "dev": true, - "dependencies": { - "@sinonjs/commons": "^1.7.0" - } - }, - "node_modules/@sinonjs/samsam": { - "version": "6.0.2", - "resolved": "https://registry.npmjs.org/@sinonjs/samsam/-/samsam-6.0.2.tgz", - "integrity": "sha512-jxPRPp9n93ci7b8hMfJOFDPRLFYadN6FSpeROFTR4UNF4i5b+EK6m4QXPO46BDhFgRy1JuS87zAnFOzCUwMJcQ==", - "dev": true, - "dependencies": { - "@sinonjs/commons": "^1.6.0", - "lodash.get": "^4.4.2", - "type-detect": "^4.0.8" - } - }, - "node_modules/@sinonjs/text-encoding": { - "version": "0.7.1", - "resolved": "https://registry.npmjs.org/@sinonjs/text-encoding/-/text-encoding-0.7.1.tgz", - "integrity": "sha512-+iTbntw2IZPb/anVDbypzfQa+ay64MW0Zo8aJ8gZPWMMK6/OubMVb6lUPMagqjOPnmtauXnFCACVl3O7ogjeqQ==", - "dev": true - }, - "node_modules/@szmarczak/http-timer": { - "version": "4.0.6", - "resolved": "https://registry.npmjs.org/@szmarczak/http-timer/-/http-timer-4.0.6.tgz", - "integrity": "sha512-4BAffykYOgO+5nzBWYwE3W90sBgLJoUPRWWcL8wlyiM8IB8ipJz3UMJ9KXQd1RKQXpKp8Tutn80HZtWsu2u76w==", - "dev": true, - "dependencies": { - "defer-to-connect": "^2.0.0" - }, - "engines": { - "node": ">=10" - } - }, - "node_modules/@tsconfig/node10": { - "version": "1.0.8", - "resolved": "https://registry.npmjs.org/@tsconfig/node10/-/node10-1.0.8.tgz", - "integrity": "sha512-6XFfSQmMgq0CFLY1MslA/CPUfhIL919M1rMsa5lP2P097N2Wd1sSX0tx1u4olM16fLNhtHZpRhedZJphNJqmZg==", - "dev": true - }, - "node_modules/@tsconfig/node12": { - "version": "1.0.9", - "resolved": "https://registry.npmjs.org/@tsconfig/node12/-/node12-1.0.9.tgz", - "integrity": "sha512-/yBMcem+fbvhSREH+s14YJi18sp7J9jpuhYByADT2rypfajMZZN4WQ6zBGgBKp53NKmqI36wFYDb3yaMPurITw==", - "dev": true - }, - "node_modules/@tsconfig/node14": { - "version": "1.0.1", - "resolved": "https://registry.npmjs.org/@tsconfig/node14/-/node14-1.0.1.tgz", - "integrity": "sha512-509r2+yARFfHHE7T6Puu2jjkoycftovhXRqW328PDXTVGKihlb1P8Z9mMZH04ebyajfRY7dedfGynlrFHJUQCg==", - "dev": true - }, - "node_modules/@tsconfig/node16": { - "version": "1.0.2", - "resolved": "https://registry.npmjs.org/@tsconfig/node16/-/node16-1.0.2.tgz", - "integrity": "sha512-eZxlbI8GZscaGS7kkc/trHTT5xgrjH3/1n2JDwusC9iahPKWMRvRjJSAN5mCXviuTGQ/lHnhvv8Q1YTpnfz9gA==", - "dev": true - }, - "node_modules/@types/cacheable-request": { - "version": "6.0.2", - "resolved": "https://registry.npmjs.org/@types/cacheable-request/-/cacheable-request-6.0.2.tgz", - "integrity": "sha512-B3xVo+dlKM6nnKTcmm5ZtY/OL8bOAOd2Olee9M1zft65ox50OzjEHW91sDiU9j6cvW8Ejg1/Qkf4xd2kugApUA==", - "dev": true, - "dependencies": { - "@types/http-cache-semantics": "*", - "@types/keyv": "*", - "@types/node": "*", - "@types/responselike": "*" - } - }, - "node_modules/@types/http-cache-semantics": { - "version": "4.0.1", - "resolved": "https://registry.npmjs.org/@types/http-cache-semantics/-/http-cache-semantics-4.0.1.tgz", - "integrity": "sha512-SZs7ekbP8CN0txVG2xVRH6EgKmEm31BOxA07vkFaETzZz1xh+cbt8BcI0slpymvwhx5dlFnQG2rTlPVQn+iRPQ==", - "dev": true - }, - "node_modules/@types/json-schema": { - "version": "7.0.9", - "resolved": "https://registry.npmjs.org/@types/json-schema/-/json-schema-7.0.9.tgz", - "integrity": "sha512-qcUXuemtEu+E5wZSJHNxUXeCZhAfXKQ41D+duX+VYPde7xyEVZci+/oXKJL13tnRs9lR2pr4fod59GT6/X1/yQ==", - "dev": true - }, - "node_modules/@types/keyv": { - "version": "3.1.3", - "resolved": "https://registry.npmjs.org/@types/keyv/-/keyv-3.1.3.tgz", - "integrity": "sha512-FXCJgyyN3ivVgRoml4h94G/p3kY+u/B86La+QptcqJaWtBWtmc6TtkNfS40n9bIvyLteHh7zXOtgbobORKPbDg==", - "dev": true, - "dependencies": { - "@types/node": "*" - } - }, - "node_modules/@types/mocha": { - "version": "9.0.0", - "resolved": "https://registry.npmjs.org/@types/mocha/-/mocha-9.0.0.tgz", - "integrity": "sha512-scN0hAWyLVAvLR9AyW7HoFF5sJZglyBsbPuHO4fv7JRvfmPBMfp1ozWqOf/e4wwPNxezBZXRfWzMb6iFLgEVRA==", - "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==", - "dev": true - }, - "node_modules/@types/parse-json": { - "version": "4.0.0", - "resolved": "https://registry.npmjs.org/@types/parse-json/-/parse-json-4.0.0.tgz", - "integrity": "sha512-//oorEZjL6sbPcKUaCdIGlIUeH26mgzimjBB77G6XRgnDl/L5wOnpyBGRe/Mmf5CVW3PwEBE1NjiMZ/ssFh4wA==", - "dev": true - }, - "node_modules/@types/responselike": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/@types/responselike/-/responselike-1.0.0.tgz", - "integrity": "sha512-85Y2BjiufFzaMIlvJDvTTB8Fxl2xfLo4HgmHzVBz08w4wDePCTjYw66PdrolO0kzli3yam/YCgRufyo1DdQVTA==", - "dev": true, - "dependencies": { - "@types/node": "*" - } - }, - "node_modules/@types/sinon": { - "version": "10.0.6", - "resolved": "https://registry.npmjs.org/@types/sinon/-/sinon-10.0.6.tgz", - "integrity": "sha512-6EF+wzMWvBNeGrfP3Nx60hhx+FfwSg1JJBLAAP/IdIUq0EYkqCYf70VT3PhuhPX9eLD+Dp+lNdpb/ZeHG8Yezg==", - "dev": true, - "dependencies": { - "@sinonjs/fake-timers": "^7.1.0" - } - }, - "node_modules/@types/yallist": { - "version": "4.0.1", - "resolved": "https://registry.npmjs.org/@types/yallist/-/yallist-4.0.1.tgz", - "integrity": "sha512-G3FNJfaYtN8URU6wd6+uwFI62KO79j7n3XTYcwcFncP8gkfoi0b821GoVVt0oqKVnCqKYOMNKIGpakPoFhzAGA==", - "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==", - "dev": true, - "dependencies": { - "@types/yargs-parser": "*" - } - }, - "node_modules/@types/yargs-parser": { - "version": "20.2.1", - "resolved": "https://registry.npmjs.org/@types/yargs-parser/-/yargs-parser-20.2.1.tgz", - "integrity": "sha512-7tFImggNeNBVMsn0vLrpn1H1uPrUBdnARPTpZoitY37ZrdJREzf7I16tMrlK3hen349gr1NYh8CmZQa7CTG6Aw==", - "dev": true - }, - "node_modules/@typescript-eslint/eslint-plugin": { - "version": "5.3.1", - "resolved": "https://registry.npmjs.org/@typescript-eslint/eslint-plugin/-/eslint-plugin-5.3.1.tgz", - "integrity": "sha512-cFImaoIr5Ojj358xI/SDhjog57OK2NqlpxwdcgyxDA3bJlZcJq5CPzUXtpD7CxI2Hm6ATU7w5fQnnkVnmwpHqw==", - "dev": true, - "dependencies": { - "@typescript-eslint/experimental-utils": "5.3.1", - "@typescript-eslint/scope-manager": "5.3.1", - "debug": "^4.3.2", - "functional-red-black-tree": "^1.0.1", - "ignore": "^5.1.8", - "regexpp": "^3.2.0", - "semver": "^7.3.5", - "tsutils": "^3.21.0" - }, - "engines": { - "node": "^12.22.0 || ^14.17.0 || >=16.0.0" - }, - "funding": { - "type": "opencollective", - "url": "https://opencollective.com/typescript-eslint" - }, - "peerDependencies": { - "@typescript-eslint/parser": "^5.0.0", - "eslint": "^6.0.0 || ^7.0.0 || ^8.0.0" - }, - "peerDependenciesMeta": { - "typescript": { - "optional": true - } - } - }, - "node_modules/@typescript-eslint/experimental-utils": { - "version": "5.3.1", - "resolved": "https://registry.npmjs.org/@typescript-eslint/experimental-utils/-/experimental-utils-5.3.1.tgz", - "integrity": "sha512-RgFn5asjZ5daUhbK5Sp0peq0SSMytqcrkNfU4pnDma2D8P3ElZ6JbYjY8IMSFfZAJ0f3x3tnO3vXHweYg0g59w==", - "dev": true, - "dependencies": { - "@types/json-schema": "^7.0.9", - "@typescript-eslint/scope-manager": "5.3.1", - "@typescript-eslint/types": "5.3.1", - "@typescript-eslint/typescript-estree": "5.3.1", - "eslint-scope": "^5.1.1", - "eslint-utils": "^3.0.0" - }, - "engines": { - "node": "^12.22.0 || ^14.17.0 || >=16.0.0" - }, - "funding": { - "type": "opencollective", - "url": "https://opencollective.com/typescript-eslint" - }, - "peerDependencies": { - "eslint": "*" - } - }, - "node_modules/@typescript-eslint/parser": { - "version": "5.3.1", - "resolved": "https://registry.npmjs.org/@typescript-eslint/parser/-/parser-5.3.1.tgz", - "integrity": "sha512-TD+ONlx5c+Qhk21x9gsJAMRohWAUMavSOmJgv3JGy9dgPhuBd5Wok0lmMClZDyJNLLZK1JRKiATzCKZNUmoyfw==", - "dev": true, - "dependencies": { - "@typescript-eslint/scope-manager": "5.3.1", - "@typescript-eslint/types": "5.3.1", - "@typescript-eslint/typescript-estree": "5.3.1", - "debug": "^4.3.2" - }, - "engines": { - "node": "^12.22.0 || ^14.17.0 || >=16.0.0" - }, - "funding": { - "type": "opencollective", - "url": "https://opencollective.com/typescript-eslint" - }, - "peerDependencies": { - "eslint": "^6.0.0 || ^7.0.0 || ^8.0.0" - }, - "peerDependenciesMeta": { - "typescript": { - "optional": true - } - } - }, - "node_modules/@typescript-eslint/scope-manager": { - "version": "5.3.1", - "resolved": "https://registry.npmjs.org/@typescript-eslint/scope-manager/-/scope-manager-5.3.1.tgz", - "integrity": "sha512-XksFVBgAq0Y9H40BDbuPOTUIp7dn4u8oOuhcgGq7EoDP50eqcafkMVGrypyVGvDYHzjhdUCUwuwVUK4JhkMAMg==", - "dev": true, - "dependencies": { - "@typescript-eslint/types": "5.3.1", - "@typescript-eslint/visitor-keys": "5.3.1" - }, - "engines": { - "node": "^12.22.0 || ^14.17.0 || >=16.0.0" - }, - "funding": { - "type": "opencollective", - "url": "https://opencollective.com/typescript-eslint" - } - }, - "node_modules/@typescript-eslint/types": { - "version": "5.3.1", - "resolved": "https://registry.npmjs.org/@typescript-eslint/types/-/types-5.3.1.tgz", - "integrity": "sha512-bG7HeBLolxKHtdHG54Uac750eXuQQPpdJfCYuw4ZI3bZ7+GgKClMWM8jExBtp7NSP4m8PmLRM8+lhzkYnSmSxQ==", - "dev": true, - "engines": { - "node": "^12.22.0 || ^14.17.0 || >=16.0.0" - }, - "funding": { - "type": "opencollective", - "url": "https://opencollective.com/typescript-eslint" - } - }, - "node_modules/@typescript-eslint/typescript-estree": { - "version": "5.3.1", - "resolved": "https://registry.npmjs.org/@typescript-eslint/typescript-estree/-/typescript-estree-5.3.1.tgz", - "integrity": "sha512-PwFbh/PKDVo/Wct6N3w+E4rLZxUDgsoII/GrWM2A62ETOzJd4M6s0Mu7w4CWsZraTbaC5UQI+dLeyOIFF1PquQ==", - "dev": true, - "dependencies": { - "@typescript-eslint/types": "5.3.1", - "@typescript-eslint/visitor-keys": "5.3.1", - "debug": "^4.3.2", - "globby": "^11.0.4", - "is-glob": "^4.0.3", - "semver": "^7.3.5", - "tsutils": "^3.21.0" - }, - "engines": { - "node": "^12.22.0 || ^14.17.0 || >=16.0.0" - }, - "funding": { - "type": "opencollective", - "url": "https://opencollective.com/typescript-eslint" - }, - "peerDependenciesMeta": { - "typescript": { - "optional": true - } - } - }, - "node_modules/@typescript-eslint/visitor-keys": { - "version": "5.3.1", - "resolved": "https://registry.npmjs.org/@typescript-eslint/visitor-keys/-/visitor-keys-5.3.1.tgz", - "integrity": "sha512-3cHUzUuVTuNHx0Gjjt5pEHa87+lzyqOiHXy/Gz+SJOCW1mpw9xQHIIEwnKn+Thph1mgWyZ90nboOcSuZr/jTTQ==", - "dev": true, - "dependencies": { - "@typescript-eslint/types": "5.3.1", - "eslint-visitor-keys": "^3.0.0" - }, - "engines": { - "node": "^12.22.0 || ^14.17.0 || >=16.0.0" - }, - "funding": { - "type": "opencollective", - "url": "https://opencollective.com/typescript-eslint" - } - }, - "node_modules/@ungap/promise-all-settled": { - "version": "1.1.2", - "resolved": "https://registry.npmjs.org/@ungap/promise-all-settled/-/promise-all-settled-1.1.2.tgz", - "integrity": "sha512-sL/cEvJWAnClXw0wHk85/2L0G6Sj8UB0Ctc1TEMbKSsmpRosqhwj9gWgFRZSrBr2f9tiXISwNhCPmlfqUqyb9Q==", - "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==", - "dev": true, - "bin": { - "acorn": "bin/acorn" - }, - "engines": { - "node": ">=0.4.0" - } - }, - "node_modules/acorn-jsx": { - "version": "5.3.2", - "resolved": "https://registry.npmjs.org/acorn-jsx/-/acorn-jsx-5.3.2.tgz", - "integrity": "sha512-rq9s+JNhf0IChjtDXxllJ7g41oZk5SlXtp0LHwyA5cejwn7vKmKp4pPri6YEePv2PU65sAsegbXtIinmDFDXgQ==", - "dev": true, - "peerDependencies": { - "acorn": "^6.0.0 || ^7.0.0 || ^8.0.0" - } - }, - "node_modules/acorn-walk": { - "version": "8.2.0", - "resolved": "https://registry.npmjs.org/acorn-walk/-/acorn-walk-8.2.0.tgz", - "integrity": "sha512-k+iyHEuPgSw6SbuDpGQM+06HQUa04DZ3o+F6CSzXMvvI5KMvnaEqXe+YVe555R9nn6GPt404fos4wcgpw12SDA==", - "dev": true, - "engines": { - "node": ">=0.4.0" - } - }, - "node_modules/aggregate-error": { - "version": "3.1.0", - "resolved": "https://registry.npmjs.org/aggregate-error/-/aggregate-error-3.1.0.tgz", - "integrity": "sha512-4I7Td01quW/RpocfNayFdFVk1qSuoh0E7JrbRJ16nH01HhKFQ88INq9Sd+nd72zqRySlr9BmDA8xlEJ6vJMrYA==", - "dev": true, - "dependencies": { - "clean-stack": "^2.0.0", - "indent-string": "^4.0.0" - }, - "engines": { - "node": ">=8" - } - }, - "node_modules/ajv": { - "version": "6.12.6", - "resolved": "https://registry.npmjs.org/ajv/-/ajv-6.12.6.tgz", - "integrity": "sha512-j3fVLgvTo527anyYyJOGTYJbG+vnnQYvE0m5mmkc1TK+nxAppkCLMIL0aZ4dblVCNoGShhm+kzE4ZUykBoMg4g==", - "dev": true, - "dependencies": { - "fast-deep-equal": "^3.1.1", - "fast-json-stable-stringify": "^2.0.0", - "json-schema-traverse": "^0.4.1", - "uri-js": "^4.2.2" - }, - "funding": { - "type": "github", - "url": "https://github.com/sponsors/epoberezkin" - } - }, - "node_modules/ansi-align": { - "version": "3.0.1", - "resolved": "https://registry.npmjs.org/ansi-align/-/ansi-align-3.0.1.tgz", - "integrity": "sha512-IOfwwBF5iczOjp/WeY4YxyjqAFMQoZufdQWDd19SEExbVLNXqvpzSJ/M7Za4/sCPmQ0+GRquoA7bGcINcxew6w==", - "dev": true, - "dependencies": { - "string-width": "^4.1.0" - } - }, - "node_modules/ansi-colors": { - "version": "4.1.1", - "resolved": "https://registry.npmjs.org/ansi-colors/-/ansi-colors-4.1.1.tgz", - "integrity": "sha512-JoX0apGbHaUJBNl6yF+p6JAFYZ666/hhCGKN5t9QFjbJQKUU/g8MNbFDbvfrgKXvI1QpZplPOnwIo99lX/AAmA==", - "dev": true, - "engines": { - "node": ">=6" - } - }, - "node_modules/ansi-escapes": { - "version": "4.3.2", - "resolved": "https://registry.npmjs.org/ansi-escapes/-/ansi-escapes-4.3.2.tgz", - "integrity": "sha512-gKXj5ALrKWQLsYG9jlTRmR/xKluxHV+Z9QEwNIgCfM1/uwPMCuzVVnh5mwTd+OuBZcwSIMbqssNWRm1lE51QaQ==", - "dev": true, - "dependencies": { - "type-fest": "^0.21.3" - }, - "engines": { - "node": ">=8" - }, - "funding": { - "url": "https://github.com/sponsors/sindresorhus" - } - }, - "node_modules/ansi-escapes/node_modules/type-fest": { - "version": "0.21.3", - "resolved": "https://registry.npmjs.org/type-fest/-/type-fest-0.21.3.tgz", - "integrity": "sha512-t0rzBq87m3fVcduHDUFhKmyyX+9eo6WQjZvf51Ea/M0Q7+T374Jp1aUiyUl0GKxp8M/OETVHSDvmkyPgvX+X2w==", - "dev": true, - "engines": { - "node": ">=10" - }, - "funding": { - "url": "https://github.com/sponsors/sindresorhus" - } - }, - "node_modules/ansi-regex": { - "version": "5.0.1", - "resolved": "https://registry.npmjs.org/ansi-regex/-/ansi-regex-5.0.1.tgz", - "integrity": "sha512-quJQXlTSUGL2LH9SUXo8VwsY4soanhgo6LNSm84E1LBcE8s3O0wpdiRzyR9z/ZZJMlMWv37qOOb9pdJlMUEKFQ==", - "dev": true, - "engines": { - "node": ">=8" - } - }, - "node_modules/ansi-styles": { - "version": "4.3.0", - "resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-4.3.0.tgz", - "integrity": "sha512-zbB9rCJAT1rbjiVDb2hqKFHNYLxgtk8NURxZ3IZwD3F6NtxbXZQCnnSi1Lkx+IDohdPlFp222wVALIheZJQSEg==", - "dev": true, - "dependencies": { - "color-convert": "^2.0.1" - }, - "engines": { - "node": ">=8" - }, - "funding": { - "url": "https://github.com/chalk/ansi-styles?sponsor=1" - } - }, - "node_modules/anymatch": { - "version": "3.1.2", - "resolved": "https://registry.npmjs.org/anymatch/-/anymatch-3.1.2.tgz", - "integrity": "sha512-P43ePfOAIupkguHUycrc4qJ9kz8ZiuOUijaETwX7THt0Y/GNK7v0aa8rY816xWjZ7rJdA5XdMcpVFTKMq+RvWg==", - "dev": true, - "dependencies": { - "normalize-path": "^3.0.0", - "picomatch": "^2.0.4" - }, - "engines": { - "node": ">= 8" - } - }, - "node_modules/append-transform": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/append-transform/-/append-transform-2.0.0.tgz", - "integrity": "sha512-7yeyCEurROLQJFv5Xj4lEGTy0borxepjFv1g22oAdqFu//SrAlDl1O1Nxx15SH1RoliUml6p8dwJW9jvZughhg==", - "dev": true, - "dependencies": { - "default-require-extensions": "^3.0.0" - }, - "engines": { - "node": ">=8" - } - }, - "node_modules/archy": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/archy/-/archy-1.0.0.tgz", - "integrity": "sha1-+cjBN1fMHde8N5rHeyxipcKGjEA=", - "dev": true - }, - "node_modules/arg": { - "version": "4.1.3", - "resolved": "https://registry.npmjs.org/arg/-/arg-4.1.3.tgz", - "integrity": "sha512-58S9QDqG0Xx27YwPSt9fJxivjYl432YCwfDMfZ+71RAqUrZef7LrKQZ3LHLOwCS4FLNBplP533Zx895SeOCHvA==", - "dev": true - }, - "node_modules/argparse": { - "version": "2.0.1", - "resolved": "https://registry.npmjs.org/argparse/-/argparse-2.0.1.tgz", - "integrity": "sha512-8+9WqebbFzpX9OR+Wa6O29asIogeRMzcGtAINdpMHHyAg10f05aSFVBbcEqGf/PXw1EjAZ+q2/bEBg3DvurK3Q==", - "dev": true - }, - "node_modules/array-union": { - "version": "2.1.0", - "resolved": "https://registry.npmjs.org/array-union/-/array-union-2.1.0.tgz", - "integrity": "sha512-HGyxoOTYUyCM6stUe6EJgnd4EoewAI7zMdfqO+kGjnlZmBDz/cR5pf8r/cR4Wq60sL/p0IkcjUEEPwS3GFrIyw==", - "dev": true, - "engines": { - "node": ">=8" - } - }, - "node_modules/async-retry": { - "version": "1.3.3", - "resolved": "https://registry.npmjs.org/async-retry/-/async-retry-1.3.3.tgz", - "integrity": "sha512-wfr/jstw9xNi/0teMHrRW7dsz3Lt5ARhYNZ2ewpadnhaIp5mbALhOAP+EAdsC7t4Z6wqsDVv9+W6gm1Dk9mEyw==", - "dev": true, - "dependencies": { - "retry": "0.13.1" - } - }, - "node_modules/asynckit": { - "version": "0.4.0", - "resolved": "https://registry.npmjs.org/asynckit/-/asynckit-0.4.0.tgz", - "integrity": "sha1-x57Zf380y48robyXkLzDZkdLS3k=", - "dev": true - }, - "node_modules/balanced-match": { - "version": "1.0.2", - "resolved": "https://registry.npmjs.org/balanced-match/-/balanced-match-1.0.2.tgz", - "integrity": "sha512-3oSeUO0TMV67hN1AmbXsK4yaqU7tjiHlbxRDZOpH0KW9+CeX4bRAaX0Anxt0tx2MrpRpWwQaPwIlISEJhYU5Pw==", - "dev": true - }, - "node_modules/base64-js": { - "version": "1.5.1", - "resolved": "https://registry.npmjs.org/base64-js/-/base64-js-1.5.1.tgz", - "integrity": "sha512-AKpaYlHn8t4SVbOHCy+b5+KKgvR4vrsD8vbvrbiQJps7fKDTkjkDry6ji0rUJjC0kzbNePLwzxq8iypo41qeWA==", - "dev": true, - "funding": [ - { - "type": "github", - "url": "https://github.com/sponsors/feross" - }, - { - "type": "patreon", - "url": "https://www.patreon.com/feross" - }, - { - "type": "consulting", - "url": "https://feross.org/support" - } - ] - }, - "node_modules/before-after-hook": { - "version": "2.2.2", - "resolved": "https://registry.npmjs.org/before-after-hook/-/before-after-hook-2.2.2.tgz", - "integrity": "sha512-3pZEU3NT5BFUo/AD5ERPWOgQOCZITni6iavr5AUw5AUwQjMlI0kzu5btnyD39AF0gUEsDPwJT+oY1ORBJijPjQ==", - "dev": true - }, - "node_modules/binary-extensions": { - "version": "2.2.0", - "resolved": "https://registry.npmjs.org/binary-extensions/-/binary-extensions-2.2.0.tgz", - "integrity": "sha512-jDctJ/IVQbZoJykoeHbhXpOlNBqGNcwXJKJog42E5HDPUwQTSdjCHdihjj0DlnheQ7blbT6dHOafNAiS8ooQKA==", - "dev": true, - "engines": { - "node": ">=8" - } - }, - "node_modules/bl": { - "version": "4.1.0", - "resolved": "https://registry.npmjs.org/bl/-/bl-4.1.0.tgz", - "integrity": "sha512-1W07cM9gS6DcLperZfFSj+bWLtaPGSOHWhPiGzXmvVJbRLdG82sH/Kn8EtW1VqWVA54AKf2h5k5BbnIbwF3h6w==", - "dev": true, - "dependencies": { - "buffer": "^5.5.0", - "inherits": "^2.0.4", - "readable-stream": "^3.4.0" - } - }, - "node_modules/boxen": { - "version": "5.1.2", - "resolved": "https://registry.npmjs.org/boxen/-/boxen-5.1.2.tgz", - "integrity": "sha512-9gYgQKXx+1nP8mP7CzFyaUARhg7D3n1dF/FnErWmu9l6JvGpNUN278h0aSb+QjoiKSWG+iZ3uHrcqk0qrY9RQQ==", - "dev": true, - "dependencies": { - "ansi-align": "^3.0.0", - "camelcase": "^6.2.0", - "chalk": "^4.1.0", - "cli-boxes": "^2.2.1", - "string-width": "^4.2.2", - "type-fest": "^0.20.2", - "widest-line": "^3.1.0", - "wrap-ansi": "^7.0.0" - }, - "engines": { - "node": ">=10" - }, - "funding": { - "url": "https://github.com/sponsors/sindresorhus" - } - }, - "node_modules/boxen/node_modules/camelcase": { - "version": "6.2.0", - "resolved": "https://registry.npmjs.org/camelcase/-/camelcase-6.2.0.tgz", - "integrity": "sha512-c7wVvbw3f37nuobQNtgsgG9POC9qMbNuMQmTCqZv23b6MIz0fcYpBiOlv9gEN/hdLdnZTDQhg6e9Dq5M1vKvfg==", - "dev": true, - "engines": { - "node": ">=10" - }, - "funding": { - "url": "https://github.com/sponsors/sindresorhus" - } - }, - "node_modules/brace-expansion": { - "version": "1.1.11", - "resolved": "https://registry.npmjs.org/brace-expansion/-/brace-expansion-1.1.11.tgz", - "integrity": "sha512-iCuPHDFgrHX7H2vEI/5xpz07zSHB00TpugqhmYtVmMO6518mCuRMoOYFldEBl0g187ufozdaHgWKcYFb61qGiA==", - "dev": true, - "dependencies": { - "balanced-match": "^1.0.0", - "concat-map": "0.0.1" - } - }, - "node_modules/braces": { - "version": "3.0.2", - "resolved": "https://registry.npmjs.org/braces/-/braces-3.0.2.tgz", - "integrity": "sha512-b8um+L1RzM3WDSzvhm6gIz1yfTbBt6YTlcEKAvsmqCZZFw46z626lVj9j1yEPW33H5H+lBQpZMP1k8l+78Ha0A==", - "dev": true, - "dependencies": { - "fill-range": "^7.0.1" - }, - "engines": { - "node": ">=8" - } - }, - "node_modules/browser-stdout": { - "version": "1.3.1", - "resolved": "https://registry.npmjs.org/browser-stdout/-/browser-stdout-1.3.1.tgz", - "integrity": "sha512-qhAVI1+Av2X7qelOfAIYwXONood6XlZE/fXaBSmW/T5SzLAmCgzi+eiWE7fUvbHaeNBQH13UftjpXxsfLkMpgw==", - "dev": true - }, - "node_modules/browserslist": { - "version": "4.17.6", - "resolved": "https://registry.npmjs.org/browserslist/-/browserslist-4.17.6.tgz", - "integrity": "sha512-uPgz3vyRTlEiCv4ee9KlsKgo2V6qPk7Jsn0KAn2OBqbqKo3iNcPEC1Ti6J4dwnz+aIRfEEEuOzC9IBk8tXUomw==", - "dev": true, - "dependencies": { - "caniuse-lite": "^1.0.30001274", - "electron-to-chromium": "^1.3.886", - "escalade": "^3.1.1", - "node-releases": "^2.0.1", - "picocolors": "^1.0.0" - }, - "bin": { - "browserslist": "cli.js" - }, - "engines": { - "node": "^6 || ^7 || ^8 || ^9 || ^10 || ^11 || ^12 || >=13.7" - }, - "funding": { - "type": "opencollective", - "url": "https://opencollective.com/browserslist" - } - }, - "node_modules/buffer": { - "version": "5.7.1", - "resolved": "https://registry.npmjs.org/buffer/-/buffer-5.7.1.tgz", - "integrity": "sha512-EHcyIPBQ4BSGlvjB16k5KgAJ27CIsHY/2JBmCRReo48y9rQ3MaUzWX3KVlBa4U7MyX02HdVj0K7C3WaB3ju7FQ==", - "dev": true, - "funding": [ - { - "type": "github", - "url": "https://github.com/sponsors/feross" - }, - { - "type": "patreon", - "url": "https://www.patreon.com/feross" - }, - { - "type": "consulting", - "url": "https://feross.org/support" - } - ], - "dependencies": { - "base64-js": "^1.3.1", - "ieee754": "^1.1.13" - } - }, - "node_modules/buffer-from": { - "version": "1.1.2", - "resolved": "https://registry.npmjs.org/buffer-from/-/buffer-from-1.1.2.tgz", - "integrity": "sha512-E+XQCRwSbaaiChtv6k6Dwgc+bx+Bs6vuKJHHl5kox/BaKbhiXzqQOwK4cO22yElGp2OCmjwVhT3HmxgyPGnJfQ==", - "dev": true - }, - "node_modules/cacheable-lookup": { - "version": "5.0.4", - "resolved": "https://registry.npmjs.org/cacheable-lookup/-/cacheable-lookup-5.0.4.tgz", - "integrity": "sha512-2/kNscPhpcxrOigMZzbiWF7dz8ilhb/nIHU3EyZiXWXpeq/au8qJ8VhdftMkty3n7Gj6HIGalQG8oiBNB3AJgA==", - "dev": true, - "engines": { - "node": ">=10.6.0" - } - }, - "node_modules/cacheable-request": { - "version": "7.0.2", - "resolved": "https://registry.npmjs.org/cacheable-request/-/cacheable-request-7.0.2.tgz", - "integrity": "sha512-pouW8/FmiPQbuGpkXQ9BAPv/Mo5xDGANgSNXzTzJ8DrKGuXOssM4wIQRjfanNRh3Yu5cfYPvcorqbhg2KIJtew==", - "dev": true, - "dependencies": { - "clone-response": "^1.0.2", - "get-stream": "^5.1.0", - "http-cache-semantics": "^4.0.0", - "keyv": "^4.0.0", - "lowercase-keys": "^2.0.0", - "normalize-url": "^6.0.1", - "responselike": "^2.0.0" - }, - "engines": { - "node": ">=8" - } - }, - "node_modules/cacheable-request/node_modules/get-stream": { - "version": "5.2.0", - "resolved": "https://registry.npmjs.org/get-stream/-/get-stream-5.2.0.tgz", - "integrity": "sha512-nBF+F1rAZVCu/p7rjzgA+Yb4lfYXrpl7a6VmJrU8wF9I1CKvP/QwPNZHnOlwbTkY6dvtFIzFMSyQXbLoTQPRpA==", - "dev": true, - "dependencies": { - "pump": "^3.0.0" - }, - "engines": { - "node": ">=8" - }, - "funding": { - "url": "https://github.com/sponsors/sindresorhus" - } - }, - "node_modules/caching-transform": { - "version": "4.0.0", - "resolved": "https://registry.npmjs.org/caching-transform/-/caching-transform-4.0.0.tgz", - "integrity": "sha512-kpqOvwXnjjN44D89K5ccQC+RUrsy7jB/XLlRrx0D7/2HNcTPqzsb6XgYoErwko6QsV184CA2YgS1fxDiiDZMWA==", - "dev": true, - "dependencies": { - "hasha": "^5.0.0", - "make-dir": "^3.0.0", - "package-hash": "^4.0.0", - "write-file-atomic": "^3.0.0" - }, - "engines": { - "node": ">=8" - } - }, - "node_modules/call-bind": { - "version": "1.0.2", - "resolved": "https://registry.npmjs.org/call-bind/-/call-bind-1.0.2.tgz", - "integrity": "sha512-7O+FbCihrB5WGbFYesctwmTKae6rOiIzmz1icreWJ+0aA7LJfuqhEso2T9ncpcFtzMQtzXf2QGGueWJGTYsqrA==", - "dev": true, - "dependencies": { - "function-bind": "^1.1.1", - "get-intrinsic": "^1.0.2" - }, - "funding": { - "url": "https://github.com/sponsors/ljharb" - } - }, - "node_modules/callsites": { - "version": "3.1.0", - "resolved": "https://registry.npmjs.org/callsites/-/callsites-3.1.0.tgz", - "integrity": "sha512-P8BjAsXvZS+VIDUI11hHCQEv74YT67YUi5JJFNWIqL235sBmjX4+qx9Muvls5ivyNENctx46xQLQ3aTuE7ssaQ==", - "dev": true, - "engines": { - "node": ">=6" - } - }, - "node_modules/camelcase": { - "version": "5.3.1", - "resolved": "https://registry.npmjs.org/camelcase/-/camelcase-5.3.1.tgz", - "integrity": "sha512-L28STB170nwWS63UjtlEOE3dldQApaJXZkOI1uMFfzf3rRuPegHaHesyee+YxQ+W6SvRDQV6UrdOdRiR153wJg==", - "dev": true, - "engines": { - "node": ">=6" - } - }, - "node_modules/caniuse-lite": { - "version": "1.0.30001278", - "resolved": "https://registry.npmjs.org/caniuse-lite/-/caniuse-lite-1.0.30001278.tgz", - "integrity": "sha512-mpF9KeH8u5cMoEmIic/cr7PNS+F5LWBk0t2ekGT60lFf0Wq+n9LspAj0g3P+o7DQhD3sUdlMln4YFAWhFYn9jg==", - "dev": true, - "funding": { - "type": "opencollective", - "url": "https://opencollective.com/browserslist" - } - }, - "node_modules/chalk": { - "version": "4.1.2", - "resolved": "https://registry.npmjs.org/chalk/-/chalk-4.1.2.tgz", - "integrity": "sha512-oKnbhFyRIXpUuez8iBMmyEa4nbj4IOQyuhc/wy9kY7/WVPcwIO9VA668Pu8RkO7+0G76SLROeyw9CpQ061i4mA==", - "dev": true, - "dependencies": { - "ansi-styles": "^4.1.0", - "supports-color": "^7.1.0" - }, - "engines": { - "node": ">=10" - }, - "funding": { - "url": "https://github.com/chalk/chalk?sponsor=1" - } - }, - "node_modules/chardet": { - "version": "0.7.0", - "resolved": "https://registry.npmjs.org/chardet/-/chardet-0.7.0.tgz", - "integrity": "sha512-mT8iDcrh03qDGRRmoA2hmBJnxpllMR+0/0qlzjqZES6NdiWDcZkCNAk4rPFZ9Q85r27unkiNNg8ZOiwZXBHwcA==", - "dev": true - }, - "node_modules/chokidar": { - "version": "3.5.2", - "resolved": "https://registry.npmjs.org/chokidar/-/chokidar-3.5.2.tgz", - "integrity": "sha512-ekGhOnNVPgT77r4K/U3GDhu+FQ2S8TnK/s2KbIGXi0SZWuwkZ2QNyfWdZW+TVfn84DpEP7rLeCt2UI6bJ8GwbQ==", - "dev": true, - "dependencies": { - "anymatch": "~3.1.2", - "braces": "~3.0.2", - "glob-parent": "~5.1.2", - "is-binary-path": "~2.1.0", - "is-glob": "~4.0.1", - "normalize-path": "~3.0.0", - "readdirp": "~3.6.0" - }, - "engines": { - "node": ">= 8.10.0" - }, - "optionalDependencies": { - "fsevents": "~2.3.2" - } - }, - "node_modules/chokidar/node_modules/glob-parent": { - "version": "5.1.2", - "resolved": "https://registry.npmjs.org/glob-parent/-/glob-parent-5.1.2.tgz", - "integrity": "sha512-AOIgSQCepiJYwP3ARnGx+5VnTu2HBYdzbGP45eLw1vr3zB3vZLeyed1sC9hnbcOc9/SrMyM5RPQrkGz4aS9Zow==", - "dev": true, - "dependencies": { - "is-glob": "^4.0.1" - }, - "engines": { - "node": ">= 6" - } - }, - "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==", - "dev": true - }, - "node_modules/clean-stack": { - "version": "2.2.0", - "resolved": "https://registry.npmjs.org/clean-stack/-/clean-stack-2.2.0.tgz", - "integrity": "sha512-4diC9HaTE+KRAMWhDhrGOECgWZxoevMc5TlkObMqNSsVU62PYzXZ/SMTjzyGAFF1YusgxGcSWTEXBhp0CPwQ1A==", - "dev": true, - "engines": { - "node": ">=6" - } - }, - "node_modules/cli-boxes": { - "version": "2.2.1", - "resolved": "https://registry.npmjs.org/cli-boxes/-/cli-boxes-2.2.1.tgz", - "integrity": "sha512-y4coMcylgSCdVinjiDBuR8PCC2bLjyGTwEmPb9NHR/QaNU6EUOXcTY/s6VjGMD6ENSEaeQYHCY0GNGS5jfMwPw==", - "dev": true, - "engines": { - "node": ">=6" - }, - "funding": { - "url": "https://github.com/sponsors/sindresorhus" - } - }, - "node_modules/cli-cursor": { - "version": "3.1.0", - "resolved": "https://registry.npmjs.org/cli-cursor/-/cli-cursor-3.1.0.tgz", - "integrity": "sha512-I/zHAwsKf9FqGoXM4WWRACob9+SNukZTd94DWF57E4toouRulbCxcUh6RKUEOQlYTHJnzkPMySvPNaaSLNfLZw==", - "dev": true, - "dependencies": { - "restore-cursor": "^3.1.0" - }, - "engines": { - "node": ">=8" - } - }, - "node_modules/cli-spinners": { - "version": "2.6.1", - "resolved": "https://registry.npmjs.org/cli-spinners/-/cli-spinners-2.6.1.tgz", - "integrity": "sha512-x/5fWmGMnbKQAaNwN+UZlV79qBLM9JFnJuJ03gIi5whrob0xV0ofNVHy9DhwGdsMJQc2OKv0oGmLzvaqvAVv+g==", - "dev": true, - "engines": { - "node": ">=6" - }, - "funding": { - "url": "https://github.com/sponsors/sindresorhus" - } - }, - "node_modules/cli-width": { - "version": "3.0.0", - "resolved": "https://registry.npmjs.org/cli-width/-/cli-width-3.0.0.tgz", - "integrity": "sha512-FxqpkPPwu1HjuN93Omfm4h8uIanXofW0RxVEW3k5RKx+mJJYSthzNhp32Kzxxy3YAEZ/Dc/EWN1vZRY0+kOhbw==", - "dev": true, - "engines": { - "node": ">= 10" - } - }, - "node_modules/cliui": { - "version": "7.0.4", - "resolved": "https://registry.npmjs.org/cliui/-/cliui-7.0.4.tgz", - "integrity": "sha512-OcRE68cOsVMXp1Yvonl/fzkQOyjLSu/8bhPDfQt0e0/Eb283TKP20Fs2MqoPsr9SwA595rRCA+QMzYc9nBP+JQ==", - "dev": true, - "dependencies": { - "string-width": "^4.2.0", - "strip-ansi": "^6.0.0", - "wrap-ansi": "^7.0.0" - } - }, - "node_modules/clone": { - "version": "1.0.4", - "resolved": "https://registry.npmjs.org/clone/-/clone-1.0.4.tgz", - "integrity": "sha1-2jCcwmPfFZlMaIypAheco8fNfH4=", - "dev": true, - "engines": { - "node": ">=0.8" - } - }, - "node_modules/clone-response": { - "version": "1.0.2", - "resolved": "https://registry.npmjs.org/clone-response/-/clone-response-1.0.2.tgz", - "integrity": "sha1-0dyXOSAxTfZ/vrlCI7TuNQI56Ws=", - "dev": true, - "dependencies": { - "mimic-response": "^1.0.0" - } - }, - "node_modules/cluster-key-slot": { - "version": "1.1.0", - "resolved": "https://registry.npmjs.org/cluster-key-slot/-/cluster-key-slot-1.1.0.tgz", - "integrity": "sha512-2Nii8p3RwAPiFwsnZvukotvow2rIHM+yQ6ZcBXGHdniadkYGZYiGmkHJIbZPIV9nfv7m/U1IPMVVcAhoWFeklw==", - "engines": { - "node": ">=0.10.0" - } - }, - "node_modules/color-convert": { - "version": "2.0.1", - "resolved": "https://registry.npmjs.org/color-convert/-/color-convert-2.0.1.tgz", - "integrity": "sha512-RRECPsj7iu/xb5oKYcsFHSppFNnsj/52OVTRKb4zP5onXwVF3zVmmToNcOfGC+CRDpfK/U584fMg38ZHCaElKQ==", - "dev": true, - "dependencies": { - "color-name": "~1.1.4" - }, - "engines": { - "node": ">=7.0.0" - } - }, - "node_modules/color-name": { - "version": "1.1.4", - "resolved": "https://registry.npmjs.org/color-name/-/color-name-1.1.4.tgz", - "integrity": "sha512-dOy+3AuW3a2wNbZHIuMZpTcgjGuLU/uBL/ubcZF9OXbDo8ff4O8yVp5Bf0efS8uEoYo5q4Fx7dY9OgQGXgAsQA==", - "dev": true - }, - "node_modules/combined-stream": { - "version": "1.0.8", - "resolved": "https://registry.npmjs.org/combined-stream/-/combined-stream-1.0.8.tgz", - "integrity": "sha512-FQN4MRfuJeHf7cBbBMJFXhKSDq+2kAArBlmRBvcvFE5BB1HZKXtSFASDhdlz9zOYwxh8lDdnvmMOe/+5cdoEdg==", - "dev": true, - "dependencies": { - "delayed-stream": "~1.0.0" - }, - "engines": { - "node": ">= 0.8" - } - }, - "node_modules/commondir": { - "version": "1.0.1", - "resolved": "https://registry.npmjs.org/commondir/-/commondir-1.0.1.tgz", - "integrity": "sha1-3dgA2gxmEnOTzKWVDqloo6rxJTs=", - "dev": true - }, - "node_modules/concat-map": { - "version": "0.0.1", - "resolved": "https://registry.npmjs.org/concat-map/-/concat-map-0.0.1.tgz", - "integrity": "sha1-2Klr13/Wjfd5OnMDajug1UBdR3s=", - "dev": true - }, - "node_modules/configstore": { - "version": "5.0.1", - "resolved": "https://registry.npmjs.org/configstore/-/configstore-5.0.1.tgz", - "integrity": "sha512-aMKprgk5YhBNyH25hj8wGt2+D52Sw1DRRIzqBwLp2Ya9mFmY8KPvvtvmna8SxVR9JMZ4kzMD68N22vlaRpkeFA==", - "dev": true, - "dependencies": { - "dot-prop": "^5.2.0", - "graceful-fs": "^4.1.2", - "make-dir": "^3.0.0", - "unique-string": "^2.0.0", - "write-file-atomic": "^3.0.0", - "xdg-basedir": "^4.0.0" - }, - "engines": { - "node": ">=8" - } - }, - "node_modules/convert-source-map": { - "version": "1.8.0", - "resolved": "https://registry.npmjs.org/convert-source-map/-/convert-source-map-1.8.0.tgz", - "integrity": "sha512-+OQdjP49zViI/6i7nIJpA8rAl4sV/JdPfU9nZs3VqOwGIgizICvuN2ru6fMd+4llL0tar18UYJXfZ/TWtmhUjA==", - "dev": true, - "dependencies": { - "safe-buffer": "~5.1.1" - } - }, - "node_modules/cosmiconfig": { - "version": "7.0.1", - "resolved": "https://registry.npmjs.org/cosmiconfig/-/cosmiconfig-7.0.1.tgz", - "integrity": "sha512-a1YWNUV2HwGimB7dU2s1wUMurNKjpx60HxBB6xUM8Re+2s1g1IIfJvFR0/iCF+XHdE0GMTKTuLR32UQff4TEyQ==", - "dev": true, - "dependencies": { - "@types/parse-json": "^4.0.0", - "import-fresh": "^3.2.1", - "parse-json": "^5.0.0", - "path-type": "^4.0.0", - "yaml": "^1.10.0" - }, - "engines": { - "node": ">=10" - } - }, - "node_modules/create-require": { - "version": "1.1.1", - "resolved": "https://registry.npmjs.org/create-require/-/create-require-1.1.1.tgz", - "integrity": "sha512-dcKFX3jn0MpIaXjisoRvexIJVEKzaq7z2rZKxf+MSr9TkdmHmsU4m2lcLojrj/FHl8mk5VxMmYA+ftRkP/3oKQ==", - "dev": true - }, - "node_modules/cross-spawn": { - "version": "7.0.3", - "resolved": "https://registry.npmjs.org/cross-spawn/-/cross-spawn-7.0.3.tgz", - "integrity": "sha512-iRDPJKUPVEND7dHPO8rkbOnPpyDygcDFtWjpeWNCgy8WP2rXcxXL8TskReQl6OrB2G7+UJrags1q15Fudc7G6w==", - "dev": true, - "dependencies": { - "path-key": "^3.1.0", - "shebang-command": "^2.0.0", - "which": "^2.0.1" - }, - "engines": { - "node": ">= 8" - } - }, - "node_modules/crypto-random-string": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/crypto-random-string/-/crypto-random-string-2.0.0.tgz", - "integrity": "sha512-v1plID3y9r/lPhviJ1wrXpLeyUIGAZ2SHNYTEapm7/8A9nLPoyvVp3RK/EPFqn5kEznyWgYZNsRtYYIWbuG8KA==", - "dev": true, - "engines": { - "node": ">=8" - } - }, - "node_modules/debug": { - "version": "4.3.2", - "resolved": "https://registry.npmjs.org/debug/-/debug-4.3.2.tgz", - "integrity": "sha512-mOp8wKcvj7XxC78zLgw/ZA+6TSgkoE2C/ienthhRD298T7UNwAg9diBpLRxC0mOezLl4B0xV7M0cCO6P/O0Xhw==", - "dev": true, - "dependencies": { - "ms": "2.1.2" - }, - "engines": { - "node": ">=6.0" - }, - "peerDependenciesMeta": { - "supports-color": { - "optional": true - } - } - }, - "node_modules/decamelize": { - "version": "1.2.0", - "resolved": "https://registry.npmjs.org/decamelize/-/decamelize-1.2.0.tgz", - "integrity": "sha1-9lNNFRSCabIDUue+4m9QH5oZEpA=", - "dev": true, - "engines": { - "node": ">=0.10.0" - } - }, - "node_modules/decode-uri-component": { - "version": "0.2.0", - "resolved": "https://registry.npmjs.org/decode-uri-component/-/decode-uri-component-0.2.0.tgz", - "integrity": "sha1-6zkTMzRYd1y4TNGh+uBiEGu4dUU=", - "dev": true, - "engines": { - "node": ">=0.10" - } - }, - "node_modules/decompress-response": { - "version": "6.0.0", - "resolved": "https://registry.npmjs.org/decompress-response/-/decompress-response-6.0.0.tgz", - "integrity": "sha512-aW35yZM6Bb/4oJlZncMH2LCoZtJXTRxES17vE3hoRiowU2kWHaJKFkSBDnDR+cm9J+9QhXmREyIfv0pji9ejCQ==", - "dev": true, - "dependencies": { - "mimic-response": "^3.1.0" - }, - "engines": { - "node": ">=10" - }, - "funding": { - "url": "https://github.com/sponsors/sindresorhus" - } - }, - "node_modules/decompress-response/node_modules/mimic-response": { - "version": "3.1.0", - "resolved": "https://registry.npmjs.org/mimic-response/-/mimic-response-3.1.0.tgz", - "integrity": "sha512-z0yWI+4FDrrweS8Zmt4Ej5HdJmky15+L2e6Wgn3+iK5fWzb6T3fhNFq2+MeTRb064c6Wr4N/wv0DzQTjNzHNGQ==", - "dev": true, - "engines": { - "node": ">=10" - }, - "funding": { - "url": "https://github.com/sponsors/sindresorhus" - } - }, - "node_modules/deep-extend": { - "version": "0.6.0", - "resolved": "https://registry.npmjs.org/deep-extend/-/deep-extend-0.6.0.tgz", - "integrity": "sha512-LOHxIOaPYdHlJRtCQfDIVZtfw/ufM8+rVj649RIHzcm/vGwQRXFt6OPqIFWsm2XEMrNIEtWR64sY1LEKD2vAOA==", - "dev": true, - "engines": { - "node": ">=4.0.0" - } - }, - "node_modules/deep-is": { - "version": "0.1.4", - "resolved": "https://registry.npmjs.org/deep-is/-/deep-is-0.1.4.tgz", - "integrity": "sha512-oIPzksmTg4/MriiaYGO+okXDT7ztn/w3Eptv/+gSIdMdKsJo0u4CfYNFJPy+4SKMuCqGw2wxnA+URMg3t8a/bQ==", - "dev": true - }, - "node_modules/default-require-extensions": { - "version": "3.0.0", - "resolved": "https://registry.npmjs.org/default-require-extensions/-/default-require-extensions-3.0.0.tgz", - "integrity": "sha512-ek6DpXq/SCpvjhpFsLFRVtIxJCRw6fUR42lYMVZuUMK7n8eMz4Uh5clckdBjEpLhn/gEBZo7hDJnJcwdKLKQjg==", - "dev": true, - "dependencies": { - "strip-bom": "^4.0.0" - }, - "engines": { - "node": ">=8" - } - }, - "node_modules/defaults": { - "version": "1.0.3", - "resolved": "https://registry.npmjs.org/defaults/-/defaults-1.0.3.tgz", - "integrity": "sha1-xlYFHpgX2f8I7YgUd/P+QBnz730=", - "dev": true, - "dependencies": { - "clone": "^1.0.2" - } - }, - "node_modules/defer-to-connect": { - "version": "2.0.1", - "resolved": "https://registry.npmjs.org/defer-to-connect/-/defer-to-connect-2.0.1.tgz", - "integrity": "sha512-4tvttepXG1VaYGrRibk5EwJd1t4udunSOVMdLSAL6mId1ix438oPwPZMALY41FCijukO1L0twNcGsdzS7dHgDg==", - "dev": true, - "engines": { - "node": ">=10" - } - }, - "node_modules/delayed-stream": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/delayed-stream/-/delayed-stream-1.0.0.tgz", - "integrity": "sha1-3zrhmayt+31ECqrgsp4icrJOxhk=", - "dev": true, - "engines": { - "node": ">=0.4.0" - } - }, - "node_modules/deprecated-obj": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/deprecated-obj/-/deprecated-obj-2.0.0.tgz", - "integrity": "sha512-CkdywZC2rJ8RGh+y3MM1fw1EJ4oO/oNExGbRFv0AQoMS+faTd3nO7slYjkj/6t8OnIMUE+wxh6G97YHhK1ytrw==", - "dev": true, - "dependencies": { - "flat": "^5.0.2", - "lodash": "^4.17.20" - }, - "engines": { - "node": ">=10" - } - }, - "node_modules/deprecation": { - "version": "2.3.1", - "resolved": "https://registry.npmjs.org/deprecation/-/deprecation-2.3.1.tgz", - "integrity": "sha512-xmHIy4F3scKVwMsQ4WnVaS8bHOx0DmVwRywosKhaILI0ywMDWPtBSku2HNxRvF7jtwDRsoEwYQSfbxj8b7RlJQ==", - "dev": true - }, - "node_modules/diff": { - "version": "5.0.0", - "resolved": "https://registry.npmjs.org/diff/-/diff-5.0.0.tgz", - "integrity": "sha512-/VTCrvm5Z0JGty/BWHljh+BAiw3IK+2j87NGMu8Nwc/f48WoDAC395uomO9ZD117ZOBaHmkX1oyLvkVM/aIT3w==", - "dev": true, - "engines": { - "node": ">=0.3.1" - } - }, - "node_modules/dir-glob": { - "version": "3.0.1", - "resolved": "https://registry.npmjs.org/dir-glob/-/dir-glob-3.0.1.tgz", - "integrity": "sha512-WkrWp9GR4KXfKGYzOLmTuGVi1UWFfws377n9cc55/tb6DuqyF6pcQ5AbiHEshaDpY9v6oaSr2XCDidGmMwdzIA==", - "dev": true, - "dependencies": { - "path-type": "^4.0.0" - }, - "engines": { - "node": ">=8" - } - }, - "node_modules/doctrine": { - "version": "3.0.0", - "resolved": "https://registry.npmjs.org/doctrine/-/doctrine-3.0.0.tgz", - "integrity": "sha512-yS+Q5i3hBf7GBkd4KG8a7eBNNWNGLTaEwwYWUijIYM7zrlYDM0BFXHjjPWlWZ1Rg7UaddZeIDmi9jF3HmqiQ2w==", - "dev": true, - "dependencies": { - "esutils": "^2.0.2" - }, - "engines": { - "node": ">=6.0.0" - } - }, - "node_modules/dot-prop": { - "version": "5.3.0", - "resolved": "https://registry.npmjs.org/dot-prop/-/dot-prop-5.3.0.tgz", - "integrity": "sha512-QM8q3zDe58hqUqjraQOmzZ1LIH9SWQJTlEKCH4kJ2oQvLZk7RbQXvtDM2XEq3fwkV9CCvvH4LA0AV+ogFsBM2Q==", - "dev": true, - "dependencies": { - "is-obj": "^2.0.0" - }, - "engines": { - "node": ">=8" - } - }, - "node_modules/duplexer3": { - "version": "0.1.4", - "resolved": "https://registry.npmjs.org/duplexer3/-/duplexer3-0.1.4.tgz", - "integrity": "sha1-7gHdHKwO08vH/b6jfcCo8c4ALOI=", - "dev": true - }, - "node_modules/electron-to-chromium": { - "version": "1.3.891", - "resolved": "https://registry.npmjs.org/electron-to-chromium/-/electron-to-chromium-1.3.891.tgz", - "integrity": "sha512-3cpwR82QkIS01CN/dup/4Yr3BiOiRLlZlcAFn/5FbNCunMO9ojqDgEP9JEo1QNLflu3pEnPWve50gHOEKc7r6w==", - "dev": true - }, - "node_modules/emoji-regex": { - "version": "8.0.0", - "resolved": "https://registry.npmjs.org/emoji-regex/-/emoji-regex-8.0.0.tgz", - "integrity": "sha512-MSjYzcWNOA0ewAHpz0MxpYFvwg6yjy1NG3xteoqz644VCo/RPgnr1/GGt+ic3iJTzQ8Eu3TdM14SawnVUmGE6A==", - "dev": true - }, - "node_modules/end-of-stream": { - "version": "1.4.4", - "resolved": "https://registry.npmjs.org/end-of-stream/-/end-of-stream-1.4.4.tgz", - "integrity": "sha512-+uw1inIHVPQoaVuHzRyXd21icM+cnt4CzD5rW+NC1wjOUSTOs+Te7FOv7AhN7vS9x/oIyhLP5PR1H+phQAHu5Q==", - "dev": true, - "dependencies": { - "once": "^1.4.0" - } - }, - "node_modules/enquirer": { - "version": "2.3.6", - "resolved": "https://registry.npmjs.org/enquirer/-/enquirer-2.3.6.tgz", - "integrity": "sha512-yjNnPr315/FjS4zIsUxYguYUPP2e1NK4d7E7ZOLiyYCcbFBiTMyID+2wvm2w6+pZ/odMA7cRkjhsPbltwBOrLg==", - "dev": true, - "dependencies": { - "ansi-colors": "^4.1.1" - }, - "engines": { - "node": ">=8.6" - } - }, - "node_modules/error-ex": { - "version": "1.3.2", - "resolved": "https://registry.npmjs.org/error-ex/-/error-ex-1.3.2.tgz", - "integrity": "sha512-7dFHNmqeFSEt2ZBsCriorKnn3Z2pj+fd9kmI6QoWw4//DL+icEBfc0U7qJCisqrTsKTjw4fNFy2pW9OqStD84g==", - "dev": true, - "dependencies": { - "is-arrayish": "^0.2.1" - } - }, - "node_modules/es6-error": { - "version": "4.1.1", - "resolved": "https://registry.npmjs.org/es6-error/-/es6-error-4.1.1.tgz", - "integrity": "sha512-Um/+FxMr9CISWh0bi5Zv0iOD+4cFh5qLeks1qhAopKVAJw3drgKbKySikp7wGhDL0HPeaja0P5ULZrxLkniUVg==", - "dev": true - }, - "node_modules/escalade": { - "version": "3.1.1", - "resolved": "https://registry.npmjs.org/escalade/-/escalade-3.1.1.tgz", - "integrity": "sha512-k0er2gUkLf8O0zKJiAhmkTnJlTvINGv7ygDNPbeIsX/TJjGJZHuh9B2UxbsaEkmlEo9MfhrSzmhIlhRlI2GXnw==", - "dev": true, - "engines": { - "node": ">=6" - } - }, - "node_modules/escape-goat": { - "version": "2.1.1", - "resolved": "https://registry.npmjs.org/escape-goat/-/escape-goat-2.1.1.tgz", - "integrity": "sha512-8/uIhbG12Csjy2JEW7D9pHbreaVaS/OpN3ycnyvElTdwM5n6GY6W6e2IPemfvGZeUMqZ9A/3GqIZMgKnBhAw/Q==", - "dev": true, - "engines": { - "node": ">=8" - } - }, - "node_modules/escape-string-regexp": { - "version": "4.0.0", - "resolved": "https://registry.npmjs.org/escape-string-regexp/-/escape-string-regexp-4.0.0.tgz", - "integrity": "sha512-TtpcNJ3XAzx3Gq8sWRzJaVajRs0uVxA2YAkdb1jm2YkPz4G6egUFAyA3n5vtEIZefPk5Wa4UXbKuS5fKkJWdgA==", - "dev": true, - "engines": { - "node": ">=10" - }, - "funding": { - "url": "https://github.com/sponsors/sindresorhus" - } - }, - "node_modules/eslint": { - "version": "8.2.0", - "resolved": "https://registry.npmjs.org/eslint/-/eslint-8.2.0.tgz", - "integrity": "sha512-erw7XmM+CLxTOickrimJ1SiF55jiNlVSp2qqm0NuBWPtHYQCegD5ZMaW0c3i5ytPqL+SSLaCxdvQXFPLJn+ABw==", - "dev": true, - "dependencies": { - "@eslint/eslintrc": "^1.0.4", - "@humanwhocodes/config-array": "^0.6.0", - "ajv": "^6.10.0", - "chalk": "^4.0.0", - "cross-spawn": "^7.0.2", - "debug": "^4.3.2", - "doctrine": "^3.0.0", - "enquirer": "^2.3.5", - "escape-string-regexp": "^4.0.0", - "eslint-scope": "^6.0.0", - "eslint-utils": "^3.0.0", - "eslint-visitor-keys": "^3.0.0", - "espree": "^9.0.0", - "esquery": "^1.4.0", - "esutils": "^2.0.2", - "fast-deep-equal": "^3.1.3", - "file-entry-cache": "^6.0.1", - "functional-red-black-tree": "^1.0.1", - "glob-parent": "^6.0.1", - "globals": "^13.6.0", - "ignore": "^4.0.6", - "import-fresh": "^3.0.0", - "imurmurhash": "^0.1.4", - "is-glob": "^4.0.0", - "js-yaml": "^4.1.0", - "json-stable-stringify-without-jsonify": "^1.0.1", - "levn": "^0.4.1", - "lodash.merge": "^4.6.2", - "minimatch": "^3.0.4", - "natural-compare": "^1.4.0", - "optionator": "^0.9.1", - "progress": "^2.0.0", - "regexpp": "^3.2.0", - "semver": "^7.2.1", - "strip-ansi": "^6.0.1", - "strip-json-comments": "^3.1.0", - "text-table": "^0.2.0", - "v8-compile-cache": "^2.0.3" - }, - "bin": { - "eslint": "bin/eslint.js" - }, - "engines": { - "node": "^12.22.0 || ^14.17.0 || >=16.0.0" - }, - "funding": { - "url": "https://opencollective.com/eslint" - } - }, - "node_modules/eslint-scope": { - "version": "5.1.1", - "resolved": "https://registry.npmjs.org/eslint-scope/-/eslint-scope-5.1.1.tgz", - "integrity": "sha512-2NxwbF/hZ0KpepYN0cNbo+FN6XoK7GaHlQhgx/hIZl6Va0bF45RQOOwhLIy8lQDbuCiadSLCBnH2CFYquit5bw==", - "dev": true, - "dependencies": { - "esrecurse": "^4.3.0", - "estraverse": "^4.1.1" - }, - "engines": { - "node": ">=8.0.0" - } - }, - "node_modules/eslint-utils": { - "version": "3.0.0", - "resolved": "https://registry.npmjs.org/eslint-utils/-/eslint-utils-3.0.0.tgz", - "integrity": "sha512-uuQC43IGctw68pJA1RgbQS8/NP7rch6Cwd4j3ZBtgo4/8Flj4eGE7ZYSZRN3iq5pVUv6GPdW5Z1RFleo84uLDA==", - "dev": true, - "dependencies": { - "eslint-visitor-keys": "^2.0.0" - }, - "engines": { - "node": "^10.0.0 || ^12.0.0 || >= 14.0.0" - }, - "funding": { - "url": "https://github.com/sponsors/mysticatea" - }, - "peerDependencies": { - "eslint": ">=5" - } - }, - "node_modules/eslint-utils/node_modules/eslint-visitor-keys": { - "version": "2.1.0", - "resolved": "https://registry.npmjs.org/eslint-visitor-keys/-/eslint-visitor-keys-2.1.0.tgz", - "integrity": "sha512-0rSmRBzXgDzIsD6mGdJgevzgezI534Cer5L/vyMX0kHzT/jiB43jRhd9YUlMGYLQy2zprNmoT8qasCGtY+QaKw==", - "dev": true, - "engines": { - "node": ">=10" - } - }, - "node_modules/eslint-visitor-keys": { - "version": "3.1.0", - "resolved": "https://registry.npmjs.org/eslint-visitor-keys/-/eslint-visitor-keys-3.1.0.tgz", - "integrity": "sha512-yWJFpu4DtjsWKkt5GeNBBuZMlNcYVs6vRCLoCVEJrTjaSB6LC98gFipNK/erM2Heg/E8mIK+hXG/pJMLK+eRZA==", - "dev": true, - "engines": { - "node": "^12.22.0 || ^14.17.0 || >=16.0.0" - } - }, - "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==", - "dev": true, - "dependencies": { - "esrecurse": "^4.3.0", - "estraverse": "^5.2.0" - }, - "engines": { - "node": "^12.22.0 || ^14.17.0 || >=16.0.0" - } - }, - "node_modules/eslint/node_modules/estraverse": { - "version": "5.3.0", - "resolved": "https://registry.npmjs.org/estraverse/-/estraverse-5.3.0.tgz", - "integrity": "sha512-MMdARuVEQziNTeJD8DgMqmhwR11BRQ/cBP+pLtYdSTnf3MIO8fFeiINEbX36ZdNlfU/7A9f3gUw49B3oQsvwBA==", - "dev": true, - "engines": { - "node": ">=4.0" - } - }, - "node_modules/eslint/node_modules/ignore": { - "version": "4.0.6", - "resolved": "https://registry.npmjs.org/ignore/-/ignore-4.0.6.tgz", - "integrity": "sha512-cyFDKrqc/YdcWFniJhzI42+AzS+gNwmUzOSFcRCQYwySuBBBy/KjuxWLZ/FHEH6Moq1NizMOBWyTcv8O4OZIMg==", - "dev": true, - "engines": { - "node": ">= 4" - } - }, - "node_modules/espree": { - "version": "9.0.0", - "resolved": "https://registry.npmjs.org/espree/-/espree-9.0.0.tgz", - "integrity": "sha512-r5EQJcYZ2oaGbeR0jR0fFVijGOcwai07/690YRXLINuhmVeRY4UKSAsQPe/0BNuDgwP7Ophoc1PRsr2E3tkbdQ==", - "dev": true, - "dependencies": { - "acorn": "^8.5.0", - "acorn-jsx": "^5.3.1", - "eslint-visitor-keys": "^3.0.0" - }, - "engines": { - "node": "^12.22.0 || ^14.17.0 || >=16.0.0" - } - }, - "node_modules/esprima": { - "version": "4.0.1", - "resolved": "https://registry.npmjs.org/esprima/-/esprima-4.0.1.tgz", - "integrity": "sha512-eGuFFw7Upda+g4p+QHvnW0RyTX/SVeJBDM/gCtMARO0cLuT2HcEKnTPvhjV6aGeqrCB/sbNop0Kszm0jsaWU4A==", - "dev": true, - "bin": { - "esparse": "bin/esparse.js", - "esvalidate": "bin/esvalidate.js" - }, - "engines": { - "node": ">=4" - } - }, - "node_modules/esquery": { - "version": "1.4.0", - "resolved": "https://registry.npmjs.org/esquery/-/esquery-1.4.0.tgz", - "integrity": "sha512-cCDispWt5vHHtwMY2YrAQ4ibFkAL8RbH5YGBnZBc90MolvvfkkQcJro/aZiAQUlQ3qgrYS6D6v8Gc5G5CQsc9w==", - "dev": true, - "dependencies": { - "estraverse": "^5.1.0" - }, - "engines": { - "node": ">=0.10" - } - }, - "node_modules/esquery/node_modules/estraverse": { - "version": "5.3.0", - "resolved": "https://registry.npmjs.org/estraverse/-/estraverse-5.3.0.tgz", - "integrity": "sha512-MMdARuVEQziNTeJD8DgMqmhwR11BRQ/cBP+pLtYdSTnf3MIO8fFeiINEbX36ZdNlfU/7A9f3gUw49B3oQsvwBA==", - "dev": true, - "engines": { - "node": ">=4.0" - } - }, - "node_modules/esrecurse": { - "version": "4.3.0", - "resolved": "https://registry.npmjs.org/esrecurse/-/esrecurse-4.3.0.tgz", - "integrity": "sha512-KmfKL3b6G+RXvP8N1vr3Tq1kL/oCFgn2NYXEtqP8/L3pKapUA4G8cFVaoF3SU323CD4XypR/ffioHmkti6/Tag==", - "dev": true, - "dependencies": { - "estraverse": "^5.2.0" - }, - "engines": { - "node": ">=4.0" - } - }, - "node_modules/esrecurse/node_modules/estraverse": { - "version": "5.3.0", - "resolved": "https://registry.npmjs.org/estraverse/-/estraverse-5.3.0.tgz", - "integrity": "sha512-MMdARuVEQziNTeJD8DgMqmhwR11BRQ/cBP+pLtYdSTnf3MIO8fFeiINEbX36ZdNlfU/7A9f3gUw49B3oQsvwBA==", - "dev": true, - "engines": { - "node": ">=4.0" - } - }, - "node_modules/estraverse": { - "version": "4.3.0", - "resolved": "https://registry.npmjs.org/estraverse/-/estraverse-4.3.0.tgz", - "integrity": "sha512-39nnKffWz8xN1BU/2c79n9nB9HDzo0niYUqx6xyqUnyoAnQyyWpOTdZEeiCch8BBu515t4wp9ZmgVfVhn9EBpw==", - "dev": true, - "engines": { - "node": ">=4.0" - } - }, - "node_modules/esutils": { - "version": "2.0.3", - "resolved": "https://registry.npmjs.org/esutils/-/esutils-2.0.3.tgz", - "integrity": "sha512-kVscqXk4OCp68SZ0dkgEKVi6/8ij300KBWTJq32P/dYeWTSwK41WyTxalN1eRmA5Z9UU/LX9D7FWSmV9SAYx6g==", - "dev": true, - "engines": { - "node": ">=0.10.0" - } - }, - "node_modules/execa": { - "version": "5.1.1", - "resolved": "https://registry.npmjs.org/execa/-/execa-5.1.1.tgz", - "integrity": "sha512-8uSpZZocAZRBAPIEINJj3Lo9HyGitllczc27Eh5YYojjMFMn8yHMDMaUHE2Jqfq05D/wucwI4JGURyXt1vchyg==", - "dev": true, - "dependencies": { - "cross-spawn": "^7.0.3", - "get-stream": "^6.0.0", - "human-signals": "^2.1.0", - "is-stream": "^2.0.0", - "merge-stream": "^2.0.0", - "npm-run-path": "^4.0.1", - "onetime": "^5.1.2", - "signal-exit": "^3.0.3", - "strip-final-newline": "^2.0.0" - }, - "engines": { - "node": ">=10" - }, - "funding": { - "url": "https://github.com/sindresorhus/execa?sponsor=1" - } - }, - "node_modules/external-editor": { - "version": "3.1.0", - "resolved": "https://registry.npmjs.org/external-editor/-/external-editor-3.1.0.tgz", - "integrity": "sha512-hMQ4CX1p1izmuLYyZqLMO/qGNw10wSv9QDCPfzXfyFrOaCSSoRfqE1Kf1s5an66J5JZC62NewG+mK49jOCtQew==", - "dev": true, - "dependencies": { - "chardet": "^0.7.0", - "iconv-lite": "^0.4.24", - "tmp": "^0.0.33" - }, - "engines": { - "node": ">=4" - } - }, - "node_modules/fast-deep-equal": { - "version": "3.1.3", - "resolved": "https://registry.npmjs.org/fast-deep-equal/-/fast-deep-equal-3.1.3.tgz", - "integrity": "sha512-f3qQ9oQy9j2AhBe/H9VC91wLmKBCCU/gDOnKNAYG5hswO7BLKj09Hc5HYNz9cGI++xlpDCIgDaitVs03ATR84Q==", - "dev": true - }, - "node_modules/fast-glob": { - "version": "3.2.7", - "resolved": "https://registry.npmjs.org/fast-glob/-/fast-glob-3.2.7.tgz", - "integrity": "sha512-rYGMRwip6lUMvYD3BTScMwT1HtAs2d71SMv66Vrxs0IekGZEjhM0pcMfjQPnknBt2zeCwQMEupiN02ZP4DiT1Q==", - "dev": true, - "dependencies": { - "@nodelib/fs.stat": "^2.0.2", - "@nodelib/fs.walk": "^1.2.3", - "glob-parent": "^5.1.2", - "merge2": "^1.3.0", - "micromatch": "^4.0.4" - }, - "engines": { - "node": ">=8" - } - }, - "node_modules/fast-glob/node_modules/glob-parent": { - "version": "5.1.2", - "resolved": "https://registry.npmjs.org/glob-parent/-/glob-parent-5.1.2.tgz", - "integrity": "sha512-AOIgSQCepiJYwP3ARnGx+5VnTu2HBYdzbGP45eLw1vr3zB3vZLeyed1sC9hnbcOc9/SrMyM5RPQrkGz4aS9Zow==", - "dev": true, - "dependencies": { - "is-glob": "^4.0.1" - }, - "engines": { - "node": ">= 6" - } - }, - "node_modules/fast-json-stable-stringify": { - "version": "2.1.0", - "resolved": "https://registry.npmjs.org/fast-json-stable-stringify/-/fast-json-stable-stringify-2.1.0.tgz", - "integrity": "sha512-lhd/wF+Lk98HZoTCtlVraHtfh5XYijIjalXck7saUtuanSDyLMxnHhSXEDJqHxD7msR8D0uCmqlkwjCV8xvwHw==", - "dev": true - }, - "node_modules/fast-levenshtein": { - "version": "2.0.6", - "resolved": "https://registry.npmjs.org/fast-levenshtein/-/fast-levenshtein-2.0.6.tgz", - "integrity": "sha1-PYpcZog6FqMMqGQ+hR8Zuqd5eRc=", - "dev": true - }, - "node_modules/fastq": { - "version": "1.13.0", - "resolved": "https://registry.npmjs.org/fastq/-/fastq-1.13.0.tgz", - "integrity": "sha512-YpkpUnK8od0o1hmeSc7UUs/eB/vIPWJYjKck2QKIzAf71Vm1AAQ3EbuZB3g2JIy+pg+ERD0vqI79KyZiB2e2Nw==", - "dev": true, - "dependencies": { - "reusify": "^1.0.4" - } - }, - "node_modules/figures": { - "version": "3.2.0", - "resolved": "https://registry.npmjs.org/figures/-/figures-3.2.0.tgz", - "integrity": "sha512-yaduQFRKLXYOGgEn6AZau90j3ggSOyiqXU0F9JZfeXYhNa+Jk4X+s45A2zg5jns87GAFa34BBm2kXw4XpNcbdg==", - "dev": true, - "dependencies": { - "escape-string-regexp": "^1.0.5" - }, - "engines": { - "node": ">=8" - }, - "funding": { - "url": "https://github.com/sponsors/sindresorhus" - } - }, - "node_modules/figures/node_modules/escape-string-regexp": { - "version": "1.0.5", - "resolved": "https://registry.npmjs.org/escape-string-regexp/-/escape-string-regexp-1.0.5.tgz", - "integrity": "sha1-G2HAViGQqN/2rjuyzwIAyhMLhtQ=", - "dev": true, - "engines": { - "node": ">=0.8.0" - } - }, - "node_modules/file-entry-cache": { - "version": "6.0.1", - "resolved": "https://registry.npmjs.org/file-entry-cache/-/file-entry-cache-6.0.1.tgz", - "integrity": "sha512-7Gps/XWymbLk2QLYK4NzpMOrYjMhdIxXuIvy2QBsLE6ljuodKvdkWs/cpyJJ3CVIVpH0Oi1Hvg1ovbMzLdFBBg==", - "dev": true, - "dependencies": { - "flat-cache": "^3.0.4" - }, - "engines": { - "node": "^10.12.0 || >=12.0.0" - } - }, - "node_modules/fill-range": { - "version": "7.0.1", - "resolved": "https://registry.npmjs.org/fill-range/-/fill-range-7.0.1.tgz", - "integrity": "sha512-qOo9F+dMUmC2Lcb4BbVvnKJxTPjCm+RRpe4gDuGrzkL7mEVl/djYSu2OdQ2Pa302N4oqkSg9ir6jaLWJ2USVpQ==", - "dev": true, - "dependencies": { - "to-regex-range": "^5.0.1" - }, - "engines": { - "node": ">=8" - } - }, - "node_modules/filter-obj": { - "version": "1.1.0", - "resolved": "https://registry.npmjs.org/filter-obj/-/filter-obj-1.1.0.tgz", - "integrity": "sha1-mzERErxsYSehbgFsbF1/GeCAXFs=", - "dev": true, - "engines": { - "node": ">=0.10.0" - } - }, - "node_modules/find-cache-dir": { - "version": "3.3.2", - "resolved": "https://registry.npmjs.org/find-cache-dir/-/find-cache-dir-3.3.2.tgz", - "integrity": "sha512-wXZV5emFEjrridIgED11OoUKLxiYjAcqot/NJdAkOhlJ+vGzwhOAfcG5OX1jP+S0PcjEn8bdMJv+g2jwQ3Onig==", - "dev": true, - "dependencies": { - "commondir": "^1.0.1", - "make-dir": "^3.0.2", - "pkg-dir": "^4.1.0" - }, - "engines": { - "node": ">=8" - }, - "funding": { - "url": "https://github.com/avajs/find-cache-dir?sponsor=1" - } - }, - "node_modules/find-up": { - "version": "5.0.0", - "resolved": "https://registry.npmjs.org/find-up/-/find-up-5.0.0.tgz", - "integrity": "sha512-78/PXT1wlLLDgTzDs7sjq9hzz0vXD+zn+7wypEe4fXQxCmdmqfGsEPQxmiCSQI3ajFV91bVSsvNtrJRiW6nGng==", - "dev": true, - "dependencies": { - "locate-path": "^6.0.0", - "path-exists": "^4.0.0" - }, - "engines": { - "node": ">=10" - }, - "funding": { - "url": "https://github.com/sponsors/sindresorhus" - } - }, - "node_modules/flat": { - "version": "5.0.2", - "resolved": "https://registry.npmjs.org/flat/-/flat-5.0.2.tgz", - "integrity": "sha512-b6suED+5/3rTpUBdG1gupIl8MPFCAMA0QXwmljLhvCUKcUvdE4gWky9zpuGCcXHOsz4J9wPGNWq6OKpmIzz3hQ==", - "dev": true, - "bin": { - "flat": "cli.js" - } - }, - "node_modules/flat-cache": { - "version": "3.0.4", - "resolved": "https://registry.npmjs.org/flat-cache/-/flat-cache-3.0.4.tgz", - "integrity": "sha512-dm9s5Pw7Jc0GvMYbshN6zchCA9RgQlzzEZX3vylR9IqFfS8XciblUXOKfW6SiuJ0e13eDYZoZV5wdrev7P3Nwg==", - "dev": true, - "dependencies": { - "flatted": "^3.1.0", - "rimraf": "^3.0.2" - }, - "engines": { - "node": "^10.12.0 || >=12.0.0" - } - }, - "node_modules/flatted": { - "version": "3.2.2", - "resolved": "https://registry.npmjs.org/flatted/-/flatted-3.2.2.tgz", - "integrity": "sha512-JaTY/wtrcSyvXJl4IMFHPKyFur1sE9AUqc0QnhOaJ0CxHtAoIV8pYDzeEfAaNEtGkOfq4gr3LBFmdXW5mOQFnA==", - "dev": true - }, - "node_modules/foreground-child": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/foreground-child/-/foreground-child-2.0.0.tgz", - "integrity": "sha512-dCIq9FpEcyQyXKCkyzmlPTFNgrCzPudOe+mhvJU5zAtlBnGVy2yKxtfsxK2tQBThwq225jcvBjpw1Gr40uzZCA==", - "dev": true, - "dependencies": { - "cross-spawn": "^7.0.0", - "signal-exit": "^3.0.2" - }, - "engines": { - "node": ">=8.0.0" - } - }, - "node_modules/form-data": { - "version": "4.0.0", - "resolved": "https://registry.npmjs.org/form-data/-/form-data-4.0.0.tgz", - "integrity": "sha512-ETEklSGi5t0QMZuiXoA/Q6vcnxcLQP5vdugSpuAyi6SVGi2clPPp+xgEhuMaHC+zGgn31Kd235W35f7Hykkaww==", - "dev": true, - "dependencies": { - "asynckit": "^0.4.0", - "combined-stream": "^1.0.8", - "mime-types": "^2.1.12" - }, - "engines": { - "node": ">= 6" - } - }, - "node_modules/fromentries": { - "version": "1.3.2", - "resolved": "https://registry.npmjs.org/fromentries/-/fromentries-1.3.2.tgz", - "integrity": "sha512-cHEpEQHUg0f8XdtZCc2ZAhrHzKzT0MrFUTcvx+hfxYu7rGMDc5SKoXFh+n4YigxsHXRzc6OrCshdR1bWH6HHyg==", - "dev": true, - "funding": [ - { - "type": "github", - "url": "https://github.com/sponsors/feross" - }, - { - "type": "patreon", - "url": "https://www.patreon.com/feross" - }, - { - "type": "consulting", - "url": "https://feross.org/support" - } - ] - }, - "node_modules/fs.realpath": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/fs.realpath/-/fs.realpath-1.0.0.tgz", - "integrity": "sha1-FQStJSMVjKpA20onh8sBQRmU6k8=", - "dev": true - }, - "node_modules/fsevents": { - "version": "2.3.2", - "resolved": "https://registry.npmjs.org/fsevents/-/fsevents-2.3.2.tgz", - "integrity": "sha512-xiqMQR4xAeHTuB9uWm+fFRcIOgKBMiOBP+eXiyT7jsgVCq1bkVygt00oASowB7EdtpOHaaPgKt812P9ab+DDKA==", - "dev": true, - "hasInstallScript": true, - "optional": true, - "os": [ - "darwin" - ], - "engines": { - "node": "^8.16.0 || ^10.6.0 || >=11.0.0" - } - }, - "node_modules/function-bind": { - "version": "1.1.1", - "resolved": "https://registry.npmjs.org/function-bind/-/function-bind-1.1.1.tgz", - "integrity": "sha512-yIovAzMX49sF8Yl58fSCWJ5svSLuaibPxXQJFLmBObTuCr0Mf1KiPopGM9NiFjiYBCbfaa2Fh6breQ6ANVTI0A==", - "dev": true - }, - "node_modules/functional-red-black-tree": { - "version": "1.0.1", - "resolved": "https://registry.npmjs.org/functional-red-black-tree/-/functional-red-black-tree-1.0.1.tgz", - "integrity": "sha1-GwqzvVU7Kg1jmdKcDj6gslIHgyc=", - "dev": true - }, - "node_modules/generic-pool": { - "version": "3.8.2", - "resolved": "https://registry.npmjs.org/generic-pool/-/generic-pool-3.8.2.tgz", - "integrity": "sha512-nGToKy6p3PAbYQ7p1UlWl6vSPwfwU6TMSWK7TTu+WUY4ZjyZQGniGGt2oNVvyNSpyZYSB43zMXVLcBm08MTMkg==", - "engines": { - "node": ">= 4" - } - }, - "node_modules/gensync": { - "version": "1.0.0-beta.2", - "resolved": "https://registry.npmjs.org/gensync/-/gensync-1.0.0-beta.2.tgz", - "integrity": "sha512-3hN7NaskYvMDLQY55gnW3NQ+mesEAepTqlg+VEbj7zzqEMBVNhzcGYYeqFo/TlYz6eQiFcp1HcsCZO+nGgS8zg==", - "dev": true, - "engines": { - "node": ">=6.9.0" - } - }, - "node_modules/get-caller-file": { - "version": "2.0.5", - "resolved": "https://registry.npmjs.org/get-caller-file/-/get-caller-file-2.0.5.tgz", - "integrity": "sha512-DyFP3BM/3YHTQOCUL/w0OZHR0lpKeGrxotcHWcqNEdnltqFwXVfhEBQ94eIo34AfQpo0rGki4cyIiftY06h2Fg==", - "dev": true, - "engines": { - "node": "6.* || 8.* || >= 10.*" - } - }, - "node_modules/get-intrinsic": { - "version": "1.1.1", - "resolved": "https://registry.npmjs.org/get-intrinsic/-/get-intrinsic-1.1.1.tgz", - "integrity": "sha512-kWZrnVM42QCiEA2Ig1bG8zjoIMOgxWwYCEeNdwY6Tv/cOSeGpcoX4pXHfKUxNKVoArnrEr2e9srnAxxGIraS9Q==", - "dev": true, - "dependencies": { - "function-bind": "^1.1.1", - "has": "^1.0.3", - "has-symbols": "^1.0.1" - }, - "funding": { - "url": "https://github.com/sponsors/ljharb" - } - }, - "node_modules/get-package-type": { - "version": "0.1.0", - "resolved": "https://registry.npmjs.org/get-package-type/-/get-package-type-0.1.0.tgz", - "integrity": "sha512-pjzuKtY64GYfWizNAJ0fr9VqttZkNiK2iS430LtIHzjBEr6bX8Am2zm4sW4Ro5wjWW5cAlRL1qAMTcXbjNAO2Q==", - "dev": true, - "engines": { - "node": ">=8.0.0" - } - }, - "node_modules/get-stream": { - "version": "6.0.1", - "resolved": "https://registry.npmjs.org/get-stream/-/get-stream-6.0.1.tgz", - "integrity": "sha512-ts6Wi+2j3jQjqi70w5AlN8DFnkSwC+MqmxEzdEALB2qXZYV3X/b1CTfgPLGJNMeAWxdPfU8FO1ms3NUfaHCPYg==", - "dev": true, - "engines": { - "node": ">=10" - }, - "funding": { - "url": "https://github.com/sponsors/sindresorhus" - } - }, - "node_modules/git-up": { - "version": "4.0.5", - "resolved": "https://registry.npmjs.org/git-up/-/git-up-4.0.5.tgz", - "integrity": "sha512-YUvVDg/vX3d0syBsk/CKUTib0srcQME0JyHkL5BaYdwLsiCslPWmDSi8PUMo9pXYjrryMcmsCoCgsTpSCJEQaA==", - "dev": true, - "dependencies": { - "is-ssh": "^1.3.0", - "parse-url": "^6.0.0" - } - }, - "node_modules/git-url-parse": { - "version": "11.6.0", - "resolved": "https://registry.npmjs.org/git-url-parse/-/git-url-parse-11.6.0.tgz", - "integrity": "sha512-WWUxvJs5HsyHL6L08wOusa/IXYtMuCAhrMmnTjQPpBU0TTHyDhnOATNH3xNQz7YOQUsqIIPTGr4xiVti1Hsk5g==", - "dev": true, - "dependencies": { - "git-up": "^4.0.0" - } - }, - "node_modules/glob": { - "version": "7.1.7", - "resolved": "https://registry.npmjs.org/glob/-/glob-7.1.7.tgz", - "integrity": "sha512-OvD9ENzPLbegENnYP5UUfJIirTg4+XwMWGaQfQTY0JenxNvvIKP3U3/tAQSPIu/lHxXYSZmpXlUHeqAIdKzBLQ==", - "dev": true, - "dependencies": { - "fs.realpath": "^1.0.0", - "inflight": "^1.0.4", - "inherits": "2", - "minimatch": "^3.0.4", - "once": "^1.3.0", - "path-is-absolute": "^1.0.0" - }, - "engines": { - "node": "*" - }, - "funding": { - "url": "https://github.com/sponsors/isaacs" - } - }, - "node_modules/glob-parent": { - "version": "6.0.2", - "resolved": "https://registry.npmjs.org/glob-parent/-/glob-parent-6.0.2.tgz", - "integrity": "sha512-XxwI8EOhVQgWp6iDL+3b0r86f4d6AX6zSU55HfB4ydCEuXLXc5FcYeOu+nnGftS4TEju/11rt4KJPTMgbfmv4A==", - "dev": true, - "dependencies": { - "is-glob": "^4.0.3" - }, - "engines": { - "node": ">=10.13.0" - } - }, - "node_modules/global-dirs": { - "version": "3.0.0", - "resolved": "https://registry.npmjs.org/global-dirs/-/global-dirs-3.0.0.tgz", - "integrity": "sha512-v8ho2DS5RiCjftj1nD9NmnfaOzTdud7RRnVd9kFNOjqZbISlx5DQ+OrTkywgd0dIt7oFCvKetZSHoHcP3sDdiA==", - "dev": true, - "dependencies": { - "ini": "2.0.0" - }, - "engines": { - "node": ">=10" - }, - "funding": { - "url": "https://github.com/sponsors/sindresorhus" - } - }, - "node_modules/globals": { - "version": "13.12.0", - "resolved": "https://registry.npmjs.org/globals/-/globals-13.12.0.tgz", - "integrity": "sha512-uS8X6lSKN2JumVoXrbUz+uG4BYG+eiawqm3qFcT7ammfbUHeCBoJMlHcec/S3krSk73/AE/f0szYFmgAA3kYZg==", - "dev": true, - "dependencies": { - "type-fest": "^0.20.2" - }, - "engines": { - "node": ">=8" - }, - "funding": { - "url": "https://github.com/sponsors/sindresorhus" - } - }, - "node_modules/globby": { - "version": "11.0.4", - "resolved": "https://registry.npmjs.org/globby/-/globby-11.0.4.tgz", - "integrity": "sha512-9O4MVG9ioZJ08ffbcyVYyLOJLk5JQ688pJ4eMGLpdWLHq/Wr1D9BlriLQyL0E+jbkuePVZXYFj47QM/v093wHg==", - "dev": true, - "dependencies": { - "array-union": "^2.1.0", - "dir-glob": "^3.0.1", - "fast-glob": "^3.1.1", - "ignore": "^5.1.4", - "merge2": "^1.3.0", - "slash": "^3.0.0" - }, - "engines": { - "node": ">=10" - }, - "funding": { - "url": "https://github.com/sponsors/sindresorhus" - } - }, - "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==", - "dev": true, - "dependencies": { - "@sindresorhus/is": "^4.0.0", - "@szmarczak/http-timer": "^4.0.5", - "@types/cacheable-request": "^6.0.1", - "@types/responselike": "^1.0.0", - "cacheable-lookup": "^5.0.3", - "cacheable-request": "^7.0.1", - "decompress-response": "^6.0.0", - "http2-wrapper": "^1.0.0-beta.5.2", - "lowercase-keys": "^2.0.0", - "p-cancelable": "^2.0.0", - "responselike": "^2.0.0" - }, - "engines": { - "node": ">=10.19.0" - }, - "funding": { - "url": "https://github.com/sindresorhus/got?sponsor=1" - } - }, - "node_modules/graceful-fs": { - "version": "4.2.8", - "resolved": "https://registry.npmjs.org/graceful-fs/-/graceful-fs-4.2.8.tgz", - "integrity": "sha512-qkIilPUYcNhJpd33n0GBXTB1MMPp14TxEsEs0pTrsSVucApsYzW5V+Q8Qxhik6KU3evy+qkAAowTByymK0avdg==", - "dev": true - }, - "node_modules/growl": { - "version": "1.10.5", - "resolved": "https://registry.npmjs.org/growl/-/growl-1.10.5.tgz", - "integrity": "sha512-qBr4OuELkhPenW6goKVXiv47US3clb3/IbuWF9KNKEijAy9oeHxU9IgzjvJhHkUzhaj7rOUD7+YGWqUjLp5oSA==", - "dev": true, - "engines": { - "node": ">=4.x" - } - }, - "node_modules/handlebars": { - "version": "4.7.7", - "resolved": "https://registry.npmjs.org/handlebars/-/handlebars-4.7.7.tgz", - "integrity": "sha512-aAcXm5OAfE/8IXkcZvCepKU3VzW1/39Fb5ZuqMtgI/hT8X2YgoMvBY5dLhq/cpOvw7Lk1nK/UF71aLG/ZnVYRA==", - "dev": true, - "dependencies": { - "minimist": "^1.2.5", - "neo-async": "^2.6.0", - "source-map": "^0.6.1", - "wordwrap": "^1.0.0" - }, - "bin": { - "handlebars": "bin/handlebars" - }, - "engines": { - "node": ">=0.4.7" - }, - "optionalDependencies": { - "uglify-js": "^3.1.4" - } - }, - "node_modules/handlebars/node_modules/source-map": { - "version": "0.6.1", - "resolved": "https://registry.npmjs.org/source-map/-/source-map-0.6.1.tgz", - "integrity": "sha512-UjgapumWlbMhkBgzT7Ykc5YXUT46F0iKu8SGXq0bcwP5dz/h0Plj6enJqjz1Zbq2l5WaqYnrVbwWOWMyF3F47g==", - "dev": true, - "engines": { - "node": ">=0.10.0" - } - }, - "node_modules/has": { - "version": "1.0.3", - "resolved": "https://registry.npmjs.org/has/-/has-1.0.3.tgz", - "integrity": "sha512-f2dvO0VU6Oej7RkWJGrehjbzMAjFp5/VKPp5tTpWIV4JHHZK1/BxbFRtf/siA2SWTe09caDmVtYYzWEIbBS4zw==", - "dev": true, - "dependencies": { - "function-bind": "^1.1.1" - }, - "engines": { - "node": ">= 0.4.0" - } - }, - "node_modules/has-flag": { - "version": "4.0.0", - "resolved": "https://registry.npmjs.org/has-flag/-/has-flag-4.0.0.tgz", - "integrity": "sha512-EykJT/Q1KjTWctppgIAgfSO0tKVuZUjhgMr17kqTumMl6Afv3EISleU7qZUzoXDFTAHTDC4NOoG/ZxU3EvlMPQ==", - "dev": true, - "engines": { - "node": ">=8" - } - }, - "node_modules/has-symbols": { - "version": "1.0.2", - "resolved": "https://registry.npmjs.org/has-symbols/-/has-symbols-1.0.2.tgz", - "integrity": "sha512-chXa79rL/UC2KlX17jo3vRGz0azaWEx5tGqZg5pO3NUyEJVB17dMruQlzCCOfUvElghKcm5194+BCRvi2Rv/Gw==", - "dev": true, - "engines": { - "node": ">= 0.4" - }, - "funding": { - "url": "https://github.com/sponsors/ljharb" - } - }, - "node_modules/has-yarn": { - "version": "2.1.0", - "resolved": "https://registry.npmjs.org/has-yarn/-/has-yarn-2.1.0.tgz", - "integrity": "sha512-UqBRqi4ju7T+TqGNdqAO0PaSVGsDGJUBQvk9eUWNGRY1CFGDzYhLWoM7JQEemnlvVcv/YEmc2wNW8BC24EnUsw==", - "dev": true, - "engines": { - "node": ">=8" - } - }, - "node_modules/hasha": { - "version": "5.2.2", - "resolved": "https://registry.npmjs.org/hasha/-/hasha-5.2.2.tgz", - "integrity": "sha512-Hrp5vIK/xr5SkeN2onO32H0MgNZ0f17HRNH39WfL0SYUNOTZ5Lz1TJ8Pajo/87dYGEFlLMm7mIc/k/s6Bvz9HQ==", - "dev": true, - "dependencies": { - "is-stream": "^2.0.0", - "type-fest": "^0.8.0" - }, - "engines": { - "node": ">=8" - }, - "funding": { - "url": "https://github.com/sponsors/sindresorhus" - } - }, - "node_modules/hasha/node_modules/type-fest": { - "version": "0.8.1", - "resolved": "https://registry.npmjs.org/type-fest/-/type-fest-0.8.1.tgz", - "integrity": "sha512-4dbzIzqvjtgiM5rw1k5rEHtBANKmdudhGyBEajN01fEyhaAIhsoKNy6y7+IN93IfpFtwY9iqi7kD+xwKhQsNJA==", - "dev": true, - "engines": { - "node": ">=8" - } - }, - "node_modules/he": { - "version": "1.2.0", - "resolved": "https://registry.npmjs.org/he/-/he-1.2.0.tgz", - "integrity": "sha512-F/1DnUGPopORZi0ni+CvrCgHQ5FyEAHRLSApuYWMmrbSwoN2Mn/7k+Gl38gJnR7yyDZk6WLXwiGod1JOWNDKGw==", - "dev": true, - "bin": { - "he": "bin/he" - } - }, - "node_modules/html-escaper": { - "version": "2.0.2", - "resolved": "https://registry.npmjs.org/html-escaper/-/html-escaper-2.0.2.tgz", - "integrity": "sha512-H2iMtd0I4Mt5eYiapRdIDjp+XzelXQ0tFE4JS7YFwFevXXMmOp9myNrUvCg0D6ws8iqkRPBfKHgbwig1SmlLfg==", - "dev": true - }, - "node_modules/http-cache-semantics": { - "version": "4.1.0", - "resolved": "https://registry.npmjs.org/http-cache-semantics/-/http-cache-semantics-4.1.0.tgz", - "integrity": "sha512-carPklcUh7ROWRK7Cv27RPtdhYhUsela/ue5/jKzjegVvXDqM2ILE9Q2BGn9JZJh1g87cp56su/FgQSzcWS8cQ==", - "dev": true - }, - "node_modules/http2-wrapper": { - "version": "1.0.3", - "resolved": "https://registry.npmjs.org/http2-wrapper/-/http2-wrapper-1.0.3.tgz", - "integrity": "sha512-V+23sDMr12Wnz7iTcDeJr3O6AIxlnvT/bmaAAAP/Xda35C90p9599p0F1eHR/N1KILWSoWVAiOMFjBBXaXSMxg==", - "dev": true, - "dependencies": { - "quick-lru": "^5.1.1", - "resolve-alpn": "^1.0.0" - }, - "engines": { - "node": ">=10.19.0" - } - }, - "node_modules/human-signals": { - "version": "2.1.0", - "resolved": "https://registry.npmjs.org/human-signals/-/human-signals-2.1.0.tgz", - "integrity": "sha512-B4FFZ6q/T2jhhksgkbEW3HBvWIfDW85snkQgawt07S7J5QXTk6BkNV+0yAeZrM5QpMAdYlocGoljn0sJ/WQkFw==", - "dev": true, - "engines": { - "node": ">=10.17.0" - } - }, - "node_modules/iconv-lite": { - "version": "0.4.24", - "resolved": "https://registry.npmjs.org/iconv-lite/-/iconv-lite-0.4.24.tgz", - "integrity": "sha512-v3MXnZAcvnywkTUEZomIActle7RXXeedOR31wwl7VlyoXO4Qi9arvSenNQWne1TcRwhCL1HwLI21bEqdpj8/rA==", - "dev": true, - "dependencies": { - "safer-buffer": ">= 2.1.2 < 3" - }, - "engines": { - "node": ">=0.10.0" - } - }, - "node_modules/ieee754": { - "version": "1.2.1", - "resolved": "https://registry.npmjs.org/ieee754/-/ieee754-1.2.1.tgz", - "integrity": "sha512-dcyqhDvX1C46lXZcVqCpK+FtMRQVdIMN6/Df5js2zouUsqG7I6sFxitIC+7KYK29KdXOLHdu9zL4sFnoVQnqaA==", - "dev": true, - "funding": [ - { - "type": "github", - "url": "https://github.com/sponsors/feross" - }, - { - "type": "patreon", - "url": "https://www.patreon.com/feross" - }, - { - "type": "consulting", - "url": "https://feross.org/support" - } - ] - }, - "node_modules/ignore": { - "version": "5.1.9", - "resolved": "https://registry.npmjs.org/ignore/-/ignore-5.1.9.tgz", - "integrity": "sha512-2zeMQpbKz5dhZ9IwL0gbxSW5w0NK/MSAMtNuhgIHEPmaU3vPdKPL0UdvUCXs5SS4JAwsBxysK5sFMW8ocFiVjQ==", - "dev": true, - "engines": { - "node": ">= 4" - } - }, - "node_modules/import-cwd": { - "version": "3.0.0", - "resolved": "https://registry.npmjs.org/import-cwd/-/import-cwd-3.0.0.tgz", - "integrity": "sha512-4pnzH16plW+hgvRECbDWpQl3cqtvSofHWh44met7ESfZ8UZOWWddm8hEyDTqREJ9RbYHY8gi8DqmaelApoOGMg==", - "dev": true, - "dependencies": { - "import-from": "^3.0.0" - }, - "engines": { - "node": ">=8" - } - }, - "node_modules/import-fresh": { - "version": "3.3.0", - "resolved": "https://registry.npmjs.org/import-fresh/-/import-fresh-3.3.0.tgz", - "integrity": "sha512-veYYhQa+D1QBKznvhUHxb8faxlrwUnxseDAbAp457E0wLNio2bOSKnjYDhMj+YiAq61xrMGhQk9iXVk5FzgQMw==", - "dev": true, - "dependencies": { - "parent-module": "^1.0.0", - "resolve-from": "^4.0.0" - }, - "engines": { - "node": ">=6" - }, - "funding": { - "url": "https://github.com/sponsors/sindresorhus" - } - }, - "node_modules/import-from": { - "version": "3.0.0", - "resolved": "https://registry.npmjs.org/import-from/-/import-from-3.0.0.tgz", - "integrity": "sha512-CiuXOFFSzkU5x/CR0+z7T91Iht4CXgfCxVOFRhh2Zyhg5wOpWvvDLQUsWl+gcN+QscYBjez8hDCt85O7RLDttQ==", - "dev": true, - "dependencies": { - "resolve-from": "^5.0.0" - }, - "engines": { - "node": ">=8" - } - }, - "node_modules/import-from/node_modules/resolve-from": { - "version": "5.0.0", - "resolved": "https://registry.npmjs.org/resolve-from/-/resolve-from-5.0.0.tgz", - "integrity": "sha512-qYg9KP24dD5qka9J47d0aVky0N+b4fTU89LN9iDnjB5waksiC49rvMB0PrUJQGoTmH50XPiqOvAjDfaijGxYZw==", - "dev": true, - "engines": { - "node": ">=8" - } - }, - "node_modules/import-lazy": { - "version": "2.1.0", - "resolved": "https://registry.npmjs.org/import-lazy/-/import-lazy-2.1.0.tgz", - "integrity": "sha1-BWmOPUXIjo1+nZLLBYTnfwlvPkM=", - "dev": true, - "engines": { - "node": ">=4" - } - }, - "node_modules/imurmurhash": { - "version": "0.1.4", - "resolved": "https://registry.npmjs.org/imurmurhash/-/imurmurhash-0.1.4.tgz", - "integrity": "sha1-khi5srkoojixPcT7a21XbyMUU+o=", - "dev": true, - "engines": { - "node": ">=0.8.19" - } - }, - "node_modules/indent-string": { - "version": "4.0.0", - "resolved": "https://registry.npmjs.org/indent-string/-/indent-string-4.0.0.tgz", - "integrity": "sha512-EdDDZu4A2OyIK7Lr/2zG+w5jmbuk1DVBnEwREQvBzspBJkCEbRa8GxU1lghYcaGJCnRWibjDXlq779X1/y5xwg==", - "dev": true, - "engines": { - "node": ">=8" - } - }, - "node_modules/inflight": { - "version": "1.0.6", - "resolved": "https://registry.npmjs.org/inflight/-/inflight-1.0.6.tgz", - "integrity": "sha1-Sb1jMdfQLQwJvJEKEHW6gWW1bfk=", - "dev": true, - "dependencies": { - "once": "^1.3.0", - "wrappy": "1" - } - }, - "node_modules/inherits": { - "version": "2.0.4", - "resolved": "https://registry.npmjs.org/inherits/-/inherits-2.0.4.tgz", - "integrity": "sha512-k/vGaX4/Yla3WzyMCvTQOXYeIHvqOKtnqBduzTHpzpQZzAskKMhZ2K+EnBiSM9zGSoIFeMpXKxa4dYeZIQqewQ==", - "dev": true - }, - "node_modules/ini": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/ini/-/ini-2.0.0.tgz", - "integrity": "sha512-7PnF4oN3CvZF23ADhA5wRaYEQpJ8qygSkbtTXWBeXWXmEVRXK+1ITciHWwHhsjv1TmW0MgacIv6hEi5pX5NQdA==", - "dev": true, - "engines": { - "node": ">=10" - } - }, - "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==", - "dev": true, - "dependencies": { - "ansi-escapes": "^4.2.1", - "chalk": "^4.1.1", - "cli-cursor": "^3.1.0", - "cli-width": "^3.0.0", - "external-editor": "^3.0.3", - "figures": "^3.0.0", - "lodash": "^4.17.21", - "mute-stream": "0.0.8", - "ora": "^5.4.1", - "run-async": "^2.4.0", - "rxjs": "^7.2.0", - "string-width": "^4.1.0", - "strip-ansi": "^6.0.0", - "through": "^2.3.6" - }, - "engines": { - "node": ">=8.0.0" - } - }, - "node_modules/interpret": { - "version": "1.4.0", - "resolved": "https://registry.npmjs.org/interpret/-/interpret-1.4.0.tgz", - "integrity": "sha512-agE4QfB2Lkp9uICn7BAqoscw4SZP9kTE2hxiFI3jBPmXJfdqiahTbUuKGsMoN2GtqL9AxhYioAcVvgsb1HvRbA==", - "dev": true, - "engines": { - "node": ">= 0.10" - } - }, - "node_modules/is-arrayish": { - "version": "0.2.1", - "resolved": "https://registry.npmjs.org/is-arrayish/-/is-arrayish-0.2.1.tgz", - "integrity": "sha1-d8mYQFJ6qOyxqLppe4BkWnqSap0=", - "dev": true - }, - "node_modules/is-binary-path": { - "version": "2.1.0", - "resolved": "https://registry.npmjs.org/is-binary-path/-/is-binary-path-2.1.0.tgz", - "integrity": "sha512-ZMERYes6pDydyuGidse7OsHxtbI7WVeUEozgR/g7rd0xUimYNlvZRE/K2MgZTjWy725IfelLeVcEM97mmtRGXw==", - "dev": true, - "dependencies": { - "binary-extensions": "^2.0.0" - }, - "engines": { - "node": ">=8" - } - }, - "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==", - "dev": true, - "dependencies": { - "ci-info": "^3.1.1" - }, - "bin": { - "is-ci": "bin.js" - } - }, - "node_modules/is-core-module": { - "version": "2.8.0", - "resolved": "https://registry.npmjs.org/is-core-module/-/is-core-module-2.8.0.tgz", - "integrity": "sha512-vd15qHsaqrRL7dtH6QNuy0ndJmRDrS9HAM1CAiSifNUFv4x1a0CCVsj18hJ1mShxIG6T2i1sO78MkP56r0nYRw==", - "dev": true, - "dependencies": { - "has": "^1.0.3" - }, - "funding": { - "url": "https://github.com/sponsors/ljharb" - } - }, - "node_modules/is-docker": { - "version": "2.2.1", - "resolved": "https://registry.npmjs.org/is-docker/-/is-docker-2.2.1.tgz", - "integrity": "sha512-F+i2BKsFrH66iaUFc0woD8sLy8getkwTwtOBjvs56Cx4CgJDeKQeqfz8wAYiSb8JOprWhHH5p77PbmYCvvUuXQ==", - "dev": true, - "bin": { - "is-docker": "cli.js" - }, - "engines": { - "node": ">=8" - }, - "funding": { - "url": "https://github.com/sponsors/sindresorhus" - } - }, - "node_modules/is-extglob": { - "version": "2.1.1", - "resolved": "https://registry.npmjs.org/is-extglob/-/is-extglob-2.1.1.tgz", - "integrity": "sha1-qIwCU1eR8C7TfHahueqXc8gz+MI=", - "dev": true, - "engines": { - "node": ">=0.10.0" - } - }, - "node_modules/is-fullwidth-code-point": { - "version": "3.0.0", - "resolved": "https://registry.npmjs.org/is-fullwidth-code-point/-/is-fullwidth-code-point-3.0.0.tgz", - "integrity": "sha512-zymm5+u+sCsSWyD9qNaejV3DFvhCKclKdizYaJUuHA83RLjb7nSuGnddCHGv0hk+KY7BMAlsWeK4Ueg6EV6XQg==", - "dev": true, - "engines": { - "node": ">=8" - } - }, - "node_modules/is-glob": { - "version": "4.0.3", - "resolved": "https://registry.npmjs.org/is-glob/-/is-glob-4.0.3.tgz", - "integrity": "sha512-xelSayHH36ZgE7ZWhli7pW34hNbNl8Ojv5KVmkJD4hBdD3th8Tfk9vYasLM+mXWOZhFkgZfxhLSnrwRr4elSSg==", - "dev": true, - "dependencies": { - "is-extglob": "^2.1.1" - }, - "engines": { - "node": ">=0.10.0" - } - }, - "node_modules/is-installed-globally": { - "version": "0.4.0", - "resolved": "https://registry.npmjs.org/is-installed-globally/-/is-installed-globally-0.4.0.tgz", - "integrity": "sha512-iwGqO3J21aaSkC7jWnHP/difazwS7SFeIqxv6wEtLU8Y5KlzFTjyqcSIT0d8s4+dDhKytsk9PJZ2BkS5eZwQRQ==", - "dev": true, - "dependencies": { - "global-dirs": "^3.0.0", - "is-path-inside": "^3.0.2" - }, - "engines": { - "node": ">=10" - }, - "funding": { - "url": "https://github.com/sponsors/sindresorhus" - } - }, - "node_modules/is-interactive": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/is-interactive/-/is-interactive-1.0.0.tgz", - "integrity": "sha512-2HvIEKRoqS62guEC+qBjpvRubdX910WCMuJTZ+I9yvqKU2/12eSL549HMwtabb4oupdj2sMP50k+XJfB/8JE6w==", - "dev": true, - "engines": { - "node": ">=8" - } - }, - "node_modules/is-npm": { - "version": "5.0.0", - "resolved": "https://registry.npmjs.org/is-npm/-/is-npm-5.0.0.tgz", - "integrity": "sha512-WW/rQLOazUq+ST/bCAVBp/2oMERWLsR7OrKyt052dNDk4DHcDE0/7QSXITlmi+VBcV13DfIbysG3tZJm5RfdBA==", - "dev": true, - "engines": { - "node": ">=10" - }, - "funding": { - "url": "https://github.com/sponsors/sindresorhus" - } - }, - "node_modules/is-number": { - "version": "7.0.0", - "resolved": "https://registry.npmjs.org/is-number/-/is-number-7.0.0.tgz", - "integrity": "sha512-41Cifkg6e8TylSpdtTpeLVMqvSBEVzTttHvERD741+pnZ8ANv0004MRL43QKPDlK9cGvNp6NZWZUBlbGXYxxng==", - "dev": true, - "engines": { - "node": ">=0.12.0" - } - }, - "node_modules/is-obj": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/is-obj/-/is-obj-2.0.0.tgz", - "integrity": "sha512-drqDG3cbczxxEJRoOXcOjtdp1J/lyp1mNn0xaznRs8+muBhgQcrnbspox5X5fOw0HnMnbfDzvnEMEtqDEJEo8w==", - "dev": true, - "engines": { - "node": ">=8" - } - }, - "node_modules/is-path-inside": { - "version": "3.0.3", - "resolved": "https://registry.npmjs.org/is-path-inside/-/is-path-inside-3.0.3.tgz", - "integrity": "sha512-Fd4gABb+ycGAmKou8eMftCupSir5lRxqf4aD/vd0cD2qc4HL07OjCeuHMr8Ro4CoMaeCKDB0/ECBOVWjTwUvPQ==", - "dev": true, - "engines": { - "node": ">=8" - } - }, - "node_modules/is-plain-obj": { - "version": "2.1.0", - "resolved": "https://registry.npmjs.org/is-plain-obj/-/is-plain-obj-2.1.0.tgz", - "integrity": "sha512-YWnfyRwxL/+SsrWYfOpUtz5b3YD+nyfkHvjbcanzk8zgyO4ASD67uVMRt8k5bM4lLMDnXfriRhOpemw+NfT1eA==", - "dev": true, - "engines": { - "node": ">=8" - } - }, - "node_modules/is-plain-object": { - "version": "5.0.0", - "resolved": "https://registry.npmjs.org/is-plain-object/-/is-plain-object-5.0.0.tgz", - "integrity": "sha512-VRSzKkbMm5jMDoKLbltAkFQ5Qr7VDiTFGXxYFXXowVj387GeGNOCsOH6Msy00SGZ3Fp84b1Naa1psqgcCIEP5Q==", - "dev": true, - "engines": { - "node": ">=0.10.0" - } - }, - "node_modules/is-ssh": { - "version": "1.3.3", - "resolved": "https://registry.npmjs.org/is-ssh/-/is-ssh-1.3.3.tgz", - "integrity": "sha512-NKzJmQzJfEEma3w5cJNcUMxoXfDjz0Zj0eyCalHn2E6VOwlzjZo0yuO2fcBSf8zhFuVCL/82/r5gRcoi6aEPVQ==", - "dev": true, - "dependencies": { - "protocols": "^1.1.0" - } - }, - "node_modules/is-stream": { - "version": "2.0.1", - "resolved": "https://registry.npmjs.org/is-stream/-/is-stream-2.0.1.tgz", - "integrity": "sha512-hFoiJiTl63nn+kstHGBtewWSKnQLpyb155KHheA1l39uvtO9nWIop1p3udqPcUd/xbF1VLMO4n7OI6p7RbngDg==", - "dev": true, - "engines": { - "node": ">=8" - }, - "funding": { - "url": "https://github.com/sponsors/sindresorhus" - } - }, - "node_modules/is-typedarray": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/is-typedarray/-/is-typedarray-1.0.0.tgz", - "integrity": "sha1-5HnICFjfDBsR3dppQPlgEfzaSpo=", - "dev": true - }, - "node_modules/is-unicode-supported": { - "version": "0.1.0", - "resolved": "https://registry.npmjs.org/is-unicode-supported/-/is-unicode-supported-0.1.0.tgz", - "integrity": "sha512-knxG2q4UC3u8stRGyAVJCOdxFmv5DZiRcdlIaAQXAbSfJya+OhopNotLQrstBhququ4ZpuKbDc/8S6mgXgPFPw==", - "dev": true, - "engines": { - "node": ">=10" - }, - "funding": { - "url": "https://github.com/sponsors/sindresorhus" - } - }, - "node_modules/is-windows": { - "version": "1.0.2", - "resolved": "https://registry.npmjs.org/is-windows/-/is-windows-1.0.2.tgz", - "integrity": "sha512-eXK1UInq2bPmjyX6e3VHIzMLobc4J94i4AWn+Hpq3OU5KkrRC96OAcR3PRJ/pGu6m8TRnBHP9dkXQVsT/COVIA==", - "dev": true, - "engines": { - "node": ">=0.10.0" - } - }, - "node_modules/is-wsl": { - "version": "2.2.0", - "resolved": "https://registry.npmjs.org/is-wsl/-/is-wsl-2.2.0.tgz", - "integrity": "sha512-fKzAra0rGJUUBwGBgNkHZuToZcn+TtXHpeCgmkMJMMYx1sQDYaCSyjJBSCa2nH1DGm7s3n1oBnohoVTBaN7Lww==", - "dev": true, - "dependencies": { - "is-docker": "^2.0.0" - }, - "engines": { - "node": ">=8" - } - }, - "node_modules/is-yarn-global": { - "version": "0.3.0", - "resolved": "https://registry.npmjs.org/is-yarn-global/-/is-yarn-global-0.3.0.tgz", - "integrity": "sha512-VjSeb/lHmkoyd8ryPVIKvOCn4D1koMqY+vqyjjUfc3xyKtP4dYOxM44sZrnqQSzSds3xyOrUTLTC9LVCVgLngw==", - "dev": true - }, - "node_modules/isarray": { - "version": "0.0.1", - "resolved": "https://registry.npmjs.org/isarray/-/isarray-0.0.1.tgz", - "integrity": "sha1-ihis/Kmo9Bd+Cav8YDiTmwXR7t8=", - "dev": true - }, - "node_modules/isexe": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/isexe/-/isexe-2.0.0.tgz", - "integrity": "sha1-6PvzdNxVb/iUehDcsFctYz8s+hA=", - "dev": true - }, - "node_modules/istanbul-lib-coverage": { - "version": "3.2.0", - "resolved": "https://registry.npmjs.org/istanbul-lib-coverage/-/istanbul-lib-coverage-3.2.0.tgz", - "integrity": "sha512-eOeJ5BHCmHYvQK7xt9GkdHuzuCGS1Y6g9Gvnx3Ym33fz/HpLRYxiS0wHNr+m/MBC8B647Xt608vCDEvhl9c6Mw==", - "dev": true, - "engines": { - "node": ">=8" - } - }, - "node_modules/istanbul-lib-hook": { - "version": "3.0.0", - "resolved": "https://registry.npmjs.org/istanbul-lib-hook/-/istanbul-lib-hook-3.0.0.tgz", - "integrity": "sha512-Pt/uge1Q9s+5VAZ+pCo16TYMWPBIl+oaNIjgLQxcX0itS6ueeaA+pEfThZpH8WxhFgCiEb8sAJY6MdUKgiIWaQ==", - "dev": true, - "dependencies": { - "append-transform": "^2.0.0" - }, - "engines": { - "node": ">=8" - } - }, - "node_modules/istanbul-lib-instrument": { - "version": "4.0.3", - "resolved": "https://registry.npmjs.org/istanbul-lib-instrument/-/istanbul-lib-instrument-4.0.3.tgz", - "integrity": "sha512-BXgQl9kf4WTCPCCpmFGoJkz/+uhvm7h7PFKUYxh7qarQd3ER33vHG//qaE8eN25l07YqZPpHXU9I09l/RD5aGQ==", - "dev": true, - "dependencies": { - "@babel/core": "^7.7.5", - "@istanbuljs/schema": "^0.1.2", - "istanbul-lib-coverage": "^3.0.0", - "semver": "^6.3.0" - }, - "engines": { - "node": ">=8" - } - }, - "node_modules/istanbul-lib-instrument/node_modules/semver": { - "version": "6.3.0", - "resolved": "https://registry.npmjs.org/semver/-/semver-6.3.0.tgz", - "integrity": "sha512-b39TBaTSfV6yBrapU89p5fKekE2m/NwnDocOVruQFS1/veMgdzuPcnOM34M6CwxW8jH/lxEa5rBoDeUwu5HHTw==", - "dev": true, - "bin": { - "semver": "bin/semver.js" - } - }, - "node_modules/istanbul-lib-processinfo": { - "version": "2.0.2", - "resolved": "https://registry.npmjs.org/istanbul-lib-processinfo/-/istanbul-lib-processinfo-2.0.2.tgz", - "integrity": "sha512-kOwpa7z9hme+IBPZMzQ5vdQj8srYgAtaRqeI48NGmAQ+/5yKiHLV0QbYqQpxsdEF0+w14SoB8YbnHKcXE2KnYw==", - "dev": true, - "dependencies": { - "archy": "^1.0.0", - "cross-spawn": "^7.0.0", - "istanbul-lib-coverage": "^3.0.0-alpha.1", - "make-dir": "^3.0.0", - "p-map": "^3.0.0", - "rimraf": "^3.0.0", - "uuid": "^3.3.3" - }, - "engines": { - "node": ">=8" - } - }, - "node_modules/istanbul-lib-report": { - "version": "3.0.0", - "resolved": "https://registry.npmjs.org/istanbul-lib-report/-/istanbul-lib-report-3.0.0.tgz", - "integrity": "sha512-wcdi+uAKzfiGT2abPpKZ0hSU1rGQjUQnLvtY5MpQ7QCTahD3VODhcu4wcfY1YtkGaDD5yuydOLINXsfbus9ROw==", - "dev": true, - "dependencies": { - "istanbul-lib-coverage": "^3.0.0", - "make-dir": "^3.0.0", - "supports-color": "^7.1.0" - }, - "engines": { - "node": ">=8" - } - }, - "node_modules/istanbul-lib-source-maps": { - "version": "4.0.1", - "resolved": "https://registry.npmjs.org/istanbul-lib-source-maps/-/istanbul-lib-source-maps-4.0.1.tgz", - "integrity": "sha512-n3s8EwkdFIJCG3BPKBYvskgXGoy88ARzvegkitk60NxRdwltLOTaH7CUiMRXvwYorl0Q712iEjcWB+fK/MrWVw==", - "dev": true, - "dependencies": { - "debug": "^4.1.1", - "istanbul-lib-coverage": "^3.0.0", - "source-map": "^0.6.1" - }, - "engines": { - "node": ">=10" - } - }, - "node_modules/istanbul-lib-source-maps/node_modules/source-map": { - "version": "0.6.1", - "resolved": "https://registry.npmjs.org/source-map/-/source-map-0.6.1.tgz", - "integrity": "sha512-UjgapumWlbMhkBgzT7Ykc5YXUT46F0iKu8SGXq0bcwP5dz/h0Plj6enJqjz1Zbq2l5WaqYnrVbwWOWMyF3F47g==", - "dev": true, - "engines": { - "node": ">=0.10.0" - } - }, - "node_modules/istanbul-reports": { - "version": "3.0.5", - "resolved": "https://registry.npmjs.org/istanbul-reports/-/istanbul-reports-3.0.5.tgz", - "integrity": "sha512-5+19PlhnGabNWB7kOFnuxT8H3T/iIyQzIbQMxXsURmmvKg86P2sbkrGOT77VnHw0Qr0gc2XzRaRfMZYYbSQCJQ==", - "dev": true, - "dependencies": { - "html-escaper": "^2.0.0", - "istanbul-lib-report": "^3.0.0" - }, - "engines": { - "node": ">=8" - } - }, - "node_modules/js-tokens": { - "version": "4.0.0", - "resolved": "https://registry.npmjs.org/js-tokens/-/js-tokens-4.0.0.tgz", - "integrity": "sha512-RdJUflcE3cUzKiMqQgsCu06FPu9UdIJO0beYbPhHN4k6apgJtifcoCtT9bcxOpYBtpD2kCM6Sbzg4CausW/PKQ==", - "dev": true - }, - "node_modules/js-yaml": { - "version": "4.1.0", - "resolved": "https://registry.npmjs.org/js-yaml/-/js-yaml-4.1.0.tgz", - "integrity": "sha512-wpxZs9NoxZaJESJGIZTyDEaYpl0FKSA+FB9aJiyemKhMwkxQg63h4T1KJgUGHpTqPDNRcmmYLugrRjJlBtWvRA==", - "dev": true, - "dependencies": { - "argparse": "^2.0.1" - }, - "bin": { - "js-yaml": "bin/js-yaml.js" - } - }, - "node_modules/jsesc": { - "version": "2.5.2", - "resolved": "https://registry.npmjs.org/jsesc/-/jsesc-2.5.2.tgz", - "integrity": "sha512-OYu7XEzjkCQ3C5Ps3QIZsQfNpqoJyZZA99wd9aWd05NCtC5pWOkShK2mkL6HXQR6/Cy2lbNdPlZBpuQHXE63gA==", - "dev": true, - "bin": { - "jsesc": "bin/jsesc" - }, - "engines": { - "node": ">=4" - } - }, - "node_modules/json-buffer": { - "version": "3.0.1", - "resolved": "https://registry.npmjs.org/json-buffer/-/json-buffer-3.0.1.tgz", - "integrity": "sha512-4bV5BfR2mqfQTJm+V5tPPdf+ZpuhiIvTuAB5g8kcrXOZpTT/QwwVRWBywX1ozr6lEuPdbHxwaJlm9G6mI2sfSQ==", - "dev": true - }, - "node_modules/json-parse-even-better-errors": { - "version": "2.3.1", - "resolved": "https://registry.npmjs.org/json-parse-even-better-errors/-/json-parse-even-better-errors-2.3.1.tgz", - "integrity": "sha512-xyFwyhro/JEof6Ghe2iz2NcXoj2sloNsWr/XsERDK/oiPCfaNhl5ONfp+jQdAZRQQ0IJWNzH9zIZF7li91kh2w==", - "dev": true - }, - "node_modules/json-schema-traverse": { - "version": "0.4.1", - "resolved": "https://registry.npmjs.org/json-schema-traverse/-/json-schema-traverse-0.4.1.tgz", - "integrity": "sha512-xbbCH5dCYU5T8LcEhhuh7HJ88HXuW3qsI3Y0zOZFKfZEHcpWiHU/Jxzk629Brsab/mMiHQti9wMP+845RPe3Vg==", - "dev": true - }, - "node_modules/json-stable-stringify-without-jsonify": { - "version": "1.0.1", - "resolved": "https://registry.npmjs.org/json-stable-stringify-without-jsonify/-/json-stable-stringify-without-jsonify-1.0.1.tgz", - "integrity": "sha1-nbe1lJatPzz+8wp1FC0tkwrXJlE=", - "dev": true - }, - "node_modules/json5": { - "version": "2.2.0", - "resolved": "https://registry.npmjs.org/json5/-/json5-2.2.0.tgz", - "integrity": "sha512-f+8cldu7X/y7RAJurMEJmdoKXGB/X550w2Nr3tTbezL6RwEE/iMcm+tZnXeoZtKuOq6ft8+CqzEkrIgx1fPoQA==", - "dev": true, - "dependencies": { - "minimist": "^1.2.5" - }, - "bin": { - "json5": "lib/cli.js" - }, - "engines": { - "node": ">=6" - } - }, - "node_modules/jsonc-parser": { - "version": "3.0.0", - "resolved": "https://registry.npmjs.org/jsonc-parser/-/jsonc-parser-3.0.0.tgz", - "integrity": "sha512-fQzRfAbIBnR0IQvftw9FJveWiHp72Fg20giDrHz6TdfB12UH/uue0D3hm57UB5KgAVuniLMCaS8P1IMj9NR7cA==", - "dev": true - }, - "node_modules/just-extend": { - "version": "4.2.1", - "resolved": "https://registry.npmjs.org/just-extend/-/just-extend-4.2.1.tgz", - "integrity": "sha512-g3UB796vUFIY90VIv/WX3L2c8CS2MdWUww3CNrYmqza1Fg0DURc2K/O4YrnklBdQarSJ/y8JnJYDGc+1iumQjg==", - "dev": true - }, - "node_modules/keyv": { - "version": "4.0.4", - "resolved": "https://registry.npmjs.org/keyv/-/keyv-4.0.4.tgz", - "integrity": "sha512-vqNHbAc8BBsxk+7QBYLW0Y219rWcClspR6WSeoHYKG5mnsSoOH+BL1pWq02DDCVdvvuUny5rkBlzMRzoqc+GIg==", - "dev": true, - "dependencies": { - "json-buffer": "3.0.1" - } - }, - "node_modules/latest-version": { - "version": "5.1.0", - "resolved": "https://registry.npmjs.org/latest-version/-/latest-version-5.1.0.tgz", - "integrity": "sha512-weT+r0kTkRQdCdYCNtkMwWXQTMEswKrFBkm4ckQOMVhhqhIMI1UT2hMj+1iigIhgSZm5gTmrRXBNoGUgaTY1xA==", - "dev": true, - "dependencies": { - "package-json": "^6.3.0" - }, - "engines": { - "node": ">=8" - } - }, - "node_modules/levn": { - "version": "0.4.1", - "resolved": "https://registry.npmjs.org/levn/-/levn-0.4.1.tgz", - "integrity": "sha512-+bT2uH4E5LGE7h/n3evcS/sQlJXCpIp6ym8OWJ5eV6+67Dsql/LaaT7qJBAt2rzfoa/5QBGBhxDix1dMt2kQKQ==", - "dev": true, - "dependencies": { - "prelude-ls": "^1.2.1", - "type-check": "~0.4.0" - }, - "engines": { - "node": ">= 0.8.0" - } - }, - "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=", - "dev": true - }, - "node_modules/locate-path": { - "version": "6.0.0", - "resolved": "https://registry.npmjs.org/locate-path/-/locate-path-6.0.0.tgz", - "integrity": "sha512-iPZK6eYjbxRu3uB4/WZ3EsEIMJFMqAoopl3R+zuq0UjcAm/MO6KCweDgPfP3elTztoKP3KtnVHxTn2NHBSDVUw==", - "dev": true, - "dependencies": { - "p-locate": "^5.0.0" - }, - "engines": { - "node": ">=10" - }, - "funding": { - "url": "https://github.com/sponsors/sindresorhus" - } - }, - "node_modules/lodash": { - "version": "4.17.21", - "resolved": "https://registry.npmjs.org/lodash/-/lodash-4.17.21.tgz", - "integrity": "sha512-v2kDEe57lecTulaDIuNTPy3Ry4gLGJ6Z1O3vE1krgXZNrsQ+LFTGHVxVjcXPs17LhbZVGedAJv8XZ1tvj5FvSg==", - "dev": true - }, - "node_modules/lodash.flattendeep": { - "version": "4.4.0", - "resolved": "https://registry.npmjs.org/lodash.flattendeep/-/lodash.flattendeep-4.4.0.tgz", - "integrity": "sha1-+wMJF/hqMTTlvJvsDWngAT3f7bI=", - "dev": true - }, - "node_modules/lodash.get": { - "version": "4.4.2", - "resolved": "https://registry.npmjs.org/lodash.get/-/lodash.get-4.4.2.tgz", - "integrity": "sha1-LRd/ZS+jHpObRDjVNBSZ36OCXpk=", - "dev": true - }, - "node_modules/lodash.merge": { - "version": "4.6.2", - "resolved": "https://registry.npmjs.org/lodash.merge/-/lodash.merge-4.6.2.tgz", - "integrity": "sha512-0KpjqXRVvrYyCsX1swR/XTK0va6VQkQM6MNo7PqW77ByjAhoARA8EfrP1N4+KlKj8YS0ZUCtRT/YUuhyYDujIQ==", - "dev": true - }, - "node_modules/log-symbols": { - "version": "4.1.0", - "resolved": "https://registry.npmjs.org/log-symbols/-/log-symbols-4.1.0.tgz", - "integrity": "sha512-8XPvpAA8uyhfteu8pIvQxpJZ7SYYdpUivZpGy6sFsBuKRY/7rQGavedeB8aK+Zkyq6upMFVL/9AW6vOYzfRyLg==", - "dev": true, - "dependencies": { - "chalk": "^4.1.0", - "is-unicode-supported": "^0.1.0" - }, - "engines": { - "node": ">=10" - }, - "funding": { - "url": "https://github.com/sponsors/sindresorhus" - } - }, - "node_modules/lowercase-keys": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/lowercase-keys/-/lowercase-keys-2.0.0.tgz", - "integrity": "sha512-tqNXrS78oMOE73NMxK4EMLQsQowWf8jKooH9g7xPavRT706R6bkQJ6DY2Te7QukaZsulxa30wQ7bk0pm4XiHmA==", - "dev": true, - "engines": { - "node": ">=8" - } - }, - "node_modules/lru-cache": { - "version": "6.0.0", - "resolved": "https://registry.npmjs.org/lru-cache/-/lru-cache-6.0.0.tgz", - "integrity": "sha512-Jo6dJ04CmSjuznwJSS3pUeWmd/H0ffTlkXXgwZi+eq1UCmqQwCh+eLsYOYCwY991i2Fah4h1BEMCx4qThGbsiA==", - "dev": true, - "dependencies": { - "yallist": "^4.0.0" - }, - "engines": { - "node": ">=10" - } - }, - "node_modules/lunr": { - "version": "2.3.9", - "resolved": "https://registry.npmjs.org/lunr/-/lunr-2.3.9.tgz", - "integrity": "sha512-zTU3DaZaF3Rt9rhN3uBMGQD3dD2/vFQqnvZCDv4dl5iOzq2IZQqTxu90r4E5J+nP70J3ilqVCrbho2eWaeW8Ow==", - "dev": true - }, - "node_modules/macos-release": { - "version": "2.5.0", - "resolved": "https://registry.npmjs.org/macos-release/-/macos-release-2.5.0.tgz", - "integrity": "sha512-EIgv+QZ9r+814gjJj0Bt5vSLJLzswGmSUbUpbi9AIr/fsN2IWFBl2NucV9PAiek+U1STK468tEkxmVYUtuAN3g==", - "dev": true, - "engines": { - "node": ">=6" - }, - "funding": { - "url": "https://github.com/sponsors/sindresorhus" - } - }, - "node_modules/make-dir": { - "version": "3.1.0", - "resolved": "https://registry.npmjs.org/make-dir/-/make-dir-3.1.0.tgz", - "integrity": "sha512-g3FeP20LNwhALb/6Cz6Dd4F2ngze0jz7tbzrD2wAV+o9FeNHe4rL+yK2md0J/fiSf1sa1ADhXqi5+oVwOM/eGw==", - "dev": true, - "dependencies": { - "semver": "^6.0.0" - }, - "engines": { - "node": ">=8" - }, - "funding": { - "url": "https://github.com/sponsors/sindresorhus" - } - }, - "node_modules/make-dir/node_modules/semver": { - "version": "6.3.0", - "resolved": "https://registry.npmjs.org/semver/-/semver-6.3.0.tgz", - "integrity": "sha512-b39TBaTSfV6yBrapU89p5fKekE2m/NwnDocOVruQFS1/veMgdzuPcnOM34M6CwxW8jH/lxEa5rBoDeUwu5HHTw==", - "dev": true, - "bin": { - "semver": "bin/semver.js" - } - }, - "node_modules/make-error": { - "version": "1.3.6", - "resolved": "https://registry.npmjs.org/make-error/-/make-error-1.3.6.tgz", - "integrity": "sha512-s8UhlNe7vPKomQhC1qFelMokr/Sc3AgNbso3n74mVPA5LTZwkB9NlXf4XPamLxJE8h0gh73rM94xvwRT2CVInw==", - "dev": true - }, - "node_modules/marked": { - "version": "3.0.8", - "resolved": "https://registry.npmjs.org/marked/-/marked-3.0.8.tgz", - "integrity": "sha512-0gVrAjo5m0VZSJb4rpL59K1unJAMb/hm8HRXqasD8VeC8m91ytDPMritgFSlKonfdt+rRYYpP/JfLxgIX8yoSw==", - "dev": true, - "bin": { - "marked": "bin/marked" - }, - "engines": { - "node": ">= 12" - } - }, - "node_modules/merge-stream": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/merge-stream/-/merge-stream-2.0.0.tgz", - "integrity": "sha512-abv/qOcuPfk3URPfDzmZU1LKmuw8kT+0nIHvKrKgFrwifol/doWcdA4ZqsWQ8ENrFKkd67Mfpo/LovbIUsbt3w==", - "dev": true - }, - "node_modules/merge2": { - "version": "1.4.1", - "resolved": "https://registry.npmjs.org/merge2/-/merge2-1.4.1.tgz", - "integrity": "sha512-8q7VEgMJW4J8tcfVPy8g09NcQwZdbwFEqhe/WZkoIzjn/3TGDwtOCYtXGxA3O8tPzpczCCDgv+P2P5y00ZJOOg==", - "dev": true, - "engines": { - "node": ">= 8" - } - }, - "node_modules/micromatch": { - "version": "4.0.4", - "resolved": "https://registry.npmjs.org/micromatch/-/micromatch-4.0.4.tgz", - "integrity": "sha512-pRmzw/XUcwXGpD9aI9q/0XOwLNygjETJ8y0ao0wdqprrzDa4YnxLcz7fQRZr8voh8V10kGhABbNcHVk5wHgWwg==", - "dev": true, - "dependencies": { - "braces": "^3.0.1", - "picomatch": "^2.2.3" - }, - "engines": { - "node": ">=8.6" - } - }, - "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==", - "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==", - "dev": true, - "dependencies": { - "mime-db": "1.49.0" - }, - "engines": { - "node": ">= 0.6" - } - }, - "node_modules/mimic-fn": { - "version": "2.1.0", - "resolved": "https://registry.npmjs.org/mimic-fn/-/mimic-fn-2.1.0.tgz", - "integrity": "sha512-OqbOk5oEQeAZ8WXWydlu9HJjz9WVdEIvamMCcXmuqUYjTknH/sqsWvhQ3vgwKFRR1HpjvNBKQ37nbJgYzGqGcg==", - "dev": true, - "engines": { - "node": ">=6" - } - }, - "node_modules/mimic-response": { - "version": "1.0.1", - "resolved": "https://registry.npmjs.org/mimic-response/-/mimic-response-1.0.1.tgz", - "integrity": "sha512-j5EctnkH7amfV/q5Hgmoal1g2QHFJRraOtmx0JpIqkxhBhI/lJSl1nMpQ45hVarwNETOoWEimndZ4QK0RHxuxQ==", - "dev": true, - "engines": { - "node": ">=4" - } - }, - "node_modules/minimatch": { - "version": "3.0.4", - "resolved": "https://registry.npmjs.org/minimatch/-/minimatch-3.0.4.tgz", - "integrity": "sha512-yJHVQEhyqPLUTgt9B83PXu6W3rx4MvvHvSUvToogpwoGDOUQ+yDrR0HRot+yOCdCO7u4hX3pWft6kWBBcqh0UA==", - "dev": true, - "dependencies": { - "brace-expansion": "^1.1.7" - }, - "engines": { - "node": "*" - } - }, - "node_modules/minimist": { - "version": "1.2.5", - "resolved": "https://registry.npmjs.org/minimist/-/minimist-1.2.5.tgz", - "integrity": "sha512-FM9nNUYrRBAELZQT3xeZQ7fmMOBg6nWNmJKTcgsJeaLstP/UODVpGsr5OhXhhXg6f+qtJ8uiZ+PUxkDWcgIXLw==", - "dev": true - }, - "node_modules/mocha": { - "version": "9.1.3", - "resolved": "https://registry.npmjs.org/mocha/-/mocha-9.1.3.tgz", - "integrity": "sha512-Xcpl9FqXOAYqI3j79pEtHBBnQgVXIhpULjGQa7DVb0Po+VzmSIK9kanAiWLHoRR/dbZ2qpdPshuXr8l1VaHCzw==", - "dev": true, - "dependencies": { - "@ungap/promise-all-settled": "1.1.2", - "ansi-colors": "4.1.1", - "browser-stdout": "1.3.1", - "chokidar": "3.5.2", - "debug": "4.3.2", - "diff": "5.0.0", - "escape-string-regexp": "4.0.0", - "find-up": "5.0.0", - "glob": "7.1.7", - "growl": "1.10.5", - "he": "1.2.0", - "js-yaml": "4.1.0", - "log-symbols": "4.1.0", - "minimatch": "3.0.4", - "ms": "2.1.3", - "nanoid": "3.1.25", - "serialize-javascript": "6.0.0", - "strip-json-comments": "3.1.1", - "supports-color": "8.1.1", - "which": "2.0.2", - "workerpool": "6.1.5", - "yargs": "16.2.0", - "yargs-parser": "20.2.4", - "yargs-unparser": "2.0.0" - }, - "bin": { - "_mocha": "bin/_mocha", - "mocha": "bin/mocha" - }, - "engines": { - "node": ">= 12.0.0" - }, - "funding": { - "type": "opencollective", - "url": "https://opencollective.com/mochajs" - } - }, - "node_modules/mocha/node_modules/ms": { - "version": "2.1.3", - "resolved": "https://registry.npmjs.org/ms/-/ms-2.1.3.tgz", - "integrity": "sha512-6FlzubTLZG3J2a/NVCAleEhjzq5oxgHyaCU9yYXvcLsvoVaHJq/s5xXI6/XXP6tz7R9xAOtHnSO/tXtF3WRTlA==", - "dev": true - }, - "node_modules/mocha/node_modules/supports-color": { - "version": "8.1.1", - "resolved": "https://registry.npmjs.org/supports-color/-/supports-color-8.1.1.tgz", - "integrity": "sha512-MpUEN2OodtUzxvKQl72cUF7RQ5EiHsGvSsVG0ia9c5RbWGL2CI4C7EpPS8UTBIplnlzZiNuV56w+FuNxy3ty2Q==", - "dev": true, - "dependencies": { - "has-flag": "^4.0.0" - }, - "engines": { - "node": ">=10" - }, - "funding": { - "url": "https://github.com/chalk/supports-color?sponsor=1" - } - }, - "node_modules/mocha/node_modules/yargs": { - "version": "16.2.0", - "resolved": "https://registry.npmjs.org/yargs/-/yargs-16.2.0.tgz", - "integrity": "sha512-D1mvvtDG0L5ft/jGWkLpG1+m0eQxOfaBvTNELraWj22wSVUMWxZUvYgJYcKh6jGGIkJFhH4IZPQhR4TKpc8mBw==", - "dev": true, - "dependencies": { - "cliui": "^7.0.2", - "escalade": "^3.1.1", - "get-caller-file": "^2.0.5", - "require-directory": "^2.1.1", - "string-width": "^4.2.0", - "y18n": "^5.0.5", - "yargs-parser": "^20.2.2" - }, - "engines": { - "node": ">=10" - } - }, - "node_modules/ms": { - "version": "2.1.2", - "resolved": "https://registry.npmjs.org/ms/-/ms-2.1.2.tgz", - "integrity": "sha512-sGkPx+VjMtmA6MX27oA4FBFELFCZZ4S4XqeGOXCv68tT+jb3vk/RyaKWP0PTKyWtmLSM0b+adUTEvbs1PEaH2w==", - "dev": true - }, - "node_modules/mute-stream": { - "version": "0.0.8", - "resolved": "https://registry.npmjs.org/mute-stream/-/mute-stream-0.0.8.tgz", - "integrity": "sha512-nnbWWOkoWyUsTjKrhgD0dcz22mdkSnpYqbEjIm2nhwhuxlSkpywJmBo8h0ZqJdkp73mb90SssHkN4rsRaBAfAA==", - "dev": true - }, - "node_modules/nanoid": { - "version": "3.1.25", - "resolved": "https://registry.npmjs.org/nanoid/-/nanoid-3.1.25.tgz", - "integrity": "sha512-rdwtIXaXCLFAQbnfqDRnI6jaRHp9fTcYBjtFKE8eezcZ7LuLjhUaQGNeMXf1HmRoCH32CLz6XwX0TtxEOS/A3Q==", - "dev": true, - "bin": { - "nanoid": "bin/nanoid.cjs" - }, - "engines": { - "node": "^10 || ^12 || ^13.7 || ^14 || >=15.0.1" - } - }, - "node_modules/natural-compare": { - "version": "1.4.0", - "resolved": "https://registry.npmjs.org/natural-compare/-/natural-compare-1.4.0.tgz", - "integrity": "sha1-Sr6/7tdUHywnrPspvbvRXI1bpPc=", - "dev": true - }, - "node_modules/neo-async": { - "version": "2.6.2", - "resolved": "https://registry.npmjs.org/neo-async/-/neo-async-2.6.2.tgz", - "integrity": "sha512-Yd3UES5mWCSqR+qNT93S3UoYUkqAZ9lLg8a7g9rimsWmYGK8cVToA4/sF3RrshdyV3sAGMXVUmpMYOw+dLpOuw==", - "dev": true - }, - "node_modules/new-github-release-url": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/new-github-release-url/-/new-github-release-url-1.0.0.tgz", - "integrity": "sha512-dle7yf655IMjyFUqn6Nxkb18r4AOAkzRcgcZv6WZ0IqrOH4QCEZ8Sm6I7XX21zvHdBeeMeTkhR9qT2Z0EJDx6A==", - "dev": true, - "dependencies": { - "type-fest": "^0.4.1" - }, - "engines": { - "node": ">=10" - } - }, - "node_modules/new-github-release-url/node_modules/type-fest": { - "version": "0.4.1", - "resolved": "https://registry.npmjs.org/type-fest/-/type-fest-0.4.1.tgz", - "integrity": "sha512-IwzA/LSfD2vC1/YDYMv/zHP4rDF1usCwllsDpbolT3D4fUepIO7f9K70jjmUewU/LmGUKJcwcVtDCpnKk4BPMw==", - "dev": true, - "engines": { - "node": ">=6" - } - }, - "node_modules/nise": { - "version": "5.1.0", - "resolved": "https://registry.npmjs.org/nise/-/nise-5.1.0.tgz", - "integrity": "sha512-W5WlHu+wvo3PaKLsJJkgPup2LrsXCcm7AWwyNZkUnn5rwPkuPBi3Iwk5SQtN0mv+K65k7nKKjwNQ30wg3wLAQQ==", - "dev": true, - "dependencies": { - "@sinonjs/commons": "^1.7.0", - "@sinonjs/fake-timers": "^7.0.4", - "@sinonjs/text-encoding": "^0.7.1", - "just-extend": "^4.0.2", - "path-to-regexp": "^1.7.0" - } - }, - "node_modules/node-fetch": { - "version": "2.6.6", - "resolved": "https://registry.npmjs.org/node-fetch/-/node-fetch-2.6.6.tgz", - "integrity": "sha512-Z8/6vRlTUChSdIgMa51jxQ4lrw/Jy5SOW10ObaA47/RElsAN2c5Pn8bTgFGWn/ibwzXTE8qwr1Yzx28vsecXEA==", - "dev": true, - "dependencies": { - "whatwg-url": "^5.0.0" - }, - "engines": { - "node": "4.x || >=6.0.0" - } - }, - "node_modules/node-preload": { - "version": "0.2.1", - "resolved": "https://registry.npmjs.org/node-preload/-/node-preload-0.2.1.tgz", - "integrity": "sha512-RM5oyBy45cLEoHqCeh+MNuFAxO0vTFBLskvQbOKnEE7YTTSN4tbN8QWDIPQ6L+WvKsB/qLEGpYe2ZZ9d4W9OIQ==", - "dev": true, - "dependencies": { - "process-on-spawn": "^1.0.0" - }, - "engines": { - "node": ">=8" - } - }, - "node_modules/node-releases": { - "version": "2.0.1", - "resolved": "https://registry.npmjs.org/node-releases/-/node-releases-2.0.1.tgz", - "integrity": "sha512-CqyzN6z7Q6aMeF/ktcMVTzhAHCEpf8SOarwpzpf8pNBY2k5/oM34UHldUwp8VKI7uxct2HxSRdJjBaZeESzcxA==", - "dev": true - }, - "node_modules/normalize-path": { - "version": "3.0.0", - "resolved": "https://registry.npmjs.org/normalize-path/-/normalize-path-3.0.0.tgz", - "integrity": "sha512-6eZs5Ls3WtCisHWp9S2GUy8dqkpGi4BVSz3GaqiE6ezub0512ESztXUwUB6C6IKbQkY2Pnb/mD4WYojCRwcwLA==", - "dev": true, - "engines": { - "node": ">=0.10.0" - } - }, - "node_modules/normalize-url": { - "version": "6.1.0", - "resolved": "https://registry.npmjs.org/normalize-url/-/normalize-url-6.1.0.tgz", - "integrity": "sha512-DlL+XwOy3NxAQ8xuC0okPgK46iuVNAK01YN7RueYBqqFeGsBjV9XmCAzAdgt+667bCl5kPh9EqKKDwnaPG1I7A==", - "dev": true, - "engines": { - "node": ">=10" - }, - "funding": { - "url": "https://github.com/sponsors/sindresorhus" - } - }, - "node_modules/npm-run-path": { - "version": "4.0.1", - "resolved": "https://registry.npmjs.org/npm-run-path/-/npm-run-path-4.0.1.tgz", - "integrity": "sha512-S48WzZW777zhNIrn7gxOlISNAqi9ZC/uQFnRdbeIHhZhCA6UqpkOT8T1G7BvfdgP4Er8gF4sUbaS0i7QvIfCWw==", - "dev": true, - "dependencies": { - "path-key": "^3.0.0" - }, - "engines": { - "node": ">=8" - } - }, - "node_modules/nyc": { - "version": "15.1.0", - "resolved": "https://registry.npmjs.org/nyc/-/nyc-15.1.0.tgz", - "integrity": "sha512-jMW04n9SxKdKi1ZMGhvUTHBN0EICCRkHemEoE5jm6mTYcqcdas0ATzgUgejlQUHMvpnOZqGB5Xxsv9KxJW1j8A==", - "dev": true, - "dependencies": { - "@istanbuljs/load-nyc-config": "^1.0.0", - "@istanbuljs/schema": "^0.1.2", - "caching-transform": "^4.0.0", - "convert-source-map": "^1.7.0", - "decamelize": "^1.2.0", - "find-cache-dir": "^3.2.0", - "find-up": "^4.1.0", - "foreground-child": "^2.0.0", - "get-package-type": "^0.1.0", - "glob": "^7.1.6", - "istanbul-lib-coverage": "^3.0.0", - "istanbul-lib-hook": "^3.0.0", - "istanbul-lib-instrument": "^4.0.0", - "istanbul-lib-processinfo": "^2.0.2", - "istanbul-lib-report": "^3.0.0", - "istanbul-lib-source-maps": "^4.0.0", - "istanbul-reports": "^3.0.2", - "make-dir": "^3.0.0", - "node-preload": "^0.2.1", - "p-map": "^3.0.0", - "process-on-spawn": "^1.0.0", - "resolve-from": "^5.0.0", - "rimraf": "^3.0.0", - "signal-exit": "^3.0.2", - "spawn-wrap": "^2.0.0", - "test-exclude": "^6.0.0", - "yargs": "^15.0.2" - }, - "bin": { - "nyc": "bin/nyc.js" - }, - "engines": { - "node": ">=8.9" - } - }, - "node_modules/nyc/node_modules/cliui": { - "version": "6.0.0", - "resolved": "https://registry.npmjs.org/cliui/-/cliui-6.0.0.tgz", - "integrity": "sha512-t6wbgtoCXvAzst7QgXxJYqPt0usEfbgQdftEPbLL/cvv6HPE5VgvqCuAIDR0NgU52ds6rFwqrgakNLrHEjCbrQ==", - "dev": true, - "dependencies": { - "string-width": "^4.2.0", - "strip-ansi": "^6.0.0", - "wrap-ansi": "^6.2.0" - } - }, - "node_modules/nyc/node_modules/find-up": { - "version": "4.1.0", - "resolved": "https://registry.npmjs.org/find-up/-/find-up-4.1.0.tgz", - "integrity": "sha512-PpOwAdQ/YlXQ2vj8a3h8IipDuYRi3wceVQQGYWxNINccq40Anw7BlsEXCMbt1Zt+OLA6Fq9suIpIWD0OsnISlw==", - "dev": true, - "dependencies": { - "locate-path": "^5.0.0", - "path-exists": "^4.0.0" - }, - "engines": { - "node": ">=8" - } - }, - "node_modules/nyc/node_modules/locate-path": { - "version": "5.0.0", - "resolved": "https://registry.npmjs.org/locate-path/-/locate-path-5.0.0.tgz", - "integrity": "sha512-t7hw9pI+WvuwNJXwk5zVHpyhIqzg2qTlklJOf0mVxGSbe3Fp2VieZcduNYjaLDoy6p9uGpQEGWG87WpMKlNq8g==", - "dev": true, - "dependencies": { - "p-locate": "^4.1.0" - }, - "engines": { - "node": ">=8" - } - }, - "node_modules/nyc/node_modules/p-limit": { - "version": "2.3.0", - "resolved": "https://registry.npmjs.org/p-limit/-/p-limit-2.3.0.tgz", - "integrity": "sha512-//88mFWSJx8lxCzwdAABTJL2MyWB12+eIY7MDL2SqLmAkeKU9qxRvWuSyTjm3FUmpBEMuFfckAIqEaVGUDxb6w==", - "dev": true, - "dependencies": { - "p-try": "^2.0.0" - }, - "engines": { - "node": ">=6" - }, - "funding": { - "url": "https://github.com/sponsors/sindresorhus" - } - }, - "node_modules/nyc/node_modules/p-locate": { - "version": "4.1.0", - "resolved": "https://registry.npmjs.org/p-locate/-/p-locate-4.1.0.tgz", - "integrity": "sha512-R79ZZ/0wAxKGu3oYMlz8jy/kbhsNrS7SKZ7PxEHBgJ5+F2mtFW2fK2cOtBh1cHYkQsbzFV7I+EoRKe6Yt0oK7A==", - "dev": true, - "dependencies": { - "p-limit": "^2.2.0" - }, - "engines": { - "node": ">=8" - } - }, - "node_modules/nyc/node_modules/resolve-from": { - "version": "5.0.0", - "resolved": "https://registry.npmjs.org/resolve-from/-/resolve-from-5.0.0.tgz", - "integrity": "sha512-qYg9KP24dD5qka9J47d0aVky0N+b4fTU89LN9iDnjB5waksiC49rvMB0PrUJQGoTmH50XPiqOvAjDfaijGxYZw==", - "dev": true, - "engines": { - "node": ">=8" - } - }, - "node_modules/nyc/node_modules/wrap-ansi": { - "version": "6.2.0", - "resolved": "https://registry.npmjs.org/wrap-ansi/-/wrap-ansi-6.2.0.tgz", - "integrity": "sha512-r6lPcBGxZXlIcymEu7InxDMhdW0KDxpLgoFLcguasxCaJ/SOIZwINatK9KY/tf+ZrlywOKU0UDj3ATXUBfxJXA==", - "dev": true, - "dependencies": { - "ansi-styles": "^4.0.0", - "string-width": "^4.1.0", - "strip-ansi": "^6.0.0" - }, - "engines": { - "node": ">=8" - } - }, - "node_modules/nyc/node_modules/y18n": { - "version": "4.0.3", - "resolved": "https://registry.npmjs.org/y18n/-/y18n-4.0.3.tgz", - "integrity": "sha512-JKhqTOwSrqNA1NY5lSztJ1GrBiUodLMmIZuLiDaMRJ+itFd+ABVE8XBjOvIWL+rSqNDC74LCSFmlb/U4UZ4hJQ==", - "dev": true - }, - "node_modules/nyc/node_modules/yargs": { - "version": "15.4.1", - "resolved": "https://registry.npmjs.org/yargs/-/yargs-15.4.1.tgz", - "integrity": "sha512-aePbxDmcYW++PaqBsJ+HYUFwCdv4LVvdnhBy78E57PIor8/OVvhMrADFFEDh8DHDFRv/O9i3lPhsENjO7QX0+A==", - "dev": true, - "dependencies": { - "cliui": "^6.0.0", - "decamelize": "^1.2.0", - "find-up": "^4.1.0", - "get-caller-file": "^2.0.1", - "require-directory": "^2.1.1", - "require-main-filename": "^2.0.0", - "set-blocking": "^2.0.0", - "string-width": "^4.2.0", - "which-module": "^2.0.0", - "y18n": "^4.0.0", - "yargs-parser": "^18.1.2" - }, - "engines": { - "node": ">=8" - } - }, - "node_modules/nyc/node_modules/yargs-parser": { - "version": "18.1.3", - "resolved": "https://registry.npmjs.org/yargs-parser/-/yargs-parser-18.1.3.tgz", - "integrity": "sha512-o50j0JeToy/4K6OZcaQmW6lyXXKhq7csREXcDwk2omFPJEwUNOVtJKvmDr9EI1fAJZUyZcRF7kxGBWmRXudrCQ==", - "dev": true, - "dependencies": { - "camelcase": "^5.0.0", - "decamelize": "^1.2.0" - }, - "engines": { - "node": ">=6" - } - }, - "node_modules/object-inspect": { - "version": "1.11.0", - "resolved": "https://registry.npmjs.org/object-inspect/-/object-inspect-1.11.0.tgz", - "integrity": "sha512-jp7ikS6Sd3GxQfZJPyH3cjcbJF6GZPClgdV+EFygjFLQ5FmW/dRUnTd9PQ9k0JhoNDabWFbpF1yCdSWCC6gexg==", - "dev": true, - "funding": { - "url": "https://github.com/sponsors/ljharb" - } - }, - "node_modules/once": { - "version": "1.4.0", - "resolved": "https://registry.npmjs.org/once/-/once-1.4.0.tgz", - "integrity": "sha1-WDsap3WWHUsROsF9nFC6753Xa9E=", - "dev": true, - "dependencies": { - "wrappy": "1" - } - }, - "node_modules/onetime": { - "version": "5.1.2", - "resolved": "https://registry.npmjs.org/onetime/-/onetime-5.1.2.tgz", - "integrity": "sha512-kbpaSSGJTWdAY5KPVeMOKXSrPtr8C8C7wodJbcsd51jRnmD+GZu8Y0VoU6Dm5Z4vWr0Ig/1NKuWRKf7j5aaYSg==", - "dev": true, - "dependencies": { - "mimic-fn": "^2.1.0" - }, - "engines": { - "node": ">=6" - }, - "funding": { - "url": "https://github.com/sponsors/sindresorhus" - } - }, - "node_modules/onigasm": { - "version": "2.2.5", - "resolved": "https://registry.npmjs.org/onigasm/-/onigasm-2.2.5.tgz", - "integrity": "sha512-F+th54mPc0l1lp1ZcFMyL/jTs2Tlq4SqIHKIXGZOR/VkHkF9A7Fr5rRr5+ZG/lWeRsyrClLYRq7s/yFQ/XhWCA==", - "dev": true, - "dependencies": { - "lru-cache": "^5.1.1" - } - }, - "node_modules/onigasm/node_modules/lru-cache": { - "version": "5.1.1", - "resolved": "https://registry.npmjs.org/lru-cache/-/lru-cache-5.1.1.tgz", - "integrity": "sha512-KpNARQA3Iwv+jTA0utUVVbrh+Jlrr1Fv0e56GGzAFOXN7dk/FviaDW8LHmK52DlcH4WP2n6gI8vN1aesBFgo9w==", - "dev": true, - "dependencies": { - "yallist": "^3.0.2" - } - }, - "node_modules/onigasm/node_modules/yallist": { - "version": "3.1.1", - "resolved": "https://registry.npmjs.org/yallist/-/yallist-3.1.1.tgz", - "integrity": "sha512-a4UGQaWPH59mOXUYnAG2ewncQS4i4F43Tv3JoAM+s2VDAmS9NsK8GpDMLrCHPksFT7h3K6TOoUNn2pb7RoXx4g==", - "dev": true - }, - "node_modules/open": { - "version": "7.4.2", - "resolved": "https://registry.npmjs.org/open/-/open-7.4.2.tgz", - "integrity": "sha512-MVHddDVweXZF3awtlAS+6pgKLlm/JgxZ90+/NBurBoQctVOOB/zDdVjcyPzQ+0laDGbsWgrRkflI65sQeOgT9Q==", - "dev": true, - "dependencies": { - "is-docker": "^2.0.0", - "is-wsl": "^2.1.1" - }, - "engines": { - "node": ">=8" - }, - "funding": { - "url": "https://github.com/sponsors/sindresorhus" - } - }, - "node_modules/optionator": { - "version": "0.9.1", - "resolved": "https://registry.npmjs.org/optionator/-/optionator-0.9.1.tgz", - "integrity": "sha512-74RlY5FCnhq4jRxVUPKDaRwrVNXMqsGsiW6AJw4XK8hmtm10wC0ypZBLw5IIp85NZMr91+qd1RvvENwg7jjRFw==", - "dev": true, - "dependencies": { - "deep-is": "^0.1.3", - "fast-levenshtein": "^2.0.6", - "levn": "^0.4.1", - "prelude-ls": "^1.2.1", - "type-check": "^0.4.0", - "word-wrap": "^1.2.3" - }, - "engines": { - "node": ">= 0.8.0" - } - }, - "node_modules/ora": { - "version": "5.4.1", - "resolved": "https://registry.npmjs.org/ora/-/ora-5.4.1.tgz", - "integrity": "sha512-5b6Y85tPxZZ7QytO+BQzysW31HJku27cRIlkbAXaNx+BdcVi+LlRFmVXzeF6a7JCwJpyw5c4b+YSVImQIrBpuQ==", - "dev": true, - "dependencies": { - "bl": "^4.1.0", - "chalk": "^4.1.0", - "cli-cursor": "^3.1.0", - "cli-spinners": "^2.5.0", - "is-interactive": "^1.0.0", - "is-unicode-supported": "^0.1.0", - "log-symbols": "^4.1.0", - "strip-ansi": "^6.0.0", - "wcwidth": "^1.0.1" - }, - "engines": { - "node": ">=10" - }, - "funding": { - "url": "https://github.com/sponsors/sindresorhus" - } - }, - "node_modules/os-name": { - "version": "4.0.1", - "resolved": "https://registry.npmjs.org/os-name/-/os-name-4.0.1.tgz", - "integrity": "sha512-xl9MAoU97MH1Xt5K9ERft2YfCAoaO6msy1OBA0ozxEC0x0TmIoE6K3QvgJMMZA9yKGLmHXNY/YZoDbiGDj4zYw==", - "dev": true, - "dependencies": { - "macos-release": "^2.5.0", - "windows-release": "^4.0.0" - }, - "engines": { - "node": ">=10" - }, - "funding": { - "url": "https://github.com/sponsors/sindresorhus" - } - }, - "node_modules/os-tmpdir": { - "version": "1.0.2", - "resolved": "https://registry.npmjs.org/os-tmpdir/-/os-tmpdir-1.0.2.tgz", - "integrity": "sha1-u+Z0BseaqFxc/sdm/lc0VV36EnQ=", - "dev": true, - "engines": { - "node": ">=0.10.0" - } - }, - "node_modules/p-cancelable": { - "version": "2.1.1", - "resolved": "https://registry.npmjs.org/p-cancelable/-/p-cancelable-2.1.1.tgz", - "integrity": "sha512-BZOr3nRQHOntUjTrH8+Lh54smKHoHyur8We1V8DSMVrl5A2malOOwuJRnKRDjSnkoeBh4at6BwEnb5I7Jl31wg==", - "dev": true, - "engines": { - "node": ">=8" - } - }, - "node_modules/p-limit": { - "version": "3.1.0", - "resolved": "https://registry.npmjs.org/p-limit/-/p-limit-3.1.0.tgz", - "integrity": "sha512-TYOanM3wGwNGsZN2cVTYPArw454xnXj5qmWF1bEoAc4+cU/ol7GVh7odevjp1FNHduHc3KZMcFduxU5Xc6uJRQ==", - "dev": true, - "dependencies": { - "yocto-queue": "^0.1.0" - }, - "engines": { - "node": ">=10" - }, - "funding": { - "url": "https://github.com/sponsors/sindresorhus" - } - }, - "node_modules/p-locate": { - "version": "5.0.0", - "resolved": "https://registry.npmjs.org/p-locate/-/p-locate-5.0.0.tgz", - "integrity": "sha512-LaNjtRWUBY++zB5nE/NwcaoMylSPk+S+ZHNB1TzdbMJMny6dynpAGt7X/tl/QYq3TIeE6nxHppbo2LGymrG5Pw==", - "dev": true, - "dependencies": { - "p-limit": "^3.0.2" - }, - "engines": { - "node": ">=10" - }, - "funding": { - "url": "https://github.com/sponsors/sindresorhus" - } - }, - "node_modules/p-map": { - "version": "3.0.0", - "resolved": "https://registry.npmjs.org/p-map/-/p-map-3.0.0.tgz", - "integrity": "sha512-d3qXVTF/s+W+CdJ5A29wywV2n8CQQYahlgz2bFiA+4eVNJbHJodPZ+/gXwPGh0bOqA+j8S+6+ckmvLGPk1QpxQ==", - "dev": true, - "dependencies": { - "aggregate-error": "^3.0.0" - }, - "engines": { - "node": ">=8" - } - }, - "node_modules/p-try": { - "version": "2.2.0", - "resolved": "https://registry.npmjs.org/p-try/-/p-try-2.2.0.tgz", - "integrity": "sha512-R4nPAVTAU0B9D35/Gk3uJf/7XYbQcyohSKdvAxIRSNghFl4e71hVoGnBNQz9cWaXxO2I10KTC+3jMdvvoKw6dQ==", - "dev": true, - "engines": { - "node": ">=6" - } - }, - "node_modules/package-hash": { - "version": "4.0.0", - "resolved": "https://registry.npmjs.org/package-hash/-/package-hash-4.0.0.tgz", - "integrity": "sha512-whdkPIooSu/bASggZ96BWVvZTRMOFxnyUG5PnTSGKoJE2gd5mbVNmR2Nj20QFzxYYgAXpoqC+AiXzl+UMRh7zQ==", - "dev": true, - "dependencies": { - "graceful-fs": "^4.1.15", - "hasha": "^5.0.0", - "lodash.flattendeep": "^4.4.0", - "release-zalgo": "^1.0.0" - }, - "engines": { - "node": ">=8" - } - }, - "node_modules/package-json": { - "version": "6.5.0", - "resolved": "https://registry.npmjs.org/package-json/-/package-json-6.5.0.tgz", - "integrity": "sha512-k3bdm2n25tkyxcjSKzB5x8kfVxlMdgsbPr0GkZcwHsLpba6cBjqCt1KlcChKEvxHIcTB1FVMuwoijZ26xex5MQ==", - "dev": true, - "dependencies": { - "got": "^9.6.0", - "registry-auth-token": "^4.0.0", - "registry-url": "^5.0.0", - "semver": "^6.2.0" - }, - "engines": { - "node": ">=8" - } - }, - "node_modules/package-json/node_modules/@sindresorhus/is": { - "version": "0.14.0", - "resolved": "https://registry.npmjs.org/@sindresorhus/is/-/is-0.14.0.tgz", - "integrity": "sha512-9NET910DNaIPngYnLLPeg+Ogzqsi9uM4mSboU5y6p8S5DzMTVEsJZrawi+BoDNUVBa2DhJqQYUFvMDfgU062LQ==", - "dev": true, - "engines": { - "node": ">=6" - } - }, - "node_modules/package-json/node_modules/@szmarczak/http-timer": { - "version": "1.1.2", - "resolved": "https://registry.npmjs.org/@szmarczak/http-timer/-/http-timer-1.1.2.tgz", - "integrity": "sha512-XIB2XbzHTN6ieIjfIMV9hlVcfPU26s2vafYWQcZHWXHOxiaRZYEDKEwdl129Zyg50+foYV2jCgtrqSA6qNuNSA==", - "dev": true, - "dependencies": { - "defer-to-connect": "^1.0.1" - }, - "engines": { - "node": ">=6" - } - }, - "node_modules/package-json/node_modules/cacheable-request": { - "version": "6.1.0", - "resolved": "https://registry.npmjs.org/cacheable-request/-/cacheable-request-6.1.0.tgz", - "integrity": "sha512-Oj3cAGPCqOZX7Rz64Uny2GYAZNliQSqfbePrgAQ1wKAihYmCUnraBtJtKcGR4xz7wF+LoJC+ssFZvv5BgF9Igg==", - "dev": true, - "dependencies": { - "clone-response": "^1.0.2", - "get-stream": "^5.1.0", - "http-cache-semantics": "^4.0.0", - "keyv": "^3.0.0", - "lowercase-keys": "^2.0.0", - "normalize-url": "^4.1.0", - "responselike": "^1.0.2" - }, - "engines": { - "node": ">=8" - } - }, - "node_modules/package-json/node_modules/cacheable-request/node_modules/get-stream": { - "version": "5.2.0", - "resolved": "https://registry.npmjs.org/get-stream/-/get-stream-5.2.0.tgz", - "integrity": "sha512-nBF+F1rAZVCu/p7rjzgA+Yb4lfYXrpl7a6VmJrU8wF9I1CKvP/QwPNZHnOlwbTkY6dvtFIzFMSyQXbLoTQPRpA==", - "dev": true, - "dependencies": { - "pump": "^3.0.0" - }, - "engines": { - "node": ">=8" - }, - "funding": { - "url": "https://github.com/sponsors/sindresorhus" - } - }, - "node_modules/package-json/node_modules/decompress-response": { - "version": "3.3.0", - "resolved": "https://registry.npmjs.org/decompress-response/-/decompress-response-3.3.0.tgz", - "integrity": "sha1-gKTdMjdIOEv6JICDYirt7Jgq3/M=", - "dev": true, - "dependencies": { - "mimic-response": "^1.0.0" - }, - "engines": { - "node": ">=4" - } - }, - "node_modules/package-json/node_modules/defer-to-connect": { - "version": "1.1.3", - "resolved": "https://registry.npmjs.org/defer-to-connect/-/defer-to-connect-1.1.3.tgz", - "integrity": "sha512-0ISdNousHvZT2EiFlZeZAHBUvSxmKswVCEf8hW7KWgG4a8MVEu/3Vb6uWYozkjylyCxe0JBIiRB1jV45S70WVQ==", - "dev": true - }, - "node_modules/package-json/node_modules/get-stream": { - "version": "4.1.0", - "resolved": "https://registry.npmjs.org/get-stream/-/get-stream-4.1.0.tgz", - "integrity": "sha512-GMat4EJ5161kIy2HevLlr4luNjBgvmj413KaQA7jt4V8B4RDsfpHk7WQ9GVqfYyyx8OS/L66Kox+rJRNklLK7w==", - "dev": true, - "dependencies": { - "pump": "^3.0.0" - }, - "engines": { - "node": ">=6" - } - }, - "node_modules/package-json/node_modules/got": { - "version": "9.6.0", - "resolved": "https://registry.npmjs.org/got/-/got-9.6.0.tgz", - "integrity": "sha512-R7eWptXuGYxwijs0eV+v3o6+XH1IqVK8dJOEecQfTmkncw9AV4dcw/Dhxi8MdlqPthxxpZyizMzyg8RTmEsG+Q==", - "dev": true, - "dependencies": { - "@sindresorhus/is": "^0.14.0", - "@szmarczak/http-timer": "^1.1.2", - "cacheable-request": "^6.0.0", - "decompress-response": "^3.3.0", - "duplexer3": "^0.1.4", - "get-stream": "^4.1.0", - "lowercase-keys": "^1.0.1", - "mimic-response": "^1.0.1", - "p-cancelable": "^1.0.0", - "to-readable-stream": "^1.0.0", - "url-parse-lax": "^3.0.0" - }, - "engines": { - "node": ">=8.6" - } - }, - "node_modules/package-json/node_modules/got/node_modules/lowercase-keys": { - "version": "1.0.1", - "resolved": "https://registry.npmjs.org/lowercase-keys/-/lowercase-keys-1.0.1.tgz", - "integrity": "sha512-G2Lj61tXDnVFFOi8VZds+SoQjtQC3dgokKdDG2mTm1tx4m50NUHBOZSBwQQHyy0V12A0JTG4icfZQH+xPyh8VA==", - "dev": true, - "engines": { - "node": ">=0.10.0" - } - }, - "node_modules/package-json/node_modules/json-buffer": { - "version": "3.0.0", - "resolved": "https://registry.npmjs.org/json-buffer/-/json-buffer-3.0.0.tgz", - "integrity": "sha1-Wx85evx11ne96Lz8Dkfh+aPZqJg=", - "dev": true - }, - "node_modules/package-json/node_modules/keyv": { - "version": "3.1.0", - "resolved": "https://registry.npmjs.org/keyv/-/keyv-3.1.0.tgz", - "integrity": "sha512-9ykJ/46SN/9KPM/sichzQ7OvXyGDYKGTaDlKMGCAlg2UK8KRy4jb0d8sFc+0Tt0YYnThq8X2RZgCg74RPxgcVA==", - "dev": true, - "dependencies": { - "json-buffer": "3.0.0" - } - }, - "node_modules/package-json/node_modules/normalize-url": { - "version": "4.5.1", - "resolved": "https://registry.npmjs.org/normalize-url/-/normalize-url-4.5.1.tgz", - "integrity": "sha512-9UZCFRHQdNrfTpGg8+1INIg93B6zE0aXMVFkw1WFwvO4SlZywU6aLg5Of0Ap/PgcbSw4LNxvMWXMeugwMCX0AA==", - "dev": true, - "engines": { - "node": ">=8" - } - }, - "node_modules/package-json/node_modules/p-cancelable": { - "version": "1.1.0", - "resolved": "https://registry.npmjs.org/p-cancelable/-/p-cancelable-1.1.0.tgz", - "integrity": "sha512-s73XxOZ4zpt1edZYZzvhqFa6uvQc1vwUa0K0BdtIZgQMAJj9IbebH+JkgKZc9h+B05PKHLOTl4ajG1BmNrVZlw==", - "dev": true, - "engines": { - "node": ">=6" - } - }, - "node_modules/package-json/node_modules/responselike": { - "version": "1.0.2", - "resolved": "https://registry.npmjs.org/responselike/-/responselike-1.0.2.tgz", - "integrity": "sha1-kYcg7ztjHFZCvgaPFa3lpG9Loec=", - "dev": true, - "dependencies": { - "lowercase-keys": "^1.0.0" - } - }, - "node_modules/package-json/node_modules/responselike/node_modules/lowercase-keys": { - "version": "1.0.1", - "resolved": "https://registry.npmjs.org/lowercase-keys/-/lowercase-keys-1.0.1.tgz", - "integrity": "sha512-G2Lj61tXDnVFFOi8VZds+SoQjtQC3dgokKdDG2mTm1tx4m50NUHBOZSBwQQHyy0V12A0JTG4icfZQH+xPyh8VA==", - "dev": true, - "engines": { - "node": ">=0.10.0" - } - }, - "node_modules/package-json/node_modules/semver": { - "version": "6.3.0", - "resolved": "https://registry.npmjs.org/semver/-/semver-6.3.0.tgz", - "integrity": "sha512-b39TBaTSfV6yBrapU89p5fKekE2m/NwnDocOVruQFS1/veMgdzuPcnOM34M6CwxW8jH/lxEa5rBoDeUwu5HHTw==", - "dev": true, - "bin": { - "semver": "bin/semver.js" - } - }, - "node_modules/parent-module": { - "version": "1.0.1", - "resolved": "https://registry.npmjs.org/parent-module/-/parent-module-1.0.1.tgz", - "integrity": "sha512-GQ2EWRpQV8/o+Aw8YqtfZZPfNRWZYkbidE9k5rpl/hC3vtHHBfGm2Ifi6qWV+coDGkrUKZAxE3Lot5kcsRlh+g==", - "dev": true, - "dependencies": { - "callsites": "^3.0.0" - }, - "engines": { - "node": ">=6" - } - }, - "node_modules/parse-json": { - "version": "5.2.0", - "resolved": "https://registry.npmjs.org/parse-json/-/parse-json-5.2.0.tgz", - "integrity": "sha512-ayCKvm/phCGxOkYRSCM82iDwct8/EonSEgCSxWxD7ve6jHggsFl4fZVQBPRNgQoKiuV/odhFrGzQXZwbifC8Rg==", - "dev": true, - "dependencies": { - "@babel/code-frame": "^7.0.0", - "error-ex": "^1.3.1", - "json-parse-even-better-errors": "^2.3.0", - "lines-and-columns": "^1.1.6" - }, - "engines": { - "node": ">=8" - }, - "funding": { - "url": "https://github.com/sponsors/sindresorhus" - } - }, - "node_modules/parse-path": { - "version": "4.0.3", - "resolved": "https://registry.npmjs.org/parse-path/-/parse-path-4.0.3.tgz", - "integrity": "sha512-9Cepbp2asKnWTJ9x2kpw6Fe8y9JDbqwahGCTvklzd/cEq5C5JC59x2Xb0Kx+x0QZ8bvNquGO8/BWP0cwBHzSAA==", - "dev": true, - "dependencies": { - "is-ssh": "^1.3.0", - "protocols": "^1.4.0", - "qs": "^6.9.4", - "query-string": "^6.13.8" - } - }, - "node_modules/parse-url": { - "version": "6.0.0", - "resolved": "https://registry.npmjs.org/parse-url/-/parse-url-6.0.0.tgz", - "integrity": "sha512-cYyojeX7yIIwuJzledIHeLUBVJ6COVLeT4eF+2P6aKVzwvgKQPndCBv3+yQ7pcWjqToYwaligxzSYNNmGoMAvw==", - "dev": true, - "dependencies": { - "is-ssh": "^1.3.0", - "normalize-url": "^6.1.0", - "parse-path": "^4.0.0", - "protocols": "^1.4.0" - } - }, - "node_modules/path-exists": { - "version": "4.0.0", - "resolved": "https://registry.npmjs.org/path-exists/-/path-exists-4.0.0.tgz", - "integrity": "sha512-ak9Qy5Q7jYb2Wwcey5Fpvg2KoAc/ZIhLSLOSBmRmygPsGwkVVt0fZa0qrtMz+m6tJTAHfZQ8FnmB4MG4LWy7/w==", - "dev": true, - "engines": { - "node": ">=8" - } - }, - "node_modules/path-is-absolute": { - "version": "1.0.1", - "resolved": "https://registry.npmjs.org/path-is-absolute/-/path-is-absolute-1.0.1.tgz", - "integrity": "sha1-F0uSaHNVNP+8es5r9TpanhtcX18=", - "dev": true, - "engines": { - "node": ">=0.10.0" - } - }, - "node_modules/path-key": { - "version": "3.1.1", - "resolved": "https://registry.npmjs.org/path-key/-/path-key-3.1.1.tgz", - "integrity": "sha512-ojmeN0qd+y0jszEtoY48r0Peq5dwMEkIlCOu6Q5f41lfkswXuKtYrhgoTpLnyIcHm24Uhqx+5Tqm2InSwLhE6Q==", - "dev": true, - "engines": { - "node": ">=8" - } - }, - "node_modules/path-parse": { - "version": "1.0.7", - "resolved": "https://registry.npmjs.org/path-parse/-/path-parse-1.0.7.tgz", - "integrity": "sha512-LDJzPVEEEPR+y48z93A0Ed0yXb8pAByGWo/k5YYdYgpY2/2EsOsksJrq7lOHxryrVOn1ejG6oAp8ahvOIQD8sw==", - "dev": true - }, - "node_modules/path-to-regexp": { - "version": "1.8.0", - "resolved": "https://registry.npmjs.org/path-to-regexp/-/path-to-regexp-1.8.0.tgz", - "integrity": "sha512-n43JRhlUKUAlibEJhPeir1ncUID16QnEjNpwzNdO3Lm4ywrBpBZ5oLD0I6br9evr1Y9JTqwRtAh7JLoOzAQdVA==", - "dev": true, - "dependencies": { - "isarray": "0.0.1" - } - }, - "node_modules/path-type": { - "version": "4.0.0", - "resolved": "https://registry.npmjs.org/path-type/-/path-type-4.0.0.tgz", - "integrity": "sha512-gDKb8aZMDeD/tZWs9P6+q0J9Mwkdl6xMV8TjnGP3qJVJ06bdMgkbBlLU8IdfOsIsFz2BW1rNVT3XuNEl8zPAvw==", - "dev": true, - "engines": { - "node": ">=8" - } - }, - "node_modules/picocolors": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/picocolors/-/picocolors-1.0.0.tgz", - "integrity": "sha512-1fygroTLlHu66zi26VoTDv8yRgm0Fccecssto+MhsZ0D/DGW2sm8E8AjW7NU5VVTRt5GxbeZ5qBuJr+HyLYkjQ==", - "dev": true - }, - "node_modules/picomatch": { - "version": "2.3.0", - "resolved": "https://registry.npmjs.org/picomatch/-/picomatch-2.3.0.tgz", - "integrity": "sha512-lY1Q/PiJGC2zOv/z391WOTD+Z02bCgsFfvxoXXf6h7kv9o+WmsmzYqrAwY63sNgOxE4xEdq0WyUnXfKeBrSvYw==", - "dev": true, - "engines": { - "node": ">=8.6" - }, - "funding": { - "url": "https://github.com/sponsors/jonschlinkert" - } - }, - "node_modules/pkg-dir": { - "version": "4.2.0", - "resolved": "https://registry.npmjs.org/pkg-dir/-/pkg-dir-4.2.0.tgz", - "integrity": "sha512-HRDzbaKjC+AOWVXxAU/x54COGeIv9eb+6CkDSQoNTt4XyWoIJvuPsXizxu/Fr23EiekbtZwmh1IcIG/l/a10GQ==", - "dev": true, - "dependencies": { - "find-up": "^4.0.0" - }, - "engines": { - "node": ">=8" - } - }, - "node_modules/pkg-dir/node_modules/find-up": { - "version": "4.1.0", - "resolved": "https://registry.npmjs.org/find-up/-/find-up-4.1.0.tgz", - "integrity": "sha512-PpOwAdQ/YlXQ2vj8a3h8IipDuYRi3wceVQQGYWxNINccq40Anw7BlsEXCMbt1Zt+OLA6Fq9suIpIWD0OsnISlw==", - "dev": true, - "dependencies": { - "locate-path": "^5.0.0", - "path-exists": "^4.0.0" - }, - "engines": { - "node": ">=8" - } - }, - "node_modules/pkg-dir/node_modules/locate-path": { - "version": "5.0.0", - "resolved": "https://registry.npmjs.org/locate-path/-/locate-path-5.0.0.tgz", - "integrity": "sha512-t7hw9pI+WvuwNJXwk5zVHpyhIqzg2qTlklJOf0mVxGSbe3Fp2VieZcduNYjaLDoy6p9uGpQEGWG87WpMKlNq8g==", - "dev": true, - "dependencies": { - "p-locate": "^4.1.0" - }, - "engines": { - "node": ">=8" - } - }, - "node_modules/pkg-dir/node_modules/p-limit": { - "version": "2.3.0", - "resolved": "https://registry.npmjs.org/p-limit/-/p-limit-2.3.0.tgz", - "integrity": "sha512-//88mFWSJx8lxCzwdAABTJL2MyWB12+eIY7MDL2SqLmAkeKU9qxRvWuSyTjm3FUmpBEMuFfckAIqEaVGUDxb6w==", - "dev": true, - "dependencies": { - "p-try": "^2.0.0" - }, - "engines": { - "node": ">=6" - }, - "funding": { - "url": "https://github.com/sponsors/sindresorhus" - } - }, - "node_modules/pkg-dir/node_modules/p-locate": { - "version": "4.1.0", - "resolved": "https://registry.npmjs.org/p-locate/-/p-locate-4.1.0.tgz", - "integrity": "sha512-R79ZZ/0wAxKGu3oYMlz8jy/kbhsNrS7SKZ7PxEHBgJ5+F2mtFW2fK2cOtBh1cHYkQsbzFV7I+EoRKe6Yt0oK7A==", - "dev": true, - "dependencies": { - "p-limit": "^2.2.0" - }, - "engines": { - "node": ">=8" - } - }, - "node_modules/prelude-ls": { - "version": "1.2.1", - "resolved": "https://registry.npmjs.org/prelude-ls/-/prelude-ls-1.2.1.tgz", - "integrity": "sha512-vkcDPrRZo1QZLbn5RLGPpg/WmIQ65qoWWhcGKf/b5eplkkarX0m9z8ppCat4mlOqUsWpyNuYgO3VRyrYHSzX5g==", - "dev": true, - "engines": { - "node": ">= 0.8.0" - } - }, - "node_modules/prepend-http": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/prepend-http/-/prepend-http-2.0.0.tgz", - "integrity": "sha1-6SQ0v6XqjBn0HN/UAddBo8gZ2Jc=", - "dev": true, - "engines": { - "node": ">=4" - } - }, - "node_modules/process-on-spawn": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/process-on-spawn/-/process-on-spawn-1.0.0.tgz", - "integrity": "sha512-1WsPDsUSMmZH5LeMLegqkPDrsGgsWwk1Exipy2hvB0o/F0ASzbpIctSCcZIK1ykJvtTJULEH+20WOFjMvGnCTg==", - "dev": true, - "dependencies": { - "fromentries": "^1.2.0" - }, - "engines": { - "node": ">=8" - } - }, - "node_modules/progress": { - "version": "2.0.3", - "resolved": "https://registry.npmjs.org/progress/-/progress-2.0.3.tgz", - "integrity": "sha512-7PiHtLll5LdnKIMw100I+8xJXR5gW2QwWYkT6iJva0bXitZKa/XMrSbdmg3r2Xnaidz9Qumd0VPaMrZlF9V9sA==", - "dev": true, - "engines": { - "node": ">=0.4.0" - } - }, - "node_modules/protocols": { - "version": "1.4.8", - "resolved": "https://registry.npmjs.org/protocols/-/protocols-1.4.8.tgz", - "integrity": "sha512-IgjKyaUSjsROSO8/D49Ab7hP8mJgTYcqApOqdPhLoPxAplXmkp+zRvsrSQjFn5by0rhm4VH0GAUELIPpx7B1yg==", - "dev": true - }, - "node_modules/pump": { - "version": "3.0.0", - "resolved": "https://registry.npmjs.org/pump/-/pump-3.0.0.tgz", - "integrity": "sha512-LwZy+p3SFs1Pytd/jYct4wpv49HiYCqd9Rlc5ZVdk0V+8Yzv6jR5Blk3TRmPL1ft69TxP0IMZGJ+WPFU2BFhww==", - "dev": true, - "dependencies": { - "end-of-stream": "^1.1.0", - "once": "^1.3.1" - } - }, - "node_modules/punycode": { - "version": "2.1.1", - "resolved": "https://registry.npmjs.org/punycode/-/punycode-2.1.1.tgz", - "integrity": "sha512-XRsRjdf+j5ml+y/6GKHPZbrF/8p2Yga0JPtdqTIY2Xe5ohJPD9saDJJLPvp9+NSBprVvevdXZybnj2cv8OEd0A==", - "dev": true, - "engines": { - "node": ">=6" - } - }, - "node_modules/pupa": { - "version": "2.1.1", - "resolved": "https://registry.npmjs.org/pupa/-/pupa-2.1.1.tgz", - "integrity": "sha512-l1jNAspIBSFqbT+y+5FosojNpVpF94nlI+wDUpqP9enwOTfHx9f0gh5nB96vl+6yTpsJsypeNrwfzPrKuHB41A==", - "dev": true, - "dependencies": { - "escape-goat": "^2.0.0" - }, - "engines": { - "node": ">=8" - } - }, - "node_modules/qs": { - "version": "6.10.1", - "resolved": "https://registry.npmjs.org/qs/-/qs-6.10.1.tgz", - "integrity": "sha512-M528Hph6wsSVOBiYUnGf+K/7w0hNshs/duGsNXPUCLH5XAqjEtiPGwNONLV0tBH8NoGb0mvD5JubnUTrujKDTg==", - "dev": true, - "dependencies": { - "side-channel": "^1.0.4" - }, - "engines": { - "node": ">=0.6" - }, - "funding": { - "url": "https://github.com/sponsors/ljharb" - } - }, - "node_modules/query-string": { - "version": "6.14.1", - "resolved": "https://registry.npmjs.org/query-string/-/query-string-6.14.1.tgz", - "integrity": "sha512-XDxAeVmpfu1/6IjyT/gXHOl+S0vQ9owggJ30hhWKdHAsNPOcasn5o9BW0eejZqL2e4vMjhAxoW3jVHcD6mbcYw==", - "dev": true, - "dependencies": { - "decode-uri-component": "^0.2.0", - "filter-obj": "^1.1.0", - "split-on-first": "^1.0.0", - "strict-uri-encode": "^2.0.0" - }, - "engines": { - "node": ">=6" - }, - "funding": { - "url": "https://github.com/sponsors/sindresorhus" - } - }, - "node_modules/queue-microtask": { - "version": "1.2.3", - "resolved": "https://registry.npmjs.org/queue-microtask/-/queue-microtask-1.2.3.tgz", - "integrity": "sha512-NuaNSa6flKT5JaSYQzJok04JzTL1CA6aGhv5rfLW3PgqA+M2ChpZQnAC8h8i4ZFkBS8X5RqkDBHA7r4hej3K9A==", - "dev": true, - "funding": [ - { - "type": "github", - "url": "https://github.com/sponsors/feross" - }, - { - "type": "patreon", - "url": "https://www.patreon.com/feross" - }, - { - "type": "consulting", - "url": "https://feross.org/support" - } - ] - }, - "node_modules/quick-lru": { - "version": "5.1.1", - "resolved": "https://registry.npmjs.org/quick-lru/-/quick-lru-5.1.1.tgz", - "integrity": "sha512-WuyALRjWPDGtt/wzJiadO5AXY+8hZ80hVpe6MyivgraREW751X3SbhRvG3eLKOYN+8VEvqLcf3wdnt44Z4S4SA==", - "dev": true, - "engines": { - "node": ">=10" - }, - "funding": { - "url": "https://github.com/sponsors/sindresorhus" - } - }, - "node_modules/randombytes": { - "version": "2.1.0", - "resolved": "https://registry.npmjs.org/randombytes/-/randombytes-2.1.0.tgz", - "integrity": "sha512-vYl3iOX+4CKUWuxGi9Ukhie6fsqXqS9FE2Zaic4tNFD2N2QQaXOMFbuKK4QmDHC0JO6B1Zp41J0LpT0oR68amQ==", - "dev": true, - "dependencies": { - "safe-buffer": "^5.1.0" - } - }, - "node_modules/rc": { - "version": "1.2.8", - "resolved": "https://registry.npmjs.org/rc/-/rc-1.2.8.tgz", - "integrity": "sha512-y3bGgqKj3QBdxLbLkomlohkvsA8gdAiUQlSBJnBhfn+BPxg4bc62d8TcBW15wavDfgexCgccckhcZvywyQYPOw==", - "dev": true, - "dependencies": { - "deep-extend": "^0.6.0", - "ini": "~1.3.0", - "minimist": "^1.2.0", - "strip-json-comments": "~2.0.1" - }, - "bin": { - "rc": "cli.js" - } - }, - "node_modules/rc/node_modules/ini": { - "version": "1.3.8", - "resolved": "https://registry.npmjs.org/ini/-/ini-1.3.8.tgz", - "integrity": "sha512-JV/yugV2uzW5iMRSiZAyDtQd+nxtUnjeLt0acNdw98kKLrvuRVyB80tsREOE7yvGVgalhZ6RNXCmEHkUKBKxew==", - "dev": true - }, - "node_modules/rc/node_modules/strip-json-comments": { - "version": "2.0.1", - "resolved": "https://registry.npmjs.org/strip-json-comments/-/strip-json-comments-2.0.1.tgz", - "integrity": "sha1-PFMZQukIwml8DsNEhYwobHygpgo=", - "dev": true, - "engines": { - "node": ">=0.10.0" - } - }, - "node_modules/readable-stream": { - "version": "3.6.0", - "resolved": "https://registry.npmjs.org/readable-stream/-/readable-stream-3.6.0.tgz", - "integrity": "sha512-BViHy7LKeTz4oNnkcLJ+lVSL6vpiFeX6/d3oSH8zCW7UxP2onchk+vTGB143xuFjHS3deTgkKoXXymXqymiIdA==", - "dev": true, - "dependencies": { - "inherits": "^2.0.3", - "string_decoder": "^1.1.1", - "util-deprecate": "^1.0.1" - }, - "engines": { - "node": ">= 6" - } - }, - "node_modules/readdirp": { - "version": "3.6.0", - "resolved": "https://registry.npmjs.org/readdirp/-/readdirp-3.6.0.tgz", - "integrity": "sha512-hOS089on8RduqdbhvQ5Z37A0ESjsqz6qnRcffsMU3495FuTdqSm+7bhJ29JvIOsBDEEnan5DPu9t3To9VRlMzA==", - "dev": true, - "dependencies": { - "picomatch": "^2.2.1" - }, - "engines": { - "node": ">=8.10.0" - } - }, - "node_modules/rechoir": { - "version": "0.6.2", - "resolved": "https://registry.npmjs.org/rechoir/-/rechoir-0.6.2.tgz", - "integrity": "sha1-hSBLVNuoLVdC4oyWdW70OvUOM4Q=", - "dev": true, - "dependencies": { - "resolve": "^1.1.6" - }, - "engines": { - "node": ">= 0.10" - } - }, - "node_modules/redis": { - "resolved": "packages/all-in-one", - "link": true - }, - "node_modules/redis-errors": { - "version": "1.2.0", - "resolved": "https://registry.npmjs.org/redis-errors/-/redis-errors-1.2.0.tgz", - "integrity": "sha1-62LSrbFeTq9GEMBK/hUpOEJQq60=", - "engines": { - "node": ">=4" - } - }, - "node_modules/redis-parser": { - "version": "3.0.0", - "resolved": "https://registry.npmjs.org/redis-parser/-/redis-parser-3.0.0.tgz", - "integrity": "sha1-tm2CjNyv5rS4pCin3vTGvKwxyLQ=", - "dependencies": { - "redis-errors": "^1.0.0" - }, - "engines": { - "node": ">=4" - } - }, - "node_modules/regexpp": { - "version": "3.2.0", - "resolved": "https://registry.npmjs.org/regexpp/-/regexpp-3.2.0.tgz", - "integrity": "sha512-pq2bWo9mVD43nbts2wGv17XLiNLya+GklZ8kaDLV2Z08gDCsGpnKn9BFMepvWuHCbyVvY7J5o5+BVvoQbmlJLg==", - "dev": true, - "engines": { - "node": ">=8" - }, - "funding": { - "url": "https://github.com/sponsors/mysticatea" - } - }, - "node_modules/registry-auth-token": { - "version": "4.2.1", - "resolved": "https://registry.npmjs.org/registry-auth-token/-/registry-auth-token-4.2.1.tgz", - "integrity": "sha512-6gkSb4U6aWJB4SF2ZvLb76yCBjcvufXBqvvEx1HbmKPkutswjW1xNVRY0+daljIYRbogN7O0etYSlbiaEQyMyw==", - "dev": true, - "dependencies": { - "rc": "^1.2.8" - }, - "engines": { - "node": ">=6.0.0" - } - }, - "node_modules/registry-url": { - "version": "5.1.0", - "resolved": "https://registry.npmjs.org/registry-url/-/registry-url-5.1.0.tgz", - "integrity": "sha512-8acYXXTI0AkQv6RAOjE3vOaIXZkT9wo4LOFbBKYQEEnnMNBpKqdUrI6S4NT0KPIo/WVvJ5tE/X5LF/TQUf0ekw==", - "dev": true, - "dependencies": { - "rc": "^1.2.8" - }, - "engines": { - "node": ">=8" - } - }, - "node_modules/release-it": { - "version": "14.11.6", - "resolved": "https://registry.npmjs.org/release-it/-/release-it-14.11.6.tgz", - "integrity": "sha512-6BNcuzFZHThBUBJ/xYw/bxZ+58CAwrwf1zgmjq2Ibl3nlDZbjphHG6iqxkJu7mZ8TIWs6NjloEAhqpjeXoN//Q==", - "dev": true, - "dependencies": { - "@iarna/toml": "2.2.5", - "@octokit/rest": "18.10.0", - "async-retry": "1.3.3", - "chalk": "4.1.2", - "cosmiconfig": "7.0.1", - "debug": "4.3.2", - "deprecated-obj": "2.0.0", - "execa": "5.1.1", - "form-data": "4.0.0", - "git-url-parse": "11.6.0", - "globby": "11.0.4", - "got": "11.8.2", - "import-cwd": "3.0.0", - "inquirer": "8.1.5", - "is-ci": "3.0.0", - "lodash": "4.17.21", - "mime-types": "2.1.32", - "new-github-release-url": "1.0.0", - "open": "7.4.2", - "ora": "5.4.1", - "os-name": "4.0.1", - "parse-json": "5.2.0", - "semver": "7.3.5", - "shelljs": "0.8.4", - "update-notifier": "5.1.0", - "url-join": "4.0.1", - "uuid": "8.3.2", - "yaml": "1.10.2", - "yargs-parser": "20.2.9" - }, - "bin": { - "release-it": "bin/release-it.js" - }, - "engines": { - "node": ">=10" - } - }, - "node_modules/release-it/node_modules/uuid": { - "version": "8.3.2", - "resolved": "https://registry.npmjs.org/uuid/-/uuid-8.3.2.tgz", - "integrity": "sha512-+NYs2QeMWy+GWFOEm9xnn6HCDp0l7QBD7ml8zLUmJ+93Q5NF0NocErnwkTkXVFNiX3/fpC6afS8Dhb/gz7R7eg==", - "dev": true, - "bin": { - "uuid": "dist/bin/uuid" - } - }, - "node_modules/release-it/node_modules/yargs-parser": { - "version": "20.2.9", - "resolved": "https://registry.npmjs.org/yargs-parser/-/yargs-parser-20.2.9.tgz", - "integrity": "sha512-y11nGElTIV+CT3Zv9t7VKl+Q3hTQoT9a1Qzezhhl6Rp21gJ/IVTW7Z3y9EWXhuUBC2Shnf+DX0antecpAwSP8w==", - "dev": true, - "engines": { - "node": ">=10" - } - }, - "node_modules/release-zalgo": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/release-zalgo/-/release-zalgo-1.0.0.tgz", - "integrity": "sha1-CXALflB0Mpc5Mw5TXFqQ+2eFFzA=", - "dev": true, - "dependencies": { - "es6-error": "^4.0.1" - }, - "engines": { - "node": ">=4" - } - }, - "node_modules/require-directory": { - "version": "2.1.1", - "resolved": "https://registry.npmjs.org/require-directory/-/require-directory-2.1.1.tgz", - "integrity": "sha1-jGStX9MNqxyXbiNE/+f3kqam30I=", - "dev": true, - "engines": { - "node": ">=0.10.0" - } - }, - "node_modules/require-main-filename": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/require-main-filename/-/require-main-filename-2.0.0.tgz", - "integrity": "sha512-NKN5kMDylKuldxYLSUfrbo5Tuzh4hd+2E8NPPX02mZtn1VuREQToYe/ZdlJy+J3uCpfaiGF05e7B8W0iXbQHmg==", - "dev": true - }, - "node_modules/resolve": { - "version": "1.20.0", - "resolved": "https://registry.npmjs.org/resolve/-/resolve-1.20.0.tgz", - "integrity": "sha512-wENBPt4ySzg4ybFQW2TT1zMQucPK95HSh/nq2CFTZVOGut2+pQvSsgtda4d26YrYcr067wjbmzOG8byDPBX63A==", - "dev": true, - "dependencies": { - "is-core-module": "^2.2.0", - "path-parse": "^1.0.6" - }, - "funding": { - "url": "https://github.com/sponsors/ljharb" - } - }, - "node_modules/resolve-alpn": { - "version": "1.2.1", - "resolved": "https://registry.npmjs.org/resolve-alpn/-/resolve-alpn-1.2.1.tgz", - "integrity": "sha512-0a1F4l73/ZFZOakJnQ3FvkJ2+gSTQWz/r2KE5OdDY0TxPm5h4GkqkWWfM47T7HsbnOtcJVEF4epCVy6u7Q3K+g==", - "dev": true - }, - "node_modules/resolve-from": { - "version": "4.0.0", - "resolved": "https://registry.npmjs.org/resolve-from/-/resolve-from-4.0.0.tgz", - "integrity": "sha512-pb/MYmXstAkysRFx8piNI1tGFNQIFA3vkE3Gq4EuA1dF6gHp/+vgZqsCGJapvy8N3Q+4o7FwvquPJcnZ7RYy4g==", - "dev": true, - "engines": { - "node": ">=4" - } - }, - "node_modules/responselike": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/responselike/-/responselike-2.0.0.tgz", - "integrity": "sha512-xH48u3FTB9VsZw7R+vvgaKeLKzT6jOogbQhEe/jewwnZgzPcnyWui2Av6JpoYZF/91uueC+lqhWqeURw5/qhCw==", - "dev": true, - "dependencies": { - "lowercase-keys": "^2.0.0" - } - }, - "node_modules/restore-cursor": { - "version": "3.1.0", - "resolved": "https://registry.npmjs.org/restore-cursor/-/restore-cursor-3.1.0.tgz", - "integrity": "sha512-l+sSefzHpj5qimhFSE5a8nufZYAM3sBSVMAPtYkmC+4EH2anSGaEMXSD0izRQbu9nfyQ9y5JrVmp7E8oZrUjvA==", - "dev": true, - "dependencies": { - "onetime": "^5.1.0", - "signal-exit": "^3.0.2" - }, - "engines": { - "node": ">=8" - } - }, - "node_modules/retry": { - "version": "0.13.1", - "resolved": "https://registry.npmjs.org/retry/-/retry-0.13.1.tgz", - "integrity": "sha512-XQBQ3I8W1Cge0Seh+6gjj03LbmRFWuoszgK9ooCpwYIrhhoO80pfq4cUkU5DkknwfOfFteRwlZ56PYOGYyFWdg==", - "dev": true, - "engines": { - "node": ">= 4" - } - }, - "node_modules/reusify": { - "version": "1.0.4", - "resolved": "https://registry.npmjs.org/reusify/-/reusify-1.0.4.tgz", - "integrity": "sha512-U9nH88a3fc/ekCF1l0/UP1IosiuIjyTh7hBvXVMHYgVcfGvt897Xguj2UOLDeI5BG2m7/uwyaLVT6fbtCwTyzw==", - "dev": true, - "engines": { - "iojs": ">=1.0.0", - "node": ">=0.10.0" - } - }, - "node_modules/rimraf": { - "version": "3.0.2", - "resolved": "https://registry.npmjs.org/rimraf/-/rimraf-3.0.2.tgz", - "integrity": "sha512-JZkJMZkAGFFPP2YqXZXPbMlMBgsxzE8ILs4lMIX/2o0L9UBw9O/Y3o6wFw/i9YLapcUJWwqbi3kdxIPdC62TIA==", - "dev": true, - "dependencies": { - "glob": "^7.1.3" - }, - "bin": { - "rimraf": "bin.js" - }, - "funding": { - "url": "https://github.com/sponsors/isaacs" - } - }, - "node_modules/run-async": { - "version": "2.4.1", - "resolved": "https://registry.npmjs.org/run-async/-/run-async-2.4.1.tgz", - "integrity": "sha512-tvVnVv01b8c1RrA6Ep7JkStj85Guv/YrMcwqYQnwjsAS2cTmmPGBBjAjpCW7RrSodNSoE2/qg9O4bceNvUuDgQ==", - "dev": true, - "engines": { - "node": ">=0.12.0" - } - }, - "node_modules/run-parallel": { - "version": "1.2.0", - "resolved": "https://registry.npmjs.org/run-parallel/-/run-parallel-1.2.0.tgz", - "integrity": "sha512-5l4VyZR86LZ/lDxZTR6jqL8AFE2S0IFLMP26AbjsLVADxHdhB/c0GUsH+y39UfCi3dzz8OlQuPmnaJOMoDHQBA==", - "dev": true, - "funding": [ - { - "type": "github", - "url": "https://github.com/sponsors/feross" - }, - { - "type": "patreon", - "url": "https://www.patreon.com/feross" - }, - { - "type": "consulting", - "url": "https://feross.org/support" - } - ], - "dependencies": { - "queue-microtask": "^1.2.2" - } - }, - "node_modules/rxjs": { - "version": "7.4.0", - "resolved": "https://registry.npmjs.org/rxjs/-/rxjs-7.4.0.tgz", - "integrity": "sha512-7SQDi7xeTMCJpqViXh8gL/lebcwlp3d831F05+9B44A4B0WfsEwUQHR64gsH1kvJ+Ep/J9K2+n1hVl1CsGN23w==", - "dev": true, - "dependencies": { - "tslib": "~2.1.0" - } - }, - "node_modules/safe-buffer": { - "version": "5.1.2", - "resolved": "https://registry.npmjs.org/safe-buffer/-/safe-buffer-5.1.2.tgz", - "integrity": "sha512-Gd2UZBJDkXlY7GbJxfsE8/nvKkUEU1G38c1siN6QP6a9PT9MmHB8GnpscSmMJSoF8LOIrt8ud/wPtojys4G6+g==", - "dev": true - }, - "node_modules/safer-buffer": { - "version": "2.1.2", - "resolved": "https://registry.npmjs.org/safer-buffer/-/safer-buffer-2.1.2.tgz", - "integrity": "sha512-YZo3K82SD7Riyi0E1EQPojLz7kpepnSQI9IyPbHHg1XXXevb5dJI7tpyN2ADxGcQbHG7vcyRHk0cbwqcQriUtg==", - "dev": true - }, - "node_modules/semver": { - "version": "7.3.5", - "resolved": "https://registry.npmjs.org/semver/-/semver-7.3.5.tgz", - "integrity": "sha512-PoeGJYh8HK4BTO/a9Tf6ZG3veo/A7ZVsYrSA6J8ny9nb3B1VrpkuN+z9OE5wfE5p6H4LchYZsegiQgbJD94ZFQ==", - "dev": true, - "dependencies": { - "lru-cache": "^6.0.0" - }, - "bin": { - "semver": "bin/semver.js" - }, - "engines": { - "node": ">=10" - } - }, - "node_modules/semver-diff": { - "version": "3.1.1", - "resolved": "https://registry.npmjs.org/semver-diff/-/semver-diff-3.1.1.tgz", - "integrity": "sha512-GX0Ix/CJcHyB8c4ykpHGIAvLyOwOobtM/8d+TQkAd81/bEjgPHrfba41Vpesr7jX/t8Uh+R3EX9eAS5be+jQYg==", - "dev": true, - "dependencies": { - "semver": "^6.3.0" - }, - "engines": { - "node": ">=8" - } - }, - "node_modules/semver-diff/node_modules/semver": { - "version": "6.3.0", - "resolved": "https://registry.npmjs.org/semver/-/semver-6.3.0.tgz", - "integrity": "sha512-b39TBaTSfV6yBrapU89p5fKekE2m/NwnDocOVruQFS1/veMgdzuPcnOM34M6CwxW8jH/lxEa5rBoDeUwu5HHTw==", - "dev": true, - "bin": { - "semver": "bin/semver.js" - } - }, - "node_modules/serialize-javascript": { - "version": "6.0.0", - "resolved": "https://registry.npmjs.org/serialize-javascript/-/serialize-javascript-6.0.0.tgz", - "integrity": "sha512-Qr3TosvguFt8ePWqsvRfrKyQXIiW+nGbYpy8XK24NQHE83caxWt+mIymTT19DGFbNWNLfEwsrkSmN64lVWB9ag==", - "dev": true, - "dependencies": { - "randombytes": "^2.1.0" - } - }, - "node_modules/set-blocking": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/set-blocking/-/set-blocking-2.0.0.tgz", - "integrity": "sha1-BF+XgtARrppoA93TgrJDkrPYkPc=", - "dev": true - }, - "node_modules/shebang-command": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/shebang-command/-/shebang-command-2.0.0.tgz", - "integrity": "sha512-kHxr2zZpYtdmrN1qDjrrX/Z1rR1kG8Dx+gkpK1G4eXmvXswmcE1hTWBWYUzlraYw1/yZp6YuDY77YtvbN0dmDA==", - "dev": true, - "dependencies": { - "shebang-regex": "^3.0.0" - }, - "engines": { - "node": ">=8" - } - }, - "node_modules/shebang-regex": { - "version": "3.0.0", - "resolved": "https://registry.npmjs.org/shebang-regex/-/shebang-regex-3.0.0.tgz", - "integrity": "sha512-7++dFhtcx3353uBaq8DDR4NuxBetBzC7ZQOhmTQInHEd6bSrXdiEyzCvG07Z44UYdLShWUyXt5M/yhz8ekcb1A==", - "dev": true, - "engines": { - "node": ">=8" - } - }, - "node_modules/shelljs": { - "version": "0.8.4", - "resolved": "https://registry.npmjs.org/shelljs/-/shelljs-0.8.4.tgz", - "integrity": "sha512-7gk3UZ9kOfPLIAbslLzyWeGiEqx9e3rxwZM0KE6EL8GlGwjym9Mrlx5/p33bWTu9YG6vcS4MBxYZDHYr5lr8BQ==", - "dev": true, - "dependencies": { - "glob": "^7.0.0", - "interpret": "^1.0.0", - "rechoir": "^0.6.2" - }, - "bin": { - "shjs": "bin/shjs" - }, - "engines": { - "node": ">=4" - } - }, - "node_modules/shiki": { - "version": "0.9.12", - "resolved": "https://registry.npmjs.org/shiki/-/shiki-0.9.12.tgz", - "integrity": "sha512-VXcROdldv0/Qu0w2XvzU4IrvTeBNs/Kj/FCmtcEXGz7Tic/veQzliJj6tEiAgoKianhQstpYmbPDStHU5Opqcw==", - "dev": true, - "dependencies": { - "jsonc-parser": "^3.0.0", - "onigasm": "^2.2.5", - "vscode-textmate": "5.2.0" - } - }, - "node_modules/side-channel": { - "version": "1.0.4", - "resolved": "https://registry.npmjs.org/side-channel/-/side-channel-1.0.4.tgz", - "integrity": "sha512-q5XPytqFEIKHkGdiMIrY10mvLRvnQh42/+GoBlFW3b2LXLE2xxJpZFdm94we0BaoV3RwJyGqg5wS7epxTv0Zvw==", - "dev": true, - "dependencies": { - "call-bind": "^1.0.0", - "get-intrinsic": "^1.0.2", - "object-inspect": "^1.9.0" - }, - "funding": { - "url": "https://github.com/sponsors/ljharb" - } - }, - "node_modules/signal-exit": { - "version": "3.0.5", - "resolved": "https://registry.npmjs.org/signal-exit/-/signal-exit-3.0.5.tgz", - "integrity": "sha512-KWcOiKeQj6ZyXx7zq4YxSMgHRlod4czeBQZrPb8OKcohcqAXShm7E20kEMle9WBt26hFcAf0qLOcp5zmY7kOqQ==", - "dev": true - }, - "node_modules/sinon": { - "version": "11.1.2", - "resolved": "https://registry.npmjs.org/sinon/-/sinon-11.1.2.tgz", - "integrity": "sha512-59237HChms4kg7/sXhiRcUzdSkKuydDeTiamT/jesUVHshBgL8XAmhgFo0GfK6RruMDM/iRSij1EybmMog9cJw==", - "dev": true, - "dependencies": { - "@sinonjs/commons": "^1.8.3", - "@sinonjs/fake-timers": "^7.1.2", - "@sinonjs/samsam": "^6.0.2", - "diff": "^5.0.0", - "nise": "^5.1.0", - "supports-color": "^7.2.0" - }, - "funding": { - "type": "opencollective", - "url": "https://opencollective.com/sinon" - } - }, - "node_modules/slash": { - "version": "3.0.0", - "resolved": "https://registry.npmjs.org/slash/-/slash-3.0.0.tgz", - "integrity": "sha512-g9Q1haeby36OSStwb4ntCGGGaKsaVSjQ68fBxoQcutl5fS1vuY18H3wSt3jFyFtrkx+Kz0V1G85A4MyAdDMi2Q==", - "dev": true, - "engines": { - "node": ">=8" - } - }, - "node_modules/source-map": { - "version": "0.5.7", - "resolved": "https://registry.npmjs.org/source-map/-/source-map-0.5.7.tgz", - "integrity": "sha1-igOdLRAh0i0eoUyA2OpGi6LvP8w=", - "dev": true, - "engines": { - "node": ">=0.10.0" - } - }, - "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==", - "dev": true, - "dependencies": { - "buffer-from": "^1.0.0", - "source-map": "^0.6.0" - } - }, - "node_modules/source-map-support/node_modules/source-map": { - "version": "0.6.1", - "resolved": "https://registry.npmjs.org/source-map/-/source-map-0.6.1.tgz", - "integrity": "sha512-UjgapumWlbMhkBgzT7Ykc5YXUT46F0iKu8SGXq0bcwP5dz/h0Plj6enJqjz1Zbq2l5WaqYnrVbwWOWMyF3F47g==", - "dev": true, - "engines": { - "node": ">=0.10.0" - } - }, - "node_modules/spawn-wrap": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/spawn-wrap/-/spawn-wrap-2.0.0.tgz", - "integrity": "sha512-EeajNjfN9zMnULLwhZZQU3GWBoFNkbngTUPfaawT4RkMiviTxcX0qfhVbGey39mfctfDHkWtuecgQ8NJcyQWHg==", - "dev": true, - "dependencies": { - "foreground-child": "^2.0.0", - "is-windows": "^1.0.2", - "make-dir": "^3.0.0", - "rimraf": "^3.0.0", - "signal-exit": "^3.0.2", - "which": "^2.0.1" - }, - "engines": { - "node": ">=8" - } - }, - "node_modules/split-on-first": { - "version": "1.1.0", - "resolved": "https://registry.npmjs.org/split-on-first/-/split-on-first-1.1.0.tgz", - "integrity": "sha512-43ZssAJaMusuKWL8sKUBQXHWOpq8d6CfN/u1p4gUzfJkM05C8rxTmYrkIPTXapZpORA6LkkzcUulJ8FqA7Uudw==", - "dev": true, - "engines": { - "node": ">=6" - } - }, - "node_modules/sprintf-js": { - "version": "1.0.3", - "resolved": "https://registry.npmjs.org/sprintf-js/-/sprintf-js-1.0.3.tgz", - "integrity": "sha1-BOaSb2YolTVPPdAVIDYzuFcpfiw=", - "dev": true - }, - "node_modules/strict-uri-encode": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/strict-uri-encode/-/strict-uri-encode-2.0.0.tgz", - "integrity": "sha1-ucczDHBChi9rFC3CdLvMWGbONUY=", - "dev": true, - "engines": { - "node": ">=4" - } - }, - "node_modules/string_decoder": { - "version": "1.3.0", - "resolved": "https://registry.npmjs.org/string_decoder/-/string_decoder-1.3.0.tgz", - "integrity": "sha512-hkRX8U1WjJFd8LsDJ2yQ/wWWxaopEsABU1XfkM8A+j0+85JAGppt16cr1Whg6KIbb4okU6Mql6BOj+uup/wKeA==", - "dev": true, - "dependencies": { - "safe-buffer": "~5.2.0" - } - }, - "node_modules/string_decoder/node_modules/safe-buffer": { - "version": "5.2.1", - "resolved": "https://registry.npmjs.org/safe-buffer/-/safe-buffer-5.2.1.tgz", - "integrity": "sha512-rp3So07KcdmmKbGvgaNxQSJr7bGVSVk5S9Eq1F+ppbRo70+YeaDxkw5Dd8NPN+GD6bjnYm2VuPuCXmpuYvmCXQ==", - "dev": true, - "funding": [ - { - "type": "github", - "url": "https://github.com/sponsors/feross" - }, - { - "type": "patreon", - "url": "https://www.patreon.com/feross" - }, - { - "type": "consulting", - "url": "https://feross.org/support" - } - ] - }, - "node_modules/string-width": { - "version": "4.2.3", - "resolved": "https://registry.npmjs.org/string-width/-/string-width-4.2.3.tgz", - "integrity": "sha512-wKyQRQpjJ0sIp62ErSZdGsjMJWsap5oRNihHhu6G7JVO/9jIB6UyevL+tXuOqrng8j/cxKTWyWUwvSTriiZz/g==", - "dev": true, - "dependencies": { - "emoji-regex": "^8.0.0", - "is-fullwidth-code-point": "^3.0.0", - "strip-ansi": "^6.0.1" - }, - "engines": { - "node": ">=8" - } - }, - "node_modules/strip-ansi": { - "version": "6.0.1", - "resolved": "https://registry.npmjs.org/strip-ansi/-/strip-ansi-6.0.1.tgz", - "integrity": "sha512-Y38VPSHcqkFrCpFnQ9vuSXmquuv5oXOKpGeT6aGrr3o3Gc9AlVa6JBfUSOCnbxGGZF+/0ooI7KrPuUSztUdU5A==", - "dev": true, - "dependencies": { - "ansi-regex": "^5.0.1" - }, - "engines": { - "node": ">=8" - } - }, - "node_modules/strip-bom": { - "version": "4.0.0", - "resolved": "https://registry.npmjs.org/strip-bom/-/strip-bom-4.0.0.tgz", - "integrity": "sha512-3xurFv5tEgii33Zi8Jtp55wEIILR9eh34FAW00PZf+JnSsTmV/ioewSgQl97JHvgjoRGwPShsWm+IdrxB35d0w==", - "dev": true, - "engines": { - "node": ">=8" - } - }, - "node_modules/strip-final-newline": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/strip-final-newline/-/strip-final-newline-2.0.0.tgz", - "integrity": "sha512-BrpvfNAE3dcvq7ll3xVumzjKjZQ5tI1sEUIKr3Uoks0XUl45St3FlatVqef9prk4jRDzhW6WZg+3bk93y6pLjA==", - "dev": true, - "engines": { - "node": ">=6" - } - }, - "node_modules/strip-json-comments": { - "version": "3.1.1", - "resolved": "https://registry.npmjs.org/strip-json-comments/-/strip-json-comments-3.1.1.tgz", - "integrity": "sha512-6fPc+R4ihwqP6N/aIv2f1gMH8lOVtWQHoqC4yK6oSDVVocumAsfCqjkXnqiYMhmMwS/mEHLp7Vehlt3ql6lEig==", - "dev": true, - "engines": { - "node": ">=8" - }, - "funding": { - "url": "https://github.com/sponsors/sindresorhus" - } - }, - "node_modules/supports-color": { - "version": "7.2.0", - "resolved": "https://registry.npmjs.org/supports-color/-/supports-color-7.2.0.tgz", - "integrity": "sha512-qpCAvRl9stuOHveKsn7HncJRvv501qIacKzQlO/+Lwxc9+0q2wLyv4Dfvt80/DPn2pqOBsJdDiogXGR9+OvwRw==", - "dev": true, - "dependencies": { - "has-flag": "^4.0.0" - }, - "engines": { - "node": ">=8" - } - }, - "node_modules/test-exclude": { - "version": "6.0.0", - "resolved": "https://registry.npmjs.org/test-exclude/-/test-exclude-6.0.0.tgz", - "integrity": "sha512-cAGWPIyOHU6zlmg88jwm7VRyXnMN7iV68OGAbYDk/Mh/xC/pzVPlQtY6ngoIH/5/tciuhGfvESU8GrHrcxD56w==", - "dev": true, - "dependencies": { - "@istanbuljs/schema": "^0.1.2", - "glob": "^7.1.4", - "minimatch": "^3.0.4" - }, - "engines": { - "node": ">=8" - } - }, - "node_modules/text-table": { - "version": "0.2.0", - "resolved": "https://registry.npmjs.org/text-table/-/text-table-0.2.0.tgz", - "integrity": "sha1-f17oI66AUgfACvLfSoTsP8+lcLQ=", - "dev": true - }, - "node_modules/through": { - "version": "2.3.8", - "resolved": "https://registry.npmjs.org/through/-/through-2.3.8.tgz", - "integrity": "sha1-DdTJ/6q8NXlgsbckEV1+Doai4fU=", - "dev": true - }, - "node_modules/tmp": { - "version": "0.0.33", - "resolved": "https://registry.npmjs.org/tmp/-/tmp-0.0.33.tgz", - "integrity": "sha512-jRCJlojKnZ3addtTOjdIqoRuPEKBvNXcGYqzO6zWZX8KfKEpnGY5jfggJQ3EjKuu8D4bJRr0y+cYJFmYbImXGw==", - "dev": true, - "dependencies": { - "os-tmpdir": "~1.0.2" - }, - "engines": { - "node": ">=0.6.0" - } - }, - "node_modules/to-fast-properties": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/to-fast-properties/-/to-fast-properties-2.0.0.tgz", - "integrity": "sha1-3F5pjL0HkmW8c+A3doGk5Og/YW4=", - "dev": true, - "engines": { - "node": ">=4" - } - }, - "node_modules/to-readable-stream": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/to-readable-stream/-/to-readable-stream-1.0.0.tgz", - "integrity": "sha512-Iq25XBt6zD5npPhlLVXGFN3/gyR2/qODcKNNyTMd4vbm39HUaOiAM4PMq0eMVC/Tkxz+Zjdsc55g9yyz+Yq00Q==", - "dev": true, - "engines": { - "node": ">=6" - } - }, - "node_modules/to-regex-range": { - "version": "5.0.1", - "resolved": "https://registry.npmjs.org/to-regex-range/-/to-regex-range-5.0.1.tgz", - "integrity": "sha512-65P7iz6X5yEr1cwcgvQxbbIw7Uk3gOy5dIdtZ4rDveLqhrdJP+Li/Hx6tyK0NEb+2GCyneCMJiGqrADCSNk8sQ==", - "dev": true, - "dependencies": { - "is-number": "^7.0.0" - }, - "engines": { - "node": ">=8.0" - } - }, - "node_modules/tr46": { - "version": "0.0.3", - "resolved": "https://registry.npmjs.org/tr46/-/tr46-0.0.3.tgz", - "integrity": "sha1-gYT9NH2snNwYWZLzpmIuFLnZq2o=", - "dev": true - }, - "node_modules/ts-node": { - "version": "10.4.0", - "resolved": "https://registry.npmjs.org/ts-node/-/ts-node-10.4.0.tgz", - "integrity": "sha512-g0FlPvvCXSIO1JDF6S232P5jPYqBkRL9qly81ZgAOSU7rwI0stphCgd2kLiCrU9DjQCrJMWEqcNSjQL02s6d8A==", - "dev": true, - "dependencies": { - "@cspotcode/source-map-support": "0.7.0", - "@tsconfig/node10": "^1.0.7", - "@tsconfig/node12": "^1.0.7", - "@tsconfig/node14": "^1.0.0", - "@tsconfig/node16": "^1.0.2", - "acorn": "^8.4.1", - "acorn-walk": "^8.1.1", - "arg": "^4.1.0", - "create-require": "^1.1.0", - "diff": "^4.0.1", - "make-error": "^1.1.1", - "yn": "3.1.1" - }, - "bin": { - "ts-node": "dist/bin.js", - "ts-node-cwd": "dist/bin-cwd.js", - "ts-node-script": "dist/bin-script.js", - "ts-node-transpile-only": "dist/bin-transpile.js", - "ts-script": "dist/bin-script-deprecated.js" - }, - "peerDependencies": { - "@swc/core": ">=1.2.50", - "@swc/wasm": ">=1.2.50", - "@types/node": "*", - "typescript": ">=2.7" - }, - "peerDependenciesMeta": { - "@swc/core": { - "optional": true - }, - "@swc/wasm": { - "optional": true - } - } - }, - "node_modules/ts-node/node_modules/diff": { - "version": "4.0.2", - "resolved": "https://registry.npmjs.org/diff/-/diff-4.0.2.tgz", - "integrity": "sha512-58lmxKSA4BNyLz+HHMUzlOEpg09FV+ev6ZMe3vJihgdxzgcwZ8VoEEPmALCZG9LmqfVoNMMKpttIYTVG6uDY7A==", - "dev": true, - "engines": { - "node": ">=0.3.1" - } - }, - "node_modules/tslib": { - "version": "2.1.0", - "resolved": "https://registry.npmjs.org/tslib/-/tslib-2.1.0.tgz", - "integrity": "sha512-hcVC3wYEziELGGmEEXue7D75zbwIIVUMWAVbHItGPx0ziyXxrOMQx4rQEVEV45Ut/1IotuEvwqPopzIOkDMf0A==", - "dev": true - }, - "node_modules/tsutils": { - "version": "3.21.0", - "resolved": "https://registry.npmjs.org/tsutils/-/tsutils-3.21.0.tgz", - "integrity": "sha512-mHKK3iUXL+3UF6xL5k0PEhKRUBKPBCv/+RkEOpjRWxxx27KKRBmmA60A9pgOUvMi8GKhRMPEmjBRPzs2W7O1OA==", - "dev": true, - "dependencies": { - "tslib": "^1.8.1" - }, - "engines": { - "node": ">= 6" - }, - "peerDependencies": { - "typescript": ">=2.8.0 || >= 3.2.0-dev || >= 3.3.0-dev || >= 3.4.0-dev || >= 3.5.0-dev || >= 3.6.0-dev || >= 3.6.0-beta || >= 3.7.0-dev || >= 3.7.0-beta" - } - }, - "node_modules/tsutils/node_modules/tslib": { - "version": "1.14.1", - "resolved": "https://registry.npmjs.org/tslib/-/tslib-1.14.1.tgz", - "integrity": "sha512-Xni35NKzjgMrwevysHTCArtLDpPvye8zV/0E4EyYn43P7/7qvQwPh9BGkHewbMulVntbigmcT7rdX3BNo9wRJg==", - "dev": true - }, - "node_modules/type-check": { - "version": "0.4.0", - "resolved": "https://registry.npmjs.org/type-check/-/type-check-0.4.0.tgz", - "integrity": "sha512-XleUoc9uwGXqjWwXaUTZAmzMcFZ5858QA2vvx1Ur5xIcixXIP+8LnFDgRplU30us6teqdlskFfu+ae4K79Ooew==", - "dev": true, - "dependencies": { - "prelude-ls": "^1.2.1" - }, - "engines": { - "node": ">= 0.8.0" - } - }, - "node_modules/type-detect": { - "version": "4.0.8", - "resolved": "https://registry.npmjs.org/type-detect/-/type-detect-4.0.8.tgz", - "integrity": "sha512-0fr/mIH1dlO+x7TlcMy+bIDqKPsw/70tVyeHW787goQjhmqaZe10uwLujubK9q9Lg6Fiho1KUKDYz0Z7k7g5/g==", - "dev": true, - "engines": { - "node": ">=4" - } - }, - "node_modules/type-fest": { - "version": "0.20.2", - "resolved": "https://registry.npmjs.org/type-fest/-/type-fest-0.20.2.tgz", - "integrity": "sha512-Ne+eE4r0/iWnpAxD852z3A+N0Bt5RN//NjJwRd2VFHEmrywxf5vsZlh4R6lixl6B+wz/8d+maTSAkN1FIkI3LQ==", - "dev": true, - "engines": { - "node": ">=10" - }, - "funding": { - "url": "https://github.com/sponsors/sindresorhus" - } - }, - "node_modules/typedarray-to-buffer": { - "version": "3.1.5", - "resolved": "https://registry.npmjs.org/typedarray-to-buffer/-/typedarray-to-buffer-3.1.5.tgz", - "integrity": "sha512-zdu8XMNEDepKKR+XYOXAVPtWui0ly0NtohUscw+UmaHiAWT8hrV1rr//H6V+0DvJ3OQ19S979M0laLfX8rm82Q==", - "dev": true, - "dependencies": { - "is-typedarray": "^1.0.0" - } - }, - "node_modules/typedoc": { - "version": "0.22.8", - "resolved": "https://registry.npmjs.org/typedoc/-/typedoc-0.22.8.tgz", - "integrity": "sha512-92S+YzyhospdXN5rnkYUTgirdTYqNWY7NP9vco+IqQQoiSXzVSUsawVro+tMyEEsWUS7EMaJ2YOjB9uE0CBi6A==", - "dev": true, - "dependencies": { - "glob": "^7.2.0", - "lunr": "^2.3.9", - "marked": "^3.0.8", - "minimatch": "^3.0.4", - "shiki": "^0.9.12" - }, - "bin": { - "typedoc": "bin/typedoc" - }, - "engines": { - "node": ">= 12.10.0" - }, - "peerDependencies": { - "typescript": "4.0.x || 4.1.x || 4.2.x || 4.3.x || 4.4.x" - } - }, - "node_modules/typedoc-github-wiki-theme": { - "version": "0.6.0", - "resolved": "https://registry.npmjs.org/typedoc-github-wiki-theme/-/typedoc-github-wiki-theme-0.6.0.tgz", - "integrity": "sha512-uHhR7PwAZ4JgO0KzlLocWSvMqKsSZ/wxUQYGKskIepzsotPAQcAWnSSnGi6gdkSw8UAfIIppdD7H1AmI39962w==", - "dev": true, - "peerDependencies": { - "typedoc": ">=0.22.0", - "typedoc-plugin-markdown": ">=3.11.0" - } - }, - "node_modules/typedoc-plugin-markdown": { - "version": "3.11.3", - "resolved": "https://registry.npmjs.org/typedoc-plugin-markdown/-/typedoc-plugin-markdown-3.11.3.tgz", - "integrity": "sha512-rWiHbEIe0oZetDIsBR24XJVxGOJ91kDcHoj2KhFKxCLoJGX659EKBQkHne9QJ4W2stGhu1fRgFyQaouSBnxukA==", - "dev": true, - "dependencies": { - "handlebars": "^4.7.7" - }, - "peerDependencies": { - "typedoc": ">=0.22.0" - } - }, - "node_modules/typedoc/node_modules/glob": { - "version": "7.2.0", - "resolved": "https://registry.npmjs.org/glob/-/glob-7.2.0.tgz", - "integrity": "sha512-lmLf6gtyrPq8tTjSmrO94wBeQbFR3HbLHbuyD69wuyQkImp2hWqMGB47OX65FBkPffO641IP9jWa1z4ivqG26Q==", - "dev": true, - "dependencies": { - "fs.realpath": "^1.0.0", - "inflight": "^1.0.4", - "inherits": "2", - "minimatch": "^3.0.4", - "once": "^1.3.0", - "path-is-absolute": "^1.0.0" - }, - "engines": { - "node": "*" - }, - "funding": { - "url": "https://github.com/sponsors/isaacs" - } - }, - "node_modules/typescript": { - "version": "4.4.4", - "resolved": "https://registry.npmjs.org/typescript/-/typescript-4.4.4.tgz", - "integrity": "sha512-DqGhF5IKoBl8WNf8C1gu8q0xZSInh9j1kJJMqT3a94w1JzVaBU4EXOSMrz9yDqMT0xt3selp83fuFMQ0uzv6qA==", - "dev": true, - "bin": { - "tsc": "bin/tsc", - "tsserver": "bin/tsserver" - }, - "engines": { - "node": ">=4.2.0" - } - }, - "node_modules/uglify-js": { - "version": "3.14.3", - "resolved": "https://registry.npmjs.org/uglify-js/-/uglify-js-3.14.3.tgz", - "integrity": "sha512-mic3aOdiq01DuSVx0TseaEzMIVqebMZ0Z3vaeDhFEh9bsc24hV1TFvN74reA2vs08D0ZWfNjAcJ3UbVLaBss+g==", - "dev": true, - "optional": true, - "bin": { - "uglifyjs": "bin/uglifyjs" - }, - "engines": { - "node": ">=0.8.0" - } - }, - "node_modules/unique-string": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/unique-string/-/unique-string-2.0.0.tgz", - "integrity": "sha512-uNaeirEPvpZWSgzwsPGtU2zVSTrn/8L5q/IexZmH0eH6SA73CmAA5U4GwORTxQAZs95TAXLNqeLoPPNO5gZfWg==", - "dev": true, - "dependencies": { - "crypto-random-string": "^2.0.0" - }, - "engines": { - "node": ">=8" - } - }, - "node_modules/universal-user-agent": { - "version": "6.0.0", - "resolved": "https://registry.npmjs.org/universal-user-agent/-/universal-user-agent-6.0.0.tgz", - "integrity": "sha512-isyNax3wXoKaulPDZWHQqbmIx1k2tb9fb3GGDBRxCscfYV2Ch7WxPArBsFEG8s/safwXTT7H4QGhaIkTp9447w==", - "dev": true - }, - "node_modules/update-notifier": { - "version": "5.1.0", - "resolved": "https://registry.npmjs.org/update-notifier/-/update-notifier-5.1.0.tgz", - "integrity": "sha512-ItnICHbeMh9GqUy31hFPrD1kcuZ3rpxDZbf4KUDavXwS0bW5m7SLbDQpGX3UYr072cbrF5hFUs3r5tUsPwjfHw==", - "dev": true, - "dependencies": { - "boxen": "^5.0.0", - "chalk": "^4.1.0", - "configstore": "^5.0.1", - "has-yarn": "^2.1.0", - "import-lazy": "^2.1.0", - "is-ci": "^2.0.0", - "is-installed-globally": "^0.4.0", - "is-npm": "^5.0.0", - "is-yarn-global": "^0.3.0", - "latest-version": "^5.1.0", - "pupa": "^2.1.1", - "semver": "^7.3.4", - "semver-diff": "^3.1.1", - "xdg-basedir": "^4.0.0" - }, - "engines": { - "node": ">=10" - }, - "funding": { - "url": "https://github.com/yeoman/update-notifier?sponsor=1" - } - }, - "node_modules/update-notifier/node_modules/ci-info": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/ci-info/-/ci-info-2.0.0.tgz", - "integrity": "sha512-5tK7EtrZ0N+OLFMthtqOj4fI2Jeb88C4CAZPu25LDVUgXJ0A3Js4PMGqrn0JU1W0Mh1/Z8wZzYPxqUrXeBboCQ==", - "dev": true - }, - "node_modules/update-notifier/node_modules/is-ci": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/is-ci/-/is-ci-2.0.0.tgz", - "integrity": "sha512-YfJT7rkpQB0updsdHLGWrvhBJfcfzNNawYDNIyQXJz0IViGf75O8EBPKSdvw2rF+LGCsX4FZ8tcr3b19LcZq4w==", - "dev": true, - "dependencies": { - "ci-info": "^2.0.0" - }, - "bin": { - "is-ci": "bin.js" - } - }, - "node_modules/uri-js": { - "version": "4.4.1", - "resolved": "https://registry.npmjs.org/uri-js/-/uri-js-4.4.1.tgz", - "integrity": "sha512-7rKUyy33Q1yc98pQ1DAmLtwX109F7TIfWlW1Ydo8Wl1ii1SeHieeh0HHfPeL2fMXK6z0s8ecKs9frCuLJvndBg==", - "dev": true, - "dependencies": { - "punycode": "^2.1.0" - } - }, - "node_modules/url-join": { - "version": "4.0.1", - "resolved": "https://registry.npmjs.org/url-join/-/url-join-4.0.1.tgz", - "integrity": "sha512-jk1+QP6ZJqyOiuEI9AEWQfju/nB2Pw466kbA0LEZljHwKeMgd9WrAEgEGxjPDD2+TNbbb37rTyhEfrCXfuKXnA==", - "dev": true - }, - "node_modules/url-parse-lax": { - "version": "3.0.0", - "resolved": "https://registry.npmjs.org/url-parse-lax/-/url-parse-lax-3.0.0.tgz", - "integrity": "sha1-FrXK/Afb42dsGxmZF3gj1lA6yww=", - "dev": true, - "dependencies": { - "prepend-http": "^2.0.0" - }, - "engines": { - "node": ">=4" - } - }, - "node_modules/util-deprecate": { - "version": "1.0.2", - "resolved": "https://registry.npmjs.org/util-deprecate/-/util-deprecate-1.0.2.tgz", - "integrity": "sha1-RQ1Nyfpw3nMnYvvS1KKJgUGaDM8=", - "dev": true - }, - "node_modules/uuid": { - "version": "3.4.0", - "resolved": "https://registry.npmjs.org/uuid/-/uuid-3.4.0.tgz", - "integrity": "sha512-HjSDRw6gZE5JMggctHBcjVak08+KEVhSIiDzFnT9S9aegmp85S/bReBVTb4QTFaRNptJ9kuYaNhnbNEOkbKb/A==", - "deprecated": "Please upgrade to version 7 or higher. Older versions may use Math.random() in certain circumstances, which is known to be problematic. See https://v8.dev/blog/math-random for details.", - "dev": true, - "bin": { - "uuid": "bin/uuid" - } - }, - "node_modules/v8-compile-cache": { - "version": "2.3.0", - "resolved": "https://registry.npmjs.org/v8-compile-cache/-/v8-compile-cache-2.3.0.tgz", - "integrity": "sha512-l8lCEmLcLYZh4nbunNZvQCJc5pv7+RCwa8q/LdUx8u7lsWvPDKmpodJAJNwkAhJC//dFY48KuIEmjtd4RViDrA==", - "dev": true - }, - "node_modules/vscode-textmate": { - "version": "5.2.0", - "resolved": "https://registry.npmjs.org/vscode-textmate/-/vscode-textmate-5.2.0.tgz", - "integrity": "sha512-Uw5ooOQxRASHgu6C7GVvUxisKXfSgW4oFlO+aa+PAkgmH89O3CXxEEzNRNtHSqtXFTl0nAC1uYj0GMSH27uwtQ==", - "dev": true - }, - "node_modules/wcwidth": { - "version": "1.0.1", - "resolved": "https://registry.npmjs.org/wcwidth/-/wcwidth-1.0.1.tgz", - "integrity": "sha1-8LDc+RW8X/FSivrbLA4XtTLaL+g=", - "dev": true, - "dependencies": { - "defaults": "^1.0.3" - } - }, - "node_modules/webidl-conversions": { - "version": "3.0.1", - "resolved": "https://registry.npmjs.org/webidl-conversions/-/webidl-conversions-3.0.1.tgz", - "integrity": "sha1-JFNCdeKnvGvnvIZhHMFq4KVlSHE=", - "dev": true - }, - "node_modules/whatwg-url": { - "version": "5.0.0", - "resolved": "https://registry.npmjs.org/whatwg-url/-/whatwg-url-5.0.0.tgz", - "integrity": "sha1-lmRU6HZUYuN2RNNib2dCzotwll0=", - "dev": true, - "dependencies": { - "tr46": "~0.0.3", - "webidl-conversions": "^3.0.0" - } - }, - "node_modules/which": { - "version": "2.0.2", - "resolved": "https://registry.npmjs.org/which/-/which-2.0.2.tgz", - "integrity": "sha512-BLI3Tl1TW3Pvl70l3yq3Y64i+awpwXqsGBYWkkqMtnbXgrMD+yj7rhW0kuEDxzJaYXGjEW5ogapKNMEKNMjibA==", - "dev": true, - "dependencies": { - "isexe": "^2.0.0" - }, - "bin": { - "node-which": "bin/node-which" - }, - "engines": { - "node": ">= 8" - } - }, - "node_modules/which-module": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/which-module/-/which-module-2.0.0.tgz", - "integrity": "sha1-2e8H3Od7mQK4o6j6SzHD4/fm6Ho=", - "dev": true - }, - "node_modules/widest-line": { - "version": "3.1.0", - "resolved": "https://registry.npmjs.org/widest-line/-/widest-line-3.1.0.tgz", - "integrity": "sha512-NsmoXalsWVDMGupxZ5R08ka9flZjjiLvHVAWYOKtiKM8ujtZWr9cRffak+uSE48+Ob8ObalXpwyeUiyDD6QFgg==", - "dev": true, - "dependencies": { - "string-width": "^4.0.0" - }, - "engines": { - "node": ">=8" - } - }, - "node_modules/windows-release": { - "version": "4.0.0", - "resolved": "https://registry.npmjs.org/windows-release/-/windows-release-4.0.0.tgz", - "integrity": "sha512-OxmV4wzDKB1x7AZaZgXMVsdJ1qER1ed83ZrTYd5Bwq2HfJVg3DJS8nqlAG4sMoJ7mu8cuRmLEYyU13BKwctRAg==", - "dev": true, - "dependencies": { - "execa": "^4.0.2" - }, - "engines": { - "node": ">=10" - }, - "funding": { - "url": "https://github.com/sponsors/sindresorhus" - } - }, - "node_modules/windows-release/node_modules/execa": { - "version": "4.1.0", - "resolved": "https://registry.npmjs.org/execa/-/execa-4.1.0.tgz", - "integrity": "sha512-j5W0//W7f8UxAn8hXVnwG8tLwdiUy4FJLcSupCg6maBYZDpyBvTApK7KyuI4bKj8KOh1r2YH+6ucuYtJv1bTZA==", - "dev": true, - "dependencies": { - "cross-spawn": "^7.0.0", - "get-stream": "^5.0.0", - "human-signals": "^1.1.1", - "is-stream": "^2.0.0", - "merge-stream": "^2.0.0", - "npm-run-path": "^4.0.0", - "onetime": "^5.1.0", - "signal-exit": "^3.0.2", - "strip-final-newline": "^2.0.0" - }, - "engines": { - "node": ">=10" - }, - "funding": { - "url": "https://github.com/sindresorhus/execa?sponsor=1" - } - }, - "node_modules/windows-release/node_modules/get-stream": { - "version": "5.2.0", - "resolved": "https://registry.npmjs.org/get-stream/-/get-stream-5.2.0.tgz", - "integrity": "sha512-nBF+F1rAZVCu/p7rjzgA+Yb4lfYXrpl7a6VmJrU8wF9I1CKvP/QwPNZHnOlwbTkY6dvtFIzFMSyQXbLoTQPRpA==", - "dev": true, - "dependencies": { - "pump": "^3.0.0" - }, - "engines": { - "node": ">=8" - }, - "funding": { - "url": "https://github.com/sponsors/sindresorhus" - } - }, - "node_modules/windows-release/node_modules/human-signals": { - "version": "1.1.1", - "resolved": "https://registry.npmjs.org/human-signals/-/human-signals-1.1.1.tgz", - "integrity": "sha512-SEQu7vl8KjNL2eoGBLF3+wAjpsNfA9XMlXAYj/3EdaNfAlxKthD1xjEQfGOUhllCGGJVNY34bRr6lPINhNjyZw==", - "dev": true, - "engines": { - "node": ">=8.12.0" - } - }, - "node_modules/word-wrap": { - "version": "1.2.3", - "resolved": "https://registry.npmjs.org/word-wrap/-/word-wrap-1.2.3.tgz", - "integrity": "sha512-Hz/mrNwitNRh/HUAtM/VT/5VH+ygD6DV7mYKZAtHOrbs8U7lvPS6xf7EJKMF0uW1KJCl0H701g3ZGus+muE5vQ==", - "dev": true, - "engines": { - "node": ">=0.10.0" - } - }, - "node_modules/wordwrap": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/wordwrap/-/wordwrap-1.0.0.tgz", - "integrity": "sha1-J1hIEIkUVqQXHI0CJkQa3pDLyus=", - "dev": true - }, - "node_modules/workerpool": { - "version": "6.1.5", - "resolved": "https://registry.npmjs.org/workerpool/-/workerpool-6.1.5.tgz", - "integrity": "sha512-XdKkCK0Zqc6w3iTxLckiuJ81tiD/o5rBE/m+nXpRCB+/Sq4DqkfXZ/x0jW02DG1tGsfUGXbTJyZDP+eu67haSw==", - "dev": true - }, - "node_modules/wrap-ansi": { - "version": "7.0.0", - "resolved": "https://registry.npmjs.org/wrap-ansi/-/wrap-ansi-7.0.0.tgz", - "integrity": "sha512-YVGIj2kamLSTxw6NsZjoBxfSwsn0ycdesmc4p+Q21c5zPuZ1pl+NfxVdxPtdHvmNVOQ6XSYG4AUtyt/Fi7D16Q==", - "dev": true, - "dependencies": { - "ansi-styles": "^4.0.0", - "string-width": "^4.1.0", - "strip-ansi": "^6.0.0" - }, - "engines": { - "node": ">=10" - }, - "funding": { - "url": "https://github.com/chalk/wrap-ansi?sponsor=1" - } - }, - "node_modules/wrappy": { - "version": "1.0.2", - "resolved": "https://registry.npmjs.org/wrappy/-/wrappy-1.0.2.tgz", - "integrity": "sha1-tSQ9jz7BqjXxNkYFvA0QNuMKtp8=", - "dev": true - }, - "node_modules/write-file-atomic": { - "version": "3.0.3", - "resolved": "https://registry.npmjs.org/write-file-atomic/-/write-file-atomic-3.0.3.tgz", - "integrity": "sha512-AvHcyZ5JnSfq3ioSyjrBkH9yW4m7Ayk8/9My/DD9onKeu/94fwrMocemO2QAJFAlnnDN+ZDS+ZjAR5ua1/PV/Q==", - "dev": true, - "dependencies": { - "imurmurhash": "^0.1.4", - "is-typedarray": "^1.0.0", - "signal-exit": "^3.0.2", - "typedarray-to-buffer": "^3.1.5" - } - }, - "node_modules/xdg-basedir": { - "version": "4.0.0", - "resolved": "https://registry.npmjs.org/xdg-basedir/-/xdg-basedir-4.0.0.tgz", - "integrity": "sha512-PSNhEJDejZYV7h50BohL09Er9VaIefr2LMAf3OEmpCkjOi34eYyQYAXUTjEQtZJTKcF0E2UKTh+osDLsgNim9Q==", - "dev": true, - "engines": { - "node": ">=8" - } - }, - "node_modules/y18n": { - "version": "5.0.8", - "resolved": "https://registry.npmjs.org/y18n/-/y18n-5.0.8.tgz", - "integrity": "sha512-0pfFzegeDWJHJIAmTLRP2DwHjdF5s7jo9tuztdQxAhINCdvS+3nGINqPd00AphqJR/0LhANUS6/+7SCb98YOfA==", - "dev": true, - "engines": { - "node": ">=10" - } - }, - "node_modules/yallist": { - "version": "4.0.0", - "resolved": "https://registry.npmjs.org/yallist/-/yallist-4.0.0.tgz", - "integrity": "sha512-3wdGidZyq5PB084XLES5TpOSRA3wjXAlIWMhum2kRcv/41Sn2emQ0dycQW4uZXLejwKvg6EsvbdlVL+FYEct7A==" - }, - "node_modules/yaml": { - "version": "1.10.2", - "resolved": "https://registry.npmjs.org/yaml/-/yaml-1.10.2.tgz", - "integrity": "sha512-r3vXyErRCYJ7wg28yvBY5VSoAF8ZvlcW9/BwUzEtUsjvX/DKs24dIkuwjtuprwJJHsbyUbLApepYTR1BN4uHrg==", - "dev": true, - "engines": { - "node": ">= 6" - } - }, - "node_modules/yargs": { - "version": "17.2.1", - "resolved": "https://registry.npmjs.org/yargs/-/yargs-17.2.1.tgz", - "integrity": "sha512-XfR8du6ua4K6uLGm5S6fA+FIJom/MdJcFNVY8geLlp2v8GYbOXD4EB1tPNZsRn4vBzKGMgb5DRZMeWuFc2GO8Q==", - "dev": true, - "dependencies": { - "cliui": "^7.0.2", - "escalade": "^3.1.1", - "get-caller-file": "^2.0.5", - "require-directory": "^2.1.1", - "string-width": "^4.2.0", - "y18n": "^5.0.5", - "yargs-parser": "^20.2.2" - }, - "engines": { - "node": ">=12" - } - }, - "node_modules/yargs-parser": { - "version": "20.2.4", - "resolved": "https://registry.npmjs.org/yargs-parser/-/yargs-parser-20.2.4.tgz", - "integrity": "sha512-WOkpgNhPTlE73h4VFAFsOnomJVaovO8VqLDzy5saChRBFQFBoMYirowyW+Q9HB4HFF4Z7VZTiG3iSzJJA29yRA==", - "dev": true, - "engines": { - "node": ">=10" - } - }, - "node_modules/yargs-unparser": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/yargs-unparser/-/yargs-unparser-2.0.0.tgz", - "integrity": "sha512-7pRTIA9Qc1caZ0bZ6RYRGbHJthJWuakf+WmHK0rVeLkNrrGhfoabBNdue6kdINI6r4if7ocq9aD/n7xwKOdzOA==", - "dev": true, - "dependencies": { - "camelcase": "^6.0.0", - "decamelize": "^4.0.0", - "flat": "^5.0.2", - "is-plain-obj": "^2.1.0" - }, - "engines": { - "node": ">=10" - } - }, - "node_modules/yargs-unparser/node_modules/camelcase": { - "version": "6.2.0", - "resolved": "https://registry.npmjs.org/camelcase/-/camelcase-6.2.0.tgz", - "integrity": "sha512-c7wVvbw3f37nuobQNtgsgG9POC9qMbNuMQmTCqZv23b6MIz0fcYpBiOlv9gEN/hdLdnZTDQhg6e9Dq5M1vKvfg==", - "dev": true, - "engines": { - "node": ">=10" - }, - "funding": { - "url": "https://github.com/sponsors/sindresorhus" - } - }, - "node_modules/yargs-unparser/node_modules/decamelize": { - "version": "4.0.0", - "resolved": "https://registry.npmjs.org/decamelize/-/decamelize-4.0.0.tgz", - "integrity": "sha512-9iE1PgSik9HeIIw2JO94IidnE3eBoQrFJ3w7sFuzSX4DpmZ3v5sZpUiV5Swcf6mQEF+Y0ru8Neo+p+nyh2J+hQ==", - "dev": true, - "engines": { - "node": ">=10" - }, - "funding": { - "url": "https://github.com/sponsors/sindresorhus" - } - }, - "node_modules/yn": { - "version": "3.1.1", - "resolved": "https://registry.npmjs.org/yn/-/yn-3.1.1.tgz", - "integrity": "sha512-Ux4ygGWsu2c7isFWe8Yu1YluJmqVhxqK2cLXNQA5AcC3QfbGNpM7fu0Y8b/z16pXLnFxZYvWhd3fhBY9DLmC6Q==", - "dev": true, - "engines": { - "node": ">=6" - } - }, - "node_modules/yocto-queue": { - "version": "0.1.0", - "resolved": "https://registry.npmjs.org/yocto-queue/-/yocto-queue-0.1.0.tgz", - "integrity": "sha512-rVksvsnNCdJ/ohGc6xgPwyN8eheCxsiLM8mxuE/t/mOVqJewPuO1miLpTHQiRgTKCLexL4MeAFVagts7HmNZ2Q==", - "dev": true, - "engines": { - "node": ">=10" - }, - "funding": { - "url": "https://github.com/sponsors/sindresorhus" - } - }, - "packages/all-in-one": { - "name": "redis", - "version": "4.0.0-rc.3", - "license": "MIT", - "dependencies": { - "@redis/client": "^4.0.0-rc", - "@redis/json": "^1.0.0-rc", - "@redis/search": "^1.0.0-rc" - }, - "devDependencies": { - "release-it": "^14.11.6", - "typescript": "^4.4.4" - } - }, - "packages/client": { - "name": "@redis/client", - "version": "4.0.0-rc.3", - "license": "MIT", - "dependencies": { - "cluster-key-slot": "1.1.0", - "generic-pool": "3.8.2", - "redis-parser": "3.0.0", - "yallist": "4.0.0" - }, - "devDependencies": { - "@istanbuljs/nyc-config-typescript": "^1.0.1", - "@redis/test-utils": "*", - "@types/node": "^16.11.6", - "@types/sinon": "^10.0.6", - "@types/yallist": "^4.0.1", - "@typescript-eslint/eslint-plugin": "^5.2.0", - "@typescript-eslint/parser": "^5.2.0", - "eslint": "^8.1.0", - "nyc": "^15.1.0", - "release-it": "^14.11.6", - "sinon": "^11.1.2", - "source-map-support": "^0.5.20", - "ts-node": "^10.4.0", - "typedoc": "^0.22.7", - "typedoc-github-wiki-theme": "^0.6.0", - "typedoc-plugin-markdown": "^3.11.3", - "typescript": "^4.4.4" - }, - "engines": { - "node": ">=12" - } - }, - "packages/json": { - "name": "@redis/json", - "version": "1.0.0-rc.0", - "license": "MIT", - "devDependencies": { - "@istanbuljs/nyc-config-typescript": "^1.0.1", - "@redis/test-utils": "*", - "@types/node": "^16.11.6", - "nyc": "^15.1.0", - "release-it": "^14.11.6", - "source-map-support": "^0.5.20", - "ts-node": "^10.4.0", - "typescript": "^4.4.4" - }, - "peerDependencies": { - "@redis/client": "^4.0.0-rc" - } - }, - "packages/search": { - "name": "@redis/search", - "version": "1.0.0-rc.0", - "license": "MIT", - "devDependencies": { - "@istanbuljs/nyc-config-typescript": "^1.0.1", - "@redis/test-utils": "*", - "@types/node": "^16.11.6", - "nyc": "^15.1.0", - "release-it": "^14.11.6", - "source-map-support": "^0.5.20", - "ts-node": "^10.4.0", - "typescript": "^4.4.4" - }, - "peerDependencies": { - "@redis/client": "^4.0.0-rc" - } - }, - "packages/test-utils": { - "name": "@redis/test-utils", - "devDependencies": { - "@istanbuljs/nyc-config-typescript": "^1.0.1", - "@tsconfig/node12": "^1.0.9", - "@types/mocha": "^9.0.0", - "@types/node": "^16.11.6", - "@types/yargs": "^17.0.5", - "mocha": "^9.1.3", - "nyc": "^15.1.0", - "release-it": "^14.11.6", - "source-map-support": "^0.5.20", - "ts-node": "^10.4.0", - "typescript": "^4.4.4", - "yargs": "^17.2.1" - }, - "peerDependencies": { - "@redis/client": "^4.0.0-rc" - } - }, - "packages/time-series": { - "name": "@redis/time-series", - "version": "1.0.0", - "license": "ISC", - "devDependencies": { - "@istanbuljs/nyc-config-typescript": "^1.0.1", - "@redis/test-utils": "*", - "@types/node": "^16.11.6", - "nyc": "^15.1.0", - "release-it": "^14.11.6", - "source-map-support": "^0.5.20", - "ts-node": "^10.4.0", - "typescript": "^4.4.4" - } - } - }, - "dependencies": { - "@babel/code-frame": { - "version": "7.16.0", - "resolved": "https://registry.npmjs.org/@babel/code-frame/-/code-frame-7.16.0.tgz", - "integrity": "sha512-IF4EOMEV+bfYwOmNxGzSnjR2EmQod7f1UXOpZM3l4i4o4QNwzjtJAu/HxdjHq0aYBvdqMuQEY1eg0nqW9ZPORA==", - "dev": true, - "requires": { - "@babel/highlight": "^7.16.0" - } - }, - "@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==", - "dev": true - }, - "@babel/core": { - "version": "7.16.0", - "resolved": "https://registry.npmjs.org/@babel/core/-/core-7.16.0.tgz", - "integrity": "sha512-mYZEvshBRHGsIAiyH5PzCFTCfbWfoYbO/jcSdXQSUQu1/pW0xDZAUP7KEc32heqWTAfAHhV9j1vH8Sav7l+JNQ==", - "dev": true, - "requires": { - "@babel/code-frame": "^7.16.0", - "@babel/generator": "^7.16.0", - "@babel/helper-compilation-targets": "^7.16.0", - "@babel/helper-module-transforms": "^7.16.0", - "@babel/helpers": "^7.16.0", - "@babel/parser": "^7.16.0", - "@babel/template": "^7.16.0", - "@babel/traverse": "^7.16.0", - "@babel/types": "^7.16.0", - "convert-source-map": "^1.7.0", - "debug": "^4.1.0", - "gensync": "^1.0.0-beta.2", - "json5": "^2.1.2", - "semver": "^6.3.0", - "source-map": "^0.5.0" - }, - "dependencies": { - "semver": { - "version": "6.3.0", - "resolved": "https://registry.npmjs.org/semver/-/semver-6.3.0.tgz", - "integrity": "sha512-b39TBaTSfV6yBrapU89p5fKekE2m/NwnDocOVruQFS1/veMgdzuPcnOM34M6CwxW8jH/lxEa5rBoDeUwu5HHTw==", - "dev": true - } - } - }, - "@babel/generator": { - "version": "7.16.0", - "resolved": "https://registry.npmjs.org/@babel/generator/-/generator-7.16.0.tgz", - "integrity": "sha512-RR8hUCfRQn9j9RPKEVXo9LiwoxLPYn6hNZlvUOR8tSnaxlD0p0+la00ZP9/SnRt6HchKr+X0fO2r8vrETiJGew==", - "dev": true, - "requires": { - "@babel/types": "^7.16.0", - "jsesc": "^2.5.1", - "source-map": "^0.5.0" - } - }, - "@babel/helper-compilation-targets": { - "version": "7.16.0", - "resolved": "https://registry.npmjs.org/@babel/helper-compilation-targets/-/helper-compilation-targets-7.16.0.tgz", - "integrity": "sha512-S7iaOT1SYlqK0sQaCi21RX4+13hmdmnxIEAnQUB/eh7GeAnRjOUgTYpLkUOiRXzD+yog1JxP0qyAQZ7ZxVxLVg==", - "dev": true, - "requires": { - "@babel/compat-data": "^7.16.0", - "@babel/helper-validator-option": "^7.14.5", - "browserslist": "^4.16.6", - "semver": "^6.3.0" - }, - "dependencies": { - "semver": { - "version": "6.3.0", - "resolved": "https://registry.npmjs.org/semver/-/semver-6.3.0.tgz", - "integrity": "sha512-b39TBaTSfV6yBrapU89p5fKekE2m/NwnDocOVruQFS1/veMgdzuPcnOM34M6CwxW8jH/lxEa5rBoDeUwu5HHTw==", - "dev": true - } - } - }, - "@babel/helper-function-name": { - "version": "7.16.0", - "resolved": "https://registry.npmjs.org/@babel/helper-function-name/-/helper-function-name-7.16.0.tgz", - "integrity": "sha512-BZh4mEk1xi2h4HFjWUXRQX5AEx4rvaZxHgax9gcjdLWdkjsY7MKt5p0otjsg5noXw+pB+clMCjw+aEVYADMjog==", - "dev": true, - "requires": { - "@babel/helper-get-function-arity": "^7.16.0", - "@babel/template": "^7.16.0", - "@babel/types": "^7.16.0" - } - }, - "@babel/helper-get-function-arity": { - "version": "7.16.0", - "resolved": "https://registry.npmjs.org/@babel/helper-get-function-arity/-/helper-get-function-arity-7.16.0.tgz", - "integrity": "sha512-ASCquNcywC1NkYh/z7Cgp3w31YW8aojjYIlNg4VeJiHkqyP4AzIvr4qx7pYDb4/s8YcsZWqqOSxgkvjUz1kpDQ==", - "dev": true, - "requires": { - "@babel/types": "^7.16.0" - } - }, - "@babel/helper-hoist-variables": { - "version": "7.16.0", - "resolved": "https://registry.npmjs.org/@babel/helper-hoist-variables/-/helper-hoist-variables-7.16.0.tgz", - "integrity": "sha512-1AZlpazjUR0EQZQv3sgRNfM9mEVWPK3M6vlalczA+EECcPz3XPh6VplbErL5UoMpChhSck5wAJHthlj1bYpcmg==", - "dev": true, - "requires": { - "@babel/types": "^7.16.0" - } - }, - "@babel/helper-member-expression-to-functions": { - "version": "7.16.0", - "resolved": "https://registry.npmjs.org/@babel/helper-member-expression-to-functions/-/helper-member-expression-to-functions-7.16.0.tgz", - "integrity": "sha512-bsjlBFPuWT6IWhl28EdrQ+gTvSvj5tqVP5Xeftp07SEuz5pLnsXZuDkDD3Rfcxy0IsHmbZ+7B2/9SHzxO0T+sQ==", - "dev": true, - "requires": { - "@babel/types": "^7.16.0" - } - }, - "@babel/helper-module-imports": { - "version": "7.16.0", - "resolved": "https://registry.npmjs.org/@babel/helper-module-imports/-/helper-module-imports-7.16.0.tgz", - "integrity": "sha512-kkH7sWzKPq0xt3H1n+ghb4xEMP8k0U7XV3kkB+ZGy69kDk2ySFW1qPi06sjKzFY3t1j6XbJSqr4mF9L7CYVyhg==", - "dev": true, - "requires": { - "@babel/types": "^7.16.0" - } - }, - "@babel/helper-module-transforms": { - "version": "7.16.0", - "resolved": "https://registry.npmjs.org/@babel/helper-module-transforms/-/helper-module-transforms-7.16.0.tgz", - "integrity": "sha512-My4cr9ATcaBbmaEa8M0dZNA74cfI6gitvUAskgDtAFmAqyFKDSHQo5YstxPbN+lzHl2D9l/YOEFqb2mtUh4gfA==", - "dev": true, - "requires": { - "@babel/helper-module-imports": "^7.16.0", - "@babel/helper-replace-supers": "^7.16.0", - "@babel/helper-simple-access": "^7.16.0", - "@babel/helper-split-export-declaration": "^7.16.0", - "@babel/helper-validator-identifier": "^7.15.7", - "@babel/template": "^7.16.0", - "@babel/traverse": "^7.16.0", - "@babel/types": "^7.16.0" - } - }, - "@babel/helper-optimise-call-expression": { - "version": "7.16.0", - "resolved": "https://registry.npmjs.org/@babel/helper-optimise-call-expression/-/helper-optimise-call-expression-7.16.0.tgz", - "integrity": "sha512-SuI467Gi2V8fkofm2JPnZzB/SUuXoJA5zXe/xzyPP2M04686RzFKFHPK6HDVN6JvWBIEW8tt9hPR7fXdn2Lgpw==", - "dev": true, - "requires": { - "@babel/types": "^7.16.0" - } - }, - "@babel/helper-replace-supers": { - "version": "7.16.0", - "resolved": "https://registry.npmjs.org/@babel/helper-replace-supers/-/helper-replace-supers-7.16.0.tgz", - "integrity": "sha512-TQxuQfSCdoha7cpRNJvfaYxxxzmbxXw/+6cS7V02eeDYyhxderSoMVALvwupA54/pZcOTtVeJ0xccp1nGWladA==", - "dev": true, - "requires": { - "@babel/helper-member-expression-to-functions": "^7.16.0", - "@babel/helper-optimise-call-expression": "^7.16.0", - "@babel/traverse": "^7.16.0", - "@babel/types": "^7.16.0" - } - }, - "@babel/helper-simple-access": { - "version": "7.16.0", - "resolved": "https://registry.npmjs.org/@babel/helper-simple-access/-/helper-simple-access-7.16.0.tgz", - "integrity": "sha512-o1rjBT/gppAqKsYfUdfHq5Rk03lMQrkPHG1OWzHWpLgVXRH4HnMM9Et9CVdIqwkCQlobnGHEJMsgWP/jE1zUiw==", - "dev": true, - "requires": { - "@babel/types": "^7.16.0" - } - }, - "@babel/helper-split-export-declaration": { - "version": "7.16.0", - "resolved": "https://registry.npmjs.org/@babel/helper-split-export-declaration/-/helper-split-export-declaration-7.16.0.tgz", - "integrity": "sha512-0YMMRpuDFNGTHNRiiqJX19GjNXA4H0E8jZ2ibccfSxaCogbm3am5WN/2nQNj0YnQwGWM1J06GOcQ2qnh3+0paw==", - "dev": true, - "requires": { - "@babel/types": "^7.16.0" - } - }, - "@babel/helper-validator-identifier": { - "version": "7.15.7", - "resolved": "https://registry.npmjs.org/@babel/helper-validator-identifier/-/helper-validator-identifier-7.15.7.tgz", - "integrity": "sha512-K4JvCtQqad9OY2+yTU8w+E82ywk/fe+ELNlt1G8z3bVGlZfn/hOcQQsUhGhW/N+tb3fxK800wLtKOE/aM0m72w==", - "dev": true - }, - "@babel/helper-validator-option": { - "version": "7.14.5", - "resolved": "https://registry.npmjs.org/@babel/helper-validator-option/-/helper-validator-option-7.14.5.tgz", - "integrity": "sha512-OX8D5eeX4XwcroVW45NMvoYaIuFI+GQpA2a8Gi+X/U/cDUIRsV37qQfF905F0htTRCREQIB4KqPeaveRJUl3Ow==", - "dev": true - }, - "@babel/helpers": { - "version": "7.16.0", - "resolved": "https://registry.npmjs.org/@babel/helpers/-/helpers-7.16.0.tgz", - "integrity": "sha512-dVRM0StFMdKlkt7cVcGgwD8UMaBfWJHl3A83Yfs8GQ3MO0LHIIIMvK7Fa0RGOGUQ10qikLaX6D7o5htcQWgTMQ==", - "dev": true, - "requires": { - "@babel/template": "^7.16.0", - "@babel/traverse": "^7.16.0", - "@babel/types": "^7.16.0" - } - }, - "@babel/highlight": { - "version": "7.16.0", - "resolved": "https://registry.npmjs.org/@babel/highlight/-/highlight-7.16.0.tgz", - "integrity": "sha512-t8MH41kUQylBtu2+4IQA3atqevA2lRgqA2wyVB/YiWmsDSuylZZuXOUy9ric30hfzauEFfdsuk/eXTRrGrfd0g==", - "dev": true, - "requires": { - "@babel/helper-validator-identifier": "^7.15.7", - "chalk": "^2.0.0", - "js-tokens": "^4.0.0" - }, - "dependencies": { - "ansi-styles": { - "version": "3.2.1", - "resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-3.2.1.tgz", - "integrity": "sha512-VT0ZI6kZRdTh8YyJw3SMbYm/u+NqfsAxEpWO0Pf9sq8/e94WxxOpPKx9FR1FlyCtOVDNOQ+8ntlqFxiRc+r5qA==", - "dev": true, - "requires": { - "color-convert": "^1.9.0" - } - }, - "chalk": { - "version": "2.4.2", - "resolved": "https://registry.npmjs.org/chalk/-/chalk-2.4.2.tgz", - "integrity": "sha512-Mti+f9lpJNcwF4tWV8/OrTTtF1gZi+f8FqlyAdouralcFWFQWF2+NgCHShjkCb+IFBLq9buZwE1xckQU4peSuQ==", - "dev": true, - "requires": { - "ansi-styles": "^3.2.1", - "escape-string-regexp": "^1.0.5", - "supports-color": "^5.3.0" - } - }, - "color-convert": { - "version": "1.9.3", - "resolved": "https://registry.npmjs.org/color-convert/-/color-convert-1.9.3.tgz", - "integrity": "sha512-QfAUtd+vFdAtFQcC8CCyYt1fYWxSqAiK2cSD6zDB8N3cpsEBAvRxp9zOGg6G/SHHJYAT88/az/IuDGALsNVbGg==", - "dev": true, - "requires": { - "color-name": "1.1.3" - } - }, - "color-name": { - "version": "1.1.3", - "resolved": "https://registry.npmjs.org/color-name/-/color-name-1.1.3.tgz", - "integrity": "sha1-p9BVi9icQveV3UIyj3QIMcpTvCU=", - "dev": true - }, - "escape-string-regexp": { - "version": "1.0.5", - "resolved": "https://registry.npmjs.org/escape-string-regexp/-/escape-string-regexp-1.0.5.tgz", - "integrity": "sha1-G2HAViGQqN/2rjuyzwIAyhMLhtQ=", - "dev": true - }, - "has-flag": { - "version": "3.0.0", - "resolved": "https://registry.npmjs.org/has-flag/-/has-flag-3.0.0.tgz", - "integrity": "sha1-tdRU3CGZriJWmfNGfloH87lVuv0=", - "dev": true - }, - "supports-color": { - "version": "5.5.0", - "resolved": "https://registry.npmjs.org/supports-color/-/supports-color-5.5.0.tgz", - "integrity": "sha512-QjVjwdXIt408MIiAqCX4oUKsgU2EqAGzs2Ppkm4aQYbjm+ZEWEcW4SfFNTr4uMNZma0ey4f5lgLrkB0aX0QMow==", - "dev": true, - "requires": { - "has-flag": "^3.0.0" - } - } - } - }, - "@babel/parser": { - "version": "7.16.2", - "resolved": "https://registry.npmjs.org/@babel/parser/-/parser-7.16.2.tgz", - "integrity": "sha512-RUVpT0G2h6rOZwqLDTrKk7ksNv7YpAilTnYe1/Q+eDjxEceRMKVWbCsX7t8h6C1qCFi/1Y8WZjcEPBAFG27GPw==", - "dev": true - }, - "@babel/template": { - "version": "7.16.0", - "resolved": "https://registry.npmjs.org/@babel/template/-/template-7.16.0.tgz", - "integrity": "sha512-MnZdpFD/ZdYhXwiunMqqgyZyucaYsbL0IrjoGjaVhGilz+x8YB++kRfygSOIj1yOtWKPlx7NBp+9I1RQSgsd5A==", - "dev": true, - "requires": { - "@babel/code-frame": "^7.16.0", - "@babel/parser": "^7.16.0", - "@babel/types": "^7.16.0" - } - }, - "@babel/traverse": { - "version": "7.16.0", - "resolved": "https://registry.npmjs.org/@babel/traverse/-/traverse-7.16.0.tgz", - "integrity": "sha512-qQ84jIs1aRQxaGaxSysII9TuDaguZ5yVrEuC0BN2vcPlalwfLovVmCjbFDPECPXcYM/wLvNFfp8uDOliLxIoUQ==", - "dev": true, - "requires": { - "@babel/code-frame": "^7.16.0", - "@babel/generator": "^7.16.0", - "@babel/helper-function-name": "^7.16.0", - "@babel/helper-hoist-variables": "^7.16.0", - "@babel/helper-split-export-declaration": "^7.16.0", - "@babel/parser": "^7.16.0", - "@babel/types": "^7.16.0", - "debug": "^4.1.0", - "globals": "^11.1.0" - }, - "dependencies": { - "globals": { - "version": "11.12.0", - "resolved": "https://registry.npmjs.org/globals/-/globals-11.12.0.tgz", - "integrity": "sha512-WOBp/EEGUiIsJSp7wcv/y6MO+lV9UoncWqxuFfm8eBwzWNgyfBd6Gz+IeKQ9jCmyhoH99g15M3T+QaVHFjizVA==", - "dev": true - } - } - }, - "@babel/types": { - "version": "7.16.0", - "resolved": "https://registry.npmjs.org/@babel/types/-/types-7.16.0.tgz", - "integrity": "sha512-PJgg/k3SdLsGb3hhisFvtLOw5ts113klrpLuIPtCJIU+BB24fqq6lf8RWqKJEjzqXR9AEH1rIb5XTqwBHB+kQg==", - "dev": true, - "requires": { - "@babel/helper-validator-identifier": "^7.15.7", - "to-fast-properties": "^2.0.0" - } - }, - "@cspotcode/source-map-consumer": { - "version": "0.8.0", - "resolved": "https://registry.npmjs.org/@cspotcode/source-map-consumer/-/source-map-consumer-0.8.0.tgz", - "integrity": "sha512-41qniHzTU8yAGbCp04ohlmSrZf8bkf/iJsl3V0dRGsQN/5GFfx+LbCSsCpp2gqrqjTVg/K6O8ycoV35JIwAzAg==", - "dev": true - }, - "@cspotcode/source-map-support": { - "version": "0.7.0", - "resolved": "https://registry.npmjs.org/@cspotcode/source-map-support/-/source-map-support-0.7.0.tgz", - "integrity": "sha512-X4xqRHqN8ACt2aHVe51OxeA2HjbcL4MqFqXkrmQszJ1NOUuUu5u6Vqx/0lZSVNku7velL5FC/s5uEAj1lsBMhA==", - "dev": true, - "requires": { - "@cspotcode/source-map-consumer": "0.8.0" - } - }, - "@eslint/eslintrc": { - "version": "1.0.4", - "resolved": "https://registry.npmjs.org/@eslint/eslintrc/-/eslintrc-1.0.4.tgz", - "integrity": "sha512-h8Vx6MdxwWI2WM8/zREHMoqdgLNXEL4QX3MWSVMdyNJGvXVOs+6lp+m2hc3FnuMHDc4poxFNI20vCk0OmI4G0Q==", - "dev": true, - "requires": { - "ajv": "^6.12.4", - "debug": "^4.3.2", - "espree": "^9.0.0", - "globals": "^13.9.0", - "ignore": "^4.0.6", - "import-fresh": "^3.2.1", - "js-yaml": "^4.1.0", - "minimatch": "^3.0.4", - "strip-json-comments": "^3.1.1" - }, - "dependencies": { - "ignore": { - "version": "4.0.6", - "resolved": "https://registry.npmjs.org/ignore/-/ignore-4.0.6.tgz", - "integrity": "sha512-cyFDKrqc/YdcWFniJhzI42+AzS+gNwmUzOSFcRCQYwySuBBBy/KjuxWLZ/FHEH6Moq1NizMOBWyTcv8O4OZIMg==", - "dev": true - } - } - }, - "@humanwhocodes/config-array": { - "version": "0.6.0", - "resolved": "https://registry.npmjs.org/@humanwhocodes/config-array/-/config-array-0.6.0.tgz", - "integrity": "sha512-JQlEKbcgEUjBFhLIF4iqM7u/9lwgHRBcpHrmUNCALK0Q3amXN6lxdoXLnF0sm11E9VqTmBALR87IlUg1bZ8A9A==", - "dev": true, - "requires": { - "@humanwhocodes/object-schema": "^1.2.0", - "debug": "^4.1.1", - "minimatch": "^3.0.4" - } - }, - "@humanwhocodes/object-schema": { - "version": "1.2.1", - "resolved": "https://registry.npmjs.org/@humanwhocodes/object-schema/-/object-schema-1.2.1.tgz", - "integrity": "sha512-ZnQMnLV4e7hDlUvw8H+U8ASL02SS2Gn6+9Ac3wGGLIe7+je2AeAOxPY+izIPJDfFDb7eDjev0Us8MO1iFRN8hA==", - "dev": true - }, - "@iarna/toml": { - "version": "2.2.5", - "resolved": "https://registry.npmjs.org/@iarna/toml/-/toml-2.2.5.tgz", - "integrity": "sha512-trnsAYxU3xnS1gPHPyU961coFyLkh4gAD/0zQ5mymY4yOZ+CYvsPqUbOFSw0aDM4y0tV7tiFxL/1XfXPNC6IPg==", - "dev": true - }, - "@istanbuljs/load-nyc-config": { - "version": "1.1.0", - "resolved": "https://registry.npmjs.org/@istanbuljs/load-nyc-config/-/load-nyc-config-1.1.0.tgz", - "integrity": "sha512-VjeHSlIzpv/NyD3N0YuHfXOPDIixcA1q2ZV98wsMqcYlPmv2n3Yb2lYP9XMElnaFVXg5A7YLTeLu6V84uQDjmQ==", - "dev": true, - "requires": { - "camelcase": "^5.3.1", - "find-up": "^4.1.0", - "get-package-type": "^0.1.0", - "js-yaml": "^3.13.1", - "resolve-from": "^5.0.0" - }, - "dependencies": { - "argparse": { - "version": "1.0.10", - "resolved": "https://registry.npmjs.org/argparse/-/argparse-1.0.10.tgz", - "integrity": "sha512-o5Roy6tNG4SL/FOkCAN6RzjiakZS25RLYFrcMttJqbdd8BWrnA+fGz57iN5Pb06pvBGvl5gQ0B48dJlslXvoTg==", - "dev": true, - "requires": { - "sprintf-js": "~1.0.2" - } - }, - "find-up": { - "version": "4.1.0", - "resolved": "https://registry.npmjs.org/find-up/-/find-up-4.1.0.tgz", - "integrity": "sha512-PpOwAdQ/YlXQ2vj8a3h8IipDuYRi3wceVQQGYWxNINccq40Anw7BlsEXCMbt1Zt+OLA6Fq9suIpIWD0OsnISlw==", - "dev": true, - "requires": { - "locate-path": "^5.0.0", - "path-exists": "^4.0.0" - } - }, - "js-yaml": { - "version": "3.14.1", - "resolved": "https://registry.npmjs.org/js-yaml/-/js-yaml-3.14.1.tgz", - "integrity": "sha512-okMH7OXXJ7YrN9Ok3/SXrnu4iX9yOk+25nqX4imS2npuvTYDmo/QEZoqwZkYaIDk3jVvBOTOIEgEhaLOynBS9g==", - "dev": true, - "requires": { - "argparse": "^1.0.7", - "esprima": "^4.0.0" - } - }, - "locate-path": { - "version": "5.0.0", - "resolved": "https://registry.npmjs.org/locate-path/-/locate-path-5.0.0.tgz", - "integrity": "sha512-t7hw9pI+WvuwNJXwk5zVHpyhIqzg2qTlklJOf0mVxGSbe3Fp2VieZcduNYjaLDoy6p9uGpQEGWG87WpMKlNq8g==", - "dev": true, - "requires": { - "p-locate": "^4.1.0" - } - }, - "p-limit": { - "version": "2.3.0", - "resolved": "https://registry.npmjs.org/p-limit/-/p-limit-2.3.0.tgz", - "integrity": "sha512-//88mFWSJx8lxCzwdAABTJL2MyWB12+eIY7MDL2SqLmAkeKU9qxRvWuSyTjm3FUmpBEMuFfckAIqEaVGUDxb6w==", - "dev": true, - "requires": { - "p-try": "^2.0.0" - } - }, - "p-locate": { - "version": "4.1.0", - "resolved": "https://registry.npmjs.org/p-locate/-/p-locate-4.1.0.tgz", - "integrity": "sha512-R79ZZ/0wAxKGu3oYMlz8jy/kbhsNrS7SKZ7PxEHBgJ5+F2mtFW2fK2cOtBh1cHYkQsbzFV7I+EoRKe6Yt0oK7A==", - "dev": true, - "requires": { - "p-limit": "^2.2.0" - } - }, - "resolve-from": { - "version": "5.0.0", - "resolved": "https://registry.npmjs.org/resolve-from/-/resolve-from-5.0.0.tgz", - "integrity": "sha512-qYg9KP24dD5qka9J47d0aVky0N+b4fTU89LN9iDnjB5waksiC49rvMB0PrUJQGoTmH50XPiqOvAjDfaijGxYZw==", - "dev": true - } - } - }, - "@istanbuljs/nyc-config-typescript": { - "version": "1.0.1", - "resolved": "https://registry.npmjs.org/@istanbuljs/nyc-config-typescript/-/nyc-config-typescript-1.0.1.tgz", - "integrity": "sha512-/gz6LgVpky205LuoOfwEZmnUtaSmdk0QIMcNFj9OvxhiMhPpKftMgZmGN7jNj7jR+lr8IB1Yks3QSSSNSxfoaQ==", - "dev": true, - "requires": { - "@istanbuljs/schema": "^0.1.2" - } - }, - "@istanbuljs/schema": { - "version": "0.1.3", - "resolved": "https://registry.npmjs.org/@istanbuljs/schema/-/schema-0.1.3.tgz", - "integrity": "sha512-ZXRY4jNvVgSVQ8DL3LTcakaAtXwTVUxE81hslsyD2AtoXW/wVob10HkOJ1X/pAlcI7D+2YoZKg5do8G/w6RYgA==", - "dev": true - }, - "@nodelib/fs.scandir": { - "version": "2.1.5", - "resolved": "https://registry.npmjs.org/@nodelib/fs.scandir/-/fs.scandir-2.1.5.tgz", - "integrity": "sha512-vq24Bq3ym5HEQm2NKCr3yXDwjc7vTsEThRDnkp2DK9p1uqLR+DHurm/NOTo0KG7HYHU7eppKZj3MyqYuMBf62g==", - "dev": true, - "requires": { - "@nodelib/fs.stat": "2.0.5", - "run-parallel": "^1.1.9" - } - }, - "@nodelib/fs.stat": { - "version": "2.0.5", - "resolved": "https://registry.npmjs.org/@nodelib/fs.stat/-/fs.stat-2.0.5.tgz", - "integrity": "sha512-RkhPPp2zrqDAQA/2jNhnztcPAlv64XdhIp7a7454A5ovI7Bukxgt7MX7udwAu3zg1DcpPU0rz3VV1SeaqvY4+A==", - "dev": true - }, - "@nodelib/fs.walk": { - "version": "1.2.8", - "resolved": "https://registry.npmjs.org/@nodelib/fs.walk/-/fs.walk-1.2.8.tgz", - "integrity": "sha512-oGB+UxlgWcgQkgwo8GcEGwemoTFt3FIO9ababBmaGwXIoBKZ+GTy0pP185beGg7Llih/NSHSV2XAs1lnznocSg==", - "dev": true, - "requires": { - "@nodelib/fs.scandir": "2.1.5", - "fastq": "^1.6.0" - } - }, - "@octokit/auth-token": { - "version": "2.5.0", - "resolved": "https://registry.npmjs.org/@octokit/auth-token/-/auth-token-2.5.0.tgz", - "integrity": "sha512-r5FVUJCOLl19AxiuZD2VRZ/ORjp/4IN98Of6YJoJOkY75CIBuYfmiNHGrDwXr+aLGG55igl9QrxX3hbiXlLb+g==", - "dev": true, - "requires": { - "@octokit/types": "^6.0.3" - } - }, - "@octokit/core": { - "version": "3.5.1", - "resolved": "https://registry.npmjs.org/@octokit/core/-/core-3.5.1.tgz", - "integrity": "sha512-omncwpLVxMP+GLpLPgeGJBF6IWJFjXDS5flY5VbppePYX9XehevbDykRH9PdCdvqt9TS5AOTiDide7h0qrkHjw==", - "dev": true, - "requires": { - "@octokit/auth-token": "^2.4.4", - "@octokit/graphql": "^4.5.8", - "@octokit/request": "^5.6.0", - "@octokit/request-error": "^2.0.5", - "@octokit/types": "^6.0.3", - "before-after-hook": "^2.2.0", - "universal-user-agent": "^6.0.0" - } - }, - "@octokit/endpoint": { - "version": "6.0.12", - "resolved": "https://registry.npmjs.org/@octokit/endpoint/-/endpoint-6.0.12.tgz", - "integrity": "sha512-lF3puPwkQWGfkMClXb4k/eUT/nZKQfxinRWJrdZaJO85Dqwo/G0yOC434Jr2ojwafWJMYqFGFa5ms4jJUgujdA==", - "dev": true, - "requires": { - "@octokit/types": "^6.0.3", - "is-plain-object": "^5.0.0", - "universal-user-agent": "^6.0.0" - } - }, - "@octokit/graphql": { - "version": "4.8.0", - "resolved": "https://registry.npmjs.org/@octokit/graphql/-/graphql-4.8.0.tgz", - "integrity": "sha512-0gv+qLSBLKF0z8TKaSKTsS39scVKF9dbMxJpj3U0vC7wjNWFuIpL/z76Qe2fiuCbDRcJSavkXsVtMS6/dtQQsg==", - "dev": true, - "requires": { - "@octokit/request": "^5.6.0", - "@octokit/types": "^6.0.3", - "universal-user-agent": "^6.0.0" - } - }, - "@octokit/openapi-types": { - "version": "11.2.0", - "resolved": "https://registry.npmjs.org/@octokit/openapi-types/-/openapi-types-11.2.0.tgz", - "integrity": "sha512-PBsVO+15KSlGmiI8QAzaqvsNlZlrDlyAJYcrXBCvVUxCp7VnXjkwPoFHgjEJXx3WF9BAwkA6nfCUA7i9sODzKA==", - "dev": true - }, - "@octokit/plugin-paginate-rest": { - "version": "2.17.0", - "resolved": "https://registry.npmjs.org/@octokit/plugin-paginate-rest/-/plugin-paginate-rest-2.17.0.tgz", - "integrity": "sha512-tzMbrbnam2Mt4AhuyCHvpRkS0oZ5MvwwcQPYGtMv4tUa5kkzG58SVB0fcsLulOZQeRnOgdkZWkRUiyBlh0Bkyw==", - "dev": true, - "requires": { - "@octokit/types": "^6.34.0" - } - }, - "@octokit/plugin-request-log": { - "version": "1.0.4", - "resolved": "https://registry.npmjs.org/@octokit/plugin-request-log/-/plugin-request-log-1.0.4.tgz", - "integrity": "sha512-mLUsMkgP7K/cnFEw07kWqXGF5LKrOkD+lhCrKvPHXWDywAwuDUeDwWBpc69XK3pNX0uKiVt8g5z96PJ6z9xCFA==", - "dev": true, - "requires": {} - }, - "@octokit/plugin-rest-endpoint-methods": { - "version": "5.13.0", - "resolved": "https://registry.npmjs.org/@octokit/plugin-rest-endpoint-methods/-/plugin-rest-endpoint-methods-5.13.0.tgz", - "integrity": "sha512-uJjMTkN1KaOIgNtUPMtIXDOjx6dGYysdIFhgA52x4xSadQCz3b/zJexvITDVpANnfKPW/+E0xkOvLntqMYpviA==", - "dev": true, - "requires": { - "@octokit/types": "^6.34.0", - "deprecation": "^2.3.1" - } - }, - "@octokit/request": { - "version": "5.6.2", - "resolved": "https://registry.npmjs.org/@octokit/request/-/request-5.6.2.tgz", - "integrity": "sha512-je66CvSEVf0jCpRISxkUcCa0UkxmFs6eGDRSbfJtAVwbLH5ceqF+YEyC8lj8ystKyZTy8adWr0qmkY52EfOeLA==", - "dev": true, - "requires": { - "@octokit/endpoint": "^6.0.1", - "@octokit/request-error": "^2.1.0", - "@octokit/types": "^6.16.1", - "is-plain-object": "^5.0.0", - "node-fetch": "^2.6.1", - "universal-user-agent": "^6.0.0" - } - }, - "@octokit/request-error": { - "version": "2.1.0", - "resolved": "https://registry.npmjs.org/@octokit/request-error/-/request-error-2.1.0.tgz", - "integrity": "sha512-1VIvgXxs9WHSjicsRwq8PlR2LR2x6DwsJAaFgzdi0JfJoGSO8mYI/cHJQ+9FbN21aa+DrgNLnwObmyeSC8Rmpg==", - "dev": true, - "requires": { - "@octokit/types": "^6.0.3", - "deprecation": "^2.0.0", - "once": "^1.4.0" - } - }, - "@octokit/rest": { - "version": "18.10.0", - "resolved": "https://registry.npmjs.org/@octokit/rest/-/rest-18.10.0.tgz", - "integrity": "sha512-esHR5OKy38bccL/sajHqZudZCvmv4yjovMJzyXlphaUo7xykmtOdILGJ3aAm0mFHmMLmPFmDMJXf39cAjNJsrw==", - "dev": true, - "requires": { - "@octokit/core": "^3.5.1", - "@octokit/plugin-paginate-rest": "^2.16.0", - "@octokit/plugin-request-log": "^1.0.4", - "@octokit/plugin-rest-endpoint-methods": "^5.9.0" - } - }, - "@octokit/types": { - "version": "6.34.0", - "resolved": "https://registry.npmjs.org/@octokit/types/-/types-6.34.0.tgz", - "integrity": "sha512-s1zLBjWhdEI2zwaoSgyOFoKSl109CUcVBCc7biPJ3aAf6LGLU6szDvi31JPU7bxfla2lqfhjbbg/5DdFNxOwHw==", - "dev": true, - "requires": { - "@octokit/openapi-types": "^11.2.0" - } - }, - "@redis/client": { - "version": "file:packages/client", - "requires": { - "@istanbuljs/nyc-config-typescript": "^1.0.1", - "@redis/test-utils": "*", - "@types/node": "^16.11.6", - "@types/sinon": "^10.0.6", - "@types/yallist": "^4.0.1", - "@typescript-eslint/eslint-plugin": "^5.2.0", - "@typescript-eslint/parser": "^5.2.0", - "cluster-key-slot": "1.1.0", - "eslint": "^8.1.0", - "generic-pool": "3.8.2", - "nyc": "^15.1.0", - "redis-parser": "3.0.0", - "release-it": "^14.11.6", - "sinon": "^11.1.2", - "source-map-support": "^0.5.20", - "ts-node": "^10.4.0", - "typedoc": "^0.22.7", - "typedoc-github-wiki-theme": "^0.6.0", - "typedoc-plugin-markdown": "^3.11.3", - "typescript": "^4.4.4", - "yallist": "4.0.0" - } - }, - "@redis/json": { - "version": "file:packages/json", - "requires": { - "@istanbuljs/nyc-config-typescript": "^1.0.1", - "@redis/test-utils": "*", - "@types/node": "^16.11.6", - "nyc": "^15.1.0", - "release-it": "^14.11.6", - "source-map-support": "^0.5.20", - "ts-node": "^10.4.0", - "typescript": "^4.4.4" - } - }, - "@redis/search": { - "version": "file:packages/search", - "requires": { - "@istanbuljs/nyc-config-typescript": "^1.0.1", - "@redis/test-utils": "*", - "@types/node": "^16.11.6", - "nyc": "^15.1.0", - "release-it": "^14.11.6", - "source-map-support": "^0.5.20", - "ts-node": "^10.4.0", - "typescript": "^4.4.4" - } - }, - "@redis/test-utils": { - "version": "file:packages/test-utils", - "requires": { - "@istanbuljs/nyc-config-typescript": "^1.0.1", - "@tsconfig/node12": "^1.0.9", - "@types/mocha": "^9.0.0", - "@types/node": "^16.11.6", - "@types/yargs": "^17.0.5", - "mocha": "^9.1.3", - "nyc": "^15.1.0", - "release-it": "^14.11.6", - "source-map-support": "^0.5.20", - "ts-node": "^10.4.0", - "typescript": "^4.4.4", - "yargs": "^17.2.1" - } - }, - "@redis/time-series": { - "version": "file:packages/time-series", - "requires": { - "@istanbuljs/nyc-config-typescript": "^1.0.1", - "@redis/test-utils": "*", - "@types/node": "^16.11.6", - "nyc": "^15.1.0", - "release-it": "^14.11.6", - "source-map-support": "^0.5.20", - "ts-node": "^10.4.0", - "typescript": "^4.4.4" - } - }, - "@sindresorhus/is": { - "version": "4.2.0", - "resolved": "https://registry.npmjs.org/@sindresorhus/is/-/is-4.2.0.tgz", - "integrity": "sha512-VkE3KLBmJwcCaVARtQpfuKcKv8gcBmUubrfHGF84dXuuW6jgsRYxPtzcIhPyK9WAPpRt2/xY6zkD9MnRaJzSyw==", - "dev": true - }, - "@sinonjs/commons": { - "version": "1.8.3", - "resolved": "https://registry.npmjs.org/@sinonjs/commons/-/commons-1.8.3.tgz", - "integrity": "sha512-xkNcLAn/wZaX14RPlwizcKicDk9G3F8m2nU3L7Ukm5zBgTwiT0wsoFAHx9Jq56fJA1z/7uKGtCRu16sOUCLIHQ==", - "dev": true, - "requires": { - "type-detect": "4.0.8" - } - }, - "@sinonjs/fake-timers": { - "version": "7.1.2", - "resolved": "https://registry.npmjs.org/@sinonjs/fake-timers/-/fake-timers-7.1.2.tgz", - "integrity": "sha512-iQADsW4LBMISqZ6Ci1dupJL9pprqwcVFTcOsEmQOEhW+KLCVn/Y4Jrvg2k19fIHCp+iFprriYPTdRcQR8NbUPg==", - "dev": true, - "requires": { - "@sinonjs/commons": "^1.7.0" - } - }, - "@sinonjs/samsam": { - "version": "6.0.2", - "resolved": "https://registry.npmjs.org/@sinonjs/samsam/-/samsam-6.0.2.tgz", - "integrity": "sha512-jxPRPp9n93ci7b8hMfJOFDPRLFYadN6FSpeROFTR4UNF4i5b+EK6m4QXPO46BDhFgRy1JuS87zAnFOzCUwMJcQ==", - "dev": true, - "requires": { - "@sinonjs/commons": "^1.6.0", - "lodash.get": "^4.4.2", - "type-detect": "^4.0.8" - } - }, - "@sinonjs/text-encoding": { - "version": "0.7.1", - "resolved": "https://registry.npmjs.org/@sinonjs/text-encoding/-/text-encoding-0.7.1.tgz", - "integrity": "sha512-+iTbntw2IZPb/anVDbypzfQa+ay64MW0Zo8aJ8gZPWMMK6/OubMVb6lUPMagqjOPnmtauXnFCACVl3O7ogjeqQ==", - "dev": true - }, - "@szmarczak/http-timer": { - "version": "4.0.6", - "resolved": "https://registry.npmjs.org/@szmarczak/http-timer/-/http-timer-4.0.6.tgz", - "integrity": "sha512-4BAffykYOgO+5nzBWYwE3W90sBgLJoUPRWWcL8wlyiM8IB8ipJz3UMJ9KXQd1RKQXpKp8Tutn80HZtWsu2u76w==", - "dev": true, - "requires": { - "defer-to-connect": "^2.0.0" - } - }, - "@tsconfig/node10": { - "version": "1.0.8", - "resolved": "https://registry.npmjs.org/@tsconfig/node10/-/node10-1.0.8.tgz", - "integrity": "sha512-6XFfSQmMgq0CFLY1MslA/CPUfhIL919M1rMsa5lP2P097N2Wd1sSX0tx1u4olM16fLNhtHZpRhedZJphNJqmZg==", - "dev": true - }, - "@tsconfig/node12": { - "version": "1.0.9", - "resolved": "https://registry.npmjs.org/@tsconfig/node12/-/node12-1.0.9.tgz", - "integrity": "sha512-/yBMcem+fbvhSREH+s14YJi18sp7J9jpuhYByADT2rypfajMZZN4WQ6zBGgBKp53NKmqI36wFYDb3yaMPurITw==", - "dev": true - }, - "@tsconfig/node14": { - "version": "1.0.1", - "resolved": "https://registry.npmjs.org/@tsconfig/node14/-/node14-1.0.1.tgz", - "integrity": "sha512-509r2+yARFfHHE7T6Puu2jjkoycftovhXRqW328PDXTVGKihlb1P8Z9mMZH04ebyajfRY7dedfGynlrFHJUQCg==", - "dev": true - }, - "@tsconfig/node16": { - "version": "1.0.2", - "resolved": "https://registry.npmjs.org/@tsconfig/node16/-/node16-1.0.2.tgz", - "integrity": "sha512-eZxlbI8GZscaGS7kkc/trHTT5xgrjH3/1n2JDwusC9iahPKWMRvRjJSAN5mCXviuTGQ/lHnhvv8Q1YTpnfz9gA==", - "dev": true - }, - "@types/cacheable-request": { - "version": "6.0.2", - "resolved": "https://registry.npmjs.org/@types/cacheable-request/-/cacheable-request-6.0.2.tgz", - "integrity": "sha512-B3xVo+dlKM6nnKTcmm5ZtY/OL8bOAOd2Olee9M1zft65ox50OzjEHW91sDiU9j6cvW8Ejg1/Qkf4xd2kugApUA==", - "dev": true, - "requires": { - "@types/http-cache-semantics": "*", - "@types/keyv": "*", - "@types/node": "*", - "@types/responselike": "*" - } - }, - "@types/http-cache-semantics": { - "version": "4.0.1", - "resolved": "https://registry.npmjs.org/@types/http-cache-semantics/-/http-cache-semantics-4.0.1.tgz", - "integrity": "sha512-SZs7ekbP8CN0txVG2xVRH6EgKmEm31BOxA07vkFaETzZz1xh+cbt8BcI0slpymvwhx5dlFnQG2rTlPVQn+iRPQ==", - "dev": true - }, - "@types/json-schema": { - "version": "7.0.9", - "resolved": "https://registry.npmjs.org/@types/json-schema/-/json-schema-7.0.9.tgz", - "integrity": "sha512-qcUXuemtEu+E5wZSJHNxUXeCZhAfXKQ41D+duX+VYPde7xyEVZci+/oXKJL13tnRs9lR2pr4fod59GT6/X1/yQ==", - "dev": true - }, - "@types/keyv": { - "version": "3.1.3", - "resolved": "https://registry.npmjs.org/@types/keyv/-/keyv-3.1.3.tgz", - "integrity": "sha512-FXCJgyyN3ivVgRoml4h94G/p3kY+u/B86La+QptcqJaWtBWtmc6TtkNfS40n9bIvyLteHh7zXOtgbobORKPbDg==", - "dev": true, - "requires": { - "@types/node": "*" - } - }, - "@types/mocha": { - "version": "9.0.0", - "resolved": "https://registry.npmjs.org/@types/mocha/-/mocha-9.0.0.tgz", - "integrity": "sha512-scN0hAWyLVAvLR9AyW7HoFF5sJZglyBsbPuHO4fv7JRvfmPBMfp1ozWqOf/e4wwPNxezBZXRfWzMb6iFLgEVRA==", - "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==", - "dev": true - }, - "@types/parse-json": { - "version": "4.0.0", - "resolved": "https://registry.npmjs.org/@types/parse-json/-/parse-json-4.0.0.tgz", - "integrity": "sha512-//oorEZjL6sbPcKUaCdIGlIUeH26mgzimjBB77G6XRgnDl/L5wOnpyBGRe/Mmf5CVW3PwEBE1NjiMZ/ssFh4wA==", - "dev": true - }, - "@types/responselike": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/@types/responselike/-/responselike-1.0.0.tgz", - "integrity": "sha512-85Y2BjiufFzaMIlvJDvTTB8Fxl2xfLo4HgmHzVBz08w4wDePCTjYw66PdrolO0kzli3yam/YCgRufyo1DdQVTA==", - "dev": true, - "requires": { - "@types/node": "*" - } - }, - "@types/sinon": { - "version": "10.0.6", - "resolved": "https://registry.npmjs.org/@types/sinon/-/sinon-10.0.6.tgz", - "integrity": "sha512-6EF+wzMWvBNeGrfP3Nx60hhx+FfwSg1JJBLAAP/IdIUq0EYkqCYf70VT3PhuhPX9eLD+Dp+lNdpb/ZeHG8Yezg==", - "dev": true, - "requires": { - "@sinonjs/fake-timers": "^7.1.0" - } - }, - "@types/yallist": { - "version": "4.0.1", - "resolved": "https://registry.npmjs.org/@types/yallist/-/yallist-4.0.1.tgz", - "integrity": "sha512-G3FNJfaYtN8URU6wd6+uwFI62KO79j7n3XTYcwcFncP8gkfoi0b821GoVVt0oqKVnCqKYOMNKIGpakPoFhzAGA==", - "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==", - "dev": true, - "requires": { - "@types/yargs-parser": "*" - } - }, - "@types/yargs-parser": { - "version": "20.2.1", - "resolved": "https://registry.npmjs.org/@types/yargs-parser/-/yargs-parser-20.2.1.tgz", - "integrity": "sha512-7tFImggNeNBVMsn0vLrpn1H1uPrUBdnARPTpZoitY37ZrdJREzf7I16tMrlK3hen349gr1NYh8CmZQa7CTG6Aw==", - "dev": true - }, - "@typescript-eslint/eslint-plugin": { - "version": "5.3.1", - "resolved": "https://registry.npmjs.org/@typescript-eslint/eslint-plugin/-/eslint-plugin-5.3.1.tgz", - "integrity": "sha512-cFImaoIr5Ojj358xI/SDhjog57OK2NqlpxwdcgyxDA3bJlZcJq5CPzUXtpD7CxI2Hm6ATU7w5fQnnkVnmwpHqw==", - "dev": true, - "requires": { - "@typescript-eslint/experimental-utils": "5.3.1", - "@typescript-eslint/scope-manager": "5.3.1", - "debug": "^4.3.2", - "functional-red-black-tree": "^1.0.1", - "ignore": "^5.1.8", - "regexpp": "^3.2.0", - "semver": "^7.3.5", - "tsutils": "^3.21.0" - } - }, - "@typescript-eslint/experimental-utils": { - "version": "5.3.1", - "resolved": "https://registry.npmjs.org/@typescript-eslint/experimental-utils/-/experimental-utils-5.3.1.tgz", - "integrity": "sha512-RgFn5asjZ5daUhbK5Sp0peq0SSMytqcrkNfU4pnDma2D8P3ElZ6JbYjY8IMSFfZAJ0f3x3tnO3vXHweYg0g59w==", - "dev": true, - "requires": { - "@types/json-schema": "^7.0.9", - "@typescript-eslint/scope-manager": "5.3.1", - "@typescript-eslint/types": "5.3.1", - "@typescript-eslint/typescript-estree": "5.3.1", - "eslint-scope": "^5.1.1", - "eslint-utils": "^3.0.0" - } - }, - "@typescript-eslint/parser": { - "version": "5.3.1", - "resolved": "https://registry.npmjs.org/@typescript-eslint/parser/-/parser-5.3.1.tgz", - "integrity": "sha512-TD+ONlx5c+Qhk21x9gsJAMRohWAUMavSOmJgv3JGy9dgPhuBd5Wok0lmMClZDyJNLLZK1JRKiATzCKZNUmoyfw==", - "dev": true, - "requires": { - "@typescript-eslint/scope-manager": "5.3.1", - "@typescript-eslint/types": "5.3.1", - "@typescript-eslint/typescript-estree": "5.3.1", - "debug": "^4.3.2" - } - }, - "@typescript-eslint/scope-manager": { - "version": "5.3.1", - "resolved": "https://registry.npmjs.org/@typescript-eslint/scope-manager/-/scope-manager-5.3.1.tgz", - "integrity": "sha512-XksFVBgAq0Y9H40BDbuPOTUIp7dn4u8oOuhcgGq7EoDP50eqcafkMVGrypyVGvDYHzjhdUCUwuwVUK4JhkMAMg==", - "dev": true, - "requires": { - "@typescript-eslint/types": "5.3.1", - "@typescript-eslint/visitor-keys": "5.3.1" - } - }, - "@typescript-eslint/types": { - "version": "5.3.1", - "resolved": "https://registry.npmjs.org/@typescript-eslint/types/-/types-5.3.1.tgz", - "integrity": "sha512-bG7HeBLolxKHtdHG54Uac750eXuQQPpdJfCYuw4ZI3bZ7+GgKClMWM8jExBtp7NSP4m8PmLRM8+lhzkYnSmSxQ==", - "dev": true - }, - "@typescript-eslint/typescript-estree": { - "version": "5.3.1", - "resolved": "https://registry.npmjs.org/@typescript-eslint/typescript-estree/-/typescript-estree-5.3.1.tgz", - "integrity": "sha512-PwFbh/PKDVo/Wct6N3w+E4rLZxUDgsoII/GrWM2A62ETOzJd4M6s0Mu7w4CWsZraTbaC5UQI+dLeyOIFF1PquQ==", - "dev": true, - "requires": { - "@typescript-eslint/types": "5.3.1", - "@typescript-eslint/visitor-keys": "5.3.1", - "debug": "^4.3.2", - "globby": "^11.0.4", - "is-glob": "^4.0.3", - "semver": "^7.3.5", - "tsutils": "^3.21.0" - } - }, - "@typescript-eslint/visitor-keys": { - "version": "5.3.1", - "resolved": "https://registry.npmjs.org/@typescript-eslint/visitor-keys/-/visitor-keys-5.3.1.tgz", - "integrity": "sha512-3cHUzUuVTuNHx0Gjjt5pEHa87+lzyqOiHXy/Gz+SJOCW1mpw9xQHIIEwnKn+Thph1mgWyZ90nboOcSuZr/jTTQ==", - "dev": true, - "requires": { - "@typescript-eslint/types": "5.3.1", - "eslint-visitor-keys": "^3.0.0" - } - }, - "@ungap/promise-all-settled": { - "version": "1.1.2", - "resolved": "https://registry.npmjs.org/@ungap/promise-all-settled/-/promise-all-settled-1.1.2.tgz", - "integrity": "sha512-sL/cEvJWAnClXw0wHk85/2L0G6Sj8UB0Ctc1TEMbKSsmpRosqhwj9gWgFRZSrBr2f9tiXISwNhCPmlfqUqyb9Q==", - "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==", - "dev": true - }, - "acorn-jsx": { - "version": "5.3.2", - "resolved": "https://registry.npmjs.org/acorn-jsx/-/acorn-jsx-5.3.2.tgz", - "integrity": "sha512-rq9s+JNhf0IChjtDXxllJ7g41oZk5SlXtp0LHwyA5cejwn7vKmKp4pPri6YEePv2PU65sAsegbXtIinmDFDXgQ==", - "dev": true, - "requires": {} - }, - "acorn-walk": { - "version": "8.2.0", - "resolved": "https://registry.npmjs.org/acorn-walk/-/acorn-walk-8.2.0.tgz", - "integrity": "sha512-k+iyHEuPgSw6SbuDpGQM+06HQUa04DZ3o+F6CSzXMvvI5KMvnaEqXe+YVe555R9nn6GPt404fos4wcgpw12SDA==", - "dev": true - }, - "aggregate-error": { - "version": "3.1.0", - "resolved": "https://registry.npmjs.org/aggregate-error/-/aggregate-error-3.1.0.tgz", - "integrity": "sha512-4I7Td01quW/RpocfNayFdFVk1qSuoh0E7JrbRJ16nH01HhKFQ88INq9Sd+nd72zqRySlr9BmDA8xlEJ6vJMrYA==", - "dev": true, - "requires": { - "clean-stack": "^2.0.0", - "indent-string": "^4.0.0" - } - }, - "ajv": { - "version": "6.12.6", - "resolved": "https://registry.npmjs.org/ajv/-/ajv-6.12.6.tgz", - "integrity": "sha512-j3fVLgvTo527anyYyJOGTYJbG+vnnQYvE0m5mmkc1TK+nxAppkCLMIL0aZ4dblVCNoGShhm+kzE4ZUykBoMg4g==", - "dev": true, - "requires": { - "fast-deep-equal": "^3.1.1", - "fast-json-stable-stringify": "^2.0.0", - "json-schema-traverse": "^0.4.1", - "uri-js": "^4.2.2" - } - }, - "ansi-align": { - "version": "3.0.1", - "resolved": "https://registry.npmjs.org/ansi-align/-/ansi-align-3.0.1.tgz", - "integrity": "sha512-IOfwwBF5iczOjp/WeY4YxyjqAFMQoZufdQWDd19SEExbVLNXqvpzSJ/M7Za4/sCPmQ0+GRquoA7bGcINcxew6w==", - "dev": true, - "requires": { - "string-width": "^4.1.0" - } - }, - "ansi-colors": { - "version": "4.1.1", - "resolved": "https://registry.npmjs.org/ansi-colors/-/ansi-colors-4.1.1.tgz", - "integrity": "sha512-JoX0apGbHaUJBNl6yF+p6JAFYZ666/hhCGKN5t9QFjbJQKUU/g8MNbFDbvfrgKXvI1QpZplPOnwIo99lX/AAmA==", - "dev": true - }, - "ansi-escapes": { - "version": "4.3.2", - "resolved": "https://registry.npmjs.org/ansi-escapes/-/ansi-escapes-4.3.2.tgz", - "integrity": "sha512-gKXj5ALrKWQLsYG9jlTRmR/xKluxHV+Z9QEwNIgCfM1/uwPMCuzVVnh5mwTd+OuBZcwSIMbqssNWRm1lE51QaQ==", - "dev": true, - "requires": { - "type-fest": "^0.21.3" - }, - "dependencies": { - "type-fest": { - "version": "0.21.3", - "resolved": "https://registry.npmjs.org/type-fest/-/type-fest-0.21.3.tgz", - "integrity": "sha512-t0rzBq87m3fVcduHDUFhKmyyX+9eo6WQjZvf51Ea/M0Q7+T374Jp1aUiyUl0GKxp8M/OETVHSDvmkyPgvX+X2w==", - "dev": true - } - } - }, - "ansi-regex": { - "version": "5.0.1", - "resolved": "https://registry.npmjs.org/ansi-regex/-/ansi-regex-5.0.1.tgz", - "integrity": "sha512-quJQXlTSUGL2LH9SUXo8VwsY4soanhgo6LNSm84E1LBcE8s3O0wpdiRzyR9z/ZZJMlMWv37qOOb9pdJlMUEKFQ==", - "dev": true - }, - "ansi-styles": { - "version": "4.3.0", - "resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-4.3.0.tgz", - "integrity": "sha512-zbB9rCJAT1rbjiVDb2hqKFHNYLxgtk8NURxZ3IZwD3F6NtxbXZQCnnSi1Lkx+IDohdPlFp222wVALIheZJQSEg==", - "dev": true, - "requires": { - "color-convert": "^2.0.1" - } - }, - "anymatch": { - "version": "3.1.2", - "resolved": "https://registry.npmjs.org/anymatch/-/anymatch-3.1.2.tgz", - "integrity": "sha512-P43ePfOAIupkguHUycrc4qJ9kz8ZiuOUijaETwX7THt0Y/GNK7v0aa8rY816xWjZ7rJdA5XdMcpVFTKMq+RvWg==", - "dev": true, - "requires": { - "normalize-path": "^3.0.0", - "picomatch": "^2.0.4" - } - }, - "append-transform": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/append-transform/-/append-transform-2.0.0.tgz", - "integrity": "sha512-7yeyCEurROLQJFv5Xj4lEGTy0borxepjFv1g22oAdqFu//SrAlDl1O1Nxx15SH1RoliUml6p8dwJW9jvZughhg==", - "dev": true, - "requires": { - "default-require-extensions": "^3.0.0" - } - }, - "archy": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/archy/-/archy-1.0.0.tgz", - "integrity": "sha1-+cjBN1fMHde8N5rHeyxipcKGjEA=", - "dev": true - }, - "arg": { - "version": "4.1.3", - "resolved": "https://registry.npmjs.org/arg/-/arg-4.1.3.tgz", - "integrity": "sha512-58S9QDqG0Xx27YwPSt9fJxivjYl432YCwfDMfZ+71RAqUrZef7LrKQZ3LHLOwCS4FLNBplP533Zx895SeOCHvA==", - "dev": true - }, - "argparse": { - "version": "2.0.1", - "resolved": "https://registry.npmjs.org/argparse/-/argparse-2.0.1.tgz", - "integrity": "sha512-8+9WqebbFzpX9OR+Wa6O29asIogeRMzcGtAINdpMHHyAg10f05aSFVBbcEqGf/PXw1EjAZ+q2/bEBg3DvurK3Q==", - "dev": true - }, - "array-union": { - "version": "2.1.0", - "resolved": "https://registry.npmjs.org/array-union/-/array-union-2.1.0.tgz", - "integrity": "sha512-HGyxoOTYUyCM6stUe6EJgnd4EoewAI7zMdfqO+kGjnlZmBDz/cR5pf8r/cR4Wq60sL/p0IkcjUEEPwS3GFrIyw==", - "dev": true - }, - "async-retry": { - "version": "1.3.3", - "resolved": "https://registry.npmjs.org/async-retry/-/async-retry-1.3.3.tgz", - "integrity": "sha512-wfr/jstw9xNi/0teMHrRW7dsz3Lt5ARhYNZ2ewpadnhaIp5mbALhOAP+EAdsC7t4Z6wqsDVv9+W6gm1Dk9mEyw==", - "dev": true, - "requires": { - "retry": "0.13.1" - } - }, - "asynckit": { - "version": "0.4.0", - "resolved": "https://registry.npmjs.org/asynckit/-/asynckit-0.4.0.tgz", - "integrity": "sha1-x57Zf380y48robyXkLzDZkdLS3k=", - "dev": true - }, - "balanced-match": { - "version": "1.0.2", - "resolved": "https://registry.npmjs.org/balanced-match/-/balanced-match-1.0.2.tgz", - "integrity": "sha512-3oSeUO0TMV67hN1AmbXsK4yaqU7tjiHlbxRDZOpH0KW9+CeX4bRAaX0Anxt0tx2MrpRpWwQaPwIlISEJhYU5Pw==", - "dev": true - }, - "base64-js": { - "version": "1.5.1", - "resolved": "https://registry.npmjs.org/base64-js/-/base64-js-1.5.1.tgz", - "integrity": "sha512-AKpaYlHn8t4SVbOHCy+b5+KKgvR4vrsD8vbvrbiQJps7fKDTkjkDry6ji0rUJjC0kzbNePLwzxq8iypo41qeWA==", - "dev": true - }, - "before-after-hook": { - "version": "2.2.2", - "resolved": "https://registry.npmjs.org/before-after-hook/-/before-after-hook-2.2.2.tgz", - "integrity": "sha512-3pZEU3NT5BFUo/AD5ERPWOgQOCZITni6iavr5AUw5AUwQjMlI0kzu5btnyD39AF0gUEsDPwJT+oY1ORBJijPjQ==", - "dev": true - }, - "binary-extensions": { - "version": "2.2.0", - "resolved": "https://registry.npmjs.org/binary-extensions/-/binary-extensions-2.2.0.tgz", - "integrity": "sha512-jDctJ/IVQbZoJykoeHbhXpOlNBqGNcwXJKJog42E5HDPUwQTSdjCHdihjj0DlnheQ7blbT6dHOafNAiS8ooQKA==", - "dev": true - }, - "bl": { - "version": "4.1.0", - "resolved": "https://registry.npmjs.org/bl/-/bl-4.1.0.tgz", - "integrity": "sha512-1W07cM9gS6DcLperZfFSj+bWLtaPGSOHWhPiGzXmvVJbRLdG82sH/Kn8EtW1VqWVA54AKf2h5k5BbnIbwF3h6w==", - "dev": true, - "requires": { - "buffer": "^5.5.0", - "inherits": "^2.0.4", - "readable-stream": "^3.4.0" - } - }, - "boxen": { - "version": "5.1.2", - "resolved": "https://registry.npmjs.org/boxen/-/boxen-5.1.2.tgz", - "integrity": "sha512-9gYgQKXx+1nP8mP7CzFyaUARhg7D3n1dF/FnErWmu9l6JvGpNUN278h0aSb+QjoiKSWG+iZ3uHrcqk0qrY9RQQ==", - "dev": true, - "requires": { - "ansi-align": "^3.0.0", - "camelcase": "^6.2.0", - "chalk": "^4.1.0", - "cli-boxes": "^2.2.1", - "string-width": "^4.2.2", - "type-fest": "^0.20.2", - "widest-line": "^3.1.0", - "wrap-ansi": "^7.0.0" - }, - "dependencies": { - "camelcase": { - "version": "6.2.0", - "resolved": "https://registry.npmjs.org/camelcase/-/camelcase-6.2.0.tgz", - "integrity": "sha512-c7wVvbw3f37nuobQNtgsgG9POC9qMbNuMQmTCqZv23b6MIz0fcYpBiOlv9gEN/hdLdnZTDQhg6e9Dq5M1vKvfg==", - "dev": true - } - } - }, - "brace-expansion": { - "version": "1.1.11", - "resolved": "https://registry.npmjs.org/brace-expansion/-/brace-expansion-1.1.11.tgz", - "integrity": "sha512-iCuPHDFgrHX7H2vEI/5xpz07zSHB00TpugqhmYtVmMO6518mCuRMoOYFldEBl0g187ufozdaHgWKcYFb61qGiA==", - "dev": true, - "requires": { - "balanced-match": "^1.0.0", - "concat-map": "0.0.1" - } - }, - "braces": { - "version": "3.0.2", - "resolved": "https://registry.npmjs.org/braces/-/braces-3.0.2.tgz", - "integrity": "sha512-b8um+L1RzM3WDSzvhm6gIz1yfTbBt6YTlcEKAvsmqCZZFw46z626lVj9j1yEPW33H5H+lBQpZMP1k8l+78Ha0A==", - "dev": true, - "requires": { - "fill-range": "^7.0.1" - } - }, - "browser-stdout": { - "version": "1.3.1", - "resolved": "https://registry.npmjs.org/browser-stdout/-/browser-stdout-1.3.1.tgz", - "integrity": "sha512-qhAVI1+Av2X7qelOfAIYwXONood6XlZE/fXaBSmW/T5SzLAmCgzi+eiWE7fUvbHaeNBQH13UftjpXxsfLkMpgw==", - "dev": true - }, - "browserslist": { - "version": "4.17.6", - "resolved": "https://registry.npmjs.org/browserslist/-/browserslist-4.17.6.tgz", - "integrity": "sha512-uPgz3vyRTlEiCv4ee9KlsKgo2V6qPk7Jsn0KAn2OBqbqKo3iNcPEC1Ti6J4dwnz+aIRfEEEuOzC9IBk8tXUomw==", - "dev": true, - "requires": { - "caniuse-lite": "^1.0.30001274", - "electron-to-chromium": "^1.3.886", - "escalade": "^3.1.1", - "node-releases": "^2.0.1", - "picocolors": "^1.0.0" - } - }, - "buffer": { - "version": "5.7.1", - "resolved": "https://registry.npmjs.org/buffer/-/buffer-5.7.1.tgz", - "integrity": "sha512-EHcyIPBQ4BSGlvjB16k5KgAJ27CIsHY/2JBmCRReo48y9rQ3MaUzWX3KVlBa4U7MyX02HdVj0K7C3WaB3ju7FQ==", - "dev": true, - "requires": { - "base64-js": "^1.3.1", - "ieee754": "^1.1.13" - } - }, - "buffer-from": { - "version": "1.1.2", - "resolved": "https://registry.npmjs.org/buffer-from/-/buffer-from-1.1.2.tgz", - "integrity": "sha512-E+XQCRwSbaaiChtv6k6Dwgc+bx+Bs6vuKJHHl5kox/BaKbhiXzqQOwK4cO22yElGp2OCmjwVhT3HmxgyPGnJfQ==", - "dev": true - }, - "cacheable-lookup": { - "version": "5.0.4", - "resolved": "https://registry.npmjs.org/cacheable-lookup/-/cacheable-lookup-5.0.4.tgz", - "integrity": "sha512-2/kNscPhpcxrOigMZzbiWF7dz8ilhb/nIHU3EyZiXWXpeq/au8qJ8VhdftMkty3n7Gj6HIGalQG8oiBNB3AJgA==", - "dev": true - }, - "cacheable-request": { - "version": "7.0.2", - "resolved": "https://registry.npmjs.org/cacheable-request/-/cacheable-request-7.0.2.tgz", - "integrity": "sha512-pouW8/FmiPQbuGpkXQ9BAPv/Mo5xDGANgSNXzTzJ8DrKGuXOssM4wIQRjfanNRh3Yu5cfYPvcorqbhg2KIJtew==", - "dev": true, - "requires": { - "clone-response": "^1.0.2", - "get-stream": "^5.1.0", - "http-cache-semantics": "^4.0.0", - "keyv": "^4.0.0", - "lowercase-keys": "^2.0.0", - "normalize-url": "^6.0.1", - "responselike": "^2.0.0" - }, - "dependencies": { - "get-stream": { - "version": "5.2.0", - "resolved": "https://registry.npmjs.org/get-stream/-/get-stream-5.2.0.tgz", - "integrity": "sha512-nBF+F1rAZVCu/p7rjzgA+Yb4lfYXrpl7a6VmJrU8wF9I1CKvP/QwPNZHnOlwbTkY6dvtFIzFMSyQXbLoTQPRpA==", - "dev": true, - "requires": { - "pump": "^3.0.0" - } - } - } - }, - "caching-transform": { - "version": "4.0.0", - "resolved": "https://registry.npmjs.org/caching-transform/-/caching-transform-4.0.0.tgz", - "integrity": "sha512-kpqOvwXnjjN44D89K5ccQC+RUrsy7jB/XLlRrx0D7/2HNcTPqzsb6XgYoErwko6QsV184CA2YgS1fxDiiDZMWA==", - "dev": true, - "requires": { - "hasha": "^5.0.0", - "make-dir": "^3.0.0", - "package-hash": "^4.0.0", - "write-file-atomic": "^3.0.0" - } - }, - "call-bind": { - "version": "1.0.2", - "resolved": "https://registry.npmjs.org/call-bind/-/call-bind-1.0.2.tgz", - "integrity": "sha512-7O+FbCihrB5WGbFYesctwmTKae6rOiIzmz1icreWJ+0aA7LJfuqhEso2T9ncpcFtzMQtzXf2QGGueWJGTYsqrA==", - "dev": true, - "requires": { - "function-bind": "^1.1.1", - "get-intrinsic": "^1.0.2" - } - }, - "callsites": { - "version": "3.1.0", - "resolved": "https://registry.npmjs.org/callsites/-/callsites-3.1.0.tgz", - "integrity": "sha512-P8BjAsXvZS+VIDUI11hHCQEv74YT67YUi5JJFNWIqL235sBmjX4+qx9Muvls5ivyNENctx46xQLQ3aTuE7ssaQ==", - "dev": true - }, - "camelcase": { - "version": "5.3.1", - "resolved": "https://registry.npmjs.org/camelcase/-/camelcase-5.3.1.tgz", - "integrity": "sha512-L28STB170nwWS63UjtlEOE3dldQApaJXZkOI1uMFfzf3rRuPegHaHesyee+YxQ+W6SvRDQV6UrdOdRiR153wJg==", - "dev": true - }, - "caniuse-lite": { - "version": "1.0.30001278", - "resolved": "https://registry.npmjs.org/caniuse-lite/-/caniuse-lite-1.0.30001278.tgz", - "integrity": "sha512-mpF9KeH8u5cMoEmIic/cr7PNS+F5LWBk0t2ekGT60lFf0Wq+n9LspAj0g3P+o7DQhD3sUdlMln4YFAWhFYn9jg==", - "dev": true - }, - "chalk": { - "version": "4.1.2", - "resolved": "https://registry.npmjs.org/chalk/-/chalk-4.1.2.tgz", - "integrity": "sha512-oKnbhFyRIXpUuez8iBMmyEa4nbj4IOQyuhc/wy9kY7/WVPcwIO9VA668Pu8RkO7+0G76SLROeyw9CpQ061i4mA==", - "dev": true, - "requires": { - "ansi-styles": "^4.1.0", - "supports-color": "^7.1.0" - } - }, - "chardet": { - "version": "0.7.0", - "resolved": "https://registry.npmjs.org/chardet/-/chardet-0.7.0.tgz", - "integrity": "sha512-mT8iDcrh03qDGRRmoA2hmBJnxpllMR+0/0qlzjqZES6NdiWDcZkCNAk4rPFZ9Q85r27unkiNNg8ZOiwZXBHwcA==", - "dev": true - }, - "chokidar": { - "version": "3.5.2", - "resolved": "https://registry.npmjs.org/chokidar/-/chokidar-3.5.2.tgz", - "integrity": "sha512-ekGhOnNVPgT77r4K/U3GDhu+FQ2S8TnK/s2KbIGXi0SZWuwkZ2QNyfWdZW+TVfn84DpEP7rLeCt2UI6bJ8GwbQ==", - "dev": true, - "requires": { - "anymatch": "~3.1.2", - "braces": "~3.0.2", - "fsevents": "~2.3.2", - "glob-parent": "~5.1.2", - "is-binary-path": "~2.1.0", - "is-glob": "~4.0.1", - "normalize-path": "~3.0.0", - "readdirp": "~3.6.0" - }, - "dependencies": { - "glob-parent": { - "version": "5.1.2", - "resolved": "https://registry.npmjs.org/glob-parent/-/glob-parent-5.1.2.tgz", - "integrity": "sha512-AOIgSQCepiJYwP3ARnGx+5VnTu2HBYdzbGP45eLw1vr3zB3vZLeyed1sC9hnbcOc9/SrMyM5RPQrkGz4aS9Zow==", - "dev": true, - "requires": { - "is-glob": "^4.0.1" - } - } - } - }, - "ci-info": { - "version": "3.2.0", - "resolved": "https://registry.npmjs.org/ci-info/-/ci-info-3.2.0.tgz", - "integrity": "sha512-dVqRX7fLUm8J6FgHJ418XuIgDLZDkYcDFTeL6TA2gt5WlIZUQrrH6EZrNClwT/H0FateUsZkGIOPRrLbP+PR9A==", - "dev": true - }, - "clean-stack": { - "version": "2.2.0", - "resolved": "https://registry.npmjs.org/clean-stack/-/clean-stack-2.2.0.tgz", - "integrity": "sha512-4diC9HaTE+KRAMWhDhrGOECgWZxoevMc5TlkObMqNSsVU62PYzXZ/SMTjzyGAFF1YusgxGcSWTEXBhp0CPwQ1A==", - "dev": true - }, - "cli-boxes": { - "version": "2.2.1", - "resolved": "https://registry.npmjs.org/cli-boxes/-/cli-boxes-2.2.1.tgz", - "integrity": "sha512-y4coMcylgSCdVinjiDBuR8PCC2bLjyGTwEmPb9NHR/QaNU6EUOXcTY/s6VjGMD6ENSEaeQYHCY0GNGS5jfMwPw==", - "dev": true - }, - "cli-cursor": { - "version": "3.1.0", - "resolved": "https://registry.npmjs.org/cli-cursor/-/cli-cursor-3.1.0.tgz", - "integrity": "sha512-I/zHAwsKf9FqGoXM4WWRACob9+SNukZTd94DWF57E4toouRulbCxcUh6RKUEOQlYTHJnzkPMySvPNaaSLNfLZw==", - "dev": true, - "requires": { - "restore-cursor": "^3.1.0" - } - }, - "cli-spinners": { - "version": "2.6.1", - "resolved": "https://registry.npmjs.org/cli-spinners/-/cli-spinners-2.6.1.tgz", - "integrity": "sha512-x/5fWmGMnbKQAaNwN+UZlV79qBLM9JFnJuJ03gIi5whrob0xV0ofNVHy9DhwGdsMJQc2OKv0oGmLzvaqvAVv+g==", - "dev": true - }, - "cli-width": { - "version": "3.0.0", - "resolved": "https://registry.npmjs.org/cli-width/-/cli-width-3.0.0.tgz", - "integrity": "sha512-FxqpkPPwu1HjuN93Omfm4h8uIanXofW0RxVEW3k5RKx+mJJYSthzNhp32Kzxxy3YAEZ/Dc/EWN1vZRY0+kOhbw==", - "dev": true - }, - "cliui": { - "version": "7.0.4", - "resolved": "https://registry.npmjs.org/cliui/-/cliui-7.0.4.tgz", - "integrity": "sha512-OcRE68cOsVMXp1Yvonl/fzkQOyjLSu/8bhPDfQt0e0/Eb283TKP20Fs2MqoPsr9SwA595rRCA+QMzYc9nBP+JQ==", - "dev": true, - "requires": { - "string-width": "^4.2.0", - "strip-ansi": "^6.0.0", - "wrap-ansi": "^7.0.0" - } - }, - "clone": { - "version": "1.0.4", - "resolved": "https://registry.npmjs.org/clone/-/clone-1.0.4.tgz", - "integrity": "sha1-2jCcwmPfFZlMaIypAheco8fNfH4=", - "dev": true - }, - "clone-response": { - "version": "1.0.2", - "resolved": "https://registry.npmjs.org/clone-response/-/clone-response-1.0.2.tgz", - "integrity": "sha1-0dyXOSAxTfZ/vrlCI7TuNQI56Ws=", - "dev": true, - "requires": { - "mimic-response": "^1.0.0" - } - }, - "cluster-key-slot": { - "version": "1.1.0", - "resolved": "https://registry.npmjs.org/cluster-key-slot/-/cluster-key-slot-1.1.0.tgz", - "integrity": "sha512-2Nii8p3RwAPiFwsnZvukotvow2rIHM+yQ6ZcBXGHdniadkYGZYiGmkHJIbZPIV9nfv7m/U1IPMVVcAhoWFeklw==" - }, - "color-convert": { - "version": "2.0.1", - "resolved": "https://registry.npmjs.org/color-convert/-/color-convert-2.0.1.tgz", - "integrity": "sha512-RRECPsj7iu/xb5oKYcsFHSppFNnsj/52OVTRKb4zP5onXwVF3zVmmToNcOfGC+CRDpfK/U584fMg38ZHCaElKQ==", - "dev": true, - "requires": { - "color-name": "~1.1.4" - } - }, - "color-name": { - "version": "1.1.4", - "resolved": "https://registry.npmjs.org/color-name/-/color-name-1.1.4.tgz", - "integrity": "sha512-dOy+3AuW3a2wNbZHIuMZpTcgjGuLU/uBL/ubcZF9OXbDo8ff4O8yVp5Bf0efS8uEoYo5q4Fx7dY9OgQGXgAsQA==", - "dev": true - }, - "combined-stream": { - "version": "1.0.8", - "resolved": "https://registry.npmjs.org/combined-stream/-/combined-stream-1.0.8.tgz", - "integrity": "sha512-FQN4MRfuJeHf7cBbBMJFXhKSDq+2kAArBlmRBvcvFE5BB1HZKXtSFASDhdlz9zOYwxh8lDdnvmMOe/+5cdoEdg==", - "dev": true, - "requires": { - "delayed-stream": "~1.0.0" - } - }, - "commondir": { - "version": "1.0.1", - "resolved": "https://registry.npmjs.org/commondir/-/commondir-1.0.1.tgz", - "integrity": "sha1-3dgA2gxmEnOTzKWVDqloo6rxJTs=", - "dev": true - }, - "concat-map": { - "version": "0.0.1", - "resolved": "https://registry.npmjs.org/concat-map/-/concat-map-0.0.1.tgz", - "integrity": "sha1-2Klr13/Wjfd5OnMDajug1UBdR3s=", - "dev": true - }, - "configstore": { - "version": "5.0.1", - "resolved": "https://registry.npmjs.org/configstore/-/configstore-5.0.1.tgz", - "integrity": "sha512-aMKprgk5YhBNyH25hj8wGt2+D52Sw1DRRIzqBwLp2Ya9mFmY8KPvvtvmna8SxVR9JMZ4kzMD68N22vlaRpkeFA==", - "dev": true, - "requires": { - "dot-prop": "^5.2.0", - "graceful-fs": "^4.1.2", - "make-dir": "^3.0.0", - "unique-string": "^2.0.0", - "write-file-atomic": "^3.0.0", - "xdg-basedir": "^4.0.0" - } - }, - "convert-source-map": { - "version": "1.8.0", - "resolved": "https://registry.npmjs.org/convert-source-map/-/convert-source-map-1.8.0.tgz", - "integrity": "sha512-+OQdjP49zViI/6i7nIJpA8rAl4sV/JdPfU9nZs3VqOwGIgizICvuN2ru6fMd+4llL0tar18UYJXfZ/TWtmhUjA==", - "dev": true, - "requires": { - "safe-buffer": "~5.1.1" - } - }, - "cosmiconfig": { - "version": "7.0.1", - "resolved": "https://registry.npmjs.org/cosmiconfig/-/cosmiconfig-7.0.1.tgz", - "integrity": "sha512-a1YWNUV2HwGimB7dU2s1wUMurNKjpx60HxBB6xUM8Re+2s1g1IIfJvFR0/iCF+XHdE0GMTKTuLR32UQff4TEyQ==", - "dev": true, - "requires": { - "@types/parse-json": "^4.0.0", - "import-fresh": "^3.2.1", - "parse-json": "^5.0.0", - "path-type": "^4.0.0", - "yaml": "^1.10.0" - } - }, - "create-require": { - "version": "1.1.1", - "resolved": "https://registry.npmjs.org/create-require/-/create-require-1.1.1.tgz", - "integrity": "sha512-dcKFX3jn0MpIaXjisoRvexIJVEKzaq7z2rZKxf+MSr9TkdmHmsU4m2lcLojrj/FHl8mk5VxMmYA+ftRkP/3oKQ==", - "dev": true - }, - "cross-spawn": { - "version": "7.0.3", - "resolved": "https://registry.npmjs.org/cross-spawn/-/cross-spawn-7.0.3.tgz", - "integrity": "sha512-iRDPJKUPVEND7dHPO8rkbOnPpyDygcDFtWjpeWNCgy8WP2rXcxXL8TskReQl6OrB2G7+UJrags1q15Fudc7G6w==", - "dev": true, - "requires": { - "path-key": "^3.1.0", - "shebang-command": "^2.0.0", - "which": "^2.0.1" - } - }, - "crypto-random-string": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/crypto-random-string/-/crypto-random-string-2.0.0.tgz", - "integrity": "sha512-v1plID3y9r/lPhviJ1wrXpLeyUIGAZ2SHNYTEapm7/8A9nLPoyvVp3RK/EPFqn5kEznyWgYZNsRtYYIWbuG8KA==", - "dev": true - }, - "debug": { - "version": "4.3.2", - "resolved": "https://registry.npmjs.org/debug/-/debug-4.3.2.tgz", - "integrity": "sha512-mOp8wKcvj7XxC78zLgw/ZA+6TSgkoE2C/ienthhRD298T7UNwAg9diBpLRxC0mOezLl4B0xV7M0cCO6P/O0Xhw==", - "dev": true, - "requires": { - "ms": "2.1.2" - } - }, - "decamelize": { - "version": "1.2.0", - "resolved": "https://registry.npmjs.org/decamelize/-/decamelize-1.2.0.tgz", - "integrity": "sha1-9lNNFRSCabIDUue+4m9QH5oZEpA=", - "dev": true - }, - "decode-uri-component": { - "version": "0.2.0", - "resolved": "https://registry.npmjs.org/decode-uri-component/-/decode-uri-component-0.2.0.tgz", - "integrity": "sha1-6zkTMzRYd1y4TNGh+uBiEGu4dUU=", - "dev": true - }, - "decompress-response": { - "version": "6.0.0", - "resolved": "https://registry.npmjs.org/decompress-response/-/decompress-response-6.0.0.tgz", - "integrity": "sha512-aW35yZM6Bb/4oJlZncMH2LCoZtJXTRxES17vE3hoRiowU2kWHaJKFkSBDnDR+cm9J+9QhXmREyIfv0pji9ejCQ==", - "dev": true, - "requires": { - "mimic-response": "^3.1.0" - }, - "dependencies": { - "mimic-response": { - "version": "3.1.0", - "resolved": "https://registry.npmjs.org/mimic-response/-/mimic-response-3.1.0.tgz", - "integrity": "sha512-z0yWI+4FDrrweS8Zmt4Ej5HdJmky15+L2e6Wgn3+iK5fWzb6T3fhNFq2+MeTRb064c6Wr4N/wv0DzQTjNzHNGQ==", - "dev": true - } - } - }, - "deep-extend": { - "version": "0.6.0", - "resolved": "https://registry.npmjs.org/deep-extend/-/deep-extend-0.6.0.tgz", - "integrity": "sha512-LOHxIOaPYdHlJRtCQfDIVZtfw/ufM8+rVj649RIHzcm/vGwQRXFt6OPqIFWsm2XEMrNIEtWR64sY1LEKD2vAOA==", - "dev": true - }, - "deep-is": { - "version": "0.1.4", - "resolved": "https://registry.npmjs.org/deep-is/-/deep-is-0.1.4.tgz", - "integrity": "sha512-oIPzksmTg4/MriiaYGO+okXDT7ztn/w3Eptv/+gSIdMdKsJo0u4CfYNFJPy+4SKMuCqGw2wxnA+URMg3t8a/bQ==", - "dev": true - }, - "default-require-extensions": { - "version": "3.0.0", - "resolved": "https://registry.npmjs.org/default-require-extensions/-/default-require-extensions-3.0.0.tgz", - "integrity": "sha512-ek6DpXq/SCpvjhpFsLFRVtIxJCRw6fUR42lYMVZuUMK7n8eMz4Uh5clckdBjEpLhn/gEBZo7hDJnJcwdKLKQjg==", - "dev": true, - "requires": { - "strip-bom": "^4.0.0" - } - }, - "defaults": { - "version": "1.0.3", - "resolved": "https://registry.npmjs.org/defaults/-/defaults-1.0.3.tgz", - "integrity": "sha1-xlYFHpgX2f8I7YgUd/P+QBnz730=", - "dev": true, - "requires": { - "clone": "^1.0.2" - } - }, - "defer-to-connect": { - "version": "2.0.1", - "resolved": "https://registry.npmjs.org/defer-to-connect/-/defer-to-connect-2.0.1.tgz", - "integrity": "sha512-4tvttepXG1VaYGrRibk5EwJd1t4udunSOVMdLSAL6mId1ix438oPwPZMALY41FCijukO1L0twNcGsdzS7dHgDg==", - "dev": true - }, - "delayed-stream": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/delayed-stream/-/delayed-stream-1.0.0.tgz", - "integrity": "sha1-3zrhmayt+31ECqrgsp4icrJOxhk=", - "dev": true - }, - "deprecated-obj": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/deprecated-obj/-/deprecated-obj-2.0.0.tgz", - "integrity": "sha512-CkdywZC2rJ8RGh+y3MM1fw1EJ4oO/oNExGbRFv0AQoMS+faTd3nO7slYjkj/6t8OnIMUE+wxh6G97YHhK1ytrw==", - "dev": true, - "requires": { - "flat": "^5.0.2", - "lodash": "^4.17.20" - } - }, - "deprecation": { - "version": "2.3.1", - "resolved": "https://registry.npmjs.org/deprecation/-/deprecation-2.3.1.tgz", - "integrity": "sha512-xmHIy4F3scKVwMsQ4WnVaS8bHOx0DmVwRywosKhaILI0ywMDWPtBSku2HNxRvF7jtwDRsoEwYQSfbxj8b7RlJQ==", - "dev": true - }, - "diff": { - "version": "5.0.0", - "resolved": "https://registry.npmjs.org/diff/-/diff-5.0.0.tgz", - "integrity": "sha512-/VTCrvm5Z0JGty/BWHljh+BAiw3IK+2j87NGMu8Nwc/f48WoDAC395uomO9ZD117ZOBaHmkX1oyLvkVM/aIT3w==", - "dev": true - }, - "dir-glob": { - "version": "3.0.1", - "resolved": "https://registry.npmjs.org/dir-glob/-/dir-glob-3.0.1.tgz", - "integrity": "sha512-WkrWp9GR4KXfKGYzOLmTuGVi1UWFfws377n9cc55/tb6DuqyF6pcQ5AbiHEshaDpY9v6oaSr2XCDidGmMwdzIA==", - "dev": true, - "requires": { - "path-type": "^4.0.0" - } - }, - "doctrine": { - "version": "3.0.0", - "resolved": "https://registry.npmjs.org/doctrine/-/doctrine-3.0.0.tgz", - "integrity": "sha512-yS+Q5i3hBf7GBkd4KG8a7eBNNWNGLTaEwwYWUijIYM7zrlYDM0BFXHjjPWlWZ1Rg7UaddZeIDmi9jF3HmqiQ2w==", - "dev": true, - "requires": { - "esutils": "^2.0.2" - } - }, - "dot-prop": { - "version": "5.3.0", - "resolved": "https://registry.npmjs.org/dot-prop/-/dot-prop-5.3.0.tgz", - "integrity": "sha512-QM8q3zDe58hqUqjraQOmzZ1LIH9SWQJTlEKCH4kJ2oQvLZk7RbQXvtDM2XEq3fwkV9CCvvH4LA0AV+ogFsBM2Q==", - "dev": true, - "requires": { - "is-obj": "^2.0.0" - } - }, - "duplexer3": { - "version": "0.1.4", - "resolved": "https://registry.npmjs.org/duplexer3/-/duplexer3-0.1.4.tgz", - "integrity": "sha1-7gHdHKwO08vH/b6jfcCo8c4ALOI=", - "dev": true - }, - "electron-to-chromium": { - "version": "1.3.891", - "resolved": "https://registry.npmjs.org/electron-to-chromium/-/electron-to-chromium-1.3.891.tgz", - "integrity": "sha512-3cpwR82QkIS01CN/dup/4Yr3BiOiRLlZlcAFn/5FbNCunMO9ojqDgEP9JEo1QNLflu3pEnPWve50gHOEKc7r6w==", - "dev": true - }, - "emoji-regex": { - "version": "8.0.0", - "resolved": "https://registry.npmjs.org/emoji-regex/-/emoji-regex-8.0.0.tgz", - "integrity": "sha512-MSjYzcWNOA0ewAHpz0MxpYFvwg6yjy1NG3xteoqz644VCo/RPgnr1/GGt+ic3iJTzQ8Eu3TdM14SawnVUmGE6A==", - "dev": true - }, - "end-of-stream": { - "version": "1.4.4", - "resolved": "https://registry.npmjs.org/end-of-stream/-/end-of-stream-1.4.4.tgz", - "integrity": "sha512-+uw1inIHVPQoaVuHzRyXd21icM+cnt4CzD5rW+NC1wjOUSTOs+Te7FOv7AhN7vS9x/oIyhLP5PR1H+phQAHu5Q==", - "dev": true, - "requires": { - "once": "^1.4.0" - } - }, - "enquirer": { - "version": "2.3.6", - "resolved": "https://registry.npmjs.org/enquirer/-/enquirer-2.3.6.tgz", - "integrity": "sha512-yjNnPr315/FjS4zIsUxYguYUPP2e1NK4d7E7ZOLiyYCcbFBiTMyID+2wvm2w6+pZ/odMA7cRkjhsPbltwBOrLg==", - "dev": true, - "requires": { - "ansi-colors": "^4.1.1" - } - }, - "error-ex": { - "version": "1.3.2", - "resolved": "https://registry.npmjs.org/error-ex/-/error-ex-1.3.2.tgz", - "integrity": "sha512-7dFHNmqeFSEt2ZBsCriorKnn3Z2pj+fd9kmI6QoWw4//DL+icEBfc0U7qJCisqrTsKTjw4fNFy2pW9OqStD84g==", - "dev": true, - "requires": { - "is-arrayish": "^0.2.1" - } - }, - "es6-error": { - "version": "4.1.1", - "resolved": "https://registry.npmjs.org/es6-error/-/es6-error-4.1.1.tgz", - "integrity": "sha512-Um/+FxMr9CISWh0bi5Zv0iOD+4cFh5qLeks1qhAopKVAJw3drgKbKySikp7wGhDL0HPeaja0P5ULZrxLkniUVg==", - "dev": true - }, - "escalade": { - "version": "3.1.1", - "resolved": "https://registry.npmjs.org/escalade/-/escalade-3.1.1.tgz", - "integrity": "sha512-k0er2gUkLf8O0zKJiAhmkTnJlTvINGv7ygDNPbeIsX/TJjGJZHuh9B2UxbsaEkmlEo9MfhrSzmhIlhRlI2GXnw==", - "dev": true - }, - "escape-goat": { - "version": "2.1.1", - "resolved": "https://registry.npmjs.org/escape-goat/-/escape-goat-2.1.1.tgz", - "integrity": "sha512-8/uIhbG12Csjy2JEW7D9pHbreaVaS/OpN3ycnyvElTdwM5n6GY6W6e2IPemfvGZeUMqZ9A/3GqIZMgKnBhAw/Q==", - "dev": true - }, - "escape-string-regexp": { - "version": "4.0.0", - "resolved": "https://registry.npmjs.org/escape-string-regexp/-/escape-string-regexp-4.0.0.tgz", - "integrity": "sha512-TtpcNJ3XAzx3Gq8sWRzJaVajRs0uVxA2YAkdb1jm2YkPz4G6egUFAyA3n5vtEIZefPk5Wa4UXbKuS5fKkJWdgA==", - "dev": true - }, - "eslint": { - "version": "8.2.0", - "resolved": "https://registry.npmjs.org/eslint/-/eslint-8.2.0.tgz", - "integrity": "sha512-erw7XmM+CLxTOickrimJ1SiF55jiNlVSp2qqm0NuBWPtHYQCegD5ZMaW0c3i5ytPqL+SSLaCxdvQXFPLJn+ABw==", - "dev": true, - "requires": { - "@eslint/eslintrc": "^1.0.4", - "@humanwhocodes/config-array": "^0.6.0", - "ajv": "^6.10.0", - "chalk": "^4.0.0", - "cross-spawn": "^7.0.2", - "debug": "^4.3.2", - "doctrine": "^3.0.0", - "enquirer": "^2.3.5", - "escape-string-regexp": "^4.0.0", - "eslint-scope": "^6.0.0", - "eslint-utils": "^3.0.0", - "eslint-visitor-keys": "^3.0.0", - "espree": "^9.0.0", - "esquery": "^1.4.0", - "esutils": "^2.0.2", - "fast-deep-equal": "^3.1.3", - "file-entry-cache": "^6.0.1", - "functional-red-black-tree": "^1.0.1", - "glob-parent": "^6.0.1", - "globals": "^13.6.0", - "ignore": "^4.0.6", - "import-fresh": "^3.0.0", - "imurmurhash": "^0.1.4", - "is-glob": "^4.0.0", - "js-yaml": "^4.1.0", - "json-stable-stringify-without-jsonify": "^1.0.1", - "levn": "^0.4.1", - "lodash.merge": "^4.6.2", - "minimatch": "^3.0.4", - "natural-compare": "^1.4.0", - "optionator": "^0.9.1", - "progress": "^2.0.0", - "regexpp": "^3.2.0", - "semver": "^7.2.1", - "strip-ansi": "^6.0.1", - "strip-json-comments": "^3.1.0", - "text-table": "^0.2.0", - "v8-compile-cache": "^2.0.3" - }, - "dependencies": { - "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==", - "dev": true, - "requires": { - "esrecurse": "^4.3.0", - "estraverse": "^5.2.0" - } - }, - "estraverse": { - "version": "5.3.0", - "resolved": "https://registry.npmjs.org/estraverse/-/estraverse-5.3.0.tgz", - "integrity": "sha512-MMdARuVEQziNTeJD8DgMqmhwR11BRQ/cBP+pLtYdSTnf3MIO8fFeiINEbX36ZdNlfU/7A9f3gUw49B3oQsvwBA==", - "dev": true - }, - "ignore": { - "version": "4.0.6", - "resolved": "https://registry.npmjs.org/ignore/-/ignore-4.0.6.tgz", - "integrity": "sha512-cyFDKrqc/YdcWFniJhzI42+AzS+gNwmUzOSFcRCQYwySuBBBy/KjuxWLZ/FHEH6Moq1NizMOBWyTcv8O4OZIMg==", - "dev": true - } - } - }, - "eslint-scope": { - "version": "5.1.1", - "resolved": "https://registry.npmjs.org/eslint-scope/-/eslint-scope-5.1.1.tgz", - "integrity": "sha512-2NxwbF/hZ0KpepYN0cNbo+FN6XoK7GaHlQhgx/hIZl6Va0bF45RQOOwhLIy8lQDbuCiadSLCBnH2CFYquit5bw==", - "dev": true, - "requires": { - "esrecurse": "^4.3.0", - "estraverse": "^4.1.1" - } - }, - "eslint-utils": { - "version": "3.0.0", - "resolved": "https://registry.npmjs.org/eslint-utils/-/eslint-utils-3.0.0.tgz", - "integrity": "sha512-uuQC43IGctw68pJA1RgbQS8/NP7rch6Cwd4j3ZBtgo4/8Flj4eGE7ZYSZRN3iq5pVUv6GPdW5Z1RFleo84uLDA==", - "dev": true, - "requires": { - "eslint-visitor-keys": "^2.0.0" - }, - "dependencies": { - "eslint-visitor-keys": { - "version": "2.1.0", - "resolved": "https://registry.npmjs.org/eslint-visitor-keys/-/eslint-visitor-keys-2.1.0.tgz", - "integrity": "sha512-0rSmRBzXgDzIsD6mGdJgevzgezI534Cer5L/vyMX0kHzT/jiB43jRhd9YUlMGYLQy2zprNmoT8qasCGtY+QaKw==", - "dev": true - } - } - }, - "eslint-visitor-keys": { - "version": "3.1.0", - "resolved": "https://registry.npmjs.org/eslint-visitor-keys/-/eslint-visitor-keys-3.1.0.tgz", - "integrity": "sha512-yWJFpu4DtjsWKkt5GeNBBuZMlNcYVs6vRCLoCVEJrTjaSB6LC98gFipNK/erM2Heg/E8mIK+hXG/pJMLK+eRZA==", - "dev": true - }, - "espree": { - "version": "9.0.0", - "resolved": "https://registry.npmjs.org/espree/-/espree-9.0.0.tgz", - "integrity": "sha512-r5EQJcYZ2oaGbeR0jR0fFVijGOcwai07/690YRXLINuhmVeRY4UKSAsQPe/0BNuDgwP7Ophoc1PRsr2E3tkbdQ==", - "dev": true, - "requires": { - "acorn": "^8.5.0", - "acorn-jsx": "^5.3.1", - "eslint-visitor-keys": "^3.0.0" - } - }, - "esprima": { - "version": "4.0.1", - "resolved": "https://registry.npmjs.org/esprima/-/esprima-4.0.1.tgz", - "integrity": "sha512-eGuFFw7Upda+g4p+QHvnW0RyTX/SVeJBDM/gCtMARO0cLuT2HcEKnTPvhjV6aGeqrCB/sbNop0Kszm0jsaWU4A==", - "dev": true - }, - "esquery": { - "version": "1.4.0", - "resolved": "https://registry.npmjs.org/esquery/-/esquery-1.4.0.tgz", - "integrity": "sha512-cCDispWt5vHHtwMY2YrAQ4ibFkAL8RbH5YGBnZBc90MolvvfkkQcJro/aZiAQUlQ3qgrYS6D6v8Gc5G5CQsc9w==", - "dev": true, - "requires": { - "estraverse": "^5.1.0" - }, - "dependencies": { - "estraverse": { - "version": "5.3.0", - "resolved": "https://registry.npmjs.org/estraverse/-/estraverse-5.3.0.tgz", - "integrity": "sha512-MMdARuVEQziNTeJD8DgMqmhwR11BRQ/cBP+pLtYdSTnf3MIO8fFeiINEbX36ZdNlfU/7A9f3gUw49B3oQsvwBA==", - "dev": true - } - } - }, - "esrecurse": { - "version": "4.3.0", - "resolved": "https://registry.npmjs.org/esrecurse/-/esrecurse-4.3.0.tgz", - "integrity": "sha512-KmfKL3b6G+RXvP8N1vr3Tq1kL/oCFgn2NYXEtqP8/L3pKapUA4G8cFVaoF3SU323CD4XypR/ffioHmkti6/Tag==", - "dev": true, - "requires": { - "estraverse": "^5.2.0" - }, - "dependencies": { - "estraverse": { - "version": "5.3.0", - "resolved": "https://registry.npmjs.org/estraverse/-/estraverse-5.3.0.tgz", - "integrity": "sha512-MMdARuVEQziNTeJD8DgMqmhwR11BRQ/cBP+pLtYdSTnf3MIO8fFeiINEbX36ZdNlfU/7A9f3gUw49B3oQsvwBA==", - "dev": true - } - } - }, - "estraverse": { - "version": "4.3.0", - "resolved": "https://registry.npmjs.org/estraverse/-/estraverse-4.3.0.tgz", - "integrity": "sha512-39nnKffWz8xN1BU/2c79n9nB9HDzo0niYUqx6xyqUnyoAnQyyWpOTdZEeiCch8BBu515t4wp9ZmgVfVhn9EBpw==", - "dev": true - }, - "esutils": { - "version": "2.0.3", - "resolved": "https://registry.npmjs.org/esutils/-/esutils-2.0.3.tgz", - "integrity": "sha512-kVscqXk4OCp68SZ0dkgEKVi6/8ij300KBWTJq32P/dYeWTSwK41WyTxalN1eRmA5Z9UU/LX9D7FWSmV9SAYx6g==", - "dev": true - }, - "execa": { - "version": "5.1.1", - "resolved": "https://registry.npmjs.org/execa/-/execa-5.1.1.tgz", - "integrity": "sha512-8uSpZZocAZRBAPIEINJj3Lo9HyGitllczc27Eh5YYojjMFMn8yHMDMaUHE2Jqfq05D/wucwI4JGURyXt1vchyg==", - "dev": true, - "requires": { - "cross-spawn": "^7.0.3", - "get-stream": "^6.0.0", - "human-signals": "^2.1.0", - "is-stream": "^2.0.0", - "merge-stream": "^2.0.0", - "npm-run-path": "^4.0.1", - "onetime": "^5.1.2", - "signal-exit": "^3.0.3", - "strip-final-newline": "^2.0.0" - } - }, - "external-editor": { - "version": "3.1.0", - "resolved": "https://registry.npmjs.org/external-editor/-/external-editor-3.1.0.tgz", - "integrity": "sha512-hMQ4CX1p1izmuLYyZqLMO/qGNw10wSv9QDCPfzXfyFrOaCSSoRfqE1Kf1s5an66J5JZC62NewG+mK49jOCtQew==", - "dev": true, - "requires": { - "chardet": "^0.7.0", - "iconv-lite": "^0.4.24", - "tmp": "^0.0.33" - } - }, - "fast-deep-equal": { - "version": "3.1.3", - "resolved": "https://registry.npmjs.org/fast-deep-equal/-/fast-deep-equal-3.1.3.tgz", - "integrity": "sha512-f3qQ9oQy9j2AhBe/H9VC91wLmKBCCU/gDOnKNAYG5hswO7BLKj09Hc5HYNz9cGI++xlpDCIgDaitVs03ATR84Q==", - "dev": true - }, - "fast-glob": { - "version": "3.2.7", - "resolved": "https://registry.npmjs.org/fast-glob/-/fast-glob-3.2.7.tgz", - "integrity": "sha512-rYGMRwip6lUMvYD3BTScMwT1HtAs2d71SMv66Vrxs0IekGZEjhM0pcMfjQPnknBt2zeCwQMEupiN02ZP4DiT1Q==", - "dev": true, - "requires": { - "@nodelib/fs.stat": "^2.0.2", - "@nodelib/fs.walk": "^1.2.3", - "glob-parent": "^5.1.2", - "merge2": "^1.3.0", - "micromatch": "^4.0.4" - }, - "dependencies": { - "glob-parent": { - "version": "5.1.2", - "resolved": "https://registry.npmjs.org/glob-parent/-/glob-parent-5.1.2.tgz", - "integrity": "sha512-AOIgSQCepiJYwP3ARnGx+5VnTu2HBYdzbGP45eLw1vr3zB3vZLeyed1sC9hnbcOc9/SrMyM5RPQrkGz4aS9Zow==", - "dev": true, - "requires": { - "is-glob": "^4.0.1" - } - } - } - }, - "fast-json-stable-stringify": { - "version": "2.1.0", - "resolved": "https://registry.npmjs.org/fast-json-stable-stringify/-/fast-json-stable-stringify-2.1.0.tgz", - "integrity": "sha512-lhd/wF+Lk98HZoTCtlVraHtfh5XYijIjalXck7saUtuanSDyLMxnHhSXEDJqHxD7msR8D0uCmqlkwjCV8xvwHw==", - "dev": true - }, - "fast-levenshtein": { - "version": "2.0.6", - "resolved": "https://registry.npmjs.org/fast-levenshtein/-/fast-levenshtein-2.0.6.tgz", - "integrity": "sha1-PYpcZog6FqMMqGQ+hR8Zuqd5eRc=", - "dev": true - }, - "fastq": { - "version": "1.13.0", - "resolved": "https://registry.npmjs.org/fastq/-/fastq-1.13.0.tgz", - "integrity": "sha512-YpkpUnK8od0o1hmeSc7UUs/eB/vIPWJYjKck2QKIzAf71Vm1AAQ3EbuZB3g2JIy+pg+ERD0vqI79KyZiB2e2Nw==", - "dev": true, - "requires": { - "reusify": "^1.0.4" - } - }, - "figures": { - "version": "3.2.0", - "resolved": "https://registry.npmjs.org/figures/-/figures-3.2.0.tgz", - "integrity": "sha512-yaduQFRKLXYOGgEn6AZau90j3ggSOyiqXU0F9JZfeXYhNa+Jk4X+s45A2zg5jns87GAFa34BBm2kXw4XpNcbdg==", - "dev": true, - "requires": { - "escape-string-regexp": "^1.0.5" - }, - "dependencies": { - "escape-string-regexp": { - "version": "1.0.5", - "resolved": "https://registry.npmjs.org/escape-string-regexp/-/escape-string-regexp-1.0.5.tgz", - "integrity": "sha1-G2HAViGQqN/2rjuyzwIAyhMLhtQ=", - "dev": true - } - } - }, - "file-entry-cache": { - "version": "6.0.1", - "resolved": "https://registry.npmjs.org/file-entry-cache/-/file-entry-cache-6.0.1.tgz", - "integrity": "sha512-7Gps/XWymbLk2QLYK4NzpMOrYjMhdIxXuIvy2QBsLE6ljuodKvdkWs/cpyJJ3CVIVpH0Oi1Hvg1ovbMzLdFBBg==", - "dev": true, - "requires": { - "flat-cache": "^3.0.4" - } - }, - "fill-range": { - "version": "7.0.1", - "resolved": "https://registry.npmjs.org/fill-range/-/fill-range-7.0.1.tgz", - "integrity": "sha512-qOo9F+dMUmC2Lcb4BbVvnKJxTPjCm+RRpe4gDuGrzkL7mEVl/djYSu2OdQ2Pa302N4oqkSg9ir6jaLWJ2USVpQ==", - "dev": true, - "requires": { - "to-regex-range": "^5.0.1" - } - }, - "filter-obj": { - "version": "1.1.0", - "resolved": "https://registry.npmjs.org/filter-obj/-/filter-obj-1.1.0.tgz", - "integrity": "sha1-mzERErxsYSehbgFsbF1/GeCAXFs=", - "dev": true - }, - "find-cache-dir": { - "version": "3.3.2", - "resolved": "https://registry.npmjs.org/find-cache-dir/-/find-cache-dir-3.3.2.tgz", - "integrity": "sha512-wXZV5emFEjrridIgED11OoUKLxiYjAcqot/NJdAkOhlJ+vGzwhOAfcG5OX1jP+S0PcjEn8bdMJv+g2jwQ3Onig==", - "dev": true, - "requires": { - "commondir": "^1.0.1", - "make-dir": "^3.0.2", - "pkg-dir": "^4.1.0" - } - }, - "find-up": { - "version": "5.0.0", - "resolved": "https://registry.npmjs.org/find-up/-/find-up-5.0.0.tgz", - "integrity": "sha512-78/PXT1wlLLDgTzDs7sjq9hzz0vXD+zn+7wypEe4fXQxCmdmqfGsEPQxmiCSQI3ajFV91bVSsvNtrJRiW6nGng==", - "dev": true, - "requires": { - "locate-path": "^6.0.0", - "path-exists": "^4.0.0" - } - }, - "flat": { - "version": "5.0.2", - "resolved": "https://registry.npmjs.org/flat/-/flat-5.0.2.tgz", - "integrity": "sha512-b6suED+5/3rTpUBdG1gupIl8MPFCAMA0QXwmljLhvCUKcUvdE4gWky9zpuGCcXHOsz4J9wPGNWq6OKpmIzz3hQ==", - "dev": true - }, - "flat-cache": { - "version": "3.0.4", - "resolved": "https://registry.npmjs.org/flat-cache/-/flat-cache-3.0.4.tgz", - "integrity": "sha512-dm9s5Pw7Jc0GvMYbshN6zchCA9RgQlzzEZX3vylR9IqFfS8XciblUXOKfW6SiuJ0e13eDYZoZV5wdrev7P3Nwg==", - "dev": true, - "requires": { - "flatted": "^3.1.0", - "rimraf": "^3.0.2" - } - }, - "flatted": { - "version": "3.2.2", - "resolved": "https://registry.npmjs.org/flatted/-/flatted-3.2.2.tgz", - "integrity": "sha512-JaTY/wtrcSyvXJl4IMFHPKyFur1sE9AUqc0QnhOaJ0CxHtAoIV8pYDzeEfAaNEtGkOfq4gr3LBFmdXW5mOQFnA==", - "dev": true - }, - "foreground-child": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/foreground-child/-/foreground-child-2.0.0.tgz", - "integrity": "sha512-dCIq9FpEcyQyXKCkyzmlPTFNgrCzPudOe+mhvJU5zAtlBnGVy2yKxtfsxK2tQBThwq225jcvBjpw1Gr40uzZCA==", - "dev": true, - "requires": { - "cross-spawn": "^7.0.0", - "signal-exit": "^3.0.2" - } - }, - "form-data": { - "version": "4.0.0", - "resolved": "https://registry.npmjs.org/form-data/-/form-data-4.0.0.tgz", - "integrity": "sha512-ETEklSGi5t0QMZuiXoA/Q6vcnxcLQP5vdugSpuAyi6SVGi2clPPp+xgEhuMaHC+zGgn31Kd235W35f7Hykkaww==", - "dev": true, - "requires": { - "asynckit": "^0.4.0", - "combined-stream": "^1.0.8", - "mime-types": "^2.1.12" - } - }, - "fromentries": { - "version": "1.3.2", - "resolved": "https://registry.npmjs.org/fromentries/-/fromentries-1.3.2.tgz", - "integrity": "sha512-cHEpEQHUg0f8XdtZCc2ZAhrHzKzT0MrFUTcvx+hfxYu7rGMDc5SKoXFh+n4YigxsHXRzc6OrCshdR1bWH6HHyg==", - "dev": true - }, - "fs.realpath": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/fs.realpath/-/fs.realpath-1.0.0.tgz", - "integrity": "sha1-FQStJSMVjKpA20onh8sBQRmU6k8=", - "dev": true - }, - "fsevents": { - "version": "2.3.2", - "resolved": "https://registry.npmjs.org/fsevents/-/fsevents-2.3.2.tgz", - "integrity": "sha512-xiqMQR4xAeHTuB9uWm+fFRcIOgKBMiOBP+eXiyT7jsgVCq1bkVygt00oASowB7EdtpOHaaPgKt812P9ab+DDKA==", - "dev": true, - "optional": true - }, - "function-bind": { - "version": "1.1.1", - "resolved": "https://registry.npmjs.org/function-bind/-/function-bind-1.1.1.tgz", - "integrity": "sha512-yIovAzMX49sF8Yl58fSCWJ5svSLuaibPxXQJFLmBObTuCr0Mf1KiPopGM9NiFjiYBCbfaa2Fh6breQ6ANVTI0A==", - "dev": true - }, - "functional-red-black-tree": { - "version": "1.0.1", - "resolved": "https://registry.npmjs.org/functional-red-black-tree/-/functional-red-black-tree-1.0.1.tgz", - "integrity": "sha1-GwqzvVU7Kg1jmdKcDj6gslIHgyc=", - "dev": true - }, - "generic-pool": { - "version": "3.8.2", - "resolved": "https://registry.npmjs.org/generic-pool/-/generic-pool-3.8.2.tgz", - "integrity": "sha512-nGToKy6p3PAbYQ7p1UlWl6vSPwfwU6TMSWK7TTu+WUY4ZjyZQGniGGt2oNVvyNSpyZYSB43zMXVLcBm08MTMkg==" - }, - "gensync": { - "version": "1.0.0-beta.2", - "resolved": "https://registry.npmjs.org/gensync/-/gensync-1.0.0-beta.2.tgz", - "integrity": "sha512-3hN7NaskYvMDLQY55gnW3NQ+mesEAepTqlg+VEbj7zzqEMBVNhzcGYYeqFo/TlYz6eQiFcp1HcsCZO+nGgS8zg==", - "dev": true - }, - "get-caller-file": { - "version": "2.0.5", - "resolved": "https://registry.npmjs.org/get-caller-file/-/get-caller-file-2.0.5.tgz", - "integrity": "sha512-DyFP3BM/3YHTQOCUL/w0OZHR0lpKeGrxotcHWcqNEdnltqFwXVfhEBQ94eIo34AfQpo0rGki4cyIiftY06h2Fg==", - "dev": true - }, - "get-intrinsic": { - "version": "1.1.1", - "resolved": "https://registry.npmjs.org/get-intrinsic/-/get-intrinsic-1.1.1.tgz", - "integrity": "sha512-kWZrnVM42QCiEA2Ig1bG8zjoIMOgxWwYCEeNdwY6Tv/cOSeGpcoX4pXHfKUxNKVoArnrEr2e9srnAxxGIraS9Q==", - "dev": true, - "requires": { - "function-bind": "^1.1.1", - "has": "^1.0.3", - "has-symbols": "^1.0.1" - } - }, - "get-package-type": { - "version": "0.1.0", - "resolved": "https://registry.npmjs.org/get-package-type/-/get-package-type-0.1.0.tgz", - "integrity": "sha512-pjzuKtY64GYfWizNAJ0fr9VqttZkNiK2iS430LtIHzjBEr6bX8Am2zm4sW4Ro5wjWW5cAlRL1qAMTcXbjNAO2Q==", - "dev": true - }, - "get-stream": { - "version": "6.0.1", - "resolved": "https://registry.npmjs.org/get-stream/-/get-stream-6.0.1.tgz", - "integrity": "sha512-ts6Wi+2j3jQjqi70w5AlN8DFnkSwC+MqmxEzdEALB2qXZYV3X/b1CTfgPLGJNMeAWxdPfU8FO1ms3NUfaHCPYg==", - "dev": true - }, - "git-up": { - "version": "4.0.5", - "resolved": "https://registry.npmjs.org/git-up/-/git-up-4.0.5.tgz", - "integrity": "sha512-YUvVDg/vX3d0syBsk/CKUTib0srcQME0JyHkL5BaYdwLsiCslPWmDSi8PUMo9pXYjrryMcmsCoCgsTpSCJEQaA==", - "dev": true, - "requires": { - "is-ssh": "^1.3.0", - "parse-url": "^6.0.0" - } - }, - "git-url-parse": { - "version": "11.6.0", - "resolved": "https://registry.npmjs.org/git-url-parse/-/git-url-parse-11.6.0.tgz", - "integrity": "sha512-WWUxvJs5HsyHL6L08wOusa/IXYtMuCAhrMmnTjQPpBU0TTHyDhnOATNH3xNQz7YOQUsqIIPTGr4xiVti1Hsk5g==", - "dev": true, - "requires": { - "git-up": "^4.0.0" - } - }, - "glob": { - "version": "7.1.7", - "resolved": "https://registry.npmjs.org/glob/-/glob-7.1.7.tgz", - "integrity": "sha512-OvD9ENzPLbegENnYP5UUfJIirTg4+XwMWGaQfQTY0JenxNvvIKP3U3/tAQSPIu/lHxXYSZmpXlUHeqAIdKzBLQ==", - "dev": true, - "requires": { - "fs.realpath": "^1.0.0", - "inflight": "^1.0.4", - "inherits": "2", - "minimatch": "^3.0.4", - "once": "^1.3.0", - "path-is-absolute": "^1.0.0" - } - }, - "glob-parent": { - "version": "6.0.2", - "resolved": "https://registry.npmjs.org/glob-parent/-/glob-parent-6.0.2.tgz", - "integrity": "sha512-XxwI8EOhVQgWp6iDL+3b0r86f4d6AX6zSU55HfB4ydCEuXLXc5FcYeOu+nnGftS4TEju/11rt4KJPTMgbfmv4A==", - "dev": true, - "requires": { - "is-glob": "^4.0.3" - } - }, - "global-dirs": { - "version": "3.0.0", - "resolved": "https://registry.npmjs.org/global-dirs/-/global-dirs-3.0.0.tgz", - "integrity": "sha512-v8ho2DS5RiCjftj1nD9NmnfaOzTdud7RRnVd9kFNOjqZbISlx5DQ+OrTkywgd0dIt7oFCvKetZSHoHcP3sDdiA==", - "dev": true, - "requires": { - "ini": "2.0.0" - } - }, - "globals": { - "version": "13.12.0", - "resolved": "https://registry.npmjs.org/globals/-/globals-13.12.0.tgz", - "integrity": "sha512-uS8X6lSKN2JumVoXrbUz+uG4BYG+eiawqm3qFcT7ammfbUHeCBoJMlHcec/S3krSk73/AE/f0szYFmgAA3kYZg==", - "dev": true, - "requires": { - "type-fest": "^0.20.2" - } - }, - "globby": { - "version": "11.0.4", - "resolved": "https://registry.npmjs.org/globby/-/globby-11.0.4.tgz", - "integrity": "sha512-9O4MVG9ioZJ08ffbcyVYyLOJLk5JQ688pJ4eMGLpdWLHq/Wr1D9BlriLQyL0E+jbkuePVZXYFj47QM/v093wHg==", - "dev": true, - "requires": { - "array-union": "^2.1.0", - "dir-glob": "^3.0.1", - "fast-glob": "^3.1.1", - "ignore": "^5.1.4", - "merge2": "^1.3.0", - "slash": "^3.0.0" - } - }, - "got": { - "version": "11.8.2", - "resolved": "https://registry.npmjs.org/got/-/got-11.8.2.tgz", - "integrity": "sha512-D0QywKgIe30ODs+fm8wMZiAcZjypcCodPNuMz5H9Mny7RJ+IjJ10BdmGW7OM7fHXP+O7r6ZwapQ/YQmMSvB0UQ==", - "dev": true, - "requires": { - "@sindresorhus/is": "^4.0.0", - "@szmarczak/http-timer": "^4.0.5", - "@types/cacheable-request": "^6.0.1", - "@types/responselike": "^1.0.0", - "cacheable-lookup": "^5.0.3", - "cacheable-request": "^7.0.1", - "decompress-response": "^6.0.0", - "http2-wrapper": "^1.0.0-beta.5.2", - "lowercase-keys": "^2.0.0", - "p-cancelable": "^2.0.0", - "responselike": "^2.0.0" - } - }, - "graceful-fs": { - "version": "4.2.8", - "resolved": "https://registry.npmjs.org/graceful-fs/-/graceful-fs-4.2.8.tgz", - "integrity": "sha512-qkIilPUYcNhJpd33n0GBXTB1MMPp14TxEsEs0pTrsSVucApsYzW5V+Q8Qxhik6KU3evy+qkAAowTByymK0avdg==", - "dev": true - }, - "growl": { - "version": "1.10.5", - "resolved": "https://registry.npmjs.org/growl/-/growl-1.10.5.tgz", - "integrity": "sha512-qBr4OuELkhPenW6goKVXiv47US3clb3/IbuWF9KNKEijAy9oeHxU9IgzjvJhHkUzhaj7rOUD7+YGWqUjLp5oSA==", - "dev": true - }, - "handlebars": { - "version": "4.7.7", - "resolved": "https://registry.npmjs.org/handlebars/-/handlebars-4.7.7.tgz", - "integrity": "sha512-aAcXm5OAfE/8IXkcZvCepKU3VzW1/39Fb5ZuqMtgI/hT8X2YgoMvBY5dLhq/cpOvw7Lk1nK/UF71aLG/ZnVYRA==", - "dev": true, - "requires": { - "minimist": "^1.2.5", - "neo-async": "^2.6.0", - "source-map": "^0.6.1", - "uglify-js": "^3.1.4", - "wordwrap": "^1.0.0" - }, - "dependencies": { - "source-map": { - "version": "0.6.1", - "resolved": "https://registry.npmjs.org/source-map/-/source-map-0.6.1.tgz", - "integrity": "sha512-UjgapumWlbMhkBgzT7Ykc5YXUT46F0iKu8SGXq0bcwP5dz/h0Plj6enJqjz1Zbq2l5WaqYnrVbwWOWMyF3F47g==", - "dev": true - } - } - }, - "has": { - "version": "1.0.3", - "resolved": "https://registry.npmjs.org/has/-/has-1.0.3.tgz", - "integrity": "sha512-f2dvO0VU6Oej7RkWJGrehjbzMAjFp5/VKPp5tTpWIV4JHHZK1/BxbFRtf/siA2SWTe09caDmVtYYzWEIbBS4zw==", - "dev": true, - "requires": { - "function-bind": "^1.1.1" - } - }, - "has-flag": { - "version": "4.0.0", - "resolved": "https://registry.npmjs.org/has-flag/-/has-flag-4.0.0.tgz", - "integrity": "sha512-EykJT/Q1KjTWctppgIAgfSO0tKVuZUjhgMr17kqTumMl6Afv3EISleU7qZUzoXDFTAHTDC4NOoG/ZxU3EvlMPQ==", - "dev": true - }, - "has-symbols": { - "version": "1.0.2", - "resolved": "https://registry.npmjs.org/has-symbols/-/has-symbols-1.0.2.tgz", - "integrity": "sha512-chXa79rL/UC2KlX17jo3vRGz0azaWEx5tGqZg5pO3NUyEJVB17dMruQlzCCOfUvElghKcm5194+BCRvi2Rv/Gw==", - "dev": true - }, - "has-yarn": { - "version": "2.1.0", - "resolved": "https://registry.npmjs.org/has-yarn/-/has-yarn-2.1.0.tgz", - "integrity": "sha512-UqBRqi4ju7T+TqGNdqAO0PaSVGsDGJUBQvk9eUWNGRY1CFGDzYhLWoM7JQEemnlvVcv/YEmc2wNW8BC24EnUsw==", - "dev": true - }, - "hasha": { - "version": "5.2.2", - "resolved": "https://registry.npmjs.org/hasha/-/hasha-5.2.2.tgz", - "integrity": "sha512-Hrp5vIK/xr5SkeN2onO32H0MgNZ0f17HRNH39WfL0SYUNOTZ5Lz1TJ8Pajo/87dYGEFlLMm7mIc/k/s6Bvz9HQ==", - "dev": true, - "requires": { - "is-stream": "^2.0.0", - "type-fest": "^0.8.0" - }, - "dependencies": { - "type-fest": { - "version": "0.8.1", - "resolved": "https://registry.npmjs.org/type-fest/-/type-fest-0.8.1.tgz", - "integrity": "sha512-4dbzIzqvjtgiM5rw1k5rEHtBANKmdudhGyBEajN01fEyhaAIhsoKNy6y7+IN93IfpFtwY9iqi7kD+xwKhQsNJA==", - "dev": true - } - } - }, - "he": { - "version": "1.2.0", - "resolved": "https://registry.npmjs.org/he/-/he-1.2.0.tgz", - "integrity": "sha512-F/1DnUGPopORZi0ni+CvrCgHQ5FyEAHRLSApuYWMmrbSwoN2Mn/7k+Gl38gJnR7yyDZk6WLXwiGod1JOWNDKGw==", - "dev": true - }, - "html-escaper": { - "version": "2.0.2", - "resolved": "https://registry.npmjs.org/html-escaper/-/html-escaper-2.0.2.tgz", - "integrity": "sha512-H2iMtd0I4Mt5eYiapRdIDjp+XzelXQ0tFE4JS7YFwFevXXMmOp9myNrUvCg0D6ws8iqkRPBfKHgbwig1SmlLfg==", - "dev": true - }, - "http-cache-semantics": { - "version": "4.1.0", - "resolved": "https://registry.npmjs.org/http-cache-semantics/-/http-cache-semantics-4.1.0.tgz", - "integrity": "sha512-carPklcUh7ROWRK7Cv27RPtdhYhUsela/ue5/jKzjegVvXDqM2ILE9Q2BGn9JZJh1g87cp56su/FgQSzcWS8cQ==", - "dev": true - }, - "http2-wrapper": { - "version": "1.0.3", - "resolved": "https://registry.npmjs.org/http2-wrapper/-/http2-wrapper-1.0.3.tgz", - "integrity": "sha512-V+23sDMr12Wnz7iTcDeJr3O6AIxlnvT/bmaAAAP/Xda35C90p9599p0F1eHR/N1KILWSoWVAiOMFjBBXaXSMxg==", - "dev": true, - "requires": { - "quick-lru": "^5.1.1", - "resolve-alpn": "^1.0.0" - } - }, - "human-signals": { - "version": "2.1.0", - "resolved": "https://registry.npmjs.org/human-signals/-/human-signals-2.1.0.tgz", - "integrity": "sha512-B4FFZ6q/T2jhhksgkbEW3HBvWIfDW85snkQgawt07S7J5QXTk6BkNV+0yAeZrM5QpMAdYlocGoljn0sJ/WQkFw==", - "dev": true - }, - "iconv-lite": { - "version": "0.4.24", - "resolved": "https://registry.npmjs.org/iconv-lite/-/iconv-lite-0.4.24.tgz", - "integrity": "sha512-v3MXnZAcvnywkTUEZomIActle7RXXeedOR31wwl7VlyoXO4Qi9arvSenNQWne1TcRwhCL1HwLI21bEqdpj8/rA==", - "dev": true, - "requires": { - "safer-buffer": ">= 2.1.2 < 3" - } - }, - "ieee754": { - "version": "1.2.1", - "resolved": "https://registry.npmjs.org/ieee754/-/ieee754-1.2.1.tgz", - "integrity": "sha512-dcyqhDvX1C46lXZcVqCpK+FtMRQVdIMN6/Df5js2zouUsqG7I6sFxitIC+7KYK29KdXOLHdu9zL4sFnoVQnqaA==", - "dev": true - }, - "ignore": { - "version": "5.1.9", - "resolved": "https://registry.npmjs.org/ignore/-/ignore-5.1.9.tgz", - "integrity": "sha512-2zeMQpbKz5dhZ9IwL0gbxSW5w0NK/MSAMtNuhgIHEPmaU3vPdKPL0UdvUCXs5SS4JAwsBxysK5sFMW8ocFiVjQ==", - "dev": true - }, - "import-cwd": { - "version": "3.0.0", - "resolved": "https://registry.npmjs.org/import-cwd/-/import-cwd-3.0.0.tgz", - "integrity": "sha512-4pnzH16plW+hgvRECbDWpQl3cqtvSofHWh44met7ESfZ8UZOWWddm8hEyDTqREJ9RbYHY8gi8DqmaelApoOGMg==", - "dev": true, - "requires": { - "import-from": "^3.0.0" - } - }, - "import-fresh": { - "version": "3.3.0", - "resolved": "https://registry.npmjs.org/import-fresh/-/import-fresh-3.3.0.tgz", - "integrity": "sha512-veYYhQa+D1QBKznvhUHxb8faxlrwUnxseDAbAp457E0wLNio2bOSKnjYDhMj+YiAq61xrMGhQk9iXVk5FzgQMw==", - "dev": true, - "requires": { - "parent-module": "^1.0.0", - "resolve-from": "^4.0.0" - } - }, - "import-from": { - "version": "3.0.0", - "resolved": "https://registry.npmjs.org/import-from/-/import-from-3.0.0.tgz", - "integrity": "sha512-CiuXOFFSzkU5x/CR0+z7T91Iht4CXgfCxVOFRhh2Zyhg5wOpWvvDLQUsWl+gcN+QscYBjez8hDCt85O7RLDttQ==", - "dev": true, - "requires": { - "resolve-from": "^5.0.0" - }, - "dependencies": { - "resolve-from": { - "version": "5.0.0", - "resolved": "https://registry.npmjs.org/resolve-from/-/resolve-from-5.0.0.tgz", - "integrity": "sha512-qYg9KP24dD5qka9J47d0aVky0N+b4fTU89LN9iDnjB5waksiC49rvMB0PrUJQGoTmH50XPiqOvAjDfaijGxYZw==", - "dev": true - } - } - }, - "import-lazy": { - "version": "2.1.0", - "resolved": "https://registry.npmjs.org/import-lazy/-/import-lazy-2.1.0.tgz", - "integrity": "sha1-BWmOPUXIjo1+nZLLBYTnfwlvPkM=", - "dev": true - }, - "imurmurhash": { - "version": "0.1.4", - "resolved": "https://registry.npmjs.org/imurmurhash/-/imurmurhash-0.1.4.tgz", - "integrity": "sha1-khi5srkoojixPcT7a21XbyMUU+o=", - "dev": true - }, - "indent-string": { - "version": "4.0.0", - "resolved": "https://registry.npmjs.org/indent-string/-/indent-string-4.0.0.tgz", - "integrity": "sha512-EdDDZu4A2OyIK7Lr/2zG+w5jmbuk1DVBnEwREQvBzspBJkCEbRa8GxU1lghYcaGJCnRWibjDXlq779X1/y5xwg==", - "dev": true - }, - "inflight": { - "version": "1.0.6", - "resolved": "https://registry.npmjs.org/inflight/-/inflight-1.0.6.tgz", - "integrity": "sha1-Sb1jMdfQLQwJvJEKEHW6gWW1bfk=", - "dev": true, - "requires": { - "once": "^1.3.0", - "wrappy": "1" - } - }, - "inherits": { - "version": "2.0.4", - "resolved": "https://registry.npmjs.org/inherits/-/inherits-2.0.4.tgz", - "integrity": "sha512-k/vGaX4/Yla3WzyMCvTQOXYeIHvqOKtnqBduzTHpzpQZzAskKMhZ2K+EnBiSM9zGSoIFeMpXKxa4dYeZIQqewQ==", - "dev": true - }, - "ini": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/ini/-/ini-2.0.0.tgz", - "integrity": "sha512-7PnF4oN3CvZF23ADhA5wRaYEQpJ8qygSkbtTXWBeXWXmEVRXK+1ITciHWwHhsjv1TmW0MgacIv6hEi5pX5NQdA==", - "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==", - "dev": true, - "requires": { - "ansi-escapes": "^4.2.1", - "chalk": "^4.1.1", - "cli-cursor": "^3.1.0", - "cli-width": "^3.0.0", - "external-editor": "^3.0.3", - "figures": "^3.0.0", - "lodash": "^4.17.21", - "mute-stream": "0.0.8", - "ora": "^5.4.1", - "run-async": "^2.4.0", - "rxjs": "^7.2.0", - "string-width": "^4.1.0", - "strip-ansi": "^6.0.0", - "through": "^2.3.6" - } - }, - "interpret": { - "version": "1.4.0", - "resolved": "https://registry.npmjs.org/interpret/-/interpret-1.4.0.tgz", - "integrity": "sha512-agE4QfB2Lkp9uICn7BAqoscw4SZP9kTE2hxiFI3jBPmXJfdqiahTbUuKGsMoN2GtqL9AxhYioAcVvgsb1HvRbA==", - "dev": true - }, - "is-arrayish": { - "version": "0.2.1", - "resolved": "https://registry.npmjs.org/is-arrayish/-/is-arrayish-0.2.1.tgz", - "integrity": "sha1-d8mYQFJ6qOyxqLppe4BkWnqSap0=", - "dev": true - }, - "is-binary-path": { - "version": "2.1.0", - "resolved": "https://registry.npmjs.org/is-binary-path/-/is-binary-path-2.1.0.tgz", - "integrity": "sha512-ZMERYes6pDydyuGidse7OsHxtbI7WVeUEozgR/g7rd0xUimYNlvZRE/K2MgZTjWy725IfelLeVcEM97mmtRGXw==", - "dev": true, - "requires": { - "binary-extensions": "^2.0.0" - } - }, - "is-ci": { - "version": "3.0.0", - "resolved": "https://registry.npmjs.org/is-ci/-/is-ci-3.0.0.tgz", - "integrity": "sha512-kDXyttuLeslKAHYL/K28F2YkM3x5jvFPEw3yXbRptXydjD9rpLEz+C5K5iutY9ZiUu6AP41JdvRQwF4Iqs4ZCQ==", - "dev": true, - "requires": { - "ci-info": "^3.1.1" - } - }, - "is-core-module": { - "version": "2.8.0", - "resolved": "https://registry.npmjs.org/is-core-module/-/is-core-module-2.8.0.tgz", - "integrity": "sha512-vd15qHsaqrRL7dtH6QNuy0ndJmRDrS9HAM1CAiSifNUFv4x1a0CCVsj18hJ1mShxIG6T2i1sO78MkP56r0nYRw==", - "dev": true, - "requires": { - "has": "^1.0.3" - } - }, - "is-docker": { - "version": "2.2.1", - "resolved": "https://registry.npmjs.org/is-docker/-/is-docker-2.2.1.tgz", - "integrity": "sha512-F+i2BKsFrH66iaUFc0woD8sLy8getkwTwtOBjvs56Cx4CgJDeKQeqfz8wAYiSb8JOprWhHH5p77PbmYCvvUuXQ==", - "dev": true - }, - "is-extglob": { - "version": "2.1.1", - "resolved": "https://registry.npmjs.org/is-extglob/-/is-extglob-2.1.1.tgz", - "integrity": "sha1-qIwCU1eR8C7TfHahueqXc8gz+MI=", - "dev": true - }, - "is-fullwidth-code-point": { - "version": "3.0.0", - "resolved": "https://registry.npmjs.org/is-fullwidth-code-point/-/is-fullwidth-code-point-3.0.0.tgz", - "integrity": "sha512-zymm5+u+sCsSWyD9qNaejV3DFvhCKclKdizYaJUuHA83RLjb7nSuGnddCHGv0hk+KY7BMAlsWeK4Ueg6EV6XQg==", - "dev": true - }, - "is-glob": { - "version": "4.0.3", - "resolved": "https://registry.npmjs.org/is-glob/-/is-glob-4.0.3.tgz", - "integrity": "sha512-xelSayHH36ZgE7ZWhli7pW34hNbNl8Ojv5KVmkJD4hBdD3th8Tfk9vYasLM+mXWOZhFkgZfxhLSnrwRr4elSSg==", - "dev": true, - "requires": { - "is-extglob": "^2.1.1" - } - }, - "is-installed-globally": { - "version": "0.4.0", - "resolved": "https://registry.npmjs.org/is-installed-globally/-/is-installed-globally-0.4.0.tgz", - "integrity": "sha512-iwGqO3J21aaSkC7jWnHP/difazwS7SFeIqxv6wEtLU8Y5KlzFTjyqcSIT0d8s4+dDhKytsk9PJZ2BkS5eZwQRQ==", - "dev": true, - "requires": { - "global-dirs": "^3.0.0", - "is-path-inside": "^3.0.2" - } - }, - "is-interactive": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/is-interactive/-/is-interactive-1.0.0.tgz", - "integrity": "sha512-2HvIEKRoqS62guEC+qBjpvRubdX910WCMuJTZ+I9yvqKU2/12eSL549HMwtabb4oupdj2sMP50k+XJfB/8JE6w==", - "dev": true - }, - "is-npm": { - "version": "5.0.0", - "resolved": "https://registry.npmjs.org/is-npm/-/is-npm-5.0.0.tgz", - "integrity": "sha512-WW/rQLOazUq+ST/bCAVBp/2oMERWLsR7OrKyt052dNDk4DHcDE0/7QSXITlmi+VBcV13DfIbysG3tZJm5RfdBA==", - "dev": true - }, - "is-number": { - "version": "7.0.0", - "resolved": "https://registry.npmjs.org/is-number/-/is-number-7.0.0.tgz", - "integrity": "sha512-41Cifkg6e8TylSpdtTpeLVMqvSBEVzTttHvERD741+pnZ8ANv0004MRL43QKPDlK9cGvNp6NZWZUBlbGXYxxng==", - "dev": true - }, - "is-obj": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/is-obj/-/is-obj-2.0.0.tgz", - "integrity": "sha512-drqDG3cbczxxEJRoOXcOjtdp1J/lyp1mNn0xaznRs8+muBhgQcrnbspox5X5fOw0HnMnbfDzvnEMEtqDEJEo8w==", - "dev": true - }, - "is-path-inside": { - "version": "3.0.3", - "resolved": "https://registry.npmjs.org/is-path-inside/-/is-path-inside-3.0.3.tgz", - "integrity": "sha512-Fd4gABb+ycGAmKou8eMftCupSir5lRxqf4aD/vd0cD2qc4HL07OjCeuHMr8Ro4CoMaeCKDB0/ECBOVWjTwUvPQ==", - "dev": true - }, - "is-plain-obj": { - "version": "2.1.0", - "resolved": "https://registry.npmjs.org/is-plain-obj/-/is-plain-obj-2.1.0.tgz", - "integrity": "sha512-YWnfyRwxL/+SsrWYfOpUtz5b3YD+nyfkHvjbcanzk8zgyO4ASD67uVMRt8k5bM4lLMDnXfriRhOpemw+NfT1eA==", - "dev": true - }, - "is-plain-object": { - "version": "5.0.0", - "resolved": "https://registry.npmjs.org/is-plain-object/-/is-plain-object-5.0.0.tgz", - "integrity": "sha512-VRSzKkbMm5jMDoKLbltAkFQ5Qr7VDiTFGXxYFXXowVj387GeGNOCsOH6Msy00SGZ3Fp84b1Naa1psqgcCIEP5Q==", - "dev": true - }, - "is-ssh": { - "version": "1.3.3", - "resolved": "https://registry.npmjs.org/is-ssh/-/is-ssh-1.3.3.tgz", - "integrity": "sha512-NKzJmQzJfEEma3w5cJNcUMxoXfDjz0Zj0eyCalHn2E6VOwlzjZo0yuO2fcBSf8zhFuVCL/82/r5gRcoi6aEPVQ==", - "dev": true, - "requires": { - "protocols": "^1.1.0" - } - }, - "is-stream": { - "version": "2.0.1", - "resolved": "https://registry.npmjs.org/is-stream/-/is-stream-2.0.1.tgz", - "integrity": "sha512-hFoiJiTl63nn+kstHGBtewWSKnQLpyb155KHheA1l39uvtO9nWIop1p3udqPcUd/xbF1VLMO4n7OI6p7RbngDg==", - "dev": true - }, - "is-typedarray": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/is-typedarray/-/is-typedarray-1.0.0.tgz", - "integrity": "sha1-5HnICFjfDBsR3dppQPlgEfzaSpo=", - "dev": true - }, - "is-unicode-supported": { - "version": "0.1.0", - "resolved": "https://registry.npmjs.org/is-unicode-supported/-/is-unicode-supported-0.1.0.tgz", - "integrity": "sha512-knxG2q4UC3u8stRGyAVJCOdxFmv5DZiRcdlIaAQXAbSfJya+OhopNotLQrstBhququ4ZpuKbDc/8S6mgXgPFPw==", - "dev": true - }, - "is-windows": { - "version": "1.0.2", - "resolved": "https://registry.npmjs.org/is-windows/-/is-windows-1.0.2.tgz", - "integrity": "sha512-eXK1UInq2bPmjyX6e3VHIzMLobc4J94i4AWn+Hpq3OU5KkrRC96OAcR3PRJ/pGu6m8TRnBHP9dkXQVsT/COVIA==", - "dev": true - }, - "is-wsl": { - "version": "2.2.0", - "resolved": "https://registry.npmjs.org/is-wsl/-/is-wsl-2.2.0.tgz", - "integrity": "sha512-fKzAra0rGJUUBwGBgNkHZuToZcn+TtXHpeCgmkMJMMYx1sQDYaCSyjJBSCa2nH1DGm7s3n1oBnohoVTBaN7Lww==", - "dev": true, - "requires": { - "is-docker": "^2.0.0" - } - }, - "is-yarn-global": { - "version": "0.3.0", - "resolved": "https://registry.npmjs.org/is-yarn-global/-/is-yarn-global-0.3.0.tgz", - "integrity": "sha512-VjSeb/lHmkoyd8ryPVIKvOCn4D1koMqY+vqyjjUfc3xyKtP4dYOxM44sZrnqQSzSds3xyOrUTLTC9LVCVgLngw==", - "dev": true - }, - "isarray": { - "version": "0.0.1", - "resolved": "https://registry.npmjs.org/isarray/-/isarray-0.0.1.tgz", - "integrity": "sha1-ihis/Kmo9Bd+Cav8YDiTmwXR7t8=", - "dev": true - }, - "isexe": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/isexe/-/isexe-2.0.0.tgz", - "integrity": "sha1-6PvzdNxVb/iUehDcsFctYz8s+hA=", - "dev": true - }, - "istanbul-lib-coverage": { - "version": "3.2.0", - "resolved": "https://registry.npmjs.org/istanbul-lib-coverage/-/istanbul-lib-coverage-3.2.0.tgz", - "integrity": "sha512-eOeJ5BHCmHYvQK7xt9GkdHuzuCGS1Y6g9Gvnx3Ym33fz/HpLRYxiS0wHNr+m/MBC8B647Xt608vCDEvhl9c6Mw==", - "dev": true - }, - "istanbul-lib-hook": { - "version": "3.0.0", - "resolved": "https://registry.npmjs.org/istanbul-lib-hook/-/istanbul-lib-hook-3.0.0.tgz", - "integrity": "sha512-Pt/uge1Q9s+5VAZ+pCo16TYMWPBIl+oaNIjgLQxcX0itS6ueeaA+pEfThZpH8WxhFgCiEb8sAJY6MdUKgiIWaQ==", - "dev": true, - "requires": { - "append-transform": "^2.0.0" - } - }, - "istanbul-lib-instrument": { - "version": "4.0.3", - "resolved": "https://registry.npmjs.org/istanbul-lib-instrument/-/istanbul-lib-instrument-4.0.3.tgz", - "integrity": "sha512-BXgQl9kf4WTCPCCpmFGoJkz/+uhvm7h7PFKUYxh7qarQd3ER33vHG//qaE8eN25l07YqZPpHXU9I09l/RD5aGQ==", - "dev": true, - "requires": { - "@babel/core": "^7.7.5", - "@istanbuljs/schema": "^0.1.2", - "istanbul-lib-coverage": "^3.0.0", - "semver": "^6.3.0" - }, - "dependencies": { - "semver": { - "version": "6.3.0", - "resolved": "https://registry.npmjs.org/semver/-/semver-6.3.0.tgz", - "integrity": "sha512-b39TBaTSfV6yBrapU89p5fKekE2m/NwnDocOVruQFS1/veMgdzuPcnOM34M6CwxW8jH/lxEa5rBoDeUwu5HHTw==", - "dev": true - } - } - }, - "istanbul-lib-processinfo": { - "version": "2.0.2", - "resolved": "https://registry.npmjs.org/istanbul-lib-processinfo/-/istanbul-lib-processinfo-2.0.2.tgz", - "integrity": "sha512-kOwpa7z9hme+IBPZMzQ5vdQj8srYgAtaRqeI48NGmAQ+/5yKiHLV0QbYqQpxsdEF0+w14SoB8YbnHKcXE2KnYw==", - "dev": true, - "requires": { - "archy": "^1.0.0", - "cross-spawn": "^7.0.0", - "istanbul-lib-coverage": "^3.0.0-alpha.1", - "make-dir": "^3.0.0", - "p-map": "^3.0.0", - "rimraf": "^3.0.0", - "uuid": "^3.3.3" - } - }, - "istanbul-lib-report": { - "version": "3.0.0", - "resolved": "https://registry.npmjs.org/istanbul-lib-report/-/istanbul-lib-report-3.0.0.tgz", - "integrity": "sha512-wcdi+uAKzfiGT2abPpKZ0hSU1rGQjUQnLvtY5MpQ7QCTahD3VODhcu4wcfY1YtkGaDD5yuydOLINXsfbus9ROw==", - "dev": true, - "requires": { - "istanbul-lib-coverage": "^3.0.0", - "make-dir": "^3.0.0", - "supports-color": "^7.1.0" - } - }, - "istanbul-lib-source-maps": { - "version": "4.0.1", - "resolved": "https://registry.npmjs.org/istanbul-lib-source-maps/-/istanbul-lib-source-maps-4.0.1.tgz", - "integrity": "sha512-n3s8EwkdFIJCG3BPKBYvskgXGoy88ARzvegkitk60NxRdwltLOTaH7CUiMRXvwYorl0Q712iEjcWB+fK/MrWVw==", - "dev": true, - "requires": { - "debug": "^4.1.1", - "istanbul-lib-coverage": "^3.0.0", - "source-map": "^0.6.1" - }, - "dependencies": { - "source-map": { - "version": "0.6.1", - "resolved": "https://registry.npmjs.org/source-map/-/source-map-0.6.1.tgz", - "integrity": "sha512-UjgapumWlbMhkBgzT7Ykc5YXUT46F0iKu8SGXq0bcwP5dz/h0Plj6enJqjz1Zbq2l5WaqYnrVbwWOWMyF3F47g==", - "dev": true - } - } - }, - "istanbul-reports": { - "version": "3.0.5", - "resolved": "https://registry.npmjs.org/istanbul-reports/-/istanbul-reports-3.0.5.tgz", - "integrity": "sha512-5+19PlhnGabNWB7kOFnuxT8H3T/iIyQzIbQMxXsURmmvKg86P2sbkrGOT77VnHw0Qr0gc2XzRaRfMZYYbSQCJQ==", - "dev": true, - "requires": { - "html-escaper": "^2.0.0", - "istanbul-lib-report": "^3.0.0" - } - }, - "js-tokens": { - "version": "4.0.0", - "resolved": "https://registry.npmjs.org/js-tokens/-/js-tokens-4.0.0.tgz", - "integrity": "sha512-RdJUflcE3cUzKiMqQgsCu06FPu9UdIJO0beYbPhHN4k6apgJtifcoCtT9bcxOpYBtpD2kCM6Sbzg4CausW/PKQ==", - "dev": true - }, - "js-yaml": { - "version": "4.1.0", - "resolved": "https://registry.npmjs.org/js-yaml/-/js-yaml-4.1.0.tgz", - "integrity": "sha512-wpxZs9NoxZaJESJGIZTyDEaYpl0FKSA+FB9aJiyemKhMwkxQg63h4T1KJgUGHpTqPDNRcmmYLugrRjJlBtWvRA==", - "dev": true, - "requires": { - "argparse": "^2.0.1" - } - }, - "jsesc": { - "version": "2.5.2", - "resolved": "https://registry.npmjs.org/jsesc/-/jsesc-2.5.2.tgz", - "integrity": "sha512-OYu7XEzjkCQ3C5Ps3QIZsQfNpqoJyZZA99wd9aWd05NCtC5pWOkShK2mkL6HXQR6/Cy2lbNdPlZBpuQHXE63gA==", - "dev": true - }, - "json-buffer": { - "version": "3.0.1", - "resolved": "https://registry.npmjs.org/json-buffer/-/json-buffer-3.0.1.tgz", - "integrity": "sha512-4bV5BfR2mqfQTJm+V5tPPdf+ZpuhiIvTuAB5g8kcrXOZpTT/QwwVRWBywX1ozr6lEuPdbHxwaJlm9G6mI2sfSQ==", - "dev": true - }, - "json-parse-even-better-errors": { - "version": "2.3.1", - "resolved": "https://registry.npmjs.org/json-parse-even-better-errors/-/json-parse-even-better-errors-2.3.1.tgz", - "integrity": "sha512-xyFwyhro/JEof6Ghe2iz2NcXoj2sloNsWr/XsERDK/oiPCfaNhl5ONfp+jQdAZRQQ0IJWNzH9zIZF7li91kh2w==", - "dev": true - }, - "json-schema-traverse": { - "version": "0.4.1", - "resolved": "https://registry.npmjs.org/json-schema-traverse/-/json-schema-traverse-0.4.1.tgz", - "integrity": "sha512-xbbCH5dCYU5T8LcEhhuh7HJ88HXuW3qsI3Y0zOZFKfZEHcpWiHU/Jxzk629Brsab/mMiHQti9wMP+845RPe3Vg==", - "dev": true - }, - "json-stable-stringify-without-jsonify": { - "version": "1.0.1", - "resolved": "https://registry.npmjs.org/json-stable-stringify-without-jsonify/-/json-stable-stringify-without-jsonify-1.0.1.tgz", - "integrity": "sha1-nbe1lJatPzz+8wp1FC0tkwrXJlE=", - "dev": true - }, - "json5": { - "version": "2.2.0", - "resolved": "https://registry.npmjs.org/json5/-/json5-2.2.0.tgz", - "integrity": "sha512-f+8cldu7X/y7RAJurMEJmdoKXGB/X550w2Nr3tTbezL6RwEE/iMcm+tZnXeoZtKuOq6ft8+CqzEkrIgx1fPoQA==", - "dev": true, - "requires": { - "minimist": "^1.2.5" - } - }, - "jsonc-parser": { - "version": "3.0.0", - "resolved": "https://registry.npmjs.org/jsonc-parser/-/jsonc-parser-3.0.0.tgz", - "integrity": "sha512-fQzRfAbIBnR0IQvftw9FJveWiHp72Fg20giDrHz6TdfB12UH/uue0D3hm57UB5KgAVuniLMCaS8P1IMj9NR7cA==", - "dev": true - }, - "just-extend": { - "version": "4.2.1", - "resolved": "https://registry.npmjs.org/just-extend/-/just-extend-4.2.1.tgz", - "integrity": "sha512-g3UB796vUFIY90VIv/WX3L2c8CS2MdWUww3CNrYmqza1Fg0DURc2K/O4YrnklBdQarSJ/y8JnJYDGc+1iumQjg==", - "dev": true - }, - "keyv": { - "version": "4.0.4", - "resolved": "https://registry.npmjs.org/keyv/-/keyv-4.0.4.tgz", - "integrity": "sha512-vqNHbAc8BBsxk+7QBYLW0Y219rWcClspR6WSeoHYKG5mnsSoOH+BL1pWq02DDCVdvvuUny5rkBlzMRzoqc+GIg==", - "dev": true, - "requires": { - "json-buffer": "3.0.1" - } - }, - "latest-version": { - "version": "5.1.0", - "resolved": "https://registry.npmjs.org/latest-version/-/latest-version-5.1.0.tgz", - "integrity": "sha512-weT+r0kTkRQdCdYCNtkMwWXQTMEswKrFBkm4ckQOMVhhqhIMI1UT2hMj+1iigIhgSZm5gTmrRXBNoGUgaTY1xA==", - "dev": true, - "requires": { - "package-json": "^6.3.0" - } - }, - "levn": { - "version": "0.4.1", - "resolved": "https://registry.npmjs.org/levn/-/levn-0.4.1.tgz", - "integrity": "sha512-+bT2uH4E5LGE7h/n3evcS/sQlJXCpIp6ym8OWJ5eV6+67Dsql/LaaT7qJBAt2rzfoa/5QBGBhxDix1dMt2kQKQ==", - "dev": true, - "requires": { - "prelude-ls": "^1.2.1", - "type-check": "~0.4.0" - } - }, - "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=", - "dev": true - }, - "locate-path": { - "version": "6.0.0", - "resolved": "https://registry.npmjs.org/locate-path/-/locate-path-6.0.0.tgz", - "integrity": "sha512-iPZK6eYjbxRu3uB4/WZ3EsEIMJFMqAoopl3R+zuq0UjcAm/MO6KCweDgPfP3elTztoKP3KtnVHxTn2NHBSDVUw==", - "dev": true, - "requires": { - "p-locate": "^5.0.0" - } - }, - "lodash": { - "version": "4.17.21", - "resolved": "https://registry.npmjs.org/lodash/-/lodash-4.17.21.tgz", - "integrity": "sha512-v2kDEe57lecTulaDIuNTPy3Ry4gLGJ6Z1O3vE1krgXZNrsQ+LFTGHVxVjcXPs17LhbZVGedAJv8XZ1tvj5FvSg==", - "dev": true - }, - "lodash.flattendeep": { - "version": "4.4.0", - "resolved": "https://registry.npmjs.org/lodash.flattendeep/-/lodash.flattendeep-4.4.0.tgz", - "integrity": "sha1-+wMJF/hqMTTlvJvsDWngAT3f7bI=", - "dev": true - }, - "lodash.get": { - "version": "4.4.2", - "resolved": "https://registry.npmjs.org/lodash.get/-/lodash.get-4.4.2.tgz", - "integrity": "sha1-LRd/ZS+jHpObRDjVNBSZ36OCXpk=", - "dev": true - }, - "lodash.merge": { - "version": "4.6.2", - "resolved": "https://registry.npmjs.org/lodash.merge/-/lodash.merge-4.6.2.tgz", - "integrity": "sha512-0KpjqXRVvrYyCsX1swR/XTK0va6VQkQM6MNo7PqW77ByjAhoARA8EfrP1N4+KlKj8YS0ZUCtRT/YUuhyYDujIQ==", - "dev": true - }, - "log-symbols": { - "version": "4.1.0", - "resolved": "https://registry.npmjs.org/log-symbols/-/log-symbols-4.1.0.tgz", - "integrity": "sha512-8XPvpAA8uyhfteu8pIvQxpJZ7SYYdpUivZpGy6sFsBuKRY/7rQGavedeB8aK+Zkyq6upMFVL/9AW6vOYzfRyLg==", - "dev": true, - "requires": { - "chalk": "^4.1.0", - "is-unicode-supported": "^0.1.0" - } - }, - "lowercase-keys": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/lowercase-keys/-/lowercase-keys-2.0.0.tgz", - "integrity": "sha512-tqNXrS78oMOE73NMxK4EMLQsQowWf8jKooH9g7xPavRT706R6bkQJ6DY2Te7QukaZsulxa30wQ7bk0pm4XiHmA==", - "dev": true - }, - "lru-cache": { - "version": "6.0.0", - "resolved": "https://registry.npmjs.org/lru-cache/-/lru-cache-6.0.0.tgz", - "integrity": "sha512-Jo6dJ04CmSjuznwJSS3pUeWmd/H0ffTlkXXgwZi+eq1UCmqQwCh+eLsYOYCwY991i2Fah4h1BEMCx4qThGbsiA==", - "dev": true, - "requires": { - "yallist": "^4.0.0" - } - }, - "lunr": { - "version": "2.3.9", - "resolved": "https://registry.npmjs.org/lunr/-/lunr-2.3.9.tgz", - "integrity": "sha512-zTU3DaZaF3Rt9rhN3uBMGQD3dD2/vFQqnvZCDv4dl5iOzq2IZQqTxu90r4E5J+nP70J3ilqVCrbho2eWaeW8Ow==", - "dev": true - }, - "macos-release": { - "version": "2.5.0", - "resolved": "https://registry.npmjs.org/macos-release/-/macos-release-2.5.0.tgz", - "integrity": "sha512-EIgv+QZ9r+814gjJj0Bt5vSLJLzswGmSUbUpbi9AIr/fsN2IWFBl2NucV9PAiek+U1STK468tEkxmVYUtuAN3g==", - "dev": true - }, - "make-dir": { - "version": "3.1.0", - "resolved": "https://registry.npmjs.org/make-dir/-/make-dir-3.1.0.tgz", - "integrity": "sha512-g3FeP20LNwhALb/6Cz6Dd4F2ngze0jz7tbzrD2wAV+o9FeNHe4rL+yK2md0J/fiSf1sa1ADhXqi5+oVwOM/eGw==", - "dev": true, - "requires": { - "semver": "^6.0.0" - }, - "dependencies": { - "semver": { - "version": "6.3.0", - "resolved": "https://registry.npmjs.org/semver/-/semver-6.3.0.tgz", - "integrity": "sha512-b39TBaTSfV6yBrapU89p5fKekE2m/NwnDocOVruQFS1/veMgdzuPcnOM34M6CwxW8jH/lxEa5rBoDeUwu5HHTw==", - "dev": true - } - } - }, - "make-error": { - "version": "1.3.6", - "resolved": "https://registry.npmjs.org/make-error/-/make-error-1.3.6.tgz", - "integrity": "sha512-s8UhlNe7vPKomQhC1qFelMokr/Sc3AgNbso3n74mVPA5LTZwkB9NlXf4XPamLxJE8h0gh73rM94xvwRT2CVInw==", - "dev": true - }, - "marked": { - "version": "3.0.8", - "resolved": "https://registry.npmjs.org/marked/-/marked-3.0.8.tgz", - "integrity": "sha512-0gVrAjo5m0VZSJb4rpL59K1unJAMb/hm8HRXqasD8VeC8m91ytDPMritgFSlKonfdt+rRYYpP/JfLxgIX8yoSw==", - "dev": true - }, - "merge-stream": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/merge-stream/-/merge-stream-2.0.0.tgz", - "integrity": "sha512-abv/qOcuPfk3URPfDzmZU1LKmuw8kT+0nIHvKrKgFrwifol/doWcdA4ZqsWQ8ENrFKkd67Mfpo/LovbIUsbt3w==", - "dev": true - }, - "merge2": { - "version": "1.4.1", - "resolved": "https://registry.npmjs.org/merge2/-/merge2-1.4.1.tgz", - "integrity": "sha512-8q7VEgMJW4J8tcfVPy8g09NcQwZdbwFEqhe/WZkoIzjn/3TGDwtOCYtXGxA3O8tPzpczCCDgv+P2P5y00ZJOOg==", - "dev": true - }, - "micromatch": { - "version": "4.0.4", - "resolved": "https://registry.npmjs.org/micromatch/-/micromatch-4.0.4.tgz", - "integrity": "sha512-pRmzw/XUcwXGpD9aI9q/0XOwLNygjETJ8y0ao0wdqprrzDa4YnxLcz7fQRZr8voh8V10kGhABbNcHVk5wHgWwg==", - "dev": true, - "requires": { - "braces": "^3.0.1", - "picomatch": "^2.2.3" - } - }, - "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==", - "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==", - "dev": true, - "requires": { - "mime-db": "1.49.0" - } - }, - "mimic-fn": { - "version": "2.1.0", - "resolved": "https://registry.npmjs.org/mimic-fn/-/mimic-fn-2.1.0.tgz", - "integrity": "sha512-OqbOk5oEQeAZ8WXWydlu9HJjz9WVdEIvamMCcXmuqUYjTknH/sqsWvhQ3vgwKFRR1HpjvNBKQ37nbJgYzGqGcg==", - "dev": true - }, - "mimic-response": { - "version": "1.0.1", - "resolved": "https://registry.npmjs.org/mimic-response/-/mimic-response-1.0.1.tgz", - "integrity": "sha512-j5EctnkH7amfV/q5Hgmoal1g2QHFJRraOtmx0JpIqkxhBhI/lJSl1nMpQ45hVarwNETOoWEimndZ4QK0RHxuxQ==", - "dev": true - }, - "minimatch": { - "version": "3.0.4", - "resolved": "https://registry.npmjs.org/minimatch/-/minimatch-3.0.4.tgz", - "integrity": "sha512-yJHVQEhyqPLUTgt9B83PXu6W3rx4MvvHvSUvToogpwoGDOUQ+yDrR0HRot+yOCdCO7u4hX3pWft6kWBBcqh0UA==", - "dev": true, - "requires": { - "brace-expansion": "^1.1.7" - } - }, - "minimist": { - "version": "1.2.5", - "resolved": "https://registry.npmjs.org/minimist/-/minimist-1.2.5.tgz", - "integrity": "sha512-FM9nNUYrRBAELZQT3xeZQ7fmMOBg6nWNmJKTcgsJeaLstP/UODVpGsr5OhXhhXg6f+qtJ8uiZ+PUxkDWcgIXLw==", - "dev": true - }, - "mocha": { - "version": "9.1.3", - "resolved": "https://registry.npmjs.org/mocha/-/mocha-9.1.3.tgz", - "integrity": "sha512-Xcpl9FqXOAYqI3j79pEtHBBnQgVXIhpULjGQa7DVb0Po+VzmSIK9kanAiWLHoRR/dbZ2qpdPshuXr8l1VaHCzw==", - "dev": true, - "requires": { - "@ungap/promise-all-settled": "1.1.2", - "ansi-colors": "4.1.1", - "browser-stdout": "1.3.1", - "chokidar": "3.5.2", - "debug": "4.3.2", - "diff": "5.0.0", - "escape-string-regexp": "4.0.0", - "find-up": "5.0.0", - "glob": "7.1.7", - "growl": "1.10.5", - "he": "1.2.0", - "js-yaml": "4.1.0", - "log-symbols": "4.1.0", - "minimatch": "3.0.4", - "ms": "2.1.3", - "nanoid": "3.1.25", - "serialize-javascript": "6.0.0", - "strip-json-comments": "3.1.1", - "supports-color": "8.1.1", - "which": "2.0.2", - "workerpool": "6.1.5", - "yargs": "16.2.0", - "yargs-parser": "20.2.4", - "yargs-unparser": "2.0.0" - }, - "dependencies": { - "ms": { - "version": "2.1.3", - "resolved": "https://registry.npmjs.org/ms/-/ms-2.1.3.tgz", - "integrity": "sha512-6FlzubTLZG3J2a/NVCAleEhjzq5oxgHyaCU9yYXvcLsvoVaHJq/s5xXI6/XXP6tz7R9xAOtHnSO/tXtF3WRTlA==", - "dev": true - }, - "supports-color": { - "version": "8.1.1", - "resolved": "https://registry.npmjs.org/supports-color/-/supports-color-8.1.1.tgz", - "integrity": "sha512-MpUEN2OodtUzxvKQl72cUF7RQ5EiHsGvSsVG0ia9c5RbWGL2CI4C7EpPS8UTBIplnlzZiNuV56w+FuNxy3ty2Q==", - "dev": true, - "requires": { - "has-flag": "^4.0.0" - } - }, - "yargs": { - "version": "16.2.0", - "resolved": "https://registry.npmjs.org/yargs/-/yargs-16.2.0.tgz", - "integrity": "sha512-D1mvvtDG0L5ft/jGWkLpG1+m0eQxOfaBvTNELraWj22wSVUMWxZUvYgJYcKh6jGGIkJFhH4IZPQhR4TKpc8mBw==", - "dev": true, - "requires": { - "cliui": "^7.0.2", - "escalade": "^3.1.1", - "get-caller-file": "^2.0.5", - "require-directory": "^2.1.1", - "string-width": "^4.2.0", - "y18n": "^5.0.5", - "yargs-parser": "^20.2.2" - } - } - } - }, - "ms": { - "version": "2.1.2", - "resolved": "https://registry.npmjs.org/ms/-/ms-2.1.2.tgz", - "integrity": "sha512-sGkPx+VjMtmA6MX27oA4FBFELFCZZ4S4XqeGOXCv68tT+jb3vk/RyaKWP0PTKyWtmLSM0b+adUTEvbs1PEaH2w==", - "dev": true - }, - "mute-stream": { - "version": "0.0.8", - "resolved": "https://registry.npmjs.org/mute-stream/-/mute-stream-0.0.8.tgz", - "integrity": "sha512-nnbWWOkoWyUsTjKrhgD0dcz22mdkSnpYqbEjIm2nhwhuxlSkpywJmBo8h0ZqJdkp73mb90SssHkN4rsRaBAfAA==", - "dev": true - }, - "nanoid": { - "version": "3.1.25", - "resolved": "https://registry.npmjs.org/nanoid/-/nanoid-3.1.25.tgz", - "integrity": "sha512-rdwtIXaXCLFAQbnfqDRnI6jaRHp9fTcYBjtFKE8eezcZ7LuLjhUaQGNeMXf1HmRoCH32CLz6XwX0TtxEOS/A3Q==", - "dev": true - }, - "natural-compare": { - "version": "1.4.0", - "resolved": "https://registry.npmjs.org/natural-compare/-/natural-compare-1.4.0.tgz", - "integrity": "sha1-Sr6/7tdUHywnrPspvbvRXI1bpPc=", - "dev": true - }, - "neo-async": { - "version": "2.6.2", - "resolved": "https://registry.npmjs.org/neo-async/-/neo-async-2.6.2.tgz", - "integrity": "sha512-Yd3UES5mWCSqR+qNT93S3UoYUkqAZ9lLg8a7g9rimsWmYGK8cVToA4/sF3RrshdyV3sAGMXVUmpMYOw+dLpOuw==", - "dev": true - }, - "new-github-release-url": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/new-github-release-url/-/new-github-release-url-1.0.0.tgz", - "integrity": "sha512-dle7yf655IMjyFUqn6Nxkb18r4AOAkzRcgcZv6WZ0IqrOH4QCEZ8Sm6I7XX21zvHdBeeMeTkhR9qT2Z0EJDx6A==", - "dev": true, - "requires": { - "type-fest": "^0.4.1" - }, - "dependencies": { - "type-fest": { - "version": "0.4.1", - "resolved": "https://registry.npmjs.org/type-fest/-/type-fest-0.4.1.tgz", - "integrity": "sha512-IwzA/LSfD2vC1/YDYMv/zHP4rDF1usCwllsDpbolT3D4fUepIO7f9K70jjmUewU/LmGUKJcwcVtDCpnKk4BPMw==", - "dev": true - } - } - }, - "nise": { - "version": "5.1.0", - "resolved": "https://registry.npmjs.org/nise/-/nise-5.1.0.tgz", - "integrity": "sha512-W5WlHu+wvo3PaKLsJJkgPup2LrsXCcm7AWwyNZkUnn5rwPkuPBi3Iwk5SQtN0mv+K65k7nKKjwNQ30wg3wLAQQ==", - "dev": true, - "requires": { - "@sinonjs/commons": "^1.7.0", - "@sinonjs/fake-timers": "^7.0.4", - "@sinonjs/text-encoding": "^0.7.1", - "just-extend": "^4.0.2", - "path-to-regexp": "^1.7.0" - } - }, - "node-fetch": { - "version": "2.6.6", - "resolved": "https://registry.npmjs.org/node-fetch/-/node-fetch-2.6.6.tgz", - "integrity": "sha512-Z8/6vRlTUChSdIgMa51jxQ4lrw/Jy5SOW10ObaA47/RElsAN2c5Pn8bTgFGWn/ibwzXTE8qwr1Yzx28vsecXEA==", - "dev": true, - "requires": { - "whatwg-url": "^5.0.0" - } - }, - "node-preload": { - "version": "0.2.1", - "resolved": "https://registry.npmjs.org/node-preload/-/node-preload-0.2.1.tgz", - "integrity": "sha512-RM5oyBy45cLEoHqCeh+MNuFAxO0vTFBLskvQbOKnEE7YTTSN4tbN8QWDIPQ6L+WvKsB/qLEGpYe2ZZ9d4W9OIQ==", - "dev": true, - "requires": { - "process-on-spawn": "^1.0.0" - } - }, - "node-releases": { - "version": "2.0.1", - "resolved": "https://registry.npmjs.org/node-releases/-/node-releases-2.0.1.tgz", - "integrity": "sha512-CqyzN6z7Q6aMeF/ktcMVTzhAHCEpf8SOarwpzpf8pNBY2k5/oM34UHldUwp8VKI7uxct2HxSRdJjBaZeESzcxA==", - "dev": true - }, - "normalize-path": { - "version": "3.0.0", - "resolved": "https://registry.npmjs.org/normalize-path/-/normalize-path-3.0.0.tgz", - "integrity": "sha512-6eZs5Ls3WtCisHWp9S2GUy8dqkpGi4BVSz3GaqiE6ezub0512ESztXUwUB6C6IKbQkY2Pnb/mD4WYojCRwcwLA==", - "dev": true - }, - "normalize-url": { - "version": "6.1.0", - "resolved": "https://registry.npmjs.org/normalize-url/-/normalize-url-6.1.0.tgz", - "integrity": "sha512-DlL+XwOy3NxAQ8xuC0okPgK46iuVNAK01YN7RueYBqqFeGsBjV9XmCAzAdgt+667bCl5kPh9EqKKDwnaPG1I7A==", - "dev": true - }, - "npm-run-path": { - "version": "4.0.1", - "resolved": "https://registry.npmjs.org/npm-run-path/-/npm-run-path-4.0.1.tgz", - "integrity": "sha512-S48WzZW777zhNIrn7gxOlISNAqi9ZC/uQFnRdbeIHhZhCA6UqpkOT8T1G7BvfdgP4Er8gF4sUbaS0i7QvIfCWw==", - "dev": true, - "requires": { - "path-key": "^3.0.0" - } - }, - "nyc": { - "version": "15.1.0", - "resolved": "https://registry.npmjs.org/nyc/-/nyc-15.1.0.tgz", - "integrity": "sha512-jMW04n9SxKdKi1ZMGhvUTHBN0EICCRkHemEoE5jm6mTYcqcdas0ATzgUgejlQUHMvpnOZqGB5Xxsv9KxJW1j8A==", - "dev": true, - "requires": { - "@istanbuljs/load-nyc-config": "^1.0.0", - "@istanbuljs/schema": "^0.1.2", - "caching-transform": "^4.0.0", - "convert-source-map": "^1.7.0", - "decamelize": "^1.2.0", - "find-cache-dir": "^3.2.0", - "find-up": "^4.1.0", - "foreground-child": "^2.0.0", - "get-package-type": "^0.1.0", - "glob": "^7.1.6", - "istanbul-lib-coverage": "^3.0.0", - "istanbul-lib-hook": "^3.0.0", - "istanbul-lib-instrument": "^4.0.0", - "istanbul-lib-processinfo": "^2.0.2", - "istanbul-lib-report": "^3.0.0", - "istanbul-lib-source-maps": "^4.0.0", - "istanbul-reports": "^3.0.2", - "make-dir": "^3.0.0", - "node-preload": "^0.2.1", - "p-map": "^3.0.0", - "process-on-spawn": "^1.0.0", - "resolve-from": "^5.0.0", - "rimraf": "^3.0.0", - "signal-exit": "^3.0.2", - "spawn-wrap": "^2.0.0", - "test-exclude": "^6.0.0", - "yargs": "^15.0.2" - }, - "dependencies": { - "cliui": { - "version": "6.0.0", - "resolved": "https://registry.npmjs.org/cliui/-/cliui-6.0.0.tgz", - "integrity": "sha512-t6wbgtoCXvAzst7QgXxJYqPt0usEfbgQdftEPbLL/cvv6HPE5VgvqCuAIDR0NgU52ds6rFwqrgakNLrHEjCbrQ==", - "dev": true, - "requires": { - "string-width": "^4.2.0", - "strip-ansi": "^6.0.0", - "wrap-ansi": "^6.2.0" - } - }, - "find-up": { - "version": "4.1.0", - "resolved": "https://registry.npmjs.org/find-up/-/find-up-4.1.0.tgz", - "integrity": "sha512-PpOwAdQ/YlXQ2vj8a3h8IipDuYRi3wceVQQGYWxNINccq40Anw7BlsEXCMbt1Zt+OLA6Fq9suIpIWD0OsnISlw==", - "dev": true, - "requires": { - "locate-path": "^5.0.0", - "path-exists": "^4.0.0" - } - }, - "locate-path": { - "version": "5.0.0", - "resolved": "https://registry.npmjs.org/locate-path/-/locate-path-5.0.0.tgz", - "integrity": "sha512-t7hw9pI+WvuwNJXwk5zVHpyhIqzg2qTlklJOf0mVxGSbe3Fp2VieZcduNYjaLDoy6p9uGpQEGWG87WpMKlNq8g==", - "dev": true, - "requires": { - "p-locate": "^4.1.0" - } - }, - "p-limit": { - "version": "2.3.0", - "resolved": "https://registry.npmjs.org/p-limit/-/p-limit-2.3.0.tgz", - "integrity": "sha512-//88mFWSJx8lxCzwdAABTJL2MyWB12+eIY7MDL2SqLmAkeKU9qxRvWuSyTjm3FUmpBEMuFfckAIqEaVGUDxb6w==", - "dev": true, - "requires": { - "p-try": "^2.0.0" - } - }, - "p-locate": { - "version": "4.1.0", - "resolved": "https://registry.npmjs.org/p-locate/-/p-locate-4.1.0.tgz", - "integrity": "sha512-R79ZZ/0wAxKGu3oYMlz8jy/kbhsNrS7SKZ7PxEHBgJ5+F2mtFW2fK2cOtBh1cHYkQsbzFV7I+EoRKe6Yt0oK7A==", - "dev": true, - "requires": { - "p-limit": "^2.2.0" - } - }, - "resolve-from": { - "version": "5.0.0", - "resolved": "https://registry.npmjs.org/resolve-from/-/resolve-from-5.0.0.tgz", - "integrity": "sha512-qYg9KP24dD5qka9J47d0aVky0N+b4fTU89LN9iDnjB5waksiC49rvMB0PrUJQGoTmH50XPiqOvAjDfaijGxYZw==", - "dev": true - }, - "wrap-ansi": { - "version": "6.2.0", - "resolved": "https://registry.npmjs.org/wrap-ansi/-/wrap-ansi-6.2.0.tgz", - "integrity": "sha512-r6lPcBGxZXlIcymEu7InxDMhdW0KDxpLgoFLcguasxCaJ/SOIZwINatK9KY/tf+ZrlywOKU0UDj3ATXUBfxJXA==", - "dev": true, - "requires": { - "ansi-styles": "^4.0.0", - "string-width": "^4.1.0", - "strip-ansi": "^6.0.0" - } - }, - "y18n": { - "version": "4.0.3", - "resolved": "https://registry.npmjs.org/y18n/-/y18n-4.0.3.tgz", - "integrity": "sha512-JKhqTOwSrqNA1NY5lSztJ1GrBiUodLMmIZuLiDaMRJ+itFd+ABVE8XBjOvIWL+rSqNDC74LCSFmlb/U4UZ4hJQ==", - "dev": true - }, - "yargs": { - "version": "15.4.1", - "resolved": "https://registry.npmjs.org/yargs/-/yargs-15.4.1.tgz", - "integrity": "sha512-aePbxDmcYW++PaqBsJ+HYUFwCdv4LVvdnhBy78E57PIor8/OVvhMrADFFEDh8DHDFRv/O9i3lPhsENjO7QX0+A==", - "dev": true, - "requires": { - "cliui": "^6.0.0", - "decamelize": "^1.2.0", - "find-up": "^4.1.0", - "get-caller-file": "^2.0.1", - "require-directory": "^2.1.1", - "require-main-filename": "^2.0.0", - "set-blocking": "^2.0.0", - "string-width": "^4.2.0", - "which-module": "^2.0.0", - "y18n": "^4.0.0", - "yargs-parser": "^18.1.2" - } - }, - "yargs-parser": { - "version": "18.1.3", - "resolved": "https://registry.npmjs.org/yargs-parser/-/yargs-parser-18.1.3.tgz", - "integrity": "sha512-o50j0JeToy/4K6OZcaQmW6lyXXKhq7csREXcDwk2omFPJEwUNOVtJKvmDr9EI1fAJZUyZcRF7kxGBWmRXudrCQ==", - "dev": true, - "requires": { - "camelcase": "^5.0.0", - "decamelize": "^1.2.0" - } - } - } - }, - "object-inspect": { - "version": "1.11.0", - "resolved": "https://registry.npmjs.org/object-inspect/-/object-inspect-1.11.0.tgz", - "integrity": "sha512-jp7ikS6Sd3GxQfZJPyH3cjcbJF6GZPClgdV+EFygjFLQ5FmW/dRUnTd9PQ9k0JhoNDabWFbpF1yCdSWCC6gexg==", - "dev": true - }, - "once": { - "version": "1.4.0", - "resolved": "https://registry.npmjs.org/once/-/once-1.4.0.tgz", - "integrity": "sha1-WDsap3WWHUsROsF9nFC6753Xa9E=", - "dev": true, - "requires": { - "wrappy": "1" - } - }, - "onetime": { - "version": "5.1.2", - "resolved": "https://registry.npmjs.org/onetime/-/onetime-5.1.2.tgz", - "integrity": "sha512-kbpaSSGJTWdAY5KPVeMOKXSrPtr8C8C7wodJbcsd51jRnmD+GZu8Y0VoU6Dm5Z4vWr0Ig/1NKuWRKf7j5aaYSg==", - "dev": true, - "requires": { - "mimic-fn": "^2.1.0" - } - }, - "onigasm": { - "version": "2.2.5", - "resolved": "https://registry.npmjs.org/onigasm/-/onigasm-2.2.5.tgz", - "integrity": "sha512-F+th54mPc0l1lp1ZcFMyL/jTs2Tlq4SqIHKIXGZOR/VkHkF9A7Fr5rRr5+ZG/lWeRsyrClLYRq7s/yFQ/XhWCA==", - "dev": true, - "requires": { - "lru-cache": "^5.1.1" - }, - "dependencies": { - "lru-cache": { - "version": "5.1.1", - "resolved": "https://registry.npmjs.org/lru-cache/-/lru-cache-5.1.1.tgz", - "integrity": "sha512-KpNARQA3Iwv+jTA0utUVVbrh+Jlrr1Fv0e56GGzAFOXN7dk/FviaDW8LHmK52DlcH4WP2n6gI8vN1aesBFgo9w==", - "dev": true, - "requires": { - "yallist": "^3.0.2" - } - }, - "yallist": { - "version": "3.1.1", - "resolved": "https://registry.npmjs.org/yallist/-/yallist-3.1.1.tgz", - "integrity": "sha512-a4UGQaWPH59mOXUYnAG2ewncQS4i4F43Tv3JoAM+s2VDAmS9NsK8GpDMLrCHPksFT7h3K6TOoUNn2pb7RoXx4g==", - "dev": true - } - } - }, - "open": { - "version": "7.4.2", - "resolved": "https://registry.npmjs.org/open/-/open-7.4.2.tgz", - "integrity": "sha512-MVHddDVweXZF3awtlAS+6pgKLlm/JgxZ90+/NBurBoQctVOOB/zDdVjcyPzQ+0laDGbsWgrRkflI65sQeOgT9Q==", - "dev": true, - "requires": { - "is-docker": "^2.0.0", - "is-wsl": "^2.1.1" - } - }, - "optionator": { - "version": "0.9.1", - "resolved": "https://registry.npmjs.org/optionator/-/optionator-0.9.1.tgz", - "integrity": "sha512-74RlY5FCnhq4jRxVUPKDaRwrVNXMqsGsiW6AJw4XK8hmtm10wC0ypZBLw5IIp85NZMr91+qd1RvvENwg7jjRFw==", - "dev": true, - "requires": { - "deep-is": "^0.1.3", - "fast-levenshtein": "^2.0.6", - "levn": "^0.4.1", - "prelude-ls": "^1.2.1", - "type-check": "^0.4.0", - "word-wrap": "^1.2.3" - } - }, - "ora": { - "version": "5.4.1", - "resolved": "https://registry.npmjs.org/ora/-/ora-5.4.1.tgz", - "integrity": "sha512-5b6Y85tPxZZ7QytO+BQzysW31HJku27cRIlkbAXaNx+BdcVi+LlRFmVXzeF6a7JCwJpyw5c4b+YSVImQIrBpuQ==", - "dev": true, - "requires": { - "bl": "^4.1.0", - "chalk": "^4.1.0", - "cli-cursor": "^3.1.0", - "cli-spinners": "^2.5.0", - "is-interactive": "^1.0.0", - "is-unicode-supported": "^0.1.0", - "log-symbols": "^4.1.0", - "strip-ansi": "^6.0.0", - "wcwidth": "^1.0.1" - } - }, - "os-name": { - "version": "4.0.1", - "resolved": "https://registry.npmjs.org/os-name/-/os-name-4.0.1.tgz", - "integrity": "sha512-xl9MAoU97MH1Xt5K9ERft2YfCAoaO6msy1OBA0ozxEC0x0TmIoE6K3QvgJMMZA9yKGLmHXNY/YZoDbiGDj4zYw==", - "dev": true, - "requires": { - "macos-release": "^2.5.0", - "windows-release": "^4.0.0" - } - }, - "os-tmpdir": { - "version": "1.0.2", - "resolved": "https://registry.npmjs.org/os-tmpdir/-/os-tmpdir-1.0.2.tgz", - "integrity": "sha1-u+Z0BseaqFxc/sdm/lc0VV36EnQ=", - "dev": true - }, - "p-cancelable": { - "version": "2.1.1", - "resolved": "https://registry.npmjs.org/p-cancelable/-/p-cancelable-2.1.1.tgz", - "integrity": "sha512-BZOr3nRQHOntUjTrH8+Lh54smKHoHyur8We1V8DSMVrl5A2malOOwuJRnKRDjSnkoeBh4at6BwEnb5I7Jl31wg==", - "dev": true - }, - "p-limit": { - "version": "3.1.0", - "resolved": "https://registry.npmjs.org/p-limit/-/p-limit-3.1.0.tgz", - "integrity": "sha512-TYOanM3wGwNGsZN2cVTYPArw454xnXj5qmWF1bEoAc4+cU/ol7GVh7odevjp1FNHduHc3KZMcFduxU5Xc6uJRQ==", - "dev": true, - "requires": { - "yocto-queue": "^0.1.0" - } - }, - "p-locate": { - "version": "5.0.0", - "resolved": "https://registry.npmjs.org/p-locate/-/p-locate-5.0.0.tgz", - "integrity": "sha512-LaNjtRWUBY++zB5nE/NwcaoMylSPk+S+ZHNB1TzdbMJMny6dynpAGt7X/tl/QYq3TIeE6nxHppbo2LGymrG5Pw==", - "dev": true, - "requires": { - "p-limit": "^3.0.2" - } - }, - "p-map": { - "version": "3.0.0", - "resolved": "https://registry.npmjs.org/p-map/-/p-map-3.0.0.tgz", - "integrity": "sha512-d3qXVTF/s+W+CdJ5A29wywV2n8CQQYahlgz2bFiA+4eVNJbHJodPZ+/gXwPGh0bOqA+j8S+6+ckmvLGPk1QpxQ==", - "dev": true, - "requires": { - "aggregate-error": "^3.0.0" - } - }, - "p-try": { - "version": "2.2.0", - "resolved": "https://registry.npmjs.org/p-try/-/p-try-2.2.0.tgz", - "integrity": "sha512-R4nPAVTAU0B9D35/Gk3uJf/7XYbQcyohSKdvAxIRSNghFl4e71hVoGnBNQz9cWaXxO2I10KTC+3jMdvvoKw6dQ==", - "dev": true - }, - "package-hash": { - "version": "4.0.0", - "resolved": "https://registry.npmjs.org/package-hash/-/package-hash-4.0.0.tgz", - "integrity": "sha512-whdkPIooSu/bASggZ96BWVvZTRMOFxnyUG5PnTSGKoJE2gd5mbVNmR2Nj20QFzxYYgAXpoqC+AiXzl+UMRh7zQ==", - "dev": true, - "requires": { - "graceful-fs": "^4.1.15", - "hasha": "^5.0.0", - "lodash.flattendeep": "^4.4.0", - "release-zalgo": "^1.0.0" - } - }, - "package-json": { - "version": "6.5.0", - "resolved": "https://registry.npmjs.org/package-json/-/package-json-6.5.0.tgz", - "integrity": "sha512-k3bdm2n25tkyxcjSKzB5x8kfVxlMdgsbPr0GkZcwHsLpba6cBjqCt1KlcChKEvxHIcTB1FVMuwoijZ26xex5MQ==", - "dev": true, - "requires": { - "got": "^9.6.0", - "registry-auth-token": "^4.0.0", - "registry-url": "^5.0.0", - "semver": "^6.2.0" - }, - "dependencies": { - "@sindresorhus/is": { - "version": "0.14.0", - "resolved": "https://registry.npmjs.org/@sindresorhus/is/-/is-0.14.0.tgz", - "integrity": "sha512-9NET910DNaIPngYnLLPeg+Ogzqsi9uM4mSboU5y6p8S5DzMTVEsJZrawi+BoDNUVBa2DhJqQYUFvMDfgU062LQ==", - "dev": true - }, - "@szmarczak/http-timer": { - "version": "1.1.2", - "resolved": "https://registry.npmjs.org/@szmarczak/http-timer/-/http-timer-1.1.2.tgz", - "integrity": "sha512-XIB2XbzHTN6ieIjfIMV9hlVcfPU26s2vafYWQcZHWXHOxiaRZYEDKEwdl129Zyg50+foYV2jCgtrqSA6qNuNSA==", - "dev": true, - "requires": { - "defer-to-connect": "^1.0.1" - } - }, - "cacheable-request": { - "version": "6.1.0", - "resolved": "https://registry.npmjs.org/cacheable-request/-/cacheable-request-6.1.0.tgz", - "integrity": "sha512-Oj3cAGPCqOZX7Rz64Uny2GYAZNliQSqfbePrgAQ1wKAihYmCUnraBtJtKcGR4xz7wF+LoJC+ssFZvv5BgF9Igg==", - "dev": true, - "requires": { - "clone-response": "^1.0.2", - "get-stream": "^5.1.0", - "http-cache-semantics": "^4.0.0", - "keyv": "^3.0.0", - "lowercase-keys": "^2.0.0", - "normalize-url": "^4.1.0", - "responselike": "^1.0.2" - }, - "dependencies": { - "get-stream": { - "version": "5.2.0", - "resolved": "https://registry.npmjs.org/get-stream/-/get-stream-5.2.0.tgz", - "integrity": "sha512-nBF+F1rAZVCu/p7rjzgA+Yb4lfYXrpl7a6VmJrU8wF9I1CKvP/QwPNZHnOlwbTkY6dvtFIzFMSyQXbLoTQPRpA==", - "dev": true, - "requires": { - "pump": "^3.0.0" - } - } - } - }, - "decompress-response": { - "version": "3.3.0", - "resolved": "https://registry.npmjs.org/decompress-response/-/decompress-response-3.3.0.tgz", - "integrity": "sha1-gKTdMjdIOEv6JICDYirt7Jgq3/M=", - "dev": true, - "requires": { - "mimic-response": "^1.0.0" - } - }, - "defer-to-connect": { - "version": "1.1.3", - "resolved": "https://registry.npmjs.org/defer-to-connect/-/defer-to-connect-1.1.3.tgz", - "integrity": "sha512-0ISdNousHvZT2EiFlZeZAHBUvSxmKswVCEf8hW7KWgG4a8MVEu/3Vb6uWYozkjylyCxe0JBIiRB1jV45S70WVQ==", - "dev": true - }, - "get-stream": { - "version": "4.1.0", - "resolved": "https://registry.npmjs.org/get-stream/-/get-stream-4.1.0.tgz", - "integrity": "sha512-GMat4EJ5161kIy2HevLlr4luNjBgvmj413KaQA7jt4V8B4RDsfpHk7WQ9GVqfYyyx8OS/L66Kox+rJRNklLK7w==", - "dev": true, - "requires": { - "pump": "^3.0.0" - } - }, - "got": { - "version": "9.6.0", - "resolved": "https://registry.npmjs.org/got/-/got-9.6.0.tgz", - "integrity": "sha512-R7eWptXuGYxwijs0eV+v3o6+XH1IqVK8dJOEecQfTmkncw9AV4dcw/Dhxi8MdlqPthxxpZyizMzyg8RTmEsG+Q==", - "dev": true, - "requires": { - "@sindresorhus/is": "^0.14.0", - "@szmarczak/http-timer": "^1.1.2", - "cacheable-request": "^6.0.0", - "decompress-response": "^3.3.0", - "duplexer3": "^0.1.4", - "get-stream": "^4.1.0", - "lowercase-keys": "^1.0.1", - "mimic-response": "^1.0.1", - "p-cancelable": "^1.0.0", - "to-readable-stream": "^1.0.0", - "url-parse-lax": "^3.0.0" - }, - "dependencies": { - "lowercase-keys": { - "version": "1.0.1", - "resolved": "https://registry.npmjs.org/lowercase-keys/-/lowercase-keys-1.0.1.tgz", - "integrity": "sha512-G2Lj61tXDnVFFOi8VZds+SoQjtQC3dgokKdDG2mTm1tx4m50NUHBOZSBwQQHyy0V12A0JTG4icfZQH+xPyh8VA==", - "dev": true - } - } - }, - "json-buffer": { - "version": "3.0.0", - "resolved": "https://registry.npmjs.org/json-buffer/-/json-buffer-3.0.0.tgz", - "integrity": "sha1-Wx85evx11ne96Lz8Dkfh+aPZqJg=", - "dev": true - }, - "keyv": { - "version": "3.1.0", - "resolved": "https://registry.npmjs.org/keyv/-/keyv-3.1.0.tgz", - "integrity": "sha512-9ykJ/46SN/9KPM/sichzQ7OvXyGDYKGTaDlKMGCAlg2UK8KRy4jb0d8sFc+0Tt0YYnThq8X2RZgCg74RPxgcVA==", - "dev": true, - "requires": { - "json-buffer": "3.0.0" - } - }, - "normalize-url": { - "version": "4.5.1", - "resolved": "https://registry.npmjs.org/normalize-url/-/normalize-url-4.5.1.tgz", - "integrity": "sha512-9UZCFRHQdNrfTpGg8+1INIg93B6zE0aXMVFkw1WFwvO4SlZywU6aLg5Of0Ap/PgcbSw4LNxvMWXMeugwMCX0AA==", - "dev": true - }, - "p-cancelable": { - "version": "1.1.0", - "resolved": "https://registry.npmjs.org/p-cancelable/-/p-cancelable-1.1.0.tgz", - "integrity": "sha512-s73XxOZ4zpt1edZYZzvhqFa6uvQc1vwUa0K0BdtIZgQMAJj9IbebH+JkgKZc9h+B05PKHLOTl4ajG1BmNrVZlw==", - "dev": true - }, - "responselike": { - "version": "1.0.2", - "resolved": "https://registry.npmjs.org/responselike/-/responselike-1.0.2.tgz", - "integrity": "sha1-kYcg7ztjHFZCvgaPFa3lpG9Loec=", - "dev": true, - "requires": { - "lowercase-keys": "^1.0.0" - }, - "dependencies": { - "lowercase-keys": { - "version": "1.0.1", - "resolved": "https://registry.npmjs.org/lowercase-keys/-/lowercase-keys-1.0.1.tgz", - "integrity": "sha512-G2Lj61tXDnVFFOi8VZds+SoQjtQC3dgokKdDG2mTm1tx4m50NUHBOZSBwQQHyy0V12A0JTG4icfZQH+xPyh8VA==", - "dev": true - } - } - }, - "semver": { - "version": "6.3.0", - "resolved": "https://registry.npmjs.org/semver/-/semver-6.3.0.tgz", - "integrity": "sha512-b39TBaTSfV6yBrapU89p5fKekE2m/NwnDocOVruQFS1/veMgdzuPcnOM34M6CwxW8jH/lxEa5rBoDeUwu5HHTw==", - "dev": true - } - } - }, - "parent-module": { - "version": "1.0.1", - "resolved": "https://registry.npmjs.org/parent-module/-/parent-module-1.0.1.tgz", - "integrity": "sha512-GQ2EWRpQV8/o+Aw8YqtfZZPfNRWZYkbidE9k5rpl/hC3vtHHBfGm2Ifi6qWV+coDGkrUKZAxE3Lot5kcsRlh+g==", - "dev": true, - "requires": { - "callsites": "^3.0.0" - } - }, - "parse-json": { - "version": "5.2.0", - "resolved": "https://registry.npmjs.org/parse-json/-/parse-json-5.2.0.tgz", - "integrity": "sha512-ayCKvm/phCGxOkYRSCM82iDwct8/EonSEgCSxWxD7ve6jHggsFl4fZVQBPRNgQoKiuV/odhFrGzQXZwbifC8Rg==", - "dev": true, - "requires": { - "@babel/code-frame": "^7.0.0", - "error-ex": "^1.3.1", - "json-parse-even-better-errors": "^2.3.0", - "lines-and-columns": "^1.1.6" - } - }, - "parse-path": { - "version": "4.0.3", - "resolved": "https://registry.npmjs.org/parse-path/-/parse-path-4.0.3.tgz", - "integrity": "sha512-9Cepbp2asKnWTJ9x2kpw6Fe8y9JDbqwahGCTvklzd/cEq5C5JC59x2Xb0Kx+x0QZ8bvNquGO8/BWP0cwBHzSAA==", - "dev": true, - "requires": { - "is-ssh": "^1.3.0", - "protocols": "^1.4.0", - "qs": "^6.9.4", - "query-string": "^6.13.8" - } - }, - "parse-url": { - "version": "6.0.0", - "resolved": "https://registry.npmjs.org/parse-url/-/parse-url-6.0.0.tgz", - "integrity": "sha512-cYyojeX7yIIwuJzledIHeLUBVJ6COVLeT4eF+2P6aKVzwvgKQPndCBv3+yQ7pcWjqToYwaligxzSYNNmGoMAvw==", - "dev": true, - "requires": { - "is-ssh": "^1.3.0", - "normalize-url": "^6.1.0", - "parse-path": "^4.0.0", - "protocols": "^1.4.0" - } - }, - "path-exists": { - "version": "4.0.0", - "resolved": "https://registry.npmjs.org/path-exists/-/path-exists-4.0.0.tgz", - "integrity": "sha512-ak9Qy5Q7jYb2Wwcey5Fpvg2KoAc/ZIhLSLOSBmRmygPsGwkVVt0fZa0qrtMz+m6tJTAHfZQ8FnmB4MG4LWy7/w==", - "dev": true - }, - "path-is-absolute": { - "version": "1.0.1", - "resolved": "https://registry.npmjs.org/path-is-absolute/-/path-is-absolute-1.0.1.tgz", - "integrity": "sha1-F0uSaHNVNP+8es5r9TpanhtcX18=", - "dev": true - }, - "path-key": { - "version": "3.1.1", - "resolved": "https://registry.npmjs.org/path-key/-/path-key-3.1.1.tgz", - "integrity": "sha512-ojmeN0qd+y0jszEtoY48r0Peq5dwMEkIlCOu6Q5f41lfkswXuKtYrhgoTpLnyIcHm24Uhqx+5Tqm2InSwLhE6Q==", - "dev": true - }, - "path-parse": { - "version": "1.0.7", - "resolved": "https://registry.npmjs.org/path-parse/-/path-parse-1.0.7.tgz", - "integrity": "sha512-LDJzPVEEEPR+y48z93A0Ed0yXb8pAByGWo/k5YYdYgpY2/2EsOsksJrq7lOHxryrVOn1ejG6oAp8ahvOIQD8sw==", - "dev": true - }, - "path-to-regexp": { - "version": "1.8.0", - "resolved": "https://registry.npmjs.org/path-to-regexp/-/path-to-regexp-1.8.0.tgz", - "integrity": "sha512-n43JRhlUKUAlibEJhPeir1ncUID16QnEjNpwzNdO3Lm4ywrBpBZ5oLD0I6br9evr1Y9JTqwRtAh7JLoOzAQdVA==", - "dev": true, - "requires": { - "isarray": "0.0.1" - } - }, - "path-type": { - "version": "4.0.0", - "resolved": "https://registry.npmjs.org/path-type/-/path-type-4.0.0.tgz", - "integrity": "sha512-gDKb8aZMDeD/tZWs9P6+q0J9Mwkdl6xMV8TjnGP3qJVJ06bdMgkbBlLU8IdfOsIsFz2BW1rNVT3XuNEl8zPAvw==", - "dev": true - }, - "picocolors": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/picocolors/-/picocolors-1.0.0.tgz", - "integrity": "sha512-1fygroTLlHu66zi26VoTDv8yRgm0Fccecssto+MhsZ0D/DGW2sm8E8AjW7NU5VVTRt5GxbeZ5qBuJr+HyLYkjQ==", - "dev": true - }, - "picomatch": { - "version": "2.3.0", - "resolved": "https://registry.npmjs.org/picomatch/-/picomatch-2.3.0.tgz", - "integrity": "sha512-lY1Q/PiJGC2zOv/z391WOTD+Z02bCgsFfvxoXXf6h7kv9o+WmsmzYqrAwY63sNgOxE4xEdq0WyUnXfKeBrSvYw==", - "dev": true - }, - "pkg-dir": { - "version": "4.2.0", - "resolved": "https://registry.npmjs.org/pkg-dir/-/pkg-dir-4.2.0.tgz", - "integrity": "sha512-HRDzbaKjC+AOWVXxAU/x54COGeIv9eb+6CkDSQoNTt4XyWoIJvuPsXizxu/Fr23EiekbtZwmh1IcIG/l/a10GQ==", - "dev": true, - "requires": { - "find-up": "^4.0.0" - }, - "dependencies": { - "find-up": { - "version": "4.1.0", - "resolved": "https://registry.npmjs.org/find-up/-/find-up-4.1.0.tgz", - "integrity": "sha512-PpOwAdQ/YlXQ2vj8a3h8IipDuYRi3wceVQQGYWxNINccq40Anw7BlsEXCMbt1Zt+OLA6Fq9suIpIWD0OsnISlw==", - "dev": true, - "requires": { - "locate-path": "^5.0.0", - "path-exists": "^4.0.0" - } - }, - "locate-path": { - "version": "5.0.0", - "resolved": "https://registry.npmjs.org/locate-path/-/locate-path-5.0.0.tgz", - "integrity": "sha512-t7hw9pI+WvuwNJXwk5zVHpyhIqzg2qTlklJOf0mVxGSbe3Fp2VieZcduNYjaLDoy6p9uGpQEGWG87WpMKlNq8g==", - "dev": true, - "requires": { - "p-locate": "^4.1.0" - } - }, - "p-limit": { - "version": "2.3.0", - "resolved": "https://registry.npmjs.org/p-limit/-/p-limit-2.3.0.tgz", - "integrity": "sha512-//88mFWSJx8lxCzwdAABTJL2MyWB12+eIY7MDL2SqLmAkeKU9qxRvWuSyTjm3FUmpBEMuFfckAIqEaVGUDxb6w==", - "dev": true, - "requires": { - "p-try": "^2.0.0" - } - }, - "p-locate": { - "version": "4.1.0", - "resolved": "https://registry.npmjs.org/p-locate/-/p-locate-4.1.0.tgz", - "integrity": "sha512-R79ZZ/0wAxKGu3oYMlz8jy/kbhsNrS7SKZ7PxEHBgJ5+F2mtFW2fK2cOtBh1cHYkQsbzFV7I+EoRKe6Yt0oK7A==", - "dev": true, - "requires": { - "p-limit": "^2.2.0" - } - } - } - }, - "prelude-ls": { - "version": "1.2.1", - "resolved": "https://registry.npmjs.org/prelude-ls/-/prelude-ls-1.2.1.tgz", - "integrity": "sha512-vkcDPrRZo1QZLbn5RLGPpg/WmIQ65qoWWhcGKf/b5eplkkarX0m9z8ppCat4mlOqUsWpyNuYgO3VRyrYHSzX5g==", - "dev": true - }, - "prepend-http": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/prepend-http/-/prepend-http-2.0.0.tgz", - "integrity": "sha1-6SQ0v6XqjBn0HN/UAddBo8gZ2Jc=", - "dev": true - }, - "process-on-spawn": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/process-on-spawn/-/process-on-spawn-1.0.0.tgz", - "integrity": "sha512-1WsPDsUSMmZH5LeMLegqkPDrsGgsWwk1Exipy2hvB0o/F0ASzbpIctSCcZIK1ykJvtTJULEH+20WOFjMvGnCTg==", - "dev": true, - "requires": { - "fromentries": "^1.2.0" - } - }, - "progress": { - "version": "2.0.3", - "resolved": "https://registry.npmjs.org/progress/-/progress-2.0.3.tgz", - "integrity": "sha512-7PiHtLll5LdnKIMw100I+8xJXR5gW2QwWYkT6iJva0bXitZKa/XMrSbdmg3r2Xnaidz9Qumd0VPaMrZlF9V9sA==", - "dev": true - }, - "protocols": { - "version": "1.4.8", - "resolved": "https://registry.npmjs.org/protocols/-/protocols-1.4.8.tgz", - "integrity": "sha512-IgjKyaUSjsROSO8/D49Ab7hP8mJgTYcqApOqdPhLoPxAplXmkp+zRvsrSQjFn5by0rhm4VH0GAUELIPpx7B1yg==", - "dev": true - }, - "pump": { - "version": "3.0.0", - "resolved": "https://registry.npmjs.org/pump/-/pump-3.0.0.tgz", - "integrity": "sha512-LwZy+p3SFs1Pytd/jYct4wpv49HiYCqd9Rlc5ZVdk0V+8Yzv6jR5Blk3TRmPL1ft69TxP0IMZGJ+WPFU2BFhww==", - "dev": true, - "requires": { - "end-of-stream": "^1.1.0", - "once": "^1.3.1" - } - }, - "punycode": { - "version": "2.1.1", - "resolved": "https://registry.npmjs.org/punycode/-/punycode-2.1.1.tgz", - "integrity": "sha512-XRsRjdf+j5ml+y/6GKHPZbrF/8p2Yga0JPtdqTIY2Xe5ohJPD9saDJJLPvp9+NSBprVvevdXZybnj2cv8OEd0A==", - "dev": true - }, - "pupa": { - "version": "2.1.1", - "resolved": "https://registry.npmjs.org/pupa/-/pupa-2.1.1.tgz", - "integrity": "sha512-l1jNAspIBSFqbT+y+5FosojNpVpF94nlI+wDUpqP9enwOTfHx9f0gh5nB96vl+6yTpsJsypeNrwfzPrKuHB41A==", - "dev": true, - "requires": { - "escape-goat": "^2.0.0" - } - }, - "qs": { - "version": "6.10.1", - "resolved": "https://registry.npmjs.org/qs/-/qs-6.10.1.tgz", - "integrity": "sha512-M528Hph6wsSVOBiYUnGf+K/7w0hNshs/duGsNXPUCLH5XAqjEtiPGwNONLV0tBH8NoGb0mvD5JubnUTrujKDTg==", - "dev": true, - "requires": { - "side-channel": "^1.0.4" - } - }, - "query-string": { - "version": "6.14.1", - "resolved": "https://registry.npmjs.org/query-string/-/query-string-6.14.1.tgz", - "integrity": "sha512-XDxAeVmpfu1/6IjyT/gXHOl+S0vQ9owggJ30hhWKdHAsNPOcasn5o9BW0eejZqL2e4vMjhAxoW3jVHcD6mbcYw==", - "dev": true, - "requires": { - "decode-uri-component": "^0.2.0", - "filter-obj": "^1.1.0", - "split-on-first": "^1.0.0", - "strict-uri-encode": "^2.0.0" - } - }, - "queue-microtask": { - "version": "1.2.3", - "resolved": "https://registry.npmjs.org/queue-microtask/-/queue-microtask-1.2.3.tgz", - "integrity": "sha512-NuaNSa6flKT5JaSYQzJok04JzTL1CA6aGhv5rfLW3PgqA+M2ChpZQnAC8h8i4ZFkBS8X5RqkDBHA7r4hej3K9A==", - "dev": true - }, - "quick-lru": { - "version": "5.1.1", - "resolved": "https://registry.npmjs.org/quick-lru/-/quick-lru-5.1.1.tgz", - "integrity": "sha512-WuyALRjWPDGtt/wzJiadO5AXY+8hZ80hVpe6MyivgraREW751X3SbhRvG3eLKOYN+8VEvqLcf3wdnt44Z4S4SA==", - "dev": true - }, - "randombytes": { - "version": "2.1.0", - "resolved": "https://registry.npmjs.org/randombytes/-/randombytes-2.1.0.tgz", - "integrity": "sha512-vYl3iOX+4CKUWuxGi9Ukhie6fsqXqS9FE2Zaic4tNFD2N2QQaXOMFbuKK4QmDHC0JO6B1Zp41J0LpT0oR68amQ==", - "dev": true, - "requires": { - "safe-buffer": "^5.1.0" - } - }, - "rc": { - "version": "1.2.8", - "resolved": "https://registry.npmjs.org/rc/-/rc-1.2.8.tgz", - "integrity": "sha512-y3bGgqKj3QBdxLbLkomlohkvsA8gdAiUQlSBJnBhfn+BPxg4bc62d8TcBW15wavDfgexCgccckhcZvywyQYPOw==", - "dev": true, - "requires": { - "deep-extend": "^0.6.0", - "ini": "~1.3.0", - "minimist": "^1.2.0", - "strip-json-comments": "~2.0.1" - }, - "dependencies": { - "ini": { - "version": "1.3.8", - "resolved": "https://registry.npmjs.org/ini/-/ini-1.3.8.tgz", - "integrity": "sha512-JV/yugV2uzW5iMRSiZAyDtQd+nxtUnjeLt0acNdw98kKLrvuRVyB80tsREOE7yvGVgalhZ6RNXCmEHkUKBKxew==", - "dev": true - }, - "strip-json-comments": { - "version": "2.0.1", - "resolved": "https://registry.npmjs.org/strip-json-comments/-/strip-json-comments-2.0.1.tgz", - "integrity": "sha1-PFMZQukIwml8DsNEhYwobHygpgo=", - "dev": true - } - } - }, - "readable-stream": { - "version": "3.6.0", - "resolved": "https://registry.npmjs.org/readable-stream/-/readable-stream-3.6.0.tgz", - "integrity": "sha512-BViHy7LKeTz4oNnkcLJ+lVSL6vpiFeX6/d3oSH8zCW7UxP2onchk+vTGB143xuFjHS3deTgkKoXXymXqymiIdA==", - "dev": true, - "requires": { - "inherits": "^2.0.3", - "string_decoder": "^1.1.1", - "util-deprecate": "^1.0.1" - } - }, - "readdirp": { - "version": "3.6.0", - "resolved": "https://registry.npmjs.org/readdirp/-/readdirp-3.6.0.tgz", - "integrity": "sha512-hOS089on8RduqdbhvQ5Z37A0ESjsqz6qnRcffsMU3495FuTdqSm+7bhJ29JvIOsBDEEnan5DPu9t3To9VRlMzA==", - "dev": true, - "requires": { - "picomatch": "^2.2.1" - } - }, - "rechoir": { - "version": "0.6.2", - "resolved": "https://registry.npmjs.org/rechoir/-/rechoir-0.6.2.tgz", - "integrity": "sha1-hSBLVNuoLVdC4oyWdW70OvUOM4Q=", - "dev": true, - "requires": { - "resolve": "^1.1.6" - } - }, - "redis": { - "version": "file:packages/all-in-one", - "requires": { - "@redis/client": "^4.0.0-rc", - "@redis/json": "^1.0.0-rc", - "@redis/search": "^1.0.0-rc", - "release-it": "^14.11.6", - "typescript": "^4.4.4" - } - }, - "redis-errors": { - "version": "1.2.0", - "resolved": "https://registry.npmjs.org/redis-errors/-/redis-errors-1.2.0.tgz", - "integrity": "sha1-62LSrbFeTq9GEMBK/hUpOEJQq60=" - }, - "redis-parser": { - "version": "3.0.0", - "resolved": "https://registry.npmjs.org/redis-parser/-/redis-parser-3.0.0.tgz", - "integrity": "sha1-tm2CjNyv5rS4pCin3vTGvKwxyLQ=", - "requires": { - "redis-errors": "^1.0.0" - } - }, - "regexpp": { - "version": "3.2.0", - "resolved": "https://registry.npmjs.org/regexpp/-/regexpp-3.2.0.tgz", - "integrity": "sha512-pq2bWo9mVD43nbts2wGv17XLiNLya+GklZ8kaDLV2Z08gDCsGpnKn9BFMepvWuHCbyVvY7J5o5+BVvoQbmlJLg==", - "dev": true - }, - "registry-auth-token": { - "version": "4.2.1", - "resolved": "https://registry.npmjs.org/registry-auth-token/-/registry-auth-token-4.2.1.tgz", - "integrity": "sha512-6gkSb4U6aWJB4SF2ZvLb76yCBjcvufXBqvvEx1HbmKPkutswjW1xNVRY0+daljIYRbogN7O0etYSlbiaEQyMyw==", - "dev": true, - "requires": { - "rc": "^1.2.8" - } - }, - "registry-url": { - "version": "5.1.0", - "resolved": "https://registry.npmjs.org/registry-url/-/registry-url-5.1.0.tgz", - "integrity": "sha512-8acYXXTI0AkQv6RAOjE3vOaIXZkT9wo4LOFbBKYQEEnnMNBpKqdUrI6S4NT0KPIo/WVvJ5tE/X5LF/TQUf0ekw==", - "dev": true, - "requires": { - "rc": "^1.2.8" - } - }, - "release-it": { - "version": "14.11.6", - "resolved": "https://registry.npmjs.org/release-it/-/release-it-14.11.6.tgz", - "integrity": "sha512-6BNcuzFZHThBUBJ/xYw/bxZ+58CAwrwf1zgmjq2Ibl3nlDZbjphHG6iqxkJu7mZ8TIWs6NjloEAhqpjeXoN//Q==", - "dev": true, - "requires": { - "@iarna/toml": "2.2.5", - "@octokit/rest": "18.10.0", - "async-retry": "1.3.3", - "chalk": "4.1.2", - "cosmiconfig": "7.0.1", - "debug": "4.3.2", - "deprecated-obj": "2.0.0", - "execa": "5.1.1", - "form-data": "4.0.0", - "git-url-parse": "11.6.0", - "globby": "11.0.4", - "got": "11.8.2", - "import-cwd": "3.0.0", - "inquirer": "8.1.5", - "is-ci": "3.0.0", - "lodash": "4.17.21", - "mime-types": "2.1.32", - "new-github-release-url": "1.0.0", - "open": "7.4.2", - "ora": "5.4.1", - "os-name": "4.0.1", - "parse-json": "5.2.0", - "semver": "7.3.5", - "shelljs": "0.8.4", - "update-notifier": "5.1.0", - "url-join": "4.0.1", - "uuid": "8.3.2", - "yaml": "1.10.2", - "yargs-parser": "20.2.9" - }, - "dependencies": { - "uuid": { - "version": "8.3.2", - "resolved": "https://registry.npmjs.org/uuid/-/uuid-8.3.2.tgz", - "integrity": "sha512-+NYs2QeMWy+GWFOEm9xnn6HCDp0l7QBD7ml8zLUmJ+93Q5NF0NocErnwkTkXVFNiX3/fpC6afS8Dhb/gz7R7eg==", - "dev": true - }, - "yargs-parser": { - "version": "20.2.9", - "resolved": "https://registry.npmjs.org/yargs-parser/-/yargs-parser-20.2.9.tgz", - "integrity": "sha512-y11nGElTIV+CT3Zv9t7VKl+Q3hTQoT9a1Qzezhhl6Rp21gJ/IVTW7Z3y9EWXhuUBC2Shnf+DX0antecpAwSP8w==", - "dev": true - } - } - }, - "release-zalgo": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/release-zalgo/-/release-zalgo-1.0.0.tgz", - "integrity": "sha1-CXALflB0Mpc5Mw5TXFqQ+2eFFzA=", - "dev": true, - "requires": { - "es6-error": "^4.0.1" - } - }, - "require-directory": { - "version": "2.1.1", - "resolved": "https://registry.npmjs.org/require-directory/-/require-directory-2.1.1.tgz", - "integrity": "sha1-jGStX9MNqxyXbiNE/+f3kqam30I=", - "dev": true - }, - "require-main-filename": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/require-main-filename/-/require-main-filename-2.0.0.tgz", - "integrity": "sha512-NKN5kMDylKuldxYLSUfrbo5Tuzh4hd+2E8NPPX02mZtn1VuREQToYe/ZdlJy+J3uCpfaiGF05e7B8W0iXbQHmg==", - "dev": true - }, - "resolve": { - "version": "1.20.0", - "resolved": "https://registry.npmjs.org/resolve/-/resolve-1.20.0.tgz", - "integrity": "sha512-wENBPt4ySzg4ybFQW2TT1zMQucPK95HSh/nq2CFTZVOGut2+pQvSsgtda4d26YrYcr067wjbmzOG8byDPBX63A==", - "dev": true, - "requires": { - "is-core-module": "^2.2.0", - "path-parse": "^1.0.6" - } - }, - "resolve-alpn": { - "version": "1.2.1", - "resolved": "https://registry.npmjs.org/resolve-alpn/-/resolve-alpn-1.2.1.tgz", - "integrity": "sha512-0a1F4l73/ZFZOakJnQ3FvkJ2+gSTQWz/r2KE5OdDY0TxPm5h4GkqkWWfM47T7HsbnOtcJVEF4epCVy6u7Q3K+g==", - "dev": true - }, - "resolve-from": { - "version": "4.0.0", - "resolved": "https://registry.npmjs.org/resolve-from/-/resolve-from-4.0.0.tgz", - "integrity": "sha512-pb/MYmXstAkysRFx8piNI1tGFNQIFA3vkE3Gq4EuA1dF6gHp/+vgZqsCGJapvy8N3Q+4o7FwvquPJcnZ7RYy4g==", - "dev": true - }, - "responselike": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/responselike/-/responselike-2.0.0.tgz", - "integrity": "sha512-xH48u3FTB9VsZw7R+vvgaKeLKzT6jOogbQhEe/jewwnZgzPcnyWui2Av6JpoYZF/91uueC+lqhWqeURw5/qhCw==", - "dev": true, - "requires": { - "lowercase-keys": "^2.0.0" - } - }, - "restore-cursor": { - "version": "3.1.0", - "resolved": "https://registry.npmjs.org/restore-cursor/-/restore-cursor-3.1.0.tgz", - "integrity": "sha512-l+sSefzHpj5qimhFSE5a8nufZYAM3sBSVMAPtYkmC+4EH2anSGaEMXSD0izRQbu9nfyQ9y5JrVmp7E8oZrUjvA==", - "dev": true, - "requires": { - "onetime": "^5.1.0", - "signal-exit": "^3.0.2" - } - }, - "retry": { - "version": "0.13.1", - "resolved": "https://registry.npmjs.org/retry/-/retry-0.13.1.tgz", - "integrity": "sha512-XQBQ3I8W1Cge0Seh+6gjj03LbmRFWuoszgK9ooCpwYIrhhoO80pfq4cUkU5DkknwfOfFteRwlZ56PYOGYyFWdg==", - "dev": true - }, - "reusify": { - "version": "1.0.4", - "resolved": "https://registry.npmjs.org/reusify/-/reusify-1.0.4.tgz", - "integrity": "sha512-U9nH88a3fc/ekCF1l0/UP1IosiuIjyTh7hBvXVMHYgVcfGvt897Xguj2UOLDeI5BG2m7/uwyaLVT6fbtCwTyzw==", - "dev": true - }, - "rimraf": { - "version": "3.0.2", - "resolved": "https://registry.npmjs.org/rimraf/-/rimraf-3.0.2.tgz", - "integrity": "sha512-JZkJMZkAGFFPP2YqXZXPbMlMBgsxzE8ILs4lMIX/2o0L9UBw9O/Y3o6wFw/i9YLapcUJWwqbi3kdxIPdC62TIA==", - "dev": true, - "requires": { - "glob": "^7.1.3" - } - }, - "run-async": { - "version": "2.4.1", - "resolved": "https://registry.npmjs.org/run-async/-/run-async-2.4.1.tgz", - "integrity": "sha512-tvVnVv01b8c1RrA6Ep7JkStj85Guv/YrMcwqYQnwjsAS2cTmmPGBBjAjpCW7RrSodNSoE2/qg9O4bceNvUuDgQ==", - "dev": true - }, - "run-parallel": { - "version": "1.2.0", - "resolved": "https://registry.npmjs.org/run-parallel/-/run-parallel-1.2.0.tgz", - "integrity": "sha512-5l4VyZR86LZ/lDxZTR6jqL8AFE2S0IFLMP26AbjsLVADxHdhB/c0GUsH+y39UfCi3dzz8OlQuPmnaJOMoDHQBA==", - "dev": true, - "requires": { - "queue-microtask": "^1.2.2" - } - }, - "rxjs": { - "version": "7.4.0", - "resolved": "https://registry.npmjs.org/rxjs/-/rxjs-7.4.0.tgz", - "integrity": "sha512-7SQDi7xeTMCJpqViXh8gL/lebcwlp3d831F05+9B44A4B0WfsEwUQHR64gsH1kvJ+Ep/J9K2+n1hVl1CsGN23w==", - "dev": true, - "requires": { - "tslib": "~2.1.0" - } - }, - "safe-buffer": { - "version": "5.1.2", - "resolved": "https://registry.npmjs.org/safe-buffer/-/safe-buffer-5.1.2.tgz", - "integrity": "sha512-Gd2UZBJDkXlY7GbJxfsE8/nvKkUEU1G38c1siN6QP6a9PT9MmHB8GnpscSmMJSoF8LOIrt8ud/wPtojys4G6+g==", - "dev": true - }, - "safer-buffer": { - "version": "2.1.2", - "resolved": "https://registry.npmjs.org/safer-buffer/-/safer-buffer-2.1.2.tgz", - "integrity": "sha512-YZo3K82SD7Riyi0E1EQPojLz7kpepnSQI9IyPbHHg1XXXevb5dJI7tpyN2ADxGcQbHG7vcyRHk0cbwqcQriUtg==", - "dev": true - }, - "semver": { - "version": "7.3.5", - "resolved": "https://registry.npmjs.org/semver/-/semver-7.3.5.tgz", - "integrity": "sha512-PoeGJYh8HK4BTO/a9Tf6ZG3veo/A7ZVsYrSA6J8ny9nb3B1VrpkuN+z9OE5wfE5p6H4LchYZsegiQgbJD94ZFQ==", - "dev": true, - "requires": { - "lru-cache": "^6.0.0" - } - }, - "semver-diff": { - "version": "3.1.1", - "resolved": "https://registry.npmjs.org/semver-diff/-/semver-diff-3.1.1.tgz", - "integrity": "sha512-GX0Ix/CJcHyB8c4ykpHGIAvLyOwOobtM/8d+TQkAd81/bEjgPHrfba41Vpesr7jX/t8Uh+R3EX9eAS5be+jQYg==", - "dev": true, - "requires": { - "semver": "^6.3.0" - }, - "dependencies": { - "semver": { - "version": "6.3.0", - "resolved": "https://registry.npmjs.org/semver/-/semver-6.3.0.tgz", - "integrity": "sha512-b39TBaTSfV6yBrapU89p5fKekE2m/NwnDocOVruQFS1/veMgdzuPcnOM34M6CwxW8jH/lxEa5rBoDeUwu5HHTw==", - "dev": true - } - } - }, - "serialize-javascript": { - "version": "6.0.0", - "resolved": "https://registry.npmjs.org/serialize-javascript/-/serialize-javascript-6.0.0.tgz", - "integrity": "sha512-Qr3TosvguFt8ePWqsvRfrKyQXIiW+nGbYpy8XK24NQHE83caxWt+mIymTT19DGFbNWNLfEwsrkSmN64lVWB9ag==", - "dev": true, - "requires": { - "randombytes": "^2.1.0" - } - }, - "set-blocking": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/set-blocking/-/set-blocking-2.0.0.tgz", - "integrity": "sha1-BF+XgtARrppoA93TgrJDkrPYkPc=", - "dev": true - }, - "shebang-command": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/shebang-command/-/shebang-command-2.0.0.tgz", - "integrity": "sha512-kHxr2zZpYtdmrN1qDjrrX/Z1rR1kG8Dx+gkpK1G4eXmvXswmcE1hTWBWYUzlraYw1/yZp6YuDY77YtvbN0dmDA==", - "dev": true, - "requires": { - "shebang-regex": "^3.0.0" - } - }, - "shebang-regex": { - "version": "3.0.0", - "resolved": "https://registry.npmjs.org/shebang-regex/-/shebang-regex-3.0.0.tgz", - "integrity": "sha512-7++dFhtcx3353uBaq8DDR4NuxBetBzC7ZQOhmTQInHEd6bSrXdiEyzCvG07Z44UYdLShWUyXt5M/yhz8ekcb1A==", - "dev": true - }, - "shelljs": { - "version": "0.8.4", - "resolved": "https://registry.npmjs.org/shelljs/-/shelljs-0.8.4.tgz", - "integrity": "sha512-7gk3UZ9kOfPLIAbslLzyWeGiEqx9e3rxwZM0KE6EL8GlGwjym9Mrlx5/p33bWTu9YG6vcS4MBxYZDHYr5lr8BQ==", - "dev": true, - "requires": { - "glob": "^7.0.0", - "interpret": "^1.0.0", - "rechoir": "^0.6.2" - } - }, - "shiki": { - "version": "0.9.12", - "resolved": "https://registry.npmjs.org/shiki/-/shiki-0.9.12.tgz", - "integrity": "sha512-VXcROdldv0/Qu0w2XvzU4IrvTeBNs/Kj/FCmtcEXGz7Tic/veQzliJj6tEiAgoKianhQstpYmbPDStHU5Opqcw==", - "dev": true, - "requires": { - "jsonc-parser": "^3.0.0", - "onigasm": "^2.2.5", - "vscode-textmate": "5.2.0" - } - }, - "side-channel": { - "version": "1.0.4", - "resolved": "https://registry.npmjs.org/side-channel/-/side-channel-1.0.4.tgz", - "integrity": "sha512-q5XPytqFEIKHkGdiMIrY10mvLRvnQh42/+GoBlFW3b2LXLE2xxJpZFdm94we0BaoV3RwJyGqg5wS7epxTv0Zvw==", - "dev": true, - "requires": { - "call-bind": "^1.0.0", - "get-intrinsic": "^1.0.2", - "object-inspect": "^1.9.0" - } - }, - "signal-exit": { - "version": "3.0.5", - "resolved": "https://registry.npmjs.org/signal-exit/-/signal-exit-3.0.5.tgz", - "integrity": "sha512-KWcOiKeQj6ZyXx7zq4YxSMgHRlod4czeBQZrPb8OKcohcqAXShm7E20kEMle9WBt26hFcAf0qLOcp5zmY7kOqQ==", - "dev": true - }, - "sinon": { - "version": "11.1.2", - "resolved": "https://registry.npmjs.org/sinon/-/sinon-11.1.2.tgz", - "integrity": "sha512-59237HChms4kg7/sXhiRcUzdSkKuydDeTiamT/jesUVHshBgL8XAmhgFo0GfK6RruMDM/iRSij1EybmMog9cJw==", - "dev": true, - "requires": { - "@sinonjs/commons": "^1.8.3", - "@sinonjs/fake-timers": "^7.1.2", - "@sinonjs/samsam": "^6.0.2", - "diff": "^5.0.0", - "nise": "^5.1.0", - "supports-color": "^7.2.0" - } - }, - "slash": { - "version": "3.0.0", - "resolved": "https://registry.npmjs.org/slash/-/slash-3.0.0.tgz", - "integrity": "sha512-g9Q1haeby36OSStwb4ntCGGGaKsaVSjQ68fBxoQcutl5fS1vuY18H3wSt3jFyFtrkx+Kz0V1G85A4MyAdDMi2Q==", - "dev": true - }, - "source-map": { - "version": "0.5.7", - "resolved": "https://registry.npmjs.org/source-map/-/source-map-0.5.7.tgz", - "integrity": "sha1-igOdLRAh0i0eoUyA2OpGi6LvP8w=", - "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==", - "dev": true, - "requires": { - "buffer-from": "^1.0.0", - "source-map": "^0.6.0" - }, - "dependencies": { - "source-map": { - "version": "0.6.1", - "resolved": "https://registry.npmjs.org/source-map/-/source-map-0.6.1.tgz", - "integrity": "sha512-UjgapumWlbMhkBgzT7Ykc5YXUT46F0iKu8SGXq0bcwP5dz/h0Plj6enJqjz1Zbq2l5WaqYnrVbwWOWMyF3F47g==", - "dev": true - } - } - }, - "spawn-wrap": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/spawn-wrap/-/spawn-wrap-2.0.0.tgz", - "integrity": "sha512-EeajNjfN9zMnULLwhZZQU3GWBoFNkbngTUPfaawT4RkMiviTxcX0qfhVbGey39mfctfDHkWtuecgQ8NJcyQWHg==", - "dev": true, - "requires": { - "foreground-child": "^2.0.0", - "is-windows": "^1.0.2", - "make-dir": "^3.0.0", - "rimraf": "^3.0.0", - "signal-exit": "^3.0.2", - "which": "^2.0.1" - } - }, - "split-on-first": { - "version": "1.1.0", - "resolved": "https://registry.npmjs.org/split-on-first/-/split-on-first-1.1.0.tgz", - "integrity": "sha512-43ZssAJaMusuKWL8sKUBQXHWOpq8d6CfN/u1p4gUzfJkM05C8rxTmYrkIPTXapZpORA6LkkzcUulJ8FqA7Uudw==", - "dev": true - }, - "sprintf-js": { - "version": "1.0.3", - "resolved": "https://registry.npmjs.org/sprintf-js/-/sprintf-js-1.0.3.tgz", - "integrity": "sha1-BOaSb2YolTVPPdAVIDYzuFcpfiw=", - "dev": true - }, - "strict-uri-encode": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/strict-uri-encode/-/strict-uri-encode-2.0.0.tgz", - "integrity": "sha1-ucczDHBChi9rFC3CdLvMWGbONUY=", - "dev": true - }, - "string_decoder": { - "version": "1.3.0", - "resolved": "https://registry.npmjs.org/string_decoder/-/string_decoder-1.3.0.tgz", - "integrity": "sha512-hkRX8U1WjJFd8LsDJ2yQ/wWWxaopEsABU1XfkM8A+j0+85JAGppt16cr1Whg6KIbb4okU6Mql6BOj+uup/wKeA==", - "dev": true, - "requires": { - "safe-buffer": "~5.2.0" - }, - "dependencies": { - "safe-buffer": { - "version": "5.2.1", - "resolved": "https://registry.npmjs.org/safe-buffer/-/safe-buffer-5.2.1.tgz", - "integrity": "sha512-rp3So07KcdmmKbGvgaNxQSJr7bGVSVk5S9Eq1F+ppbRo70+YeaDxkw5Dd8NPN+GD6bjnYm2VuPuCXmpuYvmCXQ==", - "dev": true - } - } - }, - "string-width": { - "version": "4.2.3", - "resolved": "https://registry.npmjs.org/string-width/-/string-width-4.2.3.tgz", - "integrity": "sha512-wKyQRQpjJ0sIp62ErSZdGsjMJWsap5oRNihHhu6G7JVO/9jIB6UyevL+tXuOqrng8j/cxKTWyWUwvSTriiZz/g==", - "dev": true, - "requires": { - "emoji-regex": "^8.0.0", - "is-fullwidth-code-point": "^3.0.0", - "strip-ansi": "^6.0.1" - } - }, - "strip-ansi": { - "version": "6.0.1", - "resolved": "https://registry.npmjs.org/strip-ansi/-/strip-ansi-6.0.1.tgz", - "integrity": "sha512-Y38VPSHcqkFrCpFnQ9vuSXmquuv5oXOKpGeT6aGrr3o3Gc9AlVa6JBfUSOCnbxGGZF+/0ooI7KrPuUSztUdU5A==", - "dev": true, - "requires": { - "ansi-regex": "^5.0.1" - } - }, - "strip-bom": { - "version": "4.0.0", - "resolved": "https://registry.npmjs.org/strip-bom/-/strip-bom-4.0.0.tgz", - "integrity": "sha512-3xurFv5tEgii33Zi8Jtp55wEIILR9eh34FAW00PZf+JnSsTmV/ioewSgQl97JHvgjoRGwPShsWm+IdrxB35d0w==", - "dev": true - }, - "strip-final-newline": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/strip-final-newline/-/strip-final-newline-2.0.0.tgz", - "integrity": "sha512-BrpvfNAE3dcvq7ll3xVumzjKjZQ5tI1sEUIKr3Uoks0XUl45St3FlatVqef9prk4jRDzhW6WZg+3bk93y6pLjA==", - "dev": true - }, - "strip-json-comments": { - "version": "3.1.1", - "resolved": "https://registry.npmjs.org/strip-json-comments/-/strip-json-comments-3.1.1.tgz", - "integrity": "sha512-6fPc+R4ihwqP6N/aIv2f1gMH8lOVtWQHoqC4yK6oSDVVocumAsfCqjkXnqiYMhmMwS/mEHLp7Vehlt3ql6lEig==", - "dev": true - }, - "supports-color": { - "version": "7.2.0", - "resolved": "https://registry.npmjs.org/supports-color/-/supports-color-7.2.0.tgz", - "integrity": "sha512-qpCAvRl9stuOHveKsn7HncJRvv501qIacKzQlO/+Lwxc9+0q2wLyv4Dfvt80/DPn2pqOBsJdDiogXGR9+OvwRw==", - "dev": true, - "requires": { - "has-flag": "^4.0.0" - } - }, - "test-exclude": { - "version": "6.0.0", - "resolved": "https://registry.npmjs.org/test-exclude/-/test-exclude-6.0.0.tgz", - "integrity": "sha512-cAGWPIyOHU6zlmg88jwm7VRyXnMN7iV68OGAbYDk/Mh/xC/pzVPlQtY6ngoIH/5/tciuhGfvESU8GrHrcxD56w==", - "dev": true, - "requires": { - "@istanbuljs/schema": "^0.1.2", - "glob": "^7.1.4", - "minimatch": "^3.0.4" - } - }, - "text-table": { - "version": "0.2.0", - "resolved": "https://registry.npmjs.org/text-table/-/text-table-0.2.0.tgz", - "integrity": "sha1-f17oI66AUgfACvLfSoTsP8+lcLQ=", - "dev": true - }, - "through": { - "version": "2.3.8", - "resolved": "https://registry.npmjs.org/through/-/through-2.3.8.tgz", - "integrity": "sha1-DdTJ/6q8NXlgsbckEV1+Doai4fU=", - "dev": true - }, - "tmp": { - "version": "0.0.33", - "resolved": "https://registry.npmjs.org/tmp/-/tmp-0.0.33.tgz", - "integrity": "sha512-jRCJlojKnZ3addtTOjdIqoRuPEKBvNXcGYqzO6zWZX8KfKEpnGY5jfggJQ3EjKuu8D4bJRr0y+cYJFmYbImXGw==", - "dev": true, - "requires": { - "os-tmpdir": "~1.0.2" - } - }, - "to-fast-properties": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/to-fast-properties/-/to-fast-properties-2.0.0.tgz", - "integrity": "sha1-3F5pjL0HkmW8c+A3doGk5Og/YW4=", - "dev": true - }, - "to-readable-stream": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/to-readable-stream/-/to-readable-stream-1.0.0.tgz", - "integrity": "sha512-Iq25XBt6zD5npPhlLVXGFN3/gyR2/qODcKNNyTMd4vbm39HUaOiAM4PMq0eMVC/Tkxz+Zjdsc55g9yyz+Yq00Q==", - "dev": true - }, - "to-regex-range": { - "version": "5.0.1", - "resolved": "https://registry.npmjs.org/to-regex-range/-/to-regex-range-5.0.1.tgz", - "integrity": "sha512-65P7iz6X5yEr1cwcgvQxbbIw7Uk3gOy5dIdtZ4rDveLqhrdJP+Li/Hx6tyK0NEb+2GCyneCMJiGqrADCSNk8sQ==", - "dev": true, - "requires": { - "is-number": "^7.0.0" - } - }, - "tr46": { - "version": "0.0.3", - "resolved": "https://registry.npmjs.org/tr46/-/tr46-0.0.3.tgz", - "integrity": "sha1-gYT9NH2snNwYWZLzpmIuFLnZq2o=", - "dev": true - }, - "ts-node": { - "version": "10.4.0", - "resolved": "https://registry.npmjs.org/ts-node/-/ts-node-10.4.0.tgz", - "integrity": "sha512-g0FlPvvCXSIO1JDF6S232P5jPYqBkRL9qly81ZgAOSU7rwI0stphCgd2kLiCrU9DjQCrJMWEqcNSjQL02s6d8A==", - "dev": true, - "requires": { - "@cspotcode/source-map-support": "0.7.0", - "@tsconfig/node10": "^1.0.7", - "@tsconfig/node12": "^1.0.7", - "@tsconfig/node14": "^1.0.0", - "@tsconfig/node16": "^1.0.2", - "acorn": "^8.4.1", - "acorn-walk": "^8.1.1", - "arg": "^4.1.0", - "create-require": "^1.1.0", - "diff": "^4.0.1", - "make-error": "^1.1.1", - "yn": "3.1.1" - }, - "dependencies": { - "diff": { - "version": "4.0.2", - "resolved": "https://registry.npmjs.org/diff/-/diff-4.0.2.tgz", - "integrity": "sha512-58lmxKSA4BNyLz+HHMUzlOEpg09FV+ev6ZMe3vJihgdxzgcwZ8VoEEPmALCZG9LmqfVoNMMKpttIYTVG6uDY7A==", - "dev": true - } - } - }, - "tslib": { - "version": "2.1.0", - "resolved": "https://registry.npmjs.org/tslib/-/tslib-2.1.0.tgz", - "integrity": "sha512-hcVC3wYEziELGGmEEXue7D75zbwIIVUMWAVbHItGPx0ziyXxrOMQx4rQEVEV45Ut/1IotuEvwqPopzIOkDMf0A==", - "dev": true - }, - "tsutils": { - "version": "3.21.0", - "resolved": "https://registry.npmjs.org/tsutils/-/tsutils-3.21.0.tgz", - "integrity": "sha512-mHKK3iUXL+3UF6xL5k0PEhKRUBKPBCv/+RkEOpjRWxxx27KKRBmmA60A9pgOUvMi8GKhRMPEmjBRPzs2W7O1OA==", - "dev": true, - "requires": { - "tslib": "^1.8.1" - }, - "dependencies": { - "tslib": { - "version": "1.14.1", - "resolved": "https://registry.npmjs.org/tslib/-/tslib-1.14.1.tgz", - "integrity": "sha512-Xni35NKzjgMrwevysHTCArtLDpPvye8zV/0E4EyYn43P7/7qvQwPh9BGkHewbMulVntbigmcT7rdX3BNo9wRJg==", - "dev": true - } - } - }, - "type-check": { - "version": "0.4.0", - "resolved": "https://registry.npmjs.org/type-check/-/type-check-0.4.0.tgz", - "integrity": "sha512-XleUoc9uwGXqjWwXaUTZAmzMcFZ5858QA2vvx1Ur5xIcixXIP+8LnFDgRplU30us6teqdlskFfu+ae4K79Ooew==", - "dev": true, - "requires": { - "prelude-ls": "^1.2.1" - } - }, - "type-detect": { - "version": "4.0.8", - "resolved": "https://registry.npmjs.org/type-detect/-/type-detect-4.0.8.tgz", - "integrity": "sha512-0fr/mIH1dlO+x7TlcMy+bIDqKPsw/70tVyeHW787goQjhmqaZe10uwLujubK9q9Lg6Fiho1KUKDYz0Z7k7g5/g==", - "dev": true - }, - "type-fest": { - "version": "0.20.2", - "resolved": "https://registry.npmjs.org/type-fest/-/type-fest-0.20.2.tgz", - "integrity": "sha512-Ne+eE4r0/iWnpAxD852z3A+N0Bt5RN//NjJwRd2VFHEmrywxf5vsZlh4R6lixl6B+wz/8d+maTSAkN1FIkI3LQ==", - "dev": true - }, - "typedarray-to-buffer": { - "version": "3.1.5", - "resolved": "https://registry.npmjs.org/typedarray-to-buffer/-/typedarray-to-buffer-3.1.5.tgz", - "integrity": "sha512-zdu8XMNEDepKKR+XYOXAVPtWui0ly0NtohUscw+UmaHiAWT8hrV1rr//H6V+0DvJ3OQ19S979M0laLfX8rm82Q==", - "dev": true, - "requires": { - "is-typedarray": "^1.0.0" - } - }, - "typedoc": { - "version": "0.22.8", - "resolved": "https://registry.npmjs.org/typedoc/-/typedoc-0.22.8.tgz", - "integrity": "sha512-92S+YzyhospdXN5rnkYUTgirdTYqNWY7NP9vco+IqQQoiSXzVSUsawVro+tMyEEsWUS7EMaJ2YOjB9uE0CBi6A==", - "dev": true, - "requires": { - "glob": "^7.2.0", - "lunr": "^2.3.9", - "marked": "^3.0.8", - "minimatch": "^3.0.4", - "shiki": "^0.9.12" - }, - "dependencies": { - "glob": { - "version": "7.2.0", - "resolved": "https://registry.npmjs.org/glob/-/glob-7.2.0.tgz", - "integrity": "sha512-lmLf6gtyrPq8tTjSmrO94wBeQbFR3HbLHbuyD69wuyQkImp2hWqMGB47OX65FBkPffO641IP9jWa1z4ivqG26Q==", - "dev": true, - "requires": { - "fs.realpath": "^1.0.0", - "inflight": "^1.0.4", - "inherits": "2", - "minimatch": "^3.0.4", - "once": "^1.3.0", - "path-is-absolute": "^1.0.0" - } - } - } - }, - "typedoc-github-wiki-theme": { - "version": "0.6.0", - "resolved": "https://registry.npmjs.org/typedoc-github-wiki-theme/-/typedoc-github-wiki-theme-0.6.0.tgz", - "integrity": "sha512-uHhR7PwAZ4JgO0KzlLocWSvMqKsSZ/wxUQYGKskIepzsotPAQcAWnSSnGi6gdkSw8UAfIIppdD7H1AmI39962w==", - "dev": true, - "requires": {} - }, - "typedoc-plugin-markdown": { - "version": "3.11.3", - "resolved": "https://registry.npmjs.org/typedoc-plugin-markdown/-/typedoc-plugin-markdown-3.11.3.tgz", - "integrity": "sha512-rWiHbEIe0oZetDIsBR24XJVxGOJ91kDcHoj2KhFKxCLoJGX659EKBQkHne9QJ4W2stGhu1fRgFyQaouSBnxukA==", - "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==", - "dev": true - }, - "uglify-js": { - "version": "3.14.3", - "resolved": "https://registry.npmjs.org/uglify-js/-/uglify-js-3.14.3.tgz", - "integrity": "sha512-mic3aOdiq01DuSVx0TseaEzMIVqebMZ0Z3vaeDhFEh9bsc24hV1TFvN74reA2vs08D0ZWfNjAcJ3UbVLaBss+g==", - "dev": true, - "optional": true - }, - "unique-string": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/unique-string/-/unique-string-2.0.0.tgz", - "integrity": "sha512-uNaeirEPvpZWSgzwsPGtU2zVSTrn/8L5q/IexZmH0eH6SA73CmAA5U4GwORTxQAZs95TAXLNqeLoPPNO5gZfWg==", - "dev": true, - "requires": { - "crypto-random-string": "^2.0.0" - } - }, - "universal-user-agent": { - "version": "6.0.0", - "resolved": "https://registry.npmjs.org/universal-user-agent/-/universal-user-agent-6.0.0.tgz", - "integrity": "sha512-isyNax3wXoKaulPDZWHQqbmIx1k2tb9fb3GGDBRxCscfYV2Ch7WxPArBsFEG8s/safwXTT7H4QGhaIkTp9447w==", - "dev": true - }, - "update-notifier": { - "version": "5.1.0", - "resolved": "https://registry.npmjs.org/update-notifier/-/update-notifier-5.1.0.tgz", - "integrity": "sha512-ItnICHbeMh9GqUy31hFPrD1kcuZ3rpxDZbf4KUDavXwS0bW5m7SLbDQpGX3UYr072cbrF5hFUs3r5tUsPwjfHw==", - "dev": true, - "requires": { - "boxen": "^5.0.0", - "chalk": "^4.1.0", - "configstore": "^5.0.1", - "has-yarn": "^2.1.0", - "import-lazy": "^2.1.0", - "is-ci": "^2.0.0", - "is-installed-globally": "^0.4.0", - "is-npm": "^5.0.0", - "is-yarn-global": "^0.3.0", - "latest-version": "^5.1.0", - "pupa": "^2.1.1", - "semver": "^7.3.4", - "semver-diff": "^3.1.1", - "xdg-basedir": "^4.0.0" - }, - "dependencies": { - "ci-info": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/ci-info/-/ci-info-2.0.0.tgz", - "integrity": "sha512-5tK7EtrZ0N+OLFMthtqOj4fI2Jeb88C4CAZPu25LDVUgXJ0A3Js4PMGqrn0JU1W0Mh1/Z8wZzYPxqUrXeBboCQ==", - "dev": true - }, - "is-ci": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/is-ci/-/is-ci-2.0.0.tgz", - "integrity": "sha512-YfJT7rkpQB0updsdHLGWrvhBJfcfzNNawYDNIyQXJz0IViGf75O8EBPKSdvw2rF+LGCsX4FZ8tcr3b19LcZq4w==", - "dev": true, - "requires": { - "ci-info": "^2.0.0" - } - } - } - }, - "uri-js": { - "version": "4.4.1", - "resolved": "https://registry.npmjs.org/uri-js/-/uri-js-4.4.1.tgz", - "integrity": "sha512-7rKUyy33Q1yc98pQ1DAmLtwX109F7TIfWlW1Ydo8Wl1ii1SeHieeh0HHfPeL2fMXK6z0s8ecKs9frCuLJvndBg==", - "dev": true, - "requires": { - "punycode": "^2.1.0" - } - }, - "url-join": { - "version": "4.0.1", - "resolved": "https://registry.npmjs.org/url-join/-/url-join-4.0.1.tgz", - "integrity": "sha512-jk1+QP6ZJqyOiuEI9AEWQfju/nB2Pw466kbA0LEZljHwKeMgd9WrAEgEGxjPDD2+TNbbb37rTyhEfrCXfuKXnA==", - "dev": true - }, - "url-parse-lax": { - "version": "3.0.0", - "resolved": "https://registry.npmjs.org/url-parse-lax/-/url-parse-lax-3.0.0.tgz", - "integrity": "sha1-FrXK/Afb42dsGxmZF3gj1lA6yww=", - "dev": true, - "requires": { - "prepend-http": "^2.0.0" - } - }, - "util-deprecate": { - "version": "1.0.2", - "resolved": "https://registry.npmjs.org/util-deprecate/-/util-deprecate-1.0.2.tgz", - "integrity": "sha1-RQ1Nyfpw3nMnYvvS1KKJgUGaDM8=", - "dev": true - }, - "uuid": { - "version": "3.4.0", - "resolved": "https://registry.npmjs.org/uuid/-/uuid-3.4.0.tgz", - "integrity": "sha512-HjSDRw6gZE5JMggctHBcjVak08+KEVhSIiDzFnT9S9aegmp85S/bReBVTb4QTFaRNptJ9kuYaNhnbNEOkbKb/A==", - "dev": true - }, - "v8-compile-cache": { - "version": "2.3.0", - "resolved": "https://registry.npmjs.org/v8-compile-cache/-/v8-compile-cache-2.3.0.tgz", - "integrity": "sha512-l8lCEmLcLYZh4nbunNZvQCJc5pv7+RCwa8q/LdUx8u7lsWvPDKmpodJAJNwkAhJC//dFY48KuIEmjtd4RViDrA==", - "dev": true - }, - "vscode-textmate": { - "version": "5.2.0", - "resolved": "https://registry.npmjs.org/vscode-textmate/-/vscode-textmate-5.2.0.tgz", - "integrity": "sha512-Uw5ooOQxRASHgu6C7GVvUxisKXfSgW4oFlO+aa+PAkgmH89O3CXxEEzNRNtHSqtXFTl0nAC1uYj0GMSH27uwtQ==", - "dev": true - }, - "wcwidth": { - "version": "1.0.1", - "resolved": "https://registry.npmjs.org/wcwidth/-/wcwidth-1.0.1.tgz", - "integrity": "sha1-8LDc+RW8X/FSivrbLA4XtTLaL+g=", - "dev": true, - "requires": { - "defaults": "^1.0.3" - } - }, - "webidl-conversions": { - "version": "3.0.1", - "resolved": "https://registry.npmjs.org/webidl-conversions/-/webidl-conversions-3.0.1.tgz", - "integrity": "sha1-JFNCdeKnvGvnvIZhHMFq4KVlSHE=", - "dev": true - }, - "whatwg-url": { - "version": "5.0.0", - "resolved": "https://registry.npmjs.org/whatwg-url/-/whatwg-url-5.0.0.tgz", - "integrity": "sha1-lmRU6HZUYuN2RNNib2dCzotwll0=", - "dev": true, - "requires": { - "tr46": "~0.0.3", - "webidl-conversions": "^3.0.0" - } - }, - "which": { - "version": "2.0.2", - "resolved": "https://registry.npmjs.org/which/-/which-2.0.2.tgz", - "integrity": "sha512-BLI3Tl1TW3Pvl70l3yq3Y64i+awpwXqsGBYWkkqMtnbXgrMD+yj7rhW0kuEDxzJaYXGjEW5ogapKNMEKNMjibA==", - "dev": true, - "requires": { - "isexe": "^2.0.0" - } - }, - "which-module": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/which-module/-/which-module-2.0.0.tgz", - "integrity": "sha1-2e8H3Od7mQK4o6j6SzHD4/fm6Ho=", - "dev": true - }, - "widest-line": { - "version": "3.1.0", - "resolved": "https://registry.npmjs.org/widest-line/-/widest-line-3.1.0.tgz", - "integrity": "sha512-NsmoXalsWVDMGupxZ5R08ka9flZjjiLvHVAWYOKtiKM8ujtZWr9cRffak+uSE48+Ob8ObalXpwyeUiyDD6QFgg==", - "dev": true, - "requires": { - "string-width": "^4.0.0" - } - }, - "windows-release": { - "version": "4.0.0", - "resolved": "https://registry.npmjs.org/windows-release/-/windows-release-4.0.0.tgz", - "integrity": "sha512-OxmV4wzDKB1x7AZaZgXMVsdJ1qER1ed83ZrTYd5Bwq2HfJVg3DJS8nqlAG4sMoJ7mu8cuRmLEYyU13BKwctRAg==", - "dev": true, - "requires": { - "execa": "^4.0.2" - }, - "dependencies": { - "execa": { - "version": "4.1.0", - "resolved": "https://registry.npmjs.org/execa/-/execa-4.1.0.tgz", - "integrity": "sha512-j5W0//W7f8UxAn8hXVnwG8tLwdiUy4FJLcSupCg6maBYZDpyBvTApK7KyuI4bKj8KOh1r2YH+6ucuYtJv1bTZA==", - "dev": true, - "requires": { - "cross-spawn": "^7.0.0", - "get-stream": "^5.0.0", - "human-signals": "^1.1.1", - "is-stream": "^2.0.0", - "merge-stream": "^2.0.0", - "npm-run-path": "^4.0.0", - "onetime": "^5.1.0", - "signal-exit": "^3.0.2", - "strip-final-newline": "^2.0.0" - } - }, - "get-stream": { - "version": "5.2.0", - "resolved": "https://registry.npmjs.org/get-stream/-/get-stream-5.2.0.tgz", - "integrity": "sha512-nBF+F1rAZVCu/p7rjzgA+Yb4lfYXrpl7a6VmJrU8wF9I1CKvP/QwPNZHnOlwbTkY6dvtFIzFMSyQXbLoTQPRpA==", - "dev": true, - "requires": { - "pump": "^3.0.0" - } - }, - "human-signals": { - "version": "1.1.1", - "resolved": "https://registry.npmjs.org/human-signals/-/human-signals-1.1.1.tgz", - "integrity": "sha512-SEQu7vl8KjNL2eoGBLF3+wAjpsNfA9XMlXAYj/3EdaNfAlxKthD1xjEQfGOUhllCGGJVNY34bRr6lPINhNjyZw==", - "dev": true - } - } - }, - "word-wrap": { - "version": "1.2.3", - "resolved": "https://registry.npmjs.org/word-wrap/-/word-wrap-1.2.3.tgz", - "integrity": "sha512-Hz/mrNwitNRh/HUAtM/VT/5VH+ygD6DV7mYKZAtHOrbs8U7lvPS6xf7EJKMF0uW1KJCl0H701g3ZGus+muE5vQ==", - "dev": true - }, - "wordwrap": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/wordwrap/-/wordwrap-1.0.0.tgz", - "integrity": "sha1-J1hIEIkUVqQXHI0CJkQa3pDLyus=", - "dev": true - }, - "workerpool": { - "version": "6.1.5", - "resolved": "https://registry.npmjs.org/workerpool/-/workerpool-6.1.5.tgz", - "integrity": "sha512-XdKkCK0Zqc6w3iTxLckiuJ81tiD/o5rBE/m+nXpRCB+/Sq4DqkfXZ/x0jW02DG1tGsfUGXbTJyZDP+eu67haSw==", - "dev": true - }, - "wrap-ansi": { - "version": "7.0.0", - "resolved": "https://registry.npmjs.org/wrap-ansi/-/wrap-ansi-7.0.0.tgz", - "integrity": "sha512-YVGIj2kamLSTxw6NsZjoBxfSwsn0ycdesmc4p+Q21c5zPuZ1pl+NfxVdxPtdHvmNVOQ6XSYG4AUtyt/Fi7D16Q==", - "dev": true, - "requires": { - "ansi-styles": "^4.0.0", - "string-width": "^4.1.0", - "strip-ansi": "^6.0.0" - } - }, - "wrappy": { - "version": "1.0.2", - "resolved": "https://registry.npmjs.org/wrappy/-/wrappy-1.0.2.tgz", - "integrity": "sha1-tSQ9jz7BqjXxNkYFvA0QNuMKtp8=", - "dev": true - }, - "write-file-atomic": { - "version": "3.0.3", - "resolved": "https://registry.npmjs.org/write-file-atomic/-/write-file-atomic-3.0.3.tgz", - "integrity": "sha512-AvHcyZ5JnSfq3ioSyjrBkH9yW4m7Ayk8/9My/DD9onKeu/94fwrMocemO2QAJFAlnnDN+ZDS+ZjAR5ua1/PV/Q==", - "dev": true, - "requires": { - "imurmurhash": "^0.1.4", - "is-typedarray": "^1.0.0", - "signal-exit": "^3.0.2", - "typedarray-to-buffer": "^3.1.5" - } - }, - "xdg-basedir": { - "version": "4.0.0", - "resolved": "https://registry.npmjs.org/xdg-basedir/-/xdg-basedir-4.0.0.tgz", - "integrity": "sha512-PSNhEJDejZYV7h50BohL09Er9VaIefr2LMAf3OEmpCkjOi34eYyQYAXUTjEQtZJTKcF0E2UKTh+osDLsgNim9Q==", - "dev": true - }, - "y18n": { - "version": "5.0.8", - "resolved": "https://registry.npmjs.org/y18n/-/y18n-5.0.8.tgz", - "integrity": "sha512-0pfFzegeDWJHJIAmTLRP2DwHjdF5s7jo9tuztdQxAhINCdvS+3nGINqPd00AphqJR/0LhANUS6/+7SCb98YOfA==", - "dev": true - }, - "yallist": { - "version": "4.0.0", - "resolved": "https://registry.npmjs.org/yallist/-/yallist-4.0.0.tgz", - "integrity": "sha512-3wdGidZyq5PB084XLES5TpOSRA3wjXAlIWMhum2kRcv/41Sn2emQ0dycQW4uZXLejwKvg6EsvbdlVL+FYEct7A==" - }, - "yaml": { - "version": "1.10.2", - "resolved": "https://registry.npmjs.org/yaml/-/yaml-1.10.2.tgz", - "integrity": "sha512-r3vXyErRCYJ7wg28yvBY5VSoAF8ZvlcW9/BwUzEtUsjvX/DKs24dIkuwjtuprwJJHsbyUbLApepYTR1BN4uHrg==", - "dev": true - }, - "yargs": { - "version": "17.2.1", - "resolved": "https://registry.npmjs.org/yargs/-/yargs-17.2.1.tgz", - "integrity": "sha512-XfR8du6ua4K6uLGm5S6fA+FIJom/MdJcFNVY8geLlp2v8GYbOXD4EB1tPNZsRn4vBzKGMgb5DRZMeWuFc2GO8Q==", - "dev": true, - "requires": { - "cliui": "^7.0.2", - "escalade": "^3.1.1", - "get-caller-file": "^2.0.5", - "require-directory": "^2.1.1", - "string-width": "^4.2.0", - "y18n": "^5.0.5", - "yargs-parser": "^20.2.2" - } - }, - "yargs-parser": { - "version": "20.2.4", - "resolved": "https://registry.npmjs.org/yargs-parser/-/yargs-parser-20.2.4.tgz", - "integrity": "sha512-WOkpgNhPTlE73h4VFAFsOnomJVaovO8VqLDzy5saChRBFQFBoMYirowyW+Q9HB4HFF4Z7VZTiG3iSzJJA29yRA==", - "dev": true - }, - "yargs-unparser": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/yargs-unparser/-/yargs-unparser-2.0.0.tgz", - "integrity": "sha512-7pRTIA9Qc1caZ0bZ6RYRGbHJthJWuakf+WmHK0rVeLkNrrGhfoabBNdue6kdINI6r4if7ocq9aD/n7xwKOdzOA==", - "dev": true, - "requires": { - "camelcase": "^6.0.0", - "decamelize": "^4.0.0", - "flat": "^5.0.2", - "is-plain-obj": "^2.1.0" - }, - "dependencies": { - "camelcase": { - "version": "6.2.0", - "resolved": "https://registry.npmjs.org/camelcase/-/camelcase-6.2.0.tgz", - "integrity": "sha512-c7wVvbw3f37nuobQNtgsgG9POC9qMbNuMQmTCqZv23b6MIz0fcYpBiOlv9gEN/hdLdnZTDQhg6e9Dq5M1vKvfg==", - "dev": true - }, - "decamelize": { - "version": "4.0.0", - "resolved": "https://registry.npmjs.org/decamelize/-/decamelize-4.0.0.tgz", - "integrity": "sha512-9iE1PgSik9HeIIw2JO94IidnE3eBoQrFJ3w7sFuzSX4DpmZ3v5sZpUiV5Swcf6mQEF+Y0ru8Neo+p+nyh2J+hQ==", - "dev": true - } - } - }, - "yn": { - "version": "3.1.1", - "resolved": "https://registry.npmjs.org/yn/-/yn-3.1.1.tgz", - "integrity": "sha512-Ux4ygGWsu2c7isFWe8Yu1YluJmqVhxqK2cLXNQA5AcC3QfbGNpM7fu0Y8b/z16pXLnFxZYvWhd3fhBY9DLmC6Q==", - "dev": true - }, - "yocto-queue": { - "version": "0.1.0", - "resolved": "https://registry.npmjs.org/yocto-queue/-/yocto-queue-0.1.0.tgz", - "integrity": "sha512-rVksvsnNCdJ/ohGc6xgPwyN8eheCxsiLM8mxuE/t/mOVqJewPuO1miLpTHQiRgTKCLexL4MeAFVagts7HmNZ2Q==", - "dev": true - } - } -} diff --git a/packages/all-in-one/index.ts b/packages/all-in-one/index.ts index b0cd5c1f8f7..7913630a199 100644 --- a/packages/all-in-one/index.ts +++ b/packages/all-in-one/index.ts @@ -4,6 +4,8 @@ import { RedisClientOptions, RedisClientType } from '@redis/client/dist/lib/clie import RedisJSON from '@redis/json'; import RediSearch from '@redis/search'; +export * from '@redis/search'; + const modules = { json: RedisJSON, ft: RediSearch @@ -17,3 +19,4 @@ export function createClient>( modules }); } + diff --git a/packages/all-in-one/package.json b/packages/all-in-one/package.json index 8fdb7149717..1fcaa1697d0 100644 --- a/packages/all-in-one/package.json +++ b/packages/all-in-one/package.json @@ -3,12 +3,12 @@ "version": "4.0.0-rc.3", "license": "MIT", "main": "./dist/index.js", - "types": "./dist/index.ts", + "types": "./dist/index.d.ts", "scripts": { "build": "tsc" }, "dependencies": { - "@redis/client": "^4.0.0-rc", + "@redis/client": "^1.0.0-rc", "@redis/json": "^1.0.0-rc", "@redis/search": "^1.0.0-rc" }, diff --git a/packages/client/LICENSE b/packages/client/LICENSE deleted file mode 100644 index db86cc4de7f..00000000000 --- a/packages/client/LICENSE +++ /dev/null @@ -1,24 +0,0 @@ -MIT License - -Copyright (c) 2016-present Node Redis contributors. - -Permission is hereby granted, free of charge, to any person -obtaining a copy of this software and associated documentation -files (the "Software"), to deal in the Software without -restriction, including without limitation the rights to use, -copy, modify, merge, publish, distribute, sublicense, and/or sell -copies of the Software, and to permit persons to whom the -Software is furnished to do so, subject to the following -conditions: - -The above copyright notice and this permission notice shall be -included in all copies or substantial portions of the Software. - -THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, -EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES -OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND -NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT -HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, -WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING -FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR -OTHER DEALINGS IN THE SOFTWARE. diff --git a/packages/client/README.md b/packages/client/README.md deleted file mode 100644 index 6b136bf48a5..00000000000 --- a/packages/client/README.md +++ /dev/null @@ -1,294 +0,0 @@ -

- - - -

Node Redis

-

- - - ---- - -## 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 { - 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. diff --git a/packages/client/examples/package-lock.json b/packages/client/examples/package-lock.json deleted file mode 100644 index e99b7b2184c..00000000000 --- a/packages/client/examples/package-lock.json +++ /dev/null @@ -1,87 +0,0 @@ -{ - "name": "node-redis-examples", - "version": "1.0.0", - "lockfileVersion": 2, - "requires": true, - "packages": { - "": { - "name": "node-redis-examples", - "version": "1.0.0", - "dependencies": { - "redis": "../" - } - }, - "..": { - "version": "4.0.0-rc.3", - "license": "MIT", - "dependencies": { - "cluster-key-slot": "1.1.0", - "generic-pool": "3.8.2", - "redis-parser": "3.0.0", - "yallist": "4.0.0" - }, - "devDependencies": { - "@istanbuljs/nyc-config-typescript": "^1.0.1", - "@tsconfig/node12": "^1.0.9", - "@types/mocha": "^9.0.0", - "@types/node": "^16.11.1", - "@types/sinon": "^10.0.4", - "@types/which": "^2.0.1", - "@types/yallist": "^4.0.1", - "@typescript-eslint/eslint-plugin": "^5.1.0", - "@typescript-eslint/parser": "^5.1.0", - "eslint": "^8.0.1", - "mocha": "^9.1.3", - "nyc": "^15.1.0", - "release-it": "^14.11.6", - "sinon": "^11.1.2", - "source-map-support": "^0.5.20", - "ts-node": "^10.3.0", - "typedoc": "^0.22.6", - "typedoc-github-wiki-theme": "^0.6.0", - "typedoc-plugin-markdown": "^3.11.3", - "typescript": "^4.4.4", - "which": "^2.0.2" - }, - "engines": { - "node": ">=12" - } - }, - "node_modules/redis": { - "resolved": "..", - "link": true - } - }, - "dependencies": { - "redis": { - "version": "file:..", - "requires": { - "@istanbuljs/nyc-config-typescript": "^1.0.1", - "@tsconfig/node12": "^1.0.9", - "@types/mocha": "^9.0.0", - "@types/node": "^16.11.1", - "@types/sinon": "^10.0.4", - "@types/which": "^2.0.1", - "@types/yallist": "^4.0.1", - "@typescript-eslint/eslint-plugin": "^5.1.0", - "@typescript-eslint/parser": "^5.1.0", - "cluster-key-slot": "1.1.0", - "eslint": "^8.0.1", - "generic-pool": "3.8.2", - "mocha": "^9.1.3", - "nyc": "^15.1.0", - "redis-parser": "3.0.0", - "release-it": "^14.11.6", - "sinon": "^11.1.2", - "source-map-support": "^0.5.20", - "ts-node": "^10.3.0", - "typedoc": "^0.22.6", - "typedoc-github-wiki-theme": "^0.6.0", - "typedoc-plugin-markdown": "^3.11.3", - "typescript": "^4.4.4", - "which": "^2.0.2", - "yallist": "4.0.0" - } - } - } -} diff --git a/packages/client/package.json b/packages/client/package.json index 1b4124c2eb3..812f742db84 100644 --- a/packages/client/package.json +++ b/packages/client/package.json @@ -1,6 +1,6 @@ { "name": "@redis/client", - "version": "4.0.0-rc.3", + "version": "1.0.0-rc", "license": "MIT", "main": "./dist/index.js", "types": "./dist/index.d.ts", diff --git a/packages/json/lib/commands/GET.ts b/packages/json/lib/commands/GET.ts index 8b69dc80533..36bb9bc4e4c 100644 --- a/packages/json/lib/commands/GET.ts +++ b/packages/json/lib/commands/GET.ts @@ -1,4 +1,4 @@ -import { pushVerdictArguments } from '@redis/client/lib/commands/generic-transformers'; +import { pushVerdictArguments } from '@redis/client/dist/lib/commands/generic-transformers'; export const FIRST_KEY_INDEX = 1; diff --git a/packages/json/package.json b/packages/json/package.json index 7f53cc0b807..1dcf907ea0e 100644 --- a/packages/json/package.json +++ b/packages/json/package.json @@ -9,7 +9,7 @@ "build": "tsc" }, "peerDependencies": { - "@redis/client": "^4.0.0-rc" + "@redis/client": "^1.0.0-rc" }, "devDependencies": { "@istanbuljs/nyc-config-typescript": "^1.0.1", diff --git a/packages/search/lib/commands/CREATE.spec.ts b/packages/search/lib/commands/CREATE.spec.ts index 2ac68cc9710..5bdf4c93a43 100644 --- a/packages/search/lib/commands/CREATE.spec.ts +++ b/packages/search/lib/commands/CREATE.spec.ts @@ -1,6 +1,7 @@ import { strict as assert } from 'assert'; import testUtils, { GLOBAL } from '../test-utils'; -import { RedisSearchLanguages, SchemaFieldTypes, SchemaTextFieldPhonetics, transformArguments } from './CREATE'; +import { SchemaFieldTypes, SchemaTextFieldPhonetics, transformArguments } from './CREATE'; +import { RedisSearchLanguages } from '.'; describe('CREATE', () => { describe('transformArguments', () => { @@ -111,30 +112,32 @@ describe('CREATE', () => { ['FT.CREATE', 'index', 'SCHEMA', 'field', 'TAG', 'SEPERATOR', 'seperator'] ); }); - }); - describe('with generic options', () => { - it('with AS', () => { + + + it('with CASESENSITIVE', () => { assert.deepEqual( transformArguments('index', { field: { - type: SchemaFieldTypes.TEXT, - AS: 'as' + type: SchemaFieldTypes.TAG, + CASESENSITIVE: true } }), - ['FT.CREATE', 'index', 'SCHEMA', 'field', 'AS', 'as', 'TEXT'] + ['FT.CREATE', 'index', 'SCHEMA', 'field', 'TAG', 'CASESENSITIVE'] ); }); + }); - it('with CASESENSITIVE', () => { + describe('with generic options', () => { + it('with AS', () => { assert.deepEqual( transformArguments('index', { field: { type: SchemaFieldTypes.TEXT, - CASESENSITIVE: true + AS: 'as' } }), - ['FT.CREATE', 'index', 'SCHEMA', 'field', 'TEXT', 'CASESENSITIVE'] + ['FT.CREATE', 'index', 'SCHEMA', 'field', 'AS', 'as', 'TEXT'] ); }); diff --git a/packages/search/lib/commands/CREATE.ts b/packages/search/lib/commands/CREATE.ts index 94c063ec503..b67896cd64c 100644 --- a/packages/search/lib/commands/CREATE.ts +++ b/packages/search/lib/commands/CREATE.ts @@ -1,17 +1,16 @@ import { pushOptionalVerdictArgument } from '@redis/client/dist/lib/commands/generic-transformers'; -import { PropertyName } from '.'; +import { RedisSearchLanguages, PropertyName } from '.'; export enum SchemaFieldTypes { TEXT = 'TEXT', NUMERIC = 'NUMERIC', GEO = 'GEO', - TAG = 'TAG', + TAG = 'TAG' } type CreateSchemaField> = T | ({ type: T; AS?: string; - CASESENSITIVE?: true; SORTABLE?: true | 'UNF'; NOINDEX?: true; } & E); @@ -35,6 +34,7 @@ type CreateSchemaGeoField = CreateSchemaField; type CreateSchemaTagField = CreateSchemaField; interface CreateSchema { @@ -45,34 +45,6 @@ interface CreateSchema { CreateSchemaTagField } -export enum RedisSearchLanguages { - ARABIC = 'Arabic', - BASQUE = 'Basque', - CATALANA = 'Catalan', - DANISH = 'Danish', - DUTCH = 'Dutch', - ENGLISH = 'English', - FINNISH = 'Finnish', - FRENCH = 'French', - GERMAN = 'German', - GREEK = 'Greek', - HUNGARIAN = 'Hungarian', - INDONESAIN = 'Indonesian', - IRISH = 'Irish', - ITALIAN = 'Italian', - LITHUANIAN = 'Lithuanian', - NEPALI = 'Nepali', - NORWEIGAN = 'Norwegian', - PORTUGUESE = 'Portuguese', - ROMANIAN = 'Romanian', - RUSSIAN = 'Russian', - SPANISH = 'Spanish', - SWEDISH = 'Swedish', - TAMIL = 'Tamil', - TURKISH = 'Turkish', - CHINESE = 'Chinese' -} - interface CreateOptions { ON?: 'HASH' | 'JSON'; PREFIX?: string | Array; @@ -196,11 +168,11 @@ export function transformArguments(index: string, schema: CreateSchema, options? args.push('SEPERATOR', fieldOptions.SEPERATOR); } - break; - } + if (fieldOptions.CASESENSITIVE) { + args.push('CASESENSITIVE'); + } - if (fieldOptions.CASESENSITIVE) { - args.push('CASESENSITIVE'); + break; } if (fieldOptions.SORTABLE) { diff --git a/packages/search/lib/commands/SEARCH.spec.ts b/packages/search/lib/commands/SEARCH.spec.ts index efe9c899ac2..15dc4740948 100644 --- a/packages/search/lib/commands/SEARCH.spec.ts +++ b/packages/search/lib/commands/SEARCH.spec.ts @@ -1,6 +1,7 @@ import { strict as assert } from 'assert'; +import { RedisSearchLanguages } from '.'; import testUtils, { GLOBAL } from '../test-utils'; -import { RedisSearchLanguages, SchemaFieldTypes } from './CREATE'; +import { SchemaFieldTypes } from './CREATE'; import { transformArguments } from './SEARCH'; describe('SEARCH', () => { diff --git a/packages/search/lib/commands/SEARCH.ts b/packages/search/lib/commands/SEARCH.ts index 6b14a0a18fd..a773514a738 100644 --- a/packages/search/lib/commands/SEARCH.ts +++ b/packages/search/lib/commands/SEARCH.ts @@ -1,8 +1,6 @@ import { RedisCommandArguments } from '@redis/client/dist/lib/commands'; import { pushOptionalVerdictArgument, pushVerdictArgument, transformReplyTuples } from '@redis/client/dist/lib/commands/generic-transformers'; -import { type } from 'os'; -import { PropertyName, pushSortByArguments, SortByOptions } from '.'; -import { RedisSearchLanguages } from './CREATE'; +import { RedisSearchLanguages, PropertyName, pushSortByArguments, SortByOptions } from '.'; export const FIRST_KEY_INDEX = 1; @@ -41,7 +39,7 @@ interface SearchOptions { TAGS?: { open: string; close: string; - } + }; }; SLOP?: number; INORDER?: true; diff --git a/packages/search/lib/commands/index.ts b/packages/search/lib/commands/index.ts index f54a71e0e0b..c10a10774b7 100644 --- a/packages/search/lib/commands/index.ts +++ b/packages/search/lib/commands/index.ts @@ -87,6 +87,34 @@ export default { tagVals: TAGVALS }; +export enum RedisSearchLanguages { + ARABIC = 'Arabic', + BASQUE = 'Basque', + CATALANA = 'Catalan', + DANISH = 'Danish', + DUTCH = 'Dutch', + ENGLISH = 'English', + FINNISH = 'Finnish', + FRENCH = 'French', + GERMAN = 'German', + GREEK = 'Greek', + HUNGARIAN = 'Hungarian', + INDONESAIN = 'Indonesian', + IRISH = 'Irish', + ITALIAN = 'Italian', + LITHUANIAN = 'Lithuanian', + NEPALI = 'Nepali', + NORWEIGAN = 'Norwegian', + PORTUGUESE = 'Portuguese', + ROMANIAN = 'Romanian', + RUSSIAN = 'Russian', + SPANISH = 'Spanish', + SWEDISH = 'Swedish', + TAMIL = 'Tamil', + TURKISH = 'Turkish', + CHINESE = 'Chinese' +} + export type PropertyName = `${'@' | '$.'}${string}`; export type SortByOptions = PropertyName | { diff --git a/packages/search/lib/index.ts b/packages/search/lib/index.ts index bc0e103e8c8..9e0be7b169c 100644 --- a/packages/search/lib/index.ts +++ b/packages/search/lib/index.ts @@ -1 +1,4 @@ export { default } from './commands'; + +export { SchemaFieldTypes, SchemaTextFieldPhonetics } from './commands/CREATE'; +export { AggregateSteps, AggregateGroupByReducers } from './commands/AGGREGATE'; diff --git a/packages/search/package.json b/packages/search/package.json index b44dc8fb528..5f8e9e550db 100644 --- a/packages/search/package.json +++ b/packages/search/package.json @@ -9,7 +9,7 @@ "build": "tsc" }, "peerDependencies": { - "@redis/client": "^4.0.0-rc" + "@redis/client": "^1.0.0-rc" }, "devDependencies": { "@istanbuljs/nyc-config-typescript": "^1.0.1", diff --git a/packages/test-utils/lib/dockers.ts b/packages/test-utils/lib/dockers.ts index 28bd1e49057..29f93b9c825 100644 --- a/packages/test-utils/lib/dockers.ts +++ b/packages/test-utils/lib/dockers.ts @@ -1,8 +1,8 @@ import { createConnection } from 'net'; import { once } from 'events'; -import { RedisModules, RedisScripts } from '@redis/client/lib/commands'; -import RedisClient, { RedisClientType } from '@redis/client/lib/client'; -import { promiseTimeout } from '@redis/client/lib/utils'; +import { RedisModules, RedisScripts } from '@redis/client/dist/lib/commands'; +import RedisClient, { RedisClientType } from '@redis/client/dist/lib/client'; +import { promiseTimeout } from '@redis/client/dist/lib/utils'; import path from 'path'; import { promisify } from 'util'; import { exec } from 'child_process'; diff --git a/packages/test-utils/lib/index.ts b/packages/test-utils/lib/index.ts index f9a1fc1dbd9..e553e132894 100644 --- a/packages/test-utils/lib/index.ts +++ b/packages/test-utils/lib/index.ts @@ -1,6 +1,6 @@ -import { RedisModules, RedisScripts } from '@redis/client/lib/commands'; -import RedisClient, { RedisClientOptions, RedisClientType } from '@redis/client/lib/client'; -import RedisCluster, { RedisClusterOptions, RedisClusterType } from '@redis/client/lib/cluster'; +import { RedisModules, RedisScripts } from '@redis/client/dist/lib/commands'; +import RedisClient, { RedisClientOptions, RedisClientType } from '@redis/client/dist/lib/client'; +import RedisCluster, { RedisClusterOptions, RedisClusterType } from '@redis/client/dist/lib/cluster'; import { RedisServerDockerConfig, spawnRedisServer, spawnRedisCluster } from './dockers'; import yargs from 'yargs'; import { hideBin } from 'yargs/helpers'; diff --git a/packages/test-utils/package.json b/packages/test-utils/package.json index 0c934ace9fb..fba6744136a 100644 --- a/packages/test-utils/package.json +++ b/packages/test-utils/package.json @@ -8,7 +8,7 @@ "test": "echo \"TODO\"" }, "peerDependencies": { - "@redis/client": "^4.0.0-rc" + "@redis/client": "^1.0.0-rc" }, "devDependencies": { "@istanbuljs/nyc-config-typescript": "^1.0.1", From ca4ec86f89cf8b5713a16f1ae787f367ba8ff9b1 Mon Sep 17 00:00:00 2001 From: leibale Date: Tue, 9 Nov 2021 18:09:19 -0500 Subject: [PATCH 0933/1748] add package-lock.json --- package-lock.json | 11351 ++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 11351 insertions(+) create mode 100644 package-lock.json diff --git a/package-lock.json b/package-lock.json new file mode 100644 index 00000000000..b9e9ed91405 --- /dev/null +++ b/package-lock.json @@ -0,0 +1,11351 @@ +{ + "name": "redis-monorepo", + "lockfileVersion": 2, + "requires": true, + "packages": { + "": { + "name": "redis-monorepo", + "workspaces": [ + "./packages/*" + ], + "devDependencies": { + "@tsconfig/node12": "^1.0.9" + } + }, + "node_modules/@babel/code-frame": { + "version": "7.16.0", + "resolved": "https://registry.npmjs.org/@babel/code-frame/-/code-frame-7.16.0.tgz", + "integrity": "sha512-IF4EOMEV+bfYwOmNxGzSnjR2EmQod7f1UXOpZM3l4i4o4QNwzjtJAu/HxdjHq0aYBvdqMuQEY1eg0nqW9ZPORA==", + "dev": true, + "dependencies": { + "@babel/highlight": "^7.16.0" + }, + "engines": { + "node": ">=6.9.0" + } + }, + "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==", + "dev": true, + "engines": { + "node": ">=6.9.0" + } + }, + "node_modules/@babel/core": { + "version": "7.16.0", + "resolved": "https://registry.npmjs.org/@babel/core/-/core-7.16.0.tgz", + "integrity": "sha512-mYZEvshBRHGsIAiyH5PzCFTCfbWfoYbO/jcSdXQSUQu1/pW0xDZAUP7KEc32heqWTAfAHhV9j1vH8Sav7l+JNQ==", + "dev": true, + "dependencies": { + "@babel/code-frame": "^7.16.0", + "@babel/generator": "^7.16.0", + "@babel/helper-compilation-targets": "^7.16.0", + "@babel/helper-module-transforms": "^7.16.0", + "@babel/helpers": "^7.16.0", + "@babel/parser": "^7.16.0", + "@babel/template": "^7.16.0", + "@babel/traverse": "^7.16.0", + "@babel/types": "^7.16.0", + "convert-source-map": "^1.7.0", + "debug": "^4.1.0", + "gensync": "^1.0.0-beta.2", + "json5": "^2.1.2", + "semver": "^6.3.0", + "source-map": "^0.5.0" + }, + "engines": { + "node": ">=6.9.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/babel" + } + }, + "node_modules/@babel/core/node_modules/semver": { + "version": "6.3.0", + "resolved": "https://registry.npmjs.org/semver/-/semver-6.3.0.tgz", + "integrity": "sha512-b39TBaTSfV6yBrapU89p5fKekE2m/NwnDocOVruQFS1/veMgdzuPcnOM34M6CwxW8jH/lxEa5rBoDeUwu5HHTw==", + "dev": true, + "bin": { + "semver": "bin/semver.js" + } + }, + "node_modules/@babel/generator": { + "version": "7.16.0", + "resolved": "https://registry.npmjs.org/@babel/generator/-/generator-7.16.0.tgz", + "integrity": "sha512-RR8hUCfRQn9j9RPKEVXo9LiwoxLPYn6hNZlvUOR8tSnaxlD0p0+la00ZP9/SnRt6HchKr+X0fO2r8vrETiJGew==", + "dev": true, + "dependencies": { + "@babel/types": "^7.16.0", + "jsesc": "^2.5.1", + "source-map": "^0.5.0" + }, + "engines": { + "node": ">=6.9.0" + } + }, + "node_modules/@babel/helper-compilation-targets": { + "version": "7.16.3", + "resolved": "https://registry.npmjs.org/@babel/helper-compilation-targets/-/helper-compilation-targets-7.16.3.tgz", + "integrity": "sha512-vKsoSQAyBmxS35JUOOt+07cLc6Nk/2ljLIHwmq2/NM6hdioUaqEXq/S+nXvbvXbZkNDlWOymPanJGOc4CBjSJA==", + "dev": true, + "dependencies": { + "@babel/compat-data": "^7.16.0", + "@babel/helper-validator-option": "^7.14.5", + "browserslist": "^4.17.5", + "semver": "^6.3.0" + }, + "engines": { + "node": ">=6.9.0" + }, + "peerDependencies": { + "@babel/core": "^7.0.0" + } + }, + "node_modules/@babel/helper-compilation-targets/node_modules/semver": { + "version": "6.3.0", + "resolved": "https://registry.npmjs.org/semver/-/semver-6.3.0.tgz", + "integrity": "sha512-b39TBaTSfV6yBrapU89p5fKekE2m/NwnDocOVruQFS1/veMgdzuPcnOM34M6CwxW8jH/lxEa5rBoDeUwu5HHTw==", + "dev": true, + "bin": { + "semver": "bin/semver.js" + } + }, + "node_modules/@babel/helper-function-name": { + "version": "7.16.0", + "resolved": "https://registry.npmjs.org/@babel/helper-function-name/-/helper-function-name-7.16.0.tgz", + "integrity": "sha512-BZh4mEk1xi2h4HFjWUXRQX5AEx4rvaZxHgax9gcjdLWdkjsY7MKt5p0otjsg5noXw+pB+clMCjw+aEVYADMjog==", + "dev": true, + "dependencies": { + "@babel/helper-get-function-arity": "^7.16.0", + "@babel/template": "^7.16.0", + "@babel/types": "^7.16.0" + }, + "engines": { + "node": ">=6.9.0" + } + }, + "node_modules/@babel/helper-get-function-arity": { + "version": "7.16.0", + "resolved": "https://registry.npmjs.org/@babel/helper-get-function-arity/-/helper-get-function-arity-7.16.0.tgz", + "integrity": "sha512-ASCquNcywC1NkYh/z7Cgp3w31YW8aojjYIlNg4VeJiHkqyP4AzIvr4qx7pYDb4/s8YcsZWqqOSxgkvjUz1kpDQ==", + "dev": true, + "dependencies": { + "@babel/types": "^7.16.0" + }, + "engines": { + "node": ">=6.9.0" + } + }, + "node_modules/@babel/helper-hoist-variables": { + "version": "7.16.0", + "resolved": "https://registry.npmjs.org/@babel/helper-hoist-variables/-/helper-hoist-variables-7.16.0.tgz", + "integrity": "sha512-1AZlpazjUR0EQZQv3sgRNfM9mEVWPK3M6vlalczA+EECcPz3XPh6VplbErL5UoMpChhSck5wAJHthlj1bYpcmg==", + "dev": true, + "dependencies": { + "@babel/types": "^7.16.0" + }, + "engines": { + "node": ">=6.9.0" + } + }, + "node_modules/@babel/helper-member-expression-to-functions": { + "version": "7.16.0", + "resolved": "https://registry.npmjs.org/@babel/helper-member-expression-to-functions/-/helper-member-expression-to-functions-7.16.0.tgz", + "integrity": "sha512-bsjlBFPuWT6IWhl28EdrQ+gTvSvj5tqVP5Xeftp07SEuz5pLnsXZuDkDD3Rfcxy0IsHmbZ+7B2/9SHzxO0T+sQ==", + "dev": true, + "dependencies": { + "@babel/types": "^7.16.0" + }, + "engines": { + "node": ">=6.9.0" + } + }, + "node_modules/@babel/helper-module-imports": { + "version": "7.16.0", + "resolved": "https://registry.npmjs.org/@babel/helper-module-imports/-/helper-module-imports-7.16.0.tgz", + "integrity": "sha512-kkH7sWzKPq0xt3H1n+ghb4xEMP8k0U7XV3kkB+ZGy69kDk2ySFW1qPi06sjKzFY3t1j6XbJSqr4mF9L7CYVyhg==", + "dev": true, + "dependencies": { + "@babel/types": "^7.16.0" + }, + "engines": { + "node": ">=6.9.0" + } + }, + "node_modules/@babel/helper-module-transforms": { + "version": "7.16.0", + "resolved": "https://registry.npmjs.org/@babel/helper-module-transforms/-/helper-module-transforms-7.16.0.tgz", + "integrity": "sha512-My4cr9ATcaBbmaEa8M0dZNA74cfI6gitvUAskgDtAFmAqyFKDSHQo5YstxPbN+lzHl2D9l/YOEFqb2mtUh4gfA==", + "dev": true, + "dependencies": { + "@babel/helper-module-imports": "^7.16.0", + "@babel/helper-replace-supers": "^7.16.0", + "@babel/helper-simple-access": "^7.16.0", + "@babel/helper-split-export-declaration": "^7.16.0", + "@babel/helper-validator-identifier": "^7.15.7", + "@babel/template": "^7.16.0", + "@babel/traverse": "^7.16.0", + "@babel/types": "^7.16.0" + }, + "engines": { + "node": ">=6.9.0" + } + }, + "node_modules/@babel/helper-optimise-call-expression": { + "version": "7.16.0", + "resolved": "https://registry.npmjs.org/@babel/helper-optimise-call-expression/-/helper-optimise-call-expression-7.16.0.tgz", + "integrity": "sha512-SuI467Gi2V8fkofm2JPnZzB/SUuXoJA5zXe/xzyPP2M04686RzFKFHPK6HDVN6JvWBIEW8tt9hPR7fXdn2Lgpw==", + "dev": true, + "dependencies": { + "@babel/types": "^7.16.0" + }, + "engines": { + "node": ">=6.9.0" + } + }, + "node_modules/@babel/helper-replace-supers": { + "version": "7.16.0", + "resolved": "https://registry.npmjs.org/@babel/helper-replace-supers/-/helper-replace-supers-7.16.0.tgz", + "integrity": "sha512-TQxuQfSCdoha7cpRNJvfaYxxxzmbxXw/+6cS7V02eeDYyhxderSoMVALvwupA54/pZcOTtVeJ0xccp1nGWladA==", + "dev": true, + "dependencies": { + "@babel/helper-member-expression-to-functions": "^7.16.0", + "@babel/helper-optimise-call-expression": "^7.16.0", + "@babel/traverse": "^7.16.0", + "@babel/types": "^7.16.0" + }, + "engines": { + "node": ">=6.9.0" + } + }, + "node_modules/@babel/helper-simple-access": { + "version": "7.16.0", + "resolved": "https://registry.npmjs.org/@babel/helper-simple-access/-/helper-simple-access-7.16.0.tgz", + "integrity": "sha512-o1rjBT/gppAqKsYfUdfHq5Rk03lMQrkPHG1OWzHWpLgVXRH4HnMM9Et9CVdIqwkCQlobnGHEJMsgWP/jE1zUiw==", + "dev": true, + "dependencies": { + "@babel/types": "^7.16.0" + }, + "engines": { + "node": ">=6.9.0" + } + }, + "node_modules/@babel/helper-split-export-declaration": { + "version": "7.16.0", + "resolved": "https://registry.npmjs.org/@babel/helper-split-export-declaration/-/helper-split-export-declaration-7.16.0.tgz", + "integrity": "sha512-0YMMRpuDFNGTHNRiiqJX19GjNXA4H0E8jZ2ibccfSxaCogbm3am5WN/2nQNj0YnQwGWM1J06GOcQ2qnh3+0paw==", + "dev": true, + "dependencies": { + "@babel/types": "^7.16.0" + }, + "engines": { + "node": ">=6.9.0" + } + }, + "node_modules/@babel/helper-validator-identifier": { + "version": "7.15.7", + "resolved": "https://registry.npmjs.org/@babel/helper-validator-identifier/-/helper-validator-identifier-7.15.7.tgz", + "integrity": "sha512-K4JvCtQqad9OY2+yTU8w+E82ywk/fe+ELNlt1G8z3bVGlZfn/hOcQQsUhGhW/N+tb3fxK800wLtKOE/aM0m72w==", + "dev": true, + "engines": { + "node": ">=6.9.0" + } + }, + "node_modules/@babel/helper-validator-option": { + "version": "7.14.5", + "resolved": "https://registry.npmjs.org/@babel/helper-validator-option/-/helper-validator-option-7.14.5.tgz", + "integrity": "sha512-OX8D5eeX4XwcroVW45NMvoYaIuFI+GQpA2a8Gi+X/U/cDUIRsV37qQfF905F0htTRCREQIB4KqPeaveRJUl3Ow==", + "dev": true, + "engines": { + "node": ">=6.9.0" + } + }, + "node_modules/@babel/helpers": { + "version": "7.16.3", + "resolved": "https://registry.npmjs.org/@babel/helpers/-/helpers-7.16.3.tgz", + "integrity": "sha512-Xn8IhDlBPhvYTvgewPKawhADichOsbkZuzN7qz2BusOM0brChsyXMDJvldWaYMMUNiCQdQzNEioXTp3sC8Nt8w==", + "dev": true, + "dependencies": { + "@babel/template": "^7.16.0", + "@babel/traverse": "^7.16.3", + "@babel/types": "^7.16.0" + }, + "engines": { + "node": ">=6.9.0" + } + }, + "node_modules/@babel/highlight": { + "version": "7.16.0", + "resolved": "https://registry.npmjs.org/@babel/highlight/-/highlight-7.16.0.tgz", + "integrity": "sha512-t8MH41kUQylBtu2+4IQA3atqevA2lRgqA2wyVB/YiWmsDSuylZZuXOUy9ric30hfzauEFfdsuk/eXTRrGrfd0g==", + "dev": true, + "dependencies": { + "@babel/helper-validator-identifier": "^7.15.7", + "chalk": "^2.0.0", + "js-tokens": "^4.0.0" + }, + "engines": { + "node": ">=6.9.0" + } + }, + "node_modules/@babel/highlight/node_modules/ansi-styles": { + "version": "3.2.1", + "resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-3.2.1.tgz", + "integrity": "sha512-VT0ZI6kZRdTh8YyJw3SMbYm/u+NqfsAxEpWO0Pf9sq8/e94WxxOpPKx9FR1FlyCtOVDNOQ+8ntlqFxiRc+r5qA==", + "dev": true, + "dependencies": { + "color-convert": "^1.9.0" + }, + "engines": { + "node": ">=4" + } + }, + "node_modules/@babel/highlight/node_modules/chalk": { + "version": "2.4.2", + "resolved": "https://registry.npmjs.org/chalk/-/chalk-2.4.2.tgz", + "integrity": "sha512-Mti+f9lpJNcwF4tWV8/OrTTtF1gZi+f8FqlyAdouralcFWFQWF2+NgCHShjkCb+IFBLq9buZwE1xckQU4peSuQ==", + "dev": true, + "dependencies": { + "ansi-styles": "^3.2.1", + "escape-string-regexp": "^1.0.5", + "supports-color": "^5.3.0" + }, + "engines": { + "node": ">=4" + } + }, + "node_modules/@babel/highlight/node_modules/color-convert": { + "version": "1.9.3", + "resolved": "https://registry.npmjs.org/color-convert/-/color-convert-1.9.3.tgz", + "integrity": "sha512-QfAUtd+vFdAtFQcC8CCyYt1fYWxSqAiK2cSD6zDB8N3cpsEBAvRxp9zOGg6G/SHHJYAT88/az/IuDGALsNVbGg==", + "dev": true, + "dependencies": { + "color-name": "1.1.3" + } + }, + "node_modules/@babel/highlight/node_modules/color-name": { + "version": "1.1.3", + "resolved": "https://registry.npmjs.org/color-name/-/color-name-1.1.3.tgz", + "integrity": "sha1-p9BVi9icQveV3UIyj3QIMcpTvCU=", + "dev": true + }, + "node_modules/@babel/highlight/node_modules/escape-string-regexp": { + "version": "1.0.5", + "resolved": "https://registry.npmjs.org/escape-string-regexp/-/escape-string-regexp-1.0.5.tgz", + "integrity": "sha1-G2HAViGQqN/2rjuyzwIAyhMLhtQ=", + "dev": true, + "engines": { + "node": ">=0.8.0" + } + }, + "node_modules/@babel/highlight/node_modules/has-flag": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/has-flag/-/has-flag-3.0.0.tgz", + "integrity": "sha1-tdRU3CGZriJWmfNGfloH87lVuv0=", + "dev": true, + "engines": { + "node": ">=4" + } + }, + "node_modules/@babel/highlight/node_modules/supports-color": { + "version": "5.5.0", + "resolved": "https://registry.npmjs.org/supports-color/-/supports-color-5.5.0.tgz", + "integrity": "sha512-QjVjwdXIt408MIiAqCX4oUKsgU2EqAGzs2Ppkm4aQYbjm+ZEWEcW4SfFNTr4uMNZma0ey4f5lgLrkB0aX0QMow==", + "dev": true, + "dependencies": { + "has-flag": "^3.0.0" + }, + "engines": { + "node": ">=4" + } + }, + "node_modules/@babel/parser": { + "version": "7.16.3", + "resolved": "https://registry.npmjs.org/@babel/parser/-/parser-7.16.3.tgz", + "integrity": "sha512-dcNwU1O4sx57ClvLBVFbEgx0UZWfd0JQX5X6fxFRCLHelFBGXFfSz6Y0FAq2PEwUqlqLkdVjVr4VASEOuUnLJw==", + "dev": true, + "bin": { + "parser": "bin/babel-parser.js" + }, + "engines": { + "node": ">=6.0.0" + } + }, + "node_modules/@babel/template": { + "version": "7.16.0", + "resolved": "https://registry.npmjs.org/@babel/template/-/template-7.16.0.tgz", + "integrity": "sha512-MnZdpFD/ZdYhXwiunMqqgyZyucaYsbL0IrjoGjaVhGilz+x8YB++kRfygSOIj1yOtWKPlx7NBp+9I1RQSgsd5A==", + "dev": true, + "dependencies": { + "@babel/code-frame": "^7.16.0", + "@babel/parser": "^7.16.0", + "@babel/types": "^7.16.0" + }, + "engines": { + "node": ">=6.9.0" + } + }, + "node_modules/@babel/traverse": { + "version": "7.16.3", + "resolved": "https://registry.npmjs.org/@babel/traverse/-/traverse-7.16.3.tgz", + "integrity": "sha512-eolumr1vVMjqevCpwVO99yN/LoGL0EyHiLO5I043aYQvwOJ9eR5UsZSClHVCzfhBduMAsSzgA/6AyqPjNayJag==", + "dev": true, + "dependencies": { + "@babel/code-frame": "^7.16.0", + "@babel/generator": "^7.16.0", + "@babel/helper-function-name": "^7.16.0", + "@babel/helper-hoist-variables": "^7.16.0", + "@babel/helper-split-export-declaration": "^7.16.0", + "@babel/parser": "^7.16.3", + "@babel/types": "^7.16.0", + "debug": "^4.1.0", + "globals": "^11.1.0" + }, + "engines": { + "node": ">=6.9.0" + } + }, + "node_modules/@babel/traverse/node_modules/globals": { + "version": "11.12.0", + "resolved": "https://registry.npmjs.org/globals/-/globals-11.12.0.tgz", + "integrity": "sha512-WOBp/EEGUiIsJSp7wcv/y6MO+lV9UoncWqxuFfm8eBwzWNgyfBd6Gz+IeKQ9jCmyhoH99g15M3T+QaVHFjizVA==", + "dev": true, + "engines": { + "node": ">=4" + } + }, + "node_modules/@babel/types": { + "version": "7.16.0", + "resolved": "https://registry.npmjs.org/@babel/types/-/types-7.16.0.tgz", + "integrity": "sha512-PJgg/k3SdLsGb3hhisFvtLOw5ts113klrpLuIPtCJIU+BB24fqq6lf8RWqKJEjzqXR9AEH1rIb5XTqwBHB+kQg==", + "dev": true, + "dependencies": { + "@babel/helper-validator-identifier": "^7.15.7", + "to-fast-properties": "^2.0.0" + }, + "engines": { + "node": ">=6.9.0" + } + }, + "node_modules/@cspotcode/source-map-consumer": { + "version": "0.8.0", + "resolved": "https://registry.npmjs.org/@cspotcode/source-map-consumer/-/source-map-consumer-0.8.0.tgz", + "integrity": "sha512-41qniHzTU8yAGbCp04ohlmSrZf8bkf/iJsl3V0dRGsQN/5GFfx+LbCSsCpp2gqrqjTVg/K6O8ycoV35JIwAzAg==", + "dev": true, + "engines": { + "node": ">= 12" + } + }, + "node_modules/@cspotcode/source-map-support": { + "version": "0.7.0", + "resolved": "https://registry.npmjs.org/@cspotcode/source-map-support/-/source-map-support-0.7.0.tgz", + "integrity": "sha512-X4xqRHqN8ACt2aHVe51OxeA2HjbcL4MqFqXkrmQszJ1NOUuUu5u6Vqx/0lZSVNku7velL5FC/s5uEAj1lsBMhA==", + "dev": true, + "dependencies": { + "@cspotcode/source-map-consumer": "0.8.0" + }, + "engines": { + "node": ">=12" + } + }, + "node_modules/@eslint/eslintrc": { + "version": "1.0.4", + "resolved": "https://registry.npmjs.org/@eslint/eslintrc/-/eslintrc-1.0.4.tgz", + "integrity": "sha512-h8Vx6MdxwWI2WM8/zREHMoqdgLNXEL4QX3MWSVMdyNJGvXVOs+6lp+m2hc3FnuMHDc4poxFNI20vCk0OmI4G0Q==", + "dev": true, + "dependencies": { + "ajv": "^6.12.4", + "debug": "^4.3.2", + "espree": "^9.0.0", + "globals": "^13.9.0", + "ignore": "^4.0.6", + "import-fresh": "^3.2.1", + "js-yaml": "^4.1.0", + "minimatch": "^3.0.4", + "strip-json-comments": "^3.1.1" + }, + "engines": { + "node": "^12.22.0 || ^14.17.0 || >=16.0.0" + } + }, + "node_modules/@eslint/eslintrc/node_modules/ignore": { + "version": "4.0.6", + "resolved": "https://registry.npmjs.org/ignore/-/ignore-4.0.6.tgz", + "integrity": "sha512-cyFDKrqc/YdcWFniJhzI42+AzS+gNwmUzOSFcRCQYwySuBBBy/KjuxWLZ/FHEH6Moq1NizMOBWyTcv8O4OZIMg==", + "dev": true, + "engines": { + "node": ">= 4" + } + }, + "node_modules/@humanwhocodes/config-array": { + "version": "0.6.0", + "resolved": "https://registry.npmjs.org/@humanwhocodes/config-array/-/config-array-0.6.0.tgz", + "integrity": "sha512-JQlEKbcgEUjBFhLIF4iqM7u/9lwgHRBcpHrmUNCALK0Q3amXN6lxdoXLnF0sm11E9VqTmBALR87IlUg1bZ8A9A==", + "dev": true, + "dependencies": { + "@humanwhocodes/object-schema": "^1.2.0", + "debug": "^4.1.1", + "minimatch": "^3.0.4" + }, + "engines": { + "node": ">=10.10.0" + } + }, + "node_modules/@humanwhocodes/object-schema": { + "version": "1.2.1", + "resolved": "https://registry.npmjs.org/@humanwhocodes/object-schema/-/object-schema-1.2.1.tgz", + "integrity": "sha512-ZnQMnLV4e7hDlUvw8H+U8ASL02SS2Gn6+9Ac3wGGLIe7+je2AeAOxPY+izIPJDfFDb7eDjev0Us8MO1iFRN8hA==", + "dev": true + }, + "node_modules/@iarna/toml": { + "version": "2.2.5", + "resolved": "https://registry.npmjs.org/@iarna/toml/-/toml-2.2.5.tgz", + "integrity": "sha512-trnsAYxU3xnS1gPHPyU961coFyLkh4gAD/0zQ5mymY4yOZ+CYvsPqUbOFSw0aDM4y0tV7tiFxL/1XfXPNC6IPg==", + "dev": true + }, + "node_modules/@istanbuljs/load-nyc-config": { + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/@istanbuljs/load-nyc-config/-/load-nyc-config-1.1.0.tgz", + "integrity": "sha512-VjeHSlIzpv/NyD3N0YuHfXOPDIixcA1q2ZV98wsMqcYlPmv2n3Yb2lYP9XMElnaFVXg5A7YLTeLu6V84uQDjmQ==", + "dev": true, + "dependencies": { + "camelcase": "^5.3.1", + "find-up": "^4.1.0", + "get-package-type": "^0.1.0", + "js-yaml": "^3.13.1", + "resolve-from": "^5.0.0" + }, + "engines": { + "node": ">=8" + } + }, + "node_modules/@istanbuljs/load-nyc-config/node_modules/argparse": { + "version": "1.0.10", + "resolved": "https://registry.npmjs.org/argparse/-/argparse-1.0.10.tgz", + "integrity": "sha512-o5Roy6tNG4SL/FOkCAN6RzjiakZS25RLYFrcMttJqbdd8BWrnA+fGz57iN5Pb06pvBGvl5gQ0B48dJlslXvoTg==", + "dev": true, + "dependencies": { + "sprintf-js": "~1.0.2" + } + }, + "node_modules/@istanbuljs/load-nyc-config/node_modules/find-up": { + "version": "4.1.0", + "resolved": "https://registry.npmjs.org/find-up/-/find-up-4.1.0.tgz", + "integrity": "sha512-PpOwAdQ/YlXQ2vj8a3h8IipDuYRi3wceVQQGYWxNINccq40Anw7BlsEXCMbt1Zt+OLA6Fq9suIpIWD0OsnISlw==", + "dev": true, + "dependencies": { + "locate-path": "^5.0.0", + "path-exists": "^4.0.0" + }, + "engines": { + "node": ">=8" + } + }, + "node_modules/@istanbuljs/load-nyc-config/node_modules/js-yaml": { + "version": "3.14.1", + "resolved": "https://registry.npmjs.org/js-yaml/-/js-yaml-3.14.1.tgz", + "integrity": "sha512-okMH7OXXJ7YrN9Ok3/SXrnu4iX9yOk+25nqX4imS2npuvTYDmo/QEZoqwZkYaIDk3jVvBOTOIEgEhaLOynBS9g==", + "dev": true, + "dependencies": { + "argparse": "^1.0.7", + "esprima": "^4.0.0" + }, + "bin": { + "js-yaml": "bin/js-yaml.js" + } + }, + "node_modules/@istanbuljs/load-nyc-config/node_modules/locate-path": { + "version": "5.0.0", + "resolved": "https://registry.npmjs.org/locate-path/-/locate-path-5.0.0.tgz", + "integrity": "sha512-t7hw9pI+WvuwNJXwk5zVHpyhIqzg2qTlklJOf0mVxGSbe3Fp2VieZcduNYjaLDoy6p9uGpQEGWG87WpMKlNq8g==", + "dev": true, + "dependencies": { + "p-locate": "^4.1.0" + }, + "engines": { + "node": ">=8" + } + }, + "node_modules/@istanbuljs/load-nyc-config/node_modules/p-limit": { + "version": "2.3.0", + "resolved": "https://registry.npmjs.org/p-limit/-/p-limit-2.3.0.tgz", + "integrity": "sha512-//88mFWSJx8lxCzwdAABTJL2MyWB12+eIY7MDL2SqLmAkeKU9qxRvWuSyTjm3FUmpBEMuFfckAIqEaVGUDxb6w==", + "dev": true, + "dependencies": { + "p-try": "^2.0.0" + }, + "engines": { + "node": ">=6" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/@istanbuljs/load-nyc-config/node_modules/p-locate": { + "version": "4.1.0", + "resolved": "https://registry.npmjs.org/p-locate/-/p-locate-4.1.0.tgz", + "integrity": "sha512-R79ZZ/0wAxKGu3oYMlz8jy/kbhsNrS7SKZ7PxEHBgJ5+F2mtFW2fK2cOtBh1cHYkQsbzFV7I+EoRKe6Yt0oK7A==", + "dev": true, + "dependencies": { + "p-limit": "^2.2.0" + }, + "engines": { + "node": ">=8" + } + }, + "node_modules/@istanbuljs/load-nyc-config/node_modules/resolve-from": { + "version": "5.0.0", + "resolved": "https://registry.npmjs.org/resolve-from/-/resolve-from-5.0.0.tgz", + "integrity": "sha512-qYg9KP24dD5qka9J47d0aVky0N+b4fTU89LN9iDnjB5waksiC49rvMB0PrUJQGoTmH50XPiqOvAjDfaijGxYZw==", + "dev": true, + "engines": { + "node": ">=8" + } + }, + "node_modules/@istanbuljs/nyc-config-typescript": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/@istanbuljs/nyc-config-typescript/-/nyc-config-typescript-1.0.1.tgz", + "integrity": "sha512-/gz6LgVpky205LuoOfwEZmnUtaSmdk0QIMcNFj9OvxhiMhPpKftMgZmGN7jNj7jR+lr8IB1Yks3QSSSNSxfoaQ==", + "dev": true, + "dependencies": { + "@istanbuljs/schema": "^0.1.2" + }, + "engines": { + "node": ">=8" + }, + "peerDependencies": { + "nyc": ">=15", + "source-map-support": "*", + "ts-node": "*" + } + }, + "node_modules/@istanbuljs/schema": { + "version": "0.1.3", + "resolved": "https://registry.npmjs.org/@istanbuljs/schema/-/schema-0.1.3.tgz", + "integrity": "sha512-ZXRY4jNvVgSVQ8DL3LTcakaAtXwTVUxE81hslsyD2AtoXW/wVob10HkOJ1X/pAlcI7D+2YoZKg5do8G/w6RYgA==", + "dev": true, + "engines": { + "node": ">=8" + } + }, + "node_modules/@nodelib/fs.scandir": { + "version": "2.1.5", + "resolved": "https://registry.npmjs.org/@nodelib/fs.scandir/-/fs.scandir-2.1.5.tgz", + "integrity": "sha512-vq24Bq3ym5HEQm2NKCr3yXDwjc7vTsEThRDnkp2DK9p1uqLR+DHurm/NOTo0KG7HYHU7eppKZj3MyqYuMBf62g==", + "dev": true, + "dependencies": { + "@nodelib/fs.stat": "2.0.5", + "run-parallel": "^1.1.9" + }, + "engines": { + "node": ">= 8" + } + }, + "node_modules/@nodelib/fs.stat": { + "version": "2.0.5", + "resolved": "https://registry.npmjs.org/@nodelib/fs.stat/-/fs.stat-2.0.5.tgz", + "integrity": "sha512-RkhPPp2zrqDAQA/2jNhnztcPAlv64XdhIp7a7454A5ovI7Bukxgt7MX7udwAu3zg1DcpPU0rz3VV1SeaqvY4+A==", + "dev": true, + "engines": { + "node": ">= 8" + } + }, + "node_modules/@nodelib/fs.walk": { + "version": "1.2.8", + "resolved": "https://registry.npmjs.org/@nodelib/fs.walk/-/fs.walk-1.2.8.tgz", + "integrity": "sha512-oGB+UxlgWcgQkgwo8GcEGwemoTFt3FIO9ababBmaGwXIoBKZ+GTy0pP185beGg7Llih/NSHSV2XAs1lnznocSg==", + "dev": true, + "dependencies": { + "@nodelib/fs.scandir": "2.1.5", + "fastq": "^1.6.0" + }, + "engines": { + "node": ">= 8" + } + }, + "node_modules/@octokit/auth-token": { + "version": "2.5.0", + "resolved": "https://registry.npmjs.org/@octokit/auth-token/-/auth-token-2.5.0.tgz", + "integrity": "sha512-r5FVUJCOLl19AxiuZD2VRZ/ORjp/4IN98Of6YJoJOkY75CIBuYfmiNHGrDwXr+aLGG55igl9QrxX3hbiXlLb+g==", + "dev": true, + "dependencies": { + "@octokit/types": "^6.0.3" + } + }, + "node_modules/@octokit/core": { + "version": "3.5.1", + "resolved": "https://registry.npmjs.org/@octokit/core/-/core-3.5.1.tgz", + "integrity": "sha512-omncwpLVxMP+GLpLPgeGJBF6IWJFjXDS5flY5VbppePYX9XehevbDykRH9PdCdvqt9TS5AOTiDide7h0qrkHjw==", + "dev": true, + "dependencies": { + "@octokit/auth-token": "^2.4.4", + "@octokit/graphql": "^4.5.8", + "@octokit/request": "^5.6.0", + "@octokit/request-error": "^2.0.5", + "@octokit/types": "^6.0.3", + "before-after-hook": "^2.2.0", + "universal-user-agent": "^6.0.0" + } + }, + "node_modules/@octokit/endpoint": { + "version": "6.0.12", + "resolved": "https://registry.npmjs.org/@octokit/endpoint/-/endpoint-6.0.12.tgz", + "integrity": "sha512-lF3puPwkQWGfkMClXb4k/eUT/nZKQfxinRWJrdZaJO85Dqwo/G0yOC434Jr2ojwafWJMYqFGFa5ms4jJUgujdA==", + "dev": true, + "dependencies": { + "@octokit/types": "^6.0.3", + "is-plain-object": "^5.0.0", + "universal-user-agent": "^6.0.0" + } + }, + "node_modules/@octokit/graphql": { + "version": "4.8.0", + "resolved": "https://registry.npmjs.org/@octokit/graphql/-/graphql-4.8.0.tgz", + "integrity": "sha512-0gv+qLSBLKF0z8TKaSKTsS39scVKF9dbMxJpj3U0vC7wjNWFuIpL/z76Qe2fiuCbDRcJSavkXsVtMS6/dtQQsg==", + "dev": true, + "dependencies": { + "@octokit/request": "^5.6.0", + "@octokit/types": "^6.0.3", + "universal-user-agent": "^6.0.0" + } + }, + "node_modules/@octokit/openapi-types": { + "version": "11.2.0", + "resolved": "https://registry.npmjs.org/@octokit/openapi-types/-/openapi-types-11.2.0.tgz", + "integrity": "sha512-PBsVO+15KSlGmiI8QAzaqvsNlZlrDlyAJYcrXBCvVUxCp7VnXjkwPoFHgjEJXx3WF9BAwkA6nfCUA7i9sODzKA==", + "dev": true + }, + "node_modules/@octokit/plugin-paginate-rest": { + "version": "2.17.0", + "resolved": "https://registry.npmjs.org/@octokit/plugin-paginate-rest/-/plugin-paginate-rest-2.17.0.tgz", + "integrity": "sha512-tzMbrbnam2Mt4AhuyCHvpRkS0oZ5MvwwcQPYGtMv4tUa5kkzG58SVB0fcsLulOZQeRnOgdkZWkRUiyBlh0Bkyw==", + "dev": true, + "dependencies": { + "@octokit/types": "^6.34.0" + }, + "peerDependencies": { + "@octokit/core": ">=2" + } + }, + "node_modules/@octokit/plugin-request-log": { + "version": "1.0.4", + "resolved": "https://registry.npmjs.org/@octokit/plugin-request-log/-/plugin-request-log-1.0.4.tgz", + "integrity": "sha512-mLUsMkgP7K/cnFEw07kWqXGF5LKrOkD+lhCrKvPHXWDywAwuDUeDwWBpc69XK3pNX0uKiVt8g5z96PJ6z9xCFA==", + "dev": true, + "peerDependencies": { + "@octokit/core": ">=3" + } + }, + "node_modules/@octokit/plugin-rest-endpoint-methods": { + "version": "5.13.0", + "resolved": "https://registry.npmjs.org/@octokit/plugin-rest-endpoint-methods/-/plugin-rest-endpoint-methods-5.13.0.tgz", + "integrity": "sha512-uJjMTkN1KaOIgNtUPMtIXDOjx6dGYysdIFhgA52x4xSadQCz3b/zJexvITDVpANnfKPW/+E0xkOvLntqMYpviA==", + "dev": true, + "dependencies": { + "@octokit/types": "^6.34.0", + "deprecation": "^2.3.1" + }, + "peerDependencies": { + "@octokit/core": ">=3" + } + }, + "node_modules/@octokit/request": { + "version": "5.6.2", + "resolved": "https://registry.npmjs.org/@octokit/request/-/request-5.6.2.tgz", + "integrity": "sha512-je66CvSEVf0jCpRISxkUcCa0UkxmFs6eGDRSbfJtAVwbLH5ceqF+YEyC8lj8ystKyZTy8adWr0qmkY52EfOeLA==", + "dev": true, + "dependencies": { + "@octokit/endpoint": "^6.0.1", + "@octokit/request-error": "^2.1.0", + "@octokit/types": "^6.16.1", + "is-plain-object": "^5.0.0", + "node-fetch": "^2.6.1", + "universal-user-agent": "^6.0.0" + } + }, + "node_modules/@octokit/request-error": { + "version": "2.1.0", + "resolved": "https://registry.npmjs.org/@octokit/request-error/-/request-error-2.1.0.tgz", + "integrity": "sha512-1VIvgXxs9WHSjicsRwq8PlR2LR2x6DwsJAaFgzdi0JfJoGSO8mYI/cHJQ+9FbN21aa+DrgNLnwObmyeSC8Rmpg==", + "dev": true, + "dependencies": { + "@octokit/types": "^6.0.3", + "deprecation": "^2.0.0", + "once": "^1.4.0" + } + }, + "node_modules/@octokit/rest": { + "version": "18.10.0", + "resolved": "https://registry.npmjs.org/@octokit/rest/-/rest-18.10.0.tgz", + "integrity": "sha512-esHR5OKy38bccL/sajHqZudZCvmv4yjovMJzyXlphaUo7xykmtOdILGJ3aAm0mFHmMLmPFmDMJXf39cAjNJsrw==", + "dev": true, + "dependencies": { + "@octokit/core": "^3.5.1", + "@octokit/plugin-paginate-rest": "^2.16.0", + "@octokit/plugin-request-log": "^1.0.4", + "@octokit/plugin-rest-endpoint-methods": "^5.9.0" + } + }, + "node_modules/@octokit/types": { + "version": "6.34.0", + "resolved": "https://registry.npmjs.org/@octokit/types/-/types-6.34.0.tgz", + "integrity": "sha512-s1zLBjWhdEI2zwaoSgyOFoKSl109CUcVBCc7biPJ3aAf6LGLU6szDvi31JPU7bxfla2lqfhjbbg/5DdFNxOwHw==", + "dev": true, + "dependencies": { + "@octokit/openapi-types": "^11.2.0" + } + }, + "node_modules/@redis/client": { + "resolved": "packages/client", + "link": true + }, + "node_modules/@redis/json": { + "resolved": "packages/json", + "link": true + }, + "node_modules/@redis/search": { + "resolved": "packages/search", + "link": true + }, + "node_modules/@redis/test-utils": { + "resolved": "packages/test-utils", + "link": true + }, + "node_modules/@redis/time-series": { + "resolved": "packages/time-series", + "link": true + }, + "node_modules/@sindresorhus/is": { + "version": "4.2.0", + "resolved": "https://registry.npmjs.org/@sindresorhus/is/-/is-4.2.0.tgz", + "integrity": "sha512-VkE3KLBmJwcCaVARtQpfuKcKv8gcBmUubrfHGF84dXuuW6jgsRYxPtzcIhPyK9WAPpRt2/xY6zkD9MnRaJzSyw==", + "dev": true, + "engines": { + "node": ">=10" + }, + "funding": { + "url": "https://github.com/sindresorhus/is?sponsor=1" + } + }, + "node_modules/@sinonjs/commons": { + "version": "1.8.3", + "resolved": "https://registry.npmjs.org/@sinonjs/commons/-/commons-1.8.3.tgz", + "integrity": "sha512-xkNcLAn/wZaX14RPlwizcKicDk9G3F8m2nU3L7Ukm5zBgTwiT0wsoFAHx9Jq56fJA1z/7uKGtCRu16sOUCLIHQ==", + "dev": true, + "dependencies": { + "type-detect": "4.0.8" + } + }, + "node_modules/@sinonjs/fake-timers": { + "version": "7.1.2", + "resolved": "https://registry.npmjs.org/@sinonjs/fake-timers/-/fake-timers-7.1.2.tgz", + "integrity": "sha512-iQADsW4LBMISqZ6Ci1dupJL9pprqwcVFTcOsEmQOEhW+KLCVn/Y4Jrvg2k19fIHCp+iFprriYPTdRcQR8NbUPg==", + "dev": true, + "dependencies": { + "@sinonjs/commons": "^1.7.0" + } + }, + "node_modules/@sinonjs/samsam": { + "version": "6.0.2", + "resolved": "https://registry.npmjs.org/@sinonjs/samsam/-/samsam-6.0.2.tgz", + "integrity": "sha512-jxPRPp9n93ci7b8hMfJOFDPRLFYadN6FSpeROFTR4UNF4i5b+EK6m4QXPO46BDhFgRy1JuS87zAnFOzCUwMJcQ==", + "dev": true, + "dependencies": { + "@sinonjs/commons": "^1.6.0", + "lodash.get": "^4.4.2", + "type-detect": "^4.0.8" + } + }, + "node_modules/@sinonjs/text-encoding": { + "version": "0.7.1", + "resolved": "https://registry.npmjs.org/@sinonjs/text-encoding/-/text-encoding-0.7.1.tgz", + "integrity": "sha512-+iTbntw2IZPb/anVDbypzfQa+ay64MW0Zo8aJ8gZPWMMK6/OubMVb6lUPMagqjOPnmtauXnFCACVl3O7ogjeqQ==", + "dev": true + }, + "node_modules/@szmarczak/http-timer": { + "version": "4.0.6", + "resolved": "https://registry.npmjs.org/@szmarczak/http-timer/-/http-timer-4.0.6.tgz", + "integrity": "sha512-4BAffykYOgO+5nzBWYwE3W90sBgLJoUPRWWcL8wlyiM8IB8ipJz3UMJ9KXQd1RKQXpKp8Tutn80HZtWsu2u76w==", + "dev": true, + "dependencies": { + "defer-to-connect": "^2.0.0" + }, + "engines": { + "node": ">=10" + } + }, + "node_modules/@tsconfig/node10": { + "version": "1.0.8", + "resolved": "https://registry.npmjs.org/@tsconfig/node10/-/node10-1.0.8.tgz", + "integrity": "sha512-6XFfSQmMgq0CFLY1MslA/CPUfhIL919M1rMsa5lP2P097N2Wd1sSX0tx1u4olM16fLNhtHZpRhedZJphNJqmZg==", + "dev": true + }, + "node_modules/@tsconfig/node12": { + "version": "1.0.9", + "resolved": "https://registry.npmjs.org/@tsconfig/node12/-/node12-1.0.9.tgz", + "integrity": "sha512-/yBMcem+fbvhSREH+s14YJi18sp7J9jpuhYByADT2rypfajMZZN4WQ6zBGgBKp53NKmqI36wFYDb3yaMPurITw==", + "dev": true + }, + "node_modules/@tsconfig/node14": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/@tsconfig/node14/-/node14-1.0.1.tgz", + "integrity": "sha512-509r2+yARFfHHE7T6Puu2jjkoycftovhXRqW328PDXTVGKihlb1P8Z9mMZH04ebyajfRY7dedfGynlrFHJUQCg==", + "dev": true + }, + "node_modules/@tsconfig/node16": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/@tsconfig/node16/-/node16-1.0.2.tgz", + "integrity": "sha512-eZxlbI8GZscaGS7kkc/trHTT5xgrjH3/1n2JDwusC9iahPKWMRvRjJSAN5mCXviuTGQ/lHnhvv8Q1YTpnfz9gA==", + "dev": true + }, + "node_modules/@types/cacheable-request": { + "version": "6.0.2", + "resolved": "https://registry.npmjs.org/@types/cacheable-request/-/cacheable-request-6.0.2.tgz", + "integrity": "sha512-B3xVo+dlKM6nnKTcmm5ZtY/OL8bOAOd2Olee9M1zft65ox50OzjEHW91sDiU9j6cvW8Ejg1/Qkf4xd2kugApUA==", + "dev": true, + "dependencies": { + "@types/http-cache-semantics": "*", + "@types/keyv": "*", + "@types/node": "*", + "@types/responselike": "*" + } + }, + "node_modules/@types/http-cache-semantics": { + "version": "4.0.1", + "resolved": "https://registry.npmjs.org/@types/http-cache-semantics/-/http-cache-semantics-4.0.1.tgz", + "integrity": "sha512-SZs7ekbP8CN0txVG2xVRH6EgKmEm31BOxA07vkFaETzZz1xh+cbt8BcI0slpymvwhx5dlFnQG2rTlPVQn+iRPQ==", + "dev": true + }, + "node_modules/@types/json-schema": { + "version": "7.0.9", + "resolved": "https://registry.npmjs.org/@types/json-schema/-/json-schema-7.0.9.tgz", + "integrity": "sha512-qcUXuemtEu+E5wZSJHNxUXeCZhAfXKQ41D+duX+VYPde7xyEVZci+/oXKJL13tnRs9lR2pr4fod59GT6/X1/yQ==", + "dev": true + }, + "node_modules/@types/keyv": { + "version": "3.1.3", + "resolved": "https://registry.npmjs.org/@types/keyv/-/keyv-3.1.3.tgz", + "integrity": "sha512-FXCJgyyN3ivVgRoml4h94G/p3kY+u/B86La+QptcqJaWtBWtmc6TtkNfS40n9bIvyLteHh7zXOtgbobORKPbDg==", + "dev": true, + "dependencies": { + "@types/node": "*" + } + }, + "node_modules/@types/mocha": { + "version": "9.0.0", + "resolved": "https://registry.npmjs.org/@types/mocha/-/mocha-9.0.0.tgz", + "integrity": "sha512-scN0hAWyLVAvLR9AyW7HoFF5sJZglyBsbPuHO4fv7JRvfmPBMfp1ozWqOf/e4wwPNxezBZXRfWzMb6iFLgEVRA==", + "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==", + "dev": true + }, + "node_modules/@types/parse-json": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/@types/parse-json/-/parse-json-4.0.0.tgz", + "integrity": "sha512-//oorEZjL6sbPcKUaCdIGlIUeH26mgzimjBB77G6XRgnDl/L5wOnpyBGRe/Mmf5CVW3PwEBE1NjiMZ/ssFh4wA==", + "dev": true + }, + "node_modules/@types/responselike": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/@types/responselike/-/responselike-1.0.0.tgz", + "integrity": "sha512-85Y2BjiufFzaMIlvJDvTTB8Fxl2xfLo4HgmHzVBz08w4wDePCTjYw66PdrolO0kzli3yam/YCgRufyo1DdQVTA==", + "dev": true, + "dependencies": { + "@types/node": "*" + } + }, + "node_modules/@types/sinon": { + "version": "10.0.6", + "resolved": "https://registry.npmjs.org/@types/sinon/-/sinon-10.0.6.tgz", + "integrity": "sha512-6EF+wzMWvBNeGrfP3Nx60hhx+FfwSg1JJBLAAP/IdIUq0EYkqCYf70VT3PhuhPX9eLD+Dp+lNdpb/ZeHG8Yezg==", + "dev": true, + "dependencies": { + "@sinonjs/fake-timers": "^7.1.0" + } + }, + "node_modules/@types/yallist": { + "version": "4.0.1", + "resolved": "https://registry.npmjs.org/@types/yallist/-/yallist-4.0.1.tgz", + "integrity": "sha512-G3FNJfaYtN8URU6wd6+uwFI62KO79j7n3XTYcwcFncP8gkfoi0b821GoVVt0oqKVnCqKYOMNKIGpakPoFhzAGA==", + "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==", + "dev": true, + "dependencies": { + "@types/yargs-parser": "*" + } + }, + "node_modules/@types/yargs-parser": { + "version": "20.2.1", + "resolved": "https://registry.npmjs.org/@types/yargs-parser/-/yargs-parser-20.2.1.tgz", + "integrity": "sha512-7tFImggNeNBVMsn0vLrpn1H1uPrUBdnARPTpZoitY37ZrdJREzf7I16tMrlK3hen349gr1NYh8CmZQa7CTG6Aw==", + "dev": true + }, + "node_modules/@typescript-eslint/eslint-plugin": { + "version": "5.3.1", + "resolved": "https://registry.npmjs.org/@typescript-eslint/eslint-plugin/-/eslint-plugin-5.3.1.tgz", + "integrity": "sha512-cFImaoIr5Ojj358xI/SDhjog57OK2NqlpxwdcgyxDA3bJlZcJq5CPzUXtpD7CxI2Hm6ATU7w5fQnnkVnmwpHqw==", + "dev": true, + "dependencies": { + "@typescript-eslint/experimental-utils": "5.3.1", + "@typescript-eslint/scope-manager": "5.3.1", + "debug": "^4.3.2", + "functional-red-black-tree": "^1.0.1", + "ignore": "^5.1.8", + "regexpp": "^3.2.0", + "semver": "^7.3.5", + "tsutils": "^3.21.0" + }, + "engines": { + "node": "^12.22.0 || ^14.17.0 || >=16.0.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/typescript-eslint" + }, + "peerDependencies": { + "@typescript-eslint/parser": "^5.0.0", + "eslint": "^6.0.0 || ^7.0.0 || ^8.0.0" + }, + "peerDependenciesMeta": { + "typescript": { + "optional": true + } + } + }, + "node_modules/@typescript-eslint/experimental-utils": { + "version": "5.3.1", + "resolved": "https://registry.npmjs.org/@typescript-eslint/experimental-utils/-/experimental-utils-5.3.1.tgz", + "integrity": "sha512-RgFn5asjZ5daUhbK5Sp0peq0SSMytqcrkNfU4pnDma2D8P3ElZ6JbYjY8IMSFfZAJ0f3x3tnO3vXHweYg0g59w==", + "dev": true, + "dependencies": { + "@types/json-schema": "^7.0.9", + "@typescript-eslint/scope-manager": "5.3.1", + "@typescript-eslint/types": "5.3.1", + "@typescript-eslint/typescript-estree": "5.3.1", + "eslint-scope": "^5.1.1", + "eslint-utils": "^3.0.0" + }, + "engines": { + "node": "^12.22.0 || ^14.17.0 || >=16.0.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/typescript-eslint" + }, + "peerDependencies": { + "eslint": "*" + } + }, + "node_modules/@typescript-eslint/parser": { + "version": "5.3.1", + "resolved": "https://registry.npmjs.org/@typescript-eslint/parser/-/parser-5.3.1.tgz", + "integrity": "sha512-TD+ONlx5c+Qhk21x9gsJAMRohWAUMavSOmJgv3JGy9dgPhuBd5Wok0lmMClZDyJNLLZK1JRKiATzCKZNUmoyfw==", + "dev": true, + "dependencies": { + "@typescript-eslint/scope-manager": "5.3.1", + "@typescript-eslint/types": "5.3.1", + "@typescript-eslint/typescript-estree": "5.3.1", + "debug": "^4.3.2" + }, + "engines": { + "node": "^12.22.0 || ^14.17.0 || >=16.0.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/typescript-eslint" + }, + "peerDependencies": { + "eslint": "^6.0.0 || ^7.0.0 || ^8.0.0" + }, + "peerDependenciesMeta": { + "typescript": { + "optional": true + } + } + }, + "node_modules/@typescript-eslint/scope-manager": { + "version": "5.3.1", + "resolved": "https://registry.npmjs.org/@typescript-eslint/scope-manager/-/scope-manager-5.3.1.tgz", + "integrity": "sha512-XksFVBgAq0Y9H40BDbuPOTUIp7dn4u8oOuhcgGq7EoDP50eqcafkMVGrypyVGvDYHzjhdUCUwuwVUK4JhkMAMg==", + "dev": true, + "dependencies": { + "@typescript-eslint/types": "5.3.1", + "@typescript-eslint/visitor-keys": "5.3.1" + }, + "engines": { + "node": "^12.22.0 || ^14.17.0 || >=16.0.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/typescript-eslint" + } + }, + "node_modules/@typescript-eslint/types": { + "version": "5.3.1", + "resolved": "https://registry.npmjs.org/@typescript-eslint/types/-/types-5.3.1.tgz", + "integrity": "sha512-bG7HeBLolxKHtdHG54Uac750eXuQQPpdJfCYuw4ZI3bZ7+GgKClMWM8jExBtp7NSP4m8PmLRM8+lhzkYnSmSxQ==", + "dev": true, + "engines": { + "node": "^12.22.0 || ^14.17.0 || >=16.0.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/typescript-eslint" + } + }, + "node_modules/@typescript-eslint/typescript-estree": { + "version": "5.3.1", + "resolved": "https://registry.npmjs.org/@typescript-eslint/typescript-estree/-/typescript-estree-5.3.1.tgz", + "integrity": "sha512-PwFbh/PKDVo/Wct6N3w+E4rLZxUDgsoII/GrWM2A62ETOzJd4M6s0Mu7w4CWsZraTbaC5UQI+dLeyOIFF1PquQ==", + "dev": true, + "dependencies": { + "@typescript-eslint/types": "5.3.1", + "@typescript-eslint/visitor-keys": "5.3.1", + "debug": "^4.3.2", + "globby": "^11.0.4", + "is-glob": "^4.0.3", + "semver": "^7.3.5", + "tsutils": "^3.21.0" + }, + "engines": { + "node": "^12.22.0 || ^14.17.0 || >=16.0.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/typescript-eslint" + }, + "peerDependenciesMeta": { + "typescript": { + "optional": true + } + } + }, + "node_modules/@typescript-eslint/visitor-keys": { + "version": "5.3.1", + "resolved": "https://registry.npmjs.org/@typescript-eslint/visitor-keys/-/visitor-keys-5.3.1.tgz", + "integrity": "sha512-3cHUzUuVTuNHx0Gjjt5pEHa87+lzyqOiHXy/Gz+SJOCW1mpw9xQHIIEwnKn+Thph1mgWyZ90nboOcSuZr/jTTQ==", + "dev": true, + "dependencies": { + "@typescript-eslint/types": "5.3.1", + "eslint-visitor-keys": "^3.0.0" + }, + "engines": { + "node": "^12.22.0 || ^14.17.0 || >=16.0.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/typescript-eslint" + } + }, + "node_modules/@ungap/promise-all-settled": { + "version": "1.1.2", + "resolved": "https://registry.npmjs.org/@ungap/promise-all-settled/-/promise-all-settled-1.1.2.tgz", + "integrity": "sha512-sL/cEvJWAnClXw0wHk85/2L0G6Sj8UB0Ctc1TEMbKSsmpRosqhwj9gWgFRZSrBr2f9tiXISwNhCPmlfqUqyb9Q==", + "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==", + "dev": true, + "bin": { + "acorn": "bin/acorn" + }, + "engines": { + "node": ">=0.4.0" + } + }, + "node_modules/acorn-jsx": { + "version": "5.3.2", + "resolved": "https://registry.npmjs.org/acorn-jsx/-/acorn-jsx-5.3.2.tgz", + "integrity": "sha512-rq9s+JNhf0IChjtDXxllJ7g41oZk5SlXtp0LHwyA5cejwn7vKmKp4pPri6YEePv2PU65sAsegbXtIinmDFDXgQ==", + "dev": true, + "peerDependencies": { + "acorn": "^6.0.0 || ^7.0.0 || ^8.0.0" + } + }, + "node_modules/acorn-walk": { + "version": "8.2.0", + "resolved": "https://registry.npmjs.org/acorn-walk/-/acorn-walk-8.2.0.tgz", + "integrity": "sha512-k+iyHEuPgSw6SbuDpGQM+06HQUa04DZ3o+F6CSzXMvvI5KMvnaEqXe+YVe555R9nn6GPt404fos4wcgpw12SDA==", + "dev": true, + "engines": { + "node": ">=0.4.0" + } + }, + "node_modules/aggregate-error": { + "version": "3.1.0", + "resolved": "https://registry.npmjs.org/aggregate-error/-/aggregate-error-3.1.0.tgz", + "integrity": "sha512-4I7Td01quW/RpocfNayFdFVk1qSuoh0E7JrbRJ16nH01HhKFQ88INq9Sd+nd72zqRySlr9BmDA8xlEJ6vJMrYA==", + "dev": true, + "dependencies": { + "clean-stack": "^2.0.0", + "indent-string": "^4.0.0" + }, + "engines": { + "node": ">=8" + } + }, + "node_modules/ajv": { + "version": "6.12.6", + "resolved": "https://registry.npmjs.org/ajv/-/ajv-6.12.6.tgz", + "integrity": "sha512-j3fVLgvTo527anyYyJOGTYJbG+vnnQYvE0m5mmkc1TK+nxAppkCLMIL0aZ4dblVCNoGShhm+kzE4ZUykBoMg4g==", + "dev": true, + "dependencies": { + "fast-deep-equal": "^3.1.1", + "fast-json-stable-stringify": "^2.0.0", + "json-schema-traverse": "^0.4.1", + "uri-js": "^4.2.2" + }, + "funding": { + "type": "github", + "url": "https://github.com/sponsors/epoberezkin" + } + }, + "node_modules/ansi-align": { + "version": "3.0.1", + "resolved": "https://registry.npmjs.org/ansi-align/-/ansi-align-3.0.1.tgz", + "integrity": "sha512-IOfwwBF5iczOjp/WeY4YxyjqAFMQoZufdQWDd19SEExbVLNXqvpzSJ/M7Za4/sCPmQ0+GRquoA7bGcINcxew6w==", + "dev": true, + "dependencies": { + "string-width": "^4.1.0" + } + }, + "node_modules/ansi-colors": { + "version": "4.1.1", + "resolved": "https://registry.npmjs.org/ansi-colors/-/ansi-colors-4.1.1.tgz", + "integrity": "sha512-JoX0apGbHaUJBNl6yF+p6JAFYZ666/hhCGKN5t9QFjbJQKUU/g8MNbFDbvfrgKXvI1QpZplPOnwIo99lX/AAmA==", + "dev": true, + "engines": { + "node": ">=6" + } + }, + "node_modules/ansi-escapes": { + "version": "4.3.2", + "resolved": "https://registry.npmjs.org/ansi-escapes/-/ansi-escapes-4.3.2.tgz", + "integrity": "sha512-gKXj5ALrKWQLsYG9jlTRmR/xKluxHV+Z9QEwNIgCfM1/uwPMCuzVVnh5mwTd+OuBZcwSIMbqssNWRm1lE51QaQ==", + "dev": true, + "dependencies": { + "type-fest": "^0.21.3" + }, + "engines": { + "node": ">=8" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/ansi-escapes/node_modules/type-fest": { + "version": "0.21.3", + "resolved": "https://registry.npmjs.org/type-fest/-/type-fest-0.21.3.tgz", + "integrity": "sha512-t0rzBq87m3fVcduHDUFhKmyyX+9eo6WQjZvf51Ea/M0Q7+T374Jp1aUiyUl0GKxp8M/OETVHSDvmkyPgvX+X2w==", + "dev": true, + "engines": { + "node": ">=10" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/ansi-regex": { + "version": "5.0.1", + "resolved": "https://registry.npmjs.org/ansi-regex/-/ansi-regex-5.0.1.tgz", + "integrity": "sha512-quJQXlTSUGL2LH9SUXo8VwsY4soanhgo6LNSm84E1LBcE8s3O0wpdiRzyR9z/ZZJMlMWv37qOOb9pdJlMUEKFQ==", + "dev": true, + "engines": { + "node": ">=8" + } + }, + "node_modules/ansi-styles": { + "version": "4.3.0", + "resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-4.3.0.tgz", + "integrity": "sha512-zbB9rCJAT1rbjiVDb2hqKFHNYLxgtk8NURxZ3IZwD3F6NtxbXZQCnnSi1Lkx+IDohdPlFp222wVALIheZJQSEg==", + "dev": true, + "dependencies": { + "color-convert": "^2.0.1" + }, + "engines": { + "node": ">=8" + }, + "funding": { + "url": "https://github.com/chalk/ansi-styles?sponsor=1" + } + }, + "node_modules/anymatch": { + "version": "3.1.2", + "resolved": "https://registry.npmjs.org/anymatch/-/anymatch-3.1.2.tgz", + "integrity": "sha512-P43ePfOAIupkguHUycrc4qJ9kz8ZiuOUijaETwX7THt0Y/GNK7v0aa8rY816xWjZ7rJdA5XdMcpVFTKMq+RvWg==", + "dev": true, + "dependencies": { + "normalize-path": "^3.0.0", + "picomatch": "^2.0.4" + }, + "engines": { + "node": ">= 8" + } + }, + "node_modules/append-transform": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/append-transform/-/append-transform-2.0.0.tgz", + "integrity": "sha512-7yeyCEurROLQJFv5Xj4lEGTy0borxepjFv1g22oAdqFu//SrAlDl1O1Nxx15SH1RoliUml6p8dwJW9jvZughhg==", + "dev": true, + "dependencies": { + "default-require-extensions": "^3.0.0" + }, + "engines": { + "node": ">=8" + } + }, + "node_modules/archy": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/archy/-/archy-1.0.0.tgz", + "integrity": "sha1-+cjBN1fMHde8N5rHeyxipcKGjEA=", + "dev": true + }, + "node_modules/arg": { + "version": "4.1.3", + "resolved": "https://registry.npmjs.org/arg/-/arg-4.1.3.tgz", + "integrity": "sha512-58S9QDqG0Xx27YwPSt9fJxivjYl432YCwfDMfZ+71RAqUrZef7LrKQZ3LHLOwCS4FLNBplP533Zx895SeOCHvA==", + "dev": true + }, + "node_modules/argparse": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/argparse/-/argparse-2.0.1.tgz", + "integrity": "sha512-8+9WqebbFzpX9OR+Wa6O29asIogeRMzcGtAINdpMHHyAg10f05aSFVBbcEqGf/PXw1EjAZ+q2/bEBg3DvurK3Q==", + "dev": true + }, + "node_modules/array-union": { + "version": "2.1.0", + "resolved": "https://registry.npmjs.org/array-union/-/array-union-2.1.0.tgz", + "integrity": "sha512-HGyxoOTYUyCM6stUe6EJgnd4EoewAI7zMdfqO+kGjnlZmBDz/cR5pf8r/cR4Wq60sL/p0IkcjUEEPwS3GFrIyw==", + "dev": true, + "engines": { + "node": ">=8" + } + }, + "node_modules/async-retry": { + "version": "1.3.3", + "resolved": "https://registry.npmjs.org/async-retry/-/async-retry-1.3.3.tgz", + "integrity": "sha512-wfr/jstw9xNi/0teMHrRW7dsz3Lt5ARhYNZ2ewpadnhaIp5mbALhOAP+EAdsC7t4Z6wqsDVv9+W6gm1Dk9mEyw==", + "dev": true, + "dependencies": { + "retry": "0.13.1" + } + }, + "node_modules/asynckit": { + "version": "0.4.0", + "resolved": "https://registry.npmjs.org/asynckit/-/asynckit-0.4.0.tgz", + "integrity": "sha1-x57Zf380y48robyXkLzDZkdLS3k=", + "dev": true + }, + "node_modules/balanced-match": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/balanced-match/-/balanced-match-1.0.2.tgz", + "integrity": "sha512-3oSeUO0TMV67hN1AmbXsK4yaqU7tjiHlbxRDZOpH0KW9+CeX4bRAaX0Anxt0tx2MrpRpWwQaPwIlISEJhYU5Pw==", + "dev": true + }, + "node_modules/base64-js": { + "version": "1.5.1", + "resolved": "https://registry.npmjs.org/base64-js/-/base64-js-1.5.1.tgz", + "integrity": "sha512-AKpaYlHn8t4SVbOHCy+b5+KKgvR4vrsD8vbvrbiQJps7fKDTkjkDry6ji0rUJjC0kzbNePLwzxq8iypo41qeWA==", + "dev": true, + "funding": [ + { + "type": "github", + "url": "https://github.com/sponsors/feross" + }, + { + "type": "patreon", + "url": "https://www.patreon.com/feross" + }, + { + "type": "consulting", + "url": "https://feross.org/support" + } + ] + }, + "node_modules/before-after-hook": { + "version": "2.2.2", + "resolved": "https://registry.npmjs.org/before-after-hook/-/before-after-hook-2.2.2.tgz", + "integrity": "sha512-3pZEU3NT5BFUo/AD5ERPWOgQOCZITni6iavr5AUw5AUwQjMlI0kzu5btnyD39AF0gUEsDPwJT+oY1ORBJijPjQ==", + "dev": true + }, + "node_modules/binary-extensions": { + "version": "2.2.0", + "resolved": "https://registry.npmjs.org/binary-extensions/-/binary-extensions-2.2.0.tgz", + "integrity": "sha512-jDctJ/IVQbZoJykoeHbhXpOlNBqGNcwXJKJog42E5HDPUwQTSdjCHdihjj0DlnheQ7blbT6dHOafNAiS8ooQKA==", + "dev": true, + "engines": { + "node": ">=8" + } + }, + "node_modules/bl": { + "version": "4.1.0", + "resolved": "https://registry.npmjs.org/bl/-/bl-4.1.0.tgz", + "integrity": "sha512-1W07cM9gS6DcLperZfFSj+bWLtaPGSOHWhPiGzXmvVJbRLdG82sH/Kn8EtW1VqWVA54AKf2h5k5BbnIbwF3h6w==", + "dev": true, + "dependencies": { + "buffer": "^5.5.0", + "inherits": "^2.0.4", + "readable-stream": "^3.4.0" + } + }, + "node_modules/boxen": { + "version": "5.1.2", + "resolved": "https://registry.npmjs.org/boxen/-/boxen-5.1.2.tgz", + "integrity": "sha512-9gYgQKXx+1nP8mP7CzFyaUARhg7D3n1dF/FnErWmu9l6JvGpNUN278h0aSb+QjoiKSWG+iZ3uHrcqk0qrY9RQQ==", + "dev": true, + "dependencies": { + "ansi-align": "^3.0.0", + "camelcase": "^6.2.0", + "chalk": "^4.1.0", + "cli-boxes": "^2.2.1", + "string-width": "^4.2.2", + "type-fest": "^0.20.2", + "widest-line": "^3.1.0", + "wrap-ansi": "^7.0.0" + }, + "engines": { + "node": ">=10" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/boxen/node_modules/camelcase": { + "version": "6.2.0", + "resolved": "https://registry.npmjs.org/camelcase/-/camelcase-6.2.0.tgz", + "integrity": "sha512-c7wVvbw3f37nuobQNtgsgG9POC9qMbNuMQmTCqZv23b6MIz0fcYpBiOlv9gEN/hdLdnZTDQhg6e9Dq5M1vKvfg==", + "dev": true, + "engines": { + "node": ">=10" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/brace-expansion": { + "version": "1.1.11", + "resolved": "https://registry.npmjs.org/brace-expansion/-/brace-expansion-1.1.11.tgz", + "integrity": "sha512-iCuPHDFgrHX7H2vEI/5xpz07zSHB00TpugqhmYtVmMO6518mCuRMoOYFldEBl0g187ufozdaHgWKcYFb61qGiA==", + "dev": true, + "dependencies": { + "balanced-match": "^1.0.0", + "concat-map": "0.0.1" + } + }, + "node_modules/braces": { + "version": "3.0.2", + "resolved": "https://registry.npmjs.org/braces/-/braces-3.0.2.tgz", + "integrity": "sha512-b8um+L1RzM3WDSzvhm6gIz1yfTbBt6YTlcEKAvsmqCZZFw46z626lVj9j1yEPW33H5H+lBQpZMP1k8l+78Ha0A==", + "dev": true, + "dependencies": { + "fill-range": "^7.0.1" + }, + "engines": { + "node": ">=8" + } + }, + "node_modules/browser-stdout": { + "version": "1.3.1", + "resolved": "https://registry.npmjs.org/browser-stdout/-/browser-stdout-1.3.1.tgz", + "integrity": "sha512-qhAVI1+Av2X7qelOfAIYwXONood6XlZE/fXaBSmW/T5SzLAmCgzi+eiWE7fUvbHaeNBQH13UftjpXxsfLkMpgw==", + "dev": true + }, + "node_modules/browserslist": { + "version": "4.17.6", + "resolved": "https://registry.npmjs.org/browserslist/-/browserslist-4.17.6.tgz", + "integrity": "sha512-uPgz3vyRTlEiCv4ee9KlsKgo2V6qPk7Jsn0KAn2OBqbqKo3iNcPEC1Ti6J4dwnz+aIRfEEEuOzC9IBk8tXUomw==", + "dev": true, + "dependencies": { + "caniuse-lite": "^1.0.30001274", + "electron-to-chromium": "^1.3.886", + "escalade": "^3.1.1", + "node-releases": "^2.0.1", + "picocolors": "^1.0.0" + }, + "bin": { + "browserslist": "cli.js" + }, + "engines": { + "node": "^6 || ^7 || ^8 || ^9 || ^10 || ^11 || ^12 || >=13.7" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/browserslist" + } + }, + "node_modules/buffer": { + "version": "5.7.1", + "resolved": "https://registry.npmjs.org/buffer/-/buffer-5.7.1.tgz", + "integrity": "sha512-EHcyIPBQ4BSGlvjB16k5KgAJ27CIsHY/2JBmCRReo48y9rQ3MaUzWX3KVlBa4U7MyX02HdVj0K7C3WaB3ju7FQ==", + "dev": true, + "funding": [ + { + "type": "github", + "url": "https://github.com/sponsors/feross" + }, + { + "type": "patreon", + "url": "https://www.patreon.com/feross" + }, + { + "type": "consulting", + "url": "https://feross.org/support" + } + ], + "dependencies": { + "base64-js": "^1.3.1", + "ieee754": "^1.1.13" + } + }, + "node_modules/buffer-from": { + "version": "1.1.2", + "resolved": "https://registry.npmjs.org/buffer-from/-/buffer-from-1.1.2.tgz", + "integrity": "sha512-E+XQCRwSbaaiChtv6k6Dwgc+bx+Bs6vuKJHHl5kox/BaKbhiXzqQOwK4cO22yElGp2OCmjwVhT3HmxgyPGnJfQ==", + "dev": true + }, + "node_modules/cacheable-lookup": { + "version": "5.0.4", + "resolved": "https://registry.npmjs.org/cacheable-lookup/-/cacheable-lookup-5.0.4.tgz", + "integrity": "sha512-2/kNscPhpcxrOigMZzbiWF7dz8ilhb/nIHU3EyZiXWXpeq/au8qJ8VhdftMkty3n7Gj6HIGalQG8oiBNB3AJgA==", + "dev": true, + "engines": { + "node": ">=10.6.0" + } + }, + "node_modules/cacheable-request": { + "version": "7.0.2", + "resolved": "https://registry.npmjs.org/cacheable-request/-/cacheable-request-7.0.2.tgz", + "integrity": "sha512-pouW8/FmiPQbuGpkXQ9BAPv/Mo5xDGANgSNXzTzJ8DrKGuXOssM4wIQRjfanNRh3Yu5cfYPvcorqbhg2KIJtew==", + "dev": true, + "dependencies": { + "clone-response": "^1.0.2", + "get-stream": "^5.1.0", + "http-cache-semantics": "^4.0.0", + "keyv": "^4.0.0", + "lowercase-keys": "^2.0.0", + "normalize-url": "^6.0.1", + "responselike": "^2.0.0" + }, + "engines": { + "node": ">=8" + } + }, + "node_modules/cacheable-request/node_modules/get-stream": { + "version": "5.2.0", + "resolved": "https://registry.npmjs.org/get-stream/-/get-stream-5.2.0.tgz", + "integrity": "sha512-nBF+F1rAZVCu/p7rjzgA+Yb4lfYXrpl7a6VmJrU8wF9I1CKvP/QwPNZHnOlwbTkY6dvtFIzFMSyQXbLoTQPRpA==", + "dev": true, + "dependencies": { + "pump": "^3.0.0" + }, + "engines": { + "node": ">=8" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/caching-transform": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/caching-transform/-/caching-transform-4.0.0.tgz", + "integrity": "sha512-kpqOvwXnjjN44D89K5ccQC+RUrsy7jB/XLlRrx0D7/2HNcTPqzsb6XgYoErwko6QsV184CA2YgS1fxDiiDZMWA==", + "dev": true, + "dependencies": { + "hasha": "^5.0.0", + "make-dir": "^3.0.0", + "package-hash": "^4.0.0", + "write-file-atomic": "^3.0.0" + }, + "engines": { + "node": ">=8" + } + }, + "node_modules/call-bind": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/call-bind/-/call-bind-1.0.2.tgz", + "integrity": "sha512-7O+FbCihrB5WGbFYesctwmTKae6rOiIzmz1icreWJ+0aA7LJfuqhEso2T9ncpcFtzMQtzXf2QGGueWJGTYsqrA==", + "dev": true, + "dependencies": { + "function-bind": "^1.1.1", + "get-intrinsic": "^1.0.2" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/callsites": { + "version": "3.1.0", + "resolved": "https://registry.npmjs.org/callsites/-/callsites-3.1.0.tgz", + "integrity": "sha512-P8BjAsXvZS+VIDUI11hHCQEv74YT67YUi5JJFNWIqL235sBmjX4+qx9Muvls5ivyNENctx46xQLQ3aTuE7ssaQ==", + "dev": true, + "engines": { + "node": ">=6" + } + }, + "node_modules/camelcase": { + "version": "5.3.1", + "resolved": "https://registry.npmjs.org/camelcase/-/camelcase-5.3.1.tgz", + "integrity": "sha512-L28STB170nwWS63UjtlEOE3dldQApaJXZkOI1uMFfzf3rRuPegHaHesyee+YxQ+W6SvRDQV6UrdOdRiR153wJg==", + "dev": true, + "engines": { + "node": ">=6" + } + }, + "node_modules/caniuse-lite": { + "version": "1.0.30001279", + "resolved": "https://registry.npmjs.org/caniuse-lite/-/caniuse-lite-1.0.30001279.tgz", + "integrity": "sha512-VfEHpzHEXj6/CxggTwSFoZBBYGQfQv9Cf42KPlO79sWXCD1QNKWKsKzFeWL7QpZHJQYAvocqV6Rty1yJMkqWLQ==", + "dev": true, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/browserslist" + } + }, + "node_modules/chalk": { + "version": "4.1.2", + "resolved": "https://registry.npmjs.org/chalk/-/chalk-4.1.2.tgz", + "integrity": "sha512-oKnbhFyRIXpUuez8iBMmyEa4nbj4IOQyuhc/wy9kY7/WVPcwIO9VA668Pu8RkO7+0G76SLROeyw9CpQ061i4mA==", + "dev": true, + "dependencies": { + "ansi-styles": "^4.1.0", + "supports-color": "^7.1.0" + }, + "engines": { + "node": ">=10" + }, + "funding": { + "url": "https://github.com/chalk/chalk?sponsor=1" + } + }, + "node_modules/chardet": { + "version": "0.7.0", + "resolved": "https://registry.npmjs.org/chardet/-/chardet-0.7.0.tgz", + "integrity": "sha512-mT8iDcrh03qDGRRmoA2hmBJnxpllMR+0/0qlzjqZES6NdiWDcZkCNAk4rPFZ9Q85r27unkiNNg8ZOiwZXBHwcA==", + "dev": true + }, + "node_modules/chokidar": { + "version": "3.5.2", + "resolved": "https://registry.npmjs.org/chokidar/-/chokidar-3.5.2.tgz", + "integrity": "sha512-ekGhOnNVPgT77r4K/U3GDhu+FQ2S8TnK/s2KbIGXi0SZWuwkZ2QNyfWdZW+TVfn84DpEP7rLeCt2UI6bJ8GwbQ==", + "dev": true, + "dependencies": { + "anymatch": "~3.1.2", + "braces": "~3.0.2", + "glob-parent": "~5.1.2", + "is-binary-path": "~2.1.0", + "is-glob": "~4.0.1", + "normalize-path": "~3.0.0", + "readdirp": "~3.6.0" + }, + "engines": { + "node": ">= 8.10.0" + }, + "optionalDependencies": { + "fsevents": "~2.3.2" + } + }, + "node_modules/chokidar/node_modules/glob-parent": { + "version": "5.1.2", + "resolved": "https://registry.npmjs.org/glob-parent/-/glob-parent-5.1.2.tgz", + "integrity": "sha512-AOIgSQCepiJYwP3ARnGx+5VnTu2HBYdzbGP45eLw1vr3zB3vZLeyed1sC9hnbcOc9/SrMyM5RPQrkGz4aS9Zow==", + "dev": true, + "dependencies": { + "is-glob": "^4.0.1" + }, + "engines": { + "node": ">= 6" + } + }, + "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==", + "dev": true + }, + "node_modules/clean-stack": { + "version": "2.2.0", + "resolved": "https://registry.npmjs.org/clean-stack/-/clean-stack-2.2.0.tgz", + "integrity": "sha512-4diC9HaTE+KRAMWhDhrGOECgWZxoevMc5TlkObMqNSsVU62PYzXZ/SMTjzyGAFF1YusgxGcSWTEXBhp0CPwQ1A==", + "dev": true, + "engines": { + "node": ">=6" + } + }, + "node_modules/cli-boxes": { + "version": "2.2.1", + "resolved": "https://registry.npmjs.org/cli-boxes/-/cli-boxes-2.2.1.tgz", + "integrity": "sha512-y4coMcylgSCdVinjiDBuR8PCC2bLjyGTwEmPb9NHR/QaNU6EUOXcTY/s6VjGMD6ENSEaeQYHCY0GNGS5jfMwPw==", + "dev": true, + "engines": { + "node": ">=6" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/cli-cursor": { + "version": "3.1.0", + "resolved": "https://registry.npmjs.org/cli-cursor/-/cli-cursor-3.1.0.tgz", + "integrity": "sha512-I/zHAwsKf9FqGoXM4WWRACob9+SNukZTd94DWF57E4toouRulbCxcUh6RKUEOQlYTHJnzkPMySvPNaaSLNfLZw==", + "dev": true, + "dependencies": { + "restore-cursor": "^3.1.0" + }, + "engines": { + "node": ">=8" + } + }, + "node_modules/cli-spinners": { + "version": "2.6.1", + "resolved": "https://registry.npmjs.org/cli-spinners/-/cli-spinners-2.6.1.tgz", + "integrity": "sha512-x/5fWmGMnbKQAaNwN+UZlV79qBLM9JFnJuJ03gIi5whrob0xV0ofNVHy9DhwGdsMJQc2OKv0oGmLzvaqvAVv+g==", + "dev": true, + "engines": { + "node": ">=6" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/cli-width": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/cli-width/-/cli-width-3.0.0.tgz", + "integrity": "sha512-FxqpkPPwu1HjuN93Omfm4h8uIanXofW0RxVEW3k5RKx+mJJYSthzNhp32Kzxxy3YAEZ/Dc/EWN1vZRY0+kOhbw==", + "dev": true, + "engines": { + "node": ">= 10" + } + }, + "node_modules/cliui": { + "version": "7.0.4", + "resolved": "https://registry.npmjs.org/cliui/-/cliui-7.0.4.tgz", + "integrity": "sha512-OcRE68cOsVMXp1Yvonl/fzkQOyjLSu/8bhPDfQt0e0/Eb283TKP20Fs2MqoPsr9SwA595rRCA+QMzYc9nBP+JQ==", + "dev": true, + "dependencies": { + "string-width": "^4.2.0", + "strip-ansi": "^6.0.0", + "wrap-ansi": "^7.0.0" + } + }, + "node_modules/clone": { + "version": "1.0.4", + "resolved": "https://registry.npmjs.org/clone/-/clone-1.0.4.tgz", + "integrity": "sha1-2jCcwmPfFZlMaIypAheco8fNfH4=", + "dev": true, + "engines": { + "node": ">=0.8" + } + }, + "node_modules/clone-response": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/clone-response/-/clone-response-1.0.2.tgz", + "integrity": "sha1-0dyXOSAxTfZ/vrlCI7TuNQI56Ws=", + "dev": true, + "dependencies": { + "mimic-response": "^1.0.0" + } + }, + "node_modules/cluster-key-slot": { + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/cluster-key-slot/-/cluster-key-slot-1.1.0.tgz", + "integrity": "sha512-2Nii8p3RwAPiFwsnZvukotvow2rIHM+yQ6ZcBXGHdniadkYGZYiGmkHJIbZPIV9nfv7m/U1IPMVVcAhoWFeklw==", + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/color-convert": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/color-convert/-/color-convert-2.0.1.tgz", + "integrity": "sha512-RRECPsj7iu/xb5oKYcsFHSppFNnsj/52OVTRKb4zP5onXwVF3zVmmToNcOfGC+CRDpfK/U584fMg38ZHCaElKQ==", + "dev": true, + "dependencies": { + "color-name": "~1.1.4" + }, + "engines": { + "node": ">=7.0.0" + } + }, + "node_modules/color-name": { + "version": "1.1.4", + "resolved": "https://registry.npmjs.org/color-name/-/color-name-1.1.4.tgz", + "integrity": "sha512-dOy+3AuW3a2wNbZHIuMZpTcgjGuLU/uBL/ubcZF9OXbDo8ff4O8yVp5Bf0efS8uEoYo5q4Fx7dY9OgQGXgAsQA==", + "dev": true + }, + "node_modules/combined-stream": { + "version": "1.0.8", + "resolved": "https://registry.npmjs.org/combined-stream/-/combined-stream-1.0.8.tgz", + "integrity": "sha512-FQN4MRfuJeHf7cBbBMJFXhKSDq+2kAArBlmRBvcvFE5BB1HZKXtSFASDhdlz9zOYwxh8lDdnvmMOe/+5cdoEdg==", + "dev": true, + "dependencies": { + "delayed-stream": "~1.0.0" + }, + "engines": { + "node": ">= 0.8" + } + }, + "node_modules/commondir": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/commondir/-/commondir-1.0.1.tgz", + "integrity": "sha1-3dgA2gxmEnOTzKWVDqloo6rxJTs=", + "dev": true + }, + "node_modules/concat-map": { + "version": "0.0.1", + "resolved": "https://registry.npmjs.org/concat-map/-/concat-map-0.0.1.tgz", + "integrity": "sha1-2Klr13/Wjfd5OnMDajug1UBdR3s=", + "dev": true + }, + "node_modules/configstore": { + "version": "5.0.1", + "resolved": "https://registry.npmjs.org/configstore/-/configstore-5.0.1.tgz", + "integrity": "sha512-aMKprgk5YhBNyH25hj8wGt2+D52Sw1DRRIzqBwLp2Ya9mFmY8KPvvtvmna8SxVR9JMZ4kzMD68N22vlaRpkeFA==", + "dev": true, + "dependencies": { + "dot-prop": "^5.2.0", + "graceful-fs": "^4.1.2", + "make-dir": "^3.0.0", + "unique-string": "^2.0.0", + "write-file-atomic": "^3.0.0", + "xdg-basedir": "^4.0.0" + }, + "engines": { + "node": ">=8" + } + }, + "node_modules/convert-source-map": { + "version": "1.8.0", + "resolved": "https://registry.npmjs.org/convert-source-map/-/convert-source-map-1.8.0.tgz", + "integrity": "sha512-+OQdjP49zViI/6i7nIJpA8rAl4sV/JdPfU9nZs3VqOwGIgizICvuN2ru6fMd+4llL0tar18UYJXfZ/TWtmhUjA==", + "dev": true, + "dependencies": { + "safe-buffer": "~5.1.1" + } + }, + "node_modules/cosmiconfig": { + "version": "7.0.1", + "resolved": "https://registry.npmjs.org/cosmiconfig/-/cosmiconfig-7.0.1.tgz", + "integrity": "sha512-a1YWNUV2HwGimB7dU2s1wUMurNKjpx60HxBB6xUM8Re+2s1g1IIfJvFR0/iCF+XHdE0GMTKTuLR32UQff4TEyQ==", + "dev": true, + "dependencies": { + "@types/parse-json": "^4.0.0", + "import-fresh": "^3.2.1", + "parse-json": "^5.0.0", + "path-type": "^4.0.0", + "yaml": "^1.10.0" + }, + "engines": { + "node": ">=10" + } + }, + "node_modules/create-require": { + "version": "1.1.1", + "resolved": "https://registry.npmjs.org/create-require/-/create-require-1.1.1.tgz", + "integrity": "sha512-dcKFX3jn0MpIaXjisoRvexIJVEKzaq7z2rZKxf+MSr9TkdmHmsU4m2lcLojrj/FHl8mk5VxMmYA+ftRkP/3oKQ==", + "dev": true + }, + "node_modules/cross-spawn": { + "version": "7.0.3", + "resolved": "https://registry.npmjs.org/cross-spawn/-/cross-spawn-7.0.3.tgz", + "integrity": "sha512-iRDPJKUPVEND7dHPO8rkbOnPpyDygcDFtWjpeWNCgy8WP2rXcxXL8TskReQl6OrB2G7+UJrags1q15Fudc7G6w==", + "dev": true, + "dependencies": { + "path-key": "^3.1.0", + "shebang-command": "^2.0.0", + "which": "^2.0.1" + }, + "engines": { + "node": ">= 8" + } + }, + "node_modules/crypto-random-string": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/crypto-random-string/-/crypto-random-string-2.0.0.tgz", + "integrity": "sha512-v1plID3y9r/lPhviJ1wrXpLeyUIGAZ2SHNYTEapm7/8A9nLPoyvVp3RK/EPFqn5kEznyWgYZNsRtYYIWbuG8KA==", + "dev": true, + "engines": { + "node": ">=8" + } + }, + "node_modules/debug": { + "version": "4.3.2", + "resolved": "https://registry.npmjs.org/debug/-/debug-4.3.2.tgz", + "integrity": "sha512-mOp8wKcvj7XxC78zLgw/ZA+6TSgkoE2C/ienthhRD298T7UNwAg9diBpLRxC0mOezLl4B0xV7M0cCO6P/O0Xhw==", + "dev": true, + "dependencies": { + "ms": "2.1.2" + }, + "engines": { + "node": ">=6.0" + }, + "peerDependenciesMeta": { + "supports-color": { + "optional": true + } + } + }, + "node_modules/decamelize": { + "version": "1.2.0", + "resolved": "https://registry.npmjs.org/decamelize/-/decamelize-1.2.0.tgz", + "integrity": "sha1-9lNNFRSCabIDUue+4m9QH5oZEpA=", + "dev": true, + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/decode-uri-component": { + "version": "0.2.0", + "resolved": "https://registry.npmjs.org/decode-uri-component/-/decode-uri-component-0.2.0.tgz", + "integrity": "sha1-6zkTMzRYd1y4TNGh+uBiEGu4dUU=", + "dev": true, + "engines": { + "node": ">=0.10" + } + }, + "node_modules/decompress-response": { + "version": "6.0.0", + "resolved": "https://registry.npmjs.org/decompress-response/-/decompress-response-6.0.0.tgz", + "integrity": "sha512-aW35yZM6Bb/4oJlZncMH2LCoZtJXTRxES17vE3hoRiowU2kWHaJKFkSBDnDR+cm9J+9QhXmREyIfv0pji9ejCQ==", + "dev": true, + "dependencies": { + "mimic-response": "^3.1.0" + }, + "engines": { + "node": ">=10" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/decompress-response/node_modules/mimic-response": { + "version": "3.1.0", + "resolved": "https://registry.npmjs.org/mimic-response/-/mimic-response-3.1.0.tgz", + "integrity": "sha512-z0yWI+4FDrrweS8Zmt4Ej5HdJmky15+L2e6Wgn3+iK5fWzb6T3fhNFq2+MeTRb064c6Wr4N/wv0DzQTjNzHNGQ==", + "dev": true, + "engines": { + "node": ">=10" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/deep-extend": { + "version": "0.6.0", + "resolved": "https://registry.npmjs.org/deep-extend/-/deep-extend-0.6.0.tgz", + "integrity": "sha512-LOHxIOaPYdHlJRtCQfDIVZtfw/ufM8+rVj649RIHzcm/vGwQRXFt6OPqIFWsm2XEMrNIEtWR64sY1LEKD2vAOA==", + "dev": true, + "engines": { + "node": ">=4.0.0" + } + }, + "node_modules/deep-is": { + "version": "0.1.4", + "resolved": "https://registry.npmjs.org/deep-is/-/deep-is-0.1.4.tgz", + "integrity": "sha512-oIPzksmTg4/MriiaYGO+okXDT7ztn/w3Eptv/+gSIdMdKsJo0u4CfYNFJPy+4SKMuCqGw2wxnA+URMg3t8a/bQ==", + "dev": true + }, + "node_modules/default-require-extensions": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/default-require-extensions/-/default-require-extensions-3.0.0.tgz", + "integrity": "sha512-ek6DpXq/SCpvjhpFsLFRVtIxJCRw6fUR42lYMVZuUMK7n8eMz4Uh5clckdBjEpLhn/gEBZo7hDJnJcwdKLKQjg==", + "dev": true, + "dependencies": { + "strip-bom": "^4.0.0" + }, + "engines": { + "node": ">=8" + } + }, + "node_modules/defaults": { + "version": "1.0.3", + "resolved": "https://registry.npmjs.org/defaults/-/defaults-1.0.3.tgz", + "integrity": "sha1-xlYFHpgX2f8I7YgUd/P+QBnz730=", + "dev": true, + "dependencies": { + "clone": "^1.0.2" + } + }, + "node_modules/defer-to-connect": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/defer-to-connect/-/defer-to-connect-2.0.1.tgz", + "integrity": "sha512-4tvttepXG1VaYGrRibk5EwJd1t4udunSOVMdLSAL6mId1ix438oPwPZMALY41FCijukO1L0twNcGsdzS7dHgDg==", + "dev": true, + "engines": { + "node": ">=10" + } + }, + "node_modules/delayed-stream": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/delayed-stream/-/delayed-stream-1.0.0.tgz", + "integrity": "sha1-3zrhmayt+31ECqrgsp4icrJOxhk=", + "dev": true, + "engines": { + "node": ">=0.4.0" + } + }, + "node_modules/deprecated-obj": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/deprecated-obj/-/deprecated-obj-2.0.0.tgz", + "integrity": "sha512-CkdywZC2rJ8RGh+y3MM1fw1EJ4oO/oNExGbRFv0AQoMS+faTd3nO7slYjkj/6t8OnIMUE+wxh6G97YHhK1ytrw==", + "dev": true, + "dependencies": { + "flat": "^5.0.2", + "lodash": "^4.17.20" + }, + "engines": { + "node": ">=10" + } + }, + "node_modules/deprecation": { + "version": "2.3.1", + "resolved": "https://registry.npmjs.org/deprecation/-/deprecation-2.3.1.tgz", + "integrity": "sha512-xmHIy4F3scKVwMsQ4WnVaS8bHOx0DmVwRywosKhaILI0ywMDWPtBSku2HNxRvF7jtwDRsoEwYQSfbxj8b7RlJQ==", + "dev": true + }, + "node_modules/diff": { + "version": "5.0.0", + "resolved": "https://registry.npmjs.org/diff/-/diff-5.0.0.tgz", + "integrity": "sha512-/VTCrvm5Z0JGty/BWHljh+BAiw3IK+2j87NGMu8Nwc/f48WoDAC395uomO9ZD117ZOBaHmkX1oyLvkVM/aIT3w==", + "dev": true, + "engines": { + "node": ">=0.3.1" + } + }, + "node_modules/dir-glob": { + "version": "3.0.1", + "resolved": "https://registry.npmjs.org/dir-glob/-/dir-glob-3.0.1.tgz", + "integrity": "sha512-WkrWp9GR4KXfKGYzOLmTuGVi1UWFfws377n9cc55/tb6DuqyF6pcQ5AbiHEshaDpY9v6oaSr2XCDidGmMwdzIA==", + "dev": true, + "dependencies": { + "path-type": "^4.0.0" + }, + "engines": { + "node": ">=8" + } + }, + "node_modules/doctrine": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/doctrine/-/doctrine-3.0.0.tgz", + "integrity": "sha512-yS+Q5i3hBf7GBkd4KG8a7eBNNWNGLTaEwwYWUijIYM7zrlYDM0BFXHjjPWlWZ1Rg7UaddZeIDmi9jF3HmqiQ2w==", + "dev": true, + "dependencies": { + "esutils": "^2.0.2" + }, + "engines": { + "node": ">=6.0.0" + } + }, + "node_modules/dot-prop": { + "version": "5.3.0", + "resolved": "https://registry.npmjs.org/dot-prop/-/dot-prop-5.3.0.tgz", + "integrity": "sha512-QM8q3zDe58hqUqjraQOmzZ1LIH9SWQJTlEKCH4kJ2oQvLZk7RbQXvtDM2XEq3fwkV9CCvvH4LA0AV+ogFsBM2Q==", + "dev": true, + "dependencies": { + "is-obj": "^2.0.0" + }, + "engines": { + "node": ">=8" + } + }, + "node_modules/duplexer3": { + "version": "0.1.4", + "resolved": "https://registry.npmjs.org/duplexer3/-/duplexer3-0.1.4.tgz", + "integrity": "sha1-7gHdHKwO08vH/b6jfcCo8c4ALOI=", + "dev": true + }, + "node_modules/electron-to-chromium": { + "version": "1.3.893", + "resolved": "https://registry.npmjs.org/electron-to-chromium/-/electron-to-chromium-1.3.893.tgz", + "integrity": "sha512-ChtwF7qB03INq1SyMpue08wc6cve+ktj2UC/Y7se9vB+JryfzziJeYwsgb8jLaCA5GMkHCdn5M62PfSMWhifZg==", + "dev": true + }, + "node_modules/emoji-regex": { + "version": "8.0.0", + "resolved": "https://registry.npmjs.org/emoji-regex/-/emoji-regex-8.0.0.tgz", + "integrity": "sha512-MSjYzcWNOA0ewAHpz0MxpYFvwg6yjy1NG3xteoqz644VCo/RPgnr1/GGt+ic3iJTzQ8Eu3TdM14SawnVUmGE6A==", + "dev": true + }, + "node_modules/end-of-stream": { + "version": "1.4.4", + "resolved": "https://registry.npmjs.org/end-of-stream/-/end-of-stream-1.4.4.tgz", + "integrity": "sha512-+uw1inIHVPQoaVuHzRyXd21icM+cnt4CzD5rW+NC1wjOUSTOs+Te7FOv7AhN7vS9x/oIyhLP5PR1H+phQAHu5Q==", + "dev": true, + "dependencies": { + "once": "^1.4.0" + } + }, + "node_modules/enquirer": { + "version": "2.3.6", + "resolved": "https://registry.npmjs.org/enquirer/-/enquirer-2.3.6.tgz", + "integrity": "sha512-yjNnPr315/FjS4zIsUxYguYUPP2e1NK4d7E7ZOLiyYCcbFBiTMyID+2wvm2w6+pZ/odMA7cRkjhsPbltwBOrLg==", + "dev": true, + "dependencies": { + "ansi-colors": "^4.1.1" + }, + "engines": { + "node": ">=8.6" + } + }, + "node_modules/error-ex": { + "version": "1.3.2", + "resolved": "https://registry.npmjs.org/error-ex/-/error-ex-1.3.2.tgz", + "integrity": "sha512-7dFHNmqeFSEt2ZBsCriorKnn3Z2pj+fd9kmI6QoWw4//DL+icEBfc0U7qJCisqrTsKTjw4fNFy2pW9OqStD84g==", + "dev": true, + "dependencies": { + "is-arrayish": "^0.2.1" + } + }, + "node_modules/es6-error": { + "version": "4.1.1", + "resolved": "https://registry.npmjs.org/es6-error/-/es6-error-4.1.1.tgz", + "integrity": "sha512-Um/+FxMr9CISWh0bi5Zv0iOD+4cFh5qLeks1qhAopKVAJw3drgKbKySikp7wGhDL0HPeaja0P5ULZrxLkniUVg==", + "dev": true + }, + "node_modules/escalade": { + "version": "3.1.1", + "resolved": "https://registry.npmjs.org/escalade/-/escalade-3.1.1.tgz", + "integrity": "sha512-k0er2gUkLf8O0zKJiAhmkTnJlTvINGv7ygDNPbeIsX/TJjGJZHuh9B2UxbsaEkmlEo9MfhrSzmhIlhRlI2GXnw==", + "dev": true, + "engines": { + "node": ">=6" + } + }, + "node_modules/escape-goat": { + "version": "2.1.1", + "resolved": "https://registry.npmjs.org/escape-goat/-/escape-goat-2.1.1.tgz", + "integrity": "sha512-8/uIhbG12Csjy2JEW7D9pHbreaVaS/OpN3ycnyvElTdwM5n6GY6W6e2IPemfvGZeUMqZ9A/3GqIZMgKnBhAw/Q==", + "dev": true, + "engines": { + "node": ">=8" + } + }, + "node_modules/escape-string-regexp": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/escape-string-regexp/-/escape-string-regexp-4.0.0.tgz", + "integrity": "sha512-TtpcNJ3XAzx3Gq8sWRzJaVajRs0uVxA2YAkdb1jm2YkPz4G6egUFAyA3n5vtEIZefPk5Wa4UXbKuS5fKkJWdgA==", + "dev": true, + "engines": { + "node": ">=10" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/eslint": { + "version": "8.2.0", + "resolved": "https://registry.npmjs.org/eslint/-/eslint-8.2.0.tgz", + "integrity": "sha512-erw7XmM+CLxTOickrimJ1SiF55jiNlVSp2qqm0NuBWPtHYQCegD5ZMaW0c3i5ytPqL+SSLaCxdvQXFPLJn+ABw==", + "dev": true, + "dependencies": { + "@eslint/eslintrc": "^1.0.4", + "@humanwhocodes/config-array": "^0.6.0", + "ajv": "^6.10.0", + "chalk": "^4.0.0", + "cross-spawn": "^7.0.2", + "debug": "^4.3.2", + "doctrine": "^3.0.0", + "enquirer": "^2.3.5", + "escape-string-regexp": "^4.0.0", + "eslint-scope": "^6.0.0", + "eslint-utils": "^3.0.0", + "eslint-visitor-keys": "^3.0.0", + "espree": "^9.0.0", + "esquery": "^1.4.0", + "esutils": "^2.0.2", + "fast-deep-equal": "^3.1.3", + "file-entry-cache": "^6.0.1", + "functional-red-black-tree": "^1.0.1", + "glob-parent": "^6.0.1", + "globals": "^13.6.0", + "ignore": "^4.0.6", + "import-fresh": "^3.0.0", + "imurmurhash": "^0.1.4", + "is-glob": "^4.0.0", + "js-yaml": "^4.1.0", + "json-stable-stringify-without-jsonify": "^1.0.1", + "levn": "^0.4.1", + "lodash.merge": "^4.6.2", + "minimatch": "^3.0.4", + "natural-compare": "^1.4.0", + "optionator": "^0.9.1", + "progress": "^2.0.0", + "regexpp": "^3.2.0", + "semver": "^7.2.1", + "strip-ansi": "^6.0.1", + "strip-json-comments": "^3.1.0", + "text-table": "^0.2.0", + "v8-compile-cache": "^2.0.3" + }, + "bin": { + "eslint": "bin/eslint.js" + }, + "engines": { + "node": "^12.22.0 || ^14.17.0 || >=16.0.0" + }, + "funding": { + "url": "https://opencollective.com/eslint" + } + }, + "node_modules/eslint-scope": { + "version": "5.1.1", + "resolved": "https://registry.npmjs.org/eslint-scope/-/eslint-scope-5.1.1.tgz", + "integrity": "sha512-2NxwbF/hZ0KpepYN0cNbo+FN6XoK7GaHlQhgx/hIZl6Va0bF45RQOOwhLIy8lQDbuCiadSLCBnH2CFYquit5bw==", + "dev": true, + "dependencies": { + "esrecurse": "^4.3.0", + "estraverse": "^4.1.1" + }, + "engines": { + "node": ">=8.0.0" + } + }, + "node_modules/eslint-utils": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/eslint-utils/-/eslint-utils-3.0.0.tgz", + "integrity": "sha512-uuQC43IGctw68pJA1RgbQS8/NP7rch6Cwd4j3ZBtgo4/8Flj4eGE7ZYSZRN3iq5pVUv6GPdW5Z1RFleo84uLDA==", + "dev": true, + "dependencies": { + "eslint-visitor-keys": "^2.0.0" + }, + "engines": { + "node": "^10.0.0 || ^12.0.0 || >= 14.0.0" + }, + "funding": { + "url": "https://github.com/sponsors/mysticatea" + }, + "peerDependencies": { + "eslint": ">=5" + } + }, + "node_modules/eslint-utils/node_modules/eslint-visitor-keys": { + "version": "2.1.0", + "resolved": "https://registry.npmjs.org/eslint-visitor-keys/-/eslint-visitor-keys-2.1.0.tgz", + "integrity": "sha512-0rSmRBzXgDzIsD6mGdJgevzgezI534Cer5L/vyMX0kHzT/jiB43jRhd9YUlMGYLQy2zprNmoT8qasCGtY+QaKw==", + "dev": true, + "engines": { + "node": ">=10" + } + }, + "node_modules/eslint-visitor-keys": { + "version": "3.1.0", + "resolved": "https://registry.npmjs.org/eslint-visitor-keys/-/eslint-visitor-keys-3.1.0.tgz", + "integrity": "sha512-yWJFpu4DtjsWKkt5GeNBBuZMlNcYVs6vRCLoCVEJrTjaSB6LC98gFipNK/erM2Heg/E8mIK+hXG/pJMLK+eRZA==", + "dev": true, + "engines": { + "node": "^12.22.0 || ^14.17.0 || >=16.0.0" + } + }, + "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==", + "dev": true, + "dependencies": { + "esrecurse": "^4.3.0", + "estraverse": "^5.2.0" + }, + "engines": { + "node": "^12.22.0 || ^14.17.0 || >=16.0.0" + } + }, + "node_modules/eslint/node_modules/estraverse": { + "version": "5.3.0", + "resolved": "https://registry.npmjs.org/estraverse/-/estraverse-5.3.0.tgz", + "integrity": "sha512-MMdARuVEQziNTeJD8DgMqmhwR11BRQ/cBP+pLtYdSTnf3MIO8fFeiINEbX36ZdNlfU/7A9f3gUw49B3oQsvwBA==", + "dev": true, + "engines": { + "node": ">=4.0" + } + }, + "node_modules/eslint/node_modules/ignore": { + "version": "4.0.6", + "resolved": "https://registry.npmjs.org/ignore/-/ignore-4.0.6.tgz", + "integrity": "sha512-cyFDKrqc/YdcWFniJhzI42+AzS+gNwmUzOSFcRCQYwySuBBBy/KjuxWLZ/FHEH6Moq1NizMOBWyTcv8O4OZIMg==", + "dev": true, + "engines": { + "node": ">= 4" + } + }, + "node_modules/espree": { + "version": "9.0.0", + "resolved": "https://registry.npmjs.org/espree/-/espree-9.0.0.tgz", + "integrity": "sha512-r5EQJcYZ2oaGbeR0jR0fFVijGOcwai07/690YRXLINuhmVeRY4UKSAsQPe/0BNuDgwP7Ophoc1PRsr2E3tkbdQ==", + "dev": true, + "dependencies": { + "acorn": "^8.5.0", + "acorn-jsx": "^5.3.1", + "eslint-visitor-keys": "^3.0.0" + }, + "engines": { + "node": "^12.22.0 || ^14.17.0 || >=16.0.0" + } + }, + "node_modules/esprima": { + "version": "4.0.1", + "resolved": "https://registry.npmjs.org/esprima/-/esprima-4.0.1.tgz", + "integrity": "sha512-eGuFFw7Upda+g4p+QHvnW0RyTX/SVeJBDM/gCtMARO0cLuT2HcEKnTPvhjV6aGeqrCB/sbNop0Kszm0jsaWU4A==", + "dev": true, + "bin": { + "esparse": "bin/esparse.js", + "esvalidate": "bin/esvalidate.js" + }, + "engines": { + "node": ">=4" + } + }, + "node_modules/esquery": { + "version": "1.4.0", + "resolved": "https://registry.npmjs.org/esquery/-/esquery-1.4.0.tgz", + "integrity": "sha512-cCDispWt5vHHtwMY2YrAQ4ibFkAL8RbH5YGBnZBc90MolvvfkkQcJro/aZiAQUlQ3qgrYS6D6v8Gc5G5CQsc9w==", + "dev": true, + "dependencies": { + "estraverse": "^5.1.0" + }, + "engines": { + "node": ">=0.10" + } + }, + "node_modules/esquery/node_modules/estraverse": { + "version": "5.3.0", + "resolved": "https://registry.npmjs.org/estraverse/-/estraverse-5.3.0.tgz", + "integrity": "sha512-MMdARuVEQziNTeJD8DgMqmhwR11BRQ/cBP+pLtYdSTnf3MIO8fFeiINEbX36ZdNlfU/7A9f3gUw49B3oQsvwBA==", + "dev": true, + "engines": { + "node": ">=4.0" + } + }, + "node_modules/esrecurse": { + "version": "4.3.0", + "resolved": "https://registry.npmjs.org/esrecurse/-/esrecurse-4.3.0.tgz", + "integrity": "sha512-KmfKL3b6G+RXvP8N1vr3Tq1kL/oCFgn2NYXEtqP8/L3pKapUA4G8cFVaoF3SU323CD4XypR/ffioHmkti6/Tag==", + "dev": true, + "dependencies": { + "estraverse": "^5.2.0" + }, + "engines": { + "node": ">=4.0" + } + }, + "node_modules/esrecurse/node_modules/estraverse": { + "version": "5.3.0", + "resolved": "https://registry.npmjs.org/estraverse/-/estraverse-5.3.0.tgz", + "integrity": "sha512-MMdARuVEQziNTeJD8DgMqmhwR11BRQ/cBP+pLtYdSTnf3MIO8fFeiINEbX36ZdNlfU/7A9f3gUw49B3oQsvwBA==", + "dev": true, + "engines": { + "node": ">=4.0" + } + }, + "node_modules/estraverse": { + "version": "4.3.0", + "resolved": "https://registry.npmjs.org/estraverse/-/estraverse-4.3.0.tgz", + "integrity": "sha512-39nnKffWz8xN1BU/2c79n9nB9HDzo0niYUqx6xyqUnyoAnQyyWpOTdZEeiCch8BBu515t4wp9ZmgVfVhn9EBpw==", + "dev": true, + "engines": { + "node": ">=4.0" + } + }, + "node_modules/esutils": { + "version": "2.0.3", + "resolved": "https://registry.npmjs.org/esutils/-/esutils-2.0.3.tgz", + "integrity": "sha512-kVscqXk4OCp68SZ0dkgEKVi6/8ij300KBWTJq32P/dYeWTSwK41WyTxalN1eRmA5Z9UU/LX9D7FWSmV9SAYx6g==", + "dev": true, + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/execa": { + "version": "5.1.1", + "resolved": "https://registry.npmjs.org/execa/-/execa-5.1.1.tgz", + "integrity": "sha512-8uSpZZocAZRBAPIEINJj3Lo9HyGitllczc27Eh5YYojjMFMn8yHMDMaUHE2Jqfq05D/wucwI4JGURyXt1vchyg==", + "dev": true, + "dependencies": { + "cross-spawn": "^7.0.3", + "get-stream": "^6.0.0", + "human-signals": "^2.1.0", + "is-stream": "^2.0.0", + "merge-stream": "^2.0.0", + "npm-run-path": "^4.0.1", + "onetime": "^5.1.2", + "signal-exit": "^3.0.3", + "strip-final-newline": "^2.0.0" + }, + "engines": { + "node": ">=10" + }, + "funding": { + "url": "https://github.com/sindresorhus/execa?sponsor=1" + } + }, + "node_modules/external-editor": { + "version": "3.1.0", + "resolved": "https://registry.npmjs.org/external-editor/-/external-editor-3.1.0.tgz", + "integrity": "sha512-hMQ4CX1p1izmuLYyZqLMO/qGNw10wSv9QDCPfzXfyFrOaCSSoRfqE1Kf1s5an66J5JZC62NewG+mK49jOCtQew==", + "dev": true, + "dependencies": { + "chardet": "^0.7.0", + "iconv-lite": "^0.4.24", + "tmp": "^0.0.33" + }, + "engines": { + "node": ">=4" + } + }, + "node_modules/fast-deep-equal": { + "version": "3.1.3", + "resolved": "https://registry.npmjs.org/fast-deep-equal/-/fast-deep-equal-3.1.3.tgz", + "integrity": "sha512-f3qQ9oQy9j2AhBe/H9VC91wLmKBCCU/gDOnKNAYG5hswO7BLKj09Hc5HYNz9cGI++xlpDCIgDaitVs03ATR84Q==", + "dev": true + }, + "node_modules/fast-glob": { + "version": "3.2.7", + "resolved": "https://registry.npmjs.org/fast-glob/-/fast-glob-3.2.7.tgz", + "integrity": "sha512-rYGMRwip6lUMvYD3BTScMwT1HtAs2d71SMv66Vrxs0IekGZEjhM0pcMfjQPnknBt2zeCwQMEupiN02ZP4DiT1Q==", + "dev": true, + "dependencies": { + "@nodelib/fs.stat": "^2.0.2", + "@nodelib/fs.walk": "^1.2.3", + "glob-parent": "^5.1.2", + "merge2": "^1.3.0", + "micromatch": "^4.0.4" + }, + "engines": { + "node": ">=8" + } + }, + "node_modules/fast-glob/node_modules/glob-parent": { + "version": "5.1.2", + "resolved": "https://registry.npmjs.org/glob-parent/-/glob-parent-5.1.2.tgz", + "integrity": "sha512-AOIgSQCepiJYwP3ARnGx+5VnTu2HBYdzbGP45eLw1vr3zB3vZLeyed1sC9hnbcOc9/SrMyM5RPQrkGz4aS9Zow==", + "dev": true, + "dependencies": { + "is-glob": "^4.0.1" + }, + "engines": { + "node": ">= 6" + } + }, + "node_modules/fast-json-stable-stringify": { + "version": "2.1.0", + "resolved": "https://registry.npmjs.org/fast-json-stable-stringify/-/fast-json-stable-stringify-2.1.0.tgz", + "integrity": "sha512-lhd/wF+Lk98HZoTCtlVraHtfh5XYijIjalXck7saUtuanSDyLMxnHhSXEDJqHxD7msR8D0uCmqlkwjCV8xvwHw==", + "dev": true + }, + "node_modules/fast-levenshtein": { + "version": "2.0.6", + "resolved": "https://registry.npmjs.org/fast-levenshtein/-/fast-levenshtein-2.0.6.tgz", + "integrity": "sha1-PYpcZog6FqMMqGQ+hR8Zuqd5eRc=", + "dev": true + }, + "node_modules/fastq": { + "version": "1.13.0", + "resolved": "https://registry.npmjs.org/fastq/-/fastq-1.13.0.tgz", + "integrity": "sha512-YpkpUnK8od0o1hmeSc7UUs/eB/vIPWJYjKck2QKIzAf71Vm1AAQ3EbuZB3g2JIy+pg+ERD0vqI79KyZiB2e2Nw==", + "dev": true, + "dependencies": { + "reusify": "^1.0.4" + } + }, + "node_modules/figures": { + "version": "3.2.0", + "resolved": "https://registry.npmjs.org/figures/-/figures-3.2.0.tgz", + "integrity": "sha512-yaduQFRKLXYOGgEn6AZau90j3ggSOyiqXU0F9JZfeXYhNa+Jk4X+s45A2zg5jns87GAFa34BBm2kXw4XpNcbdg==", + "dev": true, + "dependencies": { + "escape-string-regexp": "^1.0.5" + }, + "engines": { + "node": ">=8" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/figures/node_modules/escape-string-regexp": { + "version": "1.0.5", + "resolved": "https://registry.npmjs.org/escape-string-regexp/-/escape-string-regexp-1.0.5.tgz", + "integrity": "sha1-G2HAViGQqN/2rjuyzwIAyhMLhtQ=", + "dev": true, + "engines": { + "node": ">=0.8.0" + } + }, + "node_modules/file-entry-cache": { + "version": "6.0.1", + "resolved": "https://registry.npmjs.org/file-entry-cache/-/file-entry-cache-6.0.1.tgz", + "integrity": "sha512-7Gps/XWymbLk2QLYK4NzpMOrYjMhdIxXuIvy2QBsLE6ljuodKvdkWs/cpyJJ3CVIVpH0Oi1Hvg1ovbMzLdFBBg==", + "dev": true, + "dependencies": { + "flat-cache": "^3.0.4" + }, + "engines": { + "node": "^10.12.0 || >=12.0.0" + } + }, + "node_modules/fill-range": { + "version": "7.0.1", + "resolved": "https://registry.npmjs.org/fill-range/-/fill-range-7.0.1.tgz", + "integrity": "sha512-qOo9F+dMUmC2Lcb4BbVvnKJxTPjCm+RRpe4gDuGrzkL7mEVl/djYSu2OdQ2Pa302N4oqkSg9ir6jaLWJ2USVpQ==", + "dev": true, + "dependencies": { + "to-regex-range": "^5.0.1" + }, + "engines": { + "node": ">=8" + } + }, + "node_modules/filter-obj": { + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/filter-obj/-/filter-obj-1.1.0.tgz", + "integrity": "sha1-mzERErxsYSehbgFsbF1/GeCAXFs=", + "dev": true, + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/find-cache-dir": { + "version": "3.3.2", + "resolved": "https://registry.npmjs.org/find-cache-dir/-/find-cache-dir-3.3.2.tgz", + "integrity": "sha512-wXZV5emFEjrridIgED11OoUKLxiYjAcqot/NJdAkOhlJ+vGzwhOAfcG5OX1jP+S0PcjEn8bdMJv+g2jwQ3Onig==", + "dev": true, + "dependencies": { + "commondir": "^1.0.1", + "make-dir": "^3.0.2", + "pkg-dir": "^4.1.0" + }, + "engines": { + "node": ">=8" + }, + "funding": { + "url": "https://github.com/avajs/find-cache-dir?sponsor=1" + } + }, + "node_modules/find-up": { + "version": "5.0.0", + "resolved": "https://registry.npmjs.org/find-up/-/find-up-5.0.0.tgz", + "integrity": "sha512-78/PXT1wlLLDgTzDs7sjq9hzz0vXD+zn+7wypEe4fXQxCmdmqfGsEPQxmiCSQI3ajFV91bVSsvNtrJRiW6nGng==", + "dev": true, + "dependencies": { + "locate-path": "^6.0.0", + "path-exists": "^4.0.0" + }, + "engines": { + "node": ">=10" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/flat": { + "version": "5.0.2", + "resolved": "https://registry.npmjs.org/flat/-/flat-5.0.2.tgz", + "integrity": "sha512-b6suED+5/3rTpUBdG1gupIl8MPFCAMA0QXwmljLhvCUKcUvdE4gWky9zpuGCcXHOsz4J9wPGNWq6OKpmIzz3hQ==", + "dev": true, + "bin": { + "flat": "cli.js" + } + }, + "node_modules/flat-cache": { + "version": "3.0.4", + "resolved": "https://registry.npmjs.org/flat-cache/-/flat-cache-3.0.4.tgz", + "integrity": "sha512-dm9s5Pw7Jc0GvMYbshN6zchCA9RgQlzzEZX3vylR9IqFfS8XciblUXOKfW6SiuJ0e13eDYZoZV5wdrev7P3Nwg==", + "dev": true, + "dependencies": { + "flatted": "^3.1.0", + "rimraf": "^3.0.2" + }, + "engines": { + "node": "^10.12.0 || >=12.0.0" + } + }, + "node_modules/flatted": { + "version": "3.2.4", + "resolved": "https://registry.npmjs.org/flatted/-/flatted-3.2.4.tgz", + "integrity": "sha512-8/sOawo8tJ4QOBX8YlQBMxL8+RLZfxMQOif9o0KUKTNTjMYElWPE0r/m5VNFxTRd0NSw8qSy8dajrwX4RYI1Hw==", + "dev": true + }, + "node_modules/foreground-child": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/foreground-child/-/foreground-child-2.0.0.tgz", + "integrity": "sha512-dCIq9FpEcyQyXKCkyzmlPTFNgrCzPudOe+mhvJU5zAtlBnGVy2yKxtfsxK2tQBThwq225jcvBjpw1Gr40uzZCA==", + "dev": true, + "dependencies": { + "cross-spawn": "^7.0.0", + "signal-exit": "^3.0.2" + }, + "engines": { + "node": ">=8.0.0" + } + }, + "node_modules/form-data": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/form-data/-/form-data-4.0.0.tgz", + "integrity": "sha512-ETEklSGi5t0QMZuiXoA/Q6vcnxcLQP5vdugSpuAyi6SVGi2clPPp+xgEhuMaHC+zGgn31Kd235W35f7Hykkaww==", + "dev": true, + "dependencies": { + "asynckit": "^0.4.0", + "combined-stream": "^1.0.8", + "mime-types": "^2.1.12" + }, + "engines": { + "node": ">= 6" + } + }, + "node_modules/fromentries": { + "version": "1.3.2", + "resolved": "https://registry.npmjs.org/fromentries/-/fromentries-1.3.2.tgz", + "integrity": "sha512-cHEpEQHUg0f8XdtZCc2ZAhrHzKzT0MrFUTcvx+hfxYu7rGMDc5SKoXFh+n4YigxsHXRzc6OrCshdR1bWH6HHyg==", + "dev": true, + "funding": [ + { + "type": "github", + "url": "https://github.com/sponsors/feross" + }, + { + "type": "patreon", + "url": "https://www.patreon.com/feross" + }, + { + "type": "consulting", + "url": "https://feross.org/support" + } + ] + }, + "node_modules/fs.realpath": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/fs.realpath/-/fs.realpath-1.0.0.tgz", + "integrity": "sha1-FQStJSMVjKpA20onh8sBQRmU6k8=", + "dev": true + }, + "node_modules/fsevents": { + "version": "2.3.2", + "resolved": "https://registry.npmjs.org/fsevents/-/fsevents-2.3.2.tgz", + "integrity": "sha512-xiqMQR4xAeHTuB9uWm+fFRcIOgKBMiOBP+eXiyT7jsgVCq1bkVygt00oASowB7EdtpOHaaPgKt812P9ab+DDKA==", + "dev": true, + "hasInstallScript": true, + "optional": true, + "os": [ + "darwin" + ], + "engines": { + "node": "^8.16.0 || ^10.6.0 || >=11.0.0" + } + }, + "node_modules/function-bind": { + "version": "1.1.1", + "resolved": "https://registry.npmjs.org/function-bind/-/function-bind-1.1.1.tgz", + "integrity": "sha512-yIovAzMX49sF8Yl58fSCWJ5svSLuaibPxXQJFLmBObTuCr0Mf1KiPopGM9NiFjiYBCbfaa2Fh6breQ6ANVTI0A==", + "dev": true + }, + "node_modules/functional-red-black-tree": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/functional-red-black-tree/-/functional-red-black-tree-1.0.1.tgz", + "integrity": "sha1-GwqzvVU7Kg1jmdKcDj6gslIHgyc=", + "dev": true + }, + "node_modules/generic-pool": { + "version": "3.8.2", + "resolved": "https://registry.npmjs.org/generic-pool/-/generic-pool-3.8.2.tgz", + "integrity": "sha512-nGToKy6p3PAbYQ7p1UlWl6vSPwfwU6TMSWK7TTu+WUY4ZjyZQGniGGt2oNVvyNSpyZYSB43zMXVLcBm08MTMkg==", + "engines": { + "node": ">= 4" + } + }, + "node_modules/gensync": { + "version": "1.0.0-beta.2", + "resolved": "https://registry.npmjs.org/gensync/-/gensync-1.0.0-beta.2.tgz", + "integrity": "sha512-3hN7NaskYvMDLQY55gnW3NQ+mesEAepTqlg+VEbj7zzqEMBVNhzcGYYeqFo/TlYz6eQiFcp1HcsCZO+nGgS8zg==", + "dev": true, + "engines": { + "node": ">=6.9.0" + } + }, + "node_modules/get-caller-file": { + "version": "2.0.5", + "resolved": "https://registry.npmjs.org/get-caller-file/-/get-caller-file-2.0.5.tgz", + "integrity": "sha512-DyFP3BM/3YHTQOCUL/w0OZHR0lpKeGrxotcHWcqNEdnltqFwXVfhEBQ94eIo34AfQpo0rGki4cyIiftY06h2Fg==", + "dev": true, + "engines": { + "node": "6.* || 8.* || >= 10.*" + } + }, + "node_modules/get-intrinsic": { + "version": "1.1.1", + "resolved": "https://registry.npmjs.org/get-intrinsic/-/get-intrinsic-1.1.1.tgz", + "integrity": "sha512-kWZrnVM42QCiEA2Ig1bG8zjoIMOgxWwYCEeNdwY6Tv/cOSeGpcoX4pXHfKUxNKVoArnrEr2e9srnAxxGIraS9Q==", + "dev": true, + "dependencies": { + "function-bind": "^1.1.1", + "has": "^1.0.3", + "has-symbols": "^1.0.1" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/get-package-type": { + "version": "0.1.0", + "resolved": "https://registry.npmjs.org/get-package-type/-/get-package-type-0.1.0.tgz", + "integrity": "sha512-pjzuKtY64GYfWizNAJ0fr9VqttZkNiK2iS430LtIHzjBEr6bX8Am2zm4sW4Ro5wjWW5cAlRL1qAMTcXbjNAO2Q==", + "dev": true, + "engines": { + "node": ">=8.0.0" + } + }, + "node_modules/get-stream": { + "version": "6.0.1", + "resolved": "https://registry.npmjs.org/get-stream/-/get-stream-6.0.1.tgz", + "integrity": "sha512-ts6Wi+2j3jQjqi70w5AlN8DFnkSwC+MqmxEzdEALB2qXZYV3X/b1CTfgPLGJNMeAWxdPfU8FO1ms3NUfaHCPYg==", + "dev": true, + "engines": { + "node": ">=10" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/git-up": { + "version": "4.0.5", + "resolved": "https://registry.npmjs.org/git-up/-/git-up-4.0.5.tgz", + "integrity": "sha512-YUvVDg/vX3d0syBsk/CKUTib0srcQME0JyHkL5BaYdwLsiCslPWmDSi8PUMo9pXYjrryMcmsCoCgsTpSCJEQaA==", + "dev": true, + "dependencies": { + "is-ssh": "^1.3.0", + "parse-url": "^6.0.0" + } + }, + "node_modules/git-url-parse": { + "version": "11.6.0", + "resolved": "https://registry.npmjs.org/git-url-parse/-/git-url-parse-11.6.0.tgz", + "integrity": "sha512-WWUxvJs5HsyHL6L08wOusa/IXYtMuCAhrMmnTjQPpBU0TTHyDhnOATNH3xNQz7YOQUsqIIPTGr4xiVti1Hsk5g==", + "dev": true, + "dependencies": { + "git-up": "^4.0.0" + } + }, + "node_modules/glob": { + "version": "7.1.7", + "resolved": "https://registry.npmjs.org/glob/-/glob-7.1.7.tgz", + "integrity": "sha512-OvD9ENzPLbegENnYP5UUfJIirTg4+XwMWGaQfQTY0JenxNvvIKP3U3/tAQSPIu/lHxXYSZmpXlUHeqAIdKzBLQ==", + "dev": true, + "dependencies": { + "fs.realpath": "^1.0.0", + "inflight": "^1.0.4", + "inherits": "2", + "minimatch": "^3.0.4", + "once": "^1.3.0", + "path-is-absolute": "^1.0.0" + }, + "engines": { + "node": "*" + }, + "funding": { + "url": "https://github.com/sponsors/isaacs" + } + }, + "node_modules/glob-parent": { + "version": "6.0.2", + "resolved": "https://registry.npmjs.org/glob-parent/-/glob-parent-6.0.2.tgz", + "integrity": "sha512-XxwI8EOhVQgWp6iDL+3b0r86f4d6AX6zSU55HfB4ydCEuXLXc5FcYeOu+nnGftS4TEju/11rt4KJPTMgbfmv4A==", + "dev": true, + "dependencies": { + "is-glob": "^4.0.3" + }, + "engines": { + "node": ">=10.13.0" + } + }, + "node_modules/global-dirs": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/global-dirs/-/global-dirs-3.0.0.tgz", + "integrity": "sha512-v8ho2DS5RiCjftj1nD9NmnfaOzTdud7RRnVd9kFNOjqZbISlx5DQ+OrTkywgd0dIt7oFCvKetZSHoHcP3sDdiA==", + "dev": true, + "dependencies": { + "ini": "2.0.0" + }, + "engines": { + "node": ">=10" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/globals": { + "version": "13.12.0", + "resolved": "https://registry.npmjs.org/globals/-/globals-13.12.0.tgz", + "integrity": "sha512-uS8X6lSKN2JumVoXrbUz+uG4BYG+eiawqm3qFcT7ammfbUHeCBoJMlHcec/S3krSk73/AE/f0szYFmgAA3kYZg==", + "dev": true, + "dependencies": { + "type-fest": "^0.20.2" + }, + "engines": { + "node": ">=8" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/globby": { + "version": "11.0.4", + "resolved": "https://registry.npmjs.org/globby/-/globby-11.0.4.tgz", + "integrity": "sha512-9O4MVG9ioZJ08ffbcyVYyLOJLk5JQ688pJ4eMGLpdWLHq/Wr1D9BlriLQyL0E+jbkuePVZXYFj47QM/v093wHg==", + "dev": true, + "dependencies": { + "array-union": "^2.1.0", + "dir-glob": "^3.0.1", + "fast-glob": "^3.1.1", + "ignore": "^5.1.4", + "merge2": "^1.3.0", + "slash": "^3.0.0" + }, + "engines": { + "node": ">=10" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "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==", + "dev": true, + "dependencies": { + "@sindresorhus/is": "^4.0.0", + "@szmarczak/http-timer": "^4.0.5", + "@types/cacheable-request": "^6.0.1", + "@types/responselike": "^1.0.0", + "cacheable-lookup": "^5.0.3", + "cacheable-request": "^7.0.1", + "decompress-response": "^6.0.0", + "http2-wrapper": "^1.0.0-beta.5.2", + "lowercase-keys": "^2.0.0", + "p-cancelable": "^2.0.0", + "responselike": "^2.0.0" + }, + "engines": { + "node": ">=10.19.0" + }, + "funding": { + "url": "https://github.com/sindresorhus/got?sponsor=1" + } + }, + "node_modules/graceful-fs": { + "version": "4.2.8", + "resolved": "https://registry.npmjs.org/graceful-fs/-/graceful-fs-4.2.8.tgz", + "integrity": "sha512-qkIilPUYcNhJpd33n0GBXTB1MMPp14TxEsEs0pTrsSVucApsYzW5V+Q8Qxhik6KU3evy+qkAAowTByymK0avdg==", + "dev": true + }, + "node_modules/growl": { + "version": "1.10.5", + "resolved": "https://registry.npmjs.org/growl/-/growl-1.10.5.tgz", + "integrity": "sha512-qBr4OuELkhPenW6goKVXiv47US3clb3/IbuWF9KNKEijAy9oeHxU9IgzjvJhHkUzhaj7rOUD7+YGWqUjLp5oSA==", + "dev": true, + "engines": { + "node": ">=4.x" + } + }, + "node_modules/handlebars": { + "version": "4.7.7", + "resolved": "https://registry.npmjs.org/handlebars/-/handlebars-4.7.7.tgz", + "integrity": "sha512-aAcXm5OAfE/8IXkcZvCepKU3VzW1/39Fb5ZuqMtgI/hT8X2YgoMvBY5dLhq/cpOvw7Lk1nK/UF71aLG/ZnVYRA==", + "dev": true, + "dependencies": { + "minimist": "^1.2.5", + "neo-async": "^2.6.0", + "source-map": "^0.6.1", + "wordwrap": "^1.0.0" + }, + "bin": { + "handlebars": "bin/handlebars" + }, + "engines": { + "node": ">=0.4.7" + }, + "optionalDependencies": { + "uglify-js": "^3.1.4" + } + }, + "node_modules/handlebars/node_modules/source-map": { + "version": "0.6.1", + "resolved": "https://registry.npmjs.org/source-map/-/source-map-0.6.1.tgz", + "integrity": "sha512-UjgapumWlbMhkBgzT7Ykc5YXUT46F0iKu8SGXq0bcwP5dz/h0Plj6enJqjz1Zbq2l5WaqYnrVbwWOWMyF3F47g==", + "dev": true, + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/has": { + "version": "1.0.3", + "resolved": "https://registry.npmjs.org/has/-/has-1.0.3.tgz", + "integrity": "sha512-f2dvO0VU6Oej7RkWJGrehjbzMAjFp5/VKPp5tTpWIV4JHHZK1/BxbFRtf/siA2SWTe09caDmVtYYzWEIbBS4zw==", + "dev": true, + "dependencies": { + "function-bind": "^1.1.1" + }, + "engines": { + "node": ">= 0.4.0" + } + }, + "node_modules/has-flag": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/has-flag/-/has-flag-4.0.0.tgz", + "integrity": "sha512-EykJT/Q1KjTWctppgIAgfSO0tKVuZUjhgMr17kqTumMl6Afv3EISleU7qZUzoXDFTAHTDC4NOoG/ZxU3EvlMPQ==", + "dev": true, + "engines": { + "node": ">=8" + } + }, + "node_modules/has-symbols": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/has-symbols/-/has-symbols-1.0.2.tgz", + "integrity": "sha512-chXa79rL/UC2KlX17jo3vRGz0azaWEx5tGqZg5pO3NUyEJVB17dMruQlzCCOfUvElghKcm5194+BCRvi2Rv/Gw==", + "dev": true, + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/has-yarn": { + "version": "2.1.0", + "resolved": "https://registry.npmjs.org/has-yarn/-/has-yarn-2.1.0.tgz", + "integrity": "sha512-UqBRqi4ju7T+TqGNdqAO0PaSVGsDGJUBQvk9eUWNGRY1CFGDzYhLWoM7JQEemnlvVcv/YEmc2wNW8BC24EnUsw==", + "dev": true, + "engines": { + "node": ">=8" + } + }, + "node_modules/hasha": { + "version": "5.2.2", + "resolved": "https://registry.npmjs.org/hasha/-/hasha-5.2.2.tgz", + "integrity": "sha512-Hrp5vIK/xr5SkeN2onO32H0MgNZ0f17HRNH39WfL0SYUNOTZ5Lz1TJ8Pajo/87dYGEFlLMm7mIc/k/s6Bvz9HQ==", + "dev": true, + "dependencies": { + "is-stream": "^2.0.0", + "type-fest": "^0.8.0" + }, + "engines": { + "node": ">=8" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/hasha/node_modules/type-fest": { + "version": "0.8.1", + "resolved": "https://registry.npmjs.org/type-fest/-/type-fest-0.8.1.tgz", + "integrity": "sha512-4dbzIzqvjtgiM5rw1k5rEHtBANKmdudhGyBEajN01fEyhaAIhsoKNy6y7+IN93IfpFtwY9iqi7kD+xwKhQsNJA==", + "dev": true, + "engines": { + "node": ">=8" + } + }, + "node_modules/he": { + "version": "1.2.0", + "resolved": "https://registry.npmjs.org/he/-/he-1.2.0.tgz", + "integrity": "sha512-F/1DnUGPopORZi0ni+CvrCgHQ5FyEAHRLSApuYWMmrbSwoN2Mn/7k+Gl38gJnR7yyDZk6WLXwiGod1JOWNDKGw==", + "dev": true, + "bin": { + "he": "bin/he" + } + }, + "node_modules/html-escaper": { + "version": "2.0.2", + "resolved": "https://registry.npmjs.org/html-escaper/-/html-escaper-2.0.2.tgz", + "integrity": "sha512-H2iMtd0I4Mt5eYiapRdIDjp+XzelXQ0tFE4JS7YFwFevXXMmOp9myNrUvCg0D6ws8iqkRPBfKHgbwig1SmlLfg==", + "dev": true + }, + "node_modules/http-cache-semantics": { + "version": "4.1.0", + "resolved": "https://registry.npmjs.org/http-cache-semantics/-/http-cache-semantics-4.1.0.tgz", + "integrity": "sha512-carPklcUh7ROWRK7Cv27RPtdhYhUsela/ue5/jKzjegVvXDqM2ILE9Q2BGn9JZJh1g87cp56su/FgQSzcWS8cQ==", + "dev": true + }, + "node_modules/http2-wrapper": { + "version": "1.0.3", + "resolved": "https://registry.npmjs.org/http2-wrapper/-/http2-wrapper-1.0.3.tgz", + "integrity": "sha512-V+23sDMr12Wnz7iTcDeJr3O6AIxlnvT/bmaAAAP/Xda35C90p9599p0F1eHR/N1KILWSoWVAiOMFjBBXaXSMxg==", + "dev": true, + "dependencies": { + "quick-lru": "^5.1.1", + "resolve-alpn": "^1.0.0" + }, + "engines": { + "node": ">=10.19.0" + } + }, + "node_modules/human-signals": { + "version": "2.1.0", + "resolved": "https://registry.npmjs.org/human-signals/-/human-signals-2.1.0.tgz", + "integrity": "sha512-B4FFZ6q/T2jhhksgkbEW3HBvWIfDW85snkQgawt07S7J5QXTk6BkNV+0yAeZrM5QpMAdYlocGoljn0sJ/WQkFw==", + "dev": true, + "engines": { + "node": ">=10.17.0" + } + }, + "node_modules/iconv-lite": { + "version": "0.4.24", + "resolved": "https://registry.npmjs.org/iconv-lite/-/iconv-lite-0.4.24.tgz", + "integrity": "sha512-v3MXnZAcvnywkTUEZomIActle7RXXeedOR31wwl7VlyoXO4Qi9arvSenNQWne1TcRwhCL1HwLI21bEqdpj8/rA==", + "dev": true, + "dependencies": { + "safer-buffer": ">= 2.1.2 < 3" + }, + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/ieee754": { + "version": "1.2.1", + "resolved": "https://registry.npmjs.org/ieee754/-/ieee754-1.2.1.tgz", + "integrity": "sha512-dcyqhDvX1C46lXZcVqCpK+FtMRQVdIMN6/Df5js2zouUsqG7I6sFxitIC+7KYK29KdXOLHdu9zL4sFnoVQnqaA==", + "dev": true, + "funding": [ + { + "type": "github", + "url": "https://github.com/sponsors/feross" + }, + { + "type": "patreon", + "url": "https://www.patreon.com/feross" + }, + { + "type": "consulting", + "url": "https://feross.org/support" + } + ] + }, + "node_modules/ignore": { + "version": "5.1.9", + "resolved": "https://registry.npmjs.org/ignore/-/ignore-5.1.9.tgz", + "integrity": "sha512-2zeMQpbKz5dhZ9IwL0gbxSW5w0NK/MSAMtNuhgIHEPmaU3vPdKPL0UdvUCXs5SS4JAwsBxysK5sFMW8ocFiVjQ==", + "dev": true, + "engines": { + "node": ">= 4" + } + }, + "node_modules/import-cwd": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/import-cwd/-/import-cwd-3.0.0.tgz", + "integrity": "sha512-4pnzH16plW+hgvRECbDWpQl3cqtvSofHWh44met7ESfZ8UZOWWddm8hEyDTqREJ9RbYHY8gi8DqmaelApoOGMg==", + "dev": true, + "dependencies": { + "import-from": "^3.0.0" + }, + "engines": { + "node": ">=8" + } + }, + "node_modules/import-fresh": { + "version": "3.3.0", + "resolved": "https://registry.npmjs.org/import-fresh/-/import-fresh-3.3.0.tgz", + "integrity": "sha512-veYYhQa+D1QBKznvhUHxb8faxlrwUnxseDAbAp457E0wLNio2bOSKnjYDhMj+YiAq61xrMGhQk9iXVk5FzgQMw==", + "dev": true, + "dependencies": { + "parent-module": "^1.0.0", + "resolve-from": "^4.0.0" + }, + "engines": { + "node": ">=6" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/import-from": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/import-from/-/import-from-3.0.0.tgz", + "integrity": "sha512-CiuXOFFSzkU5x/CR0+z7T91Iht4CXgfCxVOFRhh2Zyhg5wOpWvvDLQUsWl+gcN+QscYBjez8hDCt85O7RLDttQ==", + "dev": true, + "dependencies": { + "resolve-from": "^5.0.0" + }, + "engines": { + "node": ">=8" + } + }, + "node_modules/import-from/node_modules/resolve-from": { + "version": "5.0.0", + "resolved": "https://registry.npmjs.org/resolve-from/-/resolve-from-5.0.0.tgz", + "integrity": "sha512-qYg9KP24dD5qka9J47d0aVky0N+b4fTU89LN9iDnjB5waksiC49rvMB0PrUJQGoTmH50XPiqOvAjDfaijGxYZw==", + "dev": true, + "engines": { + "node": ">=8" + } + }, + "node_modules/import-lazy": { + "version": "2.1.0", + "resolved": "https://registry.npmjs.org/import-lazy/-/import-lazy-2.1.0.tgz", + "integrity": "sha1-BWmOPUXIjo1+nZLLBYTnfwlvPkM=", + "dev": true, + "engines": { + "node": ">=4" + } + }, + "node_modules/imurmurhash": { + "version": "0.1.4", + "resolved": "https://registry.npmjs.org/imurmurhash/-/imurmurhash-0.1.4.tgz", + "integrity": "sha1-khi5srkoojixPcT7a21XbyMUU+o=", + "dev": true, + "engines": { + "node": ">=0.8.19" + } + }, + "node_modules/indent-string": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/indent-string/-/indent-string-4.0.0.tgz", + "integrity": "sha512-EdDDZu4A2OyIK7Lr/2zG+w5jmbuk1DVBnEwREQvBzspBJkCEbRa8GxU1lghYcaGJCnRWibjDXlq779X1/y5xwg==", + "dev": true, + "engines": { + "node": ">=8" + } + }, + "node_modules/inflight": { + "version": "1.0.6", + "resolved": "https://registry.npmjs.org/inflight/-/inflight-1.0.6.tgz", + "integrity": "sha1-Sb1jMdfQLQwJvJEKEHW6gWW1bfk=", + "dev": true, + "dependencies": { + "once": "^1.3.0", + "wrappy": "1" + } + }, + "node_modules/inherits": { + "version": "2.0.4", + "resolved": "https://registry.npmjs.org/inherits/-/inherits-2.0.4.tgz", + "integrity": "sha512-k/vGaX4/Yla3WzyMCvTQOXYeIHvqOKtnqBduzTHpzpQZzAskKMhZ2K+EnBiSM9zGSoIFeMpXKxa4dYeZIQqewQ==", + "dev": true + }, + "node_modules/ini": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/ini/-/ini-2.0.0.tgz", + "integrity": "sha512-7PnF4oN3CvZF23ADhA5wRaYEQpJ8qygSkbtTXWBeXWXmEVRXK+1ITciHWwHhsjv1TmW0MgacIv6hEi5pX5NQdA==", + "dev": true, + "engines": { + "node": ">=10" + } + }, + "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==", + "dev": true, + "dependencies": { + "ansi-escapes": "^4.2.1", + "chalk": "^4.1.1", + "cli-cursor": "^3.1.0", + "cli-width": "^3.0.0", + "external-editor": "^3.0.3", + "figures": "^3.0.0", + "lodash": "^4.17.21", + "mute-stream": "0.0.8", + "ora": "^5.4.1", + "run-async": "^2.4.0", + "rxjs": "^7.2.0", + "string-width": "^4.1.0", + "strip-ansi": "^6.0.0", + "through": "^2.3.6" + }, + "engines": { + "node": ">=8.0.0" + } + }, + "node_modules/interpret": { + "version": "1.4.0", + "resolved": "https://registry.npmjs.org/interpret/-/interpret-1.4.0.tgz", + "integrity": "sha512-agE4QfB2Lkp9uICn7BAqoscw4SZP9kTE2hxiFI3jBPmXJfdqiahTbUuKGsMoN2GtqL9AxhYioAcVvgsb1HvRbA==", + "dev": true, + "engines": { + "node": ">= 0.10" + } + }, + "node_modules/is-arrayish": { + "version": "0.2.1", + "resolved": "https://registry.npmjs.org/is-arrayish/-/is-arrayish-0.2.1.tgz", + "integrity": "sha1-d8mYQFJ6qOyxqLppe4BkWnqSap0=", + "dev": true + }, + "node_modules/is-binary-path": { + "version": "2.1.0", + "resolved": "https://registry.npmjs.org/is-binary-path/-/is-binary-path-2.1.0.tgz", + "integrity": "sha512-ZMERYes6pDydyuGidse7OsHxtbI7WVeUEozgR/g7rd0xUimYNlvZRE/K2MgZTjWy725IfelLeVcEM97mmtRGXw==", + "dev": true, + "dependencies": { + "binary-extensions": "^2.0.0" + }, + "engines": { + "node": ">=8" + } + }, + "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==", + "dev": true, + "dependencies": { + "ci-info": "^3.1.1" + }, + "bin": { + "is-ci": "bin.js" + } + }, + "node_modules/is-core-module": { + "version": "2.8.0", + "resolved": "https://registry.npmjs.org/is-core-module/-/is-core-module-2.8.0.tgz", + "integrity": "sha512-vd15qHsaqrRL7dtH6QNuy0ndJmRDrS9HAM1CAiSifNUFv4x1a0CCVsj18hJ1mShxIG6T2i1sO78MkP56r0nYRw==", + "dev": true, + "dependencies": { + "has": "^1.0.3" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/is-docker": { + "version": "2.2.1", + "resolved": "https://registry.npmjs.org/is-docker/-/is-docker-2.2.1.tgz", + "integrity": "sha512-F+i2BKsFrH66iaUFc0woD8sLy8getkwTwtOBjvs56Cx4CgJDeKQeqfz8wAYiSb8JOprWhHH5p77PbmYCvvUuXQ==", + "dev": true, + "bin": { + "is-docker": "cli.js" + }, + "engines": { + "node": ">=8" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/is-extglob": { + "version": "2.1.1", + "resolved": "https://registry.npmjs.org/is-extglob/-/is-extglob-2.1.1.tgz", + "integrity": "sha1-qIwCU1eR8C7TfHahueqXc8gz+MI=", + "dev": true, + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/is-fullwidth-code-point": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/is-fullwidth-code-point/-/is-fullwidth-code-point-3.0.0.tgz", + "integrity": "sha512-zymm5+u+sCsSWyD9qNaejV3DFvhCKclKdizYaJUuHA83RLjb7nSuGnddCHGv0hk+KY7BMAlsWeK4Ueg6EV6XQg==", + "dev": true, + "engines": { + "node": ">=8" + } + }, + "node_modules/is-glob": { + "version": "4.0.3", + "resolved": "https://registry.npmjs.org/is-glob/-/is-glob-4.0.3.tgz", + "integrity": "sha512-xelSayHH36ZgE7ZWhli7pW34hNbNl8Ojv5KVmkJD4hBdD3th8Tfk9vYasLM+mXWOZhFkgZfxhLSnrwRr4elSSg==", + "dev": true, + "dependencies": { + "is-extglob": "^2.1.1" + }, + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/is-installed-globally": { + "version": "0.4.0", + "resolved": "https://registry.npmjs.org/is-installed-globally/-/is-installed-globally-0.4.0.tgz", + "integrity": "sha512-iwGqO3J21aaSkC7jWnHP/difazwS7SFeIqxv6wEtLU8Y5KlzFTjyqcSIT0d8s4+dDhKytsk9PJZ2BkS5eZwQRQ==", + "dev": true, + "dependencies": { + "global-dirs": "^3.0.0", + "is-path-inside": "^3.0.2" + }, + "engines": { + "node": ">=10" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/is-interactive": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/is-interactive/-/is-interactive-1.0.0.tgz", + "integrity": "sha512-2HvIEKRoqS62guEC+qBjpvRubdX910WCMuJTZ+I9yvqKU2/12eSL549HMwtabb4oupdj2sMP50k+XJfB/8JE6w==", + "dev": true, + "engines": { + "node": ">=8" + } + }, + "node_modules/is-npm": { + "version": "5.0.0", + "resolved": "https://registry.npmjs.org/is-npm/-/is-npm-5.0.0.tgz", + "integrity": "sha512-WW/rQLOazUq+ST/bCAVBp/2oMERWLsR7OrKyt052dNDk4DHcDE0/7QSXITlmi+VBcV13DfIbysG3tZJm5RfdBA==", + "dev": true, + "engines": { + "node": ">=10" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/is-number": { + "version": "7.0.0", + "resolved": "https://registry.npmjs.org/is-number/-/is-number-7.0.0.tgz", + "integrity": "sha512-41Cifkg6e8TylSpdtTpeLVMqvSBEVzTttHvERD741+pnZ8ANv0004MRL43QKPDlK9cGvNp6NZWZUBlbGXYxxng==", + "dev": true, + "engines": { + "node": ">=0.12.0" + } + }, + "node_modules/is-obj": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/is-obj/-/is-obj-2.0.0.tgz", + "integrity": "sha512-drqDG3cbczxxEJRoOXcOjtdp1J/lyp1mNn0xaznRs8+muBhgQcrnbspox5X5fOw0HnMnbfDzvnEMEtqDEJEo8w==", + "dev": true, + "engines": { + "node": ">=8" + } + }, + "node_modules/is-path-inside": { + "version": "3.0.3", + "resolved": "https://registry.npmjs.org/is-path-inside/-/is-path-inside-3.0.3.tgz", + "integrity": "sha512-Fd4gABb+ycGAmKou8eMftCupSir5lRxqf4aD/vd0cD2qc4HL07OjCeuHMr8Ro4CoMaeCKDB0/ECBOVWjTwUvPQ==", + "dev": true, + "engines": { + "node": ">=8" + } + }, + "node_modules/is-plain-obj": { + "version": "2.1.0", + "resolved": "https://registry.npmjs.org/is-plain-obj/-/is-plain-obj-2.1.0.tgz", + "integrity": "sha512-YWnfyRwxL/+SsrWYfOpUtz5b3YD+nyfkHvjbcanzk8zgyO4ASD67uVMRt8k5bM4lLMDnXfriRhOpemw+NfT1eA==", + "dev": true, + "engines": { + "node": ">=8" + } + }, + "node_modules/is-plain-object": { + "version": "5.0.0", + "resolved": "https://registry.npmjs.org/is-plain-object/-/is-plain-object-5.0.0.tgz", + "integrity": "sha512-VRSzKkbMm5jMDoKLbltAkFQ5Qr7VDiTFGXxYFXXowVj387GeGNOCsOH6Msy00SGZ3Fp84b1Naa1psqgcCIEP5Q==", + "dev": true, + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/is-ssh": { + "version": "1.3.3", + "resolved": "https://registry.npmjs.org/is-ssh/-/is-ssh-1.3.3.tgz", + "integrity": "sha512-NKzJmQzJfEEma3w5cJNcUMxoXfDjz0Zj0eyCalHn2E6VOwlzjZo0yuO2fcBSf8zhFuVCL/82/r5gRcoi6aEPVQ==", + "dev": true, + "dependencies": { + "protocols": "^1.1.0" + } + }, + "node_modules/is-stream": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/is-stream/-/is-stream-2.0.1.tgz", + "integrity": "sha512-hFoiJiTl63nn+kstHGBtewWSKnQLpyb155KHheA1l39uvtO9nWIop1p3udqPcUd/xbF1VLMO4n7OI6p7RbngDg==", + "dev": true, + "engines": { + "node": ">=8" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/is-typedarray": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/is-typedarray/-/is-typedarray-1.0.0.tgz", + "integrity": "sha1-5HnICFjfDBsR3dppQPlgEfzaSpo=", + "dev": true + }, + "node_modules/is-unicode-supported": { + "version": "0.1.0", + "resolved": "https://registry.npmjs.org/is-unicode-supported/-/is-unicode-supported-0.1.0.tgz", + "integrity": "sha512-knxG2q4UC3u8stRGyAVJCOdxFmv5DZiRcdlIaAQXAbSfJya+OhopNotLQrstBhququ4ZpuKbDc/8S6mgXgPFPw==", + "dev": true, + "engines": { + "node": ">=10" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/is-windows": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/is-windows/-/is-windows-1.0.2.tgz", + "integrity": "sha512-eXK1UInq2bPmjyX6e3VHIzMLobc4J94i4AWn+Hpq3OU5KkrRC96OAcR3PRJ/pGu6m8TRnBHP9dkXQVsT/COVIA==", + "dev": true, + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/is-wsl": { + "version": "2.2.0", + "resolved": "https://registry.npmjs.org/is-wsl/-/is-wsl-2.2.0.tgz", + "integrity": "sha512-fKzAra0rGJUUBwGBgNkHZuToZcn+TtXHpeCgmkMJMMYx1sQDYaCSyjJBSCa2nH1DGm7s3n1oBnohoVTBaN7Lww==", + "dev": true, + "dependencies": { + "is-docker": "^2.0.0" + }, + "engines": { + "node": ">=8" + } + }, + "node_modules/is-yarn-global": { + "version": "0.3.0", + "resolved": "https://registry.npmjs.org/is-yarn-global/-/is-yarn-global-0.3.0.tgz", + "integrity": "sha512-VjSeb/lHmkoyd8ryPVIKvOCn4D1koMqY+vqyjjUfc3xyKtP4dYOxM44sZrnqQSzSds3xyOrUTLTC9LVCVgLngw==", + "dev": true + }, + "node_modules/isarray": { + "version": "0.0.1", + "resolved": "https://registry.npmjs.org/isarray/-/isarray-0.0.1.tgz", + "integrity": "sha1-ihis/Kmo9Bd+Cav8YDiTmwXR7t8=", + "dev": true + }, + "node_modules/isexe": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/isexe/-/isexe-2.0.0.tgz", + "integrity": "sha1-6PvzdNxVb/iUehDcsFctYz8s+hA=", + "dev": true + }, + "node_modules/istanbul-lib-coverage": { + "version": "3.2.0", + "resolved": "https://registry.npmjs.org/istanbul-lib-coverage/-/istanbul-lib-coverage-3.2.0.tgz", + "integrity": "sha512-eOeJ5BHCmHYvQK7xt9GkdHuzuCGS1Y6g9Gvnx3Ym33fz/HpLRYxiS0wHNr+m/MBC8B647Xt608vCDEvhl9c6Mw==", + "dev": true, + "engines": { + "node": ">=8" + } + }, + "node_modules/istanbul-lib-hook": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/istanbul-lib-hook/-/istanbul-lib-hook-3.0.0.tgz", + "integrity": "sha512-Pt/uge1Q9s+5VAZ+pCo16TYMWPBIl+oaNIjgLQxcX0itS6ueeaA+pEfThZpH8WxhFgCiEb8sAJY6MdUKgiIWaQ==", + "dev": true, + "dependencies": { + "append-transform": "^2.0.0" + }, + "engines": { + "node": ">=8" + } + }, + "node_modules/istanbul-lib-instrument": { + "version": "4.0.3", + "resolved": "https://registry.npmjs.org/istanbul-lib-instrument/-/istanbul-lib-instrument-4.0.3.tgz", + "integrity": "sha512-BXgQl9kf4WTCPCCpmFGoJkz/+uhvm7h7PFKUYxh7qarQd3ER33vHG//qaE8eN25l07YqZPpHXU9I09l/RD5aGQ==", + "dev": true, + "dependencies": { + "@babel/core": "^7.7.5", + "@istanbuljs/schema": "^0.1.2", + "istanbul-lib-coverage": "^3.0.0", + "semver": "^6.3.0" + }, + "engines": { + "node": ">=8" + } + }, + "node_modules/istanbul-lib-instrument/node_modules/semver": { + "version": "6.3.0", + "resolved": "https://registry.npmjs.org/semver/-/semver-6.3.0.tgz", + "integrity": "sha512-b39TBaTSfV6yBrapU89p5fKekE2m/NwnDocOVruQFS1/veMgdzuPcnOM34M6CwxW8jH/lxEa5rBoDeUwu5HHTw==", + "dev": true, + "bin": { + "semver": "bin/semver.js" + } + }, + "node_modules/istanbul-lib-processinfo": { + "version": "2.0.2", + "resolved": "https://registry.npmjs.org/istanbul-lib-processinfo/-/istanbul-lib-processinfo-2.0.2.tgz", + "integrity": "sha512-kOwpa7z9hme+IBPZMzQ5vdQj8srYgAtaRqeI48NGmAQ+/5yKiHLV0QbYqQpxsdEF0+w14SoB8YbnHKcXE2KnYw==", + "dev": true, + "dependencies": { + "archy": "^1.0.0", + "cross-spawn": "^7.0.0", + "istanbul-lib-coverage": "^3.0.0-alpha.1", + "make-dir": "^3.0.0", + "p-map": "^3.0.0", + "rimraf": "^3.0.0", + "uuid": "^3.3.3" + }, + "engines": { + "node": ">=8" + } + }, + "node_modules/istanbul-lib-report": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/istanbul-lib-report/-/istanbul-lib-report-3.0.0.tgz", + "integrity": "sha512-wcdi+uAKzfiGT2abPpKZ0hSU1rGQjUQnLvtY5MpQ7QCTahD3VODhcu4wcfY1YtkGaDD5yuydOLINXsfbus9ROw==", + "dev": true, + "dependencies": { + "istanbul-lib-coverage": "^3.0.0", + "make-dir": "^3.0.0", + "supports-color": "^7.1.0" + }, + "engines": { + "node": ">=8" + } + }, + "node_modules/istanbul-lib-source-maps": { + "version": "4.0.1", + "resolved": "https://registry.npmjs.org/istanbul-lib-source-maps/-/istanbul-lib-source-maps-4.0.1.tgz", + "integrity": "sha512-n3s8EwkdFIJCG3BPKBYvskgXGoy88ARzvegkitk60NxRdwltLOTaH7CUiMRXvwYorl0Q712iEjcWB+fK/MrWVw==", + "dev": true, + "dependencies": { + "debug": "^4.1.1", + "istanbul-lib-coverage": "^3.0.0", + "source-map": "^0.6.1" + }, + "engines": { + "node": ">=10" + } + }, + "node_modules/istanbul-lib-source-maps/node_modules/source-map": { + "version": "0.6.1", + "resolved": "https://registry.npmjs.org/source-map/-/source-map-0.6.1.tgz", + "integrity": "sha512-UjgapumWlbMhkBgzT7Ykc5YXUT46F0iKu8SGXq0bcwP5dz/h0Plj6enJqjz1Zbq2l5WaqYnrVbwWOWMyF3F47g==", + "dev": true, + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/istanbul-reports": { + "version": "3.0.5", + "resolved": "https://registry.npmjs.org/istanbul-reports/-/istanbul-reports-3.0.5.tgz", + "integrity": "sha512-5+19PlhnGabNWB7kOFnuxT8H3T/iIyQzIbQMxXsURmmvKg86P2sbkrGOT77VnHw0Qr0gc2XzRaRfMZYYbSQCJQ==", + "dev": true, + "dependencies": { + "html-escaper": "^2.0.0", + "istanbul-lib-report": "^3.0.0" + }, + "engines": { + "node": ">=8" + } + }, + "node_modules/js-tokens": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/js-tokens/-/js-tokens-4.0.0.tgz", + "integrity": "sha512-RdJUflcE3cUzKiMqQgsCu06FPu9UdIJO0beYbPhHN4k6apgJtifcoCtT9bcxOpYBtpD2kCM6Sbzg4CausW/PKQ==", + "dev": true + }, + "node_modules/js-yaml": { + "version": "4.1.0", + "resolved": "https://registry.npmjs.org/js-yaml/-/js-yaml-4.1.0.tgz", + "integrity": "sha512-wpxZs9NoxZaJESJGIZTyDEaYpl0FKSA+FB9aJiyemKhMwkxQg63h4T1KJgUGHpTqPDNRcmmYLugrRjJlBtWvRA==", + "dev": true, + "dependencies": { + "argparse": "^2.0.1" + }, + "bin": { + "js-yaml": "bin/js-yaml.js" + } + }, + "node_modules/jsesc": { + "version": "2.5.2", + "resolved": "https://registry.npmjs.org/jsesc/-/jsesc-2.5.2.tgz", + "integrity": "sha512-OYu7XEzjkCQ3C5Ps3QIZsQfNpqoJyZZA99wd9aWd05NCtC5pWOkShK2mkL6HXQR6/Cy2lbNdPlZBpuQHXE63gA==", + "dev": true, + "bin": { + "jsesc": "bin/jsesc" + }, + "engines": { + "node": ">=4" + } + }, + "node_modules/json-buffer": { + "version": "3.0.1", + "resolved": "https://registry.npmjs.org/json-buffer/-/json-buffer-3.0.1.tgz", + "integrity": "sha512-4bV5BfR2mqfQTJm+V5tPPdf+ZpuhiIvTuAB5g8kcrXOZpTT/QwwVRWBywX1ozr6lEuPdbHxwaJlm9G6mI2sfSQ==", + "dev": true + }, + "node_modules/json-parse-even-better-errors": { + "version": "2.3.1", + "resolved": "https://registry.npmjs.org/json-parse-even-better-errors/-/json-parse-even-better-errors-2.3.1.tgz", + "integrity": "sha512-xyFwyhro/JEof6Ghe2iz2NcXoj2sloNsWr/XsERDK/oiPCfaNhl5ONfp+jQdAZRQQ0IJWNzH9zIZF7li91kh2w==", + "dev": true + }, + "node_modules/json-schema-traverse": { + "version": "0.4.1", + "resolved": "https://registry.npmjs.org/json-schema-traverse/-/json-schema-traverse-0.4.1.tgz", + "integrity": "sha512-xbbCH5dCYU5T8LcEhhuh7HJ88HXuW3qsI3Y0zOZFKfZEHcpWiHU/Jxzk629Brsab/mMiHQti9wMP+845RPe3Vg==", + "dev": true + }, + "node_modules/json-stable-stringify-without-jsonify": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/json-stable-stringify-without-jsonify/-/json-stable-stringify-without-jsonify-1.0.1.tgz", + "integrity": "sha1-nbe1lJatPzz+8wp1FC0tkwrXJlE=", + "dev": true + }, + "node_modules/json5": { + "version": "2.2.0", + "resolved": "https://registry.npmjs.org/json5/-/json5-2.2.0.tgz", + "integrity": "sha512-f+8cldu7X/y7RAJurMEJmdoKXGB/X550w2Nr3tTbezL6RwEE/iMcm+tZnXeoZtKuOq6ft8+CqzEkrIgx1fPoQA==", + "dev": true, + "dependencies": { + "minimist": "^1.2.5" + }, + "bin": { + "json5": "lib/cli.js" + }, + "engines": { + "node": ">=6" + } + }, + "node_modules/jsonc-parser": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/jsonc-parser/-/jsonc-parser-3.0.0.tgz", + "integrity": "sha512-fQzRfAbIBnR0IQvftw9FJveWiHp72Fg20giDrHz6TdfB12UH/uue0D3hm57UB5KgAVuniLMCaS8P1IMj9NR7cA==", + "dev": true + }, + "node_modules/just-extend": { + "version": "4.2.1", + "resolved": "https://registry.npmjs.org/just-extend/-/just-extend-4.2.1.tgz", + "integrity": "sha512-g3UB796vUFIY90VIv/WX3L2c8CS2MdWUww3CNrYmqza1Fg0DURc2K/O4YrnklBdQarSJ/y8JnJYDGc+1iumQjg==", + "dev": true + }, + "node_modules/keyv": { + "version": "4.0.4", + "resolved": "https://registry.npmjs.org/keyv/-/keyv-4.0.4.tgz", + "integrity": "sha512-vqNHbAc8BBsxk+7QBYLW0Y219rWcClspR6WSeoHYKG5mnsSoOH+BL1pWq02DDCVdvvuUny5rkBlzMRzoqc+GIg==", + "dev": true, + "dependencies": { + "json-buffer": "3.0.1" + } + }, + "node_modules/latest-version": { + "version": "5.1.0", + "resolved": "https://registry.npmjs.org/latest-version/-/latest-version-5.1.0.tgz", + "integrity": "sha512-weT+r0kTkRQdCdYCNtkMwWXQTMEswKrFBkm4ckQOMVhhqhIMI1UT2hMj+1iigIhgSZm5gTmrRXBNoGUgaTY1xA==", + "dev": true, + "dependencies": { + "package-json": "^6.3.0" + }, + "engines": { + "node": ">=8" + } + }, + "node_modules/levn": { + "version": "0.4.1", + "resolved": "https://registry.npmjs.org/levn/-/levn-0.4.1.tgz", + "integrity": "sha512-+bT2uH4E5LGE7h/n3evcS/sQlJXCpIp6ym8OWJ5eV6+67Dsql/LaaT7qJBAt2rzfoa/5QBGBhxDix1dMt2kQKQ==", + "dev": true, + "dependencies": { + "prelude-ls": "^1.2.1", + "type-check": "~0.4.0" + }, + "engines": { + "node": ">= 0.8.0" + } + }, + "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=", + "dev": true + }, + "node_modules/locate-path": { + "version": "6.0.0", + "resolved": "https://registry.npmjs.org/locate-path/-/locate-path-6.0.0.tgz", + "integrity": "sha512-iPZK6eYjbxRu3uB4/WZ3EsEIMJFMqAoopl3R+zuq0UjcAm/MO6KCweDgPfP3elTztoKP3KtnVHxTn2NHBSDVUw==", + "dev": true, + "dependencies": { + "p-locate": "^5.0.0" + }, + "engines": { + "node": ">=10" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/lodash": { + "version": "4.17.21", + "resolved": "https://registry.npmjs.org/lodash/-/lodash-4.17.21.tgz", + "integrity": "sha512-v2kDEe57lecTulaDIuNTPy3Ry4gLGJ6Z1O3vE1krgXZNrsQ+LFTGHVxVjcXPs17LhbZVGedAJv8XZ1tvj5FvSg==", + "dev": true + }, + "node_modules/lodash.flattendeep": { + "version": "4.4.0", + "resolved": "https://registry.npmjs.org/lodash.flattendeep/-/lodash.flattendeep-4.4.0.tgz", + "integrity": "sha1-+wMJF/hqMTTlvJvsDWngAT3f7bI=", + "dev": true + }, + "node_modules/lodash.get": { + "version": "4.4.2", + "resolved": "https://registry.npmjs.org/lodash.get/-/lodash.get-4.4.2.tgz", + "integrity": "sha1-LRd/ZS+jHpObRDjVNBSZ36OCXpk=", + "dev": true + }, + "node_modules/lodash.merge": { + "version": "4.6.2", + "resolved": "https://registry.npmjs.org/lodash.merge/-/lodash.merge-4.6.2.tgz", + "integrity": "sha512-0KpjqXRVvrYyCsX1swR/XTK0va6VQkQM6MNo7PqW77ByjAhoARA8EfrP1N4+KlKj8YS0ZUCtRT/YUuhyYDujIQ==", + "dev": true + }, + "node_modules/log-symbols": { + "version": "4.1.0", + "resolved": "https://registry.npmjs.org/log-symbols/-/log-symbols-4.1.0.tgz", + "integrity": "sha512-8XPvpAA8uyhfteu8pIvQxpJZ7SYYdpUivZpGy6sFsBuKRY/7rQGavedeB8aK+Zkyq6upMFVL/9AW6vOYzfRyLg==", + "dev": true, + "dependencies": { + "chalk": "^4.1.0", + "is-unicode-supported": "^0.1.0" + }, + "engines": { + "node": ">=10" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/lowercase-keys": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/lowercase-keys/-/lowercase-keys-2.0.0.tgz", + "integrity": "sha512-tqNXrS78oMOE73NMxK4EMLQsQowWf8jKooH9g7xPavRT706R6bkQJ6DY2Te7QukaZsulxa30wQ7bk0pm4XiHmA==", + "dev": true, + "engines": { + "node": ">=8" + } + }, + "node_modules/lru-cache": { + "version": "6.0.0", + "resolved": "https://registry.npmjs.org/lru-cache/-/lru-cache-6.0.0.tgz", + "integrity": "sha512-Jo6dJ04CmSjuznwJSS3pUeWmd/H0ffTlkXXgwZi+eq1UCmqQwCh+eLsYOYCwY991i2Fah4h1BEMCx4qThGbsiA==", + "dev": true, + "dependencies": { + "yallist": "^4.0.0" + }, + "engines": { + "node": ">=10" + } + }, + "node_modules/lunr": { + "version": "2.3.9", + "resolved": "https://registry.npmjs.org/lunr/-/lunr-2.3.9.tgz", + "integrity": "sha512-zTU3DaZaF3Rt9rhN3uBMGQD3dD2/vFQqnvZCDv4dl5iOzq2IZQqTxu90r4E5J+nP70J3ilqVCrbho2eWaeW8Ow==", + "dev": true + }, + "node_modules/macos-release": { + "version": "2.5.0", + "resolved": "https://registry.npmjs.org/macos-release/-/macos-release-2.5.0.tgz", + "integrity": "sha512-EIgv+QZ9r+814gjJj0Bt5vSLJLzswGmSUbUpbi9AIr/fsN2IWFBl2NucV9PAiek+U1STK468tEkxmVYUtuAN3g==", + "dev": true, + "engines": { + "node": ">=6" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/make-dir": { + "version": "3.1.0", + "resolved": "https://registry.npmjs.org/make-dir/-/make-dir-3.1.0.tgz", + "integrity": "sha512-g3FeP20LNwhALb/6Cz6Dd4F2ngze0jz7tbzrD2wAV+o9FeNHe4rL+yK2md0J/fiSf1sa1ADhXqi5+oVwOM/eGw==", + "dev": true, + "dependencies": { + "semver": "^6.0.0" + }, + "engines": { + "node": ">=8" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/make-dir/node_modules/semver": { + "version": "6.3.0", + "resolved": "https://registry.npmjs.org/semver/-/semver-6.3.0.tgz", + "integrity": "sha512-b39TBaTSfV6yBrapU89p5fKekE2m/NwnDocOVruQFS1/veMgdzuPcnOM34M6CwxW8jH/lxEa5rBoDeUwu5HHTw==", + "dev": true, + "bin": { + "semver": "bin/semver.js" + } + }, + "node_modules/make-error": { + "version": "1.3.6", + "resolved": "https://registry.npmjs.org/make-error/-/make-error-1.3.6.tgz", + "integrity": "sha512-s8UhlNe7vPKomQhC1qFelMokr/Sc3AgNbso3n74mVPA5LTZwkB9NlXf4XPamLxJE8h0gh73rM94xvwRT2CVInw==", + "dev": true + }, + "node_modules/marked": { + "version": "3.0.8", + "resolved": "https://registry.npmjs.org/marked/-/marked-3.0.8.tgz", + "integrity": "sha512-0gVrAjo5m0VZSJb4rpL59K1unJAMb/hm8HRXqasD8VeC8m91ytDPMritgFSlKonfdt+rRYYpP/JfLxgIX8yoSw==", + "dev": true, + "bin": { + "marked": "bin/marked" + }, + "engines": { + "node": ">= 12" + } + }, + "node_modules/merge-stream": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/merge-stream/-/merge-stream-2.0.0.tgz", + "integrity": "sha512-abv/qOcuPfk3URPfDzmZU1LKmuw8kT+0nIHvKrKgFrwifol/doWcdA4ZqsWQ8ENrFKkd67Mfpo/LovbIUsbt3w==", + "dev": true + }, + "node_modules/merge2": { + "version": "1.4.1", + "resolved": "https://registry.npmjs.org/merge2/-/merge2-1.4.1.tgz", + "integrity": "sha512-8q7VEgMJW4J8tcfVPy8g09NcQwZdbwFEqhe/WZkoIzjn/3TGDwtOCYtXGxA3O8tPzpczCCDgv+P2P5y00ZJOOg==", + "dev": true, + "engines": { + "node": ">= 8" + } + }, + "node_modules/micromatch": { + "version": "4.0.4", + "resolved": "https://registry.npmjs.org/micromatch/-/micromatch-4.0.4.tgz", + "integrity": "sha512-pRmzw/XUcwXGpD9aI9q/0XOwLNygjETJ8y0ao0wdqprrzDa4YnxLcz7fQRZr8voh8V10kGhABbNcHVk5wHgWwg==", + "dev": true, + "dependencies": { + "braces": "^3.0.1", + "picomatch": "^2.2.3" + }, + "engines": { + "node": ">=8.6" + } + }, + "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==", + "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==", + "dev": true, + "dependencies": { + "mime-db": "1.49.0" + }, + "engines": { + "node": ">= 0.6" + } + }, + "node_modules/mimic-fn": { + "version": "2.1.0", + "resolved": "https://registry.npmjs.org/mimic-fn/-/mimic-fn-2.1.0.tgz", + "integrity": "sha512-OqbOk5oEQeAZ8WXWydlu9HJjz9WVdEIvamMCcXmuqUYjTknH/sqsWvhQ3vgwKFRR1HpjvNBKQ37nbJgYzGqGcg==", + "dev": true, + "engines": { + "node": ">=6" + } + }, + "node_modules/mimic-response": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/mimic-response/-/mimic-response-1.0.1.tgz", + "integrity": "sha512-j5EctnkH7amfV/q5Hgmoal1g2QHFJRraOtmx0JpIqkxhBhI/lJSl1nMpQ45hVarwNETOoWEimndZ4QK0RHxuxQ==", + "dev": true, + "engines": { + "node": ">=4" + } + }, + "node_modules/minimatch": { + "version": "3.0.4", + "resolved": "https://registry.npmjs.org/minimatch/-/minimatch-3.0.4.tgz", + "integrity": "sha512-yJHVQEhyqPLUTgt9B83PXu6W3rx4MvvHvSUvToogpwoGDOUQ+yDrR0HRot+yOCdCO7u4hX3pWft6kWBBcqh0UA==", + "dev": true, + "dependencies": { + "brace-expansion": "^1.1.7" + }, + "engines": { + "node": "*" + } + }, + "node_modules/minimist": { + "version": "1.2.5", + "resolved": "https://registry.npmjs.org/minimist/-/minimist-1.2.5.tgz", + "integrity": "sha512-FM9nNUYrRBAELZQT3xeZQ7fmMOBg6nWNmJKTcgsJeaLstP/UODVpGsr5OhXhhXg6f+qtJ8uiZ+PUxkDWcgIXLw==", + "dev": true + }, + "node_modules/mocha": { + "version": "9.1.3", + "resolved": "https://registry.npmjs.org/mocha/-/mocha-9.1.3.tgz", + "integrity": "sha512-Xcpl9FqXOAYqI3j79pEtHBBnQgVXIhpULjGQa7DVb0Po+VzmSIK9kanAiWLHoRR/dbZ2qpdPshuXr8l1VaHCzw==", + "dev": true, + "dependencies": { + "@ungap/promise-all-settled": "1.1.2", + "ansi-colors": "4.1.1", + "browser-stdout": "1.3.1", + "chokidar": "3.5.2", + "debug": "4.3.2", + "diff": "5.0.0", + "escape-string-regexp": "4.0.0", + "find-up": "5.0.0", + "glob": "7.1.7", + "growl": "1.10.5", + "he": "1.2.0", + "js-yaml": "4.1.0", + "log-symbols": "4.1.0", + "minimatch": "3.0.4", + "ms": "2.1.3", + "nanoid": "3.1.25", + "serialize-javascript": "6.0.0", + "strip-json-comments": "3.1.1", + "supports-color": "8.1.1", + "which": "2.0.2", + "workerpool": "6.1.5", + "yargs": "16.2.0", + "yargs-parser": "20.2.4", + "yargs-unparser": "2.0.0" + }, + "bin": { + "_mocha": "bin/_mocha", + "mocha": "bin/mocha" + }, + "engines": { + "node": ">= 12.0.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/mochajs" + } + }, + "node_modules/mocha/node_modules/ms": { + "version": "2.1.3", + "resolved": "https://registry.npmjs.org/ms/-/ms-2.1.3.tgz", + "integrity": "sha512-6FlzubTLZG3J2a/NVCAleEhjzq5oxgHyaCU9yYXvcLsvoVaHJq/s5xXI6/XXP6tz7R9xAOtHnSO/tXtF3WRTlA==", + "dev": true + }, + "node_modules/mocha/node_modules/supports-color": { + "version": "8.1.1", + "resolved": "https://registry.npmjs.org/supports-color/-/supports-color-8.1.1.tgz", + "integrity": "sha512-MpUEN2OodtUzxvKQl72cUF7RQ5EiHsGvSsVG0ia9c5RbWGL2CI4C7EpPS8UTBIplnlzZiNuV56w+FuNxy3ty2Q==", + "dev": true, + "dependencies": { + "has-flag": "^4.0.0" + }, + "engines": { + "node": ">=10" + }, + "funding": { + "url": "https://github.com/chalk/supports-color?sponsor=1" + } + }, + "node_modules/mocha/node_modules/yargs": { + "version": "16.2.0", + "resolved": "https://registry.npmjs.org/yargs/-/yargs-16.2.0.tgz", + "integrity": "sha512-D1mvvtDG0L5ft/jGWkLpG1+m0eQxOfaBvTNELraWj22wSVUMWxZUvYgJYcKh6jGGIkJFhH4IZPQhR4TKpc8mBw==", + "dev": true, + "dependencies": { + "cliui": "^7.0.2", + "escalade": "^3.1.1", + "get-caller-file": "^2.0.5", + "require-directory": "^2.1.1", + "string-width": "^4.2.0", + "y18n": "^5.0.5", + "yargs-parser": "^20.2.2" + }, + "engines": { + "node": ">=10" + } + }, + "node_modules/ms": { + "version": "2.1.2", + "resolved": "https://registry.npmjs.org/ms/-/ms-2.1.2.tgz", + "integrity": "sha512-sGkPx+VjMtmA6MX27oA4FBFELFCZZ4S4XqeGOXCv68tT+jb3vk/RyaKWP0PTKyWtmLSM0b+adUTEvbs1PEaH2w==", + "dev": true + }, + "node_modules/mute-stream": { + "version": "0.0.8", + "resolved": "https://registry.npmjs.org/mute-stream/-/mute-stream-0.0.8.tgz", + "integrity": "sha512-nnbWWOkoWyUsTjKrhgD0dcz22mdkSnpYqbEjIm2nhwhuxlSkpywJmBo8h0ZqJdkp73mb90SssHkN4rsRaBAfAA==", + "dev": true + }, + "node_modules/nanoid": { + "version": "3.1.25", + "resolved": "https://registry.npmjs.org/nanoid/-/nanoid-3.1.25.tgz", + "integrity": "sha512-rdwtIXaXCLFAQbnfqDRnI6jaRHp9fTcYBjtFKE8eezcZ7LuLjhUaQGNeMXf1HmRoCH32CLz6XwX0TtxEOS/A3Q==", + "dev": true, + "bin": { + "nanoid": "bin/nanoid.cjs" + }, + "engines": { + "node": "^10 || ^12 || ^13.7 || ^14 || >=15.0.1" + } + }, + "node_modules/natural-compare": { + "version": "1.4.0", + "resolved": "https://registry.npmjs.org/natural-compare/-/natural-compare-1.4.0.tgz", + "integrity": "sha1-Sr6/7tdUHywnrPspvbvRXI1bpPc=", + "dev": true + }, + "node_modules/neo-async": { + "version": "2.6.2", + "resolved": "https://registry.npmjs.org/neo-async/-/neo-async-2.6.2.tgz", + "integrity": "sha512-Yd3UES5mWCSqR+qNT93S3UoYUkqAZ9lLg8a7g9rimsWmYGK8cVToA4/sF3RrshdyV3sAGMXVUmpMYOw+dLpOuw==", + "dev": true + }, + "node_modules/new-github-release-url": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/new-github-release-url/-/new-github-release-url-1.0.0.tgz", + "integrity": "sha512-dle7yf655IMjyFUqn6Nxkb18r4AOAkzRcgcZv6WZ0IqrOH4QCEZ8Sm6I7XX21zvHdBeeMeTkhR9qT2Z0EJDx6A==", + "dev": true, + "dependencies": { + "type-fest": "^0.4.1" + }, + "engines": { + "node": ">=10" + } + }, + "node_modules/new-github-release-url/node_modules/type-fest": { + "version": "0.4.1", + "resolved": "https://registry.npmjs.org/type-fest/-/type-fest-0.4.1.tgz", + "integrity": "sha512-IwzA/LSfD2vC1/YDYMv/zHP4rDF1usCwllsDpbolT3D4fUepIO7f9K70jjmUewU/LmGUKJcwcVtDCpnKk4BPMw==", + "dev": true, + "engines": { + "node": ">=6" + } + }, + "node_modules/nise": { + "version": "5.1.0", + "resolved": "https://registry.npmjs.org/nise/-/nise-5.1.0.tgz", + "integrity": "sha512-W5WlHu+wvo3PaKLsJJkgPup2LrsXCcm7AWwyNZkUnn5rwPkuPBi3Iwk5SQtN0mv+K65k7nKKjwNQ30wg3wLAQQ==", + "dev": true, + "dependencies": { + "@sinonjs/commons": "^1.7.0", + "@sinonjs/fake-timers": "^7.0.4", + "@sinonjs/text-encoding": "^0.7.1", + "just-extend": "^4.0.2", + "path-to-regexp": "^1.7.0" + } + }, + "node_modules/node-fetch": { + "version": "2.6.6", + "resolved": "https://registry.npmjs.org/node-fetch/-/node-fetch-2.6.6.tgz", + "integrity": "sha512-Z8/6vRlTUChSdIgMa51jxQ4lrw/Jy5SOW10ObaA47/RElsAN2c5Pn8bTgFGWn/ibwzXTE8qwr1Yzx28vsecXEA==", + "dev": true, + "dependencies": { + "whatwg-url": "^5.0.0" + }, + "engines": { + "node": "4.x || >=6.0.0" + } + }, + "node_modules/node-preload": { + "version": "0.2.1", + "resolved": "https://registry.npmjs.org/node-preload/-/node-preload-0.2.1.tgz", + "integrity": "sha512-RM5oyBy45cLEoHqCeh+MNuFAxO0vTFBLskvQbOKnEE7YTTSN4tbN8QWDIPQ6L+WvKsB/qLEGpYe2ZZ9d4W9OIQ==", + "dev": true, + "dependencies": { + "process-on-spawn": "^1.0.0" + }, + "engines": { + "node": ">=8" + } + }, + "node_modules/node-releases": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/node-releases/-/node-releases-2.0.1.tgz", + "integrity": "sha512-CqyzN6z7Q6aMeF/ktcMVTzhAHCEpf8SOarwpzpf8pNBY2k5/oM34UHldUwp8VKI7uxct2HxSRdJjBaZeESzcxA==", + "dev": true + }, + "node_modules/normalize-path": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/normalize-path/-/normalize-path-3.0.0.tgz", + "integrity": "sha512-6eZs5Ls3WtCisHWp9S2GUy8dqkpGi4BVSz3GaqiE6ezub0512ESztXUwUB6C6IKbQkY2Pnb/mD4WYojCRwcwLA==", + "dev": true, + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/normalize-url": { + "version": "6.1.0", + "resolved": "https://registry.npmjs.org/normalize-url/-/normalize-url-6.1.0.tgz", + "integrity": "sha512-DlL+XwOy3NxAQ8xuC0okPgK46iuVNAK01YN7RueYBqqFeGsBjV9XmCAzAdgt+667bCl5kPh9EqKKDwnaPG1I7A==", + "dev": true, + "engines": { + "node": ">=10" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/npm-run-path": { + "version": "4.0.1", + "resolved": "https://registry.npmjs.org/npm-run-path/-/npm-run-path-4.0.1.tgz", + "integrity": "sha512-S48WzZW777zhNIrn7gxOlISNAqi9ZC/uQFnRdbeIHhZhCA6UqpkOT8T1G7BvfdgP4Er8gF4sUbaS0i7QvIfCWw==", + "dev": true, + "dependencies": { + "path-key": "^3.0.0" + }, + "engines": { + "node": ">=8" + } + }, + "node_modules/nyc": { + "version": "15.1.0", + "resolved": "https://registry.npmjs.org/nyc/-/nyc-15.1.0.tgz", + "integrity": "sha512-jMW04n9SxKdKi1ZMGhvUTHBN0EICCRkHemEoE5jm6mTYcqcdas0ATzgUgejlQUHMvpnOZqGB5Xxsv9KxJW1j8A==", + "dev": true, + "dependencies": { + "@istanbuljs/load-nyc-config": "^1.0.0", + "@istanbuljs/schema": "^0.1.2", + "caching-transform": "^4.0.0", + "convert-source-map": "^1.7.0", + "decamelize": "^1.2.0", + "find-cache-dir": "^3.2.0", + "find-up": "^4.1.0", + "foreground-child": "^2.0.0", + "get-package-type": "^0.1.0", + "glob": "^7.1.6", + "istanbul-lib-coverage": "^3.0.0", + "istanbul-lib-hook": "^3.0.0", + "istanbul-lib-instrument": "^4.0.0", + "istanbul-lib-processinfo": "^2.0.2", + "istanbul-lib-report": "^3.0.0", + "istanbul-lib-source-maps": "^4.0.0", + "istanbul-reports": "^3.0.2", + "make-dir": "^3.0.0", + "node-preload": "^0.2.1", + "p-map": "^3.0.0", + "process-on-spawn": "^1.0.0", + "resolve-from": "^5.0.0", + "rimraf": "^3.0.0", + "signal-exit": "^3.0.2", + "spawn-wrap": "^2.0.0", + "test-exclude": "^6.0.0", + "yargs": "^15.0.2" + }, + "bin": { + "nyc": "bin/nyc.js" + }, + "engines": { + "node": ">=8.9" + } + }, + "node_modules/nyc/node_modules/cliui": { + "version": "6.0.0", + "resolved": "https://registry.npmjs.org/cliui/-/cliui-6.0.0.tgz", + "integrity": "sha512-t6wbgtoCXvAzst7QgXxJYqPt0usEfbgQdftEPbLL/cvv6HPE5VgvqCuAIDR0NgU52ds6rFwqrgakNLrHEjCbrQ==", + "dev": true, + "dependencies": { + "string-width": "^4.2.0", + "strip-ansi": "^6.0.0", + "wrap-ansi": "^6.2.0" + } + }, + "node_modules/nyc/node_modules/find-up": { + "version": "4.1.0", + "resolved": "https://registry.npmjs.org/find-up/-/find-up-4.1.0.tgz", + "integrity": "sha512-PpOwAdQ/YlXQ2vj8a3h8IipDuYRi3wceVQQGYWxNINccq40Anw7BlsEXCMbt1Zt+OLA6Fq9suIpIWD0OsnISlw==", + "dev": true, + "dependencies": { + "locate-path": "^5.0.0", + "path-exists": "^4.0.0" + }, + "engines": { + "node": ">=8" + } + }, + "node_modules/nyc/node_modules/locate-path": { + "version": "5.0.0", + "resolved": "https://registry.npmjs.org/locate-path/-/locate-path-5.0.0.tgz", + "integrity": "sha512-t7hw9pI+WvuwNJXwk5zVHpyhIqzg2qTlklJOf0mVxGSbe3Fp2VieZcduNYjaLDoy6p9uGpQEGWG87WpMKlNq8g==", + "dev": true, + "dependencies": { + "p-locate": "^4.1.0" + }, + "engines": { + "node": ">=8" + } + }, + "node_modules/nyc/node_modules/p-limit": { + "version": "2.3.0", + "resolved": "https://registry.npmjs.org/p-limit/-/p-limit-2.3.0.tgz", + "integrity": "sha512-//88mFWSJx8lxCzwdAABTJL2MyWB12+eIY7MDL2SqLmAkeKU9qxRvWuSyTjm3FUmpBEMuFfckAIqEaVGUDxb6w==", + "dev": true, + "dependencies": { + "p-try": "^2.0.0" + }, + "engines": { + "node": ">=6" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/nyc/node_modules/p-locate": { + "version": "4.1.0", + "resolved": "https://registry.npmjs.org/p-locate/-/p-locate-4.1.0.tgz", + "integrity": "sha512-R79ZZ/0wAxKGu3oYMlz8jy/kbhsNrS7SKZ7PxEHBgJ5+F2mtFW2fK2cOtBh1cHYkQsbzFV7I+EoRKe6Yt0oK7A==", + "dev": true, + "dependencies": { + "p-limit": "^2.2.0" + }, + "engines": { + "node": ">=8" + } + }, + "node_modules/nyc/node_modules/resolve-from": { + "version": "5.0.0", + "resolved": "https://registry.npmjs.org/resolve-from/-/resolve-from-5.0.0.tgz", + "integrity": "sha512-qYg9KP24dD5qka9J47d0aVky0N+b4fTU89LN9iDnjB5waksiC49rvMB0PrUJQGoTmH50XPiqOvAjDfaijGxYZw==", + "dev": true, + "engines": { + "node": ">=8" + } + }, + "node_modules/nyc/node_modules/wrap-ansi": { + "version": "6.2.0", + "resolved": "https://registry.npmjs.org/wrap-ansi/-/wrap-ansi-6.2.0.tgz", + "integrity": "sha512-r6lPcBGxZXlIcymEu7InxDMhdW0KDxpLgoFLcguasxCaJ/SOIZwINatK9KY/tf+ZrlywOKU0UDj3ATXUBfxJXA==", + "dev": true, + "dependencies": { + "ansi-styles": "^4.0.0", + "string-width": "^4.1.0", + "strip-ansi": "^6.0.0" + }, + "engines": { + "node": ">=8" + } + }, + "node_modules/nyc/node_modules/y18n": { + "version": "4.0.3", + "resolved": "https://registry.npmjs.org/y18n/-/y18n-4.0.3.tgz", + "integrity": "sha512-JKhqTOwSrqNA1NY5lSztJ1GrBiUodLMmIZuLiDaMRJ+itFd+ABVE8XBjOvIWL+rSqNDC74LCSFmlb/U4UZ4hJQ==", + "dev": true + }, + "node_modules/nyc/node_modules/yargs": { + "version": "15.4.1", + "resolved": "https://registry.npmjs.org/yargs/-/yargs-15.4.1.tgz", + "integrity": "sha512-aePbxDmcYW++PaqBsJ+HYUFwCdv4LVvdnhBy78E57PIor8/OVvhMrADFFEDh8DHDFRv/O9i3lPhsENjO7QX0+A==", + "dev": true, + "dependencies": { + "cliui": "^6.0.0", + "decamelize": "^1.2.0", + "find-up": "^4.1.0", + "get-caller-file": "^2.0.1", + "require-directory": "^2.1.1", + "require-main-filename": "^2.0.0", + "set-blocking": "^2.0.0", + "string-width": "^4.2.0", + "which-module": "^2.0.0", + "y18n": "^4.0.0", + "yargs-parser": "^18.1.2" + }, + "engines": { + "node": ">=8" + } + }, + "node_modules/nyc/node_modules/yargs-parser": { + "version": "18.1.3", + "resolved": "https://registry.npmjs.org/yargs-parser/-/yargs-parser-18.1.3.tgz", + "integrity": "sha512-o50j0JeToy/4K6OZcaQmW6lyXXKhq7csREXcDwk2omFPJEwUNOVtJKvmDr9EI1fAJZUyZcRF7kxGBWmRXudrCQ==", + "dev": true, + "dependencies": { + "camelcase": "^5.0.0", + "decamelize": "^1.2.0" + }, + "engines": { + "node": ">=6" + } + }, + "node_modules/object-inspect": { + "version": "1.11.0", + "resolved": "https://registry.npmjs.org/object-inspect/-/object-inspect-1.11.0.tgz", + "integrity": "sha512-jp7ikS6Sd3GxQfZJPyH3cjcbJF6GZPClgdV+EFygjFLQ5FmW/dRUnTd9PQ9k0JhoNDabWFbpF1yCdSWCC6gexg==", + "dev": true, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/once": { + "version": "1.4.0", + "resolved": "https://registry.npmjs.org/once/-/once-1.4.0.tgz", + "integrity": "sha1-WDsap3WWHUsROsF9nFC6753Xa9E=", + "dev": true, + "dependencies": { + "wrappy": "1" + } + }, + "node_modules/onetime": { + "version": "5.1.2", + "resolved": "https://registry.npmjs.org/onetime/-/onetime-5.1.2.tgz", + "integrity": "sha512-kbpaSSGJTWdAY5KPVeMOKXSrPtr8C8C7wodJbcsd51jRnmD+GZu8Y0VoU6Dm5Z4vWr0Ig/1NKuWRKf7j5aaYSg==", + "dev": true, + "dependencies": { + "mimic-fn": "^2.1.0" + }, + "engines": { + "node": ">=6" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/onigasm": { + "version": "2.2.5", + "resolved": "https://registry.npmjs.org/onigasm/-/onigasm-2.2.5.tgz", + "integrity": "sha512-F+th54mPc0l1lp1ZcFMyL/jTs2Tlq4SqIHKIXGZOR/VkHkF9A7Fr5rRr5+ZG/lWeRsyrClLYRq7s/yFQ/XhWCA==", + "dev": true, + "dependencies": { + "lru-cache": "^5.1.1" + } + }, + "node_modules/onigasm/node_modules/lru-cache": { + "version": "5.1.1", + "resolved": "https://registry.npmjs.org/lru-cache/-/lru-cache-5.1.1.tgz", + "integrity": "sha512-KpNARQA3Iwv+jTA0utUVVbrh+Jlrr1Fv0e56GGzAFOXN7dk/FviaDW8LHmK52DlcH4WP2n6gI8vN1aesBFgo9w==", + "dev": true, + "dependencies": { + "yallist": "^3.0.2" + } + }, + "node_modules/onigasm/node_modules/yallist": { + "version": "3.1.1", + "resolved": "https://registry.npmjs.org/yallist/-/yallist-3.1.1.tgz", + "integrity": "sha512-a4UGQaWPH59mOXUYnAG2ewncQS4i4F43Tv3JoAM+s2VDAmS9NsK8GpDMLrCHPksFT7h3K6TOoUNn2pb7RoXx4g==", + "dev": true + }, + "node_modules/open": { + "version": "7.4.2", + "resolved": "https://registry.npmjs.org/open/-/open-7.4.2.tgz", + "integrity": "sha512-MVHddDVweXZF3awtlAS+6pgKLlm/JgxZ90+/NBurBoQctVOOB/zDdVjcyPzQ+0laDGbsWgrRkflI65sQeOgT9Q==", + "dev": true, + "dependencies": { + "is-docker": "^2.0.0", + "is-wsl": "^2.1.1" + }, + "engines": { + "node": ">=8" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/optionator": { + "version": "0.9.1", + "resolved": "https://registry.npmjs.org/optionator/-/optionator-0.9.1.tgz", + "integrity": "sha512-74RlY5FCnhq4jRxVUPKDaRwrVNXMqsGsiW6AJw4XK8hmtm10wC0ypZBLw5IIp85NZMr91+qd1RvvENwg7jjRFw==", + "dev": true, + "dependencies": { + "deep-is": "^0.1.3", + "fast-levenshtein": "^2.0.6", + "levn": "^0.4.1", + "prelude-ls": "^1.2.1", + "type-check": "^0.4.0", + "word-wrap": "^1.2.3" + }, + "engines": { + "node": ">= 0.8.0" + } + }, + "node_modules/ora": { + "version": "5.4.1", + "resolved": "https://registry.npmjs.org/ora/-/ora-5.4.1.tgz", + "integrity": "sha512-5b6Y85tPxZZ7QytO+BQzysW31HJku27cRIlkbAXaNx+BdcVi+LlRFmVXzeF6a7JCwJpyw5c4b+YSVImQIrBpuQ==", + "dev": true, + "dependencies": { + "bl": "^4.1.0", + "chalk": "^4.1.0", + "cli-cursor": "^3.1.0", + "cli-spinners": "^2.5.0", + "is-interactive": "^1.0.0", + "is-unicode-supported": "^0.1.0", + "log-symbols": "^4.1.0", + "strip-ansi": "^6.0.0", + "wcwidth": "^1.0.1" + }, + "engines": { + "node": ">=10" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/os-name": { + "version": "4.0.1", + "resolved": "https://registry.npmjs.org/os-name/-/os-name-4.0.1.tgz", + "integrity": "sha512-xl9MAoU97MH1Xt5K9ERft2YfCAoaO6msy1OBA0ozxEC0x0TmIoE6K3QvgJMMZA9yKGLmHXNY/YZoDbiGDj4zYw==", + "dev": true, + "dependencies": { + "macos-release": "^2.5.0", + "windows-release": "^4.0.0" + }, + "engines": { + "node": ">=10" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/os-tmpdir": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/os-tmpdir/-/os-tmpdir-1.0.2.tgz", + "integrity": "sha1-u+Z0BseaqFxc/sdm/lc0VV36EnQ=", + "dev": true, + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/p-cancelable": { + "version": "2.1.1", + "resolved": "https://registry.npmjs.org/p-cancelable/-/p-cancelable-2.1.1.tgz", + "integrity": "sha512-BZOr3nRQHOntUjTrH8+Lh54smKHoHyur8We1V8DSMVrl5A2malOOwuJRnKRDjSnkoeBh4at6BwEnb5I7Jl31wg==", + "dev": true, + "engines": { + "node": ">=8" + } + }, + "node_modules/p-limit": { + "version": "3.1.0", + "resolved": "https://registry.npmjs.org/p-limit/-/p-limit-3.1.0.tgz", + "integrity": "sha512-TYOanM3wGwNGsZN2cVTYPArw454xnXj5qmWF1bEoAc4+cU/ol7GVh7odevjp1FNHduHc3KZMcFduxU5Xc6uJRQ==", + "dev": true, + "dependencies": { + "yocto-queue": "^0.1.0" + }, + "engines": { + "node": ">=10" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/p-locate": { + "version": "5.0.0", + "resolved": "https://registry.npmjs.org/p-locate/-/p-locate-5.0.0.tgz", + "integrity": "sha512-LaNjtRWUBY++zB5nE/NwcaoMylSPk+S+ZHNB1TzdbMJMny6dynpAGt7X/tl/QYq3TIeE6nxHppbo2LGymrG5Pw==", + "dev": true, + "dependencies": { + "p-limit": "^3.0.2" + }, + "engines": { + "node": ">=10" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/p-map": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/p-map/-/p-map-3.0.0.tgz", + "integrity": "sha512-d3qXVTF/s+W+CdJ5A29wywV2n8CQQYahlgz2bFiA+4eVNJbHJodPZ+/gXwPGh0bOqA+j8S+6+ckmvLGPk1QpxQ==", + "dev": true, + "dependencies": { + "aggregate-error": "^3.0.0" + }, + "engines": { + "node": ">=8" + } + }, + "node_modules/p-try": { + "version": "2.2.0", + "resolved": "https://registry.npmjs.org/p-try/-/p-try-2.2.0.tgz", + "integrity": "sha512-R4nPAVTAU0B9D35/Gk3uJf/7XYbQcyohSKdvAxIRSNghFl4e71hVoGnBNQz9cWaXxO2I10KTC+3jMdvvoKw6dQ==", + "dev": true, + "engines": { + "node": ">=6" + } + }, + "node_modules/package-hash": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/package-hash/-/package-hash-4.0.0.tgz", + "integrity": "sha512-whdkPIooSu/bASggZ96BWVvZTRMOFxnyUG5PnTSGKoJE2gd5mbVNmR2Nj20QFzxYYgAXpoqC+AiXzl+UMRh7zQ==", + "dev": true, + "dependencies": { + "graceful-fs": "^4.1.15", + "hasha": "^5.0.0", + "lodash.flattendeep": "^4.4.0", + "release-zalgo": "^1.0.0" + }, + "engines": { + "node": ">=8" + } + }, + "node_modules/package-json": { + "version": "6.5.0", + "resolved": "https://registry.npmjs.org/package-json/-/package-json-6.5.0.tgz", + "integrity": "sha512-k3bdm2n25tkyxcjSKzB5x8kfVxlMdgsbPr0GkZcwHsLpba6cBjqCt1KlcChKEvxHIcTB1FVMuwoijZ26xex5MQ==", + "dev": true, + "dependencies": { + "got": "^9.6.0", + "registry-auth-token": "^4.0.0", + "registry-url": "^5.0.0", + "semver": "^6.2.0" + }, + "engines": { + "node": ">=8" + } + }, + "node_modules/package-json/node_modules/@sindresorhus/is": { + "version": "0.14.0", + "resolved": "https://registry.npmjs.org/@sindresorhus/is/-/is-0.14.0.tgz", + "integrity": "sha512-9NET910DNaIPngYnLLPeg+Ogzqsi9uM4mSboU5y6p8S5DzMTVEsJZrawi+BoDNUVBa2DhJqQYUFvMDfgU062LQ==", + "dev": true, + "engines": { + "node": ">=6" + } + }, + "node_modules/package-json/node_modules/@szmarczak/http-timer": { + "version": "1.1.2", + "resolved": "https://registry.npmjs.org/@szmarczak/http-timer/-/http-timer-1.1.2.tgz", + "integrity": "sha512-XIB2XbzHTN6ieIjfIMV9hlVcfPU26s2vafYWQcZHWXHOxiaRZYEDKEwdl129Zyg50+foYV2jCgtrqSA6qNuNSA==", + "dev": true, + "dependencies": { + "defer-to-connect": "^1.0.1" + }, + "engines": { + "node": ">=6" + } + }, + "node_modules/package-json/node_modules/cacheable-request": { + "version": "6.1.0", + "resolved": "https://registry.npmjs.org/cacheable-request/-/cacheable-request-6.1.0.tgz", + "integrity": "sha512-Oj3cAGPCqOZX7Rz64Uny2GYAZNliQSqfbePrgAQ1wKAihYmCUnraBtJtKcGR4xz7wF+LoJC+ssFZvv5BgF9Igg==", + "dev": true, + "dependencies": { + "clone-response": "^1.0.2", + "get-stream": "^5.1.0", + "http-cache-semantics": "^4.0.0", + "keyv": "^3.0.0", + "lowercase-keys": "^2.0.0", + "normalize-url": "^4.1.0", + "responselike": "^1.0.2" + }, + "engines": { + "node": ">=8" + } + }, + "node_modules/package-json/node_modules/cacheable-request/node_modules/get-stream": { + "version": "5.2.0", + "resolved": "https://registry.npmjs.org/get-stream/-/get-stream-5.2.0.tgz", + "integrity": "sha512-nBF+F1rAZVCu/p7rjzgA+Yb4lfYXrpl7a6VmJrU8wF9I1CKvP/QwPNZHnOlwbTkY6dvtFIzFMSyQXbLoTQPRpA==", + "dev": true, + "dependencies": { + "pump": "^3.0.0" + }, + "engines": { + "node": ">=8" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/package-json/node_modules/decompress-response": { + "version": "3.3.0", + "resolved": "https://registry.npmjs.org/decompress-response/-/decompress-response-3.3.0.tgz", + "integrity": "sha1-gKTdMjdIOEv6JICDYirt7Jgq3/M=", + "dev": true, + "dependencies": { + "mimic-response": "^1.0.0" + }, + "engines": { + "node": ">=4" + } + }, + "node_modules/package-json/node_modules/defer-to-connect": { + "version": "1.1.3", + "resolved": "https://registry.npmjs.org/defer-to-connect/-/defer-to-connect-1.1.3.tgz", + "integrity": "sha512-0ISdNousHvZT2EiFlZeZAHBUvSxmKswVCEf8hW7KWgG4a8MVEu/3Vb6uWYozkjylyCxe0JBIiRB1jV45S70WVQ==", + "dev": true + }, + "node_modules/package-json/node_modules/get-stream": { + "version": "4.1.0", + "resolved": "https://registry.npmjs.org/get-stream/-/get-stream-4.1.0.tgz", + "integrity": "sha512-GMat4EJ5161kIy2HevLlr4luNjBgvmj413KaQA7jt4V8B4RDsfpHk7WQ9GVqfYyyx8OS/L66Kox+rJRNklLK7w==", + "dev": true, + "dependencies": { + "pump": "^3.0.0" + }, + "engines": { + "node": ">=6" + } + }, + "node_modules/package-json/node_modules/got": { + "version": "9.6.0", + "resolved": "https://registry.npmjs.org/got/-/got-9.6.0.tgz", + "integrity": "sha512-R7eWptXuGYxwijs0eV+v3o6+XH1IqVK8dJOEecQfTmkncw9AV4dcw/Dhxi8MdlqPthxxpZyizMzyg8RTmEsG+Q==", + "dev": true, + "dependencies": { + "@sindresorhus/is": "^0.14.0", + "@szmarczak/http-timer": "^1.1.2", + "cacheable-request": "^6.0.0", + "decompress-response": "^3.3.0", + "duplexer3": "^0.1.4", + "get-stream": "^4.1.0", + "lowercase-keys": "^1.0.1", + "mimic-response": "^1.0.1", + "p-cancelable": "^1.0.0", + "to-readable-stream": "^1.0.0", + "url-parse-lax": "^3.0.0" + }, + "engines": { + "node": ">=8.6" + } + }, + "node_modules/package-json/node_modules/got/node_modules/lowercase-keys": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/lowercase-keys/-/lowercase-keys-1.0.1.tgz", + "integrity": "sha512-G2Lj61tXDnVFFOi8VZds+SoQjtQC3dgokKdDG2mTm1tx4m50NUHBOZSBwQQHyy0V12A0JTG4icfZQH+xPyh8VA==", + "dev": true, + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/package-json/node_modules/json-buffer": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/json-buffer/-/json-buffer-3.0.0.tgz", + "integrity": "sha1-Wx85evx11ne96Lz8Dkfh+aPZqJg=", + "dev": true + }, + "node_modules/package-json/node_modules/keyv": { + "version": "3.1.0", + "resolved": "https://registry.npmjs.org/keyv/-/keyv-3.1.0.tgz", + "integrity": "sha512-9ykJ/46SN/9KPM/sichzQ7OvXyGDYKGTaDlKMGCAlg2UK8KRy4jb0d8sFc+0Tt0YYnThq8X2RZgCg74RPxgcVA==", + "dev": true, + "dependencies": { + "json-buffer": "3.0.0" + } + }, + "node_modules/package-json/node_modules/normalize-url": { + "version": "4.5.1", + "resolved": "https://registry.npmjs.org/normalize-url/-/normalize-url-4.5.1.tgz", + "integrity": "sha512-9UZCFRHQdNrfTpGg8+1INIg93B6zE0aXMVFkw1WFwvO4SlZywU6aLg5Of0Ap/PgcbSw4LNxvMWXMeugwMCX0AA==", + "dev": true, + "engines": { + "node": ">=8" + } + }, + "node_modules/package-json/node_modules/p-cancelable": { + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/p-cancelable/-/p-cancelable-1.1.0.tgz", + "integrity": "sha512-s73XxOZ4zpt1edZYZzvhqFa6uvQc1vwUa0K0BdtIZgQMAJj9IbebH+JkgKZc9h+B05PKHLOTl4ajG1BmNrVZlw==", + "dev": true, + "engines": { + "node": ">=6" + } + }, + "node_modules/package-json/node_modules/responselike": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/responselike/-/responselike-1.0.2.tgz", + "integrity": "sha1-kYcg7ztjHFZCvgaPFa3lpG9Loec=", + "dev": true, + "dependencies": { + "lowercase-keys": "^1.0.0" + } + }, + "node_modules/package-json/node_modules/responselike/node_modules/lowercase-keys": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/lowercase-keys/-/lowercase-keys-1.0.1.tgz", + "integrity": "sha512-G2Lj61tXDnVFFOi8VZds+SoQjtQC3dgokKdDG2mTm1tx4m50NUHBOZSBwQQHyy0V12A0JTG4icfZQH+xPyh8VA==", + "dev": true, + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/package-json/node_modules/semver": { + "version": "6.3.0", + "resolved": "https://registry.npmjs.org/semver/-/semver-6.3.0.tgz", + "integrity": "sha512-b39TBaTSfV6yBrapU89p5fKekE2m/NwnDocOVruQFS1/veMgdzuPcnOM34M6CwxW8jH/lxEa5rBoDeUwu5HHTw==", + "dev": true, + "bin": { + "semver": "bin/semver.js" + } + }, + "node_modules/parent-module": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/parent-module/-/parent-module-1.0.1.tgz", + "integrity": "sha512-GQ2EWRpQV8/o+Aw8YqtfZZPfNRWZYkbidE9k5rpl/hC3vtHHBfGm2Ifi6qWV+coDGkrUKZAxE3Lot5kcsRlh+g==", + "dev": true, + "dependencies": { + "callsites": "^3.0.0" + }, + "engines": { + "node": ">=6" + } + }, + "node_modules/parse-json": { + "version": "5.2.0", + "resolved": "https://registry.npmjs.org/parse-json/-/parse-json-5.2.0.tgz", + "integrity": "sha512-ayCKvm/phCGxOkYRSCM82iDwct8/EonSEgCSxWxD7ve6jHggsFl4fZVQBPRNgQoKiuV/odhFrGzQXZwbifC8Rg==", + "dev": true, + "dependencies": { + "@babel/code-frame": "^7.0.0", + "error-ex": "^1.3.1", + "json-parse-even-better-errors": "^2.3.0", + "lines-and-columns": "^1.1.6" + }, + "engines": { + "node": ">=8" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/parse-path": { + "version": "4.0.3", + "resolved": "https://registry.npmjs.org/parse-path/-/parse-path-4.0.3.tgz", + "integrity": "sha512-9Cepbp2asKnWTJ9x2kpw6Fe8y9JDbqwahGCTvklzd/cEq5C5JC59x2Xb0Kx+x0QZ8bvNquGO8/BWP0cwBHzSAA==", + "dev": true, + "dependencies": { + "is-ssh": "^1.3.0", + "protocols": "^1.4.0", + "qs": "^6.9.4", + "query-string": "^6.13.8" + } + }, + "node_modules/parse-url": { + "version": "6.0.0", + "resolved": "https://registry.npmjs.org/parse-url/-/parse-url-6.0.0.tgz", + "integrity": "sha512-cYyojeX7yIIwuJzledIHeLUBVJ6COVLeT4eF+2P6aKVzwvgKQPndCBv3+yQ7pcWjqToYwaligxzSYNNmGoMAvw==", + "dev": true, + "dependencies": { + "is-ssh": "^1.3.0", + "normalize-url": "^6.1.0", + "parse-path": "^4.0.0", + "protocols": "^1.4.0" + } + }, + "node_modules/path-exists": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/path-exists/-/path-exists-4.0.0.tgz", + "integrity": "sha512-ak9Qy5Q7jYb2Wwcey5Fpvg2KoAc/ZIhLSLOSBmRmygPsGwkVVt0fZa0qrtMz+m6tJTAHfZQ8FnmB4MG4LWy7/w==", + "dev": true, + "engines": { + "node": ">=8" + } + }, + "node_modules/path-is-absolute": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/path-is-absolute/-/path-is-absolute-1.0.1.tgz", + "integrity": "sha1-F0uSaHNVNP+8es5r9TpanhtcX18=", + "dev": true, + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/path-key": { + "version": "3.1.1", + "resolved": "https://registry.npmjs.org/path-key/-/path-key-3.1.1.tgz", + "integrity": "sha512-ojmeN0qd+y0jszEtoY48r0Peq5dwMEkIlCOu6Q5f41lfkswXuKtYrhgoTpLnyIcHm24Uhqx+5Tqm2InSwLhE6Q==", + "dev": true, + "engines": { + "node": ">=8" + } + }, + "node_modules/path-parse": { + "version": "1.0.7", + "resolved": "https://registry.npmjs.org/path-parse/-/path-parse-1.0.7.tgz", + "integrity": "sha512-LDJzPVEEEPR+y48z93A0Ed0yXb8pAByGWo/k5YYdYgpY2/2EsOsksJrq7lOHxryrVOn1ejG6oAp8ahvOIQD8sw==", + "dev": true + }, + "node_modules/path-to-regexp": { + "version": "1.8.0", + "resolved": "https://registry.npmjs.org/path-to-regexp/-/path-to-regexp-1.8.0.tgz", + "integrity": "sha512-n43JRhlUKUAlibEJhPeir1ncUID16QnEjNpwzNdO3Lm4ywrBpBZ5oLD0I6br9evr1Y9JTqwRtAh7JLoOzAQdVA==", + "dev": true, + "dependencies": { + "isarray": "0.0.1" + } + }, + "node_modules/path-type": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/path-type/-/path-type-4.0.0.tgz", + "integrity": "sha512-gDKb8aZMDeD/tZWs9P6+q0J9Mwkdl6xMV8TjnGP3qJVJ06bdMgkbBlLU8IdfOsIsFz2BW1rNVT3XuNEl8zPAvw==", + "dev": true, + "engines": { + "node": ">=8" + } + }, + "node_modules/picocolors": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/picocolors/-/picocolors-1.0.0.tgz", + "integrity": "sha512-1fygroTLlHu66zi26VoTDv8yRgm0Fccecssto+MhsZ0D/DGW2sm8E8AjW7NU5VVTRt5GxbeZ5qBuJr+HyLYkjQ==", + "dev": true + }, + "node_modules/picomatch": { + "version": "2.3.0", + "resolved": "https://registry.npmjs.org/picomatch/-/picomatch-2.3.0.tgz", + "integrity": "sha512-lY1Q/PiJGC2zOv/z391WOTD+Z02bCgsFfvxoXXf6h7kv9o+WmsmzYqrAwY63sNgOxE4xEdq0WyUnXfKeBrSvYw==", + "dev": true, + "engines": { + "node": ">=8.6" + }, + "funding": { + "url": "https://github.com/sponsors/jonschlinkert" + } + }, + "node_modules/pkg-dir": { + "version": "4.2.0", + "resolved": "https://registry.npmjs.org/pkg-dir/-/pkg-dir-4.2.0.tgz", + "integrity": "sha512-HRDzbaKjC+AOWVXxAU/x54COGeIv9eb+6CkDSQoNTt4XyWoIJvuPsXizxu/Fr23EiekbtZwmh1IcIG/l/a10GQ==", + "dev": true, + "dependencies": { + "find-up": "^4.0.0" + }, + "engines": { + "node": ">=8" + } + }, + "node_modules/pkg-dir/node_modules/find-up": { + "version": "4.1.0", + "resolved": "https://registry.npmjs.org/find-up/-/find-up-4.1.0.tgz", + "integrity": "sha512-PpOwAdQ/YlXQ2vj8a3h8IipDuYRi3wceVQQGYWxNINccq40Anw7BlsEXCMbt1Zt+OLA6Fq9suIpIWD0OsnISlw==", + "dev": true, + "dependencies": { + "locate-path": "^5.0.0", + "path-exists": "^4.0.0" + }, + "engines": { + "node": ">=8" + } + }, + "node_modules/pkg-dir/node_modules/locate-path": { + "version": "5.0.0", + "resolved": "https://registry.npmjs.org/locate-path/-/locate-path-5.0.0.tgz", + "integrity": "sha512-t7hw9pI+WvuwNJXwk5zVHpyhIqzg2qTlklJOf0mVxGSbe3Fp2VieZcduNYjaLDoy6p9uGpQEGWG87WpMKlNq8g==", + "dev": true, + "dependencies": { + "p-locate": "^4.1.0" + }, + "engines": { + "node": ">=8" + } + }, + "node_modules/pkg-dir/node_modules/p-limit": { + "version": "2.3.0", + "resolved": "https://registry.npmjs.org/p-limit/-/p-limit-2.3.0.tgz", + "integrity": "sha512-//88mFWSJx8lxCzwdAABTJL2MyWB12+eIY7MDL2SqLmAkeKU9qxRvWuSyTjm3FUmpBEMuFfckAIqEaVGUDxb6w==", + "dev": true, + "dependencies": { + "p-try": "^2.0.0" + }, + "engines": { + "node": ">=6" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/pkg-dir/node_modules/p-locate": { + "version": "4.1.0", + "resolved": "https://registry.npmjs.org/p-locate/-/p-locate-4.1.0.tgz", + "integrity": "sha512-R79ZZ/0wAxKGu3oYMlz8jy/kbhsNrS7SKZ7PxEHBgJ5+F2mtFW2fK2cOtBh1cHYkQsbzFV7I+EoRKe6Yt0oK7A==", + "dev": true, + "dependencies": { + "p-limit": "^2.2.0" + }, + "engines": { + "node": ">=8" + } + }, + "node_modules/prelude-ls": { + "version": "1.2.1", + "resolved": "https://registry.npmjs.org/prelude-ls/-/prelude-ls-1.2.1.tgz", + "integrity": "sha512-vkcDPrRZo1QZLbn5RLGPpg/WmIQ65qoWWhcGKf/b5eplkkarX0m9z8ppCat4mlOqUsWpyNuYgO3VRyrYHSzX5g==", + "dev": true, + "engines": { + "node": ">= 0.8.0" + } + }, + "node_modules/prepend-http": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/prepend-http/-/prepend-http-2.0.0.tgz", + "integrity": "sha1-6SQ0v6XqjBn0HN/UAddBo8gZ2Jc=", + "dev": true, + "engines": { + "node": ">=4" + } + }, + "node_modules/process-on-spawn": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/process-on-spawn/-/process-on-spawn-1.0.0.tgz", + "integrity": "sha512-1WsPDsUSMmZH5LeMLegqkPDrsGgsWwk1Exipy2hvB0o/F0ASzbpIctSCcZIK1ykJvtTJULEH+20WOFjMvGnCTg==", + "dev": true, + "dependencies": { + "fromentries": "^1.2.0" + }, + "engines": { + "node": ">=8" + } + }, + "node_modules/progress": { + "version": "2.0.3", + "resolved": "https://registry.npmjs.org/progress/-/progress-2.0.3.tgz", + "integrity": "sha512-7PiHtLll5LdnKIMw100I+8xJXR5gW2QwWYkT6iJva0bXitZKa/XMrSbdmg3r2Xnaidz9Qumd0VPaMrZlF9V9sA==", + "dev": true, + "engines": { + "node": ">=0.4.0" + } + }, + "node_modules/protocols": { + "version": "1.4.8", + "resolved": "https://registry.npmjs.org/protocols/-/protocols-1.4.8.tgz", + "integrity": "sha512-IgjKyaUSjsROSO8/D49Ab7hP8mJgTYcqApOqdPhLoPxAplXmkp+zRvsrSQjFn5by0rhm4VH0GAUELIPpx7B1yg==", + "dev": true + }, + "node_modules/pump": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/pump/-/pump-3.0.0.tgz", + "integrity": "sha512-LwZy+p3SFs1Pytd/jYct4wpv49HiYCqd9Rlc5ZVdk0V+8Yzv6jR5Blk3TRmPL1ft69TxP0IMZGJ+WPFU2BFhww==", + "dev": true, + "dependencies": { + "end-of-stream": "^1.1.0", + "once": "^1.3.1" + } + }, + "node_modules/punycode": { + "version": "2.1.1", + "resolved": "https://registry.npmjs.org/punycode/-/punycode-2.1.1.tgz", + "integrity": "sha512-XRsRjdf+j5ml+y/6GKHPZbrF/8p2Yga0JPtdqTIY2Xe5ohJPD9saDJJLPvp9+NSBprVvevdXZybnj2cv8OEd0A==", + "dev": true, + "engines": { + "node": ">=6" + } + }, + "node_modules/pupa": { + "version": "2.1.1", + "resolved": "https://registry.npmjs.org/pupa/-/pupa-2.1.1.tgz", + "integrity": "sha512-l1jNAspIBSFqbT+y+5FosojNpVpF94nlI+wDUpqP9enwOTfHx9f0gh5nB96vl+6yTpsJsypeNrwfzPrKuHB41A==", + "dev": true, + "dependencies": { + "escape-goat": "^2.0.0" + }, + "engines": { + "node": ">=8" + } + }, + "node_modules/qs": { + "version": "6.10.1", + "resolved": "https://registry.npmjs.org/qs/-/qs-6.10.1.tgz", + "integrity": "sha512-M528Hph6wsSVOBiYUnGf+K/7w0hNshs/duGsNXPUCLH5XAqjEtiPGwNONLV0tBH8NoGb0mvD5JubnUTrujKDTg==", + "dev": true, + "dependencies": { + "side-channel": "^1.0.4" + }, + "engines": { + "node": ">=0.6" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/query-string": { + "version": "6.14.1", + "resolved": "https://registry.npmjs.org/query-string/-/query-string-6.14.1.tgz", + "integrity": "sha512-XDxAeVmpfu1/6IjyT/gXHOl+S0vQ9owggJ30hhWKdHAsNPOcasn5o9BW0eejZqL2e4vMjhAxoW3jVHcD6mbcYw==", + "dev": true, + "dependencies": { + "decode-uri-component": "^0.2.0", + "filter-obj": "^1.1.0", + "split-on-first": "^1.0.0", + "strict-uri-encode": "^2.0.0" + }, + "engines": { + "node": ">=6" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/queue-microtask": { + "version": "1.2.3", + "resolved": "https://registry.npmjs.org/queue-microtask/-/queue-microtask-1.2.3.tgz", + "integrity": "sha512-NuaNSa6flKT5JaSYQzJok04JzTL1CA6aGhv5rfLW3PgqA+M2ChpZQnAC8h8i4ZFkBS8X5RqkDBHA7r4hej3K9A==", + "dev": true, + "funding": [ + { + "type": "github", + "url": "https://github.com/sponsors/feross" + }, + { + "type": "patreon", + "url": "https://www.patreon.com/feross" + }, + { + "type": "consulting", + "url": "https://feross.org/support" + } + ] + }, + "node_modules/quick-lru": { + "version": "5.1.1", + "resolved": "https://registry.npmjs.org/quick-lru/-/quick-lru-5.1.1.tgz", + "integrity": "sha512-WuyALRjWPDGtt/wzJiadO5AXY+8hZ80hVpe6MyivgraREW751X3SbhRvG3eLKOYN+8VEvqLcf3wdnt44Z4S4SA==", + "dev": true, + "engines": { + "node": ">=10" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/randombytes": { + "version": "2.1.0", + "resolved": "https://registry.npmjs.org/randombytes/-/randombytes-2.1.0.tgz", + "integrity": "sha512-vYl3iOX+4CKUWuxGi9Ukhie6fsqXqS9FE2Zaic4tNFD2N2QQaXOMFbuKK4QmDHC0JO6B1Zp41J0LpT0oR68amQ==", + "dev": true, + "dependencies": { + "safe-buffer": "^5.1.0" + } + }, + "node_modules/rc": { + "version": "1.2.8", + "resolved": "https://registry.npmjs.org/rc/-/rc-1.2.8.tgz", + "integrity": "sha512-y3bGgqKj3QBdxLbLkomlohkvsA8gdAiUQlSBJnBhfn+BPxg4bc62d8TcBW15wavDfgexCgccckhcZvywyQYPOw==", + "dev": true, + "dependencies": { + "deep-extend": "^0.6.0", + "ini": "~1.3.0", + "minimist": "^1.2.0", + "strip-json-comments": "~2.0.1" + }, + "bin": { + "rc": "cli.js" + } + }, + "node_modules/rc/node_modules/ini": { + "version": "1.3.8", + "resolved": "https://registry.npmjs.org/ini/-/ini-1.3.8.tgz", + "integrity": "sha512-JV/yugV2uzW5iMRSiZAyDtQd+nxtUnjeLt0acNdw98kKLrvuRVyB80tsREOE7yvGVgalhZ6RNXCmEHkUKBKxew==", + "dev": true + }, + "node_modules/rc/node_modules/strip-json-comments": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/strip-json-comments/-/strip-json-comments-2.0.1.tgz", + "integrity": "sha1-PFMZQukIwml8DsNEhYwobHygpgo=", + "dev": true, + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/readable-stream": { + "version": "3.6.0", + "resolved": "https://registry.npmjs.org/readable-stream/-/readable-stream-3.6.0.tgz", + "integrity": "sha512-BViHy7LKeTz4oNnkcLJ+lVSL6vpiFeX6/d3oSH8zCW7UxP2onchk+vTGB143xuFjHS3deTgkKoXXymXqymiIdA==", + "dev": true, + "dependencies": { + "inherits": "^2.0.3", + "string_decoder": "^1.1.1", + "util-deprecate": "^1.0.1" + }, + "engines": { + "node": ">= 6" + } + }, + "node_modules/readdirp": { + "version": "3.6.0", + "resolved": "https://registry.npmjs.org/readdirp/-/readdirp-3.6.0.tgz", + "integrity": "sha512-hOS089on8RduqdbhvQ5Z37A0ESjsqz6qnRcffsMU3495FuTdqSm+7bhJ29JvIOsBDEEnan5DPu9t3To9VRlMzA==", + "dev": true, + "dependencies": { + "picomatch": "^2.2.1" + }, + "engines": { + "node": ">=8.10.0" + } + }, + "node_modules/rechoir": { + "version": "0.6.2", + "resolved": "https://registry.npmjs.org/rechoir/-/rechoir-0.6.2.tgz", + "integrity": "sha1-hSBLVNuoLVdC4oyWdW70OvUOM4Q=", + "dev": true, + "dependencies": { + "resolve": "^1.1.6" + }, + "engines": { + "node": ">= 0.10" + } + }, + "node_modules/redis": { + "resolved": "packages/all-in-one", + "link": true + }, + "node_modules/redis-errors": { + "version": "1.2.0", + "resolved": "https://registry.npmjs.org/redis-errors/-/redis-errors-1.2.0.tgz", + "integrity": "sha1-62LSrbFeTq9GEMBK/hUpOEJQq60=", + "engines": { + "node": ">=4" + } + }, + "node_modules/redis-parser": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/redis-parser/-/redis-parser-3.0.0.tgz", + "integrity": "sha1-tm2CjNyv5rS4pCin3vTGvKwxyLQ=", + "dependencies": { + "redis-errors": "^1.0.0" + }, + "engines": { + "node": ">=4" + } + }, + "node_modules/regexpp": { + "version": "3.2.0", + "resolved": "https://registry.npmjs.org/regexpp/-/regexpp-3.2.0.tgz", + "integrity": "sha512-pq2bWo9mVD43nbts2wGv17XLiNLya+GklZ8kaDLV2Z08gDCsGpnKn9BFMepvWuHCbyVvY7J5o5+BVvoQbmlJLg==", + "dev": true, + "engines": { + "node": ">=8" + }, + "funding": { + "url": "https://github.com/sponsors/mysticatea" + } + }, + "node_modules/registry-auth-token": { + "version": "4.2.1", + "resolved": "https://registry.npmjs.org/registry-auth-token/-/registry-auth-token-4.2.1.tgz", + "integrity": "sha512-6gkSb4U6aWJB4SF2ZvLb76yCBjcvufXBqvvEx1HbmKPkutswjW1xNVRY0+daljIYRbogN7O0etYSlbiaEQyMyw==", + "dev": true, + "dependencies": { + "rc": "^1.2.8" + }, + "engines": { + "node": ">=6.0.0" + } + }, + "node_modules/registry-url": { + "version": "5.1.0", + "resolved": "https://registry.npmjs.org/registry-url/-/registry-url-5.1.0.tgz", + "integrity": "sha512-8acYXXTI0AkQv6RAOjE3vOaIXZkT9wo4LOFbBKYQEEnnMNBpKqdUrI6S4NT0KPIo/WVvJ5tE/X5LF/TQUf0ekw==", + "dev": true, + "dependencies": { + "rc": "^1.2.8" + }, + "engines": { + "node": ">=8" + } + }, + "node_modules/release-it": { + "version": "14.11.6", + "resolved": "https://registry.npmjs.org/release-it/-/release-it-14.11.6.tgz", + "integrity": "sha512-6BNcuzFZHThBUBJ/xYw/bxZ+58CAwrwf1zgmjq2Ibl3nlDZbjphHG6iqxkJu7mZ8TIWs6NjloEAhqpjeXoN//Q==", + "dev": true, + "dependencies": { + "@iarna/toml": "2.2.5", + "@octokit/rest": "18.10.0", + "async-retry": "1.3.3", + "chalk": "4.1.2", + "cosmiconfig": "7.0.1", + "debug": "4.3.2", + "deprecated-obj": "2.0.0", + "execa": "5.1.1", + "form-data": "4.0.0", + "git-url-parse": "11.6.0", + "globby": "11.0.4", + "got": "11.8.2", + "import-cwd": "3.0.0", + "inquirer": "8.1.5", + "is-ci": "3.0.0", + "lodash": "4.17.21", + "mime-types": "2.1.32", + "new-github-release-url": "1.0.0", + "open": "7.4.2", + "ora": "5.4.1", + "os-name": "4.0.1", + "parse-json": "5.2.0", + "semver": "7.3.5", + "shelljs": "0.8.4", + "update-notifier": "5.1.0", + "url-join": "4.0.1", + "uuid": "8.3.2", + "yaml": "1.10.2", + "yargs-parser": "20.2.9" + }, + "bin": { + "release-it": "bin/release-it.js" + }, + "engines": { + "node": ">=10" + } + }, + "node_modules/release-it/node_modules/uuid": { + "version": "8.3.2", + "resolved": "https://registry.npmjs.org/uuid/-/uuid-8.3.2.tgz", + "integrity": "sha512-+NYs2QeMWy+GWFOEm9xnn6HCDp0l7QBD7ml8zLUmJ+93Q5NF0NocErnwkTkXVFNiX3/fpC6afS8Dhb/gz7R7eg==", + "dev": true, + "bin": { + "uuid": "dist/bin/uuid" + } + }, + "node_modules/release-it/node_modules/yargs-parser": { + "version": "20.2.9", + "resolved": "https://registry.npmjs.org/yargs-parser/-/yargs-parser-20.2.9.tgz", + "integrity": "sha512-y11nGElTIV+CT3Zv9t7VKl+Q3hTQoT9a1Qzezhhl6Rp21gJ/IVTW7Z3y9EWXhuUBC2Shnf+DX0antecpAwSP8w==", + "dev": true, + "engines": { + "node": ">=10" + } + }, + "node_modules/release-zalgo": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/release-zalgo/-/release-zalgo-1.0.0.tgz", + "integrity": "sha1-CXALflB0Mpc5Mw5TXFqQ+2eFFzA=", + "dev": true, + "dependencies": { + "es6-error": "^4.0.1" + }, + "engines": { + "node": ">=4" + } + }, + "node_modules/require-directory": { + "version": "2.1.1", + "resolved": "https://registry.npmjs.org/require-directory/-/require-directory-2.1.1.tgz", + "integrity": "sha1-jGStX9MNqxyXbiNE/+f3kqam30I=", + "dev": true, + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/require-main-filename": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/require-main-filename/-/require-main-filename-2.0.0.tgz", + "integrity": "sha512-NKN5kMDylKuldxYLSUfrbo5Tuzh4hd+2E8NPPX02mZtn1VuREQToYe/ZdlJy+J3uCpfaiGF05e7B8W0iXbQHmg==", + "dev": true + }, + "node_modules/resolve": { + "version": "1.20.0", + "resolved": "https://registry.npmjs.org/resolve/-/resolve-1.20.0.tgz", + "integrity": "sha512-wENBPt4ySzg4ybFQW2TT1zMQucPK95HSh/nq2CFTZVOGut2+pQvSsgtda4d26YrYcr067wjbmzOG8byDPBX63A==", + "dev": true, + "dependencies": { + "is-core-module": "^2.2.0", + "path-parse": "^1.0.6" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/resolve-alpn": { + "version": "1.2.1", + "resolved": "https://registry.npmjs.org/resolve-alpn/-/resolve-alpn-1.2.1.tgz", + "integrity": "sha512-0a1F4l73/ZFZOakJnQ3FvkJ2+gSTQWz/r2KE5OdDY0TxPm5h4GkqkWWfM47T7HsbnOtcJVEF4epCVy6u7Q3K+g==", + "dev": true + }, + "node_modules/resolve-from": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/resolve-from/-/resolve-from-4.0.0.tgz", + "integrity": "sha512-pb/MYmXstAkysRFx8piNI1tGFNQIFA3vkE3Gq4EuA1dF6gHp/+vgZqsCGJapvy8N3Q+4o7FwvquPJcnZ7RYy4g==", + "dev": true, + "engines": { + "node": ">=4" + } + }, + "node_modules/responselike": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/responselike/-/responselike-2.0.0.tgz", + "integrity": "sha512-xH48u3FTB9VsZw7R+vvgaKeLKzT6jOogbQhEe/jewwnZgzPcnyWui2Av6JpoYZF/91uueC+lqhWqeURw5/qhCw==", + "dev": true, + "dependencies": { + "lowercase-keys": "^2.0.0" + } + }, + "node_modules/restore-cursor": { + "version": "3.1.0", + "resolved": "https://registry.npmjs.org/restore-cursor/-/restore-cursor-3.1.0.tgz", + "integrity": "sha512-l+sSefzHpj5qimhFSE5a8nufZYAM3sBSVMAPtYkmC+4EH2anSGaEMXSD0izRQbu9nfyQ9y5JrVmp7E8oZrUjvA==", + "dev": true, + "dependencies": { + "onetime": "^5.1.0", + "signal-exit": "^3.0.2" + }, + "engines": { + "node": ">=8" + } + }, + "node_modules/retry": { + "version": "0.13.1", + "resolved": "https://registry.npmjs.org/retry/-/retry-0.13.1.tgz", + "integrity": "sha512-XQBQ3I8W1Cge0Seh+6gjj03LbmRFWuoszgK9ooCpwYIrhhoO80pfq4cUkU5DkknwfOfFteRwlZ56PYOGYyFWdg==", + "dev": true, + "engines": { + "node": ">= 4" + } + }, + "node_modules/reusify": { + "version": "1.0.4", + "resolved": "https://registry.npmjs.org/reusify/-/reusify-1.0.4.tgz", + "integrity": "sha512-U9nH88a3fc/ekCF1l0/UP1IosiuIjyTh7hBvXVMHYgVcfGvt897Xguj2UOLDeI5BG2m7/uwyaLVT6fbtCwTyzw==", + "dev": true, + "engines": { + "iojs": ">=1.0.0", + "node": ">=0.10.0" + } + }, + "node_modules/rimraf": { + "version": "3.0.2", + "resolved": "https://registry.npmjs.org/rimraf/-/rimraf-3.0.2.tgz", + "integrity": "sha512-JZkJMZkAGFFPP2YqXZXPbMlMBgsxzE8ILs4lMIX/2o0L9UBw9O/Y3o6wFw/i9YLapcUJWwqbi3kdxIPdC62TIA==", + "dev": true, + "dependencies": { + "glob": "^7.1.3" + }, + "bin": { + "rimraf": "bin.js" + }, + "funding": { + "url": "https://github.com/sponsors/isaacs" + } + }, + "node_modules/run-async": { + "version": "2.4.1", + "resolved": "https://registry.npmjs.org/run-async/-/run-async-2.4.1.tgz", + "integrity": "sha512-tvVnVv01b8c1RrA6Ep7JkStj85Guv/YrMcwqYQnwjsAS2cTmmPGBBjAjpCW7RrSodNSoE2/qg9O4bceNvUuDgQ==", + "dev": true, + "engines": { + "node": ">=0.12.0" + } + }, + "node_modules/run-parallel": { + "version": "1.2.0", + "resolved": "https://registry.npmjs.org/run-parallel/-/run-parallel-1.2.0.tgz", + "integrity": "sha512-5l4VyZR86LZ/lDxZTR6jqL8AFE2S0IFLMP26AbjsLVADxHdhB/c0GUsH+y39UfCi3dzz8OlQuPmnaJOMoDHQBA==", + "dev": true, + "funding": [ + { + "type": "github", + "url": "https://github.com/sponsors/feross" + }, + { + "type": "patreon", + "url": "https://www.patreon.com/feross" + }, + { + "type": "consulting", + "url": "https://feross.org/support" + } + ], + "dependencies": { + "queue-microtask": "^1.2.2" + } + }, + "node_modules/rxjs": { + "version": "7.4.0", + "resolved": "https://registry.npmjs.org/rxjs/-/rxjs-7.4.0.tgz", + "integrity": "sha512-7SQDi7xeTMCJpqViXh8gL/lebcwlp3d831F05+9B44A4B0WfsEwUQHR64gsH1kvJ+Ep/J9K2+n1hVl1CsGN23w==", + "dev": true, + "dependencies": { + "tslib": "~2.1.0" + } + }, + "node_modules/safe-buffer": { + "version": "5.1.2", + "resolved": "https://registry.npmjs.org/safe-buffer/-/safe-buffer-5.1.2.tgz", + "integrity": "sha512-Gd2UZBJDkXlY7GbJxfsE8/nvKkUEU1G38c1siN6QP6a9PT9MmHB8GnpscSmMJSoF8LOIrt8ud/wPtojys4G6+g==", + "dev": true + }, + "node_modules/safer-buffer": { + "version": "2.1.2", + "resolved": "https://registry.npmjs.org/safer-buffer/-/safer-buffer-2.1.2.tgz", + "integrity": "sha512-YZo3K82SD7Riyi0E1EQPojLz7kpepnSQI9IyPbHHg1XXXevb5dJI7tpyN2ADxGcQbHG7vcyRHk0cbwqcQriUtg==", + "dev": true + }, + "node_modules/semver": { + "version": "7.3.5", + "resolved": "https://registry.npmjs.org/semver/-/semver-7.3.5.tgz", + "integrity": "sha512-PoeGJYh8HK4BTO/a9Tf6ZG3veo/A7ZVsYrSA6J8ny9nb3B1VrpkuN+z9OE5wfE5p6H4LchYZsegiQgbJD94ZFQ==", + "dev": true, + "dependencies": { + "lru-cache": "^6.0.0" + }, + "bin": { + "semver": "bin/semver.js" + }, + "engines": { + "node": ">=10" + } + }, + "node_modules/semver-diff": { + "version": "3.1.1", + "resolved": "https://registry.npmjs.org/semver-diff/-/semver-diff-3.1.1.tgz", + "integrity": "sha512-GX0Ix/CJcHyB8c4ykpHGIAvLyOwOobtM/8d+TQkAd81/bEjgPHrfba41Vpesr7jX/t8Uh+R3EX9eAS5be+jQYg==", + "dev": true, + "dependencies": { + "semver": "^6.3.0" + }, + "engines": { + "node": ">=8" + } + }, + "node_modules/semver-diff/node_modules/semver": { + "version": "6.3.0", + "resolved": "https://registry.npmjs.org/semver/-/semver-6.3.0.tgz", + "integrity": "sha512-b39TBaTSfV6yBrapU89p5fKekE2m/NwnDocOVruQFS1/veMgdzuPcnOM34M6CwxW8jH/lxEa5rBoDeUwu5HHTw==", + "dev": true, + "bin": { + "semver": "bin/semver.js" + } + }, + "node_modules/serialize-javascript": { + "version": "6.0.0", + "resolved": "https://registry.npmjs.org/serialize-javascript/-/serialize-javascript-6.0.0.tgz", + "integrity": "sha512-Qr3TosvguFt8ePWqsvRfrKyQXIiW+nGbYpy8XK24NQHE83caxWt+mIymTT19DGFbNWNLfEwsrkSmN64lVWB9ag==", + "dev": true, + "dependencies": { + "randombytes": "^2.1.0" + } + }, + "node_modules/set-blocking": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/set-blocking/-/set-blocking-2.0.0.tgz", + "integrity": "sha1-BF+XgtARrppoA93TgrJDkrPYkPc=", + "dev": true + }, + "node_modules/shebang-command": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/shebang-command/-/shebang-command-2.0.0.tgz", + "integrity": "sha512-kHxr2zZpYtdmrN1qDjrrX/Z1rR1kG8Dx+gkpK1G4eXmvXswmcE1hTWBWYUzlraYw1/yZp6YuDY77YtvbN0dmDA==", + "dev": true, + "dependencies": { + "shebang-regex": "^3.0.0" + }, + "engines": { + "node": ">=8" + } + }, + "node_modules/shebang-regex": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/shebang-regex/-/shebang-regex-3.0.0.tgz", + "integrity": "sha512-7++dFhtcx3353uBaq8DDR4NuxBetBzC7ZQOhmTQInHEd6bSrXdiEyzCvG07Z44UYdLShWUyXt5M/yhz8ekcb1A==", + "dev": true, + "engines": { + "node": ">=8" + } + }, + "node_modules/shelljs": { + "version": "0.8.4", + "resolved": "https://registry.npmjs.org/shelljs/-/shelljs-0.8.4.tgz", + "integrity": "sha512-7gk3UZ9kOfPLIAbslLzyWeGiEqx9e3rxwZM0KE6EL8GlGwjym9Mrlx5/p33bWTu9YG6vcS4MBxYZDHYr5lr8BQ==", + "dev": true, + "dependencies": { + "glob": "^7.0.0", + "interpret": "^1.0.0", + "rechoir": "^0.6.2" + }, + "bin": { + "shjs": "bin/shjs" + }, + "engines": { + "node": ">=4" + } + }, + "node_modules/shiki": { + "version": "0.9.12", + "resolved": "https://registry.npmjs.org/shiki/-/shiki-0.9.12.tgz", + "integrity": "sha512-VXcROdldv0/Qu0w2XvzU4IrvTeBNs/Kj/FCmtcEXGz7Tic/veQzliJj6tEiAgoKianhQstpYmbPDStHU5Opqcw==", + "dev": true, + "dependencies": { + "jsonc-parser": "^3.0.0", + "onigasm": "^2.2.5", + "vscode-textmate": "5.2.0" + } + }, + "node_modules/side-channel": { + "version": "1.0.4", + "resolved": "https://registry.npmjs.org/side-channel/-/side-channel-1.0.4.tgz", + "integrity": "sha512-q5XPytqFEIKHkGdiMIrY10mvLRvnQh42/+GoBlFW3b2LXLE2xxJpZFdm94we0BaoV3RwJyGqg5wS7epxTv0Zvw==", + "dev": true, + "dependencies": { + "call-bind": "^1.0.0", + "get-intrinsic": "^1.0.2", + "object-inspect": "^1.9.0" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/signal-exit": { + "version": "3.0.5", + "resolved": "https://registry.npmjs.org/signal-exit/-/signal-exit-3.0.5.tgz", + "integrity": "sha512-KWcOiKeQj6ZyXx7zq4YxSMgHRlod4czeBQZrPb8OKcohcqAXShm7E20kEMle9WBt26hFcAf0qLOcp5zmY7kOqQ==", + "dev": true + }, + "node_modules/sinon": { + "version": "11.1.2", + "resolved": "https://registry.npmjs.org/sinon/-/sinon-11.1.2.tgz", + "integrity": "sha512-59237HChms4kg7/sXhiRcUzdSkKuydDeTiamT/jesUVHshBgL8XAmhgFo0GfK6RruMDM/iRSij1EybmMog9cJw==", + "dev": true, + "dependencies": { + "@sinonjs/commons": "^1.8.3", + "@sinonjs/fake-timers": "^7.1.2", + "@sinonjs/samsam": "^6.0.2", + "diff": "^5.0.0", + "nise": "^5.1.0", + "supports-color": "^7.2.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/sinon" + } + }, + "node_modules/slash": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/slash/-/slash-3.0.0.tgz", + "integrity": "sha512-g9Q1haeby36OSStwb4ntCGGGaKsaVSjQ68fBxoQcutl5fS1vuY18H3wSt3jFyFtrkx+Kz0V1G85A4MyAdDMi2Q==", + "dev": true, + "engines": { + "node": ">=8" + } + }, + "node_modules/source-map": { + "version": "0.5.7", + "resolved": "https://registry.npmjs.org/source-map/-/source-map-0.5.7.tgz", + "integrity": "sha1-igOdLRAh0i0eoUyA2OpGi6LvP8w=", + "dev": true, + "engines": { + "node": ">=0.10.0" + } + }, + "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==", + "dev": true, + "dependencies": { + "buffer-from": "^1.0.0", + "source-map": "^0.6.0" + } + }, + "node_modules/source-map-support/node_modules/source-map": { + "version": "0.6.1", + "resolved": "https://registry.npmjs.org/source-map/-/source-map-0.6.1.tgz", + "integrity": "sha512-UjgapumWlbMhkBgzT7Ykc5YXUT46F0iKu8SGXq0bcwP5dz/h0Plj6enJqjz1Zbq2l5WaqYnrVbwWOWMyF3F47g==", + "dev": true, + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/spawn-wrap": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/spawn-wrap/-/spawn-wrap-2.0.0.tgz", + "integrity": "sha512-EeajNjfN9zMnULLwhZZQU3GWBoFNkbngTUPfaawT4RkMiviTxcX0qfhVbGey39mfctfDHkWtuecgQ8NJcyQWHg==", + "dev": true, + "dependencies": { + "foreground-child": "^2.0.0", + "is-windows": "^1.0.2", + "make-dir": "^3.0.0", + "rimraf": "^3.0.0", + "signal-exit": "^3.0.2", + "which": "^2.0.1" + }, + "engines": { + "node": ">=8" + } + }, + "node_modules/split-on-first": { + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/split-on-first/-/split-on-first-1.1.0.tgz", + "integrity": "sha512-43ZssAJaMusuKWL8sKUBQXHWOpq8d6CfN/u1p4gUzfJkM05C8rxTmYrkIPTXapZpORA6LkkzcUulJ8FqA7Uudw==", + "dev": true, + "engines": { + "node": ">=6" + } + }, + "node_modules/sprintf-js": { + "version": "1.0.3", + "resolved": "https://registry.npmjs.org/sprintf-js/-/sprintf-js-1.0.3.tgz", + "integrity": "sha1-BOaSb2YolTVPPdAVIDYzuFcpfiw=", + "dev": true + }, + "node_modules/strict-uri-encode": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/strict-uri-encode/-/strict-uri-encode-2.0.0.tgz", + "integrity": "sha1-ucczDHBChi9rFC3CdLvMWGbONUY=", + "dev": true, + "engines": { + "node": ">=4" + } + }, + "node_modules/string_decoder": { + "version": "1.3.0", + "resolved": "https://registry.npmjs.org/string_decoder/-/string_decoder-1.3.0.tgz", + "integrity": "sha512-hkRX8U1WjJFd8LsDJ2yQ/wWWxaopEsABU1XfkM8A+j0+85JAGppt16cr1Whg6KIbb4okU6Mql6BOj+uup/wKeA==", + "dev": true, + "dependencies": { + "safe-buffer": "~5.2.0" + } + }, + "node_modules/string_decoder/node_modules/safe-buffer": { + "version": "5.2.1", + "resolved": "https://registry.npmjs.org/safe-buffer/-/safe-buffer-5.2.1.tgz", + "integrity": "sha512-rp3So07KcdmmKbGvgaNxQSJr7bGVSVk5S9Eq1F+ppbRo70+YeaDxkw5Dd8NPN+GD6bjnYm2VuPuCXmpuYvmCXQ==", + "dev": true, + "funding": [ + { + "type": "github", + "url": "https://github.com/sponsors/feross" + }, + { + "type": "patreon", + "url": "https://www.patreon.com/feross" + }, + { + "type": "consulting", + "url": "https://feross.org/support" + } + ] + }, + "node_modules/string-width": { + "version": "4.2.3", + "resolved": "https://registry.npmjs.org/string-width/-/string-width-4.2.3.tgz", + "integrity": "sha512-wKyQRQpjJ0sIp62ErSZdGsjMJWsap5oRNihHhu6G7JVO/9jIB6UyevL+tXuOqrng8j/cxKTWyWUwvSTriiZz/g==", + "dev": true, + "dependencies": { + "emoji-regex": "^8.0.0", + "is-fullwidth-code-point": "^3.0.0", + "strip-ansi": "^6.0.1" + }, + "engines": { + "node": ">=8" + } + }, + "node_modules/strip-ansi": { + "version": "6.0.1", + "resolved": "https://registry.npmjs.org/strip-ansi/-/strip-ansi-6.0.1.tgz", + "integrity": "sha512-Y38VPSHcqkFrCpFnQ9vuSXmquuv5oXOKpGeT6aGrr3o3Gc9AlVa6JBfUSOCnbxGGZF+/0ooI7KrPuUSztUdU5A==", + "dev": true, + "dependencies": { + "ansi-regex": "^5.0.1" + }, + "engines": { + "node": ">=8" + } + }, + "node_modules/strip-bom": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/strip-bom/-/strip-bom-4.0.0.tgz", + "integrity": "sha512-3xurFv5tEgii33Zi8Jtp55wEIILR9eh34FAW00PZf+JnSsTmV/ioewSgQl97JHvgjoRGwPShsWm+IdrxB35d0w==", + "dev": true, + "engines": { + "node": ">=8" + } + }, + "node_modules/strip-final-newline": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/strip-final-newline/-/strip-final-newline-2.0.0.tgz", + "integrity": "sha512-BrpvfNAE3dcvq7ll3xVumzjKjZQ5tI1sEUIKr3Uoks0XUl45St3FlatVqef9prk4jRDzhW6WZg+3bk93y6pLjA==", + "dev": true, + "engines": { + "node": ">=6" + } + }, + "node_modules/strip-json-comments": { + "version": "3.1.1", + "resolved": "https://registry.npmjs.org/strip-json-comments/-/strip-json-comments-3.1.1.tgz", + "integrity": "sha512-6fPc+R4ihwqP6N/aIv2f1gMH8lOVtWQHoqC4yK6oSDVVocumAsfCqjkXnqiYMhmMwS/mEHLp7Vehlt3ql6lEig==", + "dev": true, + "engines": { + "node": ">=8" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/supports-color": { + "version": "7.2.0", + "resolved": "https://registry.npmjs.org/supports-color/-/supports-color-7.2.0.tgz", + "integrity": "sha512-qpCAvRl9stuOHveKsn7HncJRvv501qIacKzQlO/+Lwxc9+0q2wLyv4Dfvt80/DPn2pqOBsJdDiogXGR9+OvwRw==", + "dev": true, + "dependencies": { + "has-flag": "^4.0.0" + }, + "engines": { + "node": ">=8" + } + }, + "node_modules/test-exclude": { + "version": "6.0.0", + "resolved": "https://registry.npmjs.org/test-exclude/-/test-exclude-6.0.0.tgz", + "integrity": "sha512-cAGWPIyOHU6zlmg88jwm7VRyXnMN7iV68OGAbYDk/Mh/xC/pzVPlQtY6ngoIH/5/tciuhGfvESU8GrHrcxD56w==", + "dev": true, + "dependencies": { + "@istanbuljs/schema": "^0.1.2", + "glob": "^7.1.4", + "minimatch": "^3.0.4" + }, + "engines": { + "node": ">=8" + } + }, + "node_modules/text-table": { + "version": "0.2.0", + "resolved": "https://registry.npmjs.org/text-table/-/text-table-0.2.0.tgz", + "integrity": "sha1-f17oI66AUgfACvLfSoTsP8+lcLQ=", + "dev": true + }, + "node_modules/through": { + "version": "2.3.8", + "resolved": "https://registry.npmjs.org/through/-/through-2.3.8.tgz", + "integrity": "sha1-DdTJ/6q8NXlgsbckEV1+Doai4fU=", + "dev": true + }, + "node_modules/tmp": { + "version": "0.0.33", + "resolved": "https://registry.npmjs.org/tmp/-/tmp-0.0.33.tgz", + "integrity": "sha512-jRCJlojKnZ3addtTOjdIqoRuPEKBvNXcGYqzO6zWZX8KfKEpnGY5jfggJQ3EjKuu8D4bJRr0y+cYJFmYbImXGw==", + "dev": true, + "dependencies": { + "os-tmpdir": "~1.0.2" + }, + "engines": { + "node": ">=0.6.0" + } + }, + "node_modules/to-fast-properties": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/to-fast-properties/-/to-fast-properties-2.0.0.tgz", + "integrity": "sha1-3F5pjL0HkmW8c+A3doGk5Og/YW4=", + "dev": true, + "engines": { + "node": ">=4" + } + }, + "node_modules/to-readable-stream": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/to-readable-stream/-/to-readable-stream-1.0.0.tgz", + "integrity": "sha512-Iq25XBt6zD5npPhlLVXGFN3/gyR2/qODcKNNyTMd4vbm39HUaOiAM4PMq0eMVC/Tkxz+Zjdsc55g9yyz+Yq00Q==", + "dev": true, + "engines": { + "node": ">=6" + } + }, + "node_modules/to-regex-range": { + "version": "5.0.1", + "resolved": "https://registry.npmjs.org/to-regex-range/-/to-regex-range-5.0.1.tgz", + "integrity": "sha512-65P7iz6X5yEr1cwcgvQxbbIw7Uk3gOy5dIdtZ4rDveLqhrdJP+Li/Hx6tyK0NEb+2GCyneCMJiGqrADCSNk8sQ==", + "dev": true, + "dependencies": { + "is-number": "^7.0.0" + }, + "engines": { + "node": ">=8.0" + } + }, + "node_modules/tr46": { + "version": "0.0.3", + "resolved": "https://registry.npmjs.org/tr46/-/tr46-0.0.3.tgz", + "integrity": "sha1-gYT9NH2snNwYWZLzpmIuFLnZq2o=", + "dev": true + }, + "node_modules/ts-node": { + "version": "10.4.0", + "resolved": "https://registry.npmjs.org/ts-node/-/ts-node-10.4.0.tgz", + "integrity": "sha512-g0FlPvvCXSIO1JDF6S232P5jPYqBkRL9qly81ZgAOSU7rwI0stphCgd2kLiCrU9DjQCrJMWEqcNSjQL02s6d8A==", + "dev": true, + "dependencies": { + "@cspotcode/source-map-support": "0.7.0", + "@tsconfig/node10": "^1.0.7", + "@tsconfig/node12": "^1.0.7", + "@tsconfig/node14": "^1.0.0", + "@tsconfig/node16": "^1.0.2", + "acorn": "^8.4.1", + "acorn-walk": "^8.1.1", + "arg": "^4.1.0", + "create-require": "^1.1.0", + "diff": "^4.0.1", + "make-error": "^1.1.1", + "yn": "3.1.1" + }, + "bin": { + "ts-node": "dist/bin.js", + "ts-node-cwd": "dist/bin-cwd.js", + "ts-node-script": "dist/bin-script.js", + "ts-node-transpile-only": "dist/bin-transpile.js", + "ts-script": "dist/bin-script-deprecated.js" + }, + "peerDependencies": { + "@swc/core": ">=1.2.50", + "@swc/wasm": ">=1.2.50", + "@types/node": "*", + "typescript": ">=2.7" + }, + "peerDependenciesMeta": { + "@swc/core": { + "optional": true + }, + "@swc/wasm": { + "optional": true + } + } + }, + "node_modules/ts-node/node_modules/diff": { + "version": "4.0.2", + "resolved": "https://registry.npmjs.org/diff/-/diff-4.0.2.tgz", + "integrity": "sha512-58lmxKSA4BNyLz+HHMUzlOEpg09FV+ev6ZMe3vJihgdxzgcwZ8VoEEPmALCZG9LmqfVoNMMKpttIYTVG6uDY7A==", + "dev": true, + "engines": { + "node": ">=0.3.1" + } + }, + "node_modules/tslib": { + "version": "2.1.0", + "resolved": "https://registry.npmjs.org/tslib/-/tslib-2.1.0.tgz", + "integrity": "sha512-hcVC3wYEziELGGmEEXue7D75zbwIIVUMWAVbHItGPx0ziyXxrOMQx4rQEVEV45Ut/1IotuEvwqPopzIOkDMf0A==", + "dev": true + }, + "node_modules/tsutils": { + "version": "3.21.0", + "resolved": "https://registry.npmjs.org/tsutils/-/tsutils-3.21.0.tgz", + "integrity": "sha512-mHKK3iUXL+3UF6xL5k0PEhKRUBKPBCv/+RkEOpjRWxxx27KKRBmmA60A9pgOUvMi8GKhRMPEmjBRPzs2W7O1OA==", + "dev": true, + "dependencies": { + "tslib": "^1.8.1" + }, + "engines": { + "node": ">= 6" + }, + "peerDependencies": { + "typescript": ">=2.8.0 || >= 3.2.0-dev || >= 3.3.0-dev || >= 3.4.0-dev || >= 3.5.0-dev || >= 3.6.0-dev || >= 3.6.0-beta || >= 3.7.0-dev || >= 3.7.0-beta" + } + }, + "node_modules/tsutils/node_modules/tslib": { + "version": "1.14.1", + "resolved": "https://registry.npmjs.org/tslib/-/tslib-1.14.1.tgz", + "integrity": "sha512-Xni35NKzjgMrwevysHTCArtLDpPvye8zV/0E4EyYn43P7/7qvQwPh9BGkHewbMulVntbigmcT7rdX3BNo9wRJg==", + "dev": true + }, + "node_modules/type-check": { + "version": "0.4.0", + "resolved": "https://registry.npmjs.org/type-check/-/type-check-0.4.0.tgz", + "integrity": "sha512-XleUoc9uwGXqjWwXaUTZAmzMcFZ5858QA2vvx1Ur5xIcixXIP+8LnFDgRplU30us6teqdlskFfu+ae4K79Ooew==", + "dev": true, + "dependencies": { + "prelude-ls": "^1.2.1" + }, + "engines": { + "node": ">= 0.8.0" + } + }, + "node_modules/type-detect": { + "version": "4.0.8", + "resolved": "https://registry.npmjs.org/type-detect/-/type-detect-4.0.8.tgz", + "integrity": "sha512-0fr/mIH1dlO+x7TlcMy+bIDqKPsw/70tVyeHW787goQjhmqaZe10uwLujubK9q9Lg6Fiho1KUKDYz0Z7k7g5/g==", + "dev": true, + "engines": { + "node": ">=4" + } + }, + "node_modules/type-fest": { + "version": "0.20.2", + "resolved": "https://registry.npmjs.org/type-fest/-/type-fest-0.20.2.tgz", + "integrity": "sha512-Ne+eE4r0/iWnpAxD852z3A+N0Bt5RN//NjJwRd2VFHEmrywxf5vsZlh4R6lixl6B+wz/8d+maTSAkN1FIkI3LQ==", + "dev": true, + "engines": { + "node": ">=10" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/typedarray-to-buffer": { + "version": "3.1.5", + "resolved": "https://registry.npmjs.org/typedarray-to-buffer/-/typedarray-to-buffer-3.1.5.tgz", + "integrity": "sha512-zdu8XMNEDepKKR+XYOXAVPtWui0ly0NtohUscw+UmaHiAWT8hrV1rr//H6V+0DvJ3OQ19S979M0laLfX8rm82Q==", + "dev": true, + "dependencies": { + "is-typedarray": "^1.0.0" + } + }, + "node_modules/typedoc": { + "version": "0.22.8", + "resolved": "https://registry.npmjs.org/typedoc/-/typedoc-0.22.8.tgz", + "integrity": "sha512-92S+YzyhospdXN5rnkYUTgirdTYqNWY7NP9vco+IqQQoiSXzVSUsawVro+tMyEEsWUS7EMaJ2YOjB9uE0CBi6A==", + "dev": true, + "dependencies": { + "glob": "^7.2.0", + "lunr": "^2.3.9", + "marked": "^3.0.8", + "minimatch": "^3.0.4", + "shiki": "^0.9.12" + }, + "bin": { + "typedoc": "bin/typedoc" + }, + "engines": { + "node": ">= 12.10.0" + }, + "peerDependencies": { + "typescript": "4.0.x || 4.1.x || 4.2.x || 4.3.x || 4.4.x" + } + }, + "node_modules/typedoc-github-wiki-theme": { + "version": "0.6.0", + "resolved": "https://registry.npmjs.org/typedoc-github-wiki-theme/-/typedoc-github-wiki-theme-0.6.0.tgz", + "integrity": "sha512-uHhR7PwAZ4JgO0KzlLocWSvMqKsSZ/wxUQYGKskIepzsotPAQcAWnSSnGi6gdkSw8UAfIIppdD7H1AmI39962w==", + "dev": true, + "peerDependencies": { + "typedoc": ">=0.22.0", + "typedoc-plugin-markdown": ">=3.11.0" + } + }, + "node_modules/typedoc-plugin-markdown": { + "version": "3.11.3", + "resolved": "https://registry.npmjs.org/typedoc-plugin-markdown/-/typedoc-plugin-markdown-3.11.3.tgz", + "integrity": "sha512-rWiHbEIe0oZetDIsBR24XJVxGOJ91kDcHoj2KhFKxCLoJGX659EKBQkHne9QJ4W2stGhu1fRgFyQaouSBnxukA==", + "dev": true, + "dependencies": { + "handlebars": "^4.7.7" + }, + "peerDependencies": { + "typedoc": ">=0.22.0" + } + }, + "node_modules/typedoc/node_modules/glob": { + "version": "7.2.0", + "resolved": "https://registry.npmjs.org/glob/-/glob-7.2.0.tgz", + "integrity": "sha512-lmLf6gtyrPq8tTjSmrO94wBeQbFR3HbLHbuyD69wuyQkImp2hWqMGB47OX65FBkPffO641IP9jWa1z4ivqG26Q==", + "dev": true, + "dependencies": { + "fs.realpath": "^1.0.0", + "inflight": "^1.0.4", + "inherits": "2", + "minimatch": "^3.0.4", + "once": "^1.3.0", + "path-is-absolute": "^1.0.0" + }, + "engines": { + "node": "*" + }, + "funding": { + "url": "https://github.com/sponsors/isaacs" + } + }, + "node_modules/typescript": { + "version": "4.4.4", + "resolved": "https://registry.npmjs.org/typescript/-/typescript-4.4.4.tgz", + "integrity": "sha512-DqGhF5IKoBl8WNf8C1gu8q0xZSInh9j1kJJMqT3a94w1JzVaBU4EXOSMrz9yDqMT0xt3selp83fuFMQ0uzv6qA==", + "dev": true, + "bin": { + "tsc": "bin/tsc", + "tsserver": "bin/tsserver" + }, + "engines": { + "node": ">=4.2.0" + } + }, + "node_modules/uglify-js": { + "version": "3.14.3", + "resolved": "https://registry.npmjs.org/uglify-js/-/uglify-js-3.14.3.tgz", + "integrity": "sha512-mic3aOdiq01DuSVx0TseaEzMIVqebMZ0Z3vaeDhFEh9bsc24hV1TFvN74reA2vs08D0ZWfNjAcJ3UbVLaBss+g==", + "dev": true, + "optional": true, + "bin": { + "uglifyjs": "bin/uglifyjs" + }, + "engines": { + "node": ">=0.8.0" + } + }, + "node_modules/unique-string": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/unique-string/-/unique-string-2.0.0.tgz", + "integrity": "sha512-uNaeirEPvpZWSgzwsPGtU2zVSTrn/8L5q/IexZmH0eH6SA73CmAA5U4GwORTxQAZs95TAXLNqeLoPPNO5gZfWg==", + "dev": true, + "dependencies": { + "crypto-random-string": "^2.0.0" + }, + "engines": { + "node": ">=8" + } + }, + "node_modules/universal-user-agent": { + "version": "6.0.0", + "resolved": "https://registry.npmjs.org/universal-user-agent/-/universal-user-agent-6.0.0.tgz", + "integrity": "sha512-isyNax3wXoKaulPDZWHQqbmIx1k2tb9fb3GGDBRxCscfYV2Ch7WxPArBsFEG8s/safwXTT7H4QGhaIkTp9447w==", + "dev": true + }, + "node_modules/update-notifier": { + "version": "5.1.0", + "resolved": "https://registry.npmjs.org/update-notifier/-/update-notifier-5.1.0.tgz", + "integrity": "sha512-ItnICHbeMh9GqUy31hFPrD1kcuZ3rpxDZbf4KUDavXwS0bW5m7SLbDQpGX3UYr072cbrF5hFUs3r5tUsPwjfHw==", + "dev": true, + "dependencies": { + "boxen": "^5.0.0", + "chalk": "^4.1.0", + "configstore": "^5.0.1", + "has-yarn": "^2.1.0", + "import-lazy": "^2.1.0", + "is-ci": "^2.0.0", + "is-installed-globally": "^0.4.0", + "is-npm": "^5.0.0", + "is-yarn-global": "^0.3.0", + "latest-version": "^5.1.0", + "pupa": "^2.1.1", + "semver": "^7.3.4", + "semver-diff": "^3.1.1", + "xdg-basedir": "^4.0.0" + }, + "engines": { + "node": ">=10" + }, + "funding": { + "url": "https://github.com/yeoman/update-notifier?sponsor=1" + } + }, + "node_modules/update-notifier/node_modules/ci-info": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/ci-info/-/ci-info-2.0.0.tgz", + "integrity": "sha512-5tK7EtrZ0N+OLFMthtqOj4fI2Jeb88C4CAZPu25LDVUgXJ0A3Js4PMGqrn0JU1W0Mh1/Z8wZzYPxqUrXeBboCQ==", + "dev": true + }, + "node_modules/update-notifier/node_modules/is-ci": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/is-ci/-/is-ci-2.0.0.tgz", + "integrity": "sha512-YfJT7rkpQB0updsdHLGWrvhBJfcfzNNawYDNIyQXJz0IViGf75O8EBPKSdvw2rF+LGCsX4FZ8tcr3b19LcZq4w==", + "dev": true, + "dependencies": { + "ci-info": "^2.0.0" + }, + "bin": { + "is-ci": "bin.js" + } + }, + "node_modules/uri-js": { + "version": "4.4.1", + "resolved": "https://registry.npmjs.org/uri-js/-/uri-js-4.4.1.tgz", + "integrity": "sha512-7rKUyy33Q1yc98pQ1DAmLtwX109F7TIfWlW1Ydo8Wl1ii1SeHieeh0HHfPeL2fMXK6z0s8ecKs9frCuLJvndBg==", + "dev": true, + "dependencies": { + "punycode": "^2.1.0" + } + }, + "node_modules/url-join": { + "version": "4.0.1", + "resolved": "https://registry.npmjs.org/url-join/-/url-join-4.0.1.tgz", + "integrity": "sha512-jk1+QP6ZJqyOiuEI9AEWQfju/nB2Pw466kbA0LEZljHwKeMgd9WrAEgEGxjPDD2+TNbbb37rTyhEfrCXfuKXnA==", + "dev": true + }, + "node_modules/url-parse-lax": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/url-parse-lax/-/url-parse-lax-3.0.0.tgz", + "integrity": "sha1-FrXK/Afb42dsGxmZF3gj1lA6yww=", + "dev": true, + "dependencies": { + "prepend-http": "^2.0.0" + }, + "engines": { + "node": ">=4" + } + }, + "node_modules/util-deprecate": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/util-deprecate/-/util-deprecate-1.0.2.tgz", + "integrity": "sha1-RQ1Nyfpw3nMnYvvS1KKJgUGaDM8=", + "dev": true + }, + "node_modules/uuid": { + "version": "3.4.0", + "resolved": "https://registry.npmjs.org/uuid/-/uuid-3.4.0.tgz", + "integrity": "sha512-HjSDRw6gZE5JMggctHBcjVak08+KEVhSIiDzFnT9S9aegmp85S/bReBVTb4QTFaRNptJ9kuYaNhnbNEOkbKb/A==", + "deprecated": "Please upgrade to version 7 or higher. Older versions may use Math.random() in certain circumstances, which is known to be problematic. See https://v8.dev/blog/math-random for details.", + "dev": true, + "bin": { + "uuid": "bin/uuid" + } + }, + "node_modules/v8-compile-cache": { + "version": "2.3.0", + "resolved": "https://registry.npmjs.org/v8-compile-cache/-/v8-compile-cache-2.3.0.tgz", + "integrity": "sha512-l8lCEmLcLYZh4nbunNZvQCJc5pv7+RCwa8q/LdUx8u7lsWvPDKmpodJAJNwkAhJC//dFY48KuIEmjtd4RViDrA==", + "dev": true + }, + "node_modules/vscode-textmate": { + "version": "5.2.0", + "resolved": "https://registry.npmjs.org/vscode-textmate/-/vscode-textmate-5.2.0.tgz", + "integrity": "sha512-Uw5ooOQxRASHgu6C7GVvUxisKXfSgW4oFlO+aa+PAkgmH89O3CXxEEzNRNtHSqtXFTl0nAC1uYj0GMSH27uwtQ==", + "dev": true + }, + "node_modules/wcwidth": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/wcwidth/-/wcwidth-1.0.1.tgz", + "integrity": "sha1-8LDc+RW8X/FSivrbLA4XtTLaL+g=", + "dev": true, + "dependencies": { + "defaults": "^1.0.3" + } + }, + "node_modules/webidl-conversions": { + "version": "3.0.1", + "resolved": "https://registry.npmjs.org/webidl-conversions/-/webidl-conversions-3.0.1.tgz", + "integrity": "sha1-JFNCdeKnvGvnvIZhHMFq4KVlSHE=", + "dev": true + }, + "node_modules/whatwg-url": { + "version": "5.0.0", + "resolved": "https://registry.npmjs.org/whatwg-url/-/whatwg-url-5.0.0.tgz", + "integrity": "sha1-lmRU6HZUYuN2RNNib2dCzotwll0=", + "dev": true, + "dependencies": { + "tr46": "~0.0.3", + "webidl-conversions": "^3.0.0" + } + }, + "node_modules/which": { + "version": "2.0.2", + "resolved": "https://registry.npmjs.org/which/-/which-2.0.2.tgz", + "integrity": "sha512-BLI3Tl1TW3Pvl70l3yq3Y64i+awpwXqsGBYWkkqMtnbXgrMD+yj7rhW0kuEDxzJaYXGjEW5ogapKNMEKNMjibA==", + "dev": true, + "dependencies": { + "isexe": "^2.0.0" + }, + "bin": { + "node-which": "bin/node-which" + }, + "engines": { + "node": ">= 8" + } + }, + "node_modules/which-module": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/which-module/-/which-module-2.0.0.tgz", + "integrity": "sha1-2e8H3Od7mQK4o6j6SzHD4/fm6Ho=", + "dev": true + }, + "node_modules/widest-line": { + "version": "3.1.0", + "resolved": "https://registry.npmjs.org/widest-line/-/widest-line-3.1.0.tgz", + "integrity": "sha512-NsmoXalsWVDMGupxZ5R08ka9flZjjiLvHVAWYOKtiKM8ujtZWr9cRffak+uSE48+Ob8ObalXpwyeUiyDD6QFgg==", + "dev": true, + "dependencies": { + "string-width": "^4.0.0" + }, + "engines": { + "node": ">=8" + } + }, + "node_modules/windows-release": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/windows-release/-/windows-release-4.0.0.tgz", + "integrity": "sha512-OxmV4wzDKB1x7AZaZgXMVsdJ1qER1ed83ZrTYd5Bwq2HfJVg3DJS8nqlAG4sMoJ7mu8cuRmLEYyU13BKwctRAg==", + "dev": true, + "dependencies": { + "execa": "^4.0.2" + }, + "engines": { + "node": ">=10" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/windows-release/node_modules/execa": { + "version": "4.1.0", + "resolved": "https://registry.npmjs.org/execa/-/execa-4.1.0.tgz", + "integrity": "sha512-j5W0//W7f8UxAn8hXVnwG8tLwdiUy4FJLcSupCg6maBYZDpyBvTApK7KyuI4bKj8KOh1r2YH+6ucuYtJv1bTZA==", + "dev": true, + "dependencies": { + "cross-spawn": "^7.0.0", + "get-stream": "^5.0.0", + "human-signals": "^1.1.1", + "is-stream": "^2.0.0", + "merge-stream": "^2.0.0", + "npm-run-path": "^4.0.0", + "onetime": "^5.1.0", + "signal-exit": "^3.0.2", + "strip-final-newline": "^2.0.0" + }, + "engines": { + "node": ">=10" + }, + "funding": { + "url": "https://github.com/sindresorhus/execa?sponsor=1" + } + }, + "node_modules/windows-release/node_modules/get-stream": { + "version": "5.2.0", + "resolved": "https://registry.npmjs.org/get-stream/-/get-stream-5.2.0.tgz", + "integrity": "sha512-nBF+F1rAZVCu/p7rjzgA+Yb4lfYXrpl7a6VmJrU8wF9I1CKvP/QwPNZHnOlwbTkY6dvtFIzFMSyQXbLoTQPRpA==", + "dev": true, + "dependencies": { + "pump": "^3.0.0" + }, + "engines": { + "node": ">=8" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/windows-release/node_modules/human-signals": { + "version": "1.1.1", + "resolved": "https://registry.npmjs.org/human-signals/-/human-signals-1.1.1.tgz", + "integrity": "sha512-SEQu7vl8KjNL2eoGBLF3+wAjpsNfA9XMlXAYj/3EdaNfAlxKthD1xjEQfGOUhllCGGJVNY34bRr6lPINhNjyZw==", + "dev": true, + "engines": { + "node": ">=8.12.0" + } + }, + "node_modules/word-wrap": { + "version": "1.2.3", + "resolved": "https://registry.npmjs.org/word-wrap/-/word-wrap-1.2.3.tgz", + "integrity": "sha512-Hz/mrNwitNRh/HUAtM/VT/5VH+ygD6DV7mYKZAtHOrbs8U7lvPS6xf7EJKMF0uW1KJCl0H701g3ZGus+muE5vQ==", + "dev": true, + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/wordwrap": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/wordwrap/-/wordwrap-1.0.0.tgz", + "integrity": "sha1-J1hIEIkUVqQXHI0CJkQa3pDLyus=", + "dev": true + }, + "node_modules/workerpool": { + "version": "6.1.5", + "resolved": "https://registry.npmjs.org/workerpool/-/workerpool-6.1.5.tgz", + "integrity": "sha512-XdKkCK0Zqc6w3iTxLckiuJ81tiD/o5rBE/m+nXpRCB+/Sq4DqkfXZ/x0jW02DG1tGsfUGXbTJyZDP+eu67haSw==", + "dev": true + }, + "node_modules/wrap-ansi": { + "version": "7.0.0", + "resolved": "https://registry.npmjs.org/wrap-ansi/-/wrap-ansi-7.0.0.tgz", + "integrity": "sha512-YVGIj2kamLSTxw6NsZjoBxfSwsn0ycdesmc4p+Q21c5zPuZ1pl+NfxVdxPtdHvmNVOQ6XSYG4AUtyt/Fi7D16Q==", + "dev": true, + "dependencies": { + "ansi-styles": "^4.0.0", + "string-width": "^4.1.0", + "strip-ansi": "^6.0.0" + }, + "engines": { + "node": ">=10" + }, + "funding": { + "url": "https://github.com/chalk/wrap-ansi?sponsor=1" + } + }, + "node_modules/wrappy": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/wrappy/-/wrappy-1.0.2.tgz", + "integrity": "sha1-tSQ9jz7BqjXxNkYFvA0QNuMKtp8=", + "dev": true + }, + "node_modules/write-file-atomic": { + "version": "3.0.3", + "resolved": "https://registry.npmjs.org/write-file-atomic/-/write-file-atomic-3.0.3.tgz", + "integrity": "sha512-AvHcyZ5JnSfq3ioSyjrBkH9yW4m7Ayk8/9My/DD9onKeu/94fwrMocemO2QAJFAlnnDN+ZDS+ZjAR5ua1/PV/Q==", + "dev": true, + "dependencies": { + "imurmurhash": "^0.1.4", + "is-typedarray": "^1.0.0", + "signal-exit": "^3.0.2", + "typedarray-to-buffer": "^3.1.5" + } + }, + "node_modules/xdg-basedir": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/xdg-basedir/-/xdg-basedir-4.0.0.tgz", + "integrity": "sha512-PSNhEJDejZYV7h50BohL09Er9VaIefr2LMAf3OEmpCkjOi34eYyQYAXUTjEQtZJTKcF0E2UKTh+osDLsgNim9Q==", + "dev": true, + "engines": { + "node": ">=8" + } + }, + "node_modules/y18n": { + "version": "5.0.8", + "resolved": "https://registry.npmjs.org/y18n/-/y18n-5.0.8.tgz", + "integrity": "sha512-0pfFzegeDWJHJIAmTLRP2DwHjdF5s7jo9tuztdQxAhINCdvS+3nGINqPd00AphqJR/0LhANUS6/+7SCb98YOfA==", + "dev": true, + "engines": { + "node": ">=10" + } + }, + "node_modules/yallist": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/yallist/-/yallist-4.0.0.tgz", + "integrity": "sha512-3wdGidZyq5PB084XLES5TpOSRA3wjXAlIWMhum2kRcv/41Sn2emQ0dycQW4uZXLejwKvg6EsvbdlVL+FYEct7A==" + }, + "node_modules/yaml": { + "version": "1.10.2", + "resolved": "https://registry.npmjs.org/yaml/-/yaml-1.10.2.tgz", + "integrity": "sha512-r3vXyErRCYJ7wg28yvBY5VSoAF8ZvlcW9/BwUzEtUsjvX/DKs24dIkuwjtuprwJJHsbyUbLApepYTR1BN4uHrg==", + "dev": true, + "engines": { + "node": ">= 6" + } + }, + "node_modules/yargs": { + "version": "17.2.1", + "resolved": "https://registry.npmjs.org/yargs/-/yargs-17.2.1.tgz", + "integrity": "sha512-XfR8du6ua4K6uLGm5S6fA+FIJom/MdJcFNVY8geLlp2v8GYbOXD4EB1tPNZsRn4vBzKGMgb5DRZMeWuFc2GO8Q==", + "dev": true, + "dependencies": { + "cliui": "^7.0.2", + "escalade": "^3.1.1", + "get-caller-file": "^2.0.5", + "require-directory": "^2.1.1", + "string-width": "^4.2.0", + "y18n": "^5.0.5", + "yargs-parser": "^20.2.2" + }, + "engines": { + "node": ">=12" + } + }, + "node_modules/yargs-parser": { + "version": "20.2.4", + "resolved": "https://registry.npmjs.org/yargs-parser/-/yargs-parser-20.2.4.tgz", + "integrity": "sha512-WOkpgNhPTlE73h4VFAFsOnomJVaovO8VqLDzy5saChRBFQFBoMYirowyW+Q9HB4HFF4Z7VZTiG3iSzJJA29yRA==", + "dev": true, + "engines": { + "node": ">=10" + } + }, + "node_modules/yargs-unparser": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/yargs-unparser/-/yargs-unparser-2.0.0.tgz", + "integrity": "sha512-7pRTIA9Qc1caZ0bZ6RYRGbHJthJWuakf+WmHK0rVeLkNrrGhfoabBNdue6kdINI6r4if7ocq9aD/n7xwKOdzOA==", + "dev": true, + "dependencies": { + "camelcase": "^6.0.0", + "decamelize": "^4.0.0", + "flat": "^5.0.2", + "is-plain-obj": "^2.1.0" + }, + "engines": { + "node": ">=10" + } + }, + "node_modules/yargs-unparser/node_modules/camelcase": { + "version": "6.2.0", + "resolved": "https://registry.npmjs.org/camelcase/-/camelcase-6.2.0.tgz", + "integrity": "sha512-c7wVvbw3f37nuobQNtgsgG9POC9qMbNuMQmTCqZv23b6MIz0fcYpBiOlv9gEN/hdLdnZTDQhg6e9Dq5M1vKvfg==", + "dev": true, + "engines": { + "node": ">=10" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/yargs-unparser/node_modules/decamelize": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/decamelize/-/decamelize-4.0.0.tgz", + "integrity": "sha512-9iE1PgSik9HeIIw2JO94IidnE3eBoQrFJ3w7sFuzSX4DpmZ3v5sZpUiV5Swcf6mQEF+Y0ru8Neo+p+nyh2J+hQ==", + "dev": true, + "engines": { + "node": ">=10" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/yn": { + "version": "3.1.1", + "resolved": "https://registry.npmjs.org/yn/-/yn-3.1.1.tgz", + "integrity": "sha512-Ux4ygGWsu2c7isFWe8Yu1YluJmqVhxqK2cLXNQA5AcC3QfbGNpM7fu0Y8b/z16pXLnFxZYvWhd3fhBY9DLmC6Q==", + "dev": true, + "engines": { + "node": ">=6" + } + }, + "node_modules/yocto-queue": { + "version": "0.1.0", + "resolved": "https://registry.npmjs.org/yocto-queue/-/yocto-queue-0.1.0.tgz", + "integrity": "sha512-rVksvsnNCdJ/ohGc6xgPwyN8eheCxsiLM8mxuE/t/mOVqJewPuO1miLpTHQiRgTKCLexL4MeAFVagts7HmNZ2Q==", + "dev": true, + "engines": { + "node": ">=10" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "packages/all-in-one": { + "name": "redis", + "version": "4.0.0-rc.3", + "license": "MIT", + "dependencies": { + "@redis/client": "^1.0.0-rc", + "@redis/json": "^1.0.0-rc", + "@redis/search": "^1.0.0-rc" + }, + "devDependencies": { + "release-it": "^14.11.6", + "typescript": "^4.4.4" + } + }, + "packages/client": { + "name": "@redis/client", + "version": "1.0.0-rc", + "license": "MIT", + "dependencies": { + "cluster-key-slot": "1.1.0", + "generic-pool": "3.8.2", + "redis-parser": "3.0.0", + "yallist": "4.0.0" + }, + "devDependencies": { + "@istanbuljs/nyc-config-typescript": "^1.0.1", + "@redis/test-utils": "*", + "@types/node": "^16.11.6", + "@types/sinon": "^10.0.6", + "@types/yallist": "^4.0.1", + "@typescript-eslint/eslint-plugin": "^5.2.0", + "@typescript-eslint/parser": "^5.2.0", + "eslint": "^8.1.0", + "nyc": "^15.1.0", + "release-it": "^14.11.6", + "sinon": "^11.1.2", + "source-map-support": "^0.5.20", + "ts-node": "^10.4.0", + "typedoc": "^0.22.7", + "typedoc-github-wiki-theme": "^0.6.0", + "typedoc-plugin-markdown": "^3.11.3", + "typescript": "^4.4.4" + }, + "engines": { + "node": ">=12" + } + }, + "packages/json": { + "name": "@redis/json", + "version": "1.0.0-rc.0", + "license": "MIT", + "devDependencies": { + "@istanbuljs/nyc-config-typescript": "^1.0.1", + "@redis/test-utils": "*", + "@types/node": "^16.11.6", + "nyc": "^15.1.0", + "release-it": "^14.11.6", + "source-map-support": "^0.5.20", + "ts-node": "^10.4.0", + "typescript": "^4.4.4" + }, + "peerDependencies": { + "@redis/client": "^1.0.0-rc" + } + }, + "packages/search": { + "name": "@redis/search", + "version": "1.0.0-rc.0", + "license": "MIT", + "devDependencies": { + "@istanbuljs/nyc-config-typescript": "^1.0.1", + "@redis/test-utils": "*", + "@types/node": "^16.11.6", + "nyc": "^15.1.0", + "release-it": "^14.11.6", + "source-map-support": "^0.5.20", + "ts-node": "^10.4.0", + "typescript": "^4.4.4" + }, + "peerDependencies": { + "@redis/client": "^1.0.0-rc" + } + }, + "packages/test-utils": { + "name": "@redis/test-utils", + "devDependencies": { + "@istanbuljs/nyc-config-typescript": "^1.0.1", + "@tsconfig/node12": "^1.0.9", + "@types/mocha": "^9.0.0", + "@types/node": "^16.11.6", + "@types/yargs": "^17.0.5", + "mocha": "^9.1.3", + "nyc": "^15.1.0", + "release-it": "^14.11.6", + "source-map-support": "^0.5.20", + "ts-node": "^10.4.0", + "typescript": "^4.4.4", + "yargs": "^17.2.1" + }, + "peerDependencies": { + "@redis/client": "^1.0.0-rc" + } + }, + "packages/time-series": { + "name": "@redis/time-series", + "version": "1.0.0", + "license": "ISC", + "devDependencies": { + "@istanbuljs/nyc-config-typescript": "^1.0.1", + "@redis/test-utils": "*", + "@types/node": "^16.11.6", + "nyc": "^15.1.0", + "release-it": "^14.11.6", + "source-map-support": "^0.5.20", + "ts-node": "^10.4.0", + "typescript": "^4.4.4" + } + } + }, + "dependencies": { + "@babel/code-frame": { + "version": "7.16.0", + "resolved": "https://registry.npmjs.org/@babel/code-frame/-/code-frame-7.16.0.tgz", + "integrity": "sha512-IF4EOMEV+bfYwOmNxGzSnjR2EmQod7f1UXOpZM3l4i4o4QNwzjtJAu/HxdjHq0aYBvdqMuQEY1eg0nqW9ZPORA==", + "dev": true, + "requires": { + "@babel/highlight": "^7.16.0" + } + }, + "@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==", + "dev": true + }, + "@babel/core": { + "version": "7.16.0", + "resolved": "https://registry.npmjs.org/@babel/core/-/core-7.16.0.tgz", + "integrity": "sha512-mYZEvshBRHGsIAiyH5PzCFTCfbWfoYbO/jcSdXQSUQu1/pW0xDZAUP7KEc32heqWTAfAHhV9j1vH8Sav7l+JNQ==", + "dev": true, + "requires": { + "@babel/code-frame": "^7.16.0", + "@babel/generator": "^7.16.0", + "@babel/helper-compilation-targets": "^7.16.0", + "@babel/helper-module-transforms": "^7.16.0", + "@babel/helpers": "^7.16.0", + "@babel/parser": "^7.16.0", + "@babel/template": "^7.16.0", + "@babel/traverse": "^7.16.0", + "@babel/types": "^7.16.0", + "convert-source-map": "^1.7.0", + "debug": "^4.1.0", + "gensync": "^1.0.0-beta.2", + "json5": "^2.1.2", + "semver": "^6.3.0", + "source-map": "^0.5.0" + }, + "dependencies": { + "semver": { + "version": "6.3.0", + "resolved": "https://registry.npmjs.org/semver/-/semver-6.3.0.tgz", + "integrity": "sha512-b39TBaTSfV6yBrapU89p5fKekE2m/NwnDocOVruQFS1/veMgdzuPcnOM34M6CwxW8jH/lxEa5rBoDeUwu5HHTw==", + "dev": true + } + } + }, + "@babel/generator": { + "version": "7.16.0", + "resolved": "https://registry.npmjs.org/@babel/generator/-/generator-7.16.0.tgz", + "integrity": "sha512-RR8hUCfRQn9j9RPKEVXo9LiwoxLPYn6hNZlvUOR8tSnaxlD0p0+la00ZP9/SnRt6HchKr+X0fO2r8vrETiJGew==", + "dev": true, + "requires": { + "@babel/types": "^7.16.0", + "jsesc": "^2.5.1", + "source-map": "^0.5.0" + } + }, + "@babel/helper-compilation-targets": { + "version": "7.16.3", + "resolved": "https://registry.npmjs.org/@babel/helper-compilation-targets/-/helper-compilation-targets-7.16.3.tgz", + "integrity": "sha512-vKsoSQAyBmxS35JUOOt+07cLc6Nk/2ljLIHwmq2/NM6hdioUaqEXq/S+nXvbvXbZkNDlWOymPanJGOc4CBjSJA==", + "dev": true, + "requires": { + "@babel/compat-data": "^7.16.0", + "@babel/helper-validator-option": "^7.14.5", + "browserslist": "^4.17.5", + "semver": "^6.3.0" + }, + "dependencies": { + "semver": { + "version": "6.3.0", + "resolved": "https://registry.npmjs.org/semver/-/semver-6.3.0.tgz", + "integrity": "sha512-b39TBaTSfV6yBrapU89p5fKekE2m/NwnDocOVruQFS1/veMgdzuPcnOM34M6CwxW8jH/lxEa5rBoDeUwu5HHTw==", + "dev": true + } + } + }, + "@babel/helper-function-name": { + "version": "7.16.0", + "resolved": "https://registry.npmjs.org/@babel/helper-function-name/-/helper-function-name-7.16.0.tgz", + "integrity": "sha512-BZh4mEk1xi2h4HFjWUXRQX5AEx4rvaZxHgax9gcjdLWdkjsY7MKt5p0otjsg5noXw+pB+clMCjw+aEVYADMjog==", + "dev": true, + "requires": { + "@babel/helper-get-function-arity": "^7.16.0", + "@babel/template": "^7.16.0", + "@babel/types": "^7.16.0" + } + }, + "@babel/helper-get-function-arity": { + "version": "7.16.0", + "resolved": "https://registry.npmjs.org/@babel/helper-get-function-arity/-/helper-get-function-arity-7.16.0.tgz", + "integrity": "sha512-ASCquNcywC1NkYh/z7Cgp3w31YW8aojjYIlNg4VeJiHkqyP4AzIvr4qx7pYDb4/s8YcsZWqqOSxgkvjUz1kpDQ==", + "dev": true, + "requires": { + "@babel/types": "^7.16.0" + } + }, + "@babel/helper-hoist-variables": { + "version": "7.16.0", + "resolved": "https://registry.npmjs.org/@babel/helper-hoist-variables/-/helper-hoist-variables-7.16.0.tgz", + "integrity": "sha512-1AZlpazjUR0EQZQv3sgRNfM9mEVWPK3M6vlalczA+EECcPz3XPh6VplbErL5UoMpChhSck5wAJHthlj1bYpcmg==", + "dev": true, + "requires": { + "@babel/types": "^7.16.0" + } + }, + "@babel/helper-member-expression-to-functions": { + "version": "7.16.0", + "resolved": "https://registry.npmjs.org/@babel/helper-member-expression-to-functions/-/helper-member-expression-to-functions-7.16.0.tgz", + "integrity": "sha512-bsjlBFPuWT6IWhl28EdrQ+gTvSvj5tqVP5Xeftp07SEuz5pLnsXZuDkDD3Rfcxy0IsHmbZ+7B2/9SHzxO0T+sQ==", + "dev": true, + "requires": { + "@babel/types": "^7.16.0" + } + }, + "@babel/helper-module-imports": { + "version": "7.16.0", + "resolved": "https://registry.npmjs.org/@babel/helper-module-imports/-/helper-module-imports-7.16.0.tgz", + "integrity": "sha512-kkH7sWzKPq0xt3H1n+ghb4xEMP8k0U7XV3kkB+ZGy69kDk2ySFW1qPi06sjKzFY3t1j6XbJSqr4mF9L7CYVyhg==", + "dev": true, + "requires": { + "@babel/types": "^7.16.0" + } + }, + "@babel/helper-module-transforms": { + "version": "7.16.0", + "resolved": "https://registry.npmjs.org/@babel/helper-module-transforms/-/helper-module-transforms-7.16.0.tgz", + "integrity": "sha512-My4cr9ATcaBbmaEa8M0dZNA74cfI6gitvUAskgDtAFmAqyFKDSHQo5YstxPbN+lzHl2D9l/YOEFqb2mtUh4gfA==", + "dev": true, + "requires": { + "@babel/helper-module-imports": "^7.16.0", + "@babel/helper-replace-supers": "^7.16.0", + "@babel/helper-simple-access": "^7.16.0", + "@babel/helper-split-export-declaration": "^7.16.0", + "@babel/helper-validator-identifier": "^7.15.7", + "@babel/template": "^7.16.0", + "@babel/traverse": "^7.16.0", + "@babel/types": "^7.16.0" + } + }, + "@babel/helper-optimise-call-expression": { + "version": "7.16.0", + "resolved": "https://registry.npmjs.org/@babel/helper-optimise-call-expression/-/helper-optimise-call-expression-7.16.0.tgz", + "integrity": "sha512-SuI467Gi2V8fkofm2JPnZzB/SUuXoJA5zXe/xzyPP2M04686RzFKFHPK6HDVN6JvWBIEW8tt9hPR7fXdn2Lgpw==", + "dev": true, + "requires": { + "@babel/types": "^7.16.0" + } + }, + "@babel/helper-replace-supers": { + "version": "7.16.0", + "resolved": "https://registry.npmjs.org/@babel/helper-replace-supers/-/helper-replace-supers-7.16.0.tgz", + "integrity": "sha512-TQxuQfSCdoha7cpRNJvfaYxxxzmbxXw/+6cS7V02eeDYyhxderSoMVALvwupA54/pZcOTtVeJ0xccp1nGWladA==", + "dev": true, + "requires": { + "@babel/helper-member-expression-to-functions": "^7.16.0", + "@babel/helper-optimise-call-expression": "^7.16.0", + "@babel/traverse": "^7.16.0", + "@babel/types": "^7.16.0" + } + }, + "@babel/helper-simple-access": { + "version": "7.16.0", + "resolved": "https://registry.npmjs.org/@babel/helper-simple-access/-/helper-simple-access-7.16.0.tgz", + "integrity": "sha512-o1rjBT/gppAqKsYfUdfHq5Rk03lMQrkPHG1OWzHWpLgVXRH4HnMM9Et9CVdIqwkCQlobnGHEJMsgWP/jE1zUiw==", + "dev": true, + "requires": { + "@babel/types": "^7.16.0" + } + }, + "@babel/helper-split-export-declaration": { + "version": "7.16.0", + "resolved": "https://registry.npmjs.org/@babel/helper-split-export-declaration/-/helper-split-export-declaration-7.16.0.tgz", + "integrity": "sha512-0YMMRpuDFNGTHNRiiqJX19GjNXA4H0E8jZ2ibccfSxaCogbm3am5WN/2nQNj0YnQwGWM1J06GOcQ2qnh3+0paw==", + "dev": true, + "requires": { + "@babel/types": "^7.16.0" + } + }, + "@babel/helper-validator-identifier": { + "version": "7.15.7", + "resolved": "https://registry.npmjs.org/@babel/helper-validator-identifier/-/helper-validator-identifier-7.15.7.tgz", + "integrity": "sha512-K4JvCtQqad9OY2+yTU8w+E82ywk/fe+ELNlt1G8z3bVGlZfn/hOcQQsUhGhW/N+tb3fxK800wLtKOE/aM0m72w==", + "dev": true + }, + "@babel/helper-validator-option": { + "version": "7.14.5", + "resolved": "https://registry.npmjs.org/@babel/helper-validator-option/-/helper-validator-option-7.14.5.tgz", + "integrity": "sha512-OX8D5eeX4XwcroVW45NMvoYaIuFI+GQpA2a8Gi+X/U/cDUIRsV37qQfF905F0htTRCREQIB4KqPeaveRJUl3Ow==", + "dev": true + }, + "@babel/helpers": { + "version": "7.16.3", + "resolved": "https://registry.npmjs.org/@babel/helpers/-/helpers-7.16.3.tgz", + "integrity": "sha512-Xn8IhDlBPhvYTvgewPKawhADichOsbkZuzN7qz2BusOM0brChsyXMDJvldWaYMMUNiCQdQzNEioXTp3sC8Nt8w==", + "dev": true, + "requires": { + "@babel/template": "^7.16.0", + "@babel/traverse": "^7.16.3", + "@babel/types": "^7.16.0" + } + }, + "@babel/highlight": { + "version": "7.16.0", + "resolved": "https://registry.npmjs.org/@babel/highlight/-/highlight-7.16.0.tgz", + "integrity": "sha512-t8MH41kUQylBtu2+4IQA3atqevA2lRgqA2wyVB/YiWmsDSuylZZuXOUy9ric30hfzauEFfdsuk/eXTRrGrfd0g==", + "dev": true, + "requires": { + "@babel/helper-validator-identifier": "^7.15.7", + "chalk": "^2.0.0", + "js-tokens": "^4.0.0" + }, + "dependencies": { + "ansi-styles": { + "version": "3.2.1", + "resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-3.2.1.tgz", + "integrity": "sha512-VT0ZI6kZRdTh8YyJw3SMbYm/u+NqfsAxEpWO0Pf9sq8/e94WxxOpPKx9FR1FlyCtOVDNOQ+8ntlqFxiRc+r5qA==", + "dev": true, + "requires": { + "color-convert": "^1.9.0" + } + }, + "chalk": { + "version": "2.4.2", + "resolved": "https://registry.npmjs.org/chalk/-/chalk-2.4.2.tgz", + "integrity": "sha512-Mti+f9lpJNcwF4tWV8/OrTTtF1gZi+f8FqlyAdouralcFWFQWF2+NgCHShjkCb+IFBLq9buZwE1xckQU4peSuQ==", + "dev": true, + "requires": { + "ansi-styles": "^3.2.1", + "escape-string-regexp": "^1.0.5", + "supports-color": "^5.3.0" + } + }, + "color-convert": { + "version": "1.9.3", + "resolved": "https://registry.npmjs.org/color-convert/-/color-convert-1.9.3.tgz", + "integrity": "sha512-QfAUtd+vFdAtFQcC8CCyYt1fYWxSqAiK2cSD6zDB8N3cpsEBAvRxp9zOGg6G/SHHJYAT88/az/IuDGALsNVbGg==", + "dev": true, + "requires": { + "color-name": "1.1.3" + } + }, + "color-name": { + "version": "1.1.3", + "resolved": "https://registry.npmjs.org/color-name/-/color-name-1.1.3.tgz", + "integrity": "sha1-p9BVi9icQveV3UIyj3QIMcpTvCU=", + "dev": true + }, + "escape-string-regexp": { + "version": "1.0.5", + "resolved": "https://registry.npmjs.org/escape-string-regexp/-/escape-string-regexp-1.0.5.tgz", + "integrity": "sha1-G2HAViGQqN/2rjuyzwIAyhMLhtQ=", + "dev": true + }, + "has-flag": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/has-flag/-/has-flag-3.0.0.tgz", + "integrity": "sha1-tdRU3CGZriJWmfNGfloH87lVuv0=", + "dev": true + }, + "supports-color": { + "version": "5.5.0", + "resolved": "https://registry.npmjs.org/supports-color/-/supports-color-5.5.0.tgz", + "integrity": "sha512-QjVjwdXIt408MIiAqCX4oUKsgU2EqAGzs2Ppkm4aQYbjm+ZEWEcW4SfFNTr4uMNZma0ey4f5lgLrkB0aX0QMow==", + "dev": true, + "requires": { + "has-flag": "^3.0.0" + } + } + } + }, + "@babel/parser": { + "version": "7.16.3", + "resolved": "https://registry.npmjs.org/@babel/parser/-/parser-7.16.3.tgz", + "integrity": "sha512-dcNwU1O4sx57ClvLBVFbEgx0UZWfd0JQX5X6fxFRCLHelFBGXFfSz6Y0FAq2PEwUqlqLkdVjVr4VASEOuUnLJw==", + "dev": true + }, + "@babel/template": { + "version": "7.16.0", + "resolved": "https://registry.npmjs.org/@babel/template/-/template-7.16.0.tgz", + "integrity": "sha512-MnZdpFD/ZdYhXwiunMqqgyZyucaYsbL0IrjoGjaVhGilz+x8YB++kRfygSOIj1yOtWKPlx7NBp+9I1RQSgsd5A==", + "dev": true, + "requires": { + "@babel/code-frame": "^7.16.0", + "@babel/parser": "^7.16.0", + "@babel/types": "^7.16.0" + } + }, + "@babel/traverse": { + "version": "7.16.3", + "resolved": "https://registry.npmjs.org/@babel/traverse/-/traverse-7.16.3.tgz", + "integrity": "sha512-eolumr1vVMjqevCpwVO99yN/LoGL0EyHiLO5I043aYQvwOJ9eR5UsZSClHVCzfhBduMAsSzgA/6AyqPjNayJag==", + "dev": true, + "requires": { + "@babel/code-frame": "^7.16.0", + "@babel/generator": "^7.16.0", + "@babel/helper-function-name": "^7.16.0", + "@babel/helper-hoist-variables": "^7.16.0", + "@babel/helper-split-export-declaration": "^7.16.0", + "@babel/parser": "^7.16.3", + "@babel/types": "^7.16.0", + "debug": "^4.1.0", + "globals": "^11.1.0" + }, + "dependencies": { + "globals": { + "version": "11.12.0", + "resolved": "https://registry.npmjs.org/globals/-/globals-11.12.0.tgz", + "integrity": "sha512-WOBp/EEGUiIsJSp7wcv/y6MO+lV9UoncWqxuFfm8eBwzWNgyfBd6Gz+IeKQ9jCmyhoH99g15M3T+QaVHFjizVA==", + "dev": true + } + } + }, + "@babel/types": { + "version": "7.16.0", + "resolved": "https://registry.npmjs.org/@babel/types/-/types-7.16.0.tgz", + "integrity": "sha512-PJgg/k3SdLsGb3hhisFvtLOw5ts113klrpLuIPtCJIU+BB24fqq6lf8RWqKJEjzqXR9AEH1rIb5XTqwBHB+kQg==", + "dev": true, + "requires": { + "@babel/helper-validator-identifier": "^7.15.7", + "to-fast-properties": "^2.0.0" + } + }, + "@cspotcode/source-map-consumer": { + "version": "0.8.0", + "resolved": "https://registry.npmjs.org/@cspotcode/source-map-consumer/-/source-map-consumer-0.8.0.tgz", + "integrity": "sha512-41qniHzTU8yAGbCp04ohlmSrZf8bkf/iJsl3V0dRGsQN/5GFfx+LbCSsCpp2gqrqjTVg/K6O8ycoV35JIwAzAg==", + "dev": true + }, + "@cspotcode/source-map-support": { + "version": "0.7.0", + "resolved": "https://registry.npmjs.org/@cspotcode/source-map-support/-/source-map-support-0.7.0.tgz", + "integrity": "sha512-X4xqRHqN8ACt2aHVe51OxeA2HjbcL4MqFqXkrmQszJ1NOUuUu5u6Vqx/0lZSVNku7velL5FC/s5uEAj1lsBMhA==", + "dev": true, + "requires": { + "@cspotcode/source-map-consumer": "0.8.0" + } + }, + "@eslint/eslintrc": { + "version": "1.0.4", + "resolved": "https://registry.npmjs.org/@eslint/eslintrc/-/eslintrc-1.0.4.tgz", + "integrity": "sha512-h8Vx6MdxwWI2WM8/zREHMoqdgLNXEL4QX3MWSVMdyNJGvXVOs+6lp+m2hc3FnuMHDc4poxFNI20vCk0OmI4G0Q==", + "dev": true, + "requires": { + "ajv": "^6.12.4", + "debug": "^4.3.2", + "espree": "^9.0.0", + "globals": "^13.9.0", + "ignore": "^4.0.6", + "import-fresh": "^3.2.1", + "js-yaml": "^4.1.0", + "minimatch": "^3.0.4", + "strip-json-comments": "^3.1.1" + }, + "dependencies": { + "ignore": { + "version": "4.0.6", + "resolved": "https://registry.npmjs.org/ignore/-/ignore-4.0.6.tgz", + "integrity": "sha512-cyFDKrqc/YdcWFniJhzI42+AzS+gNwmUzOSFcRCQYwySuBBBy/KjuxWLZ/FHEH6Moq1NizMOBWyTcv8O4OZIMg==", + "dev": true + } + } + }, + "@humanwhocodes/config-array": { + "version": "0.6.0", + "resolved": "https://registry.npmjs.org/@humanwhocodes/config-array/-/config-array-0.6.0.tgz", + "integrity": "sha512-JQlEKbcgEUjBFhLIF4iqM7u/9lwgHRBcpHrmUNCALK0Q3amXN6lxdoXLnF0sm11E9VqTmBALR87IlUg1bZ8A9A==", + "dev": true, + "requires": { + "@humanwhocodes/object-schema": "^1.2.0", + "debug": "^4.1.1", + "minimatch": "^3.0.4" + } + }, + "@humanwhocodes/object-schema": { + "version": "1.2.1", + "resolved": "https://registry.npmjs.org/@humanwhocodes/object-schema/-/object-schema-1.2.1.tgz", + "integrity": "sha512-ZnQMnLV4e7hDlUvw8H+U8ASL02SS2Gn6+9Ac3wGGLIe7+je2AeAOxPY+izIPJDfFDb7eDjev0Us8MO1iFRN8hA==", + "dev": true + }, + "@iarna/toml": { + "version": "2.2.5", + "resolved": "https://registry.npmjs.org/@iarna/toml/-/toml-2.2.5.tgz", + "integrity": "sha512-trnsAYxU3xnS1gPHPyU961coFyLkh4gAD/0zQ5mymY4yOZ+CYvsPqUbOFSw0aDM4y0tV7tiFxL/1XfXPNC6IPg==", + "dev": true + }, + "@istanbuljs/load-nyc-config": { + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/@istanbuljs/load-nyc-config/-/load-nyc-config-1.1.0.tgz", + "integrity": "sha512-VjeHSlIzpv/NyD3N0YuHfXOPDIixcA1q2ZV98wsMqcYlPmv2n3Yb2lYP9XMElnaFVXg5A7YLTeLu6V84uQDjmQ==", + "dev": true, + "requires": { + "camelcase": "^5.3.1", + "find-up": "^4.1.0", + "get-package-type": "^0.1.0", + "js-yaml": "^3.13.1", + "resolve-from": "^5.0.0" + }, + "dependencies": { + "argparse": { + "version": "1.0.10", + "resolved": "https://registry.npmjs.org/argparse/-/argparse-1.0.10.tgz", + "integrity": "sha512-o5Roy6tNG4SL/FOkCAN6RzjiakZS25RLYFrcMttJqbdd8BWrnA+fGz57iN5Pb06pvBGvl5gQ0B48dJlslXvoTg==", + "dev": true, + "requires": { + "sprintf-js": "~1.0.2" + } + }, + "find-up": { + "version": "4.1.0", + "resolved": "https://registry.npmjs.org/find-up/-/find-up-4.1.0.tgz", + "integrity": "sha512-PpOwAdQ/YlXQ2vj8a3h8IipDuYRi3wceVQQGYWxNINccq40Anw7BlsEXCMbt1Zt+OLA6Fq9suIpIWD0OsnISlw==", + "dev": true, + "requires": { + "locate-path": "^5.0.0", + "path-exists": "^4.0.0" + } + }, + "js-yaml": { + "version": "3.14.1", + "resolved": "https://registry.npmjs.org/js-yaml/-/js-yaml-3.14.1.tgz", + "integrity": "sha512-okMH7OXXJ7YrN9Ok3/SXrnu4iX9yOk+25nqX4imS2npuvTYDmo/QEZoqwZkYaIDk3jVvBOTOIEgEhaLOynBS9g==", + "dev": true, + "requires": { + "argparse": "^1.0.7", + "esprima": "^4.0.0" + } + }, + "locate-path": { + "version": "5.0.0", + "resolved": "https://registry.npmjs.org/locate-path/-/locate-path-5.0.0.tgz", + "integrity": "sha512-t7hw9pI+WvuwNJXwk5zVHpyhIqzg2qTlklJOf0mVxGSbe3Fp2VieZcduNYjaLDoy6p9uGpQEGWG87WpMKlNq8g==", + "dev": true, + "requires": { + "p-locate": "^4.1.0" + } + }, + "p-limit": { + "version": "2.3.0", + "resolved": "https://registry.npmjs.org/p-limit/-/p-limit-2.3.0.tgz", + "integrity": "sha512-//88mFWSJx8lxCzwdAABTJL2MyWB12+eIY7MDL2SqLmAkeKU9qxRvWuSyTjm3FUmpBEMuFfckAIqEaVGUDxb6w==", + "dev": true, + "requires": { + "p-try": "^2.0.0" + } + }, + "p-locate": { + "version": "4.1.0", + "resolved": "https://registry.npmjs.org/p-locate/-/p-locate-4.1.0.tgz", + "integrity": "sha512-R79ZZ/0wAxKGu3oYMlz8jy/kbhsNrS7SKZ7PxEHBgJ5+F2mtFW2fK2cOtBh1cHYkQsbzFV7I+EoRKe6Yt0oK7A==", + "dev": true, + "requires": { + "p-limit": "^2.2.0" + } + }, + "resolve-from": { + "version": "5.0.0", + "resolved": "https://registry.npmjs.org/resolve-from/-/resolve-from-5.0.0.tgz", + "integrity": "sha512-qYg9KP24dD5qka9J47d0aVky0N+b4fTU89LN9iDnjB5waksiC49rvMB0PrUJQGoTmH50XPiqOvAjDfaijGxYZw==", + "dev": true + } + } + }, + "@istanbuljs/nyc-config-typescript": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/@istanbuljs/nyc-config-typescript/-/nyc-config-typescript-1.0.1.tgz", + "integrity": "sha512-/gz6LgVpky205LuoOfwEZmnUtaSmdk0QIMcNFj9OvxhiMhPpKftMgZmGN7jNj7jR+lr8IB1Yks3QSSSNSxfoaQ==", + "dev": true, + "requires": { + "@istanbuljs/schema": "^0.1.2" + } + }, + "@istanbuljs/schema": { + "version": "0.1.3", + "resolved": "https://registry.npmjs.org/@istanbuljs/schema/-/schema-0.1.3.tgz", + "integrity": "sha512-ZXRY4jNvVgSVQ8DL3LTcakaAtXwTVUxE81hslsyD2AtoXW/wVob10HkOJ1X/pAlcI7D+2YoZKg5do8G/w6RYgA==", + "dev": true + }, + "@nodelib/fs.scandir": { + "version": "2.1.5", + "resolved": "https://registry.npmjs.org/@nodelib/fs.scandir/-/fs.scandir-2.1.5.tgz", + "integrity": "sha512-vq24Bq3ym5HEQm2NKCr3yXDwjc7vTsEThRDnkp2DK9p1uqLR+DHurm/NOTo0KG7HYHU7eppKZj3MyqYuMBf62g==", + "dev": true, + "requires": { + "@nodelib/fs.stat": "2.0.5", + "run-parallel": "^1.1.9" + } + }, + "@nodelib/fs.stat": { + "version": "2.0.5", + "resolved": "https://registry.npmjs.org/@nodelib/fs.stat/-/fs.stat-2.0.5.tgz", + "integrity": "sha512-RkhPPp2zrqDAQA/2jNhnztcPAlv64XdhIp7a7454A5ovI7Bukxgt7MX7udwAu3zg1DcpPU0rz3VV1SeaqvY4+A==", + "dev": true + }, + "@nodelib/fs.walk": { + "version": "1.2.8", + "resolved": "https://registry.npmjs.org/@nodelib/fs.walk/-/fs.walk-1.2.8.tgz", + "integrity": "sha512-oGB+UxlgWcgQkgwo8GcEGwemoTFt3FIO9ababBmaGwXIoBKZ+GTy0pP185beGg7Llih/NSHSV2XAs1lnznocSg==", + "dev": true, + "requires": { + "@nodelib/fs.scandir": "2.1.5", + "fastq": "^1.6.0" + } + }, + "@octokit/auth-token": { + "version": "2.5.0", + "resolved": "https://registry.npmjs.org/@octokit/auth-token/-/auth-token-2.5.0.tgz", + "integrity": "sha512-r5FVUJCOLl19AxiuZD2VRZ/ORjp/4IN98Of6YJoJOkY75CIBuYfmiNHGrDwXr+aLGG55igl9QrxX3hbiXlLb+g==", + "dev": true, + "requires": { + "@octokit/types": "^6.0.3" + } + }, + "@octokit/core": { + "version": "3.5.1", + "resolved": "https://registry.npmjs.org/@octokit/core/-/core-3.5.1.tgz", + "integrity": "sha512-omncwpLVxMP+GLpLPgeGJBF6IWJFjXDS5flY5VbppePYX9XehevbDykRH9PdCdvqt9TS5AOTiDide7h0qrkHjw==", + "dev": true, + "requires": { + "@octokit/auth-token": "^2.4.4", + "@octokit/graphql": "^4.5.8", + "@octokit/request": "^5.6.0", + "@octokit/request-error": "^2.0.5", + "@octokit/types": "^6.0.3", + "before-after-hook": "^2.2.0", + "universal-user-agent": "^6.0.0" + } + }, + "@octokit/endpoint": { + "version": "6.0.12", + "resolved": "https://registry.npmjs.org/@octokit/endpoint/-/endpoint-6.0.12.tgz", + "integrity": "sha512-lF3puPwkQWGfkMClXb4k/eUT/nZKQfxinRWJrdZaJO85Dqwo/G0yOC434Jr2ojwafWJMYqFGFa5ms4jJUgujdA==", + "dev": true, + "requires": { + "@octokit/types": "^6.0.3", + "is-plain-object": "^5.0.0", + "universal-user-agent": "^6.0.0" + } + }, + "@octokit/graphql": { + "version": "4.8.0", + "resolved": "https://registry.npmjs.org/@octokit/graphql/-/graphql-4.8.0.tgz", + "integrity": "sha512-0gv+qLSBLKF0z8TKaSKTsS39scVKF9dbMxJpj3U0vC7wjNWFuIpL/z76Qe2fiuCbDRcJSavkXsVtMS6/dtQQsg==", + "dev": true, + "requires": { + "@octokit/request": "^5.6.0", + "@octokit/types": "^6.0.3", + "universal-user-agent": "^6.0.0" + } + }, + "@octokit/openapi-types": { + "version": "11.2.0", + "resolved": "https://registry.npmjs.org/@octokit/openapi-types/-/openapi-types-11.2.0.tgz", + "integrity": "sha512-PBsVO+15KSlGmiI8QAzaqvsNlZlrDlyAJYcrXBCvVUxCp7VnXjkwPoFHgjEJXx3WF9BAwkA6nfCUA7i9sODzKA==", + "dev": true + }, + "@octokit/plugin-paginate-rest": { + "version": "2.17.0", + "resolved": "https://registry.npmjs.org/@octokit/plugin-paginate-rest/-/plugin-paginate-rest-2.17.0.tgz", + "integrity": "sha512-tzMbrbnam2Mt4AhuyCHvpRkS0oZ5MvwwcQPYGtMv4tUa5kkzG58SVB0fcsLulOZQeRnOgdkZWkRUiyBlh0Bkyw==", + "dev": true, + "requires": { + "@octokit/types": "^6.34.0" + } + }, + "@octokit/plugin-request-log": { + "version": "1.0.4", + "resolved": "https://registry.npmjs.org/@octokit/plugin-request-log/-/plugin-request-log-1.0.4.tgz", + "integrity": "sha512-mLUsMkgP7K/cnFEw07kWqXGF5LKrOkD+lhCrKvPHXWDywAwuDUeDwWBpc69XK3pNX0uKiVt8g5z96PJ6z9xCFA==", + "dev": true, + "requires": {} + }, + "@octokit/plugin-rest-endpoint-methods": { + "version": "5.13.0", + "resolved": "https://registry.npmjs.org/@octokit/plugin-rest-endpoint-methods/-/plugin-rest-endpoint-methods-5.13.0.tgz", + "integrity": "sha512-uJjMTkN1KaOIgNtUPMtIXDOjx6dGYysdIFhgA52x4xSadQCz3b/zJexvITDVpANnfKPW/+E0xkOvLntqMYpviA==", + "dev": true, + "requires": { + "@octokit/types": "^6.34.0", + "deprecation": "^2.3.1" + } + }, + "@octokit/request": { + "version": "5.6.2", + "resolved": "https://registry.npmjs.org/@octokit/request/-/request-5.6.2.tgz", + "integrity": "sha512-je66CvSEVf0jCpRISxkUcCa0UkxmFs6eGDRSbfJtAVwbLH5ceqF+YEyC8lj8ystKyZTy8adWr0qmkY52EfOeLA==", + "dev": true, + "requires": { + "@octokit/endpoint": "^6.0.1", + "@octokit/request-error": "^2.1.0", + "@octokit/types": "^6.16.1", + "is-plain-object": "^5.0.0", + "node-fetch": "^2.6.1", + "universal-user-agent": "^6.0.0" + } + }, + "@octokit/request-error": { + "version": "2.1.0", + "resolved": "https://registry.npmjs.org/@octokit/request-error/-/request-error-2.1.0.tgz", + "integrity": "sha512-1VIvgXxs9WHSjicsRwq8PlR2LR2x6DwsJAaFgzdi0JfJoGSO8mYI/cHJQ+9FbN21aa+DrgNLnwObmyeSC8Rmpg==", + "dev": true, + "requires": { + "@octokit/types": "^6.0.3", + "deprecation": "^2.0.0", + "once": "^1.4.0" + } + }, + "@octokit/rest": { + "version": "18.10.0", + "resolved": "https://registry.npmjs.org/@octokit/rest/-/rest-18.10.0.tgz", + "integrity": "sha512-esHR5OKy38bccL/sajHqZudZCvmv4yjovMJzyXlphaUo7xykmtOdILGJ3aAm0mFHmMLmPFmDMJXf39cAjNJsrw==", + "dev": true, + "requires": { + "@octokit/core": "^3.5.1", + "@octokit/plugin-paginate-rest": "^2.16.0", + "@octokit/plugin-request-log": "^1.0.4", + "@octokit/plugin-rest-endpoint-methods": "^5.9.0" + } + }, + "@octokit/types": { + "version": "6.34.0", + "resolved": "https://registry.npmjs.org/@octokit/types/-/types-6.34.0.tgz", + "integrity": "sha512-s1zLBjWhdEI2zwaoSgyOFoKSl109CUcVBCc7biPJ3aAf6LGLU6szDvi31JPU7bxfla2lqfhjbbg/5DdFNxOwHw==", + "dev": true, + "requires": { + "@octokit/openapi-types": "^11.2.0" + } + }, + "@redis/client": { + "version": "file:packages/client", + "requires": { + "@istanbuljs/nyc-config-typescript": "^1.0.1", + "@redis/test-utils": "*", + "@types/node": "^16.11.6", + "@types/sinon": "^10.0.6", + "@types/yallist": "^4.0.1", + "@typescript-eslint/eslint-plugin": "^5.2.0", + "@typescript-eslint/parser": "^5.2.0", + "cluster-key-slot": "1.1.0", + "eslint": "^8.1.0", + "generic-pool": "3.8.2", + "nyc": "^15.1.0", + "redis-parser": "3.0.0", + "release-it": "^14.11.6", + "sinon": "^11.1.2", + "source-map-support": "^0.5.20", + "ts-node": "^10.4.0", + "typedoc": "^0.22.7", + "typedoc-github-wiki-theme": "^0.6.0", + "typedoc-plugin-markdown": "^3.11.3", + "typescript": "^4.4.4", + "yallist": "4.0.0" + } + }, + "@redis/json": { + "version": "file:packages/json", + "requires": { + "@istanbuljs/nyc-config-typescript": "^1.0.1", + "@redis/test-utils": "*", + "@types/node": "^16.11.6", + "nyc": "^15.1.0", + "release-it": "^14.11.6", + "source-map-support": "^0.5.20", + "ts-node": "^10.4.0", + "typescript": "^4.4.4" + } + }, + "@redis/search": { + "version": "file:packages/search", + "requires": { + "@istanbuljs/nyc-config-typescript": "^1.0.1", + "@redis/test-utils": "*", + "@types/node": "^16.11.6", + "nyc": "^15.1.0", + "release-it": "^14.11.6", + "source-map-support": "^0.5.20", + "ts-node": "^10.4.0", + "typescript": "^4.4.4" + } + }, + "@redis/test-utils": { + "version": "file:packages/test-utils", + "requires": { + "@istanbuljs/nyc-config-typescript": "^1.0.1", + "@tsconfig/node12": "^1.0.9", + "@types/mocha": "^9.0.0", + "@types/node": "^16.11.6", + "@types/yargs": "^17.0.5", + "mocha": "^9.1.3", + "nyc": "^15.1.0", + "release-it": "^14.11.6", + "source-map-support": "^0.5.20", + "ts-node": "^10.4.0", + "typescript": "^4.4.4", + "yargs": "^17.2.1" + } + }, + "@redis/time-series": { + "version": "file:packages/time-series", + "requires": { + "@istanbuljs/nyc-config-typescript": "^1.0.1", + "@redis/test-utils": "*", + "@types/node": "^16.11.6", + "nyc": "^15.1.0", + "release-it": "^14.11.6", + "source-map-support": "^0.5.20", + "ts-node": "^10.4.0", + "typescript": "^4.4.4" + } + }, + "@sindresorhus/is": { + "version": "4.2.0", + "resolved": "https://registry.npmjs.org/@sindresorhus/is/-/is-4.2.0.tgz", + "integrity": "sha512-VkE3KLBmJwcCaVARtQpfuKcKv8gcBmUubrfHGF84dXuuW6jgsRYxPtzcIhPyK9WAPpRt2/xY6zkD9MnRaJzSyw==", + "dev": true + }, + "@sinonjs/commons": { + "version": "1.8.3", + "resolved": "https://registry.npmjs.org/@sinonjs/commons/-/commons-1.8.3.tgz", + "integrity": "sha512-xkNcLAn/wZaX14RPlwizcKicDk9G3F8m2nU3L7Ukm5zBgTwiT0wsoFAHx9Jq56fJA1z/7uKGtCRu16sOUCLIHQ==", + "dev": true, + "requires": { + "type-detect": "4.0.8" + } + }, + "@sinonjs/fake-timers": { + "version": "7.1.2", + "resolved": "https://registry.npmjs.org/@sinonjs/fake-timers/-/fake-timers-7.1.2.tgz", + "integrity": "sha512-iQADsW4LBMISqZ6Ci1dupJL9pprqwcVFTcOsEmQOEhW+KLCVn/Y4Jrvg2k19fIHCp+iFprriYPTdRcQR8NbUPg==", + "dev": true, + "requires": { + "@sinonjs/commons": "^1.7.0" + } + }, + "@sinonjs/samsam": { + "version": "6.0.2", + "resolved": "https://registry.npmjs.org/@sinonjs/samsam/-/samsam-6.0.2.tgz", + "integrity": "sha512-jxPRPp9n93ci7b8hMfJOFDPRLFYadN6FSpeROFTR4UNF4i5b+EK6m4QXPO46BDhFgRy1JuS87zAnFOzCUwMJcQ==", + "dev": true, + "requires": { + "@sinonjs/commons": "^1.6.0", + "lodash.get": "^4.4.2", + "type-detect": "^4.0.8" + } + }, + "@sinonjs/text-encoding": { + "version": "0.7.1", + "resolved": "https://registry.npmjs.org/@sinonjs/text-encoding/-/text-encoding-0.7.1.tgz", + "integrity": "sha512-+iTbntw2IZPb/anVDbypzfQa+ay64MW0Zo8aJ8gZPWMMK6/OubMVb6lUPMagqjOPnmtauXnFCACVl3O7ogjeqQ==", + "dev": true + }, + "@szmarczak/http-timer": { + "version": "4.0.6", + "resolved": "https://registry.npmjs.org/@szmarczak/http-timer/-/http-timer-4.0.6.tgz", + "integrity": "sha512-4BAffykYOgO+5nzBWYwE3W90sBgLJoUPRWWcL8wlyiM8IB8ipJz3UMJ9KXQd1RKQXpKp8Tutn80HZtWsu2u76w==", + "dev": true, + "requires": { + "defer-to-connect": "^2.0.0" + } + }, + "@tsconfig/node10": { + "version": "1.0.8", + "resolved": "https://registry.npmjs.org/@tsconfig/node10/-/node10-1.0.8.tgz", + "integrity": "sha512-6XFfSQmMgq0CFLY1MslA/CPUfhIL919M1rMsa5lP2P097N2Wd1sSX0tx1u4olM16fLNhtHZpRhedZJphNJqmZg==", + "dev": true + }, + "@tsconfig/node12": { + "version": "1.0.9", + "resolved": "https://registry.npmjs.org/@tsconfig/node12/-/node12-1.0.9.tgz", + "integrity": "sha512-/yBMcem+fbvhSREH+s14YJi18sp7J9jpuhYByADT2rypfajMZZN4WQ6zBGgBKp53NKmqI36wFYDb3yaMPurITw==", + "dev": true + }, + "@tsconfig/node14": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/@tsconfig/node14/-/node14-1.0.1.tgz", + "integrity": "sha512-509r2+yARFfHHE7T6Puu2jjkoycftovhXRqW328PDXTVGKihlb1P8Z9mMZH04ebyajfRY7dedfGynlrFHJUQCg==", + "dev": true + }, + "@tsconfig/node16": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/@tsconfig/node16/-/node16-1.0.2.tgz", + "integrity": "sha512-eZxlbI8GZscaGS7kkc/trHTT5xgrjH3/1n2JDwusC9iahPKWMRvRjJSAN5mCXviuTGQ/lHnhvv8Q1YTpnfz9gA==", + "dev": true + }, + "@types/cacheable-request": { + "version": "6.0.2", + "resolved": "https://registry.npmjs.org/@types/cacheable-request/-/cacheable-request-6.0.2.tgz", + "integrity": "sha512-B3xVo+dlKM6nnKTcmm5ZtY/OL8bOAOd2Olee9M1zft65ox50OzjEHW91sDiU9j6cvW8Ejg1/Qkf4xd2kugApUA==", + "dev": true, + "requires": { + "@types/http-cache-semantics": "*", + "@types/keyv": "*", + "@types/node": "*", + "@types/responselike": "*" + } + }, + "@types/http-cache-semantics": { + "version": "4.0.1", + "resolved": "https://registry.npmjs.org/@types/http-cache-semantics/-/http-cache-semantics-4.0.1.tgz", + "integrity": "sha512-SZs7ekbP8CN0txVG2xVRH6EgKmEm31BOxA07vkFaETzZz1xh+cbt8BcI0slpymvwhx5dlFnQG2rTlPVQn+iRPQ==", + "dev": true + }, + "@types/json-schema": { + "version": "7.0.9", + "resolved": "https://registry.npmjs.org/@types/json-schema/-/json-schema-7.0.9.tgz", + "integrity": "sha512-qcUXuemtEu+E5wZSJHNxUXeCZhAfXKQ41D+duX+VYPde7xyEVZci+/oXKJL13tnRs9lR2pr4fod59GT6/X1/yQ==", + "dev": true + }, + "@types/keyv": { + "version": "3.1.3", + "resolved": "https://registry.npmjs.org/@types/keyv/-/keyv-3.1.3.tgz", + "integrity": "sha512-FXCJgyyN3ivVgRoml4h94G/p3kY+u/B86La+QptcqJaWtBWtmc6TtkNfS40n9bIvyLteHh7zXOtgbobORKPbDg==", + "dev": true, + "requires": { + "@types/node": "*" + } + }, + "@types/mocha": { + "version": "9.0.0", + "resolved": "https://registry.npmjs.org/@types/mocha/-/mocha-9.0.0.tgz", + "integrity": "sha512-scN0hAWyLVAvLR9AyW7HoFF5sJZglyBsbPuHO4fv7JRvfmPBMfp1ozWqOf/e4wwPNxezBZXRfWzMb6iFLgEVRA==", + "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==", + "dev": true + }, + "@types/parse-json": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/@types/parse-json/-/parse-json-4.0.0.tgz", + "integrity": "sha512-//oorEZjL6sbPcKUaCdIGlIUeH26mgzimjBB77G6XRgnDl/L5wOnpyBGRe/Mmf5CVW3PwEBE1NjiMZ/ssFh4wA==", + "dev": true + }, + "@types/responselike": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/@types/responselike/-/responselike-1.0.0.tgz", + "integrity": "sha512-85Y2BjiufFzaMIlvJDvTTB8Fxl2xfLo4HgmHzVBz08w4wDePCTjYw66PdrolO0kzli3yam/YCgRufyo1DdQVTA==", + "dev": true, + "requires": { + "@types/node": "*" + } + }, + "@types/sinon": { + "version": "10.0.6", + "resolved": "https://registry.npmjs.org/@types/sinon/-/sinon-10.0.6.tgz", + "integrity": "sha512-6EF+wzMWvBNeGrfP3Nx60hhx+FfwSg1JJBLAAP/IdIUq0EYkqCYf70VT3PhuhPX9eLD+Dp+lNdpb/ZeHG8Yezg==", + "dev": true, + "requires": { + "@sinonjs/fake-timers": "^7.1.0" + } + }, + "@types/yallist": { + "version": "4.0.1", + "resolved": "https://registry.npmjs.org/@types/yallist/-/yallist-4.0.1.tgz", + "integrity": "sha512-G3FNJfaYtN8URU6wd6+uwFI62KO79j7n3XTYcwcFncP8gkfoi0b821GoVVt0oqKVnCqKYOMNKIGpakPoFhzAGA==", + "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==", + "dev": true, + "requires": { + "@types/yargs-parser": "*" + } + }, + "@types/yargs-parser": { + "version": "20.2.1", + "resolved": "https://registry.npmjs.org/@types/yargs-parser/-/yargs-parser-20.2.1.tgz", + "integrity": "sha512-7tFImggNeNBVMsn0vLrpn1H1uPrUBdnARPTpZoitY37ZrdJREzf7I16tMrlK3hen349gr1NYh8CmZQa7CTG6Aw==", + "dev": true + }, + "@typescript-eslint/eslint-plugin": { + "version": "5.3.1", + "resolved": "https://registry.npmjs.org/@typescript-eslint/eslint-plugin/-/eslint-plugin-5.3.1.tgz", + "integrity": "sha512-cFImaoIr5Ojj358xI/SDhjog57OK2NqlpxwdcgyxDA3bJlZcJq5CPzUXtpD7CxI2Hm6ATU7w5fQnnkVnmwpHqw==", + "dev": true, + "requires": { + "@typescript-eslint/experimental-utils": "5.3.1", + "@typescript-eslint/scope-manager": "5.3.1", + "debug": "^4.3.2", + "functional-red-black-tree": "^1.0.1", + "ignore": "^5.1.8", + "regexpp": "^3.2.0", + "semver": "^7.3.5", + "tsutils": "^3.21.0" + } + }, + "@typescript-eslint/experimental-utils": { + "version": "5.3.1", + "resolved": "https://registry.npmjs.org/@typescript-eslint/experimental-utils/-/experimental-utils-5.3.1.tgz", + "integrity": "sha512-RgFn5asjZ5daUhbK5Sp0peq0SSMytqcrkNfU4pnDma2D8P3ElZ6JbYjY8IMSFfZAJ0f3x3tnO3vXHweYg0g59w==", + "dev": true, + "requires": { + "@types/json-schema": "^7.0.9", + "@typescript-eslint/scope-manager": "5.3.1", + "@typescript-eslint/types": "5.3.1", + "@typescript-eslint/typescript-estree": "5.3.1", + "eslint-scope": "^5.1.1", + "eslint-utils": "^3.0.0" + } + }, + "@typescript-eslint/parser": { + "version": "5.3.1", + "resolved": "https://registry.npmjs.org/@typescript-eslint/parser/-/parser-5.3.1.tgz", + "integrity": "sha512-TD+ONlx5c+Qhk21x9gsJAMRohWAUMavSOmJgv3JGy9dgPhuBd5Wok0lmMClZDyJNLLZK1JRKiATzCKZNUmoyfw==", + "dev": true, + "requires": { + "@typescript-eslint/scope-manager": "5.3.1", + "@typescript-eslint/types": "5.3.1", + "@typescript-eslint/typescript-estree": "5.3.1", + "debug": "^4.3.2" + } + }, + "@typescript-eslint/scope-manager": { + "version": "5.3.1", + "resolved": "https://registry.npmjs.org/@typescript-eslint/scope-manager/-/scope-manager-5.3.1.tgz", + "integrity": "sha512-XksFVBgAq0Y9H40BDbuPOTUIp7dn4u8oOuhcgGq7EoDP50eqcafkMVGrypyVGvDYHzjhdUCUwuwVUK4JhkMAMg==", + "dev": true, + "requires": { + "@typescript-eslint/types": "5.3.1", + "@typescript-eslint/visitor-keys": "5.3.1" + } + }, + "@typescript-eslint/types": { + "version": "5.3.1", + "resolved": "https://registry.npmjs.org/@typescript-eslint/types/-/types-5.3.1.tgz", + "integrity": "sha512-bG7HeBLolxKHtdHG54Uac750eXuQQPpdJfCYuw4ZI3bZ7+GgKClMWM8jExBtp7NSP4m8PmLRM8+lhzkYnSmSxQ==", + "dev": true + }, + "@typescript-eslint/typescript-estree": { + "version": "5.3.1", + "resolved": "https://registry.npmjs.org/@typescript-eslint/typescript-estree/-/typescript-estree-5.3.1.tgz", + "integrity": "sha512-PwFbh/PKDVo/Wct6N3w+E4rLZxUDgsoII/GrWM2A62ETOzJd4M6s0Mu7w4CWsZraTbaC5UQI+dLeyOIFF1PquQ==", + "dev": true, + "requires": { + "@typescript-eslint/types": "5.3.1", + "@typescript-eslint/visitor-keys": "5.3.1", + "debug": "^4.3.2", + "globby": "^11.0.4", + "is-glob": "^4.0.3", + "semver": "^7.3.5", + "tsutils": "^3.21.0" + } + }, + "@typescript-eslint/visitor-keys": { + "version": "5.3.1", + "resolved": "https://registry.npmjs.org/@typescript-eslint/visitor-keys/-/visitor-keys-5.3.1.tgz", + "integrity": "sha512-3cHUzUuVTuNHx0Gjjt5pEHa87+lzyqOiHXy/Gz+SJOCW1mpw9xQHIIEwnKn+Thph1mgWyZ90nboOcSuZr/jTTQ==", + "dev": true, + "requires": { + "@typescript-eslint/types": "5.3.1", + "eslint-visitor-keys": "^3.0.0" + } + }, + "@ungap/promise-all-settled": { + "version": "1.1.2", + "resolved": "https://registry.npmjs.org/@ungap/promise-all-settled/-/promise-all-settled-1.1.2.tgz", + "integrity": "sha512-sL/cEvJWAnClXw0wHk85/2L0G6Sj8UB0Ctc1TEMbKSsmpRosqhwj9gWgFRZSrBr2f9tiXISwNhCPmlfqUqyb9Q==", + "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==", + "dev": true + }, + "acorn-jsx": { + "version": "5.3.2", + "resolved": "https://registry.npmjs.org/acorn-jsx/-/acorn-jsx-5.3.2.tgz", + "integrity": "sha512-rq9s+JNhf0IChjtDXxllJ7g41oZk5SlXtp0LHwyA5cejwn7vKmKp4pPri6YEePv2PU65sAsegbXtIinmDFDXgQ==", + "dev": true, + "requires": {} + }, + "acorn-walk": { + "version": "8.2.0", + "resolved": "https://registry.npmjs.org/acorn-walk/-/acorn-walk-8.2.0.tgz", + "integrity": "sha512-k+iyHEuPgSw6SbuDpGQM+06HQUa04DZ3o+F6CSzXMvvI5KMvnaEqXe+YVe555R9nn6GPt404fos4wcgpw12SDA==", + "dev": true + }, + "aggregate-error": { + "version": "3.1.0", + "resolved": "https://registry.npmjs.org/aggregate-error/-/aggregate-error-3.1.0.tgz", + "integrity": "sha512-4I7Td01quW/RpocfNayFdFVk1qSuoh0E7JrbRJ16nH01HhKFQ88INq9Sd+nd72zqRySlr9BmDA8xlEJ6vJMrYA==", + "dev": true, + "requires": { + "clean-stack": "^2.0.0", + "indent-string": "^4.0.0" + } + }, + "ajv": { + "version": "6.12.6", + "resolved": "https://registry.npmjs.org/ajv/-/ajv-6.12.6.tgz", + "integrity": "sha512-j3fVLgvTo527anyYyJOGTYJbG+vnnQYvE0m5mmkc1TK+nxAppkCLMIL0aZ4dblVCNoGShhm+kzE4ZUykBoMg4g==", + "dev": true, + "requires": { + "fast-deep-equal": "^3.1.1", + "fast-json-stable-stringify": "^2.0.0", + "json-schema-traverse": "^0.4.1", + "uri-js": "^4.2.2" + } + }, + "ansi-align": { + "version": "3.0.1", + "resolved": "https://registry.npmjs.org/ansi-align/-/ansi-align-3.0.1.tgz", + "integrity": "sha512-IOfwwBF5iczOjp/WeY4YxyjqAFMQoZufdQWDd19SEExbVLNXqvpzSJ/M7Za4/sCPmQ0+GRquoA7bGcINcxew6w==", + "dev": true, + "requires": { + "string-width": "^4.1.0" + } + }, + "ansi-colors": { + "version": "4.1.1", + "resolved": "https://registry.npmjs.org/ansi-colors/-/ansi-colors-4.1.1.tgz", + "integrity": "sha512-JoX0apGbHaUJBNl6yF+p6JAFYZ666/hhCGKN5t9QFjbJQKUU/g8MNbFDbvfrgKXvI1QpZplPOnwIo99lX/AAmA==", + "dev": true + }, + "ansi-escapes": { + "version": "4.3.2", + "resolved": "https://registry.npmjs.org/ansi-escapes/-/ansi-escapes-4.3.2.tgz", + "integrity": "sha512-gKXj5ALrKWQLsYG9jlTRmR/xKluxHV+Z9QEwNIgCfM1/uwPMCuzVVnh5mwTd+OuBZcwSIMbqssNWRm1lE51QaQ==", + "dev": true, + "requires": { + "type-fest": "^0.21.3" + }, + "dependencies": { + "type-fest": { + "version": "0.21.3", + "resolved": "https://registry.npmjs.org/type-fest/-/type-fest-0.21.3.tgz", + "integrity": "sha512-t0rzBq87m3fVcduHDUFhKmyyX+9eo6WQjZvf51Ea/M0Q7+T374Jp1aUiyUl0GKxp8M/OETVHSDvmkyPgvX+X2w==", + "dev": true + } + } + }, + "ansi-regex": { + "version": "5.0.1", + "resolved": "https://registry.npmjs.org/ansi-regex/-/ansi-regex-5.0.1.tgz", + "integrity": "sha512-quJQXlTSUGL2LH9SUXo8VwsY4soanhgo6LNSm84E1LBcE8s3O0wpdiRzyR9z/ZZJMlMWv37qOOb9pdJlMUEKFQ==", + "dev": true + }, + "ansi-styles": { + "version": "4.3.0", + "resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-4.3.0.tgz", + "integrity": "sha512-zbB9rCJAT1rbjiVDb2hqKFHNYLxgtk8NURxZ3IZwD3F6NtxbXZQCnnSi1Lkx+IDohdPlFp222wVALIheZJQSEg==", + "dev": true, + "requires": { + "color-convert": "^2.0.1" + } + }, + "anymatch": { + "version": "3.1.2", + "resolved": "https://registry.npmjs.org/anymatch/-/anymatch-3.1.2.tgz", + "integrity": "sha512-P43ePfOAIupkguHUycrc4qJ9kz8ZiuOUijaETwX7THt0Y/GNK7v0aa8rY816xWjZ7rJdA5XdMcpVFTKMq+RvWg==", + "dev": true, + "requires": { + "normalize-path": "^3.0.0", + "picomatch": "^2.0.4" + } + }, + "append-transform": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/append-transform/-/append-transform-2.0.0.tgz", + "integrity": "sha512-7yeyCEurROLQJFv5Xj4lEGTy0borxepjFv1g22oAdqFu//SrAlDl1O1Nxx15SH1RoliUml6p8dwJW9jvZughhg==", + "dev": true, + "requires": { + "default-require-extensions": "^3.0.0" + } + }, + "archy": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/archy/-/archy-1.0.0.tgz", + "integrity": "sha1-+cjBN1fMHde8N5rHeyxipcKGjEA=", + "dev": true + }, + "arg": { + "version": "4.1.3", + "resolved": "https://registry.npmjs.org/arg/-/arg-4.1.3.tgz", + "integrity": "sha512-58S9QDqG0Xx27YwPSt9fJxivjYl432YCwfDMfZ+71RAqUrZef7LrKQZ3LHLOwCS4FLNBplP533Zx895SeOCHvA==", + "dev": true + }, + "argparse": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/argparse/-/argparse-2.0.1.tgz", + "integrity": "sha512-8+9WqebbFzpX9OR+Wa6O29asIogeRMzcGtAINdpMHHyAg10f05aSFVBbcEqGf/PXw1EjAZ+q2/bEBg3DvurK3Q==", + "dev": true + }, + "array-union": { + "version": "2.1.0", + "resolved": "https://registry.npmjs.org/array-union/-/array-union-2.1.0.tgz", + "integrity": "sha512-HGyxoOTYUyCM6stUe6EJgnd4EoewAI7zMdfqO+kGjnlZmBDz/cR5pf8r/cR4Wq60sL/p0IkcjUEEPwS3GFrIyw==", + "dev": true + }, + "async-retry": { + "version": "1.3.3", + "resolved": "https://registry.npmjs.org/async-retry/-/async-retry-1.3.3.tgz", + "integrity": "sha512-wfr/jstw9xNi/0teMHrRW7dsz3Lt5ARhYNZ2ewpadnhaIp5mbALhOAP+EAdsC7t4Z6wqsDVv9+W6gm1Dk9mEyw==", + "dev": true, + "requires": { + "retry": "0.13.1" + } + }, + "asynckit": { + "version": "0.4.0", + "resolved": "https://registry.npmjs.org/asynckit/-/asynckit-0.4.0.tgz", + "integrity": "sha1-x57Zf380y48robyXkLzDZkdLS3k=", + "dev": true + }, + "balanced-match": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/balanced-match/-/balanced-match-1.0.2.tgz", + "integrity": "sha512-3oSeUO0TMV67hN1AmbXsK4yaqU7tjiHlbxRDZOpH0KW9+CeX4bRAaX0Anxt0tx2MrpRpWwQaPwIlISEJhYU5Pw==", + "dev": true + }, + "base64-js": { + "version": "1.5.1", + "resolved": "https://registry.npmjs.org/base64-js/-/base64-js-1.5.1.tgz", + "integrity": "sha512-AKpaYlHn8t4SVbOHCy+b5+KKgvR4vrsD8vbvrbiQJps7fKDTkjkDry6ji0rUJjC0kzbNePLwzxq8iypo41qeWA==", + "dev": true + }, + "before-after-hook": { + "version": "2.2.2", + "resolved": "https://registry.npmjs.org/before-after-hook/-/before-after-hook-2.2.2.tgz", + "integrity": "sha512-3pZEU3NT5BFUo/AD5ERPWOgQOCZITni6iavr5AUw5AUwQjMlI0kzu5btnyD39AF0gUEsDPwJT+oY1ORBJijPjQ==", + "dev": true + }, + "binary-extensions": { + "version": "2.2.0", + "resolved": "https://registry.npmjs.org/binary-extensions/-/binary-extensions-2.2.0.tgz", + "integrity": "sha512-jDctJ/IVQbZoJykoeHbhXpOlNBqGNcwXJKJog42E5HDPUwQTSdjCHdihjj0DlnheQ7blbT6dHOafNAiS8ooQKA==", + "dev": true + }, + "bl": { + "version": "4.1.0", + "resolved": "https://registry.npmjs.org/bl/-/bl-4.1.0.tgz", + "integrity": "sha512-1W07cM9gS6DcLperZfFSj+bWLtaPGSOHWhPiGzXmvVJbRLdG82sH/Kn8EtW1VqWVA54AKf2h5k5BbnIbwF3h6w==", + "dev": true, + "requires": { + "buffer": "^5.5.0", + "inherits": "^2.0.4", + "readable-stream": "^3.4.0" + } + }, + "boxen": { + "version": "5.1.2", + "resolved": "https://registry.npmjs.org/boxen/-/boxen-5.1.2.tgz", + "integrity": "sha512-9gYgQKXx+1nP8mP7CzFyaUARhg7D3n1dF/FnErWmu9l6JvGpNUN278h0aSb+QjoiKSWG+iZ3uHrcqk0qrY9RQQ==", + "dev": true, + "requires": { + "ansi-align": "^3.0.0", + "camelcase": "^6.2.0", + "chalk": "^4.1.0", + "cli-boxes": "^2.2.1", + "string-width": "^4.2.2", + "type-fest": "^0.20.2", + "widest-line": "^3.1.0", + "wrap-ansi": "^7.0.0" + }, + "dependencies": { + "camelcase": { + "version": "6.2.0", + "resolved": "https://registry.npmjs.org/camelcase/-/camelcase-6.2.0.tgz", + "integrity": "sha512-c7wVvbw3f37nuobQNtgsgG9POC9qMbNuMQmTCqZv23b6MIz0fcYpBiOlv9gEN/hdLdnZTDQhg6e9Dq5M1vKvfg==", + "dev": true + } + } + }, + "brace-expansion": { + "version": "1.1.11", + "resolved": "https://registry.npmjs.org/brace-expansion/-/brace-expansion-1.1.11.tgz", + "integrity": "sha512-iCuPHDFgrHX7H2vEI/5xpz07zSHB00TpugqhmYtVmMO6518mCuRMoOYFldEBl0g187ufozdaHgWKcYFb61qGiA==", + "dev": true, + "requires": { + "balanced-match": "^1.0.0", + "concat-map": "0.0.1" + } + }, + "braces": { + "version": "3.0.2", + "resolved": "https://registry.npmjs.org/braces/-/braces-3.0.2.tgz", + "integrity": "sha512-b8um+L1RzM3WDSzvhm6gIz1yfTbBt6YTlcEKAvsmqCZZFw46z626lVj9j1yEPW33H5H+lBQpZMP1k8l+78Ha0A==", + "dev": true, + "requires": { + "fill-range": "^7.0.1" + } + }, + "browser-stdout": { + "version": "1.3.1", + "resolved": "https://registry.npmjs.org/browser-stdout/-/browser-stdout-1.3.1.tgz", + "integrity": "sha512-qhAVI1+Av2X7qelOfAIYwXONood6XlZE/fXaBSmW/T5SzLAmCgzi+eiWE7fUvbHaeNBQH13UftjpXxsfLkMpgw==", + "dev": true + }, + "browserslist": { + "version": "4.17.6", + "resolved": "https://registry.npmjs.org/browserslist/-/browserslist-4.17.6.tgz", + "integrity": "sha512-uPgz3vyRTlEiCv4ee9KlsKgo2V6qPk7Jsn0KAn2OBqbqKo3iNcPEC1Ti6J4dwnz+aIRfEEEuOzC9IBk8tXUomw==", + "dev": true, + "requires": { + "caniuse-lite": "^1.0.30001274", + "electron-to-chromium": "^1.3.886", + "escalade": "^3.1.1", + "node-releases": "^2.0.1", + "picocolors": "^1.0.0" + } + }, + "buffer": { + "version": "5.7.1", + "resolved": "https://registry.npmjs.org/buffer/-/buffer-5.7.1.tgz", + "integrity": "sha512-EHcyIPBQ4BSGlvjB16k5KgAJ27CIsHY/2JBmCRReo48y9rQ3MaUzWX3KVlBa4U7MyX02HdVj0K7C3WaB3ju7FQ==", + "dev": true, + "requires": { + "base64-js": "^1.3.1", + "ieee754": "^1.1.13" + } + }, + "buffer-from": { + "version": "1.1.2", + "resolved": "https://registry.npmjs.org/buffer-from/-/buffer-from-1.1.2.tgz", + "integrity": "sha512-E+XQCRwSbaaiChtv6k6Dwgc+bx+Bs6vuKJHHl5kox/BaKbhiXzqQOwK4cO22yElGp2OCmjwVhT3HmxgyPGnJfQ==", + "dev": true + }, + "cacheable-lookup": { + "version": "5.0.4", + "resolved": "https://registry.npmjs.org/cacheable-lookup/-/cacheable-lookup-5.0.4.tgz", + "integrity": "sha512-2/kNscPhpcxrOigMZzbiWF7dz8ilhb/nIHU3EyZiXWXpeq/au8qJ8VhdftMkty3n7Gj6HIGalQG8oiBNB3AJgA==", + "dev": true + }, + "cacheable-request": { + "version": "7.0.2", + "resolved": "https://registry.npmjs.org/cacheable-request/-/cacheable-request-7.0.2.tgz", + "integrity": "sha512-pouW8/FmiPQbuGpkXQ9BAPv/Mo5xDGANgSNXzTzJ8DrKGuXOssM4wIQRjfanNRh3Yu5cfYPvcorqbhg2KIJtew==", + "dev": true, + "requires": { + "clone-response": "^1.0.2", + "get-stream": "^5.1.0", + "http-cache-semantics": "^4.0.0", + "keyv": "^4.0.0", + "lowercase-keys": "^2.0.0", + "normalize-url": "^6.0.1", + "responselike": "^2.0.0" + }, + "dependencies": { + "get-stream": { + "version": "5.2.0", + "resolved": "https://registry.npmjs.org/get-stream/-/get-stream-5.2.0.tgz", + "integrity": "sha512-nBF+F1rAZVCu/p7rjzgA+Yb4lfYXrpl7a6VmJrU8wF9I1CKvP/QwPNZHnOlwbTkY6dvtFIzFMSyQXbLoTQPRpA==", + "dev": true, + "requires": { + "pump": "^3.0.0" + } + } + } + }, + "caching-transform": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/caching-transform/-/caching-transform-4.0.0.tgz", + "integrity": "sha512-kpqOvwXnjjN44D89K5ccQC+RUrsy7jB/XLlRrx0D7/2HNcTPqzsb6XgYoErwko6QsV184CA2YgS1fxDiiDZMWA==", + "dev": true, + "requires": { + "hasha": "^5.0.0", + "make-dir": "^3.0.0", + "package-hash": "^4.0.0", + "write-file-atomic": "^3.0.0" + } + }, + "call-bind": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/call-bind/-/call-bind-1.0.2.tgz", + "integrity": "sha512-7O+FbCihrB5WGbFYesctwmTKae6rOiIzmz1icreWJ+0aA7LJfuqhEso2T9ncpcFtzMQtzXf2QGGueWJGTYsqrA==", + "dev": true, + "requires": { + "function-bind": "^1.1.1", + "get-intrinsic": "^1.0.2" + } + }, + "callsites": { + "version": "3.1.0", + "resolved": "https://registry.npmjs.org/callsites/-/callsites-3.1.0.tgz", + "integrity": "sha512-P8BjAsXvZS+VIDUI11hHCQEv74YT67YUi5JJFNWIqL235sBmjX4+qx9Muvls5ivyNENctx46xQLQ3aTuE7ssaQ==", + "dev": true + }, + "camelcase": { + "version": "5.3.1", + "resolved": "https://registry.npmjs.org/camelcase/-/camelcase-5.3.1.tgz", + "integrity": "sha512-L28STB170nwWS63UjtlEOE3dldQApaJXZkOI1uMFfzf3rRuPegHaHesyee+YxQ+W6SvRDQV6UrdOdRiR153wJg==", + "dev": true + }, + "caniuse-lite": { + "version": "1.0.30001279", + "resolved": "https://registry.npmjs.org/caniuse-lite/-/caniuse-lite-1.0.30001279.tgz", + "integrity": "sha512-VfEHpzHEXj6/CxggTwSFoZBBYGQfQv9Cf42KPlO79sWXCD1QNKWKsKzFeWL7QpZHJQYAvocqV6Rty1yJMkqWLQ==", + "dev": true + }, + "chalk": { + "version": "4.1.2", + "resolved": "https://registry.npmjs.org/chalk/-/chalk-4.1.2.tgz", + "integrity": "sha512-oKnbhFyRIXpUuez8iBMmyEa4nbj4IOQyuhc/wy9kY7/WVPcwIO9VA668Pu8RkO7+0G76SLROeyw9CpQ061i4mA==", + "dev": true, + "requires": { + "ansi-styles": "^4.1.0", + "supports-color": "^7.1.0" + } + }, + "chardet": { + "version": "0.7.0", + "resolved": "https://registry.npmjs.org/chardet/-/chardet-0.7.0.tgz", + "integrity": "sha512-mT8iDcrh03qDGRRmoA2hmBJnxpllMR+0/0qlzjqZES6NdiWDcZkCNAk4rPFZ9Q85r27unkiNNg8ZOiwZXBHwcA==", + "dev": true + }, + "chokidar": { + "version": "3.5.2", + "resolved": "https://registry.npmjs.org/chokidar/-/chokidar-3.5.2.tgz", + "integrity": "sha512-ekGhOnNVPgT77r4K/U3GDhu+FQ2S8TnK/s2KbIGXi0SZWuwkZ2QNyfWdZW+TVfn84DpEP7rLeCt2UI6bJ8GwbQ==", + "dev": true, + "requires": { + "anymatch": "~3.1.2", + "braces": "~3.0.2", + "fsevents": "~2.3.2", + "glob-parent": "~5.1.2", + "is-binary-path": "~2.1.0", + "is-glob": "~4.0.1", + "normalize-path": "~3.0.0", + "readdirp": "~3.6.0" + }, + "dependencies": { + "glob-parent": { + "version": "5.1.2", + "resolved": "https://registry.npmjs.org/glob-parent/-/glob-parent-5.1.2.tgz", + "integrity": "sha512-AOIgSQCepiJYwP3ARnGx+5VnTu2HBYdzbGP45eLw1vr3zB3vZLeyed1sC9hnbcOc9/SrMyM5RPQrkGz4aS9Zow==", + "dev": true, + "requires": { + "is-glob": "^4.0.1" + } + } + } + }, + "ci-info": { + "version": "3.2.0", + "resolved": "https://registry.npmjs.org/ci-info/-/ci-info-3.2.0.tgz", + "integrity": "sha512-dVqRX7fLUm8J6FgHJ418XuIgDLZDkYcDFTeL6TA2gt5WlIZUQrrH6EZrNClwT/H0FateUsZkGIOPRrLbP+PR9A==", + "dev": true + }, + "clean-stack": { + "version": "2.2.0", + "resolved": "https://registry.npmjs.org/clean-stack/-/clean-stack-2.2.0.tgz", + "integrity": "sha512-4diC9HaTE+KRAMWhDhrGOECgWZxoevMc5TlkObMqNSsVU62PYzXZ/SMTjzyGAFF1YusgxGcSWTEXBhp0CPwQ1A==", + "dev": true + }, + "cli-boxes": { + "version": "2.2.1", + "resolved": "https://registry.npmjs.org/cli-boxes/-/cli-boxes-2.2.1.tgz", + "integrity": "sha512-y4coMcylgSCdVinjiDBuR8PCC2bLjyGTwEmPb9NHR/QaNU6EUOXcTY/s6VjGMD6ENSEaeQYHCY0GNGS5jfMwPw==", + "dev": true + }, + "cli-cursor": { + "version": "3.1.0", + "resolved": "https://registry.npmjs.org/cli-cursor/-/cli-cursor-3.1.0.tgz", + "integrity": "sha512-I/zHAwsKf9FqGoXM4WWRACob9+SNukZTd94DWF57E4toouRulbCxcUh6RKUEOQlYTHJnzkPMySvPNaaSLNfLZw==", + "dev": true, + "requires": { + "restore-cursor": "^3.1.0" + } + }, + "cli-spinners": { + "version": "2.6.1", + "resolved": "https://registry.npmjs.org/cli-spinners/-/cli-spinners-2.6.1.tgz", + "integrity": "sha512-x/5fWmGMnbKQAaNwN+UZlV79qBLM9JFnJuJ03gIi5whrob0xV0ofNVHy9DhwGdsMJQc2OKv0oGmLzvaqvAVv+g==", + "dev": true + }, + "cli-width": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/cli-width/-/cli-width-3.0.0.tgz", + "integrity": "sha512-FxqpkPPwu1HjuN93Omfm4h8uIanXofW0RxVEW3k5RKx+mJJYSthzNhp32Kzxxy3YAEZ/Dc/EWN1vZRY0+kOhbw==", + "dev": true + }, + "cliui": { + "version": "7.0.4", + "resolved": "https://registry.npmjs.org/cliui/-/cliui-7.0.4.tgz", + "integrity": "sha512-OcRE68cOsVMXp1Yvonl/fzkQOyjLSu/8bhPDfQt0e0/Eb283TKP20Fs2MqoPsr9SwA595rRCA+QMzYc9nBP+JQ==", + "dev": true, + "requires": { + "string-width": "^4.2.0", + "strip-ansi": "^6.0.0", + "wrap-ansi": "^7.0.0" + } + }, + "clone": { + "version": "1.0.4", + "resolved": "https://registry.npmjs.org/clone/-/clone-1.0.4.tgz", + "integrity": "sha1-2jCcwmPfFZlMaIypAheco8fNfH4=", + "dev": true + }, + "clone-response": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/clone-response/-/clone-response-1.0.2.tgz", + "integrity": "sha1-0dyXOSAxTfZ/vrlCI7TuNQI56Ws=", + "dev": true, + "requires": { + "mimic-response": "^1.0.0" + } + }, + "cluster-key-slot": { + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/cluster-key-slot/-/cluster-key-slot-1.1.0.tgz", + "integrity": "sha512-2Nii8p3RwAPiFwsnZvukotvow2rIHM+yQ6ZcBXGHdniadkYGZYiGmkHJIbZPIV9nfv7m/U1IPMVVcAhoWFeklw==" + }, + "color-convert": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/color-convert/-/color-convert-2.0.1.tgz", + "integrity": "sha512-RRECPsj7iu/xb5oKYcsFHSppFNnsj/52OVTRKb4zP5onXwVF3zVmmToNcOfGC+CRDpfK/U584fMg38ZHCaElKQ==", + "dev": true, + "requires": { + "color-name": "~1.1.4" + } + }, + "color-name": { + "version": "1.1.4", + "resolved": "https://registry.npmjs.org/color-name/-/color-name-1.1.4.tgz", + "integrity": "sha512-dOy+3AuW3a2wNbZHIuMZpTcgjGuLU/uBL/ubcZF9OXbDo8ff4O8yVp5Bf0efS8uEoYo5q4Fx7dY9OgQGXgAsQA==", + "dev": true + }, + "combined-stream": { + "version": "1.0.8", + "resolved": "https://registry.npmjs.org/combined-stream/-/combined-stream-1.0.8.tgz", + "integrity": "sha512-FQN4MRfuJeHf7cBbBMJFXhKSDq+2kAArBlmRBvcvFE5BB1HZKXtSFASDhdlz9zOYwxh8lDdnvmMOe/+5cdoEdg==", + "dev": true, + "requires": { + "delayed-stream": "~1.0.0" + } + }, + "commondir": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/commondir/-/commondir-1.0.1.tgz", + "integrity": "sha1-3dgA2gxmEnOTzKWVDqloo6rxJTs=", + "dev": true + }, + "concat-map": { + "version": "0.0.1", + "resolved": "https://registry.npmjs.org/concat-map/-/concat-map-0.0.1.tgz", + "integrity": "sha1-2Klr13/Wjfd5OnMDajug1UBdR3s=", + "dev": true + }, + "configstore": { + "version": "5.0.1", + "resolved": "https://registry.npmjs.org/configstore/-/configstore-5.0.1.tgz", + "integrity": "sha512-aMKprgk5YhBNyH25hj8wGt2+D52Sw1DRRIzqBwLp2Ya9mFmY8KPvvtvmna8SxVR9JMZ4kzMD68N22vlaRpkeFA==", + "dev": true, + "requires": { + "dot-prop": "^5.2.0", + "graceful-fs": "^4.1.2", + "make-dir": "^3.0.0", + "unique-string": "^2.0.0", + "write-file-atomic": "^3.0.0", + "xdg-basedir": "^4.0.0" + } + }, + "convert-source-map": { + "version": "1.8.0", + "resolved": "https://registry.npmjs.org/convert-source-map/-/convert-source-map-1.8.0.tgz", + "integrity": "sha512-+OQdjP49zViI/6i7nIJpA8rAl4sV/JdPfU9nZs3VqOwGIgizICvuN2ru6fMd+4llL0tar18UYJXfZ/TWtmhUjA==", + "dev": true, + "requires": { + "safe-buffer": "~5.1.1" + } + }, + "cosmiconfig": { + "version": "7.0.1", + "resolved": "https://registry.npmjs.org/cosmiconfig/-/cosmiconfig-7.0.1.tgz", + "integrity": "sha512-a1YWNUV2HwGimB7dU2s1wUMurNKjpx60HxBB6xUM8Re+2s1g1IIfJvFR0/iCF+XHdE0GMTKTuLR32UQff4TEyQ==", + "dev": true, + "requires": { + "@types/parse-json": "^4.0.0", + "import-fresh": "^3.2.1", + "parse-json": "^5.0.0", + "path-type": "^4.0.0", + "yaml": "^1.10.0" + } + }, + "create-require": { + "version": "1.1.1", + "resolved": "https://registry.npmjs.org/create-require/-/create-require-1.1.1.tgz", + "integrity": "sha512-dcKFX3jn0MpIaXjisoRvexIJVEKzaq7z2rZKxf+MSr9TkdmHmsU4m2lcLojrj/FHl8mk5VxMmYA+ftRkP/3oKQ==", + "dev": true + }, + "cross-spawn": { + "version": "7.0.3", + "resolved": "https://registry.npmjs.org/cross-spawn/-/cross-spawn-7.0.3.tgz", + "integrity": "sha512-iRDPJKUPVEND7dHPO8rkbOnPpyDygcDFtWjpeWNCgy8WP2rXcxXL8TskReQl6OrB2G7+UJrags1q15Fudc7G6w==", + "dev": true, + "requires": { + "path-key": "^3.1.0", + "shebang-command": "^2.0.0", + "which": "^2.0.1" + } + }, + "crypto-random-string": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/crypto-random-string/-/crypto-random-string-2.0.0.tgz", + "integrity": "sha512-v1plID3y9r/lPhviJ1wrXpLeyUIGAZ2SHNYTEapm7/8A9nLPoyvVp3RK/EPFqn5kEznyWgYZNsRtYYIWbuG8KA==", + "dev": true + }, + "debug": { + "version": "4.3.2", + "resolved": "https://registry.npmjs.org/debug/-/debug-4.3.2.tgz", + "integrity": "sha512-mOp8wKcvj7XxC78zLgw/ZA+6TSgkoE2C/ienthhRD298T7UNwAg9diBpLRxC0mOezLl4B0xV7M0cCO6P/O0Xhw==", + "dev": true, + "requires": { + "ms": "2.1.2" + } + }, + "decamelize": { + "version": "1.2.0", + "resolved": "https://registry.npmjs.org/decamelize/-/decamelize-1.2.0.tgz", + "integrity": "sha1-9lNNFRSCabIDUue+4m9QH5oZEpA=", + "dev": true + }, + "decode-uri-component": { + "version": "0.2.0", + "resolved": "https://registry.npmjs.org/decode-uri-component/-/decode-uri-component-0.2.0.tgz", + "integrity": "sha1-6zkTMzRYd1y4TNGh+uBiEGu4dUU=", + "dev": true + }, + "decompress-response": { + "version": "6.0.0", + "resolved": "https://registry.npmjs.org/decompress-response/-/decompress-response-6.0.0.tgz", + "integrity": "sha512-aW35yZM6Bb/4oJlZncMH2LCoZtJXTRxES17vE3hoRiowU2kWHaJKFkSBDnDR+cm9J+9QhXmREyIfv0pji9ejCQ==", + "dev": true, + "requires": { + "mimic-response": "^3.1.0" + }, + "dependencies": { + "mimic-response": { + "version": "3.1.0", + "resolved": "https://registry.npmjs.org/mimic-response/-/mimic-response-3.1.0.tgz", + "integrity": "sha512-z0yWI+4FDrrweS8Zmt4Ej5HdJmky15+L2e6Wgn3+iK5fWzb6T3fhNFq2+MeTRb064c6Wr4N/wv0DzQTjNzHNGQ==", + "dev": true + } + } + }, + "deep-extend": { + "version": "0.6.0", + "resolved": "https://registry.npmjs.org/deep-extend/-/deep-extend-0.6.0.tgz", + "integrity": "sha512-LOHxIOaPYdHlJRtCQfDIVZtfw/ufM8+rVj649RIHzcm/vGwQRXFt6OPqIFWsm2XEMrNIEtWR64sY1LEKD2vAOA==", + "dev": true + }, + "deep-is": { + "version": "0.1.4", + "resolved": "https://registry.npmjs.org/deep-is/-/deep-is-0.1.4.tgz", + "integrity": "sha512-oIPzksmTg4/MriiaYGO+okXDT7ztn/w3Eptv/+gSIdMdKsJo0u4CfYNFJPy+4SKMuCqGw2wxnA+URMg3t8a/bQ==", + "dev": true + }, + "default-require-extensions": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/default-require-extensions/-/default-require-extensions-3.0.0.tgz", + "integrity": "sha512-ek6DpXq/SCpvjhpFsLFRVtIxJCRw6fUR42lYMVZuUMK7n8eMz4Uh5clckdBjEpLhn/gEBZo7hDJnJcwdKLKQjg==", + "dev": true, + "requires": { + "strip-bom": "^4.0.0" + } + }, + "defaults": { + "version": "1.0.3", + "resolved": "https://registry.npmjs.org/defaults/-/defaults-1.0.3.tgz", + "integrity": "sha1-xlYFHpgX2f8I7YgUd/P+QBnz730=", + "dev": true, + "requires": { + "clone": "^1.0.2" + } + }, + "defer-to-connect": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/defer-to-connect/-/defer-to-connect-2.0.1.tgz", + "integrity": "sha512-4tvttepXG1VaYGrRibk5EwJd1t4udunSOVMdLSAL6mId1ix438oPwPZMALY41FCijukO1L0twNcGsdzS7dHgDg==", + "dev": true + }, + "delayed-stream": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/delayed-stream/-/delayed-stream-1.0.0.tgz", + "integrity": "sha1-3zrhmayt+31ECqrgsp4icrJOxhk=", + "dev": true + }, + "deprecated-obj": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/deprecated-obj/-/deprecated-obj-2.0.0.tgz", + "integrity": "sha512-CkdywZC2rJ8RGh+y3MM1fw1EJ4oO/oNExGbRFv0AQoMS+faTd3nO7slYjkj/6t8OnIMUE+wxh6G97YHhK1ytrw==", + "dev": true, + "requires": { + "flat": "^5.0.2", + "lodash": "^4.17.20" + } + }, + "deprecation": { + "version": "2.3.1", + "resolved": "https://registry.npmjs.org/deprecation/-/deprecation-2.3.1.tgz", + "integrity": "sha512-xmHIy4F3scKVwMsQ4WnVaS8bHOx0DmVwRywosKhaILI0ywMDWPtBSku2HNxRvF7jtwDRsoEwYQSfbxj8b7RlJQ==", + "dev": true + }, + "diff": { + "version": "5.0.0", + "resolved": "https://registry.npmjs.org/diff/-/diff-5.0.0.tgz", + "integrity": "sha512-/VTCrvm5Z0JGty/BWHljh+BAiw3IK+2j87NGMu8Nwc/f48WoDAC395uomO9ZD117ZOBaHmkX1oyLvkVM/aIT3w==", + "dev": true + }, + "dir-glob": { + "version": "3.0.1", + "resolved": "https://registry.npmjs.org/dir-glob/-/dir-glob-3.0.1.tgz", + "integrity": "sha512-WkrWp9GR4KXfKGYzOLmTuGVi1UWFfws377n9cc55/tb6DuqyF6pcQ5AbiHEshaDpY9v6oaSr2XCDidGmMwdzIA==", + "dev": true, + "requires": { + "path-type": "^4.0.0" + } + }, + "doctrine": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/doctrine/-/doctrine-3.0.0.tgz", + "integrity": "sha512-yS+Q5i3hBf7GBkd4KG8a7eBNNWNGLTaEwwYWUijIYM7zrlYDM0BFXHjjPWlWZ1Rg7UaddZeIDmi9jF3HmqiQ2w==", + "dev": true, + "requires": { + "esutils": "^2.0.2" + } + }, + "dot-prop": { + "version": "5.3.0", + "resolved": "https://registry.npmjs.org/dot-prop/-/dot-prop-5.3.0.tgz", + "integrity": "sha512-QM8q3zDe58hqUqjraQOmzZ1LIH9SWQJTlEKCH4kJ2oQvLZk7RbQXvtDM2XEq3fwkV9CCvvH4LA0AV+ogFsBM2Q==", + "dev": true, + "requires": { + "is-obj": "^2.0.0" + } + }, + "duplexer3": { + "version": "0.1.4", + "resolved": "https://registry.npmjs.org/duplexer3/-/duplexer3-0.1.4.tgz", + "integrity": "sha1-7gHdHKwO08vH/b6jfcCo8c4ALOI=", + "dev": true + }, + "electron-to-chromium": { + "version": "1.3.893", + "resolved": "https://registry.npmjs.org/electron-to-chromium/-/electron-to-chromium-1.3.893.tgz", + "integrity": "sha512-ChtwF7qB03INq1SyMpue08wc6cve+ktj2UC/Y7se9vB+JryfzziJeYwsgb8jLaCA5GMkHCdn5M62PfSMWhifZg==", + "dev": true + }, + "emoji-regex": { + "version": "8.0.0", + "resolved": "https://registry.npmjs.org/emoji-regex/-/emoji-regex-8.0.0.tgz", + "integrity": "sha512-MSjYzcWNOA0ewAHpz0MxpYFvwg6yjy1NG3xteoqz644VCo/RPgnr1/GGt+ic3iJTzQ8Eu3TdM14SawnVUmGE6A==", + "dev": true + }, + "end-of-stream": { + "version": "1.4.4", + "resolved": "https://registry.npmjs.org/end-of-stream/-/end-of-stream-1.4.4.tgz", + "integrity": "sha512-+uw1inIHVPQoaVuHzRyXd21icM+cnt4CzD5rW+NC1wjOUSTOs+Te7FOv7AhN7vS9x/oIyhLP5PR1H+phQAHu5Q==", + "dev": true, + "requires": { + "once": "^1.4.0" + } + }, + "enquirer": { + "version": "2.3.6", + "resolved": "https://registry.npmjs.org/enquirer/-/enquirer-2.3.6.tgz", + "integrity": "sha512-yjNnPr315/FjS4zIsUxYguYUPP2e1NK4d7E7ZOLiyYCcbFBiTMyID+2wvm2w6+pZ/odMA7cRkjhsPbltwBOrLg==", + "dev": true, + "requires": { + "ansi-colors": "^4.1.1" + } + }, + "error-ex": { + "version": "1.3.2", + "resolved": "https://registry.npmjs.org/error-ex/-/error-ex-1.3.2.tgz", + "integrity": "sha512-7dFHNmqeFSEt2ZBsCriorKnn3Z2pj+fd9kmI6QoWw4//DL+icEBfc0U7qJCisqrTsKTjw4fNFy2pW9OqStD84g==", + "dev": true, + "requires": { + "is-arrayish": "^0.2.1" + } + }, + "es6-error": { + "version": "4.1.1", + "resolved": "https://registry.npmjs.org/es6-error/-/es6-error-4.1.1.tgz", + "integrity": "sha512-Um/+FxMr9CISWh0bi5Zv0iOD+4cFh5qLeks1qhAopKVAJw3drgKbKySikp7wGhDL0HPeaja0P5ULZrxLkniUVg==", + "dev": true + }, + "escalade": { + "version": "3.1.1", + "resolved": "https://registry.npmjs.org/escalade/-/escalade-3.1.1.tgz", + "integrity": "sha512-k0er2gUkLf8O0zKJiAhmkTnJlTvINGv7ygDNPbeIsX/TJjGJZHuh9B2UxbsaEkmlEo9MfhrSzmhIlhRlI2GXnw==", + "dev": true + }, + "escape-goat": { + "version": "2.1.1", + "resolved": "https://registry.npmjs.org/escape-goat/-/escape-goat-2.1.1.tgz", + "integrity": "sha512-8/uIhbG12Csjy2JEW7D9pHbreaVaS/OpN3ycnyvElTdwM5n6GY6W6e2IPemfvGZeUMqZ9A/3GqIZMgKnBhAw/Q==", + "dev": true + }, + "escape-string-regexp": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/escape-string-regexp/-/escape-string-regexp-4.0.0.tgz", + "integrity": "sha512-TtpcNJ3XAzx3Gq8sWRzJaVajRs0uVxA2YAkdb1jm2YkPz4G6egUFAyA3n5vtEIZefPk5Wa4UXbKuS5fKkJWdgA==", + "dev": true + }, + "eslint": { + "version": "8.2.0", + "resolved": "https://registry.npmjs.org/eslint/-/eslint-8.2.0.tgz", + "integrity": "sha512-erw7XmM+CLxTOickrimJ1SiF55jiNlVSp2qqm0NuBWPtHYQCegD5ZMaW0c3i5ytPqL+SSLaCxdvQXFPLJn+ABw==", + "dev": true, + "requires": { + "@eslint/eslintrc": "^1.0.4", + "@humanwhocodes/config-array": "^0.6.0", + "ajv": "^6.10.0", + "chalk": "^4.0.0", + "cross-spawn": "^7.0.2", + "debug": "^4.3.2", + "doctrine": "^3.0.0", + "enquirer": "^2.3.5", + "escape-string-regexp": "^4.0.0", + "eslint-scope": "^6.0.0", + "eslint-utils": "^3.0.0", + "eslint-visitor-keys": "^3.0.0", + "espree": "^9.0.0", + "esquery": "^1.4.0", + "esutils": "^2.0.2", + "fast-deep-equal": "^3.1.3", + "file-entry-cache": "^6.0.1", + "functional-red-black-tree": "^1.0.1", + "glob-parent": "^6.0.1", + "globals": "^13.6.0", + "ignore": "^4.0.6", + "import-fresh": "^3.0.0", + "imurmurhash": "^0.1.4", + "is-glob": "^4.0.0", + "js-yaml": "^4.1.0", + "json-stable-stringify-without-jsonify": "^1.0.1", + "levn": "^0.4.1", + "lodash.merge": "^4.6.2", + "minimatch": "^3.0.4", + "natural-compare": "^1.4.0", + "optionator": "^0.9.1", + "progress": "^2.0.0", + "regexpp": "^3.2.0", + "semver": "^7.2.1", + "strip-ansi": "^6.0.1", + "strip-json-comments": "^3.1.0", + "text-table": "^0.2.0", + "v8-compile-cache": "^2.0.3" + }, + "dependencies": { + "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==", + "dev": true, + "requires": { + "esrecurse": "^4.3.0", + "estraverse": "^5.2.0" + } + }, + "estraverse": { + "version": "5.3.0", + "resolved": "https://registry.npmjs.org/estraverse/-/estraverse-5.3.0.tgz", + "integrity": "sha512-MMdARuVEQziNTeJD8DgMqmhwR11BRQ/cBP+pLtYdSTnf3MIO8fFeiINEbX36ZdNlfU/7A9f3gUw49B3oQsvwBA==", + "dev": true + }, + "ignore": { + "version": "4.0.6", + "resolved": "https://registry.npmjs.org/ignore/-/ignore-4.0.6.tgz", + "integrity": "sha512-cyFDKrqc/YdcWFniJhzI42+AzS+gNwmUzOSFcRCQYwySuBBBy/KjuxWLZ/FHEH6Moq1NizMOBWyTcv8O4OZIMg==", + "dev": true + } + } + }, + "eslint-scope": { + "version": "5.1.1", + "resolved": "https://registry.npmjs.org/eslint-scope/-/eslint-scope-5.1.1.tgz", + "integrity": "sha512-2NxwbF/hZ0KpepYN0cNbo+FN6XoK7GaHlQhgx/hIZl6Va0bF45RQOOwhLIy8lQDbuCiadSLCBnH2CFYquit5bw==", + "dev": true, + "requires": { + "esrecurse": "^4.3.0", + "estraverse": "^4.1.1" + } + }, + "eslint-utils": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/eslint-utils/-/eslint-utils-3.0.0.tgz", + "integrity": "sha512-uuQC43IGctw68pJA1RgbQS8/NP7rch6Cwd4j3ZBtgo4/8Flj4eGE7ZYSZRN3iq5pVUv6GPdW5Z1RFleo84uLDA==", + "dev": true, + "requires": { + "eslint-visitor-keys": "^2.0.0" + }, + "dependencies": { + "eslint-visitor-keys": { + "version": "2.1.0", + "resolved": "https://registry.npmjs.org/eslint-visitor-keys/-/eslint-visitor-keys-2.1.0.tgz", + "integrity": "sha512-0rSmRBzXgDzIsD6mGdJgevzgezI534Cer5L/vyMX0kHzT/jiB43jRhd9YUlMGYLQy2zprNmoT8qasCGtY+QaKw==", + "dev": true + } + } + }, + "eslint-visitor-keys": { + "version": "3.1.0", + "resolved": "https://registry.npmjs.org/eslint-visitor-keys/-/eslint-visitor-keys-3.1.0.tgz", + "integrity": "sha512-yWJFpu4DtjsWKkt5GeNBBuZMlNcYVs6vRCLoCVEJrTjaSB6LC98gFipNK/erM2Heg/E8mIK+hXG/pJMLK+eRZA==", + "dev": true + }, + "espree": { + "version": "9.0.0", + "resolved": "https://registry.npmjs.org/espree/-/espree-9.0.0.tgz", + "integrity": "sha512-r5EQJcYZ2oaGbeR0jR0fFVijGOcwai07/690YRXLINuhmVeRY4UKSAsQPe/0BNuDgwP7Ophoc1PRsr2E3tkbdQ==", + "dev": true, + "requires": { + "acorn": "^8.5.0", + "acorn-jsx": "^5.3.1", + "eslint-visitor-keys": "^3.0.0" + } + }, + "esprima": { + "version": "4.0.1", + "resolved": "https://registry.npmjs.org/esprima/-/esprima-4.0.1.tgz", + "integrity": "sha512-eGuFFw7Upda+g4p+QHvnW0RyTX/SVeJBDM/gCtMARO0cLuT2HcEKnTPvhjV6aGeqrCB/sbNop0Kszm0jsaWU4A==", + "dev": true + }, + "esquery": { + "version": "1.4.0", + "resolved": "https://registry.npmjs.org/esquery/-/esquery-1.4.0.tgz", + "integrity": "sha512-cCDispWt5vHHtwMY2YrAQ4ibFkAL8RbH5YGBnZBc90MolvvfkkQcJro/aZiAQUlQ3qgrYS6D6v8Gc5G5CQsc9w==", + "dev": true, + "requires": { + "estraverse": "^5.1.0" + }, + "dependencies": { + "estraverse": { + "version": "5.3.0", + "resolved": "https://registry.npmjs.org/estraverse/-/estraverse-5.3.0.tgz", + "integrity": "sha512-MMdARuVEQziNTeJD8DgMqmhwR11BRQ/cBP+pLtYdSTnf3MIO8fFeiINEbX36ZdNlfU/7A9f3gUw49B3oQsvwBA==", + "dev": true + } + } + }, + "esrecurse": { + "version": "4.3.0", + "resolved": "https://registry.npmjs.org/esrecurse/-/esrecurse-4.3.0.tgz", + "integrity": "sha512-KmfKL3b6G+RXvP8N1vr3Tq1kL/oCFgn2NYXEtqP8/L3pKapUA4G8cFVaoF3SU323CD4XypR/ffioHmkti6/Tag==", + "dev": true, + "requires": { + "estraverse": "^5.2.0" + }, + "dependencies": { + "estraverse": { + "version": "5.3.0", + "resolved": "https://registry.npmjs.org/estraverse/-/estraverse-5.3.0.tgz", + "integrity": "sha512-MMdARuVEQziNTeJD8DgMqmhwR11BRQ/cBP+pLtYdSTnf3MIO8fFeiINEbX36ZdNlfU/7A9f3gUw49B3oQsvwBA==", + "dev": true + } + } + }, + "estraverse": { + "version": "4.3.0", + "resolved": "https://registry.npmjs.org/estraverse/-/estraverse-4.3.0.tgz", + "integrity": "sha512-39nnKffWz8xN1BU/2c79n9nB9HDzo0niYUqx6xyqUnyoAnQyyWpOTdZEeiCch8BBu515t4wp9ZmgVfVhn9EBpw==", + "dev": true + }, + "esutils": { + "version": "2.0.3", + "resolved": "https://registry.npmjs.org/esutils/-/esutils-2.0.3.tgz", + "integrity": "sha512-kVscqXk4OCp68SZ0dkgEKVi6/8ij300KBWTJq32P/dYeWTSwK41WyTxalN1eRmA5Z9UU/LX9D7FWSmV9SAYx6g==", + "dev": true + }, + "execa": { + "version": "5.1.1", + "resolved": "https://registry.npmjs.org/execa/-/execa-5.1.1.tgz", + "integrity": "sha512-8uSpZZocAZRBAPIEINJj3Lo9HyGitllczc27Eh5YYojjMFMn8yHMDMaUHE2Jqfq05D/wucwI4JGURyXt1vchyg==", + "dev": true, + "requires": { + "cross-spawn": "^7.0.3", + "get-stream": "^6.0.0", + "human-signals": "^2.1.0", + "is-stream": "^2.0.0", + "merge-stream": "^2.0.0", + "npm-run-path": "^4.0.1", + "onetime": "^5.1.2", + "signal-exit": "^3.0.3", + "strip-final-newline": "^2.0.0" + } + }, + "external-editor": { + "version": "3.1.0", + "resolved": "https://registry.npmjs.org/external-editor/-/external-editor-3.1.0.tgz", + "integrity": "sha512-hMQ4CX1p1izmuLYyZqLMO/qGNw10wSv9QDCPfzXfyFrOaCSSoRfqE1Kf1s5an66J5JZC62NewG+mK49jOCtQew==", + "dev": true, + "requires": { + "chardet": "^0.7.0", + "iconv-lite": "^0.4.24", + "tmp": "^0.0.33" + } + }, + "fast-deep-equal": { + "version": "3.1.3", + "resolved": "https://registry.npmjs.org/fast-deep-equal/-/fast-deep-equal-3.1.3.tgz", + "integrity": "sha512-f3qQ9oQy9j2AhBe/H9VC91wLmKBCCU/gDOnKNAYG5hswO7BLKj09Hc5HYNz9cGI++xlpDCIgDaitVs03ATR84Q==", + "dev": true + }, + "fast-glob": { + "version": "3.2.7", + "resolved": "https://registry.npmjs.org/fast-glob/-/fast-glob-3.2.7.tgz", + "integrity": "sha512-rYGMRwip6lUMvYD3BTScMwT1HtAs2d71SMv66Vrxs0IekGZEjhM0pcMfjQPnknBt2zeCwQMEupiN02ZP4DiT1Q==", + "dev": true, + "requires": { + "@nodelib/fs.stat": "^2.0.2", + "@nodelib/fs.walk": "^1.2.3", + "glob-parent": "^5.1.2", + "merge2": "^1.3.0", + "micromatch": "^4.0.4" + }, + "dependencies": { + "glob-parent": { + "version": "5.1.2", + "resolved": "https://registry.npmjs.org/glob-parent/-/glob-parent-5.1.2.tgz", + "integrity": "sha512-AOIgSQCepiJYwP3ARnGx+5VnTu2HBYdzbGP45eLw1vr3zB3vZLeyed1sC9hnbcOc9/SrMyM5RPQrkGz4aS9Zow==", + "dev": true, + "requires": { + "is-glob": "^4.0.1" + } + } + } + }, + "fast-json-stable-stringify": { + "version": "2.1.0", + "resolved": "https://registry.npmjs.org/fast-json-stable-stringify/-/fast-json-stable-stringify-2.1.0.tgz", + "integrity": "sha512-lhd/wF+Lk98HZoTCtlVraHtfh5XYijIjalXck7saUtuanSDyLMxnHhSXEDJqHxD7msR8D0uCmqlkwjCV8xvwHw==", + "dev": true + }, + "fast-levenshtein": { + "version": "2.0.6", + "resolved": "https://registry.npmjs.org/fast-levenshtein/-/fast-levenshtein-2.0.6.tgz", + "integrity": "sha1-PYpcZog6FqMMqGQ+hR8Zuqd5eRc=", + "dev": true + }, + "fastq": { + "version": "1.13.0", + "resolved": "https://registry.npmjs.org/fastq/-/fastq-1.13.0.tgz", + "integrity": "sha512-YpkpUnK8od0o1hmeSc7UUs/eB/vIPWJYjKck2QKIzAf71Vm1AAQ3EbuZB3g2JIy+pg+ERD0vqI79KyZiB2e2Nw==", + "dev": true, + "requires": { + "reusify": "^1.0.4" + } + }, + "figures": { + "version": "3.2.0", + "resolved": "https://registry.npmjs.org/figures/-/figures-3.2.0.tgz", + "integrity": "sha512-yaduQFRKLXYOGgEn6AZau90j3ggSOyiqXU0F9JZfeXYhNa+Jk4X+s45A2zg5jns87GAFa34BBm2kXw4XpNcbdg==", + "dev": true, + "requires": { + "escape-string-regexp": "^1.0.5" + }, + "dependencies": { + "escape-string-regexp": { + "version": "1.0.5", + "resolved": "https://registry.npmjs.org/escape-string-regexp/-/escape-string-regexp-1.0.5.tgz", + "integrity": "sha1-G2HAViGQqN/2rjuyzwIAyhMLhtQ=", + "dev": true + } + } + }, + "file-entry-cache": { + "version": "6.0.1", + "resolved": "https://registry.npmjs.org/file-entry-cache/-/file-entry-cache-6.0.1.tgz", + "integrity": "sha512-7Gps/XWymbLk2QLYK4NzpMOrYjMhdIxXuIvy2QBsLE6ljuodKvdkWs/cpyJJ3CVIVpH0Oi1Hvg1ovbMzLdFBBg==", + "dev": true, + "requires": { + "flat-cache": "^3.0.4" + } + }, + "fill-range": { + "version": "7.0.1", + "resolved": "https://registry.npmjs.org/fill-range/-/fill-range-7.0.1.tgz", + "integrity": "sha512-qOo9F+dMUmC2Lcb4BbVvnKJxTPjCm+RRpe4gDuGrzkL7mEVl/djYSu2OdQ2Pa302N4oqkSg9ir6jaLWJ2USVpQ==", + "dev": true, + "requires": { + "to-regex-range": "^5.0.1" + } + }, + "filter-obj": { + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/filter-obj/-/filter-obj-1.1.0.tgz", + "integrity": "sha1-mzERErxsYSehbgFsbF1/GeCAXFs=", + "dev": true + }, + "find-cache-dir": { + "version": "3.3.2", + "resolved": "https://registry.npmjs.org/find-cache-dir/-/find-cache-dir-3.3.2.tgz", + "integrity": "sha512-wXZV5emFEjrridIgED11OoUKLxiYjAcqot/NJdAkOhlJ+vGzwhOAfcG5OX1jP+S0PcjEn8bdMJv+g2jwQ3Onig==", + "dev": true, + "requires": { + "commondir": "^1.0.1", + "make-dir": "^3.0.2", + "pkg-dir": "^4.1.0" + } + }, + "find-up": { + "version": "5.0.0", + "resolved": "https://registry.npmjs.org/find-up/-/find-up-5.0.0.tgz", + "integrity": "sha512-78/PXT1wlLLDgTzDs7sjq9hzz0vXD+zn+7wypEe4fXQxCmdmqfGsEPQxmiCSQI3ajFV91bVSsvNtrJRiW6nGng==", + "dev": true, + "requires": { + "locate-path": "^6.0.0", + "path-exists": "^4.0.0" + } + }, + "flat": { + "version": "5.0.2", + "resolved": "https://registry.npmjs.org/flat/-/flat-5.0.2.tgz", + "integrity": "sha512-b6suED+5/3rTpUBdG1gupIl8MPFCAMA0QXwmljLhvCUKcUvdE4gWky9zpuGCcXHOsz4J9wPGNWq6OKpmIzz3hQ==", + "dev": true + }, + "flat-cache": { + "version": "3.0.4", + "resolved": "https://registry.npmjs.org/flat-cache/-/flat-cache-3.0.4.tgz", + "integrity": "sha512-dm9s5Pw7Jc0GvMYbshN6zchCA9RgQlzzEZX3vylR9IqFfS8XciblUXOKfW6SiuJ0e13eDYZoZV5wdrev7P3Nwg==", + "dev": true, + "requires": { + "flatted": "^3.1.0", + "rimraf": "^3.0.2" + } + }, + "flatted": { + "version": "3.2.4", + "resolved": "https://registry.npmjs.org/flatted/-/flatted-3.2.4.tgz", + "integrity": "sha512-8/sOawo8tJ4QOBX8YlQBMxL8+RLZfxMQOif9o0KUKTNTjMYElWPE0r/m5VNFxTRd0NSw8qSy8dajrwX4RYI1Hw==", + "dev": true + }, + "foreground-child": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/foreground-child/-/foreground-child-2.0.0.tgz", + "integrity": "sha512-dCIq9FpEcyQyXKCkyzmlPTFNgrCzPudOe+mhvJU5zAtlBnGVy2yKxtfsxK2tQBThwq225jcvBjpw1Gr40uzZCA==", + "dev": true, + "requires": { + "cross-spawn": "^7.0.0", + "signal-exit": "^3.0.2" + } + }, + "form-data": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/form-data/-/form-data-4.0.0.tgz", + "integrity": "sha512-ETEklSGi5t0QMZuiXoA/Q6vcnxcLQP5vdugSpuAyi6SVGi2clPPp+xgEhuMaHC+zGgn31Kd235W35f7Hykkaww==", + "dev": true, + "requires": { + "asynckit": "^0.4.0", + "combined-stream": "^1.0.8", + "mime-types": "^2.1.12" + } + }, + "fromentries": { + "version": "1.3.2", + "resolved": "https://registry.npmjs.org/fromentries/-/fromentries-1.3.2.tgz", + "integrity": "sha512-cHEpEQHUg0f8XdtZCc2ZAhrHzKzT0MrFUTcvx+hfxYu7rGMDc5SKoXFh+n4YigxsHXRzc6OrCshdR1bWH6HHyg==", + "dev": true + }, + "fs.realpath": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/fs.realpath/-/fs.realpath-1.0.0.tgz", + "integrity": "sha1-FQStJSMVjKpA20onh8sBQRmU6k8=", + "dev": true + }, + "fsevents": { + "version": "2.3.2", + "resolved": "https://registry.npmjs.org/fsevents/-/fsevents-2.3.2.tgz", + "integrity": "sha512-xiqMQR4xAeHTuB9uWm+fFRcIOgKBMiOBP+eXiyT7jsgVCq1bkVygt00oASowB7EdtpOHaaPgKt812P9ab+DDKA==", + "dev": true, + "optional": true + }, + "function-bind": { + "version": "1.1.1", + "resolved": "https://registry.npmjs.org/function-bind/-/function-bind-1.1.1.tgz", + "integrity": "sha512-yIovAzMX49sF8Yl58fSCWJ5svSLuaibPxXQJFLmBObTuCr0Mf1KiPopGM9NiFjiYBCbfaa2Fh6breQ6ANVTI0A==", + "dev": true + }, + "functional-red-black-tree": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/functional-red-black-tree/-/functional-red-black-tree-1.0.1.tgz", + "integrity": "sha1-GwqzvVU7Kg1jmdKcDj6gslIHgyc=", + "dev": true + }, + "generic-pool": { + "version": "3.8.2", + "resolved": "https://registry.npmjs.org/generic-pool/-/generic-pool-3.8.2.tgz", + "integrity": "sha512-nGToKy6p3PAbYQ7p1UlWl6vSPwfwU6TMSWK7TTu+WUY4ZjyZQGniGGt2oNVvyNSpyZYSB43zMXVLcBm08MTMkg==" + }, + "gensync": { + "version": "1.0.0-beta.2", + "resolved": "https://registry.npmjs.org/gensync/-/gensync-1.0.0-beta.2.tgz", + "integrity": "sha512-3hN7NaskYvMDLQY55gnW3NQ+mesEAepTqlg+VEbj7zzqEMBVNhzcGYYeqFo/TlYz6eQiFcp1HcsCZO+nGgS8zg==", + "dev": true + }, + "get-caller-file": { + "version": "2.0.5", + "resolved": "https://registry.npmjs.org/get-caller-file/-/get-caller-file-2.0.5.tgz", + "integrity": "sha512-DyFP3BM/3YHTQOCUL/w0OZHR0lpKeGrxotcHWcqNEdnltqFwXVfhEBQ94eIo34AfQpo0rGki4cyIiftY06h2Fg==", + "dev": true + }, + "get-intrinsic": { + "version": "1.1.1", + "resolved": "https://registry.npmjs.org/get-intrinsic/-/get-intrinsic-1.1.1.tgz", + "integrity": "sha512-kWZrnVM42QCiEA2Ig1bG8zjoIMOgxWwYCEeNdwY6Tv/cOSeGpcoX4pXHfKUxNKVoArnrEr2e9srnAxxGIraS9Q==", + "dev": true, + "requires": { + "function-bind": "^1.1.1", + "has": "^1.0.3", + "has-symbols": "^1.0.1" + } + }, + "get-package-type": { + "version": "0.1.0", + "resolved": "https://registry.npmjs.org/get-package-type/-/get-package-type-0.1.0.tgz", + "integrity": "sha512-pjzuKtY64GYfWizNAJ0fr9VqttZkNiK2iS430LtIHzjBEr6bX8Am2zm4sW4Ro5wjWW5cAlRL1qAMTcXbjNAO2Q==", + "dev": true + }, + "get-stream": { + "version": "6.0.1", + "resolved": "https://registry.npmjs.org/get-stream/-/get-stream-6.0.1.tgz", + "integrity": "sha512-ts6Wi+2j3jQjqi70w5AlN8DFnkSwC+MqmxEzdEALB2qXZYV3X/b1CTfgPLGJNMeAWxdPfU8FO1ms3NUfaHCPYg==", + "dev": true + }, + "git-up": { + "version": "4.0.5", + "resolved": "https://registry.npmjs.org/git-up/-/git-up-4.0.5.tgz", + "integrity": "sha512-YUvVDg/vX3d0syBsk/CKUTib0srcQME0JyHkL5BaYdwLsiCslPWmDSi8PUMo9pXYjrryMcmsCoCgsTpSCJEQaA==", + "dev": true, + "requires": { + "is-ssh": "^1.3.0", + "parse-url": "^6.0.0" + } + }, + "git-url-parse": { + "version": "11.6.0", + "resolved": "https://registry.npmjs.org/git-url-parse/-/git-url-parse-11.6.0.tgz", + "integrity": "sha512-WWUxvJs5HsyHL6L08wOusa/IXYtMuCAhrMmnTjQPpBU0TTHyDhnOATNH3xNQz7YOQUsqIIPTGr4xiVti1Hsk5g==", + "dev": true, + "requires": { + "git-up": "^4.0.0" + } + }, + "glob": { + "version": "7.1.7", + "resolved": "https://registry.npmjs.org/glob/-/glob-7.1.7.tgz", + "integrity": "sha512-OvD9ENzPLbegENnYP5UUfJIirTg4+XwMWGaQfQTY0JenxNvvIKP3U3/tAQSPIu/lHxXYSZmpXlUHeqAIdKzBLQ==", + "dev": true, + "requires": { + "fs.realpath": "^1.0.0", + "inflight": "^1.0.4", + "inherits": "2", + "minimatch": "^3.0.4", + "once": "^1.3.0", + "path-is-absolute": "^1.0.0" + } + }, + "glob-parent": { + "version": "6.0.2", + "resolved": "https://registry.npmjs.org/glob-parent/-/glob-parent-6.0.2.tgz", + "integrity": "sha512-XxwI8EOhVQgWp6iDL+3b0r86f4d6AX6zSU55HfB4ydCEuXLXc5FcYeOu+nnGftS4TEju/11rt4KJPTMgbfmv4A==", + "dev": true, + "requires": { + "is-glob": "^4.0.3" + } + }, + "global-dirs": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/global-dirs/-/global-dirs-3.0.0.tgz", + "integrity": "sha512-v8ho2DS5RiCjftj1nD9NmnfaOzTdud7RRnVd9kFNOjqZbISlx5DQ+OrTkywgd0dIt7oFCvKetZSHoHcP3sDdiA==", + "dev": true, + "requires": { + "ini": "2.0.0" + } + }, + "globals": { + "version": "13.12.0", + "resolved": "https://registry.npmjs.org/globals/-/globals-13.12.0.tgz", + "integrity": "sha512-uS8X6lSKN2JumVoXrbUz+uG4BYG+eiawqm3qFcT7ammfbUHeCBoJMlHcec/S3krSk73/AE/f0szYFmgAA3kYZg==", + "dev": true, + "requires": { + "type-fest": "^0.20.2" + } + }, + "globby": { + "version": "11.0.4", + "resolved": "https://registry.npmjs.org/globby/-/globby-11.0.4.tgz", + "integrity": "sha512-9O4MVG9ioZJ08ffbcyVYyLOJLk5JQ688pJ4eMGLpdWLHq/Wr1D9BlriLQyL0E+jbkuePVZXYFj47QM/v093wHg==", + "dev": true, + "requires": { + "array-union": "^2.1.0", + "dir-glob": "^3.0.1", + "fast-glob": "^3.1.1", + "ignore": "^5.1.4", + "merge2": "^1.3.0", + "slash": "^3.0.0" + } + }, + "got": { + "version": "11.8.2", + "resolved": "https://registry.npmjs.org/got/-/got-11.8.2.tgz", + "integrity": "sha512-D0QywKgIe30ODs+fm8wMZiAcZjypcCodPNuMz5H9Mny7RJ+IjJ10BdmGW7OM7fHXP+O7r6ZwapQ/YQmMSvB0UQ==", + "dev": true, + "requires": { + "@sindresorhus/is": "^4.0.0", + "@szmarczak/http-timer": "^4.0.5", + "@types/cacheable-request": "^6.0.1", + "@types/responselike": "^1.0.0", + "cacheable-lookup": "^5.0.3", + "cacheable-request": "^7.0.1", + "decompress-response": "^6.0.0", + "http2-wrapper": "^1.0.0-beta.5.2", + "lowercase-keys": "^2.0.0", + "p-cancelable": "^2.0.0", + "responselike": "^2.0.0" + } + }, + "graceful-fs": { + "version": "4.2.8", + "resolved": "https://registry.npmjs.org/graceful-fs/-/graceful-fs-4.2.8.tgz", + "integrity": "sha512-qkIilPUYcNhJpd33n0GBXTB1MMPp14TxEsEs0pTrsSVucApsYzW5V+Q8Qxhik6KU3evy+qkAAowTByymK0avdg==", + "dev": true + }, + "growl": { + "version": "1.10.5", + "resolved": "https://registry.npmjs.org/growl/-/growl-1.10.5.tgz", + "integrity": "sha512-qBr4OuELkhPenW6goKVXiv47US3clb3/IbuWF9KNKEijAy9oeHxU9IgzjvJhHkUzhaj7rOUD7+YGWqUjLp5oSA==", + "dev": true + }, + "handlebars": { + "version": "4.7.7", + "resolved": "https://registry.npmjs.org/handlebars/-/handlebars-4.7.7.tgz", + "integrity": "sha512-aAcXm5OAfE/8IXkcZvCepKU3VzW1/39Fb5ZuqMtgI/hT8X2YgoMvBY5dLhq/cpOvw7Lk1nK/UF71aLG/ZnVYRA==", + "dev": true, + "requires": { + "minimist": "^1.2.5", + "neo-async": "^2.6.0", + "source-map": "^0.6.1", + "uglify-js": "^3.1.4", + "wordwrap": "^1.0.0" + }, + "dependencies": { + "source-map": { + "version": "0.6.1", + "resolved": "https://registry.npmjs.org/source-map/-/source-map-0.6.1.tgz", + "integrity": "sha512-UjgapumWlbMhkBgzT7Ykc5YXUT46F0iKu8SGXq0bcwP5dz/h0Plj6enJqjz1Zbq2l5WaqYnrVbwWOWMyF3F47g==", + "dev": true + } + } + }, + "has": { + "version": "1.0.3", + "resolved": "https://registry.npmjs.org/has/-/has-1.0.3.tgz", + "integrity": "sha512-f2dvO0VU6Oej7RkWJGrehjbzMAjFp5/VKPp5tTpWIV4JHHZK1/BxbFRtf/siA2SWTe09caDmVtYYzWEIbBS4zw==", + "dev": true, + "requires": { + "function-bind": "^1.1.1" + } + }, + "has-flag": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/has-flag/-/has-flag-4.0.0.tgz", + "integrity": "sha512-EykJT/Q1KjTWctppgIAgfSO0tKVuZUjhgMr17kqTumMl6Afv3EISleU7qZUzoXDFTAHTDC4NOoG/ZxU3EvlMPQ==", + "dev": true + }, + "has-symbols": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/has-symbols/-/has-symbols-1.0.2.tgz", + "integrity": "sha512-chXa79rL/UC2KlX17jo3vRGz0azaWEx5tGqZg5pO3NUyEJVB17dMruQlzCCOfUvElghKcm5194+BCRvi2Rv/Gw==", + "dev": true + }, + "has-yarn": { + "version": "2.1.0", + "resolved": "https://registry.npmjs.org/has-yarn/-/has-yarn-2.1.0.tgz", + "integrity": "sha512-UqBRqi4ju7T+TqGNdqAO0PaSVGsDGJUBQvk9eUWNGRY1CFGDzYhLWoM7JQEemnlvVcv/YEmc2wNW8BC24EnUsw==", + "dev": true + }, + "hasha": { + "version": "5.2.2", + "resolved": "https://registry.npmjs.org/hasha/-/hasha-5.2.2.tgz", + "integrity": "sha512-Hrp5vIK/xr5SkeN2onO32H0MgNZ0f17HRNH39WfL0SYUNOTZ5Lz1TJ8Pajo/87dYGEFlLMm7mIc/k/s6Bvz9HQ==", + "dev": true, + "requires": { + "is-stream": "^2.0.0", + "type-fest": "^0.8.0" + }, + "dependencies": { + "type-fest": { + "version": "0.8.1", + "resolved": "https://registry.npmjs.org/type-fest/-/type-fest-0.8.1.tgz", + "integrity": "sha512-4dbzIzqvjtgiM5rw1k5rEHtBANKmdudhGyBEajN01fEyhaAIhsoKNy6y7+IN93IfpFtwY9iqi7kD+xwKhQsNJA==", + "dev": true + } + } + }, + "he": { + "version": "1.2.0", + "resolved": "https://registry.npmjs.org/he/-/he-1.2.0.tgz", + "integrity": "sha512-F/1DnUGPopORZi0ni+CvrCgHQ5FyEAHRLSApuYWMmrbSwoN2Mn/7k+Gl38gJnR7yyDZk6WLXwiGod1JOWNDKGw==", + "dev": true + }, + "html-escaper": { + "version": "2.0.2", + "resolved": "https://registry.npmjs.org/html-escaper/-/html-escaper-2.0.2.tgz", + "integrity": "sha512-H2iMtd0I4Mt5eYiapRdIDjp+XzelXQ0tFE4JS7YFwFevXXMmOp9myNrUvCg0D6ws8iqkRPBfKHgbwig1SmlLfg==", + "dev": true + }, + "http-cache-semantics": { + "version": "4.1.0", + "resolved": "https://registry.npmjs.org/http-cache-semantics/-/http-cache-semantics-4.1.0.tgz", + "integrity": "sha512-carPklcUh7ROWRK7Cv27RPtdhYhUsela/ue5/jKzjegVvXDqM2ILE9Q2BGn9JZJh1g87cp56su/FgQSzcWS8cQ==", + "dev": true + }, + "http2-wrapper": { + "version": "1.0.3", + "resolved": "https://registry.npmjs.org/http2-wrapper/-/http2-wrapper-1.0.3.tgz", + "integrity": "sha512-V+23sDMr12Wnz7iTcDeJr3O6AIxlnvT/bmaAAAP/Xda35C90p9599p0F1eHR/N1KILWSoWVAiOMFjBBXaXSMxg==", + "dev": true, + "requires": { + "quick-lru": "^5.1.1", + "resolve-alpn": "^1.0.0" + } + }, + "human-signals": { + "version": "2.1.0", + "resolved": "https://registry.npmjs.org/human-signals/-/human-signals-2.1.0.tgz", + "integrity": "sha512-B4FFZ6q/T2jhhksgkbEW3HBvWIfDW85snkQgawt07S7J5QXTk6BkNV+0yAeZrM5QpMAdYlocGoljn0sJ/WQkFw==", + "dev": true + }, + "iconv-lite": { + "version": "0.4.24", + "resolved": "https://registry.npmjs.org/iconv-lite/-/iconv-lite-0.4.24.tgz", + "integrity": "sha512-v3MXnZAcvnywkTUEZomIActle7RXXeedOR31wwl7VlyoXO4Qi9arvSenNQWne1TcRwhCL1HwLI21bEqdpj8/rA==", + "dev": true, + "requires": { + "safer-buffer": ">= 2.1.2 < 3" + } + }, + "ieee754": { + "version": "1.2.1", + "resolved": "https://registry.npmjs.org/ieee754/-/ieee754-1.2.1.tgz", + "integrity": "sha512-dcyqhDvX1C46lXZcVqCpK+FtMRQVdIMN6/Df5js2zouUsqG7I6sFxitIC+7KYK29KdXOLHdu9zL4sFnoVQnqaA==", + "dev": true + }, + "ignore": { + "version": "5.1.9", + "resolved": "https://registry.npmjs.org/ignore/-/ignore-5.1.9.tgz", + "integrity": "sha512-2zeMQpbKz5dhZ9IwL0gbxSW5w0NK/MSAMtNuhgIHEPmaU3vPdKPL0UdvUCXs5SS4JAwsBxysK5sFMW8ocFiVjQ==", + "dev": true + }, + "import-cwd": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/import-cwd/-/import-cwd-3.0.0.tgz", + "integrity": "sha512-4pnzH16plW+hgvRECbDWpQl3cqtvSofHWh44met7ESfZ8UZOWWddm8hEyDTqREJ9RbYHY8gi8DqmaelApoOGMg==", + "dev": true, + "requires": { + "import-from": "^3.0.0" + } + }, + "import-fresh": { + "version": "3.3.0", + "resolved": "https://registry.npmjs.org/import-fresh/-/import-fresh-3.3.0.tgz", + "integrity": "sha512-veYYhQa+D1QBKznvhUHxb8faxlrwUnxseDAbAp457E0wLNio2bOSKnjYDhMj+YiAq61xrMGhQk9iXVk5FzgQMw==", + "dev": true, + "requires": { + "parent-module": "^1.0.0", + "resolve-from": "^4.0.0" + } + }, + "import-from": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/import-from/-/import-from-3.0.0.tgz", + "integrity": "sha512-CiuXOFFSzkU5x/CR0+z7T91Iht4CXgfCxVOFRhh2Zyhg5wOpWvvDLQUsWl+gcN+QscYBjez8hDCt85O7RLDttQ==", + "dev": true, + "requires": { + "resolve-from": "^5.0.0" + }, + "dependencies": { + "resolve-from": { + "version": "5.0.0", + "resolved": "https://registry.npmjs.org/resolve-from/-/resolve-from-5.0.0.tgz", + "integrity": "sha512-qYg9KP24dD5qka9J47d0aVky0N+b4fTU89LN9iDnjB5waksiC49rvMB0PrUJQGoTmH50XPiqOvAjDfaijGxYZw==", + "dev": true + } + } + }, + "import-lazy": { + "version": "2.1.0", + "resolved": "https://registry.npmjs.org/import-lazy/-/import-lazy-2.1.0.tgz", + "integrity": "sha1-BWmOPUXIjo1+nZLLBYTnfwlvPkM=", + "dev": true + }, + "imurmurhash": { + "version": "0.1.4", + "resolved": "https://registry.npmjs.org/imurmurhash/-/imurmurhash-0.1.4.tgz", + "integrity": "sha1-khi5srkoojixPcT7a21XbyMUU+o=", + "dev": true + }, + "indent-string": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/indent-string/-/indent-string-4.0.0.tgz", + "integrity": "sha512-EdDDZu4A2OyIK7Lr/2zG+w5jmbuk1DVBnEwREQvBzspBJkCEbRa8GxU1lghYcaGJCnRWibjDXlq779X1/y5xwg==", + "dev": true + }, + "inflight": { + "version": "1.0.6", + "resolved": "https://registry.npmjs.org/inflight/-/inflight-1.0.6.tgz", + "integrity": "sha1-Sb1jMdfQLQwJvJEKEHW6gWW1bfk=", + "dev": true, + "requires": { + "once": "^1.3.0", + "wrappy": "1" + } + }, + "inherits": { + "version": "2.0.4", + "resolved": "https://registry.npmjs.org/inherits/-/inherits-2.0.4.tgz", + "integrity": "sha512-k/vGaX4/Yla3WzyMCvTQOXYeIHvqOKtnqBduzTHpzpQZzAskKMhZ2K+EnBiSM9zGSoIFeMpXKxa4dYeZIQqewQ==", + "dev": true + }, + "ini": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/ini/-/ini-2.0.0.tgz", + "integrity": "sha512-7PnF4oN3CvZF23ADhA5wRaYEQpJ8qygSkbtTXWBeXWXmEVRXK+1ITciHWwHhsjv1TmW0MgacIv6hEi5pX5NQdA==", + "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==", + "dev": true, + "requires": { + "ansi-escapes": "^4.2.1", + "chalk": "^4.1.1", + "cli-cursor": "^3.1.0", + "cli-width": "^3.0.0", + "external-editor": "^3.0.3", + "figures": "^3.0.0", + "lodash": "^4.17.21", + "mute-stream": "0.0.8", + "ora": "^5.4.1", + "run-async": "^2.4.0", + "rxjs": "^7.2.0", + "string-width": "^4.1.0", + "strip-ansi": "^6.0.0", + "through": "^2.3.6" + } + }, + "interpret": { + "version": "1.4.0", + "resolved": "https://registry.npmjs.org/interpret/-/interpret-1.4.0.tgz", + "integrity": "sha512-agE4QfB2Lkp9uICn7BAqoscw4SZP9kTE2hxiFI3jBPmXJfdqiahTbUuKGsMoN2GtqL9AxhYioAcVvgsb1HvRbA==", + "dev": true + }, + "is-arrayish": { + "version": "0.2.1", + "resolved": "https://registry.npmjs.org/is-arrayish/-/is-arrayish-0.2.1.tgz", + "integrity": "sha1-d8mYQFJ6qOyxqLppe4BkWnqSap0=", + "dev": true + }, + "is-binary-path": { + "version": "2.1.0", + "resolved": "https://registry.npmjs.org/is-binary-path/-/is-binary-path-2.1.0.tgz", + "integrity": "sha512-ZMERYes6pDydyuGidse7OsHxtbI7WVeUEozgR/g7rd0xUimYNlvZRE/K2MgZTjWy725IfelLeVcEM97mmtRGXw==", + "dev": true, + "requires": { + "binary-extensions": "^2.0.0" + } + }, + "is-ci": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/is-ci/-/is-ci-3.0.0.tgz", + "integrity": "sha512-kDXyttuLeslKAHYL/K28F2YkM3x5jvFPEw3yXbRptXydjD9rpLEz+C5K5iutY9ZiUu6AP41JdvRQwF4Iqs4ZCQ==", + "dev": true, + "requires": { + "ci-info": "^3.1.1" + } + }, + "is-core-module": { + "version": "2.8.0", + "resolved": "https://registry.npmjs.org/is-core-module/-/is-core-module-2.8.0.tgz", + "integrity": "sha512-vd15qHsaqrRL7dtH6QNuy0ndJmRDrS9HAM1CAiSifNUFv4x1a0CCVsj18hJ1mShxIG6T2i1sO78MkP56r0nYRw==", + "dev": true, + "requires": { + "has": "^1.0.3" + } + }, + "is-docker": { + "version": "2.2.1", + "resolved": "https://registry.npmjs.org/is-docker/-/is-docker-2.2.1.tgz", + "integrity": "sha512-F+i2BKsFrH66iaUFc0woD8sLy8getkwTwtOBjvs56Cx4CgJDeKQeqfz8wAYiSb8JOprWhHH5p77PbmYCvvUuXQ==", + "dev": true + }, + "is-extglob": { + "version": "2.1.1", + "resolved": "https://registry.npmjs.org/is-extglob/-/is-extglob-2.1.1.tgz", + "integrity": "sha1-qIwCU1eR8C7TfHahueqXc8gz+MI=", + "dev": true + }, + "is-fullwidth-code-point": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/is-fullwidth-code-point/-/is-fullwidth-code-point-3.0.0.tgz", + "integrity": "sha512-zymm5+u+sCsSWyD9qNaejV3DFvhCKclKdizYaJUuHA83RLjb7nSuGnddCHGv0hk+KY7BMAlsWeK4Ueg6EV6XQg==", + "dev": true + }, + "is-glob": { + "version": "4.0.3", + "resolved": "https://registry.npmjs.org/is-glob/-/is-glob-4.0.3.tgz", + "integrity": "sha512-xelSayHH36ZgE7ZWhli7pW34hNbNl8Ojv5KVmkJD4hBdD3th8Tfk9vYasLM+mXWOZhFkgZfxhLSnrwRr4elSSg==", + "dev": true, + "requires": { + "is-extglob": "^2.1.1" + } + }, + "is-installed-globally": { + "version": "0.4.0", + "resolved": "https://registry.npmjs.org/is-installed-globally/-/is-installed-globally-0.4.0.tgz", + "integrity": "sha512-iwGqO3J21aaSkC7jWnHP/difazwS7SFeIqxv6wEtLU8Y5KlzFTjyqcSIT0d8s4+dDhKytsk9PJZ2BkS5eZwQRQ==", + "dev": true, + "requires": { + "global-dirs": "^3.0.0", + "is-path-inside": "^3.0.2" + } + }, + "is-interactive": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/is-interactive/-/is-interactive-1.0.0.tgz", + "integrity": "sha512-2HvIEKRoqS62guEC+qBjpvRubdX910WCMuJTZ+I9yvqKU2/12eSL549HMwtabb4oupdj2sMP50k+XJfB/8JE6w==", + "dev": true + }, + "is-npm": { + "version": "5.0.0", + "resolved": "https://registry.npmjs.org/is-npm/-/is-npm-5.0.0.tgz", + "integrity": "sha512-WW/rQLOazUq+ST/bCAVBp/2oMERWLsR7OrKyt052dNDk4DHcDE0/7QSXITlmi+VBcV13DfIbysG3tZJm5RfdBA==", + "dev": true + }, + "is-number": { + "version": "7.0.0", + "resolved": "https://registry.npmjs.org/is-number/-/is-number-7.0.0.tgz", + "integrity": "sha512-41Cifkg6e8TylSpdtTpeLVMqvSBEVzTttHvERD741+pnZ8ANv0004MRL43QKPDlK9cGvNp6NZWZUBlbGXYxxng==", + "dev": true + }, + "is-obj": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/is-obj/-/is-obj-2.0.0.tgz", + "integrity": "sha512-drqDG3cbczxxEJRoOXcOjtdp1J/lyp1mNn0xaznRs8+muBhgQcrnbspox5X5fOw0HnMnbfDzvnEMEtqDEJEo8w==", + "dev": true + }, + "is-path-inside": { + "version": "3.0.3", + "resolved": "https://registry.npmjs.org/is-path-inside/-/is-path-inside-3.0.3.tgz", + "integrity": "sha512-Fd4gABb+ycGAmKou8eMftCupSir5lRxqf4aD/vd0cD2qc4HL07OjCeuHMr8Ro4CoMaeCKDB0/ECBOVWjTwUvPQ==", + "dev": true + }, + "is-plain-obj": { + "version": "2.1.0", + "resolved": "https://registry.npmjs.org/is-plain-obj/-/is-plain-obj-2.1.0.tgz", + "integrity": "sha512-YWnfyRwxL/+SsrWYfOpUtz5b3YD+nyfkHvjbcanzk8zgyO4ASD67uVMRt8k5bM4lLMDnXfriRhOpemw+NfT1eA==", + "dev": true + }, + "is-plain-object": { + "version": "5.0.0", + "resolved": "https://registry.npmjs.org/is-plain-object/-/is-plain-object-5.0.0.tgz", + "integrity": "sha512-VRSzKkbMm5jMDoKLbltAkFQ5Qr7VDiTFGXxYFXXowVj387GeGNOCsOH6Msy00SGZ3Fp84b1Naa1psqgcCIEP5Q==", + "dev": true + }, + "is-ssh": { + "version": "1.3.3", + "resolved": "https://registry.npmjs.org/is-ssh/-/is-ssh-1.3.3.tgz", + "integrity": "sha512-NKzJmQzJfEEma3w5cJNcUMxoXfDjz0Zj0eyCalHn2E6VOwlzjZo0yuO2fcBSf8zhFuVCL/82/r5gRcoi6aEPVQ==", + "dev": true, + "requires": { + "protocols": "^1.1.0" + } + }, + "is-stream": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/is-stream/-/is-stream-2.0.1.tgz", + "integrity": "sha512-hFoiJiTl63nn+kstHGBtewWSKnQLpyb155KHheA1l39uvtO9nWIop1p3udqPcUd/xbF1VLMO4n7OI6p7RbngDg==", + "dev": true + }, + "is-typedarray": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/is-typedarray/-/is-typedarray-1.0.0.tgz", + "integrity": "sha1-5HnICFjfDBsR3dppQPlgEfzaSpo=", + "dev": true + }, + "is-unicode-supported": { + "version": "0.1.0", + "resolved": "https://registry.npmjs.org/is-unicode-supported/-/is-unicode-supported-0.1.0.tgz", + "integrity": "sha512-knxG2q4UC3u8stRGyAVJCOdxFmv5DZiRcdlIaAQXAbSfJya+OhopNotLQrstBhququ4ZpuKbDc/8S6mgXgPFPw==", + "dev": true + }, + "is-windows": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/is-windows/-/is-windows-1.0.2.tgz", + "integrity": "sha512-eXK1UInq2bPmjyX6e3VHIzMLobc4J94i4AWn+Hpq3OU5KkrRC96OAcR3PRJ/pGu6m8TRnBHP9dkXQVsT/COVIA==", + "dev": true + }, + "is-wsl": { + "version": "2.2.0", + "resolved": "https://registry.npmjs.org/is-wsl/-/is-wsl-2.2.0.tgz", + "integrity": "sha512-fKzAra0rGJUUBwGBgNkHZuToZcn+TtXHpeCgmkMJMMYx1sQDYaCSyjJBSCa2nH1DGm7s3n1oBnohoVTBaN7Lww==", + "dev": true, + "requires": { + "is-docker": "^2.0.0" + } + }, + "is-yarn-global": { + "version": "0.3.0", + "resolved": "https://registry.npmjs.org/is-yarn-global/-/is-yarn-global-0.3.0.tgz", + "integrity": "sha512-VjSeb/lHmkoyd8ryPVIKvOCn4D1koMqY+vqyjjUfc3xyKtP4dYOxM44sZrnqQSzSds3xyOrUTLTC9LVCVgLngw==", + "dev": true + }, + "isarray": { + "version": "0.0.1", + "resolved": "https://registry.npmjs.org/isarray/-/isarray-0.0.1.tgz", + "integrity": "sha1-ihis/Kmo9Bd+Cav8YDiTmwXR7t8=", + "dev": true + }, + "isexe": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/isexe/-/isexe-2.0.0.tgz", + "integrity": "sha1-6PvzdNxVb/iUehDcsFctYz8s+hA=", + "dev": true + }, + "istanbul-lib-coverage": { + "version": "3.2.0", + "resolved": "https://registry.npmjs.org/istanbul-lib-coverage/-/istanbul-lib-coverage-3.2.0.tgz", + "integrity": "sha512-eOeJ5BHCmHYvQK7xt9GkdHuzuCGS1Y6g9Gvnx3Ym33fz/HpLRYxiS0wHNr+m/MBC8B647Xt608vCDEvhl9c6Mw==", + "dev": true + }, + "istanbul-lib-hook": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/istanbul-lib-hook/-/istanbul-lib-hook-3.0.0.tgz", + "integrity": "sha512-Pt/uge1Q9s+5VAZ+pCo16TYMWPBIl+oaNIjgLQxcX0itS6ueeaA+pEfThZpH8WxhFgCiEb8sAJY6MdUKgiIWaQ==", + "dev": true, + "requires": { + "append-transform": "^2.0.0" + } + }, + "istanbul-lib-instrument": { + "version": "4.0.3", + "resolved": "https://registry.npmjs.org/istanbul-lib-instrument/-/istanbul-lib-instrument-4.0.3.tgz", + "integrity": "sha512-BXgQl9kf4WTCPCCpmFGoJkz/+uhvm7h7PFKUYxh7qarQd3ER33vHG//qaE8eN25l07YqZPpHXU9I09l/RD5aGQ==", + "dev": true, + "requires": { + "@babel/core": "^7.7.5", + "@istanbuljs/schema": "^0.1.2", + "istanbul-lib-coverage": "^3.0.0", + "semver": "^6.3.0" + }, + "dependencies": { + "semver": { + "version": "6.3.0", + "resolved": "https://registry.npmjs.org/semver/-/semver-6.3.0.tgz", + "integrity": "sha512-b39TBaTSfV6yBrapU89p5fKekE2m/NwnDocOVruQFS1/veMgdzuPcnOM34M6CwxW8jH/lxEa5rBoDeUwu5HHTw==", + "dev": true + } + } + }, + "istanbul-lib-processinfo": { + "version": "2.0.2", + "resolved": "https://registry.npmjs.org/istanbul-lib-processinfo/-/istanbul-lib-processinfo-2.0.2.tgz", + "integrity": "sha512-kOwpa7z9hme+IBPZMzQ5vdQj8srYgAtaRqeI48NGmAQ+/5yKiHLV0QbYqQpxsdEF0+w14SoB8YbnHKcXE2KnYw==", + "dev": true, + "requires": { + "archy": "^1.0.0", + "cross-spawn": "^7.0.0", + "istanbul-lib-coverage": "^3.0.0-alpha.1", + "make-dir": "^3.0.0", + "p-map": "^3.0.0", + "rimraf": "^3.0.0", + "uuid": "^3.3.3" + } + }, + "istanbul-lib-report": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/istanbul-lib-report/-/istanbul-lib-report-3.0.0.tgz", + "integrity": "sha512-wcdi+uAKzfiGT2abPpKZ0hSU1rGQjUQnLvtY5MpQ7QCTahD3VODhcu4wcfY1YtkGaDD5yuydOLINXsfbus9ROw==", + "dev": true, + "requires": { + "istanbul-lib-coverage": "^3.0.0", + "make-dir": "^3.0.0", + "supports-color": "^7.1.0" + } + }, + "istanbul-lib-source-maps": { + "version": "4.0.1", + "resolved": "https://registry.npmjs.org/istanbul-lib-source-maps/-/istanbul-lib-source-maps-4.0.1.tgz", + "integrity": "sha512-n3s8EwkdFIJCG3BPKBYvskgXGoy88ARzvegkitk60NxRdwltLOTaH7CUiMRXvwYorl0Q712iEjcWB+fK/MrWVw==", + "dev": true, + "requires": { + "debug": "^4.1.1", + "istanbul-lib-coverage": "^3.0.0", + "source-map": "^0.6.1" + }, + "dependencies": { + "source-map": { + "version": "0.6.1", + "resolved": "https://registry.npmjs.org/source-map/-/source-map-0.6.1.tgz", + "integrity": "sha512-UjgapumWlbMhkBgzT7Ykc5YXUT46F0iKu8SGXq0bcwP5dz/h0Plj6enJqjz1Zbq2l5WaqYnrVbwWOWMyF3F47g==", + "dev": true + } + } + }, + "istanbul-reports": { + "version": "3.0.5", + "resolved": "https://registry.npmjs.org/istanbul-reports/-/istanbul-reports-3.0.5.tgz", + "integrity": "sha512-5+19PlhnGabNWB7kOFnuxT8H3T/iIyQzIbQMxXsURmmvKg86P2sbkrGOT77VnHw0Qr0gc2XzRaRfMZYYbSQCJQ==", + "dev": true, + "requires": { + "html-escaper": "^2.0.0", + "istanbul-lib-report": "^3.0.0" + } + }, + "js-tokens": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/js-tokens/-/js-tokens-4.0.0.tgz", + "integrity": "sha512-RdJUflcE3cUzKiMqQgsCu06FPu9UdIJO0beYbPhHN4k6apgJtifcoCtT9bcxOpYBtpD2kCM6Sbzg4CausW/PKQ==", + "dev": true + }, + "js-yaml": { + "version": "4.1.0", + "resolved": "https://registry.npmjs.org/js-yaml/-/js-yaml-4.1.0.tgz", + "integrity": "sha512-wpxZs9NoxZaJESJGIZTyDEaYpl0FKSA+FB9aJiyemKhMwkxQg63h4T1KJgUGHpTqPDNRcmmYLugrRjJlBtWvRA==", + "dev": true, + "requires": { + "argparse": "^2.0.1" + } + }, + "jsesc": { + "version": "2.5.2", + "resolved": "https://registry.npmjs.org/jsesc/-/jsesc-2.5.2.tgz", + "integrity": "sha512-OYu7XEzjkCQ3C5Ps3QIZsQfNpqoJyZZA99wd9aWd05NCtC5pWOkShK2mkL6HXQR6/Cy2lbNdPlZBpuQHXE63gA==", + "dev": true + }, + "json-buffer": { + "version": "3.0.1", + "resolved": "https://registry.npmjs.org/json-buffer/-/json-buffer-3.0.1.tgz", + "integrity": "sha512-4bV5BfR2mqfQTJm+V5tPPdf+ZpuhiIvTuAB5g8kcrXOZpTT/QwwVRWBywX1ozr6lEuPdbHxwaJlm9G6mI2sfSQ==", + "dev": true + }, + "json-parse-even-better-errors": { + "version": "2.3.1", + "resolved": "https://registry.npmjs.org/json-parse-even-better-errors/-/json-parse-even-better-errors-2.3.1.tgz", + "integrity": "sha512-xyFwyhro/JEof6Ghe2iz2NcXoj2sloNsWr/XsERDK/oiPCfaNhl5ONfp+jQdAZRQQ0IJWNzH9zIZF7li91kh2w==", + "dev": true + }, + "json-schema-traverse": { + "version": "0.4.1", + "resolved": "https://registry.npmjs.org/json-schema-traverse/-/json-schema-traverse-0.4.1.tgz", + "integrity": "sha512-xbbCH5dCYU5T8LcEhhuh7HJ88HXuW3qsI3Y0zOZFKfZEHcpWiHU/Jxzk629Brsab/mMiHQti9wMP+845RPe3Vg==", + "dev": true + }, + "json-stable-stringify-without-jsonify": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/json-stable-stringify-without-jsonify/-/json-stable-stringify-without-jsonify-1.0.1.tgz", + "integrity": "sha1-nbe1lJatPzz+8wp1FC0tkwrXJlE=", + "dev": true + }, + "json5": { + "version": "2.2.0", + "resolved": "https://registry.npmjs.org/json5/-/json5-2.2.0.tgz", + "integrity": "sha512-f+8cldu7X/y7RAJurMEJmdoKXGB/X550w2Nr3tTbezL6RwEE/iMcm+tZnXeoZtKuOq6ft8+CqzEkrIgx1fPoQA==", + "dev": true, + "requires": { + "minimist": "^1.2.5" + } + }, + "jsonc-parser": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/jsonc-parser/-/jsonc-parser-3.0.0.tgz", + "integrity": "sha512-fQzRfAbIBnR0IQvftw9FJveWiHp72Fg20giDrHz6TdfB12UH/uue0D3hm57UB5KgAVuniLMCaS8P1IMj9NR7cA==", + "dev": true + }, + "just-extend": { + "version": "4.2.1", + "resolved": "https://registry.npmjs.org/just-extend/-/just-extend-4.2.1.tgz", + "integrity": "sha512-g3UB796vUFIY90VIv/WX3L2c8CS2MdWUww3CNrYmqza1Fg0DURc2K/O4YrnklBdQarSJ/y8JnJYDGc+1iumQjg==", + "dev": true + }, + "keyv": { + "version": "4.0.4", + "resolved": "https://registry.npmjs.org/keyv/-/keyv-4.0.4.tgz", + "integrity": "sha512-vqNHbAc8BBsxk+7QBYLW0Y219rWcClspR6WSeoHYKG5mnsSoOH+BL1pWq02DDCVdvvuUny5rkBlzMRzoqc+GIg==", + "dev": true, + "requires": { + "json-buffer": "3.0.1" + } + }, + "latest-version": { + "version": "5.1.0", + "resolved": "https://registry.npmjs.org/latest-version/-/latest-version-5.1.0.tgz", + "integrity": "sha512-weT+r0kTkRQdCdYCNtkMwWXQTMEswKrFBkm4ckQOMVhhqhIMI1UT2hMj+1iigIhgSZm5gTmrRXBNoGUgaTY1xA==", + "dev": true, + "requires": { + "package-json": "^6.3.0" + } + }, + "levn": { + "version": "0.4.1", + "resolved": "https://registry.npmjs.org/levn/-/levn-0.4.1.tgz", + "integrity": "sha512-+bT2uH4E5LGE7h/n3evcS/sQlJXCpIp6ym8OWJ5eV6+67Dsql/LaaT7qJBAt2rzfoa/5QBGBhxDix1dMt2kQKQ==", + "dev": true, + "requires": { + "prelude-ls": "^1.2.1", + "type-check": "~0.4.0" + } + }, + "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=", + "dev": true + }, + "locate-path": { + "version": "6.0.0", + "resolved": "https://registry.npmjs.org/locate-path/-/locate-path-6.0.0.tgz", + "integrity": "sha512-iPZK6eYjbxRu3uB4/WZ3EsEIMJFMqAoopl3R+zuq0UjcAm/MO6KCweDgPfP3elTztoKP3KtnVHxTn2NHBSDVUw==", + "dev": true, + "requires": { + "p-locate": "^5.0.0" + } + }, + "lodash": { + "version": "4.17.21", + "resolved": "https://registry.npmjs.org/lodash/-/lodash-4.17.21.tgz", + "integrity": "sha512-v2kDEe57lecTulaDIuNTPy3Ry4gLGJ6Z1O3vE1krgXZNrsQ+LFTGHVxVjcXPs17LhbZVGedAJv8XZ1tvj5FvSg==", + "dev": true + }, + "lodash.flattendeep": { + "version": "4.4.0", + "resolved": "https://registry.npmjs.org/lodash.flattendeep/-/lodash.flattendeep-4.4.0.tgz", + "integrity": "sha1-+wMJF/hqMTTlvJvsDWngAT3f7bI=", + "dev": true + }, + "lodash.get": { + "version": "4.4.2", + "resolved": "https://registry.npmjs.org/lodash.get/-/lodash.get-4.4.2.tgz", + "integrity": "sha1-LRd/ZS+jHpObRDjVNBSZ36OCXpk=", + "dev": true + }, + "lodash.merge": { + "version": "4.6.2", + "resolved": "https://registry.npmjs.org/lodash.merge/-/lodash.merge-4.6.2.tgz", + "integrity": "sha512-0KpjqXRVvrYyCsX1swR/XTK0va6VQkQM6MNo7PqW77ByjAhoARA8EfrP1N4+KlKj8YS0ZUCtRT/YUuhyYDujIQ==", + "dev": true + }, + "log-symbols": { + "version": "4.1.0", + "resolved": "https://registry.npmjs.org/log-symbols/-/log-symbols-4.1.0.tgz", + "integrity": "sha512-8XPvpAA8uyhfteu8pIvQxpJZ7SYYdpUivZpGy6sFsBuKRY/7rQGavedeB8aK+Zkyq6upMFVL/9AW6vOYzfRyLg==", + "dev": true, + "requires": { + "chalk": "^4.1.0", + "is-unicode-supported": "^0.1.0" + } + }, + "lowercase-keys": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/lowercase-keys/-/lowercase-keys-2.0.0.tgz", + "integrity": "sha512-tqNXrS78oMOE73NMxK4EMLQsQowWf8jKooH9g7xPavRT706R6bkQJ6DY2Te7QukaZsulxa30wQ7bk0pm4XiHmA==", + "dev": true + }, + "lru-cache": { + "version": "6.0.0", + "resolved": "https://registry.npmjs.org/lru-cache/-/lru-cache-6.0.0.tgz", + "integrity": "sha512-Jo6dJ04CmSjuznwJSS3pUeWmd/H0ffTlkXXgwZi+eq1UCmqQwCh+eLsYOYCwY991i2Fah4h1BEMCx4qThGbsiA==", + "dev": true, + "requires": { + "yallist": "^4.0.0" + } + }, + "lunr": { + "version": "2.3.9", + "resolved": "https://registry.npmjs.org/lunr/-/lunr-2.3.9.tgz", + "integrity": "sha512-zTU3DaZaF3Rt9rhN3uBMGQD3dD2/vFQqnvZCDv4dl5iOzq2IZQqTxu90r4E5J+nP70J3ilqVCrbho2eWaeW8Ow==", + "dev": true + }, + "macos-release": { + "version": "2.5.0", + "resolved": "https://registry.npmjs.org/macos-release/-/macos-release-2.5.0.tgz", + "integrity": "sha512-EIgv+QZ9r+814gjJj0Bt5vSLJLzswGmSUbUpbi9AIr/fsN2IWFBl2NucV9PAiek+U1STK468tEkxmVYUtuAN3g==", + "dev": true + }, + "make-dir": { + "version": "3.1.0", + "resolved": "https://registry.npmjs.org/make-dir/-/make-dir-3.1.0.tgz", + "integrity": "sha512-g3FeP20LNwhALb/6Cz6Dd4F2ngze0jz7tbzrD2wAV+o9FeNHe4rL+yK2md0J/fiSf1sa1ADhXqi5+oVwOM/eGw==", + "dev": true, + "requires": { + "semver": "^6.0.0" + }, + "dependencies": { + "semver": { + "version": "6.3.0", + "resolved": "https://registry.npmjs.org/semver/-/semver-6.3.0.tgz", + "integrity": "sha512-b39TBaTSfV6yBrapU89p5fKekE2m/NwnDocOVruQFS1/veMgdzuPcnOM34M6CwxW8jH/lxEa5rBoDeUwu5HHTw==", + "dev": true + } + } + }, + "make-error": { + "version": "1.3.6", + "resolved": "https://registry.npmjs.org/make-error/-/make-error-1.3.6.tgz", + "integrity": "sha512-s8UhlNe7vPKomQhC1qFelMokr/Sc3AgNbso3n74mVPA5LTZwkB9NlXf4XPamLxJE8h0gh73rM94xvwRT2CVInw==", + "dev": true + }, + "marked": { + "version": "3.0.8", + "resolved": "https://registry.npmjs.org/marked/-/marked-3.0.8.tgz", + "integrity": "sha512-0gVrAjo5m0VZSJb4rpL59K1unJAMb/hm8HRXqasD8VeC8m91ytDPMritgFSlKonfdt+rRYYpP/JfLxgIX8yoSw==", + "dev": true + }, + "merge-stream": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/merge-stream/-/merge-stream-2.0.0.tgz", + "integrity": "sha512-abv/qOcuPfk3URPfDzmZU1LKmuw8kT+0nIHvKrKgFrwifol/doWcdA4ZqsWQ8ENrFKkd67Mfpo/LovbIUsbt3w==", + "dev": true + }, + "merge2": { + "version": "1.4.1", + "resolved": "https://registry.npmjs.org/merge2/-/merge2-1.4.1.tgz", + "integrity": "sha512-8q7VEgMJW4J8tcfVPy8g09NcQwZdbwFEqhe/WZkoIzjn/3TGDwtOCYtXGxA3O8tPzpczCCDgv+P2P5y00ZJOOg==", + "dev": true + }, + "micromatch": { + "version": "4.0.4", + "resolved": "https://registry.npmjs.org/micromatch/-/micromatch-4.0.4.tgz", + "integrity": "sha512-pRmzw/XUcwXGpD9aI9q/0XOwLNygjETJ8y0ao0wdqprrzDa4YnxLcz7fQRZr8voh8V10kGhABbNcHVk5wHgWwg==", + "dev": true, + "requires": { + "braces": "^3.0.1", + "picomatch": "^2.2.3" + } + }, + "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==", + "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==", + "dev": true, + "requires": { + "mime-db": "1.49.0" + } + }, + "mimic-fn": { + "version": "2.1.0", + "resolved": "https://registry.npmjs.org/mimic-fn/-/mimic-fn-2.1.0.tgz", + "integrity": "sha512-OqbOk5oEQeAZ8WXWydlu9HJjz9WVdEIvamMCcXmuqUYjTknH/sqsWvhQ3vgwKFRR1HpjvNBKQ37nbJgYzGqGcg==", + "dev": true + }, + "mimic-response": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/mimic-response/-/mimic-response-1.0.1.tgz", + "integrity": "sha512-j5EctnkH7amfV/q5Hgmoal1g2QHFJRraOtmx0JpIqkxhBhI/lJSl1nMpQ45hVarwNETOoWEimndZ4QK0RHxuxQ==", + "dev": true + }, + "minimatch": { + "version": "3.0.4", + "resolved": "https://registry.npmjs.org/minimatch/-/minimatch-3.0.4.tgz", + "integrity": "sha512-yJHVQEhyqPLUTgt9B83PXu6W3rx4MvvHvSUvToogpwoGDOUQ+yDrR0HRot+yOCdCO7u4hX3pWft6kWBBcqh0UA==", + "dev": true, + "requires": { + "brace-expansion": "^1.1.7" + } + }, + "minimist": { + "version": "1.2.5", + "resolved": "https://registry.npmjs.org/minimist/-/minimist-1.2.5.tgz", + "integrity": "sha512-FM9nNUYrRBAELZQT3xeZQ7fmMOBg6nWNmJKTcgsJeaLstP/UODVpGsr5OhXhhXg6f+qtJ8uiZ+PUxkDWcgIXLw==", + "dev": true + }, + "mocha": { + "version": "9.1.3", + "resolved": "https://registry.npmjs.org/mocha/-/mocha-9.1.3.tgz", + "integrity": "sha512-Xcpl9FqXOAYqI3j79pEtHBBnQgVXIhpULjGQa7DVb0Po+VzmSIK9kanAiWLHoRR/dbZ2qpdPshuXr8l1VaHCzw==", + "dev": true, + "requires": { + "@ungap/promise-all-settled": "1.1.2", + "ansi-colors": "4.1.1", + "browser-stdout": "1.3.1", + "chokidar": "3.5.2", + "debug": "4.3.2", + "diff": "5.0.0", + "escape-string-regexp": "4.0.0", + "find-up": "5.0.0", + "glob": "7.1.7", + "growl": "1.10.5", + "he": "1.2.0", + "js-yaml": "4.1.0", + "log-symbols": "4.1.0", + "minimatch": "3.0.4", + "ms": "2.1.3", + "nanoid": "3.1.25", + "serialize-javascript": "6.0.0", + "strip-json-comments": "3.1.1", + "supports-color": "8.1.1", + "which": "2.0.2", + "workerpool": "6.1.5", + "yargs": "16.2.0", + "yargs-parser": "20.2.4", + "yargs-unparser": "2.0.0" + }, + "dependencies": { + "ms": { + "version": "2.1.3", + "resolved": "https://registry.npmjs.org/ms/-/ms-2.1.3.tgz", + "integrity": "sha512-6FlzubTLZG3J2a/NVCAleEhjzq5oxgHyaCU9yYXvcLsvoVaHJq/s5xXI6/XXP6tz7R9xAOtHnSO/tXtF3WRTlA==", + "dev": true + }, + "supports-color": { + "version": "8.1.1", + "resolved": "https://registry.npmjs.org/supports-color/-/supports-color-8.1.1.tgz", + "integrity": "sha512-MpUEN2OodtUzxvKQl72cUF7RQ5EiHsGvSsVG0ia9c5RbWGL2CI4C7EpPS8UTBIplnlzZiNuV56w+FuNxy3ty2Q==", + "dev": true, + "requires": { + "has-flag": "^4.0.0" + } + }, + "yargs": { + "version": "16.2.0", + "resolved": "https://registry.npmjs.org/yargs/-/yargs-16.2.0.tgz", + "integrity": "sha512-D1mvvtDG0L5ft/jGWkLpG1+m0eQxOfaBvTNELraWj22wSVUMWxZUvYgJYcKh6jGGIkJFhH4IZPQhR4TKpc8mBw==", + "dev": true, + "requires": { + "cliui": "^7.0.2", + "escalade": "^3.1.1", + "get-caller-file": "^2.0.5", + "require-directory": "^2.1.1", + "string-width": "^4.2.0", + "y18n": "^5.0.5", + "yargs-parser": "^20.2.2" + } + } + } + }, + "ms": { + "version": "2.1.2", + "resolved": "https://registry.npmjs.org/ms/-/ms-2.1.2.tgz", + "integrity": "sha512-sGkPx+VjMtmA6MX27oA4FBFELFCZZ4S4XqeGOXCv68tT+jb3vk/RyaKWP0PTKyWtmLSM0b+adUTEvbs1PEaH2w==", + "dev": true + }, + "mute-stream": { + "version": "0.0.8", + "resolved": "https://registry.npmjs.org/mute-stream/-/mute-stream-0.0.8.tgz", + "integrity": "sha512-nnbWWOkoWyUsTjKrhgD0dcz22mdkSnpYqbEjIm2nhwhuxlSkpywJmBo8h0ZqJdkp73mb90SssHkN4rsRaBAfAA==", + "dev": true + }, + "nanoid": { + "version": "3.1.25", + "resolved": "https://registry.npmjs.org/nanoid/-/nanoid-3.1.25.tgz", + "integrity": "sha512-rdwtIXaXCLFAQbnfqDRnI6jaRHp9fTcYBjtFKE8eezcZ7LuLjhUaQGNeMXf1HmRoCH32CLz6XwX0TtxEOS/A3Q==", + "dev": true + }, + "natural-compare": { + "version": "1.4.0", + "resolved": "https://registry.npmjs.org/natural-compare/-/natural-compare-1.4.0.tgz", + "integrity": "sha1-Sr6/7tdUHywnrPspvbvRXI1bpPc=", + "dev": true + }, + "neo-async": { + "version": "2.6.2", + "resolved": "https://registry.npmjs.org/neo-async/-/neo-async-2.6.2.tgz", + "integrity": "sha512-Yd3UES5mWCSqR+qNT93S3UoYUkqAZ9lLg8a7g9rimsWmYGK8cVToA4/sF3RrshdyV3sAGMXVUmpMYOw+dLpOuw==", + "dev": true + }, + "new-github-release-url": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/new-github-release-url/-/new-github-release-url-1.0.0.tgz", + "integrity": "sha512-dle7yf655IMjyFUqn6Nxkb18r4AOAkzRcgcZv6WZ0IqrOH4QCEZ8Sm6I7XX21zvHdBeeMeTkhR9qT2Z0EJDx6A==", + "dev": true, + "requires": { + "type-fest": "^0.4.1" + }, + "dependencies": { + "type-fest": { + "version": "0.4.1", + "resolved": "https://registry.npmjs.org/type-fest/-/type-fest-0.4.1.tgz", + "integrity": "sha512-IwzA/LSfD2vC1/YDYMv/zHP4rDF1usCwllsDpbolT3D4fUepIO7f9K70jjmUewU/LmGUKJcwcVtDCpnKk4BPMw==", + "dev": true + } + } + }, + "nise": { + "version": "5.1.0", + "resolved": "https://registry.npmjs.org/nise/-/nise-5.1.0.tgz", + "integrity": "sha512-W5WlHu+wvo3PaKLsJJkgPup2LrsXCcm7AWwyNZkUnn5rwPkuPBi3Iwk5SQtN0mv+K65k7nKKjwNQ30wg3wLAQQ==", + "dev": true, + "requires": { + "@sinonjs/commons": "^1.7.0", + "@sinonjs/fake-timers": "^7.0.4", + "@sinonjs/text-encoding": "^0.7.1", + "just-extend": "^4.0.2", + "path-to-regexp": "^1.7.0" + } + }, + "node-fetch": { + "version": "2.6.6", + "resolved": "https://registry.npmjs.org/node-fetch/-/node-fetch-2.6.6.tgz", + "integrity": "sha512-Z8/6vRlTUChSdIgMa51jxQ4lrw/Jy5SOW10ObaA47/RElsAN2c5Pn8bTgFGWn/ibwzXTE8qwr1Yzx28vsecXEA==", + "dev": true, + "requires": { + "whatwg-url": "^5.0.0" + } + }, + "node-preload": { + "version": "0.2.1", + "resolved": "https://registry.npmjs.org/node-preload/-/node-preload-0.2.1.tgz", + "integrity": "sha512-RM5oyBy45cLEoHqCeh+MNuFAxO0vTFBLskvQbOKnEE7YTTSN4tbN8QWDIPQ6L+WvKsB/qLEGpYe2ZZ9d4W9OIQ==", + "dev": true, + "requires": { + "process-on-spawn": "^1.0.0" + } + }, + "node-releases": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/node-releases/-/node-releases-2.0.1.tgz", + "integrity": "sha512-CqyzN6z7Q6aMeF/ktcMVTzhAHCEpf8SOarwpzpf8pNBY2k5/oM34UHldUwp8VKI7uxct2HxSRdJjBaZeESzcxA==", + "dev": true + }, + "normalize-path": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/normalize-path/-/normalize-path-3.0.0.tgz", + "integrity": "sha512-6eZs5Ls3WtCisHWp9S2GUy8dqkpGi4BVSz3GaqiE6ezub0512ESztXUwUB6C6IKbQkY2Pnb/mD4WYojCRwcwLA==", + "dev": true + }, + "normalize-url": { + "version": "6.1.0", + "resolved": "https://registry.npmjs.org/normalize-url/-/normalize-url-6.1.0.tgz", + "integrity": "sha512-DlL+XwOy3NxAQ8xuC0okPgK46iuVNAK01YN7RueYBqqFeGsBjV9XmCAzAdgt+667bCl5kPh9EqKKDwnaPG1I7A==", + "dev": true + }, + "npm-run-path": { + "version": "4.0.1", + "resolved": "https://registry.npmjs.org/npm-run-path/-/npm-run-path-4.0.1.tgz", + "integrity": "sha512-S48WzZW777zhNIrn7gxOlISNAqi9ZC/uQFnRdbeIHhZhCA6UqpkOT8T1G7BvfdgP4Er8gF4sUbaS0i7QvIfCWw==", + "dev": true, + "requires": { + "path-key": "^3.0.0" + } + }, + "nyc": { + "version": "15.1.0", + "resolved": "https://registry.npmjs.org/nyc/-/nyc-15.1.0.tgz", + "integrity": "sha512-jMW04n9SxKdKi1ZMGhvUTHBN0EICCRkHemEoE5jm6mTYcqcdas0ATzgUgejlQUHMvpnOZqGB5Xxsv9KxJW1j8A==", + "dev": true, + "requires": { + "@istanbuljs/load-nyc-config": "^1.0.0", + "@istanbuljs/schema": "^0.1.2", + "caching-transform": "^4.0.0", + "convert-source-map": "^1.7.0", + "decamelize": "^1.2.0", + "find-cache-dir": "^3.2.0", + "find-up": "^4.1.0", + "foreground-child": "^2.0.0", + "get-package-type": "^0.1.0", + "glob": "^7.1.6", + "istanbul-lib-coverage": "^3.0.0", + "istanbul-lib-hook": "^3.0.0", + "istanbul-lib-instrument": "^4.0.0", + "istanbul-lib-processinfo": "^2.0.2", + "istanbul-lib-report": "^3.0.0", + "istanbul-lib-source-maps": "^4.0.0", + "istanbul-reports": "^3.0.2", + "make-dir": "^3.0.0", + "node-preload": "^0.2.1", + "p-map": "^3.0.0", + "process-on-spawn": "^1.0.0", + "resolve-from": "^5.0.0", + "rimraf": "^3.0.0", + "signal-exit": "^3.0.2", + "spawn-wrap": "^2.0.0", + "test-exclude": "^6.0.0", + "yargs": "^15.0.2" + }, + "dependencies": { + "cliui": { + "version": "6.0.0", + "resolved": "https://registry.npmjs.org/cliui/-/cliui-6.0.0.tgz", + "integrity": "sha512-t6wbgtoCXvAzst7QgXxJYqPt0usEfbgQdftEPbLL/cvv6HPE5VgvqCuAIDR0NgU52ds6rFwqrgakNLrHEjCbrQ==", + "dev": true, + "requires": { + "string-width": "^4.2.0", + "strip-ansi": "^6.0.0", + "wrap-ansi": "^6.2.0" + } + }, + "find-up": { + "version": "4.1.0", + "resolved": "https://registry.npmjs.org/find-up/-/find-up-4.1.0.tgz", + "integrity": "sha512-PpOwAdQ/YlXQ2vj8a3h8IipDuYRi3wceVQQGYWxNINccq40Anw7BlsEXCMbt1Zt+OLA6Fq9suIpIWD0OsnISlw==", + "dev": true, + "requires": { + "locate-path": "^5.0.0", + "path-exists": "^4.0.0" + } + }, + "locate-path": { + "version": "5.0.0", + "resolved": "https://registry.npmjs.org/locate-path/-/locate-path-5.0.0.tgz", + "integrity": "sha512-t7hw9pI+WvuwNJXwk5zVHpyhIqzg2qTlklJOf0mVxGSbe3Fp2VieZcduNYjaLDoy6p9uGpQEGWG87WpMKlNq8g==", + "dev": true, + "requires": { + "p-locate": "^4.1.0" + } + }, + "p-limit": { + "version": "2.3.0", + "resolved": "https://registry.npmjs.org/p-limit/-/p-limit-2.3.0.tgz", + "integrity": "sha512-//88mFWSJx8lxCzwdAABTJL2MyWB12+eIY7MDL2SqLmAkeKU9qxRvWuSyTjm3FUmpBEMuFfckAIqEaVGUDxb6w==", + "dev": true, + "requires": { + "p-try": "^2.0.0" + } + }, + "p-locate": { + "version": "4.1.0", + "resolved": "https://registry.npmjs.org/p-locate/-/p-locate-4.1.0.tgz", + "integrity": "sha512-R79ZZ/0wAxKGu3oYMlz8jy/kbhsNrS7SKZ7PxEHBgJ5+F2mtFW2fK2cOtBh1cHYkQsbzFV7I+EoRKe6Yt0oK7A==", + "dev": true, + "requires": { + "p-limit": "^2.2.0" + } + }, + "resolve-from": { + "version": "5.0.0", + "resolved": "https://registry.npmjs.org/resolve-from/-/resolve-from-5.0.0.tgz", + "integrity": "sha512-qYg9KP24dD5qka9J47d0aVky0N+b4fTU89LN9iDnjB5waksiC49rvMB0PrUJQGoTmH50XPiqOvAjDfaijGxYZw==", + "dev": true + }, + "wrap-ansi": { + "version": "6.2.0", + "resolved": "https://registry.npmjs.org/wrap-ansi/-/wrap-ansi-6.2.0.tgz", + "integrity": "sha512-r6lPcBGxZXlIcymEu7InxDMhdW0KDxpLgoFLcguasxCaJ/SOIZwINatK9KY/tf+ZrlywOKU0UDj3ATXUBfxJXA==", + "dev": true, + "requires": { + "ansi-styles": "^4.0.0", + "string-width": "^4.1.0", + "strip-ansi": "^6.0.0" + } + }, + "y18n": { + "version": "4.0.3", + "resolved": "https://registry.npmjs.org/y18n/-/y18n-4.0.3.tgz", + "integrity": "sha512-JKhqTOwSrqNA1NY5lSztJ1GrBiUodLMmIZuLiDaMRJ+itFd+ABVE8XBjOvIWL+rSqNDC74LCSFmlb/U4UZ4hJQ==", + "dev": true + }, + "yargs": { + "version": "15.4.1", + "resolved": "https://registry.npmjs.org/yargs/-/yargs-15.4.1.tgz", + "integrity": "sha512-aePbxDmcYW++PaqBsJ+HYUFwCdv4LVvdnhBy78E57PIor8/OVvhMrADFFEDh8DHDFRv/O9i3lPhsENjO7QX0+A==", + "dev": true, + "requires": { + "cliui": "^6.0.0", + "decamelize": "^1.2.0", + "find-up": "^4.1.0", + "get-caller-file": "^2.0.1", + "require-directory": "^2.1.1", + "require-main-filename": "^2.0.0", + "set-blocking": "^2.0.0", + "string-width": "^4.2.0", + "which-module": "^2.0.0", + "y18n": "^4.0.0", + "yargs-parser": "^18.1.2" + } + }, + "yargs-parser": { + "version": "18.1.3", + "resolved": "https://registry.npmjs.org/yargs-parser/-/yargs-parser-18.1.3.tgz", + "integrity": "sha512-o50j0JeToy/4K6OZcaQmW6lyXXKhq7csREXcDwk2omFPJEwUNOVtJKvmDr9EI1fAJZUyZcRF7kxGBWmRXudrCQ==", + "dev": true, + "requires": { + "camelcase": "^5.0.0", + "decamelize": "^1.2.0" + } + } + } + }, + "object-inspect": { + "version": "1.11.0", + "resolved": "https://registry.npmjs.org/object-inspect/-/object-inspect-1.11.0.tgz", + "integrity": "sha512-jp7ikS6Sd3GxQfZJPyH3cjcbJF6GZPClgdV+EFygjFLQ5FmW/dRUnTd9PQ9k0JhoNDabWFbpF1yCdSWCC6gexg==", + "dev": true + }, + "once": { + "version": "1.4.0", + "resolved": "https://registry.npmjs.org/once/-/once-1.4.0.tgz", + "integrity": "sha1-WDsap3WWHUsROsF9nFC6753Xa9E=", + "dev": true, + "requires": { + "wrappy": "1" + } + }, + "onetime": { + "version": "5.1.2", + "resolved": "https://registry.npmjs.org/onetime/-/onetime-5.1.2.tgz", + "integrity": "sha512-kbpaSSGJTWdAY5KPVeMOKXSrPtr8C8C7wodJbcsd51jRnmD+GZu8Y0VoU6Dm5Z4vWr0Ig/1NKuWRKf7j5aaYSg==", + "dev": true, + "requires": { + "mimic-fn": "^2.1.0" + } + }, + "onigasm": { + "version": "2.2.5", + "resolved": "https://registry.npmjs.org/onigasm/-/onigasm-2.2.5.tgz", + "integrity": "sha512-F+th54mPc0l1lp1ZcFMyL/jTs2Tlq4SqIHKIXGZOR/VkHkF9A7Fr5rRr5+ZG/lWeRsyrClLYRq7s/yFQ/XhWCA==", + "dev": true, + "requires": { + "lru-cache": "^5.1.1" + }, + "dependencies": { + "lru-cache": { + "version": "5.1.1", + "resolved": "https://registry.npmjs.org/lru-cache/-/lru-cache-5.1.1.tgz", + "integrity": "sha512-KpNARQA3Iwv+jTA0utUVVbrh+Jlrr1Fv0e56GGzAFOXN7dk/FviaDW8LHmK52DlcH4WP2n6gI8vN1aesBFgo9w==", + "dev": true, + "requires": { + "yallist": "^3.0.2" + } + }, + "yallist": { + "version": "3.1.1", + "resolved": "https://registry.npmjs.org/yallist/-/yallist-3.1.1.tgz", + "integrity": "sha512-a4UGQaWPH59mOXUYnAG2ewncQS4i4F43Tv3JoAM+s2VDAmS9NsK8GpDMLrCHPksFT7h3K6TOoUNn2pb7RoXx4g==", + "dev": true + } + } + }, + "open": { + "version": "7.4.2", + "resolved": "https://registry.npmjs.org/open/-/open-7.4.2.tgz", + "integrity": "sha512-MVHddDVweXZF3awtlAS+6pgKLlm/JgxZ90+/NBurBoQctVOOB/zDdVjcyPzQ+0laDGbsWgrRkflI65sQeOgT9Q==", + "dev": true, + "requires": { + "is-docker": "^2.0.0", + "is-wsl": "^2.1.1" + } + }, + "optionator": { + "version": "0.9.1", + "resolved": "https://registry.npmjs.org/optionator/-/optionator-0.9.1.tgz", + "integrity": "sha512-74RlY5FCnhq4jRxVUPKDaRwrVNXMqsGsiW6AJw4XK8hmtm10wC0ypZBLw5IIp85NZMr91+qd1RvvENwg7jjRFw==", + "dev": true, + "requires": { + "deep-is": "^0.1.3", + "fast-levenshtein": "^2.0.6", + "levn": "^0.4.1", + "prelude-ls": "^1.2.1", + "type-check": "^0.4.0", + "word-wrap": "^1.2.3" + } + }, + "ora": { + "version": "5.4.1", + "resolved": "https://registry.npmjs.org/ora/-/ora-5.4.1.tgz", + "integrity": "sha512-5b6Y85tPxZZ7QytO+BQzysW31HJku27cRIlkbAXaNx+BdcVi+LlRFmVXzeF6a7JCwJpyw5c4b+YSVImQIrBpuQ==", + "dev": true, + "requires": { + "bl": "^4.1.0", + "chalk": "^4.1.0", + "cli-cursor": "^3.1.0", + "cli-spinners": "^2.5.0", + "is-interactive": "^1.0.0", + "is-unicode-supported": "^0.1.0", + "log-symbols": "^4.1.0", + "strip-ansi": "^6.0.0", + "wcwidth": "^1.0.1" + } + }, + "os-name": { + "version": "4.0.1", + "resolved": "https://registry.npmjs.org/os-name/-/os-name-4.0.1.tgz", + "integrity": "sha512-xl9MAoU97MH1Xt5K9ERft2YfCAoaO6msy1OBA0ozxEC0x0TmIoE6K3QvgJMMZA9yKGLmHXNY/YZoDbiGDj4zYw==", + "dev": true, + "requires": { + "macos-release": "^2.5.0", + "windows-release": "^4.0.0" + } + }, + "os-tmpdir": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/os-tmpdir/-/os-tmpdir-1.0.2.tgz", + "integrity": "sha1-u+Z0BseaqFxc/sdm/lc0VV36EnQ=", + "dev": true + }, + "p-cancelable": { + "version": "2.1.1", + "resolved": "https://registry.npmjs.org/p-cancelable/-/p-cancelable-2.1.1.tgz", + "integrity": "sha512-BZOr3nRQHOntUjTrH8+Lh54smKHoHyur8We1V8DSMVrl5A2malOOwuJRnKRDjSnkoeBh4at6BwEnb5I7Jl31wg==", + "dev": true + }, + "p-limit": { + "version": "3.1.0", + "resolved": "https://registry.npmjs.org/p-limit/-/p-limit-3.1.0.tgz", + "integrity": "sha512-TYOanM3wGwNGsZN2cVTYPArw454xnXj5qmWF1bEoAc4+cU/ol7GVh7odevjp1FNHduHc3KZMcFduxU5Xc6uJRQ==", + "dev": true, + "requires": { + "yocto-queue": "^0.1.0" + } + }, + "p-locate": { + "version": "5.0.0", + "resolved": "https://registry.npmjs.org/p-locate/-/p-locate-5.0.0.tgz", + "integrity": "sha512-LaNjtRWUBY++zB5nE/NwcaoMylSPk+S+ZHNB1TzdbMJMny6dynpAGt7X/tl/QYq3TIeE6nxHppbo2LGymrG5Pw==", + "dev": true, + "requires": { + "p-limit": "^3.0.2" + } + }, + "p-map": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/p-map/-/p-map-3.0.0.tgz", + "integrity": "sha512-d3qXVTF/s+W+CdJ5A29wywV2n8CQQYahlgz2bFiA+4eVNJbHJodPZ+/gXwPGh0bOqA+j8S+6+ckmvLGPk1QpxQ==", + "dev": true, + "requires": { + "aggregate-error": "^3.0.0" + } + }, + "p-try": { + "version": "2.2.0", + "resolved": "https://registry.npmjs.org/p-try/-/p-try-2.2.0.tgz", + "integrity": "sha512-R4nPAVTAU0B9D35/Gk3uJf/7XYbQcyohSKdvAxIRSNghFl4e71hVoGnBNQz9cWaXxO2I10KTC+3jMdvvoKw6dQ==", + "dev": true + }, + "package-hash": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/package-hash/-/package-hash-4.0.0.tgz", + "integrity": "sha512-whdkPIooSu/bASggZ96BWVvZTRMOFxnyUG5PnTSGKoJE2gd5mbVNmR2Nj20QFzxYYgAXpoqC+AiXzl+UMRh7zQ==", + "dev": true, + "requires": { + "graceful-fs": "^4.1.15", + "hasha": "^5.0.0", + "lodash.flattendeep": "^4.4.0", + "release-zalgo": "^1.0.0" + } + }, + "package-json": { + "version": "6.5.0", + "resolved": "https://registry.npmjs.org/package-json/-/package-json-6.5.0.tgz", + "integrity": "sha512-k3bdm2n25tkyxcjSKzB5x8kfVxlMdgsbPr0GkZcwHsLpba6cBjqCt1KlcChKEvxHIcTB1FVMuwoijZ26xex5MQ==", + "dev": true, + "requires": { + "got": "^9.6.0", + "registry-auth-token": "^4.0.0", + "registry-url": "^5.0.0", + "semver": "^6.2.0" + }, + "dependencies": { + "@sindresorhus/is": { + "version": "0.14.0", + "resolved": "https://registry.npmjs.org/@sindresorhus/is/-/is-0.14.0.tgz", + "integrity": "sha512-9NET910DNaIPngYnLLPeg+Ogzqsi9uM4mSboU5y6p8S5DzMTVEsJZrawi+BoDNUVBa2DhJqQYUFvMDfgU062LQ==", + "dev": true + }, + "@szmarczak/http-timer": { + "version": "1.1.2", + "resolved": "https://registry.npmjs.org/@szmarczak/http-timer/-/http-timer-1.1.2.tgz", + "integrity": "sha512-XIB2XbzHTN6ieIjfIMV9hlVcfPU26s2vafYWQcZHWXHOxiaRZYEDKEwdl129Zyg50+foYV2jCgtrqSA6qNuNSA==", + "dev": true, + "requires": { + "defer-to-connect": "^1.0.1" + } + }, + "cacheable-request": { + "version": "6.1.0", + "resolved": "https://registry.npmjs.org/cacheable-request/-/cacheable-request-6.1.0.tgz", + "integrity": "sha512-Oj3cAGPCqOZX7Rz64Uny2GYAZNliQSqfbePrgAQ1wKAihYmCUnraBtJtKcGR4xz7wF+LoJC+ssFZvv5BgF9Igg==", + "dev": true, + "requires": { + "clone-response": "^1.0.2", + "get-stream": "^5.1.0", + "http-cache-semantics": "^4.0.0", + "keyv": "^3.0.0", + "lowercase-keys": "^2.0.0", + "normalize-url": "^4.1.0", + "responselike": "^1.0.2" + }, + "dependencies": { + "get-stream": { + "version": "5.2.0", + "resolved": "https://registry.npmjs.org/get-stream/-/get-stream-5.2.0.tgz", + "integrity": "sha512-nBF+F1rAZVCu/p7rjzgA+Yb4lfYXrpl7a6VmJrU8wF9I1CKvP/QwPNZHnOlwbTkY6dvtFIzFMSyQXbLoTQPRpA==", + "dev": true, + "requires": { + "pump": "^3.0.0" + } + } + } + }, + "decompress-response": { + "version": "3.3.0", + "resolved": "https://registry.npmjs.org/decompress-response/-/decompress-response-3.3.0.tgz", + "integrity": "sha1-gKTdMjdIOEv6JICDYirt7Jgq3/M=", + "dev": true, + "requires": { + "mimic-response": "^1.0.0" + } + }, + "defer-to-connect": { + "version": "1.1.3", + "resolved": "https://registry.npmjs.org/defer-to-connect/-/defer-to-connect-1.1.3.tgz", + "integrity": "sha512-0ISdNousHvZT2EiFlZeZAHBUvSxmKswVCEf8hW7KWgG4a8MVEu/3Vb6uWYozkjylyCxe0JBIiRB1jV45S70WVQ==", + "dev": true + }, + "get-stream": { + "version": "4.1.0", + "resolved": "https://registry.npmjs.org/get-stream/-/get-stream-4.1.0.tgz", + "integrity": "sha512-GMat4EJ5161kIy2HevLlr4luNjBgvmj413KaQA7jt4V8B4RDsfpHk7WQ9GVqfYyyx8OS/L66Kox+rJRNklLK7w==", + "dev": true, + "requires": { + "pump": "^3.0.0" + } + }, + "got": { + "version": "9.6.0", + "resolved": "https://registry.npmjs.org/got/-/got-9.6.0.tgz", + "integrity": "sha512-R7eWptXuGYxwijs0eV+v3o6+XH1IqVK8dJOEecQfTmkncw9AV4dcw/Dhxi8MdlqPthxxpZyizMzyg8RTmEsG+Q==", + "dev": true, + "requires": { + "@sindresorhus/is": "^0.14.0", + "@szmarczak/http-timer": "^1.1.2", + "cacheable-request": "^6.0.0", + "decompress-response": "^3.3.0", + "duplexer3": "^0.1.4", + "get-stream": "^4.1.0", + "lowercase-keys": "^1.0.1", + "mimic-response": "^1.0.1", + "p-cancelable": "^1.0.0", + "to-readable-stream": "^1.0.0", + "url-parse-lax": "^3.0.0" + }, + "dependencies": { + "lowercase-keys": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/lowercase-keys/-/lowercase-keys-1.0.1.tgz", + "integrity": "sha512-G2Lj61tXDnVFFOi8VZds+SoQjtQC3dgokKdDG2mTm1tx4m50NUHBOZSBwQQHyy0V12A0JTG4icfZQH+xPyh8VA==", + "dev": true + } + } + }, + "json-buffer": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/json-buffer/-/json-buffer-3.0.0.tgz", + "integrity": "sha1-Wx85evx11ne96Lz8Dkfh+aPZqJg=", + "dev": true + }, + "keyv": { + "version": "3.1.0", + "resolved": "https://registry.npmjs.org/keyv/-/keyv-3.1.0.tgz", + "integrity": "sha512-9ykJ/46SN/9KPM/sichzQ7OvXyGDYKGTaDlKMGCAlg2UK8KRy4jb0d8sFc+0Tt0YYnThq8X2RZgCg74RPxgcVA==", + "dev": true, + "requires": { + "json-buffer": "3.0.0" + } + }, + "normalize-url": { + "version": "4.5.1", + "resolved": "https://registry.npmjs.org/normalize-url/-/normalize-url-4.5.1.tgz", + "integrity": "sha512-9UZCFRHQdNrfTpGg8+1INIg93B6zE0aXMVFkw1WFwvO4SlZywU6aLg5Of0Ap/PgcbSw4LNxvMWXMeugwMCX0AA==", + "dev": true + }, + "p-cancelable": { + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/p-cancelable/-/p-cancelable-1.1.0.tgz", + "integrity": "sha512-s73XxOZ4zpt1edZYZzvhqFa6uvQc1vwUa0K0BdtIZgQMAJj9IbebH+JkgKZc9h+B05PKHLOTl4ajG1BmNrVZlw==", + "dev": true + }, + "responselike": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/responselike/-/responselike-1.0.2.tgz", + "integrity": "sha1-kYcg7ztjHFZCvgaPFa3lpG9Loec=", + "dev": true, + "requires": { + "lowercase-keys": "^1.0.0" + }, + "dependencies": { + "lowercase-keys": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/lowercase-keys/-/lowercase-keys-1.0.1.tgz", + "integrity": "sha512-G2Lj61tXDnVFFOi8VZds+SoQjtQC3dgokKdDG2mTm1tx4m50NUHBOZSBwQQHyy0V12A0JTG4icfZQH+xPyh8VA==", + "dev": true + } + } + }, + "semver": { + "version": "6.3.0", + "resolved": "https://registry.npmjs.org/semver/-/semver-6.3.0.tgz", + "integrity": "sha512-b39TBaTSfV6yBrapU89p5fKekE2m/NwnDocOVruQFS1/veMgdzuPcnOM34M6CwxW8jH/lxEa5rBoDeUwu5HHTw==", + "dev": true + } + } + }, + "parent-module": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/parent-module/-/parent-module-1.0.1.tgz", + "integrity": "sha512-GQ2EWRpQV8/o+Aw8YqtfZZPfNRWZYkbidE9k5rpl/hC3vtHHBfGm2Ifi6qWV+coDGkrUKZAxE3Lot5kcsRlh+g==", + "dev": true, + "requires": { + "callsites": "^3.0.0" + } + }, + "parse-json": { + "version": "5.2.0", + "resolved": "https://registry.npmjs.org/parse-json/-/parse-json-5.2.0.tgz", + "integrity": "sha512-ayCKvm/phCGxOkYRSCM82iDwct8/EonSEgCSxWxD7ve6jHggsFl4fZVQBPRNgQoKiuV/odhFrGzQXZwbifC8Rg==", + "dev": true, + "requires": { + "@babel/code-frame": "^7.0.0", + "error-ex": "^1.3.1", + "json-parse-even-better-errors": "^2.3.0", + "lines-and-columns": "^1.1.6" + } + }, + "parse-path": { + "version": "4.0.3", + "resolved": "https://registry.npmjs.org/parse-path/-/parse-path-4.0.3.tgz", + "integrity": "sha512-9Cepbp2asKnWTJ9x2kpw6Fe8y9JDbqwahGCTvklzd/cEq5C5JC59x2Xb0Kx+x0QZ8bvNquGO8/BWP0cwBHzSAA==", + "dev": true, + "requires": { + "is-ssh": "^1.3.0", + "protocols": "^1.4.0", + "qs": "^6.9.4", + "query-string": "^6.13.8" + } + }, + "parse-url": { + "version": "6.0.0", + "resolved": "https://registry.npmjs.org/parse-url/-/parse-url-6.0.0.tgz", + "integrity": "sha512-cYyojeX7yIIwuJzledIHeLUBVJ6COVLeT4eF+2P6aKVzwvgKQPndCBv3+yQ7pcWjqToYwaligxzSYNNmGoMAvw==", + "dev": true, + "requires": { + "is-ssh": "^1.3.0", + "normalize-url": "^6.1.0", + "parse-path": "^4.0.0", + "protocols": "^1.4.0" + } + }, + "path-exists": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/path-exists/-/path-exists-4.0.0.tgz", + "integrity": "sha512-ak9Qy5Q7jYb2Wwcey5Fpvg2KoAc/ZIhLSLOSBmRmygPsGwkVVt0fZa0qrtMz+m6tJTAHfZQ8FnmB4MG4LWy7/w==", + "dev": true + }, + "path-is-absolute": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/path-is-absolute/-/path-is-absolute-1.0.1.tgz", + "integrity": "sha1-F0uSaHNVNP+8es5r9TpanhtcX18=", + "dev": true + }, + "path-key": { + "version": "3.1.1", + "resolved": "https://registry.npmjs.org/path-key/-/path-key-3.1.1.tgz", + "integrity": "sha512-ojmeN0qd+y0jszEtoY48r0Peq5dwMEkIlCOu6Q5f41lfkswXuKtYrhgoTpLnyIcHm24Uhqx+5Tqm2InSwLhE6Q==", + "dev": true + }, + "path-parse": { + "version": "1.0.7", + "resolved": "https://registry.npmjs.org/path-parse/-/path-parse-1.0.7.tgz", + "integrity": "sha512-LDJzPVEEEPR+y48z93A0Ed0yXb8pAByGWo/k5YYdYgpY2/2EsOsksJrq7lOHxryrVOn1ejG6oAp8ahvOIQD8sw==", + "dev": true + }, + "path-to-regexp": { + "version": "1.8.0", + "resolved": "https://registry.npmjs.org/path-to-regexp/-/path-to-regexp-1.8.0.tgz", + "integrity": "sha512-n43JRhlUKUAlibEJhPeir1ncUID16QnEjNpwzNdO3Lm4ywrBpBZ5oLD0I6br9evr1Y9JTqwRtAh7JLoOzAQdVA==", + "dev": true, + "requires": { + "isarray": "0.0.1" + } + }, + "path-type": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/path-type/-/path-type-4.0.0.tgz", + "integrity": "sha512-gDKb8aZMDeD/tZWs9P6+q0J9Mwkdl6xMV8TjnGP3qJVJ06bdMgkbBlLU8IdfOsIsFz2BW1rNVT3XuNEl8zPAvw==", + "dev": true + }, + "picocolors": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/picocolors/-/picocolors-1.0.0.tgz", + "integrity": "sha512-1fygroTLlHu66zi26VoTDv8yRgm0Fccecssto+MhsZ0D/DGW2sm8E8AjW7NU5VVTRt5GxbeZ5qBuJr+HyLYkjQ==", + "dev": true + }, + "picomatch": { + "version": "2.3.0", + "resolved": "https://registry.npmjs.org/picomatch/-/picomatch-2.3.0.tgz", + "integrity": "sha512-lY1Q/PiJGC2zOv/z391WOTD+Z02bCgsFfvxoXXf6h7kv9o+WmsmzYqrAwY63sNgOxE4xEdq0WyUnXfKeBrSvYw==", + "dev": true + }, + "pkg-dir": { + "version": "4.2.0", + "resolved": "https://registry.npmjs.org/pkg-dir/-/pkg-dir-4.2.0.tgz", + "integrity": "sha512-HRDzbaKjC+AOWVXxAU/x54COGeIv9eb+6CkDSQoNTt4XyWoIJvuPsXizxu/Fr23EiekbtZwmh1IcIG/l/a10GQ==", + "dev": true, + "requires": { + "find-up": "^4.0.0" + }, + "dependencies": { + "find-up": { + "version": "4.1.0", + "resolved": "https://registry.npmjs.org/find-up/-/find-up-4.1.0.tgz", + "integrity": "sha512-PpOwAdQ/YlXQ2vj8a3h8IipDuYRi3wceVQQGYWxNINccq40Anw7BlsEXCMbt1Zt+OLA6Fq9suIpIWD0OsnISlw==", + "dev": true, + "requires": { + "locate-path": "^5.0.0", + "path-exists": "^4.0.0" + } + }, + "locate-path": { + "version": "5.0.0", + "resolved": "https://registry.npmjs.org/locate-path/-/locate-path-5.0.0.tgz", + "integrity": "sha512-t7hw9pI+WvuwNJXwk5zVHpyhIqzg2qTlklJOf0mVxGSbe3Fp2VieZcduNYjaLDoy6p9uGpQEGWG87WpMKlNq8g==", + "dev": true, + "requires": { + "p-locate": "^4.1.0" + } + }, + "p-limit": { + "version": "2.3.0", + "resolved": "https://registry.npmjs.org/p-limit/-/p-limit-2.3.0.tgz", + "integrity": "sha512-//88mFWSJx8lxCzwdAABTJL2MyWB12+eIY7MDL2SqLmAkeKU9qxRvWuSyTjm3FUmpBEMuFfckAIqEaVGUDxb6w==", + "dev": true, + "requires": { + "p-try": "^2.0.0" + } + }, + "p-locate": { + "version": "4.1.0", + "resolved": "https://registry.npmjs.org/p-locate/-/p-locate-4.1.0.tgz", + "integrity": "sha512-R79ZZ/0wAxKGu3oYMlz8jy/kbhsNrS7SKZ7PxEHBgJ5+F2mtFW2fK2cOtBh1cHYkQsbzFV7I+EoRKe6Yt0oK7A==", + "dev": true, + "requires": { + "p-limit": "^2.2.0" + } + } + } + }, + "prelude-ls": { + "version": "1.2.1", + "resolved": "https://registry.npmjs.org/prelude-ls/-/prelude-ls-1.2.1.tgz", + "integrity": "sha512-vkcDPrRZo1QZLbn5RLGPpg/WmIQ65qoWWhcGKf/b5eplkkarX0m9z8ppCat4mlOqUsWpyNuYgO3VRyrYHSzX5g==", + "dev": true + }, + "prepend-http": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/prepend-http/-/prepend-http-2.0.0.tgz", + "integrity": "sha1-6SQ0v6XqjBn0HN/UAddBo8gZ2Jc=", + "dev": true + }, + "process-on-spawn": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/process-on-spawn/-/process-on-spawn-1.0.0.tgz", + "integrity": "sha512-1WsPDsUSMmZH5LeMLegqkPDrsGgsWwk1Exipy2hvB0o/F0ASzbpIctSCcZIK1ykJvtTJULEH+20WOFjMvGnCTg==", + "dev": true, + "requires": { + "fromentries": "^1.2.0" + } + }, + "progress": { + "version": "2.0.3", + "resolved": "https://registry.npmjs.org/progress/-/progress-2.0.3.tgz", + "integrity": "sha512-7PiHtLll5LdnKIMw100I+8xJXR5gW2QwWYkT6iJva0bXitZKa/XMrSbdmg3r2Xnaidz9Qumd0VPaMrZlF9V9sA==", + "dev": true + }, + "protocols": { + "version": "1.4.8", + "resolved": "https://registry.npmjs.org/protocols/-/protocols-1.4.8.tgz", + "integrity": "sha512-IgjKyaUSjsROSO8/D49Ab7hP8mJgTYcqApOqdPhLoPxAplXmkp+zRvsrSQjFn5by0rhm4VH0GAUELIPpx7B1yg==", + "dev": true + }, + "pump": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/pump/-/pump-3.0.0.tgz", + "integrity": "sha512-LwZy+p3SFs1Pytd/jYct4wpv49HiYCqd9Rlc5ZVdk0V+8Yzv6jR5Blk3TRmPL1ft69TxP0IMZGJ+WPFU2BFhww==", + "dev": true, + "requires": { + "end-of-stream": "^1.1.0", + "once": "^1.3.1" + } + }, + "punycode": { + "version": "2.1.1", + "resolved": "https://registry.npmjs.org/punycode/-/punycode-2.1.1.tgz", + "integrity": "sha512-XRsRjdf+j5ml+y/6GKHPZbrF/8p2Yga0JPtdqTIY2Xe5ohJPD9saDJJLPvp9+NSBprVvevdXZybnj2cv8OEd0A==", + "dev": true + }, + "pupa": { + "version": "2.1.1", + "resolved": "https://registry.npmjs.org/pupa/-/pupa-2.1.1.tgz", + "integrity": "sha512-l1jNAspIBSFqbT+y+5FosojNpVpF94nlI+wDUpqP9enwOTfHx9f0gh5nB96vl+6yTpsJsypeNrwfzPrKuHB41A==", + "dev": true, + "requires": { + "escape-goat": "^2.0.0" + } + }, + "qs": { + "version": "6.10.1", + "resolved": "https://registry.npmjs.org/qs/-/qs-6.10.1.tgz", + "integrity": "sha512-M528Hph6wsSVOBiYUnGf+K/7w0hNshs/duGsNXPUCLH5XAqjEtiPGwNONLV0tBH8NoGb0mvD5JubnUTrujKDTg==", + "dev": true, + "requires": { + "side-channel": "^1.0.4" + } + }, + "query-string": { + "version": "6.14.1", + "resolved": "https://registry.npmjs.org/query-string/-/query-string-6.14.1.tgz", + "integrity": "sha512-XDxAeVmpfu1/6IjyT/gXHOl+S0vQ9owggJ30hhWKdHAsNPOcasn5o9BW0eejZqL2e4vMjhAxoW3jVHcD6mbcYw==", + "dev": true, + "requires": { + "decode-uri-component": "^0.2.0", + "filter-obj": "^1.1.0", + "split-on-first": "^1.0.0", + "strict-uri-encode": "^2.0.0" + } + }, + "queue-microtask": { + "version": "1.2.3", + "resolved": "https://registry.npmjs.org/queue-microtask/-/queue-microtask-1.2.3.tgz", + "integrity": "sha512-NuaNSa6flKT5JaSYQzJok04JzTL1CA6aGhv5rfLW3PgqA+M2ChpZQnAC8h8i4ZFkBS8X5RqkDBHA7r4hej3K9A==", + "dev": true + }, + "quick-lru": { + "version": "5.1.1", + "resolved": "https://registry.npmjs.org/quick-lru/-/quick-lru-5.1.1.tgz", + "integrity": "sha512-WuyALRjWPDGtt/wzJiadO5AXY+8hZ80hVpe6MyivgraREW751X3SbhRvG3eLKOYN+8VEvqLcf3wdnt44Z4S4SA==", + "dev": true + }, + "randombytes": { + "version": "2.1.0", + "resolved": "https://registry.npmjs.org/randombytes/-/randombytes-2.1.0.tgz", + "integrity": "sha512-vYl3iOX+4CKUWuxGi9Ukhie6fsqXqS9FE2Zaic4tNFD2N2QQaXOMFbuKK4QmDHC0JO6B1Zp41J0LpT0oR68amQ==", + "dev": true, + "requires": { + "safe-buffer": "^5.1.0" + } + }, + "rc": { + "version": "1.2.8", + "resolved": "https://registry.npmjs.org/rc/-/rc-1.2.8.tgz", + "integrity": "sha512-y3bGgqKj3QBdxLbLkomlohkvsA8gdAiUQlSBJnBhfn+BPxg4bc62d8TcBW15wavDfgexCgccckhcZvywyQYPOw==", + "dev": true, + "requires": { + "deep-extend": "^0.6.0", + "ini": "~1.3.0", + "minimist": "^1.2.0", + "strip-json-comments": "~2.0.1" + }, + "dependencies": { + "ini": { + "version": "1.3.8", + "resolved": "https://registry.npmjs.org/ini/-/ini-1.3.8.tgz", + "integrity": "sha512-JV/yugV2uzW5iMRSiZAyDtQd+nxtUnjeLt0acNdw98kKLrvuRVyB80tsREOE7yvGVgalhZ6RNXCmEHkUKBKxew==", + "dev": true + }, + "strip-json-comments": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/strip-json-comments/-/strip-json-comments-2.0.1.tgz", + "integrity": "sha1-PFMZQukIwml8DsNEhYwobHygpgo=", + "dev": true + } + } + }, + "readable-stream": { + "version": "3.6.0", + "resolved": "https://registry.npmjs.org/readable-stream/-/readable-stream-3.6.0.tgz", + "integrity": "sha512-BViHy7LKeTz4oNnkcLJ+lVSL6vpiFeX6/d3oSH8zCW7UxP2onchk+vTGB143xuFjHS3deTgkKoXXymXqymiIdA==", + "dev": true, + "requires": { + "inherits": "^2.0.3", + "string_decoder": "^1.1.1", + "util-deprecate": "^1.0.1" + } + }, + "readdirp": { + "version": "3.6.0", + "resolved": "https://registry.npmjs.org/readdirp/-/readdirp-3.6.0.tgz", + "integrity": "sha512-hOS089on8RduqdbhvQ5Z37A0ESjsqz6qnRcffsMU3495FuTdqSm+7bhJ29JvIOsBDEEnan5DPu9t3To9VRlMzA==", + "dev": true, + "requires": { + "picomatch": "^2.2.1" + } + }, + "rechoir": { + "version": "0.6.2", + "resolved": "https://registry.npmjs.org/rechoir/-/rechoir-0.6.2.tgz", + "integrity": "sha1-hSBLVNuoLVdC4oyWdW70OvUOM4Q=", + "dev": true, + "requires": { + "resolve": "^1.1.6" + } + }, + "redis": { + "version": "file:packages/all-in-one", + "requires": { + "@redis/client": "^1.0.0-rc", + "@redis/json": "^1.0.0-rc", + "@redis/search": "^1.0.0-rc", + "release-it": "^14.11.6", + "typescript": "^4.4.4" + } + }, + "redis-errors": { + "version": "1.2.0", + "resolved": "https://registry.npmjs.org/redis-errors/-/redis-errors-1.2.0.tgz", + "integrity": "sha1-62LSrbFeTq9GEMBK/hUpOEJQq60=" + }, + "redis-parser": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/redis-parser/-/redis-parser-3.0.0.tgz", + "integrity": "sha1-tm2CjNyv5rS4pCin3vTGvKwxyLQ=", + "requires": { + "redis-errors": "^1.0.0" + } + }, + "regexpp": { + "version": "3.2.0", + "resolved": "https://registry.npmjs.org/regexpp/-/regexpp-3.2.0.tgz", + "integrity": "sha512-pq2bWo9mVD43nbts2wGv17XLiNLya+GklZ8kaDLV2Z08gDCsGpnKn9BFMepvWuHCbyVvY7J5o5+BVvoQbmlJLg==", + "dev": true + }, + "registry-auth-token": { + "version": "4.2.1", + "resolved": "https://registry.npmjs.org/registry-auth-token/-/registry-auth-token-4.2.1.tgz", + "integrity": "sha512-6gkSb4U6aWJB4SF2ZvLb76yCBjcvufXBqvvEx1HbmKPkutswjW1xNVRY0+daljIYRbogN7O0etYSlbiaEQyMyw==", + "dev": true, + "requires": { + "rc": "^1.2.8" + } + }, + "registry-url": { + "version": "5.1.0", + "resolved": "https://registry.npmjs.org/registry-url/-/registry-url-5.1.0.tgz", + "integrity": "sha512-8acYXXTI0AkQv6RAOjE3vOaIXZkT9wo4LOFbBKYQEEnnMNBpKqdUrI6S4NT0KPIo/WVvJ5tE/X5LF/TQUf0ekw==", + "dev": true, + "requires": { + "rc": "^1.2.8" + } + }, + "release-it": { + "version": "14.11.6", + "resolved": "https://registry.npmjs.org/release-it/-/release-it-14.11.6.tgz", + "integrity": "sha512-6BNcuzFZHThBUBJ/xYw/bxZ+58CAwrwf1zgmjq2Ibl3nlDZbjphHG6iqxkJu7mZ8TIWs6NjloEAhqpjeXoN//Q==", + "dev": true, + "requires": { + "@iarna/toml": "2.2.5", + "@octokit/rest": "18.10.0", + "async-retry": "1.3.3", + "chalk": "4.1.2", + "cosmiconfig": "7.0.1", + "debug": "4.3.2", + "deprecated-obj": "2.0.0", + "execa": "5.1.1", + "form-data": "4.0.0", + "git-url-parse": "11.6.0", + "globby": "11.0.4", + "got": "11.8.2", + "import-cwd": "3.0.0", + "inquirer": "8.1.5", + "is-ci": "3.0.0", + "lodash": "4.17.21", + "mime-types": "2.1.32", + "new-github-release-url": "1.0.0", + "open": "7.4.2", + "ora": "5.4.1", + "os-name": "4.0.1", + "parse-json": "5.2.0", + "semver": "7.3.5", + "shelljs": "0.8.4", + "update-notifier": "5.1.0", + "url-join": "4.0.1", + "uuid": "8.3.2", + "yaml": "1.10.2", + "yargs-parser": "20.2.9" + }, + "dependencies": { + "uuid": { + "version": "8.3.2", + "resolved": "https://registry.npmjs.org/uuid/-/uuid-8.3.2.tgz", + "integrity": "sha512-+NYs2QeMWy+GWFOEm9xnn6HCDp0l7QBD7ml8zLUmJ+93Q5NF0NocErnwkTkXVFNiX3/fpC6afS8Dhb/gz7R7eg==", + "dev": true + }, + "yargs-parser": { + "version": "20.2.9", + "resolved": "https://registry.npmjs.org/yargs-parser/-/yargs-parser-20.2.9.tgz", + "integrity": "sha512-y11nGElTIV+CT3Zv9t7VKl+Q3hTQoT9a1Qzezhhl6Rp21gJ/IVTW7Z3y9EWXhuUBC2Shnf+DX0antecpAwSP8w==", + "dev": true + } + } + }, + "release-zalgo": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/release-zalgo/-/release-zalgo-1.0.0.tgz", + "integrity": "sha1-CXALflB0Mpc5Mw5TXFqQ+2eFFzA=", + "dev": true, + "requires": { + "es6-error": "^4.0.1" + } + }, + "require-directory": { + "version": "2.1.1", + "resolved": "https://registry.npmjs.org/require-directory/-/require-directory-2.1.1.tgz", + "integrity": "sha1-jGStX9MNqxyXbiNE/+f3kqam30I=", + "dev": true + }, + "require-main-filename": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/require-main-filename/-/require-main-filename-2.0.0.tgz", + "integrity": "sha512-NKN5kMDylKuldxYLSUfrbo5Tuzh4hd+2E8NPPX02mZtn1VuREQToYe/ZdlJy+J3uCpfaiGF05e7B8W0iXbQHmg==", + "dev": true + }, + "resolve": { + "version": "1.20.0", + "resolved": "https://registry.npmjs.org/resolve/-/resolve-1.20.0.tgz", + "integrity": "sha512-wENBPt4ySzg4ybFQW2TT1zMQucPK95HSh/nq2CFTZVOGut2+pQvSsgtda4d26YrYcr067wjbmzOG8byDPBX63A==", + "dev": true, + "requires": { + "is-core-module": "^2.2.0", + "path-parse": "^1.0.6" + } + }, + "resolve-alpn": { + "version": "1.2.1", + "resolved": "https://registry.npmjs.org/resolve-alpn/-/resolve-alpn-1.2.1.tgz", + "integrity": "sha512-0a1F4l73/ZFZOakJnQ3FvkJ2+gSTQWz/r2KE5OdDY0TxPm5h4GkqkWWfM47T7HsbnOtcJVEF4epCVy6u7Q3K+g==", + "dev": true + }, + "resolve-from": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/resolve-from/-/resolve-from-4.0.0.tgz", + "integrity": "sha512-pb/MYmXstAkysRFx8piNI1tGFNQIFA3vkE3Gq4EuA1dF6gHp/+vgZqsCGJapvy8N3Q+4o7FwvquPJcnZ7RYy4g==", + "dev": true + }, + "responselike": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/responselike/-/responselike-2.0.0.tgz", + "integrity": "sha512-xH48u3FTB9VsZw7R+vvgaKeLKzT6jOogbQhEe/jewwnZgzPcnyWui2Av6JpoYZF/91uueC+lqhWqeURw5/qhCw==", + "dev": true, + "requires": { + "lowercase-keys": "^2.0.0" + } + }, + "restore-cursor": { + "version": "3.1.0", + "resolved": "https://registry.npmjs.org/restore-cursor/-/restore-cursor-3.1.0.tgz", + "integrity": "sha512-l+sSefzHpj5qimhFSE5a8nufZYAM3sBSVMAPtYkmC+4EH2anSGaEMXSD0izRQbu9nfyQ9y5JrVmp7E8oZrUjvA==", + "dev": true, + "requires": { + "onetime": "^5.1.0", + "signal-exit": "^3.0.2" + } + }, + "retry": { + "version": "0.13.1", + "resolved": "https://registry.npmjs.org/retry/-/retry-0.13.1.tgz", + "integrity": "sha512-XQBQ3I8W1Cge0Seh+6gjj03LbmRFWuoszgK9ooCpwYIrhhoO80pfq4cUkU5DkknwfOfFteRwlZ56PYOGYyFWdg==", + "dev": true + }, + "reusify": { + "version": "1.0.4", + "resolved": "https://registry.npmjs.org/reusify/-/reusify-1.0.4.tgz", + "integrity": "sha512-U9nH88a3fc/ekCF1l0/UP1IosiuIjyTh7hBvXVMHYgVcfGvt897Xguj2UOLDeI5BG2m7/uwyaLVT6fbtCwTyzw==", + "dev": true + }, + "rimraf": { + "version": "3.0.2", + "resolved": "https://registry.npmjs.org/rimraf/-/rimraf-3.0.2.tgz", + "integrity": "sha512-JZkJMZkAGFFPP2YqXZXPbMlMBgsxzE8ILs4lMIX/2o0L9UBw9O/Y3o6wFw/i9YLapcUJWwqbi3kdxIPdC62TIA==", + "dev": true, + "requires": { + "glob": "^7.1.3" + } + }, + "run-async": { + "version": "2.4.1", + "resolved": "https://registry.npmjs.org/run-async/-/run-async-2.4.1.tgz", + "integrity": "sha512-tvVnVv01b8c1RrA6Ep7JkStj85Guv/YrMcwqYQnwjsAS2cTmmPGBBjAjpCW7RrSodNSoE2/qg9O4bceNvUuDgQ==", + "dev": true + }, + "run-parallel": { + "version": "1.2.0", + "resolved": "https://registry.npmjs.org/run-parallel/-/run-parallel-1.2.0.tgz", + "integrity": "sha512-5l4VyZR86LZ/lDxZTR6jqL8AFE2S0IFLMP26AbjsLVADxHdhB/c0GUsH+y39UfCi3dzz8OlQuPmnaJOMoDHQBA==", + "dev": true, + "requires": { + "queue-microtask": "^1.2.2" + } + }, + "rxjs": { + "version": "7.4.0", + "resolved": "https://registry.npmjs.org/rxjs/-/rxjs-7.4.0.tgz", + "integrity": "sha512-7SQDi7xeTMCJpqViXh8gL/lebcwlp3d831F05+9B44A4B0WfsEwUQHR64gsH1kvJ+Ep/J9K2+n1hVl1CsGN23w==", + "dev": true, + "requires": { + "tslib": "~2.1.0" + } + }, + "safe-buffer": { + "version": "5.1.2", + "resolved": "https://registry.npmjs.org/safe-buffer/-/safe-buffer-5.1.2.tgz", + "integrity": "sha512-Gd2UZBJDkXlY7GbJxfsE8/nvKkUEU1G38c1siN6QP6a9PT9MmHB8GnpscSmMJSoF8LOIrt8ud/wPtojys4G6+g==", + "dev": true + }, + "safer-buffer": { + "version": "2.1.2", + "resolved": "https://registry.npmjs.org/safer-buffer/-/safer-buffer-2.1.2.tgz", + "integrity": "sha512-YZo3K82SD7Riyi0E1EQPojLz7kpepnSQI9IyPbHHg1XXXevb5dJI7tpyN2ADxGcQbHG7vcyRHk0cbwqcQriUtg==", + "dev": true + }, + "semver": { + "version": "7.3.5", + "resolved": "https://registry.npmjs.org/semver/-/semver-7.3.5.tgz", + "integrity": "sha512-PoeGJYh8HK4BTO/a9Tf6ZG3veo/A7ZVsYrSA6J8ny9nb3B1VrpkuN+z9OE5wfE5p6H4LchYZsegiQgbJD94ZFQ==", + "dev": true, + "requires": { + "lru-cache": "^6.0.0" + } + }, + "semver-diff": { + "version": "3.1.1", + "resolved": "https://registry.npmjs.org/semver-diff/-/semver-diff-3.1.1.tgz", + "integrity": "sha512-GX0Ix/CJcHyB8c4ykpHGIAvLyOwOobtM/8d+TQkAd81/bEjgPHrfba41Vpesr7jX/t8Uh+R3EX9eAS5be+jQYg==", + "dev": true, + "requires": { + "semver": "^6.3.0" + }, + "dependencies": { + "semver": { + "version": "6.3.0", + "resolved": "https://registry.npmjs.org/semver/-/semver-6.3.0.tgz", + "integrity": "sha512-b39TBaTSfV6yBrapU89p5fKekE2m/NwnDocOVruQFS1/veMgdzuPcnOM34M6CwxW8jH/lxEa5rBoDeUwu5HHTw==", + "dev": true + } + } + }, + "serialize-javascript": { + "version": "6.0.0", + "resolved": "https://registry.npmjs.org/serialize-javascript/-/serialize-javascript-6.0.0.tgz", + "integrity": "sha512-Qr3TosvguFt8ePWqsvRfrKyQXIiW+nGbYpy8XK24NQHE83caxWt+mIymTT19DGFbNWNLfEwsrkSmN64lVWB9ag==", + "dev": true, + "requires": { + "randombytes": "^2.1.0" + } + }, + "set-blocking": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/set-blocking/-/set-blocking-2.0.0.tgz", + "integrity": "sha1-BF+XgtARrppoA93TgrJDkrPYkPc=", + "dev": true + }, + "shebang-command": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/shebang-command/-/shebang-command-2.0.0.tgz", + "integrity": "sha512-kHxr2zZpYtdmrN1qDjrrX/Z1rR1kG8Dx+gkpK1G4eXmvXswmcE1hTWBWYUzlraYw1/yZp6YuDY77YtvbN0dmDA==", + "dev": true, + "requires": { + "shebang-regex": "^3.0.0" + } + }, + "shebang-regex": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/shebang-regex/-/shebang-regex-3.0.0.tgz", + "integrity": "sha512-7++dFhtcx3353uBaq8DDR4NuxBetBzC7ZQOhmTQInHEd6bSrXdiEyzCvG07Z44UYdLShWUyXt5M/yhz8ekcb1A==", + "dev": true + }, + "shelljs": { + "version": "0.8.4", + "resolved": "https://registry.npmjs.org/shelljs/-/shelljs-0.8.4.tgz", + "integrity": "sha512-7gk3UZ9kOfPLIAbslLzyWeGiEqx9e3rxwZM0KE6EL8GlGwjym9Mrlx5/p33bWTu9YG6vcS4MBxYZDHYr5lr8BQ==", + "dev": true, + "requires": { + "glob": "^7.0.0", + "interpret": "^1.0.0", + "rechoir": "^0.6.2" + } + }, + "shiki": { + "version": "0.9.12", + "resolved": "https://registry.npmjs.org/shiki/-/shiki-0.9.12.tgz", + "integrity": "sha512-VXcROdldv0/Qu0w2XvzU4IrvTeBNs/Kj/FCmtcEXGz7Tic/veQzliJj6tEiAgoKianhQstpYmbPDStHU5Opqcw==", + "dev": true, + "requires": { + "jsonc-parser": "^3.0.0", + "onigasm": "^2.2.5", + "vscode-textmate": "5.2.0" + } + }, + "side-channel": { + "version": "1.0.4", + "resolved": "https://registry.npmjs.org/side-channel/-/side-channel-1.0.4.tgz", + "integrity": "sha512-q5XPytqFEIKHkGdiMIrY10mvLRvnQh42/+GoBlFW3b2LXLE2xxJpZFdm94we0BaoV3RwJyGqg5wS7epxTv0Zvw==", + "dev": true, + "requires": { + "call-bind": "^1.0.0", + "get-intrinsic": "^1.0.2", + "object-inspect": "^1.9.0" + } + }, + "signal-exit": { + "version": "3.0.5", + "resolved": "https://registry.npmjs.org/signal-exit/-/signal-exit-3.0.5.tgz", + "integrity": "sha512-KWcOiKeQj6ZyXx7zq4YxSMgHRlod4czeBQZrPb8OKcohcqAXShm7E20kEMle9WBt26hFcAf0qLOcp5zmY7kOqQ==", + "dev": true + }, + "sinon": { + "version": "11.1.2", + "resolved": "https://registry.npmjs.org/sinon/-/sinon-11.1.2.tgz", + "integrity": "sha512-59237HChms4kg7/sXhiRcUzdSkKuydDeTiamT/jesUVHshBgL8XAmhgFo0GfK6RruMDM/iRSij1EybmMog9cJw==", + "dev": true, + "requires": { + "@sinonjs/commons": "^1.8.3", + "@sinonjs/fake-timers": "^7.1.2", + "@sinonjs/samsam": "^6.0.2", + "diff": "^5.0.0", + "nise": "^5.1.0", + "supports-color": "^7.2.0" + } + }, + "slash": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/slash/-/slash-3.0.0.tgz", + "integrity": "sha512-g9Q1haeby36OSStwb4ntCGGGaKsaVSjQ68fBxoQcutl5fS1vuY18H3wSt3jFyFtrkx+Kz0V1G85A4MyAdDMi2Q==", + "dev": true + }, + "source-map": { + "version": "0.5.7", + "resolved": "https://registry.npmjs.org/source-map/-/source-map-0.5.7.tgz", + "integrity": "sha1-igOdLRAh0i0eoUyA2OpGi6LvP8w=", + "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==", + "dev": true, + "requires": { + "buffer-from": "^1.0.0", + "source-map": "^0.6.0" + }, + "dependencies": { + "source-map": { + "version": "0.6.1", + "resolved": "https://registry.npmjs.org/source-map/-/source-map-0.6.1.tgz", + "integrity": "sha512-UjgapumWlbMhkBgzT7Ykc5YXUT46F0iKu8SGXq0bcwP5dz/h0Plj6enJqjz1Zbq2l5WaqYnrVbwWOWMyF3F47g==", + "dev": true + } + } + }, + "spawn-wrap": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/spawn-wrap/-/spawn-wrap-2.0.0.tgz", + "integrity": "sha512-EeajNjfN9zMnULLwhZZQU3GWBoFNkbngTUPfaawT4RkMiviTxcX0qfhVbGey39mfctfDHkWtuecgQ8NJcyQWHg==", + "dev": true, + "requires": { + "foreground-child": "^2.0.0", + "is-windows": "^1.0.2", + "make-dir": "^3.0.0", + "rimraf": "^3.0.0", + "signal-exit": "^3.0.2", + "which": "^2.0.1" + } + }, + "split-on-first": { + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/split-on-first/-/split-on-first-1.1.0.tgz", + "integrity": "sha512-43ZssAJaMusuKWL8sKUBQXHWOpq8d6CfN/u1p4gUzfJkM05C8rxTmYrkIPTXapZpORA6LkkzcUulJ8FqA7Uudw==", + "dev": true + }, + "sprintf-js": { + "version": "1.0.3", + "resolved": "https://registry.npmjs.org/sprintf-js/-/sprintf-js-1.0.3.tgz", + "integrity": "sha1-BOaSb2YolTVPPdAVIDYzuFcpfiw=", + "dev": true + }, + "strict-uri-encode": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/strict-uri-encode/-/strict-uri-encode-2.0.0.tgz", + "integrity": "sha1-ucczDHBChi9rFC3CdLvMWGbONUY=", + "dev": true + }, + "string_decoder": { + "version": "1.3.0", + "resolved": "https://registry.npmjs.org/string_decoder/-/string_decoder-1.3.0.tgz", + "integrity": "sha512-hkRX8U1WjJFd8LsDJ2yQ/wWWxaopEsABU1XfkM8A+j0+85JAGppt16cr1Whg6KIbb4okU6Mql6BOj+uup/wKeA==", + "dev": true, + "requires": { + "safe-buffer": "~5.2.0" + }, + "dependencies": { + "safe-buffer": { + "version": "5.2.1", + "resolved": "https://registry.npmjs.org/safe-buffer/-/safe-buffer-5.2.1.tgz", + "integrity": "sha512-rp3So07KcdmmKbGvgaNxQSJr7bGVSVk5S9Eq1F+ppbRo70+YeaDxkw5Dd8NPN+GD6bjnYm2VuPuCXmpuYvmCXQ==", + "dev": true + } + } + }, + "string-width": { + "version": "4.2.3", + "resolved": "https://registry.npmjs.org/string-width/-/string-width-4.2.3.tgz", + "integrity": "sha512-wKyQRQpjJ0sIp62ErSZdGsjMJWsap5oRNihHhu6G7JVO/9jIB6UyevL+tXuOqrng8j/cxKTWyWUwvSTriiZz/g==", + "dev": true, + "requires": { + "emoji-regex": "^8.0.0", + "is-fullwidth-code-point": "^3.0.0", + "strip-ansi": "^6.0.1" + } + }, + "strip-ansi": { + "version": "6.0.1", + "resolved": "https://registry.npmjs.org/strip-ansi/-/strip-ansi-6.0.1.tgz", + "integrity": "sha512-Y38VPSHcqkFrCpFnQ9vuSXmquuv5oXOKpGeT6aGrr3o3Gc9AlVa6JBfUSOCnbxGGZF+/0ooI7KrPuUSztUdU5A==", + "dev": true, + "requires": { + "ansi-regex": "^5.0.1" + } + }, + "strip-bom": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/strip-bom/-/strip-bom-4.0.0.tgz", + "integrity": "sha512-3xurFv5tEgii33Zi8Jtp55wEIILR9eh34FAW00PZf+JnSsTmV/ioewSgQl97JHvgjoRGwPShsWm+IdrxB35d0w==", + "dev": true + }, + "strip-final-newline": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/strip-final-newline/-/strip-final-newline-2.0.0.tgz", + "integrity": "sha512-BrpvfNAE3dcvq7ll3xVumzjKjZQ5tI1sEUIKr3Uoks0XUl45St3FlatVqef9prk4jRDzhW6WZg+3bk93y6pLjA==", + "dev": true + }, + "strip-json-comments": { + "version": "3.1.1", + "resolved": "https://registry.npmjs.org/strip-json-comments/-/strip-json-comments-3.1.1.tgz", + "integrity": "sha512-6fPc+R4ihwqP6N/aIv2f1gMH8lOVtWQHoqC4yK6oSDVVocumAsfCqjkXnqiYMhmMwS/mEHLp7Vehlt3ql6lEig==", + "dev": true + }, + "supports-color": { + "version": "7.2.0", + "resolved": "https://registry.npmjs.org/supports-color/-/supports-color-7.2.0.tgz", + "integrity": "sha512-qpCAvRl9stuOHveKsn7HncJRvv501qIacKzQlO/+Lwxc9+0q2wLyv4Dfvt80/DPn2pqOBsJdDiogXGR9+OvwRw==", + "dev": true, + "requires": { + "has-flag": "^4.0.0" + } + }, + "test-exclude": { + "version": "6.0.0", + "resolved": "https://registry.npmjs.org/test-exclude/-/test-exclude-6.0.0.tgz", + "integrity": "sha512-cAGWPIyOHU6zlmg88jwm7VRyXnMN7iV68OGAbYDk/Mh/xC/pzVPlQtY6ngoIH/5/tciuhGfvESU8GrHrcxD56w==", + "dev": true, + "requires": { + "@istanbuljs/schema": "^0.1.2", + "glob": "^7.1.4", + "minimatch": "^3.0.4" + } + }, + "text-table": { + "version": "0.2.0", + "resolved": "https://registry.npmjs.org/text-table/-/text-table-0.2.0.tgz", + "integrity": "sha1-f17oI66AUgfACvLfSoTsP8+lcLQ=", + "dev": true + }, + "through": { + "version": "2.3.8", + "resolved": "https://registry.npmjs.org/through/-/through-2.3.8.tgz", + "integrity": "sha1-DdTJ/6q8NXlgsbckEV1+Doai4fU=", + "dev": true + }, + "tmp": { + "version": "0.0.33", + "resolved": "https://registry.npmjs.org/tmp/-/tmp-0.0.33.tgz", + "integrity": "sha512-jRCJlojKnZ3addtTOjdIqoRuPEKBvNXcGYqzO6zWZX8KfKEpnGY5jfggJQ3EjKuu8D4bJRr0y+cYJFmYbImXGw==", + "dev": true, + "requires": { + "os-tmpdir": "~1.0.2" + } + }, + "to-fast-properties": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/to-fast-properties/-/to-fast-properties-2.0.0.tgz", + "integrity": "sha1-3F5pjL0HkmW8c+A3doGk5Og/YW4=", + "dev": true + }, + "to-readable-stream": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/to-readable-stream/-/to-readable-stream-1.0.0.tgz", + "integrity": "sha512-Iq25XBt6zD5npPhlLVXGFN3/gyR2/qODcKNNyTMd4vbm39HUaOiAM4PMq0eMVC/Tkxz+Zjdsc55g9yyz+Yq00Q==", + "dev": true + }, + "to-regex-range": { + "version": "5.0.1", + "resolved": "https://registry.npmjs.org/to-regex-range/-/to-regex-range-5.0.1.tgz", + "integrity": "sha512-65P7iz6X5yEr1cwcgvQxbbIw7Uk3gOy5dIdtZ4rDveLqhrdJP+Li/Hx6tyK0NEb+2GCyneCMJiGqrADCSNk8sQ==", + "dev": true, + "requires": { + "is-number": "^7.0.0" + } + }, + "tr46": { + "version": "0.0.3", + "resolved": "https://registry.npmjs.org/tr46/-/tr46-0.0.3.tgz", + "integrity": "sha1-gYT9NH2snNwYWZLzpmIuFLnZq2o=", + "dev": true + }, + "ts-node": { + "version": "10.4.0", + "resolved": "https://registry.npmjs.org/ts-node/-/ts-node-10.4.0.tgz", + "integrity": "sha512-g0FlPvvCXSIO1JDF6S232P5jPYqBkRL9qly81ZgAOSU7rwI0stphCgd2kLiCrU9DjQCrJMWEqcNSjQL02s6d8A==", + "dev": true, + "requires": { + "@cspotcode/source-map-support": "0.7.0", + "@tsconfig/node10": "^1.0.7", + "@tsconfig/node12": "^1.0.7", + "@tsconfig/node14": "^1.0.0", + "@tsconfig/node16": "^1.0.2", + "acorn": "^8.4.1", + "acorn-walk": "^8.1.1", + "arg": "^4.1.0", + "create-require": "^1.1.0", + "diff": "^4.0.1", + "make-error": "^1.1.1", + "yn": "3.1.1" + }, + "dependencies": { + "diff": { + "version": "4.0.2", + "resolved": "https://registry.npmjs.org/diff/-/diff-4.0.2.tgz", + "integrity": "sha512-58lmxKSA4BNyLz+HHMUzlOEpg09FV+ev6ZMe3vJihgdxzgcwZ8VoEEPmALCZG9LmqfVoNMMKpttIYTVG6uDY7A==", + "dev": true + } + } + }, + "tslib": { + "version": "2.1.0", + "resolved": "https://registry.npmjs.org/tslib/-/tslib-2.1.0.tgz", + "integrity": "sha512-hcVC3wYEziELGGmEEXue7D75zbwIIVUMWAVbHItGPx0ziyXxrOMQx4rQEVEV45Ut/1IotuEvwqPopzIOkDMf0A==", + "dev": true + }, + "tsutils": { + "version": "3.21.0", + "resolved": "https://registry.npmjs.org/tsutils/-/tsutils-3.21.0.tgz", + "integrity": "sha512-mHKK3iUXL+3UF6xL5k0PEhKRUBKPBCv/+RkEOpjRWxxx27KKRBmmA60A9pgOUvMi8GKhRMPEmjBRPzs2W7O1OA==", + "dev": true, + "requires": { + "tslib": "^1.8.1" + }, + "dependencies": { + "tslib": { + "version": "1.14.1", + "resolved": "https://registry.npmjs.org/tslib/-/tslib-1.14.1.tgz", + "integrity": "sha512-Xni35NKzjgMrwevysHTCArtLDpPvye8zV/0E4EyYn43P7/7qvQwPh9BGkHewbMulVntbigmcT7rdX3BNo9wRJg==", + "dev": true + } + } + }, + "type-check": { + "version": "0.4.0", + "resolved": "https://registry.npmjs.org/type-check/-/type-check-0.4.0.tgz", + "integrity": "sha512-XleUoc9uwGXqjWwXaUTZAmzMcFZ5858QA2vvx1Ur5xIcixXIP+8LnFDgRplU30us6teqdlskFfu+ae4K79Ooew==", + "dev": true, + "requires": { + "prelude-ls": "^1.2.1" + } + }, + "type-detect": { + "version": "4.0.8", + "resolved": "https://registry.npmjs.org/type-detect/-/type-detect-4.0.8.tgz", + "integrity": "sha512-0fr/mIH1dlO+x7TlcMy+bIDqKPsw/70tVyeHW787goQjhmqaZe10uwLujubK9q9Lg6Fiho1KUKDYz0Z7k7g5/g==", + "dev": true + }, + "type-fest": { + "version": "0.20.2", + "resolved": "https://registry.npmjs.org/type-fest/-/type-fest-0.20.2.tgz", + "integrity": "sha512-Ne+eE4r0/iWnpAxD852z3A+N0Bt5RN//NjJwRd2VFHEmrywxf5vsZlh4R6lixl6B+wz/8d+maTSAkN1FIkI3LQ==", + "dev": true + }, + "typedarray-to-buffer": { + "version": "3.1.5", + "resolved": "https://registry.npmjs.org/typedarray-to-buffer/-/typedarray-to-buffer-3.1.5.tgz", + "integrity": "sha512-zdu8XMNEDepKKR+XYOXAVPtWui0ly0NtohUscw+UmaHiAWT8hrV1rr//H6V+0DvJ3OQ19S979M0laLfX8rm82Q==", + "dev": true, + "requires": { + "is-typedarray": "^1.0.0" + } + }, + "typedoc": { + "version": "0.22.8", + "resolved": "https://registry.npmjs.org/typedoc/-/typedoc-0.22.8.tgz", + "integrity": "sha512-92S+YzyhospdXN5rnkYUTgirdTYqNWY7NP9vco+IqQQoiSXzVSUsawVro+tMyEEsWUS7EMaJ2YOjB9uE0CBi6A==", + "dev": true, + "requires": { + "glob": "^7.2.0", + "lunr": "^2.3.9", + "marked": "^3.0.8", + "minimatch": "^3.0.4", + "shiki": "^0.9.12" + }, + "dependencies": { + "glob": { + "version": "7.2.0", + "resolved": "https://registry.npmjs.org/glob/-/glob-7.2.0.tgz", + "integrity": "sha512-lmLf6gtyrPq8tTjSmrO94wBeQbFR3HbLHbuyD69wuyQkImp2hWqMGB47OX65FBkPffO641IP9jWa1z4ivqG26Q==", + "dev": true, + "requires": { + "fs.realpath": "^1.0.0", + "inflight": "^1.0.4", + "inherits": "2", + "minimatch": "^3.0.4", + "once": "^1.3.0", + "path-is-absolute": "^1.0.0" + } + } + } + }, + "typedoc-github-wiki-theme": { + "version": "0.6.0", + "resolved": "https://registry.npmjs.org/typedoc-github-wiki-theme/-/typedoc-github-wiki-theme-0.6.0.tgz", + "integrity": "sha512-uHhR7PwAZ4JgO0KzlLocWSvMqKsSZ/wxUQYGKskIepzsotPAQcAWnSSnGi6gdkSw8UAfIIppdD7H1AmI39962w==", + "dev": true, + "requires": {} + }, + "typedoc-plugin-markdown": { + "version": "3.11.3", + "resolved": "https://registry.npmjs.org/typedoc-plugin-markdown/-/typedoc-plugin-markdown-3.11.3.tgz", + "integrity": "sha512-rWiHbEIe0oZetDIsBR24XJVxGOJ91kDcHoj2KhFKxCLoJGX659EKBQkHne9QJ4W2stGhu1fRgFyQaouSBnxukA==", + "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==", + "dev": true + }, + "uglify-js": { + "version": "3.14.3", + "resolved": "https://registry.npmjs.org/uglify-js/-/uglify-js-3.14.3.tgz", + "integrity": "sha512-mic3aOdiq01DuSVx0TseaEzMIVqebMZ0Z3vaeDhFEh9bsc24hV1TFvN74reA2vs08D0ZWfNjAcJ3UbVLaBss+g==", + "dev": true, + "optional": true + }, + "unique-string": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/unique-string/-/unique-string-2.0.0.tgz", + "integrity": "sha512-uNaeirEPvpZWSgzwsPGtU2zVSTrn/8L5q/IexZmH0eH6SA73CmAA5U4GwORTxQAZs95TAXLNqeLoPPNO5gZfWg==", + "dev": true, + "requires": { + "crypto-random-string": "^2.0.0" + } + }, + "universal-user-agent": { + "version": "6.0.0", + "resolved": "https://registry.npmjs.org/universal-user-agent/-/universal-user-agent-6.0.0.tgz", + "integrity": "sha512-isyNax3wXoKaulPDZWHQqbmIx1k2tb9fb3GGDBRxCscfYV2Ch7WxPArBsFEG8s/safwXTT7H4QGhaIkTp9447w==", + "dev": true + }, + "update-notifier": { + "version": "5.1.0", + "resolved": "https://registry.npmjs.org/update-notifier/-/update-notifier-5.1.0.tgz", + "integrity": "sha512-ItnICHbeMh9GqUy31hFPrD1kcuZ3rpxDZbf4KUDavXwS0bW5m7SLbDQpGX3UYr072cbrF5hFUs3r5tUsPwjfHw==", + "dev": true, + "requires": { + "boxen": "^5.0.0", + "chalk": "^4.1.0", + "configstore": "^5.0.1", + "has-yarn": "^2.1.0", + "import-lazy": "^2.1.0", + "is-ci": "^2.0.0", + "is-installed-globally": "^0.4.0", + "is-npm": "^5.0.0", + "is-yarn-global": "^0.3.0", + "latest-version": "^5.1.0", + "pupa": "^2.1.1", + "semver": "^7.3.4", + "semver-diff": "^3.1.1", + "xdg-basedir": "^4.0.0" + }, + "dependencies": { + "ci-info": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/ci-info/-/ci-info-2.0.0.tgz", + "integrity": "sha512-5tK7EtrZ0N+OLFMthtqOj4fI2Jeb88C4CAZPu25LDVUgXJ0A3Js4PMGqrn0JU1W0Mh1/Z8wZzYPxqUrXeBboCQ==", + "dev": true + }, + "is-ci": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/is-ci/-/is-ci-2.0.0.tgz", + "integrity": "sha512-YfJT7rkpQB0updsdHLGWrvhBJfcfzNNawYDNIyQXJz0IViGf75O8EBPKSdvw2rF+LGCsX4FZ8tcr3b19LcZq4w==", + "dev": true, + "requires": { + "ci-info": "^2.0.0" + } + } + } + }, + "uri-js": { + "version": "4.4.1", + "resolved": "https://registry.npmjs.org/uri-js/-/uri-js-4.4.1.tgz", + "integrity": "sha512-7rKUyy33Q1yc98pQ1DAmLtwX109F7TIfWlW1Ydo8Wl1ii1SeHieeh0HHfPeL2fMXK6z0s8ecKs9frCuLJvndBg==", + "dev": true, + "requires": { + "punycode": "^2.1.0" + } + }, + "url-join": { + "version": "4.0.1", + "resolved": "https://registry.npmjs.org/url-join/-/url-join-4.0.1.tgz", + "integrity": "sha512-jk1+QP6ZJqyOiuEI9AEWQfju/nB2Pw466kbA0LEZljHwKeMgd9WrAEgEGxjPDD2+TNbbb37rTyhEfrCXfuKXnA==", + "dev": true + }, + "url-parse-lax": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/url-parse-lax/-/url-parse-lax-3.0.0.tgz", + "integrity": "sha1-FrXK/Afb42dsGxmZF3gj1lA6yww=", + "dev": true, + "requires": { + "prepend-http": "^2.0.0" + } + }, + "util-deprecate": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/util-deprecate/-/util-deprecate-1.0.2.tgz", + "integrity": "sha1-RQ1Nyfpw3nMnYvvS1KKJgUGaDM8=", + "dev": true + }, + "uuid": { + "version": "3.4.0", + "resolved": "https://registry.npmjs.org/uuid/-/uuid-3.4.0.tgz", + "integrity": "sha512-HjSDRw6gZE5JMggctHBcjVak08+KEVhSIiDzFnT9S9aegmp85S/bReBVTb4QTFaRNptJ9kuYaNhnbNEOkbKb/A==", + "dev": true + }, + "v8-compile-cache": { + "version": "2.3.0", + "resolved": "https://registry.npmjs.org/v8-compile-cache/-/v8-compile-cache-2.3.0.tgz", + "integrity": "sha512-l8lCEmLcLYZh4nbunNZvQCJc5pv7+RCwa8q/LdUx8u7lsWvPDKmpodJAJNwkAhJC//dFY48KuIEmjtd4RViDrA==", + "dev": true + }, + "vscode-textmate": { + "version": "5.2.0", + "resolved": "https://registry.npmjs.org/vscode-textmate/-/vscode-textmate-5.2.0.tgz", + "integrity": "sha512-Uw5ooOQxRASHgu6C7GVvUxisKXfSgW4oFlO+aa+PAkgmH89O3CXxEEzNRNtHSqtXFTl0nAC1uYj0GMSH27uwtQ==", + "dev": true + }, + "wcwidth": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/wcwidth/-/wcwidth-1.0.1.tgz", + "integrity": "sha1-8LDc+RW8X/FSivrbLA4XtTLaL+g=", + "dev": true, + "requires": { + "defaults": "^1.0.3" + } + }, + "webidl-conversions": { + "version": "3.0.1", + "resolved": "https://registry.npmjs.org/webidl-conversions/-/webidl-conversions-3.0.1.tgz", + "integrity": "sha1-JFNCdeKnvGvnvIZhHMFq4KVlSHE=", + "dev": true + }, + "whatwg-url": { + "version": "5.0.0", + "resolved": "https://registry.npmjs.org/whatwg-url/-/whatwg-url-5.0.0.tgz", + "integrity": "sha1-lmRU6HZUYuN2RNNib2dCzotwll0=", + "dev": true, + "requires": { + "tr46": "~0.0.3", + "webidl-conversions": "^3.0.0" + } + }, + "which": { + "version": "2.0.2", + "resolved": "https://registry.npmjs.org/which/-/which-2.0.2.tgz", + "integrity": "sha512-BLI3Tl1TW3Pvl70l3yq3Y64i+awpwXqsGBYWkkqMtnbXgrMD+yj7rhW0kuEDxzJaYXGjEW5ogapKNMEKNMjibA==", + "dev": true, + "requires": { + "isexe": "^2.0.0" + } + }, + "which-module": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/which-module/-/which-module-2.0.0.tgz", + "integrity": "sha1-2e8H3Od7mQK4o6j6SzHD4/fm6Ho=", + "dev": true + }, + "widest-line": { + "version": "3.1.0", + "resolved": "https://registry.npmjs.org/widest-line/-/widest-line-3.1.0.tgz", + "integrity": "sha512-NsmoXalsWVDMGupxZ5R08ka9flZjjiLvHVAWYOKtiKM8ujtZWr9cRffak+uSE48+Ob8ObalXpwyeUiyDD6QFgg==", + "dev": true, + "requires": { + "string-width": "^4.0.0" + } + }, + "windows-release": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/windows-release/-/windows-release-4.0.0.tgz", + "integrity": "sha512-OxmV4wzDKB1x7AZaZgXMVsdJ1qER1ed83ZrTYd5Bwq2HfJVg3DJS8nqlAG4sMoJ7mu8cuRmLEYyU13BKwctRAg==", + "dev": true, + "requires": { + "execa": "^4.0.2" + }, + "dependencies": { + "execa": { + "version": "4.1.0", + "resolved": "https://registry.npmjs.org/execa/-/execa-4.1.0.tgz", + "integrity": "sha512-j5W0//W7f8UxAn8hXVnwG8tLwdiUy4FJLcSupCg6maBYZDpyBvTApK7KyuI4bKj8KOh1r2YH+6ucuYtJv1bTZA==", + "dev": true, + "requires": { + "cross-spawn": "^7.0.0", + "get-stream": "^5.0.0", + "human-signals": "^1.1.1", + "is-stream": "^2.0.0", + "merge-stream": "^2.0.0", + "npm-run-path": "^4.0.0", + "onetime": "^5.1.0", + "signal-exit": "^3.0.2", + "strip-final-newline": "^2.0.0" + } + }, + "get-stream": { + "version": "5.2.0", + "resolved": "https://registry.npmjs.org/get-stream/-/get-stream-5.2.0.tgz", + "integrity": "sha512-nBF+F1rAZVCu/p7rjzgA+Yb4lfYXrpl7a6VmJrU8wF9I1CKvP/QwPNZHnOlwbTkY6dvtFIzFMSyQXbLoTQPRpA==", + "dev": true, + "requires": { + "pump": "^3.0.0" + } + }, + "human-signals": { + "version": "1.1.1", + "resolved": "https://registry.npmjs.org/human-signals/-/human-signals-1.1.1.tgz", + "integrity": "sha512-SEQu7vl8KjNL2eoGBLF3+wAjpsNfA9XMlXAYj/3EdaNfAlxKthD1xjEQfGOUhllCGGJVNY34bRr6lPINhNjyZw==", + "dev": true + } + } + }, + "word-wrap": { + "version": "1.2.3", + "resolved": "https://registry.npmjs.org/word-wrap/-/word-wrap-1.2.3.tgz", + "integrity": "sha512-Hz/mrNwitNRh/HUAtM/VT/5VH+ygD6DV7mYKZAtHOrbs8U7lvPS6xf7EJKMF0uW1KJCl0H701g3ZGus+muE5vQ==", + "dev": true + }, + "wordwrap": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/wordwrap/-/wordwrap-1.0.0.tgz", + "integrity": "sha1-J1hIEIkUVqQXHI0CJkQa3pDLyus=", + "dev": true + }, + "workerpool": { + "version": "6.1.5", + "resolved": "https://registry.npmjs.org/workerpool/-/workerpool-6.1.5.tgz", + "integrity": "sha512-XdKkCK0Zqc6w3iTxLckiuJ81tiD/o5rBE/m+nXpRCB+/Sq4DqkfXZ/x0jW02DG1tGsfUGXbTJyZDP+eu67haSw==", + "dev": true + }, + "wrap-ansi": { + "version": "7.0.0", + "resolved": "https://registry.npmjs.org/wrap-ansi/-/wrap-ansi-7.0.0.tgz", + "integrity": "sha512-YVGIj2kamLSTxw6NsZjoBxfSwsn0ycdesmc4p+Q21c5zPuZ1pl+NfxVdxPtdHvmNVOQ6XSYG4AUtyt/Fi7D16Q==", + "dev": true, + "requires": { + "ansi-styles": "^4.0.0", + "string-width": "^4.1.0", + "strip-ansi": "^6.0.0" + } + }, + "wrappy": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/wrappy/-/wrappy-1.0.2.tgz", + "integrity": "sha1-tSQ9jz7BqjXxNkYFvA0QNuMKtp8=", + "dev": true + }, + "write-file-atomic": { + "version": "3.0.3", + "resolved": "https://registry.npmjs.org/write-file-atomic/-/write-file-atomic-3.0.3.tgz", + "integrity": "sha512-AvHcyZ5JnSfq3ioSyjrBkH9yW4m7Ayk8/9My/DD9onKeu/94fwrMocemO2QAJFAlnnDN+ZDS+ZjAR5ua1/PV/Q==", + "dev": true, + "requires": { + "imurmurhash": "^0.1.4", + "is-typedarray": "^1.0.0", + "signal-exit": "^3.0.2", + "typedarray-to-buffer": "^3.1.5" + } + }, + "xdg-basedir": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/xdg-basedir/-/xdg-basedir-4.0.0.tgz", + "integrity": "sha512-PSNhEJDejZYV7h50BohL09Er9VaIefr2LMAf3OEmpCkjOi34eYyQYAXUTjEQtZJTKcF0E2UKTh+osDLsgNim9Q==", + "dev": true + }, + "y18n": { + "version": "5.0.8", + "resolved": "https://registry.npmjs.org/y18n/-/y18n-5.0.8.tgz", + "integrity": "sha512-0pfFzegeDWJHJIAmTLRP2DwHjdF5s7jo9tuztdQxAhINCdvS+3nGINqPd00AphqJR/0LhANUS6/+7SCb98YOfA==", + "dev": true + }, + "yallist": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/yallist/-/yallist-4.0.0.tgz", + "integrity": "sha512-3wdGidZyq5PB084XLES5TpOSRA3wjXAlIWMhum2kRcv/41Sn2emQ0dycQW4uZXLejwKvg6EsvbdlVL+FYEct7A==" + }, + "yaml": { + "version": "1.10.2", + "resolved": "https://registry.npmjs.org/yaml/-/yaml-1.10.2.tgz", + "integrity": "sha512-r3vXyErRCYJ7wg28yvBY5VSoAF8ZvlcW9/BwUzEtUsjvX/DKs24dIkuwjtuprwJJHsbyUbLApepYTR1BN4uHrg==", + "dev": true + }, + "yargs": { + "version": "17.2.1", + "resolved": "https://registry.npmjs.org/yargs/-/yargs-17.2.1.tgz", + "integrity": "sha512-XfR8du6ua4K6uLGm5S6fA+FIJom/MdJcFNVY8geLlp2v8GYbOXD4EB1tPNZsRn4vBzKGMgb5DRZMeWuFc2GO8Q==", + "dev": true, + "requires": { + "cliui": "^7.0.2", + "escalade": "^3.1.1", + "get-caller-file": "^2.0.5", + "require-directory": "^2.1.1", + "string-width": "^4.2.0", + "y18n": "^5.0.5", + "yargs-parser": "^20.2.2" + } + }, + "yargs-parser": { + "version": "20.2.4", + "resolved": "https://registry.npmjs.org/yargs-parser/-/yargs-parser-20.2.4.tgz", + "integrity": "sha512-WOkpgNhPTlE73h4VFAFsOnomJVaovO8VqLDzy5saChRBFQFBoMYirowyW+Q9HB4HFF4Z7VZTiG3iSzJJA29yRA==", + "dev": true + }, + "yargs-unparser": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/yargs-unparser/-/yargs-unparser-2.0.0.tgz", + "integrity": "sha512-7pRTIA9Qc1caZ0bZ6RYRGbHJthJWuakf+WmHK0rVeLkNrrGhfoabBNdue6kdINI6r4if7ocq9aD/n7xwKOdzOA==", + "dev": true, + "requires": { + "camelcase": "^6.0.0", + "decamelize": "^4.0.0", + "flat": "^5.0.2", + "is-plain-obj": "^2.1.0" + }, + "dependencies": { + "camelcase": { + "version": "6.2.0", + "resolved": "https://registry.npmjs.org/camelcase/-/camelcase-6.2.0.tgz", + "integrity": "sha512-c7wVvbw3f37nuobQNtgsgG9POC9qMbNuMQmTCqZv23b6MIz0fcYpBiOlv9gEN/hdLdnZTDQhg6e9Dq5M1vKvfg==", + "dev": true + }, + "decamelize": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/decamelize/-/decamelize-4.0.0.tgz", + "integrity": "sha512-9iE1PgSik9HeIIw2JO94IidnE3eBoQrFJ3w7sFuzSX4DpmZ3v5sZpUiV5Swcf6mQEF+Y0ru8Neo+p+nyh2J+hQ==", + "dev": true + } + } + }, + "yn": { + "version": "3.1.1", + "resolved": "https://registry.npmjs.org/yn/-/yn-3.1.1.tgz", + "integrity": "sha512-Ux4ygGWsu2c7isFWe8Yu1YluJmqVhxqK2cLXNQA5AcC3QfbGNpM7fu0Y8b/z16pXLnFxZYvWhd3fhBY9DLmC6Q==", + "dev": true + }, + "yocto-queue": { + "version": "0.1.0", + "resolved": "https://registry.npmjs.org/yocto-queue/-/yocto-queue-0.1.0.tgz", + "integrity": "sha512-rVksvsnNCdJ/ohGc6xgPwyN8eheCxsiLM8mxuE/t/mOVqJewPuO1miLpTHQiRgTKCLexL4MeAFVagts7HmNZ2Q==", + "dev": true + } + } +} From 162ac4ce64c39db42066f767d908e90e9ad82387 Mon Sep 17 00:00:00 2001 From: leibale Date: Tue, 9 Nov 2021 18:14:32 -0500 Subject: [PATCH 0934/1748] update CONTRIBUTING.md --- CONTRIBUTING.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/CONTRIBUTING.md b/CONTRIBUTING.md index a041584e6fd..0243cc19359 100644 --- a/CONTRIBUTING.md +++ b/CONTRIBUTING.md @@ -45,7 +45,7 @@ A huge thank you to the original author of Node Redis, [Matthew Ranney](https:// Node Redis has a full test suite with coverage setup. -To run the tests, run `npm install` to install dependencies, then run `npm test`. +To run the tests, run `npm install` to install dependencies, then run `npm run build:tests-tools && npm test`. Note that the test suite assumes that [`docker`](https://www.docker.com/) is installed in your environment. From 3ad8a227eb2d98b99a515696a66b7c7bb12c95af Mon Sep 17 00:00:00 2001 From: leibale Date: Tue, 9 Nov 2021 18:24:01 -0500 Subject: [PATCH 0935/1748] update examples --- examples/README.md | 17 +++++++++-------- examples/search+json.js | 2 +- 2 files changed, 10 insertions(+), 9 deletions(-) diff --git a/examples/README.md b/examples/README.md index 9b285315219..aef0b38bdbb 100644 --- a/examples/README.md +++ b/examples/README.md @@ -2,13 +2,14 @@ This folder contains example scripts showing how to use Node Redis in different scenarios. -| File Name | Description | -|-----------------------------|--------------------------------------| -| `connect-as-acl-user.js` | Connect to Redis 6 using an ACL user | -| `blocking-list-pop.js` | Block until an element is pushed to a list | -| `lua-multi-incr.js` | Define a custom lua script that allows you to perform INCRBY on multiple keys | -| `command-with-modifiers.js` | Define a script that allows to run a command with several modifiers | -| `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 | +| `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 | ## Contributing @@ -46,7 +47,7 @@ When adding a new example, please follow these guidelines: * Leave an empty line at the end of your `.js` file * Update this `README.md` file to add your example to the table -Use [connect-as-acl-user.js](connect-as-acl-user.js) as a guide to develop a well formatted example script. +Use [connect-as-acl-user.js](./connect-as-acl-user.js) as a guide to develop a well formatted example script. ### Example Template diff --git a/examples/search+json.js b/examples/search+json.js index 43eaa2a158d..647daab6231 100644 --- a/examples/search+json.js +++ b/examples/search+json.js @@ -1,4 +1,4 @@ -// RediSearch & ReJSON example +// Use Redis Search and Redis JSON import { createClient, SchemaFieldTypes, AggregateGroupByReducers, AggregateSteps } from 'redis'; From 0e70f245c8514c5843556d183465d7c16343c4e9 Mon Sep 17 00:00:00 2001 From: leibale Date: Tue, 9 Nov 2021 19:32:15 -0500 Subject: [PATCH 0936/1748] uncomment some tests --- packages/client/lib/client/index.spec.ts | 20 ++++++++++---------- packages/client/lib/commands/COMMAND.spec.ts | 6 +++--- 2 files changed, 13 insertions(+), 13 deletions(-) diff --git a/packages/client/lib/client/index.spec.ts b/packages/client/lib/client/index.spec.ts index 21abe8a25f1..51dded18b1f 100644 --- a/packages/client/lib/client/index.spec.ts +++ b/packages/client/lib/client/index.spec.ts @@ -444,16 +444,16 @@ describe('Client', () => { } }); - // testUtils.testWithClient('executeIsolated', async client => { - // await client.sendCommand(['CLIENT', 'SETNAME', 'client']); - - // assert.equal( - // await client.executeIsolated(isolatedClient => - // isolatedClient.sendCommand(['CLIENT', 'GETNAME']) - // ), - // null - // ); - // }, GLOBAL.SERVERS.OPEN); + testUtils.testWithClient('executeIsolated', async client => { + await client.sendCommand(['CLIENT', 'SETNAME', 'client']); + + assert.equal( + await client.executeIsolated(isolatedClient => + isolatedClient.sendCommand(['CLIENT', 'GETNAME']) + ), + null + ); + }, GLOBAL.SERVERS.OPEN); async function killClient(client: RedisClientType): Promise { const onceErrorPromise = once(client, 'error'); diff --git a/packages/client/lib/commands/COMMAND.spec.ts b/packages/client/lib/commands/COMMAND.spec.ts index 04ceab2a07c..baad79845ab 100644 --- a/packages/client/lib/commands/COMMAND.spec.ts +++ b/packages/client/lib/commands/COMMAND.spec.ts @@ -11,7 +11,7 @@ describe('COMMAND', () => { ); }); - // testUtils.testWithClient('client.command', async client => { - // assertPingCommand((await client.command()).find(command => command.name === 'ping')); - // }, GLOBAL.SERVERS.OPEN); + testUtils.testWithClient('client.command', async client => { + assertPingCommand((await client.command()).find(command => command.name === 'ping')); + }, GLOBAL.SERVERS.OPEN); }); From 57539e7c3f1b72910c594ffa236a83daed8ca293 Mon Sep 17 00:00:00 2001 From: leibale Date: Tue, 9 Nov 2021 19:32:31 -0500 Subject: [PATCH 0937/1748] fix test-utils --- packages/test-utils/lib/dockers.ts | 6 +++--- packages/test-utils/lib/index.ts | 6 +++--- 2 files changed, 6 insertions(+), 6 deletions(-) diff --git a/packages/test-utils/lib/dockers.ts b/packages/test-utils/lib/dockers.ts index 29f93b9c825..28bd1e49057 100644 --- a/packages/test-utils/lib/dockers.ts +++ b/packages/test-utils/lib/dockers.ts @@ -1,8 +1,8 @@ import { createConnection } from 'net'; import { once } from 'events'; -import { RedisModules, RedisScripts } from '@redis/client/dist/lib/commands'; -import RedisClient, { RedisClientType } from '@redis/client/dist/lib/client'; -import { promiseTimeout } from '@redis/client/dist/lib/utils'; +import { RedisModules, RedisScripts } from '@redis/client/lib/commands'; +import RedisClient, { RedisClientType } from '@redis/client/lib/client'; +import { promiseTimeout } from '@redis/client/lib/utils'; import path from 'path'; import { promisify } from 'util'; import { exec } from 'child_process'; diff --git a/packages/test-utils/lib/index.ts b/packages/test-utils/lib/index.ts index e553e132894..f9a1fc1dbd9 100644 --- a/packages/test-utils/lib/index.ts +++ b/packages/test-utils/lib/index.ts @@ -1,6 +1,6 @@ -import { RedisModules, RedisScripts } from '@redis/client/dist/lib/commands'; -import RedisClient, { RedisClientOptions, RedisClientType } from '@redis/client/dist/lib/client'; -import RedisCluster, { RedisClusterOptions, RedisClusterType } from '@redis/client/dist/lib/cluster'; +import { RedisModules, RedisScripts } from '@redis/client/lib/commands'; +import RedisClient, { RedisClientOptions, RedisClientType } from '@redis/client/lib/client'; +import RedisCluster, { RedisClusterOptions, RedisClusterType } from '@redis/client/lib/cluster'; import { RedisServerDockerConfig, spawnRedisServer, spawnRedisCluster } from './dockers'; import yargs from 'yargs'; import { hideBin } from 'yargs/helpers'; From 38426fe2a48ac950f3c1cd130f63b6ed539494d9 Mon Sep 17 00:00:00 2001 From: leibale Date: Tue, 9 Nov 2021 19:55:16 -0500 Subject: [PATCH 0938/1748] move "all-in-one" to root folder --- .github/workflows/tests.yml | 1 + .npmignore | 9 + examples/package.json | 2 +- examples/search+json.js | 2 - packages/all-in-one/index.ts => index.ts | 0 package-lock.json | 686 ++++++++++++----------- package.json | 32 +- packages/all-in-one/package.json | 27 - packages/all-in-one/tsconfig.json | 9 - packages/client/.npmignore | 1 - packages/client/package.json | 12 +- packages/client/tsconfig.json | 2 +- packages/json/tsconfig.json | 2 +- packages/search/tsconfig.json | 2 +- packages/test-utils/tsconfig.json | 2 +- tsconfig.base.json | 15 + tsconfig.json | 16 +- 17 files changed, 410 insertions(+), 410 deletions(-) create mode 100644 .npmignore rename packages/all-in-one/index.ts => index.ts (100%) delete mode 100644 packages/all-in-one/package.json delete mode 100644 packages/all-in-one/tsconfig.json create mode 100644 tsconfig.base.json diff --git a/.github/workflows/tests.yml b/.github/workflows/tests.yml index e85f7e1fd56..a0152cea869 100644 --- a/.github/workflows/tests.yml +++ b/.github/workflows/tests.yml @@ -27,6 +27,7 @@ jobs: - name: Update npm run: npm i -g npm + if: ${{ matrix.node-version <= 14.x }} - name: Install Packages run: npm ci diff --git a/.npmignore b/.npmignore new file mode 100644 index 00000000000..0689fb63fe4 --- /dev/null +++ b/.npmignore @@ -0,0 +1,9 @@ +.github/ +.vscode/ +docs/ +examples/ +packages/ +.deepsource.toml +index.ts +tsconfig.base.json +tsconfig.json diff --git a/examples/package.json b/examples/package.json index 452d88cbe5d..65ba1442f7e 100644 --- a/examples/package.json +++ b/examples/package.json @@ -6,7 +6,7 @@ "private": true, "type": "module", "dependencies": { - "redis": "../packages/all-in-one" + "redis": "../" } } diff --git a/examples/search+json.js b/examples/search+json.js index 647daab6231..adc298289cd 100644 --- a/examples/search+json.js +++ b/examples/search+json.js @@ -7,8 +7,6 @@ async function searchPlusJson() { await client.connect(); - await client.flushAll(); - // Create an index await client.ft.create('users', { '$.name': { diff --git a/packages/all-in-one/index.ts b/index.ts similarity index 100% rename from packages/all-in-one/index.ts rename to index.ts diff --git a/package-lock.json b/package-lock.json index b9e9ed91405..2b4d4be9c61 100644 --- a/package-lock.json +++ b/package-lock.json @@ -1,15 +1,25 @@ { - "name": "redis-monorepo", + "name": "redis", + "version": "4.0.0-rc.3", "lockfileVersion": 2, "requires": true, "packages": { "": { - "name": "redis-monorepo", + "name": "redis", + "version": "4.0.0-rc.3", + "license": "MIT", "workspaces": [ "./packages/*" ], + "dependencies": { + "@redis/client": "^1.0.0-rc", + "@redis/json": "^1.0.0-rc", + "@redis/search": "^1.0.0-rc" + }, "devDependencies": { - "@tsconfig/node12": "^1.0.9" + "@tsconfig/node12": "^1.0.9", + "release-it": "^14.11.6", + "typescript": "^4.4.4" } }, "node_modules/@babel/code-frame": { @@ -332,15 +342,6 @@ "integrity": "sha1-p9BVi9icQveV3UIyj3QIMcpTvCU=", "dev": true }, - "node_modules/@babel/highlight/node_modules/escape-string-regexp": { - "version": "1.0.5", - "resolved": "https://registry.npmjs.org/escape-string-regexp/-/escape-string-regexp-1.0.5.tgz", - "integrity": "sha1-G2HAViGQqN/2rjuyzwIAyhMLhtQ=", - "dev": true, - "engines": { - "node": ">=0.8.0" - } - }, "node_modules/@babel/highlight/node_modules/has-flag": { "version": "3.0.0", "resolved": "https://registry.npmjs.org/has-flag/-/has-flag-3.0.0.tgz", @@ -480,6 +481,18 @@ "node": ">= 4" } }, + "node_modules/@eslint/eslintrc/node_modules/strip-json-comments": { + "version": "3.1.1", + "resolved": "https://registry.npmjs.org/strip-json-comments/-/strip-json-comments-3.1.1.tgz", + "integrity": "sha512-6fPc+R4ihwqP6N/aIv2f1gMH8lOVtWQHoqC4yK6oSDVVocumAsfCqjkXnqiYMhmMwS/mEHLp7Vehlt3ql6lEig==", + "dev": true, + "engines": { + "node": ">=8" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, "node_modules/@humanwhocodes/config-array": { "version": "0.6.0", "resolved": "https://registry.npmjs.org/@humanwhocodes/config-array/-/config-array-0.6.0.tgz", @@ -531,6 +544,15 @@ "sprintf-js": "~1.0.2" } }, + "node_modules/@istanbuljs/load-nyc-config/node_modules/camelcase": { + "version": "5.3.1", + "resolved": "https://registry.npmjs.org/camelcase/-/camelcase-5.3.1.tgz", + "integrity": "sha512-L28STB170nwWS63UjtlEOE3dldQApaJXZkOI1uMFfzf3rRuPegHaHesyee+YxQ+W6SvRDQV6UrdOdRiR153wJg==", + "dev": true, + "engines": { + "node": ">=6" + } + }, "node_modules/@istanbuljs/load-nyc-config/node_modules/find-up": { "version": "4.1.0", "resolved": "https://registry.npmjs.org/find-up/-/find-up-4.1.0.tgz", @@ -1246,18 +1268,6 @@ "url": "https://github.com/sponsors/sindresorhus" } }, - "node_modules/ansi-escapes/node_modules/type-fest": { - "version": "0.21.3", - "resolved": "https://registry.npmjs.org/type-fest/-/type-fest-0.21.3.tgz", - "integrity": "sha512-t0rzBq87m3fVcduHDUFhKmyyX+9eo6WQjZvf51Ea/M0Q7+T374Jp1aUiyUl0GKxp8M/OETVHSDvmkyPgvX+X2w==", - "dev": true, - "engines": { - "node": ">=10" - }, - "funding": { - "url": "https://github.com/sponsors/sindresorhus" - } - }, "node_modules/ansi-regex": { "version": "5.0.1", "resolved": "https://registry.npmjs.org/ansi-regex/-/ansi-regex-5.0.1.tgz", @@ -1423,10 +1433,10 @@ "url": "https://github.com/sponsors/sindresorhus" } }, - "node_modules/boxen/node_modules/camelcase": { - "version": "6.2.0", - "resolved": "https://registry.npmjs.org/camelcase/-/camelcase-6.2.0.tgz", - "integrity": "sha512-c7wVvbw3f37nuobQNtgsgG9POC9qMbNuMQmTCqZv23b6MIz0fcYpBiOlv9gEN/hdLdnZTDQhg6e9Dq5M1vKvfg==", + "node_modules/boxen/node_modules/type-fest": { + "version": "0.20.2", + "resolved": "https://registry.npmjs.org/type-fest/-/type-fest-0.20.2.tgz", + "integrity": "sha512-Ne+eE4r0/iWnpAxD852z3A+N0Bt5RN//NjJwRd2VFHEmrywxf5vsZlh4R6lixl6B+wz/8d+maTSAkN1FIkI3LQ==", "dev": true, "engines": { "node": ">=10" @@ -1596,12 +1606,15 @@ } }, "node_modules/camelcase": { - "version": "5.3.1", - "resolved": "https://registry.npmjs.org/camelcase/-/camelcase-5.3.1.tgz", - "integrity": "sha512-L28STB170nwWS63UjtlEOE3dldQApaJXZkOI1uMFfzf3rRuPegHaHesyee+YxQ+W6SvRDQV6UrdOdRiR153wJg==", + "version": "6.2.0", + "resolved": "https://registry.npmjs.org/camelcase/-/camelcase-6.2.0.tgz", + "integrity": "sha512-c7wVvbw3f37nuobQNtgsgG9POC9qMbNuMQmTCqZv23b6MIz0fcYpBiOlv9gEN/hdLdnZTDQhg6e9Dq5M1vKvfg==", "dev": true, "engines": { - "node": ">=6" + "node": ">=10" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" } }, "node_modules/caniuse-lite": { @@ -1657,18 +1670,6 @@ "fsevents": "~2.3.2" } }, - "node_modules/chokidar/node_modules/glob-parent": { - "version": "5.1.2", - "resolved": "https://registry.npmjs.org/glob-parent/-/glob-parent-5.1.2.tgz", - "integrity": "sha512-AOIgSQCepiJYwP3ARnGx+5VnTu2HBYdzbGP45eLw1vr3zB3vZLeyed1sC9hnbcOc9/SrMyM5RPQrkGz4aS9Zow==", - "dev": true, - "dependencies": { - "is-glob": "^4.0.1" - }, - "engines": { - "node": ">= 6" - } - }, "node_modules/ci-info": { "version": "3.2.0", "resolved": "https://registry.npmjs.org/ci-info/-/ci-info-3.2.0.tgz", @@ -1834,6 +1835,12 @@ "safe-buffer": "~5.1.1" } }, + "node_modules/convert-source-map/node_modules/safe-buffer": { + "version": "5.1.2", + "resolved": "https://registry.npmjs.org/safe-buffer/-/safe-buffer-5.1.2.tgz", + "integrity": "sha512-Gd2UZBJDkXlY7GbJxfsE8/nvKkUEU1G38c1siN6QP6a9PT9MmHB8GnpscSmMJSoF8LOIrt8ud/wPtojys4G6+g==", + "dev": true + }, "node_modules/cosmiconfig": { "version": "7.0.1", "resolved": "https://registry.npmjs.org/cosmiconfig/-/cosmiconfig-7.0.1.tgz", @@ -2132,15 +2139,12 @@ } }, "node_modules/escape-string-regexp": { - "version": "4.0.0", - "resolved": "https://registry.npmjs.org/escape-string-regexp/-/escape-string-regexp-4.0.0.tgz", - "integrity": "sha512-TtpcNJ3XAzx3Gq8sWRzJaVajRs0uVxA2YAkdb1jm2YkPz4G6egUFAyA3n5vtEIZefPk5Wa4UXbKuS5fKkJWdgA==", + "version": "1.0.5", + "resolved": "https://registry.npmjs.org/escape-string-regexp/-/escape-string-regexp-1.0.5.tgz", + "integrity": "sha1-G2HAViGQqN/2rjuyzwIAyhMLhtQ=", "dev": true, "engines": { - "node": ">=10" - }, - "funding": { - "url": "https://github.com/sponsors/sindresorhus" + "node": ">=0.8.0" } }, "node_modules/eslint": { @@ -2247,6 +2251,18 @@ "node": "^12.22.0 || ^14.17.0 || >=16.0.0" } }, + "node_modules/eslint/node_modules/escape-string-regexp": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/escape-string-regexp/-/escape-string-regexp-4.0.0.tgz", + "integrity": "sha512-TtpcNJ3XAzx3Gq8sWRzJaVajRs0uVxA2YAkdb1jm2YkPz4G6egUFAyA3n5vtEIZefPk5Wa4UXbKuS5fKkJWdgA==", + "dev": true, + "engines": { + "node": ">=10" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, "node_modules/eslint/node_modules/eslint-scope": { "version": "6.0.0", "resolved": "https://registry.npmjs.org/eslint-scope/-/eslint-scope-6.0.0.tgz", @@ -2269,6 +2285,18 @@ "node": ">=4.0" } }, + "node_modules/eslint/node_modules/glob-parent": { + "version": "6.0.2", + "resolved": "https://registry.npmjs.org/glob-parent/-/glob-parent-6.0.2.tgz", + "integrity": "sha512-XxwI8EOhVQgWp6iDL+3b0r86f4d6AX6zSU55HfB4ydCEuXLXc5FcYeOu+nnGftS4TEju/11rt4KJPTMgbfmv4A==", + "dev": true, + "dependencies": { + "is-glob": "^4.0.3" + }, + "engines": { + "node": ">=10.13.0" + } + }, "node_modules/eslint/node_modules/ignore": { "version": "4.0.6", "resolved": "https://registry.npmjs.org/ignore/-/ignore-4.0.6.tgz", @@ -2278,6 +2306,18 @@ "node": ">= 4" } }, + "node_modules/eslint/node_modules/strip-json-comments": { + "version": "3.1.1", + "resolved": "https://registry.npmjs.org/strip-json-comments/-/strip-json-comments-3.1.1.tgz", + "integrity": "sha512-6fPc+R4ihwqP6N/aIv2f1gMH8lOVtWQHoqC4yK6oSDVVocumAsfCqjkXnqiYMhmMwS/mEHLp7Vehlt3ql6lEig==", + "dev": true, + "engines": { + "node": ">=8" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, "node_modules/espree": { "version": "9.0.0", "resolved": "https://registry.npmjs.org/espree/-/espree-9.0.0.tgz", @@ -2424,18 +2464,6 @@ "node": ">=8" } }, - "node_modules/fast-glob/node_modules/glob-parent": { - "version": "5.1.2", - "resolved": "https://registry.npmjs.org/glob-parent/-/glob-parent-5.1.2.tgz", - "integrity": "sha512-AOIgSQCepiJYwP3ARnGx+5VnTu2HBYdzbGP45eLw1vr3zB3vZLeyed1sC9hnbcOc9/SrMyM5RPQrkGz4aS9Zow==", - "dev": true, - "dependencies": { - "is-glob": "^4.0.1" - }, - "engines": { - "node": ">= 6" - } - }, "node_modules/fast-json-stable-stringify": { "version": "2.1.0", "resolved": "https://registry.npmjs.org/fast-json-stable-stringify/-/fast-json-stable-stringify-2.1.0.tgz", @@ -2472,15 +2500,6 @@ "url": "https://github.com/sponsors/sindresorhus" } }, - "node_modules/figures/node_modules/escape-string-regexp": { - "version": "1.0.5", - "resolved": "https://registry.npmjs.org/escape-string-regexp/-/escape-string-regexp-1.0.5.tgz", - "integrity": "sha1-G2HAViGQqN/2rjuyzwIAyhMLhtQ=", - "dev": true, - "engines": { - "node": ">=0.8.0" - } - }, "node_modules/file-entry-cache": { "version": "6.0.1", "resolved": "https://registry.npmjs.org/file-entry-cache/-/file-entry-cache-6.0.1.tgz", @@ -2735,9 +2754,9 @@ } }, "node_modules/glob": { - "version": "7.1.7", - "resolved": "https://registry.npmjs.org/glob/-/glob-7.1.7.tgz", - "integrity": "sha512-OvD9ENzPLbegENnYP5UUfJIirTg4+XwMWGaQfQTY0JenxNvvIKP3U3/tAQSPIu/lHxXYSZmpXlUHeqAIdKzBLQ==", + "version": "7.2.0", + "resolved": "https://registry.npmjs.org/glob/-/glob-7.2.0.tgz", + "integrity": "sha512-lmLf6gtyrPq8tTjSmrO94wBeQbFR3HbLHbuyD69wuyQkImp2hWqMGB47OX65FBkPffO641IP9jWa1z4ivqG26Q==", "dev": true, "dependencies": { "fs.realpath": "^1.0.0", @@ -2755,15 +2774,15 @@ } }, "node_modules/glob-parent": { - "version": "6.0.2", - "resolved": "https://registry.npmjs.org/glob-parent/-/glob-parent-6.0.2.tgz", - "integrity": "sha512-XxwI8EOhVQgWp6iDL+3b0r86f4d6AX6zSU55HfB4ydCEuXLXc5FcYeOu+nnGftS4TEju/11rt4KJPTMgbfmv4A==", + "version": "5.1.2", + "resolved": "https://registry.npmjs.org/glob-parent/-/glob-parent-5.1.2.tgz", + "integrity": "sha512-AOIgSQCepiJYwP3ARnGx+5VnTu2HBYdzbGP45eLw1vr3zB3vZLeyed1sC9hnbcOc9/SrMyM5RPQrkGz4aS9Zow==", "dev": true, "dependencies": { - "is-glob": "^4.0.3" + "is-glob": "^4.0.1" }, "engines": { - "node": ">=10.13.0" + "node": ">= 6" } }, "node_modules/global-dirs": { @@ -2796,6 +2815,18 @@ "url": "https://github.com/sponsors/sindresorhus" } }, + "node_modules/globals/node_modules/type-fest": { + "version": "0.20.2", + "resolved": "https://registry.npmjs.org/type-fest/-/type-fest-0.20.2.tgz", + "integrity": "sha512-Ne+eE4r0/iWnpAxD852z3A+N0Bt5RN//NjJwRd2VFHEmrywxf5vsZlh4R6lixl6B+wz/8d+maTSAkN1FIkI3LQ==", + "dev": true, + "engines": { + "node": ">=10" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, "node_modules/globby": { "version": "11.0.4", "resolved": "https://registry.npmjs.org/globby/-/globby-11.0.4.tgz", @@ -3482,6 +3513,16 @@ "node": ">=8" } }, + "node_modules/istanbul-lib-processinfo/node_modules/uuid": { + "version": "3.4.0", + "resolved": "https://registry.npmjs.org/uuid/-/uuid-3.4.0.tgz", + "integrity": "sha512-HjSDRw6gZE5JMggctHBcjVak08+KEVhSIiDzFnT9S9aegmp85S/bReBVTb4QTFaRNptJ9kuYaNhnbNEOkbKb/A==", + "deprecated": "Please upgrade to version 7 or higher. Older versions may use Math.random() in certain circumstances, which is known to be problematic. See https://v8.dev/blog/math-random for details.", + "dev": true, + "bin": { + "uuid": "bin/uuid" + } + }, "node_modules/istanbul-lib-report": { "version": "3.0.0", "resolved": "https://registry.npmjs.org/istanbul-lib-report/-/istanbul-lib-report-3.0.0.tgz", @@ -3917,12 +3958,56 @@ "url": "https://opencollective.com/mochajs" } }, + "node_modules/mocha/node_modules/escape-string-regexp": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/escape-string-regexp/-/escape-string-regexp-4.0.0.tgz", + "integrity": "sha512-TtpcNJ3XAzx3Gq8sWRzJaVajRs0uVxA2YAkdb1jm2YkPz4G6egUFAyA3n5vtEIZefPk5Wa4UXbKuS5fKkJWdgA==", + "dev": true, + "engines": { + "node": ">=10" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/mocha/node_modules/glob": { + "version": "7.1.7", + "resolved": "https://registry.npmjs.org/glob/-/glob-7.1.7.tgz", + "integrity": "sha512-OvD9ENzPLbegENnYP5UUfJIirTg4+XwMWGaQfQTY0JenxNvvIKP3U3/tAQSPIu/lHxXYSZmpXlUHeqAIdKzBLQ==", + "dev": true, + "dependencies": { + "fs.realpath": "^1.0.0", + "inflight": "^1.0.4", + "inherits": "2", + "minimatch": "^3.0.4", + "once": "^1.3.0", + "path-is-absolute": "^1.0.0" + }, + "engines": { + "node": "*" + }, + "funding": { + "url": "https://github.com/sponsors/isaacs" + } + }, "node_modules/mocha/node_modules/ms": { "version": "2.1.3", "resolved": "https://registry.npmjs.org/ms/-/ms-2.1.3.tgz", "integrity": "sha512-6FlzubTLZG3J2a/NVCAleEhjzq5oxgHyaCU9yYXvcLsvoVaHJq/s5xXI6/XXP6tz7R9xAOtHnSO/tXtF3WRTlA==", "dev": true }, + "node_modules/mocha/node_modules/strip-json-comments": { + "version": "3.1.1", + "resolved": "https://registry.npmjs.org/strip-json-comments/-/strip-json-comments-3.1.1.tgz", + "integrity": "sha512-6fPc+R4ihwqP6N/aIv2f1gMH8lOVtWQHoqC4yK6oSDVVocumAsfCqjkXnqiYMhmMwS/mEHLp7Vehlt3ql6lEig==", + "dev": true, + "engines": { + "node": ">=8" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, "node_modules/mocha/node_modules/supports-color": { "version": "8.1.1", "resolved": "https://registry.npmjs.org/supports-color/-/supports-color-8.1.1.tgz", @@ -3956,6 +4041,15 @@ "node": ">=10" } }, + "node_modules/mocha/node_modules/yargs-parser": { + "version": "20.2.4", + "resolved": "https://registry.npmjs.org/yargs-parser/-/yargs-parser-20.2.4.tgz", + "integrity": "sha512-WOkpgNhPTlE73h4VFAFsOnomJVaovO8VqLDzy5saChRBFQFBoMYirowyW+Q9HB4HFF4Z7VZTiG3iSzJJA29yRA==", + "dev": true, + "engines": { + "node": ">=10" + } + }, "node_modules/ms": { "version": "2.1.2", "resolved": "https://registry.npmjs.org/ms/-/ms-2.1.2.tgz", @@ -4130,6 +4224,15 @@ "node": ">=8.9" } }, + "node_modules/nyc/node_modules/camelcase": { + "version": "5.3.1", + "resolved": "https://registry.npmjs.org/camelcase/-/camelcase-5.3.1.tgz", + "integrity": "sha512-L28STB170nwWS63UjtlEOE3dldQApaJXZkOI1uMFfzf3rRuPegHaHesyee+YxQ+W6SvRDQV6UrdOdRiR153wJg==", + "dev": true, + "engines": { + "node": ">=6" + } + }, "node_modules/nyc/node_modules/cliui": { "version": "6.0.0", "resolved": "https://registry.npmjs.org/cliui/-/cliui-6.0.0.tgz", @@ -5018,15 +5121,6 @@ "integrity": "sha512-JV/yugV2uzW5iMRSiZAyDtQd+nxtUnjeLt0acNdw98kKLrvuRVyB80tsREOE7yvGVgalhZ6RNXCmEHkUKBKxew==", "dev": true }, - "node_modules/rc/node_modules/strip-json-comments": { - "version": "2.0.1", - "resolved": "https://registry.npmjs.org/strip-json-comments/-/strip-json-comments-2.0.1.tgz", - "integrity": "sha1-PFMZQukIwml8DsNEhYwobHygpgo=", - "dev": true, - "engines": { - "node": ">=0.10.0" - } - }, "node_modules/readable-stream": { "version": "3.6.0", "resolved": "https://registry.npmjs.org/readable-stream/-/readable-stream-3.6.0.tgz", @@ -5065,10 +5159,6 @@ "node": ">= 0.10" } }, - "node_modules/redis": { - "resolved": "packages/all-in-one", - "link": true - }, "node_modules/redis-errors": { "version": "1.2.0", "resolved": "https://registry.npmjs.org/redis-errors/-/redis-errors-1.2.0.tgz", @@ -5167,24 +5257,6 @@ "node": ">=10" } }, - "node_modules/release-it/node_modules/uuid": { - "version": "8.3.2", - "resolved": "https://registry.npmjs.org/uuid/-/uuid-8.3.2.tgz", - "integrity": "sha512-+NYs2QeMWy+GWFOEm9xnn6HCDp0l7QBD7ml8zLUmJ+93Q5NF0NocErnwkTkXVFNiX3/fpC6afS8Dhb/gz7R7eg==", - "dev": true, - "bin": { - "uuid": "dist/bin/uuid" - } - }, - "node_modules/release-it/node_modules/yargs-parser": { - "version": "20.2.9", - "resolved": "https://registry.npmjs.org/yargs-parser/-/yargs-parser-20.2.9.tgz", - "integrity": "sha512-y11nGElTIV+CT3Zv9t7VKl+Q3hTQoT9a1Qzezhhl6Rp21gJ/IVTW7Z3y9EWXhuUBC2Shnf+DX0antecpAwSP8w==", - "dev": true, - "engines": { - "node": ">=10" - } - }, "node_modules/release-zalgo": { "version": "1.0.0", "resolved": "https://registry.npmjs.org/release-zalgo/-/release-zalgo-1.0.0.tgz", @@ -5338,10 +5410,24 @@ } }, "node_modules/safe-buffer": { - "version": "5.1.2", - "resolved": "https://registry.npmjs.org/safe-buffer/-/safe-buffer-5.1.2.tgz", - "integrity": "sha512-Gd2UZBJDkXlY7GbJxfsE8/nvKkUEU1G38c1siN6QP6a9PT9MmHB8GnpscSmMJSoF8LOIrt8ud/wPtojys4G6+g==", - "dev": true + "version": "5.2.1", + "resolved": "https://registry.npmjs.org/safe-buffer/-/safe-buffer-5.2.1.tgz", + "integrity": "sha512-rp3So07KcdmmKbGvgaNxQSJr7bGVSVk5S9Eq1F+ppbRo70+YeaDxkw5Dd8NPN+GD6bjnYm2VuPuCXmpuYvmCXQ==", + "dev": true, + "funding": [ + { + "type": "github", + "url": "https://github.com/sponsors/feross" + }, + { + "type": "patreon", + "url": "https://www.patreon.com/feross" + }, + { + "type": "consulting", + "url": "https://feross.org/support" + } + ] }, "node_modules/safer-buffer": { "version": "2.1.2", @@ -5574,26 +5660,6 @@ "safe-buffer": "~5.2.0" } }, - "node_modules/string_decoder/node_modules/safe-buffer": { - "version": "5.2.1", - "resolved": "https://registry.npmjs.org/safe-buffer/-/safe-buffer-5.2.1.tgz", - "integrity": "sha512-rp3So07KcdmmKbGvgaNxQSJr7bGVSVk5S9Eq1F+ppbRo70+YeaDxkw5Dd8NPN+GD6bjnYm2VuPuCXmpuYvmCXQ==", - "dev": true, - "funding": [ - { - "type": "github", - "url": "https://github.com/sponsors/feross" - }, - { - "type": "patreon", - "url": "https://www.patreon.com/feross" - }, - { - "type": "consulting", - "url": "https://feross.org/support" - } - ] - }, "node_modules/string-width": { "version": "4.2.3", "resolved": "https://registry.npmjs.org/string-width/-/string-width-4.2.3.tgz", @@ -5639,15 +5705,12 @@ } }, "node_modules/strip-json-comments": { - "version": "3.1.1", - "resolved": "https://registry.npmjs.org/strip-json-comments/-/strip-json-comments-3.1.1.tgz", - "integrity": "sha512-6fPc+R4ihwqP6N/aIv2f1gMH8lOVtWQHoqC4yK6oSDVVocumAsfCqjkXnqiYMhmMwS/mEHLp7Vehlt3ql6lEig==", - "dev": true, - "engines": { - "node": ">=8" - }, - "funding": { - "url": "https://github.com/sponsors/sindresorhus" + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/strip-json-comments/-/strip-json-comments-2.0.1.tgz", + "integrity": "sha1-PFMZQukIwml8DsNEhYwobHygpgo=", + "dev": true, + "engines": { + "node": ">=0.10.0" } }, "node_modules/supports-color": { @@ -5835,9 +5898,9 @@ } }, "node_modules/type-fest": { - "version": "0.20.2", - "resolved": "https://registry.npmjs.org/type-fest/-/type-fest-0.20.2.tgz", - "integrity": "sha512-Ne+eE4r0/iWnpAxD852z3A+N0Bt5RN//NjJwRd2VFHEmrywxf5vsZlh4R6lixl6B+wz/8d+maTSAkN1FIkI3LQ==", + "version": "0.21.3", + "resolved": "https://registry.npmjs.org/type-fest/-/type-fest-0.21.3.tgz", + "integrity": "sha512-t0rzBq87m3fVcduHDUFhKmyyX+9eo6WQjZvf51Ea/M0Q7+T374Jp1aUiyUl0GKxp8M/OETVHSDvmkyPgvX+X2w==", "dev": true, "engines": { "node": ">=10" @@ -5899,26 +5962,6 @@ "typedoc": ">=0.22.0" } }, - "node_modules/typedoc/node_modules/glob": { - "version": "7.2.0", - "resolved": "https://registry.npmjs.org/glob/-/glob-7.2.0.tgz", - "integrity": "sha512-lmLf6gtyrPq8tTjSmrO94wBeQbFR3HbLHbuyD69wuyQkImp2hWqMGB47OX65FBkPffO641IP9jWa1z4ivqG26Q==", - "dev": true, - "dependencies": { - "fs.realpath": "^1.0.0", - "inflight": "^1.0.4", - "inherits": "2", - "minimatch": "^3.0.4", - "once": "^1.3.0", - "path-is-absolute": "^1.0.0" - }, - "engines": { - "node": "*" - }, - "funding": { - "url": "https://github.com/sponsors/isaacs" - } - }, "node_modules/typescript": { "version": "4.4.4", "resolved": "https://registry.npmjs.org/typescript/-/typescript-4.4.4.tgz", @@ -6043,13 +6086,12 @@ "dev": true }, "node_modules/uuid": { - "version": "3.4.0", - "resolved": "https://registry.npmjs.org/uuid/-/uuid-3.4.0.tgz", - "integrity": "sha512-HjSDRw6gZE5JMggctHBcjVak08+KEVhSIiDzFnT9S9aegmp85S/bReBVTb4QTFaRNptJ9kuYaNhnbNEOkbKb/A==", - "deprecated": "Please upgrade to version 7 or higher. Older versions may use Math.random() in certain circumstances, which is known to be problematic. See https://v8.dev/blog/math-random for details.", + "version": "8.3.2", + "resolved": "https://registry.npmjs.org/uuid/-/uuid-8.3.2.tgz", + "integrity": "sha512-+NYs2QeMWy+GWFOEm9xnn6HCDp0l7QBD7ml8zLUmJ+93Q5NF0NocErnwkTkXVFNiX3/fpC6afS8Dhb/gz7R7eg==", "dev": true, "bin": { - "uuid": "bin/uuid" + "uuid": "dist/bin/uuid" } }, "node_modules/v8-compile-cache": { @@ -6291,9 +6333,9 @@ } }, "node_modules/yargs-parser": { - "version": "20.2.4", - "resolved": "https://registry.npmjs.org/yargs-parser/-/yargs-parser-20.2.4.tgz", - "integrity": "sha512-WOkpgNhPTlE73h4VFAFsOnomJVaovO8VqLDzy5saChRBFQFBoMYirowyW+Q9HB4HFF4Z7VZTiG3iSzJJA29yRA==", + "version": "20.2.9", + "resolved": "https://registry.npmjs.org/yargs-parser/-/yargs-parser-20.2.9.tgz", + "integrity": "sha512-y11nGElTIV+CT3Zv9t7VKl+Q3hTQoT9a1Qzezhhl6Rp21gJ/IVTW7Z3y9EWXhuUBC2Shnf+DX0antecpAwSP8w==", "dev": true, "engines": { "node": ">=10" @@ -6314,18 +6356,6 @@ "node": ">=10" } }, - "node_modules/yargs-unparser/node_modules/camelcase": { - "version": "6.2.0", - "resolved": "https://registry.npmjs.org/camelcase/-/camelcase-6.2.0.tgz", - "integrity": "sha512-c7wVvbw3f37nuobQNtgsgG9POC9qMbNuMQmTCqZv23b6MIz0fcYpBiOlv9gEN/hdLdnZTDQhg6e9Dq5M1vKvfg==", - "dev": true, - "engines": { - "node": ">=10" - }, - "funding": { - "url": "https://github.com/sponsors/sindresorhus" - } - }, "node_modules/yargs-unparser/node_modules/decamelize": { "version": "4.0.0", "resolved": "https://registry.npmjs.org/decamelize/-/decamelize-4.0.0.tgz", @@ -6359,22 +6389,7 @@ "url": "https://github.com/sponsors/sindresorhus" } }, - "packages/all-in-one": { - "name": "redis", - "version": "4.0.0-rc.3", - "license": "MIT", - "dependencies": { - "@redis/client": "^1.0.0-rc", - "@redis/json": "^1.0.0-rc", - "@redis/search": "^1.0.0-rc" - }, - "devDependencies": { - "release-it": "^14.11.6", - "typescript": "^4.4.4" - } - }, "packages/client": { - "name": "@redis/client", "version": "1.0.0-rc", "license": "MIT", "dependencies": { @@ -6407,7 +6422,6 @@ } }, "packages/json": { - "name": "@redis/json", "version": "1.0.0-rc.0", "license": "MIT", "devDependencies": { @@ -6425,7 +6439,6 @@ } }, "packages/search": { - "name": "@redis/search", "version": "1.0.0-rc.0", "license": "MIT", "devDependencies": { @@ -6443,7 +6456,6 @@ } }, "packages/test-utils": { - "name": "@redis/test-utils", "devDependencies": { "@istanbuljs/nyc-config-typescript": "^1.0.1", "@tsconfig/node12": "^1.0.9", @@ -6463,7 +6475,6 @@ } }, "packages/time-series": { - "name": "@redis/time-series", "version": "1.0.0", "license": "ISC", "devDependencies": { @@ -6727,12 +6738,6 @@ "integrity": "sha1-p9BVi9icQveV3UIyj3QIMcpTvCU=", "dev": true }, - "escape-string-regexp": { - "version": "1.0.5", - "resolved": "https://registry.npmjs.org/escape-string-regexp/-/escape-string-regexp-1.0.5.tgz", - "integrity": "sha1-G2HAViGQqN/2rjuyzwIAyhMLhtQ=", - "dev": true - }, "has-flag": { "version": "3.0.0", "resolved": "https://registry.npmjs.org/has-flag/-/has-flag-3.0.0.tgz", @@ -6839,6 +6844,12 @@ "resolved": "https://registry.npmjs.org/ignore/-/ignore-4.0.6.tgz", "integrity": "sha512-cyFDKrqc/YdcWFniJhzI42+AzS+gNwmUzOSFcRCQYwySuBBBy/KjuxWLZ/FHEH6Moq1NizMOBWyTcv8O4OZIMg==", "dev": true + }, + "strip-json-comments": { + "version": "3.1.1", + "resolved": "https://registry.npmjs.org/strip-json-comments/-/strip-json-comments-3.1.1.tgz", + "integrity": "sha512-6fPc+R4ihwqP6N/aIv2f1gMH8lOVtWQHoqC4yK6oSDVVocumAsfCqjkXnqiYMhmMwS/mEHLp7Vehlt3ql6lEig==", + "dev": true } } }, @@ -6887,6 +6898,12 @@ "sprintf-js": "~1.0.2" } }, + "camelcase": { + "version": "5.3.1", + "resolved": "https://registry.npmjs.org/camelcase/-/camelcase-5.3.1.tgz", + "integrity": "sha512-L28STB170nwWS63UjtlEOE3dldQApaJXZkOI1uMFfzf3rRuPegHaHesyee+YxQ+W6SvRDQV6UrdOdRiR153wJg==", + "dev": true + }, "find-up": { "version": "4.1.0", "resolved": "https://registry.npmjs.org/find-up/-/find-up-4.1.0.tgz", @@ -7505,14 +7522,6 @@ "dev": true, "requires": { "type-fest": "^0.21.3" - }, - "dependencies": { - "type-fest": { - "version": "0.21.3", - "resolved": "https://registry.npmjs.org/type-fest/-/type-fest-0.21.3.tgz", - "integrity": "sha512-t0rzBq87m3fVcduHDUFhKmyyX+9eo6WQjZvf51Ea/M0Q7+T374Jp1aUiyUl0GKxp8M/OETVHSDvmkyPgvX+X2w==", - "dev": true - } } }, "ansi-regex": { @@ -7639,10 +7648,10 @@ "wrap-ansi": "^7.0.0" }, "dependencies": { - "camelcase": { - "version": "6.2.0", - "resolved": "https://registry.npmjs.org/camelcase/-/camelcase-6.2.0.tgz", - "integrity": "sha512-c7wVvbw3f37nuobQNtgsgG9POC9qMbNuMQmTCqZv23b6MIz0fcYpBiOlv9gEN/hdLdnZTDQhg6e9Dq5M1vKvfg==", + "type-fest": { + "version": "0.20.2", + "resolved": "https://registry.npmjs.org/type-fest/-/type-fest-0.20.2.tgz", + "integrity": "sha512-Ne+eE4r0/iWnpAxD852z3A+N0Bt5RN//NjJwRd2VFHEmrywxf5vsZlh4R6lixl6B+wz/8d+maTSAkN1FIkI3LQ==", "dev": true } } @@ -7762,9 +7771,9 @@ "dev": true }, "camelcase": { - "version": "5.3.1", - "resolved": "https://registry.npmjs.org/camelcase/-/camelcase-5.3.1.tgz", - "integrity": "sha512-L28STB170nwWS63UjtlEOE3dldQApaJXZkOI1uMFfzf3rRuPegHaHesyee+YxQ+W6SvRDQV6UrdOdRiR153wJg==", + "version": "6.2.0", + "resolved": "https://registry.npmjs.org/camelcase/-/camelcase-6.2.0.tgz", + "integrity": "sha512-c7wVvbw3f37nuobQNtgsgG9POC9qMbNuMQmTCqZv23b6MIz0fcYpBiOlv9gEN/hdLdnZTDQhg6e9Dq5M1vKvfg==", "dev": true }, "caniuse-lite": { @@ -7803,17 +7812,6 @@ "is-glob": "~4.0.1", "normalize-path": "~3.0.0", "readdirp": "~3.6.0" - }, - "dependencies": { - "glob-parent": { - "version": "5.1.2", - "resolved": "https://registry.npmjs.org/glob-parent/-/glob-parent-5.1.2.tgz", - "integrity": "sha512-AOIgSQCepiJYwP3ARnGx+5VnTu2HBYdzbGP45eLw1vr3zB3vZLeyed1sC9hnbcOc9/SrMyM5RPQrkGz4aS9Zow==", - "dev": true, - "requires": { - "is-glob": "^4.0.1" - } - } } }, "ci-info": { @@ -7943,6 +7941,14 @@ "dev": true, "requires": { "safe-buffer": "~5.1.1" + }, + "dependencies": { + "safe-buffer": { + "version": "5.1.2", + "resolved": "https://registry.npmjs.org/safe-buffer/-/safe-buffer-5.1.2.tgz", + "integrity": "sha512-Gd2UZBJDkXlY7GbJxfsE8/nvKkUEU1G38c1siN6QP6a9PT9MmHB8GnpscSmMJSoF8LOIrt8ud/wPtojys4G6+g==", + "dev": true + } } }, "cosmiconfig": { @@ -8174,9 +8180,9 @@ "dev": true }, "escape-string-regexp": { - "version": "4.0.0", - "resolved": "https://registry.npmjs.org/escape-string-regexp/-/escape-string-regexp-4.0.0.tgz", - "integrity": "sha512-TtpcNJ3XAzx3Gq8sWRzJaVajRs0uVxA2YAkdb1jm2YkPz4G6egUFAyA3n5vtEIZefPk5Wa4UXbKuS5fKkJWdgA==", + "version": "1.0.5", + "resolved": "https://registry.npmjs.org/escape-string-regexp/-/escape-string-regexp-1.0.5.tgz", + "integrity": "sha1-G2HAViGQqN/2rjuyzwIAyhMLhtQ=", "dev": true }, "eslint": { @@ -8225,6 +8231,12 @@ "v8-compile-cache": "^2.0.3" }, "dependencies": { + "escape-string-regexp": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/escape-string-regexp/-/escape-string-regexp-4.0.0.tgz", + "integrity": "sha512-TtpcNJ3XAzx3Gq8sWRzJaVajRs0uVxA2YAkdb1jm2YkPz4G6egUFAyA3n5vtEIZefPk5Wa4UXbKuS5fKkJWdgA==", + "dev": true + }, "eslint-scope": { "version": "6.0.0", "resolved": "https://registry.npmjs.org/eslint-scope/-/eslint-scope-6.0.0.tgz", @@ -8241,11 +8253,26 @@ "integrity": "sha512-MMdARuVEQziNTeJD8DgMqmhwR11BRQ/cBP+pLtYdSTnf3MIO8fFeiINEbX36ZdNlfU/7A9f3gUw49B3oQsvwBA==", "dev": true }, + "glob-parent": { + "version": "6.0.2", + "resolved": "https://registry.npmjs.org/glob-parent/-/glob-parent-6.0.2.tgz", + "integrity": "sha512-XxwI8EOhVQgWp6iDL+3b0r86f4d6AX6zSU55HfB4ydCEuXLXc5FcYeOu+nnGftS4TEju/11rt4KJPTMgbfmv4A==", + "dev": true, + "requires": { + "is-glob": "^4.0.3" + } + }, "ignore": { "version": "4.0.6", "resolved": "https://registry.npmjs.org/ignore/-/ignore-4.0.6.tgz", "integrity": "sha512-cyFDKrqc/YdcWFniJhzI42+AzS+gNwmUzOSFcRCQYwySuBBBy/KjuxWLZ/FHEH6Moq1NizMOBWyTcv8O4OZIMg==", "dev": true + }, + "strip-json-comments": { + "version": "3.1.1", + "resolved": "https://registry.npmjs.org/strip-json-comments/-/strip-json-comments-3.1.1.tgz", + "integrity": "sha512-6fPc+R4ihwqP6N/aIv2f1gMH8lOVtWQHoqC4yK6oSDVVocumAsfCqjkXnqiYMhmMwS/mEHLp7Vehlt3ql6lEig==", + "dev": true } } }, @@ -8390,17 +8417,6 @@ "glob-parent": "^5.1.2", "merge2": "^1.3.0", "micromatch": "^4.0.4" - }, - "dependencies": { - "glob-parent": { - "version": "5.1.2", - "resolved": "https://registry.npmjs.org/glob-parent/-/glob-parent-5.1.2.tgz", - "integrity": "sha512-AOIgSQCepiJYwP3ARnGx+5VnTu2HBYdzbGP45eLw1vr3zB3vZLeyed1sC9hnbcOc9/SrMyM5RPQrkGz4aS9Zow==", - "dev": true, - "requires": { - "is-glob": "^4.0.1" - } - } } }, "fast-json-stable-stringify": { @@ -8431,14 +8447,6 @@ "dev": true, "requires": { "escape-string-regexp": "^1.0.5" - }, - "dependencies": { - "escape-string-regexp": { - "version": "1.0.5", - "resolved": "https://registry.npmjs.org/escape-string-regexp/-/escape-string-regexp-1.0.5.tgz", - "integrity": "sha1-G2HAViGQqN/2rjuyzwIAyhMLhtQ=", - "dev": true - } } }, "file-entry-cache": { @@ -8620,9 +8628,9 @@ } }, "glob": { - "version": "7.1.7", - "resolved": "https://registry.npmjs.org/glob/-/glob-7.1.7.tgz", - "integrity": "sha512-OvD9ENzPLbegENnYP5UUfJIirTg4+XwMWGaQfQTY0JenxNvvIKP3U3/tAQSPIu/lHxXYSZmpXlUHeqAIdKzBLQ==", + "version": "7.2.0", + "resolved": "https://registry.npmjs.org/glob/-/glob-7.2.0.tgz", + "integrity": "sha512-lmLf6gtyrPq8tTjSmrO94wBeQbFR3HbLHbuyD69wuyQkImp2hWqMGB47OX65FBkPffO641IP9jWa1z4ivqG26Q==", "dev": true, "requires": { "fs.realpath": "^1.0.0", @@ -8634,12 +8642,12 @@ } }, "glob-parent": { - "version": "6.0.2", - "resolved": "https://registry.npmjs.org/glob-parent/-/glob-parent-6.0.2.tgz", - "integrity": "sha512-XxwI8EOhVQgWp6iDL+3b0r86f4d6AX6zSU55HfB4ydCEuXLXc5FcYeOu+nnGftS4TEju/11rt4KJPTMgbfmv4A==", + "version": "5.1.2", + "resolved": "https://registry.npmjs.org/glob-parent/-/glob-parent-5.1.2.tgz", + "integrity": "sha512-AOIgSQCepiJYwP3ARnGx+5VnTu2HBYdzbGP45eLw1vr3zB3vZLeyed1sC9hnbcOc9/SrMyM5RPQrkGz4aS9Zow==", "dev": true, "requires": { - "is-glob": "^4.0.3" + "is-glob": "^4.0.1" } }, "global-dirs": { @@ -8658,6 +8666,14 @@ "dev": true, "requires": { "type-fest": "^0.20.2" + }, + "dependencies": { + "type-fest": { + "version": "0.20.2", + "resolved": "https://registry.npmjs.org/type-fest/-/type-fest-0.20.2.tgz", + "integrity": "sha512-Ne+eE4r0/iWnpAxD852z3A+N0Bt5RN//NjJwRd2VFHEmrywxf5vsZlh4R6lixl6B+wz/8d+maTSAkN1FIkI3LQ==", + "dev": true + } } }, "globby": { @@ -9150,6 +9166,14 @@ "p-map": "^3.0.0", "rimraf": "^3.0.0", "uuid": "^3.3.3" + }, + "dependencies": { + "uuid": { + "version": "3.4.0", + "resolved": "https://registry.npmjs.org/uuid/-/uuid-3.4.0.tgz", + "integrity": "sha512-HjSDRw6gZE5JMggctHBcjVak08+KEVhSIiDzFnT9S9aegmp85S/bReBVTb4QTFaRNptJ9kuYaNhnbNEOkbKb/A==", + "dev": true + } } }, "istanbul-lib-report": { @@ -9487,12 +9511,38 @@ "yargs-unparser": "2.0.0" }, "dependencies": { + "escape-string-regexp": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/escape-string-regexp/-/escape-string-regexp-4.0.0.tgz", + "integrity": "sha512-TtpcNJ3XAzx3Gq8sWRzJaVajRs0uVxA2YAkdb1jm2YkPz4G6egUFAyA3n5vtEIZefPk5Wa4UXbKuS5fKkJWdgA==", + "dev": true + }, + "glob": { + "version": "7.1.7", + "resolved": "https://registry.npmjs.org/glob/-/glob-7.1.7.tgz", + "integrity": "sha512-OvD9ENzPLbegENnYP5UUfJIirTg4+XwMWGaQfQTY0JenxNvvIKP3U3/tAQSPIu/lHxXYSZmpXlUHeqAIdKzBLQ==", + "dev": true, + "requires": { + "fs.realpath": "^1.0.0", + "inflight": "^1.0.4", + "inherits": "2", + "minimatch": "^3.0.4", + "once": "^1.3.0", + "path-is-absolute": "^1.0.0" + } + }, "ms": { "version": "2.1.3", "resolved": "https://registry.npmjs.org/ms/-/ms-2.1.3.tgz", "integrity": "sha512-6FlzubTLZG3J2a/NVCAleEhjzq5oxgHyaCU9yYXvcLsvoVaHJq/s5xXI6/XXP6tz7R9xAOtHnSO/tXtF3WRTlA==", "dev": true }, + "strip-json-comments": { + "version": "3.1.1", + "resolved": "https://registry.npmjs.org/strip-json-comments/-/strip-json-comments-3.1.1.tgz", + "integrity": "sha512-6fPc+R4ihwqP6N/aIv2f1gMH8lOVtWQHoqC4yK6oSDVVocumAsfCqjkXnqiYMhmMwS/mEHLp7Vehlt3ql6lEig==", + "dev": true + }, "supports-color": { "version": "8.1.1", "resolved": "https://registry.npmjs.org/supports-color/-/supports-color-8.1.1.tgz", @@ -9516,6 +9566,12 @@ "y18n": "^5.0.5", "yargs-parser": "^20.2.2" } + }, + "yargs-parser": { + "version": "20.2.4", + "resolved": "https://registry.npmjs.org/yargs-parser/-/yargs-parser-20.2.4.tgz", + "integrity": "sha512-WOkpgNhPTlE73h4VFAFsOnomJVaovO8VqLDzy5saChRBFQFBoMYirowyW+Q9HB4HFF4Z7VZTiG3iSzJJA29yRA==", + "dev": true } } }, @@ -9659,6 +9715,12 @@ "yargs": "^15.0.2" }, "dependencies": { + "camelcase": { + "version": "5.3.1", + "resolved": "https://registry.npmjs.org/camelcase/-/camelcase-5.3.1.tgz", + "integrity": "sha512-L28STB170nwWS63UjtlEOE3dldQApaJXZkOI1uMFfzf3rRuPegHaHesyee+YxQ+W6SvRDQV6UrdOdRiR153wJg==", + "dev": true + }, "cliui": { "version": "6.0.0", "resolved": "https://registry.npmjs.org/cliui/-/cliui-6.0.0.tgz", @@ -10336,12 +10398,6 @@ "resolved": "https://registry.npmjs.org/ini/-/ini-1.3.8.tgz", "integrity": "sha512-JV/yugV2uzW5iMRSiZAyDtQd+nxtUnjeLt0acNdw98kKLrvuRVyB80tsREOE7yvGVgalhZ6RNXCmEHkUKBKxew==", "dev": true - }, - "strip-json-comments": { - "version": "2.0.1", - "resolved": "https://registry.npmjs.org/strip-json-comments/-/strip-json-comments-2.0.1.tgz", - "integrity": "sha1-PFMZQukIwml8DsNEhYwobHygpgo=", - "dev": true } } }, @@ -10374,16 +10430,6 @@ "resolve": "^1.1.6" } }, - "redis": { - "version": "file:packages/all-in-one", - "requires": { - "@redis/client": "^1.0.0-rc", - "@redis/json": "^1.0.0-rc", - "@redis/search": "^1.0.0-rc", - "release-it": "^14.11.6", - "typescript": "^4.4.4" - } - }, "redis-errors": { "version": "1.2.0", "resolved": "https://registry.npmjs.org/redis-errors/-/redis-errors-1.2.0.tgz", @@ -10456,20 +10502,6 @@ "uuid": "8.3.2", "yaml": "1.10.2", "yargs-parser": "20.2.9" - }, - "dependencies": { - "uuid": { - "version": "8.3.2", - "resolved": "https://registry.npmjs.org/uuid/-/uuid-8.3.2.tgz", - "integrity": "sha512-+NYs2QeMWy+GWFOEm9xnn6HCDp0l7QBD7ml8zLUmJ+93Q5NF0NocErnwkTkXVFNiX3/fpC6afS8Dhb/gz7R7eg==", - "dev": true - }, - "yargs-parser": { - "version": "20.2.9", - "resolved": "https://registry.npmjs.org/yargs-parser/-/yargs-parser-20.2.9.tgz", - "integrity": "sha512-y11nGElTIV+CT3Zv9t7VKl+Q3hTQoT9a1Qzezhhl6Rp21gJ/IVTW7Z3y9EWXhuUBC2Shnf+DX0antecpAwSP8w==", - "dev": true - } } }, "release-zalgo": { @@ -10580,9 +10612,9 @@ } }, "safe-buffer": { - "version": "5.1.2", - "resolved": "https://registry.npmjs.org/safe-buffer/-/safe-buffer-5.1.2.tgz", - "integrity": "sha512-Gd2UZBJDkXlY7GbJxfsE8/nvKkUEU1G38c1siN6QP6a9PT9MmHB8GnpscSmMJSoF8LOIrt8ud/wPtojys4G6+g==", + "version": "5.2.1", + "resolved": "https://registry.npmjs.org/safe-buffer/-/safe-buffer-5.2.1.tgz", + "integrity": "sha512-rp3So07KcdmmKbGvgaNxQSJr7bGVSVk5S9Eq1F+ppbRo70+YeaDxkw5Dd8NPN+GD6bjnYm2VuPuCXmpuYvmCXQ==", "dev": true }, "safer-buffer": { @@ -10769,14 +10801,6 @@ "dev": true, "requires": { "safe-buffer": "~5.2.0" - }, - "dependencies": { - "safe-buffer": { - "version": "5.2.1", - "resolved": "https://registry.npmjs.org/safe-buffer/-/safe-buffer-5.2.1.tgz", - "integrity": "sha512-rp3So07KcdmmKbGvgaNxQSJr7bGVSVk5S9Eq1F+ppbRo70+YeaDxkw5Dd8NPN+GD6bjnYm2VuPuCXmpuYvmCXQ==", - "dev": true - } } }, "string-width": { @@ -10812,9 +10836,9 @@ "dev": true }, "strip-json-comments": { - "version": "3.1.1", - "resolved": "https://registry.npmjs.org/strip-json-comments/-/strip-json-comments-3.1.1.tgz", - "integrity": "sha512-6fPc+R4ihwqP6N/aIv2f1gMH8lOVtWQHoqC4yK6oSDVVocumAsfCqjkXnqiYMhmMwS/mEHLp7Vehlt3ql6lEig==", + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/strip-json-comments/-/strip-json-comments-2.0.1.tgz", + "integrity": "sha1-PFMZQukIwml8DsNEhYwobHygpgo=", "dev": true }, "supports-color": { @@ -10952,9 +10976,9 @@ "dev": true }, "type-fest": { - "version": "0.20.2", - "resolved": "https://registry.npmjs.org/type-fest/-/type-fest-0.20.2.tgz", - "integrity": "sha512-Ne+eE4r0/iWnpAxD852z3A+N0Bt5RN//NjJwRd2VFHEmrywxf5vsZlh4R6lixl6B+wz/8d+maTSAkN1FIkI3LQ==", + "version": "0.21.3", + "resolved": "https://registry.npmjs.org/type-fest/-/type-fest-0.21.3.tgz", + "integrity": "sha512-t0rzBq87m3fVcduHDUFhKmyyX+9eo6WQjZvf51Ea/M0Q7+T374Jp1aUiyUl0GKxp8M/OETVHSDvmkyPgvX+X2w==", "dev": true }, "typedarray-to-buffer": { @@ -10977,22 +11001,6 @@ "marked": "^3.0.8", "minimatch": "^3.0.4", "shiki": "^0.9.12" - }, - "dependencies": { - "glob": { - "version": "7.2.0", - "resolved": "https://registry.npmjs.org/glob/-/glob-7.2.0.tgz", - "integrity": "sha512-lmLf6gtyrPq8tTjSmrO94wBeQbFR3HbLHbuyD69wuyQkImp2hWqMGB47OX65FBkPffO641IP9jWa1z4ivqG26Q==", - "dev": true, - "requires": { - "fs.realpath": "^1.0.0", - "inflight": "^1.0.4", - "inherits": "2", - "minimatch": "^3.0.4", - "once": "^1.3.0", - "path-is-absolute": "^1.0.0" - } - } } }, "typedoc-github-wiki-theme": { @@ -11109,9 +11117,9 @@ "dev": true }, "uuid": { - "version": "3.4.0", - "resolved": "https://registry.npmjs.org/uuid/-/uuid-3.4.0.tgz", - "integrity": "sha512-HjSDRw6gZE5JMggctHBcjVak08+KEVhSIiDzFnT9S9aegmp85S/bReBVTb4QTFaRNptJ9kuYaNhnbNEOkbKb/A==", + "version": "8.3.2", + "resolved": "https://registry.npmjs.org/uuid/-/uuid-8.3.2.tgz", + "integrity": "sha512-+NYs2QeMWy+GWFOEm9xnn6HCDp0l7QBD7ml8zLUmJ+93Q5NF0NocErnwkTkXVFNiX3/fpC6afS8Dhb/gz7R7eg==", "dev": true }, "v8-compile-cache": { @@ -11304,9 +11312,9 @@ } }, "yargs-parser": { - "version": "20.2.4", - "resolved": "https://registry.npmjs.org/yargs-parser/-/yargs-parser-20.2.4.tgz", - "integrity": "sha512-WOkpgNhPTlE73h4VFAFsOnomJVaovO8VqLDzy5saChRBFQFBoMYirowyW+Q9HB4HFF4Z7VZTiG3iSzJJA29yRA==", + "version": "20.2.9", + "resolved": "https://registry.npmjs.org/yargs-parser/-/yargs-parser-20.2.9.tgz", + "integrity": "sha512-y11nGElTIV+CT3Zv9t7VKl+Q3hTQoT9a1Qzezhhl6Rp21gJ/IVTW7Z3y9EWXhuUBC2Shnf+DX0antecpAwSP8w==", "dev": true }, "yargs-unparser": { @@ -11321,12 +11329,6 @@ "is-plain-obj": "^2.1.0" }, "dependencies": { - "camelcase": { - "version": "6.2.0", - "resolved": "https://registry.npmjs.org/camelcase/-/camelcase-6.2.0.tgz", - "integrity": "sha512-c7wVvbw3f37nuobQNtgsgG9POC9qMbNuMQmTCqZv23b6MIz0fcYpBiOlv9gEN/hdLdnZTDQhg6e9Dq5M1vKvfg==", - "dev": true - }, "decamelize": { "version": "4.0.0", "resolved": "https://registry.npmjs.org/decamelize/-/decamelize-4.0.0.tgz", diff --git a/package.json b/package.json index e7de3fc8674..051abd3a75d 100644 --- a/package.json +++ b/package.json @@ -1,6 +1,9 @@ { - "name": "redis-monorepo", - "private": true, + "name": "redis", + "version": "4.0.0-rc.3", + "license": "MIT", + "main": "./dist/index.js", + "types": "./dist/index.d.ts", "workspaces": [ "./packages/*" ], @@ -9,11 +12,26 @@ "build:client": "npm run build -w ./packages/client", "build:test-utils": "npm run build -w ./packages/test-utils", "build:tests-tools": "npm run build:client && npm run build:test-utils", - "build:modules": "find ./packages -mindepth 1 -maxdepth 1 -type d ! -name 'client' ! -name 'test-utils' ! -name 'all-in-one' -exec npm run build -w {} \\;", - "build:all-in-one": "npm run build -w ./packages/all-in-one", - "build": "npm run build:client && npm run build:test-utils && npm run build:modules && npm run build:all-in-one" + "build:modules": "find ./packages -mindepth 1 -maxdepth 1 -type d ! -name 'client' ! -name 'test-utils' -exec npm run build -w {} \\;", + "build": "tsc", + "build-all": "npm run build:client && npm run build:test-utils && npm run build:modules && npm run build" + }, + "dependencies": { + "@redis/client": "^1.0.0-rc", + "@redis/json": "^1.0.0-rc", + "@redis/search": "^1.0.0-rc" }, "devDependencies": { - "@tsconfig/node12": "^1.0.9" - } + "@tsconfig/node12": "^1.0.9", + "release-it": "^14.11.6", + "typescript": "^4.4.4" + }, + "repository": { + "type": "git", + "url": "git://github.com/redis/node-redis.git" + }, + "bugs": { + "url": "https://github.com/redis/node-redis/issues" + }, + "homepage": "https://github.com/redis/node-redis" } diff --git a/packages/all-in-one/package.json b/packages/all-in-one/package.json deleted file mode 100644 index 1fcaa1697d0..00000000000 --- a/packages/all-in-one/package.json +++ /dev/null @@ -1,27 +0,0 @@ -{ - "name": "redis", - "version": "4.0.0-rc.3", - "license": "MIT", - "main": "./dist/index.js", - "types": "./dist/index.d.ts", - "scripts": { - "build": "tsc" - }, - "dependencies": { - "@redis/client": "^1.0.0-rc", - "@redis/json": "^1.0.0-rc", - "@redis/search": "^1.0.0-rc" - }, - "devDependencies": { - "release-it": "^14.11.6", - "typescript": "^4.4.4" - }, - "repository": { - "type": "git", - "url": "git://github.com/redis/node-redis.git" - }, - "bugs": { - "url": "https://github.com/redis/node-redis/issues" - }, - "homepage": "https://github.com/redis/node-redis" -} diff --git a/packages/all-in-one/tsconfig.json b/packages/all-in-one/tsconfig.json deleted file mode 100644 index 103760b874b..00000000000 --- a/packages/all-in-one/tsconfig.json +++ /dev/null @@ -1,9 +0,0 @@ -{ - "extends": "../../tsconfig", - "compilerOptions": { - "outDir": "./dist" - }, - "include": [ - "./index.ts" - ] - } diff --git a/packages/client/.npmignore b/packages/client/.npmignore index b7310e15767..9f9e21d98b8 100644 --- a/packages/client/.npmignore +++ b/packages/client/.npmignore @@ -1,7 +1,6 @@ .nyc_output/ coverage/ documentation/ -examples/ lib/ .nycrc.json dump.rdb diff --git a/packages/client/package.json b/packages/client/package.json index 812f742db84..0288ed25644 100644 --- a/packages/client/package.json +++ b/packages/client/package.json @@ -19,18 +19,18 @@ "devDependencies": { "@istanbuljs/nyc-config-typescript": "^1.0.1", "@redis/test-utils": "*", - "@types/node": "^16.11.6", + "@types/node": "^16.11.7", "@types/sinon": "^10.0.6", "@types/yallist": "^4.0.1", - "@typescript-eslint/eslint-plugin": "^5.2.0", - "@typescript-eslint/parser": "^5.2.0", - "eslint": "^8.1.0", + "@typescript-eslint/eslint-plugin": "^5.3.1", + "@typescript-eslint/parser": "^5.3.1", + "eslint": "^8.2.0", "nyc": "^15.1.0", "release-it": "^14.11.6", - "sinon": "^11.1.2", + "sinon": "^12.0.1", "source-map-support": "^0.5.20", "ts-node": "^10.4.0", - "typedoc": "^0.22.7", + "typedoc": "^0.22.8", "typedoc-github-wiki-theme": "^0.6.0", "typedoc-plugin-markdown": "^3.11.3", "typescript": "^4.4.4" diff --git a/packages/client/tsconfig.json b/packages/client/tsconfig.json index 1700c9097bf..3280af594ef 100644 --- a/packages/client/tsconfig.json +++ b/packages/client/tsconfig.json @@ -1,5 +1,5 @@ { - "extends": "../../tsconfig.json", + "extends": "../../tsconfig.base.json", "compilerOptions": { "outDir": "./dist" }, diff --git a/packages/json/tsconfig.json b/packages/json/tsconfig.json index fdb86c004cc..14fda1d8711 100644 --- a/packages/json/tsconfig.json +++ b/packages/json/tsconfig.json @@ -1,5 +1,5 @@ { - "extends": "../../tsconfig", + "extends": "../../tsconfig.base.json", "compilerOptions": { "outDir": "./dist" }, diff --git a/packages/search/tsconfig.json b/packages/search/tsconfig.json index fdb86c004cc..14fda1d8711 100644 --- a/packages/search/tsconfig.json +++ b/packages/search/tsconfig.json @@ -1,5 +1,5 @@ { - "extends": "../../tsconfig", + "extends": "../../tsconfig.base.json", "compilerOptions": { "outDir": "./dist" }, diff --git a/packages/test-utils/tsconfig.json b/packages/test-utils/tsconfig.json index fdb86c004cc..14fda1d8711 100644 --- a/packages/test-utils/tsconfig.json +++ b/packages/test-utils/tsconfig.json @@ -1,5 +1,5 @@ { - "extends": "../../tsconfig", + "extends": "../../tsconfig.base.json", "compilerOptions": { "outDir": "./dist" }, diff --git a/tsconfig.base.json b/tsconfig.base.json new file mode 100644 index 00000000000..d6cd971b01d --- /dev/null +++ b/tsconfig.base.json @@ -0,0 +1,15 @@ +{ + "extends": "@tsconfig/node12/tsconfig.json", + "compilerOptions": { + "declaration": true, + "allowJs": true, + "useDefineForClassFields": true + }, + "files": [ + "./packages/client/lib/ts-declarations/cluster-key-slot.d.ts", + "./packages/client/lib/ts-declarations/redis-parser.d.ts" + ], + "ts-node": { + "files": true + } +} diff --git a/tsconfig.json b/tsconfig.json index fa46effbdcc..285b7ff0a97 100644 --- a/tsconfig.json +++ b/tsconfig.json @@ -1,15 +1,9 @@ { - "extends": "@tsconfig/node12/tsconfig", + "extends": "./tsconfig.base.json", "compilerOptions": { - "declaration": true, - "allowJs": true, - "useDefineForClassFields": true + "outDir": "./dist" }, - "files": [ - "./packages/client/lib/ts-declarations/cluster-key-slot.d.ts", - "./packages/client/lib/ts-declarations/redis-parser.d.ts" - ], - "ts-node": { - "files": true - } + "include": [ + "./index.ts" + ] } From 27a1fa7d1474d9707159dda19181acb72422b752 Mon Sep 17 00:00:00 2001 From: leibale Date: Tue, 9 Nov 2021 19:57:04 -0500 Subject: [PATCH 0939/1748] fix tests workflow --- .github/workflows/tests.yml | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/.github/workflows/tests.yml b/.github/workflows/tests.yml index a0152cea869..ad34df0b33a 100644 --- a/.github/workflows/tests.yml +++ b/.github/workflows/tests.yml @@ -12,7 +12,7 @@ jobs: strategy: fail-fast: false matrix: - node-version: [12.x, 14.x, 16.x] + node-version: [12, 14, 16] redis-version: [5, 6.0, 6.2] steps: @@ -27,7 +27,7 @@ jobs: - name: Update npm run: npm i -g npm - if: ${{ matrix.node-version <= 14.x }} + if: ${{ matrix.node-version <= 14 }} - name: Install Packages run: npm ci From f1f9c7ac241125b9bcb410f5ad14403a8a94fa20 Mon Sep 17 00:00:00 2001 From: leibale Date: Wed, 10 Nov 2021 14:20:46 -0500 Subject: [PATCH 0940/1748] fix bug in cluster slots, enhance live resharding test --- index.ts | 1 - packages/client/lib/cluster/cluster-slots.ts | 2 +- packages/client/lib/cluster/index.spec.ts | 6 ++++++ 3 files changed, 7 insertions(+), 2 deletions(-) diff --git a/index.ts b/index.ts index 7913630a199..3bbb4c9d943 100644 --- a/index.ts +++ b/index.ts @@ -19,4 +19,3 @@ export function createClient>( modules }); } - diff --git a/packages/client/lib/cluster/cluster-slots.ts b/packages/client/lib/cluster/cluster-slots.ts index f65411d7974..4c53445b680 100644 --- a/packages/client/lib/cluster/cluster-slots.ts +++ b/packages/client/lib/cluster/cluster-slots.ts @@ -82,7 +82,7 @@ export default class RedisClusterSlots { 10 ); + await Promise.all( + cluster.getMasters().map(({ client }) => { + return client.clusterSetSlot(slot, ClusterSlotStates.NODE, to!.id); + }) + ); + // should be able to get the key from the new node assert.equal( await cluster.get(key), From 3d009780ee6eb388750d53263a168e3bedfd65a1 Mon Sep 17 00:00:00 2001 From: leibale Date: Wed, 10 Nov 2021 14:42:53 -0500 Subject: [PATCH 0941/1748] fix live resharding test --- packages/client/lib/cluster/index.spec.ts | 37 ++++++++++++----------- 1 file changed, 19 insertions(+), 18 deletions(-) diff --git a/packages/client/lib/cluster/index.spec.ts b/packages/client/lib/cluster/index.spec.ts index 395d44e1a3c..c926c02d03a 100644 --- a/packages/client/lib/cluster/index.spec.ts +++ b/packages/client/lib/cluster/index.spec.ts @@ -53,42 +53,43 @@ describe('Cluster', () => { await cluster.set(key, value); const slot = calculateSlot(key), - from = cluster.getSlotMaster(slot), - to = cluster.getMasters().find(node => node.id !== from.id); + source = cluster.getSlotMaster(slot), + destination = cluster.getMasters().find(node => node.id !== source.id)!; - await to!.client.clusterSetSlot(slot, ClusterSlotStates.IMPORTING, from.id); + await Promise.all([ + source.client.clusterSetSlot(slot, ClusterSlotStates.MIGRATING, destination.id), + destination.client.clusterSetSlot(slot, ClusterSlotStates.IMPORTING, destination.id) + ]); - // should be able to get the key from the original node before it was migrated + // should be able to get the key from the source node using "ASKING" assert.equal( await cluster.get(key), value ); - await from.client.clusterSetSlot(slot, ClusterSlotStates.MIGRATING, to!.id); + await Promise.all([ + source.client.migrate( + '127.0.0.1', + (destination.client.options).socket.port, + key, + 0, + 10 + ) + ]); - // should be able to get the key from the original node using the "ASKING" command + // should be able to get the key from the destination node using the "ASKING" command assert.equal( await cluster.get(key), value ); - const { port: toPort } = to!.client.options!.socket; - - await from.client.migrate( - '127.0.0.1', - toPort, - key, - 0, - 10 - ); - await Promise.all( cluster.getMasters().map(({ client }) => { - return client.clusterSetSlot(slot, ClusterSlotStates.NODE, to!.id); + return client.clusterSetSlot(slot, ClusterSlotStates.NODE, destination.id); }) ); - // should be able to get the key from the new node + // should handle "MOVED" errors assert.equal( await cluster.get(key), value From 53bc5647930ca03c6d33b0f67d370349125901a5 Mon Sep 17 00:00:00 2001 From: leibale Date: Wed, 10 Nov 2021 16:57:15 -0500 Subject: [PATCH 0942/1748] fix #1707 - handle number arguments in legacy mode --- packages/client/lib/client/index.spec.ts | 10 +++++----- packages/client/lib/client/index.ts | 16 +++++++++------- packages/client/lib/client/multi-command.ts | 6 +++--- packages/client/lib/commander.ts | 15 +++++++++++++++ 4 files changed, 32 insertions(+), 15 deletions(-) diff --git a/packages/client/lib/client/index.spec.ts b/packages/client/lib/client/index.spec.ts index 51dded18b1f..3f0bca45e27 100644 --- a/packages/client/lib/client/index.spec.ts +++ b/packages/client/lib/client/index.spec.ts @@ -1,6 +1,6 @@ import { strict as assert } from 'assert'; import testUtils, { GLOBAL, waitTillBeenCalled } from '../test-utils'; -import RedisClient, { RedisClientType } from '.'; +import RedisClient, { ClientLegacyCommandArguments, RedisClientType } from '.'; import { RedisClientMultiCommandType } from './multi-command'; import { RedisCommandArguments, RedisCommandRawReply, RedisModules, RedisScripts } from '../commands'; import { AbortError, ClientClosedError, ConnectionTimeoutError, DisconnectsClientError, SocketClosedUnexpectedlyError, WatchError } from '../errors'; @@ -170,7 +170,7 @@ describe('Client', () => { } }); - function setAsync(client: RedisClientType, ...args: Array): Promise { + function setAsync(client: RedisClientType, ...args: ClientLegacyCommandArguments): Promise { return new Promise((resolve, reject) => { (client as any).set(...args, (err: Error | undefined, reply: RedisCommandRawReply) => { if (err) return reject(err); @@ -204,10 +204,10 @@ describe('Client', () => { } }); - testUtils.testWithClient('client.{command} should accept mix of strings and array of strings', async client => { + testUtils.testWithClient('client.{command} should accept mix of arrays and arguments', async client => { assert.equal( - await setAsync(client, ['a'], 'b', ['XX']), - null + await setAsync(client, ['a'], 'b', ['EX', 1]), + 'OK' ); }, { ...GLOBAL.SERVERS.OPEN, diff --git a/packages/client/lib/client/index.ts b/packages/client/lib/client/index.ts index 40499cbf7c0..dd1bf833344 100644 --- a/packages/client/lib/client/index.ts +++ b/packages/client/lib/client/index.ts @@ -9,7 +9,7 @@ import { CommandOptions, commandOptions, isCommandOptions } from '../command-opt import { ScanOptions, ZMember } from '../commands/generic-transformers'; import { ScanCommandOptions } from '../commands/SCAN'; import { HScanTuple } from '../commands/HSCAN'; -import { extendWithCommands, extendWithModulesAndScripts, transformCommandArguments, transformCommandReply } from '../commander'; +import { extendWithCommands, extendWithModulesAndScripts, LegacyCommandArguments, transformCommandArguments, transformCommandReply, transformLegacyCommandArguments } from '../commander'; import { Pool, Options as PoolOptions, createPool } from 'generic-pool'; import { ClientClosedError, DisconnectsClientError } from '../errors'; import { URL } from 'url'; @@ -55,6 +55,7 @@ export interface ClientCommandOptions extends QueueCommandOptions { type ClientLegacyCallback = (err: Error | null, reply?: RedisCommandRawReply) => void; +export type ClientLegacyCommandArguments = LegacyCommandArguments | [...LegacyCommandArguments, ClientLegacyCallback]; export default class RedisClient extends EventEmitter { static commandOptions(options: ClientCommandOptions): CommandOptions { return commandOptions(options); @@ -246,12 +247,13 @@ export default class RedisClient if (!this.#options?.legacyMode) return; (this as any).#v4.sendCommand = this.#sendCommand.bind(this); - (this as any).sendCommand = (...args: Array): void => { - const callback = typeof args[args.length - 1] === 'function' ? - args[args.length - 1] as ClientLegacyCallback : - undefined, - actualArgs = !callback ? args : args.slice(0, -1); - this.#sendCommand(actualArgs.flat() as Array) + (this as any).sendCommand = (...args: ClientLegacyCommandArguments): void => { + let callback: ClientLegacyCallback; + if (typeof args[args.length - 1] === 'function') { + callback = args.pop() as ClientLegacyCallback; + } + + this.#sendCommand(transformLegacyCommandArguments(args as LegacyCommandArguments)) .then((reply: RedisCommandRawReply) => { if (!callback) return; diff --git a/packages/client/lib/client/multi-command.ts b/packages/client/lib/client/multi-command.ts index e4b2c165c8f..9c19d3d0687 100644 --- a/packages/client/lib/client/multi-command.ts +++ b/packages/client/lib/client/multi-command.ts @@ -1,7 +1,7 @@ import COMMANDS from './commands'; import { RedisCommand, RedisCommandArguments, RedisCommandRawReply, RedisModules, RedisPlugins, RedisScript, RedisScripts } from '../commands'; import RedisMultiCommand, { RedisMultiQueuedCommand } from '../multi-command'; -import { extendWithCommands, extendWithModulesAndScripts } from '../commander'; +import { extendWithCommands, extendWithModulesAndScripts, LegacyCommandArguments, transformLegacyCommandArguments } from '../commander'; type RedisClientMultiCommandSignature = (...args: Parameters) => RedisClientMultiCommandType; @@ -52,8 +52,8 @@ export default class RedisClientMultiCommand { #legacyMode(): void { this.v4.addCommand = this.addCommand.bind(this); - (this as any).addCommand = (...args: Array>): this => { - this.#multi.addCommand(args.flat()); + (this as any).addCommand = (...args: LegacyCommandArguments): this => { + this.#multi.addCommand(transformLegacyCommandArguments(args)); return this; }; this.v4.exec = this.exec.bind(this); diff --git a/packages/client/lib/commander.ts b/packages/client/lib/commander.ts index 5871c2f235b..50d416f5b5c 100644 --- a/packages/client/lib/commander.ts +++ b/packages/client/lib/commander.ts @@ -113,3 +113,18 @@ export function transformCommandReply( return command.transformReply(rawReply, preserved); } + +export type LegacyCommandArguments = Array; + +export function transformLegacyCommandArguments(args: LegacyCommandArguments, flat: RedisCommandArguments = []): RedisCommandArguments { + for (const arg of args) { + if (Array.isArray(arg)) { + transformLegacyCommandArguments(arg, flat); + continue; + } + + flat.push(typeof arg === 'number' ? arg.toString() : arg); + } + + return flat; +} From d4f63219af829a7f8bdcb683fa375989cd1e4f81 Mon Sep 17 00:00:00 2001 From: Da-Jin Chu Date: Wed, 10 Nov 2021 23:44:22 -0500 Subject: [PATCH 0943/1748] Add rejectedUnauthorized and other TLS options (#1708) --- packages/client/lib/client/socket.ts | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/packages/client/lib/client/socket.ts b/packages/client/lib/client/socket.ts index 3a816604718..630d0c7b411 100644 --- a/packages/client/lib/client/socket.ts +++ b/packages/client/lib/client/socket.ts @@ -22,7 +22,7 @@ export interface RedisUnixSocketOptions extends RedisSocketCommonOptions { path: string; } -export interface RedisTlsSocketOptions extends RedisNetSocketOptions, tls.SecureContextOptions { +export interface RedisTlsSocketOptions extends RedisNetSocketOptions, tls.SecureContextOptions, tls.CommonConnectionOption { tls: true; } From a6a1637686a9ef3804e7c588e82299e542277ad7 Mon Sep 17 00:00:00 2001 From: Leibale Eidelman Date: Thu, 11 Nov 2021 00:45:34 -0500 Subject: [PATCH 0944/1748] Update socket.ts --- packages/client/lib/client/socket.ts | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/packages/client/lib/client/socket.ts b/packages/client/lib/client/socket.ts index 630d0c7b411..81a3aa098a2 100644 --- a/packages/client/lib/client/socket.ts +++ b/packages/client/lib/client/socket.ts @@ -22,7 +22,7 @@ export interface RedisUnixSocketOptions extends RedisSocketCommonOptions { path: string; } -export interface RedisTlsSocketOptions extends RedisNetSocketOptions, tls.SecureContextOptions, tls.CommonConnectionOption { +export interface RedisTlsSocketOptions extends RedisNetSocketOptions, tls.SecureContextOptions, tls.CommonConnectionOptions { tls: true; } From 41423fe8aa71cc9da34e1b6e3c80fa5675093ae0 Mon Sep 17 00:00:00 2001 From: leibale Date: Thu, 11 Nov 2021 17:43:17 -0500 Subject: [PATCH 0945/1748] fix #1716 - decode username and password from url --- packages/client/lib/client/index.ts | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/packages/client/lib/client/index.ts b/packages/client/lib/client/index.ts index dd1bf833344..05c219ca524 100644 --- a/packages/client/lib/client/index.ts +++ b/packages/client/lib/client/index.ts @@ -101,11 +101,11 @@ export default class RedisClient } if (username) { - parsed.username = username; + parsed.username = decodeURIComponent(username); } if (password) { - parsed.password = password; + parsed.password = decodeURIComponent(password); } if (pathname.length > 1) { From ba66892b7c317a5ce696b7910685c8dd0728a22b Mon Sep 17 00:00:00 2001 From: leibale Date: Thu, 11 Nov 2021 18:50:32 -0500 Subject: [PATCH 0946/1748] fix some Z (sorted list) commands, increase commands test coverage --- .../client/lib/commands/COMMAND_INFO.spec.ts | 17 ++++++++++++++--- packages/client/lib/commands/HSET.ts | 2 +- packages/client/lib/commands/ZCOUNT.ts | 8 ++++---- packages/client/lib/commands/ZRANGE.spec.ts | 7 ------- packages/client/lib/commands/ZRANGE.ts | 6 +++--- packages/client/lib/commands/ZRANGEBYLEX.ts | 6 +++--- packages/client/lib/commands/ZRANGEBYSCORE.ts | 6 +++--- packages/client/lib/commands/ZRANGESTORE.ts | 14 ++++++++++---- .../lib/commands/generic-transformers.spec.ts | 17 +++++++++++++++++ .../client/lib/commands/generic-transformers.ts | 6 ++++++ 10 files changed, 61 insertions(+), 28 deletions(-) diff --git a/packages/client/lib/commands/COMMAND_INFO.spec.ts b/packages/client/lib/commands/COMMAND_INFO.spec.ts index 3b2672dcb6b..8e02409001c 100644 --- a/packages/client/lib/commands/COMMAND_INFO.spec.ts +++ b/packages/client/lib/commands/COMMAND_INFO.spec.ts @@ -30,7 +30,18 @@ describe('COMMAND INFO', () => { ); }); - testUtils.testWithClient('client.commandInfo', async client => { - assertPingCommand((await client.commandInfo(['PING']))[0]); - }, GLOBAL.SERVERS.OPEN); + describe('client.commandInfo', () => { + testUtils.testWithClient('PING', async client => { + assertPingCommand((await client.commandInfo(['PING']))[0]); + }, GLOBAL.SERVERS.OPEN); + + testUtils.testWithClient('DOSE_NOT_EXISTS', async client => { + assert.deepEqual( + await client.commandInfo(['DOSE_NOT_EXISTS']), + [null] + ); + }, GLOBAL.SERVERS.OPEN); + }); + + }); diff --git a/packages/client/lib/commands/HSET.ts b/packages/client/lib/commands/HSET.ts index 4c60b91ad89..f7c56c5768d 100644 --- a/packages/client/lib/commands/HSET.ts +++ b/packages/client/lib/commands/HSET.ts @@ -23,7 +23,7 @@ export function transformArguments(...[ key, value, fieldValue ]: SingleFieldArg pushMap(args, value); } else if (Array.isArray(value)) { pushTuples(args, value); - } else if (typeof value === 'object' && value !== null) { + } else { pushObject(args, value); } diff --git a/packages/client/lib/commands/ZCOUNT.ts b/packages/client/lib/commands/ZCOUNT.ts index a18ba0fd790..83a0710fa11 100644 --- a/packages/client/lib/commands/ZCOUNT.ts +++ b/packages/client/lib/commands/ZCOUNT.ts @@ -1,15 +1,15 @@ -import { transformArgumentNumberInfinity } from './generic-transformers'; +import { transformArgumentStringNumberInfinity } from './generic-transformers'; export const FIRST_KEY_INDEX = 1; export const IS_READ_ONLY = true; -export function transformArguments(key: string, min: number, max: number): Array { +export function transformArguments(key: string, min: string | number, max: string | number): Array { return [ 'ZCOUNT', key, - transformArgumentNumberInfinity(min), - transformArgumentNumberInfinity(max) + transformArgumentStringNumberInfinity(min), + transformArgumentStringNumberInfinity(max) ]; } diff --git a/packages/client/lib/commands/ZRANGE.spec.ts b/packages/client/lib/commands/ZRANGE.spec.ts index 03687c2ba9d..a280aff0033 100644 --- a/packages/client/lib/commands/ZRANGE.spec.ts +++ b/packages/client/lib/commands/ZRANGE.spec.ts @@ -11,13 +11,6 @@ describe('ZRANGE', () => { ); }); - it('using strings', () => { - assert.deepEqual( - transformArguments('src', '0', '1'), - ['ZRANGE', 'src', '0', '1'] - ); - }); - it('with BYSCORE', () => { assert.deepEqual( transformArguments('src', 0, 1, { diff --git a/packages/client/lib/commands/ZRANGE.ts b/packages/client/lib/commands/ZRANGE.ts index 391c5ca893e..3d30e0f8dd2 100644 --- a/packages/client/lib/commands/ZRANGE.ts +++ b/packages/client/lib/commands/ZRANGE.ts @@ -1,4 +1,4 @@ -import { transformArgumentNumberInfinity } from './generic-transformers'; +import { transformArgumentStringNumberInfinity } from './generic-transformers'; export const FIRST_KEY_INDEX = 1; @@ -17,8 +17,8 @@ export function transformArguments(key: string, min: string | number, max: strin const args = [ 'ZRANGE', key, - typeof min === 'string' ? min : transformArgumentNumberInfinity(min), - typeof max === 'string' ? max : transformArgumentNumberInfinity(max) + transformArgumentStringNumberInfinity(min), + transformArgumentStringNumberInfinity(max) ]; switch (options?.BY) { diff --git a/packages/client/lib/commands/ZRANGEBYLEX.ts b/packages/client/lib/commands/ZRANGEBYLEX.ts index 7e2e7613b00..214d796da0a 100644 --- a/packages/client/lib/commands/ZRANGEBYLEX.ts +++ b/packages/client/lib/commands/ZRANGEBYLEX.ts @@ -1,5 +1,5 @@ import { RedisCommandArguments } from '.'; -import { transformArgumentNumberInfinity } from './generic-transformers'; +import { transformArgumentStringNumberInfinity } from './generic-transformers'; export const FIRST_KEY_INDEX = 1; @@ -21,8 +21,8 @@ export function transformArguments( const args = [ 'ZRANGEBYLEX', key, - typeof min === 'string' ? min : transformArgumentNumberInfinity(min), - typeof max === 'string' ? max : transformArgumentNumberInfinity(max) + transformArgumentStringNumberInfinity(min), + transformArgumentStringNumberInfinity(max) ]; if (options?.LIMIT) { diff --git a/packages/client/lib/commands/ZRANGEBYSCORE.ts b/packages/client/lib/commands/ZRANGEBYSCORE.ts index 48dd8a415c6..f6097fad581 100644 --- a/packages/client/lib/commands/ZRANGEBYSCORE.ts +++ b/packages/client/lib/commands/ZRANGEBYSCORE.ts @@ -1,5 +1,5 @@ import { RedisCommandArguments } from '.'; -import { transformArgumentNumberInfinity } from './generic-transformers'; +import { transformArgumentStringNumberInfinity } from './generic-transformers'; export const FIRST_KEY_INDEX = 1; @@ -21,8 +21,8 @@ export function transformArguments( const args = [ 'ZRANGEBYSCORE', key, - typeof min === 'string' ? min : transformArgumentNumberInfinity(min), - typeof max === 'string' ? max : transformArgumentNumberInfinity(max) + transformArgumentStringNumberInfinity(min), + transformArgumentStringNumberInfinity(max) ]; if (options?.LIMIT) { diff --git a/packages/client/lib/commands/ZRANGESTORE.ts b/packages/client/lib/commands/ZRANGESTORE.ts index 6ad75661668..c76afe61029 100644 --- a/packages/client/lib/commands/ZRANGESTORE.ts +++ b/packages/client/lib/commands/ZRANGESTORE.ts @@ -1,4 +1,4 @@ -import { transformArgumentNumberInfinity } from './generic-transformers'; +import { transformArgumentStringNumberInfinity } from './generic-transformers'; export const FIRST_KEY_INDEX = 1; @@ -12,13 +12,19 @@ interface ZRangeStoreOptions { WITHSCORES?: true; } -export function transformArguments(dst: string, src: string, min: number, max: number, options?: ZRangeStoreOptions): Array { +export function transformArguments( + dst: string, + src: string, + min: string | number, + max: string | number, + options?: ZRangeStoreOptions +): Array { const args = [ 'ZRANGESTORE', dst, src, - transformArgumentNumberInfinity(min), - transformArgumentNumberInfinity(max) + transformArgumentStringNumberInfinity(min), + transformArgumentStringNumberInfinity(max) ]; switch (options?.BY) { diff --git a/packages/client/lib/commands/generic-transformers.spec.ts b/packages/client/lib/commands/generic-transformers.spec.ts index 887d8720def..fa5c5086136 100644 --- a/packages/client/lib/commands/generic-transformers.spec.ts +++ b/packages/client/lib/commands/generic-transformers.spec.ts @@ -7,6 +7,7 @@ import { transformReplyNumberInfinityArray, transformReplyNumberInfinityNull, transformArgumentNumberInfinity, + transformArgumentStringNumberInfinity, transformReplyTuples, transformReplyStreamMessages, transformReplyStreamsMessages, @@ -175,6 +176,22 @@ describe('Generic Transformers', () => { }); }); + describe('transformArgumentStringNumberInfinity', () => { + it("'0.5'", () => { + assert.equal( + transformArgumentStringNumberInfinity('0.5'), + '0.5' + ); + }); + + it('0.5', () => { + assert.equal( + transformArgumentStringNumberInfinity(0.5), + '0.5' + ); + }); + }); + it('transformReplyTuples', () => { assert.deepEqual( transformReplyTuples(['key1', 'value1', 'key2', 'value2']), diff --git a/packages/client/lib/commands/generic-transformers.ts b/packages/client/lib/commands/generic-transformers.ts index a531e86b432..3075636e756 100644 --- a/packages/client/lib/commands/generic-transformers.ts +++ b/packages/client/lib/commands/generic-transformers.ts @@ -69,6 +69,12 @@ export function transformArgumentNumberInfinity(num: number): string { } } +export function transformArgumentStringNumberInfinity(num: string | number): string { + if (typeof num === 'string') return num; + + return transformArgumentNumberInfinity(num); +} + export interface TuplesObject { [field: string]: string; } From f93e28f8f2439a19bade4491903f5227848fd3bc Mon Sep 17 00:00:00 2001 From: leibale Date: Thu, 11 Nov 2021 18:52:31 -0500 Subject: [PATCH 0947/1748] remove empty lines --- packages/client/lib/commands/COMMAND_INFO.spec.ts | 2 -- 1 file changed, 2 deletions(-) diff --git a/packages/client/lib/commands/COMMAND_INFO.spec.ts b/packages/client/lib/commands/COMMAND_INFO.spec.ts index 8e02409001c..d6488460484 100644 --- a/packages/client/lib/commands/COMMAND_INFO.spec.ts +++ b/packages/client/lib/commands/COMMAND_INFO.spec.ts @@ -42,6 +42,4 @@ describe('COMMAND INFO', () => { ); }, GLOBAL.SERVERS.OPEN); }); - - }); From a9337b414f84d89ab39e8cf361f73914eed14c9f Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Henrique=20Corr=C3=AAa?= <75134774+HeCorr@users.noreply.github.com> Date: Sun, 14 Nov 2021 17:52:59 -0300 Subject: [PATCH 0948/1748] fix 'Scenario' typo (#1720) --- docs/isolated-execution.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/docs/isolated-execution.md b/docs/isolated-execution.md index 78b34252a0f..1fd16b8f462 100644 --- a/docs/isolated-execution.md +++ b/docs/isolated-execution.md @@ -10,7 +10,7 @@ Below are several examples of how to use isolated execution. > NOTE: Behind the scences we're using [`generic-pool`](https://www.npmjs.com/package/generic-pool) to provide a pool of connections that can be isolated. Go there to learn more. -## The Simple Secnario +## The Simple Scenario This just isolates execution on a single connection. Do what you want with that connection: From 994d8dcbe1300aa9096676b171c1ce7e739320f3 Mon Sep 17 00:00:00 2001 From: leibale Date: Mon, 15 Nov 2021 16:00:28 -0500 Subject: [PATCH 0949/1748] update readmes, add createCluster to the `redis` package --- .github/README.md | 317 ++++++++++++++++++++++++++++++++++ README.md | 319 +---------------------------------- index.ts | 14 +- packages/client/README.md | 2 + packages/client/package.json | 2 +- packages/json/.npmignore | 5 + packages/json/README.md | 2 + packages/search/.npmignore | 5 + packages/search/README.md | 2 + 9 files changed, 349 insertions(+), 319 deletions(-) create mode 100644 .github/README.md create mode 100644 packages/client/README.md create mode 100644 packages/json/.npmignore create mode 100644 packages/json/README.md create mode 100644 packages/search/.npmignore create mode 100644 packages/search/README.md diff --git a/.github/README.md b/.github/README.md new file mode 100644 index 00000000000..b5894f6f708 --- /dev/null +++ b/.github/README.md @@ -0,0 +1,317 @@ +

+ + + +

Node Redis

+

+ + + +--- + +## 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 { + 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](./packages/all-in-one) | | +| [@redis/client](./packages/client) | | +| [@redis/json](./packages/json) | [Redis JSON](https://oss.redis.com/redisjson/) commands | +| [@redis/search](./packages/search) | [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! + + + + + +## License + +This repository is licensed under the "MIT" license. See [LICENSE](LICENSE). diff --git a/README.md b/README.md index b5894f6f708..a98e6a261b2 100644 --- a/README.md +++ b/README.md @@ -1,317 +1,2 @@ -

- - - -

Node Redis

-

- - - ---- - -## 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 { - 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](./packages/all-in-one) | | -| [@redis/client](./packages/client) | | -| [@redis/json](./packages/json) | [Redis JSON](https://oss.redis.com/redisjson/) commands | -| [@redis/search](./packages/search) | [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! - - - - - -## License - -This repository is licensed under the "MIT" license. See [LICENSE](LICENSE). +# redis +The sources and docs for this package are in the main [node-redis](https://github.com/redis/node-redis) repo. diff --git a/index.ts b/index.ts index 3bbb4c9d943..610da7c2bdf 100644 --- a/index.ts +++ b/index.ts @@ -1,9 +1,12 @@ -import { createClient as _createClient } from '@redis/client'; +import { createClient as _createClient, createCluster as _createCluster } from '@redis/client'; import { RedisScripts } from '@redis/client/dist/lib/commands'; import { RedisClientOptions, RedisClientType } from '@redis/client/dist/lib/client'; +import { RedisClusterOptions, RedisClusterType } from '@redis/client/dist/lib/cluster'; import RedisJSON from '@redis/json'; import RediSearch from '@redis/search'; +export * from '@redis/client'; +export * from '@redis/json'; export * from '@redis/search'; const modules = { @@ -19,3 +22,12 @@ export function createClient>( modules }); } + +export function createCluster>( + options: Omit, 'modules'> +): RedisClusterType { + return _createCluster({ + ...options, + modules + }); +} diff --git a/packages/client/README.md b/packages/client/README.md new file mode 100644 index 00000000000..d9c8f80f2f3 --- /dev/null +++ b/packages/client/README.md @@ -0,0 +1,2 @@ +# @redis/client +The sources and docs for this package are in the main [node-redis](https://github.com/redis/node-redis) repo. diff --git a/packages/client/package.json b/packages/client/package.json index 0288ed25644..88f40a4b19d 100644 --- a/packages/client/package.json +++ b/packages/client/package.json @@ -45,5 +45,5 @@ "bugs": { "url": "https://github.com/redis/node-redis/issues" }, - "homepage": "https://github.com/redis/node-redis" + "homepage": "https://github.com/redis/node-redis/tree/master/packages/client" } diff --git a/packages/json/.npmignore b/packages/json/.npmignore new file mode 100644 index 00000000000..6b24efa5953 --- /dev/null +++ b/packages/json/.npmignore @@ -0,0 +1,5 @@ +.nyc_output/ +coverage/ +lib/ +.nycrc.json +tsconfig.json diff --git a/packages/json/README.md b/packages/json/README.md new file mode 100644 index 00000000000..45264d671b4 --- /dev/null +++ b/packages/json/README.md @@ -0,0 +1,2 @@ +# @redis/json +The sources and docs for this package are in the main [node-redis](https://github.com/redis/node-redis) repo. diff --git a/packages/search/.npmignore b/packages/search/.npmignore new file mode 100644 index 00000000000..6b24efa5953 --- /dev/null +++ b/packages/search/.npmignore @@ -0,0 +1,5 @@ +.nyc_output/ +coverage/ +lib/ +.nycrc.json +tsconfig.json diff --git a/packages/search/README.md b/packages/search/README.md new file mode 100644 index 00000000000..08db1a72dcf --- /dev/null +++ b/packages/search/README.md @@ -0,0 +1,2 @@ +# @redis/search +The sources and docs for this package are in the main [node-redis](https://github.com/redis/node-redis) repo. From 3979e30222bbb00c60d1129f14fb5fc722e48832 Mon Sep 17 00:00:00 2001 From: leibale Date: Mon, 15 Nov 2021 16:42:34 -0500 Subject: [PATCH 0950/1748] add .release-it.json files, update some md files --- .github/README.md | 28 +++++++++++----------------- .release-it.json | 5 +++++ docs/client-configuration.md | 2 +- packages/client/.release-it.json | 5 +++++ packages/json/.release-it.json | 5 +++++ packages/search/.release-it.json | 5 +++++ 6 files changed, 32 insertions(+), 18 deletions(-) create mode 100644 .release-it.json create mode 100644 packages/client/.release-it.json create mode 100644 packages/json/.release-it.json create mode 100644 packages/search/.release-it.json diff --git a/.github/README.md b/.github/README.md index b5894f6f708..6bc4458828d 100644 --- a/.github/README.md +++ b/.github/README.md @@ -9,12 +9,6 @@ Coverage - - Downloads - - - Version - Chat @@ -28,7 +22,7 @@ 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). +> :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 @@ -57,7 +51,7 @@ createClient({ }); ``` -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). +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 @@ -115,7 +109,7 @@ const [setKeyReply, otherKeyValue] = await client 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). +To dig deeper into transactions, check out the [Isolated Execution Guide](../docs/isolated-execution.md). ### Blocking Commands @@ -133,7 +127,7 @@ await client.lPush('key', ['1', '2']); await blPopPromise; // '2' ``` -To learn more about isolated execution, check out the [guide](./docs/isolated-execution.md). +To learn more about isolated execution, check out the [guide](../docs/isolated-execution.md). ### Pub/Sub @@ -278,7 +272,7 @@ await Promise.all([ ### Clustering -Check out the [Clustering Guide](./docs/clustering.md) when using Node Redis to connect to a Redis Cluster. +Check out the [Clustering Guide](../docs/clustering.md) when using Node Redis to connect to a Redis Cluster. ## Supported Redis versions @@ -295,12 +289,12 @@ Node Redis is supported with the following versions of Redis: ## Packages -| Name | Description | -|------------------------------------|------------------------------------------------------------| -| [redis](./packages/all-in-one) | | -| [@redis/client](./packages/client) | | -| [@redis/json](./packages/json) | [Redis JSON](https://oss.redis.com/redisjson/) commands | -| [@redis/search](./packages/search) | [Redis Search](https://oss.redis.com/redisearch/) commands | +| Name | Description | +|-------------------------------------|------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------| +| [redis](../) | [![Downloads](https://img.shields.io/npm/dm/redis.svg)](https://www.npmjs.com/package/redis/v/next) [![Version](https://img.shields.io/npm/v/redis/next.svg)](https://www.npmjs.com/package/redis/v/next) | +| [@redis/client](../packages/client) | [![Downloads](https://img.shields.io/npm/dm/@redis/client.svg)](https://www.npmjs.com/package/@redis/client/v/next) [![Version](https://img.shields.io/npm/v/@redis/client/next.svg)](https://www.npmjs.com/package/@redis/client/v/next) | +| [@redis/json](../packages/json) | [![Downloads](https://img.shields.io/npm/dm/@redis/json.svg)](https://www.npmjs.com/package/@redis/json/v/next) [![Version](https://img.shields.io/npm/v/@redis/json/next.svg)](https://www.npmjs.com/package/@redis/json/v/next) [Redis JSON](https://oss.redis.com/redisjson/) commands | +| [@redis/search](../packages/search) | [![Downloads](https://img.shields.io/npm/dm/@redis/search.svg)](https://www.npmjs.com/package/@redis/search/v/next) [![Version](https://img.shields.io/npm/v/@redis/search/next.svg)](https://www.npmjs.com/package/@redis/search/v/next) [Redis Search](https://oss.redis.com/redisearch/) commands | ## Contributing diff --git a/.release-it.json b/.release-it.json new file mode 100644 index 00000000000..984af58564a --- /dev/null +++ b/.release-it.json @@ -0,0 +1,5 @@ +{ + "git": { + "tagName": "redis@${version}" + } +} \ No newline at end of file diff --git a/docs/client-configuration.md b/docs/client-configuration.md index 3b6b7dcad73..1dbbdd8cba2 100644 --- a/docs/client-configuration.md +++ b/docs/client-configuration.md @@ -15,7 +15,7 @@ | 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](../../README.md#modules) to include | +| modules | | Object defining which [Redis Modules](../.github/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 | diff --git a/packages/client/.release-it.json b/packages/client/.release-it.json new file mode 100644 index 00000000000..d8e2b65186a --- /dev/null +++ b/packages/client/.release-it.json @@ -0,0 +1,5 @@ +{ + "git": { + "tagName": "client@${version}" + } +} \ No newline at end of file diff --git a/packages/json/.release-it.json b/packages/json/.release-it.json new file mode 100644 index 00000000000..3486f4456ad --- /dev/null +++ b/packages/json/.release-it.json @@ -0,0 +1,5 @@ +{ + "git": { + "tagName": "json@${version}" + } +} \ No newline at end of file diff --git a/packages/search/.release-it.json b/packages/search/.release-it.json new file mode 100644 index 00000000000..c181fc970b1 --- /dev/null +++ b/packages/search/.release-it.json @@ -0,0 +1,5 @@ +{ + "git": { + "tagName": "search@${version}" + } +} \ No newline at end of file From ddf539ad5fa9d515d5d0bace33805f73dfa00bdd Mon Sep 17 00:00:00 2001 From: leibale Date: Mon, 15 Nov 2021 16:45:10 -0500 Subject: [PATCH 0951/1748] run tests on pull requests too --- .github/workflows/documentation.yml | 5 ----- .github/workflows/tests.yml | 11 ++++------- 2 files changed, 4 insertions(+), 12 deletions(-) diff --git a/.github/workflows/documentation.yml b/.github/workflows/documentation.yml index 9575d4639b9..861010b7f01 100644 --- a/.github/workflows/documentation.yml +++ b/.github/workflows/documentation.yml @@ -9,21 +9,16 @@ on: jobs: documentation: runs-on: ubuntu-latest - steps: - uses: actions/checkout@v2.3.4 with: fetch-depth: 1 - - name: Use Node.js uses: actions/setup-node@v2.3.0 - - name: Install Packages run: npm ci - - name: Generate Documentation run: npm run documentation - - name: Upload Documentation to Wiki uses: SwiftDocOrg/github-wiki-publish-action@v1 with: diff --git a/.github/workflows/tests.yml b/.github/workflows/tests.yml index ad34df0b33a..f8e68c0e203 100644 --- a/.github/workflows/tests.yml +++ b/.github/workflows/tests.yml @@ -5,6 +5,10 @@ on: branches: - master - v4.0 + pull_request: + branches: + - master + - v4.0 jobs: tests: @@ -14,30 +18,23 @@ jobs: matrix: node-version: [12, 14, 16] redis-version: [5, 6.0, 6.2] - steps: - uses: actions/checkout@v2.3.4 with: fetch-depth: 1 - - name: Use Node.js ${{ matrix.node-version }} uses: actions/setup-node@v2.3.0 with: node-version: ${{ matrix.node-version }} - - name: Update npm run: npm i -g npm if: ${{ matrix.node-version <= 14 }} - - name: Install Packages run: npm ci - - name: Build tests tools run: npm run build:tests-tools - - name: Run Tests run: npm run test -- --forbid-only --redis-version=${{ matrix.redis-version }} - - name: Upload to Codecov run: | curl https://keybase.io/codecovsecurity/pgp_keys.asc | gpg --no-default-keyring --keyring trustedkeys.gpg --import From 7d2629f083b6067c8986162cba48f5d5349e66d7 Mon Sep 17 00:00:00 2001 From: Evert Pot Date: Mon, 15 Nov 2021 16:52:48 -0500 Subject: [PATCH 0952/1748] 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 --- package-lock.json | 116 ++++++++++++------ packages/client/lib/client/commands-queue.ts | 8 +- packages/client/lib/client/index.ts | 2 +- packages/client/lib/client/socket.ts | 6 +- packages/client/lib/cluster/cluster-slots.ts | 6 +- packages/client/lib/cluster/index.spec.ts | 6 +- .../lib/ts-declarations/redis-parser.d.ts | 15 --- packages/client/package.json | 1 + packages/test-utils/lib/dockers.ts | 2 +- tsconfig.base.json | 6 +- 10 files changed, 104 insertions(+), 64 deletions(-) delete mode 100644 packages/client/lib/ts-declarations/redis-parser.d.ts diff --git a/package-lock.json b/package-lock.json index 2b4d4be9c61..22f8983cac2 100644 --- a/package-lock.json +++ b/package-lock.json @@ -836,10 +836,6 @@ "resolved": "packages/test-utils", "link": true }, - "node_modules/@redis/time-series": { - "resolved": "packages/time-series", - "link": true - }, "node_modules/@sindresorhus/is": { "version": "4.2.0", "resolved": "https://registry.npmjs.org/@sindresorhus/is/-/is-4.2.0.tgz", @@ -974,6 +970,22 @@ "integrity": "sha512-//oorEZjL6sbPcKUaCdIGlIUeH26mgzimjBB77G6XRgnDl/L5wOnpyBGRe/Mmf5CVW3PwEBE1NjiMZ/ssFh4wA==", "dev": true }, + "node_modules/@types/redis-errors": { + "version": "1.2.1", + "resolved": "https://registry.npmjs.org/@types/redis-errors/-/redis-errors-1.2.1.tgz", + "integrity": "sha512-9p6SQPeDAIR4z3ZdrPsRH/sSRMeIA2fdRKqZ3Y1thQOLeDH4aLY+J4Ze32zjg4Dq7655Y0LonCoRrH5O7vtr4w==", + "dev": true + }, + "node_modules/@types/redis-parser": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/@types/redis-parser/-/redis-parser-3.0.0.tgz", + "integrity": "sha512-TH9NnE980dXWKjFRkck6FIv9akrD6G+vX9XXONoBgG9+NfP4iZ+SbGkaN7S15c6+JeZ+zBN62bHt2lNyUYwqoA==", + "dev": true, + "dependencies": { + "@types/node": "*", + "@types/redis-errors": "*" + } + }, "node_modules/@types/responselike": { "version": "1.0.0", "resolved": "https://registry.npmjs.org/@types/responselike/-/responselike-1.0.0.tgz", @@ -5556,13 +5568,13 @@ "dev": true }, "node_modules/sinon": { - "version": "11.1.2", - "resolved": "https://registry.npmjs.org/sinon/-/sinon-11.1.2.tgz", - "integrity": "sha512-59237HChms4kg7/sXhiRcUzdSkKuydDeTiamT/jesUVHshBgL8XAmhgFo0GfK6RruMDM/iRSij1EybmMog9cJw==", + "version": "12.0.1", + "resolved": "https://registry.npmjs.org/sinon/-/sinon-12.0.1.tgz", + "integrity": "sha512-iGu29Xhym33ydkAT+aNQFBINakjq69kKO6ByPvTsm3yyIACfyQttRTP03aBP/I8GfhFmLzrnKwNNkr0ORb1udg==", "dev": true, "dependencies": { "@sinonjs/commons": "^1.8.3", - "@sinonjs/fake-timers": "^7.1.2", + "@sinonjs/fake-timers": "^8.1.0", "@sinonjs/samsam": "^6.0.2", "diff": "^5.0.0", "nise": "^5.1.0", @@ -5573,6 +5585,15 @@ "url": "https://opencollective.com/sinon" } }, + "node_modules/sinon/node_modules/@sinonjs/fake-timers": { + "version": "8.1.0", + "resolved": "https://registry.npmjs.org/@sinonjs/fake-timers/-/fake-timers-8.1.0.tgz", + "integrity": "sha512-OAPJUAtgeINhh/TAlUID4QTs53Njm7xzddaVlEs/SXwgtiD1tW22zAB/W1wdqfrpmikgaWQ9Fw6Ws+hsiRm5Vg==", + "dev": true, + "dependencies": { + "@sinonjs/commons": "^1.7.0" + } + }, "node_modules/slash": { "version": "3.0.0", "resolved": "https://registry.npmjs.org/slash/-/slash-3.0.0.tgz", @@ -6390,6 +6411,7 @@ } }, "packages/client": { + "name": "@redis/client", "version": "1.0.0-rc", "license": "MIT", "dependencies": { @@ -6401,18 +6423,19 @@ "devDependencies": { "@istanbuljs/nyc-config-typescript": "^1.0.1", "@redis/test-utils": "*", - "@types/node": "^16.11.6", + "@types/node": "^16.11.7", + "@types/redis-parser": "^3.0.0", "@types/sinon": "^10.0.6", "@types/yallist": "^4.0.1", - "@typescript-eslint/eslint-plugin": "^5.2.0", - "@typescript-eslint/parser": "^5.2.0", - "eslint": "^8.1.0", + "@typescript-eslint/eslint-plugin": "^5.3.1", + "@typescript-eslint/parser": "^5.3.1", + "eslint": "^8.2.0", "nyc": "^15.1.0", "release-it": "^14.11.6", - "sinon": "^11.1.2", + "sinon": "^12.0.1", "source-map-support": "^0.5.20", "ts-node": "^10.4.0", - "typedoc": "^0.22.7", + "typedoc": "^0.22.8", "typedoc-github-wiki-theme": "^0.6.0", "typedoc-plugin-markdown": "^3.11.3", "typescript": "^4.4.4" @@ -6422,6 +6445,7 @@ } }, "packages/json": { + "name": "@redis/json", "version": "1.0.0-rc.0", "license": "MIT", "devDependencies": { @@ -6439,6 +6463,7 @@ } }, "packages/search": { + "name": "@redis/search", "version": "1.0.0-rc.0", "license": "MIT", "devDependencies": { @@ -6456,6 +6481,7 @@ } }, "packages/test-utils": { + "name": "@redis/test-utils", "devDependencies": { "@istanbuljs/nyc-config-typescript": "^1.0.1", "@tsconfig/node12": "^1.0.9", @@ -6476,6 +6502,7 @@ }, "packages/time-series": { "version": "1.0.0", + "extraneous": true, "license": "ISC", "devDependencies": { "@istanbuljs/nyc-config-typescript": "^1.0.1", @@ -7129,21 +7156,22 @@ "requires": { "@istanbuljs/nyc-config-typescript": "^1.0.1", "@redis/test-utils": "*", - "@types/node": "^16.11.6", + "@types/node": "^16.11.7", + "@types/redis-parser": "*", "@types/sinon": "^10.0.6", "@types/yallist": "^4.0.1", - "@typescript-eslint/eslint-plugin": "^5.2.0", - "@typescript-eslint/parser": "^5.2.0", + "@typescript-eslint/eslint-plugin": "^5.3.1", + "@typescript-eslint/parser": "^5.3.1", "cluster-key-slot": "1.1.0", - "eslint": "^8.1.0", + "eslint": "^8.2.0", "generic-pool": "3.8.2", "nyc": "^15.1.0", "redis-parser": "3.0.0", "release-it": "^14.11.6", - "sinon": "^11.1.2", + "sinon": "^12.0.1", "source-map-support": "^0.5.20", "ts-node": "^10.4.0", - "typedoc": "^0.22.7", + "typedoc": "^0.22.8", "typedoc-github-wiki-theme": "^0.6.0", "typedoc-plugin-markdown": "^3.11.3", "typescript": "^4.4.4", @@ -7193,19 +7221,6 @@ "yargs": "^17.2.1" } }, - "@redis/time-series": { - "version": "file:packages/time-series", - "requires": { - "@istanbuljs/nyc-config-typescript": "^1.0.1", - "@redis/test-utils": "*", - "@types/node": "^16.11.6", - "nyc": "^15.1.0", - "release-it": "^14.11.6", - "source-map-support": "^0.5.20", - "ts-node": "^10.4.0", - "typescript": "^4.4.4" - } - }, "@sindresorhus/is": { "version": "4.2.0", "resolved": "https://registry.npmjs.org/@sindresorhus/is/-/is-4.2.0.tgz", @@ -7331,6 +7346,22 @@ "integrity": "sha512-//oorEZjL6sbPcKUaCdIGlIUeH26mgzimjBB77G6XRgnDl/L5wOnpyBGRe/Mmf5CVW3PwEBE1NjiMZ/ssFh4wA==", "dev": true }, + "@types/redis-errors": { + "version": "1.2.1", + "resolved": "https://registry.npmjs.org/@types/redis-errors/-/redis-errors-1.2.1.tgz", + "integrity": "sha512-9p6SQPeDAIR4z3ZdrPsRH/sSRMeIA2fdRKqZ3Y1thQOLeDH4aLY+J4Ze32zjg4Dq7655Y0LonCoRrH5O7vtr4w==", + "dev": true + }, + "@types/redis-parser": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/@types/redis-parser/-/redis-parser-3.0.0.tgz", + "integrity": "sha512-TH9NnE980dXWKjFRkck6FIv9akrD6G+vX9XXONoBgG9+NfP4iZ+SbGkaN7S15c6+JeZ+zBN62bHt2lNyUYwqoA==", + "dev": true, + "requires": { + "@types/node": "*", + "@types/redis-errors": "*" + } + }, "@types/responselike": { "version": "1.0.0", "resolved": "https://registry.npmjs.org/@types/responselike/-/responselike-1.0.0.tgz", @@ -10719,17 +10750,28 @@ "dev": true }, "sinon": { - "version": "11.1.2", - "resolved": "https://registry.npmjs.org/sinon/-/sinon-11.1.2.tgz", - "integrity": "sha512-59237HChms4kg7/sXhiRcUzdSkKuydDeTiamT/jesUVHshBgL8XAmhgFo0GfK6RruMDM/iRSij1EybmMog9cJw==", + "version": "12.0.1", + "resolved": "https://registry.npmjs.org/sinon/-/sinon-12.0.1.tgz", + "integrity": "sha512-iGu29Xhym33ydkAT+aNQFBINakjq69kKO6ByPvTsm3yyIACfyQttRTP03aBP/I8GfhFmLzrnKwNNkr0ORb1udg==", "dev": true, "requires": { "@sinonjs/commons": "^1.8.3", - "@sinonjs/fake-timers": "^7.1.2", + "@sinonjs/fake-timers": "^8.1.0", "@sinonjs/samsam": "^6.0.2", "diff": "^5.0.0", "nise": "^5.1.0", "supports-color": "^7.2.0" + }, + "dependencies": { + "@sinonjs/fake-timers": { + "version": "8.1.0", + "resolved": "https://registry.npmjs.org/@sinonjs/fake-timers/-/fake-timers-8.1.0.tgz", + "integrity": "sha512-OAPJUAtgeINhh/TAlUID4QTs53Njm7xzddaVlEs/SXwgtiD1tW22zAB/W1wdqfrpmikgaWQ9Fw6Ws+hsiRm5Vg==", + "dev": true, + "requires": { + "@sinonjs/commons": "^1.7.0" + } + } } }, "slash": { diff --git a/packages/client/lib/client/commands-queue.ts b/packages/client/lib/client/commands-queue.ts index 4e2a66c606e..4fcae1e8b63 100644 --- a/packages/client/lib/client/commands-queue.ts +++ b/packages/client/lib/client/commands-queue.ts @@ -1,8 +1,12 @@ -import LinkedList from 'yallist'; -import RedisParser from 'redis-parser'; +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; diff --git a/packages/client/lib/client/index.ts b/packages/client/lib/client/index.ts index 05c219ca524..8802631eda1 100644 --- a/packages/client/lib/client/index.ts +++ b/packages/client/lib/client/index.ts @@ -4,7 +4,7 @@ import RedisSocket, { RedisSocketOptions, RedisNetSocketOptions, RedisTlsSocketO import RedisCommandsQueue, { PubSubListener, PubSubSubscribeCommands, PubSubUnsubscribeCommands, QueueCommandOptions } from './commands-queue'; import RedisClientMultiCommand, { RedisClientMultiCommandType } from './multi-command'; import { RedisMultiQueuedCommand } from '../multi-command'; -import EventEmitter from 'events'; +import { EventEmitter } from 'events'; import { CommandOptions, commandOptions, isCommandOptions } from '../command-options'; import { ScanOptions, ZMember } from '../commands/generic-transformers'; import { ScanCommandOptions } from '../commands/SCAN'; diff --git a/packages/client/lib/client/socket.ts b/packages/client/lib/client/socket.ts index 81a3aa098a2..d42b42d64d6 100644 --- a/packages/client/lib/client/socket.ts +++ b/packages/client/lib/client/socket.ts @@ -1,6 +1,6 @@ -import EventEmitter from 'events'; -import net from 'net'; -import tls from 'tls'; +import { EventEmitter } from 'events'; +import * as net from 'net'; +import * as tls from 'tls'; import { encodeCommand } from '../commander'; import { RedisCommandArguments } from '../commands'; import { ConnectionTimeoutError, ClientClosedError, SocketClosedUnexpectedlyError } from '../errors'; diff --git a/packages/client/lib/cluster/cluster-slots.ts b/packages/client/lib/cluster/cluster-slots.ts index 4c53445b680..ff4c79b4d36 100644 --- a/packages/client/lib/cluster/cluster-slots.ts +++ b/packages/client/lib/cluster/cluster-slots.ts @@ -1,9 +1,13 @@ -import calculateSlot from 'cluster-key-slot'; import RedisClient, { InstantiableRedisClient, RedisClientType } from '../client'; import { RedisClusterMasterNode, RedisClusterReplicaNode } from '../commands/CLUSTER_NODES'; import { RedisClusterClientOptions, RedisClusterOptions } from '.'; import { RedisModules, RedisScripts } from '../commands'; +// We need to use 'require', because it's not possible with Typescript to import +// function that are exported as 'module.exports = function`, without esModuleInterop +// set to true. +const calculateSlot = require('cluster-key-slot'); + export interface ClusterNode { id: string; client: RedisClientType; diff --git a/packages/client/lib/cluster/index.spec.ts b/packages/client/lib/cluster/index.spec.ts index c926c02d03a..66d458522e4 100644 --- a/packages/client/lib/cluster/index.spec.ts +++ b/packages/client/lib/cluster/index.spec.ts @@ -1,9 +1,13 @@ import { strict as assert } from 'assert'; import testUtils, { GLOBAL } from '../test-utils'; -import calculateSlot from 'cluster-key-slot'; import { ClusterSlotStates } from '../commands/CLUSTER_SETSLOT'; import { SQUARE_SCRIPT } from '../client/index.spec'; +// We need to use 'require', because it's not possible with Typescript to import +// function that are exported as 'module.exports = function`, without esModuleInterop +// set to true. +const calculateSlot = require('cluster-key-slot'); + describe('Cluster', () => { testUtils.testWithCluster('sendCommand', async cluster => { await cluster.connect(); diff --git a/packages/client/lib/ts-declarations/redis-parser.d.ts b/packages/client/lib/ts-declarations/redis-parser.d.ts deleted file mode 100644 index 7ec129ed8cd..00000000000 --- a/packages/client/lib/ts-declarations/redis-parser.d.ts +++ /dev/null @@ -1,15 +0,0 @@ -declare module 'redis-parser' { - interface RedisParserCallbacks { - returnReply(reply: unknown): void; - returnError(err: Error): void; - returnFatalError?(err: Error): void; - } - - export default class RedisParser { - constructor(callbacks: RedisParserCallbacks); - - setReturnBuffers(returnBuffers?: boolean): void; - - execute(buffer: Buffer): void; - } -} diff --git a/packages/client/package.json b/packages/client/package.json index 88f40a4b19d..1f8226cc987 100644 --- a/packages/client/package.json +++ b/packages/client/package.json @@ -20,6 +20,7 @@ "@istanbuljs/nyc-config-typescript": "^1.0.1", "@redis/test-utils": "*", "@types/node": "^16.11.7", + "@types/redis-parser": "^3.0.0", "@types/sinon": "^10.0.6", "@types/yallist": "^4.0.1", "@typescript-eslint/eslint-plugin": "^5.3.1", diff --git a/packages/test-utils/lib/dockers.ts b/packages/test-utils/lib/dockers.ts index 28bd1e49057..052388970ec 100644 --- a/packages/test-utils/lib/dockers.ts +++ b/packages/test-utils/lib/dockers.ts @@ -3,7 +3,7 @@ import { once } from 'events'; import { RedisModules, RedisScripts } from '@redis/client/lib/commands'; import RedisClient, { RedisClientType } from '@redis/client/lib/client'; import { promiseTimeout } from '@redis/client/lib/utils'; -import path from 'path'; +import * as path from 'path'; import { promisify } from 'util'; import { exec } from 'child_process'; const execAsync = promisify(exec); diff --git a/tsconfig.base.json b/tsconfig.base.json index d6cd971b01d..7df81029664 100644 --- a/tsconfig.base.json +++ b/tsconfig.base.json @@ -3,11 +3,11 @@ "compilerOptions": { "declaration": true, "allowJs": true, - "useDefineForClassFields": true + "useDefineForClassFields": true, + "esModuleInterop": false }, "files": [ - "./packages/client/lib/ts-declarations/cluster-key-slot.d.ts", - "./packages/client/lib/ts-declarations/redis-parser.d.ts" + "./packages/client/lib/ts-declarations/cluster-key-slot.d.ts" ], "ts-node": { "files": true From 93e7c908af57c0c4669dc3af0d38321875b10271 Mon Sep 17 00:00:00 2001 From: leibale Date: Mon, 15 Nov 2021 17:11:39 -0500 Subject: [PATCH 0953/1748] npm update, remove html from readme --- .github/README.md | 25 +-- package-lock.json | 297 ++++++++++++++----------------- package.json | 2 +- packages/client/package.json | 10 +- packages/json/package.json | 4 +- packages/search/package.json | 4 +- packages/test-utils/package.json | 5 +- 7 files changed, 155 insertions(+), 192 deletions(-) diff --git a/.github/README.md b/.github/README.md index 6bc4458828d..171850fd2dd 100644 --- a/.github/README.md +++ b/.github/README.md @@ -1,20 +1,7 @@ -

- - - -

Node Redis

-

- - - ---- +# Node-Redis + +[![Coverage](https://codecov.io/gh/redis/node-redis/branch/master/graph/badge.svg?token=xcfqHhJC37)](https://codecov.io/gh/redis/node-redis) +[![Chat](https://img.shields.io/discord/697882427875393627)](https://discord.gg/XMMVgxUm) ## Installation @@ -302,9 +289,7 @@ If you'd like to contribute, check out the [contributing guide](CONTRIBUTING.md) Thank you to all the people who already contributed to Node Redis! - - - +[![Contributors](https://contrib.rocks/image?repo=redis/node-redis)](https://github.com/redis/node-redis/graphs/contributors) ## License diff --git a/package-lock.json b/package-lock.json index 22f8983cac2..2732b2d9a16 100644 --- a/package-lock.json +++ b/package-lock.json @@ -18,7 +18,7 @@ }, "devDependencies": { "@tsconfig/node12": "^1.0.9", - "release-it": "^14.11.6", + "release-it": "^14.11.7", "typescript": "^4.4.4" } }, @@ -1026,13 +1026,13 @@ "dev": true }, "node_modules/@typescript-eslint/eslint-plugin": { - "version": "5.3.1", - "resolved": "https://registry.npmjs.org/@typescript-eslint/eslint-plugin/-/eslint-plugin-5.3.1.tgz", - "integrity": "sha512-cFImaoIr5Ojj358xI/SDhjog57OK2NqlpxwdcgyxDA3bJlZcJq5CPzUXtpD7CxI2Hm6ATU7w5fQnnkVnmwpHqw==", + "version": "5.4.0", + "resolved": "https://registry.npmjs.org/@typescript-eslint/eslint-plugin/-/eslint-plugin-5.4.0.tgz", + "integrity": "sha512-9/yPSBlwzsetCsGEn9j24D8vGQgJkOTr4oMLas/w886ZtzKIs1iyoqFrwsX2fqYEeUwsdBpC21gcjRGo57u0eg==", "dev": true, "dependencies": { - "@typescript-eslint/experimental-utils": "5.3.1", - "@typescript-eslint/scope-manager": "5.3.1", + "@typescript-eslint/experimental-utils": "5.4.0", + "@typescript-eslint/scope-manager": "5.4.0", "debug": "^4.3.2", "functional-red-black-tree": "^1.0.1", "ignore": "^5.1.8", @@ -1058,15 +1058,15 @@ } }, "node_modules/@typescript-eslint/experimental-utils": { - "version": "5.3.1", - "resolved": "https://registry.npmjs.org/@typescript-eslint/experimental-utils/-/experimental-utils-5.3.1.tgz", - "integrity": "sha512-RgFn5asjZ5daUhbK5Sp0peq0SSMytqcrkNfU4pnDma2D8P3ElZ6JbYjY8IMSFfZAJ0f3x3tnO3vXHweYg0g59w==", + "version": "5.4.0", + "resolved": "https://registry.npmjs.org/@typescript-eslint/experimental-utils/-/experimental-utils-5.4.0.tgz", + "integrity": "sha512-Nz2JDIQUdmIGd6p33A+naQmwfkU5KVTLb/5lTk+tLVTDacZKoGQisj8UCxk7onJcrgjIvr8xWqkYI+DbI3TfXg==", "dev": true, "dependencies": { "@types/json-schema": "^7.0.9", - "@typescript-eslint/scope-manager": "5.3.1", - "@typescript-eslint/types": "5.3.1", - "@typescript-eslint/typescript-estree": "5.3.1", + "@typescript-eslint/scope-manager": "5.4.0", + "@typescript-eslint/types": "5.4.0", + "@typescript-eslint/typescript-estree": "5.4.0", "eslint-scope": "^5.1.1", "eslint-utils": "^3.0.0" }, @@ -1082,14 +1082,14 @@ } }, "node_modules/@typescript-eslint/parser": { - "version": "5.3.1", - "resolved": "https://registry.npmjs.org/@typescript-eslint/parser/-/parser-5.3.1.tgz", - "integrity": "sha512-TD+ONlx5c+Qhk21x9gsJAMRohWAUMavSOmJgv3JGy9dgPhuBd5Wok0lmMClZDyJNLLZK1JRKiATzCKZNUmoyfw==", + "version": "5.4.0", + "resolved": "https://registry.npmjs.org/@typescript-eslint/parser/-/parser-5.4.0.tgz", + "integrity": "sha512-JoB41EmxiYpaEsRwpZEYAJ9XQURPFer8hpkIW9GiaspVLX8oqbqNM8P4EP8HOZg96yaALiLEVWllA2E8vwsIKw==", "dev": true, "dependencies": { - "@typescript-eslint/scope-manager": "5.3.1", - "@typescript-eslint/types": "5.3.1", - "@typescript-eslint/typescript-estree": "5.3.1", + "@typescript-eslint/scope-manager": "5.4.0", + "@typescript-eslint/types": "5.4.0", + "@typescript-eslint/typescript-estree": "5.4.0", "debug": "^4.3.2" }, "engines": { @@ -1109,13 +1109,13 @@ } }, "node_modules/@typescript-eslint/scope-manager": { - "version": "5.3.1", - "resolved": "https://registry.npmjs.org/@typescript-eslint/scope-manager/-/scope-manager-5.3.1.tgz", - "integrity": "sha512-XksFVBgAq0Y9H40BDbuPOTUIp7dn4u8oOuhcgGq7EoDP50eqcafkMVGrypyVGvDYHzjhdUCUwuwVUK4JhkMAMg==", + "version": "5.4.0", + "resolved": "https://registry.npmjs.org/@typescript-eslint/scope-manager/-/scope-manager-5.4.0.tgz", + "integrity": "sha512-pRxFjYwoi8R+n+sibjgF9iUiAELU9ihPBtHzocyW8v8D8G8KeQvXTsW7+CBYIyTYsmhtNk50QPGLE3vrvhM5KA==", "dev": true, "dependencies": { - "@typescript-eslint/types": "5.3.1", - "@typescript-eslint/visitor-keys": "5.3.1" + "@typescript-eslint/types": "5.4.0", + "@typescript-eslint/visitor-keys": "5.4.0" }, "engines": { "node": "^12.22.0 || ^14.17.0 || >=16.0.0" @@ -1126,9 +1126,9 @@ } }, "node_modules/@typescript-eslint/types": { - "version": "5.3.1", - "resolved": "https://registry.npmjs.org/@typescript-eslint/types/-/types-5.3.1.tgz", - "integrity": "sha512-bG7HeBLolxKHtdHG54Uac750eXuQQPpdJfCYuw4ZI3bZ7+GgKClMWM8jExBtp7NSP4m8PmLRM8+lhzkYnSmSxQ==", + "version": "5.4.0", + "resolved": "https://registry.npmjs.org/@typescript-eslint/types/-/types-5.4.0.tgz", + "integrity": "sha512-GjXNpmn+n1LvnttarX+sPD6+S7giO+9LxDIGlRl4wK3a7qMWALOHYuVSZpPTfEIklYjaWuMtfKdeByx0AcaThA==", "dev": true, "engines": { "node": "^12.22.0 || ^14.17.0 || >=16.0.0" @@ -1139,13 +1139,13 @@ } }, "node_modules/@typescript-eslint/typescript-estree": { - "version": "5.3.1", - "resolved": "https://registry.npmjs.org/@typescript-eslint/typescript-estree/-/typescript-estree-5.3.1.tgz", - "integrity": "sha512-PwFbh/PKDVo/Wct6N3w+E4rLZxUDgsoII/GrWM2A62ETOzJd4M6s0Mu7w4CWsZraTbaC5UQI+dLeyOIFF1PquQ==", + "version": "5.4.0", + "resolved": "https://registry.npmjs.org/@typescript-eslint/typescript-estree/-/typescript-estree-5.4.0.tgz", + "integrity": "sha512-nhlNoBdhKuwiLMx6GrybPT3SFILm5Gij2YBdPEPFlYNFAXUJWX6QRgvi/lwVoadaQEFsizohs6aFRMqsXI2ewA==", "dev": true, "dependencies": { - "@typescript-eslint/types": "5.3.1", - "@typescript-eslint/visitor-keys": "5.3.1", + "@typescript-eslint/types": "5.4.0", + "@typescript-eslint/visitor-keys": "5.4.0", "debug": "^4.3.2", "globby": "^11.0.4", "is-glob": "^4.0.3", @@ -1166,12 +1166,12 @@ } }, "node_modules/@typescript-eslint/visitor-keys": { - "version": "5.3.1", - "resolved": "https://registry.npmjs.org/@typescript-eslint/visitor-keys/-/visitor-keys-5.3.1.tgz", - "integrity": "sha512-3cHUzUuVTuNHx0Gjjt5pEHa87+lzyqOiHXy/Gz+SJOCW1mpw9xQHIIEwnKn+Thph1mgWyZ90nboOcSuZr/jTTQ==", + "version": "5.4.0", + "resolved": "https://registry.npmjs.org/@typescript-eslint/visitor-keys/-/visitor-keys-5.4.0.tgz", + "integrity": "sha512-PVbax7MeE7tdLfW5SA0fs8NGVVr+buMPrcj+CWYWPXsZCH8qZ1THufDzbXm1xrZ2b2PA1iENJ0sRq5fuUtvsJg==", "dev": true, "dependencies": { - "@typescript-eslint/types": "5.3.1", + "@typescript-eslint/types": "5.4.0", "eslint-visitor-keys": "^3.0.0" }, "engines": { @@ -1486,13 +1486,13 @@ "dev": true }, "node_modules/browserslist": { - "version": "4.17.6", - "resolved": "https://registry.npmjs.org/browserslist/-/browserslist-4.17.6.tgz", - "integrity": "sha512-uPgz3vyRTlEiCv4ee9KlsKgo2V6qPk7Jsn0KAn2OBqbqKo3iNcPEC1Ti6J4dwnz+aIRfEEEuOzC9IBk8tXUomw==", + "version": "4.18.1", + "resolved": "https://registry.npmjs.org/browserslist/-/browserslist-4.18.1.tgz", + "integrity": "sha512-8ScCzdpPwR2wQh8IT82CA2VgDwjHyqMovPBZSNH54+tm4Jk2pCuv90gmAdH6J84OCRWi0b4gMe6O6XPXuJnjgQ==", "dev": true, "dependencies": { - "caniuse-lite": "^1.0.30001274", - "electron-to-chromium": "^1.3.886", + "caniuse-lite": "^1.0.30001280", + "electron-to-chromium": "^1.3.896", "escalade": "^3.1.1", "node-releases": "^2.0.1", "picocolors": "^1.0.0" @@ -1618,9 +1618,9 @@ } }, "node_modules/camelcase": { - "version": "6.2.0", - "resolved": "https://registry.npmjs.org/camelcase/-/camelcase-6.2.0.tgz", - "integrity": "sha512-c7wVvbw3f37nuobQNtgsgG9POC9qMbNuMQmTCqZv23b6MIz0fcYpBiOlv9gEN/hdLdnZTDQhg6e9Dq5M1vKvfg==", + "version": "6.2.1", + "resolved": "https://registry.npmjs.org/camelcase/-/camelcase-6.2.1.tgz", + "integrity": "sha512-tVI4q5jjFV5CavAU8DXfza/TJcZutVKo/5Foskmsqcm0MsL91moHvwiGNnqaa2o6PF/7yT5ikDRcVcl8Rj6LCA==", "dev": true, "engines": { "node": ">=10" @@ -1630,9 +1630,9 @@ } }, "node_modules/caniuse-lite": { - "version": "1.0.30001279", - "resolved": "https://registry.npmjs.org/caniuse-lite/-/caniuse-lite-1.0.30001279.tgz", - "integrity": "sha512-VfEHpzHEXj6/CxggTwSFoZBBYGQfQv9Cf42KPlO79sWXCD1QNKWKsKzFeWL7QpZHJQYAvocqV6Rty1yJMkqWLQ==", + "version": "1.0.30001280", + "resolved": "https://registry.npmjs.org/caniuse-lite/-/caniuse-lite-1.0.30001280.tgz", + "integrity": "sha512-kFXwYvHe5rix25uwueBxC569o53J6TpnGu0BEEn+6Lhl2vsnAumRFWEBhDft1fwyo6m1r4i+RqA4+163FpeFcA==", "dev": true, "funding": { "type": "opencollective", @@ -2085,9 +2085,9 @@ "dev": true }, "node_modules/electron-to-chromium": { - "version": "1.3.893", - "resolved": "https://registry.npmjs.org/electron-to-chromium/-/electron-to-chromium-1.3.893.tgz", - "integrity": "sha512-ChtwF7qB03INq1SyMpue08wc6cve+ktj2UC/Y7se9vB+JryfzziJeYwsgb8jLaCA5GMkHCdn5M62PfSMWhifZg==", + "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==", "dev": true }, "node_modules/emoji-regex": { @@ -5227,9 +5227,9 @@ } }, "node_modules/release-it": { - "version": "14.11.6", - "resolved": "https://registry.npmjs.org/release-it/-/release-it-14.11.6.tgz", - "integrity": "sha512-6BNcuzFZHThBUBJ/xYw/bxZ+58CAwrwf1zgmjq2Ibl3nlDZbjphHG6iqxkJu7mZ8TIWs6NjloEAhqpjeXoN//Q==", + "version": "14.11.7", + "resolved": "https://registry.npmjs.org/release-it/-/release-it-14.11.7.tgz", + "integrity": "sha512-m4p9+x6AEQPczc96Jyg6dGFeovpJVgRCtA1lxeIgTmQVt9dutYPkkjZeJngZgUJ17/Lb1bx6ZzW2qsKmopKnbQ==", "dev": true, "dependencies": { "@iarna/toml": "2.2.5", @@ -5940,9 +5940,9 @@ } }, "node_modules/typedoc": { - "version": "0.22.8", - "resolved": "https://registry.npmjs.org/typedoc/-/typedoc-0.22.8.tgz", - "integrity": "sha512-92S+YzyhospdXN5rnkYUTgirdTYqNWY7NP9vco+IqQQoiSXzVSUsawVro+tMyEEsWUS7EMaJ2YOjB9uE0CBi6A==", + "version": "0.22.9", + "resolved": "https://registry.npmjs.org/typedoc/-/typedoc-0.22.9.tgz", + "integrity": "sha512-84PjudoXVcap6bwdZFbYIUWlgdz/iLV09ZHwrCzhtHWXaDQG6mlosJ8te6DSThuRkRvQjp46HO+qY/P7Gpm78g==", "dev": true, "dependencies": { "glob": "^7.2.0", @@ -5972,9 +5972,9 @@ } }, "node_modules/typedoc-plugin-markdown": { - "version": "3.11.3", - "resolved": "https://registry.npmjs.org/typedoc-plugin-markdown/-/typedoc-plugin-markdown-3.11.3.tgz", - "integrity": "sha512-rWiHbEIe0oZetDIsBR24XJVxGOJ91kDcHoj2KhFKxCLoJGX659EKBQkHne9QJ4W2stGhu1fRgFyQaouSBnxukA==", + "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==", "dev": true, "dependencies": { "handlebars": "^4.7.7" @@ -6411,7 +6411,6 @@ } }, "packages/client": { - "name": "@redis/client", "version": "1.0.0-rc", "license": "MIT", "dependencies": { @@ -6427,17 +6426,17 @@ "@types/redis-parser": "^3.0.0", "@types/sinon": "^10.0.6", "@types/yallist": "^4.0.1", - "@typescript-eslint/eslint-plugin": "^5.3.1", - "@typescript-eslint/parser": "^5.3.1", + "@typescript-eslint/eslint-plugin": "^5.4.0", + "@typescript-eslint/parser": "^5.4.0", "eslint": "^8.2.0", "nyc": "^15.1.0", - "release-it": "^14.11.6", + "release-it": "^14.11.7", "sinon": "^12.0.1", "source-map-support": "^0.5.20", "ts-node": "^10.4.0", - "typedoc": "^0.22.8", + "typedoc": "^0.22.9", "typedoc-github-wiki-theme": "^0.6.0", - "typedoc-plugin-markdown": "^3.11.3", + "typedoc-plugin-markdown": "^3.11.6", "typescript": "^4.4.4" }, "engines": { @@ -6445,15 +6444,14 @@ } }, "packages/json": { - "name": "@redis/json", "version": "1.0.0-rc.0", "license": "MIT", "devDependencies": { "@istanbuljs/nyc-config-typescript": "^1.0.1", "@redis/test-utils": "*", - "@types/node": "^16.11.6", + "@types/node": "^16.11.7", "nyc": "^15.1.0", - "release-it": "^14.11.6", + "release-it": "^14.11.7", "source-map-support": "^0.5.20", "ts-node": "^10.4.0", "typescript": "^4.4.4" @@ -6463,15 +6461,14 @@ } }, "packages/search": { - "name": "@redis/search", "version": "1.0.0-rc.0", "license": "MIT", "devDependencies": { "@istanbuljs/nyc-config-typescript": "^1.0.1", "@redis/test-utils": "*", - "@types/node": "^16.11.6", + "@types/node": "^16.11.7", "nyc": "^15.1.0", - "release-it": "^14.11.6", + "release-it": "^14.11.7", "source-map-support": "^0.5.20", "ts-node": "^10.4.0", "typescript": "^4.4.4" @@ -6481,16 +6478,14 @@ } }, "packages/test-utils": { - "name": "@redis/test-utils", "devDependencies": { "@istanbuljs/nyc-config-typescript": "^1.0.1", - "@tsconfig/node12": "^1.0.9", "@types/mocha": "^9.0.0", - "@types/node": "^16.11.6", + "@types/node": "^16.11.7", "@types/yargs": "^17.0.5", "mocha": "^9.1.3", "nyc": "^15.1.0", - "release-it": "^14.11.6", + "release-it": "^14.11.7", "source-map-support": "^0.5.20", "ts-node": "^10.4.0", "typescript": "^4.4.4", @@ -6499,21 +6494,6 @@ "peerDependencies": { "@redis/client": "^1.0.0-rc" } - }, - "packages/time-series": { - "version": "1.0.0", - "extraneous": true, - "license": "ISC", - "devDependencies": { - "@istanbuljs/nyc-config-typescript": "^1.0.1", - "@redis/test-utils": "*", - "@types/node": "^16.11.6", - "nyc": "^15.1.0", - "release-it": "^14.11.6", - "source-map-support": "^0.5.20", - "ts-node": "^10.4.0", - "typescript": "^4.4.4" - } } }, "dependencies": { @@ -7157,23 +7137,23 @@ "@istanbuljs/nyc-config-typescript": "^1.0.1", "@redis/test-utils": "*", "@types/node": "^16.11.7", - "@types/redis-parser": "*", + "@types/redis-parser": "^3.0.0", "@types/sinon": "^10.0.6", "@types/yallist": "^4.0.1", - "@typescript-eslint/eslint-plugin": "^5.3.1", - "@typescript-eslint/parser": "^5.3.1", + "@typescript-eslint/eslint-plugin": "^5.4.0", + "@typescript-eslint/parser": "^5.4.0", "cluster-key-slot": "1.1.0", "eslint": "^8.2.0", "generic-pool": "3.8.2", "nyc": "^15.1.0", "redis-parser": "3.0.0", - "release-it": "^14.11.6", + "release-it": "^14.11.7", "sinon": "^12.0.1", "source-map-support": "^0.5.20", "ts-node": "^10.4.0", - "typedoc": "^0.22.8", + "typedoc": "^0.22.9", "typedoc-github-wiki-theme": "^0.6.0", - "typedoc-plugin-markdown": "^3.11.3", + "typedoc-plugin-markdown": "^3.11.6", "typescript": "^4.4.4", "yallist": "4.0.0" } @@ -7183,9 +7163,9 @@ "requires": { "@istanbuljs/nyc-config-typescript": "^1.0.1", "@redis/test-utils": "*", - "@types/node": "^16.11.6", + "@types/node": "^16.11.7", "nyc": "^15.1.0", - "release-it": "^14.11.6", + "release-it": "^14.11.7", "source-map-support": "^0.5.20", "ts-node": "^10.4.0", "typescript": "^4.4.4" @@ -7196,9 +7176,9 @@ "requires": { "@istanbuljs/nyc-config-typescript": "^1.0.1", "@redis/test-utils": "*", - "@types/node": "^16.11.6", + "@types/node": "^16.11.7", "nyc": "^15.1.0", - "release-it": "^14.11.6", + "release-it": "^14.11.7", "source-map-support": "^0.5.20", "ts-node": "^10.4.0", "typescript": "^4.4.4" @@ -7208,13 +7188,12 @@ "version": "file:packages/test-utils", "requires": { "@istanbuljs/nyc-config-typescript": "^1.0.1", - "@tsconfig/node12": "^1.0.9", "@types/mocha": "^9.0.0", - "@types/node": "^16.11.6", + "@types/node": "^16.11.7", "@types/yargs": "^17.0.5", "mocha": "^9.1.3", "nyc": "^15.1.0", - "release-it": "^14.11.6", + "release-it": "^14.11.7", "source-map-support": "^0.5.20", "ts-node": "^10.4.0", "typescript": "^4.4.4", @@ -7402,13 +7381,13 @@ "dev": true }, "@typescript-eslint/eslint-plugin": { - "version": "5.3.1", - "resolved": "https://registry.npmjs.org/@typescript-eslint/eslint-plugin/-/eslint-plugin-5.3.1.tgz", - "integrity": "sha512-cFImaoIr5Ojj358xI/SDhjog57OK2NqlpxwdcgyxDA3bJlZcJq5CPzUXtpD7CxI2Hm6ATU7w5fQnnkVnmwpHqw==", + "version": "5.4.0", + "resolved": "https://registry.npmjs.org/@typescript-eslint/eslint-plugin/-/eslint-plugin-5.4.0.tgz", + "integrity": "sha512-9/yPSBlwzsetCsGEn9j24D8vGQgJkOTr4oMLas/w886ZtzKIs1iyoqFrwsX2fqYEeUwsdBpC21gcjRGo57u0eg==", "dev": true, "requires": { - "@typescript-eslint/experimental-utils": "5.3.1", - "@typescript-eslint/scope-manager": "5.3.1", + "@typescript-eslint/experimental-utils": "5.4.0", + "@typescript-eslint/scope-manager": "5.4.0", "debug": "^4.3.2", "functional-red-black-tree": "^1.0.1", "ignore": "^5.1.8", @@ -7418,55 +7397,55 @@ } }, "@typescript-eslint/experimental-utils": { - "version": "5.3.1", - "resolved": "https://registry.npmjs.org/@typescript-eslint/experimental-utils/-/experimental-utils-5.3.1.tgz", - "integrity": "sha512-RgFn5asjZ5daUhbK5Sp0peq0SSMytqcrkNfU4pnDma2D8P3ElZ6JbYjY8IMSFfZAJ0f3x3tnO3vXHweYg0g59w==", + "version": "5.4.0", + "resolved": "https://registry.npmjs.org/@typescript-eslint/experimental-utils/-/experimental-utils-5.4.0.tgz", + "integrity": "sha512-Nz2JDIQUdmIGd6p33A+naQmwfkU5KVTLb/5lTk+tLVTDacZKoGQisj8UCxk7onJcrgjIvr8xWqkYI+DbI3TfXg==", "dev": true, "requires": { "@types/json-schema": "^7.0.9", - "@typescript-eslint/scope-manager": "5.3.1", - "@typescript-eslint/types": "5.3.1", - "@typescript-eslint/typescript-estree": "5.3.1", + "@typescript-eslint/scope-manager": "5.4.0", + "@typescript-eslint/types": "5.4.0", + "@typescript-eslint/typescript-estree": "5.4.0", "eslint-scope": "^5.1.1", "eslint-utils": "^3.0.0" } }, "@typescript-eslint/parser": { - "version": "5.3.1", - "resolved": "https://registry.npmjs.org/@typescript-eslint/parser/-/parser-5.3.1.tgz", - "integrity": "sha512-TD+ONlx5c+Qhk21x9gsJAMRohWAUMavSOmJgv3JGy9dgPhuBd5Wok0lmMClZDyJNLLZK1JRKiATzCKZNUmoyfw==", + "version": "5.4.0", + "resolved": "https://registry.npmjs.org/@typescript-eslint/parser/-/parser-5.4.0.tgz", + "integrity": "sha512-JoB41EmxiYpaEsRwpZEYAJ9XQURPFer8hpkIW9GiaspVLX8oqbqNM8P4EP8HOZg96yaALiLEVWllA2E8vwsIKw==", "dev": true, "requires": { - "@typescript-eslint/scope-manager": "5.3.1", - "@typescript-eslint/types": "5.3.1", - "@typescript-eslint/typescript-estree": "5.3.1", + "@typescript-eslint/scope-manager": "5.4.0", + "@typescript-eslint/types": "5.4.0", + "@typescript-eslint/typescript-estree": "5.4.0", "debug": "^4.3.2" } }, "@typescript-eslint/scope-manager": { - "version": "5.3.1", - "resolved": "https://registry.npmjs.org/@typescript-eslint/scope-manager/-/scope-manager-5.3.1.tgz", - "integrity": "sha512-XksFVBgAq0Y9H40BDbuPOTUIp7dn4u8oOuhcgGq7EoDP50eqcafkMVGrypyVGvDYHzjhdUCUwuwVUK4JhkMAMg==", + "version": "5.4.0", + "resolved": "https://registry.npmjs.org/@typescript-eslint/scope-manager/-/scope-manager-5.4.0.tgz", + "integrity": "sha512-pRxFjYwoi8R+n+sibjgF9iUiAELU9ihPBtHzocyW8v8D8G8KeQvXTsW7+CBYIyTYsmhtNk50QPGLE3vrvhM5KA==", "dev": true, "requires": { - "@typescript-eslint/types": "5.3.1", - "@typescript-eslint/visitor-keys": "5.3.1" + "@typescript-eslint/types": "5.4.0", + "@typescript-eslint/visitor-keys": "5.4.0" } }, "@typescript-eslint/types": { - "version": "5.3.1", - "resolved": "https://registry.npmjs.org/@typescript-eslint/types/-/types-5.3.1.tgz", - "integrity": "sha512-bG7HeBLolxKHtdHG54Uac750eXuQQPpdJfCYuw4ZI3bZ7+GgKClMWM8jExBtp7NSP4m8PmLRM8+lhzkYnSmSxQ==", + "version": "5.4.0", + "resolved": "https://registry.npmjs.org/@typescript-eslint/types/-/types-5.4.0.tgz", + "integrity": "sha512-GjXNpmn+n1LvnttarX+sPD6+S7giO+9LxDIGlRl4wK3a7qMWALOHYuVSZpPTfEIklYjaWuMtfKdeByx0AcaThA==", "dev": true }, "@typescript-eslint/typescript-estree": { - "version": "5.3.1", - "resolved": "https://registry.npmjs.org/@typescript-eslint/typescript-estree/-/typescript-estree-5.3.1.tgz", - "integrity": "sha512-PwFbh/PKDVo/Wct6N3w+E4rLZxUDgsoII/GrWM2A62ETOzJd4M6s0Mu7w4CWsZraTbaC5UQI+dLeyOIFF1PquQ==", + "version": "5.4.0", + "resolved": "https://registry.npmjs.org/@typescript-eslint/typescript-estree/-/typescript-estree-5.4.0.tgz", + "integrity": "sha512-nhlNoBdhKuwiLMx6GrybPT3SFILm5Gij2YBdPEPFlYNFAXUJWX6QRgvi/lwVoadaQEFsizohs6aFRMqsXI2ewA==", "dev": true, "requires": { - "@typescript-eslint/types": "5.3.1", - "@typescript-eslint/visitor-keys": "5.3.1", + "@typescript-eslint/types": "5.4.0", + "@typescript-eslint/visitor-keys": "5.4.0", "debug": "^4.3.2", "globby": "^11.0.4", "is-glob": "^4.0.3", @@ -7475,12 +7454,12 @@ } }, "@typescript-eslint/visitor-keys": { - "version": "5.3.1", - "resolved": "https://registry.npmjs.org/@typescript-eslint/visitor-keys/-/visitor-keys-5.3.1.tgz", - "integrity": "sha512-3cHUzUuVTuNHx0Gjjt5pEHa87+lzyqOiHXy/Gz+SJOCW1mpw9xQHIIEwnKn+Thph1mgWyZ90nboOcSuZr/jTTQ==", + "version": "5.4.0", + "resolved": "https://registry.npmjs.org/@typescript-eslint/visitor-keys/-/visitor-keys-5.4.0.tgz", + "integrity": "sha512-PVbax7MeE7tdLfW5SA0fs8NGVVr+buMPrcj+CWYWPXsZCH8qZ1THufDzbXm1xrZ2b2PA1iENJ0sRq5fuUtvsJg==", "dev": true, "requires": { - "@typescript-eslint/types": "5.3.1", + "@typescript-eslint/types": "5.4.0", "eslint-visitor-keys": "^3.0.0" } }, @@ -7713,13 +7692,13 @@ "dev": true }, "browserslist": { - "version": "4.17.6", - "resolved": "https://registry.npmjs.org/browserslist/-/browserslist-4.17.6.tgz", - "integrity": "sha512-uPgz3vyRTlEiCv4ee9KlsKgo2V6qPk7Jsn0KAn2OBqbqKo3iNcPEC1Ti6J4dwnz+aIRfEEEuOzC9IBk8tXUomw==", + "version": "4.18.1", + "resolved": "https://registry.npmjs.org/browserslist/-/browserslist-4.18.1.tgz", + "integrity": "sha512-8ScCzdpPwR2wQh8IT82CA2VgDwjHyqMovPBZSNH54+tm4Jk2pCuv90gmAdH6J84OCRWi0b4gMe6O6XPXuJnjgQ==", "dev": true, "requires": { - "caniuse-lite": "^1.0.30001274", - "electron-to-chromium": "^1.3.886", + "caniuse-lite": "^1.0.30001280", + "electron-to-chromium": "^1.3.896", "escalade": "^3.1.1", "node-releases": "^2.0.1", "picocolors": "^1.0.0" @@ -7802,15 +7781,15 @@ "dev": true }, "camelcase": { - "version": "6.2.0", - "resolved": "https://registry.npmjs.org/camelcase/-/camelcase-6.2.0.tgz", - "integrity": "sha512-c7wVvbw3f37nuobQNtgsgG9POC9qMbNuMQmTCqZv23b6MIz0fcYpBiOlv9gEN/hdLdnZTDQhg6e9Dq5M1vKvfg==", + "version": "6.2.1", + "resolved": "https://registry.npmjs.org/camelcase/-/camelcase-6.2.1.tgz", + "integrity": "sha512-tVI4q5jjFV5CavAU8DXfza/TJcZutVKo/5Foskmsqcm0MsL91moHvwiGNnqaa2o6PF/7yT5ikDRcVcl8Rj6LCA==", "dev": true }, "caniuse-lite": { - "version": "1.0.30001279", - "resolved": "https://registry.npmjs.org/caniuse-lite/-/caniuse-lite-1.0.30001279.tgz", - "integrity": "sha512-VfEHpzHEXj6/CxggTwSFoZBBYGQfQv9Cf42KPlO79sWXCD1QNKWKsKzFeWL7QpZHJQYAvocqV6Rty1yJMkqWLQ==", + "version": "1.0.30001280", + "resolved": "https://registry.npmjs.org/caniuse-lite/-/caniuse-lite-1.0.30001280.tgz", + "integrity": "sha512-kFXwYvHe5rix25uwueBxC569o53J6TpnGu0BEEn+6Lhl2vsnAumRFWEBhDft1fwyo6m1r4i+RqA4+163FpeFcA==", "dev": true }, "chalk": { @@ -8154,9 +8133,9 @@ "dev": true }, "electron-to-chromium": { - "version": "1.3.893", - "resolved": "https://registry.npmjs.org/electron-to-chromium/-/electron-to-chromium-1.3.893.tgz", - "integrity": "sha512-ChtwF7qB03INq1SyMpue08wc6cve+ktj2UC/Y7se9vB+JryfzziJeYwsgb8jLaCA5GMkHCdn5M62PfSMWhifZg==", + "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==", "dev": true }, "emoji-regex": { @@ -10499,9 +10478,9 @@ } }, "release-it": { - "version": "14.11.6", - "resolved": "https://registry.npmjs.org/release-it/-/release-it-14.11.6.tgz", - "integrity": "sha512-6BNcuzFZHThBUBJ/xYw/bxZ+58CAwrwf1zgmjq2Ibl3nlDZbjphHG6iqxkJu7mZ8TIWs6NjloEAhqpjeXoN//Q==", + "version": "14.11.7", + "resolved": "https://registry.npmjs.org/release-it/-/release-it-14.11.7.tgz", + "integrity": "sha512-m4p9+x6AEQPczc96Jyg6dGFeovpJVgRCtA1lxeIgTmQVt9dutYPkkjZeJngZgUJ17/Lb1bx6ZzW2qsKmopKnbQ==", "dev": true, "requires": { "@iarna/toml": "2.2.5", @@ -11033,9 +11012,9 @@ } }, "typedoc": { - "version": "0.22.8", - "resolved": "https://registry.npmjs.org/typedoc/-/typedoc-0.22.8.tgz", - "integrity": "sha512-92S+YzyhospdXN5rnkYUTgirdTYqNWY7NP9vco+IqQQoiSXzVSUsawVro+tMyEEsWUS7EMaJ2YOjB9uE0CBi6A==", + "version": "0.22.9", + "resolved": "https://registry.npmjs.org/typedoc/-/typedoc-0.22.9.tgz", + "integrity": "sha512-84PjudoXVcap6bwdZFbYIUWlgdz/iLV09ZHwrCzhtHWXaDQG6mlosJ8te6DSThuRkRvQjp46HO+qY/P7Gpm78g==", "dev": true, "requires": { "glob": "^7.2.0", @@ -11053,9 +11032,9 @@ "requires": {} }, "typedoc-plugin-markdown": { - "version": "3.11.3", - "resolved": "https://registry.npmjs.org/typedoc-plugin-markdown/-/typedoc-plugin-markdown-3.11.3.tgz", - "integrity": "sha512-rWiHbEIe0oZetDIsBR24XJVxGOJ91kDcHoj2KhFKxCLoJGX659EKBQkHne9QJ4W2stGhu1fRgFyQaouSBnxukA==", + "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==", "dev": true, "requires": { "handlebars": "^4.7.7" diff --git a/package.json b/package.json index 051abd3a75d..d576c3232b7 100644 --- a/package.json +++ b/package.json @@ -23,7 +23,7 @@ }, "devDependencies": { "@tsconfig/node12": "^1.0.9", - "release-it": "^14.11.6", + "release-it": "^14.11.7", "typescript": "^4.4.4" }, "repository": { diff --git a/packages/client/package.json b/packages/client/package.json index 1f8226cc987..3e874af1afc 100644 --- a/packages/client/package.json +++ b/packages/client/package.json @@ -23,17 +23,17 @@ "@types/redis-parser": "^3.0.0", "@types/sinon": "^10.0.6", "@types/yallist": "^4.0.1", - "@typescript-eslint/eslint-plugin": "^5.3.1", - "@typescript-eslint/parser": "^5.3.1", + "@typescript-eslint/eslint-plugin": "^5.4.0", + "@typescript-eslint/parser": "^5.4.0", "eslint": "^8.2.0", "nyc": "^15.1.0", - "release-it": "^14.11.6", + "release-it": "^14.11.7", "sinon": "^12.0.1", "source-map-support": "^0.5.20", "ts-node": "^10.4.0", - "typedoc": "^0.22.8", + "typedoc": "^0.22.9", "typedoc-github-wiki-theme": "^0.6.0", - "typedoc-plugin-markdown": "^3.11.3", + "typedoc-plugin-markdown": "^3.11.6", "typescript": "^4.4.4" }, "engines": { diff --git a/packages/json/package.json b/packages/json/package.json index 1dcf907ea0e..41f1ec3064d 100644 --- a/packages/json/package.json +++ b/packages/json/package.json @@ -14,9 +14,9 @@ "devDependencies": { "@istanbuljs/nyc-config-typescript": "^1.0.1", "@redis/test-utils": "*", - "@types/node": "^16.11.6", + "@types/node": "^16.11.7", "nyc": "^15.1.0", - "release-it": "^14.11.6", + "release-it": "^14.11.7", "source-map-support": "^0.5.20", "ts-node": "^10.4.0", "typescript": "^4.4.4" diff --git a/packages/search/package.json b/packages/search/package.json index 5f8e9e550db..33bf7fffe4a 100644 --- a/packages/search/package.json +++ b/packages/search/package.json @@ -14,9 +14,9 @@ "devDependencies": { "@istanbuljs/nyc-config-typescript": "^1.0.1", "@redis/test-utils": "*", - "@types/node": "^16.11.6", + "@types/node": "^16.11.7", "nyc": "^15.1.0", - "release-it": "^14.11.6", + "release-it": "^14.11.7", "source-map-support": "^0.5.20", "ts-node": "^10.4.0", "typescript": "^4.4.4" diff --git a/packages/test-utils/package.json b/packages/test-utils/package.json index fba6744136a..a1b7c4d4166 100644 --- a/packages/test-utils/package.json +++ b/packages/test-utils/package.json @@ -12,13 +12,12 @@ }, "devDependencies": { "@istanbuljs/nyc-config-typescript": "^1.0.1", - "@tsconfig/node12": "^1.0.9", "@types/mocha": "^9.0.0", - "@types/node": "^16.11.6", + "@types/node": "^16.11.7", "@types/yargs": "^17.0.5", "mocha": "^9.1.3", "nyc": "^15.1.0", - "release-it": "^14.11.6", + "release-it": "^14.11.7", "source-map-support": "^0.5.20", "ts-node": "^10.4.0", "typescript": "^4.4.4", From 397f14cee1894b1a01b5ed609d244669553b848f Mon Sep 17 00:00:00 2001 From: leibale Date: Mon, 15 Nov 2021 17:25:23 -0500 Subject: [PATCH 0954/1748] add tests and licence badges --- .github/README.md | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/.github/README.md b/.github/README.md index 171850fd2dd..f14b71d9f27 100644 --- a/.github/README.md +++ b/.github/README.md @@ -1,7 +1,9 @@ # Node-Redis +[![Tests](https://img.shields.io/github/workflow/status/redis/node-redis/Tests/master.svg?label=tests)](https://codecov.io/gh/redis/node-redis) [![Coverage](https://codecov.io/gh/redis/node-redis/branch/master/graph/badge.svg?token=xcfqHhJC37)](https://codecov.io/gh/redis/node-redis) -[![Chat](https://img.shields.io/discord/697882427875393627)](https://discord.gg/XMMVgxUm) +[![License](https://img.shields.io/github/license/redis/node-redis.svg)](https://codecov.io/gh/redis/node-redis) +[![Chat](https://img.shields.io/discord/697882427875393627.svg)](https://discord.gg/XMMVgxUm) ## Installation From d79a815b294d795c69c1ea0fe4051664f684a07c Mon Sep 17 00:00:00 2001 From: leibale Date: Mon, 15 Nov 2021 17:47:06 -0500 Subject: [PATCH 0955/1748] update changelog.md --- packages/client/CHANGELOG.md | 33 ++++++++++++++++++++++++++++----- 1 file changed, 28 insertions(+), 5 deletions(-) diff --git a/packages/client/CHANGELOG.md b/packages/client/CHANGELOG.md index d0095714010..21b7177e8b6 100644 --- a/packages/client/CHANGELOG.md +++ b/packages/client/CHANGELOG.md @@ -2,7 +2,7 @@ ## v4.0.0 -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. +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. ### Breaking Changes @@ -17,12 +17,35 @@ 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](./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 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 the ability to extend Node Redis with Redis Module commands +## v3.1.2 + +### Fixes + +- Exclude unnecessary files from tarball + +## v3.1.1 + +### Enhancements + +- Upgrade node and dependencies + +### Fixes + +- Fix a potential exponential regex in monitor mode + +## v3.1.0 - 31 Mar, 2021 + +### Enhancements + +- Upgrade node and dependencies and redis-commands to support Redis 6 +- Add support for Redis 6 `auth pass [user]` + ## v3.0.0 - 09 Feb, 2020 This version is mainly a release to distribute all the unreleased changes on master since 2017 and additionally removes From 95400322832d1b42e72c125d275d580e4d47e3ff Mon Sep 17 00:00:00 2001 From: leibale Date: Mon, 15 Nov 2021 17:56:27 -0500 Subject: [PATCH 0956/1748] update .npmignore and .release-it.json --- .npmignore | 3 +++ .release-it.json | 4 +++- packages/client/.npmignore | 2 ++ packages/client/.release-it.json | 4 +++- packages/json/.npmignore | 1 + packages/json/.release-it.json | 4 +++- packages/search/.npmignore | 1 + packages/search/.release-it.json | 4 +++- 8 files changed, 19 insertions(+), 4 deletions(-) diff --git a/.npmignore b/.npmignore index 0689fb63fe4..a36c5e83cf2 100644 --- a/.npmignore +++ b/.npmignore @@ -4,6 +4,9 @@ docs/ examples/ packages/ .deepsource.toml +.release-it.json +CONTRIBUTING.md +SECURITY.md index.ts tsconfig.base.json tsconfig.json diff --git a/.release-it.json b/.release-it.json index 984af58564a..7fd2ef2991e 100644 --- a/.release-it.json +++ b/.release-it.json @@ -1,5 +1,7 @@ { "git": { - "tagName": "redis@${version}" + "tagName": "redis@${version}", + "commitMessage": "Release ${tagName}", + "tagAnnotation": "Release ${tagName}" } } \ No newline at end of file diff --git a/packages/client/.npmignore b/packages/client/.npmignore index 9f9e21d98b8..d064e1d0db6 100644 --- a/packages/client/.npmignore +++ b/packages/client/.npmignore @@ -2,7 +2,9 @@ coverage/ documentation/ lib/ +.eslintrc.json .nycrc.json +.release-it.json dump.rdb index.ts tsconfig.json diff --git a/packages/client/.release-it.json b/packages/client/.release-it.json index d8e2b65186a..7e3b1cc5950 100644 --- a/packages/client/.release-it.json +++ b/packages/client/.release-it.json @@ -1,5 +1,7 @@ { "git": { - "tagName": "client@${version}" + "tagName": "client@${version}", + "commitMessage": "Release ${tagName}", + "tagAnnotation": "Release ${tagName}" } } \ No newline at end of file diff --git a/packages/json/.npmignore b/packages/json/.npmignore index 6b24efa5953..2b5908aef84 100644 --- a/packages/json/.npmignore +++ b/packages/json/.npmignore @@ -2,4 +2,5 @@ coverage/ lib/ .nycrc.json +.release-it tsconfig.json diff --git a/packages/json/.release-it.json b/packages/json/.release-it.json index 3486f4456ad..d31e16c3414 100644 --- a/packages/json/.release-it.json +++ b/packages/json/.release-it.json @@ -1,5 +1,7 @@ { "git": { - "tagName": "json@${version}" + "tagName": "json@${version}", + "commitMessage": "Release ${tagName}", + "tagAnnotation": "Release ${tagName}" } } \ No newline at end of file diff --git a/packages/search/.npmignore b/packages/search/.npmignore index 6b24efa5953..2b5908aef84 100644 --- a/packages/search/.npmignore +++ b/packages/search/.npmignore @@ -2,4 +2,5 @@ coverage/ lib/ .nycrc.json +.release-it tsconfig.json diff --git a/packages/search/.release-it.json b/packages/search/.release-it.json index c181fc970b1..f1c81dab939 100644 --- a/packages/search/.release-it.json +++ b/packages/search/.release-it.json @@ -1,5 +1,7 @@ { "git": { - "tagName": "search@${version}" + "tagName": "search@${version}", + "commitMessage": "Release ${tagName}", + "tagAnnotation": "Release ${tagName}" } } \ No newline at end of file From 4f1af43b5f504d0fb1f680de1172dee41d6fbfb1 Mon Sep 17 00:00:00 2001 From: leibale Date: Mon, 15 Nov 2021 18:01:03 -0500 Subject: [PATCH 0957/1748] update .release-it.json --- packages/client/.release-it.json | 3 +++ packages/json/.release-it.json | 3 +++ packages/search/.release-it.json | 3 +++ 3 files changed, 9 insertions(+) diff --git a/packages/client/.release-it.json b/packages/client/.release-it.json index 7e3b1cc5950..60235ee23ea 100644 --- a/packages/client/.release-it.json +++ b/packages/client/.release-it.json @@ -3,5 +3,8 @@ "tagName": "client@${version}", "commitMessage": "Release ${tagName}", "tagAnnotation": "Release ${tagName}" + }, + "npm": { + "publishArgs": ["--access", "public"] } } \ No newline at end of file diff --git a/packages/json/.release-it.json b/packages/json/.release-it.json index d31e16c3414..68337fbd5cd 100644 --- a/packages/json/.release-it.json +++ b/packages/json/.release-it.json @@ -3,5 +3,8 @@ "tagName": "json@${version}", "commitMessage": "Release ${tagName}", "tagAnnotation": "Release ${tagName}" + }, + "npm": { + "publishArgs": ["--access", "public"] } } \ No newline at end of file diff --git a/packages/search/.release-it.json b/packages/search/.release-it.json index f1c81dab939..9abec2264c4 100644 --- a/packages/search/.release-it.json +++ b/packages/search/.release-it.json @@ -3,5 +3,8 @@ "tagName": "search@${version}", "commitMessage": "Release ${tagName}", "tagAnnotation": "Release ${tagName}" + }, + "npm": { + "publishArgs": ["--access", "public"] } } \ No newline at end of file From d32f1edf8a8ede15ede5654168e345ebd95c3ac6 Mon Sep 17 00:00:00 2001 From: leibale Date: Mon, 15 Nov 2021 18:03:13 -0500 Subject: [PATCH 0958/1748] Release client@1.0.0-rc.0 --- packages/client/package.json | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/packages/client/package.json b/packages/client/package.json index 3e874af1afc..ea2ce116256 100644 --- a/packages/client/package.json +++ b/packages/client/package.json @@ -1,6 +1,6 @@ { "name": "@redis/client", - "version": "1.0.0-rc", + "version": "1.0.0-rc.0", "license": "MIT", "main": "./dist/index.js", "types": "./dist/index.d.ts", From 9e15472d90b1f778c435af1211cd448e9ca591fb Mon Sep 17 00:00:00 2001 From: leibale Date: Mon, 15 Nov 2021 18:07:19 -0500 Subject: [PATCH 0959/1748] revert d32f1edf8a8ede15ede5654168e345ebd95c3ac6 --- packages/client/package.json | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/packages/client/package.json b/packages/client/package.json index ea2ce116256..3e874af1afc 100644 --- a/packages/client/package.json +++ b/packages/client/package.json @@ -1,6 +1,6 @@ { "name": "@redis/client", - "version": "1.0.0-rc.0", + "version": "1.0.0-rc", "license": "MIT", "main": "./dist/index.js", "types": "./dist/index.d.ts", From 7fc1d2d7cf2a1bde225444b9cd94a7e1170d18f1 Mon Sep 17 00:00:00 2001 From: leibale Date: Mon, 15 Nov 2021 18:27:09 -0500 Subject: [PATCH 0960/1748] fix .npmignore --- packages/json/.npmignore | 2 +- packages/search/.npmignore | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/packages/json/.npmignore b/packages/json/.npmignore index 2b5908aef84..bbef2b404fb 100644 --- a/packages/json/.npmignore +++ b/packages/json/.npmignore @@ -2,5 +2,5 @@ coverage/ lib/ .nycrc.json -.release-it +.release-it.json tsconfig.json diff --git a/packages/search/.npmignore b/packages/search/.npmignore index 2b5908aef84..bbef2b404fb 100644 --- a/packages/search/.npmignore +++ b/packages/search/.npmignore @@ -2,5 +2,5 @@ coverage/ lib/ .nycrc.json -.release-it +.release-it.json tsconfig.json From 0cabe71cf846dead6abb56c33a23d13990ce8a08 Mon Sep 17 00:00:00 2001 From: leibale Date: Tue, 16 Nov 2021 02:35:24 -0500 Subject: [PATCH 0961/1748] replace @redis with @node-redis --- .github/README.md | 6 +- .release-it.json | 2 +- index.ts | 18 +- package-lock.json | 192 +++++++++++----------- package.json | 6 +- packages/client/.release-it.json | 2 +- packages/client/README.md | 2 +- packages/client/lib/test-utils.ts | 2 +- packages/client/package.json | 4 +- packages/json/.release-it.json | 2 +- packages/json/README.md | 2 +- packages/json/lib/commands/GET.ts | 2 +- packages/json/lib/test-utils.ts | 2 +- packages/json/package.json | 6 +- packages/search/.release-it.json | 2 +- packages/search/README.md | 2 +- packages/search/lib/commands/AGGREGATE.ts | 4 +- packages/search/lib/commands/CREATE.ts | 2 +- packages/search/lib/commands/DICTADD.ts | 4 +- packages/search/lib/commands/DICTDEL.ts | 4 +- packages/search/lib/commands/SEARCH.ts | 4 +- packages/search/lib/commands/SUGDEL.ts | 2 +- packages/search/lib/commands/SYNUPDATE.ts | 4 +- packages/search/lib/commands/index.ts | 2 +- packages/search/lib/test-utils.ts | 2 +- packages/search/package.json | 6 +- packages/test-utils/lib/dockers.ts | 6 +- packages/test-utils/lib/index.ts | 6 +- packages/test-utils/package.json | 4 +- 29 files changed, 153 insertions(+), 149 deletions(-) diff --git a/.github/README.md b/.github/README.md index f14b71d9f27..c7046805107 100644 --- a/.github/README.md +++ b/.github/README.md @@ -281,9 +281,9 @@ Node Redis is supported with the following versions of Redis: | Name | Description | |-------------------------------------|------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------| | [redis](../) | [![Downloads](https://img.shields.io/npm/dm/redis.svg)](https://www.npmjs.com/package/redis/v/next) [![Version](https://img.shields.io/npm/v/redis/next.svg)](https://www.npmjs.com/package/redis/v/next) | -| [@redis/client](../packages/client) | [![Downloads](https://img.shields.io/npm/dm/@redis/client.svg)](https://www.npmjs.com/package/@redis/client/v/next) [![Version](https://img.shields.io/npm/v/@redis/client/next.svg)](https://www.npmjs.com/package/@redis/client/v/next) | -| [@redis/json](../packages/json) | [![Downloads](https://img.shields.io/npm/dm/@redis/json.svg)](https://www.npmjs.com/package/@redis/json/v/next) [![Version](https://img.shields.io/npm/v/@redis/json/next.svg)](https://www.npmjs.com/package/@redis/json/v/next) [Redis JSON](https://oss.redis.com/redisjson/) commands | -| [@redis/search](../packages/search) | [![Downloads](https://img.shields.io/npm/dm/@redis/search.svg)](https://www.npmjs.com/package/@redis/search/v/next) [![Version](https://img.shields.io/npm/v/@redis/search/next.svg)](https://www.npmjs.com/package/@redis/search/v/next) [Redis Search](https://oss.redis.com/redisearch/) commands | +| [@node-redis/client](../packages/client) | [![Downloads](https://img.shields.io/npm/dm/@node-redis/client.svg)](https://www.npmjs.com/package/@node-redis/client/v/next) [![Version](https://img.shields.io/npm/v/@node-redis/client/next.svg)](https://www.npmjs.com/package/@node-redis/client/v/next) | +| [@node-redis/json](../packages/json) | [![Downloads](https://img.shields.io/npm/dm/@node-redis/json.svg)](https://www.npmjs.com/package/@node-redis/json/v/next) [![Version](https://img.shields.io/npm/v/@node-redis/json/next.svg)](https://www.npmjs.com/package/@node-redis/json/v/next) [Redis JSON](https://oss.redis.com/redisjson/) commands | +| [@node-redis/search](../packages/search) | [![Downloads](https://img.shields.io/npm/dm/@node-redis/search.svg)](https://www.npmjs.com/package/@node-redis/search/v/next) [![Version](https://img.shields.io/npm/v/@node-redis/search/next.svg)](https://www.npmjs.com/package/@node-redis/search/v/next) [Redis Search](https://oss.redis.com/redisearch/) commands | ## Contributing diff --git a/.release-it.json b/.release-it.json index 7fd2ef2991e..982b4ac6cbe 100644 --- a/.release-it.json +++ b/.release-it.json @@ -4,4 +4,4 @@ "commitMessage": "Release ${tagName}", "tagAnnotation": "Release ${tagName}" } -} \ No newline at end of file +} diff --git a/index.ts b/index.ts index 610da7c2bdf..8fb31edb08a 100644 --- a/index.ts +++ b/index.ts @@ -1,13 +1,13 @@ -import { createClient as _createClient, createCluster as _createCluster } from '@redis/client'; -import { RedisScripts } from '@redis/client/dist/lib/commands'; -import { RedisClientOptions, RedisClientType } from '@redis/client/dist/lib/client'; -import { RedisClusterOptions, RedisClusterType } from '@redis/client/dist/lib/cluster'; -import RedisJSON from '@redis/json'; -import RediSearch from '@redis/search'; +import { createClient as _createClient, createCluster as _createCluster } from '@node-redis/client'; +import { RedisScripts } from '@node-redis/client/dist/lib/commands'; +import { RedisClientOptions, RedisClientType } from '@node-redis/client/dist/lib/client'; +import { RedisClusterOptions, RedisClusterType } from '@node-redis/client/dist/lib/cluster'; +import RedisJSON from '@node-redis/json'; +import RediSearch from '@node-redis/search'; -export * from '@redis/client'; -export * from '@redis/json'; -export * from '@redis/search'; +export * from '@node-redis/client'; +export * from '@node-redis/json'; +export * from '@node-redis/search'; const modules = { json: RedisJSON, diff --git a/package-lock.json b/package-lock.json index 2732b2d9a16..5af35d6badb 100644 --- a/package-lock.json +++ b/package-lock.json @@ -12,9 +12,9 @@ "./packages/*" ], "dependencies": { - "@redis/client": "^1.0.0-rc", - "@redis/json": "^1.0.0-rc", - "@redis/search": "^1.0.0-rc" + "@node-redis/client": "^1.0.0-rc", + "@node-redis/json": "^1.0.0-rc", + "@node-redis/search": "^1.0.0-rc" }, "devDependencies": { "@tsconfig/node12": "^1.0.9", @@ -653,6 +653,22 @@ "node": ">=8" } }, + "node_modules/@node-redis/client": { + "resolved": "packages/client", + "link": true + }, + "node_modules/@node-redis/json": { + "resolved": "packages/json", + "link": true + }, + "node_modules/@node-redis/search": { + "resolved": "packages/search", + "link": true + }, + "node_modules/@node-redis/test-utils": { + "resolved": "packages/test-utils", + "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", @@ -820,22 +836,6 @@ "@octokit/openapi-types": "^11.2.0" } }, - "node_modules/@redis/client": { - "resolved": "packages/client", - "link": true - }, - "node_modules/@redis/json": { - "resolved": "packages/json", - "link": true - }, - "node_modules/@redis/search": { - "resolved": "packages/search", - "link": true - }, - "node_modules/@redis/test-utils": { - "resolved": "packages/test-utils", - "link": true - }, "node_modules/@sindresorhus/is": { "version": "4.2.0", "resolved": "https://registry.npmjs.org/@sindresorhus/is/-/is-4.2.0.tgz", @@ -6411,6 +6411,7 @@ } }, "packages/client": { + "name": "@node-redis/client", "version": "1.0.0-rc", "license": "MIT", "dependencies": { @@ -6421,7 +6422,7 @@ }, "devDependencies": { "@istanbuljs/nyc-config-typescript": "^1.0.1", - "@redis/test-utils": "*", + "@node-redis/test-utils": "*", "@types/node": "^16.11.7", "@types/redis-parser": "^3.0.0", "@types/sinon": "^10.0.6", @@ -6444,11 +6445,12 @@ } }, "packages/json": { + "name": "@node-redis/json", "version": "1.0.0-rc.0", "license": "MIT", "devDependencies": { "@istanbuljs/nyc-config-typescript": "^1.0.1", - "@redis/test-utils": "*", + "@node-redis/test-utils": "*", "@types/node": "^16.11.7", "nyc": "^15.1.0", "release-it": "^14.11.7", @@ -6457,15 +6459,16 @@ "typescript": "^4.4.4" }, "peerDependencies": { - "@redis/client": "^1.0.0-rc" + "@node-redis/client": "^1.0.0-rc" } }, "packages/search": { + "name": "@node-redis/search", "version": "1.0.0-rc.0", "license": "MIT", "devDependencies": { "@istanbuljs/nyc-config-typescript": "^1.0.1", - "@redis/test-utils": "*", + "@node-redis/test-utils": "*", "@types/node": "^16.11.7", "nyc": "^15.1.0", "release-it": "^14.11.7", @@ -6474,10 +6477,11 @@ "typescript": "^4.4.4" }, "peerDependencies": { - "@redis/client": "^1.0.0-rc" + "@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", @@ -6492,7 +6496,7 @@ "yargs": "^17.2.1" }, "peerDependencies": { - "@redis/client": "^1.0.0-rc" + "@node-redis/client": "^1.0.0-rc" } } }, @@ -6981,6 +6985,75 @@ "integrity": "sha512-ZXRY4jNvVgSVQ8DL3LTcakaAtXwTVUxE81hslsyD2AtoXW/wVob10HkOJ1X/pAlcI7D+2YoZKg5do8G/w6RYgA==", "dev": true }, + "@node-redis/client": { + "version": "file:packages/client", + "requires": { + "@istanbuljs/nyc-config-typescript": "^1.0.1", + "@node-redis/test-utils": "*", + "@types/node": "^16.11.7", + "@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", + "generic-pool": "3.8.2", + "nyc": "^15.1.0", + "redis-parser": "3.0.0", + "release-it": "^14.11.7", + "sinon": "^12.0.1", + "source-map-support": "^0.5.20", + "ts-node": "^10.4.0", + "typedoc": "^0.22.9", + "typedoc-github-wiki-theme": "^0.6.0", + "typedoc-plugin-markdown": "^3.11.6", + "typescript": "^4.4.4", + "yallist": "4.0.0" + } + }, + "@node-redis/json": { + "version": "file:packages/json", + "requires": { + "@istanbuljs/nyc-config-typescript": "^1.0.1", + "@node-redis/test-utils": "*", + "@types/node": "^16.11.7", + "nyc": "^15.1.0", + "release-it": "^14.11.7", + "source-map-support": "^0.5.20", + "ts-node": "^10.4.0", + "typescript": "^4.4.4" + } + }, + "@node-redis/search": { + "version": "file:packages/search", + "requires": { + "@istanbuljs/nyc-config-typescript": "^1.0.1", + "@node-redis/test-utils": "*", + "@types/node": "^16.11.7", + "nyc": "^15.1.0", + "release-it": "^14.11.7", + "source-map-support": "^0.5.20", + "ts-node": "^10.4.0", + "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", @@ -7131,75 +7204,6 @@ "@octokit/openapi-types": "^11.2.0" } }, - "@redis/client": { - "version": "file:packages/client", - "requires": { - "@istanbuljs/nyc-config-typescript": "^1.0.1", - "@redis/test-utils": "*", - "@types/node": "^16.11.7", - "@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", - "generic-pool": "3.8.2", - "nyc": "^15.1.0", - "redis-parser": "3.0.0", - "release-it": "^14.11.7", - "sinon": "^12.0.1", - "source-map-support": "^0.5.20", - "ts-node": "^10.4.0", - "typedoc": "^0.22.9", - "typedoc-github-wiki-theme": "^0.6.0", - "typedoc-plugin-markdown": "^3.11.6", - "typescript": "^4.4.4", - "yallist": "4.0.0" - } - }, - "@redis/json": { - "version": "file:packages/json", - "requires": { - "@istanbuljs/nyc-config-typescript": "^1.0.1", - "@redis/test-utils": "*", - "@types/node": "^16.11.7", - "nyc": "^15.1.0", - "release-it": "^14.11.7", - "source-map-support": "^0.5.20", - "ts-node": "^10.4.0", - "typescript": "^4.4.4" - } - }, - "@redis/search": { - "version": "file:packages/search", - "requires": { - "@istanbuljs/nyc-config-typescript": "^1.0.1", - "@redis/test-utils": "*", - "@types/node": "^16.11.7", - "nyc": "^15.1.0", - "release-it": "^14.11.7", - "source-map-support": "^0.5.20", - "ts-node": "^10.4.0", - "typescript": "^4.4.4" - } - }, - "@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" - } - }, "@sindresorhus/is": { "version": "4.2.0", "resolved": "https://registry.npmjs.org/@sindresorhus/is/-/is-4.2.0.tgz", diff --git a/package.json b/package.json index d576c3232b7..5986f0f0683 100644 --- a/package.json +++ b/package.json @@ -17,9 +17,9 @@ "build-all": "npm run build:client && npm run build:test-utils && npm run build:modules && npm run build" }, "dependencies": { - "@redis/client": "^1.0.0-rc", - "@redis/json": "^1.0.0-rc", - "@redis/search": "^1.0.0-rc" + "@node-redis/client": "^1.0.0-rc", + "@node-redis/json": "^1.0.0-rc", + "@node-redis/search": "^1.0.0-rc" }, "devDependencies": { "@tsconfig/node12": "^1.0.9", diff --git a/packages/client/.release-it.json b/packages/client/.release-it.json index 60235ee23ea..035124348ca 100644 --- a/packages/client/.release-it.json +++ b/packages/client/.release-it.json @@ -7,4 +7,4 @@ "npm": { "publishArgs": ["--access", "public"] } -} \ No newline at end of file +} diff --git a/packages/client/README.md b/packages/client/README.md index d9c8f80f2f3..37c326fc42e 100644 --- a/packages/client/README.md +++ b/packages/client/README.md @@ -1,2 +1,2 @@ -# @redis/client +# @node-redis/client The sources and docs for this package are in the main [node-redis](https://github.com/redis/node-redis) repo. diff --git a/packages/client/lib/test-utils.ts b/packages/client/lib/test-utils.ts index 85057da841e..321a9da63d5 100644 --- a/packages/client/lib/test-utils.ts +++ b/packages/client/lib/test-utils.ts @@ -1,4 +1,4 @@ -import TestUtils from '@redis/test-utils'; +import TestUtils from '@node-redis/test-utils'; import { SinonSpy } from 'sinon'; import { promiseTimeout } from './utils'; diff --git a/packages/client/package.json b/packages/client/package.json index 3e874af1afc..8a2e2ed7251 100644 --- a/packages/client/package.json +++ b/packages/client/package.json @@ -1,5 +1,5 @@ { - "name": "@redis/client", + "name": "@node-redis/client", "version": "1.0.0-rc", "license": "MIT", "main": "./dist/index.js", @@ -18,7 +18,7 @@ }, "devDependencies": { "@istanbuljs/nyc-config-typescript": "^1.0.1", - "@redis/test-utils": "*", + "@node-redis/test-utils": "*", "@types/node": "^16.11.7", "@types/redis-parser": "^3.0.0", "@types/sinon": "^10.0.6", diff --git a/packages/json/.release-it.json b/packages/json/.release-it.json index 68337fbd5cd..ab495a49b13 100644 --- a/packages/json/.release-it.json +++ b/packages/json/.release-it.json @@ -7,4 +7,4 @@ "npm": { "publishArgs": ["--access", "public"] } -} \ No newline at end of file +} diff --git a/packages/json/README.md b/packages/json/README.md index 45264d671b4..1cd599d5ea8 100644 --- a/packages/json/README.md +++ b/packages/json/README.md @@ -1,2 +1,2 @@ -# @redis/json +# @node-redis/json The sources and docs for this package are in the main [node-redis](https://github.com/redis/node-redis) repo. diff --git a/packages/json/lib/commands/GET.ts b/packages/json/lib/commands/GET.ts index 36bb9bc4e4c..8e897bb67d2 100644 --- a/packages/json/lib/commands/GET.ts +++ b/packages/json/lib/commands/GET.ts @@ -1,4 +1,4 @@ -import { pushVerdictArguments } from '@redis/client/dist/lib/commands/generic-transformers'; +import { pushVerdictArguments } from '@node-redis/client/dist/lib/commands/generic-transformers'; export const FIRST_KEY_INDEX = 1; diff --git a/packages/json/lib/test-utils.ts b/packages/json/lib/test-utils.ts index 140e781d91e..eeee3c77c9d 100644 --- a/packages/json/lib/test-utils.ts +++ b/packages/json/lib/test-utils.ts @@ -1,4 +1,4 @@ -import TestUtils from '@redis/test-utils'; +import TestUtils from '@node-redis/test-utils'; import RedisJSON from '.'; export default new TestUtils({ diff --git a/packages/json/package.json b/packages/json/package.json index 41f1ec3064d..7e5f6e10c1f 100644 --- a/packages/json/package.json +++ b/packages/json/package.json @@ -1,5 +1,5 @@ { - "name": "@redis/json", + "name": "@node-redis/json", "version": "1.0.0-rc.0", "license": "MIT", "main": "./dist/index.js", @@ -9,11 +9,11 @@ "build": "tsc" }, "peerDependencies": { - "@redis/client": "^1.0.0-rc" + "@node-redis/client": "^1.0.0-rc" }, "devDependencies": { "@istanbuljs/nyc-config-typescript": "^1.0.1", - "@redis/test-utils": "*", + "@node-redis/test-utils": "*", "@types/node": "^16.11.7", "nyc": "^15.1.0", "release-it": "^14.11.7", diff --git a/packages/search/.release-it.json b/packages/search/.release-it.json index 9abec2264c4..72cb1016ef4 100644 --- a/packages/search/.release-it.json +++ b/packages/search/.release-it.json @@ -7,4 +7,4 @@ "npm": { "publishArgs": ["--access", "public"] } -} \ No newline at end of file +} diff --git a/packages/search/README.md b/packages/search/README.md index 08db1a72dcf..856a75fbb5a 100644 --- a/packages/search/README.md +++ b/packages/search/README.md @@ -1,2 +1,2 @@ -# @redis/search +# @node-redis/search The sources and docs for this package are in the main [node-redis](https://github.com/redis/node-redis) repo. diff --git a/packages/search/lib/commands/AGGREGATE.ts b/packages/search/lib/commands/AGGREGATE.ts index affc98b51d7..c81dcfef4dd 100644 --- a/packages/search/lib/commands/AGGREGATE.ts +++ b/packages/search/lib/commands/AGGREGATE.ts @@ -1,5 +1,5 @@ -import { RedisCommandArguments } from '@redis/client/dist/lib/commands'; -import { pushVerdictArgument, transformReplyTuples, TuplesObject } from '@redis/client/dist/lib/commands/generic-transformers'; +import { RedisCommandArguments } from '@node-redis/client/dist/lib/commands'; +import { pushVerdictArgument, transformReplyTuples, TuplesObject } from '@node-redis/client/dist/lib/commands/generic-transformers'; import { PropertyName, pushArgumentsWithLength, pushSortByArguments, SortByOptions } from '.'; export enum AggregateSteps { diff --git a/packages/search/lib/commands/CREATE.ts b/packages/search/lib/commands/CREATE.ts index b67896cd64c..1a5e45a4a88 100644 --- a/packages/search/lib/commands/CREATE.ts +++ b/packages/search/lib/commands/CREATE.ts @@ -1,4 +1,4 @@ -import { pushOptionalVerdictArgument } from '@redis/client/dist/lib/commands/generic-transformers'; +import { pushOptionalVerdictArgument } from '@node-redis/client/dist/lib/commands/generic-transformers'; import { RedisSearchLanguages, PropertyName } from '.'; export enum SchemaFieldTypes { diff --git a/packages/search/lib/commands/DICTADD.ts b/packages/search/lib/commands/DICTADD.ts index 60af11fd41f..b3f993395fa 100644 --- a/packages/search/lib/commands/DICTADD.ts +++ b/packages/search/lib/commands/DICTADD.ts @@ -1,5 +1,5 @@ -import { RedisCommandArguments } from '@redis/client/dist/lib/commands'; -import { pushVerdictArguments } from '@redis/client/dist/lib/commands/generic-transformers'; +import { RedisCommandArguments } from '@node-redis/client/dist/lib/commands'; +import { pushVerdictArguments } from '@node-redis/client/dist/lib/commands/generic-transformers'; export function transformArguments(dictionary: string, term: string | Array): RedisCommandArguments { return pushVerdictArguments(['FT.DICTADD', dictionary], term); diff --git a/packages/search/lib/commands/DICTDEL.ts b/packages/search/lib/commands/DICTDEL.ts index a1b728f1926..bd047a5031d 100644 --- a/packages/search/lib/commands/DICTDEL.ts +++ b/packages/search/lib/commands/DICTDEL.ts @@ -1,5 +1,5 @@ -import { RedisCommandArguments } from '@redis/client/dist/lib/commands'; -import { pushVerdictArguments } from '@redis/client/dist/lib/commands/generic-transformers'; +import { RedisCommandArguments } from '@node-redis/client/dist/lib/commands'; +import { pushVerdictArguments } from '@node-redis/client/dist/lib/commands/generic-transformers'; export function transformArguments(dictionary: string, term: string | Array): RedisCommandArguments { return pushVerdictArguments(['FT.DICTDEL', dictionary], term); diff --git a/packages/search/lib/commands/SEARCH.ts b/packages/search/lib/commands/SEARCH.ts index a773514a738..9616bfa0bee 100644 --- a/packages/search/lib/commands/SEARCH.ts +++ b/packages/search/lib/commands/SEARCH.ts @@ -1,5 +1,5 @@ -import { RedisCommandArguments } from '@redis/client/dist/lib/commands'; -import { pushOptionalVerdictArgument, pushVerdictArgument, transformReplyTuples } from '@redis/client/dist/lib/commands/generic-transformers'; +import { RedisCommandArguments } from '@node-redis/client/dist/lib/commands'; +import { pushOptionalVerdictArgument, pushVerdictArgument, transformReplyTuples } from '@node-redis/client/dist/lib/commands/generic-transformers'; import { RedisSearchLanguages, PropertyName, pushSortByArguments, SortByOptions } from '.'; export const FIRST_KEY_INDEX = 1; diff --git a/packages/search/lib/commands/SUGDEL.ts b/packages/search/lib/commands/SUGDEL.ts index be2d4262caa..43f4744c00d 100644 --- a/packages/search/lib/commands/SUGDEL.ts +++ b/packages/search/lib/commands/SUGDEL.ts @@ -2,4 +2,4 @@ export function transformArguments(key: string, string: string): Array { return ['FT.SUGDEL', key, string]; } -export { transformReplyBoolean as transformReply } from '@redis/client/dist/lib/commands/generic-transformers'; +export { transformReplyBoolean as transformReply } from '@node-redis/client/dist/lib/commands/generic-transformers'; diff --git a/packages/search/lib/commands/SYNUPDATE.ts b/packages/search/lib/commands/SYNUPDATE.ts index 3384ea59d94..b59d35617fa 100644 --- a/packages/search/lib/commands/SYNUPDATE.ts +++ b/packages/search/lib/commands/SYNUPDATE.ts @@ -1,5 +1,5 @@ -import { pushVerdictArguments } from '@redis/client/dist/lib/commands/generic-transformers'; -import { RedisCommandArguments } from '@redis/client/dist/lib/commands'; +import { pushVerdictArguments } from '@node-redis/client/dist/lib/commands/generic-transformers'; +import { RedisCommandArguments } from '@node-redis/client/dist/lib/commands'; interface SynUpdateOptions { SKIPINITIALSCAN?: true; diff --git a/packages/search/lib/commands/index.ts b/packages/search/lib/commands/index.ts index c10a10774b7..70626c00df0 100644 --- a/packages/search/lib/commands/index.ts +++ b/packages/search/lib/commands/index.ts @@ -26,7 +26,7 @@ import * as SUGLEN from './SUGLEN'; import * as SYNDUMP from './SYNDUMP'; import * as SYNUPDATE from './SYNUPDATE'; import * as TAGVALS from './TAGVALS'; -import { RedisCommandArguments } from '@redis/client/dist/lib/commands'; +import { RedisCommandArguments } from '@node-redis/client/dist/lib/commands'; export default { _LIST, diff --git a/packages/search/lib/test-utils.ts b/packages/search/lib/test-utils.ts index c68504c21d7..4af05e10623 100644 --- a/packages/search/lib/test-utils.ts +++ b/packages/search/lib/test-utils.ts @@ -1,4 +1,4 @@ -import TestUtils from '@redis/test-utils'; +import TestUtils from '@node-redis/test-utils'; import RediSearch from '.'; export default new TestUtils({ diff --git a/packages/search/package.json b/packages/search/package.json index 33bf7fffe4a..a72678c2add 100644 --- a/packages/search/package.json +++ b/packages/search/package.json @@ -1,5 +1,5 @@ { - "name": "@redis/search", + "name": "@node-redis/search", "version": "1.0.0-rc.0", "license": "MIT", "main": "./dist/index.js", @@ -9,11 +9,11 @@ "build": "tsc" }, "peerDependencies": { - "@redis/client": "^1.0.0-rc" + "@node-redis/client": "^1.0.0-rc" }, "devDependencies": { "@istanbuljs/nyc-config-typescript": "^1.0.1", - "@redis/test-utils": "*", + "@node-redis/test-utils": "*", "@types/node": "^16.11.7", "nyc": "^15.1.0", "release-it": "^14.11.7", diff --git a/packages/test-utils/lib/dockers.ts b/packages/test-utils/lib/dockers.ts index 052388970ec..3dd6dcf3ebf 100644 --- a/packages/test-utils/lib/dockers.ts +++ b/packages/test-utils/lib/dockers.ts @@ -1,8 +1,8 @@ import { createConnection } from 'net'; import { once } from 'events'; -import { RedisModules, RedisScripts } from '@redis/client/lib/commands'; -import RedisClient, { RedisClientType } from '@redis/client/lib/client'; -import { promiseTimeout } from '@redis/client/lib/utils'; +import { RedisModules, RedisScripts } from '@node-redis/client/lib/commands'; +import RedisClient, { RedisClientType } from '@node-redis/client/lib/client'; +import { promiseTimeout } from '@node-redis/client/lib/utils'; import * as path from 'path'; import { promisify } from 'util'; import { exec } from 'child_process'; diff --git a/packages/test-utils/lib/index.ts b/packages/test-utils/lib/index.ts index f9a1fc1dbd9..8e20df75818 100644 --- a/packages/test-utils/lib/index.ts +++ b/packages/test-utils/lib/index.ts @@ -1,6 +1,6 @@ -import { RedisModules, RedisScripts } from '@redis/client/lib/commands'; -import RedisClient, { RedisClientOptions, RedisClientType } from '@redis/client/lib/client'; -import RedisCluster, { RedisClusterOptions, RedisClusterType } from '@redis/client/lib/cluster'; +import { RedisModules, RedisScripts } from '@node-redis/client/lib/commands'; +import RedisClient, { RedisClientOptions, RedisClientType } from '@node-redis/client/lib/client'; +import RedisCluster, { RedisClusterOptions, RedisClusterType } from '@node-redis/client/lib/cluster'; import { RedisServerDockerConfig, spawnRedisServer, spawnRedisCluster } from './dockers'; import yargs from 'yargs'; import { hideBin } from 'yargs/helpers'; diff --git a/packages/test-utils/package.json b/packages/test-utils/package.json index a1b7c4d4166..47ddc25acff 100644 --- a/packages/test-utils/package.json +++ b/packages/test-utils/package.json @@ -1,5 +1,5 @@ { - "name": "@redis/test-utils", + "name": "@node-redis/test-utils", "private": true, "main": "./dist/index.js", "types": "./dist/index.d.ts", @@ -8,7 +8,7 @@ "test": "echo \"TODO\"" }, "peerDependencies": { - "@redis/client": "^1.0.0-rc" + "@node-redis/client": "^1.0.0-rc" }, "devDependencies": { "@istanbuljs/nyc-config-typescript": "^1.0.1", From b2aacb6d3e004a26ffc4b8ea8dc0aaad10f25d7b Mon Sep 17 00:00:00 2001 From: leibale Date: Tue, 16 Nov 2021 02:36:10 -0500 Subject: [PATCH 0962/1748] Release client@1.0.0-rc.0 --- packages/client/package.json | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/packages/client/package.json b/packages/client/package.json index 8a2e2ed7251..7a6d23f5ff9 100644 --- a/packages/client/package.json +++ b/packages/client/package.json @@ -1,6 +1,6 @@ { "name": "@node-redis/client", - "version": "1.0.0-rc", + "version": "1.0.0-rc.0", "license": "MIT", "main": "./dist/index.js", "types": "./dist/index.d.ts", From 4bca55afff38fe24bf513e58efa430a6c60833ce Mon Sep 17 00:00:00 2001 From: leibale Date: Tue, 16 Nov 2021 02:37:37 -0500 Subject: [PATCH 0963/1748] update json & search version --- packages/json/package.json | 2 +- packages/search/package.json | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/packages/json/package.json b/packages/json/package.json index 7e5f6e10c1f..69950442b29 100644 --- a/packages/json/package.json +++ b/packages/json/package.json @@ -1,6 +1,6 @@ { "name": "@node-redis/json", - "version": "1.0.0-rc.0", + "version": "1.0.0-rc", "license": "MIT", "main": "./dist/index.js", "types": "./dist/index.d.ts", diff --git a/packages/search/package.json b/packages/search/package.json index a72678c2add..1b7f3c9fbe2 100644 --- a/packages/search/package.json +++ b/packages/search/package.json @@ -1,6 +1,6 @@ { "name": "@node-redis/search", - "version": "1.0.0-rc.0", + "version": "1.0.0-rc", "license": "MIT", "main": "./dist/index.js", "types": "./dist/index.d.ts", From be063d1a368934b8c3eff2289ff57425528cf317 Mon Sep 17 00:00:00 2001 From: leibale Date: Tue, 16 Nov 2021 02:37:55 -0500 Subject: [PATCH 0964/1748] Release json@1.0.0-rc.0 --- packages/json/package.json | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/packages/json/package.json b/packages/json/package.json index 69950442b29..7e5f6e10c1f 100644 --- a/packages/json/package.json +++ b/packages/json/package.json @@ -1,6 +1,6 @@ { "name": "@node-redis/json", - "version": "1.0.0-rc", + "version": "1.0.0-rc.0", "license": "MIT", "main": "./dist/index.js", "types": "./dist/index.d.ts", From f2a3b3fb5e20d137edb91cc8a2de4f9f4512ab63 Mon Sep 17 00:00:00 2001 From: leibale Date: Tue, 16 Nov 2021 02:38:25 -0500 Subject: [PATCH 0965/1748] Release search@1.0.0-rc.0 --- packages/search/package.json | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/packages/search/package.json b/packages/search/package.json index 1b7f3c9fbe2..a72678c2add 100644 --- a/packages/search/package.json +++ b/packages/search/package.json @@ -1,6 +1,6 @@ { "name": "@node-redis/search", - "version": "1.0.0-rc", + "version": "1.0.0-rc.0", "license": "MIT", "main": "./dist/index.js", "types": "./dist/index.d.ts", From b8359fe1839b58733a8e619f307d594149cfd5e9 Mon Sep 17 00:00:00 2001 From: leibale Date: Tue, 16 Nov 2021 02:40:07 -0500 Subject: [PATCH 0966/1748] update dependencies --- package.json | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/package.json b/package.json index 5986f0f0683..2063ee4079b 100644 --- a/package.json +++ b/package.json @@ -17,9 +17,9 @@ "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", - "@node-redis/json": "^1.0.0-rc", - "@node-redis/search": "^1.0.0-rc" + "@node-redis/client": "^1.0.0-rc.0", + "@node-redis/json": "^1.0.0-rc.0", + "@node-redis/search": "^1.0.0-rc.0" }, "devDependencies": { "@tsconfig/node12": "^1.0.9", From 362daf63e4e5eab2a27dadee897ba388a879356b Mon Sep 17 00:00:00 2001 From: leibale Date: Tue, 16 Nov 2021 02:40:26 -0500 Subject: [PATCH 0967/1748] Release redis@4.0.0-rc.4 --- package-lock.json | 4 ++-- package.json | 2 +- 2 files changed, 3 insertions(+), 3 deletions(-) diff --git a/package-lock.json b/package-lock.json index 5af35d6badb..5c3ebb5c208 100644 --- a/package-lock.json +++ b/package-lock.json @@ -1,12 +1,12 @@ { "name": "redis", - "version": "4.0.0-rc.3", + "version": "4.0.0-rc.4", "lockfileVersion": 2, "requires": true, "packages": { "": { "name": "redis", - "version": "4.0.0-rc.3", + "version": "4.0.0-rc.4", "license": "MIT", "workspaces": [ "./packages/*" diff --git a/package.json b/package.json index 2063ee4079b..49339e44d26 100644 --- a/package.json +++ b/package.json @@ -1,6 +1,6 @@ { "name": "redis", - "version": "4.0.0-rc.3", + "version": "4.0.0-rc.4", "license": "MIT", "main": "./dist/index.js", "types": "./dist/index.d.ts", From eed479778f4a18f3f6be134a9c9c0a1bb94585a6 Mon Sep 17 00:00:00 2001 From: Leibale Eidelman Date: Tue, 16 Nov 2021 02:48:20 -0500 Subject: [PATCH 0968/1748] v4.0.0-rc.4 (#1723) MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit * 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 * fix #1652 * ref #1653 - better types * better types * fix 54124793ad1b633d39d372bdff487c9888c017a7 * 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 * 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 * 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 * 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 * 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 * Update(examples): fix case, add timeout, update readme Signed-off-by: Aditya Rastogi 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 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 Co-authored-by: Simon Prickett 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 * revert d32f1edf8a8ede15ede5654168e345ebd95c3ac6 * 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 Co-authored-by: Richard Samuelsson Co-authored-by: mustard Co-authored-by: Simon Prickett Co-authored-by: Simon Prickett Co-authored-by: Suze Shardlow Co-authored-by: Joshua T Co-authored-by: Aditya Rastogi Co-authored-by: Rohan Kumar Co-authored-by: Kalki Co-authored-by: Chayim Co-authored-by: Da-Jin Chu Co-authored-by: Henrique Corrêa <75134774+HeCorr@users.noreply.github.com> Co-authored-by: Evert Pot --- .github/README.md | 298 ++ .github/release-drafter-config.yml | 43 + .github/workflows/benchmark.yml | 46 - .github/workflows/documentation.yml | 5 - .github/workflows/release-drafter.yml | 19 + .github/workflows/tests.yml | 55 +- .gitignore | 8 +- .npmignore | 24 +- .release-it.json | 7 + CONTRIBUTING.md | 7 +- LICENSE | 2 +- README.md | 291 +- SECURITY.md | 6 +- benchmark/.gitignore | 1 - benchmark/index.js | 81 - benchmark/package-lock.json | 849 ---- benchmark/package.json | 17 - docs/client-configuration.md | 2 +- docs/clustering.md | 56 + docs/isolated-execution.md | 2 +- examples/.gitignore | 1 + examples/README.md | 76 + examples/blocking-list-pop.js | 32 + examples/command-with-modifiers.js | 31 + examples/connect-as-acl-user.js | 30 + examples/lua-multi-incr.js | 31 + examples/package.json | 12 + examples/search+json.js | 74 + examples/set-scan.js | 19 + index.ts | 35 +- lib/cluster/index.spec.ts | 114 - lib/commands/ACL_GETUSER.spec.ts | 27 - lib/commands/COMMAND.spec.ts | 30 - lib/commands/COMMAND_INFO.spec.ts | 30 - lib/commands/CONFIG_GET.ts | 7 - lib/commands/ZCOUNT.ts | 16 - lib/test-utils.ts | 377 -- lib/ts-declarations/redis-parser.d.ts | 15 - package-lock.json | 3459 ++++++++++++----- package.json | 65 +- packages/client/.eslintrc.json | 15 + packages/client/.gitignore | 1 + packages/client/.npmignore | 10 + packages/client/.nycrc.json | 4 + packages/client/.release-it.json | 10 + CHANGELOG.md => packages/client/CHANGELOG.md | 33 +- packages/client/README.md | 2 + packages/client/index.ts | 10 + .../client/lib}/client/commands-queue.ts | 25 +- .../client/lib}/client/commands.ts | 2 +- .../client/lib}/client/index.spec.ts | 524 +-- {lib => packages/client/lib}/client/index.ts | 118 +- .../client/lib}/client/multi-command.ts | 12 +- .../client/lib}/client/socket.spec.ts | 2 +- {lib => packages/client/lib}/client/socket.ts | 92 +- .../client/lib}/cluster/cluster-slots.ts | 12 +- .../client/lib}/cluster/commands.ts | 0 packages/client/lib/cluster/index.spec.ts | 105 + {lib => packages/client/lib}/cluster/index.ts | 17 +- .../client/lib}/cluster/multi-command.ts | 6 +- .../client/lib}/command-options.ts | 0 .../client/lib}/commander.spec.ts | 0 {lib => packages/client/lib}/commander.ts | 15 + .../client/lib}/commands/ACL_CAT.spec.ts | 4 +- .../client/lib}/commands/ACL_CAT.ts | 0 .../client/lib}/commands/ACL_DELUSER.spec.ts | 8 +- .../client/lib}/commands/ACL_DELUSER.ts | 0 .../client/lib}/commands/ACL_GENPASS.spec.ts | 4 +- .../client/lib}/commands/ACL_GENPASS.ts | 0 .../client/lib/commands/ACL_GETUSER.spec.ts | 32 + .../client/lib}/commands/ACL_GETUSER.ts | 0 .../client/lib}/commands/ACL_LIST.spec.ts | 4 +- .../client/lib}/commands/ACL_LIST.ts | 0 .../client/lib}/commands/ACL_LOAD.spec.ts | 4 +- .../client/lib}/commands/ACL_LOAD.ts | 0 .../client/lib}/commands/ACL_LOG.spec.ts | 4 +- .../client/lib}/commands/ACL_LOG.ts | 0 .../lib}/commands/ACL_LOG_RESET.spec.ts | 4 +- .../client/lib}/commands/ACL_LOG_RESET.ts | 0 .../client/lib}/commands/ACL_SAVE.spec.ts | 4 +- .../client/lib}/commands/ACL_SAVE.ts | 0 .../client/lib}/commands/ACL_SETUSER.spec.ts | 4 +- .../client/lib}/commands/ACL_SETUSER.ts | 0 .../client/lib}/commands/ACL_USERS.spec.ts | 4 +- .../client/lib}/commands/ACL_USERS.ts | 0 .../client/lib}/commands/ACL_WHOAMI.spec.ts | 4 +- .../client/lib}/commands/ACL_WHOAMI.ts | 0 .../client/lib}/commands/APPEND.spec.ts | 2 +- .../client/lib}/commands/APPEND.ts | 0 .../client/lib}/commands/ASKING.spec.ts | 0 .../client/lib}/commands/ASKING.ts | 0 .../client/lib}/commands/AUTH.spec.ts | 0 {lib => packages/client/lib}/commands/AUTH.ts | 0 .../client/lib}/commands/BGREWRITEAOF.spec.ts | 0 .../client/lib}/commands/BGREWRITEAOF.ts | 0 .../client/lib}/commands/BGSAVE.spec.ts | 0 .../client/lib}/commands/BGSAVE.ts | 0 .../client/lib}/commands/BITCOUNT.spec.ts | 6 +- .../client/lib}/commands/BITCOUNT.ts | 0 .../client/lib}/commands/BITFIELD.spec.ts | 6 +- .../client/lib}/commands/BITFIELD.ts | 0 .../client/lib}/commands/BITOP.spec.ts | 10 +- .../client/lib}/commands/BITOP.ts | 0 .../client/lib}/commands/BITPOS.spec.ts | 10 +- .../client/lib}/commands/BITPOS.ts | 0 .../client/lib}/commands/BLMOVE.spec.ts | 12 +- .../client/lib}/commands/BLMOVE.ts | 0 .../client/lib}/commands/BLPOP.spec.ts | 10 +- .../client/lib}/commands/BLPOP.ts | 0 .../client/lib}/commands/BRPOP.spec.ts | 10 +- .../client/lib}/commands/BRPOP.ts | 0 .../client/lib}/commands/BRPOPLPUSH.spec.ts | 10 +- .../client/lib}/commands/BRPOPLPUSH.ts | 0 .../client/lib}/commands/BZPOPMAX.spec.ts | 6 +- .../client/lib}/commands/BZPOPMAX.ts | 0 .../client/lib}/commands/BZPOPMIN.spec.ts | 6 +- .../client/lib}/commands/BZPOPMIN.ts | 0 .../client/lib}/commands/CLIENT_ID.spec.ts | 6 +- .../client/lib}/commands/CLIENT_ID.ts | 0 .../client/lib}/commands/CLIENT_INFO.spec.ts | 0 .../client/lib}/commands/CLIENT_INFO.ts | 0 .../lib}/commands/CLUSTER_ADDSLOTS.spec.ts | 0 .../client/lib}/commands/CLUSTER_ADDSLOTS.ts | 0 .../lib}/commands/CLUSTER_FLUSHSLOTS.spec.ts | 0 .../lib}/commands/CLUSTER_FLUSHSLOTS.ts | 0 .../commands/CLUSTER_GETKEYSINSLOT.spec.ts | 0 .../lib}/commands/CLUSTER_GETKEYSINSLOT.ts | 0 .../client/lib}/commands/CLUSTER_INFO.spec.ts | 0 .../client/lib}/commands/CLUSTER_INFO.ts | 0 .../client/lib}/commands/CLUSTER_MEET.spec.ts | 0 .../client/lib}/commands/CLUSTER_MEET.ts | 0 .../lib}/commands/CLUSTER_NODES.spec.ts | 0 .../client/lib}/commands/CLUSTER_NODES.ts | 0 .../lib}/commands/CLUSTER_RESET.spec.ts | 0 .../client/lib}/commands/CLUSTER_RESET.ts | 0 .../lib}/commands/CLUSTER_SETSLOT.spec.ts | 0 .../client/lib}/commands/CLUSTER_SETSLOT.ts | 0 .../lib}/commands/CLUSTER_SLOTS.spec.ts | 2 +- .../client/lib}/commands/CLUSTER_SLOTS.ts | 0 packages/client/lib/commands/COMMAND.spec.ts | 17 + .../client/lib}/commands/COMMAND.ts | 0 .../lib}/commands/COMMAND_COUNT.spec.ts | 6 +- .../client/lib}/commands/COMMAND_COUNT.ts | 2 +- .../lib}/commands/COMMAND_GETKEYS.spec.ts | 6 +- .../client/lib}/commands/COMMAND_GETKEYS.ts | 2 +- .../client/lib/commands/COMMAND_INFO.spec.ts | 45 + .../client/lib}/commands/COMMAND_INFO.ts | 0 .../client/lib}/commands/CONFIG_GET.spec.ts | 0 packages/client/lib/commands/CONFIG_GET.ts | 5 + .../lib}/commands/CONFIG_RESETSTAT.spec.ts | 0 .../client/lib}/commands/CONFIG_RESETSTAT.ts | 0 .../lib}/commands/CONFIG_REWRITE.spec.ts | 0 .../client/lib}/commands/CONFIG_REWRITE.ts | 0 .../client/lib}/commands/CONFIG_SET.spec.ts | 0 .../client/lib}/commands/CONFIG_SET.ts | 0 .../client/lib}/commands/COPY.spec.ts | 8 +- {lib => packages/client/lib}/commands/COPY.ts | 4 +- .../client/lib}/commands/DBSIZE.spec.ts | 6 +- .../client/lib}/commands/DBSIZE.ts | 0 .../client/lib}/commands/DECR.spec.ts | 6 +- {lib => packages/client/lib}/commands/DECR.ts | 0 .../client/lib}/commands/DECRBY.spec.ts | 6 +- .../client/lib}/commands/DECRBY.ts | 0 .../client/lib}/commands/DEL.spec.ts | 6 +- {lib => packages/client/lib}/commands/DEL.ts | 0 .../client/lib}/commands/DISCARD.spec.ts | 0 .../client/lib}/commands/DISCARD.ts | 0 .../client/lib}/commands/DUMP.spec.ts | 6 +- {lib => packages/client/lib}/commands/DUMP.ts | 0 .../client/lib}/commands/ECHO.spec.ts | 6 +- {lib => packages/client/lib}/commands/ECHO.ts | 0 .../client/lib}/commands/EVAL.spec.ts | 10 +- {lib => packages/client/lib}/commands/EVAL.ts | 0 .../client/lib}/commands/EVALSHA.spec.ts | 0 .../client/lib}/commands/EVALSHA.ts | 0 .../client/lib}/commands/EXISTS.spec.ts | 6 +- .../client/lib}/commands/EXISTS.ts | 4 +- .../client/lib}/commands/EXPIRE.spec.ts | 6 +- .../client/lib}/commands/EXPIRE.ts | 4 +- .../client/lib}/commands/EXPIREAT.spec.ts | 8 +- .../client/lib}/commands/EXPIREAT.ts | 4 +- .../client/lib}/commands/FAILOVER.spec.ts | 0 .../client/lib}/commands/FAILOVER.ts | 0 .../client/lib}/commands/FLUSHALL.spec.ts | 6 +- .../client/lib}/commands/FLUSHALL.ts | 0 .../client/lib}/commands/FLUSHDB.spec.ts | 6 +- .../client/lib}/commands/FLUSHDB.ts | 0 .../client/lib}/commands/GEOADD.spec.ts | 10 +- .../client/lib}/commands/GEOADD.ts | 0 .../client/lib}/commands/GEODIST.spec.ts | 14 +- .../client/lib}/commands/GEODIST.ts | 0 .../client/lib}/commands/GEOHASH.spec.ts | 10 +- .../client/lib}/commands/GEOHASH.ts | 0 .../client/lib}/commands/GEOPOS.spec.ts | 14 +- .../client/lib}/commands/GEOPOS.ts | 0 .../client/lib}/commands/GEOSEARCH.spec.ts | 12 +- .../client/lib}/commands/GEOSEARCH.ts | 0 .../lib}/commands/GEOSEARCHSTORE.spec.ts | 12 +- .../client/lib}/commands/GEOSEARCHSTORE.ts | 0 .../lib}/commands/GEOSEARCH_WITH.spec.ts | 14 +- .../client/lib}/commands/GEOSEARCH_WITH.ts | 4 +- .../client/lib}/commands/GET.spec.ts | 10 +- {lib => packages/client/lib}/commands/GET.ts | 0 .../client/lib}/commands/GETBIT.spec.ts | 10 +- .../client/lib}/commands/GETBIT.ts | 0 .../client/lib}/commands/GETDEL.spec.ts | 13 +- .../client/lib}/commands/GETDEL.ts | 0 .../client/lib}/commands/GETEX.spec.ts | 12 +- .../client/lib}/commands/GETEX.ts | 0 .../client/lib}/commands/GETRANGE.spec.ts | 11 +- .../client/lib}/commands/GETRANGE.ts | 0 .../client/lib}/commands/GETSET.spec.ts | 10 +- .../client/lib}/commands/GETSET.ts | 0 .../client/lib}/commands/GET_BUFFER.spec.ts | 10 +- .../client/lib}/commands/GET_BUFFER.ts | 0 .../client/lib}/commands/HDEL.spec.ts | 6 +- {lib => packages/client/lib}/commands/HDEL.ts | 0 .../client/lib}/commands/HELLO.spec.ts | 29 +- .../client/lib}/commands/HELLO.ts | 0 .../client/lib}/commands/HEXISTS.spec.ts | 6 +- .../client/lib}/commands/HEXISTS.ts | 4 +- .../client/lib}/commands/HGET.spec.ts | 6 +- {lib => packages/client/lib}/commands/HGET.ts | 0 .../client/lib}/commands/HGETALL.spec.ts | 6 +- .../client/lib}/commands/HGETALL.ts | 4 +- .../client/lib}/commands/HINCRBY.spec.ts | 6 +- .../client/lib}/commands/HINCRBY.ts | 0 .../client/lib}/commands/HINCRBYFLOAT.spec.ts | 6 +- .../client/lib}/commands/HINCRBYFLOAT.ts | 0 .../client/lib}/commands/HKEYS.spec.ts | 6 +- .../client/lib}/commands/HKEYS.ts | 0 .../client/lib}/commands/HLEN.spec.ts | 6 +- {lib => packages/client/lib}/commands/HLEN.ts | 0 .../client/lib}/commands/HMGET.spec.ts | 6 +- .../client/lib}/commands/HMGET.ts | 0 .../client/lib}/commands/HRANDFIELD.spec.ts | 8 +- .../client/lib}/commands/HRANDFIELD.ts | 0 .../lib}/commands/HRANDFIELD_COUNT.spec.ts | 8 +- .../client/lib}/commands/HRANDFIELD_COUNT.ts | 0 .../HRANDFIELD_COUNT_WITHVALUES.spec.ts | 8 +- .../commands/HRANDFIELD_COUNT_WITHVALUES.ts | 3 +- .../client/lib}/commands/HSCAN.spec.ts | 6 +- .../client/lib}/commands/HSCAN.ts | 0 .../client/lib}/commands/HSET.spec.ts | 10 +- {lib => packages/client/lib}/commands/HSET.ts | 4 +- .../client/lib}/commands/HSETNX.spec.ts | 6 +- .../client/lib}/commands/HSETNX.ts | 4 +- .../client/lib}/commands/HSTRLEN.spec.ts | 6 +- .../client/lib}/commands/HSTRLEN.ts | 0 .../client/lib}/commands/HVALS.spec.ts | 6 +- .../client/lib}/commands/HVALS.ts | 0 .../client/lib}/commands/INCR.spec.ts | 6 +- {lib => packages/client/lib}/commands/INCR.ts | 0 .../client/lib}/commands/INCRBY.spec.ts | 6 +- .../client/lib}/commands/INCRBY.ts | 0 .../client/lib}/commands/INCRBYFLOAT.spec.ts | 6 +- .../client/lib}/commands/INCRBYFLOAT.ts | 0 .../client/lib}/commands/INFO.spec.ts | 0 {lib => packages/client/lib}/commands/INFO.ts | 0 .../client/lib}/commands/KEYS.spec.ts | 6 +- {lib => packages/client/lib}/commands/KEYS.ts | 0 .../client/lib}/commands/LASTSAVE.spec.ts | 6 +- .../client/lib}/commands/LASTSAVE.ts | 0 .../client/lib}/commands/LINDEX.spec.ts | 10 +- .../client/lib}/commands/LINDEX.ts | 2 +- .../client/lib}/commands/LINSERT.spec.ts | 10 +- .../client/lib}/commands/LINSERT.ts | 0 .../client/lib}/commands/LLEN.spec.ts | 10 +- {lib => packages/client/lib}/commands/LLEN.ts | 0 .../client/lib}/commands/LMOVE.spec.ts | 12 +- .../client/lib}/commands/LMOVE.ts | 0 .../client/lib}/commands/LOLWUT.spec.ts | 7 +- .../client/lib}/commands/LOLWUT.ts | 0 .../client/lib}/commands/LPOP.spec.ts | 10 +- {lib => packages/client/lib}/commands/LPOP.ts | 0 .../client/lib}/commands/LPOP_COUNT.spec.ts | 12 +- .../client/lib}/commands/LPOP_COUNT.ts | 0 .../client/lib}/commands/LPOS.spec.ts | 12 +- {lib => packages/client/lib}/commands/LPOS.ts | 0 .../client/lib}/commands/LPOS_COUNT.spec.ts | 12 +- .../client/lib}/commands/LPOS_COUNT.ts | 0 .../client/lib}/commands/LPUSH.spec.ts | 10 +- .../client/lib}/commands/LPUSH.ts | 0 .../client/lib}/commands/LPUSHX.spec.ts | 10 +- .../client/lib}/commands/LPUSHX.ts | 0 .../client/lib}/commands/LRANGE.spec.ts | 10 +- .../client/lib}/commands/LRANGE.ts | 0 .../client/lib}/commands/LREM.spec.ts | 10 +- {lib => packages/client/lib}/commands/LREM.ts | 0 .../client/lib}/commands/LSET.spec.ts | 10 +- {lib => packages/client/lib}/commands/LSET.ts | 0 .../client/lib}/commands/LTRIM.spec.ts | 10 +- .../client/lib}/commands/LTRIM.ts | 2 +- .../lib}/commands/MEMORY_DOCTOR.spec.ts | 6 +- .../client/lib}/commands/MEMORY_DOCTOR.ts | 0 .../lib}/commands/MEMORY_MALLOC-STATS.spec.ts | 6 +- .../lib}/commands/MEMORY_MALLOC-STATS.ts | 0 .../client/lib}/commands/MEMORY_PURGE.spec.ts | 6 +- .../client/lib}/commands/MEMORY_PURGE.ts | 0 .../client/lib}/commands/MEMORY_STATS.spec.ts | 0 .../client/lib}/commands/MEMORY_STATS.ts | 0 .../client/lib}/commands/MEMORY_USAGE.spec.ts | 6 +- .../client/lib}/commands/MEMORY_USAGE.ts | 0 .../client/lib}/commands/MGET.spec.ts | 10 +- {lib => packages/client/lib}/commands/MGET.ts | 0 .../client/lib}/commands/MIGRATE.spec.ts | 0 .../client/lib}/commands/MIGRATE.ts | 0 .../client/lib}/commands/MODULE_LIST.spec.ts | 0 .../client/lib}/commands/MODULE_LIST.ts | 0 .../client/lib}/commands/MODULE_LOAD.spec.ts | 0 .../client/lib}/commands/MODULE_LOAD.ts | 0 .../lib}/commands/MODULE_UNLOAD.spec.ts | 0 .../client/lib}/commands/MODULE_UNLOAD.ts | 0 .../client/lib}/commands/MOVE.spec.ts | 6 +- {lib => packages/client/lib}/commands/MOVE.ts | 4 +- .../client/lib}/commands/MSET.spec.ts | 10 +- {lib => packages/client/lib}/commands/MSET.ts | 0 .../client/lib}/commands/MSETNX.spec.ts | 10 +- .../client/lib}/commands/MSETNX.ts | 4 +- .../client/lib}/commands/PERSIST.spec.ts | 6 +- .../client/lib}/commands/PERSIST.ts | 4 +- .../client/lib}/commands/PEXPIRE.spec.ts | 6 +- .../client/lib}/commands/PEXPIRE.ts | 4 +- .../client/lib}/commands/PEXPIREAT.spec.ts | 8 +- .../client/lib}/commands/PEXPIREAT.ts | 4 +- .../client/lib}/commands/PFADD.spec.ts | 6 +- .../client/lib}/commands/PFADD.ts | 4 +- .../client/lib}/commands/PFCOUNT.spec.ts | 6 +- .../client/lib}/commands/PFCOUNT.ts | 0 .../client/lib}/commands/PFMERGE.spec.ts | 6 +- .../client/lib}/commands/PFMERGE.ts | 0 .../client/lib}/commands/PING.spec.ts | 6 +- {lib => packages/client/lib}/commands/PING.ts | 0 .../client/lib}/commands/PSETEX.spec.ts | 10 +- .../client/lib}/commands/PSETEX.ts | 0 .../client/lib}/commands/PTTL.spec.ts | 6 +- {lib => packages/client/lib}/commands/PTTL.ts | 0 .../client/lib}/commands/PUBLISH.spec.ts | 6 +- .../client/lib}/commands/PUBLISH.ts | 0 .../lib}/commands/PUBSUB_CHANNELS.spec.ts | 6 +- .../client/lib}/commands/PUBSUB_CHANNELS.ts | 0 .../lib}/commands/PUBSUB_NUMPAT.spec.ts | 6 +- .../client/lib}/commands/PUBSUB_NUMPAT.ts | 0 .../lib}/commands/PUBSUB_NUMSUB.spec.ts | 6 +- .../client/lib}/commands/PUBSUB_NUMSUB.ts | 0 .../client/lib}/commands/RANDOMKEY.spec.ts | 6 +- .../client/lib}/commands/RANDOMKEY.ts | 0 .../client/lib}/commands/READONLY.spec.ts | 0 .../client/lib}/commands/READONLY.ts | 0 .../client/lib}/commands/READWRITE.spec.ts | 0 .../client/lib}/commands/READWRITE.ts | 0 .../client/lib}/commands/RENAME.spec.ts | 6 +- .../client/lib}/commands/RENAME.ts | 0 .../client/lib}/commands/RENAMENX.spec.ts | 6 +- .../client/lib}/commands/RENAMENX.ts | 4 +- .../client/lib}/commands/REPLICAOF.spec.ts | 0 .../client/lib}/commands/REPLICAOF.ts | 0 .../lib}/commands/RESTORE-ASKING.spec.ts | 0 .../client/lib}/commands/RESTORE-ASKING.ts | 0 .../client/lib}/commands/ROLE.spec.ts | 6 +- {lib => packages/client/lib}/commands/ROLE.ts | 0 .../client/lib}/commands/RPOP.spec.ts | 10 +- {lib => packages/client/lib}/commands/RPOP.ts | 0 .../client/lib}/commands/RPOPLPUSH.spec.ts | 10 +- .../client/lib}/commands/RPOPLPUSH.ts | 0 .../client/lib}/commands/RPOP_COUNT.spec.ts | 12 +- .../client/lib}/commands/RPOP_COUNT.ts | 0 .../client/lib}/commands/RPUSH.spec.ts | 10 +- .../client/lib}/commands/RPUSH.ts | 0 .../client/lib}/commands/RPUSHX.spec.ts | 10 +- .../client/lib}/commands/RPUSHX.ts | 0 .../client/lib}/commands/SADD.spec.ts | 6 +- {lib => packages/client/lib}/commands/SADD.ts | 0 .../client/lib}/commands/SAVE.spec.ts | 0 {lib => packages/client/lib}/commands/SAVE.ts | 0 .../client/lib}/commands/SCAN.spec.ts | 6 +- {lib => packages/client/lib}/commands/SCAN.ts | 0 .../client/lib}/commands/SCARD.spec.ts | 6 +- .../client/lib}/commands/SCARD.ts | 0 .../client/lib}/commands/SCRIPT_DEBUG.spec.ts | 6 +- .../client/lib}/commands/SCRIPT_DEBUG.ts | 0 .../lib}/commands/SCRIPT_EXISTS.spec.ts | 6 +- .../client/lib}/commands/SCRIPT_EXISTS.ts | 4 +- .../client/lib}/commands/SCRIPT_FLUSH.spec.ts | 6 +- .../client/lib}/commands/SCRIPT_FLUSH.ts | 0 .../client/lib}/commands/SCRIPT_KILL.spec.ts | 0 .../client/lib}/commands/SCRIPT_KILL.ts | 0 .../client/lib}/commands/SCRIPT_LOAD.spec.ts | 6 +- .../client/lib}/commands/SCRIPT_LOAD.ts | 0 .../client/lib}/commands/SDIFF.spec.ts | 6 +- .../client/lib}/commands/SDIFF.ts | 0 .../client/lib}/commands/SDIFFSTORE.spec.ts | 6 +- .../client/lib}/commands/SDIFFSTORE.ts | 0 .../client/lib}/commands/SET.spec.ts | 13 +- {lib => packages/client/lib}/commands/SET.ts | 52 +- .../client/lib}/commands/SETBIT.spec.ts | 10 +- .../client/lib}/commands/SETBIT.ts | 0 .../client/lib}/commands/SETEX.spec.ts | 10 +- .../client/lib}/commands/SETEX.ts | 0 .../client/lib}/commands/SETNX .spec.ts | 10 +- .../client/lib}/commands/SETNX.ts | 4 +- .../client/lib}/commands/SETRANGE.spec.ts | 10 +- .../client/lib}/commands/SETRANGE.ts | 0 .../client/lib}/commands/SHUTDOWN.spec.ts | 0 .../client/lib}/commands/SHUTDOWN.ts | 0 .../client/lib}/commands/SINTER.spec.ts | 6 +- .../client/lib}/commands/SINTER.ts | 0 .../client/lib}/commands/SINTERSTORE.spec.ts | 6 +- .../client/lib}/commands/SINTERSTORE.ts | 0 .../client/lib}/commands/SISMEMBER.spec.ts | 8 +- .../client/lib}/commands/SISMEMBER.ts | 4 +- .../client/lib}/commands/SMEMBERS.spec.ts | 6 +- .../client/lib}/commands/SMEMBERS.ts | 0 .../client/lib}/commands/SMISMEMBER.spec.ts | 8 +- .../client/lib}/commands/SMISMEMBER.ts | 4 +- .../client/lib}/commands/SMOVE.spec.ts | 6 +- .../client/lib}/commands/SMOVE.ts | 4 +- .../client/lib}/commands/SORT.spec.ts | 8 +- {lib => packages/client/lib}/commands/SORT.ts | 0 .../client/lib}/commands/SPOP.spec.ts | 6 +- {lib => packages/client/lib}/commands/SPOP.ts | 0 .../client/lib}/commands/SRANDMEMBER.spec.ts | 6 +- .../client/lib}/commands/SRANDMEMBER.ts | 0 .../lib}/commands/SRANDMEMBER_COUNT.spec.ts | 6 +- .../client/lib}/commands/SRANDMEMBER_COUNT.ts | 0 .../client/lib}/commands/SREM.spec.ts | 6 +- {lib => packages/client/lib}/commands/SREM.ts | 0 .../client/lib}/commands/SSCAN.spec.ts | 6 +- .../client/lib}/commands/SSCAN.ts | 0 .../client/lib}/commands/STRLEN.spec.ts | 10 +- .../client/lib}/commands/STRLEN.ts | 0 .../client/lib}/commands/SUNION.spec.ts | 6 +- .../client/lib}/commands/SUNION.ts | 0 .../client/lib}/commands/SUNIONSTORE.spec.ts | 6 +- .../client/lib}/commands/SUNIONSTORE.ts | 0 .../client/lib}/commands/SWAPDB.spec.ts | 6 +- .../client/lib}/commands/SWAPDB.ts | 0 .../client/lib}/commands/TIME.spec.ts | 6 +- {lib => packages/client/lib}/commands/TIME.ts | 0 .../client/lib}/commands/TOUCH.spec.ts | 6 +- .../client/lib}/commands/TOUCH.ts | 0 .../client/lib}/commands/TTL.spec.ts | 6 +- {lib => packages/client/lib}/commands/TTL.ts | 0 .../client/lib}/commands/TYPE.spec.ts | 6 +- {lib => packages/client/lib}/commands/TYPE.ts | 0 .../client/lib}/commands/UNLINK.spec.ts | 6 +- .../client/lib}/commands/UNLINK.ts | 0 .../client/lib}/commands/UNWATCH.spec.ts | 6 +- .../client/lib}/commands/UNWATCH.ts | 0 .../client/lib}/commands/WAIT.spec.ts | 6 +- {lib => packages/client/lib}/commands/WAIT.ts | 0 .../client/lib}/commands/WATCH.spec.ts | 0 .../client/lib}/commands/WATCH.ts | 0 .../client/lib}/commands/XACK.spec.ts | 6 +- {lib => packages/client/lib}/commands/XACK.ts | 0 .../client/lib}/commands/XADD.spec.ts | 8 +- {lib => packages/client/lib}/commands/XADD.ts | 0 .../client/lib}/commands/XAUTOCLAIM.spec.ts | 10 +- .../client/lib}/commands/XAUTOCLAIM.ts | 0 .../lib}/commands/XAUTOCLAIM_JUSTID.spec.ts | 8 +- .../client/lib}/commands/XAUTOCLAIM_JUSTID.ts | 0 .../client/lib}/commands/XCLAIM.spec.ts | 8 +- .../client/lib}/commands/XCLAIM.ts | 4 +- .../lib}/commands/XCLAIM_JUSTID.spec.ts | 8 +- .../client/lib}/commands/XCLAIM_JUSTID.ts | 0 .../client/lib}/commands/XDEL.spec.ts | 6 +- {lib => packages/client/lib}/commands/XDEL.ts | 0 .../lib}/commands/XGROUP_CREATE.spec.ts | 6 +- .../client/lib}/commands/XGROUP_CREATE.ts | 0 .../commands/XGROUP_CREATECONSUMER.spec.ts | 10 +- .../lib}/commands/XGROUP_CREATECONSUMER.ts | 4 +- .../lib}/commands/XGROUP_DELCONSUMER.spec.ts | 8 +- .../lib}/commands/XGROUP_DELCONSUMER.ts | 0 .../lib}/commands/XGROUP_DESTROY.spec.ts | 8 +- .../client/lib}/commands/XGROUP_DESTROY.ts | 4 +- .../client/lib}/commands/XGROUP_SETID.spec.ts | 6 +- .../client/lib}/commands/XGROUP_SETID.ts | 0 .../lib}/commands/XINFO_CONSUMERS.spec.ts | 8 +- .../client/lib}/commands/XINFO_CONSUMERS.ts | 0 .../client/lib}/commands/XINFO_GROUPS.spec.ts | 8 +- .../client/lib}/commands/XINFO_GROUPS.ts | 0 .../client/lib}/commands/XINFO_STREAM.spec.ts | 6 +- .../client/lib}/commands/XINFO_STREAM.ts | 0 .../client/lib}/commands/XLEN.spec.ts | 6 +- {lib => packages/client/lib}/commands/XLEN.ts | 0 .../client/lib}/commands/XPENDING.spec.ts | 6 +- .../client/lib}/commands/XPENDING.ts | 0 .../lib}/commands/XPENDING_RANGE.spec.ts | 10 +- .../client/lib}/commands/XPENDING_RANGE.ts | 6 +- .../client/lib}/commands/XRANGE.spec.ts | 8 +- .../client/lib}/commands/XRANGE.ts | 4 +- .../client/lib}/commands/XREAD.spec.ts | 10 +- .../client/lib}/commands/XREAD.ts | 4 +- .../client/lib}/commands/XREADGROUP.spec.ts | 44 +- .../client/lib}/commands/XREADGROUP.ts | 4 +- .../client/lib}/commands/XREVRANGE.spec.ts | 8 +- .../client/lib}/commands/XREVRANGE.ts | 4 +- .../client/lib}/commands/XTRIM.spec.ts | 6 +- .../client/lib}/commands/XTRIM.ts | 0 .../client/lib}/commands/ZADD.spec.ts | 6 +- {lib => packages/client/lib}/commands/ZADD.ts | 6 +- .../client/lib}/commands/ZCARD.spec.ts | 6 +- .../client/lib}/commands/ZCARD.ts | 0 .../client/lib}/commands/ZCOUNT.spec.ts | 6 +- packages/client/lib/commands/ZCOUNT.ts | 16 + .../client/lib}/commands/ZDIFF.spec.ts | 8 +- .../client/lib}/commands/ZDIFF.ts | 0 .../client/lib}/commands/ZDIFFSTORE.spec.ts | 8 +- .../client/lib}/commands/ZDIFFSTORE.ts | 0 .../lib}/commands/ZDIFF_WITHSCORES.spec.ts | 8 +- .../client/lib}/commands/ZDIFF_WITHSCORES.ts | 3 +- .../client/lib}/commands/ZINCRBY.spec.ts | 6 +- .../client/lib}/commands/ZINCRBY.ts | 4 +- .../client/lib}/commands/ZINTER.spec.ts | 10 +- .../client/lib}/commands/ZINTER.ts | 0 .../client/lib}/commands/ZINTERSTORE.spec.ts | 6 +- .../client/lib}/commands/ZINTERSTORE.ts | 0 .../lib}/commands/ZINTER_WITHSCORES.spec.ts | 8 +- .../client/lib}/commands/ZINTER_WITHSCORES.ts | 3 +- .../client/lib}/commands/ZLEXCOUNT.spec.ts | 6 +- .../client/lib}/commands/ZLEXCOUNT.ts | 0 .../client/lib}/commands/ZMSCORE.spec.ts | 8 +- .../client/lib}/commands/ZMSCORE.ts | 4 +- .../client/lib}/commands/ZPOPMAX.spec.ts | 10 +- .../client/lib}/commands/ZPOPMAX.ts | 0 .../lib}/commands/ZPOPMAX_COUNT.spec.ts | 6 +- .../client/lib}/commands/ZPOPMAX_COUNT.ts | 3 +- .../client/lib}/commands/ZPOPMIN.spec.ts | 10 +- .../client/lib}/commands/ZPOPMIN.ts | 0 .../lib}/commands/ZPOPMIN_COUNT.spec.ts | 6 +- .../client/lib}/commands/ZPOPMIN_COUNT.ts | 3 +- .../client/lib}/commands/ZRANDMEMBER.spec.ts | 8 +- .../client/lib}/commands/ZRANDMEMBER.ts | 0 .../lib}/commands/ZRANDMEMBER_COUNT.spec.ts | 8 +- .../client/lib}/commands/ZRANDMEMBER_COUNT.ts | 0 .../ZRANDMEMBER_COUNT_WITHSCORES.spec.ts | 8 +- .../commands/ZRANDMEMBER_COUNT_WITHSCORES.ts | 3 +- .../client/lib}/commands/ZRANGE.spec.ts | 13 +- .../client/lib}/commands/ZRANGE.ts | 6 +- .../client/lib}/commands/ZRANGEBYLEX.spec.ts | 6 +- .../client/lib}/commands/ZRANGEBYLEX.ts | 6 +- .../lib}/commands/ZRANGEBYSCORE.spec.ts | 6 +- .../client/lib}/commands/ZRANGEBYSCORE.ts | 6 +- .../commands/ZRANGEBYSCORE_WITHSCORES.spec.ts | 6 +- .../lib}/commands/ZRANGEBYSCORE_WITHSCORES.ts | 3 +- .../client/lib}/commands/ZRANGESTORE.spec.ts | 9 +- .../client/lib}/commands/ZRANGESTORE.ts | 14 +- .../lib}/commands/ZRANGE_WITHSCORES.spec.ts | 6 +- .../client/lib}/commands/ZRANGE_WITHSCORES.ts | 3 +- .../client/lib}/commands/ZRANK.spec.ts | 6 +- .../client/lib}/commands/ZRANK.ts | 0 .../client/lib}/commands/ZREM.spec.ts | 6 +- {lib => packages/client/lib}/commands/ZREM.ts | 0 .../lib}/commands/ZREMRANGEBYLEX.spec.ts | 6 +- .../client/lib}/commands/ZREMRANGEBYLEX.ts | 0 .../lib}/commands/ZREMRANGEBYRANK.spec.ts | 6 +- .../client/lib}/commands/ZREMRANGEBYRANK.ts | 0 .../lib}/commands/ZREMRANGEBYSCORE.spec.ts | 6 +- .../client/lib}/commands/ZREMRANGEBYSCORE.ts | 0 .../client/lib}/commands/ZREVRANK.spec.ts | 6 +- .../client/lib}/commands/ZREVRANK.ts | 0 .../client/lib}/commands/ZSCAN.spec.ts | 6 +- .../client/lib}/commands/ZSCAN.ts | 0 .../client/lib}/commands/ZSCORE.spec.ts | 6 +- .../client/lib}/commands/ZSCORE.ts | 4 +- .../client/lib}/commands/ZUNION.spec.ts | 8 +- .../client/lib}/commands/ZUNION.ts | 0 .../client/lib}/commands/ZUNIONSTORE.spec.ts | 6 +- .../client/lib}/commands/ZUNIONSTORE.ts | 0 .../lib}/commands/ZUNION_WITHSCORES.spec.ts | 8 +- .../client/lib}/commands/ZUNION_WITHSCORES.ts | 3 +- .../commands/generic-transformers.spec.ts | 19 +- .../lib}/commands/generic-transformers.ts | 6 + .../client/lib}/commands/index.ts | 0 {lib => packages/client/lib}/errors.ts | 12 + {lib => packages/client/lib}/lua-script.ts | 0 .../client/lib}/multi-command.spec.ts | 0 {lib => packages/client/lib}/multi-command.ts | 0 packages/client/lib/test-utils.ts | 49 + .../ts-declarations/cluster-key-slot.d.ts | 0 {lib => packages/client/lib}/utils.ts | 0 packages/client/package.json | 50 + packages/client/tsconfig.json | 27 + packages/json/.npmignore | 6 + .nycrc.json => packages/json/.nycrc.json | 2 +- packages/json/.release-it.json | 10 + packages/json/README.md | 2 + packages/json/lib/commands/ARRAPPEND.spec.ts | 30 + packages/json/lib/commands/ARRAPPEND.ts | 15 + packages/json/lib/commands/ARRINDEX.spec.ts | 37 + packages/json/lib/commands/ARRINDEX.ts | 21 + packages/json/lib/commands/ARRINSERT.spec.ts | 30 + packages/json/lib/commands/ARRINSERT.ts | 15 + packages/json/lib/commands/ARRLEN.spec.ts | 30 + packages/json/lib/commands/ARRLEN.ts | 15 + packages/json/lib/commands/ARRPOP.spec.ts | 37 + packages/json/lib/commands/ARRPOP.ts | 17 + packages/json/lib/commands/ARRTRIM.spec.ts | 21 + packages/json/lib/commands/ARRTRIM.ts | 7 + .../json/lib/commands/DEBUG_MEMORY.spec.ts | 28 + packages/json/lib/commands/DEBUG_MEMORY.ts | 13 + packages/json/lib/commands/DEL.spec.ts | 28 + packages/json/lib/commands/DEL.ts | 13 + packages/json/lib/commands/FORGET.spec.ts | 28 + packages/json/lib/commands/FORGET.ts | 13 + packages/json/lib/commands/GET.spec.ts | 78 + packages/json/lib/commands/GET.ts | 41 + packages/json/lib/commands/MGET.spec.ts | 19 + packages/json/lib/commands/MGET.ts | 15 + packages/json/lib/commands/NUMINCRBY.spec.ts | 21 + packages/json/lib/commands/NUMINCRBY.ts | 7 + packages/json/lib/commands/NUMMULTBY.spec.ts | 21 + packages/json/lib/commands/NUMMULTBY.ts | 7 + packages/json/lib/commands/OBJKEYS.spec.ts | 28 + packages/json/lib/commands/OBJKEYS.ts | 13 + packages/json/lib/commands/OBJLEN.spec.ts | 28 + packages/json/lib/commands/OBJLEN.ts | 13 + packages/json/lib/commands/RESP.spec.ts | 28 + packages/json/lib/commands/RESP.ts | 15 + packages/json/lib/commands/SET.spec.ts | 35 + packages/json/lib/commands/SET.ts | 25 + packages/json/lib/commands/STRAPPEND.spec.ts | 30 + packages/json/lib/commands/STRAPPEND.ts | 21 + packages/json/lib/commands/STRLEN.spec.ts | 30 + packages/json/lib/commands/STRLEN.ts | 15 + packages/json/lib/commands/TYPE.spec.ts | 28 + packages/json/lib/commands/TYPE.ts | 13 + packages/json/lib/commands/index.ts | 94 + packages/json/lib/index.ts | 1 + packages/json/lib/test-utils.ts | 21 + packages/json/package.json | 24 + packages/json/tsconfig.json | 9 + packages/search/.npmignore | 6 + packages/search/.nycrc.json | 4 + packages/search/.release-it.json | 10 + packages/search/README.md | 2 + .../search/lib/commands/AGGREGATE.spec.ts | 482 +++ packages/search/lib/commands/AGGREGATE.ts | 283 ++ packages/search/lib/commands/ALIASADD.spec.ts | 11 + packages/search/lib/commands/ALIASADD.ts | 5 + packages/search/lib/commands/ALIASDEL.spec.ts | 11 + packages/search/lib/commands/ALIASDEL.ts | 5 + .../search/lib/commands/ALIASUPDATE.spec.ts | 11 + packages/search/lib/commands/ALIASUPDATE.ts | 5 + .../search/lib/commands/CONFIG_GET.spec.ts | 25 + packages/search/lib/commands/CONFIG_GET.ts | 16 + .../search/lib/commands/CONFIG_SET.spec.ts | 12 + packages/search/lib/commands/CONFIG_SET.ts | 5 + packages/search/lib/commands/CREATE.spec.ts | 350 ++ packages/search/lib/commands/CREATE.ts | 194 + packages/search/lib/commands/DICTADD.spec.ts | 28 + packages/search/lib/commands/DICTADD.ts | 8 + packages/search/lib/commands/DICTDEL.spec.ts | 28 + packages/search/lib/commands/DICTDEL.ts | 8 + packages/search/lib/commands/DICTDUMP.spec.ts | 21 + packages/search/lib/commands/DICTDUMP.ts | 5 + .../search/lib/commands/DROPINDEX.spec.ts | 33 + packages/search/lib/commands/DROPINDEX.ts | 15 + packages/search/lib/commands/EXPLAIN.spec.ts | 11 + packages/search/lib/commands/EXPLAIN.ts | 7 + .../search/lib/commands/EXPLAINCLI.spec.ts | 11 + packages/search/lib/commands/EXPLAINCLI.ts | 7 + packages/search/lib/commands/INFO.spec.ts | 65 + packages/search/lib/commands/INFO.ts | 171 + packages/search/lib/commands/PROFILE.ts | 26 + packages/search/lib/commands/SEARCH.spec.ts | 244 ++ packages/search/lib/commands/SEARCH.ts | 200 + .../search/lib/commands/SPELLCHECK.spec.ts | 71 + packages/search/lib/commands/SPELLCHECK.ts | 57 + packages/search/lib/commands/SUGADD.spec.ts | 35 + packages/search/lib/commands/SUGADD.ts | 20 + packages/search/lib/commands/SUGDEL.spec.ts | 19 + packages/search/lib/commands/SUGDEL.ts | 5 + packages/search/lib/commands/SUGGET.spec.ts | 46 + packages/search/lib/commands/SUGGET.ts | 22 + .../lib/commands/SUGGET_WITHPAYLOADS.spec.ts | 33 + .../lib/commands/SUGGET_WITHPAYLOADS.ts | 29 + .../lib/commands/SUGGET_WITHSCORES.spec.ts | 33 + .../search/lib/commands/SUGGET_WITHSCORES.ts | 29 + .../SUGGET_WITHSCORES_WITHPAYLOADS.spec.ts | 34 + .../SUGGET_WITHSCORES_WITHPAYLOADS.ts | 30 + packages/search/lib/commands/SUGLEN.spec.ts | 19 + packages/search/lib/commands/SUGLEN.ts | 7 + packages/search/lib/commands/SYNDUMP.spec.ts | 23 + packages/search/lib/commands/SYNDUMP.ts | 5 + .../search/lib/commands/SYNUPDATE.spec.ts | 39 + packages/search/lib/commands/SYNUPDATE.ts | 23 + packages/search/lib/commands/TAGVALS.spec.ts | 24 + packages/search/lib/commands/TAGVALS.ts | 5 + packages/search/lib/commands/_LIST.spec.ts | 19 + packages/search/lib/commands/_LIST.ts | 5 + packages/search/lib/commands/index.spec.ts | 46 + packages/search/lib/commands/index.ts | 161 + packages/search/lib/index.ts | 4 + packages/search/lib/test-utils.ts | 21 + packages/search/package.json | 24 + packages/search/tsconfig.json | 9 + packages/test-utils/docker/Dockerfile | 9 + packages/test-utils/docker/entrypoint.sh | 7 + packages/test-utils/lib/dockers.ts | 222 ++ packages/test-utils/lib/index.ts | 181 + packages/test-utils/package.json | 26 + packages/test-utils/tsconfig.json | 9 + tsconfig.base.json | 15 + tsconfig.json | 32 +- 705 files changed, 9977 insertions(+), 4367 deletions(-) create mode 100644 .github/README.md create mode 100644 .github/release-drafter-config.yml delete mode 100644 .github/workflows/benchmark.yml create mode 100644 .github/workflows/release-drafter.yml create mode 100644 .release-it.json delete mode 100644 benchmark/.gitignore delete mode 100644 benchmark/index.js delete mode 100644 benchmark/package-lock.json delete mode 100644 benchmark/package.json create mode 100644 docs/clustering.md create mode 100644 examples/.gitignore create mode 100644 examples/README.md create mode 100644 examples/blocking-list-pop.js create mode 100644 examples/command-with-modifiers.js create mode 100644 examples/connect-as-acl-user.js create mode 100644 examples/lua-multi-incr.js create mode 100644 examples/package.json create mode 100644 examples/search+json.js create mode 100644 examples/set-scan.js delete mode 100644 lib/cluster/index.spec.ts delete mode 100644 lib/commands/ACL_GETUSER.spec.ts delete mode 100644 lib/commands/COMMAND.spec.ts delete mode 100644 lib/commands/COMMAND_INFO.spec.ts delete mode 100644 lib/commands/CONFIG_GET.ts delete mode 100644 lib/commands/ZCOUNT.ts delete mode 100644 lib/test-utils.ts delete mode 100644 lib/ts-declarations/redis-parser.d.ts create mode 100644 packages/client/.eslintrc.json create mode 100644 packages/client/.gitignore create mode 100644 packages/client/.npmignore create mode 100644 packages/client/.nycrc.json create mode 100644 packages/client/.release-it.json rename CHANGELOG.md => packages/client/CHANGELOG.md (98%) create mode 100644 packages/client/README.md create mode 100644 packages/client/index.ts rename {lib => packages/client/lib}/client/commands-queue.ts (92%) rename {lib => packages/client/lib}/client/commands.ts (99%) rename {lib => packages/client/lib}/client/index.spec.ts (51%) rename {lib => packages/client/lib}/client/index.ts (80%) rename {lib => packages/client/lib}/client/multi-command.ts (89%) rename {lib => packages/client/lib}/client/socket.spec.ts (98%) rename {lib => packages/client/lib}/client/socket.ts (76%) rename {lib => packages/client/lib}/cluster/cluster-slots.ts (94%) rename {lib => packages/client/lib}/cluster/commands.ts (100%) create mode 100644 packages/client/lib/cluster/index.spec.ts rename {lib => packages/client/lib}/cluster/index.ts (91%) rename {lib => packages/client/lib}/cluster/multi-command.ts (94%) rename {lib => packages/client/lib}/command-options.ts (100%) rename {lib => packages/client/lib}/commander.spec.ts (100%) rename {lib => packages/client/lib}/commander.ts (88%) rename {lib => packages/client/lib}/commands/ACL_CAT.spec.ts (82%) rename {lib => packages/client/lib}/commands/ACL_CAT.ts (100%) rename {lib => packages/client/lib}/commands/ACL_DELUSER.spec.ts (73%) rename {lib => packages/client/lib}/commands/ACL_DELUSER.ts (100%) rename {lib => packages/client/lib}/commands/ACL_GENPASS.spec.ts (82%) rename {lib => packages/client/lib}/commands/ACL_GENPASS.ts (100%) create mode 100644 packages/client/lib/commands/ACL_GETUSER.spec.ts rename {lib => packages/client/lib}/commands/ACL_GETUSER.ts (100%) rename {lib => packages/client/lib}/commands/ACL_LIST.spec.ts (70%) rename {lib => packages/client/lib}/commands/ACL_LIST.ts (100%) rename {lib => packages/client/lib}/commands/ACL_LOAD.spec.ts (70%) rename {lib => packages/client/lib}/commands/ACL_LOAD.ts (100%) rename {lib => packages/client/lib}/commands/ACL_LOG.spec.ts (93%) rename {lib => packages/client/lib}/commands/ACL_LOG.ts (100%) rename {lib => packages/client/lib}/commands/ACL_LOG_RESET.spec.ts (72%) rename {lib => packages/client/lib}/commands/ACL_LOG_RESET.ts (100%) rename {lib => packages/client/lib}/commands/ACL_SAVE.spec.ts (70%) rename {lib => packages/client/lib}/commands/ACL_SAVE.ts (100%) rename {lib => packages/client/lib}/commands/ACL_SETUSER.spec.ts (84%) rename {lib => packages/client/lib}/commands/ACL_SETUSER.ts (100%) rename {lib => packages/client/lib}/commands/ACL_USERS.spec.ts (71%) rename {lib => packages/client/lib}/commands/ACL_USERS.ts (100%) rename {lib => packages/client/lib}/commands/ACL_WHOAMI.spec.ts (71%) rename {lib => packages/client/lib}/commands/ACL_WHOAMI.ts (100%) rename {lib => packages/client/lib}/commands/APPEND.spec.ts (90%) rename {lib => packages/client/lib}/commands/APPEND.ts (100%) rename {lib => packages/client/lib}/commands/ASKING.spec.ts (100%) rename {lib => packages/client/lib}/commands/ASKING.ts (100%) rename {lib => packages/client/lib}/commands/AUTH.spec.ts (100%) rename {lib => packages/client/lib}/commands/AUTH.ts (100%) rename {lib => packages/client/lib}/commands/BGREWRITEAOF.spec.ts (100%) rename {lib => packages/client/lib}/commands/BGREWRITEAOF.ts (100%) rename {lib => packages/client/lib}/commands/BGSAVE.spec.ts (100%) rename {lib => packages/client/lib}/commands/BGSAVE.ts (100%) rename {lib => packages/client/lib}/commands/BITCOUNT.spec.ts (82%) rename {lib => packages/client/lib}/commands/BITCOUNT.ts (100%) rename {lib => packages/client/lib}/commands/BITFIELD.spec.ts (88%) rename {lib => packages/client/lib}/commands/BITFIELD.ts (100%) rename {lib => packages/client/lib}/commands/BITOP.spec.ts (75%) rename {lib => packages/client/lib}/commands/BITOP.ts (100%) rename {lib => packages/client/lib}/commands/BITPOS.spec.ts (77%) rename {lib => packages/client/lib}/commands/BITPOS.ts (100%) rename {lib => packages/client/lib}/commands/BLMOVE.spec.ts (75%) rename {lib => packages/client/lib}/commands/BLMOVE.ts (100%) rename {lib => packages/client/lib}/commands/BLPOP.spec.ts (87%) rename {lib => packages/client/lib}/commands/BLPOP.ts (100%) rename {lib => packages/client/lib}/commands/BRPOP.spec.ts (87%) rename {lib => packages/client/lib}/commands/BRPOP.ts (100%) rename {lib => packages/client/lib}/commands/BRPOPLPUSH.spec.ts (80%) rename {lib => packages/client/lib}/commands/BRPOPLPUSH.ts (100%) rename {lib => packages/client/lib}/commands/BZPOPMAX.spec.ts (91%) rename {lib => packages/client/lib}/commands/BZPOPMAX.ts (100%) rename {lib => packages/client/lib}/commands/BZPOPMIN.spec.ts (91%) rename {lib => packages/client/lib}/commands/BZPOPMIN.ts (100%) rename {lib => packages/client/lib}/commands/CLIENT_ID.spec.ts (71%) rename {lib => packages/client/lib}/commands/CLIENT_ID.ts (100%) rename {lib => packages/client/lib}/commands/CLIENT_INFO.spec.ts (100%) rename {lib => packages/client/lib}/commands/CLIENT_INFO.ts (100%) rename {lib => packages/client/lib}/commands/CLUSTER_ADDSLOTS.spec.ts (100%) rename {lib => packages/client/lib}/commands/CLUSTER_ADDSLOTS.ts (100%) rename {lib => packages/client/lib}/commands/CLUSTER_FLUSHSLOTS.spec.ts (100%) rename {lib => packages/client/lib}/commands/CLUSTER_FLUSHSLOTS.ts (100%) rename {lib => packages/client/lib}/commands/CLUSTER_GETKEYSINSLOT.spec.ts (100%) rename {lib => packages/client/lib}/commands/CLUSTER_GETKEYSINSLOT.ts (100%) rename {lib => packages/client/lib}/commands/CLUSTER_INFO.spec.ts (100%) rename {lib => packages/client/lib}/commands/CLUSTER_INFO.ts (100%) rename {lib => packages/client/lib}/commands/CLUSTER_MEET.spec.ts (100%) rename {lib => packages/client/lib}/commands/CLUSTER_MEET.ts (100%) rename {lib => packages/client/lib}/commands/CLUSTER_NODES.spec.ts (100%) rename {lib => packages/client/lib}/commands/CLUSTER_NODES.ts (100%) rename {lib => packages/client/lib}/commands/CLUSTER_RESET.spec.ts (100%) rename {lib => packages/client/lib}/commands/CLUSTER_RESET.ts (100%) rename {lib => packages/client/lib}/commands/CLUSTER_SETSLOT.spec.ts (100%) rename {lib => packages/client/lib}/commands/CLUSTER_SETSLOT.ts (100%) rename {lib => packages/client/lib}/commands/CLUSTER_SLOTS.spec.ts (99%) rename {lib => packages/client/lib}/commands/CLUSTER_SLOTS.ts (100%) create mode 100644 packages/client/lib/commands/COMMAND.spec.ts rename {lib => packages/client/lib}/commands/COMMAND.ts (100%) rename {lib => packages/client/lib}/commands/COMMAND_COUNT.spec.ts (71%) rename {lib => packages/client/lib}/commands/COMMAND_COUNT.ts (77%) rename {lib => packages/client/lib}/commands/COMMAND_GETKEYS.spec.ts (73%) rename {lib => packages/client/lib}/commands/COMMAND_GETKEYS.ts (78%) create mode 100644 packages/client/lib/commands/COMMAND_INFO.spec.ts rename {lib => packages/client/lib}/commands/COMMAND_INFO.ts (100%) rename {lib => packages/client/lib}/commands/CONFIG_GET.spec.ts (100%) create mode 100644 packages/client/lib/commands/CONFIG_GET.ts rename {lib => packages/client/lib}/commands/CONFIG_RESETSTAT.spec.ts (100%) rename {lib => packages/client/lib}/commands/CONFIG_RESETSTAT.ts (100%) rename {lib => packages/client/lib}/commands/CONFIG_REWRITE.spec.ts (100%) rename {lib => packages/client/lib}/commands/CONFIG_REWRITE.ts (100%) rename {lib => packages/client/lib}/commands/CONFIG_SET.spec.ts (100%) rename {lib => packages/client/lib}/commands/CONFIG_SET.ts (100%) rename {lib => packages/client/lib}/commands/COPY.spec.ts (88%) rename {lib => packages/client/lib}/commands/COPY.ts (80%) rename {lib => packages/client/lib}/commands/DBSIZE.spec.ts (70%) rename {lib => packages/client/lib}/commands/DBSIZE.ts (100%) rename {lib => packages/client/lib}/commands/DECR.spec.ts (70%) rename {lib => packages/client/lib}/commands/DECR.ts (100%) rename {lib => packages/client/lib}/commands/DECRBY.spec.ts (71%) rename {lib => packages/client/lib}/commands/DECRBY.ts (100%) rename {lib => packages/client/lib}/commands/DEL.spec.ts (80%) rename {lib => packages/client/lib}/commands/DEL.ts (100%) rename {lib => packages/client/lib}/commands/DISCARD.spec.ts (100%) rename {lib => packages/client/lib}/commands/DISCARD.ts (100%) rename {lib => packages/client/lib}/commands/DUMP.spec.ts (52%) rename {lib => packages/client/lib}/commands/DUMP.ts (100%) rename {lib => packages/client/lib}/commands/ECHO.spec.ts (72%) rename {lib => packages/client/lib}/commands/ECHO.ts (100%) rename {lib => packages/client/lib}/commands/EVAL.spec.ts (69%) rename {lib => packages/client/lib}/commands/EVAL.ts (100%) rename {lib => packages/client/lib}/commands/EVALSHA.spec.ts (100%) rename {lib => packages/client/lib}/commands/EVALSHA.ts (100%) rename {lib => packages/client/lib}/commands/EXISTS.spec.ts (80%) rename {lib => packages/client/lib}/commands/EXISTS.ts (64%) rename {lib => packages/client/lib}/commands/EXPIRE.spec.ts (71%) rename {lib => packages/client/lib}/commands/EXPIRE.ts (52%) rename {lib => packages/client/lib}/commands/EXPIREAT.spec.ts (81%) rename {lib => packages/client/lib}/commands/EXPIREAT.ts (56%) rename {lib => packages/client/lib}/commands/FAILOVER.spec.ts (100%) rename {lib => packages/client/lib}/commands/FAILOVER.ts (100%) rename {lib => packages/client/lib}/commands/FLUSHALL.spec.ts (84%) rename {lib => packages/client/lib}/commands/FLUSHALL.ts (100%) rename {lib => packages/client/lib}/commands/FLUSHDB.spec.ts (84%) rename {lib => packages/client/lib}/commands/FLUSHDB.ts (100%) rename {lib => packages/client/lib}/commands/GEOADD.spec.ts (90%) rename {lib => packages/client/lib}/commands/GEOADD.ts (100%) rename {lib => packages/client/lib}/commands/GEODIST.spec.ts (78%) rename {lib => packages/client/lib}/commands/GEODIST.ts (100%) rename {lib => packages/client/lib}/commands/GEOHASH.spec.ts (74%) rename {lib => packages/client/lib}/commands/GEOHASH.ts (100%) rename {lib => packages/client/lib}/commands/GEOPOS.spec.ts (82%) rename {lib => packages/client/lib}/commands/GEOPOS.ts (100%) rename {lib => packages/client/lib}/commands/GEOSEARCH.spec.ts (68%) rename {lib => packages/client/lib}/commands/GEOSEARCH.ts (100%) rename {lib => packages/client/lib}/commands/GEOSEARCHSTORE.spec.ts (85%) rename {lib => packages/client/lib}/commands/GEOSEARCHSTORE.ts (100%) rename {lib => packages/client/lib}/commands/GEOSEARCH_WITH.spec.ts (74%) rename {lib => packages/client/lib}/commands/GEOSEARCH_WITH.ts (82%) rename {lib => packages/client/lib}/commands/GET.spec.ts (62%) rename {lib => packages/client/lib}/commands/GET.ts (100%) rename {lib => packages/client/lib}/commands/GETBIT.spec.ts (63%) rename {lib => packages/client/lib}/commands/GETBIT.ts (100%) rename {lib => packages/client/lib}/commands/GETDEL.spec.ts (56%) rename {lib => packages/client/lib}/commands/GETDEL.ts (100%) rename {lib => packages/client/lib}/commands/GETEX.spec.ts (87%) rename {lib => packages/client/lib}/commands/GETEX.ts (100%) rename {lib => packages/client/lib}/commands/GETRANGE.spec.ts (64%) rename {lib => packages/client/lib}/commands/GETRANGE.ts (100%) rename {lib => packages/client/lib}/commands/GETSET.spec.ts (64%) rename {lib => packages/client/lib}/commands/GETSET.ts (100%) rename {lib => packages/client/lib}/commands/GET_BUFFER.spec.ts (62%) rename {lib => packages/client/lib}/commands/GET_BUFFER.ts (100%) rename {lib => packages/client/lib}/commands/HDEL.spec.ts (81%) rename {lib => packages/client/lib}/commands/HDEL.ts (100%) rename {lib => packages/client/lib}/commands/HELLO.spec.ts (73%) rename {lib => packages/client/lib}/commands/HELLO.ts (100%) rename {lib => packages/client/lib}/commands/HEXISTS.spec.ts (72%) rename {lib => packages/client/lib}/commands/HEXISTS.ts (56%) rename {lib => packages/client/lib}/commands/HGET.spec.ts (72%) rename {lib => packages/client/lib}/commands/HGET.ts (100%) rename {lib => packages/client/lib}/commands/HGETALL.spec.ts (88%) rename {lib => packages/client/lib}/commands/HGETALL.ts (53%) rename {lib => packages/client/lib}/commands/HINCRBY.spec.ts (73%) rename {lib => packages/client/lib}/commands/HINCRBY.ts (100%) rename {lib => packages/client/lib}/commands/HINCRBYFLOAT.spec.ts (73%) rename {lib => packages/client/lib}/commands/HINCRBYFLOAT.ts (100%) rename {lib => packages/client/lib}/commands/HKEYS.spec.ts (71%) rename {lib => packages/client/lib}/commands/HKEYS.ts (100%) rename {lib => packages/client/lib}/commands/HLEN.spec.ts (70%) rename {lib => packages/client/lib}/commands/HLEN.ts (100%) rename {lib => packages/client/lib}/commands/HMGET.spec.ts (81%) rename {lib => packages/client/lib}/commands/HMGET.ts (100%) rename {lib => packages/client/lib}/commands/HRANDFIELD.spec.ts (62%) rename {lib => packages/client/lib}/commands/HRANDFIELD.ts (100%) rename {lib => packages/client/lib}/commands/HRANDFIELD_COUNT.spec.ts (63%) rename {lib => packages/client/lib}/commands/HRANDFIELD_COUNT.ts (100%) rename {lib => packages/client/lib}/commands/HRANDFIELD_COUNT_WITHVALUES.spec.ts (65%) rename {lib => packages/client/lib}/commands/HRANDFIELD_COUNT_WITHVALUES.ts (74%) rename {lib => packages/client/lib}/commands/HSCAN.spec.ts (93%) rename {lib => packages/client/lib}/commands/HSCAN.ts (100%) rename {lib => packages/client/lib}/commands/HSET.spec.ts (81%) rename {lib => packages/client/lib}/commands/HSET.ts (92%) rename {lib => packages/client/lib}/commands/HSETNX.spec.ts (73%) rename {lib => packages/client/lib}/commands/HSETNX.ts (59%) rename {lib => packages/client/lib}/commands/HSTRLEN.spec.ts (72%) rename {lib => packages/client/lib}/commands/HSTRLEN.ts (100%) rename {lib => packages/client/lib}/commands/HVALS.spec.ts (71%) rename {lib => packages/client/lib}/commands/HVALS.ts (100%) rename {lib => packages/client/lib}/commands/INCR.spec.ts (70%) rename {lib => packages/client/lib}/commands/INCR.ts (100%) rename {lib => packages/client/lib}/commands/INCRBY.spec.ts (71%) rename {lib => packages/client/lib}/commands/INCRBY.ts (100%) rename {lib => packages/client/lib}/commands/INCRBYFLOAT.spec.ts (72%) rename {lib => packages/client/lib}/commands/INCRBYFLOAT.ts (100%) rename {lib => packages/client/lib}/commands/INFO.spec.ts (100%) rename {lib => packages/client/lib}/commands/INFO.ts (100%) rename {lib => packages/client/lib}/commands/KEYS.spec.ts (53%) rename {lib => packages/client/lib}/commands/KEYS.ts (100%) rename {lib => packages/client/lib}/commands/LASTSAVE.spec.ts (68%) rename {lib => packages/client/lib}/commands/LASTSAVE.ts (100%) rename {lib => packages/client/lib}/commands/LINDEX.spec.ts (65%) rename {lib => packages/client/lib}/commands/LINDEX.ts (77%) rename {lib => packages/client/lib}/commands/LINSERT.spec.ts (68%) rename {lib => packages/client/lib}/commands/LINSERT.ts (100%) rename {lib => packages/client/lib}/commands/LLEN.spec.ts (62%) rename {lib => packages/client/lib}/commands/LLEN.ts (100%) rename {lib => packages/client/lib}/commands/LMOVE.spec.ts (63%) rename {lib => packages/client/lib}/commands/LMOVE.ts (100%) rename {lib => packages/client/lib}/commands/LOLWUT.spec.ts (84%) rename {lib => packages/client/lib}/commands/LOLWUT.ts (100%) rename {lib => packages/client/lib}/commands/LPOP.spec.ts (62%) rename {lib => packages/client/lib}/commands/LPOP.ts (100%) rename {lib => packages/client/lib}/commands/LPOP_COUNT.spec.ts (57%) rename {lib => packages/client/lib}/commands/LPOP_COUNT.ts (100%) rename {lib => packages/client/lib}/commands/LPOS.spec.ts (79%) rename {lib => packages/client/lib}/commands/LPOS.ts (100%) rename {lib => packages/client/lib}/commands/LPOS_COUNT.spec.ts (80%) rename {lib => packages/client/lib}/commands/LPOS_COUNT.ts (100%) rename {lib => packages/client/lib}/commands/LPUSH.spec.ts (73%) rename {lib => packages/client/lib}/commands/LPUSH.ts (100%) rename {lib => packages/client/lib}/commands/LPUSHX.spec.ts (73%) rename {lib => packages/client/lib}/commands/LPUSHX.ts (100%) rename {lib => packages/client/lib}/commands/LRANGE.spec.ts (64%) rename {lib => packages/client/lib}/commands/LRANGE.ts (100%) rename {lib => packages/client/lib}/commands/LREM.spec.ts (65%) rename {lib => packages/client/lib}/commands/LREM.ts (100%) rename {lib => packages/client/lib}/commands/LSET.spec.ts (69%) rename {lib => packages/client/lib}/commands/LSET.ts (100%) rename {lib => packages/client/lib}/commands/LTRIM.spec.ts (64%) rename {lib => packages/client/lib}/commands/LTRIM.ts (97%) rename {lib => packages/client/lib}/commands/MEMORY_DOCTOR.spec.ts (71%) rename {lib => packages/client/lib}/commands/MEMORY_DOCTOR.ts (100%) rename {lib => packages/client/lib}/commands/MEMORY_MALLOC-STATS.spec.ts (72%) rename {lib => packages/client/lib}/commands/MEMORY_MALLOC-STATS.ts (100%) rename {lib => packages/client/lib}/commands/MEMORY_PURGE.spec.ts (71%) rename {lib => packages/client/lib}/commands/MEMORY_PURGE.ts (100%) rename {lib => packages/client/lib}/commands/MEMORY_STATS.spec.ts (100%) rename {lib => packages/client/lib}/commands/MEMORY_STATS.ts (100%) rename {lib => packages/client/lib}/commands/MEMORY_USAGE.spec.ts (82%) rename {lib => packages/client/lib}/commands/MEMORY_USAGE.ts (100%) rename {lib => packages/client/lib}/commands/MGET.spec.ts (64%) rename {lib => packages/client/lib}/commands/MGET.ts (100%) rename {lib => packages/client/lib}/commands/MIGRATE.spec.ts (100%) rename {lib => packages/client/lib}/commands/MIGRATE.ts (100%) rename {lib => packages/client/lib}/commands/MODULE_LIST.spec.ts (100%) rename {lib => packages/client/lib}/commands/MODULE_LIST.ts (100%) rename {lib => packages/client/lib}/commands/MODULE_LOAD.spec.ts (100%) rename {lib => packages/client/lib}/commands/MODULE_LOAD.ts (100%) rename {lib => packages/client/lib}/commands/MODULE_UNLOAD.spec.ts (100%) rename {lib => packages/client/lib}/commands/MODULE_UNLOAD.ts (100%) rename {lib => packages/client/lib}/commands/MOVE.spec.ts (71%) rename {lib => packages/client/lib}/commands/MOVE.ts (50%) rename {lib => packages/client/lib}/commands/MSET.spec.ts (81%) rename {lib => packages/client/lib}/commands/MSET.ts (100%) rename {lib => packages/client/lib}/commands/MSETNX.spec.ts (81%) rename {lib => packages/client/lib}/commands/MSETNX.ts (77%) rename {lib => packages/client/lib}/commands/PERSIST.spec.ts (71%) rename {lib => packages/client/lib}/commands/PERSIST.ts (52%) rename {lib => packages/client/lib}/commands/PEXPIRE.spec.ts (71%) rename {lib => packages/client/lib}/commands/PEXPIRE.ts (60%) rename {lib => packages/client/lib}/commands/PEXPIREAT.spec.ts (81%) rename {lib => packages/client/lib}/commands/PEXPIREAT.ts (63%) rename {lib => packages/client/lib}/commands/PFADD.spec.ts (81%) rename {lib => packages/client/lib}/commands/PFADD.ts (62%) rename {lib => packages/client/lib}/commands/PFCOUNT.spec.ts (80%) rename {lib => packages/client/lib}/commands/PFCOUNT.ts (100%) rename {lib => packages/client/lib}/commands/PFMERGE.spec.ts (82%) rename {lib => packages/client/lib}/commands/PFMERGE.ts (100%) rename {lib => packages/client/lib}/commands/PING.spec.ts (52%) rename {lib => packages/client/lib}/commands/PING.ts (100%) rename {lib => packages/client/lib}/commands/PSETEX.spec.ts (65%) rename {lib => packages/client/lib}/commands/PSETEX.ts (100%) rename {lib => packages/client/lib}/commands/PTTL.spec.ts (70%) rename {lib => packages/client/lib}/commands/PTTL.ts (100%) rename {lib => packages/client/lib}/commands/PUBLISH.spec.ts (73%) rename {lib => packages/client/lib}/commands/PUBLISH.ts (100%) rename {lib => packages/client/lib}/commands/PUBSUB_CHANNELS.spec.ts (80%) rename {lib => packages/client/lib}/commands/PUBSUB_CHANNELS.ts (100%) rename {lib => packages/client/lib}/commands/PUBSUB_NUMPAT.spec.ts (70%) rename {lib => packages/client/lib}/commands/PUBSUB_NUMPAT.ts (100%) rename {lib => packages/client/lib}/commands/PUBSUB_NUMSUB.spec.ts (84%) rename {lib => packages/client/lib}/commands/PUBSUB_NUMSUB.ts (100%) rename {lib => packages/client/lib}/commands/RANDOMKEY.spec.ts (70%) rename {lib => packages/client/lib}/commands/RANDOMKEY.ts (100%) rename {lib => packages/client/lib}/commands/READONLY.spec.ts (100%) rename {lib => packages/client/lib}/commands/READONLY.ts (100%) rename {lib => packages/client/lib}/commands/READWRITE.spec.ts (100%) rename {lib => packages/client/lib}/commands/READWRITE.ts (100%) rename {lib => packages/client/lib}/commands/RENAME.spec.ts (74%) rename {lib => packages/client/lib}/commands/RENAME.ts (100%) rename {lib => packages/client/lib}/commands/RENAMENX.spec.ts (74%) rename {lib => packages/client/lib}/commands/RENAMENX.ts (57%) rename {lib => packages/client/lib}/commands/REPLICAOF.spec.ts (100%) rename {lib => packages/client/lib}/commands/REPLICAOF.ts (100%) rename {lib => packages/client/lib}/commands/RESTORE-ASKING.spec.ts (100%) rename {lib => packages/client/lib}/commands/RESTORE-ASKING.ts (100%) rename {lib => packages/client/lib}/commands/ROLE.spec.ts (93%) rename {lib => packages/client/lib}/commands/ROLE.ts (100%) rename {lib => packages/client/lib}/commands/RPOP.spec.ts (62%) rename {lib => packages/client/lib}/commands/RPOP.ts (100%) rename {lib => packages/client/lib}/commands/RPOPLPUSH.spec.ts (66%) rename {lib => packages/client/lib}/commands/RPOPLPUSH.ts (100%) rename {lib => packages/client/lib}/commands/RPOP_COUNT.spec.ts (57%) rename {lib => packages/client/lib}/commands/RPOP_COUNT.ts (100%) rename {lib => packages/client/lib}/commands/RPUSH.spec.ts (73%) rename {lib => packages/client/lib}/commands/RPUSH.ts (100%) rename {lib => packages/client/lib}/commands/RPUSHX.spec.ts (73%) rename {lib => packages/client/lib}/commands/RPUSHX.ts (100%) rename {lib => packages/client/lib}/commands/SADD.spec.ts (81%) rename {lib => packages/client/lib}/commands/SADD.ts (100%) rename {lib => packages/client/lib}/commands/SAVE.spec.ts (100%) rename {lib => packages/client/lib}/commands/SAVE.ts (100%) rename {lib => packages/client/lib}/commands/SCAN.spec.ts (93%) rename {lib => packages/client/lib}/commands/SCAN.ts (100%) rename {lib => packages/client/lib}/commands/SCARD.spec.ts (70%) rename {lib => packages/client/lib}/commands/SCARD.ts (100%) rename {lib => packages/client/lib}/commands/SCRIPT_DEBUG.spec.ts (71%) rename {lib => packages/client/lib}/commands/SCRIPT_DEBUG.ts (100%) rename {lib => packages/client/lib}/commands/SCRIPT_EXISTS.spec.ts (80%) rename {lib => packages/client/lib}/commands/SCRIPT_EXISTS.ts (56%) rename {lib => packages/client/lib}/commands/SCRIPT_FLUSH.spec.ts (80%) rename {lib => packages/client/lib}/commands/SCRIPT_FLUSH.ts (100%) rename {lib => packages/client/lib}/commands/SCRIPT_KILL.spec.ts (100%) rename {lib => packages/client/lib}/commands/SCRIPT_KILL.ts (100%) rename {lib => packages/client/lib}/commands/SCRIPT_LOAD.spec.ts (77%) rename {lib => packages/client/lib}/commands/SCRIPT_LOAD.ts (100%) rename {lib => packages/client/lib}/commands/SDIFF.spec.ts (80%) rename {lib => packages/client/lib}/commands/SDIFF.ts (100%) rename {lib => packages/client/lib}/commands/SDIFFSTORE.spec.ts (82%) rename {lib => packages/client/lib}/commands/SDIFFSTORE.ts (100%) rename {lib => packages/client/lib}/commands/SET.spec.ts (91%) rename {lib => packages/client/lib}/commands/SET.ts (59%) rename {lib => packages/client/lib}/commands/SETBIT.spec.ts (64%) rename {lib => packages/client/lib}/commands/SETBIT.ts (100%) rename {lib => packages/client/lib}/commands/SETEX.spec.ts (65%) rename {lib => packages/client/lib}/commands/SETEX.ts (100%) rename {lib => packages/client/lib}/commands/SETNX .spec.ts (64%) rename {lib => packages/client/lib}/commands/SETNX.ts (56%) rename {lib => packages/client/lib}/commands/SETRANGE.spec.ts (65%) rename {lib => packages/client/lib}/commands/SETRANGE.ts (100%) rename {lib => packages/client/lib}/commands/SHUTDOWN.spec.ts (100%) rename {lib => packages/client/lib}/commands/SHUTDOWN.ts (100%) rename {lib => packages/client/lib}/commands/SINTER.spec.ts (80%) rename {lib => packages/client/lib}/commands/SINTER.ts (100%) rename {lib => packages/client/lib}/commands/SINTERSTORE.spec.ts (82%) rename {lib => packages/client/lib}/commands/SINTERSTORE.ts (100%) rename {lib => packages/client/lib}/commands/SISMEMBER.spec.ts (63%) rename {lib => packages/client/lib}/commands/SISMEMBER.ts (57%) rename {lib => packages/client/lib}/commands/SMEMBERS.spec.ts (71%) rename {lib => packages/client/lib}/commands/SMEMBERS.ts (100%) rename {lib => packages/client/lib}/commands/SMISMEMBER.spec.ts (64%) rename {lib => packages/client/lib}/commands/SMISMEMBER.ts (57%) rename {lib => packages/client/lib}/commands/SMOVE.spec.ts (75%) rename {lib => packages/client/lib}/commands/SMOVE.ts (62%) rename {lib => packages/client/lib}/commands/SORT.spec.ts (94%) rename {lib => packages/client/lib}/commands/SORT.ts (100%) rename {lib => packages/client/lib}/commands/SPOP.spec.ts (80%) rename {lib => packages/client/lib}/commands/SPOP.ts (100%) rename {lib => packages/client/lib}/commands/SRANDMEMBER.spec.ts (71%) rename {lib => packages/client/lib}/commands/SRANDMEMBER.ts (100%) rename {lib => packages/client/lib}/commands/SRANDMEMBER_COUNT.spec.ts (72%) rename {lib => packages/client/lib}/commands/SRANDMEMBER_COUNT.ts (100%) rename {lib => packages/client/lib}/commands/SREM.spec.ts (81%) rename {lib => packages/client/lib}/commands/SREM.ts (100%) rename {lib => packages/client/lib}/commands/SSCAN.spec.ts (92%) rename {lib => packages/client/lib}/commands/SSCAN.ts (100%) rename {lib => packages/client/lib}/commands/STRLEN.spec.ts (62%) rename {lib => packages/client/lib}/commands/STRLEN.ts (100%) rename {lib => packages/client/lib}/commands/SUNION.spec.ts (80%) rename {lib => packages/client/lib}/commands/SUNION.ts (100%) rename {lib => packages/client/lib}/commands/SUNIONSTORE.spec.ts (82%) rename {lib => packages/client/lib}/commands/SUNIONSTORE.ts (100%) rename {lib => packages/client/lib}/commands/SWAPDB.spec.ts (71%) rename {lib => packages/client/lib}/commands/SWAPDB.ts (100%) rename {lib => packages/client/lib}/commands/TIME.spec.ts (73%) rename {lib => packages/client/lib}/commands/TIME.ts (100%) rename {lib => packages/client/lib}/commands/TOUCH.spec.ts (80%) rename {lib => packages/client/lib}/commands/TOUCH.ts (100%) rename {lib => packages/client/lib}/commands/TTL.spec.ts (70%) rename {lib => packages/client/lib}/commands/TTL.ts (100%) rename {lib => packages/client/lib}/commands/TYPE.spec.ts (71%) rename {lib => packages/client/lib}/commands/TYPE.ts (100%) rename {lib => packages/client/lib}/commands/UNLINK.spec.ts (80%) rename {lib => packages/client/lib}/commands/UNLINK.ts (100%) rename {lib => packages/client/lib}/commands/UNWATCH.spec.ts (70%) rename {lib => packages/client/lib}/commands/UNWATCH.ts (100%) rename {lib => packages/client/lib}/commands/WAIT.spec.ts (70%) rename {lib => packages/client/lib}/commands/WAIT.ts (100%) rename {lib => packages/client/lib}/commands/WATCH.spec.ts (100%) rename {lib => packages/client/lib}/commands/WATCH.ts (100%) rename {lib => packages/client/lib}/commands/XACK.spec.ts (82%) rename {lib => packages/client/lib}/commands/XACK.ts (100%) rename {lib => packages/client/lib}/commands/XADD.spec.ts (95%) rename {lib => packages/client/lib}/commands/XADD.ts (100%) rename {lib => packages/client/lib}/commands/XAUTOCLAIM.spec.ts (82%) rename {lib => packages/client/lib}/commands/XAUTOCLAIM.ts (100%) rename {lib => packages/client/lib}/commands/XAUTOCLAIM_JUSTID.spec.ts (76%) rename {lib => packages/client/lib}/commands/XAUTOCLAIM_JUSTID.ts (100%) rename {lib => packages/client/lib}/commands/XCLAIM.spec.ts (94%) rename {lib => packages/client/lib}/commands/XCLAIM.ts (85%) rename {lib => packages/client/lib}/commands/XCLAIM_JUSTID.spec.ts (78%) rename {lib => packages/client/lib}/commands/XCLAIM_JUSTID.ts (100%) rename {lib => packages/client/lib}/commands/XDEL.spec.ts (81%) rename {lib => packages/client/lib}/commands/XDEL.ts (100%) rename {lib => packages/client/lib}/commands/XGROUP_CREATE.spec.ts (84%) rename {lib => packages/client/lib}/commands/XGROUP_CREATE.ts (100%) rename {lib => packages/client/lib}/commands/XGROUP_CREATECONSUMER.spec.ts (69%) rename {lib => packages/client/lib}/commands/XGROUP_CREATECONSUMER.ts (62%) rename {lib => packages/client/lib}/commands/XGROUP_DELCONSUMER.spec.ts (77%) rename {lib => packages/client/lib}/commands/XGROUP_DELCONSUMER.ts (100%) rename {lib => packages/client/lib}/commands/XGROUP_DESTROY.spec.ts (76%) rename {lib => packages/client/lib}/commands/XGROUP_DESTROY.ts (58%) rename {lib => packages/client/lib}/commands/XGROUP_SETID.spec.ts (77%) rename {lib => packages/client/lib}/commands/XGROUP_SETID.ts (100%) rename {lib => packages/client/lib}/commands/XINFO_CONSUMERS.spec.ts (86%) rename {lib => packages/client/lib}/commands/XINFO_CONSUMERS.ts (100%) rename {lib => packages/client/lib}/commands/XINFO_GROUPS.spec.ts (89%) rename {lib => packages/client/lib}/commands/XINFO_GROUPS.ts (100%) rename {lib => packages/client/lib}/commands/XINFO_STREAM.spec.ts (93%) rename {lib => packages/client/lib}/commands/XINFO_STREAM.ts (100%) rename {lib => packages/client/lib}/commands/XLEN.spec.ts (70%) rename {lib => packages/client/lib}/commands/XLEN.ts (100%) rename {lib => packages/client/lib}/commands/XPENDING.spec.ts (82%) rename {lib => packages/client/lib}/commands/XPENDING.ts (100%) rename {lib => packages/client/lib}/commands/XPENDING_RANGE.spec.ts (90%) rename {lib => packages/client/lib}/commands/XPENDING_RANGE.ts (81%) rename {lib => packages/client/lib}/commands/XRANGE.spec.ts (82%) rename {lib => packages/client/lib}/commands/XRANGE.ts (74%) rename {lib => packages/client/lib}/commands/XREAD.spec.ts (90%) rename {lib => packages/client/lib}/commands/XREAD.ts (88%) rename {lib => packages/client/lib}/commands/XREADGROUP.spec.ts (87%) rename {lib => packages/client/lib}/commands/XREADGROUP.ts (90%) rename {lib => packages/client/lib}/commands/XREVRANGE.spec.ts (82%) rename {lib => packages/client/lib}/commands/XREVRANGE.ts (71%) rename {lib => packages/client/lib}/commands/XTRIM.spec.ts (90%) rename {lib => packages/client/lib}/commands/XTRIM.ts (100%) rename {lib => packages/client/lib}/commands/ZADD.spec.ts (95%) rename {lib => packages/client/lib}/commands/ZADD.ts (83%) rename {lib => packages/client/lib}/commands/ZCARD.spec.ts (70%) rename {lib => packages/client/lib}/commands/ZCARD.ts (100%) rename {lib => packages/client/lib}/commands/ZCOUNT.spec.ts (72%) create mode 100644 packages/client/lib/commands/ZCOUNT.ts rename {lib => packages/client/lib}/commands/ZDIFF.spec.ts (72%) rename {lib => packages/client/lib}/commands/ZDIFF.ts (100%) rename {lib => packages/client/lib}/commands/ZDIFFSTORE.spec.ts (75%) rename {lib => packages/client/lib}/commands/ZDIFFSTORE.ts (100%) rename {lib => packages/client/lib}/commands/ZDIFF_WITHSCORES.spec.ts (73%) rename {lib => packages/client/lib}/commands/ZDIFF_WITHSCORES.ts (72%) rename {lib => packages/client/lib}/commands/ZINCRBY.spec.ts (73%) rename {lib => packages/client/lib}/commands/ZINCRBY.ts (61%) rename {lib => packages/client/lib}/commands/ZINTER.spec.ts (85%) rename {lib => packages/client/lib}/commands/ZINTER.ts (100%) rename {lib => packages/client/lib}/commands/ZINTERSTORE.spec.ts (91%) rename {lib => packages/client/lib}/commands/ZINTERSTORE.ts (100%) rename {lib => packages/client/lib}/commands/ZINTER_WITHSCORES.spec.ts (86%) rename {lib => packages/client/lib}/commands/ZINTER_WITHSCORES.ts (73%) rename {lib => packages/client/lib}/commands/ZLEXCOUNT.spec.ts (73%) rename {lib => packages/client/lib}/commands/ZLEXCOUNT.ts (100%) rename {lib => packages/client/lib}/commands/ZMSCORE.spec.ts (73%) rename {lib => packages/client/lib}/commands/ZMSCORE.ts (62%) rename {lib => packages/client/lib}/commands/ZPOPMAX.spec.ts (79%) rename {lib => packages/client/lib}/commands/ZPOPMAX.ts (100%) rename {lib => packages/client/lib}/commands/ZPOPMAX_COUNT.spec.ts (72%) rename {lib => packages/client/lib}/commands/ZPOPMAX_COUNT.ts (67%) rename {lib => packages/client/lib}/commands/ZPOPMIN.spec.ts (79%) rename {lib => packages/client/lib}/commands/ZPOPMIN.ts (100%) rename {lib => packages/client/lib}/commands/ZPOPMIN_COUNT.spec.ts (72%) rename {lib => packages/client/lib}/commands/ZPOPMIN_COUNT.ts (67%) rename {lib => packages/client/lib}/commands/ZRANDMEMBER.spec.ts (62%) rename {lib => packages/client/lib}/commands/ZRANDMEMBER.ts (100%) rename {lib => packages/client/lib}/commands/ZRANDMEMBER_COUNT.spec.ts (63%) rename {lib => packages/client/lib}/commands/ZRANDMEMBER_COUNT.ts (100%) rename {lib => packages/client/lib}/commands/ZRANDMEMBER_COUNT_WITHSCORES.spec.ts (64%) rename {lib => packages/client/lib}/commands/ZRANDMEMBER_COUNT_WITHSCORES.ts (73%) rename {lib => packages/client/lib}/commands/ZRANGE.spec.ts (85%) rename {lib => packages/client/lib}/commands/ZRANGE.ts (78%) rename {lib => packages/client/lib}/commands/ZRANGEBYLEX.spec.ts (84%) rename {lib => packages/client/lib}/commands/ZRANGEBYLEX.ts (74%) rename {lib => packages/client/lib}/commands/ZRANGEBYSCORE.spec.ts (84%) rename {lib => packages/client/lib}/commands/ZRANGEBYSCORE.ts (74%) rename {lib => packages/client/lib}/commands/ZRANGEBYSCORE_WITHSCORES.spec.ts (84%) rename {lib => packages/client/lib}/commands/ZRANGEBYSCORE_WITHSCORES.ts (78%) rename {lib => packages/client/lib}/commands/ZRANGESTORE.spec.ts (90%) rename {lib => packages/client/lib}/commands/ZRANGESTORE.ts (72%) rename {lib => packages/client/lib}/commands/ZRANGE_WITHSCORES.spec.ts (92%) rename {lib => packages/client/lib}/commands/ZRANGE_WITHSCORES.ts (70%) rename {lib => packages/client/lib}/commands/ZRANK.spec.ts (72%) rename {lib => packages/client/lib}/commands/ZRANK.ts (100%) rename {lib => packages/client/lib}/commands/ZREM.spec.ts (81%) rename {lib => packages/client/lib}/commands/ZREM.ts (100%) rename {lib => packages/client/lib}/commands/ZREMRANGEBYLEX.spec.ts (73%) rename {lib => packages/client/lib}/commands/ZREMRANGEBYLEX.ts (100%) rename {lib => packages/client/lib}/commands/ZREMRANGEBYRANK.spec.ts (72%) rename {lib => packages/client/lib}/commands/ZREMRANGEBYRANK.ts (100%) rename {lib => packages/client/lib}/commands/ZREMRANGEBYSCORE.spec.ts (72%) rename {lib => packages/client/lib}/commands/ZREMRANGEBYSCORE.ts (100%) rename {lib => packages/client/lib}/commands/ZREVRANK.spec.ts (72%) rename {lib => packages/client/lib}/commands/ZREVRANK.ts (100%) rename {lib => packages/client/lib}/commands/ZSCAN.spec.ts (93%) rename {lib => packages/client/lib}/commands/ZSCAN.ts (100%) rename {lib => packages/client/lib}/commands/ZSCORE.spec.ts (72%) rename {lib => packages/client/lib}/commands/ZSCORE.ts (57%) rename {lib => packages/client/lib}/commands/ZUNION.spec.ts (83%) rename {lib => packages/client/lib}/commands/ZUNION.ts (100%) rename {lib => packages/client/lib}/commands/ZUNIONSTORE.spec.ts (91%) rename {lib => packages/client/lib}/commands/ZUNIONSTORE.ts (100%) rename {lib => packages/client/lib}/commands/ZUNION_WITHSCORES.spec.ts (83%) rename {lib => packages/client/lib}/commands/ZUNION_WITHSCORES.ts (73%) rename {lib => packages/client/lib}/commands/generic-transformers.spec.ts (97%) rename {lib => packages/client/lib}/commands/generic-transformers.ts (98%) rename {lib => packages/client/lib}/commands/index.ts (100%) rename {lib => packages/client/lib}/errors.ts (65%) rename {lib => packages/client/lib}/lua-script.ts (100%) rename {lib => packages/client/lib}/multi-command.spec.ts (100%) rename {lib => packages/client/lib}/multi-command.ts (100%) create mode 100644 packages/client/lib/test-utils.ts rename {lib => packages/client/lib}/ts-declarations/cluster-key-slot.d.ts (100%) rename {lib => packages/client/lib}/utils.ts (100%) create mode 100644 packages/client/package.json create mode 100644 packages/client/tsconfig.json create mode 100644 packages/json/.npmignore rename .nycrc.json => packages/json/.nycrc.json (98%) create mode 100644 packages/json/.release-it.json create mode 100644 packages/json/README.md create mode 100644 packages/json/lib/commands/ARRAPPEND.spec.ts create mode 100644 packages/json/lib/commands/ARRAPPEND.ts create mode 100644 packages/json/lib/commands/ARRINDEX.spec.ts create mode 100644 packages/json/lib/commands/ARRINDEX.ts create mode 100644 packages/json/lib/commands/ARRINSERT.spec.ts create mode 100644 packages/json/lib/commands/ARRINSERT.ts create mode 100644 packages/json/lib/commands/ARRLEN.spec.ts create mode 100644 packages/json/lib/commands/ARRLEN.ts create mode 100644 packages/json/lib/commands/ARRPOP.spec.ts create mode 100644 packages/json/lib/commands/ARRPOP.ts create mode 100644 packages/json/lib/commands/ARRTRIM.spec.ts create mode 100644 packages/json/lib/commands/ARRTRIM.ts create mode 100644 packages/json/lib/commands/DEBUG_MEMORY.spec.ts create mode 100644 packages/json/lib/commands/DEBUG_MEMORY.ts create mode 100644 packages/json/lib/commands/DEL.spec.ts create mode 100644 packages/json/lib/commands/DEL.ts create mode 100644 packages/json/lib/commands/FORGET.spec.ts create mode 100644 packages/json/lib/commands/FORGET.ts create mode 100644 packages/json/lib/commands/GET.spec.ts create mode 100644 packages/json/lib/commands/GET.ts create mode 100644 packages/json/lib/commands/MGET.spec.ts create mode 100644 packages/json/lib/commands/MGET.ts create mode 100644 packages/json/lib/commands/NUMINCRBY.spec.ts create mode 100644 packages/json/lib/commands/NUMINCRBY.ts create mode 100644 packages/json/lib/commands/NUMMULTBY.spec.ts create mode 100644 packages/json/lib/commands/NUMMULTBY.ts create mode 100644 packages/json/lib/commands/OBJKEYS.spec.ts create mode 100644 packages/json/lib/commands/OBJKEYS.ts create mode 100644 packages/json/lib/commands/OBJLEN.spec.ts create mode 100644 packages/json/lib/commands/OBJLEN.ts create mode 100644 packages/json/lib/commands/RESP.spec.ts create mode 100644 packages/json/lib/commands/RESP.ts create mode 100644 packages/json/lib/commands/SET.spec.ts create mode 100644 packages/json/lib/commands/SET.ts create mode 100644 packages/json/lib/commands/STRAPPEND.spec.ts create mode 100644 packages/json/lib/commands/STRAPPEND.ts create mode 100644 packages/json/lib/commands/STRLEN.spec.ts create mode 100644 packages/json/lib/commands/STRLEN.ts create mode 100644 packages/json/lib/commands/TYPE.spec.ts create mode 100644 packages/json/lib/commands/TYPE.ts create mode 100644 packages/json/lib/commands/index.ts create mode 100644 packages/json/lib/index.ts create mode 100644 packages/json/lib/test-utils.ts create mode 100644 packages/json/package.json create mode 100644 packages/json/tsconfig.json create mode 100644 packages/search/.npmignore create mode 100644 packages/search/.nycrc.json create mode 100644 packages/search/.release-it.json create mode 100644 packages/search/README.md create mode 100644 packages/search/lib/commands/AGGREGATE.spec.ts create mode 100644 packages/search/lib/commands/AGGREGATE.ts create mode 100644 packages/search/lib/commands/ALIASADD.spec.ts create mode 100644 packages/search/lib/commands/ALIASADD.ts create mode 100644 packages/search/lib/commands/ALIASDEL.spec.ts create mode 100644 packages/search/lib/commands/ALIASDEL.ts create mode 100644 packages/search/lib/commands/ALIASUPDATE.spec.ts create mode 100644 packages/search/lib/commands/ALIASUPDATE.ts create mode 100644 packages/search/lib/commands/CONFIG_GET.spec.ts create mode 100644 packages/search/lib/commands/CONFIG_GET.ts create mode 100644 packages/search/lib/commands/CONFIG_SET.spec.ts create mode 100644 packages/search/lib/commands/CONFIG_SET.ts create mode 100644 packages/search/lib/commands/CREATE.spec.ts create mode 100644 packages/search/lib/commands/CREATE.ts create mode 100644 packages/search/lib/commands/DICTADD.spec.ts create mode 100644 packages/search/lib/commands/DICTADD.ts create mode 100644 packages/search/lib/commands/DICTDEL.spec.ts create mode 100644 packages/search/lib/commands/DICTDEL.ts create mode 100644 packages/search/lib/commands/DICTDUMP.spec.ts create mode 100644 packages/search/lib/commands/DICTDUMP.ts create mode 100644 packages/search/lib/commands/DROPINDEX.spec.ts create mode 100644 packages/search/lib/commands/DROPINDEX.ts create mode 100644 packages/search/lib/commands/EXPLAIN.spec.ts create mode 100644 packages/search/lib/commands/EXPLAIN.ts create mode 100644 packages/search/lib/commands/EXPLAINCLI.spec.ts create mode 100644 packages/search/lib/commands/EXPLAINCLI.ts create mode 100644 packages/search/lib/commands/INFO.spec.ts create mode 100644 packages/search/lib/commands/INFO.ts create mode 100644 packages/search/lib/commands/PROFILE.ts create mode 100644 packages/search/lib/commands/SEARCH.spec.ts create mode 100644 packages/search/lib/commands/SEARCH.ts create mode 100644 packages/search/lib/commands/SPELLCHECK.spec.ts create mode 100644 packages/search/lib/commands/SPELLCHECK.ts create mode 100644 packages/search/lib/commands/SUGADD.spec.ts create mode 100644 packages/search/lib/commands/SUGADD.ts create mode 100644 packages/search/lib/commands/SUGDEL.spec.ts create mode 100644 packages/search/lib/commands/SUGDEL.ts create mode 100644 packages/search/lib/commands/SUGGET.spec.ts create mode 100644 packages/search/lib/commands/SUGGET.ts create mode 100644 packages/search/lib/commands/SUGGET_WITHPAYLOADS.spec.ts create mode 100644 packages/search/lib/commands/SUGGET_WITHPAYLOADS.ts create mode 100644 packages/search/lib/commands/SUGGET_WITHSCORES.spec.ts create mode 100644 packages/search/lib/commands/SUGGET_WITHSCORES.ts create mode 100644 packages/search/lib/commands/SUGGET_WITHSCORES_WITHPAYLOADS.spec.ts create mode 100644 packages/search/lib/commands/SUGGET_WITHSCORES_WITHPAYLOADS.ts create mode 100644 packages/search/lib/commands/SUGLEN.spec.ts create mode 100644 packages/search/lib/commands/SUGLEN.ts create mode 100644 packages/search/lib/commands/SYNDUMP.spec.ts create mode 100644 packages/search/lib/commands/SYNDUMP.ts create mode 100644 packages/search/lib/commands/SYNUPDATE.spec.ts create mode 100644 packages/search/lib/commands/SYNUPDATE.ts create mode 100644 packages/search/lib/commands/TAGVALS.spec.ts create mode 100644 packages/search/lib/commands/TAGVALS.ts create mode 100644 packages/search/lib/commands/_LIST.spec.ts create mode 100644 packages/search/lib/commands/_LIST.ts create mode 100644 packages/search/lib/commands/index.spec.ts create mode 100644 packages/search/lib/commands/index.ts create mode 100644 packages/search/lib/index.ts create mode 100644 packages/search/lib/test-utils.ts create mode 100644 packages/search/package.json create mode 100644 packages/search/tsconfig.json create mode 100644 packages/test-utils/docker/Dockerfile create mode 100755 packages/test-utils/docker/entrypoint.sh create mode 100644 packages/test-utils/lib/dockers.ts create mode 100644 packages/test-utils/lib/index.ts create mode 100644 packages/test-utils/package.json create mode 100644 packages/test-utils/tsconfig.json create mode 100644 tsconfig.base.json diff --git a/.github/README.md b/.github/README.md new file mode 100644 index 00000000000..c7046805107 --- /dev/null +++ b/.github/README.md @@ -0,0 +1,298 @@ +# Node-Redis + +[![Tests](https://img.shields.io/github/workflow/status/redis/node-redis/Tests/master.svg?label=tests)](https://codecov.io/gh/redis/node-redis) +[![Coverage](https://codecov.io/gh/redis/node-redis/branch/master/graph/badge.svg?token=xcfqHhJC37)](https://codecov.io/gh/redis/node-redis) +[![License](https://img.shields.io/github/license/redis/node-redis.svg)](https://codecov.io/gh/redis/node-redis) +[![Chat](https://img.shields.io/discord/697882427875393627.svg)](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 { + 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](../) | [![Downloads](https://img.shields.io/npm/dm/redis.svg)](https://www.npmjs.com/package/redis/v/next) [![Version](https://img.shields.io/npm/v/redis/next.svg)](https://www.npmjs.com/package/redis/v/next) | +| [@node-redis/client](../packages/client) | [![Downloads](https://img.shields.io/npm/dm/@node-redis/client.svg)](https://www.npmjs.com/package/@node-redis/client/v/next) [![Version](https://img.shields.io/npm/v/@node-redis/client/next.svg)](https://www.npmjs.com/package/@node-redis/client/v/next) | +| [@node-redis/json](../packages/json) | [![Downloads](https://img.shields.io/npm/dm/@node-redis/json.svg)](https://www.npmjs.com/package/@node-redis/json/v/next) [![Version](https://img.shields.io/npm/v/@node-redis/json/next.svg)](https://www.npmjs.com/package/@node-redis/json/v/next) [Redis JSON](https://oss.redis.com/redisjson/) commands | +| [@node-redis/search](../packages/search) | [![Downloads](https://img.shields.io/npm/dm/@node-redis/search.svg)](https://www.npmjs.com/package/@node-redis/search/v/next) [![Version](https://img.shields.io/npm/v/@node-redis/search/next.svg)](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! + +[![Contributors](https://contrib.rocks/image?repo=redis/node-redis)](https://github.com/redis/node-redis/graphs/contributors) + +## License + +This repository is licensed under the "MIT" license. See [LICENSE](LICENSE). diff --git a/.github/release-drafter-config.yml b/.github/release-drafter-config.yml new file mode 100644 index 00000000000..9a98b1c08a3 --- /dev/null +++ b/.github/release-drafter-config.yml @@ -0,0 +1,43 @@ +name-template: 'Version $NEXT_PATCH_VERSION' +tag-template: 'v$NEXT_PATCH_VERSION' +autolabeler: + - label: 'chore' + files: + - '*.md' + - '.github/*' + - label: 'bug' + branch: + - '/bug-.+' + - label: 'chore' + branch: + - '/chore-.+' + - label: 'feature' + branch: + - '/feature-.+' +categories: + - title: 'Breaking Changes' + labels: + - 'breakingchange' + - title: '🚀 New Features' + labels: + - 'feature' + - 'enhancement' + - title: '🐛 Bug Fixes' + labels: + - 'fix' + - 'bugfix' + - 'bug' + - title: '🧰 Maintenance' + label: 'chore' +change-template: '- $TITLE (#$NUMBER)' +exclude-labels: + - 'skip-changelog' +template: | + ## Changes + + $CHANGES + + ## Contributors + We'd like to thank all the contributors who worked on this release! + + $CONTRIBUTORS diff --git a/.github/workflows/benchmark.yml b/.github/workflows/benchmark.yml deleted file mode 100644 index 2df438eb19c..00000000000 --- a/.github/workflows/benchmark.yml +++ /dev/null @@ -1,46 +0,0 @@ -name: Benchmark - -on: - push: - branches: - - master - - v4.0 - -jobs: - benchmark: - name: Benchmark - runs-on: ubuntu-latest - strategy: - fail-fast: false - matrix: - node-version: [16.x] - redis-version: [6.x] - - steps: - - uses: actions/checkout@v2.3.4 - with: - fetch-depth: 1 - - - name: Use Node.js ${{ matrix.node-version }} - uses: actions/setup-node@v2.3.0 - with: - node-version: ${{ matrix.node-version }} - - - name: Setup Redis - uses: shogo82148/actions-setup-redis@v1.12.0 - with: - redis-version: ${{ matrix.redis-version }} - - - name: Install Packages - run: npm ci - - - name: Build - run: npm run build - - - name: Install Benchmark Packages - run: npm ci - working-directory: ./benchmark - - - name: Benchmark - run: npm run start - working-directory: ./benchmark diff --git a/.github/workflows/documentation.yml b/.github/workflows/documentation.yml index 9575d4639b9..861010b7f01 100644 --- a/.github/workflows/documentation.yml +++ b/.github/workflows/documentation.yml @@ -9,21 +9,16 @@ on: jobs: documentation: runs-on: ubuntu-latest - steps: - uses: actions/checkout@v2.3.4 with: fetch-depth: 1 - - name: Use Node.js uses: actions/setup-node@v2.3.0 - - name: Install Packages run: npm ci - - name: Generate Documentation run: npm run documentation - - name: Upload Documentation to Wiki uses: SwiftDocOrg/github-wiki-publish-action@v1 with: diff --git a/.github/workflows/release-drafter.yml b/.github/workflows/release-drafter.yml new file mode 100644 index 00000000000..ec2d88bf6e7 --- /dev/null +++ b/.github/workflows/release-drafter.yml @@ -0,0 +1,19 @@ +name: Release Drafter + +on: + push: + # branches to consider in the event; optional, defaults to all + branches: + - master + +jobs: + update_release_draft: + runs-on: ubuntu-latest + steps: + # Drafts your next Release notes as Pull Requests are merged into "master" + - uses: release-drafter/release-drafter@v5 + with: + # (Optional) specify config name to use, relative to .github/. Default: release-drafter.yml + config-name: release-drafter-config.yml + env: + GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }} diff --git a/.github/workflows/tests.yml b/.github/workflows/tests.yml index 557d4f452dc..f8e68c0e203 100644 --- a/.github/workflows/tests.yml +++ b/.github/workflows/tests.yml @@ -5,6 +5,10 @@ on: branches: - master - v4.0 + pull_request: + branches: + - master + - v4.0 jobs: tests: @@ -12,47 +16,32 @@ jobs: strategy: fail-fast: false matrix: - node-version: [12.x, 14.x, 16.x] - redis-version: [5.x, 6.x] - + node-version: [12, 14, 16] + redis-version: [5, 6.0, 6.2] steps: - uses: actions/checkout@v2.3.4 with: fetch-depth: 1 - - name: Use Node.js ${{ matrix.node-version }} uses: actions/setup-node@v2.3.0 with: node-version: ${{ matrix.node-version }} - - - name: Setup Redis - uses: shogo82148/actions-setup-redis@v1.12.0 - with: - redis-version: ${{ matrix.redis-version }} - auto-start: "false" - + - name: Update npm + run: npm i -g npm + if: ${{ matrix.node-version <= 14 }} - name: Install Packages run: npm ci - + - name: Build tests tools + run: npm run build:tests-tools - name: Run Tests - run: npm run test - - - name: Generate lcov - run: ./node_modules/.bin/nyc report -r lcov - - - name: Coveralls - uses: coverallsapp/github-action@1.1.3 - with: - github-token: ${{ secrets.GITHUB_TOKEN }} - flag-name: Node ${{ matrix.node-version }} Redis ${{ matrix.redis-version }} - parallel: true - - finish: - needs: tests - runs-on: ubuntu-latest - steps: - - name: Coveralls Finished - uses: coverallsapp/github-action@1.1.3 - with: - github-token: ${{ secrets.github_token }} - parallel-finished: true + run: npm run test -- --forbid-only --redis-version=${{ matrix.redis-version }} + - name: Upload to Codecov + run: | + curl https://keybase.io/codecovsecurity/pgp_keys.asc | gpg --no-default-keyring --keyring trustedkeys.gpg --import + curl -Os https://uploader.codecov.io/latest/linux/codecov + curl -Os https://uploader.codecov.io/latest/linux/codecov.SHA256SUM + curl -Os https://uploader.codecov.io/latest/linux/codecov.SHA256SUM.sig + gpgv codecov.SHA256SUM.sig codecov.SHA256SUM + shasum -a 256 -c codecov.SHA256SUM + chmod +x codecov + ./codecov diff --git a/.gitignore b/.gitignore index 0bdff14c7ff..9ee58bfbd30 100644 --- a/.gitignore +++ b/.gitignore @@ -1,8 +1,8 @@ -.vscode/ .idea/ -node_modules/ -dist/ .nyc_output/ +.vscode/ coverage/ +dist/ +node_modules/ +.DS_Store dump.rdb -documentation/ diff --git a/.npmignore b/.npmignore index e3fc1a7731d..a36c5e83cf2 100644 --- a/.npmignore +++ b/.npmignore @@ -1,18 +1,12 @@ +.github/ .vscode/ -.idea/ -node_modules/ -.nyc_output/ -coverage/ -dump.rdb -documentation/ -CONTRIBUTING.md -tsconfig.json +docs/ +examples/ +packages/ .deepsource.toml -.nycrc.json -benchmark/ -.github/ -scripts/ -lib/ +.release-it.json +CONTRIBUTING.md +SECURITY.md index.ts -*.spec.* -dist/lib/test-utils.* +tsconfig.base.json +tsconfig.json diff --git a/.release-it.json b/.release-it.json new file mode 100644 index 00000000000..982b4ac6cbe --- /dev/null +++ b/.release-it.json @@ -0,0 +1,7 @@ +{ + "git": { + "tagName": "redis@${version}", + "commitMessage": "Release ${tagName}", + "tagAnnotation": "Release ${tagName}" + } +} diff --git a/CONTRIBUTING.md b/CONTRIBUTING.md index fbad5205081..0243cc19359 100644 --- a/CONTRIBUTING.md +++ b/CONTRIBUTING.md @@ -45,12 +45,9 @@ A huge thank you to the original author of Node Redis, [Matthew Ranney](https:// Node Redis has a full test suite with coverage setup. -To run the tests, run `npm install` to install dependencies, then run `npm test`. +To run the tests, run `npm install` to install dependencies, then run `npm run build:tests-tools && npm test`. -Note that the test suite assumes that a few tools are installed in your environment, such as: - -- redis (make sure redis-server is not running when starting the tests, it's part of the test-suite to start it and you'll end up with a "port already in use" error) -- stunnel (for TLS tests) +Note that the test suite assumes that [`docker`](https://www.docker.com/) is installed in your environment. ### Submitting Code for Review diff --git a/LICENSE b/LICENSE index 5cb3bb4180b..db86cc4de7f 100644 --- a/LICENSE +++ b/LICENSE @@ -1,4 +1,4 @@ - MIT License +MIT License Copyright (c) 2016-present Node Redis contributors. diff --git a/README.md b/README.md index c768b691d71..a98e6a261b2 100644 --- a/README.md +++ b/README.md @@ -1,289 +1,2 @@ -

- - - -

Node Redis

-

- - - ---- - -## 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 in the [Wiki](https://github.com/NodeRedis/node-redis/wiki/lib.socket#RedisSocketOptions). - -### 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'); - -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 member of client.hScanIterator('hash')) {} -for await (const { field, value } of client.sScanIterator('set')) {} -for await (const { member, score } 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 } from 'redis'; -import { defineScript } from 'redis/lua-script'; - -(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 { - return [key, number.toString()]; - }, - transformReply(reply: number): number { - return reply; - } - }) - } - }); - - await client.connect(); - - await client.set('key', '1'); - await client.add('key', 2); // 3 -})(); -``` - -### Cluster - -Connecting to a cluster is a bit different. Create the client by specifying some (or all) of the nodes in your cluster and then use it like a non-clustered client: - -```typescript -import { createCluster } from 'redis'; - -(async () => { - const cluster = createCluster({ - rootNodes: [ - { - url: 'redis://10.0.0.1:30001' - }, - { - url: 'redis://10.0.0.2:30002' - } - ] - }); - - cluster.on('error', (err) => console.log('Redis Cluster Error', err)); - - await cluster.connect(); - - await cluster.set('key', 'value'); - const value = await cluster.get('key'); -})(); -``` - -### 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==') -]); -``` - -## 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! - - - - - -## License - -This repository is licensed under the "MIT" license. See [LICENSE](LICENSE). +# redis +The sources and docs for this package are in the main [node-redis](https://github.com/redis/node-redis) repo. diff --git a/SECURITY.md b/SECURITY.md index 0839a123c96..f96aa68dc12 100644 --- a/SECURITY.md +++ b/SECURITY.md @@ -5,9 +5,9 @@ Node Redis is generally backwards compatible with very few exceptions, so we recommend users to always use the latest version to experience stability, performance and security. | Version | Supported | -| ------- | ------------------ | -| 4.0.x | :white_check_mark: | -| 3.1.x | :white_check_mark: | +|---------|--------------------| +| 4.0.z | :heavy_check_mark: | +| 3.1.z | :heavy_check_mark: | | < 3.1 | :x: | ## Reporting a Vulnerability diff --git a/benchmark/.gitignore b/benchmark/.gitignore deleted file mode 100644 index 3c3629e647f..00000000000 --- a/benchmark/.gitignore +++ /dev/null @@ -1 +0,0 @@ -node_modules diff --git a/benchmark/index.js b/benchmark/index.js deleted file mode 100644 index 37f88176653..00000000000 --- a/benchmark/index.js +++ /dev/null @@ -1,81 +0,0 @@ -import { add, suite, cycle, complete } from 'benny'; -import v4 from 'v4'; -import v3 from 'v3'; -import { once } from 'events'; - -const v4Client = v4.createClient(), - v4LegacyClient = v4.createClient({ - legacyMode: true - }), - v3Client = v3.createClient(); - -await Promise.all([ - v4Client.connect(), - v4LegacyClient.connect(), - once(v3Client, 'connect') -]); - -const key = random(100), - value = random(100); - -function random(size) { - const result = []; - - for (let i = 0; i < size; i++) { - result.push(Math.floor(Math.random() * 10)); - } - - return result.join(''); -} - -suite( - 'SET GET', - add('v4', async () => { - await Promise.all([ - v4Client.set(key, value), - v4Client.get(key) - ]); - }), - add('v4 - legacy mode', () => { - return new Promise((resolve, reject) => { - v4LegacyClient.set(key, value); - v4LegacyClient.get(key, (err, reply) => { - if (err) { - reject(err); - } else { - resolve(reply); - } - }); - }); - }), - add('v3', () => { - return new Promise((resolve, reject) => { - v3Client.set(key, value); - v3Client.get(key, (err, reply) => { - if (err) { - reject(err); - } else { - resolve(reply); - } - }); - }); - }), - cycle(), - complete(), - complete(() => { - return Promise.all([ - v4Client.disconnect(), - v4LegacyClient.disconnect(), - new Promise((resolve, reject) => { - v3Client.quit((err) => { - if (err) { - reject(err); - } else { - resolve(err); - } - }); - }) - ]); - }) -); - diff --git a/benchmark/package-lock.json b/benchmark/package-lock.json deleted file mode 100644 index 4afaf8c305c..00000000000 --- a/benchmark/package-lock.json +++ /dev/null @@ -1,849 +0,0 @@ -{ - "name": "benchmark", - "lockfileVersion": 2, - "requires": true, - "packages": { - "": { - "name": "benchmark", - "license": "ISC", - "dependencies": { - "benny": "3.6.15", - "v3": "npm:redis@3.1.2", - "v4": "file:../" - } - }, - "..": { - "name": "redis", - "version": "4.0.0-rc.2", - "license": "MIT", - "dependencies": { - "cluster-key-slot": "1.1.0", - "generic-pool": "3.8.2", - "redis-parser": "3.0.0", - "yallist": "4.0.0" - }, - "devDependencies": { - "@istanbuljs/nyc-config-typescript": "^1.0.1", - "@tsconfig/node12": "^1.0.9", - "@types/mocha": "^9.0.0", - "@types/node": "^16.10.3", - "@types/sinon": "^10.0.4", - "@types/which": "^2.0.1", - "@types/yallist": "^4.0.1", - "mocha": "^9.1.2", - "nyc": "^15.1.0", - "release-it": "^14.11.6", - "sinon": "^11.1.2", - "source-map-support": "^0.5.20", - "ts-node": "^10.3.0", - "typedoc": "^0.22.5", - "typedoc-github-wiki-theme": "^0.6.0", - "typedoc-plugin-markdown": "^3.11.3", - "typescript": "^4.4.3", - "which": "^2.0.2" - }, - "engines": { - "node": ">=12" - } - }, - "node_modules/@arrows/array": { - "version": "1.4.1", - "resolved": "https://registry.npmjs.org/@arrows/array/-/array-1.4.1.tgz", - "integrity": "sha512-MGYS8xi3c4tTy1ivhrVntFvufoNzje0PchjEz6G/SsWRgUKxL4tKwS6iPdO8vsaJYldagAeWMd5KRD0aX3Q39g==", - "dependencies": { - "@arrows/composition": "^1.2.2" - } - }, - "node_modules/@arrows/composition": { - "version": "1.2.2", - "resolved": "https://registry.npmjs.org/@arrows/composition/-/composition-1.2.2.tgz", - "integrity": "sha512-9fh1yHwrx32lundiB3SlZ/VwuStPB4QakPsSLrGJFH6rCXvdrd060ivAZ7/2vlqPnEjBkPRRXOcG1YOu19p2GQ==" - }, - "node_modules/@arrows/dispatch": { - "version": "1.0.3", - "resolved": "https://registry.npmjs.org/@arrows/dispatch/-/dispatch-1.0.3.tgz", - "integrity": "sha512-v/HwvrFonitYZM2PmBlAlCqVqxrkIIoiEuy5bQgn0BdfvlL0ooSBzcPzTMrtzY8eYktPyYcHg8fLbSgyybXEqw==", - "dependencies": { - "@arrows/composition": "^1.2.2" - } - }, - "node_modules/@arrows/error": { - "version": "1.0.2", - "resolved": "https://registry.npmjs.org/@arrows/error/-/error-1.0.2.tgz", - "integrity": "sha512-yvkiv1ay4Z3+Z6oQsUkedsQm5aFdyPpkBUQs8vejazU/RmANABx6bMMcBPPHI4aW43VPQmXFfBzr/4FExwWTEA==" - }, - "node_modules/@arrows/multimethod": { - "version": "1.1.7", - "resolved": "https://registry.npmjs.org/@arrows/multimethod/-/multimethod-1.1.7.tgz", - "integrity": "sha512-EjHD3XuGAV4G28rm7mu8k7zQJh/EOizh104/p9i2ofGcnL5mgKONFH/Bq6H3SJjM+WDAlKcR9WBpNhaAKCnH2g==", - "dependencies": { - "@arrows/array": "^1.4.0", - "@arrows/composition": "^1.2.2", - "@arrows/error": "^1.0.2", - "fast-deep-equal": "^3.1.1" - } - }, - "node_modules/ansi-escapes": { - "version": "4.3.2", - "resolved": "https://registry.npmjs.org/ansi-escapes/-/ansi-escapes-4.3.2.tgz", - "integrity": "sha512-gKXj5ALrKWQLsYG9jlTRmR/xKluxHV+Z9QEwNIgCfM1/uwPMCuzVVnh5mwTd+OuBZcwSIMbqssNWRm1lE51QaQ==", - "dependencies": { - "type-fest": "^0.21.3" - }, - "engines": { - "node": ">=8" - }, - "funding": { - "url": "https://github.com/sponsors/sindresorhus" - } - }, - "node_modules/ansi-regex": { - "version": "5.0.1", - "resolved": "https://registry.npmjs.org/ansi-regex/-/ansi-regex-5.0.1.tgz", - "integrity": "sha512-quJQXlTSUGL2LH9SUXo8VwsY4soanhgo6LNSm84E1LBcE8s3O0wpdiRzyR9z/ZZJMlMWv37qOOb9pdJlMUEKFQ==", - "engines": { - "node": ">=8" - } - }, - "node_modules/ansi-styles": { - "version": "4.3.0", - "resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-4.3.0.tgz", - "integrity": "sha512-zbB9rCJAT1rbjiVDb2hqKFHNYLxgtk8NURxZ3IZwD3F6NtxbXZQCnnSi1Lkx+IDohdPlFp222wVALIheZJQSEg==", - "dependencies": { - "color-convert": "^2.0.1" - }, - "engines": { - "node": ">=8" - }, - "funding": { - "url": "https://github.com/chalk/ansi-styles?sponsor=1" - } - }, - "node_modules/astral-regex": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/astral-regex/-/astral-regex-2.0.0.tgz", - "integrity": "sha512-Z7tMw1ytTXt5jqMcOP+OQteU1VuNK9Y02uuJtKQ1Sv69jXQKKg5cibLwGJow8yzZP+eAc18EmLGPal0bp36rvQ==", - "engines": { - "node": ">=8" - } - }, - "node_modules/at-least-node": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/at-least-node/-/at-least-node-1.0.0.tgz", - "integrity": "sha512-+q/t7Ekv1EDY2l6Gda6LLiX14rU9TV20Wa3ofeQmwPFZbOMo9DXrLbOjFaaclkXKWidIaopwAObQDqwWtGUjqg==", - "engines": { - "node": ">= 4.0.0" - } - }, - "node_modules/benchmark": { - "version": "2.1.4", - "resolved": "https://registry.npmjs.org/benchmark/-/benchmark-2.1.4.tgz", - "integrity": "sha1-CfPeMckWQl1JjMLuVloOvzwqVik=", - "dependencies": { - "lodash": "^4.17.4", - "platform": "^1.3.3" - } - }, - "node_modules/benny": { - "version": "3.6.15", - "resolved": "https://registry.npmjs.org/benny/-/benny-3.6.15.tgz", - "integrity": "sha512-kq6XVGGYVou3Y8KNPs3SEF881vi5fJ8sIf9w69D2rreiNfRicWVWK6u6/mObMw6BiexoHHumtipn5gcu0Tngng==", - "dependencies": { - "@arrows/composition": "^1.0.0", - "@arrows/dispatch": "^1.0.2", - "@arrows/multimethod": "^1.1.6", - "benchmark": "^2.1.4", - "fs-extra": "^9.0.1", - "json2csv": "^5.0.4", - "kleur": "^4.1.3", - "log-update": "^4.0.0", - "prettier": "^2.1.2", - "stats-median": "^1.0.1" - } - }, - "node_modules/cli-cursor": { - "version": "3.1.0", - "resolved": "https://registry.npmjs.org/cli-cursor/-/cli-cursor-3.1.0.tgz", - "integrity": "sha512-I/zHAwsKf9FqGoXM4WWRACob9+SNukZTd94DWF57E4toouRulbCxcUh6RKUEOQlYTHJnzkPMySvPNaaSLNfLZw==", - "dependencies": { - "restore-cursor": "^3.1.0" - }, - "engines": { - "node": ">=8" - } - }, - "node_modules/color-convert": { - "version": "2.0.1", - "resolved": "https://registry.npmjs.org/color-convert/-/color-convert-2.0.1.tgz", - "integrity": "sha512-RRECPsj7iu/xb5oKYcsFHSppFNnsj/52OVTRKb4zP5onXwVF3zVmmToNcOfGC+CRDpfK/U584fMg38ZHCaElKQ==", - "dependencies": { - "color-name": "~1.1.4" - }, - "engines": { - "node": ">=7.0.0" - } - }, - "node_modules/color-name": { - "version": "1.1.4", - "resolved": "https://registry.npmjs.org/color-name/-/color-name-1.1.4.tgz", - "integrity": "sha512-dOy+3AuW3a2wNbZHIuMZpTcgjGuLU/uBL/ubcZF9OXbDo8ff4O8yVp5Bf0efS8uEoYo5q4Fx7dY9OgQGXgAsQA==" - }, - "node_modules/commander": { - "version": "6.2.1", - "resolved": "https://registry.npmjs.org/commander/-/commander-6.2.1.tgz", - "integrity": "sha512-U7VdrJFnJgo4xjrHpTzu0yrHPGImdsmD95ZlgYSEajAn2JKzDhDTPG9kBTefmObL2w/ngeZnilk+OV9CG3d7UA==", - "engines": { - "node": ">= 6" - } - }, - "node_modules/denque": { - "version": "1.5.1", - "resolved": "https://registry.npmjs.org/denque/-/denque-1.5.1.tgz", - "integrity": "sha512-XwE+iZ4D6ZUB7mfYRMb5wByE8L74HCn30FBN7sWnXksWc1LO1bPDl67pBR9o/kC4z/xSNAwkMYcGgqDV3BE3Hw==", - "engines": { - "node": ">=0.10" - } - }, - "node_modules/emoji-regex": { - "version": "8.0.0", - "resolved": "https://registry.npmjs.org/emoji-regex/-/emoji-regex-8.0.0.tgz", - "integrity": "sha512-MSjYzcWNOA0ewAHpz0MxpYFvwg6yjy1NG3xteoqz644VCo/RPgnr1/GGt+ic3iJTzQ8Eu3TdM14SawnVUmGE6A==" - }, - "node_modules/fast-deep-equal": { - "version": "3.1.3", - "resolved": "https://registry.npmjs.org/fast-deep-equal/-/fast-deep-equal-3.1.3.tgz", - "integrity": "sha512-f3qQ9oQy9j2AhBe/H9VC91wLmKBCCU/gDOnKNAYG5hswO7BLKj09Hc5HYNz9cGI++xlpDCIgDaitVs03ATR84Q==" - }, - "node_modules/fs-extra": { - "version": "9.1.0", - "resolved": "https://registry.npmjs.org/fs-extra/-/fs-extra-9.1.0.tgz", - "integrity": "sha512-hcg3ZmepS30/7BSFqRvoo3DOMQu7IjqxO5nCDt+zM9XWjb33Wg7ziNT+Qvqbuc3+gWpzO02JubVyk2G4Zvo1OQ==", - "dependencies": { - "at-least-node": "^1.0.0", - "graceful-fs": "^4.2.0", - "jsonfile": "^6.0.1", - "universalify": "^2.0.0" - }, - "engines": { - "node": ">=10" - } - }, - "node_modules/graceful-fs": { - "version": "4.2.8", - "resolved": "https://registry.npmjs.org/graceful-fs/-/graceful-fs-4.2.8.tgz", - "integrity": "sha512-qkIilPUYcNhJpd33n0GBXTB1MMPp14TxEsEs0pTrsSVucApsYzW5V+Q8Qxhik6KU3evy+qkAAowTByymK0avdg==" - }, - "node_modules/is-fullwidth-code-point": { - "version": "3.0.0", - "resolved": "https://registry.npmjs.org/is-fullwidth-code-point/-/is-fullwidth-code-point-3.0.0.tgz", - "integrity": "sha512-zymm5+u+sCsSWyD9qNaejV3DFvhCKclKdizYaJUuHA83RLjb7nSuGnddCHGv0hk+KY7BMAlsWeK4Ueg6EV6XQg==", - "engines": { - "node": ">=8" - } - }, - "node_modules/json2csv": { - "version": "5.0.6", - "resolved": "https://registry.npmjs.org/json2csv/-/json2csv-5.0.6.tgz", - "integrity": "sha512-0/4Lv6IenJV0qj2oBdgPIAmFiKKnh8qh7bmLFJ+/ZZHLjSeiL3fKKGX3UryvKPbxFbhV+JcYo9KUC19GJ/Z/4A==", - "dependencies": { - "commander": "^6.1.0", - "jsonparse": "^1.3.1", - "lodash.get": "^4.4.2" - }, - "bin": { - "json2csv": "bin/json2csv.js" - }, - "engines": { - "node": ">= 10", - "npm": ">= 6.13.0" - } - }, - "node_modules/jsonfile": { - "version": "6.1.0", - "resolved": "https://registry.npmjs.org/jsonfile/-/jsonfile-6.1.0.tgz", - "integrity": "sha512-5dgndWOriYSm5cnYaJNhalLNDKOqFwyDB/rr1E9ZsGciGvKPs8R2xYGCacuf3z6K1YKDz182fd+fY3cn3pMqXQ==", - "dependencies": { - "universalify": "^2.0.0" - }, - "optionalDependencies": { - "graceful-fs": "^4.1.6" - } - }, - "node_modules/jsonparse": { - "version": "1.3.1", - "resolved": "https://registry.npmjs.org/jsonparse/-/jsonparse-1.3.1.tgz", - "integrity": "sha1-P02uSpH6wxX3EGL4UhzCOfE2YoA=", - "engines": [ - "node >= 0.2.0" - ] - }, - "node_modules/kleur": { - "version": "4.1.4", - "resolved": "https://registry.npmjs.org/kleur/-/kleur-4.1.4.tgz", - "integrity": "sha512-8QADVssbrFjivHWQU7KkMgptGTl6WAcSdlbBPY4uNF+mWr6DGcKrvY2w4FQJoXch7+fKMjj0dRrL75vk3k23OA==", - "engines": { - "node": ">=6" - } - }, - "node_modules/lodash": { - "version": "4.17.21", - "resolved": "https://registry.npmjs.org/lodash/-/lodash-4.17.21.tgz", - "integrity": "sha512-v2kDEe57lecTulaDIuNTPy3Ry4gLGJ6Z1O3vE1krgXZNrsQ+LFTGHVxVjcXPs17LhbZVGedAJv8XZ1tvj5FvSg==" - }, - "node_modules/lodash.get": { - "version": "4.4.2", - "resolved": "https://registry.npmjs.org/lodash.get/-/lodash.get-4.4.2.tgz", - "integrity": "sha1-LRd/ZS+jHpObRDjVNBSZ36OCXpk=" - }, - "node_modules/log-update": { - "version": "4.0.0", - "resolved": "https://registry.npmjs.org/log-update/-/log-update-4.0.0.tgz", - "integrity": "sha512-9fkkDevMefjg0mmzWFBW8YkFP91OrizzkW3diF7CpG+S2EYdy4+TVfGwz1zeF8x7hCx1ovSPTOE9Ngib74qqUg==", - "dependencies": { - "ansi-escapes": "^4.3.0", - "cli-cursor": "^3.1.0", - "slice-ansi": "^4.0.0", - "wrap-ansi": "^6.2.0" - }, - "engines": { - "node": ">=10" - }, - "funding": { - "url": "https://github.com/sponsors/sindresorhus" - } - }, - "node_modules/mimic-fn": { - "version": "2.1.0", - "resolved": "https://registry.npmjs.org/mimic-fn/-/mimic-fn-2.1.0.tgz", - "integrity": "sha512-OqbOk5oEQeAZ8WXWydlu9HJjz9WVdEIvamMCcXmuqUYjTknH/sqsWvhQ3vgwKFRR1HpjvNBKQ37nbJgYzGqGcg==", - "engines": { - "node": ">=6" - } - }, - "node_modules/onetime": { - "version": "5.1.2", - "resolved": "https://registry.npmjs.org/onetime/-/onetime-5.1.2.tgz", - "integrity": "sha512-kbpaSSGJTWdAY5KPVeMOKXSrPtr8C8C7wodJbcsd51jRnmD+GZu8Y0VoU6Dm5Z4vWr0Ig/1NKuWRKf7j5aaYSg==", - "dependencies": { - "mimic-fn": "^2.1.0" - }, - "engines": { - "node": ">=6" - }, - "funding": { - "url": "https://github.com/sponsors/sindresorhus" - } - }, - "node_modules/platform": { - "version": "1.3.6", - "resolved": "https://registry.npmjs.org/platform/-/platform-1.3.6.tgz", - "integrity": "sha512-fnWVljUchTro6RiCFvCXBbNhJc2NijN7oIQxbwsyL0buWJPG85v81ehlHI9fXrJsMNgTofEoWIQeClKpgxFLrg==" - }, - "node_modules/prettier": { - "version": "2.4.1", - "resolved": "https://registry.npmjs.org/prettier/-/prettier-2.4.1.tgz", - "integrity": "sha512-9fbDAXSBcc6Bs1mZrDYb3XKzDLm4EXXL9sC1LqKP5rZkT6KRr/rf9amVUcODVXgguK/isJz0d0hP72WeaKWsvA==", - "bin": { - "prettier": "bin-prettier.js" - }, - "engines": { - "node": ">=10.13.0" - } - }, - "node_modules/redis-commands": { - "version": "1.7.0", - "resolved": "https://registry.npmjs.org/redis-commands/-/redis-commands-1.7.0.tgz", - "integrity": "sha512-nJWqw3bTFy21hX/CPKHth6sfhZbdiHP6bTawSgQBlKOVRG7EZkfHbbHwQJnrE4vsQf0CMNE+3gJ4Fmm16vdVlQ==" - }, - "node_modules/redis-errors": { - "version": "1.2.0", - "resolved": "https://registry.npmjs.org/redis-errors/-/redis-errors-1.2.0.tgz", - "integrity": "sha1-62LSrbFeTq9GEMBK/hUpOEJQq60=", - "engines": { - "node": ">=4" - } - }, - "node_modules/redis-parser": { - "version": "3.0.0", - "resolved": "https://registry.npmjs.org/redis-parser/-/redis-parser-3.0.0.tgz", - "integrity": "sha1-tm2CjNyv5rS4pCin3vTGvKwxyLQ=", - "dependencies": { - "redis-errors": "^1.0.0" - }, - "engines": { - "node": ">=4" - } - }, - "node_modules/restore-cursor": { - "version": "3.1.0", - "resolved": "https://registry.npmjs.org/restore-cursor/-/restore-cursor-3.1.0.tgz", - "integrity": "sha512-l+sSefzHpj5qimhFSE5a8nufZYAM3sBSVMAPtYkmC+4EH2anSGaEMXSD0izRQbu9nfyQ9y5JrVmp7E8oZrUjvA==", - "dependencies": { - "onetime": "^5.1.0", - "signal-exit": "^3.0.2" - }, - "engines": { - "node": ">=8" - } - }, - "node_modules/signal-exit": { - "version": "3.0.5", - "resolved": "https://registry.npmjs.org/signal-exit/-/signal-exit-3.0.5.tgz", - "integrity": "sha512-KWcOiKeQj6ZyXx7zq4YxSMgHRlod4czeBQZrPb8OKcohcqAXShm7E20kEMle9WBt26hFcAf0qLOcp5zmY7kOqQ==" - }, - "node_modules/slice-ansi": { - "version": "4.0.0", - "resolved": "https://registry.npmjs.org/slice-ansi/-/slice-ansi-4.0.0.tgz", - "integrity": "sha512-qMCMfhY040cVHT43K9BFygqYbUPFZKHOg7K73mtTWJRb8pyP3fzf4Ixd5SzdEJQ6MRUg/WBnOLxghZtKKurENQ==", - "dependencies": { - "ansi-styles": "^4.0.0", - "astral-regex": "^2.0.0", - "is-fullwidth-code-point": "^3.0.0" - }, - "engines": { - "node": ">=10" - }, - "funding": { - "url": "https://github.com/chalk/slice-ansi?sponsor=1" - } - }, - "node_modules/stats-median": { - "version": "1.0.1", - "resolved": "https://registry.npmjs.org/stats-median/-/stats-median-1.0.1.tgz", - "integrity": "sha512-IYsheLg6dasD3zT/w9+8Iq9tcIQqqu91ZIpJOnIEM25C3X/g4Tl8mhXwW2ZQpbrsJISr9+wizEYgsibN5/b32Q==" - }, - "node_modules/string-width": { - "version": "4.2.3", - "resolved": "https://registry.npmjs.org/string-width/-/string-width-4.2.3.tgz", - "integrity": "sha512-wKyQRQpjJ0sIp62ErSZdGsjMJWsap5oRNihHhu6G7JVO/9jIB6UyevL+tXuOqrng8j/cxKTWyWUwvSTriiZz/g==", - "dependencies": { - "emoji-regex": "^8.0.0", - "is-fullwidth-code-point": "^3.0.0", - "strip-ansi": "^6.0.1" - }, - "engines": { - "node": ">=8" - } - }, - "node_modules/strip-ansi": { - "version": "6.0.1", - "resolved": "https://registry.npmjs.org/strip-ansi/-/strip-ansi-6.0.1.tgz", - "integrity": "sha512-Y38VPSHcqkFrCpFnQ9vuSXmquuv5oXOKpGeT6aGrr3o3Gc9AlVa6JBfUSOCnbxGGZF+/0ooI7KrPuUSztUdU5A==", - "dependencies": { - "ansi-regex": "^5.0.1" - }, - "engines": { - "node": ">=8" - } - }, - "node_modules/type-fest": { - "version": "0.21.3", - "resolved": "https://registry.npmjs.org/type-fest/-/type-fest-0.21.3.tgz", - "integrity": "sha512-t0rzBq87m3fVcduHDUFhKmyyX+9eo6WQjZvf51Ea/M0Q7+T374Jp1aUiyUl0GKxp8M/OETVHSDvmkyPgvX+X2w==", - "engines": { - "node": ">=10" - }, - "funding": { - "url": "https://github.com/sponsors/sindresorhus" - } - }, - "node_modules/universalify": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/universalify/-/universalify-2.0.0.tgz", - "integrity": "sha512-hAZsKq7Yy11Zu1DE0OzWjw7nnLZmJZYTDZZyEFHZdUhV8FkH5MCfoU1XMaxXovpyW5nq5scPqq0ZDP9Zyl04oQ==", - "engines": { - "node": ">= 10.0.0" - } - }, - "node_modules/v3": { - "name": "redis", - "version": "3.1.2", - "resolved": "https://registry.npmjs.org/redis/-/redis-3.1.2.tgz", - "integrity": "sha512-grn5KoZLr/qrRQVwoSkmzdbw6pwF+/rwODtrOr6vuBRiR/f3rjSTGupbF90Zpqm2oenix8Do6RV7pYEkGwlKkw==", - "dependencies": { - "denque": "^1.5.0", - "redis-commands": "^1.7.0", - "redis-errors": "^1.2.0", - "redis-parser": "^3.0.0" - }, - "engines": { - "node": ">=10" - }, - "funding": { - "type": "opencollective", - "url": "https://opencollective.com/node-redis" - } - }, - "node_modules/v4": { - "resolved": "..", - "link": true - }, - "node_modules/wrap-ansi": { - "version": "6.2.0", - "resolved": "https://registry.npmjs.org/wrap-ansi/-/wrap-ansi-6.2.0.tgz", - "integrity": "sha512-r6lPcBGxZXlIcymEu7InxDMhdW0KDxpLgoFLcguasxCaJ/SOIZwINatK9KY/tf+ZrlywOKU0UDj3ATXUBfxJXA==", - "dependencies": { - "ansi-styles": "^4.0.0", - "string-width": "^4.1.0", - "strip-ansi": "^6.0.0" - }, - "engines": { - "node": ">=8" - } - } - }, - "dependencies": { - "@arrows/array": { - "version": "1.4.1", - "resolved": "https://registry.npmjs.org/@arrows/array/-/array-1.4.1.tgz", - "integrity": "sha512-MGYS8xi3c4tTy1ivhrVntFvufoNzje0PchjEz6G/SsWRgUKxL4tKwS6iPdO8vsaJYldagAeWMd5KRD0aX3Q39g==", - "requires": { - "@arrows/composition": "^1.2.2" - } - }, - "@arrows/composition": { - "version": "1.2.2", - "resolved": "https://registry.npmjs.org/@arrows/composition/-/composition-1.2.2.tgz", - "integrity": "sha512-9fh1yHwrx32lundiB3SlZ/VwuStPB4QakPsSLrGJFH6rCXvdrd060ivAZ7/2vlqPnEjBkPRRXOcG1YOu19p2GQ==" - }, - "@arrows/dispatch": { - "version": "1.0.3", - "resolved": "https://registry.npmjs.org/@arrows/dispatch/-/dispatch-1.0.3.tgz", - "integrity": "sha512-v/HwvrFonitYZM2PmBlAlCqVqxrkIIoiEuy5bQgn0BdfvlL0ooSBzcPzTMrtzY8eYktPyYcHg8fLbSgyybXEqw==", - "requires": { - "@arrows/composition": "^1.2.2" - } - }, - "@arrows/error": { - "version": "1.0.2", - "resolved": "https://registry.npmjs.org/@arrows/error/-/error-1.0.2.tgz", - "integrity": "sha512-yvkiv1ay4Z3+Z6oQsUkedsQm5aFdyPpkBUQs8vejazU/RmANABx6bMMcBPPHI4aW43VPQmXFfBzr/4FExwWTEA==" - }, - "@arrows/multimethod": { - "version": "1.1.7", - "resolved": "https://registry.npmjs.org/@arrows/multimethod/-/multimethod-1.1.7.tgz", - "integrity": "sha512-EjHD3XuGAV4G28rm7mu8k7zQJh/EOizh104/p9i2ofGcnL5mgKONFH/Bq6H3SJjM+WDAlKcR9WBpNhaAKCnH2g==", - "requires": { - "@arrows/array": "^1.4.0", - "@arrows/composition": "^1.2.2", - "@arrows/error": "^1.0.2", - "fast-deep-equal": "^3.1.1" - } - }, - "ansi-escapes": { - "version": "4.3.2", - "resolved": "https://registry.npmjs.org/ansi-escapes/-/ansi-escapes-4.3.2.tgz", - "integrity": "sha512-gKXj5ALrKWQLsYG9jlTRmR/xKluxHV+Z9QEwNIgCfM1/uwPMCuzVVnh5mwTd+OuBZcwSIMbqssNWRm1lE51QaQ==", - "requires": { - "type-fest": "^0.21.3" - } - }, - "ansi-regex": { - "version": "5.0.1", - "resolved": "https://registry.npmjs.org/ansi-regex/-/ansi-regex-5.0.1.tgz", - "integrity": "sha512-quJQXlTSUGL2LH9SUXo8VwsY4soanhgo6LNSm84E1LBcE8s3O0wpdiRzyR9z/ZZJMlMWv37qOOb9pdJlMUEKFQ==" - }, - "ansi-styles": { - "version": "4.3.0", - "resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-4.3.0.tgz", - "integrity": "sha512-zbB9rCJAT1rbjiVDb2hqKFHNYLxgtk8NURxZ3IZwD3F6NtxbXZQCnnSi1Lkx+IDohdPlFp222wVALIheZJQSEg==", - "requires": { - "color-convert": "^2.0.1" - } - }, - "astral-regex": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/astral-regex/-/astral-regex-2.0.0.tgz", - "integrity": "sha512-Z7tMw1ytTXt5jqMcOP+OQteU1VuNK9Y02uuJtKQ1Sv69jXQKKg5cibLwGJow8yzZP+eAc18EmLGPal0bp36rvQ==" - }, - "at-least-node": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/at-least-node/-/at-least-node-1.0.0.tgz", - "integrity": "sha512-+q/t7Ekv1EDY2l6Gda6LLiX14rU9TV20Wa3ofeQmwPFZbOMo9DXrLbOjFaaclkXKWidIaopwAObQDqwWtGUjqg==" - }, - "benchmark": { - "version": "2.1.4", - "resolved": "https://registry.npmjs.org/benchmark/-/benchmark-2.1.4.tgz", - "integrity": "sha1-CfPeMckWQl1JjMLuVloOvzwqVik=", - "requires": { - "lodash": "^4.17.4", - "platform": "^1.3.3" - } - }, - "benny": { - "version": "3.6.15", - "resolved": "https://registry.npmjs.org/benny/-/benny-3.6.15.tgz", - "integrity": "sha512-kq6XVGGYVou3Y8KNPs3SEF881vi5fJ8sIf9w69D2rreiNfRicWVWK6u6/mObMw6BiexoHHumtipn5gcu0Tngng==", - "requires": { - "@arrows/composition": "^1.0.0", - "@arrows/dispatch": "^1.0.2", - "@arrows/multimethod": "^1.1.6", - "benchmark": "^2.1.4", - "fs-extra": "^9.0.1", - "json2csv": "^5.0.4", - "kleur": "^4.1.3", - "log-update": "^4.0.0", - "prettier": "^2.1.2", - "stats-median": "^1.0.1" - } - }, - "cli-cursor": { - "version": "3.1.0", - "resolved": "https://registry.npmjs.org/cli-cursor/-/cli-cursor-3.1.0.tgz", - "integrity": "sha512-I/zHAwsKf9FqGoXM4WWRACob9+SNukZTd94DWF57E4toouRulbCxcUh6RKUEOQlYTHJnzkPMySvPNaaSLNfLZw==", - "requires": { - "restore-cursor": "^3.1.0" - } - }, - "color-convert": { - "version": "2.0.1", - "resolved": "https://registry.npmjs.org/color-convert/-/color-convert-2.0.1.tgz", - "integrity": "sha512-RRECPsj7iu/xb5oKYcsFHSppFNnsj/52OVTRKb4zP5onXwVF3zVmmToNcOfGC+CRDpfK/U584fMg38ZHCaElKQ==", - "requires": { - "color-name": "~1.1.4" - } - }, - "color-name": { - "version": "1.1.4", - "resolved": "https://registry.npmjs.org/color-name/-/color-name-1.1.4.tgz", - "integrity": "sha512-dOy+3AuW3a2wNbZHIuMZpTcgjGuLU/uBL/ubcZF9OXbDo8ff4O8yVp5Bf0efS8uEoYo5q4Fx7dY9OgQGXgAsQA==" - }, - "commander": { - "version": "6.2.1", - "resolved": "https://registry.npmjs.org/commander/-/commander-6.2.1.tgz", - "integrity": "sha512-U7VdrJFnJgo4xjrHpTzu0yrHPGImdsmD95ZlgYSEajAn2JKzDhDTPG9kBTefmObL2w/ngeZnilk+OV9CG3d7UA==" - }, - "denque": { - "version": "1.5.1", - "resolved": "https://registry.npmjs.org/denque/-/denque-1.5.1.tgz", - "integrity": "sha512-XwE+iZ4D6ZUB7mfYRMb5wByE8L74HCn30FBN7sWnXksWc1LO1bPDl67pBR9o/kC4z/xSNAwkMYcGgqDV3BE3Hw==" - }, - "emoji-regex": { - "version": "8.0.0", - "resolved": "https://registry.npmjs.org/emoji-regex/-/emoji-regex-8.0.0.tgz", - "integrity": "sha512-MSjYzcWNOA0ewAHpz0MxpYFvwg6yjy1NG3xteoqz644VCo/RPgnr1/GGt+ic3iJTzQ8Eu3TdM14SawnVUmGE6A==" - }, - "fast-deep-equal": { - "version": "3.1.3", - "resolved": "https://registry.npmjs.org/fast-deep-equal/-/fast-deep-equal-3.1.3.tgz", - "integrity": "sha512-f3qQ9oQy9j2AhBe/H9VC91wLmKBCCU/gDOnKNAYG5hswO7BLKj09Hc5HYNz9cGI++xlpDCIgDaitVs03ATR84Q==" - }, - "fs-extra": { - "version": "9.1.0", - "resolved": "https://registry.npmjs.org/fs-extra/-/fs-extra-9.1.0.tgz", - "integrity": "sha512-hcg3ZmepS30/7BSFqRvoo3DOMQu7IjqxO5nCDt+zM9XWjb33Wg7ziNT+Qvqbuc3+gWpzO02JubVyk2G4Zvo1OQ==", - "requires": { - "at-least-node": "^1.0.0", - "graceful-fs": "^4.2.0", - "jsonfile": "^6.0.1", - "universalify": "^2.0.0" - } - }, - "graceful-fs": { - "version": "4.2.8", - "resolved": "https://registry.npmjs.org/graceful-fs/-/graceful-fs-4.2.8.tgz", - "integrity": "sha512-qkIilPUYcNhJpd33n0GBXTB1MMPp14TxEsEs0pTrsSVucApsYzW5V+Q8Qxhik6KU3evy+qkAAowTByymK0avdg==" - }, - "is-fullwidth-code-point": { - "version": "3.0.0", - "resolved": "https://registry.npmjs.org/is-fullwidth-code-point/-/is-fullwidth-code-point-3.0.0.tgz", - "integrity": "sha512-zymm5+u+sCsSWyD9qNaejV3DFvhCKclKdizYaJUuHA83RLjb7nSuGnddCHGv0hk+KY7BMAlsWeK4Ueg6EV6XQg==" - }, - "json2csv": { - "version": "5.0.6", - "resolved": "https://registry.npmjs.org/json2csv/-/json2csv-5.0.6.tgz", - "integrity": "sha512-0/4Lv6IenJV0qj2oBdgPIAmFiKKnh8qh7bmLFJ+/ZZHLjSeiL3fKKGX3UryvKPbxFbhV+JcYo9KUC19GJ/Z/4A==", - "requires": { - "commander": "^6.1.0", - "jsonparse": "^1.3.1", - "lodash.get": "^4.4.2" - } - }, - "jsonfile": { - "version": "6.1.0", - "resolved": "https://registry.npmjs.org/jsonfile/-/jsonfile-6.1.0.tgz", - "integrity": "sha512-5dgndWOriYSm5cnYaJNhalLNDKOqFwyDB/rr1E9ZsGciGvKPs8R2xYGCacuf3z6K1YKDz182fd+fY3cn3pMqXQ==", - "requires": { - "graceful-fs": "^4.1.6", - "universalify": "^2.0.0" - } - }, - "jsonparse": { - "version": "1.3.1", - "resolved": "https://registry.npmjs.org/jsonparse/-/jsonparse-1.3.1.tgz", - "integrity": "sha1-P02uSpH6wxX3EGL4UhzCOfE2YoA=" - }, - "kleur": { - "version": "4.1.4", - "resolved": "https://registry.npmjs.org/kleur/-/kleur-4.1.4.tgz", - "integrity": "sha512-8QADVssbrFjivHWQU7KkMgptGTl6WAcSdlbBPY4uNF+mWr6DGcKrvY2w4FQJoXch7+fKMjj0dRrL75vk3k23OA==" - }, - "lodash": { - "version": "4.17.21", - "resolved": "https://registry.npmjs.org/lodash/-/lodash-4.17.21.tgz", - "integrity": "sha512-v2kDEe57lecTulaDIuNTPy3Ry4gLGJ6Z1O3vE1krgXZNrsQ+LFTGHVxVjcXPs17LhbZVGedAJv8XZ1tvj5FvSg==" - }, - "lodash.get": { - "version": "4.4.2", - "resolved": "https://registry.npmjs.org/lodash.get/-/lodash.get-4.4.2.tgz", - "integrity": "sha1-LRd/ZS+jHpObRDjVNBSZ36OCXpk=" - }, - "log-update": { - "version": "4.0.0", - "resolved": "https://registry.npmjs.org/log-update/-/log-update-4.0.0.tgz", - "integrity": "sha512-9fkkDevMefjg0mmzWFBW8YkFP91OrizzkW3diF7CpG+S2EYdy4+TVfGwz1zeF8x7hCx1ovSPTOE9Ngib74qqUg==", - "requires": { - "ansi-escapes": "^4.3.0", - "cli-cursor": "^3.1.0", - "slice-ansi": "^4.0.0", - "wrap-ansi": "^6.2.0" - } - }, - "mimic-fn": { - "version": "2.1.0", - "resolved": "https://registry.npmjs.org/mimic-fn/-/mimic-fn-2.1.0.tgz", - "integrity": "sha512-OqbOk5oEQeAZ8WXWydlu9HJjz9WVdEIvamMCcXmuqUYjTknH/sqsWvhQ3vgwKFRR1HpjvNBKQ37nbJgYzGqGcg==" - }, - "onetime": { - "version": "5.1.2", - "resolved": "https://registry.npmjs.org/onetime/-/onetime-5.1.2.tgz", - "integrity": "sha512-kbpaSSGJTWdAY5KPVeMOKXSrPtr8C8C7wodJbcsd51jRnmD+GZu8Y0VoU6Dm5Z4vWr0Ig/1NKuWRKf7j5aaYSg==", - "requires": { - "mimic-fn": "^2.1.0" - } - }, - "platform": { - "version": "1.3.6", - "resolved": "https://registry.npmjs.org/platform/-/platform-1.3.6.tgz", - "integrity": "sha512-fnWVljUchTro6RiCFvCXBbNhJc2NijN7oIQxbwsyL0buWJPG85v81ehlHI9fXrJsMNgTofEoWIQeClKpgxFLrg==" - }, - "prettier": { - "version": "2.4.1", - "resolved": "https://registry.npmjs.org/prettier/-/prettier-2.4.1.tgz", - "integrity": "sha512-9fbDAXSBcc6Bs1mZrDYb3XKzDLm4EXXL9sC1LqKP5rZkT6KRr/rf9amVUcODVXgguK/isJz0d0hP72WeaKWsvA==" - }, - "redis-commands": { - "version": "1.7.0", - "resolved": "https://registry.npmjs.org/redis-commands/-/redis-commands-1.7.0.tgz", - "integrity": "sha512-nJWqw3bTFy21hX/CPKHth6sfhZbdiHP6bTawSgQBlKOVRG7EZkfHbbHwQJnrE4vsQf0CMNE+3gJ4Fmm16vdVlQ==" - }, - "redis-errors": { - "version": "1.2.0", - "resolved": "https://registry.npmjs.org/redis-errors/-/redis-errors-1.2.0.tgz", - "integrity": "sha1-62LSrbFeTq9GEMBK/hUpOEJQq60=" - }, - "redis-parser": { - "version": "3.0.0", - "resolved": "https://registry.npmjs.org/redis-parser/-/redis-parser-3.0.0.tgz", - "integrity": "sha1-tm2CjNyv5rS4pCin3vTGvKwxyLQ=", - "requires": { - "redis-errors": "^1.0.0" - } - }, - "restore-cursor": { - "version": "3.1.0", - "resolved": "https://registry.npmjs.org/restore-cursor/-/restore-cursor-3.1.0.tgz", - "integrity": "sha512-l+sSefzHpj5qimhFSE5a8nufZYAM3sBSVMAPtYkmC+4EH2anSGaEMXSD0izRQbu9nfyQ9y5JrVmp7E8oZrUjvA==", - "requires": { - "onetime": "^5.1.0", - "signal-exit": "^3.0.2" - } - }, - "signal-exit": { - "version": "3.0.5", - "resolved": "https://registry.npmjs.org/signal-exit/-/signal-exit-3.0.5.tgz", - "integrity": "sha512-KWcOiKeQj6ZyXx7zq4YxSMgHRlod4czeBQZrPb8OKcohcqAXShm7E20kEMle9WBt26hFcAf0qLOcp5zmY7kOqQ==" - }, - "slice-ansi": { - "version": "4.0.0", - "resolved": "https://registry.npmjs.org/slice-ansi/-/slice-ansi-4.0.0.tgz", - "integrity": "sha512-qMCMfhY040cVHT43K9BFygqYbUPFZKHOg7K73mtTWJRb8pyP3fzf4Ixd5SzdEJQ6MRUg/WBnOLxghZtKKurENQ==", - "requires": { - "ansi-styles": "^4.0.0", - "astral-regex": "^2.0.0", - "is-fullwidth-code-point": "^3.0.0" - } - }, - "stats-median": { - "version": "1.0.1", - "resolved": "https://registry.npmjs.org/stats-median/-/stats-median-1.0.1.tgz", - "integrity": "sha512-IYsheLg6dasD3zT/w9+8Iq9tcIQqqu91ZIpJOnIEM25C3X/g4Tl8mhXwW2ZQpbrsJISr9+wizEYgsibN5/b32Q==" - }, - "string-width": { - "version": "4.2.3", - "resolved": "https://registry.npmjs.org/string-width/-/string-width-4.2.3.tgz", - "integrity": "sha512-wKyQRQpjJ0sIp62ErSZdGsjMJWsap5oRNihHhu6G7JVO/9jIB6UyevL+tXuOqrng8j/cxKTWyWUwvSTriiZz/g==", - "requires": { - "emoji-regex": "^8.0.0", - "is-fullwidth-code-point": "^3.0.0", - "strip-ansi": "^6.0.1" - } - }, - "strip-ansi": { - "version": "6.0.1", - "resolved": "https://registry.npmjs.org/strip-ansi/-/strip-ansi-6.0.1.tgz", - "integrity": "sha512-Y38VPSHcqkFrCpFnQ9vuSXmquuv5oXOKpGeT6aGrr3o3Gc9AlVa6JBfUSOCnbxGGZF+/0ooI7KrPuUSztUdU5A==", - "requires": { - "ansi-regex": "^5.0.1" - } - }, - "type-fest": { - "version": "0.21.3", - "resolved": "https://registry.npmjs.org/type-fest/-/type-fest-0.21.3.tgz", - "integrity": "sha512-t0rzBq87m3fVcduHDUFhKmyyX+9eo6WQjZvf51Ea/M0Q7+T374Jp1aUiyUl0GKxp8M/OETVHSDvmkyPgvX+X2w==" - }, - "universalify": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/universalify/-/universalify-2.0.0.tgz", - "integrity": "sha512-hAZsKq7Yy11Zu1DE0OzWjw7nnLZmJZYTDZZyEFHZdUhV8FkH5MCfoU1XMaxXovpyW5nq5scPqq0ZDP9Zyl04oQ==" - }, - "v3": { - "version": "npm:redis@3.1.2", - "resolved": "https://registry.npmjs.org/redis/-/redis-3.1.2.tgz", - "integrity": "sha512-grn5KoZLr/qrRQVwoSkmzdbw6pwF+/rwODtrOr6vuBRiR/f3rjSTGupbF90Zpqm2oenix8Do6RV7pYEkGwlKkw==", - "requires": { - "denque": "^1.5.0", - "redis-commands": "^1.7.0", - "redis-errors": "^1.2.0", - "redis-parser": "^3.0.0" - } - }, - "v4": { - "version": "file:..", - "requires": { - "@istanbuljs/nyc-config-typescript": "^1.0.1", - "@tsconfig/node12": "^1.0.9", - "@types/mocha": "^9.0.0", - "@types/node": "^16.10.3", - "@types/sinon": "^10.0.4", - "@types/which": "^2.0.1", - "@types/yallist": "^4.0.1", - "cluster-key-slot": "1.1.0", - "generic-pool": "3.8.2", - "mocha": "^9.1.2", - "nyc": "^15.1.0", - "redis-parser": "3.0.0", - "release-it": "^14.11.6", - "sinon": "^11.1.2", - "source-map-support": "^0.5.20", - "ts-node": "^10.3.0", - "typedoc": "^0.22.5", - "typedoc-github-wiki-theme": "^0.6.0", - "typedoc-plugin-markdown": "^3.11.3", - "typescript": "^4.4.3", - "which": "^2.0.2", - "yallist": "4.0.0" - } - }, - "wrap-ansi": { - "version": "6.2.0", - "resolved": "https://registry.npmjs.org/wrap-ansi/-/wrap-ansi-6.2.0.tgz", - "integrity": "sha512-r6lPcBGxZXlIcymEu7InxDMhdW0KDxpLgoFLcguasxCaJ/SOIZwINatK9KY/tf+ZrlywOKU0UDj3ATXUBfxJXA==", - "requires": { - "ansi-styles": "^4.0.0", - "string-width": "^4.1.0", - "strip-ansi": "^6.0.0" - } - } - } -} diff --git a/benchmark/package.json b/benchmark/package.json deleted file mode 100644 index ab874090c4b..00000000000 --- a/benchmark/package.json +++ /dev/null @@ -1,17 +0,0 @@ -{ - "name": "benchmark", - "private": true, - "description": "", - "main": "index.js", - "type": "module", - "scripts": { - "start": "node ./" - }, - "author": "", - "license": "ISC", - "dependencies": { - "benny": "3.6.15", - "v3": "npm:redis@3.1.2", - "v4": "file:../" - } -} diff --git a/docs/client-configuration.md b/docs/client-configuration.md index 11fdb0a6819..1dbbdd8cba2 100644 --- a/docs/client-configuration.md +++ b/docs/client-configuration.md @@ -15,7 +15,7 @@ | 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](https://redis.io/modules) to include (TODO - document) | +| modules | | Object defining which [Redis Modules](../.github/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 | diff --git a/docs/clustering.md b/docs/clustering.md new file mode 100644 index 00000000000..3b5ef94a5c7 --- /dev/null +++ b/docs/clustering.md @@ -0,0 +1,56 @@ +# Clustering + +## Basic Example + +Connecting to a cluster is a bit different. Create the client by specifying some (or all) of the nodes in your cluster and then use it like a regular client instance: + +```typescript +import { createCluster } from 'redis'; + +(async () => { + const cluster = createCluster({ + rootNodes: [ + { + url: 'redis://10.0.0.1:30001' + }, + { + url: 'redis://10.0.0.2:30002' + } + ] + }); + + cluster.on('error', (err) => console.log('Redis Cluster Error', err)); + + await cluster.connect(); + + await cluster.set('key', 'value'); + const value = await cluster.get('key'); +})(); +``` + +## `createCluster` configuration + +> See the [client configuration](./client-configuration.md) page for the `rootNodes` and `defaults` configuration schemas. + +| Property | Default | Description | +|------------------------|---------|---------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------| +| rootNodes | | An array of root nodes that are part of the cluster, which will be used to get the cluster topology. Each element in the array is a client configuration object. There is no need to specify every node in the cluster, 3 should be enough to reliably connect and obtain the cluster configuration from the server | +| defaults | | The default configuration values for every client in the cluster. Use this for example when specifying an ACL user to connect with | +| useReplicas | `false` | When `true`, distribute load by executing readonly commands (such as `GET`, `GEOSEARCH`, etc.) across all cluster nodes. When `false`, only use master nodes | +| maxCommandRedirections | `16` | The maximum number of times a command will be redirected due to `MOVED` or `ASK` errors | +| 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 + +### Commands that operate on Redis Keys + +Commands such as `GET`, `SET`, etc. will be routed by the first key, for instance `MGET 1 2 3` will be routed by the key `1`. + +### [Server Commands](https://redis.io/commands#server) + +Admin commands such as `MEMORY STATS`, `FLUSHALL`, etc. are not attached to the cluster, and should be executed on a specific node using `.getSlot()` or `.getAllMasters()`. + +### "Forwarded Commands" + +Some commands (e.g. `PUBLISH`) are forwarded to other cluster nodes by the Redis server. The client will send these commands to a random node in order to spread the load across the cluster. diff --git a/docs/isolated-execution.md b/docs/isolated-execution.md index 78b34252a0f..1fd16b8f462 100644 --- a/docs/isolated-execution.md +++ b/docs/isolated-execution.md @@ -10,7 +10,7 @@ Below are several examples of how to use isolated execution. > NOTE: Behind the scences we're using [`generic-pool`](https://www.npmjs.com/package/generic-pool) to provide a pool of connections that can be isolated. Go there to learn more. -## The Simple Secnario +## The Simple Scenario This just isolates execution on a single connection. Do what you want with that connection: diff --git a/examples/.gitignore b/examples/.gitignore new file mode 100644 index 00000000000..d8b83df9cdb --- /dev/null +++ b/examples/.gitignore @@ -0,0 +1 @@ +package-lock.json diff --git a/examples/README.md b/examples/README.md new file mode 100644 index 00000000000..aef0b38bdbb --- /dev/null +++ b/examples/README.md @@ -0,0 +1,76 @@ +# Node Redis: Examples + +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 | + +## Contributing + +We'd love to see more examples here. If you have an idea that you'd like to see included here, submit a Pull Request and we'll be sure to review it! Don't forget to check out our [contributing guide](../CONTRIBUTING.md). + +## Setup + +To set up the examples folder so that you can run an example / develop one of your own: + +``` +$ git clone https://github.com/redis/node-redis.git +$ cd node-redis +$ npm install -ws && npm run build +$ cd examples +$ npm install +``` + +### Coding Guidelines for Examples + +When adding a new example, please follow these guidelines: + +* Add your code in a single JavaScript or TypeScript file per example, directly in the `examples` folder +* Do not introduce other dependencies in your example +* Give your `.js` file a meaningful name using `-` separators e.g. `adding-to-a-stream.js` / `adding-to-a-stream.ts` +* Indent your code using 2 spaces +* Use the single line `//` comment style and comment your code +* Add a comment at the top of your `.js` / `.ts` file describing what your example does +* Add a comment at the top of your `.js` / `.ts` file describing any Redis commands that need to be run to set up data for your example (try and keep this minimal) +* Use semicolons +* Use `async` and `await` +* Use single quotes, `'hello'` not `"hello"` +* Place your example code in a single `async` function where possible, named according to the file name e.g. `add-to-stream.js` would contain `const addtoStream = async () => { ... };`, and call this function at the end of the file e.g. `addToStream();` +* Unless your example requires a connection string, assume Redis is on the default localhost port 6379 with no password +* Use meaningful example data, let's not use `foo`, `bar`, `baz` etc! +* Leave an empty line at the end of your `.js` file +* Update this `README.md` file to add your example to the table + +Use [connect-as-acl-user.js](./connect-as-acl-user.js) as a guide to develop a well formatted example script. + +### Example Template + +Here's a starter template for adding a new example, imagine this is stored in `do-something.js`: + +```javascript +// This comment should describe what the example does +// and can extend to multiple lines. + +// Set up the data in redis-cli using these commands: +// + +import { createClient } from 'redis'; + +async function doSomething() { + const client = createClient(); + + await client.connect(); + + // Add your example code here... + + await client.quit(); +} + +doSomething(); +``` diff --git a/examples/blocking-list-pop.js b/examples/blocking-list-pop.js new file mode 100644 index 00000000000..59dde3274a3 --- /dev/null +++ b/examples/blocking-list-pop.js @@ -0,0 +1,32 @@ +// This example shows how to use the blocking LPUSH command. + +// This code shows how to run with isolation the blPop Command to block the script while waiting for a value to be pushed to the list. +// The script will be blocked until the LPUSH command is executed. +// After which we log the list and quit the client. + +import { createClient, commandOptions } from 'redis'; + +async function blockingListPop() { + const client = createClient(); + + await client.connect(); + + const keyName = 'keyName'; + + const blpopPromise = client.blPop( + commandOptions({ isolated: true }), + keyName, + 0 + ); + + await client.lPush(keyName, 'value'); + + await blpopPromise; + + console.log('blpopPromise resolved'); + console.log(keyName); + + await client.quit(); +} + +blockingListPop(); diff --git a/examples/command-with-modifiers.js b/examples/command-with-modifiers.js new file mode 100644 index 00000000000..78b1d2e3d2f --- /dev/null +++ b/examples/command-with-modifiers.js @@ -0,0 +1,31 @@ +// Define a custom script that shows example of SET command +// with several modifiers. + +import { createClient } from 'redis'; + +async function commandWithModifiers() { + const client = createClient(); + + await client.connect(); + await client.del('mykey'); + + let result = await client.set('mykey', 'myvalue', { + EX: 60, + GET: true + }); + + console.log(result); //nil + + result = await client.set('mykey', 'newvalue', { + EX: 60, + GET: true + } + ); + + console.log(result); //myvalue + + await client.quit(); +} + +commandWithModifiers(); + diff --git a/examples/connect-as-acl-user.js b/examples/connect-as-acl-user.js new file mode 100644 index 00000000000..26e1e443b0a --- /dev/null +++ b/examples/connect-as-acl-user.js @@ -0,0 +1,30 @@ +// Connect to Redis 6.x as an ACL user. Attempt to run a command +// that the user is allowed to execute, and a command that the +// user is not allowed to execute. + +// Create the test user in redis-cli with this command: +// acl setuser testuser on >testpassword +ping + +import { createClient } from 'redis'; + +async function connectWithACLUser() { + const client = createClient({ + url: 'redis://testuser:testpassword@127.0.0.1:6379' + }); + + await client.connect(); + + // Returns PONG + console.log(`Response from PING command: ${await client.ping()}`); + + try { + // This will error as this user is not allowed to run this command... + console.log(`Response from GET command: ${await client.get('somekey')}`); + } catch (e) { + console.log(`GET command failed: ${e.message}`); + } + + await client.quit(); +} + +connectWithACLUser(); diff --git a/examples/lua-multi-incr.js b/examples/lua-multi-incr.js new file mode 100644 index 00000000000..ec433c27ea2 --- /dev/null +++ b/examples/lua-multi-incr.js @@ -0,0 +1,31 @@ +// Define a custome lua script that accepts two keys and an amount to +// increment each of them by + +import { createClient, defineScript } from 'redis'; + +async function luaMultiIncr() { + const client = createClient({ + scripts: { + mincr: defineScript({ + NUMBER_OF_KEYS: 2, + SCRIPT: + 'return {' + + 'redis.pcall("INCRBY", KEYS[1], ARGV[1]),' + + 'redis.pcall("INCRBY", KEYS[2], ARGV[1])' + + '}', + transformArguments(key1, key2, increment) { + return [key1, key2, increment.toString()]; + }, + }), + }, + }); + + await client.connect(); + + await client.set('mykey', '5'); + console.log(await client.mincr('mykey', 'myotherkey', 10)); // [ 15, 10 ] + + await client.quit(); +} + +luaMultiIncr(); diff --git a/examples/package.json b/examples/package.json new file mode 100644 index 00000000000..65ba1442f7e --- /dev/null +++ b/examples/package.json @@ -0,0 +1,12 @@ +{ + "name": "node-redis-examples", + "version": "1.0.0", + "description": "node-redis 4 example script", + "main": "index.js", + "private": true, + "type": "module", + "dependencies": { + "redis": "../" + } +} + diff --git a/examples/search+json.js b/examples/search+json.js new file mode 100644 index 00000000000..adc298289cd --- /dev/null +++ b/examples/search+json.js @@ -0,0 +1,74 @@ +// 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(); diff --git a/examples/set-scan.js b/examples/set-scan.js new file mode 100644 index 00000000000..3cca05b152d --- /dev/null +++ b/examples/set-scan.js @@ -0,0 +1,19 @@ +// An example script that shows how to use the SSCAN iterator functionality to retrieve the contents of a Redis set. +// Create the set in redis-cli with this command: +// sadd setName a b c d e f g h i j k l m n o p q + +import { createClient } from 'redis'; + +async function setScan() { + const client = createClient(); + await client.connect(); + + const setName = 'setName'; + for await (const member of client.sScanIterator(setName)) { + console.log(member); + } + + await client.quit(); +} + +setScan(); diff --git a/index.ts b/index.ts index fb448f989c7..8fb31edb08a 100644 --- a/index.ts +++ b/index.ts @@ -1,8 +1,33 @@ -import RedisClient from './lib/client'; -import RedisCluster from './lib/cluster'; +import { createClient as _createClient, createCluster as _createCluster } from '@node-redis/client'; +import { RedisScripts } from '@node-redis/client/dist/lib/commands'; +import { RedisClientOptions, RedisClientType } from '@node-redis/client/dist/lib/client'; +import { RedisClusterOptions, RedisClusterType } from '@node-redis/client/dist/lib/cluster'; +import RedisJSON from '@node-redis/json'; +import RediSearch from '@node-redis/search'; -export const createClient = RedisClient.create; +export * from '@node-redis/client'; +export * from '@node-redis/json'; +export * from '@node-redis/search'; -export const commandOptions = RedisClient.commandOptions; +const modules = { + json: RedisJSON, + ft: RediSearch +}; -export const createCluster = RedisCluster.create; +export function createClient>( + options?: Omit, 'modules'> +): RedisClientType { + return _createClient({ + ...options, + modules + }); +} + +export function createCluster>( + options: Omit, 'modules'> +): RedisClusterType { + return _createCluster({ + ...options, + modules + }); +} diff --git a/lib/cluster/index.spec.ts b/lib/cluster/index.spec.ts deleted file mode 100644 index f2227395385..00000000000 --- a/lib/cluster/index.spec.ts +++ /dev/null @@ -1,114 +0,0 @@ -import { strict as assert } from 'assert'; -import RedisCluster from '.'; -import { defineScript } from '../lua-script'; -import { itWithCluster, itWithDedicatedCluster, TestRedisClusters, TEST_REDIS_CLUSTERES } from '../test-utils'; -import calculateSlot from 'cluster-key-slot'; -import { ClusterSlotStates } from '../commands/CLUSTER_SETSLOT'; - -describe('Cluster', () => { - it('sendCommand', async () => { - const cluster = RedisCluster.create({ - ...TEST_REDIS_CLUSTERES[TestRedisClusters.OPEN], - useReplicas: true - }); - - await cluster.connect(); - - try { - await cluster.publish('channel', 'message'); - await cluster.set('a', 'b'); - await cluster.set('a{a}', 'bb'); - await cluster.set('aa', 'bb'); - await cluster.get('aa'); - await cluster.get('aa'); - await cluster.get('aa'); - await cluster.get('aa'); - } finally { - await cluster.disconnect(); - } - }); - - itWithCluster(TestRedisClusters.OPEN, 'multi', async cluster => { - const key = 'key'; - assert.deepEqual( - await cluster.multi() - .set(key, 'value') - .get(key) - .exec(), - ['OK', 'value'] - ); - }); - - it('scripts', async () => { - const cluster = RedisCluster.create({ - ...TEST_REDIS_CLUSTERES[TestRedisClusters.OPEN], - scripts: { - add: defineScript({ - NUMBER_OF_KEYS: 0, - SCRIPT: 'return ARGV[1] + 1;', - transformArguments(number: number): Array { - assert.equal(number, 1); - return [number.toString()]; - }, - transformReply(reply: number): number { - assert.equal(reply, 2); - return reply; - } - }) - } - }); - - await cluster.connect(); - - try { - assert.equal( - await cluster.add(1), - 2 - ); - } finally { - await cluster.disconnect(); - } - }); - - itWithDedicatedCluster('should handle live resharding', async cluster => { - const key = 'key', - value = 'value'; - await cluster.set(key, value); - - const slot = calculateSlot(key), - from = cluster.getSlotMaster(slot), - to = cluster.getMasters().find(node => node.id !== from.id); - - await to!.client.clusterSetSlot(slot, ClusterSlotStates.IMPORTING, from.id); - - // should be able to get the key from the original node before it was migrated - assert.equal( - await cluster.get(key), - value - ); - - await from.client.clusterSetSlot(slot, ClusterSlotStates.MIGRATING, to!.id); - - // should be able to get the key from the original node using the "ASKING" command - assert.equal( - await cluster.get(key), - value - ); - - const { port: toPort } = to!.client.options!.socket; - - await from.client.migrate( - '127.0.0.1', - toPort, - key, - 0, - 10 - ); - - // should be able to get the key from the new node - assert.equal( - await cluster.get(key), - value - ); - }); -}); diff --git a/lib/commands/ACL_GETUSER.spec.ts b/lib/commands/ACL_GETUSER.spec.ts deleted file mode 100644 index c43cdc364ae..00000000000 --- a/lib/commands/ACL_GETUSER.spec.ts +++ /dev/null @@ -1,27 +0,0 @@ -import { strict as assert } from 'assert'; -import { describeHandleMinimumRedisVersion, itWithClient, TestRedisServers } from '../test-utils'; -import { transformArguments } from './ACL_GETUSER'; - -describe('ACL GETUSER', () => { - describeHandleMinimumRedisVersion([6]); - - it('transformArguments', () => { - assert.deepEqual( - transformArguments('username'), - ['ACL', 'GETUSER', 'username'] - ); - }); - - itWithClient(TestRedisServers.OPEN, 'client.aclGetUser', async client => { - assert.deepEqual( - await client.aclGetUser('default'), - { - flags: ['on', 'allkeys', 'allchannels', 'allcommands', 'nopass'], - passwords: [], - commands: '+@all', - keys: ['*'], - channels: ['*'] - } - ); - }); -}); diff --git a/lib/commands/COMMAND.spec.ts b/lib/commands/COMMAND.spec.ts deleted file mode 100644 index 1f036dadc17..00000000000 --- a/lib/commands/COMMAND.spec.ts +++ /dev/null @@ -1,30 +0,0 @@ -import { strict as assert } from 'assert'; -import { itWithClient, TestRedisServers } from '../test-utils'; -import { transformArguments } from './COMMAND'; -import { CommandCategories, CommandFlags } from './generic-transformers'; - -describe('COMMAND', () => { - it('transformArguments', () => { - assert.deepEqual( - transformArguments(), - ['COMMAND'] - ); - }); - - itWithClient(TestRedisServers.OPEN, 'client.command', async client => { - assert.deepEqual( - (await client.command()).find(command => command.name === 'ping'), - { - name: 'ping', - arity: -1, - flags: new Set([CommandFlags.STALE, CommandFlags.FAST]), - firstKeyIndex: 0, - lastKeyIndex: 0, - step: 0, - categories: new Set([CommandCategories.FAST, CommandCategories.CONNECTION]) - } - ); - }, { - minimumRedisVersion: [6] - }); -}); diff --git a/lib/commands/COMMAND_INFO.spec.ts b/lib/commands/COMMAND_INFO.spec.ts deleted file mode 100644 index 59a61f6680a..00000000000 --- a/lib/commands/COMMAND_INFO.spec.ts +++ /dev/null @@ -1,30 +0,0 @@ -import { strict as assert } from 'assert'; -import { itWithClient, TestRedisServers } from '../test-utils'; -import { transformArguments } from './COMMAND_INFO'; -import { CommandCategories, CommandFlags } from './generic-transformers'; - -describe('COMMAND INFO', () => { - it('transformArguments', () => { - assert.deepEqual( - transformArguments(['PING']), - ['COMMAND', 'INFO', 'PING'] - ); - }); - - itWithClient(TestRedisServers.OPEN, 'client.commandInfo', async client => { - assert.deepEqual( - await client.commandInfo(['PING']), - [{ - name: 'ping', - arity: -1, - flags: new Set([CommandFlags.STALE, CommandFlags.FAST]), - firstKeyIndex: 0, - lastKeyIndex: 0, - step: 0, - categories: new Set([CommandCategories.FAST, CommandCategories.CONNECTION]) - }] - ); - }, { - minimumRedisVersion: [6] - }); -}); diff --git a/lib/commands/CONFIG_GET.ts b/lib/commands/CONFIG_GET.ts deleted file mode 100644 index 423683c13a4..00000000000 --- a/lib/commands/CONFIG_GET.ts +++ /dev/null @@ -1,7 +0,0 @@ -import { transformReplyTuples } from './generic-transformers'; - -export function transformArguments(parameter: string): Array { - return ['CONFIG', 'GET', parameter]; -} - -export const transformReply = transformReplyTuples; diff --git a/lib/commands/ZCOUNT.ts b/lib/commands/ZCOUNT.ts deleted file mode 100644 index a18ba0fd790..00000000000 --- a/lib/commands/ZCOUNT.ts +++ /dev/null @@ -1,16 +0,0 @@ -import { transformArgumentNumberInfinity } from './generic-transformers'; - -export const FIRST_KEY_INDEX = 1; - -export const IS_READ_ONLY = true; - -export function transformArguments(key: string, min: number, max: number): Array { - return [ - 'ZCOUNT', - key, - transformArgumentNumberInfinity(min), - transformArgumentNumberInfinity(max) - ]; -} - -export declare function transformReply(): number; diff --git a/lib/test-utils.ts b/lib/test-utils.ts deleted file mode 100644 index 978940ff93d..00000000000 --- a/lib/test-utils.ts +++ /dev/null @@ -1,377 +0,0 @@ -import { strict as assert } from 'assert'; -import RedisClient, { RedisClientOptions, RedisClientType } from './client'; -import { execSync, spawn } from 'child_process'; -import { once } from 'events'; -import which from 'which'; -import { SinonSpy } from 'sinon'; -import RedisCluster, { RedisClusterOptions, RedisClusterType } from './cluster'; -import { promises as fs } from 'fs'; -import { Context as MochaContext } from 'mocha'; -import { promiseTimeout } from './utils'; -import { RedisModules, RedisScripts } from './commands'; - -type RedisVersion = [major: number, minor: number, patch: number]; - -type PartialRedisVersion = RedisVersion | [major: number, minor: number] | [major: number]; - -const REDIS_PATH = which.sync('redis-server'); -export const REDIS_VERSION = getRedisVersion(); - -function getRedisVersion(): RedisVersion { - const raw = execSync(`${REDIS_PATH} -v`).toString(), - indexOfVersion = raw.indexOf('v='); - - if (indexOfVersion === -1) { - throw new Error('Unknown redis version'); - } - - const start = indexOfVersion + 2; - return raw.substring( - start, - raw.indexOf(' ', start) - ).split('.', 3).map(Number) as RedisVersion; -} - -export function isRedisVersionGreaterThan(minimumVersion: PartialRedisVersion | undefined): boolean { - if (minimumVersion === undefined) return true; - - const lastIndex = minimumVersion.length - 1; - for (let i = 0; i < lastIndex; i++) { - if (REDIS_VERSION[i] > minimumVersion[i]) { - return true; - } else if (minimumVersion[i] > REDIS_VERSION[i]) { - return false; - } - } - - return REDIS_VERSION[lastIndex] >= minimumVersion[lastIndex]; -} - -export enum TestRedisServers { - OPEN, - PASSWORD -} - -export const TEST_REDIS_SERVERS: Record> = {}; - -export enum TestRedisClusters { - OPEN -} - -export const TEST_REDIS_CLUSTERES: Record> = {}; - -let port = 6379; - -interface SpawnRedisServerResult { - port: number; - cleanup: () => Promise; -} - -async function spawnRedisServer(args?: Array): Promise { - const currentPort = port++, - process = spawn(REDIS_PATH, [ - '--save', - '', - '--port', - currentPort.toString(), - ...(args ?? []) - ]); - - process - .once('error', err => console.error('Redis process error', err)) - .once('close', code => console.error(`Redis process closed unexpectedly with code ${code}`)); - - for await (const chunk of process.stdout) { - if (chunk.toString().includes('Ready to accept connections')) { - break; - } - } - - if (process.exitCode !== null) { - throw new Error('Error while spawning redis server'); - } - - return { - port: currentPort, - async cleanup(): Promise { - process.removeAllListeners('close'); - assert.ok(process.kill()); - await once(process, 'close'); - } - }; -} - -async function spawnGlobalRedisServer(args?: Array): Promise { - const { port, cleanup } = await spawnRedisServer(args); - after(cleanup); - return port; -} - -const SLOTS = 16384; - -interface SpawnRedisClusterNodeResult extends SpawnRedisServerResult { - client: RedisClientType -} - -async function spawnRedisClusterNode( - type: TestRedisClusters | null, - nodeIndex: number, - fromSlot: number, - toSlot: number, - args?: Array -): Promise { - const clusterConfigFile = `/tmp/${type}-${nodeIndex}.conf`, - { port, cleanup: originalCleanup } = await spawnRedisServer([ - '--cluster-enabled', - 'yes', - '--cluster-node-timeout', - '5000', - '--cluster-config-file', - clusterConfigFile, - ...(args ?? []) - ]); - - const client = RedisClient.create({ - socket: { - port - } - }); - - await client.connect(); - - const range = []; - for (let i = fromSlot; i < toSlot; i++) { - range.push(i); - } - - await Promise.all([ - client.clusterFlushSlots(), - client.clusterAddSlots(range) - ]); - - return { - port, - async cleanup(): Promise { - await originalCleanup(); - - try { - await fs.unlink(clusterConfigFile); - } catch (err: any) { - if (err.code === 'ENOENT') return; - - throw err; - } - }, - client - }; -} - -export async function spawnRedisCluster(type: TestRedisClusters | null, numberOfNodes: number, args?: Array): Promise> { - const spawnPromises = [], - slotsPerNode = Math.floor(SLOTS / numberOfNodes); - for (let i = 0; i < numberOfNodes; i++) { - const fromSlot = i * slotsPerNode; - spawnPromises.push( - spawnRedisClusterNode( - type, - i, - fromSlot, - i === numberOfNodes - 1 ? SLOTS : fromSlot + slotsPerNode, - args - ) - ); - } - - const spawnResults = await Promise.all(spawnPromises), - meetPromises = []; - for (let i = 1; i < spawnResults.length; i++) { - meetPromises.push( - spawnResults[i].client.clusterMeet( - '127.0.0.1', - spawnResults[i - 1].port - ) - ); - } - - await Promise.all(meetPromises); - - while (!(await clusterIsReady(spawnResults))) { - await promiseTimeout(100); - } - - await Promise.all( - spawnResults.map(result => result.client.disconnect()) - ); - - return spawnResults; -} - -async function clusterIsReady(spawnResults: Array): Promise { - const nodesClusetrInfo = await Promise.all( - spawnResults.map(result => result.client.clusterInfo()) - ); - - return nodesClusetrInfo.every(({ state }) => state === 'ok'); -} - -export async function spawnGlobalRedisCluster(type: TestRedisClusters | null, numberOfNodes: number, args?: Array): Promise> { - const results = await spawnRedisCluster(type, numberOfNodes, args); - - after(() => Promise.all( - results.map(({ cleanup }) => cleanup()) - )); - - return results.map(({ port }) => port); -} - -async function spawnOpenServer(): Promise { - TEST_REDIS_SERVERS[TestRedisServers.OPEN] = { - socket: { - port: await spawnGlobalRedisServer() - } - }; -} - -async function spawnPasswordServer(): Promise { - TEST_REDIS_SERVERS[TestRedisServers.PASSWORD] = { - socket: { - port: await spawnGlobalRedisServer(['--requirepass', 'password']), - }, - password: 'password' - }; - - if (isRedisVersionGreaterThan([6])) { - TEST_REDIS_SERVERS[TestRedisServers.PASSWORD].username = 'default'; - } -} - -async function spawnOpenCluster(): Promise { - TEST_REDIS_CLUSTERES[TestRedisClusters.OPEN] = { - rootNodes: (await spawnGlobalRedisCluster(TestRedisClusters.OPEN, 3)).map(port => ({ - socket: { - port - } - })) - }; -} - -before(function () { - this.timeout(10000); - - return Promise.all([ - spawnOpenServer(), - spawnPasswordServer(), - spawnOpenCluster() - ]); -}); - -interface RedisTestOptions { - minimumRedisVersion?: PartialRedisVersion; -} - -export function handleMinimumRedisVersion(mochaContext: MochaContext, minimumVersion: PartialRedisVersion | undefined): boolean { - if (isRedisVersionGreaterThan(minimumVersion)) { - return false; - } - - mochaContext.skip(); - return true; -} - -export function describeHandleMinimumRedisVersion(minimumVersion: PartialRedisVersion): void { - before(function () { - handleMinimumRedisVersion(this, minimumVersion); - }); -} - -export function itWithClient( - type: TestRedisServers, - title: string, - fn: (client: RedisClientType) => Promise, - options?: RedisTestOptions -): void { - it(title, async function () { - if (handleMinimumRedisVersion(this, options?.minimumRedisVersion)) return; - - const client = RedisClient.create(TEST_REDIS_SERVERS[type]); - - await client.connect(); - - try { - await client.flushAll(); - await fn(client); - } finally { - await client.flushAll(); - await client.disconnect(); - } - }); -} - -export function itWithCluster( - type: TestRedisClusters, - title: string, - fn: (cluster: RedisClusterType) => Promise, - options?: RedisTestOptions -): void { - it(title, async function () { - if (handleMinimumRedisVersion(this, options?.minimumRedisVersion)) return; - - const cluster = RedisCluster.create(TEST_REDIS_CLUSTERES[type]); - - await cluster.connect(); - - try { - await clusterFlushAll(cluster); - await fn(cluster); - } finally { - await clusterFlushAll(cluster); - await cluster.disconnect(); - } - }); -} - -export function itWithDedicatedCluster(title: string, fn: (cluster: RedisClusterType) => Promise): void { - it(title, async function () { - this.timeout(10000); - - const spawnResults = await spawnRedisCluster(null, 3), - cluster = RedisCluster.create({ - rootNodes: [{ - socket: { - port: spawnResults[0].port - } - }] - }); - - await cluster.connect(); - - try { - await fn(cluster); - } finally { - await cluster.disconnect(); - - for (const { cleanup } of spawnResults) { - await cleanup(); - } - } - }); -} - -async function clusterFlushAll(cluster: RedisCluster): Promise { - await Promise.all( - cluster.getMasters().map(({ client }) => client.flushAll()) - ); -} - -export async function waitTillBeenCalled(spy: SinonSpy): Promise { - const start = process.hrtime.bigint(), - calls = spy.callCount; - - do { - if (process.hrtime.bigint() - start > 1_000_000_000) { - throw new Error('Waiting for more than 1 second'); - } - - await promiseTimeout(1); - } while (spy.callCount === calls) -} diff --git a/lib/ts-declarations/redis-parser.d.ts b/lib/ts-declarations/redis-parser.d.ts deleted file mode 100644 index 7ec129ed8cd..00000000000 --- a/lib/ts-declarations/redis-parser.d.ts +++ /dev/null @@ -1,15 +0,0 @@ -declare module 'redis-parser' { - interface RedisParserCallbacks { - returnReply(reply: unknown): void; - returnError(err: Error): void; - returnFatalError?(err: Error): void; - } - - export default class RedisParser { - constructor(callbacks: RedisParserCallbacks); - - setReturnBuffers(returnBuffers?: boolean): void; - - execute(buffer: Buffer): void; - } -} diff --git a/package-lock.json b/package-lock.json index f47208d6c9d..5c3ebb5c208 100644 --- a/package-lock.json +++ b/package-lock.json @@ -1,79 +1,63 @@ { "name": "redis", - "version": "4.0.0-rc.3", + "version": "4.0.0-rc.4", "lockfileVersion": 2, "requires": true, "packages": { "": { "name": "redis", - "version": "4.0.0-rc.3", + "version": "4.0.0-rc.4", "license": "MIT", + "workspaces": [ + "./packages/*" + ], "dependencies": { - "cluster-key-slot": "1.1.0", - "generic-pool": "3.8.2", - "redis-parser": "3.0.0", - "yallist": "4.0.0" + "@node-redis/client": "^1.0.0-rc", + "@node-redis/json": "^1.0.0-rc", + "@node-redis/search": "^1.0.0-rc" }, "devDependencies": { - "@istanbuljs/nyc-config-typescript": "^1.0.1", "@tsconfig/node12": "^1.0.9", - "@types/mocha": "^9.0.0", - "@types/node": "^16.10.3", - "@types/sinon": "^10.0.4", - "@types/which": "^2.0.1", - "@types/yallist": "^4.0.1", - "mocha": "^9.1.2", - "nyc": "^15.1.0", - "release-it": "^14.11.6", - "sinon": "^11.1.2", - "source-map-support": "^0.5.20", - "ts-node": "^10.3.0", - "typedoc": "^0.22.5", - "typedoc-github-wiki-theme": "^0.6.0", - "typedoc-plugin-markdown": "^3.11.3", - "typescript": "^4.4.3", - "which": "^2.0.2" - }, - "engines": { - "node": ">=12" + "release-it": "^14.11.7", + "typescript": "^4.4.4" } }, "node_modules/@babel/code-frame": { - "version": "7.15.8", - "resolved": "https://registry.npmjs.org/@babel/code-frame/-/code-frame-7.15.8.tgz", - "integrity": "sha512-2IAnmn8zbvC/jKYhq5Ki9I+DwjlrtMPUCH/CpHvqI4dNnlwHwsxoIhlc8WcYY5LSYknXQtAlFYuHfqAFCvQ4Wg==", + "version": "7.16.0", + "resolved": "https://registry.npmjs.org/@babel/code-frame/-/code-frame-7.16.0.tgz", + "integrity": "sha512-IF4EOMEV+bfYwOmNxGzSnjR2EmQod7f1UXOpZM3l4i4o4QNwzjtJAu/HxdjHq0aYBvdqMuQEY1eg0nqW9ZPORA==", "dev": true, "dependencies": { - "@babel/highlight": "^7.14.5" + "@babel/highlight": "^7.16.0" }, "engines": { "node": ">=6.9.0" } }, "node_modules/@babel/compat-data": { - "version": "7.15.0", - "resolved": "https://registry.npmjs.org/@babel/compat-data/-/compat-data-7.15.0.tgz", - "integrity": "sha512-0NqAC1IJE0S0+lL1SWFMxMkz1pKCNCjI4tr2Zx4LJSXxCLAdr6KyArnY+sno5m3yH9g737ygOyPABDsnXkpxiA==", + "version": "7.16.0", + "resolved": "https://registry.npmjs.org/@babel/compat-data/-/compat-data-7.16.0.tgz", + "integrity": "sha512-DGjt2QZse5SGd9nfOSqO4WLJ8NN/oHkijbXbPrxuoJO3oIPJL3TciZs9FX+cOHNiY9E9l0opL8g7BmLe3T+9ew==", "dev": true, "engines": { "node": ">=6.9.0" } }, "node_modules/@babel/core": { - "version": "7.15.8", - "resolved": "https://registry.npmjs.org/@babel/core/-/core-7.15.8.tgz", - "integrity": "sha512-3UG9dsxvYBMYwRv+gS41WKHno4K60/9GPy1CJaH6xy3Elq8CTtvtjT5R5jmNhXfCYLX2mTw+7/aq5ak/gOE0og==", - "dev": true, - "dependencies": { - "@babel/code-frame": "^7.15.8", - "@babel/generator": "^7.15.8", - "@babel/helper-compilation-targets": "^7.15.4", - "@babel/helper-module-transforms": "^7.15.8", - "@babel/helpers": "^7.15.4", - "@babel/parser": "^7.15.8", - "@babel/template": "^7.15.4", - "@babel/traverse": "^7.15.4", - "@babel/types": "^7.15.6", + "version": "7.16.0", + "resolved": "https://registry.npmjs.org/@babel/core/-/core-7.16.0.tgz", + "integrity": "sha512-mYZEvshBRHGsIAiyH5PzCFTCfbWfoYbO/jcSdXQSUQu1/pW0xDZAUP7KEc32heqWTAfAHhV9j1vH8Sav7l+JNQ==", + "dev": true, + "dependencies": { + "@babel/code-frame": "^7.16.0", + "@babel/generator": "^7.16.0", + "@babel/helper-compilation-targets": "^7.16.0", + "@babel/helper-module-transforms": "^7.16.0", + "@babel/helpers": "^7.16.0", + "@babel/parser": "^7.16.0", + "@babel/template": "^7.16.0", + "@babel/traverse": "^7.16.0", + "@babel/types": "^7.16.0", "convert-source-map": "^1.7.0", "debug": "^4.1.0", "gensync": "^1.0.0-beta.2", @@ -89,13 +73,22 @@ "url": "https://opencollective.com/babel" } }, + "node_modules/@babel/core/node_modules/semver": { + "version": "6.3.0", + "resolved": "https://registry.npmjs.org/semver/-/semver-6.3.0.tgz", + "integrity": "sha512-b39TBaTSfV6yBrapU89p5fKekE2m/NwnDocOVruQFS1/veMgdzuPcnOM34M6CwxW8jH/lxEa5rBoDeUwu5HHTw==", + "dev": true, + "bin": { + "semver": "bin/semver.js" + } + }, "node_modules/@babel/generator": { - "version": "7.15.8", - "resolved": "https://registry.npmjs.org/@babel/generator/-/generator-7.15.8.tgz", - "integrity": "sha512-ECmAKstXbp1cvpTTZciZCgfOt6iN64lR0d+euv3UZisU5awfRawOvg07Utn/qBGuH4bRIEZKrA/4LzZyXhZr8g==", + "version": "7.16.0", + "resolved": "https://registry.npmjs.org/@babel/generator/-/generator-7.16.0.tgz", + "integrity": "sha512-RR8hUCfRQn9j9RPKEVXo9LiwoxLPYn6hNZlvUOR8tSnaxlD0p0+la00ZP9/SnRt6HchKr+X0fO2r8vrETiJGew==", "dev": true, "dependencies": { - "@babel/types": "^7.15.6", + "@babel/types": "^7.16.0", "jsesc": "^2.5.1", "source-map": "^0.5.0" }, @@ -104,14 +97,14 @@ } }, "node_modules/@babel/helper-compilation-targets": { - "version": "7.15.4", - "resolved": "https://registry.npmjs.org/@babel/helper-compilation-targets/-/helper-compilation-targets-7.15.4.tgz", - "integrity": "sha512-rMWPCirulnPSe4d+gwdWXLfAXTTBj8M3guAf5xFQJ0nvFY7tfNAFnWdqaHegHlgDZOCT4qvhF3BYlSJag8yhqQ==", + "version": "7.16.3", + "resolved": "https://registry.npmjs.org/@babel/helper-compilation-targets/-/helper-compilation-targets-7.16.3.tgz", + "integrity": "sha512-vKsoSQAyBmxS35JUOOt+07cLc6Nk/2ljLIHwmq2/NM6hdioUaqEXq/S+nXvbvXbZkNDlWOymPanJGOc4CBjSJA==", "dev": true, "dependencies": { - "@babel/compat-data": "^7.15.0", + "@babel/compat-data": "^7.16.0", "@babel/helper-validator-option": "^7.14.5", - "browserslist": "^4.16.6", + "browserslist": "^4.17.5", "semver": "^6.3.0" }, "engines": { @@ -121,133 +114,142 @@ "@babel/core": "^7.0.0" } }, + "node_modules/@babel/helper-compilation-targets/node_modules/semver": { + "version": "6.3.0", + "resolved": "https://registry.npmjs.org/semver/-/semver-6.3.0.tgz", + "integrity": "sha512-b39TBaTSfV6yBrapU89p5fKekE2m/NwnDocOVruQFS1/veMgdzuPcnOM34M6CwxW8jH/lxEa5rBoDeUwu5HHTw==", + "dev": true, + "bin": { + "semver": "bin/semver.js" + } + }, "node_modules/@babel/helper-function-name": { - "version": "7.15.4", - "resolved": "https://registry.npmjs.org/@babel/helper-function-name/-/helper-function-name-7.15.4.tgz", - "integrity": "sha512-Z91cOMM4DseLIGOnog+Z8OI6YseR9bua+HpvLAQ2XayUGU+neTtX+97caALaLdyu53I/fjhbeCnWnRH1O3jFOw==", + "version": "7.16.0", + "resolved": "https://registry.npmjs.org/@babel/helper-function-name/-/helper-function-name-7.16.0.tgz", + "integrity": "sha512-BZh4mEk1xi2h4HFjWUXRQX5AEx4rvaZxHgax9gcjdLWdkjsY7MKt5p0otjsg5noXw+pB+clMCjw+aEVYADMjog==", "dev": true, "dependencies": { - "@babel/helper-get-function-arity": "^7.15.4", - "@babel/template": "^7.15.4", - "@babel/types": "^7.15.4" + "@babel/helper-get-function-arity": "^7.16.0", + "@babel/template": "^7.16.0", + "@babel/types": "^7.16.0" }, "engines": { "node": ">=6.9.0" } }, "node_modules/@babel/helper-get-function-arity": { - "version": "7.15.4", - "resolved": "https://registry.npmjs.org/@babel/helper-get-function-arity/-/helper-get-function-arity-7.15.4.tgz", - "integrity": "sha512-1/AlxSF92CmGZzHnC515hm4SirTxtpDnLEJ0UyEMgTMZN+6bxXKg04dKhiRx5Enel+SUA1G1t5Ed/yQia0efrA==", + "version": "7.16.0", + "resolved": "https://registry.npmjs.org/@babel/helper-get-function-arity/-/helper-get-function-arity-7.16.0.tgz", + "integrity": "sha512-ASCquNcywC1NkYh/z7Cgp3w31YW8aojjYIlNg4VeJiHkqyP4AzIvr4qx7pYDb4/s8YcsZWqqOSxgkvjUz1kpDQ==", "dev": true, "dependencies": { - "@babel/types": "^7.15.4" + "@babel/types": "^7.16.0" }, "engines": { "node": ">=6.9.0" } }, "node_modules/@babel/helper-hoist-variables": { - "version": "7.15.4", - "resolved": "https://registry.npmjs.org/@babel/helper-hoist-variables/-/helper-hoist-variables-7.15.4.tgz", - "integrity": "sha512-VTy085egb3jUGVK9ycIxQiPbquesq0HUQ+tPO0uv5mPEBZipk+5FkRKiWq5apuyTE9FUrjENB0rCf8y+n+UuhA==", + "version": "7.16.0", + "resolved": "https://registry.npmjs.org/@babel/helper-hoist-variables/-/helper-hoist-variables-7.16.0.tgz", + "integrity": "sha512-1AZlpazjUR0EQZQv3sgRNfM9mEVWPK3M6vlalczA+EECcPz3XPh6VplbErL5UoMpChhSck5wAJHthlj1bYpcmg==", "dev": true, "dependencies": { - "@babel/types": "^7.15.4" + "@babel/types": "^7.16.0" }, "engines": { "node": ">=6.9.0" } }, "node_modules/@babel/helper-member-expression-to-functions": { - "version": "7.15.4", - "resolved": "https://registry.npmjs.org/@babel/helper-member-expression-to-functions/-/helper-member-expression-to-functions-7.15.4.tgz", - "integrity": "sha512-cokOMkxC/BTyNP1AlY25HuBWM32iCEsLPI4BHDpJCHHm1FU2E7dKWWIXJgQgSFiu4lp8q3bL1BIKwqkSUviqtA==", + "version": "7.16.0", + "resolved": "https://registry.npmjs.org/@babel/helper-member-expression-to-functions/-/helper-member-expression-to-functions-7.16.0.tgz", + "integrity": "sha512-bsjlBFPuWT6IWhl28EdrQ+gTvSvj5tqVP5Xeftp07SEuz5pLnsXZuDkDD3Rfcxy0IsHmbZ+7B2/9SHzxO0T+sQ==", "dev": true, "dependencies": { - "@babel/types": "^7.15.4" + "@babel/types": "^7.16.0" }, "engines": { "node": ">=6.9.0" } }, "node_modules/@babel/helper-module-imports": { - "version": "7.15.4", - "resolved": "https://registry.npmjs.org/@babel/helper-module-imports/-/helper-module-imports-7.15.4.tgz", - "integrity": "sha512-jeAHZbzUwdW/xHgHQ3QmWR4Jg6j15q4w/gCfwZvtqOxoo5DKtLHk8Bsf4c5RZRC7NmLEs+ohkdq8jFefuvIxAA==", + "version": "7.16.0", + "resolved": "https://registry.npmjs.org/@babel/helper-module-imports/-/helper-module-imports-7.16.0.tgz", + "integrity": "sha512-kkH7sWzKPq0xt3H1n+ghb4xEMP8k0U7XV3kkB+ZGy69kDk2ySFW1qPi06sjKzFY3t1j6XbJSqr4mF9L7CYVyhg==", "dev": true, "dependencies": { - "@babel/types": "^7.15.4" + "@babel/types": "^7.16.0" }, "engines": { "node": ">=6.9.0" } }, "node_modules/@babel/helper-module-transforms": { - "version": "7.15.8", - "resolved": "https://registry.npmjs.org/@babel/helper-module-transforms/-/helper-module-transforms-7.15.8.tgz", - "integrity": "sha512-DfAfA6PfpG8t4S6npwzLvTUpp0sS7JrcuaMiy1Y5645laRJIp/LiLGIBbQKaXSInK8tiGNI7FL7L8UvB8gdUZg==", + "version": "7.16.0", + "resolved": "https://registry.npmjs.org/@babel/helper-module-transforms/-/helper-module-transforms-7.16.0.tgz", + "integrity": "sha512-My4cr9ATcaBbmaEa8M0dZNA74cfI6gitvUAskgDtAFmAqyFKDSHQo5YstxPbN+lzHl2D9l/YOEFqb2mtUh4gfA==", "dev": true, "dependencies": { - "@babel/helper-module-imports": "^7.15.4", - "@babel/helper-replace-supers": "^7.15.4", - "@babel/helper-simple-access": "^7.15.4", - "@babel/helper-split-export-declaration": "^7.15.4", + "@babel/helper-module-imports": "^7.16.0", + "@babel/helper-replace-supers": "^7.16.0", + "@babel/helper-simple-access": "^7.16.0", + "@babel/helper-split-export-declaration": "^7.16.0", "@babel/helper-validator-identifier": "^7.15.7", - "@babel/template": "^7.15.4", - "@babel/traverse": "^7.15.4", - "@babel/types": "^7.15.6" + "@babel/template": "^7.16.0", + "@babel/traverse": "^7.16.0", + "@babel/types": "^7.16.0" }, "engines": { "node": ">=6.9.0" } }, "node_modules/@babel/helper-optimise-call-expression": { - "version": "7.15.4", - "resolved": "https://registry.npmjs.org/@babel/helper-optimise-call-expression/-/helper-optimise-call-expression-7.15.4.tgz", - "integrity": "sha512-E/z9rfbAOt1vDW1DR7k4SzhzotVV5+qMciWV6LaG1g4jeFrkDlJedjtV4h0i4Q/ITnUu+Pk08M7fczsB9GXBDw==", + "version": "7.16.0", + "resolved": "https://registry.npmjs.org/@babel/helper-optimise-call-expression/-/helper-optimise-call-expression-7.16.0.tgz", + "integrity": "sha512-SuI467Gi2V8fkofm2JPnZzB/SUuXoJA5zXe/xzyPP2M04686RzFKFHPK6HDVN6JvWBIEW8tt9hPR7fXdn2Lgpw==", "dev": true, "dependencies": { - "@babel/types": "^7.15.4" + "@babel/types": "^7.16.0" }, "engines": { "node": ">=6.9.0" } }, "node_modules/@babel/helper-replace-supers": { - "version": "7.15.4", - "resolved": "https://registry.npmjs.org/@babel/helper-replace-supers/-/helper-replace-supers-7.15.4.tgz", - "integrity": "sha512-/ztT6khaXF37MS47fufrKvIsiQkx1LBRvSJNzRqmbyeZnTwU9qBxXYLaaT/6KaxfKhjs2Wy8kG8ZdsFUuWBjzw==", + "version": "7.16.0", + "resolved": "https://registry.npmjs.org/@babel/helper-replace-supers/-/helper-replace-supers-7.16.0.tgz", + "integrity": "sha512-TQxuQfSCdoha7cpRNJvfaYxxxzmbxXw/+6cS7V02eeDYyhxderSoMVALvwupA54/pZcOTtVeJ0xccp1nGWladA==", "dev": true, "dependencies": { - "@babel/helper-member-expression-to-functions": "^7.15.4", - "@babel/helper-optimise-call-expression": "^7.15.4", - "@babel/traverse": "^7.15.4", - "@babel/types": "^7.15.4" + "@babel/helper-member-expression-to-functions": "^7.16.0", + "@babel/helper-optimise-call-expression": "^7.16.0", + "@babel/traverse": "^7.16.0", + "@babel/types": "^7.16.0" }, "engines": { "node": ">=6.9.0" } }, "node_modules/@babel/helper-simple-access": { - "version": "7.15.4", - "resolved": "https://registry.npmjs.org/@babel/helper-simple-access/-/helper-simple-access-7.15.4.tgz", - "integrity": "sha512-UzazrDoIVOZZcTeHHEPYrr1MvTR/K+wgLg6MY6e1CJyaRhbibftF6fR2KU2sFRtI/nERUZR9fBd6aKgBlIBaPg==", + "version": "7.16.0", + "resolved": "https://registry.npmjs.org/@babel/helper-simple-access/-/helper-simple-access-7.16.0.tgz", + "integrity": "sha512-o1rjBT/gppAqKsYfUdfHq5Rk03lMQrkPHG1OWzHWpLgVXRH4HnMM9Et9CVdIqwkCQlobnGHEJMsgWP/jE1zUiw==", "dev": true, "dependencies": { - "@babel/types": "^7.15.4" + "@babel/types": "^7.16.0" }, "engines": { "node": ">=6.9.0" } }, "node_modules/@babel/helper-split-export-declaration": { - "version": "7.15.4", - "resolved": "https://registry.npmjs.org/@babel/helper-split-export-declaration/-/helper-split-export-declaration-7.15.4.tgz", - "integrity": "sha512-HsFqhLDZ08DxCpBdEVtKmywj6PQbwnF6HHybur0MAnkAKnlS6uHkwnmRIkElB2Owpfb4xL4NwDmDLFubueDXsw==", + "version": "7.16.0", + "resolved": "https://registry.npmjs.org/@babel/helper-split-export-declaration/-/helper-split-export-declaration-7.16.0.tgz", + "integrity": "sha512-0YMMRpuDFNGTHNRiiqJX19GjNXA4H0E8jZ2ibccfSxaCogbm3am5WN/2nQNj0YnQwGWM1J06GOcQ2qnh3+0paw==", "dev": true, "dependencies": { - "@babel/types": "^7.15.4" + "@babel/types": "^7.16.0" }, "engines": { "node": ">=6.9.0" @@ -272,26 +274,26 @@ } }, "node_modules/@babel/helpers": { - "version": "7.15.4", - "resolved": "https://registry.npmjs.org/@babel/helpers/-/helpers-7.15.4.tgz", - "integrity": "sha512-V45u6dqEJ3w2rlryYYXf6i9rQ5YMNu4FLS6ngs8ikblhu2VdR1AqAd6aJjBzmf2Qzh6KOLqKHxEN9+TFbAkAVQ==", + "version": "7.16.3", + "resolved": "https://registry.npmjs.org/@babel/helpers/-/helpers-7.16.3.tgz", + "integrity": "sha512-Xn8IhDlBPhvYTvgewPKawhADichOsbkZuzN7qz2BusOM0brChsyXMDJvldWaYMMUNiCQdQzNEioXTp3sC8Nt8w==", "dev": true, "dependencies": { - "@babel/template": "^7.15.4", - "@babel/traverse": "^7.15.4", - "@babel/types": "^7.15.4" + "@babel/template": "^7.16.0", + "@babel/traverse": "^7.16.3", + "@babel/types": "^7.16.0" }, "engines": { "node": ">=6.9.0" } }, "node_modules/@babel/highlight": { - "version": "7.14.5", - "resolved": "https://registry.npmjs.org/@babel/highlight/-/highlight-7.14.5.tgz", - "integrity": "sha512-qf9u2WFWVV0MppaL877j2dBtQIDgmidgjGk5VIMw3OadXvYaXn66U1BFlH2t4+t3i+8PhedppRv+i40ABzd+gg==", + "version": "7.16.0", + "resolved": "https://registry.npmjs.org/@babel/highlight/-/highlight-7.16.0.tgz", + "integrity": "sha512-t8MH41kUQylBtu2+4IQA3atqevA2lRgqA2wyVB/YiWmsDSuylZZuXOUy9ric30hfzauEFfdsuk/eXTRrGrfd0g==", "dev": true, "dependencies": { - "@babel/helper-validator-identifier": "^7.14.5", + "@babel/helper-validator-identifier": "^7.15.7", "chalk": "^2.0.0", "js-tokens": "^4.0.0" }, @@ -340,15 +342,6 @@ "integrity": "sha1-p9BVi9icQveV3UIyj3QIMcpTvCU=", "dev": true }, - "node_modules/@babel/highlight/node_modules/escape-string-regexp": { - "version": "1.0.5", - "resolved": "https://registry.npmjs.org/escape-string-regexp/-/escape-string-regexp-1.0.5.tgz", - "integrity": "sha1-G2HAViGQqN/2rjuyzwIAyhMLhtQ=", - "dev": true, - "engines": { - "node": ">=0.8.0" - } - }, "node_modules/@babel/highlight/node_modules/has-flag": { "version": "3.0.0", "resolved": "https://registry.npmjs.org/has-flag/-/has-flag-3.0.0.tgz", @@ -371,9 +364,9 @@ } }, "node_modules/@babel/parser": { - "version": "7.15.8", - "resolved": "https://registry.npmjs.org/@babel/parser/-/parser-7.15.8.tgz", - "integrity": "sha512-BRYa3wcQnjS/nqI8Ac94pYYpJfojHVvVXJ97+IDCImX4Jc8W8Xv1+47enbruk+q1etOpsQNwnfFcNGw+gtPGxA==", + "version": "7.16.3", + "resolved": "https://registry.npmjs.org/@babel/parser/-/parser-7.16.3.tgz", + "integrity": "sha512-dcNwU1O4sx57ClvLBVFbEgx0UZWfd0JQX5X6fxFRCLHelFBGXFfSz6Y0FAq2PEwUqlqLkdVjVr4VASEOuUnLJw==", "dev": true, "bin": { "parser": "bin/babel-parser.js" @@ -383,32 +376,32 @@ } }, "node_modules/@babel/template": { - "version": "7.15.4", - "resolved": "https://registry.npmjs.org/@babel/template/-/template-7.15.4.tgz", - "integrity": "sha512-UgBAfEa1oGuYgDIPM2G+aHa4Nlo9Lh6mGD2bDBGMTbYnc38vulXPuC1MGjYILIEmlwl6Rd+BPR9ee3gm20CBtg==", + "version": "7.16.0", + "resolved": "https://registry.npmjs.org/@babel/template/-/template-7.16.0.tgz", + "integrity": "sha512-MnZdpFD/ZdYhXwiunMqqgyZyucaYsbL0IrjoGjaVhGilz+x8YB++kRfygSOIj1yOtWKPlx7NBp+9I1RQSgsd5A==", "dev": true, "dependencies": { - "@babel/code-frame": "^7.14.5", - "@babel/parser": "^7.15.4", - "@babel/types": "^7.15.4" + "@babel/code-frame": "^7.16.0", + "@babel/parser": "^7.16.0", + "@babel/types": "^7.16.0" }, "engines": { "node": ">=6.9.0" } }, "node_modules/@babel/traverse": { - "version": "7.15.4", - "resolved": "https://registry.npmjs.org/@babel/traverse/-/traverse-7.15.4.tgz", - "integrity": "sha512-W6lQD8l4rUbQR/vYgSuCAE75ADyyQvOpFVsvPPdkhf6lATXAsQIG9YdtOcu8BB1dZ0LKu+Zo3c1wEcbKeuhdlA==", - "dev": true, - "dependencies": { - "@babel/code-frame": "^7.14.5", - "@babel/generator": "^7.15.4", - "@babel/helper-function-name": "^7.15.4", - "@babel/helper-hoist-variables": "^7.15.4", - "@babel/helper-split-export-declaration": "^7.15.4", - "@babel/parser": "^7.15.4", - "@babel/types": "^7.15.4", + "version": "7.16.3", + "resolved": "https://registry.npmjs.org/@babel/traverse/-/traverse-7.16.3.tgz", + "integrity": "sha512-eolumr1vVMjqevCpwVO99yN/LoGL0EyHiLO5I043aYQvwOJ9eR5UsZSClHVCzfhBduMAsSzgA/6AyqPjNayJag==", + "dev": true, + "dependencies": { + "@babel/code-frame": "^7.16.0", + "@babel/generator": "^7.16.0", + "@babel/helper-function-name": "^7.16.0", + "@babel/helper-hoist-variables": "^7.16.0", + "@babel/helper-split-export-declaration": "^7.16.0", + "@babel/parser": "^7.16.3", + "@babel/types": "^7.16.0", "debug": "^4.1.0", "globals": "^11.1.0" }, @@ -416,13 +409,22 @@ "node": ">=6.9.0" } }, + "node_modules/@babel/traverse/node_modules/globals": { + "version": "11.12.0", + "resolved": "https://registry.npmjs.org/globals/-/globals-11.12.0.tgz", + "integrity": "sha512-WOBp/EEGUiIsJSp7wcv/y6MO+lV9UoncWqxuFfm8eBwzWNgyfBd6Gz+IeKQ9jCmyhoH99g15M3T+QaVHFjizVA==", + "dev": true, + "engines": { + "node": ">=4" + } + }, "node_modules/@babel/types": { - "version": "7.15.6", - "resolved": "https://registry.npmjs.org/@babel/types/-/types-7.15.6.tgz", - "integrity": "sha512-BPU+7QhqNjmWyDO0/vitH/CuhpV8ZmK1wpKva8nuyNF5MJfuRNWMc+hc14+u9xT93kvykMdncrJT19h74uB1Ig==", + "version": "7.16.0", + "resolved": "https://registry.npmjs.org/@babel/types/-/types-7.16.0.tgz", + "integrity": "sha512-PJgg/k3SdLsGb3hhisFvtLOw5ts113klrpLuIPtCJIU+BB24fqq6lf8RWqKJEjzqXR9AEH1rIb5XTqwBHB+kQg==", "dev": true, "dependencies": { - "@babel/helper-validator-identifier": "^7.14.9", + "@babel/helper-validator-identifier": "^7.15.7", "to-fast-properties": "^2.0.0" }, "engines": { @@ -450,6 +452,67 @@ "node": ">=12" } }, + "node_modules/@eslint/eslintrc": { + "version": "1.0.4", + "resolved": "https://registry.npmjs.org/@eslint/eslintrc/-/eslintrc-1.0.4.tgz", + "integrity": "sha512-h8Vx6MdxwWI2WM8/zREHMoqdgLNXEL4QX3MWSVMdyNJGvXVOs+6lp+m2hc3FnuMHDc4poxFNI20vCk0OmI4G0Q==", + "dev": true, + "dependencies": { + "ajv": "^6.12.4", + "debug": "^4.3.2", + "espree": "^9.0.0", + "globals": "^13.9.0", + "ignore": "^4.0.6", + "import-fresh": "^3.2.1", + "js-yaml": "^4.1.0", + "minimatch": "^3.0.4", + "strip-json-comments": "^3.1.1" + }, + "engines": { + "node": "^12.22.0 || ^14.17.0 || >=16.0.0" + } + }, + "node_modules/@eslint/eslintrc/node_modules/ignore": { + "version": "4.0.6", + "resolved": "https://registry.npmjs.org/ignore/-/ignore-4.0.6.tgz", + "integrity": "sha512-cyFDKrqc/YdcWFniJhzI42+AzS+gNwmUzOSFcRCQYwySuBBBy/KjuxWLZ/FHEH6Moq1NizMOBWyTcv8O4OZIMg==", + "dev": true, + "engines": { + "node": ">= 4" + } + }, + "node_modules/@eslint/eslintrc/node_modules/strip-json-comments": { + "version": "3.1.1", + "resolved": "https://registry.npmjs.org/strip-json-comments/-/strip-json-comments-3.1.1.tgz", + "integrity": "sha512-6fPc+R4ihwqP6N/aIv2f1gMH8lOVtWQHoqC4yK6oSDVVocumAsfCqjkXnqiYMhmMwS/mEHLp7Vehlt3ql6lEig==", + "dev": true, + "engines": { + "node": ">=8" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/@humanwhocodes/config-array": { + "version": "0.6.0", + "resolved": "https://registry.npmjs.org/@humanwhocodes/config-array/-/config-array-0.6.0.tgz", + "integrity": "sha512-JQlEKbcgEUjBFhLIF4iqM7u/9lwgHRBcpHrmUNCALK0Q3amXN6lxdoXLnF0sm11E9VqTmBALR87IlUg1bZ8A9A==", + "dev": true, + "dependencies": { + "@humanwhocodes/object-schema": "^1.2.0", + "debug": "^4.1.1", + "minimatch": "^3.0.4" + }, + "engines": { + "node": ">=10.10.0" + } + }, + "node_modules/@humanwhocodes/object-schema": { + "version": "1.2.1", + "resolved": "https://registry.npmjs.org/@humanwhocodes/object-schema/-/object-schema-1.2.1.tgz", + "integrity": "sha512-ZnQMnLV4e7hDlUvw8H+U8ASL02SS2Gn6+9Ac3wGGLIe7+je2AeAOxPY+izIPJDfFDb7eDjev0Us8MO1iFRN8hA==", + "dev": true + }, "node_modules/@iarna/toml": { "version": "2.2.5", "resolved": "https://registry.npmjs.org/@iarna/toml/-/toml-2.2.5.tgz", @@ -481,6 +544,15 @@ "sprintf-js": "~1.0.2" } }, + "node_modules/@istanbuljs/load-nyc-config/node_modules/camelcase": { + "version": "5.3.1", + "resolved": "https://registry.npmjs.org/camelcase/-/camelcase-5.3.1.tgz", + "integrity": "sha512-L28STB170nwWS63UjtlEOE3dldQApaJXZkOI1uMFfzf3rRuPegHaHesyee+YxQ+W6SvRDQV6UrdOdRiR153wJg==", + "dev": true, + "engines": { + "node": ">=6" + } + }, "node_modules/@istanbuljs/load-nyc-config/node_modules/find-up": { "version": "4.1.0", "resolved": "https://registry.npmjs.org/find-up/-/find-up-4.1.0.tgz", @@ -546,6 +618,15 @@ "node": ">=8" } }, + "node_modules/@istanbuljs/load-nyc-config/node_modules/resolve-from": { + "version": "5.0.0", + "resolved": "https://registry.npmjs.org/resolve-from/-/resolve-from-5.0.0.tgz", + "integrity": "sha512-qYg9KP24dD5qka9J47d0aVky0N+b4fTU89LN9iDnjB5waksiC49rvMB0PrUJQGoTmH50XPiqOvAjDfaijGxYZw==", + "dev": true, + "engines": { + "node": ">=8" + } + }, "node_modules/@istanbuljs/nyc-config-typescript": { "version": "1.0.1", "resolved": "https://registry.npmjs.org/@istanbuljs/nyc-config-typescript/-/nyc-config-typescript-1.0.1.tgz", @@ -572,6 +653,22 @@ "node": ">=8" } }, + "node_modules/@node-redis/client": { + "resolved": "packages/client", + "link": true + }, + "node_modules/@node-redis/json": { + "resolved": "packages/json", + "link": true + }, + "node_modules/@node-redis/search": { + "resolved": "packages/search", + "link": true + }, + "node_modules/@node-redis/test-utils": { + "resolved": "packages/test-utils", + "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", @@ -840,6 +937,12 @@ "integrity": "sha512-SZs7ekbP8CN0txVG2xVRH6EgKmEm31BOxA07vkFaETzZz1xh+cbt8BcI0slpymvwhx5dlFnQG2rTlPVQn+iRPQ==", "dev": true }, + "node_modules/@types/json-schema": { + "version": "7.0.9", + "resolved": "https://registry.npmjs.org/@types/json-schema/-/json-schema-7.0.9.tgz", + "integrity": "sha512-qcUXuemtEu+E5wZSJHNxUXeCZhAfXKQ41D+duX+VYPde7xyEVZci+/oXKJL13tnRs9lR2pr4fod59GT6/X1/yQ==", + "dev": true + }, "node_modules/@types/keyv": { "version": "3.1.3", "resolved": "https://registry.npmjs.org/@types/keyv/-/keyv-3.1.3.tgz", @@ -856,9 +959,9 @@ "dev": true }, "node_modules/@types/node": { - "version": "16.10.3", - "resolved": "https://registry.npmjs.org/@types/node/-/node-16.10.3.tgz", - "integrity": "sha512-ho3Ruq+fFnBrZhUYI46n/bV2GjwzSkwuT4dTf0GkuNFmnb8nq4ny2z9JEVemFi6bdEJanHLlYfy9c6FN9B9McQ==", + "version": "16.11.7", + "resolved": "https://registry.npmjs.org/@types/node/-/node-16.11.7.tgz", + "integrity": "sha512-QB5D2sqfSjCmTuWcBWyJ+/44bcjO7VbjSbOE0ucoVbAsSNQc4Lt6QkgkVXkTDwkL4z/beecZNDvVX15D4P8Jbw==", "dev": true }, "node_modules/@types/parse-json": { @@ -867,6 +970,22 @@ "integrity": "sha512-//oorEZjL6sbPcKUaCdIGlIUeH26mgzimjBB77G6XRgnDl/L5wOnpyBGRe/Mmf5CVW3PwEBE1NjiMZ/ssFh4wA==", "dev": true }, + "node_modules/@types/redis-errors": { + "version": "1.2.1", + "resolved": "https://registry.npmjs.org/@types/redis-errors/-/redis-errors-1.2.1.tgz", + "integrity": "sha512-9p6SQPeDAIR4z3ZdrPsRH/sSRMeIA2fdRKqZ3Y1thQOLeDH4aLY+J4Ze32zjg4Dq7655Y0LonCoRrH5O7vtr4w==", + "dev": true + }, + "node_modules/@types/redis-parser": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/@types/redis-parser/-/redis-parser-3.0.0.tgz", + "integrity": "sha512-TH9NnE980dXWKjFRkck6FIv9akrD6G+vX9XXONoBgG9+NfP4iZ+SbGkaN7S15c6+JeZ+zBN62bHt2lNyUYwqoA==", + "dev": true, + "dependencies": { + "@types/node": "*", + "@types/redis-errors": "*" + } + }, "node_modules/@types/responselike": { "version": "1.0.0", "resolved": "https://registry.npmjs.org/@types/responselike/-/responselike-1.0.0.tgz", @@ -877,26 +996,192 @@ } }, "node_modules/@types/sinon": { - "version": "10.0.4", - "resolved": "https://registry.npmjs.org/@types/sinon/-/sinon-10.0.4.tgz", - "integrity": "sha512-fOYjrxQv8zJsqOY6V6ecP4eZhQBxtY80X0er1VVnUIAIZo74jHm8e1vguG5Yt4Iv8W2Wr7TgibB8MfRe32k9pA==", + "version": "10.0.6", + "resolved": "https://registry.npmjs.org/@types/sinon/-/sinon-10.0.6.tgz", + "integrity": "sha512-6EF+wzMWvBNeGrfP3Nx60hhx+FfwSg1JJBLAAP/IdIUq0EYkqCYf70VT3PhuhPX9eLD+Dp+lNdpb/ZeHG8Yezg==", "dev": true, "dependencies": { "@sinonjs/fake-timers": "^7.1.0" } }, - "node_modules/@types/which": { - "version": "2.0.1", - "resolved": "https://registry.npmjs.org/@types/which/-/which-2.0.1.tgz", - "integrity": "sha512-Jjakcv8Roqtio6w1gr0D7y6twbhx6gGgFGF5BLwajPpnOIOxFkakFhCq+LmyyeAz7BX6ULrjBOxdKaCDy+4+dQ==", - "dev": true - }, "node_modules/@types/yallist": { "version": "4.0.1", "resolved": "https://registry.npmjs.org/@types/yallist/-/yallist-4.0.1.tgz", "integrity": "sha512-G3FNJfaYtN8URU6wd6+uwFI62KO79j7n3XTYcwcFncP8gkfoi0b821GoVVt0oqKVnCqKYOMNKIGpakPoFhzAGA==", "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==", + "dev": true, + "dependencies": { + "@types/yargs-parser": "*" + } + }, + "node_modules/@types/yargs-parser": { + "version": "20.2.1", + "resolved": "https://registry.npmjs.org/@types/yargs-parser/-/yargs-parser-20.2.1.tgz", + "integrity": "sha512-7tFImggNeNBVMsn0vLrpn1H1uPrUBdnARPTpZoitY37ZrdJREzf7I16tMrlK3hen349gr1NYh8CmZQa7CTG6Aw==", + "dev": true + }, + "node_modules/@typescript-eslint/eslint-plugin": { + "version": "5.4.0", + "resolved": "https://registry.npmjs.org/@typescript-eslint/eslint-plugin/-/eslint-plugin-5.4.0.tgz", + "integrity": "sha512-9/yPSBlwzsetCsGEn9j24D8vGQgJkOTr4oMLas/w886ZtzKIs1iyoqFrwsX2fqYEeUwsdBpC21gcjRGo57u0eg==", + "dev": true, + "dependencies": { + "@typescript-eslint/experimental-utils": "5.4.0", + "@typescript-eslint/scope-manager": "5.4.0", + "debug": "^4.3.2", + "functional-red-black-tree": "^1.0.1", + "ignore": "^5.1.8", + "regexpp": "^3.2.0", + "semver": "^7.3.5", + "tsutils": "^3.21.0" + }, + "engines": { + "node": "^12.22.0 || ^14.17.0 || >=16.0.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/typescript-eslint" + }, + "peerDependencies": { + "@typescript-eslint/parser": "^5.0.0", + "eslint": "^6.0.0 || ^7.0.0 || ^8.0.0" + }, + "peerDependenciesMeta": { + "typescript": { + "optional": true + } + } + }, + "node_modules/@typescript-eslint/experimental-utils": { + "version": "5.4.0", + "resolved": "https://registry.npmjs.org/@typescript-eslint/experimental-utils/-/experimental-utils-5.4.0.tgz", + "integrity": "sha512-Nz2JDIQUdmIGd6p33A+naQmwfkU5KVTLb/5lTk+tLVTDacZKoGQisj8UCxk7onJcrgjIvr8xWqkYI+DbI3TfXg==", + "dev": true, + "dependencies": { + "@types/json-schema": "^7.0.9", + "@typescript-eslint/scope-manager": "5.4.0", + "@typescript-eslint/types": "5.4.0", + "@typescript-eslint/typescript-estree": "5.4.0", + "eslint-scope": "^5.1.1", + "eslint-utils": "^3.0.0" + }, + "engines": { + "node": "^12.22.0 || ^14.17.0 || >=16.0.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/typescript-eslint" + }, + "peerDependencies": { + "eslint": "*" + } + }, + "node_modules/@typescript-eslint/parser": { + "version": "5.4.0", + "resolved": "https://registry.npmjs.org/@typescript-eslint/parser/-/parser-5.4.0.tgz", + "integrity": "sha512-JoB41EmxiYpaEsRwpZEYAJ9XQURPFer8hpkIW9GiaspVLX8oqbqNM8P4EP8HOZg96yaALiLEVWllA2E8vwsIKw==", + "dev": true, + "dependencies": { + "@typescript-eslint/scope-manager": "5.4.0", + "@typescript-eslint/types": "5.4.0", + "@typescript-eslint/typescript-estree": "5.4.0", + "debug": "^4.3.2" + }, + "engines": { + "node": "^12.22.0 || ^14.17.0 || >=16.0.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/typescript-eslint" + }, + "peerDependencies": { + "eslint": "^6.0.0 || ^7.0.0 || ^8.0.0" + }, + "peerDependenciesMeta": { + "typescript": { + "optional": true + } + } + }, + "node_modules/@typescript-eslint/scope-manager": { + "version": "5.4.0", + "resolved": "https://registry.npmjs.org/@typescript-eslint/scope-manager/-/scope-manager-5.4.0.tgz", + "integrity": "sha512-pRxFjYwoi8R+n+sibjgF9iUiAELU9ihPBtHzocyW8v8D8G8KeQvXTsW7+CBYIyTYsmhtNk50QPGLE3vrvhM5KA==", + "dev": true, + "dependencies": { + "@typescript-eslint/types": "5.4.0", + "@typescript-eslint/visitor-keys": "5.4.0" + }, + "engines": { + "node": "^12.22.0 || ^14.17.0 || >=16.0.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/typescript-eslint" + } + }, + "node_modules/@typescript-eslint/types": { + "version": "5.4.0", + "resolved": "https://registry.npmjs.org/@typescript-eslint/types/-/types-5.4.0.tgz", + "integrity": "sha512-GjXNpmn+n1LvnttarX+sPD6+S7giO+9LxDIGlRl4wK3a7qMWALOHYuVSZpPTfEIklYjaWuMtfKdeByx0AcaThA==", + "dev": true, + "engines": { + "node": "^12.22.0 || ^14.17.0 || >=16.0.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/typescript-eslint" + } + }, + "node_modules/@typescript-eslint/typescript-estree": { + "version": "5.4.0", + "resolved": "https://registry.npmjs.org/@typescript-eslint/typescript-estree/-/typescript-estree-5.4.0.tgz", + "integrity": "sha512-nhlNoBdhKuwiLMx6GrybPT3SFILm5Gij2YBdPEPFlYNFAXUJWX6QRgvi/lwVoadaQEFsizohs6aFRMqsXI2ewA==", + "dev": true, + "dependencies": { + "@typescript-eslint/types": "5.4.0", + "@typescript-eslint/visitor-keys": "5.4.0", + "debug": "^4.3.2", + "globby": "^11.0.4", + "is-glob": "^4.0.3", + "semver": "^7.3.5", + "tsutils": "^3.21.0" + }, + "engines": { + "node": "^12.22.0 || ^14.17.0 || >=16.0.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/typescript-eslint" + }, + "peerDependenciesMeta": { + "typescript": { + "optional": true + } + } + }, + "node_modules/@typescript-eslint/visitor-keys": { + "version": "5.4.0", + "resolved": "https://registry.npmjs.org/@typescript-eslint/visitor-keys/-/visitor-keys-5.4.0.tgz", + "integrity": "sha512-PVbax7MeE7tdLfW5SA0fs8NGVVr+buMPrcj+CWYWPXsZCH8qZ1THufDzbXm1xrZ2b2PA1iENJ0sRq5fuUtvsJg==", + "dev": true, + "dependencies": { + "@typescript-eslint/types": "5.4.0", + "eslint-visitor-keys": "^3.0.0" + }, + "engines": { + "node": "^12.22.0 || ^14.17.0 || >=16.0.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/typescript-eslint" + } + }, "node_modules/@ungap/promise-all-settled": { "version": "1.1.2", "resolved": "https://registry.npmjs.org/@ungap/promise-all-settled/-/promise-all-settled-1.1.2.tgz", @@ -915,6 +1200,15 @@ "node": ">=0.4.0" } }, + "node_modules/acorn-jsx": { + "version": "5.3.2", + "resolved": "https://registry.npmjs.org/acorn-jsx/-/acorn-jsx-5.3.2.tgz", + "integrity": "sha512-rq9s+JNhf0IChjtDXxllJ7g41oZk5SlXtp0LHwyA5cejwn7vKmKp4pPri6YEePv2PU65sAsegbXtIinmDFDXgQ==", + "dev": true, + "peerDependencies": { + "acorn": "^6.0.0 || ^7.0.0 || ^8.0.0" + } + }, "node_modules/acorn-walk": { "version": "8.2.0", "resolved": "https://registry.npmjs.org/acorn-walk/-/acorn-walk-8.2.0.tgz", @@ -937,6 +1231,22 @@ "node": ">=8" } }, + "node_modules/ajv": { + "version": "6.12.6", + "resolved": "https://registry.npmjs.org/ajv/-/ajv-6.12.6.tgz", + "integrity": "sha512-j3fVLgvTo527anyYyJOGTYJbG+vnnQYvE0m5mmkc1TK+nxAppkCLMIL0aZ4dblVCNoGShhm+kzE4ZUykBoMg4g==", + "dev": true, + "dependencies": { + "fast-deep-equal": "^3.1.1", + "fast-json-stable-stringify": "^2.0.0", + "json-schema-traverse": "^0.4.1", + "uri-js": "^4.2.2" + }, + "funding": { + "type": "github", + "url": "https://github.com/sponsors/epoberezkin" + } + }, "node_modules/ansi-align": { "version": "3.0.1", "resolved": "https://registry.npmjs.org/ansi-align/-/ansi-align-3.0.1.tgz", @@ -970,18 +1280,6 @@ "url": "https://github.com/sponsors/sindresorhus" } }, - "node_modules/ansi-escapes/node_modules/type-fest": { - "version": "0.21.3", - "resolved": "https://registry.npmjs.org/type-fest/-/type-fest-0.21.3.tgz", - "integrity": "sha512-t0rzBq87m3fVcduHDUFhKmyyX+9eo6WQjZvf51Ea/M0Q7+T374Jp1aUiyUl0GKxp8M/OETVHSDvmkyPgvX+X2w==", - "dev": true, - "engines": { - "node": ">=10" - }, - "funding": { - "url": "https://github.com/sponsors/sindresorhus" - } - }, "node_modules/ansi-regex": { "version": "5.0.1", "resolved": "https://registry.npmjs.org/ansi-regex/-/ansi-regex-5.0.1.tgz", @@ -1147,18 +1445,6 @@ "url": "https://github.com/sponsors/sindresorhus" } }, - "node_modules/boxen/node_modules/camelcase": { - "version": "6.2.0", - "resolved": "https://registry.npmjs.org/camelcase/-/camelcase-6.2.0.tgz", - "integrity": "sha512-c7wVvbw3f37nuobQNtgsgG9POC9qMbNuMQmTCqZv23b6MIz0fcYpBiOlv9gEN/hdLdnZTDQhg6e9Dq5M1vKvfg==", - "dev": true, - "engines": { - "node": ">=10" - }, - "funding": { - "url": "https://github.com/sponsors/sindresorhus" - } - }, "node_modules/boxen/node_modules/type-fest": { "version": "0.20.2", "resolved": "https://registry.npmjs.org/type-fest/-/type-fest-0.20.2.tgz", @@ -1200,16 +1486,16 @@ "dev": true }, "node_modules/browserslist": { - "version": "4.17.3", - "resolved": "https://registry.npmjs.org/browserslist/-/browserslist-4.17.3.tgz", - "integrity": "sha512-59IqHJV5VGdcJZ+GZ2hU5n4Kv3YiASzW6Xk5g9tf5a/MAzGeFwgGWU39fVzNIOVcgB3+Gp+kiQu0HEfTVU/3VQ==", + "version": "4.18.1", + "resolved": "https://registry.npmjs.org/browserslist/-/browserslist-4.18.1.tgz", + "integrity": "sha512-8ScCzdpPwR2wQh8IT82CA2VgDwjHyqMovPBZSNH54+tm4Jk2pCuv90gmAdH6J84OCRWi0b4gMe6O6XPXuJnjgQ==", "dev": true, "dependencies": { - "caniuse-lite": "^1.0.30001264", - "electron-to-chromium": "^1.3.857", + "caniuse-lite": "^1.0.30001280", + "electron-to-chromium": "^1.3.896", "escalade": "^3.1.1", - "node-releases": "^1.1.77", - "picocolors": "^0.2.1" + "node-releases": "^2.0.1", + "picocolors": "^1.0.0" }, "bin": { "browserslist": "cli.js" @@ -1332,18 +1618,21 @@ } }, "node_modules/camelcase": { - "version": "5.3.1", - "resolved": "https://registry.npmjs.org/camelcase/-/camelcase-5.3.1.tgz", - "integrity": "sha512-L28STB170nwWS63UjtlEOE3dldQApaJXZkOI1uMFfzf3rRuPegHaHesyee+YxQ+W6SvRDQV6UrdOdRiR153wJg==", + "version": "6.2.1", + "resolved": "https://registry.npmjs.org/camelcase/-/camelcase-6.2.1.tgz", + "integrity": "sha512-tVI4q5jjFV5CavAU8DXfza/TJcZutVKo/5Foskmsqcm0MsL91moHvwiGNnqaa2o6PF/7yT5ikDRcVcl8Rj6LCA==", "dev": true, "engines": { - "node": ">=6" + "node": ">=10" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" } }, "node_modules/caniuse-lite": { - "version": "1.0.30001265", - "resolved": "https://registry.npmjs.org/caniuse-lite/-/caniuse-lite-1.0.30001265.tgz", - "integrity": "sha512-YzBnspggWV5hep1m9Z6sZVLOt7vrju8xWooFAgN6BA5qvy98qPAPb7vNUzypFaoh2pb3vlfzbDO8tB57UPGbtw==", + "version": "1.0.30001280", + "resolved": "https://registry.npmjs.org/caniuse-lite/-/caniuse-lite-1.0.30001280.tgz", + "integrity": "sha512-kFXwYvHe5rix25uwueBxC569o53J6TpnGu0BEEn+6Lhl2vsnAumRFWEBhDft1fwyo6m1r4i+RqA4+163FpeFcA==", "dev": true, "funding": { "type": "opencollective", @@ -1366,18 +1655,6 @@ "url": "https://github.com/chalk/chalk?sponsor=1" } }, - "node_modules/chalk/node_modules/supports-color": { - "version": "7.2.0", - "resolved": "https://registry.npmjs.org/supports-color/-/supports-color-7.2.0.tgz", - "integrity": "sha512-qpCAvRl9stuOHveKsn7HncJRvv501qIacKzQlO/+Lwxc9+0q2wLyv4Dfvt80/DPn2pqOBsJdDiogXGR9+OvwRw==", - "dev": true, - "dependencies": { - "has-flag": "^4.0.0" - }, - "engines": { - "node": ">=8" - } - }, "node_modules/chardet": { "version": "0.7.0", "resolved": "https://registry.npmjs.org/chardet/-/chardet-0.7.0.tgz", @@ -1570,7 +1847,13 @@ "safe-buffer": "~5.1.1" } }, - "node_modules/cosmiconfig": { + "node_modules/convert-source-map/node_modules/safe-buffer": { + "version": "5.1.2", + "resolved": "https://registry.npmjs.org/safe-buffer/-/safe-buffer-5.1.2.tgz", + "integrity": "sha512-Gd2UZBJDkXlY7GbJxfsE8/nvKkUEU1G38c1siN6QP6a9PT9MmHB8GnpscSmMJSoF8LOIrt8ud/wPtojys4G6+g==", + "dev": true + }, + "node_modules/cosmiconfig": { "version": "7.0.1", "resolved": "https://registry.npmjs.org/cosmiconfig/-/cosmiconfig-7.0.1.tgz", "integrity": "sha512-a1YWNUV2HwGimB7dU2s1wUMurNKjpx60HxBB6xUM8Re+2s1g1IIfJvFR0/iCF+XHdE0GMTKTuLR32UQff4TEyQ==", @@ -1632,12 +1915,6 @@ } } }, - "node_modules/debug/node_modules/ms": { - "version": "2.1.2", - "resolved": "https://registry.npmjs.org/ms/-/ms-2.1.2.tgz", - "integrity": "sha512-sGkPx+VjMtmA6MX27oA4FBFELFCZZ4S4XqeGOXCv68tT+jb3vk/RyaKWP0PTKyWtmLSM0b+adUTEvbs1PEaH2w==", - "dev": true - }, "node_modules/decamelize": { "version": "1.2.0", "resolved": "https://registry.npmjs.org/decamelize/-/decamelize-1.2.0.tgz", @@ -1692,6 +1969,12 @@ "node": ">=4.0.0" } }, + "node_modules/deep-is": { + "version": "0.1.4", + "resolved": "https://registry.npmjs.org/deep-is/-/deep-is-0.1.4.tgz", + "integrity": "sha512-oIPzksmTg4/MriiaYGO+okXDT7ztn/w3Eptv/+gSIdMdKsJo0u4CfYNFJPy+4SKMuCqGw2wxnA+URMg3t8a/bQ==", + "dev": true + }, "node_modules/default-require-extensions": { "version": "3.0.0", "resolved": "https://registry.npmjs.org/default-require-extensions/-/default-require-extensions-3.0.0.tgz", @@ -1771,6 +2054,18 @@ "node": ">=8" } }, + "node_modules/doctrine": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/doctrine/-/doctrine-3.0.0.tgz", + "integrity": "sha512-yS+Q5i3hBf7GBkd4KG8a7eBNNWNGLTaEwwYWUijIYM7zrlYDM0BFXHjjPWlWZ1Rg7UaddZeIDmi9jF3HmqiQ2w==", + "dev": true, + "dependencies": { + "esutils": "^2.0.2" + }, + "engines": { + "node": ">=6.0.0" + } + }, "node_modules/dot-prop": { "version": "5.3.0", "resolved": "https://registry.npmjs.org/dot-prop/-/dot-prop-5.3.0.tgz", @@ -1790,9 +2085,9 @@ "dev": true }, "node_modules/electron-to-chromium": { - "version": "1.3.864", - "resolved": "https://registry.npmjs.org/electron-to-chromium/-/electron-to-chromium-1.3.864.tgz", - "integrity": "sha512-v4rbad8GO6/yVI92WOeU9Wgxc4NA0n4f6P1FvZTY+jyY7JHEhw3bduYu60v3Q1h81Cg6eo4ApZrFPuycwd5hGw==", + "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==", "dev": true }, "node_modules/emoji-regex": { @@ -1810,6 +2105,18 @@ "once": "^1.4.0" } }, + "node_modules/enquirer": { + "version": "2.3.6", + "resolved": "https://registry.npmjs.org/enquirer/-/enquirer-2.3.6.tgz", + "integrity": "sha512-yjNnPr315/FjS4zIsUxYguYUPP2e1NK4d7E7ZOLiyYCcbFBiTMyID+2wvm2w6+pZ/odMA7cRkjhsPbltwBOrLg==", + "dev": true, + "dependencies": { + "ansi-colors": "^4.1.1" + }, + "engines": { + "node": ">=8.6" + } + }, "node_modules/error-ex": { "version": "1.3.2", "resolved": "https://registry.npmjs.org/error-ex/-/error-ex-1.3.2.tgz", @@ -1844,6 +2151,119 @@ } }, "node_modules/escape-string-regexp": { + "version": "1.0.5", + "resolved": "https://registry.npmjs.org/escape-string-regexp/-/escape-string-regexp-1.0.5.tgz", + "integrity": "sha1-G2HAViGQqN/2rjuyzwIAyhMLhtQ=", + "dev": true, + "engines": { + "node": ">=0.8.0" + } + }, + "node_modules/eslint": { + "version": "8.2.0", + "resolved": "https://registry.npmjs.org/eslint/-/eslint-8.2.0.tgz", + "integrity": "sha512-erw7XmM+CLxTOickrimJ1SiF55jiNlVSp2qqm0NuBWPtHYQCegD5ZMaW0c3i5ytPqL+SSLaCxdvQXFPLJn+ABw==", + "dev": true, + "dependencies": { + "@eslint/eslintrc": "^1.0.4", + "@humanwhocodes/config-array": "^0.6.0", + "ajv": "^6.10.0", + "chalk": "^4.0.0", + "cross-spawn": "^7.0.2", + "debug": "^4.3.2", + "doctrine": "^3.0.0", + "enquirer": "^2.3.5", + "escape-string-regexp": "^4.0.0", + "eslint-scope": "^6.0.0", + "eslint-utils": "^3.0.0", + "eslint-visitor-keys": "^3.0.0", + "espree": "^9.0.0", + "esquery": "^1.4.0", + "esutils": "^2.0.2", + "fast-deep-equal": "^3.1.3", + "file-entry-cache": "^6.0.1", + "functional-red-black-tree": "^1.0.1", + "glob-parent": "^6.0.1", + "globals": "^13.6.0", + "ignore": "^4.0.6", + "import-fresh": "^3.0.0", + "imurmurhash": "^0.1.4", + "is-glob": "^4.0.0", + "js-yaml": "^4.1.0", + "json-stable-stringify-without-jsonify": "^1.0.1", + "levn": "^0.4.1", + "lodash.merge": "^4.6.2", + "minimatch": "^3.0.4", + "natural-compare": "^1.4.0", + "optionator": "^0.9.1", + "progress": "^2.0.0", + "regexpp": "^3.2.0", + "semver": "^7.2.1", + "strip-ansi": "^6.0.1", + "strip-json-comments": "^3.1.0", + "text-table": "^0.2.0", + "v8-compile-cache": "^2.0.3" + }, + "bin": { + "eslint": "bin/eslint.js" + }, + "engines": { + "node": "^12.22.0 || ^14.17.0 || >=16.0.0" + }, + "funding": { + "url": "https://opencollective.com/eslint" + } + }, + "node_modules/eslint-scope": { + "version": "5.1.1", + "resolved": "https://registry.npmjs.org/eslint-scope/-/eslint-scope-5.1.1.tgz", + "integrity": "sha512-2NxwbF/hZ0KpepYN0cNbo+FN6XoK7GaHlQhgx/hIZl6Va0bF45RQOOwhLIy8lQDbuCiadSLCBnH2CFYquit5bw==", + "dev": true, + "dependencies": { + "esrecurse": "^4.3.0", + "estraverse": "^4.1.1" + }, + "engines": { + "node": ">=8.0.0" + } + }, + "node_modules/eslint-utils": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/eslint-utils/-/eslint-utils-3.0.0.tgz", + "integrity": "sha512-uuQC43IGctw68pJA1RgbQS8/NP7rch6Cwd4j3ZBtgo4/8Flj4eGE7ZYSZRN3iq5pVUv6GPdW5Z1RFleo84uLDA==", + "dev": true, + "dependencies": { + "eslint-visitor-keys": "^2.0.0" + }, + "engines": { + "node": "^10.0.0 || ^12.0.0 || >= 14.0.0" + }, + "funding": { + "url": "https://github.com/sponsors/mysticatea" + }, + "peerDependencies": { + "eslint": ">=5" + } + }, + "node_modules/eslint-utils/node_modules/eslint-visitor-keys": { + "version": "2.1.0", + "resolved": "https://registry.npmjs.org/eslint-visitor-keys/-/eslint-visitor-keys-2.1.0.tgz", + "integrity": "sha512-0rSmRBzXgDzIsD6mGdJgevzgezI534Cer5L/vyMX0kHzT/jiB43jRhd9YUlMGYLQy2zprNmoT8qasCGtY+QaKw==", + "dev": true, + "engines": { + "node": ">=10" + } + }, + "node_modules/eslint-visitor-keys": { + "version": "3.1.0", + "resolved": "https://registry.npmjs.org/eslint-visitor-keys/-/eslint-visitor-keys-3.1.0.tgz", + "integrity": "sha512-yWJFpu4DtjsWKkt5GeNBBuZMlNcYVs6vRCLoCVEJrTjaSB6LC98gFipNK/erM2Heg/E8mIK+hXG/pJMLK+eRZA==", + "dev": true, + "engines": { + "node": "^12.22.0 || ^14.17.0 || >=16.0.0" + } + }, + "node_modules/eslint/node_modules/escape-string-regexp": { "version": "4.0.0", "resolved": "https://registry.npmjs.org/escape-string-regexp/-/escape-string-regexp-4.0.0.tgz", "integrity": "sha512-TtpcNJ3XAzx3Gq8sWRzJaVajRs0uVxA2YAkdb1jm2YkPz4G6egUFAyA3n5vtEIZefPk5Wa4UXbKuS5fKkJWdgA==", @@ -1855,6 +2275,75 @@ "url": "https://github.com/sponsors/sindresorhus" } }, + "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==", + "dev": true, + "dependencies": { + "esrecurse": "^4.3.0", + "estraverse": "^5.2.0" + }, + "engines": { + "node": "^12.22.0 || ^14.17.0 || >=16.0.0" + } + }, + "node_modules/eslint/node_modules/estraverse": { + "version": "5.3.0", + "resolved": "https://registry.npmjs.org/estraverse/-/estraverse-5.3.0.tgz", + "integrity": "sha512-MMdARuVEQziNTeJD8DgMqmhwR11BRQ/cBP+pLtYdSTnf3MIO8fFeiINEbX36ZdNlfU/7A9f3gUw49B3oQsvwBA==", + "dev": true, + "engines": { + "node": ">=4.0" + } + }, + "node_modules/eslint/node_modules/glob-parent": { + "version": "6.0.2", + "resolved": "https://registry.npmjs.org/glob-parent/-/glob-parent-6.0.2.tgz", + "integrity": "sha512-XxwI8EOhVQgWp6iDL+3b0r86f4d6AX6zSU55HfB4ydCEuXLXc5FcYeOu+nnGftS4TEju/11rt4KJPTMgbfmv4A==", + "dev": true, + "dependencies": { + "is-glob": "^4.0.3" + }, + "engines": { + "node": ">=10.13.0" + } + }, + "node_modules/eslint/node_modules/ignore": { + "version": "4.0.6", + "resolved": "https://registry.npmjs.org/ignore/-/ignore-4.0.6.tgz", + "integrity": "sha512-cyFDKrqc/YdcWFniJhzI42+AzS+gNwmUzOSFcRCQYwySuBBBy/KjuxWLZ/FHEH6Moq1NizMOBWyTcv8O4OZIMg==", + "dev": true, + "engines": { + "node": ">= 4" + } + }, + "node_modules/eslint/node_modules/strip-json-comments": { + "version": "3.1.1", + "resolved": "https://registry.npmjs.org/strip-json-comments/-/strip-json-comments-3.1.1.tgz", + "integrity": "sha512-6fPc+R4ihwqP6N/aIv2f1gMH8lOVtWQHoqC4yK6oSDVVocumAsfCqjkXnqiYMhmMwS/mEHLp7Vehlt3ql6lEig==", + "dev": true, + "engines": { + "node": ">=8" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/espree": { + "version": "9.0.0", + "resolved": "https://registry.npmjs.org/espree/-/espree-9.0.0.tgz", + "integrity": "sha512-r5EQJcYZ2oaGbeR0jR0fFVijGOcwai07/690YRXLINuhmVeRY4UKSAsQPe/0BNuDgwP7Ophoc1PRsr2E3tkbdQ==", + "dev": true, + "dependencies": { + "acorn": "^8.5.0", + "acorn-jsx": "^5.3.1", + "eslint-visitor-keys": "^3.0.0" + }, + "engines": { + "node": "^12.22.0 || ^14.17.0 || >=16.0.0" + } + }, "node_modules/esprima": { "version": "4.0.1", "resolved": "https://registry.npmjs.org/esprima/-/esprima-4.0.1.tgz", @@ -1868,6 +2357,66 @@ "node": ">=4" } }, + "node_modules/esquery": { + "version": "1.4.0", + "resolved": "https://registry.npmjs.org/esquery/-/esquery-1.4.0.tgz", + "integrity": "sha512-cCDispWt5vHHtwMY2YrAQ4ibFkAL8RbH5YGBnZBc90MolvvfkkQcJro/aZiAQUlQ3qgrYS6D6v8Gc5G5CQsc9w==", + "dev": true, + "dependencies": { + "estraverse": "^5.1.0" + }, + "engines": { + "node": ">=0.10" + } + }, + "node_modules/esquery/node_modules/estraverse": { + "version": "5.3.0", + "resolved": "https://registry.npmjs.org/estraverse/-/estraverse-5.3.0.tgz", + "integrity": "sha512-MMdARuVEQziNTeJD8DgMqmhwR11BRQ/cBP+pLtYdSTnf3MIO8fFeiINEbX36ZdNlfU/7A9f3gUw49B3oQsvwBA==", + "dev": true, + "engines": { + "node": ">=4.0" + } + }, + "node_modules/esrecurse": { + "version": "4.3.0", + "resolved": "https://registry.npmjs.org/esrecurse/-/esrecurse-4.3.0.tgz", + "integrity": "sha512-KmfKL3b6G+RXvP8N1vr3Tq1kL/oCFgn2NYXEtqP8/L3pKapUA4G8cFVaoF3SU323CD4XypR/ffioHmkti6/Tag==", + "dev": true, + "dependencies": { + "estraverse": "^5.2.0" + }, + "engines": { + "node": ">=4.0" + } + }, + "node_modules/esrecurse/node_modules/estraverse": { + "version": "5.3.0", + "resolved": "https://registry.npmjs.org/estraverse/-/estraverse-5.3.0.tgz", + "integrity": "sha512-MMdARuVEQziNTeJD8DgMqmhwR11BRQ/cBP+pLtYdSTnf3MIO8fFeiINEbX36ZdNlfU/7A9f3gUw49B3oQsvwBA==", + "dev": true, + "engines": { + "node": ">=4.0" + } + }, + "node_modules/estraverse": { + "version": "4.3.0", + "resolved": "https://registry.npmjs.org/estraverse/-/estraverse-4.3.0.tgz", + "integrity": "sha512-39nnKffWz8xN1BU/2c79n9nB9HDzo0niYUqx6xyqUnyoAnQyyWpOTdZEeiCch8BBu515t4wp9ZmgVfVhn9EBpw==", + "dev": true, + "engines": { + "node": ">=4.0" + } + }, + "node_modules/esutils": { + "version": "2.0.3", + "resolved": "https://registry.npmjs.org/esutils/-/esutils-2.0.3.tgz", + "integrity": "sha512-kVscqXk4OCp68SZ0dkgEKVi6/8ij300KBWTJq32P/dYeWTSwK41WyTxalN1eRmA5Z9UU/LX9D7FWSmV9SAYx6g==", + "dev": true, + "engines": { + "node": ">=0.10.0" + } + }, "node_modules/execa": { "version": "5.1.1", "resolved": "https://registry.npmjs.org/execa/-/execa-5.1.1.tgz", @@ -1905,6 +2454,12 @@ "node": ">=4" } }, + "node_modules/fast-deep-equal": { + "version": "3.1.3", + "resolved": "https://registry.npmjs.org/fast-deep-equal/-/fast-deep-equal-3.1.3.tgz", + "integrity": "sha512-f3qQ9oQy9j2AhBe/H9VC91wLmKBCCU/gDOnKNAYG5hswO7BLKj09Hc5HYNz9cGI++xlpDCIgDaitVs03ATR84Q==", + "dev": true + }, "node_modules/fast-glob": { "version": "3.2.7", "resolved": "https://registry.npmjs.org/fast-glob/-/fast-glob-3.2.7.tgz", @@ -1921,6 +2476,18 @@ "node": ">=8" } }, + "node_modules/fast-json-stable-stringify": { + "version": "2.1.0", + "resolved": "https://registry.npmjs.org/fast-json-stable-stringify/-/fast-json-stable-stringify-2.1.0.tgz", + "integrity": "sha512-lhd/wF+Lk98HZoTCtlVraHtfh5XYijIjalXck7saUtuanSDyLMxnHhSXEDJqHxD7msR8D0uCmqlkwjCV8xvwHw==", + "dev": true + }, + "node_modules/fast-levenshtein": { + "version": "2.0.6", + "resolved": "https://registry.npmjs.org/fast-levenshtein/-/fast-levenshtein-2.0.6.tgz", + "integrity": "sha1-PYpcZog6FqMMqGQ+hR8Zuqd5eRc=", + "dev": true + }, "node_modules/fastq": { "version": "1.13.0", "resolved": "https://registry.npmjs.org/fastq/-/fastq-1.13.0.tgz", @@ -1945,13 +2512,16 @@ "url": "https://github.com/sponsors/sindresorhus" } }, - "node_modules/figures/node_modules/escape-string-regexp": { - "version": "1.0.5", - "resolved": "https://registry.npmjs.org/escape-string-regexp/-/escape-string-regexp-1.0.5.tgz", - "integrity": "sha1-G2HAViGQqN/2rjuyzwIAyhMLhtQ=", + "node_modules/file-entry-cache": { + "version": "6.0.1", + "resolved": "https://registry.npmjs.org/file-entry-cache/-/file-entry-cache-6.0.1.tgz", + "integrity": "sha512-7Gps/XWymbLk2QLYK4NzpMOrYjMhdIxXuIvy2QBsLE6ljuodKvdkWs/cpyJJ3CVIVpH0Oi1Hvg1ovbMzLdFBBg==", "dev": true, + "dependencies": { + "flat-cache": "^3.0.4" + }, "engines": { - "node": ">=0.8.0" + "node": "^10.12.0 || >=12.0.0" } }, "node_modules/fill-range": { @@ -2017,6 +2587,25 @@ "flat": "cli.js" } }, + "node_modules/flat-cache": { + "version": "3.0.4", + "resolved": "https://registry.npmjs.org/flat-cache/-/flat-cache-3.0.4.tgz", + "integrity": "sha512-dm9s5Pw7Jc0GvMYbshN6zchCA9RgQlzzEZX3vylR9IqFfS8XciblUXOKfW6SiuJ0e13eDYZoZV5wdrev7P3Nwg==", + "dev": true, + "dependencies": { + "flatted": "^3.1.0", + "rimraf": "^3.0.2" + }, + "engines": { + "node": "^10.12.0 || >=12.0.0" + } + }, + "node_modules/flatted": { + "version": "3.2.4", + "resolved": "https://registry.npmjs.org/flatted/-/flatted-3.2.4.tgz", + "integrity": "sha512-8/sOawo8tJ4QOBX8YlQBMxL8+RLZfxMQOif9o0KUKTNTjMYElWPE0r/m5VNFxTRd0NSw8qSy8dajrwX4RYI1Hw==", + "dev": true + }, "node_modules/foreground-child": { "version": "2.0.0", "resolved": "https://registry.npmjs.org/foreground-child/-/foreground-child-2.0.0.tgz", @@ -2090,6 +2679,12 @@ "integrity": "sha512-yIovAzMX49sF8Yl58fSCWJ5svSLuaibPxXQJFLmBObTuCr0Mf1KiPopGM9NiFjiYBCbfaa2Fh6breQ6ANVTI0A==", "dev": true }, + "node_modules/functional-red-black-tree": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/functional-red-black-tree/-/functional-red-black-tree-1.0.1.tgz", + "integrity": "sha1-GwqzvVU7Kg1jmdKcDj6gslIHgyc=", + "dev": true + }, "node_modules/generic-pool": { "version": "3.8.2", "resolved": "https://registry.npmjs.org/generic-pool/-/generic-pool-3.8.2.tgz", @@ -2171,9 +2766,9 @@ } }, "node_modules/glob": { - "version": "7.1.7", - "resolved": "https://registry.npmjs.org/glob/-/glob-7.1.7.tgz", - "integrity": "sha512-OvD9ENzPLbegENnYP5UUfJIirTg4+XwMWGaQfQTY0JenxNvvIKP3U3/tAQSPIu/lHxXYSZmpXlUHeqAIdKzBLQ==", + "version": "7.2.0", + "resolved": "https://registry.npmjs.org/glob/-/glob-7.2.0.tgz", + "integrity": "sha512-lmLf6gtyrPq8tTjSmrO94wBeQbFR3HbLHbuyD69wuyQkImp2hWqMGB47OX65FBkPffO641IP9jWa1z4ivqG26Q==", "dev": true, "dependencies": { "fs.realpath": "^1.0.0", @@ -2218,12 +2813,30 @@ } }, "node_modules/globals": { - "version": "11.12.0", - "resolved": "https://registry.npmjs.org/globals/-/globals-11.12.0.tgz", - "integrity": "sha512-WOBp/EEGUiIsJSp7wcv/y6MO+lV9UoncWqxuFfm8eBwzWNgyfBd6Gz+IeKQ9jCmyhoH99g15M3T+QaVHFjizVA==", + "version": "13.12.0", + "resolved": "https://registry.npmjs.org/globals/-/globals-13.12.0.tgz", + "integrity": "sha512-uS8X6lSKN2JumVoXrbUz+uG4BYG+eiawqm3qFcT7ammfbUHeCBoJMlHcec/S3krSk73/AE/f0szYFmgAA3kYZg==", "dev": true, + "dependencies": { + "type-fest": "^0.20.2" + }, "engines": { - "node": ">=4" + "node": ">=8" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/globals/node_modules/type-fest": { + "version": "0.20.2", + "resolved": "https://registry.npmjs.org/type-fest/-/type-fest-0.20.2.tgz", + "integrity": "sha512-Ne+eE4r0/iWnpAxD852z3A+N0Bt5RN//NjJwRd2VFHEmrywxf5vsZlh4R6lixl6B+wz/8d+maTSAkN1FIkI3LQ==", + "dev": true, + "engines": { + "node": ">=10" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" } }, "node_modules/globby": { @@ -2374,6 +2987,15 @@ "url": "https://github.com/sponsors/sindresorhus" } }, + "node_modules/hasha/node_modules/type-fest": { + "version": "0.8.1", + "resolved": "https://registry.npmjs.org/type-fest/-/type-fest-0.8.1.tgz", + "integrity": "sha512-4dbzIzqvjtgiM5rw1k5rEHtBANKmdudhGyBEajN01fEyhaAIhsoKNy6y7+IN93IfpFtwY9iqi7kD+xwKhQsNJA==", + "dev": true, + "engines": { + "node": ">=8" + } + }, "node_modules/he": { "version": "1.2.0", "resolved": "https://registry.npmjs.org/he/-/he-1.2.0.tgz", @@ -2450,9 +3072,9 @@ ] }, "node_modules/ignore": { - "version": "5.1.8", - "resolved": "https://registry.npmjs.org/ignore/-/ignore-5.1.8.tgz", - "integrity": "sha512-BMpfD7PpiETpBl/A6S498BaIJ6Y/ABT93ETbby2fP00v4EbvPBXWEoaR1UBPKs3iR53pJY7EtZk5KACI57i1Uw==", + "version": "5.1.9", + "resolved": "https://registry.npmjs.org/ignore/-/ignore-5.1.9.tgz", + "integrity": "sha512-2zeMQpbKz5dhZ9IwL0gbxSW5w0NK/MSAMtNuhgIHEPmaU3vPdKPL0UdvUCXs5SS4JAwsBxysK5sFMW8ocFiVjQ==", "dev": true, "engines": { "node": ">= 4" @@ -2486,15 +3108,6 @@ "url": "https://github.com/sponsors/sindresorhus" } }, - "node_modules/import-fresh/node_modules/resolve-from": { - "version": "4.0.0", - "resolved": "https://registry.npmjs.org/resolve-from/-/resolve-from-4.0.0.tgz", - "integrity": "sha512-pb/MYmXstAkysRFx8piNI1tGFNQIFA3vkE3Gq4EuA1dF6gHp/+vgZqsCGJapvy8N3Q+4o7FwvquPJcnZ7RYy4g==", - "dev": true, - "engines": { - "node": ">=4" - } - }, "node_modules/import-from": { "version": "3.0.0", "resolved": "https://registry.npmjs.org/import-from/-/import-from-3.0.0.tgz", @@ -2507,6 +3120,15 @@ "node": ">=8" } }, + "node_modules/import-from/node_modules/resolve-from": { + "version": "5.0.0", + "resolved": "https://registry.npmjs.org/resolve-from/-/resolve-from-5.0.0.tgz", + "integrity": "sha512-qYg9KP24dD5qka9J47d0aVky0N+b4fTU89LN9iDnjB5waksiC49rvMB0PrUJQGoTmH50XPiqOvAjDfaijGxYZw==", + "dev": true, + "engines": { + "node": ">=8" + } + }, "node_modules/import-lazy": { "version": "2.1.0", "resolved": "https://registry.npmjs.org/import-lazy/-/import-lazy-2.1.0.tgz", @@ -2624,9 +3246,9 @@ } }, "node_modules/is-core-module": { - "version": "2.7.0", - "resolved": "https://registry.npmjs.org/is-core-module/-/is-core-module-2.7.0.tgz", - "integrity": "sha512-ByY+tjCciCr+9nLryBYcSD50EOGWt95c7tIsKTG1J2ixKKXPvF7Ej3AVd+UfDydAJom3biBGDBALaO79ktwgEQ==", + "version": "2.8.0", + "resolved": "https://registry.npmjs.org/is-core-module/-/is-core-module-2.8.0.tgz", + "integrity": "sha512-vd15qHsaqrRL7dtH6QNuy0ndJmRDrS9HAM1CAiSifNUFv4x1a0CCVsj18hJ1mShxIG6T2i1sO78MkP56r0nYRw==", "dev": true, "dependencies": { "has": "^1.0.3" @@ -2841,9 +3463,9 @@ "dev": true }, "node_modules/istanbul-lib-coverage": { - "version": "3.0.0", - "resolved": "https://registry.npmjs.org/istanbul-lib-coverage/-/istanbul-lib-coverage-3.0.0.tgz", - "integrity": "sha512-UiUIqxMgRDET6eR+o5HbfRYP1l0hqkWOs7vNxC/mggutCMUIhWMm8gAHb8tHlyfD3/l6rlgNA5cKdDzEAf6hEg==", + "version": "3.2.0", + "resolved": "https://registry.npmjs.org/istanbul-lib-coverage/-/istanbul-lib-coverage-3.2.0.tgz", + "integrity": "sha512-eOeJ5BHCmHYvQK7xt9GkdHuzuCGS1Y6g9Gvnx3Ym33fz/HpLRYxiS0wHNr+m/MBC8B647Xt608vCDEvhl9c6Mw==", "dev": true, "engines": { "node": ">=8" @@ -2876,6 +3498,15 @@ "node": ">=8" } }, + "node_modules/istanbul-lib-instrument/node_modules/semver": { + "version": "6.3.0", + "resolved": "https://registry.npmjs.org/semver/-/semver-6.3.0.tgz", + "integrity": "sha512-b39TBaTSfV6yBrapU89p5fKekE2m/NwnDocOVruQFS1/veMgdzuPcnOM34M6CwxW8jH/lxEa5rBoDeUwu5HHTw==", + "dev": true, + "bin": { + "semver": "bin/semver.js" + } + }, "node_modules/istanbul-lib-processinfo": { "version": "2.0.2", "resolved": "https://registry.npmjs.org/istanbul-lib-processinfo/-/istanbul-lib-processinfo-2.0.2.tgz", @@ -2894,6 +3525,16 @@ "node": ">=8" } }, + "node_modules/istanbul-lib-processinfo/node_modules/uuid": { + "version": "3.4.0", + "resolved": "https://registry.npmjs.org/uuid/-/uuid-3.4.0.tgz", + "integrity": "sha512-HjSDRw6gZE5JMggctHBcjVak08+KEVhSIiDzFnT9S9aegmp85S/bReBVTb4QTFaRNptJ9kuYaNhnbNEOkbKb/A==", + "deprecated": "Please upgrade to version 7 or higher. Older versions may use Math.random() in certain circumstances, which is known to be problematic. See https://v8.dev/blog/math-random for details.", + "dev": true, + "bin": { + "uuid": "bin/uuid" + } + }, "node_modules/istanbul-lib-report": { "version": "3.0.0", "resolved": "https://registry.npmjs.org/istanbul-lib-report/-/istanbul-lib-report-3.0.0.tgz", @@ -2908,22 +3549,10 @@ "node": ">=8" } }, - "node_modules/istanbul-lib-report/node_modules/supports-color": { - "version": "7.2.0", - "resolved": "https://registry.npmjs.org/supports-color/-/supports-color-7.2.0.tgz", - "integrity": "sha512-qpCAvRl9stuOHveKsn7HncJRvv501qIacKzQlO/+Lwxc9+0q2wLyv4Dfvt80/DPn2pqOBsJdDiogXGR9+OvwRw==", - "dev": true, - "dependencies": { - "has-flag": "^4.0.0" - }, - "engines": { - "node": ">=8" - } - }, "node_modules/istanbul-lib-source-maps": { - "version": "4.0.0", - "resolved": "https://registry.npmjs.org/istanbul-lib-source-maps/-/istanbul-lib-source-maps-4.0.0.tgz", - "integrity": "sha512-c16LpFRkR8vQXyHZ5nLpY35JZtzj1PQY1iZmesUbf1FZHbIupcWfjgOXBY9YHkLEQ6puz1u4Dgj6qmU/DisrZg==", + "version": "4.0.1", + "resolved": "https://registry.npmjs.org/istanbul-lib-source-maps/-/istanbul-lib-source-maps-4.0.1.tgz", + "integrity": "sha512-n3s8EwkdFIJCG3BPKBYvskgXGoy88ARzvegkitk60NxRdwltLOTaH7CUiMRXvwYorl0Q712iEjcWB+fK/MrWVw==", "dev": true, "dependencies": { "debug": "^4.1.1", @@ -2931,7 +3560,7 @@ "source-map": "^0.6.1" }, "engines": { - "node": ">=8" + "node": ">=10" } }, "node_modules/istanbul-lib-source-maps/node_modules/source-map": { @@ -2944,9 +3573,9 @@ } }, "node_modules/istanbul-reports": { - "version": "3.0.3", - "resolved": "https://registry.npmjs.org/istanbul-reports/-/istanbul-reports-3.0.3.tgz", - "integrity": "sha512-0i77ZFLsb9U3DHi22WzmIngVzfoyxxbQcZRqlF3KoKmCJGq9nhFHoGi8FqBztN2rE8w6hURnZghetn0xpkVb6A==", + "version": "3.0.5", + "resolved": "https://registry.npmjs.org/istanbul-reports/-/istanbul-reports-3.0.5.tgz", + "integrity": "sha512-5+19PlhnGabNWB7kOFnuxT8H3T/iIyQzIbQMxXsURmmvKg86P2sbkrGOT77VnHw0Qr0gc2XzRaRfMZYYbSQCJQ==", "dev": true, "dependencies": { "html-escaper": "^2.0.0", @@ -2998,6 +3627,18 @@ "integrity": "sha512-xyFwyhro/JEof6Ghe2iz2NcXoj2sloNsWr/XsERDK/oiPCfaNhl5ONfp+jQdAZRQQ0IJWNzH9zIZF7li91kh2w==", "dev": true }, + "node_modules/json-schema-traverse": { + "version": "0.4.1", + "resolved": "https://registry.npmjs.org/json-schema-traverse/-/json-schema-traverse-0.4.1.tgz", + "integrity": "sha512-xbbCH5dCYU5T8LcEhhuh7HJ88HXuW3qsI3Y0zOZFKfZEHcpWiHU/Jxzk629Brsab/mMiHQti9wMP+845RPe3Vg==", + "dev": true + }, + "node_modules/json-stable-stringify-without-jsonify": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/json-stable-stringify-without-jsonify/-/json-stable-stringify-without-jsonify-1.0.1.tgz", + "integrity": "sha1-nbe1lJatPzz+8wp1FC0tkwrXJlE=", + "dev": true + }, "node_modules/json5": { "version": "2.2.0", "resolved": "https://registry.npmjs.org/json5/-/json5-2.2.0.tgz", @@ -3026,9 +3667,9 @@ "dev": true }, "node_modules/keyv": { - "version": "4.0.3", - "resolved": "https://registry.npmjs.org/keyv/-/keyv-4.0.3.tgz", - "integrity": "sha512-zdGa2TOpSZPq5mU6iowDARnMBZgtCqJ11dJROFi6tg6kTn4nuUdU09lFyLFSaHrWqpIJ+EBq4E8/Dc0Vx5vLdA==", + "version": "4.0.4", + "resolved": "https://registry.npmjs.org/keyv/-/keyv-4.0.4.tgz", + "integrity": "sha512-vqNHbAc8BBsxk+7QBYLW0Y219rWcClspR6WSeoHYKG5mnsSoOH+BL1pWq02DDCVdvvuUny5rkBlzMRzoqc+GIg==", "dev": true, "dependencies": { "json-buffer": "3.0.1" @@ -3046,6 +3687,19 @@ "node": ">=8" } }, + "node_modules/levn": { + "version": "0.4.1", + "resolved": "https://registry.npmjs.org/levn/-/levn-0.4.1.tgz", + "integrity": "sha512-+bT2uH4E5LGE7h/n3evcS/sQlJXCpIp6ym8OWJ5eV6+67Dsql/LaaT7qJBAt2rzfoa/5QBGBhxDix1dMt2kQKQ==", + "dev": true, + "dependencies": { + "prelude-ls": "^1.2.1", + "type-check": "~0.4.0" + }, + "engines": { + "node": ">= 0.8.0" + } + }, "node_modules/lines-and-columns": { "version": "1.1.6", "resolved": "https://registry.npmjs.org/lines-and-columns/-/lines-and-columns-1.1.6.tgz", @@ -3085,6 +3739,12 @@ "integrity": "sha1-LRd/ZS+jHpObRDjVNBSZ36OCXpk=", "dev": true }, + "node_modules/lodash.merge": { + "version": "4.6.2", + "resolved": "https://registry.npmjs.org/lodash.merge/-/lodash.merge-4.6.2.tgz", + "integrity": "sha512-0KpjqXRVvrYyCsX1swR/XTK0va6VQkQM6MNo7PqW77ByjAhoARA8EfrP1N4+KlKj8YS0ZUCtRT/YUuhyYDujIQ==", + "dev": true + }, "node_modules/log-symbols": { "version": "4.1.0", "resolved": "https://registry.npmjs.org/log-symbols/-/log-symbols-4.1.0.tgz", @@ -3111,20 +3771,17 @@ } }, "node_modules/lru-cache": { - "version": "5.1.1", - "resolved": "https://registry.npmjs.org/lru-cache/-/lru-cache-5.1.1.tgz", - "integrity": "sha512-KpNARQA3Iwv+jTA0utUVVbrh+Jlrr1Fv0e56GGzAFOXN7dk/FviaDW8LHmK52DlcH4WP2n6gI8vN1aesBFgo9w==", + "version": "6.0.0", + "resolved": "https://registry.npmjs.org/lru-cache/-/lru-cache-6.0.0.tgz", + "integrity": "sha512-Jo6dJ04CmSjuznwJSS3pUeWmd/H0ffTlkXXgwZi+eq1UCmqQwCh+eLsYOYCwY991i2Fah4h1BEMCx4qThGbsiA==", "dev": true, "dependencies": { - "yallist": "^3.0.2" + "yallist": "^4.0.0" + }, + "engines": { + "node": ">=10" } }, - "node_modules/lru-cache/node_modules/yallist": { - "version": "3.1.1", - "resolved": "https://registry.npmjs.org/yallist/-/yallist-3.1.1.tgz", - "integrity": "sha512-a4UGQaWPH59mOXUYnAG2ewncQS4i4F43Tv3JoAM+s2VDAmS9NsK8GpDMLrCHPksFT7h3K6TOoUNn2pb7RoXx4g==", - "dev": true - }, "node_modules/lunr": { "version": "2.3.9", "resolved": "https://registry.npmjs.org/lunr/-/lunr-2.3.9.tgz", @@ -3158,6 +3815,15 @@ "url": "https://github.com/sponsors/sindresorhus" } }, + "node_modules/make-dir/node_modules/semver": { + "version": "6.3.0", + "resolved": "https://registry.npmjs.org/semver/-/semver-6.3.0.tgz", + "integrity": "sha512-b39TBaTSfV6yBrapU89p5fKekE2m/NwnDocOVruQFS1/veMgdzuPcnOM34M6CwxW8jH/lxEa5rBoDeUwu5HHTw==", + "dev": true, + "bin": { + "semver": "bin/semver.js" + } + }, "node_modules/make-error": { "version": "1.3.6", "resolved": "https://registry.npmjs.org/make-error/-/make-error-1.3.6.tgz", @@ -3165,9 +3831,9 @@ "dev": true }, "node_modules/marked": { - "version": "3.0.7", - "resolved": "https://registry.npmjs.org/marked/-/marked-3.0.7.tgz", - "integrity": "sha512-ctKqbnLuNbsHbI26cfMyOlKgXGfl1orOv1AvWWDX7AkgfMOwCWvmuYc+mVLeWhQ9W6hdWVBynOs96VkcscKo0Q==", + "version": "3.0.8", + "resolved": "https://registry.npmjs.org/marked/-/marked-3.0.8.tgz", + "integrity": "sha512-0gVrAjo5m0VZSJb4rpL59K1unJAMb/hm8HRXqasD8VeC8m91ytDPMritgFSlKonfdt+rRYYpP/JfLxgIX8yoSw==", "dev": true, "bin": { "marked": "bin/marked" @@ -3262,9 +3928,9 @@ "dev": true }, "node_modules/mocha": { - "version": "9.1.2", - "resolved": "https://registry.npmjs.org/mocha/-/mocha-9.1.2.tgz", - "integrity": "sha512-ta3LtJ+63RIBP03VBjMGtSqbe6cWXRejF9SyM9Zyli1CKZJZ+vfCTj3oW24V7wAphMJdpOFLoMI3hjJ1LWbs0w==", + "version": "9.1.3", + "resolved": "https://registry.npmjs.org/mocha/-/mocha-9.1.3.tgz", + "integrity": "sha512-Xcpl9FqXOAYqI3j79pEtHBBnQgVXIhpULjGQa7DVb0Po+VzmSIK9kanAiWLHoRR/dbZ2qpdPshuXr8l1VaHCzw==", "dev": true, "dependencies": { "@ungap/promise-all-settled": "1.1.2", @@ -3304,12 +3970,104 @@ "url": "https://opencollective.com/mochajs" } }, - "node_modules/ms": { + "node_modules/mocha/node_modules/escape-string-regexp": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/escape-string-regexp/-/escape-string-regexp-4.0.0.tgz", + "integrity": "sha512-TtpcNJ3XAzx3Gq8sWRzJaVajRs0uVxA2YAkdb1jm2YkPz4G6egUFAyA3n5vtEIZefPk5Wa4UXbKuS5fKkJWdgA==", + "dev": true, + "engines": { + "node": ">=10" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/mocha/node_modules/glob": { + "version": "7.1.7", + "resolved": "https://registry.npmjs.org/glob/-/glob-7.1.7.tgz", + "integrity": "sha512-OvD9ENzPLbegENnYP5UUfJIirTg4+XwMWGaQfQTY0JenxNvvIKP3U3/tAQSPIu/lHxXYSZmpXlUHeqAIdKzBLQ==", + "dev": true, + "dependencies": { + "fs.realpath": "^1.0.0", + "inflight": "^1.0.4", + "inherits": "2", + "minimatch": "^3.0.4", + "once": "^1.3.0", + "path-is-absolute": "^1.0.0" + }, + "engines": { + "node": "*" + }, + "funding": { + "url": "https://github.com/sponsors/isaacs" + } + }, + "node_modules/mocha/node_modules/ms": { "version": "2.1.3", "resolved": "https://registry.npmjs.org/ms/-/ms-2.1.3.tgz", "integrity": "sha512-6FlzubTLZG3J2a/NVCAleEhjzq5oxgHyaCU9yYXvcLsvoVaHJq/s5xXI6/XXP6tz7R9xAOtHnSO/tXtF3WRTlA==", "dev": true }, + "node_modules/mocha/node_modules/strip-json-comments": { + "version": "3.1.1", + "resolved": "https://registry.npmjs.org/strip-json-comments/-/strip-json-comments-3.1.1.tgz", + "integrity": "sha512-6fPc+R4ihwqP6N/aIv2f1gMH8lOVtWQHoqC4yK6oSDVVocumAsfCqjkXnqiYMhmMwS/mEHLp7Vehlt3ql6lEig==", + "dev": true, + "engines": { + "node": ">=8" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/mocha/node_modules/supports-color": { + "version": "8.1.1", + "resolved": "https://registry.npmjs.org/supports-color/-/supports-color-8.1.1.tgz", + "integrity": "sha512-MpUEN2OodtUzxvKQl72cUF7RQ5EiHsGvSsVG0ia9c5RbWGL2CI4C7EpPS8UTBIplnlzZiNuV56w+FuNxy3ty2Q==", + "dev": true, + "dependencies": { + "has-flag": "^4.0.0" + }, + "engines": { + "node": ">=10" + }, + "funding": { + "url": "https://github.com/chalk/supports-color?sponsor=1" + } + }, + "node_modules/mocha/node_modules/yargs": { + "version": "16.2.0", + "resolved": "https://registry.npmjs.org/yargs/-/yargs-16.2.0.tgz", + "integrity": "sha512-D1mvvtDG0L5ft/jGWkLpG1+m0eQxOfaBvTNELraWj22wSVUMWxZUvYgJYcKh6jGGIkJFhH4IZPQhR4TKpc8mBw==", + "dev": true, + "dependencies": { + "cliui": "^7.0.2", + "escalade": "^3.1.1", + "get-caller-file": "^2.0.5", + "require-directory": "^2.1.1", + "string-width": "^4.2.0", + "y18n": "^5.0.5", + "yargs-parser": "^20.2.2" + }, + "engines": { + "node": ">=10" + } + }, + "node_modules/mocha/node_modules/yargs-parser": { + "version": "20.2.4", + "resolved": "https://registry.npmjs.org/yargs-parser/-/yargs-parser-20.2.4.tgz", + "integrity": "sha512-WOkpgNhPTlE73h4VFAFsOnomJVaovO8VqLDzy5saChRBFQFBoMYirowyW+Q9HB4HFF4Z7VZTiG3iSzJJA29yRA==", + "dev": true, + "engines": { + "node": ">=10" + } + }, + "node_modules/ms": { + "version": "2.1.2", + "resolved": "https://registry.npmjs.org/ms/-/ms-2.1.2.tgz", + "integrity": "sha512-sGkPx+VjMtmA6MX27oA4FBFELFCZZ4S4XqeGOXCv68tT+jb3vk/RyaKWP0PTKyWtmLSM0b+adUTEvbs1PEaH2w==", + "dev": true + }, "node_modules/mute-stream": { "version": "0.0.8", "resolved": "https://registry.npmjs.org/mute-stream/-/mute-stream-0.0.8.tgz", @@ -3328,6 +4086,12 @@ "node": "^10 || ^12 || ^13.7 || ^14 || >=15.0.1" } }, + "node_modules/natural-compare": { + "version": "1.4.0", + "resolved": "https://registry.npmjs.org/natural-compare/-/natural-compare-1.4.0.tgz", + "integrity": "sha1-Sr6/7tdUHywnrPspvbvRXI1bpPc=", + "dev": true + }, "node_modules/neo-async": { "version": "2.6.2", "resolved": "https://registry.npmjs.org/neo-async/-/neo-async-2.6.2.tgz", @@ -3369,9 +4133,9 @@ } }, "node_modules/node-fetch": { - "version": "2.6.5", - "resolved": "https://registry.npmjs.org/node-fetch/-/node-fetch-2.6.5.tgz", - "integrity": "sha512-mmlIVHJEu5rnIxgEgez6b9GgWXbkZj5YZ7fx+2r94a2E+Uirsp6HsPTPlomfdHtpt/B0cdKviwkoaM6pyvUOpQ==", + "version": "2.6.6", + "resolved": "https://registry.npmjs.org/node-fetch/-/node-fetch-2.6.6.tgz", + "integrity": "sha512-Z8/6vRlTUChSdIgMa51jxQ4lrw/Jy5SOW10ObaA47/RElsAN2c5Pn8bTgFGWn/ibwzXTE8qwr1Yzx28vsecXEA==", "dev": true, "dependencies": { "whatwg-url": "^5.0.0" @@ -3393,9 +4157,9 @@ } }, "node_modules/node-releases": { - "version": "1.1.77", - "resolved": "https://registry.npmjs.org/node-releases/-/node-releases-1.1.77.tgz", - "integrity": "sha512-rB1DUFUNAN4Gn9keO2K1efO35IDK7yKHCdCaIMvFO7yUYmmZYeDjnGKle26G4rwj+LKRQpjyUUvMkPglwGCYNQ==", + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/node-releases/-/node-releases-2.0.1.tgz", + "integrity": "sha512-CqyzN6z7Q6aMeF/ktcMVTzhAHCEpf8SOarwpzpf8pNBY2k5/oM34UHldUwp8VKI7uxct2HxSRdJjBaZeESzcxA==", "dev": true }, "node_modules/normalize-path": { @@ -3472,6 +4236,15 @@ "node": ">=8.9" } }, + "node_modules/nyc/node_modules/camelcase": { + "version": "5.3.1", + "resolved": "https://registry.npmjs.org/camelcase/-/camelcase-5.3.1.tgz", + "integrity": "sha512-L28STB170nwWS63UjtlEOE3dldQApaJXZkOI1uMFfzf3rRuPegHaHesyee+YxQ+W6SvRDQV6UrdOdRiR153wJg==", + "dev": true, + "engines": { + "node": ">=6" + } + }, "node_modules/nyc/node_modules/cliui": { "version": "6.0.0", "resolved": "https://registry.npmjs.org/cliui/-/cliui-6.0.0.tgz", @@ -3535,6 +4308,15 @@ "node": ">=8" } }, + "node_modules/nyc/node_modules/resolve-from": { + "version": "5.0.0", + "resolved": "https://registry.npmjs.org/resolve-from/-/resolve-from-5.0.0.tgz", + "integrity": "sha512-qYg9KP24dD5qka9J47d0aVky0N+b4fTU89LN9iDnjB5waksiC49rvMB0PrUJQGoTmH50XPiqOvAjDfaijGxYZw==", + "dev": true, + "engines": { + "node": ">=8" + } + }, "node_modules/nyc/node_modules/wrap-ansi": { "version": "6.2.0", "resolved": "https://registry.npmjs.org/wrap-ansi/-/wrap-ansi-6.2.0.tgz", @@ -3632,6 +4414,21 @@ "lru-cache": "^5.1.1" } }, + "node_modules/onigasm/node_modules/lru-cache": { + "version": "5.1.1", + "resolved": "https://registry.npmjs.org/lru-cache/-/lru-cache-5.1.1.tgz", + "integrity": "sha512-KpNARQA3Iwv+jTA0utUVVbrh+Jlrr1Fv0e56GGzAFOXN7dk/FviaDW8LHmK52DlcH4WP2n6gI8vN1aesBFgo9w==", + "dev": true, + "dependencies": { + "yallist": "^3.0.2" + } + }, + "node_modules/onigasm/node_modules/yallist": { + "version": "3.1.1", + "resolved": "https://registry.npmjs.org/yallist/-/yallist-3.1.1.tgz", + "integrity": "sha512-a4UGQaWPH59mOXUYnAG2ewncQS4i4F43Tv3JoAM+s2VDAmS9NsK8GpDMLrCHPksFT7h3K6TOoUNn2pb7RoXx4g==", + "dev": true + }, "node_modules/open": { "version": "7.4.2", "resolved": "https://registry.npmjs.org/open/-/open-7.4.2.tgz", @@ -3648,6 +4445,23 @@ "url": "https://github.com/sponsors/sindresorhus" } }, + "node_modules/optionator": { + "version": "0.9.1", + "resolved": "https://registry.npmjs.org/optionator/-/optionator-0.9.1.tgz", + "integrity": "sha512-74RlY5FCnhq4jRxVUPKDaRwrVNXMqsGsiW6AJw4XK8hmtm10wC0ypZBLw5IIp85NZMr91+qd1RvvENwg7jjRFw==", + "dev": true, + "dependencies": { + "deep-is": "^0.1.3", + "fast-levenshtein": "^2.0.6", + "levn": "^0.4.1", + "prelude-ls": "^1.2.1", + "type-check": "^0.4.0", + "word-wrap": "^1.2.3" + }, + "engines": { + "node": ">= 0.8.0" + } + }, "node_modules/ora": { "version": "5.4.1", "resolved": "https://registry.npmjs.org/ora/-/ora-5.4.1.tgz", @@ -3952,6 +4766,15 @@ "node": ">=0.10.0" } }, + "node_modules/package-json/node_modules/semver": { + "version": "6.3.0", + "resolved": "https://registry.npmjs.org/semver/-/semver-6.3.0.tgz", + "integrity": "sha512-b39TBaTSfV6yBrapU89p5fKekE2m/NwnDocOVruQFS1/veMgdzuPcnOM34M6CwxW8jH/lxEa5rBoDeUwu5HHTw==", + "dev": true, + "bin": { + "semver": "bin/semver.js" + } + }, "node_modules/parent-module": { "version": "1.0.1", "resolved": "https://registry.npmjs.org/parent-module/-/parent-module-1.0.1.tgz", @@ -4058,9 +4881,9 @@ } }, "node_modules/picocolors": { - "version": "0.2.1", - "resolved": "https://registry.npmjs.org/picocolors/-/picocolors-0.2.1.tgz", - "integrity": "sha512-cMlDqaLEqfSaW8Z7N5Jw+lyIW869EzT73/F5lhtY9cLGoVxSXznfgfXMO0Z5K0o0Q2TkTXq+0KFsdnSe3jDViA==", + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/picocolors/-/picocolors-1.0.0.tgz", + "integrity": "sha512-1fygroTLlHu66zi26VoTDv8yRgm0Fccecssto+MhsZ0D/DGW2sm8E8AjW7NU5VVTRt5GxbeZ5qBuJr+HyLYkjQ==", "dev": true }, "node_modules/picomatch": { @@ -4139,6 +4962,15 @@ "node": ">=8" } }, + "node_modules/prelude-ls": { + "version": "1.2.1", + "resolved": "https://registry.npmjs.org/prelude-ls/-/prelude-ls-1.2.1.tgz", + "integrity": "sha512-vkcDPrRZo1QZLbn5RLGPpg/WmIQ65qoWWhcGKf/b5eplkkarX0m9z8ppCat4mlOqUsWpyNuYgO3VRyrYHSzX5g==", + "dev": true, + "engines": { + "node": ">= 0.8.0" + } + }, "node_modules/prepend-http": { "version": "2.0.0", "resolved": "https://registry.npmjs.org/prepend-http/-/prepend-http-2.0.0.tgz", @@ -4160,6 +4992,15 @@ "node": ">=8" } }, + "node_modules/progress": { + "version": "2.0.3", + "resolved": "https://registry.npmjs.org/progress/-/progress-2.0.3.tgz", + "integrity": "sha512-7PiHtLll5LdnKIMw100I+8xJXR5gW2QwWYkT6iJva0bXitZKa/XMrSbdmg3r2Xnaidz9Qumd0VPaMrZlF9V9sA==", + "dev": true, + "engines": { + "node": ">=0.4.0" + } + }, "node_modules/protocols": { "version": "1.4.8", "resolved": "https://registry.npmjs.org/protocols/-/protocols-1.4.8.tgz", @@ -4176,6 +5017,15 @@ "once": "^1.3.1" } }, + "node_modules/punycode": { + "version": "2.1.1", + "resolved": "https://registry.npmjs.org/punycode/-/punycode-2.1.1.tgz", + "integrity": "sha512-XRsRjdf+j5ml+y/6GKHPZbrF/8p2Yga0JPtdqTIY2Xe5ohJPD9saDJJLPvp9+NSBprVvevdXZybnj2cv8OEd0A==", + "dev": true, + "engines": { + "node": ">=6" + } + }, "node_modules/pupa": { "version": "2.1.1", "resolved": "https://registry.npmjs.org/pupa/-/pupa-2.1.1.tgz", @@ -4283,15 +5133,6 @@ "integrity": "sha512-JV/yugV2uzW5iMRSiZAyDtQd+nxtUnjeLt0acNdw98kKLrvuRVyB80tsREOE7yvGVgalhZ6RNXCmEHkUKBKxew==", "dev": true }, - "node_modules/rc/node_modules/strip-json-comments": { - "version": "2.0.1", - "resolved": "https://registry.npmjs.org/strip-json-comments/-/strip-json-comments-2.0.1.tgz", - "integrity": "sha1-PFMZQukIwml8DsNEhYwobHygpgo=", - "dev": true, - "engines": { - "node": ">=0.10.0" - } - }, "node_modules/readable-stream": { "version": "3.6.0", "resolved": "https://registry.npmjs.org/readable-stream/-/readable-stream-3.6.0.tgz", @@ -4349,6 +5190,18 @@ "node": ">=4" } }, + "node_modules/regexpp": { + "version": "3.2.0", + "resolved": "https://registry.npmjs.org/regexpp/-/regexpp-3.2.0.tgz", + "integrity": "sha512-pq2bWo9mVD43nbts2wGv17XLiNLya+GklZ8kaDLV2Z08gDCsGpnKn9BFMepvWuHCbyVvY7J5o5+BVvoQbmlJLg==", + "dev": true, + "engines": { + "node": ">=8" + }, + "funding": { + "url": "https://github.com/sponsors/mysticatea" + } + }, "node_modules/registry-auth-token": { "version": "4.2.1", "resolved": "https://registry.npmjs.org/registry-auth-token/-/registry-auth-token-4.2.1.tgz", @@ -4374,9 +5227,9 @@ } }, "node_modules/release-it": { - "version": "14.11.6", - "resolved": "https://registry.npmjs.org/release-it/-/release-it-14.11.6.tgz", - "integrity": "sha512-6BNcuzFZHThBUBJ/xYw/bxZ+58CAwrwf1zgmjq2Ibl3nlDZbjphHG6iqxkJu7mZ8TIWs6NjloEAhqpjeXoN//Q==", + "version": "14.11.7", + "resolved": "https://registry.npmjs.org/release-it/-/release-it-14.11.7.tgz", + "integrity": "sha512-m4p9+x6AEQPczc96Jyg6dGFeovpJVgRCtA1lxeIgTmQVt9dutYPkkjZeJngZgUJ17/Lb1bx6ZzW2qsKmopKnbQ==", "dev": true, "dependencies": { "@iarna/toml": "2.2.5", @@ -4416,70 +5269,25 @@ "node": ">=10" } }, - "node_modules/release-it/node_modules/lru-cache": { - "version": "6.0.0", - "resolved": "https://registry.npmjs.org/lru-cache/-/lru-cache-6.0.0.tgz", - "integrity": "sha512-Jo6dJ04CmSjuznwJSS3pUeWmd/H0ffTlkXXgwZi+eq1UCmqQwCh+eLsYOYCwY991i2Fah4h1BEMCx4qThGbsiA==", + "node_modules/release-zalgo": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/release-zalgo/-/release-zalgo-1.0.0.tgz", + "integrity": "sha1-CXALflB0Mpc5Mw5TXFqQ+2eFFzA=", "dev": true, "dependencies": { - "yallist": "^4.0.0" + "es6-error": "^4.0.1" }, "engines": { - "node": ">=10" + "node": ">=4" } }, - "node_modules/release-it/node_modules/semver": { - "version": "7.3.5", - "resolved": "https://registry.npmjs.org/semver/-/semver-7.3.5.tgz", - "integrity": "sha512-PoeGJYh8HK4BTO/a9Tf6ZG3veo/A7ZVsYrSA6J8ny9nb3B1VrpkuN+z9OE5wfE5p6H4LchYZsegiQgbJD94ZFQ==", + "node_modules/require-directory": { + "version": "2.1.1", + "resolved": "https://registry.npmjs.org/require-directory/-/require-directory-2.1.1.tgz", + "integrity": "sha1-jGStX9MNqxyXbiNE/+f3kqam30I=", "dev": true, - "dependencies": { - "lru-cache": "^6.0.0" - }, - "bin": { - "semver": "bin/semver.js" - }, "engines": { - "node": ">=10" - } - }, - "node_modules/release-it/node_modules/uuid": { - "version": "8.3.2", - "resolved": "https://registry.npmjs.org/uuid/-/uuid-8.3.2.tgz", - "integrity": "sha512-+NYs2QeMWy+GWFOEm9xnn6HCDp0l7QBD7ml8zLUmJ+93Q5NF0NocErnwkTkXVFNiX3/fpC6afS8Dhb/gz7R7eg==", - "dev": true, - "bin": { - "uuid": "dist/bin/uuid" - } - }, - "node_modules/release-it/node_modules/yargs-parser": { - "version": "20.2.9", - "resolved": "https://registry.npmjs.org/yargs-parser/-/yargs-parser-20.2.9.tgz", - "integrity": "sha512-y11nGElTIV+CT3Zv9t7VKl+Q3hTQoT9a1Qzezhhl6Rp21gJ/IVTW7Z3y9EWXhuUBC2Shnf+DX0antecpAwSP8w==", - "dev": true, - "engines": { - "node": ">=10" - } - }, - "node_modules/release-zalgo": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/release-zalgo/-/release-zalgo-1.0.0.tgz", - "integrity": "sha1-CXALflB0Mpc5Mw5TXFqQ+2eFFzA=", - "dev": true, - "dependencies": { - "es6-error": "^4.0.1" - }, - "engines": { - "node": ">=4" - } - }, - "node_modules/require-directory": { - "version": "2.1.1", - "resolved": "https://registry.npmjs.org/require-directory/-/require-directory-2.1.1.tgz", - "integrity": "sha1-jGStX9MNqxyXbiNE/+f3kqam30I=", - "dev": true, - "engines": { - "node": ">=0.10.0" + "node": ">=0.10.0" } }, "node_modules/require-main-filename": { @@ -4508,12 +5316,12 @@ "dev": true }, "node_modules/resolve-from": { - "version": "5.0.0", - "resolved": "https://registry.npmjs.org/resolve-from/-/resolve-from-5.0.0.tgz", - "integrity": "sha512-qYg9KP24dD5qka9J47d0aVky0N+b4fTU89LN9iDnjB5waksiC49rvMB0PrUJQGoTmH50XPiqOvAjDfaijGxYZw==", + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/resolve-from/-/resolve-from-4.0.0.tgz", + "integrity": "sha512-pb/MYmXstAkysRFx8piNI1tGFNQIFA3vkE3Gq4EuA1dF6gHp/+vgZqsCGJapvy8N3Q+4o7FwvquPJcnZ7RYy4g==", "dev": true, "engines": { - "node": ">=8" + "node": ">=4" } }, "node_modules/responselike": { @@ -4614,10 +5422,24 @@ } }, "node_modules/safe-buffer": { - "version": "5.1.2", - "resolved": "https://registry.npmjs.org/safe-buffer/-/safe-buffer-5.1.2.tgz", - "integrity": "sha512-Gd2UZBJDkXlY7GbJxfsE8/nvKkUEU1G38c1siN6QP6a9PT9MmHB8GnpscSmMJSoF8LOIrt8ud/wPtojys4G6+g==", - "dev": true + "version": "5.2.1", + "resolved": "https://registry.npmjs.org/safe-buffer/-/safe-buffer-5.2.1.tgz", + "integrity": "sha512-rp3So07KcdmmKbGvgaNxQSJr7bGVSVk5S9Eq1F+ppbRo70+YeaDxkw5Dd8NPN+GD6bjnYm2VuPuCXmpuYvmCXQ==", + "dev": true, + "funding": [ + { + "type": "github", + "url": "https://github.com/sponsors/feross" + }, + { + "type": "patreon", + "url": "https://www.patreon.com/feross" + }, + { + "type": "consulting", + "url": "https://feross.org/support" + } + ] }, "node_modules/safer-buffer": { "version": "2.1.2", @@ -4626,12 +5448,18 @@ "dev": true }, "node_modules/semver": { - "version": "6.3.0", - "resolved": "https://registry.npmjs.org/semver/-/semver-6.3.0.tgz", - "integrity": "sha512-b39TBaTSfV6yBrapU89p5fKekE2m/NwnDocOVruQFS1/veMgdzuPcnOM34M6CwxW8jH/lxEa5rBoDeUwu5HHTw==", + "version": "7.3.5", + "resolved": "https://registry.npmjs.org/semver/-/semver-7.3.5.tgz", + "integrity": "sha512-PoeGJYh8HK4BTO/a9Tf6ZG3veo/A7ZVsYrSA6J8ny9nb3B1VrpkuN+z9OE5wfE5p6H4LchYZsegiQgbJD94ZFQ==", "dev": true, + "dependencies": { + "lru-cache": "^6.0.0" + }, "bin": { "semver": "bin/semver.js" + }, + "engines": { + "node": ">=10" } }, "node_modules/semver-diff": { @@ -4646,6 +5474,15 @@ "node": ">=8" } }, + "node_modules/semver-diff/node_modules/semver": { + "version": "6.3.0", + "resolved": "https://registry.npmjs.org/semver/-/semver-6.3.0.tgz", + "integrity": "sha512-b39TBaTSfV6yBrapU89p5fKekE2m/NwnDocOVruQFS1/veMgdzuPcnOM34M6CwxW8jH/lxEa5rBoDeUwu5HHTw==", + "dev": true, + "bin": { + "semver": "bin/semver.js" + } + }, "node_modules/serialize-javascript": { "version": "6.0.0", "resolved": "https://registry.npmjs.org/serialize-javascript/-/serialize-javascript-6.0.0.tgz", @@ -4700,9 +5537,9 @@ } }, "node_modules/shiki": { - "version": "0.9.11", - "resolved": "https://registry.npmjs.org/shiki/-/shiki-0.9.11.tgz", - "integrity": "sha512-tjruNTLFhU0hruCPoJP0y+B9LKOmcqUhTpxn7pcJB3fa+04gFChuEmxmrUfOJ7ZO6Jd+HwMnDHgY3lv3Tqonuw==", + "version": "0.9.12", + "resolved": "https://registry.npmjs.org/shiki/-/shiki-0.9.12.tgz", + "integrity": "sha512-VXcROdldv0/Qu0w2XvzU4IrvTeBNs/Kj/FCmtcEXGz7Tic/veQzliJj6tEiAgoKianhQstpYmbPDStHU5Opqcw==", "dev": true, "dependencies": { "jsonc-parser": "^3.0.0", @@ -4731,13 +5568,13 @@ "dev": true }, "node_modules/sinon": { - "version": "11.1.2", - "resolved": "https://registry.npmjs.org/sinon/-/sinon-11.1.2.tgz", - "integrity": "sha512-59237HChms4kg7/sXhiRcUzdSkKuydDeTiamT/jesUVHshBgL8XAmhgFo0GfK6RruMDM/iRSij1EybmMog9cJw==", + "version": "12.0.1", + "resolved": "https://registry.npmjs.org/sinon/-/sinon-12.0.1.tgz", + "integrity": "sha512-iGu29Xhym33ydkAT+aNQFBINakjq69kKO6ByPvTsm3yyIACfyQttRTP03aBP/I8GfhFmLzrnKwNNkr0ORb1udg==", "dev": true, "dependencies": { "@sinonjs/commons": "^1.8.3", - "@sinonjs/fake-timers": "^7.1.2", + "@sinonjs/fake-timers": "^8.1.0", "@sinonjs/samsam": "^6.0.2", "diff": "^5.0.0", "nise": "^5.1.0", @@ -4748,16 +5585,13 @@ "url": "https://opencollective.com/sinon" } }, - "node_modules/sinon/node_modules/supports-color": { - "version": "7.2.0", - "resolved": "https://registry.npmjs.org/supports-color/-/supports-color-7.2.0.tgz", - "integrity": "sha512-qpCAvRl9stuOHveKsn7HncJRvv501qIacKzQlO/+Lwxc9+0q2wLyv4Dfvt80/DPn2pqOBsJdDiogXGR9+OvwRw==", + "node_modules/sinon/node_modules/@sinonjs/fake-timers": { + "version": "8.1.0", + "resolved": "https://registry.npmjs.org/@sinonjs/fake-timers/-/fake-timers-8.1.0.tgz", + "integrity": "sha512-OAPJUAtgeINhh/TAlUID4QTs53Njm7xzddaVlEs/SXwgtiD1tW22zAB/W1wdqfrpmikgaWQ9Fw6Ws+hsiRm5Vg==", "dev": true, "dependencies": { - "has-flag": "^4.0.0" - }, - "engines": { - "node": ">=8" + "@sinonjs/commons": "^1.7.0" } }, "node_modules/slash": { @@ -4847,26 +5681,6 @@ "safe-buffer": "~5.2.0" } }, - "node_modules/string_decoder/node_modules/safe-buffer": { - "version": "5.2.1", - "resolved": "https://registry.npmjs.org/safe-buffer/-/safe-buffer-5.2.1.tgz", - "integrity": "sha512-rp3So07KcdmmKbGvgaNxQSJr7bGVSVk5S9Eq1F+ppbRo70+YeaDxkw5Dd8NPN+GD6bjnYm2VuPuCXmpuYvmCXQ==", - "dev": true, - "funding": [ - { - "type": "github", - "url": "https://github.com/sponsors/feross" - }, - { - "type": "patreon", - "url": "https://www.patreon.com/feross" - }, - { - "type": "consulting", - "url": "https://feross.org/support" - } - ] - }, "node_modules/string-width": { "version": "4.2.3", "resolved": "https://registry.npmjs.org/string-width/-/string-width-4.2.3.tgz", @@ -4912,30 +5726,24 @@ } }, "node_modules/strip-json-comments": { - "version": "3.1.1", - "resolved": "https://registry.npmjs.org/strip-json-comments/-/strip-json-comments-3.1.1.tgz", - "integrity": "sha512-6fPc+R4ihwqP6N/aIv2f1gMH8lOVtWQHoqC4yK6oSDVVocumAsfCqjkXnqiYMhmMwS/mEHLp7Vehlt3ql6lEig==", + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/strip-json-comments/-/strip-json-comments-2.0.1.tgz", + "integrity": "sha1-PFMZQukIwml8DsNEhYwobHygpgo=", "dev": true, "engines": { - "node": ">=8" - }, - "funding": { - "url": "https://github.com/sponsors/sindresorhus" + "node": ">=0.10.0" } }, "node_modules/supports-color": { - "version": "8.1.1", - "resolved": "https://registry.npmjs.org/supports-color/-/supports-color-8.1.1.tgz", - "integrity": "sha512-MpUEN2OodtUzxvKQl72cUF7RQ5EiHsGvSsVG0ia9c5RbWGL2CI4C7EpPS8UTBIplnlzZiNuV56w+FuNxy3ty2Q==", + "version": "7.2.0", + "resolved": "https://registry.npmjs.org/supports-color/-/supports-color-7.2.0.tgz", + "integrity": "sha512-qpCAvRl9stuOHveKsn7HncJRvv501qIacKzQlO/+Lwxc9+0q2wLyv4Dfvt80/DPn2pqOBsJdDiogXGR9+OvwRw==", "dev": true, "dependencies": { "has-flag": "^4.0.0" }, "engines": { - "node": ">=10" - }, - "funding": { - "url": "https://github.com/chalk/supports-color?sponsor=1" + "node": ">=8" } }, "node_modules/test-exclude": { @@ -4952,6 +5760,12 @@ "node": ">=8" } }, + "node_modules/text-table": { + "version": "0.2.0", + "resolved": "https://registry.npmjs.org/text-table/-/text-table-0.2.0.tgz", + "integrity": "sha1-f17oI66AUgfACvLfSoTsP8+lcLQ=", + "dev": true + }, "node_modules/through": { "version": "2.3.8", "resolved": "https://registry.npmjs.org/through/-/through-2.3.8.tgz", @@ -5007,9 +5821,9 @@ "dev": true }, "node_modules/ts-node": { - "version": "10.3.0", - "resolved": "https://registry.npmjs.org/ts-node/-/ts-node-10.3.0.tgz", - "integrity": "sha512-RYIy3i8IgpFH45AX4fQHExrT8BxDeKTdC83QFJkNzkvt8uFB6QJ8XMyhynYiKMLxt9a7yuXaDBZNOYS3XjDcYw==", + "version": "10.4.0", + "resolved": "https://registry.npmjs.org/ts-node/-/ts-node-10.4.0.tgz", + "integrity": "sha512-g0FlPvvCXSIO1JDF6S232P5jPYqBkRL9qly81ZgAOSU7rwI0stphCgd2kLiCrU9DjQCrJMWEqcNSjQL02s6d8A==", "dev": true, "dependencies": { "@cspotcode/source-map-support": "0.7.0", @@ -5062,6 +5876,39 @@ "integrity": "sha512-hcVC3wYEziELGGmEEXue7D75zbwIIVUMWAVbHItGPx0ziyXxrOMQx4rQEVEV45Ut/1IotuEvwqPopzIOkDMf0A==", "dev": true }, + "node_modules/tsutils": { + "version": "3.21.0", + "resolved": "https://registry.npmjs.org/tsutils/-/tsutils-3.21.0.tgz", + "integrity": "sha512-mHKK3iUXL+3UF6xL5k0PEhKRUBKPBCv/+RkEOpjRWxxx27KKRBmmA60A9pgOUvMi8GKhRMPEmjBRPzs2W7O1OA==", + "dev": true, + "dependencies": { + "tslib": "^1.8.1" + }, + "engines": { + "node": ">= 6" + }, + "peerDependencies": { + "typescript": ">=2.8.0 || >= 3.2.0-dev || >= 3.3.0-dev || >= 3.4.0-dev || >= 3.5.0-dev || >= 3.6.0-dev || >= 3.6.0-beta || >= 3.7.0-dev || >= 3.7.0-beta" + } + }, + "node_modules/tsutils/node_modules/tslib": { + "version": "1.14.1", + "resolved": "https://registry.npmjs.org/tslib/-/tslib-1.14.1.tgz", + "integrity": "sha512-Xni35NKzjgMrwevysHTCArtLDpPvye8zV/0E4EyYn43P7/7qvQwPh9BGkHewbMulVntbigmcT7rdX3BNo9wRJg==", + "dev": true + }, + "node_modules/type-check": { + "version": "0.4.0", + "resolved": "https://registry.npmjs.org/type-check/-/type-check-0.4.0.tgz", + "integrity": "sha512-XleUoc9uwGXqjWwXaUTZAmzMcFZ5858QA2vvx1Ur5xIcixXIP+8LnFDgRplU30us6teqdlskFfu+ae4K79Ooew==", + "dev": true, + "dependencies": { + "prelude-ls": "^1.2.1" + }, + "engines": { + "node": ">= 0.8.0" + } + }, "node_modules/type-detect": { "version": "4.0.8", "resolved": "https://registry.npmjs.org/type-detect/-/type-detect-4.0.8.tgz", @@ -5072,12 +5919,15 @@ } }, "node_modules/type-fest": { - "version": "0.8.1", - "resolved": "https://registry.npmjs.org/type-fest/-/type-fest-0.8.1.tgz", - "integrity": "sha512-4dbzIzqvjtgiM5rw1k5rEHtBANKmdudhGyBEajN01fEyhaAIhsoKNy6y7+IN93IfpFtwY9iqi7kD+xwKhQsNJA==", + "version": "0.21.3", + "resolved": "https://registry.npmjs.org/type-fest/-/type-fest-0.21.3.tgz", + "integrity": "sha512-t0rzBq87m3fVcduHDUFhKmyyX+9eo6WQjZvf51Ea/M0Q7+T374Jp1aUiyUl0GKxp8M/OETVHSDvmkyPgvX+X2w==", "dev": true, "engines": { - "node": ">=8" + "node": ">=10" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" } }, "node_modules/typedarray-to-buffer": { @@ -5090,16 +5940,16 @@ } }, "node_modules/typedoc": { - "version": "0.22.5", - "resolved": "https://registry.npmjs.org/typedoc/-/typedoc-0.22.5.tgz", - "integrity": "sha512-KFrWGU1iKiTGw0RcyjLNYDmhd7uICU14HgBNPmFKY/sT4Pm/fraaLyWyisst9vGTUAKxqibqoDITR7+ZcAkhHg==", + "version": "0.22.9", + "resolved": "https://registry.npmjs.org/typedoc/-/typedoc-0.22.9.tgz", + "integrity": "sha512-84PjudoXVcap6bwdZFbYIUWlgdz/iLV09ZHwrCzhtHWXaDQG6mlosJ8te6DSThuRkRvQjp46HO+qY/P7Gpm78g==", "dev": true, "dependencies": { "glob": "^7.2.0", "lunr": "^2.3.9", - "marked": "^3.0.4", + "marked": "^3.0.8", "minimatch": "^3.0.4", - "shiki": "^0.9.11" + "shiki": "^0.9.12" }, "bin": { "typedoc": "bin/typedoc" @@ -5122,9 +5972,9 @@ } }, "node_modules/typedoc-plugin-markdown": { - "version": "3.11.3", - "resolved": "https://registry.npmjs.org/typedoc-plugin-markdown/-/typedoc-plugin-markdown-3.11.3.tgz", - "integrity": "sha512-rWiHbEIe0oZetDIsBR24XJVxGOJ91kDcHoj2KhFKxCLoJGX659EKBQkHne9QJ4W2stGhu1fRgFyQaouSBnxukA==", + "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==", "dev": true, "dependencies": { "handlebars": "^4.7.7" @@ -5133,30 +5983,10 @@ "typedoc": ">=0.22.0" } }, - "node_modules/typedoc/node_modules/glob": { - "version": "7.2.0", - "resolved": "https://registry.npmjs.org/glob/-/glob-7.2.0.tgz", - "integrity": "sha512-lmLf6gtyrPq8tTjSmrO94wBeQbFR3HbLHbuyD69wuyQkImp2hWqMGB47OX65FBkPffO641IP9jWa1z4ivqG26Q==", - "dev": true, - "dependencies": { - "fs.realpath": "^1.0.0", - "inflight": "^1.0.4", - "inherits": "2", - "minimatch": "^3.0.4", - "once": "^1.3.0", - "path-is-absolute": "^1.0.0" - }, - "engines": { - "node": "*" - }, - "funding": { - "url": "https://github.com/sponsors/isaacs" - } - }, "node_modules/typescript": { - "version": "4.4.3", - "resolved": "https://registry.npmjs.org/typescript/-/typescript-4.4.3.tgz", - "integrity": "sha512-4xfscpisVgqqDfPaJo5vkd+Qd/ItkoagnHpufr+i2QCHBsNYp+G7UAoyFl8aPtx879u38wPV65rZ8qbGZijalA==", + "version": "4.4.4", + "resolved": "https://registry.npmjs.org/typescript/-/typescript-4.4.4.tgz", + "integrity": "sha512-DqGhF5IKoBl8WNf8C1gu8q0xZSInh9j1kJJMqT3a94w1JzVaBU4EXOSMrz9yDqMT0xt3selp83fuFMQ0uzv6qA==", "dev": true, "bin": { "tsc": "bin/tsc", @@ -5167,9 +5997,9 @@ } }, "node_modules/uglify-js": { - "version": "3.14.2", - "resolved": "https://registry.npmjs.org/uglify-js/-/uglify-js-3.14.2.tgz", - "integrity": "sha512-rtPMlmcO4agTUfz10CbgJ1k6UAoXM2gWb3GoMPPZB/+/Ackf8lNWk11K4rYi2D0apgoFRLtQOZhb+/iGNJq26A==", + "version": "3.14.3", + "resolved": "https://registry.npmjs.org/uglify-js/-/uglify-js-3.14.3.tgz", + "integrity": "sha512-mic3aOdiq01DuSVx0TseaEzMIVqebMZ0Z3vaeDhFEh9bsc24hV1TFvN74reA2vs08D0ZWfNjAcJ3UbVLaBss+g==", "dev": true, "optional": true, "bin": { @@ -5243,31 +6073,13 @@ "is-ci": "bin.js" } }, - "node_modules/update-notifier/node_modules/lru-cache": { - "version": "6.0.0", - "resolved": "https://registry.npmjs.org/lru-cache/-/lru-cache-6.0.0.tgz", - "integrity": "sha512-Jo6dJ04CmSjuznwJSS3pUeWmd/H0ffTlkXXgwZi+eq1UCmqQwCh+eLsYOYCwY991i2Fah4h1BEMCx4qThGbsiA==", + "node_modules/uri-js": { + "version": "4.4.1", + "resolved": "https://registry.npmjs.org/uri-js/-/uri-js-4.4.1.tgz", + "integrity": "sha512-7rKUyy33Q1yc98pQ1DAmLtwX109F7TIfWlW1Ydo8Wl1ii1SeHieeh0HHfPeL2fMXK6z0s8ecKs9frCuLJvndBg==", "dev": true, "dependencies": { - "yallist": "^4.0.0" - }, - "engines": { - "node": ">=10" - } - }, - "node_modules/update-notifier/node_modules/semver": { - "version": "7.3.5", - "resolved": "https://registry.npmjs.org/semver/-/semver-7.3.5.tgz", - "integrity": "sha512-PoeGJYh8HK4BTO/a9Tf6ZG3veo/A7ZVsYrSA6J8ny9nb3B1VrpkuN+z9OE5wfE5p6H4LchYZsegiQgbJD94ZFQ==", - "dev": true, - "dependencies": { - "lru-cache": "^6.0.0" - }, - "bin": { - "semver": "bin/semver.js" - }, - "engines": { - "node": ">=10" + "punycode": "^2.1.0" } }, "node_modules/url-join": { @@ -5295,15 +6107,20 @@ "dev": true }, "node_modules/uuid": { - "version": "3.4.0", - "resolved": "https://registry.npmjs.org/uuid/-/uuid-3.4.0.tgz", - "integrity": "sha512-HjSDRw6gZE5JMggctHBcjVak08+KEVhSIiDzFnT9S9aegmp85S/bReBVTb4QTFaRNptJ9kuYaNhnbNEOkbKb/A==", - "deprecated": "Please upgrade to version 7 or higher. Older versions may use Math.random() in certain circumstances, which is known to be problematic. See https://v8.dev/blog/math-random for details.", + "version": "8.3.2", + "resolved": "https://registry.npmjs.org/uuid/-/uuid-8.3.2.tgz", + "integrity": "sha512-+NYs2QeMWy+GWFOEm9xnn6HCDp0l7QBD7ml8zLUmJ+93Q5NF0NocErnwkTkXVFNiX3/fpC6afS8Dhb/gz7R7eg==", "dev": true, "bin": { - "uuid": "bin/uuid" + "uuid": "dist/bin/uuid" } }, + "node_modules/v8-compile-cache": { + "version": "2.3.0", + "resolved": "https://registry.npmjs.org/v8-compile-cache/-/v8-compile-cache-2.3.0.tgz", + "integrity": "sha512-l8lCEmLcLYZh4nbunNZvQCJc5pv7+RCwa8q/LdUx8u7lsWvPDKmpodJAJNwkAhJC//dFY48KuIEmjtd4RViDrA==", + "dev": true + }, "node_modules/vscode-textmate": { "version": "5.2.0", "resolved": "https://registry.npmjs.org/vscode-textmate/-/vscode-textmate-5.2.0.tgz", @@ -5430,6 +6247,15 @@ "node": ">=8.12.0" } }, + "node_modules/word-wrap": { + "version": "1.2.3", + "resolved": "https://registry.npmjs.org/word-wrap/-/word-wrap-1.2.3.tgz", + "integrity": "sha512-Hz/mrNwitNRh/HUAtM/VT/5VH+ygD6DV7mYKZAtHOrbs8U7lvPS6xf7EJKMF0uW1KJCl0H701g3ZGus+muE5vQ==", + "dev": true, + "engines": { + "node": ">=0.10.0" + } + }, "node_modules/wordwrap": { "version": "1.0.0", "resolved": "https://registry.npmjs.org/wordwrap/-/wordwrap-1.0.0.tgz", @@ -5510,9 +6336,9 @@ } }, "node_modules/yargs": { - "version": "16.2.0", - "resolved": "https://registry.npmjs.org/yargs/-/yargs-16.2.0.tgz", - "integrity": "sha512-D1mvvtDG0L5ft/jGWkLpG1+m0eQxOfaBvTNELraWj22wSVUMWxZUvYgJYcKh6jGGIkJFhH4IZPQhR4TKpc8mBw==", + "version": "17.2.1", + "resolved": "https://registry.npmjs.org/yargs/-/yargs-17.2.1.tgz", + "integrity": "sha512-XfR8du6ua4K6uLGm5S6fA+FIJom/MdJcFNVY8geLlp2v8GYbOXD4EB1tPNZsRn4vBzKGMgb5DRZMeWuFc2GO8Q==", "dev": true, "dependencies": { "cliui": "^7.0.2", @@ -5524,13 +6350,13 @@ "yargs-parser": "^20.2.2" }, "engines": { - "node": ">=10" + "node": ">=12" } }, "node_modules/yargs-parser": { - "version": "20.2.4", - "resolved": "https://registry.npmjs.org/yargs-parser/-/yargs-parser-20.2.4.tgz", - "integrity": "sha512-WOkpgNhPTlE73h4VFAFsOnomJVaovO8VqLDzy5saChRBFQFBoMYirowyW+Q9HB4HFF4Z7VZTiG3iSzJJA29yRA==", + "version": "20.2.9", + "resolved": "https://registry.npmjs.org/yargs-parser/-/yargs-parser-20.2.9.tgz", + "integrity": "sha512-y11nGElTIV+CT3Zv9t7VKl+Q3hTQoT9a1Qzezhhl6Rp21gJ/IVTW7Z3y9EWXhuUBC2Shnf+DX0antecpAwSP8w==", "dev": true, "engines": { "node": ">=10" @@ -5551,18 +6377,6 @@ "node": ">=10" } }, - "node_modules/yargs-unparser/node_modules/camelcase": { - "version": "6.2.0", - "resolved": "https://registry.npmjs.org/camelcase/-/camelcase-6.2.0.tgz", - "integrity": "sha512-c7wVvbw3f37nuobQNtgsgG9POC9qMbNuMQmTCqZv23b6MIz0fcYpBiOlv9gEN/hdLdnZTDQhg6e9Dq5M1vKvfg==", - "dev": true, - "engines": { - "node": ">=10" - }, - "funding": { - "url": "https://github.com/sponsors/sindresorhus" - } - }, "node_modules/yargs-unparser/node_modules/decamelize": { "version": "4.0.0", "resolved": "https://registry.npmjs.org/decamelize/-/decamelize-4.0.0.tgz", @@ -5595,170 +6409,275 @@ "funding": { "url": "https://github.com/sponsors/sindresorhus" } + }, + "packages/client": { + "name": "@node-redis/client", + "version": "1.0.0-rc", + "license": "MIT", + "dependencies": { + "cluster-key-slot": "1.1.0", + "generic-pool": "3.8.2", + "redis-parser": "3.0.0", + "yallist": "4.0.0" + }, + "devDependencies": { + "@istanbuljs/nyc-config-typescript": "^1.0.1", + "@node-redis/test-utils": "*", + "@types/node": "^16.11.7", + "@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", + "nyc": "^15.1.0", + "release-it": "^14.11.7", + "sinon": "^12.0.1", + "source-map-support": "^0.5.20", + "ts-node": "^10.4.0", + "typedoc": "^0.22.9", + "typedoc-github-wiki-theme": "^0.6.0", + "typedoc-plugin-markdown": "^3.11.6", + "typescript": "^4.4.4" + }, + "engines": { + "node": ">=12" + } + }, + "packages/json": { + "name": "@node-redis/json", + "version": "1.0.0-rc.0", + "license": "MIT", + "devDependencies": { + "@istanbuljs/nyc-config-typescript": "^1.0.1", + "@node-redis/test-utils": "*", + "@types/node": "^16.11.7", + "nyc": "^15.1.0", + "release-it": "^14.11.7", + "source-map-support": "^0.5.20", + "ts-node": "^10.4.0", + "typescript": "^4.4.4" + }, + "peerDependencies": { + "@node-redis/client": "^1.0.0-rc" + } + }, + "packages/search": { + "name": "@node-redis/search", + "version": "1.0.0-rc.0", + "license": "MIT", + "devDependencies": { + "@istanbuljs/nyc-config-typescript": "^1.0.1", + "@node-redis/test-utils": "*", + "@types/node": "^16.11.7", + "nyc": "^15.1.0", + "release-it": "^14.11.7", + "source-map-support": "^0.5.20", + "ts-node": "^10.4.0", + "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" + } } }, "dependencies": { "@babel/code-frame": { - "version": "7.15.8", - "resolved": "https://registry.npmjs.org/@babel/code-frame/-/code-frame-7.15.8.tgz", - "integrity": "sha512-2IAnmn8zbvC/jKYhq5Ki9I+DwjlrtMPUCH/CpHvqI4dNnlwHwsxoIhlc8WcYY5LSYknXQtAlFYuHfqAFCvQ4Wg==", + "version": "7.16.0", + "resolved": "https://registry.npmjs.org/@babel/code-frame/-/code-frame-7.16.0.tgz", + "integrity": "sha512-IF4EOMEV+bfYwOmNxGzSnjR2EmQod7f1UXOpZM3l4i4o4QNwzjtJAu/HxdjHq0aYBvdqMuQEY1eg0nqW9ZPORA==", "dev": true, "requires": { - "@babel/highlight": "^7.14.5" + "@babel/highlight": "^7.16.0" } }, "@babel/compat-data": { - "version": "7.15.0", - "resolved": "https://registry.npmjs.org/@babel/compat-data/-/compat-data-7.15.0.tgz", - "integrity": "sha512-0NqAC1IJE0S0+lL1SWFMxMkz1pKCNCjI4tr2Zx4LJSXxCLAdr6KyArnY+sno5m3yH9g737ygOyPABDsnXkpxiA==", + "version": "7.16.0", + "resolved": "https://registry.npmjs.org/@babel/compat-data/-/compat-data-7.16.0.tgz", + "integrity": "sha512-DGjt2QZse5SGd9nfOSqO4WLJ8NN/oHkijbXbPrxuoJO3oIPJL3TciZs9FX+cOHNiY9E9l0opL8g7BmLe3T+9ew==", "dev": true }, "@babel/core": { - "version": "7.15.8", - "resolved": "https://registry.npmjs.org/@babel/core/-/core-7.15.8.tgz", - "integrity": "sha512-3UG9dsxvYBMYwRv+gS41WKHno4K60/9GPy1CJaH6xy3Elq8CTtvtjT5R5jmNhXfCYLX2mTw+7/aq5ak/gOE0og==", - "dev": true, - "requires": { - "@babel/code-frame": "^7.15.8", - "@babel/generator": "^7.15.8", - "@babel/helper-compilation-targets": "^7.15.4", - "@babel/helper-module-transforms": "^7.15.8", - "@babel/helpers": "^7.15.4", - "@babel/parser": "^7.15.8", - "@babel/template": "^7.15.4", - "@babel/traverse": "^7.15.4", - "@babel/types": "^7.15.6", + "version": "7.16.0", + "resolved": "https://registry.npmjs.org/@babel/core/-/core-7.16.0.tgz", + "integrity": "sha512-mYZEvshBRHGsIAiyH5PzCFTCfbWfoYbO/jcSdXQSUQu1/pW0xDZAUP7KEc32heqWTAfAHhV9j1vH8Sav7l+JNQ==", + "dev": true, + "requires": { + "@babel/code-frame": "^7.16.0", + "@babel/generator": "^7.16.0", + "@babel/helper-compilation-targets": "^7.16.0", + "@babel/helper-module-transforms": "^7.16.0", + "@babel/helpers": "^7.16.0", + "@babel/parser": "^7.16.0", + "@babel/template": "^7.16.0", + "@babel/traverse": "^7.16.0", + "@babel/types": "^7.16.0", "convert-source-map": "^1.7.0", "debug": "^4.1.0", "gensync": "^1.0.0-beta.2", "json5": "^2.1.2", "semver": "^6.3.0", "source-map": "^0.5.0" + }, + "dependencies": { + "semver": { + "version": "6.3.0", + "resolved": "https://registry.npmjs.org/semver/-/semver-6.3.0.tgz", + "integrity": "sha512-b39TBaTSfV6yBrapU89p5fKekE2m/NwnDocOVruQFS1/veMgdzuPcnOM34M6CwxW8jH/lxEa5rBoDeUwu5HHTw==", + "dev": true + } } }, "@babel/generator": { - "version": "7.15.8", - "resolved": "https://registry.npmjs.org/@babel/generator/-/generator-7.15.8.tgz", - "integrity": "sha512-ECmAKstXbp1cvpTTZciZCgfOt6iN64lR0d+euv3UZisU5awfRawOvg07Utn/qBGuH4bRIEZKrA/4LzZyXhZr8g==", + "version": "7.16.0", + "resolved": "https://registry.npmjs.org/@babel/generator/-/generator-7.16.0.tgz", + "integrity": "sha512-RR8hUCfRQn9j9RPKEVXo9LiwoxLPYn6hNZlvUOR8tSnaxlD0p0+la00ZP9/SnRt6HchKr+X0fO2r8vrETiJGew==", "dev": true, "requires": { - "@babel/types": "^7.15.6", + "@babel/types": "^7.16.0", "jsesc": "^2.5.1", "source-map": "^0.5.0" } }, "@babel/helper-compilation-targets": { - "version": "7.15.4", - "resolved": "https://registry.npmjs.org/@babel/helper-compilation-targets/-/helper-compilation-targets-7.15.4.tgz", - "integrity": "sha512-rMWPCirulnPSe4d+gwdWXLfAXTTBj8M3guAf5xFQJ0nvFY7tfNAFnWdqaHegHlgDZOCT4qvhF3BYlSJag8yhqQ==", + "version": "7.16.3", + "resolved": "https://registry.npmjs.org/@babel/helper-compilation-targets/-/helper-compilation-targets-7.16.3.tgz", + "integrity": "sha512-vKsoSQAyBmxS35JUOOt+07cLc6Nk/2ljLIHwmq2/NM6hdioUaqEXq/S+nXvbvXbZkNDlWOymPanJGOc4CBjSJA==", "dev": true, "requires": { - "@babel/compat-data": "^7.15.0", + "@babel/compat-data": "^7.16.0", "@babel/helper-validator-option": "^7.14.5", - "browserslist": "^4.16.6", + "browserslist": "^4.17.5", "semver": "^6.3.0" + }, + "dependencies": { + "semver": { + "version": "6.3.0", + "resolved": "https://registry.npmjs.org/semver/-/semver-6.3.0.tgz", + "integrity": "sha512-b39TBaTSfV6yBrapU89p5fKekE2m/NwnDocOVruQFS1/veMgdzuPcnOM34M6CwxW8jH/lxEa5rBoDeUwu5HHTw==", + "dev": true + } } }, "@babel/helper-function-name": { - "version": "7.15.4", - "resolved": "https://registry.npmjs.org/@babel/helper-function-name/-/helper-function-name-7.15.4.tgz", - "integrity": "sha512-Z91cOMM4DseLIGOnog+Z8OI6YseR9bua+HpvLAQ2XayUGU+neTtX+97caALaLdyu53I/fjhbeCnWnRH1O3jFOw==", + "version": "7.16.0", + "resolved": "https://registry.npmjs.org/@babel/helper-function-name/-/helper-function-name-7.16.0.tgz", + "integrity": "sha512-BZh4mEk1xi2h4HFjWUXRQX5AEx4rvaZxHgax9gcjdLWdkjsY7MKt5p0otjsg5noXw+pB+clMCjw+aEVYADMjog==", "dev": true, "requires": { - "@babel/helper-get-function-arity": "^7.15.4", - "@babel/template": "^7.15.4", - "@babel/types": "^7.15.4" + "@babel/helper-get-function-arity": "^7.16.0", + "@babel/template": "^7.16.0", + "@babel/types": "^7.16.0" } }, "@babel/helper-get-function-arity": { - "version": "7.15.4", - "resolved": "https://registry.npmjs.org/@babel/helper-get-function-arity/-/helper-get-function-arity-7.15.4.tgz", - "integrity": "sha512-1/AlxSF92CmGZzHnC515hm4SirTxtpDnLEJ0UyEMgTMZN+6bxXKg04dKhiRx5Enel+SUA1G1t5Ed/yQia0efrA==", + "version": "7.16.0", + "resolved": "https://registry.npmjs.org/@babel/helper-get-function-arity/-/helper-get-function-arity-7.16.0.tgz", + "integrity": "sha512-ASCquNcywC1NkYh/z7Cgp3w31YW8aojjYIlNg4VeJiHkqyP4AzIvr4qx7pYDb4/s8YcsZWqqOSxgkvjUz1kpDQ==", "dev": true, "requires": { - "@babel/types": "^7.15.4" + "@babel/types": "^7.16.0" } }, "@babel/helper-hoist-variables": { - "version": "7.15.4", - "resolved": "https://registry.npmjs.org/@babel/helper-hoist-variables/-/helper-hoist-variables-7.15.4.tgz", - "integrity": "sha512-VTy085egb3jUGVK9ycIxQiPbquesq0HUQ+tPO0uv5mPEBZipk+5FkRKiWq5apuyTE9FUrjENB0rCf8y+n+UuhA==", + "version": "7.16.0", + "resolved": "https://registry.npmjs.org/@babel/helper-hoist-variables/-/helper-hoist-variables-7.16.0.tgz", + "integrity": "sha512-1AZlpazjUR0EQZQv3sgRNfM9mEVWPK3M6vlalczA+EECcPz3XPh6VplbErL5UoMpChhSck5wAJHthlj1bYpcmg==", "dev": true, "requires": { - "@babel/types": "^7.15.4" + "@babel/types": "^7.16.0" } }, "@babel/helper-member-expression-to-functions": { - "version": "7.15.4", - "resolved": "https://registry.npmjs.org/@babel/helper-member-expression-to-functions/-/helper-member-expression-to-functions-7.15.4.tgz", - "integrity": "sha512-cokOMkxC/BTyNP1AlY25HuBWM32iCEsLPI4BHDpJCHHm1FU2E7dKWWIXJgQgSFiu4lp8q3bL1BIKwqkSUviqtA==", + "version": "7.16.0", + "resolved": "https://registry.npmjs.org/@babel/helper-member-expression-to-functions/-/helper-member-expression-to-functions-7.16.0.tgz", + "integrity": "sha512-bsjlBFPuWT6IWhl28EdrQ+gTvSvj5tqVP5Xeftp07SEuz5pLnsXZuDkDD3Rfcxy0IsHmbZ+7B2/9SHzxO0T+sQ==", "dev": true, "requires": { - "@babel/types": "^7.15.4" + "@babel/types": "^7.16.0" } }, "@babel/helper-module-imports": { - "version": "7.15.4", - "resolved": "https://registry.npmjs.org/@babel/helper-module-imports/-/helper-module-imports-7.15.4.tgz", - "integrity": "sha512-jeAHZbzUwdW/xHgHQ3QmWR4Jg6j15q4w/gCfwZvtqOxoo5DKtLHk8Bsf4c5RZRC7NmLEs+ohkdq8jFefuvIxAA==", + "version": "7.16.0", + "resolved": "https://registry.npmjs.org/@babel/helper-module-imports/-/helper-module-imports-7.16.0.tgz", + "integrity": "sha512-kkH7sWzKPq0xt3H1n+ghb4xEMP8k0U7XV3kkB+ZGy69kDk2ySFW1qPi06sjKzFY3t1j6XbJSqr4mF9L7CYVyhg==", "dev": true, "requires": { - "@babel/types": "^7.15.4" + "@babel/types": "^7.16.0" } }, "@babel/helper-module-transforms": { - "version": "7.15.8", - "resolved": "https://registry.npmjs.org/@babel/helper-module-transforms/-/helper-module-transforms-7.15.8.tgz", - "integrity": "sha512-DfAfA6PfpG8t4S6npwzLvTUpp0sS7JrcuaMiy1Y5645laRJIp/LiLGIBbQKaXSInK8tiGNI7FL7L8UvB8gdUZg==", + "version": "7.16.0", + "resolved": "https://registry.npmjs.org/@babel/helper-module-transforms/-/helper-module-transforms-7.16.0.tgz", + "integrity": "sha512-My4cr9ATcaBbmaEa8M0dZNA74cfI6gitvUAskgDtAFmAqyFKDSHQo5YstxPbN+lzHl2D9l/YOEFqb2mtUh4gfA==", "dev": true, "requires": { - "@babel/helper-module-imports": "^7.15.4", - "@babel/helper-replace-supers": "^7.15.4", - "@babel/helper-simple-access": "^7.15.4", - "@babel/helper-split-export-declaration": "^7.15.4", + "@babel/helper-module-imports": "^7.16.0", + "@babel/helper-replace-supers": "^7.16.0", + "@babel/helper-simple-access": "^7.16.0", + "@babel/helper-split-export-declaration": "^7.16.0", "@babel/helper-validator-identifier": "^7.15.7", - "@babel/template": "^7.15.4", - "@babel/traverse": "^7.15.4", - "@babel/types": "^7.15.6" + "@babel/template": "^7.16.0", + "@babel/traverse": "^7.16.0", + "@babel/types": "^7.16.0" } }, "@babel/helper-optimise-call-expression": { - "version": "7.15.4", - "resolved": "https://registry.npmjs.org/@babel/helper-optimise-call-expression/-/helper-optimise-call-expression-7.15.4.tgz", - "integrity": "sha512-E/z9rfbAOt1vDW1DR7k4SzhzotVV5+qMciWV6LaG1g4jeFrkDlJedjtV4h0i4Q/ITnUu+Pk08M7fczsB9GXBDw==", + "version": "7.16.0", + "resolved": "https://registry.npmjs.org/@babel/helper-optimise-call-expression/-/helper-optimise-call-expression-7.16.0.tgz", + "integrity": "sha512-SuI467Gi2V8fkofm2JPnZzB/SUuXoJA5zXe/xzyPP2M04686RzFKFHPK6HDVN6JvWBIEW8tt9hPR7fXdn2Lgpw==", "dev": true, "requires": { - "@babel/types": "^7.15.4" + "@babel/types": "^7.16.0" } }, "@babel/helper-replace-supers": { - "version": "7.15.4", - "resolved": "https://registry.npmjs.org/@babel/helper-replace-supers/-/helper-replace-supers-7.15.4.tgz", - "integrity": "sha512-/ztT6khaXF37MS47fufrKvIsiQkx1LBRvSJNzRqmbyeZnTwU9qBxXYLaaT/6KaxfKhjs2Wy8kG8ZdsFUuWBjzw==", + "version": "7.16.0", + "resolved": "https://registry.npmjs.org/@babel/helper-replace-supers/-/helper-replace-supers-7.16.0.tgz", + "integrity": "sha512-TQxuQfSCdoha7cpRNJvfaYxxxzmbxXw/+6cS7V02eeDYyhxderSoMVALvwupA54/pZcOTtVeJ0xccp1nGWladA==", "dev": true, "requires": { - "@babel/helper-member-expression-to-functions": "^7.15.4", - "@babel/helper-optimise-call-expression": "^7.15.4", - "@babel/traverse": "^7.15.4", - "@babel/types": "^7.15.4" + "@babel/helper-member-expression-to-functions": "^7.16.0", + "@babel/helper-optimise-call-expression": "^7.16.0", + "@babel/traverse": "^7.16.0", + "@babel/types": "^7.16.0" } }, "@babel/helper-simple-access": { - "version": "7.15.4", - "resolved": "https://registry.npmjs.org/@babel/helper-simple-access/-/helper-simple-access-7.15.4.tgz", - "integrity": "sha512-UzazrDoIVOZZcTeHHEPYrr1MvTR/K+wgLg6MY6e1CJyaRhbibftF6fR2KU2sFRtI/nERUZR9fBd6aKgBlIBaPg==", + "version": "7.16.0", + "resolved": "https://registry.npmjs.org/@babel/helper-simple-access/-/helper-simple-access-7.16.0.tgz", + "integrity": "sha512-o1rjBT/gppAqKsYfUdfHq5Rk03lMQrkPHG1OWzHWpLgVXRH4HnMM9Et9CVdIqwkCQlobnGHEJMsgWP/jE1zUiw==", "dev": true, "requires": { - "@babel/types": "^7.15.4" + "@babel/types": "^7.16.0" } }, "@babel/helper-split-export-declaration": { - "version": "7.15.4", - "resolved": "https://registry.npmjs.org/@babel/helper-split-export-declaration/-/helper-split-export-declaration-7.15.4.tgz", - "integrity": "sha512-HsFqhLDZ08DxCpBdEVtKmywj6PQbwnF6HHybur0MAnkAKnlS6uHkwnmRIkElB2Owpfb4xL4NwDmDLFubueDXsw==", + "version": "7.16.0", + "resolved": "https://registry.npmjs.org/@babel/helper-split-export-declaration/-/helper-split-export-declaration-7.16.0.tgz", + "integrity": "sha512-0YMMRpuDFNGTHNRiiqJX19GjNXA4H0E8jZ2ibccfSxaCogbm3am5WN/2nQNj0YnQwGWM1J06GOcQ2qnh3+0paw==", "dev": true, "requires": { - "@babel/types": "^7.15.4" + "@babel/types": "^7.16.0" } }, "@babel/helper-validator-identifier": { @@ -5774,23 +6693,23 @@ "dev": true }, "@babel/helpers": { - "version": "7.15.4", - "resolved": "https://registry.npmjs.org/@babel/helpers/-/helpers-7.15.4.tgz", - "integrity": "sha512-V45u6dqEJ3w2rlryYYXf6i9rQ5YMNu4FLS6ngs8ikblhu2VdR1AqAd6aJjBzmf2Qzh6KOLqKHxEN9+TFbAkAVQ==", + "version": "7.16.3", + "resolved": "https://registry.npmjs.org/@babel/helpers/-/helpers-7.16.3.tgz", + "integrity": "sha512-Xn8IhDlBPhvYTvgewPKawhADichOsbkZuzN7qz2BusOM0brChsyXMDJvldWaYMMUNiCQdQzNEioXTp3sC8Nt8w==", "dev": true, "requires": { - "@babel/template": "^7.15.4", - "@babel/traverse": "^7.15.4", - "@babel/types": "^7.15.4" + "@babel/template": "^7.16.0", + "@babel/traverse": "^7.16.3", + "@babel/types": "^7.16.0" } }, "@babel/highlight": { - "version": "7.14.5", - "resolved": "https://registry.npmjs.org/@babel/highlight/-/highlight-7.14.5.tgz", - "integrity": "sha512-qf9u2WFWVV0MppaL877j2dBtQIDgmidgjGk5VIMw3OadXvYaXn66U1BFlH2t4+t3i+8PhedppRv+i40ABzd+gg==", + "version": "7.16.0", + "resolved": "https://registry.npmjs.org/@babel/highlight/-/highlight-7.16.0.tgz", + "integrity": "sha512-t8MH41kUQylBtu2+4IQA3atqevA2lRgqA2wyVB/YiWmsDSuylZZuXOUy9ric30hfzauEFfdsuk/eXTRrGrfd0g==", "dev": true, "requires": { - "@babel/helper-validator-identifier": "^7.14.5", + "@babel/helper-validator-identifier": "^7.15.7", "chalk": "^2.0.0", "js-tokens": "^4.0.0" }, @@ -5830,12 +6749,6 @@ "integrity": "sha1-p9BVi9icQveV3UIyj3QIMcpTvCU=", "dev": true }, - "escape-string-regexp": { - "version": "1.0.5", - "resolved": "https://registry.npmjs.org/escape-string-regexp/-/escape-string-regexp-1.0.5.tgz", - "integrity": "sha1-G2HAViGQqN/2rjuyzwIAyhMLhtQ=", - "dev": true - }, "has-flag": { "version": "3.0.0", "resolved": "https://registry.npmjs.org/has-flag/-/has-flag-3.0.0.tgz", @@ -5854,46 +6767,54 @@ } }, "@babel/parser": { - "version": "7.15.8", - "resolved": "https://registry.npmjs.org/@babel/parser/-/parser-7.15.8.tgz", - "integrity": "sha512-BRYa3wcQnjS/nqI8Ac94pYYpJfojHVvVXJ97+IDCImX4Jc8W8Xv1+47enbruk+q1etOpsQNwnfFcNGw+gtPGxA==", + "version": "7.16.3", + "resolved": "https://registry.npmjs.org/@babel/parser/-/parser-7.16.3.tgz", + "integrity": "sha512-dcNwU1O4sx57ClvLBVFbEgx0UZWfd0JQX5X6fxFRCLHelFBGXFfSz6Y0FAq2PEwUqlqLkdVjVr4VASEOuUnLJw==", "dev": true }, "@babel/template": { - "version": "7.15.4", - "resolved": "https://registry.npmjs.org/@babel/template/-/template-7.15.4.tgz", - "integrity": "sha512-UgBAfEa1oGuYgDIPM2G+aHa4Nlo9Lh6mGD2bDBGMTbYnc38vulXPuC1MGjYILIEmlwl6Rd+BPR9ee3gm20CBtg==", + "version": "7.16.0", + "resolved": "https://registry.npmjs.org/@babel/template/-/template-7.16.0.tgz", + "integrity": "sha512-MnZdpFD/ZdYhXwiunMqqgyZyucaYsbL0IrjoGjaVhGilz+x8YB++kRfygSOIj1yOtWKPlx7NBp+9I1RQSgsd5A==", "dev": true, "requires": { - "@babel/code-frame": "^7.14.5", - "@babel/parser": "^7.15.4", - "@babel/types": "^7.15.4" + "@babel/code-frame": "^7.16.0", + "@babel/parser": "^7.16.0", + "@babel/types": "^7.16.0" } }, "@babel/traverse": { - "version": "7.15.4", - "resolved": "https://registry.npmjs.org/@babel/traverse/-/traverse-7.15.4.tgz", - "integrity": "sha512-W6lQD8l4rUbQR/vYgSuCAE75ADyyQvOpFVsvPPdkhf6lATXAsQIG9YdtOcu8BB1dZ0LKu+Zo3c1wEcbKeuhdlA==", - "dev": true, - "requires": { - "@babel/code-frame": "^7.14.5", - "@babel/generator": "^7.15.4", - "@babel/helper-function-name": "^7.15.4", - "@babel/helper-hoist-variables": "^7.15.4", - "@babel/helper-split-export-declaration": "^7.15.4", - "@babel/parser": "^7.15.4", - "@babel/types": "^7.15.4", + "version": "7.16.3", + "resolved": "https://registry.npmjs.org/@babel/traverse/-/traverse-7.16.3.tgz", + "integrity": "sha512-eolumr1vVMjqevCpwVO99yN/LoGL0EyHiLO5I043aYQvwOJ9eR5UsZSClHVCzfhBduMAsSzgA/6AyqPjNayJag==", + "dev": true, + "requires": { + "@babel/code-frame": "^7.16.0", + "@babel/generator": "^7.16.0", + "@babel/helper-function-name": "^7.16.0", + "@babel/helper-hoist-variables": "^7.16.0", + "@babel/helper-split-export-declaration": "^7.16.0", + "@babel/parser": "^7.16.3", + "@babel/types": "^7.16.0", "debug": "^4.1.0", "globals": "^11.1.0" + }, + "dependencies": { + "globals": { + "version": "11.12.0", + "resolved": "https://registry.npmjs.org/globals/-/globals-11.12.0.tgz", + "integrity": "sha512-WOBp/EEGUiIsJSp7wcv/y6MO+lV9UoncWqxuFfm8eBwzWNgyfBd6Gz+IeKQ9jCmyhoH99g15M3T+QaVHFjizVA==", + "dev": true + } } }, "@babel/types": { - "version": "7.15.6", - "resolved": "https://registry.npmjs.org/@babel/types/-/types-7.15.6.tgz", - "integrity": "sha512-BPU+7QhqNjmWyDO0/vitH/CuhpV8ZmK1wpKva8nuyNF5MJfuRNWMc+hc14+u9xT93kvykMdncrJT19h74uB1Ig==", + "version": "7.16.0", + "resolved": "https://registry.npmjs.org/@babel/types/-/types-7.16.0.tgz", + "integrity": "sha512-PJgg/k3SdLsGb3hhisFvtLOw5ts113klrpLuIPtCJIU+BB24fqq6lf8RWqKJEjzqXR9AEH1rIb5XTqwBHB+kQg==", "dev": true, "requires": { - "@babel/helper-validator-identifier": "^7.14.9", + "@babel/helper-validator-identifier": "^7.15.7", "to-fast-properties": "^2.0.0" } }, @@ -5912,6 +6833,54 @@ "@cspotcode/source-map-consumer": "0.8.0" } }, + "@eslint/eslintrc": { + "version": "1.0.4", + "resolved": "https://registry.npmjs.org/@eslint/eslintrc/-/eslintrc-1.0.4.tgz", + "integrity": "sha512-h8Vx6MdxwWI2WM8/zREHMoqdgLNXEL4QX3MWSVMdyNJGvXVOs+6lp+m2hc3FnuMHDc4poxFNI20vCk0OmI4G0Q==", + "dev": true, + "requires": { + "ajv": "^6.12.4", + "debug": "^4.3.2", + "espree": "^9.0.0", + "globals": "^13.9.0", + "ignore": "^4.0.6", + "import-fresh": "^3.2.1", + "js-yaml": "^4.1.0", + "minimatch": "^3.0.4", + "strip-json-comments": "^3.1.1" + }, + "dependencies": { + "ignore": { + "version": "4.0.6", + "resolved": "https://registry.npmjs.org/ignore/-/ignore-4.0.6.tgz", + "integrity": "sha512-cyFDKrqc/YdcWFniJhzI42+AzS+gNwmUzOSFcRCQYwySuBBBy/KjuxWLZ/FHEH6Moq1NizMOBWyTcv8O4OZIMg==", + "dev": true + }, + "strip-json-comments": { + "version": "3.1.1", + "resolved": "https://registry.npmjs.org/strip-json-comments/-/strip-json-comments-3.1.1.tgz", + "integrity": "sha512-6fPc+R4ihwqP6N/aIv2f1gMH8lOVtWQHoqC4yK6oSDVVocumAsfCqjkXnqiYMhmMwS/mEHLp7Vehlt3ql6lEig==", + "dev": true + } + } + }, + "@humanwhocodes/config-array": { + "version": "0.6.0", + "resolved": "https://registry.npmjs.org/@humanwhocodes/config-array/-/config-array-0.6.0.tgz", + "integrity": "sha512-JQlEKbcgEUjBFhLIF4iqM7u/9lwgHRBcpHrmUNCALK0Q3amXN6lxdoXLnF0sm11E9VqTmBALR87IlUg1bZ8A9A==", + "dev": true, + "requires": { + "@humanwhocodes/object-schema": "^1.2.0", + "debug": "^4.1.1", + "minimatch": "^3.0.4" + } + }, + "@humanwhocodes/object-schema": { + "version": "1.2.1", + "resolved": "https://registry.npmjs.org/@humanwhocodes/object-schema/-/object-schema-1.2.1.tgz", + "integrity": "sha512-ZnQMnLV4e7hDlUvw8H+U8ASL02SS2Gn6+9Ac3wGGLIe7+je2AeAOxPY+izIPJDfFDb7eDjev0Us8MO1iFRN8hA==", + "dev": true + }, "@iarna/toml": { "version": "2.2.5", "resolved": "https://registry.npmjs.org/@iarna/toml/-/toml-2.2.5.tgz", @@ -5940,6 +6909,12 @@ "sprintf-js": "~1.0.2" } }, + "camelcase": { + "version": "5.3.1", + "resolved": "https://registry.npmjs.org/camelcase/-/camelcase-5.3.1.tgz", + "integrity": "sha512-L28STB170nwWS63UjtlEOE3dldQApaJXZkOI1uMFfzf3rRuPegHaHesyee+YxQ+W6SvRDQV6UrdOdRiR153wJg==", + "dev": true + }, "find-up": { "version": "4.1.0", "resolved": "https://registry.npmjs.org/find-up/-/find-up-4.1.0.tgz", @@ -5986,6 +6961,12 @@ "requires": { "p-limit": "^2.2.0" } + }, + "resolve-from": { + "version": "5.0.0", + "resolved": "https://registry.npmjs.org/resolve-from/-/resolve-from-5.0.0.tgz", + "integrity": "sha512-qYg9KP24dD5qka9J47d0aVky0N+b4fTU89LN9iDnjB5waksiC49rvMB0PrUJQGoTmH50XPiqOvAjDfaijGxYZw==", + "dev": true } } }, @@ -6004,6 +6985,75 @@ "integrity": "sha512-ZXRY4jNvVgSVQ8DL3LTcakaAtXwTVUxE81hslsyD2AtoXW/wVob10HkOJ1X/pAlcI7D+2YoZKg5do8G/w6RYgA==", "dev": true }, + "@node-redis/client": { + "version": "file:packages/client", + "requires": { + "@istanbuljs/nyc-config-typescript": "^1.0.1", + "@node-redis/test-utils": "*", + "@types/node": "^16.11.7", + "@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", + "generic-pool": "3.8.2", + "nyc": "^15.1.0", + "redis-parser": "3.0.0", + "release-it": "^14.11.7", + "sinon": "^12.0.1", + "source-map-support": "^0.5.20", + "ts-node": "^10.4.0", + "typedoc": "^0.22.9", + "typedoc-github-wiki-theme": "^0.6.0", + "typedoc-plugin-markdown": "^3.11.6", + "typescript": "^4.4.4", + "yallist": "4.0.0" + } + }, + "@node-redis/json": { + "version": "file:packages/json", + "requires": { + "@istanbuljs/nyc-config-typescript": "^1.0.1", + "@node-redis/test-utils": "*", + "@types/node": "^16.11.7", + "nyc": "^15.1.0", + "release-it": "^14.11.7", + "source-map-support": "^0.5.20", + "ts-node": "^10.4.0", + "typescript": "^4.4.4" + } + }, + "@node-redis/search": { + "version": "file:packages/search", + "requires": { + "@istanbuljs/nyc-config-typescript": "^1.0.1", + "@node-redis/test-utils": "*", + "@types/node": "^16.11.7", + "nyc": "^15.1.0", + "release-it": "^14.11.7", + "source-map-support": "^0.5.20", + "ts-node": "^10.4.0", + "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", @@ -6246,6 +7296,12 @@ "integrity": "sha512-SZs7ekbP8CN0txVG2xVRH6EgKmEm31BOxA07vkFaETzZz1xh+cbt8BcI0slpymvwhx5dlFnQG2rTlPVQn+iRPQ==", "dev": true }, + "@types/json-schema": { + "version": "7.0.9", + "resolved": "https://registry.npmjs.org/@types/json-schema/-/json-schema-7.0.9.tgz", + "integrity": "sha512-qcUXuemtEu+E5wZSJHNxUXeCZhAfXKQ41D+duX+VYPde7xyEVZci+/oXKJL13tnRs9lR2pr4fod59GT6/X1/yQ==", + "dev": true + }, "@types/keyv": { "version": "3.1.3", "resolved": "https://registry.npmjs.org/@types/keyv/-/keyv-3.1.3.tgz", @@ -6262,9 +7318,9 @@ "dev": true }, "@types/node": { - "version": "16.10.3", - "resolved": "https://registry.npmjs.org/@types/node/-/node-16.10.3.tgz", - "integrity": "sha512-ho3Ruq+fFnBrZhUYI46n/bV2GjwzSkwuT4dTf0GkuNFmnb8nq4ny2z9JEVemFi6bdEJanHLlYfy9c6FN9B9McQ==", + "version": "16.11.7", + "resolved": "https://registry.npmjs.org/@types/node/-/node-16.11.7.tgz", + "integrity": "sha512-QB5D2sqfSjCmTuWcBWyJ+/44bcjO7VbjSbOE0ucoVbAsSNQc4Lt6QkgkVXkTDwkL4z/beecZNDvVX15D4P8Jbw==", "dev": true }, "@types/parse-json": { @@ -6273,36 +7329,144 @@ "integrity": "sha512-//oorEZjL6sbPcKUaCdIGlIUeH26mgzimjBB77G6XRgnDl/L5wOnpyBGRe/Mmf5CVW3PwEBE1NjiMZ/ssFh4wA==", "dev": true }, - "@types/responselike": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/@types/responselike/-/responselike-1.0.0.tgz", - "integrity": "sha512-85Y2BjiufFzaMIlvJDvTTB8Fxl2xfLo4HgmHzVBz08w4wDePCTjYw66PdrolO0kzli3yam/YCgRufyo1DdQVTA==", + "@types/redis-errors": { + "version": "1.2.1", + "resolved": "https://registry.npmjs.org/@types/redis-errors/-/redis-errors-1.2.1.tgz", + "integrity": "sha512-9p6SQPeDAIR4z3ZdrPsRH/sSRMeIA2fdRKqZ3Y1thQOLeDH4aLY+J4Ze32zjg4Dq7655Y0LonCoRrH5O7vtr4w==", + "dev": true + }, + "@types/redis-parser": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/@types/redis-parser/-/redis-parser-3.0.0.tgz", + "integrity": "sha512-TH9NnE980dXWKjFRkck6FIv9akrD6G+vX9XXONoBgG9+NfP4iZ+SbGkaN7S15c6+JeZ+zBN62bHt2lNyUYwqoA==", + "dev": true, + "requires": { + "@types/node": "*", + "@types/redis-errors": "*" + } + }, + "@types/responselike": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/@types/responselike/-/responselike-1.0.0.tgz", + "integrity": "sha512-85Y2BjiufFzaMIlvJDvTTB8Fxl2xfLo4HgmHzVBz08w4wDePCTjYw66PdrolO0kzli3yam/YCgRufyo1DdQVTA==", + "dev": true, + "requires": { + "@types/node": "*" + } + }, + "@types/sinon": { + "version": "10.0.6", + "resolved": "https://registry.npmjs.org/@types/sinon/-/sinon-10.0.6.tgz", + "integrity": "sha512-6EF+wzMWvBNeGrfP3Nx60hhx+FfwSg1JJBLAAP/IdIUq0EYkqCYf70VT3PhuhPX9eLD+Dp+lNdpb/ZeHG8Yezg==", + "dev": true, + "requires": { + "@sinonjs/fake-timers": "^7.1.0" + } + }, + "@types/yallist": { + "version": "4.0.1", + "resolved": "https://registry.npmjs.org/@types/yallist/-/yallist-4.0.1.tgz", + "integrity": "sha512-G3FNJfaYtN8URU6wd6+uwFI62KO79j7n3XTYcwcFncP8gkfoi0b821GoVVt0oqKVnCqKYOMNKIGpakPoFhzAGA==", + "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==", + "dev": true, + "requires": { + "@types/yargs-parser": "*" + } + }, + "@types/yargs-parser": { + "version": "20.2.1", + "resolved": "https://registry.npmjs.org/@types/yargs-parser/-/yargs-parser-20.2.1.tgz", + "integrity": "sha512-7tFImggNeNBVMsn0vLrpn1H1uPrUBdnARPTpZoitY37ZrdJREzf7I16tMrlK3hen349gr1NYh8CmZQa7CTG6Aw==", + "dev": true + }, + "@typescript-eslint/eslint-plugin": { + "version": "5.4.0", + "resolved": "https://registry.npmjs.org/@typescript-eslint/eslint-plugin/-/eslint-plugin-5.4.0.tgz", + "integrity": "sha512-9/yPSBlwzsetCsGEn9j24D8vGQgJkOTr4oMLas/w886ZtzKIs1iyoqFrwsX2fqYEeUwsdBpC21gcjRGo57u0eg==", + "dev": true, + "requires": { + "@typescript-eslint/experimental-utils": "5.4.0", + "@typescript-eslint/scope-manager": "5.4.0", + "debug": "^4.3.2", + "functional-red-black-tree": "^1.0.1", + "ignore": "^5.1.8", + "regexpp": "^3.2.0", + "semver": "^7.3.5", + "tsutils": "^3.21.0" + } + }, + "@typescript-eslint/experimental-utils": { + "version": "5.4.0", + "resolved": "https://registry.npmjs.org/@typescript-eslint/experimental-utils/-/experimental-utils-5.4.0.tgz", + "integrity": "sha512-Nz2JDIQUdmIGd6p33A+naQmwfkU5KVTLb/5lTk+tLVTDacZKoGQisj8UCxk7onJcrgjIvr8xWqkYI+DbI3TfXg==", "dev": true, "requires": { - "@types/node": "*" + "@types/json-schema": "^7.0.9", + "@typescript-eslint/scope-manager": "5.4.0", + "@typescript-eslint/types": "5.4.0", + "@typescript-eslint/typescript-estree": "5.4.0", + "eslint-scope": "^5.1.1", + "eslint-utils": "^3.0.0" } }, - "@types/sinon": { - "version": "10.0.4", - "resolved": "https://registry.npmjs.org/@types/sinon/-/sinon-10.0.4.tgz", - "integrity": "sha512-fOYjrxQv8zJsqOY6V6ecP4eZhQBxtY80X0er1VVnUIAIZo74jHm8e1vguG5Yt4Iv8W2Wr7TgibB8MfRe32k9pA==", + "@typescript-eslint/parser": { + "version": "5.4.0", + "resolved": "https://registry.npmjs.org/@typescript-eslint/parser/-/parser-5.4.0.tgz", + "integrity": "sha512-JoB41EmxiYpaEsRwpZEYAJ9XQURPFer8hpkIW9GiaspVLX8oqbqNM8P4EP8HOZg96yaALiLEVWllA2E8vwsIKw==", "dev": true, "requires": { - "@sinonjs/fake-timers": "^7.1.0" + "@typescript-eslint/scope-manager": "5.4.0", + "@typescript-eslint/types": "5.4.0", + "@typescript-eslint/typescript-estree": "5.4.0", + "debug": "^4.3.2" } }, - "@types/which": { - "version": "2.0.1", - "resolved": "https://registry.npmjs.org/@types/which/-/which-2.0.1.tgz", - "integrity": "sha512-Jjakcv8Roqtio6w1gr0D7y6twbhx6gGgFGF5BLwajPpnOIOxFkakFhCq+LmyyeAz7BX6ULrjBOxdKaCDy+4+dQ==", - "dev": true + "@typescript-eslint/scope-manager": { + "version": "5.4.0", + "resolved": "https://registry.npmjs.org/@typescript-eslint/scope-manager/-/scope-manager-5.4.0.tgz", + "integrity": "sha512-pRxFjYwoi8R+n+sibjgF9iUiAELU9ihPBtHzocyW8v8D8G8KeQvXTsW7+CBYIyTYsmhtNk50QPGLE3vrvhM5KA==", + "dev": true, + "requires": { + "@typescript-eslint/types": "5.4.0", + "@typescript-eslint/visitor-keys": "5.4.0" + } }, - "@types/yallist": { - "version": "4.0.1", - "resolved": "https://registry.npmjs.org/@types/yallist/-/yallist-4.0.1.tgz", - "integrity": "sha512-G3FNJfaYtN8URU6wd6+uwFI62KO79j7n3XTYcwcFncP8gkfoi0b821GoVVt0oqKVnCqKYOMNKIGpakPoFhzAGA==", + "@typescript-eslint/types": { + "version": "5.4.0", + "resolved": "https://registry.npmjs.org/@typescript-eslint/types/-/types-5.4.0.tgz", + "integrity": "sha512-GjXNpmn+n1LvnttarX+sPD6+S7giO+9LxDIGlRl4wK3a7qMWALOHYuVSZpPTfEIklYjaWuMtfKdeByx0AcaThA==", "dev": true }, + "@typescript-eslint/typescript-estree": { + "version": "5.4.0", + "resolved": "https://registry.npmjs.org/@typescript-eslint/typescript-estree/-/typescript-estree-5.4.0.tgz", + "integrity": "sha512-nhlNoBdhKuwiLMx6GrybPT3SFILm5Gij2YBdPEPFlYNFAXUJWX6QRgvi/lwVoadaQEFsizohs6aFRMqsXI2ewA==", + "dev": true, + "requires": { + "@typescript-eslint/types": "5.4.0", + "@typescript-eslint/visitor-keys": "5.4.0", + "debug": "^4.3.2", + "globby": "^11.0.4", + "is-glob": "^4.0.3", + "semver": "^7.3.5", + "tsutils": "^3.21.0" + } + }, + "@typescript-eslint/visitor-keys": { + "version": "5.4.0", + "resolved": "https://registry.npmjs.org/@typescript-eslint/visitor-keys/-/visitor-keys-5.4.0.tgz", + "integrity": "sha512-PVbax7MeE7tdLfW5SA0fs8NGVVr+buMPrcj+CWYWPXsZCH8qZ1THufDzbXm1xrZ2b2PA1iENJ0sRq5fuUtvsJg==", + "dev": true, + "requires": { + "@typescript-eslint/types": "5.4.0", + "eslint-visitor-keys": "^3.0.0" + } + }, "@ungap/promise-all-settled": { "version": "1.1.2", "resolved": "https://registry.npmjs.org/@ungap/promise-all-settled/-/promise-all-settled-1.1.2.tgz", @@ -6315,6 +7479,13 @@ "integrity": "sha512-yXbYeFy+jUuYd3/CDcg2NkIYE991XYX/bje7LmjJigUciaeO1JR4XxXgCIV1/Zc/dRuFEyw1L0pbA+qynJkW5Q==", "dev": true }, + "acorn-jsx": { + "version": "5.3.2", + "resolved": "https://registry.npmjs.org/acorn-jsx/-/acorn-jsx-5.3.2.tgz", + "integrity": "sha512-rq9s+JNhf0IChjtDXxllJ7g41oZk5SlXtp0LHwyA5cejwn7vKmKp4pPri6YEePv2PU65sAsegbXtIinmDFDXgQ==", + "dev": true, + "requires": {} + }, "acorn-walk": { "version": "8.2.0", "resolved": "https://registry.npmjs.org/acorn-walk/-/acorn-walk-8.2.0.tgz", @@ -6331,6 +7502,18 @@ "indent-string": "^4.0.0" } }, + "ajv": { + "version": "6.12.6", + "resolved": "https://registry.npmjs.org/ajv/-/ajv-6.12.6.tgz", + "integrity": "sha512-j3fVLgvTo527anyYyJOGTYJbG+vnnQYvE0m5mmkc1TK+nxAppkCLMIL0aZ4dblVCNoGShhm+kzE4ZUykBoMg4g==", + "dev": true, + "requires": { + "fast-deep-equal": "^3.1.1", + "fast-json-stable-stringify": "^2.0.0", + "json-schema-traverse": "^0.4.1", + "uri-js": "^4.2.2" + } + }, "ansi-align": { "version": "3.0.1", "resolved": "https://registry.npmjs.org/ansi-align/-/ansi-align-3.0.1.tgz", @@ -6353,14 +7536,6 @@ "dev": true, "requires": { "type-fest": "^0.21.3" - }, - "dependencies": { - "type-fest": { - "version": "0.21.3", - "resolved": "https://registry.npmjs.org/type-fest/-/type-fest-0.21.3.tgz", - "integrity": "sha512-t0rzBq87m3fVcduHDUFhKmyyX+9eo6WQjZvf51Ea/M0Q7+T374Jp1aUiyUl0GKxp8M/OETVHSDvmkyPgvX+X2w==", - "dev": true - } } }, "ansi-regex": { @@ -6487,12 +7662,6 @@ "wrap-ansi": "^7.0.0" }, "dependencies": { - "camelcase": { - "version": "6.2.0", - "resolved": "https://registry.npmjs.org/camelcase/-/camelcase-6.2.0.tgz", - "integrity": "sha512-c7wVvbw3f37nuobQNtgsgG9POC9qMbNuMQmTCqZv23b6MIz0fcYpBiOlv9gEN/hdLdnZTDQhg6e9Dq5M1vKvfg==", - "dev": true - }, "type-fest": { "version": "0.20.2", "resolved": "https://registry.npmjs.org/type-fest/-/type-fest-0.20.2.tgz", @@ -6527,16 +7696,16 @@ "dev": true }, "browserslist": { - "version": "4.17.3", - "resolved": "https://registry.npmjs.org/browserslist/-/browserslist-4.17.3.tgz", - "integrity": "sha512-59IqHJV5VGdcJZ+GZ2hU5n4Kv3YiASzW6Xk5g9tf5a/MAzGeFwgGWU39fVzNIOVcgB3+Gp+kiQu0HEfTVU/3VQ==", + "version": "4.18.1", + "resolved": "https://registry.npmjs.org/browserslist/-/browserslist-4.18.1.tgz", + "integrity": "sha512-8ScCzdpPwR2wQh8IT82CA2VgDwjHyqMovPBZSNH54+tm4Jk2pCuv90gmAdH6J84OCRWi0b4gMe6O6XPXuJnjgQ==", "dev": true, "requires": { - "caniuse-lite": "^1.0.30001264", - "electron-to-chromium": "^1.3.857", + "caniuse-lite": "^1.0.30001280", + "electron-to-chromium": "^1.3.896", "escalade": "^3.1.1", - "node-releases": "^1.1.77", - "picocolors": "^0.2.1" + "node-releases": "^2.0.1", + "picocolors": "^1.0.0" } }, "buffer": { @@ -6616,15 +7785,15 @@ "dev": true }, "camelcase": { - "version": "5.3.1", - "resolved": "https://registry.npmjs.org/camelcase/-/camelcase-5.3.1.tgz", - "integrity": "sha512-L28STB170nwWS63UjtlEOE3dldQApaJXZkOI1uMFfzf3rRuPegHaHesyee+YxQ+W6SvRDQV6UrdOdRiR153wJg==", + "version": "6.2.1", + "resolved": "https://registry.npmjs.org/camelcase/-/camelcase-6.2.1.tgz", + "integrity": "sha512-tVI4q5jjFV5CavAU8DXfza/TJcZutVKo/5Foskmsqcm0MsL91moHvwiGNnqaa2o6PF/7yT5ikDRcVcl8Rj6LCA==", "dev": true }, "caniuse-lite": { - "version": "1.0.30001265", - "resolved": "https://registry.npmjs.org/caniuse-lite/-/caniuse-lite-1.0.30001265.tgz", - "integrity": "sha512-YzBnspggWV5hep1m9Z6sZVLOt7vrju8xWooFAgN6BA5qvy98qPAPb7vNUzypFaoh2pb3vlfzbDO8tB57UPGbtw==", + "version": "1.0.30001280", + "resolved": "https://registry.npmjs.org/caniuse-lite/-/caniuse-lite-1.0.30001280.tgz", + "integrity": "sha512-kFXwYvHe5rix25uwueBxC569o53J6TpnGu0BEEn+6Lhl2vsnAumRFWEBhDft1fwyo6m1r4i+RqA4+163FpeFcA==", "dev": true }, "chalk": { @@ -6635,17 +7804,6 @@ "requires": { "ansi-styles": "^4.1.0", "supports-color": "^7.1.0" - }, - "dependencies": { - "supports-color": { - "version": "7.2.0", - "resolved": "https://registry.npmjs.org/supports-color/-/supports-color-7.2.0.tgz", - "integrity": "sha512-qpCAvRl9stuOHveKsn7HncJRvv501qIacKzQlO/+Lwxc9+0q2wLyv4Dfvt80/DPn2pqOBsJdDiogXGR9+OvwRw==", - "dev": true, - "requires": { - "has-flag": "^4.0.0" - } - } } }, "chardet": { @@ -6797,6 +7955,14 @@ "dev": true, "requires": { "safe-buffer": "~5.1.1" + }, + "dependencies": { + "safe-buffer": { + "version": "5.1.2", + "resolved": "https://registry.npmjs.org/safe-buffer/-/safe-buffer-5.1.2.tgz", + "integrity": "sha512-Gd2UZBJDkXlY7GbJxfsE8/nvKkUEU1G38c1siN6QP6a9PT9MmHB8GnpscSmMJSoF8LOIrt8ud/wPtojys4G6+g==", + "dev": true + } } }, "cosmiconfig": { @@ -6842,14 +8008,6 @@ "dev": true, "requires": { "ms": "2.1.2" - }, - "dependencies": { - "ms": { - "version": "2.1.2", - "resolved": "https://registry.npmjs.org/ms/-/ms-2.1.2.tgz", - "integrity": "sha512-sGkPx+VjMtmA6MX27oA4FBFELFCZZ4S4XqeGOXCv68tT+jb3vk/RyaKWP0PTKyWtmLSM0b+adUTEvbs1PEaH2w==", - "dev": true - } } }, "decamelize": { @@ -6887,6 +8045,12 @@ "integrity": "sha512-LOHxIOaPYdHlJRtCQfDIVZtfw/ufM8+rVj649RIHzcm/vGwQRXFt6OPqIFWsm2XEMrNIEtWR64sY1LEKD2vAOA==", "dev": true }, + "deep-is": { + "version": "0.1.4", + "resolved": "https://registry.npmjs.org/deep-is/-/deep-is-0.1.4.tgz", + "integrity": "sha512-oIPzksmTg4/MriiaYGO+okXDT7ztn/w3Eptv/+gSIdMdKsJo0u4CfYNFJPy+4SKMuCqGw2wxnA+URMg3t8a/bQ==", + "dev": true + }, "default-require-extensions": { "version": "3.0.0", "resolved": "https://registry.npmjs.org/default-require-extensions/-/default-require-extensions-3.0.0.tgz", @@ -6948,6 +8112,15 @@ "path-type": "^4.0.0" } }, + "doctrine": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/doctrine/-/doctrine-3.0.0.tgz", + "integrity": "sha512-yS+Q5i3hBf7GBkd4KG8a7eBNNWNGLTaEwwYWUijIYM7zrlYDM0BFXHjjPWlWZ1Rg7UaddZeIDmi9jF3HmqiQ2w==", + "dev": true, + "requires": { + "esutils": "^2.0.2" + } + }, "dot-prop": { "version": "5.3.0", "resolved": "https://registry.npmjs.org/dot-prop/-/dot-prop-5.3.0.tgz", @@ -6964,9 +8137,9 @@ "dev": true }, "electron-to-chromium": { - "version": "1.3.864", - "resolved": "https://registry.npmjs.org/electron-to-chromium/-/electron-to-chromium-1.3.864.tgz", - "integrity": "sha512-v4rbad8GO6/yVI92WOeU9Wgxc4NA0n4f6P1FvZTY+jyY7JHEhw3bduYu60v3Q1h81Cg6eo4ApZrFPuycwd5hGw==", + "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==", "dev": true }, "emoji-regex": { @@ -6984,6 +8157,15 @@ "once": "^1.4.0" } }, + "enquirer": { + "version": "2.3.6", + "resolved": "https://registry.npmjs.org/enquirer/-/enquirer-2.3.6.tgz", + "integrity": "sha512-yjNnPr315/FjS4zIsUxYguYUPP2e1NK4d7E7ZOLiyYCcbFBiTMyID+2wvm2w6+pZ/odMA7cRkjhsPbltwBOrLg==", + "dev": true, + "requires": { + "ansi-colors": "^4.1.1" + } + }, "error-ex": { "version": "1.3.2", "resolved": "https://registry.npmjs.org/error-ex/-/error-ex-1.3.2.tgz", @@ -7012,17 +8194,198 @@ "dev": true }, "escape-string-regexp": { - "version": "4.0.0", - "resolved": "https://registry.npmjs.org/escape-string-regexp/-/escape-string-regexp-4.0.0.tgz", - "integrity": "sha512-TtpcNJ3XAzx3Gq8sWRzJaVajRs0uVxA2YAkdb1jm2YkPz4G6egUFAyA3n5vtEIZefPk5Wa4UXbKuS5fKkJWdgA==", + "version": "1.0.5", + "resolved": "https://registry.npmjs.org/escape-string-regexp/-/escape-string-regexp-1.0.5.tgz", + "integrity": "sha1-G2HAViGQqN/2rjuyzwIAyhMLhtQ=", + "dev": true + }, + "eslint": { + "version": "8.2.0", + "resolved": "https://registry.npmjs.org/eslint/-/eslint-8.2.0.tgz", + "integrity": "sha512-erw7XmM+CLxTOickrimJ1SiF55jiNlVSp2qqm0NuBWPtHYQCegD5ZMaW0c3i5ytPqL+SSLaCxdvQXFPLJn+ABw==", + "dev": true, + "requires": { + "@eslint/eslintrc": "^1.0.4", + "@humanwhocodes/config-array": "^0.6.0", + "ajv": "^6.10.0", + "chalk": "^4.0.0", + "cross-spawn": "^7.0.2", + "debug": "^4.3.2", + "doctrine": "^3.0.0", + "enquirer": "^2.3.5", + "escape-string-regexp": "^4.0.0", + "eslint-scope": "^6.0.0", + "eslint-utils": "^3.0.0", + "eslint-visitor-keys": "^3.0.0", + "espree": "^9.0.0", + "esquery": "^1.4.0", + "esutils": "^2.0.2", + "fast-deep-equal": "^3.1.3", + "file-entry-cache": "^6.0.1", + "functional-red-black-tree": "^1.0.1", + "glob-parent": "^6.0.1", + "globals": "^13.6.0", + "ignore": "^4.0.6", + "import-fresh": "^3.0.0", + "imurmurhash": "^0.1.4", + "is-glob": "^4.0.0", + "js-yaml": "^4.1.0", + "json-stable-stringify-without-jsonify": "^1.0.1", + "levn": "^0.4.1", + "lodash.merge": "^4.6.2", + "minimatch": "^3.0.4", + "natural-compare": "^1.4.0", + "optionator": "^0.9.1", + "progress": "^2.0.0", + "regexpp": "^3.2.0", + "semver": "^7.2.1", + "strip-ansi": "^6.0.1", + "strip-json-comments": "^3.1.0", + "text-table": "^0.2.0", + "v8-compile-cache": "^2.0.3" + }, + "dependencies": { + "escape-string-regexp": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/escape-string-regexp/-/escape-string-regexp-4.0.0.tgz", + "integrity": "sha512-TtpcNJ3XAzx3Gq8sWRzJaVajRs0uVxA2YAkdb1jm2YkPz4G6egUFAyA3n5vtEIZefPk5Wa4UXbKuS5fKkJWdgA==", + "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==", + "dev": true, + "requires": { + "esrecurse": "^4.3.0", + "estraverse": "^5.2.0" + } + }, + "estraverse": { + "version": "5.3.0", + "resolved": "https://registry.npmjs.org/estraverse/-/estraverse-5.3.0.tgz", + "integrity": "sha512-MMdARuVEQziNTeJD8DgMqmhwR11BRQ/cBP+pLtYdSTnf3MIO8fFeiINEbX36ZdNlfU/7A9f3gUw49B3oQsvwBA==", + "dev": true + }, + "glob-parent": { + "version": "6.0.2", + "resolved": "https://registry.npmjs.org/glob-parent/-/glob-parent-6.0.2.tgz", + "integrity": "sha512-XxwI8EOhVQgWp6iDL+3b0r86f4d6AX6zSU55HfB4ydCEuXLXc5FcYeOu+nnGftS4TEju/11rt4KJPTMgbfmv4A==", + "dev": true, + "requires": { + "is-glob": "^4.0.3" + } + }, + "ignore": { + "version": "4.0.6", + "resolved": "https://registry.npmjs.org/ignore/-/ignore-4.0.6.tgz", + "integrity": "sha512-cyFDKrqc/YdcWFniJhzI42+AzS+gNwmUzOSFcRCQYwySuBBBy/KjuxWLZ/FHEH6Moq1NizMOBWyTcv8O4OZIMg==", + "dev": true + }, + "strip-json-comments": { + "version": "3.1.1", + "resolved": "https://registry.npmjs.org/strip-json-comments/-/strip-json-comments-3.1.1.tgz", + "integrity": "sha512-6fPc+R4ihwqP6N/aIv2f1gMH8lOVtWQHoqC4yK6oSDVVocumAsfCqjkXnqiYMhmMwS/mEHLp7Vehlt3ql6lEig==", + "dev": true + } + } + }, + "eslint-scope": { + "version": "5.1.1", + "resolved": "https://registry.npmjs.org/eslint-scope/-/eslint-scope-5.1.1.tgz", + "integrity": "sha512-2NxwbF/hZ0KpepYN0cNbo+FN6XoK7GaHlQhgx/hIZl6Va0bF45RQOOwhLIy8lQDbuCiadSLCBnH2CFYquit5bw==", + "dev": true, + "requires": { + "esrecurse": "^4.3.0", + "estraverse": "^4.1.1" + } + }, + "eslint-utils": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/eslint-utils/-/eslint-utils-3.0.0.tgz", + "integrity": "sha512-uuQC43IGctw68pJA1RgbQS8/NP7rch6Cwd4j3ZBtgo4/8Flj4eGE7ZYSZRN3iq5pVUv6GPdW5Z1RFleo84uLDA==", + "dev": true, + "requires": { + "eslint-visitor-keys": "^2.0.0" + }, + "dependencies": { + "eslint-visitor-keys": { + "version": "2.1.0", + "resolved": "https://registry.npmjs.org/eslint-visitor-keys/-/eslint-visitor-keys-2.1.0.tgz", + "integrity": "sha512-0rSmRBzXgDzIsD6mGdJgevzgezI534Cer5L/vyMX0kHzT/jiB43jRhd9YUlMGYLQy2zprNmoT8qasCGtY+QaKw==", + "dev": true + } + } + }, + "eslint-visitor-keys": { + "version": "3.1.0", + "resolved": "https://registry.npmjs.org/eslint-visitor-keys/-/eslint-visitor-keys-3.1.0.tgz", + "integrity": "sha512-yWJFpu4DtjsWKkt5GeNBBuZMlNcYVs6vRCLoCVEJrTjaSB6LC98gFipNK/erM2Heg/E8mIK+hXG/pJMLK+eRZA==", "dev": true }, + "espree": { + "version": "9.0.0", + "resolved": "https://registry.npmjs.org/espree/-/espree-9.0.0.tgz", + "integrity": "sha512-r5EQJcYZ2oaGbeR0jR0fFVijGOcwai07/690YRXLINuhmVeRY4UKSAsQPe/0BNuDgwP7Ophoc1PRsr2E3tkbdQ==", + "dev": true, + "requires": { + "acorn": "^8.5.0", + "acorn-jsx": "^5.3.1", + "eslint-visitor-keys": "^3.0.0" + } + }, "esprima": { "version": "4.0.1", "resolved": "https://registry.npmjs.org/esprima/-/esprima-4.0.1.tgz", "integrity": "sha512-eGuFFw7Upda+g4p+QHvnW0RyTX/SVeJBDM/gCtMARO0cLuT2HcEKnTPvhjV6aGeqrCB/sbNop0Kszm0jsaWU4A==", "dev": true }, + "esquery": { + "version": "1.4.0", + "resolved": "https://registry.npmjs.org/esquery/-/esquery-1.4.0.tgz", + "integrity": "sha512-cCDispWt5vHHtwMY2YrAQ4ibFkAL8RbH5YGBnZBc90MolvvfkkQcJro/aZiAQUlQ3qgrYS6D6v8Gc5G5CQsc9w==", + "dev": true, + "requires": { + "estraverse": "^5.1.0" + }, + "dependencies": { + "estraverse": { + "version": "5.3.0", + "resolved": "https://registry.npmjs.org/estraverse/-/estraverse-5.3.0.tgz", + "integrity": "sha512-MMdARuVEQziNTeJD8DgMqmhwR11BRQ/cBP+pLtYdSTnf3MIO8fFeiINEbX36ZdNlfU/7A9f3gUw49B3oQsvwBA==", + "dev": true + } + } + }, + "esrecurse": { + "version": "4.3.0", + "resolved": "https://registry.npmjs.org/esrecurse/-/esrecurse-4.3.0.tgz", + "integrity": "sha512-KmfKL3b6G+RXvP8N1vr3Tq1kL/oCFgn2NYXEtqP8/L3pKapUA4G8cFVaoF3SU323CD4XypR/ffioHmkti6/Tag==", + "dev": true, + "requires": { + "estraverse": "^5.2.0" + }, + "dependencies": { + "estraverse": { + "version": "5.3.0", + "resolved": "https://registry.npmjs.org/estraverse/-/estraverse-5.3.0.tgz", + "integrity": "sha512-MMdARuVEQziNTeJD8DgMqmhwR11BRQ/cBP+pLtYdSTnf3MIO8fFeiINEbX36ZdNlfU/7A9f3gUw49B3oQsvwBA==", + "dev": true + } + } + }, + "estraverse": { + "version": "4.3.0", + "resolved": "https://registry.npmjs.org/estraverse/-/estraverse-4.3.0.tgz", + "integrity": "sha512-39nnKffWz8xN1BU/2c79n9nB9HDzo0niYUqx6xyqUnyoAnQyyWpOTdZEeiCch8BBu515t4wp9ZmgVfVhn9EBpw==", + "dev": true + }, + "esutils": { + "version": "2.0.3", + "resolved": "https://registry.npmjs.org/esutils/-/esutils-2.0.3.tgz", + "integrity": "sha512-kVscqXk4OCp68SZ0dkgEKVi6/8ij300KBWTJq32P/dYeWTSwK41WyTxalN1eRmA5Z9UU/LX9D7FWSmV9SAYx6g==", + "dev": true + }, "execa": { "version": "5.1.1", "resolved": "https://registry.npmjs.org/execa/-/execa-5.1.1.tgz", @@ -7051,6 +8414,12 @@ "tmp": "^0.0.33" } }, + "fast-deep-equal": { + "version": "3.1.3", + "resolved": "https://registry.npmjs.org/fast-deep-equal/-/fast-deep-equal-3.1.3.tgz", + "integrity": "sha512-f3qQ9oQy9j2AhBe/H9VC91wLmKBCCU/gDOnKNAYG5hswO7BLKj09Hc5HYNz9cGI++xlpDCIgDaitVs03ATR84Q==", + "dev": true + }, "fast-glob": { "version": "3.2.7", "resolved": "https://registry.npmjs.org/fast-glob/-/fast-glob-3.2.7.tgz", @@ -7064,6 +8433,18 @@ "micromatch": "^4.0.4" } }, + "fast-json-stable-stringify": { + "version": "2.1.0", + "resolved": "https://registry.npmjs.org/fast-json-stable-stringify/-/fast-json-stable-stringify-2.1.0.tgz", + "integrity": "sha512-lhd/wF+Lk98HZoTCtlVraHtfh5XYijIjalXck7saUtuanSDyLMxnHhSXEDJqHxD7msR8D0uCmqlkwjCV8xvwHw==", + "dev": true + }, + "fast-levenshtein": { + "version": "2.0.6", + "resolved": "https://registry.npmjs.org/fast-levenshtein/-/fast-levenshtein-2.0.6.tgz", + "integrity": "sha1-PYpcZog6FqMMqGQ+hR8Zuqd5eRc=", + "dev": true + }, "fastq": { "version": "1.13.0", "resolved": "https://registry.npmjs.org/fastq/-/fastq-1.13.0.tgz", @@ -7080,14 +8461,15 @@ "dev": true, "requires": { "escape-string-regexp": "^1.0.5" - }, - "dependencies": { - "escape-string-regexp": { - "version": "1.0.5", - "resolved": "https://registry.npmjs.org/escape-string-regexp/-/escape-string-regexp-1.0.5.tgz", - "integrity": "sha1-G2HAViGQqN/2rjuyzwIAyhMLhtQ=", - "dev": true - } + } + }, + "file-entry-cache": { + "version": "6.0.1", + "resolved": "https://registry.npmjs.org/file-entry-cache/-/file-entry-cache-6.0.1.tgz", + "integrity": "sha512-7Gps/XWymbLk2QLYK4NzpMOrYjMhdIxXuIvy2QBsLE6ljuodKvdkWs/cpyJJ3CVIVpH0Oi1Hvg1ovbMzLdFBBg==", + "dev": true, + "requires": { + "flat-cache": "^3.0.4" } }, "fill-range": { @@ -7132,6 +8514,22 @@ "integrity": "sha512-b6suED+5/3rTpUBdG1gupIl8MPFCAMA0QXwmljLhvCUKcUvdE4gWky9zpuGCcXHOsz4J9wPGNWq6OKpmIzz3hQ==", "dev": true }, + "flat-cache": { + "version": "3.0.4", + "resolved": "https://registry.npmjs.org/flat-cache/-/flat-cache-3.0.4.tgz", + "integrity": "sha512-dm9s5Pw7Jc0GvMYbshN6zchCA9RgQlzzEZX3vylR9IqFfS8XciblUXOKfW6SiuJ0e13eDYZoZV5wdrev7P3Nwg==", + "dev": true, + "requires": { + "flatted": "^3.1.0", + "rimraf": "^3.0.2" + } + }, + "flatted": { + "version": "3.2.4", + "resolved": "https://registry.npmjs.org/flatted/-/flatted-3.2.4.tgz", + "integrity": "sha512-8/sOawo8tJ4QOBX8YlQBMxL8+RLZfxMQOif9o0KUKTNTjMYElWPE0r/m5VNFxTRd0NSw8qSy8dajrwX4RYI1Hw==", + "dev": true + }, "foreground-child": { "version": "2.0.0", "resolved": "https://registry.npmjs.org/foreground-child/-/foreground-child-2.0.0.tgz", @@ -7178,6 +8576,12 @@ "integrity": "sha512-yIovAzMX49sF8Yl58fSCWJ5svSLuaibPxXQJFLmBObTuCr0Mf1KiPopGM9NiFjiYBCbfaa2Fh6breQ6ANVTI0A==", "dev": true }, + "functional-red-black-tree": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/functional-red-black-tree/-/functional-red-black-tree-1.0.1.tgz", + "integrity": "sha1-GwqzvVU7Kg1jmdKcDj6gslIHgyc=", + "dev": true + }, "generic-pool": { "version": "3.8.2", "resolved": "https://registry.npmjs.org/generic-pool/-/generic-pool-3.8.2.tgz", @@ -7238,9 +8642,9 @@ } }, "glob": { - "version": "7.1.7", - "resolved": "https://registry.npmjs.org/glob/-/glob-7.1.7.tgz", - "integrity": "sha512-OvD9ENzPLbegENnYP5UUfJIirTg4+XwMWGaQfQTY0JenxNvvIKP3U3/tAQSPIu/lHxXYSZmpXlUHeqAIdKzBLQ==", + "version": "7.2.0", + "resolved": "https://registry.npmjs.org/glob/-/glob-7.2.0.tgz", + "integrity": "sha512-lmLf6gtyrPq8tTjSmrO94wBeQbFR3HbLHbuyD69wuyQkImp2hWqMGB47OX65FBkPffO641IP9jWa1z4ivqG26Q==", "dev": true, "requires": { "fs.realpath": "^1.0.0", @@ -7270,10 +8674,21 @@ } }, "globals": { - "version": "11.12.0", - "resolved": "https://registry.npmjs.org/globals/-/globals-11.12.0.tgz", - "integrity": "sha512-WOBp/EEGUiIsJSp7wcv/y6MO+lV9UoncWqxuFfm8eBwzWNgyfBd6Gz+IeKQ9jCmyhoH99g15M3T+QaVHFjizVA==", - "dev": true + "version": "13.12.0", + "resolved": "https://registry.npmjs.org/globals/-/globals-13.12.0.tgz", + "integrity": "sha512-uS8X6lSKN2JumVoXrbUz+uG4BYG+eiawqm3qFcT7ammfbUHeCBoJMlHcec/S3krSk73/AE/f0szYFmgAA3kYZg==", + "dev": true, + "requires": { + "type-fest": "^0.20.2" + }, + "dependencies": { + "type-fest": { + "version": "0.20.2", + "resolved": "https://registry.npmjs.org/type-fest/-/type-fest-0.20.2.tgz", + "integrity": "sha512-Ne+eE4r0/iWnpAxD852z3A+N0Bt5RN//NjJwRd2VFHEmrywxf5vsZlh4R6lixl6B+wz/8d+maTSAkN1FIkI3LQ==", + "dev": true + } + } }, "globby": { "version": "11.0.4", @@ -7376,6 +8791,14 @@ "requires": { "is-stream": "^2.0.0", "type-fest": "^0.8.0" + }, + "dependencies": { + "type-fest": { + "version": "0.8.1", + "resolved": "https://registry.npmjs.org/type-fest/-/type-fest-0.8.1.tgz", + "integrity": "sha512-4dbzIzqvjtgiM5rw1k5rEHtBANKmdudhGyBEajN01fEyhaAIhsoKNy6y7+IN93IfpFtwY9iqi7kD+xwKhQsNJA==", + "dev": true + } } }, "he": { @@ -7428,9 +8851,9 @@ "dev": true }, "ignore": { - "version": "5.1.8", - "resolved": "https://registry.npmjs.org/ignore/-/ignore-5.1.8.tgz", - "integrity": "sha512-BMpfD7PpiETpBl/A6S498BaIJ6Y/ABT93ETbby2fP00v4EbvPBXWEoaR1UBPKs3iR53pJY7EtZk5KACI57i1Uw==", + "version": "5.1.9", + "resolved": "https://registry.npmjs.org/ignore/-/ignore-5.1.9.tgz", + "integrity": "sha512-2zeMQpbKz5dhZ9IwL0gbxSW5w0NK/MSAMtNuhgIHEPmaU3vPdKPL0UdvUCXs5SS4JAwsBxysK5sFMW8ocFiVjQ==", "dev": true }, "import-cwd": { @@ -7450,14 +8873,6 @@ "requires": { "parent-module": "^1.0.0", "resolve-from": "^4.0.0" - }, - "dependencies": { - "resolve-from": { - "version": "4.0.0", - "resolved": "https://registry.npmjs.org/resolve-from/-/resolve-from-4.0.0.tgz", - "integrity": "sha512-pb/MYmXstAkysRFx8piNI1tGFNQIFA3vkE3Gq4EuA1dF6gHp/+vgZqsCGJapvy8N3Q+4o7FwvquPJcnZ7RYy4g==", - "dev": true - } } }, "import-from": { @@ -7467,6 +8882,14 @@ "dev": true, "requires": { "resolve-from": "^5.0.0" + }, + "dependencies": { + "resolve-from": { + "version": "5.0.0", + "resolved": "https://registry.npmjs.org/resolve-from/-/resolve-from-5.0.0.tgz", + "integrity": "sha512-qYg9KP24dD5qka9J47d0aVky0N+b4fTU89LN9iDnjB5waksiC49rvMB0PrUJQGoTmH50XPiqOvAjDfaijGxYZw==", + "dev": true + } } }, "import-lazy": { @@ -7562,9 +8985,9 @@ } }, "is-core-module": { - "version": "2.7.0", - "resolved": "https://registry.npmjs.org/is-core-module/-/is-core-module-2.7.0.tgz", - "integrity": "sha512-ByY+tjCciCr+9nLryBYcSD50EOGWt95c7tIsKTG1J2ixKKXPvF7Ej3AVd+UfDydAJom3biBGDBALaO79ktwgEQ==", + "version": "2.8.0", + "resolved": "https://registry.npmjs.org/is-core-module/-/is-core-module-2.8.0.tgz", + "integrity": "sha512-vd15qHsaqrRL7dtH6QNuy0ndJmRDrS9HAM1CAiSifNUFv4x1a0CCVsj18hJ1mShxIG6T2i1sO78MkP56r0nYRw==", "dev": true, "requires": { "has": "^1.0.3" @@ -7710,9 +9133,9 @@ "dev": true }, "istanbul-lib-coverage": { - "version": "3.0.0", - "resolved": "https://registry.npmjs.org/istanbul-lib-coverage/-/istanbul-lib-coverage-3.0.0.tgz", - "integrity": "sha512-UiUIqxMgRDET6eR+o5HbfRYP1l0hqkWOs7vNxC/mggutCMUIhWMm8gAHb8tHlyfD3/l6rlgNA5cKdDzEAf6hEg==", + "version": "3.2.0", + "resolved": "https://registry.npmjs.org/istanbul-lib-coverage/-/istanbul-lib-coverage-3.2.0.tgz", + "integrity": "sha512-eOeJ5BHCmHYvQK7xt9GkdHuzuCGS1Y6g9Gvnx3Ym33fz/HpLRYxiS0wHNr+m/MBC8B647Xt608vCDEvhl9c6Mw==", "dev": true }, "istanbul-lib-hook": { @@ -7734,6 +9157,14 @@ "@istanbuljs/schema": "^0.1.2", "istanbul-lib-coverage": "^3.0.0", "semver": "^6.3.0" + }, + "dependencies": { + "semver": { + "version": "6.3.0", + "resolved": "https://registry.npmjs.org/semver/-/semver-6.3.0.tgz", + "integrity": "sha512-b39TBaTSfV6yBrapU89p5fKekE2m/NwnDocOVruQFS1/veMgdzuPcnOM34M6CwxW8jH/lxEa5rBoDeUwu5HHTw==", + "dev": true + } } }, "istanbul-lib-processinfo": { @@ -7749,6 +9180,14 @@ "p-map": "^3.0.0", "rimraf": "^3.0.0", "uuid": "^3.3.3" + }, + "dependencies": { + "uuid": { + "version": "3.4.0", + "resolved": "https://registry.npmjs.org/uuid/-/uuid-3.4.0.tgz", + "integrity": "sha512-HjSDRw6gZE5JMggctHBcjVak08+KEVhSIiDzFnT9S9aegmp85S/bReBVTb4QTFaRNptJ9kuYaNhnbNEOkbKb/A==", + "dev": true + } } }, "istanbul-lib-report": { @@ -7760,23 +9199,12 @@ "istanbul-lib-coverage": "^3.0.0", "make-dir": "^3.0.0", "supports-color": "^7.1.0" - }, - "dependencies": { - "supports-color": { - "version": "7.2.0", - "resolved": "https://registry.npmjs.org/supports-color/-/supports-color-7.2.0.tgz", - "integrity": "sha512-qpCAvRl9stuOHveKsn7HncJRvv501qIacKzQlO/+Lwxc9+0q2wLyv4Dfvt80/DPn2pqOBsJdDiogXGR9+OvwRw==", - "dev": true, - "requires": { - "has-flag": "^4.0.0" - } - } } }, "istanbul-lib-source-maps": { - "version": "4.0.0", - "resolved": "https://registry.npmjs.org/istanbul-lib-source-maps/-/istanbul-lib-source-maps-4.0.0.tgz", - "integrity": "sha512-c16LpFRkR8vQXyHZ5nLpY35JZtzj1PQY1iZmesUbf1FZHbIupcWfjgOXBY9YHkLEQ6puz1u4Dgj6qmU/DisrZg==", + "version": "4.0.1", + "resolved": "https://registry.npmjs.org/istanbul-lib-source-maps/-/istanbul-lib-source-maps-4.0.1.tgz", + "integrity": "sha512-n3s8EwkdFIJCG3BPKBYvskgXGoy88ARzvegkitk60NxRdwltLOTaH7CUiMRXvwYorl0Q712iEjcWB+fK/MrWVw==", "dev": true, "requires": { "debug": "^4.1.1", @@ -7793,9 +9221,9 @@ } }, "istanbul-reports": { - "version": "3.0.3", - "resolved": "https://registry.npmjs.org/istanbul-reports/-/istanbul-reports-3.0.3.tgz", - "integrity": "sha512-0i77ZFLsb9U3DHi22WzmIngVzfoyxxbQcZRqlF3KoKmCJGq9nhFHoGi8FqBztN2rE8w6hURnZghetn0xpkVb6A==", + "version": "3.0.5", + "resolved": "https://registry.npmjs.org/istanbul-reports/-/istanbul-reports-3.0.5.tgz", + "integrity": "sha512-5+19PlhnGabNWB7kOFnuxT8H3T/iIyQzIbQMxXsURmmvKg86P2sbkrGOT77VnHw0Qr0gc2XzRaRfMZYYbSQCJQ==", "dev": true, "requires": { "html-escaper": "^2.0.0", @@ -7835,6 +9263,18 @@ "integrity": "sha512-xyFwyhro/JEof6Ghe2iz2NcXoj2sloNsWr/XsERDK/oiPCfaNhl5ONfp+jQdAZRQQ0IJWNzH9zIZF7li91kh2w==", "dev": true }, + "json-schema-traverse": { + "version": "0.4.1", + "resolved": "https://registry.npmjs.org/json-schema-traverse/-/json-schema-traverse-0.4.1.tgz", + "integrity": "sha512-xbbCH5dCYU5T8LcEhhuh7HJ88HXuW3qsI3Y0zOZFKfZEHcpWiHU/Jxzk629Brsab/mMiHQti9wMP+845RPe3Vg==", + "dev": true + }, + "json-stable-stringify-without-jsonify": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/json-stable-stringify-without-jsonify/-/json-stable-stringify-without-jsonify-1.0.1.tgz", + "integrity": "sha1-nbe1lJatPzz+8wp1FC0tkwrXJlE=", + "dev": true + }, "json5": { "version": "2.2.0", "resolved": "https://registry.npmjs.org/json5/-/json5-2.2.0.tgz", @@ -7857,9 +9297,9 @@ "dev": true }, "keyv": { - "version": "4.0.3", - "resolved": "https://registry.npmjs.org/keyv/-/keyv-4.0.3.tgz", - "integrity": "sha512-zdGa2TOpSZPq5mU6iowDARnMBZgtCqJ11dJROFi6tg6kTn4nuUdU09lFyLFSaHrWqpIJ+EBq4E8/Dc0Vx5vLdA==", + "version": "4.0.4", + "resolved": "https://registry.npmjs.org/keyv/-/keyv-4.0.4.tgz", + "integrity": "sha512-vqNHbAc8BBsxk+7QBYLW0Y219rWcClspR6WSeoHYKG5mnsSoOH+BL1pWq02DDCVdvvuUny5rkBlzMRzoqc+GIg==", "dev": true, "requires": { "json-buffer": "3.0.1" @@ -7874,6 +9314,16 @@ "package-json": "^6.3.0" } }, + "levn": { + "version": "0.4.1", + "resolved": "https://registry.npmjs.org/levn/-/levn-0.4.1.tgz", + "integrity": "sha512-+bT2uH4E5LGE7h/n3evcS/sQlJXCpIp6ym8OWJ5eV6+67Dsql/LaaT7qJBAt2rzfoa/5QBGBhxDix1dMt2kQKQ==", + "dev": true, + "requires": { + "prelude-ls": "^1.2.1", + "type-check": "~0.4.0" + } + }, "lines-and-columns": { "version": "1.1.6", "resolved": "https://registry.npmjs.org/lines-and-columns/-/lines-and-columns-1.1.6.tgz", @@ -7907,6 +9357,12 @@ "integrity": "sha1-LRd/ZS+jHpObRDjVNBSZ36OCXpk=", "dev": true }, + "lodash.merge": { + "version": "4.6.2", + "resolved": "https://registry.npmjs.org/lodash.merge/-/lodash.merge-4.6.2.tgz", + "integrity": "sha512-0KpjqXRVvrYyCsX1swR/XTK0va6VQkQM6MNo7PqW77ByjAhoARA8EfrP1N4+KlKj8YS0ZUCtRT/YUuhyYDujIQ==", + "dev": true + }, "log-symbols": { "version": "4.1.0", "resolved": "https://registry.npmjs.org/log-symbols/-/log-symbols-4.1.0.tgz", @@ -7924,20 +9380,12 @@ "dev": true }, "lru-cache": { - "version": "5.1.1", - "resolved": "https://registry.npmjs.org/lru-cache/-/lru-cache-5.1.1.tgz", - "integrity": "sha512-KpNARQA3Iwv+jTA0utUVVbrh+Jlrr1Fv0e56GGzAFOXN7dk/FviaDW8LHmK52DlcH4WP2n6gI8vN1aesBFgo9w==", + "version": "6.0.0", + "resolved": "https://registry.npmjs.org/lru-cache/-/lru-cache-6.0.0.tgz", + "integrity": "sha512-Jo6dJ04CmSjuznwJSS3pUeWmd/H0ffTlkXXgwZi+eq1UCmqQwCh+eLsYOYCwY991i2Fah4h1BEMCx4qThGbsiA==", "dev": true, "requires": { - "yallist": "^3.0.2" - }, - "dependencies": { - "yallist": { - "version": "3.1.1", - "resolved": "https://registry.npmjs.org/yallist/-/yallist-3.1.1.tgz", - "integrity": "sha512-a4UGQaWPH59mOXUYnAG2ewncQS4i4F43Tv3JoAM+s2VDAmS9NsK8GpDMLrCHPksFT7h3K6TOoUNn2pb7RoXx4g==", - "dev": true - } + "yallist": "^4.0.0" } }, "lunr": { @@ -7959,6 +9407,14 @@ "dev": true, "requires": { "semver": "^6.0.0" + }, + "dependencies": { + "semver": { + "version": "6.3.0", + "resolved": "https://registry.npmjs.org/semver/-/semver-6.3.0.tgz", + "integrity": "sha512-b39TBaTSfV6yBrapU89p5fKekE2m/NwnDocOVruQFS1/veMgdzuPcnOM34M6CwxW8jH/lxEa5rBoDeUwu5HHTw==", + "dev": true + } } }, "make-error": { @@ -7968,9 +9424,9 @@ "dev": true }, "marked": { - "version": "3.0.7", - "resolved": "https://registry.npmjs.org/marked/-/marked-3.0.7.tgz", - "integrity": "sha512-ctKqbnLuNbsHbI26cfMyOlKgXGfl1orOv1AvWWDX7AkgfMOwCWvmuYc+mVLeWhQ9W6hdWVBynOs96VkcscKo0Q==", + "version": "3.0.8", + "resolved": "https://registry.npmjs.org/marked/-/marked-3.0.8.tgz", + "integrity": "sha512-0gVrAjo5m0VZSJb4rpL59K1unJAMb/hm8HRXqasD8VeC8m91ytDPMritgFSlKonfdt+rRYYpP/JfLxgIX8yoSw==", "dev": true }, "merge-stream": { @@ -8038,9 +9494,9 @@ "dev": true }, "mocha": { - "version": "9.1.2", - "resolved": "https://registry.npmjs.org/mocha/-/mocha-9.1.2.tgz", - "integrity": "sha512-ta3LtJ+63RIBP03VBjMGtSqbe6cWXRejF9SyM9Zyli1CKZJZ+vfCTj3oW24V7wAphMJdpOFLoMI3hjJ1LWbs0w==", + "version": "9.1.3", + "resolved": "https://registry.npmjs.org/mocha/-/mocha-9.1.3.tgz", + "integrity": "sha512-Xcpl9FqXOAYqI3j79pEtHBBnQgVXIhpULjGQa7DVb0Po+VzmSIK9kanAiWLHoRR/dbZ2qpdPshuXr8l1VaHCzw==", "dev": true, "requires": { "@ungap/promise-all-settled": "1.1.2", @@ -8067,12 +9523,76 @@ "yargs": "16.2.0", "yargs-parser": "20.2.4", "yargs-unparser": "2.0.0" + }, + "dependencies": { + "escape-string-regexp": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/escape-string-regexp/-/escape-string-regexp-4.0.0.tgz", + "integrity": "sha512-TtpcNJ3XAzx3Gq8sWRzJaVajRs0uVxA2YAkdb1jm2YkPz4G6egUFAyA3n5vtEIZefPk5Wa4UXbKuS5fKkJWdgA==", + "dev": true + }, + "glob": { + "version": "7.1.7", + "resolved": "https://registry.npmjs.org/glob/-/glob-7.1.7.tgz", + "integrity": "sha512-OvD9ENzPLbegENnYP5UUfJIirTg4+XwMWGaQfQTY0JenxNvvIKP3U3/tAQSPIu/lHxXYSZmpXlUHeqAIdKzBLQ==", + "dev": true, + "requires": { + "fs.realpath": "^1.0.0", + "inflight": "^1.0.4", + "inherits": "2", + "minimatch": "^3.0.4", + "once": "^1.3.0", + "path-is-absolute": "^1.0.0" + } + }, + "ms": { + "version": "2.1.3", + "resolved": "https://registry.npmjs.org/ms/-/ms-2.1.3.tgz", + "integrity": "sha512-6FlzubTLZG3J2a/NVCAleEhjzq5oxgHyaCU9yYXvcLsvoVaHJq/s5xXI6/XXP6tz7R9xAOtHnSO/tXtF3WRTlA==", + "dev": true + }, + "strip-json-comments": { + "version": "3.1.1", + "resolved": "https://registry.npmjs.org/strip-json-comments/-/strip-json-comments-3.1.1.tgz", + "integrity": "sha512-6fPc+R4ihwqP6N/aIv2f1gMH8lOVtWQHoqC4yK6oSDVVocumAsfCqjkXnqiYMhmMwS/mEHLp7Vehlt3ql6lEig==", + "dev": true + }, + "supports-color": { + "version": "8.1.1", + "resolved": "https://registry.npmjs.org/supports-color/-/supports-color-8.1.1.tgz", + "integrity": "sha512-MpUEN2OodtUzxvKQl72cUF7RQ5EiHsGvSsVG0ia9c5RbWGL2CI4C7EpPS8UTBIplnlzZiNuV56w+FuNxy3ty2Q==", + "dev": true, + "requires": { + "has-flag": "^4.0.0" + } + }, + "yargs": { + "version": "16.2.0", + "resolved": "https://registry.npmjs.org/yargs/-/yargs-16.2.0.tgz", + "integrity": "sha512-D1mvvtDG0L5ft/jGWkLpG1+m0eQxOfaBvTNELraWj22wSVUMWxZUvYgJYcKh6jGGIkJFhH4IZPQhR4TKpc8mBw==", + "dev": true, + "requires": { + "cliui": "^7.0.2", + "escalade": "^3.1.1", + "get-caller-file": "^2.0.5", + "require-directory": "^2.1.1", + "string-width": "^4.2.0", + "y18n": "^5.0.5", + "yargs-parser": "^20.2.2" + } + }, + "yargs-parser": { + "version": "20.2.4", + "resolved": "https://registry.npmjs.org/yargs-parser/-/yargs-parser-20.2.4.tgz", + "integrity": "sha512-WOkpgNhPTlE73h4VFAFsOnomJVaovO8VqLDzy5saChRBFQFBoMYirowyW+Q9HB4HFF4Z7VZTiG3iSzJJA29yRA==", + "dev": true + } } }, "ms": { - "version": "2.1.3", - "resolved": "https://registry.npmjs.org/ms/-/ms-2.1.3.tgz", - "integrity": "sha512-6FlzubTLZG3J2a/NVCAleEhjzq5oxgHyaCU9yYXvcLsvoVaHJq/s5xXI6/XXP6tz7R9xAOtHnSO/tXtF3WRTlA==", + "version": "2.1.2", + "resolved": "https://registry.npmjs.org/ms/-/ms-2.1.2.tgz", + "integrity": "sha512-sGkPx+VjMtmA6MX27oA4FBFELFCZZ4S4XqeGOXCv68tT+jb3vk/RyaKWP0PTKyWtmLSM0b+adUTEvbs1PEaH2w==", "dev": true }, "mute-stream": { @@ -8087,6 +9607,12 @@ "integrity": "sha512-rdwtIXaXCLFAQbnfqDRnI6jaRHp9fTcYBjtFKE8eezcZ7LuLjhUaQGNeMXf1HmRoCH32CLz6XwX0TtxEOS/A3Q==", "dev": true }, + "natural-compare": { + "version": "1.4.0", + "resolved": "https://registry.npmjs.org/natural-compare/-/natural-compare-1.4.0.tgz", + "integrity": "sha1-Sr6/7tdUHywnrPspvbvRXI1bpPc=", + "dev": true + }, "neo-async": { "version": "2.6.2", "resolved": "https://registry.npmjs.org/neo-async/-/neo-async-2.6.2.tgz", @@ -8124,9 +9650,9 @@ } }, "node-fetch": { - "version": "2.6.5", - "resolved": "https://registry.npmjs.org/node-fetch/-/node-fetch-2.6.5.tgz", - "integrity": "sha512-mmlIVHJEu5rnIxgEgez6b9GgWXbkZj5YZ7fx+2r94a2E+Uirsp6HsPTPlomfdHtpt/B0cdKviwkoaM6pyvUOpQ==", + "version": "2.6.6", + "resolved": "https://registry.npmjs.org/node-fetch/-/node-fetch-2.6.6.tgz", + "integrity": "sha512-Z8/6vRlTUChSdIgMa51jxQ4lrw/Jy5SOW10ObaA47/RElsAN2c5Pn8bTgFGWn/ibwzXTE8qwr1Yzx28vsecXEA==", "dev": true, "requires": { "whatwg-url": "^5.0.0" @@ -8142,9 +9668,9 @@ } }, "node-releases": { - "version": "1.1.77", - "resolved": "https://registry.npmjs.org/node-releases/-/node-releases-1.1.77.tgz", - "integrity": "sha512-rB1DUFUNAN4Gn9keO2K1efO35IDK7yKHCdCaIMvFO7yUYmmZYeDjnGKle26G4rwj+LKRQpjyUUvMkPglwGCYNQ==", + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/node-releases/-/node-releases-2.0.1.tgz", + "integrity": "sha512-CqyzN6z7Q6aMeF/ktcMVTzhAHCEpf8SOarwpzpf8pNBY2k5/oM34UHldUwp8VKI7uxct2HxSRdJjBaZeESzcxA==", "dev": true }, "normalize-path": { @@ -8203,6 +9729,12 @@ "yargs": "^15.0.2" }, "dependencies": { + "camelcase": { + "version": "5.3.1", + "resolved": "https://registry.npmjs.org/camelcase/-/camelcase-5.3.1.tgz", + "integrity": "sha512-L28STB170nwWS63UjtlEOE3dldQApaJXZkOI1uMFfzf3rRuPegHaHesyee+YxQ+W6SvRDQV6UrdOdRiR153wJg==", + "dev": true + }, "cliui": { "version": "6.0.0", "resolved": "https://registry.npmjs.org/cliui/-/cliui-6.0.0.tgz", @@ -8251,6 +9783,12 @@ "p-limit": "^2.2.0" } }, + "resolve-from": { + "version": "5.0.0", + "resolved": "https://registry.npmjs.org/resolve-from/-/resolve-from-5.0.0.tgz", + "integrity": "sha512-qYg9KP24dD5qka9J47d0aVky0N+b4fTU89LN9iDnjB5waksiC49rvMB0PrUJQGoTmH50XPiqOvAjDfaijGxYZw==", + "dev": true + }, "wrap-ansi": { "version": "6.2.0", "resolved": "https://registry.npmjs.org/wrap-ansi/-/wrap-ansi-6.2.0.tgz", @@ -8330,6 +9868,23 @@ "dev": true, "requires": { "lru-cache": "^5.1.1" + }, + "dependencies": { + "lru-cache": { + "version": "5.1.1", + "resolved": "https://registry.npmjs.org/lru-cache/-/lru-cache-5.1.1.tgz", + "integrity": "sha512-KpNARQA3Iwv+jTA0utUVVbrh+Jlrr1Fv0e56GGzAFOXN7dk/FviaDW8LHmK52DlcH4WP2n6gI8vN1aesBFgo9w==", + "dev": true, + "requires": { + "yallist": "^3.0.2" + } + }, + "yallist": { + "version": "3.1.1", + "resolved": "https://registry.npmjs.org/yallist/-/yallist-3.1.1.tgz", + "integrity": "sha512-a4UGQaWPH59mOXUYnAG2ewncQS4i4F43Tv3JoAM+s2VDAmS9NsK8GpDMLrCHPksFT7h3K6TOoUNn2pb7RoXx4g==", + "dev": true + } } }, "open": { @@ -8342,6 +9897,20 @@ "is-wsl": "^2.1.1" } }, + "optionator": { + "version": "0.9.1", + "resolved": "https://registry.npmjs.org/optionator/-/optionator-0.9.1.tgz", + "integrity": "sha512-74RlY5FCnhq4jRxVUPKDaRwrVNXMqsGsiW6AJw4XK8hmtm10wC0ypZBLw5IIp85NZMr91+qd1RvvENwg7jjRFw==", + "dev": true, + "requires": { + "deep-is": "^0.1.3", + "fast-levenshtein": "^2.0.6", + "levn": "^0.4.1", + "prelude-ls": "^1.2.1", + "type-check": "^0.4.0", + "word-wrap": "^1.2.3" + } + }, "ora": { "version": "5.4.1", "resolved": "https://registry.npmjs.org/ora/-/ora-5.4.1.tgz", @@ -8573,6 +10142,12 @@ "dev": true } } + }, + "semver": { + "version": "6.3.0", + "resolved": "https://registry.npmjs.org/semver/-/semver-6.3.0.tgz", + "integrity": "sha512-b39TBaTSfV6yBrapU89p5fKekE2m/NwnDocOVruQFS1/veMgdzuPcnOM34M6CwxW8jH/lxEa5rBoDeUwu5HHTw==", + "dev": true } } }, @@ -8661,9 +10236,9 @@ "dev": true }, "picocolors": { - "version": "0.2.1", - "resolved": "https://registry.npmjs.org/picocolors/-/picocolors-0.2.1.tgz", - "integrity": "sha512-cMlDqaLEqfSaW8Z7N5Jw+lyIW869EzT73/F5lhtY9cLGoVxSXznfgfXMO0Z5K0o0Q2TkTXq+0KFsdnSe3jDViA==", + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/picocolors/-/picocolors-1.0.0.tgz", + "integrity": "sha512-1fygroTLlHu66zi26VoTDv8yRgm0Fccecssto+MhsZ0D/DGW2sm8E8AjW7NU5VVTRt5GxbeZ5qBuJr+HyLYkjQ==", "dev": true }, "picomatch": { @@ -8720,6 +10295,12 @@ } } }, + "prelude-ls": { + "version": "1.2.1", + "resolved": "https://registry.npmjs.org/prelude-ls/-/prelude-ls-1.2.1.tgz", + "integrity": "sha512-vkcDPrRZo1QZLbn5RLGPpg/WmIQ65qoWWhcGKf/b5eplkkarX0m9z8ppCat4mlOqUsWpyNuYgO3VRyrYHSzX5g==", + "dev": true + }, "prepend-http": { "version": "2.0.0", "resolved": "https://registry.npmjs.org/prepend-http/-/prepend-http-2.0.0.tgz", @@ -8735,6 +10316,12 @@ "fromentries": "^1.2.0" } }, + "progress": { + "version": "2.0.3", + "resolved": "https://registry.npmjs.org/progress/-/progress-2.0.3.tgz", + "integrity": "sha512-7PiHtLll5LdnKIMw100I+8xJXR5gW2QwWYkT6iJva0bXitZKa/XMrSbdmg3r2Xnaidz9Qumd0VPaMrZlF9V9sA==", + "dev": true + }, "protocols": { "version": "1.4.8", "resolved": "https://registry.npmjs.org/protocols/-/protocols-1.4.8.tgz", @@ -8751,6 +10338,12 @@ "once": "^1.3.1" } }, + "punycode": { + "version": "2.1.1", + "resolved": "https://registry.npmjs.org/punycode/-/punycode-2.1.1.tgz", + "integrity": "sha512-XRsRjdf+j5ml+y/6GKHPZbrF/8p2Yga0JPtdqTIY2Xe5ohJPD9saDJJLPvp9+NSBprVvevdXZybnj2cv8OEd0A==", + "dev": true + }, "pupa": { "version": "2.1.1", "resolved": "https://registry.npmjs.org/pupa/-/pupa-2.1.1.tgz", @@ -8819,12 +10412,6 @@ "resolved": "https://registry.npmjs.org/ini/-/ini-1.3.8.tgz", "integrity": "sha512-JV/yugV2uzW5iMRSiZAyDtQd+nxtUnjeLt0acNdw98kKLrvuRVyB80tsREOE7yvGVgalhZ6RNXCmEHkUKBKxew==", "dev": true - }, - "strip-json-comments": { - "version": "2.0.1", - "resolved": "https://registry.npmjs.org/strip-json-comments/-/strip-json-comments-2.0.1.tgz", - "integrity": "sha1-PFMZQukIwml8DsNEhYwobHygpgo=", - "dev": true } } }, @@ -8870,6 +10457,12 @@ "redis-errors": "^1.0.0" } }, + "regexpp": { + "version": "3.2.0", + "resolved": "https://registry.npmjs.org/regexpp/-/regexpp-3.2.0.tgz", + "integrity": "sha512-pq2bWo9mVD43nbts2wGv17XLiNLya+GklZ8kaDLV2Z08gDCsGpnKn9BFMepvWuHCbyVvY7J5o5+BVvoQbmlJLg==", + "dev": true + }, "registry-auth-token": { "version": "4.2.1", "resolved": "https://registry.npmjs.org/registry-auth-token/-/registry-auth-token-4.2.1.tgz", @@ -8889,9 +10482,9 @@ } }, "release-it": { - "version": "14.11.6", - "resolved": "https://registry.npmjs.org/release-it/-/release-it-14.11.6.tgz", - "integrity": "sha512-6BNcuzFZHThBUBJ/xYw/bxZ+58CAwrwf1zgmjq2Ibl3nlDZbjphHG6iqxkJu7mZ8TIWs6NjloEAhqpjeXoN//Q==", + "version": "14.11.7", + "resolved": "https://registry.npmjs.org/release-it/-/release-it-14.11.7.tgz", + "integrity": "sha512-m4p9+x6AEQPczc96Jyg6dGFeovpJVgRCtA1lxeIgTmQVt9dutYPkkjZeJngZgUJ17/Lb1bx6ZzW2qsKmopKnbQ==", "dev": true, "requires": { "@iarna/toml": "2.2.5", @@ -8923,38 +10516,6 @@ "uuid": "8.3.2", "yaml": "1.10.2", "yargs-parser": "20.2.9" - }, - "dependencies": { - "lru-cache": { - "version": "6.0.0", - "resolved": "https://registry.npmjs.org/lru-cache/-/lru-cache-6.0.0.tgz", - "integrity": "sha512-Jo6dJ04CmSjuznwJSS3pUeWmd/H0ffTlkXXgwZi+eq1UCmqQwCh+eLsYOYCwY991i2Fah4h1BEMCx4qThGbsiA==", - "dev": true, - "requires": { - "yallist": "^4.0.0" - } - }, - "semver": { - "version": "7.3.5", - "resolved": "https://registry.npmjs.org/semver/-/semver-7.3.5.tgz", - "integrity": "sha512-PoeGJYh8HK4BTO/a9Tf6ZG3veo/A7ZVsYrSA6J8ny9nb3B1VrpkuN+z9OE5wfE5p6H4LchYZsegiQgbJD94ZFQ==", - "dev": true, - "requires": { - "lru-cache": "^6.0.0" - } - }, - "uuid": { - "version": "8.3.2", - "resolved": "https://registry.npmjs.org/uuid/-/uuid-8.3.2.tgz", - "integrity": "sha512-+NYs2QeMWy+GWFOEm9xnn6HCDp0l7QBD7ml8zLUmJ+93Q5NF0NocErnwkTkXVFNiX3/fpC6afS8Dhb/gz7R7eg==", - "dev": true - }, - "yargs-parser": { - "version": "20.2.9", - "resolved": "https://registry.npmjs.org/yargs-parser/-/yargs-parser-20.2.9.tgz", - "integrity": "sha512-y11nGElTIV+CT3Zv9t7VKl+Q3hTQoT9a1Qzezhhl6Rp21gJ/IVTW7Z3y9EWXhuUBC2Shnf+DX0antecpAwSP8w==", - "dev": true - } } }, "release-zalgo": { @@ -8995,9 +10556,9 @@ "dev": true }, "resolve-from": { - "version": "5.0.0", - "resolved": "https://registry.npmjs.org/resolve-from/-/resolve-from-5.0.0.tgz", - "integrity": "sha512-qYg9KP24dD5qka9J47d0aVky0N+b4fTU89LN9iDnjB5waksiC49rvMB0PrUJQGoTmH50XPiqOvAjDfaijGxYZw==", + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/resolve-from/-/resolve-from-4.0.0.tgz", + "integrity": "sha512-pb/MYmXstAkysRFx8piNI1tGFNQIFA3vkE3Gq4EuA1dF6gHp/+vgZqsCGJapvy8N3Q+4o7FwvquPJcnZ7RYy4g==", "dev": true }, "responselike": { @@ -9065,9 +10626,9 @@ } }, "safe-buffer": { - "version": "5.1.2", - "resolved": "https://registry.npmjs.org/safe-buffer/-/safe-buffer-5.1.2.tgz", - "integrity": "sha512-Gd2UZBJDkXlY7GbJxfsE8/nvKkUEU1G38c1siN6QP6a9PT9MmHB8GnpscSmMJSoF8LOIrt8ud/wPtojys4G6+g==", + "version": "5.2.1", + "resolved": "https://registry.npmjs.org/safe-buffer/-/safe-buffer-5.2.1.tgz", + "integrity": "sha512-rp3So07KcdmmKbGvgaNxQSJr7bGVSVk5S9Eq1F+ppbRo70+YeaDxkw5Dd8NPN+GD6bjnYm2VuPuCXmpuYvmCXQ==", "dev": true }, "safer-buffer": { @@ -9077,10 +10638,13 @@ "dev": true }, "semver": { - "version": "6.3.0", - "resolved": "https://registry.npmjs.org/semver/-/semver-6.3.0.tgz", - "integrity": "sha512-b39TBaTSfV6yBrapU89p5fKekE2m/NwnDocOVruQFS1/veMgdzuPcnOM34M6CwxW8jH/lxEa5rBoDeUwu5HHTw==", - "dev": true + "version": "7.3.5", + "resolved": "https://registry.npmjs.org/semver/-/semver-7.3.5.tgz", + "integrity": "sha512-PoeGJYh8HK4BTO/a9Tf6ZG3veo/A7ZVsYrSA6J8ny9nb3B1VrpkuN+z9OE5wfE5p6H4LchYZsegiQgbJD94ZFQ==", + "dev": true, + "requires": { + "lru-cache": "^6.0.0" + } }, "semver-diff": { "version": "3.1.1", @@ -9089,6 +10653,14 @@ "dev": true, "requires": { "semver": "^6.3.0" + }, + "dependencies": { + "semver": { + "version": "6.3.0", + "resolved": "https://registry.npmjs.org/semver/-/semver-6.3.0.tgz", + "integrity": "sha512-b39TBaTSfV6yBrapU89p5fKekE2m/NwnDocOVruQFS1/veMgdzuPcnOM34M6CwxW8jH/lxEa5rBoDeUwu5HHTw==", + "dev": true + } } }, "serialize-javascript": { @@ -9133,9 +10705,9 @@ } }, "shiki": { - "version": "0.9.11", - "resolved": "https://registry.npmjs.org/shiki/-/shiki-0.9.11.tgz", - "integrity": "sha512-tjruNTLFhU0hruCPoJP0y+B9LKOmcqUhTpxn7pcJB3fa+04gFChuEmxmrUfOJ7ZO6Jd+HwMnDHgY3lv3Tqonuw==", + "version": "0.9.12", + "resolved": "https://registry.npmjs.org/shiki/-/shiki-0.9.12.tgz", + "integrity": "sha512-VXcROdldv0/Qu0w2XvzU4IrvTeBNs/Kj/FCmtcEXGz7Tic/veQzliJj6tEiAgoKianhQstpYmbPDStHU5Opqcw==", "dev": true, "requires": { "jsonc-parser": "^3.0.0", @@ -9161,26 +10733,26 @@ "dev": true }, "sinon": { - "version": "11.1.2", - "resolved": "https://registry.npmjs.org/sinon/-/sinon-11.1.2.tgz", - "integrity": "sha512-59237HChms4kg7/sXhiRcUzdSkKuydDeTiamT/jesUVHshBgL8XAmhgFo0GfK6RruMDM/iRSij1EybmMog9cJw==", + "version": "12.0.1", + "resolved": "https://registry.npmjs.org/sinon/-/sinon-12.0.1.tgz", + "integrity": "sha512-iGu29Xhym33ydkAT+aNQFBINakjq69kKO6ByPvTsm3yyIACfyQttRTP03aBP/I8GfhFmLzrnKwNNkr0ORb1udg==", "dev": true, "requires": { "@sinonjs/commons": "^1.8.3", - "@sinonjs/fake-timers": "^7.1.2", + "@sinonjs/fake-timers": "^8.1.0", "@sinonjs/samsam": "^6.0.2", "diff": "^5.0.0", "nise": "^5.1.0", "supports-color": "^7.2.0" }, "dependencies": { - "supports-color": { - "version": "7.2.0", - "resolved": "https://registry.npmjs.org/supports-color/-/supports-color-7.2.0.tgz", - "integrity": "sha512-qpCAvRl9stuOHveKsn7HncJRvv501qIacKzQlO/+Lwxc9+0q2wLyv4Dfvt80/DPn2pqOBsJdDiogXGR9+OvwRw==", + "@sinonjs/fake-timers": { + "version": "8.1.0", + "resolved": "https://registry.npmjs.org/@sinonjs/fake-timers/-/fake-timers-8.1.0.tgz", + "integrity": "sha512-OAPJUAtgeINhh/TAlUID4QTs53Njm7xzddaVlEs/SXwgtiD1tW22zAB/W1wdqfrpmikgaWQ9Fw6Ws+hsiRm5Vg==", "dev": true, "requires": { - "has-flag": "^4.0.0" + "@sinonjs/commons": "^1.7.0" } } } @@ -9254,14 +10826,6 @@ "dev": true, "requires": { "safe-buffer": "~5.2.0" - }, - "dependencies": { - "safe-buffer": { - "version": "5.2.1", - "resolved": "https://registry.npmjs.org/safe-buffer/-/safe-buffer-5.2.1.tgz", - "integrity": "sha512-rp3So07KcdmmKbGvgaNxQSJr7bGVSVk5S9Eq1F+ppbRo70+YeaDxkw5Dd8NPN+GD6bjnYm2VuPuCXmpuYvmCXQ==", - "dev": true - } } }, "string-width": { @@ -9297,15 +10861,15 @@ "dev": true }, "strip-json-comments": { - "version": "3.1.1", - "resolved": "https://registry.npmjs.org/strip-json-comments/-/strip-json-comments-3.1.1.tgz", - "integrity": "sha512-6fPc+R4ihwqP6N/aIv2f1gMH8lOVtWQHoqC4yK6oSDVVocumAsfCqjkXnqiYMhmMwS/mEHLp7Vehlt3ql6lEig==", + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/strip-json-comments/-/strip-json-comments-2.0.1.tgz", + "integrity": "sha1-PFMZQukIwml8DsNEhYwobHygpgo=", "dev": true }, "supports-color": { - "version": "8.1.1", - "resolved": "https://registry.npmjs.org/supports-color/-/supports-color-8.1.1.tgz", - "integrity": "sha512-MpUEN2OodtUzxvKQl72cUF7RQ5EiHsGvSsVG0ia9c5RbWGL2CI4C7EpPS8UTBIplnlzZiNuV56w+FuNxy3ty2Q==", + "version": "7.2.0", + "resolved": "https://registry.npmjs.org/supports-color/-/supports-color-7.2.0.tgz", + "integrity": "sha512-qpCAvRl9stuOHveKsn7HncJRvv501qIacKzQlO/+Lwxc9+0q2wLyv4Dfvt80/DPn2pqOBsJdDiogXGR9+OvwRw==", "dev": true, "requires": { "has-flag": "^4.0.0" @@ -9322,6 +10886,12 @@ "minimatch": "^3.0.4" } }, + "text-table": { + "version": "0.2.0", + "resolved": "https://registry.npmjs.org/text-table/-/text-table-0.2.0.tgz", + "integrity": "sha1-f17oI66AUgfACvLfSoTsP8+lcLQ=", + "dev": true + }, "through": { "version": "2.3.8", "resolved": "https://registry.npmjs.org/through/-/through-2.3.8.tgz", @@ -9365,9 +10935,9 @@ "dev": true }, "ts-node": { - "version": "10.3.0", - "resolved": "https://registry.npmjs.org/ts-node/-/ts-node-10.3.0.tgz", - "integrity": "sha512-RYIy3i8IgpFH45AX4fQHExrT8BxDeKTdC83QFJkNzkvt8uFB6QJ8XMyhynYiKMLxt9a7yuXaDBZNOYS3XjDcYw==", + "version": "10.4.0", + "resolved": "https://registry.npmjs.org/ts-node/-/ts-node-10.4.0.tgz", + "integrity": "sha512-g0FlPvvCXSIO1JDF6S232P5jPYqBkRL9qly81ZgAOSU7rwI0stphCgd2kLiCrU9DjQCrJMWEqcNSjQL02s6d8A==", "dev": true, "requires": { "@cspotcode/source-map-support": "0.7.0", @@ -9398,6 +10968,32 @@ "integrity": "sha512-hcVC3wYEziELGGmEEXue7D75zbwIIVUMWAVbHItGPx0ziyXxrOMQx4rQEVEV45Ut/1IotuEvwqPopzIOkDMf0A==", "dev": true }, + "tsutils": { + "version": "3.21.0", + "resolved": "https://registry.npmjs.org/tsutils/-/tsutils-3.21.0.tgz", + "integrity": "sha512-mHKK3iUXL+3UF6xL5k0PEhKRUBKPBCv/+RkEOpjRWxxx27KKRBmmA60A9pgOUvMi8GKhRMPEmjBRPzs2W7O1OA==", + "dev": true, + "requires": { + "tslib": "^1.8.1" + }, + "dependencies": { + "tslib": { + "version": "1.14.1", + "resolved": "https://registry.npmjs.org/tslib/-/tslib-1.14.1.tgz", + "integrity": "sha512-Xni35NKzjgMrwevysHTCArtLDpPvye8zV/0E4EyYn43P7/7qvQwPh9BGkHewbMulVntbigmcT7rdX3BNo9wRJg==", + "dev": true + } + } + }, + "type-check": { + "version": "0.4.0", + "resolved": "https://registry.npmjs.org/type-check/-/type-check-0.4.0.tgz", + "integrity": "sha512-XleUoc9uwGXqjWwXaUTZAmzMcFZ5858QA2vvx1Ur5xIcixXIP+8LnFDgRplU30us6teqdlskFfu+ae4K79Ooew==", + "dev": true, + "requires": { + "prelude-ls": "^1.2.1" + } + }, "type-detect": { "version": "4.0.8", "resolved": "https://registry.npmjs.org/type-detect/-/type-detect-4.0.8.tgz", @@ -9405,9 +11001,9 @@ "dev": true }, "type-fest": { - "version": "0.8.1", - "resolved": "https://registry.npmjs.org/type-fest/-/type-fest-0.8.1.tgz", - "integrity": "sha512-4dbzIzqvjtgiM5rw1k5rEHtBANKmdudhGyBEajN01fEyhaAIhsoKNy6y7+IN93IfpFtwY9iqi7kD+xwKhQsNJA==", + "version": "0.21.3", + "resolved": "https://registry.npmjs.org/type-fest/-/type-fest-0.21.3.tgz", + "integrity": "sha512-t0rzBq87m3fVcduHDUFhKmyyX+9eo6WQjZvf51Ea/M0Q7+T374Jp1aUiyUl0GKxp8M/OETVHSDvmkyPgvX+X2w==", "dev": true }, "typedarray-to-buffer": { @@ -9420,32 +11016,16 @@ } }, "typedoc": { - "version": "0.22.5", - "resolved": "https://registry.npmjs.org/typedoc/-/typedoc-0.22.5.tgz", - "integrity": "sha512-KFrWGU1iKiTGw0RcyjLNYDmhd7uICU14HgBNPmFKY/sT4Pm/fraaLyWyisst9vGTUAKxqibqoDITR7+ZcAkhHg==", + "version": "0.22.9", + "resolved": "https://registry.npmjs.org/typedoc/-/typedoc-0.22.9.tgz", + "integrity": "sha512-84PjudoXVcap6bwdZFbYIUWlgdz/iLV09ZHwrCzhtHWXaDQG6mlosJ8te6DSThuRkRvQjp46HO+qY/P7Gpm78g==", "dev": true, "requires": { "glob": "^7.2.0", "lunr": "^2.3.9", - "marked": "^3.0.4", + "marked": "^3.0.8", "minimatch": "^3.0.4", - "shiki": "^0.9.11" - }, - "dependencies": { - "glob": { - "version": "7.2.0", - "resolved": "https://registry.npmjs.org/glob/-/glob-7.2.0.tgz", - "integrity": "sha512-lmLf6gtyrPq8tTjSmrO94wBeQbFR3HbLHbuyD69wuyQkImp2hWqMGB47OX65FBkPffO641IP9jWa1z4ivqG26Q==", - "dev": true, - "requires": { - "fs.realpath": "^1.0.0", - "inflight": "^1.0.4", - "inherits": "2", - "minimatch": "^3.0.4", - "once": "^1.3.0", - "path-is-absolute": "^1.0.0" - } - } + "shiki": "^0.9.12" } }, "typedoc-github-wiki-theme": { @@ -9456,24 +11036,24 @@ "requires": {} }, "typedoc-plugin-markdown": { - "version": "3.11.3", - "resolved": "https://registry.npmjs.org/typedoc-plugin-markdown/-/typedoc-plugin-markdown-3.11.3.tgz", - "integrity": "sha512-rWiHbEIe0oZetDIsBR24XJVxGOJ91kDcHoj2KhFKxCLoJGX659EKBQkHne9QJ4W2stGhu1fRgFyQaouSBnxukA==", + "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==", "dev": true, "requires": { "handlebars": "^4.7.7" } }, "typescript": { - "version": "4.4.3", - "resolved": "https://registry.npmjs.org/typescript/-/typescript-4.4.3.tgz", - "integrity": "sha512-4xfscpisVgqqDfPaJo5vkd+Qd/ItkoagnHpufr+i2QCHBsNYp+G7UAoyFl8aPtx879u38wPV65rZ8qbGZijalA==", + "version": "4.4.4", + "resolved": "https://registry.npmjs.org/typescript/-/typescript-4.4.4.tgz", + "integrity": "sha512-DqGhF5IKoBl8WNf8C1gu8q0xZSInh9j1kJJMqT3a94w1JzVaBU4EXOSMrz9yDqMT0xt3selp83fuFMQ0uzv6qA==", "dev": true }, "uglify-js": { - "version": "3.14.2", - "resolved": "https://registry.npmjs.org/uglify-js/-/uglify-js-3.14.2.tgz", - "integrity": "sha512-rtPMlmcO4agTUfz10CbgJ1k6UAoXM2gWb3GoMPPZB/+/Ackf8lNWk11K4rYi2D0apgoFRLtQOZhb+/iGNJq26A==", + "version": "3.14.3", + "resolved": "https://registry.npmjs.org/uglify-js/-/uglify-js-3.14.3.tgz", + "integrity": "sha512-mic3aOdiq01DuSVx0TseaEzMIVqebMZ0Z3vaeDhFEh9bsc24hV1TFvN74reA2vs08D0ZWfNjAcJ3UbVLaBss+g==", "dev": true, "optional": true }, @@ -9528,27 +11108,18 @@ "requires": { "ci-info": "^2.0.0" } - }, - "lru-cache": { - "version": "6.0.0", - "resolved": "https://registry.npmjs.org/lru-cache/-/lru-cache-6.0.0.tgz", - "integrity": "sha512-Jo6dJ04CmSjuznwJSS3pUeWmd/H0ffTlkXXgwZi+eq1UCmqQwCh+eLsYOYCwY991i2Fah4h1BEMCx4qThGbsiA==", - "dev": true, - "requires": { - "yallist": "^4.0.0" - } - }, - "semver": { - "version": "7.3.5", - "resolved": "https://registry.npmjs.org/semver/-/semver-7.3.5.tgz", - "integrity": "sha512-PoeGJYh8HK4BTO/a9Tf6ZG3veo/A7ZVsYrSA6J8ny9nb3B1VrpkuN+z9OE5wfE5p6H4LchYZsegiQgbJD94ZFQ==", - "dev": true, - "requires": { - "lru-cache": "^6.0.0" - } } } }, + "uri-js": { + "version": "4.4.1", + "resolved": "https://registry.npmjs.org/uri-js/-/uri-js-4.4.1.tgz", + "integrity": "sha512-7rKUyy33Q1yc98pQ1DAmLtwX109F7TIfWlW1Ydo8Wl1ii1SeHieeh0HHfPeL2fMXK6z0s8ecKs9frCuLJvndBg==", + "dev": true, + "requires": { + "punycode": "^2.1.0" + } + }, "url-join": { "version": "4.0.1", "resolved": "https://registry.npmjs.org/url-join/-/url-join-4.0.1.tgz", @@ -9571,9 +11142,15 @@ "dev": true }, "uuid": { - "version": "3.4.0", - "resolved": "https://registry.npmjs.org/uuid/-/uuid-3.4.0.tgz", - "integrity": "sha512-HjSDRw6gZE5JMggctHBcjVak08+KEVhSIiDzFnT9S9aegmp85S/bReBVTb4QTFaRNptJ9kuYaNhnbNEOkbKb/A==", + "version": "8.3.2", + "resolved": "https://registry.npmjs.org/uuid/-/uuid-8.3.2.tgz", + "integrity": "sha512-+NYs2QeMWy+GWFOEm9xnn6HCDp0l7QBD7ml8zLUmJ+93Q5NF0NocErnwkTkXVFNiX3/fpC6afS8Dhb/gz7R7eg==", + "dev": true + }, + "v8-compile-cache": { + "version": "2.3.0", + "resolved": "https://registry.npmjs.org/v8-compile-cache/-/v8-compile-cache-2.3.0.tgz", + "integrity": "sha512-l8lCEmLcLYZh4nbunNZvQCJc5pv7+RCwa8q/LdUx8u7lsWvPDKmpodJAJNwkAhJC//dFY48KuIEmjtd4RViDrA==", "dev": true }, "vscode-textmate": { @@ -9674,6 +11251,12 @@ } } }, + "word-wrap": { + "version": "1.2.3", + "resolved": "https://registry.npmjs.org/word-wrap/-/word-wrap-1.2.3.tgz", + "integrity": "sha512-Hz/mrNwitNRh/HUAtM/VT/5VH+ygD6DV7mYKZAtHOrbs8U7lvPS6xf7EJKMF0uW1KJCl0H701g3ZGus+muE5vQ==", + "dev": true + }, "wordwrap": { "version": "1.0.0", "resolved": "https://registry.npmjs.org/wordwrap/-/wordwrap-1.0.0.tgz", @@ -9739,9 +11322,9 @@ "dev": true }, "yargs": { - "version": "16.2.0", - "resolved": "https://registry.npmjs.org/yargs/-/yargs-16.2.0.tgz", - "integrity": "sha512-D1mvvtDG0L5ft/jGWkLpG1+m0eQxOfaBvTNELraWj22wSVUMWxZUvYgJYcKh6jGGIkJFhH4IZPQhR4TKpc8mBw==", + "version": "17.2.1", + "resolved": "https://registry.npmjs.org/yargs/-/yargs-17.2.1.tgz", + "integrity": "sha512-XfR8du6ua4K6uLGm5S6fA+FIJom/MdJcFNVY8geLlp2v8GYbOXD4EB1tPNZsRn4vBzKGMgb5DRZMeWuFc2GO8Q==", "dev": true, "requires": { "cliui": "^7.0.2", @@ -9754,9 +11337,9 @@ } }, "yargs-parser": { - "version": "20.2.4", - "resolved": "https://registry.npmjs.org/yargs-parser/-/yargs-parser-20.2.4.tgz", - "integrity": "sha512-WOkpgNhPTlE73h4VFAFsOnomJVaovO8VqLDzy5saChRBFQFBoMYirowyW+Q9HB4HFF4Z7VZTiG3iSzJJA29yRA==", + "version": "20.2.9", + "resolved": "https://registry.npmjs.org/yargs-parser/-/yargs-parser-20.2.9.tgz", + "integrity": "sha512-y11nGElTIV+CT3Zv9t7VKl+Q3hTQoT9a1Qzezhhl6Rp21gJ/IVTW7Z3y9EWXhuUBC2Shnf+DX0antecpAwSP8w==", "dev": true }, "yargs-unparser": { @@ -9771,12 +11354,6 @@ "is-plain-obj": "^2.1.0" }, "dependencies": { - "camelcase": { - "version": "6.2.0", - "resolved": "https://registry.npmjs.org/camelcase/-/camelcase-6.2.0.tgz", - "integrity": "sha512-c7wVvbw3f37nuobQNtgsgG9POC9qMbNuMQmTCqZv23b6MIz0fcYpBiOlv9gEN/hdLdnZTDQhg6e9Dq5M1vKvfg==", - "dev": true - }, "decamelize": { "version": "4.0.0", "resolved": "https://registry.npmjs.org/decamelize/-/decamelize-4.0.0.tgz", diff --git a/package.json b/package.json index 7f8720f47bf..49339e44d26 100644 --- a/package.json +++ b/package.json @@ -1,66 +1,37 @@ { "name": "redis", - "version": "4.0.0-rc.3", - "description": "A high performance Redis client.", - "keywords": [ - "database", - "redis", - "pubsub" - ], - "author": "Matt Ranney ", - "contributors": [ - { - "name": "Mike Diarmid (Salakar)", - "url": "https://github.com/salakar" - }, - { - "name": "Ruben Bridgewater (BridgeAR)", - "url": "https://github.com/BridgeAR" - } - ], + "version": "4.0.0-rc.4", "license": "MIT", "main": "./dist/index.js", "types": "./dist/index.d.ts", + "workspaces": [ + "./packages/*" + ], "scripts": { - "test": "nyc -r text-summary -r html mocha -r source-map-support/register -r ts-node/register './lib/**/*.spec.ts'", + "test": "npm run test -ws --if-present", + "build:client": "npm run build -w ./packages/client", + "build:test-utils": "npm run build -w ./packages/test-utils", + "build:tests-tools": "npm run build:client && npm run build:test-utils", + "build:modules": "find ./packages -mindepth 1 -maxdepth 1 -type d ! -name 'client' ! -name 'test-utils' -exec npm run build -w {} \\;", "build": "tsc", - "documentation": "typedoc" + "build-all": "npm run build:client && npm run build:test-utils && npm run build:modules && npm run build" }, "dependencies": { - "cluster-key-slot": "1.1.0", - "generic-pool": "3.8.2", - "redis-parser": "3.0.0", - "yallist": "4.0.0" + "@node-redis/client": "^1.0.0-rc.0", + "@node-redis/json": "^1.0.0-rc.0", + "@node-redis/search": "^1.0.0-rc.0" }, "devDependencies": { - "@istanbuljs/nyc-config-typescript": "^1.0.1", "@tsconfig/node12": "^1.0.9", - "@types/mocha": "^9.0.0", - "@types/node": "^16.10.3", - "@types/sinon": "^10.0.4", - "@types/which": "^2.0.1", - "@types/yallist": "^4.0.1", - "mocha": "^9.1.2", - "nyc": "^15.1.0", - "release-it": "^14.11.6", - "sinon": "^11.1.2", - "source-map-support": "^0.5.20", - "ts-node": "^10.3.0", - "typedoc": "^0.22.5", - "typedoc-github-wiki-theme": "^0.6.0", - "typedoc-plugin-markdown": "^3.11.3", - "typescript": "^4.4.3", - "which": "^2.0.2" - }, - "engines": { - "node": ">=12" + "release-it": "^14.11.7", + "typescript": "^4.4.4" }, "repository": { "type": "git", - "url": "git://github.com/NodeRedis/node-redis.git" + "url": "git://github.com/redis/node-redis.git" }, "bugs": { - "url": "https://github.com/NodeRedis/node-redis/issues" + "url": "https://github.com/redis/node-redis/issues" }, - "homepage": "https://github.com/NodeRedis/node-redis" + "homepage": "https://github.com/redis/node-redis" } diff --git a/packages/client/.eslintrc.json b/packages/client/.eslintrc.json new file mode 100644 index 00000000000..4536bc31338 --- /dev/null +++ b/packages/client/.eslintrc.json @@ -0,0 +1,15 @@ +{ + "root": true, + "parser": "@typescript-eslint/parser", + "plugins": [ + "@typescript-eslint" + ], + "extends": [ + "eslint:recommended", + "plugin:@typescript-eslint/eslint-recommended", + "plugin:@typescript-eslint/recommended" + ], + "rules": { + "semi": [2, "always"] + } + } diff --git a/packages/client/.gitignore b/packages/client/.gitignore new file mode 100644 index 00000000000..2d7ddbc106e --- /dev/null +++ b/packages/client/.gitignore @@ -0,0 +1 @@ +documentation/ diff --git a/packages/client/.npmignore b/packages/client/.npmignore new file mode 100644 index 00000000000..d064e1d0db6 --- /dev/null +++ b/packages/client/.npmignore @@ -0,0 +1,10 @@ +.nyc_output/ +coverage/ +documentation/ +lib/ +.eslintrc.json +.nycrc.json +.release-it.json +dump.rdb +index.ts +tsconfig.json diff --git a/packages/client/.nycrc.json b/packages/client/.nycrc.json new file mode 100644 index 00000000000..dd42463d9cb --- /dev/null +++ b/packages/client/.nycrc.json @@ -0,0 +1,4 @@ +{ + "extends": "@istanbuljs/nyc-config-typescript", + "exclude": ["**/*.spec.ts", "lib/test-utils.ts", "examples/*"] +} diff --git a/packages/client/.release-it.json b/packages/client/.release-it.json new file mode 100644 index 00000000000..035124348ca --- /dev/null +++ b/packages/client/.release-it.json @@ -0,0 +1,10 @@ +{ + "git": { + "tagName": "client@${version}", + "commitMessage": "Release ${tagName}", + "tagAnnotation": "Release ${tagName}" + }, + "npm": { + "publishArgs": ["--access", "public"] + } +} diff --git a/CHANGELOG.md b/packages/client/CHANGELOG.md similarity index 98% rename from CHANGELOG.md rename to packages/client/CHANGELOG.md index d0095714010..21b7177e8b6 100644 --- a/CHANGELOG.md +++ b/packages/client/CHANGELOG.md @@ -2,7 +2,7 @@ ## v4.0.0 -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. +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. ### Breaking Changes @@ -17,12 +17,35 @@ 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](./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 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 the ability to extend Node Redis with Redis Module commands +## v3.1.2 + +### Fixes + +- Exclude unnecessary files from tarball + +## v3.1.1 + +### Enhancements + +- Upgrade node and dependencies + +### Fixes + +- Fix a potential exponential regex in monitor mode + +## v3.1.0 - 31 Mar, 2021 + +### Enhancements + +- Upgrade node and dependencies and redis-commands to support Redis 6 +- Add support for Redis 6 `auth pass [user]` + ## v3.0.0 - 09 Feb, 2020 This version is mainly a release to distribute all the unreleased changes on master since 2017 and additionally removes diff --git a/packages/client/README.md b/packages/client/README.md new file mode 100644 index 00000000000..37c326fc42e --- /dev/null +++ b/packages/client/README.md @@ -0,0 +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. diff --git a/packages/client/index.ts b/packages/client/index.ts new file mode 100644 index 00000000000..408cbe3b996 --- /dev/null +++ b/packages/client/index.ts @@ -0,0 +1,10 @@ +import RedisClient from './lib/client'; +import RedisCluster from './lib/cluster'; + +export const createClient = RedisClient.create; + +export const commandOptions = RedisClient.commandOptions; + +export const createCluster = RedisCluster.create; + +export { defineScript } from './lib/lua-script'; diff --git a/lib/client/commands-queue.ts b/packages/client/lib/client/commands-queue.ts similarity index 92% rename from lib/client/commands-queue.ts rename to packages/client/lib/client/commands-queue.ts index 791c7638bad..4fcae1e8b63 100644 --- a/lib/client/commands-queue.ts +++ b/packages/client/lib/client/commands-queue.ts @@ -1,25 +1,29 @@ -import LinkedList from 'yallist'; -import RedisParser from 'redis-parser'; +import * as LinkedList from 'yallist'; import { AbortError } from '../errors'; -import { RedisCommandRawReply } from '../commands'; +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?: any; // TODO: `AbortSignal` type is incorrect + signal?: AbortSignal; } interface CommandWaitingToBeSent extends CommandWaitingForReply { - args: Array; + args: RedisCommandArguments; chainId?: symbol; abort?: { - signal: any; // TODO: `AbortSignal` type is incorrect + signal: AbortSignal; listener(): void; }; } interface CommandWaitingForReply { - resolve(reply?: any): void; + resolve(reply?: unknown): void; reject(err: Error): void; channelsCounter?: number; bufferMode?: boolean; @@ -107,7 +111,7 @@ export default class RedisCommandsQueue { this.#maxLength = maxLength; } - addCommand(args: Array, options?: QueueCommandOptions, bufferMode?: boolean): Promise { + addCommand(args: RedisCommandArguments, options?: QueueCommandOptions, bufferMode?: boolean): Promise { if (this.#pubSubState.subscribing || this.#pubSubState.subscribed) { return Promise.reject(new Error('Cannot send commands in PubSub mode')); } else if (this.#maxLength && this.#waitingToBeSent.length + this.#waitingForReply.length >= this.#maxLength) { @@ -135,7 +139,8 @@ export default class RedisCommandsQueue { signal: options.signal, listener }; - options.signal.addEventListener('abort', listener, { + // AbortSignal type is incorrent + (options.signal as any).addEventListener('abort', listener, { once: true }); } @@ -246,7 +251,7 @@ export default class RedisCommandsQueue { ]); } - getCommandToSend(): Array | undefined { + getCommandToSend(): RedisCommandArguments | undefined { const toSend = this.#waitingToBeSent.shift(); if (toSend) { diff --git a/lib/client/commands.ts b/packages/client/lib/client/commands.ts similarity index 99% rename from lib/client/commands.ts rename to packages/client/lib/client/commands.ts index c34f34be4fa..de901152f11 100644 --- a/lib/client/commands.ts +++ b/packages/client/lib/client/commands.ts @@ -229,5 +229,5 @@ export default { UNWATCH, unwatch: UNWATCH, WAIT, - wait: WAIT, + wait: WAIT }; diff --git a/lib/client/index.spec.ts b/packages/client/lib/client/index.spec.ts similarity index 51% rename from lib/client/index.spec.ts rename to packages/client/lib/client/index.spec.ts index e98814d0582..3f0bca45e27 100644 --- a/lib/client/index.spec.ts +++ b/packages/client/lib/client/index.spec.ts @@ -1,11 +1,12 @@ -import { strict as assert, AssertionError } from 'assert'; -import { once } from 'events'; -import { itWithClient, TEST_REDIS_SERVERS, TestRedisServers, waitTillBeenCalled, isRedisVersionGreaterThan } from '../test-utils'; -import RedisClient from '.'; -import { AbortError, ClientClosedError, ConnectionTimeoutError, WatchError } from '../errors'; +import { strict as assert } from 'assert'; +import testUtils, { GLOBAL, waitTillBeenCalled } from '../test-utils'; +import RedisClient, { ClientLegacyCommandArguments, RedisClientType } from '.'; +import { RedisClientMultiCommandType } from './multi-command'; +import { RedisCommandArguments, RedisCommandRawReply, RedisModules, RedisScripts } from '../commands'; +import { AbortError, ClientClosedError, ConnectionTimeoutError, DisconnectsClientError, SocketClosedUnexpectedlyError, WatchError } from '../errors'; import { defineScript } from '../lua-script'; import { spy } from 'sinon'; -import { RedisNetSocketOptions } from '../client/socket'; +import { once } from 'events'; export const SQUARE_SCRIPT = defineScript({ NUMBER_OF_KEYS: 0, @@ -75,204 +76,242 @@ describe('Client', () => { } ); }); - - it('createClient with url', async () => { - const client = RedisClient.create({ - url: `redis://localhost:${(TEST_REDIS_SERVERS[TestRedisServers.OPEN].socket as RedisNetSocketOptions)!.port!.toString()}/1` - }); - - await client.connect(); - - try { - assert.equal( - await client.ping(), - 'PONG' - ); - } finally { - await client.disconnect(); - } - }) }); describe('authentication', () => { - itWithClient(TestRedisServers.PASSWORD, 'Client should be authenticated', async client => { + testUtils.testWithClient('Client should be authenticated', async client => { assert.equal( await client.ping(), 'PONG' ); - }); - - it('should not retry connecting if failed due to wrong auth', async () => { - const client = RedisClient.create({ - ...TEST_REDIS_SERVERS[TestRedisServers.PASSWORD], - password: 'wrongpassword' - }); + }, GLOBAL.SERVERS.PASSWORD); + + testUtils.testWithClient('should not retry connecting if failed due to wrong auth', async client => { + let message; + if (testUtils.isVersionGreaterThan([6, 2])) { + message = 'WRONGPASS invalid username-password pair or user is disabled.'; + } else if (testUtils.isVersionGreaterThan([6])) { + message = 'WRONGPASS invalid username-password pair'; + } else { + message = 'ERR invalid password'; + } await assert.rejects( client.connect(), - { - message: isRedisVersionGreaterThan([6]) ? - 'WRONGPASS invalid username-password pair or user is disabled.' : - 'ERR invalid password' - } + { message } ); assert.equal(client.isOpen, false); + }, { + ...GLOBAL.SERVERS.PASSWORD, + clientOptions: { + password: 'wrongpassword' + }, + disableClientSetup: true }); - }); - describe('legacyMode', () => { - const client = RedisClient.create({ - ...TEST_REDIS_SERVERS[TestRedisServers.OPEN], - scripts: { - square: SQUARE_SCRIPT + testUtils.testWithClient('should execute AUTH before SELECT', async client => { + assert.equal( + (await client.clientInfo()).db, + 2 + ); + }, { + ...GLOBAL.SERVERS.PASSWORD, + clientOptions: { + ...GLOBAL.SERVERS.PASSWORD.clientOptions, + database: 2 }, - legacyMode: true + minimumDockerVersion: [6, 2] }); + }); - before(() => client.connect()); - afterEach(() => client.v4.flushAll()); - after(() => client.disconnect()); - - it('client.sendCommand should call the callback', done => { - (client as any).sendCommand('PING', (err?: Error, reply?: string) => { - if (err) { - return done(err); - } + describe('legacyMode', () => { + function sendCommandAsync(client: RedisClientType, args: RedisCommandArguments): Promise { + return new Promise((resolve, reject) => { + (client as any).sendCommand(args, (err: Error | undefined, reply: RedisCommandRawReply) => { + if (err) return reject(err); - try { - assert.equal(reply, 'PONG'); - done(); - } catch (err) { - done(err); - } + resolve(reply); + }); }); + } + + testUtils.testWithClient('client.sendCommand should call the callback', async client => { + assert.equal( + await sendCommandAsync(client, ['PING']), + 'PONG' + ); + }, { + ...GLOBAL.SERVERS.OPEN, + clientOptions: { + legacyMode: true + } }); - it('client.sendCommand should work without callback', async () => { - (client as any).sendCommand('PING'); + testUtils.testWithClient('client.sendCommand should work without callback', async client => { + client.sendCommand(['PING']); await client.v4.ping(); // make sure the first command was replied + }, { + ...GLOBAL.SERVERS.OPEN, + clientOptions: { + legacyMode: true + } }); - it('client.v4.sendCommand should return a promise', async () => { + testUtils.testWithClient('client.v4.sendCommand should return a promise', async client => { assert.equal( await client.v4.sendCommand(['PING']), 'PONG' ); + }, { + ...GLOBAL.SERVERS.OPEN, + clientOptions: { + legacyMode: true + } }); - it('client.{command} should accept vardict arguments', done => { - (client as any).set('a', 'b', (err?: Error, reply?: string) => { - if (err) { - return done(err); - } + function setAsync(client: RedisClientType, ...args: ClientLegacyCommandArguments): Promise { + return new Promise((resolve, reject) => { + (client as any).set(...args, (err: Error | undefined, reply: RedisCommandRawReply) => { + if (err) return reject(err); - try { - assert.equal(reply, 'OK'); - done(); - } catch (err) { - done(err); - } + resolve(reply); + }); }); - }); - - it('client.{command} should accept arguments array', done => { - (client as any).set(['a', 'b'], (err?: Error, reply?: string) => { - if (err) { - return done(err); - } + } - try { - assert.equal(reply, 'OK'); - done(); - } catch (err) { - done(err); - } - }); + testUtils.testWithClient('client.{command} should accept vardict arguments', async client => { + assert.equal( + await setAsync(client, 'a', 'b'), + 'OK' + ); + }, { + ...GLOBAL.SERVERS.OPEN, + clientOptions: { + legacyMode: true + } }); - it('client.{command} should accept mix of strings and array of strings', done => { - (client as any).set(['a'], 'b', ['XX'], (err?: Error, reply?: string) => { - if (err) { - return done(err); - } + testUtils.testWithClient('client.{command} should accept arguments array', async client => { + assert.equal( + await setAsync(client, ['a', 'b']), + 'OK' + ); + }, { + ...GLOBAL.SERVERS.OPEN, + clientOptions: { + legacyMode: true + } + }); - try { - assert.equal(reply, null); - done(); - } catch (err) { - done(err); - } - }); + testUtils.testWithClient('client.{command} should accept mix of arrays and arguments', async client => { + assert.equal( + await setAsync(client, ['a'], 'b', ['EX', 1]), + 'OK' + ); + }, { + ...GLOBAL.SERVERS.OPEN, + clientOptions: { + legacyMode: true + } }); - it('client.multi.ping.exec should call the callback', done => { - (client as any).multi() - .ping() - .exec((err?: Error, reply?: string) => { - if (err) { - return done(err); - } + function multiExecAsync(multi: RedisClientMultiCommandType): Promise> { + return new Promise((resolve, reject) => { + (multi as any).exec((err: Error | undefined, replies: Array) => { + if (err) return reject(err); - try { - assert.deepEqual(reply, ['PONG']); - done(); - } catch (err) { - done(err); - } + resolve(replies); }); + }); + } + + testUtils.testWithClient('client.multi.ping.exec should call the callback', async client => { + assert.deepEqual( + await multiExecAsync( + client.multi().ping() + ), + ['PONG'] + ); + }, { + ...GLOBAL.SERVERS.OPEN, + clientOptions: { + legacyMode: true + } }); - it('client.multi.ping.exec should work without callback', async () => { - (client as any).multi() + testUtils.testWithClient('client.multi.ping.exec should call the callback', async client => { + client.multi() .ping() .exec(); await client.v4.ping(); // make sure the first command was replied + }, { + ...GLOBAL.SERVERS.OPEN, + clientOptions: { + legacyMode: true + } }); - it('client.multi.ping.v4.ping.v4.exec should return a promise', async () => { + testUtils.testWithClient('client.multi.ping.v4.ping.v4.exec should return a promise', async client => { assert.deepEqual( - await ((client as any).multi() + await client.multi() .ping() .v4.ping() - .v4.exec()), + .v4.exec(), ['PONG', 'PONG'] ); + }, { + ...GLOBAL.SERVERS.OPEN, + clientOptions: { + legacyMode: true + } }); - it('client.{script} should return a promise', async () => { - assert.equal(await client.square(2), 4); + testUtils.testWithClient('client.{script} should return a promise', async client => { + assert.equal( + await client.square(2), + 4 + ); + }, { + ...GLOBAL.SERVERS.OPEN, + clientOptions: { + legacyMode: true, + scripts: { + square: SQUARE_SCRIPT + } + } }); }); describe('events', () => { - it('connect, ready, end', async () => { - const client = RedisClient.create(TEST_REDIS_SERVERS[TestRedisServers.OPEN]); - + testUtils.testWithClient('connect, ready, end', async client => { await Promise.all([ - client.connect(), once(client, 'connect'), - once(client, 'ready') + once(client, 'ready'), + client.connect() ]); await Promise.all([ - client.disconnect(), - once(client, 'end') + once(client, 'end'), + client.disconnect() ]); + }, { + ...GLOBAL.SERVERS.OPEN, + disableClientSetup: true }); }); describe('sendCommand', () => { - itWithClient(TestRedisServers.OPEN, 'PING', async client => { + testUtils.testWithClient('PING', async client => { assert.equal(await client.sendCommand(['PING']), 'PONG'); - }); + }, GLOBAL.SERVERS.OPEN); - itWithClient(TestRedisServers.OPEN, 'bufferMode', async client => { + testUtils.testWithClient('bufferMode', async client => { assert.deepEqual( await client.sendCommand(['PING'], undefined, true), Buffer.from('PONG') ); - }); + }, GLOBAL.SERVERS.OPEN); describe('AbortController', () => { before(function () { @@ -281,13 +320,13 @@ describe('Client', () => { } }); - itWithClient(TestRedisServers.OPEN, 'success', async client => { + testUtils.testWithClient('success', async client => { await client.sendCommand(['PING'], { signal: new AbortController().signal }); - }); + }, GLOBAL.SERVERS.OPEN); - itWithClient(TestRedisServers.OPEN, 'AbortError', client => { + testUtils.testWithClient('AbortError', client => { const controller = new AbortController(); controller.abort(); @@ -297,12 +336,12 @@ describe('Client', () => { }), AbortError ); - }); + }, GLOBAL.SERVERS.OPEN); }); }); describe('multi', () => { - itWithClient(TestRedisServers.OPEN, 'simple', async client => { + testUtils.testWithClient('simple', async client => { assert.deepEqual( await client.multi() .ping() @@ -311,44 +350,35 @@ describe('Client', () => { .exec(), ['PONG', 'OK', 'value'] ); - }); - - itWithClient(TestRedisServers.OPEN, 'should reject the whole chain on error', client => { - client.on('error', () => { - // ignore errors - }); + }, GLOBAL.SERVERS.OPEN); + testUtils.testWithClient('should reject the whole chain on error', client => { return assert.rejects( client.multi() .ping() - .addCommand(['DEBUG', 'RESTART']) + .addCommand(['INVALID COMMAND']) .ping() .exec() ); - }); + }, GLOBAL.SERVERS.OPEN); - it('with script', async () => { - const client = RedisClient.create({ + testUtils.testWithClient('with script', async client => { + assert.deepEqual( + await client.multi() + .square(2) + .exec(), + [4] + ); + }, { + ...GLOBAL.SERVERS.OPEN, + clientOptions: { scripts: { square: SQUARE_SCRIPT } - }); - - await client.connect(); - - try { - assert.deepEqual( - await client.multi() - .square(2) - .exec(), - [4] - ); - } finally { - await client.disconnect(); } }); - itWithClient(TestRedisServers.OPEN, 'WatchError', async client => { + testUtils.testWithClient('WatchError', async client => { await client.watch('key'); await client.set( @@ -365,39 +395,40 @@ describe('Client', () => { .exec(), WatchError ); - }); + }, GLOBAL.SERVERS.OPEN); - itWithClient(TestRedisServers.OPEN, 'execAsPipeline', async client => { + testUtils.testWithClient('execAsPipeline', async client => { assert.deepEqual( await client.multi() .ping() .exec(true), ['PONG'] ); - }); + }, GLOBAL.SERVERS.OPEN); }); - it('scripts', async () => { - const client = RedisClient.create({ + testUtils.testWithClient('scripts', async client => { + assert.equal( + await client.square(2), + 4 + ); + }, { + ...GLOBAL.SERVERS.OPEN, + clientOptions: { scripts: { square: SQUARE_SCRIPT } - }); - - await client.connect(); - - try { - assert.equal( - await client.square(2), - 4 - ); - } finally { - await client.disconnect(); } }); - it('modules', async () => { - const client = RedisClient.create({ + testUtils.testWithClient('modules', async client => { + assert.equal( + await client.module.echo('message'), + 'message' + ); + }, { + ...GLOBAL.SERVERS.OPEN, + clientOptions: { modules: { module: { echo: { @@ -410,21 +441,10 @@ describe('Client', () => { } } } - }); - - await client.connect(); - - try { - assert.equal( - await client.module.echo('message'), - 'message' - ); - } finally { - await client.disconnect(); } }); - itWithClient(TestRedisServers.OPEN, 'executeIsolated', async client => { + testUtils.testWithClient('executeIsolated', async client => { await client.sendCommand(['CLIENT', 'SETNAME', 'client']); assert.equal( @@ -433,35 +453,35 @@ describe('Client', () => { ), null ); - }); - - itWithClient(TestRedisServers.OPEN, 'should reconnect after DEBUG RESTART', async client => { - client.on('error', () => { - // ignore errors - }); - - await client.sendCommand(['CLIENT', 'SETNAME', 'client']); - await assert.rejects(client.sendCommand(['DEBUG', 'RESTART'])); - assert.ok(await client.sendCommand(['CLIENT', 'GETNAME']) === null); - }); + }, GLOBAL.SERVERS.OPEN); + + async function killClient(client: RedisClientType): Promise { + const onceErrorPromise = once(client, 'error'); + await client.sendCommand(['QUIT']); + await Promise.all([ + onceErrorPromise, + assert.rejects(client.ping(), SocketClosedUnexpectedlyError) + ]); + } - itWithClient(TestRedisServers.OPEN, 'should SELECT db after reconnection', async client => { - client.on('error', () => { - // ignore errors - }); + testUtils.testWithClient('should reconnect when socket disconnects', async client => { + await killClient(client); + await assert.doesNotReject(client.ping()); + }, GLOBAL.SERVERS.OPEN); + testUtils.testWithClient('should remember selected db', async client => { await client.select(1); - await assert.rejects(client.sendCommand(['DEBUG', 'RESTART'])); + await killClient(client); assert.equal( (await client.clientInfo()).db, 1 ); }, { - // because of CLIENT INFO - minimumRedisVersion: [6, 2] + ...GLOBAL.SERVERS.OPEN, + minimumDockerVersion: [6, 2] // CLIENT INFO }); - itWithClient(TestRedisServers.OPEN, 'scanIterator', async client => { + testUtils.testWithClient('scanIterator', async client => { const promises = [], keys = new Set(); for (let i = 0; i < 100; i++) { @@ -478,9 +498,9 @@ describe('Client', () => { } assert.deepEqual(keys, results); - }); + }, GLOBAL.SERVERS.OPEN); - itWithClient(TestRedisServers.OPEN, 'hScanIterator', async client => { + testUtils.testWithClient('hScanIterator', async client => { const hash: Record = {}; for (let i = 0; i < 100; i++) { hash[i.toString()] = i.toString(); @@ -494,9 +514,9 @@ describe('Client', () => { } assert.deepEqual(hash, results); - }); + }, GLOBAL.SERVERS.OPEN); - itWithClient(TestRedisServers.OPEN, 'sScanIterator', async client => { + testUtils.testWithClient('sScanIterator', async client => { const members = new Set(); for (let i = 0; i < 100; i++) { members.add(i.toString()); @@ -510,9 +530,9 @@ describe('Client', () => { } assert.deepEqual(members, results); - }); + }, GLOBAL.SERVERS.OPEN); - itWithClient(TestRedisServers.OPEN, 'zScanIterator', async client => { + testUtils.testWithClient('zScanIterator', async client => { const members = []; for (let i = 0; i < 100; i++) { members.push({ @@ -538,9 +558,9 @@ describe('Client', () => { [...map.entries()].sort(sort), members.map(member => [member.value, member.score]).sort(sort) ); - }); + }, GLOBAL.SERVERS.OPEN); - itWithClient(TestRedisServers.OPEN, 'PubSub', async publisher => { + testUtils.testWithClient('PubSub', async publisher => { const subscriber = publisher.duplicate(); await subscriber.connect(); @@ -603,49 +623,59 @@ describe('Client', () => { } finally { await subscriber.disconnect(); } - }); + }, GLOBAL.SERVERS.OPEN); + + testUtils.testWithClient('ConnectionTimeoutError', async client => { + const promise = assert.rejects(client.connect(), ConnectionTimeoutError), + start = process.hrtime.bigint(); - it('ConnectionTimeoutError', async () => { - const client = RedisClient.create({ + while (process.hrtime.bigint() - start < 1_000_000) { + // block the event loop for 1ms, to make sure the connection will timeout + } + + await promise; + }, { + ...GLOBAL.SERVERS.OPEN, + clientOptions: { socket: { - ...TEST_REDIS_SERVERS[TestRedisServers.OPEN], connectTimeout: 1 } - }); + }, + disableClientSetup: true + }); - try { - const promise = assert.rejects(client.connect(), ConnectionTimeoutError), - start = process.hrtime.bigint(); + testUtils.testWithClient('client.quit', async client => { + await client.connect(); - // block the event loop for 1ms, to make sure the connection will timeout - while (process.hrtime.bigint() - start < 1_000_000) {} + const pingPromise = client.ping(), + quitPromise = client.quit(); + assert.equal(client.isOpen, false); - await promise; - } catch (err) { - if (err instanceof AssertionError) { - await client.disconnect(); - } + const [ping] = await Promise.all([ + pingPromise, + assert.doesNotReject(quitPromise), + assert.rejects(client.ping(), ClientClosedError) + ]); - throw err; - } + assert.equal(ping, 'PONG'); + }, { + ...GLOBAL.SERVERS.OPEN, + disableClientSetup: true }); - it('client.quit', async () => { - const client = RedisClient.create(TEST_REDIS_SERVERS[TestRedisServers.OPEN]); - + testUtils.testWithClient('client.disconnect', async client => { await client.connect(); - try { - const quitPromise = client.quit(); - assert.equal(client.isOpen, false); - await Promise.all([ - quitPromise, - assert.rejects(client.ping(), ClientClosedError) - ]); - } finally { - if (client.isOpen) { - await client.disconnect(); - } - } + const pingPromise = client.ping(), + disconnectPromise = client.disconnect(); + assert.equal(client.isOpen, false); + await Promise.all([ + assert.rejects(pingPromise, DisconnectsClientError), + assert.doesNotReject(disconnectPromise), + assert.rejects(client.ping(), ClientClosedError) + ]); + }, { + ...GLOBAL.SERVERS.OPEN, + disableClientSetup: true }); }); diff --git a/lib/client/index.ts b/packages/client/lib/client/index.ts similarity index 80% rename from lib/client/index.ts rename to packages/client/lib/client/index.ts index 1a5384f509f..8802631eda1 100644 --- a/lib/client/index.ts +++ b/packages/client/lib/client/index.ts @@ -4,14 +4,14 @@ import RedisSocket, { RedisSocketOptions, RedisNetSocketOptions, RedisTlsSocketO import RedisCommandsQueue, { PubSubListener, PubSubSubscribeCommands, PubSubUnsubscribeCommands, QueueCommandOptions } from './commands-queue'; import RedisClientMultiCommand, { RedisClientMultiCommandType } from './multi-command'; import { RedisMultiQueuedCommand } from '../multi-command'; -import EventEmitter from 'events'; +import { EventEmitter } from 'events'; import { CommandOptions, commandOptions, isCommandOptions } from '../command-options'; import { ScanOptions, ZMember } from '../commands/generic-transformers'; import { ScanCommandOptions } from '../commands/SCAN'; import { HScanTuple } from '../commands/HSCAN'; -import { encodeCommand, extendWithCommands, extendWithModulesAndScripts, transformCommandArguments, transformCommandReply } from '../commander'; +import { extendWithCommands, extendWithModulesAndScripts, LegacyCommandArguments, transformCommandArguments, transformCommandReply, transformLegacyCommandArguments } from '../commander'; import { Pool, Options as PoolOptions, createPool } from 'generic-pool'; -import { ClientClosedError } from '../errors'; +import { ClientClosedError, DisconnectsClientError } from '../errors'; import { URL } from 'url'; export interface RedisClientOptions extends RedisPlugins { @@ -34,16 +34,16 @@ type WithCommands = { }; export type WithModules = { - [P in keyof M]: { + [P in keyof M as M[P] extends never ? never : P]: { [C in keyof M[P]]: RedisClientCommandSignature; }; }; export type WithScripts = { - [P in keyof S]: RedisClientCommandSignature; + [P in keyof S as S[P] extends never ? never : P]: RedisClientCommandSignature; }; -export type RedisClientType = +export type RedisClientType, S extends RedisScripts = Record> = RedisClient & WithCommands & WithModules & WithScripts; export type InstantiableRedisClient = @@ -53,12 +53,15 @@ export interface ClientCommandOptions extends QueueCommandOptions { isolated?: boolean; } +type ClientLegacyCallback = (err: Error | null, reply?: RedisCommandRawReply) => void; + +export type ClientLegacyCommandArguments = LegacyCommandArguments | [...LegacyCommandArguments, ClientLegacyCallback]; export default class RedisClient extends EventEmitter { static commandOptions(options: ClientCommandOptions): CommandOptions { return commandOptions(options); } - static extend(plugins?: RedisPlugins): InstantiableRedisClient { + static extend, S extends RedisScripts = Record>(plugins?: RedisPlugins): InstantiableRedisClient { const Client = extendWithModulesAndScripts({ BaseClass: RedisClient, modules: plugins?.modules, @@ -74,14 +77,14 @@ export default class RedisClient return Client; } - static create(options?: RedisClientOptions): RedisClientType { + static create, S extends RedisScripts = Record>(options?: RedisClientOptions): RedisClientType { return new (RedisClient.extend(options))(options); } - static parseURL(url: string): RedisClientOptions<{}, {}> { + static parseURL(url: string): RedisClientOptions, Record> { // https://www.iana.org/assignments/uri-schemes/prov/redis const { hostname, port, protocol, username, password, pathname } = new URL(url), - parsed: RedisClientOptions<{}, {}> = { + parsed: RedisClientOptions, Record> = { socket: { host: hostname } @@ -98,11 +101,11 @@ export default class RedisClient } if (username) { - parsed.username = username; + parsed.username = decodeURIComponent(username); } if (password) { - parsed.password = password; + parsed.password = decodeURIComponent(password); } if (pathname.length > 1) { @@ -177,28 +180,47 @@ export default class RedisClient #initiateSocket(): RedisSocket { const socketInitiator = async (): Promise => { - const v4Commands = this.#options?.legacyMode ? this.#v4 : this, - promises = []; + const promises = []; if (this.#selectedDB !== 0) { - promises.push(v4Commands.select(RedisClient.commandOptions({ asap: true }), this.#selectedDB)); + promises.push( + this.#queue.addCommand( + ['SELECT', this.#selectedDB.toString()], + { asap: true } + ) + ); } if (this.#options?.readonly) { - promises.push(v4Commands.readonly(RedisClient.commandOptions({ asap: true }))); + promises.push( + this.#queue.addCommand( + COMMANDS.READONLY.transformArguments(), + { asap: true } + ) + ); } if (this.#options?.username || this.#options?.password) { - promises.push(v4Commands.auth(RedisClient.commandOptions({ asap: true }), this.#options)); + promises.push( + this.#queue.addCommand( + COMMANDS.AUTH.transformArguments({ + username: this.#options.username, + password: this.#options.password ?? '' + }), + { asap: true } + ) + ); } const resubscribePromise = this.#queue.resubscribe(); if (resubscribePromise) { promises.push(resubscribePromise); - this.#tick(); } - await Promise.all(promises); + if (promises.length) { + this.#tick(true); + await Promise.all(promises); + } }; return new RedisSocket(socketInitiator, this.#options?.socket) @@ -213,6 +235,7 @@ export default class RedisClient this.#tick(); }) .on('reconnecting', () => this.emit('reconnecting')) + .on('drain', () => this.#tick()) .on('end', () => this.emit('end')); } @@ -224,11 +247,14 @@ export default class RedisClient if (!this.#options?.legacyMode) return; (this as any).#v4.sendCommand = this.#sendCommand.bind(this); - (this as any).sendCommand = (...args: Array): void => { - const callback = typeof args[args.length - 1] === 'function' ? args[args.length - 1] as Function : undefined, - actualArgs = !callback ? args : args.slice(0, -1); - this.#sendCommand(actualArgs.flat() as Array) - .then((reply: unknown) => { + (this as any).sendCommand = (...args: ClientLegacyCommandArguments): void => { + let callback: ClientLegacyCallback; + if (typeof args[args.length - 1] === 'function') { + callback = args.pop() as ClientLegacyCallback; + } + + this.#sendCommand(transformLegacyCommandArguments(args as LegacyCommandArguments)) + .then((reply: RedisCommandRawReply) => { if (!callback) return; // https://github.com/NodeRedis/node-redis#commands:~:text=minimal%20parsing @@ -297,9 +323,9 @@ export default class RedisClient } // using `#sendCommand` cause `sendCommand` is overwritten in legacy mode - async #sendCommand(args: RedisCommandArguments, options?: ClientCommandOptions, bufferMode?: boolean): Promise { + #sendCommand(args: RedisCommandArguments, options?: ClientCommandOptions, bufferMode?: boolean): Promise { if (!this.#socket.isOpen) { - throw new ClientClosedError(); + return Promise.reject(new ClientClosedError()); } if (options?.isolated) { @@ -313,7 +339,7 @@ export default class RedisClient const promise = this.#queue.addCommand(args, options, bufferMode); this.#tick(); - return await promise; + return promise; } async scriptsExecutor(script: RedisScript, args: Array): Promise> { @@ -400,33 +426,29 @@ export default class RedisClient QUIT(): Promise { return this.#socket.quit(() => { - const promise = this.#queue.addCommand(['QUIT']); + const quitPromise = this.#queue.addCommand(['QUIT']); this.#tick(); - return promise; + return Promise.all([ + quitPromise, + this.#destroyIsolationPool() + ]); }); } quit = this.QUIT; - #tick(): void { - if (!this.#socket.isSocketExists) { + #tick(force = false): void { + if (this.#socket.writableNeedDrain || (!force && !this.#socket.isReady)) { return; } this.#socket.cork(); - while (true) { + while (!this.#socket.writableNeedDrain) { const args = this.#queue.getCommandToSend(); if (args === undefined) break; - let writeResult; - for (const toWrite of encodeCommand(args)) { - writeResult = this.#socket.write(toWrite); - } - - if (!writeResult) { - break; - } + this.#socket.writeCommand(args); } } @@ -463,7 +485,7 @@ export default class RedisClient for (const key of reply.keys) { yield key; } - } while (cursor !== 0) + } while (cursor !== 0); } async* hScanIterator(key: string, options?: ScanOptions): AsyncIterable { @@ -474,7 +496,7 @@ export default class RedisClient for (const tuple of reply.tuples) { yield tuple; } - } while (cursor !== 0) + } while (cursor !== 0); } async* sScanIterator(key: string, options?: ScanOptions): AsyncIterable { @@ -485,7 +507,7 @@ export default class RedisClient for (const member of reply.members) { yield member; } - } while (cursor !== 0) + } while (cursor !== 0); } async* zScanIterator(key: string, options?: ScanOptions): AsyncIterable { @@ -496,15 +518,13 @@ export default class RedisClient for (const member of reply.members) { yield member; } - } while (cursor !== 0) + } while (cursor !== 0); } async disconnect(): Promise { - this.#queue.flushAll(new Error('Disconnecting')); - await Promise.all([ - this.#socket.disconnect(), - this.#destroyIsolationPool() - ]); + this.#queue.flushAll(new DisconnectsClientError()); + this.#socket.disconnect(); + await this.#destroyIsolationPool(); } async #destroyIsolationPool(): Promise { diff --git a/lib/client/multi-command.ts b/packages/client/lib/client/multi-command.ts similarity index 89% rename from lib/client/multi-command.ts rename to packages/client/lib/client/multi-command.ts index ba02c2b9aa8..9c19d3d0687 100644 --- a/lib/client/multi-command.ts +++ b/packages/client/lib/client/multi-command.ts @@ -1,7 +1,7 @@ import COMMANDS from './commands'; import { RedisCommand, RedisCommandArguments, RedisCommandRawReply, RedisModules, RedisPlugins, RedisScript, RedisScripts } from '../commands'; import RedisMultiCommand, { RedisMultiQueuedCommand } from '../multi-command'; -import { extendWithCommands, extendWithModulesAndScripts } from '../commander'; +import { extendWithCommands, extendWithModulesAndScripts, LegacyCommandArguments, transformLegacyCommandArguments } from '../commander'; type RedisClientMultiCommandSignature = (...args: Parameters) => RedisClientMultiCommandType; @@ -11,16 +11,16 @@ type WithCommands = { }; type WithModules = { - [P in keyof M]: { + [P in keyof M as M[P] extends never ? never : P]: { [C in keyof M[P]]: RedisClientMultiCommandSignature; }; }; type WithScripts = { - [P in keyof S]: RedisClientMultiCommandSignature + [P in keyof S as S[P] extends never ? never : P]: RedisClientMultiCommandSignature }; -export type RedisClientMultiCommandType = +export type RedisClientMultiCommandType, S extends RedisScripts = Record> = RedisClientMultiCommand & WithCommands & WithModules & WithScripts; export type RedisClientMultiExecutor = (queue: Array, chainId?: symbol) => Promise>; @@ -52,8 +52,8 @@ export default class RedisClientMultiCommand { #legacyMode(): void { this.v4.addCommand = this.addCommand.bind(this); - (this as any).addCommand = (...args: Array>): this => { - this.#multi.addCommand(args.flat()); + (this as any).addCommand = (...args: LegacyCommandArguments): this => { + this.#multi.addCommand(transformLegacyCommandArguments(args)); return this; }; this.v4.exec = this.exec.bind(this); diff --git a/lib/client/socket.spec.ts b/packages/client/lib/client/socket.spec.ts similarity index 98% rename from lib/client/socket.spec.ts rename to packages/client/lib/client/socket.spec.ts index 11c02d0885c..263320bbf72 100644 --- a/lib/client/socket.spec.ts +++ b/packages/client/lib/client/socket.spec.ts @@ -33,6 +33,6 @@ describe('Socket', () => { return assert.rejects(socket.connect(), { message: '50' }); - }) + }); }); }); diff --git a/lib/client/socket.ts b/packages/client/lib/client/socket.ts similarity index 76% rename from lib/client/socket.ts rename to packages/client/lib/client/socket.ts index ca48ad4d542..d42b42d64d6 100644 --- a/lib/client/socket.ts +++ b/packages/client/lib/client/socket.ts @@ -1,7 +1,9 @@ -import EventEmitter from 'events'; -import net from 'net'; -import tls from 'tls'; -import { ConnectionTimeoutError, ClientClosedError } from '../errors'; +import { EventEmitter } from 'events'; +import * as net from 'net'; +import * as tls from 'tls'; +import { encodeCommand } from '../commander'; +import { RedisCommandArguments } from '../commands'; +import { ConnectionTimeoutError, ClientClosedError, SocketClosedUnexpectedlyError } from '../errors'; import { promiseTimeout } from '../utils'; export interface RedisSocketCommonOptions { @@ -20,7 +22,7 @@ export interface RedisUnixSocketOptions extends RedisSocketCommonOptions { path: string; } -export interface RedisTlsSocketOptions extends RedisNetSocketOptions, tls.SecureContextOptions { +export interface RedisTlsSocketOptions extends RedisNetSocketOptions, tls.SecureContextOptions, tls.CommonConnectionOptions { tls: true; } @@ -72,8 +74,18 @@ export default class RedisSocket extends EventEmitter { return this.#isOpen; } - get isSocketExists(): boolean { - return !!this.#socket; + #isReady = false; + + get isReady(): boolean { + return this.#isReady; + } + + // `writable.writableNeedDrain` was added in v15.2.0 and therefore can't be used + // https://nodejs.org/api/stream.html#stream_writable_writableneeddrain + #writableNeedDrain = false; + + get writableNeedDrain(): boolean { + return this.#writableNeedDrain; } constructor(initiator?: RedisSocketInitiator, options?: RedisSocketOptions) { @@ -85,33 +97,39 @@ export default class RedisSocket extends EventEmitter { async connect(): Promise { if (this.#isOpen) { - throw new Error('Socket is connection/connecting'); + throw new Error('Socket already opened'); } - this.#isOpen = true; - - try { - await this.#connect(); - } catch (err) { - this.#isOpen = false; - throw err; - } + return this.#connect(); } async #connect(hadError?: boolean): Promise { + this.#isOpen = true; this.#socket = await this.#retryConnection(0, hadError); + this.#writableNeedDrain = false; + + if (!this.#isOpen) { + this.disconnect(); + return; + } + this.emit('connect'); if (this.#initiator) { try { await this.#initiator(); } catch (err) { - this.#socket.end(); + this.#socket.destroy(); this.#socket = undefined; + this.#isOpen = false; throw err; } + + if (!this.#isOpen) return; } + this.#isReady = true; + this.emit('ready'); } @@ -160,10 +178,13 @@ export default class RedisSocket extends EventEmitter { .once('error', (err: Error) => this.#onSocketError(err)) .once('close', hadError => { if (!hadError && this.#isOpen) { - this.#onSocketError(new Error('Socket closed unexpectedly')); + this.#onSocketError(new SocketClosedUnexpectedlyError()); } }) - .on('drain', () => this.emit('drain')) + .on('drain', () => { + this.#writableNeedDrain = false; + this.emit('drain'); + }) .on('data', (data: Buffer) => this.emit('data', data)); resolve(socket); @@ -186,30 +207,32 @@ export default class RedisSocket extends EventEmitter { } #onSocketError(err: Error): void { - this.#socket = undefined; + this.#isReady = false; this.emit('error', err); - this.#connect(true) - .catch(err => this.emit('error', err)); + this.#connect(true).catch(() => { + // the error was already emitted, silently ignore it + }); } - write(toWrite: string | Buffer): boolean { + writeCommand(args: RedisCommandArguments): void { if (!this.#socket) { throw new ClientClosedError(); } - return this.#socket.write(toWrite); + for (const toWrite of encodeCommand(args)) { + this.#writableNeedDrain = !this.#socket.write(toWrite); + } } - async disconnect(ignoreIsOpen = false): Promise { - if ((!ignoreIsOpen && !this.#isOpen) || !this.#socket) { + disconnect(): void { + if (!this.#socket) { throw new ClientClosedError(); } else { - this.#isOpen = false; + this.#isOpen = this.#isReady = false; } - this.#socket.end(); - await EventEmitter.once(this.#socket, 'end'); + this.#socket.destroy(); this.#socket = undefined; this.emit('end'); } @@ -220,15 +243,8 @@ export default class RedisSocket extends EventEmitter { } this.#isOpen = false; - - - try { - await fn(); - await this.disconnect(true); - } catch (err) { - this.#isOpen = true; - throw err; - } + await fn(); + this.disconnect(); } #isCorked = false; diff --git a/lib/cluster/cluster-slots.ts b/packages/client/lib/cluster/cluster-slots.ts similarity index 94% rename from lib/cluster/cluster-slots.ts rename to packages/client/lib/cluster/cluster-slots.ts index 63834d4b4ca..ff4c79b4d36 100644 --- a/lib/cluster/cluster-slots.ts +++ b/packages/client/lib/cluster/cluster-slots.ts @@ -1,9 +1,13 @@ -import calculateSlot from 'cluster-key-slot'; import RedisClient, { InstantiableRedisClient, RedisClientType } from '../client'; import { RedisClusterMasterNode, RedisClusterReplicaNode } from '../commands/CLUSTER_NODES'; import { RedisClusterClientOptions, RedisClusterOptions } from '.'; import { RedisModules, RedisScripts } from '../commands'; +// We need to use 'require', because it's not possible with Typescript to import +// function that are exported as 'module.exports = function`, without esModuleInterop +// set to true. +const calculateSlot = require('cluster-key-slot'); + export interface ClusterNode { id: string; client: RedisClientType; @@ -69,7 +73,7 @@ export default class RedisClusterSlots): Promise { - // Override this.#slots and add not existing clients to this.#clientByKey + // Override this.#slots and add not existing clients to this.#nodeByUrl const promises: Array> = [], clientsInUse = new Set(); for (const master of masters) { @@ -82,13 +86,13 @@ export default class RedisClusterSlots { + testUtils.testWithCluster('sendCommand', async cluster => { + await cluster.connect(); + + try { + await cluster.publish('channel', 'message'); + await cluster.set('a', 'b'); + await cluster.set('a{a}', 'bb'); + await cluster.set('aa', 'bb'); + await cluster.get('aa'); + await cluster.get('aa'); + await cluster.get('aa'); + await cluster.get('aa'); + } finally { + await cluster.disconnect(); + } + }, GLOBAL.CLUSTERS.OPEN); + + testUtils.testWithCluster('multi', async cluster => { + const key = 'key'; + assert.deepEqual( + await cluster.multi() + .set(key, 'value') + .get(key) + .exec(), + ['OK', 'value'] + ); + }, GLOBAL.CLUSTERS.OPEN); + + testUtils.testWithCluster('scripts', async cluster => { + assert.equal( + await cluster.square(2), + 4 + ); + }, { + ...GLOBAL.CLUSTERS.OPEN, + clusterConfiguration: { + scripts: { + square: SQUARE_SCRIPT + } + } + }); + + testUtils.testWithCluster('should handle live resharding', async cluster => { + const key = 'key', + value = 'value'; + await cluster.set(key, value); + + const slot = calculateSlot(key), + source = cluster.getSlotMaster(slot), + destination = cluster.getMasters().find(node => node.id !== source.id)!; + + await Promise.all([ + source.client.clusterSetSlot(slot, ClusterSlotStates.MIGRATING, destination.id), + destination.client.clusterSetSlot(slot, ClusterSlotStates.IMPORTING, destination.id) + ]); + + // should be able to get the key from the source node using "ASKING" + assert.equal( + await cluster.get(key), + value + ); + + await Promise.all([ + source.client.migrate( + '127.0.0.1', + (destination.client.options).socket.port, + key, + 0, + 10 + ) + ]); + + // should be able to get the key from the destination node using the "ASKING" command + assert.equal( + await cluster.get(key), + value + ); + + await Promise.all( + cluster.getMasters().map(({ client }) => { + return client.clusterSetSlot(slot, ClusterSlotStates.NODE, destination.id); + }) + ); + + // should handle "MOVED" errors + assert.equal( + await cluster.get(key), + value + ); + }, { + serverArguments: [], + numberOfNodes: 2 + }); +}); diff --git a/lib/cluster/index.ts b/packages/client/lib/cluster/index.ts similarity index 91% rename from lib/cluster/index.ts rename to packages/client/lib/cluster/index.ts index aeaabecae35..fcf6d4754bb 100644 --- a/lib/cluster/index.ts +++ b/packages/client/lib/cluster/index.ts @@ -1,5 +1,5 @@ import COMMANDS from './commands'; -import { RedisCommand, RedisCommandArguments, RedisCommandReply, RedisModules, RedisScript, RedisScripts } from '../commands'; +import { RedisCommand, RedisCommandArguments, RedisCommandReply, RedisModules, RedisPlugins, RedisScript, RedisScripts } from '../commands'; import { ClientCommandOptions, RedisClientCommandSignature, RedisClientOptions, RedisClientType, WithModules, WithScripts } from '../client'; import RedisClusterSlots, { ClusterNode } from './cluster-slots'; import { extendWithModulesAndScripts, transformCommandArguments, transformCommandReply, extendWithCommands } from '../commander'; @@ -7,14 +7,9 @@ import { EventEmitter } from 'events'; import RedisClusterMultiCommand, { RedisClusterMultiCommandType } from './multi-command'; import { RedisMultiQueuedCommand } from '../multi-command'; -export type RedisClusterClientOptions = Omit, 'modules' | 'scripts'>; +export type RedisClusterClientOptions = Omit, Record>, 'modules' | 'scripts'>; -export interface RedisClusterPlugins { - modules?: M; - scripts?: S; -} - -export interface RedisClusterOptions extends RedisClusterPlugins { +export interface RedisClusterOptions extends RedisPlugins { rootNodes: Array; defaults?: Partial; useReplicas?: boolean; @@ -25,10 +20,10 @@ type WithCommands = { [P in keyof typeof COMMANDS]: RedisClientCommandSignature<(typeof COMMANDS)[P]>; }; -export type RedisClusterType = +export type RedisClusterType, S extends RedisScripts = Record> = RedisCluster & WithCommands & WithModules & WithScripts; -export default class RedisCluster extends EventEmitter { +export default class RedisCluster, S extends RedisScripts = Record> extends EventEmitter { static extractFirstKey(command: RedisCommand, originalArgs: Array, redisArgs: RedisCommandArguments): string | Buffer | undefined { if (command.FIRST_KEY_INDEX === undefined) { return undefined; @@ -39,7 +34,7 @@ export default class RedisCluster(options?: RedisClusterOptions): RedisClusterType { + static create, S extends RedisScripts = Record>(options?: RedisClusterOptions): RedisClusterType { return new (extendWithModulesAndScripts({ BaseClass: RedisCluster, modules: options?.modules, diff --git a/lib/cluster/multi-command.ts b/packages/client/lib/cluster/multi-command.ts similarity index 94% rename from lib/cluster/multi-command.ts rename to packages/client/lib/cluster/multi-command.ts index 8ffd39a90e0..8a798ca9092 100644 --- a/lib/cluster/multi-command.ts +++ b/packages/client/lib/cluster/multi-command.ts @@ -12,16 +12,16 @@ type WithCommands = { }; type WithModules = { - [P in keyof M]: { + [P in keyof M as M[P] extends never ? never : P]: { [C in keyof M[P]]: RedisClusterMultiCommandSignature; }; }; type WithScripts = { - [P in keyof S]: RedisClusterMultiCommandSignature + [P in keyof S as S[P] extends never ? never : P]: RedisClusterMultiCommandSignature }; -export type RedisClusterMultiCommandType = +export type RedisClusterMultiCommandType, S extends RedisScripts = Record> = RedisClusterMultiCommand & WithCommands & WithModules & WithScripts; export type RedisClusterMultiExecutor = (queue: Array, firstKey?: string | Buffer, chainId?: symbol) => Promise>; diff --git a/lib/command-options.ts b/packages/client/lib/command-options.ts similarity index 100% rename from lib/command-options.ts rename to packages/client/lib/command-options.ts diff --git a/lib/commander.spec.ts b/packages/client/lib/commander.spec.ts similarity index 100% rename from lib/commander.spec.ts rename to packages/client/lib/commander.spec.ts diff --git a/lib/commander.ts b/packages/client/lib/commander.ts similarity index 88% rename from lib/commander.ts rename to packages/client/lib/commander.ts index 5871c2f235b..50d416f5b5c 100644 --- a/lib/commander.ts +++ b/packages/client/lib/commander.ts @@ -113,3 +113,18 @@ export function transformCommandReply( return command.transformReply(rawReply, preserved); } + +export type LegacyCommandArguments = Array; + +export function transformLegacyCommandArguments(args: LegacyCommandArguments, flat: RedisCommandArguments = []): RedisCommandArguments { + for (const arg of args) { + if (Array.isArray(arg)) { + transformLegacyCommandArguments(arg, flat); + continue; + } + + flat.push(typeof arg === 'number' ? arg.toString() : arg); + } + + return flat; +} diff --git a/lib/commands/ACL_CAT.spec.ts b/packages/client/lib/commands/ACL_CAT.spec.ts similarity index 82% rename from lib/commands/ACL_CAT.spec.ts rename to packages/client/lib/commands/ACL_CAT.spec.ts index 77ed1cb7a07..521871a1c6b 100644 --- a/lib/commands/ACL_CAT.spec.ts +++ b/packages/client/lib/commands/ACL_CAT.spec.ts @@ -1,9 +1,9 @@ import { strict as assert } from 'assert'; -import { describeHandleMinimumRedisVersion } from '../test-utils'; +import testUtils from '../test-utils'; import { transformArguments } from './ACL_CAT'; describe('ACL CAT', () => { - describeHandleMinimumRedisVersion([6]); + testUtils.isVersionGreaterThanHook([6]); describe('transformArguments', () => { it('simple', () => { diff --git a/lib/commands/ACL_CAT.ts b/packages/client/lib/commands/ACL_CAT.ts similarity index 100% rename from lib/commands/ACL_CAT.ts rename to packages/client/lib/commands/ACL_CAT.ts diff --git a/lib/commands/ACL_DELUSER.spec.ts b/packages/client/lib/commands/ACL_DELUSER.spec.ts similarity index 73% rename from lib/commands/ACL_DELUSER.spec.ts rename to packages/client/lib/commands/ACL_DELUSER.spec.ts index c64e8db1965..5c5ea2fa2a3 100644 --- a/lib/commands/ACL_DELUSER.spec.ts +++ b/packages/client/lib/commands/ACL_DELUSER.spec.ts @@ -1,9 +1,9 @@ import { strict as assert } from 'assert'; -import { describeHandleMinimumRedisVersion, itWithClient, TestRedisServers } from '../test-utils'; +import testUtils, { GLOBAL } from '../test-utils'; import { transformArguments } from './ACL_DELUSER'; describe('ACL DELUSER', () => { - describeHandleMinimumRedisVersion([6]); + testUtils.isVersionGreaterThanHook([6]); describe('transformArguments', () => { it('string', () => { @@ -21,10 +21,10 @@ describe('ACL DELUSER', () => { }); }); - itWithClient(TestRedisServers.OPEN, 'client.aclDelUser', async client => { + testUtils.testWithClient('client.aclDelUser', async client => { assert.equal( await client.aclDelUser('dosenotexists'), 0 ); - }); + }, GLOBAL.SERVERS.OPEN); }); diff --git a/lib/commands/ACL_DELUSER.ts b/packages/client/lib/commands/ACL_DELUSER.ts similarity index 100% rename from lib/commands/ACL_DELUSER.ts rename to packages/client/lib/commands/ACL_DELUSER.ts diff --git a/lib/commands/ACL_GENPASS.spec.ts b/packages/client/lib/commands/ACL_GENPASS.spec.ts similarity index 82% rename from lib/commands/ACL_GENPASS.spec.ts rename to packages/client/lib/commands/ACL_GENPASS.spec.ts index a288a4f7142..3b2a022f972 100644 --- a/lib/commands/ACL_GENPASS.spec.ts +++ b/packages/client/lib/commands/ACL_GENPASS.spec.ts @@ -1,9 +1,9 @@ import { strict as assert } from 'assert'; -import { describeHandleMinimumRedisVersion } from '../test-utils'; +import testUtils from '../test-utils'; import { transformArguments } from './ACL_GENPASS'; describe('ACL GENPASS', () => { - describeHandleMinimumRedisVersion([6]); + testUtils.isVersionGreaterThanHook([6]); describe('transformArguments', () => { it('simple', () => { diff --git a/lib/commands/ACL_GENPASS.ts b/packages/client/lib/commands/ACL_GENPASS.ts similarity index 100% rename from lib/commands/ACL_GENPASS.ts rename to packages/client/lib/commands/ACL_GENPASS.ts diff --git a/packages/client/lib/commands/ACL_GETUSER.spec.ts b/packages/client/lib/commands/ACL_GETUSER.spec.ts new file mode 100644 index 00000000000..fcc10768e61 --- /dev/null +++ b/packages/client/lib/commands/ACL_GETUSER.spec.ts @@ -0,0 +1,32 @@ +import { strict as assert } from 'assert'; +import testUtils, { GLOBAL } from '../test-utils'; +import { transformArguments } from './ACL_GETUSER'; + +describe('ACL GETUSER', () => { + testUtils.isVersionGreaterThanHook([6]); + + it('transformArguments', () => { + assert.deepEqual( + transformArguments('username'), + ['ACL', 'GETUSER', 'username'] + ); + }); + + testUtils.testWithClient('client.aclGetUser', async client => { + assert.deepEqual( + await client.aclGetUser('default'), + { + passwords: [], + commands: '+@all', + keys: ['*'], + ...(testUtils.isVersionGreaterThan([6, 2]) ? { + flags: ['on', 'allkeys', 'allchannels', 'allcommands', 'nopass'], + channels: ['*'] + } : { + flags: ['on', 'allkeys', 'allcommands', 'nopass'], + channels: undefined + }) + } + ); + }, GLOBAL.SERVERS.OPEN); +}); diff --git a/lib/commands/ACL_GETUSER.ts b/packages/client/lib/commands/ACL_GETUSER.ts similarity index 100% rename from lib/commands/ACL_GETUSER.ts rename to packages/client/lib/commands/ACL_GETUSER.ts diff --git a/lib/commands/ACL_LIST.spec.ts b/packages/client/lib/commands/ACL_LIST.spec.ts similarity index 70% rename from lib/commands/ACL_LIST.spec.ts rename to packages/client/lib/commands/ACL_LIST.spec.ts index ab6bae762f1..9f9156db7a2 100644 --- a/lib/commands/ACL_LIST.spec.ts +++ b/packages/client/lib/commands/ACL_LIST.spec.ts @@ -1,9 +1,9 @@ import { strict as assert } from 'assert'; -import { describeHandleMinimumRedisVersion } from '../test-utils'; +import testUtils from '../test-utils'; import { transformArguments } from './ACL_LIST'; describe('ACL LIST', () => { - describeHandleMinimumRedisVersion([6]); + testUtils.isVersionGreaterThanHook([6]); it('transformArguments', () => { assert.deepEqual( diff --git a/lib/commands/ACL_LIST.ts b/packages/client/lib/commands/ACL_LIST.ts similarity index 100% rename from lib/commands/ACL_LIST.ts rename to packages/client/lib/commands/ACL_LIST.ts diff --git a/lib/commands/ACL_LOAD.spec.ts b/packages/client/lib/commands/ACL_LOAD.spec.ts similarity index 70% rename from lib/commands/ACL_LOAD.spec.ts rename to packages/client/lib/commands/ACL_LOAD.spec.ts index d173d7f1355..703d5eeb252 100644 --- a/lib/commands/ACL_LOAD.spec.ts +++ b/packages/client/lib/commands/ACL_LOAD.spec.ts @@ -1,9 +1,9 @@ import { strict as assert } from 'assert'; -import { describeHandleMinimumRedisVersion } from '../test-utils'; +import testUtils from '../test-utils'; import { transformArguments } from './ACL_SAVE'; describe('ACL SAVE', () => { - describeHandleMinimumRedisVersion([6]); + testUtils.isVersionGreaterThanHook([6]); it('transformArguments', () => { assert.deepEqual( diff --git a/lib/commands/ACL_LOAD.ts b/packages/client/lib/commands/ACL_LOAD.ts similarity index 100% rename from lib/commands/ACL_LOAD.ts rename to packages/client/lib/commands/ACL_LOAD.ts diff --git a/lib/commands/ACL_LOG.spec.ts b/packages/client/lib/commands/ACL_LOG.spec.ts similarity index 93% rename from lib/commands/ACL_LOG.spec.ts rename to packages/client/lib/commands/ACL_LOG.spec.ts index 3ce76ce4563..a8296d31da6 100644 --- a/lib/commands/ACL_LOG.spec.ts +++ b/packages/client/lib/commands/ACL_LOG.spec.ts @@ -1,9 +1,9 @@ import { strict as assert } from 'assert'; -import { describeHandleMinimumRedisVersion } from '../test-utils'; +import testUtils from '../test-utils'; import { transformArguments, transformReply } from './ACL_LOG'; describe('ACL LOG', () => { - describeHandleMinimumRedisVersion([6]); + testUtils.isVersionGreaterThanHook([6]); describe('transformArguments', () => { it('simple', () => { diff --git a/lib/commands/ACL_LOG.ts b/packages/client/lib/commands/ACL_LOG.ts similarity index 100% rename from lib/commands/ACL_LOG.ts rename to packages/client/lib/commands/ACL_LOG.ts diff --git a/lib/commands/ACL_LOG_RESET.spec.ts b/packages/client/lib/commands/ACL_LOG_RESET.spec.ts similarity index 72% rename from lib/commands/ACL_LOG_RESET.spec.ts rename to packages/client/lib/commands/ACL_LOG_RESET.spec.ts index 3f0e628d9f0..5d26e45d04f 100644 --- a/lib/commands/ACL_LOG_RESET.spec.ts +++ b/packages/client/lib/commands/ACL_LOG_RESET.spec.ts @@ -1,9 +1,9 @@ import { strict as assert } from 'assert'; -import { describeHandleMinimumRedisVersion } from '../test-utils'; +import testUtils from '../test-utils'; import { transformArguments } from './ACL_LOG_RESET'; describe('ACL LOG RESET', () => { - describeHandleMinimumRedisVersion([6]); + testUtils.isVersionGreaterThanHook([6]); it('transformArguments', () => { assert.deepEqual( diff --git a/lib/commands/ACL_LOG_RESET.ts b/packages/client/lib/commands/ACL_LOG_RESET.ts similarity index 100% rename from lib/commands/ACL_LOG_RESET.ts rename to packages/client/lib/commands/ACL_LOG_RESET.ts diff --git a/lib/commands/ACL_SAVE.spec.ts b/packages/client/lib/commands/ACL_SAVE.spec.ts similarity index 70% rename from lib/commands/ACL_SAVE.spec.ts rename to packages/client/lib/commands/ACL_SAVE.spec.ts index b34c7bb0e6f..f4de312bb7a 100644 --- a/lib/commands/ACL_SAVE.spec.ts +++ b/packages/client/lib/commands/ACL_SAVE.spec.ts @@ -1,9 +1,9 @@ import { strict as assert } from 'assert'; -import { describeHandleMinimumRedisVersion } from '../test-utils'; +import testUtils from '../test-utils'; import { transformArguments } from './ACL_LOAD'; describe('ACL LOAD', () => { - describeHandleMinimumRedisVersion([6]); + testUtils.isVersionGreaterThanHook([6]); it('transformArguments', () => { assert.deepEqual( diff --git a/lib/commands/ACL_SAVE.ts b/packages/client/lib/commands/ACL_SAVE.ts similarity index 100% rename from lib/commands/ACL_SAVE.ts rename to packages/client/lib/commands/ACL_SAVE.ts diff --git a/lib/commands/ACL_SETUSER.spec.ts b/packages/client/lib/commands/ACL_SETUSER.spec.ts similarity index 84% rename from lib/commands/ACL_SETUSER.spec.ts rename to packages/client/lib/commands/ACL_SETUSER.spec.ts index f3badfcdca8..9c8ea8a59e0 100644 --- a/lib/commands/ACL_SETUSER.spec.ts +++ b/packages/client/lib/commands/ACL_SETUSER.spec.ts @@ -1,9 +1,9 @@ import { strict as assert } from 'assert'; -import { describeHandleMinimumRedisVersion } from '../test-utils'; +import testUtils from '../test-utils'; import { transformArguments } from './ACL_SETUSER'; describe('ACL SETUSER', () => { - describeHandleMinimumRedisVersion([6]); + testUtils.isVersionGreaterThanHook([6]); describe('transformArguments', () => { it('string', () => { diff --git a/lib/commands/ACL_SETUSER.ts b/packages/client/lib/commands/ACL_SETUSER.ts similarity index 100% rename from lib/commands/ACL_SETUSER.ts rename to packages/client/lib/commands/ACL_SETUSER.ts diff --git a/lib/commands/ACL_USERS.spec.ts b/packages/client/lib/commands/ACL_USERS.spec.ts similarity index 71% rename from lib/commands/ACL_USERS.spec.ts rename to packages/client/lib/commands/ACL_USERS.spec.ts index 14b76725fcd..35e06ce8494 100644 --- a/lib/commands/ACL_USERS.spec.ts +++ b/packages/client/lib/commands/ACL_USERS.spec.ts @@ -1,9 +1,9 @@ import { strict as assert } from 'assert'; -import { describeHandleMinimumRedisVersion } from '../test-utils'; +import testUtils from '../test-utils'; import { transformArguments } from './ACL_USERS'; describe('ACL USERS', () => { - describeHandleMinimumRedisVersion([6]); + testUtils.isVersionGreaterThanHook([6]); it('transformArguments', () => { assert.deepEqual( diff --git a/lib/commands/ACL_USERS.ts b/packages/client/lib/commands/ACL_USERS.ts similarity index 100% rename from lib/commands/ACL_USERS.ts rename to packages/client/lib/commands/ACL_USERS.ts diff --git a/lib/commands/ACL_WHOAMI.spec.ts b/packages/client/lib/commands/ACL_WHOAMI.spec.ts similarity index 71% rename from lib/commands/ACL_WHOAMI.spec.ts rename to packages/client/lib/commands/ACL_WHOAMI.spec.ts index a933057ea9d..32eb327beea 100644 --- a/lib/commands/ACL_WHOAMI.spec.ts +++ b/packages/client/lib/commands/ACL_WHOAMI.spec.ts @@ -1,9 +1,9 @@ import { strict as assert } from 'assert'; -import { describeHandleMinimumRedisVersion } from '../test-utils'; +import testUtils from '../test-utils'; import { transformArguments } from './ACL_WHOAMI'; describe('ACL WHOAMI', () => { - describeHandleMinimumRedisVersion([6]); + testUtils.isVersionGreaterThanHook([6]); it('transformArguments', () => { assert.deepEqual( diff --git a/lib/commands/ACL_WHOAMI.ts b/packages/client/lib/commands/ACL_WHOAMI.ts similarity index 100% rename from lib/commands/ACL_WHOAMI.ts rename to packages/client/lib/commands/ACL_WHOAMI.ts diff --git a/lib/commands/APPEND.spec.ts b/packages/client/lib/commands/APPEND.spec.ts similarity index 90% rename from lib/commands/APPEND.spec.ts rename to packages/client/lib/commands/APPEND.spec.ts index 283ab807956..23353866843 100644 --- a/lib/commands/APPEND.spec.ts +++ b/packages/client/lib/commands/APPEND.spec.ts @@ -1,7 +1,7 @@ import { strict as assert } from 'assert'; import { transformArguments } from './APPEND'; -describe('AUTH', () => { +describe('APPEND', () => { it('transformArguments', () => { assert.deepEqual( transformArguments('key', 'value'), diff --git a/lib/commands/APPEND.ts b/packages/client/lib/commands/APPEND.ts similarity index 100% rename from lib/commands/APPEND.ts rename to packages/client/lib/commands/APPEND.ts diff --git a/lib/commands/ASKING.spec.ts b/packages/client/lib/commands/ASKING.spec.ts similarity index 100% rename from lib/commands/ASKING.spec.ts rename to packages/client/lib/commands/ASKING.spec.ts diff --git a/lib/commands/ASKING.ts b/packages/client/lib/commands/ASKING.ts similarity index 100% rename from lib/commands/ASKING.ts rename to packages/client/lib/commands/ASKING.ts diff --git a/lib/commands/AUTH.spec.ts b/packages/client/lib/commands/AUTH.spec.ts similarity index 100% rename from lib/commands/AUTH.spec.ts rename to packages/client/lib/commands/AUTH.spec.ts diff --git a/lib/commands/AUTH.ts b/packages/client/lib/commands/AUTH.ts similarity index 100% rename from lib/commands/AUTH.ts rename to packages/client/lib/commands/AUTH.ts diff --git a/lib/commands/BGREWRITEAOF.spec.ts b/packages/client/lib/commands/BGREWRITEAOF.spec.ts similarity index 100% rename from lib/commands/BGREWRITEAOF.spec.ts rename to packages/client/lib/commands/BGREWRITEAOF.spec.ts diff --git a/lib/commands/BGREWRITEAOF.ts b/packages/client/lib/commands/BGREWRITEAOF.ts similarity index 100% rename from lib/commands/BGREWRITEAOF.ts rename to packages/client/lib/commands/BGREWRITEAOF.ts diff --git a/lib/commands/BGSAVE.spec.ts b/packages/client/lib/commands/BGSAVE.spec.ts similarity index 100% rename from lib/commands/BGSAVE.spec.ts rename to packages/client/lib/commands/BGSAVE.spec.ts diff --git a/lib/commands/BGSAVE.ts b/packages/client/lib/commands/BGSAVE.ts similarity index 100% rename from lib/commands/BGSAVE.ts rename to packages/client/lib/commands/BGSAVE.ts diff --git a/lib/commands/BITCOUNT.spec.ts b/packages/client/lib/commands/BITCOUNT.spec.ts similarity index 82% rename from lib/commands/BITCOUNT.spec.ts rename to packages/client/lib/commands/BITCOUNT.spec.ts index bf4cf39cab6..8919957cf92 100644 --- a/lib/commands/BITCOUNT.spec.ts +++ b/packages/client/lib/commands/BITCOUNT.spec.ts @@ -1,5 +1,5 @@ import { strict as assert } from 'assert'; -import { TestRedisServers, itWithClient } from '../test-utils'; +import testUtils, { GLOBAL } from '../test-utils'; import { transformArguments } from './BITCOUNT'; describe('BITCOUNT', () => { @@ -22,10 +22,10 @@ describe('BITCOUNT', () => { }); }); - itWithClient(TestRedisServers.OPEN, 'client.bitCount', async client => { + testUtils.testWithClient('client.bitCount', async client => { assert.equal( await client.bitCount('key'), 0 ); - }); + }, GLOBAL.SERVERS.OPEN); }); diff --git a/lib/commands/BITCOUNT.ts b/packages/client/lib/commands/BITCOUNT.ts similarity index 100% rename from lib/commands/BITCOUNT.ts rename to packages/client/lib/commands/BITCOUNT.ts diff --git a/lib/commands/BITFIELD.spec.ts b/packages/client/lib/commands/BITFIELD.spec.ts similarity index 88% rename from lib/commands/BITFIELD.spec.ts rename to packages/client/lib/commands/BITFIELD.spec.ts index 4d6d9d11c1a..93a5cb08a63 100644 --- a/lib/commands/BITFIELD.spec.ts +++ b/packages/client/lib/commands/BITFIELD.spec.ts @@ -1,5 +1,5 @@ import { strict as assert } from 'assert'; -import { TestRedisServers, itWithClient } from '../test-utils'; +import testUtils, { GLOBAL } from '../test-utils'; import { transformArguments } from './BITFIELD'; describe('BITFIELD', () => { @@ -33,10 +33,10 @@ describe('BITFIELD', () => { ); }); - itWithClient(TestRedisServers.OPEN, 'client.bitField', async client => { + testUtils.testWithClient('client.bitField', async client => { assert.deepEqual( await client.bitField('key', []), [] ); - }); + }, GLOBAL.SERVERS.OPEN); }); diff --git a/lib/commands/BITFIELD.ts b/packages/client/lib/commands/BITFIELD.ts similarity index 100% rename from lib/commands/BITFIELD.ts rename to packages/client/lib/commands/BITFIELD.ts diff --git a/lib/commands/BITOP.spec.ts b/packages/client/lib/commands/BITOP.spec.ts similarity index 75% rename from lib/commands/BITOP.spec.ts rename to packages/client/lib/commands/BITOP.spec.ts index aa863e5f2d2..554530d56f4 100644 --- a/lib/commands/BITOP.spec.ts +++ b/packages/client/lib/commands/BITOP.spec.ts @@ -1,5 +1,5 @@ import { strict as assert } from 'assert'; -import { TestRedisServers, itWithClient, TestRedisClusters, itWithCluster } from '../test-utils'; +import testUtils, { GLOBAL } from '../test-utils'; import { transformArguments } from './BITOP'; describe('BITOP', () => { @@ -19,17 +19,17 @@ describe('BITOP', () => { }); }); - itWithClient(TestRedisServers.OPEN, 'client.bitOp', async client => { + testUtils.testWithClient('client.bitOp', async client => { assert.equal( await client.bitOp('AND', 'destKey', 'key'), 0 ); - }); + }, GLOBAL.SERVERS.OPEN); - itWithCluster(TestRedisClusters.OPEN, 'cluster.bitOp', async cluster => { + testUtils.testWithCluster('cluster.bitOp', async cluster => { assert.equal( await cluster.bitOp('AND', '{tag}destKey', '{tag}key'), 0 ); - }); + }, GLOBAL.CLUSTERS.OPEN); }); diff --git a/lib/commands/BITOP.ts b/packages/client/lib/commands/BITOP.ts similarity index 100% rename from lib/commands/BITOP.ts rename to packages/client/lib/commands/BITOP.ts diff --git a/lib/commands/BITPOS.spec.ts b/packages/client/lib/commands/BITPOS.spec.ts similarity index 77% rename from lib/commands/BITPOS.spec.ts rename to packages/client/lib/commands/BITPOS.spec.ts index ad08e708c54..354deea6195 100644 --- a/lib/commands/BITPOS.spec.ts +++ b/packages/client/lib/commands/BITPOS.spec.ts @@ -1,5 +1,5 @@ import { strict as assert } from 'assert'; -import { TestRedisServers, itWithClient, TestRedisClusters, itWithCluster } from '../test-utils'; +import testUtils, { GLOBAL } from '../test-utils'; import { transformArguments } from './BITPOS'; describe('BITPOS', () => { @@ -26,17 +26,17 @@ describe('BITPOS', () => { }); }); - itWithClient(TestRedisServers.OPEN, 'client.bitPos', async client => { + testUtils.testWithClient('client.bitPos', async client => { assert.equal( await client.bitPos('key', 1, 1), -1 ); - }); + }, GLOBAL.SERVERS.OPEN); - itWithCluster(TestRedisClusters.OPEN, 'cluster.bitPos', async cluster => { + testUtils.testWithCluster('cluster.bitPos', async cluster => { assert.equal( await cluster.bitPos('key', 1, 1), -1 ); - }); + }, GLOBAL.CLUSTERS.OPEN); }); diff --git a/lib/commands/BITPOS.ts b/packages/client/lib/commands/BITPOS.ts similarity index 100% rename from lib/commands/BITPOS.ts rename to packages/client/lib/commands/BITPOS.ts diff --git a/lib/commands/BLMOVE.spec.ts b/packages/client/lib/commands/BLMOVE.spec.ts similarity index 75% rename from lib/commands/BLMOVE.spec.ts rename to packages/client/lib/commands/BLMOVE.spec.ts index b942864758f..3b86c1ec91e 100644 --- a/lib/commands/BLMOVE.spec.ts +++ b/packages/client/lib/commands/BLMOVE.spec.ts @@ -1,10 +1,10 @@ import { strict as assert } from 'assert'; -import { TestRedisServers, itWithClient, itWithCluster, TestRedisClusters, describeHandleMinimumRedisVersion } from '../test-utils'; +import testUtils, { GLOBAL } from '../test-utils'; import { transformArguments } from './BLMOVE'; import { commandOptions } from '../../index'; describe('BLMOVE', () => { - describeHandleMinimumRedisVersion([6, 2]); + testUtils.isVersionGreaterThanHook([6, 2]); it('transformArguments', () => { assert.deepEqual( @@ -13,7 +13,7 @@ describe('BLMOVE', () => { ); }); - itWithClient(TestRedisServers.OPEN, 'client.blMove', async client => { + testUtils.testWithClient('client.blMove', async client => { const [blMoveReply] = await Promise.all([ client.blMove(commandOptions({ isolated: true @@ -25,9 +25,9 @@ describe('BLMOVE', () => { blMoveReply, 'element' ); - }); + }, GLOBAL.SERVERS.OPEN); - itWithCluster(TestRedisClusters.OPEN, 'cluster.blMove', async cluster => { + testUtils.testWithCluster('cluster.blMove', async cluster => { const [blMoveReply] = await Promise.all([ cluster.blMove(commandOptions({ isolated: true @@ -39,5 +39,5 @@ describe('BLMOVE', () => { blMoveReply, 'element' ); - }); + }, GLOBAL.CLUSTERS.OPEN); }); diff --git a/lib/commands/BLMOVE.ts b/packages/client/lib/commands/BLMOVE.ts similarity index 100% rename from lib/commands/BLMOVE.ts rename to packages/client/lib/commands/BLMOVE.ts diff --git a/lib/commands/BLPOP.spec.ts b/packages/client/lib/commands/BLPOP.spec.ts similarity index 87% rename from lib/commands/BLPOP.spec.ts rename to packages/client/lib/commands/BLPOP.spec.ts index 651dd09eaf3..4b93c0b43b8 100644 --- a/lib/commands/BLPOP.spec.ts +++ b/packages/client/lib/commands/BLPOP.spec.ts @@ -1,5 +1,5 @@ import { strict as assert } from 'assert'; -import { TestRedisServers, itWithClient, itWithCluster, TestRedisClusters } from '../test-utils'; +import testUtils, { GLOBAL } from '../test-utils'; import { transformArguments, transformReply } from './BLPOP'; import { commandOptions } from '../../index'; @@ -39,7 +39,7 @@ describe('BLPOP', () => { }); }); - itWithClient(TestRedisServers.OPEN, 'client.blPop', async client => { + testUtils.testWithClient('client.blPop', async client => { const [ blPopReply ] = await Promise.all([ client.blPop( commandOptions({ isolated: true }), @@ -56,9 +56,9 @@ describe('BLPOP', () => { element: 'element' } ); - }); + }, GLOBAL.SERVERS.OPEN); - itWithCluster(TestRedisClusters.OPEN, 'cluster.blPop', async cluster => { + testUtils.testWithCluster('cluster.blPop', async cluster => { const [ blPopReply ] = await Promise.all([ cluster.blPop( commandOptions({ isolated: true }), @@ -75,5 +75,5 @@ describe('BLPOP', () => { element: 'element' } ); - }); + }, GLOBAL.CLUSTERS.OPEN); }); diff --git a/lib/commands/BLPOP.ts b/packages/client/lib/commands/BLPOP.ts similarity index 100% rename from lib/commands/BLPOP.ts rename to packages/client/lib/commands/BLPOP.ts diff --git a/lib/commands/BRPOP.spec.ts b/packages/client/lib/commands/BRPOP.spec.ts similarity index 87% rename from lib/commands/BRPOP.spec.ts rename to packages/client/lib/commands/BRPOP.spec.ts index 9a7d0bbc37d..fc203e1abdf 100644 --- a/lib/commands/BRPOP.spec.ts +++ b/packages/client/lib/commands/BRPOP.spec.ts @@ -1,5 +1,5 @@ import { strict as assert } from 'assert'; -import { TestRedisServers, itWithClient, itWithCluster, TestRedisClusters } from '../test-utils'; +import testUtils, { GLOBAL } from '../test-utils'; import { transformArguments, transformReply } from './BRPOP'; import { commandOptions } from '../../index'; @@ -39,7 +39,7 @@ describe('BRPOP', () => { }); }); - itWithClient(TestRedisServers.OPEN, 'client.brPop', async client => { + testUtils.testWithClient('client.brPop', async client => { const [ brPopReply ] = await Promise.all([ client.brPop( commandOptions({ isolated: true }), @@ -56,9 +56,9 @@ describe('BRPOP', () => { element: 'element' } ); - }); + }, GLOBAL.SERVERS.OPEN); - itWithCluster(TestRedisClusters.OPEN, 'cluster.brPop', async cluster => { + testUtils.testWithCluster('cluster.brPop', async cluster => { const [ brPopReply ] = await Promise.all([ cluster.brPop( commandOptions({ isolated: true }), @@ -75,5 +75,5 @@ describe('BRPOP', () => { element: 'element' } ); - }); + }, GLOBAL.CLUSTERS.OPEN); }); diff --git a/lib/commands/BRPOP.ts b/packages/client/lib/commands/BRPOP.ts similarity index 100% rename from lib/commands/BRPOP.ts rename to packages/client/lib/commands/BRPOP.ts diff --git a/lib/commands/BRPOPLPUSH.spec.ts b/packages/client/lib/commands/BRPOPLPUSH.spec.ts similarity index 80% rename from lib/commands/BRPOPLPUSH.spec.ts rename to packages/client/lib/commands/BRPOPLPUSH.spec.ts index 08bcf5e4d94..214af4553ac 100644 --- a/lib/commands/BRPOPLPUSH.spec.ts +++ b/packages/client/lib/commands/BRPOPLPUSH.spec.ts @@ -1,5 +1,5 @@ import { strict as assert } from 'assert'; -import { TestRedisServers, itWithClient, itWithCluster, TestRedisClusters } from '../test-utils'; +import testUtils, { GLOBAL } from '../test-utils'; import { transformArguments } from './BRPOPLPUSH'; import { commandOptions } from '../../index'; @@ -11,7 +11,7 @@ describe('BRPOPLPUSH', () => { ); }); - itWithClient(TestRedisServers.OPEN, 'client.brPopLPush', async client => { + testUtils.testWithClient('client.brPopLPush', async client => { const [ popReply ] = await Promise.all([ client.brPopLPush( commandOptions({ isolated: true }), @@ -26,9 +26,9 @@ describe('BRPOPLPUSH', () => { popReply, 'element' ); - }); + }, GLOBAL.SERVERS.OPEN); - itWithCluster(TestRedisClusters.OPEN, 'cluster.brPopLPush', async cluster => { + testUtils.testWithCluster('cluster.brPopLPush', async cluster => { const [ popReply ] = await Promise.all([ cluster.brPopLPush( commandOptions({ isolated: true }), @@ -43,5 +43,5 @@ describe('BRPOPLPUSH', () => { popReply, 'element' ); - }); + }, GLOBAL.CLUSTERS.OPEN); }); diff --git a/lib/commands/BRPOPLPUSH.ts b/packages/client/lib/commands/BRPOPLPUSH.ts similarity index 100% rename from lib/commands/BRPOPLPUSH.ts rename to packages/client/lib/commands/BRPOPLPUSH.ts diff --git a/lib/commands/BZPOPMAX.spec.ts b/packages/client/lib/commands/BZPOPMAX.spec.ts similarity index 91% rename from lib/commands/BZPOPMAX.spec.ts rename to packages/client/lib/commands/BZPOPMAX.spec.ts index 090dfba096d..e1c37478469 100644 --- a/lib/commands/BZPOPMAX.spec.ts +++ b/packages/client/lib/commands/BZPOPMAX.spec.ts @@ -1,5 +1,5 @@ import { strict as assert } from 'assert'; -import { TestRedisServers, itWithClient } from '../test-utils'; +import testUtils, { GLOBAL } from '../test-utils'; import { transformArguments, transformReply } from './BZPOPMAX'; import { commandOptions } from '../../index'; @@ -40,7 +40,7 @@ describe('BZPOPMAX', () => { }); }); - itWithClient(TestRedisServers.OPEN, 'client.bzPopMax', async client => { + testUtils.testWithClient('client.bzPopMax', async client => { const [ bzPopMaxReply ] = await Promise.all([ client.bzPopMax( commandOptions({ isolated: true }), @@ -61,5 +61,5 @@ describe('BZPOPMAX', () => { score: 1 } ); - }); + }, GLOBAL.SERVERS.OPEN); }); diff --git a/lib/commands/BZPOPMAX.ts b/packages/client/lib/commands/BZPOPMAX.ts similarity index 100% rename from lib/commands/BZPOPMAX.ts rename to packages/client/lib/commands/BZPOPMAX.ts diff --git a/lib/commands/BZPOPMIN.spec.ts b/packages/client/lib/commands/BZPOPMIN.spec.ts similarity index 91% rename from lib/commands/BZPOPMIN.spec.ts rename to packages/client/lib/commands/BZPOPMIN.spec.ts index 8b8977f9b3a..4cd1ec1b220 100644 --- a/lib/commands/BZPOPMIN.spec.ts +++ b/packages/client/lib/commands/BZPOPMIN.spec.ts @@ -1,5 +1,5 @@ import { strict as assert } from 'assert'; -import { TestRedisServers, itWithClient } from '../test-utils'; +import testUtils, { GLOBAL } from '../test-utils'; import { transformArguments, transformReply } from './BZPOPMIN'; import { commandOptions } from '../../index'; @@ -40,7 +40,7 @@ describe('BZPOPMIN', () => { }); }); - itWithClient(TestRedisServers.OPEN, 'client.bzPopMin', async client => { + testUtils.testWithClient('client.bzPopMin', async client => { const [ bzPopMinReply ] = await Promise.all([ client.bzPopMin( commandOptions({ isolated: true }), @@ -61,5 +61,5 @@ describe('BZPOPMIN', () => { score: 1 } ); - }); + }, GLOBAL.SERVERS.OPEN); }); diff --git a/lib/commands/BZPOPMIN.ts b/packages/client/lib/commands/BZPOPMIN.ts similarity index 100% rename from lib/commands/BZPOPMIN.ts rename to packages/client/lib/commands/BZPOPMIN.ts diff --git a/lib/commands/CLIENT_ID.spec.ts b/packages/client/lib/commands/CLIENT_ID.spec.ts similarity index 71% rename from lib/commands/CLIENT_ID.spec.ts rename to packages/client/lib/commands/CLIENT_ID.spec.ts index cb7dfd9f730..6792a8c31be 100644 --- a/lib/commands/CLIENT_ID.spec.ts +++ b/packages/client/lib/commands/CLIENT_ID.spec.ts @@ -1,5 +1,5 @@ import { strict as assert } from 'assert'; -import { TestRedisServers, itWithClient } from '../test-utils'; +import testUtils, { GLOBAL } from '../test-utils'; import { transformArguments } from './CLIENT_ID'; describe('CLIENT ID', () => { @@ -10,10 +10,10 @@ describe('CLIENT ID', () => { ); }); - itWithClient(TestRedisServers.OPEN, 'client.clientId', async client => { + testUtils.testWithClient('client.clientId', async client => { assert.equal( typeof (await client.clientId()), 'number' ); - }); + }, GLOBAL.SERVERS.OPEN); }); diff --git a/lib/commands/CLIENT_ID.ts b/packages/client/lib/commands/CLIENT_ID.ts similarity index 100% rename from lib/commands/CLIENT_ID.ts rename to packages/client/lib/commands/CLIENT_ID.ts diff --git a/lib/commands/CLIENT_INFO.spec.ts b/packages/client/lib/commands/CLIENT_INFO.spec.ts similarity index 100% rename from lib/commands/CLIENT_INFO.spec.ts rename to packages/client/lib/commands/CLIENT_INFO.spec.ts diff --git a/lib/commands/CLIENT_INFO.ts b/packages/client/lib/commands/CLIENT_INFO.ts similarity index 100% rename from lib/commands/CLIENT_INFO.ts rename to packages/client/lib/commands/CLIENT_INFO.ts diff --git a/lib/commands/CLUSTER_ADDSLOTS.spec.ts b/packages/client/lib/commands/CLUSTER_ADDSLOTS.spec.ts similarity index 100% rename from lib/commands/CLUSTER_ADDSLOTS.spec.ts rename to packages/client/lib/commands/CLUSTER_ADDSLOTS.spec.ts diff --git a/lib/commands/CLUSTER_ADDSLOTS.ts b/packages/client/lib/commands/CLUSTER_ADDSLOTS.ts similarity index 100% rename from lib/commands/CLUSTER_ADDSLOTS.ts rename to packages/client/lib/commands/CLUSTER_ADDSLOTS.ts diff --git a/lib/commands/CLUSTER_FLUSHSLOTS.spec.ts b/packages/client/lib/commands/CLUSTER_FLUSHSLOTS.spec.ts similarity index 100% rename from lib/commands/CLUSTER_FLUSHSLOTS.spec.ts rename to packages/client/lib/commands/CLUSTER_FLUSHSLOTS.spec.ts diff --git a/lib/commands/CLUSTER_FLUSHSLOTS.ts b/packages/client/lib/commands/CLUSTER_FLUSHSLOTS.ts similarity index 100% rename from lib/commands/CLUSTER_FLUSHSLOTS.ts rename to packages/client/lib/commands/CLUSTER_FLUSHSLOTS.ts diff --git a/lib/commands/CLUSTER_GETKEYSINSLOT.spec.ts b/packages/client/lib/commands/CLUSTER_GETKEYSINSLOT.spec.ts similarity index 100% rename from lib/commands/CLUSTER_GETKEYSINSLOT.spec.ts rename to packages/client/lib/commands/CLUSTER_GETKEYSINSLOT.spec.ts diff --git a/lib/commands/CLUSTER_GETKEYSINSLOT.ts b/packages/client/lib/commands/CLUSTER_GETKEYSINSLOT.ts similarity index 100% rename from lib/commands/CLUSTER_GETKEYSINSLOT.ts rename to packages/client/lib/commands/CLUSTER_GETKEYSINSLOT.ts diff --git a/lib/commands/CLUSTER_INFO.spec.ts b/packages/client/lib/commands/CLUSTER_INFO.spec.ts similarity index 100% rename from lib/commands/CLUSTER_INFO.spec.ts rename to packages/client/lib/commands/CLUSTER_INFO.spec.ts diff --git a/lib/commands/CLUSTER_INFO.ts b/packages/client/lib/commands/CLUSTER_INFO.ts similarity index 100% rename from lib/commands/CLUSTER_INFO.ts rename to packages/client/lib/commands/CLUSTER_INFO.ts diff --git a/lib/commands/CLUSTER_MEET.spec.ts b/packages/client/lib/commands/CLUSTER_MEET.spec.ts similarity index 100% rename from lib/commands/CLUSTER_MEET.spec.ts rename to packages/client/lib/commands/CLUSTER_MEET.spec.ts diff --git a/lib/commands/CLUSTER_MEET.ts b/packages/client/lib/commands/CLUSTER_MEET.ts similarity index 100% rename from lib/commands/CLUSTER_MEET.ts rename to packages/client/lib/commands/CLUSTER_MEET.ts diff --git a/lib/commands/CLUSTER_NODES.spec.ts b/packages/client/lib/commands/CLUSTER_NODES.spec.ts similarity index 100% rename from lib/commands/CLUSTER_NODES.spec.ts rename to packages/client/lib/commands/CLUSTER_NODES.spec.ts diff --git a/lib/commands/CLUSTER_NODES.ts b/packages/client/lib/commands/CLUSTER_NODES.ts similarity index 100% rename from lib/commands/CLUSTER_NODES.ts rename to packages/client/lib/commands/CLUSTER_NODES.ts diff --git a/lib/commands/CLUSTER_RESET.spec.ts b/packages/client/lib/commands/CLUSTER_RESET.spec.ts similarity index 100% rename from lib/commands/CLUSTER_RESET.spec.ts rename to packages/client/lib/commands/CLUSTER_RESET.spec.ts diff --git a/lib/commands/CLUSTER_RESET.ts b/packages/client/lib/commands/CLUSTER_RESET.ts similarity index 100% rename from lib/commands/CLUSTER_RESET.ts rename to packages/client/lib/commands/CLUSTER_RESET.ts diff --git a/lib/commands/CLUSTER_SETSLOT.spec.ts b/packages/client/lib/commands/CLUSTER_SETSLOT.spec.ts similarity index 100% rename from lib/commands/CLUSTER_SETSLOT.spec.ts rename to packages/client/lib/commands/CLUSTER_SETSLOT.spec.ts diff --git a/lib/commands/CLUSTER_SETSLOT.ts b/packages/client/lib/commands/CLUSTER_SETSLOT.ts similarity index 100% rename from lib/commands/CLUSTER_SETSLOT.ts rename to packages/client/lib/commands/CLUSTER_SETSLOT.ts diff --git a/lib/commands/CLUSTER_SLOTS.spec.ts b/packages/client/lib/commands/CLUSTER_SLOTS.spec.ts similarity index 99% rename from lib/commands/CLUSTER_SLOTS.spec.ts rename to packages/client/lib/commands/CLUSTER_SLOTS.spec.ts index ec6773bcdd4..6efbfe13ce1 100644 --- a/lib/commands/CLUSTER_SLOTS.spec.ts +++ b/packages/client/lib/commands/CLUSTER_SLOTS.spec.ts @@ -71,6 +71,6 @@ describe('CLUSTER SLOTS', () => { id: '58e6e48d41228013e5d9c1c37c5060693925e97e' }] }] - ) + ); }); }); diff --git a/lib/commands/CLUSTER_SLOTS.ts b/packages/client/lib/commands/CLUSTER_SLOTS.ts similarity index 100% rename from lib/commands/CLUSTER_SLOTS.ts rename to packages/client/lib/commands/CLUSTER_SLOTS.ts diff --git a/packages/client/lib/commands/COMMAND.spec.ts b/packages/client/lib/commands/COMMAND.spec.ts new file mode 100644 index 00000000000..baad79845ab --- /dev/null +++ b/packages/client/lib/commands/COMMAND.spec.ts @@ -0,0 +1,17 @@ +import { strict as assert } from 'assert'; +import testUtils, { GLOBAL } from '../test-utils'; +import { transformArguments } from './COMMAND'; +import { assertPingCommand } from './COMMAND_INFO.spec'; + +describe('COMMAND', () => { + it('transformArguments', () => { + assert.deepEqual( + transformArguments(), + ['COMMAND'] + ); + }); + + testUtils.testWithClient('client.command', async client => { + assertPingCommand((await client.command()).find(command => command.name === 'ping')); + }, GLOBAL.SERVERS.OPEN); +}); diff --git a/lib/commands/COMMAND.ts b/packages/client/lib/commands/COMMAND.ts similarity index 100% rename from lib/commands/COMMAND.ts rename to packages/client/lib/commands/COMMAND.ts diff --git a/lib/commands/COMMAND_COUNT.spec.ts b/packages/client/lib/commands/COMMAND_COUNT.spec.ts similarity index 71% rename from lib/commands/COMMAND_COUNT.spec.ts rename to packages/client/lib/commands/COMMAND_COUNT.spec.ts index 23e83c71cec..71482382f67 100644 --- a/lib/commands/COMMAND_COUNT.spec.ts +++ b/packages/client/lib/commands/COMMAND_COUNT.spec.ts @@ -1,5 +1,5 @@ import { strict as assert } from 'assert'; -import { TestRedisServers, itWithClient } from '../test-utils'; +import testUtils, { GLOBAL } from '../test-utils'; import { transformArguments } from './COMMAND_COUNT'; describe('COMMAND COUNT', () => { @@ -10,10 +10,10 @@ describe('COMMAND COUNT', () => { ); }); - itWithClient(TestRedisServers.OPEN, 'client.commandCount', async client => { + testUtils.testWithClient('client.commandCount', async client => { assert.equal( typeof await client.commandCount(), 'number' ); - }); + }, GLOBAL.SERVERS.OPEN); }); diff --git a/lib/commands/COMMAND_COUNT.ts b/packages/client/lib/commands/COMMAND_COUNT.ts similarity index 77% rename from lib/commands/COMMAND_COUNT.ts rename to packages/client/lib/commands/COMMAND_COUNT.ts index 5b8283bcc66..34c6a088da6 100644 --- a/lib/commands/COMMAND_COUNT.ts +++ b/packages/client/lib/commands/COMMAND_COUNT.ts @@ -6,4 +6,4 @@ export function transformArguments(): RedisCommandArguments { return ['COMMAND', 'COUNT']; } -declare function transformReply(): number; +export declare function transformReply(): number; diff --git a/lib/commands/COMMAND_GETKEYS.spec.ts b/packages/client/lib/commands/COMMAND_GETKEYS.spec.ts similarity index 73% rename from lib/commands/COMMAND_GETKEYS.spec.ts rename to packages/client/lib/commands/COMMAND_GETKEYS.spec.ts index f2630db9afa..a92d032c5d6 100644 --- a/lib/commands/COMMAND_GETKEYS.spec.ts +++ b/packages/client/lib/commands/COMMAND_GETKEYS.spec.ts @@ -1,5 +1,5 @@ import { strict as assert } from 'assert'; -import { TestRedisServers, itWithClient } from '../test-utils'; +import testUtils, { GLOBAL } from '../test-utils'; import { transformArguments } from './COMMAND_GETKEYS'; describe('COMMAND GETKEYS', () => { @@ -10,10 +10,10 @@ describe('COMMAND GETKEYS', () => { ); }); - itWithClient(TestRedisServers.OPEN, 'client.commandGetKeys', async client => { + testUtils.testWithClient('client.commandGetKeys', async client => { assert.deepEqual( await client.commandGetKeys(['GET', 'key']), ['key'] ); - }); + }, GLOBAL.SERVERS.OPEN); }); diff --git a/lib/commands/COMMAND_GETKEYS.ts b/packages/client/lib/commands/COMMAND_GETKEYS.ts similarity index 78% rename from lib/commands/COMMAND_GETKEYS.ts rename to packages/client/lib/commands/COMMAND_GETKEYS.ts index caf342088fb..1c38515aef9 100644 --- a/lib/commands/COMMAND_GETKEYS.ts +++ b/packages/client/lib/commands/COMMAND_GETKEYS.ts @@ -6,4 +6,4 @@ export function transformArguments(args: Array): RedisCommandArguments { return ['COMMAND', 'GETKEYS', ...args]; } -declare function transformReply(): Array; +export declare function transformReply(): Array; diff --git a/packages/client/lib/commands/COMMAND_INFO.spec.ts b/packages/client/lib/commands/COMMAND_INFO.spec.ts new file mode 100644 index 00000000000..d6488460484 --- /dev/null +++ b/packages/client/lib/commands/COMMAND_INFO.spec.ts @@ -0,0 +1,45 @@ +import { strict as assert } from 'assert'; +import testUtils, { GLOBAL } from '../test-utils'; +import { transformArguments } from './COMMAND_INFO'; +import { CommandCategories, CommandFlags, CommandReply } from './generic-transformers'; + +export function assertPingCommand(commandInfo: CommandReply | null | undefined): void { + assert.deepEqual( + commandInfo, + { + name: 'ping', + arity: -1, + flags: new Set([CommandFlags.STALE, CommandFlags.FAST]), + firstKeyIndex: 0, + lastKeyIndex: 0, + step: 0, + categories: new Set( + testUtils.isVersionGreaterThan([6]) ? + [CommandCategories.FAST, CommandCategories.CONNECTION] : + [] + ) + } + ); +} + +describe('COMMAND INFO', () => { + it('transformArguments', () => { + assert.deepEqual( + transformArguments(['PING']), + ['COMMAND', 'INFO', 'PING'] + ); + }); + + describe('client.commandInfo', () => { + testUtils.testWithClient('PING', async client => { + assertPingCommand((await client.commandInfo(['PING']))[0]); + }, GLOBAL.SERVERS.OPEN); + + testUtils.testWithClient('DOSE_NOT_EXISTS', async client => { + assert.deepEqual( + await client.commandInfo(['DOSE_NOT_EXISTS']), + [null] + ); + }, GLOBAL.SERVERS.OPEN); + }); +}); diff --git a/lib/commands/COMMAND_INFO.ts b/packages/client/lib/commands/COMMAND_INFO.ts similarity index 100% rename from lib/commands/COMMAND_INFO.ts rename to packages/client/lib/commands/COMMAND_INFO.ts diff --git a/lib/commands/CONFIG_GET.spec.ts b/packages/client/lib/commands/CONFIG_GET.spec.ts similarity index 100% rename from lib/commands/CONFIG_GET.spec.ts rename to packages/client/lib/commands/CONFIG_GET.spec.ts diff --git a/packages/client/lib/commands/CONFIG_GET.ts b/packages/client/lib/commands/CONFIG_GET.ts new file mode 100644 index 00000000000..35907742e25 --- /dev/null +++ b/packages/client/lib/commands/CONFIG_GET.ts @@ -0,0 +1,5 @@ +export function transformArguments(parameter: string): Array { + return ['CONFIG', 'GET', parameter]; +} + +export { transformReplyTuples as transformReply } from './generic-transformers'; diff --git a/lib/commands/CONFIG_RESETSTAT.spec.ts b/packages/client/lib/commands/CONFIG_RESETSTAT.spec.ts similarity index 100% rename from lib/commands/CONFIG_RESETSTAT.spec.ts rename to packages/client/lib/commands/CONFIG_RESETSTAT.spec.ts diff --git a/lib/commands/CONFIG_RESETSTAT.ts b/packages/client/lib/commands/CONFIG_RESETSTAT.ts similarity index 100% rename from lib/commands/CONFIG_RESETSTAT.ts rename to packages/client/lib/commands/CONFIG_RESETSTAT.ts diff --git a/lib/commands/CONFIG_REWRITE.spec.ts b/packages/client/lib/commands/CONFIG_REWRITE.spec.ts similarity index 100% rename from lib/commands/CONFIG_REWRITE.spec.ts rename to packages/client/lib/commands/CONFIG_REWRITE.spec.ts diff --git a/lib/commands/CONFIG_REWRITE.ts b/packages/client/lib/commands/CONFIG_REWRITE.ts similarity index 100% rename from lib/commands/CONFIG_REWRITE.ts rename to packages/client/lib/commands/CONFIG_REWRITE.ts diff --git a/lib/commands/CONFIG_SET.spec.ts b/packages/client/lib/commands/CONFIG_SET.spec.ts similarity index 100% rename from lib/commands/CONFIG_SET.spec.ts rename to packages/client/lib/commands/CONFIG_SET.spec.ts diff --git a/lib/commands/CONFIG_SET.ts b/packages/client/lib/commands/CONFIG_SET.ts similarity index 100% rename from lib/commands/CONFIG_SET.ts rename to packages/client/lib/commands/CONFIG_SET.ts diff --git a/lib/commands/COPY.spec.ts b/packages/client/lib/commands/COPY.spec.ts similarity index 88% rename from lib/commands/COPY.spec.ts rename to packages/client/lib/commands/COPY.spec.ts index fb35be863ab..0d68e969cdb 100644 --- a/lib/commands/COPY.spec.ts +++ b/packages/client/lib/commands/COPY.spec.ts @@ -1,9 +1,9 @@ import { strict as assert } from 'assert'; -import { TestRedisServers, itWithClient, describeHandleMinimumRedisVersion } from '../test-utils'; +import testUtils, { GLOBAL } from '../test-utils'; import { transformArguments, transformReply } from './COPY'; describe('COPY', () => { - describeHandleMinimumRedisVersion([6, 2]); + testUtils.isVersionGreaterThanHook([6, 2]); describe('transformArguments', () => { it('simple', () => { @@ -58,10 +58,10 @@ describe('COPY', () => { }); }); - itWithClient(TestRedisServers.OPEN, 'client.copy', async client => { + testUtils.testWithClient('client.copy', async client => { assert.equal( await client.copy('source', 'destination'), false ); - }); + }, GLOBAL.SERVERS.OPEN); }); diff --git a/lib/commands/COPY.ts b/packages/client/lib/commands/COPY.ts similarity index 80% rename from lib/commands/COPY.ts rename to packages/client/lib/commands/COPY.ts index 534b0d9c48c..c7a44e45c3c 100644 --- a/lib/commands/COPY.ts +++ b/packages/client/lib/commands/COPY.ts @@ -1,5 +1,3 @@ -import { transformReplyBoolean } from './generic-transformers'; - interface CopyCommandOptions { destinationDb?: number; replace?: boolean; @@ -21,4 +19,4 @@ export function transformArguments(source: string, destination: string, options? return args; } -export const transformReply = transformReplyBoolean; +export { transformReplyBoolean as transformReply } from './generic-transformers'; diff --git a/lib/commands/DBSIZE.spec.ts b/packages/client/lib/commands/DBSIZE.spec.ts similarity index 70% rename from lib/commands/DBSIZE.spec.ts rename to packages/client/lib/commands/DBSIZE.spec.ts index 36f591dbd29..a014a46e6e2 100644 --- a/lib/commands/DBSIZE.spec.ts +++ b/packages/client/lib/commands/DBSIZE.spec.ts @@ -1,5 +1,5 @@ import { strict as assert } from 'assert'; -import { TestRedisServers, itWithClient } from '../test-utils'; +import testUtils, { GLOBAL } from '../test-utils'; import { transformArguments } from './DBSIZE'; describe('DBSIZE', () => { @@ -10,10 +10,10 @@ describe('DBSIZE', () => { ); }); - itWithClient(TestRedisServers.OPEN, 'client.dbSize', async client => { + testUtils.testWithClient('client.dbSize', async client => { assert.equal( await client.dbSize(), 0 ); - }); + }, GLOBAL.SERVERS.OPEN); }); diff --git a/lib/commands/DBSIZE.ts b/packages/client/lib/commands/DBSIZE.ts similarity index 100% rename from lib/commands/DBSIZE.ts rename to packages/client/lib/commands/DBSIZE.ts diff --git a/lib/commands/DECR.spec.ts b/packages/client/lib/commands/DECR.spec.ts similarity index 70% rename from lib/commands/DECR.spec.ts rename to packages/client/lib/commands/DECR.spec.ts index 5b4b4f0fd33..75e1205feda 100644 --- a/lib/commands/DECR.spec.ts +++ b/packages/client/lib/commands/DECR.spec.ts @@ -1,5 +1,5 @@ import { strict as assert } from 'assert'; -import { TestRedisServers, itWithClient } from '../test-utils'; +import testUtils, { GLOBAL } from '../test-utils'; import { transformArguments } from './DECR'; describe('DECR', () => { @@ -10,10 +10,10 @@ describe('DECR', () => { ); }); - itWithClient(TestRedisServers.OPEN, 'client.decr', async client => { + testUtils.testWithClient('client.decr', async client => { assert.equal( await client.decr('key'), -1 ); - }); + }, GLOBAL.SERVERS.OPEN); }); diff --git a/lib/commands/DECR.ts b/packages/client/lib/commands/DECR.ts similarity index 100% rename from lib/commands/DECR.ts rename to packages/client/lib/commands/DECR.ts diff --git a/lib/commands/DECRBY.spec.ts b/packages/client/lib/commands/DECRBY.spec.ts similarity index 71% rename from lib/commands/DECRBY.spec.ts rename to packages/client/lib/commands/DECRBY.spec.ts index 1c9ac69bb96..d2c23e94728 100644 --- a/lib/commands/DECRBY.spec.ts +++ b/packages/client/lib/commands/DECRBY.spec.ts @@ -1,5 +1,5 @@ import { strict as assert } from 'assert'; -import { TestRedisServers, itWithClient } from '../test-utils'; +import testUtils, { GLOBAL } from '../test-utils'; import { transformArguments } from './DECRBY'; describe('DECRBY', () => { @@ -10,10 +10,10 @@ describe('DECRBY', () => { ); }); - itWithClient(TestRedisServers.OPEN, 'client.decrBy', async client => { + testUtils.testWithClient('client.decrBy', async client => { assert.equal( await client.decrBy('key', 2), -2 ); - }); + }, GLOBAL.SERVERS.OPEN); }); diff --git a/lib/commands/DECRBY.ts b/packages/client/lib/commands/DECRBY.ts similarity index 100% rename from lib/commands/DECRBY.ts rename to packages/client/lib/commands/DECRBY.ts diff --git a/lib/commands/DEL.spec.ts b/packages/client/lib/commands/DEL.spec.ts similarity index 80% rename from lib/commands/DEL.spec.ts rename to packages/client/lib/commands/DEL.spec.ts index ec780de67a0..75a29a8f641 100644 --- a/lib/commands/DEL.spec.ts +++ b/packages/client/lib/commands/DEL.spec.ts @@ -1,5 +1,5 @@ import { strict as assert } from 'assert'; -import { TestRedisServers, itWithClient } from '../test-utils'; +import testUtils, { GLOBAL } from '../test-utils'; import { transformArguments } from './DEL'; describe('DEL', () => { @@ -19,10 +19,10 @@ describe('DEL', () => { }); }); - itWithClient(TestRedisServers.OPEN, 'client.del', async client => { + testUtils.testWithClient('client.del', async client => { assert.equal( await client.del('key'), 0 ); - }); + }, GLOBAL.SERVERS.OPEN); }); diff --git a/lib/commands/DEL.ts b/packages/client/lib/commands/DEL.ts similarity index 100% rename from lib/commands/DEL.ts rename to packages/client/lib/commands/DEL.ts diff --git a/lib/commands/DISCARD.spec.ts b/packages/client/lib/commands/DISCARD.spec.ts similarity index 100% rename from lib/commands/DISCARD.spec.ts rename to packages/client/lib/commands/DISCARD.spec.ts diff --git a/lib/commands/DISCARD.ts b/packages/client/lib/commands/DISCARD.ts similarity index 100% rename from lib/commands/DISCARD.ts rename to packages/client/lib/commands/DISCARD.ts diff --git a/lib/commands/DUMP.spec.ts b/packages/client/lib/commands/DUMP.spec.ts similarity index 52% rename from lib/commands/DUMP.spec.ts rename to packages/client/lib/commands/DUMP.spec.ts index e3f42c57578..aebbf4f3f7c 100644 --- a/lib/commands/DUMP.spec.ts +++ b/packages/client/lib/commands/DUMP.spec.ts @@ -1,11 +1,11 @@ import { strict as assert } from 'assert'; -import { TestRedisServers, itWithClient } from '../test-utils'; +import testUtils, { GLOBAL } from '../test-utils'; describe('DUMP', () => { - itWithClient(TestRedisServers.OPEN, 'client.dump', async client => { + testUtils.testWithClient('client.dump', async client => { assert.equal( await client.dump('key'), null ); - }); + }, GLOBAL.SERVERS.OPEN); }); diff --git a/lib/commands/DUMP.ts b/packages/client/lib/commands/DUMP.ts similarity index 100% rename from lib/commands/DUMP.ts rename to packages/client/lib/commands/DUMP.ts diff --git a/lib/commands/ECHO.spec.ts b/packages/client/lib/commands/ECHO.spec.ts similarity index 72% rename from lib/commands/ECHO.spec.ts rename to packages/client/lib/commands/ECHO.spec.ts index d91b7373950..27f6b2a17d3 100644 --- a/lib/commands/ECHO.spec.ts +++ b/packages/client/lib/commands/ECHO.spec.ts @@ -1,5 +1,5 @@ import { strict as assert } from 'assert'; -import { TestRedisServers, itWithClient } from '../test-utils'; +import testUtils, { GLOBAL } from '../test-utils'; import { transformArguments } from './ECHO'; describe('ECHO', () => { @@ -10,10 +10,10 @@ describe('ECHO', () => { ); }); - itWithClient(TestRedisServers.OPEN, 'client.echo', async client => { + testUtils.testWithClient('client.echo', async client => { assert.equal( await client.echo('message'), 'message' ); - }); + }, GLOBAL.SERVERS.OPEN); }); diff --git a/lib/commands/ECHO.ts b/packages/client/lib/commands/ECHO.ts similarity index 100% rename from lib/commands/ECHO.ts rename to packages/client/lib/commands/ECHO.ts diff --git a/lib/commands/EVAL.spec.ts b/packages/client/lib/commands/EVAL.spec.ts similarity index 69% rename from lib/commands/EVAL.spec.ts rename to packages/client/lib/commands/EVAL.spec.ts index 2be1aedf08a..7aa029362fd 100644 --- a/lib/commands/EVAL.spec.ts +++ b/packages/client/lib/commands/EVAL.spec.ts @@ -1,5 +1,5 @@ import { strict as assert } from 'assert'; -import { TestRedisServers, itWithClient, TestRedisClusters, itWithCluster } from '../test-utils'; +import testUtils, { GLOBAL } from '../test-utils'; import { transformArguments } from './EVAL'; describe('EVAL', () => { @@ -13,17 +13,17 @@ describe('EVAL', () => { ); }); - itWithClient(TestRedisServers.OPEN, 'client.eval', async client => { + testUtils.testWithClient('client.eval', async client => { assert.equal( await client.eval('return 1'), 1 ); - }); + }, GLOBAL.SERVERS.OPEN); - itWithCluster(TestRedisClusters.OPEN, 'cluster.eval', async cluster => { + testUtils.testWithCluster('cluster.eval', async cluster => { assert.equal( await cluster.eval('return 1'), 1 ); - }); + }, GLOBAL.CLUSTERS.OPEN); }); diff --git a/lib/commands/EVAL.ts b/packages/client/lib/commands/EVAL.ts similarity index 100% rename from lib/commands/EVAL.ts rename to packages/client/lib/commands/EVAL.ts diff --git a/lib/commands/EVALSHA.spec.ts b/packages/client/lib/commands/EVALSHA.spec.ts similarity index 100% rename from lib/commands/EVALSHA.spec.ts rename to packages/client/lib/commands/EVALSHA.spec.ts diff --git a/lib/commands/EVALSHA.ts b/packages/client/lib/commands/EVALSHA.ts similarity index 100% rename from lib/commands/EVALSHA.ts rename to packages/client/lib/commands/EVALSHA.ts diff --git a/lib/commands/EXISTS.spec.ts b/packages/client/lib/commands/EXISTS.spec.ts similarity index 80% rename from lib/commands/EXISTS.spec.ts rename to packages/client/lib/commands/EXISTS.spec.ts index 3cba44b563f..241a97c362e 100644 --- a/lib/commands/EXISTS.spec.ts +++ b/packages/client/lib/commands/EXISTS.spec.ts @@ -1,5 +1,5 @@ import { strict as assert } from 'assert'; -import { TestRedisServers, itWithClient } from '../test-utils'; +import testUtils, { GLOBAL } from '../test-utils'; import { transformArguments } from './EXISTS'; describe('EXISTS', () => { @@ -19,10 +19,10 @@ describe('EXISTS', () => { }); }); - itWithClient(TestRedisServers.OPEN, 'client.exists', async client => { + testUtils.testWithClient('client.exists', async client => { assert.equal( await client.exists('key'), false ); - }); + }, GLOBAL.SERVERS.OPEN); }); diff --git a/lib/commands/EXISTS.ts b/packages/client/lib/commands/EXISTS.ts similarity index 64% rename from lib/commands/EXISTS.ts rename to packages/client/lib/commands/EXISTS.ts index aac164fc953..5a9c0e3be66 100644 --- a/lib/commands/EXISTS.ts +++ b/packages/client/lib/commands/EXISTS.ts @@ -1,5 +1,5 @@ import { RedisCommandArguments } from '.'; -import { pushVerdictArguments, transformReplyBoolean } from './generic-transformers'; +import { pushVerdictArguments } from './generic-transformers'; export const FIRST_KEY_INDEX = 1; @@ -9,4 +9,4 @@ export function transformArguments(keys: string | Array): RedisCommandAr return pushVerdictArguments(['EXISTS'], keys); } -export const transformReply = transformReplyBoolean; +export { transformReplyBoolean as transformReply } from './generic-transformers'; diff --git a/lib/commands/EXPIRE.spec.ts b/packages/client/lib/commands/EXPIRE.spec.ts similarity index 71% rename from lib/commands/EXPIRE.spec.ts rename to packages/client/lib/commands/EXPIRE.spec.ts index 6550532cab2..e2dc6e03123 100644 --- a/lib/commands/EXPIRE.spec.ts +++ b/packages/client/lib/commands/EXPIRE.spec.ts @@ -1,5 +1,5 @@ import { strict as assert } from 'assert'; -import { TestRedisServers, itWithClient } from '../test-utils'; +import testUtils, { GLOBAL } from '../test-utils'; import { transformArguments } from './EXPIRE'; describe('EXPIRE', () => { @@ -10,10 +10,10 @@ describe('EXPIRE', () => { ); }); - itWithClient(TestRedisServers.OPEN, 'client.expire', async client => { + testUtils.testWithClient('client.expire', async client => { assert.equal( await client.expire('key', 0), false ); - }); + }, GLOBAL.SERVERS.OPEN); }); diff --git a/lib/commands/EXPIRE.ts b/packages/client/lib/commands/EXPIRE.ts similarity index 52% rename from lib/commands/EXPIRE.ts rename to packages/client/lib/commands/EXPIRE.ts index 04b0504a6f7..36bcf8b882d 100644 --- a/lib/commands/EXPIRE.ts +++ b/packages/client/lib/commands/EXPIRE.ts @@ -1,7 +1,5 @@ -import { transformReplyBoolean } from './generic-transformers'; - export function transformArguments(key: string, seconds: number): Array { return ['EXPIRE', key, seconds.toString()]; } -export const transformReply = transformReplyBoolean; +export { transformReplyBoolean as transformReply } from './generic-transformers'; diff --git a/lib/commands/EXPIREAT.spec.ts b/packages/client/lib/commands/EXPIREAT.spec.ts similarity index 81% rename from lib/commands/EXPIREAT.spec.ts rename to packages/client/lib/commands/EXPIREAT.spec.ts index cefe9fa9b8b..1a11af1a735 100644 --- a/lib/commands/EXPIREAT.spec.ts +++ b/packages/client/lib/commands/EXPIREAT.spec.ts @@ -1,5 +1,5 @@ import { strict as assert } from 'assert'; -import { TestRedisServers, itWithClient } from '../test-utils'; +import testUtils, { GLOBAL } from '../test-utils'; import { transformArguments } from './EXPIREAT'; describe('EXPIREAT', () => { @@ -10,7 +10,7 @@ describe('EXPIREAT', () => { ['EXPIREAT', 'key', '1'] ); }); - + it('date', () => { const d = new Date(); assert.deepEqual( @@ -20,10 +20,10 @@ describe('EXPIREAT', () => { }); }); - itWithClient(TestRedisServers.OPEN, 'client.expireAt', async client => { + testUtils.testWithClient('client.expireAt', async client => { assert.equal( await client.expireAt('key', 1), false ); - }); + }, GLOBAL.SERVERS.OPEN); }); diff --git a/lib/commands/EXPIREAT.ts b/packages/client/lib/commands/EXPIREAT.ts similarity index 56% rename from lib/commands/EXPIREAT.ts rename to packages/client/lib/commands/EXPIREAT.ts index b7bfdcaa422..72142e4cb79 100644 --- a/lib/commands/EXPIREAT.ts +++ b/packages/client/lib/commands/EXPIREAT.ts @@ -1,4 +1,4 @@ -import { transformEXAT, transformReplyBoolean } from './generic-transformers'; +import { transformEXAT } from './generic-transformers'; export function transformArguments(key: string, timestamp: number | Date): Array { return [ @@ -8,4 +8,4 @@ export function transformArguments(key: string, timestamp: number | Date): Array ]; } -export const transformReply = transformReplyBoolean; +export { transformReplyBoolean as transformReply } from './generic-transformers'; diff --git a/lib/commands/FAILOVER.spec.ts b/packages/client/lib/commands/FAILOVER.spec.ts similarity index 100% rename from lib/commands/FAILOVER.spec.ts rename to packages/client/lib/commands/FAILOVER.spec.ts diff --git a/lib/commands/FAILOVER.ts b/packages/client/lib/commands/FAILOVER.ts similarity index 100% rename from lib/commands/FAILOVER.ts rename to packages/client/lib/commands/FAILOVER.ts diff --git a/lib/commands/FLUSHALL.spec.ts b/packages/client/lib/commands/FLUSHALL.spec.ts similarity index 84% rename from lib/commands/FLUSHALL.spec.ts rename to packages/client/lib/commands/FLUSHALL.spec.ts index 7f1c5ffd282..db5bb72e9cb 100644 --- a/lib/commands/FLUSHALL.spec.ts +++ b/packages/client/lib/commands/FLUSHALL.spec.ts @@ -1,5 +1,5 @@ import { strict as assert } from 'assert'; -import { TestRedisServers, itWithClient } from '../test-utils'; +import testUtils, { GLOBAL } from '../test-utils'; import { RedisFlushModes, transformArguments } from './FLUSHALL'; describe('FLUSHALL', () => { @@ -26,10 +26,10 @@ describe('FLUSHALL', () => { }); }); - itWithClient(TestRedisServers.OPEN, 'client.flushAll', async client => { + testUtils.testWithClient('client.flushAll', async client => { assert.equal( await client.flushAll(), 'OK' ); - }); + }, GLOBAL.SERVERS.OPEN); }); diff --git a/lib/commands/FLUSHALL.ts b/packages/client/lib/commands/FLUSHALL.ts similarity index 100% rename from lib/commands/FLUSHALL.ts rename to packages/client/lib/commands/FLUSHALL.ts diff --git a/lib/commands/FLUSHDB.spec.ts b/packages/client/lib/commands/FLUSHDB.spec.ts similarity index 84% rename from lib/commands/FLUSHDB.spec.ts rename to packages/client/lib/commands/FLUSHDB.spec.ts index e237e527680..bf460e9e7a8 100644 --- a/lib/commands/FLUSHDB.spec.ts +++ b/packages/client/lib/commands/FLUSHDB.spec.ts @@ -1,5 +1,5 @@ import { strict as assert } from 'assert'; -import { TestRedisServers, itWithClient } from '../test-utils'; +import testUtils, { GLOBAL } from '../test-utils'; import { RedisFlushModes } from './FLUSHALL'; import { transformArguments } from './FLUSHDB'; @@ -27,10 +27,10 @@ describe('FLUSHDB', () => { }); }); - itWithClient(TestRedisServers.OPEN, 'client.flushDb', async client => { + testUtils.testWithClient('client.flushDb', async client => { assert.equal( await client.flushDb(), 'OK' ); - }); + }, GLOBAL.SERVERS.OPEN); }); diff --git a/lib/commands/FLUSHDB.ts b/packages/client/lib/commands/FLUSHDB.ts similarity index 100% rename from lib/commands/FLUSHDB.ts rename to packages/client/lib/commands/FLUSHDB.ts diff --git a/lib/commands/GEOADD.spec.ts b/packages/client/lib/commands/GEOADD.spec.ts similarity index 90% rename from lib/commands/GEOADD.spec.ts rename to packages/client/lib/commands/GEOADD.spec.ts index 673e962093f..6425c881c9d 100644 --- a/lib/commands/GEOADD.spec.ts +++ b/packages/client/lib/commands/GEOADD.spec.ts @@ -1,5 +1,5 @@ import { strict as assert } from 'assert'; -import { TestRedisServers, itWithClient, TestRedisClusters, itWithCluster } from '../test-utils'; +import testUtils, { GLOBAL } from '../test-utils'; import { transformArguments } from './GEOADD'; describe('GEOADD', () => { @@ -71,7 +71,7 @@ describe('GEOADD', () => { }); }); - itWithClient(TestRedisServers.OPEN, 'client.geoAdd', async client => { + testUtils.testWithClient('client.geoAdd', async client => { assert.equal( await client.geoAdd('key', { member: 'member', @@ -80,9 +80,9 @@ describe('GEOADD', () => { }), 1 ); - }); + }, GLOBAL.SERVERS.OPEN); - itWithCluster(TestRedisClusters.OPEN, 'cluster.geoAdd', async cluster => { + testUtils.testWithCluster('cluster.geoAdd', async cluster => { assert.equal( await cluster.geoAdd('key', { member: 'member', @@ -91,5 +91,5 @@ describe('GEOADD', () => { }), 1 ); - }); + }, GLOBAL.CLUSTERS.OPEN); }); diff --git a/lib/commands/GEOADD.ts b/packages/client/lib/commands/GEOADD.ts similarity index 100% rename from lib/commands/GEOADD.ts rename to packages/client/lib/commands/GEOADD.ts diff --git a/lib/commands/GEODIST.spec.ts b/packages/client/lib/commands/GEODIST.spec.ts similarity index 78% rename from lib/commands/GEODIST.spec.ts rename to packages/client/lib/commands/GEODIST.spec.ts index 2cff8ecbd82..bbc62480ee1 100644 --- a/lib/commands/GEODIST.spec.ts +++ b/packages/client/lib/commands/GEODIST.spec.ts @@ -1,5 +1,5 @@ import { strict as assert } from 'assert'; -import { TestRedisServers, itWithClient, TestRedisClusters, itWithCluster } from '../test-utils'; +import testUtils, { GLOBAL } from '../test-utils'; import { transformArguments } from './GEODIST'; describe('GEODIST', () => { @@ -20,14 +20,14 @@ describe('GEODIST', () => { }); describe('client.geoDist', () => { - itWithClient(TestRedisServers.OPEN, 'null', async client => { + testUtils.testWithClient('null', async client => { assert.equal( await client.geoDist('key', '1', '2'), null ); - }); + }, GLOBAL.SERVERS.OPEN); - itWithClient(TestRedisServers.OPEN, 'with value', async client => { + testUtils.testWithClient('with value', async client => { const [, dist] = await Promise.all([ client.geoAdd('key', [{ member: '1', @@ -45,13 +45,13 @@ describe('GEODIST', () => { dist, 157270.0561 ); - }); + }, GLOBAL.SERVERS.OPEN); }); - itWithCluster(TestRedisClusters.OPEN, 'cluster.geoDist', async cluster => { + testUtils.testWithCluster('cluster.geoDist', async cluster => { assert.equal( await cluster.geoDist('key', '1', '2'), null ); - }); + }, GLOBAL.CLUSTERS.OPEN); }); diff --git a/lib/commands/GEODIST.ts b/packages/client/lib/commands/GEODIST.ts similarity index 100% rename from lib/commands/GEODIST.ts rename to packages/client/lib/commands/GEODIST.ts diff --git a/lib/commands/GEOHASH.spec.ts b/packages/client/lib/commands/GEOHASH.spec.ts similarity index 74% rename from lib/commands/GEOHASH.spec.ts rename to packages/client/lib/commands/GEOHASH.spec.ts index b79de235557..c421c148f43 100644 --- a/lib/commands/GEOHASH.spec.ts +++ b/packages/client/lib/commands/GEOHASH.spec.ts @@ -1,5 +1,5 @@ import { strict as assert } from 'assert'; -import { TestRedisServers, itWithClient, TestRedisClusters, itWithCluster } from '../test-utils'; +import testUtils, { GLOBAL } from '../test-utils'; import { transformArguments } from './GEOHASH'; describe('GEOHASH', () => { @@ -19,17 +19,17 @@ describe('GEOHASH', () => { }); }); - itWithClient(TestRedisServers.OPEN, 'client.geoHash', async client => { + testUtils.testWithClient('client.geoHash', async client => { assert.deepEqual( await client.geoHash('key', 'member'), [null] ); - }); + }, GLOBAL.SERVERS.OPEN); - itWithCluster(TestRedisClusters.OPEN, 'cluster.geoHash', async cluster => { + testUtils.testWithCluster('cluster.geoHash', async cluster => { assert.deepEqual( await cluster.geoHash('key', 'member'), [null] ); - }); + }, GLOBAL.CLUSTERS.OPEN); }); diff --git a/lib/commands/GEOHASH.ts b/packages/client/lib/commands/GEOHASH.ts similarity index 100% rename from lib/commands/GEOHASH.ts rename to packages/client/lib/commands/GEOHASH.ts diff --git a/lib/commands/GEOPOS.spec.ts b/packages/client/lib/commands/GEOPOS.spec.ts similarity index 82% rename from lib/commands/GEOPOS.spec.ts rename to packages/client/lib/commands/GEOPOS.spec.ts index e15abeff516..9c08ccd08f5 100644 --- a/lib/commands/GEOPOS.spec.ts +++ b/packages/client/lib/commands/GEOPOS.spec.ts @@ -1,5 +1,5 @@ import { strict as assert } from 'assert'; -import { TestRedisServers, itWithClient, TestRedisClusters, itWithCluster } from '../test-utils'; +import testUtils, { GLOBAL } from '../test-utils'; import { transformArguments, transformReply } from './GEOPOS'; describe('GEOPOS', () => { @@ -39,14 +39,14 @@ describe('GEOPOS', () => { }); describe('client.geoPos', () => { - itWithClient(TestRedisServers.OPEN, 'null', async client => { + testUtils.testWithClient('null', async client => { assert.deepEqual( await client.geoPos('key', 'member'), [null] ); - }); + }, GLOBAL.SERVERS.OPEN); - itWithClient(TestRedisServers.OPEN, 'with member', async client => { + testUtils.testWithClient('with member', async client => { const coordinates = { longitude: '-122.06429868936538696', latitude: '37.37749628831998194' @@ -61,13 +61,13 @@ describe('GEOPOS', () => { await client.geoPos('key', 'member'), [coordinates] ); - }); + }, GLOBAL.SERVERS.OPEN); }); - itWithCluster(TestRedisClusters.OPEN, 'cluster.geoPos', async cluster => { + testUtils.testWithCluster('cluster.geoPos', async cluster => { assert.deepEqual( await cluster.geoPos('key', 'member'), [null] ); - }); + }, GLOBAL.CLUSTERS.OPEN); }); diff --git a/lib/commands/GEOPOS.ts b/packages/client/lib/commands/GEOPOS.ts similarity index 100% rename from lib/commands/GEOPOS.ts rename to packages/client/lib/commands/GEOPOS.ts diff --git a/lib/commands/GEOSEARCH.spec.ts b/packages/client/lib/commands/GEOSEARCH.spec.ts similarity index 68% rename from lib/commands/GEOSEARCH.spec.ts rename to packages/client/lib/commands/GEOSEARCH.spec.ts index a8606b3f74c..ec0d4bcc4f8 100644 --- a/lib/commands/GEOSEARCH.spec.ts +++ b/packages/client/lib/commands/GEOSEARCH.spec.ts @@ -1,9 +1,9 @@ import { strict as assert } from 'assert'; -import { TestRedisServers, itWithClient, TestRedisClusters, itWithCluster, describeHandleMinimumRedisVersion } from '../test-utils'; +import testUtils, { GLOBAL } from '../test-utils'; import { transformArguments } from './GEOSEARCH'; describe('GEOSEARCH', () => { - describeHandleMinimumRedisVersion([6, 2]); + testUtils.isVersionGreaterThanHook([6, 2]); it('transformArguments', () => { assert.deepEqual( @@ -15,7 +15,7 @@ describe('GEOSEARCH', () => { ); }); - itWithClient(TestRedisServers.OPEN, 'client.geoSearch', async client => { + testUtils.testWithClient('client.geoSearch', async client => { assert.deepEqual( await client.geoSearch('key', 'member', { radius: 1, @@ -23,9 +23,9 @@ describe('GEOSEARCH', () => { }), [] ); - }); + }, GLOBAL.SERVERS.OPEN); - itWithCluster(TestRedisClusters.OPEN, 'cluster.geoSearch', async cluster => { + testUtils.testWithCluster('cluster.geoSearch', async cluster => { assert.deepEqual( await cluster.geoSearch('key', 'member', { radius: 1, @@ -33,5 +33,5 @@ describe('GEOSEARCH', () => { }), [] ); - }); + }, GLOBAL.CLUSTERS.OPEN); }); diff --git a/lib/commands/GEOSEARCH.ts b/packages/client/lib/commands/GEOSEARCH.ts similarity index 100% rename from lib/commands/GEOSEARCH.ts rename to packages/client/lib/commands/GEOSEARCH.ts diff --git a/lib/commands/GEOSEARCHSTORE.spec.ts b/packages/client/lib/commands/GEOSEARCHSTORE.spec.ts similarity index 85% rename from lib/commands/GEOSEARCHSTORE.spec.ts rename to packages/client/lib/commands/GEOSEARCHSTORE.spec.ts index ad33c62b78c..eb32fa134e4 100644 --- a/lib/commands/GEOSEARCHSTORE.spec.ts +++ b/packages/client/lib/commands/GEOSEARCHSTORE.spec.ts @@ -1,9 +1,9 @@ import { strict as assert } from 'assert'; -import { TestRedisServers, itWithClient, TestRedisClusters, itWithCluster, describeHandleMinimumRedisVersion } from '../test-utils'; +import testUtils, { GLOBAL } from '../test-utils'; import { transformArguments, transformReply } from './GEOSEARCHSTORE'; describe('GEOSEARCHSTORE', () => { - describeHandleMinimumRedisVersion([6, 2]); + testUtils.isVersionGreaterThanHook([6, 2]); describe('transformArguments', () => { it('simple', () => { @@ -47,7 +47,7 @@ describe('GEOSEARCHSTORE', () => { ); }); - itWithClient(TestRedisServers.OPEN, 'client.geoSearchStore', async client => { + testUtils.testWithClient('client.geoSearchStore', async client => { await client.geoAdd('source', { longitude: 1, latitude: 1, @@ -61,9 +61,9 @@ describe('GEOSEARCHSTORE', () => { }), 1 ); - }); + }, GLOBAL.SERVERS.OPEN); - itWithCluster(TestRedisClusters.OPEN, 'cluster.geoSearchStore', async cluster => { + testUtils.testWithCluster('cluster.geoSearchStore', async cluster => { await cluster.geoAdd('{tag}source', { longitude: 1, latitude: 1, @@ -77,5 +77,5 @@ describe('GEOSEARCHSTORE', () => { }), 1 ); - }); + }, GLOBAL.CLUSTERS.OPEN); }); diff --git a/lib/commands/GEOSEARCHSTORE.ts b/packages/client/lib/commands/GEOSEARCHSTORE.ts similarity index 100% rename from lib/commands/GEOSEARCHSTORE.ts rename to packages/client/lib/commands/GEOSEARCHSTORE.ts diff --git a/lib/commands/GEOSEARCH_WITH.spec.ts b/packages/client/lib/commands/GEOSEARCH_WITH.spec.ts similarity index 74% rename from lib/commands/GEOSEARCH_WITH.spec.ts rename to packages/client/lib/commands/GEOSEARCH_WITH.spec.ts index 922c00d7194..c1f5213775a 100644 --- a/lib/commands/GEOSEARCH_WITH.spec.ts +++ b/packages/client/lib/commands/GEOSEARCH_WITH.spec.ts @@ -1,14 +1,14 @@ import { strict as assert } from 'assert'; +import testUtils, { GLOBAL } from '../test-utils'; import { RedisCommandArguments } from '.'; -import { TestRedisServers, itWithClient, TestRedisClusters, itWithCluster, describeHandleMinimumRedisVersion } from '../test-utils'; import { GeoReplyWith } from './generic-transformers'; import { transformArguments } from './GEOSEARCH_WITH'; describe('GEOSEARCH WITH', () => { - describeHandleMinimumRedisVersion([6, 2]); + testUtils.isVersionGreaterThanHook([6, 2]); it('transformArguments', () => { - const expectedReply: RedisCommandArguments = ['GEOSEARCH', 'key', 'FROMMEMBER', 'member', 'BYRADIUS', '1', 'm', 'WITHDIST'] + const expectedReply: RedisCommandArguments = ['GEOSEARCH', 'key', 'FROMMEMBER', 'member', 'BYRADIUS', '1', 'm', 'WITHDIST']; expectedReply.preserve = ['WITHDIST']; assert.deepEqual( @@ -20,7 +20,7 @@ describe('GEOSEARCH WITH', () => { ); }); - itWithClient(TestRedisServers.OPEN, 'client.geoSearchWith', async client => { + testUtils.testWithClient('client.geoSearchWith', async client => { assert.deepEqual( await client.geoSearchWith('key', 'member', { radius: 1, @@ -28,9 +28,9 @@ describe('GEOSEARCH WITH', () => { }, [GeoReplyWith.DISTANCE]), [] ); - }); + }, GLOBAL.SERVERS.OPEN); - itWithCluster(TestRedisClusters.OPEN, 'cluster.geoSearchWith', async cluster => { + testUtils.testWithCluster('cluster.geoSearchWith', async cluster => { assert.deepEqual( await cluster.geoSearchWith('key', 'member', { radius: 1, @@ -38,5 +38,5 @@ describe('GEOSEARCH WITH', () => { }, [GeoReplyWith.DISTANCE]), [] ); - }); + }, GLOBAL.CLUSTERS.OPEN); }); diff --git a/lib/commands/GEOSEARCH_WITH.ts b/packages/client/lib/commands/GEOSEARCH_WITH.ts similarity index 82% rename from lib/commands/GEOSEARCH_WITH.ts rename to packages/client/lib/commands/GEOSEARCH_WITH.ts index cd461c9677f..64c6d88e33c 100644 --- a/lib/commands/GEOSEARCH_WITH.ts +++ b/packages/client/lib/commands/GEOSEARCH_WITH.ts @@ -1,5 +1,5 @@ import { RedisCommandArguments } from '.'; -import { GeoSearchFrom, GeoSearchBy, GeoReplyWith, GeoSearchOptions, transformGeoMembersWithReply } from './generic-transformers'; +import { GeoSearchFrom, GeoSearchBy, GeoReplyWith, GeoSearchOptions } from './generic-transformers'; import { transformArguments as geoSearchTransformArguments } from './GEOSEARCH'; export { FIRST_KEY_INDEX, IS_READ_ONLY } from './GEOSEARCH'; @@ -20,4 +20,4 @@ export function transformArguments( return args; } -export const transformReply = transformGeoMembersWithReply; +export { transformGeoMembersWithReply as transformReply } from './generic-transformers'; diff --git a/lib/commands/GET.spec.ts b/packages/client/lib/commands/GET.spec.ts similarity index 62% rename from lib/commands/GET.spec.ts rename to packages/client/lib/commands/GET.spec.ts index 303be60fe99..4c197f99a4e 100644 --- a/lib/commands/GET.spec.ts +++ b/packages/client/lib/commands/GET.spec.ts @@ -1,5 +1,5 @@ import { strict as assert } from 'assert'; -import { TestRedisServers, itWithClient, TestRedisClusters, itWithCluster } from '../test-utils'; +import testUtils, { GLOBAL } from '../test-utils'; import { transformArguments } from './GET'; describe('GET', () => { @@ -10,17 +10,17 @@ describe('GET', () => { ); }); - itWithClient(TestRedisServers.OPEN, 'client.get', async client => { + testUtils.testWithClient('client.get', async client => { assert.equal( await client.get('key'), null ); - }); + }, GLOBAL.SERVERS.OPEN); - itWithCluster(TestRedisClusters.OPEN, 'cluster.get', async cluster => { + testUtils.testWithCluster('cluster.get', async cluster => { assert.equal( await cluster.get('key'), null ); - }); + }, GLOBAL.CLUSTERS.OPEN); }); diff --git a/lib/commands/GET.ts b/packages/client/lib/commands/GET.ts similarity index 100% rename from lib/commands/GET.ts rename to packages/client/lib/commands/GET.ts diff --git a/lib/commands/GETBIT.spec.ts b/packages/client/lib/commands/GETBIT.spec.ts similarity index 63% rename from lib/commands/GETBIT.spec.ts rename to packages/client/lib/commands/GETBIT.spec.ts index 7163b4ba255..4206084eced 100644 --- a/lib/commands/GETBIT.spec.ts +++ b/packages/client/lib/commands/GETBIT.spec.ts @@ -1,5 +1,5 @@ import { strict as assert } from 'assert'; -import { TestRedisServers, itWithClient, TestRedisClusters, itWithCluster } from '../test-utils'; +import testUtils, { GLOBAL } from '../test-utils'; import { transformArguments } from './GETBIT'; describe('GETBIT', () => { @@ -10,17 +10,17 @@ describe('GETBIT', () => { ); }); - itWithClient(TestRedisServers.OPEN, 'client.getBit', async client => { + testUtils.testWithClient('client.getBit', async client => { assert.equal( await client.getBit('key', 0), 0 ); - }); + }, GLOBAL.SERVERS.OPEN); - itWithCluster(TestRedisClusters.OPEN, 'cluster.getBit', async cluster => { + testUtils.testWithCluster('cluster.getBit', async cluster => { assert.equal( await cluster.getBit('key', 0), 0 ); - }); + }, GLOBAL.CLUSTERS.OPEN); }); diff --git a/lib/commands/GETBIT.ts b/packages/client/lib/commands/GETBIT.ts similarity index 100% rename from lib/commands/GETBIT.ts rename to packages/client/lib/commands/GETBIT.ts diff --git a/lib/commands/GETDEL.spec.ts b/packages/client/lib/commands/GETDEL.spec.ts similarity index 56% rename from lib/commands/GETDEL.spec.ts rename to packages/client/lib/commands/GETDEL.spec.ts index 232c08b9500..db3a486696a 100644 --- a/lib/commands/GETDEL.spec.ts +++ b/packages/client/lib/commands/GETDEL.spec.ts @@ -1,9 +1,9 @@ import { strict as assert } from 'assert'; -import { TestRedisServers, itWithClient, TestRedisClusters, itWithCluster, describeHandleMinimumRedisVersion } from '../test-utils'; +import testUtils, { GLOBAL } from '../test-utils'; import { transformArguments } from './GETDEL'; describe('GETDEL', () => { - describeHandleMinimumRedisVersion([6, 2]); + testUtils.isVersionGreaterThanHook([6, 2]); it('transformArguments', () => { assert.deepEqual( @@ -12,18 +12,17 @@ describe('GETDEL', () => { ); }); - itWithClient(TestRedisServers.OPEN, 'client.getDel', async client => { + testUtils.testWithClient('client.getDel', async client => { assert.equal( await client.getDel('key'), null ); - }); - + }, GLOBAL.SERVERS.OPEN); - itWithCluster(TestRedisClusters.OPEN, 'cluster.getDel', async cluster => { + testUtils.testWithCluster('cluster.getDel', async cluster => { assert.equal( await cluster.getDel('key'), null ); - }); + }, GLOBAL.CLUSTERS.OPEN); }); diff --git a/lib/commands/GETDEL.ts b/packages/client/lib/commands/GETDEL.ts similarity index 100% rename from lib/commands/GETDEL.ts rename to packages/client/lib/commands/GETDEL.ts diff --git a/lib/commands/GETEX.spec.ts b/packages/client/lib/commands/GETEX.spec.ts similarity index 87% rename from lib/commands/GETEX.spec.ts rename to packages/client/lib/commands/GETEX.spec.ts index 830f12cedf8..1bf86089da1 100644 --- a/lib/commands/GETEX.spec.ts +++ b/packages/client/lib/commands/GETEX.spec.ts @@ -1,9 +1,9 @@ import { strict as assert } from 'assert'; -import { TestRedisServers, itWithClient, TestRedisClusters, itWithCluster, describeHandleMinimumRedisVersion } from '../test-utils'; +import testUtils, { GLOBAL } from '../test-utils'; import { transformArguments } from './GETEX'; describe('GETEX', () => { - describeHandleMinimumRedisVersion([6, 2]); + testUtils.isVersionGreaterThanHook([6, 2]); describe('transformArguments', () => { it('EX', () => { @@ -76,21 +76,21 @@ describe('GETEX', () => { }); }); - itWithClient(TestRedisServers.OPEN, 'client.getEx', async client => { + testUtils.testWithClient('client.getEx', async client => { assert.equal( await client.getEx('key', { PERSIST: true }), null ); - }); + }, GLOBAL.SERVERS.OPEN); - itWithCluster(TestRedisClusters.OPEN, 'cluster.getEx', async cluster => { + testUtils.testWithCluster('cluster.getEx', async cluster => { assert.equal( await cluster.getEx('key', { PERSIST: true }), null ); - }); + }, GLOBAL.CLUSTERS.OPEN); }); diff --git a/lib/commands/GETEX.ts b/packages/client/lib/commands/GETEX.ts similarity index 100% rename from lib/commands/GETEX.ts rename to packages/client/lib/commands/GETEX.ts diff --git a/lib/commands/GETRANGE.spec.ts b/packages/client/lib/commands/GETRANGE.spec.ts similarity index 64% rename from lib/commands/GETRANGE.spec.ts rename to packages/client/lib/commands/GETRANGE.spec.ts index 726311e6844..0c9dbc2c70f 100644 --- a/lib/commands/GETRANGE.spec.ts +++ b/packages/client/lib/commands/GETRANGE.spec.ts @@ -1,5 +1,5 @@ import { strict as assert } from 'assert'; -import { TestRedisServers, itWithClient, TestRedisClusters, itWithCluster } from '../test-utils'; +import testUtils, { GLOBAL } from '../test-utils'; import { transformArguments } from './GETRANGE'; describe('GETRANGE', () => { @@ -10,18 +10,17 @@ describe('GETRANGE', () => { ); }); - itWithClient(TestRedisServers.OPEN, 'client.getRange', async client => { + testUtils.testWithClient('client.getRange', async client => { assert.equal( await client.getRange('key', 0, -1), '' ); - }); - + }, GLOBAL.SERVERS.OPEN); - itWithCluster(TestRedisClusters.OPEN, 'cluster.lTrim', async cluster => { + testUtils.testWithCluster('cluster.lTrim', async cluster => { assert.equal( await cluster.getRange('key', 0, -1), '' ); - }); + }, GLOBAL.CLUSTERS.OPEN); }); diff --git a/lib/commands/GETRANGE.ts b/packages/client/lib/commands/GETRANGE.ts similarity index 100% rename from lib/commands/GETRANGE.ts rename to packages/client/lib/commands/GETRANGE.ts diff --git a/lib/commands/GETSET.spec.ts b/packages/client/lib/commands/GETSET.spec.ts similarity index 64% rename from lib/commands/GETSET.spec.ts rename to packages/client/lib/commands/GETSET.spec.ts index 4af5ab39ca2..73fbcec57ea 100644 --- a/lib/commands/GETSET.spec.ts +++ b/packages/client/lib/commands/GETSET.spec.ts @@ -1,5 +1,5 @@ import { strict as assert } from 'assert'; -import { TestRedisServers, itWithClient, TestRedisClusters, itWithCluster } from '../test-utils'; +import testUtils, { GLOBAL } from '../test-utils'; import { transformArguments } from './GETSET'; describe('GETSET', () => { @@ -10,17 +10,17 @@ describe('GETSET', () => { ); }); - itWithClient(TestRedisServers.OPEN, 'client.getSet', async client => { + testUtils.testWithClient('client.getSet', async client => { assert.equal( await client.getSet('key', 'value'), null ); - }); + }, GLOBAL.SERVERS.OPEN); - itWithCluster(TestRedisClusters.OPEN, 'cluster.getSet', async cluster => { + testUtils.testWithCluster('cluster.getSet', async cluster => { assert.equal( await cluster.getSet('key', 'value'), null ); - }); + }, GLOBAL.CLUSTERS.OPEN); }); diff --git a/lib/commands/GETSET.ts b/packages/client/lib/commands/GETSET.ts similarity index 100% rename from lib/commands/GETSET.ts rename to packages/client/lib/commands/GETSET.ts diff --git a/lib/commands/GET_BUFFER.spec.ts b/packages/client/lib/commands/GET_BUFFER.spec.ts similarity index 62% rename from lib/commands/GET_BUFFER.spec.ts rename to packages/client/lib/commands/GET_BUFFER.spec.ts index 533eb808c49..1f1a86799f4 100644 --- a/lib/commands/GET_BUFFER.spec.ts +++ b/packages/client/lib/commands/GET_BUFFER.spec.ts @@ -1,22 +1,22 @@ import { strict as assert } from 'assert'; -import { TestRedisServers, itWithClient, TestRedisClusters, itWithCluster } from '../test-utils'; +import testUtils, { GLOBAL } from '../test-utils'; describe('GET_BUFFER', () => { - itWithClient(TestRedisServers.OPEN, 'client.getBuffer', async client => { + testUtils.testWithClient('client.getBuffer', async client => { const buffer = Buffer.from('string'); await client.set('key', buffer); assert.deepEqual( buffer, await client.getBuffer('key') ); - }); + }, GLOBAL.SERVERS.OPEN); - itWithCluster(TestRedisClusters.OPEN, 'cluster.getBuffer', async cluster => { + testUtils.testWithCluster('cluster.getBuffer', async cluster => { const buffer = Buffer.from('string'); await cluster.set('key', buffer); assert.deepEqual( buffer, await cluster.getBuffer('key') ); - }); + }, GLOBAL.CLUSTERS.OPEN); }); diff --git a/lib/commands/GET_BUFFER.ts b/packages/client/lib/commands/GET_BUFFER.ts similarity index 100% rename from lib/commands/GET_BUFFER.ts rename to packages/client/lib/commands/GET_BUFFER.ts diff --git a/lib/commands/HDEL.spec.ts b/packages/client/lib/commands/HDEL.spec.ts similarity index 81% rename from lib/commands/HDEL.spec.ts rename to packages/client/lib/commands/HDEL.spec.ts index 04191f51ada..eb24bcfacbd 100644 --- a/lib/commands/HDEL.spec.ts +++ b/packages/client/lib/commands/HDEL.spec.ts @@ -1,5 +1,5 @@ import { strict as assert } from 'assert'; -import { TestRedisServers, itWithClient } from '../test-utils'; +import testUtils, { GLOBAL } from '../test-utils'; import { transformArguments } from './HDEL'; describe('HDEL', () => { @@ -19,10 +19,10 @@ describe('HDEL', () => { }); }); - itWithClient(TestRedisServers.OPEN, 'client.hDel', async client => { + testUtils.testWithClient('client.hDel', async client => { assert.equal( await client.hDel('key', 'field'), 0 ); - }); + }, GLOBAL.SERVERS.OPEN); }); diff --git a/lib/commands/HDEL.ts b/packages/client/lib/commands/HDEL.ts similarity index 100% rename from lib/commands/HDEL.ts rename to packages/client/lib/commands/HDEL.ts diff --git a/lib/commands/HELLO.spec.ts b/packages/client/lib/commands/HELLO.spec.ts similarity index 73% rename from lib/commands/HELLO.spec.ts rename to packages/client/lib/commands/HELLO.spec.ts index 7642f739d92..12d6d98c7c9 100644 --- a/lib/commands/HELLO.spec.ts +++ b/packages/client/lib/commands/HELLO.spec.ts @@ -1,9 +1,9 @@ import { strict as assert } from 'assert'; -import { REDIS_VERSION, TestRedisServers, itWithClient, describeHandleMinimumRedisVersion } from '../test-utils'; +import testUtils, { GLOBAL } from '../test-utils'; import { transformArguments } from './HELLO'; describe('HELLO', () => { - describeHandleMinimumRedisVersion([6]); + testUtils.isVersionGreaterThanHook([6]); describe('transformArguments', () => { it('simple', () => { @@ -60,18 +60,17 @@ describe('HELLO', () => { }); }); - itWithClient(TestRedisServers.OPEN, 'client.hello', async client => { - assert.deepEqual( - await client.hello(), - { - server: 'redis', - version: REDIS_VERSION.join('.'), - proto: 2, - id: await client.clientId(), - mode: 'standalone', - role: 'master', - modules: [] - } - ); + testUtils.testWithClient('client.hello', async client => { + const reply = await client.hello(); + assert.equal(reply.server, 'redis'); + assert.equal(typeof reply.version, 'string'); + assert.equal(reply.proto, 2); + assert.equal(typeof reply.id, 'number'); + assert.equal(reply.mode, 'standalone'); + assert.equal(reply.role, 'master'); + assert.deepEqual(reply.modules, []); + }, { + ...GLOBAL.SERVERS.OPEN, + minimumDockerVersion: [6, 2] }); }); diff --git a/lib/commands/HELLO.ts b/packages/client/lib/commands/HELLO.ts similarity index 100% rename from lib/commands/HELLO.ts rename to packages/client/lib/commands/HELLO.ts diff --git a/lib/commands/HEXISTS.spec.ts b/packages/client/lib/commands/HEXISTS.spec.ts similarity index 72% rename from lib/commands/HEXISTS.spec.ts rename to packages/client/lib/commands/HEXISTS.spec.ts index 26c411c432d..3764319c123 100644 --- a/lib/commands/HEXISTS.spec.ts +++ b/packages/client/lib/commands/HEXISTS.spec.ts @@ -1,5 +1,5 @@ import { strict as assert } from 'assert'; -import { TestRedisServers, itWithClient } from '../test-utils'; +import testUtils, { GLOBAL } from '../test-utils'; import { transformArguments } from './HEXISTS'; describe('HEXISTS', () => { @@ -10,10 +10,10 @@ describe('HEXISTS', () => { ); }); - itWithClient(TestRedisServers.OPEN, 'client.hExists', async client => { + testUtils.testWithClient('client.hExists', async client => { assert.equal( await client.hExists('key', 'field'), false ); - }); + }, GLOBAL.SERVERS.OPEN); }); diff --git a/lib/commands/HEXISTS.ts b/packages/client/lib/commands/HEXISTS.ts similarity index 56% rename from lib/commands/HEXISTS.ts rename to packages/client/lib/commands/HEXISTS.ts index 7cf0b158d92..0eae5e03e52 100644 --- a/lib/commands/HEXISTS.ts +++ b/packages/client/lib/commands/HEXISTS.ts @@ -1,9 +1,7 @@ -import { transformReplyBoolean } from './generic-transformers'; - export const FIRST_KEY_INDEX = 1; export function transformArguments(key: string, field: string): Array { return ['HEXISTS', key, field]; } -export const transformReply = transformReplyBoolean; +export { transformReplyBoolean as transformReply } from './generic-transformers'; diff --git a/lib/commands/HGET.spec.ts b/packages/client/lib/commands/HGET.spec.ts similarity index 72% rename from lib/commands/HGET.spec.ts rename to packages/client/lib/commands/HGET.spec.ts index c78550c5179..6b6d0a3ee22 100644 --- a/lib/commands/HGET.spec.ts +++ b/packages/client/lib/commands/HGET.spec.ts @@ -1,5 +1,5 @@ import { strict as assert } from 'assert'; -import { TestRedisServers, itWithClient } from '../test-utils'; +import testUtils, { GLOBAL } from '../test-utils'; import { transformArguments } from './HGET'; describe('HGET', () => { @@ -10,10 +10,10 @@ describe('HGET', () => { ); }); - itWithClient(TestRedisServers.OPEN, 'client.hGet', async client => { + testUtils.testWithClient('client.hGet', async client => { assert.equal( await client.hGet('key', 'field'), null ); - }); + }, GLOBAL.SERVERS.OPEN); }); diff --git a/lib/commands/HGET.ts b/packages/client/lib/commands/HGET.ts similarity index 100% rename from lib/commands/HGET.ts rename to packages/client/lib/commands/HGET.ts diff --git a/lib/commands/HGETALL.spec.ts b/packages/client/lib/commands/HGETALL.spec.ts similarity index 88% rename from lib/commands/HGETALL.spec.ts rename to packages/client/lib/commands/HGETALL.spec.ts index 68b51a2902b..fcd1a30457c 100644 --- a/lib/commands/HGETALL.spec.ts +++ b/packages/client/lib/commands/HGETALL.spec.ts @@ -1,5 +1,5 @@ import { strict as assert } from 'assert'; -import { TestRedisServers, itWithClient } from '../test-utils'; +import testUtils, { GLOBAL } from '../test-utils'; import { transformReply } from './HGETALL'; describe('HGETALL', () => { @@ -32,10 +32,10 @@ describe('HGETALL', () => { }); }); - itWithClient(TestRedisServers.OPEN, 'client.hGetAll', async client => { + testUtils.testWithClient('client.hGetAll', async client => { assert.deepEqual( await client.hGetAll('key'), Object.create(null) ); - }); + }, GLOBAL.SERVERS.OPEN); }); diff --git a/lib/commands/HGETALL.ts b/packages/client/lib/commands/HGETALL.ts similarity index 53% rename from lib/commands/HGETALL.ts rename to packages/client/lib/commands/HGETALL.ts index 8ac14ec4963..55c6d3456a1 100644 --- a/lib/commands/HGETALL.ts +++ b/packages/client/lib/commands/HGETALL.ts @@ -1,9 +1,7 @@ -import { transformReplyTuples } from './generic-transformers'; - export const FIRST_KEY_INDEX = 1; export function transformArguments(key: string): Array { return ['HGETALL', key]; } -export const transformReply = transformReplyTuples; +export { transformReplyTuples as transformReply } from './generic-transformers'; diff --git a/lib/commands/HINCRBY.spec.ts b/packages/client/lib/commands/HINCRBY.spec.ts similarity index 73% rename from lib/commands/HINCRBY.spec.ts rename to packages/client/lib/commands/HINCRBY.spec.ts index 898dfd1172f..de406217921 100644 --- a/lib/commands/HINCRBY.spec.ts +++ b/packages/client/lib/commands/HINCRBY.spec.ts @@ -1,5 +1,5 @@ import { strict as assert } from 'assert'; -import { TestRedisServers, itWithClient } from '../test-utils'; +import testUtils, { GLOBAL } from '../test-utils'; import { transformArguments } from './HINCRBY'; describe('HINCRBY', () => { @@ -10,10 +10,10 @@ describe('HINCRBY', () => { ); }); - itWithClient(TestRedisServers.OPEN, 'client.hIncrBy', async client => { + testUtils.testWithClient('client.hIncrBy', async client => { assert.equal( await client.hIncrBy('key', 'field', 1), 1 ); - }); + }, GLOBAL.SERVERS.OPEN); }); diff --git a/lib/commands/HINCRBY.ts b/packages/client/lib/commands/HINCRBY.ts similarity index 100% rename from lib/commands/HINCRBY.ts rename to packages/client/lib/commands/HINCRBY.ts diff --git a/lib/commands/HINCRBYFLOAT.spec.ts b/packages/client/lib/commands/HINCRBYFLOAT.spec.ts similarity index 73% rename from lib/commands/HINCRBYFLOAT.spec.ts rename to packages/client/lib/commands/HINCRBYFLOAT.spec.ts index 83e87538c54..bd0147a3481 100644 --- a/lib/commands/HINCRBYFLOAT.spec.ts +++ b/packages/client/lib/commands/HINCRBYFLOAT.spec.ts @@ -1,5 +1,5 @@ import { strict as assert } from 'assert'; -import { TestRedisServers, itWithClient } from '../test-utils'; +import testUtils, { GLOBAL } from '../test-utils'; import { transformArguments } from './HINCRBYFLOAT'; describe('HINCRBYFLOAT', () => { @@ -10,10 +10,10 @@ describe('HINCRBYFLOAT', () => { ); }); - itWithClient(TestRedisServers.OPEN, 'client.hIncrByFloat', async client => { + testUtils.testWithClient('client.hIncrByFloat', async client => { assert.equal( await client.hIncrByFloat('key', 'field', 1.5), '1.5' ); - }); + }, GLOBAL.SERVERS.OPEN); }); diff --git a/lib/commands/HINCRBYFLOAT.ts b/packages/client/lib/commands/HINCRBYFLOAT.ts similarity index 100% rename from lib/commands/HINCRBYFLOAT.ts rename to packages/client/lib/commands/HINCRBYFLOAT.ts diff --git a/lib/commands/HKEYS.spec.ts b/packages/client/lib/commands/HKEYS.spec.ts similarity index 71% rename from lib/commands/HKEYS.spec.ts rename to packages/client/lib/commands/HKEYS.spec.ts index 12190668b0b..f94538f67d3 100644 --- a/lib/commands/HKEYS.spec.ts +++ b/packages/client/lib/commands/HKEYS.spec.ts @@ -1,5 +1,5 @@ import { strict as assert } from 'assert'; -import { TestRedisServers, itWithClient } from '../test-utils'; +import testUtils, { GLOBAL } from '../test-utils'; import { transformArguments } from './HKEYS'; describe('HKEYS', () => { @@ -10,10 +10,10 @@ describe('HKEYS', () => { ); }); - itWithClient(TestRedisServers.OPEN, 'client.hKeys', async client => { + testUtils.testWithClient('client.hKeys', async client => { assert.deepEqual( await client.hKeys('key'), [] ); - }); + }, GLOBAL.SERVERS.OPEN); }); diff --git a/lib/commands/HKEYS.ts b/packages/client/lib/commands/HKEYS.ts similarity index 100% rename from lib/commands/HKEYS.ts rename to packages/client/lib/commands/HKEYS.ts diff --git a/lib/commands/HLEN.spec.ts b/packages/client/lib/commands/HLEN.spec.ts similarity index 70% rename from lib/commands/HLEN.spec.ts rename to packages/client/lib/commands/HLEN.spec.ts index e9aaa64e6e5..be9d4b13a7d 100644 --- a/lib/commands/HLEN.spec.ts +++ b/packages/client/lib/commands/HLEN.spec.ts @@ -1,5 +1,5 @@ import { strict as assert } from 'assert'; -import { TestRedisServers, itWithClient } from '../test-utils'; +import testUtils, { GLOBAL } from '../test-utils'; import { transformArguments } from './HLEN'; describe('HLEN', () => { @@ -10,10 +10,10 @@ describe('HLEN', () => { ); }); - itWithClient(TestRedisServers.OPEN, 'client.hLen', async client => { + testUtils.testWithClient('client.hLen', async client => { assert.equal( await client.hLen('key'), 0 ); - }); + }, GLOBAL.SERVERS.OPEN); }); diff --git a/lib/commands/HLEN.ts b/packages/client/lib/commands/HLEN.ts similarity index 100% rename from lib/commands/HLEN.ts rename to packages/client/lib/commands/HLEN.ts diff --git a/lib/commands/HMGET.spec.ts b/packages/client/lib/commands/HMGET.spec.ts similarity index 81% rename from lib/commands/HMGET.spec.ts rename to packages/client/lib/commands/HMGET.spec.ts index 3b1c286e748..a7c934b760d 100644 --- a/lib/commands/HMGET.spec.ts +++ b/packages/client/lib/commands/HMGET.spec.ts @@ -1,5 +1,5 @@ import { strict as assert } from 'assert'; -import { TestRedisServers, itWithClient } from '../test-utils'; +import testUtils, { GLOBAL } from '../test-utils'; import { transformArguments } from './HMGET'; describe('HMGET', () => { @@ -19,10 +19,10 @@ describe('HMGET', () => { }); }); - itWithClient(TestRedisServers.OPEN, 'client.hmGet', async client => { + testUtils.testWithClient('client.hmGet', async client => { assert.deepEqual( await client.hmGet('key', 'field'), [null] ); - }); + }, GLOBAL.SERVERS.OPEN); }); diff --git a/lib/commands/HMGET.ts b/packages/client/lib/commands/HMGET.ts similarity index 100% rename from lib/commands/HMGET.ts rename to packages/client/lib/commands/HMGET.ts diff --git a/lib/commands/HRANDFIELD.spec.ts b/packages/client/lib/commands/HRANDFIELD.spec.ts similarity index 62% rename from lib/commands/HRANDFIELD.spec.ts rename to packages/client/lib/commands/HRANDFIELD.spec.ts index 70e2585cf93..df0a4fc7a1d 100644 --- a/lib/commands/HRANDFIELD.spec.ts +++ b/packages/client/lib/commands/HRANDFIELD.spec.ts @@ -1,9 +1,9 @@ import { strict as assert } from 'assert'; -import { TestRedisServers, itWithClient, describeHandleMinimumRedisVersion } from '../test-utils'; +import testUtils, { GLOBAL } from '../test-utils'; import { transformArguments } from './HRANDFIELD'; describe('HRANDFIELD', () => { - describeHandleMinimumRedisVersion([6, 2]); + testUtils.isVersionGreaterThanHook([6, 2]); it('transformArguments', () => { assert.deepEqual( @@ -12,10 +12,10 @@ describe('HRANDFIELD', () => { ); }); - itWithClient(TestRedisServers.OPEN, 'client.hRandField', async client => { + testUtils.testWithClient('client.hRandField', async client => { assert.equal( await client.hRandField('key'), null ); - }); + }, GLOBAL.SERVERS.OPEN); }); diff --git a/lib/commands/HRANDFIELD.ts b/packages/client/lib/commands/HRANDFIELD.ts similarity index 100% rename from lib/commands/HRANDFIELD.ts rename to packages/client/lib/commands/HRANDFIELD.ts diff --git a/lib/commands/HRANDFIELD_COUNT.spec.ts b/packages/client/lib/commands/HRANDFIELD_COUNT.spec.ts similarity index 63% rename from lib/commands/HRANDFIELD_COUNT.spec.ts rename to packages/client/lib/commands/HRANDFIELD_COUNT.spec.ts index 6954bd484af..4bfce0f7e23 100644 --- a/lib/commands/HRANDFIELD_COUNT.spec.ts +++ b/packages/client/lib/commands/HRANDFIELD_COUNT.spec.ts @@ -1,9 +1,9 @@ import { strict as assert } from 'assert'; -import { TestRedisServers, itWithClient, describeHandleMinimumRedisVersion } from '../test-utils'; +import testUtils, { GLOBAL } from '../test-utils'; import { transformArguments } from './HRANDFIELD_COUNT'; describe('HRANDFIELD COUNT', () => { - describeHandleMinimumRedisVersion([6, 2, 5]); + testUtils.isVersionGreaterThanHook([6, 2, 5]); it('transformArguments', () => { assert.deepEqual( @@ -12,10 +12,10 @@ describe('HRANDFIELD COUNT', () => { ); }); - itWithClient(TestRedisServers.OPEN, 'client.hRandFieldCount', async client => { + testUtils.testWithClient('client.hRandFieldCount', async client => { assert.deepEqual( await client.hRandFieldCount('key', 1), [] ); - }); + }, GLOBAL.SERVERS.OPEN); }); diff --git a/lib/commands/HRANDFIELD_COUNT.ts b/packages/client/lib/commands/HRANDFIELD_COUNT.ts similarity index 100% rename from lib/commands/HRANDFIELD_COUNT.ts rename to packages/client/lib/commands/HRANDFIELD_COUNT.ts diff --git a/lib/commands/HRANDFIELD_COUNT_WITHVALUES.spec.ts b/packages/client/lib/commands/HRANDFIELD_COUNT_WITHVALUES.spec.ts similarity index 65% rename from lib/commands/HRANDFIELD_COUNT_WITHVALUES.spec.ts rename to packages/client/lib/commands/HRANDFIELD_COUNT_WITHVALUES.spec.ts index 0c26cbc7938..c4e6409a726 100644 --- a/lib/commands/HRANDFIELD_COUNT_WITHVALUES.spec.ts +++ b/packages/client/lib/commands/HRANDFIELD_COUNT_WITHVALUES.spec.ts @@ -1,9 +1,9 @@ import { strict as assert } from 'assert'; -import { TestRedisServers, itWithClient, describeHandleMinimumRedisVersion } from '../test-utils'; +import testUtils, { GLOBAL } from '../test-utils'; import { transformArguments } from './HRANDFIELD_COUNT_WITHVALUES'; describe('HRANDFIELD COUNT WITHVALUES', () => { - describeHandleMinimumRedisVersion([6, 2, 5]); + testUtils.isVersionGreaterThanHook([6, 2, 5]); it('transformArguments', () => { assert.deepEqual( @@ -12,10 +12,10 @@ describe('HRANDFIELD COUNT WITHVALUES', () => { ); }); - itWithClient(TestRedisServers.OPEN, 'client.hRandFieldCountWithValues', async client => { + testUtils.testWithClient('client.hRandFieldCountWithValues', async client => { assert.deepEqual( await client.hRandFieldCountWithValues('key', 1), Object.create(null) ); - }); + }, GLOBAL.SERVERS.OPEN); }); diff --git a/lib/commands/HRANDFIELD_COUNT_WITHVALUES.ts b/packages/client/lib/commands/HRANDFIELD_COUNT_WITHVALUES.ts similarity index 74% rename from lib/commands/HRANDFIELD_COUNT_WITHVALUES.ts rename to packages/client/lib/commands/HRANDFIELD_COUNT_WITHVALUES.ts index 53856c1984e..1769cae0b76 100644 --- a/lib/commands/HRANDFIELD_COUNT_WITHVALUES.ts +++ b/packages/client/lib/commands/HRANDFIELD_COUNT_WITHVALUES.ts @@ -1,4 +1,3 @@ -import { transformReplyTuples } from './generic-transformers'; import { transformArguments as transformHRandFieldCountArguments } from './HRANDFIELD_COUNT'; export { FIRST_KEY_INDEX } from './HRANDFIELD_COUNT'; @@ -10,4 +9,4 @@ export function transformArguments(key: string, count: number): Array { ]; } -export const transformReply = transformReplyTuples; +export { transformReplyTuples as transformReply } from './generic-transformers'; diff --git a/lib/commands/HSCAN.spec.ts b/packages/client/lib/commands/HSCAN.spec.ts similarity index 93% rename from lib/commands/HSCAN.spec.ts rename to packages/client/lib/commands/HSCAN.spec.ts index 7441dd48d52..b426763b99b 100644 --- a/lib/commands/HSCAN.spec.ts +++ b/packages/client/lib/commands/HSCAN.spec.ts @@ -1,5 +1,5 @@ import { strict as assert } from 'assert'; -import { TestRedisServers, itWithClient } from '../test-utils'; +import testUtils, { GLOBAL } from '../test-utils'; import { transformArguments, transformReply } from './HSCAN'; describe('HSCAN', () => { @@ -65,7 +65,7 @@ describe('HSCAN', () => { }); }); - itWithClient(TestRedisServers.OPEN, 'client.hScan', async client => { + testUtils.testWithClient('client.hScan', async client => { assert.deepEqual( await client.hScan('key', 0), { @@ -73,5 +73,5 @@ describe('HSCAN', () => { tuples: [] } ); - }); + }, GLOBAL.SERVERS.OPEN); }); diff --git a/lib/commands/HSCAN.ts b/packages/client/lib/commands/HSCAN.ts similarity index 100% rename from lib/commands/HSCAN.ts rename to packages/client/lib/commands/HSCAN.ts diff --git a/lib/commands/HSET.spec.ts b/packages/client/lib/commands/HSET.spec.ts similarity index 81% rename from lib/commands/HSET.spec.ts rename to packages/client/lib/commands/HSET.spec.ts index e8dfe7865d3..507c7bbbf74 100644 --- a/lib/commands/HSET.spec.ts +++ b/packages/client/lib/commands/HSET.spec.ts @@ -1,6 +1,6 @@ import { strict as assert } from 'assert'; import { transformArguments } from './HSET'; -import { TestRedisServers, itWithClient, TestRedisClusters, itWithCluster } from '../test-utils'; +import testUtils, { GLOBAL } from '../test-utils'; describe('HSET', () => { describe('transformArguments', () => { @@ -33,17 +33,17 @@ describe('HSET', () => { }); }); - itWithClient(TestRedisServers.OPEN, 'client.hSet', async client => { + testUtils.testWithClient('client.hSet', async client => { assert.equal( await client.hSet('key', 'field', 'value'), 1 ); - }); + }, GLOBAL.SERVERS.OPEN); - itWithCluster(TestRedisClusters.OPEN, 'cluster.hSet', async cluster => { + testUtils.testWithCluster('cluster.hSet', async cluster => { assert.equal( await cluster.hSet('key', { field: 'value' }), 1 ); - }); + }, GLOBAL.CLUSTERS.OPEN); }); \ No newline at end of file diff --git a/lib/commands/HSET.ts b/packages/client/lib/commands/HSET.ts similarity index 92% rename from lib/commands/HSET.ts rename to packages/client/lib/commands/HSET.ts index 1d4acd6c018..f7c56c5768d 100644 --- a/lib/commands/HSET.ts +++ b/packages/client/lib/commands/HSET.ts @@ -23,7 +23,7 @@ export function transformArguments(...[ key, value, fieldValue ]: SingleFieldArg pushMap(args, value); } else if (Array.isArray(value)) { pushTuples(args, value); - } else if (typeof value === 'object' && value !== null) { + } else { pushObject(args, value); } @@ -46,4 +46,4 @@ function pushObject(args: Array, object: HSETObject): void { } } -export declare function transformReply(): string; +export declare function transformReply(): number; diff --git a/lib/commands/HSETNX.spec.ts b/packages/client/lib/commands/HSETNX.spec.ts similarity index 73% rename from lib/commands/HSETNX.spec.ts rename to packages/client/lib/commands/HSETNX.spec.ts index f810c5e2b9a..190fa50ae97 100644 --- a/lib/commands/HSETNX.spec.ts +++ b/packages/client/lib/commands/HSETNX.spec.ts @@ -1,5 +1,5 @@ import { strict as assert } from 'assert'; -import { TestRedisServers, itWithClient } from '../test-utils'; +import testUtils, { GLOBAL } from '../test-utils'; import { transformArguments } from './HSETNX'; describe('HSETNX', () => { @@ -10,10 +10,10 @@ describe('HSETNX', () => { ); }); - itWithClient(TestRedisServers.OPEN, 'client.hSetNX', async client => { + testUtils.testWithClient('client.hSetNX', async client => { assert.equal( await client.hSetNX('key', 'field', 'value'), true ); - }); + }, GLOBAL.SERVERS.OPEN); }); diff --git a/lib/commands/HSETNX.ts b/packages/client/lib/commands/HSETNX.ts similarity index 59% rename from lib/commands/HSETNX.ts rename to packages/client/lib/commands/HSETNX.ts index 0eef8752529..83112f8945e 100644 --- a/lib/commands/HSETNX.ts +++ b/packages/client/lib/commands/HSETNX.ts @@ -1,9 +1,7 @@ -import { transformReplyBoolean } from './generic-transformers'; - export const FIRST_KEY_INDEX = 1; export function transformArguments(key: string, field: string, value: string): Array { return ['HSETNX', key, field, value]; } -export const transformReply = transformReplyBoolean; +export { transformReplyBoolean as transformReply } from './generic-transformers'; diff --git a/lib/commands/HSTRLEN.spec.ts b/packages/client/lib/commands/HSTRLEN.spec.ts similarity index 72% rename from lib/commands/HSTRLEN.spec.ts rename to packages/client/lib/commands/HSTRLEN.spec.ts index 35bf08d54c4..79c3150211e 100644 --- a/lib/commands/HSTRLEN.spec.ts +++ b/packages/client/lib/commands/HSTRLEN.spec.ts @@ -1,5 +1,5 @@ import { strict as assert } from 'assert'; -import { TestRedisServers, itWithClient } from '../test-utils'; +import testUtils, { GLOBAL } from '../test-utils'; import { transformArguments } from './HSTRLEN'; describe('HSTRLEN', () => { @@ -10,10 +10,10 @@ describe('HSTRLEN', () => { ); }); - itWithClient(TestRedisServers.OPEN, 'client.hStrLen', async client => { + testUtils.testWithClient('client.hStrLen', async client => { assert.equal( await client.hStrLen('key', 'field'), 0 ); - }); + }, GLOBAL.SERVERS.OPEN); }); diff --git a/lib/commands/HSTRLEN.ts b/packages/client/lib/commands/HSTRLEN.ts similarity index 100% rename from lib/commands/HSTRLEN.ts rename to packages/client/lib/commands/HSTRLEN.ts diff --git a/lib/commands/HVALS.spec.ts b/packages/client/lib/commands/HVALS.spec.ts similarity index 71% rename from lib/commands/HVALS.spec.ts rename to packages/client/lib/commands/HVALS.spec.ts index 9e6451f500e..d0a6c39ce5f 100644 --- a/lib/commands/HVALS.spec.ts +++ b/packages/client/lib/commands/HVALS.spec.ts @@ -1,5 +1,5 @@ import { strict as assert } from 'assert'; -import { TestRedisServers, itWithClient } from '../test-utils'; +import testUtils, { GLOBAL } from '../test-utils'; import { transformArguments } from './HVALS'; describe('HVALS', () => { @@ -10,10 +10,10 @@ describe('HVALS', () => { ); }); - itWithClient(TestRedisServers.OPEN, 'client.hVals', async client => { + testUtils.testWithClient('client.hVals', async client => { assert.deepEqual( await client.hVals('key'), [] ); - }); + }, GLOBAL.SERVERS.OPEN); }); diff --git a/lib/commands/HVALS.ts b/packages/client/lib/commands/HVALS.ts similarity index 100% rename from lib/commands/HVALS.ts rename to packages/client/lib/commands/HVALS.ts diff --git a/lib/commands/INCR.spec.ts b/packages/client/lib/commands/INCR.spec.ts similarity index 70% rename from lib/commands/INCR.spec.ts rename to packages/client/lib/commands/INCR.spec.ts index d64c3696af4..321d83edc54 100644 --- a/lib/commands/INCR.spec.ts +++ b/packages/client/lib/commands/INCR.spec.ts @@ -1,5 +1,5 @@ import { strict as assert } from 'assert'; -import { TestRedisServers, itWithClient } from '../test-utils'; +import testUtils, { GLOBAL } from '../test-utils'; import { transformArguments } from './INCR'; describe('INCR', () => { @@ -10,10 +10,10 @@ describe('INCR', () => { ); }); - itWithClient(TestRedisServers.OPEN, 'client.incr', async client => { + testUtils.testWithClient('client.incr', async client => { assert.equal( await client.incr('key'), 1 ); - }); + }, GLOBAL.SERVERS.OPEN); }); diff --git a/lib/commands/INCR.ts b/packages/client/lib/commands/INCR.ts similarity index 100% rename from lib/commands/INCR.ts rename to packages/client/lib/commands/INCR.ts diff --git a/lib/commands/INCRBY.spec.ts b/packages/client/lib/commands/INCRBY.spec.ts similarity index 71% rename from lib/commands/INCRBY.spec.ts rename to packages/client/lib/commands/INCRBY.spec.ts index 875277570cb..a671d0ec259 100644 --- a/lib/commands/INCRBY.spec.ts +++ b/packages/client/lib/commands/INCRBY.spec.ts @@ -1,5 +1,5 @@ import { strict as assert } from 'assert'; -import { TestRedisServers, itWithClient } from '../test-utils'; +import testUtils, { GLOBAL } from '../test-utils'; import { transformArguments } from './INCRBY'; describe('INCR', () => { @@ -10,10 +10,10 @@ describe('INCR', () => { ); }); - itWithClient(TestRedisServers.OPEN, 'client.incrBy', async client => { + testUtils.testWithClient('client.incrBy', async client => { assert.equal( await client.incrBy('key', 1), 1 ); - }); + }, GLOBAL.SERVERS.OPEN); }); diff --git a/lib/commands/INCRBY.ts b/packages/client/lib/commands/INCRBY.ts similarity index 100% rename from lib/commands/INCRBY.ts rename to packages/client/lib/commands/INCRBY.ts diff --git a/lib/commands/INCRBYFLOAT.spec.ts b/packages/client/lib/commands/INCRBYFLOAT.spec.ts similarity index 72% rename from lib/commands/INCRBYFLOAT.spec.ts rename to packages/client/lib/commands/INCRBYFLOAT.spec.ts index fe062b62905..b2dd5aa5da9 100644 --- a/lib/commands/INCRBYFLOAT.spec.ts +++ b/packages/client/lib/commands/INCRBYFLOAT.spec.ts @@ -1,5 +1,5 @@ import { strict as assert } from 'assert'; -import { TestRedisServers, itWithClient } from '../test-utils'; +import testUtils, { GLOBAL } from '../test-utils'; import { transformArguments } from './INCRBYFLOAT'; describe('INCRBYFLOAT', () => { @@ -10,10 +10,10 @@ describe('INCRBYFLOAT', () => { ); }); - itWithClient(TestRedisServers.OPEN, 'client.incrByFloat', async client => { + testUtils.testWithClient('client.incrByFloat', async client => { assert.equal( await client.incrByFloat('key', 1.5), '1.5' ); - }); + }, GLOBAL.SERVERS.OPEN); }); diff --git a/lib/commands/INCRBYFLOAT.ts b/packages/client/lib/commands/INCRBYFLOAT.ts similarity index 100% rename from lib/commands/INCRBYFLOAT.ts rename to packages/client/lib/commands/INCRBYFLOAT.ts diff --git a/lib/commands/INFO.spec.ts b/packages/client/lib/commands/INFO.spec.ts similarity index 100% rename from lib/commands/INFO.spec.ts rename to packages/client/lib/commands/INFO.spec.ts diff --git a/lib/commands/INFO.ts b/packages/client/lib/commands/INFO.ts similarity index 100% rename from lib/commands/INFO.ts rename to packages/client/lib/commands/INFO.ts diff --git a/lib/commands/KEYS.spec.ts b/packages/client/lib/commands/KEYS.spec.ts similarity index 53% rename from lib/commands/KEYS.spec.ts rename to packages/client/lib/commands/KEYS.spec.ts index d11e8a0f58b..c066331ea7c 100644 --- a/lib/commands/KEYS.spec.ts +++ b/packages/client/lib/commands/KEYS.spec.ts @@ -1,11 +1,11 @@ import { strict as assert } from 'assert'; -import { TestRedisServers, itWithClient } from '../test-utils'; +import testUtils, { GLOBAL } from '../test-utils'; describe('KEYS', () => { - itWithClient(TestRedisServers.OPEN, 'client.keys', async client => { + testUtils.testWithClient('client.keys', async client => { assert.deepEqual( await client.keys('pattern'), [] ); - }); + }, GLOBAL.SERVERS.OPEN); }); diff --git a/lib/commands/KEYS.ts b/packages/client/lib/commands/KEYS.ts similarity index 100% rename from lib/commands/KEYS.ts rename to packages/client/lib/commands/KEYS.ts diff --git a/lib/commands/LASTSAVE.spec.ts b/packages/client/lib/commands/LASTSAVE.spec.ts similarity index 68% rename from lib/commands/LASTSAVE.spec.ts rename to packages/client/lib/commands/LASTSAVE.spec.ts index b8d801f70b5..a6b4863f39e 100644 --- a/lib/commands/LASTSAVE.spec.ts +++ b/packages/client/lib/commands/LASTSAVE.spec.ts @@ -1,5 +1,5 @@ import { strict as assert } from 'assert'; -import { TestRedisServers, itWithClient } from '../test-utils'; +import testUtils, { GLOBAL } from '../test-utils'; import { transformArguments } from './LASTSAVE'; describe('LASTSAVE', () => { @@ -10,7 +10,7 @@ describe('LASTSAVE', () => { ); }); - itWithClient(TestRedisServers.OPEN, 'client.lastSave', async client => { + testUtils.testWithClient('client.lastSave', async client => { assert.ok((await client.lastSave()) instanceof Date); - }); + }, GLOBAL.SERVERS.OPEN); }); diff --git a/lib/commands/LASTSAVE.ts b/packages/client/lib/commands/LASTSAVE.ts similarity index 100% rename from lib/commands/LASTSAVE.ts rename to packages/client/lib/commands/LASTSAVE.ts diff --git a/lib/commands/LINDEX.spec.ts b/packages/client/lib/commands/LINDEX.spec.ts similarity index 65% rename from lib/commands/LINDEX.spec.ts rename to packages/client/lib/commands/LINDEX.spec.ts index 74a6706ecdc..5e0b1473ec4 100644 --- a/lib/commands/LINDEX.spec.ts +++ b/packages/client/lib/commands/LINDEX.spec.ts @@ -1,5 +1,5 @@ import { strict as assert } from 'assert'; -import { TestRedisServers, itWithClient, itWithCluster, TestRedisClusters } from '../test-utils'; +import testUtils, { GLOBAL } from '../test-utils'; import { transformArguments } from './LINDEX'; describe('LINDEX', () => { @@ -10,17 +10,17 @@ describe('LINDEX', () => { ); }); - itWithClient(TestRedisServers.OPEN, 'client.lIndex', async client => { + testUtils.testWithClient('client.lIndex', async client => { assert.equal( await client.lIndex('key', 'element'), null ); - }); + }, GLOBAL.SERVERS.OPEN); - itWithCluster(TestRedisClusters.OPEN, 'cluster.lIndex', async cluster => { + testUtils.testWithCluster('cluster.lIndex', async cluster => { assert.equal( await cluster.lIndex('key', 'element'), null ); - }); + }, GLOBAL.CLUSTERS.OPEN); }); diff --git a/lib/commands/LINDEX.ts b/packages/client/lib/commands/LINDEX.ts similarity index 77% rename from lib/commands/LINDEX.ts rename to packages/client/lib/commands/LINDEX.ts index 9a89b41da55..4c283f0912c 100644 --- a/lib/commands/LINDEX.ts +++ b/packages/client/lib/commands/LINDEX.ts @@ -6,4 +6,4 @@ export function transformArguments(key: string, element: string): Array return ['LINDEX', key, element]; } -export declare function transformReply(): number | null; +export declare function transformReply(): string | null; diff --git a/lib/commands/LINSERT.spec.ts b/packages/client/lib/commands/LINSERT.spec.ts similarity index 68% rename from lib/commands/LINSERT.spec.ts rename to packages/client/lib/commands/LINSERT.spec.ts index 286e61d06d6..6cc429d6a2c 100644 --- a/lib/commands/LINSERT.spec.ts +++ b/packages/client/lib/commands/LINSERT.spec.ts @@ -1,5 +1,5 @@ import { strict as assert } from 'assert'; -import { TestRedisServers, itWithClient, itWithCluster, TestRedisClusters } from '../test-utils'; +import testUtils, { GLOBAL } from '../test-utils'; import { transformArguments } from './LINSERT'; describe('LINSERT', () => { @@ -10,17 +10,17 @@ describe('LINSERT', () => { ); }); - itWithClient(TestRedisServers.OPEN, 'client.lInsert', async client => { + testUtils.testWithClient('client.lInsert', async client => { assert.equal( await client.lInsert('key', 'BEFORE', 'pivot', 'element'), 0 ); - }); + }, GLOBAL.SERVERS.OPEN); - itWithCluster(TestRedisClusters.OPEN, 'cluster.lLen', async cluster => { + testUtils.testWithCluster('cluster.lLen', async cluster => { assert.equal( await cluster.lInsert('key', 'BEFORE', 'pivot', 'element'), 0 ); - }); + }, GLOBAL.CLUSTERS.OPEN); }); diff --git a/lib/commands/LINSERT.ts b/packages/client/lib/commands/LINSERT.ts similarity index 100% rename from lib/commands/LINSERT.ts rename to packages/client/lib/commands/LINSERT.ts diff --git a/lib/commands/LLEN.spec.ts b/packages/client/lib/commands/LLEN.spec.ts similarity index 62% rename from lib/commands/LLEN.spec.ts rename to packages/client/lib/commands/LLEN.spec.ts index 6e4581ddd1f..fb126ddad55 100644 --- a/lib/commands/LLEN.spec.ts +++ b/packages/client/lib/commands/LLEN.spec.ts @@ -1,5 +1,5 @@ import { strict as assert } from 'assert'; -import { TestRedisServers, itWithClient, itWithCluster, TestRedisClusters } from '../test-utils'; +import testUtils, { GLOBAL } from '../test-utils'; import { transformArguments } from './LLEN'; describe('LLEN', () => { @@ -10,17 +10,17 @@ describe('LLEN', () => { ); }); - itWithClient(TestRedisServers.OPEN, 'client.lLen', async client => { + testUtils.testWithClient('client.lLen', async client => { assert.equal( await client.lLen('key'), 0 ); - }); + }, GLOBAL.SERVERS.OPEN); - itWithCluster(TestRedisClusters.OPEN, 'cluster.lLen', async cluster => { + testUtils.testWithCluster('cluster.lLen', async cluster => { assert.equal( await cluster.lLen('key'), 0 ); - }); + }, GLOBAL.CLUSTERS.OPEN); }); diff --git a/lib/commands/LLEN.ts b/packages/client/lib/commands/LLEN.ts similarity index 100% rename from lib/commands/LLEN.ts rename to packages/client/lib/commands/LLEN.ts diff --git a/lib/commands/LMOVE.spec.ts b/packages/client/lib/commands/LMOVE.spec.ts similarity index 63% rename from lib/commands/LMOVE.spec.ts rename to packages/client/lib/commands/LMOVE.spec.ts index bcb897f76ac..f1d418c394e 100644 --- a/lib/commands/LMOVE.spec.ts +++ b/packages/client/lib/commands/LMOVE.spec.ts @@ -1,9 +1,9 @@ import { strict as assert } from 'assert'; -import { TestRedisServers, itWithClient, itWithCluster, TestRedisClusters, describeHandleMinimumRedisVersion } from '../test-utils'; +import testUtils, { GLOBAL } from '../test-utils'; import { transformArguments } from './LMOVE'; describe('LMOVE', () => { - describeHandleMinimumRedisVersion([6, 2]); + testUtils.isVersionGreaterThanHook([6, 2]); it('transformArguments', () => { assert.deepEqual( @@ -12,17 +12,17 @@ describe('LMOVE', () => { ); }); - itWithClient(TestRedisServers.OPEN, 'client.lMove', async client => { + testUtils.testWithClient('client.lMove', async client => { assert.equal( await client.lMove('source', 'destination', 'LEFT', 'RIGHT'), null ); - }); + }, GLOBAL.SERVERS.OPEN); - itWithCluster(TestRedisClusters.OPEN, 'cluster.lMove', async cluster => { + testUtils.testWithCluster('cluster.lMove', async cluster => { assert.equal( await cluster.lMove('{tag}source', '{tag}destination', 'LEFT', 'RIGHT'), null ); - }); + }, GLOBAL.CLUSTERS.OPEN); }); diff --git a/lib/commands/LMOVE.ts b/packages/client/lib/commands/LMOVE.ts similarity index 100% rename from lib/commands/LMOVE.ts rename to packages/client/lib/commands/LMOVE.ts diff --git a/lib/commands/LOLWUT.spec.ts b/packages/client/lib/commands/LOLWUT.spec.ts similarity index 84% rename from lib/commands/LOLWUT.spec.ts rename to packages/client/lib/commands/LOLWUT.spec.ts index 8f4478aecc7..db335893302 100644 --- a/lib/commands/LOLWUT.spec.ts +++ b/packages/client/lib/commands/LOLWUT.spec.ts @@ -1,5 +1,5 @@ import { strict as assert } from 'assert'; -import { TestRedisServers, itWithClient } from '../test-utils'; +import testUtils, { GLOBAL } from '../test-utils'; import { transformArguments } from './LOLWUT'; describe('LOLWUT', () => { @@ -26,11 +26,10 @@ describe('LOLWUT', () => { }); }); - - itWithClient(TestRedisServers.OPEN, 'client.LOLWUT', async client => { + testUtils.testWithClient('client.LOLWUT', async client => { assert.equal( typeof (await client.LOLWUT()), 'string' ); - }); + }, GLOBAL.SERVERS.OPEN); }); diff --git a/lib/commands/LOLWUT.ts b/packages/client/lib/commands/LOLWUT.ts similarity index 100% rename from lib/commands/LOLWUT.ts rename to packages/client/lib/commands/LOLWUT.ts diff --git a/lib/commands/LPOP.spec.ts b/packages/client/lib/commands/LPOP.spec.ts similarity index 62% rename from lib/commands/LPOP.spec.ts rename to packages/client/lib/commands/LPOP.spec.ts index b593f657427..d694fb10588 100644 --- a/lib/commands/LPOP.spec.ts +++ b/packages/client/lib/commands/LPOP.spec.ts @@ -1,5 +1,5 @@ import { strict as assert } from 'assert'; -import { TestRedisServers, itWithClient, itWithCluster, TestRedisClusters } from '../test-utils'; +import testUtils, { GLOBAL } from '../test-utils'; import { transformArguments } from './LPOP'; describe('LPOP', () => { @@ -10,17 +10,17 @@ describe('LPOP', () => { ); }); - itWithClient(TestRedisServers.OPEN, 'client.lPop', async client => { + testUtils.testWithClient('client.lPop', async client => { assert.equal( await client.lPop('key'), null ); - }); + }, GLOBAL.SERVERS.OPEN); - itWithCluster(TestRedisClusters.OPEN, 'cluster.lPop', async cluster => { + testUtils.testWithCluster('cluster.lPop', async cluster => { assert.equal( await cluster.lPop('key'), null ); - }); + }, GLOBAL.CLUSTERS.OPEN); }); diff --git a/lib/commands/LPOP.ts b/packages/client/lib/commands/LPOP.ts similarity index 100% rename from lib/commands/LPOP.ts rename to packages/client/lib/commands/LPOP.ts diff --git a/lib/commands/LPOP_COUNT.spec.ts b/packages/client/lib/commands/LPOP_COUNT.spec.ts similarity index 57% rename from lib/commands/LPOP_COUNT.spec.ts rename to packages/client/lib/commands/LPOP_COUNT.spec.ts index 89150dbf4de..9d87fad3862 100644 --- a/lib/commands/LPOP_COUNT.spec.ts +++ b/packages/client/lib/commands/LPOP_COUNT.spec.ts @@ -1,9 +1,9 @@ import { strict as assert } from 'assert'; -import { TestRedisServers, itWithClient, itWithCluster, TestRedisClusters, describeHandleMinimumRedisVersion } from '../test-utils'; +import testUtils, { GLOBAL } from '../test-utils'; import { transformArguments } from './LPOP_COUNT'; describe('LPOP COUNT', () => { - describeHandleMinimumRedisVersion([6, 2]); + testUtils.isVersionGreaterThanHook([6, 2]); it('transformArguments', () => { assert.deepEqual( @@ -12,17 +12,17 @@ describe('LPOP COUNT', () => { ); }); - itWithClient(TestRedisServers.OPEN, 'client.lPopCount', async client => { + testUtils.testWithClient('client.lPopCount', async client => { assert.equal( await client.lPopCount('key', 1), null ); - }); + }, GLOBAL.SERVERS.OPEN); - itWithCluster(TestRedisClusters.OPEN, 'cluster.lPop', async cluster => { + testUtils.testWithCluster('cluster.lPopCount', async cluster => { assert.equal( await cluster.lPopCount('key', 1), null ); - }); + }, GLOBAL.CLUSTERS.OPEN); }); diff --git a/lib/commands/LPOP_COUNT.ts b/packages/client/lib/commands/LPOP_COUNT.ts similarity index 100% rename from lib/commands/LPOP_COUNT.ts rename to packages/client/lib/commands/LPOP_COUNT.ts diff --git a/lib/commands/LPOS.spec.ts b/packages/client/lib/commands/LPOS.spec.ts similarity index 79% rename from lib/commands/LPOS.spec.ts rename to packages/client/lib/commands/LPOS.spec.ts index 1cf9e35209d..6b6050f2c3b 100644 --- a/lib/commands/LPOS.spec.ts +++ b/packages/client/lib/commands/LPOS.spec.ts @@ -1,9 +1,9 @@ import { strict as assert } from 'assert'; -import { TestRedisServers, itWithClient, TestRedisClusters, itWithCluster, describeHandleMinimumRedisVersion } from '../test-utils'; +import testUtils, { GLOBAL } from '../test-utils'; import { transformArguments } from './LPOS'; describe('LPOS', () => { - describeHandleMinimumRedisVersion([6, 0, 6]); + testUtils.isVersionGreaterThanHook([6, 0, 6]); describe('transformArguments', () => { it('simple', () => { @@ -42,17 +42,17 @@ describe('LPOS', () => { }); }); - itWithClient(TestRedisServers.OPEN, 'client.lPos', async client => { + testUtils.testWithClient('client.lPos', async client => { assert.equal( await client.lPos('key', 'element'), null ); - }); + }, GLOBAL.SERVERS.OPEN); - itWithCluster(TestRedisClusters.OPEN, 'cluster.lPos', async cluster => { + testUtils.testWithCluster('cluster.lPos', async cluster => { assert.equal( await cluster.lPos('key', 'element'), null ); - }); + }, GLOBAL.CLUSTERS.OPEN); }); diff --git a/lib/commands/LPOS.ts b/packages/client/lib/commands/LPOS.ts similarity index 100% rename from lib/commands/LPOS.ts rename to packages/client/lib/commands/LPOS.ts diff --git a/lib/commands/LPOS_COUNT.spec.ts b/packages/client/lib/commands/LPOS_COUNT.spec.ts similarity index 80% rename from lib/commands/LPOS_COUNT.spec.ts rename to packages/client/lib/commands/LPOS_COUNT.spec.ts index 1d80bd45c3c..4b01f2f59b9 100644 --- a/lib/commands/LPOS_COUNT.spec.ts +++ b/packages/client/lib/commands/LPOS_COUNT.spec.ts @@ -1,9 +1,9 @@ import { strict as assert } from 'assert'; -import { TestRedisServers, itWithClient, itWithCluster, TestRedisClusters, describeHandleMinimumRedisVersion } from '../test-utils'; +import testUtils, { GLOBAL } from '../test-utils'; import { transformArguments } from './LPOS_COUNT'; describe('LPOS COUNT', () => { - describeHandleMinimumRedisVersion([6, 0, 6]); + testUtils.isVersionGreaterThanHook([6, 0, 6]); describe('transformArguments', () => { it('simple', () => { @@ -42,17 +42,17 @@ describe('LPOS COUNT', () => { }); }); - itWithClient(TestRedisServers.OPEN, 'client.lPosCount', async client => { + testUtils.testWithClient('client.lPosCount', async client => { assert.deepEqual( await client.lPosCount('key', 'element', 0), [] ); - }); + }, GLOBAL.SERVERS.OPEN); - itWithCluster(TestRedisClusters.OPEN, 'cluster.lPosCount', async cluster => { + testUtils.testWithCluster('cluster.lPosCount', async cluster => { assert.deepEqual( await cluster.lPosCount('key', 'element', 0), [] ); - }); + }, GLOBAL.CLUSTERS.OPEN); }); diff --git a/lib/commands/LPOS_COUNT.ts b/packages/client/lib/commands/LPOS_COUNT.ts similarity index 100% rename from lib/commands/LPOS_COUNT.ts rename to packages/client/lib/commands/LPOS_COUNT.ts diff --git a/lib/commands/LPUSH.spec.ts b/packages/client/lib/commands/LPUSH.spec.ts similarity index 73% rename from lib/commands/LPUSH.spec.ts rename to packages/client/lib/commands/LPUSH.spec.ts index 44cf8c12d5f..b5b1f5084eb 100644 --- a/lib/commands/LPUSH.spec.ts +++ b/packages/client/lib/commands/LPUSH.spec.ts @@ -1,5 +1,5 @@ import { strict as assert } from 'assert'; -import { TestRedisServers, itWithClient, itWithCluster, TestRedisClusters } from '../test-utils'; +import testUtils, { GLOBAL } from '../test-utils'; import { transformArguments } from './LPUSH'; describe('LPUSH', () => { @@ -19,17 +19,17 @@ describe('LPUSH', () => { }); }); - itWithClient(TestRedisServers.OPEN, 'client.lPush', async client => { + testUtils.testWithClient('client.lPush', async client => { assert.equal( await client.lPush('key', 'field'), 1 ); - }); + }, GLOBAL.SERVERS.OPEN); - itWithCluster(TestRedisClusters.OPEN, 'cluster.lPush', async cluster => { + testUtils.testWithCluster('cluster.lPush', async cluster => { assert.equal( await cluster.lPush('key', 'field'), 1 ); - }); + }, GLOBAL.CLUSTERS.OPEN); }); diff --git a/lib/commands/LPUSH.ts b/packages/client/lib/commands/LPUSH.ts similarity index 100% rename from lib/commands/LPUSH.ts rename to packages/client/lib/commands/LPUSH.ts diff --git a/lib/commands/LPUSHX.spec.ts b/packages/client/lib/commands/LPUSHX.spec.ts similarity index 73% rename from lib/commands/LPUSHX.spec.ts rename to packages/client/lib/commands/LPUSHX.spec.ts index 1150c4d64dd..d978e5a588f 100644 --- a/lib/commands/LPUSHX.spec.ts +++ b/packages/client/lib/commands/LPUSHX.spec.ts @@ -1,5 +1,5 @@ import { strict as assert } from 'assert'; -import { TestRedisServers, itWithClient, itWithCluster, TestRedisClusters } from '../test-utils'; +import testUtils, { GLOBAL } from '../test-utils'; import { transformArguments } from './LPUSHX'; describe('LPUSHX', () => { @@ -19,17 +19,17 @@ describe('LPUSHX', () => { }); }); - itWithClient(TestRedisServers.OPEN, 'client.lPushX', async client => { + testUtils.testWithClient('client.lPushX', async client => { assert.equal( await client.lPushX('key', 'element'), 0 ); - }); + }, GLOBAL.SERVERS.OPEN); - itWithCluster(TestRedisClusters.OPEN, 'cluster.lPushX', async cluster => { + testUtils.testWithCluster('cluster.lPushX', async cluster => { assert.equal( await cluster.lPushX('key', 'element'), 0 ); - }); + }, GLOBAL.CLUSTERS.OPEN); }); diff --git a/lib/commands/LPUSHX.ts b/packages/client/lib/commands/LPUSHX.ts similarity index 100% rename from lib/commands/LPUSHX.ts rename to packages/client/lib/commands/LPUSHX.ts diff --git a/lib/commands/LRANGE.spec.ts b/packages/client/lib/commands/LRANGE.spec.ts similarity index 64% rename from lib/commands/LRANGE.spec.ts rename to packages/client/lib/commands/LRANGE.spec.ts index 843b7b8815e..dffe6087b80 100644 --- a/lib/commands/LRANGE.spec.ts +++ b/packages/client/lib/commands/LRANGE.spec.ts @@ -1,6 +1,6 @@ import { strict as assert } from 'assert'; -import { TestRedisServers, itWithClient, itWithCluster, TestRedisClusters } from '../test-utils'; +import testUtils, { GLOBAL } from '../test-utils'; import { transformArguments } from './LRANGE'; describe('LRANGE', () => { @@ -11,17 +11,17 @@ describe('LRANGE', () => { ); }); - itWithClient(TestRedisServers.OPEN, 'client.lRange', async client => { + testUtils.testWithClient('client.lRange', async client => { assert.deepEqual( await client.lRange('key', 0, -1), [] ); - }); + }, GLOBAL.SERVERS.OPEN); - itWithCluster(TestRedisClusters.OPEN, 'cluster.lRange', async cluster => { + testUtils.testWithCluster('cluster.lRange', async cluster => { assert.deepEqual( await cluster.lRange('key', 0, -1), [] ); - }); + }, GLOBAL.CLUSTERS.OPEN); }); diff --git a/lib/commands/LRANGE.ts b/packages/client/lib/commands/LRANGE.ts similarity index 100% rename from lib/commands/LRANGE.ts rename to packages/client/lib/commands/LRANGE.ts diff --git a/lib/commands/LREM.spec.ts b/packages/client/lib/commands/LREM.spec.ts similarity index 65% rename from lib/commands/LREM.spec.ts rename to packages/client/lib/commands/LREM.spec.ts index e2f027ffeb8..3405f4beb07 100644 --- a/lib/commands/LREM.spec.ts +++ b/packages/client/lib/commands/LREM.spec.ts @@ -1,6 +1,6 @@ import { strict as assert } from 'assert'; -import { TestRedisServers, itWithClient, itWithCluster, TestRedisClusters } from '../test-utils'; +import testUtils, { GLOBAL } from '../test-utils'; import { transformArguments } from './LREM'; describe('LREM', () => { @@ -11,17 +11,17 @@ describe('LREM', () => { ); }); - itWithClient(TestRedisServers.OPEN, 'client.lRem', async client => { + testUtils.testWithClient('client.lRem', async client => { assert.equal( await client.lRem('key', 0, 'element'), 0 ); - }); + }, GLOBAL.SERVERS.OPEN); - itWithCluster(TestRedisClusters.OPEN, 'cluster.lRem', async cluster => { + testUtils.testWithCluster('cluster.lRem', async cluster => { assert.equal( await cluster.lRem('key', 0, 'element'), 0 ); - }); + }, GLOBAL.CLUSTERS.OPEN); }); diff --git a/lib/commands/LREM.ts b/packages/client/lib/commands/LREM.ts similarity index 100% rename from lib/commands/LREM.ts rename to packages/client/lib/commands/LREM.ts diff --git a/lib/commands/LSET.spec.ts b/packages/client/lib/commands/LSET.spec.ts similarity index 69% rename from lib/commands/LSET.spec.ts rename to packages/client/lib/commands/LSET.spec.ts index a5fe78cf4c3..d7241032cc6 100644 --- a/lib/commands/LSET.spec.ts +++ b/packages/client/lib/commands/LSET.spec.ts @@ -1,5 +1,5 @@ import { strict as assert } from 'assert'; -import { TestRedisServers, itWithClient, itWithCluster, TestRedisClusters } from '../test-utils'; +import testUtils, { GLOBAL } from '../test-utils'; import { transformArguments } from './LSET'; describe('LSET', () => { @@ -10,19 +10,19 @@ describe('LSET', () => { ); }); - itWithClient(TestRedisServers.OPEN, 'client.lSet', async client => { + testUtils.testWithClient('client.lSet', async client => { await client.lPush('key', 'element'); assert.equal( await client.lSet('key', 0, 'element'), 'OK' ); - }); + }, GLOBAL.SERVERS.OPEN); - itWithCluster(TestRedisClusters.OPEN, 'cluster.lSet', async cluster => { + testUtils.testWithCluster('cluster.lSet', async cluster => { await cluster.lPush('key', 'element'); assert.equal( await cluster.lSet('key', 0, 'element'), 'OK' ); - }); + }, GLOBAL.CLUSTERS.OPEN); }); diff --git a/lib/commands/LSET.ts b/packages/client/lib/commands/LSET.ts similarity index 100% rename from lib/commands/LSET.ts rename to packages/client/lib/commands/LSET.ts diff --git a/lib/commands/LTRIM.spec.ts b/packages/client/lib/commands/LTRIM.spec.ts similarity index 64% rename from lib/commands/LTRIM.spec.ts rename to packages/client/lib/commands/LTRIM.spec.ts index 8092ba6af1e..5b6ac5d3660 100644 --- a/lib/commands/LTRIM.spec.ts +++ b/packages/client/lib/commands/LTRIM.spec.ts @@ -1,5 +1,5 @@ import { strict as assert } from 'assert'; -import { TestRedisServers, itWithClient, itWithCluster, TestRedisClusters } from '../test-utils'; +import testUtils, { GLOBAL } from '../test-utils'; import { transformArguments } from './LTRIM'; describe('LTRIM', () => { @@ -10,17 +10,17 @@ describe('LTRIM', () => { ); }); - itWithClient(TestRedisServers.OPEN, 'client.lTrim', async client => { + testUtils.testWithClient('client.lTrim', async client => { assert.equal( await client.lTrim('key', 0, -1), 'OK' ); - }); + }, GLOBAL.SERVERS.OPEN); - itWithCluster(TestRedisClusters.OPEN, 'cluster.lTrim', async cluster => { + testUtils.testWithCluster('cluster.lTrim', async cluster => { assert.equal( await cluster.lTrim('key', 0, -1), 'OK' ); - }); + }, GLOBAL.CLUSTERS.OPEN); }); diff --git a/lib/commands/LTRIM.ts b/packages/client/lib/commands/LTRIM.ts similarity index 97% rename from lib/commands/LTRIM.ts rename to packages/client/lib/commands/LTRIM.ts index 018afd90a15..ab78e5305aa 100644 --- a/lib/commands/LTRIM.ts +++ b/packages/client/lib/commands/LTRIM.ts @@ -6,7 +6,7 @@ export function transformArguments(key: string, start: number, stop: number): Ar key, start.toString(), stop.toString() - ] + ]; } export declare function transformReply(): string; diff --git a/lib/commands/MEMORY_DOCTOR.spec.ts b/packages/client/lib/commands/MEMORY_DOCTOR.spec.ts similarity index 71% rename from lib/commands/MEMORY_DOCTOR.spec.ts rename to packages/client/lib/commands/MEMORY_DOCTOR.spec.ts index 1b4d16fa0db..ad97047606c 100644 --- a/lib/commands/MEMORY_DOCTOR.spec.ts +++ b/packages/client/lib/commands/MEMORY_DOCTOR.spec.ts @@ -1,5 +1,5 @@ import { strict as assert } from 'assert'; -import { TestRedisServers, itWithClient } from '../test-utils'; +import testUtils, { GLOBAL } from '../test-utils'; import { transformArguments } from './MEMORY_DOCTOR'; describe('MEMORY DOCTOR', () => { @@ -10,10 +10,10 @@ describe('MEMORY DOCTOR', () => { ); }); - itWithClient(TestRedisServers.OPEN, 'client.memoryDoctor', async client => { + testUtils.testWithClient('client.memoryDoctor', async client => { assert.equal( typeof (await client.memoryDoctor()), 'string' ); - }); + }, GLOBAL.SERVERS.OPEN); }); diff --git a/lib/commands/MEMORY_DOCTOR.ts b/packages/client/lib/commands/MEMORY_DOCTOR.ts similarity index 100% rename from lib/commands/MEMORY_DOCTOR.ts rename to packages/client/lib/commands/MEMORY_DOCTOR.ts diff --git a/lib/commands/MEMORY_MALLOC-STATS.spec.ts b/packages/client/lib/commands/MEMORY_MALLOC-STATS.spec.ts similarity index 72% rename from lib/commands/MEMORY_MALLOC-STATS.spec.ts rename to packages/client/lib/commands/MEMORY_MALLOC-STATS.spec.ts index 034b94f7c66..ce866f1e116 100644 --- a/lib/commands/MEMORY_MALLOC-STATS.spec.ts +++ b/packages/client/lib/commands/MEMORY_MALLOC-STATS.spec.ts @@ -1,5 +1,5 @@ import { strict as assert } from 'assert'; -import { TestRedisServers, itWithClient } from '../test-utils'; +import testUtils, { GLOBAL } from '../test-utils'; import { transformArguments } from './MEMORY_MALLOC-STATS'; describe('MEMORY MALLOC-STATS', () => { @@ -10,10 +10,10 @@ describe('MEMORY MALLOC-STATS', () => { ); }); - itWithClient(TestRedisServers.OPEN, 'client.memoryMallocStats', async client => { + testUtils.testWithClient('client.memoryMallocStats', async client => { assert.equal( typeof (await client.memoryDoctor()), 'string' ); - }); + }, GLOBAL.SERVERS.OPEN); }); diff --git a/lib/commands/MEMORY_MALLOC-STATS.ts b/packages/client/lib/commands/MEMORY_MALLOC-STATS.ts similarity index 100% rename from lib/commands/MEMORY_MALLOC-STATS.ts rename to packages/client/lib/commands/MEMORY_MALLOC-STATS.ts diff --git a/lib/commands/MEMORY_PURGE.spec.ts b/packages/client/lib/commands/MEMORY_PURGE.spec.ts similarity index 71% rename from lib/commands/MEMORY_PURGE.spec.ts rename to packages/client/lib/commands/MEMORY_PURGE.spec.ts index 97ca6feebf6..5d34331feb6 100644 --- a/lib/commands/MEMORY_PURGE.spec.ts +++ b/packages/client/lib/commands/MEMORY_PURGE.spec.ts @@ -1,5 +1,5 @@ import { strict as assert } from 'assert'; -import { TestRedisServers, itWithClient } from '../test-utils'; +import testUtils, { GLOBAL } from '../test-utils'; import { transformArguments } from './MEMORY_PURGE'; describe('MEMORY PURGE', () => { @@ -10,10 +10,10 @@ describe('MEMORY PURGE', () => { ); }); - itWithClient(TestRedisServers.OPEN, 'client.memoryPurge', async client => { + testUtils.testWithClient('client.memoryPurge', async client => { assert.equal( await client.memoryPurge(), 'OK' ); - }); + }, GLOBAL.SERVERS.OPEN); }); diff --git a/lib/commands/MEMORY_PURGE.ts b/packages/client/lib/commands/MEMORY_PURGE.ts similarity index 100% rename from lib/commands/MEMORY_PURGE.ts rename to packages/client/lib/commands/MEMORY_PURGE.ts diff --git a/lib/commands/MEMORY_STATS.spec.ts b/packages/client/lib/commands/MEMORY_STATS.spec.ts similarity index 100% rename from lib/commands/MEMORY_STATS.spec.ts rename to packages/client/lib/commands/MEMORY_STATS.spec.ts diff --git a/lib/commands/MEMORY_STATS.ts b/packages/client/lib/commands/MEMORY_STATS.ts similarity index 100% rename from lib/commands/MEMORY_STATS.ts rename to packages/client/lib/commands/MEMORY_STATS.ts diff --git a/lib/commands/MEMORY_USAGE.spec.ts b/packages/client/lib/commands/MEMORY_USAGE.spec.ts similarity index 82% rename from lib/commands/MEMORY_USAGE.spec.ts rename to packages/client/lib/commands/MEMORY_USAGE.spec.ts index 90dff62c674..fe5ff404d93 100644 --- a/lib/commands/MEMORY_USAGE.spec.ts +++ b/packages/client/lib/commands/MEMORY_USAGE.spec.ts @@ -1,5 +1,5 @@ import { strict as assert } from 'assert'; -import { TestRedisServers, itWithClient } from '../test-utils'; +import testUtils, { GLOBAL } from '../test-utils'; import { transformArguments } from './MEMORY_USAGE'; describe('MEMORY USAGE', () => { @@ -21,10 +21,10 @@ describe('MEMORY USAGE', () => { }); }); - itWithClient(TestRedisServers.OPEN, 'client.memoryUsage', async client => { + testUtils.testWithClient('client.memoryUsage', async client => { assert.equal( await client.memoryUsage('key'), null ); - }); + }, GLOBAL.SERVERS.OPEN); }); diff --git a/lib/commands/MEMORY_USAGE.ts b/packages/client/lib/commands/MEMORY_USAGE.ts similarity index 100% rename from lib/commands/MEMORY_USAGE.ts rename to packages/client/lib/commands/MEMORY_USAGE.ts diff --git a/lib/commands/MGET.spec.ts b/packages/client/lib/commands/MGET.spec.ts similarity index 64% rename from lib/commands/MGET.spec.ts rename to packages/client/lib/commands/MGET.spec.ts index c8c020fe433..9ff47895f4e 100644 --- a/lib/commands/MGET.spec.ts +++ b/packages/client/lib/commands/MGET.spec.ts @@ -1,5 +1,5 @@ import { strict as assert } from 'assert'; -import { TestRedisServers, itWithClient, TestRedisClusters, itWithCluster } from '../test-utils'; +import testUtils, { GLOBAL } from '../test-utils'; import { transformArguments } from './MGET'; describe('MGET', () => { @@ -10,17 +10,17 @@ describe('MGET', () => { ); }); - itWithClient(TestRedisServers.OPEN, 'client.mGet', async client => { + testUtils.testWithClient('client.mGet', async client => { assert.deepEqual( await client.mGet(['key']), [null] ); - }); + }, GLOBAL.SERVERS.OPEN); - itWithCluster(TestRedisClusters.OPEN, 'cluster.mGet', async cluster => { + testUtils.testWithCluster('cluster.mGet', async cluster => { assert.deepEqual( await cluster.mGet(['key']), [null] ); - }); + }, GLOBAL.CLUSTERS.OPEN); }); diff --git a/lib/commands/MGET.ts b/packages/client/lib/commands/MGET.ts similarity index 100% rename from lib/commands/MGET.ts rename to packages/client/lib/commands/MGET.ts diff --git a/lib/commands/MIGRATE.spec.ts b/packages/client/lib/commands/MIGRATE.spec.ts similarity index 100% rename from lib/commands/MIGRATE.spec.ts rename to packages/client/lib/commands/MIGRATE.spec.ts diff --git a/lib/commands/MIGRATE.ts b/packages/client/lib/commands/MIGRATE.ts similarity index 100% rename from lib/commands/MIGRATE.ts rename to packages/client/lib/commands/MIGRATE.ts diff --git a/lib/commands/MODULE_LIST.spec.ts b/packages/client/lib/commands/MODULE_LIST.spec.ts similarity index 100% rename from lib/commands/MODULE_LIST.spec.ts rename to packages/client/lib/commands/MODULE_LIST.spec.ts diff --git a/lib/commands/MODULE_LIST.ts b/packages/client/lib/commands/MODULE_LIST.ts similarity index 100% rename from lib/commands/MODULE_LIST.ts rename to packages/client/lib/commands/MODULE_LIST.ts diff --git a/lib/commands/MODULE_LOAD.spec.ts b/packages/client/lib/commands/MODULE_LOAD.spec.ts similarity index 100% rename from lib/commands/MODULE_LOAD.spec.ts rename to packages/client/lib/commands/MODULE_LOAD.spec.ts diff --git a/lib/commands/MODULE_LOAD.ts b/packages/client/lib/commands/MODULE_LOAD.ts similarity index 100% rename from lib/commands/MODULE_LOAD.ts rename to packages/client/lib/commands/MODULE_LOAD.ts diff --git a/lib/commands/MODULE_UNLOAD.spec.ts b/packages/client/lib/commands/MODULE_UNLOAD.spec.ts similarity index 100% rename from lib/commands/MODULE_UNLOAD.spec.ts rename to packages/client/lib/commands/MODULE_UNLOAD.spec.ts diff --git a/lib/commands/MODULE_UNLOAD.ts b/packages/client/lib/commands/MODULE_UNLOAD.ts similarity index 100% rename from lib/commands/MODULE_UNLOAD.ts rename to packages/client/lib/commands/MODULE_UNLOAD.ts diff --git a/lib/commands/MOVE.spec.ts b/packages/client/lib/commands/MOVE.spec.ts similarity index 71% rename from lib/commands/MOVE.spec.ts rename to packages/client/lib/commands/MOVE.spec.ts index a05ca4613e9..f7fdc481cbf 100644 --- a/lib/commands/MOVE.spec.ts +++ b/packages/client/lib/commands/MOVE.spec.ts @@ -1,5 +1,5 @@ import { strict as assert } from 'assert'; -import { TestRedisServers, itWithClient } from '../test-utils'; +import testUtils, { GLOBAL } from '../test-utils'; import { transformArguments } from './MOVE'; describe('MOVE', () => { @@ -10,10 +10,10 @@ describe('MOVE', () => { ); }); - itWithClient(TestRedisServers.OPEN, 'client.move', async client => { + testUtils.testWithClient('client.move', async client => { assert.equal( await client.move('key', 1), false ); - }); + }, GLOBAL.SERVERS.OPEN); }); diff --git a/lib/commands/MOVE.ts b/packages/client/lib/commands/MOVE.ts similarity index 50% rename from lib/commands/MOVE.ts rename to packages/client/lib/commands/MOVE.ts index 74bb88c5e74..93896a63054 100644 --- a/lib/commands/MOVE.ts +++ b/packages/client/lib/commands/MOVE.ts @@ -1,7 +1,5 @@ -import { transformReplyBoolean } from './generic-transformers'; - export function transformArguments(key: string, db: number): Array { return ['MOVE', key, db.toString()]; } -export const transformReply = transformReplyBoolean; \ No newline at end of file +export { transformReplyBoolean as transformReply } from './generic-transformers'; diff --git a/lib/commands/MSET.spec.ts b/packages/client/lib/commands/MSET.spec.ts similarity index 81% rename from lib/commands/MSET.spec.ts rename to packages/client/lib/commands/MSET.spec.ts index 4445f4a7281..0568f38487e 100644 --- a/lib/commands/MSET.spec.ts +++ b/packages/client/lib/commands/MSET.spec.ts @@ -1,5 +1,5 @@ import { strict as assert } from 'assert'; -import { TestRedisServers, itWithClient, TestRedisClusters, itWithCluster } from '../test-utils'; +import testUtils, { GLOBAL } from '../test-utils'; import { transformArguments } from './MSET'; describe('MSET', () => { @@ -26,17 +26,17 @@ describe('MSET', () => { }); }); - itWithClient(TestRedisServers.OPEN, 'client.mSet', async client => { + testUtils.testWithClient('client.mSet', async client => { assert.equal( await client.mSet(['key1', 'value1', 'key2', 'value2']), 'OK' ); - }); + }, GLOBAL.SERVERS.OPEN); - itWithCluster(TestRedisClusters.OPEN, 'cluster.mSet', async cluster => { + testUtils.testWithCluster('cluster.mSet', async cluster => { assert.equal( await cluster.mSet(['{key}1', 'value1', '{key}2', 'value2']), 'OK' ); - }); + }, GLOBAL.CLUSTERS.OPEN); }); diff --git a/lib/commands/MSET.ts b/packages/client/lib/commands/MSET.ts similarity index 100% rename from lib/commands/MSET.ts rename to packages/client/lib/commands/MSET.ts diff --git a/lib/commands/MSETNX.spec.ts b/packages/client/lib/commands/MSETNX.spec.ts similarity index 81% rename from lib/commands/MSETNX.spec.ts rename to packages/client/lib/commands/MSETNX.spec.ts index 7f61a43e8d0..854a9affd8a 100644 --- a/lib/commands/MSETNX.spec.ts +++ b/packages/client/lib/commands/MSETNX.spec.ts @@ -1,5 +1,5 @@ import { strict as assert } from 'assert'; -import { TestRedisServers, itWithClient, TestRedisClusters, itWithCluster } from '../test-utils'; +import testUtils, { GLOBAL } from '../test-utils'; import { transformArguments } from './MSETNX'; describe('MSETNX', () => { @@ -26,17 +26,17 @@ describe('MSETNX', () => { }); }); - itWithClient(TestRedisServers.OPEN, 'client.mSetNX', async client => { + testUtils.testWithClient('client.mSetNX', async client => { assert.equal( await client.mSetNX(['key1', 'value1', 'key2', 'value2']), true ); - }); + }, GLOBAL.SERVERS.OPEN); - itWithCluster(TestRedisClusters.OPEN, 'cluster.mSetNX', async cluster => { + testUtils.testWithCluster('cluster.mSetNX', async cluster => { assert.equal( await cluster.mSetNX(['{key}1', 'value1', '{key}2', 'value2']), true ); - }); + }, GLOBAL.CLUSTERS.OPEN); }); diff --git a/lib/commands/MSETNX.ts b/packages/client/lib/commands/MSETNX.ts similarity index 77% rename from lib/commands/MSETNX.ts rename to packages/client/lib/commands/MSETNX.ts index c9c8c840374..fb7b028bc25 100644 --- a/lib/commands/MSETNX.ts +++ b/packages/client/lib/commands/MSETNX.ts @@ -1,5 +1,3 @@ -import { transformReplyBoolean } from './generic-transformers'; - export const FIRST_KEY_INDEX = 1; export function transformArguments(toSet: Array<[string, string]> | Array | Record): Array { @@ -16,4 +14,4 @@ export function transformArguments(toSet: Array<[string, string]> | Array { @@ -10,10 +10,10 @@ describe('PERSIST', () => { ); }); - itWithClient(TestRedisServers.OPEN, 'client.persist', async client => { + testUtils.testWithClient('client.persist', async client => { assert.equal( await client.persist('key'), false ); - }); + }, GLOBAL.SERVERS.OPEN); }); diff --git a/lib/commands/PERSIST.ts b/packages/client/lib/commands/PERSIST.ts similarity index 52% rename from lib/commands/PERSIST.ts rename to packages/client/lib/commands/PERSIST.ts index fc85a21c98c..b43310c136d 100644 --- a/lib/commands/PERSIST.ts +++ b/packages/client/lib/commands/PERSIST.ts @@ -1,9 +1,7 @@ -import { transformReplyBoolean } from './generic-transformers'; - export const FIRST_KEY_INDEX = 1; export function transformArguments(key: string): Array { return ['PERSIST', key]; } -export const transformReply = transformReplyBoolean; +export { transformReplyBoolean as transformReply } from './generic-transformers'; diff --git a/lib/commands/PEXPIRE.spec.ts b/packages/client/lib/commands/PEXPIRE.spec.ts similarity index 71% rename from lib/commands/PEXPIRE.spec.ts rename to packages/client/lib/commands/PEXPIRE.spec.ts index b7c4e1df461..4738edcf8f0 100644 --- a/lib/commands/PEXPIRE.spec.ts +++ b/packages/client/lib/commands/PEXPIRE.spec.ts @@ -1,5 +1,5 @@ import { strict as assert } from 'assert'; -import { TestRedisServers, itWithClient } from '../test-utils'; +import testUtils, { GLOBAL } from '../test-utils'; import { transformArguments } from './PEXPIRE'; describe('PEXPIRE', () => { @@ -10,10 +10,10 @@ describe('PEXPIRE', () => { ); }); - itWithClient(TestRedisServers.OPEN, 'client.pExpire', async client => { + testUtils.testWithClient('client.pExpire', async client => { assert.equal( await client.pExpire('key', 1), false ); - }); + }, GLOBAL.SERVERS.OPEN); }); diff --git a/lib/commands/PEXPIRE.ts b/packages/client/lib/commands/PEXPIRE.ts similarity index 60% rename from lib/commands/PEXPIRE.ts rename to packages/client/lib/commands/PEXPIRE.ts index d795f2fc0d0..f3b70279aa7 100644 --- a/lib/commands/PEXPIRE.ts +++ b/packages/client/lib/commands/PEXPIRE.ts @@ -1,9 +1,7 @@ -import { transformReplyBoolean } from './generic-transformers'; - export const FIRST_KEY_INDEX = 1; export function transformArguments(key: string, milliseconds: number): Array { return ['PEXPIRE', key, milliseconds.toString()]; } -export const transformReply = transformReplyBoolean; +export { transformReplyBoolean as transformReply } from './generic-transformers'; diff --git a/lib/commands/PEXPIREAT.spec.ts b/packages/client/lib/commands/PEXPIREAT.spec.ts similarity index 81% rename from lib/commands/PEXPIREAT.spec.ts rename to packages/client/lib/commands/PEXPIREAT.spec.ts index 6e5fc37ed5c..19fc3b888d7 100644 --- a/lib/commands/PEXPIREAT.spec.ts +++ b/packages/client/lib/commands/PEXPIREAT.spec.ts @@ -1,5 +1,5 @@ import { strict as assert } from 'assert'; -import { TestRedisServers, itWithClient } from '../test-utils'; +import testUtils, { GLOBAL } from '../test-utils'; import { transformArguments } from './PEXPIREAT'; describe('PEXPIREAT', () => { @@ -10,7 +10,7 @@ describe('PEXPIREAT', () => { ['PEXPIREAT', 'key', '1'] ); }); - + it('date', () => { const d = new Date(); assert.deepEqual( @@ -20,10 +20,10 @@ describe('PEXPIREAT', () => { }); }); - itWithClient(TestRedisServers.OPEN, 'client.pExpireAt', async client => { + testUtils.testWithClient('client.pExpireAt', async client => { assert.equal( await client.pExpireAt('key', 1), false ); - }); + }, GLOBAL.SERVERS.OPEN); }); diff --git a/lib/commands/PEXPIREAT.ts b/packages/client/lib/commands/PEXPIREAT.ts similarity index 63% rename from lib/commands/PEXPIREAT.ts rename to packages/client/lib/commands/PEXPIREAT.ts index 91f38f88946..5dded48d278 100644 --- a/lib/commands/PEXPIREAT.ts +++ b/packages/client/lib/commands/PEXPIREAT.ts @@ -1,4 +1,4 @@ -import { transformPXAT, transformReplyBoolean } from './generic-transformers'; +import { transformPXAT } from './generic-transformers'; export const FIRST_KEY_INDEX = 1; @@ -10,4 +10,4 @@ export function transformArguments(key: string, millisecondsTimestamp: number | ]; } -export const transformReply = transformReplyBoolean; +export { transformReplyBoolean as transformReply } from './generic-transformers'; diff --git a/lib/commands/PFADD.spec.ts b/packages/client/lib/commands/PFADD.spec.ts similarity index 81% rename from lib/commands/PFADD.spec.ts rename to packages/client/lib/commands/PFADD.spec.ts index 74f03ea3ce2..8c0e752fd50 100644 --- a/lib/commands/PFADD.spec.ts +++ b/packages/client/lib/commands/PFADD.spec.ts @@ -1,5 +1,5 @@ import { strict as assert } from 'assert'; -import { TestRedisServers, itWithClient } from '../test-utils'; +import testUtils, { GLOBAL } from '../test-utils'; import { transformArguments } from './PFADD'; describe('PFADD', () => { @@ -19,10 +19,10 @@ describe('PFADD', () => { }); }); - itWithClient(TestRedisServers.OPEN, 'client.pfAdd', async client => { + testUtils.testWithClient('client.pfAdd', async client => { assert.equal( await client.pfAdd('key', '1'), true ); - }); + }, GLOBAL.SERVERS.OPEN); }); diff --git a/lib/commands/PFADD.ts b/packages/client/lib/commands/PFADD.ts similarity index 62% rename from lib/commands/PFADD.ts rename to packages/client/lib/commands/PFADD.ts index 4328a18dfe5..e45e83c3ae0 100644 --- a/lib/commands/PFADD.ts +++ b/packages/client/lib/commands/PFADD.ts @@ -1,5 +1,5 @@ import { RedisCommandArguments } from '.'; -import { pushVerdictArguments, transformReplyBoolean } from './generic-transformers'; +import { pushVerdictArguments } from './generic-transformers'; export const FIRST_KEY_INDEX = 1; @@ -7,4 +7,4 @@ export function transformArguments(key: string, element: string | Array) return pushVerdictArguments(['PFADD', key], element); } -export const transformReply = transformReplyBoolean; +export { transformReplyBoolean as transformReply } from './generic-transformers'; diff --git a/lib/commands/PFCOUNT.spec.ts b/packages/client/lib/commands/PFCOUNT.spec.ts similarity index 80% rename from lib/commands/PFCOUNT.spec.ts rename to packages/client/lib/commands/PFCOUNT.spec.ts index 049fa2c200c..a1ea06c4494 100644 --- a/lib/commands/PFCOUNT.spec.ts +++ b/packages/client/lib/commands/PFCOUNT.spec.ts @@ -1,5 +1,5 @@ import { strict as assert } from 'assert'; -import { TestRedisServers, itWithClient } from '../test-utils'; +import testUtils, { GLOBAL } from '../test-utils'; import { transformArguments } from './PFCOUNT'; describe('PFCOUNT', () => { @@ -19,10 +19,10 @@ describe('PFCOUNT', () => { }); }); - itWithClient(TestRedisServers.OPEN, 'client.pfCount', async client => { + testUtils.testWithClient('client.pfCount', async client => { assert.equal( await client.pfCount('key'), 0 ); - }); + }, GLOBAL.SERVERS.OPEN); }); diff --git a/lib/commands/PFCOUNT.ts b/packages/client/lib/commands/PFCOUNT.ts similarity index 100% rename from lib/commands/PFCOUNT.ts rename to packages/client/lib/commands/PFCOUNT.ts diff --git a/lib/commands/PFMERGE.spec.ts b/packages/client/lib/commands/PFMERGE.spec.ts similarity index 82% rename from lib/commands/PFMERGE.spec.ts rename to packages/client/lib/commands/PFMERGE.spec.ts index 1f6ed24bcd1..881fc5f5439 100644 --- a/lib/commands/PFMERGE.spec.ts +++ b/packages/client/lib/commands/PFMERGE.spec.ts @@ -1,5 +1,5 @@ import { strict as assert } from 'assert'; -import { TestRedisServers, itWithClient } from '../test-utils'; +import testUtils, { GLOBAL } from '../test-utils'; import { transformArguments } from './PFMERGE'; describe('PFMERGE', () => { @@ -19,10 +19,10 @@ describe('PFMERGE', () => { }); }); - itWithClient(TestRedisServers.OPEN, 'client.pfMerge', async client => { + testUtils.testWithClient('client.pfMerge', async client => { assert.equal( await client.pfMerge('destination', 'source'), 'OK' ); - }); + }, GLOBAL.SERVERS.OPEN); }); diff --git a/lib/commands/PFMERGE.ts b/packages/client/lib/commands/PFMERGE.ts similarity index 100% rename from lib/commands/PFMERGE.ts rename to packages/client/lib/commands/PFMERGE.ts diff --git a/lib/commands/PING.spec.ts b/packages/client/lib/commands/PING.spec.ts similarity index 52% rename from lib/commands/PING.spec.ts rename to packages/client/lib/commands/PING.spec.ts index 43b683f192d..85b48fec6b5 100644 --- a/lib/commands/PING.spec.ts +++ b/packages/client/lib/commands/PING.spec.ts @@ -1,11 +1,11 @@ import { strict as assert } from 'assert'; -import { TestRedisServers, itWithClient } from '../test-utils'; +import testUtils, { GLOBAL } from '../test-utils'; describe('PING', () => { - itWithClient(TestRedisServers.OPEN, 'client.ping', async client => { + testUtils.testWithClient('client.ping', async client => { assert.equal( await client.ping(), 'PONG' ); - }); + }, GLOBAL.SERVERS.OPEN); }); diff --git a/lib/commands/PING.ts b/packages/client/lib/commands/PING.ts similarity index 100% rename from lib/commands/PING.ts rename to packages/client/lib/commands/PING.ts diff --git a/lib/commands/PSETEX.spec.ts b/packages/client/lib/commands/PSETEX.spec.ts similarity index 65% rename from lib/commands/PSETEX.spec.ts rename to packages/client/lib/commands/PSETEX.spec.ts index c98142effa9..61a6e682b08 100644 --- a/lib/commands/PSETEX.spec.ts +++ b/packages/client/lib/commands/PSETEX.spec.ts @@ -1,5 +1,5 @@ import { strict as assert } from 'assert'; -import { TestRedisServers, itWithClient, TestRedisClusters, itWithCluster } from '../test-utils'; +import testUtils, { GLOBAL } from '../test-utils'; import { transformArguments } from './PSETEX'; describe('PSETEX', () => { @@ -10,17 +10,17 @@ describe('PSETEX', () => { ); }); - itWithClient(TestRedisServers.OPEN, 'client.pSetEx', async client => { + testUtils.testWithClient('client.pSetEx', async client => { assert.equal( await client.pSetEx('key', 1, 'value'), 'OK' ); - }); + }, GLOBAL.SERVERS.OPEN); - itWithCluster(TestRedisClusters.OPEN, 'cluster.pSetEx', async cluster => { + testUtils.testWithCluster('cluster.pSetEx', async cluster => { assert.equal( await cluster.pSetEx('key', 1, 'value'), 'OK' ); - }); + }, GLOBAL.CLUSTERS.OPEN); }); diff --git a/lib/commands/PSETEX.ts b/packages/client/lib/commands/PSETEX.ts similarity index 100% rename from lib/commands/PSETEX.ts rename to packages/client/lib/commands/PSETEX.ts diff --git a/lib/commands/PTTL.spec.ts b/packages/client/lib/commands/PTTL.spec.ts similarity index 70% rename from lib/commands/PTTL.spec.ts rename to packages/client/lib/commands/PTTL.spec.ts index 35f48c2cc3e..e65421de590 100644 --- a/lib/commands/PTTL.spec.ts +++ b/packages/client/lib/commands/PTTL.spec.ts @@ -1,5 +1,5 @@ import { strict as assert } from 'assert'; -import { TestRedisServers, itWithClient } from '../test-utils'; +import testUtils, { GLOBAL } from '../test-utils'; import { transformArguments } from './PTTL'; describe('PTTL', () => { @@ -10,10 +10,10 @@ describe('PTTL', () => { ); }); - itWithClient(TestRedisServers.OPEN, 'client.pTTL', async client => { + testUtils.testWithClient('client.pTTL', async client => { assert.equal( await client.pTTL('key'), -2 ); - }); + }, GLOBAL.SERVERS.OPEN); }); diff --git a/lib/commands/PTTL.ts b/packages/client/lib/commands/PTTL.ts similarity index 100% rename from lib/commands/PTTL.ts rename to packages/client/lib/commands/PTTL.ts diff --git a/lib/commands/PUBLISH.spec.ts b/packages/client/lib/commands/PUBLISH.spec.ts similarity index 73% rename from lib/commands/PUBLISH.spec.ts rename to packages/client/lib/commands/PUBLISH.spec.ts index e746b9490e0..b2084e668ba 100644 --- a/lib/commands/PUBLISH.spec.ts +++ b/packages/client/lib/commands/PUBLISH.spec.ts @@ -1,5 +1,5 @@ import { strict as assert } from 'assert'; -import { TestRedisServers, itWithClient } from '../test-utils'; +import testUtils, { GLOBAL } from '../test-utils'; import { transformArguments } from './PUBLISH'; describe('PUBLISH', () => { @@ -10,10 +10,10 @@ describe('PUBLISH', () => { ); }); - itWithClient(TestRedisServers.OPEN, 'client.publish', async client => { + testUtils.testWithClient('client.publish', async client => { assert.equal( await client.publish('channel', 'message'), 0 ); - }); + }, GLOBAL.SERVERS.OPEN); }); diff --git a/lib/commands/PUBLISH.ts b/packages/client/lib/commands/PUBLISH.ts similarity index 100% rename from lib/commands/PUBLISH.ts rename to packages/client/lib/commands/PUBLISH.ts diff --git a/lib/commands/PUBSUB_CHANNELS.spec.ts b/packages/client/lib/commands/PUBSUB_CHANNELS.spec.ts similarity index 80% rename from lib/commands/PUBSUB_CHANNELS.spec.ts rename to packages/client/lib/commands/PUBSUB_CHANNELS.spec.ts index 9e148bc7fda..c427eab4850 100644 --- a/lib/commands/PUBSUB_CHANNELS.spec.ts +++ b/packages/client/lib/commands/PUBSUB_CHANNELS.spec.ts @@ -1,5 +1,5 @@ import { strict as assert } from 'assert'; -import { TestRedisServers, itWithClient } from '../test-utils'; +import testUtils, { GLOBAL } from '../test-utils'; import { transformArguments } from './PUBSUB_CHANNELS'; describe('PUBSUB CHANNELS', () => { @@ -19,10 +19,10 @@ describe('PUBSUB CHANNELS', () => { }); }); - itWithClient(TestRedisServers.OPEN, 'client.pubSubChannels', async client => { + testUtils.testWithClient('client.pubSubChannels', async client => { assert.deepEqual( await client.pubSubChannels(), [] ); - }); + }, GLOBAL.SERVERS.OPEN); }); diff --git a/lib/commands/PUBSUB_CHANNELS.ts b/packages/client/lib/commands/PUBSUB_CHANNELS.ts similarity index 100% rename from lib/commands/PUBSUB_CHANNELS.ts rename to packages/client/lib/commands/PUBSUB_CHANNELS.ts diff --git a/lib/commands/PUBSUB_NUMPAT.spec.ts b/packages/client/lib/commands/PUBSUB_NUMPAT.spec.ts similarity index 70% rename from lib/commands/PUBSUB_NUMPAT.spec.ts rename to packages/client/lib/commands/PUBSUB_NUMPAT.spec.ts index 55eef6a97de..d738b916c60 100644 --- a/lib/commands/PUBSUB_NUMPAT.spec.ts +++ b/packages/client/lib/commands/PUBSUB_NUMPAT.spec.ts @@ -1,5 +1,5 @@ import { strict as assert } from 'assert'; -import { TestRedisServers, itWithClient } from '../test-utils'; +import testUtils, { GLOBAL } from '../test-utils'; import { transformArguments } from './PUBSUB_NUMPAT'; describe('PUBSUB NUMPAT', () => { @@ -10,10 +10,10 @@ describe('PUBSUB NUMPAT', () => { ); }); - itWithClient(TestRedisServers.OPEN, 'client.pubSubNumPat', async client => { + testUtils.testWithClient('client.pubSubNumPat', async client => { assert.equal( await client.pubSubNumPat(), 0 ); - }); + }, GLOBAL.SERVERS.OPEN); }); diff --git a/lib/commands/PUBSUB_NUMPAT.ts b/packages/client/lib/commands/PUBSUB_NUMPAT.ts similarity index 100% rename from lib/commands/PUBSUB_NUMPAT.ts rename to packages/client/lib/commands/PUBSUB_NUMPAT.ts diff --git a/lib/commands/PUBSUB_NUMSUB.spec.ts b/packages/client/lib/commands/PUBSUB_NUMSUB.spec.ts similarity index 84% rename from lib/commands/PUBSUB_NUMSUB.spec.ts rename to packages/client/lib/commands/PUBSUB_NUMSUB.spec.ts index ef44faf2c0b..e35558ef865 100644 --- a/lib/commands/PUBSUB_NUMSUB.spec.ts +++ b/packages/client/lib/commands/PUBSUB_NUMSUB.spec.ts @@ -1,5 +1,5 @@ import { strict as assert } from 'assert'; -import { TestRedisServers, itWithClient } from '../test-utils'; +import testUtils, { GLOBAL } from '../test-utils'; import { transformArguments } from './PUBSUB_NUMSUB'; describe('PUBSUB NUMSUB', () => { @@ -26,10 +26,10 @@ describe('PUBSUB NUMSUB', () => { }); }); - itWithClient(TestRedisServers.OPEN, 'client.pubSubNumSub', async client => { + testUtils.testWithClient('client.pubSubNumSub', async client => { assert.deepEqual( await client.pubSubNumSub(), Object.create(null) ); - }); + }, GLOBAL.SERVERS.OPEN); }); diff --git a/lib/commands/PUBSUB_NUMSUB.ts b/packages/client/lib/commands/PUBSUB_NUMSUB.ts similarity index 100% rename from lib/commands/PUBSUB_NUMSUB.ts rename to packages/client/lib/commands/PUBSUB_NUMSUB.ts diff --git a/lib/commands/RANDOMKEY.spec.ts b/packages/client/lib/commands/RANDOMKEY.spec.ts similarity index 70% rename from lib/commands/RANDOMKEY.spec.ts rename to packages/client/lib/commands/RANDOMKEY.spec.ts index 171c42be116..81c42b2fd83 100644 --- a/lib/commands/RANDOMKEY.spec.ts +++ b/packages/client/lib/commands/RANDOMKEY.spec.ts @@ -1,5 +1,5 @@ import { strict as assert } from 'assert'; -import { TestRedisServers, itWithClient } from '../test-utils'; +import testUtils, { GLOBAL } from '../test-utils'; import { transformArguments } from './RANDOMKEY'; describe('RANDOMKEY', () => { @@ -10,10 +10,10 @@ describe('RANDOMKEY', () => { ); }); - itWithClient(TestRedisServers.OPEN, 'client.randomKey', async client => { + testUtils.testWithClient('client.randomKey', async client => { assert.equal( await client.randomKey(), null ); - }); + }, GLOBAL.SERVERS.OPEN); }); diff --git a/lib/commands/RANDOMKEY.ts b/packages/client/lib/commands/RANDOMKEY.ts similarity index 100% rename from lib/commands/RANDOMKEY.ts rename to packages/client/lib/commands/RANDOMKEY.ts diff --git a/lib/commands/READONLY.spec.ts b/packages/client/lib/commands/READONLY.spec.ts similarity index 100% rename from lib/commands/READONLY.spec.ts rename to packages/client/lib/commands/READONLY.spec.ts diff --git a/lib/commands/READONLY.ts b/packages/client/lib/commands/READONLY.ts similarity index 100% rename from lib/commands/READONLY.ts rename to packages/client/lib/commands/READONLY.ts diff --git a/lib/commands/READWRITE.spec.ts b/packages/client/lib/commands/READWRITE.spec.ts similarity index 100% rename from lib/commands/READWRITE.spec.ts rename to packages/client/lib/commands/READWRITE.spec.ts diff --git a/lib/commands/READWRITE.ts b/packages/client/lib/commands/READWRITE.ts similarity index 100% rename from lib/commands/READWRITE.ts rename to packages/client/lib/commands/READWRITE.ts diff --git a/lib/commands/RENAME.spec.ts b/packages/client/lib/commands/RENAME.spec.ts similarity index 74% rename from lib/commands/RENAME.spec.ts rename to packages/client/lib/commands/RENAME.spec.ts index 9d447c600b6..49e0af600f6 100644 --- a/lib/commands/RENAME.spec.ts +++ b/packages/client/lib/commands/RENAME.spec.ts @@ -1,5 +1,5 @@ import { strict as assert } from 'assert'; -import { TestRedisServers, itWithClient } from '../test-utils'; +import testUtils, { GLOBAL } from '../test-utils'; import { transformArguments } from './RENAME'; describe('RENAME', () => { @@ -10,12 +10,12 @@ describe('RENAME', () => { ); }); - itWithClient(TestRedisServers.OPEN, 'client.rename', async client => { + testUtils.testWithClient('client.rename', async client => { await client.set('from', 'value'); assert.equal( await client.rename('from', 'to'), 'OK' ); - }); + }, GLOBAL.SERVERS.OPEN); }); diff --git a/lib/commands/RENAME.ts b/packages/client/lib/commands/RENAME.ts similarity index 100% rename from lib/commands/RENAME.ts rename to packages/client/lib/commands/RENAME.ts diff --git a/lib/commands/RENAMENX.spec.ts b/packages/client/lib/commands/RENAMENX.spec.ts similarity index 74% rename from lib/commands/RENAMENX.spec.ts rename to packages/client/lib/commands/RENAMENX.spec.ts index f438834b90e..6345eb5bd09 100644 --- a/lib/commands/RENAMENX.spec.ts +++ b/packages/client/lib/commands/RENAMENX.spec.ts @@ -1,5 +1,5 @@ import { strict as assert } from 'assert'; -import { TestRedisServers, itWithClient } from '../test-utils'; +import testUtils, { GLOBAL } from '../test-utils'; import { transformArguments } from './RENAMENX'; describe('RENAMENX', () => { @@ -10,12 +10,12 @@ describe('RENAMENX', () => { ); }); - itWithClient(TestRedisServers.OPEN, 'client.renameNX', async client => { + testUtils.testWithClient('client.renameNX', async client => { await client.set('from', 'value'); assert.equal( await client.renameNX('from', 'to'), true ); - }); + }, GLOBAL.SERVERS.OPEN); }); diff --git a/lib/commands/RENAMENX.ts b/packages/client/lib/commands/RENAMENX.ts similarity index 57% rename from lib/commands/RENAMENX.ts rename to packages/client/lib/commands/RENAMENX.ts index 883d2ca2961..2cfec007741 100644 --- a/lib/commands/RENAMENX.ts +++ b/packages/client/lib/commands/RENAMENX.ts @@ -1,9 +1,7 @@ -import { transformReplyBoolean } from './generic-transformers'; - export const FIRST_KEY_INDEX = 1; export function transformArguments(key: string, newKey: string): Array { return ['RENAMENX', key, newKey]; } -export const transformReply = transformReplyBoolean; +export { transformReplyBoolean as transformReply } from './generic-transformers'; diff --git a/lib/commands/REPLICAOF.spec.ts b/packages/client/lib/commands/REPLICAOF.spec.ts similarity index 100% rename from lib/commands/REPLICAOF.spec.ts rename to packages/client/lib/commands/REPLICAOF.spec.ts diff --git a/lib/commands/REPLICAOF.ts b/packages/client/lib/commands/REPLICAOF.ts similarity index 100% rename from lib/commands/REPLICAOF.ts rename to packages/client/lib/commands/REPLICAOF.ts diff --git a/lib/commands/RESTORE-ASKING.spec.ts b/packages/client/lib/commands/RESTORE-ASKING.spec.ts similarity index 100% rename from lib/commands/RESTORE-ASKING.spec.ts rename to packages/client/lib/commands/RESTORE-ASKING.spec.ts diff --git a/lib/commands/RESTORE-ASKING.ts b/packages/client/lib/commands/RESTORE-ASKING.ts similarity index 100% rename from lib/commands/RESTORE-ASKING.ts rename to packages/client/lib/commands/RESTORE-ASKING.ts diff --git a/lib/commands/ROLE.spec.ts b/packages/client/lib/commands/ROLE.spec.ts similarity index 93% rename from lib/commands/ROLE.spec.ts rename to packages/client/lib/commands/ROLE.spec.ts index 5b647e07ca6..2e6d9b163ae 100644 --- a/lib/commands/ROLE.spec.ts +++ b/packages/client/lib/commands/ROLE.spec.ts @@ -1,5 +1,5 @@ import { strict as assert } from 'assert'; -import { itWithClient, TestRedisServers } from '../test-utils'; +import testUtils, { GLOBAL } from '../test-utils'; import { transformArguments, transformReply } from './ROLE'; describe('ROLE', () => { @@ -56,7 +56,7 @@ describe('ROLE', () => { }); }); - itWithClient(TestRedisServers.OPEN, 'client.role', async client => { + testUtils.testWithClient('client.role', async client => { assert.deepEqual( await client.role(), { @@ -65,5 +65,5 @@ describe('ROLE', () => { replicas: [] } ); - }); + }, GLOBAL.SERVERS.OPEN); }); diff --git a/lib/commands/ROLE.ts b/packages/client/lib/commands/ROLE.ts similarity index 100% rename from lib/commands/ROLE.ts rename to packages/client/lib/commands/ROLE.ts diff --git a/lib/commands/RPOP.spec.ts b/packages/client/lib/commands/RPOP.spec.ts similarity index 62% rename from lib/commands/RPOP.spec.ts rename to packages/client/lib/commands/RPOP.spec.ts index 2a753ff1a66..6e57afa3216 100644 --- a/lib/commands/RPOP.spec.ts +++ b/packages/client/lib/commands/RPOP.spec.ts @@ -1,5 +1,5 @@ import { strict as assert } from 'assert'; -import { TestRedisServers, itWithClient, itWithCluster, TestRedisClusters } from '../test-utils'; +import testUtils, { GLOBAL } from '../test-utils'; import { transformArguments } from './RPOP'; describe('RPOP', () => { @@ -10,17 +10,17 @@ describe('RPOP', () => { ); }); - itWithClient(TestRedisServers.OPEN, 'client.rPop', async client => { + testUtils.testWithClient('client.rPop', async client => { assert.equal( await client.rPop('key'), null ); - }); + }, GLOBAL.SERVERS.OPEN); - itWithCluster(TestRedisClusters.OPEN, 'cluster.rPop', async cluster => { + testUtils.testWithCluster('cluster.rPop', async cluster => { assert.equal( await cluster.rPop('key'), null ); - }); + }, GLOBAL.CLUSTERS.OPEN); }); diff --git a/lib/commands/RPOP.ts b/packages/client/lib/commands/RPOP.ts similarity index 100% rename from lib/commands/RPOP.ts rename to packages/client/lib/commands/RPOP.ts diff --git a/lib/commands/RPOPLPUSH.spec.ts b/packages/client/lib/commands/RPOPLPUSH.spec.ts similarity index 66% rename from lib/commands/RPOPLPUSH.spec.ts rename to packages/client/lib/commands/RPOPLPUSH.spec.ts index 75b5f2e18f9..cef3049bd91 100644 --- a/lib/commands/RPOPLPUSH.spec.ts +++ b/packages/client/lib/commands/RPOPLPUSH.spec.ts @@ -1,5 +1,5 @@ import { strict as assert } from 'assert'; -import { TestRedisServers, itWithClient, itWithCluster, TestRedisClusters } from '../test-utils'; +import testUtils, { GLOBAL } from '../test-utils'; import { transformArguments } from './RPOPLPUSH'; describe('RPOPLPUSH', () => { @@ -10,17 +10,17 @@ describe('RPOPLPUSH', () => { ); }); - itWithClient(TestRedisServers.OPEN, 'client.rPopLPush', async client => { + testUtils.testWithClient('client.rPopLPush', async client => { assert.equal( await client.rPopLPush('source', 'destination'), null ); - }); + }, GLOBAL.SERVERS.OPEN); - itWithCluster(TestRedisClusters.OPEN, 'cluster.rPopLPush', async cluster => { + testUtils.testWithCluster('cluster.rPopLPush', async cluster => { assert.equal( await cluster.rPopLPush('{tag}source', '{tag}destination'), null ); - }); + }, GLOBAL.CLUSTERS.OPEN); }); diff --git a/lib/commands/RPOPLPUSH.ts b/packages/client/lib/commands/RPOPLPUSH.ts similarity index 100% rename from lib/commands/RPOPLPUSH.ts rename to packages/client/lib/commands/RPOPLPUSH.ts diff --git a/lib/commands/RPOP_COUNT.spec.ts b/packages/client/lib/commands/RPOP_COUNT.spec.ts similarity index 57% rename from lib/commands/RPOP_COUNT.spec.ts rename to packages/client/lib/commands/RPOP_COUNT.spec.ts index 2624540f124..3657a608039 100644 --- a/lib/commands/RPOP_COUNT.spec.ts +++ b/packages/client/lib/commands/RPOP_COUNT.spec.ts @@ -1,9 +1,9 @@ import { strict as assert } from 'assert'; -import { TestRedisServers, itWithClient, itWithCluster, TestRedisClusters, describeHandleMinimumRedisVersion } from '../test-utils'; +import testUtils, { GLOBAL } from '../test-utils'; import { transformArguments } from './RPOP_COUNT'; describe('RPOP COUNT', () => { - describeHandleMinimumRedisVersion([6, 2]); + testUtils.isVersionGreaterThanHook([6, 2]); it('transformArguments', () => { assert.deepEqual( @@ -12,17 +12,17 @@ describe('RPOP COUNT', () => { ); }); - itWithClient(TestRedisServers.OPEN, 'client.rPopCount', async client => { + testUtils.testWithClient('client.rPopCount', async client => { assert.equal( await client.rPopCount('key', 1), null ); - }); + }, GLOBAL.SERVERS.OPEN); - itWithCluster(TestRedisClusters.OPEN, 'cluster.rPopCount', async cluster => { + testUtils.testWithCluster('cluster.rPopCount', async cluster => { assert.equal( await cluster.rPopCount('key', 1), null ); - }); + }, GLOBAL.CLUSTERS.OPEN); }); diff --git a/lib/commands/RPOP_COUNT.ts b/packages/client/lib/commands/RPOP_COUNT.ts similarity index 100% rename from lib/commands/RPOP_COUNT.ts rename to packages/client/lib/commands/RPOP_COUNT.ts diff --git a/lib/commands/RPUSH.spec.ts b/packages/client/lib/commands/RPUSH.spec.ts similarity index 73% rename from lib/commands/RPUSH.spec.ts rename to packages/client/lib/commands/RPUSH.spec.ts index 4336d10c9a3..afa5c1c6400 100644 --- a/lib/commands/RPUSH.spec.ts +++ b/packages/client/lib/commands/RPUSH.spec.ts @@ -1,5 +1,5 @@ import { strict as assert } from 'assert'; -import { TestRedisServers, itWithClient, itWithCluster, TestRedisClusters } from '../test-utils'; +import testUtils, { GLOBAL } from '../test-utils'; import { transformArguments } from './RPUSH'; describe('RPUSH', () => { @@ -19,17 +19,17 @@ describe('RPUSH', () => { }); }); - itWithClient(TestRedisServers.OPEN, 'client.rPush', async client => { + testUtils.testWithClient('client.rPush', async client => { assert.equal( await client.rPush('key', 'element'), 1 ); - }); + }, GLOBAL.SERVERS.OPEN); - itWithCluster(TestRedisClusters.OPEN, 'cluster.rPush', async cluster => { + testUtils.testWithCluster('cluster.rPush', async cluster => { assert.equal( await cluster.rPush('key', 'element'), 1 ); - }); + }, GLOBAL.CLUSTERS.OPEN); }); diff --git a/lib/commands/RPUSH.ts b/packages/client/lib/commands/RPUSH.ts similarity index 100% rename from lib/commands/RPUSH.ts rename to packages/client/lib/commands/RPUSH.ts diff --git a/lib/commands/RPUSHX.spec.ts b/packages/client/lib/commands/RPUSHX.spec.ts similarity index 73% rename from lib/commands/RPUSHX.spec.ts rename to packages/client/lib/commands/RPUSHX.spec.ts index 18f91e8bef6..ee2041de6f2 100644 --- a/lib/commands/RPUSHX.spec.ts +++ b/packages/client/lib/commands/RPUSHX.spec.ts @@ -1,5 +1,5 @@ import { strict as assert } from 'assert'; -import { TestRedisServers, itWithClient, itWithCluster, TestRedisClusters } from '../test-utils'; +import testUtils, { GLOBAL } from '../test-utils'; import { transformArguments } from './RPUSHX'; describe('RPUSHX', () => { @@ -19,17 +19,17 @@ describe('RPUSHX', () => { }); }); - itWithClient(TestRedisServers.OPEN, 'client.rPushX', async client => { + testUtils.testWithClient('client.rPushX', async client => { assert.equal( await client.rPushX('key', 'element'), 0 ); - }); + }, GLOBAL.SERVERS.OPEN); - itWithCluster(TestRedisClusters.OPEN, 'cluster.rPushX', async cluster => { + testUtils.testWithCluster('cluster.rPushX', async cluster => { assert.equal( await cluster.rPushX('key', 'element'), 0 ); - }); + }, GLOBAL.CLUSTERS.OPEN); }); diff --git a/lib/commands/RPUSHX.ts b/packages/client/lib/commands/RPUSHX.ts similarity index 100% rename from lib/commands/RPUSHX.ts rename to packages/client/lib/commands/RPUSHX.ts diff --git a/lib/commands/SADD.spec.ts b/packages/client/lib/commands/SADD.spec.ts similarity index 81% rename from lib/commands/SADD.spec.ts rename to packages/client/lib/commands/SADD.spec.ts index bf1ee48fe7f..4533f6f9ad5 100644 --- a/lib/commands/SADD.spec.ts +++ b/packages/client/lib/commands/SADD.spec.ts @@ -1,5 +1,5 @@ import { strict as assert } from 'assert'; -import { TestRedisServers, itWithClient } from '../test-utils'; +import testUtils, { GLOBAL } from '../test-utils'; import { transformArguments } from './SADD'; describe('SADD', () => { @@ -19,10 +19,10 @@ describe('SADD', () => { }); }); - itWithClient(TestRedisServers.OPEN, 'client.sAdd', async client => { + testUtils.testWithClient('client.sAdd', async client => { assert.equal( await client.sAdd('key', 'member'), 1 ); - }); + }, GLOBAL.SERVERS.OPEN); }); diff --git a/lib/commands/SADD.ts b/packages/client/lib/commands/SADD.ts similarity index 100% rename from lib/commands/SADD.ts rename to packages/client/lib/commands/SADD.ts diff --git a/lib/commands/SAVE.spec.ts b/packages/client/lib/commands/SAVE.spec.ts similarity index 100% rename from lib/commands/SAVE.spec.ts rename to packages/client/lib/commands/SAVE.spec.ts diff --git a/lib/commands/SAVE.ts b/packages/client/lib/commands/SAVE.ts similarity index 100% rename from lib/commands/SAVE.ts rename to packages/client/lib/commands/SAVE.ts diff --git a/lib/commands/SCAN.spec.ts b/packages/client/lib/commands/SCAN.spec.ts similarity index 93% rename from lib/commands/SCAN.spec.ts rename to packages/client/lib/commands/SCAN.spec.ts index 975c4cb6d2f..7657b744e02 100644 --- a/lib/commands/SCAN.spec.ts +++ b/packages/client/lib/commands/SCAN.spec.ts @@ -1,5 +1,5 @@ import { strict as assert } from 'assert'; -import { TestRedisServers, itWithClient } from '../test-utils'; +import testUtils, { GLOBAL } from '../test-utils'; import { transformArguments, transformReply } from './SCAN'; describe('SCAN', () => { @@ -72,7 +72,7 @@ describe('SCAN', () => { }); }); - itWithClient(TestRedisServers.OPEN, 'client.scan', async client => { + testUtils.testWithClient('client.scan', async client => { assert.deepEqual( await client.scan(0), { @@ -80,5 +80,5 @@ describe('SCAN', () => { keys: [] } ); - }); + }, GLOBAL.SERVERS.OPEN); }); diff --git a/lib/commands/SCAN.ts b/packages/client/lib/commands/SCAN.ts similarity index 100% rename from lib/commands/SCAN.ts rename to packages/client/lib/commands/SCAN.ts diff --git a/lib/commands/SCARD.spec.ts b/packages/client/lib/commands/SCARD.spec.ts similarity index 70% rename from lib/commands/SCARD.spec.ts rename to packages/client/lib/commands/SCARD.spec.ts index b6681693814..afc21c6b00c 100644 --- a/lib/commands/SCARD.spec.ts +++ b/packages/client/lib/commands/SCARD.spec.ts @@ -1,5 +1,5 @@ import { strict as assert } from 'assert'; -import { TestRedisServers, itWithClient } from '../test-utils'; +import testUtils, { GLOBAL } from '../test-utils'; import { transformArguments } from './SCARD'; describe('SCARD', () => { @@ -10,10 +10,10 @@ describe('SCARD', () => { ); }); - itWithClient(TestRedisServers.OPEN, 'client.sCard', async client => { + testUtils.testWithClient('client.sCard', async client => { assert.equal( await client.sCard('key'), 0 ); - }); + }, GLOBAL.SERVERS.OPEN); }); diff --git a/lib/commands/SCARD.ts b/packages/client/lib/commands/SCARD.ts similarity index 100% rename from lib/commands/SCARD.ts rename to packages/client/lib/commands/SCARD.ts diff --git a/lib/commands/SCRIPT_DEBUG.spec.ts b/packages/client/lib/commands/SCRIPT_DEBUG.spec.ts similarity index 71% rename from lib/commands/SCRIPT_DEBUG.spec.ts rename to packages/client/lib/commands/SCRIPT_DEBUG.spec.ts index 9d2ad1af266..192f90f75a5 100644 --- a/lib/commands/SCRIPT_DEBUG.spec.ts +++ b/packages/client/lib/commands/SCRIPT_DEBUG.spec.ts @@ -1,5 +1,5 @@ import { strict as assert } from 'assert'; -import { TestRedisServers, itWithClient } from '../test-utils'; +import testUtils, { GLOBAL } from '../test-utils'; import { transformArguments } from './SCRIPT_DEBUG'; describe('SCRIPT DEBUG', () => { @@ -10,10 +10,10 @@ describe('SCRIPT DEBUG', () => { ); }); - itWithClient(TestRedisServers.OPEN, 'client.scriptDebug', async client => { + testUtils.testWithClient('client.scriptDebug', async client => { assert.equal( await client.scriptDebug('NO'), 'OK' ); - }); + }, GLOBAL.SERVERS.OPEN); }); diff --git a/lib/commands/SCRIPT_DEBUG.ts b/packages/client/lib/commands/SCRIPT_DEBUG.ts similarity index 100% rename from lib/commands/SCRIPT_DEBUG.ts rename to packages/client/lib/commands/SCRIPT_DEBUG.ts diff --git a/lib/commands/SCRIPT_EXISTS.spec.ts b/packages/client/lib/commands/SCRIPT_EXISTS.spec.ts similarity index 80% rename from lib/commands/SCRIPT_EXISTS.spec.ts rename to packages/client/lib/commands/SCRIPT_EXISTS.spec.ts index b23380c7579..e0fbbcc5537 100644 --- a/lib/commands/SCRIPT_EXISTS.spec.ts +++ b/packages/client/lib/commands/SCRIPT_EXISTS.spec.ts @@ -1,5 +1,5 @@ import { strict as assert } from 'assert'; -import { TestRedisServers, itWithClient } from '../test-utils'; +import testUtils, { GLOBAL } from '../test-utils'; import { transformArguments } from './SCRIPT_EXISTS'; describe('SCRIPT EXISTS', () => { @@ -19,10 +19,10 @@ describe('SCRIPT EXISTS', () => { }); }); - itWithClient(TestRedisServers.OPEN, 'client.scriptExists', async client => { + testUtils.testWithClient('client.scriptExists', async client => { assert.deepEqual( await client.scriptExists('sha1'), [false] ); - }); + }, GLOBAL.SERVERS.OPEN); }); diff --git a/lib/commands/SCRIPT_EXISTS.ts b/packages/client/lib/commands/SCRIPT_EXISTS.ts similarity index 56% rename from lib/commands/SCRIPT_EXISTS.ts rename to packages/client/lib/commands/SCRIPT_EXISTS.ts index ee89f955e50..d4f65cfd72b 100644 --- a/lib/commands/SCRIPT_EXISTS.ts +++ b/packages/client/lib/commands/SCRIPT_EXISTS.ts @@ -1,8 +1,8 @@ import { RedisCommandArguments } from '.'; -import { pushVerdictArguments, transformReplyBooleanArray } from './generic-transformers'; +import { pushVerdictArguments } from './generic-transformers'; export function transformArguments(sha1: string | Array): RedisCommandArguments { return pushVerdictArguments(['SCRIPT', 'EXISTS'], sha1); } -export const transformReply = transformReplyBooleanArray; +export { transformReplyBooleanArray as transformReply } from './generic-transformers'; diff --git a/lib/commands/SCRIPT_FLUSH.spec.ts b/packages/client/lib/commands/SCRIPT_FLUSH.spec.ts similarity index 80% rename from lib/commands/SCRIPT_FLUSH.spec.ts rename to packages/client/lib/commands/SCRIPT_FLUSH.spec.ts index c77accb50a9..ae156e937d1 100644 --- a/lib/commands/SCRIPT_FLUSH.spec.ts +++ b/packages/client/lib/commands/SCRIPT_FLUSH.spec.ts @@ -1,5 +1,5 @@ import { strict as assert } from 'assert'; -import { TestRedisServers, itWithClient } from '../test-utils'; +import testUtils, { GLOBAL } from '../test-utils'; import { transformArguments } from './SCRIPT_FLUSH'; describe('SCRIPT FLUSH', () => { @@ -19,10 +19,10 @@ describe('SCRIPT FLUSH', () => { }); }); - itWithClient(TestRedisServers.OPEN, 'client.scriptFlush', async client => { + testUtils.testWithClient('client.scriptFlush', async client => { assert.equal( await client.scriptFlush(), 'OK' ); - }); + }, GLOBAL.SERVERS.OPEN); }); diff --git a/lib/commands/SCRIPT_FLUSH.ts b/packages/client/lib/commands/SCRIPT_FLUSH.ts similarity index 100% rename from lib/commands/SCRIPT_FLUSH.ts rename to packages/client/lib/commands/SCRIPT_FLUSH.ts diff --git a/lib/commands/SCRIPT_KILL.spec.ts b/packages/client/lib/commands/SCRIPT_KILL.spec.ts similarity index 100% rename from lib/commands/SCRIPT_KILL.spec.ts rename to packages/client/lib/commands/SCRIPT_KILL.spec.ts diff --git a/lib/commands/SCRIPT_KILL.ts b/packages/client/lib/commands/SCRIPT_KILL.ts similarity index 100% rename from lib/commands/SCRIPT_KILL.ts rename to packages/client/lib/commands/SCRIPT_KILL.ts diff --git a/lib/commands/SCRIPT_LOAD.spec.ts b/packages/client/lib/commands/SCRIPT_LOAD.spec.ts similarity index 77% rename from lib/commands/SCRIPT_LOAD.spec.ts rename to packages/client/lib/commands/SCRIPT_LOAD.spec.ts index 1d7da3e9c2d..062f3c201e1 100644 --- a/lib/commands/SCRIPT_LOAD.spec.ts +++ b/packages/client/lib/commands/SCRIPT_LOAD.spec.ts @@ -1,6 +1,6 @@ import { strict as assert } from 'assert'; import { scriptSha1 } from '../lua-script'; -import { TestRedisServers, itWithClient } from '../test-utils'; +import testUtils, { GLOBAL } from '../test-utils'; import { transformArguments } from './SCRIPT_LOAD'; describe('SCRIPT LOAD', () => { @@ -14,10 +14,10 @@ describe('SCRIPT LOAD', () => { ); }); - itWithClient(TestRedisServers.OPEN, 'client.scriptLoad', async client => { + testUtils.testWithClient('client.scriptLoad', async client => { assert.equal( await client.scriptLoad(SCRIPT), SCRIPT_SHA1 ); - }); + }, GLOBAL.SERVERS.OPEN); }); diff --git a/lib/commands/SCRIPT_LOAD.ts b/packages/client/lib/commands/SCRIPT_LOAD.ts similarity index 100% rename from lib/commands/SCRIPT_LOAD.ts rename to packages/client/lib/commands/SCRIPT_LOAD.ts diff --git a/lib/commands/SDIFF.spec.ts b/packages/client/lib/commands/SDIFF.spec.ts similarity index 80% rename from lib/commands/SDIFF.spec.ts rename to packages/client/lib/commands/SDIFF.spec.ts index 82ef2dac6fc..340906e9350 100644 --- a/lib/commands/SDIFF.spec.ts +++ b/packages/client/lib/commands/SDIFF.spec.ts @@ -1,5 +1,5 @@ import { strict as assert } from 'assert'; -import { TestRedisServers, itWithClient } from '../test-utils'; +import testUtils, { GLOBAL } from '../test-utils'; import { transformArguments } from './SDIFF'; describe('SDIFF', () => { @@ -19,10 +19,10 @@ describe('SDIFF', () => { }); }); - itWithClient(TestRedisServers.OPEN, 'client.sDiff', async client => { + testUtils.testWithClient('client.sDiff', async client => { assert.deepEqual( await client.sDiff('key'), [] ); - }); + }, GLOBAL.SERVERS.OPEN); }); diff --git a/lib/commands/SDIFF.ts b/packages/client/lib/commands/SDIFF.ts similarity index 100% rename from lib/commands/SDIFF.ts rename to packages/client/lib/commands/SDIFF.ts diff --git a/lib/commands/SDIFFSTORE.spec.ts b/packages/client/lib/commands/SDIFFSTORE.spec.ts similarity index 82% rename from lib/commands/SDIFFSTORE.spec.ts rename to packages/client/lib/commands/SDIFFSTORE.spec.ts index 1e7f5f6f32c..263b4f43f64 100644 --- a/lib/commands/SDIFFSTORE.spec.ts +++ b/packages/client/lib/commands/SDIFFSTORE.spec.ts @@ -1,5 +1,5 @@ import { strict as assert } from 'assert'; -import { TestRedisServers, itWithClient } from '../test-utils'; +import testUtils, { GLOBAL } from '../test-utils'; import { transformArguments } from './SDIFFSTORE'; describe('SDIFFSTORE', () => { @@ -19,10 +19,10 @@ describe('SDIFFSTORE', () => { }); }); - itWithClient(TestRedisServers.OPEN, 'client.sDiffStore', async client => { + testUtils.testWithClient('client.sDiffStore', async client => { assert.equal( await client.sDiffStore('destination', 'key'), 0 ); - }); + }, GLOBAL.SERVERS.OPEN); }); diff --git a/lib/commands/SDIFFSTORE.ts b/packages/client/lib/commands/SDIFFSTORE.ts similarity index 100% rename from lib/commands/SDIFFSTORE.ts rename to packages/client/lib/commands/SDIFFSTORE.ts diff --git a/lib/commands/SET.spec.ts b/packages/client/lib/commands/SET.spec.ts similarity index 91% rename from lib/commands/SET.spec.ts rename to packages/client/lib/commands/SET.spec.ts index 32d138f2920..353885a3097 100644 --- a/lib/commands/SET.spec.ts +++ b/packages/client/lib/commands/SET.spec.ts @@ -1,5 +1,5 @@ import { strict as assert } from 'assert'; -import { TestRedisServers, itWithClient } from '../test-utils'; +import testUtils, { GLOBAL } from '../test-utils'; import { transformArguments } from './SET'; describe('SET', () => { @@ -100,14 +100,14 @@ describe('SET', () => { }); describe('client.set', () => { - itWithClient(TestRedisServers.OPEN, 'simple', async client => { + testUtils.testWithClient('simple', async client => { assert.equal( await client.set('key', 'value'), 'OK' ); - }); - - itWithClient(TestRedisServers.OPEN, 'with GET on empty key', async client => { + }, GLOBAL.SERVERS.OPEN); + + testUtils.testWithClient('with GET on empty key', async client => { assert.equal( await client.set('key', 'value', { GET: true @@ -115,7 +115,8 @@ describe('SET', () => { null ); }, { - minimumRedisVersion: [6, 2] + ...GLOBAL.SERVERS.OPEN, + minimumDockerVersion: [6, 2] }); }); }); diff --git a/lib/commands/SET.ts b/packages/client/lib/commands/SET.ts similarity index 59% rename from lib/commands/SET.ts rename to packages/client/lib/commands/SET.ts index b19a1b2c5c2..fdc7eef1986 100644 --- a/lib/commands/SET.ts +++ b/packages/client/lib/commands/SET.ts @@ -2,43 +2,27 @@ import { RedisCommandArguments } from '.'; export const FIRST_KEY_INDEX = 1; -interface EX { - EX: number; -} - -interface PX { - PX: number -} +type MaximumOneOf = + K extends keyof T ? { [P in K]?: T[K] } & Partial, never>> : never; -interface EXAT { +type SetTTL = MaximumOneOf<{ + EX: number; + PX: number; EXAT: number; -} - -interface PXAT { PXAT: number; -} - -interface KEEPTTL { KEEPTTL: true; -} +}>; -type SetTTL = EX | PX | EXAT | PXAT | KEEPTTL | {}; - -interface NX { +type SetGuards = MaximumOneOf<{ NX: true; -} - -interface XX { XX: true; -} - -type SetGuards = NX | XX | {}; +}>; interface SetCommonOptions { - GET: true + GET?: true; } -type SetOptions = SetTTL & SetGuards & (SetCommonOptions | {}); +type SetOptions = SetTTL & SetGuards & SetCommonOptions; export function transformArguments(key: string | Buffer, value: string | Buffer, options?: SetOptions): RedisCommandArguments { const args = ['SET', key, value]; @@ -47,25 +31,25 @@ export function transformArguments(key: string | Buffer, value: string | Buffer, return args; } - if ('EX' in options) { + if (options.EX) { args.push('EX', options.EX.toString()); - } else if ('PX' in options) { + } else if (options.PX) { args.push('PX', options.PX.toString()); - } else if ('EXAT' in options) { + } else if (options.EXAT) { args.push('EXAT', options.EXAT.toString()); - } else if ('PXAT' in options) { + } else if (options.PXAT) { args.push('PXAT', options.PXAT.toString()); - } else if ((options).KEEPTTL) { + } else if (options.KEEPTTL) { args.push('KEEPTTL'); } - if ((options).NX) { + if (options.NX) { args.push('NX'); - } else if ((options).XX) { + } else if (options.XX) { args.push('XX'); } - if ((options).GET) { + if (options.GET) { args.push('GET'); } diff --git a/lib/commands/SETBIT.spec.ts b/packages/client/lib/commands/SETBIT.spec.ts similarity index 64% rename from lib/commands/SETBIT.spec.ts rename to packages/client/lib/commands/SETBIT.spec.ts index 7347913f293..43fbff7c2d9 100644 --- a/lib/commands/SETBIT.spec.ts +++ b/packages/client/lib/commands/SETBIT.spec.ts @@ -1,5 +1,5 @@ import { strict as assert } from 'assert'; -import { TestRedisServers, itWithClient, TestRedisClusters, itWithCluster } from '../test-utils'; +import testUtils, { GLOBAL } from '../test-utils'; import { transformArguments } from './SETBIT'; describe('SETBIT', () => { @@ -10,17 +10,17 @@ describe('SETBIT', () => { ); }); - itWithClient(TestRedisServers.OPEN, 'client.setBit', async client => { + testUtils.testWithClient('client.setBit', async client => { assert.equal( await client.setBit('key', 0, 1), 0 ); - }); + }, GLOBAL.SERVERS.OPEN); - itWithCluster(TestRedisClusters.OPEN, 'cluster.setBit', async cluster => { + testUtils.testWithCluster('cluster.setBit', async cluster => { assert.equal( await cluster.setBit('key', 0, 1), 0 ); - }); + }, GLOBAL.CLUSTERS.OPEN); }); diff --git a/lib/commands/SETBIT.ts b/packages/client/lib/commands/SETBIT.ts similarity index 100% rename from lib/commands/SETBIT.ts rename to packages/client/lib/commands/SETBIT.ts diff --git a/lib/commands/SETEX.spec.ts b/packages/client/lib/commands/SETEX.spec.ts similarity index 65% rename from lib/commands/SETEX.spec.ts rename to packages/client/lib/commands/SETEX.spec.ts index 7ea55eba83c..bca298c6c04 100644 --- a/lib/commands/SETEX.spec.ts +++ b/packages/client/lib/commands/SETEX.spec.ts @@ -1,5 +1,5 @@ import { strict as assert } from 'assert'; -import { TestRedisServers, itWithClient, TestRedisClusters, itWithCluster } from '../test-utils'; +import testUtils, { GLOBAL } from '../test-utils'; import { transformArguments } from './SETEX'; describe('SETEX', () => { @@ -10,17 +10,17 @@ describe('SETEX', () => { ); }); - itWithClient(TestRedisServers.OPEN, 'client.setEx', async client => { + testUtils.testWithClient('client.setEx', async client => { assert.equal( await client.setEx('key', 1, 'value'), 'OK' ); - }); + }, GLOBAL.SERVERS.OPEN); - itWithCluster(TestRedisClusters.OPEN, 'cluster.setEx', async cluster => { + testUtils.testWithCluster('cluster.setEx', async cluster => { assert.equal( await cluster.setEx('key', 1, 'value'), 'OK' ); - }); + }, GLOBAL.CLUSTERS.OPEN); }); diff --git a/lib/commands/SETEX.ts b/packages/client/lib/commands/SETEX.ts similarity index 100% rename from lib/commands/SETEX.ts rename to packages/client/lib/commands/SETEX.ts diff --git a/lib/commands/SETNX .spec.ts b/packages/client/lib/commands/SETNX .spec.ts similarity index 64% rename from lib/commands/SETNX .spec.ts rename to packages/client/lib/commands/SETNX .spec.ts index daf3ca6e76a..c5bdfcffa2c 100644 --- a/lib/commands/SETNX .spec.ts +++ b/packages/client/lib/commands/SETNX .spec.ts @@ -1,5 +1,5 @@ import { strict as assert } from 'assert'; -import { TestRedisServers, itWithClient, TestRedisClusters, itWithCluster } from '../test-utils'; +import testUtils, { GLOBAL } from '../test-utils'; import { transformArguments } from './SETNX'; describe('SETNX', () => { @@ -10,17 +10,17 @@ describe('SETNX', () => { ); }); - itWithClient(TestRedisServers.OPEN, 'client.setNX', async client => { + testUtils.testWithClient('client.setNX', async client => { assert.equal( await client.setNX('key', 'value'), true ); - }); + }, GLOBAL.SERVERS.OPEN); - itWithCluster(TestRedisClusters.OPEN, 'cluster.setNX', async cluster => { + testUtils.testWithCluster('cluster.setNX', async cluster => { assert.equal( await cluster.setNX('key', 'value'), true ); - }); + }, GLOBAL.CLUSTERS.OPEN); }); diff --git a/lib/commands/SETNX.ts b/packages/client/lib/commands/SETNX.ts similarity index 56% rename from lib/commands/SETNX.ts rename to packages/client/lib/commands/SETNX.ts index f0097836581..b45e93b0f7d 100644 --- a/lib/commands/SETNX.ts +++ b/packages/client/lib/commands/SETNX.ts @@ -1,9 +1,7 @@ -import { transformReplyBoolean } from './generic-transformers'; - export const FIRST_KEY_INDEX = 1; export function transformArguments(key: string, value: string): Array { return ['SETNX', key, value]; } -export const transformReply = transformReplyBoolean; +export { transformReplyBoolean as transformReply } from './generic-transformers'; diff --git a/lib/commands/SETRANGE.spec.ts b/packages/client/lib/commands/SETRANGE.spec.ts similarity index 65% rename from lib/commands/SETRANGE.spec.ts rename to packages/client/lib/commands/SETRANGE.spec.ts index 766c56c5ff1..398b7730404 100644 --- a/lib/commands/SETRANGE.spec.ts +++ b/packages/client/lib/commands/SETRANGE.spec.ts @@ -1,5 +1,5 @@ import { strict as assert } from 'assert'; -import { TestRedisServers, itWithClient, itWithCluster, TestRedisClusters } from '../test-utils'; +import testUtils, { GLOBAL } from '../test-utils'; import { transformArguments } from './SETRANGE'; describe('SETRANGE', () => { @@ -10,17 +10,17 @@ describe('SETRANGE', () => { ); }); - itWithClient(TestRedisServers.OPEN, 'client.setRange', async client => { + testUtils.testWithClient('client.setRange', async client => { assert.equal( await client.setRange('key', 0, 'value'), 5 ); - }); + }, GLOBAL.SERVERS.OPEN); - itWithCluster(TestRedisClusters.OPEN, 'cluster.setRange', async cluster => { + testUtils.testWithCluster('cluster.setRange', async cluster => { assert.equal( await cluster.setRange('key', 0, 'value'), 5 ); - }); + }, GLOBAL.CLUSTERS.OPEN); }); diff --git a/lib/commands/SETRANGE.ts b/packages/client/lib/commands/SETRANGE.ts similarity index 100% rename from lib/commands/SETRANGE.ts rename to packages/client/lib/commands/SETRANGE.ts diff --git a/lib/commands/SHUTDOWN.spec.ts b/packages/client/lib/commands/SHUTDOWN.spec.ts similarity index 100% rename from lib/commands/SHUTDOWN.spec.ts rename to packages/client/lib/commands/SHUTDOWN.spec.ts diff --git a/lib/commands/SHUTDOWN.ts b/packages/client/lib/commands/SHUTDOWN.ts similarity index 100% rename from lib/commands/SHUTDOWN.ts rename to packages/client/lib/commands/SHUTDOWN.ts diff --git a/lib/commands/SINTER.spec.ts b/packages/client/lib/commands/SINTER.spec.ts similarity index 80% rename from lib/commands/SINTER.spec.ts rename to packages/client/lib/commands/SINTER.spec.ts index 8fee35427cf..2324eac3ee8 100644 --- a/lib/commands/SINTER.spec.ts +++ b/packages/client/lib/commands/SINTER.spec.ts @@ -1,5 +1,5 @@ import { strict as assert } from 'assert'; -import { TestRedisServers, itWithClient } from '../test-utils'; +import testUtils, { GLOBAL } from '../test-utils'; import { transformArguments } from './SINTER'; describe('SINTER', () => { @@ -19,10 +19,10 @@ describe('SINTER', () => { }); }); - itWithClient(TestRedisServers.OPEN, 'client.sInter', async client => { + testUtils.testWithClient('client.sInter', async client => { assert.deepEqual( await client.sInter('key'), [] ); - }); + }, GLOBAL.SERVERS.OPEN); }); diff --git a/lib/commands/SINTER.ts b/packages/client/lib/commands/SINTER.ts similarity index 100% rename from lib/commands/SINTER.ts rename to packages/client/lib/commands/SINTER.ts diff --git a/lib/commands/SINTERSTORE.spec.ts b/packages/client/lib/commands/SINTERSTORE.spec.ts similarity index 82% rename from lib/commands/SINTERSTORE.spec.ts rename to packages/client/lib/commands/SINTERSTORE.spec.ts index 013931d2312..c4a6a095e7d 100644 --- a/lib/commands/SINTERSTORE.spec.ts +++ b/packages/client/lib/commands/SINTERSTORE.spec.ts @@ -1,5 +1,5 @@ import { strict as assert } from 'assert'; -import { TestRedisServers, itWithClient } from '../test-utils'; +import testUtils, { GLOBAL } from '../test-utils'; import { transformArguments } from './SINTERSTORE'; describe('SINTERSTORE', () => { @@ -19,10 +19,10 @@ describe('SINTERSTORE', () => { }); }); - itWithClient(TestRedisServers.OPEN, 'client.sInterStore', async client => { + testUtils.testWithClient('client.sInterStore', async client => { assert.equal( await client.sInterStore('destination', 'key'), 0 ); - }); + }, GLOBAL.SERVERS.OPEN); }); diff --git a/lib/commands/SINTERSTORE.ts b/packages/client/lib/commands/SINTERSTORE.ts similarity index 100% rename from lib/commands/SINTERSTORE.ts rename to packages/client/lib/commands/SINTERSTORE.ts diff --git a/lib/commands/SISMEMBER.spec.ts b/packages/client/lib/commands/SISMEMBER.spec.ts similarity index 63% rename from lib/commands/SISMEMBER.spec.ts rename to packages/client/lib/commands/SISMEMBER.spec.ts index fec4ebfc57d..8d18c83697a 100644 --- a/lib/commands/SISMEMBER.spec.ts +++ b/packages/client/lib/commands/SISMEMBER.spec.ts @@ -1,10 +1,8 @@ import { strict as assert } from 'assert'; -import { TestRedisServers, itWithClient, describeHandleMinimumRedisVersion } from '../test-utils'; +import testUtils, { GLOBAL } from '../test-utils'; import { transformArguments } from './SISMEMBER'; describe('SISMEMBER', () => { - describeHandleMinimumRedisVersion([6, 2]); - it('transformArguments', () => { assert.deepEqual( transformArguments('key', 'member'), @@ -12,10 +10,10 @@ describe('SISMEMBER', () => { ); }); - itWithClient(TestRedisServers.OPEN, 'client.sIsMember', async client => { + testUtils.testWithClient('client.sIsMember', async client => { assert.equal( await client.sIsMember('key', 'member'), false ); - }); + }, GLOBAL.SERVERS.OPEN); }); diff --git a/lib/commands/SISMEMBER.ts b/packages/client/lib/commands/SISMEMBER.ts similarity index 57% rename from lib/commands/SISMEMBER.ts rename to packages/client/lib/commands/SISMEMBER.ts index 661410fce0d..d8c47a76a69 100644 --- a/lib/commands/SISMEMBER.ts +++ b/packages/client/lib/commands/SISMEMBER.ts @@ -1,9 +1,7 @@ -import { transformReplyBoolean } from './generic-transformers'; - export const FIRST_KEY_INDEX = 1; export function transformArguments(key: string, member: string): Array { return ['SISMEMBER', key, member]; } -export const transformReply = transformReplyBoolean; +export { transformReplyBoolean as transformReply } from './generic-transformers'; diff --git a/lib/commands/SMEMBERS.spec.ts b/packages/client/lib/commands/SMEMBERS.spec.ts similarity index 71% rename from lib/commands/SMEMBERS.spec.ts rename to packages/client/lib/commands/SMEMBERS.spec.ts index 2398dbaa8c6..b9c58c9eebb 100644 --- a/lib/commands/SMEMBERS.spec.ts +++ b/packages/client/lib/commands/SMEMBERS.spec.ts @@ -1,5 +1,5 @@ import { strict as assert } from 'assert'; -import { TestRedisServers, itWithClient } from '../test-utils'; +import testUtils, { GLOBAL } from '../test-utils'; import { transformArguments } from './SMEMBERS'; describe('SMEMBERS', () => { @@ -10,10 +10,10 @@ describe('SMEMBERS', () => { ); }); - itWithClient(TestRedisServers.OPEN, 'client.sMembers', async client => { + testUtils.testWithClient('client.sMembers', async client => { assert.deepEqual( await client.sMembers('key'), [] ); - }); + }, GLOBAL.SERVERS.OPEN); }); diff --git a/lib/commands/SMEMBERS.ts b/packages/client/lib/commands/SMEMBERS.ts similarity index 100% rename from lib/commands/SMEMBERS.ts rename to packages/client/lib/commands/SMEMBERS.ts diff --git a/lib/commands/SMISMEMBER.spec.ts b/packages/client/lib/commands/SMISMEMBER.spec.ts similarity index 64% rename from lib/commands/SMISMEMBER.spec.ts rename to packages/client/lib/commands/SMISMEMBER.spec.ts index 320f60d4ba2..e3728134029 100644 --- a/lib/commands/SMISMEMBER.spec.ts +++ b/packages/client/lib/commands/SMISMEMBER.spec.ts @@ -1,9 +1,9 @@ import { strict as assert } from 'assert'; -import { TestRedisServers, itWithClient, describeHandleMinimumRedisVersion } from '../test-utils'; +import testUtils, { GLOBAL } from '../test-utils'; import { transformArguments } from './SMISMEMBER'; describe('SMISMEMBER', () => { - describeHandleMinimumRedisVersion([6, 2]); + testUtils.isVersionGreaterThanHook([6, 2]); it('transformArguments', () => { assert.deepEqual( @@ -12,10 +12,10 @@ describe('SMISMEMBER', () => { ); }); - itWithClient(TestRedisServers.OPEN, 'client.smIsMember', async client => { + testUtils.testWithClient('client.smIsMember', async client => { assert.deepEqual( await client.smIsMember('key', ['1', '2']), [false, false] ); - }); + }, GLOBAL.SERVERS.OPEN); }); diff --git a/lib/commands/SMISMEMBER.ts b/packages/client/lib/commands/SMISMEMBER.ts similarity index 57% rename from lib/commands/SMISMEMBER.ts rename to packages/client/lib/commands/SMISMEMBER.ts index 07637a689b6..85b954bc3d7 100644 --- a/lib/commands/SMISMEMBER.ts +++ b/packages/client/lib/commands/SMISMEMBER.ts @@ -1,9 +1,7 @@ -import { transformReplyBooleanArray } from './generic-transformers'; - export const FIRST_KEY_INDEX = 1; export function transformArguments(key: string, members: Array): Array { return ['SMISMEMBER', key, ...members]; } -export const transformReply = transformReplyBooleanArray; +export { transformReplyBooleanArray as transformReply } from './generic-transformers'; diff --git a/lib/commands/SMOVE.spec.ts b/packages/client/lib/commands/SMOVE.spec.ts similarity index 75% rename from lib/commands/SMOVE.spec.ts rename to packages/client/lib/commands/SMOVE.spec.ts index 97e938a46bb..e3308ee8143 100644 --- a/lib/commands/SMOVE.spec.ts +++ b/packages/client/lib/commands/SMOVE.spec.ts @@ -1,5 +1,5 @@ import { strict as assert } from 'assert'; -import { TestRedisServers, itWithClient } from '../test-utils'; +import testUtils, { GLOBAL } from '../test-utils'; import { transformArguments } from './SMOVE'; describe('SMOVE', () => { @@ -10,10 +10,10 @@ describe('SMOVE', () => { ); }); - itWithClient(TestRedisServers.OPEN, 'client.sMove', async client => { + testUtils.testWithClient('client.sMove', async client => { assert.equal( await client.sMove('source', 'destination', 'member'), false ); - }); + }, GLOBAL.SERVERS.OPEN); }); diff --git a/lib/commands/SMOVE.ts b/packages/client/lib/commands/SMOVE.ts similarity index 62% rename from lib/commands/SMOVE.ts rename to packages/client/lib/commands/SMOVE.ts index f8922f6ca3f..7850b8f9ddc 100644 --- a/lib/commands/SMOVE.ts +++ b/packages/client/lib/commands/SMOVE.ts @@ -1,9 +1,7 @@ -import { transformReplyBoolean } from './generic-transformers'; - export const FIRST_KEY_INDEX = 1; export function transformArguments(source: string, destination: string, member: string): Array { return ['SMOVE', source, destination, member]; } -export const transformReply = transformReplyBoolean; +export { transformReplyBoolean as transformReply } from './generic-transformers'; diff --git a/lib/commands/SORT.spec.ts b/packages/client/lib/commands/SORT.spec.ts similarity index 94% rename from lib/commands/SORT.spec.ts rename to packages/client/lib/commands/SORT.spec.ts index c449e0511f0..637f48876dc 100644 --- a/lib/commands/SORT.spec.ts +++ b/packages/client/lib/commands/SORT.spec.ts @@ -1,5 +1,5 @@ import { strict as assert } from 'assert'; -import { TestRedisServers, itWithClient } from '../test-utils'; +import testUtils, { GLOBAL } from '../test-utils'; import { transformArguments } from './SORT'; describe('SORT', () => { @@ -60,7 +60,7 @@ describe('SORT', () => { ['SORT', 'key', 'ASC'] ); }); - + it('with ALPHA', () => { assert.deepEqual( transformArguments('key', { @@ -97,10 +97,10 @@ describe('SORT', () => { }); }); - itWithClient(TestRedisServers.OPEN, 'client.sort', async client => { + testUtils.testWithClient('client.sort', async client => { assert.deepEqual( await client.sort('key'), [] ); - }); + }, GLOBAL.SERVERS.OPEN); }); diff --git a/lib/commands/SORT.ts b/packages/client/lib/commands/SORT.ts similarity index 100% rename from lib/commands/SORT.ts rename to packages/client/lib/commands/SORT.ts diff --git a/lib/commands/SPOP.spec.ts b/packages/client/lib/commands/SPOP.spec.ts similarity index 80% rename from lib/commands/SPOP.spec.ts rename to packages/client/lib/commands/SPOP.spec.ts index 238c58f4796..6a384d181fc 100644 --- a/lib/commands/SPOP.spec.ts +++ b/packages/client/lib/commands/SPOP.spec.ts @@ -1,5 +1,5 @@ import { strict as assert } from 'assert'; -import { TestRedisServers, itWithClient } from '../test-utils'; +import testUtils, { GLOBAL } from '../test-utils'; import { transformArguments } from './SPOP'; describe('SPOP', () => { @@ -19,10 +19,10 @@ describe('SPOP', () => { }); }); - itWithClient(TestRedisServers.OPEN, 'client.sPop', async client => { + testUtils.testWithClient('client.sPop', async client => { assert.equal( await client.sPop('key'), null ); - }); + }, GLOBAL.SERVERS.OPEN); }); diff --git a/lib/commands/SPOP.ts b/packages/client/lib/commands/SPOP.ts similarity index 100% rename from lib/commands/SPOP.ts rename to packages/client/lib/commands/SPOP.ts diff --git a/lib/commands/SRANDMEMBER.spec.ts b/packages/client/lib/commands/SRANDMEMBER.spec.ts similarity index 71% rename from lib/commands/SRANDMEMBER.spec.ts rename to packages/client/lib/commands/SRANDMEMBER.spec.ts index 5c359f73f96..291271540be 100644 --- a/lib/commands/SRANDMEMBER.spec.ts +++ b/packages/client/lib/commands/SRANDMEMBER.spec.ts @@ -1,5 +1,5 @@ import { strict as assert } from 'assert'; -import { TestRedisServers, itWithClient } from '../test-utils'; +import testUtils, { GLOBAL } from '../test-utils'; import { transformArguments } from './SRANDMEMBER'; describe('SRANDMEMBER', () => { @@ -10,10 +10,10 @@ describe('SRANDMEMBER', () => { ); }); - itWithClient(TestRedisServers.OPEN, 'client.sRandMember', async client => { + testUtils.testWithClient('client.sRandMember', async client => { assert.equal( await client.sRandMember('key'), null ); - }); + }, GLOBAL.SERVERS.OPEN); }); diff --git a/lib/commands/SRANDMEMBER.ts b/packages/client/lib/commands/SRANDMEMBER.ts similarity index 100% rename from lib/commands/SRANDMEMBER.ts rename to packages/client/lib/commands/SRANDMEMBER.ts diff --git a/lib/commands/SRANDMEMBER_COUNT.spec.ts b/packages/client/lib/commands/SRANDMEMBER_COUNT.spec.ts similarity index 72% rename from lib/commands/SRANDMEMBER_COUNT.spec.ts rename to packages/client/lib/commands/SRANDMEMBER_COUNT.spec.ts index 81a4fd45f31..d3d787b3e63 100644 --- a/lib/commands/SRANDMEMBER_COUNT.spec.ts +++ b/packages/client/lib/commands/SRANDMEMBER_COUNT.spec.ts @@ -1,5 +1,5 @@ import { strict as assert } from 'assert'; -import { TestRedisServers, itWithClient } from '../test-utils'; +import testUtils, { GLOBAL } from '../test-utils'; import { transformArguments } from './SRANDMEMBER_COUNT'; describe('SRANDMEMBER COUNT', () => { @@ -10,10 +10,10 @@ describe('SRANDMEMBER COUNT', () => { ); }); - itWithClient(TestRedisServers.OPEN, 'client.sRandMemberCount', async client => { + testUtils.testWithClient('client.sRandMemberCount', async client => { assert.deepEqual( await client.sRandMemberCount('key', 1), [] ); - }); + }, GLOBAL.SERVERS.OPEN); }); diff --git a/lib/commands/SRANDMEMBER_COUNT.ts b/packages/client/lib/commands/SRANDMEMBER_COUNT.ts similarity index 100% rename from lib/commands/SRANDMEMBER_COUNT.ts rename to packages/client/lib/commands/SRANDMEMBER_COUNT.ts diff --git a/lib/commands/SREM.spec.ts b/packages/client/lib/commands/SREM.spec.ts similarity index 81% rename from lib/commands/SREM.spec.ts rename to packages/client/lib/commands/SREM.spec.ts index c9270624ae9..d53d7b0334d 100644 --- a/lib/commands/SREM.spec.ts +++ b/packages/client/lib/commands/SREM.spec.ts @@ -1,5 +1,5 @@ import { strict as assert } from 'assert'; -import { TestRedisServers, itWithClient } from '../test-utils'; +import testUtils, { GLOBAL } from '../test-utils'; import { transformArguments } from './SREM'; describe('SREM', () => { @@ -19,10 +19,10 @@ describe('SREM', () => { }); }); - itWithClient(TestRedisServers.OPEN, 'client.sRem', async client => { + testUtils.testWithClient('client.sRem', async client => { assert.equal( await client.sRem('key', 'member'), 0 ); - }); + }, GLOBAL.SERVERS.OPEN); }); diff --git a/lib/commands/SREM.ts b/packages/client/lib/commands/SREM.ts similarity index 100% rename from lib/commands/SREM.ts rename to packages/client/lib/commands/SREM.ts diff --git a/lib/commands/SSCAN.spec.ts b/packages/client/lib/commands/SSCAN.spec.ts similarity index 92% rename from lib/commands/SSCAN.spec.ts rename to packages/client/lib/commands/SSCAN.spec.ts index 9b203ffb83e..71a90bf81d8 100644 --- a/lib/commands/SSCAN.spec.ts +++ b/packages/client/lib/commands/SSCAN.spec.ts @@ -1,5 +1,5 @@ import { strict as assert } from 'assert'; -import { TestRedisServers, itWithClient } from '../test-utils'; +import testUtils, { GLOBAL } from '../test-utils'; import { transformArguments, transformReply } from './SSCAN'; describe('SSCAN', () => { @@ -62,7 +62,7 @@ describe('SSCAN', () => { }); }); - itWithClient(TestRedisServers.OPEN, 'client.sScan', async client => { + testUtils.testWithClient('client.sScan', async client => { assert.deepEqual( await client.sScan('key', 0), { @@ -70,5 +70,5 @@ describe('SSCAN', () => { members: [] } ); - }); + }, GLOBAL.SERVERS.OPEN); }); diff --git a/lib/commands/SSCAN.ts b/packages/client/lib/commands/SSCAN.ts similarity index 100% rename from lib/commands/SSCAN.ts rename to packages/client/lib/commands/SSCAN.ts diff --git a/lib/commands/STRLEN.spec.ts b/packages/client/lib/commands/STRLEN.spec.ts similarity index 62% rename from lib/commands/STRLEN.spec.ts rename to packages/client/lib/commands/STRLEN.spec.ts index 3d24e360372..519c68d3e5d 100644 --- a/lib/commands/STRLEN.spec.ts +++ b/packages/client/lib/commands/STRLEN.spec.ts @@ -1,5 +1,5 @@ import { strict as assert } from 'assert'; -import { TestRedisServers, itWithClient, itWithCluster, TestRedisClusters } from '../test-utils'; +import testUtils, { GLOBAL } from '../test-utils'; import { transformArguments } from './STRLEN'; describe('STRLEN', () => { @@ -10,17 +10,17 @@ describe('STRLEN', () => { ); }); - itWithClient(TestRedisServers.OPEN, 'client.strLen', async client => { + testUtils.testWithClient('client.strLen', async client => { assert.equal( await client.strLen('key'), 0 ); - }); + }, GLOBAL.SERVERS.OPEN); - itWithCluster(TestRedisClusters.OPEN, 'cluster.strLen', async cluster => { + testUtils.testWithCluster('cluster.strLen', async cluster => { assert.equal( await cluster.strLen('key'), 0 ); - }); + }, GLOBAL.CLUSTERS.OPEN); }); diff --git a/lib/commands/STRLEN.ts b/packages/client/lib/commands/STRLEN.ts similarity index 100% rename from lib/commands/STRLEN.ts rename to packages/client/lib/commands/STRLEN.ts diff --git a/lib/commands/SUNION.spec.ts b/packages/client/lib/commands/SUNION.spec.ts similarity index 80% rename from lib/commands/SUNION.spec.ts rename to packages/client/lib/commands/SUNION.spec.ts index fdf97668971..2918607c1d6 100644 --- a/lib/commands/SUNION.spec.ts +++ b/packages/client/lib/commands/SUNION.spec.ts @@ -1,5 +1,5 @@ import { strict as assert } from 'assert'; -import { TestRedisServers, itWithClient } from '../test-utils'; +import testUtils, { GLOBAL } from '../test-utils'; import { transformArguments } from './SUNION'; describe('SUNION', () => { @@ -19,10 +19,10 @@ describe('SUNION', () => { }); }); - itWithClient(TestRedisServers.OPEN, 'client.sUnion', async client => { + testUtils.testWithClient('client.sUnion', async client => { assert.deepEqual( await client.sUnion('key'), [] ); - }); + }, GLOBAL.SERVERS.OPEN); }); diff --git a/lib/commands/SUNION.ts b/packages/client/lib/commands/SUNION.ts similarity index 100% rename from lib/commands/SUNION.ts rename to packages/client/lib/commands/SUNION.ts diff --git a/lib/commands/SUNIONSTORE.spec.ts b/packages/client/lib/commands/SUNIONSTORE.spec.ts similarity index 82% rename from lib/commands/SUNIONSTORE.spec.ts rename to packages/client/lib/commands/SUNIONSTORE.spec.ts index 82c9a03a0b8..142533eea2b 100644 --- a/lib/commands/SUNIONSTORE.spec.ts +++ b/packages/client/lib/commands/SUNIONSTORE.spec.ts @@ -1,5 +1,5 @@ import { strict as assert } from 'assert'; -import { TestRedisServers, itWithClient } from '../test-utils'; +import testUtils, { GLOBAL } from '../test-utils'; import { transformArguments } from './SUNIONSTORE'; describe('SUNIONSTORE', () => { @@ -19,10 +19,10 @@ describe('SUNIONSTORE', () => { }); }); - itWithClient(TestRedisServers.OPEN, 'client.sUnionStore', async client => { + testUtils.testWithClient('client.sUnionStore', async client => { assert.equal( await client.sUnionStore('destination', 'key'), 0 ); - }); + }, GLOBAL.SERVERS.OPEN); }); diff --git a/lib/commands/SUNIONSTORE.ts b/packages/client/lib/commands/SUNIONSTORE.ts similarity index 100% rename from lib/commands/SUNIONSTORE.ts rename to packages/client/lib/commands/SUNIONSTORE.ts diff --git a/lib/commands/SWAPDB.spec.ts b/packages/client/lib/commands/SWAPDB.spec.ts similarity index 71% rename from lib/commands/SWAPDB.spec.ts rename to packages/client/lib/commands/SWAPDB.spec.ts index 1a5637ae43d..add87512a64 100644 --- a/lib/commands/SWAPDB.spec.ts +++ b/packages/client/lib/commands/SWAPDB.spec.ts @@ -1,5 +1,5 @@ import { strict as assert } from 'assert'; -import { itWithClient, TestRedisServers } from '../test-utils'; +import testUtils, { GLOBAL } from '../test-utils'; import { transformArguments } from './SWAPDB'; describe('SWAPDB', () => { @@ -10,10 +10,10 @@ describe('SWAPDB', () => { ); }); - itWithClient(TestRedisServers.OPEN, 'client.swapDb', async client => { + testUtils.testWithClient('client.swapDb', async client => { assert.equal( await client.swapDb(0, 1), 'OK' ); - }); + }, GLOBAL.SERVERS.OPEN); }); diff --git a/lib/commands/SWAPDB.ts b/packages/client/lib/commands/SWAPDB.ts similarity index 100% rename from lib/commands/SWAPDB.ts rename to packages/client/lib/commands/SWAPDB.ts diff --git a/lib/commands/TIME.spec.ts b/packages/client/lib/commands/TIME.spec.ts similarity index 73% rename from lib/commands/TIME.spec.ts rename to packages/client/lib/commands/TIME.spec.ts index 1a07114af4b..bbaa7942db0 100644 --- a/lib/commands/TIME.spec.ts +++ b/packages/client/lib/commands/TIME.spec.ts @@ -1,5 +1,5 @@ import { strict as assert } from 'assert'; -import { TestRedisServers, itWithClient } from '../test-utils'; +import testUtils, { GLOBAL } from '../test-utils'; import { transformArguments } from './TIME'; describe('TIME', () => { @@ -10,9 +10,9 @@ describe('TIME', () => { ); }); - itWithClient(TestRedisServers.OPEN, 'client.time', async client => { + testUtils.testWithClient('client.time', async client => { const reply = await client.time(); assert.ok(reply instanceof Date); assert.ok(typeof reply.microseconds === 'number'); - }); + }, GLOBAL.SERVERS.OPEN); }); diff --git a/lib/commands/TIME.ts b/packages/client/lib/commands/TIME.ts similarity index 100% rename from lib/commands/TIME.ts rename to packages/client/lib/commands/TIME.ts diff --git a/lib/commands/TOUCH.spec.ts b/packages/client/lib/commands/TOUCH.spec.ts similarity index 80% rename from lib/commands/TOUCH.spec.ts rename to packages/client/lib/commands/TOUCH.spec.ts index c4cb4356291..578c49587d7 100644 --- a/lib/commands/TOUCH.spec.ts +++ b/packages/client/lib/commands/TOUCH.spec.ts @@ -1,5 +1,5 @@ import { strict as assert } from 'assert'; -import { TestRedisServers, itWithClient } from '../test-utils'; +import testUtils, { GLOBAL } from '../test-utils'; import { transformArguments } from './TOUCH'; describe('TOUCH', () => { @@ -19,10 +19,10 @@ describe('TOUCH', () => { }); }); - itWithClient(TestRedisServers.OPEN, 'client.touch', async client => { + testUtils.testWithClient('client.touch', async client => { assert.equal( await client.touch('key'), 0 ); - }); + }, GLOBAL.SERVERS.OPEN); }); diff --git a/lib/commands/TOUCH.ts b/packages/client/lib/commands/TOUCH.ts similarity index 100% rename from lib/commands/TOUCH.ts rename to packages/client/lib/commands/TOUCH.ts diff --git a/lib/commands/TTL.spec.ts b/packages/client/lib/commands/TTL.spec.ts similarity index 70% rename from lib/commands/TTL.spec.ts rename to packages/client/lib/commands/TTL.spec.ts index bcabe8d39e5..e37a6ab714b 100644 --- a/lib/commands/TTL.spec.ts +++ b/packages/client/lib/commands/TTL.spec.ts @@ -1,5 +1,5 @@ import { strict as assert } from 'assert'; -import { TestRedisServers, itWithClient } from '../test-utils'; +import testUtils, { GLOBAL } from '../test-utils'; import { transformArguments } from './TTL'; describe('TTL', () => { @@ -10,10 +10,10 @@ describe('TTL', () => { ); }); - itWithClient(TestRedisServers.OPEN, 'client.ttl', async client => { + testUtils.testWithClient('client.ttl', async client => { assert.equal( await client.ttl('key'), -2 ); - }); + }, GLOBAL.SERVERS.OPEN); }); diff --git a/lib/commands/TTL.ts b/packages/client/lib/commands/TTL.ts similarity index 100% rename from lib/commands/TTL.ts rename to packages/client/lib/commands/TTL.ts diff --git a/lib/commands/TYPE.spec.ts b/packages/client/lib/commands/TYPE.spec.ts similarity index 71% rename from lib/commands/TYPE.spec.ts rename to packages/client/lib/commands/TYPE.spec.ts index d40f724242d..1040bf979b3 100644 --- a/lib/commands/TYPE.spec.ts +++ b/packages/client/lib/commands/TYPE.spec.ts @@ -1,5 +1,5 @@ import { strict as assert } from 'assert'; -import { TestRedisServers, itWithClient } from '../test-utils'; +import testUtils, { GLOBAL } from '../test-utils'; import { transformArguments } from './TYPE'; describe('TYPE', () => { @@ -10,10 +10,10 @@ describe('TYPE', () => { ); }); - itWithClient(TestRedisServers.OPEN, 'client.type', async client => { + testUtils.testWithClient('client.type', async client => { assert.equal( await client.type('key'), 'none' ); - }); + }, GLOBAL.SERVERS.OPEN); }); diff --git a/lib/commands/TYPE.ts b/packages/client/lib/commands/TYPE.ts similarity index 100% rename from lib/commands/TYPE.ts rename to packages/client/lib/commands/TYPE.ts diff --git a/lib/commands/UNLINK.spec.ts b/packages/client/lib/commands/UNLINK.spec.ts similarity index 80% rename from lib/commands/UNLINK.spec.ts rename to packages/client/lib/commands/UNLINK.spec.ts index a0dddf54f25..e8355407d8f 100644 --- a/lib/commands/UNLINK.spec.ts +++ b/packages/client/lib/commands/UNLINK.spec.ts @@ -1,5 +1,5 @@ import { strict as assert } from 'assert'; -import { TestRedisServers, itWithClient } from '../test-utils'; +import testUtils, { GLOBAL } from '../test-utils'; import { transformArguments } from './UNLINK'; describe('UNLINK', () => { @@ -19,10 +19,10 @@ describe('UNLINK', () => { }); }); - itWithClient(TestRedisServers.OPEN, 'client.unlink', async client => { + testUtils.testWithClient('client.unlink', async client => { assert.equal( await client.unlink('key'), 0 ); - }); + }, GLOBAL.SERVERS.OPEN); }); diff --git a/lib/commands/UNLINK.ts b/packages/client/lib/commands/UNLINK.ts similarity index 100% rename from lib/commands/UNLINK.ts rename to packages/client/lib/commands/UNLINK.ts diff --git a/lib/commands/UNWATCH.spec.ts b/packages/client/lib/commands/UNWATCH.spec.ts similarity index 70% rename from lib/commands/UNWATCH.spec.ts rename to packages/client/lib/commands/UNWATCH.spec.ts index 07059310cbc..109ed0fa7c0 100644 --- a/lib/commands/UNWATCH.spec.ts +++ b/packages/client/lib/commands/UNWATCH.spec.ts @@ -1,5 +1,5 @@ import { strict as assert } from 'assert'; -import { TestRedisServers, itWithClient } from '../test-utils'; +import testUtils, { GLOBAL } from '../test-utils'; import { transformArguments } from './UNWATCH'; describe('UNWATCH', () => { @@ -10,10 +10,10 @@ describe('UNWATCH', () => { ); }); - itWithClient(TestRedisServers.OPEN, 'client.unwatch', async client => { + testUtils.testWithClient('client.unwatch', async client => { assert.equal( await client.unwatch(), 'OK' ); - }); + }, GLOBAL.SERVERS.OPEN); }); diff --git a/lib/commands/UNWATCH.ts b/packages/client/lib/commands/UNWATCH.ts similarity index 100% rename from lib/commands/UNWATCH.ts rename to packages/client/lib/commands/UNWATCH.ts diff --git a/lib/commands/WAIT.spec.ts b/packages/client/lib/commands/WAIT.spec.ts similarity index 70% rename from lib/commands/WAIT.spec.ts rename to packages/client/lib/commands/WAIT.spec.ts index c3f53b7db70..c85ef598612 100644 --- a/lib/commands/WAIT.spec.ts +++ b/packages/client/lib/commands/WAIT.spec.ts @@ -1,5 +1,5 @@ import { strict as assert } from 'assert'; -import { TestRedisServers, itWithClient } from '../test-utils'; +import testUtils, { GLOBAL } from '../test-utils'; import { transformArguments } from './WAIT'; describe('WAIT', () => { @@ -10,10 +10,10 @@ describe('WAIT', () => { ); }); - itWithClient(TestRedisServers.OPEN, 'client.wait', async client => { + testUtils.testWithClient('client.wait', async client => { assert.equal( await client.wait(0, 1), 0 ); - }); + }, GLOBAL.SERVERS.OPEN); }); diff --git a/lib/commands/WAIT.ts b/packages/client/lib/commands/WAIT.ts similarity index 100% rename from lib/commands/WAIT.ts rename to packages/client/lib/commands/WAIT.ts diff --git a/lib/commands/WATCH.spec.ts b/packages/client/lib/commands/WATCH.spec.ts similarity index 100% rename from lib/commands/WATCH.spec.ts rename to packages/client/lib/commands/WATCH.spec.ts diff --git a/lib/commands/WATCH.ts b/packages/client/lib/commands/WATCH.ts similarity index 100% rename from lib/commands/WATCH.ts rename to packages/client/lib/commands/WATCH.ts diff --git a/lib/commands/XACK.spec.ts b/packages/client/lib/commands/XACK.spec.ts similarity index 82% rename from lib/commands/XACK.spec.ts rename to packages/client/lib/commands/XACK.spec.ts index fb267c355eb..0586a5921fd 100644 --- a/lib/commands/XACK.spec.ts +++ b/packages/client/lib/commands/XACK.spec.ts @@ -1,5 +1,5 @@ import { strict as assert } from 'assert'; -import { TestRedisServers, itWithClient } from '../test-utils'; +import testUtils, { GLOBAL } from '../test-utils'; import { transformArguments } from './XACK'; describe('XACK', () => { @@ -19,10 +19,10 @@ describe('XACK', () => { }); }); - itWithClient(TestRedisServers.OPEN, 'client.xAck', async client => { + testUtils.testWithClient('client.xAck', async client => { assert.equal( await client.xAck('key', 'group', '1-0'), 0 ); - }); + }, GLOBAL.SERVERS.OPEN); }); diff --git a/lib/commands/XACK.ts b/packages/client/lib/commands/XACK.ts similarity index 100% rename from lib/commands/XACK.ts rename to packages/client/lib/commands/XACK.ts diff --git a/lib/commands/XADD.spec.ts b/packages/client/lib/commands/XADD.spec.ts similarity index 95% rename from lib/commands/XADD.spec.ts rename to packages/client/lib/commands/XADD.spec.ts index 02e6888051c..4b556ecc27c 100644 --- a/lib/commands/XADD.spec.ts +++ b/packages/client/lib/commands/XADD.spec.ts @@ -1,5 +1,5 @@ import { strict as assert } from 'assert'; -import { TestRedisServers, itWithClient } from '../test-utils'; +import testUtils, { GLOBAL } from '../test-utils'; import { transformArguments } from './XADD'; describe('XADD', () => { @@ -60,7 +60,7 @@ describe('XADD', () => { ['XADD', 'key', 'MAXLEN', '1000', '*','field', 'value'] ); }); - + it('with TRIM.strategyModifier', () => { assert.deepEqual( transformArguments('key', '*', { @@ -107,12 +107,12 @@ describe('XADD', () => { }); }); - itWithClient(TestRedisServers.OPEN, 'client.xAdd', async client => { + testUtils.testWithClient('client.xAdd', async client => { assert.equal( typeof await client.xAdd('key', '*', { field: 'value' }), 'string' ); - }); + }, GLOBAL.SERVERS.OPEN); }); diff --git a/lib/commands/XADD.ts b/packages/client/lib/commands/XADD.ts similarity index 100% rename from lib/commands/XADD.ts rename to packages/client/lib/commands/XADD.ts diff --git a/lib/commands/XAUTOCLAIM.spec.ts b/packages/client/lib/commands/XAUTOCLAIM.spec.ts similarity index 82% rename from lib/commands/XAUTOCLAIM.spec.ts rename to packages/client/lib/commands/XAUTOCLAIM.spec.ts index a0818d5c2c3..4447a06d773 100644 --- a/lib/commands/XAUTOCLAIM.spec.ts +++ b/packages/client/lib/commands/XAUTOCLAIM.spec.ts @@ -1,9 +1,9 @@ import { strict as assert } from 'assert'; -import { TestRedisServers, itWithClient, describeHandleMinimumRedisVersion } from '../test-utils'; +import testUtils, { GLOBAL } from '../test-utils'; import { transformArguments } from './XAUTOCLAIM'; describe('XAUTOCLAIM', () => { - describeHandleMinimumRedisVersion([6, 2]); + testUtils.isVersionGreaterThanHook([6, 2]); describe('transformArguments', () => { it('simple', () => { @@ -23,14 +23,14 @@ describe('XAUTOCLAIM', () => { }); }); - itWithClient(TestRedisServers.OPEN, 'client.xAutoClaim', async client => { + testUtils.testWithClient('client.xAutoClaim', async client => { await Promise.all([ client.xGroupCreate('key', 'group', '$', { MKSTREAM: true }), client.xGroupCreateConsumer('key', 'group', 'consumer'), ]); - + assert.deepEqual( await client.xAutoClaim('key', 'group', 'consumer', 1, '0-0'), { @@ -38,5 +38,5 @@ describe('XAUTOCLAIM', () => { messages: [] } ); - }); + }, GLOBAL.SERVERS.OPEN); }); diff --git a/lib/commands/XAUTOCLAIM.ts b/packages/client/lib/commands/XAUTOCLAIM.ts similarity index 100% rename from lib/commands/XAUTOCLAIM.ts rename to packages/client/lib/commands/XAUTOCLAIM.ts diff --git a/lib/commands/XAUTOCLAIM_JUSTID.spec.ts b/packages/client/lib/commands/XAUTOCLAIM_JUSTID.spec.ts similarity index 76% rename from lib/commands/XAUTOCLAIM_JUSTID.spec.ts rename to packages/client/lib/commands/XAUTOCLAIM_JUSTID.spec.ts index d076f28751a..9aa24cd04a4 100644 --- a/lib/commands/XAUTOCLAIM_JUSTID.spec.ts +++ b/packages/client/lib/commands/XAUTOCLAIM_JUSTID.spec.ts @@ -1,9 +1,9 @@ import { strict as assert } from 'assert'; -import { TestRedisServers, itWithClient, describeHandleMinimumRedisVersion } from '../test-utils'; +import testUtils, { GLOBAL } from '../test-utils'; import { transformArguments } from './XAUTOCLAIM_JUSTID'; describe('XAUTOCLAIM JUSTID', () => { - describeHandleMinimumRedisVersion([6, 2]); + testUtils.isVersionGreaterThanHook([6, 2]); it('transformArguments', () => { assert.deepEqual( @@ -12,7 +12,7 @@ describe('XAUTOCLAIM JUSTID', () => { ); }); - itWithClient(TestRedisServers.OPEN, 'client.xAutoClaimJustId', async client => { + testUtils.testWithClient('client.xAutoClaimJustId', async client => { await Promise.all([ client.xGroupCreate('key', 'group', '$', { MKSTREAM: true @@ -27,5 +27,5 @@ describe('XAUTOCLAIM JUSTID', () => { messages: [] } ); - }); + }, GLOBAL.SERVERS.OPEN); }); diff --git a/lib/commands/XAUTOCLAIM_JUSTID.ts b/packages/client/lib/commands/XAUTOCLAIM_JUSTID.ts similarity index 100% rename from lib/commands/XAUTOCLAIM_JUSTID.ts rename to packages/client/lib/commands/XAUTOCLAIM_JUSTID.ts diff --git a/lib/commands/XCLAIM.spec.ts b/packages/client/lib/commands/XCLAIM.spec.ts similarity index 94% rename from lib/commands/XCLAIM.spec.ts rename to packages/client/lib/commands/XCLAIM.spec.ts index ff4b445dcf3..141a62ab77a 100644 --- a/lib/commands/XCLAIM.spec.ts +++ b/packages/client/lib/commands/XCLAIM.spec.ts @@ -1,5 +1,5 @@ import { strict as assert } from 'assert'; -import { TestRedisServers, itWithClient } from '../test-utils'; +import testUtils, { GLOBAL } from '../test-utils'; import { transformArguments } from './XCLAIM'; describe('XCLAIM', () => { @@ -77,14 +77,14 @@ describe('XCLAIM', () => { }); }); - itWithClient(TestRedisServers.OPEN, 'client.xClaim', async client => { + testUtils.testWithClient('client.xClaim', async client => { await client.xGroupCreate('key', 'group', '$', { MKSTREAM: true }); - + assert.deepEqual( await client.xClaim('key', 'group', 'consumer', 1, '0-0'), [] ); - }); + }, GLOBAL.SERVERS.OPEN); }); diff --git a/lib/commands/XCLAIM.ts b/packages/client/lib/commands/XCLAIM.ts similarity index 85% rename from lib/commands/XCLAIM.ts rename to packages/client/lib/commands/XCLAIM.ts index c5890a75797..c87d1551e86 100644 --- a/lib/commands/XCLAIM.ts +++ b/packages/client/lib/commands/XCLAIM.ts @@ -1,4 +1,4 @@ -import { pushVerdictArguments, transformReplyStreamMessages } from './generic-transformers'; +import { pushVerdictArguments } from './generic-transformers'; export const FIRST_KEY_INDEX = 1; @@ -43,4 +43,4 @@ export function transformArguments( return args; } -export const transformReply = transformReplyStreamMessages; +export { transformReplyStreamMessages as transformReply } from './generic-transformers'; diff --git a/lib/commands/XCLAIM_JUSTID.spec.ts b/packages/client/lib/commands/XCLAIM_JUSTID.spec.ts similarity index 78% rename from lib/commands/XCLAIM_JUSTID.spec.ts rename to packages/client/lib/commands/XCLAIM_JUSTID.spec.ts index bb31f2c4532..619f876d53d 100644 --- a/lib/commands/XCLAIM_JUSTID.spec.ts +++ b/packages/client/lib/commands/XCLAIM_JUSTID.spec.ts @@ -1,5 +1,5 @@ import { strict as assert } from 'assert'; -import { TestRedisServers, itWithClient } from '../test-utils'; +import testUtils, { GLOBAL } from '../test-utils'; import { transformArguments } from './XCLAIM_JUSTID'; describe('XCLAIM JUSTID', () => { @@ -10,14 +10,14 @@ describe('XCLAIM JUSTID', () => { ); }); - itWithClient(TestRedisServers.OPEN, 'client.xClaimJustId', async client => { + testUtils.testWithClient('client.xClaimJustId', async client => { await client.xGroupCreate('key', 'group', '$', { MKSTREAM: true }); - + assert.deepEqual( await client.xClaimJustId('key', 'group', 'consumer', 1, '0-0'), [] ); - }); + }, GLOBAL.SERVERS.OPEN); }); diff --git a/lib/commands/XCLAIM_JUSTID.ts b/packages/client/lib/commands/XCLAIM_JUSTID.ts similarity index 100% rename from lib/commands/XCLAIM_JUSTID.ts rename to packages/client/lib/commands/XCLAIM_JUSTID.ts diff --git a/lib/commands/XDEL.spec.ts b/packages/client/lib/commands/XDEL.spec.ts similarity index 81% rename from lib/commands/XDEL.spec.ts rename to packages/client/lib/commands/XDEL.spec.ts index 1a3015538f4..00f9e2f9c67 100644 --- a/lib/commands/XDEL.spec.ts +++ b/packages/client/lib/commands/XDEL.spec.ts @@ -1,5 +1,5 @@ import { strict as assert } from 'assert'; -import { TestRedisServers, itWithClient } from '../test-utils'; +import testUtils, { GLOBAL } from '../test-utils'; import { transformArguments } from './XDEL'; describe('XDEL', () => { @@ -19,10 +19,10 @@ describe('XDEL', () => { }); }); - itWithClient(TestRedisServers.OPEN, 'client.xDel', async client => { + testUtils.testWithClient('client.xDel', async client => { assert.equal( await client.xDel('key', '0-0'), 0 ); - }); + }, GLOBAL.SERVERS.OPEN); }); diff --git a/lib/commands/XDEL.ts b/packages/client/lib/commands/XDEL.ts similarity index 100% rename from lib/commands/XDEL.ts rename to packages/client/lib/commands/XDEL.ts diff --git a/lib/commands/XGROUP_CREATE.spec.ts b/packages/client/lib/commands/XGROUP_CREATE.spec.ts similarity index 84% rename from lib/commands/XGROUP_CREATE.spec.ts rename to packages/client/lib/commands/XGROUP_CREATE.spec.ts index fdbb796f107..57516e44cc8 100644 --- a/lib/commands/XGROUP_CREATE.spec.ts +++ b/packages/client/lib/commands/XGROUP_CREATE.spec.ts @@ -1,5 +1,5 @@ import { strict as assert } from 'assert'; -import { TestRedisServers, itWithClient } from '../test-utils'; +import testUtils, { GLOBAL } from '../test-utils'; import { transformArguments } from './XGROUP_CREATE'; describe('XGROUP CREATE', () => { @@ -21,12 +21,12 @@ describe('XGROUP CREATE', () => { }); }); - itWithClient(TestRedisServers.OPEN, 'client.xGroupCreate', async client => { + testUtils.testWithClient('client.xGroupCreate', async client => { assert.equal( await client.xGroupCreate('key', 'group', '$', { MKSTREAM: true }), 'OK' ); - }); + }, GLOBAL.SERVERS.OPEN); }); diff --git a/lib/commands/XGROUP_CREATE.ts b/packages/client/lib/commands/XGROUP_CREATE.ts similarity index 100% rename from lib/commands/XGROUP_CREATE.ts rename to packages/client/lib/commands/XGROUP_CREATE.ts diff --git a/lib/commands/XGROUP_CREATECONSUMER.spec.ts b/packages/client/lib/commands/XGROUP_CREATECONSUMER.spec.ts similarity index 69% rename from lib/commands/XGROUP_CREATECONSUMER.spec.ts rename to packages/client/lib/commands/XGROUP_CREATECONSUMER.spec.ts index 5b06188e302..62443345188 100644 --- a/lib/commands/XGROUP_CREATECONSUMER.spec.ts +++ b/packages/client/lib/commands/XGROUP_CREATECONSUMER.spec.ts @@ -1,9 +1,9 @@ import { strict as assert } from 'assert'; -import { TestRedisServers, itWithClient, describeHandleMinimumRedisVersion } from '../test-utils'; +import testUtils, { GLOBAL } from '../test-utils'; import { transformArguments } from './XGROUP_CREATECONSUMER'; describe('XGROUP CREATECONSUMER', () => { - describeHandleMinimumRedisVersion([6, 2]); + testUtils.isVersionGreaterThanHook([6, 2]); it('transformArguments', () => { assert.deepEqual( @@ -12,14 +12,14 @@ describe('XGROUP CREATECONSUMER', () => { ); }); - itWithClient(TestRedisServers.OPEN, 'client.xGroupCreateConsumer', async client => { + testUtils.testWithClient('client.xGroupCreateConsumer', async client => { await client.xGroupCreate('key', 'group', '$', { MKSTREAM: true }); - + assert.equal( await client.xGroupCreateConsumer('key', 'group', 'consumer'), true ); - }); + }, GLOBAL.SERVERS.OPEN); }); diff --git a/lib/commands/XGROUP_CREATECONSUMER.ts b/packages/client/lib/commands/XGROUP_CREATECONSUMER.ts similarity index 62% rename from lib/commands/XGROUP_CREATECONSUMER.ts rename to packages/client/lib/commands/XGROUP_CREATECONSUMER.ts index 395688706e2..f1a57e6fc42 100644 --- a/lib/commands/XGROUP_CREATECONSUMER.ts +++ b/packages/client/lib/commands/XGROUP_CREATECONSUMER.ts @@ -1,9 +1,7 @@ -import { transformReplyBoolean } from './generic-transformers'; - export const FIRST_KEY_INDEX = 2; export function transformArguments(key: string, group: string, consumer: string): Array { return ['XGROUP', 'CREATECONSUMER', key, group, consumer]; } -export const transformReply = transformReplyBoolean; +export { transformReplyBoolean as transformReply } from './generic-transformers'; diff --git a/lib/commands/XGROUP_DELCONSUMER.spec.ts b/packages/client/lib/commands/XGROUP_DELCONSUMER.spec.ts similarity index 77% rename from lib/commands/XGROUP_DELCONSUMER.spec.ts rename to packages/client/lib/commands/XGROUP_DELCONSUMER.spec.ts index c3cf3c2378a..d071aedf64f 100644 --- a/lib/commands/XGROUP_DELCONSUMER.spec.ts +++ b/packages/client/lib/commands/XGROUP_DELCONSUMER.spec.ts @@ -1,5 +1,5 @@ import { strict as assert } from 'assert'; -import { TestRedisServers, itWithClient } from '../test-utils'; +import testUtils, { GLOBAL } from '../test-utils'; import { transformArguments } from './XGROUP_DELCONSUMER'; describe('XGROUP DELCONSUMER', () => { @@ -10,14 +10,14 @@ describe('XGROUP DELCONSUMER', () => { ); }); - itWithClient(TestRedisServers.OPEN, 'client.xGroupDelConsumer', async client => { + testUtils.testWithClient('client.xGroupDelConsumer', async client => { await client.xGroupCreate('key', 'group', '$', { MKSTREAM: true }); - + assert.equal( await client.xGroupDelConsumer('key', 'group', 'consumer'), 0 ); - }); + }, GLOBAL.SERVERS.OPEN); }); diff --git a/lib/commands/XGROUP_DELCONSUMER.ts b/packages/client/lib/commands/XGROUP_DELCONSUMER.ts similarity index 100% rename from lib/commands/XGROUP_DELCONSUMER.ts rename to packages/client/lib/commands/XGROUP_DELCONSUMER.ts diff --git a/lib/commands/XGROUP_DESTROY.spec.ts b/packages/client/lib/commands/XGROUP_DESTROY.spec.ts similarity index 76% rename from lib/commands/XGROUP_DESTROY.spec.ts rename to packages/client/lib/commands/XGROUP_DESTROY.spec.ts index e991bc0d667..ea8e7b7be98 100644 --- a/lib/commands/XGROUP_DESTROY.spec.ts +++ b/packages/client/lib/commands/XGROUP_DESTROY.spec.ts @@ -1,5 +1,5 @@ import { strict as assert } from 'assert'; -import { TestRedisServers, itWithClient } from '../test-utils'; +import testUtils, { GLOBAL } from '../test-utils'; import { transformArguments } from './XGROUP_DESTROY'; describe('XGROUP DESTROY', () => { @@ -10,14 +10,14 @@ describe('XGROUP DESTROY', () => { ); }); - itWithClient(TestRedisServers.OPEN, 'client.xGroupDestroy', async client => { + testUtils.testWithClient('client.xGroupDestroy', async client => { await client.xGroupCreate('key', 'group', '$', { MKSTREAM: true }); - + assert.equal( await client.xGroupDestroy('key', 'group'), true ); - }); + }, GLOBAL.SERVERS.OPEN); }); diff --git a/lib/commands/XGROUP_DESTROY.ts b/packages/client/lib/commands/XGROUP_DESTROY.ts similarity index 58% rename from lib/commands/XGROUP_DESTROY.ts rename to packages/client/lib/commands/XGROUP_DESTROY.ts index 1fd25550c33..a4d67e5f4e5 100644 --- a/lib/commands/XGROUP_DESTROY.ts +++ b/packages/client/lib/commands/XGROUP_DESTROY.ts @@ -1,9 +1,7 @@ -import { transformReplyBoolean } from './generic-transformers'; - export const FIRST_KEY_INDEX = 2; export function transformArguments(key: string, group: string): Array { return ['XGROUP', 'DESTROY', key, group]; } -export const transformReply = transformReplyBoolean; +export { transformReplyBoolean as transformReply } from './generic-transformers'; diff --git a/lib/commands/XGROUP_SETID.spec.ts b/packages/client/lib/commands/XGROUP_SETID.spec.ts similarity index 77% rename from lib/commands/XGROUP_SETID.spec.ts rename to packages/client/lib/commands/XGROUP_SETID.spec.ts index 0fa10cdb0b7..8df51f5401d 100644 --- a/lib/commands/XGROUP_SETID.spec.ts +++ b/packages/client/lib/commands/XGROUP_SETID.spec.ts @@ -1,5 +1,5 @@ import { strict as assert } from 'assert'; -import { TestRedisServers, itWithClient } from '../test-utils'; +import testUtils, { GLOBAL } from '../test-utils'; import { transformArguments } from './XGROUP_SETID'; describe('XGROUP SETID', () => { @@ -10,7 +10,7 @@ describe('XGROUP SETID', () => { ); }); - itWithClient(TestRedisServers.OPEN, 'client.xGroupSetId', async client => { + testUtils.testWithClient('client.xGroupSetId', async client => { await client.xGroupCreate('key', 'group', '$', { MKSTREAM: true }); @@ -19,5 +19,5 @@ describe('XGROUP SETID', () => { await client.xGroupSetId('key', 'group', '0'), 'OK' ); - }); + }, GLOBAL.SERVERS.OPEN); }); diff --git a/lib/commands/XGROUP_SETID.ts b/packages/client/lib/commands/XGROUP_SETID.ts similarity index 100% rename from lib/commands/XGROUP_SETID.ts rename to packages/client/lib/commands/XGROUP_SETID.ts diff --git a/lib/commands/XINFO_CONSUMERS.spec.ts b/packages/client/lib/commands/XINFO_CONSUMERS.spec.ts similarity index 86% rename from lib/commands/XINFO_CONSUMERS.spec.ts rename to packages/client/lib/commands/XINFO_CONSUMERS.spec.ts index 08ef17e51aa..87c82b34f29 100644 --- a/lib/commands/XINFO_CONSUMERS.spec.ts +++ b/packages/client/lib/commands/XINFO_CONSUMERS.spec.ts @@ -1,5 +1,5 @@ import { strict as assert } from 'assert'; -import { TestRedisServers, itWithClient } from '../test-utils'; +import testUtils, { GLOBAL } from '../test-utils'; import { transformArguments, transformReply } from './XINFO_CONSUMERS'; describe('XINFO CONSUMERS', () => { @@ -26,9 +26,9 @@ describe('XINFO CONSUMERS', () => { idle: 83841983 }] ); - }) + }); - itWithClient(TestRedisServers.OPEN, 'client.xInfoConsumers', async client => { + testUtils.testWithClient('client.xInfoConsumers', async client => { await client.xGroupCreate('key', 'group', '$', { MKSTREAM: true }); @@ -37,5 +37,5 @@ describe('XINFO CONSUMERS', () => { await client.xInfoConsumers('key', 'group'), [] ); - }); + }, GLOBAL.SERVERS.OPEN); }); diff --git a/lib/commands/XINFO_CONSUMERS.ts b/packages/client/lib/commands/XINFO_CONSUMERS.ts similarity index 100% rename from lib/commands/XINFO_CONSUMERS.ts rename to packages/client/lib/commands/XINFO_CONSUMERS.ts diff --git a/lib/commands/XINFO_GROUPS.spec.ts b/packages/client/lib/commands/XINFO_GROUPS.spec.ts similarity index 89% rename from lib/commands/XINFO_GROUPS.spec.ts rename to packages/client/lib/commands/XINFO_GROUPS.spec.ts index 8fbd86ee3ee..dea8ac58d9c 100644 --- a/lib/commands/XINFO_GROUPS.spec.ts +++ b/packages/client/lib/commands/XINFO_GROUPS.spec.ts @@ -1,5 +1,5 @@ import { strict as assert } from 'assert'; -import { TestRedisServers, itWithClient } from '../test-utils'; +import testUtils, { GLOBAL } from '../test-utils'; import { transformArguments, transformReply } from './XINFO_GROUPS'; describe('XINFO GROUPS', () => { @@ -28,9 +28,9 @@ describe('XINFO GROUPS', () => { lastDeliveredId: '1588152498034-0' }] ); - }) + }); - itWithClient(TestRedisServers.OPEN, 'client.xInfoGroups', async client => { + testUtils.testWithClient('client.xInfoGroups', async client => { await client.xGroupCreate('key', 'group', '$', { MKSTREAM: true }); @@ -44,5 +44,5 @@ describe('XINFO GROUPS', () => { lastDeliveredId: '0-0' }] ); - }); + }, GLOBAL.SERVERS.OPEN); }); diff --git a/lib/commands/XINFO_GROUPS.ts b/packages/client/lib/commands/XINFO_GROUPS.ts similarity index 100% rename from lib/commands/XINFO_GROUPS.ts rename to packages/client/lib/commands/XINFO_GROUPS.ts diff --git a/lib/commands/XINFO_STREAM.spec.ts b/packages/client/lib/commands/XINFO_STREAM.spec.ts similarity index 93% rename from lib/commands/XINFO_STREAM.spec.ts rename to packages/client/lib/commands/XINFO_STREAM.spec.ts index ecab605e4e3..ca8d44f2875 100644 --- a/lib/commands/XINFO_STREAM.spec.ts +++ b/packages/client/lib/commands/XINFO_STREAM.spec.ts @@ -1,5 +1,5 @@ import { strict as assert } from 'assert'; -import { TestRedisServers, itWithClient } from '../test-utils'; +import testUtils, { GLOBAL } from '../test-utils'; import { transformArguments, transformReply } from './XINFO_STREAM'; describe('XINFO STREAM', () => { @@ -51,7 +51,7 @@ describe('XINFO STREAM', () => { ); }); - itWithClient(TestRedisServers.OPEN, 'client.xInfoStream', async client => { + testUtils.testWithClient('client.xInfoStream', async client => { await client.xGroupCreate('key', 'group', '$', { MKSTREAM: true }); @@ -68,5 +68,5 @@ describe('XINFO STREAM', () => { lastEntry: null } ); - }); + }, GLOBAL.SERVERS.OPEN); }); diff --git a/lib/commands/XINFO_STREAM.ts b/packages/client/lib/commands/XINFO_STREAM.ts similarity index 100% rename from lib/commands/XINFO_STREAM.ts rename to packages/client/lib/commands/XINFO_STREAM.ts diff --git a/lib/commands/XLEN.spec.ts b/packages/client/lib/commands/XLEN.spec.ts similarity index 70% rename from lib/commands/XLEN.spec.ts rename to packages/client/lib/commands/XLEN.spec.ts index c4f62dbc4f2..178024ba89e 100644 --- a/lib/commands/XLEN.spec.ts +++ b/packages/client/lib/commands/XLEN.spec.ts @@ -1,5 +1,5 @@ import { strict as assert } from 'assert'; -import { TestRedisServers, itWithClient } from '../test-utils'; +import testUtils, { GLOBAL } from '../test-utils'; import { transformArguments } from './XLEN'; describe('XLEN', () => { @@ -10,10 +10,10 @@ describe('XLEN', () => { ); }); - itWithClient(TestRedisServers.OPEN, 'client.xLen', async client => { + testUtils.testWithClient('client.xLen', async client => { assert.equal( await client.xLen('key'), 0 ); - }); + }, GLOBAL.SERVERS.OPEN); }); diff --git a/lib/commands/XLEN.ts b/packages/client/lib/commands/XLEN.ts similarity index 100% rename from lib/commands/XLEN.ts rename to packages/client/lib/commands/XLEN.ts diff --git a/lib/commands/XPENDING.spec.ts b/packages/client/lib/commands/XPENDING.spec.ts similarity index 82% rename from lib/commands/XPENDING.spec.ts rename to packages/client/lib/commands/XPENDING.spec.ts index 31ffeeb4230..7eb12b40efe 100644 --- a/lib/commands/XPENDING.spec.ts +++ b/packages/client/lib/commands/XPENDING.spec.ts @@ -1,5 +1,5 @@ import { strict as assert } from 'assert'; -import { TestRedisServers, itWithClient } from '../test-utils'; +import testUtils, { GLOBAL } from '../test-utils'; import { transformArguments } from './XPENDING'; describe('XPENDING', () => { @@ -12,7 +12,7 @@ describe('XPENDING', () => { }); }); - itWithClient(TestRedisServers.OPEN, 'client.xPending', async client => { + testUtils.testWithClient('client.xPending', async client => { await client.xGroupCreate('key', 'group', '$', { MKSTREAM: true }); @@ -26,5 +26,5 @@ describe('XPENDING', () => { consumers: null } ); - }); + }, GLOBAL.SERVERS.OPEN); }); diff --git a/lib/commands/XPENDING.ts b/packages/client/lib/commands/XPENDING.ts similarity index 100% rename from lib/commands/XPENDING.ts rename to packages/client/lib/commands/XPENDING.ts diff --git a/lib/commands/XPENDING_RANGE.spec.ts b/packages/client/lib/commands/XPENDING_RANGE.spec.ts similarity index 90% rename from lib/commands/XPENDING_RANGE.spec.ts rename to packages/client/lib/commands/XPENDING_RANGE.spec.ts index 76a582d3db5..0b57c704bb0 100644 --- a/lib/commands/XPENDING_RANGE.spec.ts +++ b/packages/client/lib/commands/XPENDING_RANGE.spec.ts @@ -1,5 +1,5 @@ import { strict as assert } from 'assert'; -import { TestRedisServers, itWithClient } from '../test-utils'; +import testUtils, { GLOBAL } from '../test-utils'; import { transformArguments } from './XPENDING_RANGE'; describe('XPENDING RANGE', () => { @@ -10,7 +10,7 @@ describe('XPENDING RANGE', () => { ['XPENDING', 'key', 'group', '-', '+', '1'] ); }); - + it('with IDLE', () => { assert.deepEqual( transformArguments('key', 'group', '-', '+', 1, { @@ -40,14 +40,14 @@ describe('XPENDING RANGE', () => { }); }); - itWithClient(TestRedisServers.OPEN, 'client.xPendingRange', async client => { + testUtils.testWithClient('client.xPendingRange', async client => { await client.xGroupCreate('key', 'group', '$', { MKSTREAM: true }); - + assert.deepEqual( await client.xPendingRange('key', 'group', '-', '+', 1), [] ); - }); + }, GLOBAL.SERVERS.OPEN); }); diff --git a/lib/commands/XPENDING_RANGE.ts b/packages/client/lib/commands/XPENDING_RANGE.ts similarity index 81% rename from lib/commands/XPENDING_RANGE.ts rename to packages/client/lib/commands/XPENDING_RANGE.ts index c9e8d898e86..d0b45f0fabb 100644 --- a/lib/commands/XPENDING_RANGE.ts +++ b/packages/client/lib/commands/XPENDING_RANGE.ts @@ -1,5 +1,3 @@ -import { transformReplyStreamMessages } from './generic-transformers'; - export const FIRST_KEY_INDEX = 1; export const IS_READ_ONLY = true; @@ -24,7 +22,7 @@ export function transformArguments( } args.push(start, end, count.toString()); - + if (options?.consumer) { args.push(options.consumer); } @@ -32,4 +30,4 @@ export function transformArguments( return args; } -export const transformReply = transformReplyStreamMessages; +export { transformReplyStreamMessages as transformReply } from './generic-transformers'; diff --git a/lib/commands/XRANGE.spec.ts b/packages/client/lib/commands/XRANGE.spec.ts similarity index 82% rename from lib/commands/XRANGE.spec.ts rename to packages/client/lib/commands/XRANGE.spec.ts index 55efa9d7729..01c713e9595 100644 --- a/lib/commands/XRANGE.spec.ts +++ b/packages/client/lib/commands/XRANGE.spec.ts @@ -1,5 +1,5 @@ import { strict as assert } from 'assert'; -import { TestRedisServers, itWithClient } from '../test-utils'; +import testUtils, { GLOBAL } from '../test-utils'; import { transformArguments } from './XRANGE'; describe('XRANGE', () => { @@ -10,7 +10,7 @@ describe('XRANGE', () => { ['XRANGE', 'key', '-', '+'] ); }); - + it('with COUNT', () => { assert.deepEqual( transformArguments('key', '-', '+', { @@ -21,10 +21,10 @@ describe('XRANGE', () => { }); }); - itWithClient(TestRedisServers.OPEN, 'client.xRange', async client => { + testUtils.testWithClient('client.xRange', async client => { assert.deepEqual( await client.xRange('key', '+', '-'), [] ); - }); + }, GLOBAL.SERVERS.OPEN); }); diff --git a/lib/commands/XRANGE.ts b/packages/client/lib/commands/XRANGE.ts similarity index 74% rename from lib/commands/XRANGE.ts rename to packages/client/lib/commands/XRANGE.ts index 2902d0743db..b357266c950 100644 --- a/lib/commands/XRANGE.ts +++ b/packages/client/lib/commands/XRANGE.ts @@ -1,5 +1,3 @@ -import { transformReplyStreamMessages } from './generic-transformers'; - export const FIRST_KEY_INDEX = 1; export const IS_READ_ONLY = true; @@ -18,4 +16,4 @@ export function transformArguments(key: string, start: string, end: string, opti return args; } -export const transformReply = transformReplyStreamMessages; +export { transformReplyStreamMessages as transformReply } from './generic-transformers'; diff --git a/lib/commands/XREAD.spec.ts b/packages/client/lib/commands/XREAD.spec.ts similarity index 90% rename from lib/commands/XREAD.spec.ts rename to packages/client/lib/commands/XREAD.spec.ts index 501571bfbeb..b607f53532e 100644 --- a/lib/commands/XREAD.spec.ts +++ b/packages/client/lib/commands/XREAD.spec.ts @@ -1,5 +1,5 @@ import { strict as assert } from 'assert'; -import { TestRedisServers, itWithClient, itWithCluster, TestRedisClusters } from '../test-utils'; +import testUtils, { GLOBAL } from '../test-utils'; import { FIRST_KEY_INDEX, transformArguments } from './XREAD'; describe('XREAD', () => { @@ -81,7 +81,7 @@ describe('XREAD', () => { }); }); - itWithClient(TestRedisServers.OPEN, 'client.xRead', async client => { + testUtils.testWithClient('client.xRead', async client => { assert.equal( await client.xRead({ key: 'key', @@ -89,9 +89,9 @@ describe('XREAD', () => { }), null ); - }); + }, GLOBAL.SERVERS.OPEN); - itWithCluster(TestRedisClusters.OPEN, 'cluster.xRead', async cluster => { + testUtils.testWithCluster('cluster.xRead', async cluster => { assert.equal( await cluster.xRead({ key: 'key', @@ -99,5 +99,5 @@ describe('XREAD', () => { }), null ); - }); + }, GLOBAL.CLUSTERS.OPEN); }); diff --git a/lib/commands/XREAD.ts b/packages/client/lib/commands/XREAD.ts similarity index 88% rename from lib/commands/XREAD.ts rename to packages/client/lib/commands/XREAD.ts index 1f4932837a2..00d8aa959ec 100644 --- a/lib/commands/XREAD.ts +++ b/packages/client/lib/commands/XREAD.ts @@ -1,5 +1,3 @@ -import { transformReplyStreamsMessages } from './generic-transformers'; - export const FIRST_KEY_INDEX = (streams: Array | XReadStream): string => { return Array.isArray(streams) ? streams[0].key : streams.key; }; @@ -40,4 +38,4 @@ export function transformArguments(streams: Array | XReadStream, op return args; } -export const transformReply = transformReplyStreamsMessages; +export { transformReplyStreamsMessages as transformReply } from './generic-transformers'; diff --git a/lib/commands/XREADGROUP.spec.ts b/packages/client/lib/commands/XREADGROUP.spec.ts similarity index 87% rename from lib/commands/XREADGROUP.spec.ts rename to packages/client/lib/commands/XREADGROUP.spec.ts index 8cb3147bfe7..fa196d504ad 100644 --- a/lib/commands/XREADGROUP.spec.ts +++ b/packages/client/lib/commands/XREADGROUP.spec.ts @@ -1,5 +1,5 @@ import { strict as assert } from 'assert'; -import { TestRedisServers, itWithClient, itWithCluster, TestRedisClusters } from '../test-utils'; +import testUtils, { GLOBAL } from '../test-utils'; import { FIRST_KEY_INDEX, transformArguments } from './XREADGROUP'; describe('XREADGROUP', () => { @@ -94,36 +94,22 @@ describe('XREADGROUP', () => { }); }); - itWithClient(TestRedisServers.OPEN, 'null', async client => { - const [, readGroupReply] = await Promise.all([ - client.xGroupCreate('key', 'group', '$', { - MKSTREAM: true - }), - client.xReadGroup('group', 'consumer', { - key: 'key', - id: '>' - }) - ]); - - assert.equal(readGroupReply, null); - }); - - describe('cluster.xReadGroup', () => { - itWithCluster(TestRedisClusters.OPEN, 'null', async cluster => { + describe('client.xReadGroup', () => { + testUtils.testWithClient('null', async client => { const [, readGroupReply] = await Promise.all([ - cluster.xGroupCreate('key', 'group', '$', { + client.xGroupCreate('key', 'group', '$', { MKSTREAM: true }), - cluster.xReadGroup('group', 'consumer', { + client.xReadGroup('group', 'consumer', { key: 'key', id: '>' }) ]); assert.equal(readGroupReply, null); - }); + }, GLOBAL.SERVERS.OPEN); - itWithCluster(TestRedisClusters.OPEN, 'with a message', async client => { + testUtils.testWithClient('with a message', async client => { const [, id, readGroupReply] = await Promise.all([ client.xGroupCreate('key', 'group', '$', { MKSTREAM: true @@ -148,6 +134,20 @@ describe('XREADGROUP', () => { }) }] }]); - }); + }, GLOBAL.SERVERS.OPEN); }); + + testUtils.testWithCluster('cluster.xReadGroup', async cluster => { + const [, readGroupReply] = await Promise.all([ + cluster.xGroupCreate('key', 'group', '$', { + MKSTREAM: true + }), + cluster.xReadGroup('group', 'consumer', { + key: 'key', + id: '>' + }) + ]); + + assert.equal(readGroupReply, null); + }, GLOBAL.CLUSTERS.OPEN); }); diff --git a/lib/commands/XREADGROUP.ts b/packages/client/lib/commands/XREADGROUP.ts similarity index 90% rename from lib/commands/XREADGROUP.ts rename to packages/client/lib/commands/XREADGROUP.ts index b01385e7c2f..6d329d377ff 100644 --- a/lib/commands/XREADGROUP.ts +++ b/packages/client/lib/commands/XREADGROUP.ts @@ -1,5 +1,3 @@ -import { transformReplyStreamsMessages } from './generic-transformers'; - export interface XReadGroupStream { key: string; id: string; @@ -54,4 +52,4 @@ export function transformArguments( return args; } -export const transformReply = transformReplyStreamsMessages; +export { transformReplyStreamsMessages as transformReply } from './generic-transformers'; diff --git a/lib/commands/XREVRANGE.spec.ts b/packages/client/lib/commands/XREVRANGE.spec.ts similarity index 82% rename from lib/commands/XREVRANGE.spec.ts rename to packages/client/lib/commands/XREVRANGE.spec.ts index ba009cc2bbe..fd6e1a3adfe 100644 --- a/lib/commands/XREVRANGE.spec.ts +++ b/packages/client/lib/commands/XREVRANGE.spec.ts @@ -1,5 +1,5 @@ import { strict as assert } from 'assert'; -import { TestRedisServers, itWithClient } from '../test-utils'; +import testUtils, { GLOBAL } from '../test-utils'; import { transformArguments } from './XREVRANGE'; describe('XREVRANGE', () => { @@ -10,7 +10,7 @@ describe('XREVRANGE', () => { ['XREVRANGE', 'key', '-', '+'] ); }); - + it('with COUNT', () => { assert.deepEqual( transformArguments('key', '-', '+', { @@ -21,10 +21,10 @@ describe('XREVRANGE', () => { }); }); - itWithClient(TestRedisServers.OPEN, 'client.xRevRange', async client => { + testUtils.testWithClient('client.xRevRange', async client => { assert.deepEqual( await client.xRevRange('key', '+', '-'), [] ); - }); + }, GLOBAL.SERVERS.OPEN); }); diff --git a/lib/commands/XREVRANGE.ts b/packages/client/lib/commands/XREVRANGE.ts similarity index 71% rename from lib/commands/XREVRANGE.ts rename to packages/client/lib/commands/XREVRANGE.ts index a1fbbbc128a..db48856cdcc 100644 --- a/lib/commands/XREVRANGE.ts +++ b/packages/client/lib/commands/XREVRANGE.ts @@ -1,5 +1,3 @@ -import { transformReplyStreamMessages } from './generic-transformers'; - interface XRangeRevOptions { COUNT?: number; } @@ -14,4 +12,4 @@ export function transformArguments(key: string, start: string, end: string, opti return args; } -export const transformReply = transformReplyStreamMessages; +export { transformReplyStreamMessages as transformReply } from './generic-transformers'; diff --git a/lib/commands/XTRIM.spec.ts b/packages/client/lib/commands/XTRIM.spec.ts similarity index 90% rename from lib/commands/XTRIM.spec.ts rename to packages/client/lib/commands/XTRIM.spec.ts index 0b48fd6a2d6..a8f8078eb28 100644 --- a/lib/commands/XTRIM.spec.ts +++ b/packages/client/lib/commands/XTRIM.spec.ts @@ -1,5 +1,5 @@ import { strict as assert } from 'assert'; -import { TestRedisServers, itWithClient } from '../test-utils'; +import testUtils, { GLOBAL } from '../test-utils'; import { transformArguments } from './XTRIM'; describe('XTRIM', () => { @@ -40,10 +40,10 @@ describe('XTRIM', () => { }); }); - itWithClient(TestRedisServers.OPEN, 'client.xTrim', async client => { + testUtils.testWithClient('client.xTrim', async client => { assert.equal( await client.xTrim('key', 'MAXLEN', 1), 0 ); - }); + }, GLOBAL.SERVERS.OPEN); }); diff --git a/lib/commands/XTRIM.ts b/packages/client/lib/commands/XTRIM.ts similarity index 100% rename from lib/commands/XTRIM.ts rename to packages/client/lib/commands/XTRIM.ts diff --git a/lib/commands/ZADD.spec.ts b/packages/client/lib/commands/ZADD.spec.ts similarity index 95% rename from lib/commands/ZADD.spec.ts rename to packages/client/lib/commands/ZADD.spec.ts index 7c017e45410..4f497bdca90 100644 --- a/lib/commands/ZADD.spec.ts +++ b/packages/client/lib/commands/ZADD.spec.ts @@ -1,5 +1,5 @@ import { strict as assert } from 'assert'; -import { TestRedisServers, itWithClient } from '../test-utils'; +import testUtils, { GLOBAL } from '../test-utils'; import { transformArguments } from './ZADD'; describe('ZADD', () => { @@ -115,7 +115,7 @@ describe('ZADD', () => { }); }); - itWithClient(TestRedisServers.OPEN, 'client.zAdd', async client => { + testUtils.testWithClient('client.zAdd', async client => { assert.equal( await client.zAdd('key', { value: '1', @@ -123,5 +123,5 @@ describe('ZADD', () => { }), 1 ); - }); + }, GLOBAL.SERVERS.OPEN); }); diff --git a/lib/commands/ZADD.ts b/packages/client/lib/commands/ZADD.ts similarity index 83% rename from lib/commands/ZADD.ts rename to packages/client/lib/commands/ZADD.ts index 0e43ecaf508..69f01c8d534 100644 --- a/lib/commands/ZADD.ts +++ b/packages/client/lib/commands/ZADD.ts @@ -1,4 +1,4 @@ -import { transformArgumentNumberInfinity, transformReplyNumberInfinity, ZMember } from './generic-transformers'; +import { transformArgumentNumberInfinity, ZMember } from './generic-transformers'; export const FIRST_KEY_INDEX = 1; @@ -56,11 +56,11 @@ export function transformArguments(key: string, members: ZMember | Array { @@ -10,10 +10,10 @@ describe('ZCARD', () => { ); }); - itWithClient(TestRedisServers.OPEN, 'client.zCard', async client => { + testUtils.testWithClient('client.zCard', async client => { assert.equal( await client.zCard('key'), 0 ); - }); + }, GLOBAL.SERVERS.OPEN); }); diff --git a/lib/commands/ZCARD.ts b/packages/client/lib/commands/ZCARD.ts similarity index 100% rename from lib/commands/ZCARD.ts rename to packages/client/lib/commands/ZCARD.ts diff --git a/lib/commands/ZCOUNT.spec.ts b/packages/client/lib/commands/ZCOUNT.spec.ts similarity index 72% rename from lib/commands/ZCOUNT.spec.ts rename to packages/client/lib/commands/ZCOUNT.spec.ts index e461241ce1c..e185ed3cd45 100644 --- a/lib/commands/ZCOUNT.spec.ts +++ b/packages/client/lib/commands/ZCOUNT.spec.ts @@ -1,5 +1,5 @@ import { strict as assert } from 'assert'; -import { TestRedisServers, itWithClient } from '../test-utils'; +import testUtils, { GLOBAL } from '../test-utils'; import { transformArguments } from './ZCOUNT'; describe('ZCOUNT', () => { @@ -10,10 +10,10 @@ describe('ZCOUNT', () => { ); }); - itWithClient(TestRedisServers.OPEN, 'client.zCount', async client => { + testUtils.testWithClient('client.zCount', async client => { assert.equal( await client.zCount('key', 0, 1), 0 ); - }); + }, GLOBAL.SERVERS.OPEN); }); diff --git a/packages/client/lib/commands/ZCOUNT.ts b/packages/client/lib/commands/ZCOUNT.ts new file mode 100644 index 00000000000..83a0710fa11 --- /dev/null +++ b/packages/client/lib/commands/ZCOUNT.ts @@ -0,0 +1,16 @@ +import { transformArgumentStringNumberInfinity } from './generic-transformers'; + +export const FIRST_KEY_INDEX = 1; + +export const IS_READ_ONLY = true; + +export function transformArguments(key: string, min: string | number, max: string | number): Array { + return [ + 'ZCOUNT', + key, + transformArgumentStringNumberInfinity(min), + transformArgumentStringNumberInfinity(max) + ]; +} + +export declare function transformReply(): number; diff --git a/lib/commands/ZDIFF.spec.ts b/packages/client/lib/commands/ZDIFF.spec.ts similarity index 72% rename from lib/commands/ZDIFF.spec.ts rename to packages/client/lib/commands/ZDIFF.spec.ts index f45b2af7edc..8bb1a101f53 100644 --- a/lib/commands/ZDIFF.spec.ts +++ b/packages/client/lib/commands/ZDIFF.spec.ts @@ -1,9 +1,9 @@ import { strict as assert } from 'assert'; -import { TestRedisServers, itWithClient, describeHandleMinimumRedisVersion } from '../test-utils'; +import testUtils, { GLOBAL } from '../test-utils'; import { transformArguments } from './ZDIFF'; describe('ZDIFF', () => { - describeHandleMinimumRedisVersion([6, 2]); + testUtils.isVersionGreaterThanHook([6, 2]); describe('transformArguments', () => { it('string', () => { @@ -21,10 +21,10 @@ describe('ZDIFF', () => { }); }); - itWithClient(TestRedisServers.OPEN, 'client.zDiff', async client => { + testUtils.testWithClient('client.zDiff', async client => { assert.deepEqual( await client.zDiff('key'), [] ); - }); + }, GLOBAL.SERVERS.OPEN); }); diff --git a/lib/commands/ZDIFF.ts b/packages/client/lib/commands/ZDIFF.ts similarity index 100% rename from lib/commands/ZDIFF.ts rename to packages/client/lib/commands/ZDIFF.ts diff --git a/lib/commands/ZDIFFSTORE.spec.ts b/packages/client/lib/commands/ZDIFFSTORE.spec.ts similarity index 75% rename from lib/commands/ZDIFFSTORE.spec.ts rename to packages/client/lib/commands/ZDIFFSTORE.spec.ts index 5fbeebaf502..c63902b2666 100644 --- a/lib/commands/ZDIFFSTORE.spec.ts +++ b/packages/client/lib/commands/ZDIFFSTORE.spec.ts @@ -1,9 +1,9 @@ import { strict as assert } from 'assert'; -import { TestRedisServers, itWithClient, describeHandleMinimumRedisVersion } from '../test-utils'; +import testUtils, { GLOBAL } from '../test-utils'; import { transformArguments } from './ZDIFFSTORE'; describe('ZDIFFSTORE', () => { - describeHandleMinimumRedisVersion([6, 2]); + testUtils.isVersionGreaterThanHook([6, 2]); describe('transformArguments', () => { it('string', () => { @@ -21,10 +21,10 @@ describe('ZDIFFSTORE', () => { }); }); - itWithClient(TestRedisServers.OPEN, 'client.zDiffStore', async client => { + testUtils.testWithClient('client.zDiffStore', async client => { assert.equal( await client.zDiffStore('destination', 'key'), 0 ); - }); + }, GLOBAL.SERVERS.OPEN); }); diff --git a/lib/commands/ZDIFFSTORE.ts b/packages/client/lib/commands/ZDIFFSTORE.ts similarity index 100% rename from lib/commands/ZDIFFSTORE.ts rename to packages/client/lib/commands/ZDIFFSTORE.ts diff --git a/lib/commands/ZDIFF_WITHSCORES.spec.ts b/packages/client/lib/commands/ZDIFF_WITHSCORES.spec.ts similarity index 73% rename from lib/commands/ZDIFF_WITHSCORES.spec.ts rename to packages/client/lib/commands/ZDIFF_WITHSCORES.spec.ts index 99c23108292..3b9cb725aaa 100644 --- a/lib/commands/ZDIFF_WITHSCORES.spec.ts +++ b/packages/client/lib/commands/ZDIFF_WITHSCORES.spec.ts @@ -1,9 +1,9 @@ import { strict as assert } from 'assert'; -import { TestRedisServers, itWithClient, describeHandleMinimumRedisVersion } from '../test-utils'; +import testUtils, { GLOBAL } from '../test-utils'; import { transformArguments } from './ZDIFF_WITHSCORES'; describe('ZDIFF WITHSCORES', () => { - describeHandleMinimumRedisVersion([6, 2]); + testUtils.isVersionGreaterThanHook([6, 2]); describe('transformArguments', () => { it('string', () => { @@ -21,10 +21,10 @@ describe('ZDIFF WITHSCORES', () => { }); }); - itWithClient(TestRedisServers.OPEN, 'client.zDiffWithScores', async client => { + testUtils.testWithClient('client.zDiffWithScores', async client => { assert.deepEqual( await client.zDiffWithScores('key'), [] ); - }); + }, GLOBAL.SERVERS.OPEN); }); diff --git a/lib/commands/ZDIFF_WITHSCORES.ts b/packages/client/lib/commands/ZDIFF_WITHSCORES.ts similarity index 72% rename from lib/commands/ZDIFF_WITHSCORES.ts rename to packages/client/lib/commands/ZDIFF_WITHSCORES.ts index 49707563546..578927331be 100644 --- a/lib/commands/ZDIFF_WITHSCORES.ts +++ b/packages/client/lib/commands/ZDIFF_WITHSCORES.ts @@ -1,5 +1,4 @@ import { RedisCommandArguments } from '.'; -import { transformReplySortedSetWithScores } from './generic-transformers'; import { transformArguments as transformZDiffArguments } from './ZDIFF'; export { FIRST_KEY_INDEX, IS_READ_ONLY } from './ZDIFF'; @@ -11,4 +10,4 @@ export function transformArguments(...args: Parameters { @@ -10,10 +10,10 @@ describe('ZINCRBY', () => { ); }); - itWithClient(TestRedisServers.OPEN, 'client.zIncrBy', async client => { + testUtils.testWithClient('client.zIncrBy', async client => { assert.equal( await client.zIncrBy('destination', 1, 'member'), 1 ); - }); + }, GLOBAL.SERVERS.OPEN); }); diff --git a/lib/commands/ZINCRBY.ts b/packages/client/lib/commands/ZINCRBY.ts similarity index 61% rename from lib/commands/ZINCRBY.ts rename to packages/client/lib/commands/ZINCRBY.ts index 39f32c165f7..1edf4beff4b 100644 --- a/lib/commands/ZINCRBY.ts +++ b/packages/client/lib/commands/ZINCRBY.ts @@ -1,4 +1,4 @@ -import { transformArgumentNumberInfinity, transformReplyNumberInfinity } from './generic-transformers'; +import { transformArgumentNumberInfinity } from './generic-transformers'; export const FIRST_KEY_INDEX = 1; @@ -11,4 +11,4 @@ export function transformArguments(key: string, increment: number, member: strin ]; } -export const transformReply = transformReplyNumberInfinity; +export { transformReplyNumberInfinity as transformReply } from './generic-transformers'; diff --git a/lib/commands/ZINTER.spec.ts b/packages/client/lib/commands/ZINTER.spec.ts similarity index 85% rename from lib/commands/ZINTER.spec.ts rename to packages/client/lib/commands/ZINTER.spec.ts index 998c46fd3e0..4d2d86c8869 100644 --- a/lib/commands/ZINTER.spec.ts +++ b/packages/client/lib/commands/ZINTER.spec.ts @@ -1,10 +1,10 @@ import { strict as assert } from 'assert'; -import { TestRedisServers, itWithClient, describeHandleMinimumRedisVersion } from '../test-utils'; +import testUtils, { GLOBAL } from '../test-utils'; import { transformArguments } from './ZINTER'; describe('ZINTER', () => { - describeHandleMinimumRedisVersion([6, 2]); - + testUtils.isVersionGreaterThanHook([6, 2]); + describe('transformArguments', () => { it('key (string)', () => { assert.deepEqual( @@ -49,10 +49,10 @@ describe('ZINTER', () => { }); }); - itWithClient(TestRedisServers.OPEN, 'client.zInter', async client => { + testUtils.testWithClient('client.zInter', async client => { assert.deepEqual( await client.zInter('key'), [] ); - }); + }, GLOBAL.SERVERS.OPEN); }); diff --git a/lib/commands/ZINTER.ts b/packages/client/lib/commands/ZINTER.ts similarity index 100% rename from lib/commands/ZINTER.ts rename to packages/client/lib/commands/ZINTER.ts diff --git a/lib/commands/ZINTERSTORE.spec.ts b/packages/client/lib/commands/ZINTERSTORE.spec.ts similarity index 91% rename from lib/commands/ZINTERSTORE.spec.ts rename to packages/client/lib/commands/ZINTERSTORE.spec.ts index fca03157cb2..224961f0786 100644 --- a/lib/commands/ZINTERSTORE.spec.ts +++ b/packages/client/lib/commands/ZINTERSTORE.spec.ts @@ -1,5 +1,5 @@ import { strict as assert } from 'assert'; -import { TestRedisServers, itWithClient } from '../test-utils'; +import testUtils, { GLOBAL } from '../test-utils'; import { transformArguments } from './ZINTERSTORE'; describe('ZINTERSTORE', () => { @@ -47,10 +47,10 @@ describe('ZINTERSTORE', () => { }); }); - itWithClient(TestRedisServers.OPEN, 'client.zInterStore', async client => { + testUtils.testWithClient('client.zInterStore', async client => { assert.equal( await client.zInterStore('destination', 'key'), 0 ); - }); + }, GLOBAL.SERVERS.OPEN); }); diff --git a/lib/commands/ZINTERSTORE.ts b/packages/client/lib/commands/ZINTERSTORE.ts similarity index 100% rename from lib/commands/ZINTERSTORE.ts rename to packages/client/lib/commands/ZINTERSTORE.ts diff --git a/lib/commands/ZINTER_WITHSCORES.spec.ts b/packages/client/lib/commands/ZINTER_WITHSCORES.spec.ts similarity index 86% rename from lib/commands/ZINTER_WITHSCORES.spec.ts rename to packages/client/lib/commands/ZINTER_WITHSCORES.spec.ts index f66787e3def..0eaeb26a244 100644 --- a/lib/commands/ZINTER_WITHSCORES.spec.ts +++ b/packages/client/lib/commands/ZINTER_WITHSCORES.spec.ts @@ -1,9 +1,9 @@ import { strict as assert } from 'assert'; -import { TestRedisServers, itWithClient, describeHandleMinimumRedisVersion } from '../test-utils'; +import testUtils, { GLOBAL } from '../test-utils'; import { transformArguments } from './ZINTER_WITHSCORES'; describe('ZINTER WITHSCORES', () => { - describeHandleMinimumRedisVersion([6, 2]); + testUtils.isVersionGreaterThanHook([6, 2]); describe('transformArguments', () => { it('key (string)', () => { @@ -49,10 +49,10 @@ describe('ZINTER WITHSCORES', () => { }); }); - itWithClient(TestRedisServers.OPEN, 'client.zInterWithScores', async client => { + testUtils.testWithClient('client.zInterWithScores', async client => { assert.deepEqual( await client.zInterWithScores('key'), [] ); - }); + }, GLOBAL.SERVERS.OPEN); }); diff --git a/lib/commands/ZINTER_WITHSCORES.ts b/packages/client/lib/commands/ZINTER_WITHSCORES.ts similarity index 73% rename from lib/commands/ZINTER_WITHSCORES.ts rename to packages/client/lib/commands/ZINTER_WITHSCORES.ts index f75a506de73..cfe022f9e94 100644 --- a/lib/commands/ZINTER_WITHSCORES.ts +++ b/packages/client/lib/commands/ZINTER_WITHSCORES.ts @@ -1,5 +1,4 @@ import { RedisCommandArguments } from '.'; -import { transformReplySortedSetWithScores } from './generic-transformers'; import { transformArguments as transformZInterArguments } from './ZINTER'; export { FIRST_KEY_INDEX, IS_READ_ONLY } from './ZINTER'; @@ -11,4 +10,4 @@ export function transformArguments(...args: Parameters { @@ -10,10 +10,10 @@ describe('ZLEXCOUNT', () => { ); }); - itWithClient(TestRedisServers.OPEN, 'client.zLexCount', async client => { + testUtils.testWithClient('client.zLexCount', async client => { assert.equal( await client.zLexCount('key', '[a', '[b'), 0 ); - }); + }, GLOBAL.SERVERS.OPEN); }); diff --git a/lib/commands/ZLEXCOUNT.ts b/packages/client/lib/commands/ZLEXCOUNT.ts similarity index 100% rename from lib/commands/ZLEXCOUNT.ts rename to packages/client/lib/commands/ZLEXCOUNT.ts diff --git a/lib/commands/ZMSCORE.spec.ts b/packages/client/lib/commands/ZMSCORE.spec.ts similarity index 73% rename from lib/commands/ZMSCORE.spec.ts rename to packages/client/lib/commands/ZMSCORE.spec.ts index 3cf3845392d..228c8e9d6f6 100644 --- a/lib/commands/ZMSCORE.spec.ts +++ b/packages/client/lib/commands/ZMSCORE.spec.ts @@ -1,9 +1,9 @@ import { strict as assert } from 'assert'; -import { TestRedisServers, itWithClient, describeHandleMinimumRedisVersion } from '../test-utils'; +import testUtils, { GLOBAL } from '../test-utils'; import { transformArguments } from './ZMSCORE'; describe('ZMSCORE', () => { - describeHandleMinimumRedisVersion([6, 2]); + testUtils.isVersionGreaterThanHook([6, 2]); describe('transformArguments', () => { it('string', () => { @@ -21,10 +21,10 @@ describe('ZMSCORE', () => { }); }); - itWithClient(TestRedisServers.OPEN, 'client.zmScore', async client => { + testUtils.testWithClient('client.zmScore', async client => { assert.deepEqual( await client.zmScore('key', 'member'), [null] ); - }); + }, GLOBAL.SERVERS.OPEN); }); diff --git a/lib/commands/ZMSCORE.ts b/packages/client/lib/commands/ZMSCORE.ts similarity index 62% rename from lib/commands/ZMSCORE.ts rename to packages/client/lib/commands/ZMSCORE.ts index 2790f712316..a6dd8e45e4a 100644 --- a/lib/commands/ZMSCORE.ts +++ b/packages/client/lib/commands/ZMSCORE.ts @@ -1,5 +1,5 @@ import { RedisCommandArguments } from '.'; -import { pushVerdictArguments, transformReplyNumberInfinityNullArray } from './generic-transformers'; +import { pushVerdictArguments } from './generic-transformers'; export const FIRST_KEY_INDEX = 1; @@ -9,4 +9,4 @@ export function transformArguments(key: string, member: string | Array): return pushVerdictArguments(['ZMSCORE', key], member); } -export const transformReply = transformReplyNumberInfinityNullArray; +export { transformReplyNumberInfinityNullArray as transformReply } from './generic-transformers'; diff --git a/lib/commands/ZPOPMAX.spec.ts b/packages/client/lib/commands/ZPOPMAX.spec.ts similarity index 79% rename from lib/commands/ZPOPMAX.spec.ts rename to packages/client/lib/commands/ZPOPMAX.spec.ts index ceab3cad1d0..18fba23a3e9 100644 --- a/lib/commands/ZPOPMAX.spec.ts +++ b/packages/client/lib/commands/ZPOPMAX.spec.ts @@ -1,5 +1,5 @@ import { strict as assert } from 'assert'; -import { TestRedisServers, itWithClient } from '../test-utils'; +import testUtils, { GLOBAL } from '../test-utils'; import { transformArguments, transformReply } from './ZPOPMAX'; describe('ZPOPMAX', () => { @@ -21,14 +21,14 @@ describe('ZPOPMAX', () => { }); describe('client.zPopMax', () => { - itWithClient(TestRedisServers.OPEN, 'null', async client => { + testUtils.testWithClient('null', async client => { assert.equal( await client.zPopMax('key'), null ); - }); + }, GLOBAL.SERVERS.OPEN); - itWithClient(TestRedisServers.OPEN, 'member', async client => { + testUtils.testWithClient('member', async client => { const member = { score: 1, value: 'value' }, [, zPopMaxReply] = await Promise.all([ client.zAdd('key', member), @@ -36,6 +36,6 @@ describe('ZPOPMAX', () => { ]); assert.deepEqual(zPopMaxReply, member); - }); + }, GLOBAL.SERVERS.OPEN); }); }); diff --git a/lib/commands/ZPOPMAX.ts b/packages/client/lib/commands/ZPOPMAX.ts similarity index 100% rename from lib/commands/ZPOPMAX.ts rename to packages/client/lib/commands/ZPOPMAX.ts diff --git a/lib/commands/ZPOPMAX_COUNT.spec.ts b/packages/client/lib/commands/ZPOPMAX_COUNT.spec.ts similarity index 72% rename from lib/commands/ZPOPMAX_COUNT.spec.ts rename to packages/client/lib/commands/ZPOPMAX_COUNT.spec.ts index c0e71977ee8..b282d0d3199 100644 --- a/lib/commands/ZPOPMAX_COUNT.spec.ts +++ b/packages/client/lib/commands/ZPOPMAX_COUNT.spec.ts @@ -1,5 +1,5 @@ import { strict as assert } from 'assert'; -import { TestRedisServers, itWithClient } from '../test-utils'; +import testUtils, { GLOBAL } from '../test-utils'; import { transformArguments } from './ZPOPMAX_COUNT'; describe('ZPOPMAX COUNT', () => { @@ -10,10 +10,10 @@ describe('ZPOPMAX COUNT', () => { ); }); - itWithClient(TestRedisServers.OPEN, 'client.zPopMaxCount', async client => { + testUtils.testWithClient('client.zPopMaxCount', async client => { assert.deepEqual( await client.zPopMaxCount('key', 1), [] ); - }); + }, GLOBAL.SERVERS.OPEN); }); diff --git a/lib/commands/ZPOPMAX_COUNT.ts b/packages/client/lib/commands/ZPOPMAX_COUNT.ts similarity index 67% rename from lib/commands/ZPOPMAX_COUNT.ts rename to packages/client/lib/commands/ZPOPMAX_COUNT.ts index abfa8494ec4..abef32cca56 100644 --- a/lib/commands/ZPOPMAX_COUNT.ts +++ b/packages/client/lib/commands/ZPOPMAX_COUNT.ts @@ -1,4 +1,3 @@ -import { transformReplySortedSetWithScores } from './generic-transformers'; import { transformArguments as transformZPopMaxArguments } from './ZPOPMAX'; export { FIRST_KEY_INDEX } from './ZPOPMAX'; @@ -10,4 +9,4 @@ export function transformArguments(key: string, count: number): Array { ]; } -export const transformReply = transformReplySortedSetWithScores; +export { transformReplySortedSetWithScores as transformReply } from './generic-transformers'; diff --git a/lib/commands/ZPOPMIN.spec.ts b/packages/client/lib/commands/ZPOPMIN.spec.ts similarity index 79% rename from lib/commands/ZPOPMIN.spec.ts rename to packages/client/lib/commands/ZPOPMIN.spec.ts index c69ca7c27f7..624b7054404 100644 --- a/lib/commands/ZPOPMIN.spec.ts +++ b/packages/client/lib/commands/ZPOPMIN.spec.ts @@ -1,5 +1,5 @@ import { strict as assert } from 'assert'; -import { TestRedisServers, itWithClient } from '../test-utils'; +import testUtils, { GLOBAL } from '../test-utils'; import { transformArguments, transformReply } from './ZPOPMIN'; describe('ZPOPMIN', () => { @@ -21,14 +21,14 @@ describe('ZPOPMIN', () => { }); describe('client.zPopMin', () => { - itWithClient(TestRedisServers.OPEN, 'null', async client => { + testUtils.testWithClient('null', async client => { assert.equal( await client.zPopMin('key'), null ); - }); + }, GLOBAL.SERVERS.OPEN); - itWithClient(TestRedisServers.OPEN, 'member', async client => { + testUtils.testWithClient('member', async client => { const member = { score: 1, value: 'value' }, [, zPopMinReply] = await Promise.all([ client.zAdd('key', member), @@ -36,6 +36,6 @@ describe('ZPOPMIN', () => { ]); assert.deepEqual(zPopMinReply, member); - }); + }, GLOBAL.SERVERS.OPEN); }); }); diff --git a/lib/commands/ZPOPMIN.ts b/packages/client/lib/commands/ZPOPMIN.ts similarity index 100% rename from lib/commands/ZPOPMIN.ts rename to packages/client/lib/commands/ZPOPMIN.ts diff --git a/lib/commands/ZPOPMIN_COUNT.spec.ts b/packages/client/lib/commands/ZPOPMIN_COUNT.spec.ts similarity index 72% rename from lib/commands/ZPOPMIN_COUNT.spec.ts rename to packages/client/lib/commands/ZPOPMIN_COUNT.spec.ts index 1c2745a0fdf..6d40002ab72 100644 --- a/lib/commands/ZPOPMIN_COUNT.spec.ts +++ b/packages/client/lib/commands/ZPOPMIN_COUNT.spec.ts @@ -1,5 +1,5 @@ import { strict as assert } from 'assert'; -import { TestRedisServers, itWithClient } from '../test-utils'; +import testUtils, { GLOBAL } from '../test-utils'; import { transformArguments } from './ZPOPMIN_COUNT'; describe('ZPOPMIN COUNT', () => { @@ -10,10 +10,10 @@ describe('ZPOPMIN COUNT', () => { ); }); - itWithClient(TestRedisServers.OPEN, 'client.zPopMinCount', async client => { + testUtils.testWithClient('client.zPopMinCount', async client => { assert.deepEqual( await client.zPopMinCount('key', 1), [] ); - }); + }, GLOBAL.SERVERS.OPEN); }); diff --git a/lib/commands/ZPOPMIN_COUNT.ts b/packages/client/lib/commands/ZPOPMIN_COUNT.ts similarity index 67% rename from lib/commands/ZPOPMIN_COUNT.ts rename to packages/client/lib/commands/ZPOPMIN_COUNT.ts index e313b32dc8a..7d290ebfe37 100644 --- a/lib/commands/ZPOPMIN_COUNT.ts +++ b/packages/client/lib/commands/ZPOPMIN_COUNT.ts @@ -1,4 +1,3 @@ -import { transformReplySortedSetWithScores } from './generic-transformers'; import { transformArguments as transformZPopMinArguments } from './ZPOPMIN'; export { FIRST_KEY_INDEX } from './ZPOPMIN'; @@ -10,4 +9,4 @@ export function transformArguments(key: string, count: number): Array { ]; } -export const transformReply = transformReplySortedSetWithScores; +export { transformReplySortedSetWithScores as transformReply } from './generic-transformers'; diff --git a/lib/commands/ZRANDMEMBER.spec.ts b/packages/client/lib/commands/ZRANDMEMBER.spec.ts similarity index 62% rename from lib/commands/ZRANDMEMBER.spec.ts rename to packages/client/lib/commands/ZRANDMEMBER.spec.ts index da31641a18c..c57d26f830e 100644 --- a/lib/commands/ZRANDMEMBER.spec.ts +++ b/packages/client/lib/commands/ZRANDMEMBER.spec.ts @@ -1,9 +1,9 @@ import { strict as assert } from 'assert'; -import { TestRedisServers, itWithClient, describeHandleMinimumRedisVersion } from '../test-utils'; +import testUtils, { GLOBAL } from '../test-utils'; import { transformArguments } from './ZRANDMEMBER'; describe('ZRANDMEMBER', () => { - describeHandleMinimumRedisVersion([6, 2]); + testUtils.isVersionGreaterThanHook([6, 2]); it('transformArguments', () => { assert.deepEqual( @@ -12,10 +12,10 @@ describe('ZRANDMEMBER', () => { ); }); - itWithClient(TestRedisServers.OPEN, 'client.zRandMember', async client => { + testUtils.testWithClient('client.zRandMember', async client => { assert.equal( await client.zRandMember('key'), null ); - }); + }, GLOBAL.SERVERS.OPEN); }); diff --git a/lib/commands/ZRANDMEMBER.ts b/packages/client/lib/commands/ZRANDMEMBER.ts similarity index 100% rename from lib/commands/ZRANDMEMBER.ts rename to packages/client/lib/commands/ZRANDMEMBER.ts diff --git a/lib/commands/ZRANDMEMBER_COUNT.spec.ts b/packages/client/lib/commands/ZRANDMEMBER_COUNT.spec.ts similarity index 63% rename from lib/commands/ZRANDMEMBER_COUNT.spec.ts rename to packages/client/lib/commands/ZRANDMEMBER_COUNT.spec.ts index 4c873c82d90..10db0727b23 100644 --- a/lib/commands/ZRANDMEMBER_COUNT.spec.ts +++ b/packages/client/lib/commands/ZRANDMEMBER_COUNT.spec.ts @@ -1,9 +1,9 @@ import { strict as assert } from 'assert'; -import { TestRedisServers, itWithClient, describeHandleMinimumRedisVersion } from '../test-utils'; +import testUtils, { GLOBAL } from '../test-utils'; import { transformArguments } from './ZRANDMEMBER_COUNT'; describe('ZRANDMEMBER COUNT', () => { - describeHandleMinimumRedisVersion([6, 2, 5]); + testUtils.isVersionGreaterThanHook([6, 2, 5]); it('transformArguments', () => { assert.deepEqual( @@ -12,10 +12,10 @@ describe('ZRANDMEMBER COUNT', () => { ); }); - itWithClient(TestRedisServers.OPEN, 'client.zRandMemberCount', async client => { + testUtils.testWithClient('client.zRandMemberCount', async client => { assert.deepEqual( await client.zRandMemberCount('key', 1), [] ); - }); + }, GLOBAL.SERVERS.OPEN); }); diff --git a/lib/commands/ZRANDMEMBER_COUNT.ts b/packages/client/lib/commands/ZRANDMEMBER_COUNT.ts similarity index 100% rename from lib/commands/ZRANDMEMBER_COUNT.ts rename to packages/client/lib/commands/ZRANDMEMBER_COUNT.ts diff --git a/lib/commands/ZRANDMEMBER_COUNT_WITHSCORES.spec.ts b/packages/client/lib/commands/ZRANDMEMBER_COUNT_WITHSCORES.spec.ts similarity index 64% rename from lib/commands/ZRANDMEMBER_COUNT_WITHSCORES.spec.ts rename to packages/client/lib/commands/ZRANDMEMBER_COUNT_WITHSCORES.spec.ts index 55624361fb6..5b5ec1f500f 100644 --- a/lib/commands/ZRANDMEMBER_COUNT_WITHSCORES.spec.ts +++ b/packages/client/lib/commands/ZRANDMEMBER_COUNT_WITHSCORES.spec.ts @@ -1,9 +1,9 @@ import { strict as assert } from 'assert'; -import { TestRedisServers, itWithClient, describeHandleMinimumRedisVersion } from '../test-utils'; +import testUtils, { GLOBAL } from '../test-utils'; import { transformArguments } from './ZRANDMEMBER_COUNT_WITHSCORES'; describe('ZRANDMEMBER COUNT WITHSCORES', () => { - describeHandleMinimumRedisVersion([6, 2, 5]); + testUtils.isVersionGreaterThanHook([6, 2, 5]); it('transformArguments', () => { assert.deepEqual( @@ -12,10 +12,10 @@ describe('ZRANDMEMBER COUNT WITHSCORES', () => { ); }); - itWithClient(TestRedisServers.OPEN, 'client.zRandMemberCountWithScores', async client => { + testUtils.testWithClient('client.zRandMemberCountWithScores', async client => { assert.deepEqual( await client.zRandMemberCountWithScores('key', 1), [] ); - }); + }, GLOBAL.SERVERS.OPEN); }); diff --git a/lib/commands/ZRANDMEMBER_COUNT_WITHSCORES.ts b/packages/client/lib/commands/ZRANDMEMBER_COUNT_WITHSCORES.ts similarity index 73% rename from lib/commands/ZRANDMEMBER_COUNT_WITHSCORES.ts rename to packages/client/lib/commands/ZRANDMEMBER_COUNT_WITHSCORES.ts index 6d79f41c955..8f49b326ad1 100644 --- a/lib/commands/ZRANDMEMBER_COUNT_WITHSCORES.ts +++ b/packages/client/lib/commands/ZRANDMEMBER_COUNT_WITHSCORES.ts @@ -1,4 +1,3 @@ -import { transformReplySortedSetWithScores } from './generic-transformers'; import { transformArguments as transformZRandMemberCountArguments } from './ZRANDMEMBER_COUNT'; export { FIRST_KEY_INDEX, IS_READ_ONLY } from './ZRANDMEMBER_COUNT'; @@ -10,4 +9,4 @@ export function transformArguments(...args: Parameters { @@ -11,13 +11,6 @@ describe('ZRANGE', () => { ); }); - it('using strings', () => { - assert.deepEqual( - transformArguments('src', '0', '1'), - ['ZRANGE', 'src', '0', '1'] - ); - }); - it('with BYSCORE', () => { assert.deepEqual( transformArguments('src', 0, 1, { @@ -72,10 +65,10 @@ describe('ZRANGE', () => { }); }); - itWithClient(TestRedisServers.OPEN, 'client.zRange', async client => { + testUtils.testWithClient('client.zRange', async client => { assert.deepEqual( await client.zRange('src', 0, 1), [] ); - }); + }, GLOBAL.SERVERS.OPEN); }); diff --git a/lib/commands/ZRANGE.ts b/packages/client/lib/commands/ZRANGE.ts similarity index 78% rename from lib/commands/ZRANGE.ts rename to packages/client/lib/commands/ZRANGE.ts index 391c5ca893e..3d30e0f8dd2 100644 --- a/lib/commands/ZRANGE.ts +++ b/packages/client/lib/commands/ZRANGE.ts @@ -1,4 +1,4 @@ -import { transformArgumentNumberInfinity } from './generic-transformers'; +import { transformArgumentStringNumberInfinity } from './generic-transformers'; export const FIRST_KEY_INDEX = 1; @@ -17,8 +17,8 @@ export function transformArguments(key: string, min: string | number, max: strin const args = [ 'ZRANGE', key, - typeof min === 'string' ? min : transformArgumentNumberInfinity(min), - typeof max === 'string' ? max : transformArgumentNumberInfinity(max) + transformArgumentStringNumberInfinity(min), + transformArgumentStringNumberInfinity(max) ]; switch (options?.BY) { diff --git a/lib/commands/ZRANGEBYLEX.spec.ts b/packages/client/lib/commands/ZRANGEBYLEX.spec.ts similarity index 84% rename from lib/commands/ZRANGEBYLEX.spec.ts rename to packages/client/lib/commands/ZRANGEBYLEX.spec.ts index 7f687509548..fe7b7d5a16e 100644 --- a/lib/commands/ZRANGEBYLEX.spec.ts +++ b/packages/client/lib/commands/ZRANGEBYLEX.spec.ts @@ -1,5 +1,5 @@ import { strict as assert } from 'assert'; -import { TestRedisServers, itWithClient } from '../test-utils'; +import testUtils, { GLOBAL } from '../test-utils'; import { transformArguments } from './ZRANGEBYLEX'; describe('ZRANGEBYLEX', () => { @@ -24,10 +24,10 @@ describe('ZRANGEBYLEX', () => { }); }); - itWithClient(TestRedisServers.OPEN, 'client.zRangeByLex', async client => { + testUtils.testWithClient('client.zRangeByLex', async client => { assert.deepEqual( await client.zRangeByLex('src', '-', '+'), [] ); - }); + }, GLOBAL.SERVERS.OPEN); }); diff --git a/lib/commands/ZRANGEBYLEX.ts b/packages/client/lib/commands/ZRANGEBYLEX.ts similarity index 74% rename from lib/commands/ZRANGEBYLEX.ts rename to packages/client/lib/commands/ZRANGEBYLEX.ts index 7e2e7613b00..214d796da0a 100644 --- a/lib/commands/ZRANGEBYLEX.ts +++ b/packages/client/lib/commands/ZRANGEBYLEX.ts @@ -1,5 +1,5 @@ import { RedisCommandArguments } from '.'; -import { transformArgumentNumberInfinity } from './generic-transformers'; +import { transformArgumentStringNumberInfinity } from './generic-transformers'; export const FIRST_KEY_INDEX = 1; @@ -21,8 +21,8 @@ export function transformArguments( const args = [ 'ZRANGEBYLEX', key, - typeof min === 'string' ? min : transformArgumentNumberInfinity(min), - typeof max === 'string' ? max : transformArgumentNumberInfinity(max) + transformArgumentStringNumberInfinity(min), + transformArgumentStringNumberInfinity(max) ]; if (options?.LIMIT) { diff --git a/lib/commands/ZRANGEBYSCORE.spec.ts b/packages/client/lib/commands/ZRANGEBYSCORE.spec.ts similarity index 84% rename from lib/commands/ZRANGEBYSCORE.spec.ts rename to packages/client/lib/commands/ZRANGEBYSCORE.spec.ts index 0419b232563..a3484326306 100644 --- a/lib/commands/ZRANGEBYSCORE.spec.ts +++ b/packages/client/lib/commands/ZRANGEBYSCORE.spec.ts @@ -1,5 +1,5 @@ import { strict as assert } from 'assert'; -import { TestRedisServers, itWithClient } from '../test-utils'; +import testUtils, { GLOBAL } from '../test-utils'; import { transformArguments } from './ZRANGEBYSCORE'; describe('ZRANGEBYSCORE', () => { @@ -24,10 +24,10 @@ describe('ZRANGEBYSCORE', () => { }); }); - itWithClient(TestRedisServers.OPEN, 'client.zRangeByScore', async client => { + testUtils.testWithClient('client.zRangeByScore', async client => { assert.deepEqual( await client.zRangeByScore('src', 0, 1), [] ); - }); + }, GLOBAL.SERVERS.OPEN); }); diff --git a/lib/commands/ZRANGEBYSCORE.ts b/packages/client/lib/commands/ZRANGEBYSCORE.ts similarity index 74% rename from lib/commands/ZRANGEBYSCORE.ts rename to packages/client/lib/commands/ZRANGEBYSCORE.ts index 48dd8a415c6..f6097fad581 100644 --- a/lib/commands/ZRANGEBYSCORE.ts +++ b/packages/client/lib/commands/ZRANGEBYSCORE.ts @@ -1,5 +1,5 @@ import { RedisCommandArguments } from '.'; -import { transformArgumentNumberInfinity } from './generic-transformers'; +import { transformArgumentStringNumberInfinity } from './generic-transformers'; export const FIRST_KEY_INDEX = 1; @@ -21,8 +21,8 @@ export function transformArguments( const args = [ 'ZRANGEBYSCORE', key, - typeof min === 'string' ? min : transformArgumentNumberInfinity(min), - typeof max === 'string' ? max : transformArgumentNumberInfinity(max) + transformArgumentStringNumberInfinity(min), + transformArgumentStringNumberInfinity(max) ]; if (options?.LIMIT) { diff --git a/lib/commands/ZRANGEBYSCORE_WITHSCORES.spec.ts b/packages/client/lib/commands/ZRANGEBYSCORE_WITHSCORES.spec.ts similarity index 84% rename from lib/commands/ZRANGEBYSCORE_WITHSCORES.spec.ts rename to packages/client/lib/commands/ZRANGEBYSCORE_WITHSCORES.spec.ts index 84d1aeb0aad..3552d3e2535 100644 --- a/lib/commands/ZRANGEBYSCORE_WITHSCORES.spec.ts +++ b/packages/client/lib/commands/ZRANGEBYSCORE_WITHSCORES.spec.ts @@ -1,5 +1,5 @@ import { strict as assert } from 'assert'; -import { TestRedisServers, itWithClient } from '../test-utils'; +import testUtils, { GLOBAL } from '../test-utils'; import { transformArguments } from './ZRANGEBYSCORE_WITHSCORES'; describe('ZRANGEBYSCORE WITHSCORES', () => { @@ -24,10 +24,10 @@ describe('ZRANGEBYSCORE WITHSCORES', () => { }); }); - itWithClient(TestRedisServers.OPEN, 'client.zRangeByScoreWithScores', async client => { + testUtils.testWithClient('client.zRangeByScoreWithScores', async client => { assert.deepEqual( await client.zRangeByScoreWithScores('src', 0, 1), [] ); - }); + }, GLOBAL.SERVERS.OPEN); }); diff --git a/lib/commands/ZRANGEBYSCORE_WITHSCORES.ts b/packages/client/lib/commands/ZRANGEBYSCORE_WITHSCORES.ts similarity index 78% rename from lib/commands/ZRANGEBYSCORE_WITHSCORES.ts rename to packages/client/lib/commands/ZRANGEBYSCORE_WITHSCORES.ts index f6f7f993cbc..6541662c855 100644 --- a/lib/commands/ZRANGEBYSCORE_WITHSCORES.ts +++ b/packages/client/lib/commands/ZRANGEBYSCORE_WITHSCORES.ts @@ -1,5 +1,4 @@ import { RedisCommandArguments } from '.'; -import { transformReplySortedSetWithScores } from './generic-transformers'; import { ZRangeByScoreOptions, transformArguments as transformZRangeByScoreArguments } from './ZRANGEBYSCORE'; export { FIRST_KEY_INDEX, IS_READ_ONLY } from './ZRANGEBYSCORE'; @@ -16,4 +15,4 @@ export function transformArguments( ]; } -export const transformReply = transformReplySortedSetWithScores; +export { transformReplySortedSetWithScores as transformReply } from './generic-transformers'; diff --git a/lib/commands/ZRANGESTORE.spec.ts b/packages/client/lib/commands/ZRANGESTORE.spec.ts similarity index 90% rename from lib/commands/ZRANGESTORE.spec.ts rename to packages/client/lib/commands/ZRANGESTORE.spec.ts index 54055656409..7af253e539f 100644 --- a/lib/commands/ZRANGESTORE.spec.ts +++ b/packages/client/lib/commands/ZRANGESTORE.spec.ts @@ -1,9 +1,9 @@ import { strict as assert } from 'assert'; -import { TestRedisServers, itWithClient, describeHandleMinimumRedisVersion } from '../test-utils'; +import testUtils, { GLOBAL } from '../test-utils'; import { transformArguments, transformReply } from './ZRANGESTORE'; describe('ZRANGESTORE', () => { - describeHandleMinimumRedisVersion([6, 2]); + testUtils.isVersionGreaterThanHook([6, 2]); describe('transformArguments', () => { it('simple', () => { @@ -71,13 +71,14 @@ describe('ZRANGESTORE', () => { describe('transformReply', () => { it('should throw TypeError when reply is not a number', () => { assert.throws( + // eslint-disable-next-line @typescript-eslint/no-explicit-any () => (transformReply as any)([]), TypeError ); }); }); - itWithClient(TestRedisServers.OPEN, 'client.zRangeStore', async client => { + testUtils.testWithClient('client.zRangeStore', async client => { await client.zAdd('src', { score: 0.5, value: 'value' @@ -87,5 +88,5 @@ describe('ZRANGESTORE', () => { await client.zRangeStore('dst', 'src', 0, 1), 1 ); - }); + }, GLOBAL.SERVERS.OPEN); }); diff --git a/lib/commands/ZRANGESTORE.ts b/packages/client/lib/commands/ZRANGESTORE.ts similarity index 72% rename from lib/commands/ZRANGESTORE.ts rename to packages/client/lib/commands/ZRANGESTORE.ts index 6ad75661668..c76afe61029 100644 --- a/lib/commands/ZRANGESTORE.ts +++ b/packages/client/lib/commands/ZRANGESTORE.ts @@ -1,4 +1,4 @@ -import { transformArgumentNumberInfinity } from './generic-transformers'; +import { transformArgumentStringNumberInfinity } from './generic-transformers'; export const FIRST_KEY_INDEX = 1; @@ -12,13 +12,19 @@ interface ZRangeStoreOptions { WITHSCORES?: true; } -export function transformArguments(dst: string, src: string, min: number, max: number, options?: ZRangeStoreOptions): Array { +export function transformArguments( + dst: string, + src: string, + min: string | number, + max: string | number, + options?: ZRangeStoreOptions +): Array { const args = [ 'ZRANGESTORE', dst, src, - transformArgumentNumberInfinity(min), - transformArgumentNumberInfinity(max) + transformArgumentStringNumberInfinity(min), + transformArgumentStringNumberInfinity(max) ]; switch (options?.BY) { diff --git a/lib/commands/ZRANGE_WITHSCORES.spec.ts b/packages/client/lib/commands/ZRANGE_WITHSCORES.spec.ts similarity index 92% rename from lib/commands/ZRANGE_WITHSCORES.spec.ts rename to packages/client/lib/commands/ZRANGE_WITHSCORES.spec.ts index 4c739b3d3b3..d9b07e19dda 100644 --- a/lib/commands/ZRANGE_WITHSCORES.spec.ts +++ b/packages/client/lib/commands/ZRANGE_WITHSCORES.spec.ts @@ -1,5 +1,5 @@ import { strict as assert } from 'assert'; -import { TestRedisServers, itWithClient } from '../test-utils'; +import testUtils, { GLOBAL } from '../test-utils'; import { transformArguments } from './ZRANGE_WITHSCORES'; describe('ZRANGE WITHSCORES', () => { @@ -56,10 +56,10 @@ describe('ZRANGE WITHSCORES', () => { }); }); - itWithClient(TestRedisServers.OPEN, 'client.zRangeWithScores', async client => { + testUtils.testWithClient('client.zRangeWithScores', async client => { assert.deepEqual( await client.zRangeWithScores('src', 0, 1), [] ); - }); + }, GLOBAL.SERVERS.OPEN); }); diff --git a/lib/commands/ZRANGE_WITHSCORES.ts b/packages/client/lib/commands/ZRANGE_WITHSCORES.ts similarity index 70% rename from lib/commands/ZRANGE_WITHSCORES.ts rename to packages/client/lib/commands/ZRANGE_WITHSCORES.ts index 0f777132f27..63d71dfb681 100644 --- a/lib/commands/ZRANGE_WITHSCORES.ts +++ b/packages/client/lib/commands/ZRANGE_WITHSCORES.ts @@ -1,4 +1,3 @@ -import { transformReplySortedSetWithScores } from './generic-transformers'; import { transformArguments as transformZRangeArguments } from './ZRANGE'; export { FIRST_KEY_INDEX, IS_READ_ONLY } from './ZRANGE'; @@ -10,4 +9,4 @@ export function transformArguments(...args: Parameters { @@ -10,10 +10,10 @@ describe('ZRANK', () => { ); }); - itWithClient(TestRedisServers.OPEN, 'client.zRank', async client => { + testUtils.testWithClient('client.zRank', async client => { assert.equal( await client.zRank('key', 'member'), null ); - }); + }, GLOBAL.SERVERS.OPEN); }); diff --git a/lib/commands/ZRANK.ts b/packages/client/lib/commands/ZRANK.ts similarity index 100% rename from lib/commands/ZRANK.ts rename to packages/client/lib/commands/ZRANK.ts diff --git a/lib/commands/ZREM.spec.ts b/packages/client/lib/commands/ZREM.spec.ts similarity index 81% rename from lib/commands/ZREM.spec.ts rename to packages/client/lib/commands/ZREM.spec.ts index d613832035d..3ac001708a0 100644 --- a/lib/commands/ZREM.spec.ts +++ b/packages/client/lib/commands/ZREM.spec.ts @@ -1,5 +1,5 @@ import { strict as assert } from 'assert'; -import { TestRedisServers, itWithClient } from '../test-utils'; +import testUtils, { GLOBAL } from '../test-utils'; import { transformArguments } from './ZREM'; describe('ZREM', () => { @@ -19,10 +19,10 @@ describe('ZREM', () => { }); }); - itWithClient(TestRedisServers.OPEN, 'client.zRem', async client => { + testUtils.testWithClient('client.zRem', async client => { assert.equal( await client.zRem('key', 'member'), 0 ); - }); + }, GLOBAL.SERVERS.OPEN); }); diff --git a/lib/commands/ZREM.ts b/packages/client/lib/commands/ZREM.ts similarity index 100% rename from lib/commands/ZREM.ts rename to packages/client/lib/commands/ZREM.ts diff --git a/lib/commands/ZREMRANGEBYLEX.spec.ts b/packages/client/lib/commands/ZREMRANGEBYLEX.spec.ts similarity index 73% rename from lib/commands/ZREMRANGEBYLEX.spec.ts rename to packages/client/lib/commands/ZREMRANGEBYLEX.spec.ts index 7aae059480c..b59c9e9f3b0 100644 --- a/lib/commands/ZREMRANGEBYLEX.spec.ts +++ b/packages/client/lib/commands/ZREMRANGEBYLEX.spec.ts @@ -1,5 +1,5 @@ import { strict as assert } from 'assert'; -import { TestRedisServers, itWithClient } from '../test-utils'; +import testUtils, { GLOBAL } from '../test-utils'; import { transformArguments } from './ZREMRANGEBYLEX'; describe('ZREMRANGEBYLEX', () => { @@ -10,10 +10,10 @@ describe('ZREMRANGEBYLEX', () => { ); }); - itWithClient(TestRedisServers.OPEN, 'client.zRemRangeByLex', async client => { + testUtils.testWithClient('client.zRemRangeByLex', async client => { assert.equal( await client.zRemRangeByLex('key', '[a', '[b'), 0 ); - }); + }, GLOBAL.SERVERS.OPEN); }); diff --git a/lib/commands/ZREMRANGEBYLEX.ts b/packages/client/lib/commands/ZREMRANGEBYLEX.ts similarity index 100% rename from lib/commands/ZREMRANGEBYLEX.ts rename to packages/client/lib/commands/ZREMRANGEBYLEX.ts diff --git a/lib/commands/ZREMRANGEBYRANK.spec.ts b/packages/client/lib/commands/ZREMRANGEBYRANK.spec.ts similarity index 72% rename from lib/commands/ZREMRANGEBYRANK.spec.ts rename to packages/client/lib/commands/ZREMRANGEBYRANK.spec.ts index 401b57c8e2a..c659dadb790 100644 --- a/lib/commands/ZREMRANGEBYRANK.spec.ts +++ b/packages/client/lib/commands/ZREMRANGEBYRANK.spec.ts @@ -1,5 +1,5 @@ import { strict as assert } from 'assert'; -import { TestRedisServers, itWithClient } from '../test-utils'; +import testUtils, { GLOBAL } from '../test-utils'; import { transformArguments } from './ZREMRANGEBYRANK'; describe('ZREMRANGEBYRANK', () => { @@ -10,10 +10,10 @@ describe('ZREMRANGEBYRANK', () => { ); }); - itWithClient(TestRedisServers.OPEN, 'client.zRemRangeByRank', async client => { + testUtils.testWithClient('client.zRemRangeByRank', async client => { assert.equal( await client.zRemRangeByRank('key', 0, 1), 0 ); - }); + }, GLOBAL.SERVERS.OPEN); }); diff --git a/lib/commands/ZREMRANGEBYRANK.ts b/packages/client/lib/commands/ZREMRANGEBYRANK.ts similarity index 100% rename from lib/commands/ZREMRANGEBYRANK.ts rename to packages/client/lib/commands/ZREMRANGEBYRANK.ts diff --git a/lib/commands/ZREMRANGEBYSCORE.spec.ts b/packages/client/lib/commands/ZREMRANGEBYSCORE.spec.ts similarity index 72% rename from lib/commands/ZREMRANGEBYSCORE.spec.ts rename to packages/client/lib/commands/ZREMRANGEBYSCORE.spec.ts index 141392e772b..988fd7690c9 100644 --- a/lib/commands/ZREMRANGEBYSCORE.spec.ts +++ b/packages/client/lib/commands/ZREMRANGEBYSCORE.spec.ts @@ -1,5 +1,5 @@ import { strict as assert } from 'assert'; -import { TestRedisServers, itWithClient } from '../test-utils'; +import testUtils, { GLOBAL } from '../test-utils'; import { transformArguments } from './ZREMRANGEBYSCORE'; describe('ZREMRANGEBYSCORE', () => { @@ -10,10 +10,10 @@ describe('ZREMRANGEBYSCORE', () => { ); }); - itWithClient(TestRedisServers.OPEN, 'client.zRemRangeByScore', async client => { + testUtils.testWithClient('client.zRemRangeByScore', async client => { assert.equal( await client.zRemRangeByScore('key', 0, 1), 0 ); - }); + }, GLOBAL.SERVERS.OPEN); }); diff --git a/lib/commands/ZREMRANGEBYSCORE.ts b/packages/client/lib/commands/ZREMRANGEBYSCORE.ts similarity index 100% rename from lib/commands/ZREMRANGEBYSCORE.ts rename to packages/client/lib/commands/ZREMRANGEBYSCORE.ts diff --git a/lib/commands/ZREVRANK.spec.ts b/packages/client/lib/commands/ZREVRANK.spec.ts similarity index 72% rename from lib/commands/ZREVRANK.spec.ts rename to packages/client/lib/commands/ZREVRANK.spec.ts index 727a61a35a6..d9fef0d70a4 100644 --- a/lib/commands/ZREVRANK.spec.ts +++ b/packages/client/lib/commands/ZREVRANK.spec.ts @@ -1,5 +1,5 @@ import { strict as assert } from 'assert'; -import { TestRedisServers, itWithClient } from '../test-utils'; +import testUtils, { GLOBAL } from '../test-utils'; import { transformArguments } from './ZREVRANK'; describe('ZREVRANK', () => { @@ -10,10 +10,10 @@ describe('ZREVRANK', () => { ); }); - itWithClient(TestRedisServers.OPEN, 'client.zRevRank', async client => { + testUtils.testWithClient('client.zRevRank', async client => { assert.equal( await client.zRevRank('key', 'member'), null ); - }); + }, GLOBAL.SERVERS.OPEN); }); diff --git a/lib/commands/ZREVRANK.ts b/packages/client/lib/commands/ZREVRANK.ts similarity index 100% rename from lib/commands/ZREVRANK.ts rename to packages/client/lib/commands/ZREVRANK.ts diff --git a/lib/commands/ZSCAN.spec.ts b/packages/client/lib/commands/ZSCAN.spec.ts similarity index 93% rename from lib/commands/ZSCAN.spec.ts rename to packages/client/lib/commands/ZSCAN.spec.ts index 3ff0c0a52b2..afa221a1ef3 100644 --- a/lib/commands/ZSCAN.spec.ts +++ b/packages/client/lib/commands/ZSCAN.spec.ts @@ -1,5 +1,5 @@ import { strict as assert } from 'assert'; -import { TestRedisServers, itWithClient } from '../test-utils'; +import testUtils, { GLOBAL } from '../test-utils'; import { transformArguments, transformReply } from './ZSCAN'; describe('ZSCAN', () => { @@ -65,7 +65,7 @@ describe('ZSCAN', () => { }); }); - itWithClient(TestRedisServers.OPEN, 'client.zScan', async client => { + testUtils.testWithClient('client.zScan', async client => { assert.deepEqual( await client.zScan('key', 0), { @@ -73,5 +73,5 @@ describe('ZSCAN', () => { members: [] } ); - }); + }, GLOBAL.SERVERS.OPEN); }); diff --git a/lib/commands/ZSCAN.ts b/packages/client/lib/commands/ZSCAN.ts similarity index 100% rename from lib/commands/ZSCAN.ts rename to packages/client/lib/commands/ZSCAN.ts diff --git a/lib/commands/ZSCORE.spec.ts b/packages/client/lib/commands/ZSCORE.spec.ts similarity index 72% rename from lib/commands/ZSCORE.spec.ts rename to packages/client/lib/commands/ZSCORE.spec.ts index d346a2e2c81..fe2a1c6a7c5 100644 --- a/lib/commands/ZSCORE.spec.ts +++ b/packages/client/lib/commands/ZSCORE.spec.ts @@ -1,5 +1,5 @@ import { strict as assert } from 'assert'; -import { TestRedisServers, itWithClient } from '../test-utils'; +import testUtils, { GLOBAL } from '../test-utils'; import { transformArguments } from './ZSCORE'; describe('ZSCORE', () => { @@ -10,10 +10,10 @@ describe('ZSCORE', () => { ); }); - itWithClient(TestRedisServers.OPEN, 'client.zScore', async client => { + testUtils.testWithClient('client.zScore', async client => { assert.equal( await client.zScore('key', 'member'), null ); - }); + }, GLOBAL.SERVERS.OPEN); }); diff --git a/lib/commands/ZSCORE.ts b/packages/client/lib/commands/ZSCORE.ts similarity index 57% rename from lib/commands/ZSCORE.ts rename to packages/client/lib/commands/ZSCORE.ts index 18b664a3217..2bdecaf432a 100644 --- a/lib/commands/ZSCORE.ts +++ b/packages/client/lib/commands/ZSCORE.ts @@ -1,5 +1,3 @@ -import { transformReplyNumberInfinityNull } from './generic-transformers'; - export const FIRST_KEY_INDEX = 1; export const IS_READ_ONLY = true; @@ -8,4 +6,4 @@ export function transformArguments(key: string, member: string): Array { return ['ZSCORE', key, member]; } -export const transformReply = transformReplyNumberInfinityNull; +export { transformReplyNumberInfinityNull as transformReply } from './generic-transformers'; diff --git a/lib/commands/ZUNION.spec.ts b/packages/client/lib/commands/ZUNION.spec.ts similarity index 83% rename from lib/commands/ZUNION.spec.ts rename to packages/client/lib/commands/ZUNION.spec.ts index 12e92833931..c53498cbf65 100644 --- a/lib/commands/ZUNION.spec.ts +++ b/packages/client/lib/commands/ZUNION.spec.ts @@ -1,9 +1,9 @@ import { strict as assert } from 'assert'; -import { TestRedisServers, itWithClient, describeHandleMinimumRedisVersion } from '../test-utils'; +import testUtils, { GLOBAL } from '../test-utils'; import { transformArguments } from './ZUNION'; describe('ZUNION', () => { - describeHandleMinimumRedisVersion([6, 2]); + testUtils.isVersionGreaterThanHook([6, 2]); describe('transformArguments', () => { it('key (string)', () => { @@ -39,10 +39,10 @@ describe('ZUNION', () => { }); }); - itWithClient(TestRedisServers.OPEN, 'client.zUnion', async client => { + testUtils.testWithClient('client.zUnion', async client => { assert.deepEqual( await client.zUnion('key'), [] ); - }); + }, GLOBAL.SERVERS.OPEN); }); diff --git a/lib/commands/ZUNION.ts b/packages/client/lib/commands/ZUNION.ts similarity index 100% rename from lib/commands/ZUNION.ts rename to packages/client/lib/commands/ZUNION.ts diff --git a/lib/commands/ZUNIONSTORE.spec.ts b/packages/client/lib/commands/ZUNIONSTORE.spec.ts similarity index 91% rename from lib/commands/ZUNIONSTORE.spec.ts rename to packages/client/lib/commands/ZUNIONSTORE.spec.ts index 0c4d7a3006b..8f11828b221 100644 --- a/lib/commands/ZUNIONSTORE.spec.ts +++ b/packages/client/lib/commands/ZUNIONSTORE.spec.ts @@ -1,5 +1,5 @@ import { strict as assert } from 'assert'; -import { TestRedisServers, itWithClient } from '../test-utils'; +import testUtils, { GLOBAL } from '../test-utils'; import { transformArguments } from './ZUNIONSTORE'; describe('ZUNIONSTORE', () => { @@ -47,10 +47,10 @@ describe('ZUNIONSTORE', () => { }); }); - itWithClient(TestRedisServers.OPEN, 'client.zUnionStore', async client => { + testUtils.testWithClient('client.zUnionStore', async client => { assert.equal( await client.zUnionStore('destination', 'key'), 0 ); - }); + }, GLOBAL.SERVERS.OPEN); }); diff --git a/lib/commands/ZUNIONSTORE.ts b/packages/client/lib/commands/ZUNIONSTORE.ts similarity index 100% rename from lib/commands/ZUNIONSTORE.ts rename to packages/client/lib/commands/ZUNIONSTORE.ts diff --git a/lib/commands/ZUNION_WITHSCORES.spec.ts b/packages/client/lib/commands/ZUNION_WITHSCORES.spec.ts similarity index 83% rename from lib/commands/ZUNION_WITHSCORES.spec.ts rename to packages/client/lib/commands/ZUNION_WITHSCORES.spec.ts index d9c65ba5e4b..3786a97963d 100644 --- a/lib/commands/ZUNION_WITHSCORES.spec.ts +++ b/packages/client/lib/commands/ZUNION_WITHSCORES.spec.ts @@ -1,9 +1,9 @@ import { strict as assert } from 'assert'; -import { TestRedisServers, itWithClient, describeHandleMinimumRedisVersion } from '../test-utils'; +import testUtils, { GLOBAL } from '../test-utils'; import { transformArguments } from './ZUNION_WITHSCORES'; describe('ZUNION WITHSCORES', () => { - describeHandleMinimumRedisVersion([6, 2]); + testUtils.isVersionGreaterThanHook([6, 2]); describe('transformArguments', () => { it('key (string)', () => { @@ -39,10 +39,10 @@ describe('ZUNION WITHSCORES', () => { }); }); - itWithClient(TestRedisServers.OPEN, 'client.zUnionWithScores', async client => { + testUtils.testWithClient('client.zUnionWithScores', async client => { assert.deepEqual( await client.zUnionWithScores('key'), [] ); - }); + }, GLOBAL.SERVERS.OPEN); }); diff --git a/lib/commands/ZUNION_WITHSCORES.ts b/packages/client/lib/commands/ZUNION_WITHSCORES.ts similarity index 73% rename from lib/commands/ZUNION_WITHSCORES.ts rename to packages/client/lib/commands/ZUNION_WITHSCORES.ts index d361fd432b0..3b937341921 100644 --- a/lib/commands/ZUNION_WITHSCORES.ts +++ b/packages/client/lib/commands/ZUNION_WITHSCORES.ts @@ -1,5 +1,4 @@ import { RedisCommandArguments } from '.'; -import { transformReplySortedSetWithScores } from './generic-transformers'; import { transformArguments as transformZUnionArguments } from './ZUNION'; export { FIRST_KEY_INDEX, IS_READ_ONLY } from './ZUNION'; @@ -11,4 +10,4 @@ export function transformArguments(...args: Parameters { }); }); + describe('transformArgumentStringNumberInfinity', () => { + it("'0.5'", () => { + assert.equal( + transformArgumentStringNumberInfinity('0.5'), + '0.5' + ); + }); + + it('0.5', () => { + assert.equal( + transformArgumentStringNumberInfinity(0.5), + '0.5' + ); + }); + }); + it('transformReplyTuples', () => { assert.deepEqual( transformReplyTuples(['key1', 'value1', 'key2', 'value2']), @@ -268,7 +285,7 @@ describe('Generic Transformers', () => { }) }] }] - ) + ); }); }); diff --git a/lib/commands/generic-transformers.ts b/packages/client/lib/commands/generic-transformers.ts similarity index 98% rename from lib/commands/generic-transformers.ts rename to packages/client/lib/commands/generic-transformers.ts index a531e86b432..3075636e756 100644 --- a/lib/commands/generic-transformers.ts +++ b/packages/client/lib/commands/generic-transformers.ts @@ -69,6 +69,12 @@ export function transformArgumentNumberInfinity(num: number): string { } } +export function transformArgumentStringNumberInfinity(num: string | number): string { + if (typeof num === 'string') return num; + + return transformArgumentNumberInfinity(num); +} + export interface TuplesObject { [field: string]: string; } diff --git a/lib/commands/index.ts b/packages/client/lib/commands/index.ts similarity index 100% rename from lib/commands/index.ts rename to packages/client/lib/commands/index.ts diff --git a/lib/errors.ts b/packages/client/lib/errors.ts similarity index 65% rename from lib/errors.ts rename to packages/client/lib/errors.ts index 86a65587cf5..79a438aa1d8 100644 --- a/lib/errors.ts +++ b/packages/client/lib/errors.ts @@ -21,3 +21,15 @@ export class ClientClosedError extends Error { super('The client is closed'); } } + +export class DisconnectsClientError extends Error { + constructor() { + super('Disconnects client'); + } +} + +export class SocketClosedUnexpectedlyError extends Error { + constructor() { + super('Socket closed unexpectedly'); + } +} diff --git a/lib/lua-script.ts b/packages/client/lib/lua-script.ts similarity index 100% rename from lib/lua-script.ts rename to packages/client/lib/lua-script.ts diff --git a/lib/multi-command.spec.ts b/packages/client/lib/multi-command.spec.ts similarity index 100% rename from lib/multi-command.spec.ts rename to packages/client/lib/multi-command.spec.ts diff --git a/lib/multi-command.ts b/packages/client/lib/multi-command.ts similarity index 100% rename from lib/multi-command.ts rename to packages/client/lib/multi-command.ts diff --git a/packages/client/lib/test-utils.ts b/packages/client/lib/test-utils.ts new file mode 100644 index 00000000000..321a9da63d5 --- /dev/null +++ b/packages/client/lib/test-utils.ts @@ -0,0 +1,49 @@ +import TestUtils from '@node-redis/test-utils'; +import { SinonSpy } from 'sinon'; +import { promiseTimeout } from './utils'; + +export default new TestUtils({ + defaultDockerVersion: '6.2', + dockerImageName: 'redis', + dockerImageVersionArgument: 'redis-version' +}); + +export const GLOBAL = { + SERVERS: { + OPEN: { + serverArguments: [] + }, + PASSWORD: { + serverArguments: ['--requirepass', 'password'], + clientOptions: { + password: 'password' + } + } + }, + CLUSTERS: { + OPEN: { + serverArguments: [] + }, + PASSWORD: { + serverArguments: ['--requirepass', 'password'], + clusterConfiguration: { + defaults: { + password: 'password' + } + } + } + } +}; + +export async function waitTillBeenCalled(spy: SinonSpy): Promise { + const start = process.hrtime.bigint(), + calls = spy.callCount; + + do { + if (process.hrtime.bigint() - start > 1_000_000_000) { + throw new Error('Waiting for more than 1 second'); + } + + await promiseTimeout(1); + } while (spy.callCount === calls); +} diff --git a/lib/ts-declarations/cluster-key-slot.d.ts b/packages/client/lib/ts-declarations/cluster-key-slot.d.ts similarity index 100% rename from lib/ts-declarations/cluster-key-slot.d.ts rename to packages/client/lib/ts-declarations/cluster-key-slot.d.ts diff --git a/lib/utils.ts b/packages/client/lib/utils.ts similarity index 100% rename from lib/utils.ts rename to packages/client/lib/utils.ts diff --git a/packages/client/package.json b/packages/client/package.json new file mode 100644 index 00000000000..7a6d23f5ff9 --- /dev/null +++ b/packages/client/package.json @@ -0,0 +1,50 @@ +{ + "name": "@node-redis/client", + "version": "1.0.0-rc.0", + "license": "MIT", + "main": "./dist/index.js", + "types": "./dist/index.d.ts", + "scripts": { + "test": "nyc -r text-summary -r lcov mocha -r source-map-support/register -r ts-node/register './lib/**/*.spec.ts'", + "build": "tsc", + "lint": "eslint ./*.ts ./lib/**/*.ts", + "documentation": "typedoc" + }, + "dependencies": { + "cluster-key-slot": "1.1.0", + "generic-pool": "3.8.2", + "redis-parser": "3.0.0", + "yallist": "4.0.0" + }, + "devDependencies": { + "@istanbuljs/nyc-config-typescript": "^1.0.1", + "@node-redis/test-utils": "*", + "@types/node": "^16.11.7", + "@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", + "nyc": "^15.1.0", + "release-it": "^14.11.7", + "sinon": "^12.0.1", + "source-map-support": "^0.5.20", + "ts-node": "^10.4.0", + "typedoc": "^0.22.9", + "typedoc-github-wiki-theme": "^0.6.0", + "typedoc-plugin-markdown": "^3.11.6", + "typescript": "^4.4.4" + }, + "engines": { + "node": ">=12" + }, + "repository": { + "type": "git", + "url": "git://github.com/redis/node-redis.git" + }, + "bugs": { + "url": "https://github.com/redis/node-redis/issues" + }, + "homepage": "https://github.com/redis/node-redis/tree/master/packages/client" +} diff --git a/packages/client/tsconfig.json b/packages/client/tsconfig.json new file mode 100644 index 00000000000..3280af594ef --- /dev/null +++ b/packages/client/tsconfig.json @@ -0,0 +1,27 @@ +{ + "extends": "../../tsconfig.base.json", + "compilerOptions": { + "outDir": "./dist" + }, + "include": [ + "./index.ts", + "./lib/**/*.ts" + ], + "exclude": [ + "./lib/test-utils.ts", + "./lib/**/*.spec.ts" + ], + "typedocOptions": { + "entryPoints": [ + "./index.ts", + "./lib" + ], + "entryPointStrategy": "expand", + "exclude": [ + "./lib/ts-declarations", + "./lib/test-utils.ts" + ], + "theme": "./node_modules/typedoc-github-wiki-theme/dist", + "out": "documentation" + } +} diff --git a/packages/json/.npmignore b/packages/json/.npmignore new file mode 100644 index 00000000000..bbef2b404fb --- /dev/null +++ b/packages/json/.npmignore @@ -0,0 +1,6 @@ +.nyc_output/ +coverage/ +lib/ +.nycrc.json +.release-it.json +tsconfig.json diff --git a/.nycrc.json b/packages/json/.nycrc.json similarity index 98% rename from .nycrc.json rename to packages/json/.nycrc.json index 925d954248c..b4e671e178f 100644 --- a/.nycrc.json +++ b/packages/json/.nycrc.json @@ -1,4 +1,4 @@ { "extends": "@istanbuljs/nyc-config-typescript", "exclude": ["**/*.spec.ts", "lib/test-utils.ts"] -} \ No newline at end of file +} diff --git a/packages/json/.release-it.json b/packages/json/.release-it.json new file mode 100644 index 00000000000..ab495a49b13 --- /dev/null +++ b/packages/json/.release-it.json @@ -0,0 +1,10 @@ +{ + "git": { + "tagName": "json@${version}", + "commitMessage": "Release ${tagName}", + "tagAnnotation": "Release ${tagName}" + }, + "npm": { + "publishArgs": ["--access", "public"] + } +} diff --git a/packages/json/README.md b/packages/json/README.md new file mode 100644 index 00000000000..1cd599d5ea8 --- /dev/null +++ b/packages/json/README.md @@ -0,0 +1,2 @@ +# @node-redis/json +The sources and docs for this package are in the main [node-redis](https://github.com/redis/node-redis) repo. diff --git a/packages/json/lib/commands/ARRAPPEND.spec.ts b/packages/json/lib/commands/ARRAPPEND.spec.ts new file mode 100644 index 00000000000..ab53837a000 --- /dev/null +++ b/packages/json/lib/commands/ARRAPPEND.spec.ts @@ -0,0 +1,30 @@ +import { strict as assert } from 'assert'; +import testUtils, { GLOBAL } from '../test-utils'; +import { transformArguments } from './ARRAPPEND'; + +describe('ARRAPPEND', () => { + describe('transformArguments', () => { + it('single JSON', () => { + assert.deepEqual( + transformArguments('key', '$', 1), + ['JSON.ARRAPPEND', 'key', '$', '1'] + ); + }); + + it('multiple JSONs', () => { + assert.deepEqual( + transformArguments('key', '$', 1, 2), + ['JSON.ARRAPPEND', 'key', '$', '1', '2'] + ); + }); + }); + + testUtils.testWithClient('client.json.arrAppend', async client => { + await client.json.set('key', '$', []); + + assert.deepEqual( + await client.json.arrAppend('key', '$', 1), + [1] + ); + }, GLOBAL.SERVERS.OPEN); +}); diff --git a/packages/json/lib/commands/ARRAPPEND.ts b/packages/json/lib/commands/ARRAPPEND.ts new file mode 100644 index 00000000000..2935d192996 --- /dev/null +++ b/packages/json/lib/commands/ARRAPPEND.ts @@ -0,0 +1,15 @@ +import { RedisJSON, transformRedisJsonArgument } from '.'; + +export const FIRST_KEY_INDEX = 1; + +export function transformArguments(key: string, path: string, ...jsons: Array): Array { + const args = ['JSON.ARRAPPEND', key, path]; + + for (const json of jsons) { + args.push(transformRedisJsonArgument(json)); + } + + return args; +} + +export declare function transformReply(): number | Array; diff --git a/packages/json/lib/commands/ARRINDEX.spec.ts b/packages/json/lib/commands/ARRINDEX.spec.ts new file mode 100644 index 00000000000..7a47d67126a --- /dev/null +++ b/packages/json/lib/commands/ARRINDEX.spec.ts @@ -0,0 +1,37 @@ +import { strict as assert } from 'assert'; +import testUtils, { GLOBAL } from '../test-utils'; +import { transformArguments } from './ARRINDEX'; + +describe('ARRINDEX', () => { + describe('transformArguments', () => { + it('simple', () => { + assert.deepEqual( + transformArguments('key', '$', 'json'), + ['JSON.ARRINDEX', 'key', '$', '"json"'] + ); + }); + + it('with start', () => { + assert.deepEqual( + transformArguments('key', '$', 'json', 1), + ['JSON.ARRINDEX', 'key', '$', '"json"', '1'] + ); + }); + + it('with start, end', () => { + assert.deepEqual( + transformArguments('key', '$', 'json', 1, 2), + ['JSON.ARRINDEX', 'key', '$', '"json"', '1', '2'] + ); + }); + }); + + testUtils.testWithClient('client.json.arrIndex', async client => { + await client.json.set('key', '$', []); + + assert.deepEqual( + await client.json.arrIndex('key', '$', 'json'), + [-1] + ); + }, GLOBAL.SERVERS.OPEN); +}); diff --git a/packages/json/lib/commands/ARRINDEX.ts b/packages/json/lib/commands/ARRINDEX.ts new file mode 100644 index 00000000000..5860b59cb3c --- /dev/null +++ b/packages/json/lib/commands/ARRINDEX.ts @@ -0,0 +1,21 @@ +import { RedisJSON, transformRedisJsonArgument } from '.'; + +export const FIRST_KEY_INDEX = 1; + +export const IS_READ_ONLY = true; + +export function transformArguments(key: string, path: string, json: RedisJSON, start?: number, stop?: number): Array { + const args = ['JSON.ARRINDEX', key, path, transformRedisJsonArgument(json)]; + + if (start !== undefined && start !== null) { + args.push(start.toString()); + + if (stop !== undefined && stop !== null) { + args.push(stop.toString()); + } + } + + return args; +} + +export declare function transformReply(): number | Array; diff --git a/packages/json/lib/commands/ARRINSERT.spec.ts b/packages/json/lib/commands/ARRINSERT.spec.ts new file mode 100644 index 00000000000..4b9d58b2cac --- /dev/null +++ b/packages/json/lib/commands/ARRINSERT.spec.ts @@ -0,0 +1,30 @@ +import { strict as assert } from 'assert'; +import testUtils, { GLOBAL } from '../test-utils'; +import { transformArguments } from './ARRINSERT'; + +describe('ARRINSERT', () => { + describe('transformArguments', () => { + it('single JSON', () => { + assert.deepEqual( + transformArguments('key', '$', 0, 'json'), + ['JSON.ARRINSERT', 'key', '$', '0', '"json"'] + ); + }); + + it('multiple JSONs', () => { + assert.deepEqual( + transformArguments('key', '$', 0, '1', '2'), + ['JSON.ARRINSERT', 'key', '$', '0', '"1"', '"2"'] + ); + }); + }); + + testUtils.testWithClient('client.json.arrInsert', async client => { + await client.json.set('key', '$', []); + + assert.deepEqual( + await client.json.arrInsert('key', '$', 0, 'json'), + [1] + ); + }, GLOBAL.SERVERS.OPEN); +}); diff --git a/packages/json/lib/commands/ARRINSERT.ts b/packages/json/lib/commands/ARRINSERT.ts new file mode 100644 index 00000000000..85857657019 --- /dev/null +++ b/packages/json/lib/commands/ARRINSERT.ts @@ -0,0 +1,15 @@ +import { RedisJSON, transformRedisJsonArgument } from '.'; + +export const FIRST_KEY_INDEX = 1; + +export function transformArguments(key: string, path: string, index: number, ...jsons: Array): Array { + const args = ['JSON.ARRINSERT', key, path, index.toString()]; + + for (const json of jsons) { + args.push(transformRedisJsonArgument(json)); + } + + return args; +} + +export declare function transformReply(): number | Array; diff --git a/packages/json/lib/commands/ARRLEN.spec.ts b/packages/json/lib/commands/ARRLEN.spec.ts new file mode 100644 index 00000000000..f0a3ec40a4c --- /dev/null +++ b/packages/json/lib/commands/ARRLEN.spec.ts @@ -0,0 +1,30 @@ +import { strict as assert } from 'assert'; +import testUtils, { GLOBAL } from '../test-utils'; +import { transformArguments } from './ARRLEN'; + +describe('ARRLEN', () => { + describe('transformArguments', () => { + it('without path', () => { + assert.deepEqual( + transformArguments('key'), + ['JSON.ARRLEN', 'key'] + ); + }); + + it('with path', () => { + assert.deepEqual( + transformArguments('key', '$'), + ['JSON.ARRLEN', 'key', '$'] + ); + }); + }); + + testUtils.testWithClient('client.json.arrLen', async client => { + await client.json.set('key', '$', []); + + assert.deepEqual( + await client.json.arrLen('key', '$'), + [0] + ); + }, GLOBAL.SERVERS.OPEN); +}); diff --git a/packages/json/lib/commands/ARRLEN.ts b/packages/json/lib/commands/ARRLEN.ts new file mode 100644 index 00000000000..818397b7f8d --- /dev/null +++ b/packages/json/lib/commands/ARRLEN.ts @@ -0,0 +1,15 @@ +export const FIRST_KEY_INDEX = 1; + +export const IS_READ_ONLY = true; + +export function transformArguments(key: string, path?: string): Array { + const args = ['JSON.ARRLEN', key]; + + if (path) { + args.push(path); + } + + return args; +} + +export declare function transformReply(): number | Array; diff --git a/packages/json/lib/commands/ARRPOP.spec.ts b/packages/json/lib/commands/ARRPOP.spec.ts new file mode 100644 index 00000000000..a80b8c3cbc5 --- /dev/null +++ b/packages/json/lib/commands/ARRPOP.spec.ts @@ -0,0 +1,37 @@ +import { strict as assert } from 'assert'; +import testUtils, { GLOBAL } from '../test-utils'; +import { transformArguments } from './ARRPOP'; + +describe('ARRPOP', () => { + describe('transformArguments', () => { + it('key', () => { + assert.deepEqual( + transformArguments('key'), + ['JSON.ARRPOP', 'key'] + ); + }); + + it('key, path', () => { + assert.deepEqual( + transformArguments('key', '$'), + ['JSON.ARRPOP', 'key', '$'] + ); + }); + + it('key, path, index', () => { + assert.deepEqual( + transformArguments('key', '$', 0), + ['JSON.ARRPOP', 'key', '$', '0'] + ); + }); + }); + + testUtils.testWithClient('client.json.arrPop', async client => { + await client.json.set('key', '$', []); + + assert.deepEqual( + await client.json.arrPop('key', '$'), + [null] + ); + }, GLOBAL.SERVERS.OPEN); +}); diff --git a/packages/json/lib/commands/ARRPOP.ts b/packages/json/lib/commands/ARRPOP.ts new file mode 100644 index 00000000000..5d8785a8d94 --- /dev/null +++ b/packages/json/lib/commands/ARRPOP.ts @@ -0,0 +1,17 @@ +export const FIRST_KEY_INDEX = 1; + +export function transformArguments(key: string, path?: string, index?: number): Array { + const args = ['JSON.ARRPOP', key]; + + if (path) { + args.push(path); + + if (index !== undefined && index !== null) { + args.push(index.toString()); + } + } + + return args; +} + +export { transformRedisJsonNullArrayReply as transformReply } from '.'; diff --git a/packages/json/lib/commands/ARRTRIM.spec.ts b/packages/json/lib/commands/ARRTRIM.spec.ts new file mode 100644 index 00000000000..c254e1b6a03 --- /dev/null +++ b/packages/json/lib/commands/ARRTRIM.spec.ts @@ -0,0 +1,21 @@ +import { strict as assert } from 'assert'; +import testUtils, { GLOBAL } from '../test-utils'; +import { transformArguments } from './ARRTRIM'; + +describe('ARRTRIM', () => { + it('transformArguments', () => { + assert.deepEqual( + transformArguments('key', '$', 0, 1), + ['JSON.ARRTRIM', 'key', '$', '0', '1'] + ); + }); + + testUtils.testWithClient('client.json.arrTrim', async client => { + await client.json.set('key', '$', []); + + assert.deepEqual( + await client.json.arrTrim('key', '$', 0, 1), + [0] + ); + }, GLOBAL.SERVERS.OPEN); +}); diff --git a/packages/json/lib/commands/ARRTRIM.ts b/packages/json/lib/commands/ARRTRIM.ts new file mode 100644 index 00000000000..2de444eeebd --- /dev/null +++ b/packages/json/lib/commands/ARRTRIM.ts @@ -0,0 +1,7 @@ +export const FIRST_KEY_INDEX = 1; + +export function transformArguments(key: string, path: string, start: number, stop: number): Array { + return ['JSON.ARRTRIM', key, path, start.toString(), stop.toString()]; +} + +export declare function transformReply(): number | Array; diff --git a/packages/json/lib/commands/DEBUG_MEMORY.spec.ts b/packages/json/lib/commands/DEBUG_MEMORY.spec.ts new file mode 100644 index 00000000000..468c994f2f5 --- /dev/null +++ b/packages/json/lib/commands/DEBUG_MEMORY.spec.ts @@ -0,0 +1,28 @@ +import { strict as assert } from 'assert'; +import testUtils, { GLOBAL } from '../test-utils'; +import { transformArguments } from './DEBUG_MEMORY'; + +describe('DEBUG MEMORY', () => { + describe('transformArguments', () => { + it('without path', () => { + assert.deepEqual( + transformArguments('key'), + ['JSON.DEBUG', 'MEMORY', 'key'] + ); + }); + + it('with path', () => { + assert.deepEqual( + transformArguments('key', '$'), + ['JSON.DEBUG', 'MEMORY', 'key', '$'] + ); + }); + }); + + testUtils.testWithClient('client.json.arrTrim', async client => { + assert.deepEqual( + await client.json.debugMemory('key', '$'), + [] + ); + }, GLOBAL.SERVERS.OPEN); +}); diff --git a/packages/json/lib/commands/DEBUG_MEMORY.ts b/packages/json/lib/commands/DEBUG_MEMORY.ts new file mode 100644 index 00000000000..da60b1d9529 --- /dev/null +++ b/packages/json/lib/commands/DEBUG_MEMORY.ts @@ -0,0 +1,13 @@ +export const FIRST_KEY_INDEX = 2; + +export function transformArguments(key: string, path?: string): Array { + const args = ['JSON.DEBUG', 'MEMORY', key]; + + if (path) { + args.push(path); + } + + return args; +} + +export declare function transformReply(): number; diff --git a/packages/json/lib/commands/DEL.spec.ts b/packages/json/lib/commands/DEL.spec.ts new file mode 100644 index 00000000000..a957b9584ac --- /dev/null +++ b/packages/json/lib/commands/DEL.spec.ts @@ -0,0 +1,28 @@ +import { strict as assert } from 'assert'; +import testUtils, { GLOBAL } from '../test-utils'; +import { transformArguments } from './DEL'; + +describe('DEL', () => { + describe('transformArguments', () => { + it('key', () => { + assert.deepEqual( + transformArguments('key'), + ['JSON.DEL', 'key'] + ); + }); + + it('key, path', () => { + assert.deepEqual( + transformArguments('key', '$.path'), + ['JSON.DEL', 'key', '$.path'] + ); + }); + }); + + testUtils.testWithClient('client.json.del', async client => { + assert.deepEqual( + await client.json.del('key'), + 0 + ); + }, GLOBAL.SERVERS.OPEN); +}); diff --git a/packages/json/lib/commands/DEL.ts b/packages/json/lib/commands/DEL.ts new file mode 100644 index 00000000000..090d4dbe853 --- /dev/null +++ b/packages/json/lib/commands/DEL.ts @@ -0,0 +1,13 @@ +export const FIRST_KEY_INDEX = 1; + +export function transformArguments(key: string, path?: string): Array { + const args = ['JSON.DEL', key]; + + if (path) { + args.push(path); + } + + return args; +} + +export declare function transformReply(): number; diff --git a/packages/json/lib/commands/FORGET.spec.ts b/packages/json/lib/commands/FORGET.spec.ts new file mode 100644 index 00000000000..923bb997fc8 --- /dev/null +++ b/packages/json/lib/commands/FORGET.spec.ts @@ -0,0 +1,28 @@ +import { strict as assert } from 'assert'; +import testUtils, { GLOBAL } from '../test-utils'; +import { transformArguments } from './FORGET'; + +describe('FORGET', () => { + describe('transformArguments', () => { + it('key', () => { + assert.deepEqual( + transformArguments('key'), + ['JSON.FORGET', 'key'] + ); + }); + + it('key, path', () => { + assert.deepEqual( + transformArguments('key', '$.path'), + ['JSON.FORGET', 'key', '$.path'] + ); + }); + }); + + testUtils.testWithClient('client.json.forget', async client => { + assert.deepEqual( + await client.json.forget('key'), + 0 + ); + }, GLOBAL.SERVERS.OPEN); +}); diff --git a/packages/json/lib/commands/FORGET.ts b/packages/json/lib/commands/FORGET.ts new file mode 100644 index 00000000000..cb2df3d605d --- /dev/null +++ b/packages/json/lib/commands/FORGET.ts @@ -0,0 +1,13 @@ +export const FIRST_KEY_INDEX = 1; + +export function transformArguments(key: string, path?: string): Array { + const args = ['JSON.FORGET', key]; + + if (path) { + args.push(path); + } + + return args; +} + +export declare function transformReply(): number; diff --git a/packages/json/lib/commands/GET.spec.ts b/packages/json/lib/commands/GET.spec.ts new file mode 100644 index 00000000000..ed831689a93 --- /dev/null +++ b/packages/json/lib/commands/GET.spec.ts @@ -0,0 +1,78 @@ +import { strict as assert } from 'assert'; +import testUtils, { GLOBAL } from '../test-utils'; +import { transformArguments } from './GET'; + +describe('GET', () => { + describe('transformArguments', () => { + describe('path', () => { + it('string', () => { + assert.deepEqual( + transformArguments('key', { path: '$' }), + ['JSON.GET', 'key', '$'] + ); + }); + + it('array', () => { + assert.deepEqual( + transformArguments('key', { path: ['$.1', '$.2'] }), + ['JSON.GET', 'key', '$.1', '$.2'] + ); + }); + }); + + it('key', () => { + assert.deepEqual( + transformArguments('key'), + ['JSON.GET', 'key'] + ); + }); + + it('INDENT', () => { + assert.deepEqual( + transformArguments('key', { INDENT: 'indent' }), + ['JSON.GET', 'key', 'INDENT', 'indent'] + ); + }); + + it('NEWLINE', () => { + assert.deepEqual( + transformArguments('key', { NEWLINE: 'newline' }), + ['JSON.GET', 'key', 'NEWLINE', 'newline'] + ); + }); + + it('SPACE', () => { + assert.deepEqual( + transformArguments('key', { SPACE: 'space' }), + ['JSON.GET', 'key', 'SPACE', 'space'] + ); + }); + + it('NOESCAPE', () => { + assert.deepEqual( + transformArguments('key', { NOESCAPE: true }), + ['JSON.GET', 'key', 'NOESCAPE'] + ); + }); + + it('INDENT, NEWLINE, SPACE, NOESCAPE, path', () => { + assert.deepEqual( + transformArguments('key', { + path: '$.path', + INDENT: 'indent', + NEWLINE: 'newline', + SPACE: 'space', + NOESCAPE: true + }), + ['JSON.GET', 'key', '$.path', 'INDENT', 'indent', 'NEWLINE', 'newline', 'SPACE', 'space', 'NOESCAPE'] + ); + }); + }); + + testUtils.testWithClient('client.json.get', async client => { + assert.equal( + await client.json.get('key'), + null + ); + }, GLOBAL.SERVERS.OPEN); +}); diff --git a/packages/json/lib/commands/GET.ts b/packages/json/lib/commands/GET.ts new file mode 100644 index 00000000000..8e897bb67d2 --- /dev/null +++ b/packages/json/lib/commands/GET.ts @@ -0,0 +1,41 @@ +import { pushVerdictArguments } from '@node-redis/client/dist/lib/commands/generic-transformers'; + +export const FIRST_KEY_INDEX = 1; + +export const IS_READ_ONLY = true; + +interface GetOptions { + path?: string | Array; + INDENT?: string; + NEWLINE?: string; + SPACE?: string; + NOESCAPE?: true; +} + +export function transformArguments(key: string, options?: GetOptions): Array { + const args = ['JSON.GET', key]; + + if (options?.path) { + pushVerdictArguments(args, options.path); + } + + if (options?.INDENT) { + args.push('INDENT', options.INDENT); + } + + if (options?.NEWLINE) { + args.push('NEWLINE', options.NEWLINE); + } + + if (options?.SPACE) { + args.push('SPACE', options.SPACE); + } + + if (options?.NOESCAPE) { + args.push('NOESCAPE'); + } + + return args; +} + +export { transformRedisJsonNullReply as transformReply } from '.'; diff --git a/packages/json/lib/commands/MGET.spec.ts b/packages/json/lib/commands/MGET.spec.ts new file mode 100644 index 00000000000..456e160dd50 --- /dev/null +++ b/packages/json/lib/commands/MGET.spec.ts @@ -0,0 +1,19 @@ +import { strict as assert } from 'assert'; +import testUtils, { GLOBAL } from '../test-utils'; +import { transformArguments } from './MGET'; + +describe('MGET', () => { + it('transformArguments', () => { + assert.deepEqual( + transformArguments(['1', '2'], '$'), + ['JSON.MGET', '1', '2', '$'] + ); + }); + + testUtils.testWithClient('client.json.mGet', async client => { + assert.deepEqual( + await client.json.mGet(['1', '2'], '$'), + [null, null] + ); + }, GLOBAL.SERVERS.OPEN); +}); diff --git a/packages/json/lib/commands/MGET.ts b/packages/json/lib/commands/MGET.ts new file mode 100644 index 00000000000..582b73bf85a --- /dev/null +++ b/packages/json/lib/commands/MGET.ts @@ -0,0 +1,15 @@ +import { RedisJSON, transformRedisJsonNullReply } from '.'; + +export const FIRST_KEY_INDEX = 1; + +export function transformArguments(keys: Array, path: string): Array { + return [ + 'JSON.MGET', + ...keys, + path + ]; +} + +export function transformReply(reply: Array): Array { + return reply.map(transformRedisJsonNullReply); +} diff --git a/packages/json/lib/commands/NUMINCRBY.spec.ts b/packages/json/lib/commands/NUMINCRBY.spec.ts new file mode 100644 index 00000000000..56dede68bde --- /dev/null +++ b/packages/json/lib/commands/NUMINCRBY.spec.ts @@ -0,0 +1,21 @@ +import { strict as assert } from 'assert'; +import testUtils, { GLOBAL } from '../test-utils'; +import { transformArguments } from './NUMINCRBY'; + +describe('NUMINCRBY', () => { + it('transformArguments', () => { + assert.deepEqual( + transformArguments('key', '$', 1), + ['JSON.NUMINCRBY', 'key', '$', '1'] + ); + }); + + testUtils.testWithClient('client.json.numIncrBy', async client => { + await client.json.set('key', '$', 0); + + assert.deepEqual( + await client.json.numIncrBy('key', '$', 1), + [1] + ); + }, GLOBAL.SERVERS.OPEN); +}); diff --git a/packages/json/lib/commands/NUMINCRBY.ts b/packages/json/lib/commands/NUMINCRBY.ts new file mode 100644 index 00000000000..e3d8887ea3d --- /dev/null +++ b/packages/json/lib/commands/NUMINCRBY.ts @@ -0,0 +1,7 @@ +export const FIRST_KEY_INDEX = 1; + +export function transformArguments(key: string, path: string, by: number): Array { + return ['JSON.NUMINCRBY', key, path, by.toString()]; +} + +export { transformNumbersReply as transformReply } from '.'; diff --git a/packages/json/lib/commands/NUMMULTBY.spec.ts b/packages/json/lib/commands/NUMMULTBY.spec.ts new file mode 100644 index 00000000000..3e2581a3cd8 --- /dev/null +++ b/packages/json/lib/commands/NUMMULTBY.spec.ts @@ -0,0 +1,21 @@ +import { strict as assert } from 'assert'; +import testUtils, { GLOBAL } from '../test-utils'; +import { transformArguments } from './NUMMULTBY'; + +describe('NUMMULTBY', () => { + it('transformArguments', () => { + assert.deepEqual( + transformArguments('key', '$', 2), + ['JSON.NUMMULTBY', 'key', '$', '2'] + ); + }); + + testUtils.testWithClient('client.json.numMultBy', async client => { + await client.json.set('key', '$', 1); + + assert.deepEqual( + await client.json.numMultBy('key', '$', 2), + [2] + ); + }, GLOBAL.SERVERS.OPEN); +}); diff --git a/packages/json/lib/commands/NUMMULTBY.ts b/packages/json/lib/commands/NUMMULTBY.ts new file mode 100644 index 00000000000..2082916619a --- /dev/null +++ b/packages/json/lib/commands/NUMMULTBY.ts @@ -0,0 +1,7 @@ +export const FIRST_KEY_INDEX = 1; + +export function transformArguments(key: string, path: string, by: number): Array { + return ['JSON.NUMMULTBY', key, path, by.toString()]; +} + +export { transformNumbersReply as transformReply } from '.'; diff --git a/packages/json/lib/commands/OBJKEYS.spec.ts b/packages/json/lib/commands/OBJKEYS.spec.ts new file mode 100644 index 00000000000..6288c112392 --- /dev/null +++ b/packages/json/lib/commands/OBJKEYS.spec.ts @@ -0,0 +1,28 @@ +import { strict as assert } from 'assert'; +import testUtils, { GLOBAL } from '../test-utils'; +import { transformArguments } from './OBJKEYS'; + +describe('OBJKEYS', () => { + describe('transformArguments', () => { + it('without path', () => { + assert.deepEqual( + transformArguments('key'), + ['JSON.OBJKEYS', 'key'] + ); + }); + + it('with path', () => { + assert.deepEqual( + transformArguments('key', '$'), + ['JSON.OBJKEYS', 'key', '$'] + ); + }); + }); + + // testUtils.testWithClient('client.json.objKeys', async client => { + // assert.deepEqual( + // await client.json.objKeys('key', '$'), + // [null] + // ); + // }, GLOBAL.SERVERS.OPEN); +}); diff --git a/packages/json/lib/commands/OBJKEYS.ts b/packages/json/lib/commands/OBJKEYS.ts new file mode 100644 index 00000000000..a9465c9160c --- /dev/null +++ b/packages/json/lib/commands/OBJKEYS.ts @@ -0,0 +1,13 @@ +export const FIRST_KEY_INDEX = 1; + +export function transformArguments(key: string, path?: string): Array { + const args = ['JSON.OBJKEYS', key]; + + if (path) { + args.push(path); + } + + return args; +} + +export declare function transformReply(): Array | null | Array | null>; diff --git a/packages/json/lib/commands/OBJLEN.spec.ts b/packages/json/lib/commands/OBJLEN.spec.ts new file mode 100644 index 00000000000..35b6589c875 --- /dev/null +++ b/packages/json/lib/commands/OBJLEN.spec.ts @@ -0,0 +1,28 @@ +import { strict as assert } from 'assert'; +import testUtils, { GLOBAL } from '../test-utils'; +import { transformArguments } from './OBJLEN'; + +describe('OBJLEN', () => { + describe('transformArguments', () => { + it('without path', () => { + assert.deepEqual( + transformArguments('key'), + ['JSON.OBJLEN', 'key'] + ); + }); + + it('with path', () => { + assert.deepEqual( + transformArguments('key', '$'), + ['JSON.OBJLEN', 'key', '$'] + ); + }); + }); + + // testUtils.testWithClient('client.json.objLen', async client => { + // assert.equal( + // await client.json.objLen('key', '$'), + // [null] + // ); + // }, GLOBAL.SERVERS.OPEN); +}); diff --git a/packages/json/lib/commands/OBJLEN.ts b/packages/json/lib/commands/OBJLEN.ts new file mode 100644 index 00000000000..aa800e97f71 --- /dev/null +++ b/packages/json/lib/commands/OBJLEN.ts @@ -0,0 +1,13 @@ +export const FIRST_KEY_INDEX = 1; + +export function transformArguments(key: string, path?: string): Array { + const args = ['JSON.OBJLEN', key]; + + if (path) { + args.push(path); + } + + return args; +} + +export declare function transformReply(): number | null | Array; diff --git a/packages/json/lib/commands/RESP.spec.ts b/packages/json/lib/commands/RESP.spec.ts new file mode 100644 index 00000000000..8b70962d1c5 --- /dev/null +++ b/packages/json/lib/commands/RESP.spec.ts @@ -0,0 +1,28 @@ +import { strict as assert } from 'assert'; +import testUtils, { GLOBAL } from '../test-utils'; +import { transformArguments } from './RESP'; + +describe('RESP', () => { + describe('transformArguments', () => { + it('without path', () => { + assert.deepEqual( + transformArguments('key'), + ['JSON.RESP', 'key'] + ); + }); + + it('with path', () => { + assert.deepEqual( + transformArguments('key', '$'), + ['JSON.RESP', 'key', '$'] + ); + }); + }); + + // testUtils.testWithClient('client.json.resp', async client => { + // assert.deepEqual( + // await client.json.resp('key', '$'), + // [null] + // ); + // }, GLOBAL.SERVERS.OPEN); +}); diff --git a/packages/json/lib/commands/RESP.ts b/packages/json/lib/commands/RESP.ts new file mode 100644 index 00000000000..2b56bf1f059 --- /dev/null +++ b/packages/json/lib/commands/RESP.ts @@ -0,0 +1,15 @@ +export const FIRST_KEY_INDEX = 1; + +export function transformArguments(key: string, path?: string): Array { + const args = ['JSON.RESP', key]; + + if (path) { + args.push(path); + } + + return args; +} + +type RESPReply = Array; + +export declare function transfromReply(): RESPReply; diff --git a/packages/json/lib/commands/SET.spec.ts b/packages/json/lib/commands/SET.spec.ts new file mode 100644 index 00000000000..8f8586a2047 --- /dev/null +++ b/packages/json/lib/commands/SET.spec.ts @@ -0,0 +1,35 @@ +import { strict as assert } from 'assert'; +import testUtils, { GLOBAL } from '../test-utils'; +import { transformArguments } from './SET'; + +describe('SET', () => { + describe('transformArguments', () => { + it('transformArguments', () => { + assert.deepEqual( + transformArguments('key', '$', 'json'), + ['JSON.SET', 'key', '$', '"json"'] + ); + }); + + it('NX', () => { + assert.deepEqual( + transformArguments('key', '$', 'json', { NX: true }), + ['JSON.SET', 'key', '$', '"json"', 'NX'] + ); + }); + + it('XX', () => { + assert.deepEqual( + transformArguments('key', '$', 'json', { XX: true }), + ['JSON.SET', 'key', '$', '"json"', 'XX'] + ); + }); + }); + + testUtils.testWithClient('client.json.mGet', async client => { + assert.equal( + await client.json.set('key', '$', 'json'), + 'OK' + ); + }, GLOBAL.SERVERS.OPEN); +}); diff --git a/packages/json/lib/commands/SET.ts b/packages/json/lib/commands/SET.ts new file mode 100644 index 00000000000..f50a42bf5db --- /dev/null +++ b/packages/json/lib/commands/SET.ts @@ -0,0 +1,25 @@ +import { RedisJSON, transformRedisJsonArgument } from '.'; + +export const FIRST_KEY_INDEX = 1; + +interface NX { + NX: true; +} + +interface XX { + XX: true; +} + +export function transformArguments(key: string, path: string, json: RedisJSON, options?: NX | XX): Array { + const args = ['JSON.SET', key, path, transformRedisJsonArgument(json)]; + + if ((options)?.NX) { + args.push('NX'); + } else if ((options)?.XX) { + args.push('XX'); + } + + return args; +} + +export declare function transformReply(): 'OK' | null; diff --git a/packages/json/lib/commands/STRAPPEND.spec.ts b/packages/json/lib/commands/STRAPPEND.spec.ts new file mode 100644 index 00000000000..a37eaa1d91c --- /dev/null +++ b/packages/json/lib/commands/STRAPPEND.spec.ts @@ -0,0 +1,30 @@ +import { strict as assert } from 'assert'; +import testUtils, { GLOBAL } from '../test-utils'; +import { transformArguments } from './STRAPPEND'; + +describe('STRAPPEND', () => { + describe('transformArguments', () => { + it('without path', () => { + assert.deepEqual( + transformArguments('key', 'append'), + ['JSON.STRAPPEND', 'key', '"append"'] + ); + }); + + it('with path', () => { + assert.deepEqual( + transformArguments('key', '$', 'append'), + ['JSON.STRAPPEND', 'key', '$', '"append"'] + ); + }); + }); + + testUtils.testWithClient('client.json.strAppend', async client => { + await client.json.set('key', '$', ''); + + assert.deepEqual( + await client.json.strAppend('key', '$', 'append'), + [6] + ); + }, GLOBAL.SERVERS.OPEN); +}); diff --git a/packages/json/lib/commands/STRAPPEND.ts b/packages/json/lib/commands/STRAPPEND.ts new file mode 100644 index 00000000000..eea384c93fd --- /dev/null +++ b/packages/json/lib/commands/STRAPPEND.ts @@ -0,0 +1,21 @@ +import { transformRedisJsonArgument } from '.'; + +export const FIRST_KEY_INDEX = 1; + +type AppendArguments = [key: string, append: string]; + +type AppendWithPathArguments = [key: string, path: string, append: string]; + +export function transformArguments(...[key, pathOrAppend, append]: AppendArguments | AppendWithPathArguments): Array { + const args = ['JSON.STRAPPEND', key]; + + if (append !== undefined && append !== null) { + args.push(pathOrAppend, transformRedisJsonArgument(append)); + } else { + args.push(transformRedisJsonArgument(pathOrAppend)); + } + + return args; +} + +export declare function transformReply(): number | Array; diff --git a/packages/json/lib/commands/STRLEN.spec.ts b/packages/json/lib/commands/STRLEN.spec.ts new file mode 100644 index 00000000000..cf163d3c19e --- /dev/null +++ b/packages/json/lib/commands/STRLEN.spec.ts @@ -0,0 +1,30 @@ +import { strict as assert } from 'assert'; +import testUtils, { GLOBAL } from '../test-utils'; +import { transformArguments } from './STRLEN'; + +describe('STRLEN', () => { + describe('transformArguments', () => { + it('without path', () => { + assert.deepEqual( + transformArguments('key'), + ['JSON.STRLEN', 'key'] + ); + }); + + it('with path', () => { + assert.deepEqual( + transformArguments('key', '$'), + ['JSON.STRLEN', 'key', '$'] + ); + }); + }); + + testUtils.testWithClient('client.json.strLen', async client => { + await client.json.set('key', '$', ''); + + assert.deepEqual( + await client.json.strLen('key', '$'), + [0] + ); + }, GLOBAL.SERVERS.OPEN); +}); diff --git a/packages/json/lib/commands/STRLEN.ts b/packages/json/lib/commands/STRLEN.ts new file mode 100644 index 00000000000..93f5d563baf --- /dev/null +++ b/packages/json/lib/commands/STRLEN.ts @@ -0,0 +1,15 @@ +export const FIRST_KEY_INDEX = 1; + +export const IS_READ_ONLY = true; + +export function transformArguments(key: string, path?: string): Array { + const args = ['JSON.STRLEN', key]; + + if (path) { + args.push(path); + } + + return args; +} + +export declare function transformReply(): number; diff --git a/packages/json/lib/commands/TYPE.spec.ts b/packages/json/lib/commands/TYPE.spec.ts new file mode 100644 index 00000000000..5cecfb827a7 --- /dev/null +++ b/packages/json/lib/commands/TYPE.spec.ts @@ -0,0 +1,28 @@ +import { strict as assert } from 'assert'; +import testUtils, { GLOBAL } from '../test-utils'; +import { transformArguments } from './TYPE'; + +describe('TYPE', () => { + describe('transformArguments', () => { + it('without path', () => { + assert.deepEqual( + transformArguments('key'), + ['JSON.TYPE', 'key'] + ); + }); + + it('with path', () => { + assert.deepEqual( + transformArguments('key', '$'), + ['JSON.TYPE', 'key', '$'] + ); + }); + }); + + // testUtils.testWithClient('client.json.type', async client => { + // assert.deepEqual( + // await client.json.type('key', '$'), + // [null] + // ); + // }, GLOBAL.SERVERS.OPEN); +}); diff --git a/packages/json/lib/commands/TYPE.ts b/packages/json/lib/commands/TYPE.ts new file mode 100644 index 00000000000..7fd55f625dc --- /dev/null +++ b/packages/json/lib/commands/TYPE.ts @@ -0,0 +1,13 @@ +export const FIRST_KEY_INDEX = 1; + +export function transformArguments(key: string, path?: string): Array { + const args = ['JSON.TYPE', key]; + + if (path) { + args.push(path); + } + + return args; +} + +export declare function transformReply(): string | null | Array; diff --git a/packages/json/lib/commands/index.ts b/packages/json/lib/commands/index.ts new file mode 100644 index 00000000000..91b4f7dc4b5 --- /dev/null +++ b/packages/json/lib/commands/index.ts @@ -0,0 +1,94 @@ +import * as ARRAPPEND from './ARRAPPEND'; +import * as ARRINDEX from './ARRINDEX'; +import * as ARRINSERT from './ARRINSERT'; +import * as ARRLEN from './ARRLEN'; +import * as ARRPOP from './ARRPOP'; +import * as ARRTRIM from './ARRTRIM'; +import * as DEBUG_MEMORY from './DEBUG_MEMORY'; +import * as DEL from './DEL'; +import * as FORGET from './FORGET'; +import * as GET from './GET'; +import * as MGET from './MGET'; +import * as NUMINCRBY from './NUMINCRBY'; +import * as NUMMULTBY from './NUMMULTBY'; +import * as OBJKEYS from './OBJKEYS'; +import * as OBJLEN from './OBJLEN'; +import * as RESP from './RESP'; +import * as SET from './SET'; +import * as STRAPPEND from './STRAPPEND'; +import * as STRLEN from './STRLEN'; +import * as TYPE from './TYPE'; + +export default { + ARRAPPEND, + arrAppend: ARRAPPEND, + ARRINDEX, + arrIndex: ARRINDEX, + ARRINSERT, + arrInsert: ARRINSERT, + ARRLEN, + arrLen: ARRLEN, + ARRPOP, + arrPop: ARRPOP, + ARRTRIM, + arrTrim: ARRTRIM, + DEBUG_MEMORY, + debugMemory: DEBUG_MEMORY, + DEL, + del: DEL, + FORGET, + forget: FORGET, + GET, + get: GET, + MGET, + mGet: MGET, + NUMINCRBY, + numIncrBy: NUMINCRBY, + NUMMULTBY, + numMultBy: NUMMULTBY, + OBJKEYS, + objKeys: OBJKEYS, + OBJLEN, + objLen: OBJLEN, + RESP, + resp: RESP, + SET, + set: SET, + STRAPPEND, + strAppend: STRAPPEND, + STRLEN, + strLen: STRLEN, + TYPE, + type: TYPE +}; + +// using two "objects" and not `Record` cause of: +// https://github.com/microsoft/TypeScript/issues/14174 +export type RedisJSON = null | boolean | number | string | Date | Array | { [key: string]: RedisJSON } | { [key: number]: RedisJSON }; + +export function transformRedisJsonArgument(json: RedisJSON): string { + return JSON.stringify(json); +} + +export function transformRedisJsonReply(json: string): RedisJSON { + return JSON.parse(json); +} + +export function transformRedisJsonArrayReply(jsons: Array): Array { + return jsons.map(transformRedisJsonReply) +} + +export function transformRedisJsonNullReply(json: string | null): RedisJSON | null { + if (json === null) return null; + + return transformRedisJsonReply(json); +} + + +export function transformRedisJsonNullArrayReply(jsons: Array): Array { + return jsons.map(transformRedisJsonNullReply); +} + +export function transformNumbersReply(reply: string): number | Array { + return JSON.parse(reply); +} diff --git a/packages/json/lib/index.ts b/packages/json/lib/index.ts new file mode 100644 index 00000000000..bc0e103e8c8 --- /dev/null +++ b/packages/json/lib/index.ts @@ -0,0 +1 @@ +export { default } from './commands'; diff --git a/packages/json/lib/test-utils.ts b/packages/json/lib/test-utils.ts new file mode 100644 index 00000000000..eeee3c77c9d --- /dev/null +++ b/packages/json/lib/test-utils.ts @@ -0,0 +1,21 @@ +import TestUtils from '@node-redis/test-utils'; +import RedisJSON from '.'; + +export default new TestUtils({ + dockerImageName: 'redislabs/rejson', + dockerImageVersionArgument: 'rejson-version', + defaultDockerVersion: '2.0.2' +}); + +export const GLOBAL = { + SERVERS: { + OPEN: { + serverArguments: ['--loadmodule /usr/lib/redis/modules/rejson.so'], + clientOptions: { + modules: { + json: RedisJSON + } + } + } + } +}; diff --git a/packages/json/package.json b/packages/json/package.json new file mode 100644 index 00000000000..7e5f6e10c1f --- /dev/null +++ b/packages/json/package.json @@ -0,0 +1,24 @@ +{ + "name": "@node-redis/json", + "version": "1.0.0-rc.0", + "license": "MIT", + "main": "./dist/index.js", + "types": "./dist/index.d.ts", + "scripts": { + "test": "nyc -r text-summary -r lcov mocha -r source-map-support/register -r ts-node/register './lib/**/*.spec.ts'", + "build": "tsc" + }, + "peerDependencies": { + "@node-redis/client": "^1.0.0-rc" + }, + "devDependencies": { + "@istanbuljs/nyc-config-typescript": "^1.0.1", + "@node-redis/test-utils": "*", + "@types/node": "^16.11.7", + "nyc": "^15.1.0", + "release-it": "^14.11.7", + "source-map-support": "^0.5.20", + "ts-node": "^10.4.0", + "typescript": "^4.4.4" + } +} diff --git a/packages/json/tsconfig.json b/packages/json/tsconfig.json new file mode 100644 index 00000000000..14fda1d8711 --- /dev/null +++ b/packages/json/tsconfig.json @@ -0,0 +1,9 @@ +{ + "extends": "../../tsconfig.base.json", + "compilerOptions": { + "outDir": "./dist" + }, + "include": [ + "./lib/**/*.ts" + ] +} diff --git a/packages/search/.npmignore b/packages/search/.npmignore new file mode 100644 index 00000000000..bbef2b404fb --- /dev/null +++ b/packages/search/.npmignore @@ -0,0 +1,6 @@ +.nyc_output/ +coverage/ +lib/ +.nycrc.json +.release-it.json +tsconfig.json diff --git a/packages/search/.nycrc.json b/packages/search/.nycrc.json new file mode 100644 index 00000000000..b4e671e178f --- /dev/null +++ b/packages/search/.nycrc.json @@ -0,0 +1,4 @@ +{ + "extends": "@istanbuljs/nyc-config-typescript", + "exclude": ["**/*.spec.ts", "lib/test-utils.ts"] +} diff --git a/packages/search/.release-it.json b/packages/search/.release-it.json new file mode 100644 index 00000000000..72cb1016ef4 --- /dev/null +++ b/packages/search/.release-it.json @@ -0,0 +1,10 @@ +{ + "git": { + "tagName": "search@${version}", + "commitMessage": "Release ${tagName}", + "tagAnnotation": "Release ${tagName}" + }, + "npm": { + "publishArgs": ["--access", "public"] + } +} diff --git a/packages/search/README.md b/packages/search/README.md new file mode 100644 index 00000000000..856a75fbb5a --- /dev/null +++ b/packages/search/README.md @@ -0,0 +1,2 @@ +# @node-redis/search +The sources and docs for this package are in the main [node-redis](https://github.com/redis/node-redis) repo. diff --git a/packages/search/lib/commands/AGGREGATE.spec.ts b/packages/search/lib/commands/AGGREGATE.spec.ts new file mode 100644 index 00000000000..2a6647c97a4 --- /dev/null +++ b/packages/search/lib/commands/AGGREGATE.spec.ts @@ -0,0 +1,482 @@ +import { strict as assert } from 'assert'; +import testUtils, { GLOBAL } from '../test-utils'; +import { AggregateGroupByReducers, AggregateSteps, transformArguments } from './AGGREGATE'; +import { SchemaFieldTypes } from './CREATE'; + +describe('AGGREGATE', () => { + describe('transformArguments', () => { + it('without options', () => { + assert.deepEqual( + transformArguments('index', '*'), + ['FT.AGGREGATE', 'index', '*'] + ); + }); + + it('with VERBATIM', () => { + assert.deepEqual( + transformArguments('index', '*', { VERBATIM: true }), + ['FT.AGGREGATE', 'index', '*', 'VERBATIM'] + ); + }); + + describe('with LOAD', () => { + describe('single', () => { + describe('without alias', () => { + it('string', () => { + assert.deepEqual( + transformArguments('index', '*', { LOAD: '@property' }), + ['FT.AGGREGATE', 'index', '*', 'LOAD', '1', '@property'] + ); + }); + + it('{ identifier: string }', () => { + assert.deepEqual( + transformArguments('index', '*', { + LOAD: { + identifier: '@property' + } + }), + ['FT.AGGREGATE', 'index', '*', 'LOAD', '1', '@property'] + ); + }); + }); + + it('with alias', () => { + assert.deepEqual( + transformArguments('index', '*', { + LOAD: { + identifier: '@property', + AS: 'alias' + } + }), + ['FT.AGGREGATE', 'index', '*', 'LOAD', '3', '@property', 'AS', 'alias'] + ); + }); + }); + + it('multiple', () => { + assert.deepEqual( + transformArguments('index', '*', { LOAD: ['@1', '@2'] }), + ['FT.AGGREGATE', 'index', '*', 'LOAD', '2', '@1', '@2'] + ); + }); + }); + + describe('with STEPS', () => { + describe('GROUPBY', () => { + describe('COUNT', () => { + describe('without properties', () => { + it('without alias', () => { + assert.deepEqual( + transformArguments('index', '*', { + STEPS: [{ + type: AggregateSteps.GROUPBY, + REDUCE: { + type: AggregateGroupByReducers.COUNT + } + }] + }), + ['FT.AGGREGATE', 'index', '*', 'GROUPBY', '0', 'REDUCE', 'COUNT', '0'] + ); + }); + + it('with alias', () => { + assert.deepEqual( + transformArguments('index', '*', { + STEPS: [{ + type: AggregateSteps.GROUPBY, + REDUCE: { + type: AggregateGroupByReducers.COUNT, + AS: 'count' + } + }] + }), + ['FT.AGGREGATE', 'index', '*', 'GROUPBY', '0', 'REDUCE', 'COUNT', '0', 'AS', 'count'] + ); + }); + }); + + describe('with properties', () => { + it('single', () => { + assert.deepEqual( + transformArguments('index', '*', { + STEPS: [{ + type: AggregateSteps.GROUPBY, + properties: '@property', + REDUCE: { + type: AggregateGroupByReducers.COUNT + } + }] + }), + ['FT.AGGREGATE', 'index', '*', 'GROUPBY', '1', '@property', 'REDUCE', 'COUNT', '0'] + ); + }); + + it('multiple', () => { + assert.deepEqual( + transformArguments('index', '*', { + STEPS: [{ + type: AggregateSteps.GROUPBY, + properties: ['@1', '@2'], + REDUCE: { + type: AggregateGroupByReducers.COUNT + } + }] + }), + ['FT.AGGREGATE', 'index', '*', 'GROUPBY', '2', '@1', '@2', 'REDUCE', 'COUNT', '0'] + ); + }); + }); + }); + + it('COUNT_DISTINCT', () => { + assert.deepEqual( + transformArguments('index', '*', { + STEPS: [{ + type: AggregateSteps.GROUPBY, + REDUCE: { + type: AggregateGroupByReducers.COUNT_DISTINCT, + property: '@property' + } + }] + }), + ['FT.AGGREGATE', 'index', '*', 'GROUPBY', '0', 'REDUCE', 'COUNT_DISTINCT', '1', '@property'] + ); + }); + + it('COUNT_DISTINCTISH', () => { + assert.deepEqual( + transformArguments('index', '*', { + STEPS: [{ + type: AggregateSteps.GROUPBY, + REDUCE: { + type: AggregateGroupByReducers.COUNT_DISTINCTISH, + property: '@property' + } + }] + }), + ['FT.AGGREGATE', 'index', '*', 'GROUPBY', '0', 'REDUCE', 'COUNT_DISTINCTISH', '1', '@property'] + ); + }); + + it('SUM', () => { + assert.deepEqual( + transformArguments('index', '*', { + STEPS: [{ + type: AggregateSteps.GROUPBY, + REDUCE: { + type: AggregateGroupByReducers.SUM, + property: '@property' + } + }] + }), + ['FT.AGGREGATE', 'index', '*', 'GROUPBY', '0', 'REDUCE', 'SUM', '1', '@property'] + ); + }); + + it('MIN', () => { + assert.deepEqual( + transformArguments('index', '*', { + STEPS: [{ + type: AggregateSteps.GROUPBY, + REDUCE: { + type: AggregateGroupByReducers.MIN, + property: '@property' + } + }] + }), + ['FT.AGGREGATE', 'index', '*', 'GROUPBY', '0', 'REDUCE', 'MIN', '1', '@property'] + ); + }); + + it('MAX', () => { + assert.deepEqual( + transformArguments('index', '*', { + STEPS: [{ + type: AggregateSteps.GROUPBY, + REDUCE: { + type: AggregateGroupByReducers.MAX, + property: '@property' + } + }] + }), + ['FT.AGGREGATE', 'index', '*', 'GROUPBY', '0', 'REDUCE', 'MAX', '1', '@property'] + ); + }); + + it('AVG', () => { + assert.deepEqual( + transformArguments('index', '*', { + STEPS: [{ + type: AggregateSteps.GROUPBY, + REDUCE: { + type: AggregateGroupByReducers.AVG, + property: '@property' + } + }] + }), + ['FT.AGGREGATE', 'index', '*', 'GROUPBY', '0', 'REDUCE', 'AVG', '1', '@property'] + ); + }); + + it('STDDEV', () => { + assert.deepEqual( + transformArguments('index', '*', { + STEPS: [{ + type: AggregateSteps.GROUPBY, + REDUCE: { + type: AggregateGroupByReducers.STDDEV, + property: '@property' + } + }] + }), + ['FT.AGGREGATE', 'index', '*', 'GROUPBY', '0', 'REDUCE', 'STDDEV', '1', '@property'] + ); + }); + + it('QUANTILE', () => { + assert.deepEqual( + transformArguments('index', '*', { + STEPS: [{ + type: AggregateSteps.GROUPBY, + REDUCE: { + type: AggregateGroupByReducers.QUANTILE, + property: '@property', + quantile: 0.5 + } + }] + }), + ['FT.AGGREGATE', 'index', '*', 'GROUPBY', '0', 'REDUCE', 'QUANTILE', '2', '@property', '0.5'] + ); + }); + + it('TO_LIST', () => { + assert.deepEqual( + transformArguments('index', '*', { + STEPS: [{ + type: AggregateSteps.GROUPBY, + REDUCE: { + type: AggregateGroupByReducers.TO_LIST, + property: '@property' + } + }] + }), + ['FT.AGGREGATE', 'index', '*', 'GROUPBY', '0', 'REDUCE', 'TOLIST', '1', '@property'] + ); + }); + + describe('FIRST_VALUE', () => { + it('simple', () => { + assert.deepEqual( + transformArguments('index', '*', { + STEPS: [{ + type: AggregateSteps.GROUPBY, + REDUCE: { + type: AggregateGroupByReducers.FIRST_VALUE, + property: '@property' + } + }] + }), + ['FT.AGGREGATE', 'index', '*', 'GROUPBY', '0', 'REDUCE', 'FIRST_VALUE', '1', '@property'] + ); + }); + + describe('with BY', () => { + describe('without direction', () => { + it('string', () => { + assert.deepEqual( + transformArguments('index', '*', { + STEPS: [{ + type: AggregateSteps.GROUPBY, + REDUCE: { + type: AggregateGroupByReducers.FIRST_VALUE, + property: '@property', + BY: '@by' + } + }] + }), + ['FT.AGGREGATE', 'index', '*', 'GROUPBY', '0', 'REDUCE', 'FIRST_VALUE', '3', '@property', 'BY', '@by'] + ); + }); + + + it('{ property: string }', () => { + assert.deepEqual( + transformArguments('index', '*', { + STEPS: [{ + type: AggregateSteps.GROUPBY, + REDUCE: { + type: AggregateGroupByReducers.FIRST_VALUE, + property: '@property', + BY: { + property: '@by' + } + } + }] + }), + ['FT.AGGREGATE', 'index', '*', 'GROUPBY', '0', 'REDUCE', 'FIRST_VALUE', '3', '@property', 'BY', '@by'] + ); + }); + }); + + it('with direction', () => { + assert.deepEqual( + transformArguments('index', '*', { + STEPS: [{ + type: AggregateSteps.GROUPBY, + REDUCE: { + type: AggregateGroupByReducers.FIRST_VALUE, + property: '@property', + BY: { + property: '@by', + direction: 'ASC' + } + } + }] + }), + ['FT.AGGREGATE', 'index', '*', 'GROUPBY', '0', 'REDUCE', 'FIRST_VALUE', '4', '@property', 'BY', '@by', 'ASC'] + ); + }); + }); + }); + + it('RANDOM_SAMPLE', () => { + assert.deepEqual( + transformArguments('index', '*', { + STEPS: [{ + type: AggregateSteps.GROUPBY, + REDUCE: { + type: AggregateGroupByReducers.RANDOM_SAMPLE, + property: '@property', + sampleSize: 1 + } + }] + }), + ['FT.AGGREGATE', 'index', '*', 'GROUPBY', '0', 'REDUCE', 'RANDOM_SAMPLE', '2', '@property', '1'] + ); + }); + }); + + describe('SORTBY', () => { + it('string', () => { + assert.deepEqual( + transformArguments('index', '*', { + STEPS: [{ + type: AggregateSteps.SORTBY, + BY: '@by' + }] + }), + ['FT.AGGREGATE', 'index', '*', 'SORTBY', '1', '@by'] + ); + }); + + it('Array', () => { + assert.deepEqual( + transformArguments('index', '*', { + STEPS: [{ + type: AggregateSteps.SORTBY, + BY: ['@1', '@2'] + }] + }), + ['FT.AGGREGATE', 'index', '*', 'SORTBY', '2', '@1', '@2'] + ); + }); + + it('with MAX', () => { + assert.deepEqual( + transformArguments('index', '*', { + STEPS: [{ + type: AggregateSteps.SORTBY, + BY: '@by', + MAX: 1 + }] + }), + ['FT.AGGREGATE', 'index', '*', 'SORTBY', '1', '@by', 'MAX', '1'] + ); + }); + }); + + describe('APPLY', () => { + assert.deepEqual( + transformArguments('index', '*', { + STEPS: [{ + type: AggregateSteps.APPLY, + expression: '@field + 1', + AS: 'as' + }] + }), + ['FT.AGGREGATE', 'index', '*', 'APPLY', '@field + 1', 'AS', 'as'] + ); + }); + + describe('LIMIT', () => { + assert.deepEqual( + transformArguments('index', '*', { + STEPS: [{ + type: AggregateSteps.LIMIT, + from: 0, + size: 1 + }] + }), + ['FT.AGGREGATE', 'index', '*', 'LIMIT', '0', '1'] + ); + }); + + describe('FILTER', () => { + assert.deepEqual( + transformArguments('index', '*', { + STEPS: [{ + type: AggregateSteps.FILTER, + expression: '@field != ""' + }] + }), + ['FT.AGGREGATE', 'index', '*', 'FILTER', '@field != ""'] + ); + }); + }); + }); + + testUtils.testWithClient('client.ft.aggregate', async client => { + await Promise.all([ + client.ft.create('index', { + field: SchemaFieldTypes.NUMERIC + }), + client.hSet('1', 'field', '1'), + client.hSet('2', 'field', '2') + ]); + + assert.deepEqual( + await client.ft.aggregate('index', '*', { + STEPS: [{ + type: AggregateSteps.GROUPBY, + REDUCE: [{ + type: AggregateGroupByReducers.SUM, + property: '@field', + AS: 'sum' + }, { + type: AggregateGroupByReducers.AVG, + property: '@field', + AS: 'avg' + }] + }] + }), + { + total: 1, + results: [ + Object.create(null, { + sum: { + value: '3', + configurable: true, + enumerable: true + }, + avg: { + value: '1.5', + configurable: true, + enumerable: true + } + }) + ] + } + ); + }, GLOBAL.SERVERS.OPEN); +}); diff --git a/packages/search/lib/commands/AGGREGATE.ts b/packages/search/lib/commands/AGGREGATE.ts new file mode 100644 index 00000000000..c81dcfef4dd --- /dev/null +++ b/packages/search/lib/commands/AGGREGATE.ts @@ -0,0 +1,283 @@ +import { RedisCommandArguments } from '@node-redis/client/dist/lib/commands'; +import { pushVerdictArgument, transformReplyTuples, TuplesObject } from '@node-redis/client/dist/lib/commands/generic-transformers'; +import { PropertyName, pushArgumentsWithLength, pushSortByArguments, SortByOptions } from '.'; + +export enum AggregateSteps { + GROUPBY = 'GROUPBY', + SORTBY = 'SORTBY', + APPLY = 'APPLY', + LIMIT = 'LIMIT', + FILTER = 'FILTER' +} + +interface AggregateStep { + type: T; +} + +export enum AggregateGroupByReducers { + COUNT = 'COUNT', + COUNT_DISTINCT = 'COUNT_DISTINCT', + COUNT_DISTINCTISH = 'COUNT_DISTINCTISH', + SUM = 'SUM', + MIN = 'MIN', + MAX = 'MAX', + AVG = 'AVG', + STDDEV = 'STDDEV', + QUANTILE = 'QUANTILE', + TOLIST = 'TOLIST', + TO_LIST = 'TOLIST', + FIRST_VALUE = 'FIRST_VALUE', + RANDOM_SAMPLE = 'RANDOM_SAMPLE' +} + +interface GroupByReducer { + type: T; + AS?: string; +} + +type CountReducer = GroupByReducer; + +interface CountDistinctReducer extends GroupByReducer { + property: PropertyName; +} + +interface CountDistinctishReducer extends GroupByReducer { + property: PropertyName; +} + +interface SumReducer extends GroupByReducer { + property: PropertyName; +} + +interface MinReducer extends GroupByReducer { + property: PropertyName; +} + +interface MaxReducer extends GroupByReducer { + property: PropertyName; +} + +interface AvgReducer extends GroupByReducer { + property: PropertyName; +} + +interface StdDevReducer extends GroupByReducer { + property: PropertyName; +} + +interface QuantileReducer extends GroupByReducer { + property: PropertyName; + quantile: number; +} + +interface ToListReducer extends GroupByReducer { + property: PropertyName; +} + +interface FirstValueReducer extends GroupByReducer { + property: PropertyName; + BY?: PropertyName | { + property: PropertyName; + direction?: 'ASC' | 'DESC'; + }; +} + +interface RandomSampleReducer extends GroupByReducer { + property: PropertyName; + sampleSize: number; +} + +type GroupByReducers = CountReducer | CountDistinctReducer | CountDistinctishReducer | SumReducer | MinReducer | MaxReducer | AvgReducer | StdDevReducer | QuantileReducer | ToListReducer | FirstValueReducer | RandomSampleReducer; + +interface GroupByStep extends AggregateStep { + properties?: PropertyName | Array; + REDUCE: GroupByReducers | Array; +} + +interface SortStep extends AggregateStep { + BY: SortByOptions | Array; + MAX?: number; +} + +interface ApplyStep extends AggregateStep { + expression: string; + AS: string; +} + +interface LimitStep extends AggregateStep { + from: number; + size: number; +} + +interface FilterStep extends AggregateStep { + expression: string; +} + +type LoadField = PropertyName | { + identifier: PropertyName; + AS?: string; +} + +interface AggregateOptions { + VERBATIM?: true; + LOAD?: LoadField | Array; + STEPS?: Array; +} + +export function transformArguments(index: string, query: string, options?: AggregateOptions): RedisCommandArguments { + const args = ['FT.AGGREGATE', index, query]; + + if (options?.VERBATIM) { + args.push('VERBATIM'); + } + + if (options?.LOAD) { + args.push('LOAD'); + pushArgumentsWithLength(args, () => { + if (Array.isArray(options.LOAD)) { + for (const load of options.LOAD) { + pushLoadField(args, load); + } + } else { + pushLoadField(args, options.LOAD!); + } + }); + } + + if (options?.STEPS) { + for (const step of options.STEPS) { + switch (step.type) { + case AggregateSteps.GROUPBY: + args.push('GROUPBY'); + if (!step.properties) { + args.push('0'); + } else { + pushVerdictArgument(args, step.properties); + } + + if (Array.isArray(step.REDUCE)) { + for (const reducer of step.REDUCE) { + pushGroupByReducer(args, reducer); + } + } else { + pushGroupByReducer(args, step.REDUCE); + } + + break; + + case AggregateSteps.SORTBY: + pushSortByArguments(args, 'SORTBY', step.BY); + + if (step.MAX) { + args.push('MAX', step.MAX.toString()); + } + + break; + + case AggregateSteps.APPLY: + args.push('APPLY', step.expression, 'AS', step.AS); + break; + + case AggregateSteps.LIMIT: + args.push('LIMIT', step.from.toString(), step.size.toString()); + break; + + case AggregateSteps.FILTER: + args.push('FILTER', step.expression); + break; + } + } + } + + return args; +} + +function pushLoadField(args: RedisCommandArguments, toLoad: LoadField): void { + if (typeof toLoad === 'string') { + args.push(toLoad); + } else { + args.push(toLoad.identifier); + + if (toLoad.AS) { + args.push('AS', toLoad.AS); + } + } +} + +function pushGroupByReducer(args: RedisCommandArguments, reducer: GroupByReducers): void { + args.push('REDUCE', reducer.type); + + switch (reducer.type) { + case AggregateGroupByReducers.COUNT: + args.push('0'); + break; + + case AggregateGroupByReducers.COUNT_DISTINCT: + case AggregateGroupByReducers.COUNT_DISTINCTISH: + case AggregateGroupByReducers.SUM: + case AggregateGroupByReducers.MIN: + case AggregateGroupByReducers.MAX: + case AggregateGroupByReducers.AVG: + case AggregateGroupByReducers.STDDEV: + case AggregateGroupByReducers.TOLIST: + args.push('1', reducer.property); + break; + + case AggregateGroupByReducers.QUANTILE: + args.push('2', reducer.property, reducer.quantile.toString()); + break; + + case AggregateGroupByReducers.FIRST_VALUE: { + pushArgumentsWithLength(args, () => { + args.push(reducer.property); + + if (reducer.BY) { + args.push('BY'); + if (typeof reducer.BY === 'string') { + args.push(reducer.BY); + } else { + args.push(reducer.BY.property); + + if (reducer.BY.direction) { + args.push(reducer.BY.direction); + } + } + } + }); + + break; + } + + case AggregateGroupByReducers.RANDOM_SAMPLE: + args.push('2', reducer.property, reducer.sampleSize.toString()); + break; + } + + if (reducer.AS) { + args.push('AS', reducer.AS); + } +} + +type AggregateRawReply = [ + total: number, + ...results: Array> +]; + +interface AggregateReply { + total: number; + results: Array; +} + +export function transformReply(rawReply: AggregateRawReply): AggregateReply { + const results: Array = []; + for (let i = 1; i < rawReply.length; i++) { + results.push( + transformReplyTuples(rawReply[i] as Array) + ); + } + + return { + total: rawReply[0], + results + }; +} \ No newline at end of file diff --git a/packages/search/lib/commands/ALIASADD.spec.ts b/packages/search/lib/commands/ALIASADD.spec.ts new file mode 100644 index 00000000000..7bb2452838b --- /dev/null +++ b/packages/search/lib/commands/ALIASADD.spec.ts @@ -0,0 +1,11 @@ +import { strict as assert } from 'assert'; +import { transformArguments } from './ALIASADD'; + +describe('ALIASADD', () => { + it('transformArguments', () => { + assert.deepEqual( + transformArguments('alias', 'index'), + ['FT.ALIASADD', 'alias', 'index'] + ); + }); +}); diff --git a/packages/search/lib/commands/ALIASADD.ts b/packages/search/lib/commands/ALIASADD.ts new file mode 100644 index 00000000000..552c1add695 --- /dev/null +++ b/packages/search/lib/commands/ALIASADD.ts @@ -0,0 +1,5 @@ +export function transformArguments(name: string, index: string): Array { + return ['FT.ALIASADD', name, index]; +} + +export declare function transformReply(): 'OK'; diff --git a/packages/search/lib/commands/ALIASDEL.spec.ts b/packages/search/lib/commands/ALIASDEL.spec.ts new file mode 100644 index 00000000000..5255ba835db --- /dev/null +++ b/packages/search/lib/commands/ALIASDEL.spec.ts @@ -0,0 +1,11 @@ +import { strict as assert } from 'assert'; +import { transformArguments } from './ALIASDEL'; + +describe('ALIASDEL', () => { + it('transformArguments', () => { + assert.deepEqual( + transformArguments('alias', 'index'), + ['FT.ALIASDEL', 'alias', 'index'] + ); + }); +}); diff --git a/packages/search/lib/commands/ALIASDEL.ts b/packages/search/lib/commands/ALIASDEL.ts new file mode 100644 index 00000000000..434b4df3dea --- /dev/null +++ b/packages/search/lib/commands/ALIASDEL.ts @@ -0,0 +1,5 @@ +export function transformArguments(name: string, index: string): Array { + return ['FT.ALIASDEL', name, index]; +} + +export declare function transformReply(): 'OK'; diff --git a/packages/search/lib/commands/ALIASUPDATE.spec.ts b/packages/search/lib/commands/ALIASUPDATE.spec.ts new file mode 100644 index 00000000000..79421b1a20d --- /dev/null +++ b/packages/search/lib/commands/ALIASUPDATE.spec.ts @@ -0,0 +1,11 @@ +import { strict as assert } from 'assert'; +import { transformArguments } from './ALIASUPDATE'; + +describe('ALIASUPDATE', () => { + it('transformArguments', () => { + assert.deepEqual( + transformArguments('alias', 'index'), + ['FT.ALIASUPDATE', 'alias', 'index'] + ); + }); +}); diff --git a/packages/search/lib/commands/ALIASUPDATE.ts b/packages/search/lib/commands/ALIASUPDATE.ts new file mode 100644 index 00000000000..ac64ef57c3f --- /dev/null +++ b/packages/search/lib/commands/ALIASUPDATE.ts @@ -0,0 +1,5 @@ +export function transformArguments(name: string, index: string): Array { + return ['FT.ALIASUPDATE', name, index]; +} + +export declare function transformReply(): 'OK'; diff --git a/packages/search/lib/commands/CONFIG_GET.spec.ts b/packages/search/lib/commands/CONFIG_GET.spec.ts new file mode 100644 index 00000000000..8614f443426 --- /dev/null +++ b/packages/search/lib/commands/CONFIG_GET.spec.ts @@ -0,0 +1,25 @@ +import { strict as assert } from 'assert'; +import testUtils, { GLOBAL } from '../test-utils'; +import { transformArguments } from './CONFIG_GET'; + +describe('CONFIG GET', () => { + it('transformArguments', () => { + assert.deepEqual( + transformArguments('TIMEOUT'), + ['FT.CONFIG', 'GET', 'TIMEOUT'] + ); + }); + + testUtils.testWithClient('client.ft.configGet', async client => { + assert.deepEqual( + await client.ft.configGet('TIMEOUT'), + Object.create(null, { + TIMEOUT: { + value: '500', + configurable: true, + enumerable: true + } + }) + ); + }, GLOBAL.SERVERS.OPEN); +}); diff --git a/packages/search/lib/commands/CONFIG_GET.ts b/packages/search/lib/commands/CONFIG_GET.ts new file mode 100644 index 00000000000..fbf1f1164b9 --- /dev/null +++ b/packages/search/lib/commands/CONFIG_GET.ts @@ -0,0 +1,16 @@ +export function transformArguments(option: string) { + return ['FT.CONFIG', 'GET', option]; +} + +interface ConfigGetReply { + [option: string]: string | null; +} + +export function transformReply(rawReply: Array<[string, string | null]>): ConfigGetReply { + const transformedReply: ConfigGetReply = Object.create(null); + for (const [key, value] of rawReply) { + transformedReply[key] = value; + } + + return transformedReply; +} diff --git a/packages/search/lib/commands/CONFIG_SET.spec.ts b/packages/search/lib/commands/CONFIG_SET.spec.ts new file mode 100644 index 00000000000..59cb63a3d8e --- /dev/null +++ b/packages/search/lib/commands/CONFIG_SET.spec.ts @@ -0,0 +1,12 @@ +import { strict as assert } from 'assert'; +import testUtils, { GLOBAL } from '../test-utils'; +import { transformArguments } from './CONFIG_SET'; + +describe('CONFIG SET', () => { + it('transformArguments', () => { + assert.deepEqual( + transformArguments('TIMEOUT', '500'), + ['FT.CONFIG', 'SET', 'TIMEOUT', '500'] + ); + }); +}); diff --git a/packages/search/lib/commands/CONFIG_SET.ts b/packages/search/lib/commands/CONFIG_SET.ts new file mode 100644 index 00000000000..93b76d79edf --- /dev/null +++ b/packages/search/lib/commands/CONFIG_SET.ts @@ -0,0 +1,5 @@ +export function transformArguments(option: string, value: string): Array { + return ['FT.CONFIG', 'SET', option, value]; +} + +export declare function transformReply(): 'OK'; diff --git a/packages/search/lib/commands/CREATE.spec.ts b/packages/search/lib/commands/CREATE.spec.ts new file mode 100644 index 00000000000..5bdf4c93a43 --- /dev/null +++ b/packages/search/lib/commands/CREATE.spec.ts @@ -0,0 +1,350 @@ +import { strict as assert } from 'assert'; +import testUtils, { GLOBAL } from '../test-utils'; +import { SchemaFieldTypes, SchemaTextFieldPhonetics, transformArguments } from './CREATE'; +import { RedisSearchLanguages } from '.'; + +describe('CREATE', () => { + describe('transformArguments', () => { + it('simple', () => { + assert.deepEqual( + transformArguments('index', {}), + ['FT.CREATE', 'index', 'SCHEMA'] + ); + }); + + describe('with fields', () => { + describe('TEXT', () => { + it('without options', () => { + assert.deepEqual( + transformArguments('index', { + field: SchemaFieldTypes.TEXT + }), + ['FT.CREATE', 'index', 'SCHEMA', 'field', 'TEXT'] + ); + }); + + it('with NOSTEM', () => { + assert.deepEqual( + transformArguments('index', { + field: { + type: SchemaFieldTypes.TEXT, + NOSTEM: true + } + }), + ['FT.CREATE', 'index', 'SCHEMA', 'field', 'TEXT', 'NOSTEM'] + ); + }); + + it('with WEIGHT', () => { + assert.deepEqual( + transformArguments('index', { + field: { + type: SchemaFieldTypes.TEXT, + WEIGHT: 1 + } + }), + ['FT.CREATE', 'index', 'SCHEMA', 'field', 'TEXT', 'WEIGHT', '1'] + ); + }); + + it('with PHONETIC', () => { + assert.deepEqual( + transformArguments('index', { + field: { + type: SchemaFieldTypes.TEXT, + PHONETIC: SchemaTextFieldPhonetics.DM_EN + } + }), + ['FT.CREATE', 'index', 'SCHEMA', 'field', 'TEXT', 'PHONETIC', SchemaTextFieldPhonetics.DM_EN] + ); + }); + }); + + it('NUMERIC', () => { + assert.deepEqual( + transformArguments('index', { + field: SchemaFieldTypes.NUMERIC + }), + ['FT.CREATE', 'index', 'SCHEMA', 'field', 'NUMERIC'] + ); + }); + + it('GEO', () => { + assert.deepEqual( + transformArguments('index', { + field: SchemaFieldTypes.GEO + }), + ['FT.CREATE', 'index', 'SCHEMA', 'field', 'GEO'] + ); + }); + + describe('TAG', () => { + describe('without options', () => { + it('SchemaFieldTypes.TAG', () => { + assert.deepEqual( + transformArguments('index', { + field: SchemaFieldTypes.TAG + }), + ['FT.CREATE', 'index', 'SCHEMA', 'field', 'TAG'] + ); + }); + + it('{ type: SchemaFieldTypes.TAG }', () => { + assert.deepEqual( + transformArguments('index', { + field: { + type: SchemaFieldTypes.TAG + } + }), + ['FT.CREATE', 'index', 'SCHEMA', 'field', 'TAG'] + ); + }); + }); + + it('with SEPERATOR', () => { + assert.deepEqual( + transformArguments('index', { + field: { + type: SchemaFieldTypes.TAG, + SEPERATOR: 'seperator' + } + }), + ['FT.CREATE', 'index', 'SCHEMA', 'field', 'TAG', 'SEPERATOR', 'seperator'] + ); + }); + + + + it('with CASESENSITIVE', () => { + assert.deepEqual( + transformArguments('index', { + field: { + type: SchemaFieldTypes.TAG, + CASESENSITIVE: true + } + }), + ['FT.CREATE', 'index', 'SCHEMA', 'field', 'TAG', 'CASESENSITIVE'] + ); + }); + }); + + describe('with generic options', () => { + it('with AS', () => { + assert.deepEqual( + transformArguments('index', { + field: { + type: SchemaFieldTypes.TEXT, + AS: 'as' + } + }), + ['FT.CREATE', 'index', 'SCHEMA', 'field', 'AS', 'as', 'TEXT'] + ); + }); + + describe('with SORTABLE', () => { + it('true', () => { + assert.deepEqual( + transformArguments('index', { + field: { + type: SchemaFieldTypes.TEXT, + SORTABLE: true + } + }), + ['FT.CREATE', 'index', 'SCHEMA', 'field', 'TEXT', 'SORTABLE'] + ); + }); + + it('UNF', () => { + assert.deepEqual( + transformArguments('index', { + field: { + type: SchemaFieldTypes.TEXT, + SORTABLE: 'UNF' + } + }), + ['FT.CREATE', 'index', 'SCHEMA', 'field', 'TEXT', 'SORTABLE', 'UNF'] + ); + }); + }); + + it('with NOINDEX', () => { + assert.deepEqual( + transformArguments('index', { + field: { + type: SchemaFieldTypes.TEXT, + NOINDEX: true + } + }), + ['FT.CREATE', 'index', 'SCHEMA', 'field', 'TEXT', 'NOINDEX'] + ); + }); + }); + }); + + it('with ON', () => { + assert.deepEqual( + transformArguments('index', {}, { + ON: 'HASH' + }), + ['FT.CREATE', 'index', 'ON', 'HASH', 'SCHEMA'] + ); + }); + + describe('with PREFIX', () => { + it('string', () => { + assert.deepEqual( + transformArguments('index', {}, { + PREFIX: 'prefix' + }), + ['FT.CREATE', 'index', 'PREFIX', '1', 'prefix', 'SCHEMA'] + ); + }); + + it('Array', () => { + assert.deepEqual( + transformArguments('index', {}, { + PREFIX: ['1', '2'] + }), + ['FT.CREATE', 'index', 'PREFIX', '2', '1', '2', 'SCHEMA'] + ); + }); + }); + + it('with FILTER', () => { + assert.deepEqual( + transformArguments('index', {}, { + FILTER: '@field != ""' + }), + ['FT.CREATE', 'index', 'FILTER', '@field != ""', 'SCHEMA'] + ); + }); + + it('with LANGUAGE', () => { + assert.deepEqual( + transformArguments('index', {}, { + LANGUAGE: RedisSearchLanguages.ARABIC + }), + ['FT.CREATE', 'index', 'LANGUAGE', RedisSearchLanguages.ARABIC, 'SCHEMA'] + ); + }); + + it('with LANGUAGE_FIELD', () => { + assert.deepEqual( + transformArguments('index', {}, { + LANGUAGE_FIELD: '@field' + }), + ['FT.CREATE', 'index', 'LANGUAGE_FIELD', '@field', 'SCHEMA'] + ); + }); + + it('with SCORE', () => { + assert.deepEqual( + transformArguments('index', {}, { + SCORE: 1 + }), + ['FT.CREATE', 'index', 'SCORE', '1', 'SCHEMA'] + ); + }); + + it('with SCORE_FIELD', () => { + assert.deepEqual( + transformArguments('index', {}, { + SCORE_FIELD: '@field' + }), + ['FT.CREATE', 'index', 'SCORE_FIELD', '@field', 'SCHEMA'] + ); + }); + + it('with MAXTEXTFIELDS', () => { + assert.deepEqual( + transformArguments('index', {}, { + MAXTEXTFIELDS: true + }), + ['FT.CREATE', 'index', 'MAXTEXTFIELDS', 'SCHEMA'] + ); + }); + + it('with TEMPORARY', () => { + assert.deepEqual( + transformArguments('index', {}, { + TEMPORARY: 1 + }), + ['FT.CREATE', 'index', 'TEMPORARY', '1', 'SCHEMA'] + ); + }); + + it('with NOOFFSETS', () => { + assert.deepEqual( + transformArguments('index', {}, { + NOOFFSETS: true + }), + ['FT.CREATE', 'index', 'NOOFFSETS', 'SCHEMA'] + ); + }); + + it('with NOHL', () => { + assert.deepEqual( + transformArguments('index', {}, { + NOHL: true + }), + ['FT.CREATE', 'index', 'NOHL', 'SCHEMA'] + ); + }); + + it('with NOFIELDS', () => { + assert.deepEqual( + transformArguments('index', {}, { + NOFIELDS: true + }), + ['FT.CREATE', 'index', 'NOFIELDS', 'SCHEMA'] + ); + }); + + it('with NOFREQS', () => { + assert.deepEqual( + transformArguments('index', {}, { + NOFREQS: true + }), + ['FT.CREATE', 'index', 'NOFREQS', 'SCHEMA'] + ); + }); + + it('with SKIPINITIALSCAN', () => { + assert.deepEqual( + transformArguments('index', {}, { + SKIPINITIALSCAN: true + }), + ['FT.CREATE', 'index', 'SKIPINITIALSCAN', 'SCHEMA'] + ); + }); + + describe('with STOPWORDS', () => { + it('string', () => { + assert.deepEqual( + transformArguments('index', {}, { + STOPWORDS: 'stopword' + }), + ['FT.CREATE', 'index', 'STOPWORDS', '1', 'stopword', 'SCHEMA'] + ); + }); + + it('Array', () => { + assert.deepEqual( + transformArguments('index', {}, { + STOPWORDS: ['1', '2'] + }), + ['FT.CREATE', 'index', 'STOPWORDS', '2', '1', '2', 'SCHEMA'] + ); + }); + }); + }); + + testUtils.testWithClient('client.ft.create', async client => { + assert.equal( + await client.ft.create('index', { + field: SchemaFieldTypes.TEXT // TODO: shouldn't be mandatory + }), + 'OK' + ); + }, GLOBAL.SERVERS.OPEN); +}); diff --git a/packages/search/lib/commands/CREATE.ts b/packages/search/lib/commands/CREATE.ts new file mode 100644 index 00000000000..1a5e45a4a88 --- /dev/null +++ b/packages/search/lib/commands/CREATE.ts @@ -0,0 +1,194 @@ +import { pushOptionalVerdictArgument } from '@node-redis/client/dist/lib/commands/generic-transformers'; +import { RedisSearchLanguages, PropertyName } from '.'; + +export enum SchemaFieldTypes { + TEXT = 'TEXT', + NUMERIC = 'NUMERIC', + GEO = 'GEO', + TAG = 'TAG' +} + +type CreateSchemaField> = T | ({ + type: T; + AS?: string; + SORTABLE?: true | 'UNF'; + NOINDEX?: true; +} & E); + +export enum SchemaTextFieldPhonetics { + DM_EN = 'dm:en', + DM_FR = 'dm:fr', + FM_PT = 'dm:pt', + DM_ES = 'dm:es' +} + +type CreateSchemaTextField = CreateSchemaField; + +type CreateSchemaNumericField = CreateSchemaField; + +type CreateSchemaGeoField = CreateSchemaField; + +type CreateSchemaTagField = CreateSchemaField; + +interface CreateSchema { + [field: string]: + CreateSchemaTextField | + CreateSchemaNumericField | + CreateSchemaGeoField | + CreateSchemaTagField +} + +interface CreateOptions { + ON?: 'HASH' | 'JSON'; + PREFIX?: string | Array; + FILTER?: string; + LANGUAGE?: RedisSearchLanguages; + LANGUAGE_FIELD?: PropertyName; + SCORE?: number; + SCORE_FIELD?: PropertyName; + // PAYLOAD_FIELD?: string; + MAXTEXTFIELDS?: true; + TEMPORARY?: number; + NOOFFSETS?: true; + NOHL?: true; + NOFIELDS?: true; + NOFREQS?: true; + SKIPINITIALSCAN?: true; + STOPWORDS?: string | Array; +} + +export function transformArguments(index: string, schema: CreateSchema, options?: CreateOptions): Array { + const args = ['FT.CREATE', index]; + + if (options?.ON) { + args.push('ON', options.ON); + } + + pushOptionalVerdictArgument(args, 'PREFIX', options?.PREFIX); + + if (options?.FILTER) { + args.push('FILTER', options.FILTER); + } + + if (options?.LANGUAGE) { + args.push('LANGUAGE', options.LANGUAGE); + } + + if (options?.LANGUAGE_FIELD) { + args.push('LANGUAGE_FIELD', options.LANGUAGE_FIELD); + } + + if (options?.SCORE) { + args.push('SCORE', options.SCORE.toString()); + } + + if (options?.SCORE_FIELD) { + args.push('SCORE_FIELD', options.SCORE_FIELD); + } + + // if (options?.PAYLOAD_FIELD) { + // args.push('PAYLOAD_FIELD', options.PAYLOAD_FIELD); + // } + + if (options?.MAXTEXTFIELDS) { + args.push('MAXTEXTFIELDS'); + } + + if (options?.TEMPORARY) { + args.push('TEMPORARY', options.TEMPORARY.toString()); + } + + if (options?.NOOFFSETS) { + args.push('NOOFFSETS'); + } + + if (options?.NOHL) { + args.push('NOHL'); + } + + if (options?.NOFIELDS) { + args.push('NOFIELDS'); + } + + if (options?.NOFREQS) { + args.push('NOFREQS'); + } + + if (options?.SKIPINITIALSCAN) { + args.push('SKIPINITIALSCAN'); + } + + pushOptionalVerdictArgument(args, 'STOPWORDS', options?.STOPWORDS); + + args.push('SCHEMA'); + + for (const [field, fieldOptions] of Object.entries(schema)) { + args.push(field); + + if (typeof fieldOptions === 'string') { + args.push(fieldOptions); + continue; + } + + if (fieldOptions.AS) { + args.push('AS', fieldOptions.AS); + } + + args.push(fieldOptions.type); + + switch (fieldOptions.type) { + case 'TEXT': + if (fieldOptions.NOSTEM) { + args.push('NOSTEM'); + } + + if (fieldOptions.WEIGHT) { + args.push('WEIGHT', fieldOptions.WEIGHT.toString()); + } + + if (fieldOptions.PHONETIC) { + args.push('PHONETIC', fieldOptions.PHONETIC); + } + + break; + + // case 'NUMERIC': + // case 'GEO': + // break; + + case 'TAG': + if (fieldOptions.SEPERATOR) { + args.push('SEPERATOR', fieldOptions.SEPERATOR); + } + + if (fieldOptions.CASESENSITIVE) { + args.push('CASESENSITIVE'); + } + + break; + } + + if (fieldOptions.SORTABLE) { + args.push('SORTABLE'); + + if (fieldOptions.SORTABLE === 'UNF') { + args.push('UNF'); + } + } + + if (fieldOptions.NOINDEX) { + args.push('NOINDEX'); + } + } + + return args; +} + +export declare function transformReply(): 'OK'; diff --git a/packages/search/lib/commands/DICTADD.spec.ts b/packages/search/lib/commands/DICTADD.spec.ts new file mode 100644 index 00000000000..b5f29dd4083 --- /dev/null +++ b/packages/search/lib/commands/DICTADD.spec.ts @@ -0,0 +1,28 @@ +import { strict as assert } from 'assert'; +import testUtils, { GLOBAL } from '../test-utils'; +import { transformArguments } from './DICTADD'; + +describe('DICTADD', () => { + describe('transformArguments', () => { + it('string', () => { + assert.deepEqual( + transformArguments('dictionary', 'term'), + ['FT.DICTADD', 'dictionary', 'term'] + ); + }); + + it('Array', () => { + assert.deepEqual( + transformArguments('dictionary', ['1', '2']), + ['FT.DICTADD', 'dictionary', '1', '2'] + ); + }); + }); + + testUtils.testWithClient('client.ft.dictAdd', async client => { + assert.equal( + await client.ft.dictAdd('dictionary', 'term'), + 1 + ); + }, GLOBAL.SERVERS.OPEN); +}); diff --git a/packages/search/lib/commands/DICTADD.ts b/packages/search/lib/commands/DICTADD.ts new file mode 100644 index 00000000000..b3f993395fa --- /dev/null +++ b/packages/search/lib/commands/DICTADD.ts @@ -0,0 +1,8 @@ +import { RedisCommandArguments } from '@node-redis/client/dist/lib/commands'; +import { pushVerdictArguments } from '@node-redis/client/dist/lib/commands/generic-transformers'; + +export function transformArguments(dictionary: string, term: string | Array): RedisCommandArguments { + return pushVerdictArguments(['FT.DICTADD', dictionary], term); +} + +export declare function transformReply(): number; diff --git a/packages/search/lib/commands/DICTDEL.spec.ts b/packages/search/lib/commands/DICTDEL.spec.ts new file mode 100644 index 00000000000..5ffa6b6b84f --- /dev/null +++ b/packages/search/lib/commands/DICTDEL.spec.ts @@ -0,0 +1,28 @@ +import { strict as assert } from 'assert'; +import testUtils, { GLOBAL } from '../test-utils'; +import { transformArguments } from './DICTDEL'; + +describe('DICTDEL', () => { + describe('transformArguments', () => { + it('string', () => { + assert.deepEqual( + transformArguments('dictionary', 'term'), + ['FT.DICTDEL', 'dictionary', 'term'] + ); + }); + + it('Array', () => { + assert.deepEqual( + transformArguments('dictionary', ['1', '2']), + ['FT.DICTDEL', 'dictionary', '1', '2'] + ); + }); + }); + + testUtils.testWithClient('client.ft.dictDel', async client => { + assert.equal( + await client.ft.dictDel('dictionary', 'term'), + 0 + ); + }, GLOBAL.SERVERS.OPEN); +}); diff --git a/packages/search/lib/commands/DICTDEL.ts b/packages/search/lib/commands/DICTDEL.ts new file mode 100644 index 00000000000..bd047a5031d --- /dev/null +++ b/packages/search/lib/commands/DICTDEL.ts @@ -0,0 +1,8 @@ +import { RedisCommandArguments } from '@node-redis/client/dist/lib/commands'; +import { pushVerdictArguments } from '@node-redis/client/dist/lib/commands/generic-transformers'; + +export function transformArguments(dictionary: string, term: string | Array): RedisCommandArguments { + return pushVerdictArguments(['FT.DICTDEL', dictionary], term); +} + +export declare function transformReply(): number; diff --git a/packages/search/lib/commands/DICTDUMP.spec.ts b/packages/search/lib/commands/DICTDUMP.spec.ts new file mode 100644 index 00000000000..9896fb9440d --- /dev/null +++ b/packages/search/lib/commands/DICTDUMP.spec.ts @@ -0,0 +1,21 @@ +import { strict as assert } from 'assert'; +import testUtils, { GLOBAL } from '../test-utils'; +import { transformArguments } from './DICTDUMP'; + +describe('DICTDUMP', () => { + it('transformArguments', () => { + assert.deepEqual( + transformArguments('dictionary'), + ['FT.DICTDUMP', 'dictionary'] + ); + }); + + testUtils.testWithClient('client.ft.dictDump', async client => { + await client.ft.dictAdd('dictionary', 'string') + + assert.deepEqual( + await client.ft.dictDump('dictionary'), + ['string'] + ); + }, GLOBAL.SERVERS.OPEN); +}); diff --git a/packages/search/lib/commands/DICTDUMP.ts b/packages/search/lib/commands/DICTDUMP.ts new file mode 100644 index 00000000000..1427bb42cb7 --- /dev/null +++ b/packages/search/lib/commands/DICTDUMP.ts @@ -0,0 +1,5 @@ +export function transformArguments(dictionary: string): Array { + return ['FT.DICTDUMP', dictionary]; +} + +export declare function transformReply(): Array; diff --git a/packages/search/lib/commands/DROPINDEX.spec.ts b/packages/search/lib/commands/DROPINDEX.spec.ts new file mode 100644 index 00000000000..751e274ba60 --- /dev/null +++ b/packages/search/lib/commands/DROPINDEX.spec.ts @@ -0,0 +1,33 @@ +import { strict as assert } from 'assert'; +import testUtils, { GLOBAL } from '../test-utils'; +import { SchemaFieldTypes } from './CREATE'; +import { transformArguments } from './DROPINDEX'; + +describe('DROPINDEX', () => { + describe('transformArguments', () => { + it('without options', () => { + assert.deepEqual( + transformArguments('index'), + ['FT.DROPINDEX', 'index'] + ); + }); + + it('with DD', () => { + assert.deepEqual( + transformArguments('index', { DD: true }), + ['FT.DROPINDEX', 'index', 'DD'] + ); + }); + }); + + testUtils.testWithClient('client.ft.dropIndex', async client => { + await client.ft.create('index', { + field: SchemaFieldTypes.TEXT // TODO: shouldn't be mandatory + }); + + assert.equal( + await client.ft.dropIndex('index'), + 'OK' + ); + }, GLOBAL.SERVERS.OPEN); +}); diff --git a/packages/search/lib/commands/DROPINDEX.ts b/packages/search/lib/commands/DROPINDEX.ts new file mode 100644 index 00000000000..7897a9dd82e --- /dev/null +++ b/packages/search/lib/commands/DROPINDEX.ts @@ -0,0 +1,15 @@ +interface DropIndexOptions { + DD?: true; +} + +export function transformArguments(index: string, options?: DropIndexOptions): Array { + const args = ['FT.DROPINDEX', index]; + + if (options?.DD) { + args.push('DD'); + } + + return args; +} + +export declare function transformReply(): 'OK'; diff --git a/packages/search/lib/commands/EXPLAIN.spec.ts b/packages/search/lib/commands/EXPLAIN.spec.ts new file mode 100644 index 00000000000..dd55e038710 --- /dev/null +++ b/packages/search/lib/commands/EXPLAIN.spec.ts @@ -0,0 +1,11 @@ +import { strict as assert } from 'assert'; +import { transformArguments } from './EXPLAIN'; + +describe('EXPLAIN', () => { + it('transformArguments', () => { + assert.deepEqual( + transformArguments('index', '*'), + ['FT.EXPLAIN', 'index', '*'] + ); + }); +}); diff --git a/packages/search/lib/commands/EXPLAIN.ts b/packages/search/lib/commands/EXPLAIN.ts new file mode 100644 index 00000000000..c41cd9a4aac --- /dev/null +++ b/packages/search/lib/commands/EXPLAIN.ts @@ -0,0 +1,7 @@ +export const IS_READ_ONLY = true; + +export function transformArguments(index: string, query: string): Array { + return ['FT.EXPLAIN', index, query]; +} + +export declare function transformReply(): string; diff --git a/packages/search/lib/commands/EXPLAINCLI.spec.ts b/packages/search/lib/commands/EXPLAINCLI.spec.ts new file mode 100644 index 00000000000..238ef44eaaa --- /dev/null +++ b/packages/search/lib/commands/EXPLAINCLI.spec.ts @@ -0,0 +1,11 @@ +import { strict as assert } from 'assert'; +import { transformArguments } from './EXPLAINCLI'; + +describe('EXPLAINCLI', () => { + it('transformArguments', () => { + assert.deepEqual( + transformArguments('index', '*'), + ['FT.EXPLAINCLI', 'index', '*'] + ); + }); +}); diff --git a/packages/search/lib/commands/EXPLAINCLI.ts b/packages/search/lib/commands/EXPLAINCLI.ts new file mode 100644 index 00000000000..db97fb9c8da --- /dev/null +++ b/packages/search/lib/commands/EXPLAINCLI.ts @@ -0,0 +1,7 @@ +export const IS_READ_ONLY = true; + +export function transformArguments(index: string, query: string): Array { + return ['FT.EXPLAINCLI', index, query]; +} + +export declare function transformReply(): Array; diff --git a/packages/search/lib/commands/INFO.spec.ts b/packages/search/lib/commands/INFO.spec.ts new file mode 100644 index 00000000000..fa50a4b0cd8 --- /dev/null +++ b/packages/search/lib/commands/INFO.spec.ts @@ -0,0 +1,65 @@ +import { strict as assert } from 'assert'; +import testUtils, { GLOBAL } from '../test-utils'; +import { SchemaFieldTypes } from './CREATE'; +import { transformArguments } from './INFO'; + +describe('INFO', () => { + it('transformArguments', () => { + assert.deepEqual( + transformArguments('index'), + ['FT.INFO', 'index'] + ); + }); + + testUtils.testWithClient('client.ft.info', async client => { + await client.ft.create('index', {}, { + ON: 'HASH' // TODO: shouldn't be mandatory + }); + + assert.deepEqual( + await client.ft.info('index'), + { + indexName: 'index', + indexOptions: [], + indexDefinition: { + defaultScore: '1', + keyType: 'HASH', + prefixes: [''] + }, + attributes: [], + numDocs: '0', + maxDocId: '0', + numTerms: '0', + numRecords: '0', + invertedSzMb: '0', + totalInvertedIndexBlocks: '0', + offsetVectorsSzMb: '0', + docTableSizeMb: '0', + sortableValuesSizeMb: '0', + keyTableSizeMb: '0', + recordsPerDocAvg: '-nan', + bytesPerRecordAvg: '-nan', + offsetsPerTermAvg: '-nan', + offsetBitsPerRecordAvg: '-nan', + hashIndexingFailures: '0', + indexing: '0', + percentIndexed: '1', + gcStats: { + bytesCollected: '0', + totalMsRun: '0', + totalCycles: '0', + averageCycleTimeMs: '-nan', + lastRunTimeMs: '0', + gcNumericTreesMissed: '0', + gcBlocksDenied: '0' + }, + cursorStats: { + globalIdle: 0, + globalTotal: 0, + indexCapacity: 128, + idnexTotal: 0 + } + } + ); + }, GLOBAL.SERVERS.OPEN); +}); diff --git a/packages/search/lib/commands/INFO.ts b/packages/search/lib/commands/INFO.ts new file mode 100644 index 00000000000..42451114c89 --- /dev/null +++ b/packages/search/lib/commands/INFO.ts @@ -0,0 +1,171 @@ +export function transformArguments(index: string): Array { + return ['FT.INFO', index]; +} + +type InfoRawReply = [ + _: string, + indexName: string, + _: string, + indexOptions: Array, + _: string, + indexDefinition: [ + _: string, + keyType: string, + _: string, + prefixes: Array, + _: string, + defaultScore: string + ], + _: string, + attributes: Array>, + _: string, + numDocs: string, + _: string, + maxDocId: string, + _: string, + numTerms: string, + _: string, + numRecords: string, + _: string, + invertedSzMb: string, + _: string, + totalInvertedIndexBlocks: string, + _: string, + offsetVectorsSzMb: string, + _: string, + docTableSizeMb: string, + _: string, + sortableValuesSizeMb: string, + _: string, + keyTableSizeMb: string, + _: string, + recordsPerDocAvg: string, + _: string, + bytesPerRecordAvg: string, + _: string, + offsetsPerTermAvg: string, + _: string, + offsetBitsPerRecordAvg: string, + _: string, + hashIndexingFailures: string, + _: string, + indexing: string, + _: string, + percentIndexed: string, + _: string, + gcStats: [ + _: string, + bytesCollected: string, + _: string, + totalMsRun: string, + _: string, + totalCycles: string, + _: string, + averageCycleTimeMs: string, + _: string, + lastRunTimeMs: string, + _: string, + gcNumericTreesMissed: string, + _: string, + gcBlocksDenied: string + ], + _: string, + cursorStats: [ + _: string, + globalIdle: number, + _: string, + globalTotal: number, + _: string, + indexCapacity: number, + _: string, + idnexTotal: number + ] +]; + +interface InfoReply { + indexName: string; + indexOptions: Array; + indexDefinition: { + keyType: string; + prefixes: Array; + defaultScore: string; + }; + attributes: Array>; + numDocs: string; + maxDocId: string; + numTerms: string; + numRecords: string; + invertedSzMb: string; + totalInvertedIndexBlocks: string; + offsetVectorsSzMb: string; + docTableSizeMb: string; + sortableValuesSizeMb: string; + keyTableSizeMb: string; + recordsPerDocAvg: string; + bytesPerRecordAvg: string; + offsetsPerTermAvg: string; + offsetBitsPerRecordAvg: string; + hashIndexingFailures: string; + indexing: string; + percentIndexed: string; + gcStats: { + bytesCollected: string; + totalMsRun: string; + totalCycles: string; + averageCycleTimeMs: string; + lastRunTimeMs: string; + gcNumericTreesMissed: string; + gcBlocksDenied: string; + }; + cursorStats: { + globalIdle: number; + globalTotal: number; + indexCapacity: number; + idnexTotal: number; + }; +} + +export function transformReply(rawReply: InfoRawReply): InfoReply { + return { + indexName: rawReply[1], + indexOptions: rawReply[3], + indexDefinition: { + keyType: rawReply[5][1], + prefixes: rawReply[5][3], + defaultScore: rawReply[5][5] + }, + attributes: rawReply[7], + numDocs: rawReply[9], + maxDocId: rawReply[11], + numTerms: rawReply[13], + numRecords: rawReply[15], + invertedSzMb: rawReply[17], + totalInvertedIndexBlocks: rawReply[19], + offsetVectorsSzMb: rawReply[21], + docTableSizeMb: rawReply[23], + sortableValuesSizeMb: rawReply[25], + keyTableSizeMb: rawReply[27], + recordsPerDocAvg: rawReply[29], + bytesPerRecordAvg: rawReply[31], + offsetsPerTermAvg: rawReply[33], + offsetBitsPerRecordAvg: rawReply[35], + hashIndexingFailures: rawReply[37], + indexing: rawReply[39], + percentIndexed: rawReply[41], + gcStats: { + bytesCollected: rawReply[43][1], + totalMsRun: rawReply[43][3], + totalCycles: rawReply[43][5], + averageCycleTimeMs: rawReply[43][7], + lastRunTimeMs: rawReply[43][9], + gcNumericTreesMissed: rawReply[43][11], + gcBlocksDenied: rawReply[43][13] + }, + cursorStats: { + globalIdle: rawReply[45][1], + globalTotal: rawReply[45][3], + indexCapacity: rawReply[45][5], + idnexTotal: rawReply[45][7] + } + }; +} diff --git a/packages/search/lib/commands/PROFILE.ts b/packages/search/lib/commands/PROFILE.ts new file mode 100644 index 00000000000..e315ea52304 --- /dev/null +++ b/packages/search/lib/commands/PROFILE.ts @@ -0,0 +1,26 @@ +export const IS_READ_ONLY = true; + +interface ProfileOptions { + LIMITED?: true; +} + +export function transformArguments( + index: string, + type: 'SEARCH' | 'AGGREGATE', + query: string, + options?: ProfileOptions +): Array { + const args = ['FT.PROFILE', index, type]; + + if (options?.LIMITED) { + args.push('LIMITED'); + } + + args.push('QUERY', query); + + return args; +} + +export function transformReply() { + +} diff --git a/packages/search/lib/commands/SEARCH.spec.ts b/packages/search/lib/commands/SEARCH.spec.ts new file mode 100644 index 00000000000..15dc4740948 --- /dev/null +++ b/packages/search/lib/commands/SEARCH.spec.ts @@ -0,0 +1,244 @@ +import { strict as assert } from 'assert'; +import { RedisSearchLanguages } from '.'; +import testUtils, { GLOBAL } from '../test-utils'; +import { SchemaFieldTypes } from './CREATE'; +import { transformArguments } from './SEARCH'; + +describe('SEARCH', () => { + describe('transformArguments', () => { + it('without options', () => { + assert.deepEqual( + transformArguments('index', 'query'), + ['FT.SEARCH', 'index', 'query'] + ); + }); + + it('with VERBATIM', () => { + assert.deepEqual( + transformArguments('index', 'query', { VERBATIM: true }), + ['FT.SEARCH', 'index', 'query', 'VERBATIM'] + ); + }); + + it('with NOSTOPWORDS', () => { + assert.deepEqual( + transformArguments('index', 'query', { NOSTOPWORDS: true }), + ['FT.SEARCH', 'index', 'query', 'NOSTOPWORDS'] + ); + }); + + it('with INKEYS', () => { + assert.deepEqual( + transformArguments('index', 'query', { INKEYS: 'key' }), + ['FT.SEARCH', 'index', 'query', 'INKEYS', '1', 'key'] + ); + }); + + it('with INFIELDS', () => { + assert.deepEqual( + transformArguments('index', 'query', { INFIELDS: 'field' }), + ['FT.SEARCH', 'index', 'query', 'INFIELDS', '1', 'field'] + ); + }); + + it('with RETURN', () => { + assert.deepEqual( + transformArguments('index', 'query', { RETURN: 'return' }), + ['FT.SEARCH', 'index', 'query', 'RETURN', '1', 'return'] + ); + }); + + describe('with SUMMARIZE', () => { + it('true', () => { + assert.deepEqual( + transformArguments('index', 'query', { SUMMARIZE: true }), + ['FT.SEARCH', 'index', 'query', 'SUMMARIZE'] + ); + }); + + describe('with FIELDS', () => { + it('string', () => { + assert.deepEqual( + transformArguments('index', 'query', { + SUMMARIZE: { + FIELDS: ['@field'] + } + }), + ['FT.SEARCH', 'index', 'query', 'SUMMARIZE', 'FIELDS', '1', '@field'] + ); + }); + + it('Array', () => { + assert.deepEqual( + transformArguments('index', 'query', { + SUMMARIZE: { + FIELDS: ['@1', '@2'] + } + }), + ['FT.SEARCH', 'index', 'query', 'SUMMARIZE', 'FIELDS', '2', '@1', '@2'] + ); + }); + }); + + it('with FRAGS', () => { + assert.deepEqual( + transformArguments('index', 'query', { + SUMMARIZE: { + FRAGS: 1 + } + }), + ['FT.SEARCH', 'index', 'query', 'SUMMARIZE', 'FRAGS', '1'] + ); + }); + + it('with LEN', () => { + assert.deepEqual( + transformArguments('index', 'query', { + SUMMARIZE: { + LEN: 1 + } + }), + ['FT.SEARCH', 'index', 'query', 'SUMMARIZE', 'LEN', '1'] + ); + }); + + it('with SEPARATOR', () => { + assert.deepEqual( + transformArguments('index', 'query', { + SUMMARIZE: { + SEPARATOR: 'separator' + } + }), + ['FT.SEARCH', 'index', 'query', 'SUMMARIZE', 'SEPARATOR', 'separator'] + ); + }); + }); + + describe('with HIGHLIGHT', () => { + it('true', () => { + assert.deepEqual( + transformArguments('index', 'query', { HIGHLIGHT: true }), + ['FT.SEARCH', 'index', 'query', 'HIGHLIGHT'] + ); + }); + + describe('with FIELDS', () => { + it('string', () => { + assert.deepEqual( + transformArguments('index', 'query', { + HIGHLIGHT: { + FIELDS: ['@field'] + } + }), + ['FT.SEARCH', 'index', 'query', 'HIGHLIGHT', 'FIELDS', '1', '@field'] + ); + }); + + it('Array', () => { + assert.deepEqual( + transformArguments('index', 'query', { + HIGHLIGHT: { + FIELDS: ['@1', '@2'] + } + }), + ['FT.SEARCH', 'index', 'query', 'HIGHLIGHT', 'FIELDS', '2', '@1', '@2'] + ); + }); + }); + + it('with TAGS', () => { + assert.deepEqual( + transformArguments('index', 'query', { + HIGHLIGHT: { + TAGS: { + open: 'open', + close: 'close' + } + } + }), + ['FT.SEARCH', 'index', 'query', 'HIGHLIGHT', 'TAGS', 'open', 'close'] + ); + }); + }); + + it('with SLOP', () => { + assert.deepEqual( + transformArguments('index', 'query', { SLOP: 1 }), + ['FT.SEARCH', 'index', 'query', 'SLOP', '1'] + ); + }); + + it('with INORDER', () => { + assert.deepEqual( + transformArguments('index', 'query', { INORDER: true }), + ['FT.SEARCH', 'index', 'query', 'INORDER'] + ); + }); + + it('with LANGUAGE', () => { + assert.deepEqual( + transformArguments('index', 'query', { LANGUAGE: RedisSearchLanguages.ARABIC }), + ['FT.SEARCH', 'index', 'query', 'LANGUAGE', RedisSearchLanguages.ARABIC] + ); + }); + + it('with EXPANDER', () => { + assert.deepEqual( + transformArguments('index', 'query', { EXPANDER: 'expender' }), + ['FT.SEARCH', 'index', 'query', 'EXPANDER', 'expender'] + ); + }); + + it('with SCORER', () => { + assert.deepEqual( + transformArguments('index', 'query', { SCORER: 'scorer' }), + ['FT.SEARCH', 'index', 'query', 'SCORER', 'scorer'] + ); + }); + + it('with MSORTBY', () => { + assert.deepEqual( + transformArguments('index', 'query', { MSORTBY: '@by' }), + ['FT.SEARCH', 'index', 'query', 'MSORTBY', '1', '@by'] + ); + }); + + it('with LIMIT', () => { + assert.deepEqual( + transformArguments('index', 'query', { + LIMIT: { + from: 0, + size: 1 + } + }), + ['FT.SEARCH', 'index', 'query', 'LIMIT', '0', '1'] + ); + }); + }); + + testUtils.testWithClient('client.ft.search', async client => { + await Promise.all([ + client.ft.create('index', { + field: SchemaFieldTypes.NUMERIC + }), + client.hSet('1', 'field', '1') + ]); + + assert.deepEqual( + await client.ft.search('index', '*'), + { + total: 1, + documents: [{ + id: '1', + value: Object.create(null, { + field: { + value: '1', + configurable: true, + enumerable: true + } + }) + }] + } + ); + }, GLOBAL.SERVERS.OPEN); +}); diff --git a/packages/search/lib/commands/SEARCH.ts b/packages/search/lib/commands/SEARCH.ts new file mode 100644 index 00000000000..9616bfa0bee --- /dev/null +++ b/packages/search/lib/commands/SEARCH.ts @@ -0,0 +1,200 @@ +import { RedisCommandArguments } from '@node-redis/client/dist/lib/commands'; +import { pushOptionalVerdictArgument, pushVerdictArgument, transformReplyTuples } from '@node-redis/client/dist/lib/commands/generic-transformers'; +import { RedisSearchLanguages, PropertyName, pushSortByArguments, SortByOptions } from '.'; + +export const FIRST_KEY_INDEX = 1; + +export const IS_READ_ONLY = true; + +interface SearchOptions { + // NOCONTENT?: true; TODO + VERBATIM?: true; + NOSTOPWORDS?: true; + // WITHSCORES?: true; + // WITHPAYLOADS?: true; + WITHSORTKEYS?: true; + // FILTER?: { + // field: string; + // min: number | string; + // max: number | string; + // }; + // GEOFILTER?: { + // field: string; + // lon: number; + // lat: number; + // radius: number; + // unit: 'm' | 'km' | 'mi' | 'ft'; + // }; + INKEYS?: string | Array; + INFIELDS?: string | Array; + RETURN?: string | Array; + SUMMARIZE?: true | { + FIELDS?: PropertyName | Array; + FRAGS?: number; + LEN?: number; + SEPARATOR?: string; + }; + HIGHLIGHT?: true | { + FIELDS?: PropertyName | Array; + TAGS?: { + open: string; + close: string; + }; + }; + SLOP?: number; + INORDER?: true; + LANGUAGE?: RedisSearchLanguages; + EXPANDER?: string; + SCORER?: string; + // EXPLAINSCORE?: true; // TODO: WITHSCORES + // PAYLOAD?: ; + // SORTBY?: SortByOptions; + MSORTBY?: SortByOptions | Array; + LIMIT?: { + from: number | string; + size: number | string; + }; +} + +export function transformArguments( + index: string, + query: string, + options?: SearchOptions +): RedisCommandArguments { + const args: RedisCommandArguments = ['FT.SEARCH', index, query]; + + if (options?.VERBATIM) { + args.push('VERBATIM'); + } + + if (options?.NOSTOPWORDS) { + args.push('NOSTOPWORDS'); + } + + // if (options?.WITHSCORES) { + // args.push('WITHSCORES'); + // } + + // if (options?.WITHPAYLOADS) { + // args.push('WITHPAYLOADS'); + // } + + pushOptionalVerdictArgument(args, 'INKEYS', options?.INKEYS); + pushOptionalVerdictArgument(args, 'INFIELDS', options?.INFIELDS); + pushOptionalVerdictArgument(args, 'RETURN', options?.RETURN); + + if (options?.SUMMARIZE) { + args.push('SUMMARIZE'); + + if (typeof options.SUMMARIZE === 'object') { + if (options.SUMMARIZE.FIELDS) { + args.push('FIELDS'); + pushVerdictArgument(args, options.SUMMARIZE.FIELDS); + } + + if (options.SUMMARIZE.FRAGS) { + args.push('FRAGS', options.SUMMARIZE.FRAGS.toString()); + } + + if (options.SUMMARIZE.LEN) { + args.push('LEN', options.SUMMARIZE.LEN.toString()); + } + + if (options.SUMMARIZE.SEPARATOR) { + args.push('SEPARATOR', options.SUMMARIZE.SEPARATOR); + } + } + } + + if (options?.HIGHLIGHT) { + args.push('HIGHLIGHT'); + + if (typeof options.HIGHLIGHT === 'object') { + if (options.HIGHLIGHT.FIELDS) { + args.push('FIELDS'); + pushVerdictArgument(args, options.HIGHLIGHT.FIELDS); + } + + if (options.HIGHLIGHT.TAGS) { + args.push('TAGS', options.HIGHLIGHT.TAGS.open, options.HIGHLIGHT.TAGS.close); + } + } + } + + if (options?.SLOP) { + args.push('SLOP', options.SLOP.toString()); + } + + if (options?.INORDER) { + args.push('INORDER'); + } + + if (options?.LANGUAGE) { + args.push('LANGUAGE', options.LANGUAGE); + } + + if (options?.EXPANDER) { + args.push('EXPANDER', options.EXPANDER); + } + + if (options?.SCORER) { + args.push('SCORER', options.SCORER); + } + + // if (options?.EXPLAINSCORE) { + // args.push('EXPLAINSCORE'); + // } + + // if (options?.PAYLOAD) { + // args.push('PAYLOAD', options.PAYLOAD); + // } + + // if (options?.SORTBY) { + // args.push('SORTBY'); + // pushSortByArguments(args, options.SORTBY); + // } + + if (options?.MSORTBY) { + pushSortByArguments(args, 'MSORTBY', options.MSORTBY); + } + + if (options?.LIMIT) { + args.push( + 'LIMIT', + options.LIMIT.from.toString(), + options.LIMIT.size.toString() + ); + } + + return args; +} + +interface SearchDocumentValue { + [key: string]: string | number | null | Array | SearchDocumentValue; +} + +interface SearchReply { + total: number; + documents: Array<{ + id: string; + value: SearchDocumentValue; + }>; +} + +export function transformReply(reply: Array): SearchReply { + const documents = []; + for (let i = 1; i < reply.length; i += 2) { + const tuples = reply[i + 1]; + documents.push({ + id: reply[i], + value: tuples.length === 2 && tuples[0] === '$' ? + JSON.parse(tuples[1]) : + transformReplyTuples(tuples) + }); + } + + return { + total: reply[0], + documents + }; +} diff --git a/packages/search/lib/commands/SPELLCHECK.spec.ts b/packages/search/lib/commands/SPELLCHECK.spec.ts new file mode 100644 index 00000000000..bacbe118b38 --- /dev/null +++ b/packages/search/lib/commands/SPELLCHECK.spec.ts @@ -0,0 +1,71 @@ +import { strict as assert } from 'assert'; +import testUtils, { GLOBAL } from '../test-utils'; +import { SchemaFieldTypes } from './CREATE'; +import { transformArguments } from './SPELLCHECK'; + +describe('SPELLCHECK', () => { + describe('transformArguments', () => { + it('without options', () => { + assert.deepEqual( + transformArguments('index', 'query'), + ['FT.SPELLCHECK', 'index', 'query'] + ); + }); + + it('with DISTANCE', () => { + assert.deepEqual( + transformArguments('index', 'query', { DISTANCE: 2 }), + ['FT.SPELLCHECK', 'index', 'query', 'DISTANCE', '2'] + ); + }); + + describe('with TERMS', () => { + it('single', () => { + assert.deepEqual( + transformArguments('index', 'query', { + TERMS: { + mode: 'INCLUDE', + dictionary: 'dictionary' + } + }), + ['FT.SPELLCHECK', 'index', 'query', 'TERMS', 'INCLUDE', 'dictionary'] + ); + }); + + it('multiple', () => { + assert.deepEqual( + transformArguments('index', 'query', { + TERMS: [{ + mode: 'INCLUDE', + dictionary: 'include' + }, { + mode: 'EXCLUDE', + dictionary: 'exclude' + }] + }), + ['FT.SPELLCHECK', 'index', 'query', 'TERMS', 'INCLUDE', 'include', 'TERMS', 'EXCLUDE', 'exclude'] + ); + }); + }); + }); + + testUtils.testWithClient('client.ft.spellCheck', async client => { + await Promise.all([ + client.ft.create('index', { + field: SchemaFieldTypes.TEXT + }), + client.hSet('key', 'field', 'query') + ]); + + assert.deepEqual( + await client.ft.spellCheck('index', 'quer'), + [{ + term: 'quer', + suggestions: [{ + score: 1, + suggestion: 'query' + }] + }] + ); + }, GLOBAL.SERVERS.OPEN); +}); diff --git a/packages/search/lib/commands/SPELLCHECK.ts b/packages/search/lib/commands/SPELLCHECK.ts new file mode 100644 index 00000000000..ae4cb3cdce1 --- /dev/null +++ b/packages/search/lib/commands/SPELLCHECK.ts @@ -0,0 +1,57 @@ +interface SpellCheckTerms { + mode: 'INCLUDE' | 'EXCLUDE'; + dictionary: string; +} + +interface SpellCheckOptions { + DISTANCE?: number; + TERMS?: SpellCheckTerms | Array; +} + +export function transformArguments(index: string, query: string, options?: SpellCheckOptions): Array { + const args = ['FT.SPELLCHECK', index, query]; + + if (options?.DISTANCE) { + args.push('DISTANCE', options.DISTANCE.toString()); + } + + if (options?.TERMS) { + if (Array.isArray(options.TERMS)) { + for (const term of options.TERMS) { + pushTerms(args, term); + } + } else { + pushTerms(args, options.TERMS); + } + } + + return args; +} + +function pushTerms(args: Array, { mode, dictionary }: SpellCheckTerms): void { + args.push('TERMS', mode, dictionary); +} + +type SpellCheckRawReply = Array<[ + _: string, + term: string, + suggestions: Array<[score: string, suggestion: string]> +]>; + +type SpellCheckReply = Array<{ + term: string, + suggestions: Array<{ + score: number, + suggestion: string + }> +}>; + +export function transformReply(rawReply: SpellCheckRawReply): SpellCheckReply { + return rawReply.map(([, term, suggestions]) => ({ + term, + suggestions: suggestions.map(([score, suggestion]) => ({ + score: Number(score), + suggestion + })) + })); +} diff --git a/packages/search/lib/commands/SUGADD.spec.ts b/packages/search/lib/commands/SUGADD.spec.ts new file mode 100644 index 00000000000..23294eb4abd --- /dev/null +++ b/packages/search/lib/commands/SUGADD.spec.ts @@ -0,0 +1,35 @@ +import { strict as assert } from 'assert'; +import testUtils, { GLOBAL } from '../test-utils'; +import { transformArguments } from './SUGADD'; + +describe('SUGADD', () => { + describe('transformArguments', () => { + it('without options', () => { + assert.deepEqual( + transformArguments('key', 'string', 1), + ['FT.SUGADD', 'key', 'string', '1'] + ); + }); + + it('with INCR', () => { + assert.deepEqual( + transformArguments('key', 'string', 1, { INCR: true }), + ['FT.SUGADD', 'key', 'string', '1', 'INCR'] + ); + }); + + it('with PAYLOAD', () => { + assert.deepEqual( + transformArguments('key', 'string', 1, { PAYLOAD: 'payload' }), + ['FT.SUGADD', 'key', 'string', '1', 'PAYLOAD', 'payload'] + ); + }); + }); + + testUtils.testWithClient('client.ft.sugAdd', async client => { + assert.equal( + await client.ft.sugAdd('key', 'string', 1), + 1 + ); + }, GLOBAL.SERVERS.OPEN); +}); diff --git a/packages/search/lib/commands/SUGADD.ts b/packages/search/lib/commands/SUGADD.ts new file mode 100644 index 00000000000..d68f0d98841 --- /dev/null +++ b/packages/search/lib/commands/SUGADD.ts @@ -0,0 +1,20 @@ +interface SugAddOptions { + INCR?: true; + PAYLOAD?: string; +} + +export function transformArguments(key: string, string: string, score: number, options?: SugAddOptions): Array { + const args = ['FT.SUGADD', key, string, score.toString()]; + + if (options?.INCR) { + args.push('INCR'); + } + + if (options?.PAYLOAD) { + args.push('PAYLOAD', options.PAYLOAD); + } + + return args; +} + +export declare function transformReply(): number; diff --git a/packages/search/lib/commands/SUGDEL.spec.ts b/packages/search/lib/commands/SUGDEL.spec.ts new file mode 100644 index 00000000000..3d89e3b9a72 --- /dev/null +++ b/packages/search/lib/commands/SUGDEL.spec.ts @@ -0,0 +1,19 @@ +import { strict as assert } from 'assert'; +import testUtils, { GLOBAL } from '../test-utils'; +import { transformArguments } from './SUGDEL'; + +describe('SUGDEL', () => { + it('transformArguments', () => { + assert.deepEqual( + transformArguments('key', 'string'), + ['FT.SUGDEL', 'key', 'string'] + ); + }); + + testUtils.testWithClient('client.ft.sugDel', async client => { + assert.equal( + await client.ft.sugDel('key', 'string'), + false + ); + }, GLOBAL.SERVERS.OPEN); +}); diff --git a/packages/search/lib/commands/SUGDEL.ts b/packages/search/lib/commands/SUGDEL.ts new file mode 100644 index 00000000000..43f4744c00d --- /dev/null +++ b/packages/search/lib/commands/SUGDEL.ts @@ -0,0 +1,5 @@ +export function transformArguments(key: string, string: string): Array { + return ['FT.SUGDEL', key, string]; +} + +export { transformReplyBoolean as transformReply } from '@node-redis/client/dist/lib/commands/generic-transformers'; diff --git a/packages/search/lib/commands/SUGGET.spec.ts b/packages/search/lib/commands/SUGGET.spec.ts new file mode 100644 index 00000000000..c24c2ff0863 --- /dev/null +++ b/packages/search/lib/commands/SUGGET.spec.ts @@ -0,0 +1,46 @@ +import { strict as assert } from 'assert'; +import testUtils, { GLOBAL } from '../test-utils'; +import { transformArguments } from './SUGGET'; + +describe('SUGGET', () => { + describe('transformArguments', () => { + it('without options', () => { + assert.deepEqual( + transformArguments('key', 'prefix'), + ['FT.SUGGET', 'key', 'prefix'] + ); + }); + + it('with FUZZY', () => { + assert.deepEqual( + transformArguments('key', 'prefix', { FUZZY: true }), + ['FT.SUGGET', 'key', 'prefix', 'FUZZY'] + ); + }); + + it('with MAX', () => { + assert.deepEqual( + transformArguments('key', 'prefix', { MAX: 10 }), + ['FT.SUGGET', 'key', 'prefix', 'MAX', '10'] + ); + }); + }); + + describe('client.ft.sugGet', () => { + testUtils.testWithClient('null', async client => { + assert.equal( + await client.ft.sugGet('key', 'prefix'), + null + ); + }, GLOBAL.SERVERS.OPEN); + + testUtils.testWithClient('with suggestions', async client => { + await client.ft.sugAdd('key', 'string', 1); + + assert.deepEqual( + await client.ft.sugGet('key', 'string'), + ['string'] + ); + }, GLOBAL.SERVERS.OPEN); + }); +}); diff --git a/packages/search/lib/commands/SUGGET.ts b/packages/search/lib/commands/SUGGET.ts new file mode 100644 index 00000000000..558cedeaa08 --- /dev/null +++ b/packages/search/lib/commands/SUGGET.ts @@ -0,0 +1,22 @@ +export const IS_READ_ONLY = true; + +export interface SugGetOptions { + FUZZY?: true; + MAX?: number; +} + +export function transformArguments(key: string, prefix: string, options?: SugGetOptions): Array { + const args = ['FT.SUGGET', key, prefix]; + + if (options?.FUZZY) { + args.push('FUZZY'); + } + + if (options?.MAX) { + args.push('MAX', options.MAX.toString()); + } + + return args; +} + +export declare function transformReply(): null | Array; diff --git a/packages/search/lib/commands/SUGGET_WITHPAYLOADS.spec.ts b/packages/search/lib/commands/SUGGET_WITHPAYLOADS.spec.ts new file mode 100644 index 00000000000..a4a87ebe895 --- /dev/null +++ b/packages/search/lib/commands/SUGGET_WITHPAYLOADS.spec.ts @@ -0,0 +1,33 @@ +import { strict as assert } from 'assert'; +import testUtils, { GLOBAL } from '../test-utils'; +import { transformArguments } from './SUGGET_WITHPAYLOADS'; + +describe('SUGGET WITHPAYLOADS', () => { + it('transformArguments', () => { + assert.deepEqual( + transformArguments('key', 'prefix'), + ['FT.SUGGET', 'key', 'prefix', 'WITHPAYLOADS'] + ); + }); + + describe('client.ft.sugGetWithPayloads', () => { + testUtils.testWithClient('null', async client => { + assert.equal( + await client.ft.sugGetWithPayloads('key', 'prefix'), + null + ); + }, GLOBAL.SERVERS.OPEN); + + testUtils.testWithClient('with suggestions', async client => { + await client.ft.sugAdd('key', 'string', 1, { PAYLOAD: 'payload' }); + + assert.deepEqual( + await client.ft.sugGetWithPayloads('key', 'string'), + [{ + suggestion: 'string', + payload: 'payload' + }] + ); + }, GLOBAL.SERVERS.OPEN); + }); +}); diff --git a/packages/search/lib/commands/SUGGET_WITHPAYLOADS.ts b/packages/search/lib/commands/SUGGET_WITHPAYLOADS.ts new file mode 100644 index 00000000000..7eaff4697e1 --- /dev/null +++ b/packages/search/lib/commands/SUGGET_WITHPAYLOADS.ts @@ -0,0 +1,29 @@ +import { SugGetOptions, transformArguments as transformSugGetArguments } from './SUGGET'; + +export { IS_READ_ONLY } from './SUGGET'; + +export function transformArguments(key: string, prefix: string, options?: SugGetOptions): Array { + return [ + ...transformSugGetArguments(key, prefix, options), + 'WITHPAYLOADS' + ]; +} + +export interface SuggestionWithPayload { + suggestion: string; + payload: string | null; +} + +export function transformReply(rawReply: Array | null): Array | null { + if (rawReply === null) return null; + + const transformedReply = []; + for (let i = 0; i < rawReply.length; i += 2) { + transformedReply.push({ + suggestion: rawReply[i]!, + payload: rawReply[i + 1] + }); + } + + return transformedReply; +} diff --git a/packages/search/lib/commands/SUGGET_WITHSCORES.spec.ts b/packages/search/lib/commands/SUGGET_WITHSCORES.spec.ts new file mode 100644 index 00000000000..e60daa917a9 --- /dev/null +++ b/packages/search/lib/commands/SUGGET_WITHSCORES.spec.ts @@ -0,0 +1,33 @@ +import { strict as assert } from 'assert'; +import testUtils, { GLOBAL } from '../test-utils'; +import { transformArguments } from './SUGGET_WITHSCORES'; + +describe('SUGGET WITHSCORES', () => { + it('transformArguments', () => { + assert.deepEqual( + transformArguments('key', 'prefix'), + ['FT.SUGGET', 'key', 'prefix', 'WITHSCORES'] + ); + }); + + describe('client.ft.sugGetWithScores', () => { + testUtils.testWithClient('null', async client => { + assert.equal( + await client.ft.sugGetWithScores('key', 'prefix'), + null + ); + }, GLOBAL.SERVERS.OPEN); + + testUtils.testWithClient('with suggestions', async client => { + await client.ft.sugAdd('key', 'string', 1); + + assert.deepEqual( + await client.ft.sugGetWithScores('key', 'string'), + [{ + suggestion: 'string', + score: 2147483648 + }] + ); + }, GLOBAL.SERVERS.OPEN); + }); +}); diff --git a/packages/search/lib/commands/SUGGET_WITHSCORES.ts b/packages/search/lib/commands/SUGGET_WITHSCORES.ts new file mode 100644 index 00000000000..bad5bff2999 --- /dev/null +++ b/packages/search/lib/commands/SUGGET_WITHSCORES.ts @@ -0,0 +1,29 @@ +import { SugGetOptions, transformArguments as transformSugGetArguments } from './SUGGET'; + +export { IS_READ_ONLY } from './SUGGET'; + +export function transformArguments(key: string, prefix: string, options?: SugGetOptions): Array { + return [ + ...transformSugGetArguments(key, prefix, options), + 'WITHSCORES' + ]; +} + +export interface SuggestionWithScores { + suggestion: string; + score: number; +} + +export function transformReply(rawReply: Array | null): Array | null { + if (rawReply === null) return null; + + const transformedReply = []; + for (let i = 0; i < rawReply.length; i += 2) { + transformedReply.push({ + suggestion: rawReply[i], + score: Number(rawReply[i + 1]) + }); + } + + return transformedReply; +} diff --git a/packages/search/lib/commands/SUGGET_WITHSCORES_WITHPAYLOADS.spec.ts b/packages/search/lib/commands/SUGGET_WITHSCORES_WITHPAYLOADS.spec.ts new file mode 100644 index 00000000000..0900d91b8d9 --- /dev/null +++ b/packages/search/lib/commands/SUGGET_WITHSCORES_WITHPAYLOADS.spec.ts @@ -0,0 +1,34 @@ +import { strict as assert } from 'assert'; +import testUtils, { GLOBAL } from '../test-utils'; +import { transformArguments } from './SUGGET_WITHSCORES_WITHPAYLOADS'; + +describe('SUGGET WITHSCORES WITHPAYLOADS', () => { + it('transformArguments', () => { + assert.deepEqual( + transformArguments('key', 'prefix'), + ['FT.SUGGET', 'key', 'prefix', 'WITHSCORES', 'WITHPAYLOADS'] + ); + }); + + describe('client.ft.sugGetWithScoresWithPayloads', () => { + testUtils.testWithClient('null', async client => { + assert.equal( + await client.ft.sugGetWithScoresWithPayloads('key', 'prefix'), + null + ); + }, GLOBAL.SERVERS.OPEN); + + testUtils.testWithClient('with suggestions', async client => { + await client.ft.sugAdd('key', 'string', 1, { PAYLOAD: 'payload' }); + + assert.deepEqual( + await client.ft.sugGetWithScoresWithPayloads('key', 'string'), + [{ + suggestion: 'string', + score: 2147483648, + payload: 'payload' + }] + ); + }, GLOBAL.SERVERS.OPEN); + }); +}); diff --git a/packages/search/lib/commands/SUGGET_WITHSCORES_WITHPAYLOADS.ts b/packages/search/lib/commands/SUGGET_WITHSCORES_WITHPAYLOADS.ts new file mode 100644 index 00000000000..3b2fe7667b7 --- /dev/null +++ b/packages/search/lib/commands/SUGGET_WITHSCORES_WITHPAYLOADS.ts @@ -0,0 +1,30 @@ +import { SugGetOptions, transformArguments as transformSugGetArguments } from './SUGGET'; +import { SuggestionWithPayload } from './SUGGET_WITHPAYLOADS'; +import { SuggestionWithScores } from './SUGGET_WITHSCORES'; + +export { IS_READ_ONLY } from './SUGGET'; + +export function transformArguments(key: string, prefix: string, options?: SugGetOptions): Array { + return [ + ...transformSugGetArguments(key, prefix, options), + 'WITHSCORES', + 'WITHPAYLOADS' + ]; +} + +type SuggestionWithScoresAndPayloads = SuggestionWithScores & SuggestionWithPayload; + +export function transformReply(rawReply: Array | null): Array | null { + if (rawReply === null) return null; + + const transformedReply = []; + for (let i = 0; i < rawReply.length; i += 3) { + transformedReply.push({ + suggestion: rawReply[i]!, + score: Number(rawReply[i + 1]!), + payload: rawReply[i + 2] + }); + } + + return transformedReply; +} diff --git a/packages/search/lib/commands/SUGLEN.spec.ts b/packages/search/lib/commands/SUGLEN.spec.ts new file mode 100644 index 00000000000..2ea680df953 --- /dev/null +++ b/packages/search/lib/commands/SUGLEN.spec.ts @@ -0,0 +1,19 @@ +import { strict as assert } from 'assert'; +import testUtils, { GLOBAL } from '../test-utils'; +import { transformArguments } from './SUGLEN'; + +describe('SUGLEN', () => { + it('transformArguments', () => { + assert.deepEqual( + transformArguments('key'), + ['FT.SUGLEN', 'key'] + ); + }); + + testUtils.testWithClient('client.ft.sugLen', async client => { + assert.equal( + await client.ft.sugLen('key'), + 0 + ); + }, GLOBAL.SERVERS.OPEN); +}); diff --git a/packages/search/lib/commands/SUGLEN.ts b/packages/search/lib/commands/SUGLEN.ts new file mode 100644 index 00000000000..15b3da61261 --- /dev/null +++ b/packages/search/lib/commands/SUGLEN.ts @@ -0,0 +1,7 @@ +export const IS_READ_ONLY = true; + +export function transformArguments(key: string): Array { + return ['FT.SUGLEN', key]; +} + +export declare function transformReply(): number; diff --git a/packages/search/lib/commands/SYNDUMP.spec.ts b/packages/search/lib/commands/SYNDUMP.spec.ts new file mode 100644 index 00000000000..4b0cb0c8b3a --- /dev/null +++ b/packages/search/lib/commands/SYNDUMP.spec.ts @@ -0,0 +1,23 @@ +import { strict as assert } from 'assert'; +import testUtils, { GLOBAL } from '../test-utils'; +import { transformArguments } from './SYNDUMP'; + +describe('SYNDUMP', () => { + it('transformArguments', () => { + assert.deepEqual( + transformArguments('index'), + ['FT.SYNDUMP', 'index'] + ); + }); + + testUtils.testWithClient('client.ft.synDump', async client => { + await client.ft.create('index', {}, { + ON: 'HASH' // TODO: shouldn't be mandatory + }); + + assert.deepEqual( + await client.ft.synDump('index'), + [] + ); + }, GLOBAL.SERVERS.OPEN); +}); diff --git a/packages/search/lib/commands/SYNDUMP.ts b/packages/search/lib/commands/SYNDUMP.ts new file mode 100644 index 00000000000..5f1e71aaf78 --- /dev/null +++ b/packages/search/lib/commands/SYNDUMP.ts @@ -0,0 +1,5 @@ +export function transformArguments(index: string): Array { + return ['FT.SYNDUMP', index]; +} + +export declare function transformReply(): Array; diff --git a/packages/search/lib/commands/SYNUPDATE.spec.ts b/packages/search/lib/commands/SYNUPDATE.spec.ts new file mode 100644 index 00000000000..bf7fed84934 --- /dev/null +++ b/packages/search/lib/commands/SYNUPDATE.spec.ts @@ -0,0 +1,39 @@ +import { strict as assert } from 'assert'; +import testUtils, { GLOBAL } from '../test-utils'; +import { transformArguments } from './SYNUPDATE'; + +describe('SYNUPDATE', () => { + describe('transformArguments', () => { + it('single term', () => { + assert.deepEqual( + transformArguments('index', 'groupId', 'term'), + ['FT.SYNUPDATE', 'index', 'groupId', 'term'] + ); + }); + + it('multiple terms', () => { + assert.deepEqual( + transformArguments('index', 'groupId', ['1', '2']), + ['FT.SYNUPDATE', 'index', 'groupId', '1', '2'] + ); + }); + + it('with SKIPINITIALSCAN', () => { + assert.deepEqual( + transformArguments('index', 'groupId', 'term', { SKIPINITIALSCAN: true }), + ['FT.SYNUPDATE', 'index', 'groupId', 'SKIPINITIALSCAN', 'term'] + ); + }); + }); + + testUtils.testWithClient('client.ft.synUpdate', async client => { + await client.ft.create('index', {}, { + ON: 'HASH' // TODO: shouldn't be mandatory + }); + + assert.equal( + await client.ft.synUpdate('index', 'groupId', 'term'), + 'OK' + ); + }, GLOBAL.SERVERS.OPEN); +}); diff --git a/packages/search/lib/commands/SYNUPDATE.ts b/packages/search/lib/commands/SYNUPDATE.ts new file mode 100644 index 00000000000..b59d35617fa --- /dev/null +++ b/packages/search/lib/commands/SYNUPDATE.ts @@ -0,0 +1,23 @@ +import { pushVerdictArguments } from '@node-redis/client/dist/lib/commands/generic-transformers'; +import { RedisCommandArguments } from '@node-redis/client/dist/lib/commands'; + +interface SynUpdateOptions { + SKIPINITIALSCAN?: true; +} + +export function transformArguments( + index: string, + groupId: string, + terms: string | Array, + options?: SynUpdateOptions +): RedisCommandArguments { + const args = ['FT.SYNUPDATE', index, groupId]; + + if (options?.SKIPINITIALSCAN) { + args.push('SKIPINITIALSCAN'); + } + + return pushVerdictArguments(args, terms); +} + +export declare function transformReply(): 'OK'; diff --git a/packages/search/lib/commands/TAGVALS.spec.ts b/packages/search/lib/commands/TAGVALS.spec.ts new file mode 100644 index 00000000000..1f90939bb0d --- /dev/null +++ b/packages/search/lib/commands/TAGVALS.spec.ts @@ -0,0 +1,24 @@ +import { strict as assert } from 'assert'; +import testUtils, { GLOBAL } from '../test-utils'; +import { SchemaFieldTypes } from './CREATE'; +import { transformArguments } from './TAGVALS'; + +describe('TAGVALS', () => { + it('transformArguments', () => { + assert.deepEqual( + transformArguments('index', '@field'), + ['FT.TAGVALS', 'index', '@field'] + ); + }); + + testUtils.testWithClient('client.ft.tagVals', async client => { + await client.ft.create('index', { + field: SchemaFieldTypes.TAG + }); + + assert.deepEqual( + await client.ft.tagVals('index', 'field'), + [] + ); + }, GLOBAL.SERVERS.OPEN); +}); diff --git a/packages/search/lib/commands/TAGVALS.ts b/packages/search/lib/commands/TAGVALS.ts new file mode 100644 index 00000000000..54342f0c9e5 --- /dev/null +++ b/packages/search/lib/commands/TAGVALS.ts @@ -0,0 +1,5 @@ +export function transformArguments(index: string, fieldName: string): Array { + return ['FT.TAGVALS', index, fieldName]; +} + +export declare function transformReply(): Array; diff --git a/packages/search/lib/commands/_LIST.spec.ts b/packages/search/lib/commands/_LIST.spec.ts new file mode 100644 index 00000000000..602c29975f2 --- /dev/null +++ b/packages/search/lib/commands/_LIST.spec.ts @@ -0,0 +1,19 @@ +import { strict as assert } from 'assert'; +import testUtils, { GLOBAL } from '../test-utils'; +import { transformArguments } from './_LIST'; + +describe('_LIST', () => { + it('transformArguments', () => { + assert.deepEqual( + transformArguments(), + ['FT._LIST'] + ); + }); + + testUtils.testWithClient('client.ft._list', async client => { + assert.deepEqual( + await client.ft._list(), + [] + ); + }, GLOBAL.SERVERS.OPEN); +}); diff --git a/packages/search/lib/commands/_LIST.ts b/packages/search/lib/commands/_LIST.ts new file mode 100644 index 00000000000..588ec837c3b --- /dev/null +++ b/packages/search/lib/commands/_LIST.ts @@ -0,0 +1,5 @@ +export function transformArguments(): Array { + return ['FT._LIST']; +} + +export declare function transformReply(): Array; diff --git a/packages/search/lib/commands/index.spec.ts b/packages/search/lib/commands/index.spec.ts new file mode 100644 index 00000000000..f0afa304a92 --- /dev/null +++ b/packages/search/lib/commands/index.spec.ts @@ -0,0 +1,46 @@ +import { strict as assert } from 'assert'; +import { pushArgumentsWithLength, pushSortByArguments } from '.'; + +describe('pushSortByArguments', () => { + describe('single', () => { + it('string', () => { + assert.deepEqual( + pushSortByArguments([], 'SORTBT', '@property'), + ['SORTBT', '1', '@property'] + ); + }); + + it('.BY', () => { + assert.deepEqual( + pushSortByArguments([], 'SORTBT', { BY: '@property' }), + ['SORTBT', '1', '@property'] + ); + }); + + it('with DIRECTION', () => { + assert.deepEqual( + pushSortByArguments([], 'SORTBY', { + BY: '@property', + DIRECTION: 'ASC' + }), + ['SORTBY', '2', '@property', 'ASC'] + ); + }); + }); + + it('multiple', () => { + assert.deepEqual( + pushSortByArguments([], 'SORTBY', ['@1', '@2']), + ['SORTBY', '2', '@1', '@2'] + ); + }); +}); + +it('pushArgumentsWithLength', () => { + assert.deepEqual( + pushArgumentsWithLength(['a'], args => { + args.push('b', 'c'); + }), + ['a', '2', 'b', 'c'] + ); +}); diff --git a/packages/search/lib/commands/index.ts b/packages/search/lib/commands/index.ts new file mode 100644 index 00000000000..70626c00df0 --- /dev/null +++ b/packages/search/lib/commands/index.ts @@ -0,0 +1,161 @@ +import * as _LIST from './_LIST'; +import * as AGGREGATE from './AGGREGATE'; +import * as ALIASADD from './ALIASADD'; +import * as ALIASDEL from './ALIASDEL'; +import * as ALIASUPDATE from './ALIASUPDATE'; +import * as CONFIG_GET from './CONFIG_GET'; +import * as CONFIG_SET from './CONFIG_SET'; +import * as CREATE from './CREATE'; +import * as DICTADD from './DICTADD'; +import * as DICTDEL from './DICTDEL'; +import * as DICTDUMP from './DICTDUMP'; +import * as DROPINDEX from './DROPINDEX'; +import * as EXPLAIN from './EXPLAIN'; +import * as EXPLAINCLI from './EXPLAINCLI'; +import * as INFO from './INFO'; +// import * as PROFILE from './PROFILE'; +import * as SEARCH from './SEARCH'; +import * as SPELLCHECK from './SPELLCHECK'; +import * as SUGADD from './SUGADD'; +import * as SUGDEL from './SUGDEL'; +import * as SUGGET_WITHPAYLOADS from './SUGGET_WITHPAYLOADS'; +import * as SUGGET_WITHSCORES_WITHPAYLOADS from './SUGGET_WITHSCORES_WITHPAYLOADS'; +import * as SUGGET_WITHSCORES from './SUGGET_WITHSCORES'; +import * as SUGGET from './SUGGET'; +import * as SUGLEN from './SUGLEN'; +import * as SYNDUMP from './SYNDUMP'; +import * as SYNUPDATE from './SYNUPDATE'; +import * as TAGVALS from './TAGVALS'; +import { RedisCommandArguments } from '@node-redis/client/dist/lib/commands'; + +export default { + _LIST, + _list: _LIST, + AGGREGATE, + aggregate: AGGREGATE, + ALIASADD, + aliasAdd: ALIASADD, + ALIASDEL, + aliasDel: ALIASDEL, + ALIASUPDATE, + aliasUpdate: ALIASUPDATE, + CONFIG_GET, + configGet: CONFIG_GET, + CONFIG_SET, + configSet: CONFIG_SET, + CREATE, + create: CREATE, + DICTADD, + dictAdd: DICTADD, + DICTDEL, + dictDel: DICTDEL, + DICTDUMP, + dictDump: DICTDUMP, + DROPINDEX, + dropIndex: DROPINDEX, + EXPLAIN, + explain: EXPLAIN, + EXPLAINCLI, + explainCli: EXPLAINCLI, + INFO, + info: INFO, + // PROFILE, + // profile: PROFILE, + SEARCH, + search: SEARCH, + SPELLCHECK, + spellCheck: SPELLCHECK, + SUGADD, + sugAdd: SUGADD, + SUGDEL, + sugDel: SUGDEL, + SUGGET_WITHPAYLOADS, + sugGetWithPayloads: SUGGET_WITHPAYLOADS, + SUGGET_WITHSCORES_WITHPAYLOADS, + sugGetWithScoresWithPayloads: SUGGET_WITHSCORES_WITHPAYLOADS, + SUGGET_WITHSCORES, + sugGetWithScores: SUGGET_WITHSCORES, + SUGGET, + sugGet: SUGGET, + SUGLEN, + sugLen: SUGLEN, + SYNDUMP, + synDump: SYNDUMP, + SYNUPDATE, + synUpdate: SYNUPDATE, + TAGVALS, + tagVals: TAGVALS +}; + +export enum RedisSearchLanguages { + ARABIC = 'Arabic', + BASQUE = 'Basque', + CATALANA = 'Catalan', + DANISH = 'Danish', + DUTCH = 'Dutch', + ENGLISH = 'English', + FINNISH = 'Finnish', + FRENCH = 'French', + GERMAN = 'German', + GREEK = 'Greek', + HUNGARIAN = 'Hungarian', + INDONESAIN = 'Indonesian', + IRISH = 'Irish', + ITALIAN = 'Italian', + LITHUANIAN = 'Lithuanian', + NEPALI = 'Nepali', + NORWEIGAN = 'Norwegian', + PORTUGUESE = 'Portuguese', + ROMANIAN = 'Romanian', + RUSSIAN = 'Russian', + SPANISH = 'Spanish', + SWEDISH = 'Swedish', + TAMIL = 'Tamil', + TURKISH = 'Turkish', + CHINESE = 'Chinese' +} + +export type PropertyName = `${'@' | '$.'}${string}`; + +export type SortByOptions = PropertyName | { + BY: PropertyName; + DIRECTION?: 'ASC' | 'DESC'; +}; + +function pushSortByProperty(args: RedisCommandArguments, sortBy: SortByOptions): void { + if (typeof sortBy === 'string') { + args.push(sortBy); + } else { + args.push(sortBy.BY); + + if (sortBy.DIRECTION) { + args.push(sortBy.DIRECTION); + } + } +} + +export function pushSortByArguments(args: RedisCommandArguments, name: string, sortBy: SortByOptions | Array): RedisCommandArguments { + const lengthBefore = args.push( + name, + '' // will be overwritten + ); + + if (Array.isArray(sortBy)) { + for (const field of sortBy) { + pushSortByProperty(args, field); + } + } else { + pushSortByProperty(args, sortBy); + } + + args[lengthBefore - 1] = (args.length - lengthBefore).toString(); + + return args; +} + +export function pushArgumentsWithLength(args: RedisCommandArguments, fn: (args: RedisCommandArguments) => void): RedisCommandArguments { + const lengthIndex = args.push('') - 1; + fn(args); + args[lengthIndex] = (args.length - lengthIndex - 1).toString(); + return args; +} diff --git a/packages/search/lib/index.ts b/packages/search/lib/index.ts new file mode 100644 index 00000000000..9e0be7b169c --- /dev/null +++ b/packages/search/lib/index.ts @@ -0,0 +1,4 @@ +export { default } from './commands'; + +export { SchemaFieldTypes, SchemaTextFieldPhonetics } from './commands/CREATE'; +export { AggregateSteps, AggregateGroupByReducers } from './commands/AGGREGATE'; diff --git a/packages/search/lib/test-utils.ts b/packages/search/lib/test-utils.ts new file mode 100644 index 00000000000..4af05e10623 --- /dev/null +++ b/packages/search/lib/test-utils.ts @@ -0,0 +1,21 @@ +import TestUtils from '@node-redis/test-utils'; +import RediSearch from '.'; + +export default new TestUtils({ + dockerImageName: 'redislabs/redisearch', + dockerImageVersionArgument: 'redisearch-version', + defaultDockerVersion: '2.2.1' +}); + +export const GLOBAL = { + SERVERS: { + OPEN: { + serverArguments: ['--loadmodule /usr/lib/redis/modules/redisearch.so'], + clientOptions: { + modules: { + ft: RediSearch + } + } + } + } +}; diff --git a/packages/search/package.json b/packages/search/package.json new file mode 100644 index 00000000000..a72678c2add --- /dev/null +++ b/packages/search/package.json @@ -0,0 +1,24 @@ +{ + "name": "@node-redis/search", + "version": "1.0.0-rc.0", + "license": "MIT", + "main": "./dist/index.js", + "types": "./dist/index.d.ts", + "scripts": { + "test": "nyc -r text-summary -r lcov mocha -r source-map-support/register -r ts-node/register './lib/**/*.spec.ts'", + "build": "tsc" + }, + "peerDependencies": { + "@node-redis/client": "^1.0.0-rc" + }, + "devDependencies": { + "@istanbuljs/nyc-config-typescript": "^1.0.1", + "@node-redis/test-utils": "*", + "@types/node": "^16.11.7", + "nyc": "^15.1.0", + "release-it": "^14.11.7", + "source-map-support": "^0.5.20", + "ts-node": "^10.4.0", + "typescript": "^4.4.4" + } +} diff --git a/packages/search/tsconfig.json b/packages/search/tsconfig.json new file mode 100644 index 00000000000..14fda1d8711 --- /dev/null +++ b/packages/search/tsconfig.json @@ -0,0 +1,9 @@ +{ + "extends": "../../tsconfig.base.json", + "compilerOptions": { + "outDir": "./dist" + }, + "include": [ + "./lib/**/*.ts" + ] +} diff --git a/packages/test-utils/docker/Dockerfile b/packages/test-utils/docker/Dockerfile new file mode 100644 index 00000000000..23fc0b3a517 --- /dev/null +++ b/packages/test-utils/docker/Dockerfile @@ -0,0 +1,9 @@ +ARG IMAGE +FROM ${IMAGE} + +ARG REDIS_ARGUMENTS +ENV REDIS_ARGUMENTS=${REDIS_ARGUMENTS} + +COPY ./entrypoint.sh / + +ENTRYPOINT ["/entrypoint.sh"] diff --git a/packages/test-utils/docker/entrypoint.sh b/packages/test-utils/docker/entrypoint.sh new file mode 100755 index 00000000000..244977e83c4 --- /dev/null +++ b/packages/test-utils/docker/entrypoint.sh @@ -0,0 +1,7 @@ +#!/bin/bash + +echo testststealkshdfklhasdf + +echo $REDIS_ARGUMENTS + +redis-server $REDIS_ARGUMENTS diff --git a/packages/test-utils/lib/dockers.ts b/packages/test-utils/lib/dockers.ts new file mode 100644 index 00000000000..3dd6dcf3ebf --- /dev/null +++ b/packages/test-utils/lib/dockers.ts @@ -0,0 +1,222 @@ +import { createConnection } from 'net'; +import { once } from 'events'; +import { RedisModules, RedisScripts } from '@node-redis/client/lib/commands'; +import RedisClient, { RedisClientType } from '@node-redis/client/lib/client'; +import { promiseTimeout } from '@node-redis/client/lib/utils'; +import * as path from 'path'; +import { promisify } from 'util'; +import { exec } from 'child_process'; +const execAsync = promisify(exec); + +interface ErrorWithCode extends Error { + code: string; +} + +async function isPortAvailable(port: number): Promise { + try { + const socket = createConnection({ port }); + await once(socket, 'connect'); + socket.end(); + } catch (err) { + if (err instanceof Error && (err as ErrorWithCode).code === 'ECONNREFUSED') { + return true; + } + } + + return false; +} + +const portIterator = (async function*(): AsyncIterableIterator { + for (let i = 6379; i < 65535; i++) { + if (await isPortAvailable(i)) { + yield i; + } + } + + throw new Error('All ports are in use'); +})(); + +export interface RedisServerDockerConfig { + image: string; + version: Array; +} + +export interface RedisServerDocker { + port: number; + dockerId: string; +} + +// ".." cause it'll be in `./dist` +const DOCKER_FODLER_PATH = path.join(__dirname, '../docker'); + +async function spawnRedisServerDocker({ image, version }: RedisServerDockerConfig, serverArguments: Array): Promise { + const port = (await portIterator.next()).value, + { stdout, stderr } = await execAsync( + 'docker run -d --network host $(' + + `docker build ${DOCKER_FODLER_PATH} -q ` + + `--build-arg IMAGE=${image}:${version.join('.')} ` + + `--build-arg REDIS_ARGUMENTS="--save --port ${port.toString()} ${serverArguments.join(' ')}"` + + ')' + ); + + if (!stdout) { + throw new Error(`docker run error - ${stderr}`); + } + + while (await isPortAvailable(port)) { + await promiseTimeout(500); + } + + return { + port, + dockerId: stdout.trim() + }; +} + +const RUNNING_SERVERS = new Map, ReturnType>(); + +export function spawnRedisServer(dockerConfig: RedisServerDockerConfig, serverArguments: Array): Promise { + const runningServer = RUNNING_SERVERS.get(serverArguments); + if (runningServer) { + return runningServer; + } + + const dockerPromise = spawnRedisServerDocker(dockerConfig, serverArguments); + RUNNING_SERVERS.set(serverArguments, dockerPromise); + return dockerPromise; +} + +async function dockerRemove(dockerId: string): Promise { + const { stderr } = await execAsync(`docker rm -f ${dockerId}`); + if (stderr) { + throw new Error(`docker rm error - ${stderr}`); + } +} + +after(() => { + return Promise.all( + [...RUNNING_SERVERS.values()].map(async dockerPromise => + await dockerRemove((await dockerPromise).dockerId) + ) + ); +}); + +export interface RedisClusterDockersConfig extends RedisServerDockerConfig { + numberOfNodes?: number; +} + +async function spawnRedisClusterNodeDocker( + dockersConfig: RedisClusterDockersConfig, + serverArguments: Array, + fromSlot: number, + toSlot: number, + waitForState: boolean, + meetPort?: number +): Promise { + const docker = await spawnRedisServerDocker(dockersConfig, [ + ...serverArguments, + '--cluster-enabled', + 'yes', + '--cluster-node-timeout', + '5000' + ]), + client = RedisClient.create({ + socket: { + port: docker.port + } + }); + + await client.connect(); + + try { + const range = []; + for (let i = fromSlot; i < toSlot; i++) { + range.push(i); + } + + const promises: Array> = [client.clusterAddSlots(range)]; + + if (meetPort) { + promises.push(client.clusterMeet('127.0.0.1', meetPort)); + } + + if (waitForState) { + promises.push(waitForClusterState(client)); + } + + await Promise.all(promises); + + return docker; + } finally { + await client.disconnect(); + } +} + +async function waitForClusterState(client: RedisClientType): Promise { + while ((await client.clusterInfo()).state !== 'ok') { + await promiseTimeout(500); + } +} + +const SLOTS = 16384; + +async function spawnRedisClusterDockers(dockersConfig: RedisClusterDockersConfig, serverArguments: Array): Promise> { + const numberOfNodes = dockersConfig.numberOfNodes ?? 3, + slotsPerNode = Math.floor(SLOTS / numberOfNodes), + dockers: Array = []; + for (let i = 0; i < numberOfNodes; i++) { + const fromSlot = i * slotsPerNode, + [ toSlot, waitForState ] = i === numberOfNodes - 1 ? [SLOTS, true] : [fromSlot + slotsPerNode, false]; + dockers.push( + await spawnRedisClusterNodeDocker( + dockersConfig, + serverArguments, + fromSlot, + toSlot, + waitForState, + i === 0 ? undefined : dockers[i - 1].port + ) + ); + } + + const client = RedisClient.create({ + socket: { + port: dockers[0].port + } + }); + + await client.connect(); + + try { + while ((await client.clusterInfo()).state !== 'ok') { + await promiseTimeout(500); + } + } finally { + await client.disconnect(); + } + + return dockers; +} + +const RUNNING_CLUSTERS = new Map, ReturnType>(); + +export function spawnRedisCluster(dockersConfig: RedisClusterDockersConfig, serverArguments: Array): Promise> { + const runningCluster = RUNNING_CLUSTERS.get(serverArguments); + if (runningCluster) { + return runningCluster; + } + + const dockersPromise = spawnRedisClusterDockers(dockersConfig, serverArguments); + RUNNING_CLUSTERS.set(serverArguments, dockersPromise); + return dockersPromise; +} + +after(() => { + return Promise.all( + [...RUNNING_CLUSTERS.values()].map(async dockersPromise => { + return Promise.all( + (await dockersPromise).map(({ dockerId }) => dockerRemove(dockerId)) + ); + }) + ); +}); diff --git a/packages/test-utils/lib/index.ts b/packages/test-utils/lib/index.ts new file mode 100644 index 00000000000..8e20df75818 --- /dev/null +++ b/packages/test-utils/lib/index.ts @@ -0,0 +1,181 @@ +import { RedisModules, RedisScripts } from '@node-redis/client/lib/commands'; +import RedisClient, { RedisClientOptions, RedisClientType } from '@node-redis/client/lib/client'; +import RedisCluster, { RedisClusterOptions, RedisClusterType } from '@node-redis/client/lib/cluster'; +import { RedisServerDockerConfig, spawnRedisServer, spawnRedisCluster } from './dockers'; +import yargs from 'yargs'; +import { hideBin } from 'yargs/helpers'; + +interface TestUtilsConfig { + dockerImageName: string; + dockerImageVersionArgument: string; + defaultDockerVersion: string; + defaultClientOptions?: Partial>; + defaultClusterOptions?: Partial>; +} + +interface CommonTestOptions { + minimumDockerVersion?: Array; +} + +interface ClientTestOptions extends CommonTestOptions { + serverArguments: Array; + clientOptions?: Partial>; + disableClientSetup?: boolean; +} + +interface ClusterTestOptions extends CommonTestOptions { + serverArguments: Array; + clusterConfiguration?: Partial>; + numberOfNodes?: number; +} + +export default class TestUtils { + static #getVersion(argumentName: string, defaultVersion: string): Array { + return yargs(hideBin(process.argv)) + .option(argumentName, { + type: 'string', + default: defaultVersion + }) + .coerce(argumentName, (arg: string) => { + return arg.split('.').map(x => { + const value = Number(x); + if (Number.isNaN(value)) { + throw new TypeError(`${arg} is not a valid redis version`); + } + + return value; + }); + }) + .demandOption(argumentName) + .parseSync()[argumentName]; + } + + readonly #DOCKER_IMAGE: RedisServerDockerConfig; + + constructor(config: TestUtilsConfig) { + this.#DOCKER_IMAGE = { + image: config.dockerImageName, + version: TestUtils.#getVersion(config.dockerImageVersionArgument, config.defaultDockerVersion) + }; + } + + isVersionGreaterThan(minimumVersion: Array | undefined): boolean { + if (minimumVersion === undefined) return true; + + const lastIndex = Math.min(this.#DOCKER_IMAGE.version.length, minimumVersion.length) - 1; + for (let i = 0; i < lastIndex; i++) { + if (this.#DOCKER_IMAGE.version[i] > minimumVersion[i]) { + return true; + } else if (minimumVersion[i] > this.#DOCKER_IMAGE.version[i]) { + return false; + } + } + + return this.#DOCKER_IMAGE.version[lastIndex] >= minimumVersion[lastIndex]; + } + + isVersionGreaterThanHook(minimumVersion: Array | undefined): void { + const isVersionGreaterThan = this.isVersionGreaterThan.bind(this); + before(function () { + if (!isVersionGreaterThan(minimumVersion)) { + return this.skip(); + } + }); + } + + testWithClient( + title: string, + fn: (client: RedisClientType) => Promise, + options: ClientTestOptions + ): void { + let dockerPromise: ReturnType; + if (this.isVersionGreaterThan(options.minimumDockerVersion)) { + const dockerImage = this.#DOCKER_IMAGE; + before(function () { + this.timeout(30000); + + dockerPromise = spawnRedisServer(dockerImage, options.serverArguments); + return dockerPromise; + }); + } + + it(title, async function() { + if (!dockerPromise) return this.skip(); + + const client = RedisClient.create({ + ...options?.clientOptions, + socket: { + ...options?.clientOptions?.socket, + port: (await dockerPromise).port + } + }); + + if (options.disableClientSetup) { + return fn(client); + } + + await client.connect(); + + try { + await client.flushAll(); + await fn(client); + } finally { + if (client.isOpen) { + await client.flushAll(); + await client.disconnect(); + } + } + }); + } + + static async #clusterFlushAll(cluster: RedisClusterType): Promise { + await Promise.all( + cluster.getMasters().map(({ client }) => client.flushAll()) + ); + } + + testWithCluster( + title: string, + fn: (cluster: RedisClusterType) => Promise, + options: ClusterTestOptions + ): void { + let dockersPromise: ReturnType; + if (this.isVersionGreaterThan(options.minimumDockerVersion)) { + const dockerImage = this.#DOCKER_IMAGE; + before(function () { + this.timeout(30000); + + dockersPromise = spawnRedisCluster({ + ...dockerImage, + numberOfNodes: options?.numberOfNodes + }, options.serverArguments); + return dockersPromise; + }); + } + + it(title, async function () { + if (!dockersPromise) return this.skip(); + + const dockers = await dockersPromise, + cluster = RedisCluster.create({ + ...options.clusterConfiguration, + rootNodes: dockers.map(({ port }) => ({ + socket: { + port + } + })) + }); + + + await cluster.connect(); + + try { + await TestUtils.#clusterFlushAll(cluster); + await fn(cluster); + } finally { + await TestUtils.#clusterFlushAll(cluster); + await cluster.disconnect(); + } + }); + } +} diff --git a/packages/test-utils/package.json b/packages/test-utils/package.json new file mode 100644 index 00000000000..47ddc25acff --- /dev/null +++ b/packages/test-utils/package.json @@ -0,0 +1,26 @@ +{ + "name": "@node-redis/test-utils", + "private": true, + "main": "./dist/index.js", + "types": "./dist/index.d.ts", + "scripts": { + "build": "tsc", + "test": "echo \"TODO\"" + }, + "peerDependencies": { + "@node-redis/client": "^1.0.0-rc" + }, + "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" + } +} diff --git a/packages/test-utils/tsconfig.json b/packages/test-utils/tsconfig.json new file mode 100644 index 00000000000..14fda1d8711 --- /dev/null +++ b/packages/test-utils/tsconfig.json @@ -0,0 +1,9 @@ +{ + "extends": "../../tsconfig.base.json", + "compilerOptions": { + "outDir": "./dist" + }, + "include": [ + "./lib/**/*.ts" + ] +} diff --git a/tsconfig.base.json b/tsconfig.base.json new file mode 100644 index 00000000000..7df81029664 --- /dev/null +++ b/tsconfig.base.json @@ -0,0 +1,15 @@ +{ + "extends": "@tsconfig/node12/tsconfig.json", + "compilerOptions": { + "declaration": true, + "allowJs": true, + "useDefineForClassFields": true, + "esModuleInterop": false + }, + "files": [ + "./packages/client/lib/ts-declarations/cluster-key-slot.d.ts" + ], + "ts-node": { + "files": true + } +} diff --git a/tsconfig.json b/tsconfig.json index 1f76310034d..285b7ff0a97 100644 --- a/tsconfig.json +++ b/tsconfig.json @@ -1,33 +1,9 @@ { - "extends": "@tsconfig/node12/tsconfig.json", + "extends": "./tsconfig.base.json", "compilerOptions": { - "outDir": "./dist", - "declaration": true, - "useDefineForClassFields": true, - "allowJs": true + "outDir": "./dist" }, - "files": [ - "./lib/ts-declarations/cluster-key-slot.d.ts", - "./lib/ts-declarations/redis-parser.d.ts" - ], "include": [ - "./index.ts", - "./lib/**/*.ts" - ], - "ts-node": { - "files": true - }, - "typedocOptions": { - "entryPoints": [ - "./index.ts", - "./lib" - ], - "entryPointStrategy": "expand", - "exclude": [ - "./lib/ts-declarations", - "./lib/test-utils.ts" - ], - "theme": "./node_modules/typedoc-github-wiki-theme/dist", - "out": "documentation" - } + "./index.ts" + ] } From 641d53414fa8724e7d0b3dc185aceb4a5a99fc6b Mon Sep 17 00:00:00 2001 From: leibale Date: Tue, 16 Nov 2021 13:48:56 -0500 Subject: [PATCH 0969/1748] fix #1724 - fix LINDEX signature --- packages/client/lib/commands/LINDEX.spec.ts | 8 ++++---- packages/client/lib/commands/LINDEX.ts | 4 ++-- 2 files changed, 6 insertions(+), 6 deletions(-) diff --git a/packages/client/lib/commands/LINDEX.spec.ts b/packages/client/lib/commands/LINDEX.spec.ts index 5e0b1473ec4..e70f2399fc4 100644 --- a/packages/client/lib/commands/LINDEX.spec.ts +++ b/packages/client/lib/commands/LINDEX.spec.ts @@ -5,21 +5,21 @@ 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'), + await client.lIndex('key', 0), null ); }, 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); diff --git a/packages/client/lib/commands/LINDEX.ts b/packages/client/lib/commands/LINDEX.ts index 4c283f0912c..6c31cf57cfd 100644 --- a/packages/client/lib/commands/LINDEX.ts +++ b/packages/client/lib/commands/LINDEX.ts @@ -2,8 +2,8 @@ export const FIRST_KEY_INDEX = 1; export const IS_READ_ONLY = true; -export function transformArguments(key: string, element: string): Array { - return ['LINDEX', key, element]; +export function transformArguments(key: string, index: number): Array { + return ['LINDEX', key, index.toString()]; } export declare function transformReply(): string | null; From 51b640bca9cbef5888835d37de5c95d2b19b92bd Mon Sep 17 00:00:00 2001 From: leibale Date: Tue, 16 Nov 2021 14:10:59 -0500 Subject: [PATCH 0970/1748] add positive test for LINDEX --- packages/client/lib/commands/LINDEX.spec.ts | 23 +++++++++++++++------ 1 file changed, 17 insertions(+), 6 deletions(-) diff --git a/packages/client/lib/commands/LINDEX.spec.ts b/packages/client/lib/commands/LINDEX.spec.ts index e70f2399fc4..fcabf2ba657 100644 --- a/packages/client/lib/commands/LINDEX.spec.ts +++ b/packages/client/lib/commands/LINDEX.spec.ts @@ -10,12 +10,23 @@ describe('LINDEX', () => { ); }); - testUtils.testWithClient('client.lIndex', async client => { - assert.equal( - await client.lIndex('key', 0), - 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( From fc73cb8736ddbeac35b84b3187faaa818a691cb2 Mon Sep 17 00:00:00 2001 From: leibale Date: Tue, 16 Nov 2021 21:14:47 -0500 Subject: [PATCH 0971/1748] fix #1718 - add support for buffers in pubsub --- .github/README.md | 12 ++ packages/client/lib/client/commands-queue.ts | 209 +++++++++++++------ packages/client/lib/client/index.spec.ts | 24 ++- packages/client/lib/client/index.ts | 83 ++++++-- packages/client/lib/commands/PUBLISH.ts | 4 +- 5 files changed, 244 insertions(+), 88 deletions(-) diff --git a/.github/README.md b/.github/README.md index c7046805107..2ab819c7d4e 100644 --- a/.github/README.md +++ b/.github/README.md @@ -150,6 +150,18 @@ Publish a message on a channel: await publisher.publish('channel', 'message'); ``` +There is support for buffers as well: + +```typescript +await subscriber.subscribe('channel', (message) => { + console.log(message); // +}, true); + +await subscriber.pSubscribe('channe*', (message, channel) => { + console.log(message, channel); // , +}, 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): diff --git a/packages/client/lib/client/commands-queue.ts b/packages/client/lib/client/commands-queue.ts index 4fcae1e8b63..908c58d23ac 100644 --- a/packages/client/lib/client/commands-queue.ts +++ b/packages/client/lib/client/commands-queue.ts @@ -39,9 +39,29 @@ export enum PubSubUnsubscribeCommands { PUNSUBSCRIBE = 'PUNSUBSCRIBE' } -export type PubSubListener = (message: string, channel: string) => unknown; +type PubSubArgumentTypes = Buffer | string; -export type PubSubListenersMap = Map>; +export type PubSubListener< + BUFFER_MODE extends boolean = false, + T = BUFFER_MODE extends true ? Buffer : string +> = (message: T, channel: T) => unknown; + +interface PubSubListeners { + buffers: Set>; + strings: Set>; +} + +type PubSubListenersMap = Map; + +interface PubSubState { + subscribing: number; + subscribed: number; + unsubscribing: number; + listeners: { + channels: PubSubListenersMap; + patterns: PubSubListenersMap; + }; +} export default class RedisCommandsQueue { static #flushQueue(queue: LinkedList, err: Error): void { @@ -50,10 +70,20 @@ export default class RedisCommandsQueue { } } - static #emitPubSubMessage(listeners: Set, 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; @@ -62,41 +92,43 @@ export default class RedisCommandsQueue { readonly #waitingForReply = new LinkedList(); - readonly #pubSubState = { - subscribing: 0, - subscribed: 0, - unsubscribing: 0 - }; + #pubSubState: PubSubState | undefined; - readonly #pubSubListeners = { - channels: new Map(), - patterns: 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; } } @@ -112,7 +144,7 @@ export default class RedisCommandsQueue { } addCommand(args: RedisCommandArguments, options?: QueueCommandOptions, bufferMode?: boolean): Promise { - 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')); @@ -126,7 +158,7 @@ export default class RedisCommandsQueue { chainId: options?.chainId, bufferMode, resolve, - reject, + reject }); if (options?.signal) { @@ -153,17 +185,41 @@ export default class RedisCommandsQueue { }); } - subscribe(command: PubSubSubscribeCommands, channels: string | Array, listener: PubSubListener): Promise { - const channelsToSubscribe: Array = [], - 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( + command: PubSubSubscribeCommands, + channels: PubSubArgumentTypes | Array, + listener: PubSubListener, + bufferMode?: T + ): Promise { + const pubSubState = this.#initiatePubSubState(), + channelsToSubscribe: Array = [], + 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) { @@ -173,8 +229,20 @@ export default class RedisCommandsQueue { return this.#pushPubSubCommand(command, channelsToSubscribe); } - unsubscribe(command: PubSubUnsubscribeCommands, channels?: string | Array, listener?: PubSubListener): Promise { - const listeners = command === PubSubUnsubscribeCommands.UNSUBSCRIBE ? this.#pubSubListeners.channels : this.#pubSubListeners.patterns; + unsubscribe( + command: PubSubUnsubscribeCommands, + channels?: string | Array, + listener?: PubSubListener, + bufferMode?: T + ): Promise { + 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 +251,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) { @@ -205,11 +276,12 @@ export default class RedisCommandsQueue { return this.#pushPubSubCommand(command, channelsToUnsubscribe); } - #pushPubSubCommand(command: PubSubSubscribeCommands | PubSubUnsubscribeCommands, channels: number | Array): Promise { + #pushPubSubCommand(command: PubSubSubscribeCommands | PubSubUnsubscribeCommands, channels: number | Array): Promise { 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 = [command]; + commandArgs: Array = [command]; let channelsCounter: number; if (typeof channels === 'number') { // unsubscribe only @@ -219,18 +291,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,16 +318,14 @@ export default class RedisCommandsQueue { } resubscribe(): Promise | 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()]) ]); } @@ -269,7 +347,10 @@ export default class RedisCommandsQueue { } 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); } diff --git a/packages/client/lib/client/index.spec.ts b/packages/client/lib/client/index.spec.ts index 3f0bca45e27..41e7526eb28 100644 --- a/packages/client/lib/client/index.spec.ts +++ b/packages/client/lib/client/index.spec.ts @@ -561,17 +561,27 @@ 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) ]); @@ -580,14 +590,14 @@ describe('Client', () => { 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), diff --git a/packages/client/lib/client/index.ts b/packages/client/lib/client/index.ts index 8802631eda1..c520e36a08f 100644 --- a/packages/client/lib/client/index.ts +++ b/packages/client/lib/client/index.ts @@ -388,42 +388,93 @@ export default class RedisClient select = this.SELECT; - SUBSCRIBE(channels: string | Array, listener: PubSubListener): Promise { - return this.#subscribe(PubSubSubscribeCommands.SUBSCRIBE, channels, listener); + #subscribe( + command: PubSubSubscribeCommands, + channels: string | Array, + listener: PubSubListener, + bufferMode?: T + ): Promise { + const promise = this.#queue.subscribe( + command, + channels, + listener, + bufferMode + ); + this.#tick(); + return promise; + } + + SUBSCRIBE( + channels: string | Array, + listener: PubSubListener, + bufferMode?: T + ): Promise { + return this.#subscribe( + PubSubSubscribeCommands.SUBSCRIBE, + channels, + listener, + bufferMode + ); } subscribe = this.SUBSCRIBE; - PSUBSCRIBE(patterns: string | Array, listener: PubSubListener): Promise { - return this.#subscribe(PubSubSubscribeCommands.PSUBSCRIBE, patterns, listener); + PSUBSCRIBE( + patterns: string | Array, + listener: PubSubListener, + bufferMode?: T + ): Promise { + return this.#subscribe( + PubSubSubscribeCommands.PSUBSCRIBE, + patterns, + listener, + bufferMode + ); } pSubscribe = this.PSUBSCRIBE; - #subscribe(command: PubSubSubscribeCommands, channels: string | Array, listener: PubSubListener): Promise { - const promise = this.#queue.subscribe(command, channels, listener); + #unsubscribe( + command: PubSubUnsubscribeCommands, + channels?: string | Array, + listener?: PubSubListener, + bufferMode?: T + ): Promise { + const promise = this.#queue.unsubscribe(command, channels, listener, bufferMode); this.#tick(); return promise; } - UNSUBSCRIBE(channels?: string | Array, listener?: PubSubListener): Promise { - return this.#unsubscribe(PubSubUnsubscribeCommands.UNSUBSCRIBE, channels, listener); + UNSUBSCRIBE( + channels?: string | Array, + listener?: PubSubListener, + bufferMode?: T + ): Promise { + return this.#unsubscribe( + PubSubUnsubscribeCommands.UNSUBSCRIBE, + channels, + listener, + bufferMode + ); } unsubscribe = this.UNSUBSCRIBE; - PUNSUBSCRIBE(patterns?: string | Array, listener?: PubSubListener): Promise { - return this.#unsubscribe(PubSubUnsubscribeCommands.PUNSUBSCRIBE, patterns, listener); + PUNSUBSCRIBE( + patterns?: string | Array, + listener?: PubSubListener, + bufferMode?: T + ): Promise { + return this.#unsubscribe( + PubSubUnsubscribeCommands.PUNSUBSCRIBE, + patterns, + listener, + bufferMode + ); } pUnsubscribe = this.PUNSUBSCRIBE; - #unsubscribe(command: PubSubUnsubscribeCommands, channels?: string | Array, listener?: PubSubListener): Promise { - const promise = this.#queue.unsubscribe(command, channels, listener); - this.#tick(); - return promise; - } - QUIT(): Promise { return this.#socket.quit(() => { const quitPromise = this.#queue.addCommand(['QUIT']); diff --git a/packages/client/lib/commands/PUBLISH.ts b/packages/client/lib/commands/PUBLISH.ts index eda5234df20..cbfcaabd1cd 100644 --- a/packages/client/lib/commands/PUBLISH.ts +++ b/packages/client/lib/commands/PUBLISH.ts @@ -1,4 +1,6 @@ -export function transformArguments(channel: string, message: string): Array { +import { RedisCommandArguments } from '.'; + +export function transformArguments(channel: string | Buffer, message: string | Buffer): RedisCommandArguments { return ['PUBLISH', channel, message]; } From eedce53b158040601a0484569b2d9049f9b4502a Mon Sep 17 00:00:00 2001 From: Simon Prickett Date: Wed, 17 Nov 2021 19:42:57 +0000 Subject: [PATCH 0972/1748] Fixed a few typos. --- examples/search+json.js | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/examples/search+json.js b/examples/search+json.js index adc298289cd..a2ed39b1d98 100644 --- a/examples/search+json.js +++ b/examples/search+json.js @@ -43,7 +43,7 @@ async function searchPlusJson() { // documents: [...] // } - // Some aggrigrations + // Some aggregrations console.log( await client.ft.aggregate('users', '*', { STEPS: [{ @@ -51,7 +51,7 @@ async function searchPlusJson() { REDUCE: [{ type: AggregateGroupByReducers.AVG, property: '$.age', - AS: 'avarageAge' + AS: 'averageAge' }, { type: AggregateGroupByReducers.SUM, property: '$.coins', @@ -63,7 +63,7 @@ async function searchPlusJson() { // { // total: 2, // results: [{ - // avarageAvg: '27.5', + // averageAge: '27.5', // totalCoins: '115' // }] // } From a0b6ffe9482a94c06068114525905b7d1d24c88f Mon Sep 17 00:00:00 2001 From: leibale Date: Wed, 17 Nov 2021 17:12:50 -0500 Subject: [PATCH 0973/1748] fix ARRPOP --- packages/json/lib/commands/ARRPOP.ts | 2 +- packages/json/lib/commands/index.ts | 5 +++-- 2 files changed, 4 insertions(+), 3 deletions(-) diff --git a/packages/json/lib/commands/ARRPOP.ts b/packages/json/lib/commands/ARRPOP.ts index 5d8785a8d94..932b3294d85 100644 --- a/packages/json/lib/commands/ARRPOP.ts +++ b/packages/json/lib/commands/ARRPOP.ts @@ -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 '.'; diff --git a/packages/json/lib/commands/index.ts b/packages/json/lib/commands/index.ts index 91b4f7dc4b5..a79a5370e4f 100644 --- a/packages/json/lib/commands/index.ts +++ b/packages/json/lib/commands/index.ts @@ -84,8 +84,9 @@ export function transformRedisJsonNullReply(json: string | null): RedisJSON | nu return transformRedisJsonReply(json); } - -export function transformRedisJsonNullArrayReply(jsons: Array): Array { +export function transformRedisJsonNullArrayNullReply(jsons: Array | null): Array | null { + if (jsons === null) return null; + return jsons.map(transformRedisJsonNullReply); } From 6946e36ba0e24e3122191c2cadb3ff33563316d2 Mon Sep 17 00:00:00 2001 From: Leibale Eidelman Date: Fri, 19 Nov 2021 21:43:06 -0500 Subject: [PATCH 0974/1748] fix #1726 --- docs/v3-to-v4.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/docs/v3-to-v4.md b/docs/v3-to-v4.md index 7c3e9880431..90267d8245c 100644 --- a/docs/v3-to-v4.md +++ b/docs/v3-to-v4.md @@ -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 From 42e36dfbb1b342bb46a2d72f7cfaae47866aaa9c Mon Sep 17 00:00:00 2001 From: leibale Date: Mon, 22 Nov 2021 17:42:41 -0500 Subject: [PATCH 0975/1748] enhance cluster reshard handling --- packages/client/lib/cluster/cluster-slots.ts | 62 ++++++++++++------- packages/client/lib/cluster/index.ts | 4 +- .../client/lib/commands/CLUSTER_NODES.spec.ts | 25 ++++++++ packages/client/lib/commands/CLUSTER_NODES.ts | 13 +++- 4 files changed, 76 insertions(+), 28 deletions(-) diff --git a/packages/client/lib/cluster/cluster-slots.ts b/packages/client/lib/cluster/cluster-slots.ts index ff4c79b4d36..f69449efa1a 100644 --- a/packages/client/lib/cluster/cluster-slots.ts +++ b/packages/client/lib/cluster/cluster-slots.ts @@ -42,20 +42,8 @@ export default class RedisClusterSlots): Promise { - 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 { - const client = new this.#Client(clientOptions); + const client = this.#initiateClient(clientOptions); await client.connect(); @@ -72,6 +60,29 @@ export default class RedisClusterSlots; + + async rediscover(startWith: RedisClientType): Promise { + if (!this.#runningRediscoverPromise) { + this.#runningRediscoverPromise = this.#rediscover(startWith) + .finally(() => this.#runningRediscoverPromise = undefined); + } + + return this.#runningRediscoverPromise; + } + + async #rediscover(startWith: RedisClientType): Promise { + 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): Promise { // Override this.#slots and add not existing clients to this.#nodeByUrl const promises: Array> = [], @@ -103,18 +114,23 @@ export default class RedisClusterSlots { + return new this.#Client(this.#clientOptionsDefaults(options)) + .on('error', this.#onError); + } + #initiateClientForNode(nodeData: RedisClusterMasterNode | RedisClusterReplicaNode, readonly: boolean, clientsInUse: Set, promises: Array>): ClusterNode { const url = `${nodeData.host}:${nodeData.port}`; clientsInUse.add(url); @@ -123,15 +139,13 @@ export default class RedisClusterSlots 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 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; } diff --git a/packages/client/lib/commands/CLUSTER_NODES.spec.ts b/packages/client/lib/commands/CLUSTER_NODES.spec.ts index 2b3881d8cd0..d061c59e8ee 100644 --- a/packages/client/lib/commands/CLUSTER_NODES.spec.ts +++ b/packages/client/lib/commands/CLUSTER_NODES.spec.ts @@ -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( diff --git a/packages/client/lib/commands/CLUSTER_NODES.ts b/packages/client/lib/commands/CLUSTER_NODES.ts index d04ffc10a1d..ba4477cdd20 100644 --- a/packages/client/lib/commands/CLUSTER_NODES.ts +++ b/packages/client/lib/commands/CLUSTER_NODES.ts @@ -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 { 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), From 13714e69ba0a16de1761b2ccfff142bab368f3b7 Mon Sep 17 00:00:00 2001 From: Simon Prickett Date: Tue, 23 Nov 2021 21:39:05 +0000 Subject: [PATCH 0976/1748] Adds RediSearch demo. --- examples/README.md | 17 ++++---- examples/search-hashes.js | 82 +++++++++++++++++++++++++++++++++++++++ 2 files changed, 91 insertions(+), 8 deletions(-) create mode 100644 examples/search-hashes.js diff --git a/examples/README.md b/examples/README.md index aef0b38bdbb..4d46e5f6e64 100644 --- a/examples/README.md +++ b/examples/README.md @@ -2,14 +2,15 @@ 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 | +| `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 diff --git a/examples/search-hashes.js b/examples/search-hashes.js new file mode 100644 index 00000000000..ded4b0edb95 --- /dev/null +++ b/examples/search-hashes.js @@ -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(); \ No newline at end of file From f4fa63cde34d2e25f836c3fedae1bfa0299c7e7f Mon Sep 17 00:00:00 2001 From: Simon Prickett Date: Tue, 23 Nov 2021 21:49:40 +0000 Subject: [PATCH 0977/1748] Adds intro sentence. --- .github/README.md | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/.github/README.md b/.github/README.md index 2ab819c7d4e..87d668bc7cf 100644 --- a/.github/README.md +++ b/.github/README.md @@ -5,13 +5,15 @@ [![License](https://img.shields.io/github/license/redis/node-redis.svg)](https://codecov.io/gh/redis/node-redis) [![Chat](https://img.shields.io/discord/697882427875393627.svg)](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@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). +> :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 From 967ee5801c7bcc4d49a7339247f7522adef88fea Mon Sep 17 00:00:00 2001 From: Simon Prickett Date: Wed, 24 Nov 2021 18:16:59 +0000 Subject: [PATCH 0978/1748] Made top level comment more descriptive. --- examples/search+json.js | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/examples/search+json.js b/examples/search+json.js index a2ed39b1d98..623e0bfd561 100644 --- a/examples/search+json.js +++ b/examples/search+json.js @@ -1,4 +1,4 @@ -// Use Redis Search and Redis JSON +// This example demonstrates how to use RediSearch and RedisJSON together. import { createClient, SchemaFieldTypes, AggregateGroupByReducers, AggregateSteps } from 'redis'; @@ -43,7 +43,7 @@ async function searchPlusJson() { // documents: [...] // } - // Some aggregrations + // Some aggregrations... console.log( await client.ft.aggregate('users', '*', { STEPS: [{ From 055af50890d18dfbb378cfbb608206f15fc20c6b Mon Sep 17 00:00:00 2001 From: Simon Prickett Date: Wed, 24 Nov 2021 19:32:37 +0000 Subject: [PATCH 0979/1748] Adds RedisJSON example. --- examples/README.md | 1 + examples/managing-json.js | 81 +++++++++++++++++++++++++++++++++++++++ 2 files changed, 82 insertions(+) create mode 100644 examples/managing-json.js diff --git a/examples/README.md b/examples/README.md index 4d46e5f6e64..15929679085 100644 --- a/examples/README.md +++ b/examples/README.md @@ -8,6 +8,7 @@ This folder contains example scripts showing how to use Node Redis in different | `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 | diff --git a/examples/managing-json.js b/examples/managing-json.js new file mode 100644 index 00000000000..ddb12da8cf5 --- /dev/null +++ b/examples/managing-json.js @@ -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: '', + 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(); + From 99b261b79c829b13fff5bfbbe6c0bfcbe2317dda Mon Sep 17 00:00:00 2001 From: Simon Prickett Date: Wed, 24 Nov 2021 19:40:20 +0000 Subject: [PATCH 0980/1748] Renamed JSON search example. --- examples/README.md | 2 +- examples/{search+json.js => search-json.js} | 0 2 files changed, 1 insertion(+), 1 deletion(-) rename examples/{search+json.js => search-json.js} (100%) diff --git a/examples/README.md b/examples/README.md index 15929679085..94b043ae483 100644 --- a/examples/README.md +++ b/examples/README.md @@ -10,7 +10,7 @@ This folder contains example scripts showing how to use Node Redis in different | `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 | +| `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 diff --git a/examples/search+json.js b/examples/search-json.js similarity index 100% rename from examples/search+json.js rename to examples/search-json.js From 7d58f0547980430d0d363ea8bd8e95bcad9d7962 Mon Sep 17 00:00:00 2001 From: Simon Prickett Date: Wed, 24 Nov 2021 20:03:23 +0000 Subject: [PATCH 0981/1748] Some refactoring. --- examples/search-json.js | 51 ++++++++++++++++++++++++++--------------- 1 file changed, 32 insertions(+), 19 deletions(-) diff --git a/examples/search-json.js b/examples/search-json.js index 623e0bfd561..c44bf7eef6a 100644 --- a/examples/search-json.js +++ b/examples/search-json.js @@ -2,31 +2,42 @@ import { createClient, SchemaFieldTypes, AggregateGroupByReducers, AggregateSteps } from 'redis'; -async function searchPlusJson() { +async function searchJSON() { 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' - }); + // Create an index. + try { + await client.ft.create('idx:users', { + '$.name': { + type: SchemaFieldTypes.TEXT, + SORTABLE: 'UNF' + }, + '$.age': SchemaFieldTypes.NUMERIC, + '$.coins': SchemaFieldTypes.NUMERIC + }, { + 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 + // Add some users. await Promise.all([ - client.json.set('users:1', '$', { + client.json.set('noderedis:users:1', '$', { name: 'Alice', age: 32, coins: 100 }), - client.json.set('users:2', '$', { + client.json.set('noderedis:users:2', '$', { name: 'Bob', age: 23, coins: 15 @@ -34,18 +45,20 @@ async function searchPlusJson() { ]); // Search all users under 30 + // https://oss.redis.com/redisearch/Commands/#ftsearch // TODO: why "$.age:[-inf, 30]" does not work? console.log( - await client.ft.search('users', '*') + await client.ft.search('idx:users', '*') ); // { // total: 1, // documents: [...] // } - // Some aggregrations... + // 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('users', '*', { + await client.ft.aggregate('idx:users', '*', { STEPS: [{ type: AggregateSteps.GROUPBY, REDUCE: [{ @@ -71,4 +84,4 @@ async function searchPlusJson() { await client.quit(); } -searchPlusJson(); +searchJSON(); From 862b9a132e3e35d7b5bc3411909cec1e5804943d Mon Sep 17 00:00:00 2001 From: Simon Prickett Date: Wed, 24 Nov 2021 20:40:17 +0000 Subject: [PATCH 0982/1748] Fixed search example for JSON. --- examples/search-json.js | 22 ++++++++++++++-------- 1 file changed, 14 insertions(+), 8 deletions(-) diff --git a/examples/search-json.js b/examples/search-json.js index c44bf7eef6a..a608d3aefa5 100644 --- a/examples/search-json.js +++ b/examples/search-json.js @@ -14,8 +14,14 @@ async function searchJSON() { type: SchemaFieldTypes.TEXT, SORTABLE: 'UNF' }, - '$.age': SchemaFieldTypes.NUMERIC, - '$.coins': SchemaFieldTypes.NUMERIC + '$.age': { + type: SchemaFieldTypes.NUMERIC, + AS: 'age' + }, + '$.coins': { + type: SchemaFieldTypes.NUMERIC, + AS: 'coins' + } }, { ON: 'JSON', PREFIX: 'noderedis:users' @@ -45,14 +51,14 @@ async function searchJSON() { ]); // Search all users under 30 - // https://oss.redis.com/redisearch/Commands/#ftsearch - // TODO: why "$.age:[-inf, 30]" does not work? + console.log('Users under 30 years old:'); console.log( - await client.ft.search('idx:users', '*') + // https://oss.redis.com/redisearch/Commands/#ftsearch + await client.ft.search('idx:users', '@age:[0 30]') ); // { // total: 1, - // documents: [...] + // documents: [ { id: 'noderedis:users:2', value: [Object] } ] // } // Some aggregrations, what's the average age and total number of coins... @@ -63,11 +69,11 @@ async function searchJSON() { type: AggregateSteps.GROUPBY, REDUCE: [{ type: AggregateGroupByReducers.AVG, - property: '$.age', + property: 'age', AS: 'averageAge' }, { type: AggregateGroupByReducers.SUM, - property: '$.coins', + property: 'coins', AS: 'totalCoins' }] }] From 8e95cde0fe6e949614891223daf84b099d055fe3 Mon Sep 17 00:00:00 2001 From: Simon Prickett Date: Wed, 24 Nov 2021 21:14:09 +0000 Subject: [PATCH 0983/1748] Minor wording updates. --- README.md | 2 +- packages/client/README.md | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/README.md b/README.md index a98e6a261b2..55f0dbf865b 100644 --- a/README.md +++ b/README.md @@ -1,2 +1,2 @@ # redis -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. diff --git a/packages/client/README.md b/packages/client/README.md index 37c326fc42e..6007608ea3b 100644 --- a/packages/client/README.md +++ b/packages/client/README.md @@ -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. From 07a98c8240716a2517f7d601e6dfc041dea3a9fb Mon Sep 17 00:00:00 2001 From: Simon Prickett Date: Wed, 24 Nov 2021 21:37:37 +0000 Subject: [PATCH 0984/1748] Added missing pet name. --- examples/managing-json.js | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/examples/managing-json.js b/examples/managing-json.js index ddb12da8cf5..44978a94f05 100644 --- a/examples/managing-json.js +++ b/examples/managing-json.js @@ -62,7 +62,7 @@ async function managingJSON() { // Add a new pet... await client.json.arrAppend('noderedis:jsondata', '.pets', { - name: '', + name: 'Robin', species: 'bird', isMammal: false, age: 1 From 47217d3cbd5423b131ef1890a25e0a8328163347 Mon Sep 17 00:00:00 2001 From: Simon Prickett Date: Wed, 24 Nov 2021 21:37:52 +0000 Subject: [PATCH 0985/1748] Adds JSON package overview. --- packages/json/README.md | 80 ++++++++++++++++++++++++++++++++++++++++- 1 file changed, 79 insertions(+), 1 deletion(-) diff --git a/packages/json/README.md b/packages/json/README.md index 1cd599d5ea8..aaf94d703ef 100644 --- a/packages/json/README.md +++ b/packages/json/README.md @@ -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://) 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 + }); +``` \ No newline at end of file From 7dc771fb3818de94c594b6d2213fad12f31dc1b6 Mon Sep 17 00:00:00 2001 From: Simon Prickett Date: Wed, 24 Nov 2021 21:38:46 +0000 Subject: [PATCH 0986/1748] Fixed typo. --- packages/json/README.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/packages/json/README.md b/packages/json/README.md index aaf94d703ef..fe5115de4d0 100644 --- a/packages/json/README.md +++ b/packages/json/README.md @@ -55,7 +55,7 @@ const results = await client.json.get('noderedis:jsondata', { `results` will contain the following: ```javascript - '.pets[1].name': 'Goldie', '.pets[1].age': 2 } + { '.pets[1].name': 'Goldie', '.pets[1].age': 2 } ``` ### Performing Atomic Updates on JSON Documents Stored in Redis From ae8afc3f4d5c7d482bc58d65b254d775b91eb176 Mon Sep 17 00:00:00 2001 From: Simon Prickett Date: Wed, 24 Nov 2021 22:39:05 +0000 Subject: [PATCH 0987/1748] Search package README initial version. --- packages/search/README.md | 120 +++++++++++++++++++++++++++++++++++++- 1 file changed, 119 insertions(+), 1 deletion(-) diff --git a/packages/search/README.md b/packages/search/README.md index 856a75fbb5a..f13ca05eb0f 100644 --- a/packages/search/README.md +++ b/packages/search/README.md @@ -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]'); +``` From 22edf8a6319885461dafa20163642a6f19a4a620 Mon Sep 17 00:00:00 2001 From: leibale Date: Wed, 24 Nov 2021 21:05:58 -0500 Subject: [PATCH 0988/1748] remove echo from docker entrypoint.sh --- packages/test-utils/docker/entrypoint.sh | 4 ---- 1 file changed, 4 deletions(-) diff --git a/packages/test-utils/docker/entrypoint.sh b/packages/test-utils/docker/entrypoint.sh index 244977e83c4..d4006f55622 100755 --- a/packages/test-utils/docker/entrypoint.sh +++ b/packages/test-utils/docker/entrypoint.sh @@ -1,7 +1,3 @@ #!/bin/bash -echo testststealkshdfklhasdf - -echo $REDIS_ARGUMENTS - redis-server $REDIS_ARGUMENTS From b80bbc3eba1ee5786a86812ab254804e02a28bb8 Mon Sep 17 00:00:00 2001 From: leibale Date: Wed, 24 Nov 2021 21:11:27 -0500 Subject: [PATCH 0989/1748] npm update --- package-lock.json | 472 ++++++++++++++++--------------- packages/client/package.json | 14 +- packages/json/package.json | 8 +- packages/search/package.json | 8 +- packages/test-utils/package.json | 10 +- 5 files changed, 271 insertions(+), 241 deletions(-) diff --git a/package-lock.json b/package-lock.json index 5c3ebb5c208..c6dec588c55 100644 --- a/package-lock.json +++ b/package-lock.json @@ -12,9 +12,9 @@ "./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-rc.0", + "@node-redis/json": "^1.0.0-rc.0", + "@node-redis/search": "^1.0.0-rc.0" }, "devDependencies": { "@tsconfig/node12": "^1.0.9", @@ -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", @@ -6411,8 +6415,7 @@ } }, "packages/client": { - "name": "@node-redis/client", - "version": "1.0.0-rc", + "version": "1.0.0-rc.0", "license": "MIT", "dependencies": { "cluster-key-slot": "1.1.0", @@ -6423,77 +6426,91 @@ "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" } }, "packages/json": { - "name": "@node-redis/json", "version": "1.0.0-rc.0", "license": "MIT", "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" } }, "packages/search": { - "name": "@node-redis/search", "version": "1.0.0-rc.0", "license": "MIT", "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" } }, "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", + "@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-rc" + } + }, + "packages/time-series": { + "version": "1.0.0-rc.0", + "license": "MIT", + "devDependencies": { + "@istanbuljs/nyc-config-typescript": "^1.0.1", + "@node-redis/test-utils": "*", + "@types/node": "^16.11.7", + "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" + "typescript": "^4.4.4" }, "peerDependencies": { "@node-redis/client": "^1.0.0-rc" @@ -6511,9 +6528,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 +6784,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 +7007,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,12 +7034,12 @@ "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": { @@ -7030,12 +7047,12 @@ "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/test-utils": { @@ -7043,15 +7060,28 @@ "requires": { "@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.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": "*", + "@types/node": "^16.11.7", + "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" + "typescript": "^4.4.4" } }, "@nodelib/fs.scandir": { @@ -7184,15 +7214,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 +7348,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 +7400,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 +7504,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 +7821,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 +7859,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 +8167,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 +8230,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 +8244,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 +8282,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 +8354,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 +8735,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 +8745,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 +8963,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 +9006,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 +9355,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 +9482,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 +10512,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 +10528,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 +10757,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 +10800,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 +11046,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 +11066,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": { diff --git a/packages/client/package.json b/packages/client/package.json index 7a6d23f5ff9..80c2898c7c4 100644 --- a/packages/client/package.json +++ b/packages/client/package.json @@ -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" diff --git a/packages/json/package.json b/packages/json/package.json index 7e5f6e10c1f..358ede42e1c 100644 --- a/packages/json/package.json +++ b/packages/json/package.json @@ -14,11 +14,11 @@ "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" } } diff --git a/packages/search/package.json b/packages/search/package.json index a72678c2add..7ea682d41b7 100644 --- a/packages/search/package.json +++ b/packages/search/package.json @@ -14,11 +14,11 @@ "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" } } diff --git a/packages/test-utils/package.json b/packages/test-utils/package.json index 47ddc25acff..c7cc80a174f 100644 --- a/packages/test-utils/package.json +++ b/packages/test-utils/package.json @@ -13,14 +13,14 @@ "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" } } From 4ff9a050982c164ebcfad3833c4edfa542dd761e Mon Sep 17 00:00:00 2001 From: leibale Date: Wed, 24 Nov 2021 21:17:55 -0500 Subject: [PATCH 0990/1748] update docs --- .github/README.md | 312 ---------------------------------- README.md | 314 ++++++++++++++++++++++++++++++++++- docs/client-configuration.md | 4 +- docs/clustering.md | 2 +- packages/client/CHANGELOG.md | 10 +- packages/json/README.md | 4 +- 6 files changed, 322 insertions(+), 324 deletions(-) delete mode 100644 .github/README.md diff --git a/.github/README.md b/.github/README.md deleted file mode 100644 index 87d668bc7cf..00000000000 --- a/.github/README.md +++ /dev/null @@ -1,312 +0,0 @@ -# Node-Redis - -[![Tests](https://img.shields.io/github/workflow/status/redis/node-redis/Tests/master.svg?label=tests)](https://codecov.io/gh/redis/node-redis) -[![Coverage](https://codecov.io/gh/redis/node-redis/branch/master/graph/badge.svg?token=xcfqHhJC37)](https://codecov.io/gh/redis/node-redis) -[![License](https://img.shields.io/github/license/redis/node-redis.svg)](https://codecov.io/gh/redis/node-redis) -[![Chat](https://img.shields.io/discord/697882427875393627.svg)](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@next -``` - -> :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); // -}, true); - -await subscriber.pSubscribe('channe*', (message, channel) => { - console.log(message, channel); // , -}, 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 { - 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](../) | [![Downloads](https://img.shields.io/npm/dm/redis.svg)](https://www.npmjs.com/package/redis/v/next) [![Version](https://img.shields.io/npm/v/redis/next.svg)](https://www.npmjs.com/package/redis/v/next) | -| [@node-redis/client](../packages/client) | [![Downloads](https://img.shields.io/npm/dm/@node-redis/client.svg)](https://www.npmjs.com/package/@node-redis/client/v/next) [![Version](https://img.shields.io/npm/v/@node-redis/client/next.svg)](https://www.npmjs.com/package/@node-redis/client/v/next) | -| [@node-redis/json](../packages/json) | [![Downloads](https://img.shields.io/npm/dm/@node-redis/json.svg)](https://www.npmjs.com/package/@node-redis/json/v/next) [![Version](https://img.shields.io/npm/v/@node-redis/json/next.svg)](https://www.npmjs.com/package/@node-redis/json/v/next) [Redis JSON](https://oss.redis.com/redisjson/) commands | -| [@node-redis/search](../packages/search) | [![Downloads](https://img.shields.io/npm/dm/@node-redis/search.svg)](https://www.npmjs.com/package/@node-redis/search/v/next) [![Version](https://img.shields.io/npm/v/@node-redis/search/next.svg)](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! - -[![Contributors](https://contrib.rocks/image?repo=redis/node-redis)](https://github.com/redis/node-redis/graphs/contributors) - -## License - -This repository is licensed under the "MIT" license. See [LICENSE](LICENSE). diff --git a/README.md b/README.md index 55f0dbf865b..d89219214cd 100644 --- a/README.md +++ b/README.md @@ -1,2 +1,312 @@ -# redis -The source code and documentation for this package are in the main [node-redis](https://github.com/redis/node-redis) repo. +# Node-Redis + +[![Tests](https://img.shields.io/github/workflow/status/redis/node-redis/Tests/master.svg?label=tests)](https://codecov.io/gh/redis/node-redis) +[![Coverage](https://codecov.io/gh/redis/node-redis/branch/master/graph/badge.svg?token=xcfqHhJC37)](https://codecov.io/gh/redis/node-redis) +[![License](https://img.shields.io/github/license/redis/node-redis.svg)](https://codecov.io/gh/redis/node-redis) +[![Chat](https://img.shields.io/discord/697882427875393627.svg)](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); // +}, true); + +await subscriber.pSubscribe('channe*', (message, channel) => { + console.log(message, channel); // , +}, 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 { + 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](./) | [![Downloads](https://img.shields.io/npm/dm/redis.svg)](https://www.npmjs.com/package/redis/v/next) [![Version](https://img.shields.io/npm/v/redis/next.svg)](https://www.npmjs.com/package/redis/v/next) | +| [@node-redis/client](./packages/client) | [![Downloads](https://img.shields.io/npm/dm/@node-redis/client.svg)](https://www.npmjs.com/package/@node-redis/client/v/next) [![Version](https://img.shields.io/npm/v/@node-redis/client/next.svg)](https://www.npmjs.com/package/@node-redis/client/v/next) | +| [@node-redis/json](./packages/json) | [![Downloads](https://img.shields.io/npm/dm/@node-redis/json.svg)](https://www.npmjs.com/package/@node-redis/json/v/next) [![Version](https://img.shields.io/npm/v/@node-redis/json/next.svg)](https://www.npmjs.com/package/@node-redis/json/v/next) [Redis JSON](https://oss.redis.com/redisjson/) commands | +| [@node-redis/search](./packages/search) | [![Downloads](https://img.shields.io/npm/dm/@node-redis/search.svg)](https://www.npmjs.com/package/@node-redis/search/v/next) [![Version](https://img.shields.io/npm/v/@node-redis/search/next.svg)](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! + +[![Contributors](https://contrib.rocks/image?repo=redis/node-redis)](https://github.com/redis/node-redis/graphs/contributors) + +## License + +This repository is licensed under the "MIT" license. See [LICENSE](LICENSE). diff --git a/docs/client-configuration.md b/docs/client-configuration.md index 1dbbdd8cba2..1b0194615af 100644 --- a/docs/client-configuration.md +++ b/docs/client-configuration.md @@ -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 diff --git a/docs/clustering.md b/docs/clustering.md index 3b5ef94a5c7..f5ef9a9612d 100644 --- a/docs/clustering.md +++ b/docs/clustering.md @@ -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 diff --git a/packages/client/CHANGELOG.md b/packages/client/CHANGELOG.md index 21b7177e8b6..39ea947b064 100644 --- a/packages/client/CHANGELOG.md +++ b/packages/client/CHANGELOG.md @@ -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 diff --git a/packages/json/README.md b/packages/json/README.md index fe5115de4d0..5686f852d07 100644 --- a/packages/json/README.md +++ b/packages/json/README.md @@ -1,6 +1,6 @@ # @node-redis/json -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://) to include functions for each of the RedisJSON commands. +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. @@ -32,7 +32,7 @@ await client.json.set('noderedis:jsondata', '$', { species: 'fish', age: 2, isMammal: false - } + } ] }); ``` From 96a73db0b4dc5bf47041dbc3f8192e6ae30152e2 Mon Sep 17 00:00:00 2001 From: leibale Date: Wed, 24 Nov 2021 21:27:04 -0500 Subject: [PATCH 0991/1748] fix merge --- .github/README.md | 298 -------------------------------- examples/search+json.js | 74 -------- package-lock.json | 28 --- packages/time-series/.npmignore | 6 + 4 files changed, 6 insertions(+), 400 deletions(-) delete mode 100644 .github/README.md delete mode 100644 examples/search+json.js create mode 100644 packages/time-series/.npmignore diff --git a/.github/README.md b/.github/README.md deleted file mode 100644 index c7046805107..00000000000 --- a/.github/README.md +++ /dev/null @@ -1,298 +0,0 @@ -# Node-Redis - -[![Tests](https://img.shields.io/github/workflow/status/redis/node-redis/Tests/master.svg?label=tests)](https://codecov.io/gh/redis/node-redis) -[![Coverage](https://codecov.io/gh/redis/node-redis/branch/master/graph/badge.svg?token=xcfqHhJC37)](https://codecov.io/gh/redis/node-redis) -[![License](https://img.shields.io/github/license/redis/node-redis.svg)](https://codecov.io/gh/redis/node-redis) -[![Chat](https://img.shields.io/discord/697882427875393627.svg)](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 { - 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](../) | [![Downloads](https://img.shields.io/npm/dm/redis.svg)](https://www.npmjs.com/package/redis/v/next) [![Version](https://img.shields.io/npm/v/redis/next.svg)](https://www.npmjs.com/package/redis/v/next) | -| [@node-redis/client](../packages/client) | [![Downloads](https://img.shields.io/npm/dm/@node-redis/client.svg)](https://www.npmjs.com/package/@node-redis/client/v/next) [![Version](https://img.shields.io/npm/v/@node-redis/client/next.svg)](https://www.npmjs.com/package/@node-redis/client/v/next) | -| [@node-redis/json](../packages/json) | [![Downloads](https://img.shields.io/npm/dm/@node-redis/json.svg)](https://www.npmjs.com/package/@node-redis/json/v/next) [![Version](https://img.shields.io/npm/v/@node-redis/json/next.svg)](https://www.npmjs.com/package/@node-redis/json/v/next) [Redis JSON](https://oss.redis.com/redisjson/) commands | -| [@node-redis/search](../packages/search) | [![Downloads](https://img.shields.io/npm/dm/@node-redis/search.svg)](https://www.npmjs.com/package/@node-redis/search/v/next) [![Version](https://img.shields.io/npm/v/@node-redis/search/next.svg)](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! - -[![Contributors](https://contrib.rocks/image?repo=redis/node-redis)](https://github.com/redis/node-redis/graphs/contributors) - -## License - -This repository is licensed under the "MIT" license. See [LICENSE](LICENSE). diff --git a/examples/search+json.js b/examples/search+json.js deleted file mode 100644 index adc298289cd..00000000000 --- a/examples/search+json.js +++ /dev/null @@ -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(); diff --git a/package-lock.json b/package-lock.json index 570bb53cbac..c6dec588c55 100644 --- a/package-lock.json +++ b/package-lock.json @@ -2836,9 +2836,6 @@ "resolved": "https://registry.npmjs.org/type-fest/-/type-fest-0.20.2.tgz", "integrity": "sha512-Ne+eE4r0/iWnpAxD852z3A+N0Bt5RN//NjJwRd2VFHEmrywxf5vsZlh4R6lixl6B+wz/8d+maTSAkN1FIkI3LQ==", "dev": true, - "dependencies": { - "type-fest": "^0.20.2" - }, "engines": { "node": ">=10" }, @@ -7500,31 +7497,6 @@ "eslint-visitor-keys": "^3.0.0" } }, - "@typescript-eslint/typescript-estree": { - "version": "5.4.0", - "resolved": "https://registry.npmjs.org/@typescript-eslint/typescript-estree/-/typescript-estree-5.4.0.tgz", - "integrity": "sha512-nhlNoBdhKuwiLMx6GrybPT3SFILm5Gij2YBdPEPFlYNFAXUJWX6QRgvi/lwVoadaQEFsizohs6aFRMqsXI2ewA==", - "dev": true, - "requires": { - "@typescript-eslint/types": "5.4.0", - "@typescript-eslint/visitor-keys": "5.4.0", - "debug": "^4.3.2", - "globby": "^11.0.4", - "is-glob": "^4.0.3", - "semver": "^7.3.5", - "tsutils": "^3.21.0" - } - }, - "@typescript-eslint/visitor-keys": { - "version": "5.4.0", - "resolved": "https://registry.npmjs.org/@typescript-eslint/visitor-keys/-/visitor-keys-5.4.0.tgz", - "integrity": "sha512-PVbax7MeE7tdLfW5SA0fs8NGVVr+buMPrcj+CWYWPXsZCH8qZ1THufDzbXm1xrZ2b2PA1iENJ0sRq5fuUtvsJg==", - "dev": true, - "requires": { - "@typescript-eslint/types": "5.4.0", - "eslint-visitor-keys": "^3.0.0" - } - }, "@ungap/promise-all-settled": { "version": "1.1.2", "resolved": "https://registry.npmjs.org/@ungap/promise-all-settled/-/promise-all-settled-1.1.2.tgz", diff --git a/packages/time-series/.npmignore b/packages/time-series/.npmignore new file mode 100644 index 00000000000..bbef2b404fb --- /dev/null +++ b/packages/time-series/.npmignore @@ -0,0 +1,6 @@ +.nyc_output/ +coverage/ +lib/ +.nycrc.json +.release-it.json +tsconfig.json From c74e043ee14c655b168f6f7f2fbd22030f306470 Mon Sep 17 00:00:00 2001 From: leibale Date: Wed, 24 Nov 2021 21:37:31 -0500 Subject: [PATCH 0992/1748] fix merge --- packages/client/lib/client/commands-queue.ts | 233 ++++++++++++------- packages/client/lib/client/index.spec.ts | 31 +-- packages/client/lib/commands/LINDEX.spec.ts | 32 ++- packages/client/lib/commands/LINDEX.ts | 7 +- packages/client/lib/commands/PUBLISH.ts | 4 +- 5 files changed, 189 insertions(+), 118 deletions(-) diff --git a/packages/client/lib/client/commands-queue.ts b/packages/client/lib/client/commands-queue.ts index 4fcae1e8b63..480d7d51408 100644 --- a/packages/client/lib/client/commands-queue.ts +++ b/packages/client/lib/client/commands-queue.ts @@ -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>; +export type PubSubListener< + BUFFER_MODE extends boolean = false, + T = BUFFER_MODE extends true ? Buffer : string +> = (message: T, channel: T) => unknown; + +interface PubSubListeners { + buffers: Set>; + strings: Set>; +} + +type PubSubListenersMap = Map; + +interface PubSubState { + subscribing: number; + subscribed: number; + unsubscribing: number; + listeners: { + channels: PubSubListenersMap; + patterns: PubSubListenersMap; + }; +} export default class RedisCommandsQueue { static #flushQueue(queue: LinkedList, err: Error): void { @@ -50,53 +64,64 @@ export default class RedisCommandsQueue { } } - static #emitPubSubMessage(listeners: Set, 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(); readonly #waitingForReply = new LinkedList(); - readonly #pubSubState = { - subscribing: 0, - subscribed: 0, - unsubscribing: 0 - }; + #pubSubState: PubSubState | undefined; - readonly #pubSubListeners = { - channels: new Map(), - patterns: 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(args: RedisCommandArguments, options?: QueueCommandOptions, bufferMode?: boolean): Promise { - 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({ 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, listener: PubSubListener): Promise { - const channelsToSubscribe: Array = [], - 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( + command: PubSubSubscribeCommands, + channels: PubSubArgumentTypes | Array, + listener: PubSubListener, + bufferMode?: T + ): Promise { + const pubSubState = this.#initiatePubSubState(), + channelsToSubscribe: Array = [], + 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, listener?: PubSubListener): Promise { - const listeners = command === PubSubUnsubscribeCommands.UNSUBSCRIBE ? this.#pubSubListeners.channels : this.#pubSubListeners.patterns; + unsubscribe( + command: PubSubUnsubscribeCommands, + channels?: string | Array, + listener?: PubSubListener, + bufferMode?: T + ): Promise { + 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): Promise { + #pushPubSubCommand(command: PubSubSubscribeCommands | PubSubUnsubscribeCommands, channels: number | Array): Promise { 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 = [command]; + commandArgs: Array = [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 | 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); diff --git a/packages/client/lib/client/index.spec.ts b/packages/client/lib/client/index.spec.ts index 3f0bca45e27..679c7ae692a 100644 --- a/packages/client/lib/client/index.spec.ts +++ b/packages/client/lib/client/index.spec.ts @@ -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 { diff --git a/packages/client/lib/commands/LINDEX.spec.ts b/packages/client/lib/commands/LINDEX.spec.ts index 5e0b1473ec4..aa3aafa789b 100644 --- a/packages/client/lib/commands/LINDEX.spec.ts +++ b/packages/client/lib/commands/LINDEX.spec.ts @@ -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); -}); +}); \ No newline at end of file diff --git a/packages/client/lib/commands/LINDEX.ts b/packages/client/lib/commands/LINDEX.ts index 4c283f0912c..d13bc0c2d02 100644 --- a/packages/client/lib/commands/LINDEX.ts +++ b/packages/client/lib/commands/LINDEX.ts @@ -1,9 +1,8 @@ -export const FIRST_KEY_INDEX = 1; export const IS_READ_ONLY = true; -export function transformArguments(key: string, element: string): Array { - return ['LINDEX', key, element]; +export function transformArguments(key: string, index: number): Array { + return ['LINDEX', key, index.toString()]; } -export declare function transformReply(): string | null; +export declare function transformReply(): string | null; \ No newline at end of file diff --git a/packages/client/lib/commands/PUBLISH.ts b/packages/client/lib/commands/PUBLISH.ts index eda5234df20..cbfcaabd1cd 100644 --- a/packages/client/lib/commands/PUBLISH.ts +++ b/packages/client/lib/commands/PUBLISH.ts @@ -1,4 +1,6 @@ -export function transformArguments(channel: string, message: string): Array { +import { RedisCommandArguments } from '.'; + +export function transformArguments(channel: string | Buffer, message: string | Buffer): RedisCommandArguments { return ['PUBLISH', channel, message]; } From 002d09478ac8e65b920b6baf44ab990342f87f98 Mon Sep 17 00:00:00 2001 From: leibale Date: Wed, 24 Nov 2021 21:47:45 -0500 Subject: [PATCH 0993/1748] Release client@1.0.0 --- packages/client/package.json | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/packages/client/package.json b/packages/client/package.json index 80c2898c7c4..d697d200bea 100644 --- a/packages/client/package.json +++ b/packages/client/package.json @@ -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", From ae06af64fa944948fbe76529a7eba31a0f419c57 Mon Sep 17 00:00:00 2001 From: leibale Date: Wed, 24 Nov 2021 21:50:29 -0500 Subject: [PATCH 0994/1748] npm update --- package-lock.json | 15 ++++++++++----- package.json | 6 +++--- packages/json/package.json | 2 +- packages/search/package.json | 2 +- packages/test-utils/package.json | 2 +- 5 files changed, 16 insertions(+), 11 deletions(-) diff --git a/package-lock.json b/package-lock.json index c6dec588c55..fa3dfc10102 100644 --- a/package-lock.json +++ b/package-lock.json @@ -6415,7 +6415,8 @@ } }, "packages/client": { - "version": "1.0.0-rc.0", + "name": "@node-redis/client", + "version": "1.0.0", "license": "MIT", "dependencies": { "cluster-key-slot": "1.1.0", @@ -6448,6 +6449,7 @@ } }, "packages/json": { + "name": "@node-redis/json", "version": "1.0.0-rc.0", "license": "MIT", "devDependencies": { @@ -6461,10 +6463,11 @@ "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-rc.0", "license": "MIT", "devDependencies": { @@ -6478,10 +6481,11 @@ "typescript": "^4.5.2" }, "peerDependencies": { - "@node-redis/client": "^1.0.0-rc" + "@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", @@ -6496,10 +6500,11 @@ "yargs": "^17.2.1" }, "peerDependencies": { - "@node-redis/client": "^1.0.0-rc" + "@node-redis/client": "^1.0.0" } }, "packages/time-series": { + "name": "@node-redis/time-series", "version": "1.0.0-rc.0", "license": "MIT", "devDependencies": { @@ -6513,7 +6518,7 @@ "typescript": "^4.4.4" }, "peerDependencies": { - "@node-redis/client": "^1.0.0-rc" + "@node-redis/client": "^1.0.0" } } }, diff --git a/package.json b/package.json index 49339e44d26..b4eaedb52eb 100644 --- a/package.json +++ b/package.json @@ -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/client": "^1.0.0", "@node-redis/json": "^1.0.0-rc.0", "@node-redis/search": "^1.0.0-rc.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", diff --git a/packages/json/package.json b/packages/json/package.json index 358ede42e1c..2db2c926248 100644 --- a/packages/json/package.json +++ b/packages/json/package.json @@ -9,7 +9,7 @@ "build": "tsc" }, "peerDependencies": { - "@node-redis/client": "^1.0.0-rc" + "@node-redis/client": "^1.0.0" }, "devDependencies": { "@istanbuljs/nyc-config-typescript": "^1.0.1", diff --git a/packages/search/package.json b/packages/search/package.json index 7ea682d41b7..49e191b5ea5 100644 --- a/packages/search/package.json +++ b/packages/search/package.json @@ -9,7 +9,7 @@ "build": "tsc" }, "peerDependencies": { - "@node-redis/client": "^1.0.0-rc" + "@node-redis/client": "^1.0.0" }, "devDependencies": { "@istanbuljs/nyc-config-typescript": "^1.0.1", diff --git a/packages/test-utils/package.json b/packages/test-utils/package.json index c7cc80a174f..e46f82f0c01 100644 --- a/packages/test-utils/package.json +++ b/packages/test-utils/package.json @@ -8,7 +8,7 @@ "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", From 0315f9ca95a0ae9d95dc6296fba4a6d157432d38 Mon Sep 17 00:00:00 2001 From: leibale Date: Wed, 24 Nov 2021 21:54:40 -0500 Subject: [PATCH 0995/1748] Release search@1.0.0 --- packages/search/package.json | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/packages/search/package.json b/packages/search/package.json index 49e191b5ea5..e5730ab886e 100644 --- a/packages/search/package.json +++ b/packages/search/package.json @@ -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", From 11492491d0890b53c5e5d4558e270acc92cc6e13 Mon Sep 17 00:00:00 2001 From: leibale Date: Wed, 24 Nov 2021 21:55:14 -0500 Subject: [PATCH 0996/1748] update sub modules --- package-lock.json | 12 ++++++------ package.json | 4 ++-- 2 files changed, 8 insertions(+), 8 deletions(-) diff --git a/package-lock.json b/package-lock.json index fa3dfc10102..95c8853fffa 100644 --- a/package-lock.json +++ b/package-lock.json @@ -12,14 +12,14 @@ "./packages/*" ], "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" } }, "node_modules/@babel/code-frame": { @@ -6468,7 +6468,7 @@ }, "packages/search": { "name": "@node-redis/search", - "version": "1.0.0-rc.0", + "version": "1.0.0", "license": "MIT", "devDependencies": { "@istanbuljs/nyc-config-typescript": "^1.0.1", diff --git a/package.json b/package.json index b4eaedb52eb..7e4c9cd5ba5 100644 --- a/package.json +++ b/package.json @@ -18,8 +18,8 @@ }, "dependencies": { "@node-redis/client": "^1.0.0", - "@node-redis/json": "^1.0.0-rc.0", - "@node-redis/search": "^1.0.0-rc.0" + "@node-redis/json": "^1.0.0", + "@node-redis/search": "^1.0.0" }, "devDependencies": { "@tsconfig/node12": "^1.0.9", From b2b41be03b00ba19b49c040df8940c64fc8bbd13 Mon Sep 17 00:00:00 2001 From: leibale Date: Wed, 24 Nov 2021 21:56:17 -0500 Subject: [PATCH 0997/1748] Release redis@4.0.0 --- package-lock.json | 4 ++-- package.json | 2 +- 2 files changed, 3 insertions(+), 3 deletions(-) diff --git a/package-lock.json b/package-lock.json index 95c8853fffa..69d569e7337 100644 --- a/package-lock.json +++ b/package-lock.json @@ -1,12 +1,12 @@ { "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/*" diff --git a/package.json b/package.json index 7e4c9cd5ba5..7eee7f3958b 100644 --- a/package.json +++ b/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", From 3790c6eb4c55add46e906a78c3f4ba4599cde48b Mon Sep 17 00:00:00 2001 From: Leibale Eidelman Date: Wed, 24 Nov 2021 21:59:48 -0500 Subject: [PATCH 0998/1748] v4.0.0 (#1730) MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit * 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 * fix #1652 * ref #1653 - better types * better types * fix 54124793ad1b633d39d372bdff487c9888c017a7 * 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 * 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 * 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 * 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 * 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 * Update(examples): fix case, add timeout, update readme Signed-off-by: Aditya Rastogi 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 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 Co-authored-by: Simon Prickett 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 * revert d32f1edf8a8ede15ede5654168e345ebd95c3ac6 * 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 Co-authored-by: mustard Co-authored-by: Simon Prickett Co-authored-by: Simon Prickett Co-authored-by: Suze Shardlow Co-authored-by: Joshua T Co-authored-by: Aditya Rastogi Co-authored-by: Rohan Kumar Co-authored-by: Kalki Co-authored-by: Chayim Co-authored-by: Da-Jin Chu Co-authored-by: Henrique Corrêa <75134774+HeCorr@users.noreply.github.com> Co-authored-by: Evert Pot --- .github/README.md | 298 ----------- README.md | 314 +++++++++++- docs/client-configuration.md | 4 +- docs/clustering.md | 2 +- docs/v3-to-v4.md | 2 +- examples/README.md | 18 +- examples/managing-json.js | 81 +++ examples/search+json.js | 74 --- examples/search-hashes.js | 82 +++ examples/search-json.js | 93 ++++ package-lock.json | 485 ++++++++++-------- package.json | 12 +- packages/client/CHANGELOG.md | 10 +- packages/client/README.md | 2 +- packages/client/lib/client/commands-queue.ts | 233 +++++---- packages/client/lib/client/index.spec.ts | 31 +- packages/client/lib/client/index.ts | 83 ++- packages/client/lib/cluster/cluster-slots.ts | 62 ++- packages/client/lib/cluster/index.ts | 4 +- .../client/lib/commands/CLUSTER_NODES.spec.ts | 25 + packages/client/lib/commands/CLUSTER_NODES.ts | 13 +- packages/client/lib/commands/LINDEX.spec.ts | 32 +- packages/client/lib/commands/LINDEX.ts | 7 +- packages/client/lib/commands/PUBLISH.ts | 4 +- packages/client/package.json | 16 +- packages/json/README.md | 80 ++- packages/json/lib/commands/ARRPOP.ts | 2 +- packages/json/lib/commands/index.ts | 3 +- packages/json/package.json | 10 +- packages/search/README.md | 120 ++++- packages/search/package.json | 12 +- packages/test-utils/docker/entrypoint.sh | 4 - packages/test-utils/package.json | 12 +- packages/time-series/.npmignore | 6 + 34 files changed, 1418 insertions(+), 818 deletions(-) delete mode 100644 .github/README.md create mode 100644 examples/managing-json.js delete mode 100644 examples/search+json.js create mode 100644 examples/search-hashes.js create mode 100644 examples/search-json.js create mode 100644 packages/time-series/.npmignore diff --git a/.github/README.md b/.github/README.md deleted file mode 100644 index c7046805107..00000000000 --- a/.github/README.md +++ /dev/null @@ -1,298 +0,0 @@ -# Node-Redis - -[![Tests](https://img.shields.io/github/workflow/status/redis/node-redis/Tests/master.svg?label=tests)](https://codecov.io/gh/redis/node-redis) -[![Coverage](https://codecov.io/gh/redis/node-redis/branch/master/graph/badge.svg?token=xcfqHhJC37)](https://codecov.io/gh/redis/node-redis) -[![License](https://img.shields.io/github/license/redis/node-redis.svg)](https://codecov.io/gh/redis/node-redis) -[![Chat](https://img.shields.io/discord/697882427875393627.svg)](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 { - 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](../) | [![Downloads](https://img.shields.io/npm/dm/redis.svg)](https://www.npmjs.com/package/redis/v/next) [![Version](https://img.shields.io/npm/v/redis/next.svg)](https://www.npmjs.com/package/redis/v/next) | -| [@node-redis/client](../packages/client) | [![Downloads](https://img.shields.io/npm/dm/@node-redis/client.svg)](https://www.npmjs.com/package/@node-redis/client/v/next) [![Version](https://img.shields.io/npm/v/@node-redis/client/next.svg)](https://www.npmjs.com/package/@node-redis/client/v/next) | -| [@node-redis/json](../packages/json) | [![Downloads](https://img.shields.io/npm/dm/@node-redis/json.svg)](https://www.npmjs.com/package/@node-redis/json/v/next) [![Version](https://img.shields.io/npm/v/@node-redis/json/next.svg)](https://www.npmjs.com/package/@node-redis/json/v/next) [Redis JSON](https://oss.redis.com/redisjson/) commands | -| [@node-redis/search](../packages/search) | [![Downloads](https://img.shields.io/npm/dm/@node-redis/search.svg)](https://www.npmjs.com/package/@node-redis/search/v/next) [![Version](https://img.shields.io/npm/v/@node-redis/search/next.svg)](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! - -[![Contributors](https://contrib.rocks/image?repo=redis/node-redis)](https://github.com/redis/node-redis/graphs/contributors) - -## License - -This repository is licensed under the "MIT" license. See [LICENSE](LICENSE). diff --git a/README.md b/README.md index a98e6a261b2..d89219214cd 100644 --- a/README.md +++ b/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 + +[![Tests](https://img.shields.io/github/workflow/status/redis/node-redis/Tests/master.svg?label=tests)](https://codecov.io/gh/redis/node-redis) +[![Coverage](https://codecov.io/gh/redis/node-redis/branch/master/graph/badge.svg?token=xcfqHhJC37)](https://codecov.io/gh/redis/node-redis) +[![License](https://img.shields.io/github/license/redis/node-redis.svg)](https://codecov.io/gh/redis/node-redis) +[![Chat](https://img.shields.io/discord/697882427875393627.svg)](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); // +}, true); + +await subscriber.pSubscribe('channe*', (message, channel) => { + console.log(message, channel); // , +}, 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 { + 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](./) | [![Downloads](https://img.shields.io/npm/dm/redis.svg)](https://www.npmjs.com/package/redis/v/next) [![Version](https://img.shields.io/npm/v/redis/next.svg)](https://www.npmjs.com/package/redis/v/next) | +| [@node-redis/client](./packages/client) | [![Downloads](https://img.shields.io/npm/dm/@node-redis/client.svg)](https://www.npmjs.com/package/@node-redis/client/v/next) [![Version](https://img.shields.io/npm/v/@node-redis/client/next.svg)](https://www.npmjs.com/package/@node-redis/client/v/next) | +| [@node-redis/json](./packages/json) | [![Downloads](https://img.shields.io/npm/dm/@node-redis/json.svg)](https://www.npmjs.com/package/@node-redis/json/v/next) [![Version](https://img.shields.io/npm/v/@node-redis/json/next.svg)](https://www.npmjs.com/package/@node-redis/json/v/next) [Redis JSON](https://oss.redis.com/redisjson/) commands | +| [@node-redis/search](./packages/search) | [![Downloads](https://img.shields.io/npm/dm/@node-redis/search.svg)](https://www.npmjs.com/package/@node-redis/search/v/next) [![Version](https://img.shields.io/npm/v/@node-redis/search/next.svg)](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! + +[![Contributors](https://contrib.rocks/image?repo=redis/node-redis)](https://github.com/redis/node-redis/graphs/contributors) + +## License + +This repository is licensed under the "MIT" license. See [LICENSE](LICENSE). diff --git a/docs/client-configuration.md b/docs/client-configuration.md index 1dbbdd8cba2..1b0194615af 100644 --- a/docs/client-configuration.md +++ b/docs/client-configuration.md @@ -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 diff --git a/docs/clustering.md b/docs/clustering.md index 3b5ef94a5c7..f5ef9a9612d 100644 --- a/docs/clustering.md +++ b/docs/clustering.md @@ -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 diff --git a/docs/v3-to-v4.md b/docs/v3-to-v4.md index 7c3e9880431..90267d8245c 100644 --- a/docs/v3-to-v4.md +++ b/docs/v3-to-v4.md @@ -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 diff --git a/examples/README.md b/examples/README.md index aef0b38bdbb..94b043ae483 100644 --- a/examples/README.md +++ b/examples/README.md @@ -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 diff --git a/examples/managing-json.js b/examples/managing-json.js new file mode 100644 index 00000000000..44978a94f05 --- /dev/null +++ b/examples/managing-json.js @@ -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(); + diff --git a/examples/search+json.js b/examples/search+json.js deleted file mode 100644 index adc298289cd..00000000000 --- a/examples/search+json.js +++ /dev/null @@ -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(); diff --git a/examples/search-hashes.js b/examples/search-hashes.js new file mode 100644 index 00000000000..ded4b0edb95 --- /dev/null +++ b/examples/search-hashes.js @@ -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(); \ No newline at end of file diff --git a/examples/search-json.js b/examples/search-json.js new file mode 100644 index 00000000000..a608d3aefa5 --- /dev/null +++ b/examples/search-json.js @@ -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(); diff --git a/package-lock.json b/package-lock.json index 5c3ebb5c208..69d569e7337 100644 --- a/package-lock.json +++ b/package-lock.json @@ -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,33 +6455,33 @@ "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-rc.0", + "version": "1.0.0", "license": "MIT", "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/test-utils": { @@ -6485,18 +6489,36 @@ "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.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": { + "@istanbuljs/nyc-config-typescript": "^1.0.1", + "@node-redis/test-utils": "*", + "@types/node": "^16.11.7", + "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" + "typescript": "^4.4.4" }, "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,12 +7039,12 @@ "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": { @@ -7030,12 +7052,12 @@ "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/test-utils": { @@ -7043,15 +7065,28 @@ "requires": { "@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.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": "*", + "@types/node": "^16.11.7", + "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" + "typescript": "^4.4.4" } }, "@nodelib/fs.scandir": { @@ -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": { diff --git a/package.json b/package.json index 49339e44d26..7eee7f3958b 100644 --- a/package.json +++ b/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", diff --git a/packages/client/CHANGELOG.md b/packages/client/CHANGELOG.md index 21b7177e8b6..39ea947b064 100644 --- a/packages/client/CHANGELOG.md +++ b/packages/client/CHANGELOG.md @@ -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 diff --git a/packages/client/README.md b/packages/client/README.md index 37c326fc42e..6007608ea3b 100644 --- a/packages/client/README.md +++ b/packages/client/README.md @@ -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. diff --git a/packages/client/lib/client/commands-queue.ts b/packages/client/lib/client/commands-queue.ts index 4fcae1e8b63..480d7d51408 100644 --- a/packages/client/lib/client/commands-queue.ts +++ b/packages/client/lib/client/commands-queue.ts @@ -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>; +export type PubSubListener< + BUFFER_MODE extends boolean = false, + T = BUFFER_MODE extends true ? Buffer : string +> = (message: T, channel: T) => unknown; + +interface PubSubListeners { + buffers: Set>; + strings: Set>; +} + +type PubSubListenersMap = Map; + +interface PubSubState { + subscribing: number; + subscribed: number; + unsubscribing: number; + listeners: { + channels: PubSubListenersMap; + patterns: PubSubListenersMap; + }; +} export default class RedisCommandsQueue { static #flushQueue(queue: LinkedList, err: Error): void { @@ -50,53 +64,64 @@ export default class RedisCommandsQueue { } } - static #emitPubSubMessage(listeners: Set, 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(); readonly #waitingForReply = new LinkedList(); - readonly #pubSubState = { - subscribing: 0, - subscribed: 0, - unsubscribing: 0 - }; + #pubSubState: PubSubState | undefined; - readonly #pubSubListeners = { - channels: new Map(), - patterns: 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(args: RedisCommandArguments, options?: QueueCommandOptions, bufferMode?: boolean): Promise { - 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({ 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, listener: PubSubListener): Promise { - const channelsToSubscribe: Array = [], - 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( + command: PubSubSubscribeCommands, + channels: PubSubArgumentTypes | Array, + listener: PubSubListener, + bufferMode?: T + ): Promise { + const pubSubState = this.#initiatePubSubState(), + channelsToSubscribe: Array = [], + 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, listener?: PubSubListener): Promise { - const listeners = command === PubSubUnsubscribeCommands.UNSUBSCRIBE ? this.#pubSubListeners.channels : this.#pubSubListeners.patterns; + unsubscribe( + command: PubSubUnsubscribeCommands, + channels?: string | Array, + listener?: PubSubListener, + bufferMode?: T + ): Promise { + 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): Promise { + #pushPubSubCommand(command: PubSubSubscribeCommands | PubSubUnsubscribeCommands, channels: number | Array): Promise { 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 = [command]; + commandArgs: Array = [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 | 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); diff --git a/packages/client/lib/client/index.spec.ts b/packages/client/lib/client/index.spec.ts index 3f0bca45e27..679c7ae692a 100644 --- a/packages/client/lib/client/index.spec.ts +++ b/packages/client/lib/client/index.spec.ts @@ -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 { diff --git a/packages/client/lib/client/index.ts b/packages/client/lib/client/index.ts index 8802631eda1..c520e36a08f 100644 --- a/packages/client/lib/client/index.ts +++ b/packages/client/lib/client/index.ts @@ -388,42 +388,93 @@ export default class RedisClient select = this.SELECT; - SUBSCRIBE(channels: string | Array, listener: PubSubListener): Promise { - return this.#subscribe(PubSubSubscribeCommands.SUBSCRIBE, channels, listener); + #subscribe( + command: PubSubSubscribeCommands, + channels: string | Array, + listener: PubSubListener, + bufferMode?: T + ): Promise { + const promise = this.#queue.subscribe( + command, + channels, + listener, + bufferMode + ); + this.#tick(); + return promise; + } + + SUBSCRIBE( + channels: string | Array, + listener: PubSubListener, + bufferMode?: T + ): Promise { + return this.#subscribe( + PubSubSubscribeCommands.SUBSCRIBE, + channels, + listener, + bufferMode + ); } subscribe = this.SUBSCRIBE; - PSUBSCRIBE(patterns: string | Array, listener: PubSubListener): Promise { - return this.#subscribe(PubSubSubscribeCommands.PSUBSCRIBE, patterns, listener); + PSUBSCRIBE( + patterns: string | Array, + listener: PubSubListener, + bufferMode?: T + ): Promise { + return this.#subscribe( + PubSubSubscribeCommands.PSUBSCRIBE, + patterns, + listener, + bufferMode + ); } pSubscribe = this.PSUBSCRIBE; - #subscribe(command: PubSubSubscribeCommands, channels: string | Array, listener: PubSubListener): Promise { - const promise = this.#queue.subscribe(command, channels, listener); + #unsubscribe( + command: PubSubUnsubscribeCommands, + channels?: string | Array, + listener?: PubSubListener, + bufferMode?: T + ): Promise { + const promise = this.#queue.unsubscribe(command, channels, listener, bufferMode); this.#tick(); return promise; } - UNSUBSCRIBE(channels?: string | Array, listener?: PubSubListener): Promise { - return this.#unsubscribe(PubSubUnsubscribeCommands.UNSUBSCRIBE, channels, listener); + UNSUBSCRIBE( + channels?: string | Array, + listener?: PubSubListener, + bufferMode?: T + ): Promise { + return this.#unsubscribe( + PubSubUnsubscribeCommands.UNSUBSCRIBE, + channels, + listener, + bufferMode + ); } unsubscribe = this.UNSUBSCRIBE; - PUNSUBSCRIBE(patterns?: string | Array, listener?: PubSubListener): Promise { - return this.#unsubscribe(PubSubUnsubscribeCommands.PUNSUBSCRIBE, patterns, listener); + PUNSUBSCRIBE( + patterns?: string | Array, + listener?: PubSubListener, + bufferMode?: T + ): Promise { + return this.#unsubscribe( + PubSubUnsubscribeCommands.PUNSUBSCRIBE, + patterns, + listener, + bufferMode + ); } pUnsubscribe = this.PUNSUBSCRIBE; - #unsubscribe(command: PubSubUnsubscribeCommands, channels?: string | Array, listener?: PubSubListener): Promise { - const promise = this.#queue.unsubscribe(command, channels, listener); - this.#tick(); - return promise; - } - QUIT(): Promise { return this.#socket.quit(() => { const quitPromise = this.#queue.addCommand(['QUIT']); diff --git a/packages/client/lib/cluster/cluster-slots.ts b/packages/client/lib/cluster/cluster-slots.ts index ff4c79b4d36..f69449efa1a 100644 --- a/packages/client/lib/cluster/cluster-slots.ts +++ b/packages/client/lib/cluster/cluster-slots.ts @@ -42,20 +42,8 @@ export default class RedisClusterSlots): Promise { - 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 { - const client = new this.#Client(clientOptions); + const client = this.#initiateClient(clientOptions); await client.connect(); @@ -72,6 +60,29 @@ export default class RedisClusterSlots; + + async rediscover(startWith: RedisClientType): Promise { + if (!this.#runningRediscoverPromise) { + this.#runningRediscoverPromise = this.#rediscover(startWith) + .finally(() => this.#runningRediscoverPromise = undefined); + } + + return this.#runningRediscoverPromise; + } + + async #rediscover(startWith: RedisClientType): Promise { + 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): Promise { // Override this.#slots and add not existing clients to this.#nodeByUrl const promises: Array> = [], @@ -103,18 +114,23 @@ export default class RedisClusterSlots { + return new this.#Client(this.#clientOptionsDefaults(options)) + .on('error', this.#onError); + } + #initiateClientForNode(nodeData: RedisClusterMasterNode | RedisClusterReplicaNode, readonly: boolean, clientsInUse: Set, promises: Array>): ClusterNode { const url = `${nodeData.host}:${nodeData.port}`; clientsInUse.add(url); @@ -123,15 +139,13 @@ export default class RedisClusterSlots 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 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; } diff --git a/packages/client/lib/commands/CLUSTER_NODES.spec.ts b/packages/client/lib/commands/CLUSTER_NODES.spec.ts index 2b3881d8cd0..d061c59e8ee 100644 --- a/packages/client/lib/commands/CLUSTER_NODES.spec.ts +++ b/packages/client/lib/commands/CLUSTER_NODES.spec.ts @@ -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( diff --git a/packages/client/lib/commands/CLUSTER_NODES.ts b/packages/client/lib/commands/CLUSTER_NODES.ts index d04ffc10a1d..ba4477cdd20 100644 --- a/packages/client/lib/commands/CLUSTER_NODES.ts +++ b/packages/client/lib/commands/CLUSTER_NODES.ts @@ -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 { 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), diff --git a/packages/client/lib/commands/LINDEX.spec.ts b/packages/client/lib/commands/LINDEX.spec.ts index 5e0b1473ec4..aa3aafa789b 100644 --- a/packages/client/lib/commands/LINDEX.spec.ts +++ b/packages/client/lib/commands/LINDEX.spec.ts @@ -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); -}); +}); \ No newline at end of file diff --git a/packages/client/lib/commands/LINDEX.ts b/packages/client/lib/commands/LINDEX.ts index 4c283f0912c..d13bc0c2d02 100644 --- a/packages/client/lib/commands/LINDEX.ts +++ b/packages/client/lib/commands/LINDEX.ts @@ -1,9 +1,8 @@ -export const FIRST_KEY_INDEX = 1; export const IS_READ_ONLY = true; -export function transformArguments(key: string, element: string): Array { - return ['LINDEX', key, element]; +export function transformArguments(key: string, index: number): Array { + return ['LINDEX', key, index.toString()]; } -export declare function transformReply(): string | null; +export declare function transformReply(): string | null; \ No newline at end of file diff --git a/packages/client/lib/commands/PUBLISH.ts b/packages/client/lib/commands/PUBLISH.ts index eda5234df20..cbfcaabd1cd 100644 --- a/packages/client/lib/commands/PUBLISH.ts +++ b/packages/client/lib/commands/PUBLISH.ts @@ -1,4 +1,6 @@ -export function transformArguments(channel: string, message: string): Array { +import { RedisCommandArguments } from '.'; + +export function transformArguments(channel: string | Buffer, message: string | Buffer): RedisCommandArguments { return ['PUBLISH', channel, message]; } diff --git a/packages/client/package.json b/packages/client/package.json index 7a6d23f5ff9..d697d200bea 100644 --- a/packages/client/package.json +++ b/packages/client/package.json @@ -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" diff --git a/packages/json/README.md b/packages/json/README.md index 1cd599d5ea8..5b6d5ba8ce4 100644 --- a/packages/json/README.md +++ b/packages/json/README.md @@ -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 + }); +``` diff --git a/packages/json/lib/commands/ARRPOP.ts b/packages/json/lib/commands/ARRPOP.ts index 5d8785a8d94..932b3294d85 100644 --- a/packages/json/lib/commands/ARRPOP.ts +++ b/packages/json/lib/commands/ARRPOP.ts @@ -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 '.'; diff --git a/packages/json/lib/commands/index.ts b/packages/json/lib/commands/index.ts index 91b4f7dc4b5..a3c561addcc 100644 --- a/packages/json/lib/commands/index.ts +++ b/packages/json/lib/commands/index.ts @@ -84,8 +84,9 @@ export function transformRedisJsonNullReply(json: string | null): RedisJSON | nu return transformRedisJsonReply(json); } +export function transformRedisJsonNullArrayNullReply(jsons: Array | null): Array | null { + if (jsons === null) return null; -export function transformRedisJsonNullArrayReply(jsons: Array): Array { return jsons.map(transformRedisJsonNullReply); } diff --git a/packages/json/package.json b/packages/json/package.json index 7e5f6e10c1f..2db2c926248 100644 --- a/packages/json/package.json +++ b/packages/json/package.json @@ -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" } } diff --git a/packages/search/README.md b/packages/search/README.md index 856a75fbb5a..f54316d3c18 100644 --- a/packages/search/README.md +++ b/packages/search/README.md @@ -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]'); +``` diff --git a/packages/search/package.json b/packages/search/package.json index a72678c2add..e5730ab886e 100644 --- a/packages/search/package.json +++ b/packages/search/package.json @@ -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" } } diff --git a/packages/test-utils/docker/entrypoint.sh b/packages/test-utils/docker/entrypoint.sh index 244977e83c4..d4006f55622 100755 --- a/packages/test-utils/docker/entrypoint.sh +++ b/packages/test-utils/docker/entrypoint.sh @@ -1,7 +1,3 @@ #!/bin/bash -echo testststealkshdfklhasdf - -echo $REDIS_ARGUMENTS - redis-server $REDIS_ARGUMENTS diff --git a/packages/test-utils/package.json b/packages/test-utils/package.json index 47ddc25acff..e46f82f0c01 100644 --- a/packages/test-utils/package.json +++ b/packages/test-utils/package.json @@ -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" } } diff --git a/packages/time-series/.npmignore b/packages/time-series/.npmignore new file mode 100644 index 00000000000..bbef2b404fb --- /dev/null +++ b/packages/time-series/.npmignore @@ -0,0 +1,6 @@ +.nyc_output/ +coverage/ +lib/ +.nycrc.json +.release-it.json +tsconfig.json From 957f5a17330308e883b187054216af4e29bbb1df Mon Sep 17 00:00:00 2001 From: leibale Date: Wed, 24 Nov 2021 22:06:00 -0500 Subject: [PATCH 0999/1748] update README --- README.md | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/README.md b/README.md index d89219214cd..b400b6db375 100644 --- a/README.md +++ b/README.md @@ -294,10 +294,10 @@ Node Redis is supported with the following versions of Redis: | Name | Description | |-----------------------------------------|--------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------| -| [redis](./) | [![Downloads](https://img.shields.io/npm/dm/redis.svg)](https://www.npmjs.com/package/redis/v/next) [![Version](https://img.shields.io/npm/v/redis/next.svg)](https://www.npmjs.com/package/redis/v/next) | -| [@node-redis/client](./packages/client) | [![Downloads](https://img.shields.io/npm/dm/@node-redis/client.svg)](https://www.npmjs.com/package/@node-redis/client/v/next) [![Version](https://img.shields.io/npm/v/@node-redis/client/next.svg)](https://www.npmjs.com/package/@node-redis/client/v/next) | -| [@node-redis/json](./packages/json) | [![Downloads](https://img.shields.io/npm/dm/@node-redis/json.svg)](https://www.npmjs.com/package/@node-redis/json/v/next) [![Version](https://img.shields.io/npm/v/@node-redis/json/next.svg)](https://www.npmjs.com/package/@node-redis/json/v/next) [Redis JSON](https://oss.redis.com/redisjson/) commands | -| [@node-redis/search](./packages/search) | [![Downloads](https://img.shields.io/npm/dm/@node-redis/search.svg)](https://www.npmjs.com/package/@node-redis/search/v/next) [![Version](https://img.shields.io/npm/v/@node-redis/search/next.svg)](https://www.npmjs.com/package/@node-redis/search/v/next) [Redis Search](https://oss.redis.com/redisearch/) commands | +| [redis](./) | [![Downloads](https://img.shields.io/npm/dm/redis.svg)](https://www.npmjs.com/package/redis) [![Version](https://img.shields.io/npm/v/redis.svg)](https://www.npmjs.com/package/redis) | +| [@node-redis/client](./packages/client) | [![Downloads](https://img.shields.io/npm/dm/@node-redis/client.svg)](https://www.npmjs.com/package/@node-redis/client) [![Version](https://img.shields.io/npm/v/@node-redis/client.svg)](https://www.npmjs.com/package/@node-redis/client) | +| [@node-redis/json](./packages/json) | [![Downloads](https://img.shields.io/npm/dm/@node-redis/json.svg)](https://www.npmjs.com/package/@node-redis/json) [![Version](https://img.shields.io/npm/v/@node-redis/json.svg)](https://www.npmjs.com/package/@node-redis/json) [Redis JSON](https://oss.redis.com/redisjson/) commands | +| [@node-redis/search](./packages/search) | [![Downloads](https://img.shields.io/npm/dm/@node-redis/search.svg)](https://www.npmjs.com/package/@node-redis/search) [![Version](https://img.shields.io/npm/v/@node-redis/search.svg)](https://www.npmjs.com/package/@node-redis/search) [Redis Search](https://oss.redis.com/redisearch/) commands | ## Contributing From 14b16b9e17c19982bbe470a29ff8d3e7155ae9f7 Mon Sep 17 00:00:00 2001 From: Simon Prickett Date: Fri, 26 Nov 2021 23:51:54 +0000 Subject: [PATCH 1000/1748] Fixed `zScanIterator` example (#1733) The example for `zScanIterator` had the wrong values in the `for` loop. --- README.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/README.md b/README.md index b400b6db375..cc2edec9e77 100644 --- a/README.md +++ b/README.md @@ -180,7 +180,7 @@ 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')) {} +for await (const { score, value } of client.zScanIterator('sorted-set')) {} ``` You can override the default options by providing a configuration object: From c96a0dd904a795a7b00b09602dbfff8bf8e87f22 Mon Sep 17 00:00:00 2001 From: leibale Date: Fri, 26 Nov 2021 22:57:19 -0500 Subject: [PATCH 1001/1748] fix #1738 - add support for buffers in HSET --- packages/client/lib/commands/HSET.spec.ts | 21 ++++++++--- packages/client/lib/commands/HSET.ts | 45 ++++++++++++++++------- 2 files changed, 47 insertions(+), 19 deletions(-) diff --git a/packages/client/lib/commands/HSET.spec.ts b/packages/client/lib/commands/HSET.spec.ts index 507c7bbbf74..ef00931f14b 100644 --- a/packages/client/lib/commands/HSET.spec.ts +++ b/packages/client/lib/commands/HSET.spec.ts @@ -2,13 +2,22 @@ import { strict as assert } from 'assert'; import { transformArguments } from './HSET'; import testUtils, { GLOBAL } from '../test-utils'; -describe('HSET', () => { +describe.only('HSET', () => { describe('transformArguments', () => { - it('field, value', () => { - assert.deepEqual( - transformArguments('key', 'field', 'value'), - ['HSET', 'key', 'field', 'value'] - ); + describe('field, value', () => { + it('string', () => { + assert.deepEqual( + transformArguments('key', 'field', 'value'), + ['HSET', 'key', 'field', 'value'] + ); + }); + + it('Buffer', () => { + assert.deepEqual( + transformArguments('key', Buffer.from('field'), Buffer.from('value')), + ['HSET', 'key', Buffer.from('field'), Buffer.from('value')] + ); + }); }); it('Map', () => { diff --git a/packages/client/lib/commands/HSET.ts b/packages/client/lib/commands/HSET.ts index f7c56c5768d..be9fece167d 100644 --- a/packages/client/lib/commands/HSET.ts +++ b/packages/client/lib/commands/HSET.ts @@ -1,24 +1,27 @@ import { RedisCommandArguments } from '.'; -type HSETObject = Record; +type Types = string | number | Buffer; -type HSETMap = Map; +type HSETObject = Record; -type HSETTuples = Array<[string, string]> | Array; +type HSETMap = Map; + +type HSETTuples = Array<[Types, Types]> | Array; export const FIRST_KEY_INDEX = 1; -type GenericArguments = [key: string]; +type GenericArguments = [key: string | Buffer]; -type SingleFieldArguments = [...generic: GenericArguments, field: string, value: string]; +type SingleFieldArguments = [...generic: GenericArguments, field: Types, value: Types]; type MultipleFieldsArguments = [...generic: GenericArguments, value: HSETObject | HSETMap | HSETTuples]; export function transformArguments(...[ key, value, fieldValue ]: SingleFieldArguments | MultipleFieldsArguments): RedisCommandArguments { - const args = ['HSET', key]; + const args: RedisCommandArguments = ['HSET', key]; - if (typeof value === 'string') { - args.push(value, fieldValue!); + if (typeof value === 'string' || typeof value === 'number' || Buffer.isBuffer(value)) { + pushValue(args, value); + pushValue(args, fieldValue!); } else if (value instanceof Map) { pushMap(args, value); } else if (Array.isArray(value)) { @@ -30,20 +33,36 @@ export function transformArguments(...[ key, value, fieldValue ]: SingleFieldArg return args; } -function pushMap(args: Array, map: HSETMap): void { +function pushMap(args: RedisCommandArguments, map: HSETMap): void { for (const [key, value] of map.entries()) { - args.push(key.toString(), value.toString()); + pushValue(args, key); + pushValue(args, value); } } -function pushTuples(args: Array, tuples: HSETTuples): void { - args.push(...tuples.flat()); +function pushTuples(args: RedisCommandArguments, tuples: HSETTuples): void { + for (const tuple of tuples) { + if (Array.isArray(tuple)) { + pushTuples(args, tuple); + continue; + } + + pushValue(args, tuple); + } } -function pushObject(args: Array, object: HSETObject): void { +function pushObject(args: RedisCommandArguments, object: HSETObject): void { for (const key of Object.keys(object)) { args.push(key.toString(), object[key].toString()); } } +function pushValue(args: RedisCommandArguments, value: Types): void { + args.push( + typeof value === 'number' ? + value.toString() : + value + ); +} + export declare function transformReply(): number; From 2d18125fdb33b7ca4f12637489c4b9189b682f62 Mon Sep 17 00:00:00 2001 From: leibale Date: Fri, 26 Nov 2021 23:02:29 -0500 Subject: [PATCH 1002/1748] fix for c96a0dd904a795a7b00b09602dbfff8bf8e87f22 - remove .only from HSET tests --- packages/client/lib/commands/HSET.spec.ts | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/packages/client/lib/commands/HSET.spec.ts b/packages/client/lib/commands/HSET.spec.ts index ef00931f14b..fa12e108618 100644 --- a/packages/client/lib/commands/HSET.spec.ts +++ b/packages/client/lib/commands/HSET.spec.ts @@ -2,7 +2,7 @@ import { strict as assert } from 'assert'; import { transformArguments } from './HSET'; import testUtils, { GLOBAL } from '../test-utils'; -describe.only('HSET', () => { +describe('HSET', () => { describe('transformArguments', () => { describe('field, value', () => { it('string', () => { From d0de622a86c67fe1d067c61a8e30064b0dc6443e Mon Sep 17 00:00:00 2001 From: leibale Date: Fri, 26 Nov 2021 23:08:18 -0500 Subject: [PATCH 1003/1748] increase HSET test coverage --- packages/client/lib/commands/HSET.spec.ts | 11 +++++++++-- 1 file changed, 9 insertions(+), 2 deletions(-) diff --git a/packages/client/lib/commands/HSET.spec.ts b/packages/client/lib/commands/HSET.spec.ts index fa12e108618..5815b0f389b 100644 --- a/packages/client/lib/commands/HSET.spec.ts +++ b/packages/client/lib/commands/HSET.spec.ts @@ -12,10 +12,17 @@ describe('HSET', () => { ); }); + it('number', () => { + assert.deepEqual( + transformArguments('key', '1', '2'), + ['HSET', 'key', '1', '2'] + ); + }); + it('Buffer', () => { assert.deepEqual( - transformArguments('key', Buffer.from('field'), Buffer.from('value')), - ['HSET', 'key', Buffer.from('field'), Buffer.from('value')] + transformArguments(Buffer.from('key'), Buffer.from('field'), Buffer.from('value')), + ['HSET', Buffer.from('key'), Buffer.from('field'), Buffer.from('value')] ); }); }); From ac378275edf893129fa404855179c1b30735211f Mon Sep 17 00:00:00 2001 From: leibale Date: Fri, 26 Nov 2021 23:16:13 -0500 Subject: [PATCH 1004/1748] fix for d0de622a86c67fe1d067c61a8e30064b0dc6443e --- packages/client/lib/commands/HSET.spec.ts | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/packages/client/lib/commands/HSET.spec.ts b/packages/client/lib/commands/HSET.spec.ts index 5815b0f389b..e4160810810 100644 --- a/packages/client/lib/commands/HSET.spec.ts +++ b/packages/client/lib/commands/HSET.spec.ts @@ -14,7 +14,7 @@ describe('HSET', () => { it('number', () => { assert.deepEqual( - transformArguments('key', '1', '2'), + transformArguments('key', 1, 2), ['HSET', 'key', '1', '2'] ); }); From e81bf6491413d8549c13c9d69e240a9f23483017 Mon Sep 17 00:00:00 2001 From: leibale Date: Sat, 27 Nov 2021 22:36:18 -0500 Subject: [PATCH 1005/1748] ref #1741 - fix socket options type --- packages/client/lib/client/index.ts | 5 +++-- packages/client/lib/client/socket.ts | 23 ++++++----------------- 2 files changed, 9 insertions(+), 19 deletions(-) diff --git a/packages/client/lib/client/index.ts b/packages/client/lib/client/index.ts index c520e36a08f..6bc0aec74d9 100644 --- a/packages/client/lib/client/index.ts +++ b/packages/client/lib/client/index.ts @@ -1,6 +1,6 @@ import COMMANDS from './commands'; import { RedisCommand, RedisCommandArguments, RedisCommandRawReply, RedisCommandReply, RedisModules, RedisPlugins, RedisScript, RedisScripts } from '../commands'; -import RedisSocket, { RedisSocketOptions, RedisNetSocketOptions, RedisTlsSocketOptions } from './socket'; +import RedisSocket, { RedisSocketOptions, RedisTlsSocketOptions } from './socket'; import RedisCommandsQueue, { PubSubListener, PubSubSubscribeCommands, PubSubUnsubscribeCommands, QueueCommandOptions } from './commands-queue'; import RedisClientMultiCommand, { RedisClientMultiCommandType } from './multi-command'; import { RedisMultiQueuedCommand } from '../multi-command'; @@ -13,6 +13,7 @@ import { extendWithCommands, extendWithModulesAndScripts, LegacyCommandArguments import { Pool, Options as PoolOptions, createPool } from 'generic-pool'; import { ClientClosedError, DisconnectsClientError } from '../errors'; import { URL } from 'url'; +import { TcpSocketConnectOpts } from 'net'; export interface RedisClientOptions extends RedisPlugins { url?: string; @@ -97,7 +98,7 @@ export default class RedisClient } if (port) { - (parsed.socket as RedisNetSocketOptions).port = Number(port); + (parsed.socket as TcpSocketConnectOpts).port = Number(port); } if (username) { diff --git a/packages/client/lib/client/socket.ts b/packages/client/lib/client/socket.ts index d42b42d64d6..d9930072ddd 100644 --- a/packages/client/lib/client/socket.ts +++ b/packages/client/lib/client/socket.ts @@ -13,20 +13,13 @@ export interface RedisSocketCommonOptions { reconnectStrategy?(retries: number): number | Error; } -export interface RedisNetSocketOptions extends RedisSocketCommonOptions { - port?: number; - host?: string; -} - -export interface RedisUnixSocketOptions extends RedisSocketCommonOptions { - path: string; -} +export type RedisNetSocketOptions = Partial; -export interface RedisTlsSocketOptions extends RedisNetSocketOptions, tls.SecureContextOptions, tls.CommonConnectionOptions { +export interface RedisTlsSocketOptions extends RedisSocketCommonOptions, tls.ConnectionOptions { tls: true; } -export type RedisSocketOptions = RedisNetSocketOptions | RedisUnixSocketOptions | RedisTlsSocketOptions; +export type RedisSocketOptions = RedisSocketCommonOptions & (RedisNetSocketOptions | RedisTlsSocketOptions); interface CreateSocketReturn { connectEvent: string; @@ -38,9 +31,9 @@ export type RedisSocketInitiator = () => Promise; export default class RedisSocket extends EventEmitter { static #initiateOptions(options?: RedisSocketOptions): RedisSocketOptions { options ??= {}; - if (!RedisSocket.#isUnixSocket(options)) { - (options as RedisNetSocketOptions).port ??= 6379; - (options as RedisNetSocketOptions).host ??= '127.0.0.1'; + if (!(options as net.IpcSocketConnectOpts).path) { + (options as net.TcpSocketConnectOpts).port ??= 6379; + (options as net.TcpSocketConnectOpts).host ??= '127.0.0.1'; } options.connectTimeout ??= 5000; @@ -54,10 +47,6 @@ export default class RedisSocket extends EventEmitter { return Math.min(retries * 50, 500); } - static #isUnixSocket(options: RedisSocketOptions): options is RedisUnixSocketOptions { - return Object.prototype.hasOwnProperty.call(options, 'path'); - } - static #isTlsSocket(options: RedisSocketOptions): options is RedisTlsSocketOptions { return (options as RedisTlsSocketOptions).tls === true; } From 2d2d58d88138d2e9c935d45eb574803d7e95a60e Mon Sep 17 00:00:00 2001 From: leibale Date: Sat, 27 Nov 2021 22:53:59 -0500 Subject: [PATCH 1006/1748] fix #1741 - change default to `localhost` and update docs --- docs/client-configuration.md | 5 +++-- packages/client/lib/client/socket.ts | 2 +- 2 files changed, 4 insertions(+), 3 deletions(-) diff --git a/docs/client-configuration.md b/docs/client-configuration.md index 1b0194615af..f4aa8e99d6b 100644 --- a/docs/client-configuration.md +++ b/docs/client-configuration.md @@ -3,9 +3,10 @@ | Property | Default | Description | |--------------------------|------------------------------------------|-----------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------| | url | | `redis[s]://[[username][:password]@][host][:port][/db-number]` (see [`redis`](https://www.iana.org/assignments/uri-schemes/prov/redis) and [`rediss`](https://www.iana.org/assignments/uri-schemes/prov/rediss) IANA registration for more details) | -| socket | | Object defining socket connection properties | -| socket.host | `'localhost'` | Hostname to connect to | +| socket | | Object defining socket connection properties. Any [`net.createConnection`](https://nodejs.org/api/net.html#netcreateconnectionoptions-connectlistener) option that is not listed here is supported as well | | socket.port | `6379` | Port to connect to | +| socket.host | `'localhost'` | Hostname to connect to | +| socket.family | `0` | Version of IP stack. Must be `4 \| 6 \| 0`. The value `0` indicates that both IPv4 and IPv6 addresses are allowed. | | socket.path | | UNIX Socket to connect to | | socket.connectTimeout | `5000` | The timeout for connecting to the Redis Server (in milliseconds) | | socket.noDelay | `true` | Enable/disable the use of [`Nagle's algorithm`](https://nodejs.org/api/net.html#net_socket_setnodelay_nodelay) | diff --git a/packages/client/lib/client/socket.ts b/packages/client/lib/client/socket.ts index d9930072ddd..8267e7a07b8 100644 --- a/packages/client/lib/client/socket.ts +++ b/packages/client/lib/client/socket.ts @@ -33,7 +33,7 @@ export default class RedisSocket extends EventEmitter { options ??= {}; if (!(options as net.IpcSocketConnectOpts).path) { (options as net.TcpSocketConnectOpts).port ??= 6379; - (options as net.TcpSocketConnectOpts).host ??= '127.0.0.1'; + (options as net.TcpSocketConnectOpts).host ??= 'localhost'; } options.connectTimeout ??= 5000; From 90c37bd1af8262f3f954a5b3493e978c808a131c Mon Sep 17 00:00:00 2001 From: leibale Date: Sat, 27 Nov 2021 22:58:53 -0500 Subject: [PATCH 1007/1748] fix #1739 - add support for number as value in HSET --- packages/client/lib/commands/SET.spec.ts | 9 ++++++- packages/client/lib/commands/SET.ts | 30 ++++++++++++------------ 2 files changed, 23 insertions(+), 16 deletions(-) diff --git a/packages/client/lib/commands/SET.spec.ts b/packages/client/lib/commands/SET.spec.ts index 353885a3097..f09f8621f28 100644 --- a/packages/client/lib/commands/SET.spec.ts +++ b/packages/client/lib/commands/SET.spec.ts @@ -4,13 +4,20 @@ import { transformArguments } from './SET'; describe('SET', () => { describe('transformArguments', () => { - it('simple', () => { + it('string', () => { assert.deepEqual( transformArguments('key', 'value'), ['SET', 'key', 'value'] ); }); + it('number', () => { + assert.deepEqual( + transformArguments('key', 1), + ['SET', 'key', '1'] + ); + }); + describe('TTL', () => { it('with EX', () => { assert.deepEqual( diff --git a/packages/client/lib/commands/SET.ts b/packages/client/lib/commands/SET.ts index fdc7eef1986..7a004e1fb20 100644 --- a/packages/client/lib/commands/SET.ts +++ b/packages/client/lib/commands/SET.ts @@ -24,32 +24,32 @@ interface SetCommonOptions { type SetOptions = SetTTL & SetGuards & SetCommonOptions; -export function transformArguments(key: string | Buffer, value: string | Buffer, options?: SetOptions): RedisCommandArguments { - const args = ['SET', key, value]; - - if (!options) { - return args; - } - - if (options.EX) { +export function transformArguments(key: string | Buffer, value: string | number | Buffer, options?: SetOptions): RedisCommandArguments { + const args = [ + 'SET', + key, + typeof value === 'number' ? value.toString() : value + ]; + + if (options?.EX) { args.push('EX', options.EX.toString()); - } else if (options.PX) { + } else if (options?.PX) { args.push('PX', options.PX.toString()); - } else if (options.EXAT) { + } else if (options?.EXAT) { args.push('EXAT', options.EXAT.toString()); - } else if (options.PXAT) { + } else if (options?.PXAT) { args.push('PXAT', options.PXAT.toString()); - } else if (options.KEEPTTL) { + } else if (options?.KEEPTTL) { args.push('KEEPTTL'); } - if (options.NX) { + if (options?.NX) { args.push('NX'); - } else if (options.XX) { + } else if (options?.XX) { args.push('XX'); } - if (options.GET) { + if (options?.GET) { args.push('GET'); } From f2d46c8787ee83afe7cef79a138da26fd9f41b93 Mon Sep 17 00:00:00 2001 From: leibale Date: Sat, 27 Nov 2021 23:02:52 -0500 Subject: [PATCH 1008/1748] fix tests workflow --- .github/workflows/tests.yml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.github/workflows/tests.yml b/.github/workflows/tests.yml index f8e68c0e203..ad8b0332f8e 100644 --- a/.github/workflows/tests.yml +++ b/.github/workflows/tests.yml @@ -34,7 +34,7 @@ jobs: - name: Build tests tools run: npm run build:tests-tools - name: Run Tests - run: npm run test -- --forbid-only --redis-version=${{ matrix.redis-version }} + run: npm run test -- -- --forbid-only --redis-version=${{ matrix.redis-version }} - name: Upload to Codecov run: | curl https://keybase.io/codecovsecurity/pgp_keys.asc | gpg --no-default-keyring --keyring trustedkeys.gpg --import From bc1bf7e7b1f3247884820406677abe2cb754b1b9 Mon Sep 17 00:00:00 2001 From: leibale Date: Sat, 27 Nov 2021 23:51:15 -0500 Subject: [PATCH 1009/1748] increase pushGeoCountArgument test coverage --- .../lib/commands/generic-transformers.spec.ts | 41 ++++++++++++------- 1 file changed, 26 insertions(+), 15 deletions(-) diff --git a/packages/client/lib/commands/generic-transformers.spec.ts b/packages/client/lib/commands/generic-transformers.spec.ts index fa5c5086136..53b1b41ef21 100644 --- a/packages/client/lib/commands/generic-transformers.spec.ts +++ b/packages/client/lib/commands/generic-transformers.spec.ts @@ -320,21 +320,32 @@ describe('Generic Transformers', () => { ); }); - it('with COUNT', () => { - assert.deepEqual( - pushGeoCountArgument([], 1), - ['COUNT', '1'] - ); - }); - - it('with ANY', () => { - assert.deepEqual( - pushGeoCountArgument([], { - value: 1, - ANY: true - }), - ['COUNT', '1', 'ANY'] - ); + describe('with COUNT', () => { + it('number', () => { + assert.deepEqual( + pushGeoCountArgument([], 1), + ['COUNT', '1'] + ); + }); + + describe('object', () => { + it('value', () => { + assert.deepEqual( + pushGeoCountArgument([], { value: 1 }), + ['COUNT', '1'] + ); + }); + + it('value, ANY', () => { + assert.deepEqual( + pushGeoCountArgument([], { + value: 1, + ANY: true + }), + ['COUNT', '1', 'ANY'] + ); + }); + }); }); }); From f648f37f5aa45c6d59e4b5c55ec742702ecde723 Mon Sep 17 00:00:00 2001 From: leibale Date: Mon, 29 Nov 2021 08:52:14 -0500 Subject: [PATCH 1010/1748] init time series --- packages/time-series/.nycrc.json | 4 + packages/time-series/.release-it.json | 10 + packages/time-series/README.md | 2 + packages/time-series/lib/commands/ADD.spec.ts | 80 ++++++ packages/time-series/lib/commands/ADD.ts | 46 +++ .../time-series/lib/commands/ALTER.spec.ts | 51 ++++ packages/time-series/lib/commands/ALTER.ts | 20 ++ .../time-series/lib/commands/CREATE.spec.ts | 80 ++++++ packages/time-series/lib/commands/CREATE.ts | 42 +++ .../lib/commands/CREATERULE.spec.ts | 25 ++ .../time-series/lib/commands/CREATERULE.ts | 20 ++ packages/time-series/lib/commands/DECRBY.ts | 10 + packages/time-series/lib/commands/DEL.ts | 15 + .../time-series/lib/commands/DELETERULE.ts | 9 + packages/time-series/lib/commands/GET.ts | 15 + packages/time-series/lib/commands/INCRBY.ts | 10 + packages/time-series/lib/commands/INFO.ts | 82 ++++++ .../time-series/lib/commands/INFO_DEBUG.ts | 53 ++++ packages/time-series/lib/commands/MADD.ts | 23 ++ packages/time-series/lib/commands/MGET.ts | 31 +++ .../time-series/lib/commands/QUERYINDEX.ts | 7 + packages/time-series/lib/commands/RANGE.ts | 21 ++ packages/time-series/lib/commands/REVRANGE.ts | 21 ++ .../time-series/lib/commands/index.spec.ts | 231 ++++++++++++++++ packages/time-series/lib/commands/index.ts | 261 ++++++++++++++++++ packages/time-series/lib/index.ts | 3 + packages/time-series/lib/test-utils.ts | 21 ++ packages/time-series/package.json | 24 ++ packages/time-series/tsconfig.json | 9 + 29 files changed, 1226 insertions(+) create mode 100644 packages/time-series/.nycrc.json create mode 100644 packages/time-series/.release-it.json create mode 100644 packages/time-series/README.md create mode 100644 packages/time-series/lib/commands/ADD.spec.ts create mode 100644 packages/time-series/lib/commands/ADD.ts create mode 100644 packages/time-series/lib/commands/ALTER.spec.ts create mode 100644 packages/time-series/lib/commands/ALTER.ts create mode 100644 packages/time-series/lib/commands/CREATE.spec.ts create mode 100644 packages/time-series/lib/commands/CREATE.ts create mode 100644 packages/time-series/lib/commands/CREATERULE.spec.ts create mode 100644 packages/time-series/lib/commands/CREATERULE.ts create mode 100644 packages/time-series/lib/commands/DECRBY.ts create mode 100644 packages/time-series/lib/commands/DEL.ts create mode 100644 packages/time-series/lib/commands/DELETERULE.ts create mode 100644 packages/time-series/lib/commands/GET.ts create mode 100644 packages/time-series/lib/commands/INCRBY.ts create mode 100644 packages/time-series/lib/commands/INFO.ts create mode 100644 packages/time-series/lib/commands/INFO_DEBUG.ts create mode 100644 packages/time-series/lib/commands/MADD.ts create mode 100644 packages/time-series/lib/commands/MGET.ts create mode 100644 packages/time-series/lib/commands/QUERYINDEX.ts create mode 100644 packages/time-series/lib/commands/RANGE.ts create mode 100644 packages/time-series/lib/commands/REVRANGE.ts create mode 100644 packages/time-series/lib/commands/index.spec.ts create mode 100644 packages/time-series/lib/commands/index.ts create mode 100644 packages/time-series/lib/index.ts create mode 100644 packages/time-series/lib/test-utils.ts create mode 100644 packages/time-series/package.json create mode 100644 packages/time-series/tsconfig.json diff --git a/packages/time-series/.nycrc.json b/packages/time-series/.nycrc.json new file mode 100644 index 00000000000..b4e671e178f --- /dev/null +++ b/packages/time-series/.nycrc.json @@ -0,0 +1,4 @@ +{ + "extends": "@istanbuljs/nyc-config-typescript", + "exclude": ["**/*.spec.ts", "lib/test-utils.ts"] +} diff --git a/packages/time-series/.release-it.json b/packages/time-series/.release-it.json new file mode 100644 index 00000000000..72cb1016ef4 --- /dev/null +++ b/packages/time-series/.release-it.json @@ -0,0 +1,10 @@ +{ + "git": { + "tagName": "search@${version}", + "commitMessage": "Release ${tagName}", + "tagAnnotation": "Release ${tagName}" + }, + "npm": { + "publishArgs": ["--access", "public"] + } +} diff --git a/packages/time-series/README.md b/packages/time-series/README.md new file mode 100644 index 00000000000..856a75fbb5a --- /dev/null +++ b/packages/time-series/README.md @@ -0,0 +1,2 @@ +# @node-redis/search +The sources and docs for this package are in the main [node-redis](https://github.com/redis/node-redis) repo. diff --git a/packages/time-series/lib/commands/ADD.spec.ts b/packages/time-series/lib/commands/ADD.spec.ts new file mode 100644 index 00000000000..94ad30627f8 --- /dev/null +++ b/packages/time-series/lib/commands/ADD.spec.ts @@ -0,0 +1,80 @@ +import { strict as assert } from 'assert'; +import testUtils, { GLOBAL } from '../test-utils'; +import { transformArguments } from './ADD'; +import { TimeSeriesDuplicatePolicies, TimeSeriesEncoding } from '.'; + +describe('ADD', () => { + describe('transformArguments', () => { + it('without options', () => { + assert.deepEqual( + transformArguments('key', '*', 1), + ['TS.ADD', 'key', '*', '1'] + ); + }); + + it('with RETENTION', () => { + assert.deepEqual( + transformArguments('key', '*', 1, { + RETENTION: 1 + }), + ['TS.ADD', 'key', '*', '1', 'RETENTION', '1'] + ); + }); + + it('with ENCODING', () => { + assert.deepEqual( + transformArguments('key', '*', 1, { + ENCODING: TimeSeriesEncoding.UNCOMPRESSED + }), + ['TS.ADD', 'key', '*', '1', 'ENCODING', 'UNCOMPRESSED'] + ); + }); + + it('with CHUNK_SIZE', () => { + assert.deepEqual( + transformArguments('key', '*', 1, { + CHUNK_SIZE: 1 + }), + ['TS.ADD', 'key', '*', '1', 'CHUNK_SIZE', '1'] + ); + }); + + it('with ON_DUPLICATE', () => { + assert.deepEqual( + transformArguments('key', '*', 1, { + ON_DUPLICATE: TimeSeriesDuplicatePolicies.BLOCK + }), + ['TS.ADD', 'key', '*', '1', 'ON_DUPLICATE', 'BLOCK'] + ); + }); + + it('with LABELS', () => { + assert.deepEqual( + transformArguments('key', '*', 1, { + LABELS: { label: 'value' } + }), + ['TS.ADD', 'key', '*', '1', 'LABELS', 'label', 'value'] + ); + }); + + it('with RETENTION, ENCODING, CHUNK_SIZE, ON_DUPLICATE, LABELS', () => { + assert.deepEqual( + transformArguments('key', '*', 1, { + RETENTION: 1, + ENCODING: TimeSeriesEncoding.UNCOMPRESSED, + CHUNK_SIZE: 1, + ON_DUPLICATE: TimeSeriesDuplicatePolicies.BLOCK, + LABELS: { label: 'value' } + }), + ['TS.ADD', 'key', '*', '1', 'RETENTION', '1', 'ENCODING', 'UNCOMPRESSED', 'CHUNK_SIZE', '1', 'ON_DUPLICATE', 'BLOCK', 'LABELS', 'label', 'value'] + ); + }); + }); + + testUtils.testWithClient('client.ts.add', async client => { + assert.equal( + await client.ts.add('key', 0, 1), + 0 + ); + }, GLOBAL.SERVERS.OPEN); +}); diff --git a/packages/time-series/lib/commands/ADD.ts b/packages/time-series/lib/commands/ADD.ts new file mode 100644 index 00000000000..1988a964513 --- /dev/null +++ b/packages/time-series/lib/commands/ADD.ts @@ -0,0 +1,46 @@ +import { + transformTimestampArgument, + pushRetentionArgument, + TimeSeriesEncoding, + pushEncodingArgument, + pushChunkSizeArgument, + TimeSeriesDuplicatePolicies, + Labels, + pushLabelsArgument, + Timestamp, +} from '.'; + +interface AddOptions { + RETENTION?: number; + ENCODING?: TimeSeriesEncoding; + CHUNK_SIZE?: number; + ON_DUPLICATE?: TimeSeriesDuplicatePolicies; + LABELS?: Labels; +} + +export const FIRST_KEY_INDEX = 1; + +export function transformArguments(key: string, timestamp: Timestamp, value: number, options?: AddOptions): Array { + const args = [ + 'TS.ADD', + key, + transformTimestampArgument(timestamp), + value.toString() + ]; + + pushRetentionArgument(args, options?.RETENTION); + + pushEncodingArgument(args, options?.ENCODING); + + pushChunkSizeArgument(args, options?.CHUNK_SIZE); + + if (options?.ON_DUPLICATE) { + args.push('ON_DUPLICATE', options.ON_DUPLICATE); + } + + pushLabelsArgument(args, options?.LABELS); + + return args; +} + +export declare function transformReply(): number; diff --git a/packages/time-series/lib/commands/ALTER.spec.ts b/packages/time-series/lib/commands/ALTER.spec.ts new file mode 100644 index 00000000000..868d4a9c64d --- /dev/null +++ b/packages/time-series/lib/commands/ALTER.spec.ts @@ -0,0 +1,51 @@ +import { strict as assert } from 'assert'; +import testUtils, { GLOBAL } from '../test-utils'; +import { transformArguments } from './ALTER'; + +describe('ALTER', () => { + describe('transformArguments', () => { + it('without options', () => { + assert.deepEqual( + transformArguments('key'), + ['TS.ALTER', 'key'] + ); + }); + + it('with RETENTION', () => { + assert.deepEqual( + transformArguments('key', { + RETENTION: 1 + }), + ['TS.ALTER', 'key', 'RETENTION', '1'] + ); + }); + + it('with LABELS', () => { + assert.deepEqual( + transformArguments('key', { + LABELS: { label: 'value' } + }), + ['TS.ALTER', 'key', 'LABELS', 'label', 'value'] + ); + }); + + it('with RETENTION, LABELS', () => { + assert.deepEqual( + transformArguments('key', { + RETENTION: 1, + LABELS: { label: 'value' } + }), + ['TS.ALTER', 'key', 'RETENTION', '1', 'LABELS', 'label', 'value'] + ); + }); + }); + + testUtils.testWithClient('client.ts.alter', async client => { + await client.ts.create('key'); + + assert.equal( + await client.ts.alter('key', { RETENTION: 1 }), + 'OK' + ); + }, GLOBAL.SERVERS.OPEN); +}); diff --git a/packages/time-series/lib/commands/ALTER.ts b/packages/time-series/lib/commands/ALTER.ts new file mode 100644 index 00000000000..c2c6b350522 --- /dev/null +++ b/packages/time-series/lib/commands/ALTER.ts @@ -0,0 +1,20 @@ +import { pushRetentionArgument, Labels, pushLabelsArgument } from '.'; + +export const FIRST_KEY_INDEX = 1; + +interface AlterOptions { + RETENTION?: number; + LABELS?: Labels; +} + +export function transformArguments(key: string, options?: AlterOptions): Array { + const args = ['TS.ALTER', key]; + + pushRetentionArgument(args, options?.RETENTION); + + pushLabelsArgument(args, options?.LABELS); + + return args; +} + +export declare function transformReply(): 'OK'; diff --git a/packages/time-series/lib/commands/CREATE.spec.ts b/packages/time-series/lib/commands/CREATE.spec.ts new file mode 100644 index 00000000000..fe1da99b096 --- /dev/null +++ b/packages/time-series/lib/commands/CREATE.spec.ts @@ -0,0 +1,80 @@ +import { strict as assert } from 'assert'; +import { TimeSeriesDuplicatePolicies, TimeSeriesEncoding } from '.'; +import testUtils, { GLOBAL } from '../test-utils'; +import { transformArguments } from './CREATE'; + +describe('CREATE', () => { + describe('transformArguments', () => { + it('without options', () => { + assert.deepEqual( + transformArguments('key'), + ['TS.CREATE', 'key'] + ); + }); + + it('with RETENTION', () => { + assert.deepEqual( + transformArguments('key', { + RETENTION: 1 + }), + ['TS.CREATE', 'key', 'RETENTION', '1'] + ); + }); + + it('with ENCODING', () => { + assert.deepEqual( + transformArguments('key', { + ENCODING: TimeSeriesEncoding.UNCOMPRESSED + }), + ['TS.CREATE', 'key', 'ENCODING', 'UNCOMPRESSED'] + ); + }); + + it('with CHUNK_SIZE', () => { + assert.deepEqual( + transformArguments('key', { + CHUNK_SIZE: 1 + }), + ['TS.CREATE', 'key', 'CHUNK_SIZE', '1'] + ); + }); + + it('with DUPLICATE_POLICY', () => { + assert.deepEqual( + transformArguments('key', { + DUPLICATE_POLICY: TimeSeriesDuplicatePolicies.BLOCK + }), + ['TS.CREATE', 'key', 'DUPLICATE_POLICY', 'BLOCK'] + ); + }); + + it('with LABELS', () => { + assert.deepEqual( + transformArguments('key', { + LABELS: { label: 'value' } + }), + ['TS.CREATE', 'key', 'LABELS', 'label', 'value'] + ); + }); + + it('with RETENTION, ENCODING, CHUNK_SIZE, DUPLICATE_POLICY, LABELS', () => { + assert.deepEqual( + transformArguments('key', { + RETENTION: 1, + ENCODING: TimeSeriesEncoding.UNCOMPRESSED, + CHUNK_SIZE: 1, + DUPLICATE_POLICY: TimeSeriesDuplicatePolicies.BLOCK, + LABELS: { label: 'value' } + }), + ['TS.CREATE', 'key', 'RETENTION', '1', 'ENCODING', 'UNCOMPRESSED', 'CHUNK_SIZE', '1', 'DUPLICATE_POLICY', 'BLOCK', 'LABELS', 'label', 'value'] + ); + }); + }); + + testUtils.testWithClient('client.ts.create', async client => { + assert.equal( + await client.ts.create('key'), + 'OK' + ); + }, GLOBAL.SERVERS.OPEN); +}); diff --git a/packages/time-series/lib/commands/CREATE.ts b/packages/time-series/lib/commands/CREATE.ts new file mode 100644 index 00000000000..35c0b84873f --- /dev/null +++ b/packages/time-series/lib/commands/CREATE.ts @@ -0,0 +1,42 @@ +import { + pushRetentionArgument, + TimeSeriesEncoding, + pushEncodingArgument, + pushChunkSizeArgument, + TimeSeriesDuplicatePolicies, + Labels, + pushLabelsArgument +} from '.'; + +export const FIRST_KEY_INDEX = 1; + +interface CreateOptions { + RETENTION?: number; + ENCODING?: TimeSeriesEncoding; + CHUNK_SIZE?: number; + DUPLICATE_POLICY?: TimeSeriesDuplicatePolicies; + LABELS?: Labels; +} + +export function transformArguments(key: string, options?: CreateOptions): Array { + const args = ['TS.CREATE', key]; + + pushRetentionArgument(args, options?.RETENTION); + + pushEncodingArgument(args, options?.ENCODING); + + pushChunkSizeArgument(args, options?.CHUNK_SIZE); + + if (options?.DUPLICATE_POLICY) { + args.push( + 'DUPLICATE_POLICY', + options.DUPLICATE_POLICY + ); + } + + pushLabelsArgument(args, options?.LABELS); + + return args; +} + +export declare function transformReply(): 'OK'; diff --git a/packages/time-series/lib/commands/CREATERULE.spec.ts b/packages/time-series/lib/commands/CREATERULE.spec.ts new file mode 100644 index 00000000000..a46be35b373 --- /dev/null +++ b/packages/time-series/lib/commands/CREATERULE.spec.ts @@ -0,0 +1,25 @@ +import { strict as assert } from 'assert'; +import { TimeSeriesAggregationType } from '.'; +import testUtils, { GLOBAL } from '../test-utils'; +import { transformArguments } from './CREATERULE'; + +describe('CREATERULE', () => { + it('transformArguments', () => { + assert.deepEqual( + transformArguments('source', 'destination', TimeSeriesAggregationType.AVARAGE, 1), + ['TS.CREATERULE', 'source', 'destination', 'avg', 1] + ); + }); + + testUtils.testWithClient('client.ts.createRule', async client => { + await Promise.all([ + client.ts.create('source'), + client.ts.create('destination') + ]); + + assert.equal( + await client.ts.createRule('source', 'destination', TimeSeriesAggregationType.AVARAGE, 1), + 'OK' + ); + }, GLOBAL.SERVERS.OPEN); +}); diff --git a/packages/time-series/lib/commands/CREATERULE.ts b/packages/time-series/lib/commands/CREATERULE.ts new file mode 100644 index 00000000000..e8f14f880f2 --- /dev/null +++ b/packages/time-series/lib/commands/CREATERULE.ts @@ -0,0 +1,20 @@ +import { TimeSeriesAggregationType } from '.'; + +export const FIRST_KEY_INDEX = 1; + +export function transformArguments( + sourceKey: string, + destinationKey: string, + aggregationType: TimeSeriesAggregationType, + timeBucket: number +): Array { + return [ + 'TS.CREATERULE', + sourceKey, + destinationKey, + aggregationType, + timeBucket.toString() + ]; +} + +export declare function transfromReply(): 'OK'; diff --git a/packages/time-series/lib/commands/DECRBY.ts b/packages/time-series/lib/commands/DECRBY.ts new file mode 100644 index 00000000000..b7fab3702d0 --- /dev/null +++ b/packages/time-series/lib/commands/DECRBY.ts @@ -0,0 +1,10 @@ +import { RedisCommandArguments } from '@node-redis/client/dist/lib/commands'; +import { IncrDecrOptions, transformIncrDecrArguments } from '.'; + +export const FIRST_KEY_INDEX = 1; + +export function transformArguments(key: string, value: number, options?: IncrDecrOptions): RedisCommandArguments { + return transformIncrDecrArguments('TS.DECRBY', key, value, options); +} + +export declare function transformReply(): number; diff --git a/packages/time-series/lib/commands/DEL.ts b/packages/time-series/lib/commands/DEL.ts new file mode 100644 index 00000000000..ae9a1e9fef3 --- /dev/null +++ b/packages/time-series/lib/commands/DEL.ts @@ -0,0 +1,15 @@ +import { RedisCommandArguments } from '@node-redis/client/dist/lib/commands'; +import { Timestamp, transformTimestampArgument } from '.'; + +export const FIRTS_KEY_INDEX = 1; + +export function transformArguments(key: string, fromTimestamp: Timestamp, toTimestamp: Timestamp): RedisCommandArguments { + return [ + 'TS.DEL', + key, + transformTimestampArgument(fromTimestamp), + transformTimestampArgument(toTimestamp) + ]; +} + +export declare function transformReply(): number; diff --git a/packages/time-series/lib/commands/DELETERULE.ts b/packages/time-series/lib/commands/DELETERULE.ts new file mode 100644 index 00000000000..b9ef7574c86 --- /dev/null +++ b/packages/time-series/lib/commands/DELETERULE.ts @@ -0,0 +1,9 @@ +export function transformArguments(sourceKey: string,destinationKey: string,): Array { + return [ + 'TS.DELETERULE', + sourceKey, + destinationKey, + ]; +} + +export declare function transfromReply(): 'OK'; diff --git a/packages/time-series/lib/commands/GET.ts b/packages/time-series/lib/commands/GET.ts new file mode 100644 index 00000000000..ec3b1f5f803 --- /dev/null +++ b/packages/time-series/lib/commands/GET.ts @@ -0,0 +1,15 @@ +import { SampleRawReply, SampleReply, transformSampleReply } from '.'; + +export const FIRST_KEY_INDEX = 1; + +export const IS_READ_ONLY = true; + +export function transformArguments(key: string): Array { + return ['TS.GET', key]; +} + +export function transformReply(reply: [] | SampleRawReply): null | SampleReply { + if (reply.length === 0) return null; + + return transformSampleReply(reply); +} diff --git a/packages/time-series/lib/commands/INCRBY.ts b/packages/time-series/lib/commands/INCRBY.ts new file mode 100644 index 00000000000..28267c57cc7 --- /dev/null +++ b/packages/time-series/lib/commands/INCRBY.ts @@ -0,0 +1,10 @@ +import { RedisCommandArguments } from '@node-redis/client/dist/lib/commands'; +import { IncrDecrOptions, transformIncrDecrArguments } from '.'; + +export const FIRST_KEY_INDEX = 1; + +export function transformArguments(key: string, value: number, options?: IncrDecrOptions): RedisCommandArguments { + return transformIncrDecrArguments('TS.INCRBY', key, value, options); +} + +export declare function transformReply(): number; diff --git a/packages/time-series/lib/commands/INFO.ts b/packages/time-series/lib/commands/INFO.ts new file mode 100644 index 00000000000..00e04a1985a --- /dev/null +++ b/packages/time-series/lib/commands/INFO.ts @@ -0,0 +1,82 @@ +import { TimeSeriesAggregationType, TimeSeriesDuplicatePolicies } from '.'; + +export const FIRST_KEY_INDEX = 1; + +export const IS_READ_ONLY = true; + +export function transformArguments(key: string): Array { + return ['TS.INFO', key]; +} + +export type InfoRawReply = [ + _: string, + totalSamples: number, + _: string, + memoryUsage: number, + _: string, + firstTimestamp: number, + _: string, + lastTimestamp: number, + _: string, + retentionTime: number, + _: string, + chunkCount: number, + _: string, + chunkSize: number, + _: string, + chunkType: string, + _: string, + duplicatePolicy: TimeSeriesDuplicatePolicies | null, + _: string, + labels: Array<[name: string, value: string]>, + _: string, + sourceKey: string | null, + _: string, + rules: Array<[key: string, timeBucket: number, aggregationType: TimeSeriesAggregationType]> +]; + +export interface InfoReply { + totalSamples: number; + memoryUsage: number; + firstTimestamp: number; + lastTimestamp: number; + retentionTime: number; + chunkCount: number; + chunkSize: number; + chunkType: string; + duplicatePolicy: TimeSeriesDuplicatePolicies | null; + labels: Array<{ + name: string; + value: string; + }>; + sourceKey: string | null; + rules: Array<{ + key: string; + timeBucket: number; + aggregationType: TimeSeriesAggregationType + }>; +} + +export function transformReply(reply: InfoRawReply): InfoReply { + return { + totalSamples: reply[1], + memoryUsage: reply[3], + firstTimestamp: reply[5], + lastTimestamp: reply[7], + retentionTime: reply[9], + chunkCount: reply[11], + chunkSize: reply[13], + chunkType: reply[15], + duplicatePolicy: reply[17], + labels: reply[19].map(([name, value]) => ({ + name, + value + })), + sourceKey: reply[21], + rules: reply[23].map(([key, timeBucket, aggregationType]) => ({ + key, + timeBucket, + aggregationType + })) + }; +} diff --git a/packages/time-series/lib/commands/INFO_DEBUG.ts b/packages/time-series/lib/commands/INFO_DEBUG.ts new file mode 100644 index 00000000000..6680a2044bb --- /dev/null +++ b/packages/time-series/lib/commands/INFO_DEBUG.ts @@ -0,0 +1,53 @@ +import { + transformArguments as transformInfoArguments, + InfoRawReply, + InfoReply, + transformReply as transformInfoReply +} from './INFO'; + +export { IS_READ_ONLY, FIRST_KEY_INDEX } from './INFO'; + +export function transformArguments(key: string): Array { + const args = transformInfoArguments(key); + args.push('DEBUG'); + return args; +} + +type InfoDebugRawReply = [ + ...infoArgs: InfoRawReply, + _: string, + chunks: Array<[ + _: string, + startTimestamp: number, + _: string, + endTimestamp: number, + _: string, + samples: number, + _: string, + size: number, + _: string, + bytesPerSample: string + ]> +] + +interface InfoDebugReply extends InfoReply { + chunks: Array<{ + startTimestamp: number; + endTimestamp: number; + samples: number; + size: number; + bytesPerSample: string; + }>; +} + +export function transformReply(rawReply: InfoDebugRawReply): InfoDebugReply { + const reply = transformInfoReply(rawReply as unknown as InfoRawReply); + (reply as InfoDebugReply).chunks = rawReply[25].map(chunk => ({ + startTimestamp: chunk[1], + endTimestamp: chunk[3], + samples: chunk[5], + size: chunk[7], + bytesPerSample: chunk[9] + })); + return reply as InfoDebugReply; +} diff --git a/packages/time-series/lib/commands/MADD.ts b/packages/time-series/lib/commands/MADD.ts new file mode 100644 index 00000000000..8970cac06a8 --- /dev/null +++ b/packages/time-series/lib/commands/MADD.ts @@ -0,0 +1,23 @@ +import { Timestamp, transformTimestampArgument } from '.'; + +interface MAddSample { + key: string; + timestamp: Timestamp; + value: number; +} + +export function transformArguments(toAdd: Array): Array { + const args = ['TS.MADD']; + + for (const { key, timestamp, value } of toAdd) { + args.push( + key, + transformTimestampArgument(timestamp), + value.toString() + ); + } + + return args; +} + +export declare function transformReply(): Array; diff --git a/packages/time-series/lib/commands/MGET.ts b/packages/time-series/lib/commands/MGET.ts new file mode 100644 index 00000000000..94fc45e03c4 --- /dev/null +++ b/packages/time-series/lib/commands/MGET.ts @@ -0,0 +1,31 @@ +import { pushVerdictArgument, pushVerdictArguments } from '@node-redis/client/lib/commands/generic-transformers'; + +export const IS_READ_ONLY = true; + +interface WithLabels { + WITHLABELS: true; +} + +interface SelectedLabels { + SELECTED_LABELS: string | Array; +} + +type MGetOptions = WithLabels & SelectedLabels; + +export function transformArguments(filter: string, options?: MGetOptions): Array { + const args = ['TS.MGET']; + + if (options?.WITHLABELS) { + args.push('WITHLABELS'); + } else if (options?.SELECTED_LABELS) { + pushVerdictArguments(args, options.SELECTED_LABELS); + } + + args.push('FILTER', filter); + + return args; +} + +export function transformReply() { + +} diff --git a/packages/time-series/lib/commands/QUERYINDEX.ts b/packages/time-series/lib/commands/QUERYINDEX.ts new file mode 100644 index 00000000000..c3970675ea7 --- /dev/null +++ b/packages/time-series/lib/commands/QUERYINDEX.ts @@ -0,0 +1,7 @@ +export const IS_READ_ONLY = true; + +export function transformArguments(query: string): Array { + return ['TS.QUERYINDEX', query]; +} + +export declare function transformReply(): Array; diff --git a/packages/time-series/lib/commands/RANGE.ts b/packages/time-series/lib/commands/RANGE.ts new file mode 100644 index 00000000000..b22d2e33b21 --- /dev/null +++ b/packages/time-series/lib/commands/RANGE.ts @@ -0,0 +1,21 @@ +import { RedisCommandArguments } from '@node-redis/client/dist/lib/commands'; +import { RangeOptions, Timestamp, pushRangeArguments } from '.'; + +export const FIRST_KEY_INDEX = 1; + +export const IS_READ_ONLY = true; + +export function transformArguments( + fromTimestamp: Timestamp, + toTimestamp: Timestamp, + options?: RangeOptions +): RedisCommandArguments { + return pushRangeArguments( + ['TS.RANGE'], + fromTimestamp, + toTimestamp, + options + ); +} + +export { transformRangeReply } from '.'; diff --git a/packages/time-series/lib/commands/REVRANGE.ts b/packages/time-series/lib/commands/REVRANGE.ts new file mode 100644 index 00000000000..ba961265ac6 --- /dev/null +++ b/packages/time-series/lib/commands/REVRANGE.ts @@ -0,0 +1,21 @@ +import { RedisCommandArguments } from '@node-redis/client/dist/lib/commands'; +import { RangeOptions, Timestamp, pushRangeArguments } from '.'; + +export const FIRST_KEY_INDEX = 1; + +export const IS_READ_ONLY = true; + +export function transformArguments( + fromTimestamp: Timestamp, + toTimestamp: Timestamp, + options?: RangeOptions +): RedisCommandArguments { + return pushRangeArguments( + ['TS.REVRANGE'], + fromTimestamp, + toTimestamp, + options + ); +} + +export { transformRangeReply } from '.'; diff --git a/packages/time-series/lib/commands/index.spec.ts b/packages/time-series/lib/commands/index.spec.ts new file mode 100644 index 00000000000..d02d259eb76 --- /dev/null +++ b/packages/time-series/lib/commands/index.spec.ts @@ -0,0 +1,231 @@ +import { RedisCommandArguments } from '@node-redis/client/dist/lib/commands'; +import { strict as assert } from 'assert'; +import { + transformTimestampArgument, + pushRetentionArgument, + TimeSeriesEncoding, + pushEncodingArgument, + pushChunkSizeArgument, + pushLabelsArgument, + transformIncrDecrArguments, + transformSampleReply, + pushRangeArguments, + transformRangeReply, + TimeSeriesAggregationType +} from '.'; + +describe('transformTimestampArgument', () => { + it('number', () => { + assert.equal( + transformTimestampArgument(0), + '0' + ); + }); + + it('Date', () => { + assert.equal( + transformTimestampArgument(new Date(0)), + '0' + ); + }); + + it('string', () => { + assert.equal( + transformTimestampArgument('*'), + '*' + ); + }); +}); + +function testOptionalArgument(fn: (args: RedisCommandArguments) => unknown): void { + it('undefined', () => { + assert.deepEqual( + fn([]), + [] + ); + }); +} + +describe('pushRetentionArgument', () => { + testOptionalArgument(pushRetentionArgument); + + it('number', () => { + assert.deepEqual( + pushRetentionArgument([], 1), + ['RETENTION', '1'] + ); + }); +}); + +describe('pushEncodingArgument', () => { + testOptionalArgument(pushEncodingArgument); + + it('UNCOMPRESSED', () => { + assert.deepEqual( + pushEncodingArgument([], TimeSeriesEncoding.UNCOMPRESSED), + ['ENCODING', 'UNCOMPRESSED'] + ); + }); +}); + +describe('pushChunkSizeArgument', () => { + testOptionalArgument(pushChunkSizeArgument); + + it('number', () => { + assert.deepEqual( + pushChunkSizeArgument([], 1), + ['CHUNK_SIZE', '1'] + ); + }); +}); + +describe('pushLabelsArgument', () => { + testOptionalArgument(pushLabelsArgument); + + it("{ label: 'value' }", () => { + assert.deepEqual( + pushLabelsArgument([], { label: 'value' }), + ['LABELS', 'label', 'value'] + ); + }); +}); + +describe('transformIncrDecrArguments', () => { + it('without options', () => { + assert.deepEqual( + transformIncrDecrArguments('TS.INCRBY', 'key', 1), + ['TS.INCRBY', 'key', '1'] + ); + }); + + it('with TIMESTAMP', () => { + assert.deepEqual( + transformIncrDecrArguments('TS.INCRBY', 'key', 1, { + TIMESTAMP: '*' + }), + ['TS.INCRBY', 'key', '1', 'TIMESTAMP', '*'] + ); + }); + + it('with UNCOMPRESSED', () => { + assert.deepEqual( + transformIncrDecrArguments('TS.INCRBY', 'key', 1, { + UNCOMPRESSED: true + }), + ['TS.INCRBY', 'key', '1', 'UNCOMPRESSED'] + ); + }); +}); + +it('transformSampleReply', () => { + assert.deepEqual( + transformSampleReply([1, '1.1']), + { + timestamp: 1, + value: 1.1 + } + ); +}); + +describe('pushRangeArguments', () => { + it('without options', () => { + assert.deepEqual( + pushRangeArguments([], '-', '+'), + ['-', '+'] + ); + }); + + describe('with FILTER_BY_TS', () => { + it('string', () => { + assert.deepEqual( + pushRangeArguments([], '-', '+', { + FILTER_BY_TS: 'ts' + }), + ['-', '+', 'FILTER_BY_TS', 'ts'] + ); + }); + + it('Array', () => { + assert.deepEqual( + pushRangeArguments([], '-', '+', { + FILTER_BY_TS: ['1', '2'] + }), + ['-', '+', 'FILTER_BY_TS', '1', '2'] + ); + }); + }); + + it('with FILTER_BY_VALUE', () => { + assert.deepEqual( + pushRangeArguments([], '-', '+', { + FILTER_BY_VALUE: { + min: 1, + max: 2 + } + }), + ['-', '+', 'FILTER_BY_VALUE', '1', '2'] + ); + }); + + it('with COUNT', () => { + assert.deepEqual( + pushRangeArguments([], '-', '+', { + COUNT: 1 + }), + ['-', '+', 'COUNT', '1'] + ); + }); + + it('with ALIGN', () => { + assert.deepEqual( + pushRangeArguments([], '-', '+', { + ALIGN: 1 + }), + ['-', '+', 'ALIGN', '1'] + ); + }); + + it('with AGGREGATION', () => { + assert.deepEqual( + pushRangeArguments([], '-', '+', { + AGGREGATION: { + type: TimeSeriesAggregationType.FIRST, + timeBucket: 1 + } + }), + ['-', '+', 'AGGREGATION', 'first', '1'] + ); + }); + + it('with FILTER_BY_TS, FILTER_BY_VALUE, COUNT, ALIGN, AGGREGATION', () => { + assert.deepEqual( + pushRangeArguments([], '-', '+', { + FILTER_BY_TS: 'ts', + FILTER_BY_VALUE: { + min: 1, + max: 2 + }, + COUNT: 1, + ALIGN: 1, + AGGREGATION: { + type: TimeSeriesAggregationType.FIRST, + timeBucket: 1 + } + }), + ['-', '+', 'FILTER_BY_TS', 'ts', 'FILTER_BY_VALUE', '1', '2', 'COUNT', '1', 'ALIGN', '1', 'AGGREGATION', 'first', '1'] + ); + }); +}); + +it('transformRangeReply', () => { + assert.deepEqual( + transformRangeReply([[1, '1.1'], [2, '2.2']]), + [{ + timestamp: 1, + value: 1.1 + }, { + timestamp: 2, + value: 2.2 + }] + ); +}); diff --git a/packages/time-series/lib/commands/index.ts b/packages/time-series/lib/commands/index.ts new file mode 100644 index 00000000000..07034c61372 --- /dev/null +++ b/packages/time-series/lib/commands/index.ts @@ -0,0 +1,261 @@ +import * as ADD from './ADD'; +import * as ALTER from './ALTER'; +import * as CREATE from './CREATE'; +import * as CREATERULE from './CREATERULE'; +import * as DECRBY from './DECRBY'; +import * as DEL from './DEL'; +import * as DELETERULE from './DELETERULE'; +import * as GET from './GET'; +import * as INCRBY from './INCRBY'; +import * as INFO_DEBUG from './INFO_DEBUG'; +import * as INFO from './INFO'; +import * as MADD from './MADD'; +import * as MGET from './MGET'; +import * as QUERYINDEX from './QUERYINDEX'; +import * as RANGE from './RANGE'; +import * as REVRANGE from './REVRANGE'; +import { RedisCommandArguments } from '@node-redis/client/dist/lib/commands'; +import { pushVerdictArguments } from '@node-redis/client/lib/commands/generic-transformers'; + +export default { + ADD, + add: ADD, + ALTER, + alter: ALTER, + CREATE, + create: CREATE, + CREATERULE, + createRule: CREATERULE, + DECRBY, + decrBy: DECRBY, + DEL, + del: DEL, + DELETERULE, + deleteRule: DELETERULE, + GET, + get: GET, + INCRBY, + incrBy: INCRBY, + INFO_DEBUG, + infoDebug: INFO_DEBUG, + INFO, + info: INFO, + MADD, + mAdd: MADD, + MGET, + mGet: MGET, + QUERYINDEX, + queryIndex: QUERYINDEX, + RANGE, + range: RANGE, + REVRANGE, + revRange: REVRANGE +}; + +export enum TimeSeriesAggregationType { + AVARAGE = 'avg', + SUM = 'sum', + MINIMUM = 'min', + MAXIMUM = 'max', + RANGE = 'range', + COUNT = 'count', + FIRST = 'first', + LAST = 'last', + STD_P = 'std.p', + STD_S = 'std.s', + VAR_P = 'var.p', + VAR_S = 'var.s' +} + +export type Timestamp = number | Date | string; + +export function transformTimestampArgument(timestamp: Timestamp): string { + if (typeof timestamp === 'string') return timestamp; + + return ( + typeof timestamp === 'number' ? + timestamp : + timestamp.getTime() + ).toString(); +} + +export function pushRetentionArgument(args: RedisCommandArguments, retention?: number): RedisCommandArguments { + if (retention) { + args.push( + 'RETENTION', + retention.toString() + ); + } + + return args; +} + +export enum TimeSeriesEncoding { + COMPRESSED = 'COMPRESSED', + UNCOMPRESSED = 'UNCOMPRESSED' +} + +export function pushEncodingArgument(args: RedisCommandArguments, encoding?: TimeSeriesEncoding): RedisCommandArguments { + if (encoding) { + args.push( + 'ENCODING', + encoding + ); + } + + return args; +} + +export function pushChunkSizeArgument(args: RedisCommandArguments, chunkSize?: number): RedisCommandArguments { + if (chunkSize) { + args.push( + 'CHUNK_SIZE', + chunkSize.toString() + ); + } + + return args; +} + +export enum TimeSeriesDuplicatePolicies { + BLOCK = 'BLOCK', + FIRST = 'FIRST', + LAST = 'LAST', + MIN = 'MIN', + MAX = 'MAX', + SUM = 'SUM' +} + +export type Labels = { + [label: string]: string; +}; + +export function pushLabelsArgument(args: RedisCommandArguments, labels?: Labels): RedisCommandArguments { + if (labels) { + args.push('LABELS'); + + for (const [label, value] of Object.entries(labels)) { + args.push(label, value); + } + } + + return args; +} + +export interface IncrDecrOptions { + TIMESTAMP?: Timestamp; + RETENTION?: number; + UNCOMPRESSED?: boolean; + CHUNK_SIZE?: number; + LABELS?: Labels; +} + +export function transformIncrDecrArguments( + command: 'TS.INCRBY' | 'TS.DECRBY', + key: string, + value: number, + options?: IncrDecrOptions +): RedisCommandArguments { + const args = [ + command, + key, + value.toString() + ]; + + if (options?.TIMESTAMP) { + args.push('TIMESTAMP', transformTimestampArgument(options.TIMESTAMP)); + } + + pushRetentionArgument(args, options?.RETENTION); + + if (options?.UNCOMPRESSED) { + args.push('UNCOMPRESSED'); + } + + pushChunkSizeArgument(args, options?.CHUNK_SIZE); + + pushLabelsArgument(args, options?.LABELS); + + return args; +} + +export type SampleRawReply = [timestamp: number, value: string]; + +export interface SampleReply { + timestamp: number; + value: number; +} + +export function transformSampleReply(reply: SampleRawReply): SampleReply { + return { + timestamp: reply[0], + value: Number(reply[1]) + }; +} + +export interface RangeOptions { + FILTER_BY_TS?: string | Array; + FILTER_BY_VALUE?: { + min: number; + max: number; + }; + COUNT?: number; + ALIGN?: Timestamp; + AGGREGATION?: { + type: TimeSeriesAggregationType; + timeBucket: Timestamp; + }; +} + +export function pushRangeArguments( + args: RedisCommandArguments, + fromTimestamp: Timestamp, + toTimestamp: Timestamp, + options?: RangeOptions +): RedisCommandArguments { + args.push( + transformTimestampArgument(fromTimestamp), + transformTimestampArgument(toTimestamp) + ); + + if (options?.FILTER_BY_TS) { + args.push('FILTER_BY_TS'); + pushVerdictArguments(args, options.FILTER_BY_TS); + } + + if (options?.FILTER_BY_VALUE) { + args.push( + 'FILTER_BY_VALUE', + options.FILTER_BY_VALUE.min.toString(), + options.FILTER_BY_VALUE.max.toString() + ); + } + + if (options?.COUNT) { + args.push( + 'COUNT', + options.COUNT.toString() + ); + } + + if (options?.ALIGN) { + args.push( + 'ALIGN', + transformTimestampArgument(options.ALIGN) + ); + } + + if (options?.AGGREGATION) { + args.push( + 'AGGREGATION', + options.AGGREGATION.type, + transformTimestampArgument(options.AGGREGATION.timeBucket) + ); + } + + return args; +} + +export function transformRangeReply(reply: Array): Array { + return reply.map(transformSampleReply); +} diff --git a/packages/time-series/lib/index.ts b/packages/time-series/lib/index.ts new file mode 100644 index 00000000000..567795c83c6 --- /dev/null +++ b/packages/time-series/lib/index.ts @@ -0,0 +1,3 @@ +export { default } from './commands'; + +// TODO diff --git a/packages/time-series/lib/test-utils.ts b/packages/time-series/lib/test-utils.ts new file mode 100644 index 00000000000..7beb04e2987 --- /dev/null +++ b/packages/time-series/lib/test-utils.ts @@ -0,0 +1,21 @@ +import TestUtils from '@node-redis/test-utils'; +import TimeSeries from '.'; + +export default new TestUtils({ + dockerImageName: 'redislabs/redistimeseries', + dockerImageVersionArgument: 'timeseries-version', + defaultDockerVersion: '1.6.0' +}); + +export const GLOBAL = { + SERVERS: { + OPEN: { + serverArguments: ['--loadmodule /usr/lib/redis/modules/redistimeseries.so'], + clientOptions: { + modules: { + ts: TimeSeries + } + } + } + } +}; diff --git a/packages/time-series/package.json b/packages/time-series/package.json new file mode 100644 index 00000000000..e58fd79da59 --- /dev/null +++ b/packages/time-series/package.json @@ -0,0 +1,24 @@ +{ + "name": "@node-redis/time-series", + "version": "1.0.0-rc.0", + "license": "MIT", + "main": "./dist/index.js", + "types": "./dist/index.d.ts", + "scripts": { + "test": "nyc -r text-summary -r lcov mocha -r source-map-support/register -r ts-node/register './lib/**/*.spec.ts'", + "build": "tsc" + }, + "peerDependencies": { + "@node-redis/client": "^1.0.0" + }, + "devDependencies": { + "@istanbuljs/nyc-config-typescript": "^1.0.1", + "@node-redis/test-utils": "*", + "@types/node": "^16.11.7", + "nyc": "^15.1.0", + "release-it": "^14.11.7", + "source-map-support": "^0.5.20", + "ts-node": "^10.4.0", + "typescript": "^4.4.4" + } +} diff --git a/packages/time-series/tsconfig.json b/packages/time-series/tsconfig.json new file mode 100644 index 00000000000..14fda1d8711 --- /dev/null +++ b/packages/time-series/tsconfig.json @@ -0,0 +1,9 @@ +{ + "extends": "../../tsconfig.base.json", + "compilerOptions": { + "outDir": "./dist" + }, + "include": [ + "./lib/**/*.ts" + ] +} From 9257e2343b3f3ba56e531ebe8f75639694af01ad Mon Sep 17 00:00:00 2001 From: AnnAngela Date: Tue, 30 Nov 2021 00:03:49 +0800 Subject: [PATCH 1011/1748] Update v3-to-v4.md (#1737) * Update v3-to-v4.md * Update v3-to-v4.md * Update v3-to-v4.md * Update docs/v3-to-v4.md Co-authored-by: Simon Prickett * Update docs/v3-to-v4.md Co-authored-by: Simon Prickett * Update docs/v3-to-v4.md Co-authored-by: Simon Prickett * Update docs/v3-to-v4.md Co-authored-by: Simon Prickett * Update docs/v3-to-v4.md Co-authored-by: Simon Prickett * Update docs/v3-to-v4.md Co-authored-by: Simon Prickett Co-authored-by: Leibale Eidelman Co-authored-by: Simon Prickett --- docs/v3-to-v4.md | 48 ++++++++++++++++++++++++++++++++++++++++++------ 1 file changed, 42 insertions(+), 6 deletions(-) diff --git a/docs/v3-to-v4.md b/docs/v3-to-v4.md index 90267d8245c..3ef43444c8e 100644 --- a/docs/v3-to-v4.md +++ b/docs/v3-to-v4.md @@ -2,14 +2,54 @@ Version 4 of Node Redis is a major refactor. While we have tried to maintain backwards compatibility where possible, several interfaces have changed. Read this guide to understand the differences and how to implement version 4 in your application. -## Breaking Changes +## All of the Breaking Changes See the [Change Log](../packages/client/CHANGELOG.md). -## Promises +### Promises Node Redis now uses native [Promises](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Promise) by default for all functions. +### `createClient` + +The configuration object passed to `createClient` has changed significantly with this release. See the [client configuration guide](./client-configuration.md) for details. + +### No Auto Connect + +In V4, the client does not automatically connect to the server, you need to run `.connect()` before any command, or you will receive error `ClientClosedError: The client is closed`. + +```typescript +import { createClient } from 'redis'; + +const client = createClient(); + +await client.connect(); + +await client.ping(); +``` + +### No `message` event + +In V4, you don't need to add listener to the `message` and `message_buffer` events, you can get the message directly in `subscribe`-like commands. + +The second argument of these commands is a callback, which will be triggered every time there is a message published to the channel. + +The third argument to these commands is a boolean to set `bufferMode` (default `false`). If it's set to `true` you will receive a buffer instead of a string. + +The `subscribe`-like commands return a promise. If the server returns `ok` the promise will be fulfilled, otherwise the promise will be rejected. + +```typescript +import { createClient } from 'redis'; + +const subscriber = createClient(); + +await subscriber.connect(); + +await subscriber.subscribe('channel_name', (message, channelName) => { + console.info(message, channelName); +}); +``` + ## Legacy Mode Use legacy mode to preserve the backwards compatibility of commands while still getting access to the updated experience: @@ -29,7 +69,3 @@ await client.v4.set('key', 'value', { NX: true }); ``` - -## `createClient` - -The configuration object passed to `createClient` has changed significantly with this release. See the [client configuration guide](./client-configuration.md) for details. From aec90cac2cb0588e570af43f1996063ee9953285 Mon Sep 17 00:00:00 2001 From: leibale Date: Mon, 29 Nov 2021 11:50:53 -0500 Subject: [PATCH 1012/1748] fix #1744 - add npm minimum version --- package.json | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) diff --git a/package.json b/package.json index 7eee7f3958b..62165ec48a5 100644 --- a/package.json +++ b/package.json @@ -33,5 +33,8 @@ "bugs": { "url": "https://github.com/redis/node-redis/issues" }, - "homepage": "https://github.com/redis/node-redis" + "homepage": "https://github.com/redis/node-redis", + "engines": { + "npm": ">=7" + } } From 7cce81fb1e38d13c2fd75ca478831afa5e6c43c1 Mon Sep 17 00:00:00 2001 From: Damien Arrachequesne Date: Mon, 29 Nov 2021 18:13:43 +0100 Subject: [PATCH 1013/1748] fix #1743 - restrict the list of published files (#1742) --- package.json | 3 +++ 1 file changed, 3 insertions(+) diff --git a/package.json b/package.json index 62165ec48a5..4b3b562f6a2 100644 --- a/package.json +++ b/package.json @@ -34,6 +34,9 @@ "url": "https://github.com/redis/node-redis/issues" }, "homepage": "https://github.com/redis/node-redis", + "files": [ + "dist/" + ], "engines": { "npm": ">=7" } From a743ca67371084deb65403db9ba3db42336d9f3f Mon Sep 17 00:00:00 2001 From: leibale Date: Mon, 29 Nov 2021 12:47:17 -0500 Subject: [PATCH 1014/1748] fix TS.CREATERULE --- packages/time-series/lib/commands/CREATERULE.spec.ts | 2 +- packages/time-series/lib/commands/CREATERULE.ts | 1 + 2 files changed, 2 insertions(+), 1 deletion(-) diff --git a/packages/time-series/lib/commands/CREATERULE.spec.ts b/packages/time-series/lib/commands/CREATERULE.spec.ts index a46be35b373..eb338bb8046 100644 --- a/packages/time-series/lib/commands/CREATERULE.spec.ts +++ b/packages/time-series/lib/commands/CREATERULE.spec.ts @@ -7,7 +7,7 @@ describe('CREATERULE', () => { it('transformArguments', () => { assert.deepEqual( transformArguments('source', 'destination', TimeSeriesAggregationType.AVARAGE, 1), - ['TS.CREATERULE', 'source', 'destination', 'avg', 1] + ['TS.CREATERULE', 'source', 'destination', 'AGGREGATION', 'avg', '1'] ); }); diff --git a/packages/time-series/lib/commands/CREATERULE.ts b/packages/time-series/lib/commands/CREATERULE.ts index e8f14f880f2..8d39722c9e2 100644 --- a/packages/time-series/lib/commands/CREATERULE.ts +++ b/packages/time-series/lib/commands/CREATERULE.ts @@ -12,6 +12,7 @@ export function transformArguments( 'TS.CREATERULE', sourceKey, destinationKey, + 'AGGREGATION', aggregationType, timeBucket.toString() ]; From 8f94d37466276a31d196c68268f7f408c4c4e594 Mon Sep 17 00:00:00 2001 From: kjlis Date: Mon, 29 Nov 2021 18:54:54 +0100 Subject: [PATCH 1015/1748] fix #1673 - Export RedisClientType and RedisClusterType types (#1704) --- index.ts | 4 +- package-lock.json | 127 +++++++++++++-------------------------- packages/client/index.ts | 4 ++ 3 files changed, 47 insertions(+), 88 deletions(-) diff --git a/index.ts b/index.ts index 8fb31edb08a..cca5764884d 100644 --- a/index.ts +++ b/index.ts @@ -1,7 +1,5 @@ -import { createClient as _createClient, createCluster as _createCluster } from '@node-redis/client'; +import { createClient as _createClient, createCluster as _createCluster, RedisClientOptions, RedisClientType, RedisClusterOptions, RedisClusterType } from '@node-redis/client'; import { RedisScripts } from '@node-redis/client/dist/lib/commands'; -import { RedisClientOptions, RedisClientType } from '@node-redis/client/dist/lib/client'; -import { RedisClusterOptions, RedisClusterType } from '@node-redis/client/dist/lib/cluster'; import RedisJSON from '@node-redis/json'; import RediSearch from '@node-redis/search'; diff --git a/package-lock.json b/package-lock.json index 69d569e7337..493d3736966 100644 --- a/package-lock.json +++ b/package-lock.json @@ -669,10 +669,6 @@ "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", @@ -1634,9 +1630,9 @@ } }, "node_modules/caniuse-lite": { - "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==", + "version": "1.0.30001283", + "resolved": "https://registry.npmjs.org/caniuse-lite/-/caniuse-lite-1.0.30001283.tgz", + "integrity": "sha512-9RoKo841j1GQFSJz/nCXOj0sD7tHBtlowjYlrqIUS812x9/emfBLBt6IyMz1zIaYc/eRL8Cs6HPUVi2Hzq4sIg==", "dev": true, "funding": { "type": "opencollective", @@ -2089,9 +2085,9 @@ "dev": true }, "node_modules/electron-to-chromium": { - "version": "1.4.0", - "resolved": "https://registry.npmjs.org/electron-to-chromium/-/electron-to-chromium-1.4.0.tgz", - "integrity": "sha512-+oXCt6SaIu8EmFTPx8wNGSB0tHQ5biDscnlf6Uxuz17e9CjzMRtGk9B8705aMPnj0iWr3iC74WuIkngCsLElmA==", + "version": "1.4.1", + "resolved": "https://registry.npmjs.org/electron-to-chromium/-/electron-to-chromium-1.4.1.tgz", + "integrity": "sha512-9ldvb6QMHiDpUNF1iSwBTiTT0qXEN+xIO5WlCJrC5gt0z74ofOiqR698vaJqYWnri0XZiF0YmnrFmGq/EmpGAA==", "dev": true }, "node_modules/emoji-regex": { @@ -4658,6 +4654,15 @@ "url": "https://github.com/sponsors/sindresorhus" } }, + "node_modules/package-json/node_modules/cacheable-request/node_modules/lowercase-keys": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/lowercase-keys/-/lowercase-keys-2.0.0.tgz", + "integrity": "sha512-tqNXrS78oMOE73NMxK4EMLQsQowWf8jKooH9g7xPavRT706R6bkQJ6DY2Te7QukaZsulxa30wQ7bk0pm4XiHmA==", + "dev": true, + "engines": { + "node": ">=8" + } + }, "node_modules/package-json/node_modules/decompress-response": { "version": "3.3.0", "resolved": "https://registry.npmjs.org/decompress-response/-/decompress-response-3.3.0.tgz", @@ -4710,15 +4715,6 @@ "node": ">=8.6" } }, - "node_modules/package-json/node_modules/got/node_modules/lowercase-keys": { - "version": "1.0.1", - "resolved": "https://registry.npmjs.org/lowercase-keys/-/lowercase-keys-1.0.1.tgz", - "integrity": "sha512-G2Lj61tXDnVFFOi8VZds+SoQjtQC3dgokKdDG2mTm1tx4m50NUHBOZSBwQQHyy0V12A0JTG4icfZQH+xPyh8VA==", - "dev": true, - "engines": { - "node": ">=0.10.0" - } - }, "node_modules/package-json/node_modules/json-buffer": { "version": "3.0.0", "resolved": "https://registry.npmjs.org/json-buffer/-/json-buffer-3.0.0.tgz", @@ -4734,6 +4730,15 @@ "json-buffer": "3.0.0" } }, + "node_modules/package-json/node_modules/lowercase-keys": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/lowercase-keys/-/lowercase-keys-1.0.1.tgz", + "integrity": "sha512-G2Lj61tXDnVFFOi8VZds+SoQjtQC3dgokKdDG2mTm1tx4m50NUHBOZSBwQQHyy0V12A0JTG4icfZQH+xPyh8VA==", + "dev": true, + "engines": { + "node": ">=0.10.0" + } + }, "node_modules/package-json/node_modules/normalize-url": { "version": "4.5.1", "resolved": "https://registry.npmjs.org/normalize-url/-/normalize-url-4.5.1.tgz", @@ -4761,15 +4766,6 @@ "lowercase-keys": "^1.0.0" } }, - "node_modules/package-json/node_modules/responselike/node_modules/lowercase-keys": { - "version": "1.0.1", - "resolved": "https://registry.npmjs.org/lowercase-keys/-/lowercase-keys-1.0.1.tgz", - "integrity": "sha512-G2Lj61tXDnVFFOi8VZds+SoQjtQC3dgokKdDG2mTm1tx4m50NUHBOZSBwQQHyy0V12A0JTG4icfZQH+xPyh8VA==", - "dev": true, - "engines": { - "node": ">=0.10.0" - } - }, "node_modules/package-json/node_modules/semver": { "version": "6.3.0", "resolved": "https://registry.npmjs.org/semver/-/semver-6.3.0.tgz", @@ -6415,7 +6411,6 @@ } }, "packages/client": { - "name": "@node-redis/client", "version": "1.0.0", "license": "MIT", "dependencies": { @@ -6449,7 +6444,6 @@ } }, "packages/json": { - "name": "@node-redis/json", "version": "1.0.0-rc.0", "license": "MIT", "devDependencies": { @@ -6467,7 +6461,6 @@ } }, "packages/search": { - "name": "@node-redis/search", "version": "1.0.0", "license": "MIT", "devDependencies": { @@ -6485,7 +6478,6 @@ } }, "packages/test-utils": { - "name": "@node-redis/test-utils", "devDependencies": { "@istanbuljs/nyc-config-typescript": "^1.0.1", "@types/mocha": "^9.0.0", @@ -6502,24 +6494,6 @@ "peerDependencies": { "@node-redis/client": "^1.0.0" } - }, - "packages/time-series": { - "name": "@node-redis/time-series", - "version": "1.0.0-rc.0", - "license": "MIT", - "devDependencies": { - "@istanbuljs/nyc-config-typescript": "^1.0.1", - "@node-redis/test-utils": "*", - "@types/node": "^16.11.7", - "nyc": "^15.1.0", - "release-it": "^14.11.7", - "source-map-support": "^0.5.20", - "ts-node": "^10.4.0", - "typescript": "^4.4.4" - }, - "peerDependencies": { - "@node-redis/client": "^1.0.0" - } } }, "dependencies": { @@ -7076,19 +7050,6 @@ "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": "*", - "@types/node": "^16.11.7", - "nyc": "^15.1.0", - "release-it": "^14.11.7", - "source-map-support": "^0.5.20", - "ts-node": "^10.4.0", - "typescript": "^4.4.4" - } - }, "@nodelib/fs.scandir": { "version": "2.1.5", "resolved": "https://registry.npmjs.org/@nodelib/fs.scandir/-/fs.scandir-2.1.5.tgz", @@ -7826,9 +7787,9 @@ "dev": true }, "caniuse-lite": { - "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==", + "version": "1.0.30001283", + "resolved": "https://registry.npmjs.org/caniuse-lite/-/caniuse-lite-1.0.30001283.tgz", + "integrity": "sha512-9RoKo841j1GQFSJz/nCXOj0sD7tHBtlowjYlrqIUS812x9/emfBLBt6IyMz1zIaYc/eRL8Cs6HPUVi2Hzq4sIg==", "dev": true }, "chalk": { @@ -8172,9 +8133,9 @@ "dev": true }, "electron-to-chromium": { - "version": "1.4.0", - "resolved": "https://registry.npmjs.org/electron-to-chromium/-/electron-to-chromium-1.4.0.tgz", - "integrity": "sha512-+oXCt6SaIu8EmFTPx8wNGSB0tHQ5biDscnlf6Uxuz17e9CjzMRtGk9B8705aMPnj0iWr3iC74WuIkngCsLElmA==", + "version": "1.4.1", + "resolved": "https://registry.npmjs.org/electron-to-chromium/-/electron-to-chromium-1.4.1.tgz", + "integrity": "sha512-9ldvb6QMHiDpUNF1iSwBTiTT0qXEN+xIO5WlCJrC5gt0z74ofOiqR698vaJqYWnri0XZiF0YmnrFmGq/EmpGAA==", "dev": true }, "emoji-regex": { @@ -10080,6 +10041,12 @@ "requires": { "pump": "^3.0.0" } + }, + "lowercase-keys": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/lowercase-keys/-/lowercase-keys-2.0.0.tgz", + "integrity": "sha512-tqNXrS78oMOE73NMxK4EMLQsQowWf8jKooH9g7xPavRT706R6bkQJ6DY2Te7QukaZsulxa30wQ7bk0pm4XiHmA==", + "dev": true } } }, @@ -10124,14 +10091,6 @@ "p-cancelable": "^1.0.0", "to-readable-stream": "^1.0.0", "url-parse-lax": "^3.0.0" - }, - "dependencies": { - "lowercase-keys": { - "version": "1.0.1", - "resolved": "https://registry.npmjs.org/lowercase-keys/-/lowercase-keys-1.0.1.tgz", - "integrity": "sha512-G2Lj61tXDnVFFOi8VZds+SoQjtQC3dgokKdDG2mTm1tx4m50NUHBOZSBwQQHyy0V12A0JTG4icfZQH+xPyh8VA==", - "dev": true - } } }, "json-buffer": { @@ -10149,6 +10108,12 @@ "json-buffer": "3.0.0" } }, + "lowercase-keys": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/lowercase-keys/-/lowercase-keys-1.0.1.tgz", + "integrity": "sha512-G2Lj61tXDnVFFOi8VZds+SoQjtQC3dgokKdDG2mTm1tx4m50NUHBOZSBwQQHyy0V12A0JTG4icfZQH+xPyh8VA==", + "dev": true + }, "normalize-url": { "version": "4.5.1", "resolved": "https://registry.npmjs.org/normalize-url/-/normalize-url-4.5.1.tgz", @@ -10168,14 +10133,6 @@ "dev": true, "requires": { "lowercase-keys": "^1.0.0" - }, - "dependencies": { - "lowercase-keys": { - "version": "1.0.1", - "resolved": "https://registry.npmjs.org/lowercase-keys/-/lowercase-keys-1.0.1.tgz", - "integrity": "sha512-G2Lj61tXDnVFFOi8VZds+SoQjtQC3dgokKdDG2mTm1tx4m50NUHBOZSBwQQHyy0V12A0JTG4icfZQH+xPyh8VA==", - "dev": true - } } }, "semver": { diff --git a/packages/client/index.ts b/packages/client/index.ts index 408cbe3b996..45b4f59ee4d 100644 --- a/packages/client/index.ts +++ b/packages/client/index.ts @@ -1,10 +1,14 @@ import RedisClient from './lib/client'; import RedisCluster from './lib/cluster'; +export { RedisClientType, RedisClientOptions } from './lib/client'; + export const createClient = RedisClient.create; export const commandOptions = RedisClient.commandOptions; +export { RedisClusterType, RedisClusterOptions } from './lib/cluster'; + export const createCluster = RedisCluster.create; export { defineScript } from './lib/lua-script'; From e848f90e2ae0df9269115a31679ba2866ab2fa22 Mon Sep 17 00:00:00 2001 From: leibale Date: Mon, 29 Nov 2021 13:16:47 -0500 Subject: [PATCH 1016/1748] add description to package.json --- package.json | 1 + 1 file changed, 1 insertion(+) diff --git a/package.json b/package.json index 4b3b562f6a2..51b55ef591f 100644 --- a/package.json +++ b/package.json @@ -1,5 +1,6 @@ { "name": "redis", + "description": "A modern, high performance Redis client", "version": "4.0.0", "license": "MIT", "main": "./dist/index.js", From 592714fb00d9512e4dc101108353692b3993cbd5 Mon Sep 17 00:00:00 2001 From: leibale Date: Tue, 30 Nov 2021 19:10:06 -0500 Subject: [PATCH 1017/1748] fix #1749 - FT.SEARCH SORTBY --- packages/search/lib/commands/SEARCH.ts | 17 ++++++++--------- 1 file changed, 8 insertions(+), 9 deletions(-) diff --git a/packages/search/lib/commands/SEARCH.ts b/packages/search/lib/commands/SEARCH.ts index 9616bfa0bee..3347e3751b2 100644 --- a/packages/search/lib/commands/SEARCH.ts +++ b/packages/search/lib/commands/SEARCH.ts @@ -48,8 +48,8 @@ interface SearchOptions { SCORER?: string; // EXPLAINSCORE?: true; // TODO: WITHSCORES // PAYLOAD?: ; - // SORTBY?: SortByOptions; - MSORTBY?: SortByOptions | Array; + SORTBY?: SortByOptions; + // MSORTBY?: SortByOptions | Array; LIMIT?: { from: number | string; size: number | string; @@ -149,15 +149,14 @@ export function transformArguments( // args.push('PAYLOAD', options.PAYLOAD); // } - // if (options?.SORTBY) { - // args.push('SORTBY'); - // pushSortByArguments(args, options.SORTBY); - // } - - if (options?.MSORTBY) { - pushSortByArguments(args, 'MSORTBY', options.MSORTBY); + if (options?.SORTBY) { + pushSortByArguments(args, 'SORTBY', options.SORTBY); } + // if (options?.MSORTBY) { + // pushSortByArguments(args, 'MSORTBY', options.MSORTBY); + // } + if (options?.LIMIT) { args.push( 'LIMIT', From d0203d713ae37ed4e966d89ffd702565e761f6b2 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Vojta=20Stan=C4=9Bk?= Date: Wed, 1 Dec 2021 01:17:11 +0100 Subject: [PATCH 1018/1748] Client: Export errors (#1750) * Client: Export errors * export RedisModules & RedisScripts as well Co-authored-by: Leibale Eidelman --- packages/client/index.ts | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/packages/client/index.ts b/packages/client/index.ts index 45b4f59ee4d..1a5d497aa9d 100644 --- a/packages/client/index.ts +++ b/packages/client/index.ts @@ -3,6 +3,8 @@ import RedisCluster from './lib/cluster'; export { RedisClientType, RedisClientOptions } from './lib/client'; +export { RedisModules, RedisScripts } from './lib/commands'; + export const createClient = RedisClient.create; export const commandOptions = RedisClient.commandOptions; @@ -12,3 +14,5 @@ export { RedisClusterType, RedisClusterOptions } from './lib/cluster'; export const createCluster = RedisCluster.create; export { defineScript } from './lib/lua-script'; + +export * from './lib/errors'; From a45311416ded706d8dd25ecb45c94814cc5e85b9 Mon Sep 17 00:00:00 2001 From: leibale Date: Tue, 30 Nov 2021 19:21:02 -0500 Subject: [PATCH 1019/1748] fix for 592714fb00d9512e4dc101108353692b3993cbd5 - "fix tests" --- packages/search/lib/commands/SEARCH.spec.ts | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/packages/search/lib/commands/SEARCH.spec.ts b/packages/search/lib/commands/SEARCH.spec.ts index 15dc4740948..18c3e7d737e 100644 --- a/packages/search/lib/commands/SEARCH.spec.ts +++ b/packages/search/lib/commands/SEARCH.spec.ts @@ -196,10 +196,10 @@ describe('SEARCH', () => { ); }); - it('with MSORTBY', () => { + it('with SORTBY', () => { assert.deepEqual( - transformArguments('index', 'query', { MSORTBY: '@by' }), - ['FT.SEARCH', 'index', 'query', 'MSORTBY', '1', '@by'] + transformArguments('index', 'query', { SORTBY: '@by' }), + ['FT.SEARCH', 'index', 'query', 'SORTBY', '1', '@by'] ); }); From 70872d8839fbdbbe209ebeaabacb968aea4f2dad Mon Sep 17 00:00:00 2001 From: leibale Date: Tue, 30 Nov 2021 21:28:04 -0500 Subject: [PATCH 1020/1748] fix for a45311416ded706d8dd25ecb45c94814cc5e85b9 - fix FT.SEARCH --- packages/search/lib/commands/SEARCH.spec.ts | 2 +- packages/search/lib/commands/SEARCH.ts | 5 +++-- 2 files changed, 4 insertions(+), 3 deletions(-) diff --git a/packages/search/lib/commands/SEARCH.spec.ts b/packages/search/lib/commands/SEARCH.spec.ts index 18c3e7d737e..a4d75dd895f 100644 --- a/packages/search/lib/commands/SEARCH.spec.ts +++ b/packages/search/lib/commands/SEARCH.spec.ts @@ -199,7 +199,7 @@ describe('SEARCH', () => { it('with SORTBY', () => { assert.deepEqual( transformArguments('index', 'query', { SORTBY: '@by' }), - ['FT.SEARCH', 'index', 'query', 'SORTBY', '1', '@by'] + ['FT.SEARCH', 'index', 'query', 'SORTBY', '@by'] ); }); diff --git a/packages/search/lib/commands/SEARCH.ts b/packages/search/lib/commands/SEARCH.ts index 3347e3751b2..0202d41de84 100644 --- a/packages/search/lib/commands/SEARCH.ts +++ b/packages/search/lib/commands/SEARCH.ts @@ -1,6 +1,6 @@ import { RedisCommandArguments } from '@node-redis/client/dist/lib/commands'; import { pushOptionalVerdictArgument, pushVerdictArgument, transformReplyTuples } from '@node-redis/client/dist/lib/commands/generic-transformers'; -import { RedisSearchLanguages, PropertyName, pushSortByArguments, SortByOptions } from '.'; +import { RedisSearchLanguages, PropertyName, pushSortByProperty, SortByOptions } from '.'; export const FIRST_KEY_INDEX = 1; @@ -150,7 +150,8 @@ export function transformArguments( // } if (options?.SORTBY) { - pushSortByArguments(args, 'SORTBY', options.SORTBY); + args.push('SORTBY'); + pushSortByProperty(args, options.SORTBY); } // if (options?.MSORTBY) { From cba0289ff70acbf106a74ff2524260459ff59b9f Mon Sep 17 00:00:00 2001 From: leibale Date: Tue, 30 Nov 2021 21:34:37 -0500 Subject: [PATCH 1021/1748] fix FT.SEARCH --- packages/search/lib/commands/AGGREGATE.ts | 4 ++-- packages/search/lib/commands/SEARCH.ts | 6 +++--- packages/search/lib/commands/index.ts | 6 +++--- 3 files changed, 8 insertions(+), 8 deletions(-) diff --git a/packages/search/lib/commands/AGGREGATE.ts b/packages/search/lib/commands/AGGREGATE.ts index c81dcfef4dd..25466188906 100644 --- a/packages/search/lib/commands/AGGREGATE.ts +++ b/packages/search/lib/commands/AGGREGATE.ts @@ -1,6 +1,6 @@ import { RedisCommandArguments } from '@node-redis/client/dist/lib/commands'; import { pushVerdictArgument, transformReplyTuples, TuplesObject } from '@node-redis/client/dist/lib/commands/generic-transformers'; -import { PropertyName, pushArgumentsWithLength, pushSortByArguments, SortByOptions } from '.'; +import { PropertyName, pushArgumentsWithLength, pushSortByArguments, SortByProperty } from '.'; export enum AggregateSteps { GROUPBY = 'GROUPBY', @@ -95,7 +95,7 @@ interface GroupByStep extends AggregateStep { } interface SortStep extends AggregateStep { - BY: SortByOptions | Array; + BY: SortByProperty | Array; MAX?: number; } diff --git a/packages/search/lib/commands/SEARCH.ts b/packages/search/lib/commands/SEARCH.ts index 0202d41de84..34d255e5b2c 100644 --- a/packages/search/lib/commands/SEARCH.ts +++ b/packages/search/lib/commands/SEARCH.ts @@ -1,6 +1,6 @@ import { RedisCommandArguments } from '@node-redis/client/dist/lib/commands'; import { pushOptionalVerdictArgument, pushVerdictArgument, transformReplyTuples } from '@node-redis/client/dist/lib/commands/generic-transformers'; -import { RedisSearchLanguages, PropertyName, pushSortByProperty, SortByOptions } from '.'; +import { RedisSearchLanguages, PropertyName, pushSortByProperty, SortByProperty } from '.'; export const FIRST_KEY_INDEX = 1; @@ -48,8 +48,8 @@ interface SearchOptions { SCORER?: string; // EXPLAINSCORE?: true; // TODO: WITHSCORES // PAYLOAD?: ; - SORTBY?: SortByOptions; - // MSORTBY?: SortByOptions | Array; + SORTBY?: SortByProperty; + // MSORTBY?: SortByProperty | Array; LIMIT?: { from: number | string; size: number | string; diff --git a/packages/search/lib/commands/index.ts b/packages/search/lib/commands/index.ts index 70626c00df0..d7b2b793438 100644 --- a/packages/search/lib/commands/index.ts +++ b/packages/search/lib/commands/index.ts @@ -117,12 +117,12 @@ export enum RedisSearchLanguages { export type PropertyName = `${'@' | '$.'}${string}`; -export type SortByOptions = PropertyName | { +export type SortByProperty = PropertyName | { BY: PropertyName; DIRECTION?: 'ASC' | 'DESC'; }; -function pushSortByProperty(args: RedisCommandArguments, sortBy: SortByOptions): void { +export function pushSortByProperty(args: RedisCommandArguments, sortBy: SortByProperty): void { if (typeof sortBy === 'string') { args.push(sortBy); } else { @@ -134,7 +134,7 @@ function pushSortByProperty(args: RedisCommandArguments, sortBy: SortByOptions): } } -export function pushSortByArguments(args: RedisCommandArguments, name: string, sortBy: SortByOptions | Array): RedisCommandArguments { +export function pushSortByArguments(args: RedisCommandArguments, name: string, sortBy: SortByProperty | Array): RedisCommandArguments { const lengthBefore = args.push( name, '' // will be overwritten From f55de0efbf0778f6541ac404d7bab02c74ba0b80 Mon Sep 17 00:00:00 2001 From: leibale Date: Wed, 1 Dec 2021 20:17:52 -0500 Subject: [PATCH 1022/1748] improve encodeCommand performance, add set-get-delete-string benchmark --- benchmark/lib/index.js | 51 ++ benchmark/lib/runner.js | 76 ++ benchmark/lib/set-get-delete-string/index.js | 13 + .../lib/set-get-delete-string/ioredis.js | 19 + benchmark/lib/set-get-delete-string/local.js | 19 + .../lib/set-get-delete-string/production.js | 19 + benchmark/package-lock.json | 676 ++++++++++++++++++ benchmark/package.json | 17 + packages/client/lib/commander.ts | 31 +- packages/test-utils/package.json | 3 +- 10 files changed, 916 insertions(+), 8 deletions(-) create mode 100644 benchmark/lib/index.js create mode 100644 benchmark/lib/runner.js create mode 100644 benchmark/lib/set-get-delete-string/index.js create mode 100644 benchmark/lib/set-get-delete-string/ioredis.js create mode 100644 benchmark/lib/set-get-delete-string/local.js create mode 100644 benchmark/lib/set-get-delete-string/production.js create mode 100644 benchmark/package-lock.json create mode 100644 benchmark/package.json diff --git a/benchmark/lib/index.js b/benchmark/lib/index.js new file mode 100644 index 00000000000..a5d9e2cfe45 --- /dev/null +++ b/benchmark/lib/index.js @@ -0,0 +1,51 @@ +import yargs from 'yargs'; +import { hideBin } from 'yargs/helpers'; +import { promises as fs } from 'fs'; +import { fork } from 'child_process'; +import { URL, fileURLToPath } from 'url'; +import { once } from 'events'; + +async function getPathChoices() { + const dirents = await fs.readdir(new URL('.', import.meta.url), { + withFileTypes: true + }); + + const choices = []; + for (const dirent of dirents) { + if (!dirent.isDirectory()) continue; + + choices.push(dirent.name); + } + + return choices; +} + +const argv = hideBin(process.argv); + +async function getName() { + return yargs(argv) + .option('name', { + demandOption: true, + choices: await getPathChoices() + }) + .parseSync().name; +} + + +const runnerPath = fileURLToPath(new URL('runner.js', import.meta.url)), + path = new URL(`${await getName()}/`, import.meta.url), + metadata = await import(new URL('index.js', path)); + +for (const file of await fs.readdir(path)) { + if (file === 'index.js') continue; + + const benchmarkProcess = fork(runnerPath, [ + ...argv, + '--path', + fileURLToPath(path) + file + ]); + + await once(benchmarkProcess, 'message'); + benchmarkProcess.send(metadata); + await once(benchmarkProcess, 'close'); +} diff --git a/benchmark/lib/runner.js b/benchmark/lib/runner.js new file mode 100644 index 00000000000..51928d0b660 --- /dev/null +++ b/benchmark/lib/runner.js @@ -0,0 +1,76 @@ +import yargs from 'yargs'; +import { hideBin } from 'yargs/helpers'; +import { basename } from 'path'; + +const { path, times, concurrency } = yargs(hideBin(process.argv)) + .option('path', { + type: 'string', + demandOption: true + }) + .option('times', { + type: 'number', + default: 1_000_000, + demandOption: true + }) + .option('concurrency', { + type: 'number', + default: 100, + demandOption: true + }) + .parseSync(); + +async function setup() { + const module = await import(path); + await module.setup(); + return module; +} + +function getMetadata() { + return new Promise(resolve => { + process.once('message', resolve); + process.send('ready'); + }); +} + +const [ { benchmark, teardown }, metadata ] = await Promise.all([ + setup(), + getMetadata() +]); + +async function run(times) { + return new Promise(resolve => { + let num = 0, + inProgress = 0; + + function run() { + ++inProgress; + ++num; + benchmark(metadata) + .catch(err => console.error(err)) + .finally(() => { + --inProgress; + + if (num < times) { + run(); + } else if (inProgress === 0) { + resolve(); + } + }); + } + + for (let i = 0; i < concurrency; i++) { + run(); + } + }); +} + +// warmup +await run(Math.min(times * 0.1, 10_000)); + +// benchmark +const start = process.hrtime.bigint(); +await run(times); +const took = (process.hrtime.bigint() - start); +console.log(`[${basename(path)}]: took ${took / 1_000_000n}ms, ${took / BigInt(times)}ns per operation`); + +await teardown(); diff --git a/benchmark/lib/set-get-delete-string/index.js b/benchmark/lib/set-get-delete-string/index.js new file mode 100644 index 00000000000..719edfc7fdf --- /dev/null +++ b/benchmark/lib/set-get-delete-string/index.js @@ -0,0 +1,13 @@ +import yargs from 'yargs'; +import { hideBin } from 'yargs/helpers'; +import { randomBytes } from 'crypto'; + +const { size } = yargs(hideBin(process.argv)) + .option('size', { + type: 'number', + default: 1024, + demandOption: true + }) + .parseSync(); + +export const randomString = randomBytes(size).toString('ascii'); diff --git a/benchmark/lib/set-get-delete-string/ioredis.js b/benchmark/lib/set-get-delete-string/ioredis.js new file mode 100644 index 00000000000..a6cd5ccb185 --- /dev/null +++ b/benchmark/lib/set-get-delete-string/ioredis.js @@ -0,0 +1,19 @@ +import Redis from 'ioredis'; + +const client = new Redis({ lazyConnect: true }); + +export function setup() { + return client.connect(); +} + +export function benchmark({ randomString }) { + return Promise.all([ + client.set(randomString, randomString), + client.get(randomString), + client.del(randomString) + ]); +} + +export function teardown() { + return client.disconnect(); +} diff --git a/benchmark/lib/set-get-delete-string/local.js b/benchmark/lib/set-get-delete-string/local.js new file mode 100644 index 00000000000..ed576122692 --- /dev/null +++ b/benchmark/lib/set-get-delete-string/local.js @@ -0,0 +1,19 @@ +import { createClient } from '@node-redis/client-local'; + +const client = createClient(); + +export function setup() { + return client.connect(); +} + +export function benchmark({ randomString }) { + return Promise.all([ + client.set(randomString, randomString), + client.get(randomString), + client.del(randomString) + ]); +} + +export function teardown() { + return client.disconnect(); +} diff --git a/benchmark/lib/set-get-delete-string/production.js b/benchmark/lib/set-get-delete-string/production.js new file mode 100644 index 00000000000..941291900f4 --- /dev/null +++ b/benchmark/lib/set-get-delete-string/production.js @@ -0,0 +1,19 @@ +import { createClient } from '@node-redis/client-production'; + +const client = createClient(); + +export function setup() { + return client.connect(); +} + +export function benchmark({ randomString }) { + return Promise.all([ + client.set(randomString, randomString), + client.get(randomString), + client.del(randomString) + ]); +} + +export function teardown() { + return client.disconnect(); +} diff --git a/benchmark/package-lock.json b/benchmark/package-lock.json new file mode 100644 index 00000000000..d3610f42e4a --- /dev/null +++ b/benchmark/package-lock.json @@ -0,0 +1,676 @@ +{ + "name": "@node-redis/client-benchmark", + "lockfileVersion": 2, + "requires": true, + "packages": { + "": { + "name": "@node-redis/client-benchmark", + "dependencies": { + "@node-redis/client-local": "../packages/client", + "@node-redis/client-production": "npm:@node-redis/client@1.0.0", + "hazelcast-client": "^5.0.2", + "ioredis": "4.28.1", + "redis-v3": "npm:redis@3.1.2", + "yargs": "17.3.0" + } + }, + "../packages/client": { + "name": "@node-redis/client", + "version": "1.0.0", + "license": "MIT", + "dependencies": { + "cluster-key-slot": "1.1.0", + "generic-pool": "3.8.2", + "redis-parser": "3.0.0", + "yallist": "4.0.0" + }, + "devDependencies": { + "@istanbuljs/nyc-config-typescript": "^1.0.1", + "@node-redis/benchmark": "*", + "@node-redis/test-utils": "*", + "@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.3.0", + "nyc": "^15.1.0", + "release-it": "^14.11.8", + "sinon": "^12.0.1", + "source-map-support": "^0.5.21", + "ts-node": "^10.4.0", + "typedoc": "^0.22.10", + "typedoc-github-wiki-theme": "^0.6.0", + "typedoc-plugin-markdown": "^3.11.7", + "typescript": "^4.5.2" + }, + "engines": { + "node": ">=12" + } + }, + "node_modules/@node-redis/client-local": { + "resolved": "../packages/client", + "link": true + }, + "node_modules/@node-redis/client-production": { + "name": "@node-redis/client", + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/@node-redis/client/-/client-1.0.0.tgz", + "integrity": "sha512-DWDMeZELXG3rOGzCKfJEHCkCP6rgiA1H+oqj2N0NR4Q0fQUYMxTsyoqt80GpdYLilUW6zoCiQl9yL3vJhGhiCA==", + "dependencies": { + "cluster-key-slot": "1.1.0", + "generic-pool": "3.8.2", + "redis-parser": "3.0.0", + "yallist": "4.0.0" + }, + "engines": { + "node": ">=12" + } + }, + "node_modules/@types/long": { + "version": "4.0.1", + "resolved": "https://registry.npmjs.org/@types/long/-/long-4.0.1.tgz", + "integrity": "sha512-5tXH6Bx/kNGd3MgffdmP4dy2Z+G4eaXw0SE81Tq3BNadtnMR5/ySMzX4SLEzHJzSmPNn4HIdpQsBvXMUykr58w==" + }, + "node_modules/ansi-regex": { + "version": "5.0.1", + "resolved": "https://registry.npmjs.org/ansi-regex/-/ansi-regex-5.0.1.tgz", + "integrity": "sha512-quJQXlTSUGL2LH9SUXo8VwsY4soanhgo6LNSm84E1LBcE8s3O0wpdiRzyR9z/ZZJMlMWv37qOOb9pdJlMUEKFQ==", + "engines": { + "node": ">=8" + } + }, + "node_modules/ansi-styles": { + "version": "4.3.0", + "resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-4.3.0.tgz", + "integrity": "sha512-zbB9rCJAT1rbjiVDb2hqKFHNYLxgtk8NURxZ3IZwD3F6NtxbXZQCnnSi1Lkx+IDohdPlFp222wVALIheZJQSEg==", + "dependencies": { + "color-convert": "^2.0.1" + }, + "engines": { + "node": ">=8" + }, + "funding": { + "url": "https://github.com/chalk/ansi-styles?sponsor=1" + } + }, + "node_modules/cliui": { + "version": "7.0.4", + "resolved": "https://registry.npmjs.org/cliui/-/cliui-7.0.4.tgz", + "integrity": "sha512-OcRE68cOsVMXp1Yvonl/fzkQOyjLSu/8bhPDfQt0e0/Eb283TKP20Fs2MqoPsr9SwA595rRCA+QMzYc9nBP+JQ==", + "dependencies": { + "string-width": "^4.2.0", + "strip-ansi": "^6.0.0", + "wrap-ansi": "^7.0.0" + } + }, + "node_modules/cluster-key-slot": { + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/cluster-key-slot/-/cluster-key-slot-1.1.0.tgz", + "integrity": "sha512-2Nii8p3RwAPiFwsnZvukotvow2rIHM+yQ6ZcBXGHdniadkYGZYiGmkHJIbZPIV9nfv7m/U1IPMVVcAhoWFeklw==", + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/color-convert": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/color-convert/-/color-convert-2.0.1.tgz", + "integrity": "sha512-RRECPsj7iu/xb5oKYcsFHSppFNnsj/52OVTRKb4zP5onXwVF3zVmmToNcOfGC+CRDpfK/U584fMg38ZHCaElKQ==", + "dependencies": { + "color-name": "~1.1.4" + }, + "engines": { + "node": ">=7.0.0" + } + }, + "node_modules/color-name": { + "version": "1.1.4", + "resolved": "https://registry.npmjs.org/color-name/-/color-name-1.1.4.tgz", + "integrity": "sha512-dOy+3AuW3a2wNbZHIuMZpTcgjGuLU/uBL/ubcZF9OXbDo8ff4O8yVp5Bf0efS8uEoYo5q4Fx7dY9OgQGXgAsQA==" + }, + "node_modules/debug": { + "version": "4.3.3", + "resolved": "https://registry.npmjs.org/debug/-/debug-4.3.3.tgz", + "integrity": "sha512-/zxw5+vh1Tfv+4Qn7a5nsbcJKPaSvCDhojn6FEl9vupwK2VCSDtEiEtqr8DFtzYFOdz63LBkxec7DYuc2jon6Q==", + "dependencies": { + "ms": "2.1.2" + }, + "engines": { + "node": ">=6.0" + }, + "peerDependenciesMeta": { + "supports-color": { + "optional": true + } + } + }, + "node_modules/denque": { + "version": "1.5.1", + "resolved": "https://registry.npmjs.org/denque/-/denque-1.5.1.tgz", + "integrity": "sha512-XwE+iZ4D6ZUB7mfYRMb5wByE8L74HCn30FBN7sWnXksWc1LO1bPDl67pBR9o/kC4z/xSNAwkMYcGgqDV3BE3Hw==", + "engines": { + "node": ">=0.10" + } + }, + "node_modules/emoji-regex": { + "version": "8.0.0", + "resolved": "https://registry.npmjs.org/emoji-regex/-/emoji-regex-8.0.0.tgz", + "integrity": "sha512-MSjYzcWNOA0ewAHpz0MxpYFvwg6yjy1NG3xteoqz644VCo/RPgnr1/GGt+ic3iJTzQ8Eu3TdM14SawnVUmGE6A==" + }, + "node_modules/escalade": { + "version": "3.1.1", + "resolved": "https://registry.npmjs.org/escalade/-/escalade-3.1.1.tgz", + "integrity": "sha512-k0er2gUkLf8O0zKJiAhmkTnJlTvINGv7ygDNPbeIsX/TJjGJZHuh9B2UxbsaEkmlEo9MfhrSzmhIlhRlI2GXnw==", + "engines": { + "node": ">=6" + } + }, + "node_modules/generic-pool": { + "version": "3.8.2", + "resolved": "https://registry.npmjs.org/generic-pool/-/generic-pool-3.8.2.tgz", + "integrity": "sha512-nGToKy6p3PAbYQ7p1UlWl6vSPwfwU6TMSWK7TTu+WUY4ZjyZQGniGGt2oNVvyNSpyZYSB43zMXVLcBm08MTMkg==", + "engines": { + "node": ">= 4" + } + }, + "node_modules/get-caller-file": { + "version": "2.0.5", + "resolved": "https://registry.npmjs.org/get-caller-file/-/get-caller-file-2.0.5.tgz", + "integrity": "sha512-DyFP3BM/3YHTQOCUL/w0OZHR0lpKeGrxotcHWcqNEdnltqFwXVfhEBQ94eIo34AfQpo0rGki4cyIiftY06h2Fg==", + "engines": { + "node": "6.* || 8.* || >= 10.*" + } + }, + "node_modules/hazelcast-client": { + "version": "5.0.2", + "resolved": "https://registry.npmjs.org/hazelcast-client/-/hazelcast-client-5.0.2.tgz", + "integrity": "sha512-CsNvZZn2bQn8E+j5HgXzhhEfjliBn6626qBb5upcIQHw1J9rtobiS2/pqabNKXMwKjyzVNc/ui+yqFKaGMrc6A==", + "dependencies": { + "@types/long": "4.0.1", + "long": "4.0.0" + }, + "engines": { + "node": ">=10.4.0" + } + }, + "node_modules/ioredis": { + "version": "4.28.1", + "resolved": "https://registry.npmjs.org/ioredis/-/ioredis-4.28.1.tgz", + "integrity": "sha512-7gcrUJEcPHWy+eEyq6wIZpXtfHt8crhbc5+z0sqrnHUkwBblXinygfamj+/jx83Qo+2LW3q87Nj2VsuH6BF2BA==", + "dependencies": { + "cluster-key-slot": "^1.1.0", + "debug": "^4.3.1", + "denque": "^1.1.0", + "lodash.defaults": "^4.2.0", + "lodash.flatten": "^4.4.0", + "lodash.isarguments": "^3.1.0", + "p-map": "^2.1.0", + "redis-commands": "1.7.0", + "redis-errors": "^1.2.0", + "redis-parser": "^3.0.0", + "standard-as-callback": "^2.1.0" + }, + "engines": { + "node": ">=6" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/ioredis" + } + }, + "node_modules/is-fullwidth-code-point": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/is-fullwidth-code-point/-/is-fullwidth-code-point-3.0.0.tgz", + "integrity": "sha512-zymm5+u+sCsSWyD9qNaejV3DFvhCKclKdizYaJUuHA83RLjb7nSuGnddCHGv0hk+KY7BMAlsWeK4Ueg6EV6XQg==", + "engines": { + "node": ">=8" + } + }, + "node_modules/lodash.defaults": { + "version": "4.2.0", + "resolved": "https://registry.npmjs.org/lodash.defaults/-/lodash.defaults-4.2.0.tgz", + "integrity": "sha1-0JF4cW/+pN3p5ft7N/bwgCJ0WAw=" + }, + "node_modules/lodash.flatten": { + "version": "4.4.0", + "resolved": "https://registry.npmjs.org/lodash.flatten/-/lodash.flatten-4.4.0.tgz", + "integrity": "sha1-8xwiIlqWMtK7+OSt2+8kCqdlph8=" + }, + "node_modules/lodash.isarguments": { + "version": "3.1.0", + "resolved": "https://registry.npmjs.org/lodash.isarguments/-/lodash.isarguments-3.1.0.tgz", + "integrity": "sha1-L1c9hcaiQon/AGY7SRwdM4/zRYo=" + }, + "node_modules/long": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/long/-/long-4.0.0.tgz", + "integrity": "sha512-XsP+KhQif4bjX1kbuSiySJFNAehNxgLb6hPRGJ9QsUr8ajHkuXGdrHmFUTUUXhDwVX2R5bY4JNZEwbUiMhV+MA==" + }, + "node_modules/ms": { + "version": "2.1.2", + "resolved": "https://registry.npmjs.org/ms/-/ms-2.1.2.tgz", + "integrity": "sha512-sGkPx+VjMtmA6MX27oA4FBFELFCZZ4S4XqeGOXCv68tT+jb3vk/RyaKWP0PTKyWtmLSM0b+adUTEvbs1PEaH2w==" + }, + "node_modules/p-map": { + "version": "2.1.0", + "resolved": "https://registry.npmjs.org/p-map/-/p-map-2.1.0.tgz", + "integrity": "sha512-y3b8Kpd8OAN444hxfBbFfj1FY/RjtTd8tzYwhUqNYXx0fXx2iX4maP4Qr6qhIKbQXI02wTLAda4fYUbDagTUFw==", + "engines": { + "node": ">=6" + } + }, + "node_modules/redis-commands": { + "version": "1.7.0", + "resolved": "https://registry.npmjs.org/redis-commands/-/redis-commands-1.7.0.tgz", + "integrity": "sha512-nJWqw3bTFy21hX/CPKHth6sfhZbdiHP6bTawSgQBlKOVRG7EZkfHbbHwQJnrE4vsQf0CMNE+3gJ4Fmm16vdVlQ==" + }, + "node_modules/redis-errors": { + "version": "1.2.0", + "resolved": "https://registry.npmjs.org/redis-errors/-/redis-errors-1.2.0.tgz", + "integrity": "sha1-62LSrbFeTq9GEMBK/hUpOEJQq60=", + "engines": { + "node": ">=4" + } + }, + "node_modules/redis-parser": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/redis-parser/-/redis-parser-3.0.0.tgz", + "integrity": "sha1-tm2CjNyv5rS4pCin3vTGvKwxyLQ=", + "dependencies": { + "redis-errors": "^1.0.0" + }, + "engines": { + "node": ">=4" + } + }, + "node_modules/redis-v3": { + "name": "redis", + "version": "3.1.2", + "resolved": "https://registry.npmjs.org/redis/-/redis-3.1.2.tgz", + "integrity": "sha512-grn5KoZLr/qrRQVwoSkmzdbw6pwF+/rwODtrOr6vuBRiR/f3rjSTGupbF90Zpqm2oenix8Do6RV7pYEkGwlKkw==", + "dependencies": { + "denque": "^1.5.0", + "redis-commands": "^1.7.0", + "redis-errors": "^1.2.0", + "redis-parser": "^3.0.0" + }, + "engines": { + "node": ">=10" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/node-redis" + } + }, + "node_modules/require-directory": { + "version": "2.1.1", + "resolved": "https://registry.npmjs.org/require-directory/-/require-directory-2.1.1.tgz", + "integrity": "sha1-jGStX9MNqxyXbiNE/+f3kqam30I=", + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/standard-as-callback": { + "version": "2.1.0", + "resolved": "https://registry.npmjs.org/standard-as-callback/-/standard-as-callback-2.1.0.tgz", + "integrity": "sha512-qoRRSyROncaz1z0mvYqIE4lCd9p2R90i6GxW3uZv5ucSu8tU7B5HXUP1gG8pVZsYNVaXjk8ClXHPttLyxAL48A==" + }, + "node_modules/string-width": { + "version": "4.2.3", + "resolved": "https://registry.npmjs.org/string-width/-/string-width-4.2.3.tgz", + "integrity": "sha512-wKyQRQpjJ0sIp62ErSZdGsjMJWsap5oRNihHhu6G7JVO/9jIB6UyevL+tXuOqrng8j/cxKTWyWUwvSTriiZz/g==", + "dependencies": { + "emoji-regex": "^8.0.0", + "is-fullwidth-code-point": "^3.0.0", + "strip-ansi": "^6.0.1" + }, + "engines": { + "node": ">=8" + } + }, + "node_modules/strip-ansi": { + "version": "6.0.1", + "resolved": "https://registry.npmjs.org/strip-ansi/-/strip-ansi-6.0.1.tgz", + "integrity": "sha512-Y38VPSHcqkFrCpFnQ9vuSXmquuv5oXOKpGeT6aGrr3o3Gc9AlVa6JBfUSOCnbxGGZF+/0ooI7KrPuUSztUdU5A==", + "dependencies": { + "ansi-regex": "^5.0.1" + }, + "engines": { + "node": ">=8" + } + }, + "node_modules/wrap-ansi": { + "version": "7.0.0", + "resolved": "https://registry.npmjs.org/wrap-ansi/-/wrap-ansi-7.0.0.tgz", + "integrity": "sha512-YVGIj2kamLSTxw6NsZjoBxfSwsn0ycdesmc4p+Q21c5zPuZ1pl+NfxVdxPtdHvmNVOQ6XSYG4AUtyt/Fi7D16Q==", + "dependencies": { + "ansi-styles": "^4.0.0", + "string-width": "^4.1.0", + "strip-ansi": "^6.0.0" + }, + "engines": { + "node": ">=10" + }, + "funding": { + "url": "https://github.com/chalk/wrap-ansi?sponsor=1" + } + }, + "node_modules/y18n": { + "version": "5.0.8", + "resolved": "https://registry.npmjs.org/y18n/-/y18n-5.0.8.tgz", + "integrity": "sha512-0pfFzegeDWJHJIAmTLRP2DwHjdF5s7jo9tuztdQxAhINCdvS+3nGINqPd00AphqJR/0LhANUS6/+7SCb98YOfA==", + "engines": { + "node": ">=10" + } + }, + "node_modules/yallist": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/yallist/-/yallist-4.0.0.tgz", + "integrity": "sha512-3wdGidZyq5PB084XLES5TpOSRA3wjXAlIWMhum2kRcv/41Sn2emQ0dycQW4uZXLejwKvg6EsvbdlVL+FYEct7A==" + }, + "node_modules/yargs": { + "version": "17.3.0", + "resolved": "https://registry.npmjs.org/yargs/-/yargs-17.3.0.tgz", + "integrity": "sha512-GQl1pWyDoGptFPJx9b9L6kmR33TGusZvXIZUT+BOz9f7X2L94oeAskFYLEg/FkhV06zZPBYLvLZRWeYId29lew==", + "dependencies": { + "cliui": "^7.0.2", + "escalade": "^3.1.1", + "get-caller-file": "^2.0.5", + "require-directory": "^2.1.1", + "string-width": "^4.2.3", + "y18n": "^5.0.5", + "yargs-parser": "^21.0.0" + }, + "engines": { + "node": ">=12" + } + }, + "node_modules/yargs-parser": { + "version": "21.0.0", + "resolved": "https://registry.npmjs.org/yargs-parser/-/yargs-parser-21.0.0.tgz", + "integrity": "sha512-z9kApYUOCwoeZ78rfRYYWdiU/iNL6mwwYlkkZfJoyMR1xps+NEBX5X7XmRpxkZHhXJ6+Ey00IwKxBBSW9FIjyA==", + "engines": { + "node": ">=12" + } + } + }, + "dependencies": { + "@node-redis/client-local": { + "version": "file:../packages/client", + "requires": { + "@istanbuljs/nyc-config-typescript": "^1.0.1", + "@node-redis/benchmark": "*", + "@node-redis/test-utils": "*", + "@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.3.0", + "generic-pool": "3.8.2", + "nyc": "^15.1.0", + "redis-parser": "3.0.0", + "release-it": "^14.11.8", + "sinon": "^12.0.1", + "source-map-support": "^0.5.21", + "ts-node": "^10.4.0", + "typedoc": "^0.22.10", + "typedoc-github-wiki-theme": "^0.6.0", + "typedoc-plugin-markdown": "^3.11.7", + "typescript": "^4.5.2", + "yallist": "4.0.0" + } + }, + "@node-redis/client-production": { + "version": "npm:@node-redis/client@1.0.0", + "resolved": "https://registry.npmjs.org/@node-redis/client/-/client-1.0.0.tgz", + "integrity": "sha512-DWDMeZELXG3rOGzCKfJEHCkCP6rgiA1H+oqj2N0NR4Q0fQUYMxTsyoqt80GpdYLilUW6zoCiQl9yL3vJhGhiCA==", + "requires": { + "cluster-key-slot": "1.1.0", + "generic-pool": "3.8.2", + "redis-parser": "3.0.0", + "yallist": "4.0.0" + } + }, + "@types/long": { + "version": "4.0.1", + "resolved": "https://registry.npmjs.org/@types/long/-/long-4.0.1.tgz", + "integrity": "sha512-5tXH6Bx/kNGd3MgffdmP4dy2Z+G4eaXw0SE81Tq3BNadtnMR5/ySMzX4SLEzHJzSmPNn4HIdpQsBvXMUykr58w==" + }, + "ansi-regex": { + "version": "5.0.1", + "resolved": "https://registry.npmjs.org/ansi-regex/-/ansi-regex-5.0.1.tgz", + "integrity": "sha512-quJQXlTSUGL2LH9SUXo8VwsY4soanhgo6LNSm84E1LBcE8s3O0wpdiRzyR9z/ZZJMlMWv37qOOb9pdJlMUEKFQ==" + }, + "ansi-styles": { + "version": "4.3.0", + "resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-4.3.0.tgz", + "integrity": "sha512-zbB9rCJAT1rbjiVDb2hqKFHNYLxgtk8NURxZ3IZwD3F6NtxbXZQCnnSi1Lkx+IDohdPlFp222wVALIheZJQSEg==", + "requires": { + "color-convert": "^2.0.1" + } + }, + "cliui": { + "version": "7.0.4", + "resolved": "https://registry.npmjs.org/cliui/-/cliui-7.0.4.tgz", + "integrity": "sha512-OcRE68cOsVMXp1Yvonl/fzkQOyjLSu/8bhPDfQt0e0/Eb283TKP20Fs2MqoPsr9SwA595rRCA+QMzYc9nBP+JQ==", + "requires": { + "string-width": "^4.2.0", + "strip-ansi": "^6.0.0", + "wrap-ansi": "^7.0.0" + } + }, + "cluster-key-slot": { + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/cluster-key-slot/-/cluster-key-slot-1.1.0.tgz", + "integrity": "sha512-2Nii8p3RwAPiFwsnZvukotvow2rIHM+yQ6ZcBXGHdniadkYGZYiGmkHJIbZPIV9nfv7m/U1IPMVVcAhoWFeklw==" + }, + "color-convert": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/color-convert/-/color-convert-2.0.1.tgz", + "integrity": "sha512-RRECPsj7iu/xb5oKYcsFHSppFNnsj/52OVTRKb4zP5onXwVF3zVmmToNcOfGC+CRDpfK/U584fMg38ZHCaElKQ==", + "requires": { + "color-name": "~1.1.4" + } + }, + "color-name": { + "version": "1.1.4", + "resolved": "https://registry.npmjs.org/color-name/-/color-name-1.1.4.tgz", + "integrity": "sha512-dOy+3AuW3a2wNbZHIuMZpTcgjGuLU/uBL/ubcZF9OXbDo8ff4O8yVp5Bf0efS8uEoYo5q4Fx7dY9OgQGXgAsQA==" + }, + "debug": { + "version": "4.3.3", + "resolved": "https://registry.npmjs.org/debug/-/debug-4.3.3.tgz", + "integrity": "sha512-/zxw5+vh1Tfv+4Qn7a5nsbcJKPaSvCDhojn6FEl9vupwK2VCSDtEiEtqr8DFtzYFOdz63LBkxec7DYuc2jon6Q==", + "requires": { + "ms": "2.1.2" + } + }, + "denque": { + "version": "1.5.1", + "resolved": "https://registry.npmjs.org/denque/-/denque-1.5.1.tgz", + "integrity": "sha512-XwE+iZ4D6ZUB7mfYRMb5wByE8L74HCn30FBN7sWnXksWc1LO1bPDl67pBR9o/kC4z/xSNAwkMYcGgqDV3BE3Hw==" + }, + "emoji-regex": { + "version": "8.0.0", + "resolved": "https://registry.npmjs.org/emoji-regex/-/emoji-regex-8.0.0.tgz", + "integrity": "sha512-MSjYzcWNOA0ewAHpz0MxpYFvwg6yjy1NG3xteoqz644VCo/RPgnr1/GGt+ic3iJTzQ8Eu3TdM14SawnVUmGE6A==" + }, + "escalade": { + "version": "3.1.1", + "resolved": "https://registry.npmjs.org/escalade/-/escalade-3.1.1.tgz", + "integrity": "sha512-k0er2gUkLf8O0zKJiAhmkTnJlTvINGv7ygDNPbeIsX/TJjGJZHuh9B2UxbsaEkmlEo9MfhrSzmhIlhRlI2GXnw==" + }, + "generic-pool": { + "version": "3.8.2", + "resolved": "https://registry.npmjs.org/generic-pool/-/generic-pool-3.8.2.tgz", + "integrity": "sha512-nGToKy6p3PAbYQ7p1UlWl6vSPwfwU6TMSWK7TTu+WUY4ZjyZQGniGGt2oNVvyNSpyZYSB43zMXVLcBm08MTMkg==" + }, + "get-caller-file": { + "version": "2.0.5", + "resolved": "https://registry.npmjs.org/get-caller-file/-/get-caller-file-2.0.5.tgz", + "integrity": "sha512-DyFP3BM/3YHTQOCUL/w0OZHR0lpKeGrxotcHWcqNEdnltqFwXVfhEBQ94eIo34AfQpo0rGki4cyIiftY06h2Fg==" + }, + "hazelcast-client": { + "version": "5.0.2", + "resolved": "https://registry.npmjs.org/hazelcast-client/-/hazelcast-client-5.0.2.tgz", + "integrity": "sha512-CsNvZZn2bQn8E+j5HgXzhhEfjliBn6626qBb5upcIQHw1J9rtobiS2/pqabNKXMwKjyzVNc/ui+yqFKaGMrc6A==", + "requires": { + "@types/long": "4.0.1", + "long": "4.0.0" + } + }, + "ioredis": { + "version": "4.28.1", + "resolved": "https://registry.npmjs.org/ioredis/-/ioredis-4.28.1.tgz", + "integrity": "sha512-7gcrUJEcPHWy+eEyq6wIZpXtfHt8crhbc5+z0sqrnHUkwBblXinygfamj+/jx83Qo+2LW3q87Nj2VsuH6BF2BA==", + "requires": { + "cluster-key-slot": "^1.1.0", + "debug": "^4.3.1", + "denque": "^1.1.0", + "lodash.defaults": "^4.2.0", + "lodash.flatten": "^4.4.0", + "lodash.isarguments": "^3.1.0", + "p-map": "^2.1.0", + "redis-commands": "1.7.0", + "redis-errors": "^1.2.0", + "redis-parser": "^3.0.0", + "standard-as-callback": "^2.1.0" + } + }, + "is-fullwidth-code-point": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/is-fullwidth-code-point/-/is-fullwidth-code-point-3.0.0.tgz", + "integrity": "sha512-zymm5+u+sCsSWyD9qNaejV3DFvhCKclKdizYaJUuHA83RLjb7nSuGnddCHGv0hk+KY7BMAlsWeK4Ueg6EV6XQg==" + }, + "lodash.defaults": { + "version": "4.2.0", + "resolved": "https://registry.npmjs.org/lodash.defaults/-/lodash.defaults-4.2.0.tgz", + "integrity": "sha1-0JF4cW/+pN3p5ft7N/bwgCJ0WAw=" + }, + "lodash.flatten": { + "version": "4.4.0", + "resolved": "https://registry.npmjs.org/lodash.flatten/-/lodash.flatten-4.4.0.tgz", + "integrity": "sha1-8xwiIlqWMtK7+OSt2+8kCqdlph8=" + }, + "lodash.isarguments": { + "version": "3.1.0", + "resolved": "https://registry.npmjs.org/lodash.isarguments/-/lodash.isarguments-3.1.0.tgz", + "integrity": "sha1-L1c9hcaiQon/AGY7SRwdM4/zRYo=" + }, + "long": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/long/-/long-4.0.0.tgz", + "integrity": "sha512-XsP+KhQif4bjX1kbuSiySJFNAehNxgLb6hPRGJ9QsUr8ajHkuXGdrHmFUTUUXhDwVX2R5bY4JNZEwbUiMhV+MA==" + }, + "ms": { + "version": "2.1.2", + "resolved": "https://registry.npmjs.org/ms/-/ms-2.1.2.tgz", + "integrity": "sha512-sGkPx+VjMtmA6MX27oA4FBFELFCZZ4S4XqeGOXCv68tT+jb3vk/RyaKWP0PTKyWtmLSM0b+adUTEvbs1PEaH2w==" + }, + "p-map": { + "version": "2.1.0", + "resolved": "https://registry.npmjs.org/p-map/-/p-map-2.1.0.tgz", + "integrity": "sha512-y3b8Kpd8OAN444hxfBbFfj1FY/RjtTd8tzYwhUqNYXx0fXx2iX4maP4Qr6qhIKbQXI02wTLAda4fYUbDagTUFw==" + }, + "redis-commands": { + "version": "1.7.0", + "resolved": "https://registry.npmjs.org/redis-commands/-/redis-commands-1.7.0.tgz", + "integrity": "sha512-nJWqw3bTFy21hX/CPKHth6sfhZbdiHP6bTawSgQBlKOVRG7EZkfHbbHwQJnrE4vsQf0CMNE+3gJ4Fmm16vdVlQ==" + }, + "redis-errors": { + "version": "1.2.0", + "resolved": "https://registry.npmjs.org/redis-errors/-/redis-errors-1.2.0.tgz", + "integrity": "sha1-62LSrbFeTq9GEMBK/hUpOEJQq60=" + }, + "redis-parser": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/redis-parser/-/redis-parser-3.0.0.tgz", + "integrity": "sha1-tm2CjNyv5rS4pCin3vTGvKwxyLQ=", + "requires": { + "redis-errors": "^1.0.0" + } + }, + "redis-v3": { + "version": "npm:redis@3.1.2", + "resolved": "https://registry.npmjs.org/redis/-/redis-3.1.2.tgz", + "integrity": "sha512-grn5KoZLr/qrRQVwoSkmzdbw6pwF+/rwODtrOr6vuBRiR/f3rjSTGupbF90Zpqm2oenix8Do6RV7pYEkGwlKkw==", + "requires": { + "denque": "^1.5.0", + "redis-commands": "^1.7.0", + "redis-errors": "^1.2.0", + "redis-parser": "^3.0.0" + } + }, + "require-directory": { + "version": "2.1.1", + "resolved": "https://registry.npmjs.org/require-directory/-/require-directory-2.1.1.tgz", + "integrity": "sha1-jGStX9MNqxyXbiNE/+f3kqam30I=" + }, + "standard-as-callback": { + "version": "2.1.0", + "resolved": "https://registry.npmjs.org/standard-as-callback/-/standard-as-callback-2.1.0.tgz", + "integrity": "sha512-qoRRSyROncaz1z0mvYqIE4lCd9p2R90i6GxW3uZv5ucSu8tU7B5HXUP1gG8pVZsYNVaXjk8ClXHPttLyxAL48A==" + }, + "string-width": { + "version": "4.2.3", + "resolved": "https://registry.npmjs.org/string-width/-/string-width-4.2.3.tgz", + "integrity": "sha512-wKyQRQpjJ0sIp62ErSZdGsjMJWsap5oRNihHhu6G7JVO/9jIB6UyevL+tXuOqrng8j/cxKTWyWUwvSTriiZz/g==", + "requires": { + "emoji-regex": "^8.0.0", + "is-fullwidth-code-point": "^3.0.0", + "strip-ansi": "^6.0.1" + } + }, + "strip-ansi": { + "version": "6.0.1", + "resolved": "https://registry.npmjs.org/strip-ansi/-/strip-ansi-6.0.1.tgz", + "integrity": "sha512-Y38VPSHcqkFrCpFnQ9vuSXmquuv5oXOKpGeT6aGrr3o3Gc9AlVa6JBfUSOCnbxGGZF+/0ooI7KrPuUSztUdU5A==", + "requires": { + "ansi-regex": "^5.0.1" + } + }, + "wrap-ansi": { + "version": "7.0.0", + "resolved": "https://registry.npmjs.org/wrap-ansi/-/wrap-ansi-7.0.0.tgz", + "integrity": "sha512-YVGIj2kamLSTxw6NsZjoBxfSwsn0ycdesmc4p+Q21c5zPuZ1pl+NfxVdxPtdHvmNVOQ6XSYG4AUtyt/Fi7D16Q==", + "requires": { + "ansi-styles": "^4.0.0", + "string-width": "^4.1.0", + "strip-ansi": "^6.0.0" + } + }, + "y18n": { + "version": "5.0.8", + "resolved": "https://registry.npmjs.org/y18n/-/y18n-5.0.8.tgz", + "integrity": "sha512-0pfFzegeDWJHJIAmTLRP2DwHjdF5s7jo9tuztdQxAhINCdvS+3nGINqPd00AphqJR/0LhANUS6/+7SCb98YOfA==" + }, + "yallist": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/yallist/-/yallist-4.0.0.tgz", + "integrity": "sha512-3wdGidZyq5PB084XLES5TpOSRA3wjXAlIWMhum2kRcv/41Sn2emQ0dycQW4uZXLejwKvg6EsvbdlVL+FYEct7A==" + }, + "yargs": { + "version": "17.3.0", + "resolved": "https://registry.npmjs.org/yargs/-/yargs-17.3.0.tgz", + "integrity": "sha512-GQl1pWyDoGptFPJx9b9L6kmR33TGusZvXIZUT+BOz9f7X2L94oeAskFYLEg/FkhV06zZPBYLvLZRWeYId29lew==", + "requires": { + "cliui": "^7.0.2", + "escalade": "^3.1.1", + "get-caller-file": "^2.0.5", + "require-directory": "^2.1.1", + "string-width": "^4.2.3", + "y18n": "^5.0.5", + "yargs-parser": "^21.0.0" + } + }, + "yargs-parser": { + "version": "21.0.0", + "resolved": "https://registry.npmjs.org/yargs-parser/-/yargs-parser-21.0.0.tgz", + "integrity": "sha512-z9kApYUOCwoeZ78rfRYYWdiU/iNL6mwwYlkkZfJoyMR1xps+NEBX5X7XmRpxkZHhXJ6+Ey00IwKxBBSW9FIjyA==" + } + } +} diff --git a/benchmark/package.json b/benchmark/package.json new file mode 100644 index 00000000000..4964cc60ec8 --- /dev/null +++ b/benchmark/package.json @@ -0,0 +1,17 @@ +{ + "name": "@node-redis/client-benchmark", + "private": true, + "main": "./lib", + "type": "module", + "scripts": { + "start": "node ." + }, + "dependencies": { + "@node-redis/client-local": "../packages/client", + "@node-redis/client-production": "npm:@node-redis/client@1.0.0", + "hazelcast-client": "^5.0.2", + "ioredis": "4.28.1", + "redis-v3": "npm:redis@3.1.2", + "yargs": "17.3.0" + } +} diff --git a/packages/client/lib/commander.ts b/packages/client/lib/commander.ts index 50d416f5b5c..4a2bcfea634 100644 --- a/packages/client/lib/commander.ts +++ b/packages/client/lib/commander.ts @@ -92,14 +92,33 @@ export function transformCommandArguments( const DELIMITER = '\r\n'; export function* encodeCommand(args: RedisCommandArguments): IterableIterator { - yield `*${args.length}${DELIMITER}`; - + let strings = `*${args.length}${DELIMITER}`, + stringsLength = 0; for (const arg of args) { - const byteLength = typeof arg === 'string' ? Buffer.byteLength(arg): arg.length; - yield `$${byteLength.toString()}${DELIMITER}`; - yield arg; - yield DELIMITER; + const isString = typeof arg === 'string', + byteLength = isString ? Buffer.byteLength(arg): arg.length; + strings += `$${byteLength}${DELIMITER}`; + + if (isString) { + const totalLength = stringsLength + byteLength; + if (totalLength > 1024) { + yield strings; + strings = arg; + stringsLength = byteLength; + } else { + strings += arg; + stringsLength = totalLength; + } + } else { + yield strings; + stringsLength = 0; + yield arg; + } + + strings += DELIMITER; } + + yield strings; } export function transformCommandReply( diff --git a/packages/test-utils/package.json b/packages/test-utils/package.json index e46f82f0c01..ccc3cf38cf3 100644 --- a/packages/test-utils/package.json +++ b/packages/test-utils/package.json @@ -4,8 +4,7 @@ "main": "./dist/index.js", "types": "./dist/index.d.ts", "scripts": { - "build": "tsc", - "test": "echo \"TODO\"" + "build": "tsc" }, "peerDependencies": { "@node-redis/client": "^1.0.0" From 41bd136bb7ce147528189c62aa074b98f2b3c323 Mon Sep 17 00:00:00 2001 From: Leibale Eidelman Date: Wed, 1 Dec 2021 22:54:31 -0500 Subject: [PATCH 1023/1748] fix encodeCommand --- packages/client/lib/commander.ts | 1 + 1 file changed, 1 insertion(+) diff --git a/packages/client/lib/commander.ts b/packages/client/lib/commander.ts index 4a2bcfea634..3f11ebc39a7 100644 --- a/packages/client/lib/commander.ts +++ b/packages/client/lib/commander.ts @@ -111,6 +111,7 @@ export function* encodeCommand(args: RedisCommandArguments): IterableIterator Date: Fri, 3 Dec 2021 03:12:16 +0800 Subject: [PATCH 1024/1748] Add the `event` doc (#1753) --- README.md | 14 ++++++++++++++ 1 file changed, 14 insertions(+) diff --git a/README.md b/README.md index cc2edec9e77..c1d08a957e4 100644 --- a/README.md +++ b/README.md @@ -277,6 +277,20 @@ await Promise.all([ Check out the [Clustering Guide](./docs/clustering.md) when using Node Redis to connect to a Redis Cluster. +### Events + +The Node Redis client class is an Nodejs EventEmitter and it emits an event each time the network status changes: + +| Event name | Scenes | Parameters | +|--------------|-------------------------------------------------------------------------------------------------------------------|------------------------------------------------------------------------------------------------------------------------------------| +| connect | The client is initiating a connection to the server. | _undefined_ | +| ready | The client successfully initiated the connection to the server. | _undefined_ | +| end | The client disconnected the connection to the server via `.quit()` or `.disconnect()`. | _undefined_ | +| error | When a network error has occurred, such as unable to connect to the server or the connection closed unexpectedly. | The error object, such as `SocketClosedUnexpectedlyError: Socket closed unexpectedly` or `Error: connect ECONNREFUSED [IP]:[PORT]` | +| reconnecting | The client is trying to reconnect to the server. | _undefined_ | + +The client will not emit any other events beyond those listed above. + ## Supported Redis versions Node Redis is supported with the following versions of Redis: From 7202f3582ffba33ef44c659ef88a09ef5563b900 Mon Sep 17 00:00:00 2001 From: leibale Date: Thu, 2 Dec 2021 14:28:38 -0500 Subject: [PATCH 1025/1748] hdr-histogram-js --- benchmark/lib/index.js | 1 - benchmark/lib/runner.js | 18 ++++--- benchmark/package-lock.json | 97 ++++++++++++++++++++++--------------- benchmark/package.json | 2 +- benchmark/requirements.txt | 1 + 5 files changed, 73 insertions(+), 46 deletions(-) create mode 100644 benchmark/requirements.txt diff --git a/benchmark/lib/index.js b/benchmark/lib/index.js index a5d9e2cfe45..fd811fb5928 100644 --- a/benchmark/lib/index.js +++ b/benchmark/lib/index.js @@ -31,7 +31,6 @@ async function getName() { .parseSync().name; } - const runnerPath = fileURLToPath(new URL('runner.js', import.meta.url)), path = new URL(`${await getName()}/`, import.meta.url), metadata = await import(new URL('index.js', path)); diff --git a/benchmark/lib/runner.js b/benchmark/lib/runner.js index 51928d0b660..9a38bb8b862 100644 --- a/benchmark/lib/runner.js +++ b/benchmark/lib/runner.js @@ -1,6 +1,8 @@ import yargs from 'yargs'; import { hideBin } from 'yargs/helpers'; import { basename } from 'path'; +import * as hdr from 'hdr-histogram-js'; +hdr.initWebAssemblySync(); const { path, times, concurrency } = yargs(hideBin(process.argv)) .option('path', { @@ -39,26 +41,31 @@ const [ { benchmark, teardown }, metadata ] = await Promise.all([ async function run(times) { return new Promise(resolve => { + const histogram = hdr.build({ useWebAssembly: true }); let num = 0, inProgress = 0; function run() { ++inProgress; ++num; + + const start = process.hrtime.bigint(); benchmark(metadata) .catch(err => console.error(err)) .finally(() => { + histogram.recordValue(Number(process.hrtime.bigint() - start)); --inProgress; if (num < times) { run(); } else if (inProgress === 0) { - resolve(); + resolve(histogram); } }); } - for (let i = 0; i < concurrency; i++) { + const toInitiate = Math.min(concurrency, times); + for (let i = 0; i < toInitiate; i++) { run(); } }); @@ -68,9 +75,8 @@ async function run(times) { await run(Math.min(times * 0.1, 10_000)); // benchmark -const start = process.hrtime.bigint(); -await run(times); -const took = (process.hrtime.bigint() - start); -console.log(`[${basename(path)}]: took ${took / 1_000_000n}ms, ${took / BigInt(times)}ns per operation`); +const histogram = await run(times); +console.log(`[${basename(path)}]:`); +console.table(histogram.toJSON()); await teardown(); diff --git a/benchmark/package-lock.json b/benchmark/package-lock.json index d3610f42e4a..af72486e0af 100644 --- a/benchmark/package-lock.json +++ b/benchmark/package-lock.json @@ -8,7 +8,7 @@ "dependencies": { "@node-redis/client-local": "../packages/client", "@node-redis/client-production": "npm:@node-redis/client@1.0.0", - "hazelcast-client": "^5.0.2", + "hdr-histogram-js": "^2.0.1", "ioredis": "4.28.1", "redis-v3": "npm:redis@3.1.2", "yargs": "17.3.0" @@ -26,7 +26,6 @@ }, "devDependencies": { "@istanbuljs/nyc-config-typescript": "^1.0.1", - "@node-redis/benchmark": "*", "@node-redis/test-utils": "*", "@types/node": "^16.11.10", "@types/redis-parser": "^3.0.0", @@ -49,6 +48,11 @@ "node": ">=12" } }, + "node_modules/@assemblyscript/loader": { + "version": "0.10.1", + "resolved": "https://registry.npmjs.org/@assemblyscript/loader/-/loader-0.10.1.tgz", + "integrity": "sha512-H71nDOOL8Y7kWRLqf6Sums+01Q5msqBW2KhDUTemh1tvY04eSkSXrK0uj/4mmY0Xr16/3zyZmsrxN7CKuRbNRg==" + }, "node_modules/@node-redis/client-local": { "resolved": "../packages/client", "link": true @@ -68,11 +72,6 @@ "node": ">=12" } }, - "node_modules/@types/long": { - "version": "4.0.1", - "resolved": "https://registry.npmjs.org/@types/long/-/long-4.0.1.tgz", - "integrity": "sha512-5tXH6Bx/kNGd3MgffdmP4dy2Z+G4eaXw0SE81Tq3BNadtnMR5/ySMzX4SLEzHJzSmPNn4HIdpQsBvXMUykr58w==" - }, "node_modules/ansi-regex": { "version": "5.0.1", "resolved": "https://registry.npmjs.org/ansi-regex/-/ansi-regex-5.0.1.tgz", @@ -95,6 +94,25 @@ "url": "https://github.com/chalk/ansi-styles?sponsor=1" } }, + "node_modules/base64-js": { + "version": "1.5.1", + "resolved": "https://registry.npmjs.org/base64-js/-/base64-js-1.5.1.tgz", + "integrity": "sha512-AKpaYlHn8t4SVbOHCy+b5+KKgvR4vrsD8vbvrbiQJps7fKDTkjkDry6ji0rUJjC0kzbNePLwzxq8iypo41qeWA==", + "funding": [ + { + "type": "github", + "url": "https://github.com/sponsors/feross" + }, + { + "type": "patreon", + "url": "https://www.patreon.com/feross" + }, + { + "type": "consulting", + "url": "https://feross.org/support" + } + ] + }, "node_modules/cliui": { "version": "7.0.4", "resolved": "https://registry.npmjs.org/cliui/-/cliui-7.0.4.tgz", @@ -182,16 +200,14 @@ "node": "6.* || 8.* || >= 10.*" } }, - "node_modules/hazelcast-client": { - "version": "5.0.2", - "resolved": "https://registry.npmjs.org/hazelcast-client/-/hazelcast-client-5.0.2.tgz", - "integrity": "sha512-CsNvZZn2bQn8E+j5HgXzhhEfjliBn6626qBb5upcIQHw1J9rtobiS2/pqabNKXMwKjyzVNc/ui+yqFKaGMrc6A==", + "node_modules/hdr-histogram-js": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/hdr-histogram-js/-/hdr-histogram-js-2.0.1.tgz", + "integrity": "sha512-uPZxl1dAFnjUFHWLZmt93vUUvtHeaBay9nVNHu38SdOjMSF/4KqJUqa1Seuj08ptU1rEb6AHvB41X8n/zFZ74Q==", "dependencies": { - "@types/long": "4.0.1", - "long": "4.0.0" - }, - "engines": { - "node": ">=10.4.0" + "@assemblyscript/loader": "^0.10.1", + "base64-js": "^1.2.0", + "pako": "^1.0.3" } }, "node_modules/ioredis": { @@ -242,11 +258,6 @@ "resolved": "https://registry.npmjs.org/lodash.isarguments/-/lodash.isarguments-3.1.0.tgz", "integrity": "sha1-L1c9hcaiQon/AGY7SRwdM4/zRYo=" }, - "node_modules/long": { - "version": "4.0.0", - "resolved": "https://registry.npmjs.org/long/-/long-4.0.0.tgz", - "integrity": "sha512-XsP+KhQif4bjX1kbuSiySJFNAehNxgLb6hPRGJ9QsUr8ajHkuXGdrHmFUTUUXhDwVX2R5bY4JNZEwbUiMhV+MA==" - }, "node_modules/ms": { "version": "2.1.2", "resolved": "https://registry.npmjs.org/ms/-/ms-2.1.2.tgz", @@ -260,6 +271,11 @@ "node": ">=6" } }, + "node_modules/pako": { + "version": "1.0.11", + "resolved": "https://registry.npmjs.org/pako/-/pako-1.0.11.tgz", + "integrity": "sha512-4hLB8Py4zZce5s4yd9XzopqwVv/yGNhV1Bl8NTmCq1763HeK2+EwVTv+leGeL13Dnh2wfbqowVPXCIO0z4taYw==" + }, "node_modules/redis-commands": { "version": "1.7.0", "resolved": "https://registry.npmjs.org/redis-commands/-/redis-commands-1.7.0.tgz", @@ -396,11 +412,15 @@ } }, "dependencies": { + "@assemblyscript/loader": { + "version": "0.10.1", + "resolved": "https://registry.npmjs.org/@assemblyscript/loader/-/loader-0.10.1.tgz", + "integrity": "sha512-H71nDOOL8Y7kWRLqf6Sums+01Q5msqBW2KhDUTemh1tvY04eSkSXrK0uj/4mmY0Xr16/3zyZmsrxN7CKuRbNRg==" + }, "@node-redis/client-local": { "version": "file:../packages/client", "requires": { "@istanbuljs/nyc-config-typescript": "^1.0.1", - "@node-redis/benchmark": "*", "@node-redis/test-utils": "*", "@types/node": "^16.11.10", "@types/redis-parser": "^3.0.0", @@ -435,11 +455,6 @@ "yallist": "4.0.0" } }, - "@types/long": { - "version": "4.0.1", - "resolved": "https://registry.npmjs.org/@types/long/-/long-4.0.1.tgz", - "integrity": "sha512-5tXH6Bx/kNGd3MgffdmP4dy2Z+G4eaXw0SE81Tq3BNadtnMR5/ySMzX4SLEzHJzSmPNn4HIdpQsBvXMUykr58w==" - }, "ansi-regex": { "version": "5.0.1", "resolved": "https://registry.npmjs.org/ansi-regex/-/ansi-regex-5.0.1.tgz", @@ -453,6 +468,11 @@ "color-convert": "^2.0.1" } }, + "base64-js": { + "version": "1.5.1", + "resolved": "https://registry.npmjs.org/base64-js/-/base64-js-1.5.1.tgz", + "integrity": "sha512-AKpaYlHn8t4SVbOHCy+b5+KKgvR4vrsD8vbvrbiQJps7fKDTkjkDry6ji0rUJjC0kzbNePLwzxq8iypo41qeWA==" + }, "cliui": { "version": "7.0.4", "resolved": "https://registry.npmjs.org/cliui/-/cliui-7.0.4.tgz", @@ -514,13 +534,14 @@ "resolved": "https://registry.npmjs.org/get-caller-file/-/get-caller-file-2.0.5.tgz", "integrity": "sha512-DyFP3BM/3YHTQOCUL/w0OZHR0lpKeGrxotcHWcqNEdnltqFwXVfhEBQ94eIo34AfQpo0rGki4cyIiftY06h2Fg==" }, - "hazelcast-client": { - "version": "5.0.2", - "resolved": "https://registry.npmjs.org/hazelcast-client/-/hazelcast-client-5.0.2.tgz", - "integrity": "sha512-CsNvZZn2bQn8E+j5HgXzhhEfjliBn6626qBb5upcIQHw1J9rtobiS2/pqabNKXMwKjyzVNc/ui+yqFKaGMrc6A==", + "hdr-histogram-js": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/hdr-histogram-js/-/hdr-histogram-js-2.0.1.tgz", + "integrity": "sha512-uPZxl1dAFnjUFHWLZmt93vUUvtHeaBay9nVNHu38SdOjMSF/4KqJUqa1Seuj08ptU1rEb6AHvB41X8n/zFZ74Q==", "requires": { - "@types/long": "4.0.1", - "long": "4.0.0" + "@assemblyscript/loader": "^0.10.1", + "base64-js": "^1.2.0", + "pako": "^1.0.3" } }, "ioredis": { @@ -561,11 +582,6 @@ "resolved": "https://registry.npmjs.org/lodash.isarguments/-/lodash.isarguments-3.1.0.tgz", "integrity": "sha1-L1c9hcaiQon/AGY7SRwdM4/zRYo=" }, - "long": { - "version": "4.0.0", - "resolved": "https://registry.npmjs.org/long/-/long-4.0.0.tgz", - "integrity": "sha512-XsP+KhQif4bjX1kbuSiySJFNAehNxgLb6hPRGJ9QsUr8ajHkuXGdrHmFUTUUXhDwVX2R5bY4JNZEwbUiMhV+MA==" - }, "ms": { "version": "2.1.2", "resolved": "https://registry.npmjs.org/ms/-/ms-2.1.2.tgz", @@ -576,6 +592,11 @@ "resolved": "https://registry.npmjs.org/p-map/-/p-map-2.1.0.tgz", "integrity": "sha512-y3b8Kpd8OAN444hxfBbFfj1FY/RjtTd8tzYwhUqNYXx0fXx2iX4maP4Qr6qhIKbQXI02wTLAda4fYUbDagTUFw==" }, + "pako": { + "version": "1.0.11", + "resolved": "https://registry.npmjs.org/pako/-/pako-1.0.11.tgz", + "integrity": "sha512-4hLB8Py4zZce5s4yd9XzopqwVv/yGNhV1Bl8NTmCq1763HeK2+EwVTv+leGeL13Dnh2wfbqowVPXCIO0z4taYw==" + }, "redis-commands": { "version": "1.7.0", "resolved": "https://registry.npmjs.org/redis-commands/-/redis-commands-1.7.0.tgz", diff --git a/benchmark/package.json b/benchmark/package.json index 4964cc60ec8..a4752b88335 100644 --- a/benchmark/package.json +++ b/benchmark/package.json @@ -9,7 +9,7 @@ "dependencies": { "@node-redis/client-local": "../packages/client", "@node-redis/client-production": "npm:@node-redis/client@1.0.0", - "hazelcast-client": "^5.0.2", + "hdr-histogram-js": "2.0.1", "ioredis": "4.28.1", "redis-v3": "npm:redis@3.1.2", "yargs": "17.3.0" diff --git a/benchmark/requirements.txt b/benchmark/requirements.txt new file mode 100644 index 00000000000..abff11d6abb --- /dev/null +++ b/benchmark/requirements.txt @@ -0,0 +1 @@ +redisbench_admin>=0.5.24 From 82920aef0b108721610e4138954ca825c2f807b8 Mon Sep 17 00:00:00 2001 From: leibale Date: Mon, 6 Dec 2021 20:29:59 -0500 Subject: [PATCH 1026/1748] fix #1764 - fix PubSub resubscribe --- packages/client/lib/client/commands-queue.ts | 35 ++++- packages/client/lib/client/index.spec.ts | 151 +++++++++++-------- 2 files changed, 119 insertions(+), 67 deletions(-) diff --git a/packages/client/lib/client/commands-queue.ts b/packages/client/lib/client/commands-queue.ts index 480d7d51408..efa80820904 100644 --- a/packages/client/lib/client/commands-queue.ts +++ b/packages/client/lib/client/commands-queue.ts @@ -294,9 +294,9 @@ export default class RedisCommandsQueue { } resolve(); }, - reject: () => { + reject: err => { pubSubState[inProgressKey] -= channelsCounter * (isSubscribe ? 1 : -1); - reject(); + reject(err); } }); }); @@ -307,11 +307,32 @@ export default class RedisCommandsQueue { return; } - // TODO: acl error on one channel/pattern will reject the whole command - return Promise.all([ - this.#pushPubSubCommand(PubSubSubscribeCommands.SUBSCRIBE, [...this.#pubSubState.listeners.channels.keys()]), - this.#pushPubSubCommand(PubSubSubscribeCommands.PSUBSCRIBE, [...this.#pubSubState.listeners.patterns.keys()]) - ]); + this.#pubSubState.subscribed = 0; + + const promises = [], + { channels, patterns } = this.#pubSubState.listeners; + + if (channels.size) { + promises.push( + this.#pushPubSubCommand( + PubSubSubscribeCommands.SUBSCRIBE, + [...channels.keys()] + ) + ); + } + + if (patterns.size) { + promises.push( + this.#pushPubSubCommand( + PubSubSubscribeCommands.PSUBSCRIBE, + [...patterns.keys()] + ) + ); + } + + if (promises.length) { + return Promise.all(promises); + } } getCommandToSend(): RedisCommandArguments | undefined { diff --git a/packages/client/lib/client/index.spec.ts b/packages/client/lib/client/index.spec.ts index 679c7ae692a..63bd9a1b46a 100644 --- a/packages/client/lib/client/index.spec.ts +++ b/packages/client/lib/client/index.spec.ts @@ -560,73 +560,104 @@ 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'); - } + describe('PubSub', () => { + testUtils.testWithClient('should be able to publish and subscribe to messages', 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)); - } + function assertBufferListener(message: Buffer, channel: Buffer) { + assert.ok(Buffer.isBuffer(message)); + assert.ok(Buffer.isBuffer(channel)); + } - const subscriber = publisher.duplicate(); + const subscriber = publisher.duplicate(); + + await subscriber.connect(); + + try { + const channelListener1 = spy(assertBufferListener), + channelListener2 = spy(assertStringListener), + patternListener = spy(assertStringListener); + + await Promise.all([ + subscriber.subscribe('channel', channelListener1, true), + subscriber.subscribe('channel', channelListener2), + subscriber.pSubscribe('channel*', patternListener) + ]); + await Promise.all([ + waitTillBeenCalled(channelListener1), + waitTillBeenCalled(channelListener2), + waitTillBeenCalled(patternListener), + publisher.publish(Buffer.from('channel'), Buffer.from('message')) + ]); + + assert.ok(channelListener1.calledOnceWithExactly(Buffer.from('message'), Buffer.from('channel'))); + assert.ok(channelListener2.calledOnceWithExactly('message', 'channel')); + assert.ok(patternListener.calledOnceWithExactly('message', 'channel')); + + await subscriber.unsubscribe('channel', channelListener1, true); + await Promise.all([ + waitTillBeenCalled(channelListener2), + waitTillBeenCalled(patternListener), + publisher.publish('channel', 'message') + ]); + assert.ok(channelListener1.calledOnce); + assert.ok(channelListener2.calledTwice); + assert.ok(channelListener2.secondCall.calledWithExactly('message', 'channel')); + assert.ok(patternListener.calledTwice); + assert.ok(patternListener.secondCall.calledWithExactly('message', 'channel')); + await subscriber.unsubscribe('channel'); + await Promise.all([ + waitTillBeenCalled(patternListener), + publisher.publish('channel', 'message') + ]); + assert.ok(channelListener1.calledOnce); + assert.ok(channelListener2.calledTwice); + assert.ok(patternListener.calledThrice); + assert.ok(patternListener.thirdCall.calledWithExactly('message', 'channel')); + await subscriber.pUnsubscribe(); + await publisher.publish('channel', 'message'); + assert.ok(channelListener1.calledOnce); + assert.ok(channelListener2.calledTwice); + assert.ok(patternListener.calledThrice); + // should be able to send commands when unsubsribed from all channels (see #1652) + await assert.doesNotReject(subscriber.ping()); + } finally { + await subscriber.disconnect(); + } + }, GLOBAL.SERVERS.OPEN); - await subscriber.connect(); + testUtils.testWithClient('should resubscribe', async publisher => { + const subscriber = publisher.duplicate(); - try { - const channelListener1 = spy(assertBufferListener), - channelListener2 = spy(assertStringListener), - patternListener = spy(assertStringListener); + await subscriber.connect(); - await Promise.all([ - subscriber.subscribe('channel', channelListener1, true), - subscriber.subscribe('channel', channelListener2), - subscriber.pSubscribe('channel*', patternListener) - ]); - await Promise.all([ - waitTillBeenCalled(channelListener1), - waitTillBeenCalled(channelListener2), - waitTillBeenCalled(patternListener), - publisher.publish(Buffer.from('channel'), Buffer.from('message')) - ]); + try { + const listener = spy(); + await subscriber.subscribe('channel', listener); + + subscriber.on('error', err => { + console.error('subscriber err', err.message); + }); - assert.ok(channelListener1.calledOnceWithExactly(Buffer.from('message'), Buffer.from('channel'))); - assert.ok(channelListener2.calledOnceWithExactly('message', 'channel')); - assert.ok(patternListener.calledOnceWithExactly('message', 'channel')); + await Promise.all([ + once(subscriber, 'error'), + publisher.sendCommand(['CLIENT', 'KILL', 'SKIPME', 'yes']) + ]); - await subscriber.unsubscribe('channel', channelListener1, true); - await Promise.all([ - waitTillBeenCalled(channelListener2), - waitTillBeenCalled(patternListener), - publisher.publish('channel', 'message') - ]); - assert.ok(channelListener1.calledOnce); - assert.ok(channelListener2.calledTwice); - assert.ok(channelListener2.secondCall.calledWithExactly('message', 'channel')); - assert.ok(patternListener.calledTwice); - assert.ok(patternListener.secondCall.calledWithExactly('message', 'channel')); - await subscriber.unsubscribe('channel'); - await Promise.all([ - waitTillBeenCalled(patternListener), - publisher.publish('channel', 'message') - ]); - assert.ok(channelListener1.calledOnce); - assert.ok(channelListener2.calledTwice); - assert.ok(patternListener.calledThrice); - assert.ok(patternListener.thirdCall.calledWithExactly('message', 'channel')); - await subscriber.pUnsubscribe(); - await publisher.publish('channel', 'message'); - assert.ok(channelListener1.calledOnce); - assert.ok(channelListener2.calledTwice); - assert.ok(patternListener.calledThrice); - // should be able to send commands when unsubsribed from all channels (see #1652) - await assert.doesNotReject(subscriber.ping()); - } finally { - await subscriber.disconnect(); - } - }, GLOBAL.SERVERS.OPEN); + await once(subscriber, 'ready'); + + await Promise.all([ + waitTillBeenCalled(listener), + publisher.publish('channel', 'message') + ]); + } finally { + await subscriber.disconnect(); + } + }, GLOBAL.SERVERS.OPEN); + }); testUtils.testWithClient('ConnectionTimeoutError', async client => { const promise = assert.rejects(client.connect(), ConnectionTimeoutError), From ec7ccaf82744c502c135991d7325fc81e1c12053 Mon Sep 17 00:00:00 2001 From: leibale Date: Mon, 6 Dec 2021 21:48:14 -0500 Subject: [PATCH 1027/1748] fix #1758 - implement some CLIENT commands, add `name` to `RedisClientOptions` --- docs/client-configuration.md | 1 + packages/client/lib/client/commands.ts | 15 +++ packages/client/lib/client/index.spec.ts | 29 ++++-- packages/client/lib/client/index.ts | 10 ++ .../lib/commands/CLIENT_CACHING.spec.ts | 20 ++++ .../client/lib/commands/CLIENT_CACHING.ts | 11 +++ .../lib/commands/CLIENT_GETNAME.spec.ts | 11 +++ .../client/lib/commands/CLIENT_GETNAME.ts | 7 ++ .../lib/commands/CLIENT_GETREDIR.spec.ts | 11 +++ .../client/lib/commands/CLIENT_GETREDIR.ts | 7 ++ .../client/lib/commands/CLIENT_KILL.spec.ts | 97 +++++++++++++++++++ packages/client/lib/commands/CLIENT_KILL.ts | 95 ++++++++++++++++++ .../lib/commands/CLIENT_SETNAME.spec.ts | 11 +++ .../client/lib/commands/CLIENT_SETNAME.ts | 7 ++ 14 files changed, 323 insertions(+), 9 deletions(-) create mode 100644 packages/client/lib/commands/CLIENT_CACHING.spec.ts create mode 100644 packages/client/lib/commands/CLIENT_CACHING.ts create mode 100644 packages/client/lib/commands/CLIENT_GETNAME.spec.ts create mode 100644 packages/client/lib/commands/CLIENT_GETNAME.ts create mode 100644 packages/client/lib/commands/CLIENT_GETREDIR.spec.ts create mode 100644 packages/client/lib/commands/CLIENT_GETREDIR.ts create mode 100644 packages/client/lib/commands/CLIENT_KILL.spec.ts create mode 100644 packages/client/lib/commands/CLIENT_KILL.ts create mode 100644 packages/client/lib/commands/CLIENT_SETNAME.spec.ts create mode 100644 packages/client/lib/commands/CLIENT_SETNAME.ts diff --git a/docs/client-configuration.md b/docs/client-configuration.md index f4aa8e99d6b..4ea94fcab17 100644 --- a/docs/client-configuration.md +++ b/docs/client-configuration.md @@ -15,6 +15,7 @@ | socket.reconnectStrategy | `retries => Math.min(retries * 50, 500)` | A function containing the [Reconnect Strategy](#reconnect-strategy) logic | | username | | ACL username ([see ACL guide](https://redis.io/topics/acl)) | | password | | ACL password or the old "--requirepass" password | +| name | | Connection name ([see `CLIENT SETNAME`](https://redis.io/commands/client-setname)) | | database | | Database number to connect to (see [`SELECT`](https://redis.io/commands/select) command) | | 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)) | diff --git a/packages/client/lib/client/commands.ts b/packages/client/lib/client/commands.ts index de901152f11..029a5e60f62 100644 --- a/packages/client/lib/client/commands.ts +++ b/packages/client/lib/client/commands.ts @@ -15,7 +15,12 @@ import * as ASKING from '../commands/ASKING'; import * as AUTH from '../commands/AUTH'; import * as BGREWRITEAOF from '../commands/BGREWRITEAOF'; import * as BGSAVE from '../commands/BGSAVE'; +import * as CLIENT_CACHING from '../commands/CLIENT_CACHING'; +import * as CLIENT_GETNAME from '../commands/CLIENT_GETNAME'; +import * as CLIENT_GETREDIR from '../commands/CLIENT_GETREDIR'; import * as CLIENT_ID from '../commands/CLIENT_ID'; +import * as CLIENT_KILL from '../commands/CLIENT_KILL'; +import * as CLIENT_SETNAME from '../commands/CLIENT_SETNAME'; import * as CLIENT_INFO from '../commands/CLIENT_INFO'; import * as CLUSTER_ADDSLOTS from '../commands/CLUSTER_ADDSLOTS'; import * as CLUSTER_FLUSHSLOTS from '../commands/CLUSTER_FLUSHSLOTS'; @@ -110,8 +115,18 @@ export default { bgRewriteAof: BGREWRITEAOF, BGSAVE, bgSave: BGSAVE, + CLIENT_CACHING, + clientCaching: CLIENT_CACHING, + CLIENT_GETNAME, + clientGetName: CLIENT_GETNAME, + CLIENT_GETREDIR, + clientGetRedir: CLIENT_GETREDIR, CLIENT_ID, clientId: CLIENT_ID, + CLIENT_KILL, + clientKill: CLIENT_KILL, + CLIENT_SETNAME, + clientSetName: CLIENT_SETNAME, CLIENT_INFO, clientInfo: CLIENT_INFO, CLUSTER_ADDSLOTS, diff --git a/packages/client/lib/client/index.spec.ts b/packages/client/lib/client/index.spec.ts index 63bd9a1b46a..01154e9dd1a 100644 --- a/packages/client/lib/client/index.spec.ts +++ b/packages/client/lib/client/index.spec.ts @@ -7,6 +7,7 @@ import { AbortError, ClientClosedError, ConnectionTimeoutError, DisconnectsClien import { defineScript } from '../lua-script'; import { spy } from 'sinon'; import { once } from 'events'; +import { ClientKillFilters } from '../commands/CLIENT_KILL'; export const SQUARE_SCRIPT = defineScript({ NUMBER_OF_KEYS: 0, @@ -125,6 +126,18 @@ describe('Client', () => { }); }); + testUtils.testWithClient('should set connection name', async client => { + assert.equal( + await client.clientGetName(), + 'name' + ); + }, { + ...GLOBAL.SERVERS.OPEN, + clientOptions: { + name: 'name' + } + }); + describe('legacyMode', () => { function sendCommandAsync(client: RedisClientType, args: RedisCommandArguments): Promise { return new Promise((resolve, reject) => { @@ -445,14 +458,9 @@ describe('Client', () => { }); testUtils.testWithClient('executeIsolated', async client => { - await client.sendCommand(['CLIENT', 'SETNAME', 'client']); - - assert.equal( - await client.executeIsolated(isolatedClient => - isolatedClient.sendCommand(['CLIENT', 'GETNAME']) - ), - null - ); + const id = await client.clientId(), + isolatedId = await client.executeIsolated(isolatedClient => isolatedClient.clientId()); + assert.ok(id !== isolatedId); }, GLOBAL.SERVERS.OPEN); async function killClient(client: RedisClientType): Promise { @@ -644,7 +652,10 @@ describe('Client', () => { await Promise.all([ once(subscriber, 'error'), - publisher.sendCommand(['CLIENT', 'KILL', 'SKIPME', 'yes']) + publisher.clientKill({ + filter: ClientKillFilters.SKIP_ME, + skipMe: true + }) ]); await once(subscriber, 'ready'); diff --git a/packages/client/lib/client/index.ts b/packages/client/lib/client/index.ts index 6bc0aec74d9..2bc5230580e 100644 --- a/packages/client/lib/client/index.ts +++ b/packages/client/lib/client/index.ts @@ -20,6 +20,7 @@ export interface RedisClientOptions ); } + if (this.#options?.name) { + promises.push( + this.#queue.addCommand( + COMMANDS.CLIENT_SETNAME.transformArguments(this.#options.name), + { asap: true } + ) + ); + } + if (this.#options?.username || this.#options?.password) { promises.push( this.#queue.addCommand( diff --git a/packages/client/lib/commands/CLIENT_CACHING.spec.ts b/packages/client/lib/commands/CLIENT_CACHING.spec.ts new file mode 100644 index 00000000000..d9cb9a3f796 --- /dev/null +++ b/packages/client/lib/commands/CLIENT_CACHING.spec.ts @@ -0,0 +1,20 @@ +import { strict as assert } from 'assert'; +import { transformArguments } from './CLIENT_CACHING'; + +describe('CLIENT CACHING', () => { + describe('transformArguments', () => { + it('true', () => { + assert.deepEqual( + transformArguments(true), + ['CLIENT', 'CACHING', 'YES'] + ); + }); + + it('false', () => { + assert.deepEqual( + transformArguments(false), + ['CLIENT', 'CACHING', 'NO'] + ); + }); + }); +}); diff --git a/packages/client/lib/commands/CLIENT_CACHING.ts b/packages/client/lib/commands/CLIENT_CACHING.ts new file mode 100644 index 00000000000..62a46bad6c5 --- /dev/null +++ b/packages/client/lib/commands/CLIENT_CACHING.ts @@ -0,0 +1,11 @@ +import { RedisCommandArguments } from '.'; + +export function transformArguments(value: boolean): RedisCommandArguments { + return [ + 'CLIENT', + 'CACHING', + value ? 'YES' : 'NO' + ]; +} + +export declare function transformReply(): 'OK'; diff --git a/packages/client/lib/commands/CLIENT_GETNAME.spec.ts b/packages/client/lib/commands/CLIENT_GETNAME.spec.ts new file mode 100644 index 00000000000..0a09713882f --- /dev/null +++ b/packages/client/lib/commands/CLIENT_GETNAME.spec.ts @@ -0,0 +1,11 @@ +import { strict as assert } from 'assert'; +import { transformArguments } from './CLIENT_GETNAME'; + +describe('CLIENT GETNAME', () => { + it('transformArguments', () => { + assert.deepEqual( + transformArguments(), + ['CLIENT', 'GETNAME'] + ); + }); +}); diff --git a/packages/client/lib/commands/CLIENT_GETNAME.ts b/packages/client/lib/commands/CLIENT_GETNAME.ts new file mode 100644 index 00000000000..da00539d7fb --- /dev/null +++ b/packages/client/lib/commands/CLIENT_GETNAME.ts @@ -0,0 +1,7 @@ +import { RedisCommandArguments } from '.'; + +export function transformArguments(): RedisCommandArguments { + return ['CLIENT', 'GETNAME']; +} + +export declare function transformReply(): string | null; diff --git a/packages/client/lib/commands/CLIENT_GETREDIR.spec.ts b/packages/client/lib/commands/CLIENT_GETREDIR.spec.ts new file mode 100644 index 00000000000..09dd9677e32 --- /dev/null +++ b/packages/client/lib/commands/CLIENT_GETREDIR.spec.ts @@ -0,0 +1,11 @@ +import { strict as assert } from 'assert'; +import { transformArguments } from './CLIENT_GETREDIR'; + +describe('CLIENT GETREDIR', () => { + it('transformArguments', () => { + assert.deepEqual( + transformArguments(), + ['CLIENT', 'GETREDIR'] + ); + }); +}); diff --git a/packages/client/lib/commands/CLIENT_GETREDIR.ts b/packages/client/lib/commands/CLIENT_GETREDIR.ts new file mode 100644 index 00000000000..d192adf284a --- /dev/null +++ b/packages/client/lib/commands/CLIENT_GETREDIR.ts @@ -0,0 +1,7 @@ +import { RedisCommandArguments } from '.'; + +export function transformArguments(): RedisCommandArguments { + return ['CLIENT', 'GETREDIR']; +} + +export declare function transformReply(): number; diff --git a/packages/client/lib/commands/CLIENT_KILL.spec.ts b/packages/client/lib/commands/CLIENT_KILL.spec.ts new file mode 100644 index 00000000000..0c38a0fb162 --- /dev/null +++ b/packages/client/lib/commands/CLIENT_KILL.spec.ts @@ -0,0 +1,97 @@ +import { strict as assert } from 'assert'; +import { ClientKillFilters, transformArguments } from './CLIENT_KILL'; + +describe('CLIENT KILL', () => { + describe('transformArguments', () => { + it('ADDRESS', () => { + assert.deepEqual( + transformArguments({ + filter: ClientKillFilters.ADDRESS, + address: 'ip:6379' + }), + ['CLIENT', 'KILL', 'ADDR', 'ip:6379'] + ); + }); + + it('LOCAL_ADDRESS', () => { + assert.deepEqual( + transformArguments({ + filter: ClientKillFilters.LOCAL_ADDRESS, + localAddress: 'ip:6379' + }), + ['CLIENT', 'KILL', 'LADDR', 'ip:6379'] + ); + }); + + describe('ID', () => { + it('string', () => { + assert.deepEqual( + transformArguments({ + filter: ClientKillFilters.ID, + id: '1' + }), + ['CLIENT', 'KILL', 'ID', '1'] + ); + }); + + it('number', () => { + assert.deepEqual( + transformArguments({ + filter: ClientKillFilters.ID, + id: 1 + }), + ['CLIENT', 'KILL', 'ID', '1'] + ); + }); + }); + + it('TYPE', () => { + assert.deepEqual( + transformArguments({ + filter: ClientKillFilters.TYPE, + type: 'master' + }), + ['CLIENT', 'KILL', 'TYPE', 'master'] + ); + }); + + it('USER', () => { + assert.deepEqual( + transformArguments({ + filter: ClientKillFilters.USER, + username: 'username' + }), + ['CLIENT', 'KILL', 'USER', 'username'] + ); + }); + + describe('SKIP_ME', () => { + it('undefined', () => { + assert.deepEqual( + transformArguments(ClientKillFilters.SKIP_ME), + ['CLIENT', 'KILL', 'SKIPME'] + ); + }); + + it('true', () => { + assert.deepEqual( + transformArguments({ + filter: ClientKillFilters.SKIP_ME, + skipMe: true + }), + ['CLIENT', 'KILL', 'SKIPME', 'yes'] + ); + }); + + it('false', () => { + assert.deepEqual( + transformArguments({ + filter: ClientKillFilters.SKIP_ME, + skipMe: false + }), + ['CLIENT', 'KILL', 'SKIPME', 'no'] + ); + }); + }); + }); +}); diff --git a/packages/client/lib/commands/CLIENT_KILL.ts b/packages/client/lib/commands/CLIENT_KILL.ts new file mode 100644 index 00000000000..adb2a5a6569 --- /dev/null +++ b/packages/client/lib/commands/CLIENT_KILL.ts @@ -0,0 +1,95 @@ +import { RedisCommandArguments } from '.'; + +export enum ClientKillFilters { + ADDRESS = 'ADDR', + LOCAL_ADDRESS = 'LADDR', + ID = 'ID', + TYPE = 'TYPE', + USER = 'USER', + SKIP_ME = 'SKIPME' +} + +interface KillFilter { + filter: T; +} + +interface KillAddress extends KillFilter { + address: `${string}:${number}`; +} + +interface KillLocalAddress extends KillFilter { + localAddress: `${string}:${number}`; +} + +interface KillId extends KillFilter { + id: number | `${number}`; +} + +interface KillType extends KillFilter { + type: 'normal' | 'master' | 'replica' | 'pubsub'; +} + +interface KillUser extends KillFilter { + username: string; +} + +type KillSkipMe = ClientKillFilters.SKIP_ME | (KillFilter & { + skipMe: boolean; +}); + +type KillFilters = KillAddress | KillLocalAddress | KillId | KillType | KillUser | KillSkipMe; + +export function transformArguments(filters: KillFilters | Array): RedisCommandArguments { + const args = ['CLIENT', 'KILL']; + + if (Array.isArray(filters)) { + for (const filter of filters) { + pushFilter(args, filter); + } + } else { + pushFilter(args, filters); + } + + return args; +} + +function pushFilter(args: RedisCommandArguments, filter: KillFilters): void { + if (filter === ClientKillFilters.SKIP_ME) { + args.push('SKIPME'); + return; + } + + args.push(filter.filter); + + switch(filter.filter) { + case ClientKillFilters.ADDRESS: + args.push(filter.address); + break; + + case ClientKillFilters.LOCAL_ADDRESS: + args.push(filter.localAddress); + break; + + case ClientKillFilters.ID: + args.push( + typeof filter.id === 'number' ? + filter.id.toString() : + filter.id + ); + break; + + case ClientKillFilters.TYPE: + args.push(filter.type); + break; + + case ClientKillFilters.USER: + args.push(filter.username); + break; + + case ClientKillFilters.SKIP_ME: + args.push(filter.skipMe ? 'yes' : 'no'); + break; + } +} + +export declare function transformReply(): number; diff --git a/packages/client/lib/commands/CLIENT_SETNAME.spec.ts b/packages/client/lib/commands/CLIENT_SETNAME.spec.ts new file mode 100644 index 00000000000..96618f3f79f --- /dev/null +++ b/packages/client/lib/commands/CLIENT_SETNAME.spec.ts @@ -0,0 +1,11 @@ +import { strict as assert } from 'assert'; +import { transformArguments } from './CLIENT_SETNAME'; + +describe('CLIENT SETNAME', () => { + it('transformArguments', () => { + assert.deepEqual( + transformArguments('name'), + ['CLIENT', 'SETNAME', 'name'] + ); + }); +}); diff --git a/packages/client/lib/commands/CLIENT_SETNAME.ts b/packages/client/lib/commands/CLIENT_SETNAME.ts new file mode 100644 index 00000000000..562fa9f2e96 --- /dev/null +++ b/packages/client/lib/commands/CLIENT_SETNAME.ts @@ -0,0 +1,7 @@ +import { RedisCommandArguments } from '.'; + +export function transformArguments(name: string): RedisCommandArguments { + return ['CLIENT', 'SETNAME', name]; +} + +export declare function transformReply(): string | null; From 72a53a37a1c6d2fca9cd4fa93e817bf73e80d918 Mon Sep 17 00:00:00 2001 From: leibale Date: Mon, 6 Dec 2021 21:59:43 -0500 Subject: [PATCH 1028/1748] update tests badge link --- README.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/README.md b/README.md index c1d08a957e4..b9ea4d52ea4 100644 --- a/README.md +++ b/README.md @@ -1,6 +1,6 @@ # Node-Redis -[![Tests](https://img.shields.io/github/workflow/status/redis/node-redis/Tests/master.svg?label=tests)](https://codecov.io/gh/redis/node-redis) +[![Tests](https://img.shields.io/github/workflow/status/redis/node-redis/Tests/master.svg?label=tests)](https://github.com/redis/node-redis/actions/workflows/tests.yml) [![Coverage](https://codecov.io/gh/redis/node-redis/branch/master/graph/badge.svg?token=xcfqHhJC37)](https://codecov.io/gh/redis/node-redis) [![License](https://img.shields.io/github/license/redis/node-redis.svg)](https://codecov.io/gh/redis/node-redis) [![Chat](https://img.shields.io/discord/697882427875393627.svg)](https://discord.gg/XMMVgxUm) From 88f55a48cdc18ce064f4a9bbdec2c45a90712a26 Mon Sep 17 00:00:00 2001 From: leibale Date: Mon, 6 Dec 2021 22:00:24 -0500 Subject: [PATCH 1029/1748] update license badge link --- README.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/README.md b/README.md index b9ea4d52ea4..1eb2e4b3c02 100644 --- a/README.md +++ b/README.md @@ -2,7 +2,7 @@ [![Tests](https://img.shields.io/github/workflow/status/redis/node-redis/Tests/master.svg?label=tests)](https://github.com/redis/node-redis/actions/workflows/tests.yml) [![Coverage](https://codecov.io/gh/redis/node-redis/branch/master/graph/badge.svg?token=xcfqHhJC37)](https://codecov.io/gh/redis/node-redis) -[![License](https://img.shields.io/github/license/redis/node-redis.svg)](https://codecov.io/gh/redis/node-redis) +[![License](https://img.shields.io/github/license/redis/node-redis.svg)](https://github.com/redis/node-redis/blob/master/LICENSE) [![Chat](https://img.shields.io/discord/697882427875393627.svg)](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). From 12173e1cd78b205ea61e54b780c201b4f64e8d38 Mon Sep 17 00:00:00 2001 From: leibale Date: Tue, 7 Dec 2021 01:31:18 -0500 Subject: [PATCH 1030/1748] ref #1765 - support lowercase command names in legacy mode --- packages/client/lib/client/index.ts | 5 ++--- packages/client/lib/client/multi-command.ts | 3 ++- 2 files changed, 4 insertions(+), 4 deletions(-) diff --git a/packages/client/lib/client/index.ts b/packages/client/lib/client/index.ts index 2bc5230580e..11a6823392a 100644 --- a/packages/client/lib/client/index.ts +++ b/packages/client/lib/client/index.ts @@ -303,9 +303,8 @@ export default class RedisClient #defineLegacyCommand(name: string): void { (this as any).#v4[name] = (this as any)[name].bind(this); - (this as any)[name] = (...args: Array): void => { - (this as any).sendCommand(name, ...args); - }; + (this as any)[name] = (this as any)[name.toLowerCase()] = + (...args: Array): void => (this as any).sendCommand(name, ...args); } duplicate(overrides?: Partial>): RedisClientType { diff --git a/packages/client/lib/client/multi-command.ts b/packages/client/lib/client/multi-command.ts index 9c19d3d0687..d45ac6ce311 100644 --- a/packages/client/lib/client/multi-command.ts +++ b/packages/client/lib/client/multi-command.ts @@ -81,7 +81,8 @@ export default class RedisClientMultiCommand { #defineLegacyCommand(name: string): void { (this as any).v4[name] = (this as any)[name].bind(this.v4); - (this as any)[name] = (...args: Array): void => (this as any).addCommand(name, args); + (this as any)[name] = (this as any)[name.toLowerCase()] = + (...args: Array): void => (this as any).addCommand(name, args); } commandsExecutor(command: RedisCommand, args: Array): this { From b68f79538c6933689493f9dbcf23e5bbdd873e3e Mon Sep 17 00:00:00 2001 From: leibale Date: Tue, 7 Dec 2021 11:01:42 -0500 Subject: [PATCH 1031/1748] update benchmarks --- benchmark/.gitignore | 1 + benchmark/lib/defaults.yml | 32 +++++++++ benchmark/lib/index.js | 24 +++++-- benchmark/lib/ping/ioredis.js | 19 ++++++ benchmark/lib/ping/ping.yml | 9 +++ benchmark/lib/ping/v3.js | 21 ++++++ benchmark/lib/ping/v4.js | 16 +++++ benchmark/lib/runner.js | 68 ++++++++++--------- benchmark/lib/set-get-delete-string/1KB.yml | 9 +++ benchmark/lib/set-get-delete-string/1MB.yml | 9 +++ benchmark/lib/set-get-delete-string/8B.yml | 9 +++ .../lib/set-get-delete-string/ioredis.js | 34 ++++++---- benchmark/lib/set-get-delete-string/local.js | 19 ------ .../lib/set-get-delete-string/production.js | 19 ------ benchmark/lib/set-get-delete-string/v3.js | 27 ++++++++ benchmark/lib/set-get-delete-string/v4.js | 20 ++++++ benchmark/package.json | 3 +- 17 files changed, 248 insertions(+), 91 deletions(-) create mode 100644 benchmark/.gitignore create mode 100644 benchmark/lib/defaults.yml create mode 100644 benchmark/lib/ping/ioredis.js create mode 100644 benchmark/lib/ping/ping.yml create mode 100644 benchmark/lib/ping/v3.js create mode 100644 benchmark/lib/ping/v4.js create mode 100644 benchmark/lib/set-get-delete-string/1KB.yml create mode 100644 benchmark/lib/set-get-delete-string/1MB.yml create mode 100644 benchmark/lib/set-get-delete-string/8B.yml delete mode 100644 benchmark/lib/set-get-delete-string/local.js delete mode 100644 benchmark/lib/set-get-delete-string/production.js create mode 100644 benchmark/lib/set-get-delete-string/v3.js create mode 100644 benchmark/lib/set-get-delete-string/v4.js diff --git a/benchmark/.gitignore b/benchmark/.gitignore new file mode 100644 index 00000000000..e493e278fc7 --- /dev/null +++ b/benchmark/.gitignore @@ -0,0 +1 @@ +*.js.json diff --git a/benchmark/lib/defaults.yml b/benchmark/lib/defaults.yml new file mode 100644 index 00000000000..3fa0f04614c --- /dev/null +++ b/benchmark/lib/defaults.yml @@ -0,0 +1,32 @@ +version: 0.1 + +remote: + - type: oss-standalone + - setup: redis-small + +setups: + - oss-standalone + +spec: + setups: + - name: oss-standalone + type: oss-standalone + redis_topology: + primaries: 1 + replicas: 0 + resources: + requests: + cpus: "1" + memory: "10g" + +exporter: + output_path: "./*.js.json" + redistimeseries: + timemetric: "$.timestamp" + metrics: + - "$.p0" + - "$.p50" + - "$.p95" + - "$.p99" + - "$.p100" + - "$.operationsPerSecond" \ No newline at end of file diff --git a/benchmark/lib/index.js b/benchmark/lib/index.js index fd811fb5928..15c8a12f401 100644 --- a/benchmark/lib/index.js +++ b/benchmark/lib/index.js @@ -4,6 +4,7 @@ import { promises as fs } from 'fs'; import { fork } from 'child_process'; import { URL, fileURLToPath } from 'url'; import { once } from 'events'; +import { extname } from 'path'; async function getPathChoices() { const dirents = await fs.readdir(new URL('.', import.meta.url), { @@ -32,11 +33,23 @@ async function getName() { } const runnerPath = fileURLToPath(new URL('runner.js', import.meta.url)), - path = new URL(`${await getName()}/`, import.meta.url), - metadata = await import(new URL('index.js', path)); + path = new URL(`${await getName()}/`, import.meta.url); + +async function getMetadata() { + try { + return await import(new URL('index.js', path)); + } catch (err) { + if (err.code === 'ERR_MODULE_NOT_FOUND') return; + + throw err; + } +} + +const metadata = await getMetadata(), + timestamp = Date.now(); for (const file of await fs.readdir(path)) { - if (file === 'index.js') continue; + if (file === 'index.js' || extname(file) !== '.js') continue; const benchmarkProcess = fork(runnerPath, [ ...argv, @@ -45,6 +58,9 @@ for (const file of await fs.readdir(path)) { ]); await once(benchmarkProcess, 'message'); - benchmarkProcess.send(metadata); + benchmarkProcess.send({ + metadata, + timestamp + }); await once(benchmarkProcess, 'close'); } diff --git a/benchmark/lib/ping/ioredis.js b/benchmark/lib/ping/ioredis.js new file mode 100644 index 00000000000..5ed0d1dd76b --- /dev/null +++ b/benchmark/lib/ping/ioredis.js @@ -0,0 +1,19 @@ +import Redis from 'ioredis'; + +export default async (host) => { + const client = new Redis({ + host, + lazyConnect: true + }); + + await client.connect(); + + return { + benchmark() { + return client.ping(); + }, + teardown() { + return client.disconnect(); + } + } +}; diff --git a/benchmark/lib/ping/ping.yml b/benchmark/lib/ping/ping.yml new file mode 100644 index 00000000000..cfe3c74735a --- /dev/null +++ b/benchmark/lib/ping/ping.yml @@ -0,0 +1,9 @@ +name: "ping" + +clientconfig: + - command: | + npm install -ws + npm run build:tests-tools + cd benchmark + npm install + npm run start -- --name ping --redis-server-host ${server_private_ip} diff --git a/benchmark/lib/ping/v3.js b/benchmark/lib/ping/v3.js new file mode 100644 index 00000000000..26f269a42cf --- /dev/null +++ b/benchmark/lib/ping/v3.js @@ -0,0 +1,21 @@ +import { createClient } from 'redis-v3'; +import { once } from 'events'; +import { promisify } from 'util'; + +export default async (host) => { + const client = createClient({ host }), + pingAsync = promisify(client.ping).bind(client), + quitAsync = promisify(client.quit).bind(client); + + await once(client, 'connect'); + + return { + benchmark() { + return pingAsync(); + }, + teardown() { + return quitAsync(); + } + }; + +}; diff --git a/benchmark/lib/ping/v4.js b/benchmark/lib/ping/v4.js new file mode 100644 index 00000000000..9f122c3da83 --- /dev/null +++ b/benchmark/lib/ping/v4.js @@ -0,0 +1,16 @@ +import { createClient } from '@node-redis/client'; + +export default async (host) => { + const client = createClient({ host }); + + await client.connect(); + + return { + benchmark() { + return client.ping(); + }, + teardown() { + return client.disconnect(); + } + }; +}; diff --git a/benchmark/lib/runner.js b/benchmark/lib/runner.js index 9a38bb8b862..a96ff55cab0 100644 --- a/benchmark/lib/runner.js +++ b/benchmark/lib/runner.js @@ -1,10 +1,11 @@ import yargs from 'yargs'; import { hideBin } from 'yargs/helpers'; import { basename } from 'path'; +import { promises as fs } from 'fs'; import * as hdr from 'hdr-histogram-js'; hdr.initWebAssemblySync(); -const { path, times, concurrency } = yargs(hideBin(process.argv)) +const { path, times, concurrency, 'redis-server-host': host } = yargs(hideBin(process.argv)) .option('path', { type: 'string', demandOption: true @@ -19,25 +20,19 @@ const { path, times, concurrency } = yargs(hideBin(process.argv)) default: 100, demandOption: true }) + .option('redis-server-host', { + type: 'string' + }) .parseSync(); -async function setup() { - const module = await import(path); - await module.setup(); - return module; -} - -function getMetadata() { - return new Promise(resolve => { - process.once('message', resolve); - process.send('ready'); - }); -} - -const [ { benchmark, teardown }, metadata ] = await Promise.all([ - setup(), - getMetadata() -]); +const [ { metadata, timestamp }, module ] = await Promise.all([ + new Promise(resolve => { + process.once('message', resolve); + process.send('ready'); + }), + import(path) + ]), + { benchmark, teardown } = await module.default(host, metadata); async function run(times) { return new Promise(resolve => { @@ -45,23 +40,20 @@ async function run(times) { let num = 0, inProgress = 0; - function run() { + async function run() { ++inProgress; ++num; const start = process.hrtime.bigint(); - benchmark(metadata) - .catch(err => console.error(err)) - .finally(() => { - histogram.recordValue(Number(process.hrtime.bigint() - start)); - --inProgress; + await benchmark(metadata); + histogram.recordValue(Number(process.hrtime.bigint() - start)); + --inProgress; - if (num < times) { - run(); - } else if (inProgress === 0) { - resolve(histogram); - } - }); + if (num < times) { + run(); + } else if (inProgress === 0) { + resolve(histogram); + } } const toInitiate = Math.min(concurrency, times); @@ -75,8 +67,20 @@ async function run(times) { await run(Math.min(times * 0.1, 10_000)); // benchmark -const histogram = await run(times); +const benchmarkStart = process.hrtime.bigint(), + histogram = await run(times), + benchmarkNanoseconds = process.hrtime.bigint() - benchmarkStart, + json = { + timestamp, + operationsPerSecond: times / Number(benchmarkNanoseconds) * 1_000_000_000, + p0: histogram.getValueAtPercentile(0), + p50: histogram.getValueAtPercentile(50), + p95: histogram.getValueAtPercentile(95), + p99: histogram.getValueAtPercentile(99), + p100: histogram.getValueAtPercentile(100) + }; console.log(`[${basename(path)}]:`); -console.table(histogram.toJSON()); +console.table(json); +await fs.writeFile(`${path}.json`, JSON.stringify(json)); await teardown(); diff --git a/benchmark/lib/set-get-delete-string/1KB.yml b/benchmark/lib/set-get-delete-string/1KB.yml new file mode 100644 index 00000000000..52fb611a24b --- /dev/null +++ b/benchmark/lib/set-get-delete-string/1KB.yml @@ -0,0 +1,9 @@ +name: "set-get-delete-string-1KB" + +clientconfig: + - command: | + npm install -ws + npm run build:tests-tools + cd benchmark + npm install + npm run start -- --name set-get-delete-string --size 1024 --redis-server-host ${server_private_ip} diff --git a/benchmark/lib/set-get-delete-string/1MB.yml b/benchmark/lib/set-get-delete-string/1MB.yml new file mode 100644 index 00000000000..f16d7c3696a --- /dev/null +++ b/benchmark/lib/set-get-delete-string/1MB.yml @@ -0,0 +1,9 @@ +name: "set-get-delete-string-1MB" + +clientconfig: + - command: | + npm install -ws + npm run build:tests-tools + cd benchmark + npm install + npm run start -- --name set-get-delete-string --size 1048576 --redis-server-host ${server_private_ip} diff --git a/benchmark/lib/set-get-delete-string/8B.yml b/benchmark/lib/set-get-delete-string/8B.yml new file mode 100644 index 00000000000..f625f74fca1 --- /dev/null +++ b/benchmark/lib/set-get-delete-string/8B.yml @@ -0,0 +1,9 @@ +name: "set-get-delete-string-8B" + +clientconfig: + - command: | + npm install -ws + npm run build:tests-tools + cd benchmark + npm install + npm run start -- --name set-get-delete-string --size 8 --redis-server-host ${server_private_ip} diff --git a/benchmark/lib/set-get-delete-string/ioredis.js b/benchmark/lib/set-get-delete-string/ioredis.js index a6cd5ccb185..95456233001 100644 --- a/benchmark/lib/set-get-delete-string/ioredis.js +++ b/benchmark/lib/set-get-delete-string/ioredis.js @@ -1,19 +1,23 @@ import Redis from 'ioredis'; -const client = new Redis({ lazyConnect: true }); +export default async (host, { randomString }) => { + const client = new Redis({ + host, + lazyConnect: true + }); -export function setup() { - return client.connect(); -} + await client.connect(); -export function benchmark({ randomString }) { - return Promise.all([ - client.set(randomString, randomString), - client.get(randomString), - client.del(randomString) - ]); -} - -export function teardown() { - return client.disconnect(); -} + return { + benchmark() { + return Promise.all([ + client.set(randomString, randomString), + client.get(randomString), + client.del(randomString) + ]); + }, + teardown() { + return client.disconnect(); + } + } +}; diff --git a/benchmark/lib/set-get-delete-string/local.js b/benchmark/lib/set-get-delete-string/local.js deleted file mode 100644 index ed576122692..00000000000 --- a/benchmark/lib/set-get-delete-string/local.js +++ /dev/null @@ -1,19 +0,0 @@ -import { createClient } from '@node-redis/client-local'; - -const client = createClient(); - -export function setup() { - return client.connect(); -} - -export function benchmark({ randomString }) { - return Promise.all([ - client.set(randomString, randomString), - client.get(randomString), - client.del(randomString) - ]); -} - -export function teardown() { - return client.disconnect(); -} diff --git a/benchmark/lib/set-get-delete-string/production.js b/benchmark/lib/set-get-delete-string/production.js deleted file mode 100644 index 941291900f4..00000000000 --- a/benchmark/lib/set-get-delete-string/production.js +++ /dev/null @@ -1,19 +0,0 @@ -import { createClient } from '@node-redis/client-production'; - -const client = createClient(); - -export function setup() { - return client.connect(); -} - -export function benchmark({ randomString }) { - return Promise.all([ - client.set(randomString, randomString), - client.get(randomString), - client.del(randomString) - ]); -} - -export function teardown() { - return client.disconnect(); -} diff --git a/benchmark/lib/set-get-delete-string/v3.js b/benchmark/lib/set-get-delete-string/v3.js new file mode 100644 index 00000000000..27ff6702a51 --- /dev/null +++ b/benchmark/lib/set-get-delete-string/v3.js @@ -0,0 +1,27 @@ +import { createClient } from 'redis-v3'; +import { once } from 'events'; +import { promisify } from 'util'; + +export default async (host, { randomString }) => { + const client = createClient({ host }), + setAsync = promisify(client.set).bind(client), + getAsync = promisify(client.get).bind(client), + delAsync = promisify(client.del).bind(client), + quitAsync = promisify(client.quit).bind(client); + + await once(client, 'connect'); + + return { + benchmark() { + return Promise.all([ + setAsync(randomString, randomString), + getAsync(randomString), + delAsync(randomString) + ]); + }, + teardown() { + return quitAsync(); + } + }; + +}; diff --git a/benchmark/lib/set-get-delete-string/v4.js b/benchmark/lib/set-get-delete-string/v4.js new file mode 100644 index 00000000000..8a08f63252e --- /dev/null +++ b/benchmark/lib/set-get-delete-string/v4.js @@ -0,0 +1,20 @@ +import { createClient } from '@node-redis/client'; + +export default async (host, { randomString }) => { + const client = createClient({ host }); + + await client.connect(); + + return { + benchmark() { + return Promise.all([ + client.set(randomString, randomString), + client.get(randomString), + client.del(randomString) + ]); + }, + teardown() { + return client.disconnect(); + } + }; +}; diff --git a/benchmark/package.json b/benchmark/package.json index a4752b88335..114b9e82d33 100644 --- a/benchmark/package.json +++ b/benchmark/package.json @@ -7,8 +7,7 @@ "start": "node ." }, "dependencies": { - "@node-redis/client-local": "../packages/client", - "@node-redis/client-production": "npm:@node-redis/client@1.0.0", + "@node-redis/client": "../packages/client", "hdr-histogram-js": "2.0.1", "ioredis": "4.28.1", "redis-v3": "npm:redis@3.1.2", From 73f6e6b86c51680495c45b5c5a85390d1c8c94bd Mon Sep 17 00:00:00 2001 From: leibale Date: Tue, 7 Dec 2021 11:01:54 -0500 Subject: [PATCH 1032/1748] fix LINSERT test title --- packages/client/lib/commands/LINSERT.spec.ts | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/packages/client/lib/commands/LINSERT.spec.ts b/packages/client/lib/commands/LINSERT.spec.ts index 6cc429d6a2c..6454cc48536 100644 --- a/packages/client/lib/commands/LINSERT.spec.ts +++ b/packages/client/lib/commands/LINSERT.spec.ts @@ -17,7 +17,7 @@ describe('LINSERT', () => { ); }, GLOBAL.SERVERS.OPEN); - testUtils.testWithCluster('cluster.lLen', async cluster => { + testUtils.testWithCluster('cluster.lInsert', async cluster => { assert.equal( await cluster.lInsert('key', 'BEFORE', 'pivot', 'element'), 0 From ccf06620620992cad0b5c91309a273d9c74875f4 Mon Sep 17 00:00:00 2001 From: leibale Date: Tue, 7 Dec 2021 11:07:31 -0500 Subject: [PATCH 1033/1748] fix v4 benchmarks --- benchmark/lib/ping/v4.js | 6 +++++- benchmark/lib/set-get-delete-string/v4.js | 6 +++++- 2 files changed, 10 insertions(+), 2 deletions(-) diff --git a/benchmark/lib/ping/v4.js b/benchmark/lib/ping/v4.js index 9f122c3da83..fe778bc1280 100644 --- a/benchmark/lib/ping/v4.js +++ b/benchmark/lib/ping/v4.js @@ -1,7 +1,11 @@ import { createClient } from '@node-redis/client'; export default async (host) => { - const client = createClient({ host }); + const client = createClient({ + socket: { + host + } + }); await client.connect(); diff --git a/benchmark/lib/set-get-delete-string/v4.js b/benchmark/lib/set-get-delete-string/v4.js index 8a08f63252e..f14a69c6868 100644 --- a/benchmark/lib/set-get-delete-string/v4.js +++ b/benchmark/lib/set-get-delete-string/v4.js @@ -1,7 +1,11 @@ import { createClient } from '@node-redis/client'; export default async (host, { randomString }) => { - const client = createClient({ host }); + const client = createClient({ + socket: { + host + } + }); await client.connect(); From e63ebf67927691bffe25c0f34989a6038c9b252f Mon Sep 17 00:00:00 2001 From: leibale Date: Tue, 7 Dec 2021 11:10:57 -0500 Subject: [PATCH 1034/1748] fix discord link --- README.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/README.md b/README.md index 1eb2e4b3c02..20cc6f55be8 100644 --- a/README.md +++ b/README.md @@ -3,7 +3,7 @@ [![Tests](https://img.shields.io/github/workflow/status/redis/node-redis/Tests/master.svg?label=tests)](https://github.com/redis/node-redis/actions/workflows/tests.yml) [![Coverage](https://codecov.io/gh/redis/node-redis/branch/master/graph/badge.svg?token=xcfqHhJC37)](https://codecov.io/gh/redis/node-redis) [![License](https://img.shields.io/github/license/redis/node-redis.svg)](https://github.com/redis/node-redis/blob/master/LICENSE) -[![Chat](https://img.shields.io/discord/697882427875393627.svg)](https://discord.gg/XMMVgxUm) +[![Chat](https://img.shields.io/discord/697882427875393627.svg)](https://discord.gg/redis) 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). From e11256854e8983db6470377c4a6056ff801ca066 Mon Sep 17 00:00:00 2001 From: leibale Date: Wed, 8 Dec 2021 10:27:29 -0500 Subject: [PATCH 1035/1748] fix #1771 - fix PubSub resubscribe --- packages/client/lib/client/socket.ts | 1 - 1 file changed, 1 deletion(-) diff --git a/packages/client/lib/client/socket.ts b/packages/client/lib/client/socket.ts index 8267e7a07b8..36837bcdb88 100644 --- a/packages/client/lib/client/socket.ts +++ b/packages/client/lib/client/socket.ts @@ -110,7 +110,6 @@ export default class RedisSocket extends EventEmitter { } catch (err) { this.#socket.destroy(); this.#socket = undefined; - this.#isOpen = false; throw err; } From ac808ea781c4cdcc13e4e7a68777532d646b56c9 Mon Sep 17 00:00:00 2001 From: leibale Date: Wed, 8 Dec 2021 10:28:34 -0500 Subject: [PATCH 1036/1748] fix #1766 - allow .quit() in PubSub mode --- packages/client/lib/client/commands-queue.ts | 9 ++++++++- packages/client/lib/client/index.spec.ts | 10 ++++++++++ packages/client/lib/client/index.ts | 4 +++- 3 files changed, 21 insertions(+), 2 deletions(-) diff --git a/packages/client/lib/client/commands-queue.ts b/packages/client/lib/client/commands-queue.ts index efa80820904..52f86c6375e 100644 --- a/packages/client/lib/client/commands-queue.ts +++ b/packages/client/lib/client/commands-queue.ts @@ -1,15 +1,19 @@ 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; + ignorePubSubMode?: boolean; } + interface CommandWaitingToBeSent extends CommandWaitingForReply { args: RedisCommandArguments; chainId?: symbol; @@ -18,16 +22,19 @@ 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' @@ -135,7 +142,7 @@ export default class RedisCommandsQueue { } addCommand(args: RedisCommandArguments, options?: QueueCommandOptions, bufferMode?: boolean): Promise { - if (this.#pubSubState) { + if (this.#pubSubState && !options?.ignorePubSubMode) { 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')); diff --git a/packages/client/lib/client/index.spec.ts b/packages/client/lib/client/index.spec.ts index 01154e9dd1a..fbe38adafc5 100644 --- a/packages/client/lib/client/index.spec.ts +++ b/packages/client/lib/client/index.spec.ts @@ -668,6 +668,16 @@ describe('Client', () => { await subscriber.disconnect(); } }, GLOBAL.SERVERS.OPEN); + + testUtils.testWithClient('should be able to quit in PubSub mode', async client => { + await client.subscribe('channel', () => { + // noop + }); + + await assert.doesNotReject(client.quit()); + + assert.equal(client.isOpen, false); + }, GLOBAL.SERVERS.OPEN); }); testUtils.testWithClient('ConnectionTimeoutError', async client => { diff --git a/packages/client/lib/client/index.ts b/packages/client/lib/client/index.ts index 11a6823392a..d3655b0341b 100644 --- a/packages/client/lib/client/index.ts +++ b/packages/client/lib/client/index.ts @@ -487,7 +487,9 @@ export default class RedisClient QUIT(): Promise { return this.#socket.quit(() => { - const quitPromise = this.#queue.addCommand(['QUIT']); + const quitPromise = this.#queue.addCommand(['QUIT'], { + ignorePubSubMode: true + }); this.#tick(); return Promise.all([ quitPromise, From b70aa4e470a8b3dbad731d677c6351bac6b8273b Mon Sep 17 00:00:00 2001 From: leibale Date: Thu, 9 Dec 2021 02:48:10 -0500 Subject: [PATCH 1037/1748] fix for e11256854e8983db6470377c4a6056ff801ca066 - should not retry connecting if failed due to wrong auth --- packages/client/lib/client/index.ts | 6 ++++-- packages/client/lib/client/socket.ts | 7 ++++++- packages/client/lib/errors.ts | 6 ++++++ 3 files changed, 16 insertions(+), 3 deletions(-) diff --git a/packages/client/lib/client/index.ts b/packages/client/lib/client/index.ts index d3655b0341b..3bbf14a116c 100644 --- a/packages/client/lib/client/index.ts +++ b/packages/client/lib/client/index.ts @@ -11,7 +11,7 @@ import { ScanCommandOptions } from '../commands/SCAN'; import { HScanTuple } from '../commands/HSCAN'; import { extendWithCommands, extendWithModulesAndScripts, LegacyCommandArguments, transformCommandArguments, transformCommandReply, transformLegacyCommandArguments } from '../commander'; import { Pool, Options as PoolOptions, createPool } from 'generic-pool'; -import { ClientClosedError, DisconnectsClientError } from '../errors'; +import { ClientClosedError, DisconnectsClientError, AuthError } from '../errors'; import { URL } from 'url'; import { TcpSocketConnectOpts } from 'net'; @@ -219,7 +219,9 @@ export default class RedisClient password: this.#options.password ?? '' }), { asap: true } - ) + ).catch(err => { + throw new AuthError(err.message); + }) ); } diff --git a/packages/client/lib/client/socket.ts b/packages/client/lib/client/socket.ts index 36837bcdb88..effb780a081 100644 --- a/packages/client/lib/client/socket.ts +++ b/packages/client/lib/client/socket.ts @@ -3,7 +3,7 @@ import * as net from 'net'; import * as tls from 'tls'; import { encodeCommand } from '../commander'; import { RedisCommandArguments } from '../commands'; -import { ConnectionTimeoutError, ClientClosedError, SocketClosedUnexpectedlyError } from '../errors'; +import { ConnectionTimeoutError, ClientClosedError, SocketClosedUnexpectedlyError, AuthError } from '../errors'; import { promiseTimeout } from '../utils'; export interface RedisSocketCommonOptions { @@ -110,6 +110,11 @@ export default class RedisSocket extends EventEmitter { } catch (err) { this.#socket.destroy(); this.#socket = undefined; + + if (err instanceof AuthError) { + this.#isOpen = false; + } + throw err; } diff --git a/packages/client/lib/errors.ts b/packages/client/lib/errors.ts index 79a438aa1d8..6af7d70744c 100644 --- a/packages/client/lib/errors.ts +++ b/packages/client/lib/errors.ts @@ -33,3 +33,9 @@ export class SocketClosedUnexpectedlyError extends Error { super('Socket closed unexpectedly'); } } + +export class AuthError extends Error { + constructor(message: string) { + super(message); + } +} From 0b2a8d7c3c75f504ecde80bc0e2c64e57a2bff0f Mon Sep 17 00:00:00 2001 From: Guy Korland Date: Thu, 9 Dec 2021 09:57:20 +0200 Subject: [PATCH 1038/1748] Add LGTM (#1773) * Update README.md * Update README.md Co-authored-by: Leibale Eidelman --- README.md | 1 + 1 file changed, 1 insertion(+) diff --git a/README.md b/README.md index 20cc6f55be8..28add55210b 100644 --- a/README.md +++ b/README.md @@ -3,6 +3,7 @@ [![Tests](https://img.shields.io/github/workflow/status/redis/node-redis/Tests/master.svg?label=tests)](https://github.com/redis/node-redis/actions/workflows/tests.yml) [![Coverage](https://codecov.io/gh/redis/node-redis/branch/master/graph/badge.svg?token=xcfqHhJC37)](https://codecov.io/gh/redis/node-redis) [![License](https://img.shields.io/github/license/redis/node-redis.svg)](https://github.com/redis/node-redis/blob/master/LICENSE) +[![LGTM alerts](https://img.shields.io/lgtm/alerts/g/redis/node-redis.svg?logo=LGTM)](https://lgtm.com/projects/g/redis/node-redis/alerts) [![Chat](https://img.shields.io/discord/697882427875393627.svg)](https://discord.gg/redis) 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). From bb75b06d6721fb7cd85b7ef99edf947ef8ace90f Mon Sep 17 00:00:00 2001 From: Leibale Eidelman Date: Sat, 11 Dec 2021 08:33:46 -0500 Subject: [PATCH 1039/1748] fix #1734 - fix PubSub unsubscribe race condition --- packages/client/lib/client/commands-queue.ts | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) diff --git a/packages/client/lib/client/commands-queue.ts b/packages/client/lib/client/commands-queue.ts index 52f86c6375e..2ce48100ec7 100644 --- a/packages/client/lib/client/commands-queue.ts +++ b/packages/client/lib/client/commands-queue.ts @@ -73,7 +73,10 @@ export default class RedisCommandsQueue { static #emitPubSubMessage(listenersMap: PubSubListenersMap, message: Buffer, channel: Buffer, pattern?: Buffer): void { const keyString = (pattern || channel).toString(), - listeners = listenersMap.get(keyString)!; + listeners = listenersMap.get(keyString); + + if (!listeners) return; + for (const listener of listeners.buffers) { listener(message, channel); } From 7110f23369fe9c1f8b445dcc0c8393384263cf37 Mon Sep 17 00:00:00 2001 From: Avital Fine <79420960+AvitalFineRedis@users.noreply.github.com> Date: Sun, 12 Dec 2021 14:41:44 +0100 Subject: [PATCH 1040/1748] Support RedisTimeSeries (#1757) * Implement missing commands and add test * Update DECRBY.spec.ts * Small changes * clean code * Update MGET_WITHLABELS.ts Use map in transformReply Co-authored-by: leibale --- .../time-series/lib/commands/ALTER.spec.ts | 2 +- .../time-series/lib/commands/DECRBY.spec.ts | 81 ++++++++ packages/time-series/lib/commands/DEL.spec.ts | 21 +++ .../lib/commands/DELETERULE.spec.ts | 26 +++ .../time-series/lib/commands/DELETERULE.ts | 4 +- packages/time-series/lib/commands/GET.spec.ts | 35 ++++ .../time-series/lib/commands/INCRBY.spec.ts | 91 +++++++++ .../time-series/lib/commands/INFO.spec.ts | 50 +++++ .../lib/commands/INFO_DEBUG.spec.ts | 57 ++++++ .../time-series/lib/commands/MADD.spec.ts | 39 ++++ packages/time-series/lib/commands/MADD.ts | 2 + .../time-series/lib/commands/MGET.spec.ts | 29 +++ packages/time-series/lib/commands/MGET.ts | 39 ++-- .../lib/commands/MGET_WITHLABELS.spec.ts | 39 ++++ .../lib/commands/MGET_WITHLABELS.ts | 41 +++++ .../time-series/lib/commands/MRANGE.spec.ts | 50 +++++ packages/time-series/lib/commands/MRANGE.ts | 21 +++ .../lib/commands/MRANGE_WITHLABELS.spec.ts | 52 ++++++ .../lib/commands/MRANGE_WITHLABELS.ts | 21 +++ .../lib/commands/MREVRANGE.spec.ts | 50 +++++ .../time-series/lib/commands/MREVRANGE.ts | 21 +++ .../lib/commands/MREVRANGE_WITHLABELS.spec.ts | 52 ++++++ .../lib/commands/MREVRANGE_WITHLABELS.ts | 21 +++ .../lib/commands/QUERYINDEX.spec.ts | 27 +++ .../time-series/lib/commands/RANGE.spec.ts | 38 ++++ packages/time-series/lib/commands/RANGE.ts | 9 +- .../time-series/lib/commands/REVRANGE.spec.ts | 106 +++++++++++ packages/time-series/lib/commands/REVRANGE.ts | 9 +- .../time-series/lib/commands/index.spec.ts | 148 ++++++++++++++- packages/time-series/lib/commands/index.ts | 174 ++++++++++++++++-- 30 files changed, 1309 insertions(+), 46 deletions(-) create mode 100644 packages/time-series/lib/commands/DECRBY.spec.ts create mode 100644 packages/time-series/lib/commands/DEL.spec.ts create mode 100644 packages/time-series/lib/commands/DELETERULE.spec.ts create mode 100644 packages/time-series/lib/commands/GET.spec.ts create mode 100644 packages/time-series/lib/commands/INCRBY.spec.ts create mode 100644 packages/time-series/lib/commands/INFO.spec.ts create mode 100644 packages/time-series/lib/commands/INFO_DEBUG.spec.ts create mode 100644 packages/time-series/lib/commands/MADD.spec.ts create mode 100644 packages/time-series/lib/commands/MGET.spec.ts create mode 100644 packages/time-series/lib/commands/MGET_WITHLABELS.spec.ts create mode 100644 packages/time-series/lib/commands/MGET_WITHLABELS.ts create mode 100644 packages/time-series/lib/commands/MRANGE.spec.ts create mode 100644 packages/time-series/lib/commands/MRANGE.ts create mode 100644 packages/time-series/lib/commands/MRANGE_WITHLABELS.spec.ts create mode 100644 packages/time-series/lib/commands/MRANGE_WITHLABELS.ts create mode 100644 packages/time-series/lib/commands/MREVRANGE.spec.ts create mode 100644 packages/time-series/lib/commands/MREVRANGE.ts create mode 100644 packages/time-series/lib/commands/MREVRANGE_WITHLABELS.spec.ts create mode 100644 packages/time-series/lib/commands/MREVRANGE_WITHLABELS.ts create mode 100644 packages/time-series/lib/commands/QUERYINDEX.spec.ts create mode 100644 packages/time-series/lib/commands/RANGE.spec.ts create mode 100644 packages/time-series/lib/commands/REVRANGE.spec.ts diff --git a/packages/time-series/lib/commands/ALTER.spec.ts b/packages/time-series/lib/commands/ALTER.spec.ts index 868d4a9c64d..989a0365353 100644 --- a/packages/time-series/lib/commands/ALTER.spec.ts +++ b/packages/time-series/lib/commands/ALTER.spec.ts @@ -41,7 +41,7 @@ describe('ALTER', () => { }); testUtils.testWithClient('client.ts.alter', async client => { - await client.ts.create('key'); + await client.ts.create('key'); assert.equal( await client.ts.alter('key', { RETENTION: 1 }), diff --git a/packages/time-series/lib/commands/DECRBY.spec.ts b/packages/time-series/lib/commands/DECRBY.spec.ts new file mode 100644 index 00000000000..345e651404b --- /dev/null +++ b/packages/time-series/lib/commands/DECRBY.spec.ts @@ -0,0 +1,81 @@ +import { strict as assert } from 'assert'; +import testUtils, { GLOBAL } from '../test-utils'; +import { transformArguments } from './DECRBY'; + +describe('DECRBY', () => { + describe('transformArguments', () => { + it('without options', () => { + assert.deepEqual( + transformArguments('key', 1), + ['TS.DECRBY', 'key', '1'] + ); + }); + + it('with TIMESTAMP', () => { + assert.deepEqual( + transformArguments('key', 1, { + TIMESTAMP: '*' + }), + ['TS.DECRBY', 'key', '1', 'TIMESTAMP', '*'] + ); + }); + + it('with RETENTION', () => { + assert.deepEqual( + transformArguments('key', 1, { + RETENTION: 1 + }), + ['TS.DECRBY', 'key', '1', 'RETENTION', '1'] + ); + }); + + it('with UNCOMPRESSED', () => { + assert.deepEqual( + transformArguments('key', 1, { + UNCOMPRESSED: true + }), + ['TS.DECRBY', 'key', '1', 'UNCOMPRESSED'] + ); + }); + + it('with CHUNK_SIZE', () => { + assert.deepEqual( + transformArguments('key', 1, { + CHUNK_SIZE: 100 + }), + ['TS.DECRBY', 'key', '1', 'CHUNK_SIZE', '100'] + ); + }); + + it('with LABELS', () => { + assert.deepEqual( + transformArguments('key', 1, { + LABELS: { label: 'value' } + }), + ['TS.DECRBY', 'key', '1', 'LABELS', 'label', 'value'] + ); + }); + + it('with TIMESTAMP, RETENTION, UNCOMPRESSED, CHUNK_SIZE and LABELS', () => { + assert.deepEqual( + transformArguments('key', 1, { + TIMESTAMP: '*', + RETENTION: 1, + UNCOMPRESSED: true, + CHUNK_SIZE: 2, + LABELS: { label: 'value' } + }), + ['TS.DECRBY', 'key', '1', 'TIMESTAMP', '*', 'RETENTION', '1', 'UNCOMPRESSED', 'CHUNK_SIZE', '2', 'LABELS', 'label', 'value'] + ); + }); + }); + + testUtils.testWithClient('client.ts.decrBy', async client => { + assert.equal( + await client.ts.decrBy('key', 1, { + TIMESTAMP: 0 + }), + 0 + ); + }, GLOBAL.SERVERS.OPEN); +}); diff --git a/packages/time-series/lib/commands/DEL.spec.ts b/packages/time-series/lib/commands/DEL.spec.ts new file mode 100644 index 00000000000..0fc4b465807 --- /dev/null +++ b/packages/time-series/lib/commands/DEL.spec.ts @@ -0,0 +1,21 @@ +import { strict as assert } from 'assert'; +import testUtils, { GLOBAL } from '../test-utils'; +import { transformArguments } from './DEL'; + +describe('DEL', () => { + it('transformArguments', () => { + assert.deepEqual( + transformArguments('key', '-', '+'), + ['TS.DEL', 'key', '-', '+'] + ); + }); + + testUtils.testWithClient('client.ts.del', async client => { + await client.ts.create('key'); + + assert.equal( + await client.ts.del('key', '-', '+'), + 0 + ); + }, GLOBAL.SERVERS.OPEN); +}); diff --git a/packages/time-series/lib/commands/DELETERULE.spec.ts b/packages/time-series/lib/commands/DELETERULE.spec.ts new file mode 100644 index 00000000000..5cbcc8edb54 --- /dev/null +++ b/packages/time-series/lib/commands/DELETERULE.spec.ts @@ -0,0 +1,26 @@ +import { strict as assert } from 'assert'; +import { TimeSeriesAggregationType } from '.'; +import testUtils, { GLOBAL } from '../test-utils'; +import { transformArguments } from './DELETERULE'; + +describe('DELETERULE', () => { + it('transformArguments', () => { + assert.deepEqual( + transformArguments('source', 'destination'), + ['TS.DELETERULE', 'source', 'destination'] + ); + }); + + testUtils.testWithClient('client.ts.deleteRule', async client => { + await Promise.all([ + client.ts.create('source'), + client.ts.create('destination'), + client.ts.createRule('source', 'destination', TimeSeriesAggregationType.AVARAGE, 1) + ]); + + assert.equal( + await client.ts.deleteRule('source', 'destination'), + 'OK' + ); + }, GLOBAL.SERVERS.OPEN); +}); diff --git a/packages/time-series/lib/commands/DELETERULE.ts b/packages/time-series/lib/commands/DELETERULE.ts index b9ef7574c86..fff00a906a1 100644 --- a/packages/time-series/lib/commands/DELETERULE.ts +++ b/packages/time-series/lib/commands/DELETERULE.ts @@ -1,4 +1,6 @@ -export function transformArguments(sourceKey: string,destinationKey: string,): Array { +export const FIRST_KEY_INDEX = 1; + +export function transformArguments(sourceKey: string, destinationKey: string): Array { return [ 'TS.DELETERULE', sourceKey, diff --git a/packages/time-series/lib/commands/GET.spec.ts b/packages/time-series/lib/commands/GET.spec.ts new file mode 100644 index 00000000000..0c0113f35d7 --- /dev/null +++ b/packages/time-series/lib/commands/GET.spec.ts @@ -0,0 +1,35 @@ +import { strict as assert } from 'assert'; +import testUtils, { GLOBAL } from '../test-utils'; +import { transformArguments } from './GET'; + +describe('GET', () => { + it('transformArguments', () => { + assert.deepEqual( + transformArguments('key'), + ['TS.GET', 'key'] + ); + }); + + describe('client.ts.get', () => { + testUtils.testWithClient('null', async client => { + await client.ts.create('key'); + + assert.equal( + await client.ts.get('key'), + null + ); + }, GLOBAL.SERVERS.OPEN); + + testUtils.testWithClient('with samples', async client => { + await client.ts.add('key', 0, 1); + + assert.deepEqual( + await client.ts.get('key'), + { + timestamp: 0, + value: 1 + } + ); + }, GLOBAL.SERVERS.OPEN); + }); +}); diff --git a/packages/time-series/lib/commands/INCRBY.spec.ts b/packages/time-series/lib/commands/INCRBY.spec.ts new file mode 100644 index 00000000000..acaa4cd3329 --- /dev/null +++ b/packages/time-series/lib/commands/INCRBY.spec.ts @@ -0,0 +1,91 @@ +import { strict as assert } from 'assert'; +import testUtils, { GLOBAL } from '../test-utils'; +import { transformArguments } from './INCRBY'; + +describe('INCRBY', () => { + describe('transformArguments', () => { + it('without options', () => { + assert.deepEqual( + transformArguments('key', 1), + ['TS.INCRBY', 'key', '1'] + ); + }); + + it('with TIMESTAMP', () => { + assert.deepEqual( + transformArguments('key', 1, { + TIMESTAMP: '*' + }), + ['TS.INCRBY', 'key', '1', 'TIMESTAMP', '*'] + ); + }); + + it('with RETENTION', () => { + assert.deepEqual( + transformArguments('key', 1, { + RETENTION: 1 + }), + ['TS.INCRBY', 'key', '1', 'RETENTION', '1'] + ); + }); + + it('with UNCOMPRESSED', () => { + assert.deepEqual( + transformArguments('key', 1, { + UNCOMPRESSED: true + }), + ['TS.INCRBY', 'key', '1', 'UNCOMPRESSED'] + ); + }); + + it('without UNCOMPRESSED', () => { + assert.deepEqual( + transformArguments('key', 1, { + UNCOMPRESSED: false + }), + ['TS.INCRBY', 'key', '1'] + ); + }); + + it('with CHUNK_SIZE', () => { + assert.deepEqual( + transformArguments('key', 1, { + CHUNK_SIZE: 1 + }), + ['TS.INCRBY', 'key', '1', 'CHUNK_SIZE', '1'] + ); + }); + + it('with LABELS', () => { + assert.deepEqual( + transformArguments('key', 1, { + LABELS: { label: 'value' } + }), + ['TS.INCRBY', 'key', '1', 'LABELS', 'label', 'value'] + ); + }); + + it('with TIMESTAMP, RETENTION, UNCOMPRESSED, CHUNK_SIZE and LABELS', () => { + assert.deepEqual( + transformArguments('key', 1, { + TIMESTAMP: '*', + RETENTION: 1, + UNCOMPRESSED: true, + CHUNK_SIZE: 1, + LABELS: { label: 'value' } + }), + ['TS.INCRBY', 'key', '1', 'TIMESTAMP', '*', 'RETENTION', '1', 'UNCOMPRESSED', + 'CHUNK_SIZE', '1', 'LABELS', 'label', 'value'] + ); + }); + }); + + testUtils.testWithClient('client.ts.incrBy', async client => { + assert.equal( + await client.ts.incrBy('key', 1, { + TIMESTAMP: 0 + }), + 0 + ); + }, GLOBAL.SERVERS.OPEN); +}); diff --git a/packages/time-series/lib/commands/INFO.spec.ts b/packages/time-series/lib/commands/INFO.spec.ts new file mode 100644 index 00000000000..83bad872c68 --- /dev/null +++ b/packages/time-series/lib/commands/INFO.spec.ts @@ -0,0 +1,50 @@ +import { strict as assert } from 'assert'; +import { TimeSeriesAggregationType, TimeSeriesDuplicatePolicies } from '.'; +import testUtils, { GLOBAL } from '../test-utils'; +import { transformArguments } from './INFO'; + +describe('INFO', () => { + it('transformArguments', () => { + assert.deepEqual( + transformArguments('key'), + ['TS.INFO', 'key'] + ); + }); + + testUtils.testWithClient('client.ts.info', async client => { + await Promise.all([ + client.ts.create('key', { + LABELS: { id: "2" }, + DUPLICATE_POLICY: TimeSeriesDuplicatePolicies.LAST + }), + client.ts.create('key2'), + client.ts.createRule('key', 'key2', TimeSeriesAggregationType.COUNT, 5), + client.ts.add('key', 1, 10) + ]); + + assert.deepEqual( + await client.ts.info('key'), + { + totalSamples: 1, + memoryUsage: 4261, + firstTimestamp: 1, + lastTimestamp: 1, + retentionTime: 0, + chunkCount: 1, + chunkSize: 4096, + chunkType: 'compressed', + duplicatePolicy: 'last', + labels: [{ + name: 'id', + value: '2' + }], + rules: [{ + aggregationType: 'COUNT', + key: 'key2', + timeBucket: 5 + }], + sourceKey: null + } + ); + }, GLOBAL.SERVERS.OPEN); +}); diff --git a/packages/time-series/lib/commands/INFO_DEBUG.spec.ts b/packages/time-series/lib/commands/INFO_DEBUG.spec.ts new file mode 100644 index 00000000000..d6c7f2c5f80 --- /dev/null +++ b/packages/time-series/lib/commands/INFO_DEBUG.spec.ts @@ -0,0 +1,57 @@ +import { strict as assert } from 'assert'; +import { TimeSeriesAggregationType, TimeSeriesDuplicatePolicies } from '.'; +import testUtils, { GLOBAL } from '../test-utils'; +import { transformArguments } from './INFO_DEBUG'; + +describe('INFO_DEBUG', () => { + it('transformArguments', () => { + assert.deepEqual( + transformArguments('key'), + ['TS.INFO', 'key', 'DEBUG'] + ); + }); + + testUtils.testWithClient('client.ts.get', async client => { + await Promise.all([ + client.ts.create('key', { + LABELS: { id: "2" }, + DUPLICATE_POLICY: TimeSeriesDuplicatePolicies.LAST + }), + client.ts.create('key2'), + client.ts.createRule('key', 'key2', TimeSeriesAggregationType.COUNT, 5), + client.ts.add('key', 1, 10) + ]); + + assert.deepEqual( + await client.ts.infoDebug('key'), + { + totalSamples: 1, + memoryUsage: 4261, + firstTimestamp: 1, + lastTimestamp: 1, + retentionTime: 0, + chunkCount: 1, + chunkSize: 4096, + chunkType: 'compressed', + duplicatePolicy: 'last', + labels: [{ + name: 'id', + value: '2' + }], + sourceKey: null, + rules: [{ + aggregationType: 'COUNT', + key: 'key2', + timeBucket: 5 + }], + chunks: [{ + startTimestamp: 1, + endTimestamp: 1, + samples: 1, + size: 4096, + bytesPerSample: '4096' + }] + } + ); + }, GLOBAL.SERVERS.OPEN); +}); diff --git a/packages/time-series/lib/commands/MADD.spec.ts b/packages/time-series/lib/commands/MADD.spec.ts new file mode 100644 index 00000000000..eed014f2b14 --- /dev/null +++ b/packages/time-series/lib/commands/MADD.spec.ts @@ -0,0 +1,39 @@ +import { strict as assert } from 'assert'; +import testUtils, { GLOBAL } from '../test-utils'; +import { transformArguments } from './MADD'; + +describe('MADD', () => { + it('transformArguments', () => { + assert.deepEqual( + transformArguments([{ + key: '1', + timestamp: 0, + value: 0 + }, { + key: '2', + timestamp: 1, + value: 1 + }]), + ['TS.MADD', '1', '0', '0', '2', '1', '1'] + ); + }); + + // Should we check empty array? + + testUtils.testWithClient('client.ts.mAdd', async client => { + await client.ts.create('key'); + + assert.deepEqual( + await client.ts.mAdd([{ + key: 'key', + timestamp: 0, + value: 0 + }, { + key: 'key', + timestamp: 1, + value: 1 + }]), + [0, 1] + ); + }, GLOBAL.SERVERS.OPEN); +}); diff --git a/packages/time-series/lib/commands/MADD.ts b/packages/time-series/lib/commands/MADD.ts index 8970cac06a8..426eae7e3f3 100644 --- a/packages/time-series/lib/commands/MADD.ts +++ b/packages/time-series/lib/commands/MADD.ts @@ -1,5 +1,7 @@ import { Timestamp, transformTimestampArgument } from '.'; +export const FIRST_KEY_INDEX = 1; + interface MAddSample { key: string; timestamp: Timestamp; diff --git a/packages/time-series/lib/commands/MGET.spec.ts b/packages/time-series/lib/commands/MGET.spec.ts new file mode 100644 index 00000000000..7c6c32927d8 --- /dev/null +++ b/packages/time-series/lib/commands/MGET.spec.ts @@ -0,0 +1,29 @@ +import { strict as assert } from 'assert'; +import testUtils, { GLOBAL } from '../test-utils'; +import { transformArguments } from './MGET'; + +describe('MGET', () => { + it('transformArguments', () => { + assert.deepEqual( + transformArguments('label=value'), + ['TS.MGET', 'FILTER', 'label=value'] + ); + }); + + testUtils.testWithClient('client.ts.mGet', async client => { + await client.ts.add('key', 0, 0, { + LABELS: { label: 'value' } + }); + + assert.deepEqual( + await client.ts.mGet('label=value'), + [{ + key: 'key', + sample: { + timestamp: 0, + value: 0 + } + }] + ); + }, GLOBAL.SERVERS.OPEN); +}); diff --git a/packages/time-series/lib/commands/MGET.ts b/packages/time-series/lib/commands/MGET.ts index 94fc45e03c4..1dbd077db3c 100644 --- a/packages/time-series/lib/commands/MGET.ts +++ b/packages/time-series/lib/commands/MGET.ts @@ -1,31 +1,26 @@ -import { pushVerdictArgument, pushVerdictArguments } from '@node-redis/client/lib/commands/generic-transformers'; +import { RedisCommandArguments } from '@node-redis/client/dist/lib/commands'; +import { Filter, pushFilterArgument, RawLabels, SampleRawReply, SampleReply, transformSampleReply } from '.'; export const IS_READ_ONLY = true; -interface WithLabels { - WITHLABELS: true; +export function transformArguments(filter: Filter): RedisCommandArguments { + return pushFilterArgument(['TS.MGET'], filter); } -interface SelectedLabels { - SELECTED_LABELS: string | Array; -} - -type MGetOptions = WithLabels & SelectedLabels; - -export function transformArguments(filter: string, options?: MGetOptions): Array { - const args = ['TS.MGET']; +export type MGetRawReply = Array<[ + key: string, + labels: RawLabels, + sample: SampleRawReply +]>; - if (options?.WITHLABELS) { - args.push('WITHLABELS'); - } else if (options?.SELECTED_LABELS) { - pushVerdictArguments(args, options.SELECTED_LABELS); - } - - args.push('FILTER', filter); - - return args; +export interface MGetReply { + key: string, + sample: SampleReply } -export function transformReply() { - +export function transformReply(reply: MGetRawReply): Array { + return reply.map(([key, _, sample]) => ({ + key, + sample: transformSampleReply(sample) + })); } diff --git a/packages/time-series/lib/commands/MGET_WITHLABELS.spec.ts b/packages/time-series/lib/commands/MGET_WITHLABELS.spec.ts new file mode 100644 index 00000000000..55fcfde409d --- /dev/null +++ b/packages/time-series/lib/commands/MGET_WITHLABELS.spec.ts @@ -0,0 +1,39 @@ +import { strict as assert } from 'assert'; +import testUtils, { GLOBAL } from '../test-utils'; +import { transformArguments } from './MGET_WITHLABELS'; + +describe('MGET_WITHLABELS', () => { + describe('transformArguments', () => { + it('without options', () => { + assert.deepEqual( + transformArguments('label=value'), + ['TS.MGET', 'WITHLABELS', 'FILTER', 'label=value'] + ); + }); + + it('with SELECTED_LABELS', () => { + assert.deepEqual( + transformArguments('label=value', { SELECTED_LABELS: 'label' }), + ['TS.MGET', 'SELECTED_LABELS', 'label', 'FILTER', 'label=value'] + ); + }); + }); + + testUtils.testWithClient('client.ts.mGetWithLabels', async client => { + await client.ts.add('key', 0, 0, { + LABELS: { label: 'value' } + }); + + assert.deepEqual( + await client.ts.mGetWithLabels('label=value'), + [{ + key: 'key', + labels: { label: 'value'}, + sample: { + timestamp: 0, + value: 0 + } + }] + ); + }, GLOBAL.SERVERS.OPEN); +}); diff --git a/packages/time-series/lib/commands/MGET_WITHLABELS.ts b/packages/time-series/lib/commands/MGET_WITHLABELS.ts new file mode 100644 index 00000000000..cf83f4bcd16 --- /dev/null +++ b/packages/time-series/lib/commands/MGET_WITHLABELS.ts @@ -0,0 +1,41 @@ +import { + SelectedLabels, + pushWithLabelsArgument, + Labels, + transformLablesReply, + transformSampleReply, + Filter, + pushFilterArgument +} from '.'; +import { MGetRawReply, MGetReply } from './MGET'; + +export const IS_READ_ONLY = true; + +interface MGetWithLabelsOptions { + SELECTED_LABELS?: SelectedLabels; +} + +export function transformArguments( + filter: Filter, + options?: MGetWithLabelsOptions +): Array { + const args = ['TS.MGET']; + + pushWithLabelsArgument(args, options?.SELECTED_LABELS); + + pushFilterArgument(args, filter); + + return args; +} + +export interface MGetWithLabelsReply extends MGetReply { + labels: Labels; +}; + +export function transformReply(reply: MGetRawReply): Array { + return reply.map(([key, labels, sample]) => ({ + key, + labels: transformLablesReply(labels), + sample: transformSampleReply(sample) + })); +} diff --git a/packages/time-series/lib/commands/MRANGE.spec.ts b/packages/time-series/lib/commands/MRANGE.spec.ts new file mode 100644 index 00000000000..da01ebb204c --- /dev/null +++ b/packages/time-series/lib/commands/MRANGE.spec.ts @@ -0,0 +1,50 @@ +import { strict as assert } from 'assert'; +import testUtils, { GLOBAL } from '../test-utils'; +import { transformArguments } from './MRANGE'; +import { TimeSeriesAggregationType, TimeSeriesReducers } from '.'; + +describe('MRANGE', () => { + it('transformArguments', () => { + assert.deepEqual( + transformArguments('-', '+', 'label=value', { + FILTER_BY_TS: [0], + FILTER_BY_VALUE: { + min: 0, + max: 1 + }, + COUNT: 1, + ALIGN: '-', + AGGREGATION: { + type: TimeSeriesAggregationType.AVARAGE, + timeBucket: 1 + }, + GROUPBY: { + label: 'label', + reducer: TimeSeriesReducers.SUM + }, + }), + ['TS.MRANGE', '-', '+', 'FILTER_BY_TS', '0', 'FILTER_BY_VALUE', '0', '1', + 'COUNT', '1', 'ALIGN', '-', 'AGGREGATION', 'avg', '1', 'FILTER', 'label=value', + 'GROUPBY', 'label', 'REDUCE', 'sum'] + ); + }); + + testUtils.testWithClient('client.ts.mRange', async client => { + await client.ts.add('key', 0, 0, { + LABELS: { label: 'value'} + }); + + assert.deepEqual( + await client.ts.mRange('-', '+', 'label=value', { + COUNT: 1 + }), + [{ + key: 'key', + samples: [{ + timestamp: 0, + value: 0 + }] + }] + ); + }, GLOBAL.SERVERS.OPEN); +}); diff --git a/packages/time-series/lib/commands/MRANGE.ts b/packages/time-series/lib/commands/MRANGE.ts new file mode 100644 index 00000000000..340201419f9 --- /dev/null +++ b/packages/time-series/lib/commands/MRANGE.ts @@ -0,0 +1,21 @@ +import { RedisCommandArguments } from '@node-redis/client/dist/lib/commands'; +import { MRangeOptions, Timestamp, pushMRangeArguments, Filter } from '.'; + +export const IS_READ_ONLY = true; + +export function transformArguments( + fromTimestamp: Timestamp, + toTimestamp: Timestamp, + filters: Filter, + options?: MRangeOptions +): RedisCommandArguments { + return pushMRangeArguments( + ['TS.MRANGE'], + fromTimestamp, + toTimestamp, + filters, + options + ); +} + +export { transformMRangeReply as transformReply } from '.'; diff --git a/packages/time-series/lib/commands/MRANGE_WITHLABELS.spec.ts b/packages/time-series/lib/commands/MRANGE_WITHLABELS.spec.ts new file mode 100644 index 00000000000..e8381a17935 --- /dev/null +++ b/packages/time-series/lib/commands/MRANGE_WITHLABELS.spec.ts @@ -0,0 +1,52 @@ +import { strict as assert } from 'assert'; +import testUtils, { GLOBAL } from '../test-utils'; +import { transformArguments } from './MRANGE_WITHLABELS'; +import { TimeSeriesAggregationType, TimeSeriesReducers } from '.'; + +describe('MRANGE_WITHLABELS', () => { + it('transformArguments', () => { + assert.deepEqual( + transformArguments('-', '+', 'label=value', { + FILTER_BY_TS: [0], + FILTER_BY_VALUE: { + min: 0, + max: 1 + }, + SELECTED_LABELS: ['label'], + COUNT: 1, + ALIGN: '-', + AGGREGATION: { + type: TimeSeriesAggregationType.AVARAGE, + timeBucket: 1 + }, + GROUPBY: { + label: 'label', + reducer: TimeSeriesReducers.SUM + }, + }), + ['TS.MRANGE', '-', '+', 'FILTER_BY_TS', '0', 'FILTER_BY_VALUE', '0', '1', + 'COUNT', '1', 'ALIGN', '-', 'AGGREGATION', 'avg', '1', 'SELECTED_LABELS', 'label', + 'FILTER', 'label=value', 'GROUPBY', 'label', 'REDUCE', 'sum'] + ); + }); + + testUtils.testWithClient('client.ts.mRangeWithLabels', async client => { + await client.ts.add('key', 0, 0, { + LABELS: { label: 'value'} + }); + + assert.deepEqual( + await client.ts.mRangeWithLabels('-', '+', 'label=value', { + COUNT: 1 + }), + [{ + key: 'key', + labels: { label: 'value' }, + samples: [{ + timestamp: 0, + value: 0 + }] + }] + ); + }, GLOBAL.SERVERS.OPEN); +}); diff --git a/packages/time-series/lib/commands/MRANGE_WITHLABELS.ts b/packages/time-series/lib/commands/MRANGE_WITHLABELS.ts new file mode 100644 index 00000000000..f4ce2542355 --- /dev/null +++ b/packages/time-series/lib/commands/MRANGE_WITHLABELS.ts @@ -0,0 +1,21 @@ +import { RedisCommandArguments } from '@node-redis/client/dist/lib/commands'; +import { Timestamp, MRangeWithLabelsOptions, pushMRangeWithLabelsArguments } from '.'; + +export const IS_READ_ONLY = true; + +export function transformArguments( + fromTimestamp: Timestamp, + toTimestamp: Timestamp, + filters: string | Array, + options?: MRangeWithLabelsOptions +): RedisCommandArguments { + return pushMRangeWithLabelsArguments( + ['TS.MRANGE'], + fromTimestamp, + toTimestamp, + filters, + options + ); +} + +export { transformMRangeWithLabelsReply as transformReply } from '.'; diff --git a/packages/time-series/lib/commands/MREVRANGE.spec.ts b/packages/time-series/lib/commands/MREVRANGE.spec.ts new file mode 100644 index 00000000000..08c40d8c60f --- /dev/null +++ b/packages/time-series/lib/commands/MREVRANGE.spec.ts @@ -0,0 +1,50 @@ +import { strict as assert } from 'assert'; +import testUtils, { GLOBAL } from '../test-utils'; +import { transformArguments } from './MREVRANGE'; +import { TimeSeriesAggregationType, TimeSeriesReducers } from '.'; + +describe('MREVRANGE', () => { + it('transformArguments', () => { + assert.deepEqual( + transformArguments('-', '+', 'label=value', { + FILTER_BY_TS: [0], + FILTER_BY_VALUE: { + min: 0, + max: 1 + }, + COUNT: 1, + ALIGN: '-', + AGGREGATION: { + type: TimeSeriesAggregationType.AVARAGE, + timeBucket: 1 + }, + GROUPBY: { + label: 'label', + reducer: TimeSeriesReducers.SUM + }, + }), + ['TS.MREVRANGE', '-', '+', 'FILTER_BY_TS', '0', 'FILTER_BY_VALUE', '0', '1', + 'COUNT', '1', 'ALIGN', '-', 'AGGREGATION', 'avg', '1', 'FILTER', 'label=value', + 'GROUPBY', 'label', 'REDUCE', 'sum'] + ); + }); + + testUtils.testWithClient('client.ts.mRevRange', async client => { + await client.ts.add('key', 0, 0, { + LABELS: { label: 'value'} + }); + + assert.deepEqual( + await client.ts.mRevRange('-', '+', 'label=value', { + COUNT: 1 + }), + [{ + key: 'key', + samples: [{ + timestamp: 0, + value: 0 + }] + }] + ); + }, GLOBAL.SERVERS.OPEN); +}); diff --git a/packages/time-series/lib/commands/MREVRANGE.ts b/packages/time-series/lib/commands/MREVRANGE.ts new file mode 100644 index 00000000000..f31677fdb32 --- /dev/null +++ b/packages/time-series/lib/commands/MREVRANGE.ts @@ -0,0 +1,21 @@ +import { RedisCommandArguments } from '@node-redis/client/dist/lib/commands'; +import { MRangeOptions, Timestamp, pushMRangeArguments, Filter } from '.'; + +export const IS_READ_ONLY = true; + +export function transformArguments( + fromTimestamp: Timestamp, + toTimestamp: Timestamp, + filters: Filter, + options?: MRangeOptions +): RedisCommandArguments { + return pushMRangeArguments( + ['TS.MREVRANGE'], + fromTimestamp, + toTimestamp, + filters, + options + ); +} + +export { transformMRangeReply as transformReply } from '.'; diff --git a/packages/time-series/lib/commands/MREVRANGE_WITHLABELS.spec.ts b/packages/time-series/lib/commands/MREVRANGE_WITHLABELS.spec.ts new file mode 100644 index 00000000000..a636450bef8 --- /dev/null +++ b/packages/time-series/lib/commands/MREVRANGE_WITHLABELS.spec.ts @@ -0,0 +1,52 @@ +import { strict as assert } from 'assert'; +import testUtils, { GLOBAL } from '../test-utils'; +import { transformArguments } from './MREVRANGE_WITHLABELS'; +import { TimeSeriesAggregationType, TimeSeriesReducers } from '.'; + +describe('MREVRANGE_WITHLABELS', () => { + it('transformArguments', () => { + assert.deepEqual( + transformArguments('-', '+', 'label=value', { + FILTER_BY_TS: [0], + FILTER_BY_VALUE: { + min: 0, + max: 1 + }, + SELECTED_LABELS: ['label'], + COUNT: 1, + ALIGN: '-', + AGGREGATION: { + type: TimeSeriesAggregationType.AVARAGE, + timeBucket: 1 + }, + GROUPBY: { + label: 'label', + reducer: TimeSeriesReducers.SUM + }, + }), + ['TS.MREVRANGE', '-', '+', 'FILTER_BY_TS', '0', 'FILTER_BY_VALUE', '0', '1', + 'COUNT', '1', 'ALIGN', '-', 'AGGREGATION', 'avg', '1', 'SELECTED_LABELS', 'label', + 'FILTER', 'label=value', 'GROUPBY', 'label', 'REDUCE', 'sum'] + ); + }); + + testUtils.testWithClient('client.ts.mRevRangeWithLabels', async client => { + await client.ts.add('key', 0, 0, { + LABELS: { label: 'value'} + }); + + assert.deepEqual( + await client.ts.mRevRangeWithLabels('-', '+', 'label=value', { + COUNT: 1 + }), + [{ + key: 'key', + labels: { label: 'value' }, + samples: [{ + timestamp: 0, + value: 0 + }] + }] + ); + }, GLOBAL.SERVERS.OPEN); +}); diff --git a/packages/time-series/lib/commands/MREVRANGE_WITHLABELS.ts b/packages/time-series/lib/commands/MREVRANGE_WITHLABELS.ts new file mode 100644 index 00000000000..34109e00a68 --- /dev/null +++ b/packages/time-series/lib/commands/MREVRANGE_WITHLABELS.ts @@ -0,0 +1,21 @@ +import { RedisCommandArguments } from '@node-redis/client/dist/lib/commands'; +import { Timestamp, MRangeWithLabelsOptions, pushMRangeWithLabelsArguments, Filter } from '.'; + +export const IS_READ_ONLY = true; + +export function transformArguments( + fromTimestamp: Timestamp, + toTimestamp: Timestamp, + filters: Filter, + options?: MRangeWithLabelsOptions +): RedisCommandArguments { + return pushMRangeWithLabelsArguments( + ['TS.MREVRANGE'], + fromTimestamp, + toTimestamp, + filters, + options + ); +} + +export { transformMRangeWithLabelsReply as transformReply } from '.'; diff --git a/packages/time-series/lib/commands/QUERYINDEX.spec.ts b/packages/time-series/lib/commands/QUERYINDEX.spec.ts new file mode 100644 index 00000000000..ebd3e14dcdc --- /dev/null +++ b/packages/time-series/lib/commands/QUERYINDEX.spec.ts @@ -0,0 +1,27 @@ +import { strict as assert } from 'assert'; +import testUtils, { GLOBAL } from '../test-utils'; +import { transformArguments } from './QUERYINDEX'; + +describe('QUERYINDEX', () => { + it('transformArguments', () => { + assert.deepEqual( + transformArguments('*'), + ['TS.QUERYINDEX', '*'] + ); + }); + + testUtils.testWithClient('client.ts.queryIndex', async client => { + await Promise.all([ + client.ts.create('key', { + LABELS: { + label: 'value' + } + }) + ]); + + assert.deepEqual( + await client.ts.queryIndex('label=value'), + ['key'] + ); + }, GLOBAL.SERVERS.OPEN); +}); diff --git a/packages/time-series/lib/commands/RANGE.spec.ts b/packages/time-series/lib/commands/RANGE.spec.ts new file mode 100644 index 00000000000..15d2706d952 --- /dev/null +++ b/packages/time-series/lib/commands/RANGE.spec.ts @@ -0,0 +1,38 @@ +import { strict as assert } from 'assert'; +import testUtils, { GLOBAL } from '../test-utils'; +import { transformArguments } from './RANGE'; +import { TimeSeriesAggregationType } from '.'; + +describe('RANGE', () => { + it('transformArguments', () => { + assert.deepEqual( + transformArguments('key', '-', '+', { + FILTER_BY_TS: [0], + FILTER_BY_VALUE: { + min: 1, + max: 2 + }, + COUNT: 1, + ALIGN: '-', + AGGREGATION: { + type: TimeSeriesAggregationType.AVARAGE, + timeBucket: 1 + } + }), + ['TS.RANGE', 'key', '-', '+', 'FILTER_BY_TS', '0', 'FILTER_BY_VALUE', + '1', '2', 'COUNT', '1', 'ALIGN', '-', 'AGGREGATION', 'avg', '1'] + ); + }); + + testUtils.testWithClient('client.ts.range', async client => { + await client.ts.add('key', 1, 2); + + assert.deepEqual( + await client.ts.range('key', '-', '+'), + [{ + timestamp: 1, + value: 2 + }] + ); + }, GLOBAL.SERVERS.OPEN); +}); diff --git a/packages/time-series/lib/commands/RANGE.ts b/packages/time-series/lib/commands/RANGE.ts index b22d2e33b21..73dfa90d399 100644 --- a/packages/time-series/lib/commands/RANGE.ts +++ b/packages/time-series/lib/commands/RANGE.ts @@ -1,21 +1,24 @@ import { RedisCommandArguments } from '@node-redis/client/dist/lib/commands'; -import { RangeOptions, Timestamp, pushRangeArguments } from '.'; +import { RangeOptions, Timestamp, pushRangeArguments, SampleRawReply, SampleReply, transformRangeReply } from '.'; export const FIRST_KEY_INDEX = 1; export const IS_READ_ONLY = true; export function transformArguments( + key: string, fromTimestamp: Timestamp, toTimestamp: Timestamp, options?: RangeOptions ): RedisCommandArguments { return pushRangeArguments( - ['TS.RANGE'], + ['TS.RANGE', key], fromTimestamp, toTimestamp, options ); } -export { transformRangeReply } from '.'; +export function transformReply(reply: Array): Array { + return transformRangeReply(reply); +} diff --git a/packages/time-series/lib/commands/REVRANGE.spec.ts b/packages/time-series/lib/commands/REVRANGE.spec.ts new file mode 100644 index 00000000000..10a7f4b4234 --- /dev/null +++ b/packages/time-series/lib/commands/REVRANGE.spec.ts @@ -0,0 +1,106 @@ +import { strict as assert } from 'assert'; +import testUtils, { GLOBAL } from '../test-utils'; +import { transformArguments } from './REVRANGE'; +import { TimeSeriesAggregationType } from '.'; + +describe('REVRANGE', () => { + describe('transformArguments', () => { + it('without options', () => { + assert.deepEqual( + transformArguments('key', '-', '+'), + ['TS.REVRANGE', 'key', '-', '+'] + ); + }); + + it('with FILTER_BY_TS', () => { + assert.deepEqual( + transformArguments('key', '-', '+', { + FILTER_BY_TS: [0] + }), + ['TS.REVRANGE', 'key', '-', '+', 'FILTER_BY_TS', '0'] + ); + }); + + it('with FILTER_BY_VALUE', () => { + assert.deepEqual( + transformArguments('key', '-', '+', { + FILTER_BY_VALUE: { + min: 1, + max: 2 + } + }), + ['TS.REVRANGE', 'key', '-', '+', 'FILTER_BY_VALUE', '1', '2'] + ); + }); + + it('with COUNT', () => { + assert.deepEqual( + transformArguments('key', '-', '+', { + COUNT: 1 + }), + ['TS.REVRANGE', 'key', '-', '+', 'COUNT', '1'] + ); + }); + + it('with ALIGN', () => { + assert.deepEqual( + transformArguments('key', '-', '+', { + ALIGN: '-' + }), + ['TS.REVRANGE', 'key', '-', '+', 'ALIGN', '-'] + ); + }); + + it('with AGGREGATION', () => { + assert.deepEqual( + transformArguments('key', '-', '+', { + AGGREGATION: { + type: TimeSeriesAggregationType.AVARAGE, + timeBucket: 1 + } + }), + ['TS.REVRANGE', 'key', '-', '+', 'AGGREGATION', 'avg', '1'] + ); + }); + + it('with FILTER_BY_TS, FILTER_BY_VALUE, COUNT, ALIGN, AGGREGATION', () => { + assert.deepEqual( + transformArguments('key', '-', '+', { + FILTER_BY_TS: [0], + FILTER_BY_VALUE: { + min: 1, + max: 2 + }, + COUNT: 1, + ALIGN: '-', + AGGREGATION: { + type: TimeSeriesAggregationType.AVARAGE, + timeBucket: 1 + } + }), + [ + 'TS.REVRANGE', 'key', '-', '+', 'FILTER_BY_TS', '0', 'FILTER_BY_VALUE', + '1', '2', 'COUNT', '1', 'ALIGN', '-', 'AGGREGATION', 'avg', '1' + ] + ); + }); + }); + + testUtils.testWithClient('client.ts.revRange', async client => { + await Promise.all([ + client.ts.add('key', 0, 1), + client.ts.add('key', 1, 2) + ]); + + assert.deepEqual( + await client.ts.revRange('key', '-', '+'), + [{ + timestamp: 1, + value: 2 + }, { + timestamp: 0, + value: 1 + }] + ); + }, GLOBAL.SERVERS.OPEN); +}); diff --git a/packages/time-series/lib/commands/REVRANGE.ts b/packages/time-series/lib/commands/REVRANGE.ts index ba961265ac6..f2bfcb1cb50 100644 --- a/packages/time-series/lib/commands/REVRANGE.ts +++ b/packages/time-series/lib/commands/REVRANGE.ts @@ -1,21 +1,24 @@ import { RedisCommandArguments } from '@node-redis/client/dist/lib/commands'; -import { RangeOptions, Timestamp, pushRangeArguments } from '.'; +import { RangeOptions, Timestamp, pushRangeArguments, SampleRawReply, SampleReply, transformRangeReply } from '.'; export const FIRST_KEY_INDEX = 1; export const IS_READ_ONLY = true; export function transformArguments( + key: string, fromTimestamp: Timestamp, toTimestamp: Timestamp, options?: RangeOptions ): RedisCommandArguments { return pushRangeArguments( - ['TS.REVRANGE'], + ['TS.REVRANGE', key], fromTimestamp, toTimestamp, options ); } -export { transformRangeReply } from '.'; +export function transformReply(reply: Array): Array { + return transformRangeReply(reply); +} diff --git a/packages/time-series/lib/commands/index.spec.ts b/packages/time-series/lib/commands/index.spec.ts index d02d259eb76..b9d1eaaef29 100644 --- a/packages/time-series/lib/commands/index.spec.ts +++ b/packages/time-series/lib/commands/index.spec.ts @@ -9,9 +9,17 @@ import { pushLabelsArgument, transformIncrDecrArguments, transformSampleReply, + TimeSeriesAggregationType, pushRangeArguments, + pushMRangeGroupByArguments, + TimeSeriesReducers, + pushFilterArgument, + pushMRangeArguments, + pushWithLabelsArgument, + pushMRangeWithLabelsArguments, transformRangeReply, - TimeSeriesAggregationType + transformMRangeReply, + transformMRangeWithLabelsReply } from '.'; describe('transformTimestampArgument', () => { @@ -115,6 +123,15 @@ describe('transformIncrDecrArguments', () => { ['TS.INCRBY', 'key', '1', 'UNCOMPRESSED'] ); }); + + it('with UNCOMPRESSED false', () => { + assert.deepEqual( + transformIncrDecrArguments('TS.INCRBY', 'key', 1, { + UNCOMPRESSED: false + }), + ['TS.INCRBY', 'key', '1'] + ); + }); }); it('transformSampleReply', () => { @@ -139,7 +156,7 @@ describe('pushRangeArguments', () => { it('string', () => { assert.deepEqual( pushRangeArguments([], '-', '+', { - FILTER_BY_TS: 'ts' + FILTER_BY_TS: ['ts'] }), ['-', '+', 'FILTER_BY_TS', 'ts'] ); @@ -200,7 +217,7 @@ describe('pushRangeArguments', () => { it('with FILTER_BY_TS, FILTER_BY_VALUE, COUNT, ALIGN, AGGREGATION', () => { assert.deepEqual( pushRangeArguments([], '-', '+', { - FILTER_BY_TS: 'ts', + FILTER_BY_TS: ['ts'], FILTER_BY_VALUE: { min: 1, max: 2 @@ -212,11 +229,91 @@ describe('pushRangeArguments', () => { timeBucket: 1 } }), - ['-', '+', 'FILTER_BY_TS', 'ts', 'FILTER_BY_VALUE', '1', '2', 'COUNT', '1', 'ALIGN', '1', 'AGGREGATION', 'first', '1'] + ['-', '+', 'FILTER_BY_TS', 'ts', 'FILTER_BY_VALUE', '1', '2', + 'COUNT', '1', 'ALIGN', '1', 'AGGREGATION', 'first', '1'] + ); + }); +}); + +describe('pushMRangeGroupByArguments', () => { + it('undefined', () => { + assert.deepEqual( + pushMRangeGroupByArguments([]), + [] + ); + }); + + it('with GROUPBY', () => { + assert.deepEqual( + pushMRangeGroupByArguments([], { + label: 'label', + reducer: TimeSeriesReducers.MAXIMUM + }), + ['GROUPBY', 'label', 'REDUCE', 'max'] + ); + }); +}); + +describe('pushFilterArgument', () => { + it('string', () => { + assert.deepEqual( + pushFilterArgument([], 'label=value'), + ['FILTER', 'label=value'] + ); + }); + + it('Array', () => { + assert.deepEqual( + pushFilterArgument([], ['1=1', '2=2']), + ['FILTER', '1=1', '2=2'] + ); + }); +}); + +describe('pushMRangeArguments', () => { + it('without options', () => { + assert.deepEqual( + pushMRangeArguments([], '-', '+', 'label=value'), + ['-', '+', 'FILTER', 'label=value'] + ); + }); + + it('with GROUPBY', () => { + assert.deepEqual( + pushMRangeArguments([], '-', '+', 'label=value', { + GROUPBY: { + label: 'label', + reducer: TimeSeriesReducers.MAXIMUM + } + }), + ['-', '+', 'FILTER', 'label=value', 'GROUPBY', 'label', 'REDUCE', 'max'] + ); + }); +}); + +describe('pushWithLabelsArgument', () => { + it('without selected labels', () => { + assert.deepEqual( + pushWithLabelsArgument([]), + ['WITHLABELS'] + ); + }); + + it('with selected labels', () => { + assert.deepEqual( + pushWithLabelsArgument([], ['label']), + ['SELECTED_LABELS', 'label'] ); }); }); +it('pushMRangeWithLabelsArguments', () => { + assert.deepEqual( + pushMRangeWithLabelsArguments([], '-', '+', 'label=value'), + ['-', '+', 'WITHLABELS', 'FILTER', 'label=value'] + ); +}); + it('transformRangeReply', () => { assert.deepEqual( transformRangeReply([[1, '1.1'], [2, '2.2']]), @@ -229,3 +326,46 @@ it('transformRangeReply', () => { }] ); }); + +describe('transformMRangeReply', () => { + assert.deepEqual( + transformMRangeReply([[ + 'key', + [], + [[1, '1.1'], [2, '2.2']] + ]]), + [{ + key: 'key', + samples: [{ + timestamp: 1, + value: 1.1 + }, { + timestamp: 2, + value: 2.2 + }] + }] + ); +}); + +describe('transformMRangeWithLabelsReply', () => { + assert.deepEqual( + transformMRangeWithLabelsReply([[ + 'key', + [['label', 'value']], + [[1, '1.1'], [2, '2.2']] + ]]), + [{ + key: 'key', + labels: { + label: 'value' + }, + samples: [{ + timestamp: 1, + value: 1.1 + }, { + timestamp: 2, + value: 2.2 + }] + }] + ); +}); diff --git a/packages/time-series/lib/commands/index.ts b/packages/time-series/lib/commands/index.ts index 07034c61372..b8b43eb9431 100644 --- a/packages/time-series/lib/commands/index.ts +++ b/packages/time-series/lib/commands/index.ts @@ -11,9 +11,14 @@ import * as INFO_DEBUG from './INFO_DEBUG'; import * as INFO from './INFO'; import * as MADD from './MADD'; import * as MGET from './MGET'; +import * as MGET_WITHLABELS from './MGET_WITHLABELS'; import * as QUERYINDEX from './QUERYINDEX'; import * as RANGE from './RANGE'; import * as REVRANGE from './REVRANGE'; +import * as MRANGE from './MRANGE'; +import * as MRANGE_WITHLABELS from './MRANGE_WITHLABELS'; +import * as MREVRANGE from './MREVRANGE'; +import * as MREVRANGE_WITHLABELS from './MREVRANGE_WITHLABELS'; import { RedisCommandArguments } from '@node-redis/client/dist/lib/commands'; import { pushVerdictArguments } from '@node-redis/client/lib/commands/generic-transformers'; @@ -44,12 +49,22 @@ export default { mAdd: MADD, MGET, mGet: MGET, + MGET_WITHLABELS, + mGetWithLabels: MGET_WITHLABELS, QUERYINDEX, queryIndex: QUERYINDEX, RANGE, range: RANGE, REVRANGE, - revRange: REVRANGE + revRange: REVRANGE, + MRANGE, + mRange: MRANGE, + MRANGE_WITHLABELS, + mRangeWithLabels: MRANGE_WITHLABELS, + MREVRANGE, + mRevRange: MREVRANGE, + MREVRANGE_WITHLABELS, + mRevRangeWithLabels: MREVRANGE_WITHLABELS }; export enum TimeSeriesAggregationType { @@ -67,6 +82,21 @@ export enum TimeSeriesAggregationType { VAR_S = 'var.s' } +export enum TimeSeriesDuplicatePolicies { + BLOCK = 'BLOCK', + FIRST = 'FIRST', + LAST = 'LAST', + MIN = 'MIN', + MAX = 'MAX', + SUM = 'SUM' +} + +export enum TimeSeriesReducers { + SUM = 'sum', + MINIMUM = 'min', + MAXIMUM = 'max', +} + export type Timestamp = number | Date | string; export function transformTimestampArgument(timestamp: Timestamp): string { @@ -117,19 +147,22 @@ export function pushChunkSizeArgument(args: RedisCommandArguments, chunkSize?: n return args; } -export enum TimeSeriesDuplicatePolicies { - BLOCK = 'BLOCK', - FIRST = 'FIRST', - LAST = 'LAST', - MIN = 'MIN', - MAX = 'MAX', - SUM = 'SUM' -} +export type RawLabels = Array<[label: string, value: string]>; export type Labels = { [label: string]: string; }; +export function transformLablesReply(reply: RawLabels): Labels { + const labels: Labels = {}; + + for (const [key, value] of reply) { + labels[key] = value; + } + + return labels +} + export function pushLabelsArgument(args: RedisCommandArguments, labels?: Labels): RedisCommandArguments { if (labels) { args.push('LABELS'); @@ -162,7 +195,7 @@ export function transformIncrDecrArguments( value.toString() ]; - if (options?.TIMESTAMP) { + if (options?.TIMESTAMP !== undefined && options?.TIMESTAMP !== null) { args.push('TIMESTAMP', transformTimestampArgument(options.TIMESTAMP)); } @@ -194,7 +227,7 @@ export function transformSampleReply(reply: SampleRawReply): SampleReply { } export interface RangeOptions { - FILTER_BY_TS?: string | Array; + FILTER_BY_TS?: Array; FILTER_BY_VALUE?: { min: number; max: number; @@ -220,7 +253,9 @@ export function pushRangeArguments( if (options?.FILTER_BY_TS) { args.push('FILTER_BY_TS'); - pushVerdictArguments(args, options.FILTER_BY_TS); + for(const ts of options.FILTER_BY_TS) { + args.push(transformTimestampArgument(ts)); + } } if (options?.FILTER_BY_VALUE) { @@ -256,6 +291,121 @@ export function pushRangeArguments( return args; } +interface MRangeGroupBy { + label: string; + reducer: TimeSeriesReducers; +} + +export function pushMRangeGroupByArguments(args: RedisCommandArguments, groupBy?: MRangeGroupBy): RedisCommandArguments { + if (groupBy) { + args.push( + 'GROUPBY', + groupBy.label, + 'REDUCE', + groupBy.reducer + ); + } + + return args; +} + +export type Filter = string | Array; + +export function pushFilterArgument(args: RedisCommandArguments, filter: string | Array): RedisCommandArguments { + args.push('FILTER'); + pushVerdictArguments(args, filter); + return args; +} + +export interface MRangeOptions extends RangeOptions { + GROUPBY?: MRangeGroupBy; +} + +export function pushMRangeArguments( + args: RedisCommandArguments, + fromTimestamp: Timestamp, + toTimestamp: Timestamp, + filter: Filter, + options?: MRangeOptions +): RedisCommandArguments { + pushRangeArguments(args, fromTimestamp, toTimestamp, options); + pushFilterArgument(args, filter); + pushMRangeGroupByArguments(args, options?.GROUPBY); + return args; +} + +export type SelectedLabels = string | Array; + +export function pushWithLabelsArgument(args: RedisCommandArguments, selectedLabels?: SelectedLabels): RedisCommandArguments { + if (!selectedLabels) { + args.push('WITHLABELS'); + } else { + args.push('SELECTED_LABELS'); + pushVerdictArguments(args, selectedLabels); + } + + return args; +} + +export interface MRangeWithLabelsOptions extends MRangeOptions { + SELECTED_LABELS?: SelectedLabels; +} + +export function pushMRangeWithLabelsArguments( + args: RedisCommandArguments, + fromTimestamp: Timestamp, + toTimestamp: Timestamp, + filter: Filter, + options?: MRangeWithLabelsOptions +): RedisCommandArguments { + pushRangeArguments(args, fromTimestamp, toTimestamp, options); + pushWithLabelsArgument(args, options?.SELECTED_LABELS); + pushFilterArgument(args, filter); + pushMRangeGroupByArguments(args, options?.GROUPBY); + return args; +} + export function transformRangeReply(reply: Array): Array { return reply.map(transformSampleReply); } + +type MRangeRawReply = Array<[ + key: string, + labels: RawLabels, + samples: Array +]>; + +interface MRangeReplyItem { + key: string; + samples: Array; +} + +export function transformMRangeReply(reply: MRangeRawReply): Array { + const args = []; + + for (const [key, _, sample] of reply) { + args.push({ + key, + samples: sample.map(transformSampleReply) + }); + } + + return args; +} +export interface MRangeWithLabelsReplyItem extends MRangeReplyItem { + labels: Labels; +} + +export function transformMRangeWithLabelsReply(reply: MRangeRawReply): Array { + const args = []; + + for (const [key, labels, samples] of reply) { + args.push({ + key, + labels: transformLablesReply(labels), + samples: samples.map(transformSampleReply) + }); + } + + return args; +} From 0865d2277725b64c18dad05e8a6231c547a8cbdb Mon Sep 17 00:00:00 2001 From: leibale Date: Mon, 13 Dec 2021 10:17:12 -0500 Subject: [PATCH 1041/1748] fix #1774 #1767 - update docs Co-authored-by: Chayim --- docs/FAQ.md | 12 +++++++++++ docs/client-configuration.md | 40 +++++++++++++++++++++++++++++++----- 2 files changed, 47 insertions(+), 5 deletions(-) diff --git a/docs/FAQ.md b/docs/FAQ.md index cfdb2ecaf42..5d65ff7ee0b 100644 --- a/docs/FAQ.md +++ b/docs/FAQ.md @@ -11,3 +11,15 @@ When a socket closed unexpectedly, all the commands that were already sent will Commands are pipelined using [`queueMicrotask`](https://nodejs.org/api/globals.html#globals_queuemicrotask_callback). If `socket.write()` returns `false`—meaning that ["all or part of the data was queued in user memory"](https://nodejs.org/api/net.html#net_socket_write_data_encoding_callback:~:text=all%20or%20part%20of%20the%20data%20was%20queued%20in%20user%20memory)—the commands will stack in memory until the [`drain`](https://nodejs.org/api/net.html#net_event_drain) event is fired. + +## `RedisClientType` + +Redis has support for [modules](https://redis.io/modules) and running [Lua scripts](../README.md#lua-scripts) within the Redis context. To take advantage of typing within these scenarios, `RedisClient` and `RedisCluster` should be used with [typeof](https://www.typescriptlang.org/docs/handbook/2/typeof-types.html), rather than the base types `RedisClientType` and `RedisClusterType`. + +```typescript +import { createClient } from '@node-redis/client'; + +export const client = createClient(); + +export type RedisClientType = typeof client; +``` \ No newline at end of file diff --git a/docs/client-configuration.md b/docs/client-configuration.md index 4ea94fcab17..32ecbd9f0e3 100644 --- a/docs/client-configuration.md +++ b/docs/client-configuration.md @@ -11,7 +11,7 @@ | socket.connectTimeout | `5000` | The timeout for connecting to the Redis Server (in milliseconds) | | socket.noDelay | `true` | Enable/disable the use of [`Nagle's algorithm`](https://nodejs.org/api/net.html#net_socket_setnodelay_nodelay) | | socket.keepAlive | `5000` | Enable/disable the [`keep-alive`](https://nodejs.org/api/net.html#net_socket_setkeepalive_enable_initialdelay) functionality | -| socket.tls | | Set to `true` to enable [TLS Configuration](https://nodejs.org/api/tls.html#tls_tls_connect_options_callback) | +| socket.tls | | See explanation and examples [below](#TLS) | | socket.reconnectStrategy | `retries => Math.min(retries * 50, 500)` | A function containing the [Reconnect Strategy](#reconnect-strategy) logic | | username | | ACL username ([see ACL guide](https://redis.io/topics/acl)) | | password | | ACL password or the old "--requirepass" password | @@ -26,9 +26,39 @@ ## Reconnect Strategy -You can implement a custom reconnect strategy as a function that should: +You can implement a custom reconnect strategy as a function: - Receives the number of retries attempted so far. -- Should return `number | Error`: - - `number`: the time in milliseconds to wait before trying to reconnect again. - - `Error`: close the client and flush the commands queue. +- Returns `number | Error`: + - `number`: the wait time in milliseconds prior attempting to reconnect. + - `Error`: closes the client and flushes the internal command queues. + +## TLS + +When creating a client, set `socket.tls` to `true` to enable TLS. Below are some basic examples. + +> For configuration options see [tls.connect](https://nodejs.org/api/tls.html#tlsconnectoptions-callback) and [tls.createSecureContext](https://nodejs.org/api/tls.html#tlscreatesecurecontextoptions), as those are the underlying functions used by this library. + +### Create a SSL client + +```typescript +createClient({ + socket: { + tls: true, + ca: '...', + cert: '...' + } +}); +``` + +### Create a SSL client using a self-signed certificate + +```typescript +createClient({ + socket: { + tls: true, + rejectUnauthorized: true, + cert: '...' + } +}); +``` \ No newline at end of file From 01e66e7c8ffc0ad59e08bc9e6ce860efe12b88b7 Mon Sep 17 00:00:00 2001 From: Avital Fine <79420960+AvitalFineRedis@users.noreply.github.com> Date: Mon, 13 Dec 2021 16:28:04 +0100 Subject: [PATCH 1042/1748] Search commands (#1778) * ft.alter * ft.profile --- .../search/lib/commands/AGGREGATE.spec.ts | 2 +- packages/search/lib/commands/AGGREGATE.ts | 26 +- packages/search/lib/commands/ALTER.spec.ts | 37 ++ packages/search/lib/commands/ALTER.ts | 10 + packages/search/lib/commands/CREATE.spec.ts | 4 +- packages/search/lib/commands/CREATE.ts | 107 +----- .../search/lib/commands/DROPINDEX.spec.ts | 2 +- packages/search/lib/commands/INFO.spec.ts | 1 - packages/search/lib/commands/PROFILE.ts | 26 -- .../lib/commands/PROFILE_AGGREGATE.spec.ts | 46 +++ .../search/lib/commands/PROFILE_AGGREGATE.ts | 29 ++ .../lib/commands/PROFILE_SEARCH.spec.ts | 41 ++ .../search/lib/commands/PROFILE_SEARCH.ts | 29 ++ packages/search/lib/commands/SEARCH.spec.ts | 3 +- packages/search/lib/commands/SEARCH.ts | 125 +----- .../search/lib/commands/SPELLCHECK.spec.ts | 2 +- packages/search/lib/commands/TAGVALS.spec.ts | 2 +- packages/search/lib/commands/index.ts | 359 +++++++++++++++++- packages/search/lib/index.ts | 2 +- 19 files changed, 581 insertions(+), 272 deletions(-) create mode 100644 packages/search/lib/commands/ALTER.spec.ts create mode 100644 packages/search/lib/commands/ALTER.ts delete mode 100644 packages/search/lib/commands/PROFILE.ts create mode 100644 packages/search/lib/commands/PROFILE_AGGREGATE.spec.ts create mode 100644 packages/search/lib/commands/PROFILE_AGGREGATE.ts create mode 100644 packages/search/lib/commands/PROFILE_SEARCH.spec.ts create mode 100644 packages/search/lib/commands/PROFILE_SEARCH.ts diff --git a/packages/search/lib/commands/AGGREGATE.spec.ts b/packages/search/lib/commands/AGGREGATE.spec.ts index 2a6647c97a4..24684d447dc 100644 --- a/packages/search/lib/commands/AGGREGATE.spec.ts +++ b/packages/search/lib/commands/AGGREGATE.spec.ts @@ -1,7 +1,7 @@ import { strict as assert } from 'assert'; import testUtils, { GLOBAL } from '../test-utils'; import { AggregateGroupByReducers, AggregateSteps, transformArguments } from './AGGREGATE'; -import { SchemaFieldTypes } from './CREATE'; +import { SchemaFieldTypes } from '.'; describe('AGGREGATE', () => { describe('transformArguments', () => { diff --git a/packages/search/lib/commands/AGGREGATE.ts b/packages/search/lib/commands/AGGREGATE.ts index 25466188906..7160cee1d4f 100644 --- a/packages/search/lib/commands/AGGREGATE.ts +++ b/packages/search/lib/commands/AGGREGATE.ts @@ -1,6 +1,6 @@ import { RedisCommandArguments } from '@node-redis/client/dist/lib/commands'; import { pushVerdictArgument, transformReplyTuples, TuplesObject } from '@node-redis/client/dist/lib/commands/generic-transformers'; -import { PropertyName, pushArgumentsWithLength, pushSortByArguments, SortByProperty } from '.'; +import { AggregateReply, PropertyName, pushArgumentsWithLength, pushSortByArguments, SortByProperty } from '.'; export enum AggregateSteps { GROUPBY = 'GROUPBY', @@ -118,14 +118,27 @@ type LoadField = PropertyName | { AS?: string; } -interface AggregateOptions { +export interface AggregateOptions { VERBATIM?: true; LOAD?: LoadField | Array; STEPS?: Array; } -export function transformArguments(index: string, query: string, options?: AggregateOptions): RedisCommandArguments { +export function transformArguments( + index: string, + query: string, + options?: AggregateOptions +): RedisCommandArguments { + const args = ['FT.AGGREGATE', index, query]; + pushAggregatehOptions(args, options); + return args; +} + +export function pushAggregatehOptions( + args: RedisCommandArguments, + options?: AggregateOptions +): RedisCommandArguments { if (options?.VERBATIM) { args.push('VERBATIM'); @@ -258,16 +271,11 @@ function pushGroupByReducer(args: RedisCommandArguments, reducer: GroupByReducer } } -type AggregateRawReply = [ +export type AggregateRawReply = [ total: number, ...results: Array> ]; -interface AggregateReply { - total: number; - results: Array; -} - export function transformReply(rawReply: AggregateRawReply): AggregateReply { const results: Array = []; for (let i = 1; i < rawReply.length; i++) { diff --git a/packages/search/lib/commands/ALTER.spec.ts b/packages/search/lib/commands/ALTER.spec.ts new file mode 100644 index 00000000000..e9724757ad7 --- /dev/null +++ b/packages/search/lib/commands/ALTER.spec.ts @@ -0,0 +1,37 @@ +import { strict as assert } from 'assert'; +import testUtils, { GLOBAL } from '../test-utils'; +import { transformArguments } from './ALTER'; +import { SchemaFieldTypes } from '.'; + +describe('ALTER', () => { + describe('transformArguments', () => { + it('with NOINDEX', () => { + assert.deepEqual( + transformArguments('index', { + field: { + type: SchemaFieldTypes.TEXT, + NOINDEX: true, + SORTABLE: 'UNF', + AS: 'text' + } + }), + ['FT.ALTER', 'index', 'SCHEMA', 'ADD', 'field', 'AS', 'text', 'TEXT', 'SORTABLE', 'UNF', 'NOINDEX'] + ); + }); + }); + + testUtils.testWithClient('client.ft.create', async client => { + await Promise.all([ + client.ft.create('index', { + title: SchemaFieldTypes.TEXT + }), + ]); + + assert.equal( + await client.ft.alter('index', { + body: SchemaFieldTypes.TEXT + }), + 'OK' + ); + }, GLOBAL.SERVERS.OPEN); +}); diff --git a/packages/search/lib/commands/ALTER.ts b/packages/search/lib/commands/ALTER.ts new file mode 100644 index 00000000000..8e74c64376e --- /dev/null +++ b/packages/search/lib/commands/ALTER.ts @@ -0,0 +1,10 @@ +import { CreateSchema, pushSchema } from '.'; + +export function transformArguments(index: string, schema: CreateSchema): Array { + const args = ['FT.ALTER', index, 'SCHEMA', 'ADD']; + pushSchema(args, schema); + + return args; +} + +export declare function transformReply(): 'OK'; diff --git a/packages/search/lib/commands/CREATE.spec.ts b/packages/search/lib/commands/CREATE.spec.ts index 5bdf4c93a43..115487b7e88 100644 --- a/packages/search/lib/commands/CREATE.spec.ts +++ b/packages/search/lib/commands/CREATE.spec.ts @@ -1,7 +1,7 @@ import { strict as assert } from 'assert'; import testUtils, { GLOBAL } from '../test-utils'; -import { SchemaFieldTypes, SchemaTextFieldPhonetics, transformArguments } from './CREATE'; -import { RedisSearchLanguages } from '.'; +import { transformArguments } from './CREATE'; +import { SchemaFieldTypes, SchemaTextFieldPhonetics, RedisSearchLanguages } from '.'; describe('CREATE', () => { describe('transformArguments', () => { diff --git a/packages/search/lib/commands/CREATE.ts b/packages/search/lib/commands/CREATE.ts index 1a5e45a4a88..b2f49a7f0e3 100644 --- a/packages/search/lib/commands/CREATE.ts +++ b/packages/search/lib/commands/CREATE.ts @@ -1,49 +1,5 @@ import { pushOptionalVerdictArgument } from '@node-redis/client/dist/lib/commands/generic-transformers'; -import { RedisSearchLanguages, PropertyName } from '.'; - -export enum SchemaFieldTypes { - TEXT = 'TEXT', - NUMERIC = 'NUMERIC', - GEO = 'GEO', - TAG = 'TAG' -} - -type CreateSchemaField> = T | ({ - type: T; - AS?: string; - SORTABLE?: true | 'UNF'; - NOINDEX?: true; -} & E); - -export enum SchemaTextFieldPhonetics { - DM_EN = 'dm:en', - DM_FR = 'dm:fr', - FM_PT = 'dm:pt', - DM_ES = 'dm:es' -} - -type CreateSchemaTextField = CreateSchemaField; - -type CreateSchemaNumericField = CreateSchemaField; - -type CreateSchemaGeoField = CreateSchemaField; - -type CreateSchemaTagField = CreateSchemaField; - -interface CreateSchema { - [field: string]: - CreateSchemaTextField | - CreateSchemaNumericField | - CreateSchemaGeoField | - CreateSchemaTagField -} +import { RedisSearchLanguages, PropertyName, CreateSchema, pushSchema } from '.'; interface CreateOptions { ON?: 'HASH' | 'JSON'; @@ -126,67 +82,8 @@ export function transformArguments(index: string, schema: CreateSchema, options? } pushOptionalVerdictArgument(args, 'STOPWORDS', options?.STOPWORDS); - args.push('SCHEMA'); - - for (const [field, fieldOptions] of Object.entries(schema)) { - args.push(field); - - if (typeof fieldOptions === 'string') { - args.push(fieldOptions); - continue; - } - - if (fieldOptions.AS) { - args.push('AS', fieldOptions.AS); - } - - args.push(fieldOptions.type); - - switch (fieldOptions.type) { - case 'TEXT': - if (fieldOptions.NOSTEM) { - args.push('NOSTEM'); - } - - if (fieldOptions.WEIGHT) { - args.push('WEIGHT', fieldOptions.WEIGHT.toString()); - } - - if (fieldOptions.PHONETIC) { - args.push('PHONETIC', fieldOptions.PHONETIC); - } - - break; - - // case 'NUMERIC': - // case 'GEO': - // break; - - case 'TAG': - if (fieldOptions.SEPERATOR) { - args.push('SEPERATOR', fieldOptions.SEPERATOR); - } - - if (fieldOptions.CASESENSITIVE) { - args.push('CASESENSITIVE'); - } - - break; - } - - if (fieldOptions.SORTABLE) { - args.push('SORTABLE'); - - if (fieldOptions.SORTABLE === 'UNF') { - args.push('UNF'); - } - } - - if (fieldOptions.NOINDEX) { - args.push('NOINDEX'); - } - } + pushSchema(args, schema); return args; } diff --git a/packages/search/lib/commands/DROPINDEX.spec.ts b/packages/search/lib/commands/DROPINDEX.spec.ts index 751e274ba60..b1cb7c93132 100644 --- a/packages/search/lib/commands/DROPINDEX.spec.ts +++ b/packages/search/lib/commands/DROPINDEX.spec.ts @@ -1,6 +1,6 @@ import { strict as assert } from 'assert'; import testUtils, { GLOBAL } from '../test-utils'; -import { SchemaFieldTypes } from './CREATE'; +import { SchemaFieldTypes } from '.'; import { transformArguments } from './DROPINDEX'; describe('DROPINDEX', () => { diff --git a/packages/search/lib/commands/INFO.spec.ts b/packages/search/lib/commands/INFO.spec.ts index fa50a4b0cd8..805d4c820c8 100644 --- a/packages/search/lib/commands/INFO.spec.ts +++ b/packages/search/lib/commands/INFO.spec.ts @@ -1,6 +1,5 @@ import { strict as assert } from 'assert'; import testUtils, { GLOBAL } from '../test-utils'; -import { SchemaFieldTypes } from './CREATE'; import { transformArguments } from './INFO'; describe('INFO', () => { diff --git a/packages/search/lib/commands/PROFILE.ts b/packages/search/lib/commands/PROFILE.ts deleted file mode 100644 index e315ea52304..00000000000 --- a/packages/search/lib/commands/PROFILE.ts +++ /dev/null @@ -1,26 +0,0 @@ -export const IS_READ_ONLY = true; - -interface ProfileOptions { - LIMITED?: true; -} - -export function transformArguments( - index: string, - type: 'SEARCH' | 'AGGREGATE', - query: string, - options?: ProfileOptions -): Array { - const args = ['FT.PROFILE', index, type]; - - if (options?.LIMITED) { - args.push('LIMITED'); - } - - args.push('QUERY', query); - - return args; -} - -export function transformReply() { - -} diff --git a/packages/search/lib/commands/PROFILE_AGGREGATE.spec.ts b/packages/search/lib/commands/PROFILE_AGGREGATE.spec.ts new file mode 100644 index 00000000000..c3d6f990ff7 --- /dev/null +++ b/packages/search/lib/commands/PROFILE_AGGREGATE.spec.ts @@ -0,0 +1,46 @@ +import { strict as assert } from 'assert'; +import testUtils, { GLOBAL } from '../test-utils'; +import { SchemaFieldTypes } from '.'; +import { transformArguments } from './PROFILE_AGGREGATE'; +import { AggregateSteps } from './AGGREGATE'; + +describe('PROFILE AGGREGATE', () => { + describe('transformArguments', () => { + it('without options', () => { + assert.deepEqual( + transformArguments('index', 'query'), + ['FT.PROFILE', 'index', 'AGGREGATE', 'QUERY', 'query'] + ); + }); + + it('with options', () => { + assert.deepEqual( + transformArguments('index', 'query', { + LIMITED: true, + VERBATIM: true, + STEPS: [{ + type: AggregateSteps.SORTBY, + BY: '@by' + }] + }), + ['FT.PROFILE', 'index', 'AGGREGATE', 'LIMITED', 'QUERY', 'query', + 'VERBATIM', 'SORTBY', '1', '@by'] + ); + }); + }); + + testUtils.testWithClient('client.ft.search', async client => { + await Promise.all([ + client.ft.create('index', { + field: SchemaFieldTypes.NUMERIC + }), + client.hSet('1', 'field', '1'), + client.hSet('2', 'field', '2') + ]); + + const res = await client.ft.profileAggregate('index', '*'); + assert.ok(typeof res.profile.iteratorsProfile.counter === 'number'); + assert.ok(typeof res.profile.parsingTime === 'string'); + assert.ok(res.results.total == 1); + }, GLOBAL.SERVERS.OPEN); +}); diff --git a/packages/search/lib/commands/PROFILE_AGGREGATE.ts b/packages/search/lib/commands/PROFILE_AGGREGATE.ts new file mode 100644 index 00000000000..b28e06ade91 --- /dev/null +++ b/packages/search/lib/commands/PROFILE_AGGREGATE.ts @@ -0,0 +1,29 @@ +import { pushAggregatehOptions, AggregateOptions, transformReply as transformAggregateReply, AggregateRawReply } from './AGGREGATE'; +import { ProfileOptions, ProfileRawReply, ProfileReply, transformProfile } from '.'; + +export const IS_READ_ONLY = true; + +export function transformArguments( + index: string, + query: string, + options?: ProfileOptions & AggregateOptions +): Array { + const args = ['FT.PROFILE', index, 'AGGREGATE']; + + if (options?.LIMITED) { + args.push('LIMITED'); + } + + args.push('QUERY', query); + pushAggregatehOptions(args, options) + return args; +} + +type ProfileAggeregateRawReply = ProfileRawReply; + +export function transformReply(reply: ProfileAggeregateRawReply): ProfileReply { + return { + results: transformAggregateReply(reply[0]), + profile: transformProfile(reply[1]) + }; +} diff --git a/packages/search/lib/commands/PROFILE_SEARCH.spec.ts b/packages/search/lib/commands/PROFILE_SEARCH.spec.ts new file mode 100644 index 00000000000..6d7c5adda1e --- /dev/null +++ b/packages/search/lib/commands/PROFILE_SEARCH.spec.ts @@ -0,0 +1,41 @@ +import { strict as assert } from 'assert'; +import testUtils, { GLOBAL } from '../test-utils'; +import { SchemaFieldTypes } from '.'; +import { transformArguments } from './PROFILE_SEARCH'; + +describe('PROFILE SEARCH', () => { + describe('transformArguments', () => { + it('without options', () => { + assert.deepEqual( + transformArguments('index', 'query'), + ['FT.PROFILE', 'index', 'SEARCH', 'QUERY', 'query'] + ); + }); + + it('with options', () => { + assert.deepEqual( + transformArguments('index', 'query', { + LIMITED: true, + VERBATIM: true, + INKEYS: 'key' + }), + ['FT.PROFILE', 'index', 'SEARCH', 'LIMITED', 'QUERY', 'query', + 'VERBATIM', 'INKEYS', '1', 'key'] + ); + }); + }); + + testUtils.testWithClient('client.ft.search', async client => { + await Promise.all([ + client.ft.create('index', { + field: SchemaFieldTypes.NUMERIC + }), + client.hSet('1', 'field', '1') + ]); + + const res = await client.ft.profileSearch('index', '*'); + assert.ok(typeof res.profile.iteratorsProfile.counter === 'number'); + assert.ok(typeof res.profile.parsingTime === 'string'); + assert.ok(res.results.total == 1); + }, GLOBAL.SERVERS.OPEN); +}); diff --git a/packages/search/lib/commands/PROFILE_SEARCH.ts b/packages/search/lib/commands/PROFILE_SEARCH.ts new file mode 100644 index 00000000000..bdb67ec3874 --- /dev/null +++ b/packages/search/lib/commands/PROFILE_SEARCH.ts @@ -0,0 +1,29 @@ +import { SearchOptions, SearchRawReply, transformReply as transformSearchReply } from './SEARCH'; +import { pushSearchOptions, ProfileOptions, ProfileRawReply, ProfileReply, transformProfile } from '.'; + +export const IS_READ_ONLY = true; + +export function transformArguments( + index: string, + query: string, + options?: ProfileOptions & SearchOptions +): Array { + const args = ['FT.PROFILE', index, 'SEARCH']; + + if (options?.LIMITED) { + args.push('LIMITED'); + } + + args.push('QUERY', query); + pushSearchOptions(args, options) + return args; +} + +type ProfileSearchRawReply = ProfileRawReply; + +export function transformReply(reply: ProfileSearchRawReply): ProfileReply { + return { + results: transformSearchReply(reply[0]), + profile: transformProfile(reply[1]) + }; +} diff --git a/packages/search/lib/commands/SEARCH.spec.ts b/packages/search/lib/commands/SEARCH.spec.ts index a4d75dd895f..c2f2c295c8f 100644 --- a/packages/search/lib/commands/SEARCH.spec.ts +++ b/packages/search/lib/commands/SEARCH.spec.ts @@ -1,7 +1,6 @@ import { strict as assert } from 'assert'; -import { RedisSearchLanguages } from '.'; +import { RedisSearchLanguages, SchemaFieldTypes } from '.'; import testUtils, { GLOBAL } from '../test-utils'; -import { SchemaFieldTypes } from './CREATE'; import { transformArguments } from './SEARCH'; describe('SEARCH', () => { diff --git a/packages/search/lib/commands/SEARCH.ts b/packages/search/lib/commands/SEARCH.ts index 34d255e5b2c..c289f6fc277 100644 --- a/packages/search/lib/commands/SEARCH.ts +++ b/packages/search/lib/commands/SEARCH.ts @@ -1,12 +1,12 @@ import { RedisCommandArguments } from '@node-redis/client/dist/lib/commands'; -import { pushOptionalVerdictArgument, pushVerdictArgument, transformReplyTuples } from '@node-redis/client/dist/lib/commands/generic-transformers'; -import { RedisSearchLanguages, PropertyName, pushSortByProperty, SortByProperty } from '.'; +import { transformReplyTuples } from '@node-redis/client/dist/lib/commands/generic-transformers'; +import { pushSearchOptions, RedisSearchLanguages, PropertyName, SortByProperty, SearchReply } from '.'; export const FIRST_KEY_INDEX = 1; export const IS_READ_ONLY = true; -interface SearchOptions { +export interface SearchOptions { // NOCONTENT?: true; TODO VERBATIM?: true; NOSTOPWORDS?: true; @@ -62,126 +62,13 @@ export function transformArguments( options?: SearchOptions ): RedisCommandArguments { const args: RedisCommandArguments = ['FT.SEARCH', index, query]; - - if (options?.VERBATIM) { - args.push('VERBATIM'); - } - - if (options?.NOSTOPWORDS) { - args.push('NOSTOPWORDS'); - } - - // if (options?.WITHSCORES) { - // args.push('WITHSCORES'); - // } - - // if (options?.WITHPAYLOADS) { - // args.push('WITHPAYLOADS'); - // } - - pushOptionalVerdictArgument(args, 'INKEYS', options?.INKEYS); - pushOptionalVerdictArgument(args, 'INFIELDS', options?.INFIELDS); - pushOptionalVerdictArgument(args, 'RETURN', options?.RETURN); - - if (options?.SUMMARIZE) { - args.push('SUMMARIZE'); - - if (typeof options.SUMMARIZE === 'object') { - if (options.SUMMARIZE.FIELDS) { - args.push('FIELDS'); - pushVerdictArgument(args, options.SUMMARIZE.FIELDS); - } - - if (options.SUMMARIZE.FRAGS) { - args.push('FRAGS', options.SUMMARIZE.FRAGS.toString()); - } - - if (options.SUMMARIZE.LEN) { - args.push('LEN', options.SUMMARIZE.LEN.toString()); - } - - if (options.SUMMARIZE.SEPARATOR) { - args.push('SEPARATOR', options.SUMMARIZE.SEPARATOR); - } - } - } - - if (options?.HIGHLIGHT) { - args.push('HIGHLIGHT'); - - if (typeof options.HIGHLIGHT === 'object') { - if (options.HIGHLIGHT.FIELDS) { - args.push('FIELDS'); - pushVerdictArgument(args, options.HIGHLIGHT.FIELDS); - } - - if (options.HIGHLIGHT.TAGS) { - args.push('TAGS', options.HIGHLIGHT.TAGS.open, options.HIGHLIGHT.TAGS.close); - } - } - } - - if (options?.SLOP) { - args.push('SLOP', options.SLOP.toString()); - } - - if (options?.INORDER) { - args.push('INORDER'); - } - - if (options?.LANGUAGE) { - args.push('LANGUAGE', options.LANGUAGE); - } - - if (options?.EXPANDER) { - args.push('EXPANDER', options.EXPANDER); - } - - if (options?.SCORER) { - args.push('SCORER', options.SCORER); - } - - // if (options?.EXPLAINSCORE) { - // args.push('EXPLAINSCORE'); - // } - - // if (options?.PAYLOAD) { - // args.push('PAYLOAD', options.PAYLOAD); - // } - - if (options?.SORTBY) { - args.push('SORTBY'); - pushSortByProperty(args, options.SORTBY); - } - - // if (options?.MSORTBY) { - // pushSortByArguments(args, 'MSORTBY', options.MSORTBY); - // } - - if (options?.LIMIT) { - args.push( - 'LIMIT', - options.LIMIT.from.toString(), - options.LIMIT.size.toString() - ); - } - + pushSearchOptions(args, options); return args; } -interface SearchDocumentValue { - [key: string]: string | number | null | Array | SearchDocumentValue; -} - -interface SearchReply { - total: number; - documents: Array<{ - id: string; - value: SearchDocumentValue; - }>; -} +export type SearchRawReply = Array; -export function transformReply(reply: Array): SearchReply { +export function transformReply(reply: SearchRawReply): SearchReply { const documents = []; for (let i = 1; i < reply.length; i += 2) { const tuples = reply[i + 1]; diff --git a/packages/search/lib/commands/SPELLCHECK.spec.ts b/packages/search/lib/commands/SPELLCHECK.spec.ts index bacbe118b38..fe74f5910f5 100644 --- a/packages/search/lib/commands/SPELLCHECK.spec.ts +++ b/packages/search/lib/commands/SPELLCHECK.spec.ts @@ -1,6 +1,6 @@ import { strict as assert } from 'assert'; import testUtils, { GLOBAL } from '../test-utils'; -import { SchemaFieldTypes } from './CREATE'; +import { SchemaFieldTypes } from '.'; import { transformArguments } from './SPELLCHECK'; describe('SPELLCHECK', () => { diff --git a/packages/search/lib/commands/TAGVALS.spec.ts b/packages/search/lib/commands/TAGVALS.spec.ts index 1f90939bb0d..d59bfcfe3ea 100644 --- a/packages/search/lib/commands/TAGVALS.spec.ts +++ b/packages/search/lib/commands/TAGVALS.spec.ts @@ -1,6 +1,6 @@ import { strict as assert } from 'assert'; import testUtils, { GLOBAL } from '../test-utils'; -import { SchemaFieldTypes } from './CREATE'; +import { SchemaFieldTypes } from '.'; import { transformArguments } from './TAGVALS'; describe('TAGVALS', () => { diff --git a/packages/search/lib/commands/index.ts b/packages/search/lib/commands/index.ts index d7b2b793438..f148adc08f5 100644 --- a/packages/search/lib/commands/index.ts +++ b/packages/search/lib/commands/index.ts @@ -1,4 +1,5 @@ import * as _LIST from './_LIST'; +import * as ALTER from './ALTER'; import * as AGGREGATE from './AGGREGATE'; import * as ALIASADD from './ALIASADD'; import * as ALIASDEL from './ALIASDEL'; @@ -13,7 +14,8 @@ import * as DROPINDEX from './DROPINDEX'; import * as EXPLAIN from './EXPLAIN'; import * as EXPLAINCLI from './EXPLAINCLI'; import * as INFO from './INFO'; -// import * as PROFILE from './PROFILE'; +import * as PROFILESEARCH from './PROFILE_SEARCH'; +import * as PROFILEAGGREGATE from './PROFILE_AGGREGATE'; import * as SEARCH from './SEARCH'; import * as SPELLCHECK from './SPELLCHECK'; import * as SUGADD from './SUGADD'; @@ -27,10 +29,15 @@ import * as SYNDUMP from './SYNDUMP'; import * as SYNUPDATE from './SYNUPDATE'; import * as TAGVALS from './TAGVALS'; import { RedisCommandArguments } from '@node-redis/client/dist/lib/commands'; +import { pushOptionalVerdictArgument, pushVerdictArgument, TuplesObject } from '@node-redis/client/dist/lib/commands/generic-transformers'; +import internal = require('stream'); +import { SearchOptions } from './SEARCH'; export default { _LIST, _list: _LIST, + ALTER, + alter: ALTER, AGGREGATE, aggregate: AGGREGATE, ALIASADD, @@ -59,8 +66,10 @@ export default { explainCli: EXPLAINCLI, INFO, info: INFO, - // PROFILE, - // profile: PROFILE, + PROFILESEARCH, + profileSearch: PROFILESEARCH, + PROFILEAGGREGATE, + profileAggregate: PROFILEAGGREGATE, SEARCH, search: SEARCH, SPELLCHECK, @@ -159,3 +168,347 @@ export function pushArgumentsWithLength(args: RedisCommandArguments, fn: (args: args[lengthIndex] = (args.length - lengthIndex - 1).toString(); return args; } + +export enum SchemaFieldTypes { + TEXT = 'TEXT', + NUMERIC = 'NUMERIC', + GEO = 'GEO', + TAG = 'TAG' +} + +type CreateSchemaField> = T | ({ + type: T; + AS?: string; + SORTABLE?: true | 'UNF'; + NOINDEX?: true; +} & E); + +export enum SchemaTextFieldPhonetics { + DM_EN = 'dm:en', + DM_FR = 'dm:fr', + FM_PT = 'dm:pt', + DM_ES = 'dm:es' +} + +type CreateSchemaTextField = CreateSchemaField; + +type CreateSchemaNumericField = CreateSchemaField; + +type CreateSchemaGeoField = CreateSchemaField; + +type CreateSchemaTagField = CreateSchemaField; + +export interface CreateSchema { + [field: string]: + CreateSchemaTextField | + CreateSchemaNumericField | + CreateSchemaGeoField | + CreateSchemaTagField +} + +export function pushSchema(args: RedisCommandArguments, schema: CreateSchema) { + for (const [field, fieldOptions] of Object.entries(schema)) { + args.push(field); + + if (typeof fieldOptions === 'string') { + args.push(fieldOptions); + continue; + } + + if (fieldOptions.AS) { + args.push('AS', fieldOptions.AS); + } + + args.push(fieldOptions.type); + + switch (fieldOptions.type) { + case 'TEXT': + if (fieldOptions.NOSTEM) { + args.push('NOSTEM'); + } + + if (fieldOptions.WEIGHT) { + args.push('WEIGHT', fieldOptions.WEIGHT.toString()); + } + + if (fieldOptions.PHONETIC) { + args.push('PHONETIC', fieldOptions.PHONETIC); + } + + break; + + // case 'NUMERIC': + // case 'GEO': + // break; + + case 'TAG': + if (fieldOptions.SEPERATOR) { + args.push('SEPERATOR', fieldOptions.SEPERATOR); + } + + if (fieldOptions.CASESENSITIVE) { + args.push('CASESENSITIVE'); + } + + break; + } + + if (fieldOptions.SORTABLE) { + args.push('SORTABLE'); + + if (fieldOptions.SORTABLE === 'UNF') { + args.push('UNF'); + } + } + + if (fieldOptions.NOINDEX) { + args.push('NOINDEX'); + } + } +} + +export function pushSearchOptions( + args: RedisCommandArguments, + options?: SearchOptions +): RedisCommandArguments { + + if (options?.VERBATIM) { + args.push('VERBATIM'); + } + + if (options?.NOSTOPWORDS) { + args.push('NOSTOPWORDS'); + } + + // if (options?.WITHSCORES) { + // args.push('WITHSCORES'); + // } + + // if (options?.WITHPAYLOADS) { + // args.push('WITHPAYLOADS'); + // } + + pushOptionalVerdictArgument(args, 'INKEYS', options?.INKEYS); + pushOptionalVerdictArgument(args, 'INFIELDS', options?.INFIELDS); + pushOptionalVerdictArgument(args, 'RETURN', options?.RETURN); + + if (options?.SUMMARIZE) { + args.push('SUMMARIZE'); + + if (typeof options.SUMMARIZE === 'object') { + if (options.SUMMARIZE.FIELDS) { + args.push('FIELDS'); + pushVerdictArgument(args, options.SUMMARIZE.FIELDS); + } + + if (options.SUMMARIZE.FRAGS) { + args.push('FRAGS', options.SUMMARIZE.FRAGS.toString()); + } + + if (options.SUMMARIZE.LEN) { + args.push('LEN', options.SUMMARIZE.LEN.toString()); + } + + if (options.SUMMARIZE.SEPARATOR) { + args.push('SEPARATOR', options.SUMMARIZE.SEPARATOR); + } + } + } + + if (options?.HIGHLIGHT) { + args.push('HIGHLIGHT'); + + if (typeof options.HIGHLIGHT === 'object') { + if (options.HIGHLIGHT.FIELDS) { + args.push('FIELDS'); + pushVerdictArgument(args, options.HIGHLIGHT.FIELDS); + } + + if (options.HIGHLIGHT.TAGS) { + args.push('TAGS', options.HIGHLIGHT.TAGS.open, options.HIGHLIGHT.TAGS.close); + } + } + } + + if (options?.SLOP) { + args.push('SLOP', options.SLOP.toString()); + } + + if (options?.INORDER) { + args.push('INORDER'); + } + + if (options?.LANGUAGE) { + args.push('LANGUAGE', options.LANGUAGE); + } + + if (options?.EXPANDER) { + args.push('EXPANDER', options.EXPANDER); + } + + if (options?.SCORER) { + args.push('SCORER', options.SCORER); + } + + // if (options?.EXPLAINSCORE) { + // args.push('EXPLAINSCORE'); + // } + + // if (options?.PAYLOAD) { + // args.push('PAYLOAD', options.PAYLOAD); + // } + + if (options?.SORTBY) { + args.push('SORTBY'); + pushSortByProperty(args, options.SORTBY); + } + + // if (options?.MSORTBY) { + // pushSortByArguments(args, 'MSORTBY', options.MSORTBY); + // } + + if (options?.LIMIT) { + args.push( + 'LIMIT', + options.LIMIT.from.toString(), + options.LIMIT.size.toString() + ); + } + + return args; +} + +interface SearchDocumentValue { + [key: string]: string | number | null | Array | SearchDocumentValue; +} + +export interface SearchReply { + total: number; + documents: Array<{ + id: string; + value: SearchDocumentValue; + }>; +} + +export interface AggregateReply { + total: number; + results: Array; +} + +export interface ProfileOptions { + LIMITED?: true; +} + +export type ProfileRawReply = [ + results: T, + profile: [ + _: string, + TotalProfileTime: string, + _: string, + ParsingTime: string, + _: string, + PipelineCreationTime: string, + _: string, + IteratorsProfile: Array + ] +]; + +export interface ProfileReply { + results: SearchReply | AggregateReply, + profile: ProfileData +} + +interface ChildIterator { + type?: string, + counter?: number, + term?: string, + size?: number, + time?: string, + childIterators?: Array +} + +interface IteratorsProfile { + type?: string, + counter?: number, + queryType?: string, + time?: string, + childIterators?: Array +} + +interface ProfileData { + totalProfileTime: string, + parsingTime: string, + pipelineCreationTime: string, + iteratorsProfile: IteratorsProfile +} + +export function transformProfile(reply: Array): ProfileData{ + return { + totalProfileTime: reply[0][1], + parsingTime: reply[1][1], + pipelineCreationTime: reply[2][1], + iteratorsProfile: transformIterators(reply[3][1]) + }; +} + +function transformIterators(IteratorsProfile: Array): IteratorsProfile { + var res: IteratorsProfile = {}; + for (let i = 0; i < IteratorsProfile.length; i += 2) { + const value = IteratorsProfile[i+1]; + switch (IteratorsProfile[i]) { + case 'Type': + res.type = value; + break; + case 'Counter': + res.counter = value; + break; + case 'Time': + res.time = value; + break; + case 'Query type': + res.queryType = value; + break; + case 'Child iterators': + res.childIterators = value.map(transformChildIterators); + break; + } + } + + return res; +} + +function transformChildIterators(IteratorsProfile: Array): ChildIterator { + var res: ChildIterator = {}; + for (let i = 1; i < IteratorsProfile.length; i += 2) { + const value = IteratorsProfile[i+1]; + switch (IteratorsProfile[i]) { + case 'Type': + res.type = value; + break; + case 'Counter': + res.counter = value; + break; + case 'Time': + res.time = value; + break; + case 'Size': + res.size = value; + break; + case 'Term': + res.term = value; + break; + case 'Child iterators': + res.childIterators = value.map(transformChildIterators); + break; + } + } + + return res; +} diff --git a/packages/search/lib/index.ts b/packages/search/lib/index.ts index 9e0be7b169c..9480c5a7a7f 100644 --- a/packages/search/lib/index.ts +++ b/packages/search/lib/index.ts @@ -1,4 +1,4 @@ export { default } from './commands'; -export { SchemaFieldTypes, SchemaTextFieldPhonetics } from './commands/CREATE'; +export { SchemaFieldTypes, SchemaTextFieldPhonetics } from './commands'; export { AggregateSteps, AggregateGroupByReducers } from './commands/AGGREGATE'; From aa63580b4b55e5055149a9be213d7f4e8bd1a97f Mon Sep 17 00:00:00 2001 From: leibale Date: Mon, 13 Dec 2021 14:20:46 -0500 Subject: [PATCH 1043/1748] clean code --- packages/client/lib/client/index.spec.ts | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/packages/client/lib/client/index.spec.ts b/packages/client/lib/client/index.spec.ts index fbe38adafc5..c5e05b7858c 100644 --- a/packages/client/lib/client/index.spec.ts +++ b/packages/client/lib/client/index.spec.ts @@ -3,7 +3,7 @@ import testUtils, { GLOBAL, waitTillBeenCalled } from '../test-utils'; import RedisClient, { ClientLegacyCommandArguments, RedisClientType } from '.'; import { RedisClientMultiCommandType } from './multi-command'; import { RedisCommandArguments, RedisCommandRawReply, RedisModules, RedisScripts } from '../commands'; -import { AbortError, ClientClosedError, ConnectionTimeoutError, DisconnectsClientError, SocketClosedUnexpectedlyError, WatchError } from '../errors'; +import { AbortError, AuthError, ClientClosedError, ConnectionTimeoutError, DisconnectsClientError, SocketClosedUnexpectedlyError, WatchError } from '../errors'; import { defineScript } from '../lua-script'; import { spy } from 'sinon'; import { once } from 'events'; @@ -99,7 +99,7 @@ describe('Client', () => { await assert.rejects( client.connect(), - { message } + new AuthError(message) ); assert.equal(client.isOpen, false); From 7565ae349df49fbe8348aa0b74dba1f7a805fae5 Mon Sep 17 00:00:00 2001 From: leibale Date: Mon, 13 Dec 2021 14:21:02 -0500 Subject: [PATCH 1044/1748] fix generated documentation --- .github/workflows/documentation.yml | 8 +- .gitignore | 1 + README.md | 12 +- package-lock.json | 1926 ++++++++++++++------------- package.json | 8 +- packages/client/.gitignore | 1 - packages/client/package.json | 14 +- packages/client/tsconfig.json | 3 +- packages/json/package.json | 10 +- packages/json/tsconfig.json | 13 +- packages/search/package.json | 10 +- packages/search/tsconfig.json | 13 +- packages/test-utils/package.json | 8 +- packages/time-series/package.json | 14 +- packages/time-series/tsconfig.json | 13 +- 15 files changed, 1109 insertions(+), 945 deletions(-) delete mode 100644 packages/client/.gitignore diff --git a/.github/workflows/documentation.yml b/.github/workflows/documentation.yml index 861010b7f01..e07848a6433 100644 --- a/.github/workflows/documentation.yml +++ b/.github/workflows/documentation.yml @@ -19,9 +19,5 @@ jobs: run: npm ci - name: Generate Documentation run: npm run documentation - - name: Upload Documentation to Wiki - uses: SwiftDocOrg/github-wiki-publish-action@v1 - with: - path: documentation - env: - GH_PERSONAL_ACCESS_TOKEN: ${{ secrets.BOT_PERSONAL_ACCESS_TOKEN }} + - name: Upload + run: npm run gh-pages diff --git a/.gitignore b/.gitignore index 9ee58bfbd30..dfd47ff6716 100644 --- a/.gitignore +++ b/.gitignore @@ -6,3 +6,4 @@ dist/ node_modules/ .DS_Store dump.rdb +documentation/ diff --git a/README.md b/README.md index 28add55210b..a0c98a07d8b 100644 --- a/README.md +++ b/README.md @@ -307,12 +307,12 @@ Node Redis is supported with the following versions of Redis: ## Packages -| Name | Description | -|-----------------------------------------|--------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------| -| [redis](./) | [![Downloads](https://img.shields.io/npm/dm/redis.svg)](https://www.npmjs.com/package/redis) [![Version](https://img.shields.io/npm/v/redis.svg)](https://www.npmjs.com/package/redis) | -| [@node-redis/client](./packages/client) | [![Downloads](https://img.shields.io/npm/dm/@node-redis/client.svg)](https://www.npmjs.com/package/@node-redis/client) [![Version](https://img.shields.io/npm/v/@node-redis/client.svg)](https://www.npmjs.com/package/@node-redis/client) | -| [@node-redis/json](./packages/json) | [![Downloads](https://img.shields.io/npm/dm/@node-redis/json.svg)](https://www.npmjs.com/package/@node-redis/json) [![Version](https://img.shields.io/npm/v/@node-redis/json.svg)](https://www.npmjs.com/package/@node-redis/json) [Redis JSON](https://oss.redis.com/redisjson/) commands | -| [@node-redis/search](./packages/search) | [![Downloads](https://img.shields.io/npm/dm/@node-redis/search.svg)](https://www.npmjs.com/package/@node-redis/search) [![Version](https://img.shields.io/npm/v/@node-redis/search.svg)](https://www.npmjs.com/package/@node-redis/search) [Redis Search](https://oss.redis.com/redisearch/) commands | +| Name | Description | +|-----------------------------------------|-----------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------| +| [redis](./) | [![Downloads](https://img.shields.io/npm/dm/redis.svg)](https://www.npmjs.com/package/redis) [![Version](https://img.shields.io/npm/v/redis.svg)](https://www.npmjs.com/package/redis) | +| [@node-redis/client](./packages/client) | [![Downloads](https://img.shields.io/npm/dm/@node-redis/client.svg)](https://www.npmjs.com/package/@node-redis/client) [![Version](https://img.shields.io/npm/v/@node-redis/client.svg)](https://www.npmjs.com/package/@node-redis/client) [![Docs](https://img.shields.io/badge/-documentation-dc382c)](https://redis.js.org/documentation/client/) | +| [@node-redis/json](./packages/json) | [![Downloads](https://img.shields.io/npm/dm/@node-redis/json.svg)](https://www.npmjs.com/package/@node-redis/json) [![Version](https://img.shields.io/npm/v/@node-redis/json.svg)](https://www.npmjs.com/package/@node-redis/json) [![Docs](https://img.shields.io/badge/-documentation-dc382c)](https://redis.js.org/documentation/json/) [Redis JSON](https://oss.redis.com/redisjson/) commands | +| [@node-redis/search](./packages/search) | [![Downloads](https://img.shields.io/npm/dm/@node-redis/search.svg)](https://www.npmjs.com/package/@node-redis/search) [![Version](https://img.shields.io/npm/v/@node-redis/search.svg)](https://www.npmjs.com/package/@node-redis/search) [![Docs](https://img.shields.io/badge/-documentation-dc382c)](https://redis.js.org/documentation/search/) [Redis Search](https://oss.redis.com/redisearch/) commands | ## Contributing diff --git a/package-lock.json b/package-lock.json index 493d3736966..c133c867cef 100644 --- a/package-lock.json +++ b/package-lock.json @@ -18,8 +18,13 @@ }, "devDependencies": { "@tsconfig/node12": "^1.0.9", + "gh-pages": "^3.2.3", "release-it": "^14.11.8", - "typescript": "^4.5.2" + "typedoc": "^0.22.10", + "typescript": "^4.5.3" + }, + "engines": { + "npm": ">=7" } }, "node_modules/@babel/code-frame": { @@ -73,15 +78,6 @@ "url": "https://opencollective.com/babel" } }, - "node_modules/@babel/core/node_modules/semver": { - "version": "6.3.0", - "resolved": "https://registry.npmjs.org/semver/-/semver-6.3.0.tgz", - "integrity": "sha512-b39TBaTSfV6yBrapU89p5fKekE2m/NwnDocOVruQFS1/veMgdzuPcnOM34M6CwxW8jH/lxEa5rBoDeUwu5HHTw==", - "dev": true, - "bin": { - "semver": "bin/semver.js" - } - }, "node_modules/@babel/generator": { "version": "7.16.0", "resolved": "https://registry.npmjs.org/@babel/generator/-/generator-7.16.0.tgz", @@ -114,15 +110,6 @@ "@babel/core": "^7.0.0" } }, - "node_modules/@babel/helper-compilation-targets/node_modules/semver": { - "version": "6.3.0", - "resolved": "https://registry.npmjs.org/semver/-/semver-6.3.0.tgz", - "integrity": "sha512-b39TBaTSfV6yBrapU89p5fKekE2m/NwnDocOVruQFS1/veMgdzuPcnOM34M6CwxW8jH/lxEa5rBoDeUwu5HHTw==", - "dev": true, - "bin": { - "semver": "bin/semver.js" - } - }, "node_modules/@babel/helper-function-name": { "version": "7.16.0", "resolved": "https://registry.npmjs.org/@babel/helper-function-name/-/helper-function-name-7.16.0.tgz", @@ -453,14 +440,14 @@ } }, "node_modules/@eslint/eslintrc": { - "version": "1.0.4", - "resolved": "https://registry.npmjs.org/@eslint/eslintrc/-/eslintrc-1.0.4.tgz", - "integrity": "sha512-h8Vx6MdxwWI2WM8/zREHMoqdgLNXEL4QX3MWSVMdyNJGvXVOs+6lp+m2hc3FnuMHDc4poxFNI20vCk0OmI4G0Q==", + "version": "1.0.5", + "resolved": "https://registry.npmjs.org/@eslint/eslintrc/-/eslintrc-1.0.5.tgz", + "integrity": "sha512-BLxsnmK3KyPunz5wmCCpqy0YelEoxxGmH73Is+Z74oOTMtExcjkr3dDR6quwrjh1YspA8DH9gnX1o069KiS9AQ==", "dev": true, "dependencies": { "ajv": "^6.12.4", "debug": "^4.3.2", - "espree": "^9.0.0", + "espree": "^9.2.0", "globals": "^13.9.0", "ignore": "^4.0.6", "import-fresh": "^3.2.1", @@ -494,12 +481,12 @@ } }, "node_modules/@humanwhocodes/config-array": { - "version": "0.6.0", - "resolved": "https://registry.npmjs.org/@humanwhocodes/config-array/-/config-array-0.6.0.tgz", - "integrity": "sha512-JQlEKbcgEUjBFhLIF4iqM7u/9lwgHRBcpHrmUNCALK0Q3amXN6lxdoXLnF0sm11E9VqTmBALR87IlUg1bZ8A9A==", + "version": "0.9.2", + "resolved": "https://registry.npmjs.org/@humanwhocodes/config-array/-/config-array-0.9.2.tgz", + "integrity": "sha512-UXOuFCGcwciWckOpmfKDq/GyhlTf9pN/BzG//x8p8zTOFEcGuA68ANXheFS0AGvy3qgZqLBUkMs7hqzqCKOVwA==", "dev": true, "dependencies": { - "@humanwhocodes/object-schema": "^1.2.0", + "@humanwhocodes/object-schema": "^1.2.1", "debug": "^4.1.1", "minimatch": "^3.0.4" }, @@ -553,19 +540,6 @@ "node": ">=6" } }, - "node_modules/@istanbuljs/load-nyc-config/node_modules/find-up": { - "version": "4.1.0", - "resolved": "https://registry.npmjs.org/find-up/-/find-up-4.1.0.tgz", - "integrity": "sha512-PpOwAdQ/YlXQ2vj8a3h8IipDuYRi3wceVQQGYWxNINccq40Anw7BlsEXCMbt1Zt+OLA6Fq9suIpIWD0OsnISlw==", - "dev": true, - "dependencies": { - "locate-path": "^5.0.0", - "path-exists": "^4.0.0" - }, - "engines": { - "node": ">=8" - } - }, "node_modules/@istanbuljs/load-nyc-config/node_modules/js-yaml": { "version": "3.14.1", "resolved": "https://registry.npmjs.org/js-yaml/-/js-yaml-3.14.1.tgz", @@ -579,45 +553,6 @@ "js-yaml": "bin/js-yaml.js" } }, - "node_modules/@istanbuljs/load-nyc-config/node_modules/locate-path": { - "version": "5.0.0", - "resolved": "https://registry.npmjs.org/locate-path/-/locate-path-5.0.0.tgz", - "integrity": "sha512-t7hw9pI+WvuwNJXwk5zVHpyhIqzg2qTlklJOf0mVxGSbe3Fp2VieZcduNYjaLDoy6p9uGpQEGWG87WpMKlNq8g==", - "dev": true, - "dependencies": { - "p-locate": "^4.1.0" - }, - "engines": { - "node": ">=8" - } - }, - "node_modules/@istanbuljs/load-nyc-config/node_modules/p-limit": { - "version": "2.3.0", - "resolved": "https://registry.npmjs.org/p-limit/-/p-limit-2.3.0.tgz", - "integrity": "sha512-//88mFWSJx8lxCzwdAABTJL2MyWB12+eIY7MDL2SqLmAkeKU9qxRvWuSyTjm3FUmpBEMuFfckAIqEaVGUDxb6w==", - "dev": true, - "dependencies": { - "p-try": "^2.0.0" - }, - "engines": { - "node": ">=6" - }, - "funding": { - "url": "https://github.com/sponsors/sindresorhus" - } - }, - "node_modules/@istanbuljs/load-nyc-config/node_modules/p-locate": { - "version": "4.1.0", - "resolved": "https://registry.npmjs.org/p-locate/-/p-locate-4.1.0.tgz", - "integrity": "sha512-R79ZZ/0wAxKGu3oYMlz8jy/kbhsNrS7SKZ7PxEHBgJ5+F2mtFW2fK2cOtBh1cHYkQsbzFV7I+EoRKe6Yt0oK7A==", - "dev": true, - "dependencies": { - "p-limit": "^2.2.0" - }, - "engines": { - "node": ">=8" - } - }, "node_modules/@istanbuljs/load-nyc-config/node_modules/resolve-from": { "version": "5.0.0", "resolved": "https://registry.npmjs.org/resolve-from/-/resolve-from-5.0.0.tgz", @@ -628,9 +563,9 @@ } }, "node_modules/@istanbuljs/nyc-config-typescript": { - "version": "1.0.1", - "resolved": "https://registry.npmjs.org/@istanbuljs/nyc-config-typescript/-/nyc-config-typescript-1.0.1.tgz", - "integrity": "sha512-/gz6LgVpky205LuoOfwEZmnUtaSmdk0QIMcNFj9OvxhiMhPpKftMgZmGN7jNj7jR+lr8IB1Yks3QSSSNSxfoaQ==", + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/@istanbuljs/nyc-config-typescript/-/nyc-config-typescript-1.0.2.tgz", + "integrity": "sha512-iKGIyMoyJuFnJRSVTZ78POIRvNnwZaWIf8vG4ZS3rQq58MMDrqEX2nnzx0R28V2X8JvmKYiqY9FP2hlJsm8A0w==", "dev": true, "dependencies": { "@istanbuljs/schema": "^0.1.2" @@ -639,9 +574,7 @@ "node": ">=8" }, "peerDependencies": { - "nyc": ">=15", - "source-map-support": "*", - "ts-node": "*" + "nyc": ">=15" } }, "node_modules/@istanbuljs/schema": { @@ -669,6 +602,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", @@ -959,9 +896,9 @@ "dev": true }, "node_modules/@types/node": { - "version": "16.11.10", - "resolved": "https://registry.npmjs.org/@types/node/-/node-16.11.10.tgz", - "integrity": "sha512-3aRnHa1KlOEEhJ6+CvyHKK5vE9BcLGjtUpwvqYLRvYNQKMfabu3BwfJaA/SLW8dxe28LsNDjtHwePTuzn3gmOA==", + "version": "16.11.12", + "resolved": "https://registry.npmjs.org/@types/node/-/node-16.11.12.tgz", + "integrity": "sha512-+2Iggwg7PxoO5Kyhvsq9VarmPbIelXP070HMImEpbtGCoyWNINQj4wzjbQCXzdHTRXnqufutJb5KAURZANNBAw==", "dev": true }, "node_modules/@types/parse-json": { @@ -1026,13 +963,13 @@ "dev": true }, "node_modules/@typescript-eslint/eslint-plugin": { - "version": "5.4.0", - "resolved": "https://registry.npmjs.org/@typescript-eslint/eslint-plugin/-/eslint-plugin-5.4.0.tgz", - "integrity": "sha512-9/yPSBlwzsetCsGEn9j24D8vGQgJkOTr4oMLas/w886ZtzKIs1iyoqFrwsX2fqYEeUwsdBpC21gcjRGo57u0eg==", + "version": "5.7.0", + "resolved": "https://registry.npmjs.org/@typescript-eslint/eslint-plugin/-/eslint-plugin-5.7.0.tgz", + "integrity": "sha512-8RTGBpNn5a9M628wBPrCbJ+v3YTEOE2qeZb7TDkGKTDXSj36KGRg92SpFFaR/0S3rSXQxM0Og/kV9EyadsYSBg==", "dev": true, "dependencies": { - "@typescript-eslint/experimental-utils": "5.4.0", - "@typescript-eslint/scope-manager": "5.4.0", + "@typescript-eslint/experimental-utils": "5.7.0", + "@typescript-eslint/scope-manager": "5.7.0", "debug": "^4.3.2", "functional-red-black-tree": "^1.0.1", "ignore": "^5.1.8", @@ -1057,16 +994,31 @@ } } }, + "node_modules/@typescript-eslint/eslint-plugin/node_modules/semver": { + "version": "7.3.5", + "resolved": "https://registry.npmjs.org/semver/-/semver-7.3.5.tgz", + "integrity": "sha512-PoeGJYh8HK4BTO/a9Tf6ZG3veo/A7ZVsYrSA6J8ny9nb3B1VrpkuN+z9OE5wfE5p6H4LchYZsegiQgbJD94ZFQ==", + "dev": true, + "dependencies": { + "lru-cache": "^6.0.0" + }, + "bin": { + "semver": "bin/semver.js" + }, + "engines": { + "node": ">=10" + } + }, "node_modules/@typescript-eslint/experimental-utils": { - "version": "5.4.0", - "resolved": "https://registry.npmjs.org/@typescript-eslint/experimental-utils/-/experimental-utils-5.4.0.tgz", - "integrity": "sha512-Nz2JDIQUdmIGd6p33A+naQmwfkU5KVTLb/5lTk+tLVTDacZKoGQisj8UCxk7onJcrgjIvr8xWqkYI+DbI3TfXg==", + "version": "5.7.0", + "resolved": "https://registry.npmjs.org/@typescript-eslint/experimental-utils/-/experimental-utils-5.7.0.tgz", + "integrity": "sha512-u57eZ5FbEpzN5kSjmVrSesovWslH2ZyNPnaXQMXWgH57d5+EVHEt76W75vVuI9qKZ5BMDKNfRN+pxcPEjQjb2A==", "dev": true, "dependencies": { "@types/json-schema": "^7.0.9", - "@typescript-eslint/scope-manager": "5.4.0", - "@typescript-eslint/types": "5.4.0", - "@typescript-eslint/typescript-estree": "5.4.0", + "@typescript-eslint/scope-manager": "5.7.0", + "@typescript-eslint/types": "5.7.0", + "@typescript-eslint/typescript-estree": "5.7.0", "eslint-scope": "^5.1.1", "eslint-utils": "^3.0.0" }, @@ -1082,14 +1034,14 @@ } }, "node_modules/@typescript-eslint/parser": { - "version": "5.4.0", - "resolved": "https://registry.npmjs.org/@typescript-eslint/parser/-/parser-5.4.0.tgz", - "integrity": "sha512-JoB41EmxiYpaEsRwpZEYAJ9XQURPFer8hpkIW9GiaspVLX8oqbqNM8P4EP8HOZg96yaALiLEVWllA2E8vwsIKw==", + "version": "5.7.0", + "resolved": "https://registry.npmjs.org/@typescript-eslint/parser/-/parser-5.7.0.tgz", + "integrity": "sha512-m/gWCCcS4jXw6vkrPQ1BjZ1vomP01PArgzvauBqzsoZ3urLbsRChexB8/YV8z9HwE3qlJM35FxfKZ1nfP/4x8g==", "dev": true, "dependencies": { - "@typescript-eslint/scope-manager": "5.4.0", - "@typescript-eslint/types": "5.4.0", - "@typescript-eslint/typescript-estree": "5.4.0", + "@typescript-eslint/scope-manager": "5.7.0", + "@typescript-eslint/types": "5.7.0", + "@typescript-eslint/typescript-estree": "5.7.0", "debug": "^4.3.2" }, "engines": { @@ -1109,13 +1061,13 @@ } }, "node_modules/@typescript-eslint/scope-manager": { - "version": "5.4.0", - "resolved": "https://registry.npmjs.org/@typescript-eslint/scope-manager/-/scope-manager-5.4.0.tgz", - "integrity": "sha512-pRxFjYwoi8R+n+sibjgF9iUiAELU9ihPBtHzocyW8v8D8G8KeQvXTsW7+CBYIyTYsmhtNk50QPGLE3vrvhM5KA==", + "version": "5.7.0", + "resolved": "https://registry.npmjs.org/@typescript-eslint/scope-manager/-/scope-manager-5.7.0.tgz", + "integrity": "sha512-7mxR520DGq5F7sSSgM0HSSMJ+TFUymOeFRMfUfGFAVBv8BR+Jv1vHgAouYUvWRZeszVBJlLcc9fDdktxb5kmxA==", "dev": true, "dependencies": { - "@typescript-eslint/types": "5.4.0", - "@typescript-eslint/visitor-keys": "5.4.0" + "@typescript-eslint/types": "5.7.0", + "@typescript-eslint/visitor-keys": "5.7.0" }, "engines": { "node": "^12.22.0 || ^14.17.0 || >=16.0.0" @@ -1126,9 +1078,9 @@ } }, "node_modules/@typescript-eslint/types": { - "version": "5.4.0", - "resolved": "https://registry.npmjs.org/@typescript-eslint/types/-/types-5.4.0.tgz", - "integrity": "sha512-GjXNpmn+n1LvnttarX+sPD6+S7giO+9LxDIGlRl4wK3a7qMWALOHYuVSZpPTfEIklYjaWuMtfKdeByx0AcaThA==", + "version": "5.7.0", + "resolved": "https://registry.npmjs.org/@typescript-eslint/types/-/types-5.7.0.tgz", + "integrity": "sha512-5AeYIF5p2kAneIpnLFve8g50VyAjq7udM7ApZZ9JYjdPjkz0LvODfuSHIDUVnIuUoxafoWzpFyU7Sqbxgi79mA==", "dev": true, "engines": { "node": "^12.22.0 || ^14.17.0 || >=16.0.0" @@ -1139,13 +1091,13 @@ } }, "node_modules/@typescript-eslint/typescript-estree": { - "version": "5.4.0", - "resolved": "https://registry.npmjs.org/@typescript-eslint/typescript-estree/-/typescript-estree-5.4.0.tgz", - "integrity": "sha512-nhlNoBdhKuwiLMx6GrybPT3SFILm5Gij2YBdPEPFlYNFAXUJWX6QRgvi/lwVoadaQEFsizohs6aFRMqsXI2ewA==", + "version": "5.7.0", + "resolved": "https://registry.npmjs.org/@typescript-eslint/typescript-estree/-/typescript-estree-5.7.0.tgz", + "integrity": "sha512-aO1Ql+izMrTnPj5aFFlEJkpD4jRqC4Gwhygu2oHK2wfVQpmOPbyDSveJ+r/NQo+PWV43M6uEAeLVbTi09dFLhg==", "dev": true, "dependencies": { - "@typescript-eslint/types": "5.4.0", - "@typescript-eslint/visitor-keys": "5.4.0", + "@typescript-eslint/types": "5.7.0", + "@typescript-eslint/visitor-keys": "5.7.0", "debug": "^4.3.2", "globby": "^11.0.4", "is-glob": "^4.0.3", @@ -1165,13 +1117,57 @@ } } }, + "node_modules/@typescript-eslint/typescript-estree/node_modules/array-union": { + "version": "2.1.0", + "resolved": "https://registry.npmjs.org/array-union/-/array-union-2.1.0.tgz", + "integrity": "sha512-HGyxoOTYUyCM6stUe6EJgnd4EoewAI7zMdfqO+kGjnlZmBDz/cR5pf8r/cR4Wq60sL/p0IkcjUEEPwS3GFrIyw==", + "dev": true, + "engines": { + "node": ">=8" + } + }, + "node_modules/@typescript-eslint/typescript-estree/node_modules/globby": { + "version": "11.0.4", + "resolved": "https://registry.npmjs.org/globby/-/globby-11.0.4.tgz", + "integrity": "sha512-9O4MVG9ioZJ08ffbcyVYyLOJLk5JQ688pJ4eMGLpdWLHq/Wr1D9BlriLQyL0E+jbkuePVZXYFj47QM/v093wHg==", + "dev": true, + "dependencies": { + "array-union": "^2.1.0", + "dir-glob": "^3.0.1", + "fast-glob": "^3.1.1", + "ignore": "^5.1.4", + "merge2": "^1.3.0", + "slash": "^3.0.0" + }, + "engines": { + "node": ">=10" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/@typescript-eslint/typescript-estree/node_modules/semver": { + "version": "7.3.5", + "resolved": "https://registry.npmjs.org/semver/-/semver-7.3.5.tgz", + "integrity": "sha512-PoeGJYh8HK4BTO/a9Tf6ZG3veo/A7ZVsYrSA6J8ny9nb3B1VrpkuN+z9OE5wfE5p6H4LchYZsegiQgbJD94ZFQ==", + "dev": true, + "dependencies": { + "lru-cache": "^6.0.0" + }, + "bin": { + "semver": "bin/semver.js" + }, + "engines": { + "node": ">=10" + } + }, "node_modules/@typescript-eslint/visitor-keys": { - "version": "5.4.0", - "resolved": "https://registry.npmjs.org/@typescript-eslint/visitor-keys/-/visitor-keys-5.4.0.tgz", - "integrity": "sha512-PVbax7MeE7tdLfW5SA0fs8NGVVr+buMPrcj+CWYWPXsZCH8qZ1THufDzbXm1xrZ2b2PA1iENJ0sRq5fuUtvsJg==", + "version": "5.7.0", + "resolved": "https://registry.npmjs.org/@typescript-eslint/visitor-keys/-/visitor-keys-5.7.0.tgz", + "integrity": "sha512-hdohahZ4lTFcglZSJ3DGdzxQHBSxsLVqHzkiOmKi7xVAWC4y2c1bIMKmPJSrA4aOEoRUPOKQ87Y/taC7yVHpFg==", "dev": true, "dependencies": { - "@typescript-eslint/types": "5.4.0", + "@typescript-eslint/types": "5.7.0", "eslint-visitor-keys": "^3.0.0" }, "engines": { @@ -1348,12 +1344,33 @@ "dev": true }, "node_modules/array-union": { - "version": "2.1.0", - "resolved": "https://registry.npmjs.org/array-union/-/array-union-2.1.0.tgz", - "integrity": "sha512-HGyxoOTYUyCM6stUe6EJgnd4EoewAI7zMdfqO+kGjnlZmBDz/cR5pf8r/cR4Wq60sL/p0IkcjUEEPwS3GFrIyw==", + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/array-union/-/array-union-1.0.2.tgz", + "integrity": "sha1-mjRBDk9OPaI96jdb5b5w8kd47Dk=", "dev": true, + "dependencies": { + "array-uniq": "^1.0.1" + }, "engines": { - "node": ">=8" + "node": ">=0.10.0" + } + }, + "node_modules/array-uniq": { + "version": "1.0.3", + "resolved": "https://registry.npmjs.org/array-uniq/-/array-uniq-1.0.3.tgz", + "integrity": "sha1-r2rId6Jcx/dOBYiUdThY39sk/bY=", + "dev": true, + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/async": { + "version": "2.6.3", + "resolved": "https://registry.npmjs.org/async/-/async-2.6.3.tgz", + "integrity": "sha512-zflvls11DCy+dQWzTW2dzuilv8Z5X/pjfmZOWba6TNIVDm+2UDaJmXSOXlasHKfNBs8oo3M0aT50fDEWfKZjXg==", + "dev": true, + "dependencies": { + "lodash": "^4.17.14" } }, "node_modules/async-retry": { @@ -1630,9 +1647,9 @@ } }, "node_modules/caniuse-lite": { - "version": "1.0.30001283", - "resolved": "https://registry.npmjs.org/caniuse-lite/-/caniuse-lite-1.0.30001283.tgz", - "integrity": "sha512-9RoKo841j1GQFSJz/nCXOj0sD7tHBtlowjYlrqIUS812x9/emfBLBt6IyMz1zIaYc/eRL8Cs6HPUVi2Hzq4sIg==", + "version": "1.0.30001286", + "resolved": "https://registry.npmjs.org/caniuse-lite/-/caniuse-lite-1.0.30001286.tgz", + "integrity": "sha512-zaEMRH6xg8ESMi2eQ3R4eZ5qw/hJiVsO/HlLwniIwErij0JDr9P+8V4dtx1l+kLq6j3yy8l8W4fst1lBnat5wQ==", "dev": true, "funding": { "type": "opencollective", @@ -1809,6 +1826,12 @@ "node": ">= 0.8" } }, + "node_modules/commander": { + "version": "2.20.3", + "resolved": "https://registry.npmjs.org/commander/-/commander-2.20.3.tgz", + "integrity": "sha512-GpVkmM8vF2vQUkj2LvZmD35JxeJOLCwJ9cUkugyk2nuhbv3+mJvpLYYt+0+USMxE+oj+ey/lJEnhZw75x/OMcQ==", + "dev": true + }, "node_modules/commondir": { "version": "1.0.1", "resolved": "https://registry.npmjs.org/commondir/-/commondir-1.0.1.tgz", @@ -2085,9 +2108,15 @@ "dev": true }, "node_modules/electron-to-chromium": { - "version": "1.4.1", - "resolved": "https://registry.npmjs.org/electron-to-chromium/-/electron-to-chromium-1.4.1.tgz", - "integrity": "sha512-9ldvb6QMHiDpUNF1iSwBTiTT0qXEN+xIO5WlCJrC5gt0z74ofOiqR698vaJqYWnri0XZiF0YmnrFmGq/EmpGAA==", + "version": "1.4.17", + "resolved": "https://registry.npmjs.org/electron-to-chromium/-/electron-to-chromium-1.4.17.tgz", + "integrity": "sha512-zhk1MravPtq/KBhmGB7TLBILmXTgRG9TFSI3qS3DbgyfHzIl72iiTE37r/BHIbPCJJlWIo5rySyxiH4vWhu2ZA==", + "dev": true + }, + "node_modules/email-addresses": { + "version": "3.1.0", + "resolved": "https://registry.npmjs.org/email-addresses/-/email-addresses-3.1.0.tgz", + "integrity": "sha512-k0/r7GrWVL32kZlGwfPNgB2Y/mMXVTq/decgLczm/j34whdaspNrZO8CnXPf1laaHxI6ptUlsnAxN+UAPw+fzg==", "dev": true }, "node_modules/emoji-regex": { @@ -2160,13 +2189,13 @@ } }, "node_modules/eslint": { - "version": "8.3.0", - "resolved": "https://registry.npmjs.org/eslint/-/eslint-8.3.0.tgz", - "integrity": "sha512-aIay56Ph6RxOTC7xyr59Kt3ewX185SaGnAr8eWukoPLeriCrvGjvAubxuvaXOfsxhtwV5g0uBOsyhAom4qJdww==", + "version": "8.4.1", + "resolved": "https://registry.npmjs.org/eslint/-/eslint-8.4.1.tgz", + "integrity": "sha512-TxU/p7LB1KxQ6+7aztTnO7K0i+h0tDi81YRY9VzB6Id71kNz+fFYnf5HD5UOQmxkzcoa0TlVZf9dpMtUv0GpWg==", "dev": true, "dependencies": { - "@eslint/eslintrc": "^1.0.4", - "@humanwhocodes/config-array": "^0.6.0", + "@eslint/eslintrc": "^1.0.5", + "@humanwhocodes/config-array": "^0.9.2", "ajv": "^6.10.0", "chalk": "^4.0.0", "cross-spawn": "^7.0.2", @@ -2177,7 +2206,7 @@ "eslint-scope": "^7.1.0", "eslint-utils": "^3.0.0", "eslint-visitor-keys": "^3.1.0", - "espree": "^9.1.0", + "espree": "^9.2.0", "esquery": "^1.4.0", "esutils": "^2.0.2", "fast-deep-equal": "^3.1.3", @@ -2318,6 +2347,21 @@ "node": ">= 4" } }, + "node_modules/eslint/node_modules/semver": { + "version": "7.3.5", + "resolved": "https://registry.npmjs.org/semver/-/semver-7.3.5.tgz", + "integrity": "sha512-PoeGJYh8HK4BTO/a9Tf6ZG3veo/A7ZVsYrSA6J8ny9nb3B1VrpkuN+z9OE5wfE5p6H4LchYZsegiQgbJD94ZFQ==", + "dev": true, + "dependencies": { + "lru-cache": "^6.0.0" + }, + "bin": { + "semver": "bin/semver.js" + }, + "engines": { + "node": ">=10" + } + }, "node_modules/eslint/node_modules/strip-json-comments": { "version": "3.1.1", "resolved": "https://registry.npmjs.org/strip-json-comments/-/strip-json-comments-3.1.1.tgz", @@ -2331,9 +2375,9 @@ } }, "node_modules/espree": { - "version": "9.1.0", - "resolved": "https://registry.npmjs.org/espree/-/espree-9.1.0.tgz", - "integrity": "sha512-ZgYLvCS1wxOczBYGcQT9DDWgicXwJ4dbocr9uYN+/eresBAUuBu+O4WzB21ufQ/JqQT8gyp7hJ3z8SHii32mTQ==", + "version": "9.2.0", + "resolved": "https://registry.npmjs.org/espree/-/espree-9.2.0.tgz", + "integrity": "sha512-oP3utRkynpZWF/F2x/HZJ+AGtnIclaR7z1pYPxy7NYM2fSO6LgK/Rkny8anRSPK/VwEA1eqm2squui0T7ZMOBg==", "dev": true, "dependencies": { "acorn": "^8.6.0", @@ -2524,6 +2568,32 @@ "node": "^10.12.0 || >=12.0.0" } }, + "node_modules/filename-reserved-regex": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/filename-reserved-regex/-/filename-reserved-regex-2.0.0.tgz", + "integrity": "sha1-q/c9+rc10EVECr/qLZHzieu/oik=", + "dev": true, + "engines": { + "node": ">=4" + } + }, + "node_modules/filenamify": { + "version": "4.3.0", + "resolved": "https://registry.npmjs.org/filenamify/-/filenamify-4.3.0.tgz", + "integrity": "sha512-hcFKyUG57yWGAzu1CMt/dPzYZuv+jAJUT85bL8mrXvNe6hWj6yEHEc4EdcgiA6Z3oi1/9wXJdZPXF2dZNgwgOg==", + "dev": true, + "dependencies": { + "filename-reserved-regex": "^2.0.0", + "strip-outer": "^1.0.1", + "trim-repeated": "^1.0.0" + }, + "engines": { + "node": ">=8" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, "node_modules/fill-range": { "version": "7.0.1", "resolved": "https://registry.npmjs.org/fill-range/-/fill-range-7.0.1.tgz", @@ -2563,19 +2633,16 @@ } }, "node_modules/find-up": { - "version": "5.0.0", - "resolved": "https://registry.npmjs.org/find-up/-/find-up-5.0.0.tgz", - "integrity": "sha512-78/PXT1wlLLDgTzDs7sjq9hzz0vXD+zn+7wypEe4fXQxCmdmqfGsEPQxmiCSQI3ajFV91bVSsvNtrJRiW6nGng==", + "version": "4.1.0", + "resolved": "https://registry.npmjs.org/find-up/-/find-up-4.1.0.tgz", + "integrity": "sha512-PpOwAdQ/YlXQ2vj8a3h8IipDuYRi3wceVQQGYWxNINccq40Anw7BlsEXCMbt1Zt+OLA6Fq9suIpIWD0OsnISlw==", "dev": true, "dependencies": { - "locate-path": "^6.0.0", + "locate-path": "^5.0.0", "path-exists": "^4.0.0" }, "engines": { - "node": ">=10" - }, - "funding": { - "url": "https://github.com/sponsors/sindresorhus" + "node": ">=8" } }, "node_modules/flat": { @@ -2653,6 +2720,20 @@ } ] }, + "node_modules/fs-extra": { + "version": "8.1.0", + "resolved": "https://registry.npmjs.org/fs-extra/-/fs-extra-8.1.0.tgz", + "integrity": "sha512-yhlQgA6mnOJUKOsRUFsgJdQCvkKhcz8tlZG5HBQfReYZy46OwLcY+Zia0mtdHsOo9y/hP+CxMN0TU9QxoOtG4g==", + "dev": true, + "dependencies": { + "graceful-fs": "^4.2.0", + "jsonfile": "^4.0.0", + "universalify": "^0.1.0" + }, + "engines": { + "node": ">=6 <7 || >=8" + } + }, "node_modules/fs.realpath": { "version": "1.0.0", "resolved": "https://registry.npmjs.org/fs.realpath/-/fs.realpath-1.0.0.tgz", @@ -2746,6 +2827,28 @@ "url": "https://github.com/sponsors/sindresorhus" } }, + "node_modules/gh-pages": { + "version": "3.2.3", + "resolved": "https://registry.npmjs.org/gh-pages/-/gh-pages-3.2.3.tgz", + "integrity": "sha512-jA1PbapQ1jqzacECfjUaO9gV8uBgU6XNMV0oXLtfCX3haGLe5Atq8BxlrADhbD6/UdG9j6tZLWAkAybndOXTJg==", + "dev": true, + "dependencies": { + "async": "^2.6.1", + "commander": "^2.18.0", + "email-addresses": "^3.0.1", + "filenamify": "^4.3.0", + "find-cache-dir": "^3.3.1", + "fs-extra": "^8.1.0", + "globby": "^6.1.0" + }, + "bin": { + "gh-pages": "bin/gh-pages.js", + "gh-pages-clean": "bin/gh-pages-clean.js" + }, + "engines": { + "node": ">=10" + } + }, "node_modules/git-up": { "version": "4.0.5", "resolved": "https://registry.npmjs.org/git-up/-/git-up-4.0.5.tgz", @@ -2840,23 +2943,19 @@ } }, "node_modules/globby": { - "version": "11.0.4", - "resolved": "https://registry.npmjs.org/globby/-/globby-11.0.4.tgz", - "integrity": "sha512-9O4MVG9ioZJ08ffbcyVYyLOJLk5JQ688pJ4eMGLpdWLHq/Wr1D9BlriLQyL0E+jbkuePVZXYFj47QM/v093wHg==", + "version": "6.1.0", + "resolved": "https://registry.npmjs.org/globby/-/globby-6.1.0.tgz", + "integrity": "sha1-9abXDoOV4hyFj7BInWTfAkJNUGw=", "dev": true, "dependencies": { - "array-union": "^2.1.0", - "dir-glob": "^3.0.1", - "fast-glob": "^3.1.1", - "ignore": "^5.1.4", - "merge2": "^1.3.0", - "slash": "^3.0.0" + "array-union": "^1.0.1", + "glob": "^7.0.3", + "object-assign": "^4.0.1", + "pify": "^2.0.0", + "pinkie-promise": "^2.0.0" }, "engines": { - "node": ">=10" - }, - "funding": { - "url": "https://github.com/sponsors/sindresorhus" + "node": ">=0.10.0" } }, "node_modules/got": { @@ -2899,36 +2998,6 @@ "node": ">=4.x" } }, - "node_modules/handlebars": { - "version": "4.7.7", - "resolved": "https://registry.npmjs.org/handlebars/-/handlebars-4.7.7.tgz", - "integrity": "sha512-aAcXm5OAfE/8IXkcZvCepKU3VzW1/39Fb5ZuqMtgI/hT8X2YgoMvBY5dLhq/cpOvw7Lk1nK/UF71aLG/ZnVYRA==", - "dev": true, - "dependencies": { - "minimist": "^1.2.5", - "neo-async": "^2.6.0", - "source-map": "^0.6.1", - "wordwrap": "^1.0.0" - }, - "bin": { - "handlebars": "bin/handlebars" - }, - "engines": { - "node": ">=0.4.7" - }, - "optionalDependencies": { - "uglify-js": "^3.1.4" - } - }, - "node_modules/handlebars/node_modules/source-map": { - "version": "0.6.1", - "resolved": "https://registry.npmjs.org/source-map/-/source-map-0.6.1.tgz", - "integrity": "sha512-UjgapumWlbMhkBgzT7Ykc5YXUT46F0iKu8SGXq0bcwP5dz/h0Plj6enJqjz1Zbq2l5WaqYnrVbwWOWMyF3F47g==", - "dev": true, - "engines": { - "node": ">=0.10.0" - } - }, "node_modules/has": { "version": "1.0.3", "resolved": "https://registry.npmjs.org/has/-/has-1.0.3.tgz", @@ -3498,15 +3567,6 @@ "node": ">=8" } }, - "node_modules/istanbul-lib-instrument/node_modules/semver": { - "version": "6.3.0", - "resolved": "https://registry.npmjs.org/semver/-/semver-6.3.0.tgz", - "integrity": "sha512-b39TBaTSfV6yBrapU89p5fKekE2m/NwnDocOVruQFS1/veMgdzuPcnOM34M6CwxW8jH/lxEa5rBoDeUwu5HHTw==", - "dev": true, - "bin": { - "semver": "bin/semver.js" - } - }, "node_modules/istanbul-lib-processinfo": { "version": "2.0.2", "resolved": "https://registry.npmjs.org/istanbul-lib-processinfo/-/istanbul-lib-processinfo-2.0.2.tgz", @@ -3573,9 +3633,9 @@ } }, "node_modules/istanbul-reports": { - "version": "3.0.5", - "resolved": "https://registry.npmjs.org/istanbul-reports/-/istanbul-reports-3.0.5.tgz", - "integrity": "sha512-5+19PlhnGabNWB7kOFnuxT8H3T/iIyQzIbQMxXsURmmvKg86P2sbkrGOT77VnHw0Qr0gc2XzRaRfMZYYbSQCJQ==", + "version": "3.1.1", + "resolved": "https://registry.npmjs.org/istanbul-reports/-/istanbul-reports-3.1.1.tgz", + "integrity": "sha512-q1kvhAXWSsXfMjCdNHNPKZZv94OlspKnoGv+R9RGbnqOOQ0VbNfLFgQDVgi7hHenKsndGq3/o0OBdzDXthWcNw==", "dev": true, "dependencies": { "html-escaper": "^2.0.0", @@ -3660,7 +3720,16 @@ "integrity": "sha512-fQzRfAbIBnR0IQvftw9FJveWiHp72Fg20giDrHz6TdfB12UH/uue0D3hm57UB5KgAVuniLMCaS8P1IMj9NR7cA==", "dev": true }, - "node_modules/just-extend": { + "node_modules/jsonfile": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/jsonfile/-/jsonfile-4.0.0.tgz", + "integrity": "sha1-h3Gq4HmbZAdrdmQPygWPnBDjPss=", + "dev": true, + "optionalDependencies": { + "graceful-fs": "^4.1.6" + } + }, + "node_modules/just-extend": { "version": "4.2.1", "resolved": "https://registry.npmjs.org/just-extend/-/just-extend-4.2.1.tgz", "integrity": "sha512-g3UB796vUFIY90VIv/WX3L2c8CS2MdWUww3CNrYmqza1Fg0DURc2K/O4YrnklBdQarSJ/y8JnJYDGc+1iumQjg==", @@ -3707,18 +3776,15 @@ "dev": true }, "node_modules/locate-path": { - "version": "6.0.0", - "resolved": "https://registry.npmjs.org/locate-path/-/locate-path-6.0.0.tgz", - "integrity": "sha512-iPZK6eYjbxRu3uB4/WZ3EsEIMJFMqAoopl3R+zuq0UjcAm/MO6KCweDgPfP3elTztoKP3KtnVHxTn2NHBSDVUw==", + "version": "5.0.0", + "resolved": "https://registry.npmjs.org/locate-path/-/locate-path-5.0.0.tgz", + "integrity": "sha512-t7hw9pI+WvuwNJXwk5zVHpyhIqzg2qTlklJOf0mVxGSbe3Fp2VieZcduNYjaLDoy6p9uGpQEGWG87WpMKlNq8g==", "dev": true, "dependencies": { - "p-locate": "^5.0.0" + "p-locate": "^4.1.0" }, "engines": { - "node": ">=10" - }, - "funding": { - "url": "https://github.com/sponsors/sindresorhus" + "node": ">=8" } }, "node_modules/lodash": { @@ -3815,15 +3881,6 @@ "url": "https://github.com/sponsors/sindresorhus" } }, - "node_modules/make-dir/node_modules/semver": { - "version": "6.3.0", - "resolved": "https://registry.npmjs.org/semver/-/semver-6.3.0.tgz", - "integrity": "sha512-b39TBaTSfV6yBrapU89p5fKekE2m/NwnDocOVruQFS1/veMgdzuPcnOM34M6CwxW8jH/lxEa5rBoDeUwu5HHTw==", - "dev": true, - "bin": { - "semver": "bin/semver.js" - } - }, "node_modules/make-error": { "version": "1.3.6", "resolved": "https://registry.npmjs.org/make-error/-/make-error-1.3.6.tgz", @@ -3982,6 +4039,22 @@ "url": "https://github.com/sponsors/sindresorhus" } }, + "node_modules/mocha/node_modules/find-up": { + "version": "5.0.0", + "resolved": "https://registry.npmjs.org/find-up/-/find-up-5.0.0.tgz", + "integrity": "sha512-78/PXT1wlLLDgTzDs7sjq9hzz0vXD+zn+7wypEe4fXQxCmdmqfGsEPQxmiCSQI3ajFV91bVSsvNtrJRiW6nGng==", + "dev": true, + "dependencies": { + "locate-path": "^6.0.0", + "path-exists": "^4.0.0" + }, + "engines": { + "node": ">=10" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, "node_modules/mocha/node_modules/glob": { "version": "7.1.7", "resolved": "https://registry.npmjs.org/glob/-/glob-7.1.7.tgz", @@ -4002,12 +4075,57 @@ "url": "https://github.com/sponsors/isaacs" } }, + "node_modules/mocha/node_modules/locate-path": { + "version": "6.0.0", + "resolved": "https://registry.npmjs.org/locate-path/-/locate-path-6.0.0.tgz", + "integrity": "sha512-iPZK6eYjbxRu3uB4/WZ3EsEIMJFMqAoopl3R+zuq0UjcAm/MO6KCweDgPfP3elTztoKP3KtnVHxTn2NHBSDVUw==", + "dev": true, + "dependencies": { + "p-locate": "^5.0.0" + }, + "engines": { + "node": ">=10" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, "node_modules/mocha/node_modules/ms": { "version": "2.1.3", "resolved": "https://registry.npmjs.org/ms/-/ms-2.1.3.tgz", "integrity": "sha512-6FlzubTLZG3J2a/NVCAleEhjzq5oxgHyaCU9yYXvcLsvoVaHJq/s5xXI6/XXP6tz7R9xAOtHnSO/tXtF3WRTlA==", "dev": true }, + "node_modules/mocha/node_modules/p-limit": { + "version": "3.1.0", + "resolved": "https://registry.npmjs.org/p-limit/-/p-limit-3.1.0.tgz", + "integrity": "sha512-TYOanM3wGwNGsZN2cVTYPArw454xnXj5qmWF1bEoAc4+cU/ol7GVh7odevjp1FNHduHc3KZMcFduxU5Xc6uJRQ==", + "dev": true, + "dependencies": { + "yocto-queue": "^0.1.0" + }, + "engines": { + "node": ">=10" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/mocha/node_modules/p-locate": { + "version": "5.0.0", + "resolved": "https://registry.npmjs.org/p-locate/-/p-locate-5.0.0.tgz", + "integrity": "sha512-LaNjtRWUBY++zB5nE/NwcaoMylSPk+S+ZHNB1TzdbMJMny6dynpAGt7X/tl/QYq3TIeE6nxHppbo2LGymrG5Pw==", + "dev": true, + "dependencies": { + "p-limit": "^3.0.2" + }, + "engines": { + "node": ">=10" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, "node_modules/mocha/node_modules/strip-json-comments": { "version": "3.1.1", "resolved": "https://registry.npmjs.org/strip-json-comments/-/strip-json-comments-3.1.1.tgz", @@ -4092,12 +4210,6 @@ "integrity": "sha1-Sr6/7tdUHywnrPspvbvRXI1bpPc=", "dev": true }, - "node_modules/neo-async": { - "version": "2.6.2", - "resolved": "https://registry.npmjs.org/neo-async/-/neo-async-2.6.2.tgz", - "integrity": "sha512-Yd3UES5mWCSqR+qNT93S3UoYUkqAZ9lLg8a7g9rimsWmYGK8cVToA4/sF3RrshdyV3sAGMXVUmpMYOw+dLpOuw==", - "dev": true - }, "node_modules/new-github-release-url": { "version": "1.0.0", "resolved": "https://registry.npmjs.org/new-github-release-url/-/new-github-release-url-1.0.0.tgz", @@ -4256,58 +4368,6 @@ "wrap-ansi": "^6.2.0" } }, - "node_modules/nyc/node_modules/find-up": { - "version": "4.1.0", - "resolved": "https://registry.npmjs.org/find-up/-/find-up-4.1.0.tgz", - "integrity": "sha512-PpOwAdQ/YlXQ2vj8a3h8IipDuYRi3wceVQQGYWxNINccq40Anw7BlsEXCMbt1Zt+OLA6Fq9suIpIWD0OsnISlw==", - "dev": true, - "dependencies": { - "locate-path": "^5.0.0", - "path-exists": "^4.0.0" - }, - "engines": { - "node": ">=8" - } - }, - "node_modules/nyc/node_modules/locate-path": { - "version": "5.0.0", - "resolved": "https://registry.npmjs.org/locate-path/-/locate-path-5.0.0.tgz", - "integrity": "sha512-t7hw9pI+WvuwNJXwk5zVHpyhIqzg2qTlklJOf0mVxGSbe3Fp2VieZcduNYjaLDoy6p9uGpQEGWG87WpMKlNq8g==", - "dev": true, - "dependencies": { - "p-locate": "^4.1.0" - }, - "engines": { - "node": ">=8" - } - }, - "node_modules/nyc/node_modules/p-limit": { - "version": "2.3.0", - "resolved": "https://registry.npmjs.org/p-limit/-/p-limit-2.3.0.tgz", - "integrity": "sha512-//88mFWSJx8lxCzwdAABTJL2MyWB12+eIY7MDL2SqLmAkeKU9qxRvWuSyTjm3FUmpBEMuFfckAIqEaVGUDxb6w==", - "dev": true, - "dependencies": { - "p-try": "^2.0.0" - }, - "engines": { - "node": ">=6" - }, - "funding": { - "url": "https://github.com/sponsors/sindresorhus" - } - }, - "node_modules/nyc/node_modules/p-locate": { - "version": "4.1.0", - "resolved": "https://registry.npmjs.org/p-locate/-/p-locate-4.1.0.tgz", - "integrity": "sha512-R79ZZ/0wAxKGu3oYMlz8jy/kbhsNrS7SKZ7PxEHBgJ5+F2mtFW2fK2cOtBh1cHYkQsbzFV7I+EoRKe6Yt0oK7A==", - "dev": true, - "dependencies": { - "p-limit": "^2.2.0" - }, - "engines": { - "node": ">=8" - } - }, "node_modules/nyc/node_modules/resolve-from": { "version": "5.0.0", "resolved": "https://registry.npmjs.org/resolve-from/-/resolve-from-5.0.0.tgz", @@ -4372,10 +4432,19 @@ "node": ">=6" } }, + "node_modules/object-assign": { + "version": "4.1.1", + "resolved": "https://registry.npmjs.org/object-assign/-/object-assign-4.1.1.tgz", + "integrity": "sha1-IQmtx5ZYh8/AXLvUQsrIv7s2CGM=", + "dev": true, + "engines": { + "node": ">=0.10.0" + } + }, "node_modules/object-inspect": { - "version": "1.11.0", - "resolved": "https://registry.npmjs.org/object-inspect/-/object-inspect-1.11.0.tgz", - "integrity": "sha512-jp7ikS6Sd3GxQfZJPyH3cjcbJF6GZPClgdV+EFygjFLQ5FmW/dRUnTd9PQ9k0JhoNDabWFbpF1yCdSWCC6gexg==", + "version": "1.11.1", + "resolved": "https://registry.npmjs.org/object-inspect/-/object-inspect-1.11.1.tgz", + "integrity": "sha512-If7BjFlpkzzBeV1cqgT3OSWT3azyoxDGajR+iGnFBfVV2EWyDyWaZZW2ERDjUaY2QM8i5jI3Sj7mhsM4DDAqWA==", "dev": true, "funding": { "url": "https://github.com/sponsors/ljharb" @@ -4405,30 +4474,6 @@ "url": "https://github.com/sponsors/sindresorhus" } }, - "node_modules/onigasm": { - "version": "2.2.5", - "resolved": "https://registry.npmjs.org/onigasm/-/onigasm-2.2.5.tgz", - "integrity": "sha512-F+th54mPc0l1lp1ZcFMyL/jTs2Tlq4SqIHKIXGZOR/VkHkF9A7Fr5rRr5+ZG/lWeRsyrClLYRq7s/yFQ/XhWCA==", - "dev": true, - "dependencies": { - "lru-cache": "^5.1.1" - } - }, - "node_modules/onigasm/node_modules/lru-cache": { - "version": "5.1.1", - "resolved": "https://registry.npmjs.org/lru-cache/-/lru-cache-5.1.1.tgz", - "integrity": "sha512-KpNARQA3Iwv+jTA0utUVVbrh+Jlrr1Fv0e56GGzAFOXN7dk/FviaDW8LHmK52DlcH4WP2n6gI8vN1aesBFgo9w==", - "dev": true, - "dependencies": { - "yallist": "^3.0.2" - } - }, - "node_modules/onigasm/node_modules/yallist": { - "version": "3.1.1", - "resolved": "https://registry.npmjs.org/yallist/-/yallist-3.1.1.tgz", - "integrity": "sha512-a4UGQaWPH59mOXUYnAG2ewncQS4i4F43Tv3JoAM+s2VDAmS9NsK8GpDMLrCHPksFT7h3K6TOoUNn2pb7RoXx4g==", - "dev": true - }, "node_modules/open": { "version": "7.4.2", "resolved": "https://registry.npmjs.org/open/-/open-7.4.2.tgz", @@ -4520,33 +4565,30 @@ } }, "node_modules/p-limit": { - "version": "3.1.0", - "resolved": "https://registry.npmjs.org/p-limit/-/p-limit-3.1.0.tgz", - "integrity": "sha512-TYOanM3wGwNGsZN2cVTYPArw454xnXj5qmWF1bEoAc4+cU/ol7GVh7odevjp1FNHduHc3KZMcFduxU5Xc6uJRQ==", + "version": "2.3.0", + "resolved": "https://registry.npmjs.org/p-limit/-/p-limit-2.3.0.tgz", + "integrity": "sha512-//88mFWSJx8lxCzwdAABTJL2MyWB12+eIY7MDL2SqLmAkeKU9qxRvWuSyTjm3FUmpBEMuFfckAIqEaVGUDxb6w==", "dev": true, "dependencies": { - "yocto-queue": "^0.1.0" + "p-try": "^2.0.0" }, "engines": { - "node": ">=10" + "node": ">=6" }, "funding": { "url": "https://github.com/sponsors/sindresorhus" } }, "node_modules/p-locate": { - "version": "5.0.0", - "resolved": "https://registry.npmjs.org/p-locate/-/p-locate-5.0.0.tgz", - "integrity": "sha512-LaNjtRWUBY++zB5nE/NwcaoMylSPk+S+ZHNB1TzdbMJMny6dynpAGt7X/tl/QYq3TIeE6nxHppbo2LGymrG5Pw==", + "version": "4.1.0", + "resolved": "https://registry.npmjs.org/p-locate/-/p-locate-4.1.0.tgz", + "integrity": "sha512-R79ZZ/0wAxKGu3oYMlz8jy/kbhsNrS7SKZ7PxEHBgJ5+F2mtFW2fK2cOtBh1cHYkQsbzFV7I+EoRKe6Yt0oK7A==", "dev": true, "dependencies": { - "p-limit": "^3.0.2" + "p-limit": "^2.2.0" }, "engines": { - "node": ">=10" - }, - "funding": { - "url": "https://github.com/sponsors/sindresorhus" + "node": ">=8" } }, "node_modules/p-map": { @@ -4654,15 +4696,6 @@ "url": "https://github.com/sponsors/sindresorhus" } }, - "node_modules/package-json/node_modules/cacheable-request/node_modules/lowercase-keys": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/lowercase-keys/-/lowercase-keys-2.0.0.tgz", - "integrity": "sha512-tqNXrS78oMOE73NMxK4EMLQsQowWf8jKooH9g7xPavRT706R6bkQJ6DY2Te7QukaZsulxa30wQ7bk0pm4XiHmA==", - "dev": true, - "engines": { - "node": ">=8" - } - }, "node_modules/package-json/node_modules/decompress-response": { "version": "3.3.0", "resolved": "https://registry.npmjs.org/decompress-response/-/decompress-response-3.3.0.tgz", @@ -4715,6 +4748,15 @@ "node": ">=8.6" } }, + "node_modules/package-json/node_modules/got/node_modules/lowercase-keys": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/lowercase-keys/-/lowercase-keys-1.0.1.tgz", + "integrity": "sha512-G2Lj61tXDnVFFOi8VZds+SoQjtQC3dgokKdDG2mTm1tx4m50NUHBOZSBwQQHyy0V12A0JTG4icfZQH+xPyh8VA==", + "dev": true, + "engines": { + "node": ">=0.10.0" + } + }, "node_modules/package-json/node_modules/json-buffer": { "version": "3.0.0", "resolved": "https://registry.npmjs.org/json-buffer/-/json-buffer-3.0.0.tgz", @@ -4730,15 +4772,6 @@ "json-buffer": "3.0.0" } }, - "node_modules/package-json/node_modules/lowercase-keys": { - "version": "1.0.1", - "resolved": "https://registry.npmjs.org/lowercase-keys/-/lowercase-keys-1.0.1.tgz", - "integrity": "sha512-G2Lj61tXDnVFFOi8VZds+SoQjtQC3dgokKdDG2mTm1tx4m50NUHBOZSBwQQHyy0V12A0JTG4icfZQH+xPyh8VA==", - "dev": true, - "engines": { - "node": ">=0.10.0" - } - }, "node_modules/package-json/node_modules/normalize-url": { "version": "4.5.1", "resolved": "https://registry.npmjs.org/normalize-url/-/normalize-url-4.5.1.tgz", @@ -4766,13 +4799,13 @@ "lowercase-keys": "^1.0.0" } }, - "node_modules/package-json/node_modules/semver": { - "version": "6.3.0", - "resolved": "https://registry.npmjs.org/semver/-/semver-6.3.0.tgz", - "integrity": "sha512-b39TBaTSfV6yBrapU89p5fKekE2m/NwnDocOVruQFS1/veMgdzuPcnOM34M6CwxW8jH/lxEa5rBoDeUwu5HHTw==", + "node_modules/package-json/node_modules/responselike/node_modules/lowercase-keys": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/lowercase-keys/-/lowercase-keys-1.0.1.tgz", + "integrity": "sha512-G2Lj61tXDnVFFOi8VZds+SoQjtQC3dgokKdDG2mTm1tx4m50NUHBOZSBwQQHyy0V12A0JTG4icfZQH+xPyh8VA==", "dev": true, - "bin": { - "semver": "bin/semver.js" + "engines": { + "node": ">=0.10.0" } }, "node_modules/parent-module": { @@ -4898,65 +4931,43 @@ "url": "https://github.com/sponsors/jonschlinkert" } }, - "node_modules/pkg-dir": { - "version": "4.2.0", - "resolved": "https://registry.npmjs.org/pkg-dir/-/pkg-dir-4.2.0.tgz", - "integrity": "sha512-HRDzbaKjC+AOWVXxAU/x54COGeIv9eb+6CkDSQoNTt4XyWoIJvuPsXizxu/Fr23EiekbtZwmh1IcIG/l/a10GQ==", - "dev": true, - "dependencies": { - "find-up": "^4.0.0" - }, - "engines": { - "node": ">=8" - } - }, - "node_modules/pkg-dir/node_modules/find-up": { - "version": "4.1.0", - "resolved": "https://registry.npmjs.org/find-up/-/find-up-4.1.0.tgz", - "integrity": "sha512-PpOwAdQ/YlXQ2vj8a3h8IipDuYRi3wceVQQGYWxNINccq40Anw7BlsEXCMbt1Zt+OLA6Fq9suIpIWD0OsnISlw==", + "node_modules/pify": { + "version": "2.3.0", + "resolved": "https://registry.npmjs.org/pify/-/pify-2.3.0.tgz", + "integrity": "sha1-7RQaasBDqEnqWISY59yosVMw6Qw=", "dev": true, - "dependencies": { - "locate-path": "^5.0.0", - "path-exists": "^4.0.0" - }, "engines": { - "node": ">=8" + "node": ">=0.10.0" } }, - "node_modules/pkg-dir/node_modules/locate-path": { - "version": "5.0.0", - "resolved": "https://registry.npmjs.org/locate-path/-/locate-path-5.0.0.tgz", - "integrity": "sha512-t7hw9pI+WvuwNJXwk5zVHpyhIqzg2qTlklJOf0mVxGSbe3Fp2VieZcduNYjaLDoy6p9uGpQEGWG87WpMKlNq8g==", + "node_modules/pinkie": { + "version": "2.0.4", + "resolved": "https://registry.npmjs.org/pinkie/-/pinkie-2.0.4.tgz", + "integrity": "sha1-clVrgM+g1IqXToDnckjoDtT3+HA=", "dev": true, - "dependencies": { - "p-locate": "^4.1.0" - }, "engines": { - "node": ">=8" + "node": ">=0.10.0" } }, - "node_modules/pkg-dir/node_modules/p-limit": { - "version": "2.3.0", - "resolved": "https://registry.npmjs.org/p-limit/-/p-limit-2.3.0.tgz", - "integrity": "sha512-//88mFWSJx8lxCzwdAABTJL2MyWB12+eIY7MDL2SqLmAkeKU9qxRvWuSyTjm3FUmpBEMuFfckAIqEaVGUDxb6w==", + "node_modules/pinkie-promise": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/pinkie-promise/-/pinkie-promise-2.0.1.tgz", + "integrity": "sha1-ITXW36ejWMBprJsXh3YogihFD/o=", "dev": true, "dependencies": { - "p-try": "^2.0.0" + "pinkie": "^2.0.0" }, "engines": { - "node": ">=6" - }, - "funding": { - "url": "https://github.com/sponsors/sindresorhus" + "node": ">=0.10.0" } }, - "node_modules/pkg-dir/node_modules/p-locate": { - "version": "4.1.0", - "resolved": "https://registry.npmjs.org/p-locate/-/p-locate-4.1.0.tgz", - "integrity": "sha512-R79ZZ/0wAxKGu3oYMlz8jy/kbhsNrS7SKZ7PxEHBgJ5+F2mtFW2fK2cOtBh1cHYkQsbzFV7I+EoRKe6Yt0oK7A==", + "node_modules/pkg-dir": { + "version": "4.2.0", + "resolved": "https://registry.npmjs.org/pkg-dir/-/pkg-dir-4.2.0.tgz", + "integrity": "sha512-HRDzbaKjC+AOWVXxAU/x54COGeIv9eb+6CkDSQoNTt4XyWoIJvuPsXizxu/Fr23EiekbtZwmh1IcIG/l/a10GQ==", "dev": true, "dependencies": { - "p-limit": "^2.2.0" + "find-up": "^4.0.0" }, "engines": { "node": ">=8" @@ -5039,9 +5050,9 @@ } }, "node_modules/qs": { - "version": "6.10.1", - "resolved": "https://registry.npmjs.org/qs/-/qs-6.10.1.tgz", - "integrity": "sha512-M528Hph6wsSVOBiYUnGf+K/7w0hNshs/duGsNXPUCLH5XAqjEtiPGwNONLV0tBH8NoGb0mvD5JubnUTrujKDTg==", + "version": "6.10.2", + "resolved": "https://registry.npmjs.org/qs/-/qs-6.10.2.tgz", + "integrity": "sha512-mSIdjzqznWgfd4pMii7sHtaYF8rx8861hBO80SraY5GT0XQibWZWJSid0avzHGkDIZLImux2S5mXO0Hfct2QCw==", "dev": true, "dependencies": { "side-channel": "^1.0.4" @@ -5232,38 +5243,82 @@ "integrity": "sha512-951DJ0kwjwU7CwGU3BCvRBgLxuJsOPRrZkqx0AsugJdSyPpUdwY9nlU0RAoSKqgh+VTerzecXLIIwgsGIpNxlA==", "dev": true, "dependencies": { - "@iarna/toml": "2.2.5", - "@octokit/rest": "18.12.0", - "async-retry": "1.3.3", - "chalk": "4.1.2", - "cosmiconfig": "7.0.1", - "debug": "4.3.2", - "deprecated-obj": "2.0.0", - "execa": "5.1.1", - "form-data": "4.0.0", - "git-url-parse": "11.6.0", - "globby": "11.0.4", - "got": "11.8.3", - "import-cwd": "3.0.0", - "inquirer": "8.2.0", - "is-ci": "3.0.1", - "lodash": "4.17.21", - "mime-types": "2.1.34", - "new-github-release-url": "1.0.0", - "open": "7.4.2", - "ora": "5.4.1", - "os-name": "4.0.1", - "parse-json": "5.2.0", - "semver": "7.3.5", - "shelljs": "0.8.4", - "update-notifier": "5.1.0", - "url-join": "4.0.1", - "uuid": "8.3.2", - "yaml": "1.10.2", - "yargs-parser": "20.2.9" + "@iarna/toml": "2.2.5", + "@octokit/rest": "18.12.0", + "async-retry": "1.3.3", + "chalk": "4.1.2", + "cosmiconfig": "7.0.1", + "debug": "4.3.2", + "deprecated-obj": "2.0.0", + "execa": "5.1.1", + "form-data": "4.0.0", + "git-url-parse": "11.6.0", + "globby": "11.0.4", + "got": "11.8.3", + "import-cwd": "3.0.0", + "inquirer": "8.2.0", + "is-ci": "3.0.1", + "lodash": "4.17.21", + "mime-types": "2.1.34", + "new-github-release-url": "1.0.0", + "open": "7.4.2", + "ora": "5.4.1", + "os-name": "4.0.1", + "parse-json": "5.2.0", + "semver": "7.3.5", + "shelljs": "0.8.4", + "update-notifier": "5.1.0", + "url-join": "4.0.1", + "uuid": "8.3.2", + "yaml": "1.10.2", + "yargs-parser": "20.2.9" + }, + "bin": { + "release-it": "bin/release-it.js" + }, + "engines": { + "node": ">=10" + } + }, + "node_modules/release-it/node_modules/array-union": { + "version": "2.1.0", + "resolved": "https://registry.npmjs.org/array-union/-/array-union-2.1.0.tgz", + "integrity": "sha512-HGyxoOTYUyCM6stUe6EJgnd4EoewAI7zMdfqO+kGjnlZmBDz/cR5pf8r/cR4Wq60sL/p0IkcjUEEPwS3GFrIyw==", + "dev": true, + "engines": { + "node": ">=8" + } + }, + "node_modules/release-it/node_modules/globby": { + "version": "11.0.4", + "resolved": "https://registry.npmjs.org/globby/-/globby-11.0.4.tgz", + "integrity": "sha512-9O4MVG9ioZJ08ffbcyVYyLOJLk5JQ688pJ4eMGLpdWLHq/Wr1D9BlriLQyL0E+jbkuePVZXYFj47QM/v093wHg==", + "dev": true, + "dependencies": { + "array-union": "^2.1.0", + "dir-glob": "^3.0.1", + "fast-glob": "^3.1.1", + "ignore": "^5.1.4", + "merge2": "^1.3.0", + "slash": "^3.0.0" + }, + "engines": { + "node": ">=10" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/release-it/node_modules/semver": { + "version": "7.3.5", + "resolved": "https://registry.npmjs.org/semver/-/semver-7.3.5.tgz", + "integrity": "sha512-PoeGJYh8HK4BTO/a9Tf6ZG3veo/A7ZVsYrSA6J8ny9nb3B1VrpkuN+z9OE5wfE5p6H4LchYZsegiQgbJD94ZFQ==", + "dev": true, + "dependencies": { + "lru-cache": "^6.0.0" }, "bin": { - "release-it": "bin/release-it.js" + "semver": "bin/semver.js" }, "engines": { "node": ">=10" @@ -5448,18 +5503,12 @@ "dev": true }, "node_modules/semver": { - "version": "7.3.5", - "resolved": "https://registry.npmjs.org/semver/-/semver-7.3.5.tgz", - "integrity": "sha512-PoeGJYh8HK4BTO/a9Tf6ZG3veo/A7ZVsYrSA6J8ny9nb3B1VrpkuN+z9OE5wfE5p6H4LchYZsegiQgbJD94ZFQ==", + "version": "6.3.0", + "resolved": "https://registry.npmjs.org/semver/-/semver-6.3.0.tgz", + "integrity": "sha512-b39TBaTSfV6yBrapU89p5fKekE2m/NwnDocOVruQFS1/veMgdzuPcnOM34M6CwxW8jH/lxEa5rBoDeUwu5HHTw==", "dev": true, - "dependencies": { - "lru-cache": "^6.0.0" - }, "bin": { "semver": "bin/semver.js" - }, - "engines": { - "node": ">=10" } }, "node_modules/semver-diff": { @@ -5474,15 +5523,6 @@ "node": ">=8" } }, - "node_modules/semver-diff/node_modules/semver": { - "version": "6.3.0", - "resolved": "https://registry.npmjs.org/semver/-/semver-6.3.0.tgz", - "integrity": "sha512-b39TBaTSfV6yBrapU89p5fKekE2m/NwnDocOVruQFS1/veMgdzuPcnOM34M6CwxW8jH/lxEa5rBoDeUwu5HHTw==", - "dev": true, - "bin": { - "semver": "bin/semver.js" - } - }, "node_modules/serialize-javascript": { "version": "6.0.0", "resolved": "https://registry.npmjs.org/serialize-javascript/-/serialize-javascript-6.0.0.tgz", @@ -5537,13 +5577,13 @@ } }, "node_modules/shiki": { - "version": "0.9.12", - "resolved": "https://registry.npmjs.org/shiki/-/shiki-0.9.12.tgz", - "integrity": "sha512-VXcROdldv0/Qu0w2XvzU4IrvTeBNs/Kj/FCmtcEXGz7Tic/veQzliJj6tEiAgoKianhQstpYmbPDStHU5Opqcw==", + "version": "0.9.15", + "resolved": "https://registry.npmjs.org/shiki/-/shiki-0.9.15.tgz", + "integrity": "sha512-/Y0z9IzhJ8nD9nbceORCqu6NgT9X6I8Fk8c3SICHI5NbZRLdZYFaB233gwct9sU0vvSypyaL/qaKvzyQGJBZSw==", "dev": true, "dependencies": { "jsonc-parser": "^3.0.0", - "onigasm": "^2.2.5", + "vscode-oniguruma": "^1.6.1", "vscode-textmate": "5.2.0" } }, @@ -5734,6 +5774,18 @@ "node": ">=0.10.0" } }, + "node_modules/strip-outer": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/strip-outer/-/strip-outer-1.0.1.tgz", + "integrity": "sha512-k55yxKHwaXnpYGsOzg4Vl8+tDrWylxDEpknGjhTiZB8dFRU5rTo9CAzeycivxV3s+zlTKwrs6WxMxR95n26kwg==", + "dev": true, + "dependencies": { + "escape-string-regexp": "^1.0.2" + }, + "engines": { + "node": ">=0.10.0" + } + }, "node_modules/supports-color": { "version": "7.2.0", "resolved": "https://registry.npmjs.org/supports-color/-/supports-color-7.2.0.tgz", @@ -5820,6 +5872,18 @@ "integrity": "sha1-gYT9NH2snNwYWZLzpmIuFLnZq2o=", "dev": true }, + "node_modules/trim-repeated": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/trim-repeated/-/trim-repeated-1.0.0.tgz", + "integrity": "sha1-42RqLqTokTEr9+rObPsFOAvAHCE=", + "dev": true, + "dependencies": { + "escape-string-regexp": "^1.0.2" + }, + "engines": { + "node": ">=0.10.0" + } + }, "node_modules/ts-node": { "version": "10.4.0", "resolved": "https://registry.npmjs.org/ts-node/-/ts-node-10.4.0.tgz", @@ -5961,32 +6025,10 @@ "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": { - "version": "0.6.0", - "resolved": "https://registry.npmjs.org/typedoc-github-wiki-theme/-/typedoc-github-wiki-theme-0.6.0.tgz", - "integrity": "sha512-uHhR7PwAZ4JgO0KzlLocWSvMqKsSZ/wxUQYGKskIepzsotPAQcAWnSSnGi6gdkSw8UAfIIppdD7H1AmI39962w==", - "dev": true, - "peerDependencies": { - "typedoc": ">=0.22.0", - "typedoc-plugin-markdown": ">=3.11.0" - } - }, - "node_modules/typedoc-plugin-markdown": { - "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" - }, - "peerDependencies": { - "typedoc": ">=0.22.0" - } - }, "node_modules/typescript": { - "version": "4.5.2", - "resolved": "https://registry.npmjs.org/typescript/-/typescript-4.5.2.tgz", - "integrity": "sha512-5BlMof9H1yGt0P8/WF+wPNw6GfctgGjXp5hkblpyT+8rkASSmkUKMXrxR0Xg8ThVCi/JnHQiKXeBaEwCeQwMFw==", + "version": "4.5.3", + "resolved": "https://registry.npmjs.org/typescript/-/typescript-4.5.3.tgz", + "integrity": "sha512-eVYaEHALSt+s9LbvgEv4Ef+Tdq7hBiIZgii12xXJnukryt3pMgJf6aKhoCZ3FWQsu6sydEnkg11fYXLzhLBjeQ==", "dev": true, "bin": { "tsc": "bin/tsc", @@ -5996,19 +6038,6 @@ "node": ">=4.2.0" } }, - "node_modules/uglify-js": { - "version": "3.14.3", - "resolved": "https://registry.npmjs.org/uglify-js/-/uglify-js-3.14.3.tgz", - "integrity": "sha512-mic3aOdiq01DuSVx0TseaEzMIVqebMZ0Z3vaeDhFEh9bsc24hV1TFvN74reA2vs08D0ZWfNjAcJ3UbVLaBss+g==", - "dev": true, - "optional": true, - "bin": { - "uglifyjs": "bin/uglifyjs" - }, - "engines": { - "node": ">=0.8.0" - } - }, "node_modules/unique-string": { "version": "2.0.0", "resolved": "https://registry.npmjs.org/unique-string/-/unique-string-2.0.0.tgz", @@ -6027,6 +6056,15 @@ "integrity": "sha512-isyNax3wXoKaulPDZWHQqbmIx1k2tb9fb3GGDBRxCscfYV2Ch7WxPArBsFEG8s/safwXTT7H4QGhaIkTp9447w==", "dev": true }, + "node_modules/universalify": { + "version": "0.1.2", + "resolved": "https://registry.npmjs.org/universalify/-/universalify-0.1.2.tgz", + "integrity": "sha512-rBJeI5CXAlmy1pV+617WB9J63U6XcazHHF2f2dbJix4XzpUF0RS3Zbj0FGIOCAva5P/d/GBOYaACQ1w+0azUkg==", + "dev": true, + "engines": { + "node": ">= 4.0.0" + } + }, "node_modules/update-notifier": { "version": "5.1.0", "resolved": "https://registry.npmjs.org/update-notifier/-/update-notifier-5.1.0.tgz", @@ -6073,6 +6111,21 @@ "is-ci": "bin.js" } }, + "node_modules/update-notifier/node_modules/semver": { + "version": "7.3.5", + "resolved": "https://registry.npmjs.org/semver/-/semver-7.3.5.tgz", + "integrity": "sha512-PoeGJYh8HK4BTO/a9Tf6ZG3veo/A7ZVsYrSA6J8ny9nb3B1VrpkuN+z9OE5wfE5p6H4LchYZsegiQgbJD94ZFQ==", + "dev": true, + "dependencies": { + "lru-cache": "^6.0.0" + }, + "bin": { + "semver": "bin/semver.js" + }, + "engines": { + "node": ">=10" + } + }, "node_modules/uri-js": { "version": "4.4.1", "resolved": "https://registry.npmjs.org/uri-js/-/uri-js-4.4.1.tgz", @@ -6121,6 +6174,12 @@ "integrity": "sha512-l8lCEmLcLYZh4nbunNZvQCJc5pv7+RCwa8q/LdUx8u7lsWvPDKmpodJAJNwkAhJC//dFY48KuIEmjtd4RViDrA==", "dev": true }, + "node_modules/vscode-oniguruma": { + "version": "1.6.1", + "resolved": "https://registry.npmjs.org/vscode-oniguruma/-/vscode-oniguruma-1.6.1.tgz", + "integrity": "sha512-vc4WhSIaVpgJ0jJIejjYxPvURJavX6QG41vu0mGhqywMkQqulezEqEQ3cO3gc8GvcOpX6ycmKGqRoROEMBNXTQ==", + "dev": true + }, "node_modules/vscode-textmate": { "version": "5.2.0", "resolved": "https://registry.npmjs.org/vscode-textmate/-/vscode-textmate-5.2.0.tgz", @@ -6256,12 +6315,6 @@ "node": ">=0.10.0" } }, - "node_modules/wordwrap": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/wordwrap/-/wordwrap-1.0.0.tgz", - "integrity": "sha1-J1hIEIkUVqQXHI0CJkQa3pDLyus=", - "dev": true - }, "node_modules/workerpool": { "version": "6.1.5", "resolved": "https://registry.npmjs.org/workerpool/-/workerpool-6.1.5.tgz", @@ -6336,18 +6389,18 @@ } }, "node_modules/yargs": { - "version": "17.2.1", - "resolved": "https://registry.npmjs.org/yargs/-/yargs-17.2.1.tgz", - "integrity": "sha512-XfR8du6ua4K6uLGm5S6fA+FIJom/MdJcFNVY8geLlp2v8GYbOXD4EB1tPNZsRn4vBzKGMgb5DRZMeWuFc2GO8Q==", + "version": "17.3.0", + "resolved": "https://registry.npmjs.org/yargs/-/yargs-17.3.0.tgz", + "integrity": "sha512-GQl1pWyDoGptFPJx9b9L6kmR33TGusZvXIZUT+BOz9f7X2L94oeAskFYLEg/FkhV06zZPBYLvLZRWeYId29lew==", "dev": true, "dependencies": { "cliui": "^7.0.2", "escalade": "^3.1.1", "get-caller-file": "^2.0.5", "require-directory": "^2.1.1", - "string-width": "^4.2.0", + "string-width": "^4.2.3", "y18n": "^5.0.5", - "yargs-parser": "^20.2.2" + "yargs-parser": "^21.0.0" }, "engines": { "node": ">=12" @@ -6389,6 +6442,15 @@ "url": "https://github.com/sponsors/sindresorhus" } }, + "node_modules/yargs/node_modules/yargs-parser": { + "version": "21.0.0", + "resolved": "https://registry.npmjs.org/yargs-parser/-/yargs-parser-21.0.0.tgz", + "integrity": "sha512-z9kApYUOCwoeZ78rfRYYWdiU/iNL6mwwYlkkZfJoyMR1xps+NEBX5X7XmRpxkZHhXJ6+Ey00IwKxBBSW9FIjyA==", + "dev": true, + "engines": { + "node": ">=12" + } + }, "node_modules/yn": { "version": "3.1.1", "resolved": "https://registry.npmjs.org/yn/-/yn-3.1.1.tgz", @@ -6420,24 +6482,22 @@ "yallist": "4.0.0" }, "devDependencies": { - "@istanbuljs/nyc-config-typescript": "^1.0.1", + "@istanbuljs/nyc-config-typescript": "^1.0.2", "@node-redis/test-utils": "*", - "@types/node": "^16.11.10", + "@types/node": "^16.11.12", "@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.3.0", + "@typescript-eslint/eslint-plugin": "^5.7.0", + "@typescript-eslint/parser": "^5.7.0", + "eslint": "^8.4.1", "nyc": "^15.1.0", "release-it": "^14.11.8", "sinon": "^12.0.1", "source-map-support": "^0.5.21", "ts-node": "^10.4.0", "typedoc": "^0.22.10", - "typedoc-github-wiki-theme": "^0.6.0", - "typedoc-plugin-markdown": "^3.11.7", - "typescript": "^4.5.2" + "typescript": "^4.5.3" }, "engines": { "node": ">=12" @@ -6447,14 +6507,15 @@ "version": "1.0.0-rc.0", "license": "MIT", "devDependencies": { - "@istanbuljs/nyc-config-typescript": "^1.0.1", + "@istanbuljs/nyc-config-typescript": "^1.0.2", "@node-redis/test-utils": "*", - "@types/node": "^16.11.10", + "@types/node": "^16.11.12", "nyc": "^15.1.0", "release-it": "^14.11.8", "source-map-support": "^0.5.21", "ts-node": "^10.4.0", - "typescript": "^4.5.2" + "typedoc": "^0.22.10", + "typescript": "^4.5.3" }, "peerDependencies": { "@node-redis/client": "^1.0.0" @@ -6464,14 +6525,15 @@ "version": "1.0.0", "license": "MIT", "devDependencies": { - "@istanbuljs/nyc-config-typescript": "^1.0.1", + "@istanbuljs/nyc-config-typescript": "^1.0.2", "@node-redis/test-utils": "*", - "@types/node": "^16.11.10", + "@types/node": "^16.11.12", "nyc": "^15.1.0", "release-it": "^14.11.8", "source-map-support": "^0.5.21", "ts-node": "^10.4.0", - "typescript": "^4.5.2" + "typedoc": "^0.22.10", + "typescript": "^4.5.3" }, "peerDependencies": { "@node-redis/client": "^1.0.0" @@ -6479,17 +6541,35 @@ }, "packages/test-utils": { "devDependencies": { - "@istanbuljs/nyc-config-typescript": "^1.0.1", + "@istanbuljs/nyc-config-typescript": "^1.0.2", "@types/mocha": "^9.0.0", - "@types/node": "^16.11.10", + "@types/node": "^16.11.12", "@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" + "typescript": "^4.5.3", + "yargs": "^17.3.0" + }, + "peerDependencies": { + "@node-redis/client": "^1.0.0" + } + }, + "packages/time-series": { + "version": "1.0.0-rc.0", + "license": "MIT", + "devDependencies": { + "@istanbuljs/nyc-config-typescript": "^1.0.2", + "@node-redis/test-utils": "*", + "@types/node": "^16.11.12", + "nyc": "^15.1.0", + "release-it": "^14.11.8", + "source-map-support": "^0.5.21", + "ts-node": "^10.4.0", + "typedoc": "^0.22.10", + "typescript": "^4.5.3" }, "peerDependencies": { "@node-redis/client": "^1.0.0" @@ -6533,14 +6613,6 @@ "json5": "^2.1.2", "semver": "^6.3.0", "source-map": "^0.5.0" - }, - "dependencies": { - "semver": { - "version": "6.3.0", - "resolved": "https://registry.npmjs.org/semver/-/semver-6.3.0.tgz", - "integrity": "sha512-b39TBaTSfV6yBrapU89p5fKekE2m/NwnDocOVruQFS1/veMgdzuPcnOM34M6CwxW8jH/lxEa5rBoDeUwu5HHTw==", - "dev": true - } } }, "@babel/generator": { @@ -6564,14 +6636,6 @@ "@babel/helper-validator-option": "^7.14.5", "browserslist": "^4.17.5", "semver": "^6.3.0" - }, - "dependencies": { - "semver": { - "version": "6.3.0", - "resolved": "https://registry.npmjs.org/semver/-/semver-6.3.0.tgz", - "integrity": "sha512-b39TBaTSfV6yBrapU89p5fKekE2m/NwnDocOVruQFS1/veMgdzuPcnOM34M6CwxW8jH/lxEa5rBoDeUwu5HHTw==", - "dev": true - } } }, "@babel/helper-function-name": { @@ -6830,14 +6894,14 @@ } }, "@eslint/eslintrc": { - "version": "1.0.4", - "resolved": "https://registry.npmjs.org/@eslint/eslintrc/-/eslintrc-1.0.4.tgz", - "integrity": "sha512-h8Vx6MdxwWI2WM8/zREHMoqdgLNXEL4QX3MWSVMdyNJGvXVOs+6lp+m2hc3FnuMHDc4poxFNI20vCk0OmI4G0Q==", + "version": "1.0.5", + "resolved": "https://registry.npmjs.org/@eslint/eslintrc/-/eslintrc-1.0.5.tgz", + "integrity": "sha512-BLxsnmK3KyPunz5wmCCpqy0YelEoxxGmH73Is+Z74oOTMtExcjkr3dDR6quwrjh1YspA8DH9gnX1o069KiS9AQ==", "dev": true, "requires": { "ajv": "^6.12.4", "debug": "^4.3.2", - "espree": "^9.0.0", + "espree": "^9.2.0", "globals": "^13.9.0", "ignore": "^4.0.6", "import-fresh": "^3.2.1", @@ -6861,12 +6925,12 @@ } }, "@humanwhocodes/config-array": { - "version": "0.6.0", - "resolved": "https://registry.npmjs.org/@humanwhocodes/config-array/-/config-array-0.6.0.tgz", - "integrity": "sha512-JQlEKbcgEUjBFhLIF4iqM7u/9lwgHRBcpHrmUNCALK0Q3amXN6lxdoXLnF0sm11E9VqTmBALR87IlUg1bZ8A9A==", + "version": "0.9.2", + "resolved": "https://registry.npmjs.org/@humanwhocodes/config-array/-/config-array-0.9.2.tgz", + "integrity": "sha512-UXOuFCGcwciWckOpmfKDq/GyhlTf9pN/BzG//x8p8zTOFEcGuA68ANXheFS0AGvy3qgZqLBUkMs7hqzqCKOVwA==", "dev": true, "requires": { - "@humanwhocodes/object-schema": "^1.2.0", + "@humanwhocodes/object-schema": "^1.2.1", "debug": "^4.1.1", "minimatch": "^3.0.4" } @@ -6911,16 +6975,6 @@ "integrity": "sha512-L28STB170nwWS63UjtlEOE3dldQApaJXZkOI1uMFfzf3rRuPegHaHesyee+YxQ+W6SvRDQV6UrdOdRiR153wJg==", "dev": true }, - "find-up": { - "version": "4.1.0", - "resolved": "https://registry.npmjs.org/find-up/-/find-up-4.1.0.tgz", - "integrity": "sha512-PpOwAdQ/YlXQ2vj8a3h8IipDuYRi3wceVQQGYWxNINccq40Anw7BlsEXCMbt1Zt+OLA6Fq9suIpIWD0OsnISlw==", - "dev": true, - "requires": { - "locate-path": "^5.0.0", - "path-exists": "^4.0.0" - } - }, "js-yaml": { "version": "3.14.1", "resolved": "https://registry.npmjs.org/js-yaml/-/js-yaml-3.14.1.tgz", @@ -6931,33 +6985,6 @@ "esprima": "^4.0.0" } }, - "locate-path": { - "version": "5.0.0", - "resolved": "https://registry.npmjs.org/locate-path/-/locate-path-5.0.0.tgz", - "integrity": "sha512-t7hw9pI+WvuwNJXwk5zVHpyhIqzg2qTlklJOf0mVxGSbe3Fp2VieZcduNYjaLDoy6p9uGpQEGWG87WpMKlNq8g==", - "dev": true, - "requires": { - "p-locate": "^4.1.0" - } - }, - "p-limit": { - "version": "2.3.0", - "resolved": "https://registry.npmjs.org/p-limit/-/p-limit-2.3.0.tgz", - "integrity": "sha512-//88mFWSJx8lxCzwdAABTJL2MyWB12+eIY7MDL2SqLmAkeKU9qxRvWuSyTjm3FUmpBEMuFfckAIqEaVGUDxb6w==", - "dev": true, - "requires": { - "p-try": "^2.0.0" - } - }, - "p-locate": { - "version": "4.1.0", - "resolved": "https://registry.npmjs.org/p-locate/-/p-locate-4.1.0.tgz", - "integrity": "sha512-R79ZZ/0wAxKGu3oYMlz8jy/kbhsNrS7SKZ7PxEHBgJ5+F2mtFW2fK2cOtBh1cHYkQsbzFV7I+EoRKe6Yt0oK7A==", - "dev": true, - "requires": { - "p-limit": "^2.2.0" - } - }, "resolve-from": { "version": "5.0.0", "resolved": "https://registry.npmjs.org/resolve-from/-/resolve-from-5.0.0.tgz", @@ -6967,9 +6994,9 @@ } }, "@istanbuljs/nyc-config-typescript": { - "version": "1.0.1", - "resolved": "https://registry.npmjs.org/@istanbuljs/nyc-config-typescript/-/nyc-config-typescript-1.0.1.tgz", - "integrity": "sha512-/gz6LgVpky205LuoOfwEZmnUtaSmdk0QIMcNFj9OvxhiMhPpKftMgZmGN7jNj7jR+lr8IB1Yks3QSSSNSxfoaQ==", + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/@istanbuljs/nyc-config-typescript/-/nyc-config-typescript-1.0.2.tgz", + "integrity": "sha512-iKGIyMoyJuFnJRSVTZ78POIRvNnwZaWIf8vG4ZS3rQq58MMDrqEX2nnzx0R28V2X8JvmKYiqY9FP2hlJsm8A0w==", "dev": true, "requires": { "@istanbuljs/schema": "^0.1.2" @@ -6984,16 +7011,16 @@ "@node-redis/client": { "version": "file:packages/client", "requires": { - "@istanbuljs/nyc-config-typescript": "^1.0.1", + "@istanbuljs/nyc-config-typescript": "^1.0.2", "@node-redis/test-utils": "*", - "@types/node": "^16.11.10", + "@types/node": "^16.11.12", "@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", + "@typescript-eslint/eslint-plugin": "^5.7.0", + "@typescript-eslint/parser": "^5.7.0", "cluster-key-slot": "1.1.0", - "eslint": "^8.3.0", + "eslint": "^8.4.1", "generic-pool": "3.8.2", "nyc": "^15.1.0", "redis-parser": "3.0.0", @@ -7002,52 +7029,66 @@ "source-map-support": "^0.5.21", "ts-node": "^10.4.0", "typedoc": "^0.22.10", - "typedoc-github-wiki-theme": "^0.6.0", - "typedoc-plugin-markdown": "^3.11.7", - "typescript": "^4.5.2", + "typescript": "^4.5.3", "yallist": "4.0.0" } }, "@node-redis/json": { "version": "file:packages/json", "requires": { - "@istanbuljs/nyc-config-typescript": "^1.0.1", + "@istanbuljs/nyc-config-typescript": "^1.0.2", "@node-redis/test-utils": "*", - "@types/node": "^16.11.10", + "@types/node": "^16.11.12", "nyc": "^15.1.0", "release-it": "^14.11.8", "source-map-support": "^0.5.21", "ts-node": "^10.4.0", - "typescript": "^4.5.2" + "typedoc": "^0.22.10", + "typescript": "^4.5.3" } }, "@node-redis/search": { "version": "file:packages/search", "requires": { - "@istanbuljs/nyc-config-typescript": "^1.0.1", + "@istanbuljs/nyc-config-typescript": "^1.0.2", "@node-redis/test-utils": "*", - "@types/node": "^16.11.10", + "@types/node": "^16.11.12", "nyc": "^15.1.0", "release-it": "^14.11.8", "source-map-support": "^0.5.21", "ts-node": "^10.4.0", - "typescript": "^4.5.2" + "typedoc": "^0.22.10", + "typescript": "^4.5.3" } }, "@node-redis/test-utils": { "version": "file:packages/test-utils", "requires": { - "@istanbuljs/nyc-config-typescript": "^1.0.1", + "@istanbuljs/nyc-config-typescript": "^1.0.2", "@types/mocha": "^9.0.0", - "@types/node": "^16.11.10", + "@types/node": "^16.11.12", "@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" + "typescript": "^4.5.3", + "yargs": "^17.3.0" + } + }, + "@node-redis/time-series": { + "version": "file:packages/time-series", + "requires": { + "@istanbuljs/nyc-config-typescript": "^1.0.2", + "@node-redis/test-utils": "*", + "@types/node": "^16.11.12", + "nyc": "^15.1.0", + "release-it": "^14.11.8", + "source-map-support": "^0.5.21", + "ts-node": "^10.4.0", + "typedoc": "^0.22.10", + "typescript": "^4.5.3" } }, "@nodelib/fs.scandir": { @@ -7314,9 +7355,9 @@ "dev": true }, "@types/node": { - "version": "16.11.10", - "resolved": "https://registry.npmjs.org/@types/node/-/node-16.11.10.tgz", - "integrity": "sha512-3aRnHa1KlOEEhJ6+CvyHKK5vE9BcLGjtUpwvqYLRvYNQKMfabu3BwfJaA/SLW8dxe28LsNDjtHwePTuzn3gmOA==", + "version": "16.11.12", + "resolved": "https://registry.npmjs.org/@types/node/-/node-16.11.12.tgz", + "integrity": "sha512-+2Iggwg7PxoO5Kyhvsq9VarmPbIelXP070HMImEpbtGCoyWNINQj4wzjbQCXzdHTRXnqufutJb5KAURZANNBAw==", "dev": true }, "@types/parse-json": { @@ -7381,85 +7422,127 @@ "dev": true }, "@typescript-eslint/eslint-plugin": { - "version": "5.4.0", - "resolved": "https://registry.npmjs.org/@typescript-eslint/eslint-plugin/-/eslint-plugin-5.4.0.tgz", - "integrity": "sha512-9/yPSBlwzsetCsGEn9j24D8vGQgJkOTr4oMLas/w886ZtzKIs1iyoqFrwsX2fqYEeUwsdBpC21gcjRGo57u0eg==", + "version": "5.7.0", + "resolved": "https://registry.npmjs.org/@typescript-eslint/eslint-plugin/-/eslint-plugin-5.7.0.tgz", + "integrity": "sha512-8RTGBpNn5a9M628wBPrCbJ+v3YTEOE2qeZb7TDkGKTDXSj36KGRg92SpFFaR/0S3rSXQxM0Og/kV9EyadsYSBg==", "dev": true, "requires": { - "@typescript-eslint/experimental-utils": "5.4.0", - "@typescript-eslint/scope-manager": "5.4.0", + "@typescript-eslint/experimental-utils": "5.7.0", + "@typescript-eslint/scope-manager": "5.7.0", "debug": "^4.3.2", "functional-red-black-tree": "^1.0.1", "ignore": "^5.1.8", "regexpp": "^3.2.0", "semver": "^7.3.5", "tsutils": "^3.21.0" + }, + "dependencies": { + "semver": { + "version": "7.3.5", + "resolved": "https://registry.npmjs.org/semver/-/semver-7.3.5.tgz", + "integrity": "sha512-PoeGJYh8HK4BTO/a9Tf6ZG3veo/A7ZVsYrSA6J8ny9nb3B1VrpkuN+z9OE5wfE5p6H4LchYZsegiQgbJD94ZFQ==", + "dev": true, + "requires": { + "lru-cache": "^6.0.0" + } + } } }, "@typescript-eslint/experimental-utils": { - "version": "5.4.0", - "resolved": "https://registry.npmjs.org/@typescript-eslint/experimental-utils/-/experimental-utils-5.4.0.tgz", - "integrity": "sha512-Nz2JDIQUdmIGd6p33A+naQmwfkU5KVTLb/5lTk+tLVTDacZKoGQisj8UCxk7onJcrgjIvr8xWqkYI+DbI3TfXg==", + "version": "5.7.0", + "resolved": "https://registry.npmjs.org/@typescript-eslint/experimental-utils/-/experimental-utils-5.7.0.tgz", + "integrity": "sha512-u57eZ5FbEpzN5kSjmVrSesovWslH2ZyNPnaXQMXWgH57d5+EVHEt76W75vVuI9qKZ5BMDKNfRN+pxcPEjQjb2A==", "dev": true, "requires": { "@types/json-schema": "^7.0.9", - "@typescript-eslint/scope-manager": "5.4.0", - "@typescript-eslint/types": "5.4.0", - "@typescript-eslint/typescript-estree": "5.4.0", + "@typescript-eslint/scope-manager": "5.7.0", + "@typescript-eslint/types": "5.7.0", + "@typescript-eslint/typescript-estree": "5.7.0", "eslint-scope": "^5.1.1", "eslint-utils": "^3.0.0" } }, "@typescript-eslint/parser": { - "version": "5.4.0", - "resolved": "https://registry.npmjs.org/@typescript-eslint/parser/-/parser-5.4.0.tgz", - "integrity": "sha512-JoB41EmxiYpaEsRwpZEYAJ9XQURPFer8hpkIW9GiaspVLX8oqbqNM8P4EP8HOZg96yaALiLEVWllA2E8vwsIKw==", + "version": "5.7.0", + "resolved": "https://registry.npmjs.org/@typescript-eslint/parser/-/parser-5.7.0.tgz", + "integrity": "sha512-m/gWCCcS4jXw6vkrPQ1BjZ1vomP01PArgzvauBqzsoZ3urLbsRChexB8/YV8z9HwE3qlJM35FxfKZ1nfP/4x8g==", "dev": true, "requires": { - "@typescript-eslint/scope-manager": "5.4.0", - "@typescript-eslint/types": "5.4.0", - "@typescript-eslint/typescript-estree": "5.4.0", + "@typescript-eslint/scope-manager": "5.7.0", + "@typescript-eslint/types": "5.7.0", + "@typescript-eslint/typescript-estree": "5.7.0", "debug": "^4.3.2" } }, "@typescript-eslint/scope-manager": { - "version": "5.4.0", - "resolved": "https://registry.npmjs.org/@typescript-eslint/scope-manager/-/scope-manager-5.4.0.tgz", - "integrity": "sha512-pRxFjYwoi8R+n+sibjgF9iUiAELU9ihPBtHzocyW8v8D8G8KeQvXTsW7+CBYIyTYsmhtNk50QPGLE3vrvhM5KA==", + "version": "5.7.0", + "resolved": "https://registry.npmjs.org/@typescript-eslint/scope-manager/-/scope-manager-5.7.0.tgz", + "integrity": "sha512-7mxR520DGq5F7sSSgM0HSSMJ+TFUymOeFRMfUfGFAVBv8BR+Jv1vHgAouYUvWRZeszVBJlLcc9fDdktxb5kmxA==", "dev": true, "requires": { - "@typescript-eslint/types": "5.4.0", - "@typescript-eslint/visitor-keys": "5.4.0" + "@typescript-eslint/types": "5.7.0", + "@typescript-eslint/visitor-keys": "5.7.0" } }, "@typescript-eslint/types": { - "version": "5.4.0", - "resolved": "https://registry.npmjs.org/@typescript-eslint/types/-/types-5.4.0.tgz", - "integrity": "sha512-GjXNpmn+n1LvnttarX+sPD6+S7giO+9LxDIGlRl4wK3a7qMWALOHYuVSZpPTfEIklYjaWuMtfKdeByx0AcaThA==", + "version": "5.7.0", + "resolved": "https://registry.npmjs.org/@typescript-eslint/types/-/types-5.7.0.tgz", + "integrity": "sha512-5AeYIF5p2kAneIpnLFve8g50VyAjq7udM7ApZZ9JYjdPjkz0LvODfuSHIDUVnIuUoxafoWzpFyU7Sqbxgi79mA==", "dev": true }, "@typescript-eslint/typescript-estree": { - "version": "5.4.0", - "resolved": "https://registry.npmjs.org/@typescript-eslint/typescript-estree/-/typescript-estree-5.4.0.tgz", - "integrity": "sha512-nhlNoBdhKuwiLMx6GrybPT3SFILm5Gij2YBdPEPFlYNFAXUJWX6QRgvi/lwVoadaQEFsizohs6aFRMqsXI2ewA==", + "version": "5.7.0", + "resolved": "https://registry.npmjs.org/@typescript-eslint/typescript-estree/-/typescript-estree-5.7.0.tgz", + "integrity": "sha512-aO1Ql+izMrTnPj5aFFlEJkpD4jRqC4Gwhygu2oHK2wfVQpmOPbyDSveJ+r/NQo+PWV43M6uEAeLVbTi09dFLhg==", "dev": true, "requires": { - "@typescript-eslint/types": "5.4.0", - "@typescript-eslint/visitor-keys": "5.4.0", + "@typescript-eslint/types": "5.7.0", + "@typescript-eslint/visitor-keys": "5.7.0", "debug": "^4.3.2", "globby": "^11.0.4", "is-glob": "^4.0.3", "semver": "^7.3.5", "tsutils": "^3.21.0" + }, + "dependencies": { + "array-union": { + "version": "2.1.0", + "resolved": "https://registry.npmjs.org/array-union/-/array-union-2.1.0.tgz", + "integrity": "sha512-HGyxoOTYUyCM6stUe6EJgnd4EoewAI7zMdfqO+kGjnlZmBDz/cR5pf8r/cR4Wq60sL/p0IkcjUEEPwS3GFrIyw==", + "dev": true + }, + "globby": { + "version": "11.0.4", + "resolved": "https://registry.npmjs.org/globby/-/globby-11.0.4.tgz", + "integrity": "sha512-9O4MVG9ioZJ08ffbcyVYyLOJLk5JQ688pJ4eMGLpdWLHq/Wr1D9BlriLQyL0E+jbkuePVZXYFj47QM/v093wHg==", + "dev": true, + "requires": { + "array-union": "^2.1.0", + "dir-glob": "^3.0.1", + "fast-glob": "^3.1.1", + "ignore": "^5.1.4", + "merge2": "^1.3.0", + "slash": "^3.0.0" + } + }, + "semver": { + "version": "7.3.5", + "resolved": "https://registry.npmjs.org/semver/-/semver-7.3.5.tgz", + "integrity": "sha512-PoeGJYh8HK4BTO/a9Tf6ZG3veo/A7ZVsYrSA6J8ny9nb3B1VrpkuN+z9OE5wfE5p6H4LchYZsegiQgbJD94ZFQ==", + "dev": true, + "requires": { + "lru-cache": "^6.0.0" + } + } } }, "@typescript-eslint/visitor-keys": { - "version": "5.4.0", - "resolved": "https://registry.npmjs.org/@typescript-eslint/visitor-keys/-/visitor-keys-5.4.0.tgz", - "integrity": "sha512-PVbax7MeE7tdLfW5SA0fs8NGVVr+buMPrcj+CWYWPXsZCH8qZ1THufDzbXm1xrZ2b2PA1iENJ0sRq5fuUtvsJg==", + "version": "5.7.0", + "resolved": "https://registry.npmjs.org/@typescript-eslint/visitor-keys/-/visitor-keys-5.7.0.tgz", + "integrity": "sha512-hdohahZ4lTFcglZSJ3DGdzxQHBSxsLVqHzkiOmKi7xVAWC4y2c1bIMKmPJSrA4aOEoRUPOKQ87Y/taC7yVHpFg==", "dev": true, "requires": { - "@typescript-eslint/types": "5.4.0", + "@typescript-eslint/types": "5.7.0", "eslint-visitor-keys": "^3.0.0" } }, @@ -7587,11 +7670,29 @@ "dev": true }, "array-union": { - "version": "2.1.0", - "resolved": "https://registry.npmjs.org/array-union/-/array-union-2.1.0.tgz", - "integrity": "sha512-HGyxoOTYUyCM6stUe6EJgnd4EoewAI7zMdfqO+kGjnlZmBDz/cR5pf8r/cR4Wq60sL/p0IkcjUEEPwS3GFrIyw==", + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/array-union/-/array-union-1.0.2.tgz", + "integrity": "sha1-mjRBDk9OPaI96jdb5b5w8kd47Dk=", + "dev": true, + "requires": { + "array-uniq": "^1.0.1" + } + }, + "array-uniq": { + "version": "1.0.3", + "resolved": "https://registry.npmjs.org/array-uniq/-/array-uniq-1.0.3.tgz", + "integrity": "sha1-r2rId6Jcx/dOBYiUdThY39sk/bY=", "dev": true }, + "async": { + "version": "2.6.3", + "resolved": "https://registry.npmjs.org/async/-/async-2.6.3.tgz", + "integrity": "sha512-zflvls11DCy+dQWzTW2dzuilv8Z5X/pjfmZOWba6TNIVDm+2UDaJmXSOXlasHKfNBs8oo3M0aT50fDEWfKZjXg==", + "dev": true, + "requires": { + "lodash": "^4.17.14" + } + }, "async-retry": { "version": "1.3.3", "resolved": "https://registry.npmjs.org/async-retry/-/async-retry-1.3.3.tgz", @@ -7787,9 +7888,9 @@ "dev": true }, "caniuse-lite": { - "version": "1.0.30001283", - "resolved": "https://registry.npmjs.org/caniuse-lite/-/caniuse-lite-1.0.30001283.tgz", - "integrity": "sha512-9RoKo841j1GQFSJz/nCXOj0sD7tHBtlowjYlrqIUS812x9/emfBLBt6IyMz1zIaYc/eRL8Cs6HPUVi2Hzq4sIg==", + "version": "1.0.30001286", + "resolved": "https://registry.npmjs.org/caniuse-lite/-/caniuse-lite-1.0.30001286.tgz", + "integrity": "sha512-zaEMRH6xg8ESMi2eQ3R4eZ5qw/hJiVsO/HlLwniIwErij0JDr9P+8V4dtx1l+kLq6j3yy8l8W4fst1lBnat5wQ==", "dev": true }, "chalk": { @@ -7918,6 +8019,12 @@ "delayed-stream": "~1.0.0" } }, + "commander": { + "version": "2.20.3", + "resolved": "https://registry.npmjs.org/commander/-/commander-2.20.3.tgz", + "integrity": "sha512-GpVkmM8vF2vQUkj2LvZmD35JxeJOLCwJ9cUkugyk2nuhbv3+mJvpLYYt+0+USMxE+oj+ey/lJEnhZw75x/OMcQ==", + "dev": true + }, "commondir": { "version": "1.0.1", "resolved": "https://registry.npmjs.org/commondir/-/commondir-1.0.1.tgz", @@ -8133,9 +8240,15 @@ "dev": true }, "electron-to-chromium": { - "version": "1.4.1", - "resolved": "https://registry.npmjs.org/electron-to-chromium/-/electron-to-chromium-1.4.1.tgz", - "integrity": "sha512-9ldvb6QMHiDpUNF1iSwBTiTT0qXEN+xIO5WlCJrC5gt0z74ofOiqR698vaJqYWnri0XZiF0YmnrFmGq/EmpGAA==", + "version": "1.4.17", + "resolved": "https://registry.npmjs.org/electron-to-chromium/-/electron-to-chromium-1.4.17.tgz", + "integrity": "sha512-zhk1MravPtq/KBhmGB7TLBILmXTgRG9TFSI3qS3DbgyfHzIl72iiTE37r/BHIbPCJJlWIo5rySyxiH4vWhu2ZA==", + "dev": true + }, + "email-addresses": { + "version": "3.1.0", + "resolved": "https://registry.npmjs.org/email-addresses/-/email-addresses-3.1.0.tgz", + "integrity": "sha512-k0/r7GrWVL32kZlGwfPNgB2Y/mMXVTq/decgLczm/j34whdaspNrZO8CnXPf1laaHxI6ptUlsnAxN+UAPw+fzg==", "dev": true }, "emoji-regex": { @@ -8196,13 +8309,13 @@ "dev": true }, "eslint": { - "version": "8.3.0", - "resolved": "https://registry.npmjs.org/eslint/-/eslint-8.3.0.tgz", - "integrity": "sha512-aIay56Ph6RxOTC7xyr59Kt3ewX185SaGnAr8eWukoPLeriCrvGjvAubxuvaXOfsxhtwV5g0uBOsyhAom4qJdww==", + "version": "8.4.1", + "resolved": "https://registry.npmjs.org/eslint/-/eslint-8.4.1.tgz", + "integrity": "sha512-TxU/p7LB1KxQ6+7aztTnO7K0i+h0tDi81YRY9VzB6Id71kNz+fFYnf5HD5UOQmxkzcoa0TlVZf9dpMtUv0GpWg==", "dev": true, "requires": { - "@eslint/eslintrc": "^1.0.4", - "@humanwhocodes/config-array": "^0.6.0", + "@eslint/eslintrc": "^1.0.5", + "@humanwhocodes/config-array": "^0.9.2", "ajv": "^6.10.0", "chalk": "^4.0.0", "cross-spawn": "^7.0.2", @@ -8213,7 +8326,7 @@ "eslint-scope": "^7.1.0", "eslint-utils": "^3.0.0", "eslint-visitor-keys": "^3.1.0", - "espree": "^9.1.0", + "espree": "^9.2.0", "esquery": "^1.4.0", "esutils": "^2.0.2", "fast-deep-equal": "^3.1.3", @@ -8278,6 +8391,15 @@ "integrity": "sha512-cyFDKrqc/YdcWFniJhzI42+AzS+gNwmUzOSFcRCQYwySuBBBy/KjuxWLZ/FHEH6Moq1NizMOBWyTcv8O4OZIMg==", "dev": true }, + "semver": { + "version": "7.3.5", + "resolved": "https://registry.npmjs.org/semver/-/semver-7.3.5.tgz", + "integrity": "sha512-PoeGJYh8HK4BTO/a9Tf6ZG3veo/A7ZVsYrSA6J8ny9nb3B1VrpkuN+z9OE5wfE5p6H4LchYZsegiQgbJD94ZFQ==", + "dev": true, + "requires": { + "lru-cache": "^6.0.0" + } + }, "strip-json-comments": { "version": "3.1.1", "resolved": "https://registry.npmjs.org/strip-json-comments/-/strip-json-comments-3.1.1.tgz", @@ -8320,9 +8442,9 @@ "dev": true }, "espree": { - "version": "9.1.0", - "resolved": "https://registry.npmjs.org/espree/-/espree-9.1.0.tgz", - "integrity": "sha512-ZgYLvCS1wxOczBYGcQT9DDWgicXwJ4dbocr9uYN+/eresBAUuBu+O4WzB21ufQ/JqQT8gyp7hJ3z8SHii32mTQ==", + "version": "9.2.0", + "resolved": "https://registry.npmjs.org/espree/-/espree-9.2.0.tgz", + "integrity": "sha512-oP3utRkynpZWF/F2x/HZJ+AGtnIclaR7z1pYPxy7NYM2fSO6LgK/Rkny8anRSPK/VwEA1eqm2squui0T7ZMOBg==", "dev": true, "requires": { "acorn": "^8.6.0", @@ -8468,6 +8590,23 @@ "flat-cache": "^3.0.4" } }, + "filename-reserved-regex": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/filename-reserved-regex/-/filename-reserved-regex-2.0.0.tgz", + "integrity": "sha1-q/c9+rc10EVECr/qLZHzieu/oik=", + "dev": true + }, + "filenamify": { + "version": "4.3.0", + "resolved": "https://registry.npmjs.org/filenamify/-/filenamify-4.3.0.tgz", + "integrity": "sha512-hcFKyUG57yWGAzu1CMt/dPzYZuv+jAJUT85bL8mrXvNe6hWj6yEHEc4EdcgiA6Z3oi1/9wXJdZPXF2dZNgwgOg==", + "dev": true, + "requires": { + "filename-reserved-regex": "^2.0.0", + "strip-outer": "^1.0.1", + "trim-repeated": "^1.0.0" + } + }, "fill-range": { "version": "7.0.1", "resolved": "https://registry.npmjs.org/fill-range/-/fill-range-7.0.1.tgz", @@ -8495,12 +8634,12 @@ } }, "find-up": { - "version": "5.0.0", - "resolved": "https://registry.npmjs.org/find-up/-/find-up-5.0.0.tgz", - "integrity": "sha512-78/PXT1wlLLDgTzDs7sjq9hzz0vXD+zn+7wypEe4fXQxCmdmqfGsEPQxmiCSQI3ajFV91bVSsvNtrJRiW6nGng==", + "version": "4.1.0", + "resolved": "https://registry.npmjs.org/find-up/-/find-up-4.1.0.tgz", + "integrity": "sha512-PpOwAdQ/YlXQ2vj8a3h8IipDuYRi3wceVQQGYWxNINccq40Anw7BlsEXCMbt1Zt+OLA6Fq9suIpIWD0OsnISlw==", "dev": true, "requires": { - "locate-path": "^6.0.0", + "locate-path": "^5.0.0", "path-exists": "^4.0.0" } }, @@ -8553,6 +8692,17 @@ "integrity": "sha512-cHEpEQHUg0f8XdtZCc2ZAhrHzKzT0MrFUTcvx+hfxYu7rGMDc5SKoXFh+n4YigxsHXRzc6OrCshdR1bWH6HHyg==", "dev": true }, + "fs-extra": { + "version": "8.1.0", + "resolved": "https://registry.npmjs.org/fs-extra/-/fs-extra-8.1.0.tgz", + "integrity": "sha512-yhlQgA6mnOJUKOsRUFsgJdQCvkKhcz8tlZG5HBQfReYZy46OwLcY+Zia0mtdHsOo9y/hP+CxMN0TU9QxoOtG4g==", + "dev": true, + "requires": { + "graceful-fs": "^4.2.0", + "jsonfile": "^4.0.0", + "universalify": "^0.1.0" + } + }, "fs.realpath": { "version": "1.0.0", "resolved": "https://registry.npmjs.org/fs.realpath/-/fs.realpath-1.0.0.tgz", @@ -8618,6 +8768,21 @@ "integrity": "sha512-ts6Wi+2j3jQjqi70w5AlN8DFnkSwC+MqmxEzdEALB2qXZYV3X/b1CTfgPLGJNMeAWxdPfU8FO1ms3NUfaHCPYg==", "dev": true }, + "gh-pages": { + "version": "3.2.3", + "resolved": "https://registry.npmjs.org/gh-pages/-/gh-pages-3.2.3.tgz", + "integrity": "sha512-jA1PbapQ1jqzacECfjUaO9gV8uBgU6XNMV0oXLtfCX3haGLe5Atq8BxlrADhbD6/UdG9j6tZLWAkAybndOXTJg==", + "dev": true, + "requires": { + "async": "^2.6.1", + "commander": "^2.18.0", + "email-addresses": "^3.0.1", + "filenamify": "^4.3.0", + "find-cache-dir": "^3.3.1", + "fs-extra": "^8.1.0", + "globby": "^6.1.0" + } + }, "git-up": { "version": "4.0.5", "resolved": "https://registry.npmjs.org/git-up/-/git-up-4.0.5.tgz", @@ -8687,17 +8852,16 @@ } }, "globby": { - "version": "11.0.4", - "resolved": "https://registry.npmjs.org/globby/-/globby-11.0.4.tgz", - "integrity": "sha512-9O4MVG9ioZJ08ffbcyVYyLOJLk5JQ688pJ4eMGLpdWLHq/Wr1D9BlriLQyL0E+jbkuePVZXYFj47QM/v093wHg==", + "version": "6.1.0", + "resolved": "https://registry.npmjs.org/globby/-/globby-6.1.0.tgz", + "integrity": "sha1-9abXDoOV4hyFj7BInWTfAkJNUGw=", "dev": true, "requires": { - "array-union": "^2.1.0", - "dir-glob": "^3.0.1", - "fast-glob": "^3.1.1", - "ignore": "^5.1.4", - "merge2": "^1.3.0", - "slash": "^3.0.0" + "array-union": "^1.0.1", + "glob": "^7.0.3", + "object-assign": "^4.0.1", + "pify": "^2.0.0", + "pinkie-promise": "^2.0.0" } }, "got": { @@ -8731,27 +8895,6 @@ "integrity": "sha512-qBr4OuELkhPenW6goKVXiv47US3clb3/IbuWF9KNKEijAy9oeHxU9IgzjvJhHkUzhaj7rOUD7+YGWqUjLp5oSA==", "dev": true }, - "handlebars": { - "version": "4.7.7", - "resolved": "https://registry.npmjs.org/handlebars/-/handlebars-4.7.7.tgz", - "integrity": "sha512-aAcXm5OAfE/8IXkcZvCepKU3VzW1/39Fb5ZuqMtgI/hT8X2YgoMvBY5dLhq/cpOvw7Lk1nK/UF71aLG/ZnVYRA==", - "dev": true, - "requires": { - "minimist": "^1.2.5", - "neo-async": "^2.6.0", - "source-map": "^0.6.1", - "uglify-js": "^3.1.4", - "wordwrap": "^1.0.0" - }, - "dependencies": { - "source-map": { - "version": "0.6.1", - "resolved": "https://registry.npmjs.org/source-map/-/source-map-0.6.1.tgz", - "integrity": "sha512-UjgapumWlbMhkBgzT7Ykc5YXUT46F0iKu8SGXq0bcwP5dz/h0Plj6enJqjz1Zbq2l5WaqYnrVbwWOWMyF3F47g==", - "dev": true - } - } - }, "has": { "version": "1.0.3", "resolved": "https://registry.npmjs.org/has/-/has-1.0.3.tgz", @@ -9153,14 +9296,6 @@ "@istanbuljs/schema": "^0.1.2", "istanbul-lib-coverage": "^3.0.0", "semver": "^6.3.0" - }, - "dependencies": { - "semver": { - "version": "6.3.0", - "resolved": "https://registry.npmjs.org/semver/-/semver-6.3.0.tgz", - "integrity": "sha512-b39TBaTSfV6yBrapU89p5fKekE2m/NwnDocOVruQFS1/veMgdzuPcnOM34M6CwxW8jH/lxEa5rBoDeUwu5HHTw==", - "dev": true - } } }, "istanbul-lib-processinfo": { @@ -9217,9 +9352,9 @@ } }, "istanbul-reports": { - "version": "3.0.5", - "resolved": "https://registry.npmjs.org/istanbul-reports/-/istanbul-reports-3.0.5.tgz", - "integrity": "sha512-5+19PlhnGabNWB7kOFnuxT8H3T/iIyQzIbQMxXsURmmvKg86P2sbkrGOT77VnHw0Qr0gc2XzRaRfMZYYbSQCJQ==", + "version": "3.1.1", + "resolved": "https://registry.npmjs.org/istanbul-reports/-/istanbul-reports-3.1.1.tgz", + "integrity": "sha512-q1kvhAXWSsXfMjCdNHNPKZZv94OlspKnoGv+R9RGbnqOOQ0VbNfLFgQDVgi7hHenKsndGq3/o0OBdzDXthWcNw==", "dev": true, "requires": { "html-escaper": "^2.0.0", @@ -9286,6 +9421,15 @@ "integrity": "sha512-fQzRfAbIBnR0IQvftw9FJveWiHp72Fg20giDrHz6TdfB12UH/uue0D3hm57UB5KgAVuniLMCaS8P1IMj9NR7cA==", "dev": true }, + "jsonfile": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/jsonfile/-/jsonfile-4.0.0.tgz", + "integrity": "sha1-h3Gq4HmbZAdrdmQPygWPnBDjPss=", + "dev": true, + "requires": { + "graceful-fs": "^4.1.6" + } + }, "just-extend": { "version": "4.2.1", "resolved": "https://registry.npmjs.org/just-extend/-/just-extend-4.2.1.tgz", @@ -9327,12 +9471,12 @@ "dev": true }, "locate-path": { - "version": "6.0.0", - "resolved": "https://registry.npmjs.org/locate-path/-/locate-path-6.0.0.tgz", - "integrity": "sha512-iPZK6eYjbxRu3uB4/WZ3EsEIMJFMqAoopl3R+zuq0UjcAm/MO6KCweDgPfP3elTztoKP3KtnVHxTn2NHBSDVUw==", + "version": "5.0.0", + "resolved": "https://registry.npmjs.org/locate-path/-/locate-path-5.0.0.tgz", + "integrity": "sha512-t7hw9pI+WvuwNJXwk5zVHpyhIqzg2qTlklJOf0mVxGSbe3Fp2VieZcduNYjaLDoy6p9uGpQEGWG87WpMKlNq8g==", "dev": true, "requires": { - "p-locate": "^5.0.0" + "p-locate": "^4.1.0" } }, "lodash": { @@ -9403,14 +9547,6 @@ "dev": true, "requires": { "semver": "^6.0.0" - }, - "dependencies": { - "semver": { - "version": "6.3.0", - "resolved": "https://registry.npmjs.org/semver/-/semver-6.3.0.tgz", - "integrity": "sha512-b39TBaTSfV6yBrapU89p5fKekE2m/NwnDocOVruQFS1/veMgdzuPcnOM34M6CwxW8jH/lxEa5rBoDeUwu5HHTw==", - "dev": true - } } }, "make-error": { @@ -9527,6 +9663,16 @@ "integrity": "sha512-TtpcNJ3XAzx3Gq8sWRzJaVajRs0uVxA2YAkdb1jm2YkPz4G6egUFAyA3n5vtEIZefPk5Wa4UXbKuS5fKkJWdgA==", "dev": true }, + "find-up": { + "version": "5.0.0", + "resolved": "https://registry.npmjs.org/find-up/-/find-up-5.0.0.tgz", + "integrity": "sha512-78/PXT1wlLLDgTzDs7sjq9hzz0vXD+zn+7wypEe4fXQxCmdmqfGsEPQxmiCSQI3ajFV91bVSsvNtrJRiW6nGng==", + "dev": true, + "requires": { + "locate-path": "^6.0.0", + "path-exists": "^4.0.0" + } + }, "glob": { "version": "7.1.7", "resolved": "https://registry.npmjs.org/glob/-/glob-7.1.7.tgz", @@ -9541,12 +9687,39 @@ "path-is-absolute": "^1.0.0" } }, + "locate-path": { + "version": "6.0.0", + "resolved": "https://registry.npmjs.org/locate-path/-/locate-path-6.0.0.tgz", + "integrity": "sha512-iPZK6eYjbxRu3uB4/WZ3EsEIMJFMqAoopl3R+zuq0UjcAm/MO6KCweDgPfP3elTztoKP3KtnVHxTn2NHBSDVUw==", + "dev": true, + "requires": { + "p-locate": "^5.0.0" + } + }, "ms": { "version": "2.1.3", "resolved": "https://registry.npmjs.org/ms/-/ms-2.1.3.tgz", "integrity": "sha512-6FlzubTLZG3J2a/NVCAleEhjzq5oxgHyaCU9yYXvcLsvoVaHJq/s5xXI6/XXP6tz7R9xAOtHnSO/tXtF3WRTlA==", "dev": true }, + "p-limit": { + "version": "3.1.0", + "resolved": "https://registry.npmjs.org/p-limit/-/p-limit-3.1.0.tgz", + "integrity": "sha512-TYOanM3wGwNGsZN2cVTYPArw454xnXj5qmWF1bEoAc4+cU/ol7GVh7odevjp1FNHduHc3KZMcFduxU5Xc6uJRQ==", + "dev": true, + "requires": { + "yocto-queue": "^0.1.0" + } + }, + "p-locate": { + "version": "5.0.0", + "resolved": "https://registry.npmjs.org/p-locate/-/p-locate-5.0.0.tgz", + "integrity": "sha512-LaNjtRWUBY++zB5nE/NwcaoMylSPk+S+ZHNB1TzdbMJMny6dynpAGt7X/tl/QYq3TIeE6nxHppbo2LGymrG5Pw==", + "dev": true, + "requires": { + "p-limit": "^3.0.2" + } + }, "strip-json-comments": { "version": "3.1.1", "resolved": "https://registry.npmjs.org/strip-json-comments/-/strip-json-comments-3.1.1.tgz", @@ -9609,12 +9782,6 @@ "integrity": "sha1-Sr6/7tdUHywnrPspvbvRXI1bpPc=", "dev": true }, - "neo-async": { - "version": "2.6.2", - "resolved": "https://registry.npmjs.org/neo-async/-/neo-async-2.6.2.tgz", - "integrity": "sha512-Yd3UES5mWCSqR+qNT93S3UoYUkqAZ9lLg8a7g9rimsWmYGK8cVToA4/sF3RrshdyV3sAGMXVUmpMYOw+dLpOuw==", - "dev": true - }, "new-github-release-url": { "version": "1.0.0", "resolved": "https://registry.npmjs.org/new-github-release-url/-/new-github-release-url-1.0.0.tgz", @@ -9742,43 +9909,6 @@ "wrap-ansi": "^6.2.0" } }, - "find-up": { - "version": "4.1.0", - "resolved": "https://registry.npmjs.org/find-up/-/find-up-4.1.0.tgz", - "integrity": "sha512-PpOwAdQ/YlXQ2vj8a3h8IipDuYRi3wceVQQGYWxNINccq40Anw7BlsEXCMbt1Zt+OLA6Fq9suIpIWD0OsnISlw==", - "dev": true, - "requires": { - "locate-path": "^5.0.0", - "path-exists": "^4.0.0" - } - }, - "locate-path": { - "version": "5.0.0", - "resolved": "https://registry.npmjs.org/locate-path/-/locate-path-5.0.0.tgz", - "integrity": "sha512-t7hw9pI+WvuwNJXwk5zVHpyhIqzg2qTlklJOf0mVxGSbe3Fp2VieZcduNYjaLDoy6p9uGpQEGWG87WpMKlNq8g==", - "dev": true, - "requires": { - "p-locate": "^4.1.0" - } - }, - "p-limit": { - "version": "2.3.0", - "resolved": "https://registry.npmjs.org/p-limit/-/p-limit-2.3.0.tgz", - "integrity": "sha512-//88mFWSJx8lxCzwdAABTJL2MyWB12+eIY7MDL2SqLmAkeKU9qxRvWuSyTjm3FUmpBEMuFfckAIqEaVGUDxb6w==", - "dev": true, - "requires": { - "p-try": "^2.0.0" - } - }, - "p-locate": { - "version": "4.1.0", - "resolved": "https://registry.npmjs.org/p-locate/-/p-locate-4.1.0.tgz", - "integrity": "sha512-R79ZZ/0wAxKGu3oYMlz8jy/kbhsNrS7SKZ7PxEHBgJ5+F2mtFW2fK2cOtBh1cHYkQsbzFV7I+EoRKe6Yt0oK7A==", - "dev": true, - "requires": { - "p-limit": "^2.2.0" - } - }, "resolve-from": { "version": "5.0.0", "resolved": "https://registry.npmjs.org/resolve-from/-/resolve-from-5.0.0.tgz", @@ -9833,10 +9963,16 @@ } } }, + "object-assign": { + "version": "4.1.1", + "resolved": "https://registry.npmjs.org/object-assign/-/object-assign-4.1.1.tgz", + "integrity": "sha1-IQmtx5ZYh8/AXLvUQsrIv7s2CGM=", + "dev": true + }, "object-inspect": { - "version": "1.11.0", - "resolved": "https://registry.npmjs.org/object-inspect/-/object-inspect-1.11.0.tgz", - "integrity": "sha512-jp7ikS6Sd3GxQfZJPyH3cjcbJF6GZPClgdV+EFygjFLQ5FmW/dRUnTd9PQ9k0JhoNDabWFbpF1yCdSWCC6gexg==", + "version": "1.11.1", + "resolved": "https://registry.npmjs.org/object-inspect/-/object-inspect-1.11.1.tgz", + "integrity": "sha512-If7BjFlpkzzBeV1cqgT3OSWT3azyoxDGajR+iGnFBfVV2EWyDyWaZZW2ERDjUaY2QM8i5jI3Sj7mhsM4DDAqWA==", "dev": true }, "once": { @@ -9857,32 +9993,6 @@ "mimic-fn": "^2.1.0" } }, - "onigasm": { - "version": "2.2.5", - "resolved": "https://registry.npmjs.org/onigasm/-/onigasm-2.2.5.tgz", - "integrity": "sha512-F+th54mPc0l1lp1ZcFMyL/jTs2Tlq4SqIHKIXGZOR/VkHkF9A7Fr5rRr5+ZG/lWeRsyrClLYRq7s/yFQ/XhWCA==", - "dev": true, - "requires": { - "lru-cache": "^5.1.1" - }, - "dependencies": { - "lru-cache": { - "version": "5.1.1", - "resolved": "https://registry.npmjs.org/lru-cache/-/lru-cache-5.1.1.tgz", - "integrity": "sha512-KpNARQA3Iwv+jTA0utUVVbrh+Jlrr1Fv0e56GGzAFOXN7dk/FviaDW8LHmK52DlcH4WP2n6gI8vN1aesBFgo9w==", - "dev": true, - "requires": { - "yallist": "^3.0.2" - } - }, - "yallist": { - "version": "3.1.1", - "resolved": "https://registry.npmjs.org/yallist/-/yallist-3.1.1.tgz", - "integrity": "sha512-a4UGQaWPH59mOXUYnAG2ewncQS4i4F43Tv3JoAM+s2VDAmS9NsK8GpDMLrCHPksFT7h3K6TOoUNn2pb7RoXx4g==", - "dev": true - } - } - }, "open": { "version": "7.4.2", "resolved": "https://registry.npmjs.org/open/-/open-7.4.2.tgz", @@ -9947,21 +10057,21 @@ "dev": true }, "p-limit": { - "version": "3.1.0", - "resolved": "https://registry.npmjs.org/p-limit/-/p-limit-3.1.0.tgz", - "integrity": "sha512-TYOanM3wGwNGsZN2cVTYPArw454xnXj5qmWF1bEoAc4+cU/ol7GVh7odevjp1FNHduHc3KZMcFduxU5Xc6uJRQ==", + "version": "2.3.0", + "resolved": "https://registry.npmjs.org/p-limit/-/p-limit-2.3.0.tgz", + "integrity": "sha512-//88mFWSJx8lxCzwdAABTJL2MyWB12+eIY7MDL2SqLmAkeKU9qxRvWuSyTjm3FUmpBEMuFfckAIqEaVGUDxb6w==", "dev": true, "requires": { - "yocto-queue": "^0.1.0" + "p-try": "^2.0.0" } }, "p-locate": { - "version": "5.0.0", - "resolved": "https://registry.npmjs.org/p-locate/-/p-locate-5.0.0.tgz", - "integrity": "sha512-LaNjtRWUBY++zB5nE/NwcaoMylSPk+S+ZHNB1TzdbMJMny6dynpAGt7X/tl/QYq3TIeE6nxHppbo2LGymrG5Pw==", + "version": "4.1.0", + "resolved": "https://registry.npmjs.org/p-locate/-/p-locate-4.1.0.tgz", + "integrity": "sha512-R79ZZ/0wAxKGu3oYMlz8jy/kbhsNrS7SKZ7PxEHBgJ5+F2mtFW2fK2cOtBh1cHYkQsbzFV7I+EoRKe6Yt0oK7A==", "dev": true, "requires": { - "p-limit": "^3.0.2" + "p-limit": "^2.2.0" } }, "p-map": { @@ -10041,12 +10151,6 @@ "requires": { "pump": "^3.0.0" } - }, - "lowercase-keys": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/lowercase-keys/-/lowercase-keys-2.0.0.tgz", - "integrity": "sha512-tqNXrS78oMOE73NMxK4EMLQsQowWf8jKooH9g7xPavRT706R6bkQJ6DY2Te7QukaZsulxa30wQ7bk0pm4XiHmA==", - "dev": true } } }, @@ -10091,6 +10195,14 @@ "p-cancelable": "^1.0.0", "to-readable-stream": "^1.0.0", "url-parse-lax": "^3.0.0" + }, + "dependencies": { + "lowercase-keys": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/lowercase-keys/-/lowercase-keys-1.0.1.tgz", + "integrity": "sha512-G2Lj61tXDnVFFOi8VZds+SoQjtQC3dgokKdDG2mTm1tx4m50NUHBOZSBwQQHyy0V12A0JTG4icfZQH+xPyh8VA==", + "dev": true + } } }, "json-buffer": { @@ -10108,12 +10220,6 @@ "json-buffer": "3.0.0" } }, - "lowercase-keys": { - "version": "1.0.1", - "resolved": "https://registry.npmjs.org/lowercase-keys/-/lowercase-keys-1.0.1.tgz", - "integrity": "sha512-G2Lj61tXDnVFFOi8VZds+SoQjtQC3dgokKdDG2mTm1tx4m50NUHBOZSBwQQHyy0V12A0JTG4icfZQH+xPyh8VA==", - "dev": true - }, "normalize-url": { "version": "4.5.1", "resolved": "https://registry.npmjs.org/normalize-url/-/normalize-url-4.5.1.tgz", @@ -10133,13 +10239,15 @@ "dev": true, "requires": { "lowercase-keys": "^1.0.0" + }, + "dependencies": { + "lowercase-keys": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/lowercase-keys/-/lowercase-keys-1.0.1.tgz", + "integrity": "sha512-G2Lj61tXDnVFFOi8VZds+SoQjtQC3dgokKdDG2mTm1tx4m50NUHBOZSBwQQHyy0V12A0JTG4icfZQH+xPyh8VA==", + "dev": true + } } - }, - "semver": { - "version": "6.3.0", - "resolved": "https://registry.npmjs.org/semver/-/semver-6.3.0.tgz", - "integrity": "sha512-b39TBaTSfV6yBrapU89p5fKekE2m/NwnDocOVruQFS1/veMgdzuPcnOM34M6CwxW8jH/lxEa5rBoDeUwu5HHTw==", - "dev": true } } }, @@ -10239,6 +10347,27 @@ "integrity": "sha512-lY1Q/PiJGC2zOv/z391WOTD+Z02bCgsFfvxoXXf6h7kv9o+WmsmzYqrAwY63sNgOxE4xEdq0WyUnXfKeBrSvYw==", "dev": true }, + "pify": { + "version": "2.3.0", + "resolved": "https://registry.npmjs.org/pify/-/pify-2.3.0.tgz", + "integrity": "sha1-7RQaasBDqEnqWISY59yosVMw6Qw=", + "dev": true + }, + "pinkie": { + "version": "2.0.4", + "resolved": "https://registry.npmjs.org/pinkie/-/pinkie-2.0.4.tgz", + "integrity": "sha1-clVrgM+g1IqXToDnckjoDtT3+HA=", + "dev": true + }, + "pinkie-promise": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/pinkie-promise/-/pinkie-promise-2.0.1.tgz", + "integrity": "sha1-ITXW36ejWMBprJsXh3YogihFD/o=", + "dev": true, + "requires": { + "pinkie": "^2.0.0" + } + }, "pkg-dir": { "version": "4.2.0", "resolved": "https://registry.npmjs.org/pkg-dir/-/pkg-dir-4.2.0.tgz", @@ -10246,45 +10375,6 @@ "dev": true, "requires": { "find-up": "^4.0.0" - }, - "dependencies": { - "find-up": { - "version": "4.1.0", - "resolved": "https://registry.npmjs.org/find-up/-/find-up-4.1.0.tgz", - "integrity": "sha512-PpOwAdQ/YlXQ2vj8a3h8IipDuYRi3wceVQQGYWxNINccq40Anw7BlsEXCMbt1Zt+OLA6Fq9suIpIWD0OsnISlw==", - "dev": true, - "requires": { - "locate-path": "^5.0.0", - "path-exists": "^4.0.0" - } - }, - "locate-path": { - "version": "5.0.0", - "resolved": "https://registry.npmjs.org/locate-path/-/locate-path-5.0.0.tgz", - "integrity": "sha512-t7hw9pI+WvuwNJXwk5zVHpyhIqzg2qTlklJOf0mVxGSbe3Fp2VieZcduNYjaLDoy6p9uGpQEGWG87WpMKlNq8g==", - "dev": true, - "requires": { - "p-locate": "^4.1.0" - } - }, - "p-limit": { - "version": "2.3.0", - "resolved": "https://registry.npmjs.org/p-limit/-/p-limit-2.3.0.tgz", - "integrity": "sha512-//88mFWSJx8lxCzwdAABTJL2MyWB12+eIY7MDL2SqLmAkeKU9qxRvWuSyTjm3FUmpBEMuFfckAIqEaVGUDxb6w==", - "dev": true, - "requires": { - "p-try": "^2.0.0" - } - }, - "p-locate": { - "version": "4.1.0", - "resolved": "https://registry.npmjs.org/p-locate/-/p-locate-4.1.0.tgz", - "integrity": "sha512-R79ZZ/0wAxKGu3oYMlz8jy/kbhsNrS7SKZ7PxEHBgJ5+F2mtFW2fK2cOtBh1cHYkQsbzFV7I+EoRKe6Yt0oK7A==", - "dev": true, - "requires": { - "p-limit": "^2.2.0" - } - } } }, "prelude-ls": { @@ -10346,9 +10436,9 @@ } }, "qs": { - "version": "6.10.1", - "resolved": "https://registry.npmjs.org/qs/-/qs-6.10.1.tgz", - "integrity": "sha512-M528Hph6wsSVOBiYUnGf+K/7w0hNshs/duGsNXPUCLH5XAqjEtiPGwNONLV0tBH8NoGb0mvD5JubnUTrujKDTg==", + "version": "6.10.2", + "resolved": "https://registry.npmjs.org/qs/-/qs-6.10.2.tgz", + "integrity": "sha512-mSIdjzqznWgfd4pMii7sHtaYF8rx8861hBO80SraY5GT0XQibWZWJSid0avzHGkDIZLImux2S5mXO0Hfct2QCw==", "dev": true, "requires": { "side-channel": "^1.0.4" @@ -10508,6 +10598,37 @@ "uuid": "8.3.2", "yaml": "1.10.2", "yargs-parser": "20.2.9" + }, + "dependencies": { + "array-union": { + "version": "2.1.0", + "resolved": "https://registry.npmjs.org/array-union/-/array-union-2.1.0.tgz", + "integrity": "sha512-HGyxoOTYUyCM6stUe6EJgnd4EoewAI7zMdfqO+kGjnlZmBDz/cR5pf8r/cR4Wq60sL/p0IkcjUEEPwS3GFrIyw==", + "dev": true + }, + "globby": { + "version": "11.0.4", + "resolved": "https://registry.npmjs.org/globby/-/globby-11.0.4.tgz", + "integrity": "sha512-9O4MVG9ioZJ08ffbcyVYyLOJLk5JQ688pJ4eMGLpdWLHq/Wr1D9BlriLQyL0E+jbkuePVZXYFj47QM/v093wHg==", + "dev": true, + "requires": { + "array-union": "^2.1.0", + "dir-glob": "^3.0.1", + "fast-glob": "^3.1.1", + "ignore": "^5.1.4", + "merge2": "^1.3.0", + "slash": "^3.0.0" + } + }, + "semver": { + "version": "7.3.5", + "resolved": "https://registry.npmjs.org/semver/-/semver-7.3.5.tgz", + "integrity": "sha512-PoeGJYh8HK4BTO/a9Tf6ZG3veo/A7ZVsYrSA6J8ny9nb3B1VrpkuN+z9OE5wfE5p6H4LchYZsegiQgbJD94ZFQ==", + "dev": true, + "requires": { + "lru-cache": "^6.0.0" + } + } } }, "release-zalgo": { @@ -10630,13 +10751,10 @@ "dev": true }, "semver": { - "version": "7.3.5", - "resolved": "https://registry.npmjs.org/semver/-/semver-7.3.5.tgz", - "integrity": "sha512-PoeGJYh8HK4BTO/a9Tf6ZG3veo/A7ZVsYrSA6J8ny9nb3B1VrpkuN+z9OE5wfE5p6H4LchYZsegiQgbJD94ZFQ==", - "dev": true, - "requires": { - "lru-cache": "^6.0.0" - } + "version": "6.3.0", + "resolved": "https://registry.npmjs.org/semver/-/semver-6.3.0.tgz", + "integrity": "sha512-b39TBaTSfV6yBrapU89p5fKekE2m/NwnDocOVruQFS1/veMgdzuPcnOM34M6CwxW8jH/lxEa5rBoDeUwu5HHTw==", + "dev": true }, "semver-diff": { "version": "3.1.1", @@ -10645,14 +10763,6 @@ "dev": true, "requires": { "semver": "^6.3.0" - }, - "dependencies": { - "semver": { - "version": "6.3.0", - "resolved": "https://registry.npmjs.org/semver/-/semver-6.3.0.tgz", - "integrity": "sha512-b39TBaTSfV6yBrapU89p5fKekE2m/NwnDocOVruQFS1/veMgdzuPcnOM34M6CwxW8jH/lxEa5rBoDeUwu5HHTw==", - "dev": true - } } }, "serialize-javascript": { @@ -10697,13 +10807,13 @@ } }, "shiki": { - "version": "0.9.12", - "resolved": "https://registry.npmjs.org/shiki/-/shiki-0.9.12.tgz", - "integrity": "sha512-VXcROdldv0/Qu0w2XvzU4IrvTeBNs/Kj/FCmtcEXGz7Tic/veQzliJj6tEiAgoKianhQstpYmbPDStHU5Opqcw==", + "version": "0.9.15", + "resolved": "https://registry.npmjs.org/shiki/-/shiki-0.9.15.tgz", + "integrity": "sha512-/Y0z9IzhJ8nD9nbceORCqu6NgT9X6I8Fk8c3SICHI5NbZRLdZYFaB233gwct9sU0vvSypyaL/qaKvzyQGJBZSw==", "dev": true, "requires": { "jsonc-parser": "^3.0.0", - "onigasm": "^2.2.5", + "vscode-oniguruma": "^1.6.1", "vscode-textmate": "5.2.0" } }, @@ -10858,6 +10968,15 @@ "integrity": "sha1-PFMZQukIwml8DsNEhYwobHygpgo=", "dev": true }, + "strip-outer": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/strip-outer/-/strip-outer-1.0.1.tgz", + "integrity": "sha512-k55yxKHwaXnpYGsOzg4Vl8+tDrWylxDEpknGjhTiZB8dFRU5rTo9CAzeycivxV3s+zlTKwrs6WxMxR95n26kwg==", + "dev": true, + "requires": { + "escape-string-regexp": "^1.0.2" + } + }, "supports-color": { "version": "7.2.0", "resolved": "https://registry.npmjs.org/supports-color/-/supports-color-7.2.0.tgz", @@ -10926,6 +11045,15 @@ "integrity": "sha1-gYT9NH2snNwYWZLzpmIuFLnZq2o=", "dev": true }, + "trim-repeated": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/trim-repeated/-/trim-repeated-1.0.0.tgz", + "integrity": "sha1-42RqLqTokTEr9+rObPsFOAvAHCE=", + "dev": true, + "requires": { + "escape-string-regexp": "^1.0.2" + } + }, "ts-node": { "version": "10.4.0", "resolved": "https://registry.npmjs.org/ts-node/-/ts-node-10.4.0.tgz", @@ -11020,35 +11148,12 @@ "shiki": "^0.9.12" } }, - "typedoc-github-wiki-theme": { - "version": "0.6.0", - "resolved": "https://registry.npmjs.org/typedoc-github-wiki-theme/-/typedoc-github-wiki-theme-0.6.0.tgz", - "integrity": "sha512-uHhR7PwAZ4JgO0KzlLocWSvMqKsSZ/wxUQYGKskIepzsotPAQcAWnSSnGi6gdkSw8UAfIIppdD7H1AmI39962w==", - "dev": true, - "requires": {} - }, - "typedoc-plugin-markdown": { - "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.5.2", - "resolved": "https://registry.npmjs.org/typescript/-/typescript-4.5.2.tgz", - "integrity": "sha512-5BlMof9H1yGt0P8/WF+wPNw6GfctgGjXp5hkblpyT+8rkASSmkUKMXrxR0Xg8ThVCi/JnHQiKXeBaEwCeQwMFw==", + "version": "4.5.3", + "resolved": "https://registry.npmjs.org/typescript/-/typescript-4.5.3.tgz", + "integrity": "sha512-eVYaEHALSt+s9LbvgEv4Ef+Tdq7hBiIZgii12xXJnukryt3pMgJf6aKhoCZ3FWQsu6sydEnkg11fYXLzhLBjeQ==", "dev": true }, - "uglify-js": { - "version": "3.14.3", - "resolved": "https://registry.npmjs.org/uglify-js/-/uglify-js-3.14.3.tgz", - "integrity": "sha512-mic3aOdiq01DuSVx0TseaEzMIVqebMZ0Z3vaeDhFEh9bsc24hV1TFvN74reA2vs08D0ZWfNjAcJ3UbVLaBss+g==", - "dev": true, - "optional": true - }, "unique-string": { "version": "2.0.0", "resolved": "https://registry.npmjs.org/unique-string/-/unique-string-2.0.0.tgz", @@ -11064,6 +11169,12 @@ "integrity": "sha512-isyNax3wXoKaulPDZWHQqbmIx1k2tb9fb3GGDBRxCscfYV2Ch7WxPArBsFEG8s/safwXTT7H4QGhaIkTp9447w==", "dev": true }, + "universalify": { + "version": "0.1.2", + "resolved": "https://registry.npmjs.org/universalify/-/universalify-0.1.2.tgz", + "integrity": "sha512-rBJeI5CXAlmy1pV+617WB9J63U6XcazHHF2f2dbJix4XzpUF0RS3Zbj0FGIOCAva5P/d/GBOYaACQ1w+0azUkg==", + "dev": true + }, "update-notifier": { "version": "5.1.0", "resolved": "https://registry.npmjs.org/update-notifier/-/update-notifier-5.1.0.tgz", @@ -11100,6 +11211,15 @@ "requires": { "ci-info": "^2.0.0" } + }, + "semver": { + "version": "7.3.5", + "resolved": "https://registry.npmjs.org/semver/-/semver-7.3.5.tgz", + "integrity": "sha512-PoeGJYh8HK4BTO/a9Tf6ZG3veo/A7ZVsYrSA6J8ny9nb3B1VrpkuN+z9OE5wfE5p6H4LchYZsegiQgbJD94ZFQ==", + "dev": true, + "requires": { + "lru-cache": "^6.0.0" + } } } }, @@ -11145,6 +11265,12 @@ "integrity": "sha512-l8lCEmLcLYZh4nbunNZvQCJc5pv7+RCwa8q/LdUx8u7lsWvPDKmpodJAJNwkAhJC//dFY48KuIEmjtd4RViDrA==", "dev": true }, + "vscode-oniguruma": { + "version": "1.6.1", + "resolved": "https://registry.npmjs.org/vscode-oniguruma/-/vscode-oniguruma-1.6.1.tgz", + "integrity": "sha512-vc4WhSIaVpgJ0jJIejjYxPvURJavX6QG41vu0mGhqywMkQqulezEqEQ3cO3gc8GvcOpX6ycmKGqRoROEMBNXTQ==", + "dev": true + }, "vscode-textmate": { "version": "5.2.0", "resolved": "https://registry.npmjs.org/vscode-textmate/-/vscode-textmate-5.2.0.tgz", @@ -11249,12 +11375,6 @@ "integrity": "sha512-Hz/mrNwitNRh/HUAtM/VT/5VH+ygD6DV7mYKZAtHOrbs8U7lvPS6xf7EJKMF0uW1KJCl0H701g3ZGus+muE5vQ==", "dev": true }, - "wordwrap": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/wordwrap/-/wordwrap-1.0.0.tgz", - "integrity": "sha1-J1hIEIkUVqQXHI0CJkQa3pDLyus=", - "dev": true - }, "workerpool": { "version": "6.1.5", "resolved": "https://registry.npmjs.org/workerpool/-/workerpool-6.1.5.tgz", @@ -11314,18 +11434,26 @@ "dev": true }, "yargs": { - "version": "17.2.1", - "resolved": "https://registry.npmjs.org/yargs/-/yargs-17.2.1.tgz", - "integrity": "sha512-XfR8du6ua4K6uLGm5S6fA+FIJom/MdJcFNVY8geLlp2v8GYbOXD4EB1tPNZsRn4vBzKGMgb5DRZMeWuFc2GO8Q==", + "version": "17.3.0", + "resolved": "https://registry.npmjs.org/yargs/-/yargs-17.3.0.tgz", + "integrity": "sha512-GQl1pWyDoGptFPJx9b9L6kmR33TGusZvXIZUT+BOz9f7X2L94oeAskFYLEg/FkhV06zZPBYLvLZRWeYId29lew==", "dev": true, "requires": { "cliui": "^7.0.2", "escalade": "^3.1.1", "get-caller-file": "^2.0.5", "require-directory": "^2.1.1", - "string-width": "^4.2.0", + "string-width": "^4.2.3", "y18n": "^5.0.5", - "yargs-parser": "^20.2.2" + "yargs-parser": "^21.0.0" + }, + "dependencies": { + "yargs-parser": { + "version": "21.0.0", + "resolved": "https://registry.npmjs.org/yargs-parser/-/yargs-parser-21.0.0.tgz", + "integrity": "sha512-z9kApYUOCwoeZ78rfRYYWdiU/iNL6mwwYlkkZfJoyMR1xps+NEBX5X7XmRpxkZHhXJ6+Ey00IwKxBBSW9FIjyA==", + "dev": true + } } }, "yargs-parser": { diff --git a/package.json b/package.json index 51b55ef591f..1de08eb2fba 100644 --- a/package.json +++ b/package.json @@ -15,7 +15,9 @@ "build:tests-tools": "npm run build:client && npm run build:test-utils", "build:modules": "find ./packages -mindepth 1 -maxdepth 1 -type d ! -name 'client' ! -name 'test-utils' -exec npm run build -w {} \\;", "build": "tsc", - "build-all": "npm run build:client && npm run build:test-utils && npm run build:modules && npm run build" + "build-all": "npm run build:client && npm run build:test-utils && npm run build:modules && npm run build", + "documentation": "npm run documentation -ws --if-present", + "gh-pages": "gh-pages -d ./documentation -e ./documentation" }, "dependencies": { "@node-redis/client": "^1.0.0", @@ -24,8 +26,10 @@ }, "devDependencies": { "@tsconfig/node12": "^1.0.9", + "gh-pages": "^3.2.3", "release-it": "^14.11.8", - "typescript": "^4.5.2" + "typedoc": "^0.22.10", + "typescript": "^4.5.3" }, "repository": { "type": "git", diff --git a/packages/client/.gitignore b/packages/client/.gitignore deleted file mode 100644 index 2d7ddbc106e..00000000000 --- a/packages/client/.gitignore +++ /dev/null @@ -1 +0,0 @@ -documentation/ diff --git a/packages/client/package.json b/packages/client/package.json index d697d200bea..f871e6ad161 100644 --- a/packages/client/package.json +++ b/packages/client/package.json @@ -17,24 +17,22 @@ "yallist": "4.0.0" }, "devDependencies": { - "@istanbuljs/nyc-config-typescript": "^1.0.1", + "@istanbuljs/nyc-config-typescript": "^1.0.2", "@node-redis/test-utils": "*", - "@types/node": "^16.11.10", + "@types/node": "^16.11.12", "@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.3.0", + "@typescript-eslint/eslint-plugin": "^5.7.0", + "@typescript-eslint/parser": "^5.7.0", + "eslint": "^8.4.1", "nyc": "^15.1.0", "release-it": "^14.11.8", "sinon": "^12.0.1", "source-map-support": "^0.5.21", "ts-node": "^10.4.0", "typedoc": "^0.22.10", - "typedoc-github-wiki-theme": "^0.6.0", - "typedoc-plugin-markdown": "^3.11.7", - "typescript": "^4.5.2" + "typescript": "^4.5.3" }, "engines": { "node": ">=12" diff --git a/packages/client/tsconfig.json b/packages/client/tsconfig.json index 3280af594ef..b029ca71a4f 100644 --- a/packages/client/tsconfig.json +++ b/packages/client/tsconfig.json @@ -21,7 +21,6 @@ "./lib/ts-declarations", "./lib/test-utils.ts" ], - "theme": "./node_modules/typedoc-github-wiki-theme/dist", - "out": "documentation" + "out": "../../documentation/client" } } diff --git a/packages/json/package.json b/packages/json/package.json index 2db2c926248..7216d407714 100644 --- a/packages/json/package.json +++ b/packages/json/package.json @@ -6,19 +6,21 @@ "types": "./dist/index.d.ts", "scripts": { "test": "nyc -r text-summary -r lcov mocha -r source-map-support/register -r ts-node/register './lib/**/*.spec.ts'", - "build": "tsc" + "build": "tsc", + "documentation": "typedoc" }, "peerDependencies": { "@node-redis/client": "^1.0.0" }, "devDependencies": { - "@istanbuljs/nyc-config-typescript": "^1.0.1", + "@istanbuljs/nyc-config-typescript": "^1.0.2", "@node-redis/test-utils": "*", - "@types/node": "^16.11.10", + "@types/node": "^16.11.12", "nyc": "^15.1.0", "release-it": "^14.11.8", "source-map-support": "^0.5.21", "ts-node": "^10.4.0", - "typescript": "^4.5.2" + "typedoc": "^0.22.10", + "typescript": "^4.5.3" } } diff --git a/packages/json/tsconfig.json b/packages/json/tsconfig.json index 14fda1d8711..50dd47269b7 100644 --- a/packages/json/tsconfig.json +++ b/packages/json/tsconfig.json @@ -5,5 +5,16 @@ }, "include": [ "./lib/**/*.ts" - ] + ], + "typedocOptions": { + "entryPoints": [ + "./lib" + ], + "entryPointStrategy": "expand", + "exclude": [ + "./lib/test-utils.ts", + "./lib/**/*.spec.ts" + ], + "out": "../../documentation/json" + } } diff --git a/packages/search/package.json b/packages/search/package.json index e5730ab886e..dd7aece4c8e 100644 --- a/packages/search/package.json +++ b/packages/search/package.json @@ -6,19 +6,21 @@ "types": "./dist/index.d.ts", "scripts": { "test": "nyc -r text-summary -r lcov mocha -r source-map-support/register -r ts-node/register './lib/**/*.spec.ts'", - "build": "tsc" + "build": "tsc", + "documentation": "typedoc" }, "peerDependencies": { "@node-redis/client": "^1.0.0" }, "devDependencies": { - "@istanbuljs/nyc-config-typescript": "^1.0.1", + "@istanbuljs/nyc-config-typescript": "^1.0.2", "@node-redis/test-utils": "*", - "@types/node": "^16.11.10", + "@types/node": "^16.11.12", "nyc": "^15.1.0", "release-it": "^14.11.8", "source-map-support": "^0.5.21", "ts-node": "^10.4.0", - "typescript": "^4.5.2" + "typedoc": "^0.22.10", + "typescript": "^4.5.3" } } diff --git a/packages/search/tsconfig.json b/packages/search/tsconfig.json index 14fda1d8711..a2d0e485c1d 100644 --- a/packages/search/tsconfig.json +++ b/packages/search/tsconfig.json @@ -5,5 +5,16 @@ }, "include": [ "./lib/**/*.ts" - ] + ], + "typedocOptions": { + "entryPoints": [ + "./lib" + ], + "entryPointStrategy": "expand", + "exclude": [ + "./lib/test-utils.ts", + "./lib/**/*.spec.ts" + ], + "out": "../../documentation/search" + } } diff --git a/packages/test-utils/package.json b/packages/test-utils/package.json index ccc3cf38cf3..36caf2d10eb 100644 --- a/packages/test-utils/package.json +++ b/packages/test-utils/package.json @@ -10,16 +10,16 @@ "@node-redis/client": "^1.0.0" }, "devDependencies": { - "@istanbuljs/nyc-config-typescript": "^1.0.1", + "@istanbuljs/nyc-config-typescript": "^1.0.2", "@types/mocha": "^9.0.0", - "@types/node": "^16.11.10", + "@types/node": "^16.11.12", "@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" + "typescript": "^4.5.3", + "yargs": "^17.3.0" } } diff --git a/packages/time-series/package.json b/packages/time-series/package.json index e58fd79da59..fefb8891bef 100644 --- a/packages/time-series/package.json +++ b/packages/time-series/package.json @@ -6,19 +6,21 @@ "types": "./dist/index.d.ts", "scripts": { "test": "nyc -r text-summary -r lcov mocha -r source-map-support/register -r ts-node/register './lib/**/*.spec.ts'", - "build": "tsc" + "build": "tsc", + "documentation": "typedoc" }, "peerDependencies": { "@node-redis/client": "^1.0.0" }, "devDependencies": { - "@istanbuljs/nyc-config-typescript": "^1.0.1", + "@istanbuljs/nyc-config-typescript": "^1.0.2", "@node-redis/test-utils": "*", - "@types/node": "^16.11.7", + "@types/node": "^16.11.12", "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" + "typedoc": "^0.22.10", + "typescript": "^4.5.3" } } diff --git a/packages/time-series/tsconfig.json b/packages/time-series/tsconfig.json index 14fda1d8711..24f5d4bfa0d 100644 --- a/packages/time-series/tsconfig.json +++ b/packages/time-series/tsconfig.json @@ -5,5 +5,16 @@ }, "include": [ "./lib/**/*.ts" - ] + ], + "typedocOptions": { + "entryPoints": [ + "./lib" + ], + "entryPointStrategy": "expand", + "exclude": [ + "./lib/test-utils.ts", + "./lib/**/*.spec.ts" + ], + "out": "../../documentation/time-series" + } } From 8ae010d93050db73fc2ca87c20b1ec689f0ca993 Mon Sep 17 00:00:00 2001 From: leibale Date: Mon, 13 Dec 2021 14:26:28 -0500 Subject: [PATCH 1045/1748] fix documentation workflow --- .github/workflows/documentation.yml | 2 ++ 1 file changed, 2 insertions(+) diff --git a/.github/workflows/documentation.yml b/.github/workflows/documentation.yml index e07848a6433..50652a250bf 100644 --- a/.github/workflows/documentation.yml +++ b/.github/workflows/documentation.yml @@ -17,6 +17,8 @@ jobs: uses: actions/setup-node@v2.3.0 - name: Install Packages run: npm ci + - name: Build tests tools + run: npm run build:tests-tools - name: Generate Documentation run: npm run documentation - name: Upload From 16b45d13d471e59787854551a0d0c3367b438bab Mon Sep 17 00:00:00 2001 From: leibale Date: Mon, 13 Dec 2021 14:33:11 -0500 Subject: [PATCH 1046/1748] fix documentation workflow --- .github/workflows/documentation.yml | 7 ++++++- 1 file changed, 6 insertions(+), 1 deletion(-) diff --git a/.github/workflows/documentation.yml b/.github/workflows/documentation.yml index 50652a250bf..7b3d42bf9f4 100644 --- a/.github/workflows/documentation.yml +++ b/.github/workflows/documentation.yml @@ -22,4 +22,9 @@ jobs: - name: Generate Documentation run: npm run documentation - name: Upload - run: npm run gh-pages + run: | + git remote set-url origin https://git:${GITHUB_TOKEN}@github.com/${GITHUB_REPOSITORY}.git + npm run gh-pages + env: + GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }} + From 380a436654717aa6a0c5bbb47903c70c0226c897 Mon Sep 17 00:00:00 2001 From: leibale Date: Mon, 13 Dec 2021 14:38:15 -0500 Subject: [PATCH 1047/1748] fix documentation workflow --- package.json | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/package.json b/package.json index 1de08eb2fba..c88c030cf23 100644 --- a/package.json +++ b/package.json @@ -17,7 +17,7 @@ "build": "tsc", "build-all": "npm run build:client && npm run build:test-utils && npm run build:modules && npm run build", "documentation": "npm run documentation -ws --if-present", - "gh-pages": "gh-pages -d ./documentation -e ./documentation" + "gh-pages": "gh-pages -d ./documentation -e ./documentation -u 'documentation-bot '" }, "dependencies": { "@node-redis/client": "^1.0.0", From 90ca5cb3ee82aa7421cc645d5dd016b78ba07d06 Mon Sep 17 00:00:00 2001 From: leibale Date: Mon, 13 Dec 2021 15:33:11 -0500 Subject: [PATCH 1048/1748] update CHANGELOG --- packages/client/CHANGELOG.md | 17 +++++++++++++++++ 1 file changed, 17 insertions(+) diff --git a/packages/client/CHANGELOG.md b/packages/client/CHANGELOG.md index 39ea947b064..88e3840f20d 100644 --- a/packages/client/CHANGELOG.md +++ b/packages/client/CHANGELOG.md @@ -1,5 +1,22 @@ # Changelog +## v4.0.1 - 13 Dec, 2021 + +### Fixes + +- Fix `NOAUTH` error when using authentication & database (#1681) +- Allow to `.quit()` in PubSub mode (#1766) +- Add an option to configurate `name` on a client (#1758) +- Lowercase commands (`client.hset`) in `legacyMode` +- Fix PubSub resubscribe (#1764) +- Fix `RedisSocketOptions` type (#1741) + +### Enhancements + +- Add support for `number`s and `Buffer`s in `HSET` (#1738 #1739) +- Export `RedisClientType`, `RedisClusterType` and some more types (#1673) +- First release of `@node-redis/time-series` + ## 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. From 3bfa9298c4eb9b9a8a2477d60415d42245c4defc Mon Sep 17 00:00:00 2001 From: leibale Date: Mon, 13 Dec 2021 15:34:46 -0500 Subject: [PATCH 1049/1748] Release client@1.0.1 --- packages/client/package.json | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/packages/client/package.json b/packages/client/package.json index f871e6ad161..8916ec1a57d 100644 --- a/packages/client/package.json +++ b/packages/client/package.json @@ -1,6 +1,6 @@ { "name": "@node-redis/client", - "version": "1.0.0", + "version": "1.0.1", "license": "MIT", "main": "./dist/index.js", "types": "./dist/index.d.ts", From 2449ad419731e8f1f2f97effb6bf88ac3400c677 Mon Sep 17 00:00:00 2001 From: leibale Date: Mon, 13 Dec 2021 15:37:31 -0500 Subject: [PATCH 1050/1748] update tsconfig.json --- packages/json/tsconfig.json | 4 ++++ packages/search/tsconfig.json | 4 ++++ packages/time-series/tsconfig.json | 4 ++++ 3 files changed, 12 insertions(+) diff --git a/packages/json/tsconfig.json b/packages/json/tsconfig.json index 50dd47269b7..bffc01081d7 100644 --- a/packages/json/tsconfig.json +++ b/packages/json/tsconfig.json @@ -6,6 +6,10 @@ "include": [ "./lib/**/*.ts" ], + "exclude": [ + "./lib/test-utils.ts", + "./lib/**/*.spec.ts" + ], "typedocOptions": { "entryPoints": [ "./lib" diff --git a/packages/search/tsconfig.json b/packages/search/tsconfig.json index a2d0e485c1d..ecbbb531ef1 100644 --- a/packages/search/tsconfig.json +++ b/packages/search/tsconfig.json @@ -6,6 +6,10 @@ "include": [ "./lib/**/*.ts" ], + "exclude": [ + "./lib/test-utils.ts", + "./lib/**/*.spec.ts" + ], "typedocOptions": { "entryPoints": [ "./lib" diff --git a/packages/time-series/tsconfig.json b/packages/time-series/tsconfig.json index 24f5d4bfa0d..854fb728d3d 100644 --- a/packages/time-series/tsconfig.json +++ b/packages/time-series/tsconfig.json @@ -6,6 +6,10 @@ "include": [ "./lib/**/*.ts" ], + "exclude": [ + "./lib/test-utils.ts", + "./lib/**/*.spec.ts" + ], "typedocOptions": { "entryPoints": [ "./lib" From 32bdec2f3bf04c6b47ae92ce85457e1004b95b35 Mon Sep 17 00:00:00 2001 From: leibale Date: Mon, 13 Dec 2021 15:39:37 -0500 Subject: [PATCH 1051/1748] Release json@1.0.1 --- packages/json/package.json | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/packages/json/package.json b/packages/json/package.json index 7216d407714..57e5a2357c5 100644 --- a/packages/json/package.json +++ b/packages/json/package.json @@ -1,6 +1,6 @@ { "name": "@node-redis/json", - "version": "1.0.0-rc.0", + "version": "1.0.1", "license": "MIT", "main": "./dist/index.js", "types": "./dist/index.d.ts", From 1069ef3451254dc9f335c54cdfa9fed96aaafd49 Mon Sep 17 00:00:00 2001 From: leibale Date: Mon, 13 Dec 2021 15:40:13 -0500 Subject: [PATCH 1052/1748] Release search@1.0.1 --- packages/search/package.json | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/packages/search/package.json b/packages/search/package.json index dd7aece4c8e..bca7619b845 100644 --- a/packages/search/package.json +++ b/packages/search/package.json @@ -1,6 +1,6 @@ { "name": "@node-redis/search", - "version": "1.0.0", + "version": "1.0.1", "license": "MIT", "main": "./dist/index.js", "types": "./dist/index.d.ts", From 944033ae5edfb0e4bb4903ba9cb4a6f1cf1ab71e Mon Sep 17 00:00:00 2001 From: leibale Date: Mon, 13 Dec 2021 15:41:23 -0500 Subject: [PATCH 1053/1748] fix time-series .release-it.json --- packages/time-series/.release-it.json | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/packages/time-series/.release-it.json b/packages/time-series/.release-it.json index 72cb1016ef4..b5a7c08d24f 100644 --- a/packages/time-series/.release-it.json +++ b/packages/time-series/.release-it.json @@ -1,6 +1,6 @@ { "git": { - "tagName": "search@${version}", + "tagName": "time-series@${version}", "commitMessage": "Release ${tagName}", "tagAnnotation": "Release ${tagName}" }, From 578725d895deda4b6bd141dd9770a6992e4c8bd1 Mon Sep 17 00:00:00 2001 From: leibale Date: Mon, 13 Dec 2021 15:45:25 -0500 Subject: [PATCH 1054/1748] add @node-redis/time-series to README --- README.md | 13 +++++++------ 1 file changed, 7 insertions(+), 6 deletions(-) diff --git a/README.md b/README.md index a0c98a07d8b..837e914e6df 100644 --- a/README.md +++ b/README.md @@ -307,12 +307,13 @@ Node Redis is supported with the following versions of Redis: ## Packages -| Name | Description | -|-----------------------------------------|-----------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------| -| [redis](./) | [![Downloads](https://img.shields.io/npm/dm/redis.svg)](https://www.npmjs.com/package/redis) [![Version](https://img.shields.io/npm/v/redis.svg)](https://www.npmjs.com/package/redis) | -| [@node-redis/client](./packages/client) | [![Downloads](https://img.shields.io/npm/dm/@node-redis/client.svg)](https://www.npmjs.com/package/@node-redis/client) [![Version](https://img.shields.io/npm/v/@node-redis/client.svg)](https://www.npmjs.com/package/@node-redis/client) [![Docs](https://img.shields.io/badge/-documentation-dc382c)](https://redis.js.org/documentation/client/) | -| [@node-redis/json](./packages/json) | [![Downloads](https://img.shields.io/npm/dm/@node-redis/json.svg)](https://www.npmjs.com/package/@node-redis/json) [![Version](https://img.shields.io/npm/v/@node-redis/json.svg)](https://www.npmjs.com/package/@node-redis/json) [![Docs](https://img.shields.io/badge/-documentation-dc382c)](https://redis.js.org/documentation/json/) [Redis JSON](https://oss.redis.com/redisjson/) commands | -| [@node-redis/search](./packages/search) | [![Downloads](https://img.shields.io/npm/dm/@node-redis/search.svg)](https://www.npmjs.com/package/@node-redis/search) [![Version](https://img.shields.io/npm/v/@node-redis/search.svg)](https://www.npmjs.com/package/@node-redis/search) [![Docs](https://img.shields.io/badge/-documentation-dc382c)](https://redis.js.org/documentation/search/) [Redis Search](https://oss.redis.com/redisearch/) commands | +| ----------------------------------------- | ----------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- | +|---------------------------------------------------|----------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------| +| [redis](./) | [![Downloads](https://img.shields.io/npm/dm/redis.svg)](https://www.npmjs.com/package/redis) [![Version](https://img.shields.io/npm/v/redis.svg)](https://www.npmjs.com/package/redis) | +| [@node-redis/client](./packages/client) | [![Downloads](https://img.shields.io/npm/dm/@node-redis/client.svg)](https://www.npmjs.com/package/@node-redis/client) [![Version](https://img.shields.io/npm/v/@node-redis/client.svg)](https://www.npmjs.com/package/@node-redis/client) [![Docs](https://img.shields.io/badge/-documentation-dc382c)](https://redis.js.org/documentation/client/) | +| [@node-redis/json](./packages/json) | [![Downloads](https://img.shields.io/npm/dm/@node-redis/json.svg)](https://www.npmjs.com/package/@node-redis/json) [![Version](https://img.shields.io/npm/v/@node-redis/json.svg)](https://www.npmjs.com/package/@node-redis/json) [![Docs](https://img.shields.io/badge/-documentation-dc382c)](https://redis.js.org/documentation/json/) [Redis JSON](https://oss.redis.com/redisjson/) commands | +| [@node-redis/search](./packages/search) | [![Downloads](https://img.shields.io/npm/dm/@node-redis/search.svg)](https://www.npmjs.com/package/@node-redis/search) [![Version](https://img.shields.io/npm/v/@node-redis/search.svg)](https://www.npmjs.com/package/@node-redis/search) [![Docs](https://img.shields.io/badge/-documentation-dc382c)](https://redis.js.org/documentation/search/) [Redis Search](https://oss.redis.com/redisearch/) commands | +| [@node-redis/time-series](./packages/time-series) | [![Downloads](https://img.shields.io/npm/dm/@node-redis/time-series.svg)](https://www.npmjs.com/package/@node-redis/time-series) [![Version](https://img.shields.io/npm/v/@node-redis/time-series.svg)](https://www.npmjs.com/package/@node-redis/time-series) [![Docs](https://img.shields.io/badge/-documentation-dc382c)](https://redis.js.org/documentation/time-series/) [Redis Time-Series](https://oss.redis.com/redistimeseries/) commands | ## Contributing From 3b33aba2c084efeed2d15e3ff41da34119e84307 Mon Sep 17 00:00:00 2001 From: leibale Date: Mon, 13 Dec 2021 15:46:44 -0500 Subject: [PATCH 1055/1748] add time-series to all in one --- index.ts | 5 ++++- package.json | 7 ++++--- 2 files changed, 8 insertions(+), 4 deletions(-) diff --git a/index.ts b/index.ts index cca5764884d..2875c6fd848 100644 --- a/index.ts +++ b/index.ts @@ -2,14 +2,17 @@ import { createClient as _createClient, createCluster as _createCluster, RedisCl import { RedisScripts } from '@node-redis/client/dist/lib/commands'; import RedisJSON from '@node-redis/json'; import RediSearch from '@node-redis/search'; +import RedisTimeSeries from '@node-redis/time-series'; export * from '@node-redis/client'; export * from '@node-redis/json'; export * from '@node-redis/search'; +export * from '@node-redis/time-series'; const modules = { json: RedisJSON, - ft: RediSearch + ft: RediSearch, + ts: RedisTimeSeries }; export function createClient>( diff --git a/package.json b/package.json index c88c030cf23..3492d3e7f7b 100644 --- a/package.json +++ b/package.json @@ -20,9 +20,10 @@ "gh-pages": "gh-pages -d ./documentation -e ./documentation -u 'documentation-bot '" }, "dependencies": { - "@node-redis/client": "^1.0.0", - "@node-redis/json": "^1.0.0", - "@node-redis/search": "^1.0.0" + "@node-redis/client": "^1.0.1", + "@node-redis/json": "^1.0.1", + "@node-redis/search": "^1.0.1", + "@node-redis/time-series": "^1.0.0" }, "devDependencies": { "@tsconfig/node12": "^1.0.9", From 5da5b0af67d0d4f3822b4fbe181a07cf824f1efd Mon Sep 17 00:00:00 2001 From: leibale Date: Mon, 13 Dec 2021 15:47:12 -0500 Subject: [PATCH 1056/1748] Release redis@4.0.1 --- package-lock.json | 4 ++-- package.json | 2 +- 2 files changed, 3 insertions(+), 3 deletions(-) diff --git a/package-lock.json b/package-lock.json index c133c867cef..e8de78e53c5 100644 --- a/package-lock.json +++ b/package-lock.json @@ -1,12 +1,12 @@ { "name": "redis", - "version": "4.0.0", + "version": "4.0.1", "lockfileVersion": 2, "requires": true, "packages": { "": { "name": "redis", - "version": "4.0.0", + "version": "4.0.1", "license": "MIT", "workspaces": [ "./packages/*" diff --git a/package.json b/package.json index 3492d3e7f7b..52057bda0e4 100644 --- a/package.json +++ b/package.json @@ -1,7 +1,7 @@ { "name": "redis", "description": "A modern, high performance Redis client", - "version": "4.0.0", + "version": "4.0.1", "license": "MIT", "main": "./dist/index.js", "types": "./dist/index.d.ts", From 6c9b406591c74aaed5d470ac83a94f6ab555c521 Mon Sep 17 00:00:00 2001 From: leibale Date: Mon, 13 Dec 2021 15:49:31 -0500 Subject: [PATCH 1057/1748] fix time-series README --- packages/time-series/README.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/packages/time-series/README.md b/packages/time-series/README.md index 856a75fbb5a..3242afb5532 100644 --- a/packages/time-series/README.md +++ b/packages/time-series/README.md @@ -1,2 +1,2 @@ -# @node-redis/search +# @node-redis/time-series The sources and docs for this package are in the main [node-redis](https://github.com/redis/node-redis) repo. From c0db4226e7e0fd80ccaef71f747172e92471bf48 Mon Sep 17 00:00:00 2001 From: leibale Date: Mon, 13 Dec 2021 15:56:42 -0500 Subject: [PATCH 1058/1748] remove unused import --- packages/search/lib/commands/index.ts | 5 ++--- 1 file changed, 2 insertions(+), 3 deletions(-) diff --git a/packages/search/lib/commands/index.ts b/packages/search/lib/commands/index.ts index f148adc08f5..b175a1baeed 100644 --- a/packages/search/lib/commands/index.ts +++ b/packages/search/lib/commands/index.ts @@ -30,7 +30,6 @@ import * as SYNUPDATE from './SYNUPDATE'; import * as TAGVALS from './TAGVALS'; import { RedisCommandArguments } from '@node-redis/client/dist/lib/commands'; import { pushOptionalVerdictArgument, pushVerdictArgument, TuplesObject } from '@node-redis/client/dist/lib/commands/generic-transformers'; -import internal = require('stream'); import { SearchOptions } from './SEARCH'; export default { @@ -67,7 +66,7 @@ export default { INFO, info: INFO, PROFILESEARCH, - profileSearch: PROFILESEARCH, + profileSearch: PROFILESEARCH, PROFILEAGGREGATE, profileAggregate: PROFILEAGGREGATE, SEARCH, @@ -381,7 +380,7 @@ export function pushSearchOptions( options.LIMIT.size.toString() ); } - + return args; } From 72072f6b1c654932dafe32921047223880ee6a9d Mon Sep 17 00:00:00 2001 From: leibale Date: Mon, 13 Dec 2021 15:58:51 -0500 Subject: [PATCH 1059/1748] fix packages table in README --- README.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/README.md b/README.md index 837e914e6df..40ee294e4a6 100644 --- a/README.md +++ b/README.md @@ -307,7 +307,7 @@ Node Redis is supported with the following versions of Redis: ## Packages -| ----------------------------------------- | ----------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- | +| Name | Description | |---------------------------------------------------|----------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------| | [redis](./) | [![Downloads](https://img.shields.io/npm/dm/redis.svg)](https://www.npmjs.com/package/redis) [![Version](https://img.shields.io/npm/v/redis.svg)](https://www.npmjs.com/package/redis) | | [@node-redis/client](./packages/client) | [![Downloads](https://img.shields.io/npm/dm/@node-redis/client.svg)](https://www.npmjs.com/package/@node-redis/client) [![Version](https://img.shields.io/npm/v/@node-redis/client.svg)](https://www.npmjs.com/package/@node-redis/client) [![Docs](https://img.shields.io/badge/-documentation-dc382c)](https://redis.js.org/documentation/client/) | From a1bed9a10f1cdba25b56649cf4e3d3c4c8b192db Mon Sep 17 00:00:00 2001 From: leibale Date: Wed, 15 Dec 2021 17:08:50 -0500 Subject: [PATCH 1060/1748] add buffer support to a bunch of commands --- package-lock.json | 282 ++++++++---------- packages/client/lib/client/commands-queue.ts | 12 +- packages/client/lib/client/index.ts | 2 +- packages/client/lib/cluster/cluster-slots.ts | 4 +- packages/client/lib/cluster/commands.ts | 47 ++- packages/client/lib/cluster/index.ts | 10 +- packages/client/lib/cluster/multi-command.ts | 10 +- packages/client/lib/commander.ts | 4 +- packages/client/lib/commands/APPEND.ts | 9 +- packages/client/lib/commands/BLMOVE.ts | 7 +- packages/client/lib/commands/BLMOVE_BUFFER.ts | 5 + packages/client/lib/commands/BLPOP.ts | 11 +- packages/client/lib/commands/BLPOP_BUFFER.ts | 17 ++ packages/client/lib/commands/BRPOP.ts | 14 +- packages/client/lib/commands/BRPOPLPUSH.ts | 10 +- .../client/lib/commands/BRPOPLPUSH_BUFFER.ts | 5 + packages/client/lib/commands/BRPOP_BUFFER.ts | 3 + packages/client/lib/commands/BZPOPMAX.ts | 9 +- packages/client/lib/commands/BZPOPMIN.ts | 2 +- packages/client/lib/commands/CONFIG_GET.ts | 2 +- packages/client/lib/commands/GET.ts | 4 +- packages/client/lib/commands/HGET.ts | 13 +- packages/client/lib/commands/HGETALL.ts | 4 +- .../client/lib/commands/HGETALL_BUFFER.ts | 5 + packages/client/lib/commands/HGET_BUFFER.ts | 5 + packages/client/lib/commands/HRANDFIELD.ts | 2 + .../client/lib/commands/HRANDFIELD_COUNT.ts | 2 +- .../commands/HRANDFIELD_COUNT_WITHVALUES.ts | 4 +- .../HRANDFIELD_COUNT_WITHVALUES_BUFFER.ts | 5 + packages/client/lib/commands/HSET.ts | 4 +- packages/client/lib/commands/PUBLISH.ts | 7 +- packages/client/lib/commands/SET.ts | 8 +- packages/client/lib/commands/SETEX.ts | 8 +- packages/client/lib/commands/XACK.ts | 8 +- packages/client/lib/commands/XADD.ts | 11 +- packages/client/lib/commands/XAUTOCLAIM.ts | 15 +- .../client/lib/commands/XAUTOCLAIM_BUFFER.ts | 17 ++ .../client/lib/commands/XAUTOCLAIM_JUSTID.ts | 3 +- .../lib/commands/XAUTOCLAIM_JUSTID_BUFFER.ts | 13 + packages/client/lib/commands/XCLAIM.ts | 13 +- packages/client/lib/commands/XCLAIM_BUFFER.ts | 5 + packages/client/lib/commands/XCLAIM_JUSTID.ts | 7 +- .../lib/commands/XCLAIM_JUSTID_BUFFER.ts | 5 + packages/client/lib/commands/XDEL.ts | 7 +- packages/client/lib/commands/XGROUP_CREATE.ts | 11 +- .../lib/commands/XGROUP_CREATECONSUMER.ts | 8 +- .../client/lib/commands/XGROUP_DELCONSUMER.ts | 8 +- .../client/lib/commands/XGROUP_DESTROY.ts | 7 +- packages/client/lib/commands/XGROUP_SETID.ts | 10 +- .../client/lib/commands/XINFO_CONSUMERS.ts | 7 +- packages/client/lib/commands/XINFO_GROUPS.ts | 4 +- packages/client/lib/commands/XINFO_STREAM.ts | 13 +- packages/client/lib/commands/XLEN.ts | 4 +- packages/client/lib/commands/XPENDING.ts | 31 +- .../client/lib/commands/XPENDING_RANGE.ts | 31 +- packages/client/lib/commands/XRANGE.ts | 11 +- packages/client/lib/commands/XRANGE_BUFFER.ts | 5 + packages/client/lib/commands/XREAD.ts | 17 +- packages/client/lib/commands/XREADGROUP.ts | 20 +- .../client/lib/commands/XREADGROUP_BUFFER.ts | 6 + packages/client/lib/commands/XREAD_BUFFER.ts | 5 + packages/client/lib/commands/XREVRANGE.ts | 6 +- .../client/lib/commands/XREVRANGE_BUFFER.ts | 5 + packages/client/lib/commands/XTRIM.ts | 9 +- packages/client/lib/commands/ZADD.ts | 7 +- .../client/lib/commands/ZDIFF_WITHSCORES.ts | 2 +- .../client/lib/commands/ZINTER_WITHSCORES.ts | 2 +- packages/client/lib/commands/ZPOPMAX.ts | 4 +- packages/client/lib/commands/ZPOPMAX_COUNT.ts | 2 +- packages/client/lib/commands/ZPOPMIN.ts | 4 +- packages/client/lib/commands/ZPOPMIN_COUNT.ts | 2 +- .../commands/ZRANDMEMBER_COUNT_WITHSCORES.ts | 2 +- .../lib/commands/ZRANGEBYSCORE_WITHSCORES.ts | 2 +- .../client/lib/commands/ZRANGE_WITHSCORES.ts | 2 +- packages/client/lib/commands/ZSCAN.ts | 4 +- .../client/lib/commands/ZUNION_WITHSCORES.ts | 2 +- .../lib/commands/generic-transformers.spec.ts | 167 ++++++++++- .../lib/commands/generic-transformers.ts | 90 ++++-- packages/client/lib/commands/index.ts | 6 +- .../lib/ts-declarations/cluster-key-slot.d.ts | 3 - packages/client/tsconfig.json | 4 - packages/json/tsconfig.json | 4 - packages/search/lib/commands/AGGREGATE.ts | 10 +- packages/search/lib/commands/SEARCH.ts | 4 +- packages/search/lib/commands/index.ts | 4 +- packages/search/tsconfig.json | 4 - packages/time-series/tsconfig.json | 4 - tsconfig.base.json | 3 - 88 files changed, 818 insertions(+), 378 deletions(-) create mode 100644 packages/client/lib/commands/BLMOVE_BUFFER.ts create mode 100644 packages/client/lib/commands/BLPOP_BUFFER.ts create mode 100644 packages/client/lib/commands/BRPOPLPUSH_BUFFER.ts create mode 100644 packages/client/lib/commands/BRPOP_BUFFER.ts create mode 100644 packages/client/lib/commands/HGETALL_BUFFER.ts create mode 100644 packages/client/lib/commands/HGET_BUFFER.ts create mode 100644 packages/client/lib/commands/HRANDFIELD_COUNT_WITHVALUES_BUFFER.ts create mode 100644 packages/client/lib/commands/XAUTOCLAIM_BUFFER.ts create mode 100644 packages/client/lib/commands/XAUTOCLAIM_JUSTID_BUFFER.ts create mode 100644 packages/client/lib/commands/XCLAIM_BUFFER.ts create mode 100644 packages/client/lib/commands/XCLAIM_JUSTID_BUFFER.ts create mode 100644 packages/client/lib/commands/XRANGE_BUFFER.ts create mode 100644 packages/client/lib/commands/XREADGROUP_BUFFER.ts create mode 100644 packages/client/lib/commands/XREAD_BUFFER.ts create mode 100644 packages/client/lib/commands/XREVRANGE_BUFFER.ts delete mode 100644 packages/client/lib/ts-declarations/cluster-key-slot.d.ts diff --git a/package-lock.json b/package-lock.json index e8de78e53c5..2b0acfb5622 100644 --- a/package-lock.json +++ b/package-lock.json @@ -12,9 +12,10 @@ "./packages/*" ], "dependencies": { - "@node-redis/client": "^1.0.0", - "@node-redis/json": "^1.0.0", - "@node-redis/search": "^1.0.0" + "@node-redis/client": "^1.0.1", + "@node-redis/json": "^1.0.1", + "@node-redis/search": "^1.0.1", + "@node-redis/time-series": "^1.0.0" }, "devDependencies": { "@tsconfig/node12": "^1.0.9", @@ -49,19 +50,19 @@ } }, "node_modules/@babel/core": { - "version": "7.16.0", - "resolved": "https://registry.npmjs.org/@babel/core/-/core-7.16.0.tgz", - "integrity": "sha512-mYZEvshBRHGsIAiyH5PzCFTCfbWfoYbO/jcSdXQSUQu1/pW0xDZAUP7KEc32heqWTAfAHhV9j1vH8Sav7l+JNQ==", + "version": "7.16.5", + "resolved": "https://registry.npmjs.org/@babel/core/-/core-7.16.5.tgz", + "integrity": "sha512-wUcenlLzuWMZ9Zt8S0KmFwGlH6QKRh3vsm/dhDA3CHkiTA45YuG1XkHRcNRl73EFPXDp/d5kVOU0/y7x2w6OaQ==", "dev": true, "dependencies": { "@babel/code-frame": "^7.16.0", - "@babel/generator": "^7.16.0", - "@babel/helper-compilation-targets": "^7.16.0", - "@babel/helper-module-transforms": "^7.16.0", - "@babel/helpers": "^7.16.0", - "@babel/parser": "^7.16.0", + "@babel/generator": "^7.16.5", + "@babel/helper-compilation-targets": "^7.16.3", + "@babel/helper-module-transforms": "^7.16.5", + "@babel/helpers": "^7.16.5", + "@babel/parser": "^7.16.5", "@babel/template": "^7.16.0", - "@babel/traverse": "^7.16.0", + "@babel/traverse": "^7.16.5", "@babel/types": "^7.16.0", "convert-source-map": "^1.7.0", "debug": "^4.1.0", @@ -79,9 +80,9 @@ } }, "node_modules/@babel/generator": { - "version": "7.16.0", - "resolved": "https://registry.npmjs.org/@babel/generator/-/generator-7.16.0.tgz", - "integrity": "sha512-RR8hUCfRQn9j9RPKEVXo9LiwoxLPYn6hNZlvUOR8tSnaxlD0p0+la00ZP9/SnRt6HchKr+X0fO2r8vrETiJGew==", + "version": "7.16.5", + "resolved": "https://registry.npmjs.org/@babel/generator/-/generator-7.16.5.tgz", + "integrity": "sha512-kIvCdjZqcdKqoDbVVdt5R99icaRtrtYhYK/xux5qiWCBmfdvEYMFZ68QCrpE5cbFM1JsuArUNs1ZkuKtTtUcZA==", "dev": true, "dependencies": { "@babel/types": "^7.16.0", @@ -110,6 +111,18 @@ "@babel/core": "^7.0.0" } }, + "node_modules/@babel/helper-environment-visitor": { + "version": "7.16.5", + "resolved": "https://registry.npmjs.org/@babel/helper-environment-visitor/-/helper-environment-visitor-7.16.5.tgz", + "integrity": "sha512-ODQyc5AnxmZWm/R2W7fzhamOk1ey8gSguo5SGvF0zcB3uUzRpTRmM/jmLSm9bDMyPlvbyJ+PwPEK0BWIoZ9wjg==", + "dev": true, + "dependencies": { + "@babel/types": "^7.16.0" + }, + "engines": { + "node": ">=6.9.0" + } + }, "node_modules/@babel/helper-function-name": { "version": "7.16.0", "resolved": "https://registry.npmjs.org/@babel/helper-function-name/-/helper-function-name-7.16.0.tgz", @@ -148,18 +161,6 @@ "node": ">=6.9.0" } }, - "node_modules/@babel/helper-member-expression-to-functions": { - "version": "7.16.0", - "resolved": "https://registry.npmjs.org/@babel/helper-member-expression-to-functions/-/helper-member-expression-to-functions-7.16.0.tgz", - "integrity": "sha512-bsjlBFPuWT6IWhl28EdrQ+gTvSvj5tqVP5Xeftp07SEuz5pLnsXZuDkDD3Rfcxy0IsHmbZ+7B2/9SHzxO0T+sQ==", - "dev": true, - "dependencies": { - "@babel/types": "^7.16.0" - }, - "engines": { - "node": ">=6.9.0" - } - }, "node_modules/@babel/helper-module-imports": { "version": "7.16.0", "resolved": "https://registry.npmjs.org/@babel/helper-module-imports/-/helper-module-imports-7.16.0.tgz", @@ -173,45 +174,18 @@ } }, "node_modules/@babel/helper-module-transforms": { - "version": "7.16.0", - "resolved": "https://registry.npmjs.org/@babel/helper-module-transforms/-/helper-module-transforms-7.16.0.tgz", - "integrity": "sha512-My4cr9ATcaBbmaEa8M0dZNA74cfI6gitvUAskgDtAFmAqyFKDSHQo5YstxPbN+lzHl2D9l/YOEFqb2mtUh4gfA==", + "version": "7.16.5", + "resolved": "https://registry.npmjs.org/@babel/helper-module-transforms/-/helper-module-transforms-7.16.5.tgz", + "integrity": "sha512-CkvMxgV4ZyyioElFwcuWnDCcNIeyqTkCm9BxXZi73RR1ozqlpboqsbGUNvRTflgZtFbbJ1v5Emvm+lkjMYY/LQ==", "dev": true, "dependencies": { + "@babel/helper-environment-visitor": "^7.16.5", "@babel/helper-module-imports": "^7.16.0", - "@babel/helper-replace-supers": "^7.16.0", "@babel/helper-simple-access": "^7.16.0", "@babel/helper-split-export-declaration": "^7.16.0", "@babel/helper-validator-identifier": "^7.15.7", "@babel/template": "^7.16.0", - "@babel/traverse": "^7.16.0", - "@babel/types": "^7.16.0" - }, - "engines": { - "node": ">=6.9.0" - } - }, - "node_modules/@babel/helper-optimise-call-expression": { - "version": "7.16.0", - "resolved": "https://registry.npmjs.org/@babel/helper-optimise-call-expression/-/helper-optimise-call-expression-7.16.0.tgz", - "integrity": "sha512-SuI467Gi2V8fkofm2JPnZzB/SUuXoJA5zXe/xzyPP2M04686RzFKFHPK6HDVN6JvWBIEW8tt9hPR7fXdn2Lgpw==", - "dev": true, - "dependencies": { - "@babel/types": "^7.16.0" - }, - "engines": { - "node": ">=6.9.0" - } - }, - "node_modules/@babel/helper-replace-supers": { - "version": "7.16.0", - "resolved": "https://registry.npmjs.org/@babel/helper-replace-supers/-/helper-replace-supers-7.16.0.tgz", - "integrity": "sha512-TQxuQfSCdoha7cpRNJvfaYxxxzmbxXw/+6cS7V02eeDYyhxderSoMVALvwupA54/pZcOTtVeJ0xccp1nGWladA==", - "dev": true, - "dependencies": { - "@babel/helper-member-expression-to-functions": "^7.16.0", - "@babel/helper-optimise-call-expression": "^7.16.0", - "@babel/traverse": "^7.16.0", + "@babel/traverse": "^7.16.5", "@babel/types": "^7.16.0" }, "engines": { @@ -261,13 +235,13 @@ } }, "node_modules/@babel/helpers": { - "version": "7.16.3", - "resolved": "https://registry.npmjs.org/@babel/helpers/-/helpers-7.16.3.tgz", - "integrity": "sha512-Xn8IhDlBPhvYTvgewPKawhADichOsbkZuzN7qz2BusOM0brChsyXMDJvldWaYMMUNiCQdQzNEioXTp3sC8Nt8w==", + "version": "7.16.5", + "resolved": "https://registry.npmjs.org/@babel/helpers/-/helpers-7.16.5.tgz", + "integrity": "sha512-TLgi6Lh71vvMZGEkFuIxzaPsyeYCHQ5jJOOX1f0xXn0uciFuE8cEk0wyBquMcCxBXZ5BJhE2aUB7pnWTD150Tw==", "dev": true, "dependencies": { "@babel/template": "^7.16.0", - "@babel/traverse": "^7.16.3", + "@babel/traverse": "^7.16.5", "@babel/types": "^7.16.0" }, "engines": { @@ -351,9 +325,9 @@ } }, "node_modules/@babel/parser": { - "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==", + "version": "7.16.6", + "resolved": "https://registry.npmjs.org/@babel/parser/-/parser-7.16.6.tgz", + "integrity": "sha512-Gr86ujcNuPDnNOY8mi383Hvi8IYrJVJYuf3XcuBM/Dgd+bINn/7tHqsj+tKkoreMbmGsFLsltI/JJd8fOFWGDQ==", "dev": true, "bin": { "parser": "bin/babel-parser.js" @@ -377,17 +351,18 @@ } }, "node_modules/@babel/traverse": { - "version": "7.16.3", - "resolved": "https://registry.npmjs.org/@babel/traverse/-/traverse-7.16.3.tgz", - "integrity": "sha512-eolumr1vVMjqevCpwVO99yN/LoGL0EyHiLO5I043aYQvwOJ9eR5UsZSClHVCzfhBduMAsSzgA/6AyqPjNayJag==", + "version": "7.16.5", + "resolved": "https://registry.npmjs.org/@babel/traverse/-/traverse-7.16.5.tgz", + "integrity": "sha512-FOCODAzqUMROikDYLYxl4nmwiLlu85rNqBML/A5hKRVXG2LV8d0iMqgPzdYTcIpjZEBB7D6UDU9vxRZiriASdQ==", "dev": true, "dependencies": { "@babel/code-frame": "^7.16.0", - "@babel/generator": "^7.16.0", + "@babel/generator": "^7.16.5", + "@babel/helper-environment-visitor": "^7.16.5", "@babel/helper-function-name": "^7.16.0", "@babel/helper-hoist-variables": "^7.16.0", "@babel/helper-split-export-declaration": "^7.16.0", - "@babel/parser": "^7.16.3", + "@babel/parser": "^7.16.5", "@babel/types": "^7.16.0", "debug": "^4.1.0", "globals": "^11.1.0" @@ -896,9 +871,9 @@ "dev": true }, "node_modules/@types/node": { - "version": "16.11.12", - "resolved": "https://registry.npmjs.org/@types/node/-/node-16.11.12.tgz", - "integrity": "sha512-+2Iggwg7PxoO5Kyhvsq9VarmPbIelXP070HMImEpbtGCoyWNINQj4wzjbQCXzdHTRXnqufutJb5KAURZANNBAw==", + "version": "16.11.13", + "resolved": "https://registry.npmjs.org/@types/node/-/node-16.11.13.tgz", + "integrity": "sha512-eUXZzHLHoZqj1frtUetNkUetYoJ6X55UmrVnFD4DMhVeAmwLjniZhtBmsRiemQh4uq4G3vUra/Ws/hs9vEvL3Q==", "dev": true }, "node_modules/@types/parse-json": { @@ -1503,13 +1478,13 @@ "dev": true }, "node_modules/browserslist": { - "version": "4.18.1", - "resolved": "https://registry.npmjs.org/browserslist/-/browserslist-4.18.1.tgz", - "integrity": "sha512-8ScCzdpPwR2wQh8IT82CA2VgDwjHyqMovPBZSNH54+tm4Jk2pCuv90gmAdH6J84OCRWi0b4gMe6O6XPXuJnjgQ==", + "version": "4.19.1", + "resolved": "https://registry.npmjs.org/browserslist/-/browserslist-4.19.1.tgz", + "integrity": "sha512-u2tbbG5PdKRTUoctO3NBD8FQ5HdPh1ZXPHzp1rwaa5jTc+RV9/+RlWiAIKmjRPQF+xbGM9Kklj5bZQFa2s/38A==", "dev": true, "dependencies": { - "caniuse-lite": "^1.0.30001280", - "electron-to-chromium": "^1.3.896", + "caniuse-lite": "^1.0.30001286", + "electron-to-chromium": "^1.4.17", "escalade": "^3.1.1", "node-releases": "^2.0.1", "picocolors": "^1.0.0" @@ -2108,9 +2083,9 @@ "dev": true }, "node_modules/electron-to-chromium": { - "version": "1.4.17", - "resolved": "https://registry.npmjs.org/electron-to-chromium/-/electron-to-chromium-1.4.17.tgz", - "integrity": "sha512-zhk1MravPtq/KBhmGB7TLBILmXTgRG9TFSI3qS3DbgyfHzIl72iiTE37r/BHIbPCJJlWIo5rySyxiH4vWhu2ZA==", + "version": "1.4.19", + "resolved": "https://registry.npmjs.org/electron-to-chromium/-/electron-to-chromium-1.4.19.tgz", + "integrity": "sha512-TeAjwsC/vhvxEtX/xN1JQUMkl+UrwKXlB4rwLyuLYVuBuRtqJJrU4Jy5pCVihMQg4m1ceZ3MEJ0yYuxHj8vC+w==", "dev": true }, "node_modules/email-addresses": { @@ -6026,9 +6001,9 @@ } }, "node_modules/typescript": { - "version": "4.5.3", - "resolved": "https://registry.npmjs.org/typescript/-/typescript-4.5.3.tgz", - "integrity": "sha512-eVYaEHALSt+s9LbvgEv4Ef+Tdq7hBiIZgii12xXJnukryt3pMgJf6aKhoCZ3FWQsu6sydEnkg11fYXLzhLBjeQ==", + "version": "4.5.4", + "resolved": "https://registry.npmjs.org/typescript/-/typescript-4.5.4.tgz", + "integrity": "sha512-VgYs2A2QIRuGphtzFV7aQJduJ2gyfTljngLzjpfW9FoYZF6xuw1W0vW9ghCKLfcWrCFxK81CSGRAvS1pn4fIUg==", "dev": true, "bin": { "tsc": "bin/tsc", @@ -6473,7 +6448,8 @@ } }, "packages/client": { - "version": "1.0.0", + "name": "@node-redis/client", + "version": "1.0.1", "license": "MIT", "dependencies": { "cluster-key-slot": "1.1.0", @@ -6504,7 +6480,8 @@ } }, "packages/json": { - "version": "1.0.0-rc.0", + "name": "@node-redis/json", + "version": "1.0.1", "license": "MIT", "devDependencies": { "@istanbuljs/nyc-config-typescript": "^1.0.2", @@ -6522,7 +6499,8 @@ } }, "packages/search": { - "version": "1.0.0", + "name": "@node-redis/search", + "version": "1.0.1", "license": "MIT", "devDependencies": { "@istanbuljs/nyc-config-typescript": "^1.0.2", @@ -6540,6 +6518,7 @@ } }, "packages/test-utils": { + "name": "@node-redis/test-utils", "devDependencies": { "@istanbuljs/nyc-config-typescript": "^1.0.2", "@types/mocha": "^9.0.0", @@ -6558,6 +6537,7 @@ } }, "packages/time-series": { + "name": "@node-redis/time-series", "version": "1.0.0-rc.0", "license": "MIT", "devDependencies": { @@ -6593,19 +6573,19 @@ "dev": true }, "@babel/core": { - "version": "7.16.0", - "resolved": "https://registry.npmjs.org/@babel/core/-/core-7.16.0.tgz", - "integrity": "sha512-mYZEvshBRHGsIAiyH5PzCFTCfbWfoYbO/jcSdXQSUQu1/pW0xDZAUP7KEc32heqWTAfAHhV9j1vH8Sav7l+JNQ==", + "version": "7.16.5", + "resolved": "https://registry.npmjs.org/@babel/core/-/core-7.16.5.tgz", + "integrity": "sha512-wUcenlLzuWMZ9Zt8S0KmFwGlH6QKRh3vsm/dhDA3CHkiTA45YuG1XkHRcNRl73EFPXDp/d5kVOU0/y7x2w6OaQ==", "dev": true, "requires": { "@babel/code-frame": "^7.16.0", - "@babel/generator": "^7.16.0", - "@babel/helper-compilation-targets": "^7.16.0", - "@babel/helper-module-transforms": "^7.16.0", - "@babel/helpers": "^7.16.0", - "@babel/parser": "^7.16.0", + "@babel/generator": "^7.16.5", + "@babel/helper-compilation-targets": "^7.16.3", + "@babel/helper-module-transforms": "^7.16.5", + "@babel/helpers": "^7.16.5", + "@babel/parser": "^7.16.5", "@babel/template": "^7.16.0", - "@babel/traverse": "^7.16.0", + "@babel/traverse": "^7.16.5", "@babel/types": "^7.16.0", "convert-source-map": "^1.7.0", "debug": "^4.1.0", @@ -6616,9 +6596,9 @@ } }, "@babel/generator": { - "version": "7.16.0", - "resolved": "https://registry.npmjs.org/@babel/generator/-/generator-7.16.0.tgz", - "integrity": "sha512-RR8hUCfRQn9j9RPKEVXo9LiwoxLPYn6hNZlvUOR8tSnaxlD0p0+la00ZP9/SnRt6HchKr+X0fO2r8vrETiJGew==", + "version": "7.16.5", + "resolved": "https://registry.npmjs.org/@babel/generator/-/generator-7.16.5.tgz", + "integrity": "sha512-kIvCdjZqcdKqoDbVVdt5R99icaRtrtYhYK/xux5qiWCBmfdvEYMFZ68QCrpE5cbFM1JsuArUNs1ZkuKtTtUcZA==", "dev": true, "requires": { "@babel/types": "^7.16.0", @@ -6638,6 +6618,15 @@ "semver": "^6.3.0" } }, + "@babel/helper-environment-visitor": { + "version": "7.16.5", + "resolved": "https://registry.npmjs.org/@babel/helper-environment-visitor/-/helper-environment-visitor-7.16.5.tgz", + "integrity": "sha512-ODQyc5AnxmZWm/R2W7fzhamOk1ey8gSguo5SGvF0zcB3uUzRpTRmM/jmLSm9bDMyPlvbyJ+PwPEK0BWIoZ9wjg==", + "dev": true, + "requires": { + "@babel/types": "^7.16.0" + } + }, "@babel/helper-function-name": { "version": "7.16.0", "resolved": "https://registry.npmjs.org/@babel/helper-function-name/-/helper-function-name-7.16.0.tgz", @@ -6667,15 +6656,6 @@ "@babel/types": "^7.16.0" } }, - "@babel/helper-member-expression-to-functions": { - "version": "7.16.0", - "resolved": "https://registry.npmjs.org/@babel/helper-member-expression-to-functions/-/helper-member-expression-to-functions-7.16.0.tgz", - "integrity": "sha512-bsjlBFPuWT6IWhl28EdrQ+gTvSvj5tqVP5Xeftp07SEuz5pLnsXZuDkDD3Rfcxy0IsHmbZ+7B2/9SHzxO0T+sQ==", - "dev": true, - "requires": { - "@babel/types": "^7.16.0" - } - }, "@babel/helper-module-imports": { "version": "7.16.0", "resolved": "https://registry.npmjs.org/@babel/helper-module-imports/-/helper-module-imports-7.16.0.tgz", @@ -6686,39 +6666,18 @@ } }, "@babel/helper-module-transforms": { - "version": "7.16.0", - "resolved": "https://registry.npmjs.org/@babel/helper-module-transforms/-/helper-module-transforms-7.16.0.tgz", - "integrity": "sha512-My4cr9ATcaBbmaEa8M0dZNA74cfI6gitvUAskgDtAFmAqyFKDSHQo5YstxPbN+lzHl2D9l/YOEFqb2mtUh4gfA==", + "version": "7.16.5", + "resolved": "https://registry.npmjs.org/@babel/helper-module-transforms/-/helper-module-transforms-7.16.5.tgz", + "integrity": "sha512-CkvMxgV4ZyyioElFwcuWnDCcNIeyqTkCm9BxXZi73RR1ozqlpboqsbGUNvRTflgZtFbbJ1v5Emvm+lkjMYY/LQ==", "dev": true, "requires": { + "@babel/helper-environment-visitor": "^7.16.5", "@babel/helper-module-imports": "^7.16.0", - "@babel/helper-replace-supers": "^7.16.0", "@babel/helper-simple-access": "^7.16.0", "@babel/helper-split-export-declaration": "^7.16.0", "@babel/helper-validator-identifier": "^7.15.7", "@babel/template": "^7.16.0", - "@babel/traverse": "^7.16.0", - "@babel/types": "^7.16.0" - } - }, - "@babel/helper-optimise-call-expression": { - "version": "7.16.0", - "resolved": "https://registry.npmjs.org/@babel/helper-optimise-call-expression/-/helper-optimise-call-expression-7.16.0.tgz", - "integrity": "sha512-SuI467Gi2V8fkofm2JPnZzB/SUuXoJA5zXe/xzyPP2M04686RzFKFHPK6HDVN6JvWBIEW8tt9hPR7fXdn2Lgpw==", - "dev": true, - "requires": { - "@babel/types": "^7.16.0" - } - }, - "@babel/helper-replace-supers": { - "version": "7.16.0", - "resolved": "https://registry.npmjs.org/@babel/helper-replace-supers/-/helper-replace-supers-7.16.0.tgz", - "integrity": "sha512-TQxuQfSCdoha7cpRNJvfaYxxxzmbxXw/+6cS7V02eeDYyhxderSoMVALvwupA54/pZcOTtVeJ0xccp1nGWladA==", - "dev": true, - "requires": { - "@babel/helper-member-expression-to-functions": "^7.16.0", - "@babel/helper-optimise-call-expression": "^7.16.0", - "@babel/traverse": "^7.16.0", + "@babel/traverse": "^7.16.5", "@babel/types": "^7.16.0" } }, @@ -6753,13 +6712,13 @@ "dev": true }, "@babel/helpers": { - "version": "7.16.3", - "resolved": "https://registry.npmjs.org/@babel/helpers/-/helpers-7.16.3.tgz", - "integrity": "sha512-Xn8IhDlBPhvYTvgewPKawhADichOsbkZuzN7qz2BusOM0brChsyXMDJvldWaYMMUNiCQdQzNEioXTp3sC8Nt8w==", + "version": "7.16.5", + "resolved": "https://registry.npmjs.org/@babel/helpers/-/helpers-7.16.5.tgz", + "integrity": "sha512-TLgi6Lh71vvMZGEkFuIxzaPsyeYCHQ5jJOOX1f0xXn0uciFuE8cEk0wyBquMcCxBXZ5BJhE2aUB7pnWTD150Tw==", "dev": true, "requires": { "@babel/template": "^7.16.0", - "@babel/traverse": "^7.16.3", + "@babel/traverse": "^7.16.5", "@babel/types": "^7.16.0" } }, @@ -6827,9 +6786,9 @@ } }, "@babel/parser": { - "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==", + "version": "7.16.6", + "resolved": "https://registry.npmjs.org/@babel/parser/-/parser-7.16.6.tgz", + "integrity": "sha512-Gr86ujcNuPDnNOY8mi383Hvi8IYrJVJYuf3XcuBM/Dgd+bINn/7tHqsj+tKkoreMbmGsFLsltI/JJd8fOFWGDQ==", "dev": true }, "@babel/template": { @@ -6844,17 +6803,18 @@ } }, "@babel/traverse": { - "version": "7.16.3", - "resolved": "https://registry.npmjs.org/@babel/traverse/-/traverse-7.16.3.tgz", - "integrity": "sha512-eolumr1vVMjqevCpwVO99yN/LoGL0EyHiLO5I043aYQvwOJ9eR5UsZSClHVCzfhBduMAsSzgA/6AyqPjNayJag==", + "version": "7.16.5", + "resolved": "https://registry.npmjs.org/@babel/traverse/-/traverse-7.16.5.tgz", + "integrity": "sha512-FOCODAzqUMROikDYLYxl4nmwiLlu85rNqBML/A5hKRVXG2LV8d0iMqgPzdYTcIpjZEBB7D6UDU9vxRZiriASdQ==", "dev": true, "requires": { "@babel/code-frame": "^7.16.0", - "@babel/generator": "^7.16.0", + "@babel/generator": "^7.16.5", + "@babel/helper-environment-visitor": "^7.16.5", "@babel/helper-function-name": "^7.16.0", "@babel/helper-hoist-variables": "^7.16.0", "@babel/helper-split-export-declaration": "^7.16.0", - "@babel/parser": "^7.16.3", + "@babel/parser": "^7.16.5", "@babel/types": "^7.16.0", "debug": "^4.1.0", "globals": "^11.1.0" @@ -7355,9 +7315,9 @@ "dev": true }, "@types/node": { - "version": "16.11.12", - "resolved": "https://registry.npmjs.org/@types/node/-/node-16.11.12.tgz", - "integrity": "sha512-+2Iggwg7PxoO5Kyhvsq9VarmPbIelXP070HMImEpbtGCoyWNINQj4wzjbQCXzdHTRXnqufutJb5KAURZANNBAw==", + "version": "16.11.13", + "resolved": "https://registry.npmjs.org/@types/node/-/node-16.11.13.tgz", + "integrity": "sha512-eUXZzHLHoZqj1frtUetNkUetYoJ6X55UmrVnFD4DMhVeAmwLjniZhtBmsRiemQh4uq4G3vUra/Ws/hs9vEvL3Q==", "dev": true }, "@types/parse-json": { @@ -7793,13 +7753,13 @@ "dev": true }, "browserslist": { - "version": "4.18.1", - "resolved": "https://registry.npmjs.org/browserslist/-/browserslist-4.18.1.tgz", - "integrity": "sha512-8ScCzdpPwR2wQh8IT82CA2VgDwjHyqMovPBZSNH54+tm4Jk2pCuv90gmAdH6J84OCRWi0b4gMe6O6XPXuJnjgQ==", + "version": "4.19.1", + "resolved": "https://registry.npmjs.org/browserslist/-/browserslist-4.19.1.tgz", + "integrity": "sha512-u2tbbG5PdKRTUoctO3NBD8FQ5HdPh1ZXPHzp1rwaa5jTc+RV9/+RlWiAIKmjRPQF+xbGM9Kklj5bZQFa2s/38A==", "dev": true, "requires": { - "caniuse-lite": "^1.0.30001280", - "electron-to-chromium": "^1.3.896", + "caniuse-lite": "^1.0.30001286", + "electron-to-chromium": "^1.4.17", "escalade": "^3.1.1", "node-releases": "^2.0.1", "picocolors": "^1.0.0" @@ -8240,9 +8200,9 @@ "dev": true }, "electron-to-chromium": { - "version": "1.4.17", - "resolved": "https://registry.npmjs.org/electron-to-chromium/-/electron-to-chromium-1.4.17.tgz", - "integrity": "sha512-zhk1MravPtq/KBhmGB7TLBILmXTgRG9TFSI3qS3DbgyfHzIl72iiTE37r/BHIbPCJJlWIo5rySyxiH4vWhu2ZA==", + "version": "1.4.19", + "resolved": "https://registry.npmjs.org/electron-to-chromium/-/electron-to-chromium-1.4.19.tgz", + "integrity": "sha512-TeAjwsC/vhvxEtX/xN1JQUMkl+UrwKXlB4rwLyuLYVuBuRtqJJrU4Jy5pCVihMQg4m1ceZ3MEJ0yYuxHj8vC+w==", "dev": true }, "email-addresses": { @@ -11149,9 +11109,9 @@ } }, "typescript": { - "version": "4.5.3", - "resolved": "https://registry.npmjs.org/typescript/-/typescript-4.5.3.tgz", - "integrity": "sha512-eVYaEHALSt+s9LbvgEv4Ef+Tdq7hBiIZgii12xXJnukryt3pMgJf6aKhoCZ3FWQsu6sydEnkg11fYXLzhLBjeQ==", + "version": "4.5.4", + "resolved": "https://registry.npmjs.org/typescript/-/typescript-4.5.4.tgz", + "integrity": "sha512-VgYs2A2QIRuGphtzFV7aQJduJ2gyfTljngLzjpfW9FoYZF6xuw1W0vW9ghCKLfcWrCFxK81CSGRAvS1pn4fIUg==", "dev": true }, "unique-string": { diff --git a/packages/client/lib/client/commands-queue.ts b/packages/client/lib/client/commands-queue.ts index 2ce48100ec7..4c588372593 100644 --- a/packages/client/lib/client/commands-queue.ts +++ b/packages/client/lib/client/commands-queue.ts @@ -1,6 +1,6 @@ import * as LinkedList from 'yallist'; import { AbortError } from '../errors'; -import { RedisCommandArguments, RedisCommandRawReply } from '../commands'; +import { RedisCommandArgument, 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 @@ -40,8 +40,6 @@ export enum PubSubUnsubscribeCommands { PUNSUBSCRIBE = 'PUNSUBSCRIBE' } -type PubSubArgumentTypes = Buffer | string; - export type PubSubListener< BUFFER_MODE extends boolean = false, T = BUFFER_MODE extends true ? Buffer : string @@ -197,12 +195,12 @@ export default class RedisCommandsQueue { subscribe( command: PubSubSubscribeCommands, - channels: PubSubArgumentTypes | Array, + channels: RedisCommandArgument | Array, listener: PubSubListener, bufferMode?: T ): Promise { const pubSubState = this.#initiatePubSubState(), - channelsToSubscribe: Array = [], + channelsToSubscribe: Array = [], listenersMap = command === PubSubSubscribeCommands.SUBSCRIBE ? pubSubState.listeners.channels : pubSubState.listeners.patterns; for (const channel of (Array.isArray(channels) ? channels : [channels])) { const channelString = typeof channel === 'string' ? channel : channel.toString(); @@ -271,12 +269,12 @@ export default class RedisCommandsQueue { return this.#pushPubSubCommand(command, channelsToUnsubscribe); } - #pushPubSubCommand(command: PubSubSubscribeCommands | PubSubUnsubscribeCommands, channels: number | Array): Promise { + #pushPubSubCommand(command: PubSubSubscribeCommands | PubSubUnsubscribeCommands, channels: number | Array): Promise { return new Promise((resolve, reject) => { const pubSubState = this.#initiatePubSubState(), isSubscribe = command === PubSubSubscribeCommands.SUBSCRIBE || command === PubSubSubscribeCommands.PSUBSCRIBE, inProgressKey = isSubscribe ? 'subscribing' : 'unsubscribing', - commandArgs: Array = [command]; + commandArgs: Array = [command]; let channelsCounter: number; if (typeof channels === 'number') { // unsubscribe only diff --git a/packages/client/lib/client/index.ts b/packages/client/lib/client/index.ts index 3bbf14a116c..bd2557ce92c 100644 --- a/packages/client/lib/client/index.ts +++ b/packages/client/lib/client/index.ts @@ -575,7 +575,7 @@ export default class RedisClient } while (cursor !== 0); } - async* zScanIterator(key: string, options?: ScanOptions): AsyncIterable { + async* zScanIterator(key: string, options?: ScanOptions): AsyncIterable> { let cursor = 0; do { const reply = await (this as any).zScan(key, cursor, options); diff --git a/packages/client/lib/cluster/cluster-slots.ts b/packages/client/lib/cluster/cluster-slots.ts index f69449efa1a..37a17b73aea 100644 --- a/packages/client/lib/cluster/cluster-slots.ts +++ b/packages/client/lib/cluster/cluster-slots.ts @@ -1,7 +1,7 @@ import RedisClient, { InstantiableRedisClient, RedisClientType } from '../client'; import { RedisClusterMasterNode, RedisClusterReplicaNode } from '../commands/CLUSTER_NODES'; import { RedisClusterClientOptions, RedisClusterOptions } from '.'; -import { RedisModules, RedisScripts } from '../commands'; +import { RedisCommandArgument, RedisModules, RedisScripts } from '../commands'; // We need to use 'require', because it's not possible with Typescript to import // function that are exported as 'module.exports = function`, without esModuleInterop @@ -202,7 +202,7 @@ export default class RedisClusterSlots { + getClient(firstKey?: RedisCommandArgument, isReadonly?: boolean): RedisClientType { if (!firstKey) { return this.#getRandomClient(); } diff --git a/packages/client/lib/cluster/commands.ts b/packages/client/lib/cluster/commands.ts index 9442337e62b..5a8dca4ea4e 100644 --- a/packages/client/lib/cluster/commands.ts +++ b/packages/client/lib/cluster/commands.ts @@ -4,9 +4,13 @@ import * as BITCOUNT from '../commands/BITCOUNT'; import * as BITFIELD from '../commands/BITFIELD'; import * as BITOP from '../commands/BITOP'; import * as BITPOS from '../commands/BITPOS'; +import * as BLMOVE_BUFFER from '../commands/BLMOVE_BUFFER'; import * as BLMOVE from '../commands/BLMOVE'; +import * as BLPOP_BUFFER from '../commands/BLPOP_BUFFER'; import * as BLPOP from '../commands/BLPOP'; +import * as BRPOP_BUFFER from '../commands/BRPOP_BUFFER'; import * as BRPOP from '../commands/BRPOP'; +import * as BRPOPLPUSH_BUFFER from '../commands/BRPOPLPUSH_BUFFER'; import * as BRPOPLPUSH from '../commands/BRPOPLPUSH'; import * as BZPOPMAX from '../commands/BZPOPMAX'; import * as BZPOPMIN from '../commands/BZPOPMIN'; @@ -36,13 +40,16 @@ import * as GETRANGE from '../commands/GETRANGE'; import * as GETSET from '../commands/GETSET'; import * as HDEL from '../commands/HDEL'; import * as HEXISTS from '../commands/HEXISTS'; +import * as HGET_BUFFER from '../commands/HGET_BUFFER'; import * as HGET from '../commands/HGET'; +import * as HGETALL_BUFFER from '../commands/HGETALL_BUFFER'; import * as HGETALL from '../commands/HGETALL'; import * as HINCRBY from '../commands/HINCRBY'; import * as HINCRBYFLOAT from '../commands/HINCRBYFLOAT'; import * as HKEYS from '../commands/HKEYS'; import * as HLEN from '../commands/HLEN'; import * as HMGET from '../commands/HMGET'; +import * as HRANDFIELD_COUNT_WITHVALUES_BUFFER from '../commands/HRANDFIELD_COUNT_WITHVALUES_BUFFER'; import * as HRANDFIELD_COUNT_WITHVALUES from '../commands/HRANDFIELD_COUNT_WITHVALUES'; import * as HRANDFIELD_COUNT from '../commands/HRANDFIELD_COUNT'; import * as HRANDFIELD from '../commands/HRANDFIELD'; @@ -119,10 +126,14 @@ import * as UNLINK from '../commands/UNLINK'; import * as WATCH from '../commands/WATCH'; import * as XACK from '../commands/XACK'; import * as XADD from '../commands/XADD'; +import * as XAUTOCLAIM_JUSTID_BUFFER from '../commands/XAUTOCLAIM_JUSTID_BUFFER'; +import * as XAUTOCLAIM_BUFFER from '../commands/XAUTOCLAIM_BUFFER'; import * as XAUTOCLAIM_JUSTID from '../commands/XAUTOCLAIM_JUSTID'; import * as XAUTOCLAIM from '../commands/XAUTOCLAIM'; -import * as XCLAIM from '../commands/XCLAIM'; +import * as XCLAIM_JUSTID_BUFFER from '../commands/XCLAIM_JUSTID_BUFFER'; +import * as XCLAIM_BUFFER from '../commands/XCLAIM_BUFFER'; import * as XCLAIM_JUSTID from '../commands/XCLAIM_JUSTID'; +import * as XCLAIM from '../commands/XCLAIM'; import * as XDEL from '../commands/XDEL'; import * as XGROUP_CREATE from '../commands/XGROUP_CREATE'; import * as XGROUP_CREATECONSUMER from '../commands/XGROUP_CREATECONSUMER'; @@ -135,9 +146,13 @@ import * as XINFO_STREAM from '../commands/XINFO_STREAM'; import * as XLEN from '../commands/XLEN'; import * as XPENDING_RANGE from '../commands/XPENDING_RANGE'; import * as XPENDING from '../commands/XPENDING'; +import * as XRANGE_BUFFER from '../commands/XRANGE_BUFFER'; import * as XRANGE from '../commands/XRANGE'; +import * as XREAD_BUFFER from '../commands/XREAD_BUFFER'; import * as XREAD from '../commands/XREAD'; +import * as XREADGROUP_BUFFER from '../commands/XREADGROUP_BUFFER'; import * as XREADGROUP from '../commands/XREADGROUP'; +import * as XREVRANGE_BUFFER from '../commands/XREVRANGE_BUFFER'; import * as XREVRANGE from '../commands/XREVRANGE'; import * as XTRIM from '../commands/XTRIM'; import * as ZADD from '../commands/ZADD'; @@ -188,12 +203,20 @@ export default { bitOp: BITOP, BITPOS, bitPos: BITPOS, + BLMOVE_BUFFER, + blMoveBuffer: BLMOVE_BUFFER, BLMOVE, blMove: BLMOVE, + BLPOP_BUFFER, + blPopBuffer: BLPOP_BUFFER, BLPOP, blPop: BLPOP, + BRPOP_BUFFER, + brPopBuffer: BRPOP_BUFFER, BRPOP, brPop: BRPOP, + BRPOPLPUSH_BUFFER, + brPopLPushBuffer: BRPOPLPUSH_BUFFER, BRPOPLPUSH, brPopLPush: BRPOPLPUSH, BZPOPMAX, @@ -252,8 +275,12 @@ export default { hDel: HDEL, HEXISTS, hExists: HEXISTS, + HGET_BUFFER, + hGetBuffer: HGET_BUFFER, HGET, hGet: HGET, + HGETALL_BUFFER, + hGetAllBuffer: HGETALL_BUFFER, HGETALL, hGetAll: HGETALL, HINCRBY, @@ -266,6 +293,8 @@ export default { hLen: HLEN, HMGET, hmGet: HMGET, + HRANDFIELD_COUNT_WITHVALUES_BUFFER, + hRandFieldCountWithValuesBuffer: HRANDFIELD_COUNT_WITHVALUES_BUFFER, HRANDFIELD_COUNT_WITHVALUES, hRandFieldCountWithValues: HRANDFIELD_COUNT_WITHVALUES, HRANDFIELD_COUNT, @@ -418,12 +447,20 @@ export default { xAck: XACK, XADD, xAdd: XADD, + XAUTOCLAIM_JUSTID_BUFFER, + xAutoClaimJustIdBuffer: XAUTOCLAIM_JUSTID_BUFFER, + XAUTOCLAIM_BUFFER, + xAutoClaimBuffer: XAUTOCLAIM_BUFFER, XAUTOCLAIM_JUSTID, xAutoClaimJustId: XAUTOCLAIM_JUSTID, XAUTOCLAIM, xAutoClaim: XAUTOCLAIM, XCLAIM, xClaim: XCLAIM, + XCLAIM_JUSTID_BUFFER, + xClaimJustIdBuffer: XCLAIM_JUSTID_BUFFER, + XCLAIM_BUFFER, + xClaimBuffer: XCLAIM_BUFFER, XCLAIM_JUSTID, xClaimJustId: XCLAIM_JUSTID, XDEL, @@ -450,12 +487,20 @@ export default { xPendingRange: XPENDING_RANGE, XPENDING, xPending: XPENDING, + XRANGE_BUFFER, + xRangeBuffer: XRANGE_BUFFER, XRANGE, xRange: XRANGE, + XREAD_BUFFER, + xReadBuffer: XREAD_BUFFER, XREAD, xRead: XREAD, + XREADGROUP_BUFFER, + xReadGroupBuffer: XREADGROUP_BUFFER, XREADGROUP, xReadGroup: XREADGROUP, + XREVRANGE_BUFFER, + xRevRangeBuffer: XREVRANGE_BUFFER, XREVRANGE, xRevRange: XREVRANGE, XTRIM, diff --git a/packages/client/lib/cluster/index.ts b/packages/client/lib/cluster/index.ts index 11b9c937cf8..a9443600568 100644 --- a/packages/client/lib/cluster/index.ts +++ b/packages/client/lib/cluster/index.ts @@ -1,5 +1,5 @@ import COMMANDS from './commands'; -import { RedisCommand, RedisCommandArguments, RedisCommandReply, RedisModules, RedisPlugins, RedisScript, RedisScripts } from '../commands'; +import { RedisCommand, RedisCommandArgument, RedisCommandArguments, RedisCommandReply, RedisModules, RedisPlugins, RedisScript, RedisScripts } from '../commands'; import { ClientCommandOptions, RedisClientCommandSignature, RedisClientOptions, RedisClientType, WithModules, WithScripts } from '../client'; import RedisClusterSlots, { ClusterNode } from './cluster-slots'; import { extendWithModulesAndScripts, transformCommandArguments, transformCommandReply, extendWithCommands } from '../commander'; @@ -24,7 +24,7 @@ export type RedisClusterType, S e RedisCluster & WithCommands & WithModules & WithScripts; export default class RedisCluster, S extends RedisScripts = Record> extends EventEmitter { - static extractFirstKey(command: RedisCommand, originalArgs: Array, redisArgs: RedisCommandArguments): string | Buffer | undefined { + static extractFirstKey(command: RedisCommand, originalArgs: Array, redisArgs: RedisCommandArguments): RedisCommandArgument | undefined { if (command.FIRST_KEY_INDEX === undefined) { return undefined; } else if (typeof command.FIRST_KEY_INDEX === 'number') { @@ -84,7 +84,7 @@ export default class RedisCluster } async sendCommand( - firstKey: string | Buffer | undefined, + firstKey: RedisCommandArgument | undefined, isReadonly: boolean | undefined, args: RedisCommandArguments, options?: ClientCommandOptions, @@ -175,9 +175,9 @@ export default class RedisCluster throw err; } - multi(routing?: string | Buffer): RedisClusterMultiCommandType { + multi(routing?: RedisCommandArgument): RedisClusterMultiCommandType { return new this.#Multi( - async (commands: Array, firstKey?: string | Buffer, chainId?: symbol) => { + async (commands: Array, firstKey?: RedisCommandArgument, chainId?: symbol) => { return this.#slots .getClient(firstKey) .multiExecutor(commands, chainId); diff --git a/packages/client/lib/cluster/multi-command.ts b/packages/client/lib/cluster/multi-command.ts index 8a798ca9092..974dcb10293 100644 --- a/packages/client/lib/cluster/multi-command.ts +++ b/packages/client/lib/cluster/multi-command.ts @@ -1,5 +1,5 @@ import COMMANDS from './commands'; -import { RedisCommand, RedisCommandArguments, RedisCommandRawReply, RedisModules, RedisPlugins, RedisScript, RedisScripts } from '../commands'; +import { RedisCommand, RedisCommandArgument, RedisCommandArguments, RedisCommandRawReply, RedisModules, RedisPlugins, RedisScript, RedisScripts } from '../commands'; import RedisMultiCommand, { RedisMultiQueuedCommand } from '../multi-command'; import { extendWithCommands, extendWithModulesAndScripts } from '../commander'; import RedisCluster from '.'; @@ -24,12 +24,12 @@ type WithScripts = { export type RedisClusterMultiCommandType, S extends RedisScripts = Record> = RedisClusterMultiCommand & WithCommands & WithModules & WithScripts; -export type RedisClusterMultiExecutor = (queue: Array, firstKey?: string | Buffer, chainId?: symbol) => Promise>; +export type RedisClusterMultiExecutor = (queue: Array, firstKey?: RedisCommandArgument, chainId?: symbol) => Promise>; export default class RedisClusterMultiCommand { readonly #multi = new RedisMultiCommand(); readonly #executor: RedisClusterMultiExecutor; - #firstKey: string | Buffer | undefined; + #firstKey: RedisCommandArgument | undefined; static extend( plugins?: RedisPlugins @@ -43,7 +43,7 @@ export default class RedisClusterMultiCommand { }); } - constructor(executor: RedisClusterMultiExecutor, firstKey?: string | Buffer) { + constructor(executor: RedisClusterMultiExecutor, firstKey?: RedisCommandArgument) { this.#executor = executor; this.#firstKey = firstKey; } @@ -62,7 +62,7 @@ export default class RedisClusterMultiCommand { } addCommand( - firstKey: string | Buffer | undefined, + firstKey: RedisCommandArgument | undefined, args: RedisCommandArguments, transformReply?: RedisCommand['transformReply'] ): this { diff --git a/packages/client/lib/commander.ts b/packages/client/lib/commander.ts index 3f11ebc39a7..fa1136f873e 100644 --- a/packages/client/lib/commander.ts +++ b/packages/client/lib/commander.ts @@ -1,6 +1,6 @@ import { CommandOptions, isCommandOptions } from './command-options'; -import { RedisCommand, RedisCommandArguments, RedisCommandRawReply, RedisCommandReply, RedisCommands, RedisModules, RedisScript, RedisScripts } from './commands'; +import { RedisCommand, RedisCommandArgument, RedisCommandArguments, RedisCommandRawReply, RedisCommandReply, RedisCommands, RedisModules, RedisScript, RedisScripts } from './commands'; type Instantiable = new(...args: Array) => T; @@ -91,7 +91,7 @@ export function transformCommandArguments( const DELIMITER = '\r\n'; -export function* encodeCommand(args: RedisCommandArguments): IterableIterator { +export function* encodeCommand(args: RedisCommandArguments): IterableIterator { let strings = `*${args.length}${DELIMITER}`, stringsLength = 0; for (const arg of args) { diff --git a/packages/client/lib/commands/APPEND.ts b/packages/client/lib/commands/APPEND.ts index a162dc1381c..66f7fc84798 100644 --- a/packages/client/lib/commands/APPEND.ts +++ b/packages/client/lib/commands/APPEND.ts @@ -1,7 +1,12 @@ +import { RedisCommandArgument, RedisCommandArguments } from '.'; + export const FIRST_KEY_INDEX = 1; -export function transformArguments(key: string, value: string): Array { +export function transformArguments( + key: RedisCommandArgument, + value: RedisCommandArgument +): RedisCommandArguments { return ['APPEND', key, value]; } -export declare function transformReply(): string; +export declare function transformReply(): number; diff --git a/packages/client/lib/commands/BLMOVE.ts b/packages/client/lib/commands/BLMOVE.ts index 461a146e7a3..1aa71458922 100644 --- a/packages/client/lib/commands/BLMOVE.ts +++ b/packages/client/lib/commands/BLMOVE.ts @@ -1,14 +1,15 @@ +import { RedisCommandArgument, RedisCommandArguments } from '.'; import { LMoveSide } from './LMOVE'; export const FIRST_KEY_INDEX = 1; export function transformArguments( - source: string, - destination: string, + source: RedisCommandArgument, + destination: RedisCommandArgument, sourceDirection: LMoveSide, destinationDirection: LMoveSide, timeout: number -): Array { +): RedisCommandArguments { return [ 'BLMOVE', source, diff --git a/packages/client/lib/commands/BLMOVE_BUFFER.ts b/packages/client/lib/commands/BLMOVE_BUFFER.ts new file mode 100644 index 00000000000..2e676281e2e --- /dev/null +++ b/packages/client/lib/commands/BLMOVE_BUFFER.ts @@ -0,0 +1,5 @@ +export { FIRST_KEY_INDEX, transformArguments } from './BLMOVE'; + +export const BUFFER_MODE = true; + +export declare function transformReply(): Buffer | null; diff --git a/packages/client/lib/commands/BLPOP.ts b/packages/client/lib/commands/BLPOP.ts index 15c52722941..53eaa0b78e5 100644 --- a/packages/client/lib/commands/BLPOP.ts +++ b/packages/client/lib/commands/BLPOP.ts @@ -1,9 +1,12 @@ -import { RedisCommandArguments } from '.'; +import { RedisCommandArgument, RedisCommandArguments } from '.'; import { pushVerdictArguments } from './generic-transformers'; export const FIRST_KEY_INDEX = 1; -export function transformArguments(keys: string | Buffer | Array, timeout: number): RedisCommandArguments { +export function transformArguments( + keys: RedisCommandArgument | Array, + timeout: number +): RedisCommandArguments { const args = pushVerdictArguments(['BLPOP'], keys); args.push(timeout.toString()); @@ -11,12 +14,12 @@ export function transformArguments(keys: string | Buffer | Array, timeout: number) return args; } -type BRPOPReply = null | { - key: string; - element: string; -}; - -export function transformReply(reply: null | [string, string]): BRPOPReply { - if (reply === null) return null; - - return { - key: reply[0], - element: reply[1] - }; -} +export { transformReply } from './BLPOP'; diff --git a/packages/client/lib/commands/BRPOPLPUSH.ts b/packages/client/lib/commands/BRPOPLPUSH.ts index 3f6aed35be1..7c671fd85a8 100644 --- a/packages/client/lib/commands/BRPOPLPUSH.ts +++ b/packages/client/lib/commands/BRPOPLPUSH.ts @@ -1,7 +1,13 @@ +import { RedisCommandArgument, RedisCommandArguments } from '.'; + export const FIRST_KEY_INDEX = 1; -export function transformArguments(source: string, destination: string, timeout: number): Array { +export function transformArguments( + source: RedisCommandArgument, + destination: RedisCommandArgument, + timeout: number +): RedisCommandArguments { return ['BRPOPLPUSH', source, destination, timeout.toString()]; } -export declare function transformReply(): number | null; +export declare function transformReply(): string | null; diff --git a/packages/client/lib/commands/BRPOPLPUSH_BUFFER.ts b/packages/client/lib/commands/BRPOPLPUSH_BUFFER.ts new file mode 100644 index 00000000000..255f88bab8c --- /dev/null +++ b/packages/client/lib/commands/BRPOPLPUSH_BUFFER.ts @@ -0,0 +1,5 @@ +export { FIRST_KEY_INDEX, transformArguments } from './BRPOPLPUSH'; + +export const BUFFER_MODE = true; + +export declare function transformReply(): Buffer | null; diff --git a/packages/client/lib/commands/BRPOP_BUFFER.ts b/packages/client/lib/commands/BRPOP_BUFFER.ts new file mode 100644 index 00000000000..a30a661da07 --- /dev/null +++ b/packages/client/lib/commands/BRPOP_BUFFER.ts @@ -0,0 +1,3 @@ +export { FIRST_KEY_INDEX, transformArguments } from './BRPOP'; + +export { BUFFER_MODE, transformReply } from './BLPOP_BUFFER'; diff --git a/packages/client/lib/commands/BZPOPMAX.ts b/packages/client/lib/commands/BZPOPMAX.ts index eb6647ce9e3..90f9835f27c 100644 --- a/packages/client/lib/commands/BZPOPMAX.ts +++ b/packages/client/lib/commands/BZPOPMAX.ts @@ -1,9 +1,12 @@ -import { RedisCommandArguments } from '.'; +import { RedisCommandArgument, RedisCommandArguments } from '.'; import { pushVerdictArguments, transformReplyNumberInfinity, ZMember } from './generic-transformers'; export const FIRST_KEY_INDEX = 1; -export function transformArguments(key: string | Array, timeout: number): RedisCommandArguments { +export function transformArguments( + key: RedisCommandArgument | Array, + timeout: number +): RedisCommandArguments { const args = pushVerdictArguments(['BZPOPMAX'], key); args.push(timeout.toString()); @@ -11,7 +14,7 @@ export function transformArguments(key: string | Array, timeout: number) return args; } -interface ZMemberWithKey extends ZMember { +interface ZMemberWithKey extends ZMember { key: string; } diff --git a/packages/client/lib/commands/BZPOPMIN.ts b/packages/client/lib/commands/BZPOPMIN.ts index 75b092e543b..098d9394046 100644 --- a/packages/client/lib/commands/BZPOPMIN.ts +++ b/packages/client/lib/commands/BZPOPMIN.ts @@ -11,7 +11,7 @@ export function transformArguments(key: string | Array, timeout: number) return args; } -interface ZMemberWithKey extends ZMember { +interface ZMemberWithKey extends ZMember { key: string; } diff --git a/packages/client/lib/commands/CONFIG_GET.ts b/packages/client/lib/commands/CONFIG_GET.ts index 35907742e25..3a4f0105d3d 100644 --- a/packages/client/lib/commands/CONFIG_GET.ts +++ b/packages/client/lib/commands/CONFIG_GET.ts @@ -2,4 +2,4 @@ export function transformArguments(parameter: string): Array { return ['CONFIG', 'GET', parameter]; } -export { transformReplyTuples as transformReply } from './generic-transformers'; +export { transformReplyStringTuples as transformReply } from './generic-transformers'; diff --git a/packages/client/lib/commands/GET.ts b/packages/client/lib/commands/GET.ts index 0e89a4e6a74..0bcecc98b87 100644 --- a/packages/client/lib/commands/GET.ts +++ b/packages/client/lib/commands/GET.ts @@ -1,10 +1,10 @@ -import { RedisCommandArguments } from '.'; +import { RedisCommandArgument, RedisCommandArguments } from '.'; export const FIRST_KEY_INDEX = 1; export const IS_READ_ONLY = true; -export function transformArguments(key: string | Buffer): RedisCommandArguments { +export function transformArguments(key: RedisCommandArgument): RedisCommandArguments { return ['GET', key]; } diff --git a/packages/client/lib/commands/HGET.ts b/packages/client/lib/commands/HGET.ts index edabbcd6bc7..8da48901747 100644 --- a/packages/client/lib/commands/HGET.ts +++ b/packages/client/lib/commands/HGET.ts @@ -1,9 +1,14 @@ +import { RedisCommandArgument, RedisCommandArguments } from '.'; + export const FIRST_KEY_INDEX = 1; -export function transformArguments(key: string, field: string): Array { +export const IS_READ_ONLY = true; + +export function transformArguments( + key: RedisCommandArgument, + field: RedisCommandArgument +): RedisCommandArguments { return ['HGET', key, field]; } -export function transformReply(reply?: string): string | undefined { - return reply; -} +export declare function transformReply(): string | undefined; diff --git a/packages/client/lib/commands/HGETALL.ts b/packages/client/lib/commands/HGETALL.ts index 55c6d3456a1..56bd76f4ae4 100644 --- a/packages/client/lib/commands/HGETALL.ts +++ b/packages/client/lib/commands/HGETALL.ts @@ -1,7 +1,9 @@ export const FIRST_KEY_INDEX = 1; +export const IS_READ_ONLY = true; + export function transformArguments(key: string): Array { return ['HGETALL', key]; } -export { transformReplyTuples as transformReply } from './generic-transformers'; +export { transformReplyStringTuples as transformReply } from './generic-transformers'; diff --git a/packages/client/lib/commands/HGETALL_BUFFER.ts b/packages/client/lib/commands/HGETALL_BUFFER.ts new file mode 100644 index 00000000000..61ab171e07b --- /dev/null +++ b/packages/client/lib/commands/HGETALL_BUFFER.ts @@ -0,0 +1,5 @@ +export { FIRST_KEY_INDEX, IS_READ_ONLY, transformArguments } from './HGETALL'; + +export const BUFFER_MODE = true; + +export { transformReplyBufferTuples as transformReply } from './generic-transformers'; diff --git a/packages/client/lib/commands/HGET_BUFFER.ts b/packages/client/lib/commands/HGET_BUFFER.ts new file mode 100644 index 00000000000..07d41f0dc32 --- /dev/null +++ b/packages/client/lib/commands/HGET_BUFFER.ts @@ -0,0 +1,5 @@ +export { FIRST_KEY_INDEX, IS_READ_ONLY, transformArguments } from './HGET'; + +export const BUFFER_MODE = true; + +export declare function transformReply(): Buffer | undefined; diff --git a/packages/client/lib/commands/HRANDFIELD.ts b/packages/client/lib/commands/HRANDFIELD.ts index 24ca9b83d56..63f092bf092 100644 --- a/packages/client/lib/commands/HRANDFIELD.ts +++ b/packages/client/lib/commands/HRANDFIELD.ts @@ -1,5 +1,7 @@ export const FIRST_KEY_INDEX = 1; +export const IS_READ_ONLY = true; + export function transformArguments(key: string): Array { return ['HRANDFIELD', key]; } diff --git a/packages/client/lib/commands/HRANDFIELD_COUNT.ts b/packages/client/lib/commands/HRANDFIELD_COUNT.ts index c0a8b1d449f..b97e38ea165 100644 --- a/packages/client/lib/commands/HRANDFIELD_COUNT.ts +++ b/packages/client/lib/commands/HRANDFIELD_COUNT.ts @@ -1,6 +1,6 @@ import { transformArguments as transformHRandFieldArguments } from './HRANDFIELD'; -export { FIRST_KEY_INDEX } from './HRANDFIELD'; +export { FIRST_KEY_INDEX, IS_READ_ONLY } from './HRANDFIELD'; export function transformArguments(key: string, count: number): Array { return [ diff --git a/packages/client/lib/commands/HRANDFIELD_COUNT_WITHVALUES.ts b/packages/client/lib/commands/HRANDFIELD_COUNT_WITHVALUES.ts index 1769cae0b76..807e3cc1cf7 100644 --- a/packages/client/lib/commands/HRANDFIELD_COUNT_WITHVALUES.ts +++ b/packages/client/lib/commands/HRANDFIELD_COUNT_WITHVALUES.ts @@ -1,6 +1,6 @@ import { transformArguments as transformHRandFieldCountArguments } from './HRANDFIELD_COUNT'; -export { FIRST_KEY_INDEX } from './HRANDFIELD_COUNT'; +export { FIRST_KEY_INDEX, IS_READ_ONLY } from './HRANDFIELD_COUNT'; export function transformArguments(key: string, count: number): Array { return [ @@ -9,4 +9,4 @@ export function transformArguments(key: string, count: number): Array { ]; } -export { transformReplyTuples as transformReply } from './generic-transformers'; +export { transformReplyStringTuples as transformReply } from './generic-transformers'; diff --git a/packages/client/lib/commands/HRANDFIELD_COUNT_WITHVALUES_BUFFER.ts b/packages/client/lib/commands/HRANDFIELD_COUNT_WITHVALUES_BUFFER.ts new file mode 100644 index 00000000000..6937f7400b1 --- /dev/null +++ b/packages/client/lib/commands/HRANDFIELD_COUNT_WITHVALUES_BUFFER.ts @@ -0,0 +1,5 @@ +export { FIRST_KEY_INDEX, IS_READ_ONLY, transformArguments } from './HRANDFIELD_COUNT'; + +export const BUFFER_MODE = true; + +export { transformReplyBufferTuples as transformReply } from './generic-transformers'; diff --git a/packages/client/lib/commands/HSET.ts b/packages/client/lib/commands/HSET.ts index be9fece167d..edb4c76e102 100644 --- a/packages/client/lib/commands/HSET.ts +++ b/packages/client/lib/commands/HSET.ts @@ -1,4 +1,4 @@ -import { RedisCommandArguments } from '.'; +import { RedisCommandArgument, RedisCommandArguments } from '.'; type Types = string | number | Buffer; @@ -10,7 +10,7 @@ type HSETTuples = Array<[Types, Types]> | Array; export const FIRST_KEY_INDEX = 1; -type GenericArguments = [key: string | Buffer]; +type GenericArguments = [key: RedisCommandArgument]; type SingleFieldArguments = [...generic: GenericArguments, field: Types, value: Types]; diff --git a/packages/client/lib/commands/PUBLISH.ts b/packages/client/lib/commands/PUBLISH.ts index cbfcaabd1cd..93a8016900e 100644 --- a/packages/client/lib/commands/PUBLISH.ts +++ b/packages/client/lib/commands/PUBLISH.ts @@ -1,6 +1,9 @@ -import { RedisCommandArguments } from '.'; +import { RedisCommandArgument, RedisCommandArguments } from '.'; -export function transformArguments(channel: string | Buffer, message: string | Buffer): RedisCommandArguments { +export function transformArguments( + channel: RedisCommandArgument, + message: RedisCommandArgument +): RedisCommandArguments { return ['PUBLISH', channel, message]; } diff --git a/packages/client/lib/commands/SET.ts b/packages/client/lib/commands/SET.ts index 7a004e1fb20..e94abc47ac2 100644 --- a/packages/client/lib/commands/SET.ts +++ b/packages/client/lib/commands/SET.ts @@ -1,4 +1,4 @@ -import { RedisCommandArguments } from '.'; +import { RedisCommandArgument, RedisCommandArguments } from '.'; export const FIRST_KEY_INDEX = 1; @@ -24,7 +24,11 @@ interface SetCommonOptions { type SetOptions = SetTTL & SetGuards & SetCommonOptions; -export function transformArguments(key: string | Buffer, value: string | number | Buffer, options?: SetOptions): RedisCommandArguments { +export function transformArguments( + key: RedisCommandArgument, + value: RedisCommandArgument | number, + options?: SetOptions +): RedisCommandArguments { const args = [ 'SET', key, diff --git a/packages/client/lib/commands/SETEX.ts b/packages/client/lib/commands/SETEX.ts index 89c6e06a3f2..871c286b245 100644 --- a/packages/client/lib/commands/SETEX.ts +++ b/packages/client/lib/commands/SETEX.ts @@ -1,8 +1,12 @@ -import { RedisCommandArguments } from '.'; +import { RedisCommandArgument, RedisCommandArguments } from '.'; export const FIRST_KEY_INDEX = 1; -export function transformArguments(key: string | Buffer, seconds: number, value: string): RedisCommandArguments { +export function transformArguments( + key: RedisCommandArgument, + seconds: number, + value: string +): RedisCommandArguments { return [ 'SETEX', key, diff --git a/packages/client/lib/commands/XACK.ts b/packages/client/lib/commands/XACK.ts index 0d21a0ec085..670d810fc00 100644 --- a/packages/client/lib/commands/XACK.ts +++ b/packages/client/lib/commands/XACK.ts @@ -1,9 +1,13 @@ -import { RedisCommandArguments } from '.'; +import { RedisCommandArgument, RedisCommandArguments } from '.'; import { pushVerdictArguments } from './generic-transformers'; export const FIRST_KEY_INDEX = 1; -export function transformArguments(key: string, group: string, id: string | Array): RedisCommandArguments { +export function transformArguments( + key: RedisCommandArgument, + group: RedisCommandArgument, + id: RedisCommandArgument | Array +): RedisCommandArguments { return pushVerdictArguments(['XACK', key, group], id); } diff --git a/packages/client/lib/commands/XADD.ts b/packages/client/lib/commands/XADD.ts index 7bc263b2400..e7a1b6804ff 100644 --- a/packages/client/lib/commands/XADD.ts +++ b/packages/client/lib/commands/XADD.ts @@ -1,4 +1,4 @@ -import { TuplesObject } from './generic-transformers'; +import { RedisCommandArgument, RedisCommandArguments } from '.'; export const FIRST_KEY_INDEX = 1; @@ -9,10 +9,15 @@ interface XAddOptions { strategyModifier?: '=' | '~'; threshold: number; limit?: number; - } + }; } -export function transformArguments(key: string, id: string, message: TuplesObject, options?: XAddOptions): Array { +export function transformArguments( + key: RedisCommandArgument, + id: RedisCommandArgument, + message: Record, + options?: XAddOptions +): RedisCommandArguments { const args = ['XADD', key]; if (options?.NOMKSTREAM) { diff --git a/packages/client/lib/commands/XAUTOCLAIM.ts b/packages/client/lib/commands/XAUTOCLAIM.ts index f02ccbaa2e2..9eeb04623d9 100644 --- a/packages/client/lib/commands/XAUTOCLAIM.ts +++ b/packages/client/lib/commands/XAUTOCLAIM.ts @@ -1,4 +1,5 @@ -import { StreamMessagesReply, transformReplyStreamMessages } from './generic-transformers'; +import { RedisCommandArgument, RedisCommandArguments } from '.'; +import { StreamStringsMessagesReply, transformReplyStreamStringMessages } from './generic-transformers'; export const FIRST_KEY_INDEX = 1; @@ -7,13 +8,13 @@ export interface XAutoClaimOptions { } export function transformArguments( - key: string, - group: string, - consumer: string, + key: RedisCommandArgument, + group: RedisCommandArgument, + consumer: RedisCommandArgument, minIdleTime: number, start: string, options?: XAutoClaimOptions -): Array { +): RedisCommandArguments { const args = ['XAUTOCLAIM', key, group, consumer, minIdleTime.toString(), start]; if (options?.COUNT) { @@ -25,12 +26,12 @@ export function transformArguments( interface XAutoClaimReply { nextId: string; - messages: StreamMessagesReply; + messages: StreamStringsMessagesReply; } export function transformReply(reply: [string, Array]): XAutoClaimReply { return { nextId: reply[0], - messages: transformReplyStreamMessages(reply[1]) + messages: transformReplyStreamStringMessages(reply[1]) }; } diff --git a/packages/client/lib/commands/XAUTOCLAIM_BUFFER.ts b/packages/client/lib/commands/XAUTOCLAIM_BUFFER.ts new file mode 100644 index 00000000000..dab264be873 --- /dev/null +++ b/packages/client/lib/commands/XAUTOCLAIM_BUFFER.ts @@ -0,0 +1,17 @@ +import { StreamBufferMessagesReply, transformReplyStreamBufferMessages } from './generic-transformers'; + +export { FIRST_KEY_INDEX, transformArguments } from './XAUTOCLAIM'; + +export const BUFFER_MODE = true; + +interface XAutoClaimReply { + nextId: Buffer; + messages: StreamBufferMessagesReply; +} + +export function transformReply(reply: [Buffer, Array]): XAutoClaimReply { + return { + nextId: reply[0], + messages: transformReplyStreamBufferMessages(reply[1]) + }; +} diff --git a/packages/client/lib/commands/XAUTOCLAIM_JUSTID.ts b/packages/client/lib/commands/XAUTOCLAIM_JUSTID.ts index 70c1a07f920..b18f49b6b79 100644 --- a/packages/client/lib/commands/XAUTOCLAIM_JUSTID.ts +++ b/packages/client/lib/commands/XAUTOCLAIM_JUSTID.ts @@ -1,8 +1,9 @@ +import { RedisCommandArguments } from '.'; import { transformArguments as transformXAutoClaimArguments } from './XAUTOCLAIM'; export { FIRST_KEY_INDEX } from './XAUTOCLAIM'; -export function transformArguments(...args: Parameters): Array { +export function transformArguments(...args: Parameters): RedisCommandArguments { return [ ...transformXAutoClaimArguments(...args), 'JUSTID' diff --git a/packages/client/lib/commands/XAUTOCLAIM_JUSTID_BUFFER.ts b/packages/client/lib/commands/XAUTOCLAIM_JUSTID_BUFFER.ts new file mode 100644 index 00000000000..eeb9c801e10 --- /dev/null +++ b/packages/client/lib/commands/XAUTOCLAIM_JUSTID_BUFFER.ts @@ -0,0 +1,13 @@ +export { FIRST_KEY_INDEX, transformArguments } from './XAUTOCLAIM_JUSTID'; + +interface XAutoClaimJustIdBufferReply { + nextId: Buffer; + messages: Array; +} + +export function transformReply(reply: [Buffer, Array]): XAutoClaimJustIdBufferReply { + return { + nextId: reply[0], + messages: reply[1] + }; +} diff --git a/packages/client/lib/commands/XCLAIM.ts b/packages/client/lib/commands/XCLAIM.ts index c87d1551e86..84362f917a7 100644 --- a/packages/client/lib/commands/XCLAIM.ts +++ b/packages/client/lib/commands/XCLAIM.ts @@ -1,3 +1,4 @@ +import { RedisCommandArgument, RedisCommandArguments } from '.'; import { pushVerdictArguments } from './generic-transformers'; export const FIRST_KEY_INDEX = 1; @@ -10,13 +11,13 @@ export interface XClaimOptions { } export function transformArguments( - key: string, - group: string, - consumer: string, + key: RedisCommandArgument, + group: RedisCommandArgument, + consumer: RedisCommandArgument, minIdleTime: number, - id: string | Array, + id: RedisCommandArgument | Array, options?: XClaimOptions -): Array { +): RedisCommandArguments { const args = ['XCLAIM', key, group, consumer, minIdleTime.toString()]; pushVerdictArguments(args, id); @@ -43,4 +44,4 @@ export function transformArguments( return args; } -export { transformReplyStreamMessages as transformReply } from './generic-transformers'; +export { transformReplyStreamStringMessages as transformReply } from './generic-transformers'; diff --git a/packages/client/lib/commands/XCLAIM_BUFFER.ts b/packages/client/lib/commands/XCLAIM_BUFFER.ts new file mode 100644 index 00000000000..45e960ff862 --- /dev/null +++ b/packages/client/lib/commands/XCLAIM_BUFFER.ts @@ -0,0 +1,5 @@ +export { FIRST_KEY_INDEX, transformArguments } from './XCLAIM'; + +export const BUFFER_MODE = true; + +export { transformReplyStreamBufferMessages as transformReply } from './generic-transformers'; diff --git a/packages/client/lib/commands/XCLAIM_JUSTID.ts b/packages/client/lib/commands/XCLAIM_JUSTID.ts index ab74c89420c..da2dce2d3dc 100644 --- a/packages/client/lib/commands/XCLAIM_JUSTID.ts +++ b/packages/client/lib/commands/XCLAIM_JUSTID.ts @@ -1,10 +1,11 @@ -import { transformArguments as transformArgumentsXClaim } from './XCLAIM'; +import { RedisCommandArguments } from '.'; +import { transformArguments as transformXClaimArguments } from './XCLAIM'; export { FIRST_KEY_INDEX } from './XCLAIM'; -export function transformArguments(...args: Parameters): Array { +export function transformArguments(...args: Parameters): RedisCommandArguments { return [ - ...transformArgumentsXClaim(...args), + ...transformXClaimArguments(...args), 'JUSTID' ]; } diff --git a/packages/client/lib/commands/XCLAIM_JUSTID_BUFFER.ts b/packages/client/lib/commands/XCLAIM_JUSTID_BUFFER.ts new file mode 100644 index 00000000000..9933e4a6a87 --- /dev/null +++ b/packages/client/lib/commands/XCLAIM_JUSTID_BUFFER.ts @@ -0,0 +1,5 @@ +export { FIRST_KEY_INDEX, transformArguments } from './XCLAIM_JUSTID'; + +export const BUFFER_MODE = true; + +export declare function transformReply(): Array; diff --git a/packages/client/lib/commands/XDEL.ts b/packages/client/lib/commands/XDEL.ts index 892b9a0de5b..82b30d21092 100644 --- a/packages/client/lib/commands/XDEL.ts +++ b/packages/client/lib/commands/XDEL.ts @@ -1,9 +1,12 @@ -import { RedisCommandArguments } from '.'; +import { RedisCommandArgument, RedisCommandArguments } from '.'; import { pushVerdictArguments } from './generic-transformers'; export const FIRST_KEY_INDEX = 1; -export function transformArguments(key: string, id: string | Array): RedisCommandArguments { +export function transformArguments( + key: RedisCommandArgument, + id: RedisCommandArgument | Array +): RedisCommandArguments { return pushVerdictArguments(['XDEL', key], id); } diff --git a/packages/client/lib/commands/XGROUP_CREATE.ts b/packages/client/lib/commands/XGROUP_CREATE.ts index c2d7f157615..85847029cfe 100644 --- a/packages/client/lib/commands/XGROUP_CREATE.ts +++ b/packages/client/lib/commands/XGROUP_CREATE.ts @@ -1,10 +1,17 @@ +import { RedisCommandArgument, RedisCommandArguments } from '.'; + export const FIRST_KEY_INDEX = 2; interface XGroupCreateOptions { MKSTREAM?: true; } -export function transformArguments(key: string, group: string, id: string, options?: XGroupCreateOptions): Array { +export function transformArguments( + key: RedisCommandArgument, + group: RedisCommandArgument, + id: RedisCommandArgument, + options?: XGroupCreateOptions +): RedisCommandArguments { const args = ['XGROUP', 'CREATE', key, group, id]; if (options?.MKSTREAM) { @@ -14,4 +21,4 @@ export function transformArguments(key: string, group: string, id: string, optio return args; } -export declare function transformReply(): string; +export declare function transformReply(): 'OK'; diff --git a/packages/client/lib/commands/XGROUP_CREATECONSUMER.ts b/packages/client/lib/commands/XGROUP_CREATECONSUMER.ts index f1a57e6fc42..6f1458e337e 100644 --- a/packages/client/lib/commands/XGROUP_CREATECONSUMER.ts +++ b/packages/client/lib/commands/XGROUP_CREATECONSUMER.ts @@ -1,6 +1,12 @@ +import { RedisCommandArgument, RedisCommandArguments } from '.'; + export const FIRST_KEY_INDEX = 2; -export function transformArguments(key: string, group: string, consumer: string): Array { +export function transformArguments( + key: RedisCommandArgument, + group: RedisCommandArgument, + consumer: RedisCommandArgument +): RedisCommandArguments { return ['XGROUP', 'CREATECONSUMER', key, group, consumer]; } diff --git a/packages/client/lib/commands/XGROUP_DELCONSUMER.ts b/packages/client/lib/commands/XGROUP_DELCONSUMER.ts index dc9de7bb577..4e4fc096d07 100644 --- a/packages/client/lib/commands/XGROUP_DELCONSUMER.ts +++ b/packages/client/lib/commands/XGROUP_DELCONSUMER.ts @@ -1,6 +1,12 @@ +import { RedisCommandArgument, RedisCommandArguments } from '.'; + export const FIRST_KEY_INDEX = 2; -export function transformArguments(key: string, group: string, consumer: string): Array { +export function transformArguments( + key: RedisCommandArgument, + group: RedisCommandArgument, + consumer: RedisCommandArgument +): RedisCommandArguments { return ['XGROUP', 'DELCONSUMER', key, group, consumer]; } diff --git a/packages/client/lib/commands/XGROUP_DESTROY.ts b/packages/client/lib/commands/XGROUP_DESTROY.ts index a4d67e5f4e5..3bd61b30567 100644 --- a/packages/client/lib/commands/XGROUP_DESTROY.ts +++ b/packages/client/lib/commands/XGROUP_DESTROY.ts @@ -1,6 +1,11 @@ +import { RedisCommandArgument, RedisCommandArguments } from '.'; + export const FIRST_KEY_INDEX = 2; -export function transformArguments(key: string, group: string): Array { +export function transformArguments( + key: RedisCommandArgument, + group: RedisCommandArgument +): RedisCommandArguments { return ['XGROUP', 'DESTROY', key, group]; } diff --git a/packages/client/lib/commands/XGROUP_SETID.ts b/packages/client/lib/commands/XGROUP_SETID.ts index 2d4e30d9c94..fd580d7aac6 100644 --- a/packages/client/lib/commands/XGROUP_SETID.ts +++ b/packages/client/lib/commands/XGROUP_SETID.ts @@ -1,7 +1,13 @@ +import { RedisCommandArgument, RedisCommandArguments } from '.'; + export const FIRST_KEY_INDEX = 2; -export function transformArguments(key: string, group: string, id: string): Array { +export function transformArguments( + key: RedisCommandArgument, + group: RedisCommandArgument, + id: RedisCommandArgument +): RedisCommandArguments { return ['XGROUP', 'SETID', key, group, id]; } -export declare function transformReply(): string; +export declare function transformReply(): 'OK'; diff --git a/packages/client/lib/commands/XINFO_CONSUMERS.ts b/packages/client/lib/commands/XINFO_CONSUMERS.ts index 57e60db832d..8cdcb9d1431 100644 --- a/packages/client/lib/commands/XINFO_CONSUMERS.ts +++ b/packages/client/lib/commands/XINFO_CONSUMERS.ts @@ -1,8 +1,13 @@ +import { RedisCommandArgument, RedisCommandArguments } from '.'; + export const FIRST_KEY_INDEX = 2; export const IS_READ_ONLY = true; -export function transformArguments(key: string, group: string): Array { +export function transformArguments( + key: RedisCommandArgument, + group: RedisCommandArgument +): RedisCommandArguments { return ['XINFO', 'CONSUMERS', key, group]; } diff --git a/packages/client/lib/commands/XINFO_GROUPS.ts b/packages/client/lib/commands/XINFO_GROUPS.ts index 04fb9ca39a6..2799cd72440 100644 --- a/packages/client/lib/commands/XINFO_GROUPS.ts +++ b/packages/client/lib/commands/XINFO_GROUPS.ts @@ -1,8 +1,10 @@ +import { RedisCommandArgument, RedisCommandArguments } from '.'; + export const FIRST_KEY_INDEX = 2; export const IS_READ_ONLY = true; -export function transformArguments(key: string): Array { +export function transformArguments(key: RedisCommandArgument): RedisCommandArguments { return ['XINFO', 'GROUPS', key]; } diff --git a/packages/client/lib/commands/XINFO_STREAM.ts b/packages/client/lib/commands/XINFO_STREAM.ts index 0bb44721873..c62ad1eee88 100644 --- a/packages/client/lib/commands/XINFO_STREAM.ts +++ b/packages/client/lib/commands/XINFO_STREAM.ts @@ -1,10 +1,11 @@ -import { StreamMessageReply, transformReplyTuples } from './generic-transformers'; +import { RedisCommandArgument, RedisCommandArguments } from '.'; +import { StreamStringsMessageReply, transformReplyStringTuples } from './generic-transformers'; export const FIRST_KEY_INDEX = 2; export const IS_READ_ONLY = true; -export function transformArguments(key: string): Array { +export function transformArguments(key: RedisCommandArgument): RedisCommandArguments { return ['XINFO', 'STREAM', key]; } @@ -14,8 +15,8 @@ interface XInfoStreamReply { radixTreeNodes: number; groups: number; lastGeneratedId: string; - firstEntry: StreamMessageReply | null; - lastEntry: StreamMessageReply | null; + firstEntry: StreamStringsMessageReply | null; + lastEntry: StreamStringsMessageReply | null; } export function transformReply(rawReply: Array): XInfoStreamReply { @@ -46,14 +47,14 @@ export function transformReply(rawReply: Array): XInfoStreamReply { case 'first-entry': parsedReply.firstEntry = rawReply[i + 1] ? { id: rawReply[i + 1][0], - message: transformReplyTuples(rawReply[i + 1][1]) + message: transformReplyStringTuples(rawReply[i + 1][1]) } : null; break; case 'last-entry': parsedReply.lastEntry = rawReply[i + 1] ? { id: rawReply[i + 1][0], - message: transformReplyTuples(rawReply[i + 1][1]) + message: transformReplyStringTuples(rawReply[i + 1][1]) } : null; break; } diff --git a/packages/client/lib/commands/XLEN.ts b/packages/client/lib/commands/XLEN.ts index 1cadd61952b..fda4192c8a0 100644 --- a/packages/client/lib/commands/XLEN.ts +++ b/packages/client/lib/commands/XLEN.ts @@ -1,8 +1,10 @@ +import { RedisCommandArgument, RedisCommandArguments } from '.'; + export const FIRST_KEY_INDEX = 1; export const IS_READ_ONLY = true; -export function transformArguments(key: string): Array { +export function transformArguments(key: RedisCommandArgument): RedisCommandArguments { return ['XLEN', key]; } diff --git a/packages/client/lib/commands/XPENDING.ts b/packages/client/lib/commands/XPENDING.ts index 6695ab6614c..aedd8c68853 100644 --- a/packages/client/lib/commands/XPENDING.ts +++ b/packages/client/lib/commands/XPENDING.ts @@ -1,23 +1,44 @@ +import { RedisCommandArgument, RedisCommandArguments } from '.'; + export const FIRST_KEY_INDEX = 1; export const IS_READ_ONLY = true; -export function transformArguments(key: string, group: string): Array { +export function transformArguments( + key: RedisCommandArgument, + group: RedisCommandArgument +): RedisCommandArguments { return ['XPENDING', key, group]; } +type XPendingRawReply = [ + pending: number, + firstId: string | null, + lastId: string | null, + consumers: Array<[ + name: string, + deliveriesCounter: number + ]> | null +] + interface XPendingReply { pending: number; firstId: string | null; - lastId: number | null - consumers: Array | null; + lastId: string | null + consumers: Array<{ + name: string, + deliveriesCounter: number + }> | null; } -export function transformReply(reply: [number, string | null, number | null, Array | null]): XPendingReply { +export function transformReply(reply: XPendingRawReply): XPendingReply { return { pending: reply[0], firstId: reply[1], lastId: reply[2], - consumers: reply[3] + consumers: reply[3] === null ? null : reply[3].map(([name, deliveriesCounter]) => ({ + name, + deliveriesCounter + })) }; } diff --git a/packages/client/lib/commands/XPENDING_RANGE.ts b/packages/client/lib/commands/XPENDING_RANGE.ts index d0b45f0fabb..1299f8d603e 100644 --- a/packages/client/lib/commands/XPENDING_RANGE.ts +++ b/packages/client/lib/commands/XPENDING_RANGE.ts @@ -1,3 +1,5 @@ +import { RedisCommandArgument, RedisCommandArguments } from '.'; + export const FIRST_KEY_INDEX = 1; export const IS_READ_ONLY = true; @@ -8,13 +10,13 @@ interface XPendingRangeOptions { } export function transformArguments( - key: string, - group: string, + key: RedisCommandArgument, + group: RedisCommandArgument, start: string, end: string, count: number, options?: XPendingRangeOptions -): Array { +): RedisCommandArguments { const args = ['XPENDING', key, group]; if (options?.IDLE) { @@ -30,4 +32,25 @@ export function transformArguments( return args; } -export { transformReplyStreamMessages as transformReply } from './generic-transformers'; +type XPendingRangeRawReply = Array<[ + id: number, + consumer: string, + millisecondsSinceLastDelivery: number, + deliveriesCounter: number +]>; + +type XPendingRangeReply = Array<{ + id: number; + owner: string; + millisecondsSinceLastDelivery: number; + deliveriesCounter: number; +}>; + +export function transformReply(reply: XPendingRangeRawReply): XPendingRangeReply { + return reply.map(([id, owner, millisecondsSinceLastDelivery, deliveriesCounter]) => ({ + id, + owner, + millisecondsSinceLastDelivery, + deliveriesCounter + })); +} diff --git a/packages/client/lib/commands/XRANGE.ts b/packages/client/lib/commands/XRANGE.ts index b357266c950..283e3423d22 100644 --- a/packages/client/lib/commands/XRANGE.ts +++ b/packages/client/lib/commands/XRANGE.ts @@ -1,3 +1,5 @@ +import { RedisCommandArgument, RedisCommandArguments } from '.'; + export const FIRST_KEY_INDEX = 1; export const IS_READ_ONLY = true; @@ -6,7 +8,12 @@ interface XRangeOptions { COUNT?: number; } -export function transformArguments(key: string, start: string, end: string, options?: XRangeOptions): Array { +export function transformArguments( + key: RedisCommandArgument, + start: RedisCommandArgument, + end: RedisCommandArgument, + options?: XRangeOptions +): RedisCommandArguments { const args = ['XRANGE', key, start, end]; if (options?.COUNT) { @@ -16,4 +23,4 @@ export function transformArguments(key: string, start: string, end: string, opti return args; } -export { transformReplyStreamMessages as transformReply } from './generic-transformers'; +export { transformReplyStreamStringMessages as transformReply } from './generic-transformers'; diff --git a/packages/client/lib/commands/XRANGE_BUFFER.ts b/packages/client/lib/commands/XRANGE_BUFFER.ts new file mode 100644 index 00000000000..f69c824c142 --- /dev/null +++ b/packages/client/lib/commands/XRANGE_BUFFER.ts @@ -0,0 +1,5 @@ +export { FIRST_KEY_INDEX, IS_READ_ONLY, transformArguments } from './XRANGE'; + +export const BUFFER_MODE = true; + +export { transformReplyStreamBufferMessages as transformReply } from './generic-transformers'; diff --git a/packages/client/lib/commands/XREAD.ts b/packages/client/lib/commands/XREAD.ts index 00d8aa959ec..a81ffbae599 100644 --- a/packages/client/lib/commands/XREAD.ts +++ b/packages/client/lib/commands/XREAD.ts @@ -1,12 +1,14 @@ -export const FIRST_KEY_INDEX = (streams: Array | XReadStream): string => { +import { RedisCommandArgument, RedisCommandArguments } from '.'; + +export const FIRST_KEY_INDEX = (streams: Array | XReadStream): RedisCommandArgument => { return Array.isArray(streams) ? streams[0].key : streams.key; }; export const IS_READ_ONLY = true; interface XReadStream { - key: string; - id: string; + key: RedisCommandArgument; + id: RedisCommandArgument; } interface XReadOptions { @@ -14,8 +16,11 @@ interface XReadOptions { BLOCK?: number; } -export function transformArguments(streams: Array | XReadStream, options?: XReadOptions): Array { - const args = ['XREAD']; +export function transformArguments( + streams: Array | XReadStream, + options?: XReadOptions +): RedisCommandArguments { + const args: RedisCommandArguments = ['XREAD']; if (options?.COUNT) { args.push('COUNT', options.COUNT.toString()); @@ -38,4 +43,4 @@ export function transformArguments(streams: Array | XReadStream, op return args; } -export { transformReplyStreamsMessages as transformReply } from './generic-transformers'; +export { transformReplyStreamsStringMessages as transformReply } from './generic-transformers'; diff --git a/packages/client/lib/commands/XREADGROUP.ts b/packages/client/lib/commands/XREADGROUP.ts index 6d329d377ff..5814de94eff 100644 --- a/packages/client/lib/commands/XREADGROUP.ts +++ b/packages/client/lib/commands/XREADGROUP.ts @@ -1,6 +1,8 @@ +import { RedisCommandArgument, RedisCommandArguments } from '.'; + export interface XReadGroupStream { - key: string; - id: string; + key: RedisCommandArgument; + id: RedisCommandArgument; } export interface XReadGroupOptions { @@ -10,21 +12,21 @@ export interface XReadGroupOptions { } export const FIRST_KEY_INDEX = ( - _group: string, - _consumer: string, + _group: RedisCommandArgument, + _consumer: RedisCommandArgument, streams: Array | XReadGroupStream -): string => { +): RedisCommandArgument => { return Array.isArray(streams) ? streams[0].key : streams.key; }; export const IS_READ_ONLY = true; export function transformArguments( - group: string, - consumer: string, + group: RedisCommandArgument, + consumer: RedisCommandArgument, streams: Array | XReadGroupStream, options?: XReadGroupOptions -): Array { +): RedisCommandArguments { const args = ['XREADGROUP', 'GROUP', group, consumer]; if (options?.COUNT) { @@ -52,4 +54,4 @@ export function transformArguments( return args; } -export { transformReplyStreamsMessages as transformReply } from './generic-transformers'; +export { transformReplyStreamsStringMessages as transformReply } from './generic-transformers'; diff --git a/packages/client/lib/commands/XREADGROUP_BUFFER.ts b/packages/client/lib/commands/XREADGROUP_BUFFER.ts new file mode 100644 index 00000000000..f02c8fee273 --- /dev/null +++ b/packages/client/lib/commands/XREADGROUP_BUFFER.ts @@ -0,0 +1,6 @@ + +export { FIRST_KEY_INDEX, IS_READ_ONLY, transformArguments } from './XREADGROUP'; + +export const BUFFER_MODE = true; + +export { transformReplyStreamsBufferMessages as transformReply } from './generic-transformers'; diff --git a/packages/client/lib/commands/XREAD_BUFFER.ts b/packages/client/lib/commands/XREAD_BUFFER.ts new file mode 100644 index 00000000000..c699aae8ada --- /dev/null +++ b/packages/client/lib/commands/XREAD_BUFFER.ts @@ -0,0 +1,5 @@ +export { FIRST_KEY_INDEX, IS_READ_ONLY, transformArguments } from './XREAD'; + +export const BUFFER_MODE = true; + +export { transformReplyStreamsBufferMessages as transformReply } from './generic-transformers'; diff --git a/packages/client/lib/commands/XREVRANGE.ts b/packages/client/lib/commands/XREVRANGE.ts index db48856cdcc..c61fcb7a520 100644 --- a/packages/client/lib/commands/XREVRANGE.ts +++ b/packages/client/lib/commands/XREVRANGE.ts @@ -2,6 +2,10 @@ interface XRangeRevOptions { COUNT?: number; } +export const FIRST_KEY_INDEX = 1; + +export const IS_READ_ONLY = true; + export function transformArguments(key: string, start: string, end: string, options?: XRangeRevOptions): Array { const args = ['XREVRANGE', key, start, end]; @@ -12,4 +16,4 @@ export function transformArguments(key: string, start: string, end: string, opti return args; } -export { transformReplyStreamMessages as transformReply } from './generic-transformers'; +export { transformReplyStreamStringMessages as transformReply } from './generic-transformers'; diff --git a/packages/client/lib/commands/XREVRANGE_BUFFER.ts b/packages/client/lib/commands/XREVRANGE_BUFFER.ts new file mode 100644 index 00000000000..eb45ee486ed --- /dev/null +++ b/packages/client/lib/commands/XREVRANGE_BUFFER.ts @@ -0,0 +1,5 @@ +export { FIRST_KEY_INDEX, IS_READ_ONLY, transformArguments } from './XREVRANGE'; + +export const BUFFER_MODE = true; + +export { transformReplyStreamBufferMessages as transformReply } from './generic-transformers'; diff --git a/packages/client/lib/commands/XTRIM.ts b/packages/client/lib/commands/XTRIM.ts index 520c38847b8..15b934c56ef 100644 --- a/packages/client/lib/commands/XTRIM.ts +++ b/packages/client/lib/commands/XTRIM.ts @@ -1,3 +1,5 @@ +import { RedisCommandArgument, RedisCommandArguments } from '.'; + export const FIRST_KEY_INDEX = 1; interface XTrimOptions { @@ -5,7 +7,12 @@ interface XTrimOptions { LIMIT?: number; } -export function transformArguments(key: string, strategy: 'MAXLEN' | 'MINID', threshold: number, options?: XTrimOptions): Array { +export function transformArguments( + key: RedisCommandArgument, + strategy: 'MAXLEN' | 'MINID', + threshold: number, + options?: XTrimOptions +): RedisCommandArguments { const args = ['XTRIM', key, strategy]; if (options?.strategyModifier) { diff --git a/packages/client/lib/commands/ZADD.ts b/packages/client/lib/commands/ZADD.ts index 69f01c8d534..c8b7c4002c7 100644 --- a/packages/client/lib/commands/ZADD.ts +++ b/packages/client/lib/commands/ZADD.ts @@ -1,3 +1,4 @@ +import { RedisCommandArgument, RedisCommandArguments } from '.'; import { transformArgumentNumberInfinity, ZMember } from './generic-transformers'; export const FIRST_KEY_INDEX = 1; @@ -28,7 +29,11 @@ interface INCR { type ZAddOptions = (NX | (XX & LT & GT)) & CH & INCR; -export function transformArguments(key: string, members: ZMember | Array, options?: ZAddOptions): Array { +export function transformArguments( + key: RedisCommandArgument, + members: ZMember | Array, + options?: ZAddOptions +): RedisCommandArguments { const args = ['ZADD', key]; if ((options)?.NX) { diff --git a/packages/client/lib/commands/ZDIFF_WITHSCORES.ts b/packages/client/lib/commands/ZDIFF_WITHSCORES.ts index 578927331be..961e1a73e17 100644 --- a/packages/client/lib/commands/ZDIFF_WITHSCORES.ts +++ b/packages/client/lib/commands/ZDIFF_WITHSCORES.ts @@ -10,4 +10,4 @@ export function transformArguments(...args: Parameters { ]; } -export function transformReply(reply: [string, string] | []): ZMember | null { +export function transformReply(reply: [string, string] | []): ZMember | null { if (!reply.length) return null; - + return { value: reply[0], score: transformReplyNumberInfinity(reply[1]) diff --git a/packages/client/lib/commands/ZPOPMAX_COUNT.ts b/packages/client/lib/commands/ZPOPMAX_COUNT.ts index abef32cca56..cf43cf3fe1f 100644 --- a/packages/client/lib/commands/ZPOPMAX_COUNT.ts +++ b/packages/client/lib/commands/ZPOPMAX_COUNT.ts @@ -9,4 +9,4 @@ export function transformArguments(key: string, count: number): Array { ]; } -export { transformReplySortedSetWithScores as transformReply } from './generic-transformers'; +export { transformReplySortedStringsSetWithScores as transformReply } from './generic-transformers'; diff --git a/packages/client/lib/commands/ZPOPMIN.ts b/packages/client/lib/commands/ZPOPMIN.ts index 9e1a7bdfa3d..891e56593b6 100644 --- a/packages/client/lib/commands/ZPOPMIN.ts +++ b/packages/client/lib/commands/ZPOPMIN.ts @@ -9,9 +9,9 @@ export function transformArguments(key: string): Array { ]; } -export function transformReply(reply: [string, string] | []): ZMember | null { +export function transformReply(reply: [string, string] | []): ZMember | null { if (!reply.length) return null; - + return { value: reply[0], score: transformReplyNumberInfinity(reply[1]) diff --git a/packages/client/lib/commands/ZPOPMIN_COUNT.ts b/packages/client/lib/commands/ZPOPMIN_COUNT.ts index 7d290ebfe37..e3b7adcce9f 100644 --- a/packages/client/lib/commands/ZPOPMIN_COUNT.ts +++ b/packages/client/lib/commands/ZPOPMIN_COUNT.ts @@ -9,4 +9,4 @@ export function transformArguments(key: string, count: number): Array { ]; } -export { transformReplySortedSetWithScores as transformReply } from './generic-transformers'; +export { transformReplySortedStringsSetWithScores as transformReply } from './generic-transformers'; diff --git a/packages/client/lib/commands/ZRANDMEMBER_COUNT_WITHSCORES.ts b/packages/client/lib/commands/ZRANDMEMBER_COUNT_WITHSCORES.ts index 8f49b326ad1..83ce83ec6cf 100644 --- a/packages/client/lib/commands/ZRANDMEMBER_COUNT_WITHSCORES.ts +++ b/packages/client/lib/commands/ZRANDMEMBER_COUNT_WITHSCORES.ts @@ -9,4 +9,4 @@ export function transformArguments(...args: Parameters; + members: Array>; } export function transformReply([cursor, rawMembers]: [string, Array]): ZScanReply { - const parsedMembers: Array = []; + const parsedMembers: Array> = []; for (let i = 0; i < rawMembers.length; i += 2) { parsedMembers.push({ value: rawMembers[i], diff --git a/packages/client/lib/commands/ZUNION_WITHSCORES.ts b/packages/client/lib/commands/ZUNION_WITHSCORES.ts index 3b937341921..5896c89ea1b 100644 --- a/packages/client/lib/commands/ZUNION_WITHSCORES.ts +++ b/packages/client/lib/commands/ZUNION_WITHSCORES.ts @@ -10,4 +10,4 @@ export function transformArguments(...args: Parameters { }); }); - it('transformReplyTuples', () => { + it('transformReplyStringTuples', () => { assert.deepEqual( - transformReplyTuples(['key1', 'value1', 'key2', 'value2']), + transformReplyStringTuples(['key1', 'value1', 'key2', 'value2']), Object.create(null, { key1: { value: 'value1', @@ -210,9 +214,27 @@ describe('Generic Transformers', () => { ); }); - it('transformReplyStreamMessages', () => { + it('transformReplyBufferTuples', () => { assert.deepEqual( - transformReplyStreamMessages([['0-0', ['0key', '0value']], ['1-0', ['1key', '1value']]]), + transformReplyBufferTuples([Buffer.from('key1'), Buffer.from('value1'), Buffer.from('key2'), Buffer.from('value2')]), + Object.create(null, { + key1: { + value: Buffer.from('value1'), + configurable: true, + enumerable: true + }, + key2: { + value: Buffer.from('value2'), + configurable: true, + enumerable: true + } + }) + ); + }); + + it('transformReplyStreamStringMessages', () => { + assert.deepEqual( + transformReplyStreamStringMessages([['0-0', ['0key', '0value']], ['1-0', ['1key', '1value']]]), [{ id: '0-0', message: Object.create(null, { @@ -235,17 +257,45 @@ describe('Generic Transformers', () => { ); }); - describe('transformReplyStreamsMessages', () => { + it('transformReplyStreamBufferMessages', () => { + assert.deepEqual( + transformReplyStreamBufferMessages([ + [Buffer.from('0-0'), [Buffer.from('0key'), Buffer.from('0value')]], + [Buffer.from('1-0'), [Buffer.from('1key'), Buffer.from('1value')]] + ]), + [{ + id: Buffer.from('0-0'), + message: Object.create(null, { + '0key': { + value: Buffer.from('0value'), + configurable: true, + enumerable: true + } + }) + }, { + id: Buffer.from('1-0'), + message: Object.create(null, { + '1key': { + value: Buffer.from('1value'), + configurable: true, + enumerable: true + } + }) + }] + ); + }); + + describe('transformReplyStreamsStringMessages', () => { it('null', () => { assert.equal( - transformReplyStreamsMessages(null), + transformReplyStreamsStringMessages(null), null ); }); it('with messages', () => { assert.deepEqual( - transformReplyStreamsMessages([['stream1', [['0-1', ['11key', '11value']], ['1-1', ['12key', '12value']]]], ['stream2', [['0-2', ['2key1', '2value1', '2key2', '2value2']]]]]), + transformReplyStreamsStringMessages([['stream1', [['0-1', ['11key', '11value']], ['1-1', ['12key', '12value']]]], ['stream2', [['0-2', ['2key1', '2value1', '2key2', '2value2']]]]]), [{ name: 'stream1', messages: [{ @@ -289,9 +339,77 @@ describe('Generic Transformers', () => { }); }); - it('transformReplySortedSetWithScores', () => { + describe('transformReplyStreamsBufferMessages', () => { + it('null', () => { + assert.equal( + transformReplyStreamsBufferMessages(null), + null + ); + }); + + it('with messages', () => { + assert.deepEqual( + transformReplyStreamsBufferMessages([ + [ + Buffer.from('stream1'), + [ + [Buffer.from('0-1'), [Buffer.from('11key'), Buffer.from('11value')]], + [Buffer.from('1-1'), [Buffer.from('12key'), Buffer.from('12value')]] + ] + ], + [ + Buffer.from('stream2'), + [ + [Buffer.from('0-2'), [Buffer.from('2key1'), Buffer.from('2value1'), Buffer.from('2key2'), Buffer.from('2value2')]] + ] + ] + ]), + [{ + name: Buffer.from('stream1'), + messages: [{ + id: Buffer.from('0-1'), + message: Object.create(null, { + '11key': { + value: Buffer.from('11value'), + configurable: true, + enumerable: true + } + }) + }, { + id: Buffer.from('1-1'), + message: Object.create(null, { + '12key': { + value: Buffer.from('12value'), + configurable: true, + enumerable: true + } + }) + }] + }, { + name: Buffer.from('stream2'), + messages: [{ + id: Buffer.from('0-2'), + message: Object.create(null, { + '2key1': { + value: Buffer.from('2value1'), + configurable: true, + enumerable: true + }, + '2key2': { + value: Buffer.from('2value2'), + configurable: true, + enumerable: true + } + }) + }] + }] + ); + }); + }); + + it('transformReplySortedStringsSetWithScores', () => { assert.deepEqual( - transformReplySortedSetWithScores(['member1', '0.5', 'member2', '+inf', 'member3', '-inf']), + transformReplySortedStringsSetWithScores(['member1', '0.5', 'member2', '+inf', 'member3', '-inf']), [{ value: 'member1', score: 0.5 @@ -305,6 +423,29 @@ describe('Generic Transformers', () => { ); }); + it('transformReplySortedBuffersSetWithScores', () => { + assert.deepEqual( + transformReplySortedBuffersSetWithScores([ + Buffer.from('member1'), + Buffer.from('0.5'), + Buffer.from('member2'), + Buffer.from('+inf'), + Buffer.from('member3'), + Buffer.from('-inf') + ]), + [{ + value: Buffer.from('member1'), + score: 0.5 + }, { + value: Buffer.from('member2'), + score: Infinity + }, { + value: Buffer.from('member3'), + score: -Infinity + }] + ); + }); + describe('pushGeoCountArgument', () => { it('undefined', () => { assert.deepEqual( diff --git a/packages/client/lib/commands/generic-transformers.ts b/packages/client/lib/commands/generic-transformers.ts index 3075636e756..10729132fa0 100644 --- a/packages/client/lib/commands/generic-transformers.ts +++ b/packages/client/lib/commands/generic-transformers.ts @@ -1,4 +1,4 @@ -import { RedisCommandArguments } from '.'; +import { RedisCommandArgument, RedisCommandArguments } from '.'; export function transformReplyBoolean(reply: number): boolean { return reply === 1; @@ -75,60 +75,101 @@ export function transformArgumentStringNumberInfinity(num: string | number): str return transformArgumentNumberInfinity(num); } -export interface TuplesObject { - [field: string]: string; +export function transformReplyStringTuples(reply: Array): Record { + const message = Object.create(null); + + for (let i = 0; i < reply.length; i += 2) { + message[reply[i]] = reply[i + 1]; + } + + return message; } -export function transformReplyTuples(reply: Array): TuplesObject { + +export function transformReplyBufferTuples(reply: Array): Record { const message = Object.create(null); for (let i = 0; i < reply.length; i += 2) { - message[reply[i]] = reply[i + 1]; + message[reply[i].toString()] = reply[i + 1]; } return message; } -export interface StreamMessageReply { +export interface StreamStringsMessageReply { id: string; - message: TuplesObject; + message: Record; } -export type StreamMessagesReply = Array; +export type StreamStringsMessagesReply = Array; -export function transformReplyStreamMessages(reply: Array): StreamMessagesReply { +export function transformReplyStreamStringMessages(reply: Array): StreamStringsMessagesReply { const messages = []; for (const [id, message] of reply) { messages.push({ id, - message: transformReplyTuples(message) + message: transformReplyStringTuples(message) }); } return messages; } -export type StreamsMessagesReply = Array<{ +interface StreamBufferMessageReply { + id: Buffer; + message: Record; +} + +export type StreamBufferMessagesReply = Array; + +export function transformReplyStreamBufferMessages(reply: Array): StreamBufferMessagesReply { + const messages = []; + + for (const [id, message] of reply) { + messages.push({ + id, + message: transformReplyBufferTuples(message) + }); + } + + return messages; +} + +export type StreamsStringMessagesReply = Array<{ name: string; - messages: StreamMessagesReply; + messages: StreamStringsMessagesReply; }> | null; -export function transformReplyStreamsMessages(reply: Array | null): StreamsMessagesReply | null { +export function transformReplyStreamsStringMessages(reply: Array | null): StreamsStringMessagesReply | null { if (reply === null) return null; return reply.map(([name, rawMessages]) => ({ name, - messages: transformReplyStreamMessages(rawMessages) + messages: transformReplyStreamStringMessages(rawMessages) })); } -export interface ZMember { +export type StreamsBufferMessagesReply = Array<{ + name: Buffer; + messages: StreamBufferMessagesReply; +}> | null; + +export function transformReplyStreamsBufferMessages(reply: Array | null): StreamsBufferMessagesReply | null { + if (reply === null) return null; + + return reply.map(([name, rawMessages]) => ({ + name, + messages: transformReplyStreamBufferMessages(rawMessages) + })); +} + +export interface ZMember { score: number; - value: string; + value: T; } -export function transformReplySortedSetWithScores(reply: Array): Array { +export function transformReplySortedStringsSetWithScores(reply: Array): Array> { const members = []; for (let i = 0; i < reply.length; i += 2) { @@ -141,6 +182,19 @@ export function transformReplySortedSetWithScores(reply: Array): Array): Array> { + const members = []; + + for (let i = 0; i < reply.length; i += 2) { + members.push({ + value: reply[i], + score: transformReplyNumberInfinity(reply[i + 1].toString()) + }); + } + + return members; +} + type GeoCountArgument = number | { value: number; ANY?: true @@ -314,7 +368,7 @@ export function pushStringTuplesArguments(args: Array, tuples: StringTup return args; } -export function pushVerdictArguments(args: RedisCommandArguments, value: string | Buffer | Array): RedisCommandArguments { +export function pushVerdictArguments(args: RedisCommandArguments, value: RedisCommandArgument | Array): RedisCommandArguments { if (Array.isArray(value)) { args.push(...value); } else { diff --git a/packages/client/lib/commands/index.ts b/packages/client/lib/commands/index.ts index f88d777c892..03aaf93fb17 100644 --- a/packages/client/lib/commands/index.ts +++ b/packages/client/lib/commands/index.ts @@ -2,10 +2,12 @@ import { RedisScriptConfig, SHA1 } from '../lua-script'; export type RedisCommandRawReply = string | number | Buffer | Array | null | undefined; -export type RedisCommandArguments = Array & { preserve?: unknown }; +export type RedisCommandArgument = string | Buffer; + +export type RedisCommandArguments = Array & { preserve?: unknown }; export interface RedisCommand { - FIRST_KEY_INDEX?: number | ((...args: Array) => string); + FIRST_KEY_INDEX?: number | ((...args: Array) => RedisCommandArgument); IS_READ_ONLY?: boolean; transformArguments(this: void, ...args: Array): RedisCommandArguments; BUFFER_MODE?: boolean; diff --git a/packages/client/lib/ts-declarations/cluster-key-slot.d.ts b/packages/client/lib/ts-declarations/cluster-key-slot.d.ts deleted file mode 100644 index 60421de296b..00000000000 --- a/packages/client/lib/ts-declarations/cluster-key-slot.d.ts +++ /dev/null @@ -1,3 +0,0 @@ -declare module 'cluster-key-slot' { - export default function calculateSlot(key: string | Buffer): number; -} diff --git a/packages/client/tsconfig.json b/packages/client/tsconfig.json index b029ca71a4f..5e044cbaa1e 100644 --- a/packages/client/tsconfig.json +++ b/packages/client/tsconfig.json @@ -17,10 +17,6 @@ "./lib" ], "entryPointStrategy": "expand", - "exclude": [ - "./lib/ts-declarations", - "./lib/test-utils.ts" - ], "out": "../../documentation/client" } } diff --git a/packages/json/tsconfig.json b/packages/json/tsconfig.json index bffc01081d7..367b3ef16c4 100644 --- a/packages/json/tsconfig.json +++ b/packages/json/tsconfig.json @@ -15,10 +15,6 @@ "./lib" ], "entryPointStrategy": "expand", - "exclude": [ - "./lib/test-utils.ts", - "./lib/**/*.spec.ts" - ], "out": "../../documentation/json" } } diff --git a/packages/search/lib/commands/AGGREGATE.ts b/packages/search/lib/commands/AGGREGATE.ts index 7160cee1d4f..f305fbc6cf2 100644 --- a/packages/search/lib/commands/AGGREGATE.ts +++ b/packages/search/lib/commands/AGGREGATE.ts @@ -1,5 +1,5 @@ import { RedisCommandArguments } from '@node-redis/client/dist/lib/commands'; -import { pushVerdictArgument, transformReplyTuples, TuplesObject } from '@node-redis/client/dist/lib/commands/generic-transformers'; +import { pushVerdictArgument, transformReplyStringTuples } from '@node-redis/client/dist/lib/commands/generic-transformers'; import { AggregateReply, PropertyName, pushArgumentsWithLength, pushSortByArguments, SortByProperty } from '.'; export enum AggregateSteps { @@ -125,8 +125,8 @@ export interface AggregateOptions { } export function transformArguments( - index: string, - query: string, + index: string, + query: string, options?: AggregateOptions ): RedisCommandArguments { @@ -277,10 +277,10 @@ export type AggregateRawReply = [ ]; export function transformReply(rawReply: AggregateRawReply): AggregateReply { - const results: Array = []; + const results: Array> = []; for (let i = 1; i < rawReply.length; i++) { results.push( - transformReplyTuples(rawReply[i] as Array) + transformReplyStringTuples(rawReply[i] as Array) ); } diff --git a/packages/search/lib/commands/SEARCH.ts b/packages/search/lib/commands/SEARCH.ts index c289f6fc277..3a71ec26ba6 100644 --- a/packages/search/lib/commands/SEARCH.ts +++ b/packages/search/lib/commands/SEARCH.ts @@ -1,5 +1,5 @@ import { RedisCommandArguments } from '@node-redis/client/dist/lib/commands'; -import { transformReplyTuples } from '@node-redis/client/dist/lib/commands/generic-transformers'; +import { transformReplyStringTuples } from '@node-redis/client/dist/lib/commands/generic-transformers'; import { pushSearchOptions, RedisSearchLanguages, PropertyName, SortByProperty, SearchReply } from '.'; export const FIRST_KEY_INDEX = 1; @@ -76,7 +76,7 @@ export function transformReply(reply: SearchRawReply): SearchReply { id: reply[i], value: tuples.length === 2 && tuples[0] === '$' ? JSON.parse(tuples[1]) : - transformReplyTuples(tuples) + transformReplyStringTuples(tuples) }); } diff --git a/packages/search/lib/commands/index.ts b/packages/search/lib/commands/index.ts index b175a1baeed..9b717f64a4c 100644 --- a/packages/search/lib/commands/index.ts +++ b/packages/search/lib/commands/index.ts @@ -29,7 +29,7 @@ import * as SYNDUMP from './SYNDUMP'; import * as SYNUPDATE from './SYNUPDATE'; import * as TAGVALS from './TAGVALS'; import { RedisCommandArguments } from '@node-redis/client/dist/lib/commands'; -import { pushOptionalVerdictArgument, pushVerdictArgument, TuplesObject } from '@node-redis/client/dist/lib/commands/generic-transformers'; +import { pushOptionalVerdictArgument, pushVerdictArgument } from '@node-redis/client/dist/lib/commands/generic-transformers'; import { SearchOptions } from './SEARCH'; export default { @@ -398,7 +398,7 @@ export interface SearchReply { export interface AggregateReply { total: number; - results: Array; + results: Array>; } export interface ProfileOptions { diff --git a/packages/search/tsconfig.json b/packages/search/tsconfig.json index ecbbb531ef1..f37b7dc1152 100644 --- a/packages/search/tsconfig.json +++ b/packages/search/tsconfig.json @@ -15,10 +15,6 @@ "./lib" ], "entryPointStrategy": "expand", - "exclude": [ - "./lib/test-utils.ts", - "./lib/**/*.spec.ts" - ], "out": "../../documentation/search" } } diff --git a/packages/time-series/tsconfig.json b/packages/time-series/tsconfig.json index 854fb728d3d..0c5809dcbe2 100644 --- a/packages/time-series/tsconfig.json +++ b/packages/time-series/tsconfig.json @@ -15,10 +15,6 @@ "./lib" ], "entryPointStrategy": "expand", - "exclude": [ - "./lib/test-utils.ts", - "./lib/**/*.spec.ts" - ], "out": "../../documentation/time-series" } } diff --git a/tsconfig.base.json b/tsconfig.base.json index 7df81029664..35daa1df657 100644 --- a/tsconfig.base.json +++ b/tsconfig.base.json @@ -6,9 +6,6 @@ "useDefineForClassFields": true, "esModuleInterop": false }, - "files": [ - "./packages/client/lib/ts-declarations/cluster-key-slot.d.ts" - ], "ts-node": { "files": true } From 4975eb798e791fcf7ca18c0b14380f676a819452 Mon Sep 17 00:00:00 2001 From: AdamHerrmann Date: Fri, 17 Dec 2021 16:39:19 +0100 Subject: [PATCH 1061/1748] fix defineScript types (#1788) --- packages/client/lib/lua-script.ts | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/packages/client/lib/lua-script.ts b/packages/client/lib/lua-script.ts index 3089d468d65..57de985733c 100644 --- a/packages/client/lib/lua-script.ts +++ b/packages/client/lib/lua-script.ts @@ -10,7 +10,7 @@ export interface SHA1 { SHA1: string; } -export function defineScript(script: RedisScriptConfig): typeof script & SHA1 { +export function defineScript(script: TScript): TScript & SHA1 { return { ...script, SHA1: scriptSha1(script.SCRIPT) From 2733e225aea57c0cbc9fc172a92b978c57fe5372 Mon Sep 17 00:00:00 2001 From: Leibale Eidelman Date: Sat, 18 Dec 2021 21:41:59 -0500 Subject: [PATCH 1062/1748] fix TLS self-signed certificate example --- docs/client-configuration.md | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/docs/client-configuration.md b/docs/client-configuration.md index 32ecbd9f0e3..0600d8e5763 100644 --- a/docs/client-configuration.md +++ b/docs/client-configuration.md @@ -57,8 +57,8 @@ createClient({ createClient({ socket: { tls: true, - rejectUnauthorized: true, + rejectUnauthorized: false, cert: '...' } }); -``` \ No newline at end of file +``` From a0de7967f98068790cd09c909bbbb7e7d6deefe8 Mon Sep 17 00:00:00 2001 From: leibale Date: Mon, 20 Dec 2021 14:47:51 -0500 Subject: [PATCH 1063/1748] buffers, buffers everywhere... --- README.md | 10 + package-lock.json | 1855 +++++++++-------- package.json | 12 +- packages/client/lib/client/commands-queue.ts | 25 +- packages/client/lib/client/index.spec.ts | 16 +- packages/client/lib/client/index.ts | 52 +- packages/client/lib/cluster/commands.ts | 48 - packages/client/lib/cluster/index.ts | 14 +- packages/client/lib/commands/ACL_CAT.ts | 8 +- packages/client/lib/commands/ACL_DELUSER.ts | 8 +- packages/client/lib/commands/ACL_GENPASS.ts | 6 +- packages/client/lib/commands/ACL_GETUSER.ts | 34 +- packages/client/lib/commands/ACL_LIST.ts | 6 +- packages/client/lib/commands/ACL_LOAD.ts | 6 +- packages/client/lib/commands/ACL_LOG.ts | 40 +- packages/client/lib/commands/ACL_LOG_RESET.ts | 6 +- packages/client/lib/commands/ACL_SAVE.ts | 6 +- packages/client/lib/commands/ACL_SETUSER.ts | 9 +- packages/client/lib/commands/ACL_USERS.ts | 6 +- packages/client/lib/commands/ACL_WHOAMI.ts | 6 +- packages/client/lib/commands/ASKING.ts | 6 +- packages/client/lib/commands/AUTH.ts | 10 +- packages/client/lib/commands/BGREWRITEAOF.ts | 6 +- packages/client/lib/commands/BGSAVE.ts | 6 +- packages/client/lib/commands/BITCOUNT.ts | 7 +- packages/client/lib/commands/BITOP.ts | 8 +- packages/client/lib/commands/BITPOS.ts | 8 +- packages/client/lib/commands/BLMOVE.ts | 2 +- packages/client/lib/commands/BLMOVE_BUFFER.ts | 5 - packages/client/lib/commands/BLPOP.ts | 8 +- packages/client/lib/commands/BLPOP_BUFFER.ts | 17 - packages/client/lib/commands/BRPOP.ts | 7 +- packages/client/lib/commands/BRPOPLPUSH.ts | 2 +- .../client/lib/commands/BRPOPLPUSH_BUFFER.ts | 5 - packages/client/lib/commands/BRPOP_BUFFER.ts | 3 - packages/client/lib/commands/BZPOPMAX.ts | 12 +- packages/client/lib/commands/BZPOPMIN.ts | 25 +- .../client/lib/commands/CLIENT_CACHING.ts | 2 +- .../client/lib/commands/CLIENT_SETNAME.ts | 6 +- .../client/lib/commands/COMMAND_GETKEYS.ts | 6 +- packages/client/lib/commands/CONFIG_GET.ts | 2 +- packages/client/lib/commands/COPY.ts | 10 +- packages/client/lib/commands/DECR.ts | 4 +- packages/client/lib/commands/DECRBY.ts | 7 +- packages/client/lib/commands/DEL.ts | 6 +- packages/client/lib/commands/DISCARD.ts | 4 +- packages/client/lib/commands/DUMP.ts | 6 +- packages/client/lib/commands/ECHO.ts | 6 +- packages/client/lib/commands/EXISTS.ts | 8 +- packages/client/lib/commands/EXPIRE.ts | 11 +- packages/client/lib/commands/EXPIREAT.ts | 10 +- packages/client/lib/commands/GEOADD.ts | 8 +- packages/client/lib/commands/GEODIST.ts | 11 +- packages/client/lib/commands/GEOHASH.ts | 9 +- packages/client/lib/commands/GEOPOS.ts | 15 +- packages/client/lib/commands/GEOSEARCH.ts | 7 +- .../client/lib/commands/GEOSEARCHSTORE.ts | 7 +- .../client/lib/commands/GEOSEARCH_WITH.ts | 4 +- packages/client/lib/commands/GET.spec.ts | 7 + packages/client/lib/commands/GET.ts | 2 +- packages/client/lib/commands/GETBIT.ts | 6 +- packages/client/lib/commands/GETDEL.ts | 6 +- packages/client/lib/commands/GETEX.ts | 9 +- packages/client/lib/commands/GETRANGE.ts | 10 +- packages/client/lib/commands/GETSET.ts | 9 +- .../client/lib/commands/GET_BUFFER.spec.ts | 22 - packages/client/lib/commands/GET_BUFFER.ts | 5 - packages/client/lib/commands/HDEL.ts | 7 +- packages/client/lib/commands/HELLO.ts | 25 +- packages/client/lib/commands/HEXISTS.ts | 9 +- packages/client/lib/commands/HGET.ts | 2 +- packages/client/lib/commands/HGETALL.ts | 6 +- .../client/lib/commands/HGETALL_BUFFER.ts | 5 - packages/client/lib/commands/HGET_BUFFER.ts | 5 - packages/client/lib/commands/HINCRBY.ts | 8 +- packages/client/lib/commands/HINCRBYFLOAT.ts | 8 +- packages/client/lib/commands/HKEYS.ts | 6 +- packages/client/lib/commands/HLEN.ts | 4 +- packages/client/lib/commands/HMGET.ts | 9 +- packages/client/lib/commands/HRANDFIELD.ts | 6 +- .../client/lib/commands/HRANDFIELD_COUNT.ts | 8 +- .../commands/HRANDFIELD_COUNT_WITHVALUES.ts | 8 +- .../HRANDFIELD_COUNT_WITHVALUES_BUFFER.ts | 5 - packages/client/lib/commands/HSCAN.ts | 15 +- packages/client/lib/commands/HSET.ts | 2 +- packages/client/lib/commands/HSETNX.ts | 10 +- packages/client/lib/commands/HSTRLEN.ts | 7 +- packages/client/lib/commands/HVALS.ts | 6 +- packages/client/lib/commands/INCR.ts | 4 +- packages/client/lib/commands/INCRBY.ts | 7 +- packages/client/lib/commands/INCRBYFLOAT.ts | 9 +- packages/client/lib/commands/KEYS.ts | 8 +- packages/client/lib/commands/LINDEX.ts | 8 +- packages/client/lib/commands/LINSERT.ts | 10 +- packages/client/lib/commands/LLEN.ts | 4 +- packages/client/lib/commands/LMOVE.ts | 10 +- packages/client/lib/commands/LOLWUT.ts | 4 +- packages/client/lib/commands/LPOP.ts | 6 +- packages/client/lib/commands/LPOP_COUNT.ts | 9 +- packages/client/lib/commands/LPOS.ts | 8 +- packages/client/lib/commands/LPOS_COUNT.ts | 8 +- packages/client/lib/commands/LPUSH.ts | 7 +- packages/client/lib/commands/LPUSHX.ts | 7 +- packages/client/lib/commands/LRANGE.ts | 10 +- packages/client/lib/commands/LREM.ts | 8 +- packages/client/lib/commands/LSET.ts | 10 +- packages/client/lib/commands/LTRIM.ts | 10 +- packages/client/lib/commands/MGET.ts | 8 +- packages/client/lib/commands/MIGRATE.ts | 17 +- packages/client/lib/commands/MOVE.ts | 2 +- packages/client/lib/commands/MSET.ts | 13 +- packages/client/lib/commands/MSETNX.ts | 9 +- packages/client/lib/commands/PERSIST.ts | 6 +- packages/client/lib/commands/PEXPIRE.ts | 9 +- packages/client/lib/commands/PEXPIREAT.ts | 8 +- packages/client/lib/commands/PFADD.ts | 9 +- packages/client/lib/commands/PFCOUNT.ts | 6 +- packages/client/lib/commands/PING.spec.ts | 22 +- packages/client/lib/commands/PING.ts | 4 +- packages/client/lib/commands/PSETEX.spec.ts | 1 + packages/client/lib/commands/PSETEX.ts | 10 +- packages/client/lib/commands/PTTL.ts | 4 +- packages/client/lib/commands/RANDOMKEY.ts | 8 +- packages/client/lib/commands/RENAME.ts | 9 +- packages/client/lib/commands/RENAMENX.ts | 9 +- packages/client/lib/commands/RPOP.ts | 6 +- packages/client/lib/commands/RPOPLPUSH.ts | 7 +- packages/client/lib/commands/RPOP_COUNT.ts | 9 +- packages/client/lib/commands/RPUSH.ts | 7 +- packages/client/lib/commands/RPUSHX.ts | 7 +- packages/client/lib/commands/SADD.ts | 7 +- packages/client/lib/commands/SAVE.ts | 4 +- packages/client/lib/commands/SCAN.ts | 14 +- packages/client/lib/commands/SCRIPT_EXISTS.ts | 2 +- packages/client/lib/commands/SDIFF.ts | 10 +- packages/client/lib/commands/SDIFFSTORE.ts | 7 +- packages/client/lib/commands/SET.ts | 4 +- packages/client/lib/commands/SETBIT.ts | 8 +- packages/client/lib/commands/SETEX.ts | 4 +- packages/client/lib/commands/SETNX.ts | 9 +- packages/client/lib/commands/SETRANGE.ts | 8 +- packages/client/lib/commands/SINTER.ts | 10 +- packages/client/lib/commands/SINTERSTORE.ts | 9 +- packages/client/lib/commands/SISMEMBER.ts | 9 +- packages/client/lib/commands/SMEMBERS.ts | 6 +- packages/client/lib/commands/SMISMEMBER.ts | 9 +- packages/client/lib/commands/SMOVE.ts | 10 +- packages/client/lib/commands/SPOP.ts | 9 +- packages/client/lib/commands/SRANDMEMBER.ts | 6 +- .../client/lib/commands/SRANDMEMBER_COUNT.ts | 8 +- packages/client/lib/commands/SREM.ts | 7 +- packages/client/lib/commands/SSCAN.ts | 13 +- packages/client/lib/commands/STRLEN.ts | 4 +- packages/client/lib/commands/SUNION.ts | 8 +- packages/client/lib/commands/SUNIONSTORE.ts | 7 +- packages/client/lib/commands/TOUCH.ts | 6 +- packages/client/lib/commands/TTL.ts | 4 +- packages/client/lib/commands/TYPE.ts | 6 +- packages/client/lib/commands/UNLINK.ts | 6 +- packages/client/lib/commands/XAUTOCLAIM.ts | 12 +- .../client/lib/commands/XAUTOCLAIM_BUFFER.ts | 17 - .../client/lib/commands/XAUTOCLAIM_JUSTID.ts | 10 +- .../lib/commands/XAUTOCLAIM_JUSTID_BUFFER.ts | 13 - packages/client/lib/commands/XCLAIM.ts | 2 +- packages/client/lib/commands/XCLAIM_BUFFER.ts | 5 - packages/client/lib/commands/XCLAIM_JUSTID.ts | 4 +- .../lib/commands/XCLAIM_JUSTID_BUFFER.ts | 5 - packages/client/lib/commands/XGROUP_CREATE.ts | 2 +- .../lib/commands/XGROUP_CREATECONSUMER.ts | 2 +- .../client/lib/commands/XGROUP_DESTROY.ts | 2 +- packages/client/lib/commands/XGROUP_SETID.ts | 2 +- .../client/lib/commands/XINFO_CONSUMERS.ts | 2 +- packages/client/lib/commands/XINFO_GROUPS.ts | 4 +- packages/client/lib/commands/XINFO_STREAM.ts | 12 +- packages/client/lib/commands/XPENDING.ts | 12 +- .../client/lib/commands/XPENDING_RANGE.ts | 6 +- packages/client/lib/commands/XRANGE.ts | 2 +- packages/client/lib/commands/XRANGE_BUFFER.ts | 5 - packages/client/lib/commands/XREAD.ts | 2 +- packages/client/lib/commands/XREADGROUP.ts | 2 +- .../client/lib/commands/XREADGROUP_BUFFER.ts | 6 - packages/client/lib/commands/XREAD_BUFFER.ts | 5 - packages/client/lib/commands/XREVRANGE.ts | 17 +- .../client/lib/commands/XREVRANGE_BUFFER.ts | 5 - packages/client/lib/commands/ZADD.ts | 6 +- packages/client/lib/commands/ZCARD.ts | 4 +- packages/client/lib/commands/ZCOUNT.ts | 13 +- packages/client/lib/commands/ZDIFF.ts | 8 +- packages/client/lib/commands/ZDIFFSTORE.ts | 7 +- .../client/lib/commands/ZDIFF_WITHSCORES.ts | 2 +- packages/client/lib/commands/ZINCRBY.ts | 13 +- packages/client/lib/commands/ZINTER.ts | 9 +- packages/client/lib/commands/ZINTERSTORE.ts | 8 +- .../client/lib/commands/ZINTER_WITHSCORES.ts | 2 +- packages/client/lib/commands/ZLEXCOUNT.ts | 8 +- packages/client/lib/commands/ZMSCORE.ts | 9 +- packages/client/lib/commands/ZPOPMAX.ts | 13 +- packages/client/lib/commands/ZPOPMAX_COUNT.ts | 8 +- packages/client/lib/commands/ZPOPMIN.ts | 13 +- packages/client/lib/commands/ZPOPMIN_COUNT.ts | 8 +- packages/client/lib/commands/ZRANDMEMBER.ts | 6 +- .../client/lib/commands/ZRANDMEMBER_COUNT.ts | 8 +- .../commands/ZRANDMEMBER_COUNT_WITHSCORES.ts | 5 +- packages/client/lib/commands/ZRANGE.ts | 16 +- packages/client/lib/commands/ZRANGEBYLEX.ts | 16 +- packages/client/lib/commands/ZRANGEBYSCORE.ts | 16 +- .../lib/commands/ZRANGEBYSCORE_WITHSCORES.ts | 10 +- packages/client/lib/commands/ZRANGESTORE.ts | 17 +- .../client/lib/commands/ZRANGE_WITHSCORES.ts | 5 +- packages/client/lib/commands/ZRANK.ts | 7 +- packages/client/lib/commands/ZREM.ts | 7 +- .../client/lib/commands/ZREMRANGEBYLEX.ts | 16 +- .../client/lib/commands/ZREMRANGEBYRANK.ts | 8 +- .../client/lib/commands/ZREMRANGEBYSCORE.ts | 16 +- packages/client/lib/commands/ZREVRANK.ts | 7 +- packages/client/lib/commands/ZSCAN.ts | 19 +- packages/client/lib/commands/ZSCORE.ts | 9 +- packages/client/lib/commands/ZUNION.ts | 9 +- packages/client/lib/commands/ZUNIONSTORE.ts | 8 +- .../client/lib/commands/ZUNION_WITHSCORES.ts | 2 +- .../lib/commands/generic-transformers.spec.ts | 260 +-- .../lib/commands/generic-transformers.ts | 175 +- packages/client/lib/commands/index.ts | 9 +- packages/client/lib/lua-script.ts | 2 +- packages/client/package.json | 13 +- packages/json/lib/commands/RESP.ts | 2 +- packages/json/lib/commands/index.ts | 11 +- packages/json/package.json | 7 +- packages/search/lib/commands/AGGREGATE.ts | 17 +- packages/search/lib/commands/SEARCH.ts | 4 +- packages/search/lib/commands/SUGDEL.ts | 2 +- packages/search/lib/commands/index.ts | 8 +- packages/search/package.json | 7 +- packages/test-utils/package.json | 4 +- .../time-series/lib/commands/CREATERULE.ts | 2 +- .../time-series/lib/commands/DELETERULE.ts | 2 +- packages/time-series/package.json | 7 +- 237 files changed, 2322 insertions(+), 1951 deletions(-) delete mode 100644 packages/client/lib/commands/BLMOVE_BUFFER.ts delete mode 100644 packages/client/lib/commands/BLPOP_BUFFER.ts delete mode 100644 packages/client/lib/commands/BRPOPLPUSH_BUFFER.ts delete mode 100644 packages/client/lib/commands/BRPOP_BUFFER.ts delete mode 100644 packages/client/lib/commands/GET_BUFFER.spec.ts delete mode 100644 packages/client/lib/commands/GET_BUFFER.ts delete mode 100644 packages/client/lib/commands/HGETALL_BUFFER.ts delete mode 100644 packages/client/lib/commands/HGET_BUFFER.ts delete mode 100644 packages/client/lib/commands/HRANDFIELD_COUNT_WITHVALUES_BUFFER.ts delete mode 100644 packages/client/lib/commands/XAUTOCLAIM_BUFFER.ts delete mode 100644 packages/client/lib/commands/XAUTOCLAIM_JUSTID_BUFFER.ts delete mode 100644 packages/client/lib/commands/XCLAIM_BUFFER.ts delete mode 100644 packages/client/lib/commands/XCLAIM_JUSTID_BUFFER.ts delete mode 100644 packages/client/lib/commands/XRANGE_BUFFER.ts delete mode 100644 packages/client/lib/commands/XREADGROUP_BUFFER.ts delete mode 100644 packages/client/lib/commands/XREAD_BUFFER.ts delete mode 100644 packages/client/lib/commands/XREVRANGE_BUFFER.ts diff --git a/README.md b/README.md index 40ee294e4a6..b38aadf036d 100644 --- a/README.md +++ b/README.md @@ -75,6 +75,16 @@ await client.hGetAll('key'); // { field1: 'value1', field2: 'value2' } await client.hVals('key'); // ['value1', 'value2'] ``` +`Buffer`s are supported as well: + +```typescript +await client.hSet('key', 'field', Buffer.from('value')); // 'OK' +await client.hGetAll( + commandOptions({ returnBuffers: true }), + 'key' +); // { field: } +``` + ### Unsupported Redis Commands If you want to run commands and/or use arguments that Node Redis doesn't know about (yet!) use `.sendCommand()`: diff --git a/package-lock.json b/package-lock.json index 2b0acfb5622..036227ba094 100644 --- a/package-lock.json +++ b/package-lock.json @@ -21,11 +21,11 @@ "@tsconfig/node12": "^1.0.9", "gh-pages": "^3.2.3", "release-it": "^14.11.8", - "typedoc": "^0.22.10", - "typescript": "^4.5.3" + "typescript": "^4.5.4" }, "engines": { - "npm": ">=7" + "npm": ">=7", + "typescript": ">=4" } }, "node_modules/@babel/code-frame": { @@ -79,6 +79,15 @@ "url": "https://opencollective.com/babel" } }, + "node_modules/@babel/core/node_modules/semver": { + "version": "6.3.0", + "resolved": "https://registry.npmjs.org/semver/-/semver-6.3.0.tgz", + "integrity": "sha512-b39TBaTSfV6yBrapU89p5fKekE2m/NwnDocOVruQFS1/veMgdzuPcnOM34M6CwxW8jH/lxEa5rBoDeUwu5HHTw==", + "dev": true, + "bin": { + "semver": "bin/semver.js" + } + }, "node_modules/@babel/generator": { "version": "7.16.5", "resolved": "https://registry.npmjs.org/@babel/generator/-/generator-7.16.5.tgz", @@ -111,6 +120,15 @@ "@babel/core": "^7.0.0" } }, + "node_modules/@babel/helper-compilation-targets/node_modules/semver": { + "version": "6.3.0", + "resolved": "https://registry.npmjs.org/semver/-/semver-6.3.0.tgz", + "integrity": "sha512-b39TBaTSfV6yBrapU89p5fKekE2m/NwnDocOVruQFS1/veMgdzuPcnOM34M6CwxW8jH/lxEa5rBoDeUwu5HHTw==", + "dev": true, + "bin": { + "semver": "bin/semver.js" + } + }, "node_modules/@babel/helper-environment-visitor": { "version": "7.16.5", "resolved": "https://registry.npmjs.org/@babel/helper-environment-visitor/-/helper-environment-visitor-7.16.5.tgz", @@ -303,6 +321,15 @@ "integrity": "sha1-p9BVi9icQveV3UIyj3QIMcpTvCU=", "dev": true }, + "node_modules/@babel/highlight/node_modules/escape-string-regexp": { + "version": "1.0.5", + "resolved": "https://registry.npmjs.org/escape-string-regexp/-/escape-string-regexp-1.0.5.tgz", + "integrity": "sha1-G2HAViGQqN/2rjuyzwIAyhMLhtQ=", + "dev": true, + "engines": { + "node": ">=0.8.0" + } + }, "node_modules/@babel/highlight/node_modules/has-flag": { "version": "3.0.0", "resolved": "https://registry.npmjs.org/has-flag/-/has-flag-3.0.0.tgz", @@ -443,18 +470,6 @@ "node": ">= 4" } }, - "node_modules/@eslint/eslintrc/node_modules/strip-json-comments": { - "version": "3.1.1", - "resolved": "https://registry.npmjs.org/strip-json-comments/-/strip-json-comments-3.1.1.tgz", - "integrity": "sha512-6fPc+R4ihwqP6N/aIv2f1gMH8lOVtWQHoqC4yK6oSDVVocumAsfCqjkXnqiYMhmMwS/mEHLp7Vehlt3ql6lEig==", - "dev": true, - "engines": { - "node": ">=8" - }, - "funding": { - "url": "https://github.com/sponsors/sindresorhus" - } - }, "node_modules/@humanwhocodes/config-array": { "version": "0.9.2", "resolved": "https://registry.npmjs.org/@humanwhocodes/config-array/-/config-array-0.9.2.tgz", @@ -506,13 +521,17 @@ "sprintf-js": "~1.0.2" } }, - "node_modules/@istanbuljs/load-nyc-config/node_modules/camelcase": { - "version": "5.3.1", - "resolved": "https://registry.npmjs.org/camelcase/-/camelcase-5.3.1.tgz", - "integrity": "sha512-L28STB170nwWS63UjtlEOE3dldQApaJXZkOI1uMFfzf3rRuPegHaHesyee+YxQ+W6SvRDQV6UrdOdRiR153wJg==", + "node_modules/@istanbuljs/load-nyc-config/node_modules/find-up": { + "version": "4.1.0", + "resolved": "https://registry.npmjs.org/find-up/-/find-up-4.1.0.tgz", + "integrity": "sha512-PpOwAdQ/YlXQ2vj8a3h8IipDuYRi3wceVQQGYWxNINccq40Anw7BlsEXCMbt1Zt+OLA6Fq9suIpIWD0OsnISlw==", "dev": true, + "dependencies": { + "locate-path": "^5.0.0", + "path-exists": "^4.0.0" + }, "engines": { - "node": ">=6" + "node": ">=8" } }, "node_modules/@istanbuljs/load-nyc-config/node_modules/js-yaml": { @@ -528,6 +547,45 @@ "js-yaml": "bin/js-yaml.js" } }, + "node_modules/@istanbuljs/load-nyc-config/node_modules/locate-path": { + "version": "5.0.0", + "resolved": "https://registry.npmjs.org/locate-path/-/locate-path-5.0.0.tgz", + "integrity": "sha512-t7hw9pI+WvuwNJXwk5zVHpyhIqzg2qTlklJOf0mVxGSbe3Fp2VieZcduNYjaLDoy6p9uGpQEGWG87WpMKlNq8g==", + "dev": true, + "dependencies": { + "p-locate": "^4.1.0" + }, + "engines": { + "node": ">=8" + } + }, + "node_modules/@istanbuljs/load-nyc-config/node_modules/p-limit": { + "version": "2.3.0", + "resolved": "https://registry.npmjs.org/p-limit/-/p-limit-2.3.0.tgz", + "integrity": "sha512-//88mFWSJx8lxCzwdAABTJL2MyWB12+eIY7MDL2SqLmAkeKU9qxRvWuSyTjm3FUmpBEMuFfckAIqEaVGUDxb6w==", + "dev": true, + "dependencies": { + "p-try": "^2.0.0" + }, + "engines": { + "node": ">=6" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/@istanbuljs/load-nyc-config/node_modules/p-locate": { + "version": "4.1.0", + "resolved": "https://registry.npmjs.org/p-locate/-/p-locate-4.1.0.tgz", + "integrity": "sha512-R79ZZ/0wAxKGu3oYMlz8jy/kbhsNrS7SKZ7PxEHBgJ5+F2mtFW2fK2cOtBh1cHYkQsbzFV7I+EoRKe6Yt0oK7A==", + "dev": true, + "dependencies": { + "p-limit": "^2.2.0" + }, + "engines": { + "node": ">=8" + } + }, "node_modules/@istanbuljs/load-nyc-config/node_modules/resolve-from": { "version": "5.0.0", "resolved": "https://registry.npmjs.org/resolve-from/-/resolve-from-5.0.0.tgz", @@ -871,9 +929,9 @@ "dev": true }, "node_modules/@types/node": { - "version": "16.11.13", - "resolved": "https://registry.npmjs.org/@types/node/-/node-16.11.13.tgz", - "integrity": "sha512-eUXZzHLHoZqj1frtUetNkUetYoJ6X55UmrVnFD4DMhVeAmwLjniZhtBmsRiemQh4uq4G3vUra/Ws/hs9vEvL3Q==", + "version": "17.0.1", + "resolved": "https://registry.npmjs.org/@types/node/-/node-17.0.1.tgz", + "integrity": "sha512-NXKvBVUzIbs6ylBwmOwHFkZS2EXCcjnqr8ZCRNaXBkHAf+3mn/rPcJxwrzuc6movh8fxQAsUUfYklJ/EG+hZqQ==", "dev": true }, "node_modules/@types/parse-json": { @@ -938,13 +996,13 @@ "dev": true }, "node_modules/@typescript-eslint/eslint-plugin": { - "version": "5.7.0", - "resolved": "https://registry.npmjs.org/@typescript-eslint/eslint-plugin/-/eslint-plugin-5.7.0.tgz", - "integrity": "sha512-8RTGBpNn5a9M628wBPrCbJ+v3YTEOE2qeZb7TDkGKTDXSj36KGRg92SpFFaR/0S3rSXQxM0Og/kV9EyadsYSBg==", + "version": "5.8.0", + "resolved": "https://registry.npmjs.org/@typescript-eslint/eslint-plugin/-/eslint-plugin-5.8.0.tgz", + "integrity": "sha512-spu1UW7QuBn0nJ6+psnfCc3iVoQAifjKORgBngKOmC8U/1tbe2YJMzYQqDGYB4JCss7L8+RM2kKLb1B1Aw9BNA==", "dev": true, "dependencies": { - "@typescript-eslint/experimental-utils": "5.7.0", - "@typescript-eslint/scope-manager": "5.7.0", + "@typescript-eslint/experimental-utils": "5.8.0", + "@typescript-eslint/scope-manager": "5.8.0", "debug": "^4.3.2", "functional-red-black-tree": "^1.0.1", "ignore": "^5.1.8", @@ -969,31 +1027,16 @@ } } }, - "node_modules/@typescript-eslint/eslint-plugin/node_modules/semver": { - "version": "7.3.5", - "resolved": "https://registry.npmjs.org/semver/-/semver-7.3.5.tgz", - "integrity": "sha512-PoeGJYh8HK4BTO/a9Tf6ZG3veo/A7ZVsYrSA6J8ny9nb3B1VrpkuN+z9OE5wfE5p6H4LchYZsegiQgbJD94ZFQ==", - "dev": true, - "dependencies": { - "lru-cache": "^6.0.0" - }, - "bin": { - "semver": "bin/semver.js" - }, - "engines": { - "node": ">=10" - } - }, "node_modules/@typescript-eslint/experimental-utils": { - "version": "5.7.0", - "resolved": "https://registry.npmjs.org/@typescript-eslint/experimental-utils/-/experimental-utils-5.7.0.tgz", - "integrity": "sha512-u57eZ5FbEpzN5kSjmVrSesovWslH2ZyNPnaXQMXWgH57d5+EVHEt76W75vVuI9qKZ5BMDKNfRN+pxcPEjQjb2A==", + "version": "5.8.0", + "resolved": "https://registry.npmjs.org/@typescript-eslint/experimental-utils/-/experimental-utils-5.8.0.tgz", + "integrity": "sha512-KN5FvNH71bhZ8fKtL+lhW7bjm7cxs1nt+hrDZWIqb6ViCffQcWyLunGrgvISgkRojIDcXIsH+xlFfI4RCDA0xA==", "dev": true, "dependencies": { "@types/json-schema": "^7.0.9", - "@typescript-eslint/scope-manager": "5.7.0", - "@typescript-eslint/types": "5.7.0", - "@typescript-eslint/typescript-estree": "5.7.0", + "@typescript-eslint/scope-manager": "5.8.0", + "@typescript-eslint/types": "5.8.0", + "@typescript-eslint/typescript-estree": "5.8.0", "eslint-scope": "^5.1.1", "eslint-utils": "^3.0.0" }, @@ -1005,18 +1048,18 @@ "url": "https://opencollective.com/typescript-eslint" }, "peerDependencies": { - "eslint": "*" + "eslint": "^6.0.0 || ^7.0.0 || ^8.0.0" } }, "node_modules/@typescript-eslint/parser": { - "version": "5.7.0", - "resolved": "https://registry.npmjs.org/@typescript-eslint/parser/-/parser-5.7.0.tgz", - "integrity": "sha512-m/gWCCcS4jXw6vkrPQ1BjZ1vomP01PArgzvauBqzsoZ3urLbsRChexB8/YV8z9HwE3qlJM35FxfKZ1nfP/4x8g==", + "version": "5.8.0", + "resolved": "https://registry.npmjs.org/@typescript-eslint/parser/-/parser-5.8.0.tgz", + "integrity": "sha512-Gleacp/ZhRtJRYs5/T8KQR3pAQjQI89Dn/k+OzyCKOsLiZH2/Vh60cFBTnFsHNI6WAD+lNUo/xGZ4NeA5u0Ipw==", "dev": true, "dependencies": { - "@typescript-eslint/scope-manager": "5.7.0", - "@typescript-eslint/types": "5.7.0", - "@typescript-eslint/typescript-estree": "5.7.0", + "@typescript-eslint/scope-manager": "5.8.0", + "@typescript-eslint/types": "5.8.0", + "@typescript-eslint/typescript-estree": "5.8.0", "debug": "^4.3.2" }, "engines": { @@ -1036,13 +1079,13 @@ } }, "node_modules/@typescript-eslint/scope-manager": { - "version": "5.7.0", - "resolved": "https://registry.npmjs.org/@typescript-eslint/scope-manager/-/scope-manager-5.7.0.tgz", - "integrity": "sha512-7mxR520DGq5F7sSSgM0HSSMJ+TFUymOeFRMfUfGFAVBv8BR+Jv1vHgAouYUvWRZeszVBJlLcc9fDdktxb5kmxA==", + "version": "5.8.0", + "resolved": "https://registry.npmjs.org/@typescript-eslint/scope-manager/-/scope-manager-5.8.0.tgz", + "integrity": "sha512-x82CYJsLOjPCDuFFEbS6e7K1QEWj7u5Wk1alw8A+gnJiYwNnDJk0ib6PCegbaPMjrfBvFKa7SxE3EOnnIQz2Gg==", "dev": true, "dependencies": { - "@typescript-eslint/types": "5.7.0", - "@typescript-eslint/visitor-keys": "5.7.0" + "@typescript-eslint/types": "5.8.0", + "@typescript-eslint/visitor-keys": "5.8.0" }, "engines": { "node": "^12.22.0 || ^14.17.0 || >=16.0.0" @@ -1053,9 +1096,9 @@ } }, "node_modules/@typescript-eslint/types": { - "version": "5.7.0", - "resolved": "https://registry.npmjs.org/@typescript-eslint/types/-/types-5.7.0.tgz", - "integrity": "sha512-5AeYIF5p2kAneIpnLFve8g50VyAjq7udM7ApZZ9JYjdPjkz0LvODfuSHIDUVnIuUoxafoWzpFyU7Sqbxgi79mA==", + "version": "5.8.0", + "resolved": "https://registry.npmjs.org/@typescript-eslint/types/-/types-5.8.0.tgz", + "integrity": "sha512-LdCYOqeqZWqCMOmwFnum6YfW9F3nKuxJiR84CdIRN5nfHJ7gyvGpXWqL/AaW0k3Po0+wm93ARAsOdzlZDPCcXg==", "dev": true, "engines": { "node": "^12.22.0 || ^14.17.0 || >=16.0.0" @@ -1066,13 +1109,13 @@ } }, "node_modules/@typescript-eslint/typescript-estree": { - "version": "5.7.0", - "resolved": "https://registry.npmjs.org/@typescript-eslint/typescript-estree/-/typescript-estree-5.7.0.tgz", - "integrity": "sha512-aO1Ql+izMrTnPj5aFFlEJkpD4jRqC4Gwhygu2oHK2wfVQpmOPbyDSveJ+r/NQo+PWV43M6uEAeLVbTi09dFLhg==", + "version": "5.8.0", + "resolved": "https://registry.npmjs.org/@typescript-eslint/typescript-estree/-/typescript-estree-5.8.0.tgz", + "integrity": "sha512-srfeZ3URdEcUsSLbkOFqS7WoxOqn8JNil2NSLO9O+I2/Uyc85+UlfpEvQHIpj5dVts7KKOZnftoJD/Fdv0L7nQ==", "dev": true, "dependencies": { - "@typescript-eslint/types": "5.7.0", - "@typescript-eslint/visitor-keys": "5.7.0", + "@typescript-eslint/types": "5.8.0", + "@typescript-eslint/visitor-keys": "5.8.0", "debug": "^4.3.2", "globby": "^11.0.4", "is-glob": "^4.0.3", @@ -1092,57 +1135,13 @@ } } }, - "node_modules/@typescript-eslint/typescript-estree/node_modules/array-union": { - "version": "2.1.0", - "resolved": "https://registry.npmjs.org/array-union/-/array-union-2.1.0.tgz", - "integrity": "sha512-HGyxoOTYUyCM6stUe6EJgnd4EoewAI7zMdfqO+kGjnlZmBDz/cR5pf8r/cR4Wq60sL/p0IkcjUEEPwS3GFrIyw==", - "dev": true, - "engines": { - "node": ">=8" - } - }, - "node_modules/@typescript-eslint/typescript-estree/node_modules/globby": { - "version": "11.0.4", - "resolved": "https://registry.npmjs.org/globby/-/globby-11.0.4.tgz", - "integrity": "sha512-9O4MVG9ioZJ08ffbcyVYyLOJLk5JQ688pJ4eMGLpdWLHq/Wr1D9BlriLQyL0E+jbkuePVZXYFj47QM/v093wHg==", - "dev": true, - "dependencies": { - "array-union": "^2.1.0", - "dir-glob": "^3.0.1", - "fast-glob": "^3.1.1", - "ignore": "^5.1.4", - "merge2": "^1.3.0", - "slash": "^3.0.0" - }, - "engines": { - "node": ">=10" - }, - "funding": { - "url": "https://github.com/sponsors/sindresorhus" - } - }, - "node_modules/@typescript-eslint/typescript-estree/node_modules/semver": { - "version": "7.3.5", - "resolved": "https://registry.npmjs.org/semver/-/semver-7.3.5.tgz", - "integrity": "sha512-PoeGJYh8HK4BTO/a9Tf6ZG3veo/A7ZVsYrSA6J8ny9nb3B1VrpkuN+z9OE5wfE5p6H4LchYZsegiQgbJD94ZFQ==", - "dev": true, - "dependencies": { - "lru-cache": "^6.0.0" - }, - "bin": { - "semver": "bin/semver.js" - }, - "engines": { - "node": ">=10" - } - }, "node_modules/@typescript-eslint/visitor-keys": { - "version": "5.7.0", - "resolved": "https://registry.npmjs.org/@typescript-eslint/visitor-keys/-/visitor-keys-5.7.0.tgz", - "integrity": "sha512-hdohahZ4lTFcglZSJ3DGdzxQHBSxsLVqHzkiOmKi7xVAWC4y2c1bIMKmPJSrA4aOEoRUPOKQ87Y/taC7yVHpFg==", + "version": "5.8.0", + "resolved": "https://registry.npmjs.org/@typescript-eslint/visitor-keys/-/visitor-keys-5.8.0.tgz", + "integrity": "sha512-+HDIGOEMnqbxdAHegxvnOqESUH6RWFRR2b8qxP1W9CZnnYh4Usz6MBL+2KMAgPk/P0o9c1HqnYtwzVH6GTIqug==", "dev": true, "dependencies": { - "@typescript-eslint/types": "5.7.0", + "@typescript-eslint/types": "5.8.0", "eslint-visitor-keys": "^3.0.0" }, "engines": { @@ -1251,6 +1250,18 @@ "url": "https://github.com/sponsors/sindresorhus" } }, + "node_modules/ansi-escapes/node_modules/type-fest": { + "version": "0.21.3", + "resolved": "https://registry.npmjs.org/type-fest/-/type-fest-0.21.3.tgz", + "integrity": "sha512-t0rzBq87m3fVcduHDUFhKmyyX+9eo6WQjZvf51Ea/M0Q7+T374Jp1aUiyUl0GKxp8M/OETVHSDvmkyPgvX+X2w==", + "dev": true, + "engines": { + "node": ">=10" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, "node_modules/ansi-regex": { "version": "5.0.1", "resolved": "https://registry.npmjs.org/ansi-regex/-/ansi-regex-5.0.1.tgz", @@ -1319,15 +1330,12 @@ "dev": true }, "node_modules/array-union": { - "version": "1.0.2", - "resolved": "https://registry.npmjs.org/array-union/-/array-union-1.0.2.tgz", - "integrity": "sha1-mjRBDk9OPaI96jdb5b5w8kd47Dk=", + "version": "2.1.0", + "resolved": "https://registry.npmjs.org/array-union/-/array-union-2.1.0.tgz", + "integrity": "sha512-HGyxoOTYUyCM6stUe6EJgnd4EoewAI7zMdfqO+kGjnlZmBDz/cR5pf8r/cR4Wq60sL/p0IkcjUEEPwS3GFrIyw==", "dev": true, - "dependencies": { - "array-uniq": "^1.0.1" - }, "engines": { - "node": ">=0.10.0" + "node": ">=8" } }, "node_modules/array-uniq": { @@ -1437,10 +1445,10 @@ "url": "https://github.com/sponsors/sindresorhus" } }, - "node_modules/boxen/node_modules/type-fest": { - "version": "0.20.2", - "resolved": "https://registry.npmjs.org/type-fest/-/type-fest-0.20.2.tgz", - "integrity": "sha512-Ne+eE4r0/iWnpAxD852z3A+N0Bt5RN//NjJwRd2VFHEmrywxf5vsZlh4R6lixl6B+wz/8d+maTSAkN1FIkI3LQ==", + "node_modules/boxen/node_modules/camelcase": { + "version": "6.2.1", + "resolved": "https://registry.npmjs.org/camelcase/-/camelcase-6.2.1.tgz", + "integrity": "sha512-tVI4q5jjFV5CavAU8DXfza/TJcZutVKo/5Foskmsqcm0MsL91moHvwiGNnqaa2o6PF/7yT5ikDRcVcl8Rj6LCA==", "dev": true, "engines": { "node": ">=10" @@ -1610,21 +1618,18 @@ } }, "node_modules/camelcase": { - "version": "6.2.1", - "resolved": "https://registry.npmjs.org/camelcase/-/camelcase-6.2.1.tgz", - "integrity": "sha512-tVI4q5jjFV5CavAU8DXfza/TJcZutVKo/5Foskmsqcm0MsL91moHvwiGNnqaa2o6PF/7yT5ikDRcVcl8Rj6LCA==", + "version": "5.3.1", + "resolved": "https://registry.npmjs.org/camelcase/-/camelcase-5.3.1.tgz", + "integrity": "sha512-L28STB170nwWS63UjtlEOE3dldQApaJXZkOI1uMFfzf3rRuPegHaHesyee+YxQ+W6SvRDQV6UrdOdRiR153wJg==", "dev": true, "engines": { - "node": ">=10" - }, - "funding": { - "url": "https://github.com/sponsors/sindresorhus" + "node": ">=6" } }, "node_modules/caniuse-lite": { - "version": "1.0.30001286", - "resolved": "https://registry.npmjs.org/caniuse-lite/-/caniuse-lite-1.0.30001286.tgz", - "integrity": "sha512-zaEMRH6xg8ESMi2eQ3R4eZ5qw/hJiVsO/HlLwniIwErij0JDr9P+8V4dtx1l+kLq6j3yy8l8W4fst1lBnat5wQ==", + "version": "1.0.30001291", + "resolved": "https://registry.npmjs.org/caniuse-lite/-/caniuse-lite-1.0.30001291.tgz", + "integrity": "sha512-roMV5V0HNGgJ88s42eE70sstqGW/gwFndosYrikHthw98N5tLnOTxFqMLQjZVRxTWFlJ4rn+MsgXrR7MDPY4jA==", "dev": true, "funding": { "type": "opencollective", @@ -1674,6 +1679,18 @@ "fsevents": "~2.3.2" } }, + "node_modules/chokidar/node_modules/glob-parent": { + "version": "5.1.2", + "resolved": "https://registry.npmjs.org/glob-parent/-/glob-parent-5.1.2.tgz", + "integrity": "sha512-AOIgSQCepiJYwP3ARnGx+5VnTu2HBYdzbGP45eLw1vr3zB3vZLeyed1sC9hnbcOc9/SrMyM5RPQrkGz4aS9Zow==", + "dev": true, + "dependencies": { + "is-glob": "^4.0.1" + }, + "engines": { + "node": ">= 6" + } + }, "node_modules/ci-info": { "version": "3.3.0", "resolved": "https://registry.npmjs.org/ci-info/-/ci-info-3.3.0.tgz", @@ -1845,12 +1862,6 @@ "safe-buffer": "~5.1.1" } }, - "node_modules/convert-source-map/node_modules/safe-buffer": { - "version": "5.1.2", - "resolved": "https://registry.npmjs.org/safe-buffer/-/safe-buffer-5.1.2.tgz", - "integrity": "sha512-Gd2UZBJDkXlY7GbJxfsE8/nvKkUEU1G38c1siN6QP6a9PT9MmHB8GnpscSmMJSoF8LOIrt8ud/wPtojys4G6+g==", - "dev": true - }, "node_modules/cosmiconfig": { "version": "7.0.1", "resolved": "https://registry.npmjs.org/cosmiconfig/-/cosmiconfig-7.0.1.tgz", @@ -1897,9 +1908,9 @@ } }, "node_modules/debug": { - "version": "4.3.2", - "resolved": "https://registry.npmjs.org/debug/-/debug-4.3.2.tgz", - "integrity": "sha512-mOp8wKcvj7XxC78zLgw/ZA+6TSgkoE2C/ienthhRD298T7UNwAg9diBpLRxC0mOezLl4B0xV7M0cCO6P/O0Xhw==", + "version": "4.3.3", + "resolved": "https://registry.npmjs.org/debug/-/debug-4.3.3.tgz", + "integrity": "sha512-/zxw5+vh1Tfv+4Qn7a5nsbcJKPaSvCDhojn6FEl9vupwK2VCSDtEiEtqr8DFtzYFOdz63LBkxec7DYuc2jon6Q==", "dev": true, "dependencies": { "ms": "2.1.2" @@ -2083,9 +2094,9 @@ "dev": true }, "node_modules/electron-to-chromium": { - "version": "1.4.19", - "resolved": "https://registry.npmjs.org/electron-to-chromium/-/electron-to-chromium-1.4.19.tgz", - "integrity": "sha512-TeAjwsC/vhvxEtX/xN1JQUMkl+UrwKXlB4rwLyuLYVuBuRtqJJrU4Jy5pCVihMQg4m1ceZ3MEJ0yYuxHj8vC+w==", + "version": "1.4.24", + "resolved": "https://registry.npmjs.org/electron-to-chromium/-/electron-to-chromium-1.4.24.tgz", + "integrity": "sha512-erwx5r69B/WFfFuF2jcNN0817BfDBdC4765kQ6WltOMuwsimlQo3JTEq0Cle+wpHralwdeX3OfAtw/mHxPK0Wg==", "dev": true }, "node_modules/email-addresses": { @@ -2155,18 +2166,21 @@ } }, "node_modules/escape-string-regexp": { - "version": "1.0.5", - "resolved": "https://registry.npmjs.org/escape-string-regexp/-/escape-string-regexp-1.0.5.tgz", - "integrity": "sha1-G2HAViGQqN/2rjuyzwIAyhMLhtQ=", + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/escape-string-regexp/-/escape-string-regexp-4.0.0.tgz", + "integrity": "sha512-TtpcNJ3XAzx3Gq8sWRzJaVajRs0uVxA2YAkdb1jm2YkPz4G6egUFAyA3n5vtEIZefPk5Wa4UXbKuS5fKkJWdgA==", "dev": true, "engines": { - "node": ">=0.8.0" + "node": ">=10" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" } }, "node_modules/eslint": { - "version": "8.4.1", - "resolved": "https://registry.npmjs.org/eslint/-/eslint-8.4.1.tgz", - "integrity": "sha512-TxU/p7LB1KxQ6+7aztTnO7K0i+h0tDi81YRY9VzB6Id71kNz+fFYnf5HD5UOQmxkzcoa0TlVZf9dpMtUv0GpWg==", + "version": "8.5.0", + "resolved": "https://registry.npmjs.org/eslint/-/eslint-8.5.0.tgz", + "integrity": "sha512-tVGSkgNbOfiHyVte8bCM8OmX+xG9PzVG/B4UCF60zx7j61WIVY/AqJECDgpLD4DbbESD0e174gOg3ZlrX15GDg==", "dev": true, "dependencies": { "@eslint/eslintrc": "^1.0.5", @@ -2267,18 +2281,6 @@ "node": "^12.22.0 || ^14.17.0 || >=16.0.0" } }, - "node_modules/eslint/node_modules/escape-string-regexp": { - "version": "4.0.0", - "resolved": "https://registry.npmjs.org/escape-string-regexp/-/escape-string-regexp-4.0.0.tgz", - "integrity": "sha512-TtpcNJ3XAzx3Gq8sWRzJaVajRs0uVxA2YAkdb1jm2YkPz4G6egUFAyA3n5vtEIZefPk5Wa4UXbKuS5fKkJWdgA==", - "dev": true, - "engines": { - "node": ">=10" - }, - "funding": { - "url": "https://github.com/sponsors/sindresorhus" - } - }, "node_modules/eslint/node_modules/eslint-scope": { "version": "7.1.0", "resolved": "https://registry.npmjs.org/eslint-scope/-/eslint-scope-7.1.0.tgz", @@ -2301,18 +2303,6 @@ "node": ">=4.0" } }, - "node_modules/eslint/node_modules/glob-parent": { - "version": "6.0.2", - "resolved": "https://registry.npmjs.org/glob-parent/-/glob-parent-6.0.2.tgz", - "integrity": "sha512-XxwI8EOhVQgWp6iDL+3b0r86f4d6AX6zSU55HfB4ydCEuXLXc5FcYeOu+nnGftS4TEju/11rt4KJPTMgbfmv4A==", - "dev": true, - "dependencies": { - "is-glob": "^4.0.3" - }, - "engines": { - "node": ">=10.13.0" - } - }, "node_modules/eslint/node_modules/ignore": { "version": "4.0.6", "resolved": "https://registry.npmjs.org/ignore/-/ignore-4.0.6.tgz", @@ -2322,33 +2312,6 @@ "node": ">= 4" } }, - "node_modules/eslint/node_modules/semver": { - "version": "7.3.5", - "resolved": "https://registry.npmjs.org/semver/-/semver-7.3.5.tgz", - "integrity": "sha512-PoeGJYh8HK4BTO/a9Tf6ZG3veo/A7ZVsYrSA6J8ny9nb3B1VrpkuN+z9OE5wfE5p6H4LchYZsegiQgbJD94ZFQ==", - "dev": true, - "dependencies": { - "lru-cache": "^6.0.0" - }, - "bin": { - "semver": "bin/semver.js" - }, - "engines": { - "node": ">=10" - } - }, - "node_modules/eslint/node_modules/strip-json-comments": { - "version": "3.1.1", - "resolved": "https://registry.npmjs.org/strip-json-comments/-/strip-json-comments-3.1.1.tgz", - "integrity": "sha512-6fPc+R4ihwqP6N/aIv2f1gMH8lOVtWQHoqC4yK6oSDVVocumAsfCqjkXnqiYMhmMwS/mEHLp7Vehlt3ql6lEig==", - "dev": true, - "engines": { - "node": ">=8" - }, - "funding": { - "url": "https://github.com/sponsors/sindresorhus" - } - }, "node_modules/espree": { "version": "9.2.0", "resolved": "https://registry.npmjs.org/espree/-/espree-9.2.0.tgz", @@ -2495,6 +2458,18 @@ "node": ">=8" } }, + "node_modules/fast-glob/node_modules/glob-parent": { + "version": "5.1.2", + "resolved": "https://registry.npmjs.org/glob-parent/-/glob-parent-5.1.2.tgz", + "integrity": "sha512-AOIgSQCepiJYwP3ARnGx+5VnTu2HBYdzbGP45eLw1vr3zB3vZLeyed1sC9hnbcOc9/SrMyM5RPQrkGz4aS9Zow==", + "dev": true, + "dependencies": { + "is-glob": "^4.0.1" + }, + "engines": { + "node": ">= 6" + } + }, "node_modules/fast-json-stable-stringify": { "version": "2.1.0", "resolved": "https://registry.npmjs.org/fast-json-stable-stringify/-/fast-json-stable-stringify-2.1.0.tgz", @@ -2531,6 +2506,15 @@ "url": "https://github.com/sponsors/sindresorhus" } }, + "node_modules/figures/node_modules/escape-string-regexp": { + "version": "1.0.5", + "resolved": "https://registry.npmjs.org/escape-string-regexp/-/escape-string-regexp-1.0.5.tgz", + "integrity": "sha1-G2HAViGQqN/2rjuyzwIAyhMLhtQ=", + "dev": true, + "engines": { + "node": ">=0.8.0" + } + }, "node_modules/file-entry-cache": { "version": "6.0.1", "resolved": "https://registry.npmjs.org/file-entry-cache/-/file-entry-cache-6.0.1.tgz", @@ -2608,16 +2592,19 @@ } }, "node_modules/find-up": { - "version": "4.1.0", - "resolved": "https://registry.npmjs.org/find-up/-/find-up-4.1.0.tgz", - "integrity": "sha512-PpOwAdQ/YlXQ2vj8a3h8IipDuYRi3wceVQQGYWxNINccq40Anw7BlsEXCMbt1Zt+OLA6Fq9suIpIWD0OsnISlw==", + "version": "5.0.0", + "resolved": "https://registry.npmjs.org/find-up/-/find-up-5.0.0.tgz", + "integrity": "sha512-78/PXT1wlLLDgTzDs7sjq9hzz0vXD+zn+7wypEe4fXQxCmdmqfGsEPQxmiCSQI3ajFV91bVSsvNtrJRiW6nGng==", "dev": true, "dependencies": { - "locate-path": "^5.0.0", + "locate-path": "^6.0.0", "path-exists": "^4.0.0" }, "engines": { - "node": ">=8" + "node": ">=10" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" } }, "node_modules/flat": { @@ -2824,6 +2811,34 @@ "node": ">=10" } }, + "node_modules/gh-pages/node_modules/array-union": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/array-union/-/array-union-1.0.2.tgz", + "integrity": "sha1-mjRBDk9OPaI96jdb5b5w8kd47Dk=", + "dev": true, + "dependencies": { + "array-uniq": "^1.0.1" + }, + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/gh-pages/node_modules/globby": { + "version": "6.1.0", + "resolved": "https://registry.npmjs.org/globby/-/globby-6.1.0.tgz", + "integrity": "sha1-9abXDoOV4hyFj7BInWTfAkJNUGw=", + "dev": true, + "dependencies": { + "array-union": "^1.0.1", + "glob": "^7.0.3", + "object-assign": "^4.0.1", + "pify": "^2.0.0", + "pinkie-promise": "^2.0.0" + }, + "engines": { + "node": ">=0.10.0" + } + }, "node_modules/git-up": { "version": "4.0.5", "resolved": "https://registry.npmjs.org/git-up/-/git-up-4.0.5.tgz", @@ -2844,9 +2859,9 @@ } }, "node_modules/glob": { - "version": "7.2.0", - "resolved": "https://registry.npmjs.org/glob/-/glob-7.2.0.tgz", - "integrity": "sha512-lmLf6gtyrPq8tTjSmrO94wBeQbFR3HbLHbuyD69wuyQkImp2hWqMGB47OX65FBkPffO641IP9jWa1z4ivqG26Q==", + "version": "7.1.7", + "resolved": "https://registry.npmjs.org/glob/-/glob-7.1.7.tgz", + "integrity": "sha512-OvD9ENzPLbegENnYP5UUfJIirTg4+XwMWGaQfQTY0JenxNvvIKP3U3/tAQSPIu/lHxXYSZmpXlUHeqAIdKzBLQ==", "dev": true, "dependencies": { "fs.realpath": "^1.0.0", @@ -2864,15 +2879,15 @@ } }, "node_modules/glob-parent": { - "version": "5.1.2", - "resolved": "https://registry.npmjs.org/glob-parent/-/glob-parent-5.1.2.tgz", - "integrity": "sha512-AOIgSQCepiJYwP3ARnGx+5VnTu2HBYdzbGP45eLw1vr3zB3vZLeyed1sC9hnbcOc9/SrMyM5RPQrkGz4aS9Zow==", + "version": "6.0.2", + "resolved": "https://registry.npmjs.org/glob-parent/-/glob-parent-6.0.2.tgz", + "integrity": "sha512-XxwI8EOhVQgWp6iDL+3b0r86f4d6AX6zSU55HfB4ydCEuXLXc5FcYeOu+nnGftS4TEju/11rt4KJPTMgbfmv4A==", "dev": true, "dependencies": { - "is-glob": "^4.0.1" + "is-glob": "^4.0.3" }, "engines": { - "node": ">= 6" + "node": ">=10.13.0" } }, "node_modules/global-dirs": { @@ -2905,11 +2920,19 @@ "url": "https://github.com/sponsors/sindresorhus" } }, - "node_modules/globals/node_modules/type-fest": { - "version": "0.20.2", - "resolved": "https://registry.npmjs.org/type-fest/-/type-fest-0.20.2.tgz", - "integrity": "sha512-Ne+eE4r0/iWnpAxD852z3A+N0Bt5RN//NjJwRd2VFHEmrywxf5vsZlh4R6lixl6B+wz/8d+maTSAkN1FIkI3LQ==", + "node_modules/globby": { + "version": "11.0.4", + "resolved": "https://registry.npmjs.org/globby/-/globby-11.0.4.tgz", + "integrity": "sha512-9O4MVG9ioZJ08ffbcyVYyLOJLk5JQ688pJ4eMGLpdWLHq/Wr1D9BlriLQyL0E+jbkuePVZXYFj47QM/v093wHg==", "dev": true, + "dependencies": { + "array-union": "^2.1.0", + "dir-glob": "^3.0.1", + "fast-glob": "^3.1.1", + "ignore": "^5.1.4", + "merge2": "^1.3.0", + "slash": "^3.0.0" + }, "engines": { "node": ">=10" }, @@ -2917,22 +2940,6 @@ "url": "https://github.com/sponsors/sindresorhus" } }, - "node_modules/globby": { - "version": "6.1.0", - "resolved": "https://registry.npmjs.org/globby/-/globby-6.1.0.tgz", - "integrity": "sha1-9abXDoOV4hyFj7BInWTfAkJNUGw=", - "dev": true, - "dependencies": { - "array-union": "^1.0.1", - "glob": "^7.0.3", - "object-assign": "^4.0.1", - "pify": "^2.0.0", - "pinkie-promise": "^2.0.0" - }, - "engines": { - "node": ">=0.10.0" - } - }, "node_modules/got": { "version": "11.8.3", "resolved": "https://registry.npmjs.org/got/-/got-11.8.3.tgz", @@ -3116,9 +3123,9 @@ ] }, "node_modules/ignore": { - "version": "5.1.9", - "resolved": "https://registry.npmjs.org/ignore/-/ignore-5.1.9.tgz", - "integrity": "sha512-2zeMQpbKz5dhZ9IwL0gbxSW5w0NK/MSAMtNuhgIHEPmaU3vPdKPL0UdvUCXs5SS4JAwsBxysK5sFMW8ocFiVjQ==", + "version": "5.2.0", + "resolved": "https://registry.npmjs.org/ignore/-/ignore-5.2.0.tgz", + "integrity": "sha512-CmxgYGiEPCLhfLnpPp1MoRmifwEIOgjcHXxOBjv7mY96c+eWScsOP9c112ZyLdWHi0FxHjI+4uVhKYp/gcdRmQ==", "dev": true, "engines": { "node": ">= 4" @@ -3542,6 +3549,15 @@ "node": ">=8" } }, + "node_modules/istanbul-lib-instrument/node_modules/semver": { + "version": "6.3.0", + "resolved": "https://registry.npmjs.org/semver/-/semver-6.3.0.tgz", + "integrity": "sha512-b39TBaTSfV6yBrapU89p5fKekE2m/NwnDocOVruQFS1/veMgdzuPcnOM34M6CwxW8jH/lxEa5rBoDeUwu5HHTw==", + "dev": true, + "bin": { + "semver": "bin/semver.js" + } + }, "node_modules/istanbul-lib-processinfo": { "version": "2.0.2", "resolved": "https://registry.npmjs.org/istanbul-lib-processinfo/-/istanbul-lib-processinfo-2.0.2.tgz", @@ -3560,16 +3576,6 @@ "node": ">=8" } }, - "node_modules/istanbul-lib-processinfo/node_modules/uuid": { - "version": "3.4.0", - "resolved": "https://registry.npmjs.org/uuid/-/uuid-3.4.0.tgz", - "integrity": "sha512-HjSDRw6gZE5JMggctHBcjVak08+KEVhSIiDzFnT9S9aegmp85S/bReBVTb4QTFaRNptJ9kuYaNhnbNEOkbKb/A==", - "deprecated": "Please upgrade to version 7 or higher. Older versions may use Math.random() in certain circumstances, which is known to be problematic. See https://v8.dev/blog/math-random for details.", - "dev": true, - "bin": { - "uuid": "bin/uuid" - } - }, "node_modules/istanbul-lib-report": { "version": "3.0.0", "resolved": "https://registry.npmjs.org/istanbul-lib-report/-/istanbul-lib-report-3.0.0.tgz", @@ -3751,15 +3757,18 @@ "dev": true }, "node_modules/locate-path": { - "version": "5.0.0", - "resolved": "https://registry.npmjs.org/locate-path/-/locate-path-5.0.0.tgz", - "integrity": "sha512-t7hw9pI+WvuwNJXwk5zVHpyhIqzg2qTlklJOf0mVxGSbe3Fp2VieZcduNYjaLDoy6p9uGpQEGWG87WpMKlNq8g==", + "version": "6.0.0", + "resolved": "https://registry.npmjs.org/locate-path/-/locate-path-6.0.0.tgz", + "integrity": "sha512-iPZK6eYjbxRu3uB4/WZ3EsEIMJFMqAoopl3R+zuq0UjcAm/MO6KCweDgPfP3elTztoKP3KtnVHxTn2NHBSDVUw==", "dev": true, "dependencies": { - "p-locate": "^4.1.0" + "p-locate": "^5.0.0" }, "engines": { - "node": ">=8" + "node": ">=10" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" } }, "node_modules/lodash": { @@ -3856,6 +3865,15 @@ "url": "https://github.com/sponsors/sindresorhus" } }, + "node_modules/make-dir/node_modules/semver": { + "version": "6.3.0", + "resolved": "https://registry.npmjs.org/semver/-/semver-6.3.0.tgz", + "integrity": "sha512-b39TBaTSfV6yBrapU89p5fKekE2m/NwnDocOVruQFS1/veMgdzuPcnOM34M6CwxW8jH/lxEa5rBoDeUwu5HHTw==", + "dev": true, + "bin": { + "semver": "bin/semver.js" + } + }, "node_modules/make-error": { "version": "1.3.6", "resolved": "https://registry.npmjs.org/make-error/-/make-error-1.3.6.tgz", @@ -4002,68 +4020,28 @@ "url": "https://opencollective.com/mochajs" } }, - "node_modules/mocha/node_modules/escape-string-regexp": { - "version": "4.0.0", - "resolved": "https://registry.npmjs.org/escape-string-regexp/-/escape-string-regexp-4.0.0.tgz", - "integrity": "sha512-TtpcNJ3XAzx3Gq8sWRzJaVajRs0uVxA2YAkdb1jm2YkPz4G6egUFAyA3n5vtEIZefPk5Wa4UXbKuS5fKkJWdgA==", - "dev": true, - "engines": { - "node": ">=10" - }, - "funding": { - "url": "https://github.com/sponsors/sindresorhus" - } - }, - "node_modules/mocha/node_modules/find-up": { - "version": "5.0.0", - "resolved": "https://registry.npmjs.org/find-up/-/find-up-5.0.0.tgz", - "integrity": "sha512-78/PXT1wlLLDgTzDs7sjq9hzz0vXD+zn+7wypEe4fXQxCmdmqfGsEPQxmiCSQI3ajFV91bVSsvNtrJRiW6nGng==", - "dev": true, - "dependencies": { - "locate-path": "^6.0.0", - "path-exists": "^4.0.0" - }, - "engines": { - "node": ">=10" - }, - "funding": { - "url": "https://github.com/sponsors/sindresorhus" - } - }, - "node_modules/mocha/node_modules/glob": { - "version": "7.1.7", - "resolved": "https://registry.npmjs.org/glob/-/glob-7.1.7.tgz", - "integrity": "sha512-OvD9ENzPLbegENnYP5UUfJIirTg4+XwMWGaQfQTY0JenxNvvIKP3U3/tAQSPIu/lHxXYSZmpXlUHeqAIdKzBLQ==", + "node_modules/mocha/node_modules/debug": { + "version": "4.3.2", + "resolved": "https://registry.npmjs.org/debug/-/debug-4.3.2.tgz", + "integrity": "sha512-mOp8wKcvj7XxC78zLgw/ZA+6TSgkoE2C/ienthhRD298T7UNwAg9diBpLRxC0mOezLl4B0xV7M0cCO6P/O0Xhw==", "dev": true, "dependencies": { - "fs.realpath": "^1.0.0", - "inflight": "^1.0.4", - "inherits": "2", - "minimatch": "^3.0.4", - "once": "^1.3.0", - "path-is-absolute": "^1.0.0" + "ms": "2.1.2" }, "engines": { - "node": "*" + "node": ">=6.0" }, - "funding": { - "url": "https://github.com/sponsors/isaacs" + "peerDependenciesMeta": { + "supports-color": { + "optional": true + } } }, - "node_modules/mocha/node_modules/locate-path": { - "version": "6.0.0", - "resolved": "https://registry.npmjs.org/locate-path/-/locate-path-6.0.0.tgz", - "integrity": "sha512-iPZK6eYjbxRu3uB4/WZ3EsEIMJFMqAoopl3R+zuq0UjcAm/MO6KCweDgPfP3elTztoKP3KtnVHxTn2NHBSDVUw==", - "dev": true, - "dependencies": { - "p-locate": "^5.0.0" - }, - "engines": { - "node": ">=10" - }, - "funding": { - "url": "https://github.com/sponsors/sindresorhus" - } + "node_modules/mocha/node_modules/debug/node_modules/ms": { + "version": "2.1.2", + "resolved": "https://registry.npmjs.org/ms/-/ms-2.1.2.tgz", + "integrity": "sha512-sGkPx+VjMtmA6MX27oA4FBFELFCZZ4S4XqeGOXCv68tT+jb3vk/RyaKWP0PTKyWtmLSM0b+adUTEvbs1PEaH2w==", + "dev": true }, "node_modules/mocha/node_modules/ms": { "version": "2.1.3", @@ -4071,48 +4049,6 @@ "integrity": "sha512-6FlzubTLZG3J2a/NVCAleEhjzq5oxgHyaCU9yYXvcLsvoVaHJq/s5xXI6/XXP6tz7R9xAOtHnSO/tXtF3WRTlA==", "dev": true }, - "node_modules/mocha/node_modules/p-limit": { - "version": "3.1.0", - "resolved": "https://registry.npmjs.org/p-limit/-/p-limit-3.1.0.tgz", - "integrity": "sha512-TYOanM3wGwNGsZN2cVTYPArw454xnXj5qmWF1bEoAc4+cU/ol7GVh7odevjp1FNHduHc3KZMcFduxU5Xc6uJRQ==", - "dev": true, - "dependencies": { - "yocto-queue": "^0.1.0" - }, - "engines": { - "node": ">=10" - }, - "funding": { - "url": "https://github.com/sponsors/sindresorhus" - } - }, - "node_modules/mocha/node_modules/p-locate": { - "version": "5.0.0", - "resolved": "https://registry.npmjs.org/p-locate/-/p-locate-5.0.0.tgz", - "integrity": "sha512-LaNjtRWUBY++zB5nE/NwcaoMylSPk+S+ZHNB1TzdbMJMny6dynpAGt7X/tl/QYq3TIeE6nxHppbo2LGymrG5Pw==", - "dev": true, - "dependencies": { - "p-limit": "^3.0.2" - }, - "engines": { - "node": ">=10" - }, - "funding": { - "url": "https://github.com/sponsors/sindresorhus" - } - }, - "node_modules/mocha/node_modules/strip-json-comments": { - "version": "3.1.1", - "resolved": "https://registry.npmjs.org/strip-json-comments/-/strip-json-comments-3.1.1.tgz", - "integrity": "sha512-6fPc+R4ihwqP6N/aIv2f1gMH8lOVtWQHoqC4yK6oSDVVocumAsfCqjkXnqiYMhmMwS/mEHLp7Vehlt3ql6lEig==", - "dev": true, - "engines": { - "node": ">=8" - }, - "funding": { - "url": "https://github.com/sponsors/sindresorhus" - } - }, "node_modules/mocha/node_modules/supports-color": { "version": "8.1.1", "resolved": "https://registry.npmjs.org/supports-color/-/supports-color-8.1.1.tgz", @@ -4146,15 +4082,6 @@ "node": ">=10" } }, - "node_modules/mocha/node_modules/yargs-parser": { - "version": "20.2.4", - "resolved": "https://registry.npmjs.org/yargs-parser/-/yargs-parser-20.2.4.tgz", - "integrity": "sha512-WOkpgNhPTlE73h4VFAFsOnomJVaovO8VqLDzy5saChRBFQFBoMYirowyW+Q9HB4HFF4Z7VZTiG3iSzJJA29yRA==", - "dev": true, - "engines": { - "node": ">=10" - } - }, "node_modules/ms": { "version": "2.1.2", "resolved": "https://registry.npmjs.org/ms/-/ms-2.1.2.tgz", @@ -4323,15 +4250,6 @@ "node": ">=8.9" } }, - "node_modules/nyc/node_modules/camelcase": { - "version": "5.3.1", - "resolved": "https://registry.npmjs.org/camelcase/-/camelcase-5.3.1.tgz", - "integrity": "sha512-L28STB170nwWS63UjtlEOE3dldQApaJXZkOI1uMFfzf3rRuPegHaHesyee+YxQ+W6SvRDQV6UrdOdRiR153wJg==", - "dev": true, - "engines": { - "node": ">=6" - } - }, "node_modules/nyc/node_modules/cliui": { "version": "6.0.0", "resolved": "https://registry.npmjs.org/cliui/-/cliui-6.0.0.tgz", @@ -4343,6 +4261,58 @@ "wrap-ansi": "^6.2.0" } }, + "node_modules/nyc/node_modules/find-up": { + "version": "4.1.0", + "resolved": "https://registry.npmjs.org/find-up/-/find-up-4.1.0.tgz", + "integrity": "sha512-PpOwAdQ/YlXQ2vj8a3h8IipDuYRi3wceVQQGYWxNINccq40Anw7BlsEXCMbt1Zt+OLA6Fq9suIpIWD0OsnISlw==", + "dev": true, + "dependencies": { + "locate-path": "^5.0.0", + "path-exists": "^4.0.0" + }, + "engines": { + "node": ">=8" + } + }, + "node_modules/nyc/node_modules/locate-path": { + "version": "5.0.0", + "resolved": "https://registry.npmjs.org/locate-path/-/locate-path-5.0.0.tgz", + "integrity": "sha512-t7hw9pI+WvuwNJXwk5zVHpyhIqzg2qTlklJOf0mVxGSbe3Fp2VieZcduNYjaLDoy6p9uGpQEGWG87WpMKlNq8g==", + "dev": true, + "dependencies": { + "p-locate": "^4.1.0" + }, + "engines": { + "node": ">=8" + } + }, + "node_modules/nyc/node_modules/p-limit": { + "version": "2.3.0", + "resolved": "https://registry.npmjs.org/p-limit/-/p-limit-2.3.0.tgz", + "integrity": "sha512-//88mFWSJx8lxCzwdAABTJL2MyWB12+eIY7MDL2SqLmAkeKU9qxRvWuSyTjm3FUmpBEMuFfckAIqEaVGUDxb6w==", + "dev": true, + "dependencies": { + "p-try": "^2.0.0" + }, + "engines": { + "node": ">=6" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/nyc/node_modules/p-locate": { + "version": "4.1.0", + "resolved": "https://registry.npmjs.org/p-locate/-/p-locate-4.1.0.tgz", + "integrity": "sha512-R79ZZ/0wAxKGu3oYMlz8jy/kbhsNrS7SKZ7PxEHBgJ5+F2mtFW2fK2cOtBh1cHYkQsbzFV7I+EoRKe6Yt0oK7A==", + "dev": true, + "dependencies": { + "p-limit": "^2.2.0" + }, + "engines": { + "node": ">=8" + } + }, "node_modules/nyc/node_modules/resolve-from": { "version": "5.0.0", "resolved": "https://registry.npmjs.org/resolve-from/-/resolve-from-5.0.0.tgz", @@ -4417,9 +4387,9 @@ } }, "node_modules/object-inspect": { - "version": "1.11.1", - "resolved": "https://registry.npmjs.org/object-inspect/-/object-inspect-1.11.1.tgz", - "integrity": "sha512-If7BjFlpkzzBeV1cqgT3OSWT3azyoxDGajR+iGnFBfVV2EWyDyWaZZW2ERDjUaY2QM8i5jI3Sj7mhsM4DDAqWA==", + "version": "1.12.0", + "resolved": "https://registry.npmjs.org/object-inspect/-/object-inspect-1.12.0.tgz", + "integrity": "sha512-Ho2z80bVIvJloH+YzRmpZVQe87+qASmBUKZDWgx9cu+KDrX2ZDH/3tMy+gXbZETVGs2M8YdxObOh7XAtim9Y0g==", "dev": true, "funding": { "url": "https://github.com/sponsors/ljharb" @@ -4540,30 +4510,33 @@ } }, "node_modules/p-limit": { - "version": "2.3.0", - "resolved": "https://registry.npmjs.org/p-limit/-/p-limit-2.3.0.tgz", - "integrity": "sha512-//88mFWSJx8lxCzwdAABTJL2MyWB12+eIY7MDL2SqLmAkeKU9qxRvWuSyTjm3FUmpBEMuFfckAIqEaVGUDxb6w==", + "version": "3.1.0", + "resolved": "https://registry.npmjs.org/p-limit/-/p-limit-3.1.0.tgz", + "integrity": "sha512-TYOanM3wGwNGsZN2cVTYPArw454xnXj5qmWF1bEoAc4+cU/ol7GVh7odevjp1FNHduHc3KZMcFduxU5Xc6uJRQ==", "dev": true, "dependencies": { - "p-try": "^2.0.0" + "yocto-queue": "^0.1.0" }, "engines": { - "node": ">=6" + "node": ">=10" }, "funding": { "url": "https://github.com/sponsors/sindresorhus" } }, "node_modules/p-locate": { - "version": "4.1.0", - "resolved": "https://registry.npmjs.org/p-locate/-/p-locate-4.1.0.tgz", - "integrity": "sha512-R79ZZ/0wAxKGu3oYMlz8jy/kbhsNrS7SKZ7PxEHBgJ5+F2mtFW2fK2cOtBh1cHYkQsbzFV7I+EoRKe6Yt0oK7A==", + "version": "5.0.0", + "resolved": "https://registry.npmjs.org/p-locate/-/p-locate-5.0.0.tgz", + "integrity": "sha512-LaNjtRWUBY++zB5nE/NwcaoMylSPk+S+ZHNB1TzdbMJMny6dynpAGt7X/tl/QYq3TIeE6nxHppbo2LGymrG5Pw==", "dev": true, "dependencies": { - "p-limit": "^2.2.0" + "p-limit": "^3.0.2" }, "engines": { - "node": ">=8" + "node": ">=10" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" } }, "node_modules/p-map": { @@ -4783,6 +4756,15 @@ "node": ">=0.10.0" } }, + "node_modules/package-json/node_modules/semver": { + "version": "6.3.0", + "resolved": "https://registry.npmjs.org/semver/-/semver-6.3.0.tgz", + "integrity": "sha512-b39TBaTSfV6yBrapU89p5fKekE2m/NwnDocOVruQFS1/veMgdzuPcnOM34M6CwxW8jH/lxEa5rBoDeUwu5HHTw==", + "dev": true, + "bin": { + "semver": "bin/semver.js" + } + }, "node_modules/parent-module": { "version": "1.0.1", "resolved": "https://registry.npmjs.org/parent-module/-/parent-module-1.0.1.tgz", @@ -4948,6 +4930,58 @@ "node": ">=8" } }, + "node_modules/pkg-dir/node_modules/find-up": { + "version": "4.1.0", + "resolved": "https://registry.npmjs.org/find-up/-/find-up-4.1.0.tgz", + "integrity": "sha512-PpOwAdQ/YlXQ2vj8a3h8IipDuYRi3wceVQQGYWxNINccq40Anw7BlsEXCMbt1Zt+OLA6Fq9suIpIWD0OsnISlw==", + "dev": true, + "dependencies": { + "locate-path": "^5.0.0", + "path-exists": "^4.0.0" + }, + "engines": { + "node": ">=8" + } + }, + "node_modules/pkg-dir/node_modules/locate-path": { + "version": "5.0.0", + "resolved": "https://registry.npmjs.org/locate-path/-/locate-path-5.0.0.tgz", + "integrity": "sha512-t7hw9pI+WvuwNJXwk5zVHpyhIqzg2qTlklJOf0mVxGSbe3Fp2VieZcduNYjaLDoy6p9uGpQEGWG87WpMKlNq8g==", + "dev": true, + "dependencies": { + "p-locate": "^4.1.0" + }, + "engines": { + "node": ">=8" + } + }, + "node_modules/pkg-dir/node_modules/p-limit": { + "version": "2.3.0", + "resolved": "https://registry.npmjs.org/p-limit/-/p-limit-2.3.0.tgz", + "integrity": "sha512-//88mFWSJx8lxCzwdAABTJL2MyWB12+eIY7MDL2SqLmAkeKU9qxRvWuSyTjm3FUmpBEMuFfckAIqEaVGUDxb6w==", + "dev": true, + "dependencies": { + "p-try": "^2.0.0" + }, + "engines": { + "node": ">=6" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/pkg-dir/node_modules/p-locate": { + "version": "4.1.0", + "resolved": "https://registry.npmjs.org/p-locate/-/p-locate-4.1.0.tgz", + "integrity": "sha512-R79ZZ/0wAxKGu3oYMlz8jy/kbhsNrS7SKZ7PxEHBgJ5+F2mtFW2fK2cOtBh1cHYkQsbzFV7I+EoRKe6Yt0oK7A==", + "dev": true, + "dependencies": { + "p-limit": "^2.2.0" + }, + "engines": { + "node": ">=8" + } + }, "node_modules/prelude-ls": { "version": "1.2.1", "resolved": "https://registry.npmjs.org/prelude-ls/-/prelude-ls-1.2.1.tgz", @@ -5119,6 +5153,15 @@ "integrity": "sha512-JV/yugV2uzW5iMRSiZAyDtQd+nxtUnjeLt0acNdw98kKLrvuRVyB80tsREOE7yvGVgalhZ6RNXCmEHkUKBKxew==", "dev": true }, + "node_modules/rc/node_modules/strip-json-comments": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/strip-json-comments/-/strip-json-comments-2.0.1.tgz", + "integrity": "sha1-PFMZQukIwml8DsNEhYwobHygpgo=", + "dev": true, + "engines": { + "node": ">=0.10.0" + } + }, "node_modules/readable-stream": { "version": "3.6.0", "resolved": "https://registry.npmjs.org/readable-stream/-/readable-stream-3.6.0.tgz", @@ -5255,46 +5298,37 @@ "node": ">=10" } }, - "node_modules/release-it/node_modules/array-union": { - "version": "2.1.0", - "resolved": "https://registry.npmjs.org/array-union/-/array-union-2.1.0.tgz", - "integrity": "sha512-HGyxoOTYUyCM6stUe6EJgnd4EoewAI7zMdfqO+kGjnlZmBDz/cR5pf8r/cR4Wq60sL/p0IkcjUEEPwS3GFrIyw==", - "dev": true, - "engines": { - "node": ">=8" - } - }, - "node_modules/release-it/node_modules/globby": { - "version": "11.0.4", - "resolved": "https://registry.npmjs.org/globby/-/globby-11.0.4.tgz", - "integrity": "sha512-9O4MVG9ioZJ08ffbcyVYyLOJLk5JQ688pJ4eMGLpdWLHq/Wr1D9BlriLQyL0E+jbkuePVZXYFj47QM/v093wHg==", + "node_modules/release-it/node_modules/debug": { + "version": "4.3.2", + "resolved": "https://registry.npmjs.org/debug/-/debug-4.3.2.tgz", + "integrity": "sha512-mOp8wKcvj7XxC78zLgw/ZA+6TSgkoE2C/ienthhRD298T7UNwAg9diBpLRxC0mOezLl4B0xV7M0cCO6P/O0Xhw==", "dev": true, "dependencies": { - "array-union": "^2.1.0", - "dir-glob": "^3.0.1", - "fast-glob": "^3.1.1", - "ignore": "^5.1.4", - "merge2": "^1.3.0", - "slash": "^3.0.0" + "ms": "2.1.2" }, "engines": { - "node": ">=10" + "node": ">=6.0" }, - "funding": { - "url": "https://github.com/sponsors/sindresorhus" + "peerDependenciesMeta": { + "supports-color": { + "optional": true + } } }, - "node_modules/release-it/node_modules/semver": { - "version": "7.3.5", - "resolved": "https://registry.npmjs.org/semver/-/semver-7.3.5.tgz", - "integrity": "sha512-PoeGJYh8HK4BTO/a9Tf6ZG3veo/A7ZVsYrSA6J8ny9nb3B1VrpkuN+z9OE5wfE5p6H4LchYZsegiQgbJD94ZFQ==", + "node_modules/release-it/node_modules/uuid": { + "version": "8.3.2", + "resolved": "https://registry.npmjs.org/uuid/-/uuid-8.3.2.tgz", + "integrity": "sha512-+NYs2QeMWy+GWFOEm9xnn6HCDp0l7QBD7ml8zLUmJ+93Q5NF0NocErnwkTkXVFNiX3/fpC6afS8Dhb/gz7R7eg==", "dev": true, - "dependencies": { - "lru-cache": "^6.0.0" - }, "bin": { - "semver": "bin/semver.js" - }, + "uuid": "dist/bin/uuid" + } + }, + "node_modules/release-it/node_modules/yargs-parser": { + "version": "20.2.9", + "resolved": "https://registry.npmjs.org/yargs-parser/-/yargs-parser-20.2.9.tgz", + "integrity": "sha512-y11nGElTIV+CT3Zv9t7VKl+Q3hTQoT9a1Qzezhhl6Rp21gJ/IVTW7Z3y9EWXhuUBC2Shnf+DX0antecpAwSP8w==", + "dev": true, "engines": { "node": ">=10" } @@ -5452,24 +5486,10 @@ } }, "node_modules/safe-buffer": { - "version": "5.2.1", - "resolved": "https://registry.npmjs.org/safe-buffer/-/safe-buffer-5.2.1.tgz", - "integrity": "sha512-rp3So07KcdmmKbGvgaNxQSJr7bGVSVk5S9Eq1F+ppbRo70+YeaDxkw5Dd8NPN+GD6bjnYm2VuPuCXmpuYvmCXQ==", - "dev": true, - "funding": [ - { - "type": "github", - "url": "https://github.com/sponsors/feross" - }, - { - "type": "patreon", - "url": "https://www.patreon.com/feross" - }, - { - "type": "consulting", - "url": "https://feross.org/support" - } - ] + "version": "5.1.2", + "resolved": "https://registry.npmjs.org/safe-buffer/-/safe-buffer-5.1.2.tgz", + "integrity": "sha512-Gd2UZBJDkXlY7GbJxfsE8/nvKkUEU1G38c1siN6QP6a9PT9MmHB8GnpscSmMJSoF8LOIrt8ud/wPtojys4G6+g==", + "dev": true }, "node_modules/safer-buffer": { "version": "2.1.2", @@ -5478,12 +5498,18 @@ "dev": true }, "node_modules/semver": { - "version": "6.3.0", - "resolved": "https://registry.npmjs.org/semver/-/semver-6.3.0.tgz", - "integrity": "sha512-b39TBaTSfV6yBrapU89p5fKekE2m/NwnDocOVruQFS1/veMgdzuPcnOM34M6CwxW8jH/lxEa5rBoDeUwu5HHTw==", + "version": "7.3.5", + "resolved": "https://registry.npmjs.org/semver/-/semver-7.3.5.tgz", + "integrity": "sha512-PoeGJYh8HK4BTO/a9Tf6ZG3veo/A7ZVsYrSA6J8ny9nb3B1VrpkuN+z9OE5wfE5p6H4LchYZsegiQgbJD94ZFQ==", "dev": true, + "dependencies": { + "lru-cache": "^6.0.0" + }, "bin": { "semver": "bin/semver.js" + }, + "engines": { + "node": ">=10" } }, "node_modules/semver-diff": { @@ -5498,6 +5524,15 @@ "node": ">=8" } }, + "node_modules/semver-diff/node_modules/semver": { + "version": "6.3.0", + "resolved": "https://registry.npmjs.org/semver/-/semver-6.3.0.tgz", + "integrity": "sha512-b39TBaTSfV6yBrapU89p5fKekE2m/NwnDocOVruQFS1/veMgdzuPcnOM34M6CwxW8jH/lxEa5rBoDeUwu5HHTw==", + "dev": true, + "bin": { + "semver": "bin/semver.js" + } + }, "node_modules/serialize-javascript": { "version": "6.0.0", "resolved": "https://registry.npmjs.org/serialize-javascript/-/serialize-javascript-6.0.0.tgz", @@ -5696,6 +5731,26 @@ "safe-buffer": "~5.2.0" } }, + "node_modules/string_decoder/node_modules/safe-buffer": { + "version": "5.2.1", + "resolved": "https://registry.npmjs.org/safe-buffer/-/safe-buffer-5.2.1.tgz", + "integrity": "sha512-rp3So07KcdmmKbGvgaNxQSJr7bGVSVk5S9Eq1F+ppbRo70+YeaDxkw5Dd8NPN+GD6bjnYm2VuPuCXmpuYvmCXQ==", + "dev": true, + "funding": [ + { + "type": "github", + "url": "https://github.com/sponsors/feross" + }, + { + "type": "patreon", + "url": "https://www.patreon.com/feross" + }, + { + "type": "consulting", + "url": "https://feross.org/support" + } + ] + }, "node_modules/string-width": { "version": "4.2.3", "resolved": "https://registry.npmjs.org/string-width/-/string-width-4.2.3.tgz", @@ -5741,12 +5796,15 @@ } }, "node_modules/strip-json-comments": { - "version": "2.0.1", - "resolved": "https://registry.npmjs.org/strip-json-comments/-/strip-json-comments-2.0.1.tgz", - "integrity": "sha1-PFMZQukIwml8DsNEhYwobHygpgo=", + "version": "3.1.1", + "resolved": "https://registry.npmjs.org/strip-json-comments/-/strip-json-comments-3.1.1.tgz", + "integrity": "sha512-6fPc+R4ihwqP6N/aIv2f1gMH8lOVtWQHoqC4yK6oSDVVocumAsfCqjkXnqiYMhmMwS/mEHLp7Vehlt3ql6lEig==", "dev": true, "engines": { - "node": ">=0.10.0" + "node": ">=8" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" } }, "node_modules/strip-outer": { @@ -5761,6 +5819,15 @@ "node": ">=0.10.0" } }, + "node_modules/strip-outer/node_modules/escape-string-regexp": { + "version": "1.0.5", + "resolved": "https://registry.npmjs.org/escape-string-regexp/-/escape-string-regexp-1.0.5.tgz", + "integrity": "sha1-G2HAViGQqN/2rjuyzwIAyhMLhtQ=", + "dev": true, + "engines": { + "node": ">=0.8.0" + } + }, "node_modules/supports-color": { "version": "7.2.0", "resolved": "https://registry.npmjs.org/supports-color/-/supports-color-7.2.0.tgz", @@ -5852,11 +5919,20 @@ "resolved": "https://registry.npmjs.org/trim-repeated/-/trim-repeated-1.0.0.tgz", "integrity": "sha1-42RqLqTokTEr9+rObPsFOAvAHCE=", "dev": true, - "dependencies": { - "escape-string-regexp": "^1.0.2" - }, + "dependencies": { + "escape-string-regexp": "^1.0.2" + }, + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/trim-repeated/node_modules/escape-string-regexp": { + "version": "1.0.5", + "resolved": "https://registry.npmjs.org/escape-string-regexp/-/escape-string-regexp-1.0.5.tgz", + "integrity": "sha1-G2HAViGQqN/2rjuyzwIAyhMLhtQ=", + "dev": true, "engines": { - "node": ">=0.10.0" + "node": ">=0.8.0" } }, "node_modules/ts-node": { @@ -5958,9 +6034,9 @@ } }, "node_modules/type-fest": { - "version": "0.21.3", - "resolved": "https://registry.npmjs.org/type-fest/-/type-fest-0.21.3.tgz", - "integrity": "sha512-t0rzBq87m3fVcduHDUFhKmyyX+9eo6WQjZvf51Ea/M0Q7+T374Jp1aUiyUl0GKxp8M/OETVHSDvmkyPgvX+X2w==", + "version": "0.20.2", + "resolved": "https://registry.npmjs.org/type-fest/-/type-fest-0.20.2.tgz", + "integrity": "sha512-Ne+eE4r0/iWnpAxD852z3A+N0Bt5RN//NjJwRd2VFHEmrywxf5vsZlh4R6lixl6B+wz/8d+maTSAkN1FIkI3LQ==", "dev": true, "engines": { "node": ">=10" @@ -6000,6 +6076,26 @@ "typescript": "4.0.x || 4.1.x || 4.2.x || 4.3.x || 4.4.x || 4.5.x" } }, + "node_modules/typedoc/node_modules/glob": { + "version": "7.2.0", + "resolved": "https://registry.npmjs.org/glob/-/glob-7.2.0.tgz", + "integrity": "sha512-lmLf6gtyrPq8tTjSmrO94wBeQbFR3HbLHbuyD69wuyQkImp2hWqMGB47OX65FBkPffO641IP9jWa1z4ivqG26Q==", + "dev": true, + "dependencies": { + "fs.realpath": "^1.0.0", + "inflight": "^1.0.4", + "inherits": "2", + "minimatch": "^3.0.4", + "once": "^1.3.0", + "path-is-absolute": "^1.0.0" + }, + "engines": { + "node": "*" + }, + "funding": { + "url": "https://github.com/sponsors/isaacs" + } + }, "node_modules/typescript": { "version": "4.5.4", "resolved": "https://registry.npmjs.org/typescript/-/typescript-4.5.4.tgz", @@ -6086,21 +6182,6 @@ "is-ci": "bin.js" } }, - "node_modules/update-notifier/node_modules/semver": { - "version": "7.3.5", - "resolved": "https://registry.npmjs.org/semver/-/semver-7.3.5.tgz", - "integrity": "sha512-PoeGJYh8HK4BTO/a9Tf6ZG3veo/A7ZVsYrSA6J8ny9nb3B1VrpkuN+z9OE5wfE5p6H4LchYZsegiQgbJD94ZFQ==", - "dev": true, - "dependencies": { - "lru-cache": "^6.0.0" - }, - "bin": { - "semver": "bin/semver.js" - }, - "engines": { - "node": ">=10" - } - }, "node_modules/uri-js": { "version": "4.4.1", "resolved": "https://registry.npmjs.org/uri-js/-/uri-js-4.4.1.tgz", @@ -6135,12 +6216,13 @@ "dev": true }, "node_modules/uuid": { - "version": "8.3.2", - "resolved": "https://registry.npmjs.org/uuid/-/uuid-8.3.2.tgz", - "integrity": "sha512-+NYs2QeMWy+GWFOEm9xnn6HCDp0l7QBD7ml8zLUmJ+93Q5NF0NocErnwkTkXVFNiX3/fpC6afS8Dhb/gz7R7eg==", + "version": "3.4.0", + "resolved": "https://registry.npmjs.org/uuid/-/uuid-3.4.0.tgz", + "integrity": "sha512-HjSDRw6gZE5JMggctHBcjVak08+KEVhSIiDzFnT9S9aegmp85S/bReBVTb4QTFaRNptJ9kuYaNhnbNEOkbKb/A==", + "deprecated": "Please upgrade to version 7 or higher. Older versions may use Math.random() in certain circumstances, which is known to be problematic. See https://v8.dev/blog/math-random for details.", "dev": true, "bin": { - "uuid": "dist/bin/uuid" + "uuid": "bin/uuid" } }, "node_modules/v8-compile-cache": { @@ -6382,9 +6464,9 @@ } }, "node_modules/yargs-parser": { - "version": "20.2.9", - "resolved": "https://registry.npmjs.org/yargs-parser/-/yargs-parser-20.2.9.tgz", - "integrity": "sha512-y11nGElTIV+CT3Zv9t7VKl+Q3hTQoT9a1Qzezhhl6Rp21gJ/IVTW7Z3y9EWXhuUBC2Shnf+DX0antecpAwSP8w==", + "version": "20.2.4", + "resolved": "https://registry.npmjs.org/yargs-parser/-/yargs-parser-20.2.4.tgz", + "integrity": "sha512-WOkpgNhPTlE73h4VFAFsOnomJVaovO8VqLDzy5saChRBFQFBoMYirowyW+Q9HB4HFF4Z7VZTiG3iSzJJA29yRA==", "dev": true, "engines": { "node": ">=10" @@ -6405,6 +6487,18 @@ "node": ">=10" } }, + "node_modules/yargs-unparser/node_modules/camelcase": { + "version": "6.2.1", + "resolved": "https://registry.npmjs.org/camelcase/-/camelcase-6.2.1.tgz", + "integrity": "sha512-tVI4q5jjFV5CavAU8DXfza/TJcZutVKo/5Foskmsqcm0MsL91moHvwiGNnqaa2o6PF/7yT5ikDRcVcl8Rj6LCA==", + "dev": true, + "engines": { + "node": ">=10" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, "node_modules/yargs-unparser/node_modules/decamelize": { "version": "4.0.0", "resolved": "https://registry.npmjs.org/decamelize/-/decamelize-4.0.0.tgz", @@ -6460,20 +6554,20 @@ "devDependencies": { "@istanbuljs/nyc-config-typescript": "^1.0.2", "@node-redis/test-utils": "*", - "@types/node": "^16.11.12", + "@types/node": "^17.0.1", "@types/redis-parser": "^3.0.0", "@types/sinon": "^10.0.6", "@types/yallist": "^4.0.1", - "@typescript-eslint/eslint-plugin": "^5.7.0", - "@typescript-eslint/parser": "^5.7.0", - "eslint": "^8.4.1", + "@typescript-eslint/eslint-plugin": "^5.8.0", + "@typescript-eslint/parser": "^5.8.0", + "eslint": "^8.5.0", "nyc": "^15.1.0", "release-it": "^14.11.8", "sinon": "^12.0.1", "source-map-support": "^0.5.21", "ts-node": "^10.4.0", "typedoc": "^0.22.10", - "typescript": "^4.5.3" + "typescript": "^4.5.4" }, "engines": { "node": ">=12" @@ -6486,13 +6580,13 @@ "devDependencies": { "@istanbuljs/nyc-config-typescript": "^1.0.2", "@node-redis/test-utils": "*", - "@types/node": "^16.11.12", + "@types/node": "^17.0.1", "nyc": "^15.1.0", "release-it": "^14.11.8", "source-map-support": "^0.5.21", "ts-node": "^10.4.0", "typedoc": "^0.22.10", - "typescript": "^4.5.3" + "typescript": "^4.5.4" }, "peerDependencies": { "@node-redis/client": "^1.0.0" @@ -6505,13 +6599,13 @@ "devDependencies": { "@istanbuljs/nyc-config-typescript": "^1.0.2", "@node-redis/test-utils": "*", - "@types/node": "^16.11.12", + "@types/node": "^17.0.1", "nyc": "^15.1.0", "release-it": "^14.11.8", "source-map-support": "^0.5.21", "ts-node": "^10.4.0", "typedoc": "^0.22.10", - "typescript": "^4.5.3" + "typescript": "^4.5.4" }, "peerDependencies": { "@node-redis/client": "^1.0.0" @@ -6522,14 +6616,14 @@ "devDependencies": { "@istanbuljs/nyc-config-typescript": "^1.0.2", "@types/mocha": "^9.0.0", - "@types/node": "^16.11.12", + "@types/node": "^17.0.1", "@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.3", + "typescript": "^4.5.4", "yargs": "^17.3.0" }, "peerDependencies": { @@ -6543,13 +6637,13 @@ "devDependencies": { "@istanbuljs/nyc-config-typescript": "^1.0.2", "@node-redis/test-utils": "*", - "@types/node": "^16.11.12", + "@types/node": "^17.0.1", "nyc": "^15.1.0", "release-it": "^14.11.8", "source-map-support": "^0.5.21", "ts-node": "^10.4.0", "typedoc": "^0.22.10", - "typescript": "^4.5.3" + "typescript": "^4.5.4" }, "peerDependencies": { "@node-redis/client": "^1.0.0" @@ -6593,6 +6687,14 @@ "json5": "^2.1.2", "semver": "^6.3.0", "source-map": "^0.5.0" + }, + "dependencies": { + "semver": { + "version": "6.3.0", + "resolved": "https://registry.npmjs.org/semver/-/semver-6.3.0.tgz", + "integrity": "sha512-b39TBaTSfV6yBrapU89p5fKekE2m/NwnDocOVruQFS1/veMgdzuPcnOM34M6CwxW8jH/lxEa5rBoDeUwu5HHTw==", + "dev": true + } } }, "@babel/generator": { @@ -6616,6 +6718,14 @@ "@babel/helper-validator-option": "^7.14.5", "browserslist": "^4.17.5", "semver": "^6.3.0" + }, + "dependencies": { + "semver": { + "version": "6.3.0", + "resolved": "https://registry.npmjs.org/semver/-/semver-6.3.0.tgz", + "integrity": "sha512-b39TBaTSfV6yBrapU89p5fKekE2m/NwnDocOVruQFS1/veMgdzuPcnOM34M6CwxW8jH/lxEa5rBoDeUwu5HHTw==", + "dev": true + } } }, "@babel/helper-environment-visitor": { @@ -6768,6 +6878,12 @@ "integrity": "sha1-p9BVi9icQveV3UIyj3QIMcpTvCU=", "dev": true }, + "escape-string-regexp": { + "version": "1.0.5", + "resolved": "https://registry.npmjs.org/escape-string-regexp/-/escape-string-regexp-1.0.5.tgz", + "integrity": "sha1-G2HAViGQqN/2rjuyzwIAyhMLhtQ=", + "dev": true + }, "has-flag": { "version": "3.0.0", "resolved": "https://registry.npmjs.org/has-flag/-/has-flag-3.0.0.tgz", @@ -6875,12 +6991,6 @@ "resolved": "https://registry.npmjs.org/ignore/-/ignore-4.0.6.tgz", "integrity": "sha512-cyFDKrqc/YdcWFniJhzI42+AzS+gNwmUzOSFcRCQYwySuBBBy/KjuxWLZ/FHEH6Moq1NizMOBWyTcv8O4OZIMg==", "dev": true - }, - "strip-json-comments": { - "version": "3.1.1", - "resolved": "https://registry.npmjs.org/strip-json-comments/-/strip-json-comments-3.1.1.tgz", - "integrity": "sha512-6fPc+R4ihwqP6N/aIv2f1gMH8lOVtWQHoqC4yK6oSDVVocumAsfCqjkXnqiYMhmMwS/mEHLp7Vehlt3ql6lEig==", - "dev": true } } }, @@ -6929,11 +7039,15 @@ "sprintf-js": "~1.0.2" } }, - "camelcase": { - "version": "5.3.1", - "resolved": "https://registry.npmjs.org/camelcase/-/camelcase-5.3.1.tgz", - "integrity": "sha512-L28STB170nwWS63UjtlEOE3dldQApaJXZkOI1uMFfzf3rRuPegHaHesyee+YxQ+W6SvRDQV6UrdOdRiR153wJg==", - "dev": true + "find-up": { + "version": "4.1.0", + "resolved": "https://registry.npmjs.org/find-up/-/find-up-4.1.0.tgz", + "integrity": "sha512-PpOwAdQ/YlXQ2vj8a3h8IipDuYRi3wceVQQGYWxNINccq40Anw7BlsEXCMbt1Zt+OLA6Fq9suIpIWD0OsnISlw==", + "dev": true, + "requires": { + "locate-path": "^5.0.0", + "path-exists": "^4.0.0" + } }, "js-yaml": { "version": "3.14.1", @@ -6945,6 +7059,33 @@ "esprima": "^4.0.0" } }, + "locate-path": { + "version": "5.0.0", + "resolved": "https://registry.npmjs.org/locate-path/-/locate-path-5.0.0.tgz", + "integrity": "sha512-t7hw9pI+WvuwNJXwk5zVHpyhIqzg2qTlklJOf0mVxGSbe3Fp2VieZcduNYjaLDoy6p9uGpQEGWG87WpMKlNq8g==", + "dev": true, + "requires": { + "p-locate": "^4.1.0" + } + }, + "p-limit": { + "version": "2.3.0", + "resolved": "https://registry.npmjs.org/p-limit/-/p-limit-2.3.0.tgz", + "integrity": "sha512-//88mFWSJx8lxCzwdAABTJL2MyWB12+eIY7MDL2SqLmAkeKU9qxRvWuSyTjm3FUmpBEMuFfckAIqEaVGUDxb6w==", + "dev": true, + "requires": { + "p-try": "^2.0.0" + } + }, + "p-locate": { + "version": "4.1.0", + "resolved": "https://registry.npmjs.org/p-locate/-/p-locate-4.1.0.tgz", + "integrity": "sha512-R79ZZ/0wAxKGu3oYMlz8jy/kbhsNrS7SKZ7PxEHBgJ5+F2mtFW2fK2cOtBh1cHYkQsbzFV7I+EoRKe6Yt0oK7A==", + "dev": true, + "requires": { + "p-limit": "^2.2.0" + } + }, "resolve-from": { "version": "5.0.0", "resolved": "https://registry.npmjs.org/resolve-from/-/resolve-from-5.0.0.tgz", @@ -6973,14 +7114,14 @@ "requires": { "@istanbuljs/nyc-config-typescript": "^1.0.2", "@node-redis/test-utils": "*", - "@types/node": "^16.11.12", + "@types/node": "^17.0.1", "@types/redis-parser": "^3.0.0", "@types/sinon": "^10.0.6", "@types/yallist": "^4.0.1", - "@typescript-eslint/eslint-plugin": "^5.7.0", - "@typescript-eslint/parser": "^5.7.0", + "@typescript-eslint/eslint-plugin": "^5.8.0", + "@typescript-eslint/parser": "^5.8.0", "cluster-key-slot": "1.1.0", - "eslint": "^8.4.1", + "eslint": "^8.5.0", "generic-pool": "3.8.2", "nyc": "^15.1.0", "redis-parser": "3.0.0", @@ -6989,7 +7130,7 @@ "source-map-support": "^0.5.21", "ts-node": "^10.4.0", "typedoc": "^0.22.10", - "typescript": "^4.5.3", + "typescript": "^4.5.4", "yallist": "4.0.0" } }, @@ -6998,13 +7139,13 @@ "requires": { "@istanbuljs/nyc-config-typescript": "^1.0.2", "@node-redis/test-utils": "*", - "@types/node": "^16.11.12", + "@types/node": "^17.0.1", "nyc": "^15.1.0", "release-it": "^14.11.8", "source-map-support": "^0.5.21", "ts-node": "^10.4.0", "typedoc": "^0.22.10", - "typescript": "^4.5.3" + "typescript": "^4.5.4" } }, "@node-redis/search": { @@ -7012,13 +7153,13 @@ "requires": { "@istanbuljs/nyc-config-typescript": "^1.0.2", "@node-redis/test-utils": "*", - "@types/node": "^16.11.12", + "@types/node": "^17.0.1", "nyc": "^15.1.0", "release-it": "^14.11.8", "source-map-support": "^0.5.21", "ts-node": "^10.4.0", "typedoc": "^0.22.10", - "typescript": "^4.5.3" + "typescript": "^4.5.4" } }, "@node-redis/test-utils": { @@ -7026,14 +7167,14 @@ "requires": { "@istanbuljs/nyc-config-typescript": "^1.0.2", "@types/mocha": "^9.0.0", - "@types/node": "^16.11.12", + "@types/node": "^17.0.1", "@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.3", + "typescript": "^4.5.4", "yargs": "^17.3.0" } }, @@ -7042,13 +7183,13 @@ "requires": { "@istanbuljs/nyc-config-typescript": "^1.0.2", "@node-redis/test-utils": "*", - "@types/node": "^16.11.12", + "@types/node": "^17.0.1", "nyc": "^15.1.0", "release-it": "^14.11.8", "source-map-support": "^0.5.21", "ts-node": "^10.4.0", "typedoc": "^0.22.10", - "typescript": "^4.5.3" + "typescript": "^4.5.4" } }, "@nodelib/fs.scandir": { @@ -7315,9 +7456,9 @@ "dev": true }, "@types/node": { - "version": "16.11.13", - "resolved": "https://registry.npmjs.org/@types/node/-/node-16.11.13.tgz", - "integrity": "sha512-eUXZzHLHoZqj1frtUetNkUetYoJ6X55UmrVnFD4DMhVeAmwLjniZhtBmsRiemQh4uq4G3vUra/Ws/hs9vEvL3Q==", + "version": "17.0.1", + "resolved": "https://registry.npmjs.org/@types/node/-/node-17.0.1.tgz", + "integrity": "sha512-NXKvBVUzIbs6ylBwmOwHFkZS2EXCcjnqr8ZCRNaXBkHAf+3mn/rPcJxwrzuc6movh8fxQAsUUfYklJ/EG+hZqQ==", "dev": true }, "@types/parse-json": { @@ -7382,127 +7523,85 @@ "dev": true }, "@typescript-eslint/eslint-plugin": { - "version": "5.7.0", - "resolved": "https://registry.npmjs.org/@typescript-eslint/eslint-plugin/-/eslint-plugin-5.7.0.tgz", - "integrity": "sha512-8RTGBpNn5a9M628wBPrCbJ+v3YTEOE2qeZb7TDkGKTDXSj36KGRg92SpFFaR/0S3rSXQxM0Og/kV9EyadsYSBg==", + "version": "5.8.0", + "resolved": "https://registry.npmjs.org/@typescript-eslint/eslint-plugin/-/eslint-plugin-5.8.0.tgz", + "integrity": "sha512-spu1UW7QuBn0nJ6+psnfCc3iVoQAifjKORgBngKOmC8U/1tbe2YJMzYQqDGYB4JCss7L8+RM2kKLb1B1Aw9BNA==", "dev": true, "requires": { - "@typescript-eslint/experimental-utils": "5.7.0", - "@typescript-eslint/scope-manager": "5.7.0", + "@typescript-eslint/experimental-utils": "5.8.0", + "@typescript-eslint/scope-manager": "5.8.0", "debug": "^4.3.2", "functional-red-black-tree": "^1.0.1", "ignore": "^5.1.8", "regexpp": "^3.2.0", "semver": "^7.3.5", "tsutils": "^3.21.0" - }, - "dependencies": { - "semver": { - "version": "7.3.5", - "resolved": "https://registry.npmjs.org/semver/-/semver-7.3.5.tgz", - "integrity": "sha512-PoeGJYh8HK4BTO/a9Tf6ZG3veo/A7ZVsYrSA6J8ny9nb3B1VrpkuN+z9OE5wfE5p6H4LchYZsegiQgbJD94ZFQ==", - "dev": true, - "requires": { - "lru-cache": "^6.0.0" - } - } } }, "@typescript-eslint/experimental-utils": { - "version": "5.7.0", - "resolved": "https://registry.npmjs.org/@typescript-eslint/experimental-utils/-/experimental-utils-5.7.0.tgz", - "integrity": "sha512-u57eZ5FbEpzN5kSjmVrSesovWslH2ZyNPnaXQMXWgH57d5+EVHEt76W75vVuI9qKZ5BMDKNfRN+pxcPEjQjb2A==", + "version": "5.8.0", + "resolved": "https://registry.npmjs.org/@typescript-eslint/experimental-utils/-/experimental-utils-5.8.0.tgz", + "integrity": "sha512-KN5FvNH71bhZ8fKtL+lhW7bjm7cxs1nt+hrDZWIqb6ViCffQcWyLunGrgvISgkRojIDcXIsH+xlFfI4RCDA0xA==", "dev": true, "requires": { "@types/json-schema": "^7.0.9", - "@typescript-eslint/scope-manager": "5.7.0", - "@typescript-eslint/types": "5.7.0", - "@typescript-eslint/typescript-estree": "5.7.0", + "@typescript-eslint/scope-manager": "5.8.0", + "@typescript-eslint/types": "5.8.0", + "@typescript-eslint/typescript-estree": "5.8.0", "eslint-scope": "^5.1.1", "eslint-utils": "^3.0.0" } }, "@typescript-eslint/parser": { - "version": "5.7.0", - "resolved": "https://registry.npmjs.org/@typescript-eslint/parser/-/parser-5.7.0.tgz", - "integrity": "sha512-m/gWCCcS4jXw6vkrPQ1BjZ1vomP01PArgzvauBqzsoZ3urLbsRChexB8/YV8z9HwE3qlJM35FxfKZ1nfP/4x8g==", + "version": "5.8.0", + "resolved": "https://registry.npmjs.org/@typescript-eslint/parser/-/parser-5.8.0.tgz", + "integrity": "sha512-Gleacp/ZhRtJRYs5/T8KQR3pAQjQI89Dn/k+OzyCKOsLiZH2/Vh60cFBTnFsHNI6WAD+lNUo/xGZ4NeA5u0Ipw==", "dev": true, "requires": { - "@typescript-eslint/scope-manager": "5.7.0", - "@typescript-eslint/types": "5.7.0", - "@typescript-eslint/typescript-estree": "5.7.0", + "@typescript-eslint/scope-manager": "5.8.0", + "@typescript-eslint/types": "5.8.0", + "@typescript-eslint/typescript-estree": "5.8.0", "debug": "^4.3.2" } }, "@typescript-eslint/scope-manager": { - "version": "5.7.0", - "resolved": "https://registry.npmjs.org/@typescript-eslint/scope-manager/-/scope-manager-5.7.0.tgz", - "integrity": "sha512-7mxR520DGq5F7sSSgM0HSSMJ+TFUymOeFRMfUfGFAVBv8BR+Jv1vHgAouYUvWRZeszVBJlLcc9fDdktxb5kmxA==", + "version": "5.8.0", + "resolved": "https://registry.npmjs.org/@typescript-eslint/scope-manager/-/scope-manager-5.8.0.tgz", + "integrity": "sha512-x82CYJsLOjPCDuFFEbS6e7K1QEWj7u5Wk1alw8A+gnJiYwNnDJk0ib6PCegbaPMjrfBvFKa7SxE3EOnnIQz2Gg==", "dev": true, "requires": { - "@typescript-eslint/types": "5.7.0", - "@typescript-eslint/visitor-keys": "5.7.0" + "@typescript-eslint/types": "5.8.0", + "@typescript-eslint/visitor-keys": "5.8.0" } }, "@typescript-eslint/types": { - "version": "5.7.0", - "resolved": "https://registry.npmjs.org/@typescript-eslint/types/-/types-5.7.0.tgz", - "integrity": "sha512-5AeYIF5p2kAneIpnLFve8g50VyAjq7udM7ApZZ9JYjdPjkz0LvODfuSHIDUVnIuUoxafoWzpFyU7Sqbxgi79mA==", + "version": "5.8.0", + "resolved": "https://registry.npmjs.org/@typescript-eslint/types/-/types-5.8.0.tgz", + "integrity": "sha512-LdCYOqeqZWqCMOmwFnum6YfW9F3nKuxJiR84CdIRN5nfHJ7gyvGpXWqL/AaW0k3Po0+wm93ARAsOdzlZDPCcXg==", "dev": true }, "@typescript-eslint/typescript-estree": { - "version": "5.7.0", - "resolved": "https://registry.npmjs.org/@typescript-eslint/typescript-estree/-/typescript-estree-5.7.0.tgz", - "integrity": "sha512-aO1Ql+izMrTnPj5aFFlEJkpD4jRqC4Gwhygu2oHK2wfVQpmOPbyDSveJ+r/NQo+PWV43M6uEAeLVbTi09dFLhg==", + "version": "5.8.0", + "resolved": "https://registry.npmjs.org/@typescript-eslint/typescript-estree/-/typescript-estree-5.8.0.tgz", + "integrity": "sha512-srfeZ3URdEcUsSLbkOFqS7WoxOqn8JNil2NSLO9O+I2/Uyc85+UlfpEvQHIpj5dVts7KKOZnftoJD/Fdv0L7nQ==", "dev": true, "requires": { - "@typescript-eslint/types": "5.7.0", - "@typescript-eslint/visitor-keys": "5.7.0", + "@typescript-eslint/types": "5.8.0", + "@typescript-eslint/visitor-keys": "5.8.0", "debug": "^4.3.2", "globby": "^11.0.4", "is-glob": "^4.0.3", "semver": "^7.3.5", "tsutils": "^3.21.0" - }, - "dependencies": { - "array-union": { - "version": "2.1.0", - "resolved": "https://registry.npmjs.org/array-union/-/array-union-2.1.0.tgz", - "integrity": "sha512-HGyxoOTYUyCM6stUe6EJgnd4EoewAI7zMdfqO+kGjnlZmBDz/cR5pf8r/cR4Wq60sL/p0IkcjUEEPwS3GFrIyw==", - "dev": true - }, - "globby": { - "version": "11.0.4", - "resolved": "https://registry.npmjs.org/globby/-/globby-11.0.4.tgz", - "integrity": "sha512-9O4MVG9ioZJ08ffbcyVYyLOJLk5JQ688pJ4eMGLpdWLHq/Wr1D9BlriLQyL0E+jbkuePVZXYFj47QM/v093wHg==", - "dev": true, - "requires": { - "array-union": "^2.1.0", - "dir-glob": "^3.0.1", - "fast-glob": "^3.1.1", - "ignore": "^5.1.4", - "merge2": "^1.3.0", - "slash": "^3.0.0" - } - }, - "semver": { - "version": "7.3.5", - "resolved": "https://registry.npmjs.org/semver/-/semver-7.3.5.tgz", - "integrity": "sha512-PoeGJYh8HK4BTO/a9Tf6ZG3veo/A7ZVsYrSA6J8ny9nb3B1VrpkuN+z9OE5wfE5p6H4LchYZsegiQgbJD94ZFQ==", - "dev": true, - "requires": { - "lru-cache": "^6.0.0" - } - } } }, "@typescript-eslint/visitor-keys": { - "version": "5.7.0", - "resolved": "https://registry.npmjs.org/@typescript-eslint/visitor-keys/-/visitor-keys-5.7.0.tgz", - "integrity": "sha512-hdohahZ4lTFcglZSJ3DGdzxQHBSxsLVqHzkiOmKi7xVAWC4y2c1bIMKmPJSrA4aOEoRUPOKQ87Y/taC7yVHpFg==", + "version": "5.8.0", + "resolved": "https://registry.npmjs.org/@typescript-eslint/visitor-keys/-/visitor-keys-5.8.0.tgz", + "integrity": "sha512-+HDIGOEMnqbxdAHegxvnOqESUH6RWFRR2b8qxP1W9CZnnYh4Usz6MBL+2KMAgPk/P0o9c1HqnYtwzVH6GTIqug==", "dev": true, "requires": { - "@typescript-eslint/types": "5.7.0", + "@typescript-eslint/types": "5.8.0", "eslint-visitor-keys": "^3.0.0" } }, @@ -7575,6 +7674,14 @@ "dev": true, "requires": { "type-fest": "^0.21.3" + }, + "dependencies": { + "type-fest": { + "version": "0.21.3", + "resolved": "https://registry.npmjs.org/type-fest/-/type-fest-0.21.3.tgz", + "integrity": "sha512-t0rzBq87m3fVcduHDUFhKmyyX+9eo6WQjZvf51Ea/M0Q7+T374Jp1aUiyUl0GKxp8M/OETVHSDvmkyPgvX+X2w==", + "dev": true + } } }, "ansi-regex": { @@ -7630,13 +7737,10 @@ "dev": true }, "array-union": { - "version": "1.0.2", - "resolved": "https://registry.npmjs.org/array-union/-/array-union-1.0.2.tgz", - "integrity": "sha1-mjRBDk9OPaI96jdb5b5w8kd47Dk=", - "dev": true, - "requires": { - "array-uniq": "^1.0.1" - } + "version": "2.1.0", + "resolved": "https://registry.npmjs.org/array-union/-/array-union-2.1.0.tgz", + "integrity": "sha512-HGyxoOTYUyCM6stUe6EJgnd4EoewAI7zMdfqO+kGjnlZmBDz/cR5pf8r/cR4Wq60sL/p0IkcjUEEPwS3GFrIyw==", + "dev": true }, "array-uniq": { "version": "1.0.3", @@ -7719,10 +7823,10 @@ "wrap-ansi": "^7.0.0" }, "dependencies": { - "type-fest": { - "version": "0.20.2", - "resolved": "https://registry.npmjs.org/type-fest/-/type-fest-0.20.2.tgz", - "integrity": "sha512-Ne+eE4r0/iWnpAxD852z3A+N0Bt5RN//NjJwRd2VFHEmrywxf5vsZlh4R6lixl6B+wz/8d+maTSAkN1FIkI3LQ==", + "camelcase": { + "version": "6.2.1", + "resolved": "https://registry.npmjs.org/camelcase/-/camelcase-6.2.1.tgz", + "integrity": "sha512-tVI4q5jjFV5CavAU8DXfza/TJcZutVKo/5Foskmsqcm0MsL91moHvwiGNnqaa2o6PF/7yT5ikDRcVcl8Rj6LCA==", "dev": true } } @@ -7842,15 +7946,15 @@ "dev": true }, "camelcase": { - "version": "6.2.1", - "resolved": "https://registry.npmjs.org/camelcase/-/camelcase-6.2.1.tgz", - "integrity": "sha512-tVI4q5jjFV5CavAU8DXfza/TJcZutVKo/5Foskmsqcm0MsL91moHvwiGNnqaa2o6PF/7yT5ikDRcVcl8Rj6LCA==", + "version": "5.3.1", + "resolved": "https://registry.npmjs.org/camelcase/-/camelcase-5.3.1.tgz", + "integrity": "sha512-L28STB170nwWS63UjtlEOE3dldQApaJXZkOI1uMFfzf3rRuPegHaHesyee+YxQ+W6SvRDQV6UrdOdRiR153wJg==", "dev": true }, "caniuse-lite": { - "version": "1.0.30001286", - "resolved": "https://registry.npmjs.org/caniuse-lite/-/caniuse-lite-1.0.30001286.tgz", - "integrity": "sha512-zaEMRH6xg8ESMi2eQ3R4eZ5qw/hJiVsO/HlLwniIwErij0JDr9P+8V4dtx1l+kLq6j3yy8l8W4fst1lBnat5wQ==", + "version": "1.0.30001291", + "resolved": "https://registry.npmjs.org/caniuse-lite/-/caniuse-lite-1.0.30001291.tgz", + "integrity": "sha512-roMV5V0HNGgJ88s42eE70sstqGW/gwFndosYrikHthw98N5tLnOTxFqMLQjZVRxTWFlJ4rn+MsgXrR7MDPY4jA==", "dev": true }, "chalk": { @@ -7883,6 +7987,17 @@ "is-glob": "~4.0.1", "normalize-path": "~3.0.0", "readdirp": "~3.6.0" + }, + "dependencies": { + "glob-parent": { + "version": "5.1.2", + "resolved": "https://registry.npmjs.org/glob-parent/-/glob-parent-5.1.2.tgz", + "integrity": "sha512-AOIgSQCepiJYwP3ARnGx+5VnTu2HBYdzbGP45eLw1vr3zB3vZLeyed1sC9hnbcOc9/SrMyM5RPQrkGz4aS9Zow==", + "dev": true, + "requires": { + "is-glob": "^4.0.1" + } + } } }, "ci-info": { @@ -8018,14 +8133,6 @@ "dev": true, "requires": { "safe-buffer": "~5.1.1" - }, - "dependencies": { - "safe-buffer": { - "version": "5.1.2", - "resolved": "https://registry.npmjs.org/safe-buffer/-/safe-buffer-5.1.2.tgz", - "integrity": "sha512-Gd2UZBJDkXlY7GbJxfsE8/nvKkUEU1G38c1siN6QP6a9PT9MmHB8GnpscSmMJSoF8LOIrt8ud/wPtojys4G6+g==", - "dev": true - } } }, "cosmiconfig": { @@ -8065,9 +8172,9 @@ "dev": true }, "debug": { - "version": "4.3.2", - "resolved": "https://registry.npmjs.org/debug/-/debug-4.3.2.tgz", - "integrity": "sha512-mOp8wKcvj7XxC78zLgw/ZA+6TSgkoE2C/ienthhRD298T7UNwAg9diBpLRxC0mOezLl4B0xV7M0cCO6P/O0Xhw==", + "version": "4.3.3", + "resolved": "https://registry.npmjs.org/debug/-/debug-4.3.3.tgz", + "integrity": "sha512-/zxw5+vh1Tfv+4Qn7a5nsbcJKPaSvCDhojn6FEl9vupwK2VCSDtEiEtqr8DFtzYFOdz63LBkxec7DYuc2jon6Q==", "dev": true, "requires": { "ms": "2.1.2" @@ -8200,9 +8307,9 @@ "dev": true }, "electron-to-chromium": { - "version": "1.4.19", - "resolved": "https://registry.npmjs.org/electron-to-chromium/-/electron-to-chromium-1.4.19.tgz", - "integrity": "sha512-TeAjwsC/vhvxEtX/xN1JQUMkl+UrwKXlB4rwLyuLYVuBuRtqJJrU4Jy5pCVihMQg4m1ceZ3MEJ0yYuxHj8vC+w==", + "version": "1.4.24", + "resolved": "https://registry.npmjs.org/electron-to-chromium/-/electron-to-chromium-1.4.24.tgz", + "integrity": "sha512-erwx5r69B/WFfFuF2jcNN0817BfDBdC4765kQ6WltOMuwsimlQo3JTEq0Cle+wpHralwdeX3OfAtw/mHxPK0Wg==", "dev": true }, "email-addresses": { @@ -8263,15 +8370,15 @@ "dev": true }, "escape-string-regexp": { - "version": "1.0.5", - "resolved": "https://registry.npmjs.org/escape-string-regexp/-/escape-string-regexp-1.0.5.tgz", - "integrity": "sha1-G2HAViGQqN/2rjuyzwIAyhMLhtQ=", + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/escape-string-regexp/-/escape-string-regexp-4.0.0.tgz", + "integrity": "sha512-TtpcNJ3XAzx3Gq8sWRzJaVajRs0uVxA2YAkdb1jm2YkPz4G6egUFAyA3n5vtEIZefPk5Wa4UXbKuS5fKkJWdgA==", "dev": true }, "eslint": { - "version": "8.4.1", - "resolved": "https://registry.npmjs.org/eslint/-/eslint-8.4.1.tgz", - "integrity": "sha512-TxU/p7LB1KxQ6+7aztTnO7K0i+h0tDi81YRY9VzB6Id71kNz+fFYnf5HD5UOQmxkzcoa0TlVZf9dpMtUv0GpWg==", + "version": "8.5.0", + "resolved": "https://registry.npmjs.org/eslint/-/eslint-8.5.0.tgz", + "integrity": "sha512-tVGSkgNbOfiHyVte8bCM8OmX+xG9PzVG/B4UCF60zx7j61WIVY/AqJECDgpLD4DbbESD0e174gOg3ZlrX15GDg==", "dev": true, "requires": { "@eslint/eslintrc": "^1.0.5", @@ -8314,12 +8421,6 @@ "v8-compile-cache": "^2.0.3" }, "dependencies": { - "escape-string-regexp": { - "version": "4.0.0", - "resolved": "https://registry.npmjs.org/escape-string-regexp/-/escape-string-regexp-4.0.0.tgz", - "integrity": "sha512-TtpcNJ3XAzx3Gq8sWRzJaVajRs0uVxA2YAkdb1jm2YkPz4G6egUFAyA3n5vtEIZefPk5Wa4UXbKuS5fKkJWdgA==", - "dev": true - }, "eslint-scope": { "version": "7.1.0", "resolved": "https://registry.npmjs.org/eslint-scope/-/eslint-scope-7.1.0.tgz", @@ -8336,35 +8437,11 @@ "integrity": "sha512-MMdARuVEQziNTeJD8DgMqmhwR11BRQ/cBP+pLtYdSTnf3MIO8fFeiINEbX36ZdNlfU/7A9f3gUw49B3oQsvwBA==", "dev": true }, - "glob-parent": { - "version": "6.0.2", - "resolved": "https://registry.npmjs.org/glob-parent/-/glob-parent-6.0.2.tgz", - "integrity": "sha512-XxwI8EOhVQgWp6iDL+3b0r86f4d6AX6zSU55HfB4ydCEuXLXc5FcYeOu+nnGftS4TEju/11rt4KJPTMgbfmv4A==", - "dev": true, - "requires": { - "is-glob": "^4.0.3" - } - }, "ignore": { "version": "4.0.6", "resolved": "https://registry.npmjs.org/ignore/-/ignore-4.0.6.tgz", "integrity": "sha512-cyFDKrqc/YdcWFniJhzI42+AzS+gNwmUzOSFcRCQYwySuBBBy/KjuxWLZ/FHEH6Moq1NizMOBWyTcv8O4OZIMg==", "dev": true - }, - "semver": { - "version": "7.3.5", - "resolved": "https://registry.npmjs.org/semver/-/semver-7.3.5.tgz", - "integrity": "sha512-PoeGJYh8HK4BTO/a9Tf6ZG3veo/A7ZVsYrSA6J8ny9nb3B1VrpkuN+z9OE5wfE5p6H4LchYZsegiQgbJD94ZFQ==", - "dev": true, - "requires": { - "lru-cache": "^6.0.0" - } - }, - "strip-json-comments": { - "version": "3.1.1", - "resolved": "https://registry.npmjs.org/strip-json-comments/-/strip-json-comments-3.1.1.tgz", - "integrity": "sha512-6fPc+R4ihwqP6N/aIv2f1gMH8lOVtWQHoqC4yK6oSDVVocumAsfCqjkXnqiYMhmMwS/mEHLp7Vehlt3ql6lEig==", - "dev": true } } }, @@ -8509,6 +8586,17 @@ "glob-parent": "^5.1.2", "merge2": "^1.3.0", "micromatch": "^4.0.4" + }, + "dependencies": { + "glob-parent": { + "version": "5.1.2", + "resolved": "https://registry.npmjs.org/glob-parent/-/glob-parent-5.1.2.tgz", + "integrity": "sha512-AOIgSQCepiJYwP3ARnGx+5VnTu2HBYdzbGP45eLw1vr3zB3vZLeyed1sC9hnbcOc9/SrMyM5RPQrkGz4aS9Zow==", + "dev": true, + "requires": { + "is-glob": "^4.0.1" + } + } } }, "fast-json-stable-stringify": { @@ -8539,6 +8627,14 @@ "dev": true, "requires": { "escape-string-regexp": "^1.0.5" + }, + "dependencies": { + "escape-string-regexp": { + "version": "1.0.5", + "resolved": "https://registry.npmjs.org/escape-string-regexp/-/escape-string-regexp-1.0.5.tgz", + "integrity": "sha1-G2HAViGQqN/2rjuyzwIAyhMLhtQ=", + "dev": true + } } }, "file-entry-cache": { @@ -8594,12 +8690,12 @@ } }, "find-up": { - "version": "4.1.0", - "resolved": "https://registry.npmjs.org/find-up/-/find-up-4.1.0.tgz", - "integrity": "sha512-PpOwAdQ/YlXQ2vj8a3h8IipDuYRi3wceVQQGYWxNINccq40Anw7BlsEXCMbt1Zt+OLA6Fq9suIpIWD0OsnISlw==", + "version": "5.0.0", + "resolved": "https://registry.npmjs.org/find-up/-/find-up-5.0.0.tgz", + "integrity": "sha512-78/PXT1wlLLDgTzDs7sjq9hzz0vXD+zn+7wypEe4fXQxCmdmqfGsEPQxmiCSQI3ajFV91bVSsvNtrJRiW6nGng==", "dev": true, "requires": { - "locate-path": "^5.0.0", + "locate-path": "^6.0.0", "path-exists": "^4.0.0" } }, @@ -8741,6 +8837,30 @@ "find-cache-dir": "^3.3.1", "fs-extra": "^8.1.0", "globby": "^6.1.0" + }, + "dependencies": { + "array-union": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/array-union/-/array-union-1.0.2.tgz", + "integrity": "sha1-mjRBDk9OPaI96jdb5b5w8kd47Dk=", + "dev": true, + "requires": { + "array-uniq": "^1.0.1" + } + }, + "globby": { + "version": "6.1.0", + "resolved": "https://registry.npmjs.org/globby/-/globby-6.1.0.tgz", + "integrity": "sha1-9abXDoOV4hyFj7BInWTfAkJNUGw=", + "dev": true, + "requires": { + "array-union": "^1.0.1", + "glob": "^7.0.3", + "object-assign": "^4.0.1", + "pify": "^2.0.0", + "pinkie-promise": "^2.0.0" + } + } } }, "git-up": { @@ -8763,9 +8883,9 @@ } }, "glob": { - "version": "7.2.0", - "resolved": "https://registry.npmjs.org/glob/-/glob-7.2.0.tgz", - "integrity": "sha512-lmLf6gtyrPq8tTjSmrO94wBeQbFR3HbLHbuyD69wuyQkImp2hWqMGB47OX65FBkPffO641IP9jWa1z4ivqG26Q==", + "version": "7.1.7", + "resolved": "https://registry.npmjs.org/glob/-/glob-7.1.7.tgz", + "integrity": "sha512-OvD9ENzPLbegENnYP5UUfJIirTg4+XwMWGaQfQTY0JenxNvvIKP3U3/tAQSPIu/lHxXYSZmpXlUHeqAIdKzBLQ==", "dev": true, "requires": { "fs.realpath": "^1.0.0", @@ -8777,12 +8897,12 @@ } }, "glob-parent": { - "version": "5.1.2", - "resolved": "https://registry.npmjs.org/glob-parent/-/glob-parent-5.1.2.tgz", - "integrity": "sha512-AOIgSQCepiJYwP3ARnGx+5VnTu2HBYdzbGP45eLw1vr3zB3vZLeyed1sC9hnbcOc9/SrMyM5RPQrkGz4aS9Zow==", + "version": "6.0.2", + "resolved": "https://registry.npmjs.org/glob-parent/-/glob-parent-6.0.2.tgz", + "integrity": "sha512-XxwI8EOhVQgWp6iDL+3b0r86f4d6AX6zSU55HfB4ydCEuXLXc5FcYeOu+nnGftS4TEju/11rt4KJPTMgbfmv4A==", "dev": true, "requires": { - "is-glob": "^4.0.1" + "is-glob": "^4.0.3" } }, "global-dirs": { @@ -8801,27 +8921,20 @@ "dev": true, "requires": { "type-fest": "^0.20.2" - }, - "dependencies": { - "type-fest": { - "version": "0.20.2", - "resolved": "https://registry.npmjs.org/type-fest/-/type-fest-0.20.2.tgz", - "integrity": "sha512-Ne+eE4r0/iWnpAxD852z3A+N0Bt5RN//NjJwRd2VFHEmrywxf5vsZlh4R6lixl6B+wz/8d+maTSAkN1FIkI3LQ==", - "dev": true - } } }, - "globby": { - "version": "6.1.0", - "resolved": "https://registry.npmjs.org/globby/-/globby-6.1.0.tgz", - "integrity": "sha1-9abXDoOV4hyFj7BInWTfAkJNUGw=", + "globby": { + "version": "11.0.4", + "resolved": "https://registry.npmjs.org/globby/-/globby-11.0.4.tgz", + "integrity": "sha512-9O4MVG9ioZJ08ffbcyVYyLOJLk5JQ688pJ4eMGLpdWLHq/Wr1D9BlriLQyL0E+jbkuePVZXYFj47QM/v093wHg==", "dev": true, "requires": { - "array-union": "^1.0.1", - "glob": "^7.0.3", - "object-assign": "^4.0.1", - "pify": "^2.0.0", - "pinkie-promise": "^2.0.0" + "array-union": "^2.1.0", + "dir-glob": "^3.0.1", + "fast-glob": "^3.1.1", + "ignore": "^5.1.4", + "merge2": "^1.3.0", + "slash": "^3.0.0" } }, "got": { @@ -8950,9 +9063,9 @@ "dev": true }, "ignore": { - "version": "5.1.9", - "resolved": "https://registry.npmjs.org/ignore/-/ignore-5.1.9.tgz", - "integrity": "sha512-2zeMQpbKz5dhZ9IwL0gbxSW5w0NK/MSAMtNuhgIHEPmaU3vPdKPL0UdvUCXs5SS4JAwsBxysK5sFMW8ocFiVjQ==", + "version": "5.2.0", + "resolved": "https://registry.npmjs.org/ignore/-/ignore-5.2.0.tgz", + "integrity": "sha512-CmxgYGiEPCLhfLnpPp1MoRmifwEIOgjcHXxOBjv7mY96c+eWScsOP9c112ZyLdWHi0FxHjI+4uVhKYp/gcdRmQ==", "dev": true }, "import-cwd": { @@ -9256,6 +9369,14 @@ "@istanbuljs/schema": "^0.1.2", "istanbul-lib-coverage": "^3.0.0", "semver": "^6.3.0" + }, + "dependencies": { + "semver": { + "version": "6.3.0", + "resolved": "https://registry.npmjs.org/semver/-/semver-6.3.0.tgz", + "integrity": "sha512-b39TBaTSfV6yBrapU89p5fKekE2m/NwnDocOVruQFS1/veMgdzuPcnOM34M6CwxW8jH/lxEa5rBoDeUwu5HHTw==", + "dev": true + } } }, "istanbul-lib-processinfo": { @@ -9271,14 +9392,6 @@ "p-map": "^3.0.0", "rimraf": "^3.0.0", "uuid": "^3.3.3" - }, - "dependencies": { - "uuid": { - "version": "3.4.0", - "resolved": "https://registry.npmjs.org/uuid/-/uuid-3.4.0.tgz", - "integrity": "sha512-HjSDRw6gZE5JMggctHBcjVak08+KEVhSIiDzFnT9S9aegmp85S/bReBVTb4QTFaRNptJ9kuYaNhnbNEOkbKb/A==", - "dev": true - } } }, "istanbul-lib-report": { @@ -9431,12 +9544,12 @@ "dev": true }, "locate-path": { - "version": "5.0.0", - "resolved": "https://registry.npmjs.org/locate-path/-/locate-path-5.0.0.tgz", - "integrity": "sha512-t7hw9pI+WvuwNJXwk5zVHpyhIqzg2qTlklJOf0mVxGSbe3Fp2VieZcduNYjaLDoy6p9uGpQEGWG87WpMKlNq8g==", + "version": "6.0.0", + "resolved": "https://registry.npmjs.org/locate-path/-/locate-path-6.0.0.tgz", + "integrity": "sha512-iPZK6eYjbxRu3uB4/WZ3EsEIMJFMqAoopl3R+zuq0UjcAm/MO6KCweDgPfP3elTztoKP3KtnVHxTn2NHBSDVUw==", "dev": true, "requires": { - "p-locate": "^4.1.0" + "p-locate": "^5.0.0" } }, "lodash": { @@ -9507,6 +9620,14 @@ "dev": true, "requires": { "semver": "^6.0.0" + }, + "dependencies": { + "semver": { + "version": "6.3.0", + "resolved": "https://registry.npmjs.org/semver/-/semver-6.3.0.tgz", + "integrity": "sha512-b39TBaTSfV6yBrapU89p5fKekE2m/NwnDocOVruQFS1/veMgdzuPcnOM34M6CwxW8jH/lxEa5rBoDeUwu5HHTw==", + "dev": true + } } }, "make-error": { @@ -9617,43 +9738,21 @@ "yargs-unparser": "2.0.0" }, "dependencies": { - "escape-string-regexp": { - "version": "4.0.0", - "resolved": "https://registry.npmjs.org/escape-string-regexp/-/escape-string-regexp-4.0.0.tgz", - "integrity": "sha512-TtpcNJ3XAzx3Gq8sWRzJaVajRs0uVxA2YAkdb1jm2YkPz4G6egUFAyA3n5vtEIZefPk5Wa4UXbKuS5fKkJWdgA==", - "dev": true - }, - "find-up": { - "version": "5.0.0", - "resolved": "https://registry.npmjs.org/find-up/-/find-up-5.0.0.tgz", - "integrity": "sha512-78/PXT1wlLLDgTzDs7sjq9hzz0vXD+zn+7wypEe4fXQxCmdmqfGsEPQxmiCSQI3ajFV91bVSsvNtrJRiW6nGng==", - "dev": true, - "requires": { - "locate-path": "^6.0.0", - "path-exists": "^4.0.0" - } - }, - "glob": { - "version": "7.1.7", - "resolved": "https://registry.npmjs.org/glob/-/glob-7.1.7.tgz", - "integrity": "sha512-OvD9ENzPLbegENnYP5UUfJIirTg4+XwMWGaQfQTY0JenxNvvIKP3U3/tAQSPIu/lHxXYSZmpXlUHeqAIdKzBLQ==", - "dev": true, - "requires": { - "fs.realpath": "^1.0.0", - "inflight": "^1.0.4", - "inherits": "2", - "minimatch": "^3.0.4", - "once": "^1.3.0", - "path-is-absolute": "^1.0.0" - } - }, - "locate-path": { - "version": "6.0.0", - "resolved": "https://registry.npmjs.org/locate-path/-/locate-path-6.0.0.tgz", - "integrity": "sha512-iPZK6eYjbxRu3uB4/WZ3EsEIMJFMqAoopl3R+zuq0UjcAm/MO6KCweDgPfP3elTztoKP3KtnVHxTn2NHBSDVUw==", + "debug": { + "version": "4.3.2", + "resolved": "https://registry.npmjs.org/debug/-/debug-4.3.2.tgz", + "integrity": "sha512-mOp8wKcvj7XxC78zLgw/ZA+6TSgkoE2C/ienthhRD298T7UNwAg9diBpLRxC0mOezLl4B0xV7M0cCO6P/O0Xhw==", "dev": true, "requires": { - "p-locate": "^5.0.0" + "ms": "2.1.2" + }, + "dependencies": { + "ms": { + "version": "2.1.2", + "resolved": "https://registry.npmjs.org/ms/-/ms-2.1.2.tgz", + "integrity": "sha512-sGkPx+VjMtmA6MX27oA4FBFELFCZZ4S4XqeGOXCv68tT+jb3vk/RyaKWP0PTKyWtmLSM0b+adUTEvbs1PEaH2w==", + "dev": true + } } }, "ms": { @@ -9662,30 +9761,6 @@ "integrity": "sha512-6FlzubTLZG3J2a/NVCAleEhjzq5oxgHyaCU9yYXvcLsvoVaHJq/s5xXI6/XXP6tz7R9xAOtHnSO/tXtF3WRTlA==", "dev": true }, - "p-limit": { - "version": "3.1.0", - "resolved": "https://registry.npmjs.org/p-limit/-/p-limit-3.1.0.tgz", - "integrity": "sha512-TYOanM3wGwNGsZN2cVTYPArw454xnXj5qmWF1bEoAc4+cU/ol7GVh7odevjp1FNHduHc3KZMcFduxU5Xc6uJRQ==", - "dev": true, - "requires": { - "yocto-queue": "^0.1.0" - } - }, - "p-locate": { - "version": "5.0.0", - "resolved": "https://registry.npmjs.org/p-locate/-/p-locate-5.0.0.tgz", - "integrity": "sha512-LaNjtRWUBY++zB5nE/NwcaoMylSPk+S+ZHNB1TzdbMJMny6dynpAGt7X/tl/QYq3TIeE6nxHppbo2LGymrG5Pw==", - "dev": true, - "requires": { - "p-limit": "^3.0.2" - } - }, - "strip-json-comments": { - "version": "3.1.1", - "resolved": "https://registry.npmjs.org/strip-json-comments/-/strip-json-comments-3.1.1.tgz", - "integrity": "sha512-6fPc+R4ihwqP6N/aIv2f1gMH8lOVtWQHoqC4yK6oSDVVocumAsfCqjkXnqiYMhmMwS/mEHLp7Vehlt3ql6lEig==", - "dev": true - }, "supports-color": { "version": "8.1.1", "resolved": "https://registry.npmjs.org/supports-color/-/supports-color-8.1.1.tgz", @@ -9709,12 +9784,6 @@ "y18n": "^5.0.5", "yargs-parser": "^20.2.2" } - }, - "yargs-parser": { - "version": "20.2.4", - "resolved": "https://registry.npmjs.org/yargs-parser/-/yargs-parser-20.2.4.tgz", - "integrity": "sha512-WOkpgNhPTlE73h4VFAFsOnomJVaovO8VqLDzy5saChRBFQFBoMYirowyW+Q9HB4HFF4Z7VZTiG3iSzJJA29yRA==", - "dev": true } } }, @@ -9852,12 +9921,6 @@ "yargs": "^15.0.2" }, "dependencies": { - "camelcase": { - "version": "5.3.1", - "resolved": "https://registry.npmjs.org/camelcase/-/camelcase-5.3.1.tgz", - "integrity": "sha512-L28STB170nwWS63UjtlEOE3dldQApaJXZkOI1uMFfzf3rRuPegHaHesyee+YxQ+W6SvRDQV6UrdOdRiR153wJg==", - "dev": true - }, "cliui": { "version": "6.0.0", "resolved": "https://registry.npmjs.org/cliui/-/cliui-6.0.0.tgz", @@ -9869,6 +9932,43 @@ "wrap-ansi": "^6.2.0" } }, + "find-up": { + "version": "4.1.0", + "resolved": "https://registry.npmjs.org/find-up/-/find-up-4.1.0.tgz", + "integrity": "sha512-PpOwAdQ/YlXQ2vj8a3h8IipDuYRi3wceVQQGYWxNINccq40Anw7BlsEXCMbt1Zt+OLA6Fq9suIpIWD0OsnISlw==", + "dev": true, + "requires": { + "locate-path": "^5.0.0", + "path-exists": "^4.0.0" + } + }, + "locate-path": { + "version": "5.0.0", + "resolved": "https://registry.npmjs.org/locate-path/-/locate-path-5.0.0.tgz", + "integrity": "sha512-t7hw9pI+WvuwNJXwk5zVHpyhIqzg2qTlklJOf0mVxGSbe3Fp2VieZcduNYjaLDoy6p9uGpQEGWG87WpMKlNq8g==", + "dev": true, + "requires": { + "p-locate": "^4.1.0" + } + }, + "p-limit": { + "version": "2.3.0", + "resolved": "https://registry.npmjs.org/p-limit/-/p-limit-2.3.0.tgz", + "integrity": "sha512-//88mFWSJx8lxCzwdAABTJL2MyWB12+eIY7MDL2SqLmAkeKU9qxRvWuSyTjm3FUmpBEMuFfckAIqEaVGUDxb6w==", + "dev": true, + "requires": { + "p-try": "^2.0.0" + } + }, + "p-locate": { + "version": "4.1.0", + "resolved": "https://registry.npmjs.org/p-locate/-/p-locate-4.1.0.tgz", + "integrity": "sha512-R79ZZ/0wAxKGu3oYMlz8jy/kbhsNrS7SKZ7PxEHBgJ5+F2mtFW2fK2cOtBh1cHYkQsbzFV7I+EoRKe6Yt0oK7A==", + "dev": true, + "requires": { + "p-limit": "^2.2.0" + } + }, "resolve-from": { "version": "5.0.0", "resolved": "https://registry.npmjs.org/resolve-from/-/resolve-from-5.0.0.tgz", @@ -9930,9 +10030,9 @@ "dev": true }, "object-inspect": { - "version": "1.11.1", - "resolved": "https://registry.npmjs.org/object-inspect/-/object-inspect-1.11.1.tgz", - "integrity": "sha512-If7BjFlpkzzBeV1cqgT3OSWT3azyoxDGajR+iGnFBfVV2EWyDyWaZZW2ERDjUaY2QM8i5jI3Sj7mhsM4DDAqWA==", + "version": "1.12.0", + "resolved": "https://registry.npmjs.org/object-inspect/-/object-inspect-1.12.0.tgz", + "integrity": "sha512-Ho2z80bVIvJloH+YzRmpZVQe87+qASmBUKZDWgx9cu+KDrX2ZDH/3tMy+gXbZETVGs2M8YdxObOh7XAtim9Y0g==", "dev": true }, "once": { @@ -10017,21 +10117,21 @@ "dev": true }, "p-limit": { - "version": "2.3.0", - "resolved": "https://registry.npmjs.org/p-limit/-/p-limit-2.3.0.tgz", - "integrity": "sha512-//88mFWSJx8lxCzwdAABTJL2MyWB12+eIY7MDL2SqLmAkeKU9qxRvWuSyTjm3FUmpBEMuFfckAIqEaVGUDxb6w==", + "version": "3.1.0", + "resolved": "https://registry.npmjs.org/p-limit/-/p-limit-3.1.0.tgz", + "integrity": "sha512-TYOanM3wGwNGsZN2cVTYPArw454xnXj5qmWF1bEoAc4+cU/ol7GVh7odevjp1FNHduHc3KZMcFduxU5Xc6uJRQ==", "dev": true, "requires": { - "p-try": "^2.0.0" + "yocto-queue": "^0.1.0" } }, "p-locate": { - "version": "4.1.0", - "resolved": "https://registry.npmjs.org/p-locate/-/p-locate-4.1.0.tgz", - "integrity": "sha512-R79ZZ/0wAxKGu3oYMlz8jy/kbhsNrS7SKZ7PxEHBgJ5+F2mtFW2fK2cOtBh1cHYkQsbzFV7I+EoRKe6Yt0oK7A==", + "version": "5.0.0", + "resolved": "https://registry.npmjs.org/p-locate/-/p-locate-5.0.0.tgz", + "integrity": "sha512-LaNjtRWUBY++zB5nE/NwcaoMylSPk+S+ZHNB1TzdbMJMny6dynpAGt7X/tl/QYq3TIeE6nxHppbo2LGymrG5Pw==", "dev": true, "requires": { - "p-limit": "^2.2.0" + "p-limit": "^3.0.2" } }, "p-map": { @@ -10208,6 +10308,12 @@ "dev": true } } + }, + "semver": { + "version": "6.3.0", + "resolved": "https://registry.npmjs.org/semver/-/semver-6.3.0.tgz", + "integrity": "sha512-b39TBaTSfV6yBrapU89p5fKekE2m/NwnDocOVruQFS1/veMgdzuPcnOM34M6CwxW8jH/lxEa5rBoDeUwu5HHTw==", + "dev": true } } }, @@ -10335,6 +10441,45 @@ "dev": true, "requires": { "find-up": "^4.0.0" + }, + "dependencies": { + "find-up": { + "version": "4.1.0", + "resolved": "https://registry.npmjs.org/find-up/-/find-up-4.1.0.tgz", + "integrity": "sha512-PpOwAdQ/YlXQ2vj8a3h8IipDuYRi3wceVQQGYWxNINccq40Anw7BlsEXCMbt1Zt+OLA6Fq9suIpIWD0OsnISlw==", + "dev": true, + "requires": { + "locate-path": "^5.0.0", + "path-exists": "^4.0.0" + } + }, + "locate-path": { + "version": "5.0.0", + "resolved": "https://registry.npmjs.org/locate-path/-/locate-path-5.0.0.tgz", + "integrity": "sha512-t7hw9pI+WvuwNJXwk5zVHpyhIqzg2qTlklJOf0mVxGSbe3Fp2VieZcduNYjaLDoy6p9uGpQEGWG87WpMKlNq8g==", + "dev": true, + "requires": { + "p-locate": "^4.1.0" + } + }, + "p-limit": { + "version": "2.3.0", + "resolved": "https://registry.npmjs.org/p-limit/-/p-limit-2.3.0.tgz", + "integrity": "sha512-//88mFWSJx8lxCzwdAABTJL2MyWB12+eIY7MDL2SqLmAkeKU9qxRvWuSyTjm3FUmpBEMuFfckAIqEaVGUDxb6w==", + "dev": true, + "requires": { + "p-try": "^2.0.0" + } + }, + "p-locate": { + "version": "4.1.0", + "resolved": "https://registry.npmjs.org/p-locate/-/p-locate-4.1.0.tgz", + "integrity": "sha512-R79ZZ/0wAxKGu3oYMlz8jy/kbhsNrS7SKZ7PxEHBgJ5+F2mtFW2fK2cOtBh1cHYkQsbzFV7I+EoRKe6Yt0oK7A==", + "dev": true, + "requires": { + "p-limit": "^2.2.0" + } + } } }, "prelude-ls": { @@ -10454,6 +10599,12 @@ "resolved": "https://registry.npmjs.org/ini/-/ini-1.3.8.tgz", "integrity": "sha512-JV/yugV2uzW5iMRSiZAyDtQd+nxtUnjeLt0acNdw98kKLrvuRVyB80tsREOE7yvGVgalhZ6RNXCmEHkUKBKxew==", "dev": true + }, + "strip-json-comments": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/strip-json-comments/-/strip-json-comments-2.0.1.tgz", + "integrity": "sha1-PFMZQukIwml8DsNEhYwobHygpgo=", + "dev": true } } }, @@ -10560,34 +10711,26 @@ "yargs-parser": "20.2.9" }, "dependencies": { - "array-union": { - "version": "2.1.0", - "resolved": "https://registry.npmjs.org/array-union/-/array-union-2.1.0.tgz", - "integrity": "sha512-HGyxoOTYUyCM6stUe6EJgnd4EoewAI7zMdfqO+kGjnlZmBDz/cR5pf8r/cR4Wq60sL/p0IkcjUEEPwS3GFrIyw==", - "dev": true - }, - "globby": { - "version": "11.0.4", - "resolved": "https://registry.npmjs.org/globby/-/globby-11.0.4.tgz", - "integrity": "sha512-9O4MVG9ioZJ08ffbcyVYyLOJLk5JQ688pJ4eMGLpdWLHq/Wr1D9BlriLQyL0E+jbkuePVZXYFj47QM/v093wHg==", + "debug": { + "version": "4.3.2", + "resolved": "https://registry.npmjs.org/debug/-/debug-4.3.2.tgz", + "integrity": "sha512-mOp8wKcvj7XxC78zLgw/ZA+6TSgkoE2C/ienthhRD298T7UNwAg9diBpLRxC0mOezLl4B0xV7M0cCO6P/O0Xhw==", "dev": true, "requires": { - "array-union": "^2.1.0", - "dir-glob": "^3.0.1", - "fast-glob": "^3.1.1", - "ignore": "^5.1.4", - "merge2": "^1.3.0", - "slash": "^3.0.0" + "ms": "2.1.2" } }, - "semver": { - "version": "7.3.5", - "resolved": "https://registry.npmjs.org/semver/-/semver-7.3.5.tgz", - "integrity": "sha512-PoeGJYh8HK4BTO/a9Tf6ZG3veo/A7ZVsYrSA6J8ny9nb3B1VrpkuN+z9OE5wfE5p6H4LchYZsegiQgbJD94ZFQ==", - "dev": true, - "requires": { - "lru-cache": "^6.0.0" - } + "uuid": { + "version": "8.3.2", + "resolved": "https://registry.npmjs.org/uuid/-/uuid-8.3.2.tgz", + "integrity": "sha512-+NYs2QeMWy+GWFOEm9xnn6HCDp0l7QBD7ml8zLUmJ+93Q5NF0NocErnwkTkXVFNiX3/fpC6afS8Dhb/gz7R7eg==", + "dev": true + }, + "yargs-parser": { + "version": "20.2.9", + "resolved": "https://registry.npmjs.org/yargs-parser/-/yargs-parser-20.2.9.tgz", + "integrity": "sha512-y11nGElTIV+CT3Zv9t7VKl+Q3hTQoT9a1Qzezhhl6Rp21gJ/IVTW7Z3y9EWXhuUBC2Shnf+DX0antecpAwSP8w==", + "dev": true } } }, @@ -10699,9 +10842,9 @@ } }, "safe-buffer": { - "version": "5.2.1", - "resolved": "https://registry.npmjs.org/safe-buffer/-/safe-buffer-5.2.1.tgz", - "integrity": "sha512-rp3So07KcdmmKbGvgaNxQSJr7bGVSVk5S9Eq1F+ppbRo70+YeaDxkw5Dd8NPN+GD6bjnYm2VuPuCXmpuYvmCXQ==", + "version": "5.1.2", + "resolved": "https://registry.npmjs.org/safe-buffer/-/safe-buffer-5.1.2.tgz", + "integrity": "sha512-Gd2UZBJDkXlY7GbJxfsE8/nvKkUEU1G38c1siN6QP6a9PT9MmHB8GnpscSmMJSoF8LOIrt8ud/wPtojys4G6+g==", "dev": true }, "safer-buffer": { @@ -10711,10 +10854,13 @@ "dev": true }, "semver": { - "version": "6.3.0", - "resolved": "https://registry.npmjs.org/semver/-/semver-6.3.0.tgz", - "integrity": "sha512-b39TBaTSfV6yBrapU89p5fKekE2m/NwnDocOVruQFS1/veMgdzuPcnOM34M6CwxW8jH/lxEa5rBoDeUwu5HHTw==", - "dev": true + "version": "7.3.5", + "resolved": "https://registry.npmjs.org/semver/-/semver-7.3.5.tgz", + "integrity": "sha512-PoeGJYh8HK4BTO/a9Tf6ZG3veo/A7ZVsYrSA6J8ny9nb3B1VrpkuN+z9OE5wfE5p6H4LchYZsegiQgbJD94ZFQ==", + "dev": true, + "requires": { + "lru-cache": "^6.0.0" + } }, "semver-diff": { "version": "3.1.1", @@ -10723,6 +10869,14 @@ "dev": true, "requires": { "semver": "^6.3.0" + }, + "dependencies": { + "semver": { + "version": "6.3.0", + "resolved": "https://registry.npmjs.org/semver/-/semver-6.3.0.tgz", + "integrity": "sha512-b39TBaTSfV6yBrapU89p5fKekE2m/NwnDocOVruQFS1/veMgdzuPcnOM34M6CwxW8jH/lxEa5rBoDeUwu5HHTw==", + "dev": true + } } }, "serialize-javascript": { @@ -10888,6 +11042,14 @@ "dev": true, "requires": { "safe-buffer": "~5.2.0" + }, + "dependencies": { + "safe-buffer": { + "version": "5.2.1", + "resolved": "https://registry.npmjs.org/safe-buffer/-/safe-buffer-5.2.1.tgz", + "integrity": "sha512-rp3So07KcdmmKbGvgaNxQSJr7bGVSVk5S9Eq1F+ppbRo70+YeaDxkw5Dd8NPN+GD6bjnYm2VuPuCXmpuYvmCXQ==", + "dev": true + } } }, "string-width": { @@ -10923,9 +11085,9 @@ "dev": true }, "strip-json-comments": { - "version": "2.0.1", - "resolved": "https://registry.npmjs.org/strip-json-comments/-/strip-json-comments-2.0.1.tgz", - "integrity": "sha1-PFMZQukIwml8DsNEhYwobHygpgo=", + "version": "3.1.1", + "resolved": "https://registry.npmjs.org/strip-json-comments/-/strip-json-comments-3.1.1.tgz", + "integrity": "sha512-6fPc+R4ihwqP6N/aIv2f1gMH8lOVtWQHoqC4yK6oSDVVocumAsfCqjkXnqiYMhmMwS/mEHLp7Vehlt3ql6lEig==", "dev": true }, "strip-outer": { @@ -10935,6 +11097,14 @@ "dev": true, "requires": { "escape-string-regexp": "^1.0.2" + }, + "dependencies": { + "escape-string-regexp": { + "version": "1.0.5", + "resolved": "https://registry.npmjs.org/escape-string-regexp/-/escape-string-regexp-1.0.5.tgz", + "integrity": "sha1-G2HAViGQqN/2rjuyzwIAyhMLhtQ=", + "dev": true + } } }, "supports-color": { @@ -11012,6 +11182,14 @@ "dev": true, "requires": { "escape-string-regexp": "^1.0.2" + }, + "dependencies": { + "escape-string-regexp": { + "version": "1.0.5", + "resolved": "https://registry.npmjs.org/escape-string-regexp/-/escape-string-regexp-1.0.5.tgz", + "integrity": "sha1-G2HAViGQqN/2rjuyzwIAyhMLhtQ=", + "dev": true + } } }, "ts-node": { @@ -11081,9 +11259,9 @@ "dev": true }, "type-fest": { - "version": "0.21.3", - "resolved": "https://registry.npmjs.org/type-fest/-/type-fest-0.21.3.tgz", - "integrity": "sha512-t0rzBq87m3fVcduHDUFhKmyyX+9eo6WQjZvf51Ea/M0Q7+T374Jp1aUiyUl0GKxp8M/OETVHSDvmkyPgvX+X2w==", + "version": "0.20.2", + "resolved": "https://registry.npmjs.org/type-fest/-/type-fest-0.20.2.tgz", + "integrity": "sha512-Ne+eE4r0/iWnpAxD852z3A+N0Bt5RN//NjJwRd2VFHEmrywxf5vsZlh4R6lixl6B+wz/8d+maTSAkN1FIkI3LQ==", "dev": true }, "typedarray-to-buffer": { @@ -11106,6 +11284,22 @@ "marked": "^3.0.8", "minimatch": "^3.0.4", "shiki": "^0.9.12" + }, + "dependencies": { + "glob": { + "version": "7.2.0", + "resolved": "https://registry.npmjs.org/glob/-/glob-7.2.0.tgz", + "integrity": "sha512-lmLf6gtyrPq8tTjSmrO94wBeQbFR3HbLHbuyD69wuyQkImp2hWqMGB47OX65FBkPffO641IP9jWa1z4ivqG26Q==", + "dev": true, + "requires": { + "fs.realpath": "^1.0.0", + "inflight": "^1.0.4", + "inherits": "2", + "minimatch": "^3.0.4", + "once": "^1.3.0", + "path-is-absolute": "^1.0.0" + } + } } }, "typescript": { @@ -11171,15 +11365,6 @@ "requires": { "ci-info": "^2.0.0" } - }, - "semver": { - "version": "7.3.5", - "resolved": "https://registry.npmjs.org/semver/-/semver-7.3.5.tgz", - "integrity": "sha512-PoeGJYh8HK4BTO/a9Tf6ZG3veo/A7ZVsYrSA6J8ny9nb3B1VrpkuN+z9OE5wfE5p6H4LchYZsegiQgbJD94ZFQ==", - "dev": true, - "requires": { - "lru-cache": "^6.0.0" - } } } }, @@ -11214,9 +11399,9 @@ "dev": true }, "uuid": { - "version": "8.3.2", - "resolved": "https://registry.npmjs.org/uuid/-/uuid-8.3.2.tgz", - "integrity": "sha512-+NYs2QeMWy+GWFOEm9xnn6HCDp0l7QBD7ml8zLUmJ+93Q5NF0NocErnwkTkXVFNiX3/fpC6afS8Dhb/gz7R7eg==", + "version": "3.4.0", + "resolved": "https://registry.npmjs.org/uuid/-/uuid-3.4.0.tgz", + "integrity": "sha512-HjSDRw6gZE5JMggctHBcjVak08+KEVhSIiDzFnT9S9aegmp85S/bReBVTb4QTFaRNptJ9kuYaNhnbNEOkbKb/A==", "dev": true }, "v8-compile-cache": { @@ -11417,9 +11602,9 @@ } }, "yargs-parser": { - "version": "20.2.9", - "resolved": "https://registry.npmjs.org/yargs-parser/-/yargs-parser-20.2.9.tgz", - "integrity": "sha512-y11nGElTIV+CT3Zv9t7VKl+Q3hTQoT9a1Qzezhhl6Rp21gJ/IVTW7Z3y9EWXhuUBC2Shnf+DX0antecpAwSP8w==", + "version": "20.2.4", + "resolved": "https://registry.npmjs.org/yargs-parser/-/yargs-parser-20.2.4.tgz", + "integrity": "sha512-WOkpgNhPTlE73h4VFAFsOnomJVaovO8VqLDzy5saChRBFQFBoMYirowyW+Q9HB4HFF4Z7VZTiG3iSzJJA29yRA==", "dev": true }, "yargs-unparser": { @@ -11434,6 +11619,12 @@ "is-plain-obj": "^2.1.0" }, "dependencies": { + "camelcase": { + "version": "6.2.1", + "resolved": "https://registry.npmjs.org/camelcase/-/camelcase-6.2.1.tgz", + "integrity": "sha512-tVI4q5jjFV5CavAU8DXfza/TJcZutVKo/5Foskmsqcm0MsL91moHvwiGNnqaa2o6PF/7yT5ikDRcVcl8Rj6LCA==", + "dev": true + }, "decamelize": { "version": "4.0.0", "resolved": "https://registry.npmjs.org/decamelize/-/decamelize-4.0.0.tgz", diff --git a/package.json b/package.json index 52057bda0e4..8e675bc86cd 100644 --- a/package.json +++ b/package.json @@ -5,6 +5,9 @@ "license": "MIT", "main": "./dist/index.js", "types": "./dist/index.d.ts", + "files": [ + "dist/" + ], "workspaces": [ "./packages/*" ], @@ -29,8 +32,7 @@ "@tsconfig/node12": "^1.0.9", "gh-pages": "^3.2.3", "release-it": "^14.11.8", - "typedoc": "^0.22.10", - "typescript": "^4.5.3" + "typescript": "^4.5.4" }, "repository": { "type": "git", @@ -40,10 +42,8 @@ "url": "https://github.com/redis/node-redis/issues" }, "homepage": "https://github.com/redis/node-redis", - "files": [ - "dist/" - ], "engines": { - "npm": ">=7" + "npm": ">=7", + "typescript": ">=4" } } diff --git a/packages/client/lib/client/commands-queue.ts b/packages/client/lib/client/commands-queue.ts index 4c588372593..e716dcd3a23 100644 --- a/packages/client/lib/client/commands-queue.ts +++ b/packages/client/lib/client/commands-queue.ts @@ -11,6 +11,7 @@ export interface QueueCommandOptions { asap?: boolean; chainId?: symbol; signal?: AbortSignal; + returnBuffers?: boolean; ignorePubSubMode?: boolean; } @@ -27,7 +28,7 @@ interface CommandWaitingForReply { resolve(reply?: unknown): void; reject(err: Error): void; channelsCounter?: number; - bufferMode?: boolean; + returnBuffers?: boolean; } export enum PubSubSubscribeCommands { @@ -41,8 +42,8 @@ export enum PubSubUnsubscribeCommands { } export type PubSubListener< - BUFFER_MODE extends boolean = false, - T = BUFFER_MODE extends true ? Buffer : string + RETURN_BUFFERS extends boolean = false, + T = RETURN_BUFFERS extends true ? Buffer : string > = (message: T, channel: T) => unknown; interface PubSubListeners { @@ -142,7 +143,7 @@ export default class RedisCommandsQueue { this.#maxLength = maxLength; } - addCommand(args: RedisCommandArguments, options?: QueueCommandOptions, bufferMode?: boolean): Promise { + addCommand(args: RedisCommandArguments, options?: QueueCommandOptions): Promise { if (this.#pubSubState && !options?.ignorePubSubMode) { return Promise.reject(new Error('Cannot send commands in PubSub mode')); } else if (this.#maxLength && this.#waitingToBeSent.length + this.#waitingForReply.length >= this.#maxLength) { @@ -154,7 +155,7 @@ export default class RedisCommandsQueue { const node = new LinkedList.Node({ args, chainId: options?.chainId, - bufferMode, + returnBuffers: options?.returnBuffers, resolve, reject }); @@ -197,7 +198,7 @@ export default class RedisCommandsQueue { command: PubSubSubscribeCommands, channels: RedisCommandArgument | Array, listener: PubSubListener, - bufferMode?: T + returnBuffers?: T ): Promise { const pubSubState = this.#initiatePubSubState(), channelsToSubscribe: Array = [], @@ -215,7 +216,7 @@ export default class RedisCommandsQueue { } // https://github.com/microsoft/TypeScript/issues/23132 - (bufferMode ? listeners.buffers : listeners.strings).add(listener as any); + (returnBuffers ? listeners.buffers : listeners.strings).add(listener as any); } if (!channelsToSubscribe.length) { @@ -228,7 +229,7 @@ export default class RedisCommandsQueue { command: PubSubUnsubscribeCommands, channels?: string | Array, listener?: PubSubListener, - bufferMode?: T + returnBuffers?: T ): Promise { if (!this.#pubSubState) { return Promise.resolve(); @@ -252,7 +253,7 @@ export default class RedisCommandsQueue { let shouldUnsubscribe; if (listener) { // https://github.com/microsoft/TypeScript/issues/23132 - (bufferMode ? sets.buffers : sets.strings).delete(listener as any); + (returnBuffers ? sets.buffers : sets.strings).delete(listener as any); shouldUnsubscribe = !sets.buffers.size && !sets.strings.size; } else { shouldUnsubscribe = true; @@ -289,7 +290,7 @@ export default class RedisCommandsQueue { this.#waitingToBeSent.push({ args: commandArgs, channelsCounter, - bufferMode: true, + returnBuffers: true, resolve: () => { pubSubState[inProgressKey] -= channelsCounter; if (isSubscribe) { @@ -350,7 +351,7 @@ export default class RedisCommandsQueue { resolve: toSend.resolve, reject: toSend.reject, channelsCounter: toSend.channelsCounter, - bufferMode: toSend.bufferMode + returnBuffers: toSend.returnBuffers }); } this.#chainInExecution = toSend?.chainId; @@ -359,7 +360,7 @@ export default class RedisCommandsQueue { parseResponse(data: Buffer): void { this.#parser.setReturnBuffers( - !!this.#waitingForReply.head?.value.bufferMode || + !!this.#waitingForReply.head?.value.returnBuffers || !!this.#pubSubState?.subscribed ); this.#parser.execute(data); diff --git a/packages/client/lib/client/index.spec.ts b/packages/client/lib/client/index.spec.ts index c5e05b7858c..05c0deb8974 100644 --- a/packages/client/lib/client/index.spec.ts +++ b/packages/client/lib/client/index.spec.ts @@ -319,9 +319,11 @@ describe('Client', () => { assert.equal(await client.sendCommand(['PING']), 'PONG'); }, GLOBAL.SERVERS.OPEN); - testUtils.testWithClient('bufferMode', async client => { + testUtils.testWithClient('returnBuffers', async client => { assert.deepEqual( - await client.sendCommand(['PING'], undefined, true), + await client.sendCommand(['PING'], RedisClient.commandOptions({ + returnBuffers: true + }),), Buffer.from('PONG') ); }, GLOBAL.SERVERS.OPEN); @@ -435,10 +437,10 @@ describe('Client', () => { }); testUtils.testWithClient('modules', async client => { - assert.equal( - await client.module.echo('message'), - 'message' - ); + // assert.equal( + // await client.module.echo('message'), + // 'message' + // ); }, { ...GLOBAL.SERVERS.OPEN, clientOptions: { @@ -551,7 +553,7 @@ describe('Client', () => { await client.zAdd('key', members); - const map = new Map(); + const map = new Map(); for await (const member of client.zScanIterator('key')) { map.set(member.value, member.score); } diff --git a/packages/client/lib/client/index.ts b/packages/client/lib/client/index.ts index bd2557ce92c..9e1b2d37cc9 100644 --- a/packages/client/lib/client/index.ts +++ b/packages/client/lib/client/index.ts @@ -1,5 +1,5 @@ import COMMANDS from './commands'; -import { RedisCommand, RedisCommandArguments, RedisCommandRawReply, RedisCommandReply, RedisModules, RedisPlugins, RedisScript, RedisScripts } from '../commands'; +import { RedisCommand, RedisCommandArgument, RedisCommandArguments, RedisCommandRawReply, RedisCommandReply, RedisModules, RedisPlugins, RedisScript, RedisScripts } from '../commands'; import RedisSocket, { RedisSocketOptions, RedisTlsSocketOptions } from './socket'; import RedisCommandsQueue, { PubSubListener, PubSubSubscribeCommands, PubSubUnsubscribeCommands, QueueCommandOptions } from './commands-queue'; import RedisClientMultiCommand, { RedisClientMultiCommandType } from './multi-command'; @@ -28,8 +28,30 @@ export interface RedisClientOptions = - (...args: Parameters | [options: CommandOptions, ...rest: Parameters]) => Promise>; +type ConvertArgumentType = + Type extends RedisCommandArgument ? ( + Type extends (string & ToType) ? Type : ToType + ) : ( + Type extends Set ? Set> : ( + Type extends Map ? Map> : ( + Type extends Record ? { + [Property in keyof Type]: ConvertArgumentType + } : Type + ) + ) + ); + +export type RedisClientCommandSignature< + Command extends RedisCommand, + Params extends Array = Parameters +> = >( + ...args: Params | [options: Options, ...rest: Params] +) => Promise< + ConvertArgumentType< + RedisCommandReply, + Options['returnBuffers'] extends true ? Buffer : string + > +>; type WithCommands = { [P in keyof typeof COMMANDS]: RedisClientCommandSignature<(typeof COMMANDS)[P]>; @@ -59,7 +81,7 @@ type ClientLegacyCallback = (err: Error | null, reply?: RedisCommandRawReply) => export type ClientLegacyCommandArguments = LegacyCommandArguments | [...LegacyCommandArguments, ClientLegacyCallback]; export default class RedisClient extends EventEmitter { - static commandOptions(options: ClientCommandOptions): CommandOptions { + static commandOptions(options: T): CommandOptions { return commandOptions(options); } @@ -325,17 +347,17 @@ export default class RedisClient return transformCommandReply( command, - await this.#sendCommand(redisArgs, options, command.BUFFER_MODE), + await this.#sendCommand(redisArgs, options), redisArgs.preserve ); } - sendCommand(args: RedisCommandArguments, options?: ClientCommandOptions, bufferMode?: boolean): Promise { - return this.#sendCommand(args, options, bufferMode); + sendCommand(args: RedisCommandArguments, options?: ClientCommandOptions): Promise { + return this.#sendCommand(args, options); } // using `#sendCommand` cause `sendCommand` is overwritten in legacy mode - #sendCommand(args: RedisCommandArguments, options?: ClientCommandOptions, bufferMode?: boolean): Promise { + #sendCommand(args: RedisCommandArguments, options?: ClientCommandOptions): Promise { if (!this.#socket.isOpen) { return Promise.reject(new ClientClosedError()); } @@ -349,7 +371,7 @@ export default class RedisClient ); } - const promise = this.#queue.addCommand(args, options, bufferMode); + const promise = this.#queue.addCommand(args, options); this.#tick(); return promise; } @@ -359,19 +381,19 @@ export default class RedisClient return transformCommandReply( script, - await this.executeScript(script, redisArgs, options, script.BUFFER_MODE), + await this.executeScript(script, redisArgs, options), redisArgs.preserve ); } - async executeScript(script: RedisScript, args: RedisCommandArguments, options?: ClientCommandOptions, bufferMode?: boolean): Promise> { + async executeScript(script: RedisScript, args: RedisCommandArguments, options?: ClientCommandOptions): Promise> { try { return await this.#sendCommand([ 'EVALSHA', script.SHA1, script.NUMBER_OF_KEYS.toString(), ...args - ], options, bufferMode); + ], options); } catch (err: any) { if (!err?.message?.startsWith?.('NOSCRIPT')) { throw err; @@ -382,7 +404,7 @@ export default class RedisClient script.SCRIPT, script.NUMBER_OF_KEYS.toString(), ...args - ], options, bufferMode); + ], options); } } @@ -553,7 +575,7 @@ export default class RedisClient } while (cursor !== 0); } - async* hScanIterator(key: string, options?: ScanOptions): AsyncIterable { + async* hScanIterator(key: string, options?: ScanOptions): AsyncIterable> { let cursor = 0; do { const reply = await (this as any).hScan(key, cursor, options); @@ -575,7 +597,7 @@ export default class RedisClient } while (cursor !== 0); } - async* zScanIterator(key: string, options?: ScanOptions): AsyncIterable> { + async* zScanIterator(key: string, options?: ScanOptions): AsyncIterable> { let cursor = 0; do { const reply = await (this as any).zScan(key, cursor, options); diff --git a/packages/client/lib/cluster/commands.ts b/packages/client/lib/cluster/commands.ts index 5a8dca4ea4e..4b2aba6a7ae 100644 --- a/packages/client/lib/cluster/commands.ts +++ b/packages/client/lib/cluster/commands.ts @@ -4,13 +4,9 @@ import * as BITCOUNT from '../commands/BITCOUNT'; import * as BITFIELD from '../commands/BITFIELD'; import * as BITOP from '../commands/BITOP'; import * as BITPOS from '../commands/BITPOS'; -import * as BLMOVE_BUFFER from '../commands/BLMOVE_BUFFER'; import * as BLMOVE from '../commands/BLMOVE'; -import * as BLPOP_BUFFER from '../commands/BLPOP_BUFFER'; import * as BLPOP from '../commands/BLPOP'; -import * as BRPOP_BUFFER from '../commands/BRPOP_BUFFER'; import * as BRPOP from '../commands/BRPOP'; -import * as BRPOPLPUSH_BUFFER from '../commands/BRPOPLPUSH_BUFFER'; import * as BRPOPLPUSH from '../commands/BRPOPLPUSH'; import * as BZPOPMAX from '../commands/BZPOPMAX'; import * as BZPOPMIN from '../commands/BZPOPMIN'; @@ -31,7 +27,6 @@ import * as GEOPOS from '../commands/GEOPOS'; import * as GEOSEARCH_WITH from '../commands/GEOSEARCH_WITH'; import * as GEOSEARCH from '../commands/GEOSEARCH'; import * as GEOSEARCHSTORE from '../commands/GEOSEARCHSTORE'; -import * as GET_BUFFER from '../commands/GET_BUFFER'; import * as GET from '../commands/GET'; import * as GETBIT from '../commands/GETBIT'; import * as GETDEL from '../commands/GETDEL'; @@ -40,16 +35,13 @@ import * as GETRANGE from '../commands/GETRANGE'; import * as GETSET from '../commands/GETSET'; import * as HDEL from '../commands/HDEL'; import * as HEXISTS from '../commands/HEXISTS'; -import * as HGET_BUFFER from '../commands/HGET_BUFFER'; import * as HGET from '../commands/HGET'; -import * as HGETALL_BUFFER from '../commands/HGETALL_BUFFER'; import * as HGETALL from '../commands/HGETALL'; import * as HINCRBY from '../commands/HINCRBY'; import * as HINCRBYFLOAT from '../commands/HINCRBYFLOAT'; import * as HKEYS from '../commands/HKEYS'; import * as HLEN from '../commands/HLEN'; import * as HMGET from '../commands/HMGET'; -import * as HRANDFIELD_COUNT_WITHVALUES_BUFFER from '../commands/HRANDFIELD_COUNT_WITHVALUES_BUFFER'; import * as HRANDFIELD_COUNT_WITHVALUES from '../commands/HRANDFIELD_COUNT_WITHVALUES'; import * as HRANDFIELD_COUNT from '../commands/HRANDFIELD_COUNT'; import * as HRANDFIELD from '../commands/HRANDFIELD'; @@ -126,12 +118,8 @@ import * as UNLINK from '../commands/UNLINK'; import * as WATCH from '../commands/WATCH'; import * as XACK from '../commands/XACK'; import * as XADD from '../commands/XADD'; -import * as XAUTOCLAIM_JUSTID_BUFFER from '../commands/XAUTOCLAIM_JUSTID_BUFFER'; -import * as XAUTOCLAIM_BUFFER from '../commands/XAUTOCLAIM_BUFFER'; import * as XAUTOCLAIM_JUSTID from '../commands/XAUTOCLAIM_JUSTID'; import * as XAUTOCLAIM from '../commands/XAUTOCLAIM'; -import * as XCLAIM_JUSTID_BUFFER from '../commands/XCLAIM_JUSTID_BUFFER'; -import * as XCLAIM_BUFFER from '../commands/XCLAIM_BUFFER'; import * as XCLAIM_JUSTID from '../commands/XCLAIM_JUSTID'; import * as XCLAIM from '../commands/XCLAIM'; import * as XDEL from '../commands/XDEL'; @@ -146,13 +134,9 @@ import * as XINFO_STREAM from '../commands/XINFO_STREAM'; import * as XLEN from '../commands/XLEN'; import * as XPENDING_RANGE from '../commands/XPENDING_RANGE'; import * as XPENDING from '../commands/XPENDING'; -import * as XRANGE_BUFFER from '../commands/XRANGE_BUFFER'; import * as XRANGE from '../commands/XRANGE'; -import * as XREAD_BUFFER from '../commands/XREAD_BUFFER'; import * as XREAD from '../commands/XREAD'; -import * as XREADGROUP_BUFFER from '../commands/XREADGROUP_BUFFER'; import * as XREADGROUP from '../commands/XREADGROUP'; -import * as XREVRANGE_BUFFER from '../commands/XREVRANGE_BUFFER'; import * as XREVRANGE from '../commands/XREVRANGE'; import * as XTRIM from '../commands/XTRIM'; import * as ZADD from '../commands/ZADD'; @@ -203,20 +187,12 @@ export default { bitOp: BITOP, BITPOS, bitPos: BITPOS, - BLMOVE_BUFFER, - blMoveBuffer: BLMOVE_BUFFER, BLMOVE, blMove: BLMOVE, - BLPOP_BUFFER, - blPopBuffer: BLPOP_BUFFER, BLPOP, blPop: BLPOP, - BRPOP_BUFFER, - brPopBuffer: BRPOP_BUFFER, BRPOP, brPop: BRPOP, - BRPOPLPUSH_BUFFER, - brPopLPushBuffer: BRPOPLPUSH_BUFFER, BRPOPLPUSH, brPopLPush: BRPOPLPUSH, BZPOPMAX, @@ -257,8 +233,6 @@ export default { geoSearch: GEOSEARCH, GEOSEARCHSTORE, geoSearchStore: GEOSEARCHSTORE, - GET_BUFFER, - getBuffer: GET_BUFFER, GET, get: GET, GETBIT, @@ -275,12 +249,8 @@ export default { hDel: HDEL, HEXISTS, hExists: HEXISTS, - HGET_BUFFER, - hGetBuffer: HGET_BUFFER, HGET, hGet: HGET, - HGETALL_BUFFER, - hGetAllBuffer: HGETALL_BUFFER, HGETALL, hGetAll: HGETALL, HINCRBY, @@ -293,8 +263,6 @@ export default { hLen: HLEN, HMGET, hmGet: HMGET, - HRANDFIELD_COUNT_WITHVALUES_BUFFER, - hRandFieldCountWithValuesBuffer: HRANDFIELD_COUNT_WITHVALUES_BUFFER, HRANDFIELD_COUNT_WITHVALUES, hRandFieldCountWithValues: HRANDFIELD_COUNT_WITHVALUES, HRANDFIELD_COUNT, @@ -447,20 +415,12 @@ export default { xAck: XACK, XADD, xAdd: XADD, - XAUTOCLAIM_JUSTID_BUFFER, - xAutoClaimJustIdBuffer: XAUTOCLAIM_JUSTID_BUFFER, - XAUTOCLAIM_BUFFER, - xAutoClaimBuffer: XAUTOCLAIM_BUFFER, XAUTOCLAIM_JUSTID, xAutoClaimJustId: XAUTOCLAIM_JUSTID, XAUTOCLAIM, xAutoClaim: XAUTOCLAIM, XCLAIM, xClaim: XCLAIM, - XCLAIM_JUSTID_BUFFER, - xClaimJustIdBuffer: XCLAIM_JUSTID_BUFFER, - XCLAIM_BUFFER, - xClaimBuffer: XCLAIM_BUFFER, XCLAIM_JUSTID, xClaimJustId: XCLAIM_JUSTID, XDEL, @@ -487,20 +447,12 @@ export default { xPendingRange: XPENDING_RANGE, XPENDING, xPending: XPENDING, - XRANGE_BUFFER, - xRangeBuffer: XRANGE_BUFFER, XRANGE, xRange: XRANGE, - XREAD_BUFFER, - xReadBuffer: XREAD_BUFFER, XREAD, xRead: XREAD, - XREADGROUP_BUFFER, - xReadGroupBuffer: XREADGROUP_BUFFER, XREADGROUP, xReadGroup: XREADGROUP, - XREVRANGE_BUFFER, - xRevRangeBuffer: XREVRANGE_BUFFER, XREVRANGE, xRevRange: XREVRANGE, XTRIM, diff --git a/packages/client/lib/cluster/index.ts b/packages/client/lib/cluster/index.ts index a9443600568..0a9ead636a4 100644 --- a/packages/client/lib/cluster/index.ts +++ b/packages/client/lib/cluster/index.ts @@ -76,8 +76,7 @@ export default class RedisCluster RedisCluster.extractFirstKey(command, args, redisArgs), command.IS_READ_ONLY, redisArgs, - options, - command.BUFFER_MODE + options ), redisArgs.preserve ); @@ -88,19 +87,18 @@ export default class RedisCluster isReadonly: boolean | undefined, args: RedisCommandArguments, options?: ClientCommandOptions, - bufferMode?: boolean, redirections = 0 ): Promise> { const client = this.#slots.getClient(firstKey, isReadonly); try { - return await client.sendCommand(args, options, bufferMode); + return await client.sendCommand(args, options); } catch (err: any) { const shouldRetry = await this.#handleCommandError(err, client, redirections); if (shouldRetry === true) { - return this.sendCommand(firstKey, isReadonly, args, options, bufferMode, redirections + 1); + return this.sendCommand(firstKey, isReadonly, args, options, redirections + 1); } else if (shouldRetry) { - return shouldRetry.sendCommand(args, options, bufferMode); + return shouldRetry.sendCommand(args, options); } throw err; @@ -135,13 +133,13 @@ export default class RedisCluster ); try { - return await client.executeScript(script, redisArgs, options, script.BUFFER_MODE); + return await client.executeScript(script, redisArgs, options); } catch (err: any) { const shouldRetry = await this.#handleCommandError(err, client, redirections); if (shouldRetry === true) { return this.executeScript(script, originalArgs, redisArgs, options, redirections + 1); } else if (shouldRetry) { - return shouldRetry.executeScript(script, redisArgs, options, script.BUFFER_MODE); + return shouldRetry.executeScript(script, redisArgs, options); } throw err; diff --git a/packages/client/lib/commands/ACL_CAT.ts b/packages/client/lib/commands/ACL_CAT.ts index f11be873961..161546cfbe9 100644 --- a/packages/client/lib/commands/ACL_CAT.ts +++ b/packages/client/lib/commands/ACL_CAT.ts @@ -1,5 +1,7 @@ -export function transformArguments(categoryName?: string): Array { - const args = ['ACL', 'CAT']; +import { RedisCommandArgument, RedisCommandArguments } from '.'; + +export function transformArguments(categoryName?: RedisCommandArgument): RedisCommandArguments { + const args: RedisCommandArguments = ['ACL', 'CAT']; if (categoryName) { args.push(categoryName); @@ -8,4 +10,4 @@ export function transformArguments(categoryName?: string): Array { return args; } -export declare function transformReply(): Array; +export declare function transformReply(): Array; diff --git a/packages/client/lib/commands/ACL_DELUSER.ts b/packages/client/lib/commands/ACL_DELUSER.ts index 97f50d48945..25ed1a10300 100644 --- a/packages/client/lib/commands/ACL_DELUSER.ts +++ b/packages/client/lib/commands/ACL_DELUSER.ts @@ -1,8 +1,10 @@ -import { RedisCommandArguments } from '.'; +import { RedisCommandArgument, RedisCommandArguments } from '.'; import { pushVerdictArguments } from './generic-transformers'; -export function transformArguments(username: string | Array): RedisCommandArguments { +export function transformArguments( + username: RedisCommandArgument | Array +): RedisCommandArguments { return pushVerdictArguments(['ACL', 'DELUSER'], username); } -export declare const transformReply: (reply: number) => number; +export declare function transformReply(): number; diff --git a/packages/client/lib/commands/ACL_GENPASS.ts b/packages/client/lib/commands/ACL_GENPASS.ts index 6a36c396619..91a71e220e0 100644 --- a/packages/client/lib/commands/ACL_GENPASS.ts +++ b/packages/client/lib/commands/ACL_GENPASS.ts @@ -1,4 +1,6 @@ -export function transformArguments(bits?: number): Array { +import { RedisCommandArgument, RedisCommandArguments } from '.'; + +export function transformArguments(bits?: number): RedisCommandArguments { const args = ['ACL', 'GENPASS']; if (bits) { @@ -8,4 +10,4 @@ export function transformArguments(bits?: number): Array { return args; } -export declare function transformReply(): string; +export declare function transformReply(): RedisCommandArgument; diff --git a/packages/client/lib/commands/ACL_GETUSER.ts b/packages/client/lib/commands/ACL_GETUSER.ts index 876a723c390..cdb9f3aa783 100644 --- a/packages/client/lib/commands/ACL_GETUSER.ts +++ b/packages/client/lib/commands/ACL_GETUSER.ts @@ -1,26 +1,28 @@ -export function transformArguments(username: string): Array { +import { RedisCommandArgument, RedisCommandArguments } from '.'; + +export function transformArguments(username: RedisCommandArgument): RedisCommandArguments { return ['ACL', 'GETUSER', username]; } type AclGetUserRawReply = [ - _: string, - flags: Array, - _: string, - passwords: Array, - _: string, - commands: string, - _: string, - keys: Array, - _: string, - channels: Array + _: RedisCommandArgument, + flags: Array, + _: RedisCommandArgument, + passwords: Array, + _: RedisCommandArgument, + commands: RedisCommandArgument, + _: RedisCommandArgument, + keys: Array, + _: RedisCommandArgument, + channels: Array ]; interface AclUser { - flags: Array; - passwords: Array; - commands: string; - keys: Array; - channels: Array + flags: Array; + passwords: Array; + commands: RedisCommandArgument; + keys: Array; + channels: Array } export function transformReply(reply: AclGetUserRawReply): AclUser { diff --git a/packages/client/lib/commands/ACL_LIST.ts b/packages/client/lib/commands/ACL_LIST.ts index a2caae82c46..ae523fe9ce9 100644 --- a/packages/client/lib/commands/ACL_LIST.ts +++ b/packages/client/lib/commands/ACL_LIST.ts @@ -1,5 +1,7 @@ -export function transformArguments(): Array { +import { RedisCommandArgument, RedisCommandArguments } from '.'; + +export function transformArguments(): RedisCommandArguments { return ['ACL', 'LIST']; } -export declare function transformReply(): Array; +export declare function transformReply(): Array; diff --git a/packages/client/lib/commands/ACL_LOAD.ts b/packages/client/lib/commands/ACL_LOAD.ts index 0a5ea85101d..88309102b95 100644 --- a/packages/client/lib/commands/ACL_LOAD.ts +++ b/packages/client/lib/commands/ACL_LOAD.ts @@ -1,5 +1,7 @@ -export function transformArguments(): Array { +import { RedisCommandArgument, RedisCommandArguments } from '.'; + +export function transformArguments(): RedisCommandArguments { return ['ACL', 'LOAD']; } -export declare function transformReply(): string; +export declare function transformReply(): RedisCommandArgument; diff --git a/packages/client/lib/commands/ACL_LOG.ts b/packages/client/lib/commands/ACL_LOG.ts index ed0590b5783..0fd9aa6f19d 100644 --- a/packages/client/lib/commands/ACL_LOG.ts +++ b/packages/client/lib/commands/ACL_LOG.ts @@ -1,4 +1,6 @@ -export function transformArguments(count?: number): Array { +import { RedisCommandArgument, RedisCommandArguments } from '.'; + +export function transformArguments(count?: number): RedisCommandArguments { const args = ['ACL', 'LOG']; if (count) { @@ -9,30 +11,30 @@ export function transformArguments(count?: number): Array { } type AclLogRawReply = [ - _: string, + _: RedisCommandArgument, count: number, - _: string, - reason: string, - _: string, - context: string, - _: string, - object: string, - _: string, - username: string, - _: string, - ageSeconds: string, - _: string, - clientInfo: string + _: RedisCommandArgument, + reason: RedisCommandArgument, + _: RedisCommandArgument, + context: RedisCommandArgument, + _: RedisCommandArgument, + object: RedisCommandArgument, + _: RedisCommandArgument, + username: RedisCommandArgument, + _: RedisCommandArgument, + ageSeconds: RedisCommandArgument, + _: RedisCommandArgument, + clientInfo: RedisCommandArgument ]; interface AclLog { count: number; - reason: string; - context: string; - object: string; - username: string; + reason: RedisCommandArgument; + context: RedisCommandArgument; + object: RedisCommandArgument; + username: RedisCommandArgument; ageSeconds: number; - clientInfo: string; + clientInfo: RedisCommandArgument; } export function transformReply(reply: Array): Array { diff --git a/packages/client/lib/commands/ACL_LOG_RESET.ts b/packages/client/lib/commands/ACL_LOG_RESET.ts index 5bfd7ae9392..8ff0be4f8b9 100644 --- a/packages/client/lib/commands/ACL_LOG_RESET.ts +++ b/packages/client/lib/commands/ACL_LOG_RESET.ts @@ -1,5 +1,7 @@ -export function transformArguments(): Array { +import { RedisCommandArgument, RedisCommandArguments } from '.'; + +export function transformArguments(): RedisCommandArguments { return ['ACL', 'LOG', 'RESET']; } -export declare function transformReply(): string; +export declare function transformReply(): RedisCommandArgument; diff --git a/packages/client/lib/commands/ACL_SAVE.ts b/packages/client/lib/commands/ACL_SAVE.ts index af9abeb1d5c..e57cd697297 100644 --- a/packages/client/lib/commands/ACL_SAVE.ts +++ b/packages/client/lib/commands/ACL_SAVE.ts @@ -1,5 +1,7 @@ -export function transformArguments(): Array { +import { RedisCommandArgument, RedisCommandArguments } from '.'; + +export function transformArguments(): RedisCommandArguments { return ['ACL', 'SAVE']; } -export declare function transformReply(): string; +export declare function transformReply(): RedisCommandArgument; diff --git a/packages/client/lib/commands/ACL_SETUSER.ts b/packages/client/lib/commands/ACL_SETUSER.ts index d8734f0a1ca..a12cc8ed24e 100644 --- a/packages/client/lib/commands/ACL_SETUSER.ts +++ b/packages/client/lib/commands/ACL_SETUSER.ts @@ -1,8 +1,11 @@ -import { RedisCommandArguments } from '.'; +import { RedisCommandArgument, RedisCommandArguments } from '.'; import { pushVerdictArguments } from './generic-transformers'; -export function transformArguments(username: string, rule: string | Array): RedisCommandArguments { +export function transformArguments( + username: RedisCommandArgument, + rule: RedisCommandArgument | Array +): RedisCommandArguments { return pushVerdictArguments(['ACL', 'SETUSER', username], rule); } -export declare function transformReply(): string; +export declare function transformReply(): RedisCommandArgument; diff --git a/packages/client/lib/commands/ACL_USERS.ts b/packages/client/lib/commands/ACL_USERS.ts index 91f391a5b73..7970a262e26 100644 --- a/packages/client/lib/commands/ACL_USERS.ts +++ b/packages/client/lib/commands/ACL_USERS.ts @@ -1,5 +1,7 @@ -export function transformArguments(): Array { +import { RedisCommandArgument, RedisCommandArguments } from '.'; + +export function transformArguments(): RedisCommandArguments { return ['ACL', 'USERS']; } -export declare function transformReply(): Array; +export declare function transformReply(): Array; diff --git a/packages/client/lib/commands/ACL_WHOAMI.ts b/packages/client/lib/commands/ACL_WHOAMI.ts index c855eeb1aa7..3c41171638e 100644 --- a/packages/client/lib/commands/ACL_WHOAMI.ts +++ b/packages/client/lib/commands/ACL_WHOAMI.ts @@ -1,5 +1,7 @@ -export function transformArguments(): Array { +import { RedisCommandArgument, RedisCommandArguments } from '.'; + +export function transformArguments(): RedisCommandArguments { return ['ACL', 'WHOAMI']; } -export declare function transformReply(): string; +export declare function transformReply(): RedisCommandArgument; diff --git a/packages/client/lib/commands/ASKING.ts b/packages/client/lib/commands/ASKING.ts index 8071ec20180..8a87806fe62 100644 --- a/packages/client/lib/commands/ASKING.ts +++ b/packages/client/lib/commands/ASKING.ts @@ -1,5 +1,7 @@ -export function transformArguments(): Array { +import { RedisCommandArguments, RedisCommandArgument } from '.'; + +export function transformArguments(): RedisCommandArguments { return ['ASKING']; } -export declare function transformReply(): string; +export declare function transformReply(): RedisCommandArgument; diff --git a/packages/client/lib/commands/AUTH.ts b/packages/client/lib/commands/AUTH.ts index eb2449208a8..49b0df6d313 100644 --- a/packages/client/lib/commands/AUTH.ts +++ b/packages/client/lib/commands/AUTH.ts @@ -1,9 +1,11 @@ +import { RedisCommandArgument, RedisCommandArguments } from '.'; + export interface AuthOptions { - username?: string; - password: string; + username?: RedisCommandArgument; + password: RedisCommandArgument; } -export function transformArguments({username, password}: AuthOptions): Array { +export function transformArguments({ username, password }: AuthOptions): RedisCommandArguments { if (!username) { return ['AUTH', password]; } @@ -11,4 +13,4 @@ export function transformArguments({username, password}: AuthOptions): Array { +import { RedisCommandArgument, RedisCommandArguments } from '.'; + +export function transformArguments(): RedisCommandArguments { return ['BGREWRITEAOF']; } -export declare function transformReply(): string; +export declare function transformReply(): RedisCommandArgument; diff --git a/packages/client/lib/commands/BGSAVE.ts b/packages/client/lib/commands/BGSAVE.ts index fba9e37ed76..9c90f3485be 100644 --- a/packages/client/lib/commands/BGSAVE.ts +++ b/packages/client/lib/commands/BGSAVE.ts @@ -1,8 +1,10 @@ +import { RedisCommandArgument, RedisCommandArguments } from '.'; + interface BgSaveOptions { SCHEDULE?: true; } -export function transformArguments(options?: BgSaveOptions): Array { +export function transformArguments(options?: BgSaveOptions): RedisCommandArguments { const args = ['BGSAVE']; if (options?.SCHEDULE) { @@ -12,4 +14,4 @@ export function transformArguments(options?: BgSaveOptions): Array { return args; } -export declare function transformReply(): string; +export declare function transformReply(): RedisCommandArgument; diff --git a/packages/client/lib/commands/BITCOUNT.ts b/packages/client/lib/commands/BITCOUNT.ts index 320d8f3acb7..efbc6f2220d 100644 --- a/packages/client/lib/commands/BITCOUNT.ts +++ b/packages/client/lib/commands/BITCOUNT.ts @@ -1,3 +1,5 @@ +import { RedisCommandArgument, RedisCommandArguments } from '.'; + export const FIRST_KEY_INDEX = 1; export const IS_READ_ONLY = true; @@ -7,7 +9,10 @@ interface BitCountRange { end: number; } -export function transformArguments(key: string, range?: BitCountRange): Array { +export function transformArguments( + key: RedisCommandArgument, + range?: BitCountRange +): RedisCommandArguments { const args = ['BITCOUNT', key]; if (range) { diff --git a/packages/client/lib/commands/BITOP.ts b/packages/client/lib/commands/BITOP.ts index af31f42f1dc..e2953303d41 100644 --- a/packages/client/lib/commands/BITOP.ts +++ b/packages/client/lib/commands/BITOP.ts @@ -1,11 +1,15 @@ -import { RedisCommandArguments } from '.'; +import { RedisCommandArgument, RedisCommandArguments } from '.'; import { pushVerdictArguments } from './generic-transformers'; export const FIRST_KEY_INDEX = 2; type BitOperations = 'AND' | 'OR' | 'XOR' | 'NOT'; -export function transformArguments(operation: BitOperations, destKey: string, key: string | Array): RedisCommandArguments { +export function transformArguments( + operation: BitOperations, + destKey: RedisCommandArgument, + key: RedisCommandArgument | Array +): RedisCommandArguments { return pushVerdictArguments(['BITOP', operation, destKey], key); } diff --git a/packages/client/lib/commands/BITPOS.ts b/packages/client/lib/commands/BITPOS.ts index 86f539f2dda..2e54b11bc9e 100644 --- a/packages/client/lib/commands/BITPOS.ts +++ b/packages/client/lib/commands/BITPOS.ts @@ -1,10 +1,16 @@ +import { RedisCommandArgument, RedisCommandArguments } from '.'; import { BitValue } from './generic-transformers'; export const FIRST_KEY_INDEX = 1; export const IS_READ_ONLY = true; -export function transformArguments(key: string, bit: BitValue, start?: number, end?: number): Array { +export function transformArguments( + key: RedisCommandArgument, + bit: BitValue, + start?: number, + end?: number +): RedisCommandArguments { const args = ['BITPOS', key, bit.toString()]; if (typeof start === 'number') { diff --git a/packages/client/lib/commands/BLMOVE.ts b/packages/client/lib/commands/BLMOVE.ts index 1aa71458922..329192f634b 100644 --- a/packages/client/lib/commands/BLMOVE.ts +++ b/packages/client/lib/commands/BLMOVE.ts @@ -20,4 +20,4 @@ export function transformArguments( ]; } -export declare function transformReply(): string | null; +export declare function transformReply(): RedisCommandArgument | null; diff --git a/packages/client/lib/commands/BLMOVE_BUFFER.ts b/packages/client/lib/commands/BLMOVE_BUFFER.ts deleted file mode 100644 index 2e676281e2e..00000000000 --- a/packages/client/lib/commands/BLMOVE_BUFFER.ts +++ /dev/null @@ -1,5 +0,0 @@ -export { FIRST_KEY_INDEX, transformArguments } from './BLMOVE'; - -export const BUFFER_MODE = true; - -export declare function transformReply(): Buffer | null; diff --git a/packages/client/lib/commands/BLPOP.ts b/packages/client/lib/commands/BLPOP.ts index 53eaa0b78e5..46ef41ad6f0 100644 --- a/packages/client/lib/commands/BLPOP.ts +++ b/packages/client/lib/commands/BLPOP.ts @@ -14,12 +14,14 @@ export function transformArguments( return args; } +type BLPopRawReply = null | [RedisCommandArgument, RedisCommandArgument]; + type BLPopReply = null | { - key: string; - element: string; + key: RedisCommandArgument; + element: RedisCommandArgument; }; -export function transformReply(reply: null | [string, string]): BLPopReply { +export function transformReply(reply: BLPopRawReply): BLPopReply { if (reply === null) return null; return { diff --git a/packages/client/lib/commands/BLPOP_BUFFER.ts b/packages/client/lib/commands/BLPOP_BUFFER.ts deleted file mode 100644 index 0f574db846e..00000000000 --- a/packages/client/lib/commands/BLPOP_BUFFER.ts +++ /dev/null @@ -1,17 +0,0 @@ -export { FIRST_KEY_INDEX, transformArguments } from './BLPOP'; - -export const BUFFER_MODE = true; - -type BLPopBufferReply = null | { - key: Buffer; - element: Buffer; -}; - -export function transformReply(reply: null | [Buffer, Buffer]): BLPopBufferReply { - if (reply === null) return null; - - return { - key: reply[0], - element: reply[1] - }; -} diff --git a/packages/client/lib/commands/BRPOP.ts b/packages/client/lib/commands/BRPOP.ts index e0a0c82c9e1..b30e7e2cc29 100644 --- a/packages/client/lib/commands/BRPOP.ts +++ b/packages/client/lib/commands/BRPOP.ts @@ -1,9 +1,12 @@ -import { RedisCommandArguments } from '.'; +import { RedisCommandArgument, RedisCommandArguments } from '.'; import { pushVerdictArguments } from './generic-transformers'; export const FIRST_KEY_INDEX = 1; -export function transformArguments(key: string | Array, timeout: number): RedisCommandArguments { +export function transformArguments( + key: RedisCommandArgument | Array, + timeout: number +): RedisCommandArguments { const args = pushVerdictArguments(['BRPOP'], key); args.push(timeout.toString()); diff --git a/packages/client/lib/commands/BRPOPLPUSH.ts b/packages/client/lib/commands/BRPOPLPUSH.ts index 7c671fd85a8..72c3e4aa5b2 100644 --- a/packages/client/lib/commands/BRPOPLPUSH.ts +++ b/packages/client/lib/commands/BRPOPLPUSH.ts @@ -10,4 +10,4 @@ export function transformArguments( return ['BRPOPLPUSH', source, destination, timeout.toString()]; } -export declare function transformReply(): string | null; +export declare function transformReply(): RedisCommandArgument | null; diff --git a/packages/client/lib/commands/BRPOPLPUSH_BUFFER.ts b/packages/client/lib/commands/BRPOPLPUSH_BUFFER.ts deleted file mode 100644 index 255f88bab8c..00000000000 --- a/packages/client/lib/commands/BRPOPLPUSH_BUFFER.ts +++ /dev/null @@ -1,5 +0,0 @@ -export { FIRST_KEY_INDEX, transformArguments } from './BRPOPLPUSH'; - -export const BUFFER_MODE = true; - -export declare function transformReply(): Buffer | null; diff --git a/packages/client/lib/commands/BRPOP_BUFFER.ts b/packages/client/lib/commands/BRPOP_BUFFER.ts deleted file mode 100644 index a30a661da07..00000000000 --- a/packages/client/lib/commands/BRPOP_BUFFER.ts +++ /dev/null @@ -1,3 +0,0 @@ -export { FIRST_KEY_INDEX, transformArguments } from './BRPOP'; - -export { BUFFER_MODE, transformReply } from './BLPOP_BUFFER'; diff --git a/packages/client/lib/commands/BZPOPMAX.ts b/packages/client/lib/commands/BZPOPMAX.ts index 90f9835f27c..94a30fb8dce 100644 --- a/packages/client/lib/commands/BZPOPMAX.ts +++ b/packages/client/lib/commands/BZPOPMAX.ts @@ -1,5 +1,5 @@ import { RedisCommandArgument, RedisCommandArguments } from '.'; -import { pushVerdictArguments, transformReplyNumberInfinity, ZMember } from './generic-transformers'; +import { pushVerdictArguments, transformNumberInfinityReply, ZMember } from './generic-transformers'; export const FIRST_KEY_INDEX = 1; @@ -14,18 +14,16 @@ export function transformArguments( return args; } -interface ZMemberWithKey extends ZMember { - key: string; -} +type ZMemberRawReply = [key: RedisCommandArgument, value: RedisCommandArgument, score: RedisCommandArgument] | null; -type BZPopMaxReply = ZMemberWithKey | null; +type BZPopMaxReply = (ZMember & { key: RedisCommandArgument }) | null; -export function transformReply(reply: [key: string, value: string, score: string] | null): BZPopMaxReply | null { +export function transformReply(reply: ZMemberRawReply): BZPopMaxReply | null { if (!reply) return null; return { key: reply[0], value: reply[1], - score: transformReplyNumberInfinity(reply[2]) + score: transformNumberInfinityReply(reply[2]) }; } diff --git a/packages/client/lib/commands/BZPOPMIN.ts b/packages/client/lib/commands/BZPOPMIN.ts index 098d9394046..40cb3d5dc75 100644 --- a/packages/client/lib/commands/BZPOPMIN.ts +++ b/packages/client/lib/commands/BZPOPMIN.ts @@ -1,9 +1,12 @@ -import { RedisCommandArguments } from '.'; -import { pushVerdictArguments, transformReplyNumberInfinity, ZMember } from './generic-transformers'; +import { RedisCommandArgument, RedisCommandArguments } from '.'; +import { pushVerdictArguments } from './generic-transformers'; export const FIRST_KEY_INDEX = 1; -export function transformArguments(key: string | Array, timeout: number): RedisCommandArguments { +export function transformArguments( + key: RedisCommandArgument | Array, + timeout: number +): RedisCommandArguments { const args = pushVerdictArguments(['BZPOPMIN'], key); args.push(timeout.toString()); @@ -11,18 +14,4 @@ export function transformArguments(key: string | Array, timeout: number) return args; } -interface ZMemberWithKey extends ZMember { - key: string; -} - -type BZPopMinReply = ZMemberWithKey | null; - -export function transformReply(reply: [key: string, value: string, score: string] | null): BZPopMinReply | null { - if (!reply) return null; - - return { - key: reply[0], - value: reply[1], - score: transformReplyNumberInfinity(reply[2]) - }; -} +export { transformReply } from './BZPOPMAX'; diff --git a/packages/client/lib/commands/CLIENT_CACHING.ts b/packages/client/lib/commands/CLIENT_CACHING.ts index 62a46bad6c5..bc2fbe41e9d 100644 --- a/packages/client/lib/commands/CLIENT_CACHING.ts +++ b/packages/client/lib/commands/CLIENT_CACHING.ts @@ -8,4 +8,4 @@ export function transformArguments(value: boolean): RedisCommandArguments { ]; } -export declare function transformReply(): 'OK'; +export declare function transformReply(): 'OK' | Buffer; diff --git a/packages/client/lib/commands/CLIENT_SETNAME.ts b/packages/client/lib/commands/CLIENT_SETNAME.ts index 562fa9f2e96..f5cf1c786fb 100644 --- a/packages/client/lib/commands/CLIENT_SETNAME.ts +++ b/packages/client/lib/commands/CLIENT_SETNAME.ts @@ -1,7 +1,7 @@ -import { RedisCommandArguments } from '.'; +import { RedisCommandArgument, RedisCommandArguments } from '.'; -export function transformArguments(name: string): RedisCommandArguments { +export function transformArguments(name: RedisCommandArgument): RedisCommandArguments { return ['CLIENT', 'SETNAME', name]; } -export declare function transformReply(): string | null; +export declare function transformReply(): RedisCommandArgument; diff --git a/packages/client/lib/commands/COMMAND_GETKEYS.ts b/packages/client/lib/commands/COMMAND_GETKEYS.ts index 1c38515aef9..6762fe4b58a 100644 --- a/packages/client/lib/commands/COMMAND_GETKEYS.ts +++ b/packages/client/lib/commands/COMMAND_GETKEYS.ts @@ -1,9 +1,9 @@ -import { RedisCommandArguments } from '.'; +import { RedisCommandArgument, RedisCommandArguments } from '.'; export const IS_READ_ONLY = true; -export function transformArguments(args: Array): RedisCommandArguments { +export function transformArguments(args: Array): RedisCommandArguments { return ['COMMAND', 'GETKEYS', ...args]; } -export declare function transformReply(): Array; +export declare function transformReply(): Array; diff --git a/packages/client/lib/commands/CONFIG_GET.ts b/packages/client/lib/commands/CONFIG_GET.ts index 3a4f0105d3d..3afc0eddfd0 100644 --- a/packages/client/lib/commands/CONFIG_GET.ts +++ b/packages/client/lib/commands/CONFIG_GET.ts @@ -2,4 +2,4 @@ export function transformArguments(parameter: string): Array { return ['CONFIG', 'GET', parameter]; } -export { transformReplyStringTuples as transformReply } from './generic-transformers'; +export { transformTuplesReply as transformReply } from './generic-transformers'; diff --git a/packages/client/lib/commands/COPY.ts b/packages/client/lib/commands/COPY.ts index c7a44e45c3c..b1e212a9956 100644 --- a/packages/client/lib/commands/COPY.ts +++ b/packages/client/lib/commands/COPY.ts @@ -1,3 +1,5 @@ +import { RedisCommandArgument, RedisCommandArguments } from '.'; + interface CopyCommandOptions { destinationDb?: number; replace?: boolean; @@ -5,7 +7,11 @@ interface CopyCommandOptions { export const FIRST_KEY_INDEX = 1; -export function transformArguments(source: string, destination: string, options?: CopyCommandOptions): Array { +export function transformArguments( + source: RedisCommandArgument, + destination: RedisCommandArgument, + options?: CopyCommandOptions +): RedisCommandArguments { const args = ['COPY', source, destination]; if (options?.destinationDb) { @@ -19,4 +25,4 @@ export function transformArguments(source: string, destination: string, options? return args; } -export { transformReplyBoolean as transformReply } from './generic-transformers'; +export { transformBooleanReply as transformReply } from './generic-transformers'; diff --git a/packages/client/lib/commands/DECR.ts b/packages/client/lib/commands/DECR.ts index e30d2aaf29c..2b5f2c4bb5c 100644 --- a/packages/client/lib/commands/DECR.ts +++ b/packages/client/lib/commands/DECR.ts @@ -1,6 +1,8 @@ +import { RedisCommandArgument, RedisCommandArguments } from '.'; + export const FIRST_KEY_INDEX = 1; -export function transformArguments(key: string): Array { +export function transformArguments(key: RedisCommandArgument): RedisCommandArguments { return ['DECR', key]; } diff --git a/packages/client/lib/commands/DECRBY.ts b/packages/client/lib/commands/DECRBY.ts index 561eb9491c4..afe4d79f0a1 100644 --- a/packages/client/lib/commands/DECRBY.ts +++ b/packages/client/lib/commands/DECRBY.ts @@ -1,6 +1,11 @@ +import { RedisCommandArgument, RedisCommandArguments } from '.'; + export const FIRST_KEY_INDEX = 1; -export function transformArguments(key: string, decrement: number): Array { +export function transformArguments( + key: RedisCommandArgument, + decrement: number +): RedisCommandArguments { return ['DECRBY', key, decrement.toString()]; } diff --git a/packages/client/lib/commands/DEL.ts b/packages/client/lib/commands/DEL.ts index 02ef553f647..7597cf09cb0 100644 --- a/packages/client/lib/commands/DEL.ts +++ b/packages/client/lib/commands/DEL.ts @@ -1,7 +1,9 @@ -import { RedisCommandArguments } from '.'; +import { RedisCommandArgument, RedisCommandArguments } from '.'; import { pushVerdictArguments } from './generic-transformers'; -export function transformArguments(keys: string | Array): RedisCommandArguments { +export function transformArguments( + keys: RedisCommandArgument | Array +): RedisCommandArguments { return pushVerdictArguments(['DEL'], keys); } diff --git a/packages/client/lib/commands/DISCARD.ts b/packages/client/lib/commands/DISCARD.ts index 444f800db80..acad8a722e1 100644 --- a/packages/client/lib/commands/DISCARD.ts +++ b/packages/client/lib/commands/DISCARD.ts @@ -1,5 +1,7 @@ +import { RedisCommandArgument } from '.'; + export function transformArguments(): Array { return ['DISCARD']; } -export declare function transformReply(): string; +export declare function transformReply(): RedisCommandArgument; diff --git a/packages/client/lib/commands/DUMP.ts b/packages/client/lib/commands/DUMP.ts index de85b889bb9..79805795ed9 100644 --- a/packages/client/lib/commands/DUMP.ts +++ b/packages/client/lib/commands/DUMP.ts @@ -1,5 +1,7 @@ -export function transformArguments(key: string): Array { +import { RedisCommandArgument, RedisCommandArguments } from '.'; + +export function transformArguments(key: RedisCommandArgument): RedisCommandArguments { return ['DUMP', key]; } -export declare function transformReply(): string; +export declare function transformReply(): RedisCommandArgument; diff --git a/packages/client/lib/commands/ECHO.ts b/packages/client/lib/commands/ECHO.ts index 75a91d4ac91..7a837307e2b 100644 --- a/packages/client/lib/commands/ECHO.ts +++ b/packages/client/lib/commands/ECHO.ts @@ -1,7 +1,9 @@ +import { RedisCommandArgument, RedisCommandArguments } from '.'; + export const IS_READ_ONLY = true; -export function transformArguments(message: string): Array { +export function transformArguments(message: RedisCommandArgument): RedisCommandArguments { return ['ECHO', message]; } -export declare function transformReply(): string; +export declare function transformReply(): RedisCommandArgument; diff --git a/packages/client/lib/commands/EXISTS.ts b/packages/client/lib/commands/EXISTS.ts index 5a9c0e3be66..3b4665fc7f2 100644 --- a/packages/client/lib/commands/EXISTS.ts +++ b/packages/client/lib/commands/EXISTS.ts @@ -1,12 +1,14 @@ -import { RedisCommandArguments } from '.'; +import { RedisCommandArgument, RedisCommandArguments } from '.'; import { pushVerdictArguments } from './generic-transformers'; export const FIRST_KEY_INDEX = 1; export const IS_READ_ONLY = true; -export function transformArguments(keys: string | Array): RedisCommandArguments { +export function transformArguments( + keys: RedisCommandArgument | Array +): RedisCommandArguments { return pushVerdictArguments(['EXISTS'], keys); } -export { transformReplyBoolean as transformReply } from './generic-transformers'; +export { transformBooleanReply as transformReply } from './generic-transformers'; diff --git a/packages/client/lib/commands/EXPIRE.ts b/packages/client/lib/commands/EXPIRE.ts index 36bcf8b882d..291b544c6ac 100644 --- a/packages/client/lib/commands/EXPIRE.ts +++ b/packages/client/lib/commands/EXPIRE.ts @@ -1,5 +1,12 @@ -export function transformArguments(key: string, seconds: number): Array { +import { RedisCommandArgument, RedisCommandArguments } from '.'; + +export const FIRST_KEY_INDEX = 1; + +export function transformArguments( + key: RedisCommandArgument, + seconds: number +): RedisCommandArguments { return ['EXPIRE', key, seconds.toString()]; } -export { transformReplyBoolean as transformReply } from './generic-transformers'; +export { transformBooleanReply as transformReply } from './generic-transformers'; diff --git a/packages/client/lib/commands/EXPIREAT.ts b/packages/client/lib/commands/EXPIREAT.ts index 72142e4cb79..3ddb75fe181 100644 --- a/packages/client/lib/commands/EXPIREAT.ts +++ b/packages/client/lib/commands/EXPIREAT.ts @@ -1,6 +1,12 @@ +import { RedisCommandArgument, RedisCommandArguments } from '.'; import { transformEXAT } from './generic-transformers'; -export function transformArguments(key: string, timestamp: number | Date): Array { +export const FIRST_KEY_INDEX = 1; + +export function transformArguments( + key: RedisCommandArgument, + timestamp: number | Date +): RedisCommandArguments { return [ 'EXPIREAT', key, @@ -8,4 +14,4 @@ export function transformArguments(key: string, timestamp: number | Date): Array ]; } -export { transformReplyBoolean as transformReply } from './generic-transformers'; +export { transformBooleanReply as transformReply } from './generic-transformers'; diff --git a/packages/client/lib/commands/GEOADD.ts b/packages/client/lib/commands/GEOADD.ts index 7f5ac5533e3..74adeda5142 100644 --- a/packages/client/lib/commands/GEOADD.ts +++ b/packages/client/lib/commands/GEOADD.ts @@ -1,7 +1,8 @@ +import { RedisCommandArgument, RedisCommandArguments } from '.'; import { GeoCoordinates } from './generic-transformers'; interface GeoMember extends GeoCoordinates { - member: string; + member: RedisCommandArgument; } interface NX { @@ -22,7 +23,10 @@ type GeoAddOptions = SetGuards & GeoAddCommonOptions; export const FIRST_KEY_INDEX = 1; -export function transformArguments(key: string, toAdd: GeoMember | Array, options?: GeoAddOptions): Array { +export function transformArguments( + key: RedisCommandArgument, toAdd: GeoMember | Array, + options?: GeoAddOptions +): RedisCommandArguments { const args = ['GEOADD', key]; if ((options as NX)?.NX) { diff --git a/packages/client/lib/commands/GEODIST.ts b/packages/client/lib/commands/GEODIST.ts index 6fe6fbb47ab..5dbf8ece9cc 100644 --- a/packages/client/lib/commands/GEODIST.ts +++ b/packages/client/lib/commands/GEODIST.ts @@ -1,3 +1,4 @@ +import { RedisCommandArgument, RedisCommandArguments } from '.'; import { GeoUnits } from './generic-transformers'; export const FIRST_KEY_INDEX = 1; @@ -5,11 +6,11 @@ export const FIRST_KEY_INDEX = 1; export const IS_READ_ONLY = true; export function transformArguments( - key: string, - member1: string, - member2: string, + key: RedisCommandArgument, + member1: RedisCommandArgument, + member2: RedisCommandArgument, unit?: GeoUnits -): Array { +): RedisCommandArguments { const args = ['GEODIST', key, member1, member2]; if (unit) { @@ -19,6 +20,6 @@ export function transformArguments( return args; } -export function transformReply(reply: string | null): number | null { +export function transformReply(reply: RedisCommandArgument | null): number | null { return reply === null ? null : Number(reply); } diff --git a/packages/client/lib/commands/GEOHASH.ts b/packages/client/lib/commands/GEOHASH.ts index 8613f37fa43..55e22c497e2 100644 --- a/packages/client/lib/commands/GEOHASH.ts +++ b/packages/client/lib/commands/GEOHASH.ts @@ -1,12 +1,15 @@ -import { RedisCommandArguments } from '.'; +import { RedisCommandArgument, RedisCommandArguments } from '.'; import { pushVerdictArguments } from './generic-transformers'; export const FIRST_KEY_INDEX = 1; export const IS_READ_ONLY = true; -export function transformArguments(key: string, member: string | Array): RedisCommandArguments { +export function transformArguments( + key: RedisCommandArgument, + member: RedisCommandArgument | Array +): RedisCommandArguments { return pushVerdictArguments(['GEOHASH', key], member); } -export declare function transformReply(): Array; +export declare function transformReply(): Array; diff --git a/packages/client/lib/commands/GEOPOS.ts b/packages/client/lib/commands/GEOPOS.ts index 95f33d9e3a8..0a5f079deeb 100644 --- a/packages/client/lib/commands/GEOPOS.ts +++ b/packages/client/lib/commands/GEOPOS.ts @@ -1,20 +1,25 @@ -import { RedisCommandArguments } from '.'; +import { RedisCommandArgument, RedisCommandArguments } from '.'; import { pushVerdictArguments } from './generic-transformers'; export const FIRST_KEY_INDEX = 1; export const IS_READ_ONLY = true; -export function transformArguments(key: string, member: string | Array): RedisCommandArguments { +export function transformArguments( + key: RedisCommandArgument, + member: RedisCommandArgument | Array +): RedisCommandArguments { return pushVerdictArguments(['GEOPOS', key], member); } +type GeoCoordinatesRawReply = Array<[RedisCommandArgument, RedisCommandArgument] | null>; + interface GeoCoordinates { - longitude: string; - latitude: string; + longitude: RedisCommandArgument; + latitude: RedisCommandArgument; } -export function transformReply(reply: Array<[string, string] | null>): Array { +export function transformReply(reply: GeoCoordinatesRawReply): Array { return reply.map(coordinates => coordinates === null ? null : { longitude: coordinates[0], latitude: coordinates[1] diff --git a/packages/client/lib/commands/GEOSEARCH.ts b/packages/client/lib/commands/GEOSEARCH.ts index 5453a2ae1b1..a02a21391f6 100644 --- a/packages/client/lib/commands/GEOSEARCH.ts +++ b/packages/client/lib/commands/GEOSEARCH.ts @@ -1,3 +1,4 @@ +import { RedisCommandArgument, RedisCommandArguments } from '.'; import { GeoSearchFrom, GeoSearchBy, GeoSearchOptions, pushGeoSearchArguments } from './generic-transformers'; export const FIRST_KEY_INDEX = 1; @@ -5,12 +6,12 @@ export const FIRST_KEY_INDEX = 1; export const IS_READ_ONLY = true; export function transformArguments( - key: string, + key: RedisCommandArgument, from: GeoSearchFrom, by: GeoSearchBy, options?: GeoSearchOptions -): Array { +): RedisCommandArguments { return pushGeoSearchArguments(['GEOSEARCH'], key, from, by, options); } -export declare function transformReply(): Array; +export declare function transformReply(): Array; diff --git a/packages/client/lib/commands/GEOSEARCHSTORE.ts b/packages/client/lib/commands/GEOSEARCHSTORE.ts index e10622052ba..bc06659ef09 100644 --- a/packages/client/lib/commands/GEOSEARCHSTORE.ts +++ b/packages/client/lib/commands/GEOSEARCHSTORE.ts @@ -1,3 +1,4 @@ +import { RedisCommandArgument, RedisCommandArguments } from '.'; import { GeoSearchFrom, GeoSearchBy, GeoSearchOptions, pushGeoSearchArguments } from './generic-transformers'; export const FIRST_KEY_INDEX = 1; @@ -9,12 +10,12 @@ interface GeoSearchStoreOptions extends GeoSearchOptions { } export function transformArguments( - destination: string, - source: string, + destination: RedisCommandArgument, + source: RedisCommandArgument, from: GeoSearchFrom, by: GeoSearchBy, options?: GeoSearchStoreOptions -): Array { +): RedisCommandArguments { const args = pushGeoSearchArguments( ['GEOSEARCHSTORE', destination], source, diff --git a/packages/client/lib/commands/GEOSEARCH_WITH.ts b/packages/client/lib/commands/GEOSEARCH_WITH.ts index 64c6d88e33c..d7a5f456a94 100644 --- a/packages/client/lib/commands/GEOSEARCH_WITH.ts +++ b/packages/client/lib/commands/GEOSEARCH_WITH.ts @@ -1,11 +1,11 @@ -import { RedisCommandArguments } from '.'; +import { RedisCommandArgument, RedisCommandArguments } from '.'; import { GeoSearchFrom, GeoSearchBy, GeoReplyWith, GeoSearchOptions } from './generic-transformers'; import { transformArguments as geoSearchTransformArguments } from './GEOSEARCH'; export { FIRST_KEY_INDEX, IS_READ_ONLY } from './GEOSEARCH'; export function transformArguments( - key: string, + key: RedisCommandArgument, from: GeoSearchFrom, by: GeoSearchBy, replyWith: Array, diff --git a/packages/client/lib/commands/GET.spec.ts b/packages/client/lib/commands/GET.spec.ts index 4c197f99a4e..2946ea19b60 100644 --- a/packages/client/lib/commands/GET.spec.ts +++ b/packages/client/lib/commands/GET.spec.ts @@ -1,4 +1,5 @@ import { strict as assert } from 'assert'; +import RedisClient from '../client'; import testUtils, { GLOBAL } from '../test-utils'; import { transformArguments } from './GET'; @@ -11,6 +12,12 @@ describe('GET', () => { }); testUtils.testWithClient('client.get', async client => { + const a = await client.get( + 'key' + ); + + + assert.equal( await client.get('key'), null diff --git a/packages/client/lib/commands/GET.ts b/packages/client/lib/commands/GET.ts index 0bcecc98b87..127b0a56349 100644 --- a/packages/client/lib/commands/GET.ts +++ b/packages/client/lib/commands/GET.ts @@ -8,4 +8,4 @@ export function transformArguments(key: RedisCommandArgument): RedisCommandArgum return ['GET', key]; } -export declare function transformReply(): string | null; +export declare function transformReply(): RedisCommandArgument | null; diff --git a/packages/client/lib/commands/GETBIT.ts b/packages/client/lib/commands/GETBIT.ts index 1e5fd884251..67f67f39b19 100644 --- a/packages/client/lib/commands/GETBIT.ts +++ b/packages/client/lib/commands/GETBIT.ts @@ -1,10 +1,14 @@ +import { RedisCommandArgument, RedisCommandArguments } from '.'; import { BitValue } from './generic-transformers'; export const FIRST_KEY_INDEX = 1; export const IS_READ_ONLY = true; -export function transformArguments(key: string, offset: number): Array { +export function transformArguments( + key: RedisCommandArgument, + offset: number +): RedisCommandArguments { return ['GETBIT', key, offset.toString()]; } diff --git a/packages/client/lib/commands/GETDEL.ts b/packages/client/lib/commands/GETDEL.ts index de99cc6357b..2d91e6cc025 100644 --- a/packages/client/lib/commands/GETDEL.ts +++ b/packages/client/lib/commands/GETDEL.ts @@ -1,7 +1,9 @@ +import { RedisCommandArgument, RedisCommandArguments } from '.'; + export const FIRST_KEY_INDEX = 1; -export function transformArguments(key: string): Array { +export function transformArguments(key: RedisCommandArgument): RedisCommandArguments { return ['GETDEL', key]; } -export declare function transformReply(): string | null; +export declare function transformReply(): RedisCommandArgument | null; diff --git a/packages/client/lib/commands/GETEX.ts b/packages/client/lib/commands/GETEX.ts index cd4f283eee3..5b3cec6d887 100644 --- a/packages/client/lib/commands/GETEX.ts +++ b/packages/client/lib/commands/GETEX.ts @@ -1,4 +1,4 @@ -import { RedisCommandArguments } from '.'; +import { RedisCommandArgument, RedisCommandArguments } from '.'; import { transformEXAT, transformPXAT } from './generic-transformers'; export const FIRST_KEY_INDEX = 1; @@ -15,7 +15,10 @@ type GetExModes = { PERSIST: true; }; -export function transformArguments(key: string, mode: GetExModes): RedisCommandArguments { +export function transformArguments( + key: RedisCommandArgument, + mode: GetExModes +): RedisCommandArguments { const args = ['GETEX', key]; if ('EX' in mode) { @@ -33,4 +36,4 @@ export function transformArguments(key: string, mode: GetExModes): RedisCommandA return args; } -export declare function transformReply(): string | null; +export declare function transformReply(): RedisCommandArgument | null; diff --git a/packages/client/lib/commands/GETRANGE.ts b/packages/client/lib/commands/GETRANGE.ts index babb0a6a7c2..2d12d937cc6 100644 --- a/packages/client/lib/commands/GETRANGE.ts +++ b/packages/client/lib/commands/GETRANGE.ts @@ -1,9 +1,15 @@ +import { RedisCommandArgument, RedisCommandArguments } from '.'; + export const FIRST_KEY_INDEX = 1; export const IS_READ_ONLY = true; -export function transformArguments(key: string, start: number, end: number): Array { +export function transformArguments( + key: RedisCommandArgument, + start: number, + end: number +): RedisCommandArguments { return ['GETRANGE', key, start.toString(), end.toString()]; } -export declare function transformReply(): string; +export declare function transformReply(): RedisCommandArgument; diff --git a/packages/client/lib/commands/GETSET.ts b/packages/client/lib/commands/GETSET.ts index 4d3516866fb..87d111792c6 100644 --- a/packages/client/lib/commands/GETSET.ts +++ b/packages/client/lib/commands/GETSET.ts @@ -1,7 +1,12 @@ +import { RedisCommandArgument, RedisCommandArguments } from '.'; + export const FIRST_KEY_INDEX = 1; -export function transformArguments(key: string, value: string): Array { +export function transformArguments( + key: RedisCommandArgument, + value: RedisCommandArgument +): RedisCommandArguments { return ['GETSET', key, value]; } -export declare function transformReply(): string | null; +export declare function transformReply(): RedisCommandArgument | null; diff --git a/packages/client/lib/commands/GET_BUFFER.spec.ts b/packages/client/lib/commands/GET_BUFFER.spec.ts deleted file mode 100644 index 1f1a86799f4..00000000000 --- a/packages/client/lib/commands/GET_BUFFER.spec.ts +++ /dev/null @@ -1,22 +0,0 @@ -import { strict as assert } from 'assert'; -import testUtils, { GLOBAL } from '../test-utils'; - -describe('GET_BUFFER', () => { - testUtils.testWithClient('client.getBuffer', async client => { - const buffer = Buffer.from('string'); - await client.set('key', buffer); - assert.deepEqual( - buffer, - await client.getBuffer('key') - ); - }, GLOBAL.SERVERS.OPEN); - - testUtils.testWithCluster('cluster.getBuffer', async cluster => { - const buffer = Buffer.from('string'); - await cluster.set('key', buffer); - assert.deepEqual( - buffer, - await cluster.getBuffer('key') - ); - }, GLOBAL.CLUSTERS.OPEN); -}); diff --git a/packages/client/lib/commands/GET_BUFFER.ts b/packages/client/lib/commands/GET_BUFFER.ts deleted file mode 100644 index 2f08ecb708a..00000000000 --- a/packages/client/lib/commands/GET_BUFFER.ts +++ /dev/null @@ -1,5 +0,0 @@ -export { FIRST_KEY_INDEX, IS_READ_ONLY, transformArguments } from './GET'; - -export const BUFFER_MODE = true; - -export declare function transformReply(): Buffer | null; diff --git a/packages/client/lib/commands/HDEL.ts b/packages/client/lib/commands/HDEL.ts index 58d057ebf10..1a994e109d6 100644 --- a/packages/client/lib/commands/HDEL.ts +++ b/packages/client/lib/commands/HDEL.ts @@ -1,9 +1,12 @@ -import { RedisCommandArguments } from '.'; +import { RedisCommandArgument, RedisCommandArguments } from '.'; import { pushVerdictArguments } from './generic-transformers'; export const FIRST_KEY_INDEX = 1; -export function transformArguments(key: string, field: string | Array): RedisCommandArguments { +export function transformArguments( + key: RedisCommandArgument, + field: RedisCommandArgument | Array +): RedisCommandArguments { return pushVerdictArguments(['HDEL', key], field); } diff --git a/packages/client/lib/commands/HELLO.ts b/packages/client/lib/commands/HELLO.ts index 86dae2a1d71..d943f2e4c35 100644 --- a/packages/client/lib/commands/HELLO.ts +++ b/packages/client/lib/commands/HELLO.ts @@ -1,3 +1,4 @@ +import { RedisCommandArgument, RedisCommandArguments } from '.'; import { AuthOptions } from './AUTH'; interface HelloOptions { @@ -6,8 +7,8 @@ interface HelloOptions { clientName?: string; } -export function transformArguments(options?: HelloOptions): Array { - const args = ['HELLO']; +export function transformArguments(options?: HelloOptions): RedisCommandArguments { + const args: RedisCommandArguments = ['HELLO']; if (options) { args.push(options.protover.toString()); @@ -26,29 +27,29 @@ export function transformArguments(options?: HelloOptions): Array { type HelloRawReply = [ _: never, - server: string, + server: RedisCommandArgument, _: never, - version: string, + version: RedisCommandArgument, _: never, proto: number, _: never, id: number, _: never, - mode: string, + mode: RedisCommandArgument, _: never, - role: string, + role: RedisCommandArgument, _: never, - modules: Array + modules: Array ]; interface HelloTransformedReply { - server: string; - version: string; + server: RedisCommandArgument; + version: RedisCommandArgument; proto: number; id: number; - mode: string; - role: string; - modules: Array; + mode: RedisCommandArgument; + role: RedisCommandArgument; + modules: Array; } export function transformReply(reply: HelloRawReply): HelloTransformedReply { diff --git a/packages/client/lib/commands/HEXISTS.ts b/packages/client/lib/commands/HEXISTS.ts index 0eae5e03e52..289be20aa83 100644 --- a/packages/client/lib/commands/HEXISTS.ts +++ b/packages/client/lib/commands/HEXISTS.ts @@ -1,7 +1,12 @@ +import { RedisCommandArgument, RedisCommandArguments } from '.'; + export const FIRST_KEY_INDEX = 1; -export function transformArguments(key: string, field: string): Array { +export function transformArguments( + key: RedisCommandArgument, + field: RedisCommandArgument +): RedisCommandArguments { return ['HEXISTS', key, field]; } -export { transformReplyBoolean as transformReply } from './generic-transformers'; +export { transformBooleanReply as transformReply } from './generic-transformers'; diff --git a/packages/client/lib/commands/HGET.ts b/packages/client/lib/commands/HGET.ts index 8da48901747..fcfd31e6172 100644 --- a/packages/client/lib/commands/HGET.ts +++ b/packages/client/lib/commands/HGET.ts @@ -11,4 +11,4 @@ export function transformArguments( return ['HGET', key, field]; } -export declare function transformReply(): string | undefined; +export declare function transformReply(): RedisCommandArgument | undefined; diff --git a/packages/client/lib/commands/HGETALL.ts b/packages/client/lib/commands/HGETALL.ts index 56bd76f4ae4..1ea702080b7 100644 --- a/packages/client/lib/commands/HGETALL.ts +++ b/packages/client/lib/commands/HGETALL.ts @@ -1,9 +1,11 @@ +import { RedisCommandArgument, RedisCommandArguments } from '.'; + export const FIRST_KEY_INDEX = 1; export const IS_READ_ONLY = true; -export function transformArguments(key: string): Array { +export function transformArguments(key: RedisCommandArgument): RedisCommandArguments { return ['HGETALL', key]; } -export { transformReplyStringTuples as transformReply } from './generic-transformers'; +export { transformTuplesReply as transformReply } from './generic-transformers'; diff --git a/packages/client/lib/commands/HGETALL_BUFFER.ts b/packages/client/lib/commands/HGETALL_BUFFER.ts deleted file mode 100644 index 61ab171e07b..00000000000 --- a/packages/client/lib/commands/HGETALL_BUFFER.ts +++ /dev/null @@ -1,5 +0,0 @@ -export { FIRST_KEY_INDEX, IS_READ_ONLY, transformArguments } from './HGETALL'; - -export const BUFFER_MODE = true; - -export { transformReplyBufferTuples as transformReply } from './generic-transformers'; diff --git a/packages/client/lib/commands/HGET_BUFFER.ts b/packages/client/lib/commands/HGET_BUFFER.ts deleted file mode 100644 index 07d41f0dc32..00000000000 --- a/packages/client/lib/commands/HGET_BUFFER.ts +++ /dev/null @@ -1,5 +0,0 @@ -export { FIRST_KEY_INDEX, IS_READ_ONLY, transformArguments } from './HGET'; - -export const BUFFER_MODE = true; - -export declare function transformReply(): Buffer | undefined; diff --git a/packages/client/lib/commands/HINCRBY.ts b/packages/client/lib/commands/HINCRBY.ts index 8f0e99d41f4..b2cf6eefe89 100644 --- a/packages/client/lib/commands/HINCRBY.ts +++ b/packages/client/lib/commands/HINCRBY.ts @@ -1,6 +1,12 @@ +import { RedisCommandArgument, RedisCommandArguments } from '.'; + export const FIRST_KEY_INDEX = 1; -export function transformArguments(key: string, field: string, increment: number): Array { +export function transformArguments( + key: RedisCommandArgument, + field: RedisCommandArgument, + increment: number +): RedisCommandArguments { return ['HINCRBY', key, field, increment.toString()]; } diff --git a/packages/client/lib/commands/HINCRBYFLOAT.ts b/packages/client/lib/commands/HINCRBYFLOAT.ts index 262a7d5d6b0..0e2de6e9b29 100644 --- a/packages/client/lib/commands/HINCRBYFLOAT.ts +++ b/packages/client/lib/commands/HINCRBYFLOAT.ts @@ -1,6 +1,12 @@ +import { RedisCommandArgument, RedisCommandArguments } from '.'; + export const FIRST_KEY_INDEX = 1; -export function transformArguments(key: string, field: string, increment: number): Array { +export function transformArguments( + key: RedisCommandArgument, + field: RedisCommandArgument, + increment: number +): RedisCommandArguments { return ['HINCRBYFLOAT', key, field, increment.toString()]; } diff --git a/packages/client/lib/commands/HKEYS.ts b/packages/client/lib/commands/HKEYS.ts index 358f08fc762..3d629733d0e 100644 --- a/packages/client/lib/commands/HKEYS.ts +++ b/packages/client/lib/commands/HKEYS.ts @@ -1,7 +1,9 @@ +import { RedisCommandArgument, RedisCommandArguments } from '.'; + export const FIRST_KEY_INDEX = 1; -export function transformArguments(key: string): Array { +export function transformArguments(key: RedisCommandArgument): RedisCommandArguments { return ['HKEYS', key]; } -export declare function transformReply(): Array; +export declare function transformReply(): Array; diff --git a/packages/client/lib/commands/HLEN.ts b/packages/client/lib/commands/HLEN.ts index 5c717ad7c54..15a93d408d7 100644 --- a/packages/client/lib/commands/HLEN.ts +++ b/packages/client/lib/commands/HLEN.ts @@ -1,6 +1,8 @@ +import { RedisCommandArgument, RedisCommandArguments } from '.'; + export const FIRST_KEY_INDEX = 1; -export function transformArguments(key: string): Array { +export function transformArguments(key: RedisCommandArgument): RedisCommandArguments { return ['HLEN', key]; } diff --git a/packages/client/lib/commands/HMGET.ts b/packages/client/lib/commands/HMGET.ts index 7ca3a55b69c..64b4014abeb 100644 --- a/packages/client/lib/commands/HMGET.ts +++ b/packages/client/lib/commands/HMGET.ts @@ -1,12 +1,15 @@ -import { RedisCommandArguments } from '.'; +import { RedisCommandArgument, RedisCommandArguments } from '.'; import { pushVerdictArguments } from './generic-transformers'; export const FIRST_KEY_INDEX = 1; export const IS_READ_ONLY = true; -export function transformArguments(key: string, fields: string | Array): RedisCommandArguments { +export function transformArguments( + key: RedisCommandArgument, + fields: RedisCommandArgument | Array +): RedisCommandArguments { return pushVerdictArguments(['HMGET', key], fields); } -export declare function transformReply(): Array; +export declare function transformReply(): Array; diff --git a/packages/client/lib/commands/HRANDFIELD.ts b/packages/client/lib/commands/HRANDFIELD.ts index 63f092bf092..a2c70aabd52 100644 --- a/packages/client/lib/commands/HRANDFIELD.ts +++ b/packages/client/lib/commands/HRANDFIELD.ts @@ -1,9 +1,11 @@ +import { RedisCommandArgument, RedisCommandArguments } from '.'; + export const FIRST_KEY_INDEX = 1; export const IS_READ_ONLY = true; -export function transformArguments(key: string): Array { +export function transformArguments(key: RedisCommandArgument): RedisCommandArguments { return ['HRANDFIELD', key]; } -export declare function transformReply(): string | null; +export declare function transformReply(): RedisCommandArgument | null; diff --git a/packages/client/lib/commands/HRANDFIELD_COUNT.ts b/packages/client/lib/commands/HRANDFIELD_COUNT.ts index b97e38ea165..01b8df63273 100644 --- a/packages/client/lib/commands/HRANDFIELD_COUNT.ts +++ b/packages/client/lib/commands/HRANDFIELD_COUNT.ts @@ -1,12 +1,16 @@ +import { RedisCommandArgument, RedisCommandArguments } from '.'; import { transformArguments as transformHRandFieldArguments } from './HRANDFIELD'; export { FIRST_KEY_INDEX, IS_READ_ONLY } from './HRANDFIELD'; -export function transformArguments(key: string, count: number): Array { +export function transformArguments( + key: RedisCommandArgument, + count: number +): RedisCommandArguments { return [ ...transformHRandFieldArguments(key), count.toString() ]; } -export declare function transformReply(): Array; +export declare function transformReply(): Array; diff --git a/packages/client/lib/commands/HRANDFIELD_COUNT_WITHVALUES.ts b/packages/client/lib/commands/HRANDFIELD_COUNT_WITHVALUES.ts index 807e3cc1cf7..3e09dbb9a14 100644 --- a/packages/client/lib/commands/HRANDFIELD_COUNT_WITHVALUES.ts +++ b/packages/client/lib/commands/HRANDFIELD_COUNT_WITHVALUES.ts @@ -1,12 +1,16 @@ +import { RedisCommandArgument, RedisCommandArguments } from '.'; import { transformArguments as transformHRandFieldCountArguments } from './HRANDFIELD_COUNT'; export { FIRST_KEY_INDEX, IS_READ_ONLY } from './HRANDFIELD_COUNT'; -export function transformArguments(key: string, count: number): Array { +export function transformArguments( + key: RedisCommandArgument, + count: number +): RedisCommandArguments { return [ ...transformHRandFieldCountArguments(key, count), 'WITHVALUES' ]; } -export { transformReplyStringTuples as transformReply } from './generic-transformers'; +export { transformTuplesReply as transformReply } from './generic-transformers'; diff --git a/packages/client/lib/commands/HRANDFIELD_COUNT_WITHVALUES_BUFFER.ts b/packages/client/lib/commands/HRANDFIELD_COUNT_WITHVALUES_BUFFER.ts deleted file mode 100644 index 6937f7400b1..00000000000 --- a/packages/client/lib/commands/HRANDFIELD_COUNT_WITHVALUES_BUFFER.ts +++ /dev/null @@ -1,5 +0,0 @@ -export { FIRST_KEY_INDEX, IS_READ_ONLY, transformArguments } from './HRANDFIELD_COUNT'; - -export const BUFFER_MODE = true; - -export { transformReplyBufferTuples as transformReply } from './generic-transformers'; diff --git a/packages/client/lib/commands/HSCAN.ts b/packages/client/lib/commands/HSCAN.ts index 18b1355b594..ba18fb986bc 100644 --- a/packages/client/lib/commands/HSCAN.ts +++ b/packages/client/lib/commands/HSCAN.ts @@ -1,19 +1,26 @@ +import { RedisCommandArgument, RedisCommandArguments } from '.'; import { ScanOptions, pushScanArguments } from './generic-transformers'; export const FIRST_KEY_INDEX = 1; export const IS_READ_ONLY = true; -export function transformArguments(key: string, cursor: number, options?: ScanOptions): Array { +export function transformArguments( + key: RedisCommandArgument, + cursor: number, + options?: ScanOptions +): RedisCommandArguments { return pushScanArguments([ 'HSCAN', key ], cursor, options); } +type HScanRawReply = [RedisCommandArgument, Array]; + export interface HScanTuple { - field: string; - value: string; + field: RedisCommandArgument; + value: RedisCommandArgument; } interface HScanReply { @@ -21,7 +28,7 @@ interface HScanReply { tuples: Array; } -export function transformReply([cursor, rawTuples]: [string, Array]): HScanReply { +export function transformReply([cursor, rawTuples]: HScanRawReply): HScanReply { const parsedTuples = []; for (let i = 0; i < rawTuples.length; i += 2) { parsedTuples.push({ diff --git a/packages/client/lib/commands/HSET.ts b/packages/client/lib/commands/HSET.ts index edb4c76e102..81bde83d220 100644 --- a/packages/client/lib/commands/HSET.ts +++ b/packages/client/lib/commands/HSET.ts @@ -1,6 +1,6 @@ import { RedisCommandArgument, RedisCommandArguments } from '.'; -type Types = string | number | Buffer; +type Types = RedisCommandArgument | number; type HSETObject = Record; diff --git a/packages/client/lib/commands/HSETNX.ts b/packages/client/lib/commands/HSETNX.ts index 83112f8945e..9ac6ef0edd8 100644 --- a/packages/client/lib/commands/HSETNX.ts +++ b/packages/client/lib/commands/HSETNX.ts @@ -1,7 +1,13 @@ +import { RedisCommandArgument, RedisCommandArguments } from '.'; + export const FIRST_KEY_INDEX = 1; -export function transformArguments(key: string, field: string, value: string): Array { +export function transformArguments( + key: RedisCommandArgument, + field: RedisCommandArgument, + value: RedisCommandArgument +): RedisCommandArguments { return ['HSETNX', key, field, value]; } -export { transformReplyBoolean as transformReply } from './generic-transformers'; +export { transformBooleanReply as transformReply } from './generic-transformers'; diff --git a/packages/client/lib/commands/HSTRLEN.ts b/packages/client/lib/commands/HSTRLEN.ts index d0138eb3ec9..a820e6c5643 100644 --- a/packages/client/lib/commands/HSTRLEN.ts +++ b/packages/client/lib/commands/HSTRLEN.ts @@ -1,6 +1,11 @@ +import { RedisCommandArgument, RedisCommandArguments } from '.'; + export const FIRST_KEY_INDEX = 1; -export function transformArguments(key: string, field: string): Array { +export function transformArguments( + key: RedisCommandArgument, + field: RedisCommandArgument +): RedisCommandArguments { return ['HSTRLEN', key, field]; } diff --git a/packages/client/lib/commands/HVALS.ts b/packages/client/lib/commands/HVALS.ts index cb17fdb29be..ef63fdc7f8a 100644 --- a/packages/client/lib/commands/HVALS.ts +++ b/packages/client/lib/commands/HVALS.ts @@ -1,7 +1,9 @@ +import { RedisCommandArgument, RedisCommandArguments } from '.'; + export const FIRST_KEY_INDEX = 1; -export function transformArguments(key: string): Array { +export function transformArguments(key: RedisCommandArgument): RedisCommandArguments { return ['HVALS', key]; } -export declare function transformReply(): Array; +export declare function transformReply(): Array; diff --git a/packages/client/lib/commands/INCR.ts b/packages/client/lib/commands/INCR.ts index f7b81013258..2f9a9adfe2b 100644 --- a/packages/client/lib/commands/INCR.ts +++ b/packages/client/lib/commands/INCR.ts @@ -1,6 +1,8 @@ +import { RedisCommandArgument, RedisCommandArguments } from '.'; + export const FIRST_KEY_INDEX = 1; -export function transformArguments(key: string): Array { +export function transformArguments(key: RedisCommandArgument): RedisCommandArguments { return ['INCR', key]; } diff --git a/packages/client/lib/commands/INCRBY.ts b/packages/client/lib/commands/INCRBY.ts index 8f2a4406bf9..75c61156d6b 100644 --- a/packages/client/lib/commands/INCRBY.ts +++ b/packages/client/lib/commands/INCRBY.ts @@ -1,6 +1,11 @@ +import { RedisCommandArgument, RedisCommandArguments } from '.'; + export const FIRST_KEY_INDEX = 1; -export function transformArguments(key: string, increment: number): Array { +export function transformArguments( + key: RedisCommandArgument, + increment: number +): RedisCommandArguments { return ['INCRBY', key, increment.toString()]; } diff --git a/packages/client/lib/commands/INCRBYFLOAT.ts b/packages/client/lib/commands/INCRBYFLOAT.ts index a5f99820cb3..ace3702339e 100644 --- a/packages/client/lib/commands/INCRBYFLOAT.ts +++ b/packages/client/lib/commands/INCRBYFLOAT.ts @@ -1,7 +1,12 @@ +import { RedisCommandArgument, RedisCommandArguments } from '.'; + export const FIRST_KEY_INDEX = 1; -export function transformArguments(key: string, increment: number): Array { +export function transformArguments( + key: RedisCommandArgument, + increment: number +): RedisCommandArguments { return ['INCRBYFLOAT', key, increment.toString()]; } -export declare function transformReply(): string; +export declare function transformReply(): RedisCommandArgument; diff --git a/packages/client/lib/commands/KEYS.ts b/packages/client/lib/commands/KEYS.ts index 99c99c11521..c96ee001436 100644 --- a/packages/client/lib/commands/KEYS.ts +++ b/packages/client/lib/commands/KEYS.ts @@ -1,7 +1,7 @@ -export function transformArguments(pattern: string): Array { +import { RedisCommandArgument, RedisCommandArguments } from '.'; + +export function transformArguments(pattern: RedisCommandArgument): RedisCommandArguments { return ['KEYS', pattern]; } -export function transformReply(keys: Array): Array { - return keys; -} \ No newline at end of file +export declare function transformReply(): Array; diff --git a/packages/client/lib/commands/LINDEX.ts b/packages/client/lib/commands/LINDEX.ts index d13bc0c2d02..bb657de3cb8 100644 --- a/packages/client/lib/commands/LINDEX.ts +++ b/packages/client/lib/commands/LINDEX.ts @@ -1,8 +1,12 @@ +import { RedisCommandArgument, RedisCommandArguments } from '.'; export const IS_READ_ONLY = true; -export function transformArguments(key: string, index: number): Array { +export function transformArguments( + key: RedisCommandArgument, + index: number +): RedisCommandArguments { return ['LINDEX', key, index.toString()]; } -export declare function transformReply(): string | null; \ No newline at end of file +export declare function transformReply(): RedisCommandArgument | null; diff --git a/packages/client/lib/commands/LINSERT.ts b/packages/client/lib/commands/LINSERT.ts index b1d377f92aa..0a8e1f32ba4 100644 --- a/packages/client/lib/commands/LINSERT.ts +++ b/packages/client/lib/commands/LINSERT.ts @@ -1,13 +1,15 @@ +import { RedisCommandArgument, RedisCommandArguments } from '.'; + export const FIRST_KEY_INDEX = 1; type LInsertPosition = 'BEFORE' | 'AFTER'; export function transformArguments( - key: string, + key: RedisCommandArgument, position: LInsertPosition, - pivot: string, - element: string -): Array { + pivot: RedisCommandArgument, + element: RedisCommandArgument +): RedisCommandArguments { return [ 'LINSERT', key, diff --git a/packages/client/lib/commands/LLEN.ts b/packages/client/lib/commands/LLEN.ts index 49ac1d1916c..3410e57d424 100644 --- a/packages/client/lib/commands/LLEN.ts +++ b/packages/client/lib/commands/LLEN.ts @@ -1,8 +1,10 @@ +import { RedisCommandArgument, RedisCommandArguments } from '.'; + export const FIRST_KEY_INDEX = 1; export const IS_READ_ONLY = true; -export function transformArguments(key: string): Array { +export function transformArguments(key: RedisCommandArgument): RedisCommandArguments { return ['LLEN', key]; } diff --git a/packages/client/lib/commands/LMOVE.ts b/packages/client/lib/commands/LMOVE.ts index 111e758a0a4..96946722ef5 100644 --- a/packages/client/lib/commands/LMOVE.ts +++ b/packages/client/lib/commands/LMOVE.ts @@ -1,13 +1,15 @@ +import { RedisCommandArgument, RedisCommandArguments } from '.'; + export type LMoveSide = 'LEFT' | 'RIGHT'; export const FIRST_KEY_INDEX = 1; export function transformArguments( - source: string, - destination: string, + source: RedisCommandArgument, + destination: RedisCommandArgument, sourceSide: LMoveSide, destinationSide: LMoveSide -): Array { +): RedisCommandArguments { return [ 'LMOVE', source, @@ -17,4 +19,4 @@ export function transformArguments( ]; } -export declare function transformReply(): string | null; +export declare function transformReply(): RedisCommandArgument | null; diff --git a/packages/client/lib/commands/LOLWUT.ts b/packages/client/lib/commands/LOLWUT.ts index cfe01adbcf2..5d5fc726065 100644 --- a/packages/client/lib/commands/LOLWUT.ts +++ b/packages/client/lib/commands/LOLWUT.ts @@ -1,3 +1,5 @@ +import { RedisCommandArgument } from '.'; + export const IS_READ_ONLY = true; export function transformArguments(version?: number, ...optionalArguments: Array): Array { @@ -14,4 +16,4 @@ export function transformArguments(version?: number, ...optionalArguments: Array return args; } -export declare function transformReply(): string; +export declare function transformReply(): RedisCommandArgument; diff --git a/packages/client/lib/commands/LPOP.ts b/packages/client/lib/commands/LPOP.ts index 9bf340e07c9..5dd1bea5196 100644 --- a/packages/client/lib/commands/LPOP.ts +++ b/packages/client/lib/commands/LPOP.ts @@ -1,7 +1,9 @@ +import { RedisCommandArgument, RedisCommandArguments } from '.'; + export const FIRST_KEY_INDEX = 1; -export function transformArguments(key: string): Array { +export function transformArguments(key: RedisCommandArgument): RedisCommandArguments { return ['LPOP', key]; } -export declare function transformReply(): string | null; +export declare function transformReply(): RedisCommandArgument | null; diff --git a/packages/client/lib/commands/LPOP_COUNT.ts b/packages/client/lib/commands/LPOP_COUNT.ts index 828b0251ff6..021517b018a 100644 --- a/packages/client/lib/commands/LPOP_COUNT.ts +++ b/packages/client/lib/commands/LPOP_COUNT.ts @@ -1,7 +1,12 @@ +import { RedisCommandArgument, RedisCommandArguments } from '.'; + export const FIRST_KEY_INDEX = 1; -export function transformArguments(key: string, count: number): Array { +export function transformArguments( + key: RedisCommandArgument, + count: number +): RedisCommandArguments { return ['LPOP', key, count.toString()]; } -export declare function transformReply(): Array | null; +export declare function transformReply(): Array | null; diff --git a/packages/client/lib/commands/LPOS.ts b/packages/client/lib/commands/LPOS.ts index 3371f01d67e..1f2e34ab88e 100644 --- a/packages/client/lib/commands/LPOS.ts +++ b/packages/client/lib/commands/LPOS.ts @@ -1,3 +1,5 @@ +import { RedisCommandArgument, RedisCommandArguments } from '.'; + export const FIRST_KEY_INDEX = 1; export const IS_READ_ONLY = true; @@ -7,7 +9,11 @@ export interface LPosOptions { MAXLEN?: number; } -export function transformArguments(key: string, element: string, options?: LPosOptions): Array { +export function transformArguments( + key: RedisCommandArgument, + element: RedisCommandArgument, + options?: LPosOptions +): RedisCommandArguments { const args = ['LPOS', key, element]; if (typeof options?.RANK === 'number') { diff --git a/packages/client/lib/commands/LPOS_COUNT.ts b/packages/client/lib/commands/LPOS_COUNT.ts index b5a60317eae..0549df82db5 100644 --- a/packages/client/lib/commands/LPOS_COUNT.ts +++ b/packages/client/lib/commands/LPOS_COUNT.ts @@ -1,8 +1,14 @@ +import { RedisCommandArgument, RedisCommandArguments } from '.'; import { LPosOptions } from './LPOS'; export { FIRST_KEY_INDEX, IS_READ_ONLY } from './LPOS'; -export function transformArguments(key: string, element: string, count: number, options?: LPosOptions): Array { +export function transformArguments( + key: RedisCommandArgument, + element: RedisCommandArgument, + count: number, + options?: LPosOptions +): RedisCommandArguments { const args = ['LPOS', key, element]; if (typeof options?.RANK === 'number') { diff --git a/packages/client/lib/commands/LPUSH.ts b/packages/client/lib/commands/LPUSH.ts index b9644344772..7144b146e27 100644 --- a/packages/client/lib/commands/LPUSH.ts +++ b/packages/client/lib/commands/LPUSH.ts @@ -1,9 +1,12 @@ -import { RedisCommandArguments } from '.'; +import { RedisCommandArgument, RedisCommandArguments } from '.'; import { pushVerdictArguments } from './generic-transformers'; export const FIRST_KEY_INDEX = 1; -export function transformArguments(key: string, elements: string | Array): RedisCommandArguments { +export function transformArguments( + key: RedisCommandArgument, + elements: RedisCommandArgument | Array +): RedisCommandArguments { return pushVerdictArguments(['LPUSH', key], elements);} export declare function transformReply(): number; diff --git a/packages/client/lib/commands/LPUSHX.ts b/packages/client/lib/commands/LPUSHX.ts index 5f92d84d3b0..0b518add6da 100644 --- a/packages/client/lib/commands/LPUSHX.ts +++ b/packages/client/lib/commands/LPUSHX.ts @@ -1,9 +1,12 @@ -import { RedisCommandArguments } from '.'; +import { RedisCommandArgument, RedisCommandArguments } from '.'; import { pushVerdictArguments } from './generic-transformers'; export const FIRST_KEY_INDEX = 1; -export function transformArguments(key: string, element: string | Array): RedisCommandArguments { +export function transformArguments( + key: RedisCommandArgument, + element: RedisCommandArgument | Array +): RedisCommandArguments { return pushVerdictArguments(['LPUSHX', key], element); } diff --git a/packages/client/lib/commands/LRANGE.ts b/packages/client/lib/commands/LRANGE.ts index 7ea97ec4f43..df12c57d804 100644 --- a/packages/client/lib/commands/LRANGE.ts +++ b/packages/client/lib/commands/LRANGE.ts @@ -1,8 +1,14 @@ +import { RedisCommandArgument, RedisCommandArguments } from '.'; + export const FIRST_KEY_INDEX = 1; export const IS_READ_ONLY = true; -export function transformArguments(key: string, start: number, stop: number): Array { +export function transformArguments( + key: RedisCommandArgument, + start: number, + stop: number +): RedisCommandArguments { return [ 'LRANGE', key, @@ -11,4 +17,4 @@ export function transformArguments(key: string, start: number, stop: number): Ar ]; } -export declare function transformReply(): Array; +export declare function transformReply(): Array; diff --git a/packages/client/lib/commands/LREM.ts b/packages/client/lib/commands/LREM.ts index b43f8a28427..b4951334888 100644 --- a/packages/client/lib/commands/LREM.ts +++ b/packages/client/lib/commands/LREM.ts @@ -1,6 +1,12 @@ +import { RedisCommandArgument, RedisCommandArguments } from '.'; + export const FIRST_KEY_INDEX = 1; -export function transformArguments(key: string, count: number, element: string): Array { +export function transformArguments( + key: RedisCommandArgument, + count: number, + element: RedisCommandArgument +): RedisCommandArguments { return [ 'LREM', key, diff --git a/packages/client/lib/commands/LSET.ts b/packages/client/lib/commands/LSET.ts index 1511fb9751e..33c7b4cc060 100644 --- a/packages/client/lib/commands/LSET.ts +++ b/packages/client/lib/commands/LSET.ts @@ -1,6 +1,12 @@ +import { RedisCommandArgument, RedisCommandArguments } from '.'; + export const FIRST_KEY_INDEX = 1; -export function transformArguments(key: string, index: number, element: string): Array { +export function transformArguments( + key: RedisCommandArgument, + index: number, + element: RedisCommandArgument +): RedisCommandArguments { return [ 'LSET', key, @@ -9,4 +15,4 @@ export function transformArguments(key: string, index: number, element: string): ]; } -export declare function transformReply(): string; +export declare function transformReply(): RedisCommandArgument; diff --git a/packages/client/lib/commands/LTRIM.ts b/packages/client/lib/commands/LTRIM.ts index ab78e5305aa..668497cdde6 100644 --- a/packages/client/lib/commands/LTRIM.ts +++ b/packages/client/lib/commands/LTRIM.ts @@ -1,6 +1,12 @@ +import { RedisCommandArgument, RedisCommandArguments } from '.'; + export const FIRST_KEY_INDEX = 1; -export function transformArguments(key: string, start: number, stop: number): Array { +export function transformArguments( + key: RedisCommandArgument, + start: number, + stop: number +): RedisCommandArguments { return [ 'LTRIM', key, @@ -9,4 +15,4 @@ export function transformArguments(key: string, start: number, stop: number): Ar ]; } -export declare function transformReply(): string; +export declare function transformReply(): RedisCommandArgument; diff --git a/packages/client/lib/commands/MGET.ts b/packages/client/lib/commands/MGET.ts index 6d5b9073d49..6635a2ca20c 100644 --- a/packages/client/lib/commands/MGET.ts +++ b/packages/client/lib/commands/MGET.ts @@ -1,9 +1,13 @@ +import { RedisCommandArgument, RedisCommandArguments } from '.'; + export const FIRST_KEY_INDEX = 1; export const IS_READ_ONLY = true; -export function transformArguments(keys: Array): Array { +export function transformArguments( + keys: Array +): RedisCommandArguments { return ['MGET', ...keys]; } -export declare function transformReply(): Array; +export declare function transformReply(): Array; diff --git a/packages/client/lib/commands/MIGRATE.ts b/packages/client/lib/commands/MIGRATE.ts index 4d2795123a8..d5e5977df8c 100644 --- a/packages/client/lib/commands/MIGRATE.ts +++ b/packages/client/lib/commands/MIGRATE.ts @@ -1,3 +1,4 @@ +import { RedisCommandArgument, RedisCommandArguments } from '.'; import { AuthOptions } from './AUTH'; interface MigrateOptions { @@ -7,20 +8,20 @@ interface MigrateOptions { } export function transformArguments( - host: string, + host: RedisCommandArgument, port: number, - key: string | Array, + key: RedisCommandArgument | Array, destinationDb: number, timeout: number, options?: MigrateOptions -): Array { +): RedisCommandArguments { const args = ['MIGRATE', host, port.toString()], - isKeyString = typeof key === 'string'; + isKeyArray = Array.isArray(key); - if (isKeyString) { - args.push(key); - } else { + if (isKeyArray) { args.push('""'); + } else { + args.push(key); } args.push( @@ -51,7 +52,7 @@ export function transformArguments( } } - if (!isKeyString) { + if (isKeyArray) { args.push( 'KEYS', ...key diff --git a/packages/client/lib/commands/MOVE.ts b/packages/client/lib/commands/MOVE.ts index 93896a63054..f446fd18dce 100644 --- a/packages/client/lib/commands/MOVE.ts +++ b/packages/client/lib/commands/MOVE.ts @@ -2,4 +2,4 @@ export function transformArguments(key: string, db: number): Array { return ['MOVE', key, db.toString()]; } -export { transformReplyBoolean as transformReply } from './generic-transformers'; +export { transformBooleanReply as transformReply } from './generic-transformers'; diff --git a/packages/client/lib/commands/MSET.ts b/packages/client/lib/commands/MSET.ts index d3e290df70f..bd7111659d1 100644 --- a/packages/client/lib/commands/MSET.ts +++ b/packages/client/lib/commands/MSET.ts @@ -1,7 +1,14 @@ +import { RedisCommandArgument, RedisCommandArguments } from '.'; + export const FIRST_KEY_INDEX = 1; -export function transformArguments(toSet: Array<[string, string]> | Array | Record): Array { - const args = ['MSET']; +export type MSetArguments = + Array<[RedisCommandArgument, RedisCommandArgument]> | + Array | + Record; + +export function transformArguments(toSet: MSetArguments): RedisCommandArguments { + const args: RedisCommandArguments = ['MSET']; if (Array.isArray(toSet)) { args.push(...toSet.flat()); @@ -14,4 +21,4 @@ export function transformArguments(toSet: Array<[string, string]> | Array | Array | Record): Array { - const args = ['MSETNX']; +export function transformArguments(toSet: MSetArguments): RedisCommandArguments { + const args: RedisCommandArguments = ['MSETNX']; if (Array.isArray(toSet)) { args.push(...toSet.flat()); @@ -14,4 +17,4 @@ export function transformArguments(toSet: Array<[string, string]> | Array { +export function transformArguments(key: RedisCommandArgument): RedisCommandArguments { return ['PERSIST', key]; } -export { transformReplyBoolean as transformReply } from './generic-transformers'; +export { transformBooleanReply as transformReply } from './generic-transformers'; diff --git a/packages/client/lib/commands/PEXPIRE.ts b/packages/client/lib/commands/PEXPIRE.ts index f3b70279aa7..428bfd9d261 100644 --- a/packages/client/lib/commands/PEXPIRE.ts +++ b/packages/client/lib/commands/PEXPIRE.ts @@ -1,7 +1,12 @@ +import { RedisCommandArgument, RedisCommandArguments } from '.'; + export const FIRST_KEY_INDEX = 1; -export function transformArguments(key: string, milliseconds: number): Array { +export function transformArguments( + key: RedisCommandArgument, + milliseconds: number +): RedisCommandArguments { return ['PEXPIRE', key, milliseconds.toString()]; } -export { transformReplyBoolean as transformReply } from './generic-transformers'; +export { transformBooleanReply as transformReply } from './generic-transformers'; diff --git a/packages/client/lib/commands/PEXPIREAT.ts b/packages/client/lib/commands/PEXPIREAT.ts index 5dded48d278..49872877f6e 100644 --- a/packages/client/lib/commands/PEXPIREAT.ts +++ b/packages/client/lib/commands/PEXPIREAT.ts @@ -1,8 +1,12 @@ +import { RedisCommandArgument, RedisCommandArguments } from '.'; import { transformPXAT } from './generic-transformers'; export const FIRST_KEY_INDEX = 1; -export function transformArguments(key: string, millisecondsTimestamp: number | Date): Array { +export function transformArguments( + key: RedisCommandArgument, + millisecondsTimestamp: number | Date +): RedisCommandArguments { return [ 'PEXPIREAT', key, @@ -10,4 +14,4 @@ export function transformArguments(key: string, millisecondsTimestamp: number | ]; } -export { transformReplyBoolean as transformReply } from './generic-transformers'; +export { transformBooleanReply as transformReply } from './generic-transformers'; diff --git a/packages/client/lib/commands/PFADD.ts b/packages/client/lib/commands/PFADD.ts index e45e83c3ae0..8c8985de890 100644 --- a/packages/client/lib/commands/PFADD.ts +++ b/packages/client/lib/commands/PFADD.ts @@ -1,10 +1,13 @@ -import { RedisCommandArguments } from '.'; +import { RedisCommandArgument, RedisCommandArguments } from '.'; import { pushVerdictArguments } from './generic-transformers'; export const FIRST_KEY_INDEX = 1; -export function transformArguments(key: string, element: string | Array): RedisCommandArguments { +export function transformArguments( + key: RedisCommandArgument, + element: RedisCommandArgument | Array +): RedisCommandArguments { return pushVerdictArguments(['PFADD', key], element); } -export { transformReplyBoolean as transformReply } from './generic-transformers'; +export { transformBooleanReply as transformReply } from './generic-transformers'; diff --git a/packages/client/lib/commands/PFCOUNT.ts b/packages/client/lib/commands/PFCOUNT.ts index ec6c0906041..a4cf2dbcb26 100644 --- a/packages/client/lib/commands/PFCOUNT.ts +++ b/packages/client/lib/commands/PFCOUNT.ts @@ -1,9 +1,11 @@ -import { RedisCommandArguments } from '.'; +import { RedisCommandArgument, RedisCommandArguments } from '.'; import { pushVerdictArguments } from './generic-transformers'; export const FIRST_KEY_INDEX = 1; -export function transformArguments(key: string | Array): RedisCommandArguments { +export function transformArguments( + key: RedisCommandArgument | Array +): RedisCommandArguments { return pushVerdictArguments(['PFCOUNT'], key); } diff --git a/packages/client/lib/commands/PING.spec.ts b/packages/client/lib/commands/PING.spec.ts index 85b48fec6b5..fae349176d7 100644 --- a/packages/client/lib/commands/PING.spec.ts +++ b/packages/client/lib/commands/PING.spec.ts @@ -1,11 +1,21 @@ import { strict as assert } from 'assert'; import testUtils, { GLOBAL } from '../test-utils'; +import RedisClient from '../client'; describe('PING', () => { - testUtils.testWithClient('client.ping', async client => { - assert.equal( - await client.ping(), - 'PONG' - ); - }, GLOBAL.SERVERS.OPEN); + describe('client.ping', () => { + testUtils.testWithClient('string', async client => { + assert.equal( + await client.ping(), + 'PONG' + ); + }, GLOBAL.SERVERS.OPEN); + + testUtils.testWithClient('buffer', async client => { + assert.deepEqual( + await client.ping(RedisClient.commandOptions({ returnBuffers: true })), + Buffer.from('PONG') + ); + }, GLOBAL.SERVERS.OPEN); + }); }); diff --git a/packages/client/lib/commands/PING.ts b/packages/client/lib/commands/PING.ts index 1e9aa957bf3..10ab01f7bdf 100644 --- a/packages/client/lib/commands/PING.ts +++ b/packages/client/lib/commands/PING.ts @@ -1,5 +1,7 @@ +import { RedisCommandArgument } from '.'; + export function transformArguments(): Array { return ['PING']; } -export declare function transformReply(): string; +export declare function transformReply(): RedisCommandArgument; diff --git a/packages/client/lib/commands/PSETEX.spec.ts b/packages/client/lib/commands/PSETEX.spec.ts index 61a6e682b08..f6262ed8709 100644 --- a/packages/client/lib/commands/PSETEX.spec.ts +++ b/packages/client/lib/commands/PSETEX.spec.ts @@ -11,6 +11,7 @@ describe('PSETEX', () => { }); testUtils.testWithClient('client.pSetEx', async client => { + const a = await client.pSetEx('key', 1, 'value'); assert.equal( await client.pSetEx('key', 1, 'value'), 'OK' diff --git a/packages/client/lib/commands/PSETEX.ts b/packages/client/lib/commands/PSETEX.ts index a0bd4f5c229..f2739b6e274 100644 --- a/packages/client/lib/commands/PSETEX.ts +++ b/packages/client/lib/commands/PSETEX.ts @@ -1,6 +1,12 @@ +import { RedisCommandArgument, RedisCommandArguments } from '.'; + export const FIRST_KEY_INDEX = 1; -export function transformArguments(key: string, milliseconds: number, value: string): Array { +export function transformArguments( + key: RedisCommandArgument, + milliseconds: number, + value: RedisCommandArgument +): RedisCommandArguments { return [ 'PSETEX', key, @@ -9,4 +15,4 @@ export function transformArguments(key: string, milliseconds: number, value: str ]; } -export declare function transformReply(): string; +export declare function transformReply(): RedisCommandArgument; diff --git a/packages/client/lib/commands/PTTL.ts b/packages/client/lib/commands/PTTL.ts index c1bc18323d5..a2975623f7a 100644 --- a/packages/client/lib/commands/PTTL.ts +++ b/packages/client/lib/commands/PTTL.ts @@ -1,8 +1,10 @@ +import { RedisCommandArgument, RedisCommandArguments } from '.'; + export const FIRST_KEY_INDEX = 1; export const IS_READ_ONLY = true; -export function transformArguments(key: string): Array { +export function transformArguments(key: RedisCommandArgument): RedisCommandArguments { return ['PTTL', key]; } diff --git a/packages/client/lib/commands/RANDOMKEY.ts b/packages/client/lib/commands/RANDOMKEY.ts index fad0b073c71..f2d511d4dec 100644 --- a/packages/client/lib/commands/RANDOMKEY.ts +++ b/packages/client/lib/commands/RANDOMKEY.ts @@ -1,9 +1,9 @@ +import { RedisCommandArgument, RedisCommandArguments } from '.'; + export const IS_READ_ONLY = true; -export function transformArguments(): Array { +export function transformArguments(): RedisCommandArguments { return ['RANDOMKEY']; } -export function transformReply(reply: string | null): string | null { - return reply; -} +export declare function transformReply(): RedisCommandArgument | null; diff --git a/packages/client/lib/commands/RENAME.ts b/packages/client/lib/commands/RENAME.ts index f2affada60b..2d1134084fb 100644 --- a/packages/client/lib/commands/RENAME.ts +++ b/packages/client/lib/commands/RENAME.ts @@ -1,7 +1,12 @@ +import { RedisCommandArgument, RedisCommandArguments } from '.'; + export const FIRST_KEY_INDEX = 1; -export function transformArguments(key: string, newKey: string): Array { +export function transformArguments( + key: RedisCommandArgument, + newKey: RedisCommandArgument +): RedisCommandArguments { return ['RENAME', key, newKey]; } -export declare function transformReply(): string; +export declare function transformReply(): RedisCommandArgument; diff --git a/packages/client/lib/commands/RENAMENX.ts b/packages/client/lib/commands/RENAMENX.ts index 2cfec007741..322ff0a88cc 100644 --- a/packages/client/lib/commands/RENAMENX.ts +++ b/packages/client/lib/commands/RENAMENX.ts @@ -1,7 +1,12 @@ +import { RedisCommandArgument, RedisCommandArguments } from '.'; + export const FIRST_KEY_INDEX = 1; -export function transformArguments(key: string, newKey: string): Array { +export function transformArguments( + key: RedisCommandArgument, + newKey: RedisCommandArgument +): RedisCommandArguments { return ['RENAMENX', key, newKey]; } -export { transformReplyBoolean as transformReply } from './generic-transformers'; +export { transformBooleanReply as transformReply } from './generic-transformers'; diff --git a/packages/client/lib/commands/RPOP.ts b/packages/client/lib/commands/RPOP.ts index 96735dea8b9..ed696b6d522 100644 --- a/packages/client/lib/commands/RPOP.ts +++ b/packages/client/lib/commands/RPOP.ts @@ -1,7 +1,9 @@ +import { RedisCommandArgument, RedisCommandArguments } from '.'; + export const FIRST_KEY_INDEX = 1; -export function transformArguments(key: string): Array { +export function transformArguments(key: RedisCommandArgument): RedisCommandArguments { return ['RPOP', key]; } -export declare function transformReply(): string | null; +export declare function transformReply(): RedisCommandArgument | null; diff --git a/packages/client/lib/commands/RPOPLPUSH.ts b/packages/client/lib/commands/RPOPLPUSH.ts index 23f1ff08766..d388b55ac0a 100644 --- a/packages/client/lib/commands/RPOPLPUSH.ts +++ b/packages/client/lib/commands/RPOPLPUSH.ts @@ -1,6 +1,11 @@ +import { RedisCommandArgument, RedisCommandArguments } from '.'; + export const FIRST_KEY_INDEX = 1; -export function transformArguments(source: string, destination: string): Array { +export function transformArguments( + source: RedisCommandArgument, + destination: RedisCommandArgument +): RedisCommandArguments { return ['RPOPLPUSH', source, destination]; } diff --git a/packages/client/lib/commands/RPOP_COUNT.ts b/packages/client/lib/commands/RPOP_COUNT.ts index f7f3463a3ee..b3bc778ee5c 100644 --- a/packages/client/lib/commands/RPOP_COUNT.ts +++ b/packages/client/lib/commands/RPOP_COUNT.ts @@ -1,7 +1,12 @@ +import { RedisCommandArgument, RedisCommandArguments } from '.'; + export const FIRST_KEY_INDEX = 1; -export function transformArguments(key: string, count: number): Array { +export function transformArguments( + key: RedisCommandArgument, + count: number +): RedisCommandArguments { return ['RPOP', key, count.toString()]; } -export declare function transformReply(): Array | null; +export declare function transformReply(): Array | null; diff --git a/packages/client/lib/commands/RPUSH.ts b/packages/client/lib/commands/RPUSH.ts index 575177755cc..15e282f0892 100644 --- a/packages/client/lib/commands/RPUSH.ts +++ b/packages/client/lib/commands/RPUSH.ts @@ -1,9 +1,12 @@ -import { RedisCommandArguments } from '.'; +import { RedisCommandArgument, RedisCommandArguments } from '.'; import { pushVerdictArguments } from './generic-transformers'; export const FIRST_KEY_INDEX = 1; -export function transformArguments(key: string, element: string | Array): RedisCommandArguments { +export function transformArguments( + key: RedisCommandArgument, + element: RedisCommandArgument | Array +): RedisCommandArguments { return pushVerdictArguments(['RPUSH', key], element); } diff --git a/packages/client/lib/commands/RPUSHX.ts b/packages/client/lib/commands/RPUSHX.ts index bacc60d404b..29253cd6edb 100644 --- a/packages/client/lib/commands/RPUSHX.ts +++ b/packages/client/lib/commands/RPUSHX.ts @@ -1,9 +1,12 @@ -import { RedisCommandArguments } from '.'; +import { RedisCommandArgument, RedisCommandArguments } from '.'; import { pushVerdictArguments } from './generic-transformers'; export const FIRST_KEY_INDEX = 1; -export function transformArguments(key: string, element: string | Array): RedisCommandArguments { +export function transformArguments( + key: RedisCommandArgument, + element: RedisCommandArgument | Array +): RedisCommandArguments { return pushVerdictArguments(['RPUSHX', key], element); } diff --git a/packages/client/lib/commands/SADD.ts b/packages/client/lib/commands/SADD.ts index 05e5a6858f7..7d7121e5391 100644 --- a/packages/client/lib/commands/SADD.ts +++ b/packages/client/lib/commands/SADD.ts @@ -1,9 +1,12 @@ -import { RedisCommandArguments } from '.'; +import { RedisCommandArgument, RedisCommandArguments } from '.'; import { pushVerdictArguments } from './generic-transformers'; export const FIRST_KEY_INDEX = 1; -export function transformArguments(key: string, members: string | Array): RedisCommandArguments { +export function transformArguments( + key: RedisCommandArgument, + members: RedisCommandArgument | Array +): RedisCommandArguments { return pushVerdictArguments(['SADD', key], members); } diff --git a/packages/client/lib/commands/SAVE.ts b/packages/client/lib/commands/SAVE.ts index e88575f0a5c..3d75c29df90 100644 --- a/packages/client/lib/commands/SAVE.ts +++ b/packages/client/lib/commands/SAVE.ts @@ -1,5 +1,7 @@ +import { RedisCommandArgument } from '.'; + export function transformArguments(): Array { return ['SAVE']; } -export declare function transformReply(): string; +export declare function transformReply(): RedisCommandArgument; diff --git a/packages/client/lib/commands/SCAN.ts b/packages/client/lib/commands/SCAN.ts index e3541ea9a74..ee5908eb9bd 100644 --- a/packages/client/lib/commands/SCAN.ts +++ b/packages/client/lib/commands/SCAN.ts @@ -1,11 +1,15 @@ +import { RedisCommandArgument, RedisCommandArguments } from '.'; import { ScanOptions, pushScanArguments } from './generic-transformers'; export const IS_READ_ONLY = true; export interface ScanCommandOptions extends ScanOptions { - TYPE?: string; + TYPE?: RedisCommandArgument; } -export function transformArguments(cursor: number, options?: ScanCommandOptions): Array { +export function transformArguments( + cursor: number, + options?: ScanCommandOptions +): RedisCommandArguments { const args = pushScanArguments(['SCAN'], cursor, options); if (options?.TYPE) { @@ -15,12 +19,14 @@ export function transformArguments(cursor: number, options?: ScanCommandOptions) return args; } +type ScanRawReply = [string, Array]; + export interface ScanReply { cursor: number; - keys: Array; + keys: Array; } -export function transformReply([cursor, keys]: [string, Array]): ScanReply { +export function transformReply([cursor, keys]: ScanRawReply): ScanReply { return { cursor: Number(cursor), keys diff --git a/packages/client/lib/commands/SCRIPT_EXISTS.ts b/packages/client/lib/commands/SCRIPT_EXISTS.ts index d4f65cfd72b..cee889215d3 100644 --- a/packages/client/lib/commands/SCRIPT_EXISTS.ts +++ b/packages/client/lib/commands/SCRIPT_EXISTS.ts @@ -5,4 +5,4 @@ export function transformArguments(sha1: string | Array): RedisCommandAr return pushVerdictArguments(['SCRIPT', 'EXISTS'], sha1); } -export { transformReplyBooleanArray as transformReply } from './generic-transformers'; +export { transformBooleanArrayReply as transformReply } from './generic-transformers'; diff --git a/packages/client/lib/commands/SDIFF.ts b/packages/client/lib/commands/SDIFF.ts index 7c1e4fc34a9..9c4f3b4820b 100644 --- a/packages/client/lib/commands/SDIFF.ts +++ b/packages/client/lib/commands/SDIFF.ts @@ -1,10 +1,14 @@ -import { RedisCommandArguments } from '.'; +import { RedisCommandArgument, RedisCommandArguments } from '.'; import { pushVerdictArguments } from './generic-transformers'; export const FIRST_KEY_INDEX = 1; -export function transformArguments(keys: string | Array): RedisCommandArguments { +export const IS_READ_ONLY = true; + +export function transformArguments( + keys: RedisCommandArgument | Array +): RedisCommandArguments { return pushVerdictArguments(['SDIFF'], keys); } -export declare function transformReply(): Array; +export declare function transformReply(): Array; diff --git a/packages/client/lib/commands/SDIFFSTORE.ts b/packages/client/lib/commands/SDIFFSTORE.ts index 9cca24beb61..a927e12ef0e 100644 --- a/packages/client/lib/commands/SDIFFSTORE.ts +++ b/packages/client/lib/commands/SDIFFSTORE.ts @@ -1,9 +1,12 @@ -import { RedisCommandArguments } from '.'; +import { RedisCommandArgument, RedisCommandArguments } from '.'; import { pushVerdictArguments } from './generic-transformers'; export const FIRST_KEY_INDEX = 1; -export function transformArguments(destination: string, keys: string | Array): RedisCommandArguments { +export function transformArguments( + destination: RedisCommandArgument, + keys: RedisCommandArgument | Array +): RedisCommandArguments { return pushVerdictArguments(['SDIFFSTORE', destination], keys); } diff --git a/packages/client/lib/commands/SET.ts b/packages/client/lib/commands/SET.ts index e94abc47ac2..3f9f568f23a 100644 --- a/packages/client/lib/commands/SET.ts +++ b/packages/client/lib/commands/SET.ts @@ -60,6 +60,4 @@ export function transformArguments( return args; } -export function transformReply(reply?: string): string | null { - return reply ?? null; -} +export declare function transformReply(): RedisCommandArgument | null; diff --git a/packages/client/lib/commands/SETBIT.ts b/packages/client/lib/commands/SETBIT.ts index 7b0812a55ba..94f463330a8 100644 --- a/packages/client/lib/commands/SETBIT.ts +++ b/packages/client/lib/commands/SETBIT.ts @@ -1,9 +1,13 @@ -import { RedisCommandArguments } from '.'; +import { RedisCommandArgument, RedisCommandArguments } from '.'; import { BitValue } from './generic-transformers'; export const FIRST_KEY_INDEX = 1; -export function transformArguments(key: string, offset: number, value: BitValue): RedisCommandArguments { +export function transformArguments( + key: RedisCommandArgument, + offset: number, + value: BitValue +): RedisCommandArguments { return ['SETBIT', key, offset.toString(), value.toString()]; } diff --git a/packages/client/lib/commands/SETEX.ts b/packages/client/lib/commands/SETEX.ts index 871c286b245..bb3068501f0 100644 --- a/packages/client/lib/commands/SETEX.ts +++ b/packages/client/lib/commands/SETEX.ts @@ -5,7 +5,7 @@ export const FIRST_KEY_INDEX = 1; export function transformArguments( key: RedisCommandArgument, seconds: number, - value: string + value: RedisCommandArgument ): RedisCommandArguments { return [ 'SETEX', @@ -15,4 +15,4 @@ export function transformArguments( ]; } -export declare function transformReply(): string; +export declare function transformReply(): RedisCommandArgument; diff --git a/packages/client/lib/commands/SETNX.ts b/packages/client/lib/commands/SETNX.ts index b45e93b0f7d..b01d45dc32f 100644 --- a/packages/client/lib/commands/SETNX.ts +++ b/packages/client/lib/commands/SETNX.ts @@ -1,7 +1,12 @@ +import { RedisCommandArgument, RedisCommandArguments } from '.'; + export const FIRST_KEY_INDEX = 1; -export function transformArguments(key: string, value: string): Array { +export function transformArguments( + key: RedisCommandArgument, + value: RedisCommandArgument +): RedisCommandArguments { return ['SETNX', key, value]; } -export { transformReplyBoolean as transformReply } from './generic-transformers'; +export { transformBooleanReply as transformReply } from './generic-transformers'; diff --git a/packages/client/lib/commands/SETRANGE.ts b/packages/client/lib/commands/SETRANGE.ts index fa15c41db9d..038a8a5dd7f 100644 --- a/packages/client/lib/commands/SETRANGE.ts +++ b/packages/client/lib/commands/SETRANGE.ts @@ -1,6 +1,12 @@ +import { RedisCommandArgument, RedisCommandArguments } from '.'; + export const FIRST_KEY_INDEX = 1; -export function transformArguments(key: string, offset: number, value: string): Array { +export function transformArguments( + key: RedisCommandArgument, + offset: number, + value: RedisCommandArgument +): RedisCommandArguments { return ['SETRANGE', key, offset.toString(), value]; } diff --git a/packages/client/lib/commands/SINTER.ts b/packages/client/lib/commands/SINTER.ts index 5d74e761f0e..fe1feee7ade 100644 --- a/packages/client/lib/commands/SINTER.ts +++ b/packages/client/lib/commands/SINTER.ts @@ -1,10 +1,14 @@ -import { RedisCommandArguments } from '.'; +import { RedisCommandArgument, RedisCommandArguments } from '.'; import { pushVerdictArguments } from './generic-transformers'; export const FIRST_KEY_INDEX = 1; -export function transformArguments(keys: string | Array): RedisCommandArguments { +export const IS_READ_ONLY = true; + +export function transformArguments( + keys: RedisCommandArgument | Array +): RedisCommandArguments { return pushVerdictArguments(['SINTER'], keys); } -export declare function transformReply(): Array; +export declare function transformReply(): Array; diff --git a/packages/client/lib/commands/SINTERSTORE.ts b/packages/client/lib/commands/SINTERSTORE.ts index 40f31a8b7a3..02bf9d061a0 100644 --- a/packages/client/lib/commands/SINTERSTORE.ts +++ b/packages/client/lib/commands/SINTERSTORE.ts @@ -1,10 +1,13 @@ -import { RedisCommandArguments } from '.'; +import { RedisCommandArgument, RedisCommandArguments } from '.'; import { pushVerdictArguments } from './generic-transformers'; export const FIRST_KEY_INDEX = 1; -export function transformArguments(destination: string, keys: string | Array): RedisCommandArguments { +export function transformArguments( + destination: RedisCommandArgument, + keys: RedisCommandArgument | Array +): RedisCommandArguments { return pushVerdictArguments(['SINTERSTORE', destination], keys); } -export declare function transformReply(): Array; +export declare function transformReply(): Array; diff --git a/packages/client/lib/commands/SISMEMBER.ts b/packages/client/lib/commands/SISMEMBER.ts index d8c47a76a69..4d40c63250e 100644 --- a/packages/client/lib/commands/SISMEMBER.ts +++ b/packages/client/lib/commands/SISMEMBER.ts @@ -1,7 +1,12 @@ +import { RedisCommandArgument, RedisCommandArguments } from '.'; + export const FIRST_KEY_INDEX = 1; -export function transformArguments(key: string, member: string): Array { +export function transformArguments( + key: RedisCommandArgument, + member: RedisCommandArgument +): RedisCommandArguments { return ['SISMEMBER', key, member]; } -export { transformReplyBoolean as transformReply } from './generic-transformers'; +export { transformBooleanReply as transformReply } from './generic-transformers'; diff --git a/packages/client/lib/commands/SMEMBERS.ts b/packages/client/lib/commands/SMEMBERS.ts index 71b479f9d8e..7950a4c073a 100644 --- a/packages/client/lib/commands/SMEMBERS.ts +++ b/packages/client/lib/commands/SMEMBERS.ts @@ -1,7 +1,9 @@ +import { RedisCommandArgument, RedisCommandArguments } from '.'; + export const FIRST_KEY_INDEX = 1; -export function transformArguments(key: string): Array { +export function transformArguments(key: RedisCommandArgument): RedisCommandArguments { return ['SMEMBERS', key]; } -export declare function transformReply(): Array; +export declare function transformReply(): Array; diff --git a/packages/client/lib/commands/SMISMEMBER.ts b/packages/client/lib/commands/SMISMEMBER.ts index 85b954bc3d7..175120bdfb9 100644 --- a/packages/client/lib/commands/SMISMEMBER.ts +++ b/packages/client/lib/commands/SMISMEMBER.ts @@ -1,7 +1,12 @@ +import { RedisCommandArgument, RedisCommandArguments } from '.'; + export const FIRST_KEY_INDEX = 1; -export function transformArguments(key: string, members: Array): Array { +export function transformArguments( + key: RedisCommandArgument, + members: Array +): RedisCommandArguments { return ['SMISMEMBER', key, ...members]; } -export { transformReplyBooleanArray as transformReply } from './generic-transformers'; +export { transformBooleanArrayReply as transformReply } from './generic-transformers'; diff --git a/packages/client/lib/commands/SMOVE.ts b/packages/client/lib/commands/SMOVE.ts index 7850b8f9ddc..83c4027dbd5 100644 --- a/packages/client/lib/commands/SMOVE.ts +++ b/packages/client/lib/commands/SMOVE.ts @@ -1,7 +1,13 @@ +import { RedisCommandArgument, RedisCommandArguments } from '.'; + export const FIRST_KEY_INDEX = 1; -export function transformArguments(source: string, destination: string, member: string): Array { +export function transformArguments( + source: RedisCommandArgument, + destination: RedisCommandArgument, + member: RedisCommandArgument +): RedisCommandArguments { return ['SMOVE', source, destination, member]; } -export { transformReplyBoolean as transformReply } from './generic-transformers'; +export { transformBooleanReply as transformReply } from './generic-transformers'; diff --git a/packages/client/lib/commands/SPOP.ts b/packages/client/lib/commands/SPOP.ts index 84845230d41..38ce8573f3f 100644 --- a/packages/client/lib/commands/SPOP.ts +++ b/packages/client/lib/commands/SPOP.ts @@ -1,6 +1,11 @@ +import { RedisCommandArgument, RedisCommandArguments } from '.'; + export const FIRST_KEY_INDEX = 1; -export function transformArguments(key: string, count?: number): Array { +export function transformArguments( + key: RedisCommandArgument, + count?: number +): RedisCommandArguments { const args = ['SPOP', key]; if (typeof count === 'number') { @@ -10,4 +15,4 @@ export function transformArguments(key: string, count?: number): Array { return args; } -export declare function transformReply(): Array; +export declare function transformReply(): Array; diff --git a/packages/client/lib/commands/SRANDMEMBER.ts b/packages/client/lib/commands/SRANDMEMBER.ts index c477a5691d7..d84e61993e5 100644 --- a/packages/client/lib/commands/SRANDMEMBER.ts +++ b/packages/client/lib/commands/SRANDMEMBER.ts @@ -1,7 +1,9 @@ +import { RedisCommandArgument, RedisCommandArguments } from '.'; + export const FIRST_KEY_INDEX = 1; -export function transformArguments(key: string): Array { +export function transformArguments(key: RedisCommandArgument): RedisCommandArguments { return ['SRANDMEMBER', key]; } -export declare function transformReply(): string | null; +export declare function transformReply(): RedisCommandArgument | null; diff --git a/packages/client/lib/commands/SRANDMEMBER_COUNT.ts b/packages/client/lib/commands/SRANDMEMBER_COUNT.ts index 89d9b8c4aef..d265d89e9a6 100644 --- a/packages/client/lib/commands/SRANDMEMBER_COUNT.ts +++ b/packages/client/lib/commands/SRANDMEMBER_COUNT.ts @@ -1,12 +1,16 @@ +import { RedisCommandArgument, RedisCommandArguments } from '.'; import { transformArguments as transformSRandMemberArguments } from './SRANDMEMBER'; export { FIRST_KEY_INDEX } from './SRANDMEMBER'; -export function transformArguments(key: string, count: number): Array { +export function transformArguments( + key: RedisCommandArgument, + count: number +): RedisCommandArguments { return [ ...transformSRandMemberArguments(key), count.toString() ]; } -export declare function transformReply(): Array; +export declare function transformReply(): Array; diff --git a/packages/client/lib/commands/SREM.ts b/packages/client/lib/commands/SREM.ts index 9a37ac9bf99..34aebdf02e3 100644 --- a/packages/client/lib/commands/SREM.ts +++ b/packages/client/lib/commands/SREM.ts @@ -1,9 +1,12 @@ -import { RedisCommandArguments } from '.'; +import { RedisCommandArgument, RedisCommandArguments } from '.'; import { pushVerdictArguments } from './generic-transformers'; export const FIRST_KEY_INDEX = 1; -export function transformArguments(key: string, members: string | Array): RedisCommandArguments { +export function transformArguments( + key: RedisCommandArgument, + members: RedisCommandArgument | Array +): RedisCommandArguments { return pushVerdictArguments(['SREM', key], members); } diff --git a/packages/client/lib/commands/SSCAN.ts b/packages/client/lib/commands/SSCAN.ts index 9b881f5d882..9b3938f159b 100644 --- a/packages/client/lib/commands/SSCAN.ts +++ b/packages/client/lib/commands/SSCAN.ts @@ -1,22 +1,29 @@ +import { RedisCommandArgument, RedisCommandArguments } from '.'; import { ScanOptions, pushScanArguments } from './generic-transformers'; export const FIRST_KEY_INDEX = 1; export const IS_READ_ONLY = true; -export function transformArguments(key: string, cursor: number, options?: ScanOptions): Array { +export function transformArguments( + key: RedisCommandArgument, + cursor: number, + options?: ScanOptions +): RedisCommandArguments { return pushScanArguments([ 'SSCAN', key, ], cursor, options); } +type SScanRawReply = [string, Array]; + interface SScanReply { cursor: number; - members: Array; + members: Array; } -export function transformReply([cursor, members]: [string, Array]): SScanReply { +export function transformReply([cursor, members]: SScanRawReply): SScanReply { return { cursor: Number(cursor), members diff --git a/packages/client/lib/commands/STRLEN.ts b/packages/client/lib/commands/STRLEN.ts index 208d9d73fb8..de88340d8b6 100644 --- a/packages/client/lib/commands/STRLEN.ts +++ b/packages/client/lib/commands/STRLEN.ts @@ -1,8 +1,10 @@ +import { RedisCommandArgument, RedisCommandArguments } from '.'; + export const FIRST_KEY_INDEX = 1; export const IS_READ_ONLY = true; -export function transformArguments(key: string): Array { +export function transformArguments(key: RedisCommandArgument): RedisCommandArguments { return ['STRLEN', key]; } diff --git a/packages/client/lib/commands/SUNION.ts b/packages/client/lib/commands/SUNION.ts index ae8b02b481d..52c112e6610 100644 --- a/packages/client/lib/commands/SUNION.ts +++ b/packages/client/lib/commands/SUNION.ts @@ -1,12 +1,14 @@ -import { RedisCommandArguments } from '.'; +import { RedisCommandArgument, RedisCommandArguments } from '.'; import { pushVerdictArguments } from './generic-transformers'; export const FIRST_KEY_INDEX = 1; export const IS_READ_ONLY = true; -export function transformArguments(keys: string | Array): RedisCommandArguments { +export function transformArguments( + keys: RedisCommandArgument | Array +): RedisCommandArguments { return pushVerdictArguments(['SUNION'], keys); } -export declare function transformReply(): Array; +export declare function transformReply(): Array; diff --git a/packages/client/lib/commands/SUNIONSTORE.ts b/packages/client/lib/commands/SUNIONSTORE.ts index f259769f49a..94df6771a04 100644 --- a/packages/client/lib/commands/SUNIONSTORE.ts +++ b/packages/client/lib/commands/SUNIONSTORE.ts @@ -1,9 +1,12 @@ -import { RedisCommandArguments } from '.'; +import { RedisCommandArgument, RedisCommandArguments } from '.'; import { pushVerdictArguments } from './generic-transformers'; export const FIRST_KEY_INDEX = 1; -export function transformArguments(destination: string, keys: string | Array): RedisCommandArguments { +export function transformArguments( + destination: RedisCommandArgument, + keys: RedisCommandArgument | Array +): RedisCommandArguments { return pushVerdictArguments(['SUNIONSTORE', destination], keys); } diff --git a/packages/client/lib/commands/TOUCH.ts b/packages/client/lib/commands/TOUCH.ts index a3dc31e8568..e67dff8e932 100644 --- a/packages/client/lib/commands/TOUCH.ts +++ b/packages/client/lib/commands/TOUCH.ts @@ -1,9 +1,11 @@ -import { RedisCommandArguments } from '.'; +import { RedisCommandArgument, RedisCommandArguments } from '.'; import { pushVerdictArguments } from './generic-transformers'; export const FIRST_KEY_INDEX = 1; -export function transformArguments(key: string | Array): RedisCommandArguments { +export function transformArguments( + key: RedisCommandArgument | Array +): RedisCommandArguments { return pushVerdictArguments(['TOUCH'], key); } diff --git a/packages/client/lib/commands/TTL.ts b/packages/client/lib/commands/TTL.ts index 4ae31245aa0..29586f31fa8 100644 --- a/packages/client/lib/commands/TTL.ts +++ b/packages/client/lib/commands/TTL.ts @@ -1,8 +1,10 @@ +import { RedisCommandArgument, RedisCommandArguments } from '.'; + export const FIRST_KEY_INDEX = 1; export const IS_READ_ONLY = true; -export function transformArguments(key: string): Array { +export function transformArguments(key: RedisCommandArgument): RedisCommandArguments { return ['TTL', key]; } diff --git a/packages/client/lib/commands/TYPE.ts b/packages/client/lib/commands/TYPE.ts index 283701b6e9f..10cd3f99b0e 100644 --- a/packages/client/lib/commands/TYPE.ts +++ b/packages/client/lib/commands/TYPE.ts @@ -1,9 +1,11 @@ +import { RedisCommandArgument, RedisCommandArguments } from '.'; + export const FIRST_KEY_INDEX = 1; export const IS_READ_ONLY = true; -export function transformArguments(key: string): Array { +export function transformArguments(key: RedisCommandArgument): RedisCommandArguments { return ['TYPE', key]; } -export declare function transformReply(): string; +export declare function transformReply(): RedisCommandArgument; diff --git a/packages/client/lib/commands/UNLINK.ts b/packages/client/lib/commands/UNLINK.ts index 467b4172e0f..53b0360e2df 100644 --- a/packages/client/lib/commands/UNLINK.ts +++ b/packages/client/lib/commands/UNLINK.ts @@ -1,9 +1,11 @@ -import { RedisCommandArguments } from '.'; +import { RedisCommandArgument, RedisCommandArguments } from '.'; import { pushVerdictArguments } from './generic-transformers'; export const FIRST_KEY_INDEX = 1; -export function transformArguments(key: string | Array): RedisCommandArguments { +export function transformArguments( + key: RedisCommandArgument | Array +): RedisCommandArguments { return pushVerdictArguments(['UNLINK'], key); } diff --git a/packages/client/lib/commands/XAUTOCLAIM.ts b/packages/client/lib/commands/XAUTOCLAIM.ts index 9eeb04623d9..4bf46057bac 100644 --- a/packages/client/lib/commands/XAUTOCLAIM.ts +++ b/packages/client/lib/commands/XAUTOCLAIM.ts @@ -1,5 +1,5 @@ import { RedisCommandArgument, RedisCommandArguments } from '.'; -import { StreamStringsMessagesReply, transformReplyStreamStringMessages } from './generic-transformers'; +import { StreamMessagesReply, transformStreamMessagesReply } from './generic-transformers'; export const FIRST_KEY_INDEX = 1; @@ -24,14 +24,16 @@ export function transformArguments( return args; } +type XAutoClaimRawReply = [RedisCommandArgument, Array]; + interface XAutoClaimReply { - nextId: string; - messages: StreamStringsMessagesReply; + nextId: RedisCommandArgument; + messages: StreamMessagesReply; } -export function transformReply(reply: [string, Array]): XAutoClaimReply { +export function transformReply(reply: XAutoClaimRawReply): XAutoClaimReply { return { nextId: reply[0], - messages: transformReplyStreamStringMessages(reply[1]) + messages: transformStreamMessagesReply(reply[1]) }; } diff --git a/packages/client/lib/commands/XAUTOCLAIM_BUFFER.ts b/packages/client/lib/commands/XAUTOCLAIM_BUFFER.ts deleted file mode 100644 index dab264be873..00000000000 --- a/packages/client/lib/commands/XAUTOCLAIM_BUFFER.ts +++ /dev/null @@ -1,17 +0,0 @@ -import { StreamBufferMessagesReply, transformReplyStreamBufferMessages } from './generic-transformers'; - -export { FIRST_KEY_INDEX, transformArguments } from './XAUTOCLAIM'; - -export const BUFFER_MODE = true; - -interface XAutoClaimReply { - nextId: Buffer; - messages: StreamBufferMessagesReply; -} - -export function transformReply(reply: [Buffer, Array]): XAutoClaimReply { - return { - nextId: reply[0], - messages: transformReplyStreamBufferMessages(reply[1]) - }; -} diff --git a/packages/client/lib/commands/XAUTOCLAIM_JUSTID.ts b/packages/client/lib/commands/XAUTOCLAIM_JUSTID.ts index b18f49b6b79..a30ac1579e7 100644 --- a/packages/client/lib/commands/XAUTOCLAIM_JUSTID.ts +++ b/packages/client/lib/commands/XAUTOCLAIM_JUSTID.ts @@ -1,4 +1,4 @@ -import { RedisCommandArguments } from '.'; +import { RedisCommandArgument, RedisCommandArguments } from '.'; import { transformArguments as transformXAutoClaimArguments } from './XAUTOCLAIM'; export { FIRST_KEY_INDEX } from './XAUTOCLAIM'; @@ -10,12 +10,14 @@ export function transformArguments(...args: Parameters]; + interface XAutoClaimJustIdReply { - nextId: string; - messages: Array; + nextId: RedisCommandArgument; + messages: Array; } -export function transformReply(reply: [string, Array]): XAutoClaimJustIdReply { +export function transformReply(reply: XAutoClaimJustIdRawReply): XAutoClaimJustIdReply { return { nextId: reply[0], messages: reply[1] diff --git a/packages/client/lib/commands/XAUTOCLAIM_JUSTID_BUFFER.ts b/packages/client/lib/commands/XAUTOCLAIM_JUSTID_BUFFER.ts deleted file mode 100644 index eeb9c801e10..00000000000 --- a/packages/client/lib/commands/XAUTOCLAIM_JUSTID_BUFFER.ts +++ /dev/null @@ -1,13 +0,0 @@ -export { FIRST_KEY_INDEX, transformArguments } from './XAUTOCLAIM_JUSTID'; - -interface XAutoClaimJustIdBufferReply { - nextId: Buffer; - messages: Array; -} - -export function transformReply(reply: [Buffer, Array]): XAutoClaimJustIdBufferReply { - return { - nextId: reply[0], - messages: reply[1] - }; -} diff --git a/packages/client/lib/commands/XCLAIM.ts b/packages/client/lib/commands/XCLAIM.ts index 84362f917a7..c87d2547546 100644 --- a/packages/client/lib/commands/XCLAIM.ts +++ b/packages/client/lib/commands/XCLAIM.ts @@ -44,4 +44,4 @@ export function transformArguments( return args; } -export { transformReplyStreamStringMessages as transformReply } from './generic-transformers'; +export { transformStreamMessagesReply as transformReply } from './generic-transformers'; diff --git a/packages/client/lib/commands/XCLAIM_BUFFER.ts b/packages/client/lib/commands/XCLAIM_BUFFER.ts deleted file mode 100644 index 45e960ff862..00000000000 --- a/packages/client/lib/commands/XCLAIM_BUFFER.ts +++ /dev/null @@ -1,5 +0,0 @@ -export { FIRST_KEY_INDEX, transformArguments } from './XCLAIM'; - -export const BUFFER_MODE = true; - -export { transformReplyStreamBufferMessages as transformReply } from './generic-transformers'; diff --git a/packages/client/lib/commands/XCLAIM_JUSTID.ts b/packages/client/lib/commands/XCLAIM_JUSTID.ts index da2dce2d3dc..50d0d5a0366 100644 --- a/packages/client/lib/commands/XCLAIM_JUSTID.ts +++ b/packages/client/lib/commands/XCLAIM_JUSTID.ts @@ -1,4 +1,4 @@ -import { RedisCommandArguments } from '.'; +import { RedisCommandArgument, RedisCommandArguments } from '.'; import { transformArguments as transformXClaimArguments } from './XCLAIM'; export { FIRST_KEY_INDEX } from './XCLAIM'; @@ -10,4 +10,4 @@ export function transformArguments(...args: Parameters; +export declare function transformReply(): Array; diff --git a/packages/client/lib/commands/XCLAIM_JUSTID_BUFFER.ts b/packages/client/lib/commands/XCLAIM_JUSTID_BUFFER.ts deleted file mode 100644 index 9933e4a6a87..00000000000 --- a/packages/client/lib/commands/XCLAIM_JUSTID_BUFFER.ts +++ /dev/null @@ -1,5 +0,0 @@ -export { FIRST_KEY_INDEX, transformArguments } from './XCLAIM_JUSTID'; - -export const BUFFER_MODE = true; - -export declare function transformReply(): Array; diff --git a/packages/client/lib/commands/XGROUP_CREATE.ts b/packages/client/lib/commands/XGROUP_CREATE.ts index 85847029cfe..8cfd4e262e4 100644 --- a/packages/client/lib/commands/XGROUP_CREATE.ts +++ b/packages/client/lib/commands/XGROUP_CREATE.ts @@ -21,4 +21,4 @@ export function transformArguments( return args; } -export declare function transformReply(): 'OK'; +export declare function transformReply(): RedisCommandArgument; diff --git a/packages/client/lib/commands/XGROUP_CREATECONSUMER.ts b/packages/client/lib/commands/XGROUP_CREATECONSUMER.ts index 6f1458e337e..2b816a6b480 100644 --- a/packages/client/lib/commands/XGROUP_CREATECONSUMER.ts +++ b/packages/client/lib/commands/XGROUP_CREATECONSUMER.ts @@ -10,4 +10,4 @@ export function transformArguments( return ['XGROUP', 'CREATECONSUMER', key, group, consumer]; } -export { transformReplyBoolean as transformReply } from './generic-transformers'; +export { transformBooleanReply as transformReply } from './generic-transformers'; diff --git a/packages/client/lib/commands/XGROUP_DESTROY.ts b/packages/client/lib/commands/XGROUP_DESTROY.ts index 3bd61b30567..85910c02471 100644 --- a/packages/client/lib/commands/XGROUP_DESTROY.ts +++ b/packages/client/lib/commands/XGROUP_DESTROY.ts @@ -9,4 +9,4 @@ export function transformArguments( return ['XGROUP', 'DESTROY', key, group]; } -export { transformReplyBoolean as transformReply } from './generic-transformers'; +export { transformBooleanReply as transformReply } from './generic-transformers'; diff --git a/packages/client/lib/commands/XGROUP_SETID.ts b/packages/client/lib/commands/XGROUP_SETID.ts index fd580d7aac6..e732fc8d7bf 100644 --- a/packages/client/lib/commands/XGROUP_SETID.ts +++ b/packages/client/lib/commands/XGROUP_SETID.ts @@ -10,4 +10,4 @@ export function transformArguments( return ['XGROUP', 'SETID', key, group, id]; } -export declare function transformReply(): 'OK'; +export declare function transformReply(): RedisCommandArgument; diff --git a/packages/client/lib/commands/XINFO_CONSUMERS.ts b/packages/client/lib/commands/XINFO_CONSUMERS.ts index 8cdcb9d1431..05e3a26b172 100644 --- a/packages/client/lib/commands/XINFO_CONSUMERS.ts +++ b/packages/client/lib/commands/XINFO_CONSUMERS.ts @@ -12,7 +12,7 @@ export function transformArguments( } type XInfoConsumersReply = Array<{ - name: string; + name: RedisCommandArgument; pending: number; idle: number; }>; diff --git a/packages/client/lib/commands/XINFO_GROUPS.ts b/packages/client/lib/commands/XINFO_GROUPS.ts index 2799cd72440..dcf504c8ce7 100644 --- a/packages/client/lib/commands/XINFO_GROUPS.ts +++ b/packages/client/lib/commands/XINFO_GROUPS.ts @@ -9,10 +9,10 @@ export function transformArguments(key: RedisCommandArgument): RedisCommandArgum } type XInfoGroupsReply = Array<{ - name: string; + name: RedisCommandArgument; consumers: number; pending: number; - lastDeliveredId: string; + lastDeliveredId: RedisCommandArgument; }>; export function transformReply(rawReply: Array): XInfoGroupsReply { diff --git a/packages/client/lib/commands/XINFO_STREAM.ts b/packages/client/lib/commands/XINFO_STREAM.ts index c62ad1eee88..e9de25be8cb 100644 --- a/packages/client/lib/commands/XINFO_STREAM.ts +++ b/packages/client/lib/commands/XINFO_STREAM.ts @@ -1,5 +1,5 @@ import { RedisCommandArgument, RedisCommandArguments } from '.'; -import { StreamStringsMessageReply, transformReplyStringTuples } from './generic-transformers'; +import { StreamMessageReply, transformTuplesReply } from './generic-transformers'; export const FIRST_KEY_INDEX = 2; @@ -14,9 +14,9 @@ interface XInfoStreamReply { radixTreeKeys: number; radixTreeNodes: number; groups: number; - lastGeneratedId: string; - firstEntry: StreamStringsMessageReply | null; - lastEntry: StreamStringsMessageReply | null; + lastGeneratedId: RedisCommandArgument; + firstEntry: StreamMessageReply | null; + lastEntry: StreamMessageReply | null; } export function transformReply(rawReply: Array): XInfoStreamReply { @@ -47,14 +47,14 @@ export function transformReply(rawReply: Array): XInfoStreamReply { case 'first-entry': parsedReply.firstEntry = rawReply[i + 1] ? { id: rawReply[i + 1][0], - message: transformReplyStringTuples(rawReply[i + 1][1]) + message: transformTuplesReply(rawReply[i + 1][1]) } : null; break; case 'last-entry': parsedReply.lastEntry = rawReply[i + 1] ? { id: rawReply[i + 1][0], - message: transformReplyStringTuples(rawReply[i + 1][1]) + message: transformTuplesReply(rawReply[i + 1][1]) } : null; break; } diff --git a/packages/client/lib/commands/XPENDING.ts b/packages/client/lib/commands/XPENDING.ts index aedd8c68853..a6052adb0f2 100644 --- a/packages/client/lib/commands/XPENDING.ts +++ b/packages/client/lib/commands/XPENDING.ts @@ -13,20 +13,20 @@ export function transformArguments( type XPendingRawReply = [ pending: number, - firstId: string | null, - lastId: string | null, + firstId: RedisCommandArgument | null, + lastId: RedisCommandArgument | null, consumers: Array<[ - name: string, + name: RedisCommandArgument, deliveriesCounter: number ]> | null ] interface XPendingReply { pending: number; - firstId: string | null; - lastId: string | null + firstId: RedisCommandArgument | null; + lastId: RedisCommandArgument | null consumers: Array<{ - name: string, + name: RedisCommandArgument, deliveriesCounter: number }> | null; } diff --git a/packages/client/lib/commands/XPENDING_RANGE.ts b/packages/client/lib/commands/XPENDING_RANGE.ts index 1299f8d603e..fd9a09113bf 100644 --- a/packages/client/lib/commands/XPENDING_RANGE.ts +++ b/packages/client/lib/commands/XPENDING_RANGE.ts @@ -6,7 +6,7 @@ export const IS_READ_ONLY = true; interface XPendingRangeOptions { IDLE?: number; - consumer?: string; + consumer?: RedisCommandArgument; } export function transformArguments( @@ -34,14 +34,14 @@ export function transformArguments( type XPendingRangeRawReply = Array<[ id: number, - consumer: string, + consumer: RedisCommandArgument, millisecondsSinceLastDelivery: number, deliveriesCounter: number ]>; type XPendingRangeReply = Array<{ id: number; - owner: string; + owner: RedisCommandArgument; millisecondsSinceLastDelivery: number; deliveriesCounter: number; }>; diff --git a/packages/client/lib/commands/XRANGE.ts b/packages/client/lib/commands/XRANGE.ts index 283e3423d22..ae56639f769 100644 --- a/packages/client/lib/commands/XRANGE.ts +++ b/packages/client/lib/commands/XRANGE.ts @@ -23,4 +23,4 @@ export function transformArguments( return args; } -export { transformReplyStreamStringMessages as transformReply } from './generic-transformers'; +export { transformStreamMessagesReply as transformReply } from './generic-transformers'; diff --git a/packages/client/lib/commands/XRANGE_BUFFER.ts b/packages/client/lib/commands/XRANGE_BUFFER.ts deleted file mode 100644 index f69c824c142..00000000000 --- a/packages/client/lib/commands/XRANGE_BUFFER.ts +++ /dev/null @@ -1,5 +0,0 @@ -export { FIRST_KEY_INDEX, IS_READ_ONLY, transformArguments } from './XRANGE'; - -export const BUFFER_MODE = true; - -export { transformReplyStreamBufferMessages as transformReply } from './generic-transformers'; diff --git a/packages/client/lib/commands/XREAD.ts b/packages/client/lib/commands/XREAD.ts index a81ffbae599..e5f85dbe7fe 100644 --- a/packages/client/lib/commands/XREAD.ts +++ b/packages/client/lib/commands/XREAD.ts @@ -43,4 +43,4 @@ export function transformArguments( return args; } -export { transformReplyStreamsStringMessages as transformReply } from './generic-transformers'; +export { transformStreamsMessagesReply as transformReply } from './generic-transformers'; diff --git a/packages/client/lib/commands/XREADGROUP.ts b/packages/client/lib/commands/XREADGROUP.ts index 5814de94eff..e90e698a2ad 100644 --- a/packages/client/lib/commands/XREADGROUP.ts +++ b/packages/client/lib/commands/XREADGROUP.ts @@ -54,4 +54,4 @@ export function transformArguments( return args; } -export { transformReplyStreamsStringMessages as transformReply } from './generic-transformers'; +export { transformStreamsMessagesReply as transformReply } from './generic-transformers'; diff --git a/packages/client/lib/commands/XREADGROUP_BUFFER.ts b/packages/client/lib/commands/XREADGROUP_BUFFER.ts deleted file mode 100644 index f02c8fee273..00000000000 --- a/packages/client/lib/commands/XREADGROUP_BUFFER.ts +++ /dev/null @@ -1,6 +0,0 @@ - -export { FIRST_KEY_INDEX, IS_READ_ONLY, transformArguments } from './XREADGROUP'; - -export const BUFFER_MODE = true; - -export { transformReplyStreamsBufferMessages as transformReply } from './generic-transformers'; diff --git a/packages/client/lib/commands/XREAD_BUFFER.ts b/packages/client/lib/commands/XREAD_BUFFER.ts deleted file mode 100644 index c699aae8ada..00000000000 --- a/packages/client/lib/commands/XREAD_BUFFER.ts +++ /dev/null @@ -1,5 +0,0 @@ -export { FIRST_KEY_INDEX, IS_READ_ONLY, transformArguments } from './XREAD'; - -export const BUFFER_MODE = true; - -export { transformReplyStreamsBufferMessages as transformReply } from './generic-transformers'; diff --git a/packages/client/lib/commands/XREVRANGE.ts b/packages/client/lib/commands/XREVRANGE.ts index c61fcb7a520..96bbeba83ce 100644 --- a/packages/client/lib/commands/XREVRANGE.ts +++ b/packages/client/lib/commands/XREVRANGE.ts @@ -1,12 +1,19 @@ -interface XRangeRevOptions { - COUNT?: number; -} +import { RedisCommandArgument, RedisCommandArguments } from '.'; export const FIRST_KEY_INDEX = 1; export const IS_READ_ONLY = true; -export function transformArguments(key: string, start: string, end: string, options?: XRangeRevOptions): Array { +interface XRangeRevOptions { + COUNT?: number; +} + +export function transformArguments( + key: RedisCommandArgument, + start: RedisCommandArgument, + end: RedisCommandArgument, + options?: XRangeRevOptions +): RedisCommandArguments { const args = ['XREVRANGE', key, start, end]; if (options?.COUNT) { @@ -16,4 +23,4 @@ export function transformArguments(key: string, start: string, end: string, opti return args; } -export { transformReplyStreamStringMessages as transformReply } from './generic-transformers'; +export { transformStreamMessagesReply as transformReply } from './generic-transformers'; diff --git a/packages/client/lib/commands/XREVRANGE_BUFFER.ts b/packages/client/lib/commands/XREVRANGE_BUFFER.ts deleted file mode 100644 index eb45ee486ed..00000000000 --- a/packages/client/lib/commands/XREVRANGE_BUFFER.ts +++ /dev/null @@ -1,5 +0,0 @@ -export { FIRST_KEY_INDEX, IS_READ_ONLY, transformArguments } from './XREVRANGE'; - -export const BUFFER_MODE = true; - -export { transformReplyStreamBufferMessages as transformReply } from './generic-transformers'; diff --git a/packages/client/lib/commands/ZADD.ts b/packages/client/lib/commands/ZADD.ts index c8b7c4002c7..9ac67d59cce 100644 --- a/packages/client/lib/commands/ZADD.ts +++ b/packages/client/lib/commands/ZADD.ts @@ -1,5 +1,5 @@ import { RedisCommandArgument, RedisCommandArguments } from '.'; -import { transformArgumentNumberInfinity, ZMember } from './generic-transformers'; +import { transformNumberInfinityArgument, ZMember } from './generic-transformers'; export const FIRST_KEY_INDEX = 1; @@ -60,7 +60,7 @@ export function transformArguments( for (const { score, value } of (Array.isArray(members) ? members : [members])) { args.push( - transformArgumentNumberInfinity(score), + transformNumberInfinityArgument(score), value ); } @@ -68,4 +68,4 @@ export function transformArguments( return args; } -export { transformReplyNumberInfinity as transformReply } from './generic-transformers'; +export { transformNumberInfinityReply as transformReply } from './generic-transformers'; diff --git a/packages/client/lib/commands/ZCARD.ts b/packages/client/lib/commands/ZCARD.ts index 9c76c485bf9..f208c181369 100644 --- a/packages/client/lib/commands/ZCARD.ts +++ b/packages/client/lib/commands/ZCARD.ts @@ -1,8 +1,10 @@ +import { RedisCommandArgument, RedisCommandArguments } from '.'; + export const FIRST_KEY_INDEX = 1; export const IS_READ_ONLY = true; -export function transformArguments(key: string): Array { +export function transformArguments(key: RedisCommandArgument): RedisCommandArguments { return ['ZCARD', key]; } diff --git a/packages/client/lib/commands/ZCOUNT.ts b/packages/client/lib/commands/ZCOUNT.ts index 83a0710fa11..f9700cc9099 100644 --- a/packages/client/lib/commands/ZCOUNT.ts +++ b/packages/client/lib/commands/ZCOUNT.ts @@ -1,15 +1,20 @@ -import { transformArgumentStringNumberInfinity } from './generic-transformers'; +import { RedisCommandArgument, RedisCommandArguments } from '.'; +import { transformStringNumberInfinityArgument } from './generic-transformers'; export const FIRST_KEY_INDEX = 1; export const IS_READ_ONLY = true; -export function transformArguments(key: string, min: string | number, max: string | number): Array { +export function transformArguments( + key: RedisCommandArgument, + min: RedisCommandArgument | number, + max: RedisCommandArgument | number +): RedisCommandArguments { return [ 'ZCOUNT', key, - transformArgumentStringNumberInfinity(min), - transformArgumentStringNumberInfinity(max) + transformStringNumberInfinityArgument(min), + transformStringNumberInfinityArgument(max) ]; } diff --git a/packages/client/lib/commands/ZDIFF.ts b/packages/client/lib/commands/ZDIFF.ts index a45bf01a526..f3818a139f1 100644 --- a/packages/client/lib/commands/ZDIFF.ts +++ b/packages/client/lib/commands/ZDIFF.ts @@ -1,12 +1,14 @@ -import { RedisCommandArguments } from '.'; +import { RedisCommandArgument, RedisCommandArguments } from '.'; import { pushVerdictArgument } from './generic-transformers'; export const FIRST_KEY_INDEX = 2; export const IS_READ_ONLY = true; -export function transformArguments(keys: Array | string): RedisCommandArguments { +export function transformArguments( + keys: Array | RedisCommandArgument +): RedisCommandArguments { return pushVerdictArgument(['ZDIFF'], keys); } -export declare function transformReply(): Array; +export declare function transformReply(): Array; diff --git a/packages/client/lib/commands/ZDIFFSTORE.ts b/packages/client/lib/commands/ZDIFFSTORE.ts index 9c782b8a5ab..3b9af9511c5 100644 --- a/packages/client/lib/commands/ZDIFFSTORE.ts +++ b/packages/client/lib/commands/ZDIFFSTORE.ts @@ -1,9 +1,12 @@ -import { RedisCommandArguments } from '.'; +import { RedisCommandArgument, RedisCommandArguments } from '.'; import { pushVerdictArgument } from './generic-transformers'; export const FIRST_KEY_INDEX = 1; -export function transformArguments(destination: string, keys: Array | string): RedisCommandArguments { +export function transformArguments( + destination: RedisCommandArgument, + keys: Array | RedisCommandArgument +): RedisCommandArguments { return pushVerdictArgument(['ZDIFFSTORE', destination], keys); } diff --git a/packages/client/lib/commands/ZDIFF_WITHSCORES.ts b/packages/client/lib/commands/ZDIFF_WITHSCORES.ts index 961e1a73e17..9caa13c9f8b 100644 --- a/packages/client/lib/commands/ZDIFF_WITHSCORES.ts +++ b/packages/client/lib/commands/ZDIFF_WITHSCORES.ts @@ -10,4 +10,4 @@ export function transformArguments(...args: Parameters { +export function transformArguments( + key: RedisCommandArgument, + increment: number, + member: RedisCommandArgument +): RedisCommandArguments { return [ 'ZINCRBY', key, - transformArgumentNumberInfinity(increment), + transformNumberInfinityArgument(increment), member ]; } -export { transformReplyNumberInfinity as transformReply } from './generic-transformers'; +export { transformNumberInfinityReply as transformReply } from './generic-transformers'; diff --git a/packages/client/lib/commands/ZINTER.ts b/packages/client/lib/commands/ZINTER.ts index 629515b57f9..88d7f801882 100644 --- a/packages/client/lib/commands/ZINTER.ts +++ b/packages/client/lib/commands/ZINTER.ts @@ -1,4 +1,4 @@ -import { RedisCommandArguments } from '.'; +import { RedisCommandArgument, RedisCommandArguments } from '.'; import { pushVerdictArgument } from './generic-transformers'; export const FIRST_KEY_INDEX = 2; @@ -10,7 +10,10 @@ interface ZInterOptions { AGGREGATE?: 'SUM' | 'MIN' | 'MAX'; } -export function transformArguments(keys: Array | string, options?: ZInterOptions): RedisCommandArguments { +export function transformArguments( + keys: Array | RedisCommandArgument, + options?: ZInterOptions +): RedisCommandArguments { const args = pushVerdictArgument(['ZINTER'], keys); if (options?.WEIGHTS) { @@ -27,4 +30,4 @@ export function transformArguments(keys: Array | string, options?: ZInte return args; } -export declare function transformReply(): Array; +export declare function transformReply(): Array; diff --git a/packages/client/lib/commands/ZINTERSTORE.ts b/packages/client/lib/commands/ZINTERSTORE.ts index e0916e5b14a..540f10ae2d8 100644 --- a/packages/client/lib/commands/ZINTERSTORE.ts +++ b/packages/client/lib/commands/ZINTERSTORE.ts @@ -1,4 +1,4 @@ -import { RedisCommandArguments } from '.'; +import { RedisCommandArgument, RedisCommandArguments } from '.'; import { pushVerdictArgument } from './generic-transformers'; export const FIRST_KEY_INDEX = 1; @@ -8,7 +8,11 @@ interface ZInterStoreOptions { AGGREGATE?: 'SUM' | 'MIN' | 'MAX'; } -export function transformArguments(destination: string, keys: Array | string, options?: ZInterStoreOptions): RedisCommandArguments { +export function transformArguments( + destination: RedisCommandArgument, + keys: Array | RedisCommandArgument, + options?: ZInterStoreOptions +): RedisCommandArguments { const args = pushVerdictArgument(['ZINTERSTORE', destination], keys); if (options?.WEIGHTS) { diff --git a/packages/client/lib/commands/ZINTER_WITHSCORES.ts b/packages/client/lib/commands/ZINTER_WITHSCORES.ts index cb951284bc8..c9416e9222a 100644 --- a/packages/client/lib/commands/ZINTER_WITHSCORES.ts +++ b/packages/client/lib/commands/ZINTER_WITHSCORES.ts @@ -10,4 +10,4 @@ export function transformArguments(...args: Parameters { +export function transformArguments( + key: RedisCommandArgument, + min: RedisCommandArgument, + max: RedisCommandArgument +): RedisCommandArguments { return [ 'ZLEXCOUNT', key, diff --git a/packages/client/lib/commands/ZMSCORE.ts b/packages/client/lib/commands/ZMSCORE.ts index a6dd8e45e4a..6c8c9dace31 100644 --- a/packages/client/lib/commands/ZMSCORE.ts +++ b/packages/client/lib/commands/ZMSCORE.ts @@ -1,12 +1,15 @@ -import { RedisCommandArguments } from '.'; +import { RedisCommandArgument, RedisCommandArguments } from '.'; import { pushVerdictArguments } from './generic-transformers'; export const FIRST_KEY_INDEX = 1; export const IS_READ_ONLY = true; -export function transformArguments(key: string, member: string | Array): RedisCommandArguments { +export function transformArguments( + key: RedisCommandArgument, + member: RedisCommandArgument | Array +): RedisCommandArguments { return pushVerdictArguments(['ZMSCORE', key], member); } -export { transformReplyNumberInfinityNullArray as transformReply } from './generic-transformers'; +export { transformNumberInfinityNullArrayReply as transformReply } from './generic-transformers'; diff --git a/packages/client/lib/commands/ZPOPMAX.ts b/packages/client/lib/commands/ZPOPMAX.ts index 3691f63f0cb..811166a690c 100644 --- a/packages/client/lib/commands/ZPOPMAX.ts +++ b/packages/client/lib/commands/ZPOPMAX.ts @@ -1,19 +1,12 @@ -import { transformReplyNumberInfinity, ZMember } from './generic-transformers'; +import { RedisCommandArgument, RedisCommandArguments } from '.'; export const FIRST_KEY_INDEX = 1; -export function transformArguments(key: string): Array { +export function transformArguments(key: RedisCommandArgument): RedisCommandArguments { return [ 'ZPOPMAX', key ]; } -export function transformReply(reply: [string, string] | []): ZMember | null { - if (!reply.length) return null; - - return { - value: reply[0], - score: transformReplyNumberInfinity(reply[1]) - }; -} +export { transformSortedSetMemberNullReply as transformReply } from './generic-transformers'; diff --git a/packages/client/lib/commands/ZPOPMAX_COUNT.ts b/packages/client/lib/commands/ZPOPMAX_COUNT.ts index cf43cf3fe1f..875bcfb9147 100644 --- a/packages/client/lib/commands/ZPOPMAX_COUNT.ts +++ b/packages/client/lib/commands/ZPOPMAX_COUNT.ts @@ -1,12 +1,16 @@ +import { RedisCommandArgument, RedisCommandArguments } from '.'; import { transformArguments as transformZPopMaxArguments } from './ZPOPMAX'; export { FIRST_KEY_INDEX } from './ZPOPMAX'; -export function transformArguments(key: string, count: number): Array { +export function transformArguments( + key: RedisCommandArgument, + count: number +): RedisCommandArguments { return [ ...transformZPopMaxArguments(key), count.toString() ]; } -export { transformReplySortedStringsSetWithScores as transformReply } from './generic-transformers'; +export { transformSortedSetWithScoresReply as transformReply } from './generic-transformers'; diff --git a/packages/client/lib/commands/ZPOPMIN.ts b/packages/client/lib/commands/ZPOPMIN.ts index 891e56593b6..053ffd2d2ce 100644 --- a/packages/client/lib/commands/ZPOPMIN.ts +++ b/packages/client/lib/commands/ZPOPMIN.ts @@ -1,19 +1,12 @@ -import { transformReplyNumberInfinity, ZMember } from './generic-transformers'; +import { RedisCommandArgument, RedisCommandArguments } from '.'; export const FIRST_KEY_INDEX = 1; -export function transformArguments(key: string): Array { +export function transformArguments(key: RedisCommandArgument): RedisCommandArguments { return [ 'ZPOPMIN', key ]; } -export function transformReply(reply: [string, string] | []): ZMember | null { - if (!reply.length) return null; - - return { - value: reply[0], - score: transformReplyNumberInfinity(reply[1]) - }; -} +export { transformSortedSetMemberNullReply as transformReply } from './generic-transformers'; diff --git a/packages/client/lib/commands/ZPOPMIN_COUNT.ts b/packages/client/lib/commands/ZPOPMIN_COUNT.ts index e3b7adcce9f..54125ade0ac 100644 --- a/packages/client/lib/commands/ZPOPMIN_COUNT.ts +++ b/packages/client/lib/commands/ZPOPMIN_COUNT.ts @@ -1,12 +1,16 @@ +import { RedisCommandArgument, RedisCommandArguments } from '.'; import { transformArguments as transformZPopMinArguments } from './ZPOPMIN'; export { FIRST_KEY_INDEX } from './ZPOPMIN'; -export function transformArguments(key: string, count: number): Array { +export function transformArguments( + key: RedisCommandArgument, + count: number +): RedisCommandArguments { return [ ...transformZPopMinArguments(key), count.toString() ]; } -export { transformReplySortedStringsSetWithScores as transformReply } from './generic-transformers'; +export { transformSortedSetWithScoresReply as transformReply } from './generic-transformers'; diff --git a/packages/client/lib/commands/ZRANDMEMBER.ts b/packages/client/lib/commands/ZRANDMEMBER.ts index 13bb05598b7..00420872c0c 100644 --- a/packages/client/lib/commands/ZRANDMEMBER.ts +++ b/packages/client/lib/commands/ZRANDMEMBER.ts @@ -1,9 +1,11 @@ +import { RedisCommandArgument, RedisCommandArguments } from '.'; + export const FIRST_KEY_INDEX = 1; export const IS_READ_ONLY = true; -export function transformArguments(key: string): Array { +export function transformArguments(key: RedisCommandArgument): RedisCommandArguments { return ['ZRANDMEMBER', key]; } -export declare function transformReply(): string | null; +export declare function transformReply(): RedisCommandArgument | null; diff --git a/packages/client/lib/commands/ZRANDMEMBER_COUNT.ts b/packages/client/lib/commands/ZRANDMEMBER_COUNT.ts index 1b7b8fea994..3aa91902c62 100644 --- a/packages/client/lib/commands/ZRANDMEMBER_COUNT.ts +++ b/packages/client/lib/commands/ZRANDMEMBER_COUNT.ts @@ -1,12 +1,16 @@ +import { RedisCommandArgument, RedisCommandArguments } from '.'; import { transformArguments as transformZRandMemberArguments } from './ZRANDMEMBER'; export { FIRST_KEY_INDEX, IS_READ_ONLY } from './ZRANDMEMBER'; -export function transformArguments(key: string, count: number): Array { +export function transformArguments( + key: RedisCommandArgument, + count: number +): RedisCommandArguments { return [ ...transformZRandMemberArguments(key), count.toString() ]; } -export declare function transformReply(): Array; +export declare function transformReply(): Array; diff --git a/packages/client/lib/commands/ZRANDMEMBER_COUNT_WITHSCORES.ts b/packages/client/lib/commands/ZRANDMEMBER_COUNT_WITHSCORES.ts index 83ce83ec6cf..cc9d2bc26ee 100644 --- a/packages/client/lib/commands/ZRANDMEMBER_COUNT_WITHSCORES.ts +++ b/packages/client/lib/commands/ZRANDMEMBER_COUNT_WITHSCORES.ts @@ -1,12 +1,13 @@ +import { RedisCommandArguments } from '.'; import { transformArguments as transformZRandMemberCountArguments } from './ZRANDMEMBER_COUNT'; export { FIRST_KEY_INDEX, IS_READ_ONLY } from './ZRANDMEMBER_COUNT'; -export function transformArguments(...args: Parameters): Array { +export function transformArguments(...args: Parameters): RedisCommandArguments { return [ ...transformZRandMemberCountArguments(...args), 'WITHSCORES' ]; } -export { transformReplySortedStringsSetWithScores as transformReply } from './generic-transformers'; +export { transformSortedSetWithScoresReply as transformReply } from './generic-transformers'; diff --git a/packages/client/lib/commands/ZRANGE.ts b/packages/client/lib/commands/ZRANGE.ts index 3d30e0f8dd2..83f09aaa1b0 100644 --- a/packages/client/lib/commands/ZRANGE.ts +++ b/packages/client/lib/commands/ZRANGE.ts @@ -1,4 +1,5 @@ -import { transformArgumentStringNumberInfinity } from './generic-transformers'; +import { RedisCommandArgument, RedisCommandArguments } from '.'; +import { transformStringNumberInfinityArgument } from './generic-transformers'; export const FIRST_KEY_INDEX = 1; @@ -13,12 +14,17 @@ interface ZRangeOptions { }; } -export function transformArguments(key: string, min: string | number, max: string | number, options?: ZRangeOptions): Array { +export function transformArguments( + key: RedisCommandArgument, + min: RedisCommandArgument | number, + max: RedisCommandArgument | number, + options?: ZRangeOptions +): RedisCommandArguments { const args = [ 'ZRANGE', key, - transformArgumentStringNumberInfinity(min), - transformArgumentStringNumberInfinity(max) + transformStringNumberInfinityArgument(min), + transformStringNumberInfinityArgument(max) ]; switch (options?.BY) { @@ -42,4 +48,4 @@ export function transformArguments(key: string, min: string | number, max: strin return args; } -export declare function transformReply(): Array; +export declare function transformReply(): Array; diff --git a/packages/client/lib/commands/ZRANGEBYLEX.ts b/packages/client/lib/commands/ZRANGEBYLEX.ts index 214d796da0a..d6e621a562f 100644 --- a/packages/client/lib/commands/ZRANGEBYLEX.ts +++ b/packages/client/lib/commands/ZRANGEBYLEX.ts @@ -1,5 +1,5 @@ -import { RedisCommandArguments } from '.'; -import { transformArgumentStringNumberInfinity } from './generic-transformers'; +import { RedisCommandArgument, RedisCommandArguments } from '.'; +import { transformStringNumberInfinityArgument } from './generic-transformers'; export const FIRST_KEY_INDEX = 1; @@ -13,16 +13,16 @@ export interface ZRangeByLexOptions { } export function transformArguments( - key: string, - min: number | string, - max: number | string, + key: RedisCommandArgument, + min: RedisCommandArgument, + max: RedisCommandArgument, options?: ZRangeByLexOptions ): RedisCommandArguments { const args = [ 'ZRANGEBYLEX', key, - transformArgumentStringNumberInfinity(min), - transformArgumentStringNumberInfinity(max) + transformStringNumberInfinityArgument(min), + transformStringNumberInfinityArgument(max) ]; if (options?.LIMIT) { @@ -32,4 +32,4 @@ export function transformArguments( return args; } -export declare function transformReply(): Array; +export declare function transformReply(): Array; diff --git a/packages/client/lib/commands/ZRANGEBYSCORE.ts b/packages/client/lib/commands/ZRANGEBYSCORE.ts index f6097fad581..5ab7d7ac727 100644 --- a/packages/client/lib/commands/ZRANGEBYSCORE.ts +++ b/packages/client/lib/commands/ZRANGEBYSCORE.ts @@ -1,5 +1,5 @@ -import { RedisCommandArguments } from '.'; -import { transformArgumentStringNumberInfinity } from './generic-transformers'; +import { RedisCommandArgument, RedisCommandArguments } from '.'; +import { transformStringNumberInfinityArgument } from './generic-transformers'; export const FIRST_KEY_INDEX = 1; @@ -13,16 +13,16 @@ export interface ZRangeByScoreOptions { } export function transformArguments( - key: string, - min: number | string, - max: number | string, + key: RedisCommandArgument, + min: string | number, + max: string | number, options?: ZRangeByScoreOptions ): RedisCommandArguments { const args = [ 'ZRANGEBYSCORE', key, - transformArgumentStringNumberInfinity(min), - transformArgumentStringNumberInfinity(max) + transformStringNumberInfinityArgument(min), + transformStringNumberInfinityArgument(max) ]; if (options?.LIMIT) { @@ -32,4 +32,4 @@ export function transformArguments( return args; } -export declare function transformReply(): Array; +export declare function transformReply(): Array; diff --git a/packages/client/lib/commands/ZRANGEBYSCORE_WITHSCORES.ts b/packages/client/lib/commands/ZRANGEBYSCORE_WITHSCORES.ts index f11ef26115b..c7266f1c062 100644 --- a/packages/client/lib/commands/ZRANGEBYSCORE_WITHSCORES.ts +++ b/packages/client/lib/commands/ZRANGEBYSCORE_WITHSCORES.ts @@ -1,12 +1,12 @@ -import { RedisCommandArguments } from '.'; +import { RedisCommandArgument, RedisCommandArguments } from '.'; import { ZRangeByScoreOptions, transformArguments as transformZRangeByScoreArguments } from './ZRANGEBYSCORE'; export { FIRST_KEY_INDEX, IS_READ_ONLY } from './ZRANGEBYSCORE'; export function transformArguments( - key: string, - min: number | string, - max: number | string, + key: RedisCommandArgument, + min: string | number, + max: string | number, options?: ZRangeByScoreOptions ): RedisCommandArguments { return [ @@ -15,4 +15,4 @@ export function transformArguments( ]; } -export { transformReplySortedStringsSetWithScores as transformReply } from './generic-transformers'; +export { transformSortedSetWithScoresReply as transformReply } from './generic-transformers'; diff --git a/packages/client/lib/commands/ZRANGESTORE.ts b/packages/client/lib/commands/ZRANGESTORE.ts index c76afe61029..28067ceabe0 100644 --- a/packages/client/lib/commands/ZRANGESTORE.ts +++ b/packages/client/lib/commands/ZRANGESTORE.ts @@ -1,4 +1,5 @@ -import { transformArgumentStringNumberInfinity } from './generic-transformers'; +import { RedisCommandArgument, RedisCommandArguments } from '.'; +import { transformStringNumberInfinityArgument } from './generic-transformers'; export const FIRST_KEY_INDEX = 1; @@ -13,18 +14,18 @@ interface ZRangeStoreOptions { } export function transformArguments( - dst: string, - src: string, - min: string | number, - max: string | number, + dst: RedisCommandArgument, + src: RedisCommandArgument, + min: RedisCommandArgument | number, + max: RedisCommandArgument | number, options?: ZRangeStoreOptions -): Array { +): RedisCommandArguments { const args = [ 'ZRANGESTORE', dst, src, - transformArgumentStringNumberInfinity(min), - transformArgumentStringNumberInfinity(max) + transformStringNumberInfinityArgument(min), + transformStringNumberInfinityArgument(max) ]; switch (options?.BY) { diff --git a/packages/client/lib/commands/ZRANGE_WITHSCORES.ts b/packages/client/lib/commands/ZRANGE_WITHSCORES.ts index fbecdfdacb1..23ea4d6337c 100644 --- a/packages/client/lib/commands/ZRANGE_WITHSCORES.ts +++ b/packages/client/lib/commands/ZRANGE_WITHSCORES.ts @@ -1,12 +1,13 @@ +import { RedisCommandArguments } from '.'; import { transformArguments as transformZRangeArguments } from './ZRANGE'; export { FIRST_KEY_INDEX, IS_READ_ONLY } from './ZRANGE'; -export function transformArguments(...args: Parameters): Array { +export function transformArguments(...args: Parameters): RedisCommandArguments { return [ ...transformZRangeArguments(...args), 'WITHSCORES' ]; } -export { transformReplySortedStringsSetWithScores as transformReply } from './generic-transformers'; +export { transformSortedSetWithScoresReply as transformReply } from './generic-transformers'; diff --git a/packages/client/lib/commands/ZRANK.ts b/packages/client/lib/commands/ZRANK.ts index e060ccf6f8c..33439ea4b55 100644 --- a/packages/client/lib/commands/ZRANK.ts +++ b/packages/client/lib/commands/ZRANK.ts @@ -1,8 +1,13 @@ +import { RedisCommandArgument, RedisCommandArguments } from '.'; + export const FIRST_KEY_INDEX = 1; export const IS_READ_ONLY = true; -export function transformArguments(key: string, member: string): Array { +export function transformArguments( + key: RedisCommandArgument, + member: RedisCommandArgument +): RedisCommandArguments { return ['ZRANK', key, member]; } diff --git a/packages/client/lib/commands/ZREM.ts b/packages/client/lib/commands/ZREM.ts index 332289b3fdb..7ab92c4a78f 100644 --- a/packages/client/lib/commands/ZREM.ts +++ b/packages/client/lib/commands/ZREM.ts @@ -1,9 +1,12 @@ -import { RedisCommandArguments } from '.'; +import { RedisCommandArgument, RedisCommandArguments } from '.'; import { pushVerdictArguments } from './generic-transformers'; export const FIRST_KEY_INDEX = 1; -export function transformArguments(key: string, member: string | Array): RedisCommandArguments { +export function transformArguments( + key: RedisCommandArgument, + member: RedisCommandArgument | Array +): RedisCommandArguments { return pushVerdictArguments(['ZREM', key], member); } diff --git a/packages/client/lib/commands/ZREMRANGEBYLEX.ts b/packages/client/lib/commands/ZREMRANGEBYLEX.ts index 1f17d6b986e..f1f3908f538 100644 --- a/packages/client/lib/commands/ZREMRANGEBYLEX.ts +++ b/packages/client/lib/commands/ZREMRANGEBYLEX.ts @@ -1,7 +1,19 @@ +import { RedisCommandArgument, RedisCommandArguments } from '.'; +import { transformStringNumberInfinityArgument } from './generic-transformers'; + export const FIRST_KEY_INDEX = 1; -export function transformArguments(key: string, min: string, max: string): Array { - return ['ZREMRANGEBYLEX', key, min, max]; +export function transformArguments( + key: RedisCommandArgument, + min: RedisCommandArgument | number, + max: RedisCommandArgument | number +): RedisCommandArguments { + return [ + 'ZREMRANGEBYLEX', + key, + transformStringNumberInfinityArgument(min), + transformStringNumberInfinityArgument(max) + ]; } export declare function transformReply(): number; diff --git a/packages/client/lib/commands/ZREMRANGEBYRANK.ts b/packages/client/lib/commands/ZREMRANGEBYRANK.ts index 550a56e8a85..c50d06e3bf6 100644 --- a/packages/client/lib/commands/ZREMRANGEBYRANK.ts +++ b/packages/client/lib/commands/ZREMRANGEBYRANK.ts @@ -1,6 +1,12 @@ +import { RedisCommandArgument, RedisCommandArguments } from '.'; + export const FIRST_KEY_INDEX = 1; -export function transformArguments(key: string, start: number, stop: number): Array { +export function transformArguments( + key: RedisCommandArgument, + start: number, + stop: number +): RedisCommandArguments { return ['ZREMRANGEBYRANK', key, start.toString(), stop.toString()]; } diff --git a/packages/client/lib/commands/ZREMRANGEBYSCORE.ts b/packages/client/lib/commands/ZREMRANGEBYSCORE.ts index e0186fcb64e..12d1eff811e 100644 --- a/packages/client/lib/commands/ZREMRANGEBYSCORE.ts +++ b/packages/client/lib/commands/ZREMRANGEBYSCORE.ts @@ -1,7 +1,19 @@ +import { RedisCommandArgument, RedisCommandArguments } from '.'; +import { transformStringNumberInfinityArgument } from './generic-transformers'; + export const FIRST_KEY_INDEX = 1; -export function transformArguments(key: string, min: number, max: number): Array { - return ['ZREMRANGEBYSCORE', key, min.toString(), max.toString()]; +export function transformArguments( + key: RedisCommandArgument, + min: RedisCommandArgument | number, + max: RedisCommandArgument | number, +): RedisCommandArguments { + return [ + 'ZREMRANGEBYSCORE', + key, + transformStringNumberInfinityArgument(min), + transformStringNumberInfinityArgument(max) + ]; } export declare function transformReply(): number; diff --git a/packages/client/lib/commands/ZREVRANK.ts b/packages/client/lib/commands/ZREVRANK.ts index 808d9e45dc3..b88936c0c92 100644 --- a/packages/client/lib/commands/ZREVRANK.ts +++ b/packages/client/lib/commands/ZREVRANK.ts @@ -1,8 +1,13 @@ +import { RedisCommandArgument, RedisCommandArguments } from '.'; + export const FIRST_KEY_INDEX = 1; export const IS_READ_ONLY = true; -export function transformArguments(key: string, member: string): Array { +export function transformArguments( + key: RedisCommandArgument, + member: RedisCommandArgument +): RedisCommandArguments { return ['ZREVRANK', key, member]; } diff --git a/packages/client/lib/commands/ZSCAN.ts b/packages/client/lib/commands/ZSCAN.ts index 61774c10423..f6fa17c2d4e 100644 --- a/packages/client/lib/commands/ZSCAN.ts +++ b/packages/client/lib/commands/ZSCAN.ts @@ -1,27 +1,34 @@ -import { ScanOptions, transformReplyNumberInfinity, pushScanArguments, ZMember } from './generic-transformers'; +import { RedisCommandArgument, RedisCommandArguments } from '.'; +import { ScanOptions, transformNumberInfinityReply, pushScanArguments, ZMember } from './generic-transformers'; export const FIRST_KEY_INDEX = 1; export const IS_READ_ONLY = true; -export function transformArguments(key: string, cursor: number, options?: ScanOptions): Array { +export function transformArguments( + key: RedisCommandArgument, + cursor: number, + options?: ScanOptions +): RedisCommandArguments { return pushScanArguments([ 'ZSCAN', key ], cursor, options); } +type ZScanRawReply = [RedisCommandArgument, Array]; + interface ZScanReply { cursor: number; - members: Array>; + members: Array; } -export function transformReply([cursor, rawMembers]: [string, Array]): ZScanReply { - const parsedMembers: Array> = []; +export function transformReply([cursor, rawMembers]: ZScanRawReply): ZScanReply { + const parsedMembers: Array = []; for (let i = 0; i < rawMembers.length; i += 2) { parsedMembers.push({ value: rawMembers[i], - score: transformReplyNumberInfinity(rawMembers[i + 1]) + score: transformNumberInfinityReply(rawMembers[i + 1]) }); } diff --git a/packages/client/lib/commands/ZSCORE.ts b/packages/client/lib/commands/ZSCORE.ts index 2bdecaf432a..118abc10850 100644 --- a/packages/client/lib/commands/ZSCORE.ts +++ b/packages/client/lib/commands/ZSCORE.ts @@ -1,9 +1,14 @@ +import { RedisCommandArgument, RedisCommandArguments } from '.'; + export const FIRST_KEY_INDEX = 1; export const IS_READ_ONLY = true; -export function transformArguments(key: string, member: string): Array { +export function transformArguments( + key: RedisCommandArgument, + member: RedisCommandArgument +): RedisCommandArguments { return ['ZSCORE', key, member]; } -export { transformReplyNumberInfinityNull as transformReply } from './generic-transformers'; +export { transformNumberInfinityNullReply as transformReply } from './generic-transformers'; diff --git a/packages/client/lib/commands/ZUNION.ts b/packages/client/lib/commands/ZUNION.ts index 2163978470c..f329348cc8b 100644 --- a/packages/client/lib/commands/ZUNION.ts +++ b/packages/client/lib/commands/ZUNION.ts @@ -1,4 +1,4 @@ -import { RedisCommandArguments } from '.'; +import { RedisCommandArgument, RedisCommandArguments } from '.'; import { pushVerdictArgument } from './generic-transformers'; export const FIRST_KEY_INDEX = 2; @@ -10,7 +10,10 @@ interface ZUnionOptions { AGGREGATE?: 'SUM' | 'MIN' | 'MAX'; } -export function transformArguments(keys: Array | string, options?: ZUnionOptions): RedisCommandArguments { +export function transformArguments( + keys: Array | RedisCommandArgument, + options?: ZUnionOptions +): RedisCommandArguments { const args = pushVerdictArgument(['ZUNION'], keys); if (options?.WEIGHTS) { @@ -24,4 +27,4 @@ export function transformArguments(keys: Array | string, options?: ZUnio return args; } -export declare function transformReply(): Array; +export declare function transformReply(): Array; diff --git a/packages/client/lib/commands/ZUNIONSTORE.ts b/packages/client/lib/commands/ZUNIONSTORE.ts index 406f0430c52..2a42e21bc87 100644 --- a/packages/client/lib/commands/ZUNIONSTORE.ts +++ b/packages/client/lib/commands/ZUNIONSTORE.ts @@ -1,4 +1,4 @@ -import { RedisCommandArguments } from '.'; +import { RedisCommandArgument, RedisCommandArguments } from '.'; import { pushVerdictArgument } from './generic-transformers'; export const FIRST_KEY_INDEX = 1; @@ -8,7 +8,11 @@ interface ZUnionOptions { AGGREGATE?: 'SUM' | 'MIN' | 'MAX'; } -export function transformArguments(destination: string, keys: Array | string, options?: ZUnionOptions): RedisCommandArguments { +export function transformArguments( + destination: RedisCommandArgument, + keys: Array | RedisCommandArgument, + options?: ZUnionOptions +): RedisCommandArguments { const args = pushVerdictArgument(['ZUNIONSTORE', destination], keys); if (options?.WEIGHTS) { diff --git a/packages/client/lib/commands/ZUNION_WITHSCORES.ts b/packages/client/lib/commands/ZUNION_WITHSCORES.ts index 5896c89ea1b..168cc929ac8 100644 --- a/packages/client/lib/commands/ZUNION_WITHSCORES.ts +++ b/packages/client/lib/commands/ZUNION_WITHSCORES.ts @@ -10,4 +10,4 @@ export function transformArguments(...args: Parameters { - describe('transformReplyBoolean', () => { + describe('transformBooleanReply', () => { it('0', () => { assert.equal( - transformReplyBoolean(0), + transformBooleanReply(0), false ); }); it('1', () => { assert.equal( - transformReplyBoolean(1), + transformBooleanReply(1), true ); }); }); - describe('transformReplyBooleanArray', () => { + describe('transformBooleanArrayReply', () => { it('empty array', () => { assert.deepEqual( - transformReplyBooleanArray([]), + transformBooleanArrayReply([]), [] ); }); it('0, 1', () => { assert.deepEqual( - transformReplyBooleanArray([0, 1]), + transformBooleanArrayReply([0, 1]), [false, true] ); }); @@ -102,103 +96,87 @@ describe('Generic Transformers', () => { }); }); - describe('transformReplyNumberInfinity', () => { + describe('transformNumberInfinityReply', () => { it('0.5', () => { assert.equal( - transformReplyNumberInfinity('0.5'), + transformNumberInfinityReply('0.5'), 0.5 ); }); it('+inf', () => { assert.equal( - transformReplyNumberInfinity('+inf'), + transformNumberInfinityReply('+inf'), Infinity ); }); it('-inf', () => { assert.equal( - transformReplyNumberInfinity('-inf'), + transformNumberInfinityReply('-inf'), -Infinity ); }); }); - describe('transformReplyNumberInfinityArray', () => { - it('empty array', () => { - assert.deepEqual( - transformReplyNumberInfinityArray([]), - [] - ); - }); - - it('0.5, +inf, -inf', () => { - assert.deepEqual( - transformReplyNumberInfinityArray(['0.5', '+inf', '-inf']), - [0.5, Infinity, -Infinity] - ); - }); - }); - - describe('transformReplyNumberInfinityNull', () => { + describe('transformNumberInfinityNullReply', () => { it('null', () => { assert.equal( - transformReplyNumberInfinityNull(null), + transformNumberInfinityNullReply(null), null ); }); it('1', () => { assert.equal( - transformReplyNumberInfinityNull('1'), + transformNumberInfinityNullReply('1'), 1 ); }); }); - describe('transformArgumentNumberInfinity', () => { + describe('transformNumberInfinityArgument', () => { it('0.5', () => { assert.equal( - transformArgumentNumberInfinity(0.5), + transformNumberInfinityArgument(0.5), '0.5' ); }); it('Infinity', () => { assert.equal( - transformArgumentNumberInfinity(Infinity), + transformNumberInfinityArgument(Infinity), '+inf' ); }); it('-Infinity', () => { assert.equal( - transformArgumentNumberInfinity(-Infinity), + transformNumberInfinityArgument(-Infinity), '-inf' ); }); }); - describe('transformArgumentStringNumberInfinity', () => { + describe('transformStringNumberInfinityArgument', () => { it("'0.5'", () => { assert.equal( - transformArgumentStringNumberInfinity('0.5'), + transformStringNumberInfinityArgument('0.5'), '0.5' ); }); it('0.5', () => { assert.equal( - transformArgumentStringNumberInfinity(0.5), + transformStringNumberInfinityArgument(0.5), '0.5' ); }); }); - it('transformReplyStringTuples', () => { + it('transformTuplesReply', () => { assert.deepEqual( - transformReplyStringTuples(['key1', 'value1', 'key2', 'value2']), + transformTuplesReply(['key1', 'value1', 'key2', 'value2']), Object.create(null, { key1: { value: 'value1', @@ -214,27 +192,9 @@ describe('Generic Transformers', () => { ); }); - it('transformReplyBufferTuples', () => { - assert.deepEqual( - transformReplyBufferTuples([Buffer.from('key1'), Buffer.from('value1'), Buffer.from('key2'), Buffer.from('value2')]), - Object.create(null, { - key1: { - value: Buffer.from('value1'), - configurable: true, - enumerable: true - }, - key2: { - value: Buffer.from('value2'), - configurable: true, - enumerable: true - } - }) - ); - }); - - it('transformReplyStreamStringMessages', () => { + it('transformStreamMessagesReply', () => { assert.deepEqual( - transformReplyStreamStringMessages([['0-0', ['0key', '0value']], ['1-0', ['1key', '1value']]]), + transformStreamMessagesReply([['0-0', ['0key', '0value']], ['1-0', ['1key', '1value']]]), [{ id: '0-0', message: Object.create(null, { @@ -257,45 +217,17 @@ describe('Generic Transformers', () => { ); }); - it('transformReplyStreamBufferMessages', () => { - assert.deepEqual( - transformReplyStreamBufferMessages([ - [Buffer.from('0-0'), [Buffer.from('0key'), Buffer.from('0value')]], - [Buffer.from('1-0'), [Buffer.from('1key'), Buffer.from('1value')]] - ]), - [{ - id: Buffer.from('0-0'), - message: Object.create(null, { - '0key': { - value: Buffer.from('0value'), - configurable: true, - enumerable: true - } - }) - }, { - id: Buffer.from('1-0'), - message: Object.create(null, { - '1key': { - value: Buffer.from('1value'), - configurable: true, - enumerable: true - } - }) - }] - ); - }); - - describe('transformReplyStreamsStringMessages', () => { + describe('transformStreamsMessagesReply', () => { it('null', () => { assert.equal( - transformReplyStreamsStringMessages(null), + transformStreamsMessagesReply(null), null ); }); it('with messages', () => { assert.deepEqual( - transformReplyStreamsStringMessages([['stream1', [['0-1', ['11key', '11value']], ['1-1', ['12key', '12value']]]], ['stream2', [['0-2', ['2key1', '2value1', '2key2', '2value2']]]]]), + transformStreamsMessagesReply([['stream1', [['0-1', ['11key', '11value']], ['1-1', ['12key', '12value']]]], ['stream2', [['0-2', ['2key1', '2value1', '2key2', '2value2']]]]]), [{ name: 'stream1', messages: [{ @@ -339,77 +271,9 @@ describe('Generic Transformers', () => { }); }); - describe('transformReplyStreamsBufferMessages', () => { - it('null', () => { - assert.equal( - transformReplyStreamsBufferMessages(null), - null - ); - }); - - it('with messages', () => { - assert.deepEqual( - transformReplyStreamsBufferMessages([ - [ - Buffer.from('stream1'), - [ - [Buffer.from('0-1'), [Buffer.from('11key'), Buffer.from('11value')]], - [Buffer.from('1-1'), [Buffer.from('12key'), Buffer.from('12value')]] - ] - ], - [ - Buffer.from('stream2'), - [ - [Buffer.from('0-2'), [Buffer.from('2key1'), Buffer.from('2value1'), Buffer.from('2key2'), Buffer.from('2value2')]] - ] - ] - ]), - [{ - name: Buffer.from('stream1'), - messages: [{ - id: Buffer.from('0-1'), - message: Object.create(null, { - '11key': { - value: Buffer.from('11value'), - configurable: true, - enumerable: true - } - }) - }, { - id: Buffer.from('1-1'), - message: Object.create(null, { - '12key': { - value: Buffer.from('12value'), - configurable: true, - enumerable: true - } - }) - }] - }, { - name: Buffer.from('stream2'), - messages: [{ - id: Buffer.from('0-2'), - message: Object.create(null, { - '2key1': { - value: Buffer.from('2value1'), - configurable: true, - enumerable: true - }, - '2key2': { - value: Buffer.from('2value2'), - configurable: true, - enumerable: true - } - }) - }] - }] - ); - }); - }); - - it('transformReplySortedStringsSetWithScores', () => { + it('transformSortedSetWithScoresReply', () => { assert.deepEqual( - transformReplySortedStringsSetWithScores(['member1', '0.5', 'member2', '+inf', 'member3', '-inf']), + transformSortedSetWithScoresReply(['member1', '0.5', 'member2', '+inf', 'member3', '-inf']), [{ value: 'member1', score: 0.5 @@ -423,29 +287,6 @@ describe('Generic Transformers', () => { ); }); - it('transformReplySortedBuffersSetWithScores', () => { - assert.deepEqual( - transformReplySortedBuffersSetWithScores([ - Buffer.from('member1'), - Buffer.from('0.5'), - Buffer.from('member2'), - Buffer.from('+inf'), - Buffer.from('member3'), - Buffer.from('-inf') - ]), - [{ - value: Buffer.from('member1'), - score: 0.5 - }, { - value: Buffer.from('member2'), - score: Infinity - }, { - value: Buffer.from('member3'), - score: -Infinity - }] - ); - }); - describe('pushGeoCountArgument', () => { it('undefined', () => { assert.deepEqual( @@ -721,29 +562,6 @@ describe('Generic Transformers', () => { }); }); - describe('pushStringTuplesArguments', () => { - it("['key1', 'value1', 'key2', 'value2']", () => { - assert.deepEqual( - pushStringTuplesArguments([], ['key1', 'value1', 'key2', 'value2']), - ['key1', 'value1', 'key2', 'value2'] - ); - }); - - it("[['key1', 'value1'], ['key2', 'value2']]", () => { - assert.deepEqual( - pushStringTuplesArguments([], [['key1', 'value1'], ['key2', 'value2']]), - ['key1', 'value1', 'key2', 'value2'] - ); - }); - - it("{key1: 'value1'. key2: 'value2'}", () => { - assert.deepEqual( - pushStringTuplesArguments([], { key1: 'value1', key2: 'value2' }), - ['key1', 'value1', 'key2', 'value2'] - ); - }); - }); - describe('pushVerdictArguments', () => { it('string', () => { assert.deepEqual( diff --git a/packages/client/lib/commands/generic-transformers.ts b/packages/client/lib/commands/generic-transformers.ts index 10729132fa0..0799476ae64 100644 --- a/packages/client/lib/commands/generic-transformers.ts +++ b/packages/client/lib/commands/generic-transformers.ts @@ -1,11 +1,11 @@ import { RedisCommandArgument, RedisCommandArguments } from '.'; -export function transformReplyBoolean(reply: number): boolean { +export function transformBooleanReply(reply: number): boolean { return reply === 1; } -export function transformReplyBooleanArray(reply: Array): Array { - return reply.map(transformReplyBoolean); +export function transformBooleanArrayReply(reply: Array): Array { + return reply.map(transformBooleanReply); } export type BitValue = 0 | 1; @@ -15,7 +15,11 @@ export interface ScanOptions { COUNT?: number; } -export function pushScanArguments(args: Array, cursor: number, options?: ScanOptions): Array { +export function pushScanArguments( + args: RedisCommandArguments, + cursor: number, + options?: ScanOptions +): RedisCommandArguments { args.push(cursor.toString()); if (options?.MATCH) { @@ -29,8 +33,8 @@ export function pushScanArguments(args: Array, cursor: number, options?: return args; } -export function transformReplyNumberInfinity(reply: string): number { - switch (reply) { +export function transformNumberInfinityReply(reply: RedisCommandArgument): number { + switch (reply.toString()) { case '+inf': return Infinity; @@ -42,21 +46,17 @@ export function transformReplyNumberInfinity(reply: string): number { } } -export function transformReplyNumberInfinityArray(reply: Array): Array { - return reply.map(transformReplyNumberInfinity); -} - -export function transformReplyNumberInfinityNull(reply: string | null): number | null { +export function transformNumberInfinityNullReply(reply: RedisCommandArgument | null): number | null { if (reply === null) return null; - return transformReplyNumberInfinity(reply); + return transformNumberInfinityReply(reply); } -export function transformReplyNumberInfinityNullArray(reply: Array): Array { - return reply.map(transformReplyNumberInfinityNull); +export function transformNumberInfinityNullArrayReply(reply: Array): Array { + return reply.map(transformNumberInfinityNullReply); } -export function transformArgumentNumberInfinity(num: number): string { +export function transformNumberInfinityArgument(num: number): string { switch (num) { case Infinity: return '+inf'; @@ -69,24 +69,15 @@ export function transformArgumentNumberInfinity(num: number): string { } } -export function transformArgumentStringNumberInfinity(num: string | number): string { - if (typeof num === 'string') return num; +export function transformStringNumberInfinityArgument(num: RedisCommandArgument | number): RedisCommandArgument { + if (typeof num !== 'number') return num; - return transformArgumentNumberInfinity(num); + return transformNumberInfinityArgument(num); } -export function transformReplyStringTuples(reply: Array): Record { - const message = Object.create(null); - - for (let i = 0; i < reply.length; i += 2) { - message[reply[i]] = reply[i + 1]; - } - - return message; -} - - -export function transformReplyBufferTuples(reply: Array): Record { +export function transformTuplesReply( + reply: Array +): Record { const message = Object.create(null); for (let i = 0; i < reply.length; i += 2) { @@ -96,99 +87,63 @@ export function transformReplyBufferTuples(reply: Array): Record; +export interface StreamMessageReply { + id: RedisCommandArgument; + message: Record; } -export type StreamStringsMessagesReply = Array; +export type StreamMessagesReply = Array; -export function transformReplyStreamStringMessages(reply: Array): StreamStringsMessagesReply { +export function transformStreamMessagesReply(reply: Array): StreamMessagesReply { const messages = []; for (const [id, message] of reply) { messages.push({ id, - message: transformReplyStringTuples(message) + message: transformTuplesReply(message) }); } return messages; } -interface StreamBufferMessageReply { - id: Buffer; - message: Record; -} - -export type StreamBufferMessagesReply = Array; - -export function transformReplyStreamBufferMessages(reply: Array): StreamBufferMessagesReply { - const messages = []; - - for (const [id, message] of reply) { - messages.push({ - id, - message: transformReplyBufferTuples(message) - }); - } - - return messages; -} - -export type StreamsStringMessagesReply = Array<{ - name: string; - messages: StreamStringsMessagesReply; +export type StreamsMessagesReply = Array<{ + name: RedisCommandArgument; + messages: StreamMessagesReply; }> | null; -export function transformReplyStreamsStringMessages(reply: Array | null): StreamsStringMessagesReply | null { +export function transformStreamsMessagesReply(reply: Array | null): StreamsMessagesReply | null { if (reply === null) return null; return reply.map(([name, rawMessages]) => ({ name, - messages: transformReplyStreamStringMessages(rawMessages) + messages: transformStreamMessagesReply(rawMessages) })); } -export type StreamsBufferMessagesReply = Array<{ - name: Buffer; - messages: StreamBufferMessagesReply; -}> | null; - -export function transformReplyStreamsBufferMessages(reply: Array | null): StreamsBufferMessagesReply | null { - if (reply === null) return null; - - return reply.map(([name, rawMessages]) => ({ - name, - messages: transformReplyStreamBufferMessages(rawMessages) - })); -} - -export interface ZMember { +export interface ZMember { score: number; - value: T; + value: RedisCommandArgument; } -export function transformReplySortedStringsSetWithScores(reply: Array): Array> { - const members = []; - - for (let i = 0; i < reply.length; i += 2) { - members.push({ - value: reply[i], - score: transformReplyNumberInfinity(reply[i + 1]) - }); - } +export function transformSortedSetMemberNullReply( + reply: [RedisCommandArgument, RedisCommandArgument] | [] +): ZMember | null { + if (!reply.length) return null; - return members; + return { + value: reply[0], + score: transformNumberInfinityReply(reply[1]) + }; } -export function transformReplySortedBuffersSetWithScores(reply: Array): Array> { +export function transformSortedSetWithScoresReply(reply: Array): Array { const members = []; for (let i = 0; i < reply.length; i += 2) { members.push({ value: reply[i], - score: transformReplyNumberInfinity(reply[i + 1].toString()) + score: transformNumberInfinityReply(reply[i + 1]) }); } @@ -200,7 +155,10 @@ type GeoCountArgument = number | { ANY?: true }; -export function pushGeoCountArgument(args: Array, count: GeoCountArgument | undefined): Array { +export function pushGeoCountArgument( + args: RedisCommandArguments, + count: GeoCountArgument | undefined +): RedisCommandArguments { if (typeof count === 'number') { args.push('COUNT', count.toString()); } else if (count) { @@ -244,12 +202,12 @@ export interface GeoSearchOptions { } export function pushGeoSearchArguments( - args: Array, - key: string, + args: RedisCommandArguments, + key: RedisCommandArgument, from: GeoSearchFrom, by: GeoSearchBy, options?: GeoSearchOptions -): Array { +): RedisCommandArguments { args.push(key); if (typeof from === 'string') { @@ -354,20 +312,6 @@ export function pushEvalArguments(args: Array, options?: EvalOptions): A return args; } -export type StringTuplesArguments = Array<[string, string]> | Array | Record; - -export function pushStringTuplesArguments(args: Array, tuples: StringTuplesArguments): Array { - if (Array.isArray(tuples)) { - args.push(...tuples.flat()); - } else { - for (const key of Object.keys(tuples)) { - args.push(key, tuples[key]); - } - } - - return args; -} - export function pushVerdictArguments(args: RedisCommandArguments, value: RedisCommandArgument | Array): RedisCommandArguments { if (Array.isArray(value)) { args.push(...value); @@ -378,17 +322,24 @@ export function pushVerdictArguments(args: RedisCommandArguments, value: RedisCo return args; } -export function pushVerdictArgument(args: RedisCommandArguments, value: string | Array): RedisCommandArguments { - if (typeof value === 'string') { - args.push('1', value); - } else { +export function pushVerdictArgument( + args: RedisCommandArguments, + value: RedisCommandArgument | Array +): RedisCommandArguments { + if (Array.isArray(value)) { args.push(value.length.toString(), ...value); + } else { + args.push('1', value); } return args; } -export function pushOptionalVerdictArgument(args: RedisCommandArguments, name: string, value: undefined | string | Array): RedisCommandArguments { +export function pushOptionalVerdictArgument( + args: RedisCommandArguments, + name: RedisCommandArgument, + value: undefined | RedisCommandArgument | Array +): RedisCommandArguments { if (value === undefined) return args; args.push(name); diff --git a/packages/client/lib/commands/index.ts b/packages/client/lib/commands/index.ts index 03aaf93fb17..c589770a947 100644 --- a/packages/client/lib/commands/index.ts +++ b/packages/client/lib/commands/index.ts @@ -1,6 +1,10 @@ import { RedisScriptConfig, SHA1 } from '../lua-script'; -export type RedisCommandRawReply = string | number | Buffer | Array | null | undefined; + +// https://github.com/Microsoft/TypeScript/issues/3496#issuecomment-128553540 +// eslint-disable-next-line @typescript-eslint/no-empty-interface +interface RedisCommandRawReplyArray extends Array {} +export type RedisCommandRawReply = string | number | Buffer | null | undefined | RedisCommandRawReplyArray; export type RedisCommandArgument = string | Buffer; @@ -10,8 +14,7 @@ export interface RedisCommand { FIRST_KEY_INDEX?: number | ((...args: Array) => RedisCommandArgument); IS_READ_ONLY?: boolean; transformArguments(this: void, ...args: Array): RedisCommandArguments; - BUFFER_MODE?: boolean; - transformReply?(this: void, reply: RedisCommandRawReply, preserved?: unknown): any; + transformReply?(this: void, reply: any, preserved?: any): any; } export type RedisCommandReply = C['transformReply'] extends (...args: any) => infer T ? T : RedisCommandRawReply; diff --git a/packages/client/lib/lua-script.ts b/packages/client/lib/lua-script.ts index 57de985733c..8a0481364c4 100644 --- a/packages/client/lib/lua-script.ts +++ b/packages/client/lib/lua-script.ts @@ -10,7 +10,7 @@ export interface SHA1 { SHA1: string; } -export function defineScript(script: TScript): TScript & SHA1 { +export function defineScript(script: S): S & SHA1 { return { ...script, SHA1: scriptSha1(script.SCRIPT) diff --git a/packages/client/package.json b/packages/client/package.json index 8916ec1a57d..25afddbaaac 100644 --- a/packages/client/package.json +++ b/packages/client/package.json @@ -4,6 +4,9 @@ "license": "MIT", "main": "./dist/index.js", "types": "./dist/index.d.ts", + "files": [ + "dist/" + ], "scripts": { "test": "nyc -r text-summary -r lcov mocha -r source-map-support/register -r ts-node/register './lib/**/*.spec.ts'", "build": "tsc", @@ -19,20 +22,20 @@ "devDependencies": { "@istanbuljs/nyc-config-typescript": "^1.0.2", "@node-redis/test-utils": "*", - "@types/node": "^16.11.12", + "@types/node": "^17.0.1", "@types/redis-parser": "^3.0.0", "@types/sinon": "^10.0.6", "@types/yallist": "^4.0.1", - "@typescript-eslint/eslint-plugin": "^5.7.0", - "@typescript-eslint/parser": "^5.7.0", - "eslint": "^8.4.1", + "@typescript-eslint/eslint-plugin": "^5.8.0", + "@typescript-eslint/parser": "^5.8.0", + "eslint": "^8.5.0", "nyc": "^15.1.0", "release-it": "^14.11.8", "sinon": "^12.0.1", "source-map-support": "^0.5.21", "ts-node": "^10.4.0", "typedoc": "^0.22.10", - "typescript": "^4.5.3" + "typescript": "^4.5.4" }, "engines": { "node": ">=12" diff --git a/packages/json/lib/commands/RESP.ts b/packages/json/lib/commands/RESP.ts index 2b56bf1f059..fcf54cd3537 100644 --- a/packages/json/lib/commands/RESP.ts +++ b/packages/json/lib/commands/RESP.ts @@ -12,4 +12,4 @@ export function transformArguments(key: string, path?: string): Array { type RESPReply = Array; -export declare function transfromReply(): RESPReply; +export declare function transformReply(): RESPReply; diff --git a/packages/json/lib/commands/index.ts b/packages/json/lib/commands/index.ts index a3c561addcc..f898fde584e 100644 --- a/packages/json/lib/commands/index.ts +++ b/packages/json/lib/commands/index.ts @@ -62,9 +62,14 @@ export default { type: TYPE }; -// using two "objects" and not `Record` cause of: -// https://github.com/microsoft/TypeScript/issues/14174 -export type RedisJSON = null | boolean | number | string | Date | Array | { [key: string]: RedisJSON } | { [key: number]: RedisJSON }; +// https://github.com/Microsoft/TypeScript/issues/3496#issuecomment-128553540 +// eslint-disable-next-line @typescript-eslint/no-empty-interface +interface RedisJSONArray extends Array {} +interface RedisJSONObject { + [key: string]: RedisJSON; + [key: number]: RedisJSON; +} +export type RedisJSON = null | boolean | number | string | Date | RedisJSONArray | RedisJSONObject; export function transformRedisJsonArgument(json: RedisJSON): string { return JSON.stringify(json); diff --git a/packages/json/package.json b/packages/json/package.json index 57e5a2357c5..445a95e9e30 100644 --- a/packages/json/package.json +++ b/packages/json/package.json @@ -4,6 +4,9 @@ "license": "MIT", "main": "./dist/index.js", "types": "./dist/index.d.ts", + "files": [ + "dist/" + ], "scripts": { "test": "nyc -r text-summary -r lcov mocha -r source-map-support/register -r ts-node/register './lib/**/*.spec.ts'", "build": "tsc", @@ -15,12 +18,12 @@ "devDependencies": { "@istanbuljs/nyc-config-typescript": "^1.0.2", "@node-redis/test-utils": "*", - "@types/node": "^16.11.12", + "@types/node": "^17.0.1", "nyc": "^15.1.0", "release-it": "^14.11.8", "source-map-support": "^0.5.21", "ts-node": "^10.4.0", "typedoc": "^0.22.10", - "typescript": "^4.5.3" + "typescript": "^4.5.4" } } diff --git a/packages/search/lib/commands/AGGREGATE.ts b/packages/search/lib/commands/AGGREGATE.ts index f305fbc6cf2..84a8f9a55a6 100644 --- a/packages/search/lib/commands/AGGREGATE.ts +++ b/packages/search/lib/commands/AGGREGATE.ts @@ -1,6 +1,6 @@ -import { RedisCommandArguments } from '@node-redis/client/dist/lib/commands'; -import { pushVerdictArgument, transformReplyStringTuples } from '@node-redis/client/dist/lib/commands/generic-transformers'; -import { AggregateReply, PropertyName, pushArgumentsWithLength, pushSortByArguments, SortByProperty } from '.'; +import { RedisCommandArgument, RedisCommandArguments } from '@node-redis/client/dist/lib/commands'; +import { pushVerdictArgument, transformTuplesReply } from '@node-redis/client/dist/lib/commands/generic-transformers'; +import { PropertyName, pushArgumentsWithLength, pushSortByArguments, SortByProperty } from '.'; export enum AggregateSteps { GROUPBY = 'GROUPBY', @@ -273,14 +273,19 @@ function pushGroupByReducer(args: RedisCommandArguments, reducer: GroupByReducer export type AggregateRawReply = [ total: number, - ...results: Array> + ...results: Array> ]; +export interface AggregateReply { + total: number; + results: Array>; +} + export function transformReply(rawReply: AggregateRawReply): AggregateReply { - const results: Array> = []; + const results: Array> = []; for (let i = 1; i < rawReply.length; i++) { results.push( - transformReplyStringTuples(rawReply[i] as Array) + transformTuplesReply(rawReply[i] as Array) ); } diff --git a/packages/search/lib/commands/SEARCH.ts b/packages/search/lib/commands/SEARCH.ts index 3a71ec26ba6..efbe24e1512 100644 --- a/packages/search/lib/commands/SEARCH.ts +++ b/packages/search/lib/commands/SEARCH.ts @@ -1,5 +1,5 @@ import { RedisCommandArguments } from '@node-redis/client/dist/lib/commands'; -import { transformReplyStringTuples } from '@node-redis/client/dist/lib/commands/generic-transformers'; +import { transformTuplesReply } from '@node-redis/client/dist/lib/commands/generic-transformers'; import { pushSearchOptions, RedisSearchLanguages, PropertyName, SortByProperty, SearchReply } from '.'; export const FIRST_KEY_INDEX = 1; @@ -76,7 +76,7 @@ export function transformReply(reply: SearchRawReply): SearchReply { id: reply[i], value: tuples.length === 2 && tuples[0] === '$' ? JSON.parse(tuples[1]) : - transformReplyStringTuples(tuples) + transformTuplesReply(tuples) }); } diff --git a/packages/search/lib/commands/SUGDEL.ts b/packages/search/lib/commands/SUGDEL.ts index 43f4744c00d..c6b236f4963 100644 --- a/packages/search/lib/commands/SUGDEL.ts +++ b/packages/search/lib/commands/SUGDEL.ts @@ -2,4 +2,4 @@ export function transformArguments(key: string, string: string): Array { return ['FT.SUGDEL', key, string]; } -export { transformReplyBoolean as transformReply } from '@node-redis/client/dist/lib/commands/generic-transformers'; +export { transformBooleanReply as transformReply } from '@node-redis/client/dist/lib/commands/generic-transformers'; diff --git a/packages/search/lib/commands/index.ts b/packages/search/lib/commands/index.ts index 9b717f64a4c..305b0f81fc4 100644 --- a/packages/search/lib/commands/index.ts +++ b/packages/search/lib/commands/index.ts @@ -396,10 +396,6 @@ export interface SearchReply { }>; } -export interface AggregateReply { - total: number; - results: Array>; -} export interface ProfileOptions { LIMITED?: true; @@ -420,8 +416,8 @@ export type ProfileRawReply = [ ]; export interface ProfileReply { - results: SearchReply | AggregateReply, - profile: ProfileData + results: SearchReply | AGGREGATE.AggregateReply; + profile: ProfileData; } interface ChildIterator { diff --git a/packages/search/package.json b/packages/search/package.json index bca7619b845..c15585746bb 100644 --- a/packages/search/package.json +++ b/packages/search/package.json @@ -4,6 +4,9 @@ "license": "MIT", "main": "./dist/index.js", "types": "./dist/index.d.ts", + "files": [ + "dist/" + ], "scripts": { "test": "nyc -r text-summary -r lcov mocha -r source-map-support/register -r ts-node/register './lib/**/*.spec.ts'", "build": "tsc", @@ -15,12 +18,12 @@ "devDependencies": { "@istanbuljs/nyc-config-typescript": "^1.0.2", "@node-redis/test-utils": "*", - "@types/node": "^16.11.12", + "@types/node": "^17.0.1", "nyc": "^15.1.0", "release-it": "^14.11.8", "source-map-support": "^0.5.21", "ts-node": "^10.4.0", "typedoc": "^0.22.10", - "typescript": "^4.5.3" + "typescript": "^4.5.4" } } diff --git a/packages/test-utils/package.json b/packages/test-utils/package.json index 36caf2d10eb..233d7f279ad 100644 --- a/packages/test-utils/package.json +++ b/packages/test-utils/package.json @@ -12,14 +12,14 @@ "devDependencies": { "@istanbuljs/nyc-config-typescript": "^1.0.2", "@types/mocha": "^9.0.0", - "@types/node": "^16.11.12", + "@types/node": "^17.0.1", "@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.3", + "typescript": "^4.5.4", "yargs": "^17.3.0" } } diff --git a/packages/time-series/lib/commands/CREATERULE.ts b/packages/time-series/lib/commands/CREATERULE.ts index 8d39722c9e2..9e3d25a4c8b 100644 --- a/packages/time-series/lib/commands/CREATERULE.ts +++ b/packages/time-series/lib/commands/CREATERULE.ts @@ -18,4 +18,4 @@ export function transformArguments( ]; } -export declare function transfromReply(): 'OK'; +export declare function transformReply(): 'OK'; diff --git a/packages/time-series/lib/commands/DELETERULE.ts b/packages/time-series/lib/commands/DELETERULE.ts index fff00a906a1..1714d510932 100644 --- a/packages/time-series/lib/commands/DELETERULE.ts +++ b/packages/time-series/lib/commands/DELETERULE.ts @@ -8,4 +8,4 @@ export function transformArguments(sourceKey: string, destinationKey: string): A ]; } -export declare function transfromReply(): 'OK'; +export declare function transformReply(): 'OK'; diff --git a/packages/time-series/package.json b/packages/time-series/package.json index fefb8891bef..04e18b75f90 100644 --- a/packages/time-series/package.json +++ b/packages/time-series/package.json @@ -4,6 +4,9 @@ "license": "MIT", "main": "./dist/index.js", "types": "./dist/index.d.ts", + "files": [ + "dist/" + ], "scripts": { "test": "nyc -r text-summary -r lcov mocha -r source-map-support/register -r ts-node/register './lib/**/*.spec.ts'", "build": "tsc", @@ -15,12 +18,12 @@ "devDependencies": { "@istanbuljs/nyc-config-typescript": "^1.0.2", "@node-redis/test-utils": "*", - "@types/node": "^16.11.12", + "@types/node": "^17.0.1", "nyc": "^15.1.0", "release-it": "^14.11.8", "source-map-support": "^0.5.21", "ts-node": "^10.4.0", "typedoc": "^0.22.10", - "typescript": "^4.5.3" + "typescript": "^4.5.4" } } From b37038e9dcbe1350a7fa7c441cc5c814e730aaf0 Mon Sep 17 00:00:00 2001 From: Dmitriy Fishman Date: Wed, 22 Dec 2021 15:34:17 +0200 Subject: [PATCH 1064/1748] docs: fix a typo in isolated-execution.md (#1795) --- docs/isolated-execution.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/docs/isolated-execution.md b/docs/isolated-execution.md index 1fd16b8f462..7870a4680e7 100644 --- a/docs/isolated-execution.md +++ b/docs/isolated-execution.md @@ -8,7 +8,7 @@ Sometimes you want to run your commands on an exclusive connection. There are a Below are several examples of how to use isolated execution. -> NOTE: Behind the scences we're using [`generic-pool`](https://www.npmjs.com/package/generic-pool) to provide a pool of connections that can be isolated. Go there to learn more. +> NOTE: Behind the scenes we're using [`generic-pool`](https://www.npmjs.com/package/generic-pool) to provide a pool of connections that can be isolated. Go there to learn more. ## The Simple Scenario From b97d18b61071b2d615c4606ee6b5930854b9e4e4 Mon Sep 17 00:00:00 2001 From: leibale Date: Thu, 23 Dec 2021 17:17:19 -0500 Subject: [PATCH 1065/1748] fix some types --- README.md | 6 ++++- index.ts | 4 ++-- packages/client/lib/client/index.spec.ts | 4 ++-- packages/client/lib/client/index.ts | 24 ++++++++++++-------- packages/client/lib/client/multi-command.ts | 11 +++++---- packages/client/lib/cluster/index.ts | 6 ++--- packages/client/lib/cluster/multi-command.ts | 9 ++++---- packages/client/lib/command-options.ts | 2 +- packages/client/lib/commander.ts | 2 +- packages/test-utils/lib/index.ts | 8 +++---- 10 files changed, 42 insertions(+), 34 deletions(-) diff --git a/README.md b/README.md index b38aadf036d..911112ac5ac 100644 --- a/README.md +++ b/README.md @@ -122,7 +122,11 @@ This pattern works especially well for blocking commands—such as `BLPOP` and ` ```typescript import { commandOptions } from 'redis'; -const blPopPromise = client.blPop(commandOptions({ isolated: true }), 'key', 0); +const blPopPromise = client.blPop( + commandOptions({ isolated: true }), + 'key', + 0 +); await client.lPush('key', ['1', '2']); diff --git a/index.ts b/index.ts index 2875c6fd848..18b6d4a1913 100644 --- a/index.ts +++ b/index.ts @@ -15,7 +15,7 @@ const modules = { ts: RedisTimeSeries }; -export function createClient>( +export function createClient( options?: Omit, 'modules'> ): RedisClientType { return _createClient({ @@ -24,7 +24,7 @@ export function createClient>( }); } -export function createCluster>( +export function createCluster( options: Omit, 'modules'> ): RedisClusterType { return _createCluster({ diff --git a/packages/client/lib/client/index.spec.ts b/packages/client/lib/client/index.spec.ts index 05c0deb8974..bf9fa7ae3e6 100644 --- a/packages/client/lib/client/index.spec.ts +++ b/packages/client/lib/client/index.spec.ts @@ -573,8 +573,8 @@ describe('Client', () => { describe('PubSub', () => { testUtils.testWithClient('should be able to publish and subscribe to messages', async publisher => { function assertStringListener(message: string, channel: string) { - assert.ok(typeof message === 'string'); - assert.ok(typeof channel === 'string'); + assert.equal(typeof message, 'string'); + assert.equal(typeof channel, 'string'); } function assertBufferListener(message: Buffer, channel: Buffer) { diff --git a/packages/client/lib/client/index.ts b/packages/client/lib/client/index.ts index 9e1b2d37cc9..2b801a54087 100644 --- a/packages/client/lib/client/index.ts +++ b/packages/client/lib/client/index.ts @@ -34,9 +34,11 @@ type ConvertArgumentType = ) : ( Type extends Set ? Set> : ( Type extends Map ? Map> : ( - Type extends Record ? { - [Property in keyof Type]: ConvertArgumentType - } : Type + Type extends Array ? Array> : ( + Type extends Record ? { + [Property in keyof Type]: ConvertArgumentType + } : Type + ) ) ) ); @@ -57,21 +59,23 @@ type WithCommands = { [P in keyof typeof COMMANDS]: RedisClientCommandSignature<(typeof COMMANDS)[P]>; }; +export type ExcludeMappedString = string extends S ? never : S; + export type WithModules = { - [P in keyof M as M[P] extends never ? never : P]: { - [C in keyof M[P]]: RedisClientCommandSignature; + [P in keyof M as ExcludeMappedString

]: { + [C in keyof M[P] as ExcludeMappedString]: RedisClientCommandSignature; }; }; export type WithScripts = { - [P in keyof S as S[P] extends never ? never : P]: RedisClientCommandSignature; + [P in keyof S as ExcludeMappedString

]: RedisClientCommandSignature; }; -export type RedisClientType, S extends RedisScripts = Record> = +export type RedisClientType = RedisClient & WithCommands & WithModules & WithScripts; export type InstantiableRedisClient = - new (...args: ConstructorParameters) => RedisClientType; + new (options?: RedisClientOptions) => RedisClientType; export interface ClientCommandOptions extends QueueCommandOptions { isolated?: boolean; @@ -85,7 +89,7 @@ export default class RedisClient return commandOptions(options); } - static extend, S extends RedisScripts = Record>(plugins?: RedisPlugins): InstantiableRedisClient { + static extend(plugins?: RedisPlugins): InstantiableRedisClient { const Client = extendWithModulesAndScripts({ BaseClass: RedisClient, modules: plugins?.modules, @@ -101,7 +105,7 @@ export default class RedisClient return Client; } - static create, S extends RedisScripts = Record>(options?: RedisClientOptions): RedisClientType { + static create(options?: RedisClientOptions): RedisClientType { return new (RedisClient.extend(options))(options); } diff --git a/packages/client/lib/client/multi-command.ts b/packages/client/lib/client/multi-command.ts index d45ac6ce311..5d69b933152 100644 --- a/packages/client/lib/client/multi-command.ts +++ b/packages/client/lib/client/multi-command.ts @@ -2,25 +2,26 @@ import COMMANDS from './commands'; import { RedisCommand, RedisCommandArguments, RedisCommandRawReply, RedisModules, RedisPlugins, RedisScript, RedisScripts } from '../commands'; import RedisMultiCommand, { RedisMultiQueuedCommand } from '../multi-command'; import { extendWithCommands, extendWithModulesAndScripts, LegacyCommandArguments, transformLegacyCommandArguments } from '../commander'; +import { ExcludeMappedString } from '.'; type RedisClientMultiCommandSignature = (...args: Parameters) => RedisClientMultiCommandType; type WithCommands = { - [P in keyof typeof COMMANDS]: RedisClientMultiCommandSignature<(typeof COMMANDS)[P], M, S> + [P in keyof typeof COMMANDS]: RedisClientMultiCommandSignature<(typeof COMMANDS)[P], M, S>; }; type WithModules = { - [P in keyof M as M[P] extends never ? never : P]: { - [C in keyof M[P]]: RedisClientMultiCommandSignature; + [P in keyof M as ExcludeMappedString

]: { + [C in keyof M[P] as ExcludeMappedString]: RedisClientMultiCommandSignature; }; }; type WithScripts = { - [P in keyof S as S[P] extends never ? never : P]: RedisClientMultiCommandSignature + [P in keyof S as ExcludeMappedString

]: RedisClientMultiCommandSignature; }; -export type RedisClientMultiCommandType, S extends RedisScripts = Record> = +export type RedisClientMultiCommandType = RedisClientMultiCommand & WithCommands & WithModules & WithScripts; export type RedisClientMultiExecutor = (queue: Array, chainId?: symbol) => Promise>; diff --git a/packages/client/lib/cluster/index.ts b/packages/client/lib/cluster/index.ts index 0a9ead636a4..157b607b081 100644 --- a/packages/client/lib/cluster/index.ts +++ b/packages/client/lib/cluster/index.ts @@ -20,10 +20,10 @@ type WithCommands = { [P in keyof typeof COMMANDS]: RedisClientCommandSignature<(typeof COMMANDS)[P]>; }; -export type RedisClusterType, S extends RedisScripts = Record> = +export type RedisClusterType = RedisCluster & WithCommands & WithModules & WithScripts; -export default class RedisCluster, S extends RedisScripts = Record> extends EventEmitter { +export default class RedisCluster extends EventEmitter { static extractFirstKey(command: RedisCommand, originalArgs: Array, redisArgs: RedisCommandArguments): RedisCommandArgument | undefined { if (command.FIRST_KEY_INDEX === undefined) { return undefined; @@ -34,7 +34,7 @@ export default class RedisCluster return command.FIRST_KEY_INDEX(...originalArgs); } - static create, S extends RedisScripts = Record>(options?: RedisClusterOptions): RedisClusterType { + static create(options?: RedisClusterOptions): RedisClusterType { return new (extendWithModulesAndScripts({ BaseClass: RedisCluster, modules: options?.modules, diff --git a/packages/client/lib/cluster/multi-command.ts b/packages/client/lib/cluster/multi-command.ts index 974dcb10293..a7b6f33f924 100644 --- a/packages/client/lib/cluster/multi-command.ts +++ b/packages/client/lib/cluster/multi-command.ts @@ -3,6 +3,7 @@ import { RedisCommand, RedisCommandArgument, RedisCommandArguments, RedisCommand import RedisMultiCommand, { RedisMultiQueuedCommand } from '../multi-command'; import { extendWithCommands, extendWithModulesAndScripts } from '../commander'; import RedisCluster from '.'; +import { ExcludeMappedString } from '../client'; type RedisClusterMultiCommandSignature = (...args: Parameters) => RedisClusterMultiCommandType; @@ -12,16 +13,16 @@ type WithCommands = { }; type WithModules = { - [P in keyof M as M[P] extends never ? never : P]: { - [C in keyof M[P]]: RedisClusterMultiCommandSignature; + [P in keyof M as ExcludeMappedString

]: { + [C in keyof M[P] as ExcludeMappedString]: RedisClusterMultiCommandSignature; }; }; type WithScripts = { - [P in keyof S as S[P] extends never ? never : P]: RedisClusterMultiCommandSignature + [P in keyof S as ExcludeMappedString

]: RedisClusterMultiCommandSignature }; -export type RedisClusterMultiCommandType, S extends RedisScripts = Record> = +export type RedisClusterMultiCommandType = RedisClusterMultiCommand & WithCommands & WithModules & WithScripts; export type RedisClusterMultiExecutor = (queue: Array, firstKey?: RedisCommandArgument, chainId?: symbol) => Promise>; diff --git a/packages/client/lib/command-options.ts b/packages/client/lib/command-options.ts index 2096258046f..8f91130b557 100644 --- a/packages/client/lib/command-options.ts +++ b/packages/client/lib/command-options.ts @@ -10,5 +10,5 @@ export function commandOptions(options: T): CommandOptions { } export function isCommandOptions(options: any): options is CommandOptions { - return options && options[symbol] === true; + return options?.[symbol] === true; } diff --git a/packages/client/lib/commander.ts b/packages/client/lib/commander.ts index fa1136f873e..2d129d679e2 100644 --- a/packages/client/lib/commander.ts +++ b/packages/client/lib/commander.ts @@ -70,7 +70,7 @@ export function extendWithModulesAndScripts(config: Exte return (Commander ?? config.BaseClass) as any; } -export function transformCommandArguments( +export function transformCommandArguments( command: RedisCommand, args: Array ): { diff --git a/packages/test-utils/lib/index.ts b/packages/test-utils/lib/index.ts index 8e20df75818..56847ce84dd 100644 --- a/packages/test-utils/lib/index.ts +++ b/packages/test-utils/lib/index.ts @@ -5,12 +5,10 @@ import { RedisServerDockerConfig, spawnRedisServer, spawnRedisCluster } from './ import yargs from 'yargs'; import { hideBin } from 'yargs/helpers'; -interface TestUtilsConfig { +interface TestUtilsConfig { dockerImageName: string; dockerImageVersionArgument: string; defaultDockerVersion: string; - defaultClientOptions?: Partial>; - defaultClusterOptions?: Partial>; } interface CommonTestOptions { @@ -29,7 +27,7 @@ interface ClusterTestOptions ext numberOfNodes?: number; } -export default class TestUtils { +export default class TestUtils { static #getVersion(argumentName: string, defaultVersion: string): Array { return yargs(hideBin(process.argv)) .option(argumentName, { @@ -52,7 +50,7 @@ export default class TestUtils { readonly #DOCKER_IMAGE: RedisServerDockerConfig; - constructor(config: TestUtilsConfig) { + constructor(config: TestUtilsConfig) { this.#DOCKER_IMAGE = { image: config.dockerImageName, version: TestUtils.#getVersion(config.dockerImageVersionArgument, config.defaultDockerVersion) From fd72a287b72d36e532a05692cad491c6349db2c5 Mon Sep 17 00:00:00 2001 From: leibale Date: Tue, 28 Dec 2021 20:21:51 -0500 Subject: [PATCH 1066/1748] fix #1798 - temp fix for __redis__:invalidate null messages --- packages/client/lib/client/commands-queue.ts | 6 ++++-- 1 file changed, 4 insertions(+), 2 deletions(-) diff --git a/packages/client/lib/client/commands-queue.ts b/packages/client/lib/client/commands-queue.ts index e716dcd3a23..3505ef8fa7a 100644 --- a/packages/client/lib/client/commands-queue.ts +++ b/packages/client/lib/client/commands-queue.ts @@ -71,7 +71,7 @@ export default class RedisCommandsQueue { } static #emitPubSubMessage(listenersMap: PubSubListenersMap, message: Buffer, channel: Buffer, pattern?: Buffer): void { - const keyString = (pattern || channel).toString(), + const keyString = (pattern ?? channel).toString(), listeners = listenersMap.get(keyString); if (!listeners) return; @@ -82,7 +82,9 @@ export default class RedisCommandsQueue { if (!listeners.strings.size) return; - const messageString = message.toString(), + // https://github.com/redis/redis/pull/7469 + // https://github.com/redis/redis/issues/7463 + const messageString = (Array.isArray(message) ? message.map(m => m.toString()) as any : message.toString()), channelString = pattern ? channel.toString() : keyString; for (const listener of listeners.strings) { listener(messageString, channelString); From f93ac04436fe2287aefdae4de66a644e658cbde9 Mon Sep 17 00:00:00 2001 From: Avital Fine <79420960+AvitalFineRedis@users.noreply.github.com> Date: Wed, 29 Dec 2021 17:55:09 +0100 Subject: [PATCH 1067/1748] Bloom commands (#1786) * ft.alter * bloom commands * tdigest * delete tdigest * uncomment tests * small changes * Update MADD.ts * small changes * clean code * Update README.md * Update README.md Co-authored-by: leibale --- README.md | 3 +- package-lock.json | 30 ++++++++ packages/bloom/.npmignore | 6 ++ packages/bloom/.nycrc.json | 4 ++ packages/bloom/.release-it.json | 10 +++ packages/bloom/README.md | 1 + packages/bloom/lib/commands/bloom/ADD.spec.ts | 19 +++++ packages/bloom/lib/commands/bloom/ADD.ts | 7 ++ .../bloom/lib/commands/bloom/EXISTS.spec.ts | 19 +++++ packages/bloom/lib/commands/bloom/EXISTS.ts | 9 +++ .../bloom/lib/commands/bloom/INFO.spec.ts | 24 +++++++ packages/bloom/lib/commands/bloom/INFO.ts | 38 ++++++++++ .../bloom/lib/commands/bloom/INSERT.spec.ts | 69 +++++++++++++++++++ packages/bloom/lib/commands/bloom/INSERT.ts | 46 +++++++++++++ .../lib/commands/bloom/LOADCHUNK.spec.ts | 11 +++ .../bloom/lib/commands/bloom/LOADCHUNK.ts | 13 ++++ .../bloom/lib/commands/bloom/MADD.spec.ts | 19 +++++ packages/bloom/lib/commands/bloom/MADD.ts | 7 ++ .../bloom/lib/commands/bloom/MEXISTS.spec.ts | 19 +++++ packages/bloom/lib/commands/bloom/MEXISTS.ts | 9 +++ .../bloom/lib/commands/bloom/RESERVE.spec.ts | 49 +++++++++++++ packages/bloom/lib/commands/bloom/RESERVE.ts | 27 ++++++++ .../bloom/lib/commands/bloom/SCANDUMP.spec.ts | 11 +++ packages/bloom/lib/commands/bloom/SCANDUMP.ts | 24 +++++++ packages/bloom/lib/commands/bloom/index.ts | 30 ++++++++ .../commands/count-min-sketch/INCRBY.spec.ts | 41 +++++++++++ .../lib/commands/count-min-sketch/INCRBY.ts | 29 ++++++++ .../commands/count-min-sketch/INFO.spec.ts | 25 +++++++ .../lib/commands/count-min-sketch/INFO.ts | 30 ++++++++ .../count-min-sketch/INITBYDIM.spec.ts | 19 +++++ .../commands/count-min-sketch/INITBYDIM.ts | 7 ++ .../count-min-sketch/INITBYPROB.spec.ts | 19 +++++ .../commands/count-min-sketch/INITBYPROB.ts | 7 ++ .../commands/count-min-sketch/MERGE.spec.ts | 36 ++++++++++ .../lib/commands/count-min-sketch/MERGE.ts | 37 ++++++++++ .../commands/count-min-sketch/QUERY.spec.ts | 22 ++++++ .../lib/commands/count-min-sketch/QUERY.ts | 15 ++++ .../lib/commands/count-min-sketch/index.ts | 21 ++++++ .../bloom/lib/commands/cuckoo/ADD.spec.ts | 19 +++++ packages/bloom/lib/commands/cuckoo/ADD.ts | 7 ++ .../bloom/lib/commands/cuckoo/ADDNX.spec.ts | 21 ++++++ packages/bloom/lib/commands/cuckoo/ADDNX.ts | 7 ++ .../bloom/lib/commands/cuckoo/COUNT.spec.ts | 19 +++++ packages/bloom/lib/commands/cuckoo/COUNT.ts | 7 ++ .../bloom/lib/commands/cuckoo/DEL.spec.ts | 21 ++++++ packages/bloom/lib/commands/cuckoo/DEL.ts | 7 ++ .../bloom/lib/commands/cuckoo/EXISTS.spec.ts | 19 +++++ packages/bloom/lib/commands/cuckoo/EXISTS.ts | 9 +++ .../bloom/lib/commands/cuckoo/INFO.spec.ts | 27 ++++++++ packages/bloom/lib/commands/cuckoo/INFO.ts | 50 ++++++++++++++ .../bloom/lib/commands/cuckoo/INSERT.spec.ts | 22 ++++++ packages/bloom/lib/commands/cuckoo/INSERT.ts | 18 +++++ .../lib/commands/cuckoo/INSERTNX.spec.ts | 22 ++++++ .../bloom/lib/commands/cuckoo/INSERTNX.ts | 18 +++++ .../lib/commands/cuckoo/LOADCHUNK.spec.ts | 11 +++ .../bloom/lib/commands/cuckoo/LOADCHUNK.ts | 7 ++ .../bloom/lib/commands/cuckoo/RESERVE.spec.ts | 48 +++++++++++++ packages/bloom/lib/commands/cuckoo/RESERVE.ts | 31 +++++++++ .../lib/commands/cuckoo/SCANDUMP.spec.ts | 11 +++ .../bloom/lib/commands/cuckoo/SCANDUMP.ts | 22 ++++++ .../bloom/lib/commands/cuckoo/index.spec.ts | 48 +++++++++++++ packages/bloom/lib/commands/cuckoo/index.ts | 62 +++++++++++++++++ packages/bloom/lib/commands/index.ts | 11 +++ packages/bloom/lib/commands/top-k/ADD.spec.ts | 22 ++++++ packages/bloom/lib/commands/top-k/ADD.ts | 13 ++++ .../bloom/lib/commands/top-k/COUNT.spec.ts | 21 ++++++ packages/bloom/lib/commands/top-k/COUNT.ts | 15 ++++ .../bloom/lib/commands/top-k/INCRBY.spec.ts | 42 +++++++++++ packages/bloom/lib/commands/top-k/INCRBY.ts | 29 ++++++++ .../bloom/lib/commands/top-k/INFO.spec.ts | 23 +++++++ packages/bloom/lib/commands/top-k/INFO.ts | 34 +++++++++ .../bloom/lib/commands/top-k/LIST.spec.ts | 21 ++++++ packages/bloom/lib/commands/top-k/LIST.ts | 7 ++ .../bloom/lib/commands/top-k/QUERY.spec.ts | 21 ++++++ packages/bloom/lib/commands/top-k/QUERY.ts | 15 ++++ .../bloom/lib/commands/top-k/RESERVE.spec.ts | 32 +++++++++ packages/bloom/lib/commands/top-k/RESERVE.ts | 27 ++++++++ packages/bloom/lib/commands/top-k/index.ts | 24 +++++++ packages/bloom/lib/index.ts | 1 + packages/bloom/lib/test-utils.ts | 19 +++++ packages/bloom/package.json | 29 ++++++++ packages/bloom/tsconfig.json | 9 +++ packages/client/lib/client/index.spec.ts | 4 +- packages/client/lib/commands/index.ts | 1 - 84 files changed, 1760 insertions(+), 5 deletions(-) create mode 100644 packages/bloom/.npmignore create mode 100644 packages/bloom/.nycrc.json create mode 100644 packages/bloom/.release-it.json create mode 100644 packages/bloom/README.md create mode 100644 packages/bloom/lib/commands/bloom/ADD.spec.ts create mode 100644 packages/bloom/lib/commands/bloom/ADD.ts create mode 100644 packages/bloom/lib/commands/bloom/EXISTS.spec.ts create mode 100644 packages/bloom/lib/commands/bloom/EXISTS.ts create mode 100644 packages/bloom/lib/commands/bloom/INFO.spec.ts create mode 100644 packages/bloom/lib/commands/bloom/INFO.ts create mode 100644 packages/bloom/lib/commands/bloom/INSERT.spec.ts create mode 100644 packages/bloom/lib/commands/bloom/INSERT.ts create mode 100644 packages/bloom/lib/commands/bloom/LOADCHUNK.spec.ts create mode 100644 packages/bloom/lib/commands/bloom/LOADCHUNK.ts create mode 100644 packages/bloom/lib/commands/bloom/MADD.spec.ts create mode 100644 packages/bloom/lib/commands/bloom/MADD.ts create mode 100644 packages/bloom/lib/commands/bloom/MEXISTS.spec.ts create mode 100644 packages/bloom/lib/commands/bloom/MEXISTS.ts create mode 100644 packages/bloom/lib/commands/bloom/RESERVE.spec.ts create mode 100644 packages/bloom/lib/commands/bloom/RESERVE.ts create mode 100644 packages/bloom/lib/commands/bloom/SCANDUMP.spec.ts create mode 100644 packages/bloom/lib/commands/bloom/SCANDUMP.ts create mode 100644 packages/bloom/lib/commands/bloom/index.ts create mode 100644 packages/bloom/lib/commands/count-min-sketch/INCRBY.spec.ts create mode 100644 packages/bloom/lib/commands/count-min-sketch/INCRBY.ts create mode 100644 packages/bloom/lib/commands/count-min-sketch/INFO.spec.ts create mode 100644 packages/bloom/lib/commands/count-min-sketch/INFO.ts create mode 100644 packages/bloom/lib/commands/count-min-sketch/INITBYDIM.spec.ts create mode 100644 packages/bloom/lib/commands/count-min-sketch/INITBYDIM.ts create mode 100644 packages/bloom/lib/commands/count-min-sketch/INITBYPROB.spec.ts create mode 100644 packages/bloom/lib/commands/count-min-sketch/INITBYPROB.ts create mode 100644 packages/bloom/lib/commands/count-min-sketch/MERGE.spec.ts create mode 100644 packages/bloom/lib/commands/count-min-sketch/MERGE.ts create mode 100644 packages/bloom/lib/commands/count-min-sketch/QUERY.spec.ts create mode 100644 packages/bloom/lib/commands/count-min-sketch/QUERY.ts create mode 100644 packages/bloom/lib/commands/count-min-sketch/index.ts create mode 100644 packages/bloom/lib/commands/cuckoo/ADD.spec.ts create mode 100644 packages/bloom/lib/commands/cuckoo/ADD.ts create mode 100644 packages/bloom/lib/commands/cuckoo/ADDNX.spec.ts create mode 100644 packages/bloom/lib/commands/cuckoo/ADDNX.ts create mode 100644 packages/bloom/lib/commands/cuckoo/COUNT.spec.ts create mode 100644 packages/bloom/lib/commands/cuckoo/COUNT.ts create mode 100644 packages/bloom/lib/commands/cuckoo/DEL.spec.ts create mode 100644 packages/bloom/lib/commands/cuckoo/DEL.ts create mode 100644 packages/bloom/lib/commands/cuckoo/EXISTS.spec.ts create mode 100644 packages/bloom/lib/commands/cuckoo/EXISTS.ts create mode 100644 packages/bloom/lib/commands/cuckoo/INFO.spec.ts create mode 100644 packages/bloom/lib/commands/cuckoo/INFO.ts create mode 100644 packages/bloom/lib/commands/cuckoo/INSERT.spec.ts create mode 100644 packages/bloom/lib/commands/cuckoo/INSERT.ts create mode 100644 packages/bloom/lib/commands/cuckoo/INSERTNX.spec.ts create mode 100644 packages/bloom/lib/commands/cuckoo/INSERTNX.ts create mode 100644 packages/bloom/lib/commands/cuckoo/LOADCHUNK.spec.ts create mode 100644 packages/bloom/lib/commands/cuckoo/LOADCHUNK.ts create mode 100644 packages/bloom/lib/commands/cuckoo/RESERVE.spec.ts create mode 100644 packages/bloom/lib/commands/cuckoo/RESERVE.ts create mode 100644 packages/bloom/lib/commands/cuckoo/SCANDUMP.spec.ts create mode 100644 packages/bloom/lib/commands/cuckoo/SCANDUMP.ts create mode 100644 packages/bloom/lib/commands/cuckoo/index.spec.ts create mode 100644 packages/bloom/lib/commands/cuckoo/index.ts create mode 100644 packages/bloom/lib/commands/index.ts create mode 100644 packages/bloom/lib/commands/top-k/ADD.spec.ts create mode 100644 packages/bloom/lib/commands/top-k/ADD.ts create mode 100644 packages/bloom/lib/commands/top-k/COUNT.spec.ts create mode 100644 packages/bloom/lib/commands/top-k/COUNT.ts create mode 100644 packages/bloom/lib/commands/top-k/INCRBY.spec.ts create mode 100644 packages/bloom/lib/commands/top-k/INCRBY.ts create mode 100644 packages/bloom/lib/commands/top-k/INFO.spec.ts create mode 100644 packages/bloom/lib/commands/top-k/INFO.ts create mode 100644 packages/bloom/lib/commands/top-k/LIST.spec.ts create mode 100644 packages/bloom/lib/commands/top-k/LIST.ts create mode 100644 packages/bloom/lib/commands/top-k/QUERY.spec.ts create mode 100644 packages/bloom/lib/commands/top-k/QUERY.ts create mode 100644 packages/bloom/lib/commands/top-k/RESERVE.spec.ts create mode 100644 packages/bloom/lib/commands/top-k/RESERVE.ts create mode 100644 packages/bloom/lib/commands/top-k/index.ts create mode 100644 packages/bloom/lib/index.ts create mode 100644 packages/bloom/lib/test-utils.ts create mode 100644 packages/bloom/package.json create mode 100644 packages/bloom/tsconfig.json diff --git a/README.md b/README.md index 911112ac5ac..46ee50b90fa 100644 --- a/README.md +++ b/README.md @@ -319,12 +319,11 @@ Node Redis is supported with the following versions of Redis: > Node Redis should work with older versions of Redis, but it is not fully tested and we cannot offer support. -## Packages - | Name | Description | |---------------------------------------------------|----------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------| | [redis](./) | [![Downloads](https://img.shields.io/npm/dm/redis.svg)](https://www.npmjs.com/package/redis) [![Version](https://img.shields.io/npm/v/redis.svg)](https://www.npmjs.com/package/redis) | | [@node-redis/client](./packages/client) | [![Downloads](https://img.shields.io/npm/dm/@node-redis/client.svg)](https://www.npmjs.com/package/@node-redis/client) [![Version](https://img.shields.io/npm/v/@node-redis/client.svg)](https://www.npmjs.com/package/@node-redis/client) [![Docs](https://img.shields.io/badge/-documentation-dc382c)](https://redis.js.org/documentation/client/) | +| [@node-redis/bloom](./packages/bloom) | [![Downloads](https://img.shields.io/npm/dm/@node-redis/bloom.svg)](https://www.npmjs.com/package/@node-redis/bloom) [![Version](https://img.shields.io/npm/v/@node-redis/bloom.svg)](https://www.npmjs.com/package/@node-redis/bloom) [![Docs](https://img.shields.io/badge/-documentation-dc382c)](https://redis.js.org/documentation/bloom/) [Redis Bloom](https://oss.redis.com/redisbloom/) commands | | [@node-redis/json](./packages/json) | [![Downloads](https://img.shields.io/npm/dm/@node-redis/json.svg)](https://www.npmjs.com/package/@node-redis/json) [![Version](https://img.shields.io/npm/v/@node-redis/json.svg)](https://www.npmjs.com/package/@node-redis/json) [![Docs](https://img.shields.io/badge/-documentation-dc382c)](https://redis.js.org/documentation/json/) [Redis JSON](https://oss.redis.com/redisjson/) commands | | [@node-redis/search](./packages/search) | [![Downloads](https://img.shields.io/npm/dm/@node-redis/search.svg)](https://www.npmjs.com/package/@node-redis/search) [![Version](https://img.shields.io/npm/v/@node-redis/search.svg)](https://www.npmjs.com/package/@node-redis/search) [![Docs](https://img.shields.io/badge/-documentation-dc382c)](https://redis.js.org/documentation/search/) [Redis Search](https://oss.redis.com/redisearch/) commands | | [@node-redis/time-series](./packages/time-series) | [![Downloads](https://img.shields.io/npm/dm/@node-redis/time-series.svg)](https://www.npmjs.com/package/@node-redis/time-series) [![Version](https://img.shields.io/npm/v/@node-redis/time-series.svg)](https://www.npmjs.com/package/@node-redis/time-series) [![Docs](https://img.shields.io/badge/-documentation-dc382c)](https://redis.js.org/documentation/time-series/) [Redis Time-Series](https://oss.redis.com/redistimeseries/) commands | diff --git a/package-lock.json b/package-lock.json index 036227ba094..92f25780eaf 100644 --- a/package-lock.json +++ b/package-lock.json @@ -6648,6 +6648,23 @@ "peerDependencies": { "@node-redis/client": "^1.0.0" } + }, + "packages/time-series": { + "version": "1.0.0-rc.0", + "license": "MIT", + "devDependencies": { + "@istanbuljs/nyc-config-typescript": "^1.0.1", + "@node-redis/test-utils": "*", + "@types/node": "^16.11.7", + "nyc": "^15.1.0", + "release-it": "^14.11.7", + "source-map-support": "^0.5.20", + "ts-node": "^10.4.0", + "typescript": "^4.4.4" + }, + "peerDependencies": { + "@node-redis/client": "^1.0.0" + } } }, "dependencies": { @@ -7192,6 +7209,19 @@ "typescript": "^4.5.4" } }, + "@node-redis/time-series": { + "version": "file:packages/time-series", + "requires": { + "@istanbuljs/nyc-config-typescript": "^1.0.1", + "@node-redis/test-utils": "*", + "@types/node": "^16.11.7", + "nyc": "^15.1.0", + "release-it": "^14.11.7", + "source-map-support": "^0.5.20", + "ts-node": "^10.4.0", + "typescript": "^4.4.4" + } + }, "@nodelib/fs.scandir": { "version": "2.1.5", "resolved": "https://registry.npmjs.org/@nodelib/fs.scandir/-/fs.scandir-2.1.5.tgz", diff --git a/packages/bloom/.npmignore b/packages/bloom/.npmignore new file mode 100644 index 00000000000..bbef2b404fb --- /dev/null +++ b/packages/bloom/.npmignore @@ -0,0 +1,6 @@ +.nyc_output/ +coverage/ +lib/ +.nycrc.json +.release-it.json +tsconfig.json diff --git a/packages/bloom/.nycrc.json b/packages/bloom/.nycrc.json new file mode 100644 index 00000000000..b4e671e178f --- /dev/null +++ b/packages/bloom/.nycrc.json @@ -0,0 +1,4 @@ +{ + "extends": "@istanbuljs/nyc-config-typescript", + "exclude": ["**/*.spec.ts", "lib/test-utils.ts"] +} diff --git a/packages/bloom/.release-it.json b/packages/bloom/.release-it.json new file mode 100644 index 00000000000..5d11263645f --- /dev/null +++ b/packages/bloom/.release-it.json @@ -0,0 +1,10 @@ +{ + "git": { + "tagName": "bloom@${version}", + "commitMessage": "Release ${tagName}", + "tagAnnotation": "Release ${tagName}" + }, + "npm": { + "publishArgs": ["--access", "public"] + } +} diff --git a/packages/bloom/README.md b/packages/bloom/README.md new file mode 100644 index 00000000000..d13769e02a4 --- /dev/null +++ b/packages/bloom/README.md @@ -0,0 +1 @@ +# @node-redis/bloom diff --git a/packages/bloom/lib/commands/bloom/ADD.spec.ts b/packages/bloom/lib/commands/bloom/ADD.spec.ts new file mode 100644 index 00000000000..e7ec3409136 --- /dev/null +++ b/packages/bloom/lib/commands/bloom/ADD.spec.ts @@ -0,0 +1,19 @@ +import { strict as assert } from 'assert'; +import testUtils, { GLOBAL } from '../../test-utils'; +import { transformArguments } from './ADD'; + +describe('BF ADD', () => { + it('transformArguments', () => { + assert.deepEqual( + transformArguments('key', 'item'), + ['BF.ADD', 'key', 'item'] + ); + }); + + testUtils.testWithClient('client.bf.add', async client => { + assert.equal( + await client.bf.add('key', 'item'), + true + ); + }, GLOBAL.SERVERS.OPEN); +}); diff --git a/packages/bloom/lib/commands/bloom/ADD.ts b/packages/bloom/lib/commands/bloom/ADD.ts new file mode 100644 index 00000000000..83dbc23c111 --- /dev/null +++ b/packages/bloom/lib/commands/bloom/ADD.ts @@ -0,0 +1,7 @@ +export const FIRST_KEY_INDEX = 1; + +export function transformArguments(key: string, item: string): Array { + return ['BF.ADD', key, item]; +} + +export { transformBooleanReply as transformReply } from '@node-redis/client/dist/lib/commands/generic-transformers'; diff --git a/packages/bloom/lib/commands/bloom/EXISTS.spec.ts b/packages/bloom/lib/commands/bloom/EXISTS.spec.ts new file mode 100644 index 00000000000..1088e739e61 --- /dev/null +++ b/packages/bloom/lib/commands/bloom/EXISTS.spec.ts @@ -0,0 +1,19 @@ +import { strict as assert } from 'assert'; +import testUtils, { GLOBAL } from '../../test-utils'; +import { transformArguments } from './EXISTS'; + +describe('BF EXISTS', () => { + it('transformArguments', () => { + assert.deepEqual( + transformArguments('key', 'item'), + ['BF.EXISTS', 'key', 'item'] + ); + }); + + testUtils.testWithClient('client.bf.exists', async client => { + assert.equal( + await client.bf.exists('key', 'item'), + false + ); + }, GLOBAL.SERVERS.OPEN); +}); diff --git a/packages/bloom/lib/commands/bloom/EXISTS.ts b/packages/bloom/lib/commands/bloom/EXISTS.ts new file mode 100644 index 00000000000..2f06e60b9b5 --- /dev/null +++ b/packages/bloom/lib/commands/bloom/EXISTS.ts @@ -0,0 +1,9 @@ +export const FIRST_KEY_INDEX = 1; + +export const IS_READ_ONLY = true; + +export function transformArguments(key: string, item: string): Array { + return ['BF.EXISTS', key, item]; +} + +export { transformBooleanReply as transformReply } from '@node-redis/client/dist/lib/commands/generic-transformers'; diff --git a/packages/bloom/lib/commands/bloom/INFO.spec.ts b/packages/bloom/lib/commands/bloom/INFO.spec.ts new file mode 100644 index 00000000000..7a5e5724c22 --- /dev/null +++ b/packages/bloom/lib/commands/bloom/INFO.spec.ts @@ -0,0 +1,24 @@ +import { strict as assert } from 'assert'; +import testUtils, { GLOBAL } from '../../test-utils'; +import { transformArguments } from './INFO'; + +describe('BF INFO', () => { + it('transformArguments', () => { + assert.deepEqual( + transformArguments('bloom'), + ['BF.INFO', 'bloom'] + ); + }); + + testUtils.testWithClient('client.bf.info', async client => { + await client.bf.reserve('key', 0.01, 100); + + const info = await client.bf.info('key'); + assert.equal(typeof info, 'object'); + assert.equal(info.capacity, 100); + assert.equal(typeof info.size, 'number'); + assert.equal(typeof info.numberOfFilters, 'number'); + assert.equal(typeof info.numberOfInsertedItems, 'number'); + assert.equal(typeof info.expansionRate, 'number'); + }, GLOBAL.SERVERS.OPEN); +}); diff --git a/packages/bloom/lib/commands/bloom/INFO.ts b/packages/bloom/lib/commands/bloom/INFO.ts new file mode 100644 index 00000000000..52e97646404 --- /dev/null +++ b/packages/bloom/lib/commands/bloom/INFO.ts @@ -0,0 +1,38 @@ +export const FIRST_KEY_INDEX = 1; + +export const IS_READ_ONLY = true; + +export function transformArguments(key: string): Array { + return ['BF.INFO', key]; +} + +export type InfoRawReply = [ + _: string, + capacity: number, + _: string, + size: number, + _: string, + numberOfFilters: number, + _: string, + numberOfInsertedItems: number, + _: string, + expansionRate: number, +]; + +export interface InfoReply { + capacity: number; + size: number; + numberOfFilters: number; + numberOfInsertedItems: number; + expansionRate: number; +} + +export function transformReply(reply: InfoRawReply): InfoReply { + return { + capacity: reply[1], + size: reply[3], + numberOfFilters: reply[5], + numberOfInsertedItems: reply[7], + expansionRate: reply[9] + }; +} diff --git a/packages/bloom/lib/commands/bloom/INSERT.spec.ts b/packages/bloom/lib/commands/bloom/INSERT.spec.ts new file mode 100644 index 00000000000..aff9e6e282b --- /dev/null +++ b/packages/bloom/lib/commands/bloom/INSERT.spec.ts @@ -0,0 +1,69 @@ +import { strict as assert } from 'assert'; +import testUtils, { GLOBAL } from '../../test-utils'; +import { transformArguments } from './INSERT'; + +describe('BF INSERT', () => { + describe('transformArguments', () => { + it('simple', () => { + assert.deepEqual( + transformArguments('key', 'item'), + ['BF.INSERT', 'key', 'ITEMS', 'item'] + ); + }); + + it('with CAPACITY', () => { + assert.deepEqual( + transformArguments('key', 'item', { CAPACITY: 100 }), + ['BF.INSERT', 'key', 'CAPACITY', '100', 'ITEMS', 'item'] + ); + }); + + it('with ERROR', () => { + assert.deepEqual( + transformArguments('key', 'item', { ERROR: 0.01 }), + ['BF.INSERT', 'key', 'ERROR', '0.01', 'ITEMS', 'item'] + ); + }); + + it('with EXPANSION', () => { + assert.deepEqual( + transformArguments('key', 'item', { EXPANSION: 1 }), + ['BF.INSERT', 'key', 'EXPANSION', '1', 'ITEMS', 'item'] + ); + }); + + it('with NOCREATE', () => { + assert.deepEqual( + transformArguments('key', 'item', { NOCREATE: true }), + ['BF.INSERT', 'key', 'NOCREATE', 'ITEMS', 'item'] + ); + }); + + it('with NONSCALING', () => { + assert.deepEqual( + transformArguments('key', 'item', { NONSCALING: true }), + ['BF.INSERT', 'key', 'NONSCALING', 'ITEMS', 'item'] + ); + }); + + it('with CAPACITY, ERROR, EXPANSION, NOCREATE and NONSCALING', () => { + assert.deepEqual( + transformArguments('key', 'item', { + CAPACITY: 100, + ERROR: 0.01, + EXPANSION: 1, + NOCREATE: true, + NONSCALING: true + }), + ['BF.INSERT', 'key', 'CAPACITY', '100', 'ERROR', '0.01', 'EXPANSION', '1', 'NOCREATE', 'NONSCALING', 'ITEMS', 'item'] + ); + }); + }); + + testUtils.testWithClient('client.bf.insert', async client => { + assert.deepEqual( + await client.bf.insert('key', 'item'), + [true] + ); + }, GLOBAL.SERVERS.OPEN); +}); diff --git a/packages/bloom/lib/commands/bloom/INSERT.ts b/packages/bloom/lib/commands/bloom/INSERT.ts new file mode 100644 index 00000000000..6bdb5fdf918 --- /dev/null +++ b/packages/bloom/lib/commands/bloom/INSERT.ts @@ -0,0 +1,46 @@ +import { pushVerdictArguments } from '@node-redis/client/dist/lib/commands/generic-transformers'; + +export const FIRST_KEY_INDEX = 1; + +interface InsertOptions { + CAPACITY?: number; + ERROR?: number; + EXPANSION?: number; + NOCREATE?: true; + NONSCALING?: true; +} + +export function transformArguments( + key: string, + items: string | Array, + options?: InsertOptions +): Array { + const args = ['BF.INSERT', key]; + + if (options?.CAPACITY) { + args.push('CAPACITY', options.CAPACITY.toString()); + } + + if (options?.ERROR) { + args.push('ERROR', options.ERROR.toString()); + } + + if (options?.EXPANSION) { + args.push('EXPANSION', options.EXPANSION.toString()); + } + + if (options?.NOCREATE) { + args.push('NOCREATE'); + } + + if (options?.NONSCALING) { + args.push('NONSCALING'); + } + + args.push('ITEMS'); + pushVerdictArguments(args, items); + + return args; +} + +export { transformBooleanArrayReply as transformReply } from '@node-redis/client/dist/lib/commands/generic-transformers'; diff --git a/packages/bloom/lib/commands/bloom/LOADCHUNK.spec.ts b/packages/bloom/lib/commands/bloom/LOADCHUNK.spec.ts new file mode 100644 index 00000000000..ea9f0acfa0d --- /dev/null +++ b/packages/bloom/lib/commands/bloom/LOADCHUNK.spec.ts @@ -0,0 +1,11 @@ +import { strict as assert } from 'assert'; +import { transformArguments } from './LOADCHUNK'; + +describe('BF LOADCHUNK', () => { + it('transformArguments', () => { + assert.deepEqual( + transformArguments('key', 0, ''), + ['BF.LOADCHUNK', 'key', '0', ''] + ); + }); +}); diff --git a/packages/bloom/lib/commands/bloom/LOADCHUNK.ts b/packages/bloom/lib/commands/bloom/LOADCHUNK.ts new file mode 100644 index 00000000000..b9c486c73c5 --- /dev/null +++ b/packages/bloom/lib/commands/bloom/LOADCHUNK.ts @@ -0,0 +1,13 @@ +import { RedisCommandArgument, RedisCommandArguments } from '@node-redis/client/dist/lib/commands'; + +export const FIRST_KEY_INDEX = 1; + +export function transformArguments( + key: string, + iteretor: number, + chunk: RedisCommandArgument +): RedisCommandArguments { + return ['BF.LOADCHUNK', key, iteretor.toString(), chunk]; +} + +export declare function transformReply(): 'OK'; diff --git a/packages/bloom/lib/commands/bloom/MADD.spec.ts b/packages/bloom/lib/commands/bloom/MADD.spec.ts new file mode 100644 index 00000000000..784f99926ff --- /dev/null +++ b/packages/bloom/lib/commands/bloom/MADD.spec.ts @@ -0,0 +1,19 @@ +import { strict as assert } from 'assert'; +import testUtils, { GLOBAL } from '../../test-utils'; +import { transformArguments } from './MADD'; + +describe('BF MADD', () => { + it('transformArguments', () => { + assert.deepEqual( + transformArguments('key', ['1', '2']), + ['BF.MADD', 'key', '1', '2'] + ); + }); + + testUtils.testWithClient('client.ts.mAdd', async client => { + assert.deepEqual( + await client.bf.mAdd('key', ['1', '2']), + [true, true] + ); + }, GLOBAL.SERVERS.OPEN); +}); diff --git a/packages/bloom/lib/commands/bloom/MADD.ts b/packages/bloom/lib/commands/bloom/MADD.ts new file mode 100644 index 00000000000..7a81a3a08a5 --- /dev/null +++ b/packages/bloom/lib/commands/bloom/MADD.ts @@ -0,0 +1,7 @@ +export const FIRST_KEY_INDEX = 1; + +export function transformArguments(key: string, items: Array): Array { + return ['BF.MADD', key, ...items]; +} + +export { transformBooleanArrayReply as transformReply } from '@node-redis/client/dist/lib/commands/generic-transformers'; diff --git a/packages/bloom/lib/commands/bloom/MEXISTS.spec.ts b/packages/bloom/lib/commands/bloom/MEXISTS.spec.ts new file mode 100644 index 00000000000..027e51d2c43 --- /dev/null +++ b/packages/bloom/lib/commands/bloom/MEXISTS.spec.ts @@ -0,0 +1,19 @@ +import { strict as assert } from 'assert'; +import testUtils, { GLOBAL } from '../../test-utils'; +import { transformArguments } from './MEXISTS'; + +describe('BF MEXISTS', () => { + it('transformArguments', () => { + assert.deepEqual( + transformArguments('key', ['1', '2']), + ['BF.MEXISTS', 'key', '1', '2'] + ); + }); + + testUtils.testWithClient('client.bf.mExists', async client => { + assert.deepEqual( + await client.bf.mExists('key', ['1', '2']), + [false, false] + ); + }, GLOBAL.SERVERS.OPEN); +}); diff --git a/packages/bloom/lib/commands/bloom/MEXISTS.ts b/packages/bloom/lib/commands/bloom/MEXISTS.ts new file mode 100644 index 00000000000..6b4627318b4 --- /dev/null +++ b/packages/bloom/lib/commands/bloom/MEXISTS.ts @@ -0,0 +1,9 @@ +export const FIRST_KEY_INDEX = 1; + +export const IS_READ_ONLY = true; + +export function transformArguments(key: string, items: Array): Array { + return ['BF.MEXISTS', key, ...items]; +} + +export { transformBooleanArrayReply as transformReply } from '@node-redis/client/dist/lib/commands/generic-transformers'; diff --git a/packages/bloom/lib/commands/bloom/RESERVE.spec.ts b/packages/bloom/lib/commands/bloom/RESERVE.spec.ts new file mode 100644 index 00000000000..bc872f9c3f7 --- /dev/null +++ b/packages/bloom/lib/commands/bloom/RESERVE.spec.ts @@ -0,0 +1,49 @@ +import { strict as assert } from 'assert'; +import testUtils, { GLOBAL } from '../../test-utils'; +import { transformArguments } from './RESERVE'; + +describe('BF RESERVE', () => { + describe('transformArguments', () => { + it('simple', () => { + assert.deepEqual( + transformArguments('key', 0.01, 100), + ['BF.RESERVE', 'key', '0.01', '100'] + ); + }); + + it('with EXPANSION', () => { + assert.deepEqual( + transformArguments('key', 0.01, 100, { + EXPANSION: 1 + }), + ['BF.RESERVE', 'key', '0.01', '100', 'EXPANSION', '1'] + ); + }); + + it('with NONSCALING', () => { + assert.deepEqual( + transformArguments('key', 0.01, 100, { + NONSCALING: true + }), + ['BF.RESERVE', 'key', '0.01', '100', 'NONSCALING'] + ); + }); + + it('with EXPANSION and NONSCALING', () => { + assert.deepEqual( + transformArguments('key', 0.01, 100, { + EXPANSION: 1, + NONSCALING: true + }), + ['BF.RESERVE', 'key', '0.01', '100', 'EXPANSION', '1', 'NONSCALING'] + ); + }); + }); + + testUtils.testWithClient('client.bf.reserve', async client => { + assert.equal( + await client.bf.reserve('bloom', 0.01, 100), + 'OK' + ); + }, GLOBAL.SERVERS.OPEN); +}); diff --git a/packages/bloom/lib/commands/bloom/RESERVE.ts b/packages/bloom/lib/commands/bloom/RESERVE.ts new file mode 100644 index 00000000000..18d7002f158 --- /dev/null +++ b/packages/bloom/lib/commands/bloom/RESERVE.ts @@ -0,0 +1,27 @@ +export const FIRST_KEY_INDEX = 1; + +interface ReserveOptions { + EXPANSION?: number; + NONSCALING?: true; +} + +export function transformArguments( + key: string, + errorRate: number, + capacity: number, + options?: ReserveOptions +): Array { + const args = ['BF.RESERVE', key, errorRate.toString(), capacity.toString()]; + + if (options?.EXPANSION) { + args.push('EXPANSION', options.EXPANSION.toString()); + } + + if (options?.NONSCALING) { + args.push('NONSCALING'); + } + + return args; +} + +export declare function transformReply(): 'OK'; diff --git a/packages/bloom/lib/commands/bloom/SCANDUMP.spec.ts b/packages/bloom/lib/commands/bloom/SCANDUMP.spec.ts new file mode 100644 index 00000000000..83440167593 --- /dev/null +++ b/packages/bloom/lib/commands/bloom/SCANDUMP.spec.ts @@ -0,0 +1,11 @@ +import { strict as assert } from 'assert'; +import { transformArguments } from './SCANDUMP'; + +describe('BF SCANDUMP', () => { + it('transformArguments', () => { + assert.deepEqual( + transformArguments('key', 0), + ['BF.SCANDUMP', 'key', '0'] + ); + }); +}); diff --git a/packages/bloom/lib/commands/bloom/SCANDUMP.ts b/packages/bloom/lib/commands/bloom/SCANDUMP.ts new file mode 100644 index 00000000000..04b3edc2a1f --- /dev/null +++ b/packages/bloom/lib/commands/bloom/SCANDUMP.ts @@ -0,0 +1,24 @@ +export const FIRST_KEY_INDEX = 1; + +export const IS_READ_ONLY = true; + +export function transformArguments(key: string, iterator: number): Array { + return ['BF.SCANDUMP', key, iterator.toString()]; +} + +type ScanDumpRawReply = [ + iterator: number, + chunk: string +]; + +interface ScanDumpReply { + iterator: number; + chunk: string; +} + +export function transformReply([iterator, chunk]: ScanDumpRawReply): ScanDumpReply { + return { + iterator, + chunk + }; +} diff --git a/packages/bloom/lib/commands/bloom/index.ts b/packages/bloom/lib/commands/bloom/index.ts new file mode 100644 index 00000000000..c57b0f79534 --- /dev/null +++ b/packages/bloom/lib/commands/bloom/index.ts @@ -0,0 +1,30 @@ +import * as ADD from './ADD'; +import * as EXISTS from './EXISTS'; +import * as INFO from './INFO'; +import * as INSERT from './INSERT'; +import * as LOADCHUNK from './LOADCHUNK'; +import * as MADD from './MADD'; +import * as MEXISTS from './MEXISTS'; +import * as RESERVE from './RESERVE'; +import * as SCANDUMP from './SCANDUMP'; + +export default { + ADD, + add: ADD, + EXISTS, + exists: EXISTS, + INFO, + info: INFO, + INSERT, + insert: INSERT, + LOADCHUNK, + loadChunk: LOADCHUNK, + MADD, + mAdd: MADD, + MEXISTS, + mExists: MEXISTS, + RESERVE, + reserve: RESERVE, + SCANDUMP, + scanDump: SCANDUMP +}; diff --git a/packages/bloom/lib/commands/count-min-sketch/INCRBY.spec.ts b/packages/bloom/lib/commands/count-min-sketch/INCRBY.spec.ts new file mode 100644 index 00000000000..95bb28e88b5 --- /dev/null +++ b/packages/bloom/lib/commands/count-min-sketch/INCRBY.spec.ts @@ -0,0 +1,41 @@ +import { strict as assert } from 'assert'; +import testUtils, { GLOBAL } from '../../test-utils'; +import { transformArguments } from './INCRBY'; + +describe('CMS INCRBY', () => { + describe('transformArguments', () => { + it('single item', () => { + assert.deepEqual( + transformArguments('key', { + item: 'item', + incrementBy: 1 + }), + ['CMS.INCRBY', 'key', 'item', '1'] + ); + }); + + it('multiple items', () => { + assert.deepEqual( + transformArguments('key', [{ + item: 'a', + incrementBy: 1 + }, { + item: 'b', + incrementBy: 2 + }]), + ['CMS.INCRBY', 'key', 'a', '1', 'b', '2'] + ); + }); + }); + + testUtils.testWithClient('client.cms.incrBy', async client => { + await client.cms.initByDim('key', 1000, 5); + assert.deepEqual( + await client.cms.incrBy('key', { + item: 'item', + incrementBy: 1 + }), + [1] + ); + }, GLOBAL.SERVERS.OPEN); +}); diff --git a/packages/bloom/lib/commands/count-min-sketch/INCRBY.ts b/packages/bloom/lib/commands/count-min-sketch/INCRBY.ts new file mode 100644 index 00000000000..e27fb397cdf --- /dev/null +++ b/packages/bloom/lib/commands/count-min-sketch/INCRBY.ts @@ -0,0 +1,29 @@ +export const FIRST_KEY_INDEX = 1; + +interface IncrByItem { + item: string; + incrementBy: number; +} + +export function transformArguments( + key: string, + items: IncrByItem | Array +): Array { + const args = ['CMS.INCRBY', key]; + + if (Array.isArray(items)) { + for (const item of items) { + pushIncrByItem(args, item); + } + } else { + pushIncrByItem(args, items); + } + + return args; +} + +function pushIncrByItem(args: Array, { item, incrementBy }: IncrByItem): void { + args.push(item, incrementBy.toString()); +} + +export declare function transformReply(): Array; diff --git a/packages/bloom/lib/commands/count-min-sketch/INFO.spec.ts b/packages/bloom/lib/commands/count-min-sketch/INFO.spec.ts new file mode 100644 index 00000000000..0db8a48447e --- /dev/null +++ b/packages/bloom/lib/commands/count-min-sketch/INFO.spec.ts @@ -0,0 +1,25 @@ +import { strict as assert } from 'assert'; +import testUtils, { GLOBAL } from '../../test-utils'; +import { transformArguments } from './INFO'; + +describe('CMS INFO', () => { + it('transformArguments', () => { + assert.deepEqual( + transformArguments('key'), + ['CMS.INFO', 'key'] + ); + }); + + testUtils.testWithClient('client.cms.info', async client => { + await client.cms.initByDim('key', 1000, 5); + + assert.deepEqual( + await client.cms.info('key'), + { + width: 1000, + depth: 5, + count: 0 + } + ); + }, GLOBAL.SERVERS.OPEN); +}); diff --git a/packages/bloom/lib/commands/count-min-sketch/INFO.ts b/packages/bloom/lib/commands/count-min-sketch/INFO.ts new file mode 100644 index 00000000000..6dbfffcb0e0 --- /dev/null +++ b/packages/bloom/lib/commands/count-min-sketch/INFO.ts @@ -0,0 +1,30 @@ +export const FIRST_KEY_INDEX = 1; + +export const IS_READ_ONLY = true; + +export function transformArguments(key: string): Array { + return ['CMS.INFO', key]; +} + +export type InfoRawReply = [ + _: string, + width: number, + _: string, + depth: number, + _: string, + count: number +]; + +export interface InfoReply { + width: number; + depth: number; + count: number; +} + +export function transformReply(reply: InfoRawReply): InfoReply { + return { + width: reply[1], + depth: reply[3], + count: reply[5] + }; +} diff --git a/packages/bloom/lib/commands/count-min-sketch/INITBYDIM.spec.ts b/packages/bloom/lib/commands/count-min-sketch/INITBYDIM.spec.ts new file mode 100644 index 00000000000..2a9014b765a --- /dev/null +++ b/packages/bloom/lib/commands/count-min-sketch/INITBYDIM.spec.ts @@ -0,0 +1,19 @@ +import { strict as assert } from 'assert'; +import testUtils, { GLOBAL } from '../../test-utils'; +import { transformArguments } from './INITBYDIM'; + +describe('CMS INITBYDIM', () => { + it('transformArguments', () => { + assert.deepEqual( + transformArguments('key', 1000, 5), + ['CMS.INITBYDIM', 'key', '1000', '5'] + ); + }); + + testUtils.testWithClient('client.cms.initByDim', async client => { + assert.equal( + await client.cms.initByDim('key', 1000, 5), + 'OK' + ); + }, GLOBAL.SERVERS.OPEN); +}); diff --git a/packages/bloom/lib/commands/count-min-sketch/INITBYDIM.ts b/packages/bloom/lib/commands/count-min-sketch/INITBYDIM.ts new file mode 100644 index 00000000000..4ec6cedd9ea --- /dev/null +++ b/packages/bloom/lib/commands/count-min-sketch/INITBYDIM.ts @@ -0,0 +1,7 @@ +export const FIRST_KEY_INDEX = 1; + +export function transformArguments(key: string, width: number, depth: number): Array { + return ['CMS.INITBYDIM', key, width.toString(), depth.toString()]; +} + +export declare function transformReply(): 'OK'; diff --git a/packages/bloom/lib/commands/count-min-sketch/INITBYPROB.spec.ts b/packages/bloom/lib/commands/count-min-sketch/INITBYPROB.spec.ts new file mode 100644 index 00000000000..004d3df14ef --- /dev/null +++ b/packages/bloom/lib/commands/count-min-sketch/INITBYPROB.spec.ts @@ -0,0 +1,19 @@ +import { strict as assert } from 'assert'; +import testUtils, { GLOBAL } from '../../test-utils'; +import { transformArguments } from './INITBYPROB'; + +describe('CMS INITBYPROB', () => { + it('transformArguments', () => { + assert.deepEqual( + transformArguments('key', 0.001, 0.01), + ['CMS.INITBYPROB', 'key', '0.001', '0.01'] + ); + }); + + testUtils.testWithClient('client.cms.initByProb', async client => { + assert.equal( + await client.cms.initByProb('key', 0.001, 0.01), + 'OK' + ); + }, GLOBAL.SERVERS.OPEN); +}); diff --git a/packages/bloom/lib/commands/count-min-sketch/INITBYPROB.ts b/packages/bloom/lib/commands/count-min-sketch/INITBYPROB.ts new file mode 100644 index 00000000000..7f0256515fb --- /dev/null +++ b/packages/bloom/lib/commands/count-min-sketch/INITBYPROB.ts @@ -0,0 +1,7 @@ +export const FIRST_KEY_INDEX = 1; + +export function transformArguments(key: string, error: number, probability: number): Array { + return ['CMS.INITBYPROB', key, error.toString(), probability.toString()]; +} + +export declare function transformReply(): 'OK'; diff --git a/packages/bloom/lib/commands/count-min-sketch/MERGE.spec.ts b/packages/bloom/lib/commands/count-min-sketch/MERGE.spec.ts new file mode 100644 index 00000000000..cf234e5734f --- /dev/null +++ b/packages/bloom/lib/commands/count-min-sketch/MERGE.spec.ts @@ -0,0 +1,36 @@ +import { strict as assert } from 'assert'; +import testUtils, { GLOBAL } from '../../test-utils'; +import { transformArguments } from './MERGE'; + +describe('CMS MERGE', () => { + describe('transformArguments', () => { + it('without WEIGHTS', () => { + assert.deepEqual( + transformArguments('dest', ['src']), + ['CMS.MERGE', 'dest', '1', 'src'] + ); + }); + + it('with WEIGHTS', () => { + assert.deepEqual( + transformArguments('dest', [{ + name: 'src', + weight: 1 + }]), + ['CMS.MERGE', 'dest', '1', 'src', 'WEIGHTS', '1'] + ); + }); + }); + + testUtils.testWithClient('client.cms.merge', async client => { + await Promise.all([ + client.cms.initByDim('src', 1000, 5), + client.cms.initByDim('dest', 1000, 5), + ]); + + assert.equal( + await client.cms.merge('dest', ['src']), + 'OK' + ); + }, GLOBAL.SERVERS.OPEN); +}); diff --git a/packages/bloom/lib/commands/count-min-sketch/MERGE.ts b/packages/bloom/lib/commands/count-min-sketch/MERGE.ts new file mode 100644 index 00000000000..6cca4e797cd --- /dev/null +++ b/packages/bloom/lib/commands/count-min-sketch/MERGE.ts @@ -0,0 +1,37 @@ +export const FIRST_KEY_INDEX = 1; + +interface Sketch { + name: string; + weight: number; +} + +type Sketches = Array | Array; + +export function transformArguments(dest: string, src: Sketches): Array { + const args = [ + 'CMS.MERGE', + dest, + src.length.toString() + ]; + + if (isStringSketches(src)) { + args.push(...src); + } else { + for (const sketch of src) { + args.push(sketch.name); + } + + args.push('WEIGHTS'); + for (const sketch of src) { + args.push(sketch.weight.toString()); + } + } + + return args; +} + +function isStringSketches(src: Sketches): src is Array { + return typeof src[0] === 'string'; +} + +export declare function transformReply(): 'OK'; diff --git a/packages/bloom/lib/commands/count-min-sketch/QUERY.spec.ts b/packages/bloom/lib/commands/count-min-sketch/QUERY.spec.ts new file mode 100644 index 00000000000..d391ab838be --- /dev/null +++ b/packages/bloom/lib/commands/count-min-sketch/QUERY.spec.ts @@ -0,0 +1,22 @@ +import { strict as assert } from 'assert'; +import testUtils, { GLOBAL } from '../../test-utils'; +import { transformArguments } from './QUERY'; + +describe('CMS QUERY', () => { + it('transformArguments', () => { + assert.deepEqual( + transformArguments('key', 'item'), + ['CMS.QUERY', 'key', 'item'] + ); + }); + + testUtils.testWithClient('client.cms.query', async client => { + await client.cms.initByDim('key', 1000, 5); + + assert.deepEqual( + await client.cms.query('key', 'item'), + [0] + ); + + }, GLOBAL.SERVERS.OPEN); +}); diff --git a/packages/bloom/lib/commands/count-min-sketch/QUERY.ts b/packages/bloom/lib/commands/count-min-sketch/QUERY.ts new file mode 100644 index 00000000000..4cdf8d6cc25 --- /dev/null +++ b/packages/bloom/lib/commands/count-min-sketch/QUERY.ts @@ -0,0 +1,15 @@ +import { RedisCommandArguments } from '@node-redis/client/dist/lib/commands'; +import { pushVerdictArguments } from '@node-redis/client/dist/lib/commands/generic-transformers'; + +export const FIRST_KEY_INDEX = 1; + +export const IS_READ_ONLY = true; + +export function transformArguments( + key: string, + items: string | Array +): RedisCommandArguments { + return pushVerdictArguments(['CMS.QUERY', key], items); +} + +export declare function transformReply(): Array; diff --git a/packages/bloom/lib/commands/count-min-sketch/index.ts b/packages/bloom/lib/commands/count-min-sketch/index.ts new file mode 100644 index 00000000000..1d61734a8d0 --- /dev/null +++ b/packages/bloom/lib/commands/count-min-sketch/index.ts @@ -0,0 +1,21 @@ +import * as INCRBY from './INCRBY'; +import * as INFO from './INFO'; +import * as INITBYDIM from './INITBYDIM'; +import * as INITBYPROB from './INITBYPROB'; +import * as MERGE from './MERGE'; +import * as QUERY from './QUERY'; + +export default { + INCRBY, + incrBy: INCRBY, + INFO, + info: INFO, + INITBYDIM, + initByDim: INITBYDIM, + INITBYPROB, + initByProb: INITBYPROB, + MERGE, + merge: MERGE, + QUERY, + query: QUERY +}; diff --git a/packages/bloom/lib/commands/cuckoo/ADD.spec.ts b/packages/bloom/lib/commands/cuckoo/ADD.spec.ts new file mode 100644 index 00000000000..f2c029fad3d --- /dev/null +++ b/packages/bloom/lib/commands/cuckoo/ADD.spec.ts @@ -0,0 +1,19 @@ +import { strict as assert } from 'assert'; +import testUtils, { GLOBAL } from '../../test-utils'; +import { transformArguments, transformReply } from './ADD'; + +describe('CF ADD', () => { + it('transformArguments', () => { + assert.deepEqual( + transformArguments('key', 'item'), + ['CF.ADD', 'key', 'item'] + ); + }); + + testUtils.testWithClient('client.cf.add', async client => { + assert.equal( + await client.cf.add('key', 'item'), + true + ); + }, GLOBAL.SERVERS.OPEN); +}); diff --git a/packages/bloom/lib/commands/cuckoo/ADD.ts b/packages/bloom/lib/commands/cuckoo/ADD.ts new file mode 100644 index 00000000000..d24f1cfd7a9 --- /dev/null +++ b/packages/bloom/lib/commands/cuckoo/ADD.ts @@ -0,0 +1,7 @@ +export const FIRST_KEY_INDEX = 1; + +export function transformArguments(key: string, item: string): Array { + return ['CF.ADD', key, item]; +} + +export { transformBooleanReply as transformReply } from '@node-redis/client/dist/lib/commands/generic-transformers'; diff --git a/packages/bloom/lib/commands/cuckoo/ADDNX.spec.ts b/packages/bloom/lib/commands/cuckoo/ADDNX.spec.ts new file mode 100644 index 00000000000..ddd9f922b13 --- /dev/null +++ b/packages/bloom/lib/commands/cuckoo/ADDNX.spec.ts @@ -0,0 +1,21 @@ +import { strict as assert } from 'assert'; +import testUtils, { GLOBAL } from '../../test-utils'; +import { transformArguments } from './ADDNX'; + +describe('CF ADDNX', () => { + describe('transformArguments', () => { + it('basic add', () => { + assert.deepEqual( + transformArguments('key', 'item'), + ['CF.ADDNX', 'key', 'item'] + ); + }); + }); + + testUtils.testWithClient('client.cf.add', async client => { + assert.equal( + await client.cf.addNX('key', 'item'), + true + ); + }, GLOBAL.SERVERS.OPEN); +}); diff --git a/packages/bloom/lib/commands/cuckoo/ADDNX.ts b/packages/bloom/lib/commands/cuckoo/ADDNX.ts new file mode 100644 index 00000000000..238d156700c --- /dev/null +++ b/packages/bloom/lib/commands/cuckoo/ADDNX.ts @@ -0,0 +1,7 @@ +export const FIRST_KEY_INDEX = 1; + +export function transformArguments(key: string, item: string): Array { + return ['CF.ADDNX', key, item]; +} + +export { transformBooleanReply as transformReply } from '@node-redis/client/dist/lib/commands/generic-transformers'; diff --git a/packages/bloom/lib/commands/cuckoo/COUNT.spec.ts b/packages/bloom/lib/commands/cuckoo/COUNT.spec.ts new file mode 100644 index 00000000000..29f5b415935 --- /dev/null +++ b/packages/bloom/lib/commands/cuckoo/COUNT.spec.ts @@ -0,0 +1,19 @@ +import { strict as assert } from 'assert'; +import testUtils, { GLOBAL } from '../../test-utils'; +import { transformArguments } from './COUNT'; + +describe('CF COUNT', () => { + it('transformArguments', () => { + assert.deepEqual( + transformArguments('key', 'item'), + ['CF.COUNT', 'key', 'item'] + ); + }); + + testUtils.testWithClient('client.cf.count', async client => { + assert.equal( + await client.cf.count('key', 'item'), + 0 + ); + }, GLOBAL.SERVERS.OPEN); +}); diff --git a/packages/bloom/lib/commands/cuckoo/COUNT.ts b/packages/bloom/lib/commands/cuckoo/COUNT.ts new file mode 100644 index 00000000000..c9f3e28b38a --- /dev/null +++ b/packages/bloom/lib/commands/cuckoo/COUNT.ts @@ -0,0 +1,7 @@ +export const FIRST_KEY_INDEX = 1; + +export function transformArguments(key: string, item: string): Array { + return ['CF.COUNT', key, item]; +} + +export declare function transformReply(): number; diff --git a/packages/bloom/lib/commands/cuckoo/DEL.spec.ts b/packages/bloom/lib/commands/cuckoo/DEL.spec.ts new file mode 100644 index 00000000000..03da65881c1 --- /dev/null +++ b/packages/bloom/lib/commands/cuckoo/DEL.spec.ts @@ -0,0 +1,21 @@ +import { strict as assert } from 'assert'; +import testUtils, { GLOBAL } from '../../test-utils'; +import { transformArguments } from './DEL'; + +describe('CF DEL', () => { + it('transformArguments', () => { + assert.deepEqual( + transformArguments('key', 'item'), + ['CF.DEL', 'key', 'item'] + ); + }); + + testUtils.testWithClient('client.cf.del', async client => { + await client.cf.reserve('key', 4); + + assert.equal( + await client.cf.del('key', 'item'), + false + ); + }, GLOBAL.SERVERS.OPEN); +}); diff --git a/packages/bloom/lib/commands/cuckoo/DEL.ts b/packages/bloom/lib/commands/cuckoo/DEL.ts new file mode 100644 index 00000000000..d621bd0a0f3 --- /dev/null +++ b/packages/bloom/lib/commands/cuckoo/DEL.ts @@ -0,0 +1,7 @@ +export const FIRST_KEY_INDEX = 1; + +export function transformArguments(key: string, item: string): Array { + return ['CF.DEL', key, item]; +} + +export { transformBooleanReply as transformReply } from '@node-redis/client/dist/lib/commands/generic-transformers'; diff --git a/packages/bloom/lib/commands/cuckoo/EXISTS.spec.ts b/packages/bloom/lib/commands/cuckoo/EXISTS.spec.ts new file mode 100644 index 00000000000..e281bde6d8a --- /dev/null +++ b/packages/bloom/lib/commands/cuckoo/EXISTS.spec.ts @@ -0,0 +1,19 @@ +import { strict as assert } from 'assert'; +import testUtils, { GLOBAL } from '../../test-utils'; +import { transformArguments } from './EXISTS'; + +describe('CF EXISTS', () => { + it('transformArguments', () => { + assert.deepEqual( + transformArguments('key', 'item'), + ['CF.EXISTS', 'key', 'item'] + ); + }); + + testUtils.testWithClient('client.cf.exists', async client => { + assert.equal( + await client.cf.exists('key', 'item'), + false + ); + }, GLOBAL.SERVERS.OPEN); +}); diff --git a/packages/bloom/lib/commands/cuckoo/EXISTS.ts b/packages/bloom/lib/commands/cuckoo/EXISTS.ts new file mode 100644 index 00000000000..0a43ae55ff4 --- /dev/null +++ b/packages/bloom/lib/commands/cuckoo/EXISTS.ts @@ -0,0 +1,9 @@ +export const FIRST_KEY_INDEX = 1; + +export const IS_READ_ONLY = true; + +export function transformArguments(key: string, item: string): Array { + return ['CF.EXISTS', key, item]; +} + +export { transformBooleanReply as transformReply } from '@node-redis/client/dist/lib/commands/generic-transformers'; diff --git a/packages/bloom/lib/commands/cuckoo/INFO.spec.ts b/packages/bloom/lib/commands/cuckoo/INFO.spec.ts new file mode 100644 index 00000000000..c2ac5de6fe0 --- /dev/null +++ b/packages/bloom/lib/commands/cuckoo/INFO.spec.ts @@ -0,0 +1,27 @@ +import { strict as assert } from 'assert'; +import testUtils, { GLOBAL } from '../../test-utils'; +import { transformArguments } from './INFO'; + +describe('CF INFO', () => { + it('transformArguments', () => { + assert.deepEqual( + transformArguments('cuckoo'), + ['CF.INFO', 'cuckoo'] + ); + }); + + testUtils.testWithClient('client.cf.info', async client => { + await client.cf.reserve('key', 4); + + const info = await client.cf.info('key'); + assert.equal(typeof info, 'object'); + assert.equal(typeof info.size, 'number'); + assert.equal(typeof info.numberOfBuckets, 'number'); + assert.equal(typeof info.numberOfFilters, 'number'); + assert.equal(typeof info.numberOfInsertedItems, 'number'); + assert.equal(typeof info.numberOfDeletedItems, 'number'); + assert.equal(typeof info.bucketSize, 'number'); + assert.equal(typeof info.expansionRate, 'number'); + assert.equal(typeof info.maxIteration, 'number'); + }, GLOBAL.SERVERS.OPEN); +}); diff --git a/packages/bloom/lib/commands/cuckoo/INFO.ts b/packages/bloom/lib/commands/cuckoo/INFO.ts new file mode 100644 index 00000000000..04d6954e37a --- /dev/null +++ b/packages/bloom/lib/commands/cuckoo/INFO.ts @@ -0,0 +1,50 @@ +export const FIRST_KEY_INDEX = 1; + +export const IS_READ_ONLY = true; + +export function transformArguments(key: string): Array { + return ['CF.INFO', key]; +} + +export type InfoRawReply = [ + _: string, + size: number, + _: string, + numberOfBuckets: number, + _: string, + numberOfFilters: number, + _: string, + numberOfInsertedItems: number, + _: string, + numberOfDeletedItems: number, + _: string, + bucketSize: number, + _: string, + expansionRate: number, + _: string, + maxIteration: number +]; + +export interface InfoReply { + size: number; + numberOfBuckets: number; + numberOfFilters: number; + numberOfInsertedItems: number; + numberOfDeletedItems: number; + bucketSize: number; + expansionRate: number; + maxIteration: number; +} + +export function transformReply(reply: InfoRawReply): InfoReply { + return { + size: reply[1], + numberOfBuckets: reply[3], + numberOfFilters: reply[5], + numberOfInsertedItems: reply[7], + numberOfDeletedItems: reply[9], + bucketSize: reply[11], + expansionRate: reply[13], + maxIteration: reply[15] + }; +} diff --git a/packages/bloom/lib/commands/cuckoo/INSERT.spec.ts b/packages/bloom/lib/commands/cuckoo/INSERT.spec.ts new file mode 100644 index 00000000000..9b56b86a6b7 --- /dev/null +++ b/packages/bloom/lib/commands/cuckoo/INSERT.spec.ts @@ -0,0 +1,22 @@ +import { strict as assert } from 'assert'; +import testUtils, { GLOBAL } from '../../test-utils'; +import { transformArguments } from './INSERT'; + +describe('CF INSERT', () => { + it('transformArguments', () => { + assert.deepEqual( + transformArguments('key', 'item', { + CAPACITY: 100, + NOCREATE: true + }), + ['CF.INSERT', 'key', 'CAPACITY', '100', 'NOCREATE', 'ITEMS', 'item'] + ); + }); + + testUtils.testWithClient('client.cf.insert', async client => { + assert.deepEqual( + await client.cf.insert('key', 'item'), + [true] + ); + }, GLOBAL.SERVERS.OPEN); +}); diff --git a/packages/bloom/lib/commands/cuckoo/INSERT.ts b/packages/bloom/lib/commands/cuckoo/INSERT.ts new file mode 100644 index 00000000000..c2e56b5c35c --- /dev/null +++ b/packages/bloom/lib/commands/cuckoo/INSERT.ts @@ -0,0 +1,18 @@ +import { RedisCommandArguments } from '@node-redis/client/dist/lib/commands'; +import { InsertOptions, pushInsertOptions } from "."; + +export const FIRST_KEY_INDEX = 1; + +export function transformArguments( + key: string, + items: string | Array, + options?: InsertOptions +): RedisCommandArguments { + return pushInsertOptions( + ['CF.INSERT', key], + items, + options + ); +} + +export { transformBooleanArrayReply as transformReply } from '@node-redis/client/dist/lib/commands/generic-transformers'; diff --git a/packages/bloom/lib/commands/cuckoo/INSERTNX.spec.ts b/packages/bloom/lib/commands/cuckoo/INSERTNX.spec.ts new file mode 100644 index 00000000000..7b1d974e5a6 --- /dev/null +++ b/packages/bloom/lib/commands/cuckoo/INSERTNX.spec.ts @@ -0,0 +1,22 @@ +import { strict as assert } from 'assert'; +import testUtils, { GLOBAL } from '../../test-utils'; +import { transformArguments } from './INSERTNX'; + +describe('CF INSERTNX', () => { + it('transformArguments', () => { + assert.deepEqual( + transformArguments('key', 'item', { + CAPACITY: 100, + NOCREATE: true + }), + ['CF.INSERTNX', 'key', 'CAPACITY', '100', 'NOCREATE', 'ITEMS', 'item'] + ); + }); + + testUtils.testWithClient('client.cf.insertnx', async client => { + assert.deepEqual( + await client.cf.insertNX('key', 'item'), + [true] + ); + }, GLOBAL.SERVERS.OPEN); +}); diff --git a/packages/bloom/lib/commands/cuckoo/INSERTNX.ts b/packages/bloom/lib/commands/cuckoo/INSERTNX.ts new file mode 100644 index 00000000000..e7104ea4b17 --- /dev/null +++ b/packages/bloom/lib/commands/cuckoo/INSERTNX.ts @@ -0,0 +1,18 @@ +import { RedisCommandArguments } from '@node-redis/client/dist/lib/commands'; +import { InsertOptions, pushInsertOptions } from "."; + +export const FIRST_KEY_INDEX = 1; + +export function transformArguments( + key: string, + items: string | Array, + options?: InsertOptions +): RedisCommandArguments { + return pushInsertOptions( + ['CF.INSERTNX', key], + items, + options + ); +} + +export { transformBooleanArrayReply as transformReply } from '@node-redis/client/dist/lib/commands/generic-transformers'; diff --git a/packages/bloom/lib/commands/cuckoo/LOADCHUNK.spec.ts b/packages/bloom/lib/commands/cuckoo/LOADCHUNK.spec.ts new file mode 100644 index 00000000000..68dff468d47 --- /dev/null +++ b/packages/bloom/lib/commands/cuckoo/LOADCHUNK.spec.ts @@ -0,0 +1,11 @@ +import { strict as assert } from 'assert'; +import { transformArguments } from './LOADCHUNK'; + +describe('CF LOADCHUNK', () => { + it('transformArguments', () => { + assert.deepEqual( + transformArguments('item', 0, ''), + ['CF.LOADCHUNK', 'item', '0', ''] + ); + }); +}); diff --git a/packages/bloom/lib/commands/cuckoo/LOADCHUNK.ts b/packages/bloom/lib/commands/cuckoo/LOADCHUNK.ts new file mode 100644 index 00000000000..5d221099789 --- /dev/null +++ b/packages/bloom/lib/commands/cuckoo/LOADCHUNK.ts @@ -0,0 +1,7 @@ +export const FIRST_KEY_INDEX = 1; + +export function transformArguments(key: string, iterator: number, chunk: string): Array { + return ['CF.LOADCHUNK', key, iterator.toString(), chunk]; +} + +export declare function transformReply(): 'OK'; diff --git a/packages/bloom/lib/commands/cuckoo/RESERVE.spec.ts b/packages/bloom/lib/commands/cuckoo/RESERVE.spec.ts new file mode 100644 index 00000000000..3145a222c5e --- /dev/null +++ b/packages/bloom/lib/commands/cuckoo/RESERVE.spec.ts @@ -0,0 +1,48 @@ +import { strict as assert } from 'assert'; +import testUtils, { GLOBAL } from '../../test-utils'; +import { transformArguments } from './RESERVE'; + +describe('CF RESERVE', () => { + describe('transformArguments', () => { + it('simple', () => { + assert.deepEqual( + transformArguments('key', 4), + ['CF.RESERVE', 'key', '4'] + ); + }); + + it('with EXPANSION', () => { + assert.deepEqual( + transformArguments('key', 4, { + EXPANSION: 1 + }), + ['CF.RESERVE', 'key', '4', 'EXPANSION', '1'] + ); + }); + + it('with BUCKETSIZE', () => { + assert.deepEqual( + transformArguments('key', 4, { + BUCKETSIZE: 2 + }), + ['CF.RESERVE', 'key', '4', 'BUCKETSIZE', '2'] + ); + }); + + it('with MAXITERATIONS', () => { + assert.deepEqual( + transformArguments('key', 4, { + MAXITERATIONS: 1 + }), + ['CF.RESERVE', 'key', '4', 'MAXITERATIONS', '1'] + ); + }); + }); + + testUtils.testWithClient('client.cf.reserve', async client => { + assert.equal( + await client.cf.reserve('key', 4), + 'OK' + ); + }, GLOBAL.SERVERS.OPEN); +}); diff --git a/packages/bloom/lib/commands/cuckoo/RESERVE.ts b/packages/bloom/lib/commands/cuckoo/RESERVE.ts new file mode 100644 index 00000000000..114c1fdf441 --- /dev/null +++ b/packages/bloom/lib/commands/cuckoo/RESERVE.ts @@ -0,0 +1,31 @@ +export const FIRST_KEY_INDEX = 1; + +interface ReserveOptions { + BUCKETSIZE?: number; + MAXITERATIONS?: number; + EXPANSION?: number; +} + +export function transformArguments( + key: string, + capacity: number, + options?: ReserveOptions +): Array { + const args = ['CF.RESERVE', key, capacity.toString()]; + + if (options?.BUCKETSIZE) { + args.push('BUCKETSIZE', options.BUCKETSIZE.toString()); + } + + if (options?.MAXITERATIONS) { + args.push('MAXITERATIONS', options.MAXITERATIONS.toString()); + } + + if (options?.EXPANSION) { + args.push('EXPANSION', options.EXPANSION.toString()); + } + + return args; +} + +export declare function transformReply(): 'OK'; diff --git a/packages/bloom/lib/commands/cuckoo/SCANDUMP.spec.ts b/packages/bloom/lib/commands/cuckoo/SCANDUMP.spec.ts new file mode 100644 index 00000000000..43b812999f3 --- /dev/null +++ b/packages/bloom/lib/commands/cuckoo/SCANDUMP.spec.ts @@ -0,0 +1,11 @@ +import { strict as assert } from 'assert'; +import { transformArguments } from './SCANDUMP'; + +describe('CF SCANDUMP', () => { + it('transformArguments', () => { + assert.deepEqual( + transformArguments('key', 0), + ['CF.SCANDUMP', 'key', '0'] + ); + }); +}); diff --git a/packages/bloom/lib/commands/cuckoo/SCANDUMP.ts b/packages/bloom/lib/commands/cuckoo/SCANDUMP.ts new file mode 100644 index 00000000000..dcabadb710e --- /dev/null +++ b/packages/bloom/lib/commands/cuckoo/SCANDUMP.ts @@ -0,0 +1,22 @@ +export const FIRST_KEY_INDEX = 1; + +export function transformArguments(key: string, iterator: number): Array { + return ['CF.SCANDUMP', key, iterator.toString()]; +} + +type ScanDumpRawReply = [ + iterator: number, + chunk: string +]; + +interface ScanDumpReply { + iterator: number; + chunk: string; +} + +export function transformReply([iterator, chunk]: ScanDumpRawReply): ScanDumpReply { + return { + iterator, + chunk + }; +} diff --git a/packages/bloom/lib/commands/cuckoo/index.spec.ts b/packages/bloom/lib/commands/cuckoo/index.spec.ts new file mode 100644 index 00000000000..94f3a0ae281 --- /dev/null +++ b/packages/bloom/lib/commands/cuckoo/index.spec.ts @@ -0,0 +1,48 @@ +import { strict as assert } from 'assert'; +import { pushInsertOptions } from '.'; + +describe('pushInsertOptions', () => { + describe('single item', () => { + it('single item', () => { + assert.deepEqual( + pushInsertOptions([], 'item'), + ['ITEMS', 'item'] + ); + }); + + it('multiple items', () => { + assert.deepEqual( + pushInsertOptions([], ['1', '2']), + ['ITEMS', '1', '2'] + ); + }); + }); + + it('with CAPACITY', () => { + assert.deepEqual( + pushInsertOptions([], 'item', { + CAPACITY: 100 + }), + ['CAPACITY', '100', 'ITEMS', 'item'] + ); + }); + + it('with NOCREATE', () => { + assert.deepEqual( + pushInsertOptions([], 'item', { + NOCREATE: true + }), + ['NOCREATE', 'ITEMS', 'item'] + ); + }); + + it('with CAPACITY and NOCREATE', () => { + assert.deepEqual( + pushInsertOptions([], 'item', { + CAPACITY: 100, + NOCREATE: true + }), + ['CAPACITY', '100', 'NOCREATE', 'ITEMS', 'item'] + ); + }); +}); diff --git a/packages/bloom/lib/commands/cuckoo/index.ts b/packages/bloom/lib/commands/cuckoo/index.ts new file mode 100644 index 00000000000..71137ceaaf4 --- /dev/null +++ b/packages/bloom/lib/commands/cuckoo/index.ts @@ -0,0 +1,62 @@ + +import * as ADD from './ADD'; +import * as ADDNX from './ADDNX'; +import * as COUNT from './COUNT'; +import * as DEL from './DEL'; +import * as EXISTS from './EXISTS'; +import * as INFO from './INFO'; +import * as INSERT from './INSERT'; +import * as INSERTNX from './INSERTNX'; +import * as LOADCHUNK from './LOADCHUNK'; +import * as RESERVE from './RESERVE'; +import * as SCANDUMP from './SCANDUMP'; +import { pushVerdictArguments } from '@node-redis/client/lib/commands/generic-transformers'; +import { RedisCommandArguments } from '@node-redis/client/dist/lib/commands'; + +export default { + ADD, + add: ADD, + ADDNX, + addNX: ADDNX, + COUNT, + count: COUNT, + DEL, + del: DEL, + EXISTS, + exists: EXISTS, + INFO, + info: INFO, + INSERT, + insert: INSERT, + INSERTNX, + insertNX: INSERTNX, + LOADCHUNK, + loadChunk: LOADCHUNK, + RESERVE, + reserve: RESERVE, + SCANDUMP, + scanDump: SCANDUMP +}; + +export interface InsertOptions { + CAPACITY?: number; + NOCREATE?: true; +} + +export function pushInsertOptions( + args: RedisCommandArguments, + items: string | Array, + options?: InsertOptions +): RedisCommandArguments { + if (options?.CAPACITY) { + args.push('CAPACITY'); + args.push(options.CAPACITY.toString()); + } + + if (options?.NOCREATE) { + args.push('NOCREATE'); + } + + args.push('ITEMS'); + return pushVerdictArguments(args, items); +} diff --git a/packages/bloom/lib/commands/index.ts b/packages/bloom/lib/commands/index.ts new file mode 100644 index 00000000000..665664a75b0 --- /dev/null +++ b/packages/bloom/lib/commands/index.ts @@ -0,0 +1,11 @@ +import Bloom from './bloom'; +import CountMinSketch from './count-min-sketch'; +import Cuckoo from './cuckoo'; +import TopK from './top-k'; + +export default { + bf: Bloom, + cms: CountMinSketch, + cf: Cuckoo, + topK: TopK +}; diff --git a/packages/bloom/lib/commands/top-k/ADD.spec.ts b/packages/bloom/lib/commands/top-k/ADD.spec.ts new file mode 100644 index 00000000000..149007f81d0 --- /dev/null +++ b/packages/bloom/lib/commands/top-k/ADD.spec.ts @@ -0,0 +1,22 @@ +import { strict as assert } from 'assert'; +import testUtils, { GLOBAL } from '../../test-utils'; +import { transformArguments } from './ADD'; + +describe('TOPK ADD', () => { + it('transformArguments', () => { + assert.deepEqual( + transformArguments('key', 'item'), + ['TOPK.ADD', 'key', 'item'] + ); + }); + + testUtils.testWithClient('client.topK.add', async client => { + await client.topK.reserve('topK', 3); + + assert.deepEqual( + await client.topK.add('topK', 'item'), + [null] + ); + + }, GLOBAL.SERVERS.OPEN); +}); diff --git a/packages/bloom/lib/commands/top-k/ADD.ts b/packages/bloom/lib/commands/top-k/ADD.ts new file mode 100644 index 00000000000..250b75ae045 --- /dev/null +++ b/packages/bloom/lib/commands/top-k/ADD.ts @@ -0,0 +1,13 @@ +import { RedisCommandArguments } from '@node-redis/client/dist/lib/commands'; +import { pushVerdictArguments } from '@node-redis/client/dist/lib/commands/generic-transformers'; + +export const FIRST_KEY_INDEX = 1; + +export function transformArguments( + key: string, + items: string | Array +): RedisCommandArguments { + return pushVerdictArguments(['TOPK.ADD', key], items); +} + +export declare function transformReply(): Array; diff --git a/packages/bloom/lib/commands/top-k/COUNT.spec.ts b/packages/bloom/lib/commands/top-k/COUNT.spec.ts new file mode 100644 index 00000000000..318fc74c679 --- /dev/null +++ b/packages/bloom/lib/commands/top-k/COUNT.spec.ts @@ -0,0 +1,21 @@ +import { strict as assert } from 'assert'; +import testUtils, { GLOBAL } from '../../test-utils'; +import { transformArguments } from './COUNT'; + +describe('TOPK COUNT', () => { + it('transformArguments', () => { + assert.deepEqual( + transformArguments('key', 'item'), + ['TOPK.COUNT', 'key', 'item'] + ); + }); + + testUtils.testWithClient('client.topK.count', async client => { + await client.topK.reserve('key', 3); + + assert.deepEqual( + await client.topK.count('key', 'item'), + [0] + ); + }, GLOBAL.SERVERS.OPEN); +}); diff --git a/packages/bloom/lib/commands/top-k/COUNT.ts b/packages/bloom/lib/commands/top-k/COUNT.ts new file mode 100644 index 00000000000..854d50d8973 --- /dev/null +++ b/packages/bloom/lib/commands/top-k/COUNT.ts @@ -0,0 +1,15 @@ +import { RedisCommandArguments } from '@node-redis/client/dist/lib/commands'; +import { pushVerdictArguments } from '@node-redis/client/dist/lib/commands/generic-transformers'; + +export const FIRST_KEY_INDEX = 1; + +export const IS_READ_ONLY = true; + +export function transformArguments( + key: string, + items: string | Array +): RedisCommandArguments { + return pushVerdictArguments(['TOPK.COUNT', key], items); +} + +export declare function transformReply(): Array; diff --git a/packages/bloom/lib/commands/top-k/INCRBY.spec.ts b/packages/bloom/lib/commands/top-k/INCRBY.spec.ts new file mode 100644 index 00000000000..b23ca6e0ed1 --- /dev/null +++ b/packages/bloom/lib/commands/top-k/INCRBY.spec.ts @@ -0,0 +1,42 @@ +import { strict as assert } from 'assert'; +import testUtils, { GLOBAL } from '../../test-utils'; +import { transformArguments } from './INCRBY'; + +describe('TOPK INCRBY', () => { + describe('transformArguments', () => { + it('single item', () => { + assert.deepEqual( + transformArguments('key', { + item: 'item', + incrementBy: 1 + }), + ['TOPK.INCRBY', 'key', 'item', '1'] + ); + }); + + it('multiple items', () => { + assert.deepEqual( + transformArguments('key', [{ + item: 'a', + incrementBy: 1 + }, { + item: 'b', + incrementBy: 2 + }]), + ['TOPK.INCRBY', 'key', 'a', '1', 'b', '2'] + ); + }); + }); + + testUtils.testWithClient('client.topK.incrby', async client => { + await client.topK.reserve('key', 5); + + assert.deepEqual( + await client.topK.incrBy('key', { + item: 'item', + incrementBy: 1 + }), + [null] + ); + }, GLOBAL.SERVERS.OPEN); +}); diff --git a/packages/bloom/lib/commands/top-k/INCRBY.ts b/packages/bloom/lib/commands/top-k/INCRBY.ts new file mode 100644 index 00000000000..2533cb05594 --- /dev/null +++ b/packages/bloom/lib/commands/top-k/INCRBY.ts @@ -0,0 +1,29 @@ +export const FIRST_KEY_INDEX = 1; + +interface IncrByItem { + item: string; + incrementBy: number; +} + +export function transformArguments( + key: string, + items: IncrByItem | Array +): Array { + const args = ['TOPK.INCRBY', key]; + + if (Array.isArray(items)) { + for (const item of items) { + pushIncrByItem(args, item); + } + } else { + pushIncrByItem(args, items); + } + + return args; +} + +function pushIncrByItem(args: Array, { item, incrementBy }: IncrByItem): void { + args.push(item, incrementBy.toString()); +} + +export declare function transformReply(): Array; diff --git a/packages/bloom/lib/commands/top-k/INFO.spec.ts b/packages/bloom/lib/commands/top-k/INFO.spec.ts new file mode 100644 index 00000000000..2741a58a8ba --- /dev/null +++ b/packages/bloom/lib/commands/top-k/INFO.spec.ts @@ -0,0 +1,23 @@ +import { strict as assert } from 'assert'; +import testUtils, { GLOBAL } from '../../test-utils'; +import { transformArguments } from './INFO'; + +describe('TOPK INFO', () => { + it('transformArguments', () => { + assert.deepEqual( + transformArguments('key'), + ['TOPK.INFO', 'key'] + ); + }); + + testUtils.testWithClient('client.topK.info', async client => { + await client.topK.reserve('key', 3); + + const info = await client.topK.info('key'); + assert.equal(typeof info, 'object'); + assert.equal(info.k, 3); + assert.equal(typeof info.width, 'number'); + assert.equal(typeof info.depth, 'number'); + assert.equal(typeof info.decay, 'number'); + }, GLOBAL.SERVERS.OPEN); +}); diff --git a/packages/bloom/lib/commands/top-k/INFO.ts b/packages/bloom/lib/commands/top-k/INFO.ts new file mode 100644 index 00000000000..8c9e8d432b3 --- /dev/null +++ b/packages/bloom/lib/commands/top-k/INFO.ts @@ -0,0 +1,34 @@ +export const FIRST_KEY_INDEX = 1; + +export const IS_READ_ONLY = true; + +export function transformArguments(key: string): Array { + return ['TOPK.INFO', key]; +} + +export type InfoRawReply = [ + _: string, + k: number, + _: string, + width: number, + _: string, + depth: number, + _: string, + decay: string +]; + +export interface InfoReply { + k: number, + width: number; + depth: number; + decay: number; +} + +export function transformReply(reply: InfoRawReply): InfoReply { + return { + k: reply[1], + width: reply[3], + depth: reply[5], + decay: Number(reply[7]) + }; +} diff --git a/packages/bloom/lib/commands/top-k/LIST.spec.ts b/packages/bloom/lib/commands/top-k/LIST.spec.ts new file mode 100644 index 00000000000..709ac7ffc39 --- /dev/null +++ b/packages/bloom/lib/commands/top-k/LIST.spec.ts @@ -0,0 +1,21 @@ +import { strict as assert } from 'assert'; +import testUtils, { GLOBAL } from '../../test-utils'; +import { transformArguments } from './LIST'; + +describe('TOPK LIST', () => { + it('transformArguments', () => { + assert.deepEqual( + transformArguments('key'), + ['TOPK.LIST', 'key'] + ); + }); + + testUtils.testWithClient('client.topK.list', async client => { + await client.topK.reserve('key', 3); + + assert.deepEqual( + await client.topK.list('key'), + [] + ); + }, GLOBAL.SERVERS.OPEN); +}); diff --git a/packages/bloom/lib/commands/top-k/LIST.ts b/packages/bloom/lib/commands/top-k/LIST.ts new file mode 100644 index 00000000000..d8c16545593 --- /dev/null +++ b/packages/bloom/lib/commands/top-k/LIST.ts @@ -0,0 +1,7 @@ +export const FIRST_KEY_INDEX = 1; + +export function transformArguments(key: string): Array { + return ['TOPK.LIST', key]; +} + +export declare function transformReply(): Array; diff --git a/packages/bloom/lib/commands/top-k/QUERY.spec.ts b/packages/bloom/lib/commands/top-k/QUERY.spec.ts new file mode 100644 index 00000000000..ada9e7e2e39 --- /dev/null +++ b/packages/bloom/lib/commands/top-k/QUERY.spec.ts @@ -0,0 +1,21 @@ +import { strict as assert } from 'assert'; +import testUtils, { GLOBAL } from '../../test-utils'; +import { transformArguments } from './QUERY'; + +describe('TOPK QUERY', () => { + it('transformArguments', () => { + assert.deepEqual( + transformArguments('key', 'item'), + ['TOPK.QUERY', 'key', 'item'] + ); + }); + + testUtils.testWithClient('client.cms.query', async client => { + await client.topK.reserve('key', 3); + + assert.deepEqual( + await client.topK.query('key', 'item'), + [0] + ); + }, GLOBAL.SERVERS.OPEN); +}); diff --git a/packages/bloom/lib/commands/top-k/QUERY.ts b/packages/bloom/lib/commands/top-k/QUERY.ts new file mode 100644 index 00000000000..7b261f35b80 --- /dev/null +++ b/packages/bloom/lib/commands/top-k/QUERY.ts @@ -0,0 +1,15 @@ +import { RedisCommandArguments } from '@node-redis/client/dist/lib/commands'; +import { pushVerdictArguments } from '@node-redis/client/dist/lib/commands/generic-transformers'; + +export const FIRST_KEY_INDEX = 1; + +export const IS_READ_ONLY = true; + +export function transformArguments( + key: string, + items: string | Array +): RedisCommandArguments { + return pushVerdictArguments(['TOPK.QUERY', key], items); +} + +export declare function transformReply(): Array; diff --git a/packages/bloom/lib/commands/top-k/RESERVE.spec.ts b/packages/bloom/lib/commands/top-k/RESERVE.spec.ts new file mode 100644 index 00000000000..54600c0e4f5 --- /dev/null +++ b/packages/bloom/lib/commands/top-k/RESERVE.spec.ts @@ -0,0 +1,32 @@ +import { strict as assert } from 'assert'; +import testUtils, { GLOBAL } from '../../test-utils'; +import { transformArguments } from './RESERVE'; + +describe('TOPK RESERVE', () => { + describe('transformArguments', () => { + it('simple', () => { + assert.deepEqual( + transformArguments('topK', 3), + ['TOPK.RESERVE', 'topK', '3'] + ); + }); + + it('with options', () => { + assert.deepEqual( + transformArguments('topK', 3, { + width: 8, + depth: 7, + decay: 0.9 + }), + ['TOPK.RESERVE', 'topK', '3', '8', '7', '0.9'] + ); + }); + }); + + testUtils.testWithClient('client.topK.reserve', async client => { + assert.equal( + await client.topK.reserve('topK', 3), + 'OK' + ); + }, GLOBAL.SERVERS.OPEN); +}); diff --git a/packages/bloom/lib/commands/top-k/RESERVE.ts b/packages/bloom/lib/commands/top-k/RESERVE.ts new file mode 100644 index 00000000000..6512db51e0b --- /dev/null +++ b/packages/bloom/lib/commands/top-k/RESERVE.ts @@ -0,0 +1,27 @@ +export const FIRST_KEY_INDEX = 1; + +interface ReserveOptions { + width: number; + depth: number; + decay: number; +} + +export function transformArguments( + key: string, + topK: number, + options?: ReserveOptions +): Array { + const args = ['TOPK.RESERVE', key, topK.toString()]; + + if (options) { + args.push( + options.width.toString(), + options.depth.toString(), + options.decay.toString() + ); + } + + return args; +} + +export declare function transformReply(): 'OK'; diff --git a/packages/bloom/lib/commands/top-k/index.ts b/packages/bloom/lib/commands/top-k/index.ts new file mode 100644 index 00000000000..05d6ba87eab --- /dev/null +++ b/packages/bloom/lib/commands/top-k/index.ts @@ -0,0 +1,24 @@ +import * as ADD from './ADD'; +import * as COUNT from './COUNT'; +import * as INCRBY from './INCRBY'; +import * as INFO from './INFO'; +import * as LIST from './LIST'; +import * as QUERY from './QUERY'; +import * as RESERVE from './RESERVE'; + +export default { + ADD, + add: ADD, + COUNT, + count: COUNT, + INCRBY, + incrBy: INCRBY, + INFO, + info: INFO, + LIST, + list: LIST, + QUERY, + query: QUERY, + RESERVE, + reserve: RESERVE +}; diff --git a/packages/bloom/lib/index.ts b/packages/bloom/lib/index.ts new file mode 100644 index 00000000000..bc0e103e8c8 --- /dev/null +++ b/packages/bloom/lib/index.ts @@ -0,0 +1 @@ +export { default } from './commands'; diff --git a/packages/bloom/lib/test-utils.ts b/packages/bloom/lib/test-utils.ts new file mode 100644 index 00000000000..7c5780a3cd4 --- /dev/null +++ b/packages/bloom/lib/test-utils.ts @@ -0,0 +1,19 @@ +import TestUtils from '@node-redis/test-utils'; +import RedisBloomModules from '.'; + +export default new TestUtils({ + dockerImageName: 'redislabs/rebloom', + dockerImageVersionArgument: 'redisbloom-version', + defaultDockerVersion: '2.2.9' +}); + +export const GLOBAL = { + SERVERS: { + OPEN: { + serverArguments: ['--loadmodule /usr/lib/redis/modules/redisbloom.so'], + clientOptions: { + modules: RedisBloomModules + } + } + } +}; diff --git a/packages/bloom/package.json b/packages/bloom/package.json new file mode 100644 index 00000000000..10855d64e4d --- /dev/null +++ b/packages/bloom/package.json @@ -0,0 +1,29 @@ +{ + "name": "@node-redis/bloom", + "version": "1.0.0", + "license": "MIT", + "main": "./dist/index.js", + "types": "./dist/index.d.ts", + "files": [ + "./dist" + ], + "scripts": { + "test": "nyc -r text-summary -r lcov mocha -r source-map-support/register -r ts-node/register './lib/**/*.spec.ts'", + "build": "tsc", + "documentation": "typedoc" + }, + "peerDependencies": { + "@node-redis/client": "^1.0.0" + }, + "devDependencies": { + "@istanbuljs/nyc-config-typescript": "^1.0.2", + "@node-redis/test-utils": "*", + "@types/node": "^17.0.1", + "nyc": "^15.1.0", + "release-it": "^14.11.8", + "source-map-support": "^0.5.21", + "ts-node": "^10.4.0", + "typedoc": "^0.22.10", + "typescript": "^4.5.4" + } +} diff --git a/packages/bloom/tsconfig.json b/packages/bloom/tsconfig.json new file mode 100644 index 00000000000..14fda1d8711 --- /dev/null +++ b/packages/bloom/tsconfig.json @@ -0,0 +1,9 @@ +{ + "extends": "../../tsconfig.base.json", + "compilerOptions": { + "outDir": "./dist" + }, + "include": [ + "./lib/**/*.ts" + ] +} diff --git a/packages/client/lib/client/index.spec.ts b/packages/client/lib/client/index.spec.ts index bf9fa7ae3e6..3b5e952c3bc 100644 --- a/packages/client/lib/client/index.spec.ts +++ b/packages/client/lib/client/index.spec.ts @@ -321,9 +321,9 @@ describe('Client', () => { testUtils.testWithClient('returnBuffers', async client => { assert.deepEqual( - await client.sendCommand(['PING'], RedisClient.commandOptions({ + await client.sendCommand(['PING'], { returnBuffers: true - }),), + }), Buffer.from('PONG') ); }, GLOBAL.SERVERS.OPEN); diff --git a/packages/client/lib/commands/index.ts b/packages/client/lib/commands/index.ts index c589770a947..d8cfe5332df 100644 --- a/packages/client/lib/commands/index.ts +++ b/packages/client/lib/commands/index.ts @@ -1,6 +1,5 @@ import { RedisScriptConfig, SHA1 } from '../lua-script'; - // https://github.com/Microsoft/TypeScript/issues/3496#issuecomment-128553540 // eslint-disable-next-line @typescript-eslint/no-empty-interface interface RedisCommandRawReplyArray extends Array {} From 611e4100c8205c564375e7bcf612a4effcd56cd9 Mon Sep 17 00:00:00 2001 From: leibale Date: Wed, 29 Dec 2021 12:15:30 -0500 Subject: [PATCH 1068/1748] fix bloom typedoc --- packages/bloom/tsconfig.json | 9 ++++++++- 1 file changed, 8 insertions(+), 1 deletion(-) diff --git a/packages/bloom/tsconfig.json b/packages/bloom/tsconfig.json index 14fda1d8711..19ef1005e3e 100644 --- a/packages/bloom/tsconfig.json +++ b/packages/bloom/tsconfig.json @@ -5,5 +5,12 @@ }, "include": [ "./lib/**/*.ts" - ] + ], + "typedocOptions": { + "entryPoints": [ + "./lib" + ], + "entryPointStrategy": "expand", + "out": "../../documentation/bloom" + } } From 74daee302338cf2fcc1bb48aeac02be828e0c6d7 Mon Sep 17 00:00:00 2001 From: leibale Date: Wed, 29 Dec 2021 14:28:22 -0500 Subject: [PATCH 1069/1748] fix #1801 --- packages/client/lib/client/index.spec.ts | 12 ++++++------ packages/client/lib/client/socket.ts | 2 +- 2 files changed, 7 insertions(+), 7 deletions(-) diff --git a/packages/client/lib/client/index.spec.ts b/packages/client/lib/client/index.spec.ts index 3b5e952c3bc..c5bfb32c034 100644 --- a/packages/client/lib/client/index.spec.ts +++ b/packages/client/lib/client/index.spec.ts @@ -721,8 +721,6 @@ describe('Client', () => { }); testUtils.testWithClient('client.disconnect', async client => { - await client.connect(); - const pingPromise = client.ping(), disconnectPromise = client.disconnect(); assert.equal(client.isOpen, false); @@ -731,8 +729,10 @@ describe('Client', () => { assert.doesNotReject(disconnectPromise), assert.rejects(client.ping(), ClientClosedError) ]); - }, { - ...GLOBAL.SERVERS.OPEN, - disableClientSetup: true - }); + }, GLOBAL.SERVERS.OPEN); + + testUtils.testWithClient('should be able to connect after disconnect (see #1801)', async client => { + await client.disconnect(); + await client.connect(); + }, GLOBAL.SERVERS.OPEN); }); diff --git a/packages/client/lib/client/socket.ts b/packages/client/lib/client/socket.ts index effb780a081..57ecf609d9f 100644 --- a/packages/client/lib/client/socket.ts +++ b/packages/client/lib/client/socket.ts @@ -170,7 +170,7 @@ export default class RedisSocket extends EventEmitter { .off('error', reject) .once('error', (err: Error) => this.#onSocketError(err)) .once('close', hadError => { - if (!hadError && this.#isOpen) { + if (!hadError && this.#isOpen && this.#socket === socket) { this.#onSocketError(new SocketClosedUnexpectedlyError()); } }) From 77022209bde327d6a2d004dd07153bb6140d0ae2 Mon Sep 17 00:00:00 2001 From: leibale Date: Wed, 29 Dec 2021 17:09:59 -0500 Subject: [PATCH 1070/1748] fix `returnBuffers`, add some tests --- .../lib/commands/bloom/LOADCHUNK.spec.ts | 17 +++++ .../bloom/lib/commands/bloom/SCANDUMP.spec.ts | 11 ++++ .../lib/commands/cuckoo/LOADCHUNK.spec.ts | 20 ++++++ .../bloom/lib/commands/cuckoo/LOADCHUNK.ts | 8 ++- .../lib/commands/cuckoo/SCANDUMP.spec.ts | 12 ++++ .../bloom/lib/commands/cuckoo/SCANDUMP.ts | 4 +- packages/client/lib/client/commands-queue.ts | 13 +++- packages/client/lib/client/index.ts | 2 + .../client/lib/commands/CLIENT_KILL.spec.ts | 13 ++++ packages/client/lib/commands/XPENDING.spec.ts | 62 ++++++++++++++----- packages/client/lib/commands/XPENDING.ts | 12 ++-- 11 files changed, 149 insertions(+), 25 deletions(-) diff --git a/packages/bloom/lib/commands/bloom/LOADCHUNK.spec.ts b/packages/bloom/lib/commands/bloom/LOADCHUNK.spec.ts index ea9f0acfa0d..19634cb4a78 100644 --- a/packages/bloom/lib/commands/bloom/LOADCHUNK.spec.ts +++ b/packages/bloom/lib/commands/bloom/LOADCHUNK.spec.ts @@ -1,4 +1,5 @@ import { strict as assert } from 'assert'; +import testUtils, { GLOBAL } from '../../test-utils'; import { transformArguments } from './LOADCHUNK'; describe('BF LOADCHUNK', () => { @@ -8,4 +9,20 @@ describe('BF LOADCHUNK', () => { ['BF.LOADCHUNK', 'key', '0', ''] ); }); + + testUtils.testWithClient('client.bf.loadChunk', async client => { + const [, { iterator, chunk }] = await Promise.all([ + client.bf.reserve('source', 0.01, 100), + client.bf.scanDump( + client.commandOptions({ returnBuffers: true }), + 'source', + 0 + ) + ]); + + assert.equal( + await client.bf.loadChunk('destination', iterator, chunk), + 'OK' + ); + }, GLOBAL.SERVERS.OPEN); }); diff --git a/packages/bloom/lib/commands/bloom/SCANDUMP.spec.ts b/packages/bloom/lib/commands/bloom/SCANDUMP.spec.ts index 83440167593..50119590482 100644 --- a/packages/bloom/lib/commands/bloom/SCANDUMP.spec.ts +++ b/packages/bloom/lib/commands/bloom/SCANDUMP.spec.ts @@ -1,4 +1,5 @@ import { strict as assert } from 'assert'; +import testUtils, { GLOBAL } from '../../test-utils'; import { transformArguments } from './SCANDUMP'; describe('BF SCANDUMP', () => { @@ -8,4 +9,14 @@ describe('BF SCANDUMP', () => { ['BF.SCANDUMP', 'key', '0'] ); }); + + testUtils.testWithClient('client.bf.scanDump', async client => { + const [, dump] = await Promise.all([ + client.bf.reserve('key', 0.01, 100), + client.bf.scanDump('key', 0) + ]); + assert.equal(typeof dump, 'object'); + assert.equal(typeof dump.iterator, 'number'); + assert.equal(typeof dump.chunk, 'string'); + }, GLOBAL.SERVERS.OPEN); }); diff --git a/packages/bloom/lib/commands/cuckoo/LOADCHUNK.spec.ts b/packages/bloom/lib/commands/cuckoo/LOADCHUNK.spec.ts index 68dff468d47..ca3d6f2f8f7 100644 --- a/packages/bloom/lib/commands/cuckoo/LOADCHUNK.spec.ts +++ b/packages/bloom/lib/commands/cuckoo/LOADCHUNK.spec.ts @@ -1,4 +1,5 @@ import { strict as assert } from 'assert'; +import testUtils, { GLOBAL } from '../../test-utils'; import { transformArguments } from './LOADCHUNK'; describe('CF LOADCHUNK', () => { @@ -8,4 +9,23 @@ describe('CF LOADCHUNK', () => { ['CF.LOADCHUNK', 'item', '0', ''] ); }); + + testUtils.testWithClient('client.cf.loadChunk', async client => { + const [,, { iterator, chunk }] = await Promise.all([ + client.cf.reserve('source', 4), + client.cf.add('source', 'item'), + client.cf.scanDump( + client.commandOptions({ returnBuffers: true }), + 'source', + 0 + ) + ]); + + assert.ok(Buffer.isBuffer(chunk)); + + assert.equal( + await client.cf.loadChunk('destination', iterator, chunk), + 'OK' + ); + }, GLOBAL.SERVERS.OPEN); }); diff --git a/packages/bloom/lib/commands/cuckoo/LOADCHUNK.ts b/packages/bloom/lib/commands/cuckoo/LOADCHUNK.ts index 5d221099789..5b739f67ccd 100644 --- a/packages/bloom/lib/commands/cuckoo/LOADCHUNK.ts +++ b/packages/bloom/lib/commands/cuckoo/LOADCHUNK.ts @@ -1,6 +1,12 @@ +import { RedisCommandArgument, RedisCommandArguments } from '@node-redis/client/dist/lib/commands'; + export const FIRST_KEY_INDEX = 1; -export function transformArguments(key: string, iterator: number, chunk: string): Array { +export function transformArguments( + key: string, + iterator: number, + chunk: RedisCommandArgument +): RedisCommandArguments { return ['CF.LOADCHUNK', key, iterator.toString(), chunk]; } diff --git a/packages/bloom/lib/commands/cuckoo/SCANDUMP.spec.ts b/packages/bloom/lib/commands/cuckoo/SCANDUMP.spec.ts index 43b812999f3..ec269c62aa5 100644 --- a/packages/bloom/lib/commands/cuckoo/SCANDUMP.spec.ts +++ b/packages/bloom/lib/commands/cuckoo/SCANDUMP.spec.ts @@ -1,4 +1,5 @@ import { strict as assert } from 'assert'; +import testUtils, { GLOBAL } from '../../test-utils'; import { transformArguments } from './SCANDUMP'; describe('CF SCANDUMP', () => { @@ -8,4 +9,15 @@ describe('CF SCANDUMP', () => { ['CF.SCANDUMP', 'key', '0'] ); }); + + testUtils.testWithClient('client.cf.scanDump', async client => { + await client.cf.reserve('key', 4); + assert.deepEqual( + await client.cf.scanDump('key', 0), + { + iterator: 0, + chunk: null + } + ); + }, GLOBAL.SERVERS.OPEN); }); diff --git a/packages/bloom/lib/commands/cuckoo/SCANDUMP.ts b/packages/bloom/lib/commands/cuckoo/SCANDUMP.ts index dcabadb710e..91476b49a7a 100644 --- a/packages/bloom/lib/commands/cuckoo/SCANDUMP.ts +++ b/packages/bloom/lib/commands/cuckoo/SCANDUMP.ts @@ -6,12 +6,12 @@ export function transformArguments(key: string, iterator: number): Array type ScanDumpRawReply = [ iterator: number, - chunk: string + chunk: string | null ]; interface ScanDumpReply { iterator: number; - chunk: string; + chunk: string | null; } export function transformReply([iterator, chunk]: ScanDumpRawReply): ScanDumpReply { diff --git a/packages/client/lib/client/commands-queue.ts b/packages/client/lib/client/commands-queue.ts index 3505ef8fa7a..13e37c4ccc6 100644 --- a/packages/client/lib/client/commands-queue.ts +++ b/packages/client/lib/client/commands-queue.ts @@ -360,11 +360,15 @@ export default class RedisCommandsQueue { return toSend?.args; } - parseResponse(data: Buffer): void { + #setReturnBuffers() { this.#parser.setReturnBuffers( !!this.#waitingForReply.head?.value.returnBuffers || !!this.#pubSubState?.subscribed ); + } + + parseResponse(data: Buffer): void { + this.#setReturnBuffers(); this.#parser.execute(data); } @@ -372,8 +376,12 @@ export default class RedisCommandsQueue { if (!this.#waitingForReply.length) { throw new Error('Got an unexpected reply from Redis'); } - return this.#waitingForReply.shift()!; + + const waitingForReply = this.#waitingForReply.shift()!; + this.#setReturnBuffers(); + return waitingForReply; } + flushWaitingForReply(err: Error): void { RedisCommandsQueue.#flushQueue(this.#waitingForReply, err); if (!this.#chainInExecution) { @@ -384,6 +392,7 @@ export default class RedisCommandsQueue { } this.#chainInExecution = undefined; } + flushAll(err: Error): void { RedisCommandsQueue.#flushQueue(this.#waitingForReply, err); RedisCommandsQueue.#flushQueue(this.#waitingToBeSent, err); diff --git a/packages/client/lib/client/index.ts b/packages/client/lib/client/index.ts index 2b801a54087..fe3ed36b75c 100644 --- a/packages/client/lib/client/index.ts +++ b/packages/client/lib/client/index.ts @@ -89,6 +89,8 @@ export default class RedisClient return commandOptions(options); } + commandOptions = RedisClient.commandOptions; + static extend(plugins?: RedisPlugins): InstantiableRedisClient { const Client = extendWithModulesAndScripts({ BaseClass: RedisClient, diff --git a/packages/client/lib/commands/CLIENT_KILL.spec.ts b/packages/client/lib/commands/CLIENT_KILL.spec.ts index 0c38a0fb162..2fe894f3610 100644 --- a/packages/client/lib/commands/CLIENT_KILL.spec.ts +++ b/packages/client/lib/commands/CLIENT_KILL.spec.ts @@ -93,5 +93,18 @@ describe('CLIENT KILL', () => { ); }); }); + + it('TYPE & SKIP_ME', () => { + assert.deepEqual( + transformArguments([ + { + filter: ClientKillFilters.TYPE, + type: 'master' + }, + ClientKillFilters.SKIP_ME + ]), + ['CLIENT', 'KILL', 'TYPE', 'master', 'SKIPME'] + ); + }); }); }); diff --git a/packages/client/lib/commands/XPENDING.spec.ts b/packages/client/lib/commands/XPENDING.spec.ts index 7eb12b40efe..af5c239e6c7 100644 --- a/packages/client/lib/commands/XPENDING.spec.ts +++ b/packages/client/lib/commands/XPENDING.spec.ts @@ -12,19 +12,53 @@ describe('XPENDING', () => { }); }); - testUtils.testWithClient('client.xPending', async client => { - await client.xGroupCreate('key', 'group', '$', { - MKSTREAM: true - }); + describe('client.xPending', () => { + testUtils.testWithClient('simple', async client => { + await Promise.all([ + client.xGroupCreate('key', 'group', '$', { + MKSTREAM: true + }), + client.xGroupCreateConsumer('key', 'group', 'consumer') + ]); + + assert.deepEqual( + await client.xPending('key', 'group'), + { + pending: 0, + firstId: null, + lastId: null, + consumers: null + } + ); + }, GLOBAL.SERVERS.OPEN); + + testUtils.testWithClient('with consumers', async client => { + const [,, id] = await Promise.all([ + client.xGroupCreate('key', 'group', '$', { + MKSTREAM: true + }), + client.xGroupCreateConsumer('key', 'group', 'consumer'), + client.xAdd('key', '*', { field: 'value' }), + client.xReadGroup('group', 'consumer', { + key: 'key', + id: '>' + }) + ]); + + assert.deepEqual( + await client.xPending('key', 'group'), + { + pending: 1, + firstId: id, + lastId: id, + consumers: [{ + name: 'consumer', + deliveriesCounter: 1 + }] + } + ); + }, GLOBAL.SERVERS.OPEN); + }); + - assert.deepEqual( - await client.xPending('key', 'group'), - { - pending: 0, - firstId: null, - lastId: null, - consumers: null - } - ); - }, GLOBAL.SERVERS.OPEN); }); diff --git a/packages/client/lib/commands/XPENDING.ts b/packages/client/lib/commands/XPENDING.ts index a6052adb0f2..ac56e429410 100644 --- a/packages/client/lib/commands/XPENDING.ts +++ b/packages/client/lib/commands/XPENDING.ts @@ -17,17 +17,17 @@ type XPendingRawReply = [ lastId: RedisCommandArgument | null, consumers: Array<[ name: RedisCommandArgument, - deliveriesCounter: number + deliveriesCounter: RedisCommandArgument ]> | null -] +]; interface XPendingReply { pending: number; firstId: RedisCommandArgument | null; - lastId: RedisCommandArgument | null + lastId: RedisCommandArgument | null; consumers: Array<{ - name: RedisCommandArgument, - deliveriesCounter: number + name: RedisCommandArgument; + deliveriesCounter: number; }> | null; } @@ -38,7 +38,7 @@ export function transformReply(reply: XPendingRawReply): XPendingReply { lastId: reply[2], consumers: reply[3] === null ? null : reply[3].map(([name, deliveriesCounter]) => ({ name, - deliveriesCounter + deliveriesCounter: Number(deliveriesCounter) })) }; } From 627eb78315c7f045ebb24da0e19d9274b289abd9 Mon Sep 17 00:00:00 2001 From: leibale Date: Wed, 29 Dec 2021 17:24:20 -0500 Subject: [PATCH 1071/1748] run XPEDNDING with consumers test on redis 6.2 and up, fix redis 6 tests --- .github/workflows/tests.yml | 4 ++-- packages/client/lib/commands/XPENDING.spec.ts | 16 +++++++--------- 2 files changed, 9 insertions(+), 11 deletions(-) diff --git a/.github/workflows/tests.yml b/.github/workflows/tests.yml index ad8b0332f8e..0ce929fbc5b 100644 --- a/.github/workflows/tests.yml +++ b/.github/workflows/tests.yml @@ -16,8 +16,8 @@ jobs: strategy: fail-fast: false matrix: - node-version: [12, 14, 16] - redis-version: [5, 6.0, 6.2] + node-version: ['12', '14', '16'] + redis-version: ['5', '6.0', '6.2'] steps: - uses: actions/checkout@v2.3.4 with: diff --git a/packages/client/lib/commands/XPENDING.spec.ts b/packages/client/lib/commands/XPENDING.spec.ts index af5c239e6c7..b1fef2a217f 100644 --- a/packages/client/lib/commands/XPENDING.spec.ts +++ b/packages/client/lib/commands/XPENDING.spec.ts @@ -14,12 +14,9 @@ describe('XPENDING', () => { describe('client.xPending', () => { testUtils.testWithClient('simple', async client => { - await Promise.all([ - client.xGroupCreate('key', 'group', '$', { - MKSTREAM: true - }), - client.xGroupCreateConsumer('key', 'group', 'consumer') - ]); + await client.xGroupCreate('key', 'group', '$', { + MKSTREAM: true + }); assert.deepEqual( await client.xPending('key', 'group'), @@ -57,8 +54,9 @@ describe('XPENDING', () => { }] } ); - }, GLOBAL.SERVERS.OPEN); + }, { + ...GLOBAL.SERVERS.OPEN, + minimumDockerVersion: [6, 2] + }); }); - - }); From 5c0aad07804970970616cbbe6eb5e3f6b4ccb52f Mon Sep 17 00:00:00 2001 From: leibale Date: Thu, 30 Dec 2021 13:16:49 -0500 Subject: [PATCH 1072/1748] fix imports --- packages/bloom/lib/commands/cuckoo/index.ts | 2 +- packages/time-series/lib/commands/index.ts | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/packages/bloom/lib/commands/cuckoo/index.ts b/packages/bloom/lib/commands/cuckoo/index.ts index 71137ceaaf4..30fa9609992 100644 --- a/packages/bloom/lib/commands/cuckoo/index.ts +++ b/packages/bloom/lib/commands/cuckoo/index.ts @@ -10,7 +10,7 @@ import * as INSERTNX from './INSERTNX'; import * as LOADCHUNK from './LOADCHUNK'; import * as RESERVE from './RESERVE'; import * as SCANDUMP from './SCANDUMP'; -import { pushVerdictArguments } from '@node-redis/client/lib/commands/generic-transformers'; +import { pushVerdictArguments } from '@node-redis/client/dist/lib/commands/generic-transformers'; import { RedisCommandArguments } from '@node-redis/client/dist/lib/commands'; export default { diff --git a/packages/time-series/lib/commands/index.ts b/packages/time-series/lib/commands/index.ts index b8b43eb9431..ad9d5962c9f 100644 --- a/packages/time-series/lib/commands/index.ts +++ b/packages/time-series/lib/commands/index.ts @@ -20,7 +20,7 @@ import * as MRANGE_WITHLABELS from './MRANGE_WITHLABELS'; import * as MREVRANGE from './MREVRANGE'; import * as MREVRANGE_WITHLABELS from './MREVRANGE_WITHLABELS'; import { RedisCommandArguments } from '@node-redis/client/dist/lib/commands'; -import { pushVerdictArguments } from '@node-redis/client/lib/commands/generic-transformers'; +import { pushVerdictArguments } from '@node-redis/client/dist/lib/commands/generic-transformers'; export default { ADD, From c581d5de3a62b52168154adb58a74ee331460dc3 Mon Sep 17 00:00:00 2001 From: leibale Date: Thu, 30 Dec 2021 13:17:44 -0500 Subject: [PATCH 1073/1748] fix #1802 --- packages/json/lib/commands/ARRPOP.spec.ts | 36 ++++++++++++++++++----- packages/json/lib/commands/ARRPOP.ts | 12 +++++++- packages/json/lib/commands/index.ts | 10 ------- 3 files changed, 39 insertions(+), 19 deletions(-) diff --git a/packages/json/lib/commands/ARRPOP.spec.ts b/packages/json/lib/commands/ARRPOP.spec.ts index a80b8c3cbc5..7c2ec365eb6 100644 --- a/packages/json/lib/commands/ARRPOP.spec.ts +++ b/packages/json/lib/commands/ARRPOP.spec.ts @@ -26,12 +26,32 @@ describe('ARRPOP', () => { }); }); - testUtils.testWithClient('client.json.arrPop', async client => { - await client.json.set('key', '$', []); - - assert.deepEqual( - await client.json.arrPop('key', '$'), - [null] - ); - }, GLOBAL.SERVERS.OPEN); + describe('client.json.arrPop', () => { + testUtils.testWithClient('null', async client => { + await client.json.set('key', '.', []); + + assert.equal( + await client.json.arrPop('key', '.'), + null + ); + }, GLOBAL.SERVERS.OPEN); + + testUtils.testWithClient('with value', async client => { + await client.json.set('key', '.', ['value']); + + assert.equal( + await client.json.arrPop('key', '.'), + 'value' + ); + }, GLOBAL.SERVERS.OPEN); + + testUtils.testWithClient('array', async client => { + await client.json.set('key', '$', ['value']); + + assert.deepEqual( + await client.json.arrPop('key', '$'), + ['value'] + ); + }, GLOBAL.SERVERS.OPEN); + }); }); diff --git a/packages/json/lib/commands/ARRPOP.ts b/packages/json/lib/commands/ARRPOP.ts index 932b3294d85..18830c0d314 100644 --- a/packages/json/lib/commands/ARRPOP.ts +++ b/packages/json/lib/commands/ARRPOP.ts @@ -1,3 +1,5 @@ +import { RedisJSON, transformRedisJsonNullReply } from '.'; + export const FIRST_KEY_INDEX = 1; export function transformArguments(key: string, path?: string, index?: number): Array { @@ -14,4 +16,12 @@ export function transformArguments(key: string, path?: string, index?: number): return args; } -export { transformRedisJsonNullArrayNullReply as transformReply } from '.'; +export function transformReply(reply: null | string | Array): null | RedisJSON | Array { + if (reply === null) return null; + + if (Array.isArray(reply)) { + return reply.map(transformRedisJsonNullReply); + } + + return transformRedisJsonNullReply(reply); +} diff --git a/packages/json/lib/commands/index.ts b/packages/json/lib/commands/index.ts index f898fde584e..efcf156b84d 100644 --- a/packages/json/lib/commands/index.ts +++ b/packages/json/lib/commands/index.ts @@ -79,22 +79,12 @@ export function transformRedisJsonReply(json: string): RedisJSON { return JSON.parse(json); } -export function transformRedisJsonArrayReply(jsons: Array): Array { - return jsons.map(transformRedisJsonReply) -} - export function transformRedisJsonNullReply(json: string | null): RedisJSON | null { if (json === null) return null; return transformRedisJsonReply(json); } -export function transformRedisJsonNullArrayNullReply(jsons: Array | null): Array | null { - if (jsons === null) return null; - - return jsons.map(transformRedisJsonNullReply); -} - export function transformNumbersReply(reply: string): number | Array { return JSON.parse(reply); } From c4ab64f153aef91fcfeeda127c6fe4e94fb5c832 Mon Sep 17 00:00:00 2001 From: leibale Date: Thu, 30 Dec 2021 14:08:42 -0500 Subject: [PATCH 1074/1748] add bloom to "all in one" --- index.ts | 3 +++ package.json | 1 + 2 files changed, 4 insertions(+) diff --git a/index.ts b/index.ts index 18b6d4a1913..7f1eb386a40 100644 --- a/index.ts +++ b/index.ts @@ -1,15 +1,18 @@ import { createClient as _createClient, createCluster as _createCluster, RedisClientOptions, RedisClientType, RedisClusterOptions, RedisClusterType } from '@node-redis/client'; import { RedisScripts } from '@node-redis/client/dist/lib/commands'; +import RedisBloomModules from '@node-redis/bloom'; import RedisJSON from '@node-redis/json'; import RediSearch from '@node-redis/search'; import RedisTimeSeries from '@node-redis/time-series'; export * from '@node-redis/client'; +export * from '@node-redis/bloom'; export * from '@node-redis/json'; export * from '@node-redis/search'; export * from '@node-redis/time-series'; const modules = { + ...RedisBloomModules, json: RedisJSON, ft: RediSearch, ts: RedisTimeSeries diff --git a/package.json b/package.json index 8e675bc86cd..cd76190b874 100644 --- a/package.json +++ b/package.json @@ -23,6 +23,7 @@ "gh-pages": "gh-pages -d ./documentation -e ./documentation -u 'documentation-bot '" }, "dependencies": { + "@node-redis/bloom": "^1.0.0", "@node-redis/client": "^1.0.1", "@node-redis/json": "^1.0.1", "@node-redis/search": "^1.0.1", From 29ff6c8a360d33c1431189fb42d264969fef6eae Mon Sep 17 00:00:00 2001 From: leibale Date: Thu, 30 Dec 2021 14:13:38 -0500 Subject: [PATCH 1075/1748] update dependencies --- package-lock.json | 2016 +++++++++++++---------------- packages/bloom/package.json | 2 +- packages/client/package.json | 6 +- packages/json/package.json | 2 +- packages/search/package.json | 2 +- packages/test-utils/package.json | 6 +- packages/time-series/package.json | 2 +- 7 files changed, 945 insertions(+), 1091 deletions(-) diff --git a/package-lock.json b/package-lock.json index 92f25780eaf..f8d029f64b1 100644 --- a/package-lock.json +++ b/package-lock.json @@ -12,6 +12,7 @@ "./packages/*" ], "dependencies": { + "@node-redis/bloom": "^1.0.0", "@node-redis/client": "^1.0.1", "@node-redis/json": "^1.0.1", "@node-redis/search": "^1.0.1", @@ -79,15 +80,6 @@ "url": "https://opencollective.com/babel" } }, - "node_modules/@babel/core/node_modules/semver": { - "version": "6.3.0", - "resolved": "https://registry.npmjs.org/semver/-/semver-6.3.0.tgz", - "integrity": "sha512-b39TBaTSfV6yBrapU89p5fKekE2m/NwnDocOVruQFS1/veMgdzuPcnOM34M6CwxW8jH/lxEa5rBoDeUwu5HHTw==", - "dev": true, - "bin": { - "semver": "bin/semver.js" - } - }, "node_modules/@babel/generator": { "version": "7.16.5", "resolved": "https://registry.npmjs.org/@babel/generator/-/generator-7.16.5.tgz", @@ -120,15 +112,6 @@ "@babel/core": "^7.0.0" } }, - "node_modules/@babel/helper-compilation-targets/node_modules/semver": { - "version": "6.3.0", - "resolved": "https://registry.npmjs.org/semver/-/semver-6.3.0.tgz", - "integrity": "sha512-b39TBaTSfV6yBrapU89p5fKekE2m/NwnDocOVruQFS1/veMgdzuPcnOM34M6CwxW8jH/lxEa5rBoDeUwu5HHTw==", - "dev": true, - "bin": { - "semver": "bin/semver.js" - } - }, "node_modules/@babel/helper-environment-visitor": { "version": "7.16.5", "resolved": "https://registry.npmjs.org/@babel/helper-environment-visitor/-/helper-environment-visitor-7.16.5.tgz", @@ -321,15 +304,6 @@ "integrity": "sha1-p9BVi9icQveV3UIyj3QIMcpTvCU=", "dev": true }, - "node_modules/@babel/highlight/node_modules/escape-string-regexp": { - "version": "1.0.5", - "resolved": "https://registry.npmjs.org/escape-string-regexp/-/escape-string-regexp-1.0.5.tgz", - "integrity": "sha1-G2HAViGQqN/2rjuyzwIAyhMLhtQ=", - "dev": true, - "engines": { - "node": ">=0.8.0" - } - }, "node_modules/@babel/highlight/node_modules/has-flag": { "version": "3.0.0", "resolved": "https://registry.npmjs.org/has-flag/-/has-flag-3.0.0.tgz", @@ -470,6 +444,18 @@ "node": ">= 4" } }, + "node_modules/@eslint/eslintrc/node_modules/strip-json-comments": { + "version": "3.1.1", + "resolved": "https://registry.npmjs.org/strip-json-comments/-/strip-json-comments-3.1.1.tgz", + "integrity": "sha512-6fPc+R4ihwqP6N/aIv2f1gMH8lOVtWQHoqC4yK6oSDVVocumAsfCqjkXnqiYMhmMwS/mEHLp7Vehlt3ql6lEig==", + "dev": true, + "engines": { + "node": ">=8" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, "node_modules/@humanwhocodes/config-array": { "version": "0.9.2", "resolved": "https://registry.npmjs.org/@humanwhocodes/config-array/-/config-array-0.9.2.tgz", @@ -521,17 +507,13 @@ "sprintf-js": "~1.0.2" } }, - "node_modules/@istanbuljs/load-nyc-config/node_modules/find-up": { - "version": "4.1.0", - "resolved": "https://registry.npmjs.org/find-up/-/find-up-4.1.0.tgz", - "integrity": "sha512-PpOwAdQ/YlXQ2vj8a3h8IipDuYRi3wceVQQGYWxNINccq40Anw7BlsEXCMbt1Zt+OLA6Fq9suIpIWD0OsnISlw==", + "node_modules/@istanbuljs/load-nyc-config/node_modules/camelcase": { + "version": "5.3.1", + "resolved": "https://registry.npmjs.org/camelcase/-/camelcase-5.3.1.tgz", + "integrity": "sha512-L28STB170nwWS63UjtlEOE3dldQApaJXZkOI1uMFfzf3rRuPegHaHesyee+YxQ+W6SvRDQV6UrdOdRiR153wJg==", "dev": true, - "dependencies": { - "locate-path": "^5.0.0", - "path-exists": "^4.0.0" - }, "engines": { - "node": ">=8" + "node": ">=6" } }, "node_modules/@istanbuljs/load-nyc-config/node_modules/js-yaml": { @@ -547,45 +529,6 @@ "js-yaml": "bin/js-yaml.js" } }, - "node_modules/@istanbuljs/load-nyc-config/node_modules/locate-path": { - "version": "5.0.0", - "resolved": "https://registry.npmjs.org/locate-path/-/locate-path-5.0.0.tgz", - "integrity": "sha512-t7hw9pI+WvuwNJXwk5zVHpyhIqzg2qTlklJOf0mVxGSbe3Fp2VieZcduNYjaLDoy6p9uGpQEGWG87WpMKlNq8g==", - "dev": true, - "dependencies": { - "p-locate": "^4.1.0" - }, - "engines": { - "node": ">=8" - } - }, - "node_modules/@istanbuljs/load-nyc-config/node_modules/p-limit": { - "version": "2.3.0", - "resolved": "https://registry.npmjs.org/p-limit/-/p-limit-2.3.0.tgz", - "integrity": "sha512-//88mFWSJx8lxCzwdAABTJL2MyWB12+eIY7MDL2SqLmAkeKU9qxRvWuSyTjm3FUmpBEMuFfckAIqEaVGUDxb6w==", - "dev": true, - "dependencies": { - "p-try": "^2.0.0" - }, - "engines": { - "node": ">=6" - }, - "funding": { - "url": "https://github.com/sponsors/sindresorhus" - } - }, - "node_modules/@istanbuljs/load-nyc-config/node_modules/p-locate": { - "version": "4.1.0", - "resolved": "https://registry.npmjs.org/p-locate/-/p-locate-4.1.0.tgz", - "integrity": "sha512-R79ZZ/0wAxKGu3oYMlz8jy/kbhsNrS7SKZ7PxEHBgJ5+F2mtFW2fK2cOtBh1cHYkQsbzFV7I+EoRKe6Yt0oK7A==", - "dev": true, - "dependencies": { - "p-limit": "^2.2.0" - }, - "engines": { - "node": ">=8" - } - }, "node_modules/@istanbuljs/load-nyc-config/node_modules/resolve-from": { "version": "5.0.0", "resolved": "https://registry.npmjs.org/resolve-from/-/resolve-from-5.0.0.tgz", @@ -619,10 +562,18 @@ "node": ">=8" } }, + "node_modules/@node-redis/bloom": { + "resolved": "packages/bloom", + "link": true + }, "node_modules/@node-redis/client": { "resolved": "packages/client", "link": true }, + "node_modules/@node-redis/graph": { + "resolved": "packages/graph", + "link": true + }, "node_modules/@node-redis/json": { "resolved": "packages/json", "link": true @@ -929,9 +880,9 @@ "dev": true }, "node_modules/@types/node": { - "version": "17.0.1", - "resolved": "https://registry.npmjs.org/@types/node/-/node-17.0.1.tgz", - "integrity": "sha512-NXKvBVUzIbs6ylBwmOwHFkZS2EXCcjnqr8ZCRNaXBkHAf+3mn/rPcJxwrzuc6movh8fxQAsUUfYklJ/EG+hZqQ==", + "version": "17.0.5", + "resolved": "https://registry.npmjs.org/@types/node/-/node-17.0.5.tgz", + "integrity": "sha512-w3mrvNXLeDYV1GKTZorGJQivK6XLCoGwpnyJFbJVK/aTBQUxOCaa/GlFAAN3OTDFcb7h5tiFG+YXCO2By+riZw==", "dev": true }, "node_modules/@types/parse-json": { @@ -981,9 +932,9 @@ "dev": true }, "node_modules/@types/yargs": { - "version": "17.0.7", - "resolved": "https://registry.npmjs.org/@types/yargs/-/yargs-17.0.7.tgz", - "integrity": "sha512-OvLKmpKdea1aWtqHv9bxVVcMoT6syAeK+198dfETIFkAevYRGwqh4H+KFxfjUETZuUuE5sQCAFwdOdoHUdo8eg==", + "version": "17.0.8", + "resolved": "https://registry.npmjs.org/@types/yargs/-/yargs-17.0.8.tgz", + "integrity": "sha512-wDeUwiUmem9FzsyysEwRukaEdDNcwbROvQ9QGRKaLI6t+IltNzbn4/i4asmB10auvZGQCzSQ6t0GSczEThlUXw==", "dev": true, "dependencies": { "@types/yargs-parser": "*" @@ -996,13 +947,13 @@ "dev": true }, "node_modules/@typescript-eslint/eslint-plugin": { - "version": "5.8.0", - "resolved": "https://registry.npmjs.org/@typescript-eslint/eslint-plugin/-/eslint-plugin-5.8.0.tgz", - "integrity": "sha512-spu1UW7QuBn0nJ6+psnfCc3iVoQAifjKORgBngKOmC8U/1tbe2YJMzYQqDGYB4JCss7L8+RM2kKLb1B1Aw9BNA==", + "version": "5.8.1", + "resolved": "https://registry.npmjs.org/@typescript-eslint/eslint-plugin/-/eslint-plugin-5.8.1.tgz", + "integrity": "sha512-wTZ5oEKrKj/8/366qTM366zqhIKAp6NCMweoRONtfuC07OAU9nVI2GZZdqQ1qD30WAAtcPdkH+npDwtRFdp4Rw==", "dev": true, "dependencies": { - "@typescript-eslint/experimental-utils": "5.8.0", - "@typescript-eslint/scope-manager": "5.8.0", + "@typescript-eslint/experimental-utils": "5.8.1", + "@typescript-eslint/scope-manager": "5.8.1", "debug": "^4.3.2", "functional-red-black-tree": "^1.0.1", "ignore": "^5.1.8", @@ -1027,16 +978,31 @@ } } }, + "node_modules/@typescript-eslint/eslint-plugin/node_modules/semver": { + "version": "7.3.5", + "resolved": "https://registry.npmjs.org/semver/-/semver-7.3.5.tgz", + "integrity": "sha512-PoeGJYh8HK4BTO/a9Tf6ZG3veo/A7ZVsYrSA6J8ny9nb3B1VrpkuN+z9OE5wfE5p6H4LchYZsegiQgbJD94ZFQ==", + "dev": true, + "dependencies": { + "lru-cache": "^6.0.0" + }, + "bin": { + "semver": "bin/semver.js" + }, + "engines": { + "node": ">=10" + } + }, "node_modules/@typescript-eslint/experimental-utils": { - "version": "5.8.0", - "resolved": "https://registry.npmjs.org/@typescript-eslint/experimental-utils/-/experimental-utils-5.8.0.tgz", - "integrity": "sha512-KN5FvNH71bhZ8fKtL+lhW7bjm7cxs1nt+hrDZWIqb6ViCffQcWyLunGrgvISgkRojIDcXIsH+xlFfI4RCDA0xA==", + "version": "5.8.1", + "resolved": "https://registry.npmjs.org/@typescript-eslint/experimental-utils/-/experimental-utils-5.8.1.tgz", + "integrity": "sha512-fbodVnjIDU4JpeXWRDsG5IfIjYBxEvs8EBO8W1+YVdtrc2B9ppfof5sZhVEDOtgTfFHnYQJDI8+qdqLYO4ceww==", "dev": true, "dependencies": { "@types/json-schema": "^7.0.9", - "@typescript-eslint/scope-manager": "5.8.0", - "@typescript-eslint/types": "5.8.0", - "@typescript-eslint/typescript-estree": "5.8.0", + "@typescript-eslint/scope-manager": "5.8.1", + "@typescript-eslint/types": "5.8.1", + "@typescript-eslint/typescript-estree": "5.8.1", "eslint-scope": "^5.1.1", "eslint-utils": "^3.0.0" }, @@ -1052,14 +1018,14 @@ } }, "node_modules/@typescript-eslint/parser": { - "version": "5.8.0", - "resolved": "https://registry.npmjs.org/@typescript-eslint/parser/-/parser-5.8.0.tgz", - "integrity": "sha512-Gleacp/ZhRtJRYs5/T8KQR3pAQjQI89Dn/k+OzyCKOsLiZH2/Vh60cFBTnFsHNI6WAD+lNUo/xGZ4NeA5u0Ipw==", + "version": "5.8.1", + "resolved": "https://registry.npmjs.org/@typescript-eslint/parser/-/parser-5.8.1.tgz", + "integrity": "sha512-K1giKHAjHuyB421SoXMXFHHVI4NdNY603uKw92++D3qyxSeYvC10CBJ/GE5Thpo4WTUvu1mmJI2/FFkz38F2Gw==", "dev": true, "dependencies": { - "@typescript-eslint/scope-manager": "5.8.0", - "@typescript-eslint/types": "5.8.0", - "@typescript-eslint/typescript-estree": "5.8.0", + "@typescript-eslint/scope-manager": "5.8.1", + "@typescript-eslint/types": "5.8.1", + "@typescript-eslint/typescript-estree": "5.8.1", "debug": "^4.3.2" }, "engines": { @@ -1079,13 +1045,13 @@ } }, "node_modules/@typescript-eslint/scope-manager": { - "version": "5.8.0", - "resolved": "https://registry.npmjs.org/@typescript-eslint/scope-manager/-/scope-manager-5.8.0.tgz", - "integrity": "sha512-x82CYJsLOjPCDuFFEbS6e7K1QEWj7u5Wk1alw8A+gnJiYwNnDJk0ib6PCegbaPMjrfBvFKa7SxE3EOnnIQz2Gg==", + "version": "5.8.1", + "resolved": "https://registry.npmjs.org/@typescript-eslint/scope-manager/-/scope-manager-5.8.1.tgz", + "integrity": "sha512-DGxJkNyYruFH3NIZc3PwrzwOQAg7vvgsHsHCILOLvUpupgkwDZdNq/cXU3BjF4LNrCsVg0qxEyWasys5AiJ85Q==", "dev": true, "dependencies": { - "@typescript-eslint/types": "5.8.0", - "@typescript-eslint/visitor-keys": "5.8.0" + "@typescript-eslint/types": "5.8.1", + "@typescript-eslint/visitor-keys": "5.8.1" }, "engines": { "node": "^12.22.0 || ^14.17.0 || >=16.0.0" @@ -1096,9 +1062,9 @@ } }, "node_modules/@typescript-eslint/types": { - "version": "5.8.0", - "resolved": "https://registry.npmjs.org/@typescript-eslint/types/-/types-5.8.0.tgz", - "integrity": "sha512-LdCYOqeqZWqCMOmwFnum6YfW9F3nKuxJiR84CdIRN5nfHJ7gyvGpXWqL/AaW0k3Po0+wm93ARAsOdzlZDPCcXg==", + "version": "5.8.1", + "resolved": "https://registry.npmjs.org/@typescript-eslint/types/-/types-5.8.1.tgz", + "integrity": "sha512-L/FlWCCgnjKOLefdok90/pqInkomLnAcF9UAzNr+DSqMC3IffzumHTQTrINXhP1gVp9zlHiYYjvozVZDPleLcA==", "dev": true, "engines": { "node": "^12.22.0 || ^14.17.0 || >=16.0.0" @@ -1109,13 +1075,13 @@ } }, "node_modules/@typescript-eslint/typescript-estree": { - "version": "5.8.0", - "resolved": "https://registry.npmjs.org/@typescript-eslint/typescript-estree/-/typescript-estree-5.8.0.tgz", - "integrity": "sha512-srfeZ3URdEcUsSLbkOFqS7WoxOqn8JNil2NSLO9O+I2/Uyc85+UlfpEvQHIpj5dVts7KKOZnftoJD/Fdv0L7nQ==", + "version": "5.8.1", + "resolved": "https://registry.npmjs.org/@typescript-eslint/typescript-estree/-/typescript-estree-5.8.1.tgz", + "integrity": "sha512-26lQ8l8tTbG7ri7xEcCFT9ijU5Fk+sx/KRRyyzCv7MQ+rZZlqiDPtMKWLC8P7o+dtCnby4c+OlxuX1tp8WfafQ==", "dev": true, "dependencies": { - "@typescript-eslint/types": "5.8.0", - "@typescript-eslint/visitor-keys": "5.8.0", + "@typescript-eslint/types": "5.8.1", + "@typescript-eslint/visitor-keys": "5.8.1", "debug": "^4.3.2", "globby": "^11.0.4", "is-glob": "^4.0.3", @@ -1135,13 +1101,57 @@ } } }, + "node_modules/@typescript-eslint/typescript-estree/node_modules/array-union": { + "version": "2.1.0", + "resolved": "https://registry.npmjs.org/array-union/-/array-union-2.1.0.tgz", + "integrity": "sha512-HGyxoOTYUyCM6stUe6EJgnd4EoewAI7zMdfqO+kGjnlZmBDz/cR5pf8r/cR4Wq60sL/p0IkcjUEEPwS3GFrIyw==", + "dev": true, + "engines": { + "node": ">=8" + } + }, + "node_modules/@typescript-eslint/typescript-estree/node_modules/globby": { + "version": "11.0.4", + "resolved": "https://registry.npmjs.org/globby/-/globby-11.0.4.tgz", + "integrity": "sha512-9O4MVG9ioZJ08ffbcyVYyLOJLk5JQ688pJ4eMGLpdWLHq/Wr1D9BlriLQyL0E+jbkuePVZXYFj47QM/v093wHg==", + "dev": true, + "dependencies": { + "array-union": "^2.1.0", + "dir-glob": "^3.0.1", + "fast-glob": "^3.1.1", + "ignore": "^5.1.4", + "merge2": "^1.3.0", + "slash": "^3.0.0" + }, + "engines": { + "node": ">=10" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/@typescript-eslint/typescript-estree/node_modules/semver": { + "version": "7.3.5", + "resolved": "https://registry.npmjs.org/semver/-/semver-7.3.5.tgz", + "integrity": "sha512-PoeGJYh8HK4BTO/a9Tf6ZG3veo/A7ZVsYrSA6J8ny9nb3B1VrpkuN+z9OE5wfE5p6H4LchYZsegiQgbJD94ZFQ==", + "dev": true, + "dependencies": { + "lru-cache": "^6.0.0" + }, + "bin": { + "semver": "bin/semver.js" + }, + "engines": { + "node": ">=10" + } + }, "node_modules/@typescript-eslint/visitor-keys": { - "version": "5.8.0", - "resolved": "https://registry.npmjs.org/@typescript-eslint/visitor-keys/-/visitor-keys-5.8.0.tgz", - "integrity": "sha512-+HDIGOEMnqbxdAHegxvnOqESUH6RWFRR2b8qxP1W9CZnnYh4Usz6MBL+2KMAgPk/P0o9c1HqnYtwzVH6GTIqug==", + "version": "5.8.1", + "resolved": "https://registry.npmjs.org/@typescript-eslint/visitor-keys/-/visitor-keys-5.8.1.tgz", + "integrity": "sha512-SWgiWIwocK6NralrJarPZlWdr0hZnj5GXHIgfdm8hNkyKvpeQuFyLP6YjSIe9kf3YBIfU6OHSZLYkQ+smZwtNg==", "dev": true, "dependencies": { - "@typescript-eslint/types": "5.8.0", + "@typescript-eslint/types": "5.8.1", "eslint-visitor-keys": "^3.0.0" }, "engines": { @@ -1159,9 +1169,9 @@ "dev": true }, "node_modules/acorn": { - "version": "8.6.0", - "resolved": "https://registry.npmjs.org/acorn/-/acorn-8.6.0.tgz", - "integrity": "sha512-U1riIR+lBSNi3IbxtaHOIKdH8sLFv3NYfNv8sg7ZsNhcfl4HF2++BfqqrNAxoCLQW1iiylOj76ecnaUxz+z9yw==", + "version": "8.7.0", + "resolved": "https://registry.npmjs.org/acorn/-/acorn-8.7.0.tgz", + "integrity": "sha512-V/LGr1APy+PXIwKebEWrkZPwoeoF+w1jiOBUmuxuiUIaOHtob8Qc9BTrYo7VuI5fR8tqsy+buA2WFooR5olqvQ==", "dev": true, "bin": { "acorn": "bin/acorn" @@ -1250,18 +1260,6 @@ "url": "https://github.com/sponsors/sindresorhus" } }, - "node_modules/ansi-escapes/node_modules/type-fest": { - "version": "0.21.3", - "resolved": "https://registry.npmjs.org/type-fest/-/type-fest-0.21.3.tgz", - "integrity": "sha512-t0rzBq87m3fVcduHDUFhKmyyX+9eo6WQjZvf51Ea/M0Q7+T374Jp1aUiyUl0GKxp8M/OETVHSDvmkyPgvX+X2w==", - "dev": true, - "engines": { - "node": ">=10" - }, - "funding": { - "url": "https://github.com/sponsors/sindresorhus" - } - }, "node_modules/ansi-regex": { "version": "5.0.1", "resolved": "https://registry.npmjs.org/ansi-regex/-/ansi-regex-5.0.1.tgz", @@ -1330,12 +1328,15 @@ "dev": true }, "node_modules/array-union": { - "version": "2.1.0", - "resolved": "https://registry.npmjs.org/array-union/-/array-union-2.1.0.tgz", - "integrity": "sha512-HGyxoOTYUyCM6stUe6EJgnd4EoewAI7zMdfqO+kGjnlZmBDz/cR5pf8r/cR4Wq60sL/p0IkcjUEEPwS3GFrIyw==", + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/array-union/-/array-union-1.0.2.tgz", + "integrity": "sha1-mjRBDk9OPaI96jdb5b5w8kd47Dk=", "dev": true, + "dependencies": { + "array-uniq": "^1.0.1" + }, "engines": { - "node": ">=8" + "node": ">=0.10.0" } }, "node_modules/array-uniq": { @@ -1445,10 +1446,10 @@ "url": "https://github.com/sponsors/sindresorhus" } }, - "node_modules/boxen/node_modules/camelcase": { - "version": "6.2.1", - "resolved": "https://registry.npmjs.org/camelcase/-/camelcase-6.2.1.tgz", - "integrity": "sha512-tVI4q5jjFV5CavAU8DXfza/TJcZutVKo/5Foskmsqcm0MsL91moHvwiGNnqaa2o6PF/7yT5ikDRcVcl8Rj6LCA==", + "node_modules/boxen/node_modules/type-fest": { + "version": "0.20.2", + "resolved": "https://registry.npmjs.org/type-fest/-/type-fest-0.20.2.tgz", + "integrity": "sha512-Ne+eE4r0/iWnpAxD852z3A+N0Bt5RN//NjJwRd2VFHEmrywxf5vsZlh4R6lixl6B+wz/8d+maTSAkN1FIkI3LQ==", "dev": true, "engines": { "node": ">=10" @@ -1618,18 +1619,21 @@ } }, "node_modules/camelcase": { - "version": "5.3.1", - "resolved": "https://registry.npmjs.org/camelcase/-/camelcase-5.3.1.tgz", - "integrity": "sha512-L28STB170nwWS63UjtlEOE3dldQApaJXZkOI1uMFfzf3rRuPegHaHesyee+YxQ+W6SvRDQV6UrdOdRiR153wJg==", + "version": "6.2.1", + "resolved": "https://registry.npmjs.org/camelcase/-/camelcase-6.2.1.tgz", + "integrity": "sha512-tVI4q5jjFV5CavAU8DXfza/TJcZutVKo/5Foskmsqcm0MsL91moHvwiGNnqaa2o6PF/7yT5ikDRcVcl8Rj6LCA==", "dev": true, "engines": { - "node": ">=6" + "node": ">=10" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" } }, "node_modules/caniuse-lite": { - "version": "1.0.30001291", - "resolved": "https://registry.npmjs.org/caniuse-lite/-/caniuse-lite-1.0.30001291.tgz", - "integrity": "sha512-roMV5V0HNGgJ88s42eE70sstqGW/gwFndosYrikHthw98N5tLnOTxFqMLQjZVRxTWFlJ4rn+MsgXrR7MDPY4jA==", + "version": "1.0.30001294", + "resolved": "https://registry.npmjs.org/caniuse-lite/-/caniuse-lite-1.0.30001294.tgz", + "integrity": "sha512-LiMlrs1nSKZ8qkNhpUf5KD0Al1KCBE3zaT7OLOwEkagXMEDij98SiOovn9wxVGQpklk9vVC/pUSqgYmkmKOS8g==", "dev": true, "funding": { "type": "opencollective", @@ -1679,18 +1683,6 @@ "fsevents": "~2.3.2" } }, - "node_modules/chokidar/node_modules/glob-parent": { - "version": "5.1.2", - "resolved": "https://registry.npmjs.org/glob-parent/-/glob-parent-5.1.2.tgz", - "integrity": "sha512-AOIgSQCepiJYwP3ARnGx+5VnTu2HBYdzbGP45eLw1vr3zB3vZLeyed1sC9hnbcOc9/SrMyM5RPQrkGz4aS9Zow==", - "dev": true, - "dependencies": { - "is-glob": "^4.0.1" - }, - "engines": { - "node": ">= 6" - } - }, "node_modules/ci-info": { "version": "3.3.0", "resolved": "https://registry.npmjs.org/ci-info/-/ci-info-3.3.0.tgz", @@ -1862,6 +1854,12 @@ "safe-buffer": "~5.1.1" } }, + "node_modules/convert-source-map/node_modules/safe-buffer": { + "version": "5.1.2", + "resolved": "https://registry.npmjs.org/safe-buffer/-/safe-buffer-5.1.2.tgz", + "integrity": "sha512-Gd2UZBJDkXlY7GbJxfsE8/nvKkUEU1G38c1siN6QP6a9PT9MmHB8GnpscSmMJSoF8LOIrt8ud/wPtojys4G6+g==", + "dev": true + }, "node_modules/cosmiconfig": { "version": "7.0.1", "resolved": "https://registry.npmjs.org/cosmiconfig/-/cosmiconfig-7.0.1.tgz", @@ -1908,9 +1906,9 @@ } }, "node_modules/debug": { - "version": "4.3.3", - "resolved": "https://registry.npmjs.org/debug/-/debug-4.3.3.tgz", - "integrity": "sha512-/zxw5+vh1Tfv+4Qn7a5nsbcJKPaSvCDhojn6FEl9vupwK2VCSDtEiEtqr8DFtzYFOdz63LBkxec7DYuc2jon6Q==", + "version": "4.3.2", + "resolved": "https://registry.npmjs.org/debug/-/debug-4.3.2.tgz", + "integrity": "sha512-mOp8wKcvj7XxC78zLgw/ZA+6TSgkoE2C/ienthhRD298T7UNwAg9diBpLRxC0mOezLl4B0xV7M0cCO6P/O0Xhw==", "dev": true, "dependencies": { "ms": "2.1.2" @@ -2094,9 +2092,9 @@ "dev": true }, "node_modules/electron-to-chromium": { - "version": "1.4.24", - "resolved": "https://registry.npmjs.org/electron-to-chromium/-/electron-to-chromium-1.4.24.tgz", - "integrity": "sha512-erwx5r69B/WFfFuF2jcNN0817BfDBdC4765kQ6WltOMuwsimlQo3JTEq0Cle+wpHralwdeX3OfAtw/mHxPK0Wg==", + "version": "1.4.30", + "resolved": "https://registry.npmjs.org/electron-to-chromium/-/electron-to-chromium-1.4.30.tgz", + "integrity": "sha512-609z9sIMxDHg+TcR/VB3MXwH+uwtrYyeAwWc/orhnr90ixs6WVGSrt85CDLGUdNnLqCA7liv426V20EecjvflQ==", "dev": true }, "node_modules/email-addresses": { @@ -2166,15 +2164,12 @@ } }, "node_modules/escape-string-regexp": { - "version": "4.0.0", - "resolved": "https://registry.npmjs.org/escape-string-regexp/-/escape-string-regexp-4.0.0.tgz", - "integrity": "sha512-TtpcNJ3XAzx3Gq8sWRzJaVajRs0uVxA2YAkdb1jm2YkPz4G6egUFAyA3n5vtEIZefPk5Wa4UXbKuS5fKkJWdgA==", + "version": "1.0.5", + "resolved": "https://registry.npmjs.org/escape-string-regexp/-/escape-string-regexp-1.0.5.tgz", + "integrity": "sha1-G2HAViGQqN/2rjuyzwIAyhMLhtQ=", "dev": true, "engines": { - "node": ">=10" - }, - "funding": { - "url": "https://github.com/sponsors/sindresorhus" + "node": ">=0.8.0" } }, "node_modules/eslint": { @@ -2281,6 +2276,18 @@ "node": "^12.22.0 || ^14.17.0 || >=16.0.0" } }, + "node_modules/eslint/node_modules/escape-string-regexp": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/escape-string-regexp/-/escape-string-regexp-4.0.0.tgz", + "integrity": "sha512-TtpcNJ3XAzx3Gq8sWRzJaVajRs0uVxA2YAkdb1jm2YkPz4G6egUFAyA3n5vtEIZefPk5Wa4UXbKuS5fKkJWdgA==", + "dev": true, + "engines": { + "node": ">=10" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, "node_modules/eslint/node_modules/eslint-scope": { "version": "7.1.0", "resolved": "https://registry.npmjs.org/eslint-scope/-/eslint-scope-7.1.0.tgz", @@ -2303,6 +2310,18 @@ "node": ">=4.0" } }, + "node_modules/eslint/node_modules/glob-parent": { + "version": "6.0.2", + "resolved": "https://registry.npmjs.org/glob-parent/-/glob-parent-6.0.2.tgz", + "integrity": "sha512-XxwI8EOhVQgWp6iDL+3b0r86f4d6AX6zSU55HfB4ydCEuXLXc5FcYeOu+nnGftS4TEju/11rt4KJPTMgbfmv4A==", + "dev": true, + "dependencies": { + "is-glob": "^4.0.3" + }, + "engines": { + "node": ">=10.13.0" + } + }, "node_modules/eslint/node_modules/ignore": { "version": "4.0.6", "resolved": "https://registry.npmjs.org/ignore/-/ignore-4.0.6.tgz", @@ -2312,24 +2331,51 @@ "node": ">= 4" } }, - "node_modules/espree": { - "version": "9.2.0", - "resolved": "https://registry.npmjs.org/espree/-/espree-9.2.0.tgz", - "integrity": "sha512-oP3utRkynpZWF/F2x/HZJ+AGtnIclaR7z1pYPxy7NYM2fSO6LgK/Rkny8anRSPK/VwEA1eqm2squui0T7ZMOBg==", + "node_modules/eslint/node_modules/semver": { + "version": "7.3.5", + "resolved": "https://registry.npmjs.org/semver/-/semver-7.3.5.tgz", + "integrity": "sha512-PoeGJYh8HK4BTO/a9Tf6ZG3veo/A7ZVsYrSA6J8ny9nb3B1VrpkuN+z9OE5wfE5p6H4LchYZsegiQgbJD94ZFQ==", "dev": true, "dependencies": { - "acorn": "^8.6.0", - "acorn-jsx": "^5.3.1", - "eslint-visitor-keys": "^3.1.0" + "lru-cache": "^6.0.0" + }, + "bin": { + "semver": "bin/semver.js" }, "engines": { - "node": "^12.22.0 || ^14.17.0 || >=16.0.0" + "node": ">=10" } }, - "node_modules/esprima": { - "version": "4.0.1", - "resolved": "https://registry.npmjs.org/esprima/-/esprima-4.0.1.tgz", - "integrity": "sha512-eGuFFw7Upda+g4p+QHvnW0RyTX/SVeJBDM/gCtMARO0cLuT2HcEKnTPvhjV6aGeqrCB/sbNop0Kszm0jsaWU4A==", + "node_modules/eslint/node_modules/strip-json-comments": { + "version": "3.1.1", + "resolved": "https://registry.npmjs.org/strip-json-comments/-/strip-json-comments-3.1.1.tgz", + "integrity": "sha512-6fPc+R4ihwqP6N/aIv2f1gMH8lOVtWQHoqC4yK6oSDVVocumAsfCqjkXnqiYMhmMwS/mEHLp7Vehlt3ql6lEig==", + "dev": true, + "engines": { + "node": ">=8" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/espree": { + "version": "9.2.0", + "resolved": "https://registry.npmjs.org/espree/-/espree-9.2.0.tgz", + "integrity": "sha512-oP3utRkynpZWF/F2x/HZJ+AGtnIclaR7z1pYPxy7NYM2fSO6LgK/Rkny8anRSPK/VwEA1eqm2squui0T7ZMOBg==", + "dev": true, + "dependencies": { + "acorn": "^8.6.0", + "acorn-jsx": "^5.3.1", + "eslint-visitor-keys": "^3.1.0" + }, + "engines": { + "node": "^12.22.0 || ^14.17.0 || >=16.0.0" + } + }, + "node_modules/esprima": { + "version": "4.0.1", + "resolved": "https://registry.npmjs.org/esprima/-/esprima-4.0.1.tgz", + "integrity": "sha512-eGuFFw7Upda+g4p+QHvnW0RyTX/SVeJBDM/gCtMARO0cLuT2HcEKnTPvhjV6aGeqrCB/sbNop0Kszm0jsaWU4A==", "dev": true, "bin": { "esparse": "bin/esparse.js", @@ -2458,18 +2504,6 @@ "node": ">=8" } }, - "node_modules/fast-glob/node_modules/glob-parent": { - "version": "5.1.2", - "resolved": "https://registry.npmjs.org/glob-parent/-/glob-parent-5.1.2.tgz", - "integrity": "sha512-AOIgSQCepiJYwP3ARnGx+5VnTu2HBYdzbGP45eLw1vr3zB3vZLeyed1sC9hnbcOc9/SrMyM5RPQrkGz4aS9Zow==", - "dev": true, - "dependencies": { - "is-glob": "^4.0.1" - }, - "engines": { - "node": ">= 6" - } - }, "node_modules/fast-json-stable-stringify": { "version": "2.1.0", "resolved": "https://registry.npmjs.org/fast-json-stable-stringify/-/fast-json-stable-stringify-2.1.0.tgz", @@ -2506,15 +2540,6 @@ "url": "https://github.com/sponsors/sindresorhus" } }, - "node_modules/figures/node_modules/escape-string-regexp": { - "version": "1.0.5", - "resolved": "https://registry.npmjs.org/escape-string-regexp/-/escape-string-regexp-1.0.5.tgz", - "integrity": "sha1-G2HAViGQqN/2rjuyzwIAyhMLhtQ=", - "dev": true, - "engines": { - "node": ">=0.8.0" - } - }, "node_modules/file-entry-cache": { "version": "6.0.1", "resolved": "https://registry.npmjs.org/file-entry-cache/-/file-entry-cache-6.0.1.tgz", @@ -2592,19 +2617,16 @@ } }, "node_modules/find-up": { - "version": "5.0.0", - "resolved": "https://registry.npmjs.org/find-up/-/find-up-5.0.0.tgz", - "integrity": "sha512-78/PXT1wlLLDgTzDs7sjq9hzz0vXD+zn+7wypEe4fXQxCmdmqfGsEPQxmiCSQI3ajFV91bVSsvNtrJRiW6nGng==", + "version": "4.1.0", + "resolved": "https://registry.npmjs.org/find-up/-/find-up-4.1.0.tgz", + "integrity": "sha512-PpOwAdQ/YlXQ2vj8a3h8IipDuYRi3wceVQQGYWxNINccq40Anw7BlsEXCMbt1Zt+OLA6Fq9suIpIWD0OsnISlw==", "dev": true, "dependencies": { - "locate-path": "^6.0.0", + "locate-path": "^5.0.0", "path-exists": "^4.0.0" }, "engines": { - "node": ">=10" - }, - "funding": { - "url": "https://github.com/sponsors/sindresorhus" + "node": ">=8" } }, "node_modules/flat": { @@ -2811,34 +2833,6 @@ "node": ">=10" } }, - "node_modules/gh-pages/node_modules/array-union": { - "version": "1.0.2", - "resolved": "https://registry.npmjs.org/array-union/-/array-union-1.0.2.tgz", - "integrity": "sha1-mjRBDk9OPaI96jdb5b5w8kd47Dk=", - "dev": true, - "dependencies": { - "array-uniq": "^1.0.1" - }, - "engines": { - "node": ">=0.10.0" - } - }, - "node_modules/gh-pages/node_modules/globby": { - "version": "6.1.0", - "resolved": "https://registry.npmjs.org/globby/-/globby-6.1.0.tgz", - "integrity": "sha1-9abXDoOV4hyFj7BInWTfAkJNUGw=", - "dev": true, - "dependencies": { - "array-union": "^1.0.1", - "glob": "^7.0.3", - "object-assign": "^4.0.1", - "pify": "^2.0.0", - "pinkie-promise": "^2.0.0" - }, - "engines": { - "node": ">=0.10.0" - } - }, "node_modules/git-up": { "version": "4.0.5", "resolved": "https://registry.npmjs.org/git-up/-/git-up-4.0.5.tgz", @@ -2859,9 +2853,9 @@ } }, "node_modules/glob": { - "version": "7.1.7", - "resolved": "https://registry.npmjs.org/glob/-/glob-7.1.7.tgz", - "integrity": "sha512-OvD9ENzPLbegENnYP5UUfJIirTg4+XwMWGaQfQTY0JenxNvvIKP3U3/tAQSPIu/lHxXYSZmpXlUHeqAIdKzBLQ==", + "version": "7.2.0", + "resolved": "https://registry.npmjs.org/glob/-/glob-7.2.0.tgz", + "integrity": "sha512-lmLf6gtyrPq8tTjSmrO94wBeQbFR3HbLHbuyD69wuyQkImp2hWqMGB47OX65FBkPffO641IP9jWa1z4ivqG26Q==", "dev": true, "dependencies": { "fs.realpath": "^1.0.0", @@ -2879,15 +2873,15 @@ } }, "node_modules/glob-parent": { - "version": "6.0.2", - "resolved": "https://registry.npmjs.org/glob-parent/-/glob-parent-6.0.2.tgz", - "integrity": "sha512-XxwI8EOhVQgWp6iDL+3b0r86f4d6AX6zSU55HfB4ydCEuXLXc5FcYeOu+nnGftS4TEju/11rt4KJPTMgbfmv4A==", + "version": "5.1.2", + "resolved": "https://registry.npmjs.org/glob-parent/-/glob-parent-5.1.2.tgz", + "integrity": "sha512-AOIgSQCepiJYwP3ARnGx+5VnTu2HBYdzbGP45eLw1vr3zB3vZLeyed1sC9hnbcOc9/SrMyM5RPQrkGz4aS9Zow==", "dev": true, "dependencies": { - "is-glob": "^4.0.3" + "is-glob": "^4.0.1" }, "engines": { - "node": ">=10.13.0" + "node": ">= 6" } }, "node_modules/global-dirs": { @@ -2920,19 +2914,11 @@ "url": "https://github.com/sponsors/sindresorhus" } }, - "node_modules/globby": { - "version": "11.0.4", - "resolved": "https://registry.npmjs.org/globby/-/globby-11.0.4.tgz", - "integrity": "sha512-9O4MVG9ioZJ08ffbcyVYyLOJLk5JQ688pJ4eMGLpdWLHq/Wr1D9BlriLQyL0E+jbkuePVZXYFj47QM/v093wHg==", + "node_modules/globals/node_modules/type-fest": { + "version": "0.20.2", + "resolved": "https://registry.npmjs.org/type-fest/-/type-fest-0.20.2.tgz", + "integrity": "sha512-Ne+eE4r0/iWnpAxD852z3A+N0Bt5RN//NjJwRd2VFHEmrywxf5vsZlh4R6lixl6B+wz/8d+maTSAkN1FIkI3LQ==", "dev": true, - "dependencies": { - "array-union": "^2.1.0", - "dir-glob": "^3.0.1", - "fast-glob": "^3.1.1", - "ignore": "^5.1.4", - "merge2": "^1.3.0", - "slash": "^3.0.0" - }, "engines": { "node": ">=10" }, @@ -2940,6 +2926,22 @@ "url": "https://github.com/sponsors/sindresorhus" } }, + "node_modules/globby": { + "version": "6.1.0", + "resolved": "https://registry.npmjs.org/globby/-/globby-6.1.0.tgz", + "integrity": "sha1-9abXDoOV4hyFj7BInWTfAkJNUGw=", + "dev": true, + "dependencies": { + "array-union": "^1.0.1", + "glob": "^7.0.3", + "object-assign": "^4.0.1", + "pify": "^2.0.0", + "pinkie-promise": "^2.0.0" + }, + "engines": { + "node": ">=0.10.0" + } + }, "node_modules/got": { "version": "11.8.3", "resolved": "https://registry.npmjs.org/got/-/got-11.8.3.tgz", @@ -3549,15 +3551,6 @@ "node": ">=8" } }, - "node_modules/istanbul-lib-instrument/node_modules/semver": { - "version": "6.3.0", - "resolved": "https://registry.npmjs.org/semver/-/semver-6.3.0.tgz", - "integrity": "sha512-b39TBaTSfV6yBrapU89p5fKekE2m/NwnDocOVruQFS1/veMgdzuPcnOM34M6CwxW8jH/lxEa5rBoDeUwu5HHTw==", - "dev": true, - "bin": { - "semver": "bin/semver.js" - } - }, "node_modules/istanbul-lib-processinfo": { "version": "2.0.2", "resolved": "https://registry.npmjs.org/istanbul-lib-processinfo/-/istanbul-lib-processinfo-2.0.2.tgz", @@ -3576,6 +3569,16 @@ "node": ">=8" } }, + "node_modules/istanbul-lib-processinfo/node_modules/uuid": { + "version": "3.4.0", + "resolved": "https://registry.npmjs.org/uuid/-/uuid-3.4.0.tgz", + "integrity": "sha512-HjSDRw6gZE5JMggctHBcjVak08+KEVhSIiDzFnT9S9aegmp85S/bReBVTb4QTFaRNptJ9kuYaNhnbNEOkbKb/A==", + "deprecated": "Please upgrade to version 7 or higher. Older versions may use Math.random() in certain circumstances, which is known to be problematic. See https://v8.dev/blog/math-random for details.", + "dev": true, + "bin": { + "uuid": "bin/uuid" + } + }, "node_modules/istanbul-lib-report": { "version": "3.0.0", "resolved": "https://registry.npmjs.org/istanbul-lib-report/-/istanbul-lib-report-3.0.0.tgz", @@ -3614,9 +3617,9 @@ } }, "node_modules/istanbul-reports": { - "version": "3.1.1", - "resolved": "https://registry.npmjs.org/istanbul-reports/-/istanbul-reports-3.1.1.tgz", - "integrity": "sha512-q1kvhAXWSsXfMjCdNHNPKZZv94OlspKnoGv+R9RGbnqOOQ0VbNfLFgQDVgi7hHenKsndGq3/o0OBdzDXthWcNw==", + "version": "3.1.3", + "resolved": "https://registry.npmjs.org/istanbul-reports/-/istanbul-reports-3.1.3.tgz", + "integrity": "sha512-x9LtDVtfm/t1GFiLl3NffC7hz+I1ragvgX1P/Lg1NlIagifZDKUkuuaAxH/qpwj2IuEfD8G2Bs/UKp+sZ/pKkg==", "dev": true, "dependencies": { "html-escaper": "^2.0.0", @@ -3757,18 +3760,15 @@ "dev": true }, "node_modules/locate-path": { - "version": "6.0.0", - "resolved": "https://registry.npmjs.org/locate-path/-/locate-path-6.0.0.tgz", - "integrity": "sha512-iPZK6eYjbxRu3uB4/WZ3EsEIMJFMqAoopl3R+zuq0UjcAm/MO6KCweDgPfP3elTztoKP3KtnVHxTn2NHBSDVUw==", + "version": "5.0.0", + "resolved": "https://registry.npmjs.org/locate-path/-/locate-path-5.0.0.tgz", + "integrity": "sha512-t7hw9pI+WvuwNJXwk5zVHpyhIqzg2qTlklJOf0mVxGSbe3Fp2VieZcduNYjaLDoy6p9uGpQEGWG87WpMKlNq8g==", "dev": true, "dependencies": { - "p-locate": "^5.0.0" + "p-locate": "^4.1.0" }, "engines": { - "node": ">=10" - }, - "funding": { - "url": "https://github.com/sponsors/sindresorhus" + "node": ">=8" } }, "node_modules/lodash": { @@ -3865,15 +3865,6 @@ "url": "https://github.com/sponsors/sindresorhus" } }, - "node_modules/make-dir/node_modules/semver": { - "version": "6.3.0", - "resolved": "https://registry.npmjs.org/semver/-/semver-6.3.0.tgz", - "integrity": "sha512-b39TBaTSfV6yBrapU89p5fKekE2m/NwnDocOVruQFS1/veMgdzuPcnOM34M6CwxW8jH/lxEa5rBoDeUwu5HHTw==", - "dev": true, - "bin": { - "semver": "bin/semver.js" - } - }, "node_modules/make-error": { "version": "1.3.6", "resolved": "https://registry.npmjs.org/make-error/-/make-error-1.3.6.tgz", @@ -4020,28 +4011,68 @@ "url": "https://opencollective.com/mochajs" } }, - "node_modules/mocha/node_modules/debug": { - "version": "4.3.2", - "resolved": "https://registry.npmjs.org/debug/-/debug-4.3.2.tgz", - "integrity": "sha512-mOp8wKcvj7XxC78zLgw/ZA+6TSgkoE2C/ienthhRD298T7UNwAg9diBpLRxC0mOezLl4B0xV7M0cCO6P/O0Xhw==", + "node_modules/mocha/node_modules/escape-string-regexp": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/escape-string-regexp/-/escape-string-regexp-4.0.0.tgz", + "integrity": "sha512-TtpcNJ3XAzx3Gq8sWRzJaVajRs0uVxA2YAkdb1jm2YkPz4G6egUFAyA3n5vtEIZefPk5Wa4UXbKuS5fKkJWdgA==", + "dev": true, + "engines": { + "node": ">=10" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/mocha/node_modules/find-up": { + "version": "5.0.0", + "resolved": "https://registry.npmjs.org/find-up/-/find-up-5.0.0.tgz", + "integrity": "sha512-78/PXT1wlLLDgTzDs7sjq9hzz0vXD+zn+7wypEe4fXQxCmdmqfGsEPQxmiCSQI3ajFV91bVSsvNtrJRiW6nGng==", "dev": true, "dependencies": { - "ms": "2.1.2" + "locate-path": "^6.0.0", + "path-exists": "^4.0.0" }, "engines": { - "node": ">=6.0" + "node": ">=10" }, - "peerDependenciesMeta": { - "supports-color": { - "optional": true - } + "funding": { + "url": "https://github.com/sponsors/sindresorhus" } }, - "node_modules/mocha/node_modules/debug/node_modules/ms": { - "version": "2.1.2", - "resolved": "https://registry.npmjs.org/ms/-/ms-2.1.2.tgz", - "integrity": "sha512-sGkPx+VjMtmA6MX27oA4FBFELFCZZ4S4XqeGOXCv68tT+jb3vk/RyaKWP0PTKyWtmLSM0b+adUTEvbs1PEaH2w==", - "dev": true + "node_modules/mocha/node_modules/glob": { + "version": "7.1.7", + "resolved": "https://registry.npmjs.org/glob/-/glob-7.1.7.tgz", + "integrity": "sha512-OvD9ENzPLbegENnYP5UUfJIirTg4+XwMWGaQfQTY0JenxNvvIKP3U3/tAQSPIu/lHxXYSZmpXlUHeqAIdKzBLQ==", + "dev": true, + "dependencies": { + "fs.realpath": "^1.0.0", + "inflight": "^1.0.4", + "inherits": "2", + "minimatch": "^3.0.4", + "once": "^1.3.0", + "path-is-absolute": "^1.0.0" + }, + "engines": { + "node": "*" + }, + "funding": { + "url": "https://github.com/sponsors/isaacs" + } + }, + "node_modules/mocha/node_modules/locate-path": { + "version": "6.0.0", + "resolved": "https://registry.npmjs.org/locate-path/-/locate-path-6.0.0.tgz", + "integrity": "sha512-iPZK6eYjbxRu3uB4/WZ3EsEIMJFMqAoopl3R+zuq0UjcAm/MO6KCweDgPfP3elTztoKP3KtnVHxTn2NHBSDVUw==", + "dev": true, + "dependencies": { + "p-locate": "^5.0.0" + }, + "engines": { + "node": ">=10" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } }, "node_modules/mocha/node_modules/ms": { "version": "2.1.3", @@ -4049,30 +4080,72 @@ "integrity": "sha512-6FlzubTLZG3J2a/NVCAleEhjzq5oxgHyaCU9yYXvcLsvoVaHJq/s5xXI6/XXP6tz7R9xAOtHnSO/tXtF3WRTlA==", "dev": true }, - "node_modules/mocha/node_modules/supports-color": { - "version": "8.1.1", - "resolved": "https://registry.npmjs.org/supports-color/-/supports-color-8.1.1.tgz", - "integrity": "sha512-MpUEN2OodtUzxvKQl72cUF7RQ5EiHsGvSsVG0ia9c5RbWGL2CI4C7EpPS8UTBIplnlzZiNuV56w+FuNxy3ty2Q==", + "node_modules/mocha/node_modules/p-limit": { + "version": "3.1.0", + "resolved": "https://registry.npmjs.org/p-limit/-/p-limit-3.1.0.tgz", + "integrity": "sha512-TYOanM3wGwNGsZN2cVTYPArw454xnXj5qmWF1bEoAc4+cU/ol7GVh7odevjp1FNHduHc3KZMcFduxU5Xc6uJRQ==", "dev": true, "dependencies": { - "has-flag": "^4.0.0" + "yocto-queue": "^0.1.0" }, "engines": { "node": ">=10" }, "funding": { - "url": "https://github.com/chalk/supports-color?sponsor=1" + "url": "https://github.com/sponsors/sindresorhus" } }, - "node_modules/mocha/node_modules/yargs": { - "version": "16.2.0", - "resolved": "https://registry.npmjs.org/yargs/-/yargs-16.2.0.tgz", - "integrity": "sha512-D1mvvtDG0L5ft/jGWkLpG1+m0eQxOfaBvTNELraWj22wSVUMWxZUvYgJYcKh6jGGIkJFhH4IZPQhR4TKpc8mBw==", + "node_modules/mocha/node_modules/p-locate": { + "version": "5.0.0", + "resolved": "https://registry.npmjs.org/p-locate/-/p-locate-5.0.0.tgz", + "integrity": "sha512-LaNjtRWUBY++zB5nE/NwcaoMylSPk+S+ZHNB1TzdbMJMny6dynpAGt7X/tl/QYq3TIeE6nxHppbo2LGymrG5Pw==", "dev": true, "dependencies": { - "cliui": "^7.0.2", - "escalade": "^3.1.1", - "get-caller-file": "^2.0.5", + "p-limit": "^3.0.2" + }, + "engines": { + "node": ">=10" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/mocha/node_modules/strip-json-comments": { + "version": "3.1.1", + "resolved": "https://registry.npmjs.org/strip-json-comments/-/strip-json-comments-3.1.1.tgz", + "integrity": "sha512-6fPc+R4ihwqP6N/aIv2f1gMH8lOVtWQHoqC4yK6oSDVVocumAsfCqjkXnqiYMhmMwS/mEHLp7Vehlt3ql6lEig==", + "dev": true, + "engines": { + "node": ">=8" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/mocha/node_modules/supports-color": { + "version": "8.1.1", + "resolved": "https://registry.npmjs.org/supports-color/-/supports-color-8.1.1.tgz", + "integrity": "sha512-MpUEN2OodtUzxvKQl72cUF7RQ5EiHsGvSsVG0ia9c5RbWGL2CI4C7EpPS8UTBIplnlzZiNuV56w+FuNxy3ty2Q==", + "dev": true, + "dependencies": { + "has-flag": "^4.0.0" + }, + "engines": { + "node": ">=10" + }, + "funding": { + "url": "https://github.com/chalk/supports-color?sponsor=1" + } + }, + "node_modules/mocha/node_modules/yargs": { + "version": "16.2.0", + "resolved": "https://registry.npmjs.org/yargs/-/yargs-16.2.0.tgz", + "integrity": "sha512-D1mvvtDG0L5ft/jGWkLpG1+m0eQxOfaBvTNELraWj22wSVUMWxZUvYgJYcKh6jGGIkJFhH4IZPQhR4TKpc8mBw==", + "dev": true, + "dependencies": { + "cliui": "^7.0.2", + "escalade": "^3.1.1", + "get-caller-file": "^2.0.5", "require-directory": "^2.1.1", "string-width": "^4.2.0", "y18n": "^5.0.5", @@ -4082,6 +4155,15 @@ "node": ">=10" } }, + "node_modules/mocha/node_modules/yargs-parser": { + "version": "20.2.4", + "resolved": "https://registry.npmjs.org/yargs-parser/-/yargs-parser-20.2.4.tgz", + "integrity": "sha512-WOkpgNhPTlE73h4VFAFsOnomJVaovO8VqLDzy5saChRBFQFBoMYirowyW+Q9HB4HFF4Z7VZTiG3iSzJJA29yRA==", + "dev": true, + "engines": { + "node": ">=10" + } + }, "node_modules/ms": { "version": "2.1.2", "resolved": "https://registry.npmjs.org/ms/-/ms-2.1.2.tgz", @@ -4250,6 +4332,15 @@ "node": ">=8.9" } }, + "node_modules/nyc/node_modules/camelcase": { + "version": "5.3.1", + "resolved": "https://registry.npmjs.org/camelcase/-/camelcase-5.3.1.tgz", + "integrity": "sha512-L28STB170nwWS63UjtlEOE3dldQApaJXZkOI1uMFfzf3rRuPegHaHesyee+YxQ+W6SvRDQV6UrdOdRiR153wJg==", + "dev": true, + "engines": { + "node": ">=6" + } + }, "node_modules/nyc/node_modules/cliui": { "version": "6.0.0", "resolved": "https://registry.npmjs.org/cliui/-/cliui-6.0.0.tgz", @@ -4261,58 +4352,6 @@ "wrap-ansi": "^6.2.0" } }, - "node_modules/nyc/node_modules/find-up": { - "version": "4.1.0", - "resolved": "https://registry.npmjs.org/find-up/-/find-up-4.1.0.tgz", - "integrity": "sha512-PpOwAdQ/YlXQ2vj8a3h8IipDuYRi3wceVQQGYWxNINccq40Anw7BlsEXCMbt1Zt+OLA6Fq9suIpIWD0OsnISlw==", - "dev": true, - "dependencies": { - "locate-path": "^5.0.0", - "path-exists": "^4.0.0" - }, - "engines": { - "node": ">=8" - } - }, - "node_modules/nyc/node_modules/locate-path": { - "version": "5.0.0", - "resolved": "https://registry.npmjs.org/locate-path/-/locate-path-5.0.0.tgz", - "integrity": "sha512-t7hw9pI+WvuwNJXwk5zVHpyhIqzg2qTlklJOf0mVxGSbe3Fp2VieZcduNYjaLDoy6p9uGpQEGWG87WpMKlNq8g==", - "dev": true, - "dependencies": { - "p-locate": "^4.1.0" - }, - "engines": { - "node": ">=8" - } - }, - "node_modules/nyc/node_modules/p-limit": { - "version": "2.3.0", - "resolved": "https://registry.npmjs.org/p-limit/-/p-limit-2.3.0.tgz", - "integrity": "sha512-//88mFWSJx8lxCzwdAABTJL2MyWB12+eIY7MDL2SqLmAkeKU9qxRvWuSyTjm3FUmpBEMuFfckAIqEaVGUDxb6w==", - "dev": true, - "dependencies": { - "p-try": "^2.0.0" - }, - "engines": { - "node": ">=6" - }, - "funding": { - "url": "https://github.com/sponsors/sindresorhus" - } - }, - "node_modules/nyc/node_modules/p-locate": { - "version": "4.1.0", - "resolved": "https://registry.npmjs.org/p-locate/-/p-locate-4.1.0.tgz", - "integrity": "sha512-R79ZZ/0wAxKGu3oYMlz8jy/kbhsNrS7SKZ7PxEHBgJ5+F2mtFW2fK2cOtBh1cHYkQsbzFV7I+EoRKe6Yt0oK7A==", - "dev": true, - "dependencies": { - "p-limit": "^2.2.0" - }, - "engines": { - "node": ">=8" - } - }, "node_modules/nyc/node_modules/resolve-from": { "version": "5.0.0", "resolved": "https://registry.npmjs.org/resolve-from/-/resolve-from-5.0.0.tgz", @@ -4510,33 +4549,30 @@ } }, "node_modules/p-limit": { - "version": "3.1.0", - "resolved": "https://registry.npmjs.org/p-limit/-/p-limit-3.1.0.tgz", - "integrity": "sha512-TYOanM3wGwNGsZN2cVTYPArw454xnXj5qmWF1bEoAc4+cU/ol7GVh7odevjp1FNHduHc3KZMcFduxU5Xc6uJRQ==", + "version": "2.3.0", + "resolved": "https://registry.npmjs.org/p-limit/-/p-limit-2.3.0.tgz", + "integrity": "sha512-//88mFWSJx8lxCzwdAABTJL2MyWB12+eIY7MDL2SqLmAkeKU9qxRvWuSyTjm3FUmpBEMuFfckAIqEaVGUDxb6w==", "dev": true, "dependencies": { - "yocto-queue": "^0.1.0" + "p-try": "^2.0.0" }, "engines": { - "node": ">=10" + "node": ">=6" }, "funding": { "url": "https://github.com/sponsors/sindresorhus" } }, "node_modules/p-locate": { - "version": "5.0.0", - "resolved": "https://registry.npmjs.org/p-locate/-/p-locate-5.0.0.tgz", - "integrity": "sha512-LaNjtRWUBY++zB5nE/NwcaoMylSPk+S+ZHNB1TzdbMJMny6dynpAGt7X/tl/QYq3TIeE6nxHppbo2LGymrG5Pw==", + "version": "4.1.0", + "resolved": "https://registry.npmjs.org/p-locate/-/p-locate-4.1.0.tgz", + "integrity": "sha512-R79ZZ/0wAxKGu3oYMlz8jy/kbhsNrS7SKZ7PxEHBgJ5+F2mtFW2fK2cOtBh1cHYkQsbzFV7I+EoRKe6Yt0oK7A==", "dev": true, "dependencies": { - "p-limit": "^3.0.2" + "p-limit": "^2.2.0" }, "engines": { - "node": ">=10" - }, - "funding": { - "url": "https://github.com/sponsors/sindresorhus" + "node": ">=8" } }, "node_modules/p-map": { @@ -4756,15 +4792,6 @@ "node": ">=0.10.0" } }, - "node_modules/package-json/node_modules/semver": { - "version": "6.3.0", - "resolved": "https://registry.npmjs.org/semver/-/semver-6.3.0.tgz", - "integrity": "sha512-b39TBaTSfV6yBrapU89p5fKekE2m/NwnDocOVruQFS1/veMgdzuPcnOM34M6CwxW8jH/lxEa5rBoDeUwu5HHTw==", - "dev": true, - "bin": { - "semver": "bin/semver.js" - } - }, "node_modules/parent-module": { "version": "1.0.1", "resolved": "https://registry.npmjs.org/parent-module/-/parent-module-1.0.1.tgz", @@ -4930,58 +4957,6 @@ "node": ">=8" } }, - "node_modules/pkg-dir/node_modules/find-up": { - "version": "4.1.0", - "resolved": "https://registry.npmjs.org/find-up/-/find-up-4.1.0.tgz", - "integrity": "sha512-PpOwAdQ/YlXQ2vj8a3h8IipDuYRi3wceVQQGYWxNINccq40Anw7BlsEXCMbt1Zt+OLA6Fq9suIpIWD0OsnISlw==", - "dev": true, - "dependencies": { - "locate-path": "^5.0.0", - "path-exists": "^4.0.0" - }, - "engines": { - "node": ">=8" - } - }, - "node_modules/pkg-dir/node_modules/locate-path": { - "version": "5.0.0", - "resolved": "https://registry.npmjs.org/locate-path/-/locate-path-5.0.0.tgz", - "integrity": "sha512-t7hw9pI+WvuwNJXwk5zVHpyhIqzg2qTlklJOf0mVxGSbe3Fp2VieZcduNYjaLDoy6p9uGpQEGWG87WpMKlNq8g==", - "dev": true, - "dependencies": { - "p-locate": "^4.1.0" - }, - "engines": { - "node": ">=8" - } - }, - "node_modules/pkg-dir/node_modules/p-limit": { - "version": "2.3.0", - "resolved": "https://registry.npmjs.org/p-limit/-/p-limit-2.3.0.tgz", - "integrity": "sha512-//88mFWSJx8lxCzwdAABTJL2MyWB12+eIY7MDL2SqLmAkeKU9qxRvWuSyTjm3FUmpBEMuFfckAIqEaVGUDxb6w==", - "dev": true, - "dependencies": { - "p-try": "^2.0.0" - }, - "engines": { - "node": ">=6" - }, - "funding": { - "url": "https://github.com/sponsors/sindresorhus" - } - }, - "node_modules/pkg-dir/node_modules/p-locate": { - "version": "4.1.0", - "resolved": "https://registry.npmjs.org/p-locate/-/p-locate-4.1.0.tgz", - "integrity": "sha512-R79ZZ/0wAxKGu3oYMlz8jy/kbhsNrS7SKZ7PxEHBgJ5+F2mtFW2fK2cOtBh1cHYkQsbzFV7I+EoRKe6Yt0oK7A==", - "dev": true, - "dependencies": { - "p-limit": "^2.2.0" - }, - "engines": { - "node": ">=8" - } - }, "node_modules/prelude-ls": { "version": "1.2.1", "resolved": "https://registry.npmjs.org/prelude-ls/-/prelude-ls-1.2.1.tgz", @@ -5153,15 +5128,6 @@ "integrity": "sha512-JV/yugV2uzW5iMRSiZAyDtQd+nxtUnjeLt0acNdw98kKLrvuRVyB80tsREOE7yvGVgalhZ6RNXCmEHkUKBKxew==", "dev": true }, - "node_modules/rc/node_modules/strip-json-comments": { - "version": "2.0.1", - "resolved": "https://registry.npmjs.org/strip-json-comments/-/strip-json-comments-2.0.1.tgz", - "integrity": "sha1-PFMZQukIwml8DsNEhYwobHygpgo=", - "dev": true, - "engines": { - "node": ">=0.10.0" - } - }, "node_modules/readable-stream": { "version": "3.6.0", "resolved": "https://registry.npmjs.org/readable-stream/-/readable-stream-3.6.0.tgz", @@ -5298,37 +5264,46 @@ "node": ">=10" } }, - "node_modules/release-it/node_modules/debug": { - "version": "4.3.2", - "resolved": "https://registry.npmjs.org/debug/-/debug-4.3.2.tgz", - "integrity": "sha512-mOp8wKcvj7XxC78zLgw/ZA+6TSgkoE2C/ienthhRD298T7UNwAg9diBpLRxC0mOezLl4B0xV7M0cCO6P/O0Xhw==", + "node_modules/release-it/node_modules/array-union": { + "version": "2.1.0", + "resolved": "https://registry.npmjs.org/array-union/-/array-union-2.1.0.tgz", + "integrity": "sha512-HGyxoOTYUyCM6stUe6EJgnd4EoewAI7zMdfqO+kGjnlZmBDz/cR5pf8r/cR4Wq60sL/p0IkcjUEEPwS3GFrIyw==", + "dev": true, + "engines": { + "node": ">=8" + } + }, + "node_modules/release-it/node_modules/globby": { + "version": "11.0.4", + "resolved": "https://registry.npmjs.org/globby/-/globby-11.0.4.tgz", + "integrity": "sha512-9O4MVG9ioZJ08ffbcyVYyLOJLk5JQ688pJ4eMGLpdWLHq/Wr1D9BlriLQyL0E+jbkuePVZXYFj47QM/v093wHg==", "dev": true, "dependencies": { - "ms": "2.1.2" + "array-union": "^2.1.0", + "dir-glob": "^3.0.1", + "fast-glob": "^3.1.1", + "ignore": "^5.1.4", + "merge2": "^1.3.0", + "slash": "^3.0.0" }, "engines": { - "node": ">=6.0" + "node": ">=10" }, - "peerDependenciesMeta": { - "supports-color": { - "optional": true - } + "funding": { + "url": "https://github.com/sponsors/sindresorhus" } }, - "node_modules/release-it/node_modules/uuid": { - "version": "8.3.2", - "resolved": "https://registry.npmjs.org/uuid/-/uuid-8.3.2.tgz", - "integrity": "sha512-+NYs2QeMWy+GWFOEm9xnn6HCDp0l7QBD7ml8zLUmJ+93Q5NF0NocErnwkTkXVFNiX3/fpC6afS8Dhb/gz7R7eg==", + "node_modules/release-it/node_modules/semver": { + "version": "7.3.5", + "resolved": "https://registry.npmjs.org/semver/-/semver-7.3.5.tgz", + "integrity": "sha512-PoeGJYh8HK4BTO/a9Tf6ZG3veo/A7ZVsYrSA6J8ny9nb3B1VrpkuN+z9OE5wfE5p6H4LchYZsegiQgbJD94ZFQ==", "dev": true, + "dependencies": { + "lru-cache": "^6.0.0" + }, "bin": { - "uuid": "dist/bin/uuid" - } - }, - "node_modules/release-it/node_modules/yargs-parser": { - "version": "20.2.9", - "resolved": "https://registry.npmjs.org/yargs-parser/-/yargs-parser-20.2.9.tgz", - "integrity": "sha512-y11nGElTIV+CT3Zv9t7VKl+Q3hTQoT9a1Qzezhhl6Rp21gJ/IVTW7Z3y9EWXhuUBC2Shnf+DX0antecpAwSP8w==", - "dev": true, + "semver": "bin/semver.js" + }, "engines": { "node": ">=10" } @@ -5477,19 +5452,33 @@ } }, "node_modules/rxjs": { - "version": "7.4.0", - "resolved": "https://registry.npmjs.org/rxjs/-/rxjs-7.4.0.tgz", - "integrity": "sha512-7SQDi7xeTMCJpqViXh8gL/lebcwlp3d831F05+9B44A4B0WfsEwUQHR64gsH1kvJ+Ep/J9K2+n1hVl1CsGN23w==", + "version": "7.5.1", + "resolved": "https://registry.npmjs.org/rxjs/-/rxjs-7.5.1.tgz", + "integrity": "sha512-KExVEeZWxMZnZhUZtsJcFwz8IvPvgu4G2Z2QyqjZQzUGr32KDYuSxrEYO4w3tFFNbfLozcrKUTvTPi+E9ywJkQ==", "dev": true, "dependencies": { - "tslib": "~2.1.0" + "tslib": "^2.1.0" } }, "node_modules/safe-buffer": { - "version": "5.1.2", - "resolved": "https://registry.npmjs.org/safe-buffer/-/safe-buffer-5.1.2.tgz", - "integrity": "sha512-Gd2UZBJDkXlY7GbJxfsE8/nvKkUEU1G38c1siN6QP6a9PT9MmHB8GnpscSmMJSoF8LOIrt8ud/wPtojys4G6+g==", - "dev": true + "version": "5.2.1", + "resolved": "https://registry.npmjs.org/safe-buffer/-/safe-buffer-5.2.1.tgz", + "integrity": "sha512-rp3So07KcdmmKbGvgaNxQSJr7bGVSVk5S9Eq1F+ppbRo70+YeaDxkw5Dd8NPN+GD6bjnYm2VuPuCXmpuYvmCXQ==", + "dev": true, + "funding": [ + { + "type": "github", + "url": "https://github.com/sponsors/feross" + }, + { + "type": "patreon", + "url": "https://www.patreon.com/feross" + }, + { + "type": "consulting", + "url": "https://feross.org/support" + } + ] }, "node_modules/safer-buffer": { "version": "2.1.2", @@ -5498,18 +5487,12 @@ "dev": true }, "node_modules/semver": { - "version": "7.3.5", - "resolved": "https://registry.npmjs.org/semver/-/semver-7.3.5.tgz", - "integrity": "sha512-PoeGJYh8HK4BTO/a9Tf6ZG3veo/A7ZVsYrSA6J8ny9nb3B1VrpkuN+z9OE5wfE5p6H4LchYZsegiQgbJD94ZFQ==", + "version": "6.3.0", + "resolved": "https://registry.npmjs.org/semver/-/semver-6.3.0.tgz", + "integrity": "sha512-b39TBaTSfV6yBrapU89p5fKekE2m/NwnDocOVruQFS1/veMgdzuPcnOM34M6CwxW8jH/lxEa5rBoDeUwu5HHTw==", "dev": true, - "dependencies": { - "lru-cache": "^6.0.0" - }, "bin": { "semver": "bin/semver.js" - }, - "engines": { - "node": ">=10" } }, "node_modules/semver-diff": { @@ -5524,15 +5507,6 @@ "node": ">=8" } }, - "node_modules/semver-diff/node_modules/semver": { - "version": "6.3.0", - "resolved": "https://registry.npmjs.org/semver/-/semver-6.3.0.tgz", - "integrity": "sha512-b39TBaTSfV6yBrapU89p5fKekE2m/NwnDocOVruQFS1/veMgdzuPcnOM34M6CwxW8jH/lxEa5rBoDeUwu5HHTw==", - "dev": true, - "bin": { - "semver": "bin/semver.js" - } - }, "node_modules/serialize-javascript": { "version": "6.0.0", "resolved": "https://registry.npmjs.org/serialize-javascript/-/serialize-javascript-6.0.0.tgz", @@ -5731,26 +5705,6 @@ "safe-buffer": "~5.2.0" } }, - "node_modules/string_decoder/node_modules/safe-buffer": { - "version": "5.2.1", - "resolved": "https://registry.npmjs.org/safe-buffer/-/safe-buffer-5.2.1.tgz", - "integrity": "sha512-rp3So07KcdmmKbGvgaNxQSJr7bGVSVk5S9Eq1F+ppbRo70+YeaDxkw5Dd8NPN+GD6bjnYm2VuPuCXmpuYvmCXQ==", - "dev": true, - "funding": [ - { - "type": "github", - "url": "https://github.com/sponsors/feross" - }, - { - "type": "patreon", - "url": "https://www.patreon.com/feross" - }, - { - "type": "consulting", - "url": "https://feross.org/support" - } - ] - }, "node_modules/string-width": { "version": "4.2.3", "resolved": "https://registry.npmjs.org/string-width/-/string-width-4.2.3.tgz", @@ -5796,15 +5750,12 @@ } }, "node_modules/strip-json-comments": { - "version": "3.1.1", - "resolved": "https://registry.npmjs.org/strip-json-comments/-/strip-json-comments-3.1.1.tgz", - "integrity": "sha512-6fPc+R4ihwqP6N/aIv2f1gMH8lOVtWQHoqC4yK6oSDVVocumAsfCqjkXnqiYMhmMwS/mEHLp7Vehlt3ql6lEig==", + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/strip-json-comments/-/strip-json-comments-2.0.1.tgz", + "integrity": "sha1-PFMZQukIwml8DsNEhYwobHygpgo=", "dev": true, "engines": { - "node": ">=8" - }, - "funding": { - "url": "https://github.com/sponsors/sindresorhus" + "node": ">=0.10.0" } }, "node_modules/strip-outer": { @@ -5819,15 +5770,6 @@ "node": ">=0.10.0" } }, - "node_modules/strip-outer/node_modules/escape-string-regexp": { - "version": "1.0.5", - "resolved": "https://registry.npmjs.org/escape-string-regexp/-/escape-string-regexp-1.0.5.tgz", - "integrity": "sha1-G2HAViGQqN/2rjuyzwIAyhMLhtQ=", - "dev": true, - "engines": { - "node": ">=0.8.0" - } - }, "node_modules/supports-color": { "version": "7.2.0", "resolved": "https://registry.npmjs.org/supports-color/-/supports-color-7.2.0.tgz", @@ -5926,15 +5868,6 @@ "node": ">=0.10.0" } }, - "node_modules/trim-repeated/node_modules/escape-string-regexp": { - "version": "1.0.5", - "resolved": "https://registry.npmjs.org/escape-string-regexp/-/escape-string-regexp-1.0.5.tgz", - "integrity": "sha1-G2HAViGQqN/2rjuyzwIAyhMLhtQ=", - "dev": true, - "engines": { - "node": ">=0.8.0" - } - }, "node_modules/ts-node": { "version": "10.4.0", "resolved": "https://registry.npmjs.org/ts-node/-/ts-node-10.4.0.tgz", @@ -5986,9 +5919,9 @@ } }, "node_modules/tslib": { - "version": "2.1.0", - "resolved": "https://registry.npmjs.org/tslib/-/tslib-2.1.0.tgz", - "integrity": "sha512-hcVC3wYEziELGGmEEXue7D75zbwIIVUMWAVbHItGPx0ziyXxrOMQx4rQEVEV45Ut/1IotuEvwqPopzIOkDMf0A==", + "version": "2.3.1", + "resolved": "https://registry.npmjs.org/tslib/-/tslib-2.3.1.tgz", + "integrity": "sha512-77EbyPPpMz+FRFRuAFlWMtmgUWGe9UOG2Z25NqCwiIjRhOf5iKGuzSe5P2w1laq+FkRy4p+PCuVkJSGkzTEKVw==", "dev": true }, "node_modules/tsutils": { @@ -6034,9 +5967,9 @@ } }, "node_modules/type-fest": { - "version": "0.20.2", - "resolved": "https://registry.npmjs.org/type-fest/-/type-fest-0.20.2.tgz", - "integrity": "sha512-Ne+eE4r0/iWnpAxD852z3A+N0Bt5RN//NjJwRd2VFHEmrywxf5vsZlh4R6lixl6B+wz/8d+maTSAkN1FIkI3LQ==", + "version": "0.21.3", + "resolved": "https://registry.npmjs.org/type-fest/-/type-fest-0.21.3.tgz", + "integrity": "sha512-t0rzBq87m3fVcduHDUFhKmyyX+9eo6WQjZvf51Ea/M0Q7+T374Jp1aUiyUl0GKxp8M/OETVHSDvmkyPgvX+X2w==", "dev": true, "engines": { "node": ">=10" @@ -6076,26 +6009,6 @@ "typescript": "4.0.x || 4.1.x || 4.2.x || 4.3.x || 4.4.x || 4.5.x" } }, - "node_modules/typedoc/node_modules/glob": { - "version": "7.2.0", - "resolved": "https://registry.npmjs.org/glob/-/glob-7.2.0.tgz", - "integrity": "sha512-lmLf6gtyrPq8tTjSmrO94wBeQbFR3HbLHbuyD69wuyQkImp2hWqMGB47OX65FBkPffO641IP9jWa1z4ivqG26Q==", - "dev": true, - "dependencies": { - "fs.realpath": "^1.0.0", - "inflight": "^1.0.4", - "inherits": "2", - "minimatch": "^3.0.4", - "once": "^1.3.0", - "path-is-absolute": "^1.0.0" - }, - "engines": { - "node": "*" - }, - "funding": { - "url": "https://github.com/sponsors/isaacs" - } - }, "node_modules/typescript": { "version": "4.5.4", "resolved": "https://registry.npmjs.org/typescript/-/typescript-4.5.4.tgz", @@ -6182,6 +6095,21 @@ "is-ci": "bin.js" } }, + "node_modules/update-notifier/node_modules/semver": { + "version": "7.3.5", + "resolved": "https://registry.npmjs.org/semver/-/semver-7.3.5.tgz", + "integrity": "sha512-PoeGJYh8HK4BTO/a9Tf6ZG3veo/A7ZVsYrSA6J8ny9nb3B1VrpkuN+z9OE5wfE5p6H4LchYZsegiQgbJD94ZFQ==", + "dev": true, + "dependencies": { + "lru-cache": "^6.0.0" + }, + "bin": { + "semver": "bin/semver.js" + }, + "engines": { + "node": ">=10" + } + }, "node_modules/uri-js": { "version": "4.4.1", "resolved": "https://registry.npmjs.org/uri-js/-/uri-js-4.4.1.tgz", @@ -6216,13 +6144,12 @@ "dev": true }, "node_modules/uuid": { - "version": "3.4.0", - "resolved": "https://registry.npmjs.org/uuid/-/uuid-3.4.0.tgz", - "integrity": "sha512-HjSDRw6gZE5JMggctHBcjVak08+KEVhSIiDzFnT9S9aegmp85S/bReBVTb4QTFaRNptJ9kuYaNhnbNEOkbKb/A==", - "deprecated": "Please upgrade to version 7 or higher. Older versions may use Math.random() in certain circumstances, which is known to be problematic. See https://v8.dev/blog/math-random for details.", + "version": "8.3.2", + "resolved": "https://registry.npmjs.org/uuid/-/uuid-8.3.2.tgz", + "integrity": "sha512-+NYs2QeMWy+GWFOEm9xnn6HCDp0l7QBD7ml8zLUmJ+93Q5NF0NocErnwkTkXVFNiX3/fpC6afS8Dhb/gz7R7eg==", "dev": true, "bin": { - "uuid": "bin/uuid" + "uuid": "dist/bin/uuid" } }, "node_modules/v8-compile-cache": { @@ -6446,9 +6373,9 @@ } }, "node_modules/yargs": { - "version": "17.3.0", - "resolved": "https://registry.npmjs.org/yargs/-/yargs-17.3.0.tgz", - "integrity": "sha512-GQl1pWyDoGptFPJx9b9L6kmR33TGusZvXIZUT+BOz9f7X2L94oeAskFYLEg/FkhV06zZPBYLvLZRWeYId29lew==", + "version": "17.3.1", + "resolved": "https://registry.npmjs.org/yargs/-/yargs-17.3.1.tgz", + "integrity": "sha512-WUANQeVgjLbNsEmGk20f+nlHgOqzRFpiGWVaBrYGYIGANIIu3lWjoyi0fNlFmJkvfhCZ6BXINe7/W2O2bV4iaA==", "dev": true, "dependencies": { "cliui": "^7.0.2", @@ -6464,9 +6391,9 @@ } }, "node_modules/yargs-parser": { - "version": "20.2.4", - "resolved": "https://registry.npmjs.org/yargs-parser/-/yargs-parser-20.2.4.tgz", - "integrity": "sha512-WOkpgNhPTlE73h4VFAFsOnomJVaovO8VqLDzy5saChRBFQFBoMYirowyW+Q9HB4HFF4Z7VZTiG3iSzJJA29yRA==", + "version": "20.2.9", + "resolved": "https://registry.npmjs.org/yargs-parser/-/yargs-parser-20.2.9.tgz", + "integrity": "sha512-y11nGElTIV+CT3Zv9t7VKl+Q3hTQoT9a1Qzezhhl6Rp21gJ/IVTW7Z3y9EWXhuUBC2Shnf+DX0antecpAwSP8w==", "dev": true, "engines": { "node": ">=10" @@ -6487,18 +6414,6 @@ "node": ">=10" } }, - "node_modules/yargs-unparser/node_modules/camelcase": { - "version": "6.2.1", - "resolved": "https://registry.npmjs.org/camelcase/-/camelcase-6.2.1.tgz", - "integrity": "sha512-tVI4q5jjFV5CavAU8DXfza/TJcZutVKo/5Foskmsqcm0MsL91moHvwiGNnqaa2o6PF/7yT5ikDRcVcl8Rj6LCA==", - "dev": true, - "engines": { - "node": ">=10" - }, - "funding": { - "url": "https://github.com/sponsors/sindresorhus" - } - }, "node_modules/yargs-unparser/node_modules/decamelize": { "version": "4.0.0", "resolved": "https://registry.npmjs.org/decamelize/-/decamelize-4.0.0.tgz", @@ -6541,6 +6456,25 @@ "url": "https://github.com/sponsors/sindresorhus" } }, + "packages/bloom": { + "name": "@node-redis/bloom", + "version": "1.0.0", + "license": "MIT", + "devDependencies": { + "@istanbuljs/nyc-config-typescript": "^1.0.2", + "@node-redis/test-utils": "*", + "@types/node": "^17.0.5", + "nyc": "^15.1.0", + "release-it": "^14.11.8", + "source-map-support": "^0.5.21", + "ts-node": "^10.4.0", + "typedoc": "^0.22.10", + "typescript": "^4.5.4" + }, + "peerDependencies": { + "@node-redis/client": "^1.0.0" + } + }, "packages/client": { "name": "@node-redis/client", "version": "1.0.1", @@ -6554,12 +6488,12 @@ "devDependencies": { "@istanbuljs/nyc-config-typescript": "^1.0.2", "@node-redis/test-utils": "*", - "@types/node": "^17.0.1", + "@types/node": "^17.0.5", "@types/redis-parser": "^3.0.0", "@types/sinon": "^10.0.6", "@types/yallist": "^4.0.1", - "@typescript-eslint/eslint-plugin": "^5.8.0", - "@typescript-eslint/parser": "^5.8.0", + "@typescript-eslint/eslint-plugin": "^5.8.1", + "@typescript-eslint/parser": "^5.8.1", "eslint": "^8.5.0", "nyc": "^15.1.0", "release-it": "^14.11.8", @@ -6573,6 +6507,25 @@ "node": ">=12" } }, + "packages/graph": { + "name": "@node-redis/graph", + "version": "1.0.0", + "license": "MIT", + "devDependencies": { + "@istanbuljs/nyc-config-typescript": "^1.0.2", + "@node-redis/test-utils": "*", + "@types/node": "^17.0.1", + "nyc": "^15.1.0", + "release-it": "^14.11.8", + "source-map-support": "^0.5.21", + "ts-node": "^10.4.0", + "typedoc": "^0.22.10", + "typescript": "^4.5.4" + }, + "peerDependencies": { + "@node-redis/client": "^1.0.0" + } + }, "packages/json": { "name": "@node-redis/json", "version": "1.0.1", @@ -6580,7 +6533,7 @@ "devDependencies": { "@istanbuljs/nyc-config-typescript": "^1.0.2", "@node-redis/test-utils": "*", - "@types/node": "^17.0.1", + "@types/node": "^17.0.5", "nyc": "^15.1.0", "release-it": "^14.11.8", "source-map-support": "^0.5.21", @@ -6599,7 +6552,7 @@ "devDependencies": { "@istanbuljs/nyc-config-typescript": "^1.0.2", "@node-redis/test-utils": "*", - "@types/node": "^17.0.1", + "@types/node": "^17.0.5", "nyc": "^15.1.0", "release-it": "^14.11.8", "source-map-support": "^0.5.21", @@ -6616,15 +6569,15 @@ "devDependencies": { "@istanbuljs/nyc-config-typescript": "^1.0.2", "@types/mocha": "^9.0.0", - "@types/node": "^17.0.1", - "@types/yargs": "^17.0.7", + "@types/node": "^17.0.5", + "@types/yargs": "^17.0.8", "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.4", - "yargs": "^17.3.0" + "yargs": "^17.3.1" }, "peerDependencies": { "@node-redis/client": "^1.0.0" @@ -6637,7 +6590,7 @@ "devDependencies": { "@istanbuljs/nyc-config-typescript": "^1.0.2", "@node-redis/test-utils": "*", - "@types/node": "^17.0.1", + "@types/node": "^17.0.5", "nyc": "^15.1.0", "release-it": "^14.11.8", "source-map-support": "^0.5.21", @@ -6648,23 +6601,6 @@ "peerDependencies": { "@node-redis/client": "^1.0.0" } - }, - "packages/time-series": { - "version": "1.0.0-rc.0", - "license": "MIT", - "devDependencies": { - "@istanbuljs/nyc-config-typescript": "^1.0.1", - "@node-redis/test-utils": "*", - "@types/node": "^16.11.7", - "nyc": "^15.1.0", - "release-it": "^14.11.7", - "source-map-support": "^0.5.20", - "ts-node": "^10.4.0", - "typescript": "^4.4.4" - }, - "peerDependencies": { - "@node-redis/client": "^1.0.0" - } } }, "dependencies": { @@ -6704,14 +6640,6 @@ "json5": "^2.1.2", "semver": "^6.3.0", "source-map": "^0.5.0" - }, - "dependencies": { - "semver": { - "version": "6.3.0", - "resolved": "https://registry.npmjs.org/semver/-/semver-6.3.0.tgz", - "integrity": "sha512-b39TBaTSfV6yBrapU89p5fKekE2m/NwnDocOVruQFS1/veMgdzuPcnOM34M6CwxW8jH/lxEa5rBoDeUwu5HHTw==", - "dev": true - } } }, "@babel/generator": { @@ -6735,14 +6663,6 @@ "@babel/helper-validator-option": "^7.14.5", "browserslist": "^4.17.5", "semver": "^6.3.0" - }, - "dependencies": { - "semver": { - "version": "6.3.0", - "resolved": "https://registry.npmjs.org/semver/-/semver-6.3.0.tgz", - "integrity": "sha512-b39TBaTSfV6yBrapU89p5fKekE2m/NwnDocOVruQFS1/veMgdzuPcnOM34M6CwxW8jH/lxEa5rBoDeUwu5HHTw==", - "dev": true - } } }, "@babel/helper-environment-visitor": { @@ -6895,12 +6815,6 @@ "integrity": "sha1-p9BVi9icQveV3UIyj3QIMcpTvCU=", "dev": true }, - "escape-string-regexp": { - "version": "1.0.5", - "resolved": "https://registry.npmjs.org/escape-string-regexp/-/escape-string-regexp-1.0.5.tgz", - "integrity": "sha1-G2HAViGQqN/2rjuyzwIAyhMLhtQ=", - "dev": true - }, "has-flag": { "version": "3.0.0", "resolved": "https://registry.npmjs.org/has-flag/-/has-flag-3.0.0.tgz", @@ -7008,6 +6922,12 @@ "resolved": "https://registry.npmjs.org/ignore/-/ignore-4.0.6.tgz", "integrity": "sha512-cyFDKrqc/YdcWFniJhzI42+AzS+gNwmUzOSFcRCQYwySuBBBy/KjuxWLZ/FHEH6Moq1NizMOBWyTcv8O4OZIMg==", "dev": true + }, + "strip-json-comments": { + "version": "3.1.1", + "resolved": "https://registry.npmjs.org/strip-json-comments/-/strip-json-comments-3.1.1.tgz", + "integrity": "sha512-6fPc+R4ihwqP6N/aIv2f1gMH8lOVtWQHoqC4yK6oSDVVocumAsfCqjkXnqiYMhmMwS/mEHLp7Vehlt3ql6lEig==", + "dev": true } } }, @@ -7056,15 +6976,11 @@ "sprintf-js": "~1.0.2" } }, - "find-up": { - "version": "4.1.0", - "resolved": "https://registry.npmjs.org/find-up/-/find-up-4.1.0.tgz", - "integrity": "sha512-PpOwAdQ/YlXQ2vj8a3h8IipDuYRi3wceVQQGYWxNINccq40Anw7BlsEXCMbt1Zt+OLA6Fq9suIpIWD0OsnISlw==", - "dev": true, - "requires": { - "locate-path": "^5.0.0", - "path-exists": "^4.0.0" - } + "camelcase": { + "version": "5.3.1", + "resolved": "https://registry.npmjs.org/camelcase/-/camelcase-5.3.1.tgz", + "integrity": "sha512-L28STB170nwWS63UjtlEOE3dldQApaJXZkOI1uMFfzf3rRuPegHaHesyee+YxQ+W6SvRDQV6UrdOdRiR153wJg==", + "dev": true }, "js-yaml": { "version": "3.14.1", @@ -7076,33 +6992,6 @@ "esprima": "^4.0.0" } }, - "locate-path": { - "version": "5.0.0", - "resolved": "https://registry.npmjs.org/locate-path/-/locate-path-5.0.0.tgz", - "integrity": "sha512-t7hw9pI+WvuwNJXwk5zVHpyhIqzg2qTlklJOf0mVxGSbe3Fp2VieZcduNYjaLDoy6p9uGpQEGWG87WpMKlNq8g==", - "dev": true, - "requires": { - "p-locate": "^4.1.0" - } - }, - "p-limit": { - "version": "2.3.0", - "resolved": "https://registry.npmjs.org/p-limit/-/p-limit-2.3.0.tgz", - "integrity": "sha512-//88mFWSJx8lxCzwdAABTJL2MyWB12+eIY7MDL2SqLmAkeKU9qxRvWuSyTjm3FUmpBEMuFfckAIqEaVGUDxb6w==", - "dev": true, - "requires": { - "p-try": "^2.0.0" - } - }, - "p-locate": { - "version": "4.1.0", - "resolved": "https://registry.npmjs.org/p-locate/-/p-locate-4.1.0.tgz", - "integrity": "sha512-R79ZZ/0wAxKGu3oYMlz8jy/kbhsNrS7SKZ7PxEHBgJ5+F2mtFW2fK2cOtBh1cHYkQsbzFV7I+EoRKe6Yt0oK7A==", - "dev": true, - "requires": { - "p-limit": "^2.2.0" - } - }, "resolve-from": { "version": "5.0.0", "resolved": "https://registry.npmjs.org/resolve-from/-/resolve-from-5.0.0.tgz", @@ -7126,17 +7015,31 @@ "integrity": "sha512-ZXRY4jNvVgSVQ8DL3LTcakaAtXwTVUxE81hslsyD2AtoXW/wVob10HkOJ1X/pAlcI7D+2YoZKg5do8G/w6RYgA==", "dev": true }, + "@node-redis/bloom": { + "version": "file:packages/bloom", + "requires": { + "@istanbuljs/nyc-config-typescript": "^1.0.2", + "@node-redis/test-utils": "*", + "@types/node": "^17.0.5", + "nyc": "^15.1.0", + "release-it": "^14.11.8", + "source-map-support": "^0.5.21", + "ts-node": "^10.4.0", + "typedoc": "^0.22.10", + "typescript": "^4.5.4" + } + }, "@node-redis/client": { "version": "file:packages/client", "requires": { "@istanbuljs/nyc-config-typescript": "^1.0.2", "@node-redis/test-utils": "*", - "@types/node": "^17.0.1", + "@types/node": "^17.0.5", "@types/redis-parser": "^3.0.0", "@types/sinon": "^10.0.6", "@types/yallist": "^4.0.1", - "@typescript-eslint/eslint-plugin": "^5.8.0", - "@typescript-eslint/parser": "^5.8.0", + "@typescript-eslint/eslint-plugin": "^5.8.1", + "@typescript-eslint/parser": "^5.8.1", "cluster-key-slot": "1.1.0", "eslint": "^8.5.0", "generic-pool": "3.8.2", @@ -7151,12 +7054,26 @@ "yallist": "4.0.0" } }, + "@node-redis/graph": { + "version": "file:packages/graph", + "requires": { + "@istanbuljs/nyc-config-typescript": "^1.0.2", + "@node-redis/test-utils": "*", + "@types/node": "^17.0.1", + "nyc": "^15.1.0", + "release-it": "^14.11.8", + "source-map-support": "^0.5.21", + "ts-node": "^10.4.0", + "typedoc": "^0.22.10", + "typescript": "^4.5.4" + } + }, "@node-redis/json": { "version": "file:packages/json", "requires": { "@istanbuljs/nyc-config-typescript": "^1.0.2", "@node-redis/test-utils": "*", - "@types/node": "^17.0.1", + "@types/node": "^17.0.5", "nyc": "^15.1.0", "release-it": "^14.11.8", "source-map-support": "^0.5.21", @@ -7170,7 +7087,7 @@ "requires": { "@istanbuljs/nyc-config-typescript": "^1.0.2", "@node-redis/test-utils": "*", - "@types/node": "^17.0.1", + "@types/node": "^17.0.5", "nyc": "^15.1.0", "release-it": "^14.11.8", "source-map-support": "^0.5.21", @@ -7184,15 +7101,15 @@ "requires": { "@istanbuljs/nyc-config-typescript": "^1.0.2", "@types/mocha": "^9.0.0", - "@types/node": "^17.0.1", - "@types/yargs": "^17.0.7", + "@types/node": "^17.0.5", + "@types/yargs": "^17.0.8", "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.4", - "yargs": "^17.3.0" + "yargs": "^17.3.1" } }, "@node-redis/time-series": { @@ -7200,7 +7117,7 @@ "requires": { "@istanbuljs/nyc-config-typescript": "^1.0.2", "@node-redis/test-utils": "*", - "@types/node": "^17.0.1", + "@types/node": "^17.0.5", "nyc": "^15.1.0", "release-it": "^14.11.8", "source-map-support": "^0.5.21", @@ -7209,19 +7126,6 @@ "typescript": "^4.5.4" } }, - "@node-redis/time-series": { - "version": "file:packages/time-series", - "requires": { - "@istanbuljs/nyc-config-typescript": "^1.0.1", - "@node-redis/test-utils": "*", - "@types/node": "^16.11.7", - "nyc": "^15.1.0", - "release-it": "^14.11.7", - "source-map-support": "^0.5.20", - "ts-node": "^10.4.0", - "typescript": "^4.4.4" - } - }, "@nodelib/fs.scandir": { "version": "2.1.5", "resolved": "https://registry.npmjs.org/@nodelib/fs.scandir/-/fs.scandir-2.1.5.tgz", @@ -7486,9 +7390,9 @@ "dev": true }, "@types/node": { - "version": "17.0.1", - "resolved": "https://registry.npmjs.org/@types/node/-/node-17.0.1.tgz", - "integrity": "sha512-NXKvBVUzIbs6ylBwmOwHFkZS2EXCcjnqr8ZCRNaXBkHAf+3mn/rPcJxwrzuc6movh8fxQAsUUfYklJ/EG+hZqQ==", + "version": "17.0.5", + "resolved": "https://registry.npmjs.org/@types/node/-/node-17.0.5.tgz", + "integrity": "sha512-w3mrvNXLeDYV1GKTZorGJQivK6XLCoGwpnyJFbJVK/aTBQUxOCaa/GlFAAN3OTDFcb7h5tiFG+YXCO2By+riZw==", "dev": true }, "@types/parse-json": { @@ -7538,9 +7442,9 @@ "dev": true }, "@types/yargs": { - "version": "17.0.7", - "resolved": "https://registry.npmjs.org/@types/yargs/-/yargs-17.0.7.tgz", - "integrity": "sha512-OvLKmpKdea1aWtqHv9bxVVcMoT6syAeK+198dfETIFkAevYRGwqh4H+KFxfjUETZuUuE5sQCAFwdOdoHUdo8eg==", + "version": "17.0.8", + "resolved": "https://registry.npmjs.org/@types/yargs/-/yargs-17.0.8.tgz", + "integrity": "sha512-wDeUwiUmem9FzsyysEwRukaEdDNcwbROvQ9QGRKaLI6t+IltNzbn4/i4asmB10auvZGQCzSQ6t0GSczEThlUXw==", "dev": true, "requires": { "@types/yargs-parser": "*" @@ -7553,85 +7457,127 @@ "dev": true }, "@typescript-eslint/eslint-plugin": { - "version": "5.8.0", - "resolved": "https://registry.npmjs.org/@typescript-eslint/eslint-plugin/-/eslint-plugin-5.8.0.tgz", - "integrity": "sha512-spu1UW7QuBn0nJ6+psnfCc3iVoQAifjKORgBngKOmC8U/1tbe2YJMzYQqDGYB4JCss7L8+RM2kKLb1B1Aw9BNA==", + "version": "5.8.1", + "resolved": "https://registry.npmjs.org/@typescript-eslint/eslint-plugin/-/eslint-plugin-5.8.1.tgz", + "integrity": "sha512-wTZ5oEKrKj/8/366qTM366zqhIKAp6NCMweoRONtfuC07OAU9nVI2GZZdqQ1qD30WAAtcPdkH+npDwtRFdp4Rw==", "dev": true, "requires": { - "@typescript-eslint/experimental-utils": "5.8.0", - "@typescript-eslint/scope-manager": "5.8.0", + "@typescript-eslint/experimental-utils": "5.8.1", + "@typescript-eslint/scope-manager": "5.8.1", "debug": "^4.3.2", "functional-red-black-tree": "^1.0.1", "ignore": "^5.1.8", "regexpp": "^3.2.0", "semver": "^7.3.5", "tsutils": "^3.21.0" + }, + "dependencies": { + "semver": { + "version": "7.3.5", + "resolved": "https://registry.npmjs.org/semver/-/semver-7.3.5.tgz", + "integrity": "sha512-PoeGJYh8HK4BTO/a9Tf6ZG3veo/A7ZVsYrSA6J8ny9nb3B1VrpkuN+z9OE5wfE5p6H4LchYZsegiQgbJD94ZFQ==", + "dev": true, + "requires": { + "lru-cache": "^6.0.0" + } + } } }, "@typescript-eslint/experimental-utils": { - "version": "5.8.0", - "resolved": "https://registry.npmjs.org/@typescript-eslint/experimental-utils/-/experimental-utils-5.8.0.tgz", - "integrity": "sha512-KN5FvNH71bhZ8fKtL+lhW7bjm7cxs1nt+hrDZWIqb6ViCffQcWyLunGrgvISgkRojIDcXIsH+xlFfI4RCDA0xA==", + "version": "5.8.1", + "resolved": "https://registry.npmjs.org/@typescript-eslint/experimental-utils/-/experimental-utils-5.8.1.tgz", + "integrity": "sha512-fbodVnjIDU4JpeXWRDsG5IfIjYBxEvs8EBO8W1+YVdtrc2B9ppfof5sZhVEDOtgTfFHnYQJDI8+qdqLYO4ceww==", "dev": true, "requires": { "@types/json-schema": "^7.0.9", - "@typescript-eslint/scope-manager": "5.8.0", - "@typescript-eslint/types": "5.8.0", - "@typescript-eslint/typescript-estree": "5.8.0", + "@typescript-eslint/scope-manager": "5.8.1", + "@typescript-eslint/types": "5.8.1", + "@typescript-eslint/typescript-estree": "5.8.1", "eslint-scope": "^5.1.1", "eslint-utils": "^3.0.0" } }, "@typescript-eslint/parser": { - "version": "5.8.0", - "resolved": "https://registry.npmjs.org/@typescript-eslint/parser/-/parser-5.8.0.tgz", - "integrity": "sha512-Gleacp/ZhRtJRYs5/T8KQR3pAQjQI89Dn/k+OzyCKOsLiZH2/Vh60cFBTnFsHNI6WAD+lNUo/xGZ4NeA5u0Ipw==", + "version": "5.8.1", + "resolved": "https://registry.npmjs.org/@typescript-eslint/parser/-/parser-5.8.1.tgz", + "integrity": "sha512-K1giKHAjHuyB421SoXMXFHHVI4NdNY603uKw92++D3qyxSeYvC10CBJ/GE5Thpo4WTUvu1mmJI2/FFkz38F2Gw==", "dev": true, "requires": { - "@typescript-eslint/scope-manager": "5.8.0", - "@typescript-eslint/types": "5.8.0", - "@typescript-eslint/typescript-estree": "5.8.0", + "@typescript-eslint/scope-manager": "5.8.1", + "@typescript-eslint/types": "5.8.1", + "@typescript-eslint/typescript-estree": "5.8.1", "debug": "^4.3.2" } }, "@typescript-eslint/scope-manager": { - "version": "5.8.0", - "resolved": "https://registry.npmjs.org/@typescript-eslint/scope-manager/-/scope-manager-5.8.0.tgz", - "integrity": "sha512-x82CYJsLOjPCDuFFEbS6e7K1QEWj7u5Wk1alw8A+gnJiYwNnDJk0ib6PCegbaPMjrfBvFKa7SxE3EOnnIQz2Gg==", + "version": "5.8.1", + "resolved": "https://registry.npmjs.org/@typescript-eslint/scope-manager/-/scope-manager-5.8.1.tgz", + "integrity": "sha512-DGxJkNyYruFH3NIZc3PwrzwOQAg7vvgsHsHCILOLvUpupgkwDZdNq/cXU3BjF4LNrCsVg0qxEyWasys5AiJ85Q==", "dev": true, "requires": { - "@typescript-eslint/types": "5.8.0", - "@typescript-eslint/visitor-keys": "5.8.0" + "@typescript-eslint/types": "5.8.1", + "@typescript-eslint/visitor-keys": "5.8.1" } }, "@typescript-eslint/types": { - "version": "5.8.0", - "resolved": "https://registry.npmjs.org/@typescript-eslint/types/-/types-5.8.0.tgz", - "integrity": "sha512-LdCYOqeqZWqCMOmwFnum6YfW9F3nKuxJiR84CdIRN5nfHJ7gyvGpXWqL/AaW0k3Po0+wm93ARAsOdzlZDPCcXg==", + "version": "5.8.1", + "resolved": "https://registry.npmjs.org/@typescript-eslint/types/-/types-5.8.1.tgz", + "integrity": "sha512-L/FlWCCgnjKOLefdok90/pqInkomLnAcF9UAzNr+DSqMC3IffzumHTQTrINXhP1gVp9zlHiYYjvozVZDPleLcA==", "dev": true }, "@typescript-eslint/typescript-estree": { - "version": "5.8.0", - "resolved": "https://registry.npmjs.org/@typescript-eslint/typescript-estree/-/typescript-estree-5.8.0.tgz", - "integrity": "sha512-srfeZ3URdEcUsSLbkOFqS7WoxOqn8JNil2NSLO9O+I2/Uyc85+UlfpEvQHIpj5dVts7KKOZnftoJD/Fdv0L7nQ==", + "version": "5.8.1", + "resolved": "https://registry.npmjs.org/@typescript-eslint/typescript-estree/-/typescript-estree-5.8.1.tgz", + "integrity": "sha512-26lQ8l8tTbG7ri7xEcCFT9ijU5Fk+sx/KRRyyzCv7MQ+rZZlqiDPtMKWLC8P7o+dtCnby4c+OlxuX1tp8WfafQ==", "dev": true, "requires": { - "@typescript-eslint/types": "5.8.0", - "@typescript-eslint/visitor-keys": "5.8.0", + "@typescript-eslint/types": "5.8.1", + "@typescript-eslint/visitor-keys": "5.8.1", "debug": "^4.3.2", "globby": "^11.0.4", "is-glob": "^4.0.3", "semver": "^7.3.5", "tsutils": "^3.21.0" + }, + "dependencies": { + "array-union": { + "version": "2.1.0", + "resolved": "https://registry.npmjs.org/array-union/-/array-union-2.1.0.tgz", + "integrity": "sha512-HGyxoOTYUyCM6stUe6EJgnd4EoewAI7zMdfqO+kGjnlZmBDz/cR5pf8r/cR4Wq60sL/p0IkcjUEEPwS3GFrIyw==", + "dev": true + }, + "globby": { + "version": "11.0.4", + "resolved": "https://registry.npmjs.org/globby/-/globby-11.0.4.tgz", + "integrity": "sha512-9O4MVG9ioZJ08ffbcyVYyLOJLk5JQ688pJ4eMGLpdWLHq/Wr1D9BlriLQyL0E+jbkuePVZXYFj47QM/v093wHg==", + "dev": true, + "requires": { + "array-union": "^2.1.0", + "dir-glob": "^3.0.1", + "fast-glob": "^3.1.1", + "ignore": "^5.1.4", + "merge2": "^1.3.0", + "slash": "^3.0.0" + } + }, + "semver": { + "version": "7.3.5", + "resolved": "https://registry.npmjs.org/semver/-/semver-7.3.5.tgz", + "integrity": "sha512-PoeGJYh8HK4BTO/a9Tf6ZG3veo/A7ZVsYrSA6J8ny9nb3B1VrpkuN+z9OE5wfE5p6H4LchYZsegiQgbJD94ZFQ==", + "dev": true, + "requires": { + "lru-cache": "^6.0.0" + } + } } }, "@typescript-eslint/visitor-keys": { - "version": "5.8.0", - "resolved": "https://registry.npmjs.org/@typescript-eslint/visitor-keys/-/visitor-keys-5.8.0.tgz", - "integrity": "sha512-+HDIGOEMnqbxdAHegxvnOqESUH6RWFRR2b8qxP1W9CZnnYh4Usz6MBL+2KMAgPk/P0o9c1HqnYtwzVH6GTIqug==", + "version": "5.8.1", + "resolved": "https://registry.npmjs.org/@typescript-eslint/visitor-keys/-/visitor-keys-5.8.1.tgz", + "integrity": "sha512-SWgiWIwocK6NralrJarPZlWdr0hZnj5GXHIgfdm8hNkyKvpeQuFyLP6YjSIe9kf3YBIfU6OHSZLYkQ+smZwtNg==", "dev": true, "requires": { - "@typescript-eslint/types": "5.8.0", + "@typescript-eslint/types": "5.8.1", "eslint-visitor-keys": "^3.0.0" } }, @@ -7642,9 +7588,9 @@ "dev": true }, "acorn": { - "version": "8.6.0", - "resolved": "https://registry.npmjs.org/acorn/-/acorn-8.6.0.tgz", - "integrity": "sha512-U1riIR+lBSNi3IbxtaHOIKdH8sLFv3NYfNv8sg7ZsNhcfl4HF2++BfqqrNAxoCLQW1iiylOj76ecnaUxz+z9yw==", + "version": "8.7.0", + "resolved": "https://registry.npmjs.org/acorn/-/acorn-8.7.0.tgz", + "integrity": "sha512-V/LGr1APy+PXIwKebEWrkZPwoeoF+w1jiOBUmuxuiUIaOHtob8Qc9BTrYo7VuI5fR8tqsy+buA2WFooR5olqvQ==", "dev": true }, "acorn-jsx": { @@ -7704,14 +7650,6 @@ "dev": true, "requires": { "type-fest": "^0.21.3" - }, - "dependencies": { - "type-fest": { - "version": "0.21.3", - "resolved": "https://registry.npmjs.org/type-fest/-/type-fest-0.21.3.tgz", - "integrity": "sha512-t0rzBq87m3fVcduHDUFhKmyyX+9eo6WQjZvf51Ea/M0Q7+T374Jp1aUiyUl0GKxp8M/OETVHSDvmkyPgvX+X2w==", - "dev": true - } } }, "ansi-regex": { @@ -7767,10 +7705,13 @@ "dev": true }, "array-union": { - "version": "2.1.0", - "resolved": "https://registry.npmjs.org/array-union/-/array-union-2.1.0.tgz", - "integrity": "sha512-HGyxoOTYUyCM6stUe6EJgnd4EoewAI7zMdfqO+kGjnlZmBDz/cR5pf8r/cR4Wq60sL/p0IkcjUEEPwS3GFrIyw==", - "dev": true + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/array-union/-/array-union-1.0.2.tgz", + "integrity": "sha1-mjRBDk9OPaI96jdb5b5w8kd47Dk=", + "dev": true, + "requires": { + "array-uniq": "^1.0.1" + } }, "array-uniq": { "version": "1.0.3", @@ -7853,10 +7794,10 @@ "wrap-ansi": "^7.0.0" }, "dependencies": { - "camelcase": { - "version": "6.2.1", - "resolved": "https://registry.npmjs.org/camelcase/-/camelcase-6.2.1.tgz", - "integrity": "sha512-tVI4q5jjFV5CavAU8DXfza/TJcZutVKo/5Foskmsqcm0MsL91moHvwiGNnqaa2o6PF/7yT5ikDRcVcl8Rj6LCA==", + "type-fest": { + "version": "0.20.2", + "resolved": "https://registry.npmjs.org/type-fest/-/type-fest-0.20.2.tgz", + "integrity": "sha512-Ne+eE4r0/iWnpAxD852z3A+N0Bt5RN//NjJwRd2VFHEmrywxf5vsZlh4R6lixl6B+wz/8d+maTSAkN1FIkI3LQ==", "dev": true } } @@ -7976,15 +7917,15 @@ "dev": true }, "camelcase": { - "version": "5.3.1", - "resolved": "https://registry.npmjs.org/camelcase/-/camelcase-5.3.1.tgz", - "integrity": "sha512-L28STB170nwWS63UjtlEOE3dldQApaJXZkOI1uMFfzf3rRuPegHaHesyee+YxQ+W6SvRDQV6UrdOdRiR153wJg==", + "version": "6.2.1", + "resolved": "https://registry.npmjs.org/camelcase/-/camelcase-6.2.1.tgz", + "integrity": "sha512-tVI4q5jjFV5CavAU8DXfza/TJcZutVKo/5Foskmsqcm0MsL91moHvwiGNnqaa2o6PF/7yT5ikDRcVcl8Rj6LCA==", "dev": true }, "caniuse-lite": { - "version": "1.0.30001291", - "resolved": "https://registry.npmjs.org/caniuse-lite/-/caniuse-lite-1.0.30001291.tgz", - "integrity": "sha512-roMV5V0HNGgJ88s42eE70sstqGW/gwFndosYrikHthw98N5tLnOTxFqMLQjZVRxTWFlJ4rn+MsgXrR7MDPY4jA==", + "version": "1.0.30001294", + "resolved": "https://registry.npmjs.org/caniuse-lite/-/caniuse-lite-1.0.30001294.tgz", + "integrity": "sha512-LiMlrs1nSKZ8qkNhpUf5KD0Al1KCBE3zaT7OLOwEkagXMEDij98SiOovn9wxVGQpklk9vVC/pUSqgYmkmKOS8g==", "dev": true }, "chalk": { @@ -8017,17 +7958,6 @@ "is-glob": "~4.0.1", "normalize-path": "~3.0.0", "readdirp": "~3.6.0" - }, - "dependencies": { - "glob-parent": { - "version": "5.1.2", - "resolved": "https://registry.npmjs.org/glob-parent/-/glob-parent-5.1.2.tgz", - "integrity": "sha512-AOIgSQCepiJYwP3ARnGx+5VnTu2HBYdzbGP45eLw1vr3zB3vZLeyed1sC9hnbcOc9/SrMyM5RPQrkGz4aS9Zow==", - "dev": true, - "requires": { - "is-glob": "^4.0.1" - } - } } }, "ci-info": { @@ -8163,6 +8093,14 @@ "dev": true, "requires": { "safe-buffer": "~5.1.1" + }, + "dependencies": { + "safe-buffer": { + "version": "5.1.2", + "resolved": "https://registry.npmjs.org/safe-buffer/-/safe-buffer-5.1.2.tgz", + "integrity": "sha512-Gd2UZBJDkXlY7GbJxfsE8/nvKkUEU1G38c1siN6QP6a9PT9MmHB8GnpscSmMJSoF8LOIrt8ud/wPtojys4G6+g==", + "dev": true + } } }, "cosmiconfig": { @@ -8202,9 +8140,9 @@ "dev": true }, "debug": { - "version": "4.3.3", - "resolved": "https://registry.npmjs.org/debug/-/debug-4.3.3.tgz", - "integrity": "sha512-/zxw5+vh1Tfv+4Qn7a5nsbcJKPaSvCDhojn6FEl9vupwK2VCSDtEiEtqr8DFtzYFOdz63LBkxec7DYuc2jon6Q==", + "version": "4.3.2", + "resolved": "https://registry.npmjs.org/debug/-/debug-4.3.2.tgz", + "integrity": "sha512-mOp8wKcvj7XxC78zLgw/ZA+6TSgkoE2C/ienthhRD298T7UNwAg9diBpLRxC0mOezLl4B0xV7M0cCO6P/O0Xhw==", "dev": true, "requires": { "ms": "2.1.2" @@ -8337,9 +8275,9 @@ "dev": true }, "electron-to-chromium": { - "version": "1.4.24", - "resolved": "https://registry.npmjs.org/electron-to-chromium/-/electron-to-chromium-1.4.24.tgz", - "integrity": "sha512-erwx5r69B/WFfFuF2jcNN0817BfDBdC4765kQ6WltOMuwsimlQo3JTEq0Cle+wpHralwdeX3OfAtw/mHxPK0Wg==", + "version": "1.4.30", + "resolved": "https://registry.npmjs.org/electron-to-chromium/-/electron-to-chromium-1.4.30.tgz", + "integrity": "sha512-609z9sIMxDHg+TcR/VB3MXwH+uwtrYyeAwWc/orhnr90ixs6WVGSrt85CDLGUdNnLqCA7liv426V20EecjvflQ==", "dev": true }, "email-addresses": { @@ -8400,9 +8338,9 @@ "dev": true }, "escape-string-regexp": { - "version": "4.0.0", - "resolved": "https://registry.npmjs.org/escape-string-regexp/-/escape-string-regexp-4.0.0.tgz", - "integrity": "sha512-TtpcNJ3XAzx3Gq8sWRzJaVajRs0uVxA2YAkdb1jm2YkPz4G6egUFAyA3n5vtEIZefPk5Wa4UXbKuS5fKkJWdgA==", + "version": "1.0.5", + "resolved": "https://registry.npmjs.org/escape-string-regexp/-/escape-string-regexp-1.0.5.tgz", + "integrity": "sha1-G2HAViGQqN/2rjuyzwIAyhMLhtQ=", "dev": true }, "eslint": { @@ -8451,6 +8389,12 @@ "v8-compile-cache": "^2.0.3" }, "dependencies": { + "escape-string-regexp": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/escape-string-regexp/-/escape-string-regexp-4.0.0.tgz", + "integrity": "sha512-TtpcNJ3XAzx3Gq8sWRzJaVajRs0uVxA2YAkdb1jm2YkPz4G6egUFAyA3n5vtEIZefPk5Wa4UXbKuS5fKkJWdgA==", + "dev": true + }, "eslint-scope": { "version": "7.1.0", "resolved": "https://registry.npmjs.org/eslint-scope/-/eslint-scope-7.1.0.tgz", @@ -8467,11 +8411,35 @@ "integrity": "sha512-MMdARuVEQziNTeJD8DgMqmhwR11BRQ/cBP+pLtYdSTnf3MIO8fFeiINEbX36ZdNlfU/7A9f3gUw49B3oQsvwBA==", "dev": true }, + "glob-parent": { + "version": "6.0.2", + "resolved": "https://registry.npmjs.org/glob-parent/-/glob-parent-6.0.2.tgz", + "integrity": "sha512-XxwI8EOhVQgWp6iDL+3b0r86f4d6AX6zSU55HfB4ydCEuXLXc5FcYeOu+nnGftS4TEju/11rt4KJPTMgbfmv4A==", + "dev": true, + "requires": { + "is-glob": "^4.0.3" + } + }, "ignore": { "version": "4.0.6", "resolved": "https://registry.npmjs.org/ignore/-/ignore-4.0.6.tgz", "integrity": "sha512-cyFDKrqc/YdcWFniJhzI42+AzS+gNwmUzOSFcRCQYwySuBBBy/KjuxWLZ/FHEH6Moq1NizMOBWyTcv8O4OZIMg==", "dev": true + }, + "semver": { + "version": "7.3.5", + "resolved": "https://registry.npmjs.org/semver/-/semver-7.3.5.tgz", + "integrity": "sha512-PoeGJYh8HK4BTO/a9Tf6ZG3veo/A7ZVsYrSA6J8ny9nb3B1VrpkuN+z9OE5wfE5p6H4LchYZsegiQgbJD94ZFQ==", + "dev": true, + "requires": { + "lru-cache": "^6.0.0" + } + }, + "strip-json-comments": { + "version": "3.1.1", + "resolved": "https://registry.npmjs.org/strip-json-comments/-/strip-json-comments-3.1.1.tgz", + "integrity": "sha512-6fPc+R4ihwqP6N/aIv2f1gMH8lOVtWQHoqC4yK6oSDVVocumAsfCqjkXnqiYMhmMwS/mEHLp7Vehlt3ql6lEig==", + "dev": true } } }, @@ -8616,17 +8584,6 @@ "glob-parent": "^5.1.2", "merge2": "^1.3.0", "micromatch": "^4.0.4" - }, - "dependencies": { - "glob-parent": { - "version": "5.1.2", - "resolved": "https://registry.npmjs.org/glob-parent/-/glob-parent-5.1.2.tgz", - "integrity": "sha512-AOIgSQCepiJYwP3ARnGx+5VnTu2HBYdzbGP45eLw1vr3zB3vZLeyed1sC9hnbcOc9/SrMyM5RPQrkGz4aS9Zow==", - "dev": true, - "requires": { - "is-glob": "^4.0.1" - } - } } }, "fast-json-stable-stringify": { @@ -8657,14 +8614,6 @@ "dev": true, "requires": { "escape-string-regexp": "^1.0.5" - }, - "dependencies": { - "escape-string-regexp": { - "version": "1.0.5", - "resolved": "https://registry.npmjs.org/escape-string-regexp/-/escape-string-regexp-1.0.5.tgz", - "integrity": "sha1-G2HAViGQqN/2rjuyzwIAyhMLhtQ=", - "dev": true - } } }, "file-entry-cache": { @@ -8720,12 +8669,12 @@ } }, "find-up": { - "version": "5.0.0", - "resolved": "https://registry.npmjs.org/find-up/-/find-up-5.0.0.tgz", - "integrity": "sha512-78/PXT1wlLLDgTzDs7sjq9hzz0vXD+zn+7wypEe4fXQxCmdmqfGsEPQxmiCSQI3ajFV91bVSsvNtrJRiW6nGng==", + "version": "4.1.0", + "resolved": "https://registry.npmjs.org/find-up/-/find-up-4.1.0.tgz", + "integrity": "sha512-PpOwAdQ/YlXQ2vj8a3h8IipDuYRi3wceVQQGYWxNINccq40Anw7BlsEXCMbt1Zt+OLA6Fq9suIpIWD0OsnISlw==", "dev": true, "requires": { - "locate-path": "^6.0.0", + "locate-path": "^5.0.0", "path-exists": "^4.0.0" } }, @@ -8867,30 +8816,6 @@ "find-cache-dir": "^3.3.1", "fs-extra": "^8.1.0", "globby": "^6.1.0" - }, - "dependencies": { - "array-union": { - "version": "1.0.2", - "resolved": "https://registry.npmjs.org/array-union/-/array-union-1.0.2.tgz", - "integrity": "sha1-mjRBDk9OPaI96jdb5b5w8kd47Dk=", - "dev": true, - "requires": { - "array-uniq": "^1.0.1" - } - }, - "globby": { - "version": "6.1.0", - "resolved": "https://registry.npmjs.org/globby/-/globby-6.1.0.tgz", - "integrity": "sha1-9abXDoOV4hyFj7BInWTfAkJNUGw=", - "dev": true, - "requires": { - "array-union": "^1.0.1", - "glob": "^7.0.3", - "object-assign": "^4.0.1", - "pify": "^2.0.0", - "pinkie-promise": "^2.0.0" - } - } } }, "git-up": { @@ -8913,9 +8838,9 @@ } }, "glob": { - "version": "7.1.7", - "resolved": "https://registry.npmjs.org/glob/-/glob-7.1.7.tgz", - "integrity": "sha512-OvD9ENzPLbegENnYP5UUfJIirTg4+XwMWGaQfQTY0JenxNvvIKP3U3/tAQSPIu/lHxXYSZmpXlUHeqAIdKzBLQ==", + "version": "7.2.0", + "resolved": "https://registry.npmjs.org/glob/-/glob-7.2.0.tgz", + "integrity": "sha512-lmLf6gtyrPq8tTjSmrO94wBeQbFR3HbLHbuyD69wuyQkImp2hWqMGB47OX65FBkPffO641IP9jWa1z4ivqG26Q==", "dev": true, "requires": { "fs.realpath": "^1.0.0", @@ -8927,12 +8852,12 @@ } }, "glob-parent": { - "version": "6.0.2", - "resolved": "https://registry.npmjs.org/glob-parent/-/glob-parent-6.0.2.tgz", - "integrity": "sha512-XxwI8EOhVQgWp6iDL+3b0r86f4d6AX6zSU55HfB4ydCEuXLXc5FcYeOu+nnGftS4TEju/11rt4KJPTMgbfmv4A==", + "version": "5.1.2", + "resolved": "https://registry.npmjs.org/glob-parent/-/glob-parent-5.1.2.tgz", + "integrity": "sha512-AOIgSQCepiJYwP3ARnGx+5VnTu2HBYdzbGP45eLw1vr3zB3vZLeyed1sC9hnbcOc9/SrMyM5RPQrkGz4aS9Zow==", "dev": true, "requires": { - "is-glob": "^4.0.3" + "is-glob": "^4.0.1" } }, "global-dirs": { @@ -8951,20 +8876,27 @@ "dev": true, "requires": { "type-fest": "^0.20.2" + }, + "dependencies": { + "type-fest": { + "version": "0.20.2", + "resolved": "https://registry.npmjs.org/type-fest/-/type-fest-0.20.2.tgz", + "integrity": "sha512-Ne+eE4r0/iWnpAxD852z3A+N0Bt5RN//NjJwRd2VFHEmrywxf5vsZlh4R6lixl6B+wz/8d+maTSAkN1FIkI3LQ==", + "dev": true + } } }, "globby": { - "version": "11.0.4", - "resolved": "https://registry.npmjs.org/globby/-/globby-11.0.4.tgz", - "integrity": "sha512-9O4MVG9ioZJ08ffbcyVYyLOJLk5JQ688pJ4eMGLpdWLHq/Wr1D9BlriLQyL0E+jbkuePVZXYFj47QM/v093wHg==", + "version": "6.1.0", + "resolved": "https://registry.npmjs.org/globby/-/globby-6.1.0.tgz", + "integrity": "sha1-9abXDoOV4hyFj7BInWTfAkJNUGw=", "dev": true, "requires": { - "array-union": "^2.1.0", - "dir-glob": "^3.0.1", - "fast-glob": "^3.1.1", - "ignore": "^5.1.4", - "merge2": "^1.3.0", - "slash": "^3.0.0" + "array-union": "^1.0.1", + "glob": "^7.0.3", + "object-assign": "^4.0.1", + "pify": "^2.0.0", + "pinkie-promise": "^2.0.0" } }, "got": { @@ -9399,14 +9331,6 @@ "@istanbuljs/schema": "^0.1.2", "istanbul-lib-coverage": "^3.0.0", "semver": "^6.3.0" - }, - "dependencies": { - "semver": { - "version": "6.3.0", - "resolved": "https://registry.npmjs.org/semver/-/semver-6.3.0.tgz", - "integrity": "sha512-b39TBaTSfV6yBrapU89p5fKekE2m/NwnDocOVruQFS1/veMgdzuPcnOM34M6CwxW8jH/lxEa5rBoDeUwu5HHTw==", - "dev": true - } } }, "istanbul-lib-processinfo": { @@ -9422,6 +9346,14 @@ "p-map": "^3.0.0", "rimraf": "^3.0.0", "uuid": "^3.3.3" + }, + "dependencies": { + "uuid": { + "version": "3.4.0", + "resolved": "https://registry.npmjs.org/uuid/-/uuid-3.4.0.tgz", + "integrity": "sha512-HjSDRw6gZE5JMggctHBcjVak08+KEVhSIiDzFnT9S9aegmp85S/bReBVTb4QTFaRNptJ9kuYaNhnbNEOkbKb/A==", + "dev": true + } } }, "istanbul-lib-report": { @@ -9455,9 +9387,9 @@ } }, "istanbul-reports": { - "version": "3.1.1", - "resolved": "https://registry.npmjs.org/istanbul-reports/-/istanbul-reports-3.1.1.tgz", - "integrity": "sha512-q1kvhAXWSsXfMjCdNHNPKZZv94OlspKnoGv+R9RGbnqOOQ0VbNfLFgQDVgi7hHenKsndGq3/o0OBdzDXthWcNw==", + "version": "3.1.3", + "resolved": "https://registry.npmjs.org/istanbul-reports/-/istanbul-reports-3.1.3.tgz", + "integrity": "sha512-x9LtDVtfm/t1GFiLl3NffC7hz+I1ragvgX1P/Lg1NlIagifZDKUkuuaAxH/qpwj2IuEfD8G2Bs/UKp+sZ/pKkg==", "dev": true, "requires": { "html-escaper": "^2.0.0", @@ -9574,12 +9506,12 @@ "dev": true }, "locate-path": { - "version": "6.0.0", - "resolved": "https://registry.npmjs.org/locate-path/-/locate-path-6.0.0.tgz", - "integrity": "sha512-iPZK6eYjbxRu3uB4/WZ3EsEIMJFMqAoopl3R+zuq0UjcAm/MO6KCweDgPfP3elTztoKP3KtnVHxTn2NHBSDVUw==", + "version": "5.0.0", + "resolved": "https://registry.npmjs.org/locate-path/-/locate-path-5.0.0.tgz", + "integrity": "sha512-t7hw9pI+WvuwNJXwk5zVHpyhIqzg2qTlklJOf0mVxGSbe3Fp2VieZcduNYjaLDoy6p9uGpQEGWG87WpMKlNq8g==", "dev": true, "requires": { - "p-locate": "^5.0.0" + "p-locate": "^4.1.0" } }, "lodash": { @@ -9650,14 +9582,6 @@ "dev": true, "requires": { "semver": "^6.0.0" - }, - "dependencies": { - "semver": { - "version": "6.3.0", - "resolved": "https://registry.npmjs.org/semver/-/semver-6.3.0.tgz", - "integrity": "sha512-b39TBaTSfV6yBrapU89p5fKekE2m/NwnDocOVruQFS1/veMgdzuPcnOM34M6CwxW8jH/lxEa5rBoDeUwu5HHTw==", - "dev": true - } } }, "make-error": { @@ -9768,21 +9692,43 @@ "yargs-unparser": "2.0.0" }, "dependencies": { - "debug": { - "version": "4.3.2", - "resolved": "https://registry.npmjs.org/debug/-/debug-4.3.2.tgz", - "integrity": "sha512-mOp8wKcvj7XxC78zLgw/ZA+6TSgkoE2C/ienthhRD298T7UNwAg9diBpLRxC0mOezLl4B0xV7M0cCO6P/O0Xhw==", + "escape-string-regexp": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/escape-string-regexp/-/escape-string-regexp-4.0.0.tgz", + "integrity": "sha512-TtpcNJ3XAzx3Gq8sWRzJaVajRs0uVxA2YAkdb1jm2YkPz4G6egUFAyA3n5vtEIZefPk5Wa4UXbKuS5fKkJWdgA==", + "dev": true + }, + "find-up": { + "version": "5.0.0", + "resolved": "https://registry.npmjs.org/find-up/-/find-up-5.0.0.tgz", + "integrity": "sha512-78/PXT1wlLLDgTzDs7sjq9hzz0vXD+zn+7wypEe4fXQxCmdmqfGsEPQxmiCSQI3ajFV91bVSsvNtrJRiW6nGng==", "dev": true, "requires": { - "ms": "2.1.2" - }, - "dependencies": { - "ms": { - "version": "2.1.2", - "resolved": "https://registry.npmjs.org/ms/-/ms-2.1.2.tgz", - "integrity": "sha512-sGkPx+VjMtmA6MX27oA4FBFELFCZZ4S4XqeGOXCv68tT+jb3vk/RyaKWP0PTKyWtmLSM0b+adUTEvbs1PEaH2w==", - "dev": true - } + "locate-path": "^6.0.0", + "path-exists": "^4.0.0" + } + }, + "glob": { + "version": "7.1.7", + "resolved": "https://registry.npmjs.org/glob/-/glob-7.1.7.tgz", + "integrity": "sha512-OvD9ENzPLbegENnYP5UUfJIirTg4+XwMWGaQfQTY0JenxNvvIKP3U3/tAQSPIu/lHxXYSZmpXlUHeqAIdKzBLQ==", + "dev": true, + "requires": { + "fs.realpath": "^1.0.0", + "inflight": "^1.0.4", + "inherits": "2", + "minimatch": "^3.0.4", + "once": "^1.3.0", + "path-is-absolute": "^1.0.0" + } + }, + "locate-path": { + "version": "6.0.0", + "resolved": "https://registry.npmjs.org/locate-path/-/locate-path-6.0.0.tgz", + "integrity": "sha512-iPZK6eYjbxRu3uB4/WZ3EsEIMJFMqAoopl3R+zuq0UjcAm/MO6KCweDgPfP3elTztoKP3KtnVHxTn2NHBSDVUw==", + "dev": true, + "requires": { + "p-locate": "^5.0.0" } }, "ms": { @@ -9791,6 +9737,30 @@ "integrity": "sha512-6FlzubTLZG3J2a/NVCAleEhjzq5oxgHyaCU9yYXvcLsvoVaHJq/s5xXI6/XXP6tz7R9xAOtHnSO/tXtF3WRTlA==", "dev": true }, + "p-limit": { + "version": "3.1.0", + "resolved": "https://registry.npmjs.org/p-limit/-/p-limit-3.1.0.tgz", + "integrity": "sha512-TYOanM3wGwNGsZN2cVTYPArw454xnXj5qmWF1bEoAc4+cU/ol7GVh7odevjp1FNHduHc3KZMcFduxU5Xc6uJRQ==", + "dev": true, + "requires": { + "yocto-queue": "^0.1.0" + } + }, + "p-locate": { + "version": "5.0.0", + "resolved": "https://registry.npmjs.org/p-locate/-/p-locate-5.0.0.tgz", + "integrity": "sha512-LaNjtRWUBY++zB5nE/NwcaoMylSPk+S+ZHNB1TzdbMJMny6dynpAGt7X/tl/QYq3TIeE6nxHppbo2LGymrG5Pw==", + "dev": true, + "requires": { + "p-limit": "^3.0.2" + } + }, + "strip-json-comments": { + "version": "3.1.1", + "resolved": "https://registry.npmjs.org/strip-json-comments/-/strip-json-comments-3.1.1.tgz", + "integrity": "sha512-6fPc+R4ihwqP6N/aIv2f1gMH8lOVtWQHoqC4yK6oSDVVocumAsfCqjkXnqiYMhmMwS/mEHLp7Vehlt3ql6lEig==", + "dev": true + }, "supports-color": { "version": "8.1.1", "resolved": "https://registry.npmjs.org/supports-color/-/supports-color-8.1.1.tgz", @@ -9814,6 +9784,12 @@ "y18n": "^5.0.5", "yargs-parser": "^20.2.2" } + }, + "yargs-parser": { + "version": "20.2.4", + "resolved": "https://registry.npmjs.org/yargs-parser/-/yargs-parser-20.2.4.tgz", + "integrity": "sha512-WOkpgNhPTlE73h4VFAFsOnomJVaovO8VqLDzy5saChRBFQFBoMYirowyW+Q9HB4HFF4Z7VZTiG3iSzJJA29yRA==", + "dev": true } } }, @@ -9951,6 +9927,12 @@ "yargs": "^15.0.2" }, "dependencies": { + "camelcase": { + "version": "5.3.1", + "resolved": "https://registry.npmjs.org/camelcase/-/camelcase-5.3.1.tgz", + "integrity": "sha512-L28STB170nwWS63UjtlEOE3dldQApaJXZkOI1uMFfzf3rRuPegHaHesyee+YxQ+W6SvRDQV6UrdOdRiR153wJg==", + "dev": true + }, "cliui": { "version": "6.0.0", "resolved": "https://registry.npmjs.org/cliui/-/cliui-6.0.0.tgz", @@ -9962,43 +9944,6 @@ "wrap-ansi": "^6.2.0" } }, - "find-up": { - "version": "4.1.0", - "resolved": "https://registry.npmjs.org/find-up/-/find-up-4.1.0.tgz", - "integrity": "sha512-PpOwAdQ/YlXQ2vj8a3h8IipDuYRi3wceVQQGYWxNINccq40Anw7BlsEXCMbt1Zt+OLA6Fq9suIpIWD0OsnISlw==", - "dev": true, - "requires": { - "locate-path": "^5.0.0", - "path-exists": "^4.0.0" - } - }, - "locate-path": { - "version": "5.0.0", - "resolved": "https://registry.npmjs.org/locate-path/-/locate-path-5.0.0.tgz", - "integrity": "sha512-t7hw9pI+WvuwNJXwk5zVHpyhIqzg2qTlklJOf0mVxGSbe3Fp2VieZcduNYjaLDoy6p9uGpQEGWG87WpMKlNq8g==", - "dev": true, - "requires": { - "p-locate": "^4.1.0" - } - }, - "p-limit": { - "version": "2.3.0", - "resolved": "https://registry.npmjs.org/p-limit/-/p-limit-2.3.0.tgz", - "integrity": "sha512-//88mFWSJx8lxCzwdAABTJL2MyWB12+eIY7MDL2SqLmAkeKU9qxRvWuSyTjm3FUmpBEMuFfckAIqEaVGUDxb6w==", - "dev": true, - "requires": { - "p-try": "^2.0.0" - } - }, - "p-locate": { - "version": "4.1.0", - "resolved": "https://registry.npmjs.org/p-locate/-/p-locate-4.1.0.tgz", - "integrity": "sha512-R79ZZ/0wAxKGu3oYMlz8jy/kbhsNrS7SKZ7PxEHBgJ5+F2mtFW2fK2cOtBh1cHYkQsbzFV7I+EoRKe6Yt0oK7A==", - "dev": true, - "requires": { - "p-limit": "^2.2.0" - } - }, "resolve-from": { "version": "5.0.0", "resolved": "https://registry.npmjs.org/resolve-from/-/resolve-from-5.0.0.tgz", @@ -10147,21 +10092,21 @@ "dev": true }, "p-limit": { - "version": "3.1.0", - "resolved": "https://registry.npmjs.org/p-limit/-/p-limit-3.1.0.tgz", - "integrity": "sha512-TYOanM3wGwNGsZN2cVTYPArw454xnXj5qmWF1bEoAc4+cU/ol7GVh7odevjp1FNHduHc3KZMcFduxU5Xc6uJRQ==", + "version": "2.3.0", + "resolved": "https://registry.npmjs.org/p-limit/-/p-limit-2.3.0.tgz", + "integrity": "sha512-//88mFWSJx8lxCzwdAABTJL2MyWB12+eIY7MDL2SqLmAkeKU9qxRvWuSyTjm3FUmpBEMuFfckAIqEaVGUDxb6w==", "dev": true, "requires": { - "yocto-queue": "^0.1.0" + "p-try": "^2.0.0" } }, "p-locate": { - "version": "5.0.0", - "resolved": "https://registry.npmjs.org/p-locate/-/p-locate-5.0.0.tgz", - "integrity": "sha512-LaNjtRWUBY++zB5nE/NwcaoMylSPk+S+ZHNB1TzdbMJMny6dynpAGt7X/tl/QYq3TIeE6nxHppbo2LGymrG5Pw==", + "version": "4.1.0", + "resolved": "https://registry.npmjs.org/p-locate/-/p-locate-4.1.0.tgz", + "integrity": "sha512-R79ZZ/0wAxKGu3oYMlz8jy/kbhsNrS7SKZ7PxEHBgJ5+F2mtFW2fK2cOtBh1cHYkQsbzFV7I+EoRKe6Yt0oK7A==", "dev": true, "requires": { - "p-limit": "^3.0.2" + "p-limit": "^2.2.0" } }, "p-map": { @@ -10338,12 +10283,6 @@ "dev": true } } - }, - "semver": { - "version": "6.3.0", - "resolved": "https://registry.npmjs.org/semver/-/semver-6.3.0.tgz", - "integrity": "sha512-b39TBaTSfV6yBrapU89p5fKekE2m/NwnDocOVruQFS1/veMgdzuPcnOM34M6CwxW8jH/lxEa5rBoDeUwu5HHTw==", - "dev": true } } }, @@ -10471,45 +10410,6 @@ "dev": true, "requires": { "find-up": "^4.0.0" - }, - "dependencies": { - "find-up": { - "version": "4.1.0", - "resolved": "https://registry.npmjs.org/find-up/-/find-up-4.1.0.tgz", - "integrity": "sha512-PpOwAdQ/YlXQ2vj8a3h8IipDuYRi3wceVQQGYWxNINccq40Anw7BlsEXCMbt1Zt+OLA6Fq9suIpIWD0OsnISlw==", - "dev": true, - "requires": { - "locate-path": "^5.0.0", - "path-exists": "^4.0.0" - } - }, - "locate-path": { - "version": "5.0.0", - "resolved": "https://registry.npmjs.org/locate-path/-/locate-path-5.0.0.tgz", - "integrity": "sha512-t7hw9pI+WvuwNJXwk5zVHpyhIqzg2qTlklJOf0mVxGSbe3Fp2VieZcduNYjaLDoy6p9uGpQEGWG87WpMKlNq8g==", - "dev": true, - "requires": { - "p-locate": "^4.1.0" - } - }, - "p-limit": { - "version": "2.3.0", - "resolved": "https://registry.npmjs.org/p-limit/-/p-limit-2.3.0.tgz", - "integrity": "sha512-//88mFWSJx8lxCzwdAABTJL2MyWB12+eIY7MDL2SqLmAkeKU9qxRvWuSyTjm3FUmpBEMuFfckAIqEaVGUDxb6w==", - "dev": true, - "requires": { - "p-try": "^2.0.0" - } - }, - "p-locate": { - "version": "4.1.0", - "resolved": "https://registry.npmjs.org/p-locate/-/p-locate-4.1.0.tgz", - "integrity": "sha512-R79ZZ/0wAxKGu3oYMlz8jy/kbhsNrS7SKZ7PxEHBgJ5+F2mtFW2fK2cOtBh1cHYkQsbzFV7I+EoRKe6Yt0oK7A==", - "dev": true, - "requires": { - "p-limit": "^2.2.0" - } - } } }, "prelude-ls": { @@ -10629,12 +10529,6 @@ "resolved": "https://registry.npmjs.org/ini/-/ini-1.3.8.tgz", "integrity": "sha512-JV/yugV2uzW5iMRSiZAyDtQd+nxtUnjeLt0acNdw98kKLrvuRVyB80tsREOE7yvGVgalhZ6RNXCmEHkUKBKxew==", "dev": true - }, - "strip-json-comments": { - "version": "2.0.1", - "resolved": "https://registry.npmjs.org/strip-json-comments/-/strip-json-comments-2.0.1.tgz", - "integrity": "sha1-PFMZQukIwml8DsNEhYwobHygpgo=", - "dev": true } } }, @@ -10741,26 +10635,34 @@ "yargs-parser": "20.2.9" }, "dependencies": { - "debug": { - "version": "4.3.2", - "resolved": "https://registry.npmjs.org/debug/-/debug-4.3.2.tgz", - "integrity": "sha512-mOp8wKcvj7XxC78zLgw/ZA+6TSgkoE2C/ienthhRD298T7UNwAg9diBpLRxC0mOezLl4B0xV7M0cCO6P/O0Xhw==", + "array-union": { + "version": "2.1.0", + "resolved": "https://registry.npmjs.org/array-union/-/array-union-2.1.0.tgz", + "integrity": "sha512-HGyxoOTYUyCM6stUe6EJgnd4EoewAI7zMdfqO+kGjnlZmBDz/cR5pf8r/cR4Wq60sL/p0IkcjUEEPwS3GFrIyw==", + "dev": true + }, + "globby": { + "version": "11.0.4", + "resolved": "https://registry.npmjs.org/globby/-/globby-11.0.4.tgz", + "integrity": "sha512-9O4MVG9ioZJ08ffbcyVYyLOJLk5JQ688pJ4eMGLpdWLHq/Wr1D9BlriLQyL0E+jbkuePVZXYFj47QM/v093wHg==", "dev": true, "requires": { - "ms": "2.1.2" + "array-union": "^2.1.0", + "dir-glob": "^3.0.1", + "fast-glob": "^3.1.1", + "ignore": "^5.1.4", + "merge2": "^1.3.0", + "slash": "^3.0.0" } }, - "uuid": { - "version": "8.3.2", - "resolved": "https://registry.npmjs.org/uuid/-/uuid-8.3.2.tgz", - "integrity": "sha512-+NYs2QeMWy+GWFOEm9xnn6HCDp0l7QBD7ml8zLUmJ+93Q5NF0NocErnwkTkXVFNiX3/fpC6afS8Dhb/gz7R7eg==", - "dev": true - }, - "yargs-parser": { - "version": "20.2.9", - "resolved": "https://registry.npmjs.org/yargs-parser/-/yargs-parser-20.2.9.tgz", - "integrity": "sha512-y11nGElTIV+CT3Zv9t7VKl+Q3hTQoT9a1Qzezhhl6Rp21gJ/IVTW7Z3y9EWXhuUBC2Shnf+DX0antecpAwSP8w==", - "dev": true + "semver": { + "version": "7.3.5", + "resolved": "https://registry.npmjs.org/semver/-/semver-7.3.5.tgz", + "integrity": "sha512-PoeGJYh8HK4BTO/a9Tf6ZG3veo/A7ZVsYrSA6J8ny9nb3B1VrpkuN+z9OE5wfE5p6H4LchYZsegiQgbJD94ZFQ==", + "dev": true, + "requires": { + "lru-cache": "^6.0.0" + } } } }, @@ -10863,18 +10765,18 @@ } }, "rxjs": { - "version": "7.4.0", - "resolved": "https://registry.npmjs.org/rxjs/-/rxjs-7.4.0.tgz", - "integrity": "sha512-7SQDi7xeTMCJpqViXh8gL/lebcwlp3d831F05+9B44A4B0WfsEwUQHR64gsH1kvJ+Ep/J9K2+n1hVl1CsGN23w==", + "version": "7.5.1", + "resolved": "https://registry.npmjs.org/rxjs/-/rxjs-7.5.1.tgz", + "integrity": "sha512-KExVEeZWxMZnZhUZtsJcFwz8IvPvgu4G2Z2QyqjZQzUGr32KDYuSxrEYO4w3tFFNbfLozcrKUTvTPi+E9ywJkQ==", "dev": true, "requires": { - "tslib": "~2.1.0" + "tslib": "^2.1.0" } }, "safe-buffer": { - "version": "5.1.2", - "resolved": "https://registry.npmjs.org/safe-buffer/-/safe-buffer-5.1.2.tgz", - "integrity": "sha512-Gd2UZBJDkXlY7GbJxfsE8/nvKkUEU1G38c1siN6QP6a9PT9MmHB8GnpscSmMJSoF8LOIrt8ud/wPtojys4G6+g==", + "version": "5.2.1", + "resolved": "https://registry.npmjs.org/safe-buffer/-/safe-buffer-5.2.1.tgz", + "integrity": "sha512-rp3So07KcdmmKbGvgaNxQSJr7bGVSVk5S9Eq1F+ppbRo70+YeaDxkw5Dd8NPN+GD6bjnYm2VuPuCXmpuYvmCXQ==", "dev": true }, "safer-buffer": { @@ -10884,13 +10786,10 @@ "dev": true }, "semver": { - "version": "7.3.5", - "resolved": "https://registry.npmjs.org/semver/-/semver-7.3.5.tgz", - "integrity": "sha512-PoeGJYh8HK4BTO/a9Tf6ZG3veo/A7ZVsYrSA6J8ny9nb3B1VrpkuN+z9OE5wfE5p6H4LchYZsegiQgbJD94ZFQ==", - "dev": true, - "requires": { - "lru-cache": "^6.0.0" - } + "version": "6.3.0", + "resolved": "https://registry.npmjs.org/semver/-/semver-6.3.0.tgz", + "integrity": "sha512-b39TBaTSfV6yBrapU89p5fKekE2m/NwnDocOVruQFS1/veMgdzuPcnOM34M6CwxW8jH/lxEa5rBoDeUwu5HHTw==", + "dev": true }, "semver-diff": { "version": "3.1.1", @@ -10899,14 +10798,6 @@ "dev": true, "requires": { "semver": "^6.3.0" - }, - "dependencies": { - "semver": { - "version": "6.3.0", - "resolved": "https://registry.npmjs.org/semver/-/semver-6.3.0.tgz", - "integrity": "sha512-b39TBaTSfV6yBrapU89p5fKekE2m/NwnDocOVruQFS1/veMgdzuPcnOM34M6CwxW8jH/lxEa5rBoDeUwu5HHTw==", - "dev": true - } } }, "serialize-javascript": { @@ -11072,14 +10963,6 @@ "dev": true, "requires": { "safe-buffer": "~5.2.0" - }, - "dependencies": { - "safe-buffer": { - "version": "5.2.1", - "resolved": "https://registry.npmjs.org/safe-buffer/-/safe-buffer-5.2.1.tgz", - "integrity": "sha512-rp3So07KcdmmKbGvgaNxQSJr7bGVSVk5S9Eq1F+ppbRo70+YeaDxkw5Dd8NPN+GD6bjnYm2VuPuCXmpuYvmCXQ==", - "dev": true - } } }, "string-width": { @@ -11115,9 +10998,9 @@ "dev": true }, "strip-json-comments": { - "version": "3.1.1", - "resolved": "https://registry.npmjs.org/strip-json-comments/-/strip-json-comments-3.1.1.tgz", - "integrity": "sha512-6fPc+R4ihwqP6N/aIv2f1gMH8lOVtWQHoqC4yK6oSDVVocumAsfCqjkXnqiYMhmMwS/mEHLp7Vehlt3ql6lEig==", + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/strip-json-comments/-/strip-json-comments-2.0.1.tgz", + "integrity": "sha1-PFMZQukIwml8DsNEhYwobHygpgo=", "dev": true }, "strip-outer": { @@ -11127,14 +11010,6 @@ "dev": true, "requires": { "escape-string-regexp": "^1.0.2" - }, - "dependencies": { - "escape-string-regexp": { - "version": "1.0.5", - "resolved": "https://registry.npmjs.org/escape-string-regexp/-/escape-string-regexp-1.0.5.tgz", - "integrity": "sha1-G2HAViGQqN/2rjuyzwIAyhMLhtQ=", - "dev": true - } } }, "supports-color": { @@ -11212,14 +11087,6 @@ "dev": true, "requires": { "escape-string-regexp": "^1.0.2" - }, - "dependencies": { - "escape-string-regexp": { - "version": "1.0.5", - "resolved": "https://registry.npmjs.org/escape-string-regexp/-/escape-string-regexp-1.0.5.tgz", - "integrity": "sha1-G2HAViGQqN/2rjuyzwIAyhMLhtQ=", - "dev": true - } } }, "ts-node": { @@ -11251,9 +11118,9 @@ } }, "tslib": { - "version": "2.1.0", - "resolved": "https://registry.npmjs.org/tslib/-/tslib-2.1.0.tgz", - "integrity": "sha512-hcVC3wYEziELGGmEEXue7D75zbwIIVUMWAVbHItGPx0ziyXxrOMQx4rQEVEV45Ut/1IotuEvwqPopzIOkDMf0A==", + "version": "2.3.1", + "resolved": "https://registry.npmjs.org/tslib/-/tslib-2.3.1.tgz", + "integrity": "sha512-77EbyPPpMz+FRFRuAFlWMtmgUWGe9UOG2Z25NqCwiIjRhOf5iKGuzSe5P2w1laq+FkRy4p+PCuVkJSGkzTEKVw==", "dev": true }, "tsutils": { @@ -11289,9 +11156,9 @@ "dev": true }, "type-fest": { - "version": "0.20.2", - "resolved": "https://registry.npmjs.org/type-fest/-/type-fest-0.20.2.tgz", - "integrity": "sha512-Ne+eE4r0/iWnpAxD852z3A+N0Bt5RN//NjJwRd2VFHEmrywxf5vsZlh4R6lixl6B+wz/8d+maTSAkN1FIkI3LQ==", + "version": "0.21.3", + "resolved": "https://registry.npmjs.org/type-fest/-/type-fest-0.21.3.tgz", + "integrity": "sha512-t0rzBq87m3fVcduHDUFhKmyyX+9eo6WQjZvf51Ea/M0Q7+T374Jp1aUiyUl0GKxp8M/OETVHSDvmkyPgvX+X2w==", "dev": true }, "typedarray-to-buffer": { @@ -11314,22 +11181,6 @@ "marked": "^3.0.8", "minimatch": "^3.0.4", "shiki": "^0.9.12" - }, - "dependencies": { - "glob": { - "version": "7.2.0", - "resolved": "https://registry.npmjs.org/glob/-/glob-7.2.0.tgz", - "integrity": "sha512-lmLf6gtyrPq8tTjSmrO94wBeQbFR3HbLHbuyD69wuyQkImp2hWqMGB47OX65FBkPffO641IP9jWa1z4ivqG26Q==", - "dev": true, - "requires": { - "fs.realpath": "^1.0.0", - "inflight": "^1.0.4", - "inherits": "2", - "minimatch": "^3.0.4", - "once": "^1.3.0", - "path-is-absolute": "^1.0.0" - } - } } }, "typescript": { @@ -11395,6 +11246,15 @@ "requires": { "ci-info": "^2.0.0" } + }, + "semver": { + "version": "7.3.5", + "resolved": "https://registry.npmjs.org/semver/-/semver-7.3.5.tgz", + "integrity": "sha512-PoeGJYh8HK4BTO/a9Tf6ZG3veo/A7ZVsYrSA6J8ny9nb3B1VrpkuN+z9OE5wfE5p6H4LchYZsegiQgbJD94ZFQ==", + "dev": true, + "requires": { + "lru-cache": "^6.0.0" + } } } }, @@ -11429,9 +11289,9 @@ "dev": true }, "uuid": { - "version": "3.4.0", - "resolved": "https://registry.npmjs.org/uuid/-/uuid-3.4.0.tgz", - "integrity": "sha512-HjSDRw6gZE5JMggctHBcjVak08+KEVhSIiDzFnT9S9aegmp85S/bReBVTb4QTFaRNptJ9kuYaNhnbNEOkbKb/A==", + "version": "8.3.2", + "resolved": "https://registry.npmjs.org/uuid/-/uuid-8.3.2.tgz", + "integrity": "sha512-+NYs2QeMWy+GWFOEm9xnn6HCDp0l7QBD7ml8zLUmJ+93Q5NF0NocErnwkTkXVFNiX3/fpC6afS8Dhb/gz7R7eg==", "dev": true }, "v8-compile-cache": { @@ -11609,9 +11469,9 @@ "dev": true }, "yargs": { - "version": "17.3.0", - "resolved": "https://registry.npmjs.org/yargs/-/yargs-17.3.0.tgz", - "integrity": "sha512-GQl1pWyDoGptFPJx9b9L6kmR33TGusZvXIZUT+BOz9f7X2L94oeAskFYLEg/FkhV06zZPBYLvLZRWeYId29lew==", + "version": "17.3.1", + "resolved": "https://registry.npmjs.org/yargs/-/yargs-17.3.1.tgz", + "integrity": "sha512-WUANQeVgjLbNsEmGk20f+nlHgOqzRFpiGWVaBrYGYIGANIIu3lWjoyi0fNlFmJkvfhCZ6BXINe7/W2O2bV4iaA==", "dev": true, "requires": { "cliui": "^7.0.2", @@ -11632,9 +11492,9 @@ } }, "yargs-parser": { - "version": "20.2.4", - "resolved": "https://registry.npmjs.org/yargs-parser/-/yargs-parser-20.2.4.tgz", - "integrity": "sha512-WOkpgNhPTlE73h4VFAFsOnomJVaovO8VqLDzy5saChRBFQFBoMYirowyW+Q9HB4HFF4Z7VZTiG3iSzJJA29yRA==", + "version": "20.2.9", + "resolved": "https://registry.npmjs.org/yargs-parser/-/yargs-parser-20.2.9.tgz", + "integrity": "sha512-y11nGElTIV+CT3Zv9t7VKl+Q3hTQoT9a1Qzezhhl6Rp21gJ/IVTW7Z3y9EWXhuUBC2Shnf+DX0antecpAwSP8w==", "dev": true }, "yargs-unparser": { @@ -11649,12 +11509,6 @@ "is-plain-obj": "^2.1.0" }, "dependencies": { - "camelcase": { - "version": "6.2.1", - "resolved": "https://registry.npmjs.org/camelcase/-/camelcase-6.2.1.tgz", - "integrity": "sha512-tVI4q5jjFV5CavAU8DXfza/TJcZutVKo/5Foskmsqcm0MsL91moHvwiGNnqaa2o6PF/7yT5ikDRcVcl8Rj6LCA==", - "dev": true - }, "decamelize": { "version": "4.0.0", "resolved": "https://registry.npmjs.org/decamelize/-/decamelize-4.0.0.tgz", diff --git a/packages/bloom/package.json b/packages/bloom/package.json index 10855d64e4d..1cef48990f1 100644 --- a/packages/bloom/package.json +++ b/packages/bloom/package.json @@ -18,7 +18,7 @@ "devDependencies": { "@istanbuljs/nyc-config-typescript": "^1.0.2", "@node-redis/test-utils": "*", - "@types/node": "^17.0.1", + "@types/node": "^17.0.5", "nyc": "^15.1.0", "release-it": "^14.11.8", "source-map-support": "^0.5.21", diff --git a/packages/client/package.json b/packages/client/package.json index 25afddbaaac..cf3a470c363 100644 --- a/packages/client/package.json +++ b/packages/client/package.json @@ -22,12 +22,12 @@ "devDependencies": { "@istanbuljs/nyc-config-typescript": "^1.0.2", "@node-redis/test-utils": "*", - "@types/node": "^17.0.1", + "@types/node": "^17.0.5", "@types/redis-parser": "^3.0.0", "@types/sinon": "^10.0.6", "@types/yallist": "^4.0.1", - "@typescript-eslint/eslint-plugin": "^5.8.0", - "@typescript-eslint/parser": "^5.8.0", + "@typescript-eslint/eslint-plugin": "^5.8.1", + "@typescript-eslint/parser": "^5.8.1", "eslint": "^8.5.0", "nyc": "^15.1.0", "release-it": "^14.11.8", diff --git a/packages/json/package.json b/packages/json/package.json index 445a95e9e30..769993af362 100644 --- a/packages/json/package.json +++ b/packages/json/package.json @@ -18,7 +18,7 @@ "devDependencies": { "@istanbuljs/nyc-config-typescript": "^1.0.2", "@node-redis/test-utils": "*", - "@types/node": "^17.0.1", + "@types/node": "^17.0.5", "nyc": "^15.1.0", "release-it": "^14.11.8", "source-map-support": "^0.5.21", diff --git a/packages/search/package.json b/packages/search/package.json index c15585746bb..e9268d28099 100644 --- a/packages/search/package.json +++ b/packages/search/package.json @@ -18,7 +18,7 @@ "devDependencies": { "@istanbuljs/nyc-config-typescript": "^1.0.2", "@node-redis/test-utils": "*", - "@types/node": "^17.0.1", + "@types/node": "^17.0.5", "nyc": "^15.1.0", "release-it": "^14.11.8", "source-map-support": "^0.5.21", diff --git a/packages/test-utils/package.json b/packages/test-utils/package.json index 233d7f279ad..8cecb362d37 100644 --- a/packages/test-utils/package.json +++ b/packages/test-utils/package.json @@ -12,14 +12,14 @@ "devDependencies": { "@istanbuljs/nyc-config-typescript": "^1.0.2", "@types/mocha": "^9.0.0", - "@types/node": "^17.0.1", - "@types/yargs": "^17.0.7", + "@types/node": "^17.0.5", + "@types/yargs": "^17.0.8", "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.4", - "yargs": "^17.3.0" + "yargs": "^17.3.1" } } diff --git a/packages/time-series/package.json b/packages/time-series/package.json index 04e18b75f90..63fd7aec8d9 100644 --- a/packages/time-series/package.json +++ b/packages/time-series/package.json @@ -18,7 +18,7 @@ "devDependencies": { "@istanbuljs/nyc-config-typescript": "^1.0.2", "@node-redis/test-utils": "*", - "@types/node": "^17.0.1", + "@types/node": "^17.0.5", "nyc": "^15.1.0", "release-it": "^14.11.8", "source-map-support": "^0.5.21", From 8f88eb289b6658341c0aae4c8d75a50df7954965 Mon Sep 17 00:00:00 2001 From: leibale Date: Thu, 30 Dec 2021 17:12:13 -0500 Subject: [PATCH 1076/1748] fix ASK and MOVED errors in multi as well --- packages/client/lib/cluster/index.ts | 116 +++++++++++++-------------- 1 file changed, 55 insertions(+), 61 deletions(-) diff --git a/packages/client/lib/cluster/index.ts b/packages/client/lib/cluster/index.ts index 157b607b081..10288158e4d 100644 --- a/packages/client/lib/cluster/index.ts +++ b/packages/client/lib/cluster/index.ts @@ -1,5 +1,5 @@ import COMMANDS from './commands'; -import { RedisCommand, RedisCommandArgument, RedisCommandArguments, RedisCommandReply, RedisModules, RedisPlugins, RedisScript, RedisScripts } from '../commands'; +import { RedisCommand, RedisCommandArgument, RedisCommandArguments, RedisCommandRawReply, RedisCommandReply, RedisModules, RedisPlugins, RedisScript, RedisScripts } from '../commands'; import { ClientCommandOptions, RedisClientCommandSignature, RedisClientOptions, RedisClientType, WithModules, WithScripts } from '../client'; import RedisClusterSlots, { ClusterNode } from './cluster-slots'; import { extendWithModulesAndScripts, transformCommandArguments, transformCommandReply, extendWithCommands } from '../commander'; @@ -82,27 +82,17 @@ export default class RedisCluster( + async sendCommand( firstKey: RedisCommandArgument | undefined, isReadonly: boolean | undefined, args: RedisCommandArguments, - options?: ClientCommandOptions, - redirections = 0 - ): Promise> { - const client = this.#slots.getClient(firstKey, isReadonly); - - try { - return await client.sendCommand(args, options); - } catch (err: any) { - const shouldRetry = await this.#handleCommandError(err, client, redirections); - if (shouldRetry === true) { - return this.sendCommand(firstKey, isReadonly, args, options, redirections + 1); - } else if (shouldRetry) { - return shouldRetry.sendCommand(args, options); - } - - throw err; - } + options?: ClientCommandOptions + ): Promise { + return this.#execute( + firstKey, + isReadonly, + client => client.sendCommand(args, options) + ); } async scriptsExecutor(script: RedisScript, args: Array): Promise> { @@ -124,61 +114,65 @@ export default class RedisCluster, redisArgs: RedisCommandArguments, - options?: ClientCommandOptions, - redirections = 0 + options?: ClientCommandOptions ): Promise> { - const client = this.#slots.getClient( + return this.#execute( RedisCluster.extractFirstKey(script, originalArgs, redisArgs), - script.IS_READ_ONLY + script.IS_READ_ONLY, + client => client.executeScript(script, redisArgs, options) ); - - try { - return await client.executeScript(script, redisArgs, options); - } catch (err: any) { - const shouldRetry = await this.#handleCommandError(err, client, redirections); - if (shouldRetry === true) { - return this.executeScript(script, originalArgs, redisArgs, options, redirections + 1); - } else if (shouldRetry) { - return shouldRetry.executeScript(script, redisArgs, options); - } - - throw err; - } } - async #handleCommandError(err: Error, client: RedisClientType, redirections: number): Promise> { - if (redirections > (this.#options.maxCommandRedirections ?? 16)) { - throw err; - } - - if (err.message.startsWith('ASK')) { - const url = err.message.substring(err.message.lastIndexOf(' ') + 1); - let node = this.#slots.getNodeByUrl(url); - if (!node) { - await this.#slots.rediscover(client); - node = this.#slots.getNodeByUrl(url); + async #execute( + firstKey: RedisCommandArgument | undefined, + isReadonly: boolean | undefined, + executor: (client: RedisClientType) => Promise + ): Promise { + const maxCommandRedirections = this.#options.maxCommandRedirections ?? 16; + let client = this.#slots.getClient(firstKey, isReadonly); + for (let i = 0;; i++) { + try { + return await executor(client); + } catch (err) { + if (++i > maxCommandRedirections || !(err instanceof Error)) { + throw err; + } - if (!node) { - throw new Error(`Cannot find node ${url}`); + if (err.message.startsWith('ASK')) { + const url = err.message.substring(err.message.lastIndexOf(' ') + 1); + if (this.#slots.getNodeByUrl(url)?.client === client) { + await client.asking(); + continue; + } + + await this.#slots.rediscover(client); + const redirectTo = this.#slots.getNodeByUrl(url); + if (!redirectTo) { + throw new Error(`Cannot find node ${url}`); + } + + await redirectTo.client.asking(); + client = redirectTo.client; + continue; + } else if (err.message.startsWith('MOVED')) { + await this.#slots.rediscover(client); + client = this.#slots.getClient(firstKey, isReadonly); + continue; } - } - await node.client.asking(); - return node.client; - } else if (err.message.startsWith('MOVED')) { - await this.#slots.rediscover(client); - return true; + throw err; + } } - - throw err; } multi(routing?: RedisCommandArgument): RedisClusterMultiCommandType { return new this.#Multi( - async (commands: Array, firstKey?: RedisCommandArgument, chainId?: symbol) => { - return this.#slots - .getClient(firstKey) - .multiExecutor(commands, chainId); + (commands: Array, firstKey?: RedisCommandArgument, chainId?: symbol) => { + return this.#execute( + firstKey, + false, + client => client.multiExecutor(commands, chainId) + ); }, routing ); From 2203be5fbd4883f69b9a52f7d7c44b444f361201 Mon Sep 17 00:00:00 2001 From: leibale Date: Thu, 30 Dec 2021 18:32:48 -0500 Subject: [PATCH 1077/1748] add test for RootNodesUnavailableError --- packages/client/lib/cluster/cluster-slots.ts | 3 +- packages/client/lib/cluster/index.spec.ts | 40 +++++++++++++------- packages/client/lib/errors.ts | 6 +++ 3 files changed, 34 insertions(+), 15 deletions(-) diff --git a/packages/client/lib/cluster/cluster-slots.ts b/packages/client/lib/cluster/cluster-slots.ts index 37a17b73aea..bcf95390ce3 100644 --- a/packages/client/lib/cluster/cluster-slots.ts +++ b/packages/client/lib/cluster/cluster-slots.ts @@ -2,6 +2,7 @@ import RedisClient, { InstantiableRedisClient, RedisClientType } from '../client import { RedisClusterMasterNode, RedisClusterReplicaNode } from '../commands/CLUSTER_NODES'; import { RedisClusterClientOptions, RedisClusterOptions } from '.'; import { RedisCommandArgument, RedisModules, RedisScripts } from '../commands'; +import { RootNodesUnavailableError } from '../errors'; // We need to use 'require', because it's not possible with Typescript to import // function that are exported as 'module.exports = function`, without esModuleInterop @@ -39,7 +40,7 @@ export default class RedisClusterSlots { diff --git a/packages/client/lib/cluster/index.spec.ts b/packages/client/lib/cluster/index.spec.ts index 66d458522e4..a2981d824e3 100644 --- a/packages/client/lib/cluster/index.spec.ts +++ b/packages/client/lib/cluster/index.spec.ts @@ -1,7 +1,9 @@ import { strict as assert } from 'assert'; import testUtils, { GLOBAL } from '../test-utils'; +import RedisCluster from '.'; import { ClusterSlotStates } from '../commands/CLUSTER_SETSLOT'; import { SQUARE_SCRIPT } from '../client/index.spec'; +import { RootNodesUnavailableError } from '../errors'; // We need to use 'require', because it's not possible with Typescript to import // function that are exported as 'module.exports = function`, without esModuleInterop @@ -10,20 +12,14 @@ const calculateSlot = require('cluster-key-slot'); describe('Cluster', () => { testUtils.testWithCluster('sendCommand', async cluster => { - await cluster.connect(); - - try { - await cluster.publish('channel', 'message'); - await cluster.set('a', 'b'); - await cluster.set('a{a}', 'bb'); - await cluster.set('aa', 'bb'); - await cluster.get('aa'); - await cluster.get('aa'); - await cluster.get('aa'); - await cluster.get('aa'); - } finally { - await cluster.disconnect(); - } + await cluster.publish('channel', 'message'); + await cluster.set('a', 'b'); + await cluster.set('a{a}', 'bb'); + await cluster.set('aa', 'bb'); + await cluster.get('aa'); + await cluster.get('aa'); + await cluster.get('aa'); + await cluster.get('aa'); }, GLOBAL.CLUSTERS.OPEN); testUtils.testWithCluster('multi', async cluster => { @@ -51,6 +47,22 @@ describe('Cluster', () => { } }); + it('should throw RootNodesUnavailableError', async () => { + const cluster = RedisCluster.create({ + rootNodes: [] + }); + + try { + await assert.rejects( + cluster.connect(), + RootNodesUnavailableError + ); + } catch (err) { + await cluster.disconnect(); + throw err; + } + }); + testUtils.testWithCluster('should handle live resharding', async cluster => { const key = 'key', value = 'value'; diff --git a/packages/client/lib/errors.ts b/packages/client/lib/errors.ts index 6af7d70744c..464e81c2bb1 100644 --- a/packages/client/lib/errors.ts +++ b/packages/client/lib/errors.ts @@ -39,3 +39,9 @@ export class AuthError extends Error { super(message); } } + +export class RootNodesUnavailableError extends Error { + constructor() { + super('All the root nodes are unavailable'); + } +} From 7e2059b1cf53d17d0443994afdae1dd82333fd87 Mon Sep 17 00:00:00 2001 From: leibale Date: Mon, 3 Jan 2022 16:24:59 -0500 Subject: [PATCH 1078/1748] ref #1789 - reject "hanging" promises when closing connection --- packages/client/lib/client/index.ts | 6 +++++- packages/client/lib/client/socket.ts | 20 +++++++++++++------- packages/client/lib/errors.ts | 11 +++++++++++ 3 files changed, 29 insertions(+), 8 deletions(-) diff --git a/packages/client/lib/client/index.ts b/packages/client/lib/client/index.ts index fe3ed36b75c..c9f04dc0370 100644 --- a/packages/client/lib/client/index.ts +++ b/packages/client/lib/client/index.ts @@ -268,7 +268,11 @@ export default class RedisClient .on('data', data => this.#queue.parseResponse(data)) .on('error', err => { this.emit('error', err); - this.#queue.flushWaitingForReply(err); + if (!this.#socket.isOpen) { + this.#queue.flushAll(err); + } else { + this.#queue.flushWaitingForReply(err); + } }) .on('connect', () => this.emit('connect')) .on('ready', () => { diff --git a/packages/client/lib/client/socket.ts b/packages/client/lib/client/socket.ts index 57ecf609d9f..ccbe3f7f2c6 100644 --- a/packages/client/lib/client/socket.ts +++ b/packages/client/lib/client/socket.ts @@ -3,7 +3,7 @@ import * as net from 'net'; import * as tls from 'tls'; import { encodeCommand } from '../commander'; import { RedisCommandArguments } from '../commands'; -import { ConnectionTimeoutError, ClientClosedError, SocketClosedUnexpectedlyError, AuthError } from '../errors'; +import { ConnectionTimeoutError, ClientClosedError, SocketClosedUnexpectedlyError, AuthError, ReconnectStrategyError } from '../errors'; import { promiseTimeout } from '../utils'; export interface RedisSocketCommonOptions { @@ -93,9 +93,16 @@ export default class RedisSocket extends EventEmitter { } async #connect(hadError?: boolean): Promise { - this.#isOpen = true; - this.#socket = await this.#retryConnection(0, hadError); - this.#writableNeedDrain = false; + try { + this.#isOpen = true; + this.#socket = await this.#retryConnection(0, hadError); + this.#writableNeedDrain = false; + } catch (err) { + this.#isOpen = false; + this.emit('error', err); + this.emit('end'); + throw err; + } if (!this.#isOpen) { this.disconnect(); @@ -134,17 +141,16 @@ export default class RedisSocket extends EventEmitter { try { return await this.#createSocket(); } catch (err) { - this.emit('error', err); - if (!this.#isOpen) { throw err; } const retryIn = (this.#options?.reconnectStrategy ?? RedisSocket.#defaultReconnectStrategy)(retries); if (retryIn instanceof Error) { - throw retryIn; + throw new ReconnectStrategyError(retryIn, err); } + this.emit('error', err); await promiseTimeout(retryIn); return this.#retryConnection(retries + 1); } diff --git a/packages/client/lib/errors.ts b/packages/client/lib/errors.ts index 464e81c2bb1..e43dbc81422 100644 --- a/packages/client/lib/errors.ts +++ b/packages/client/lib/errors.ts @@ -45,3 +45,14 @@ export class RootNodesUnavailableError extends Error { super('All the root nodes are unavailable'); } } + +export class ReconnectStrategyError extends Error { + originalError: Error; + socketError: unknown; + + constructor(originalError: Error, socketError: unknown) { + super(originalError.message); + this.originalError = originalError; + this.socketError = socketError; + } +} From 24f3e3f43c9f89238d21a8d63e16ca365545ad4c Mon Sep 17 00:00:00 2001 From: leibale Date: Mon, 3 Jan 2022 16:37:03 -0500 Subject: [PATCH 1079/1748] clean code --- packages/client/lib/client/index.ts | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/packages/client/lib/client/index.ts b/packages/client/lib/client/index.ts index c9f04dc0370..cae78e7f49f 100644 --- a/packages/client/lib/client/index.ts +++ b/packages/client/lib/client/index.ts @@ -268,10 +268,10 @@ export default class RedisClient .on('data', data => this.#queue.parseResponse(data)) .on('error', err => { this.emit('error', err); - if (!this.#socket.isOpen) { - this.#queue.flushAll(err); - } else { + if (this.#socket.isOpen) { this.#queue.flushWaitingForReply(err); + } else { + this.#queue.flushAll(err); } }) .on('connect', () => this.emit('connect')) From 8062c2bc77b1c59bb328309a90ef0b3643eae4cd Mon Sep 17 00:00:00 2001 From: leibale Date: Mon, 3 Jan 2022 23:25:15 -0500 Subject: [PATCH 1080/1748] fix #1783 - fix some commands in legacy mode --- packages/client/lib/client/index.ts | 8 ++++++-- packages/client/lib/client/multi-command.ts | 8 ++++++-- 2 files changed, 12 insertions(+), 4 deletions(-) diff --git a/packages/client/lib/client/index.ts b/packages/client/lib/client/index.ts index cae78e7f49f..6144c980701 100644 --- a/packages/client/lib/client/index.ts +++ b/packages/client/lib/client/index.ts @@ -320,6 +320,10 @@ export default class RedisClient this.#defineLegacyCommand(name); } + for (const name of Object.keys(COMMANDS)) { + (this as any)[name.toLowerCase()] = (this as any)[name]; + } + // hard coded commands this.#defineLegacyCommand('SELECT'); this.#defineLegacyCommand('select'); @@ -336,8 +340,8 @@ export default class RedisClient } #defineLegacyCommand(name: string): void { - (this as any).#v4[name] = (this as any)[name].bind(this); - (this as any)[name] = (this as any)[name.toLowerCase()] = + this.#v4[name] = (this as any)[name].bind(this); + (this as any)[name] = (...args: Array): void => (this as any).sendCommand(name, ...args); } diff --git a/packages/client/lib/client/multi-command.ts b/packages/client/lib/client/multi-command.ts index 5d69b933152..601334fe6d8 100644 --- a/packages/client/lib/client/multi-command.ts +++ b/packages/client/lib/client/multi-command.ts @@ -78,11 +78,15 @@ export default class RedisClientMultiCommand { for (const name of Object.keys(COMMANDS)) { this.#defineLegacyCommand(name); } + + for (const name of Object.keys(COMMANDS)) { + (this as any)[name.toLowerCase()] = (this as any)[name]; + } } #defineLegacyCommand(name: string): void { - (this as any).v4[name] = (this as any)[name].bind(this.v4); - (this as any)[name] = (this as any)[name.toLowerCase()] = + this.v4[name] = (this as any)[name].bind(this.v4); + (this as any)[name] = (...args: Array): void => (this as any).addCommand(name, args); } From d34cb9c07b29ee645d0b8a3ddfb53b88d7ee7e1d Mon Sep 17 00:00:00 2001 From: Leibale Eidelman Date: Tue, 11 Jan 2022 11:45:50 -0500 Subject: [PATCH 1081/1748] fix #1819 - fix EXISTS reply (#1820) * fix #1819 - fix EXISTS reply * fix for f3f946fd9a2edb4a112a7192210db77ca4213886 - "fix" tests --- packages/client/lib/commands/EXISTS.spec.ts | 2 +- packages/client/lib/commands/EXISTS.ts | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/packages/client/lib/commands/EXISTS.spec.ts b/packages/client/lib/commands/EXISTS.spec.ts index 241a97c362e..be1a808225e 100644 --- a/packages/client/lib/commands/EXISTS.spec.ts +++ b/packages/client/lib/commands/EXISTS.spec.ts @@ -22,7 +22,7 @@ describe('EXISTS', () => { testUtils.testWithClient('client.exists', async client => { assert.equal( await client.exists('key'), - false + 0 ); }, GLOBAL.SERVERS.OPEN); }); diff --git a/packages/client/lib/commands/EXISTS.ts b/packages/client/lib/commands/EXISTS.ts index 3b4665fc7f2..3bbc72ada46 100644 --- a/packages/client/lib/commands/EXISTS.ts +++ b/packages/client/lib/commands/EXISTS.ts @@ -11,4 +11,4 @@ export function transformArguments( return pushVerdictArguments(['EXISTS'], keys); } -export { transformBooleanReply as transformReply } from './generic-transformers'; +export declare function transformReply(): number; From 4859aa722df3de5b5bb8180f3cfd9cae8e8105c6 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Nathan=20N=C4=9Bmec?= Date: Wed, 12 Jan 2022 14:16:47 +0100 Subject: [PATCH 1082/1748] SEPARATOR typo fix (#1823) * SEPARATOR typo fix * SEPARATOR typo fix --- packages/search/lib/commands/CREATE.spec.ts | 6 +++--- packages/search/lib/commands/index.ts | 6 +++--- 2 files changed, 6 insertions(+), 6 deletions(-) diff --git a/packages/search/lib/commands/CREATE.spec.ts b/packages/search/lib/commands/CREATE.spec.ts index 115487b7e88..f760f75e116 100644 --- a/packages/search/lib/commands/CREATE.spec.ts +++ b/packages/search/lib/commands/CREATE.spec.ts @@ -101,15 +101,15 @@ describe('CREATE', () => { }); }); - it('with SEPERATOR', () => { + it('with SEPARATOR', () => { assert.deepEqual( transformArguments('index', { field: { type: SchemaFieldTypes.TAG, - SEPERATOR: 'seperator' + SEPERATOR: 'separator' } }), - ['FT.CREATE', 'index', 'SCHEMA', 'field', 'TAG', 'SEPERATOR', 'seperator'] + ['FT.CREATE', 'index', 'SCHEMA', 'field', 'TAG', 'SEPERATOR', 'separator'] ); }); diff --git a/packages/search/lib/commands/index.ts b/packages/search/lib/commands/index.ts index 305b0f81fc4..4c4530f2c94 100644 --- a/packages/search/lib/commands/index.ts +++ b/packages/search/lib/commands/index.ts @@ -200,7 +200,7 @@ type CreateSchemaNumericField = CreateSchemaField; type CreateSchemaGeoField = CreateSchemaField; type CreateSchemaTagField = CreateSchemaField; @@ -248,8 +248,8 @@ export function pushSchema(args: RedisCommandArguments, schema: CreateSchema) { // break; case 'TAG': - if (fieldOptions.SEPERATOR) { - args.push('SEPERATOR', fieldOptions.SEPERATOR); + if (fieldOptions.SEPARATOR) { + args.push('SEPARATOR', fieldOptions.SEPERATOR); } if (fieldOptions.CASESENSITIVE) { From cf6d6530372f48eae64efd3bfc470044991ba298 Mon Sep 17 00:00:00 2001 From: Leibale Eidelman Date: Wed, 12 Jan 2022 08:41:01 -0500 Subject: [PATCH 1083/1748] fix RediSearch SEPARATOR typo (#1824) --- packages/search/lib/commands/CREATE.spec.ts | 6 ++---- packages/search/lib/commands/index.ts | 2 +- 2 files changed, 3 insertions(+), 5 deletions(-) diff --git a/packages/search/lib/commands/CREATE.spec.ts b/packages/search/lib/commands/CREATE.spec.ts index f760f75e116..7d21e82d66a 100644 --- a/packages/search/lib/commands/CREATE.spec.ts +++ b/packages/search/lib/commands/CREATE.spec.ts @@ -106,15 +106,13 @@ describe('CREATE', () => { transformArguments('index', { field: { type: SchemaFieldTypes.TAG, - SEPERATOR: 'separator' + SEPARATOR: 'separator' } }), - ['FT.CREATE', 'index', 'SCHEMA', 'field', 'TAG', 'SEPERATOR', 'separator'] + ['FT.CREATE', 'index', 'SCHEMA', 'field', 'TAG', 'SEPARATOR', 'separator'] ); }); - - it('with CASESENSITIVE', () => { assert.deepEqual( transformArguments('index', { diff --git a/packages/search/lib/commands/index.ts b/packages/search/lib/commands/index.ts index 4c4530f2c94..66b891fd8c4 100644 --- a/packages/search/lib/commands/index.ts +++ b/packages/search/lib/commands/index.ts @@ -249,7 +249,7 @@ export function pushSchema(args: RedisCommandArguments, schema: CreateSchema) { case 'TAG': if (fieldOptions.SEPARATOR) { - args.push('SEPARATOR', fieldOptions.SEPERATOR); + args.push('SEPARATOR', fieldOptions.SEPARATOR); } if (fieldOptions.CASESENSITIVE) { From e3c314ee7a0802faf7ea19c6974c9d778fbf5242 Mon Sep 17 00:00:00 2001 From: Leibale Eidelman Date: Wed, 12 Jan 2022 09:40:21 -0500 Subject: [PATCH 1084/1748] update README.md (#1826) --- README.md | 2 ++ 1 file changed, 2 insertions(+) diff --git a/README.md b/README.md index 46ee50b90fa..b4de01dd1cd 100644 --- a/README.md +++ b/README.md @@ -319,6 +319,8 @@ Node Redis is supported with the following versions of Redis: > Node Redis should work with older versions of Redis, but it is not fully tested and we cannot offer support. +## Packages + | Name | Description | |---------------------------------------------------|----------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------| | [redis](./) | [![Downloads](https://img.shields.io/npm/dm/redis.svg)](https://www.npmjs.com/package/redis) [![Version](https://img.shields.io/npm/v/redis.svg)](https://www.npmjs.com/package/redis) | From 9516b88f61ddf3b1f940890f3bc0049c58f5b15c Mon Sep 17 00:00:00 2001 From: AnnAngela Date: Thu, 13 Jan 2022 19:51:41 +0800 Subject: [PATCH 1085/1748] Add the list of removed events to migration guide (#1761) * Update v3-to-v4.md * Update README.md * Correct the wrong description * Update docs/v3-to-v4.md Co-authored-by: Simon Prickett * Update docs/v3-to-v4.md Co-authored-by: Simon Prickett Co-authored-by: Simon Prickett --- README.md | 18 +++++++++--------- docs/v3-to-v4.md | 22 +++++++++++++++++++--- 2 files changed, 28 insertions(+), 12 deletions(-) diff --git a/README.md b/README.md index b4de01dd1cd..d22013328c0 100644 --- a/README.md +++ b/README.md @@ -296,15 +296,15 @@ Check out the [Clustering Guide](./docs/clustering.md) when using Node Redis to The Node Redis client class is an Nodejs EventEmitter and it emits an event each time the network status changes: -| Event name | Scenes | Parameters | -|--------------|-------------------------------------------------------------------------------------------------------------------|------------------------------------------------------------------------------------------------------------------------------------| -| connect | The client is initiating a connection to the server. | _undefined_ | -| ready | The client successfully initiated the connection to the server. | _undefined_ | -| end | The client disconnected the connection to the server via `.quit()` or `.disconnect()`. | _undefined_ | -| error | When a network error has occurred, such as unable to connect to the server or the connection closed unexpectedly. | The error object, such as `SocketClosedUnexpectedlyError: Socket closed unexpectedly` or `Error: connect ECONNREFUSED [IP]:[PORT]` | -| reconnecting | The client is trying to reconnect to the server. | _undefined_ | - -The client will not emit any other events beyond those listed above. +| Event name | Scenes | Arguments to be passed to the listener | +|----------------|-------------------------------------------------------------------------------------------------------------------|------------------------------------------------------------------------------------------------------------------------------------------------| +| `connect` | The client is initiating a connection to the server. | _No argument_ | +| `ready` | The client successfully initiated the connection to the server. | _No argument_ | +| `end` | The client disconnected the connection to the server via `.quit()` or `.disconnect()`. | _No argument_ | +| `error` | When a network error has occurred, such as unable to connect to the server or the connection closed unexpectedly. | 1 argument: The error object, such as `SocketClosedUnexpectedlyError: Socket closed unexpectedly` or `Error: connect ECONNREFUSED [IP]:[PORT]` | +| `reconnecting` | The client is trying to reconnect to the server. | _No argument_ | + +The client will not emit [any other events](./docs/v3-to-v4.md#all-the-removed-events) beyond those listed above. ## Supported Redis versions diff --git a/docs/v3-to-v4.md b/docs/v3-to-v4.md index 3ef43444c8e..834b02141c6 100644 --- a/docs/v3-to-v4.md +++ b/docs/v3-to-v4.md @@ -28,15 +28,31 @@ await client.connect(); await client.ping(); ``` -### No `message` event +### All the removed events -In V4, you don't need to add listener to the `message` and `message_buffer` events, you can get the message directly in `subscribe`-like commands. +The following events that existed in V3 were removed in V4: + +1. `warning` +2. `subscribe` +3. `psubscribe` +4. `unsubscribe` +5. `message` +6. `message_buffer` +7. `messageBuffer` +8. `pmessage` +9. `pmessage_buffer` +10. `pmessageBuffer` +11. `monitor` + +#### No `message`-like event + +In V4, you don't need to add a listener to the `message`-like events (items 5 to 10 of the above list), you can get the message directly in `subscribe`-like commands. The second argument of these commands is a callback, which will be triggered every time there is a message published to the channel. The third argument to these commands is a boolean to set `bufferMode` (default `false`). If it's set to `true` you will receive a buffer instead of a string. -The `subscribe`-like commands return a promise. If the server returns `ok` the promise will be fulfilled, otherwise the promise will be rejected. +The `subscribe`-like commands return a promise. If the command is executed successfully the promise will be fulfilled, otherwise the promise will be rejected. ```typescript import { createClient } from 'redis'; From 075ae3a9c0fd9ded22de8e28e1bad4a25a85fed8 Mon Sep 17 00:00:00 2001 From: leibale Date: Thu, 13 Jan 2022 07:10:17 -0500 Subject: [PATCH 1086/1748] upgrade dependencies --- package-lock.json | 1029 +++++++++++++++-------------- package.json | 2 +- packages/bloom/package.json | 4 +- packages/client/package.json | 10 +- packages/json/package.json | 4 +- packages/search/package.json | 2 +- packages/test-utils/package.json | 3 +- packages/time-series/package.json | 2 +- 8 files changed, 559 insertions(+), 497 deletions(-) diff --git a/package-lock.json b/package-lock.json index f8d029f64b1..a9a148702b9 100644 --- a/package-lock.json +++ b/package-lock.json @@ -21,7 +21,7 @@ "devDependencies": { "@tsconfig/node12": "^1.0.9", "gh-pages": "^3.2.3", - "release-it": "^14.11.8", + "release-it": "^14.12.1", "typescript": "^4.5.4" }, "engines": { @@ -30,41 +30,41 @@ } }, "node_modules/@babel/code-frame": { - "version": "7.16.0", - "resolved": "https://registry.npmjs.org/@babel/code-frame/-/code-frame-7.16.0.tgz", - "integrity": "sha512-IF4EOMEV+bfYwOmNxGzSnjR2EmQod7f1UXOpZM3l4i4o4QNwzjtJAu/HxdjHq0aYBvdqMuQEY1eg0nqW9ZPORA==", + "version": "7.16.7", + "resolved": "https://registry.npmjs.org/@babel/code-frame/-/code-frame-7.16.7.tgz", + "integrity": "sha512-iAXqUn8IIeBTNd72xsFlgaXHkMBMt6y4HJp1tIaK465CWLT/fG1aqB7ykr95gHHmlBdGbFeWWfyB4NJJ0nmeIg==", "dev": true, "dependencies": { - "@babel/highlight": "^7.16.0" + "@babel/highlight": "^7.16.7" }, "engines": { "node": ">=6.9.0" } }, "node_modules/@babel/compat-data": { - "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==", + "version": "7.16.8", + "resolved": "https://registry.npmjs.org/@babel/compat-data/-/compat-data-7.16.8.tgz", + "integrity": "sha512-m7OkX0IdKLKPpBlJtF561YJal5y/jyI5fNfWbPxh2D/nbzzGI4qRyrD8xO2jB24u7l+5I2a43scCG2IrfjC50Q==", "dev": true, "engines": { "node": ">=6.9.0" } }, "node_modules/@babel/core": { - "version": "7.16.5", - "resolved": "https://registry.npmjs.org/@babel/core/-/core-7.16.5.tgz", - "integrity": "sha512-wUcenlLzuWMZ9Zt8S0KmFwGlH6QKRh3vsm/dhDA3CHkiTA45YuG1XkHRcNRl73EFPXDp/d5kVOU0/y7x2w6OaQ==", - "dev": true, - "dependencies": { - "@babel/code-frame": "^7.16.0", - "@babel/generator": "^7.16.5", - "@babel/helper-compilation-targets": "^7.16.3", - "@babel/helper-module-transforms": "^7.16.5", - "@babel/helpers": "^7.16.5", - "@babel/parser": "^7.16.5", - "@babel/template": "^7.16.0", - "@babel/traverse": "^7.16.5", - "@babel/types": "^7.16.0", + "version": "7.16.7", + "resolved": "https://registry.npmjs.org/@babel/core/-/core-7.16.7.tgz", + "integrity": "sha512-aeLaqcqThRNZYmbMqtulsetOQZ/5gbR/dWruUCJcpas4Qoyy+QeagfDsPdMrqwsPRDNxJvBlRiZxxX7THO7qtA==", + "dev": true, + "dependencies": { + "@babel/code-frame": "^7.16.7", + "@babel/generator": "^7.16.7", + "@babel/helper-compilation-targets": "^7.16.7", + "@babel/helper-module-transforms": "^7.16.7", + "@babel/helpers": "^7.16.7", + "@babel/parser": "^7.16.7", + "@babel/template": "^7.16.7", + "@babel/traverse": "^7.16.7", + "@babel/types": "^7.16.7", "convert-source-map": "^1.7.0", "debug": "^4.1.0", "gensync": "^1.0.0-beta.2", @@ -81,12 +81,12 @@ } }, "node_modules/@babel/generator": { - "version": "7.16.5", - "resolved": "https://registry.npmjs.org/@babel/generator/-/generator-7.16.5.tgz", - "integrity": "sha512-kIvCdjZqcdKqoDbVVdt5R99icaRtrtYhYK/xux5qiWCBmfdvEYMFZ68QCrpE5cbFM1JsuArUNs1ZkuKtTtUcZA==", + "version": "7.16.8", + "resolved": "https://registry.npmjs.org/@babel/generator/-/generator-7.16.8.tgz", + "integrity": "sha512-1ojZwE9+lOXzcWdWmO6TbUzDfqLD39CmEhN8+2cX9XkDo5yW1OpgfejfliysR2AWLpMamTiOiAp/mtroaymhpw==", "dev": true, "dependencies": { - "@babel/types": "^7.16.0", + "@babel/types": "^7.16.8", "jsesc": "^2.5.1", "source-map": "^0.5.0" }, @@ -95,13 +95,13 @@ } }, "node_modules/@babel/helper-compilation-targets": { - "version": "7.16.3", - "resolved": "https://registry.npmjs.org/@babel/helper-compilation-targets/-/helper-compilation-targets-7.16.3.tgz", - "integrity": "sha512-vKsoSQAyBmxS35JUOOt+07cLc6Nk/2ljLIHwmq2/NM6hdioUaqEXq/S+nXvbvXbZkNDlWOymPanJGOc4CBjSJA==", + "version": "7.16.7", + "resolved": "https://registry.npmjs.org/@babel/helper-compilation-targets/-/helper-compilation-targets-7.16.7.tgz", + "integrity": "sha512-mGojBwIWcwGD6rfqgRXVlVYmPAv7eOpIemUG3dGnDdCY4Pae70ROij3XmfrH6Fa1h1aiDylpglbZyktfzyo/hA==", "dev": true, "dependencies": { - "@babel/compat-data": "^7.16.0", - "@babel/helper-validator-option": "^7.14.5", + "@babel/compat-data": "^7.16.4", + "@babel/helper-validator-option": "^7.16.7", "browserslist": "^4.17.5", "semver": "^6.3.0" }, @@ -113,149 +113,149 @@ } }, "node_modules/@babel/helper-environment-visitor": { - "version": "7.16.5", - "resolved": "https://registry.npmjs.org/@babel/helper-environment-visitor/-/helper-environment-visitor-7.16.5.tgz", - "integrity": "sha512-ODQyc5AnxmZWm/R2W7fzhamOk1ey8gSguo5SGvF0zcB3uUzRpTRmM/jmLSm9bDMyPlvbyJ+PwPEK0BWIoZ9wjg==", + "version": "7.16.7", + "resolved": "https://registry.npmjs.org/@babel/helper-environment-visitor/-/helper-environment-visitor-7.16.7.tgz", + "integrity": "sha512-SLLb0AAn6PkUeAfKJCCOl9e1R53pQlGAfc4y4XuMRZfqeMYLE0dM1LMhqbGAlGQY0lfw5/ohoYWAe9V1yibRag==", "dev": true, "dependencies": { - "@babel/types": "^7.16.0" + "@babel/types": "^7.16.7" }, "engines": { "node": ">=6.9.0" } }, "node_modules/@babel/helper-function-name": { - "version": "7.16.0", - "resolved": "https://registry.npmjs.org/@babel/helper-function-name/-/helper-function-name-7.16.0.tgz", - "integrity": "sha512-BZh4mEk1xi2h4HFjWUXRQX5AEx4rvaZxHgax9gcjdLWdkjsY7MKt5p0otjsg5noXw+pB+clMCjw+aEVYADMjog==", + "version": "7.16.7", + "resolved": "https://registry.npmjs.org/@babel/helper-function-name/-/helper-function-name-7.16.7.tgz", + "integrity": "sha512-QfDfEnIUyyBSR3HtrtGECuZ6DAyCkYFp7GHl75vFtTnn6pjKeK0T1DB5lLkFvBea8MdaiUABx3osbgLyInoejA==", "dev": true, "dependencies": { - "@babel/helper-get-function-arity": "^7.16.0", - "@babel/template": "^7.16.0", - "@babel/types": "^7.16.0" + "@babel/helper-get-function-arity": "^7.16.7", + "@babel/template": "^7.16.7", + "@babel/types": "^7.16.7" }, "engines": { "node": ">=6.9.0" } }, "node_modules/@babel/helper-get-function-arity": { - "version": "7.16.0", - "resolved": "https://registry.npmjs.org/@babel/helper-get-function-arity/-/helper-get-function-arity-7.16.0.tgz", - "integrity": "sha512-ASCquNcywC1NkYh/z7Cgp3w31YW8aojjYIlNg4VeJiHkqyP4AzIvr4qx7pYDb4/s8YcsZWqqOSxgkvjUz1kpDQ==", + "version": "7.16.7", + "resolved": "https://registry.npmjs.org/@babel/helper-get-function-arity/-/helper-get-function-arity-7.16.7.tgz", + "integrity": "sha512-flc+RLSOBXzNzVhcLu6ujeHUrD6tANAOU5ojrRx/as+tbzf8+stUCj7+IfRRoAbEZqj/ahXEMsjhOhgeZsrnTw==", "dev": true, "dependencies": { - "@babel/types": "^7.16.0" + "@babel/types": "^7.16.7" }, "engines": { "node": ">=6.9.0" } }, "node_modules/@babel/helper-hoist-variables": { - "version": "7.16.0", - "resolved": "https://registry.npmjs.org/@babel/helper-hoist-variables/-/helper-hoist-variables-7.16.0.tgz", - "integrity": "sha512-1AZlpazjUR0EQZQv3sgRNfM9mEVWPK3M6vlalczA+EECcPz3XPh6VplbErL5UoMpChhSck5wAJHthlj1bYpcmg==", + "version": "7.16.7", + "resolved": "https://registry.npmjs.org/@babel/helper-hoist-variables/-/helper-hoist-variables-7.16.7.tgz", + "integrity": "sha512-m04d/0Op34H5v7pbZw6pSKP7weA6lsMvfiIAMeIvkY/R4xQtBSMFEigu9QTZ2qB/9l22vsxtM8a+Q8CzD255fg==", "dev": true, "dependencies": { - "@babel/types": "^7.16.0" + "@babel/types": "^7.16.7" }, "engines": { "node": ">=6.9.0" } }, "node_modules/@babel/helper-module-imports": { - "version": "7.16.0", - "resolved": "https://registry.npmjs.org/@babel/helper-module-imports/-/helper-module-imports-7.16.0.tgz", - "integrity": "sha512-kkH7sWzKPq0xt3H1n+ghb4xEMP8k0U7XV3kkB+ZGy69kDk2ySFW1qPi06sjKzFY3t1j6XbJSqr4mF9L7CYVyhg==", + "version": "7.16.7", + "resolved": "https://registry.npmjs.org/@babel/helper-module-imports/-/helper-module-imports-7.16.7.tgz", + "integrity": "sha512-LVtS6TqjJHFc+nYeITRo6VLXve70xmq7wPhWTqDJusJEgGmkAACWwMiTNrvfoQo6hEhFwAIixNkvB0jPXDL8Wg==", "dev": true, "dependencies": { - "@babel/types": "^7.16.0" + "@babel/types": "^7.16.7" }, "engines": { "node": ">=6.9.0" } }, "node_modules/@babel/helper-module-transforms": { - "version": "7.16.5", - "resolved": "https://registry.npmjs.org/@babel/helper-module-transforms/-/helper-module-transforms-7.16.5.tgz", - "integrity": "sha512-CkvMxgV4ZyyioElFwcuWnDCcNIeyqTkCm9BxXZi73RR1ozqlpboqsbGUNvRTflgZtFbbJ1v5Emvm+lkjMYY/LQ==", + "version": "7.16.7", + "resolved": "https://registry.npmjs.org/@babel/helper-module-transforms/-/helper-module-transforms-7.16.7.tgz", + "integrity": "sha512-gaqtLDxJEFCeQbYp9aLAefjhkKdjKcdh6DB7jniIGU3Pz52WAmP268zK0VgPz9hUNkMSYeH976K2/Y6yPadpng==", "dev": true, "dependencies": { - "@babel/helper-environment-visitor": "^7.16.5", - "@babel/helper-module-imports": "^7.16.0", - "@babel/helper-simple-access": "^7.16.0", - "@babel/helper-split-export-declaration": "^7.16.0", - "@babel/helper-validator-identifier": "^7.15.7", - "@babel/template": "^7.16.0", - "@babel/traverse": "^7.16.5", - "@babel/types": "^7.16.0" + "@babel/helper-environment-visitor": "^7.16.7", + "@babel/helper-module-imports": "^7.16.7", + "@babel/helper-simple-access": "^7.16.7", + "@babel/helper-split-export-declaration": "^7.16.7", + "@babel/helper-validator-identifier": "^7.16.7", + "@babel/template": "^7.16.7", + "@babel/traverse": "^7.16.7", + "@babel/types": "^7.16.7" }, "engines": { "node": ">=6.9.0" } }, "node_modules/@babel/helper-simple-access": { - "version": "7.16.0", - "resolved": "https://registry.npmjs.org/@babel/helper-simple-access/-/helper-simple-access-7.16.0.tgz", - "integrity": "sha512-o1rjBT/gppAqKsYfUdfHq5Rk03lMQrkPHG1OWzHWpLgVXRH4HnMM9Et9CVdIqwkCQlobnGHEJMsgWP/jE1zUiw==", + "version": "7.16.7", + "resolved": "https://registry.npmjs.org/@babel/helper-simple-access/-/helper-simple-access-7.16.7.tgz", + "integrity": "sha512-ZIzHVyoeLMvXMN/vok/a4LWRy8G2v205mNP0XOuf9XRLyX5/u9CnVulUtDgUTama3lT+bf/UqucuZjqiGuTS1g==", "dev": true, "dependencies": { - "@babel/types": "^7.16.0" + "@babel/types": "^7.16.7" }, "engines": { "node": ">=6.9.0" } }, "node_modules/@babel/helper-split-export-declaration": { - "version": "7.16.0", - "resolved": "https://registry.npmjs.org/@babel/helper-split-export-declaration/-/helper-split-export-declaration-7.16.0.tgz", - "integrity": "sha512-0YMMRpuDFNGTHNRiiqJX19GjNXA4H0E8jZ2ibccfSxaCogbm3am5WN/2nQNj0YnQwGWM1J06GOcQ2qnh3+0paw==", + "version": "7.16.7", + "resolved": "https://registry.npmjs.org/@babel/helper-split-export-declaration/-/helper-split-export-declaration-7.16.7.tgz", + "integrity": "sha512-xbWoy/PFoxSWazIToT9Sif+jJTlrMcndIsaOKvTA6u7QEo7ilkRZpjew18/W3c7nm8fXdUDXh02VXTbZ0pGDNw==", "dev": true, "dependencies": { - "@babel/types": "^7.16.0" + "@babel/types": "^7.16.7" }, "engines": { "node": ">=6.9.0" } }, "node_modules/@babel/helper-validator-identifier": { - "version": "7.15.7", - "resolved": "https://registry.npmjs.org/@babel/helper-validator-identifier/-/helper-validator-identifier-7.15.7.tgz", - "integrity": "sha512-K4JvCtQqad9OY2+yTU8w+E82ywk/fe+ELNlt1G8z3bVGlZfn/hOcQQsUhGhW/N+tb3fxK800wLtKOE/aM0m72w==", + "version": "7.16.7", + "resolved": "https://registry.npmjs.org/@babel/helper-validator-identifier/-/helper-validator-identifier-7.16.7.tgz", + "integrity": "sha512-hsEnFemeiW4D08A5gUAZxLBTXpZ39P+a+DGDsHw1yxqyQ/jzFEnxf5uTEGp+3bzAbNOxU1paTgYS4ECU/IgfDw==", "dev": true, "engines": { "node": ">=6.9.0" } }, "node_modules/@babel/helper-validator-option": { - "version": "7.14.5", - "resolved": "https://registry.npmjs.org/@babel/helper-validator-option/-/helper-validator-option-7.14.5.tgz", - "integrity": "sha512-OX8D5eeX4XwcroVW45NMvoYaIuFI+GQpA2a8Gi+X/U/cDUIRsV37qQfF905F0htTRCREQIB4KqPeaveRJUl3Ow==", + "version": "7.16.7", + "resolved": "https://registry.npmjs.org/@babel/helper-validator-option/-/helper-validator-option-7.16.7.tgz", + "integrity": "sha512-TRtenOuRUVo9oIQGPC5G9DgK4743cdxvtOw0weQNpZXaS16SCBi5MNjZF8vba3ETURjZpTbVn7Vvcf2eAwFozQ==", "dev": true, "engines": { "node": ">=6.9.0" } }, "node_modules/@babel/helpers": { - "version": "7.16.5", - "resolved": "https://registry.npmjs.org/@babel/helpers/-/helpers-7.16.5.tgz", - "integrity": "sha512-TLgi6Lh71vvMZGEkFuIxzaPsyeYCHQ5jJOOX1f0xXn0uciFuE8cEk0wyBquMcCxBXZ5BJhE2aUB7pnWTD150Tw==", + "version": "7.16.7", + "resolved": "https://registry.npmjs.org/@babel/helpers/-/helpers-7.16.7.tgz", + "integrity": "sha512-9ZDoqtfY7AuEOt3cxchfii6C7GDyyMBffktR5B2jvWv8u2+efwvpnVKXMWzNehqy68tKgAfSwfdw/lWpthS2bw==", "dev": true, "dependencies": { - "@babel/template": "^7.16.0", - "@babel/traverse": "^7.16.5", - "@babel/types": "^7.16.0" + "@babel/template": "^7.16.7", + "@babel/traverse": "^7.16.7", + "@babel/types": "^7.16.7" }, "engines": { "node": ">=6.9.0" } }, "node_modules/@babel/highlight": { - "version": "7.16.0", - "resolved": "https://registry.npmjs.org/@babel/highlight/-/highlight-7.16.0.tgz", - "integrity": "sha512-t8MH41kUQylBtu2+4IQA3atqevA2lRgqA2wyVB/YiWmsDSuylZZuXOUy9ric30hfzauEFfdsuk/eXTRrGrfd0g==", + "version": "7.16.7", + "resolved": "https://registry.npmjs.org/@babel/highlight/-/highlight-7.16.7.tgz", + "integrity": "sha512-aKpPMfLvGO3Q97V0qhw/V2SWNWlwfJknuwAunU7wZLSfrM4xTBvg7E5opUVi1kJTBKihE38CPg4nBiqX83PWYw==", "dev": true, "dependencies": { - "@babel/helper-validator-identifier": "^7.15.7", + "@babel/helper-validator-identifier": "^7.16.7", "chalk": "^2.0.0", "js-tokens": "^4.0.0" }, @@ -326,9 +326,9 @@ } }, "node_modules/@babel/parser": { - "version": "7.16.6", - "resolved": "https://registry.npmjs.org/@babel/parser/-/parser-7.16.6.tgz", - "integrity": "sha512-Gr86ujcNuPDnNOY8mi383Hvi8IYrJVJYuf3XcuBM/Dgd+bINn/7tHqsj+tKkoreMbmGsFLsltI/JJd8fOFWGDQ==", + "version": "7.16.8", + "resolved": "https://registry.npmjs.org/@babel/parser/-/parser-7.16.8.tgz", + "integrity": "sha512-i7jDUfrVBWc+7OKcBzEe5n7fbv3i2fWtxKzzCvOjnzSxMfWMigAhtfJ7qzZNGFNMsCCd67+uz553dYKWXPvCKw==", "dev": true, "bin": { "parser": "bin/babel-parser.js" @@ -338,33 +338,33 @@ } }, "node_modules/@babel/template": { - "version": "7.16.0", - "resolved": "https://registry.npmjs.org/@babel/template/-/template-7.16.0.tgz", - "integrity": "sha512-MnZdpFD/ZdYhXwiunMqqgyZyucaYsbL0IrjoGjaVhGilz+x8YB++kRfygSOIj1yOtWKPlx7NBp+9I1RQSgsd5A==", + "version": "7.16.7", + "resolved": "https://registry.npmjs.org/@babel/template/-/template-7.16.7.tgz", + "integrity": "sha512-I8j/x8kHUrbYRTUxXrrMbfCa7jxkE7tZre39x3kjr9hvI82cK1FfqLygotcWN5kdPGWcLdWMHpSBavse5tWw3w==", "dev": true, "dependencies": { - "@babel/code-frame": "^7.16.0", - "@babel/parser": "^7.16.0", - "@babel/types": "^7.16.0" + "@babel/code-frame": "^7.16.7", + "@babel/parser": "^7.16.7", + "@babel/types": "^7.16.7" }, "engines": { "node": ">=6.9.0" } }, "node_modules/@babel/traverse": { - "version": "7.16.5", - "resolved": "https://registry.npmjs.org/@babel/traverse/-/traverse-7.16.5.tgz", - "integrity": "sha512-FOCODAzqUMROikDYLYxl4nmwiLlu85rNqBML/A5hKRVXG2LV8d0iMqgPzdYTcIpjZEBB7D6UDU9vxRZiriASdQ==", - "dev": true, - "dependencies": { - "@babel/code-frame": "^7.16.0", - "@babel/generator": "^7.16.5", - "@babel/helper-environment-visitor": "^7.16.5", - "@babel/helper-function-name": "^7.16.0", - "@babel/helper-hoist-variables": "^7.16.0", - "@babel/helper-split-export-declaration": "^7.16.0", - "@babel/parser": "^7.16.5", - "@babel/types": "^7.16.0", + "version": "7.16.8", + "resolved": "https://registry.npmjs.org/@babel/traverse/-/traverse-7.16.8.tgz", + "integrity": "sha512-xe+H7JlvKsDQwXRsBhSnq1/+9c+LlQcCK3Tn/l5sbx02HYns/cn7ibp9+RV1sIUqu7hKg91NWsgHurO9dowITQ==", + "dev": true, + "dependencies": { + "@babel/code-frame": "^7.16.7", + "@babel/generator": "^7.16.8", + "@babel/helper-environment-visitor": "^7.16.7", + "@babel/helper-function-name": "^7.16.7", + "@babel/helper-hoist-variables": "^7.16.7", + "@babel/helper-split-export-declaration": "^7.16.7", + "@babel/parser": "^7.16.8", + "@babel/types": "^7.16.8", "debug": "^4.1.0", "globals": "^11.1.0" }, @@ -382,12 +382,12 @@ } }, "node_modules/@babel/types": { - "version": "7.16.0", - "resolved": "https://registry.npmjs.org/@babel/types/-/types-7.16.0.tgz", - "integrity": "sha512-PJgg/k3SdLsGb3hhisFvtLOw5ts113klrpLuIPtCJIU+BB24fqq6lf8RWqKJEjzqXR9AEH1rIb5XTqwBHB+kQg==", + "version": "7.16.8", + "resolved": "https://registry.npmjs.org/@babel/types/-/types-7.16.8.tgz", + "integrity": "sha512-smN2DQc5s4M7fntyjGtyIPbRJv6wW4rU/94fmYJ7PKQuZkC0qGMHXJbg6sNGt12JmVr4k5YaptI/XtiLJBnmIg==", "dev": true, "dependencies": { - "@babel/helper-validator-identifier": "^7.15.7", + "@babel/helper-validator-identifier": "^7.16.7", "to-fast-properties": "^2.0.0" }, "engines": { @@ -570,10 +570,6 @@ "resolved": "packages/client", "link": true }, - "node_modules/@node-redis/graph": { - "resolved": "packages/graph", - "link": true - }, "node_modules/@node-redis/json": { "resolved": "packages/json", "link": true @@ -758,9 +754,9 @@ } }, "node_modules/@sindresorhus/is": { - "version": "4.2.0", - "resolved": "https://registry.npmjs.org/@sindresorhus/is/-/is-4.2.0.tgz", - "integrity": "sha512-VkE3KLBmJwcCaVARtQpfuKcKv8gcBmUubrfHGF84dXuuW6jgsRYxPtzcIhPyK9WAPpRt2/xY6zkD9MnRaJzSyw==", + "version": "4.2.1", + "resolved": "https://registry.npmjs.org/@sindresorhus/is/-/is-4.2.1.tgz", + "integrity": "sha512-BrzrgtaqEre0qfvI8sMTaEvx+bayuhPmfe2rfeUGPPHYr/PLxCOqkOe4TQTDPb+qcqgNcsAtXV/Ew74mcDIE8w==", "dev": true, "engines": { "node": ">=10" @@ -880,9 +876,9 @@ "dev": true }, "node_modules/@types/node": { - "version": "17.0.5", - "resolved": "https://registry.npmjs.org/@types/node/-/node-17.0.5.tgz", - "integrity": "sha512-w3mrvNXLeDYV1GKTZorGJQivK6XLCoGwpnyJFbJVK/aTBQUxOCaa/GlFAAN3OTDFcb7h5tiFG+YXCO2By+riZw==", + "version": "17.0.8", + "resolved": "https://registry.npmjs.org/@types/node/-/node-17.0.8.tgz", + "integrity": "sha512-YofkM6fGv4gDJq78g4j0mMuGMkZVxZDgtU0JRdx6FgiJDG+0fY0GKVolOV8WqVmEhLCXkQRjwDdKyPxJp/uucg==", "dev": true }, "node_modules/@types/parse-json": { @@ -947,13 +943,14 @@ "dev": true }, "node_modules/@typescript-eslint/eslint-plugin": { - "version": "5.8.1", - "resolved": "https://registry.npmjs.org/@typescript-eslint/eslint-plugin/-/eslint-plugin-5.8.1.tgz", - "integrity": "sha512-wTZ5oEKrKj/8/366qTM366zqhIKAp6NCMweoRONtfuC07OAU9nVI2GZZdqQ1qD30WAAtcPdkH+npDwtRFdp4Rw==", + "version": "5.9.1", + "resolved": "https://registry.npmjs.org/@typescript-eslint/eslint-plugin/-/eslint-plugin-5.9.1.tgz", + "integrity": "sha512-Xv9tkFlyD4MQGpJgTo6wqDqGvHIRmRgah/2Sjz1PUnJTawjHWIwBivUE9x0QtU2WVii9baYgavo/bHjrZJkqTw==", "dev": true, "dependencies": { - "@typescript-eslint/experimental-utils": "5.8.1", - "@typescript-eslint/scope-manager": "5.8.1", + "@typescript-eslint/experimental-utils": "5.9.1", + "@typescript-eslint/scope-manager": "5.9.1", + "@typescript-eslint/type-utils": "5.9.1", "debug": "^4.3.2", "functional-red-black-tree": "^1.0.1", "ignore": "^5.1.8", @@ -994,15 +991,15 @@ } }, "node_modules/@typescript-eslint/experimental-utils": { - "version": "5.8.1", - "resolved": "https://registry.npmjs.org/@typescript-eslint/experimental-utils/-/experimental-utils-5.8.1.tgz", - "integrity": "sha512-fbodVnjIDU4JpeXWRDsG5IfIjYBxEvs8EBO8W1+YVdtrc2B9ppfof5sZhVEDOtgTfFHnYQJDI8+qdqLYO4ceww==", + "version": "5.9.1", + "resolved": "https://registry.npmjs.org/@typescript-eslint/experimental-utils/-/experimental-utils-5.9.1.tgz", + "integrity": "sha512-cb1Njyss0mLL9kLXgS/eEY53SZQ9sT519wpX3i+U457l2UXRDuo87hgKfgRazmu9/tQb0x2sr3Y0yrU+Zz0y+w==", "dev": true, "dependencies": { "@types/json-schema": "^7.0.9", - "@typescript-eslint/scope-manager": "5.8.1", - "@typescript-eslint/types": "5.8.1", - "@typescript-eslint/typescript-estree": "5.8.1", + "@typescript-eslint/scope-manager": "5.9.1", + "@typescript-eslint/types": "5.9.1", + "@typescript-eslint/typescript-estree": "5.9.1", "eslint-scope": "^5.1.1", "eslint-utils": "^3.0.0" }, @@ -1018,14 +1015,14 @@ } }, "node_modules/@typescript-eslint/parser": { - "version": "5.8.1", - "resolved": "https://registry.npmjs.org/@typescript-eslint/parser/-/parser-5.8.1.tgz", - "integrity": "sha512-K1giKHAjHuyB421SoXMXFHHVI4NdNY603uKw92++D3qyxSeYvC10CBJ/GE5Thpo4WTUvu1mmJI2/FFkz38F2Gw==", + "version": "5.9.1", + "resolved": "https://registry.npmjs.org/@typescript-eslint/parser/-/parser-5.9.1.tgz", + "integrity": "sha512-PLYO0AmwD6s6n0ZQB5kqPgfvh73p0+VqopQQLuNfi7Lm0EpfKyDalchpVwkE+81k5HeiRrTV/9w1aNHzjD7C4g==", "dev": true, "dependencies": { - "@typescript-eslint/scope-manager": "5.8.1", - "@typescript-eslint/types": "5.8.1", - "@typescript-eslint/typescript-estree": "5.8.1", + "@typescript-eslint/scope-manager": "5.9.1", + "@typescript-eslint/types": "5.9.1", + "@typescript-eslint/typescript-estree": "5.9.1", "debug": "^4.3.2" }, "engines": { @@ -1045,13 +1042,31 @@ } }, "node_modules/@typescript-eslint/scope-manager": { - "version": "5.8.1", - "resolved": "https://registry.npmjs.org/@typescript-eslint/scope-manager/-/scope-manager-5.8.1.tgz", - "integrity": "sha512-DGxJkNyYruFH3NIZc3PwrzwOQAg7vvgsHsHCILOLvUpupgkwDZdNq/cXU3BjF4LNrCsVg0qxEyWasys5AiJ85Q==", + "version": "5.9.1", + "resolved": "https://registry.npmjs.org/@typescript-eslint/scope-manager/-/scope-manager-5.9.1.tgz", + "integrity": "sha512-8BwvWkho3B/UOtzRyW07ffJXPaLSUKFBjpq8aqsRvu6HdEuzCY57+ffT7QoV4QXJXWSU1+7g3wE4AlgImmQ9pQ==", + "dev": true, + "dependencies": { + "@typescript-eslint/types": "5.9.1", + "@typescript-eslint/visitor-keys": "5.9.1" + }, + "engines": { + "node": "^12.22.0 || ^14.17.0 || >=16.0.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/typescript-eslint" + } + }, + "node_modules/@typescript-eslint/type-utils": { + "version": "5.9.1", + "resolved": "https://registry.npmjs.org/@typescript-eslint/type-utils/-/type-utils-5.9.1.tgz", + "integrity": "sha512-tRSpdBnPRssjlUh35rE9ug5HrUvaB9ntREy7gPXXKwmIx61TNN7+l5YKgi1hMKxo5NvqZCfYhA5FvyuJG6X6vg==", "dev": true, "dependencies": { - "@typescript-eslint/types": "5.8.1", - "@typescript-eslint/visitor-keys": "5.8.1" + "@typescript-eslint/experimental-utils": "5.9.1", + "debug": "^4.3.2", + "tsutils": "^3.21.0" }, "engines": { "node": "^12.22.0 || ^14.17.0 || >=16.0.0" @@ -1059,12 +1074,20 @@ "funding": { "type": "opencollective", "url": "https://opencollective.com/typescript-eslint" + }, + "peerDependencies": { + "eslint": "*" + }, + "peerDependenciesMeta": { + "typescript": { + "optional": true + } } }, "node_modules/@typescript-eslint/types": { - "version": "5.8.1", - "resolved": "https://registry.npmjs.org/@typescript-eslint/types/-/types-5.8.1.tgz", - "integrity": "sha512-L/FlWCCgnjKOLefdok90/pqInkomLnAcF9UAzNr+DSqMC3IffzumHTQTrINXhP1gVp9zlHiYYjvozVZDPleLcA==", + "version": "5.9.1", + "resolved": "https://registry.npmjs.org/@typescript-eslint/types/-/types-5.9.1.tgz", + "integrity": "sha512-SsWegWudWpkZCwwYcKoDwuAjoZXnM1y2EbEerTHho19Hmm+bQ56QG4L4jrtCu0bI5STaRTvRTZmjprWlTw/5NQ==", "dev": true, "engines": { "node": "^12.22.0 || ^14.17.0 || >=16.0.0" @@ -1075,13 +1098,13 @@ } }, "node_modules/@typescript-eslint/typescript-estree": { - "version": "5.8.1", - "resolved": "https://registry.npmjs.org/@typescript-eslint/typescript-estree/-/typescript-estree-5.8.1.tgz", - "integrity": "sha512-26lQ8l8tTbG7ri7xEcCFT9ijU5Fk+sx/KRRyyzCv7MQ+rZZlqiDPtMKWLC8P7o+dtCnby4c+OlxuX1tp8WfafQ==", + "version": "5.9.1", + "resolved": "https://registry.npmjs.org/@typescript-eslint/typescript-estree/-/typescript-estree-5.9.1.tgz", + "integrity": "sha512-gL1sP6A/KG0HwrahVXI9fZyeVTxEYV//6PmcOn1tD0rw8VhUWYeZeuWHwwhnewnvEMcHjhnJLOBhA9rK4vmb8A==", "dev": true, "dependencies": { - "@typescript-eslint/types": "5.8.1", - "@typescript-eslint/visitor-keys": "5.8.1", + "@typescript-eslint/types": "5.9.1", + "@typescript-eslint/visitor-keys": "5.9.1", "debug": "^4.3.2", "globby": "^11.0.4", "is-glob": "^4.0.3", @@ -1111,16 +1134,16 @@ } }, "node_modules/@typescript-eslint/typescript-estree/node_modules/globby": { - "version": "11.0.4", - "resolved": "https://registry.npmjs.org/globby/-/globby-11.0.4.tgz", - "integrity": "sha512-9O4MVG9ioZJ08ffbcyVYyLOJLk5JQ688pJ4eMGLpdWLHq/Wr1D9BlriLQyL0E+jbkuePVZXYFj47QM/v093wHg==", + "version": "11.1.0", + "resolved": "https://registry.npmjs.org/globby/-/globby-11.1.0.tgz", + "integrity": "sha512-jhIXaOzy1sb8IyocaruWSn1TjmnBVs8Ayhcy83rmxNJ8q2uWKCAj3CnJY+KpGSXCueAPc0i05kVvVKtP1t9S3g==", "dev": true, "dependencies": { "array-union": "^2.1.0", "dir-glob": "^3.0.1", - "fast-glob": "^3.1.1", - "ignore": "^5.1.4", - "merge2": "^1.3.0", + "fast-glob": "^3.2.9", + "ignore": "^5.2.0", + "merge2": "^1.4.1", "slash": "^3.0.0" }, "engines": { @@ -1146,12 +1169,12 @@ } }, "node_modules/@typescript-eslint/visitor-keys": { - "version": "5.8.1", - "resolved": "https://registry.npmjs.org/@typescript-eslint/visitor-keys/-/visitor-keys-5.8.1.tgz", - "integrity": "sha512-SWgiWIwocK6NralrJarPZlWdr0hZnj5GXHIgfdm8hNkyKvpeQuFyLP6YjSIe9kf3YBIfU6OHSZLYkQ+smZwtNg==", + "version": "5.9.1", + "resolved": "https://registry.npmjs.org/@typescript-eslint/visitor-keys/-/visitor-keys-5.9.1.tgz", + "integrity": "sha512-Xh37pNz9e9ryW4TVdwiFzmr4hloty8cFj8GTWMXh3Z8swGwyQWeCcNgF0hm6t09iZd6eiZmIf4zHedQVP6TVtg==", "dev": true, "dependencies": { - "@typescript-eslint/types": "5.8.1", + "@typescript-eslint/types": "5.9.1", "eslint-visitor-keys": "^3.0.0" }, "engines": { @@ -1619,9 +1642,9 @@ } }, "node_modules/camelcase": { - "version": "6.2.1", - "resolved": "https://registry.npmjs.org/camelcase/-/camelcase-6.2.1.tgz", - "integrity": "sha512-tVI4q5jjFV5CavAU8DXfza/TJcZutVKo/5Foskmsqcm0MsL91moHvwiGNnqaa2o6PF/7yT5ikDRcVcl8Rj6LCA==", + "version": "6.3.0", + "resolved": "https://registry.npmjs.org/camelcase/-/camelcase-6.3.0.tgz", + "integrity": "sha512-Gmy6FhYlCY7uOElZUSbxo2UCDH8owEk996gkbrpsgGtrJLM3J7jGxl9Ic7Qwwj4ivOE5AWZWRMecDdF7hqGjFA==", "dev": true, "engines": { "node": ">=10" @@ -1631,9 +1654,9 @@ } }, "node_modules/caniuse-lite": { - "version": "1.0.30001294", - "resolved": "https://registry.npmjs.org/caniuse-lite/-/caniuse-lite-1.0.30001294.tgz", - "integrity": "sha512-LiMlrs1nSKZ8qkNhpUf5KD0Al1KCBE3zaT7OLOwEkagXMEDij98SiOovn9wxVGQpklk9vVC/pUSqgYmkmKOS8g==", + "version": "1.0.30001299", + "resolved": "https://registry.npmjs.org/caniuse-lite/-/caniuse-lite-1.0.30001299.tgz", + "integrity": "sha512-iujN4+x7QzqA2NCSrS5VUy+4gLmRd4xv6vbBBsmfVqTx8bLAD8097euLqQgKxSVLvxjSDcvF1T/i9ocgnUFexw==", "dev": true, "funding": { "type": "opencollective", @@ -1906,9 +1929,9 @@ } }, "node_modules/debug": { - "version": "4.3.2", - "resolved": "https://registry.npmjs.org/debug/-/debug-4.3.2.tgz", - "integrity": "sha512-mOp8wKcvj7XxC78zLgw/ZA+6TSgkoE2C/ienthhRD298T7UNwAg9diBpLRxC0mOezLl4B0xV7M0cCO6P/O0Xhw==", + "version": "4.3.3", + "resolved": "https://registry.npmjs.org/debug/-/debug-4.3.3.tgz", + "integrity": "sha512-/zxw5+vh1Tfv+4Qn7a5nsbcJKPaSvCDhojn6FEl9vupwK2VCSDtEiEtqr8DFtzYFOdz63LBkxec7DYuc2jon6Q==", "dev": true, "dependencies": { "ms": "2.1.2" @@ -2092,9 +2115,9 @@ "dev": true }, "node_modules/electron-to-chromium": { - "version": "1.4.30", - "resolved": "https://registry.npmjs.org/electron-to-chromium/-/electron-to-chromium-1.4.30.tgz", - "integrity": "sha512-609z9sIMxDHg+TcR/VB3MXwH+uwtrYyeAwWc/orhnr90ixs6WVGSrt85CDLGUdNnLqCA7liv426V20EecjvflQ==", + "version": "1.4.44", + "resolved": "https://registry.npmjs.org/electron-to-chromium/-/electron-to-chromium-1.4.44.tgz", + "integrity": "sha512-tHGWiUUmY7GABK8+DNcr474cnZDTzD8x1736SlDosVH8+/vRJeqfaIBAEHFtMjddz/0T4rKKYsxEc8BwQRdBpw==", "dev": true }, "node_modules/email-addresses": { @@ -2173,9 +2196,9 @@ } }, "node_modules/eslint": { - "version": "8.5.0", - "resolved": "https://registry.npmjs.org/eslint/-/eslint-8.5.0.tgz", - "integrity": "sha512-tVGSkgNbOfiHyVte8bCM8OmX+xG9PzVG/B4UCF60zx7j61WIVY/AqJECDgpLD4DbbESD0e174gOg3ZlrX15GDg==", + "version": "8.6.0", + "resolved": "https://registry.npmjs.org/eslint/-/eslint-8.6.0.tgz", + "integrity": "sha512-UvxdOJ7mXFlw7iuHZA4jmzPaUqIw54mZrv+XPYKNbKdLR0et4rf60lIZUU9kiNtnzzMzGWxMV+tQ7uG7JG8DPw==", "dev": true, "dependencies": { "@eslint/eslintrc": "^1.0.5", @@ -2190,7 +2213,7 @@ "eslint-scope": "^7.1.0", "eslint-utils": "^3.0.0", "eslint-visitor-keys": "^3.1.0", - "espree": "^9.2.0", + "espree": "^9.3.0", "esquery": "^1.4.0", "esutils": "^2.0.2", "fast-deep-equal": "^3.1.3", @@ -2359,12 +2382,12 @@ } }, "node_modules/espree": { - "version": "9.2.0", - "resolved": "https://registry.npmjs.org/espree/-/espree-9.2.0.tgz", - "integrity": "sha512-oP3utRkynpZWF/F2x/HZJ+AGtnIclaR7z1pYPxy7NYM2fSO6LgK/Rkny8anRSPK/VwEA1eqm2squui0T7ZMOBg==", + "version": "9.3.0", + "resolved": "https://registry.npmjs.org/espree/-/espree-9.3.0.tgz", + "integrity": "sha512-d/5nCsb0JcqsSEeQzFZ8DH1RmxPcglRWh24EFTlUEmCKoehXGdpsx0RkHDubqUI8LSAIKMQp4r9SzQ3n+sm4HQ==", "dev": true, "dependencies": { - "acorn": "^8.6.0", + "acorn": "^8.7.0", "acorn-jsx": "^5.3.1", "eslint-visitor-keys": "^3.1.0" }, @@ -2489,9 +2512,9 @@ "dev": true }, "node_modules/fast-glob": { - "version": "3.2.7", - "resolved": "https://registry.npmjs.org/fast-glob/-/fast-glob-3.2.7.tgz", - "integrity": "sha512-rYGMRwip6lUMvYD3BTScMwT1HtAs2d71SMv66Vrxs0IekGZEjhM0pcMfjQPnknBt2zeCwQMEupiN02ZP4DiT1Q==", + "version": "3.2.10", + "resolved": "https://registry.npmjs.org/fast-glob/-/fast-glob-3.2.10.tgz", + "integrity": "sha512-s9nFhFnvR63wls6/kM88kQqDhMu0AfdjqouE2l5GVQPbqLgyFjjU5ry/r2yKsJxpb9Py1EYNqieFrmMaX4v++A==", "dev": true, "dependencies": { "@nodelib/fs.stat": "^2.0.2", @@ -2501,7 +2524,7 @@ "micromatch": "^4.0.4" }, "engines": { - "node": ">=8" + "node": ">=8.6.0" } }, "node_modules/fast-json-stable-stringify": { @@ -2968,9 +2991,9 @@ } }, "node_modules/graceful-fs": { - "version": "4.2.8", - "resolved": "https://registry.npmjs.org/graceful-fs/-/graceful-fs-4.2.8.tgz", - "integrity": "sha512-qkIilPUYcNhJpd33n0GBXTB1MMPp14TxEsEs0pTrsSVucApsYzW5V+Q8Qxhik6KU3evy+qkAAowTByymK0avdg==", + "version": "4.2.9", + "resolved": "https://registry.npmjs.org/graceful-fs/-/graceful-fs-4.2.9.tgz", + "integrity": "sha512-NtNxqUcXgpW2iMrfqSfR73Glt39K+BLwWsPs94yR63v45T0Wbej7eRmL5cWfwEgqXnmjQp3zaJTshdRW/qC2ZQ==", "dev": true }, "node_modules/growl": { @@ -3299,9 +3322,9 @@ } }, "node_modules/is-core-module": { - "version": "2.8.0", - "resolved": "https://registry.npmjs.org/is-core-module/-/is-core-module-2.8.0.tgz", - "integrity": "sha512-vd15qHsaqrRL7dtH6QNuy0ndJmRDrS9HAM1CAiSifNUFv4x1a0CCVsj18hJ1mShxIG6T2i1sO78MkP56r0nYRw==", + "version": "2.8.1", + "resolved": "https://registry.npmjs.org/is-core-module/-/is-core-module-2.8.1.tgz", + "integrity": "sha512-SdNCUs284hr40hFTFP6l0IfZ/RSrMXF3qgoRHd3/79unUTvrFO/JoXwkGm+5J/Oe3E/b5GsnG330uUNgRpu1PA==", "dev": true, "dependencies": { "has": "^1.0.3" @@ -3720,9 +3743,9 @@ "dev": true }, "node_modules/keyv": { - "version": "4.0.4", - "resolved": "https://registry.npmjs.org/keyv/-/keyv-4.0.4.tgz", - "integrity": "sha512-vqNHbAc8BBsxk+7QBYLW0Y219rWcClspR6WSeoHYKG5mnsSoOH+BL1pWq02DDCVdvvuUny5rkBlzMRzoqc+GIg==", + "version": "4.0.5", + "resolved": "https://registry.npmjs.org/keyv/-/keyv-4.0.5.tgz", + "integrity": "sha512-531pkGLqV3BMg0eDqqJFI0R1mkK1Nm5xIP2mM6keP5P8WfFtCkg2IOwplTUmlGoTgIg9yQYZ/kdihhz89XH3vA==", "dev": true, "dependencies": { "json-buffer": "3.0.1" @@ -4011,6 +4034,29 @@ "url": "https://opencollective.com/mochajs" } }, + "node_modules/mocha/node_modules/debug": { + "version": "4.3.2", + "resolved": "https://registry.npmjs.org/debug/-/debug-4.3.2.tgz", + "integrity": "sha512-mOp8wKcvj7XxC78zLgw/ZA+6TSgkoE2C/ienthhRD298T7UNwAg9diBpLRxC0mOezLl4B0xV7M0cCO6P/O0Xhw==", + "dev": true, + "dependencies": { + "ms": "2.1.2" + }, + "engines": { + "node": ">=6.0" + }, + "peerDependenciesMeta": { + "supports-color": { + "optional": true + } + } + }, + "node_modules/mocha/node_modules/debug/node_modules/ms": { + "version": "2.1.2", + "resolved": "https://registry.npmjs.org/ms/-/ms-2.1.2.tgz", + "integrity": "sha512-sGkPx+VjMtmA6MX27oA4FBFELFCZZ4S4XqeGOXCv68tT+jb3vk/RyaKWP0PTKyWtmLSM0b+adUTEvbs1PEaH2w==", + "dev": true + }, "node_modules/mocha/node_modules/escape-string-regexp": { "version": "4.0.0", "resolved": "https://registry.npmjs.org/escape-string-regexp/-/escape-string-regexp-4.0.0.tgz", @@ -4904,9 +4950,9 @@ "dev": true }, "node_modules/picomatch": { - "version": "2.3.0", - "resolved": "https://registry.npmjs.org/picomatch/-/picomatch-2.3.0.tgz", - "integrity": "sha512-lY1Q/PiJGC2zOv/z391WOTD+Z02bCgsFfvxoXXf6h7kv9o+WmsmzYqrAwY63sNgOxE4xEdq0WyUnXfKeBrSvYw==", + "version": "2.3.1", + "resolved": "https://registry.npmjs.org/picomatch/-/picomatch-2.3.1.tgz", + "integrity": "sha512-JU3teHTNjmE2VCGFzuY8EXzCDVwEqB2a8fsIvwaStHhAWJEeVd1o1QD80CU6+ZdEXXSLbSsuLwJjkCBWqRQUVA==", "dev": true, "engines": { "node": ">=8.6" @@ -5034,9 +5080,9 @@ } }, "node_modules/qs": { - "version": "6.10.2", - "resolved": "https://registry.npmjs.org/qs/-/qs-6.10.2.tgz", - "integrity": "sha512-mSIdjzqznWgfd4pMii7sHtaYF8rx8861hBO80SraY5GT0XQibWZWJSid0avzHGkDIZLImux2S5mXO0Hfct2QCw==", + "version": "6.10.3", + "resolved": "https://registry.npmjs.org/qs/-/qs-6.10.3.tgz", + "integrity": "sha512-wr7M2E0OFRfIfJZjKGieI8lBKb7fRCH4Fv5KNPEs7gJ8jadvotdsS08PzOKR7opXhZ/Xkjtt3WF9g38drmyRqQ==", "dev": true, "dependencies": { "side-channel": "^1.0.4" @@ -5222,9 +5268,9 @@ } }, "node_modules/release-it": { - "version": "14.11.8", - "resolved": "https://registry.npmjs.org/release-it/-/release-it-14.11.8.tgz", - "integrity": "sha512-951DJ0kwjwU7CwGU3BCvRBgLxuJsOPRrZkqx0AsugJdSyPpUdwY9nlU0RAoSKqgh+VTerzecXLIIwgsGIpNxlA==", + "version": "14.12.1", + "resolved": "https://registry.npmjs.org/release-it/-/release-it-14.12.1.tgz", + "integrity": "sha512-dYPGZ7F/kfIWzsGlzNCL6PiWfPoaVUILcmqQm80kgYhI/b9RW3k6DVqE0nqI4fHxRT3fMeKWWvS0jdQmFDKn3Q==", "dev": true, "dependencies": { "@iarna/toml": "2.2.5", @@ -5232,7 +5278,7 @@ "async-retry": "1.3.3", "chalk": "4.1.2", "cosmiconfig": "7.0.1", - "debug": "4.3.2", + "debug": "4.3.3", "deprecated-obj": "2.0.0", "execa": "5.1.1", "form-data": "4.0.0", @@ -5336,13 +5382,17 @@ "dev": true }, "node_modules/resolve": { - "version": "1.20.0", - "resolved": "https://registry.npmjs.org/resolve/-/resolve-1.20.0.tgz", - "integrity": "sha512-wENBPt4ySzg4ybFQW2TT1zMQucPK95HSh/nq2CFTZVOGut2+pQvSsgtda4d26YrYcr067wjbmzOG8byDPBX63A==", + "version": "1.21.0", + "resolved": "https://registry.npmjs.org/resolve/-/resolve-1.21.0.tgz", + "integrity": "sha512-3wCbTpk5WJlyE4mSOtDLhqQmGFi0/TD9VPwmiolnk8U0wRgMEktqCXd3vy5buTO3tljvalNvKrjHEfrd2WpEKA==", "dev": true, "dependencies": { - "is-core-module": "^2.2.0", - "path-parse": "^1.0.6" + "is-core-module": "^2.8.0", + "path-parse": "^1.0.7", + "supports-preserve-symlinks-flag": "^1.0.0" + }, + "bin": { + "resolve": "bin/resolve" }, "funding": { "url": "https://github.com/sponsors/ljharb" @@ -5452,9 +5502,9 @@ } }, "node_modules/rxjs": { - "version": "7.5.1", - "resolved": "https://registry.npmjs.org/rxjs/-/rxjs-7.5.1.tgz", - "integrity": "sha512-KExVEeZWxMZnZhUZtsJcFwz8IvPvgu4G2Z2QyqjZQzUGr32KDYuSxrEYO4w3tFFNbfLozcrKUTvTPi+E9ywJkQ==", + "version": "7.5.2", + "resolved": "https://registry.npmjs.org/rxjs/-/rxjs-7.5.2.tgz", + "integrity": "sha512-PwDt186XaL3QN5qXj/H9DGyHhP3/RYYgZZwqBv9Tv8rsAaiwFH1IsJJlcgD37J7UW5a6O67qX0KWKS3/pu0m4w==", "dev": true, "dependencies": { "tslib": "^2.1.0" @@ -5782,6 +5832,18 @@ "node": ">=8" } }, + "node_modules/supports-preserve-symlinks-flag": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/supports-preserve-symlinks-flag/-/supports-preserve-symlinks-flag-1.0.0.tgz", + "integrity": "sha512-ot0WnXS9fgdkgIcePe6RHNk1WA8+muPa6cSjeR3V8K27q9BB1rTE3R1p7Hv0z1ZyAc8s6Vvv8DIyWf681MAt0w==", + "dev": true, + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, "node_modules/test-exclude": { "version": "6.0.0", "resolved": "https://registry.npmjs.org/test-exclude/-/test-exclude-6.0.0.tgz", @@ -6463,9 +6525,9 @@ "devDependencies": { "@istanbuljs/nyc-config-typescript": "^1.0.2", "@node-redis/test-utils": "*", - "@types/node": "^17.0.5", + "@types/node": "^17.0.8", "nyc": "^15.1.0", - "release-it": "^14.11.8", + "release-it": "^14.12.1", "source-map-support": "^0.5.21", "ts-node": "^10.4.0", "typedoc": "^0.22.10", @@ -6488,15 +6550,15 @@ "devDependencies": { "@istanbuljs/nyc-config-typescript": "^1.0.2", "@node-redis/test-utils": "*", - "@types/node": "^17.0.5", + "@types/node": "^17.0.8", "@types/redis-parser": "^3.0.0", "@types/sinon": "^10.0.6", "@types/yallist": "^4.0.1", - "@typescript-eslint/eslint-plugin": "^5.8.1", - "@typescript-eslint/parser": "^5.8.1", - "eslint": "^8.5.0", + "@typescript-eslint/eslint-plugin": "^5.9.1", + "@typescript-eslint/parser": "^5.9.1", + "eslint": "^8.6.0", "nyc": "^15.1.0", - "release-it": "^14.11.8", + "release-it": "^14.12.1", "sinon": "^12.0.1", "source-map-support": "^0.5.21", "ts-node": "^10.4.0", @@ -6507,25 +6569,6 @@ "node": ">=12" } }, - "packages/graph": { - "name": "@node-redis/graph", - "version": "1.0.0", - "license": "MIT", - "devDependencies": { - "@istanbuljs/nyc-config-typescript": "^1.0.2", - "@node-redis/test-utils": "*", - "@types/node": "^17.0.1", - "nyc": "^15.1.0", - "release-it": "^14.11.8", - "source-map-support": "^0.5.21", - "ts-node": "^10.4.0", - "typedoc": "^0.22.10", - "typescript": "^4.5.4" - }, - "peerDependencies": { - "@node-redis/client": "^1.0.0" - } - }, "packages/json": { "name": "@node-redis/json", "version": "1.0.1", @@ -6533,9 +6576,9 @@ "devDependencies": { "@istanbuljs/nyc-config-typescript": "^1.0.2", "@node-redis/test-utils": "*", - "@types/node": "^17.0.5", + "@types/node": "^17.0.8", "nyc": "^15.1.0", - "release-it": "^14.11.8", + "release-it": "^14.12.1", "source-map-support": "^0.5.21", "ts-node": "^10.4.0", "typedoc": "^0.22.10", @@ -6554,7 +6597,7 @@ "@node-redis/test-utils": "*", "@types/node": "^17.0.5", "nyc": "^15.1.0", - "release-it": "^14.11.8", + "release-it": "^14.12.1", "source-map-support": "^0.5.21", "ts-node": "^10.4.0", "typedoc": "^0.22.10", @@ -6569,11 +6612,10 @@ "devDependencies": { "@istanbuljs/nyc-config-typescript": "^1.0.2", "@types/mocha": "^9.0.0", - "@types/node": "^17.0.5", + "@types/node": "^17.0.8", "@types/yargs": "^17.0.8", "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.4", @@ -6592,7 +6634,7 @@ "@node-redis/test-utils": "*", "@types/node": "^17.0.5", "nyc": "^15.1.0", - "release-it": "^14.11.8", + "release-it": "^14.12.1", "source-map-support": "^0.5.21", "ts-node": "^10.4.0", "typedoc": "^0.22.10", @@ -6605,35 +6647,35 @@ }, "dependencies": { "@babel/code-frame": { - "version": "7.16.0", - "resolved": "https://registry.npmjs.org/@babel/code-frame/-/code-frame-7.16.0.tgz", - "integrity": "sha512-IF4EOMEV+bfYwOmNxGzSnjR2EmQod7f1UXOpZM3l4i4o4QNwzjtJAu/HxdjHq0aYBvdqMuQEY1eg0nqW9ZPORA==", + "version": "7.16.7", + "resolved": "https://registry.npmjs.org/@babel/code-frame/-/code-frame-7.16.7.tgz", + "integrity": "sha512-iAXqUn8IIeBTNd72xsFlgaXHkMBMt6y4HJp1tIaK465CWLT/fG1aqB7ykr95gHHmlBdGbFeWWfyB4NJJ0nmeIg==", "dev": true, "requires": { - "@babel/highlight": "^7.16.0" + "@babel/highlight": "^7.16.7" } }, "@babel/compat-data": { - "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==", + "version": "7.16.8", + "resolved": "https://registry.npmjs.org/@babel/compat-data/-/compat-data-7.16.8.tgz", + "integrity": "sha512-m7OkX0IdKLKPpBlJtF561YJal5y/jyI5fNfWbPxh2D/nbzzGI4qRyrD8xO2jB24u7l+5I2a43scCG2IrfjC50Q==", "dev": true }, "@babel/core": { - "version": "7.16.5", - "resolved": "https://registry.npmjs.org/@babel/core/-/core-7.16.5.tgz", - "integrity": "sha512-wUcenlLzuWMZ9Zt8S0KmFwGlH6QKRh3vsm/dhDA3CHkiTA45YuG1XkHRcNRl73EFPXDp/d5kVOU0/y7x2w6OaQ==", - "dev": true, - "requires": { - "@babel/code-frame": "^7.16.0", - "@babel/generator": "^7.16.5", - "@babel/helper-compilation-targets": "^7.16.3", - "@babel/helper-module-transforms": "^7.16.5", - "@babel/helpers": "^7.16.5", - "@babel/parser": "^7.16.5", - "@babel/template": "^7.16.0", - "@babel/traverse": "^7.16.5", - "@babel/types": "^7.16.0", + "version": "7.16.7", + "resolved": "https://registry.npmjs.org/@babel/core/-/core-7.16.7.tgz", + "integrity": "sha512-aeLaqcqThRNZYmbMqtulsetOQZ/5gbR/dWruUCJcpas4Qoyy+QeagfDsPdMrqwsPRDNxJvBlRiZxxX7THO7qtA==", + "dev": true, + "requires": { + "@babel/code-frame": "^7.16.7", + "@babel/generator": "^7.16.7", + "@babel/helper-compilation-targets": "^7.16.7", + "@babel/helper-module-transforms": "^7.16.7", + "@babel/helpers": "^7.16.7", + "@babel/parser": "^7.16.7", + "@babel/template": "^7.16.7", + "@babel/traverse": "^7.16.7", + "@babel/types": "^7.16.7", "convert-source-map": "^1.7.0", "debug": "^4.1.0", "gensync": "^1.0.0-beta.2", @@ -6643,139 +6685,139 @@ } }, "@babel/generator": { - "version": "7.16.5", - "resolved": "https://registry.npmjs.org/@babel/generator/-/generator-7.16.5.tgz", - "integrity": "sha512-kIvCdjZqcdKqoDbVVdt5R99icaRtrtYhYK/xux5qiWCBmfdvEYMFZ68QCrpE5cbFM1JsuArUNs1ZkuKtTtUcZA==", + "version": "7.16.8", + "resolved": "https://registry.npmjs.org/@babel/generator/-/generator-7.16.8.tgz", + "integrity": "sha512-1ojZwE9+lOXzcWdWmO6TbUzDfqLD39CmEhN8+2cX9XkDo5yW1OpgfejfliysR2AWLpMamTiOiAp/mtroaymhpw==", "dev": true, "requires": { - "@babel/types": "^7.16.0", + "@babel/types": "^7.16.8", "jsesc": "^2.5.1", "source-map": "^0.5.0" } }, "@babel/helper-compilation-targets": { - "version": "7.16.3", - "resolved": "https://registry.npmjs.org/@babel/helper-compilation-targets/-/helper-compilation-targets-7.16.3.tgz", - "integrity": "sha512-vKsoSQAyBmxS35JUOOt+07cLc6Nk/2ljLIHwmq2/NM6hdioUaqEXq/S+nXvbvXbZkNDlWOymPanJGOc4CBjSJA==", + "version": "7.16.7", + "resolved": "https://registry.npmjs.org/@babel/helper-compilation-targets/-/helper-compilation-targets-7.16.7.tgz", + "integrity": "sha512-mGojBwIWcwGD6rfqgRXVlVYmPAv7eOpIemUG3dGnDdCY4Pae70ROij3XmfrH6Fa1h1aiDylpglbZyktfzyo/hA==", "dev": true, "requires": { - "@babel/compat-data": "^7.16.0", - "@babel/helper-validator-option": "^7.14.5", + "@babel/compat-data": "^7.16.4", + "@babel/helper-validator-option": "^7.16.7", "browserslist": "^4.17.5", "semver": "^6.3.0" } }, "@babel/helper-environment-visitor": { - "version": "7.16.5", - "resolved": "https://registry.npmjs.org/@babel/helper-environment-visitor/-/helper-environment-visitor-7.16.5.tgz", - "integrity": "sha512-ODQyc5AnxmZWm/R2W7fzhamOk1ey8gSguo5SGvF0zcB3uUzRpTRmM/jmLSm9bDMyPlvbyJ+PwPEK0BWIoZ9wjg==", + "version": "7.16.7", + "resolved": "https://registry.npmjs.org/@babel/helper-environment-visitor/-/helper-environment-visitor-7.16.7.tgz", + "integrity": "sha512-SLLb0AAn6PkUeAfKJCCOl9e1R53pQlGAfc4y4XuMRZfqeMYLE0dM1LMhqbGAlGQY0lfw5/ohoYWAe9V1yibRag==", "dev": true, "requires": { - "@babel/types": "^7.16.0" + "@babel/types": "^7.16.7" } }, "@babel/helper-function-name": { - "version": "7.16.0", - "resolved": "https://registry.npmjs.org/@babel/helper-function-name/-/helper-function-name-7.16.0.tgz", - "integrity": "sha512-BZh4mEk1xi2h4HFjWUXRQX5AEx4rvaZxHgax9gcjdLWdkjsY7MKt5p0otjsg5noXw+pB+clMCjw+aEVYADMjog==", + "version": "7.16.7", + "resolved": "https://registry.npmjs.org/@babel/helper-function-name/-/helper-function-name-7.16.7.tgz", + "integrity": "sha512-QfDfEnIUyyBSR3HtrtGECuZ6DAyCkYFp7GHl75vFtTnn6pjKeK0T1DB5lLkFvBea8MdaiUABx3osbgLyInoejA==", "dev": true, "requires": { - "@babel/helper-get-function-arity": "^7.16.0", - "@babel/template": "^7.16.0", - "@babel/types": "^7.16.0" + "@babel/helper-get-function-arity": "^7.16.7", + "@babel/template": "^7.16.7", + "@babel/types": "^7.16.7" } }, "@babel/helper-get-function-arity": { - "version": "7.16.0", - "resolved": "https://registry.npmjs.org/@babel/helper-get-function-arity/-/helper-get-function-arity-7.16.0.tgz", - "integrity": "sha512-ASCquNcywC1NkYh/z7Cgp3w31YW8aojjYIlNg4VeJiHkqyP4AzIvr4qx7pYDb4/s8YcsZWqqOSxgkvjUz1kpDQ==", + "version": "7.16.7", + "resolved": "https://registry.npmjs.org/@babel/helper-get-function-arity/-/helper-get-function-arity-7.16.7.tgz", + "integrity": "sha512-flc+RLSOBXzNzVhcLu6ujeHUrD6tANAOU5ojrRx/as+tbzf8+stUCj7+IfRRoAbEZqj/ahXEMsjhOhgeZsrnTw==", "dev": true, "requires": { - "@babel/types": "^7.16.0" + "@babel/types": "^7.16.7" } }, "@babel/helper-hoist-variables": { - "version": "7.16.0", - "resolved": "https://registry.npmjs.org/@babel/helper-hoist-variables/-/helper-hoist-variables-7.16.0.tgz", - "integrity": "sha512-1AZlpazjUR0EQZQv3sgRNfM9mEVWPK3M6vlalczA+EECcPz3XPh6VplbErL5UoMpChhSck5wAJHthlj1bYpcmg==", + "version": "7.16.7", + "resolved": "https://registry.npmjs.org/@babel/helper-hoist-variables/-/helper-hoist-variables-7.16.7.tgz", + "integrity": "sha512-m04d/0Op34H5v7pbZw6pSKP7weA6lsMvfiIAMeIvkY/R4xQtBSMFEigu9QTZ2qB/9l22vsxtM8a+Q8CzD255fg==", "dev": true, "requires": { - "@babel/types": "^7.16.0" + "@babel/types": "^7.16.7" } }, "@babel/helper-module-imports": { - "version": "7.16.0", - "resolved": "https://registry.npmjs.org/@babel/helper-module-imports/-/helper-module-imports-7.16.0.tgz", - "integrity": "sha512-kkH7sWzKPq0xt3H1n+ghb4xEMP8k0U7XV3kkB+ZGy69kDk2ySFW1qPi06sjKzFY3t1j6XbJSqr4mF9L7CYVyhg==", + "version": "7.16.7", + "resolved": "https://registry.npmjs.org/@babel/helper-module-imports/-/helper-module-imports-7.16.7.tgz", + "integrity": "sha512-LVtS6TqjJHFc+nYeITRo6VLXve70xmq7wPhWTqDJusJEgGmkAACWwMiTNrvfoQo6hEhFwAIixNkvB0jPXDL8Wg==", "dev": true, "requires": { - "@babel/types": "^7.16.0" + "@babel/types": "^7.16.7" } }, "@babel/helper-module-transforms": { - "version": "7.16.5", - "resolved": "https://registry.npmjs.org/@babel/helper-module-transforms/-/helper-module-transforms-7.16.5.tgz", - "integrity": "sha512-CkvMxgV4ZyyioElFwcuWnDCcNIeyqTkCm9BxXZi73RR1ozqlpboqsbGUNvRTflgZtFbbJ1v5Emvm+lkjMYY/LQ==", + "version": "7.16.7", + "resolved": "https://registry.npmjs.org/@babel/helper-module-transforms/-/helper-module-transforms-7.16.7.tgz", + "integrity": "sha512-gaqtLDxJEFCeQbYp9aLAefjhkKdjKcdh6DB7jniIGU3Pz52WAmP268zK0VgPz9hUNkMSYeH976K2/Y6yPadpng==", "dev": true, "requires": { - "@babel/helper-environment-visitor": "^7.16.5", - "@babel/helper-module-imports": "^7.16.0", - "@babel/helper-simple-access": "^7.16.0", - "@babel/helper-split-export-declaration": "^7.16.0", - "@babel/helper-validator-identifier": "^7.15.7", - "@babel/template": "^7.16.0", - "@babel/traverse": "^7.16.5", - "@babel/types": "^7.16.0" + "@babel/helper-environment-visitor": "^7.16.7", + "@babel/helper-module-imports": "^7.16.7", + "@babel/helper-simple-access": "^7.16.7", + "@babel/helper-split-export-declaration": "^7.16.7", + "@babel/helper-validator-identifier": "^7.16.7", + "@babel/template": "^7.16.7", + "@babel/traverse": "^7.16.7", + "@babel/types": "^7.16.7" } }, "@babel/helper-simple-access": { - "version": "7.16.0", - "resolved": "https://registry.npmjs.org/@babel/helper-simple-access/-/helper-simple-access-7.16.0.tgz", - "integrity": "sha512-o1rjBT/gppAqKsYfUdfHq5Rk03lMQrkPHG1OWzHWpLgVXRH4HnMM9Et9CVdIqwkCQlobnGHEJMsgWP/jE1zUiw==", + "version": "7.16.7", + "resolved": "https://registry.npmjs.org/@babel/helper-simple-access/-/helper-simple-access-7.16.7.tgz", + "integrity": "sha512-ZIzHVyoeLMvXMN/vok/a4LWRy8G2v205mNP0XOuf9XRLyX5/u9CnVulUtDgUTama3lT+bf/UqucuZjqiGuTS1g==", "dev": true, "requires": { - "@babel/types": "^7.16.0" + "@babel/types": "^7.16.7" } }, "@babel/helper-split-export-declaration": { - "version": "7.16.0", - "resolved": "https://registry.npmjs.org/@babel/helper-split-export-declaration/-/helper-split-export-declaration-7.16.0.tgz", - "integrity": "sha512-0YMMRpuDFNGTHNRiiqJX19GjNXA4H0E8jZ2ibccfSxaCogbm3am5WN/2nQNj0YnQwGWM1J06GOcQ2qnh3+0paw==", + "version": "7.16.7", + "resolved": "https://registry.npmjs.org/@babel/helper-split-export-declaration/-/helper-split-export-declaration-7.16.7.tgz", + "integrity": "sha512-xbWoy/PFoxSWazIToT9Sif+jJTlrMcndIsaOKvTA6u7QEo7ilkRZpjew18/W3c7nm8fXdUDXh02VXTbZ0pGDNw==", "dev": true, "requires": { - "@babel/types": "^7.16.0" + "@babel/types": "^7.16.7" } }, "@babel/helper-validator-identifier": { - "version": "7.15.7", - "resolved": "https://registry.npmjs.org/@babel/helper-validator-identifier/-/helper-validator-identifier-7.15.7.tgz", - "integrity": "sha512-K4JvCtQqad9OY2+yTU8w+E82ywk/fe+ELNlt1G8z3bVGlZfn/hOcQQsUhGhW/N+tb3fxK800wLtKOE/aM0m72w==", + "version": "7.16.7", + "resolved": "https://registry.npmjs.org/@babel/helper-validator-identifier/-/helper-validator-identifier-7.16.7.tgz", + "integrity": "sha512-hsEnFemeiW4D08A5gUAZxLBTXpZ39P+a+DGDsHw1yxqyQ/jzFEnxf5uTEGp+3bzAbNOxU1paTgYS4ECU/IgfDw==", "dev": true }, "@babel/helper-validator-option": { - "version": "7.14.5", - "resolved": "https://registry.npmjs.org/@babel/helper-validator-option/-/helper-validator-option-7.14.5.tgz", - "integrity": "sha512-OX8D5eeX4XwcroVW45NMvoYaIuFI+GQpA2a8Gi+X/U/cDUIRsV37qQfF905F0htTRCREQIB4KqPeaveRJUl3Ow==", + "version": "7.16.7", + "resolved": "https://registry.npmjs.org/@babel/helper-validator-option/-/helper-validator-option-7.16.7.tgz", + "integrity": "sha512-TRtenOuRUVo9oIQGPC5G9DgK4743cdxvtOw0weQNpZXaS16SCBi5MNjZF8vba3ETURjZpTbVn7Vvcf2eAwFozQ==", "dev": true }, "@babel/helpers": { - "version": "7.16.5", - "resolved": "https://registry.npmjs.org/@babel/helpers/-/helpers-7.16.5.tgz", - "integrity": "sha512-TLgi6Lh71vvMZGEkFuIxzaPsyeYCHQ5jJOOX1f0xXn0uciFuE8cEk0wyBquMcCxBXZ5BJhE2aUB7pnWTD150Tw==", + "version": "7.16.7", + "resolved": "https://registry.npmjs.org/@babel/helpers/-/helpers-7.16.7.tgz", + "integrity": "sha512-9ZDoqtfY7AuEOt3cxchfii6C7GDyyMBffktR5B2jvWv8u2+efwvpnVKXMWzNehqy68tKgAfSwfdw/lWpthS2bw==", "dev": true, "requires": { - "@babel/template": "^7.16.0", - "@babel/traverse": "^7.16.5", - "@babel/types": "^7.16.0" + "@babel/template": "^7.16.7", + "@babel/traverse": "^7.16.7", + "@babel/types": "^7.16.7" } }, "@babel/highlight": { - "version": "7.16.0", - "resolved": "https://registry.npmjs.org/@babel/highlight/-/highlight-7.16.0.tgz", - "integrity": "sha512-t8MH41kUQylBtu2+4IQA3atqevA2lRgqA2wyVB/YiWmsDSuylZZuXOUy9ric30hfzauEFfdsuk/eXTRrGrfd0g==", + "version": "7.16.7", + "resolved": "https://registry.npmjs.org/@babel/highlight/-/highlight-7.16.7.tgz", + "integrity": "sha512-aKpPMfLvGO3Q97V0qhw/V2SWNWlwfJknuwAunU7wZLSfrM4xTBvg7E5opUVi1kJTBKihE38CPg4nBiqX83PWYw==", "dev": true, "requires": { - "@babel/helper-validator-identifier": "^7.15.7", + "@babel/helper-validator-identifier": "^7.16.7", "chalk": "^2.0.0", "js-tokens": "^4.0.0" }, @@ -6833,36 +6875,36 @@ } }, "@babel/parser": { - "version": "7.16.6", - "resolved": "https://registry.npmjs.org/@babel/parser/-/parser-7.16.6.tgz", - "integrity": "sha512-Gr86ujcNuPDnNOY8mi383Hvi8IYrJVJYuf3XcuBM/Dgd+bINn/7tHqsj+tKkoreMbmGsFLsltI/JJd8fOFWGDQ==", + "version": "7.16.8", + "resolved": "https://registry.npmjs.org/@babel/parser/-/parser-7.16.8.tgz", + "integrity": "sha512-i7jDUfrVBWc+7OKcBzEe5n7fbv3i2fWtxKzzCvOjnzSxMfWMigAhtfJ7qzZNGFNMsCCd67+uz553dYKWXPvCKw==", "dev": true }, "@babel/template": { - "version": "7.16.0", - "resolved": "https://registry.npmjs.org/@babel/template/-/template-7.16.0.tgz", - "integrity": "sha512-MnZdpFD/ZdYhXwiunMqqgyZyucaYsbL0IrjoGjaVhGilz+x8YB++kRfygSOIj1yOtWKPlx7NBp+9I1RQSgsd5A==", + "version": "7.16.7", + "resolved": "https://registry.npmjs.org/@babel/template/-/template-7.16.7.tgz", + "integrity": "sha512-I8j/x8kHUrbYRTUxXrrMbfCa7jxkE7tZre39x3kjr9hvI82cK1FfqLygotcWN5kdPGWcLdWMHpSBavse5tWw3w==", "dev": true, "requires": { - "@babel/code-frame": "^7.16.0", - "@babel/parser": "^7.16.0", - "@babel/types": "^7.16.0" + "@babel/code-frame": "^7.16.7", + "@babel/parser": "^7.16.7", + "@babel/types": "^7.16.7" } }, "@babel/traverse": { - "version": "7.16.5", - "resolved": "https://registry.npmjs.org/@babel/traverse/-/traverse-7.16.5.tgz", - "integrity": "sha512-FOCODAzqUMROikDYLYxl4nmwiLlu85rNqBML/A5hKRVXG2LV8d0iMqgPzdYTcIpjZEBB7D6UDU9vxRZiriASdQ==", - "dev": true, - "requires": { - "@babel/code-frame": "^7.16.0", - "@babel/generator": "^7.16.5", - "@babel/helper-environment-visitor": "^7.16.5", - "@babel/helper-function-name": "^7.16.0", - "@babel/helper-hoist-variables": "^7.16.0", - "@babel/helper-split-export-declaration": "^7.16.0", - "@babel/parser": "^7.16.5", - "@babel/types": "^7.16.0", + "version": "7.16.8", + "resolved": "https://registry.npmjs.org/@babel/traverse/-/traverse-7.16.8.tgz", + "integrity": "sha512-xe+H7JlvKsDQwXRsBhSnq1/+9c+LlQcCK3Tn/l5sbx02HYns/cn7ibp9+RV1sIUqu7hKg91NWsgHurO9dowITQ==", + "dev": true, + "requires": { + "@babel/code-frame": "^7.16.7", + "@babel/generator": "^7.16.8", + "@babel/helper-environment-visitor": "^7.16.7", + "@babel/helper-function-name": "^7.16.7", + "@babel/helper-hoist-variables": "^7.16.7", + "@babel/helper-split-export-declaration": "^7.16.7", + "@babel/parser": "^7.16.8", + "@babel/types": "^7.16.8", "debug": "^4.1.0", "globals": "^11.1.0" }, @@ -6876,12 +6918,12 @@ } }, "@babel/types": { - "version": "7.16.0", - "resolved": "https://registry.npmjs.org/@babel/types/-/types-7.16.0.tgz", - "integrity": "sha512-PJgg/k3SdLsGb3hhisFvtLOw5ts113klrpLuIPtCJIU+BB24fqq6lf8RWqKJEjzqXR9AEH1rIb5XTqwBHB+kQg==", + "version": "7.16.8", + "resolved": "https://registry.npmjs.org/@babel/types/-/types-7.16.8.tgz", + "integrity": "sha512-smN2DQc5s4M7fntyjGtyIPbRJv6wW4rU/94fmYJ7PKQuZkC0qGMHXJbg6sNGt12JmVr4k5YaptI/XtiLJBnmIg==", "dev": true, "requires": { - "@babel/helper-validator-identifier": "^7.15.7", + "@babel/helper-validator-identifier": "^7.16.7", "to-fast-properties": "^2.0.0" } }, @@ -7020,9 +7062,9 @@ "requires": { "@istanbuljs/nyc-config-typescript": "^1.0.2", "@node-redis/test-utils": "*", - "@types/node": "^17.0.5", + "@types/node": "^17.0.8", "nyc": "^15.1.0", - "release-it": "^14.11.8", + "release-it": "^14.12.1", "source-map-support": "^0.5.21", "ts-node": "^10.4.0", "typedoc": "^0.22.10", @@ -7034,18 +7076,18 @@ "requires": { "@istanbuljs/nyc-config-typescript": "^1.0.2", "@node-redis/test-utils": "*", - "@types/node": "^17.0.5", + "@types/node": "^17.0.8", "@types/redis-parser": "^3.0.0", "@types/sinon": "^10.0.6", "@types/yallist": "^4.0.1", - "@typescript-eslint/eslint-plugin": "^5.8.1", - "@typescript-eslint/parser": "^5.8.1", + "@typescript-eslint/eslint-plugin": "^5.9.1", + "@typescript-eslint/parser": "^5.9.1", "cluster-key-slot": "1.1.0", - "eslint": "^8.5.0", + "eslint": "^8.6.0", "generic-pool": "3.8.2", "nyc": "^15.1.0", "redis-parser": "3.0.0", - "release-it": "^14.11.8", + "release-it": "^14.12.1", "sinon": "^12.0.1", "source-map-support": "^0.5.21", "ts-node": "^10.4.0", @@ -7054,28 +7096,14 @@ "yallist": "4.0.0" } }, - "@node-redis/graph": { - "version": "file:packages/graph", - "requires": { - "@istanbuljs/nyc-config-typescript": "^1.0.2", - "@node-redis/test-utils": "*", - "@types/node": "^17.0.1", - "nyc": "^15.1.0", - "release-it": "^14.11.8", - "source-map-support": "^0.5.21", - "ts-node": "^10.4.0", - "typedoc": "^0.22.10", - "typescript": "^4.5.4" - } - }, "@node-redis/json": { "version": "file:packages/json", "requires": { "@istanbuljs/nyc-config-typescript": "^1.0.2", "@node-redis/test-utils": "*", - "@types/node": "^17.0.5", + "@types/node": "^17.0.8", "nyc": "^15.1.0", - "release-it": "^14.11.8", + "release-it": "^14.12.1", "source-map-support": "^0.5.21", "ts-node": "^10.4.0", "typedoc": "^0.22.10", @@ -7089,7 +7117,7 @@ "@node-redis/test-utils": "*", "@types/node": "^17.0.5", "nyc": "^15.1.0", - "release-it": "^14.11.8", + "release-it": "^14.12.1", "source-map-support": "^0.5.21", "ts-node": "^10.4.0", "typedoc": "^0.22.10", @@ -7101,11 +7129,10 @@ "requires": { "@istanbuljs/nyc-config-typescript": "^1.0.2", "@types/mocha": "^9.0.0", - "@types/node": "^17.0.5", + "@types/node": "^17.0.8", "@types/yargs": "^17.0.8", "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.4", @@ -7119,7 +7146,7 @@ "@node-redis/test-utils": "*", "@types/node": "^17.0.5", "nyc": "^15.1.0", - "release-it": "^14.11.8", + "release-it": "^14.12.1", "source-map-support": "^0.5.21", "ts-node": "^10.4.0", "typedoc": "^0.22.10", @@ -7277,9 +7304,9 @@ } }, "@sindresorhus/is": { - "version": "4.2.0", - "resolved": "https://registry.npmjs.org/@sindresorhus/is/-/is-4.2.0.tgz", - "integrity": "sha512-VkE3KLBmJwcCaVARtQpfuKcKv8gcBmUubrfHGF84dXuuW6jgsRYxPtzcIhPyK9WAPpRt2/xY6zkD9MnRaJzSyw==", + "version": "4.2.1", + "resolved": "https://registry.npmjs.org/@sindresorhus/is/-/is-4.2.1.tgz", + "integrity": "sha512-BrzrgtaqEre0qfvI8sMTaEvx+bayuhPmfe2rfeUGPPHYr/PLxCOqkOe4TQTDPb+qcqgNcsAtXV/Ew74mcDIE8w==", "dev": true }, "@sinonjs/commons": { @@ -7390,9 +7417,9 @@ "dev": true }, "@types/node": { - "version": "17.0.5", - "resolved": "https://registry.npmjs.org/@types/node/-/node-17.0.5.tgz", - "integrity": "sha512-w3mrvNXLeDYV1GKTZorGJQivK6XLCoGwpnyJFbJVK/aTBQUxOCaa/GlFAAN3OTDFcb7h5tiFG+YXCO2By+riZw==", + "version": "17.0.8", + "resolved": "https://registry.npmjs.org/@types/node/-/node-17.0.8.tgz", + "integrity": "sha512-YofkM6fGv4gDJq78g4j0mMuGMkZVxZDgtU0JRdx6FgiJDG+0fY0GKVolOV8WqVmEhLCXkQRjwDdKyPxJp/uucg==", "dev": true }, "@types/parse-json": { @@ -7457,13 +7484,14 @@ "dev": true }, "@typescript-eslint/eslint-plugin": { - "version": "5.8.1", - "resolved": "https://registry.npmjs.org/@typescript-eslint/eslint-plugin/-/eslint-plugin-5.8.1.tgz", - "integrity": "sha512-wTZ5oEKrKj/8/366qTM366zqhIKAp6NCMweoRONtfuC07OAU9nVI2GZZdqQ1qD30WAAtcPdkH+npDwtRFdp4Rw==", + "version": "5.9.1", + "resolved": "https://registry.npmjs.org/@typescript-eslint/eslint-plugin/-/eslint-plugin-5.9.1.tgz", + "integrity": "sha512-Xv9tkFlyD4MQGpJgTo6wqDqGvHIRmRgah/2Sjz1PUnJTawjHWIwBivUE9x0QtU2WVii9baYgavo/bHjrZJkqTw==", "dev": true, "requires": { - "@typescript-eslint/experimental-utils": "5.8.1", - "@typescript-eslint/scope-manager": "5.8.1", + "@typescript-eslint/experimental-utils": "5.9.1", + "@typescript-eslint/scope-manager": "5.9.1", + "@typescript-eslint/type-utils": "5.9.1", "debug": "^4.3.2", "functional-red-black-tree": "^1.0.1", "ignore": "^5.1.8", @@ -7484,55 +7512,66 @@ } }, "@typescript-eslint/experimental-utils": { - "version": "5.8.1", - "resolved": "https://registry.npmjs.org/@typescript-eslint/experimental-utils/-/experimental-utils-5.8.1.tgz", - "integrity": "sha512-fbodVnjIDU4JpeXWRDsG5IfIjYBxEvs8EBO8W1+YVdtrc2B9ppfof5sZhVEDOtgTfFHnYQJDI8+qdqLYO4ceww==", + "version": "5.9.1", + "resolved": "https://registry.npmjs.org/@typescript-eslint/experimental-utils/-/experimental-utils-5.9.1.tgz", + "integrity": "sha512-cb1Njyss0mLL9kLXgS/eEY53SZQ9sT519wpX3i+U457l2UXRDuo87hgKfgRazmu9/tQb0x2sr3Y0yrU+Zz0y+w==", "dev": true, "requires": { "@types/json-schema": "^7.0.9", - "@typescript-eslint/scope-manager": "5.8.1", - "@typescript-eslint/types": "5.8.1", - "@typescript-eslint/typescript-estree": "5.8.1", + "@typescript-eslint/scope-manager": "5.9.1", + "@typescript-eslint/types": "5.9.1", + "@typescript-eslint/typescript-estree": "5.9.1", "eslint-scope": "^5.1.1", "eslint-utils": "^3.0.0" } }, "@typescript-eslint/parser": { - "version": "5.8.1", - "resolved": "https://registry.npmjs.org/@typescript-eslint/parser/-/parser-5.8.1.tgz", - "integrity": "sha512-K1giKHAjHuyB421SoXMXFHHVI4NdNY603uKw92++D3qyxSeYvC10CBJ/GE5Thpo4WTUvu1mmJI2/FFkz38F2Gw==", + "version": "5.9.1", + "resolved": "https://registry.npmjs.org/@typescript-eslint/parser/-/parser-5.9.1.tgz", + "integrity": "sha512-PLYO0AmwD6s6n0ZQB5kqPgfvh73p0+VqopQQLuNfi7Lm0EpfKyDalchpVwkE+81k5HeiRrTV/9w1aNHzjD7C4g==", "dev": true, "requires": { - "@typescript-eslint/scope-manager": "5.8.1", - "@typescript-eslint/types": "5.8.1", - "@typescript-eslint/typescript-estree": "5.8.1", + "@typescript-eslint/scope-manager": "5.9.1", + "@typescript-eslint/types": "5.9.1", + "@typescript-eslint/typescript-estree": "5.9.1", "debug": "^4.3.2" } }, "@typescript-eslint/scope-manager": { - "version": "5.8.1", - "resolved": "https://registry.npmjs.org/@typescript-eslint/scope-manager/-/scope-manager-5.8.1.tgz", - "integrity": "sha512-DGxJkNyYruFH3NIZc3PwrzwOQAg7vvgsHsHCILOLvUpupgkwDZdNq/cXU3BjF4LNrCsVg0qxEyWasys5AiJ85Q==", + "version": "5.9.1", + "resolved": "https://registry.npmjs.org/@typescript-eslint/scope-manager/-/scope-manager-5.9.1.tgz", + "integrity": "sha512-8BwvWkho3B/UOtzRyW07ffJXPaLSUKFBjpq8aqsRvu6HdEuzCY57+ffT7QoV4QXJXWSU1+7g3wE4AlgImmQ9pQ==", "dev": true, "requires": { - "@typescript-eslint/types": "5.8.1", - "@typescript-eslint/visitor-keys": "5.8.1" + "@typescript-eslint/types": "5.9.1", + "@typescript-eslint/visitor-keys": "5.9.1" + } + }, + "@typescript-eslint/type-utils": { + "version": "5.9.1", + "resolved": "https://registry.npmjs.org/@typescript-eslint/type-utils/-/type-utils-5.9.1.tgz", + "integrity": "sha512-tRSpdBnPRssjlUh35rE9ug5HrUvaB9ntREy7gPXXKwmIx61TNN7+l5YKgi1hMKxo5NvqZCfYhA5FvyuJG6X6vg==", + "dev": true, + "requires": { + "@typescript-eslint/experimental-utils": "5.9.1", + "debug": "^4.3.2", + "tsutils": "^3.21.0" } }, "@typescript-eslint/types": { - "version": "5.8.1", - "resolved": "https://registry.npmjs.org/@typescript-eslint/types/-/types-5.8.1.tgz", - "integrity": "sha512-L/FlWCCgnjKOLefdok90/pqInkomLnAcF9UAzNr+DSqMC3IffzumHTQTrINXhP1gVp9zlHiYYjvozVZDPleLcA==", + "version": "5.9.1", + "resolved": "https://registry.npmjs.org/@typescript-eslint/types/-/types-5.9.1.tgz", + "integrity": "sha512-SsWegWudWpkZCwwYcKoDwuAjoZXnM1y2EbEerTHho19Hmm+bQ56QG4L4jrtCu0bI5STaRTvRTZmjprWlTw/5NQ==", "dev": true }, "@typescript-eslint/typescript-estree": { - "version": "5.8.1", - "resolved": "https://registry.npmjs.org/@typescript-eslint/typescript-estree/-/typescript-estree-5.8.1.tgz", - "integrity": "sha512-26lQ8l8tTbG7ri7xEcCFT9ijU5Fk+sx/KRRyyzCv7MQ+rZZlqiDPtMKWLC8P7o+dtCnby4c+OlxuX1tp8WfafQ==", + "version": "5.9.1", + "resolved": "https://registry.npmjs.org/@typescript-eslint/typescript-estree/-/typescript-estree-5.9.1.tgz", + "integrity": "sha512-gL1sP6A/KG0HwrahVXI9fZyeVTxEYV//6PmcOn1tD0rw8VhUWYeZeuWHwwhnewnvEMcHjhnJLOBhA9rK4vmb8A==", "dev": true, "requires": { - "@typescript-eslint/types": "5.8.1", - "@typescript-eslint/visitor-keys": "5.8.1", + "@typescript-eslint/types": "5.9.1", + "@typescript-eslint/visitor-keys": "5.9.1", "debug": "^4.3.2", "globby": "^11.0.4", "is-glob": "^4.0.3", @@ -7547,16 +7586,16 @@ "dev": true }, "globby": { - "version": "11.0.4", - "resolved": "https://registry.npmjs.org/globby/-/globby-11.0.4.tgz", - "integrity": "sha512-9O4MVG9ioZJ08ffbcyVYyLOJLk5JQ688pJ4eMGLpdWLHq/Wr1D9BlriLQyL0E+jbkuePVZXYFj47QM/v093wHg==", + "version": "11.1.0", + "resolved": "https://registry.npmjs.org/globby/-/globby-11.1.0.tgz", + "integrity": "sha512-jhIXaOzy1sb8IyocaruWSn1TjmnBVs8Ayhcy83rmxNJ8q2uWKCAj3CnJY+KpGSXCueAPc0i05kVvVKtP1t9S3g==", "dev": true, "requires": { "array-union": "^2.1.0", "dir-glob": "^3.0.1", - "fast-glob": "^3.1.1", - "ignore": "^5.1.4", - "merge2": "^1.3.0", + "fast-glob": "^3.2.9", + "ignore": "^5.2.0", + "merge2": "^1.4.1", "slash": "^3.0.0" } }, @@ -7572,12 +7611,12 @@ } }, "@typescript-eslint/visitor-keys": { - "version": "5.8.1", - "resolved": "https://registry.npmjs.org/@typescript-eslint/visitor-keys/-/visitor-keys-5.8.1.tgz", - "integrity": "sha512-SWgiWIwocK6NralrJarPZlWdr0hZnj5GXHIgfdm8hNkyKvpeQuFyLP6YjSIe9kf3YBIfU6OHSZLYkQ+smZwtNg==", + "version": "5.9.1", + "resolved": "https://registry.npmjs.org/@typescript-eslint/visitor-keys/-/visitor-keys-5.9.1.tgz", + "integrity": "sha512-Xh37pNz9e9ryW4TVdwiFzmr4hloty8cFj8GTWMXh3Z8swGwyQWeCcNgF0hm6t09iZd6eiZmIf4zHedQVP6TVtg==", "dev": true, "requires": { - "@typescript-eslint/types": "5.8.1", + "@typescript-eslint/types": "5.9.1", "eslint-visitor-keys": "^3.0.0" } }, @@ -7917,15 +7956,15 @@ "dev": true }, "camelcase": { - "version": "6.2.1", - "resolved": "https://registry.npmjs.org/camelcase/-/camelcase-6.2.1.tgz", - "integrity": "sha512-tVI4q5jjFV5CavAU8DXfza/TJcZutVKo/5Foskmsqcm0MsL91moHvwiGNnqaa2o6PF/7yT5ikDRcVcl8Rj6LCA==", + "version": "6.3.0", + "resolved": "https://registry.npmjs.org/camelcase/-/camelcase-6.3.0.tgz", + "integrity": "sha512-Gmy6FhYlCY7uOElZUSbxo2UCDH8owEk996gkbrpsgGtrJLM3J7jGxl9Ic7Qwwj4ivOE5AWZWRMecDdF7hqGjFA==", "dev": true }, "caniuse-lite": { - "version": "1.0.30001294", - "resolved": "https://registry.npmjs.org/caniuse-lite/-/caniuse-lite-1.0.30001294.tgz", - "integrity": "sha512-LiMlrs1nSKZ8qkNhpUf5KD0Al1KCBE3zaT7OLOwEkagXMEDij98SiOovn9wxVGQpklk9vVC/pUSqgYmkmKOS8g==", + "version": "1.0.30001299", + "resolved": "https://registry.npmjs.org/caniuse-lite/-/caniuse-lite-1.0.30001299.tgz", + "integrity": "sha512-iujN4+x7QzqA2NCSrS5VUy+4gLmRd4xv6vbBBsmfVqTx8bLAD8097euLqQgKxSVLvxjSDcvF1T/i9ocgnUFexw==", "dev": true }, "chalk": { @@ -8140,9 +8179,9 @@ "dev": true }, "debug": { - "version": "4.3.2", - "resolved": "https://registry.npmjs.org/debug/-/debug-4.3.2.tgz", - "integrity": "sha512-mOp8wKcvj7XxC78zLgw/ZA+6TSgkoE2C/ienthhRD298T7UNwAg9diBpLRxC0mOezLl4B0xV7M0cCO6P/O0Xhw==", + "version": "4.3.3", + "resolved": "https://registry.npmjs.org/debug/-/debug-4.3.3.tgz", + "integrity": "sha512-/zxw5+vh1Tfv+4Qn7a5nsbcJKPaSvCDhojn6FEl9vupwK2VCSDtEiEtqr8DFtzYFOdz63LBkxec7DYuc2jon6Q==", "dev": true, "requires": { "ms": "2.1.2" @@ -8275,9 +8314,9 @@ "dev": true }, "electron-to-chromium": { - "version": "1.4.30", - "resolved": "https://registry.npmjs.org/electron-to-chromium/-/electron-to-chromium-1.4.30.tgz", - "integrity": "sha512-609z9sIMxDHg+TcR/VB3MXwH+uwtrYyeAwWc/orhnr90ixs6WVGSrt85CDLGUdNnLqCA7liv426V20EecjvflQ==", + "version": "1.4.44", + "resolved": "https://registry.npmjs.org/electron-to-chromium/-/electron-to-chromium-1.4.44.tgz", + "integrity": "sha512-tHGWiUUmY7GABK8+DNcr474cnZDTzD8x1736SlDosVH8+/vRJeqfaIBAEHFtMjddz/0T4rKKYsxEc8BwQRdBpw==", "dev": true }, "email-addresses": { @@ -8344,9 +8383,9 @@ "dev": true }, "eslint": { - "version": "8.5.0", - "resolved": "https://registry.npmjs.org/eslint/-/eslint-8.5.0.tgz", - "integrity": "sha512-tVGSkgNbOfiHyVte8bCM8OmX+xG9PzVG/B4UCF60zx7j61WIVY/AqJECDgpLD4DbbESD0e174gOg3ZlrX15GDg==", + "version": "8.6.0", + "resolved": "https://registry.npmjs.org/eslint/-/eslint-8.6.0.tgz", + "integrity": "sha512-UvxdOJ7mXFlw7iuHZA4jmzPaUqIw54mZrv+XPYKNbKdLR0et4rf60lIZUU9kiNtnzzMzGWxMV+tQ7uG7JG8DPw==", "dev": true, "requires": { "@eslint/eslintrc": "^1.0.5", @@ -8361,7 +8400,7 @@ "eslint-scope": "^7.1.0", "eslint-utils": "^3.0.0", "eslint-visitor-keys": "^3.1.0", - "espree": "^9.2.0", + "espree": "^9.3.0", "esquery": "^1.4.0", "esutils": "^2.0.2", "fast-deep-equal": "^3.1.3", @@ -8477,12 +8516,12 @@ "dev": true }, "espree": { - "version": "9.2.0", - "resolved": "https://registry.npmjs.org/espree/-/espree-9.2.0.tgz", - "integrity": "sha512-oP3utRkynpZWF/F2x/HZJ+AGtnIclaR7z1pYPxy7NYM2fSO6LgK/Rkny8anRSPK/VwEA1eqm2squui0T7ZMOBg==", + "version": "9.3.0", + "resolved": "https://registry.npmjs.org/espree/-/espree-9.3.0.tgz", + "integrity": "sha512-d/5nCsb0JcqsSEeQzFZ8DH1RmxPcglRWh24EFTlUEmCKoehXGdpsx0RkHDubqUI8LSAIKMQp4r9SzQ3n+sm4HQ==", "dev": true, "requires": { - "acorn": "^8.6.0", + "acorn": "^8.7.0", "acorn-jsx": "^5.3.1", "eslint-visitor-keys": "^3.1.0" } @@ -8574,9 +8613,9 @@ "dev": true }, "fast-glob": { - "version": "3.2.7", - "resolved": "https://registry.npmjs.org/fast-glob/-/fast-glob-3.2.7.tgz", - "integrity": "sha512-rYGMRwip6lUMvYD3BTScMwT1HtAs2d71SMv66Vrxs0IekGZEjhM0pcMfjQPnknBt2zeCwQMEupiN02ZP4DiT1Q==", + "version": "3.2.10", + "resolved": "https://registry.npmjs.org/fast-glob/-/fast-glob-3.2.10.tgz", + "integrity": "sha512-s9nFhFnvR63wls6/kM88kQqDhMu0AfdjqouE2l5GVQPbqLgyFjjU5ry/r2yKsJxpb9Py1EYNqieFrmMaX4v++A==", "dev": true, "requires": { "@nodelib/fs.stat": "^2.0.2", @@ -8919,9 +8958,9 @@ } }, "graceful-fs": { - "version": "4.2.8", - "resolved": "https://registry.npmjs.org/graceful-fs/-/graceful-fs-4.2.8.tgz", - "integrity": "sha512-qkIilPUYcNhJpd33n0GBXTB1MMPp14TxEsEs0pTrsSVucApsYzW5V+Q8Qxhik6KU3evy+qkAAowTByymK0avdg==", + "version": "4.2.9", + "resolved": "https://registry.npmjs.org/graceful-fs/-/graceful-fs-4.2.9.tgz", + "integrity": "sha512-NtNxqUcXgpW2iMrfqSfR73Glt39K+BLwWsPs94yR63v45T0Wbej7eRmL5cWfwEgqXnmjQp3zaJTshdRW/qC2ZQ==", "dev": true }, "growl": { @@ -9159,9 +9198,9 @@ } }, "is-core-module": { - "version": "2.8.0", - "resolved": "https://registry.npmjs.org/is-core-module/-/is-core-module-2.8.0.tgz", - "integrity": "sha512-vd15qHsaqrRL7dtH6QNuy0ndJmRDrS9HAM1CAiSifNUFv4x1a0CCVsj18hJ1mShxIG6T2i1sO78MkP56r0nYRw==", + "version": "2.8.1", + "resolved": "https://registry.npmjs.org/is-core-module/-/is-core-module-2.8.1.tgz", + "integrity": "sha512-SdNCUs284hr40hFTFP6l0IfZ/RSrMXF3qgoRHd3/79unUTvrFO/JoXwkGm+5J/Oe3E/b5GsnG330uUNgRpu1PA==", "dev": true, "requires": { "has": "^1.0.3" @@ -9472,9 +9511,9 @@ "dev": true }, "keyv": { - "version": "4.0.4", - "resolved": "https://registry.npmjs.org/keyv/-/keyv-4.0.4.tgz", - "integrity": "sha512-vqNHbAc8BBsxk+7QBYLW0Y219rWcClspR6WSeoHYKG5mnsSoOH+BL1pWq02DDCVdvvuUny5rkBlzMRzoqc+GIg==", + "version": "4.0.5", + "resolved": "https://registry.npmjs.org/keyv/-/keyv-4.0.5.tgz", + "integrity": "sha512-531pkGLqV3BMg0eDqqJFI0R1mkK1Nm5xIP2mM6keP5P8WfFtCkg2IOwplTUmlGoTgIg9yQYZ/kdihhz89XH3vA==", "dev": true, "requires": { "json-buffer": "3.0.1" @@ -9692,6 +9731,23 @@ "yargs-unparser": "2.0.0" }, "dependencies": { + "debug": { + "version": "4.3.2", + "resolved": "https://registry.npmjs.org/debug/-/debug-4.3.2.tgz", + "integrity": "sha512-mOp8wKcvj7XxC78zLgw/ZA+6TSgkoE2C/ienthhRD298T7UNwAg9diBpLRxC0mOezLl4B0xV7M0cCO6P/O0Xhw==", + "dev": true, + "requires": { + "ms": "2.1.2" + }, + "dependencies": { + "ms": { + "version": "2.1.2", + "resolved": "https://registry.npmjs.org/ms/-/ms-2.1.2.tgz", + "integrity": "sha512-sGkPx+VjMtmA6MX27oA4FBFELFCZZ4S4XqeGOXCv68tT+jb3vk/RyaKWP0PTKyWtmLSM0b+adUTEvbs1PEaH2w==", + "dev": true + } + } + }, "escape-string-regexp": { "version": "4.0.0", "resolved": "https://registry.npmjs.org/escape-string-regexp/-/escape-string-regexp-4.0.0.tgz", @@ -10377,9 +10433,9 @@ "dev": true }, "picomatch": { - "version": "2.3.0", - "resolved": "https://registry.npmjs.org/picomatch/-/picomatch-2.3.0.tgz", - "integrity": "sha512-lY1Q/PiJGC2zOv/z391WOTD+Z02bCgsFfvxoXXf6h7kv9o+WmsmzYqrAwY63sNgOxE4xEdq0WyUnXfKeBrSvYw==", + "version": "2.3.1", + "resolved": "https://registry.npmjs.org/picomatch/-/picomatch-2.3.1.tgz", + "integrity": "sha512-JU3teHTNjmE2VCGFzuY8EXzCDVwEqB2a8fsIvwaStHhAWJEeVd1o1QD80CU6+ZdEXXSLbSsuLwJjkCBWqRQUVA==", "dev": true }, "pify": { @@ -10471,9 +10527,9 @@ } }, "qs": { - "version": "6.10.2", - "resolved": "https://registry.npmjs.org/qs/-/qs-6.10.2.tgz", - "integrity": "sha512-mSIdjzqznWgfd4pMii7sHtaYF8rx8861hBO80SraY5GT0XQibWZWJSid0avzHGkDIZLImux2S5mXO0Hfct2QCw==", + "version": "6.10.3", + "resolved": "https://registry.npmjs.org/qs/-/qs-6.10.3.tgz", + "integrity": "sha512-wr7M2E0OFRfIfJZjKGieI8lBKb7fRCH4Fv5KNPEs7gJ8jadvotdsS08PzOKR7opXhZ/Xkjtt3WF9g38drmyRqQ==", "dev": true, "requires": { "side-channel": "^1.0.4" @@ -10599,9 +10655,9 @@ } }, "release-it": { - "version": "14.11.8", - "resolved": "https://registry.npmjs.org/release-it/-/release-it-14.11.8.tgz", - "integrity": "sha512-951DJ0kwjwU7CwGU3BCvRBgLxuJsOPRrZkqx0AsugJdSyPpUdwY9nlU0RAoSKqgh+VTerzecXLIIwgsGIpNxlA==", + "version": "14.12.1", + "resolved": "https://registry.npmjs.org/release-it/-/release-it-14.12.1.tgz", + "integrity": "sha512-dYPGZ7F/kfIWzsGlzNCL6PiWfPoaVUILcmqQm80kgYhI/b9RW3k6DVqE0nqI4fHxRT3fMeKWWvS0jdQmFDKn3Q==", "dev": true, "requires": { "@iarna/toml": "2.2.5", @@ -10609,7 +10665,7 @@ "async-retry": "1.3.3", "chalk": "4.1.2", "cosmiconfig": "7.0.1", - "debug": "4.3.2", + "debug": "4.3.3", "deprecated-obj": "2.0.0", "execa": "5.1.1", "form-data": "4.0.0", @@ -10688,13 +10744,14 @@ "dev": true }, "resolve": { - "version": "1.20.0", - "resolved": "https://registry.npmjs.org/resolve/-/resolve-1.20.0.tgz", - "integrity": "sha512-wENBPt4ySzg4ybFQW2TT1zMQucPK95HSh/nq2CFTZVOGut2+pQvSsgtda4d26YrYcr067wjbmzOG8byDPBX63A==", + "version": "1.21.0", + "resolved": "https://registry.npmjs.org/resolve/-/resolve-1.21.0.tgz", + "integrity": "sha512-3wCbTpk5WJlyE4mSOtDLhqQmGFi0/TD9VPwmiolnk8U0wRgMEktqCXd3vy5buTO3tljvalNvKrjHEfrd2WpEKA==", "dev": true, "requires": { - "is-core-module": "^2.2.0", - "path-parse": "^1.0.6" + "is-core-module": "^2.8.0", + "path-parse": "^1.0.7", + "supports-preserve-symlinks-flag": "^1.0.0" } }, "resolve-alpn": { @@ -10765,9 +10822,9 @@ } }, "rxjs": { - "version": "7.5.1", - "resolved": "https://registry.npmjs.org/rxjs/-/rxjs-7.5.1.tgz", - "integrity": "sha512-KExVEeZWxMZnZhUZtsJcFwz8IvPvgu4G2Z2QyqjZQzUGr32KDYuSxrEYO4w3tFFNbfLozcrKUTvTPi+E9ywJkQ==", + "version": "7.5.2", + "resolved": "https://registry.npmjs.org/rxjs/-/rxjs-7.5.2.tgz", + "integrity": "sha512-PwDt186XaL3QN5qXj/H9DGyHhP3/RYYgZZwqBv9Tv8rsAaiwFH1IsJJlcgD37J7UW5a6O67qX0KWKS3/pu0m4w==", "dev": true, "requires": { "tslib": "^2.1.0" @@ -11021,6 +11078,12 @@ "has-flag": "^4.0.0" } }, + "supports-preserve-symlinks-flag": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/supports-preserve-symlinks-flag/-/supports-preserve-symlinks-flag-1.0.0.tgz", + "integrity": "sha512-ot0WnXS9fgdkgIcePe6RHNk1WA8+muPa6cSjeR3V8K27q9BB1rTE3R1p7Hv0z1ZyAc8s6Vvv8DIyWf681MAt0w==", + "dev": true + }, "test-exclude": { "version": "6.0.0", "resolved": "https://registry.npmjs.org/test-exclude/-/test-exclude-6.0.0.tgz", diff --git a/package.json b/package.json index cd76190b874..2c214ee9d94 100644 --- a/package.json +++ b/package.json @@ -32,7 +32,7 @@ "devDependencies": { "@tsconfig/node12": "^1.0.9", "gh-pages": "^3.2.3", - "release-it": "^14.11.8", + "release-it": "^14.12.1", "typescript": "^4.5.4" }, "repository": { diff --git a/packages/bloom/package.json b/packages/bloom/package.json index 1cef48990f1..9496b56eb3b 100644 --- a/packages/bloom/package.json +++ b/packages/bloom/package.json @@ -18,9 +18,9 @@ "devDependencies": { "@istanbuljs/nyc-config-typescript": "^1.0.2", "@node-redis/test-utils": "*", - "@types/node": "^17.0.5", + "@types/node": "^17.0.8", "nyc": "^15.1.0", - "release-it": "^14.11.8", + "release-it": "^14.12.1", "source-map-support": "^0.5.21", "ts-node": "^10.4.0", "typedoc": "^0.22.10", diff --git a/packages/client/package.json b/packages/client/package.json index cf3a470c363..f072f21c359 100644 --- a/packages/client/package.json +++ b/packages/client/package.json @@ -22,15 +22,15 @@ "devDependencies": { "@istanbuljs/nyc-config-typescript": "^1.0.2", "@node-redis/test-utils": "*", - "@types/node": "^17.0.5", + "@types/node": "^17.0.8", "@types/redis-parser": "^3.0.0", "@types/sinon": "^10.0.6", "@types/yallist": "^4.0.1", - "@typescript-eslint/eslint-plugin": "^5.8.1", - "@typescript-eslint/parser": "^5.8.1", - "eslint": "^8.5.0", + "@typescript-eslint/eslint-plugin": "^5.9.1", + "@typescript-eslint/parser": "^5.9.1", + "eslint": "^8.6.0", "nyc": "^15.1.0", - "release-it": "^14.11.8", + "release-it": "^14.12.1", "sinon": "^12.0.1", "source-map-support": "^0.5.21", "ts-node": "^10.4.0", diff --git a/packages/json/package.json b/packages/json/package.json index 769993af362..56975616f01 100644 --- a/packages/json/package.json +++ b/packages/json/package.json @@ -18,9 +18,9 @@ "devDependencies": { "@istanbuljs/nyc-config-typescript": "^1.0.2", "@node-redis/test-utils": "*", - "@types/node": "^17.0.5", + "@types/node": "^17.0.8", "nyc": "^15.1.0", - "release-it": "^14.11.8", + "release-it": "^14.12.1", "source-map-support": "^0.5.21", "ts-node": "^10.4.0", "typedoc": "^0.22.10", diff --git a/packages/search/package.json b/packages/search/package.json index e9268d28099..44d9d655ba4 100644 --- a/packages/search/package.json +++ b/packages/search/package.json @@ -20,7 +20,7 @@ "@node-redis/test-utils": "*", "@types/node": "^17.0.5", "nyc": "^15.1.0", - "release-it": "^14.11.8", + "release-it": "^14.12.1", "source-map-support": "^0.5.21", "ts-node": "^10.4.0", "typedoc": "^0.22.10", diff --git a/packages/test-utils/package.json b/packages/test-utils/package.json index 8cecb362d37..82816f9fe7e 100644 --- a/packages/test-utils/package.json +++ b/packages/test-utils/package.json @@ -12,11 +12,10 @@ "devDependencies": { "@istanbuljs/nyc-config-typescript": "^1.0.2", "@types/mocha": "^9.0.0", - "@types/node": "^17.0.5", + "@types/node": "^17.0.8", "@types/yargs": "^17.0.8", "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.4", diff --git a/packages/time-series/package.json b/packages/time-series/package.json index 63fd7aec8d9..bfcf8505af0 100644 --- a/packages/time-series/package.json +++ b/packages/time-series/package.json @@ -20,7 +20,7 @@ "@node-redis/test-utils": "*", "@types/node": "^17.0.5", "nyc": "^15.1.0", - "release-it": "^14.11.8", + "release-it": "^14.12.1", "source-map-support": "^0.5.21", "ts-node": "^10.4.0", "typedoc": "^0.22.10", From 14bbc4fdd245c89c4b0e1d13bfe1a46e67b9d8d7 Mon Sep 17 00:00:00 2001 From: leibale Date: Thu, 13 Jan 2022 07:23:12 -0500 Subject: [PATCH 1087/1748] update and move CHANGELOG.md --- packages/client/CHANGELOG.md => CHANGELOG.md | 15 +++++++++++++++ 1 file changed, 15 insertions(+) rename packages/client/CHANGELOG.md => CHANGELOG.md (99%) diff --git a/packages/client/CHANGELOG.md b/CHANGELOG.md similarity index 99% rename from packages/client/CHANGELOG.md rename to CHANGELOG.md index 88e3840f20d..ffbf10574f3 100644 --- a/packages/client/CHANGELOG.md +++ b/CHANGELOG.md @@ -1,5 +1,20 @@ # Changelog +## v4.0.2 - 13 Jan, 2022 + +### Fixes + +- Fix v4 commands in legacy mode (#1820) +- Fix `EXISTS` command reply (#1819) +- Fix handler for "__redis__:invalidate" messages (#1798) +- Fix "SEPARATOR" typo in RediSearch (#1823) + +### Enhancements + +- First release of `@node-redis/bloom` +- Add support for `Buffer`s +- Enhance `ASK` and `MOVED` errors handler + ## v4.0.1 - 13 Dec, 2021 ### Fixes From 3a6392a0add5b7bfadecefb487a061db59d31a07 Mon Sep 17 00:00:00 2001 From: leibale Date: Thu, 13 Jan 2022 07:24:22 -0500 Subject: [PATCH 1088/1748] Release client@1.0.2 --- packages/client/package.json | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/packages/client/package.json b/packages/client/package.json index f072f21c359..63cf9e817c5 100644 --- a/packages/client/package.json +++ b/packages/client/package.json @@ -1,6 +1,6 @@ { "name": "@node-redis/client", - "version": "1.0.1", + "version": "1.0.2", "license": "MIT", "main": "./dist/index.js", "types": "./dist/index.d.ts", From 5a4712948ae095c6bc0a2b05667d0a54c67d30db Mon Sep 17 00:00:00 2001 From: leibale Date: Thu, 13 Jan 2022 07:25:16 -0500 Subject: [PATCH 1089/1748] update bloom version --- packages/bloom/package.json | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/packages/bloom/package.json b/packages/bloom/package.json index 9496b56eb3b..c7131a870ee 100644 --- a/packages/bloom/package.json +++ b/packages/bloom/package.json @@ -1,6 +1,6 @@ { "name": "@node-redis/bloom", - "version": "1.0.0", + "version": "1.0.0-rc", "license": "MIT", "main": "./dist/index.js", "types": "./dist/index.d.ts", From 2c4565f56c71ab11ccff285e76480fe152c167b1 Mon Sep 17 00:00:00 2001 From: leibale Date: Thu, 13 Jan 2022 07:25:39 -0500 Subject: [PATCH 1090/1748] Release bloom@1.0.0 --- packages/bloom/package.json | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/packages/bloom/package.json b/packages/bloom/package.json index c7131a870ee..9496b56eb3b 100644 --- a/packages/bloom/package.json +++ b/packages/bloom/package.json @@ -1,6 +1,6 @@ { "name": "@node-redis/bloom", - "version": "1.0.0-rc", + "version": "1.0.0", "license": "MIT", "main": "./dist/index.js", "types": "./dist/index.d.ts", From 5a3120e7a335008c51bd4871bebd9eda320acf28 Mon Sep 17 00:00:00 2001 From: leibale Date: Thu, 13 Jan 2022 07:26:10 -0500 Subject: [PATCH 1091/1748] Release json@1.0.2 --- packages/json/package.json | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/packages/json/package.json b/packages/json/package.json index 56975616f01..d958d5e9736 100644 --- a/packages/json/package.json +++ b/packages/json/package.json @@ -1,6 +1,6 @@ { "name": "@node-redis/json", - "version": "1.0.1", + "version": "1.0.2", "license": "MIT", "main": "./dist/index.js", "types": "./dist/index.d.ts", From f91535fe2c154a683d9a3335d8669e4700f38239 Mon Sep 17 00:00:00 2001 From: leibale Date: Thu, 13 Jan 2022 07:27:30 -0500 Subject: [PATCH 1092/1748] Release search@1.0.2 --- packages/search/package.json | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/packages/search/package.json b/packages/search/package.json index 44d9d655ba4..25c557219e5 100644 --- a/packages/search/package.json +++ b/packages/search/package.json @@ -1,6 +1,6 @@ { "name": "@node-redis/search", - "version": "1.0.1", + "version": "1.0.2", "license": "MIT", "main": "./dist/index.js", "types": "./dist/index.d.ts", From 525142ea7fcf405648d9468742d57760a19b237e Mon Sep 17 00:00:00 2001 From: leibale Date: Thu, 13 Jan 2022 07:28:47 -0500 Subject: [PATCH 1093/1748] Release time-series@1.0.1 --- packages/time-series/package.json | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/packages/time-series/package.json b/packages/time-series/package.json index bfcf8505af0..39f1f2724cc 100644 --- a/packages/time-series/package.json +++ b/packages/time-series/package.json @@ -1,6 +1,6 @@ { "name": "@node-redis/time-series", - "version": "1.0.0-rc.0", + "version": "1.0.1", "license": "MIT", "main": "./dist/index.js", "types": "./dist/index.d.ts", From d5b8eadb4274805122e2d770039f4e1e5517f408 Mon Sep 17 00:00:00 2001 From: leibale Date: Thu, 13 Jan 2022 07:29:36 -0500 Subject: [PATCH 1094/1748] upgrade modules --- package.json | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/package.json b/package.json index 2c214ee9d94..f19bac5f322 100644 --- a/package.json +++ b/package.json @@ -24,10 +24,10 @@ }, "dependencies": { "@node-redis/bloom": "^1.0.0", - "@node-redis/client": "^1.0.1", - "@node-redis/json": "^1.0.1", - "@node-redis/search": "^1.0.1", - "@node-redis/time-series": "^1.0.0" + "@node-redis/client": "^1.0.2", + "@node-redis/json": "^1.0.2", + "@node-redis/search": "^1.0.2", + "@node-redis/time-series": "^1.0.1" }, "devDependencies": { "@tsconfig/node12": "^1.0.9", From 95c7dc197d95ff1c90c92ab37d8b2deb3e7ea5f3 Mon Sep 17 00:00:00 2001 From: leibale Date: Thu, 13 Jan 2022 07:30:20 -0500 Subject: [PATCH 1095/1748] Release redis@4.0.2 --- package-lock.json | 4 ++-- package.json | 2 +- 2 files changed, 3 insertions(+), 3 deletions(-) diff --git a/package-lock.json b/package-lock.json index a9a148702b9..4627f1ae5e3 100644 --- a/package-lock.json +++ b/package-lock.json @@ -1,12 +1,12 @@ { "name": "redis", - "version": "4.0.1", + "version": "4.0.2", "lockfileVersion": 2, "requires": true, "packages": { "": { "name": "redis", - "version": "4.0.1", + "version": "4.0.2", "license": "MIT", "workspaces": [ "./packages/*" diff --git a/package.json b/package.json index f19bac5f322..baa4d928a8f 100644 --- a/package.json +++ b/package.json @@ -1,7 +1,7 @@ { "name": "redis", "description": "A modern, high performance Redis client", - "version": "4.0.1", + "version": "4.0.2", "license": "MIT", "main": "./dist/index.js", "types": "./dist/index.d.ts", From 29222459c74da2d2b438fbf1ce80b715c2ba225c Mon Sep 17 00:00:00 2001 From: leibale Date: Thu, 13 Jan 2022 08:06:36 -0500 Subject: [PATCH 1096/1748] fix bloom files --- packages/bloom/package.json | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/packages/bloom/package.json b/packages/bloom/package.json index 9496b56eb3b..1752a435841 100644 --- a/packages/bloom/package.json +++ b/packages/bloom/package.json @@ -5,7 +5,7 @@ "main": "./dist/index.js", "types": "./dist/index.d.ts", "files": [ - "./dist" + "dist/" ], "scripts": { "test": "nyc -r text-summary -r lcov mocha -r source-map-support/register -r ts-node/register './lib/**/*.spec.ts'", From 7bcc1a7c4414fbaa1658e6bd7ad7f7d763e8773d Mon Sep 17 00:00:00 2001 From: leibale Date: Thu, 13 Jan 2022 08:06:58 -0500 Subject: [PATCH 1097/1748] Release bloom@1.0.1 --- packages/bloom/package.json | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/packages/bloom/package.json b/packages/bloom/package.json index 1752a435841..79b08774b75 100644 --- a/packages/bloom/package.json +++ b/packages/bloom/package.json @@ -1,6 +1,6 @@ { "name": "@node-redis/bloom", - "version": "1.0.0", + "version": "1.0.1", "license": "MIT", "main": "./dist/index.js", "types": "./dist/index.d.ts", From 841bd438130672d5d95fe9950bb6ca205427e535 Mon Sep 17 00:00:00 2001 From: leibale Date: Thu, 13 Jan 2022 09:15:35 -0500 Subject: [PATCH 1098/1748] update readmes --- packages/bloom/README.md | 2 ++ packages/client/README.md | 1 + packages/time-series/README.md | 1 + 3 files changed, 4 insertions(+) diff --git a/packages/bloom/README.md b/packages/bloom/README.md index d13769e02a4..acdf568fa11 100644 --- a/packages/bloom/README.md +++ b/packages/bloom/README.md @@ -1 +1,3 @@ # @node-redis/bloom + +The source code and documentation for this package are in the main [node-redis](https://github.com/redis/node-redis) repo. diff --git a/packages/client/README.md b/packages/client/README.md index 6007608ea3b..63b31402292 100644 --- a/packages/client/README.md +++ b/packages/client/README.md @@ -1,2 +1,3 @@ # @node-redis/client + The source code and documentation for this package are in the main [node-redis](https://github.com/redis/node-redis) repo. diff --git a/packages/time-series/README.md b/packages/time-series/README.md index 3242afb5532..b02e2648a59 100644 --- a/packages/time-series/README.md +++ b/packages/time-series/README.md @@ -1,2 +1,3 @@ # @node-redis/time-series + The sources and docs for this package are in the main [node-redis](https://github.com/redis/node-redis) repo. From 309cdbdd7ce2a2ee833bb48c86604fae968c093d Mon Sep 17 00:00:00 2001 From: Simon Prickett Date: Thu, 13 Jan 2022 16:57:03 +0000 Subject: [PATCH 1099/1748] Add stream examples. (#1830) * Adds stream consumer and producer scripts to doc. * Updated build command example. * Adds stream producer example. * Adds basic stream consumer example. * Added isolated execution. * Update README.md * Update stream-consumer.js Co-authored-by: Leibale Eidelman --- examples/README.md | 4 ++- examples/stream-consumer.js | 65 +++++++++++++++++++++++++++++++++++++ examples/stream-producer.js | 28 ++++++++++++++++ 3 files changed, 96 insertions(+), 1 deletion(-) create mode 100644 examples/stream-consumer.js create mode 100644 examples/stream-producer.js diff --git a/examples/README.md b/examples/README.md index 94b043ae483..a9c08699012 100644 --- a/examples/README.md +++ b/examples/README.md @@ -12,6 +12,8 @@ This folder contains example scripts showing how to use Node Redis in different | `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 | +| `stream-producer.js` | Adds entries to a [Redis Stream](https://redis.io/topics/streams-intro) using the `XADD` command | +| `stream-consumer.js` | Reads entries from a [Redis Stream](https://redis.io/topics/streams-intro) using the blocking `XREAD` command | ## Contributing @@ -24,7 +26,7 @@ To set up the examples folder so that you can run an example / develop one of yo ``` $ git clone https://github.com/redis/node-redis.git $ cd node-redis -$ npm install -ws && npm run build +$ npm install -ws && npm run build-all $ cd examples $ npm install ``` diff --git a/examples/stream-consumer.js b/examples/stream-consumer.js new file mode 100644 index 00000000000..4e0f140c0da --- /dev/null +++ b/examples/stream-consumer.js @@ -0,0 +1,65 @@ +// A sample stream consumer using the blocking variant of XREAD. +// This consumes entries from a stream created by stream-producer.js + +import { createClient, commandOptions } from 'redis'; + +async function streamConsumer() { + const client = createClient(); + + await client.connect(); + + let currentId = '0-0'; // Start at lowest possible stream ID + + while (true) { + try { + let response = await client.xRead( + commandOptions({ + isolated: true + }), [ + // XREAD can read from multiple streams, starting at a + // different ID for each... + { + key: 'mystream', + id: currentId + } + ], { + // Read 1 entry at a time, block for 5 seconds if there are none. + COUNT: 1, + BLOCK: 5000 + } + ); + + if (response) { + // Response is an array of streams, each containing an array of + // entries: + // [ + // { + // "name": "mystream", + // "messages": [ + // { + // "id": "1642088708425-0", + // "message": { + // "num": "999" + // } + // } + // ] + // } + // ] + console.log(JSON.stringify(response)); + + // Get the ID of the first (only) entry returned. + currentId = response[0].messages[0].id; + console.log(currentId); + } else { + // Response is null, we have read everything that is + // in the stream right now... + console.log('No new stream entries.'); + } + } + } catch (err) { + console.error(err); + } +} + +streamConsumer(); + diff --git a/examples/stream-producer.js b/examples/stream-producer.js new file mode 100644 index 00000000000..3f7a4a963c9 --- /dev/null +++ b/examples/stream-producer.js @@ -0,0 +1,28 @@ +// A sample stream producer using XADD. + +import { createClient } from 'redis'; + +async function streamProducer() { + const client = createClient(); + + await client.connect(); + await client.del('mystream'); + + let num = 0; + + while (num < 1000) { + // * = Let Redis generate a timestamp ID for this new entry. + let id = await client.xAdd('mystream', '*', { + num: `${num}` + // Other name/value pairs can go here as required... + }); + + console.log(`Added ${id} to the stream.`); + num += 1; + } + + await client.quit(); +} + +streamProducer(); + From 8a40398a75d9046226080d068e0f045d45d1e01e Mon Sep 17 00:00:00 2001 From: Leibale Eidelman Date: Thu, 13 Jan 2022 12:01:41 -0500 Subject: [PATCH 1100/1748] Update stream-consumer.js --- examples/stream-consumer.js | 13 ++++++------- 1 file changed, 6 insertions(+), 7 deletions(-) diff --git a/examples/stream-consumer.js b/examples/stream-consumer.js index 4e0f140c0da..f6ab16421cd 100644 --- a/examples/stream-consumer.js +++ b/examples/stream-consumer.js @@ -16,7 +16,7 @@ async function streamConsumer() { commandOptions({ isolated: true }), [ - // XREAD can read from multiple streams, starting at a + // XREAD can read from multiple streams, starting at a // different ID for each... { key: 'mystream', @@ -33,7 +33,7 @@ async function streamConsumer() { // Response is an array of streams, each containing an array of // entries: // [ - // { + // { // "name": "mystream", // "messages": [ // { @@ -49,17 +49,16 @@ async function streamConsumer() { // Get the ID of the first (only) entry returned. currentId = response[0].messages[0].id; - console.log(currentId); + console.log(currentId); } else { - // Response is null, we have read everything that is + // Response is null, we have read everything that is // in the stream right now... console.log('No new stream entries.'); } + } catch (err) { + console.error(err); } - } catch (err) { - console.error(err); } } streamConsumer(); - From e4601b6960bc3a8b84b3804408e89f4a15677a4d Mon Sep 17 00:00:00 2001 From: Simon Prickett Date: Sat, 15 Jan 2022 16:44:53 +0000 Subject: [PATCH 1101/1748] Adds topk example for RedisBloom (#1837) * Adds topk example. * Update topk.js Co-authored-by: Leibale Eidelman --- examples/README.md | 1 + examples/topk.js | 94 ++++++++++++++++++++++++++++++++++++++++++++++ 2 files changed, 95 insertions(+) create mode 100644 examples/topk.js diff --git a/examples/README.md b/examples/README.md index a9c08699012..5151edfe1a5 100644 --- a/examples/README.md +++ b/examples/README.md @@ -14,6 +14,7 @@ This folder contains example scripts showing how to use Node Redis in different | `set-scan.js` | An example script that shows how to use the SSCAN iterator functionality | | `stream-producer.js` | Adds entries to a [Redis Stream](https://redis.io/topics/streams-intro) using the `XADD` command | | `stream-consumer.js` | Reads entries from a [Redis Stream](https://redis.io/topics/streams-intro) using the blocking `XREAD` command | +| `topk.js` | Use the [RedisBloom](https://redisbloom.io) TopK to track the most frequently seen items. | ## Contributing diff --git a/examples/topk.js b/examples/topk.js new file mode 100644 index 00000000000..14129724341 --- /dev/null +++ b/examples/topk.js @@ -0,0 +1,94 @@ +// This example demonstrates the use of the Top K +// in the RedisBloom module (https://redisbloom.io/) + +import { createClient } from 'redis'; + +async function topK() { + const client = createClient(); + + await client.connect(); + + // Delete any pre-existing Top K. + await client.del('mytopk'); + + // Reserve a Top K to track the 10 most common items. + // https://oss.redis.com/redisbloom/TopK_Commands/#topkreserve + try { + await client.topK.reserve('mytopk', 10); + console.log('Reserved Top K.'); + } catch (e) { + if (e.message.endsWith('key already exists')) { + console.log('Top K already reserved.'); + } else { + console.log('Error, maybe RedisBloom is not installed?:'); + console.log(e); + } + } + + const teamMembers = [ + 'leibale', + 'simon', + 'guy', + 'suze', + 'brian', + 'steve', + 'kyleb', + 'kyleo', + 'josefin', + 'alex', + 'nava', + 'lance', + 'rachel', + 'kaitlyn' + ]; + + // Add random counts for random team members with TOPK.INCRBY + for (let n = 0; n < 1000; n++) { + const teamMember = teamMembers[Math.floor(Math.random() * teamMembers.length)]; + const points = Math.floor(Math.random() * 1000) + 1; + await client.topK.incrBy('mytopk', { + item: teamMember, + incrementBy: points + }); + console.log(`Added ${points} points for ${teamMember}.`); + } + + // List out the top 10 with TOPK.LIST + const top10 = await client.topK.list('mytopk'); + console.log('The top 10:'); + // top10 looks like this: + // [ + // 'guy', 'nava', + // 'kaitlyn', 'brian', + // 'simon', 'suze', + // 'lance', 'alex', + // 'steve', 'kyleo' + // ] + console.log(top10); + + // Check if a few team members are in the top 10 with TOPK.QUERY: + const [ steve, suze, leibale, frederick ] = await client.topK.query('mytopk', [ + 'steve', + 'suze', + 'leibale', + 'frederick' + ]); + + console.log(`steve ${steve === 1 ? 'is': 'is not'} in the top 10.`); + console.log(`suze ${suze === 1 ? 'is': 'is not'} in the top 10.`); + console.log(`leibale ${leibale === 1 ? 'is': 'is not'} in the top 10.`); + console.log(`frederick ${frederick === 1 ? 'is': 'is not'} in the top 10.`); + + // Get count estimate for some team members: + const [ simonCount, lanceCount ] = await client.topK.count('mytopk', [ + 'simon', + 'lance' + ]); + + console.log(`Count estimate for simon: ${simonCount}.`); + console.log(`Count estimate for lance: ${lanceCount}.`); + + await client.quit(); +} + +topK(); From 06cb63756c1ec7d478b7d56ba1043cd98cc1f3cd Mon Sep 17 00:00:00 2001 From: Simon Prickett Date: Sat, 15 Jan 2022 16:47:47 +0000 Subject: [PATCH 1102/1748] Adds Bloom Filter example using RedisBloom. (#1835) * Adds Bloom Filter example using RedisBloom. * Update bloom-filter.js Co-authored-by: Leibale Eidelman --- examples/README.md | 27 +++++++------- examples/bloom-filter.js | 79 ++++++++++++++++++++++++++++++++++++++++ 2 files changed, 93 insertions(+), 13 deletions(-) create mode 100644 examples/bloom-filter.js diff --git a/examples/README.md b/examples/README.md index 5151edfe1a5..cd7e86882da 100644 --- a/examples/README.md +++ b/examples/README.md @@ -2,19 +2,20 @@ 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 | -| `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 | -| `stream-producer.js` | Adds entries to a [Redis Stream](https://redis.io/topics/streams-intro) using the `XADD` command | -| `stream-consumer.js` | Reads entries from a [Redis Stream](https://redis.io/topics/streams-intro) using the blocking `XREAD` command | -| `topk.js` | Use the [RedisBloom](https://redisbloom.io) TopK to track the most frequently seen items. | +| File Name | Description | +|-----------------------------|---------------------------------------------------------------------------------------------------------------------------------------------------| +| `blocking-list-pop.js` | Block until an element is pushed to a list | +| `bloom-filter.js` | Space efficient set membership checks with a [Bloom Filter](https://en.wikipedia.org/wiki/Bloom_filter) using [RedisBloom](https://redisbloom.io) | +| `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 | +| `stream-producer.js` | Adds entries to a [Redis Stream](https://redis.io/topics/streams-intro) using the `XADD` command | +| `stream-consumer.js` | Reads entries from a [Redis Stream](https://redis.io/topics/streams-intro) using the blocking `XREAD` command | +| `topk.js` | Use the [RedisBloom](https://redisbloom.io) TopK to track the most frequently seen items. | ## Contributing diff --git a/examples/bloom-filter.js b/examples/bloom-filter.js new file mode 100644 index 00000000000..db55c519c33 --- /dev/null +++ b/examples/bloom-filter.js @@ -0,0 +1,79 @@ +// This example demonstrates the use of the Bloom Filter +// in the RedisBloom module (https://redisbloom.io/) + +import { createClient } from 'redis'; + +async function bloomFilter() { + const client = createClient(); + + await client.connect(); + + // Delete any pre-existing Bloom Filter. + await client.del('mybloom'); + + // Reserve a Bloom Filter with configurable error rate and capacity. + // https://oss.redis.com/redisbloom/Bloom_Commands/#bfreserve + try { + await client.bf.reserve('mybloom', 0.01, 1000); + console.log('Reserved Bloom Filter.'); + } catch (e) { + if (e.message.endsWith('item exists')) { + console.log('Bloom Filter already reserved.'); + } else { + console.log('Error, maybe RedisBloom is not installed?:'); + console.log(e); + } + } + + // Add items to Bloom Filter individually with BF.ADD command. + await Promise.all([ + client.bf.add('mybloom', 'leibale'), + client.bf.add('mybloom', 'simon'), + client.bf.add('mybloom', 'guy'), + client.bf.add('mybloom', 'suze'), + client.bf.add('mybloom', 'brian'), + client.bf.add('mybloom', 'steve'), + client.bf.add('mybloom', 'kyle'), + client.bf.add('mybloom', 'josefin'), + client.bf.add('mybloom', 'alex'), + client.bf.add('mybloom', 'nava'), + ]); + + // Add multiple items to Bloom Filter at once with BF.MADD command. + await client.bf.mAdd('mybloom', [ + 'kaitlyn', + 'rachel' + ]); + + console.log('Added members to Bloom Filter.'); + + // Check whether a member exists with the BF.EXISTS command. + const simonExists = await client.bf.exists('mybloom', 'simon'); + console.log(`simon ${simonExists ? 'may' : 'does not'} exist in the Bloom Filter.`); + + // Check whether multiple members exist with the BF.MEXISTS command: + const [ lanceExists, leibaleExists ] = await client.bf.mExists('mybloom', [ + 'lance', + 'leibale' + ]); + + console.log(`lance ${lanceExists ? 'may' : 'does not'} exist in the Bloom Filter.`); + console.log(`leibale ${leibaleExists ? 'may' : 'does not'} exist in the Bloom Filter.`); + + // Get stats for the Bloom Filter with the BF.INFO command: + const info = await client.bf.info('mybloom'); + // info looks like this: + // + // { + // capacity: 1000, + // size: 1531, + // numberOfFilters: 1, + // numberOfInsertedItems: 12, + // expansionRate: 2 + // } + console.log(info); + + await client.quit(); +} + +bloomFilter(); From 2ff7084b7286aa1ec8a7539e9789446c489cca16 Mon Sep 17 00:00:00 2001 From: Guy Korland Date: Sat, 15 Jan 2022 18:48:33 +0200 Subject: [PATCH 1103/1748] Update README.md (#1840) * Update README.md * Update README.md Co-authored-by: Leibale Eidelman --- README.md | 23 ++++++++++++----------- 1 file changed, 12 insertions(+), 11 deletions(-) diff --git a/README.md b/README.md index d22013328c0..0399595d8c4 100644 --- a/README.md +++ b/README.md @@ -8,6 +8,18 @@ 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). + +## Packages + +| Name | Description | +|---------------------------------------------------|----------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------| +| [redis](./) | [![Downloads](https://img.shields.io/npm/dm/redis.svg)](https://www.npmjs.com/package/redis) [![Version](https://img.shields.io/npm/v/redis.svg)](https://www.npmjs.com/package/redis) | +| [@node-redis/client](./packages/client) | [![Downloads](https://img.shields.io/npm/dm/@node-redis/client.svg)](https://www.npmjs.com/package/@node-redis/client) [![Version](https://img.shields.io/npm/v/@node-redis/client.svg)](https://www.npmjs.com/package/@node-redis/client) [![Docs](https://img.shields.io/badge/-documentation-dc382c)](https://redis.js.org/documentation/client/) | +| [@node-redis/bloom](./packages/bloom) | [![Downloads](https://img.shields.io/npm/dm/@node-redis/bloom.svg)](https://www.npmjs.com/package/@node-redis/bloom) [![Version](https://img.shields.io/npm/v/@node-redis/bloom.svg)](https://www.npmjs.com/package/@node-redis/bloom) [![Docs](https://img.shields.io/badge/-documentation-dc382c)](https://redis.js.org/documentation/bloom/) [Redis Bloom](https://oss.redis.com/redisbloom/) commands | +| [@node-redis/json](./packages/json) | [![Downloads](https://img.shields.io/npm/dm/@node-redis/json.svg)](https://www.npmjs.com/package/@node-redis/json) [![Version](https://img.shields.io/npm/v/@node-redis/json.svg)](https://www.npmjs.com/package/@node-redis/json) [![Docs](https://img.shields.io/badge/-documentation-dc382c)](https://redis.js.org/documentation/json/) [Redis JSON](https://oss.redis.com/redisjson/) commands | +| [@node-redis/search](./packages/search) | [![Downloads](https://img.shields.io/npm/dm/@node-redis/search.svg)](https://www.npmjs.com/package/@node-redis/search) [![Version](https://img.shields.io/npm/v/@node-redis/search.svg)](https://www.npmjs.com/package/@node-redis/search) [![Docs](https://img.shields.io/badge/-documentation-dc382c)](https://redis.js.org/documentation/search/) [Redis Search](https://oss.redis.com/redisearch/) commands | +| [@node-redis/time-series](./packages/time-series) | [![Downloads](https://img.shields.io/npm/dm/@node-redis/time-series.svg)](https://www.npmjs.com/package/@node-redis/time-series) [![Version](https://img.shields.io/npm/v/@node-redis/time-series.svg)](https://www.npmjs.com/package/@node-redis/time-series) [![Docs](https://img.shields.io/badge/-documentation-dc382c)](https://redis.js.org/documentation/time-series/) [Redis Time-Series](https://oss.redis.com/redistimeseries/) commands | + ## Installation ```bash @@ -319,17 +331,6 @@ Node Redis is supported with the following versions of Redis: > Node Redis should work with older versions of Redis, but it is not fully tested and we cannot offer support. -## Packages - -| Name | Description | -|---------------------------------------------------|----------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------| -| [redis](./) | [![Downloads](https://img.shields.io/npm/dm/redis.svg)](https://www.npmjs.com/package/redis) [![Version](https://img.shields.io/npm/v/redis.svg)](https://www.npmjs.com/package/redis) | -| [@node-redis/client](./packages/client) | [![Downloads](https://img.shields.io/npm/dm/@node-redis/client.svg)](https://www.npmjs.com/package/@node-redis/client) [![Version](https://img.shields.io/npm/v/@node-redis/client.svg)](https://www.npmjs.com/package/@node-redis/client) [![Docs](https://img.shields.io/badge/-documentation-dc382c)](https://redis.js.org/documentation/client/) | -| [@node-redis/bloom](./packages/bloom) | [![Downloads](https://img.shields.io/npm/dm/@node-redis/bloom.svg)](https://www.npmjs.com/package/@node-redis/bloom) [![Version](https://img.shields.io/npm/v/@node-redis/bloom.svg)](https://www.npmjs.com/package/@node-redis/bloom) [![Docs](https://img.shields.io/badge/-documentation-dc382c)](https://redis.js.org/documentation/bloom/) [Redis Bloom](https://oss.redis.com/redisbloom/) commands | -| [@node-redis/json](./packages/json) | [![Downloads](https://img.shields.io/npm/dm/@node-redis/json.svg)](https://www.npmjs.com/package/@node-redis/json) [![Version](https://img.shields.io/npm/v/@node-redis/json.svg)](https://www.npmjs.com/package/@node-redis/json) [![Docs](https://img.shields.io/badge/-documentation-dc382c)](https://redis.js.org/documentation/json/) [Redis JSON](https://oss.redis.com/redisjson/) commands | -| [@node-redis/search](./packages/search) | [![Downloads](https://img.shields.io/npm/dm/@node-redis/search.svg)](https://www.npmjs.com/package/@node-redis/search) [![Version](https://img.shields.io/npm/v/@node-redis/search.svg)](https://www.npmjs.com/package/@node-redis/search) [![Docs](https://img.shields.io/badge/-documentation-dc382c)](https://redis.js.org/documentation/search/) [Redis Search](https://oss.redis.com/redisearch/) commands | -| [@node-redis/time-series](./packages/time-series) | [![Downloads](https://img.shields.io/npm/dm/@node-redis/time-series.svg)](https://www.npmjs.com/package/@node-redis/time-series) [![Version](https://img.shields.io/npm/v/@node-redis/time-series.svg)](https://www.npmjs.com/package/@node-redis/time-series) [![Docs](https://img.shields.io/badge/-documentation-dc382c)](https://redis.js.org/documentation/time-series/) [Redis Time-Series](https://oss.redis.com/redistimeseries/) commands | - ## Contributing If you'd like to contribute, check out the [contributing guide](CONTRIBUTING.md). From 1f8993a14d94ac10ab09613130c7b0a4bbab3c12 Mon Sep 17 00:00:00 2001 From: Simon Prickett Date: Wed, 19 Jan 2022 18:27:04 +0000 Subject: [PATCH 1104/1748] Adds Bloom overview README. (#1850) * Adds Bloom overview README. * Update README.md * Incorporates feedback. * Update README.md Co-authored-by: Leibale Eidelman --- packages/bloom/README.md | 13 ++++++++++++- 1 file changed, 12 insertions(+), 1 deletion(-) diff --git a/packages/bloom/README.md b/packages/bloom/README.md index acdf568fa11..b4dba2a6cca 100644 --- a/packages/bloom/README.md +++ b/packages/bloom/README.md @@ -1,3 +1,14 @@ # @node-redis/bloom -The source code and documentation for this package are in the main [node-redis](https://github.com/redis/node-redis) repo. +This package provides support for the [RedisBloom](https://redisbloom.io) module, which adds additional probabilistic data structures to Redis. It extends the [Node Redis client](https://github.com/redis/node-redis) to include functions for each of the RediBloom commands. + +To use these extra commands, your Redis server must have the RedisBloom module installed. + +RedisBloom provides the following probabilistic data structures: + +* Bloom Filter: for checking set membership with a high degree of certainty. +* Cuckoo Filter: for checking set membership with a high degree of certainty. +* Count-Min Sketch: Determine the frequency of events in a stream. +* Top-K: Maintain a list of k most frequently seen items. + +For complete examples, see `bloom-filter.js`, `cuckoo-filter.js`, `count-min-sketch.js` and `topk.js` in the Node Redis examples folder. From 86c239e7e94abe42e915097d0ff6e93e6c9af34f Mon Sep 17 00:00:00 2001 From: Simon Prickett Date: Wed, 19 Jan 2022 18:30:41 +0000 Subject: [PATCH 1105/1748] Adds a basic count-min sketch example for RedisBloom. (#1842) --- examples/README.md | 1 + examples/count-min-sketch.js | 83 ++++++++++++++++++++++++++++++++++++ 2 files changed, 84 insertions(+) create mode 100644 examples/count-min-sketch.js diff --git a/examples/README.md b/examples/README.md index cd7e86882da..9a3e14360d7 100644 --- a/examples/README.md +++ b/examples/README.md @@ -8,6 +8,7 @@ This folder contains example scripts showing how to use Node Redis in different | `bloom-filter.js` | Space efficient set membership checks with a [Bloom Filter](https://en.wikipedia.org/wiki/Bloom_filter) using [RedisBloom](https://redisbloom.io) | | `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 | +| `count-min-sketch.js` | Estimate the frequency of a given event using the [RedisBloom](https://redisbloom.io) Count-Min Sketch | | `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 | diff --git a/examples/count-min-sketch.js b/examples/count-min-sketch.js new file mode 100644 index 00000000000..63c8808cc24 --- /dev/null +++ b/examples/count-min-sketch.js @@ -0,0 +1,83 @@ +// This example demonstrates the use of the Count-Min Sketch +// in the RedisBloom module (https://redisbloom.io/) + +import { createClient } from 'redis'; + +async function countMinSketch() { + const client = createClient(); + + await client.connect(); + + // Delete any pre-existing Count-Min Sketch. + await client.del('mycms'); + + // Initialize a Count-Min Sketch with error rate and probability: + // https://oss.redis.com/redisbloom/CountMinSketch_Commands/#cmsinitbyprob + try { + await client.cms.initByProb('mycms', 0.001, 0.01); + console.log('Reserved Top K.'); + } catch (e) { + console.log('Error, maybe RedisBloom is not installed?:'); + console.log(e); + } + + const teamMembers = [ + 'leibale', + 'simon', + 'guy', + 'suze', + 'brian', + 'steve', + 'kyleb', + 'kyleo', + 'josefin', + 'alex', + 'nava', + 'lance', + 'rachel', + 'kaitlyn' + ]; + + // Store actual counts for comparison with CMS. + let actualCounts = {}; + + // Randomly emit a team member and count them with the CMS. + for (let n = 0; n < 1000; n++) { + const teamMember = teamMembers[Math.floor(Math.random() * teamMembers.length)]; + await client.cms.incrBy('mycms', { + item: teamMember, + incrementBy: 1 + }); + + actualCounts[teamMember] = actualCounts[teamMember] ? actualCounts[teamMember] + 1 : 1; + + console.log(`Incremented score for ${teamMember}.`); + } + + // Get count estimate for some team members: + // https://oss.redis.com/redisbloom/CountMinSketch_Commands/#cmsquery + const [ alexCount, rachelCount ] = await client.cms.query('mycms', [ + 'simon', + 'lance' + ]); + + console.log(`Count estimate for alex: ${alexCount} (actual ${actualCounts.alex}).`); + console.log(`Count estimate for rachel: ${rachelCount} (actual ${actualCounts.rachel}).`); + + // Get overall information about the Count-Min Sketch: + // https://oss.redis.com/redisbloom/CountMinSketch_Commands/#cmsinfo + const info = await client.cms.info('mycms'); + console.log('Count-Min Sketch info:'); + + // info looks like this: + // { + // width: 2000, + // depth: 7, + // count: 1000 + // } + console.log(info); + + await client.quit(); +} + +countMinSketch(); From b68836c59f35923c5a88419ab0bf899088ac5828 Mon Sep 17 00:00:00 2001 From: Simon Prickett Date: Wed, 19 Jan 2022 18:32:20 +0000 Subject: [PATCH 1106/1748] Adds Cuckoo Filter example. (#1843) Co-authored-by: Leibale Eidelman --- examples/README.md | 31 +++++++-------- examples/cuckoo-filter.js | 81 +++++++++++++++++++++++++++++++++++++++ 2 files changed, 97 insertions(+), 15 deletions(-) create mode 100644 examples/cuckoo-filter.js diff --git a/examples/README.md b/examples/README.md index 9a3e14360d7..a387b17cb6e 100644 --- a/examples/README.md +++ b/examples/README.md @@ -2,21 +2,22 @@ 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 | -| `bloom-filter.js` | Space efficient set membership checks with a [Bloom Filter](https://en.wikipedia.org/wiki/Bloom_filter) using [RedisBloom](https://redisbloom.io) | -| `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 | -| `count-min-sketch.js` | Estimate the frequency of a given event using the [RedisBloom](https://redisbloom.io) Count-Min Sketch | -| `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 | -| `stream-producer.js` | Adds entries to a [Redis Stream](https://redis.io/topics/streams-intro) using the `XADD` command | -| `stream-consumer.js` | Reads entries from a [Redis Stream](https://redis.io/topics/streams-intro) using the blocking `XREAD` command | -| `topk.js` | Use the [RedisBloom](https://redisbloom.io) TopK to track the most frequently seen items. | +| File Name | Description | +|-----------------------------|-----------------------------------------------------------------------------------------------------------------------------------------------------| +| `blocking-list-pop.js` | Block until an element is pushed to a list | +| `bloom-filter.js` | Space efficient set membership checks with a [Bloom Filter](https://en.wikipedia.org/wiki/Bloom_filter) using [RedisBloom](https://redisbloom.io) | +| `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 | +| `count-min-sketch.js` | Estimate the frequency of a given event using the [RedisBloom](https://redisbloom.io) Count-Min Sketch | +| `cuckoo-filter.js` | Space efficient set membership checks with a [Cuckoo Filter](https://en.wikipedia.org/wiki/Cuckoo_filter) using [RedisBloom](https://redisbloom.io) | +| `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 | +| `stream-producer.js` | Adds entries to a [Redis Stream](https://redis.io/topics/streams-intro) using the `XADD` command | +| `stream-consumer.js` | Reads entries from a [Redis Stream](https://redis.io/topics/streams-intro) using the blocking `XREAD` command | +| `topk.js` | Use the [RedisBloom](https://redisbloom.io) TopK to track the most frequently seen items. | ## Contributing diff --git a/examples/cuckoo-filter.js b/examples/cuckoo-filter.js new file mode 100644 index 00000000000..e2262a9f026 --- /dev/null +++ b/examples/cuckoo-filter.js @@ -0,0 +1,81 @@ +// This example demonstrates the use of the Cuckoo Filter +// in the RedisBloom module (https://redisbloom.io/) + +import { createClient } from 'redis'; + +async function cuckooFilter() { + const client = createClient(); + + await client.connect(); + + // Delete any pre-existing Cuckoo Filter. + await client.del('mycuckoo'); + + // Reserve a Cuckoo Filter with a capacity of 10000 items. + // https://oss.redis.com/redisbloom/Cuckoo_Commands/#cfreserve + try { + await client.cf.reserve('mycuckoo', 10000); + console.log('Reserved Cuckoo Filter.'); + } catch (e) { + console.log('Error, maybe RedisBloom is not installed?:'); + console.log(e); + } + + // Add items to Cuckoo Filter individually with CF.ADD command. + https://oss.redis.com/redisbloom/Cuckoo_Commands/#cfadd + await Promise.all([ + client.cf.add('mycuckoo', 'leibale'), + client.cf.add('mycuckoo', 'simon'), + client.cf.add('mycuckoo', 'guy'), + client.cf.add('mycuckoo', 'suze'), + client.cf.add('mycuckoo', 'brian'), + client.cf.add('mycuckoo', 'steve'), + client.cf.add('mycuckoo', 'kyle'), + client.cf.add('mycuckoo', 'josefin'), + client.cf.add('mycuckoo', 'alex'), + client.cf.add('mycuckoo', 'nava'), + ]); + + // Add items to the Cuckoo Filter only if they don't exist in it... + // https://oss.redis.com/redisbloom/Cuckoo_Commands/#cfaddnx + const nxReply = await Promise.all([ + client.cf.addNX('mycuckoo', 'kaitlyn'), // New + client.cf.addNX('mycuckoo', 'rachel'), // New + client.cf.addNX('mycuckoo', 'brian') // Previously added + ]); + + console.log('Added members to Cuckoo Filter.'); + console.log('nxReply:'); + + // nxReply looks like this: + // [ + // true, + // true, + // false + // ] + console.log(nxReply); + + // Check whether a member exists with the CF.EXISTS command. + const simonExists = await client.bf.exists('mycuckoo', 'simon'); + console.log(`simon ${simonExists ? 'may' : 'does not'} exist in the Cuckoo Filter.`); + + // Get stats for the Cuckoo Filter with the CF.INFO command: + const info = await client.cf.info('mycuckoo'); + + // info looks like this: + // { + // size: 16440, + // numberOfBuckets: 8192, + // numberOfFilters: 1, + // numberOfInsertedItems: 12, + // numberOfDeletedItems: 0, + // bucketSize: 2, + // expansionRate: 1, + // maxIteration: 20 + // } + console.log(info); + + await client.quit(); +} + +cuckooFilter(); From d602682b643603cfc35581c803f3b2b2f9232bee Mon Sep 17 00:00:00 2001 From: Simon Prickett Date: Wed, 19 Jan 2022 18:35:18 +0000 Subject: [PATCH 1107/1748] Adding a RedisTimeSeries example (#1839) * Adds the start of a timeseries example. * Exports required TimeSeries items. * Fixed import. * Added TS.INFO example output. * Fixed typo. * Fixed typo. * Exported aggregation enum. * Working time series example. Co-authored-by: Leibale Eidelman --- examples/README.md | 1 + examples/time-series.js | 126 ++++++++++++++++++ .../lib/commands/CREATERULE.spec.ts | 4 +- .../lib/commands/DELETERULE.spec.ts | 2 +- .../time-series/lib/commands/MRANGE.spec.ts | 2 +- .../lib/commands/MRANGE_WITHLABELS.spec.ts | 2 +- .../lib/commands/MREVRANGE.spec.ts | 2 +- .../lib/commands/MREVRANGE_WITHLABELS.spec.ts | 2 +- .../time-series/lib/commands/RANGE.spec.ts | 2 +- .../time-series/lib/commands/REVRANGE.spec.ts | 4 +- packages/time-series/lib/commands/index.ts | 2 +- packages/time-series/lib/index.ts | 2 +- 12 files changed, 139 insertions(+), 12 deletions(-) create mode 100644 examples/time-series.js diff --git a/examples/README.md b/examples/README.md index a387b17cb6e..c96571241e2 100644 --- a/examples/README.md +++ b/examples/README.md @@ -17,6 +17,7 @@ This folder contains example scripts showing how to use Node Redis in different | `set-scan.js` | An example script that shows how to use the SSCAN iterator functionality | | `stream-producer.js` | Adds entries to a [Redis Stream](https://redis.io/topics/streams-intro) using the `XADD` command | | `stream-consumer.js` | Reads entries from a [Redis Stream](https://redis.io/topics/streams-intro) using the blocking `XREAD` command | +| `time-series.js` | Create, populate and query timeseries data with [Redis Timeseries](https://redistimeseries.io) | | `topk.js` | Use the [RedisBloom](https://redisbloom.io) TopK to track the most frequently seen items. | ## Contributing diff --git a/examples/time-series.js b/examples/time-series.js new file mode 100644 index 00000000000..2fd27e44117 --- /dev/null +++ b/examples/time-series.js @@ -0,0 +1,126 @@ +// Add data to a Redis TimeSeries and query it. +// Requires the RedisTimeSeries module: https://redistimeseries.io/ + +import { createClient } from 'redis'; +import { TimeSeriesDuplicatePolicies, TimeSeriesEncoding, TimeSeriesAggregationType } from '@node-redis/time-series'; + +async function timeSeries() { + const client = createClient(); + + await client.connect(); + await client.del('mytimeseries'); + + try { + // Create a timeseries + // https://oss.redis.com/redistimeseries/commands/#tscreate + const created = await client.ts.create('mytimeseries', { + RETENTION: 86400000, // 1 day in milliseconds + ENCODING: TimeSeriesEncoding.UNCOMPRESSED, // No compression + DUPLICATE_POLICY: TimeSeriesDuplicatePolicies.BLOCK // No duplicates + }); + + if (created === 'OK') { + console.log('Created timeseries.'); + } else { + console.log('Error creating timeseries :('); + process.exit(1); + } + + let value = Math.floor(Math.random() * 1000) + 1; // Random data point value + let currentTimestamp = 1640995200000; // Jan 1 2022 00:00:00 + let num = 0; + + while (num < 10000) { + // Add a new value to the timeseries, providing our own timestamp: + // https://oss.redis.com/redistimeseries/commands/#tsadd + await client.ts.add('mytimeseries', currentTimestamp, value); + console.log(`Added timestamp ${currentTimestamp}, value ${value}.`); + + num += 1; + value = Math.floor(Math.random() * 1000) + 1; // Get another random value + currentTimestamp += 1000; // Move on one second. + } + + // Add multiple values to the timeseries in round trip to the server: + // https://oss.redis.com/redistimeseries/commands/#tsmadd + const response = await client.ts.mAdd([{ + key: 'mytimeseries', + timestamp: currentTimestamp + 60000, + value: Math.floor(Math.random() * 1000) + 1 + }, { + key: 'mytimeseries', + timestamp: currentTimestamp + 120000, + value: Math.floor(Math.random() * 1000) + 1 + }]); + + // response = array of timestamps added by TS.MADD command. + if (response.length === 2) { + console.log('Added 2 entries to timeseries with TS.MADD.'); + } + + // Update timeseries retention with TS.ALTER: + // https://oss.redis.com/redistimeseries/commands/#tsalter + const alterResponse = await client.ts.alter('mytimeseries', { + RETENTION: 0 // Keep the entries forever + }); + + if (alterResponse === 'OK') { + console.log('Timeseries retention settings altered successfully.'); + } + + // Query the timeseries with TS.RANGE: + // https://oss.redis.com/redistimeseries/commands/#tsrangetsrevrange + const fromTimestamp = 1640995200000; // Jan 1 2022 00:00:00 + const toTimestamp = 1640995260000; // Jan 1 2022 00:01:00 + const rangeResponse = await client.ts.range('mytimeseries', fromTimestamp, toTimestamp, { + // Group into 10 second averages. + AGGREGATION: { + type: TimeSeriesAggregationType.AVERAGE, + timeBucket: 10000 + } + }); + + console.log('RANGE RESPONSE:'); + // rangeResponse looks like: + // [ + // { timestamp: 1640995200000, value: 356.8 }, + // { timestamp: 1640995210000, value: 534.8 }, + // { timestamp: 1640995220000, value: 481.3 }, + // { timestamp: 1640995230000, value: 437 }, + // { timestamp: 1640995240000, value: 507.3 }, + // { timestamp: 1640995250000, value: 581.2 }, + // { timestamp: 1640995260000, value: 600 } + // ] + + console.log(rangeResponse); + + // Get some information about the state of the timeseries. + // https://oss.redis.com/redistimeseries/commands/#tsinfo + const tsInfo = await client.ts.info('mytimeseries'); + + // tsInfo looks like this: + // { + // totalSamples: 1440, + // memoryUsage: 28904, + // firstTimestamp: 1641508920000, + // lastTimestamp: 1641595320000, + // retentionTime: 86400000, + // chunkCount: 7, + // chunkSize: 4096, + // chunkType: 'uncompressed', + // duplicatePolicy: 'block', + // labels: [], + // sourceKey: null, + // rules: [] + // } + + console.log('Timeseries info:'); + console.log(tsInfo); + } catch (e) { + console.error(e); + } + + await client.quit(); +} + +timeSeries(); diff --git a/packages/time-series/lib/commands/CREATERULE.spec.ts b/packages/time-series/lib/commands/CREATERULE.spec.ts index eb338bb8046..09050e23896 100644 --- a/packages/time-series/lib/commands/CREATERULE.spec.ts +++ b/packages/time-series/lib/commands/CREATERULE.spec.ts @@ -6,7 +6,7 @@ import { transformArguments } from './CREATERULE'; describe('CREATERULE', () => { it('transformArguments', () => { assert.deepEqual( - transformArguments('source', 'destination', TimeSeriesAggregationType.AVARAGE, 1), + transformArguments('source', 'destination', TimeSeriesAggregationType.AVERAGE, 1), ['TS.CREATERULE', 'source', 'destination', 'AGGREGATION', 'avg', '1'] ); }); @@ -18,7 +18,7 @@ describe('CREATERULE', () => { ]); assert.equal( - await client.ts.createRule('source', 'destination', TimeSeriesAggregationType.AVARAGE, 1), + await client.ts.createRule('source', 'destination', TimeSeriesAggregationType.AVERAGE, 1), 'OK' ); }, GLOBAL.SERVERS.OPEN); diff --git a/packages/time-series/lib/commands/DELETERULE.spec.ts b/packages/time-series/lib/commands/DELETERULE.spec.ts index 5cbcc8edb54..9364bea711c 100644 --- a/packages/time-series/lib/commands/DELETERULE.spec.ts +++ b/packages/time-series/lib/commands/DELETERULE.spec.ts @@ -15,7 +15,7 @@ describe('DELETERULE', () => { await Promise.all([ client.ts.create('source'), client.ts.create('destination'), - client.ts.createRule('source', 'destination', TimeSeriesAggregationType.AVARAGE, 1) + client.ts.createRule('source', 'destination', TimeSeriesAggregationType.AVERAGE, 1) ]); assert.equal( diff --git a/packages/time-series/lib/commands/MRANGE.spec.ts b/packages/time-series/lib/commands/MRANGE.spec.ts index da01ebb204c..1913576ce6f 100644 --- a/packages/time-series/lib/commands/MRANGE.spec.ts +++ b/packages/time-series/lib/commands/MRANGE.spec.ts @@ -15,7 +15,7 @@ describe('MRANGE', () => { COUNT: 1, ALIGN: '-', AGGREGATION: { - type: TimeSeriesAggregationType.AVARAGE, + type: TimeSeriesAggregationType.AVERAGE, timeBucket: 1 }, GROUPBY: { diff --git a/packages/time-series/lib/commands/MRANGE_WITHLABELS.spec.ts b/packages/time-series/lib/commands/MRANGE_WITHLABELS.spec.ts index e8381a17935..441124ccc90 100644 --- a/packages/time-series/lib/commands/MRANGE_WITHLABELS.spec.ts +++ b/packages/time-series/lib/commands/MRANGE_WITHLABELS.spec.ts @@ -16,7 +16,7 @@ describe('MRANGE_WITHLABELS', () => { COUNT: 1, ALIGN: '-', AGGREGATION: { - type: TimeSeriesAggregationType.AVARAGE, + type: TimeSeriesAggregationType.AVERAGE, timeBucket: 1 }, GROUPBY: { diff --git a/packages/time-series/lib/commands/MREVRANGE.spec.ts b/packages/time-series/lib/commands/MREVRANGE.spec.ts index 08c40d8c60f..764fbb4845e 100644 --- a/packages/time-series/lib/commands/MREVRANGE.spec.ts +++ b/packages/time-series/lib/commands/MREVRANGE.spec.ts @@ -15,7 +15,7 @@ describe('MREVRANGE', () => { COUNT: 1, ALIGN: '-', AGGREGATION: { - type: TimeSeriesAggregationType.AVARAGE, + type: TimeSeriesAggregationType.AVERAGE, timeBucket: 1 }, GROUPBY: { diff --git a/packages/time-series/lib/commands/MREVRANGE_WITHLABELS.spec.ts b/packages/time-series/lib/commands/MREVRANGE_WITHLABELS.spec.ts index a636450bef8..279bcf65283 100644 --- a/packages/time-series/lib/commands/MREVRANGE_WITHLABELS.spec.ts +++ b/packages/time-series/lib/commands/MREVRANGE_WITHLABELS.spec.ts @@ -16,7 +16,7 @@ describe('MREVRANGE_WITHLABELS', () => { COUNT: 1, ALIGN: '-', AGGREGATION: { - type: TimeSeriesAggregationType.AVARAGE, + type: TimeSeriesAggregationType.AVERAGE, timeBucket: 1 }, GROUPBY: { diff --git a/packages/time-series/lib/commands/RANGE.spec.ts b/packages/time-series/lib/commands/RANGE.spec.ts index 15d2706d952..10b2f452bd2 100644 --- a/packages/time-series/lib/commands/RANGE.spec.ts +++ b/packages/time-series/lib/commands/RANGE.spec.ts @@ -15,7 +15,7 @@ describe('RANGE', () => { COUNT: 1, ALIGN: '-', AGGREGATION: { - type: TimeSeriesAggregationType.AVARAGE, + type: TimeSeriesAggregationType.AVERAGE, timeBucket: 1 } }), diff --git a/packages/time-series/lib/commands/REVRANGE.spec.ts b/packages/time-series/lib/commands/REVRANGE.spec.ts index 10a7f4b4234..ae6722dbf45 100644 --- a/packages/time-series/lib/commands/REVRANGE.spec.ts +++ b/packages/time-series/lib/commands/REVRANGE.spec.ts @@ -55,7 +55,7 @@ describe('REVRANGE', () => { assert.deepEqual( transformArguments('key', '-', '+', { AGGREGATION: { - type: TimeSeriesAggregationType.AVARAGE, + type: TimeSeriesAggregationType.AVERAGE, timeBucket: 1 } }), @@ -74,7 +74,7 @@ describe('REVRANGE', () => { COUNT: 1, ALIGN: '-', AGGREGATION: { - type: TimeSeriesAggregationType.AVARAGE, + type: TimeSeriesAggregationType.AVERAGE, timeBucket: 1 } }), diff --git a/packages/time-series/lib/commands/index.ts b/packages/time-series/lib/commands/index.ts index ad9d5962c9f..5836f4aa33c 100644 --- a/packages/time-series/lib/commands/index.ts +++ b/packages/time-series/lib/commands/index.ts @@ -68,7 +68,7 @@ export default { }; export enum TimeSeriesAggregationType { - AVARAGE = 'avg', + AVERAGE = 'avg', SUM = 'sum', MINIMUM = 'min', MAXIMUM = 'max', diff --git a/packages/time-series/lib/index.ts b/packages/time-series/lib/index.ts index 567795c83c6..5530a328cd3 100644 --- a/packages/time-series/lib/index.ts +++ b/packages/time-series/lib/index.ts @@ -1,3 +1,3 @@ export { default } from './commands'; -// TODO +export { TimeSeriesDuplicatePolicies, TimeSeriesEncoding, TimeSeriesAggregationType } from './commands'; From 41f6b009d329a870b45a6e106699924c353ca050 Mon Sep 17 00:00:00 2001 From: Simon Prickett Date: Wed, 19 Jan 2022 18:36:24 +0000 Subject: [PATCH 1108/1748] Add streams XREADGROUP and XACK example. (#1832) * Removed stream delete command to allow consumer group example to work. * Adds stream consumer group example. * Adds stream consumer group example code. * Update README.md Co-authored-by: Leibale Eidelman --- examples/README.md | 1 + examples/stream-consumer-group.js | 104 ++++++++++++++++++++++++++++++ examples/stream-producer.js | 1 - 3 files changed, 105 insertions(+), 1 deletion(-) create mode 100644 examples/stream-consumer-group.js diff --git a/examples/README.md b/examples/README.md index c96571241e2..c3a54c1102a 100644 --- a/examples/README.md +++ b/examples/README.md @@ -19,6 +19,7 @@ This folder contains example scripts showing how to use Node Redis in different | `stream-consumer.js` | Reads entries from a [Redis Stream](https://redis.io/topics/streams-intro) using the blocking `XREAD` command | | `time-series.js` | Create, populate and query timeseries data with [Redis Timeseries](https://redistimeseries.io) | | `topk.js` | Use the [RedisBloom](https://redisbloom.io) TopK to track the most frequently seen items. | +| `stream-consumer-group.js` | Reads entties from a [Redis Stream](https://redis.io/topics/streams-intro) as part of a consumer group using the blocking `XREADGROUP` command | ## Contributing diff --git a/examples/stream-consumer-group.js b/examples/stream-consumer-group.js new file mode 100644 index 00000000000..0dc8ff20fef --- /dev/null +++ b/examples/stream-consumer-group.js @@ -0,0 +1,104 @@ +// A sample stream consumer using the blocking variant of XREADGROUP. +// This consumer works in collaboration with other instances of itself +// in the same consumer group such that the group as a whole receives +// every entry from the stream. +// +// This consumes entries from a stream created by stream-producer.js +// +// Run this as follows: +// +// $ node stream-consumer-group.js +// +// Run multiple instances with different values of +// to see them processing the stream as a group: +// +// $ node stream-consumer-group.js consumer1 +// +// In another terminal: +// +// $ node stream-consumer-group.js consumer2 + +import { createClient, commandOptions } from 'redis'; + +async function streamConsumerGroup() { + const client = createClient(); + + if (process.argv.length !== 3) { + console.log(`usage: node stream-consumer-group.js `); + process.exit(1); + } + + const consumerName = process.argv[2]; + + await client.connect(); + + // Create the consumer group (and stream) if needed... + try { + await client.xGroupCreate('mystream', 'myconsumergroup', '0', { + MKSTREAM: true + }); + console.log('Created consumer group.'); + } catch (e) { + console.log('Consumer group already exists, skipped creation.'); + } + + console.log(`Starting consumer ${consumerName}.`); + + while (true) { + try { + let response = await client.xReadGroup( + commandOptions({ + isolated: true + }), + 'myconsumergroup', + consumerName, [ + // XREADGROUP can read from multiple streams, starting at a + // different ID for each... + { + key: 'mystream', + id: '>' // Next entry ID that no consumer in this group has read + } + ], { + // Read 1 entry at a time, block for 5 seconds if there are none. + COUNT: 1, + BLOCK: 5000 + } + ); + + if (response) { + // Response is an array of streams, each containing an array of + // entries: + // + // [ + // { + // "name": "mystream", + // "messages": [ + // { + // "id": "1642088708425-0", + // "message": { + // "num": "999" + // } + // } + // ] + // } + // ] + console.log(JSON.stringify(response)); + + // Use XACK to acknowledge successful processing of this + // stream entry. + const entryId = response[0].messages[0].id; + await client.xAck('mystream', 'myconsumergroup', entryId); + + console.log(`Acknowledged processing of entry ${entryId}.`); + } else { + // Response is null, we have read everything that is + // in the stream right now... + console.log('No new stream entries.'); + } + } catch (err) { + console.error(err); + } + } +} + +streamConsumerGroup(); diff --git a/examples/stream-producer.js b/examples/stream-producer.js index 3f7a4a963c9..42c5d14bb22 100644 --- a/examples/stream-producer.js +++ b/examples/stream-producer.js @@ -6,7 +6,6 @@ async function streamProducer() { const client = createClient(); await client.connect(); - await client.del('mystream'); let num = 0; From bd1e500e14b12658e3083118a55e1a549e4cf1fe Mon Sep 17 00:00:00 2001 From: Simon Prickett Date: Thu, 20 Jan 2022 16:54:36 +0000 Subject: [PATCH 1109/1748] Adds timeseries overview. (#1853) --- packages/time-series/README.md | 6 +++++- 1 file changed, 5 insertions(+), 1 deletion(-) diff --git a/packages/time-series/README.md b/packages/time-series/README.md index b02e2648a59..5572fdb07ee 100644 --- a/packages/time-series/README.md +++ b/packages/time-series/README.md @@ -1,3 +1,7 @@ # @node-redis/time-series -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 [RedisTimeSeries](https://redistimeseries.io) module, which adds a time series data structure to Redis. It extends the [Node Redis client](https://github.com/redis/node-redis) to include functions for each of the RedisTimeSeries commands. + +To use these extra commands, your Redis server must have the RedisTimeSeries module installed. + +For an example of how to add values to a time series, query a time series, and perform aggregated queries against a time series, see `time-series.js` in the Node Redis examples folder. From 59c0fd0259f334bf7036144f0a67bda294884eee Mon Sep 17 00:00:00 2001 From: Leibale Eidelman Date: Thu, 20 Jan 2022 15:39:21 -0500 Subject: [PATCH 1110/1748] Create dependabot.yml --- .github/dependabot.yml | 6 ++++++ 1 file changed, 6 insertions(+) create mode 100644 .github/dependabot.yml diff --git a/.github/dependabot.yml b/.github/dependabot.yml new file mode 100644 index 00000000000..122534f01a5 --- /dev/null +++ b/.github/dependabot.yml @@ -0,0 +1,6 @@ +version: 2 +updates: + - package-ecosystem: 'npm' + directory: '/' + schedule: + interval: 'daily' From 47200a5244deb54bbfbcdbfe4f8dd335de146b15 Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Thu, 20 Jan 2022 15:49:35 -0500 Subject: [PATCH 1111/1748] Bump mocha from 9.1.3 to 9.1.4 (#1857) Bumps [mocha](https://github.com/mochajs/mocha) from 9.1.3 to 9.1.4. - [Release notes](https://github.com/mochajs/mocha/releases) - [Changelog](https://github.com/mochajs/mocha/blob/master/CHANGELOG.md) - [Commits](https://github.com/mochajs/mocha/compare/v9.1.3...v9.1.4) --- updated-dependencies: - dependency-name: mocha dependency-type: direct:development update-type: version-update:semver-patch ... Signed-off-by: dependabot[bot] Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com> --- package-lock.json | 18 +++++++++--------- 1 file changed, 9 insertions(+), 9 deletions(-) diff --git a/package-lock.json b/package-lock.json index 4627f1ae5e3..a52a939a28c 100644 --- a/package-lock.json +++ b/package-lock.json @@ -13,10 +13,10 @@ ], "dependencies": { "@node-redis/bloom": "^1.0.0", - "@node-redis/client": "^1.0.1", - "@node-redis/json": "^1.0.1", - "@node-redis/search": "^1.0.1", - "@node-redis/time-series": "^1.0.0" + "@node-redis/client": "^1.0.2", + "@node-redis/json": "^1.0.2", + "@node-redis/search": "^1.0.2", + "@node-redis/time-series": "^1.0.1" }, "devDependencies": { "@tsconfig/node12": "^1.0.9", @@ -6520,7 +6520,7 @@ }, "packages/bloom": { "name": "@node-redis/bloom", - "version": "1.0.0", + "version": "1.0.1", "license": "MIT", "devDependencies": { "@istanbuljs/nyc-config-typescript": "^1.0.2", @@ -6539,7 +6539,7 @@ }, "packages/client": { "name": "@node-redis/client", - "version": "1.0.1", + "version": "1.0.2", "license": "MIT", "dependencies": { "cluster-key-slot": "1.1.0", @@ -6571,7 +6571,7 @@ }, "packages/json": { "name": "@node-redis/json", - "version": "1.0.1", + "version": "1.0.2", "license": "MIT", "devDependencies": { "@istanbuljs/nyc-config-typescript": "^1.0.2", @@ -6590,7 +6590,7 @@ }, "packages/search": { "name": "@node-redis/search", - "version": "1.0.1", + "version": "1.0.2", "license": "MIT", "devDependencies": { "@istanbuljs/nyc-config-typescript": "^1.0.2", @@ -6627,7 +6627,7 @@ }, "packages/time-series": { "name": "@node-redis/time-series", - "version": "1.0.0-rc.0", + "version": "1.0.1", "license": "MIT", "devDependencies": { "@istanbuljs/nyc-config-typescript": "^1.0.2", From 3ae275ecdcc45550ad675aac764fe4eb159a7101 Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Thu, 20 Jan 2022 15:50:33 -0500 Subject: [PATCH 1112/1748] Bump typedoc from 0.22.10 to 0.22.11 (#1860) Bumps [typedoc](https://github.com/TypeStrong/TypeDoc) from 0.22.10 to 0.22.11. - [Release notes](https://github.com/TypeStrong/TypeDoc/releases) - [Changelog](https://github.com/TypeStrong/typedoc/blob/master/CHANGELOG.md) - [Commits](https://github.com/TypeStrong/TypeDoc/compare/v0.22.10...v0.22.11) --- updated-dependencies: - dependency-name: typedoc dependency-type: direct:development update-type: version-update:semver-patch ... Signed-off-by: dependabot[bot] Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com> From 7e6d4b56d4e26c4cdb5463688f9f640e1d72f47d Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Thu, 20 Jan 2022 15:50:39 -0500 Subject: [PATCH 1113/1748] Bump eslint from 8.6.0 to 8.7.0 (#1859) Bumps [eslint](https://github.com/eslint/eslint) from 8.6.0 to 8.7.0. - [Release notes](https://github.com/eslint/eslint/releases) - [Changelog](https://github.com/eslint/eslint/blob/main/CHANGELOG.md) - [Commits](https://github.com/eslint/eslint/compare/v8.6.0...v8.7.0) --- updated-dependencies: - dependency-name: eslint dependency-type: direct:development update-type: version-update:semver-minor ... Signed-off-by: dependabot[bot] Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com> From 415a10c5c3741b0bfe6c8529126c80b5a025f70e Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Thu, 20 Jan 2022 15:50:47 -0500 Subject: [PATCH 1114/1748] Bump typescript from 4.5.4 to 4.5.5 (#1858) Bumps [typescript](https://github.com/Microsoft/TypeScript) from 4.5.4 to 4.5.5. - [Release notes](https://github.com/Microsoft/TypeScript/releases) - [Commits](https://github.com/Microsoft/TypeScript/commits) --- updated-dependencies: - dependency-name: typescript dependency-type: direct:development update-type: version-update:semver-patch ... Signed-off-by: dependabot[bot] Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com> --- package-lock.json | 12 ++++++------ 1 file changed, 6 insertions(+), 6 deletions(-) diff --git a/package-lock.json b/package-lock.json index a52a939a28c..e0830d7cc05 100644 --- a/package-lock.json +++ b/package-lock.json @@ -6072,9 +6072,9 @@ } }, "node_modules/typescript": { - "version": "4.5.4", - "resolved": "https://registry.npmjs.org/typescript/-/typescript-4.5.4.tgz", - "integrity": "sha512-VgYs2A2QIRuGphtzFV7aQJduJ2gyfTljngLzjpfW9FoYZF6xuw1W0vW9ghCKLfcWrCFxK81CSGRAvS1pn4fIUg==", + "version": "4.5.5", + "resolved": "https://registry.npmjs.org/typescript/-/typescript-4.5.5.tgz", + "integrity": "sha512-TCTIul70LyWe6IJWT8QSYeA54WQe8EjQFU4wY52Fasj5UKx88LNYKCgBEHcOMOrFF1rKGbD8v/xcNWVUq9SymA==", "dev": true, "bin": { "tsc": "bin/tsc", @@ -11247,9 +11247,9 @@ } }, "typescript": { - "version": "4.5.4", - "resolved": "https://registry.npmjs.org/typescript/-/typescript-4.5.4.tgz", - "integrity": "sha512-VgYs2A2QIRuGphtzFV7aQJduJ2gyfTljngLzjpfW9FoYZF6xuw1W0vW9ghCKLfcWrCFxK81CSGRAvS1pn4fIUg==", + "version": "4.5.5", + "resolved": "https://registry.npmjs.org/typescript/-/typescript-4.5.5.tgz", + "integrity": "sha512-TCTIul70LyWe6IJWT8QSYeA54WQe8EjQFU4wY52Fasj5UKx88LNYKCgBEHcOMOrFF1rKGbD8v/xcNWVUq9SymA==", "dev": true }, "unique-string": { From eb262492e9a32a7730a5e3b40697ddb94b477ce5 Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Thu, 20 Jan 2022 15:50:53 -0500 Subject: [PATCH 1115/1748] Bump @types/node from 17.0.8 to 17.0.10 (#1861) Bumps [@types/node](https://github.com/DefinitelyTyped/DefinitelyTyped/tree/HEAD/types/node) from 17.0.8 to 17.0.10. - [Release notes](https://github.com/DefinitelyTyped/DefinitelyTyped/releases) - [Commits](https://github.com/DefinitelyTyped/DefinitelyTyped/commits/HEAD/types/node) --- updated-dependencies: - dependency-name: "@types/node" dependency-type: direct:development update-type: version-update:semver-patch ... Signed-off-by: dependabot[bot] Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com> From 76c137b5c89c4ef877a64084a8aefea4a9c64fdb Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Thu, 20 Jan 2022 15:55:34 -0500 Subject: [PATCH 1116/1748] Bump release-it from 14.12.1 to 14.12.3 (#1862) Bumps [release-it](https://github.com/release-it/release-it) from 14.12.1 to 14.12.3. - [Release notes](https://github.com/release-it/release-it/releases) - [Changelog](https://github.com/release-it/release-it/blob/master/CHANGELOG.md) - [Commits](https://github.com/release-it/release-it/compare/14.12.1...14.12.3) --- updated-dependencies: - dependency-name: release-it dependency-type: direct:development update-type: version-update:semver-patch ... Signed-off-by: dependabot[bot] Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com> --- package-lock.json | 28 ++++++++++++++-------------- 1 file changed, 14 insertions(+), 14 deletions(-) diff --git a/package-lock.json b/package-lock.json index e0830d7cc05..baf331383ee 100644 --- a/package-lock.json +++ b/package-lock.json @@ -5268,9 +5268,9 @@ } }, "node_modules/release-it": { - "version": "14.12.1", - "resolved": "https://registry.npmjs.org/release-it/-/release-it-14.12.1.tgz", - "integrity": "sha512-dYPGZ7F/kfIWzsGlzNCL6PiWfPoaVUILcmqQm80kgYhI/b9RW3k6DVqE0nqI4fHxRT3fMeKWWvS0jdQmFDKn3Q==", + "version": "14.12.3", + "resolved": "https://registry.npmjs.org/release-it/-/release-it-14.12.3.tgz", + "integrity": "sha512-qek7ml9WaxpXSjLpU4UGCF9nWpDgOODL1gZTuydafs1HdQPAeYOd2od8I8lUL4NlEKW2TirDhH4aFTVIpP3/cQ==", "dev": true, "dependencies": { "@iarna/toml": "2.2.5", @@ -5296,7 +5296,7 @@ "os-name": "4.0.1", "parse-json": "5.2.0", "semver": "7.3.5", - "shelljs": "0.8.4", + "shelljs": "0.8.5", "update-notifier": "5.1.0", "url-join": "4.0.1", "uuid": "8.3.2", @@ -5594,9 +5594,9 @@ } }, "node_modules/shelljs": { - "version": "0.8.4", - "resolved": "https://registry.npmjs.org/shelljs/-/shelljs-0.8.4.tgz", - "integrity": "sha512-7gk3UZ9kOfPLIAbslLzyWeGiEqx9e3rxwZM0KE6EL8GlGwjym9Mrlx5/p33bWTu9YG6vcS4MBxYZDHYr5lr8BQ==", + "version": "0.8.5", + "resolved": "https://registry.npmjs.org/shelljs/-/shelljs-0.8.5.tgz", + "integrity": "sha512-TiwcRcrkhHvbrZbnRcFYMLl30Dfov3HKqzp5tO5b4pt6G/SezKcYhmDg15zXVBswHmctSAQKznqNW2LO5tTDow==", "dev": true, "dependencies": { "glob": "^7.0.0", @@ -10655,9 +10655,9 @@ } }, "release-it": { - "version": "14.12.1", - "resolved": "https://registry.npmjs.org/release-it/-/release-it-14.12.1.tgz", - "integrity": "sha512-dYPGZ7F/kfIWzsGlzNCL6PiWfPoaVUILcmqQm80kgYhI/b9RW3k6DVqE0nqI4fHxRT3fMeKWWvS0jdQmFDKn3Q==", + "version": "14.12.3", + "resolved": "https://registry.npmjs.org/release-it/-/release-it-14.12.3.tgz", + "integrity": "sha512-qek7ml9WaxpXSjLpU4UGCF9nWpDgOODL1gZTuydafs1HdQPAeYOd2od8I8lUL4NlEKW2TirDhH4aFTVIpP3/cQ==", "dev": true, "requires": { "@iarna/toml": "2.2.5", @@ -10683,7 +10683,7 @@ "os-name": "4.0.1", "parse-json": "5.2.0", "semver": "7.3.5", - "shelljs": "0.8.4", + "shelljs": "0.8.5", "update-notifier": "5.1.0", "url-join": "4.0.1", "uuid": "8.3.2", @@ -10888,9 +10888,9 @@ "dev": true }, "shelljs": { - "version": "0.8.4", - "resolved": "https://registry.npmjs.org/shelljs/-/shelljs-0.8.4.tgz", - "integrity": "sha512-7gk3UZ9kOfPLIAbslLzyWeGiEqx9e3rxwZM0KE6EL8GlGwjym9Mrlx5/p33bWTu9YG6vcS4MBxYZDHYr5lr8BQ==", + "version": "0.8.5", + "resolved": "https://registry.npmjs.org/shelljs/-/shelljs-0.8.5.tgz", + "integrity": "sha512-TiwcRcrkhHvbrZbnRcFYMLl30Dfov3HKqzp5tO5b4pt6G/SezKcYhmDg15zXVBswHmctSAQKznqNW2LO5tTDow==", "dev": true, "requires": { "glob": "^7.0.0", From de16a8d2641c0b9f3a9e33c3353e41208c4be612 Mon Sep 17 00:00:00 2001 From: Leibale Eidelman Date: Thu, 20 Jan 2022 16:10:54 -0500 Subject: [PATCH 1117/1748] Update dependabot.yml --- .github/dependabot.yml | 30 ++++++++++++++++++++++++++++++ 1 file changed, 30 insertions(+) diff --git a/.github/dependabot.yml b/.github/dependabot.yml index 122534f01a5..e3517b00632 100644 --- a/.github/dependabot.yml +++ b/.github/dependabot.yml @@ -4,3 +4,33 @@ updates: directory: '/' schedule: interval: 'daily' + + - package-ecosystem: 'npm' + directory: '/packages/bloom' + schedule: + interval: 'daily' + + - package-ecosystem: 'npm' + directory: '/packages/client' + schedule: + interval: 'daily' + + - package-ecosystem: 'npm' + directory: '/packages/json' + schedule: + interval: 'daily' + + - package-ecosystem: 'npm' + directory: '/packages/search' + schedule: + interval: 'daily' + + - package-ecosystem: 'npm' + directory: '/packages/test-utils' + schedule: + interval: 'daily' + + - package-ecosystem: 'npm' + directory: '/packages/time-series' + schedule: + interval: 'daily' From bac674437f4b4875d0445a11d73b0030a97791d6 Mon Sep 17 00:00:00 2001 From: Leibale Eidelman Date: Thu, 20 Jan 2022 16:14:17 -0500 Subject: [PATCH 1118/1748] Delete dependabot.yml --- .github/dependabot.yml | 36 ------------------------------------ 1 file changed, 36 deletions(-) delete mode 100644 .github/dependabot.yml diff --git a/.github/dependabot.yml b/.github/dependabot.yml deleted file mode 100644 index e3517b00632..00000000000 --- a/.github/dependabot.yml +++ /dev/null @@ -1,36 +0,0 @@ -version: 2 -updates: - - package-ecosystem: 'npm' - directory: '/' - schedule: - interval: 'daily' - - - package-ecosystem: 'npm' - directory: '/packages/bloom' - schedule: - interval: 'daily' - - - package-ecosystem: 'npm' - directory: '/packages/client' - schedule: - interval: 'daily' - - - package-ecosystem: 'npm' - directory: '/packages/json' - schedule: - interval: 'daily' - - - package-ecosystem: 'npm' - directory: '/packages/search' - schedule: - interval: 'daily' - - - package-ecosystem: 'npm' - directory: '/packages/test-utils' - schedule: - interval: 'daily' - - - package-ecosystem: 'npm' - directory: '/packages/time-series' - schedule: - interval: 'daily' From a2299509a0ac1ebabf486a0e4ad542fb087b7196 Mon Sep 17 00:00:00 2001 From: Leibale Eidelman Date: Thu, 20 Jan 2022 16:23:31 -0500 Subject: [PATCH 1119/1748] upgrade dependencies (#1863) --- package-lock.json | 527 +++++++++++++----------------- packages/bloom/package.json | 8 +- packages/client/package.json | 16 +- packages/json/package.json | 8 +- packages/search/package.json | 8 +- packages/test-utils/package.json | 8 +- packages/time-series/package.json | 8 +- 7 files changed, 251 insertions(+), 332 deletions(-) diff --git a/package-lock.json b/package-lock.json index baf331383ee..3aeb01fe877 100644 --- a/package-lock.json +++ b/package-lock.json @@ -870,15 +870,15 @@ } }, "node_modules/@types/mocha": { - "version": "9.0.0", - "resolved": "https://registry.npmjs.org/@types/mocha/-/mocha-9.0.0.tgz", - "integrity": "sha512-scN0hAWyLVAvLR9AyW7HoFF5sJZglyBsbPuHO4fv7JRvfmPBMfp1ozWqOf/e4wwPNxezBZXRfWzMb6iFLgEVRA==", + "version": "9.1.0", + "resolved": "https://registry.npmjs.org/@types/mocha/-/mocha-9.1.0.tgz", + "integrity": "sha512-QCWHkbMv4Y5U9oW10Uxbr45qMMSzl4OzijsozynUAgx3kEHUdXB00udx2dWDQ7f2TU2a2uuiFaRZjCe3unPpeg==", "dev": true }, "node_modules/@types/node": { - "version": "17.0.8", - "resolved": "https://registry.npmjs.org/@types/node/-/node-17.0.8.tgz", - "integrity": "sha512-YofkM6fGv4gDJq78g4j0mMuGMkZVxZDgtU0JRdx6FgiJDG+0fY0GKVolOV8WqVmEhLCXkQRjwDdKyPxJp/uucg==", + "version": "17.0.10", + "resolved": "https://registry.npmjs.org/@types/node/-/node-17.0.10.tgz", + "integrity": "sha512-S/3xB4KzyFxYGCppyDt68yzBU9ysL88lSdIah4D6cptdcltc4NCPCAMc0+PCpg/lLIyC7IPvj2Z52OJWeIUkog==", "dev": true }, "node_modules/@types/parse-json": { @@ -913,9 +913,9 @@ } }, "node_modules/@types/sinon": { - "version": "10.0.6", - "resolved": "https://registry.npmjs.org/@types/sinon/-/sinon-10.0.6.tgz", - "integrity": "sha512-6EF+wzMWvBNeGrfP3Nx60hhx+FfwSg1JJBLAAP/IdIUq0EYkqCYf70VT3PhuhPX9eLD+Dp+lNdpb/ZeHG8Yezg==", + "version": "10.0.8", + "resolved": "https://registry.npmjs.org/@types/sinon/-/sinon-10.0.8.tgz", + "integrity": "sha512-XZbSLlox2KM7VaEJPZ5G/fMZXJNuAtYiFOax7UT51quZMAJRWKvugPMqNA0mV3jC9HIYpQSg6qbV+ilQMwLqyA==", "dev": true, "dependencies": { "@sinonjs/fake-timers": "^7.1.0" @@ -943,14 +943,14 @@ "dev": true }, "node_modules/@typescript-eslint/eslint-plugin": { - "version": "5.9.1", - "resolved": "https://registry.npmjs.org/@typescript-eslint/eslint-plugin/-/eslint-plugin-5.9.1.tgz", - "integrity": "sha512-Xv9tkFlyD4MQGpJgTo6wqDqGvHIRmRgah/2Sjz1PUnJTawjHWIwBivUE9x0QtU2WVii9baYgavo/bHjrZJkqTw==", + "version": "5.10.0", + "resolved": "https://registry.npmjs.org/@typescript-eslint/eslint-plugin/-/eslint-plugin-5.10.0.tgz", + "integrity": "sha512-XXVKnMsq2fuu9K2KsIxPUGqb6xAImz8MEChClbXmE3VbveFtBUU5bzM6IPVWqzyADIgdkS2Ws/6Xo7W2TeZWjQ==", "dev": true, "dependencies": { - "@typescript-eslint/experimental-utils": "5.9.1", - "@typescript-eslint/scope-manager": "5.9.1", - "@typescript-eslint/type-utils": "5.9.1", + "@typescript-eslint/scope-manager": "5.10.0", + "@typescript-eslint/type-utils": "5.10.0", + "@typescript-eslint/utils": "5.10.0", "debug": "^4.3.2", "functional-red-black-tree": "^1.0.1", "ignore": "^5.1.8", @@ -990,39 +990,15 @@ "node": ">=10" } }, - "node_modules/@typescript-eslint/experimental-utils": { - "version": "5.9.1", - "resolved": "https://registry.npmjs.org/@typescript-eslint/experimental-utils/-/experimental-utils-5.9.1.tgz", - "integrity": "sha512-cb1Njyss0mLL9kLXgS/eEY53SZQ9sT519wpX3i+U457l2UXRDuo87hgKfgRazmu9/tQb0x2sr3Y0yrU+Zz0y+w==", - "dev": true, - "dependencies": { - "@types/json-schema": "^7.0.9", - "@typescript-eslint/scope-manager": "5.9.1", - "@typescript-eslint/types": "5.9.1", - "@typescript-eslint/typescript-estree": "5.9.1", - "eslint-scope": "^5.1.1", - "eslint-utils": "^3.0.0" - }, - "engines": { - "node": "^12.22.0 || ^14.17.0 || >=16.0.0" - }, - "funding": { - "type": "opencollective", - "url": "https://opencollective.com/typescript-eslint" - }, - "peerDependencies": { - "eslint": "^6.0.0 || ^7.0.0 || ^8.0.0" - } - }, "node_modules/@typescript-eslint/parser": { - "version": "5.9.1", - "resolved": "https://registry.npmjs.org/@typescript-eslint/parser/-/parser-5.9.1.tgz", - "integrity": "sha512-PLYO0AmwD6s6n0ZQB5kqPgfvh73p0+VqopQQLuNfi7Lm0EpfKyDalchpVwkE+81k5HeiRrTV/9w1aNHzjD7C4g==", + "version": "5.10.0", + "resolved": "https://registry.npmjs.org/@typescript-eslint/parser/-/parser-5.10.0.tgz", + "integrity": "sha512-pJB2CCeHWtwOAeIxv8CHVGJhI5FNyJAIpx5Pt72YkK3QfEzt6qAlXZuyaBmyfOdM62qU0rbxJzNToPTVeJGrQw==", "dev": true, "dependencies": { - "@typescript-eslint/scope-manager": "5.9.1", - "@typescript-eslint/types": "5.9.1", - "@typescript-eslint/typescript-estree": "5.9.1", + "@typescript-eslint/scope-manager": "5.10.0", + "@typescript-eslint/types": "5.10.0", + "@typescript-eslint/typescript-estree": "5.10.0", "debug": "^4.3.2" }, "engines": { @@ -1042,13 +1018,13 @@ } }, "node_modules/@typescript-eslint/scope-manager": { - "version": "5.9.1", - "resolved": "https://registry.npmjs.org/@typescript-eslint/scope-manager/-/scope-manager-5.9.1.tgz", - "integrity": "sha512-8BwvWkho3B/UOtzRyW07ffJXPaLSUKFBjpq8aqsRvu6HdEuzCY57+ffT7QoV4QXJXWSU1+7g3wE4AlgImmQ9pQ==", + "version": "5.10.0", + "resolved": "https://registry.npmjs.org/@typescript-eslint/scope-manager/-/scope-manager-5.10.0.tgz", + "integrity": "sha512-tgNgUgb4MhqK6DoKn3RBhyZ9aJga7EQrw+2/OiDk5hKf3pTVZWyqBi7ukP+Z0iEEDMF5FDa64LqODzlfE4O/Dg==", "dev": true, "dependencies": { - "@typescript-eslint/types": "5.9.1", - "@typescript-eslint/visitor-keys": "5.9.1" + "@typescript-eslint/types": "5.10.0", + "@typescript-eslint/visitor-keys": "5.10.0" }, "engines": { "node": "^12.22.0 || ^14.17.0 || >=16.0.0" @@ -1059,12 +1035,12 @@ } }, "node_modules/@typescript-eslint/type-utils": { - "version": "5.9.1", - "resolved": "https://registry.npmjs.org/@typescript-eslint/type-utils/-/type-utils-5.9.1.tgz", - "integrity": "sha512-tRSpdBnPRssjlUh35rE9ug5HrUvaB9ntREy7gPXXKwmIx61TNN7+l5YKgi1hMKxo5NvqZCfYhA5FvyuJG6X6vg==", + "version": "5.10.0", + "resolved": "https://registry.npmjs.org/@typescript-eslint/type-utils/-/type-utils-5.10.0.tgz", + "integrity": "sha512-TzlyTmufJO5V886N+hTJBGIfnjQDQ32rJYxPaeiyWKdjsv2Ld5l8cbS7pxim4DeNs62fKzRSt8Q14Evs4JnZyQ==", "dev": true, "dependencies": { - "@typescript-eslint/experimental-utils": "5.9.1", + "@typescript-eslint/utils": "5.10.0", "debug": "^4.3.2", "tsutils": "^3.21.0" }, @@ -1085,9 +1061,9 @@ } }, "node_modules/@typescript-eslint/types": { - "version": "5.9.1", - "resolved": "https://registry.npmjs.org/@typescript-eslint/types/-/types-5.9.1.tgz", - "integrity": "sha512-SsWegWudWpkZCwwYcKoDwuAjoZXnM1y2EbEerTHho19Hmm+bQ56QG4L4jrtCu0bI5STaRTvRTZmjprWlTw/5NQ==", + "version": "5.10.0", + "resolved": "https://registry.npmjs.org/@typescript-eslint/types/-/types-5.10.0.tgz", + "integrity": "sha512-wUljCgkqHsMZbw60IbOqT/puLfyqqD5PquGiBo1u1IS3PLxdi3RDGlyf032IJyh+eQoGhz9kzhtZa+VC4eWTlQ==", "dev": true, "engines": { "node": "^12.22.0 || ^14.17.0 || >=16.0.0" @@ -1098,13 +1074,13 @@ } }, "node_modules/@typescript-eslint/typescript-estree": { - "version": "5.9.1", - "resolved": "https://registry.npmjs.org/@typescript-eslint/typescript-estree/-/typescript-estree-5.9.1.tgz", - "integrity": "sha512-gL1sP6A/KG0HwrahVXI9fZyeVTxEYV//6PmcOn1tD0rw8VhUWYeZeuWHwwhnewnvEMcHjhnJLOBhA9rK4vmb8A==", + "version": "5.10.0", + "resolved": "https://registry.npmjs.org/@typescript-eslint/typescript-estree/-/typescript-estree-5.10.0.tgz", + "integrity": "sha512-x+7e5IqfwLwsxTdliHRtlIYkgdtYXzE0CkFeV6ytAqq431ZyxCFzNMNR5sr3WOlIG/ihVZr9K/y71VHTF/DUQA==", "dev": true, "dependencies": { - "@typescript-eslint/types": "5.9.1", - "@typescript-eslint/visitor-keys": "5.9.1", + "@typescript-eslint/types": "5.10.0", + "@typescript-eslint/visitor-keys": "5.10.0", "debug": "^4.3.2", "globby": "^11.0.4", "is-glob": "^4.0.3", @@ -1168,13 +1144,37 @@ "node": ">=10" } }, + "node_modules/@typescript-eslint/utils": { + "version": "5.10.0", + "resolved": "https://registry.npmjs.org/@typescript-eslint/utils/-/utils-5.10.0.tgz", + "integrity": "sha512-IGYwlt1CVcFoE2ueW4/ioEwybR60RAdGeiJX/iDAw0t5w0wK3S7QncDwpmsM70nKgGTuVchEWB8lwZwHqPAWRg==", + "dev": true, + "dependencies": { + "@types/json-schema": "^7.0.9", + "@typescript-eslint/scope-manager": "5.10.0", + "@typescript-eslint/types": "5.10.0", + "@typescript-eslint/typescript-estree": "5.10.0", + "eslint-scope": "^5.1.1", + "eslint-utils": "^3.0.0" + }, + "engines": { + "node": "^12.22.0 || ^14.17.0 || >=16.0.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/typescript-eslint" + }, + "peerDependencies": { + "eslint": "^6.0.0 || ^7.0.0 || ^8.0.0" + } + }, "node_modules/@typescript-eslint/visitor-keys": { - "version": "5.9.1", - "resolved": "https://registry.npmjs.org/@typescript-eslint/visitor-keys/-/visitor-keys-5.9.1.tgz", - "integrity": "sha512-Xh37pNz9e9ryW4TVdwiFzmr4hloty8cFj8GTWMXh3Z8swGwyQWeCcNgF0hm6t09iZd6eiZmIf4zHedQVP6TVtg==", + "version": "5.10.0", + "resolved": "https://registry.npmjs.org/@typescript-eslint/visitor-keys/-/visitor-keys-5.10.0.tgz", + "integrity": "sha512-GMxj0K1uyrFLPKASLmZzCuSddmjZVbVj3Ouy5QVuIGKZopxvOr24JsS7gruz6C3GExE01mublZ3mIBOaon9zuQ==", "dev": true, "dependencies": { - "@typescript-eslint/types": "5.9.1", + "@typescript-eslint/types": "5.10.0", "eslint-visitor-keys": "^3.0.0" }, "engines": { @@ -2141,18 +2141,6 @@ "once": "^1.4.0" } }, - "node_modules/enquirer": { - "version": "2.3.6", - "resolved": "https://registry.npmjs.org/enquirer/-/enquirer-2.3.6.tgz", - "integrity": "sha512-yjNnPr315/FjS4zIsUxYguYUPP2e1NK4d7E7ZOLiyYCcbFBiTMyID+2wvm2w6+pZ/odMA7cRkjhsPbltwBOrLg==", - "dev": true, - "dependencies": { - "ansi-colors": "^4.1.1" - }, - "engines": { - "node": ">=8.6" - } - }, "node_modules/error-ex": { "version": "1.3.2", "resolved": "https://registry.npmjs.org/error-ex/-/error-ex-1.3.2.tgz", @@ -2196,9 +2184,9 @@ } }, "node_modules/eslint": { - "version": "8.6.0", - "resolved": "https://registry.npmjs.org/eslint/-/eslint-8.6.0.tgz", - "integrity": "sha512-UvxdOJ7mXFlw7iuHZA4jmzPaUqIw54mZrv+XPYKNbKdLR0et4rf60lIZUU9kiNtnzzMzGWxMV+tQ7uG7JG8DPw==", + "version": "8.7.0", + "resolved": "https://registry.npmjs.org/eslint/-/eslint-8.7.0.tgz", + "integrity": "sha512-ifHYzkBGrzS2iDU7KjhCAVMGCvF6M3Xfs8X8b37cgrUlDt6bWRTpRh6T/gtSXv1HJ/BUGgmjvNvOEGu85Iif7w==", "dev": true, "dependencies": { "@eslint/eslintrc": "^1.0.5", @@ -2208,11 +2196,10 @@ "cross-spawn": "^7.0.2", "debug": "^4.3.2", "doctrine": "^3.0.0", - "enquirer": "^2.3.5", "escape-string-regexp": "^4.0.0", "eslint-scope": "^7.1.0", "eslint-utils": "^3.0.0", - "eslint-visitor-keys": "^3.1.0", + "eslint-visitor-keys": "^3.2.0", "espree": "^9.3.0", "esquery": "^1.4.0", "esutils": "^2.0.2", @@ -2221,7 +2208,7 @@ "functional-red-black-tree": "^1.0.1", "glob-parent": "^6.0.1", "globals": "^13.6.0", - "ignore": "^4.0.6", + "ignore": "^5.2.0", "import-fresh": "^3.0.0", "imurmurhash": "^0.1.4", "is-glob": "^4.0.0", @@ -2232,9 +2219,7 @@ "minimatch": "^3.0.4", "natural-compare": "^1.4.0", "optionator": "^0.9.1", - "progress": "^2.0.0", "regexpp": "^3.2.0", - "semver": "^7.2.1", "strip-ansi": "^6.0.1", "strip-json-comments": "^3.1.0", "text-table": "^0.2.0", @@ -2291,9 +2276,9 @@ } }, "node_modules/eslint-visitor-keys": { - "version": "3.1.0", - "resolved": "https://registry.npmjs.org/eslint-visitor-keys/-/eslint-visitor-keys-3.1.0.tgz", - "integrity": "sha512-yWJFpu4DtjsWKkt5GeNBBuZMlNcYVs6vRCLoCVEJrTjaSB6LC98gFipNK/erM2Heg/E8mIK+hXG/pJMLK+eRZA==", + "version": "3.2.0", + "resolved": "https://registry.npmjs.org/eslint-visitor-keys/-/eslint-visitor-keys-3.2.0.tgz", + "integrity": "sha512-IOzT0X126zn7ALX0dwFiUQEdsfzrm4+ISsQS8nukaJXwEyYKRSnEIIDULYg1mCtGp7UUXgfGl7BIolXREQK+XQ==", "dev": true, "engines": { "node": "^12.22.0 || ^14.17.0 || >=16.0.0" @@ -2345,30 +2330,6 @@ "node": ">=10.13.0" } }, - "node_modules/eslint/node_modules/ignore": { - "version": "4.0.6", - "resolved": "https://registry.npmjs.org/ignore/-/ignore-4.0.6.tgz", - "integrity": "sha512-cyFDKrqc/YdcWFniJhzI42+AzS+gNwmUzOSFcRCQYwySuBBBy/KjuxWLZ/FHEH6Moq1NizMOBWyTcv8O4OZIMg==", - "dev": true, - "engines": { - "node": ">= 4" - } - }, - "node_modules/eslint/node_modules/semver": { - "version": "7.3.5", - "resolved": "https://registry.npmjs.org/semver/-/semver-7.3.5.tgz", - "integrity": "sha512-PoeGJYh8HK4BTO/a9Tf6ZG3veo/A7ZVsYrSA6J8ny9nb3B1VrpkuN+z9OE5wfE5p6H4LchYZsegiQgbJD94ZFQ==", - "dev": true, - "dependencies": { - "lru-cache": "^6.0.0" - }, - "bin": { - "semver": "bin/semver.js" - }, - "engines": { - "node": ">=10" - } - }, "node_modules/eslint/node_modules/strip-json-comments": { "version": "3.1.1", "resolved": "https://registry.npmjs.org/strip-json-comments/-/strip-json-comments-3.1.1.tgz", @@ -3895,12 +3856,12 @@ "dev": true }, "node_modules/marked": { - "version": "3.0.8", - "resolved": "https://registry.npmjs.org/marked/-/marked-3.0.8.tgz", - "integrity": "sha512-0gVrAjo5m0VZSJb4rpL59K1unJAMb/hm8HRXqasD8VeC8m91ytDPMritgFSlKonfdt+rRYYpP/JfLxgIX8yoSw==", + "version": "4.0.10", + "resolved": "https://registry.npmjs.org/marked/-/marked-4.0.10.tgz", + "integrity": "sha512-+QvuFj0nGgO970fySghXGmuw+Fd0gD2x3+MqCWLIPf5oxdv1Ka6b2q+z9RP01P/IaKPMEramy+7cNy/Lw8c3hw==", "dev": true, "bin": { - "marked": "bin/marked" + "marked": "bin/marked.js" }, "engines": { "node": ">= 12" @@ -3992,9 +3953,9 @@ "dev": true }, "node_modules/mocha": { - "version": "9.1.3", - "resolved": "https://registry.npmjs.org/mocha/-/mocha-9.1.3.tgz", - "integrity": "sha512-Xcpl9FqXOAYqI3j79pEtHBBnQgVXIhpULjGQa7DVb0Po+VzmSIK9kanAiWLHoRR/dbZ2qpdPshuXr8l1VaHCzw==", + "version": "9.1.4", + "resolved": "https://registry.npmjs.org/mocha/-/mocha-9.1.4.tgz", + "integrity": "sha512-+q2aV5VlJZuLgCWoBvGI5zEwPF9eEI0kr/sAA9Jm4xMND7RfIEyF8JE7C0JIg8WXRG+P1sdIAb5ccoHPlXLzcw==", "dev": true, "dependencies": { "@ungap/promise-all-settled": "1.1.2", @@ -5033,15 +4994,6 @@ "node": ">=8" } }, - "node_modules/progress": { - "version": "2.0.3", - "resolved": "https://registry.npmjs.org/progress/-/progress-2.0.3.tgz", - "integrity": "sha512-7PiHtLll5LdnKIMw100I+8xJXR5gW2QwWYkT6iJva0bXitZKa/XMrSbdmg3r2Xnaidz9Qumd0VPaMrZlF9V9sA==", - "dev": true, - "engines": { - "node": ">=0.4.0" - } - }, "node_modules/protocols": { "version": "1.4.8", "resolved": "https://registry.npmjs.org/protocols/-/protocols-1.4.8.tgz", @@ -5611,9 +5563,9 @@ } }, "node_modules/shiki": { - "version": "0.9.15", - "resolved": "https://registry.npmjs.org/shiki/-/shiki-0.9.15.tgz", - "integrity": "sha512-/Y0z9IzhJ8nD9nbceORCqu6NgT9X6I8Fk8c3SICHI5NbZRLdZYFaB233gwct9sU0vvSypyaL/qaKvzyQGJBZSw==", + "version": "0.10.0", + "resolved": "https://registry.npmjs.org/shiki/-/shiki-0.10.0.tgz", + "integrity": "sha512-iczxaIYeBFHTFrQPb9DVy2SKgYxC4Wo7Iucm7C17cCh2Ge/refnvHscUOxM85u57MfLoNOtjoEFUWt9gBexblA==", "dev": true, "dependencies": { "jsonc-parser": "^3.0.0", @@ -6050,16 +6002,16 @@ } }, "node_modules/typedoc": { - "version": "0.22.10", - "resolved": "https://registry.npmjs.org/typedoc/-/typedoc-0.22.10.tgz", - "integrity": "sha512-hQYZ4WtoMZ61wDC6w10kxA42+jclWngdmztNZsDvIz7BMJg7F2xnT+uYsUa7OluyKossdFj9E9Ye4QOZKTy8SA==", + "version": "0.22.11", + "resolved": "https://registry.npmjs.org/typedoc/-/typedoc-0.22.11.tgz", + "integrity": "sha512-pVr3hh6dkS3lPPaZz1fNpvcrqLdtEvXmXayN55czlamSgvEjh+57GUqfhAI1Xsuu/hNHUT1KNSx8LH2wBP/7SA==", "dev": true, "dependencies": { "glob": "^7.2.0", "lunr": "^2.3.9", - "marked": "^3.0.8", + "marked": "^4.0.10", "minimatch": "^3.0.4", - "shiki": "^0.9.12" + "shiki": "^0.10.0" }, "bin": { "typedoc": "bin/typedoc" @@ -6525,13 +6477,13 @@ "devDependencies": { "@istanbuljs/nyc-config-typescript": "^1.0.2", "@node-redis/test-utils": "*", - "@types/node": "^17.0.8", + "@types/node": "^17.0.10", "nyc": "^15.1.0", - "release-it": "^14.12.1", + "release-it": "^14.12.3", "source-map-support": "^0.5.21", "ts-node": "^10.4.0", - "typedoc": "^0.22.10", - "typescript": "^4.5.4" + "typedoc": "^0.22.11", + "typescript": "^4.5.5" }, "peerDependencies": { "@node-redis/client": "^1.0.0" @@ -6550,20 +6502,20 @@ "devDependencies": { "@istanbuljs/nyc-config-typescript": "^1.0.2", "@node-redis/test-utils": "*", - "@types/node": "^17.0.8", + "@types/node": "^17.0.10", "@types/redis-parser": "^3.0.0", - "@types/sinon": "^10.0.6", + "@types/sinon": "^10.0.8", "@types/yallist": "^4.0.1", - "@typescript-eslint/eslint-plugin": "^5.9.1", - "@typescript-eslint/parser": "^5.9.1", - "eslint": "^8.6.0", + "@typescript-eslint/eslint-plugin": "^5.10.0", + "@typescript-eslint/parser": "^5.10.0", + "eslint": "^8.7.0", "nyc": "^15.1.0", - "release-it": "^14.12.1", + "release-it": "^14.12.3", "sinon": "^12.0.1", "source-map-support": "^0.5.21", "ts-node": "^10.4.0", - "typedoc": "^0.22.10", - "typescript": "^4.5.4" + "typedoc": "^0.22.11", + "typescript": "^4.5.5" }, "engines": { "node": ">=12" @@ -6576,13 +6528,13 @@ "devDependencies": { "@istanbuljs/nyc-config-typescript": "^1.0.2", "@node-redis/test-utils": "*", - "@types/node": "^17.0.8", + "@types/node": "^17.0.10", "nyc": "^15.1.0", - "release-it": "^14.12.1", + "release-it": "^14.12.3", "source-map-support": "^0.5.21", "ts-node": "^10.4.0", - "typedoc": "^0.22.10", - "typescript": "^4.5.4" + "typedoc": "^0.22.11", + "typescript": "^4.5.5" }, "peerDependencies": { "@node-redis/client": "^1.0.0" @@ -6595,13 +6547,13 @@ "devDependencies": { "@istanbuljs/nyc-config-typescript": "^1.0.2", "@node-redis/test-utils": "*", - "@types/node": "^17.0.5", + "@types/node": "^17.0.10", "nyc": "^15.1.0", - "release-it": "^14.12.1", + "release-it": "^14.12.3", "source-map-support": "^0.5.21", "ts-node": "^10.4.0", - "typedoc": "^0.22.10", - "typescript": "^4.5.4" + "typedoc": "^0.22.11", + "typescript": "^4.5.5" }, "peerDependencies": { "@node-redis/client": "^1.0.0" @@ -6611,14 +6563,14 @@ "name": "@node-redis/test-utils", "devDependencies": { "@istanbuljs/nyc-config-typescript": "^1.0.2", - "@types/mocha": "^9.0.0", - "@types/node": "^17.0.8", + "@types/mocha": "^9.1.0", + "@types/node": "^17.0.10", "@types/yargs": "^17.0.8", - "mocha": "^9.1.3", + "mocha": "^9.1.4", "nyc": "^15.1.0", "source-map-support": "^0.5.21", "ts-node": "^10.4.0", - "typescript": "^4.5.4", + "typescript": "^4.5.5", "yargs": "^17.3.1" }, "peerDependencies": { @@ -6632,13 +6584,13 @@ "devDependencies": { "@istanbuljs/nyc-config-typescript": "^1.0.2", "@node-redis/test-utils": "*", - "@types/node": "^17.0.5", + "@types/node": "^17.0.10", "nyc": "^15.1.0", - "release-it": "^14.12.1", + "release-it": "^14.12.3", "source-map-support": "^0.5.21", "ts-node": "^10.4.0", - "typedoc": "^0.22.10", - "typescript": "^4.5.4" + "typedoc": "^0.22.11", + "typescript": "^4.5.5" }, "peerDependencies": { "@node-redis/client": "^1.0.0" @@ -7062,13 +7014,13 @@ "requires": { "@istanbuljs/nyc-config-typescript": "^1.0.2", "@node-redis/test-utils": "*", - "@types/node": "^17.0.8", + "@types/node": "^17.0.10", "nyc": "^15.1.0", - "release-it": "^14.12.1", + "release-it": "^14.12.3", "source-map-support": "^0.5.21", "ts-node": "^10.4.0", - "typedoc": "^0.22.10", - "typescript": "^4.5.4" + "typedoc": "^0.22.11", + "typescript": "^4.5.5" } }, "@node-redis/client": { @@ -7076,23 +7028,23 @@ "requires": { "@istanbuljs/nyc-config-typescript": "^1.0.2", "@node-redis/test-utils": "*", - "@types/node": "^17.0.8", + "@types/node": "^17.0.10", "@types/redis-parser": "^3.0.0", - "@types/sinon": "^10.0.6", + "@types/sinon": "^10.0.8", "@types/yallist": "^4.0.1", - "@typescript-eslint/eslint-plugin": "^5.9.1", - "@typescript-eslint/parser": "^5.9.1", + "@typescript-eslint/eslint-plugin": "^5.10.0", + "@typescript-eslint/parser": "^5.10.0", "cluster-key-slot": "1.1.0", - "eslint": "^8.6.0", + "eslint": "^8.7.0", "generic-pool": "3.8.2", "nyc": "^15.1.0", "redis-parser": "3.0.0", - "release-it": "^14.12.1", + "release-it": "^14.12.3", "sinon": "^12.0.1", "source-map-support": "^0.5.21", "ts-node": "^10.4.0", - "typedoc": "^0.22.10", - "typescript": "^4.5.4", + "typedoc": "^0.22.11", + "typescript": "^4.5.5", "yallist": "4.0.0" } }, @@ -7101,13 +7053,13 @@ "requires": { "@istanbuljs/nyc-config-typescript": "^1.0.2", "@node-redis/test-utils": "*", - "@types/node": "^17.0.8", + "@types/node": "^17.0.10", "nyc": "^15.1.0", - "release-it": "^14.12.1", + "release-it": "^14.12.3", "source-map-support": "^0.5.21", "ts-node": "^10.4.0", - "typedoc": "^0.22.10", - "typescript": "^4.5.4" + "typedoc": "^0.22.11", + "typescript": "^4.5.5" } }, "@node-redis/search": { @@ -7115,27 +7067,27 @@ "requires": { "@istanbuljs/nyc-config-typescript": "^1.0.2", "@node-redis/test-utils": "*", - "@types/node": "^17.0.5", + "@types/node": "^17.0.10", "nyc": "^15.1.0", - "release-it": "^14.12.1", + "release-it": "^14.12.3", "source-map-support": "^0.5.21", "ts-node": "^10.4.0", - "typedoc": "^0.22.10", - "typescript": "^4.5.4" + "typedoc": "^0.22.11", + "typescript": "^4.5.5" } }, "@node-redis/test-utils": { "version": "file:packages/test-utils", "requires": { "@istanbuljs/nyc-config-typescript": "^1.0.2", - "@types/mocha": "^9.0.0", - "@types/node": "^17.0.8", + "@types/mocha": "^9.1.0", + "@types/node": "^17.0.10", "@types/yargs": "^17.0.8", - "mocha": "^9.1.3", + "mocha": "^9.1.4", "nyc": "^15.1.0", "source-map-support": "^0.5.21", "ts-node": "^10.4.0", - "typescript": "^4.5.4", + "typescript": "^4.5.5", "yargs": "^17.3.1" } }, @@ -7144,13 +7096,13 @@ "requires": { "@istanbuljs/nyc-config-typescript": "^1.0.2", "@node-redis/test-utils": "*", - "@types/node": "^17.0.5", + "@types/node": "^17.0.10", "nyc": "^15.1.0", - "release-it": "^14.12.1", + "release-it": "^14.12.3", "source-map-support": "^0.5.21", "ts-node": "^10.4.0", - "typedoc": "^0.22.10", - "typescript": "^4.5.4" + "typedoc": "^0.22.11", + "typescript": "^4.5.5" } }, "@nodelib/fs.scandir": { @@ -7411,15 +7363,15 @@ } }, "@types/mocha": { - "version": "9.0.0", - "resolved": "https://registry.npmjs.org/@types/mocha/-/mocha-9.0.0.tgz", - "integrity": "sha512-scN0hAWyLVAvLR9AyW7HoFF5sJZglyBsbPuHO4fv7JRvfmPBMfp1ozWqOf/e4wwPNxezBZXRfWzMb6iFLgEVRA==", + "version": "9.1.0", + "resolved": "https://registry.npmjs.org/@types/mocha/-/mocha-9.1.0.tgz", + "integrity": "sha512-QCWHkbMv4Y5U9oW10Uxbr45qMMSzl4OzijsozynUAgx3kEHUdXB00udx2dWDQ7f2TU2a2uuiFaRZjCe3unPpeg==", "dev": true }, "@types/node": { - "version": "17.0.8", - "resolved": "https://registry.npmjs.org/@types/node/-/node-17.0.8.tgz", - "integrity": "sha512-YofkM6fGv4gDJq78g4j0mMuGMkZVxZDgtU0JRdx6FgiJDG+0fY0GKVolOV8WqVmEhLCXkQRjwDdKyPxJp/uucg==", + "version": "17.0.10", + "resolved": "https://registry.npmjs.org/@types/node/-/node-17.0.10.tgz", + "integrity": "sha512-S/3xB4KzyFxYGCppyDt68yzBU9ysL88lSdIah4D6cptdcltc4NCPCAMc0+PCpg/lLIyC7IPvj2Z52OJWeIUkog==", "dev": true }, "@types/parse-json": { @@ -7454,9 +7406,9 @@ } }, "@types/sinon": { - "version": "10.0.6", - "resolved": "https://registry.npmjs.org/@types/sinon/-/sinon-10.0.6.tgz", - "integrity": "sha512-6EF+wzMWvBNeGrfP3Nx60hhx+FfwSg1JJBLAAP/IdIUq0EYkqCYf70VT3PhuhPX9eLD+Dp+lNdpb/ZeHG8Yezg==", + "version": "10.0.8", + "resolved": "https://registry.npmjs.org/@types/sinon/-/sinon-10.0.8.tgz", + "integrity": "sha512-XZbSLlox2KM7VaEJPZ5G/fMZXJNuAtYiFOax7UT51quZMAJRWKvugPMqNA0mV3jC9HIYpQSg6qbV+ilQMwLqyA==", "dev": true, "requires": { "@sinonjs/fake-timers": "^7.1.0" @@ -7484,14 +7436,14 @@ "dev": true }, "@typescript-eslint/eslint-plugin": { - "version": "5.9.1", - "resolved": "https://registry.npmjs.org/@typescript-eslint/eslint-plugin/-/eslint-plugin-5.9.1.tgz", - "integrity": "sha512-Xv9tkFlyD4MQGpJgTo6wqDqGvHIRmRgah/2Sjz1PUnJTawjHWIwBivUE9x0QtU2WVii9baYgavo/bHjrZJkqTw==", + "version": "5.10.0", + "resolved": "https://registry.npmjs.org/@typescript-eslint/eslint-plugin/-/eslint-plugin-5.10.0.tgz", + "integrity": "sha512-XXVKnMsq2fuu9K2KsIxPUGqb6xAImz8MEChClbXmE3VbveFtBUU5bzM6IPVWqzyADIgdkS2Ws/6Xo7W2TeZWjQ==", "dev": true, "requires": { - "@typescript-eslint/experimental-utils": "5.9.1", - "@typescript-eslint/scope-manager": "5.9.1", - "@typescript-eslint/type-utils": "5.9.1", + "@typescript-eslint/scope-manager": "5.10.0", + "@typescript-eslint/type-utils": "5.10.0", + "@typescript-eslint/utils": "5.10.0", "debug": "^4.3.2", "functional-red-black-tree": "^1.0.1", "ignore": "^5.1.8", @@ -7511,67 +7463,53 @@ } } }, - "@typescript-eslint/experimental-utils": { - "version": "5.9.1", - "resolved": "https://registry.npmjs.org/@typescript-eslint/experimental-utils/-/experimental-utils-5.9.1.tgz", - "integrity": "sha512-cb1Njyss0mLL9kLXgS/eEY53SZQ9sT519wpX3i+U457l2UXRDuo87hgKfgRazmu9/tQb0x2sr3Y0yrU+Zz0y+w==", - "dev": true, - "requires": { - "@types/json-schema": "^7.0.9", - "@typescript-eslint/scope-manager": "5.9.1", - "@typescript-eslint/types": "5.9.1", - "@typescript-eslint/typescript-estree": "5.9.1", - "eslint-scope": "^5.1.1", - "eslint-utils": "^3.0.0" - } - }, "@typescript-eslint/parser": { - "version": "5.9.1", - "resolved": "https://registry.npmjs.org/@typescript-eslint/parser/-/parser-5.9.1.tgz", - "integrity": "sha512-PLYO0AmwD6s6n0ZQB5kqPgfvh73p0+VqopQQLuNfi7Lm0EpfKyDalchpVwkE+81k5HeiRrTV/9w1aNHzjD7C4g==", + "version": "5.10.0", + "resolved": "https://registry.npmjs.org/@typescript-eslint/parser/-/parser-5.10.0.tgz", + "integrity": "sha512-pJB2CCeHWtwOAeIxv8CHVGJhI5FNyJAIpx5Pt72YkK3QfEzt6qAlXZuyaBmyfOdM62qU0rbxJzNToPTVeJGrQw==", "dev": true, "requires": { - "@typescript-eslint/scope-manager": "5.9.1", - "@typescript-eslint/types": "5.9.1", - "@typescript-eslint/typescript-estree": "5.9.1", + "@typescript-eslint/scope-manager": "5.10.0", + "@typescript-eslint/types": "5.10.0", + "@typescript-eslint/typescript-estree": "5.10.0", "debug": "^4.3.2" } }, "@typescript-eslint/scope-manager": { - "version": "5.9.1", - "resolved": "https://registry.npmjs.org/@typescript-eslint/scope-manager/-/scope-manager-5.9.1.tgz", - "integrity": "sha512-8BwvWkho3B/UOtzRyW07ffJXPaLSUKFBjpq8aqsRvu6HdEuzCY57+ffT7QoV4QXJXWSU1+7g3wE4AlgImmQ9pQ==", + "version": "5.10.0", + "resolved": "https://registry.npmjs.org/@typescript-eslint/scope-manager/-/scope-manager-5.10.0.tgz", + "integrity": "sha512-tgNgUgb4MhqK6DoKn3RBhyZ9aJga7EQrw+2/OiDk5hKf3pTVZWyqBi7ukP+Z0iEEDMF5FDa64LqODzlfE4O/Dg==", "dev": true, "requires": { - "@typescript-eslint/types": "5.9.1", - "@typescript-eslint/visitor-keys": "5.9.1" + "@typescript-eslint/types": "5.10.0", + "@typescript-eslint/visitor-keys": "5.10.0" } }, "@typescript-eslint/type-utils": { - "version": "5.9.1", - "resolved": "https://registry.npmjs.org/@typescript-eslint/type-utils/-/type-utils-5.9.1.tgz", - "integrity": "sha512-tRSpdBnPRssjlUh35rE9ug5HrUvaB9ntREy7gPXXKwmIx61TNN7+l5YKgi1hMKxo5NvqZCfYhA5FvyuJG6X6vg==", + "version": "5.10.0", + "resolved": "https://registry.npmjs.org/@typescript-eslint/type-utils/-/type-utils-5.10.0.tgz", + "integrity": "sha512-TzlyTmufJO5V886N+hTJBGIfnjQDQ32rJYxPaeiyWKdjsv2Ld5l8cbS7pxim4DeNs62fKzRSt8Q14Evs4JnZyQ==", "dev": true, "requires": { - "@typescript-eslint/experimental-utils": "5.9.1", + "@typescript-eslint/utils": "5.10.0", "debug": "^4.3.2", "tsutils": "^3.21.0" } }, "@typescript-eslint/types": { - "version": "5.9.1", - "resolved": "https://registry.npmjs.org/@typescript-eslint/types/-/types-5.9.1.tgz", - "integrity": "sha512-SsWegWudWpkZCwwYcKoDwuAjoZXnM1y2EbEerTHho19Hmm+bQ56QG4L4jrtCu0bI5STaRTvRTZmjprWlTw/5NQ==", + "version": "5.10.0", + "resolved": "https://registry.npmjs.org/@typescript-eslint/types/-/types-5.10.0.tgz", + "integrity": "sha512-wUljCgkqHsMZbw60IbOqT/puLfyqqD5PquGiBo1u1IS3PLxdi3RDGlyf032IJyh+eQoGhz9kzhtZa+VC4eWTlQ==", "dev": true }, "@typescript-eslint/typescript-estree": { - "version": "5.9.1", - "resolved": "https://registry.npmjs.org/@typescript-eslint/typescript-estree/-/typescript-estree-5.9.1.tgz", - "integrity": "sha512-gL1sP6A/KG0HwrahVXI9fZyeVTxEYV//6PmcOn1tD0rw8VhUWYeZeuWHwwhnewnvEMcHjhnJLOBhA9rK4vmb8A==", + "version": "5.10.0", + "resolved": "https://registry.npmjs.org/@typescript-eslint/typescript-estree/-/typescript-estree-5.10.0.tgz", + "integrity": "sha512-x+7e5IqfwLwsxTdliHRtlIYkgdtYXzE0CkFeV6ytAqq431ZyxCFzNMNR5sr3WOlIG/ihVZr9K/y71VHTF/DUQA==", "dev": true, "requires": { - "@typescript-eslint/types": "5.9.1", - "@typescript-eslint/visitor-keys": "5.9.1", + "@typescript-eslint/types": "5.10.0", + "@typescript-eslint/visitor-keys": "5.10.0", "debug": "^4.3.2", "globby": "^11.0.4", "is-glob": "^4.0.3", @@ -7610,13 +7548,27 @@ } } }, + "@typescript-eslint/utils": { + "version": "5.10.0", + "resolved": "https://registry.npmjs.org/@typescript-eslint/utils/-/utils-5.10.0.tgz", + "integrity": "sha512-IGYwlt1CVcFoE2ueW4/ioEwybR60RAdGeiJX/iDAw0t5w0wK3S7QncDwpmsM70nKgGTuVchEWB8lwZwHqPAWRg==", + "dev": true, + "requires": { + "@types/json-schema": "^7.0.9", + "@typescript-eslint/scope-manager": "5.10.0", + "@typescript-eslint/types": "5.10.0", + "@typescript-eslint/typescript-estree": "5.10.0", + "eslint-scope": "^5.1.1", + "eslint-utils": "^3.0.0" + } + }, "@typescript-eslint/visitor-keys": { - "version": "5.9.1", - "resolved": "https://registry.npmjs.org/@typescript-eslint/visitor-keys/-/visitor-keys-5.9.1.tgz", - "integrity": "sha512-Xh37pNz9e9ryW4TVdwiFzmr4hloty8cFj8GTWMXh3Z8swGwyQWeCcNgF0hm6t09iZd6eiZmIf4zHedQVP6TVtg==", + "version": "5.10.0", + "resolved": "https://registry.npmjs.org/@typescript-eslint/visitor-keys/-/visitor-keys-5.10.0.tgz", + "integrity": "sha512-GMxj0K1uyrFLPKASLmZzCuSddmjZVbVj3Ouy5QVuIGKZopxvOr24JsS7gruz6C3GExE01mublZ3mIBOaon9zuQ==", "dev": true, "requires": { - "@typescript-eslint/types": "5.9.1", + "@typescript-eslint/types": "5.10.0", "eslint-visitor-keys": "^3.0.0" } }, @@ -8340,15 +8292,6 @@ "once": "^1.4.0" } }, - "enquirer": { - "version": "2.3.6", - "resolved": "https://registry.npmjs.org/enquirer/-/enquirer-2.3.6.tgz", - "integrity": "sha512-yjNnPr315/FjS4zIsUxYguYUPP2e1NK4d7E7ZOLiyYCcbFBiTMyID+2wvm2w6+pZ/odMA7cRkjhsPbltwBOrLg==", - "dev": true, - "requires": { - "ansi-colors": "^4.1.1" - } - }, "error-ex": { "version": "1.3.2", "resolved": "https://registry.npmjs.org/error-ex/-/error-ex-1.3.2.tgz", @@ -8383,9 +8326,9 @@ "dev": true }, "eslint": { - "version": "8.6.0", - "resolved": "https://registry.npmjs.org/eslint/-/eslint-8.6.0.tgz", - "integrity": "sha512-UvxdOJ7mXFlw7iuHZA4jmzPaUqIw54mZrv+XPYKNbKdLR0et4rf60lIZUU9kiNtnzzMzGWxMV+tQ7uG7JG8DPw==", + "version": "8.7.0", + "resolved": "https://registry.npmjs.org/eslint/-/eslint-8.7.0.tgz", + "integrity": "sha512-ifHYzkBGrzS2iDU7KjhCAVMGCvF6M3Xfs8X8b37cgrUlDt6bWRTpRh6T/gtSXv1HJ/BUGgmjvNvOEGu85Iif7w==", "dev": true, "requires": { "@eslint/eslintrc": "^1.0.5", @@ -8395,11 +8338,10 @@ "cross-spawn": "^7.0.2", "debug": "^4.3.2", "doctrine": "^3.0.0", - "enquirer": "^2.3.5", "escape-string-regexp": "^4.0.0", "eslint-scope": "^7.1.0", "eslint-utils": "^3.0.0", - "eslint-visitor-keys": "^3.1.0", + "eslint-visitor-keys": "^3.2.0", "espree": "^9.3.0", "esquery": "^1.4.0", "esutils": "^2.0.2", @@ -8408,7 +8350,7 @@ "functional-red-black-tree": "^1.0.1", "glob-parent": "^6.0.1", "globals": "^13.6.0", - "ignore": "^4.0.6", + "ignore": "^5.2.0", "import-fresh": "^3.0.0", "imurmurhash": "^0.1.4", "is-glob": "^4.0.0", @@ -8419,9 +8361,7 @@ "minimatch": "^3.0.4", "natural-compare": "^1.4.0", "optionator": "^0.9.1", - "progress": "^2.0.0", "regexpp": "^3.2.0", - "semver": "^7.2.1", "strip-ansi": "^6.0.1", "strip-json-comments": "^3.1.0", "text-table": "^0.2.0", @@ -8459,21 +8399,6 @@ "is-glob": "^4.0.3" } }, - "ignore": { - "version": "4.0.6", - "resolved": "https://registry.npmjs.org/ignore/-/ignore-4.0.6.tgz", - "integrity": "sha512-cyFDKrqc/YdcWFniJhzI42+AzS+gNwmUzOSFcRCQYwySuBBBy/KjuxWLZ/FHEH6Moq1NizMOBWyTcv8O4OZIMg==", - "dev": true - }, - "semver": { - "version": "7.3.5", - "resolved": "https://registry.npmjs.org/semver/-/semver-7.3.5.tgz", - "integrity": "sha512-PoeGJYh8HK4BTO/a9Tf6ZG3veo/A7ZVsYrSA6J8ny9nb3B1VrpkuN+z9OE5wfE5p6H4LchYZsegiQgbJD94ZFQ==", - "dev": true, - "requires": { - "lru-cache": "^6.0.0" - } - }, "strip-json-comments": { "version": "3.1.1", "resolved": "https://registry.npmjs.org/strip-json-comments/-/strip-json-comments-3.1.1.tgz", @@ -8510,9 +8435,9 @@ } }, "eslint-visitor-keys": { - "version": "3.1.0", - "resolved": "https://registry.npmjs.org/eslint-visitor-keys/-/eslint-visitor-keys-3.1.0.tgz", - "integrity": "sha512-yWJFpu4DtjsWKkt5GeNBBuZMlNcYVs6vRCLoCVEJrTjaSB6LC98gFipNK/erM2Heg/E8mIK+hXG/pJMLK+eRZA==", + "version": "3.2.0", + "resolved": "https://registry.npmjs.org/eslint-visitor-keys/-/eslint-visitor-keys-3.2.0.tgz", + "integrity": "sha512-IOzT0X126zn7ALX0dwFiUQEdsfzrm4+ISsQS8nukaJXwEyYKRSnEIIDULYg1mCtGp7UUXgfGl7BIolXREQK+XQ==", "dev": true }, "espree": { @@ -9630,9 +9555,9 @@ "dev": true }, "marked": { - "version": "3.0.8", - "resolved": "https://registry.npmjs.org/marked/-/marked-3.0.8.tgz", - "integrity": "sha512-0gVrAjo5m0VZSJb4rpL59K1unJAMb/hm8HRXqasD8VeC8m91ytDPMritgFSlKonfdt+rRYYpP/JfLxgIX8yoSw==", + "version": "4.0.10", + "resolved": "https://registry.npmjs.org/marked/-/marked-4.0.10.tgz", + "integrity": "sha512-+QvuFj0nGgO970fySghXGmuw+Fd0gD2x3+MqCWLIPf5oxdv1Ka6b2q+z9RP01P/IaKPMEramy+7cNy/Lw8c3hw==", "dev": true }, "merge-stream": { @@ -9700,9 +9625,9 @@ "dev": true }, "mocha": { - "version": "9.1.3", - "resolved": "https://registry.npmjs.org/mocha/-/mocha-9.1.3.tgz", - "integrity": "sha512-Xcpl9FqXOAYqI3j79pEtHBBnQgVXIhpULjGQa7DVb0Po+VzmSIK9kanAiWLHoRR/dbZ2qpdPshuXr8l1VaHCzw==", + "version": "9.1.4", + "resolved": "https://registry.npmjs.org/mocha/-/mocha-9.1.4.tgz", + "integrity": "sha512-+q2aV5VlJZuLgCWoBvGI5zEwPF9eEI0kr/sAA9Jm4xMND7RfIEyF8JE7C0JIg8WXRG+P1sdIAb5ccoHPlXLzcw==", "dev": true, "requires": { "@ungap/promise-all-settled": "1.1.2", @@ -10489,12 +10414,6 @@ "fromentries": "^1.2.0" } }, - "progress": { - "version": "2.0.3", - "resolved": "https://registry.npmjs.org/progress/-/progress-2.0.3.tgz", - "integrity": "sha512-7PiHtLll5LdnKIMw100I+8xJXR5gW2QwWYkT6iJva0bXitZKa/XMrSbdmg3r2Xnaidz9Qumd0VPaMrZlF9V9sA==", - "dev": true - }, "protocols": { "version": "1.4.8", "resolved": "https://registry.npmjs.org/protocols/-/protocols-1.4.8.tgz", @@ -10899,9 +10818,9 @@ } }, "shiki": { - "version": "0.9.15", - "resolved": "https://registry.npmjs.org/shiki/-/shiki-0.9.15.tgz", - "integrity": "sha512-/Y0z9IzhJ8nD9nbceORCqu6NgT9X6I8Fk8c3SICHI5NbZRLdZYFaB233gwct9sU0vvSypyaL/qaKvzyQGJBZSw==", + "version": "0.10.0", + "resolved": "https://registry.npmjs.org/shiki/-/shiki-0.10.0.tgz", + "integrity": "sha512-iczxaIYeBFHTFrQPb9DVy2SKgYxC4Wo7Iucm7C17cCh2Ge/refnvHscUOxM85u57MfLoNOtjoEFUWt9gBexblA==", "dev": true, "requires": { "jsonc-parser": "^3.0.0", @@ -11234,16 +11153,16 @@ } }, "typedoc": { - "version": "0.22.10", - "resolved": "https://registry.npmjs.org/typedoc/-/typedoc-0.22.10.tgz", - "integrity": "sha512-hQYZ4WtoMZ61wDC6w10kxA42+jclWngdmztNZsDvIz7BMJg7F2xnT+uYsUa7OluyKossdFj9E9Ye4QOZKTy8SA==", + "version": "0.22.11", + "resolved": "https://registry.npmjs.org/typedoc/-/typedoc-0.22.11.tgz", + "integrity": "sha512-pVr3hh6dkS3lPPaZz1fNpvcrqLdtEvXmXayN55czlamSgvEjh+57GUqfhAI1Xsuu/hNHUT1KNSx8LH2wBP/7SA==", "dev": true, "requires": { "glob": "^7.2.0", "lunr": "^2.3.9", - "marked": "^3.0.8", + "marked": "^4.0.10", "minimatch": "^3.0.4", - "shiki": "^0.9.12" + "shiki": "^0.10.0" } }, "typescript": { diff --git a/packages/bloom/package.json b/packages/bloom/package.json index 79b08774b75..2a295f1bf76 100644 --- a/packages/bloom/package.json +++ b/packages/bloom/package.json @@ -18,12 +18,12 @@ "devDependencies": { "@istanbuljs/nyc-config-typescript": "^1.0.2", "@node-redis/test-utils": "*", - "@types/node": "^17.0.8", + "@types/node": "^17.0.10", "nyc": "^15.1.0", - "release-it": "^14.12.1", + "release-it": "^14.12.3", "source-map-support": "^0.5.21", "ts-node": "^10.4.0", - "typedoc": "^0.22.10", - "typescript": "^4.5.4" + "typedoc": "^0.22.11", + "typescript": "^4.5.5" } } diff --git a/packages/client/package.json b/packages/client/package.json index 63cf9e817c5..5ad9fc71563 100644 --- a/packages/client/package.json +++ b/packages/client/package.json @@ -22,20 +22,20 @@ "devDependencies": { "@istanbuljs/nyc-config-typescript": "^1.0.2", "@node-redis/test-utils": "*", - "@types/node": "^17.0.8", + "@types/node": "^17.0.10", "@types/redis-parser": "^3.0.0", - "@types/sinon": "^10.0.6", + "@types/sinon": "^10.0.8", "@types/yallist": "^4.0.1", - "@typescript-eslint/eslint-plugin": "^5.9.1", - "@typescript-eslint/parser": "^5.9.1", - "eslint": "^8.6.0", + "@typescript-eslint/eslint-plugin": "^5.10.0", + "@typescript-eslint/parser": "^5.10.0", + "eslint": "^8.7.0", "nyc": "^15.1.0", - "release-it": "^14.12.1", + "release-it": "^14.12.3", "sinon": "^12.0.1", "source-map-support": "^0.5.21", "ts-node": "^10.4.0", - "typedoc": "^0.22.10", - "typescript": "^4.5.4" + "typedoc": "^0.22.11", + "typescript": "^4.5.5" }, "engines": { "node": ">=12" diff --git a/packages/json/package.json b/packages/json/package.json index d958d5e9736..40e077e92f9 100644 --- a/packages/json/package.json +++ b/packages/json/package.json @@ -18,12 +18,12 @@ "devDependencies": { "@istanbuljs/nyc-config-typescript": "^1.0.2", "@node-redis/test-utils": "*", - "@types/node": "^17.0.8", + "@types/node": "^17.0.10", "nyc": "^15.1.0", - "release-it": "^14.12.1", + "release-it": "^14.12.3", "source-map-support": "^0.5.21", "ts-node": "^10.4.0", - "typedoc": "^0.22.10", - "typescript": "^4.5.4" + "typedoc": "^0.22.11", + "typescript": "^4.5.5" } } diff --git a/packages/search/package.json b/packages/search/package.json index 25c557219e5..4c7ba0b54e0 100644 --- a/packages/search/package.json +++ b/packages/search/package.json @@ -18,12 +18,12 @@ "devDependencies": { "@istanbuljs/nyc-config-typescript": "^1.0.2", "@node-redis/test-utils": "*", - "@types/node": "^17.0.5", + "@types/node": "^17.0.10", "nyc": "^15.1.0", - "release-it": "^14.12.1", + "release-it": "^14.12.3", "source-map-support": "^0.5.21", "ts-node": "^10.4.0", - "typedoc": "^0.22.10", - "typescript": "^4.5.4" + "typedoc": "^0.22.11", + "typescript": "^4.5.5" } } diff --git a/packages/test-utils/package.json b/packages/test-utils/package.json index 82816f9fe7e..6dadabc5e87 100644 --- a/packages/test-utils/package.json +++ b/packages/test-utils/package.json @@ -11,14 +11,14 @@ }, "devDependencies": { "@istanbuljs/nyc-config-typescript": "^1.0.2", - "@types/mocha": "^9.0.0", - "@types/node": "^17.0.8", + "@types/mocha": "^9.1.0", + "@types/node": "^17.0.10", "@types/yargs": "^17.0.8", - "mocha": "^9.1.3", + "mocha": "^9.1.4", "nyc": "^15.1.0", "source-map-support": "^0.5.21", "ts-node": "^10.4.0", - "typescript": "^4.5.4", + "typescript": "^4.5.5", "yargs": "^17.3.1" } } diff --git a/packages/time-series/package.json b/packages/time-series/package.json index 39f1f2724cc..fa527a9b512 100644 --- a/packages/time-series/package.json +++ b/packages/time-series/package.json @@ -18,12 +18,12 @@ "devDependencies": { "@istanbuljs/nyc-config-typescript": "^1.0.2", "@node-redis/test-utils": "*", - "@types/node": "^17.0.5", + "@types/node": "^17.0.10", "nyc": "^15.1.0", - "release-it": "^14.12.1", + "release-it": "^14.12.3", "source-map-support": "^0.5.21", "ts-node": "^10.4.0", - "typedoc": "^0.22.10", - "typescript": "^4.5.4" + "typedoc": "^0.22.11", + "typescript": "^4.5.5" } } From e66fa6af7d023badab1fc9aa3415cd33a95c2f96 Mon Sep 17 00:00:00 2001 From: Leif Segen Date: Mon, 24 Jan 2022 11:02:54 -0600 Subject: [PATCH 1120/1748] Correct relative link to changelog (#1868) --- docs/v3-to-v4.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/docs/v3-to-v4.md b/docs/v3-to-v4.md index 834b02141c6..ccb8c2abdc2 100644 --- a/docs/v3-to-v4.md +++ b/docs/v3-to-v4.md @@ -4,7 +4,7 @@ Version 4 of Node Redis is a major refactor. While we have tried to maintain bac ## All of the Breaking Changes -See the [Change Log](../packages/client/CHANGELOG.md). +See the [Change Log](../../CHANGELOG.md). ### Promises From cf120c398445ac2ef461b6a9fe08c4350831c1b3 Mon Sep 17 00:00:00 2001 From: Leif Segen Date: Mon, 24 Jan 2022 11:03:21 -0600 Subject: [PATCH 1121/1748] Correct relative links (#1867) Fix #1866 --- CHANGELOG.md | 12 ++++++------ 1 file changed, 6 insertions(+), 6 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index ffbf10574f3..33cf69851c7 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -34,7 +34,7 @@ ## 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. +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. ### Breaking Changes @@ -49,10 +49,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](../../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 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 @@ -450,7 +450,7 @@ No code change ## v2.2.0 - 12 Oct, 2015 - The peregrino falcon -The peregrino falcon is the fasted bird on earth and this is what this release is all about: Increased performance for heavy usage by up to **400%** [sic!] and increased overall performance for any command as well. Please check the benchmarks in the [README.md](README.md) for further details. +The peregrino falcon is the fasted bird on earth and this is what this release is all about: Increased performance for heavy usage by up to **400%** [sic!] and increased overall performance for any command as well. Please check the benchmarks in the [README.md](./README.md) for further details. Features From 551d2041dc1110c26296e674ee953f48b9d641e5 Mon Sep 17 00:00:00 2001 From: Leibale Eidelman Date: Mon, 24 Jan 2022 12:03:40 -0500 Subject: [PATCH 1122/1748] fix #1854 - fix __redis__:invalidate messages handler (#1856) --- packages/client/lib/client/commands-queue.ts | 10 ++++++---- 1 file changed, 6 insertions(+), 4 deletions(-) diff --git a/packages/client/lib/client/commands-queue.ts b/packages/client/lib/client/commands-queue.ts index 13e37c4ccc6..84ec7e52c9d 100644 --- a/packages/client/lib/client/commands-queue.ts +++ b/packages/client/lib/client/commands-queue.ts @@ -82,10 +82,12 @@ export default class RedisCommandsQueue { if (!listeners.strings.size) return; - // https://github.com/redis/redis/pull/7469 - // https://github.com/redis/redis/issues/7463 - const messageString = (Array.isArray(message) ? message.map(m => m.toString()) as any : message.toString()), - channelString = pattern ? channel.toString() : keyString; + const channelString = pattern ? channel.toString() : keyString, + messageString = channelString === '__redis__:invalidate' ? + // https://github.com/redis/redis/pull/7469 + // https://github.com/redis/redis/issues/7463 + (message === null ? null : (message as any as Array).map(x => x.toString())) as any : + message.toString(); for (const listener of listeners.strings) { listener(messageString, channelString); } From 7ded3dd79fd4f4c6fedbc4f9b282b5b823a96022 Mon Sep 17 00:00:00 2001 From: Leibale Eidelman Date: Mon, 24 Jan 2022 12:04:30 -0500 Subject: [PATCH 1123/1748] fix #1846 - handle arguments that are not buffers or strings (#1849) * fix #1846 - handle arguments that are not buffers or strings * use toString() instead of throw TypeError * remove .only and uncomment tests --- packages/client/lib/client/commands-queue.ts | 13 ++++--- packages/client/lib/client/index.spec.ts | 16 +++++++-- packages/client/lib/client/index.ts | 7 ++-- packages/client/lib/client/multi-command.ts | 6 ++-- packages/client/lib/client/socket.ts | 16 ++++----- packages/client/lib/commander.spec.ts | 32 +++++++---------- packages/client/lib/commander.ts | 37 ++++++-------------- 7 files changed, 59 insertions(+), 68 deletions(-) diff --git a/packages/client/lib/client/commands-queue.ts b/packages/client/lib/client/commands-queue.ts index 84ec7e52c9d..df2c25dbc2c 100644 --- a/packages/client/lib/client/commands-queue.ts +++ b/packages/client/lib/client/commands-queue.ts @@ -26,7 +26,7 @@ interface CommandWaitingToBeSent extends CommandWaitingForReply { interface CommandWaitingForReply { resolve(reply?: unknown): void; - reject(err: Error): void; + reject(err: unknown): void; channelsCounter?: number; returnBuffers?: boolean; } @@ -142,7 +142,9 @@ export default class RedisCommandsQueue { }, returnError: (err: Error) => this.#shiftWaitingForReply().reject(err) }); + #chainInExecution: symbol | undefined; + constructor(maxLength: number | null | undefined) { this.#maxLength = maxLength; } @@ -155,6 +157,7 @@ export default class RedisCommandsQueue { } else if (options?.signal?.aborted) { return Promise.reject(new AbortError()); } + return new Promise((resolve, reject) => { const node = new LinkedList.Node({ args, @@ -178,6 +181,7 @@ export default class RedisCommandsQueue { once: true }); } + if (options?.asap) { this.#waitingToBeSent.unshiftNode(node); } else { @@ -386,12 +390,13 @@ export default class RedisCommandsQueue { flushWaitingForReply(err: Error): void { RedisCommandsQueue.#flushQueue(this.#waitingForReply, err); - if (!this.#chainInExecution) { - return; - } + + if (!this.#chainInExecution) return; + while (this.#waitingToBeSent.head?.value.chainId === this.#chainInExecution) { this.#waitingToBeSent.shift(); } + this.#chainInExecution = undefined; } diff --git a/packages/client/lib/client/index.spec.ts b/packages/client/lib/client/index.spec.ts index c5bfb32c034..1d627756c6d 100644 --- a/packages/client/lib/client/index.spec.ts +++ b/packages/client/lib/client/index.spec.ts @@ -1,6 +1,6 @@ import { strict as assert } from 'assert'; import testUtils, { GLOBAL, waitTillBeenCalled } from '../test-utils'; -import RedisClient, { ClientLegacyCommandArguments, RedisClientType } from '.'; +import RedisClient, { RedisClientType } from '.'; import { RedisClientMultiCommandType } from './multi-command'; import { RedisCommandArguments, RedisCommandRawReply, RedisModules, RedisScripts } from '../commands'; import { AbortError, AuthError, ClientClosedError, ConnectionTimeoutError, DisconnectsClientError, SocketClosedUnexpectedlyError, WatchError } from '../errors'; @@ -183,7 +183,7 @@ describe('Client', () => { } }); - function setAsync(client: RedisClientType, ...args: ClientLegacyCommandArguments): Promise { + function setAsync(client: RedisClientType, ...args: Array): Promise { return new Promise((resolve, reject) => { (client as any).set(...args, (err: Error | undefined, reply: RedisCommandRawReply) => { if (err) return reject(err); @@ -353,6 +353,18 @@ describe('Client', () => { ); }, GLOBAL.SERVERS.OPEN); }); + + testUtils.testWithClient('undefined and null should not break the client', async client => { + await assert.rejects( + client.sendCommand([null as any, undefined as any]), + 'ERR unknown command ``, with args beginning with: ``' + ); + + assert.equal( + await client.ping(), + 'PONG' + ); + }, GLOBAL.SERVERS.OPEN); }); describe('multi', () => { diff --git a/packages/client/lib/client/index.ts b/packages/client/lib/client/index.ts index 6144c980701..80f029a406e 100644 --- a/packages/client/lib/client/index.ts +++ b/packages/client/lib/client/index.ts @@ -9,7 +9,7 @@ import { CommandOptions, commandOptions, isCommandOptions } from '../command-opt import { ScanOptions, ZMember } from '../commands/generic-transformers'; import { ScanCommandOptions } from '../commands/SCAN'; import { HScanTuple } from '../commands/HSCAN'; -import { extendWithCommands, extendWithModulesAndScripts, LegacyCommandArguments, transformCommandArguments, transformCommandReply, transformLegacyCommandArguments } from '../commander'; +import { extendWithCommands, extendWithModulesAndScripts, transformCommandArguments, transformCommandReply } from '../commander'; import { Pool, Options as PoolOptions, createPool } from 'generic-pool'; import { ClientClosedError, DisconnectsClientError, AuthError } from '../errors'; import { URL } from 'url'; @@ -83,7 +83,6 @@ export interface ClientCommandOptions extends QueueCommandOptions { type ClientLegacyCallback = (err: Error | null, reply?: RedisCommandRawReply) => void; -export type ClientLegacyCommandArguments = LegacyCommandArguments | [...LegacyCommandArguments, ClientLegacyCallback]; export default class RedisClient extends EventEmitter { static commandOptions(options: T): CommandOptions { return commandOptions(options); @@ -292,13 +291,13 @@ export default class RedisClient if (!this.#options?.legacyMode) return; (this as any).#v4.sendCommand = this.#sendCommand.bind(this); - (this as any).sendCommand = (...args: ClientLegacyCommandArguments): void => { + (this as any).sendCommand = (...args: Array): void => { let callback: ClientLegacyCallback; if (typeof args[args.length - 1] === 'function') { callback = args.pop() as ClientLegacyCallback; } - this.#sendCommand(transformLegacyCommandArguments(args as LegacyCommandArguments)) + this.#sendCommand(args.flat()) .then((reply: RedisCommandRawReply) => { if (!callback) return; diff --git a/packages/client/lib/client/multi-command.ts b/packages/client/lib/client/multi-command.ts index 601334fe6d8..cce0b515f1f 100644 --- a/packages/client/lib/client/multi-command.ts +++ b/packages/client/lib/client/multi-command.ts @@ -1,7 +1,7 @@ import COMMANDS from './commands'; import { RedisCommand, RedisCommandArguments, RedisCommandRawReply, RedisModules, RedisPlugins, RedisScript, RedisScripts } from '../commands'; import RedisMultiCommand, { RedisMultiQueuedCommand } from '../multi-command'; -import { extendWithCommands, extendWithModulesAndScripts, LegacyCommandArguments, transformLegacyCommandArguments } from '../commander'; +import { extendWithCommands, extendWithModulesAndScripts } from '../commander'; import { ExcludeMappedString } from '.'; type RedisClientMultiCommandSignature = @@ -53,8 +53,8 @@ export default class RedisClientMultiCommand { #legacyMode(): void { this.v4.addCommand = this.addCommand.bind(this); - (this as any).addCommand = (...args: LegacyCommandArguments): this => { - this.#multi.addCommand(transformLegacyCommandArguments(args)); + (this as any).addCommand = (...args: Array): this => { + this.#multi.addCommand(args.flat()); return this; }; this.v4.exec = this.exec.bind(this); diff --git a/packages/client/lib/client/socket.ts b/packages/client/lib/client/socket.ts index ccbe3f7f2c6..d7b91e14c05 100644 --- a/packages/client/lib/client/socket.ts +++ b/packages/client/lib/client/socket.ts @@ -249,18 +249,16 @@ export default class RedisSocket extends EventEmitter { #isCorked = false; cork(): void { - if (!this.#socket) { + if (!this.#socket || this.#isCorked) { return; } - if (!this.#isCorked) { - this.#socket.cork(); - this.#isCorked = true; + this.#socket.cork(); + this.#isCorked = true; - queueMicrotask(() => { - this.#socket?.uncork(); - this.#isCorked = false; - }); - } + queueMicrotask(() => { + this.#socket?.uncork(); + this.#isCorked = false; + }); } } diff --git a/packages/client/lib/commander.spec.ts b/packages/client/lib/commander.spec.ts index b6ec1004613..f0690f37369 100644 --- a/packages/client/lib/commander.spec.ts +++ b/packages/client/lib/commander.spec.ts @@ -2,42 +2,34 @@ import { strict as assert } from 'assert'; import { describe } from 'mocha'; import { encodeCommand } from './commander'; -function encodeCommandToString(...args: Parameters): string { - const arr = []; - for (const item of encodeCommand(...args)) { - arr.push(item.toString()); - } - - return arr.join(''); -} describe('Commander', () => { describe('encodeCommand (see #1628)', () => { it('1 byte', () => { - assert.equal( - encodeCommandToString(['a', 'z']), - '*2\r\n$1\r\na\r\n$1\r\nz\r\n' + assert.deepEqual( + [...encodeCommand(['a', 'z'])], + ['*2\r\n$1\r\na\r\n$1\r\nz\r\n'] ); }); it('2 bytes', () => { - assert.equal( - encodeCommandToString(['א', 'ת']), - '*2\r\n$2\r\nא\r\n$2\r\nת\r\n' + assert.deepEqual( + [...encodeCommand(['א', 'ת'])], + ['*2\r\n$2\r\nא\r\n$2\r\nת\r\n'] ); }); it('4 bytes', () => { - assert.equal( - encodeCommandToString(['🐣', '🐤']), - '*2\r\n$4\r\n🐣\r\n$4\r\n🐤\r\n' + assert.deepEqual( + [...encodeCommand(['🐣', '🐤'])], + ['*2\r\n$4\r\n🐣\r\n$4\r\n🐤\r\n'] ); }); it('with a buffer', () => { - assert.equal( - encodeCommandToString([Buffer.from('string')]), - '*1\r\n$6\r\nstring\r\n' + assert.deepEqual( + [...encodeCommand([Buffer.from('string')])], + ['*1\r\n$6\r\n', Buffer.from('string'), '\r\n'] ); }); }); diff --git a/packages/client/lib/commander.ts b/packages/client/lib/commander.ts index 2d129d679e2..b3dfff0a99f 100644 --- a/packages/client/lib/commander.ts +++ b/packages/client/lib/commander.ts @@ -95,25 +95,25 @@ export function* encodeCommand(args: RedisCommandArguments): IterableIterator 1024) { yield strings; - strings = arg; + strings = string; stringsLength = byteLength; } else { - strings += arg; + strings += string; stringsLength = totalLength; } - } else { - yield strings; - strings = ''; - stringsLength = 0; - yield arg; } strings += DELIMITER; @@ -133,18 +133,3 @@ export function transformCommandReply( return command.transformReply(rawReply, preserved); } - -export type LegacyCommandArguments = Array; - -export function transformLegacyCommandArguments(args: LegacyCommandArguments, flat: RedisCommandArguments = []): RedisCommandArguments { - for (const arg of args) { - if (Array.isArray(arg)) { - transformLegacyCommandArguments(arg, flat); - continue; - } - - flat.push(typeof arg === 'number' ? arg.toString() : arg); - } - - return flat; -} From 84aebcca0f2649c1f95c334440ba3df9a506ab65 Mon Sep 17 00:00:00 2001 From: leibale Date: Mon, 24 Jan 2022 13:14:20 -0500 Subject: [PATCH 1124/1748] upgrade dependencies --- package-lock.json | 541 +++++++++++++----------------- packages/bloom/package.json | 2 +- packages/client/package.json | 6 +- packages/json/package.json | 2 +- packages/search/package.json | 2 +- packages/test-utils/package.json | 2 +- packages/time-series/package.json | 2 +- 7 files changed, 236 insertions(+), 321 deletions(-) diff --git a/package-lock.json b/package-lock.json index 3aeb01fe877..117e1691759 100644 --- a/package-lock.json +++ b/package-lock.json @@ -51,20 +51,20 @@ } }, "node_modules/@babel/core": { - "version": "7.16.7", - "resolved": "https://registry.npmjs.org/@babel/core/-/core-7.16.7.tgz", - "integrity": "sha512-aeLaqcqThRNZYmbMqtulsetOQZ/5gbR/dWruUCJcpas4Qoyy+QeagfDsPdMrqwsPRDNxJvBlRiZxxX7THO7qtA==", + "version": "7.16.12", + "resolved": "https://registry.npmjs.org/@babel/core/-/core-7.16.12.tgz", + "integrity": "sha512-dK5PtG1uiN2ikk++5OzSYsitZKny4wOCD0nrO4TqnW4BVBTQ2NGS3NgilvT/TEyxTST7LNyWV/T4tXDoD3fOgg==", "dev": true, "dependencies": { "@babel/code-frame": "^7.16.7", - "@babel/generator": "^7.16.7", + "@babel/generator": "^7.16.8", "@babel/helper-compilation-targets": "^7.16.7", "@babel/helper-module-transforms": "^7.16.7", "@babel/helpers": "^7.16.7", - "@babel/parser": "^7.16.7", + "@babel/parser": "^7.16.12", "@babel/template": "^7.16.7", - "@babel/traverse": "^7.16.7", - "@babel/types": "^7.16.7", + "@babel/traverse": "^7.16.10", + "@babel/types": "^7.16.8", "convert-source-map": "^1.7.0", "debug": "^4.1.0", "gensync": "^1.0.0-beta.2", @@ -250,9 +250,9 @@ } }, "node_modules/@babel/highlight": { - "version": "7.16.7", - "resolved": "https://registry.npmjs.org/@babel/highlight/-/highlight-7.16.7.tgz", - "integrity": "sha512-aKpPMfLvGO3Q97V0qhw/V2SWNWlwfJknuwAunU7wZLSfrM4xTBvg7E5opUVi1kJTBKihE38CPg4nBiqX83PWYw==", + "version": "7.16.10", + "resolved": "https://registry.npmjs.org/@babel/highlight/-/highlight-7.16.10.tgz", + "integrity": "sha512-5FnTQLSLswEj6IkgVw5KusNUUFY9ZGqe/TRFnP/BKYHYgfh7tc+C7mwiy95/yNP7Dh9x580Vv8r7u7ZfTBFxdw==", "dev": true, "dependencies": { "@babel/helper-validator-identifier": "^7.16.7", @@ -326,9 +326,9 @@ } }, "node_modules/@babel/parser": { - "version": "7.16.8", - "resolved": "https://registry.npmjs.org/@babel/parser/-/parser-7.16.8.tgz", - "integrity": "sha512-i7jDUfrVBWc+7OKcBzEe5n7fbv3i2fWtxKzzCvOjnzSxMfWMigAhtfJ7qzZNGFNMsCCd67+uz553dYKWXPvCKw==", + "version": "7.16.12", + "resolved": "https://registry.npmjs.org/@babel/parser/-/parser-7.16.12.tgz", + "integrity": "sha512-VfaV15po8RiZssrkPweyvbGVSe4x2y+aciFCgn0n0/SJMR22cwofRV1mtnJQYcSB1wUTaA/X1LnA3es66MCO5A==", "dev": true, "bin": { "parser": "bin/babel-parser.js" @@ -352,9 +352,9 @@ } }, "node_modules/@babel/traverse": { - "version": "7.16.8", - "resolved": "https://registry.npmjs.org/@babel/traverse/-/traverse-7.16.8.tgz", - "integrity": "sha512-xe+H7JlvKsDQwXRsBhSnq1/+9c+LlQcCK3Tn/l5sbx02HYns/cn7ibp9+RV1sIUqu7hKg91NWsgHurO9dowITQ==", + "version": "7.16.10", + "resolved": "https://registry.npmjs.org/@babel/traverse/-/traverse-7.16.10.tgz", + "integrity": "sha512-yzuaYXoRJBGMlBhsMJoUW7G1UmSb/eXr/JHYM/MsOJgavJibLwASijW7oXBdw3NQ6T0bW7Ty5P/VarOs9cHmqw==", "dev": true, "dependencies": { "@babel/code-frame": "^7.16.7", @@ -363,7 +363,7 @@ "@babel/helper-function-name": "^7.16.7", "@babel/helper-hoist-variables": "^7.16.7", "@babel/helper-split-export-declaration": "^7.16.7", - "@babel/parser": "^7.16.8", + "@babel/parser": "^7.16.10", "@babel/types": "^7.16.8", "debug": "^4.1.0", "globals": "^11.1.0" @@ -708,16 +708,16 @@ } }, "node_modules/@octokit/request": { - "version": "5.6.2", - "resolved": "https://registry.npmjs.org/@octokit/request/-/request-5.6.2.tgz", - "integrity": "sha512-je66CvSEVf0jCpRISxkUcCa0UkxmFs6eGDRSbfJtAVwbLH5ceqF+YEyC8lj8ystKyZTy8adWr0qmkY52EfOeLA==", + "version": "5.6.3", + "resolved": "https://registry.npmjs.org/@octokit/request/-/request-5.6.3.tgz", + "integrity": "sha512-bFJl0I1KVc9jYTe9tdGGpAMPy32dLBXXo1dS/YwSCTL/2nd9XeHsY616RE3HPXDVk+a+dBuzyz5YdlXwcDTr2A==", "dev": true, "dependencies": { "@octokit/endpoint": "^6.0.1", "@octokit/request-error": "^2.1.0", "@octokit/types": "^6.16.1", "is-plain-object": "^5.0.0", - "node-fetch": "^2.6.1", + "node-fetch": "^2.6.7", "universal-user-agent": "^6.0.0" } }, @@ -754,9 +754,9 @@ } }, "node_modules/@sindresorhus/is": { - "version": "4.2.1", - "resolved": "https://registry.npmjs.org/@sindresorhus/is/-/is-4.2.1.tgz", - "integrity": "sha512-BrzrgtaqEre0qfvI8sMTaEvx+bayuhPmfe2rfeUGPPHYr/PLxCOqkOe4TQTDPb+qcqgNcsAtXV/Ew74mcDIE8w==", + "version": "4.3.0", + "resolved": "https://registry.npmjs.org/@sindresorhus/is/-/is-4.3.0.tgz", + "integrity": "sha512-wwOvh0eO3PiTEivGJWiZ+b946SlMSb4pe+y+Ur/4S87cwo09pYi+FWHHnbrM3W9W7cBYKDqQXcrFYjYUCOJUEQ==", "dev": true, "engines": { "node": ">=10" @@ -943,14 +943,14 @@ "dev": true }, "node_modules/@typescript-eslint/eslint-plugin": { - "version": "5.10.0", - "resolved": "https://registry.npmjs.org/@typescript-eslint/eslint-plugin/-/eslint-plugin-5.10.0.tgz", - "integrity": "sha512-XXVKnMsq2fuu9K2KsIxPUGqb6xAImz8MEChClbXmE3VbveFtBUU5bzM6IPVWqzyADIgdkS2Ws/6Xo7W2TeZWjQ==", + "version": "5.10.1", + "resolved": "https://registry.npmjs.org/@typescript-eslint/eslint-plugin/-/eslint-plugin-5.10.1.tgz", + "integrity": "sha512-xN3CYqFlyE/qOcy978/L0xLR2HlcAGIyIK5sMOasxaaAPfQRj/MmMV6OC3I7NZO84oEUdWCOju34Z9W8E0pFDQ==", "dev": true, "dependencies": { - "@typescript-eslint/scope-manager": "5.10.0", - "@typescript-eslint/type-utils": "5.10.0", - "@typescript-eslint/utils": "5.10.0", + "@typescript-eslint/scope-manager": "5.10.1", + "@typescript-eslint/type-utils": "5.10.1", + "@typescript-eslint/utils": "5.10.1", "debug": "^4.3.2", "functional-red-black-tree": "^1.0.1", "ignore": "^5.1.8", @@ -991,14 +991,14 @@ } }, "node_modules/@typescript-eslint/parser": { - "version": "5.10.0", - "resolved": "https://registry.npmjs.org/@typescript-eslint/parser/-/parser-5.10.0.tgz", - "integrity": "sha512-pJB2CCeHWtwOAeIxv8CHVGJhI5FNyJAIpx5Pt72YkK3QfEzt6qAlXZuyaBmyfOdM62qU0rbxJzNToPTVeJGrQw==", + "version": "5.10.1", + "resolved": "https://registry.npmjs.org/@typescript-eslint/parser/-/parser-5.10.1.tgz", + "integrity": "sha512-GReo3tjNBwR5RnRO0K2wDIDN31cM3MmDtgyQ85oAxAmC5K3j/g85IjP+cDfcqDsDDBf1HNKQAD0WqOYL8jXqUA==", "dev": true, "dependencies": { - "@typescript-eslint/scope-manager": "5.10.0", - "@typescript-eslint/types": "5.10.0", - "@typescript-eslint/typescript-estree": "5.10.0", + "@typescript-eslint/scope-manager": "5.10.1", + "@typescript-eslint/types": "5.10.1", + "@typescript-eslint/typescript-estree": "5.10.1", "debug": "^4.3.2" }, "engines": { @@ -1018,13 +1018,13 @@ } }, "node_modules/@typescript-eslint/scope-manager": { - "version": "5.10.0", - "resolved": "https://registry.npmjs.org/@typescript-eslint/scope-manager/-/scope-manager-5.10.0.tgz", - "integrity": "sha512-tgNgUgb4MhqK6DoKn3RBhyZ9aJga7EQrw+2/OiDk5hKf3pTVZWyqBi7ukP+Z0iEEDMF5FDa64LqODzlfE4O/Dg==", + "version": "5.10.1", + "resolved": "https://registry.npmjs.org/@typescript-eslint/scope-manager/-/scope-manager-5.10.1.tgz", + "integrity": "sha512-Lyvi559Gvpn94k7+ElXNMEnXu/iundV5uFmCUNnftbFrUbAJ1WBoaGgkbOBm07jVZa682oaBU37ao/NGGX4ZDg==", "dev": true, "dependencies": { - "@typescript-eslint/types": "5.10.0", - "@typescript-eslint/visitor-keys": "5.10.0" + "@typescript-eslint/types": "5.10.1", + "@typescript-eslint/visitor-keys": "5.10.1" }, "engines": { "node": "^12.22.0 || ^14.17.0 || >=16.0.0" @@ -1035,12 +1035,12 @@ } }, "node_modules/@typescript-eslint/type-utils": { - "version": "5.10.0", - "resolved": "https://registry.npmjs.org/@typescript-eslint/type-utils/-/type-utils-5.10.0.tgz", - "integrity": "sha512-TzlyTmufJO5V886N+hTJBGIfnjQDQ32rJYxPaeiyWKdjsv2Ld5l8cbS7pxim4DeNs62fKzRSt8Q14Evs4JnZyQ==", + "version": "5.10.1", + "resolved": "https://registry.npmjs.org/@typescript-eslint/type-utils/-/type-utils-5.10.1.tgz", + "integrity": "sha512-AfVJkV8uck/UIoDqhu+ptEdBoQATON9GXnhOpPLzkQRJcSChkvD//qsz9JVffl2goxX+ybs5klvacE9vmrQyCw==", "dev": true, "dependencies": { - "@typescript-eslint/utils": "5.10.0", + "@typescript-eslint/utils": "5.10.1", "debug": "^4.3.2", "tsutils": "^3.21.0" }, @@ -1061,9 +1061,9 @@ } }, "node_modules/@typescript-eslint/types": { - "version": "5.10.0", - "resolved": "https://registry.npmjs.org/@typescript-eslint/types/-/types-5.10.0.tgz", - "integrity": "sha512-wUljCgkqHsMZbw60IbOqT/puLfyqqD5PquGiBo1u1IS3PLxdi3RDGlyf032IJyh+eQoGhz9kzhtZa+VC4eWTlQ==", + "version": "5.10.1", + "resolved": "https://registry.npmjs.org/@typescript-eslint/types/-/types-5.10.1.tgz", + "integrity": "sha512-ZvxQ2QMy49bIIBpTqFiOenucqUyjTQ0WNLhBM6X1fh1NNlYAC6Kxsx8bRTY3jdYsYg44a0Z/uEgQkohbR0H87Q==", "dev": true, "engines": { "node": "^12.22.0 || ^14.17.0 || >=16.0.0" @@ -1074,13 +1074,13 @@ } }, "node_modules/@typescript-eslint/typescript-estree": { - "version": "5.10.0", - "resolved": "https://registry.npmjs.org/@typescript-eslint/typescript-estree/-/typescript-estree-5.10.0.tgz", - "integrity": "sha512-x+7e5IqfwLwsxTdliHRtlIYkgdtYXzE0CkFeV6ytAqq431ZyxCFzNMNR5sr3WOlIG/ihVZr9K/y71VHTF/DUQA==", + "version": "5.10.1", + "resolved": "https://registry.npmjs.org/@typescript-eslint/typescript-estree/-/typescript-estree-5.10.1.tgz", + "integrity": "sha512-PwIGnH7jIueXv4opcwEbVGDATjGPO1dx9RkUl5LlHDSe+FXxPwFL5W/qYd5/NHr7f6lo/vvTrAzd0KlQtRusJQ==", "dev": true, "dependencies": { - "@typescript-eslint/types": "5.10.0", - "@typescript-eslint/visitor-keys": "5.10.0", + "@typescript-eslint/types": "5.10.1", + "@typescript-eslint/visitor-keys": "5.10.1", "debug": "^4.3.2", "globby": "^11.0.4", "is-glob": "^4.0.3", @@ -1145,15 +1145,15 @@ } }, "node_modules/@typescript-eslint/utils": { - "version": "5.10.0", - "resolved": "https://registry.npmjs.org/@typescript-eslint/utils/-/utils-5.10.0.tgz", - "integrity": "sha512-IGYwlt1CVcFoE2ueW4/ioEwybR60RAdGeiJX/iDAw0t5w0wK3S7QncDwpmsM70nKgGTuVchEWB8lwZwHqPAWRg==", + "version": "5.10.1", + "resolved": "https://registry.npmjs.org/@typescript-eslint/utils/-/utils-5.10.1.tgz", + "integrity": "sha512-RRmlITiUbLuTRtn/gcPRi4202niF+q7ylFLCKu4c+O/PcpRvZ/nAUwQ2G00bZgpWkhrNLNnvhZLbDn8Ml0qsQw==", "dev": true, "dependencies": { "@types/json-schema": "^7.0.9", - "@typescript-eslint/scope-manager": "5.10.0", - "@typescript-eslint/types": "5.10.0", - "@typescript-eslint/typescript-estree": "5.10.0", + "@typescript-eslint/scope-manager": "5.10.1", + "@typescript-eslint/types": "5.10.1", + "@typescript-eslint/typescript-estree": "5.10.1", "eslint-scope": "^5.1.1", "eslint-utils": "^3.0.0" }, @@ -1169,12 +1169,12 @@ } }, "node_modules/@typescript-eslint/visitor-keys": { - "version": "5.10.0", - "resolved": "https://registry.npmjs.org/@typescript-eslint/visitor-keys/-/visitor-keys-5.10.0.tgz", - "integrity": "sha512-GMxj0K1uyrFLPKASLmZzCuSddmjZVbVj3Ouy5QVuIGKZopxvOr24JsS7gruz6C3GExE01mublZ3mIBOaon9zuQ==", + "version": "5.10.1", + "resolved": "https://registry.npmjs.org/@typescript-eslint/visitor-keys/-/visitor-keys-5.10.1.tgz", + "integrity": "sha512-NjQ0Xinhy9IL979tpoTRuLKxMc0zJC7QVSdeerXs2/QvOy2yRkzX5dRb10X5woNUdJgU8G3nYRDlI33sq1K4YQ==", "dev": true, "dependencies": { - "@typescript-eslint/types": "5.10.0", + "@typescript-eslint/types": "5.10.1", "eslint-visitor-keys": "^3.0.0" }, "engines": { @@ -1654,9 +1654,9 @@ } }, "node_modules/caniuse-lite": { - "version": "1.0.30001299", - "resolved": "https://registry.npmjs.org/caniuse-lite/-/caniuse-lite-1.0.30001299.tgz", - "integrity": "sha512-iujN4+x7QzqA2NCSrS5VUy+4gLmRd4xv6vbBBsmfVqTx8bLAD8097euLqQgKxSVLvxjSDcvF1T/i9ocgnUFexw==", + "version": "1.0.30001301", + "resolved": "https://registry.npmjs.org/caniuse-lite/-/caniuse-lite-1.0.30001301.tgz", + "integrity": "sha512-csfD/GpHMqgEL3V3uIgosvh+SVIQvCh43SNu9HRbP1lnxkKm1kjDG4f32PP571JplkLjfS+mg2p1gxR7MYrrIA==", "dev": true, "funding": { "type": "opencollective", @@ -1686,10 +1686,16 @@ "dev": true }, "node_modules/chokidar": { - "version": "3.5.2", - "resolved": "https://registry.npmjs.org/chokidar/-/chokidar-3.5.2.tgz", - "integrity": "sha512-ekGhOnNVPgT77r4K/U3GDhu+FQ2S8TnK/s2KbIGXi0SZWuwkZ2QNyfWdZW+TVfn84DpEP7rLeCt2UI6bJ8GwbQ==", + "version": "3.5.3", + "resolved": "https://registry.npmjs.org/chokidar/-/chokidar-3.5.3.tgz", + "integrity": "sha512-Dr3sfKRP6oTcjf2JmUmFJfeVMvXBdegxB0iVQ5eb2V10uFJUCAS8OByZdVAyVb8xXNz3GjjTgj9kLWsZTqE6kw==", "dev": true, + "funding": [ + { + "type": "individual", + "url": "https://paulmillr.com/funding/" + } + ], "dependencies": { "anymatch": "~3.1.2", "braces": "~3.0.2", @@ -2044,19 +2050,6 @@ "node": ">=0.4.0" } }, - "node_modules/deprecated-obj": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/deprecated-obj/-/deprecated-obj-2.0.0.tgz", - "integrity": "sha512-CkdywZC2rJ8RGh+y3MM1fw1EJ4oO/oNExGbRFv0AQoMS+faTd3nO7slYjkj/6t8OnIMUE+wxh6G97YHhK1ytrw==", - "dev": true, - "dependencies": { - "flat": "^5.0.2", - "lodash": "^4.17.20" - }, - "engines": { - "node": ">=10" - } - }, "node_modules/deprecation": { "version": "2.3.1", "resolved": "https://registry.npmjs.org/deprecation/-/deprecation-2.3.1.tgz", @@ -2115,9 +2108,9 @@ "dev": true }, "node_modules/electron-to-chromium": { - "version": "1.4.44", - "resolved": "https://registry.npmjs.org/electron-to-chromium/-/electron-to-chromium-1.4.44.tgz", - "integrity": "sha512-tHGWiUUmY7GABK8+DNcr474cnZDTzD8x1736SlDosVH8+/vRJeqfaIBAEHFtMjddz/0T4rKKYsxEc8BwQRdBpw==", + "version": "1.4.51", + "resolved": "https://registry.npmjs.org/electron-to-chromium/-/electron-to-chromium-1.4.51.tgz", + "integrity": "sha512-JNEmcYl3mk1tGQmy0EvL5eik/CKSBuzAyGP0QFdG6LIgxQe3II0BL1m2zKc2MZMf3uGqHWE1TFddJML0RpjSHQ==", "dev": true }, "node_modules/email-addresses": { @@ -2473,9 +2466,9 @@ "dev": true }, "node_modules/fast-glob": { - "version": "3.2.10", - "resolved": "https://registry.npmjs.org/fast-glob/-/fast-glob-3.2.10.tgz", - "integrity": "sha512-s9nFhFnvR63wls6/kM88kQqDhMu0AfdjqouE2l5GVQPbqLgyFjjU5ry/r2yKsJxpb9Py1EYNqieFrmMaX4v++A==", + "version": "3.2.11", + "resolved": "https://registry.npmjs.org/fast-glob/-/fast-glob-3.2.11.tgz", + "integrity": "sha512-xrO3+1bxSo3ZVHAnqzyuewYT6aMFHRAd4Kcs92MAonjwQZLsK9d0SF1IyQ3k5PoirxTW0Oe/RqFgMQ6TcNE5Ew==", "dev": true, "dependencies": { "@nodelib/fs.stat": "^2.0.2", @@ -3953,32 +3946,32 @@ "dev": true }, "node_modules/mocha": { - "version": "9.1.4", - "resolved": "https://registry.npmjs.org/mocha/-/mocha-9.1.4.tgz", - "integrity": "sha512-+q2aV5VlJZuLgCWoBvGI5zEwPF9eEI0kr/sAA9Jm4xMND7RfIEyF8JE7C0JIg8WXRG+P1sdIAb5ccoHPlXLzcw==", + "version": "9.2.0", + "resolved": "https://registry.npmjs.org/mocha/-/mocha-9.2.0.tgz", + "integrity": "sha512-kNn7E8g2SzVcq0a77dkphPsDSN7P+iYkqE0ZsGCYWRsoiKjOt+NvXfaagik8vuDa6W5Zw3qxe8Jfpt5qKf+6/Q==", "dev": true, "dependencies": { "@ungap/promise-all-settled": "1.1.2", "ansi-colors": "4.1.1", "browser-stdout": "1.3.1", - "chokidar": "3.5.2", - "debug": "4.3.2", + "chokidar": "3.5.3", + "debug": "4.3.3", "diff": "5.0.0", "escape-string-regexp": "4.0.0", "find-up": "5.0.0", - "glob": "7.1.7", + "glob": "7.2.0", "growl": "1.10.5", "he": "1.2.0", "js-yaml": "4.1.0", "log-symbols": "4.1.0", "minimatch": "3.0.4", "ms": "2.1.3", - "nanoid": "3.1.25", + "nanoid": "3.2.0", "serialize-javascript": "6.0.0", "strip-json-comments": "3.1.1", "supports-color": "8.1.1", "which": "2.0.2", - "workerpool": "6.1.5", + "workerpool": "6.2.0", "yargs": "16.2.0", "yargs-parser": "20.2.4", "yargs-unparser": "2.0.0" @@ -3995,29 +3988,6 @@ "url": "https://opencollective.com/mochajs" } }, - "node_modules/mocha/node_modules/debug": { - "version": "4.3.2", - "resolved": "https://registry.npmjs.org/debug/-/debug-4.3.2.tgz", - "integrity": "sha512-mOp8wKcvj7XxC78zLgw/ZA+6TSgkoE2C/ienthhRD298T7UNwAg9diBpLRxC0mOezLl4B0xV7M0cCO6P/O0Xhw==", - "dev": true, - "dependencies": { - "ms": "2.1.2" - }, - "engines": { - "node": ">=6.0" - }, - "peerDependenciesMeta": { - "supports-color": { - "optional": true - } - } - }, - "node_modules/mocha/node_modules/debug/node_modules/ms": { - "version": "2.1.2", - "resolved": "https://registry.npmjs.org/ms/-/ms-2.1.2.tgz", - "integrity": "sha512-sGkPx+VjMtmA6MX27oA4FBFELFCZZ4S4XqeGOXCv68tT+jb3vk/RyaKWP0PTKyWtmLSM0b+adUTEvbs1PEaH2w==", - "dev": true - }, "node_modules/mocha/node_modules/escape-string-regexp": { "version": "4.0.0", "resolved": "https://registry.npmjs.org/escape-string-regexp/-/escape-string-regexp-4.0.0.tgz", @@ -4046,26 +4016,6 @@ "url": "https://github.com/sponsors/sindresorhus" } }, - "node_modules/mocha/node_modules/glob": { - "version": "7.1.7", - "resolved": "https://registry.npmjs.org/glob/-/glob-7.1.7.tgz", - "integrity": "sha512-OvD9ENzPLbegENnYP5UUfJIirTg4+XwMWGaQfQTY0JenxNvvIKP3U3/tAQSPIu/lHxXYSZmpXlUHeqAIdKzBLQ==", - "dev": true, - "dependencies": { - "fs.realpath": "^1.0.0", - "inflight": "^1.0.4", - "inherits": "2", - "minimatch": "^3.0.4", - "once": "^1.3.0", - "path-is-absolute": "^1.0.0" - }, - "engines": { - "node": "*" - }, - "funding": { - "url": "https://github.com/sponsors/isaacs" - } - }, "node_modules/mocha/node_modules/locate-path": { "version": "6.0.0", "resolved": "https://registry.npmjs.org/locate-path/-/locate-path-6.0.0.tgz", @@ -4184,9 +4134,9 @@ "dev": true }, "node_modules/nanoid": { - "version": "3.1.25", - "resolved": "https://registry.npmjs.org/nanoid/-/nanoid-3.1.25.tgz", - "integrity": "sha512-rdwtIXaXCLFAQbnfqDRnI6jaRHp9fTcYBjtFKE8eezcZ7LuLjhUaQGNeMXf1HmRoCH32CLz6XwX0TtxEOS/A3Q==", + "version": "3.2.0", + "resolved": "https://registry.npmjs.org/nanoid/-/nanoid-3.2.0.tgz", + "integrity": "sha512-fmsZYa9lpn69Ad5eDn7FMcnnSR+8R34W9qJEijxYhTbfOWzr22n1QxCMzXLK+ODyW2973V3Fux959iQoUxzUIA==", "dev": true, "bin": { "nanoid": "bin/nanoid.cjs" @@ -4236,15 +4186,23 @@ } }, "node_modules/node-fetch": { - "version": "2.6.6", - "resolved": "https://registry.npmjs.org/node-fetch/-/node-fetch-2.6.6.tgz", - "integrity": "sha512-Z8/6vRlTUChSdIgMa51jxQ4lrw/Jy5SOW10ObaA47/RElsAN2c5Pn8bTgFGWn/ibwzXTE8qwr1Yzx28vsecXEA==", + "version": "2.6.7", + "resolved": "https://registry.npmjs.org/node-fetch/-/node-fetch-2.6.7.tgz", + "integrity": "sha512-ZjMPFEfVx5j+y2yF35Kzx5sF7kDzxuDj6ziH4FFbOp87zKDZNx8yExJIb05OGF4Nlt9IHFIMBkRl41VdvcNdbQ==", "dev": true, "dependencies": { "whatwg-url": "^5.0.0" }, "engines": { "node": "4.x || >=6.0.0" + }, + "peerDependencies": { + "encoding": "^0.1.0" + }, + "peerDependenciesMeta": { + "encoding": { + "optional": true + } } }, "node_modules/node-preload": { @@ -5220,9 +5178,9 @@ } }, "node_modules/release-it": { - "version": "14.12.3", - "resolved": "https://registry.npmjs.org/release-it/-/release-it-14.12.3.tgz", - "integrity": "sha512-qek7ml9WaxpXSjLpU4UGCF9nWpDgOODL1gZTuydafs1HdQPAeYOd2od8I8lUL4NlEKW2TirDhH4aFTVIpP3/cQ==", + "version": "14.12.4", + "resolved": "https://registry.npmjs.org/release-it/-/release-it-14.12.4.tgz", + "integrity": "sha512-lqf9PMsj7ycCqFHGag8Uv7cE1hNsKa+yKUMe+Fkh9fdOfxu2F01On+YUefRCP0DuQthmr/WyLCYdrjThMEkWFQ==", "dev": true, "dependencies": { "@iarna/toml": "2.2.5", @@ -5231,7 +5189,6 @@ "chalk": "4.1.2", "cosmiconfig": "7.0.1", "debug": "4.3.3", - "deprecated-obj": "2.0.0", "execa": "5.1.1", "form-data": "4.0.0", "git-url-parse": "11.6.0", @@ -5334,12 +5291,12 @@ "dev": true }, "node_modules/resolve": { - "version": "1.21.0", - "resolved": "https://registry.npmjs.org/resolve/-/resolve-1.21.0.tgz", - "integrity": "sha512-3wCbTpk5WJlyE4mSOtDLhqQmGFi0/TD9VPwmiolnk8U0wRgMEktqCXd3vy5buTO3tljvalNvKrjHEfrd2WpEKA==", + "version": "1.22.0", + "resolved": "https://registry.npmjs.org/resolve/-/resolve-1.22.0.tgz", + "integrity": "sha512-Hhtrw0nLeSrFQ7phPp4OOcVjLPIeMnRlr5mcnVuMe7M/7eBn98A3hmFRLoFo3DLZkivSYwhRUJTyPyWAk56WLw==", "dev": true, "dependencies": { - "is-core-module": "^2.8.0", + "is-core-module": "^2.8.1", "path-parse": "^1.0.7", "supports-preserve-symlinks-flag": "^1.0.0" }, @@ -6314,9 +6271,9 @@ } }, "node_modules/workerpool": { - "version": "6.1.5", - "resolved": "https://registry.npmjs.org/workerpool/-/workerpool-6.1.5.tgz", - "integrity": "sha512-XdKkCK0Zqc6w3iTxLckiuJ81tiD/o5rBE/m+nXpRCB+/Sq4DqkfXZ/x0jW02DG1tGsfUGXbTJyZDP+eu67haSw==", + "version": "6.2.0", + "resolved": "https://registry.npmjs.org/workerpool/-/workerpool-6.2.0.tgz", + "integrity": "sha512-Rsk5qQHJ9eowMH28Jwhe8HEbmdYDX4lwoMWshiCXugjtHqMD9ZbiqSDLxcsfdqsETPzVUtX5s1Z5kStiIM6l4A==", "dev": true }, "node_modules/wrap-ansi": { @@ -6479,7 +6436,7 @@ "@node-redis/test-utils": "*", "@types/node": "^17.0.10", "nyc": "^15.1.0", - "release-it": "^14.12.3", + "release-it": "^14.12.4", "source-map-support": "^0.5.21", "ts-node": "^10.4.0", "typedoc": "^0.22.11", @@ -6506,11 +6463,11 @@ "@types/redis-parser": "^3.0.0", "@types/sinon": "^10.0.8", "@types/yallist": "^4.0.1", - "@typescript-eslint/eslint-plugin": "^5.10.0", - "@typescript-eslint/parser": "^5.10.0", + "@typescript-eslint/eslint-plugin": "^5.10.1", + "@typescript-eslint/parser": "^5.10.1", "eslint": "^8.7.0", "nyc": "^15.1.0", - "release-it": "^14.12.3", + "release-it": "^14.12.4", "sinon": "^12.0.1", "source-map-support": "^0.5.21", "ts-node": "^10.4.0", @@ -6530,7 +6487,7 @@ "@node-redis/test-utils": "*", "@types/node": "^17.0.10", "nyc": "^15.1.0", - "release-it": "^14.12.3", + "release-it": "^14.12.4", "source-map-support": "^0.5.21", "ts-node": "^10.4.0", "typedoc": "^0.22.11", @@ -6549,7 +6506,7 @@ "@node-redis/test-utils": "*", "@types/node": "^17.0.10", "nyc": "^15.1.0", - "release-it": "^14.12.3", + "release-it": "^14.12.4", "source-map-support": "^0.5.21", "ts-node": "^10.4.0", "typedoc": "^0.22.11", @@ -6566,7 +6523,7 @@ "@types/mocha": "^9.1.0", "@types/node": "^17.0.10", "@types/yargs": "^17.0.8", - "mocha": "^9.1.4", + "mocha": "^9.2.0", "nyc": "^15.1.0", "source-map-support": "^0.5.21", "ts-node": "^10.4.0", @@ -6586,7 +6543,7 @@ "@node-redis/test-utils": "*", "@types/node": "^17.0.10", "nyc": "^15.1.0", - "release-it": "^14.12.3", + "release-it": "^14.12.4", "source-map-support": "^0.5.21", "ts-node": "^10.4.0", "typedoc": "^0.22.11", @@ -6614,20 +6571,20 @@ "dev": true }, "@babel/core": { - "version": "7.16.7", - "resolved": "https://registry.npmjs.org/@babel/core/-/core-7.16.7.tgz", - "integrity": "sha512-aeLaqcqThRNZYmbMqtulsetOQZ/5gbR/dWruUCJcpas4Qoyy+QeagfDsPdMrqwsPRDNxJvBlRiZxxX7THO7qtA==", + "version": "7.16.12", + "resolved": "https://registry.npmjs.org/@babel/core/-/core-7.16.12.tgz", + "integrity": "sha512-dK5PtG1uiN2ikk++5OzSYsitZKny4wOCD0nrO4TqnW4BVBTQ2NGS3NgilvT/TEyxTST7LNyWV/T4tXDoD3fOgg==", "dev": true, "requires": { "@babel/code-frame": "^7.16.7", - "@babel/generator": "^7.16.7", + "@babel/generator": "^7.16.8", "@babel/helper-compilation-targets": "^7.16.7", "@babel/helper-module-transforms": "^7.16.7", "@babel/helpers": "^7.16.7", - "@babel/parser": "^7.16.7", + "@babel/parser": "^7.16.12", "@babel/template": "^7.16.7", - "@babel/traverse": "^7.16.7", - "@babel/types": "^7.16.7", + "@babel/traverse": "^7.16.10", + "@babel/types": "^7.16.8", "convert-source-map": "^1.7.0", "debug": "^4.1.0", "gensync": "^1.0.0-beta.2", @@ -6764,9 +6721,9 @@ } }, "@babel/highlight": { - "version": "7.16.7", - "resolved": "https://registry.npmjs.org/@babel/highlight/-/highlight-7.16.7.tgz", - "integrity": "sha512-aKpPMfLvGO3Q97V0qhw/V2SWNWlwfJknuwAunU7wZLSfrM4xTBvg7E5opUVi1kJTBKihE38CPg4nBiqX83PWYw==", + "version": "7.16.10", + "resolved": "https://registry.npmjs.org/@babel/highlight/-/highlight-7.16.10.tgz", + "integrity": "sha512-5FnTQLSLswEj6IkgVw5KusNUUFY9ZGqe/TRFnP/BKYHYgfh7tc+C7mwiy95/yNP7Dh9x580Vv8r7u7ZfTBFxdw==", "dev": true, "requires": { "@babel/helper-validator-identifier": "^7.16.7", @@ -6827,9 +6784,9 @@ } }, "@babel/parser": { - "version": "7.16.8", - "resolved": "https://registry.npmjs.org/@babel/parser/-/parser-7.16.8.tgz", - "integrity": "sha512-i7jDUfrVBWc+7OKcBzEe5n7fbv3i2fWtxKzzCvOjnzSxMfWMigAhtfJ7qzZNGFNMsCCd67+uz553dYKWXPvCKw==", + "version": "7.16.12", + "resolved": "https://registry.npmjs.org/@babel/parser/-/parser-7.16.12.tgz", + "integrity": "sha512-VfaV15po8RiZssrkPweyvbGVSe4x2y+aciFCgn0n0/SJMR22cwofRV1mtnJQYcSB1wUTaA/X1LnA3es66MCO5A==", "dev": true }, "@babel/template": { @@ -6844,9 +6801,9 @@ } }, "@babel/traverse": { - "version": "7.16.8", - "resolved": "https://registry.npmjs.org/@babel/traverse/-/traverse-7.16.8.tgz", - "integrity": "sha512-xe+H7JlvKsDQwXRsBhSnq1/+9c+LlQcCK3Tn/l5sbx02HYns/cn7ibp9+RV1sIUqu7hKg91NWsgHurO9dowITQ==", + "version": "7.16.10", + "resolved": "https://registry.npmjs.org/@babel/traverse/-/traverse-7.16.10.tgz", + "integrity": "sha512-yzuaYXoRJBGMlBhsMJoUW7G1UmSb/eXr/JHYM/MsOJgavJibLwASijW7oXBdw3NQ6T0bW7Ty5P/VarOs9cHmqw==", "dev": true, "requires": { "@babel/code-frame": "^7.16.7", @@ -6855,7 +6812,7 @@ "@babel/helper-function-name": "^7.16.7", "@babel/helper-hoist-variables": "^7.16.7", "@babel/helper-split-export-declaration": "^7.16.7", - "@babel/parser": "^7.16.8", + "@babel/parser": "^7.16.10", "@babel/types": "^7.16.8", "debug": "^4.1.0", "globals": "^11.1.0" @@ -7016,7 +6973,7 @@ "@node-redis/test-utils": "*", "@types/node": "^17.0.10", "nyc": "^15.1.0", - "release-it": "^14.12.3", + "release-it": "^14.12.4", "source-map-support": "^0.5.21", "ts-node": "^10.4.0", "typedoc": "^0.22.11", @@ -7032,14 +6989,14 @@ "@types/redis-parser": "^3.0.0", "@types/sinon": "^10.0.8", "@types/yallist": "^4.0.1", - "@typescript-eslint/eslint-plugin": "^5.10.0", - "@typescript-eslint/parser": "^5.10.0", + "@typescript-eslint/eslint-plugin": "^5.10.1", + "@typescript-eslint/parser": "^5.10.1", "cluster-key-slot": "1.1.0", "eslint": "^8.7.0", "generic-pool": "3.8.2", "nyc": "^15.1.0", "redis-parser": "3.0.0", - "release-it": "^14.12.3", + "release-it": "^14.12.4", "sinon": "^12.0.1", "source-map-support": "^0.5.21", "ts-node": "^10.4.0", @@ -7055,7 +7012,7 @@ "@node-redis/test-utils": "*", "@types/node": "^17.0.10", "nyc": "^15.1.0", - "release-it": "^14.12.3", + "release-it": "^14.12.4", "source-map-support": "^0.5.21", "ts-node": "^10.4.0", "typedoc": "^0.22.11", @@ -7069,7 +7026,7 @@ "@node-redis/test-utils": "*", "@types/node": "^17.0.10", "nyc": "^15.1.0", - "release-it": "^14.12.3", + "release-it": "^14.12.4", "source-map-support": "^0.5.21", "ts-node": "^10.4.0", "typedoc": "^0.22.11", @@ -7083,7 +7040,7 @@ "@types/mocha": "^9.1.0", "@types/node": "^17.0.10", "@types/yargs": "^17.0.8", - "mocha": "^9.1.4", + "mocha": "^9.2.0", "nyc": "^15.1.0", "source-map-support": "^0.5.21", "ts-node": "^10.4.0", @@ -7098,7 +7055,7 @@ "@node-redis/test-utils": "*", "@types/node": "^17.0.10", "nyc": "^15.1.0", - "release-it": "^14.12.3", + "release-it": "^14.12.4", "source-map-support": "^0.5.21", "ts-node": "^10.4.0", "typedoc": "^0.22.11", @@ -7210,16 +7167,16 @@ } }, "@octokit/request": { - "version": "5.6.2", - "resolved": "https://registry.npmjs.org/@octokit/request/-/request-5.6.2.tgz", - "integrity": "sha512-je66CvSEVf0jCpRISxkUcCa0UkxmFs6eGDRSbfJtAVwbLH5ceqF+YEyC8lj8ystKyZTy8adWr0qmkY52EfOeLA==", + "version": "5.6.3", + "resolved": "https://registry.npmjs.org/@octokit/request/-/request-5.6.3.tgz", + "integrity": "sha512-bFJl0I1KVc9jYTe9tdGGpAMPy32dLBXXo1dS/YwSCTL/2nd9XeHsY616RE3HPXDVk+a+dBuzyz5YdlXwcDTr2A==", "dev": true, "requires": { "@octokit/endpoint": "^6.0.1", "@octokit/request-error": "^2.1.0", "@octokit/types": "^6.16.1", "is-plain-object": "^5.0.0", - "node-fetch": "^2.6.1", + "node-fetch": "^2.6.7", "universal-user-agent": "^6.0.0" } }, @@ -7256,9 +7213,9 @@ } }, "@sindresorhus/is": { - "version": "4.2.1", - "resolved": "https://registry.npmjs.org/@sindresorhus/is/-/is-4.2.1.tgz", - "integrity": "sha512-BrzrgtaqEre0qfvI8sMTaEvx+bayuhPmfe2rfeUGPPHYr/PLxCOqkOe4TQTDPb+qcqgNcsAtXV/Ew74mcDIE8w==", + "version": "4.3.0", + "resolved": "https://registry.npmjs.org/@sindresorhus/is/-/is-4.3.0.tgz", + "integrity": "sha512-wwOvh0eO3PiTEivGJWiZ+b946SlMSb4pe+y+Ur/4S87cwo09pYi+FWHHnbrM3W9W7cBYKDqQXcrFYjYUCOJUEQ==", "dev": true }, "@sinonjs/commons": { @@ -7436,14 +7393,14 @@ "dev": true }, "@typescript-eslint/eslint-plugin": { - "version": "5.10.0", - "resolved": "https://registry.npmjs.org/@typescript-eslint/eslint-plugin/-/eslint-plugin-5.10.0.tgz", - "integrity": "sha512-XXVKnMsq2fuu9K2KsIxPUGqb6xAImz8MEChClbXmE3VbveFtBUU5bzM6IPVWqzyADIgdkS2Ws/6Xo7W2TeZWjQ==", + "version": "5.10.1", + "resolved": "https://registry.npmjs.org/@typescript-eslint/eslint-plugin/-/eslint-plugin-5.10.1.tgz", + "integrity": "sha512-xN3CYqFlyE/qOcy978/L0xLR2HlcAGIyIK5sMOasxaaAPfQRj/MmMV6OC3I7NZO84oEUdWCOju34Z9W8E0pFDQ==", "dev": true, "requires": { - "@typescript-eslint/scope-manager": "5.10.0", - "@typescript-eslint/type-utils": "5.10.0", - "@typescript-eslint/utils": "5.10.0", + "@typescript-eslint/scope-manager": "5.10.1", + "@typescript-eslint/type-utils": "5.10.1", + "@typescript-eslint/utils": "5.10.1", "debug": "^4.3.2", "functional-red-black-tree": "^1.0.1", "ignore": "^5.1.8", @@ -7464,52 +7421,52 @@ } }, "@typescript-eslint/parser": { - "version": "5.10.0", - "resolved": "https://registry.npmjs.org/@typescript-eslint/parser/-/parser-5.10.0.tgz", - "integrity": "sha512-pJB2CCeHWtwOAeIxv8CHVGJhI5FNyJAIpx5Pt72YkK3QfEzt6qAlXZuyaBmyfOdM62qU0rbxJzNToPTVeJGrQw==", + "version": "5.10.1", + "resolved": "https://registry.npmjs.org/@typescript-eslint/parser/-/parser-5.10.1.tgz", + "integrity": "sha512-GReo3tjNBwR5RnRO0K2wDIDN31cM3MmDtgyQ85oAxAmC5K3j/g85IjP+cDfcqDsDDBf1HNKQAD0WqOYL8jXqUA==", "dev": true, "requires": { - "@typescript-eslint/scope-manager": "5.10.0", - "@typescript-eslint/types": "5.10.0", - "@typescript-eslint/typescript-estree": "5.10.0", + "@typescript-eslint/scope-manager": "5.10.1", + "@typescript-eslint/types": "5.10.1", + "@typescript-eslint/typescript-estree": "5.10.1", "debug": "^4.3.2" } }, "@typescript-eslint/scope-manager": { - "version": "5.10.0", - "resolved": "https://registry.npmjs.org/@typescript-eslint/scope-manager/-/scope-manager-5.10.0.tgz", - "integrity": "sha512-tgNgUgb4MhqK6DoKn3RBhyZ9aJga7EQrw+2/OiDk5hKf3pTVZWyqBi7ukP+Z0iEEDMF5FDa64LqODzlfE4O/Dg==", + "version": "5.10.1", + "resolved": "https://registry.npmjs.org/@typescript-eslint/scope-manager/-/scope-manager-5.10.1.tgz", + "integrity": "sha512-Lyvi559Gvpn94k7+ElXNMEnXu/iundV5uFmCUNnftbFrUbAJ1WBoaGgkbOBm07jVZa682oaBU37ao/NGGX4ZDg==", "dev": true, "requires": { - "@typescript-eslint/types": "5.10.0", - "@typescript-eslint/visitor-keys": "5.10.0" + "@typescript-eslint/types": "5.10.1", + "@typescript-eslint/visitor-keys": "5.10.1" } }, "@typescript-eslint/type-utils": { - "version": "5.10.0", - "resolved": "https://registry.npmjs.org/@typescript-eslint/type-utils/-/type-utils-5.10.0.tgz", - "integrity": "sha512-TzlyTmufJO5V886N+hTJBGIfnjQDQ32rJYxPaeiyWKdjsv2Ld5l8cbS7pxim4DeNs62fKzRSt8Q14Evs4JnZyQ==", + "version": "5.10.1", + "resolved": "https://registry.npmjs.org/@typescript-eslint/type-utils/-/type-utils-5.10.1.tgz", + "integrity": "sha512-AfVJkV8uck/UIoDqhu+ptEdBoQATON9GXnhOpPLzkQRJcSChkvD//qsz9JVffl2goxX+ybs5klvacE9vmrQyCw==", "dev": true, "requires": { - "@typescript-eslint/utils": "5.10.0", + "@typescript-eslint/utils": "5.10.1", "debug": "^4.3.2", "tsutils": "^3.21.0" } }, "@typescript-eslint/types": { - "version": "5.10.0", - "resolved": "https://registry.npmjs.org/@typescript-eslint/types/-/types-5.10.0.tgz", - "integrity": "sha512-wUljCgkqHsMZbw60IbOqT/puLfyqqD5PquGiBo1u1IS3PLxdi3RDGlyf032IJyh+eQoGhz9kzhtZa+VC4eWTlQ==", + "version": "5.10.1", + "resolved": "https://registry.npmjs.org/@typescript-eslint/types/-/types-5.10.1.tgz", + "integrity": "sha512-ZvxQ2QMy49bIIBpTqFiOenucqUyjTQ0WNLhBM6X1fh1NNlYAC6Kxsx8bRTY3jdYsYg44a0Z/uEgQkohbR0H87Q==", "dev": true }, "@typescript-eslint/typescript-estree": { - "version": "5.10.0", - "resolved": "https://registry.npmjs.org/@typescript-eslint/typescript-estree/-/typescript-estree-5.10.0.tgz", - "integrity": "sha512-x+7e5IqfwLwsxTdliHRtlIYkgdtYXzE0CkFeV6ytAqq431ZyxCFzNMNR5sr3WOlIG/ihVZr9K/y71VHTF/DUQA==", + "version": "5.10.1", + "resolved": "https://registry.npmjs.org/@typescript-eslint/typescript-estree/-/typescript-estree-5.10.1.tgz", + "integrity": "sha512-PwIGnH7jIueXv4opcwEbVGDATjGPO1dx9RkUl5LlHDSe+FXxPwFL5W/qYd5/NHr7f6lo/vvTrAzd0KlQtRusJQ==", "dev": true, "requires": { - "@typescript-eslint/types": "5.10.0", - "@typescript-eslint/visitor-keys": "5.10.0", + "@typescript-eslint/types": "5.10.1", + "@typescript-eslint/visitor-keys": "5.10.1", "debug": "^4.3.2", "globby": "^11.0.4", "is-glob": "^4.0.3", @@ -7549,26 +7506,26 @@ } }, "@typescript-eslint/utils": { - "version": "5.10.0", - "resolved": "https://registry.npmjs.org/@typescript-eslint/utils/-/utils-5.10.0.tgz", - "integrity": "sha512-IGYwlt1CVcFoE2ueW4/ioEwybR60RAdGeiJX/iDAw0t5w0wK3S7QncDwpmsM70nKgGTuVchEWB8lwZwHqPAWRg==", + "version": "5.10.1", + "resolved": "https://registry.npmjs.org/@typescript-eslint/utils/-/utils-5.10.1.tgz", + "integrity": "sha512-RRmlITiUbLuTRtn/gcPRi4202niF+q7ylFLCKu4c+O/PcpRvZ/nAUwQ2G00bZgpWkhrNLNnvhZLbDn8Ml0qsQw==", "dev": true, "requires": { "@types/json-schema": "^7.0.9", - "@typescript-eslint/scope-manager": "5.10.0", - "@typescript-eslint/types": "5.10.0", - "@typescript-eslint/typescript-estree": "5.10.0", + "@typescript-eslint/scope-manager": "5.10.1", + "@typescript-eslint/types": "5.10.1", + "@typescript-eslint/typescript-estree": "5.10.1", "eslint-scope": "^5.1.1", "eslint-utils": "^3.0.0" } }, "@typescript-eslint/visitor-keys": { - "version": "5.10.0", - "resolved": "https://registry.npmjs.org/@typescript-eslint/visitor-keys/-/visitor-keys-5.10.0.tgz", - "integrity": "sha512-GMxj0K1uyrFLPKASLmZzCuSddmjZVbVj3Ouy5QVuIGKZopxvOr24JsS7gruz6C3GExE01mublZ3mIBOaon9zuQ==", + "version": "5.10.1", + "resolved": "https://registry.npmjs.org/@typescript-eslint/visitor-keys/-/visitor-keys-5.10.1.tgz", + "integrity": "sha512-NjQ0Xinhy9IL979tpoTRuLKxMc0zJC7QVSdeerXs2/QvOy2yRkzX5dRb10X5woNUdJgU8G3nYRDlI33sq1K4YQ==", "dev": true, "requires": { - "@typescript-eslint/types": "5.10.0", + "@typescript-eslint/types": "5.10.1", "eslint-visitor-keys": "^3.0.0" } }, @@ -7914,9 +7871,9 @@ "dev": true }, "caniuse-lite": { - "version": "1.0.30001299", - "resolved": "https://registry.npmjs.org/caniuse-lite/-/caniuse-lite-1.0.30001299.tgz", - "integrity": "sha512-iujN4+x7QzqA2NCSrS5VUy+4gLmRd4xv6vbBBsmfVqTx8bLAD8097euLqQgKxSVLvxjSDcvF1T/i9ocgnUFexw==", + "version": "1.0.30001301", + "resolved": "https://registry.npmjs.org/caniuse-lite/-/caniuse-lite-1.0.30001301.tgz", + "integrity": "sha512-csfD/GpHMqgEL3V3uIgosvh+SVIQvCh43SNu9HRbP1lnxkKm1kjDG4f32PP571JplkLjfS+mg2p1gxR7MYrrIA==", "dev": true }, "chalk": { @@ -7936,9 +7893,9 @@ "dev": true }, "chokidar": { - "version": "3.5.2", - "resolved": "https://registry.npmjs.org/chokidar/-/chokidar-3.5.2.tgz", - "integrity": "sha512-ekGhOnNVPgT77r4K/U3GDhu+FQ2S8TnK/s2KbIGXi0SZWuwkZ2QNyfWdZW+TVfn84DpEP7rLeCt2UI6bJ8GwbQ==", + "version": "3.5.3", + "resolved": "https://registry.npmjs.org/chokidar/-/chokidar-3.5.3.tgz", + "integrity": "sha512-Dr3sfKRP6oTcjf2JmUmFJfeVMvXBdegxB0iVQ5eb2V10uFJUCAS8OByZdVAyVb8xXNz3GjjTgj9kLWsZTqE6kw==", "dev": true, "requires": { "anymatch": "~3.1.2", @@ -8210,16 +8167,6 @@ "integrity": "sha1-3zrhmayt+31ECqrgsp4icrJOxhk=", "dev": true }, - "deprecated-obj": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/deprecated-obj/-/deprecated-obj-2.0.0.tgz", - "integrity": "sha512-CkdywZC2rJ8RGh+y3MM1fw1EJ4oO/oNExGbRFv0AQoMS+faTd3nO7slYjkj/6t8OnIMUE+wxh6G97YHhK1ytrw==", - "dev": true, - "requires": { - "flat": "^5.0.2", - "lodash": "^4.17.20" - } - }, "deprecation": { "version": "2.3.1", "resolved": "https://registry.npmjs.org/deprecation/-/deprecation-2.3.1.tgz", @@ -8266,9 +8213,9 @@ "dev": true }, "electron-to-chromium": { - "version": "1.4.44", - "resolved": "https://registry.npmjs.org/electron-to-chromium/-/electron-to-chromium-1.4.44.tgz", - "integrity": "sha512-tHGWiUUmY7GABK8+DNcr474cnZDTzD8x1736SlDosVH8+/vRJeqfaIBAEHFtMjddz/0T4rKKYsxEc8BwQRdBpw==", + "version": "1.4.51", + "resolved": "https://registry.npmjs.org/electron-to-chromium/-/electron-to-chromium-1.4.51.tgz", + "integrity": "sha512-JNEmcYl3mk1tGQmy0EvL5eik/CKSBuzAyGP0QFdG6LIgxQe3II0BL1m2zKc2MZMf3uGqHWE1TFddJML0RpjSHQ==", "dev": true }, "email-addresses": { @@ -8538,9 +8485,9 @@ "dev": true }, "fast-glob": { - "version": "3.2.10", - "resolved": "https://registry.npmjs.org/fast-glob/-/fast-glob-3.2.10.tgz", - "integrity": "sha512-s9nFhFnvR63wls6/kM88kQqDhMu0AfdjqouE2l5GVQPbqLgyFjjU5ry/r2yKsJxpb9Py1EYNqieFrmMaX4v++A==", + "version": "3.2.11", + "resolved": "https://registry.npmjs.org/fast-glob/-/fast-glob-3.2.11.tgz", + "integrity": "sha512-xrO3+1bxSo3ZVHAnqzyuewYT6aMFHRAd4Kcs92MAonjwQZLsK9d0SF1IyQ3k5PoirxTW0Oe/RqFgMQ6TcNE5Ew==", "dev": true, "requires": { "@nodelib/fs.stat": "^2.0.2", @@ -9625,54 +9572,37 @@ "dev": true }, "mocha": { - "version": "9.1.4", - "resolved": "https://registry.npmjs.org/mocha/-/mocha-9.1.4.tgz", - "integrity": "sha512-+q2aV5VlJZuLgCWoBvGI5zEwPF9eEI0kr/sAA9Jm4xMND7RfIEyF8JE7C0JIg8WXRG+P1sdIAb5ccoHPlXLzcw==", + "version": "9.2.0", + "resolved": "https://registry.npmjs.org/mocha/-/mocha-9.2.0.tgz", + "integrity": "sha512-kNn7E8g2SzVcq0a77dkphPsDSN7P+iYkqE0ZsGCYWRsoiKjOt+NvXfaagik8vuDa6W5Zw3qxe8Jfpt5qKf+6/Q==", "dev": true, "requires": { "@ungap/promise-all-settled": "1.1.2", "ansi-colors": "4.1.1", "browser-stdout": "1.3.1", - "chokidar": "3.5.2", - "debug": "4.3.2", + "chokidar": "3.5.3", + "debug": "4.3.3", "diff": "5.0.0", "escape-string-regexp": "4.0.0", "find-up": "5.0.0", - "glob": "7.1.7", + "glob": "7.2.0", "growl": "1.10.5", "he": "1.2.0", "js-yaml": "4.1.0", "log-symbols": "4.1.0", "minimatch": "3.0.4", "ms": "2.1.3", - "nanoid": "3.1.25", + "nanoid": "3.2.0", "serialize-javascript": "6.0.0", "strip-json-comments": "3.1.1", "supports-color": "8.1.1", "which": "2.0.2", - "workerpool": "6.1.5", + "workerpool": "6.2.0", "yargs": "16.2.0", "yargs-parser": "20.2.4", "yargs-unparser": "2.0.0" }, "dependencies": { - "debug": { - "version": "4.3.2", - "resolved": "https://registry.npmjs.org/debug/-/debug-4.3.2.tgz", - "integrity": "sha512-mOp8wKcvj7XxC78zLgw/ZA+6TSgkoE2C/ienthhRD298T7UNwAg9diBpLRxC0mOezLl4B0xV7M0cCO6P/O0Xhw==", - "dev": true, - "requires": { - "ms": "2.1.2" - }, - "dependencies": { - "ms": { - "version": "2.1.2", - "resolved": "https://registry.npmjs.org/ms/-/ms-2.1.2.tgz", - "integrity": "sha512-sGkPx+VjMtmA6MX27oA4FBFELFCZZ4S4XqeGOXCv68tT+jb3vk/RyaKWP0PTKyWtmLSM0b+adUTEvbs1PEaH2w==", - "dev": true - } - } - }, "escape-string-regexp": { "version": "4.0.0", "resolved": "https://registry.npmjs.org/escape-string-regexp/-/escape-string-regexp-4.0.0.tgz", @@ -9689,20 +9619,6 @@ "path-exists": "^4.0.0" } }, - "glob": { - "version": "7.1.7", - "resolved": "https://registry.npmjs.org/glob/-/glob-7.1.7.tgz", - "integrity": "sha512-OvD9ENzPLbegENnYP5UUfJIirTg4+XwMWGaQfQTY0JenxNvvIKP3U3/tAQSPIu/lHxXYSZmpXlUHeqAIdKzBLQ==", - "dev": true, - "requires": { - "fs.realpath": "^1.0.0", - "inflight": "^1.0.4", - "inherits": "2", - "minimatch": "^3.0.4", - "once": "^1.3.0", - "path-is-absolute": "^1.0.0" - } - }, "locate-path": { "version": "6.0.0", "resolved": "https://registry.npmjs.org/locate-path/-/locate-path-6.0.0.tgz", @@ -9787,9 +9703,9 @@ "dev": true }, "nanoid": { - "version": "3.1.25", - "resolved": "https://registry.npmjs.org/nanoid/-/nanoid-3.1.25.tgz", - "integrity": "sha512-rdwtIXaXCLFAQbnfqDRnI6jaRHp9fTcYBjtFKE8eezcZ7LuLjhUaQGNeMXf1HmRoCH32CLz6XwX0TtxEOS/A3Q==", + "version": "3.2.0", + "resolved": "https://registry.npmjs.org/nanoid/-/nanoid-3.2.0.tgz", + "integrity": "sha512-fmsZYa9lpn69Ad5eDn7FMcnnSR+8R34W9qJEijxYhTbfOWzr22n1QxCMzXLK+ODyW2973V3Fux959iQoUxzUIA==", "dev": true }, "natural-compare": { @@ -9829,9 +9745,9 @@ } }, "node-fetch": { - "version": "2.6.6", - "resolved": "https://registry.npmjs.org/node-fetch/-/node-fetch-2.6.6.tgz", - "integrity": "sha512-Z8/6vRlTUChSdIgMa51jxQ4lrw/Jy5SOW10ObaA47/RElsAN2c5Pn8bTgFGWn/ibwzXTE8qwr1Yzx28vsecXEA==", + "version": "2.6.7", + "resolved": "https://registry.npmjs.org/node-fetch/-/node-fetch-2.6.7.tgz", + "integrity": "sha512-ZjMPFEfVx5j+y2yF35Kzx5sF7kDzxuDj6ziH4FFbOp87zKDZNx8yExJIb05OGF4Nlt9IHFIMBkRl41VdvcNdbQ==", "dev": true, "requires": { "whatwg-url": "^5.0.0" @@ -10574,9 +10490,9 @@ } }, "release-it": { - "version": "14.12.3", - "resolved": "https://registry.npmjs.org/release-it/-/release-it-14.12.3.tgz", - "integrity": "sha512-qek7ml9WaxpXSjLpU4UGCF9nWpDgOODL1gZTuydafs1HdQPAeYOd2od8I8lUL4NlEKW2TirDhH4aFTVIpP3/cQ==", + "version": "14.12.4", + "resolved": "https://registry.npmjs.org/release-it/-/release-it-14.12.4.tgz", + "integrity": "sha512-lqf9PMsj7ycCqFHGag8Uv7cE1hNsKa+yKUMe+Fkh9fdOfxu2F01On+YUefRCP0DuQthmr/WyLCYdrjThMEkWFQ==", "dev": true, "requires": { "@iarna/toml": "2.2.5", @@ -10585,7 +10501,6 @@ "chalk": "4.1.2", "cosmiconfig": "7.0.1", "debug": "4.3.3", - "deprecated-obj": "2.0.0", "execa": "5.1.1", "form-data": "4.0.0", "git-url-parse": "11.6.0", @@ -10663,12 +10578,12 @@ "dev": true }, "resolve": { - "version": "1.21.0", - "resolved": "https://registry.npmjs.org/resolve/-/resolve-1.21.0.tgz", - "integrity": "sha512-3wCbTpk5WJlyE4mSOtDLhqQmGFi0/TD9VPwmiolnk8U0wRgMEktqCXd3vy5buTO3tljvalNvKrjHEfrd2WpEKA==", + "version": "1.22.0", + "resolved": "https://registry.npmjs.org/resolve/-/resolve-1.22.0.tgz", + "integrity": "sha512-Hhtrw0nLeSrFQ7phPp4OOcVjLPIeMnRlr5mcnVuMe7M/7eBn98A3hmFRLoFo3DLZkivSYwhRUJTyPyWAk56WLw==", "dev": true, "requires": { - "is-core-module": "^2.8.0", + "is-core-module": "^2.8.1", "path-parse": "^1.0.7", "supports-preserve-symlinks-flag": "^1.0.0" } @@ -11393,9 +11308,9 @@ "dev": true }, "workerpool": { - "version": "6.1.5", - "resolved": "https://registry.npmjs.org/workerpool/-/workerpool-6.1.5.tgz", - "integrity": "sha512-XdKkCK0Zqc6w3iTxLckiuJ81tiD/o5rBE/m+nXpRCB+/Sq4DqkfXZ/x0jW02DG1tGsfUGXbTJyZDP+eu67haSw==", + "version": "6.2.0", + "resolved": "https://registry.npmjs.org/workerpool/-/workerpool-6.2.0.tgz", + "integrity": "sha512-Rsk5qQHJ9eowMH28Jwhe8HEbmdYDX4lwoMWshiCXugjtHqMD9ZbiqSDLxcsfdqsETPzVUtX5s1Z5kStiIM6l4A==", "dev": true }, "wrap-ansi": { diff --git a/packages/bloom/package.json b/packages/bloom/package.json index 2a295f1bf76..ef012f893d7 100644 --- a/packages/bloom/package.json +++ b/packages/bloom/package.json @@ -20,7 +20,7 @@ "@node-redis/test-utils": "*", "@types/node": "^17.0.10", "nyc": "^15.1.0", - "release-it": "^14.12.3", + "release-it": "^14.12.4", "source-map-support": "^0.5.21", "ts-node": "^10.4.0", "typedoc": "^0.22.11", diff --git a/packages/client/package.json b/packages/client/package.json index 5ad9fc71563..036bcc858c7 100644 --- a/packages/client/package.json +++ b/packages/client/package.json @@ -26,11 +26,11 @@ "@types/redis-parser": "^3.0.0", "@types/sinon": "^10.0.8", "@types/yallist": "^4.0.1", - "@typescript-eslint/eslint-plugin": "^5.10.0", - "@typescript-eslint/parser": "^5.10.0", + "@typescript-eslint/eslint-plugin": "^5.10.1", + "@typescript-eslint/parser": "^5.10.1", "eslint": "^8.7.0", "nyc": "^15.1.0", - "release-it": "^14.12.3", + "release-it": "^14.12.4", "sinon": "^12.0.1", "source-map-support": "^0.5.21", "ts-node": "^10.4.0", diff --git a/packages/json/package.json b/packages/json/package.json index 40e077e92f9..db9c2373034 100644 --- a/packages/json/package.json +++ b/packages/json/package.json @@ -20,7 +20,7 @@ "@node-redis/test-utils": "*", "@types/node": "^17.0.10", "nyc": "^15.1.0", - "release-it": "^14.12.3", + "release-it": "^14.12.4", "source-map-support": "^0.5.21", "ts-node": "^10.4.0", "typedoc": "^0.22.11", diff --git a/packages/search/package.json b/packages/search/package.json index 4c7ba0b54e0..9ab292f9b83 100644 --- a/packages/search/package.json +++ b/packages/search/package.json @@ -20,7 +20,7 @@ "@node-redis/test-utils": "*", "@types/node": "^17.0.10", "nyc": "^15.1.0", - "release-it": "^14.12.3", + "release-it": "^14.12.4", "source-map-support": "^0.5.21", "ts-node": "^10.4.0", "typedoc": "^0.22.11", diff --git a/packages/test-utils/package.json b/packages/test-utils/package.json index 6dadabc5e87..d827cef26e7 100644 --- a/packages/test-utils/package.json +++ b/packages/test-utils/package.json @@ -14,7 +14,7 @@ "@types/mocha": "^9.1.0", "@types/node": "^17.0.10", "@types/yargs": "^17.0.8", - "mocha": "^9.1.4", + "mocha": "^9.2.0", "nyc": "^15.1.0", "source-map-support": "^0.5.21", "ts-node": "^10.4.0", diff --git a/packages/time-series/package.json b/packages/time-series/package.json index fa527a9b512..8242de1c132 100644 --- a/packages/time-series/package.json +++ b/packages/time-series/package.json @@ -20,7 +20,7 @@ "@node-redis/test-utils": "*", "@types/node": "^17.0.10", "nyc": "^15.1.0", - "release-it": "^14.12.3", + "release-it": "^14.12.4", "source-map-support": "^0.5.21", "ts-node": "^10.4.0", "typedoc": "^0.22.11", From 741aff0c30028c852771fb43c520093b7a23a164 Mon Sep 17 00:00:00 2001 From: Matan Yemini <50515643+MatanYemini@users.noreply.github.com> Date: Mon, 31 Jan 2022 11:33:11 +0200 Subject: [PATCH 1125/1748] update tls type to be boolean instead of "true" (RedisTlsSocketOptions) (#1851) * update tls type to be boolean instead of "true" * Update socket.ts * Update socket.ts * Update socket.ts Co-authored-by: Matan Yemini Co-authored-by: Leibale Eidelman --- packages/client/lib/client/socket.ts | 6 ++++-- 1 file changed, 4 insertions(+), 2 deletions(-) diff --git a/packages/client/lib/client/socket.ts b/packages/client/lib/client/socket.ts index d7b91e14c05..269c52381f3 100644 --- a/packages/client/lib/client/socket.ts +++ b/packages/client/lib/client/socket.ts @@ -13,9 +13,11 @@ export interface RedisSocketCommonOptions { reconnectStrategy?(retries: number): number | Error; } -export type RedisNetSocketOptions = Partial; +type RedisNetSocketOptions = Partial & { + tls?: false; +}; -export interface RedisTlsSocketOptions extends RedisSocketCommonOptions, tls.ConnectionOptions { +export interface RedisTlsSocketOptions extends tls.ConnectionOptions { tls: true; } From 21270ba6e20728706ad3794cd37b29a338d785e9 Mon Sep 17 00:00:00 2001 From: Leibale Eidelman Date: Mon, 31 Jan 2022 05:41:31 -0500 Subject: [PATCH 1126/1748] fix #1875 - fix XPENDING_RANGE id type (#1879) --- packages/client/lib/commands/XPENDING_RANGE.ts | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/packages/client/lib/commands/XPENDING_RANGE.ts b/packages/client/lib/commands/XPENDING_RANGE.ts index fd9a09113bf..87660de545d 100644 --- a/packages/client/lib/commands/XPENDING_RANGE.ts +++ b/packages/client/lib/commands/XPENDING_RANGE.ts @@ -33,14 +33,14 @@ export function transformArguments( } type XPendingRangeRawReply = Array<[ - id: number, + id: RedisCommandArgument, consumer: RedisCommandArgument, millisecondsSinceLastDelivery: number, deliveriesCounter: number ]>; type XPendingRangeReply = Array<{ - id: number; + id: RedisCommandArgument; owner: RedisCommandArgument; millisecondsSinceLastDelivery: number; deliveriesCounter: number; From 51475211a31ba5e2378d54c84f804b32b3f05e53 Mon Sep 17 00:00:00 2001 From: Leibale Eidelman Date: Mon, 31 Jan 2022 05:41:38 -0500 Subject: [PATCH 1127/1748] fix #1874 - fix TIME return type (#1880) --- packages/client/lib/client/index.ts | 8 +++++--- 1 file changed, 5 insertions(+), 3 deletions(-) diff --git a/packages/client/lib/client/index.ts b/packages/client/lib/client/index.ts index 80f029a406e..51fbde91c18 100644 --- a/packages/client/lib/client/index.ts +++ b/packages/client/lib/client/index.ts @@ -35,9 +35,11 @@ type ConvertArgumentType = Type extends Set ? Set> : ( Type extends Map ? Map> : ( Type extends Array ? Array> : ( - Type extends Record ? { - [Property in keyof Type]: ConvertArgumentType - } : Type + Type extends Date ? Type : ( + Type extends Record ? { + [Property in keyof Type]: ConvertArgumentType + } : Type + ) ) ) ) From ac1a61ff7955c39ec98b57a19da22c8ae5c53ce7 Mon Sep 17 00:00:00 2001 From: Leibale Eidelman Date: Mon, 31 Jan 2022 08:34:59 -0500 Subject: [PATCH 1128/1748] fix #1876 - remove engines (#1884) --- package.json | 6 +----- 1 file changed, 1 insertion(+), 5 deletions(-) diff --git a/package.json b/package.json index baa4d928a8f..e65d8f9811f 100644 --- a/package.json +++ b/package.json @@ -42,9 +42,5 @@ "bugs": { "url": "https://github.com/redis/node-redis/issues" }, - "homepage": "https://github.com/redis/node-redis", - "engines": { - "npm": ">=7", - "typescript": ">=4" - } + "homepage": "https://github.com/redis/node-redis" } From 8160fa7d6555bd6e012e7c33ae23ea09d2b676d2 Mon Sep 17 00:00:00 2001 From: Leibale Eidelman Date: Mon, 31 Jan 2022 08:35:24 -0500 Subject: [PATCH 1129/1748] fix #1865 - add defaults to RedisModules and RedisScripts (#1885) --- packages/client/lib/client/index.ts | 15 ++++++++++----- packages/client/lib/cluster/index.ts | 13 +++++++++---- 2 files changed, 19 insertions(+), 9 deletions(-) diff --git a/packages/client/lib/client/index.ts b/packages/client/lib/client/index.ts index 51fbde91c18..2ce93773e97 100644 --- a/packages/client/lib/client/index.ts +++ b/packages/client/lib/client/index.ts @@ -15,7 +15,10 @@ import { ClientClosedError, DisconnectsClientError, AuthError } from '../errors' import { URL } from 'url'; import { TcpSocketConnectOpts } from 'net'; -export interface RedisClientOptions extends RedisPlugins { +export interface RedisClientOptions< + M extends RedisModules = Record, + S extends RedisScripts = Record +> extends RedisPlugins { url?: string; socket?: RedisSocketOptions; username?: string; @@ -73,8 +76,10 @@ export type WithScripts = { [P in keyof S as ExcludeMappedString

]: RedisClientCommandSignature; }; -export type RedisClientType = - RedisClient & WithCommands & WithModules & WithScripts; +export type RedisClientType< + M extends RedisModules = Record, + S extends RedisScripts = Record +> = RedisClient & WithCommands & WithModules & WithScripts; export type InstantiableRedisClient = new (options?: RedisClientOptions) => RedisClientType; @@ -112,10 +117,10 @@ export default class RedisClient return new (RedisClient.extend(options))(options); } - static parseURL(url: string): RedisClientOptions, Record> { + static parseURL(url: string): RedisClientOptions { // https://www.iana.org/assignments/uri-schemes/prov/redis const { hostname, port, protocol, username, password, pathname } = new URL(url), - parsed: RedisClientOptions, Record> = { + parsed: RedisClientOptions = { socket: { host: hostname } diff --git a/packages/client/lib/cluster/index.ts b/packages/client/lib/cluster/index.ts index 10288158e4d..4f2f3f98d3e 100644 --- a/packages/client/lib/cluster/index.ts +++ b/packages/client/lib/cluster/index.ts @@ -7,9 +7,12 @@ import { EventEmitter } from 'events'; import RedisClusterMultiCommand, { RedisClusterMultiCommandType } from './multi-command'; import { RedisMultiQueuedCommand } from '../multi-command'; -export type RedisClusterClientOptions = Omit, Record>, 'modules' | 'scripts'>; +export type RedisClusterClientOptions = Omit; -export interface RedisClusterOptions extends RedisPlugins { +export interface RedisClusterOptions< + M extends RedisModules = Record, + S extends RedisScripts = Record +> extends RedisPlugins { rootNodes: Array; defaults?: Partial; useReplicas?: boolean; @@ -20,8 +23,10 @@ type WithCommands = { [P in keyof typeof COMMANDS]: RedisClientCommandSignature<(typeof COMMANDS)[P]>; }; -export type RedisClusterType = - RedisCluster & WithCommands & WithModules & WithScripts; +export type RedisClusterType< + M extends RedisModules = Record, + S extends RedisScripts = Record +> = RedisCluster & WithCommands & WithModules & WithScripts; export default class RedisCluster extends EventEmitter { static extractFirstKey(command: RedisCommand, originalArgs: Array, redisArgs: RedisCommandArguments): RedisCommandArgument | undefined { From 46b831c92282d16d5b224effa20cf8dc6002fd8c Mon Sep 17 00:00:00 2001 From: Leibale Eidelman Date: Mon, 31 Jan 2022 08:35:35 -0500 Subject: [PATCH 1130/1748] fix #1864 - cluster.quit (#1886) --- packages/client/lib/cluster/cluster-slots.ts | 19 +++++++++++++++---- packages/client/lib/cluster/index.ts | 4 ++++ 2 files changed, 19 insertions(+), 4 deletions(-) diff --git a/packages/client/lib/cluster/cluster-slots.ts b/packages/client/lib/cluster/cluster-slots.ts index bcf95390ce3..044cedf56a8 100644 --- a/packages/client/lib/cluster/cluster-slots.ts +++ b/packages/client/lib/cluster/cluster-slots.ts @@ -232,10 +232,21 @@ export default class RedisClusterSlots { - await Promise.all( - [...this.#nodeByUrl.values()].map(({ client }) => client.disconnect()) - ); + quit(): Promise { + return this.#destroy(client => client.quit()); + } + + disconnect(): Promise { + return this.#destroy(client => client.disconnect()); + } + + async #destroy(fn: (client: RedisClientType) => Promise): Promise { + const promises = []; + for (const { client } of this.#nodeByUrl.values()) { + promises.push(fn(client)); + } + + await Promise.all(promises); this.#nodeByUrl.clear(); this.#slots.splice(0); diff --git a/packages/client/lib/cluster/index.ts b/packages/client/lib/cluster/index.ts index 4f2f3f98d3e..123400dd7e5 100644 --- a/packages/client/lib/cluster/index.ts +++ b/packages/client/lib/cluster/index.ts @@ -191,6 +191,10 @@ export default class RedisCluster { + return this.#slots.quit(); + } + disconnect(): Promise { return this.#slots.disconnect(); } From 3547b2029397c3cad4fd41d8eb5bafc43fe05ad4 Mon Sep 17 00:00:00 2001 From: Leibale Eidelman Date: Mon, 31 Jan 2022 12:52:19 -0500 Subject: [PATCH 1131/1748] Graph (#1887) * init * implement graph commands * add graph to packages table * fix ts.infoDebug * fix redisearch tests * Update INFO_DEBUG.ts * fix INFO.spec.ts * test QUERY and SLOWLOG Co-authored-by: Avital-Fine --- README.md | 3 +- index.ts | 3 + package-lock.json | 73 ++++++++++++++----- packages/bloom/package.json | 2 +- packages/bloom/tsconfig.json | 4 + packages/client/package.json | 4 +- packages/graph/.nycrc.json | 4 + packages/graph/.release-it.json | 10 +++ packages/graph/README.md | 1 + .../graph/lib/commands/CONFIG_GET.spec.ts | 22 ++++++ packages/graph/lib/commands/CONFIG_GET.ts | 12 +++ .../graph/lib/commands/CONFIG_SET.spec.ts | 19 +++++ packages/graph/lib/commands/CONFIG_SET.ts | 10 +++ packages/graph/lib/commands/DELETE.spec.ts | 21 ++++++ packages/graph/lib/commands/DELETE.ts | 7 ++ packages/graph/lib/commands/EXPLAIN.spec.ts | 18 +++++ packages/graph/lib/commands/EXPLAIN.ts | 9 +++ packages/graph/lib/commands/LIST.spec.ts | 19 +++++ packages/graph/lib/commands/LIST.ts | 7 ++ packages/graph/lib/commands/PROFILE.spec.ts | 18 +++++ packages/graph/lib/commands/PROFILE.ts | 9 +++ packages/graph/lib/commands/QUERY.spec.ts | 22 ++++++ packages/graph/lib/commands/QUERY.ts | 43 +++++++++++ packages/graph/lib/commands/QUERY_RO.spec.ts | 22 ++++++ packages/graph/lib/commands/QUERY_RO.ts | 21 ++++++ packages/graph/lib/commands/SLOWLOG.spec.ts | 18 +++++ packages/graph/lib/commands/SLOWLOG.ts | 30 ++++++++ packages/graph/lib/commands/index.ts | 49 +++++++++++++ packages/graph/lib/index.ts | 1 + packages/graph/lib/test-utils.ts | 21 ++++++ packages/graph/package.json | 29 ++++++++ packages/graph/tsconfig.json | 20 +++++ packages/json/lib/test-utils.ts | 2 +- packages/json/package.json | 2 +- packages/search/lib/commands/CREATE.spec.ts | 2 +- .../search/lib/commands/DROPINDEX.spec.ts | 2 +- packages/search/lib/commands/INFO.spec.ts | 16 +++- packages/search/lib/commands/SYNDUMP.spec.ts | 5 +- .../search/lib/commands/SYNUPDATE.spec.ts | 5 +- packages/search/lib/test-utils.ts | 2 +- packages/search/package.json | 2 +- packages/test-utils/package.json | 2 +- .../lib/commands/INFO_DEBUG.spec.ts | 1 + .../time-series/lib/commands/INFO_DEBUG.ts | 6 +- packages/time-series/lib/test-utils.ts | 2 +- packages/time-series/package.json | 2 +- 46 files changed, 563 insertions(+), 39 deletions(-) create mode 100644 packages/graph/.nycrc.json create mode 100644 packages/graph/.release-it.json create mode 100644 packages/graph/README.md create mode 100644 packages/graph/lib/commands/CONFIG_GET.spec.ts create mode 100644 packages/graph/lib/commands/CONFIG_GET.ts create mode 100644 packages/graph/lib/commands/CONFIG_SET.spec.ts create mode 100644 packages/graph/lib/commands/CONFIG_SET.ts create mode 100644 packages/graph/lib/commands/DELETE.spec.ts create mode 100644 packages/graph/lib/commands/DELETE.ts create mode 100644 packages/graph/lib/commands/EXPLAIN.spec.ts create mode 100644 packages/graph/lib/commands/EXPLAIN.ts create mode 100644 packages/graph/lib/commands/LIST.spec.ts create mode 100644 packages/graph/lib/commands/LIST.ts create mode 100644 packages/graph/lib/commands/PROFILE.spec.ts create mode 100644 packages/graph/lib/commands/PROFILE.ts create mode 100644 packages/graph/lib/commands/QUERY.spec.ts create mode 100644 packages/graph/lib/commands/QUERY.ts create mode 100644 packages/graph/lib/commands/QUERY_RO.spec.ts create mode 100644 packages/graph/lib/commands/QUERY_RO.ts create mode 100644 packages/graph/lib/commands/SLOWLOG.spec.ts create mode 100644 packages/graph/lib/commands/SLOWLOG.ts create mode 100644 packages/graph/lib/commands/index.ts create mode 100644 packages/graph/lib/index.ts create mode 100644 packages/graph/lib/test-utils.ts create mode 100644 packages/graph/package.json create mode 100644 packages/graph/tsconfig.json diff --git a/README.md b/README.md index 0399595d8c4..974407fce48 100644 --- a/README.md +++ b/README.md @@ -11,11 +11,12 @@ node-redis is a modern, high performance [Redis](https://redis.io) client for No ## Packages -| Name | Description | +| Name | Description | |---------------------------------------------------|----------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------| | [redis](./) | [![Downloads](https://img.shields.io/npm/dm/redis.svg)](https://www.npmjs.com/package/redis) [![Version](https://img.shields.io/npm/v/redis.svg)](https://www.npmjs.com/package/redis) | | [@node-redis/client](./packages/client) | [![Downloads](https://img.shields.io/npm/dm/@node-redis/client.svg)](https://www.npmjs.com/package/@node-redis/client) [![Version](https://img.shields.io/npm/v/@node-redis/client.svg)](https://www.npmjs.com/package/@node-redis/client) [![Docs](https://img.shields.io/badge/-documentation-dc382c)](https://redis.js.org/documentation/client/) | | [@node-redis/bloom](./packages/bloom) | [![Downloads](https://img.shields.io/npm/dm/@node-redis/bloom.svg)](https://www.npmjs.com/package/@node-redis/bloom) [![Version](https://img.shields.io/npm/v/@node-redis/bloom.svg)](https://www.npmjs.com/package/@node-redis/bloom) [![Docs](https://img.shields.io/badge/-documentation-dc382c)](https://redis.js.org/documentation/bloom/) [Redis Bloom](https://oss.redis.com/redisbloom/) commands | +| [@node-redis/graph](./packages/graph) | [![Downloads](https://img.shields.io/npm/dm/@node-redis/graph.svg)](https://www.npmjs.com/package/@node-redis/graph) [![Version](https://img.shields.io/npm/v/@node-redis/graph.svg)](https://www.npmjs.com/package/@node-redis/graph) [![Docs](https://img.shields.io/badge/-documentation-dc382c)](https://redis.js.org/documentation/graph/) [Redis Graph](https://oss.redis.com/redisgraph/) commands | | [@node-redis/json](./packages/json) | [![Downloads](https://img.shields.io/npm/dm/@node-redis/json.svg)](https://www.npmjs.com/package/@node-redis/json) [![Version](https://img.shields.io/npm/v/@node-redis/json.svg)](https://www.npmjs.com/package/@node-redis/json) [![Docs](https://img.shields.io/badge/-documentation-dc382c)](https://redis.js.org/documentation/json/) [Redis JSON](https://oss.redis.com/redisjson/) commands | | [@node-redis/search](./packages/search) | [![Downloads](https://img.shields.io/npm/dm/@node-redis/search.svg)](https://www.npmjs.com/package/@node-redis/search) [![Version](https://img.shields.io/npm/v/@node-redis/search.svg)](https://www.npmjs.com/package/@node-redis/search) [![Docs](https://img.shields.io/badge/-documentation-dc382c)](https://redis.js.org/documentation/search/) [Redis Search](https://oss.redis.com/redisearch/) commands | | [@node-redis/time-series](./packages/time-series) | [![Downloads](https://img.shields.io/npm/dm/@node-redis/time-series.svg)](https://www.npmjs.com/package/@node-redis/time-series) [![Version](https://img.shields.io/npm/v/@node-redis/time-series.svg)](https://www.npmjs.com/package/@node-redis/time-series) [![Docs](https://img.shields.io/badge/-documentation-dc382c)](https://redis.js.org/documentation/time-series/) [Redis Time-Series](https://oss.redis.com/redistimeseries/) commands | diff --git a/index.ts b/index.ts index 7f1eb386a40..63f21c582ef 100644 --- a/index.ts +++ b/index.ts @@ -1,18 +1,21 @@ import { createClient as _createClient, createCluster as _createCluster, RedisClientOptions, RedisClientType, RedisClusterOptions, RedisClusterType } from '@node-redis/client'; import { RedisScripts } from '@node-redis/client/dist/lib/commands'; import RedisBloomModules from '@node-redis/bloom'; +import RedisGraph from '@node-redis/graph'; import RedisJSON from '@node-redis/json'; import RediSearch from '@node-redis/search'; import RedisTimeSeries from '@node-redis/time-series'; export * from '@node-redis/client'; export * from '@node-redis/bloom'; +export * from '@node-redis/graph'; export * from '@node-redis/json'; export * from '@node-redis/search'; export * from '@node-redis/time-series'; const modules = { ...RedisBloomModules, + graph: RedisGraph, json: RedisJSON, ft: RediSearch, ts: RedisTimeSeries diff --git a/package-lock.json b/package-lock.json index 117e1691759..8638fcd1075 100644 --- a/package-lock.json +++ b/package-lock.json @@ -570,6 +570,10 @@ "resolved": "packages/client", "link": true }, + "node_modules/@node-redis/graph": { + "resolved": "packages/graph", + "link": true + }, "node_modules/@node-redis/json": { "resolved": "packages/json", "link": true @@ -876,9 +880,9 @@ "dev": true }, "node_modules/@types/node": { - "version": "17.0.10", - "resolved": "https://registry.npmjs.org/@types/node/-/node-17.0.10.tgz", - "integrity": "sha512-S/3xB4KzyFxYGCppyDt68yzBU9ysL88lSdIah4D6cptdcltc4NCPCAMc0+PCpg/lLIyC7IPvj2Z52OJWeIUkog==", + "version": "17.0.13", + "resolved": "https://registry.npmjs.org/@types/node/-/node-17.0.13.tgz", + "integrity": "sha512-Y86MAxASe25hNzlDbsviXl8jQHb0RDvKt4c40ZJQ1Don0AAL0STLZSs4N+6gLEO55pedy7r2cLwS+ZDxPm/2Bw==", "dev": true }, "node_modules/@types/parse-json": { @@ -6434,7 +6438,7 @@ "devDependencies": { "@istanbuljs/nyc-config-typescript": "^1.0.2", "@node-redis/test-utils": "*", - "@types/node": "^17.0.10", + "@types/node": "^17.0.12", "nyc": "^15.1.0", "release-it": "^14.12.4", "source-map-support": "^0.5.21", @@ -6459,7 +6463,7 @@ "devDependencies": { "@istanbuljs/nyc-config-typescript": "^1.0.2", "@node-redis/test-utils": "*", - "@types/node": "^17.0.10", + "@types/node": "^17.0.12", "@types/redis-parser": "^3.0.0", "@types/sinon": "^10.0.8", "@types/yallist": "^4.0.1", @@ -6478,6 +6482,25 @@ "node": ">=12" } }, + "packages/graph": { + "name": "@node-redis/graph", + "version": "1.0.0-rc", + "license": "MIT", + "devDependencies": { + "@istanbuljs/nyc-config-typescript": "^1.0.2", + "@node-redis/test-utils": "*", + "@types/node": "^17.0.12", + "nyc": "^15.1.0", + "release-it": "^14.12.4", + "source-map-support": "^0.5.21", + "ts-node": "^10.4.0", + "typedoc": "^0.22.11", + "typescript": "^4.5.5" + }, + "peerDependencies": { + "@node-redis/client": "^1.0.0" + } + }, "packages/json": { "name": "@node-redis/json", "version": "1.0.2", @@ -6485,7 +6508,7 @@ "devDependencies": { "@istanbuljs/nyc-config-typescript": "^1.0.2", "@node-redis/test-utils": "*", - "@types/node": "^17.0.10", + "@types/node": "^17.0.12", "nyc": "^15.1.0", "release-it": "^14.12.4", "source-map-support": "^0.5.21", @@ -6504,7 +6527,7 @@ "devDependencies": { "@istanbuljs/nyc-config-typescript": "^1.0.2", "@node-redis/test-utils": "*", - "@types/node": "^17.0.10", + "@types/node": "^17.0.12", "nyc": "^15.1.0", "release-it": "^14.12.4", "source-map-support": "^0.5.21", @@ -6521,7 +6544,7 @@ "devDependencies": { "@istanbuljs/nyc-config-typescript": "^1.0.2", "@types/mocha": "^9.1.0", - "@types/node": "^17.0.10", + "@types/node": "^17.0.12", "@types/yargs": "^17.0.8", "mocha": "^9.2.0", "nyc": "^15.1.0", @@ -6541,7 +6564,7 @@ "devDependencies": { "@istanbuljs/nyc-config-typescript": "^1.0.2", "@node-redis/test-utils": "*", - "@types/node": "^17.0.10", + "@types/node": "^17.0.12", "nyc": "^15.1.0", "release-it": "^14.12.4", "source-map-support": "^0.5.21", @@ -6971,7 +6994,7 @@ "requires": { "@istanbuljs/nyc-config-typescript": "^1.0.2", "@node-redis/test-utils": "*", - "@types/node": "^17.0.10", + "@types/node": "^17.0.12", "nyc": "^15.1.0", "release-it": "^14.12.4", "source-map-support": "^0.5.21", @@ -6985,7 +7008,7 @@ "requires": { "@istanbuljs/nyc-config-typescript": "^1.0.2", "@node-redis/test-utils": "*", - "@types/node": "^17.0.10", + "@types/node": "^17.0.12", "@types/redis-parser": "^3.0.0", "@types/sinon": "^10.0.8", "@types/yallist": "^4.0.1", @@ -7005,12 +7028,26 @@ "yallist": "4.0.0" } }, + "@node-redis/graph": { + "version": "file:packages/graph", + "requires": { + "@istanbuljs/nyc-config-typescript": "^1.0.2", + "@node-redis/test-utils": "*", + "@types/node": "^17.0.12", + "nyc": "^15.1.0", + "release-it": "^14.12.4", + "source-map-support": "^0.5.21", + "ts-node": "^10.4.0", + "typedoc": "^0.22.11", + "typescript": "^4.5.5" + } + }, "@node-redis/json": { "version": "file:packages/json", "requires": { "@istanbuljs/nyc-config-typescript": "^1.0.2", "@node-redis/test-utils": "*", - "@types/node": "^17.0.10", + "@types/node": "^17.0.12", "nyc": "^15.1.0", "release-it": "^14.12.4", "source-map-support": "^0.5.21", @@ -7024,7 +7061,7 @@ "requires": { "@istanbuljs/nyc-config-typescript": "^1.0.2", "@node-redis/test-utils": "*", - "@types/node": "^17.0.10", + "@types/node": "^17.0.12", "nyc": "^15.1.0", "release-it": "^14.12.4", "source-map-support": "^0.5.21", @@ -7038,7 +7075,7 @@ "requires": { "@istanbuljs/nyc-config-typescript": "^1.0.2", "@types/mocha": "^9.1.0", - "@types/node": "^17.0.10", + "@types/node": "^17.0.12", "@types/yargs": "^17.0.8", "mocha": "^9.2.0", "nyc": "^15.1.0", @@ -7053,7 +7090,7 @@ "requires": { "@istanbuljs/nyc-config-typescript": "^1.0.2", "@node-redis/test-utils": "*", - "@types/node": "^17.0.10", + "@types/node": "^17.0.12", "nyc": "^15.1.0", "release-it": "^14.12.4", "source-map-support": "^0.5.21", @@ -7326,9 +7363,9 @@ "dev": true }, "@types/node": { - "version": "17.0.10", - "resolved": "https://registry.npmjs.org/@types/node/-/node-17.0.10.tgz", - "integrity": "sha512-S/3xB4KzyFxYGCppyDt68yzBU9ysL88lSdIah4D6cptdcltc4NCPCAMc0+PCpg/lLIyC7IPvj2Z52OJWeIUkog==", + "version": "17.0.13", + "resolved": "https://registry.npmjs.org/@types/node/-/node-17.0.13.tgz", + "integrity": "sha512-Y86MAxASe25hNzlDbsviXl8jQHb0RDvKt4c40ZJQ1Don0AAL0STLZSs4N+6gLEO55pedy7r2cLwS+ZDxPm/2Bw==", "dev": true }, "@types/parse-json": { diff --git a/packages/bloom/package.json b/packages/bloom/package.json index ef012f893d7..e16b512ec5f 100644 --- a/packages/bloom/package.json +++ b/packages/bloom/package.json @@ -18,7 +18,7 @@ "devDependencies": { "@istanbuljs/nyc-config-typescript": "^1.0.2", "@node-redis/test-utils": "*", - "@types/node": "^17.0.10", + "@types/node": "^17.0.13", "nyc": "^15.1.0", "release-it": "^14.12.4", "source-map-support": "^0.5.21", diff --git a/packages/bloom/tsconfig.json b/packages/bloom/tsconfig.json index 19ef1005e3e..da6b76e89be 100644 --- a/packages/bloom/tsconfig.json +++ b/packages/bloom/tsconfig.json @@ -6,6 +6,10 @@ "include": [ "./lib/**/*.ts" ], + "exclude": [ + "./lib/test-utils.ts", + "./lib/**/*.spec.ts" + ], "typedocOptions": { "entryPoints": [ "./lib" diff --git a/packages/client/package.json b/packages/client/package.json index 036bcc858c7..d450e412565 100644 --- a/packages/client/package.json +++ b/packages/client/package.json @@ -22,9 +22,9 @@ "devDependencies": { "@istanbuljs/nyc-config-typescript": "^1.0.2", "@node-redis/test-utils": "*", - "@types/node": "^17.0.10", + "@types/node": "^17.0.13", "@types/redis-parser": "^3.0.0", - "@types/sinon": "^10.0.8", + "@types/sinon": "^10.0.9", "@types/yallist": "^4.0.1", "@typescript-eslint/eslint-plugin": "^5.10.1", "@typescript-eslint/parser": "^5.10.1", diff --git a/packages/graph/.nycrc.json b/packages/graph/.nycrc.json new file mode 100644 index 00000000000..b4e671e178f --- /dev/null +++ b/packages/graph/.nycrc.json @@ -0,0 +1,4 @@ +{ + "extends": "@istanbuljs/nyc-config-typescript", + "exclude": ["**/*.spec.ts", "lib/test-utils.ts"] +} diff --git a/packages/graph/.release-it.json b/packages/graph/.release-it.json new file mode 100644 index 00000000000..530d8f355d4 --- /dev/null +++ b/packages/graph/.release-it.json @@ -0,0 +1,10 @@ +{ + "git": { + "tagName": "graph@${version}", + "commitMessage": "Release ${tagName}", + "tagAnnotation": "Release ${tagName}" + }, + "npm": { + "publishArgs": ["--access", "public"] + } +} diff --git a/packages/graph/README.md b/packages/graph/README.md new file mode 100644 index 00000000000..2313f474d5a --- /dev/null +++ b/packages/graph/README.md @@ -0,0 +1 @@ +# @node-redis/graph diff --git a/packages/graph/lib/commands/CONFIG_GET.spec.ts b/packages/graph/lib/commands/CONFIG_GET.spec.ts new file mode 100644 index 00000000000..6e1fa74e219 --- /dev/null +++ b/packages/graph/lib/commands/CONFIG_GET.spec.ts @@ -0,0 +1,22 @@ +import { strict as assert } from 'assert'; +import testUtils, { GLOBAL } from '../test-utils'; +import { transformArguments } from './CONFIG_GET'; + +describe('CONFIG GET', () => { + it('transformArguments', () => { + assert.deepEqual( + transformArguments('TIMEOUT'), + ['GRAPH.CONFIG', 'GET', 'TIMEOUT'] + ); + }); + + testUtils.testWithClient('client.graph.configGet', async client => { + assert.deepEqual( + await client.graph.configGet('TIMEOUT'), + [ + 'TIMEOUT', + 0 + ] + ); + }, GLOBAL.SERVERS.OPEN); +}); diff --git a/packages/graph/lib/commands/CONFIG_GET.ts b/packages/graph/lib/commands/CONFIG_GET.ts new file mode 100644 index 00000000000..ce80a1148ed --- /dev/null +++ b/packages/graph/lib/commands/CONFIG_GET.ts @@ -0,0 +1,12 @@ +export const IS_READ_ONLY = true; + +export function transformArguments(configKey: string): Array { + return ['GRAPH.CONFIG', 'GET', configKey]; +} + +type ConfigItem = [ + configKey: string, + value: number +]; + +export declare function transformReply(): ConfigItem | Array; diff --git a/packages/graph/lib/commands/CONFIG_SET.spec.ts b/packages/graph/lib/commands/CONFIG_SET.spec.ts new file mode 100644 index 00000000000..51dce0a8cd9 --- /dev/null +++ b/packages/graph/lib/commands/CONFIG_SET.spec.ts @@ -0,0 +1,19 @@ +import { strict as assert } from 'assert'; +import testUtils, { GLOBAL } from '../test-utils'; +import { transformArguments } from './CONFIG_SET'; + +describe('CONFIG SET', () => { + it('transformArguments', () => { + assert.deepEqual( + transformArguments('TIMEOUT', 0), + ['GRAPH.CONFIG', 'SET', 'TIMEOUT', '0'] + ); + }); + + testUtils.testWithClient('client.graph.configSet', async client => { + assert.equal( + await client.graph.configSet('TIMEOUT', 0), + 'OK' + ); + }, GLOBAL.SERVERS.OPEN); +}); diff --git a/packages/graph/lib/commands/CONFIG_SET.ts b/packages/graph/lib/commands/CONFIG_SET.ts new file mode 100644 index 00000000000..ac81449ad15 --- /dev/null +++ b/packages/graph/lib/commands/CONFIG_SET.ts @@ -0,0 +1,10 @@ +export function transformArguments(configKey: string, value: number): Array { + return [ + 'GRAPH.CONFIG', + 'SET', + configKey, + value.toString() + ]; +} + +export declare function transformReply(): 'OK'; diff --git a/packages/graph/lib/commands/DELETE.spec.ts b/packages/graph/lib/commands/DELETE.spec.ts new file mode 100644 index 00000000000..e51ac2bfab8 --- /dev/null +++ b/packages/graph/lib/commands/DELETE.spec.ts @@ -0,0 +1,21 @@ +import { strict as assert } from 'assert'; +import testUtils, { GLOBAL } from '../test-utils'; +import { transformArguments } from './DELETE'; + +describe('', () => { + it('transformArguments', () => { + assert.deepEqual( + transformArguments('key'), + ['GRAPH.DELETE', 'key'] + ); + }); + + testUtils.testWithClient('client.graph.delete', async client => { + await client.graph.query('key', 'RETURN 1'); + + assert.equal( + typeof await client.graph.delete('key'), + 'string' + ); + }, GLOBAL.SERVERS.OPEN); +}); diff --git a/packages/graph/lib/commands/DELETE.ts b/packages/graph/lib/commands/DELETE.ts new file mode 100644 index 00000000000..240708143c6 --- /dev/null +++ b/packages/graph/lib/commands/DELETE.ts @@ -0,0 +1,7 @@ +export const FIRST_KEY_INDEX = 1; + +export function transformArguments(key: string): Array { + return ['GRAPH.DELETE', key]; +} + +export declare function transformReply(): string; diff --git a/packages/graph/lib/commands/EXPLAIN.spec.ts b/packages/graph/lib/commands/EXPLAIN.spec.ts new file mode 100644 index 00000000000..2919a211631 --- /dev/null +++ b/packages/graph/lib/commands/EXPLAIN.spec.ts @@ -0,0 +1,18 @@ +import { strict as assert } from 'assert'; +import testUtils, { GLOBAL } from '../test-utils'; +import { transformArguments } from './EXPLAIN'; + +describe('EXPLAIN', () => { + it('transformArguments', () => { + assert.deepEqual( + transformArguments('key', 'RETURN 0'), + ['GRAPH.EXPLAIN', 'key', 'RETURN 0'] + ); + }); + + testUtils.testWithClient('client.graph.explain', async client => { + const reply = await client.graph.explain('key', 'RETURN 0'); + assert.ok(Array.isArray(reply)); + assert.ok(!reply.find(x => typeof x !== 'string')); + }, GLOBAL.SERVERS.OPEN); +}); diff --git a/packages/graph/lib/commands/EXPLAIN.ts b/packages/graph/lib/commands/EXPLAIN.ts new file mode 100644 index 00000000000..419ff62b112 --- /dev/null +++ b/packages/graph/lib/commands/EXPLAIN.ts @@ -0,0 +1,9 @@ +export const FIRST_KEY_INDEX = 1; + +export const IS_READ_ONLY = true; + +export function transformArguments(key: string, query: string): Array { + return ['GRAPH.EXPLAIN', key, query]; +} + +export declare function transfromReply(): Array; diff --git a/packages/graph/lib/commands/LIST.spec.ts b/packages/graph/lib/commands/LIST.spec.ts new file mode 100644 index 00000000000..d4fab0358b9 --- /dev/null +++ b/packages/graph/lib/commands/LIST.spec.ts @@ -0,0 +1,19 @@ +import { strict as assert } from 'assert'; +import testUtils, { GLOBAL } from '../test-utils'; +import { transformArguments } from './LIST'; + +describe('LIST', () => { + it('transformArguments', () => { + assert.deepEqual( + transformArguments(), + ['GRAPH.LIST'] + ); + }); + + testUtils.testWithClient('client.graph.list', async client => { + assert.deepEqual( + await client.graph.list(), + [] + ); + }, GLOBAL.SERVERS.OPEN); +}); diff --git a/packages/graph/lib/commands/LIST.ts b/packages/graph/lib/commands/LIST.ts new file mode 100644 index 00000000000..1939d43d889 --- /dev/null +++ b/packages/graph/lib/commands/LIST.ts @@ -0,0 +1,7 @@ +export const IS_READ_ONLY = true; + +export function transformArguments(): Array { + return ['GRAPH.LIST']; +} + +export declare function transformReply(): Array; diff --git a/packages/graph/lib/commands/PROFILE.spec.ts b/packages/graph/lib/commands/PROFILE.spec.ts new file mode 100644 index 00000000000..80857eb0ab9 --- /dev/null +++ b/packages/graph/lib/commands/PROFILE.spec.ts @@ -0,0 +1,18 @@ +import { strict as assert } from 'assert'; +import testUtils, { GLOBAL } from '../test-utils'; +import { transformArguments } from './PROFILE'; + +describe('PROFILE', () => { + it('transformArguments', () => { + assert.deepEqual( + transformArguments('key', 'RETURN 0'), + ['GRAPH.PROFILE', 'key', 'RETURN 0'] + ); + }); + + testUtils.testWithClient('client.graph.profile', async client => { + const reply = await client.graph.profile('key', 'RETURN 0'); + assert.ok(Array.isArray(reply)); + assert.ok(!reply.find(x => typeof x !== 'string')); + }, GLOBAL.SERVERS.OPEN); +}); diff --git a/packages/graph/lib/commands/PROFILE.ts b/packages/graph/lib/commands/PROFILE.ts new file mode 100644 index 00000000000..473c526e679 --- /dev/null +++ b/packages/graph/lib/commands/PROFILE.ts @@ -0,0 +1,9 @@ +export const FIRST_KEY_INDEX = 1; + +export const IS_READ_ONLY = true; + +export function transformArguments(key: string, query: string): Array { + return ['GRAPH.PROFILE', key, query]; +} + +export declare function transfromReply(): Array; diff --git a/packages/graph/lib/commands/QUERY.spec.ts b/packages/graph/lib/commands/QUERY.spec.ts new file mode 100644 index 00000000000..44492d75d27 --- /dev/null +++ b/packages/graph/lib/commands/QUERY.spec.ts @@ -0,0 +1,22 @@ +import { strict as assert } from 'assert'; +import testUtils, { GLOBAL } from '../test-utils'; +import { transformArguments } from './QUERY'; + +describe('QUERY', () => { + it('transformArguments', () => { + assert.deepEqual( + transformArguments('key', '*', 100), + ['GRAPH.QUERY', 'key', '*', '100'] + ); + }); + + testUtils.testWithClient('client.graph.query', async client => { + await client.graph.query('key', + "CREATE (r:human {name:'roi', age:34}), (a:human {name:'amit', age:32}), (r)-[:knows]->(a)" + ); + const reply = await client.graph.query('key', + "MATCH (r:human)-[:knows]->(a:human) RETURN r.age, r.name" + ); + assert.equal(reply.data.length, 1); + }, GLOBAL.SERVERS.OPEN); +}); diff --git a/packages/graph/lib/commands/QUERY.ts b/packages/graph/lib/commands/QUERY.ts new file mode 100644 index 00000000000..06436e5e74d --- /dev/null +++ b/packages/graph/lib/commands/QUERY.ts @@ -0,0 +1,43 @@ +import { RedisCommandArgument, RedisCommandArguments } from '@node-redis/client/dist/lib/commands/index'; +import { pushQueryArguments } from '.'; + +export const FIRST_KEY_INDEX = 1; + +export function transformArguments( + graph: RedisCommandArgument, + query: RedisCommandArgument, + timeout?: number +): RedisCommandArguments { + return pushQueryArguments( + ['GRAPH.QUERY'], + graph, + query, + timeout + ); +} + +type Headers = Array; + +type Data = Array>; + +type Metadata = Array; + +type QueryRawReply = [ + headers: Headers, + data: Data, + metadata: Metadata +]; + +interface QueryReply { + headers: Headers, + data: Data, + metadata: Metadata +}; + +export function transformReply(reply: QueryRawReply): QueryReply { + return { + headers: reply[0], + data: reply[1], + metadata: reply[2] + }; +} diff --git a/packages/graph/lib/commands/QUERY_RO.spec.ts b/packages/graph/lib/commands/QUERY_RO.spec.ts new file mode 100644 index 00000000000..78814603aca --- /dev/null +++ b/packages/graph/lib/commands/QUERY_RO.spec.ts @@ -0,0 +1,22 @@ +import { strict as assert } from 'assert'; +import testUtils, { GLOBAL } from '../test-utils'; +import { transformArguments } from './QUERY_RO'; + +describe('QUERY_RO', () => { + it('transformArguments', () => { + assert.deepEqual( + transformArguments('key', '*', 100), + ['GRAPH.RO_QUERY', 'key', '*', '100'] + ); + }); + + testUtils.testWithClient('client.graph.queryRo', async client => { + await client.graph.query('key', + "CREATE (r:human {name:'roi', age:34}), (a:human {name:'amit', age:32}), (r)-[:knows]->(a)" + ); + const reply = await client.graph.queryRo('key', + "MATCH (r:human)-[:knows]->(a:human) RETURN r.age, r.name" + ); + assert.equal(reply.data.length, 1); + }, GLOBAL.SERVERS.OPEN); +}); \ No newline at end of file diff --git a/packages/graph/lib/commands/QUERY_RO.ts b/packages/graph/lib/commands/QUERY_RO.ts new file mode 100644 index 00000000000..9da471adcce --- /dev/null +++ b/packages/graph/lib/commands/QUERY_RO.ts @@ -0,0 +1,21 @@ +import { RedisCommandArgument, RedisCommandArguments } from '@node-redis/client/dist/lib/commands'; +import { pushQueryArguments } from '.'; + +export { FIRST_KEY_INDEX } from './QUERY'; + +export const IS_READ_ONLY = true; + +export function transformArguments( + graph: RedisCommandArgument, + query: RedisCommandArgument, + timeout?: number +): RedisCommandArguments { + return pushQueryArguments( + ['GRAPH.RO_QUERY'], + graph, + query, + timeout + ); +} + +export { transformReply } from './QUERY'; diff --git a/packages/graph/lib/commands/SLOWLOG.spec.ts b/packages/graph/lib/commands/SLOWLOG.spec.ts new file mode 100644 index 00000000000..e3083b994d6 --- /dev/null +++ b/packages/graph/lib/commands/SLOWLOG.spec.ts @@ -0,0 +1,18 @@ +import { strict as assert } from 'assert'; +import testUtils, { GLOBAL } from '../test-utils'; +import { transformArguments } from './SLOWLOG'; + +describe('SLOWLOG', () => { + it('transformArguments', () => { + assert.deepEqual( + transformArguments('key'), + ['GRAPH.SLOWLOG', 'key'] + ); + }); + + testUtils.testWithClient('client.graph.slowLog', async client => { + await client.graph.query('key', 'RETURN 1'); + const reply = await client.graph.slowLog('key'); + assert.equal(reply.length, 1); + }, GLOBAL.SERVERS.OPEN); +}); diff --git a/packages/graph/lib/commands/SLOWLOG.ts b/packages/graph/lib/commands/SLOWLOG.ts new file mode 100644 index 00000000000..6ae87af89bf --- /dev/null +++ b/packages/graph/lib/commands/SLOWLOG.ts @@ -0,0 +1,30 @@ +export const IS_READ_ONLY = true; + +export const FIRST_KEY_INDEX = 1; + +export function transformArguments(key: string) { + return ['GRAPH.SLOWLOG', key]; +} + +type SlowLogRawReply = Array<[ + timestamp: string, + command: string, + query: string, + took: string +]>; + +type SlowLogReply = Array<{ + timestamp: Date; + command: string; + query: string; + took: number; +}>; + +export function transformReply(logs: SlowLogRawReply): SlowLogReply { + return logs.map(([timestamp, command, query, took]) => ({ + timestamp: new Date(Number(timestamp) * 1000), + command, + query, + took: Number(took) + })); +} diff --git a/packages/graph/lib/commands/index.ts b/packages/graph/lib/commands/index.ts new file mode 100644 index 00000000000..0b6180e1752 --- /dev/null +++ b/packages/graph/lib/commands/index.ts @@ -0,0 +1,49 @@ +import * as CONFIG_GET from './CONFIG_GET'; +import * as CONFIG_SET from './CONFIG_SET';; +import * as DELETE from './DELETE'; +import * as EXPLAIN from './EXPLAIN'; +import * as LIST from './LIST'; +import * as PROFILE from './PROFILE'; +import * as QUERY_RO from './QUERY_RO'; +import * as QUERY from './QUERY'; +import * as SLOWLOG from './SLOWLOG'; +import { RedisCommandArgument, RedisCommandArguments } from '@node-redis/client/dist/lib/commands'; + +export default { + CONFIG_GET, + configGet: CONFIG_GET, + CONFIG_SET, + configSet: CONFIG_SET, + DELETE, + delete: DELETE, + EXPLAIN, + explain: EXPLAIN, + LIST, + list: LIST, + PROFILE, + profile: PROFILE, + QUERY_RO, + queryRo: QUERY_RO, + QUERY, + query: QUERY, + SLOWLOG, + slowLog: SLOWLOG +}; + +export function pushQueryArguments( + args: RedisCommandArguments, + graph: RedisCommandArgument, + query: RedisCommandArgument, + timeout?: number +): RedisCommandArguments { + args.push( + graph, + query + ); + + if (timeout !== undefined) { + args.push(timeout.toString()); + } + + return args; +} \ No newline at end of file diff --git a/packages/graph/lib/index.ts b/packages/graph/lib/index.ts new file mode 100644 index 00000000000..bc0e103e8c8 --- /dev/null +++ b/packages/graph/lib/index.ts @@ -0,0 +1 @@ +export { default } from './commands'; diff --git a/packages/graph/lib/test-utils.ts b/packages/graph/lib/test-utils.ts new file mode 100644 index 00000000000..ad6dc0fd190 --- /dev/null +++ b/packages/graph/lib/test-utils.ts @@ -0,0 +1,21 @@ +import TestUtils from '@node-redis/test-utils'; +import RedisGraph from '.'; + +export default new TestUtils({ + dockerImageName: 'redislabs/redisgraph', + dockerImageVersionArgument: 'redisgraph-version', + defaultDockerVersion: '2.8.7' +}); + +export const GLOBAL = { + SERVERS: { + OPEN: { + serverArguments: ['--loadmodule /usr/lib/redis/modules/redisgraph.so'], + clientOptions: { + modules: { + graph: RedisGraph + } + } + } + } +}; diff --git a/packages/graph/package.json b/packages/graph/package.json new file mode 100644 index 00000000000..3b95952aeb5 --- /dev/null +++ b/packages/graph/package.json @@ -0,0 +1,29 @@ +{ + "name": "@node-redis/graph", + "version": "1.0.0-rc", + "license": "MIT", + "main": "./dist/index.js", + "types": "./dist/index.d.ts", + "files": [ + "dist/" + ], + "scripts": { + "test": "nyc -r text-summary -r lcov mocha -r source-map-support/register -r ts-node/register './lib/**/*.spec.ts'", + "build": "tsc", + "documentation": "typedoc" + }, + "peerDependencies": { + "@node-redis/client": "^1.0.0" + }, + "devDependencies": { + "@istanbuljs/nyc-config-typescript": "^1.0.2", + "@node-redis/test-utils": "*", + "@types/node": "^17.0.13", + "nyc": "^15.1.0", + "release-it": "^14.12.4", + "source-map-support": "^0.5.21", + "ts-node": "^10.4.0", + "typedoc": "^0.22.11", + "typescript": "^4.5.5" + } +} diff --git a/packages/graph/tsconfig.json b/packages/graph/tsconfig.json new file mode 100644 index 00000000000..9d17cb63371 --- /dev/null +++ b/packages/graph/tsconfig.json @@ -0,0 +1,20 @@ +{ + "extends": "../../tsconfig.base.json", + "compilerOptions": { + "outDir": "./dist" + }, + "include": [ + "./lib/**/*.ts" + ], + "exclude": [ + "./lib/test-utils.ts", + "./lib/**/*.spec.ts" + ], + "typedocOptions": { + "entryPoints": [ + "./lib" + ], + "entryPointStrategy": "expand", + "out": "../../documentation/graph" + } +} diff --git a/packages/json/lib/test-utils.ts b/packages/json/lib/test-utils.ts index eeee3c77c9d..c41870567ef 100644 --- a/packages/json/lib/test-utils.ts +++ b/packages/json/lib/test-utils.ts @@ -4,7 +4,7 @@ import RedisJSON from '.'; export default new TestUtils({ dockerImageName: 'redislabs/rejson', dockerImageVersionArgument: 'rejson-version', - defaultDockerVersion: '2.0.2' + defaultDockerVersion: '2.0.6' }); export const GLOBAL = { diff --git a/packages/json/package.json b/packages/json/package.json index db9c2373034..361928215a7 100644 --- a/packages/json/package.json +++ b/packages/json/package.json @@ -18,7 +18,7 @@ "devDependencies": { "@istanbuljs/nyc-config-typescript": "^1.0.2", "@node-redis/test-utils": "*", - "@types/node": "^17.0.10", + "@types/node": "^17.0.13", "nyc": "^15.1.0", "release-it": "^14.12.4", "source-map-support": "^0.5.21", diff --git a/packages/search/lib/commands/CREATE.spec.ts b/packages/search/lib/commands/CREATE.spec.ts index 7d21e82d66a..35aec47a676 100644 --- a/packages/search/lib/commands/CREATE.spec.ts +++ b/packages/search/lib/commands/CREATE.spec.ts @@ -340,7 +340,7 @@ describe('CREATE', () => { testUtils.testWithClient('client.ft.create', async client => { assert.equal( await client.ft.create('index', { - field: SchemaFieldTypes.TEXT // TODO: shouldn't be mandatory + field: SchemaFieldTypes.TEXT }), 'OK' ); diff --git a/packages/search/lib/commands/DROPINDEX.spec.ts b/packages/search/lib/commands/DROPINDEX.spec.ts index b1cb7c93132..6a60a5d851f 100644 --- a/packages/search/lib/commands/DROPINDEX.spec.ts +++ b/packages/search/lib/commands/DROPINDEX.spec.ts @@ -22,7 +22,7 @@ describe('DROPINDEX', () => { testUtils.testWithClient('client.ft.dropIndex', async client => { await client.ft.create('index', { - field: SchemaFieldTypes.TEXT // TODO: shouldn't be mandatory + field: SchemaFieldTypes.TEXT }); assert.equal( diff --git a/packages/search/lib/commands/INFO.spec.ts b/packages/search/lib/commands/INFO.spec.ts index 805d4c820c8..2ee3048c01d 100644 --- a/packages/search/lib/commands/INFO.spec.ts +++ b/packages/search/lib/commands/INFO.spec.ts @@ -1,4 +1,5 @@ import { strict as assert } from 'assert'; +import { SchemaFieldTypes } from '.'; import testUtils, { GLOBAL } from '../test-utils'; import { transformArguments } from './INFO'; @@ -11,8 +12,8 @@ describe('INFO', () => { }); testUtils.testWithClient('client.ft.info', async client => { - await client.ft.create('index', {}, { - ON: 'HASH' // TODO: shouldn't be mandatory + await client.ft.create('index', { + field: SchemaFieldTypes.TEXT }); assert.deepEqual( @@ -25,7 +26,16 @@ describe('INFO', () => { keyType: 'HASH', prefixes: [''] }, - attributes: [], + attributes: [[ + 'identifier', + 'field', + 'attribute', + 'field', + 'type', + 'TEXT', + 'WEIGHT', + '1' + ]], numDocs: '0', maxDocId: '0', numTerms: '0', diff --git a/packages/search/lib/commands/SYNDUMP.spec.ts b/packages/search/lib/commands/SYNDUMP.spec.ts index 4b0cb0c8b3a..472db54bcf8 100644 --- a/packages/search/lib/commands/SYNDUMP.spec.ts +++ b/packages/search/lib/commands/SYNDUMP.spec.ts @@ -1,6 +1,7 @@ import { strict as assert } from 'assert'; import testUtils, { GLOBAL } from '../test-utils'; import { transformArguments } from './SYNDUMP'; +import { SchemaFieldTypes } from '.'; describe('SYNDUMP', () => { it('transformArguments', () => { @@ -11,8 +12,8 @@ describe('SYNDUMP', () => { }); testUtils.testWithClient('client.ft.synDump', async client => { - await client.ft.create('index', {}, { - ON: 'HASH' // TODO: shouldn't be mandatory + await client.ft.create('index', { + field: SchemaFieldTypes.TEXT }); assert.deepEqual( diff --git a/packages/search/lib/commands/SYNUPDATE.spec.ts b/packages/search/lib/commands/SYNUPDATE.spec.ts index bf7fed84934..19ac9b85e54 100644 --- a/packages/search/lib/commands/SYNUPDATE.spec.ts +++ b/packages/search/lib/commands/SYNUPDATE.spec.ts @@ -1,6 +1,7 @@ import { strict as assert } from 'assert'; import testUtils, { GLOBAL } from '../test-utils'; import { transformArguments } from './SYNUPDATE'; +import { SchemaFieldTypes } from '.'; describe('SYNUPDATE', () => { describe('transformArguments', () => { @@ -27,8 +28,8 @@ describe('SYNUPDATE', () => { }); testUtils.testWithClient('client.ft.synUpdate', async client => { - await client.ft.create('index', {}, { - ON: 'HASH' // TODO: shouldn't be mandatory + await client.ft.create('index', { + field: SchemaFieldTypes.TEXT }); assert.equal( diff --git a/packages/search/lib/test-utils.ts b/packages/search/lib/test-utils.ts index 4af05e10623..fd11951bb57 100644 --- a/packages/search/lib/test-utils.ts +++ b/packages/search/lib/test-utils.ts @@ -4,7 +4,7 @@ import RediSearch from '.'; export default new TestUtils({ dockerImageName: 'redislabs/redisearch', dockerImageVersionArgument: 'redisearch-version', - defaultDockerVersion: '2.2.1' + defaultDockerVersion: '2.2.7' }); export const GLOBAL = { diff --git a/packages/search/package.json b/packages/search/package.json index 9ab292f9b83..1bf7e1de997 100644 --- a/packages/search/package.json +++ b/packages/search/package.json @@ -18,7 +18,7 @@ "devDependencies": { "@istanbuljs/nyc-config-typescript": "^1.0.2", "@node-redis/test-utils": "*", - "@types/node": "^17.0.10", + "@types/node": "^17.0.13", "nyc": "^15.1.0", "release-it": "^14.12.4", "source-map-support": "^0.5.21", diff --git a/packages/test-utils/package.json b/packages/test-utils/package.json index d827cef26e7..263b4cccff4 100644 --- a/packages/test-utils/package.json +++ b/packages/test-utils/package.json @@ -12,7 +12,7 @@ "devDependencies": { "@istanbuljs/nyc-config-typescript": "^1.0.2", "@types/mocha": "^9.1.0", - "@types/node": "^17.0.10", + "@types/node": "^17.0.13", "@types/yargs": "^17.0.8", "mocha": "^9.2.0", "nyc": "^15.1.0", diff --git a/packages/time-series/lib/commands/INFO_DEBUG.spec.ts b/packages/time-series/lib/commands/INFO_DEBUG.spec.ts index d6c7f2c5f80..00101d980aa 100644 --- a/packages/time-series/lib/commands/INFO_DEBUG.spec.ts +++ b/packages/time-series/lib/commands/INFO_DEBUG.spec.ts @@ -44,6 +44,7 @@ describe('INFO_DEBUG', () => { key: 'key2', timeBucket: 5 }], + keySelfName: 'key', chunks: [{ startTimestamp: 1, endTimestamp: 1, diff --git a/packages/time-series/lib/commands/INFO_DEBUG.ts b/packages/time-series/lib/commands/INFO_DEBUG.ts index 6680a2044bb..ad9522d2a60 100644 --- a/packages/time-series/lib/commands/INFO_DEBUG.ts +++ b/packages/time-series/lib/commands/INFO_DEBUG.ts @@ -16,6 +16,8 @@ export function transformArguments(key: string): Array { type InfoDebugRawReply = [ ...infoArgs: InfoRawReply, _: string, + keySelfName: string, + _: string, chunks: Array<[ _: string, startTimestamp: number, @@ -31,6 +33,7 @@ type InfoDebugRawReply = [ ] interface InfoDebugReply extends InfoReply { + keySelfName: string; chunks: Array<{ startTimestamp: number; endTimestamp: number; @@ -42,7 +45,8 @@ interface InfoDebugReply extends InfoReply { export function transformReply(rawReply: InfoDebugRawReply): InfoDebugReply { const reply = transformInfoReply(rawReply as unknown as InfoRawReply); - (reply as InfoDebugReply).chunks = rawReply[25].map(chunk => ({ + (reply as InfoDebugReply).keySelfName = rawReply[25]; + (reply as InfoDebugReply).chunks = rawReply[27].map(chunk => ({ startTimestamp: chunk[1], endTimestamp: chunk[3], samples: chunk[5], diff --git a/packages/time-series/lib/test-utils.ts b/packages/time-series/lib/test-utils.ts index 7beb04e2987..eebb1b416d8 100644 --- a/packages/time-series/lib/test-utils.ts +++ b/packages/time-series/lib/test-utils.ts @@ -4,7 +4,7 @@ import TimeSeries from '.'; export default new TestUtils({ dockerImageName: 'redislabs/redistimeseries', dockerImageVersionArgument: 'timeseries-version', - defaultDockerVersion: '1.6.0' + defaultDockerVersion: '1.6.8' }); export const GLOBAL = { diff --git a/packages/time-series/package.json b/packages/time-series/package.json index 8242de1c132..9edafb85db9 100644 --- a/packages/time-series/package.json +++ b/packages/time-series/package.json @@ -18,7 +18,7 @@ "devDependencies": { "@istanbuljs/nyc-config-typescript": "^1.0.2", "@node-redis/test-utils": "*", - "@types/node": "^17.0.10", + "@types/node": "^17.0.13", "nyc": "^15.1.0", "release-it": "^14.12.4", "source-map-support": "^0.5.21", From d78d25a0086b4837821c59ac377a361ef27c771e Mon Sep 17 00:00:00 2001 From: leibale Date: Mon, 31 Jan 2022 12:57:39 -0500 Subject: [PATCH 1132/1748] upgrade dependencies --- package-lock.json | 185 +++++++++++++++++------------------ package.json | 1 + packages/client/package.json | 4 +- 3 files changed, 94 insertions(+), 96 deletions(-) diff --git a/package-lock.json b/package-lock.json index 8638fcd1075..eb51172826e 100644 --- a/package-lock.json +++ b/package-lock.json @@ -14,6 +14,7 @@ "dependencies": { "@node-redis/bloom": "^1.0.0", "@node-redis/client": "^1.0.2", + "@node-redis/graph": "^1.0.0-rc", "@node-redis/json": "^1.0.2", "@node-redis/search": "^1.0.2", "@node-redis/time-series": "^1.0.1" @@ -23,10 +24,6 @@ "gh-pages": "^3.2.3", "release-it": "^14.12.1", "typescript": "^4.5.4" - }, - "engines": { - "npm": ">=7", - "typescript": ">=4" } }, "node_modules/@babel/code-frame": { @@ -457,9 +454,9 @@ } }, "node_modules/@humanwhocodes/config-array": { - "version": "0.9.2", - "resolved": "https://registry.npmjs.org/@humanwhocodes/config-array/-/config-array-0.9.2.tgz", - "integrity": "sha512-UXOuFCGcwciWckOpmfKDq/GyhlTf9pN/BzG//x8p8zTOFEcGuA68ANXheFS0AGvy3qgZqLBUkMs7hqzqCKOVwA==", + "version": "0.9.3", + "resolved": "https://registry.npmjs.org/@humanwhocodes/config-array/-/config-array-0.9.3.tgz", + "integrity": "sha512-3xSMlXHh03hCcCmFc0rbKp3Ivt2PFEJnQUJDDMTJQ2wkECZWdq4GePs2ctc5H8zV+cHPaq8k2vU8mrQjA6iHdQ==", "dev": true, "dependencies": { "@humanwhocodes/object-schema": "^1.2.1", @@ -758,9 +755,9 @@ } }, "node_modules/@sindresorhus/is": { - "version": "4.3.0", - "resolved": "https://registry.npmjs.org/@sindresorhus/is/-/is-4.3.0.tgz", - "integrity": "sha512-wwOvh0eO3PiTEivGJWiZ+b946SlMSb4pe+y+Ur/4S87cwo09pYi+FWHHnbrM3W9W7cBYKDqQXcrFYjYUCOJUEQ==", + "version": "4.4.0", + "resolved": "https://registry.npmjs.org/@sindresorhus/is/-/is-4.4.0.tgz", + "integrity": "sha512-QppPM/8l3Mawvh4rn9CNEYIU9bxpXUCRMaX9yUpvBk1nMKusLKpfXGDEKExKaPhLzcn3lzil7pR6rnJ11HgeRQ==", "dev": true, "engines": { "node": ">=10" @@ -788,9 +785,9 @@ } }, "node_modules/@sinonjs/samsam": { - "version": "6.0.2", - "resolved": "https://registry.npmjs.org/@sinonjs/samsam/-/samsam-6.0.2.tgz", - "integrity": "sha512-jxPRPp9n93ci7b8hMfJOFDPRLFYadN6FSpeROFTR4UNF4i5b+EK6m4QXPO46BDhFgRy1JuS87zAnFOzCUwMJcQ==", + "version": "6.1.1", + "resolved": "https://registry.npmjs.org/@sinonjs/samsam/-/samsam-6.1.1.tgz", + "integrity": "sha512-cZ7rKJTLiE7u7Wi/v9Hc2fs3Ucc3jrWeMgPHbbTCeVAB2S0wOBbYlkJVeNSL04i7fdhT8wIbDq1zhC/PXTD2SA==", "dev": true, "dependencies": { "@sinonjs/commons": "^1.6.0", @@ -917,9 +914,9 @@ } }, "node_modules/@types/sinon": { - "version": "10.0.8", - "resolved": "https://registry.npmjs.org/@types/sinon/-/sinon-10.0.8.tgz", - "integrity": "sha512-XZbSLlox2KM7VaEJPZ5G/fMZXJNuAtYiFOax7UT51quZMAJRWKvugPMqNA0mV3jC9HIYpQSg6qbV+ilQMwLqyA==", + "version": "10.0.9", + "resolved": "https://registry.npmjs.org/@types/sinon/-/sinon-10.0.9.tgz", + "integrity": "sha512-xGZVAe61omKnVGedBdTbAveuJ5QyI0LrMIcp0hc1LmVI5IEjs5qG4fM0sv9GIBA2JVoKuf7332IjQX4y5qqMMQ==", "dev": true, "dependencies": { "@sinonjs/fake-timers": "^7.1.0" @@ -1658,9 +1655,9 @@ } }, "node_modules/caniuse-lite": { - "version": "1.0.30001301", - "resolved": "https://registry.npmjs.org/caniuse-lite/-/caniuse-lite-1.0.30001301.tgz", - "integrity": "sha512-csfD/GpHMqgEL3V3uIgosvh+SVIQvCh43SNu9HRbP1lnxkKm1kjDG4f32PP571JplkLjfS+mg2p1gxR7MYrrIA==", + "version": "1.0.30001304", + "resolved": "https://registry.npmjs.org/caniuse-lite/-/caniuse-lite-1.0.30001304.tgz", + "integrity": "sha512-bdsfZd6K6ap87AGqSHJP/s1V+U6Z5lyrcbBu3ovbCCf8cSYpwTtGrCBObMpJqwxfTbLW6YTIdbb1jEeTelcpYQ==", "dev": true, "funding": { "type": "opencollective", @@ -2112,9 +2109,9 @@ "dev": true }, "node_modules/electron-to-chromium": { - "version": "1.4.51", - "resolved": "https://registry.npmjs.org/electron-to-chromium/-/electron-to-chromium-1.4.51.tgz", - "integrity": "sha512-JNEmcYl3mk1tGQmy0EvL5eik/CKSBuzAyGP0QFdG6LIgxQe3II0BL1m2zKc2MZMf3uGqHWE1TFddJML0RpjSHQ==", + "version": "1.4.57", + "resolved": "https://registry.npmjs.org/electron-to-chromium/-/electron-to-chromium-1.4.57.tgz", + "integrity": "sha512-FNC+P5K1n6pF+M0zIK+gFCoXcJhhzDViL3DRIGy2Fv5PohuSES1JHR7T+GlwxSxlzx4yYbsuzCZvHxcBSRCIOw==", "dev": true }, "node_modules/email-addresses": { @@ -2181,9 +2178,9 @@ } }, "node_modules/eslint": { - "version": "8.7.0", - "resolved": "https://registry.npmjs.org/eslint/-/eslint-8.7.0.tgz", - "integrity": "sha512-ifHYzkBGrzS2iDU7KjhCAVMGCvF6M3Xfs8X8b37cgrUlDt6bWRTpRh6T/gtSXv1HJ/BUGgmjvNvOEGu85Iif7w==", + "version": "8.8.0", + "resolved": "https://registry.npmjs.org/eslint/-/eslint-8.8.0.tgz", + "integrity": "sha512-H3KXAzQGBH1plhYS3okDix2ZthuYJlQQEGE5k0IKuEqUSiyu4AmxxlJ2MtTYeJ3xB4jDhcYCwGOg2TXYdnDXlQ==", "dev": true, "dependencies": { "@eslint/eslintrc": "^1.0.5", @@ -2633,9 +2630,9 @@ } }, "node_modules/flatted": { - "version": "3.2.4", - "resolved": "https://registry.npmjs.org/flatted/-/flatted-3.2.4.tgz", - "integrity": "sha512-8/sOawo8tJ4QOBX8YlQBMxL8+RLZfxMQOif9o0KUKTNTjMYElWPE0r/m5VNFxTRd0NSw8qSy8dajrwX4RYI1Hw==", + "version": "3.2.5", + "resolved": "https://registry.npmjs.org/flatted/-/flatted-3.2.5.tgz", + "integrity": "sha512-WIWGi2L3DyTUvUrwRKgGi9TwxQMUEqPOPQBVi71R96jZXJdFskXEmf54BoZaS1kknGODoIGASGEzBUYdyMCBJg==", "dev": true }, "node_modules/foreground-child": { @@ -3853,9 +3850,9 @@ "dev": true }, "node_modules/marked": { - "version": "4.0.10", - "resolved": "https://registry.npmjs.org/marked/-/marked-4.0.10.tgz", - "integrity": "sha512-+QvuFj0nGgO970fySghXGmuw+Fd0gD2x3+MqCWLIPf5oxdv1Ka6b2q+z9RP01P/IaKPMEramy+7cNy/Lw8c3hw==", + "version": "4.0.12", + "resolved": "https://registry.npmjs.org/marked/-/marked-4.0.12.tgz", + "integrity": "sha512-hgibXWrEDNBWgGiK18j/4lkS6ihTe9sxtV4Q1OQppb/0zzyPSzoFANBa5MfsG/zgsWklmNnhm0XACZOH/0HBiQ==", "dev": true, "bin": { "marked": "bin/marked.js" @@ -5555,14 +5552,14 @@ "dev": true }, "node_modules/sinon": { - "version": "12.0.1", - "resolved": "https://registry.npmjs.org/sinon/-/sinon-12.0.1.tgz", - "integrity": "sha512-iGu29Xhym33ydkAT+aNQFBINakjq69kKO6ByPvTsm3yyIACfyQttRTP03aBP/I8GfhFmLzrnKwNNkr0ORb1udg==", + "version": "13.0.0", + "resolved": "https://registry.npmjs.org/sinon/-/sinon-13.0.0.tgz", + "integrity": "sha512-3tjMDB/tY04b06Bnb4aMKQfNrau2C9HET+R4HVWfv2KegDVLGg4wnBqjVepvxR7S7R1GTwDZzEv52tpFipt6yA==", "dev": true, "dependencies": { "@sinonjs/commons": "^1.8.3", - "@sinonjs/fake-timers": "^8.1.0", - "@sinonjs/samsam": "^6.0.2", + "@sinonjs/fake-timers": "^9.0.0", + "@sinonjs/samsam": "^6.1.1", "diff": "^5.0.0", "nise": "^5.1.0", "supports-color": "^7.2.0" @@ -5573,9 +5570,9 @@ } }, "node_modules/sinon/node_modules/@sinonjs/fake-timers": { - "version": "8.1.0", - "resolved": "https://registry.npmjs.org/@sinonjs/fake-timers/-/fake-timers-8.1.0.tgz", - "integrity": "sha512-OAPJUAtgeINhh/TAlUID4QTs53Njm7xzddaVlEs/SXwgtiD1tW22zAB/W1wdqfrpmikgaWQ9Fw6Ws+hsiRm5Vg==", + "version": "9.0.0", + "resolved": "https://registry.npmjs.org/@sinonjs/fake-timers/-/fake-timers-9.0.0.tgz", + "integrity": "sha512-+shXA2X7KNP7H7qNbQTJ3SA+NQc0pZDSBrdvFSRwF8sAo/ohw+ZQFD8Moc+gnz51+1eRXtEQBpKWPiQ4jsRC/w==", "dev": true, "dependencies": { "@sinonjs/commons": "^1.7.0" @@ -6438,7 +6435,7 @@ "devDependencies": { "@istanbuljs/nyc-config-typescript": "^1.0.2", "@node-redis/test-utils": "*", - "@types/node": "^17.0.12", + "@types/node": "^17.0.13", "nyc": "^15.1.0", "release-it": "^14.12.4", "source-map-support": "^0.5.21", @@ -6463,16 +6460,16 @@ "devDependencies": { "@istanbuljs/nyc-config-typescript": "^1.0.2", "@node-redis/test-utils": "*", - "@types/node": "^17.0.12", + "@types/node": "^17.0.13", "@types/redis-parser": "^3.0.0", - "@types/sinon": "^10.0.8", + "@types/sinon": "^10.0.9", "@types/yallist": "^4.0.1", "@typescript-eslint/eslint-plugin": "^5.10.1", "@typescript-eslint/parser": "^5.10.1", - "eslint": "^8.7.0", + "eslint": "^8.8.0", "nyc": "^15.1.0", "release-it": "^14.12.4", - "sinon": "^12.0.1", + "sinon": "^13.0.0", "source-map-support": "^0.5.21", "ts-node": "^10.4.0", "typedoc": "^0.22.11", @@ -6489,7 +6486,7 @@ "devDependencies": { "@istanbuljs/nyc-config-typescript": "^1.0.2", "@node-redis/test-utils": "*", - "@types/node": "^17.0.12", + "@types/node": "^17.0.13", "nyc": "^15.1.0", "release-it": "^14.12.4", "source-map-support": "^0.5.21", @@ -6508,7 +6505,7 @@ "devDependencies": { "@istanbuljs/nyc-config-typescript": "^1.0.2", "@node-redis/test-utils": "*", - "@types/node": "^17.0.12", + "@types/node": "^17.0.13", "nyc": "^15.1.0", "release-it": "^14.12.4", "source-map-support": "^0.5.21", @@ -6527,7 +6524,7 @@ "devDependencies": { "@istanbuljs/nyc-config-typescript": "^1.0.2", "@node-redis/test-utils": "*", - "@types/node": "^17.0.12", + "@types/node": "^17.0.13", "nyc": "^15.1.0", "release-it": "^14.12.4", "source-map-support": "^0.5.21", @@ -6544,7 +6541,7 @@ "devDependencies": { "@istanbuljs/nyc-config-typescript": "^1.0.2", "@types/mocha": "^9.1.0", - "@types/node": "^17.0.12", + "@types/node": "^17.0.13", "@types/yargs": "^17.0.8", "mocha": "^9.2.0", "nyc": "^15.1.0", @@ -6564,7 +6561,7 @@ "devDependencies": { "@istanbuljs/nyc-config-typescript": "^1.0.2", "@node-redis/test-utils": "*", - "@types/node": "^17.0.12", + "@types/node": "^17.0.13", "nyc": "^15.1.0", "release-it": "^14.12.4", "source-map-support": "^0.5.21", @@ -6906,9 +6903,9 @@ } }, "@humanwhocodes/config-array": { - "version": "0.9.2", - "resolved": "https://registry.npmjs.org/@humanwhocodes/config-array/-/config-array-0.9.2.tgz", - "integrity": "sha512-UXOuFCGcwciWckOpmfKDq/GyhlTf9pN/BzG//x8p8zTOFEcGuA68ANXheFS0AGvy3qgZqLBUkMs7hqzqCKOVwA==", + "version": "0.9.3", + "resolved": "https://registry.npmjs.org/@humanwhocodes/config-array/-/config-array-0.9.3.tgz", + "integrity": "sha512-3xSMlXHh03hCcCmFc0rbKp3Ivt2PFEJnQUJDDMTJQ2wkECZWdq4GePs2ctc5H8zV+cHPaq8k2vU8mrQjA6iHdQ==", "dev": true, "requires": { "@humanwhocodes/object-schema": "^1.2.1", @@ -6994,7 +6991,7 @@ "requires": { "@istanbuljs/nyc-config-typescript": "^1.0.2", "@node-redis/test-utils": "*", - "@types/node": "^17.0.12", + "@types/node": "^17.0.13", "nyc": "^15.1.0", "release-it": "^14.12.4", "source-map-support": "^0.5.21", @@ -7008,19 +7005,19 @@ "requires": { "@istanbuljs/nyc-config-typescript": "^1.0.2", "@node-redis/test-utils": "*", - "@types/node": "^17.0.12", + "@types/node": "^17.0.13", "@types/redis-parser": "^3.0.0", - "@types/sinon": "^10.0.8", + "@types/sinon": "^10.0.9", "@types/yallist": "^4.0.1", "@typescript-eslint/eslint-plugin": "^5.10.1", "@typescript-eslint/parser": "^5.10.1", "cluster-key-slot": "1.1.0", - "eslint": "^8.7.0", + "eslint": "^8.8.0", "generic-pool": "3.8.2", "nyc": "^15.1.0", "redis-parser": "3.0.0", "release-it": "^14.12.4", - "sinon": "^12.0.1", + "sinon": "^13.0.0", "source-map-support": "^0.5.21", "ts-node": "^10.4.0", "typedoc": "^0.22.11", @@ -7033,7 +7030,7 @@ "requires": { "@istanbuljs/nyc-config-typescript": "^1.0.2", "@node-redis/test-utils": "*", - "@types/node": "^17.0.12", + "@types/node": "^17.0.13", "nyc": "^15.1.0", "release-it": "^14.12.4", "source-map-support": "^0.5.21", @@ -7047,7 +7044,7 @@ "requires": { "@istanbuljs/nyc-config-typescript": "^1.0.2", "@node-redis/test-utils": "*", - "@types/node": "^17.0.12", + "@types/node": "^17.0.13", "nyc": "^15.1.0", "release-it": "^14.12.4", "source-map-support": "^0.5.21", @@ -7061,7 +7058,7 @@ "requires": { "@istanbuljs/nyc-config-typescript": "^1.0.2", "@node-redis/test-utils": "*", - "@types/node": "^17.0.12", + "@types/node": "^17.0.13", "nyc": "^15.1.0", "release-it": "^14.12.4", "source-map-support": "^0.5.21", @@ -7075,7 +7072,7 @@ "requires": { "@istanbuljs/nyc-config-typescript": "^1.0.2", "@types/mocha": "^9.1.0", - "@types/node": "^17.0.12", + "@types/node": "^17.0.13", "@types/yargs": "^17.0.8", "mocha": "^9.2.0", "nyc": "^15.1.0", @@ -7090,7 +7087,7 @@ "requires": { "@istanbuljs/nyc-config-typescript": "^1.0.2", "@node-redis/test-utils": "*", - "@types/node": "^17.0.12", + "@types/node": "^17.0.13", "nyc": "^15.1.0", "release-it": "^14.12.4", "source-map-support": "^0.5.21", @@ -7250,9 +7247,9 @@ } }, "@sindresorhus/is": { - "version": "4.3.0", - "resolved": "https://registry.npmjs.org/@sindresorhus/is/-/is-4.3.0.tgz", - "integrity": "sha512-wwOvh0eO3PiTEivGJWiZ+b946SlMSb4pe+y+Ur/4S87cwo09pYi+FWHHnbrM3W9W7cBYKDqQXcrFYjYUCOJUEQ==", + "version": "4.4.0", + "resolved": "https://registry.npmjs.org/@sindresorhus/is/-/is-4.4.0.tgz", + "integrity": "sha512-QppPM/8l3Mawvh4rn9CNEYIU9bxpXUCRMaX9yUpvBk1nMKusLKpfXGDEKExKaPhLzcn3lzil7pR6rnJ11HgeRQ==", "dev": true }, "@sinonjs/commons": { @@ -7274,9 +7271,9 @@ } }, "@sinonjs/samsam": { - "version": "6.0.2", - "resolved": "https://registry.npmjs.org/@sinonjs/samsam/-/samsam-6.0.2.tgz", - "integrity": "sha512-jxPRPp9n93ci7b8hMfJOFDPRLFYadN6FSpeROFTR4UNF4i5b+EK6m4QXPO46BDhFgRy1JuS87zAnFOzCUwMJcQ==", + "version": "6.1.1", + "resolved": "https://registry.npmjs.org/@sinonjs/samsam/-/samsam-6.1.1.tgz", + "integrity": "sha512-cZ7rKJTLiE7u7Wi/v9Hc2fs3Ucc3jrWeMgPHbbTCeVAB2S0wOBbYlkJVeNSL04i7fdhT8wIbDq1zhC/PXTD2SA==", "dev": true, "requires": { "@sinonjs/commons": "^1.6.0", @@ -7400,9 +7397,9 @@ } }, "@types/sinon": { - "version": "10.0.8", - "resolved": "https://registry.npmjs.org/@types/sinon/-/sinon-10.0.8.tgz", - "integrity": "sha512-XZbSLlox2KM7VaEJPZ5G/fMZXJNuAtYiFOax7UT51quZMAJRWKvugPMqNA0mV3jC9HIYpQSg6qbV+ilQMwLqyA==", + "version": "10.0.9", + "resolved": "https://registry.npmjs.org/@types/sinon/-/sinon-10.0.9.tgz", + "integrity": "sha512-xGZVAe61omKnVGedBdTbAveuJ5QyI0LrMIcp0hc1LmVI5IEjs5qG4fM0sv9GIBA2JVoKuf7332IjQX4y5qqMMQ==", "dev": true, "requires": { "@sinonjs/fake-timers": "^7.1.0" @@ -7908,9 +7905,9 @@ "dev": true }, "caniuse-lite": { - "version": "1.0.30001301", - "resolved": "https://registry.npmjs.org/caniuse-lite/-/caniuse-lite-1.0.30001301.tgz", - "integrity": "sha512-csfD/GpHMqgEL3V3uIgosvh+SVIQvCh43SNu9HRbP1lnxkKm1kjDG4f32PP571JplkLjfS+mg2p1gxR7MYrrIA==", + "version": "1.0.30001304", + "resolved": "https://registry.npmjs.org/caniuse-lite/-/caniuse-lite-1.0.30001304.tgz", + "integrity": "sha512-bdsfZd6K6ap87AGqSHJP/s1V+U6Z5lyrcbBu3ovbCCf8cSYpwTtGrCBObMpJqwxfTbLW6YTIdbb1jEeTelcpYQ==", "dev": true }, "chalk": { @@ -8250,9 +8247,9 @@ "dev": true }, "electron-to-chromium": { - "version": "1.4.51", - "resolved": "https://registry.npmjs.org/electron-to-chromium/-/electron-to-chromium-1.4.51.tgz", - "integrity": "sha512-JNEmcYl3mk1tGQmy0EvL5eik/CKSBuzAyGP0QFdG6LIgxQe3II0BL1m2zKc2MZMf3uGqHWE1TFddJML0RpjSHQ==", + "version": "1.4.57", + "resolved": "https://registry.npmjs.org/electron-to-chromium/-/electron-to-chromium-1.4.57.tgz", + "integrity": "sha512-FNC+P5K1n6pF+M0zIK+gFCoXcJhhzDViL3DRIGy2Fv5PohuSES1JHR7T+GlwxSxlzx4yYbsuzCZvHxcBSRCIOw==", "dev": true }, "email-addresses": { @@ -8310,9 +8307,9 @@ "dev": true }, "eslint": { - "version": "8.7.0", - "resolved": "https://registry.npmjs.org/eslint/-/eslint-8.7.0.tgz", - "integrity": "sha512-ifHYzkBGrzS2iDU7KjhCAVMGCvF6M3Xfs8X8b37cgrUlDt6bWRTpRh6T/gtSXv1HJ/BUGgmjvNvOEGu85Iif7w==", + "version": "8.8.0", + "resolved": "https://registry.npmjs.org/eslint/-/eslint-8.8.0.tgz", + "integrity": "sha512-H3KXAzQGBH1plhYS3okDix2ZthuYJlQQEGE5k0IKuEqUSiyu4AmxxlJ2MtTYeJ3xB4jDhcYCwGOg2TXYdnDXlQ==", "dev": true, "requires": { "@eslint/eslintrc": "^1.0.5", @@ -8643,9 +8640,9 @@ } }, "flatted": { - "version": "3.2.4", - "resolved": "https://registry.npmjs.org/flatted/-/flatted-3.2.4.tgz", - "integrity": "sha512-8/sOawo8tJ4QOBX8YlQBMxL8+RLZfxMQOif9o0KUKTNTjMYElWPE0r/m5VNFxTRd0NSw8qSy8dajrwX4RYI1Hw==", + "version": "3.2.5", + "resolved": "https://registry.npmjs.org/flatted/-/flatted-3.2.5.tgz", + "integrity": "sha512-WIWGi2L3DyTUvUrwRKgGi9TwxQMUEqPOPQBVi71R96jZXJdFskXEmf54BoZaS1kknGODoIGASGEzBUYdyMCBJg==", "dev": true }, "foreground-child": { @@ -9539,9 +9536,9 @@ "dev": true }, "marked": { - "version": "4.0.10", - "resolved": "https://registry.npmjs.org/marked/-/marked-4.0.10.tgz", - "integrity": "sha512-+QvuFj0nGgO970fySghXGmuw+Fd0gD2x3+MqCWLIPf5oxdv1Ka6b2q+z9RP01P/IaKPMEramy+7cNy/Lw8c3hw==", + "version": "4.0.12", + "resolved": "https://registry.npmjs.org/marked/-/marked-4.0.12.tgz", + "integrity": "sha512-hgibXWrEDNBWgGiK18j/4lkS6ihTe9sxtV4Q1OQppb/0zzyPSzoFANBa5MfsG/zgsWklmNnhm0XACZOH/0HBiQ==", "dev": true }, "merge-stream": { @@ -10798,23 +10795,23 @@ "dev": true }, "sinon": { - "version": "12.0.1", - "resolved": "https://registry.npmjs.org/sinon/-/sinon-12.0.1.tgz", - "integrity": "sha512-iGu29Xhym33ydkAT+aNQFBINakjq69kKO6ByPvTsm3yyIACfyQttRTP03aBP/I8GfhFmLzrnKwNNkr0ORb1udg==", + "version": "13.0.0", + "resolved": "https://registry.npmjs.org/sinon/-/sinon-13.0.0.tgz", + "integrity": "sha512-3tjMDB/tY04b06Bnb4aMKQfNrau2C9HET+R4HVWfv2KegDVLGg4wnBqjVepvxR7S7R1GTwDZzEv52tpFipt6yA==", "dev": true, "requires": { "@sinonjs/commons": "^1.8.3", - "@sinonjs/fake-timers": "^8.1.0", - "@sinonjs/samsam": "^6.0.2", + "@sinonjs/fake-timers": "^9.0.0", + "@sinonjs/samsam": "^6.1.1", "diff": "^5.0.0", "nise": "^5.1.0", "supports-color": "^7.2.0" }, "dependencies": { "@sinonjs/fake-timers": { - "version": "8.1.0", - "resolved": "https://registry.npmjs.org/@sinonjs/fake-timers/-/fake-timers-8.1.0.tgz", - "integrity": "sha512-OAPJUAtgeINhh/TAlUID4QTs53Njm7xzddaVlEs/SXwgtiD1tW22zAB/W1wdqfrpmikgaWQ9Fw6Ws+hsiRm5Vg==", + "version": "9.0.0", + "resolved": "https://registry.npmjs.org/@sinonjs/fake-timers/-/fake-timers-9.0.0.tgz", + "integrity": "sha512-+shXA2X7KNP7H7qNbQTJ3SA+NQc0pZDSBrdvFSRwF8sAo/ohw+ZQFD8Moc+gnz51+1eRXtEQBpKWPiQ4jsRC/w==", "dev": true, "requires": { "@sinonjs/commons": "^1.7.0" diff --git a/package.json b/package.json index e65d8f9811f..4731b9eef32 100644 --- a/package.json +++ b/package.json @@ -25,6 +25,7 @@ "dependencies": { "@node-redis/bloom": "^1.0.0", "@node-redis/client": "^1.0.2", + "@node-redis/graph": "^1.0.0-rc", "@node-redis/json": "^1.0.2", "@node-redis/search": "^1.0.2", "@node-redis/time-series": "^1.0.1" diff --git a/packages/client/package.json b/packages/client/package.json index d450e412565..f29a18685ea 100644 --- a/packages/client/package.json +++ b/packages/client/package.json @@ -28,10 +28,10 @@ "@types/yallist": "^4.0.1", "@typescript-eslint/eslint-plugin": "^5.10.1", "@typescript-eslint/parser": "^5.10.1", - "eslint": "^8.7.0", + "eslint": "^8.8.0", "nyc": "^15.1.0", "release-it": "^14.12.4", - "sinon": "^12.0.1", + "sinon": "^13.0.0", "source-map-support": "^0.5.21", "ts-node": "^10.4.0", "typedoc": "^0.22.11", From a5798e2b15be3f0a38024740af0af4aab0c816fe Mon Sep 17 00:00:00 2001 From: leibale Date: Mon, 31 Jan 2022 12:59:49 -0500 Subject: [PATCH 1133/1748] Release graph@1.0.0 --- packages/graph/package.json | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/packages/graph/package.json b/packages/graph/package.json index 3b95952aeb5..ed4112766cf 100644 --- a/packages/graph/package.json +++ b/packages/graph/package.json @@ -1,6 +1,6 @@ { "name": "@node-redis/graph", - "version": "1.0.0-rc", + "version": "1.0.0", "license": "MIT", "main": "./dist/index.js", "types": "./dist/index.d.ts", From 287b33484972229d9ba134613d6c6cdae69b55c1 Mon Sep 17 00:00:00 2001 From: leibale Date: Mon, 31 Jan 2022 13:01:54 -0500 Subject: [PATCH 1134/1748] Release client@1.0.3 --- packages/client/package.json | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/packages/client/package.json b/packages/client/package.json index f29a18685ea..669b3c1288b 100644 --- a/packages/client/package.json +++ b/packages/client/package.json @@ -1,6 +1,6 @@ { "name": "@node-redis/client", - "version": "1.0.2", + "version": "1.0.3", "license": "MIT", "main": "./dist/index.js", "types": "./dist/index.d.ts", From 429c1e0f08018bd4410bb5daecc1df4a51410cc2 Mon Sep 17 00:00:00 2001 From: leibale Date: Mon, 31 Jan 2022 13:05:13 -0500 Subject: [PATCH 1135/1748] lock versions --- package-lock.json | 16 ++++++++-------- package.json | 12 ++++++------ 2 files changed, 14 insertions(+), 14 deletions(-) diff --git a/package-lock.json b/package-lock.json index eb51172826e..497ea439954 100644 --- a/package-lock.json +++ b/package-lock.json @@ -12,12 +12,12 @@ "./packages/*" ], "dependencies": { - "@node-redis/bloom": "^1.0.0", - "@node-redis/client": "^1.0.2", - "@node-redis/graph": "^1.0.0-rc", - "@node-redis/json": "^1.0.2", - "@node-redis/search": "^1.0.2", - "@node-redis/time-series": "^1.0.1" + "@node-redis/bloom": "1.0.1", + "@node-redis/client": "1.0.3", + "@node-redis/graph": "1.0.0", + "@node-redis/json": "1.0.2", + "@node-redis/search": "1.0.2", + "@node-redis/time-series": "1.0.1" }, "devDependencies": { "@tsconfig/node12": "^1.0.9", @@ -6449,7 +6449,7 @@ }, "packages/client": { "name": "@node-redis/client", - "version": "1.0.2", + "version": "1.0.3", "license": "MIT", "dependencies": { "cluster-key-slot": "1.1.0", @@ -6481,7 +6481,7 @@ }, "packages/graph": { "name": "@node-redis/graph", - "version": "1.0.0-rc", + "version": "1.0.0", "license": "MIT", "devDependencies": { "@istanbuljs/nyc-config-typescript": "^1.0.2", diff --git a/package.json b/package.json index 4731b9eef32..760dd2c02b3 100644 --- a/package.json +++ b/package.json @@ -23,12 +23,12 @@ "gh-pages": "gh-pages -d ./documentation -e ./documentation -u 'documentation-bot '" }, "dependencies": { - "@node-redis/bloom": "^1.0.0", - "@node-redis/client": "^1.0.2", - "@node-redis/graph": "^1.0.0-rc", - "@node-redis/json": "^1.0.2", - "@node-redis/search": "^1.0.2", - "@node-redis/time-series": "^1.0.1" + "@node-redis/bloom": "1.0.1", + "@node-redis/client": "1.0.3", + "@node-redis/graph": "1.0.0", + "@node-redis/json": "1.0.2", + "@node-redis/search": "1.0.2", + "@node-redis/time-series": "1.0.1" }, "devDependencies": { "@tsconfig/node12": "^1.0.9", From 16afa7d0e5761c34228d100e9cfef6e37403e50a Mon Sep 17 00:00:00 2001 From: leibale Date: Mon, 31 Jan 2022 13:05:41 -0500 Subject: [PATCH 1136/1748] Release redis@4.0.3 --- package-lock.json | 4 ++-- package.json | 2 +- 2 files changed, 3 insertions(+), 3 deletions(-) diff --git a/package-lock.json b/package-lock.json index 497ea439954..d6611e37834 100644 --- a/package-lock.json +++ b/package-lock.json @@ -1,12 +1,12 @@ { "name": "redis", - "version": "4.0.2", + "version": "4.0.3", "lockfileVersion": 2, "requires": true, "packages": { "": { "name": "redis", - "version": "4.0.2", + "version": "4.0.3", "license": "MIT", "workspaces": [ "./packages/*" diff --git a/package.json b/package.json index 760dd2c02b3..cf1859a8ba7 100644 --- a/package.json +++ b/package.json @@ -1,7 +1,7 @@ { "name": "redis", "description": "A modern, high performance Redis client", - "version": "4.0.2", + "version": "4.0.3", "license": "MIT", "main": "./dist/index.js", "types": "./dist/index.d.ts", From 9f0f7f52159b8ca544ffaa28f8e35b0c4b565464 Mon Sep 17 00:00:00 2001 From: Simon Prickett Date: Thu, 3 Feb 2022 11:43:36 +0000 Subject: [PATCH 1137/1748] Minor formatting fix. (#1890) --- examples/command-with-modifiers.js | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/examples/command-with-modifiers.js b/examples/command-with-modifiers.js index 78b1d2e3d2f..31940233815 100644 --- a/examples/command-with-modifiers.js +++ b/examples/command-with-modifiers.js @@ -19,8 +19,7 @@ async function commandWithModifiers() { result = await client.set('mykey', 'newvalue', { EX: 60, GET: true - } - ); + }); console.log(result); //myvalue From 10da3710c10c8994482ecdaf67f74b156e830290 Mon Sep 17 00:00:00 2001 From: Anastasios Selalmazidis Date: Mon, 7 Feb 2022 22:22:09 +0200 Subject: [PATCH 1138/1748] Fix CHANGELOG link in migration guide (#1896) --- docs/v3-to-v4.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/docs/v3-to-v4.md b/docs/v3-to-v4.md index ccb8c2abdc2..6f0acc54dc7 100644 --- a/docs/v3-to-v4.md +++ b/docs/v3-to-v4.md @@ -4,7 +4,7 @@ Version 4 of Node Redis is a major refactor. While we have tried to maintain bac ## All of the Breaking Changes -See the [Change Log](../../CHANGELOG.md). +See the [Change Log](../CHANGELOG.md). ### Promises From e265c521e82893f239eb7749eb7b6f367bfdb02d Mon Sep 17 00:00:00 2001 From: Leibale Eidelman Date: Mon, 7 Feb 2022 15:28:09 -0500 Subject: [PATCH 1139/1748] export RediSearchSchema type (#1825) * export RediSearchSchema type * export SearchOptions and SearchReply --- packages/search/lib/commands/ALTER.ts | 4 ++-- packages/search/lib/commands/CREATE.ts | 4 ++-- packages/search/lib/commands/index.ts | 5 ++--- packages/search/lib/index.ts | 3 ++- 4 files changed, 8 insertions(+), 8 deletions(-) diff --git a/packages/search/lib/commands/ALTER.ts b/packages/search/lib/commands/ALTER.ts index 8e74c64376e..bb4c5202c65 100644 --- a/packages/search/lib/commands/ALTER.ts +++ b/packages/search/lib/commands/ALTER.ts @@ -1,6 +1,6 @@ -import { CreateSchema, pushSchema } from '.'; +import { RediSearchSchema, pushSchema } from '.'; -export function transformArguments(index: string, schema: CreateSchema): Array { +export function transformArguments(index: string, schema: RediSearchSchema): Array { const args = ['FT.ALTER', index, 'SCHEMA', 'ADD']; pushSchema(args, schema); diff --git a/packages/search/lib/commands/CREATE.ts b/packages/search/lib/commands/CREATE.ts index b2f49a7f0e3..7578d94ede3 100644 --- a/packages/search/lib/commands/CREATE.ts +++ b/packages/search/lib/commands/CREATE.ts @@ -1,5 +1,5 @@ import { pushOptionalVerdictArgument } from '@node-redis/client/dist/lib/commands/generic-transformers'; -import { RedisSearchLanguages, PropertyName, CreateSchema, pushSchema } from '.'; +import { RedisSearchLanguages, PropertyName, RediSearchSchema, pushSchema } from '.'; interface CreateOptions { ON?: 'HASH' | 'JSON'; @@ -20,7 +20,7 @@ interface CreateOptions { STOPWORDS?: string | Array; } -export function transformArguments(index: string, schema: CreateSchema, options?: CreateOptions): Array { +export function transformArguments(index: string, schema: RediSearchSchema, options?: CreateOptions): Array { const args = ['FT.CREATE', index]; if (options?.ON) { diff --git a/packages/search/lib/commands/index.ts b/packages/search/lib/commands/index.ts index 66b891fd8c4..040242938ee 100644 --- a/packages/search/lib/commands/index.ts +++ b/packages/search/lib/commands/index.ts @@ -204,7 +204,7 @@ type CreateSchemaTagField = CreateSchemaField; -export interface CreateSchema { +export interface RediSearchSchema { [field: string]: CreateSchemaTextField | CreateSchemaNumericField | @@ -212,7 +212,7 @@ export interface CreateSchema { CreateSchemaTagField } -export function pushSchema(args: RedisCommandArguments, schema: CreateSchema) { +export function pushSchema(args: RedisCommandArguments, schema: RediSearchSchema) { for (const [field, fieldOptions] of Object.entries(schema)) { args.push(field); @@ -396,7 +396,6 @@ export interface SearchReply { }>; } - export interface ProfileOptions { LIMITED?: true; } diff --git a/packages/search/lib/index.ts b/packages/search/lib/index.ts index 9480c5a7a7f..93d1e9088fa 100644 --- a/packages/search/lib/index.ts +++ b/packages/search/lib/index.ts @@ -1,4 +1,5 @@ export { default } from './commands'; -export { SchemaFieldTypes, SchemaTextFieldPhonetics } from './commands'; +export { RediSearchSchema, SchemaFieldTypes, SchemaTextFieldPhonetics, SearchReply } from './commands'; export { AggregateSteps, AggregateGroupByReducers } from './commands/AGGREGATE'; +export { SearchOptions } from './commands/SEARCH'; \ No newline at end of file From 11b0c06a3378a589b1f464d73cf7982b8f03a1de Mon Sep 17 00:00:00 2001 From: DidaS Date: Tue, 8 Feb 2022 17:36:02 +0000 Subject: [PATCH 1140/1748] Sligth change to docs (#1902) * Updated docs to avoid confusion * Update v3-to-v4.md --- docs/v3-to-v4.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/docs/v3-to-v4.md b/docs/v3-to-v4.md index 6f0acc54dc7..0bf0d269b90 100644 --- a/docs/v3-to-v4.md +++ b/docs/v3-to-v4.md @@ -16,7 +16,7 @@ The configuration object passed to `createClient` has changed significantly with ### No Auto Connect -In V4, the client does not automatically connect to the server, you need to run `.connect()` before any command, or you will receive error `ClientClosedError: The client is closed`. +In V4, the client does not automatically connect to the server. Instead you need to run `.connect()` after creating the client or you will receive an error: `ClientClosedError: The client is closed`. ```typescript import { createClient } from 'redis'; From 9e904eb862892c2fc699ebecaaae9fbb7075b26a Mon Sep 17 00:00:00 2001 From: Leibale Eidelman Date: Mon, 14 Feb 2022 15:06:05 -0500 Subject: [PATCH 1141/1748] fix #1891 - fix ft.create type (#1901) --- packages/search/lib/commands/index.ts | 12 ++++++------ 1 file changed, 6 insertions(+), 6 deletions(-) diff --git a/packages/search/lib/commands/index.ts b/packages/search/lib/commands/index.ts index 040242938ee..0379f9252d1 100644 --- a/packages/search/lib/commands/index.ts +++ b/packages/search/lib/commands/index.ts @@ -175,7 +175,7 @@ export enum SchemaFieldTypes { TAG = 'TAG' } -type CreateSchemaField> = T | ({ +type CreateSchemaField> = T | ({ type: T; AS?: string; SORTABLE?: true | 'UNF'; @@ -209,7 +209,7 @@ export interface RediSearchSchema { CreateSchemaTextField | CreateSchemaNumericField | CreateSchemaGeoField | - CreateSchemaTagField + CreateSchemaTagField; } export function pushSchema(args: RedisCommandArguments, schema: RediSearchSchema) { @@ -228,7 +228,7 @@ export function pushSchema(args: RedisCommandArguments, schema: RediSearchSchema args.push(fieldOptions.type); switch (fieldOptions.type) { - case 'TEXT': + case SchemaFieldTypes.TEXT: if (fieldOptions.NOSTEM) { args.push('NOSTEM'); } @@ -243,11 +243,11 @@ export function pushSchema(args: RedisCommandArguments, schema: RediSearchSchema break; - // case 'NUMERIC': - // case 'GEO': + // case SchemaFieldTypes.NUMERIC: + // case SchemaFieldTypes.GEO: // break; - case 'TAG': + case SchemaFieldTypes.TAG: if (fieldOptions.SEPARATOR) { args.push('SEPARATOR', fieldOptions.SEPARATOR); } From 6dd15d96aa0534e37a6695d4a217fa82b1ac08bb Mon Sep 17 00:00:00 2001 From: Leibale Eidelman Date: Mon, 14 Feb 2022 15:23:18 -0500 Subject: [PATCH 1142/1748] ref #1888 - add disableOfflineQueue (#1900) * ref #1888 - add disableOfflineQueue * fix flushQueuesOnError * update docs Co-authored-by: Guy Royse Co-authored-by: Guy Royse --- docs/FAQ.md | 4 +++- docs/client-configuration.md | 1 + packages/client/lib/client/index.ts | 3 ++- 3 files changed, 6 insertions(+), 2 deletions(-) diff --git a/docs/FAQ.md b/docs/FAQ.md index 5d65ff7ee0b..3babbb9d845 100644 --- a/docs/FAQ.md +++ b/docs/FAQ.md @@ -4,7 +4,9 @@ Nobody has *actually* asked these questions. But, we needed somewhere to put all ## What happens when the network goes down? -When a socket closed unexpectedly, all the commands that were already sent will reject as they might have been executed on the server. The rest will remain queued in memory until a new socket is established. If the client is closed—either by returning an error from [`reconnectStrategy`](./client-configuration.md#reconnect-strategy) or by manually calling `.disconnect()`—they will be rejected. +When a socket closes unexpectedly, all the commands that were already sent will reject as they might have been executed on the server. The rest will remain queued in memory until a new socket is established. If the client is closed—either by returning an error from [`reconnectStrategy`](./client-configuration.md#reconnect-strategy) or by manually calling `.disconnect()`—they will be rejected. + +If don't want to queue commands in memory until a new socket is established, set the `disableOfflineQueue` option to `true` in the [client configuration](./client-configuration.md). This will result in those commands being rejected. ## How are commands batched? diff --git a/docs/client-configuration.md b/docs/client-configuration.md index 0600d8e5763..092f3d8eb83 100644 --- a/docs/client-configuration.md +++ b/docs/client-configuration.md @@ -20,6 +20,7 @@ | 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 | +| disableOfflineQueue | `false` | Disables offline queuing, see the [FAQ](./FAQ.md#what-happens-when-the-network-goes-down) for details | | 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)) | | isolationPoolOptions | | See the [Isolated Execution Guide](./isolated-execution.md) | diff --git a/packages/client/lib/client/index.ts b/packages/client/lib/client/index.ts index 2ce93773e97..da0b95bd420 100644 --- a/packages/client/lib/client/index.ts +++ b/packages/client/lib/client/index.ts @@ -26,6 +26,7 @@ export interface RedisClientOptions< name?: string; database?: number; commandsQueueMaxLength?: number; + disableOfflineQueue?: boolean; readonly?: boolean; legacyMode?: boolean; isolationPoolOptions?: PoolOptions; @@ -274,7 +275,7 @@ export default class RedisClient .on('data', data => this.#queue.parseResponse(data)) .on('error', err => { this.emit('error', err); - if (this.#socket.isOpen) { + if (this.#socket.isOpen && !this.#options?.disableOfflineQueue) { this.#queue.flushWaitingForReply(err); } else { this.#queue.flushAll(err); From 0803f4e19c59fc9e063840400f361573127ac411 Mon Sep 17 00:00:00 2001 From: Leibale Eidelman Date: Mon, 14 Feb 2022 15:23:35 -0500 Subject: [PATCH 1143/1748] add `nodeAddressMap` config for cluster (#1827) * add `nodeAddressMap` config for cluster * Update cluster-slots.ts * Update cluster-slots.ts * update docs Co-authored-by: Guy Royse Co-authored-by: Guy Royse --- docs/clustering.md | 23 ++++++ packages/client/lib/cluster/cluster-slots.ts | 80 ++++++++++++------- packages/client/lib/cluster/index.ts | 11 +-- .../client/lib/commands/CLUSTER_NODES.spec.ts | 12 +-- packages/client/lib/commands/CLUSTER_NODES.ts | 28 +++---- 5 files changed, 100 insertions(+), 54 deletions(-) diff --git a/docs/clustering.md b/docs/clustering.md index f5ef9a9612d..bc7a5561ae0 100644 --- a/docs/clustering.md +++ b/docs/clustering.md @@ -38,9 +38,32 @@ 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 | +| nodeAddressMap | | Object defining the [node address mapping](#node-address-map) | | 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)) | +## Node Address Map + +Your cluster might be configured to work within an internal network that your local environment doesn't have access to. For example, your development machine could only have access to external addresses, but the cluster returns its internal addresses. In this scenario, it's useful to provide a map from those internal addresses to the external ones. + +The configuration for this is a simple mapping. Just provide a `nodeAddressMap` property mapping the internal addresses and ports to the external addresses and ports. Then, any address provided to `rootNodes` or returned from the cluster will be mapped accordingly: + +```javascript +createCluster({ + rootNodes: [{ + url: '10.0.0.1:30001' + }, { + url: '10.0.0.2:30002' + }], + nodeAddressMap: { + '10.0.0.1:30001': 'external-host-1.io:30001', + '10.0.0.2:30002': 'external-host-2.io:30002' + } +}); +``` + +> This is a common problem when using ElastiCache. See [Accessing ElastiCache from outside AWS](https://docs.aws.amazon.com/AmazonElastiCache/latest/red-ug/accessing-elasticache.html) for more information on that. + ## Command Routing ### Commands that operate on Redis Keys diff --git a/packages/client/lib/cluster/cluster-slots.ts b/packages/client/lib/cluster/cluster-slots.ts index 044cedf56a8..872b5462ac5 100644 --- a/packages/client/lib/cluster/cluster-slots.ts +++ b/packages/client/lib/cluster/cluster-slots.ts @@ -14,6 +14,15 @@ export interface ClusterNode { client: RedisClientType; } +interface NodeAddress { + host: string; + port: number; +} + +export type NodeAddressMap = { + [address: string]: NodeAddress; +} | ((address: string) => NodeAddress | undefined); + interface SlotNodes { master: ClusterNode; replicas: Array>; @@ -26,7 +35,7 @@ export default class RedisClusterSlots; readonly #Client: InstantiableRedisClient; readonly #onError: OnError; - readonly #nodeByUrl = new Map>(); + readonly #nodeByAddress = new Map>(); readonly #slots: Array> = []; constructor(options: RedisClusterOptions, onError: OnError) { @@ -37,7 +46,7 @@ export default class RedisClusterSlots { for (const rootNode of this.#options.rootNodes) { - if (await this.#discoverNodes(this.#clientOptionsDefaults(rootNode))) return; + if (await this.#discoverNodes(rootNode)) return; } throw new RootNodesUnavailableError(); @@ -75,7 +84,7 @@ export default class RedisClusterSlots): Promise { if (await this.#discoverNodes(startWith.options)) return; - for (const { client } of this.#nodeByUrl.values()) { + for (const { client } of this.#nodeByAddress.values()) { if (client === startWith) continue; if (await this.#discoverNodes(client.options)) return; @@ -85,7 +94,7 @@ export default class RedisClusterSlots): Promise { - // Override this.#slots and add not existing clients to this.#nodeByUrl + // Override this.#slots and add not existing clients to this.#nodeByAddress const promises: Array> = [], clientsInUse = new Set(); for (const master of masters) { @@ -94,7 +103,7 @@ export default class RedisClusterSlots this.#initiateClientForNode(replica, true, clientsInUse, promises)) : [], - clientIterator: undefined // will be initiated in use + clientIterator: undefined // will be initiated in use }; for (const { from, to } of master.slots) { @@ -104,12 +113,12 @@ export default class RedisClusterSlots { @@ -132,16 +142,26 @@ export default class RedisClusterSlots, promises: Array>): ClusterNode { - const url = `${nodeData.host}:${nodeData.port}`; - clientsInUse.add(url); + const address = `${nodeData.host}:${nodeData.port}`; + clientsInUse.add(address); - let node = this.#nodeByUrl.get(url); + let node = this.#nodeByAddress.get(address); if (!node) { node = { id: nodeData.id, client: this.#initiateClient({ - socket: { + socket: this.#getNodeAddress(address) ?? { host: nodeData.host, port: nodeData.port }, @@ -149,7 +169,7 @@ export default class RedisClusterSlots>; #getRandomClient(): RedisClientType { - if (!this.#nodeByUrl.size) { + if (!this.#nodeByAddress.size) { throw new Error('Cluster is not connected'); } if (!this.#randomClientIterator) { - this.#randomClientIterator = this.#nodeByUrl.values(); + this.#randomClientIterator = this.#nodeByAddress.values(); } const {done, value} = this.#randomClientIterator.next(); @@ -218,8 +238,7 @@ export default class RedisClusterSlots> { const masters = []; - - for (const node of this.#nodeByUrl.values()) { + for (const node of this.#nodeByAddress.values()) { if (node.client.options?.readonly) continue; masters.push(node); @@ -228,8 +247,11 @@ export default class RedisClusterSlots | undefined { - return this.#nodeByUrl.get(url); + getNodeByAddress(address: string): ClusterNode | undefined { + const mappedAddress = this.#getNodeAddress(address); + return this.#nodeByAddress.get( + mappedAddress ? `${mappedAddress.host}:${mappedAddress.port}` : address + ); } quit(): Promise { @@ -242,13 +264,13 @@ export default class RedisClusterSlots) => Promise): Promise { const promises = []; - for (const { client } of this.#nodeByUrl.values()) { + for (const { client } of this.#nodeByAddress.values()) { promises.push(fn(client)); } await Promise.all(promises); - this.#nodeByUrl.clear(); + this.#nodeByAddress.clear(); this.#slots.splice(0); } } diff --git a/packages/client/lib/cluster/index.ts b/packages/client/lib/cluster/index.ts index 123400dd7e5..404d13b0512 100644 --- a/packages/client/lib/cluster/index.ts +++ b/packages/client/lib/cluster/index.ts @@ -1,7 +1,7 @@ import COMMANDS from './commands'; import { RedisCommand, RedisCommandArgument, RedisCommandArguments, RedisCommandRawReply, RedisCommandReply, RedisModules, RedisPlugins, RedisScript, RedisScripts } from '../commands'; import { ClientCommandOptions, RedisClientCommandSignature, RedisClientOptions, RedisClientType, WithModules, WithScripts } from '../client'; -import RedisClusterSlots, { ClusterNode } from './cluster-slots'; +import RedisClusterSlots, { ClusterNode, NodeAddressMap } from './cluster-slots'; import { extendWithModulesAndScripts, transformCommandArguments, transformCommandReply, extendWithCommands } from '../commander'; import { EventEmitter } from 'events'; import RedisClusterMultiCommand, { RedisClusterMultiCommandType } from './multi-command'; @@ -17,6 +17,7 @@ export interface RedisClusterOptions< defaults?: Partial; useReplicas?: boolean; maxCommandRedirections?: number; + nodeAddressMap?: NodeAddressMap; } type WithCommands = { @@ -144,16 +145,16 @@ export default class RedisCluster { ].join('\n')), [{ id: 'master', - url: '127.0.0.1:30001@31001', + address: '127.0.0.1:30001@31001', host: '127.0.0.1', port: 30001, cport: 31001, @@ -34,7 +34,7 @@ describe('CLUSTER NODES', () => { }], replicas: [{ id: 'slave', - url: '127.0.0.1:30002@31002', + address: '127.0.0.1:30002@31002', host: '127.0.0.1', port: 30002, cport: 31002, @@ -48,14 +48,14 @@ describe('CLUSTER NODES', () => { ); }); - it('should support urls without cport', () => { + it('should support addresses 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', + address: '127.0.0.1:30001', host: '127.0.0.1', port: 30001, cport: null, @@ -80,7 +80,7 @@ describe('CLUSTER NODES', () => { ), [{ id: 'id', - url: '127.0.0.1:30001@31001', + address: '127.0.0.1:30001@31001', host: '127.0.0.1', port: 30001, cport: 31001, @@ -102,7 +102,7 @@ describe('CLUSTER NODES', () => { ), [{ id: 'id', - url: '127.0.0.1:30001@31001', + address: '127.0.0.1:30001@31001', host: '127.0.0.1', port: 30001, cport: 31001, diff --git a/packages/client/lib/commands/CLUSTER_NODES.ts b/packages/client/lib/commands/CLUSTER_NODES.ts index ba4477cdd20..846bbccf10d 100644 --- a/packages/client/lib/commands/CLUSTER_NODES.ts +++ b/packages/client/lib/commands/CLUSTER_NODES.ts @@ -7,15 +7,15 @@ export enum RedisClusterNodeLinkStates { DISCONNECTED = 'disconnected' } -interface RedisClusterNodeTransformedUrl { +interface RedisClusterNodeAddress { host: string; port: number; cport: number | null; } -export interface RedisClusterReplicaNode extends RedisClusterNodeTransformedUrl { +export interface RedisClusterReplicaNode extends RedisClusterNodeAddress { id: string; - url: string; + address: string; flags: Array; pingSent: number; pongRecv: number; @@ -39,11 +39,11 @@ export function transformReply(reply: string): Array { replicasMap = new Map>(); for (const line of lines) { - const [id, url, flags, masterId, pingSent, pongRecv, configEpoch, linkState, ...slots] = line.split(' '), + const [id, address, flags, masterId, pingSent, pongRecv, configEpoch, linkState, ...slots] = line.split(' '), node = { id, - url, - ...transformNodeUrl(url), + address, + ...transformNodeAddress(address), flags: flags.split(','), pingSent: Number(pingSent), pongRecv: Number(pongRecv), @@ -84,22 +84,22 @@ export function transformReply(reply: string): Array { return [...mastersMap.values()]; } -function transformNodeUrl(url: string): RedisClusterNodeTransformedUrl { - const indexOfColon = url.indexOf(':'), - indexOfAt = url.indexOf('@', indexOfColon), - host = url.substring(0, indexOfColon); +function transformNodeAddress(address: string): RedisClusterNodeAddress { + const indexOfColon = address.indexOf(':'), + indexOfAt = address.indexOf('@', indexOfColon), + host = address.substring(0, indexOfColon); if (indexOfAt === -1) { return { host, - port: Number(url.substring(indexOfColon + 1)), + port: Number(address.substring(indexOfColon + 1)), cport: null }; } return { - host: url.substring(0, indexOfColon), - port: Number(url.substring(indexOfColon + 1, indexOfAt)), - cport: Number(url.substring(indexOfAt + 1)) + host: address.substring(0, indexOfColon), + port: Number(address.substring(indexOfColon + 1, indexOfAt)), + cport: Number(address.substring(indexOfAt + 1)) }; } From 23991f7a0bcbe062659a9eaade2d8cee5396a998 Mon Sep 17 00:00:00 2001 From: Leibale Eidelman Date: Tue, 15 Feb 2022 14:22:34 -0500 Subject: [PATCH 1144/1748] fix #1865 - enhance "all in one" types (#1984) --- index.ts | 48 +++++++++++++++++++++++++++++++++++++----------- 1 file changed, 37 insertions(+), 11 deletions(-) diff --git a/index.ts b/index.ts index 63f21c582ef..c948e5eac33 100644 --- a/index.ts +++ b/index.ts @@ -1,5 +1,13 @@ -import { createClient as _createClient, createCluster as _createCluster, RedisClientOptions, RedisClientType, RedisClusterOptions, RedisClusterType } from '@node-redis/client'; -import { RedisScripts } from '@node-redis/client/dist/lib/commands'; +import { + RedisModules, + RedisScripts, + createClient as _createClient, + RedisClientOptions, + RedisClientType as _RedisClientType, + createCluster as _createCluster, + RedisClusterOptions, + RedisClusterType as _RedisClusterType +} from '@node-redis/client'; import RedisBloomModules from '@node-redis/bloom'; import RedisGraph from '@node-redis/graph'; import RedisJSON from '@node-redis/json'; @@ -13,7 +21,7 @@ export * from '@node-redis/json'; export * from '@node-redis/search'; export * from '@node-redis/time-series'; -const modules = { +const modules = { ...RedisBloomModules, graph: RedisGraph, json: RedisJSON, @@ -21,20 +29,38 @@ const modules = { ts: RedisTimeSeries }; -export function createClient( - options?: Omit, 'modules'> -): RedisClientType { +export type RedisDefaultModules = typeof modules; + +export type RedisClientType< + M extends RedisModules = RedisDefaultModules, + S extends RedisScripts = Record +> = _RedisClientType; + +export function createClient( + options?: RedisClientOptions +): _RedisClientType { return _createClient({ ...options, - modules + modules: { + ...modules, + ...(options?.modules as M) + } }); } -export function createCluster( - options: Omit, 'modules'> -): RedisClusterType { +export type RedisClusterType< + M extends RedisModules = RedisDefaultModules, + S extends RedisScripts = Record +> = _RedisClusterType; + +export function createCluster( + options: RedisClusterOptions +): RedisClusterType { return _createCluster({ ...options, - modules + modules: { + ...modules, + ...(options?.modules as M) + } }); } From f738ac56b3d7cc7ff0002875f8cf33fb450bbb38 Mon Sep 17 00:00:00 2001 From: Simon Prickett Date: Tue, 15 Feb 2022 21:09:23 +0000 Subject: [PATCH 1145/1748] Fixed what I think are a couple of typos -> `SORTBT` -> `SORTBY`. (#1987) * Fixed what I think are a couple of typos -> `SORTBT` -> `SORTBY`. Fixed what I think are a couple of typos -> `SORTBT` -> `SORTBY`. * Update index.spec.ts Co-authored-by: Leibale Eidelman --- packages/search/lib/commands/index.spec.ts | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/packages/search/lib/commands/index.spec.ts b/packages/search/lib/commands/index.spec.ts index f0afa304a92..4c54a0dfdf2 100644 --- a/packages/search/lib/commands/index.spec.ts +++ b/packages/search/lib/commands/index.spec.ts @@ -5,15 +5,15 @@ describe('pushSortByArguments', () => { describe('single', () => { it('string', () => { assert.deepEqual( - pushSortByArguments([], 'SORTBT', '@property'), - ['SORTBT', '1', '@property'] + pushSortByArguments([], 'SORTBY', '@property'), + ['SORTBY', '1', '@property'] ); }); it('.BY', () => { assert.deepEqual( - pushSortByArguments([], 'SORTBT', { BY: '@property' }), - ['SORTBT', '1', '@property'] + pushSortByArguments([], 'SORTBY', { BY: '@property' }), + ['SORTBY', '1', '@property'] ); }); From c03ab8803d21fe9a5bc20595cf530f5106e829bf Mon Sep 17 00:00:00 2001 From: Leibale Eidelman Date: Mon, 21 Feb 2022 12:56:42 -0500 Subject: [PATCH 1146/1748] fix #1998 - fix RPOPLPUSH return type (#1999) --- packages/client/lib/commands/RPOPLPUSH.ts | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/packages/client/lib/commands/RPOPLPUSH.ts b/packages/client/lib/commands/RPOPLPUSH.ts index d388b55ac0a..da45f6f6024 100644 --- a/packages/client/lib/commands/RPOPLPUSH.ts +++ b/packages/client/lib/commands/RPOPLPUSH.ts @@ -9,4 +9,4 @@ export function transformArguments( return ['RPOPLPUSH', source, destination]; } -export declare function transformReply(): number | null; +export declare function transformReply(): RedisCommandArgument | null; From 6d6ec7e3e906e25d3a0c726dbaf3fab9e6b649cb Mon Sep 17 00:00:00 2001 From: JamesGDiaz <47843895+JamesGDiaz@users.noreply.github.com> Date: Mon, 21 Feb 2022 17:59:21 -0600 Subject: [PATCH 1147/1748] fix TS.QUERYINDEX with multiple filters (#1996) * fix TS.QUERYINDEX with multiple filters * Update QUERYINDEX.spec.ts * Update QUERYINDEX.spec.ts * Update QUERYINDEX.spec.ts Co-authored-by: Leibale Eidelman --- .../lib/commands/QUERYINDEX.spec.ts | 31 ++++++++++++------- .../time-series/lib/commands/QUERYINDEX.ts | 8 +++-- 2 files changed, 25 insertions(+), 14 deletions(-) diff --git a/packages/time-series/lib/commands/QUERYINDEX.spec.ts b/packages/time-series/lib/commands/QUERYINDEX.spec.ts index ebd3e14dcdc..010c5c8f639 100644 --- a/packages/time-series/lib/commands/QUERYINDEX.spec.ts +++ b/packages/time-series/lib/commands/QUERYINDEX.spec.ts @@ -3,21 +3,28 @@ import testUtils, { GLOBAL } from '../test-utils'; import { transformArguments } from './QUERYINDEX'; describe('QUERYINDEX', () => { - it('transformArguments', () => { - assert.deepEqual( - transformArguments('*'), - ['TS.QUERYINDEX', '*'] - ); + describe('transformArguments', () => { + it('single filter', () => { + assert.deepEqual( + transformArguments('*'), + ['TS.QUERYINDEX', '*'] + ); + }); + + it('multiple filters', () => { + assert.deepEqual( + transformArguments(['a=1', 'b=2']), + ['TS.QUERYINDEX', 'a=1', 'b=2'] + ); + }); }); testUtils.testWithClient('client.ts.queryIndex', async client => { - await Promise.all([ - client.ts.create('key', { - LABELS: { - label: 'value' - } - }) - ]); + await client.ts.create('key', { + LABELS: { + label: 'value' + } + }); assert.deepEqual( await client.ts.queryIndex('label=value'), diff --git a/packages/time-series/lib/commands/QUERYINDEX.ts b/packages/time-series/lib/commands/QUERYINDEX.ts index c3970675ea7..8872605fa61 100644 --- a/packages/time-series/lib/commands/QUERYINDEX.ts +++ b/packages/time-series/lib/commands/QUERYINDEX.ts @@ -1,7 +1,11 @@ +import { RedisCommandArguments } from '@node-redis/client/dist/lib/commands'; +import { pushVerdictArguments } from '@node-redis/client/dist/lib/commands/generic-transformers'; +import { Filter } from '.'; + export const IS_READ_ONLY = true; -export function transformArguments(query: string): Array { - return ['TS.QUERYINDEX', query]; +export function transformArguments(filter: Filter): RedisCommandArguments { + return pushVerdictArguments(['TS.QUERYINDEX'], filter); } export declare function transformReply(): Array; From 5900c9abadc725e167213a43f29f76bbb5bde194 Mon Sep 17 00:00:00 2001 From: leibale Date: Mon, 21 Feb 2022 20:43:41 -0500 Subject: [PATCH 1148/1748] Release search@1.0.3 --- packages/search/package.json | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/packages/search/package.json b/packages/search/package.json index 1bf7e1de997..43daa4d7327 100644 --- a/packages/search/package.json +++ b/packages/search/package.json @@ -1,6 +1,6 @@ { "name": "@node-redis/search", - "version": "1.0.2", + "version": "1.0.3", "license": "MIT", "main": "./dist/index.js", "types": "./dist/index.d.ts", From fead4069d0440772efd79127c1efdb2e3cab0901 Mon Sep 17 00:00:00 2001 From: leibale Date: Mon, 21 Feb 2022 20:49:54 -0500 Subject: [PATCH 1149/1748] Release time-series@1.0.2 --- packages/time-series/package.json | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/packages/time-series/package.json b/packages/time-series/package.json index 9edafb85db9..8891fe61f2e 100644 --- a/packages/time-series/package.json +++ b/packages/time-series/package.json @@ -1,6 +1,6 @@ { "name": "@node-redis/time-series", - "version": "1.0.1", + "version": "1.0.2", "license": "MIT", "main": "./dist/index.js", "types": "./dist/index.d.ts", From be3b331bb4a1fbda5334995d275191a138ce836e Mon Sep 17 00:00:00 2001 From: leibale Date: Mon, 21 Feb 2022 20:50:58 -0500 Subject: [PATCH 1150/1748] Release client@1.0.4 --- packages/client/package.json | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/packages/client/package.json b/packages/client/package.json index 669b3c1288b..fb7f6a6af38 100644 --- a/packages/client/package.json +++ b/packages/client/package.json @@ -1,6 +1,6 @@ { "name": "@node-redis/client", - "version": "1.0.3", + "version": "1.0.4", "license": "MIT", "main": "./dist/index.js", "types": "./dist/index.d.ts", From 9f7d9ccb6ee06066ff69046a1a3414ef0e928391 Mon Sep 17 00:00:00 2001 From: leibale Date: Mon, 21 Feb 2022 20:54:25 -0500 Subject: [PATCH 1151/1748] Upgrade dependencies --- package-lock.json | 12 ++++++------ package.json | 6 +++--- 2 files changed, 9 insertions(+), 9 deletions(-) diff --git a/package-lock.json b/package-lock.json index d6611e37834..6f137d9daaa 100644 --- a/package-lock.json +++ b/package-lock.json @@ -13,11 +13,11 @@ ], "dependencies": { "@node-redis/bloom": "1.0.1", - "@node-redis/client": "1.0.3", + "@node-redis/client": "1.0.4", "@node-redis/graph": "1.0.0", "@node-redis/json": "1.0.2", - "@node-redis/search": "1.0.2", - "@node-redis/time-series": "1.0.1" + "@node-redis/search": "1.0.3", + "@node-redis/time-series": "1.0.2" }, "devDependencies": { "@tsconfig/node12": "^1.0.9", @@ -6449,7 +6449,7 @@ }, "packages/client": { "name": "@node-redis/client", - "version": "1.0.3", + "version": "1.0.4", "license": "MIT", "dependencies": { "cluster-key-slot": "1.1.0", @@ -6519,7 +6519,7 @@ }, "packages/search": { "name": "@node-redis/search", - "version": "1.0.2", + "version": "1.0.3", "license": "MIT", "devDependencies": { "@istanbuljs/nyc-config-typescript": "^1.0.2", @@ -6556,7 +6556,7 @@ }, "packages/time-series": { "name": "@node-redis/time-series", - "version": "1.0.1", + "version": "1.0.2", "license": "MIT", "devDependencies": { "@istanbuljs/nyc-config-typescript": "^1.0.2", diff --git a/package.json b/package.json index cf1859a8ba7..9c2b6bca207 100644 --- a/package.json +++ b/package.json @@ -24,11 +24,11 @@ }, "dependencies": { "@node-redis/bloom": "1.0.1", - "@node-redis/client": "1.0.3", + "@node-redis/client": "1.0.4", "@node-redis/graph": "1.0.0", "@node-redis/json": "1.0.2", - "@node-redis/search": "1.0.2", - "@node-redis/time-series": "1.0.1" + "@node-redis/search": "1.0.3", + "@node-redis/time-series": "1.0.2" }, "devDependencies": { "@tsconfig/node12": "^1.0.9", From 00b58e850515310bb406126c1fe0fa5d858a19ae Mon Sep 17 00:00:00 2001 From: leibale Date: Mon, 21 Feb 2022 20:54:41 -0500 Subject: [PATCH 1152/1748] Release redis@4.0.4 --- package-lock.json | 4 ++-- package.json | 2 +- 2 files changed, 3 insertions(+), 3 deletions(-) diff --git a/package-lock.json b/package-lock.json index 6f137d9daaa..3072740667c 100644 --- a/package-lock.json +++ b/package-lock.json @@ -1,12 +1,12 @@ { "name": "redis", - "version": "4.0.3", + "version": "4.0.4", "lockfileVersion": 2, "requires": true, "packages": { "": { "name": "redis", - "version": "4.0.3", + "version": "4.0.4", "license": "MIT", "workspaces": [ "./packages/*" diff --git a/package.json b/package.json index 9c2b6bca207..c1b4e3bd8f1 100644 --- a/package.json +++ b/package.json @@ -1,7 +1,7 @@ { "name": "redis", "description": "A modern, high performance Redis client", - "version": "4.0.3", + "version": "4.0.4", "license": "MIT", "main": "./dist/index.js", "types": "./dist/index.d.ts", From ac7d50c7313fe1158131e13b0e3b11d982697123 Mon Sep 17 00:00:00 2001 From: Simon Prickett Date: Thu, 24 Feb 2022 23:04:06 +0000 Subject: [PATCH 1153/1748] Added sorted set example. (#2005) * Added sorted set example. * Update sorted-set.js Co-authored-by: Leibale Eidelman --- examples/README.md | 1 + examples/sorted-set.js | 35 +++++++++++++++++++++++++++++++++++ 2 files changed, 36 insertions(+) create mode 100644 examples/sorted-set.js diff --git a/examples/README.md b/examples/README.md index c3a54c1102a..eb9a0e4325f 100644 --- a/examples/README.md +++ b/examples/README.md @@ -15,6 +15,7 @@ This folder contains example scripts showing how to use Node Redis in different | `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 | +| `sorted-set.js` | Add members with scores to a Sorted Set and retrieve them using the ZSCAN iteractor functionality | | `stream-producer.js` | Adds entries to a [Redis Stream](https://redis.io/topics/streams-intro) using the `XADD` command | | `stream-consumer.js` | Reads entries from a [Redis Stream](https://redis.io/topics/streams-intro) using the blocking `XREAD` command | | `time-series.js` | Create, populate and query timeseries data with [Redis Timeseries](https://redistimeseries.io) | diff --git a/examples/sorted-set.js b/examples/sorted-set.js new file mode 100644 index 00000000000..e2c4f145116 --- /dev/null +++ b/examples/sorted-set.js @@ -0,0 +1,35 @@ +// Add several values with their scores to a Sorted Set, +// then retrieve them all using ZSCAN. + +import { createClient } from 'redis'; + +async function addToSortedSet() { + const client = createClient(); + await client.connect(); + + await client.zAdd('mysortedset', [ + { + score: 99, + value: 'Ninety Nine' + }, + { + score: 100, + value: 'One Hundred' + }, + { + score: 101, + value: 'One Hundred and One' + } + ]); + + // Get all of the values/scores from the sorted set using + // the scan approach: + // https://redis.io/commands/zscan + for await (const memberWithScore of client.zScanIterator('mysortedset')) { + console.log(memberWithScore); + } + + await client.quit(); +} + +addToSortedSet(); From 9180b9104751ed5128a62034d1e0e1152b7414c1 Mon Sep 17 00:00:00 2001 From: Leibale Eidelman Date: Wed, 2 Mar 2022 05:29:42 -0500 Subject: [PATCH 1154/1748] fix #1906 - implement BITFIELD_RO (#1988) * fix #1906 - implement BITFIELD_RO * set bitfield_ro min version to 6.2 --- packages/client/lib/cluster/commands.ts | 3 +++ packages/client/lib/commands/BITFIELD.spec.ts | 14 ++++++---- packages/client/lib/commands/BITFIELD.ts | 20 +++++++------- .../client/lib/commands/BITFIELD_RO.spec.ts | 27 +++++++++++++++++++ packages/client/lib/commands/BITFIELD_RO.ts | 26 ++++++++++++++++++ 5 files changed, 74 insertions(+), 16 deletions(-) create mode 100644 packages/client/lib/commands/BITFIELD_RO.spec.ts create mode 100644 packages/client/lib/commands/BITFIELD_RO.ts diff --git a/packages/client/lib/cluster/commands.ts b/packages/client/lib/cluster/commands.ts index 4b2aba6a7ae..b8893d9710e 100644 --- a/packages/client/lib/cluster/commands.ts +++ b/packages/client/lib/cluster/commands.ts @@ -1,6 +1,7 @@ import * as APPEND from '../commands/APPEND'; import * as BITCOUNT from '../commands/BITCOUNT'; +import * as BITFIELD_RO from '../commands/BITFIELD_RO'; import * as BITFIELD from '../commands/BITFIELD'; import * as BITOP from '../commands/BITOP'; import * as BITPOS from '../commands/BITPOS'; @@ -181,6 +182,8 @@ export default { append: APPEND, BITCOUNT, bitCount: BITCOUNT, + BITFIELD_RO, + bitFieldRo: BITFIELD_RO, BITFIELD, bitField: BITFIELD, BITOP, diff --git a/packages/client/lib/commands/BITFIELD.spec.ts b/packages/client/lib/commands/BITFIELD.spec.ts index 93a5cb08a63..aaf0f93e501 100644 --- a/packages/client/lib/commands/BITFIELD.spec.ts +++ b/packages/client/lib/commands/BITFIELD.spec.ts @@ -10,14 +10,14 @@ describe('BITFIELD', () => { behavior: 'WRAP' }, { operation: 'GET', - type: 'i8', + encoding: 'i8', offset: 0 }, { operation: 'OVERFLOW', behavior: 'SAT' }, { operation: 'SET', - type: 'i16', + encoding: 'i16', offset: 1, value: 0 }, { @@ -25,7 +25,7 @@ describe('BITFIELD', () => { behavior: 'FAIL' }, { operation: 'INCRBY', - type: 'i32', + encoding: 'i32', offset: 2, increment: 1 }]), @@ -35,8 +35,12 @@ describe('BITFIELD', () => { testUtils.testWithClient('client.bitField', async client => { assert.deepEqual( - await client.bitField('key', []), - [] + await client.bitField('key', [{ + operation: 'GET', + encoding: 'i8', + offset: 0 + }]), + [0] ); }, GLOBAL.SERVERS.OPEN); }); diff --git a/packages/client/lib/commands/BITFIELD.ts b/packages/client/lib/commands/BITFIELD.ts index 5be12cc391e..6a477b89f01 100644 --- a/packages/client/lib/commands/BITFIELD.ts +++ b/packages/client/lib/commands/BITFIELD.ts @@ -1,26 +1,24 @@ export const FIRST_KEY_INDEX = 1; -export const IS_READ_ONLY = true; +export type BitFieldEncoding = `${'i' | 'u'}${number}`; -type BitFieldType = string; // TODO 'i[1-64]' | 'u[1-63]' - -interface BitFieldOperation { +export interface BitFieldOperation { operation: S; } -interface BitFieldGetOperation extends BitFieldOperation<'GET'> { - type: BitFieldType; +export interface BitFieldGetOperation extends BitFieldOperation<'GET'> { + encoding: BitFieldEncoding; offset: number | string; } interface BitFieldSetOperation extends BitFieldOperation<'SET'> { - type: BitFieldType; + encoding: BitFieldEncoding; offset: number | string; value: number; } interface BitFieldIncrByOperation extends BitFieldOperation<'INCRBY'> { - type: BitFieldType; + encoding: BitFieldEncoding; offset: number | string; increment: number; } @@ -44,7 +42,7 @@ export function transformArguments(key: string, operations: BitFieldOperations): case 'GET': args.push( 'GET', - options.type, + options.encoding, options.offset.toString() ); break; @@ -52,7 +50,7 @@ export function transformArguments(key: string, operations: BitFieldOperations): case 'SET': args.push( 'SET', - options.type, + options.encoding, options.offset.toString(), options.value.toString() ); @@ -61,7 +59,7 @@ export function transformArguments(key: string, operations: BitFieldOperations): case 'INCRBY': args.push( 'INCRBY', - options.type, + options.encoding, options.offset.toString(), options.increment.toString() ); diff --git a/packages/client/lib/commands/BITFIELD_RO.spec.ts b/packages/client/lib/commands/BITFIELD_RO.spec.ts new file mode 100644 index 00000000000..98399d5f235 --- /dev/null +++ b/packages/client/lib/commands/BITFIELD_RO.spec.ts @@ -0,0 +1,27 @@ +import { strict as assert } from 'assert'; +import testUtils, { GLOBAL } from '../test-utils'; +import { transformArguments } from './BITFIELD_RO'; + +describe('BITFIELD RO', () => { + testUtils.isVersionGreaterThanHook([6, 2]); + + it('transformArguments', () => { + assert.deepEqual( + transformArguments('key', [{ + encoding: 'i8', + offset: 0 + }]), + ['BITFIELD_RO', 'key', 'GET', 'i8', '0'] + ); + }); + + testUtils.testWithClient('client.bitFieldRo', async client => { + assert.deepEqual( + await client.bitFieldRo('key', [{ + encoding: 'i8', + offset: 0 + }]), + [0] + ); + }, GLOBAL.SERVERS.OPEN); +}); diff --git a/packages/client/lib/commands/BITFIELD_RO.ts b/packages/client/lib/commands/BITFIELD_RO.ts new file mode 100644 index 00000000000..efd4eac188e --- /dev/null +++ b/packages/client/lib/commands/BITFIELD_RO.ts @@ -0,0 +1,26 @@ +import { BitFieldGetOperation } from './BITFIELD'; + +export const FIRST_KEY_INDEX = 1; + +export const IS_READ_ONLY = true; + +type BitFieldRoOperations = Array< + Omit & + Partial> +>; + +export function transformArguments(key: string, operations: BitFieldRoOperations): Array { + const args = ['BITFIELD_RO', key]; + + for (const operation of operations) { + args.push( + 'GET', + operation.encoding, + operation.offset.toString() + ); + } + + return args; +} + +export declare function transformReply(): Array; From 88586048e1ccb28ea4eb9af0722801415644f7de Mon Sep 17 00:00:00 2001 From: Simon Prickett Date: Mon, 7 Mar 2022 12:20:19 +0000 Subject: [PATCH 1155/1748] Fixes the time command response. (#2008) * Fixes the time command response. * Adds TIME example. * Update TIME.ts * Update get-server-time.js Co-authored-by: Leibale Eidelman --- examples/README.md | 1 + examples/get-server-time.js | 16 ++++++++++++++++ packages/client/lib/commands/TIME.ts | 2 +- 3 files changed, 18 insertions(+), 1 deletion(-) create mode 100644 examples/get-server-time.js diff --git a/examples/README.md b/examples/README.md index eb9a0e4325f..a026bc0b1c8 100644 --- a/examples/README.md +++ b/examples/README.md @@ -10,6 +10,7 @@ This folder contains example scripts showing how to use Node Redis in different | `connect-as-acl-user.js` | Connect to Redis 6 using an ACL user | | `count-min-sketch.js` | Estimate the frequency of a given event using the [RedisBloom](https://redisbloom.io) Count-Min Sketch | | `cuckoo-filter.js` | Space efficient set membership checks with a [Cuckoo Filter](https://en.wikipedia.org/wiki/Cuckoo_filter) using [RedisBloom](https://redisbloom.io) | +| `get-server-time.js` | Get the time from the Redis server | | `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 | diff --git a/examples/get-server-time.js b/examples/get-server-time.js new file mode 100644 index 00000000000..c6b7956c831 --- /dev/null +++ b/examples/get-server-time.js @@ -0,0 +1,16 @@ +// Get the time from the Redis Server. + +import { createClient } from 'redis'; + +async function getServerTime() { + const client = createClient(); + await client.connect(); + + const serverTime = await client.time(); + // 2022-02-25T12:57:40.000Z { microseconds: 351346 } + console.log(serverTime); + + await client.quit(); +} + +getServerTime(); diff --git a/packages/client/lib/commands/TIME.ts b/packages/client/lib/commands/TIME.ts index 4579845339e..1a364d6d8be 100644 --- a/packages/client/lib/commands/TIME.ts +++ b/packages/client/lib/commands/TIME.ts @@ -9,7 +9,7 @@ interface TimeReply extends Date { export function transformReply(reply: [string, string]): TimeReply { const seconds = Number(reply[0]), microseconds = Number(reply[1]), - d: Partial = new Date(seconds + Math.round(microseconds / 1000)); + d: Partial = new Date(seconds * 1000 + microseconds / 1000); d.microseconds = microseconds; return d as TimeReply; } From f79e14c8c3ed672fa2a4a42cbbf6e9e8db29829d Mon Sep 17 00:00:00 2001 From: John Hiesey Date: Mon, 7 Mar 2022 04:27:04 -0800 Subject: [PATCH 1156/1748] Fix #1870: TypeError in PubSub (#2016) * Fix #1870: TypeError in PubSub * Add test This test is for a race condition; it should reliably pass now, but is not guaranteed to fail with the previous version. --- packages/client/lib/client/commands-queue.ts | 3 ++- packages/client/lib/client/index.spec.ts | 15 +++++++++++++++ 2 files changed, 17 insertions(+), 1 deletion(-) diff --git a/packages/client/lib/client/commands-queue.ts b/packages/client/lib/client/commands-queue.ts index df2c25dbc2c..8af200314b8 100644 --- a/packages/client/lib/client/commands-queue.ts +++ b/packages/client/lib/client/commands-queue.ts @@ -369,7 +369,8 @@ export default class RedisCommandsQueue { #setReturnBuffers() { this.#parser.setReturnBuffers( !!this.#waitingForReply.head?.value.returnBuffers || - !!this.#pubSubState?.subscribed + !!this.#pubSubState?.subscribed || + !!this.#pubSubState?.subscribing ); } diff --git a/packages/client/lib/client/index.spec.ts b/packages/client/lib/client/index.spec.ts index 1d627756c6d..d52086da412 100644 --- a/packages/client/lib/client/index.spec.ts +++ b/packages/client/lib/client/index.spec.ts @@ -683,6 +683,21 @@ describe('Client', () => { } }, GLOBAL.SERVERS.OPEN); + testUtils.testWithClient('should not fail when message arrives right after subscribe', async publisher => { + const subscriber = publisher.duplicate(); + + await subscriber.connect(); + + try { + await assert.doesNotReject(Promise.all([ + subscriber.subscribe('channel', () => {}), + publisher.publish('channel', 'message') + ])); + } finally { + await subscriber.disconnect(); + } + }, GLOBAL.SERVERS.OPEN); + testUtils.testWithClient('should be able to quit in PubSub mode', async client => { await client.subscribe('channel', () => { // noop From c57da8b78bca5f6d3bb76d2c7baf5aafc5bbde4d Mon Sep 17 00:00:00 2001 From: Leibale Eidelman Date: Tue, 8 Mar 2022 17:26:13 -0500 Subject: [PATCH 1157/1748] add missing FIRST_KEY_INDEX (#2028) --- packages/client/lib/commands/DEL.ts | 2 ++ packages/client/lib/commands/DUMP.ts | 2 ++ packages/client/lib/commands/GEOADD.ts | 4 ++-- packages/client/lib/commands/HSET.ts | 4 ++-- packages/client/lib/commands/LINDEX.ts | 2 ++ packages/client/lib/commands/LMOVE.ts | 4 ++-- packages/client/lib/commands/MOVE.ts | 2 ++ packages/client/lib/commands/SORT.ts | 3 +-- packages/client/lib/commands/WATCH.ts | 2 ++ 9 files changed, 17 insertions(+), 8 deletions(-) diff --git a/packages/client/lib/commands/DEL.ts b/packages/client/lib/commands/DEL.ts index 7597cf09cb0..d60abe0f28e 100644 --- a/packages/client/lib/commands/DEL.ts +++ b/packages/client/lib/commands/DEL.ts @@ -1,6 +1,8 @@ import { RedisCommandArgument, RedisCommandArguments } from '.'; import { pushVerdictArguments } from './generic-transformers'; +export const FIRST_KEY_INDEX = 1; + export function transformArguments( keys: RedisCommandArgument | Array ): RedisCommandArguments { diff --git a/packages/client/lib/commands/DUMP.ts b/packages/client/lib/commands/DUMP.ts index 79805795ed9..fd4354db45c 100644 --- a/packages/client/lib/commands/DUMP.ts +++ b/packages/client/lib/commands/DUMP.ts @@ -1,5 +1,7 @@ import { RedisCommandArgument, RedisCommandArguments } from '.'; +export const FIRST_KEY_INDEX = 1; + export function transformArguments(key: RedisCommandArgument): RedisCommandArguments { return ['DUMP', key]; } diff --git a/packages/client/lib/commands/GEOADD.ts b/packages/client/lib/commands/GEOADD.ts index 74adeda5142..daccb0842e0 100644 --- a/packages/client/lib/commands/GEOADD.ts +++ b/packages/client/lib/commands/GEOADD.ts @@ -1,6 +1,8 @@ import { RedisCommandArgument, RedisCommandArguments } from '.'; import { GeoCoordinates } from './generic-transformers'; +export const FIRST_KEY_INDEX = 1; + interface GeoMember extends GeoCoordinates { member: RedisCommandArgument; } @@ -21,8 +23,6 @@ interface GeoAddCommonOptions { type GeoAddOptions = SetGuards & GeoAddCommonOptions; -export const FIRST_KEY_INDEX = 1; - export function transformArguments( key: RedisCommandArgument, toAdd: GeoMember | Array, options?: GeoAddOptions diff --git a/packages/client/lib/commands/HSET.ts b/packages/client/lib/commands/HSET.ts index 81bde83d220..1fe1743b6a2 100644 --- a/packages/client/lib/commands/HSET.ts +++ b/packages/client/lib/commands/HSET.ts @@ -1,5 +1,7 @@ import { RedisCommandArgument, RedisCommandArguments } from '.'; +export const FIRST_KEY_INDEX = 1; + type Types = RedisCommandArgument | number; type HSETObject = Record; @@ -8,8 +10,6 @@ type HSETMap = Map; type HSETTuples = Array<[Types, Types]> | Array; -export const FIRST_KEY_INDEX = 1; - type GenericArguments = [key: RedisCommandArgument]; type SingleFieldArguments = [...generic: GenericArguments, field: Types, value: Types]; diff --git a/packages/client/lib/commands/LINDEX.ts b/packages/client/lib/commands/LINDEX.ts index bb657de3cb8..8e74ad8aae6 100644 --- a/packages/client/lib/commands/LINDEX.ts +++ b/packages/client/lib/commands/LINDEX.ts @@ -1,5 +1,7 @@ import { RedisCommandArgument, RedisCommandArguments } from '.'; +export const FIRST_KEY_INDEX = 1; + export const IS_READ_ONLY = true; export function transformArguments( diff --git a/packages/client/lib/commands/LMOVE.ts b/packages/client/lib/commands/LMOVE.ts index 96946722ef5..7332d1a0075 100644 --- a/packages/client/lib/commands/LMOVE.ts +++ b/packages/client/lib/commands/LMOVE.ts @@ -1,9 +1,9 @@ import { RedisCommandArgument, RedisCommandArguments } from '.'; -export type LMoveSide = 'LEFT' | 'RIGHT'; - export const FIRST_KEY_INDEX = 1; +export type LMoveSide = 'LEFT' | 'RIGHT'; + export function transformArguments( source: RedisCommandArgument, destination: RedisCommandArgument, diff --git a/packages/client/lib/commands/MOVE.ts b/packages/client/lib/commands/MOVE.ts index f446fd18dce..17cc6742c5f 100644 --- a/packages/client/lib/commands/MOVE.ts +++ b/packages/client/lib/commands/MOVE.ts @@ -1,3 +1,5 @@ +export const FIRST_KEY_INDEX = 1; + export function transformArguments(key: string, db: number): Array { return ['MOVE', key, db.toString()]; } diff --git a/packages/client/lib/commands/SORT.ts b/packages/client/lib/commands/SORT.ts index 0305d93144c..dfa38dc7569 100644 --- a/packages/client/lib/commands/SORT.ts +++ b/packages/client/lib/commands/SORT.ts @@ -2,7 +2,6 @@ export const FIRST_KEY_INDEX = 1; export const IS_READ_ONLY = true; - interface SortOptions { BY?: string; LIMIT?: { @@ -25,7 +24,7 @@ export function transformArguments(key: string, options?: SortOptions): Array): RedisCommandArguments { return pushVerdictArguments(['WATCH'], key); } From bae54f19bef89aac73e4041cda63e39dacbe4f50 Mon Sep 17 00:00:00 2001 From: Simon Prickett Date: Thu, 10 Mar 2022 16:22:22 +0000 Subject: [PATCH 1158/1748] Adds example of how to escape punctuation when searching. (#2027) * Adds example of how to escape punctuation when searching. * Optimized regex. --- examples/search-json.js | 100 +++++++++++++++++++++++++++++++--------- 1 file changed, 77 insertions(+), 23 deletions(-) diff --git a/examples/search-json.js b/examples/search-json.js index a608d3aefa5..a0a29c69296 100644 --- a/examples/search-json.js +++ b/examples/search-json.js @@ -21,6 +21,10 @@ async function searchJSON() { '$.coins': { type: SchemaFieldTypes.NUMERIC, AS: 'coins' + }, + '$.email': { + type: SchemaFieldTypes.TAG, + AS: 'email' } }, { ON: 'JSON', @@ -41,12 +45,14 @@ async function searchJSON() { client.json.set('noderedis:users:1', '$', { name: 'Alice', age: 32, - coins: 100 + coins: 100, + email: 'alice@nonexist.com' }), client.json.set('noderedis:users:2', '$', { name: 'Bob', age: 23, - coins: 15 + coins: 15, + email: 'bob@somewhere.gov' }) ]); @@ -54,37 +60,85 @@ async function searchJSON() { 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]') + JSON.stringify( + await client.ft.search('idx:users', '@age:[0 30]'), + null, + 2 + ) ); // { - // total: 1, - // documents: [ { id: 'noderedis:users:2', value: [Object] } ] + // "total": 1, + // "documents": [ + // { + // "id": "noderedis:users:2", + // "value": { + // "name": "Bob", + // "age": 23, + // "coins": 15, + // "email": "bob@somewhere.gov" + // } + // } + // ] + // } + + // Find a user by email - note we need to escape . and @ characters + // in the email address. This applies for other punctuation too. + // https://oss.redis.com/redisearch/Tags/#including_punctuation_in_tags + console.log('Users with email "bob@somewhere.gov":'); + const emailAddress = 'bob@somewhere.gov'.replace(/[.@]/g, '\\$&'); + console.log( + JSON.stringify( + await client.ft.search('idx:users', `@email:{${emailAddress}}`), + null, + 2 + ) + ); + // { + // "total": 1, + // "documents": [ + // { + // "id": "noderedis:users:2", + // "value": { + // "name": "Bob", + // "age": 23, + // "coins": 15, + // "email": "bob@somewhere.gov" + // } + // } + // ] // } // Some aggregrations, what's the average age and total number of coins... // https://oss.redis.com/redisearch/Commands/#ftaggregate + console.log('Aggregation Demo:'); 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' + JSON.stringify( + 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' + }] }] - }] - }) + }), + null, + 2 + ) ); // { - // total: 2, - // results: [{ - // averageAge: '27.5', - // totalCoins: '115' - // }] + // "total": 1, + // "results": [ + // { + // "averageAge": "27.5", + // "totalCoins": "115" + // } + // ] // } await client.quit(); From be51abe3470285548a8af8b02a83a870f2094df5 Mon Sep 17 00:00:00 2001 From: Avital Fine <98389525+Avital-Fine@users.noreply.github.com> Date: Thu, 10 Mar 2022 18:20:01 +0100 Subject: [PATCH 1159/1748] Support COMMAND LIST (#2013) * Support COMMAND LIST * Update COMMAND_LIST.spec.ts * add version check * clean code Co-authored-by: leibale --- packages/client/lib/client/commands.ts | 3 + .../client/lib/commands/COMMAND_LIST.spec.ts | 56 +++++++++++++++++++ packages/client/lib/commands/COMMAND_LIST.ts | 31 ++++++++++ 3 files changed, 90 insertions(+) create mode 100644 packages/client/lib/commands/COMMAND_LIST.spec.ts create mode 100644 packages/client/lib/commands/COMMAND_LIST.ts diff --git a/packages/client/lib/client/commands.ts b/packages/client/lib/client/commands.ts index 029a5e60f62..0d9f63b003e 100644 --- a/packages/client/lib/client/commands.ts +++ b/packages/client/lib/client/commands.ts @@ -33,6 +33,7 @@ import * as CLUSTER_SLOTS from '../commands/CLUSTER_SLOTS'; import * as COMMAND_COUNT from '../commands/COMMAND_COUNT'; import * as COMMAND_GETKEYS from '../commands/COMMAND_GETKEYS'; import * as COMMAND_INFO from '../commands/COMMAND_INFO'; +import * as COMMAND_LIST from '../commands/COMMAND_LIST'; import * as COMMAND from '../commands/COMMAND'; import * as CONFIG_GET from '../commands/CONFIG_GET'; import * as CONFIG_RESETASTAT from '../commands/CONFIG_RESETSTAT'; @@ -151,6 +152,8 @@ export default { commandGetKeys: COMMAND_GETKEYS, COMMAND_INFO, commandInfo: COMMAND_INFO, + COMMAND_LIST, + commandList: COMMAND_LIST, COMMAND, command: COMMAND, CONFIG_GET, diff --git a/packages/client/lib/commands/COMMAND_LIST.spec.ts b/packages/client/lib/commands/COMMAND_LIST.spec.ts new file mode 100644 index 00000000000..ac4fee20c71 --- /dev/null +++ b/packages/client/lib/commands/COMMAND_LIST.spec.ts @@ -0,0 +1,56 @@ +import { strict as assert } from 'assert'; +import testUtils, { GLOBAL } from '../test-utils'; +import { transformArguments, FilterBy } from './COMMAND_LIST'; + +describe('COMMAND LIST', () => { + testUtils.isVersionGreaterThanHook([7, 0]); + + describe('transformArguments', () => { + it('simple', () => { + assert.deepEqual( + transformArguments(), + ['COMMAND', 'LIST'] + ); + }); + + describe('with FILTERBY', () => { + it('MODULE', () => { + assert.deepEqual( + transformArguments({ + filterBy: FilterBy.MODULE, + value: 'json' + }), + ['COMMAND', 'LIST', 'FILTERBY', 'MODULE', 'json'] + ); + }); + + it('ACLCAT', () => { + assert.deepEqual( + transformArguments({ + filterBy: FilterBy.ACLCAT, + value: 'admin' + }), + ['COMMAND', 'LIST', 'FILTERBY', 'ACLCAT', 'admin'] + ); + }); + + it('PATTERN', () => { + assert.deepEqual( + transformArguments({ + filterBy: FilterBy.PATTERN, + value: 'a*' + }), + ['COMMAND', 'LIST', 'FILTERBY', 'PATTERN', 'a*'] + ); + }); + }); + }); + + testUtils.testWithClient('client.commandList', async client => { + const commandList = await client.commandList(); + assert.ok(Array.isArray(commandList)); + for (const command of commandList) { + assert.ok(typeof command === 'string'); + } + }, GLOBAL.SERVERS.OPEN); +}); diff --git a/packages/client/lib/commands/COMMAND_LIST.ts b/packages/client/lib/commands/COMMAND_LIST.ts new file mode 100644 index 00000000000..a197bd1a4c6 --- /dev/null +++ b/packages/client/lib/commands/COMMAND_LIST.ts @@ -0,0 +1,31 @@ +import { RedisCommandArguments } from '.'; + +export const IS_READ_ONLY = true; + +export enum FilterBy { + MODULE = 'MODULE', + ACLCAT = 'ACLCAT', + PATTERN = 'PATTERN' +} + +interface Filter { + filterBy: FilterBy; + value: string; +} + + +export function transformArguments(filter?: Filter): RedisCommandArguments { + const args = ['COMMAND', 'LIST']; + + if (filter) { + args.push( + 'FILTERBY', + filter.filterBy, + filter.value + ); + } + + return args; +} + +export declare function transformReply(): Array; From 875298e6e361d1522c5269b30f756451b2975084 Mon Sep 17 00:00:00 2001 From: Avital Fine <98389525+Avital-Fine@users.noreply.github.com> Date: Sun, 20 Mar 2022 18:40:19 +0100 Subject: [PATCH 1160/1748] Support ZINTERCARD and SINTERCARD (#2040) * Support ZINTERCARD and SINTERCARD * clean code * clean code Co-authored-by: leibale --- packages/client/lib/cluster/commands.ts | 6 ++++ .../client/lib/commands/SINTERCARD.spec.ts | 30 +++++++++++++++++++ packages/client/lib/commands/SINTERCARD.ts | 21 +++++++++++++ .../client/lib/commands/ZINTERCARD.spec.ts | 30 +++++++++++++++++++ packages/client/lib/commands/ZINTERCARD.ts | 21 +++++++++++++ 5 files changed, 108 insertions(+) create mode 100644 packages/client/lib/commands/SINTERCARD.spec.ts create mode 100644 packages/client/lib/commands/SINTERCARD.ts create mode 100644 packages/client/lib/commands/ZINTERCARD.spec.ts create mode 100644 packages/client/lib/commands/ZINTERCARD.ts diff --git a/packages/client/lib/cluster/commands.ts b/packages/client/lib/cluster/commands.ts index b8893d9710e..bbfe237b094 100644 --- a/packages/client/lib/cluster/commands.ts +++ b/packages/client/lib/cluster/commands.ts @@ -98,6 +98,7 @@ import * as SETEX from '../commands/SETEX'; import * as SETNX from '../commands/SETNX'; import * as SETRANGE from '../commands/SETRANGE'; import * as SINTER from '../commands/SINTER'; +import * as SINTERCARD from '../commands/SINTERCARD'; import * as SINTERSTORE from '../commands/SINTERSTORE'; import * as SISMEMBER from '../commands/SISMEMBER'; import * as SMEMBERS from '../commands/SMEMBERS'; @@ -149,6 +150,7 @@ import * as ZDIFFSTORE from '../commands/ZDIFFSTORE'; import * as ZINCRBY from '../commands/ZINCRBY'; import * as ZINTER_WITHSCORES from '../commands/ZINTER_WITHSCORES'; import * as ZINTER from '../commands/ZINTER'; +import * as ZINTERCARD from '../commands/ZINTERCARD'; import * as ZINTERSTORE from '../commands/ZINTERSTORE'; import * as ZLEXCOUNT from '../commands/ZLEXCOUNT'; import * as ZMSCORE from '../commands/ZMSCORE'; @@ -366,6 +368,8 @@ export default { sDiffStore: SDIFFSTORE, SINTER, sInter: SINTER, + SINTERCARD, + sInterCard: SINTERCARD, SINTERSTORE, sInterStore: SINTERSTORE, SET, @@ -478,6 +482,8 @@ export default { zInterWithScores: ZINTER_WITHSCORES, ZINTER, zInter: ZINTER, + ZINTERCARD, + zInterCard: ZINTERCARD, ZINTERSTORE, zInterStore: ZINTERSTORE, ZLEXCOUNT, diff --git a/packages/client/lib/commands/SINTERCARD.spec.ts b/packages/client/lib/commands/SINTERCARD.spec.ts new file mode 100644 index 00000000000..24bd6cb8081 --- /dev/null +++ b/packages/client/lib/commands/SINTERCARD.spec.ts @@ -0,0 +1,30 @@ +import { strict as assert } from 'assert'; +import testUtils, { GLOBAL } from '../test-utils'; +import { transformArguments } from './SINTERCARD'; + +describe('SINTERCARD', () => { + testUtils.isVersionGreaterThanHook([7, 0]); + + describe('transformArguments', () => { + it('simple', () => { + assert.deepEqual( + transformArguments(['1', '2']), + ['SINTERCARD', '2', '1', '2'] + ); + }); + + it('with limit', () => { + assert.deepEqual( + transformArguments(['1', '2'], 1), + ['SINTERCARD', '2', '1', '2', 'LIMIT', '1'] + ); + }); + }); + + testUtils.testWithClient('client.sInterCard', async client => { + assert.deepEqual( + await client.sInterCard('key'), + 0 + ); + }, GLOBAL.SERVERS.OPEN); +}); diff --git a/packages/client/lib/commands/SINTERCARD.ts b/packages/client/lib/commands/SINTERCARD.ts new file mode 100644 index 00000000000..ddb7e5b00ef --- /dev/null +++ b/packages/client/lib/commands/SINTERCARD.ts @@ -0,0 +1,21 @@ +import { RedisCommandArgument, RedisCommandArguments } from '.'; +import { pushVerdictArgument } from './generic-transformers'; + +export const FIRST_KEY_INDEX = 2; + +export const IS_READ_ONLY = true; + +export function transformArguments( + keys: Array | RedisCommandArgument, + limit?: number +): RedisCommandArguments { + const args = pushVerdictArgument(['SINTERCARD'], keys); + + if (limit) { + args.push('LIMIT', limit.toString()); + } + + return args; +} + +export declare function transformReply(): number; diff --git a/packages/client/lib/commands/ZINTERCARD.spec.ts b/packages/client/lib/commands/ZINTERCARD.spec.ts new file mode 100644 index 00000000000..a2b70616d59 --- /dev/null +++ b/packages/client/lib/commands/ZINTERCARD.spec.ts @@ -0,0 +1,30 @@ +import { strict as assert } from 'assert'; +import testUtils, { GLOBAL } from '../test-utils'; +import { transformArguments } from './ZINTERCARD'; + +describe('ZINTERCARD', () => { + testUtils.isVersionGreaterThanHook([7, 0]); + + describe('transformArguments', () => { + it('simple', () => { + assert.deepEqual( + transformArguments(['1', '2']), + ['ZINTERCARD', '2', '1', '2'] + ); + }); + + it('with limit', () => { + assert.deepEqual( + transformArguments(['1', '2'], 1), + ['ZINTERCARD', '2', '1', '2', 'LIMIT', '1'] + ); + }); + }); + + testUtils.testWithClient('client.zInterCard', async client => { + assert.deepEqual( + await client.zInterCard('key'), + 0 + ); + }, GLOBAL.SERVERS.OPEN); +}); diff --git a/packages/client/lib/commands/ZINTERCARD.ts b/packages/client/lib/commands/ZINTERCARD.ts new file mode 100644 index 00000000000..ff45ab2aa01 --- /dev/null +++ b/packages/client/lib/commands/ZINTERCARD.ts @@ -0,0 +1,21 @@ +import { RedisCommandArgument, RedisCommandArguments } from '.'; +import { pushVerdictArgument } from './generic-transformers'; + +export const FIRST_KEY_INDEX = 2; + +export const IS_READ_ONLY = true; + +export function transformArguments( + keys: Array | RedisCommandArgument, + limit?: number +): RedisCommandArguments { + const args = pushVerdictArgument(['ZINTERCARD'], keys); + + if (limit) { + args.push('LIMIT', limit.toString()); + } + + return args; +} + +export declare function transformReply(): number; From c5c2bf9042c8e83147380b1bf4716f15b6046361 Mon Sep 17 00:00:00 2001 From: Avital Fine <98389525+Avital-Fine@users.noreply.github.com> Date: Sun, 20 Mar 2022 18:40:27 +0100 Subject: [PATCH 1161/1748] Support multiple parametrs on CONFIG SET (#2042) * support multiple parametrs on CONFIG SET * clean code Co-authored-by: leibale --- .../client/lib/commands/CONFIG_SET.spec.ts | 23 +++++++++++++++---- packages/client/lib/commands/CONFIG_SET.ts | 22 ++++++++++++++++-- 2 files changed, 38 insertions(+), 7 deletions(-) diff --git a/packages/client/lib/commands/CONFIG_SET.spec.ts b/packages/client/lib/commands/CONFIG_SET.spec.ts index 3127e9732bf..93a7a6ff25e 100644 --- a/packages/client/lib/commands/CONFIG_SET.spec.ts +++ b/packages/client/lib/commands/CONFIG_SET.spec.ts @@ -2,10 +2,23 @@ import { strict as assert } from 'assert'; import { transformArguments } from './CONFIG_SET'; describe('CONFIG SET', () => { - it('transformArguments', () => { - assert.deepEqual( - transformArguments('parameter', 'value'), - ['CONFIG', 'SET', 'parameter', 'value'] - ); + describe('transformArguments', () => { + it('set one parameter (old version)', () => { + assert.deepEqual( + transformArguments('parameter', 'value'), + ['CONFIG', 'SET', 'parameter', 'value'] + ); + }); + + it('set muiltiple parameters', () => { + assert.deepEqual( + transformArguments({ + 1: 'a', + 2: 'b', + 3: 'c' + }), + ['CONFIG', 'SET', '1', 'a', '2', 'b', '3', 'c'] + ); + }); }); }); diff --git a/packages/client/lib/commands/CONFIG_SET.ts b/packages/client/lib/commands/CONFIG_SET.ts index ec09e4469fa..41f40d035d2 100644 --- a/packages/client/lib/commands/CONFIG_SET.ts +++ b/packages/client/lib/commands/CONFIG_SET.ts @@ -1,5 +1,23 @@ -export function transformArguments(parameter: string, value: string): Array { - return ['CONFIG', 'SET', parameter, value]; +import { RedisCommandArgument, RedisCommandArguments } from '.'; + +type SingleParameter = [parameter: RedisCommandArgument, value: RedisCommandArgument]; + +type MultipleParameters = [config: Record]; + +export function transformArguments( + ...[parameterOrConfig, value]: SingleParameter | MultipleParameters +): RedisCommandArguments { + const args: RedisCommandArguments = ['CONFIG', 'SET']; + + if (typeof parameterOrConfig === 'string') { + args.push(parameterOrConfig, value!); + } else { + for (const [key, value] of Object.entries(parameterOrConfig)) { + args.push(key, value); + } + } + + return args; } export declare function transformReply(): string; From 24cd9ba9a41feca43da7dea4ff8639e2cc6c223b Mon Sep 17 00:00:00 2001 From: Avital Fine <98389525+Avital-Fine@users.noreply.github.com> Date: Thu, 24 Mar 2022 15:17:01 +0100 Subject: [PATCH 1162/1748] Support new expire features (#2036) * Support new expire features * Update PEXPIRETIME.ts * Update EXPIRETIME.ts * fix version skip * clean code Co-authored-by: leibale --- packages/client/lib/cluster/commands.ts | 6 ++++++ packages/client/lib/commands/EXPIRE.spec.ts | 19 ++++++++++++----- packages/client/lib/commands/EXPIRE.ts | 11 ++++++++-- packages/client/lib/commands/EXPIREAT.spec.ts | 7 +++++++ packages/client/lib/commands/EXPIREAT.ts | 11 ++++++++-- .../client/lib/commands/EXPIRETIME.spec.ts | 21 +++++++++++++++++++ packages/client/lib/commands/EXPIRETIME.ts | 9 ++++++++ packages/client/lib/commands/PEXPIRE.spec.ts | 19 ++++++++++++----- packages/client/lib/commands/PEXPIRE.ts | 11 ++++++++-- .../client/lib/commands/PEXPIREAT.spec.ts | 7 +++++++ packages/client/lib/commands/PEXPIREAT.ts | 11 ++++++++-- .../client/lib/commands/PEXPIRETIME.spec.ts | 21 +++++++++++++++++++ packages/client/lib/commands/PEXPIRETIME.ts | 9 ++++++++ 13 files changed, 144 insertions(+), 18 deletions(-) create mode 100644 packages/client/lib/commands/EXPIRETIME.spec.ts create mode 100644 packages/client/lib/commands/EXPIRETIME.ts create mode 100644 packages/client/lib/commands/PEXPIRETIME.spec.ts create mode 100644 packages/client/lib/commands/PEXPIRETIME.ts diff --git a/packages/client/lib/cluster/commands.ts b/packages/client/lib/cluster/commands.ts index bbfe237b094..a43a6a38dc7 100644 --- a/packages/client/lib/cluster/commands.ts +++ b/packages/client/lib/cluster/commands.ts @@ -21,6 +21,7 @@ import * as EVALSHA from '../commands/EVALSHA'; import * as EXISTS from '../commands/EXISTS'; import * as EXPIRE from '../commands/EXPIRE'; import * as EXPIREAT from '../commands/EXPIREAT'; +import * as EXPIRETIME from '../commands/EXPIRETIME'; import * as GEOADD from '../commands/GEOADD'; import * as GEODIST from '../commands/GEODIST'; import * as GEOHASH from '../commands/GEOHASH'; @@ -75,6 +76,7 @@ import * as MSETNX from '../commands/MSETNX'; import * as PERSIST from '../commands/PERSIST'; import * as PEXPIRE from '../commands/PEXPIRE'; import * as PEXPIREAT from '../commands/PEXPIREAT'; +import * as PEXPIRETIME from '../commands/PEXPIRETIME'; import * as PFADD from '../commands/PFADD'; import * as PFCOUNT from '../commands/PFCOUNT'; import * as PFMERGE from '../commands/PFMERGE'; @@ -224,6 +226,8 @@ export default { expire: EXPIRE, EXPIREAT, expireAt: EXPIREAT, + EXPIRETIME, + expireTime: EXPIRETIME, GEOADD, geoAdd: GEOADD, GEODIST, @@ -332,6 +336,8 @@ export default { pExpire: PEXPIRE, PEXPIREAT, pExpireAt: PEXPIREAT, + PEXPIRETIME, + pExpireTime: PEXPIRETIME, PFADD, pfAdd: PFADD, PFCOUNT, diff --git a/packages/client/lib/commands/EXPIRE.spec.ts b/packages/client/lib/commands/EXPIRE.spec.ts index e2dc6e03123..39f9d70bd93 100644 --- a/packages/client/lib/commands/EXPIRE.spec.ts +++ b/packages/client/lib/commands/EXPIRE.spec.ts @@ -3,11 +3,20 @@ import testUtils, { GLOBAL } from '../test-utils'; import { transformArguments } from './EXPIRE'; describe('EXPIRE', () => { - it('transformArguments', () => { - assert.deepEqual( - transformArguments('key', 1), - ['EXPIRE', 'key', '1'] - ); + describe('transformArguments', () => { + it('simple', () => { + assert.deepEqual( + transformArguments('key', 1), + ['EXPIRE', 'key', '1'] + ); + }); + + it('with set option', () => { + assert.deepEqual( + transformArguments('key', 1, 'NX'), + ['EXPIRE', 'key', '1', 'NX'] + ); + }); }); testUtils.testWithClient('client.expire', async client => { diff --git a/packages/client/lib/commands/EXPIRE.ts b/packages/client/lib/commands/EXPIRE.ts index 291b544c6ac..d9e85595c3b 100644 --- a/packages/client/lib/commands/EXPIRE.ts +++ b/packages/client/lib/commands/EXPIRE.ts @@ -4,9 +4,16 @@ export const FIRST_KEY_INDEX = 1; export function transformArguments( key: RedisCommandArgument, - seconds: number + seconds: number, + mode?: 'NX' | 'XX' | 'GT' | 'LT' ): RedisCommandArguments { - return ['EXPIRE', key, seconds.toString()]; + const args = ['EXPIRE', key, seconds.toString()]; + + if (mode) { + args.push(mode); + } + + return args; } export { transformBooleanReply as transformReply } from './generic-transformers'; diff --git a/packages/client/lib/commands/EXPIREAT.spec.ts b/packages/client/lib/commands/EXPIREAT.spec.ts index 1a11af1a735..0335b36f5f5 100644 --- a/packages/client/lib/commands/EXPIREAT.spec.ts +++ b/packages/client/lib/commands/EXPIREAT.spec.ts @@ -18,6 +18,13 @@ describe('EXPIREAT', () => { ['EXPIREAT', 'key', Math.floor(d.getTime() / 1000).toString()] ); }); + + it('with set option', () => { + assert.deepEqual( + transformArguments('key', 1, 'GT'), + ['EXPIREAT', 'key', '1', 'GT'] + ); + }); }); testUtils.testWithClient('client.expireAt', async client => { diff --git a/packages/client/lib/commands/EXPIREAT.ts b/packages/client/lib/commands/EXPIREAT.ts index 3ddb75fe181..84e7760fe7f 100644 --- a/packages/client/lib/commands/EXPIREAT.ts +++ b/packages/client/lib/commands/EXPIREAT.ts @@ -5,13 +5,20 @@ export const FIRST_KEY_INDEX = 1; export function transformArguments( key: RedisCommandArgument, - timestamp: number | Date + timestamp: number | Date, + mode?: 'NX' | 'XX' | 'GT' | 'LT' ): RedisCommandArguments { - return [ + const args = [ 'EXPIREAT', key, transformEXAT(timestamp) ]; + + if (mode) { + args.push(mode); + } + + return args; } export { transformBooleanReply as transformReply } from './generic-transformers'; diff --git a/packages/client/lib/commands/EXPIRETIME.spec.ts b/packages/client/lib/commands/EXPIRETIME.spec.ts new file mode 100644 index 00000000000..3f33693d89c --- /dev/null +++ b/packages/client/lib/commands/EXPIRETIME.spec.ts @@ -0,0 +1,21 @@ +import { strict as assert } from 'assert'; +import testUtils, { GLOBAL } from '../test-utils'; +import { transformArguments } from './EXPIRETIME'; + +describe('EXPIRETIME', () => { + testUtils.isVersionGreaterThanHook([7, 0]); + + it('transformArguments', () => { + assert.deepEqual( + transformArguments('key'), + ['EXPIRETIME', 'key'] + ); + }); + + testUtils.testWithClient('client.expireTime', async client => { + assert.equal( + await client.expireTime('key'), + -2 + ); + }, GLOBAL.SERVERS.OPEN); +}); diff --git a/packages/client/lib/commands/EXPIRETIME.ts b/packages/client/lib/commands/EXPIRETIME.ts new file mode 100644 index 00000000000..8c1bb075995 --- /dev/null +++ b/packages/client/lib/commands/EXPIRETIME.ts @@ -0,0 +1,9 @@ +import { RedisCommandArgument, RedisCommandArguments } from '.'; + +export const FIRST_KEY_INDEX = 1; + +export function transformArguments(key: RedisCommandArgument): RedisCommandArguments { + return ['EXPIRETIME', key]; +} + +export declare function transformReply(): number; diff --git a/packages/client/lib/commands/PEXPIRE.spec.ts b/packages/client/lib/commands/PEXPIRE.spec.ts index 4738edcf8f0..03bde656103 100644 --- a/packages/client/lib/commands/PEXPIRE.spec.ts +++ b/packages/client/lib/commands/PEXPIRE.spec.ts @@ -3,11 +3,20 @@ import testUtils, { GLOBAL } from '../test-utils'; import { transformArguments } from './PEXPIRE'; describe('PEXPIRE', () => { - it('transformArguments', () => { - assert.deepEqual( - transformArguments('key', 1), - ['PEXPIRE', 'key', '1'] - ); + describe('transformArguments', () => { + it('simple', () => { + assert.deepEqual( + transformArguments('key', 1), + ['PEXPIRE', 'key', '1'] + ); + }); + + it('with set option', () => { + assert.deepEqual( + transformArguments('key', 1, 'GT'), + ['PEXPIRE', 'key', '1', 'GT'] + ); + }); }); testUtils.testWithClient('client.pExpire', async client => { diff --git a/packages/client/lib/commands/PEXPIRE.ts b/packages/client/lib/commands/PEXPIRE.ts index 428bfd9d261..cbb5666a514 100644 --- a/packages/client/lib/commands/PEXPIRE.ts +++ b/packages/client/lib/commands/PEXPIRE.ts @@ -4,9 +4,16 @@ export const FIRST_KEY_INDEX = 1; export function transformArguments( key: RedisCommandArgument, - milliseconds: number + milliseconds: number, + mode?: 'NX' | 'XX' | 'GT' | 'LT' ): RedisCommandArguments { - return ['PEXPIRE', key, milliseconds.toString()]; + const args = ['PEXPIRE', key, milliseconds.toString()]; + + if (mode) { + args.push(mode); + } + + return args; } export { transformBooleanReply as transformReply } from './generic-transformers'; diff --git a/packages/client/lib/commands/PEXPIREAT.spec.ts b/packages/client/lib/commands/PEXPIREAT.spec.ts index 19fc3b888d7..fec03c8fb75 100644 --- a/packages/client/lib/commands/PEXPIREAT.spec.ts +++ b/packages/client/lib/commands/PEXPIREAT.spec.ts @@ -18,6 +18,13 @@ describe('PEXPIREAT', () => { ['PEXPIREAT', 'key', d.getTime().toString()] ); }); + + it('with set option', () => { + assert.deepEqual( + transformArguments('key', 1, 'XX'), + ['PEXPIREAT', 'key', '1', 'XX'] + ); + }); }); testUtils.testWithClient('client.pExpireAt', async client => { diff --git a/packages/client/lib/commands/PEXPIREAT.ts b/packages/client/lib/commands/PEXPIREAT.ts index 49872877f6e..da912ec4fcb 100644 --- a/packages/client/lib/commands/PEXPIREAT.ts +++ b/packages/client/lib/commands/PEXPIREAT.ts @@ -5,13 +5,20 @@ export const FIRST_KEY_INDEX = 1; export function transformArguments( key: RedisCommandArgument, - millisecondsTimestamp: number | Date + millisecondsTimestamp: number | Date, + mode?: 'NX' | 'XX' | 'GT' | 'LT' ): RedisCommandArguments { - return [ + const args = [ 'PEXPIREAT', key, transformPXAT(millisecondsTimestamp) ]; + + if (mode) { + args.push(mode); + } + + return args; } export { transformBooleanReply as transformReply } from './generic-transformers'; diff --git a/packages/client/lib/commands/PEXPIRETIME.spec.ts b/packages/client/lib/commands/PEXPIRETIME.spec.ts new file mode 100644 index 00000000000..8874346296b --- /dev/null +++ b/packages/client/lib/commands/PEXPIRETIME.spec.ts @@ -0,0 +1,21 @@ +import { strict as assert } from 'assert'; +import testUtils, { GLOBAL } from '../test-utils'; +import { transformArguments } from './PEXPIRETIME'; + +describe('PEXPIRETIME', () => { + testUtils.isVersionGreaterThanHook([7, 0]); + + it('transformArguments', () => { + assert.deepEqual( + transformArguments('key'), + ['PEXPIRETIME', 'key'] + ); + }); + + testUtils.testWithClient('client.pExpireTime', async client => { + assert.equal( + await client.pExpireTime('key'), + -2 + ); + }, GLOBAL.SERVERS.OPEN); +}); diff --git a/packages/client/lib/commands/PEXPIRETIME.ts b/packages/client/lib/commands/PEXPIRETIME.ts new file mode 100644 index 00000000000..4c1acba8f04 --- /dev/null +++ b/packages/client/lib/commands/PEXPIRETIME.ts @@ -0,0 +1,9 @@ +import { RedisCommandArgument, RedisCommandArguments } from '.'; + +export const FIRST_KEY_INDEX = 1; + +export function transformArguments(key: RedisCommandArgument): RedisCommandArguments { + return ['PEXPIRETIME', key]; +} + +export declare function transformReply(): number; From fe16dc0eae9eea0e1540ea2b885b8760229ddea6 Mon Sep 17 00:00:00 2001 From: Avital Fine <98389525+Avital-Fine@users.noreply.github.com> Date: Thu, 24 Mar 2022 16:00:08 +0100 Subject: [PATCH 1163/1748] Support BIT|BYTE option (#2035) * Support BIT|BYTE option * clean code * clean code * clean code Co-authored-by: leibale --- packages/client/lib/commands/BITCOUNT.spec.ts | 29 ++++++++++++++----- packages/client/lib/commands/BITCOUNT.ts | 5 ++++ packages/client/lib/commands/BITPOS.spec.ts | 9 +++++- packages/client/lib/commands/BITPOS.ts | 7 ++++- 4 files changed, 40 insertions(+), 10 deletions(-) diff --git a/packages/client/lib/commands/BITCOUNT.spec.ts b/packages/client/lib/commands/BITCOUNT.spec.ts index 8919957cf92..76e7b03f7c9 100644 --- a/packages/client/lib/commands/BITCOUNT.spec.ts +++ b/packages/client/lib/commands/BITCOUNT.spec.ts @@ -11,14 +11,27 @@ describe('BITCOUNT', () => { ); }); - it('with range', () => { - assert.deepEqual( - transformArguments('key', { - start: 0, - end: 1 - }), - ['BITCOUNT', 'key', '0', '1'] - ); + describe('with range', () => { + it('simple', () => { + assert.deepEqual( + transformArguments('key', { + start: 0, + end: 1 + }), + ['BITCOUNT', 'key', '0', '1'] + ); + }); + + it('with mode', () => { + assert.deepEqual( + transformArguments('key', { + start: 0, + end: 1, + mode: 'BIT' + }), + ['BITCOUNT', 'key', '0', '1', 'BIT'] + ); + }); }); }); diff --git a/packages/client/lib/commands/BITCOUNT.ts b/packages/client/lib/commands/BITCOUNT.ts index efbc6f2220d..d937af2b5dd 100644 --- a/packages/client/lib/commands/BITCOUNT.ts +++ b/packages/client/lib/commands/BITCOUNT.ts @@ -7,6 +7,7 @@ export const IS_READ_ONLY = true; interface BitCountRange { start: number; end: number; + mode?: 'BYTE' | 'BIT'; } export function transformArguments( @@ -20,6 +21,10 @@ export function transformArguments( range.start.toString(), range.end.toString() ); + + if (range?.mode) { + args.push(range.mode); + } } return args; diff --git a/packages/client/lib/commands/BITPOS.spec.ts b/packages/client/lib/commands/BITPOS.spec.ts index 354deea6195..2a0758fe5da 100644 --- a/packages/client/lib/commands/BITPOS.spec.ts +++ b/packages/client/lib/commands/BITPOS.spec.ts @@ -18,12 +18,19 @@ describe('BITPOS', () => { ); }); - it('with start, end', () => { + it('with start and end', () => { assert.deepEqual( transformArguments('key', 1, 1, -1), ['BITPOS', 'key', '1', '1', '-1'] ); }); + + it('with start, end and mode', () => { + assert.deepEqual( + transformArguments('key', 1, 1, -1, 'BIT'), + ['BITPOS', 'key', '1', '1', '-1', 'BIT'] + ); + }); }); testUtils.testWithClient('client.bitPos', async client => { diff --git a/packages/client/lib/commands/BITPOS.ts b/packages/client/lib/commands/BITPOS.ts index 2e54b11bc9e..a9a035fd9f2 100644 --- a/packages/client/lib/commands/BITPOS.ts +++ b/packages/client/lib/commands/BITPOS.ts @@ -9,7 +9,8 @@ export function transformArguments( key: RedisCommandArgument, bit: BitValue, start?: number, - end?: number + end?: number, + mode?: 'BYTE' | 'BIT' ): RedisCommandArguments { const args = ['BITPOS', key, bit.toString()]; @@ -21,6 +22,10 @@ export function transformArguments( args.push(end.toString()); } + if (mode) { + args.push(mode); + } + return args; } From 6b8a40a36bb207fa120aa99b277958e599b0d3b6 Mon Sep 17 00:00:00 2001 From: Avital Fine <98389525+Avital-Fine@users.noreply.github.com> Date: Sun, 27 Mar 2022 19:06:27 +0200 Subject: [PATCH 1164/1748] Support new cluster commands (#2050) * Support new cluster commands * clean code Co-authored-by: leibale --- packages/client/lib/client/commands.ts | 9 +++++ .../commands/CLUSTER_ADDSLOTSRANGE.spec.ts | 29 ++++++++++++++ .../lib/commands/CLUSTER_ADDSLOTSRANGE.ts | 13 +++++++ .../commands/CLUSTER_DELSLOTSRANGE.spec.ts | 29 ++++++++++++++ .../lib/commands/CLUSTER_DELSLOTSRANGE.ts | 13 +++++++ .../client/lib/commands/CLUSTER_LINKS.spec.ts | 27 +++++++++++++ packages/client/lib/commands/CLUSTER_LINKS.ts | 38 +++++++++++++++++++ .../lib/commands/generic-transformers.spec.ts | 28 +++++++++++++- .../lib/commands/generic-transformers.ts | 30 +++++++++++++++ 9 files changed, 215 insertions(+), 1 deletion(-) create mode 100644 packages/client/lib/commands/CLUSTER_ADDSLOTSRANGE.spec.ts create mode 100644 packages/client/lib/commands/CLUSTER_ADDSLOTSRANGE.ts create mode 100644 packages/client/lib/commands/CLUSTER_DELSLOTSRANGE.spec.ts create mode 100644 packages/client/lib/commands/CLUSTER_DELSLOTSRANGE.ts create mode 100644 packages/client/lib/commands/CLUSTER_LINKS.spec.ts create mode 100644 packages/client/lib/commands/CLUSTER_LINKS.ts diff --git a/packages/client/lib/client/commands.ts b/packages/client/lib/client/commands.ts index 0d9f63b003e..acffd4711a9 100644 --- a/packages/client/lib/client/commands.ts +++ b/packages/client/lib/client/commands.ts @@ -23,8 +23,11 @@ import * as CLIENT_KILL from '../commands/CLIENT_KILL'; import * as CLIENT_SETNAME from '../commands/CLIENT_SETNAME'; import * as CLIENT_INFO from '../commands/CLIENT_INFO'; import * as CLUSTER_ADDSLOTS from '../commands/CLUSTER_ADDSLOTS'; +import * as CLUSTER_ADDSLOTSRANGE from '../commands/CLUSTER_ADDSLOTSRANGE'; +import * as CLUSTER_DELSLOTSRANGE from '../commands/CLUSTER_DELSLOTSRANGE'; import * as CLUSTER_FLUSHSLOTS from '../commands/CLUSTER_FLUSHSLOTS'; import * as CLUSTER_INFO from '../commands/CLUSTER_INFO'; +import * as CLUSTER_LINKS from '../commands/CLUSTER_LINKS'; import * as CLUSTER_NODES from '../commands/CLUSTER_NODES'; import * as CLUSTER_MEET from '../commands/CLUSTER_MEET'; import * as CLUSTER_RESET from '../commands/CLUSTER_RESET'; @@ -132,10 +135,16 @@ export default { clientInfo: CLIENT_INFO, CLUSTER_ADDSLOTS, clusterAddSlots: CLUSTER_ADDSLOTS, + CLUSTER_ADDSLOTSRANGE, + clusterAddSlotsRange: CLUSTER_ADDSLOTSRANGE, + CLUSTER_DELSLOTSRANGE, + clusterDelSlotsRange: CLUSTER_DELSLOTSRANGE, CLUSTER_FLUSHSLOTS, clusterFlushSlots: CLUSTER_FLUSHSLOTS, CLUSTER_INFO, clusterInfo: CLUSTER_INFO, + CLUSTER_LINKS, + clusterLinks: CLUSTER_LINKS, CLUSTER_NODES, clusterNodes: CLUSTER_NODES, CLUSTER_MEET, diff --git a/packages/client/lib/commands/CLUSTER_ADDSLOTSRANGE.spec.ts b/packages/client/lib/commands/CLUSTER_ADDSLOTSRANGE.spec.ts new file mode 100644 index 00000000000..ebd1e3445ff --- /dev/null +++ b/packages/client/lib/commands/CLUSTER_ADDSLOTSRANGE.spec.ts @@ -0,0 +1,29 @@ +import { strict as assert } from 'assert'; +import { transformArguments } from './CLUSTER_ADDSLOTSRANGE'; + +describe('CLUSTER ADDSLOTSRANGE', () => { + describe('transformArguments', () => { + it('single', () => { + assert.deepEqual( + transformArguments({ + start: 0, + end: 1 + }), + ['CLUSTER', 'ADDSLOTSRANGE', '0', '1'] + ); + }); + + it('multiple', () => { + assert.deepEqual( + transformArguments([{ + start: 0, + end: 1 + }, { + start: 2, + end: 3 + }]), + ['CLUSTER', 'ADDSLOTSRANGE', '0', '1', '2', '3'] + ); + }); + }); +}); diff --git a/packages/client/lib/commands/CLUSTER_ADDSLOTSRANGE.ts b/packages/client/lib/commands/CLUSTER_ADDSLOTSRANGE.ts new file mode 100644 index 00000000000..6a8d6dc668f --- /dev/null +++ b/packages/client/lib/commands/CLUSTER_ADDSLOTSRANGE.ts @@ -0,0 +1,13 @@ +import { RedisCommandArguments } from '.'; +import { pushSlotRangesArguments, SlotRange } from './generic-transformers'; + +export function transformArguments( + ranges: SlotRange | Array +): RedisCommandArguments { + return pushSlotRangesArguments( + ['CLUSTER', 'ADDSLOTSRANGE'], + ranges + ); +} + +export declare function transformReply(): 'OK'; diff --git a/packages/client/lib/commands/CLUSTER_DELSLOTSRANGE.spec.ts b/packages/client/lib/commands/CLUSTER_DELSLOTSRANGE.spec.ts new file mode 100644 index 00000000000..8fd50d01a54 --- /dev/null +++ b/packages/client/lib/commands/CLUSTER_DELSLOTSRANGE.spec.ts @@ -0,0 +1,29 @@ +import { strict as assert } from 'assert'; +import { transformArguments } from './CLUSTER_DELSLOTSRANGE'; + +describe('CLUSTER DELSLOTSRANGE', () => { + describe('transformArguments', () => { + it('single', () => { + assert.deepEqual( + transformArguments({ + start: 0, + end: 1 + }), + ['CLUSTER', 'DELSLOTSRANGE', '0', '1'] + ); + }); + + it('multiple', () => { + assert.deepEqual( + transformArguments([{ + start: 0, + end: 1 + }, { + start: 2, + end: 3 + }]), + ['CLUSTER', 'DELSLOTSRANGE', '0', '1', '2', '3'] + ); + }); + }); +}); diff --git a/packages/client/lib/commands/CLUSTER_DELSLOTSRANGE.ts b/packages/client/lib/commands/CLUSTER_DELSLOTSRANGE.ts new file mode 100644 index 00000000000..b136113c65f --- /dev/null +++ b/packages/client/lib/commands/CLUSTER_DELSLOTSRANGE.ts @@ -0,0 +1,13 @@ +import { RedisCommandArguments } from '.'; +import { pushSlotRangesArguments, SlotRange } from './generic-transformers'; + +export function transformArguments( + ranges: SlotRange | Array +): RedisCommandArguments { + return pushSlotRangesArguments( + ['CLUSTER', 'DELSLOTSRANGE'], + ranges + ); +} + +export declare function transformReply(): 'OK'; diff --git a/packages/client/lib/commands/CLUSTER_LINKS.spec.ts b/packages/client/lib/commands/CLUSTER_LINKS.spec.ts new file mode 100644 index 00000000000..242e9012fbc --- /dev/null +++ b/packages/client/lib/commands/CLUSTER_LINKS.spec.ts @@ -0,0 +1,27 @@ +import { strict as assert } from 'assert'; +import testUtils, { GLOBAL } from '../test-utils'; +import { transformArguments } from './CLUSTER_LINKS'; + +describe('CLUSTER LINKS', () => { + testUtils.isVersionGreaterThanHook([7, 0]); + + it('transformArguments', () => { + assert.deepEqual( + transformArguments(), + ['CLUSTER', 'LINKS'] + ); + }); + + testUtils.testWithCluster('clusterNode.clusterLinks', async cluster => { + const links = await cluster.getSlotMaster(0).client.clusterLinks(); + assert.ok(Array.isArray(links)); + for (const link of links) { + assert.equal(typeof link.direction, 'string'); + assert.equal(typeof link.node, 'string'); + assert.equal(typeof link.createTime, 'number'); + assert.equal(typeof link.events, 'string'); + assert.equal(typeof link.sendBufferAllocated, 'number'); + assert.equal(typeof link.sendBufferUsed, 'number'); + } + }, GLOBAL.CLUSTERS.OPEN); +}); diff --git a/packages/client/lib/commands/CLUSTER_LINKS.ts b/packages/client/lib/commands/CLUSTER_LINKS.ts new file mode 100644 index 00000000000..9a5608c102f --- /dev/null +++ b/packages/client/lib/commands/CLUSTER_LINKS.ts @@ -0,0 +1,38 @@ +export function transformArguments(): Array { + return ['CLUSTER', 'LINKS']; +} + +type ClusterLinksRawReply = Array<[ + 'direction', + string, + 'node', + string, + 'createTime', + number, + 'events', + string, + 'send-buffer-allocated', + number, + 'send-buffer-used', + number +]>; + +type ClusterLinksReply = Array<{ + direction: string; + node: string; + createTime: number; + events: string; + sendBufferAllocated: number; + sendBufferUsed: number; +}>; + +export function transformReply(reply: ClusterLinksRawReply): ClusterLinksReply { + return reply.map(peerLink => ({ + direction: peerLink[1], + node: peerLink[3], + createTime: Number(peerLink[5]), + events: peerLink[7], + sendBufferAllocated: Number(peerLink[9]), + sendBufferUsed: Number(peerLink[11]) + })); +} diff --git a/packages/client/lib/commands/generic-transformers.spec.ts b/packages/client/lib/commands/generic-transformers.spec.ts index 96dca31dad5..6e286e284d9 100644 --- a/packages/client/lib/commands/generic-transformers.spec.ts +++ b/packages/client/lib/commands/generic-transformers.spec.ts @@ -23,7 +23,8 @@ import { pushOptionalVerdictArgument, transformCommandReply, CommandFlags, - CommandCategories + CommandCategories, + pushSlotRangesArguments } from './generic-transformers'; describe('Generic Transformers', () => { @@ -639,4 +640,29 @@ describe('Generic Transformers', () => { } ); }); + + describe('pushSlotRangesArguments', () => { + it('single range', () => { + assert.deepEqual( + pushSlotRangesArguments([], { + start: 0, + end: 1 + }), + ['0', '1'] + ); + }); + + it('multiple ranges', () => { + assert.deepEqual( + pushSlotRangesArguments([], [{ + start: 0, + end: 1 + }, { + start: 2, + end: 3 + }]), + ['0', '1', '2', '3'] + ); + }); + }); }); diff --git a/packages/client/lib/commands/generic-transformers.ts b/packages/client/lib/commands/generic-transformers.ts index 0799476ae64..a7bc6805dde 100644 --- a/packages/client/lib/commands/generic-transformers.ts +++ b/packages/client/lib/commands/generic-transformers.ts @@ -422,3 +422,33 @@ export function transformCommandReply( categories: new Set(categories) }; } + +export interface SlotRange { + start: number; + end: number; +} + +function pushSlotRangeArguments( + args: RedisCommandArguments, + range: SlotRange +): void { + args.push( + range.start.toString(), + range.end.toString() + ); +} + +export function pushSlotRangesArguments( + args: RedisCommandArguments, + ranges: SlotRange | Array +): RedisCommandArguments { + if (Array.isArray(ranges)) { + for (const range of ranges) { + pushSlotRangeArguments(args, range); + } + } else { + pushSlotRangeArguments(args, ranges); + } + + return args; +} From 6ca45f674de1eb85cec880e0644efe79af07c196 Mon Sep 17 00:00:00 2001 From: Quentin Rankin <70821136+QuentinRK@users.noreply.github.com> Date: Sun, 27 Mar 2022 18:29:10 +0100 Subject: [PATCH 1165/1748] support command LATENCY DOCTOR (#2053) * The Latency Doctor Command has been added to the project * Update LATENCY_DOCTOR.ts * Update LATENCY_DOCTOR.spec.ts * Update LATENCY_DOCTOR.spec.ts Co-authored-by: QuentinRK Co-authored-by: Leibale Eidelman --- packages/client/lib/client/commands.ts | 3 +++ .../lib/commands/LATENCY_DOCTOR.spec.ts | 19 +++++++++++++++++++ .../client/lib/commands/LATENCY_DOCTOR.ts | 5 +++++ 3 files changed, 27 insertions(+) create mode 100644 packages/client/lib/commands/LATENCY_DOCTOR.spec.ts create mode 100644 packages/client/lib/commands/LATENCY_DOCTOR.ts diff --git a/packages/client/lib/client/commands.ts b/packages/client/lib/client/commands.ts index acffd4711a9..41c0769e296 100644 --- a/packages/client/lib/client/commands.ts +++ b/packages/client/lib/client/commands.ts @@ -84,6 +84,7 @@ import * as SWAPDB from '../commands/SWAPDB'; import * as TIME from '../commands/TIME'; import * as UNWATCH from '../commands/UNWATCH'; import * as WAIT from '../commands/WAIT'; +import * as LATENCY_DOCTOR from '../commands/LATENCY_DOCTOR'; export default { ...CLUSTER_COMMANDS, @@ -193,6 +194,8 @@ export default { keys: KEYS, LASTSAVE, lastSave: LASTSAVE, + LATENCY_DOCTOR, + latencyDoctor: LATENCY_DOCTOR, LOLWUT, lolwut: LOLWUT, MEMOERY_DOCTOR, diff --git a/packages/client/lib/commands/LATENCY_DOCTOR.spec.ts b/packages/client/lib/commands/LATENCY_DOCTOR.spec.ts new file mode 100644 index 00000000000..3888ff8bd36 --- /dev/null +++ b/packages/client/lib/commands/LATENCY_DOCTOR.spec.ts @@ -0,0 +1,19 @@ +import {strict as assert} from 'assert'; +import testUtils, {GLOBAL} from '../test-utils'; +import { transformArguments } from './LATENCY_DOCTOR'; + +describe('LATENCY DOCTOR', () => { + it('transformArguments', () => { + assert.deepEqual( + transformArguments(), + ['LATENCY', 'DOCTOR'] + ); + }); + + testUtils.testWithClient('client.latencyDoctor', async client => { + assert.equal( + typeof (await client.latencyDoctor()), + 'string' + ); + }, GLOBAL.SERVERS.OPEN); +}); diff --git a/packages/client/lib/commands/LATENCY_DOCTOR.ts b/packages/client/lib/commands/LATENCY_DOCTOR.ts new file mode 100644 index 00000000000..d2106c06114 --- /dev/null +++ b/packages/client/lib/commands/LATENCY_DOCTOR.ts @@ -0,0 +1,5 @@ +export function transformArguments(): Array { + return ['LATENCY', 'DOCTOR']; +} + +export declare function transformReply(): string; From 515adf1346b5262058da75c5d14443a8e0ee9558 Mon Sep 17 00:00:00 2001 From: Avital Fine <98389525+Avital-Fine@users.noreply.github.com> Date: Sun, 27 Mar 2022 19:35:01 +0200 Subject: [PATCH 1166/1748] Support OBJECT [...] commands (#2014) * Support OBJECT [...] commands * move commands to cluster/commands.ts Co-authored-by: leibale --- packages/client/lib/cluster/commands.ts | 12 ++++++++++++ .../lib/commands/OBJECT_ENCODING.spec.ts | 19 +++++++++++++++++++ .../client/lib/commands/OBJECT_ENCODING.ts | 11 +++++++++++ .../client/lib/commands/OBJECT_FREQ.spec.ts | 19 +++++++++++++++++++ packages/client/lib/commands/OBJECT_FREQ.ts | 11 +++++++++++ .../lib/commands/OBJECT_IDLETIME.spec.ts | 19 +++++++++++++++++++ .../client/lib/commands/OBJECT_IDLETIME.ts | 11 +++++++++++ .../lib/commands/OBJECT_REFCOUNT.spec.ts | 19 +++++++++++++++++++ .../client/lib/commands/OBJECT_REFCOUNT.ts | 11 +++++++++++ 9 files changed, 132 insertions(+) create mode 100644 packages/client/lib/commands/OBJECT_ENCODING.spec.ts create mode 100644 packages/client/lib/commands/OBJECT_ENCODING.ts create mode 100644 packages/client/lib/commands/OBJECT_FREQ.spec.ts create mode 100644 packages/client/lib/commands/OBJECT_FREQ.ts create mode 100644 packages/client/lib/commands/OBJECT_IDLETIME.spec.ts create mode 100644 packages/client/lib/commands/OBJECT_IDLETIME.ts create mode 100644 packages/client/lib/commands/OBJECT_REFCOUNT.spec.ts create mode 100644 packages/client/lib/commands/OBJECT_REFCOUNT.ts diff --git a/packages/client/lib/cluster/commands.ts b/packages/client/lib/cluster/commands.ts index a43a6a38dc7..dd82e6f3147 100644 --- a/packages/client/lib/cluster/commands.ts +++ b/packages/client/lib/cluster/commands.ts @@ -73,6 +73,10 @@ import * as MGET from '../commands/MGET'; import * as MIGRATE from '../commands/MIGRATE'; import * as MSET from '../commands/MSET'; import * as MSETNX from '../commands/MSETNX'; +import * as OBJECT_ENCODING from '../commands/OBJECT_ENCODING'; +import * as OBJECT_FREQ from '../commands/OBJECT_FREQ'; +import * as OBJECT_IDLETIME from '../commands/OBJECT_IDLETIME'; +import * as OBJECT_REFCOUNT from '../commands/OBJECT_REFCOUNT'; import * as PERSIST from '../commands/PERSIST'; import * as PEXPIRE from '../commands/PEXPIRE'; import * as PEXPIREAT from '../commands/PEXPIREAT'; @@ -330,6 +334,14 @@ export default { mSet: MSET, MSETNX, mSetNX: MSETNX, + OBJECT_ENCODING, + objectEncoding: OBJECT_ENCODING, + OBJECT_FREQ, + objectFreq: OBJECT_FREQ, + OBJECT_IDLETIME, + objectIdleTime: OBJECT_IDLETIME, + OBJECT_REFCOUNT, + objectRefCount: OBJECT_REFCOUNT, PERSIST, persist: PERSIST, PEXPIRE, diff --git a/packages/client/lib/commands/OBJECT_ENCODING.spec.ts b/packages/client/lib/commands/OBJECT_ENCODING.spec.ts new file mode 100644 index 00000000000..6f42969d547 --- /dev/null +++ b/packages/client/lib/commands/OBJECT_ENCODING.spec.ts @@ -0,0 +1,19 @@ +import { strict as assert } from 'assert'; +import testUtils, { GLOBAL } from '../test-utils'; +import { transformArguments } from './OBJECT_ENCODING'; + +describe('OBJECT ENCODING', () => { + it('transformArguments', () => { + assert.deepEqual( + transformArguments('key'), + ['OBJECT', 'ENCODING', 'key'] + ); + }); + + testUtils.testWithClient('client.objectEncoding', async client => { + assert.equal( + await client.objectEncoding('key'), + null + ); + }, GLOBAL.SERVERS.OPEN); +}); diff --git a/packages/client/lib/commands/OBJECT_ENCODING.ts b/packages/client/lib/commands/OBJECT_ENCODING.ts new file mode 100644 index 00000000000..ac219ae89ed --- /dev/null +++ b/packages/client/lib/commands/OBJECT_ENCODING.ts @@ -0,0 +1,11 @@ +import { RedisCommandArgument, RedisCommandArguments } from '.'; + +export const FIRST_KEY_INDEX = 2; + +export const IS_READ_ONLY = true; + +export function transformArguments(key: RedisCommandArgument): RedisCommandArguments { + return ['OBJECT', 'ENCODING', key]; +} + +export declare function transformReply(): string | null; diff --git a/packages/client/lib/commands/OBJECT_FREQ.spec.ts b/packages/client/lib/commands/OBJECT_FREQ.spec.ts new file mode 100644 index 00000000000..6d2513cf18c --- /dev/null +++ b/packages/client/lib/commands/OBJECT_FREQ.spec.ts @@ -0,0 +1,19 @@ +import { strict as assert } from 'assert'; +import testUtils, { GLOBAL } from '../test-utils'; +import { transformArguments } from './OBJECT_FREQ'; + +describe('OBJECT FREQ', () => { + it('transformArguments', () => { + assert.deepEqual( + transformArguments('key'), + ['OBJECT', 'FREQ', 'key'] + ); + }); + + testUtils.testWithClient('client.objectFreq', async client => { + assert.equal( + await client.objectFreq('key'), + null + ); + }, GLOBAL.SERVERS.OPEN); +}); diff --git a/packages/client/lib/commands/OBJECT_FREQ.ts b/packages/client/lib/commands/OBJECT_FREQ.ts new file mode 100644 index 00000000000..071d16f2748 --- /dev/null +++ b/packages/client/lib/commands/OBJECT_FREQ.ts @@ -0,0 +1,11 @@ +import { RedisCommandArgument, RedisCommandArguments } from '.'; + +export const FIRST_KEY_INDEX = 2; + +export const IS_READ_ONLY = true; + +export function transformArguments(key: RedisCommandArgument): RedisCommandArguments { + return ['OBJECT', 'FREQ', key]; +} + +export declare function transformReply(): number | null; diff --git a/packages/client/lib/commands/OBJECT_IDLETIME.spec.ts b/packages/client/lib/commands/OBJECT_IDLETIME.spec.ts new file mode 100644 index 00000000000..61529e1366b --- /dev/null +++ b/packages/client/lib/commands/OBJECT_IDLETIME.spec.ts @@ -0,0 +1,19 @@ +import { strict as assert } from 'assert'; +import testUtils, { GLOBAL } from '../test-utils'; +import { transformArguments } from './OBJECT_IDLETIME'; + +describe('OBJECT IDLETIME', () => { + it('transformArguments', () => { + assert.deepEqual( + transformArguments('key'), + ['OBJECT', 'IDLETIME', 'key'] + ); + }); + + testUtils.testWithClient('client.objectIdleTime', async client => { + assert.equal( + await client.objectIdleTime('key'), + null + ); + }, GLOBAL.SERVERS.OPEN); +}); diff --git a/packages/client/lib/commands/OBJECT_IDLETIME.ts b/packages/client/lib/commands/OBJECT_IDLETIME.ts new file mode 100644 index 00000000000..38847d6f4cf --- /dev/null +++ b/packages/client/lib/commands/OBJECT_IDLETIME.ts @@ -0,0 +1,11 @@ +import { RedisCommandArgument, RedisCommandArguments } from '.'; + +export const FIRST_KEY_INDEX = 2; + +export const IS_READ_ONLY = true; + +export function transformArguments(key: RedisCommandArgument): RedisCommandArguments { + return ['OBJECT', 'IDLETIME', key]; +} + +export declare function transformReply(): number | null; diff --git a/packages/client/lib/commands/OBJECT_REFCOUNT.spec.ts b/packages/client/lib/commands/OBJECT_REFCOUNT.spec.ts new file mode 100644 index 00000000000..199dca3fe82 --- /dev/null +++ b/packages/client/lib/commands/OBJECT_REFCOUNT.spec.ts @@ -0,0 +1,19 @@ +import { strict as assert } from 'assert'; +import testUtils, { GLOBAL } from '../test-utils'; +import { transformArguments } from './OBJECT_REFCOUNT'; + +describe('OBJECT REFCOUNT', () => { + it('transformArguments', () => { + assert.deepEqual( + transformArguments('key'), + ['OBJECT', 'REFCOUNT', 'key'] + ); + }); + + testUtils.testWithClient('client.objectRefCount', async client => { + assert.equal( + await client.objectRefCount('key'), + null + ); + }, GLOBAL.SERVERS.OPEN); +}); diff --git a/packages/client/lib/commands/OBJECT_REFCOUNT.ts b/packages/client/lib/commands/OBJECT_REFCOUNT.ts new file mode 100644 index 00000000000..9fd259b5b90 --- /dev/null +++ b/packages/client/lib/commands/OBJECT_REFCOUNT.ts @@ -0,0 +1,11 @@ +import { RedisCommandArgument, RedisCommandArguments } from '.'; + +export const FIRST_KEY_INDEX = 2; + +export const IS_READ_ONLY = true; + +export function transformArguments(key: RedisCommandArgument): RedisCommandArguments { + return ['OBJECT', 'REFCOUNT', key]; +} + +export declare function transformReply(): number | null; From 5ade5dadc02f84f8ac7e467512536a4a8115c1a9 Mon Sep 17 00:00:00 2001 From: Leibale Eidelman Date: Sun, 27 Mar 2022 16:27:05 -0400 Subject: [PATCH 1167/1748] upgrade dependencies (#2057) * upgrade dependencies * fix "Property 'uninstall' does not exist on type 'SinonFakeTimers'." --- package-lock.json | 1355 +++++++++++---------- package.json | 4 +- packages/bloom/package.json | 10 +- packages/client/lib/client/socket.spec.ts | 2 +- packages/client/package.json | 20 +- packages/graph/package.json | 10 +- packages/json/package.json | 10 +- packages/search/package.json | 10 +- packages/test-utils/package.json | 12 +- packages/time-series/package.json | 10 +- 10 files changed, 789 insertions(+), 654 deletions(-) diff --git a/package-lock.json b/package-lock.json index 3072740667c..641d253ef0c 100644 --- a/package-lock.json +++ b/package-lock.json @@ -22,8 +22,20 @@ "devDependencies": { "@tsconfig/node12": "^1.0.9", "gh-pages": "^3.2.3", - "release-it": "^14.12.1", - "typescript": "^4.5.4" + "release-it": "^14.13.1", + "typescript": "^4.6.3" + } + }, + "node_modules/@ampproject/remapping": { + "version": "2.1.2", + "resolved": "https://registry.npmjs.org/@ampproject/remapping/-/remapping-2.1.2.tgz", + "integrity": "sha512-hoyByceqwKirw7w3Z7gnIIZC3Wx3J484Y3L/cMpXFbr7d9ZQj2mODrirNzcJa+SM3UlpWXYvKV4RlRpFXlWgXg==", + "dev": true, + "dependencies": { + "@jridgewell/trace-mapping": "^0.3.0" + }, + "engines": { + "node": ">=6.0.0" } }, "node_modules/@babel/code-frame": { @@ -39,35 +51,35 @@ } }, "node_modules/@babel/compat-data": { - "version": "7.16.8", - "resolved": "https://registry.npmjs.org/@babel/compat-data/-/compat-data-7.16.8.tgz", - "integrity": "sha512-m7OkX0IdKLKPpBlJtF561YJal5y/jyI5fNfWbPxh2D/nbzzGI4qRyrD8xO2jB24u7l+5I2a43scCG2IrfjC50Q==", + "version": "7.17.7", + "resolved": "https://registry.npmjs.org/@babel/compat-data/-/compat-data-7.17.7.tgz", + "integrity": "sha512-p8pdE6j0a29TNGebNm7NzYZWB3xVZJBZ7XGs42uAKzQo8VQ3F0By/cQCtUEABwIqw5zo6WA4NbmxsfzADzMKnQ==", "dev": true, "engines": { "node": ">=6.9.0" } }, "node_modules/@babel/core": { - "version": "7.16.12", - "resolved": "https://registry.npmjs.org/@babel/core/-/core-7.16.12.tgz", - "integrity": "sha512-dK5PtG1uiN2ikk++5OzSYsitZKny4wOCD0nrO4TqnW4BVBTQ2NGS3NgilvT/TEyxTST7LNyWV/T4tXDoD3fOgg==", + "version": "7.17.8", + "resolved": "https://registry.npmjs.org/@babel/core/-/core-7.17.8.tgz", + "integrity": "sha512-OdQDV/7cRBtJHLSOBqqbYNkOcydOgnX59TZx4puf41fzcVtN3e/4yqY8lMQsK+5X2lJtAdmA+6OHqsj1hBJ4IQ==", "dev": true, "dependencies": { + "@ampproject/remapping": "^2.1.0", "@babel/code-frame": "^7.16.7", - "@babel/generator": "^7.16.8", - "@babel/helper-compilation-targets": "^7.16.7", - "@babel/helper-module-transforms": "^7.16.7", - "@babel/helpers": "^7.16.7", - "@babel/parser": "^7.16.12", + "@babel/generator": "^7.17.7", + "@babel/helper-compilation-targets": "^7.17.7", + "@babel/helper-module-transforms": "^7.17.7", + "@babel/helpers": "^7.17.8", + "@babel/parser": "^7.17.8", "@babel/template": "^7.16.7", - "@babel/traverse": "^7.16.10", - "@babel/types": "^7.16.8", + "@babel/traverse": "^7.17.3", + "@babel/types": "^7.17.0", "convert-source-map": "^1.7.0", "debug": "^4.1.0", "gensync": "^1.0.0-beta.2", "json5": "^2.1.2", - "semver": "^6.3.0", - "source-map": "^0.5.0" + "semver": "^6.3.0" }, "engines": { "node": ">=6.9.0" @@ -78,12 +90,12 @@ } }, "node_modules/@babel/generator": { - "version": "7.16.8", - "resolved": "https://registry.npmjs.org/@babel/generator/-/generator-7.16.8.tgz", - "integrity": "sha512-1ojZwE9+lOXzcWdWmO6TbUzDfqLD39CmEhN8+2cX9XkDo5yW1OpgfejfliysR2AWLpMamTiOiAp/mtroaymhpw==", + "version": "7.17.7", + "resolved": "https://registry.npmjs.org/@babel/generator/-/generator-7.17.7.tgz", + "integrity": "sha512-oLcVCTeIFadUoArDTwpluncplrYBmTCCZZgXCbgNGvOBBiSDDK3eWO4b/+eOTli5tKv1lg+a5/NAXg+nTcei1w==", "dev": true, "dependencies": { - "@babel/types": "^7.16.8", + "@babel/types": "^7.17.0", "jsesc": "^2.5.1", "source-map": "^0.5.0" }, @@ -92,12 +104,12 @@ } }, "node_modules/@babel/helper-compilation-targets": { - "version": "7.16.7", - "resolved": "https://registry.npmjs.org/@babel/helper-compilation-targets/-/helper-compilation-targets-7.16.7.tgz", - "integrity": "sha512-mGojBwIWcwGD6rfqgRXVlVYmPAv7eOpIemUG3dGnDdCY4Pae70ROij3XmfrH6Fa1h1aiDylpglbZyktfzyo/hA==", + "version": "7.17.7", + "resolved": "https://registry.npmjs.org/@babel/helper-compilation-targets/-/helper-compilation-targets-7.17.7.tgz", + "integrity": "sha512-UFzlz2jjd8kroj0hmCFV5zr+tQPi1dpC2cRsDV/3IEW8bJfCPrPpmcSN6ZS8RqIq4LXcmpipCQFPddyFA5Yc7w==", "dev": true, "dependencies": { - "@babel/compat-data": "^7.16.4", + "@babel/compat-data": "^7.17.7", "@babel/helper-validator-option": "^7.16.7", "browserslist": "^4.17.5", "semver": "^6.3.0" @@ -172,31 +184,31 @@ } }, "node_modules/@babel/helper-module-transforms": { - "version": "7.16.7", - "resolved": "https://registry.npmjs.org/@babel/helper-module-transforms/-/helper-module-transforms-7.16.7.tgz", - "integrity": "sha512-gaqtLDxJEFCeQbYp9aLAefjhkKdjKcdh6DB7jniIGU3Pz52WAmP268zK0VgPz9hUNkMSYeH976K2/Y6yPadpng==", + "version": "7.17.7", + "resolved": "https://registry.npmjs.org/@babel/helper-module-transforms/-/helper-module-transforms-7.17.7.tgz", + "integrity": "sha512-VmZD99F3gNTYB7fJRDTi+u6l/zxY0BE6OIxPSU7a50s6ZUQkHwSDmV92FfM+oCG0pZRVojGYhkR8I0OGeCVREw==", "dev": true, "dependencies": { "@babel/helper-environment-visitor": "^7.16.7", "@babel/helper-module-imports": "^7.16.7", - "@babel/helper-simple-access": "^7.16.7", + "@babel/helper-simple-access": "^7.17.7", "@babel/helper-split-export-declaration": "^7.16.7", "@babel/helper-validator-identifier": "^7.16.7", "@babel/template": "^7.16.7", - "@babel/traverse": "^7.16.7", - "@babel/types": "^7.16.7" + "@babel/traverse": "^7.17.3", + "@babel/types": "^7.17.0" }, "engines": { "node": ">=6.9.0" } }, "node_modules/@babel/helper-simple-access": { - "version": "7.16.7", - "resolved": "https://registry.npmjs.org/@babel/helper-simple-access/-/helper-simple-access-7.16.7.tgz", - "integrity": "sha512-ZIzHVyoeLMvXMN/vok/a4LWRy8G2v205mNP0XOuf9XRLyX5/u9CnVulUtDgUTama3lT+bf/UqucuZjqiGuTS1g==", + "version": "7.17.7", + "resolved": "https://registry.npmjs.org/@babel/helper-simple-access/-/helper-simple-access-7.17.7.tgz", + "integrity": "sha512-txyMCGroZ96i+Pxr3Je3lzEJjqwaRC9buMUgtomcrLe5Nd0+fk1h0LLA+ixUF5OW7AhHuQ7Es1WcQJZmZsz2XA==", "dev": true, "dependencies": { - "@babel/types": "^7.16.7" + "@babel/types": "^7.17.0" }, "engines": { "node": ">=6.9.0" @@ -233,14 +245,14 @@ } }, "node_modules/@babel/helpers": { - "version": "7.16.7", - "resolved": "https://registry.npmjs.org/@babel/helpers/-/helpers-7.16.7.tgz", - "integrity": "sha512-9ZDoqtfY7AuEOt3cxchfii6C7GDyyMBffktR5B2jvWv8u2+efwvpnVKXMWzNehqy68tKgAfSwfdw/lWpthS2bw==", + "version": "7.17.8", + "resolved": "https://registry.npmjs.org/@babel/helpers/-/helpers-7.17.8.tgz", + "integrity": "sha512-QcL86FGxpfSJwGtAvv4iG93UL6bmqBdmoVY0CMCU2g+oD2ezQse3PT5Pa+jiD6LJndBQi0EDlpzOWNlLuhz5gw==", "dev": true, "dependencies": { "@babel/template": "^7.16.7", - "@babel/traverse": "^7.16.7", - "@babel/types": "^7.16.7" + "@babel/traverse": "^7.17.3", + "@babel/types": "^7.17.0" }, "engines": { "node": ">=6.9.0" @@ -323,9 +335,9 @@ } }, "node_modules/@babel/parser": { - "version": "7.16.12", - "resolved": "https://registry.npmjs.org/@babel/parser/-/parser-7.16.12.tgz", - "integrity": "sha512-VfaV15po8RiZssrkPweyvbGVSe4x2y+aciFCgn0n0/SJMR22cwofRV1mtnJQYcSB1wUTaA/X1LnA3es66MCO5A==", + "version": "7.17.8", + "resolved": "https://registry.npmjs.org/@babel/parser/-/parser-7.17.8.tgz", + "integrity": "sha512-BoHhDJrJXqcg+ZL16Xv39H9n+AqJ4pcDrQBGZN+wHxIysrLZ3/ECwCBUch/1zUNhnsXULcONU3Ei5Hmkfk6kiQ==", "dev": true, "bin": { "parser": "bin/babel-parser.js" @@ -349,19 +361,19 @@ } }, "node_modules/@babel/traverse": { - "version": "7.16.10", - "resolved": "https://registry.npmjs.org/@babel/traverse/-/traverse-7.16.10.tgz", - "integrity": "sha512-yzuaYXoRJBGMlBhsMJoUW7G1UmSb/eXr/JHYM/MsOJgavJibLwASijW7oXBdw3NQ6T0bW7Ty5P/VarOs9cHmqw==", + "version": "7.17.3", + "resolved": "https://registry.npmjs.org/@babel/traverse/-/traverse-7.17.3.tgz", + "integrity": "sha512-5irClVky7TxRWIRtxlh2WPUUOLhcPN06AGgaQSB8AEwuyEBgJVuJ5imdHm5zxk8w0QS5T+tDfnDxAlhWjpb7cw==", "dev": true, "dependencies": { "@babel/code-frame": "^7.16.7", - "@babel/generator": "^7.16.8", + "@babel/generator": "^7.17.3", "@babel/helper-environment-visitor": "^7.16.7", "@babel/helper-function-name": "^7.16.7", "@babel/helper-hoist-variables": "^7.16.7", "@babel/helper-split-export-declaration": "^7.16.7", - "@babel/parser": "^7.16.10", - "@babel/types": "^7.16.8", + "@babel/parser": "^7.17.3", + "@babel/types": "^7.17.0", "debug": "^4.1.0", "globals": "^11.1.0" }, @@ -379,9 +391,9 @@ } }, "node_modules/@babel/types": { - "version": "7.16.8", - "resolved": "https://registry.npmjs.org/@babel/types/-/types-7.16.8.tgz", - "integrity": "sha512-smN2DQc5s4M7fntyjGtyIPbRJv6wW4rU/94fmYJ7PKQuZkC0qGMHXJbg6sNGt12JmVr4k5YaptI/XtiLJBnmIg==", + "version": "7.17.0", + "resolved": "https://registry.npmjs.org/@babel/types/-/types-7.17.0.tgz", + "integrity": "sha512-TmKSNO4D5rzhL5bjWFcVHHLETzfQ/AmbKpKPOSjlP0WoHZ6L911fgoOKY4Alp/emzG4cHJdyN49zpgkbXFEHHw==", "dev": true, "dependencies": { "@babel/helper-validator-identifier": "^7.16.7", @@ -413,16 +425,16 @@ } }, "node_modules/@eslint/eslintrc": { - "version": "1.0.5", - "resolved": "https://registry.npmjs.org/@eslint/eslintrc/-/eslintrc-1.0.5.tgz", - "integrity": "sha512-BLxsnmK3KyPunz5wmCCpqy0YelEoxxGmH73Is+Z74oOTMtExcjkr3dDR6quwrjh1YspA8DH9gnX1o069KiS9AQ==", + "version": "1.2.1", + "resolved": "https://registry.npmjs.org/@eslint/eslintrc/-/eslintrc-1.2.1.tgz", + "integrity": "sha512-bxvbYnBPN1Gibwyp6NrpnFzA3YtRL3BBAyEAFVIpNTm2Rn4Vy87GA5M4aSn3InRrlsbX5N0GW7XIx+U4SAEKdQ==", "dev": true, "dependencies": { "ajv": "^6.12.4", "debug": "^4.3.2", - "espree": "^9.2.0", + "espree": "^9.3.1", "globals": "^13.9.0", - "ignore": "^4.0.6", + "ignore": "^5.2.0", "import-fresh": "^3.2.1", "js-yaml": "^4.1.0", "minimatch": "^3.0.4", @@ -432,15 +444,6 @@ "node": "^12.22.0 || ^14.17.0 || >=16.0.0" } }, - "node_modules/@eslint/eslintrc/node_modules/ignore": { - "version": "4.0.6", - "resolved": "https://registry.npmjs.org/ignore/-/ignore-4.0.6.tgz", - "integrity": "sha512-cyFDKrqc/YdcWFniJhzI42+AzS+gNwmUzOSFcRCQYwySuBBBy/KjuxWLZ/FHEH6Moq1NizMOBWyTcv8O4OZIMg==", - "dev": true, - "engines": { - "node": ">= 4" - } - }, "node_modules/@eslint/eslintrc/node_modules/strip-json-comments": { "version": "3.1.1", "resolved": "https://registry.npmjs.org/strip-json-comments/-/strip-json-comments-3.1.1.tgz", @@ -454,9 +457,9 @@ } }, "node_modules/@humanwhocodes/config-array": { - "version": "0.9.3", - "resolved": "https://registry.npmjs.org/@humanwhocodes/config-array/-/config-array-0.9.3.tgz", - "integrity": "sha512-3xSMlXHh03hCcCmFc0rbKp3Ivt2PFEJnQUJDDMTJQ2wkECZWdq4GePs2ctc5H8zV+cHPaq8k2vU8mrQjA6iHdQ==", + "version": "0.9.5", + "resolved": "https://registry.npmjs.org/@humanwhocodes/config-array/-/config-array-0.9.5.tgz", + "integrity": "sha512-ObyMyWxZiCu/yTisA7uzx81s40xR2fD5Cg/2Kq7G02ajkNubJf6BopgDTmDyc3U7sXpNKM8cYOw7s7Tyr+DnCw==", "dev": true, "dependencies": { "@humanwhocodes/object-schema": "^1.2.1", @@ -559,6 +562,31 @@ "node": ">=8" } }, + "node_modules/@jridgewell/resolve-uri": { + "version": "3.0.5", + "resolved": "https://registry.npmjs.org/@jridgewell/resolve-uri/-/resolve-uri-3.0.5.tgz", + "integrity": "sha512-VPeQ7+wH0itvQxnG+lIzWgkysKIr3L9sslimFW55rHMdGu/qCQ5z5h9zq4gI8uBtqkpHhsF4Z/OwExufUCThew==", + "dev": true, + "engines": { + "node": ">=6.0.0" + } + }, + "node_modules/@jridgewell/sourcemap-codec": { + "version": "1.4.11", + "resolved": "https://registry.npmjs.org/@jridgewell/sourcemap-codec/-/sourcemap-codec-1.4.11.tgz", + "integrity": "sha512-Fg32GrJo61m+VqYSdRSjRXMjQ06j8YIYfcTqndLYVAaHmroZHLJZCydsWBOTDqXS2v+mjxohBWEMfg97GXmYQg==", + "dev": true + }, + "node_modules/@jridgewell/trace-mapping": { + "version": "0.3.4", + "resolved": "https://registry.npmjs.org/@jridgewell/trace-mapping/-/trace-mapping-0.3.4.tgz", + "integrity": "sha512-vFv9ttIedivx0ux3QSjhgtCVjPZd5l46ZOMDSCwnH1yUO2e964gO8LZGyv2QkqcgR6TnBU1v+1IFqmeoG+0UJQ==", + "dev": true, + "dependencies": { + "@jridgewell/resolve-uri": "^3.0.3", + "@jridgewell/sourcemap-codec": "^1.4.10" + } + }, "node_modules/@node-redis/bloom": { "resolved": "packages/bloom", "link": true @@ -632,14 +660,14 @@ } }, "node_modules/@octokit/core": { - "version": "3.5.1", - "resolved": "https://registry.npmjs.org/@octokit/core/-/core-3.5.1.tgz", - "integrity": "sha512-omncwpLVxMP+GLpLPgeGJBF6IWJFjXDS5flY5VbppePYX9XehevbDykRH9PdCdvqt9TS5AOTiDide7h0qrkHjw==", + "version": "3.6.0", + "resolved": "https://registry.npmjs.org/@octokit/core/-/core-3.6.0.tgz", + "integrity": "sha512-7RKRKuA4xTjMhY+eG3jthb3hlZCsOwg3rztWh75Xc+ShDWOfDDATWbeZpAHBNRpm4Tv9WgBMOy1zEJYXG6NJ7Q==", "dev": true, "dependencies": { "@octokit/auth-token": "^2.4.4", "@octokit/graphql": "^4.5.8", - "@octokit/request": "^5.6.0", + "@octokit/request": "^5.6.3", "@octokit/request-error": "^2.0.5", "@octokit/types": "^6.0.3", "before-after-hook": "^2.2.0", @@ -755,9 +783,9 @@ } }, "node_modules/@sindresorhus/is": { - "version": "4.4.0", - "resolved": "https://registry.npmjs.org/@sindresorhus/is/-/is-4.4.0.tgz", - "integrity": "sha512-QppPM/8l3Mawvh4rn9CNEYIU9bxpXUCRMaX9yUpvBk1nMKusLKpfXGDEKExKaPhLzcn3lzil7pR6rnJ11HgeRQ==", + "version": "4.6.0", + "resolved": "https://registry.npmjs.org/@sindresorhus/is/-/is-4.6.0.tgz", + "integrity": "sha512-t09vSN3MdfsyCHoFcTRCH/iUtG7OJ0CsjzB8cjAmKc/va/kIgeDI/TxsigdncE/4be734m0cvIYwNaV4i2XqAw==", "dev": true, "engines": { "node": ">=10" @@ -776,9 +804,9 @@ } }, "node_modules/@sinonjs/fake-timers": { - "version": "7.1.2", - "resolved": "https://registry.npmjs.org/@sinonjs/fake-timers/-/fake-timers-7.1.2.tgz", - "integrity": "sha512-iQADsW4LBMISqZ6Ci1dupJL9pprqwcVFTcOsEmQOEhW+KLCVn/Y4Jrvg2k19fIHCp+iFprriYPTdRcQR8NbUPg==", + "version": "9.1.1", + "resolved": "https://registry.npmjs.org/@sinonjs/fake-timers/-/fake-timers-9.1.1.tgz", + "integrity": "sha512-Wp5vwlZ0lOqpSYGKqr53INws9HLkt6JDc/pDZcPf7bchQnrXJMXPns8CXx0hFikMSGSWfvtvvpb2gtMVfkWagA==", "dev": true, "dependencies": { "@sinonjs/commons": "^1.7.0" @@ -856,15 +884,15 @@ "dev": true }, "node_modules/@types/json-schema": { - "version": "7.0.9", - "resolved": "https://registry.npmjs.org/@types/json-schema/-/json-schema-7.0.9.tgz", - "integrity": "sha512-qcUXuemtEu+E5wZSJHNxUXeCZhAfXKQ41D+duX+VYPde7xyEVZci+/oXKJL13tnRs9lR2pr4fod59GT6/X1/yQ==", + "version": "7.0.11", + "resolved": "https://registry.npmjs.org/@types/json-schema/-/json-schema-7.0.11.tgz", + "integrity": "sha512-wOuvG1SN4Us4rez+tylwwwCV1psiNVOkJeM3AUWUNWg/jDQY2+HE/444y5gc+jBmRqASOm2Oeh5c1axHobwRKQ==", "dev": true }, "node_modules/@types/keyv": { - "version": "3.1.3", - "resolved": "https://registry.npmjs.org/@types/keyv/-/keyv-3.1.3.tgz", - "integrity": "sha512-FXCJgyyN3ivVgRoml4h94G/p3kY+u/B86La+QptcqJaWtBWtmc6TtkNfS40n9bIvyLteHh7zXOtgbobORKPbDg==", + "version": "3.1.4", + "resolved": "https://registry.npmjs.org/@types/keyv/-/keyv-3.1.4.tgz", + "integrity": "sha512-BQ5aZNSCpj7D6K2ksrRCTmKRLEpnPvWDiLPfoGyhZ++8YtiK9d/3DBKPJgry359X/P1PfruyYwvnvwFjuEiEIg==", "dev": true, "dependencies": { "@types/node": "*" @@ -877,9 +905,9 @@ "dev": true }, "node_modules/@types/node": { - "version": "17.0.13", - "resolved": "https://registry.npmjs.org/@types/node/-/node-17.0.13.tgz", - "integrity": "sha512-Y86MAxASe25hNzlDbsviXl8jQHb0RDvKt4c40ZJQ1Don0AAL0STLZSs4N+6gLEO55pedy7r2cLwS+ZDxPm/2Bw==", + "version": "17.0.23", + "resolved": "https://registry.npmjs.org/@types/node/-/node-17.0.23.tgz", + "integrity": "sha512-UxDxWn7dl97rKVeVS61vErvw086aCYhDLyvRQZ5Rk65rZKepaFdm53GeqXaKBuOhED4e9uWq34IC3TdSdJJ2Gw==", "dev": true }, "node_modules/@types/parse-json": { @@ -914,14 +942,20 @@ } }, "node_modules/@types/sinon": { - "version": "10.0.9", - "resolved": "https://registry.npmjs.org/@types/sinon/-/sinon-10.0.9.tgz", - "integrity": "sha512-xGZVAe61omKnVGedBdTbAveuJ5QyI0LrMIcp0hc1LmVI5IEjs5qG4fM0sv9GIBA2JVoKuf7332IjQX4y5qqMMQ==", + "version": "10.0.11", + "resolved": "https://registry.npmjs.org/@types/sinon/-/sinon-10.0.11.tgz", + "integrity": "sha512-dmZsHlBsKUtBpHriNjlK0ndlvEh8dcb9uV9Afsbt89QIyydpC7NcR+nWlAhASfy3GHnxTl4FX/aKE7XZUt/B4g==", "dev": true, "dependencies": { - "@sinonjs/fake-timers": "^7.1.0" + "@types/sinonjs__fake-timers": "*" } }, + "node_modules/@types/sinonjs__fake-timers": { + "version": "8.1.2", + "resolved": "https://registry.npmjs.org/@types/sinonjs__fake-timers/-/sinonjs__fake-timers-8.1.2.tgz", + "integrity": "sha512-9GcLXF0/v3t80caGs5p2rRfkB+a8VBGLJZVih6CNFkx8IZ994wiKKLSRs9nuFwk1HevWs/1mnUmkApGrSGsShA==", + "dev": true + }, "node_modules/@types/yallist": { "version": "4.0.1", "resolved": "https://registry.npmjs.org/@types/yallist/-/yallist-4.0.1.tgz", @@ -929,29 +963,29 @@ "dev": true }, "node_modules/@types/yargs": { - "version": "17.0.8", - "resolved": "https://registry.npmjs.org/@types/yargs/-/yargs-17.0.8.tgz", - "integrity": "sha512-wDeUwiUmem9FzsyysEwRukaEdDNcwbROvQ9QGRKaLI6t+IltNzbn4/i4asmB10auvZGQCzSQ6t0GSczEThlUXw==", + "version": "17.0.10", + "resolved": "https://registry.npmjs.org/@types/yargs/-/yargs-17.0.10.tgz", + "integrity": "sha512-gmEaFwpj/7f/ROdtIlci1R1VYU1J4j95m8T+Tj3iBgiBFKg1foE/PSl93bBd5T9LDXNPo8UlNN6W0qwD8O5OaA==", "dev": true, "dependencies": { "@types/yargs-parser": "*" } }, "node_modules/@types/yargs-parser": { - "version": "20.2.1", - "resolved": "https://registry.npmjs.org/@types/yargs-parser/-/yargs-parser-20.2.1.tgz", - "integrity": "sha512-7tFImggNeNBVMsn0vLrpn1H1uPrUBdnARPTpZoitY37ZrdJREzf7I16tMrlK3hen349gr1NYh8CmZQa7CTG6Aw==", + "version": "21.0.0", + "resolved": "https://registry.npmjs.org/@types/yargs-parser/-/yargs-parser-21.0.0.tgz", + "integrity": "sha512-iO9ZQHkZxHn4mSakYV0vFHAVDyEOIJQrV2uZ06HxEPcx+mt8swXoZHIbaaJ2crJYFfErySgktuTZ3BeLz+XmFA==", "dev": true }, "node_modules/@typescript-eslint/eslint-plugin": { - "version": "5.10.1", - "resolved": "https://registry.npmjs.org/@typescript-eslint/eslint-plugin/-/eslint-plugin-5.10.1.tgz", - "integrity": "sha512-xN3CYqFlyE/qOcy978/L0xLR2HlcAGIyIK5sMOasxaaAPfQRj/MmMV6OC3I7NZO84oEUdWCOju34Z9W8E0pFDQ==", + "version": "5.16.0", + "resolved": "https://registry.npmjs.org/@typescript-eslint/eslint-plugin/-/eslint-plugin-5.16.0.tgz", + "integrity": "sha512-SJoba1edXvQRMmNI505Uo4XmGbxCK9ARQpkvOd00anxzri9RNQk0DDCxD+LIl+jYhkzOJiOMMKYEHnHEODjdCw==", "dev": true, "dependencies": { - "@typescript-eslint/scope-manager": "5.10.1", - "@typescript-eslint/type-utils": "5.10.1", - "@typescript-eslint/utils": "5.10.1", + "@typescript-eslint/scope-manager": "5.16.0", + "@typescript-eslint/type-utils": "5.16.0", + "@typescript-eslint/utils": "5.16.0", "debug": "^4.3.2", "functional-red-black-tree": "^1.0.1", "ignore": "^5.1.8", @@ -992,14 +1026,14 @@ } }, "node_modules/@typescript-eslint/parser": { - "version": "5.10.1", - "resolved": "https://registry.npmjs.org/@typescript-eslint/parser/-/parser-5.10.1.tgz", - "integrity": "sha512-GReo3tjNBwR5RnRO0K2wDIDN31cM3MmDtgyQ85oAxAmC5K3j/g85IjP+cDfcqDsDDBf1HNKQAD0WqOYL8jXqUA==", + "version": "5.16.0", + "resolved": "https://registry.npmjs.org/@typescript-eslint/parser/-/parser-5.16.0.tgz", + "integrity": "sha512-fkDq86F0zl8FicnJtdXakFs4lnuebH6ZADDw6CYQv0UZeIjHvmEw87m9/29nk2Dv5Lmdp0zQ3zDQhiMWQf/GbA==", "dev": true, "dependencies": { - "@typescript-eslint/scope-manager": "5.10.1", - "@typescript-eslint/types": "5.10.1", - "@typescript-eslint/typescript-estree": "5.10.1", + "@typescript-eslint/scope-manager": "5.16.0", + "@typescript-eslint/types": "5.16.0", + "@typescript-eslint/typescript-estree": "5.16.0", "debug": "^4.3.2" }, "engines": { @@ -1019,13 +1053,13 @@ } }, "node_modules/@typescript-eslint/scope-manager": { - "version": "5.10.1", - "resolved": "https://registry.npmjs.org/@typescript-eslint/scope-manager/-/scope-manager-5.10.1.tgz", - "integrity": "sha512-Lyvi559Gvpn94k7+ElXNMEnXu/iundV5uFmCUNnftbFrUbAJ1WBoaGgkbOBm07jVZa682oaBU37ao/NGGX4ZDg==", + "version": "5.16.0", + "resolved": "https://registry.npmjs.org/@typescript-eslint/scope-manager/-/scope-manager-5.16.0.tgz", + "integrity": "sha512-P+Yab2Hovg8NekLIR/mOElCDPyGgFZKhGoZA901Yax6WR6HVeGLbsqJkZ+Cvk5nts/dAlFKm8PfL43UZnWdpIQ==", "dev": true, "dependencies": { - "@typescript-eslint/types": "5.10.1", - "@typescript-eslint/visitor-keys": "5.10.1" + "@typescript-eslint/types": "5.16.0", + "@typescript-eslint/visitor-keys": "5.16.0" }, "engines": { "node": "^12.22.0 || ^14.17.0 || >=16.0.0" @@ -1036,12 +1070,12 @@ } }, "node_modules/@typescript-eslint/type-utils": { - "version": "5.10.1", - "resolved": "https://registry.npmjs.org/@typescript-eslint/type-utils/-/type-utils-5.10.1.tgz", - "integrity": "sha512-AfVJkV8uck/UIoDqhu+ptEdBoQATON9GXnhOpPLzkQRJcSChkvD//qsz9JVffl2goxX+ybs5klvacE9vmrQyCw==", + "version": "5.16.0", + "resolved": "https://registry.npmjs.org/@typescript-eslint/type-utils/-/type-utils-5.16.0.tgz", + "integrity": "sha512-SKygICv54CCRl1Vq5ewwQUJV/8padIWvPgCxlWPGO/OgQLCijY9G7lDu6H+mqfQtbzDNlVjzVWQmeqbLMBLEwQ==", "dev": true, "dependencies": { - "@typescript-eslint/utils": "5.10.1", + "@typescript-eslint/utils": "5.16.0", "debug": "^4.3.2", "tsutils": "^3.21.0" }, @@ -1062,9 +1096,9 @@ } }, "node_modules/@typescript-eslint/types": { - "version": "5.10.1", - "resolved": "https://registry.npmjs.org/@typescript-eslint/types/-/types-5.10.1.tgz", - "integrity": "sha512-ZvxQ2QMy49bIIBpTqFiOenucqUyjTQ0WNLhBM6X1fh1NNlYAC6Kxsx8bRTY3jdYsYg44a0Z/uEgQkohbR0H87Q==", + "version": "5.16.0", + "resolved": "https://registry.npmjs.org/@typescript-eslint/types/-/types-5.16.0.tgz", + "integrity": "sha512-oUorOwLj/3/3p/HFwrp6m/J2VfbLC8gjW5X3awpQJ/bSG+YRGFS4dpsvtQ8T2VNveV+LflQHjlLvB6v0R87z4g==", "dev": true, "engines": { "node": "^12.22.0 || ^14.17.0 || >=16.0.0" @@ -1075,13 +1109,13 @@ } }, "node_modules/@typescript-eslint/typescript-estree": { - "version": "5.10.1", - "resolved": "https://registry.npmjs.org/@typescript-eslint/typescript-estree/-/typescript-estree-5.10.1.tgz", - "integrity": "sha512-PwIGnH7jIueXv4opcwEbVGDATjGPO1dx9RkUl5LlHDSe+FXxPwFL5W/qYd5/NHr7f6lo/vvTrAzd0KlQtRusJQ==", + "version": "5.16.0", + "resolved": "https://registry.npmjs.org/@typescript-eslint/typescript-estree/-/typescript-estree-5.16.0.tgz", + "integrity": "sha512-SE4VfbLWUZl9MR+ngLSARptUv2E8brY0luCdgmUevU6arZRY/KxYoLI/3V/yxaURR8tLRN7bmZtJdgmzLHI6pQ==", "dev": true, "dependencies": { - "@typescript-eslint/types": "5.10.1", - "@typescript-eslint/visitor-keys": "5.10.1", + "@typescript-eslint/types": "5.16.0", + "@typescript-eslint/visitor-keys": "5.16.0", "debug": "^4.3.2", "globby": "^11.0.4", "is-glob": "^4.0.3", @@ -1146,15 +1180,15 @@ } }, "node_modules/@typescript-eslint/utils": { - "version": "5.10.1", - "resolved": "https://registry.npmjs.org/@typescript-eslint/utils/-/utils-5.10.1.tgz", - "integrity": "sha512-RRmlITiUbLuTRtn/gcPRi4202niF+q7ylFLCKu4c+O/PcpRvZ/nAUwQ2G00bZgpWkhrNLNnvhZLbDn8Ml0qsQw==", + "version": "5.16.0", + "resolved": "https://registry.npmjs.org/@typescript-eslint/utils/-/utils-5.16.0.tgz", + "integrity": "sha512-iYej2ER6AwmejLWMWzJIHy3nPJeGDuCqf8Jnb+jAQVoPpmWzwQOfa9hWVB8GIQE5gsCv/rfN4T+AYb/V06WseQ==", "dev": true, "dependencies": { "@types/json-schema": "^7.0.9", - "@typescript-eslint/scope-manager": "5.10.1", - "@typescript-eslint/types": "5.10.1", - "@typescript-eslint/typescript-estree": "5.10.1", + "@typescript-eslint/scope-manager": "5.16.0", + "@typescript-eslint/types": "5.16.0", + "@typescript-eslint/typescript-estree": "5.16.0", "eslint-scope": "^5.1.1", "eslint-utils": "^3.0.0" }, @@ -1170,12 +1204,12 @@ } }, "node_modules/@typescript-eslint/visitor-keys": { - "version": "5.10.1", - "resolved": "https://registry.npmjs.org/@typescript-eslint/visitor-keys/-/visitor-keys-5.10.1.tgz", - "integrity": "sha512-NjQ0Xinhy9IL979tpoTRuLKxMc0zJC7QVSdeerXs2/QvOy2yRkzX5dRb10X5woNUdJgU8G3nYRDlI33sq1K4YQ==", + "version": "5.16.0", + "resolved": "https://registry.npmjs.org/@typescript-eslint/visitor-keys/-/visitor-keys-5.16.0.tgz", + "integrity": "sha512-jqxO8msp5vZDhikTwq9ubyMHqZ67UIvawohr4qF3KhlpL7gzSjOd+8471H3nh5LyABkaI85laEKKU8SnGUK5/g==", "dev": true, "dependencies": { - "@typescript-eslint/types": "5.10.1", + "@typescript-eslint/types": "5.16.0", "eslint-visitor-keys": "^3.0.0" }, "engines": { @@ -1511,15 +1545,25 @@ "dev": true }, "node_modules/browserslist": { - "version": "4.19.1", - "resolved": "https://registry.npmjs.org/browserslist/-/browserslist-4.19.1.tgz", - "integrity": "sha512-u2tbbG5PdKRTUoctO3NBD8FQ5HdPh1ZXPHzp1rwaa5jTc+RV9/+RlWiAIKmjRPQF+xbGM9Kklj5bZQFa2s/38A==", + "version": "4.20.2", + "resolved": "https://registry.npmjs.org/browserslist/-/browserslist-4.20.2.tgz", + "integrity": "sha512-CQOBCqp/9pDvDbx3xfMi+86pr4KXIf2FDkTTdeuYw8OxS9t898LA1Khq57gtufFILXpfgsSx5woNgsBgvGjpsA==", "dev": true, + "funding": [ + { + "type": "opencollective", + "url": "https://opencollective.com/browserslist" + }, + { + "type": "tidelift", + "url": "https://tidelift.com/funding/github/npm/browserslist" + } + ], "dependencies": { - "caniuse-lite": "^1.0.30001286", - "electron-to-chromium": "^1.4.17", + "caniuse-lite": "^1.0.30001317", + "electron-to-chromium": "^1.4.84", "escalade": "^3.1.1", - "node-releases": "^2.0.1", + "node-releases": "^2.0.2", "picocolors": "^1.0.0" }, "bin": { @@ -1527,10 +1571,6 @@ }, "engines": { "node": "^6 || ^7 || ^8 || ^9 || ^10 || ^11 || ^12 || >=13.7" - }, - "funding": { - "type": "opencollective", - "url": "https://opencollective.com/browserslist" } }, "node_modules/buffer": { @@ -1655,14 +1695,20 @@ } }, "node_modules/caniuse-lite": { - "version": "1.0.30001304", - "resolved": "https://registry.npmjs.org/caniuse-lite/-/caniuse-lite-1.0.30001304.tgz", - "integrity": "sha512-bdsfZd6K6ap87AGqSHJP/s1V+U6Z5lyrcbBu3ovbCCf8cSYpwTtGrCBObMpJqwxfTbLW6YTIdbb1jEeTelcpYQ==", + "version": "1.0.30001320", + "resolved": "https://registry.npmjs.org/caniuse-lite/-/caniuse-lite-1.0.30001320.tgz", + "integrity": "sha512-MWPzG54AGdo3nWx7zHZTefseM5Y1ccM7hlQKHRqJkPozUaw3hNbBTMmLn16GG2FUzjR13Cr3NPfhIieX5PzXDA==", "dev": true, - "funding": { - "type": "opencollective", - "url": "https://opencollective.com/browserslist" - } + "funding": [ + { + "type": "opencollective", + "url": "https://opencollective.com/browserslist" + }, + { + "type": "tidelift", + "url": "https://tidelift.com/funding/github/npm/caniuse-lite" + } + ] }, "node_modules/chalk": { "version": "4.1.2", @@ -2109,9 +2155,9 @@ "dev": true }, "node_modules/electron-to-chromium": { - "version": "1.4.57", - "resolved": "https://registry.npmjs.org/electron-to-chromium/-/electron-to-chromium-1.4.57.tgz", - "integrity": "sha512-FNC+P5K1n6pF+M0zIK+gFCoXcJhhzDViL3DRIGy2Fv5PohuSES1JHR7T+GlwxSxlzx4yYbsuzCZvHxcBSRCIOw==", + "version": "1.4.96", + "resolved": "https://registry.npmjs.org/electron-to-chromium/-/electron-to-chromium-1.4.96.tgz", + "integrity": "sha512-DPNjvNGPabv6FcyjzLAN4C0psN/GgD9rSGvMTuv81SeXG/EX3mCz0wiw9N1tUEnfQXYCJi3H8M0oFPRziZh7rw==", "dev": true }, "node_modules/email-addresses": { @@ -2178,12 +2224,12 @@ } }, "node_modules/eslint": { - "version": "8.8.0", - "resolved": "https://registry.npmjs.org/eslint/-/eslint-8.8.0.tgz", - "integrity": "sha512-H3KXAzQGBH1plhYS3okDix2ZthuYJlQQEGE5k0IKuEqUSiyu4AmxxlJ2MtTYeJ3xB4jDhcYCwGOg2TXYdnDXlQ==", + "version": "8.12.0", + "resolved": "https://registry.npmjs.org/eslint/-/eslint-8.12.0.tgz", + "integrity": "sha512-it1oBL9alZg1S8UycLm5YDMAkIhtH6FtAzuZs6YvoGVldWjbS08BkAdb/ymP9LlAyq8koANu32U7Ib/w+UNh8Q==", "dev": true, "dependencies": { - "@eslint/eslintrc": "^1.0.5", + "@eslint/eslintrc": "^1.2.1", "@humanwhocodes/config-array": "^0.9.2", "ajv": "^6.10.0", "chalk": "^4.0.0", @@ -2191,10 +2237,10 @@ "debug": "^4.3.2", "doctrine": "^3.0.0", "escape-string-regexp": "^4.0.0", - "eslint-scope": "^7.1.0", + "eslint-scope": "^7.1.1", "eslint-utils": "^3.0.0", - "eslint-visitor-keys": "^3.2.0", - "espree": "^9.3.0", + "eslint-visitor-keys": "^3.3.0", + "espree": "^9.3.1", "esquery": "^1.4.0", "esutils": "^2.0.2", "fast-deep-equal": "^3.1.3", @@ -2270,9 +2316,9 @@ } }, "node_modules/eslint-visitor-keys": { - "version": "3.2.0", - "resolved": "https://registry.npmjs.org/eslint-visitor-keys/-/eslint-visitor-keys-3.2.0.tgz", - "integrity": "sha512-IOzT0X126zn7ALX0dwFiUQEdsfzrm4+ISsQS8nukaJXwEyYKRSnEIIDULYg1mCtGp7UUXgfGl7BIolXREQK+XQ==", + "version": "3.3.0", + "resolved": "https://registry.npmjs.org/eslint-visitor-keys/-/eslint-visitor-keys-3.3.0.tgz", + "integrity": "sha512-mQ+suqKJVyeuwGYHAdjMFqjCyfl8+Ldnxuyp3ldiMBFKkvytrXUZWaiPCEav8qDHKty44bD+qV1IP4T+w+xXRA==", "dev": true, "engines": { "node": "^12.22.0 || ^14.17.0 || >=16.0.0" @@ -2291,9 +2337,9 @@ } }, "node_modules/eslint/node_modules/eslint-scope": { - "version": "7.1.0", - "resolved": "https://registry.npmjs.org/eslint-scope/-/eslint-scope-7.1.0.tgz", - "integrity": "sha512-aWwkhnS0qAXqNOgKOK0dJ2nvzEbhEvpy8OlJ9kZ0FeZnA6zpjv1/Vei+puGFFX7zkPCkHHXb7IDX3A+7yPrRWg==", + "version": "7.1.1", + "resolved": "https://registry.npmjs.org/eslint-scope/-/eslint-scope-7.1.1.tgz", + "integrity": "sha512-QKQM/UXpIiHcLqJ5AOyIW7XZmzjkzQXYE54n1++wb0u9V/abW3l9uQnxX8Z5Xd18xyKIMTUAyQ0k1e8pz6LUrw==", "dev": true, "dependencies": { "esrecurse": "^4.3.0", @@ -2337,14 +2383,14 @@ } }, "node_modules/espree": { - "version": "9.3.0", - "resolved": "https://registry.npmjs.org/espree/-/espree-9.3.0.tgz", - "integrity": "sha512-d/5nCsb0JcqsSEeQzFZ8DH1RmxPcglRWh24EFTlUEmCKoehXGdpsx0RkHDubqUI8LSAIKMQp4r9SzQ3n+sm4HQ==", + "version": "9.3.1", + "resolved": "https://registry.npmjs.org/espree/-/espree-9.3.1.tgz", + "integrity": "sha512-bvdyLmJMfwkV3NCRl5ZhJf22zBFo1y8bYh3VYb+bfzqNB4Je68P2sSuXyuFquzWLebHpNd2/d5uv7yoP9ISnGQ==", "dev": true, "dependencies": { "acorn": "^8.7.0", "acorn-jsx": "^5.3.1", - "eslint-visitor-keys": "^3.1.0" + "eslint-visitor-keys": "^3.3.0" }, "engines": { "node": "^12.22.0 || ^14.17.0 || >=16.0.0" @@ -2878,9 +2924,9 @@ } }, "node_modules/globals": { - "version": "13.12.0", - "resolved": "https://registry.npmjs.org/globals/-/globals-13.12.0.tgz", - "integrity": "sha512-uS8X6lSKN2JumVoXrbUz+uG4BYG+eiawqm3qFcT7ammfbUHeCBoJMlHcec/S3krSk73/AE/f0szYFmgAA3kYZg==", + "version": "13.13.0", + "resolved": "https://registry.npmjs.org/globals/-/globals-13.13.0.tgz", + "integrity": "sha512-EQ7Q18AJlPwp3vUDL4mKA0KXrXyNIQyWon6T6XQiBQF0XHvRsiCSrWmmeATpUzdJN2HhWZU6Pdl0a9zdep5p6A==", "dev": true, "dependencies": { "type-fest": "^0.20.2" @@ -2982,9 +3028,9 @@ } }, "node_modules/has-symbols": { - "version": "1.0.2", - "resolved": "https://registry.npmjs.org/has-symbols/-/has-symbols-1.0.2.tgz", - "integrity": "sha512-chXa79rL/UC2KlX17jo3vRGz0azaWEx5tGqZg5pO3NUyEJVB17dMruQlzCCOfUvElghKcm5194+BCRvi2Rv/Gw==", + "version": "1.0.3", + "resolved": "https://registry.npmjs.org/has-symbols/-/has-symbols-1.0.3.tgz", + "integrity": "sha512-l3LCuF6MgDNwTDKkdYGEihYjt5pRPbEg46rtlmnSPlUbgmB8LOIrKJbYYFBSbnPaJexMKtiPO8hmeRjRz2Td+A==", "dev": true, "engines": { "node": ">= 0.4" @@ -3595,9 +3641,9 @@ } }, "node_modules/istanbul-reports": { - "version": "3.1.3", - "resolved": "https://registry.npmjs.org/istanbul-reports/-/istanbul-reports-3.1.3.tgz", - "integrity": "sha512-x9LtDVtfm/t1GFiLl3NffC7hz+I1ragvgX1P/Lg1NlIagifZDKUkuuaAxH/qpwj2IuEfD8G2Bs/UKp+sZ/pKkg==", + "version": "3.1.4", + "resolved": "https://registry.npmjs.org/istanbul-reports/-/istanbul-reports-3.1.4.tgz", + "integrity": "sha512-r1/DshN4KSE7xWEknZLLLLDn5CJybV3nw01VTkp6D5jzLuELlcbudfj/eSQFvrKsJuTVCGnePO7ho82Nw9zzfw==", "dev": true, "dependencies": { "html-escaper": "^2.0.0", @@ -3662,13 +3708,10 @@ "dev": true }, "node_modules/json5": { - "version": "2.2.0", - "resolved": "https://registry.npmjs.org/json5/-/json5-2.2.0.tgz", - "integrity": "sha512-f+8cldu7X/y7RAJurMEJmdoKXGB/X550w2Nr3tTbezL6RwEE/iMcm+tZnXeoZtKuOq6ft8+CqzEkrIgx1fPoQA==", + "version": "2.2.1", + "resolved": "https://registry.npmjs.org/json5/-/json5-2.2.1.tgz", + "integrity": "sha512-1hqLFMSrGHRHxav9q9gNjJ5EXznIxGVO09xQRrwplcS8qs28pZ8s8hupZAmqDwZUmVZ2Qb2jnyPOWcDH8m8dlA==", "dev": true, - "dependencies": { - "minimist": "^1.2.5" - }, "bin": { "json5": "lib/cli.js" }, @@ -3698,9 +3741,9 @@ "dev": true }, "node_modules/keyv": { - "version": "4.0.5", - "resolved": "https://registry.npmjs.org/keyv/-/keyv-4.0.5.tgz", - "integrity": "sha512-531pkGLqV3BMg0eDqqJFI0R1mkK1Nm5xIP2mM6keP5P8WfFtCkg2IOwplTUmlGoTgIg9yQYZ/kdihhz89XH3vA==", + "version": "4.1.1", + "resolved": "https://registry.npmjs.org/keyv/-/keyv-4.1.1.tgz", + "integrity": "sha512-tGv1yP6snQVDSM4X6yxrv2zzq/EvpW+oYiUz6aueW1u9CtS8RzUQYxxmFwgZlO2jSgCxQbchhxaqXXp2hnKGpQ==", "dev": true, "dependencies": { "json-buffer": "3.0.1" @@ -3877,13 +3920,13 @@ } }, "node_modules/micromatch": { - "version": "4.0.4", - "resolved": "https://registry.npmjs.org/micromatch/-/micromatch-4.0.4.tgz", - "integrity": "sha512-pRmzw/XUcwXGpD9aI9q/0XOwLNygjETJ8y0ao0wdqprrzDa4YnxLcz7fQRZr8voh8V10kGhABbNcHVk5wHgWwg==", + "version": "4.0.5", + "resolved": "https://registry.npmjs.org/micromatch/-/micromatch-4.0.5.tgz", + "integrity": "sha512-DMy+ERcEW2q8Z2Po+WNXuw3c5YaUSFjAO5GsJqfEl7UjvtIuFKO6ZrKvcItdy98dwFI2N1tg3zNIdKaQT+aNdA==", "dev": true, "dependencies": { - "braces": "^3.0.1", - "picomatch": "^2.2.3" + "braces": "^3.0.2", + "picomatch": "^2.3.1" }, "engines": { "node": ">=8.6" @@ -3929,9 +3972,9 @@ } }, "node_modules/minimatch": { - "version": "3.0.4", - "resolved": "https://registry.npmjs.org/minimatch/-/minimatch-3.0.4.tgz", - "integrity": "sha512-yJHVQEhyqPLUTgt9B83PXu6W3rx4MvvHvSUvToogpwoGDOUQ+yDrR0HRot+yOCdCO7u4hX3pWft6kWBBcqh0UA==", + "version": "3.1.2", + "resolved": "https://registry.npmjs.org/minimatch/-/minimatch-3.1.2.tgz", + "integrity": "sha512-J7p63hRiAjw1NDEww1W7i37+ByIrOWO5XQQAzZ3VOcL0PNybwpfmV/N05zFAzwQ9USyEcX6t3UO+K5aqBQOIHw==", "dev": true, "dependencies": { "brace-expansion": "^1.1.7" @@ -3941,15 +3984,15 @@ } }, "node_modules/minimist": { - "version": "1.2.5", - "resolved": "https://registry.npmjs.org/minimist/-/minimist-1.2.5.tgz", - "integrity": "sha512-FM9nNUYrRBAELZQT3xeZQ7fmMOBg6nWNmJKTcgsJeaLstP/UODVpGsr5OhXhhXg6f+qtJ8uiZ+PUxkDWcgIXLw==", + "version": "1.2.6", + "resolved": "https://registry.npmjs.org/minimist/-/minimist-1.2.6.tgz", + "integrity": "sha512-Jsjnk4bw3YJqYzbdyBiNsPWHPfO++UGG749Cxs6peCu5Xg4nrena6OVxOYxrQTqww0Jmwt+Ref8rggumkTLz9Q==", "dev": true }, "node_modules/mocha": { - "version": "9.2.0", - "resolved": "https://registry.npmjs.org/mocha/-/mocha-9.2.0.tgz", - "integrity": "sha512-kNn7E8g2SzVcq0a77dkphPsDSN7P+iYkqE0ZsGCYWRsoiKjOt+NvXfaagik8vuDa6W5Zw3qxe8Jfpt5qKf+6/Q==", + "version": "9.2.2", + "resolved": "https://registry.npmjs.org/mocha/-/mocha-9.2.2.tgz", + "integrity": "sha512-L6XC3EdwT6YrIk0yXpavvLkn8h+EU+Y5UcCHKECyMbdUIxyMuZj4bX4U9e1nvnvUUvQVsV2VHQr5zLdcUkhW/g==", "dev": true, "dependencies": { "@ungap/promise-all-settled": "1.1.2", @@ -3965,9 +4008,9 @@ "he": "1.2.0", "js-yaml": "4.1.0", "log-symbols": "4.1.0", - "minimatch": "3.0.4", + "minimatch": "4.2.1", "ms": "2.1.3", - "nanoid": "3.2.0", + "nanoid": "3.3.1", "serialize-javascript": "6.0.0", "strip-json-comments": "3.1.1", "supports-color": "8.1.1", @@ -4032,6 +4075,18 @@ "url": "https://github.com/sponsors/sindresorhus" } }, + "node_modules/mocha/node_modules/minimatch": { + "version": "4.2.1", + "resolved": "https://registry.npmjs.org/minimatch/-/minimatch-4.2.1.tgz", + "integrity": "sha512-9Uq1ChtSZO+Mxa/CL1eGizn2vRn3MlLgzhT0Iz8zaY8NdvxvB0d5QdPFmCKf7JKA9Lerx5vRrnwO03jsSfGG9g==", + "dev": true, + "dependencies": { + "brace-expansion": "^1.1.7" + }, + "engines": { + "node": ">=10" + } + }, "node_modules/mocha/node_modules/ms": { "version": "2.1.3", "resolved": "https://registry.npmjs.org/ms/-/ms-2.1.3.tgz", @@ -4135,9 +4190,9 @@ "dev": true }, "node_modules/nanoid": { - "version": "3.2.0", - "resolved": "https://registry.npmjs.org/nanoid/-/nanoid-3.2.0.tgz", - "integrity": "sha512-fmsZYa9lpn69Ad5eDn7FMcnnSR+8R34W9qJEijxYhTbfOWzr22n1QxCMzXLK+ODyW2973V3Fux959iQoUxzUIA==", + "version": "3.3.1", + "resolved": "https://registry.npmjs.org/nanoid/-/nanoid-3.3.1.tgz", + "integrity": "sha512-n6Vs/3KGyxPQd6uO0eH4Bv0ojGSUvuLlIHtC3Y0kEO23YRge8H9x1GCzLn28YX0H66pMkxuaeESFq4tKISKwdw==", "dev": true, "bin": { "nanoid": "bin/nanoid.cjs" @@ -4174,13 +4229,13 @@ } }, "node_modules/nise": { - "version": "5.1.0", - "resolved": "https://registry.npmjs.org/nise/-/nise-5.1.0.tgz", - "integrity": "sha512-W5WlHu+wvo3PaKLsJJkgPup2LrsXCcm7AWwyNZkUnn5rwPkuPBi3Iwk5SQtN0mv+K65k7nKKjwNQ30wg3wLAQQ==", + "version": "5.1.1", + "resolved": "https://registry.npmjs.org/nise/-/nise-5.1.1.tgz", + "integrity": "sha512-yr5kW2THW1AkxVmCnKEh4nbYkJdB3I7LUkiUgOvEkOp414mc2UMaHMA7pjq1nYowhdoJZGwEKGaQVbxfpWj10A==", "dev": true, "dependencies": { - "@sinonjs/commons": "^1.7.0", - "@sinonjs/fake-timers": "^7.0.4", + "@sinonjs/commons": "^1.8.3", + "@sinonjs/fake-timers": ">=5", "@sinonjs/text-encoding": "^0.7.1", "just-extend": "^4.0.2", "path-to-regexp": "^1.7.0" @@ -4219,9 +4274,9 @@ } }, "node_modules/node-releases": { - "version": "2.0.1", - "resolved": "https://registry.npmjs.org/node-releases/-/node-releases-2.0.1.tgz", - "integrity": "sha512-CqyzN6z7Q6aMeF/ktcMVTzhAHCEpf8SOarwpzpf8pNBY2k5/oM34UHldUwp8VKI7uxct2HxSRdJjBaZeESzcxA==", + "version": "2.0.2", + "resolved": "https://registry.npmjs.org/node-releases/-/node-releases-2.0.2.tgz", + "integrity": "sha512-XxYDdcQ6eKqp/YjI+tb2C5WM2LgjnZrfYg4vgQt49EK268b6gYCHsBLrK2qvJo4FmCtqmKezb0WZFK4fkrZNsg==", "dev": true }, "node_modules/normalize-path": { @@ -5179,9 +5234,9 @@ } }, "node_modules/release-it": { - "version": "14.12.4", - "resolved": "https://registry.npmjs.org/release-it/-/release-it-14.12.4.tgz", - "integrity": "sha512-lqf9PMsj7ycCqFHGag8Uv7cE1hNsKa+yKUMe+Fkh9fdOfxu2F01On+YUefRCP0DuQthmr/WyLCYdrjThMEkWFQ==", + "version": "14.13.1", + "resolved": "https://registry.npmjs.org/release-it/-/release-it-14.13.1.tgz", + "integrity": "sha512-mrng5bqZDFMr/7oCH3kuflwjKpKki4dUp6yYGxs20scYCvvd8rHAI5pdQOJHwI5BKHAC/pad0UjAEycMWQnEIw==", "dev": true, "dependencies": { "@iarna/toml": "2.2.5", @@ -5210,6 +5265,7 @@ "update-notifier": "5.1.0", "url-join": "4.0.1", "uuid": "8.3.2", + "wildcard-match": "5.1.2", "yaml": "1.10.2", "yargs-parser": "20.2.9" }, @@ -5412,9 +5468,9 @@ } }, "node_modules/rxjs": { - "version": "7.5.2", - "resolved": "https://registry.npmjs.org/rxjs/-/rxjs-7.5.2.tgz", - "integrity": "sha512-PwDt186XaL3QN5qXj/H9DGyHhP3/RYYgZZwqBv9Tv8rsAaiwFH1IsJJlcgD37J7UW5a6O67qX0KWKS3/pu0m4w==", + "version": "7.5.5", + "resolved": "https://registry.npmjs.org/rxjs/-/rxjs-7.5.5.tgz", + "integrity": "sha512-sy+H0pQofO95VDmFLzyaw9xNJU4KTRSwQIGM6+iG3SypAtCiLDzpeG8sJrNCWn2Up9km+KhkvTdbkrdy+yzZdw==", "dev": true, "dependencies": { "tslib": "^2.1.0" @@ -5521,9 +5577,9 @@ } }, "node_modules/shiki": { - "version": "0.10.0", - "resolved": "https://registry.npmjs.org/shiki/-/shiki-0.10.0.tgz", - "integrity": "sha512-iczxaIYeBFHTFrQPb9DVy2SKgYxC4Wo7Iucm7C17cCh2Ge/refnvHscUOxM85u57MfLoNOtjoEFUWt9gBexblA==", + "version": "0.10.1", + "resolved": "https://registry.npmjs.org/shiki/-/shiki-0.10.1.tgz", + "integrity": "sha512-VsY7QJVzU51j5o1+DguUd+6vmCmZ5v/6gYu4vyYAhzjuNQU6P/vmSy4uQaOhvje031qQMiW0d2BwgMH52vqMng==", "dev": true, "dependencies": { "jsonc-parser": "^3.0.0", @@ -5546,22 +5602,22 @@ } }, "node_modules/signal-exit": { - "version": "3.0.6", - "resolved": "https://registry.npmjs.org/signal-exit/-/signal-exit-3.0.6.tgz", - "integrity": "sha512-sDl4qMFpijcGw22U5w63KmD3cZJfBuFlVNbVMKje2keoKML7X2UzWbc4XrmEbDwg0NXJc3yv4/ox7b+JWb57kQ==", + "version": "3.0.7", + "resolved": "https://registry.npmjs.org/signal-exit/-/signal-exit-3.0.7.tgz", + "integrity": "sha512-wnD2ZE+l+SPC/uoS0vXeE9L1+0wuaMqKlfz9AMUo38JsyLSBWSFcHR1Rri62LZc12vLr1gb3jl7iwQhgwpAbGQ==", "dev": true }, "node_modules/sinon": { - "version": "13.0.0", - "resolved": "https://registry.npmjs.org/sinon/-/sinon-13.0.0.tgz", - "integrity": "sha512-3tjMDB/tY04b06Bnb4aMKQfNrau2C9HET+R4HVWfv2KegDVLGg4wnBqjVepvxR7S7R1GTwDZzEv52tpFipt6yA==", + "version": "13.0.1", + "resolved": "https://registry.npmjs.org/sinon/-/sinon-13.0.1.tgz", + "integrity": "sha512-8yx2wIvkBjIq/MGY1D9h1LMraYW+z1X0mb648KZnKSdvLasvDu7maa0dFaNYdTDczFgbjNw2tOmWdTk9saVfwQ==", "dev": true, "dependencies": { "@sinonjs/commons": "^1.8.3", "@sinonjs/fake-timers": "^9.0.0", "@sinonjs/samsam": "^6.1.1", "diff": "^5.0.0", - "nise": "^5.1.0", + "nise": "^5.1.1", "supports-color": "^7.2.0" }, "funding": { @@ -5569,15 +5625,6 @@ "url": "https://opencollective.com/sinon" } }, - "node_modules/sinon/node_modules/@sinonjs/fake-timers": { - "version": "9.0.0", - "resolved": "https://registry.npmjs.org/@sinonjs/fake-timers/-/fake-timers-9.0.0.tgz", - "integrity": "sha512-+shXA2X7KNP7H7qNbQTJ3SA+NQc0pZDSBrdvFSRwF8sAo/ohw+ZQFD8Moc+gnz51+1eRXtEQBpKWPiQ4jsRC/w==", - "dev": true, - "dependencies": { - "@sinonjs/commons": "^1.7.0" - } - }, "node_modules/slash": { "version": "3.0.0", "resolved": "https://registry.npmjs.org/slash/-/slash-3.0.0.tgz", @@ -5841,9 +5888,9 @@ } }, "node_modules/ts-node": { - "version": "10.4.0", - "resolved": "https://registry.npmjs.org/ts-node/-/ts-node-10.4.0.tgz", - "integrity": "sha512-g0FlPvvCXSIO1JDF6S232P5jPYqBkRL9qly81ZgAOSU7rwI0stphCgd2kLiCrU9DjQCrJMWEqcNSjQL02s6d8A==", + "version": "10.7.0", + "resolved": "https://registry.npmjs.org/ts-node/-/ts-node-10.7.0.tgz", + "integrity": "sha512-TbIGS4xgJoX2i3do417KSaep1uRAW/Lu+WAL2doDHC0D6ummjirVOXU5/7aiZotbQ5p1Zp9tP7U6cYhA0O7M8A==", "dev": true, "dependencies": { "@cspotcode/source-map-support": "0.7.0", @@ -5857,11 +5904,13 @@ "create-require": "^1.1.0", "diff": "^4.0.1", "make-error": "^1.1.1", + "v8-compile-cache-lib": "^3.0.0", "yn": "3.1.1" }, "bin": { "ts-node": "dist/bin.js", "ts-node-cwd": "dist/bin-cwd.js", + "ts-node-esm": "dist/bin-esm.js", "ts-node-script": "dist/bin-script.js", "ts-node-transpile-only": "dist/bin-transpile.js", "ts-script": "dist/bin-script-deprecated.js" @@ -5960,16 +6009,16 @@ } }, "node_modules/typedoc": { - "version": "0.22.11", - "resolved": "https://registry.npmjs.org/typedoc/-/typedoc-0.22.11.tgz", - "integrity": "sha512-pVr3hh6dkS3lPPaZz1fNpvcrqLdtEvXmXayN55czlamSgvEjh+57GUqfhAI1Xsuu/hNHUT1KNSx8LH2wBP/7SA==", + "version": "0.22.13", + "resolved": "https://registry.npmjs.org/typedoc/-/typedoc-0.22.13.tgz", + "integrity": "sha512-NHNI7Dr6JHa/I3+c62gdRNXBIyX7P33O9TafGLd07ur3MqzcKgwTvpg18EtvCLHJyfeSthAtCLpM7WkStUmDuQ==", "dev": true, "dependencies": { "glob": "^7.2.0", "lunr": "^2.3.9", - "marked": "^4.0.10", - "minimatch": "^3.0.4", - "shiki": "^0.10.0" + "marked": "^4.0.12", + "minimatch": "^5.0.1", + "shiki": "^0.10.1" }, "bin": { "typedoc": "bin/typedoc" @@ -5978,13 +6027,34 @@ "node": ">= 12.10.0" }, "peerDependencies": { - "typescript": "4.0.x || 4.1.x || 4.2.x || 4.3.x || 4.4.x || 4.5.x" + "typescript": "4.0.x || 4.1.x || 4.2.x || 4.3.x || 4.4.x || 4.5.x || 4.6.x" + } + }, + "node_modules/typedoc/node_modules/brace-expansion": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/brace-expansion/-/brace-expansion-2.0.1.tgz", + "integrity": "sha512-XnAIvQ8eM+kC6aULx6wuQiwVsnzsi9d3WxzV3FpWTGA19F621kwdbsAcFKXgKUHZWsy+mY6iL1sHTxWEFCytDA==", + "dev": true, + "dependencies": { + "balanced-match": "^1.0.0" + } + }, + "node_modules/typedoc/node_modules/minimatch": { + "version": "5.0.1", + "resolved": "https://registry.npmjs.org/minimatch/-/minimatch-5.0.1.tgz", + "integrity": "sha512-nLDxIFRyhDblz3qMuq+SoRZED4+miJ/G+tdDrjkkkRnjAsBexeGpgjLEQ0blJy7rHhR2b93rhQY4SvyWu9v03g==", + "dev": true, + "dependencies": { + "brace-expansion": "^2.0.1" + }, + "engines": { + "node": ">=10" } }, "node_modules/typescript": { - "version": "4.5.5", - "resolved": "https://registry.npmjs.org/typescript/-/typescript-4.5.5.tgz", - "integrity": "sha512-TCTIul70LyWe6IJWT8QSYeA54WQe8EjQFU4wY52Fasj5UKx88LNYKCgBEHcOMOrFF1rKGbD8v/xcNWVUq9SymA==", + "version": "4.6.3", + "resolved": "https://registry.npmjs.org/typescript/-/typescript-4.6.3.tgz", + "integrity": "sha512-yNIatDa5iaofVozS/uQJEl3JRWLKKGJKh6Yaiv0GLGSuhpFJe7P3SbHZ8/yjAHRQwKRoA6YZqlfjXWmVzoVSMw==", "dev": true, "bin": { "tsc": "bin/tsc", @@ -6130,10 +6200,16 @@ "integrity": "sha512-l8lCEmLcLYZh4nbunNZvQCJc5pv7+RCwa8q/LdUx8u7lsWvPDKmpodJAJNwkAhJC//dFY48KuIEmjtd4RViDrA==", "dev": true }, + "node_modules/v8-compile-cache-lib": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/v8-compile-cache-lib/-/v8-compile-cache-lib-3.0.0.tgz", + "integrity": "sha512-mpSYqfsFvASnSn5qMiwrr4VKfumbPyONLCOPmsR3A6pTY/r0+tSaVbgPWSAIuzbk3lCTa+FForeTiO+wBQGkjA==", + "dev": true + }, "node_modules/vscode-oniguruma": { - "version": "1.6.1", - "resolved": "https://registry.npmjs.org/vscode-oniguruma/-/vscode-oniguruma-1.6.1.tgz", - "integrity": "sha512-vc4WhSIaVpgJ0jJIejjYxPvURJavX6QG41vu0mGhqywMkQqulezEqEQ3cO3gc8GvcOpX6ycmKGqRoROEMBNXTQ==", + "version": "1.6.2", + "resolved": "https://registry.npmjs.org/vscode-oniguruma/-/vscode-oniguruma-1.6.2.tgz", + "integrity": "sha512-KH8+KKov5eS/9WhofZR8M8dMHWN2gTxjMsG4jd04YhpbPR91fUj7rYQ2/XjeHCJWbg7X++ApRIU9NUwM2vTvLA==", "dev": true }, "node_modules/vscode-textmate": { @@ -6200,6 +6276,12 @@ "node": ">=8" } }, + "node_modules/wildcard-match": { + "version": "5.1.2", + "resolved": "https://registry.npmjs.org/wildcard-match/-/wildcard-match-5.1.2.tgz", + "integrity": "sha512-qNXwI591Z88c8bWxp+yjV60Ch4F8Riawe3iGxbzquhy8Xs9m+0+SLFBGb/0yCTIDElawtaImC37fYZ+dr32KqQ==", + "dev": true + }, "node_modules/windows-release": { "version": "4.0.0", "resolved": "https://registry.npmjs.org/windows-release/-/windows-release-4.0.0.tgz", @@ -6345,9 +6427,9 @@ } }, "node_modules/yargs": { - "version": "17.3.1", - "resolved": "https://registry.npmjs.org/yargs/-/yargs-17.3.1.tgz", - "integrity": "sha512-WUANQeVgjLbNsEmGk20f+nlHgOqzRFpiGWVaBrYGYIGANIIu3lWjoyi0fNlFmJkvfhCZ6BXINe7/W2O2bV4iaA==", + "version": "17.4.0", + "resolved": "https://registry.npmjs.org/yargs/-/yargs-17.4.0.tgz", + "integrity": "sha512-WJudfrk81yWFSOkZYpAZx4Nt7V4xp7S/uJkX0CnxovMCt1wCE8LNftPpNuF9X/u9gN5nsD7ycYtRcDf2pL3UiA==", "dev": true, "dependencies": { "cliui": "^7.0.2", @@ -6399,9 +6481,9 @@ } }, "node_modules/yargs/node_modules/yargs-parser": { - "version": "21.0.0", - "resolved": "https://registry.npmjs.org/yargs-parser/-/yargs-parser-21.0.0.tgz", - "integrity": "sha512-z9kApYUOCwoeZ78rfRYYWdiU/iNL6mwwYlkkZfJoyMR1xps+NEBX5X7XmRpxkZHhXJ6+Ey00IwKxBBSW9FIjyA==", + "version": "21.0.1", + "resolved": "https://registry.npmjs.org/yargs-parser/-/yargs-parser-21.0.1.tgz", + "integrity": "sha512-9BK1jFpLzJROCI5TzwZL/TU4gqjK5xiHV/RfWLOahrjAko/e4DJkRDZQXfvqAsiZzzYhgAzbgz6lg48jcm4GLg==", "dev": true, "engines": { "node": ">=12" @@ -6429,26 +6511,24 @@ } }, "packages/bloom": { - "name": "@node-redis/bloom", "version": "1.0.1", "license": "MIT", "devDependencies": { "@istanbuljs/nyc-config-typescript": "^1.0.2", "@node-redis/test-utils": "*", - "@types/node": "^17.0.13", + "@types/node": "^17.0.23", "nyc": "^15.1.0", - "release-it": "^14.12.4", + "release-it": "^14.13.1", "source-map-support": "^0.5.21", - "ts-node": "^10.4.0", - "typedoc": "^0.22.11", - "typescript": "^4.5.5" + "ts-node": "^10.7.0", + "typedoc": "^0.22.13", + "typescript": "^4.6.3" }, "peerDependencies": { "@node-redis/client": "^1.0.0" } }, "packages/client": { - "name": "@node-redis/client", "version": "1.0.4", "license": "MIT", "dependencies": { @@ -6460,114 +6540,109 @@ "devDependencies": { "@istanbuljs/nyc-config-typescript": "^1.0.2", "@node-redis/test-utils": "*", - "@types/node": "^17.0.13", + "@types/node": "^17.0.23", "@types/redis-parser": "^3.0.0", - "@types/sinon": "^10.0.9", + "@types/sinon": "^10.0.11", "@types/yallist": "^4.0.1", - "@typescript-eslint/eslint-plugin": "^5.10.1", - "@typescript-eslint/parser": "^5.10.1", - "eslint": "^8.8.0", + "@typescript-eslint/eslint-plugin": "^5.16.0", + "@typescript-eslint/parser": "^5.16.0", + "eslint": "^8.12.0", "nyc": "^15.1.0", - "release-it": "^14.12.4", - "sinon": "^13.0.0", + "release-it": "^14.13.1", + "sinon": "^13.0.1", "source-map-support": "^0.5.21", - "ts-node": "^10.4.0", - "typedoc": "^0.22.11", - "typescript": "^4.5.5" + "ts-node": "^10.7.0", + "typedoc": "^0.22.13", + "typescript": "^4.6.3" }, "engines": { "node": ">=12" } }, "packages/graph": { - "name": "@node-redis/graph", "version": "1.0.0", "license": "MIT", "devDependencies": { "@istanbuljs/nyc-config-typescript": "^1.0.2", "@node-redis/test-utils": "*", - "@types/node": "^17.0.13", + "@types/node": "^17.0.23", "nyc": "^15.1.0", - "release-it": "^14.12.4", + "release-it": "^14.13.1", "source-map-support": "^0.5.21", - "ts-node": "^10.4.0", - "typedoc": "^0.22.11", - "typescript": "^4.5.5" + "ts-node": "^10.7.0", + "typedoc": "^0.22.13", + "typescript": "^4.6.3" }, "peerDependencies": { "@node-redis/client": "^1.0.0" } }, "packages/json": { - "name": "@node-redis/json", "version": "1.0.2", "license": "MIT", "devDependencies": { "@istanbuljs/nyc-config-typescript": "^1.0.2", "@node-redis/test-utils": "*", - "@types/node": "^17.0.13", + "@types/node": "^17.0.23", "nyc": "^15.1.0", - "release-it": "^14.12.4", + "release-it": "^14.13.1", "source-map-support": "^0.5.21", - "ts-node": "^10.4.0", - "typedoc": "^0.22.11", - "typescript": "^4.5.5" + "ts-node": "^10.7.0", + "typedoc": "^0.22.13", + "typescript": "^4.6.3" }, "peerDependencies": { "@node-redis/client": "^1.0.0" } }, "packages/search": { - "name": "@node-redis/search", "version": "1.0.3", "license": "MIT", "devDependencies": { "@istanbuljs/nyc-config-typescript": "^1.0.2", "@node-redis/test-utils": "*", - "@types/node": "^17.0.13", + "@types/node": "^17.0.23", "nyc": "^15.1.0", - "release-it": "^14.12.4", + "release-it": "^14.13.1", "source-map-support": "^0.5.21", - "ts-node": "^10.4.0", - "typedoc": "^0.22.11", - "typescript": "^4.5.5" + "ts-node": "^10.7.0", + "typedoc": "^0.22.13", + "typescript": "^4.6.3" }, "peerDependencies": { "@node-redis/client": "^1.0.0" } }, "packages/test-utils": { - "name": "@node-redis/test-utils", "devDependencies": { "@istanbuljs/nyc-config-typescript": "^1.0.2", "@types/mocha": "^9.1.0", - "@types/node": "^17.0.13", - "@types/yargs": "^17.0.8", - "mocha": "^9.2.0", + "@types/node": "^17.0.23", + "@types/yargs": "^17.0.10", + "mocha": "^9.2.2", "nyc": "^15.1.0", "source-map-support": "^0.5.21", - "ts-node": "^10.4.0", - "typescript": "^4.5.5", - "yargs": "^17.3.1" + "ts-node": "^10.7.0", + "typescript": "^4.6.3", + "yargs": "^17.4.0" }, "peerDependencies": { "@node-redis/client": "^1.0.0" } }, "packages/time-series": { - "name": "@node-redis/time-series", "version": "1.0.2", "license": "MIT", "devDependencies": { "@istanbuljs/nyc-config-typescript": "^1.0.2", "@node-redis/test-utils": "*", - "@types/node": "^17.0.13", + "@types/node": "^17.0.23", "nyc": "^15.1.0", - "release-it": "^14.12.4", + "release-it": "^14.13.1", "source-map-support": "^0.5.21", - "ts-node": "^10.4.0", - "typedoc": "^0.22.11", - "typescript": "^4.5.5" + "ts-node": "^10.7.0", + "typedoc": "^0.22.13", + "typescript": "^4.6.3" }, "peerDependencies": { "@node-redis/client": "^1.0.0" @@ -6575,6 +6650,15 @@ } }, "dependencies": { + "@ampproject/remapping": { + "version": "2.1.2", + "resolved": "https://registry.npmjs.org/@ampproject/remapping/-/remapping-2.1.2.tgz", + "integrity": "sha512-hoyByceqwKirw7w3Z7gnIIZC3Wx3J484Y3L/cMpXFbr7d9ZQj2mODrirNzcJa+SM3UlpWXYvKV4RlRpFXlWgXg==", + "dev": true, + "requires": { + "@jridgewell/trace-mapping": "^0.3.0" + } + }, "@babel/code-frame": { "version": "7.16.7", "resolved": "https://registry.npmjs.org/@babel/code-frame/-/code-frame-7.16.7.tgz", @@ -6585,52 +6669,52 @@ } }, "@babel/compat-data": { - "version": "7.16.8", - "resolved": "https://registry.npmjs.org/@babel/compat-data/-/compat-data-7.16.8.tgz", - "integrity": "sha512-m7OkX0IdKLKPpBlJtF561YJal5y/jyI5fNfWbPxh2D/nbzzGI4qRyrD8xO2jB24u7l+5I2a43scCG2IrfjC50Q==", + "version": "7.17.7", + "resolved": "https://registry.npmjs.org/@babel/compat-data/-/compat-data-7.17.7.tgz", + "integrity": "sha512-p8pdE6j0a29TNGebNm7NzYZWB3xVZJBZ7XGs42uAKzQo8VQ3F0By/cQCtUEABwIqw5zo6WA4NbmxsfzADzMKnQ==", "dev": true }, "@babel/core": { - "version": "7.16.12", - "resolved": "https://registry.npmjs.org/@babel/core/-/core-7.16.12.tgz", - "integrity": "sha512-dK5PtG1uiN2ikk++5OzSYsitZKny4wOCD0nrO4TqnW4BVBTQ2NGS3NgilvT/TEyxTST7LNyWV/T4tXDoD3fOgg==", + "version": "7.17.8", + "resolved": "https://registry.npmjs.org/@babel/core/-/core-7.17.8.tgz", + "integrity": "sha512-OdQDV/7cRBtJHLSOBqqbYNkOcydOgnX59TZx4puf41fzcVtN3e/4yqY8lMQsK+5X2lJtAdmA+6OHqsj1hBJ4IQ==", "dev": true, "requires": { + "@ampproject/remapping": "^2.1.0", "@babel/code-frame": "^7.16.7", - "@babel/generator": "^7.16.8", - "@babel/helper-compilation-targets": "^7.16.7", - "@babel/helper-module-transforms": "^7.16.7", - "@babel/helpers": "^7.16.7", - "@babel/parser": "^7.16.12", + "@babel/generator": "^7.17.7", + "@babel/helper-compilation-targets": "^7.17.7", + "@babel/helper-module-transforms": "^7.17.7", + "@babel/helpers": "^7.17.8", + "@babel/parser": "^7.17.8", "@babel/template": "^7.16.7", - "@babel/traverse": "^7.16.10", - "@babel/types": "^7.16.8", + "@babel/traverse": "^7.17.3", + "@babel/types": "^7.17.0", "convert-source-map": "^1.7.0", "debug": "^4.1.0", "gensync": "^1.0.0-beta.2", "json5": "^2.1.2", - "semver": "^6.3.0", - "source-map": "^0.5.0" + "semver": "^6.3.0" } }, "@babel/generator": { - "version": "7.16.8", - "resolved": "https://registry.npmjs.org/@babel/generator/-/generator-7.16.8.tgz", - "integrity": "sha512-1ojZwE9+lOXzcWdWmO6TbUzDfqLD39CmEhN8+2cX9XkDo5yW1OpgfejfliysR2AWLpMamTiOiAp/mtroaymhpw==", + "version": "7.17.7", + "resolved": "https://registry.npmjs.org/@babel/generator/-/generator-7.17.7.tgz", + "integrity": "sha512-oLcVCTeIFadUoArDTwpluncplrYBmTCCZZgXCbgNGvOBBiSDDK3eWO4b/+eOTli5tKv1lg+a5/NAXg+nTcei1w==", "dev": true, "requires": { - "@babel/types": "^7.16.8", + "@babel/types": "^7.17.0", "jsesc": "^2.5.1", "source-map": "^0.5.0" } }, "@babel/helper-compilation-targets": { - "version": "7.16.7", - "resolved": "https://registry.npmjs.org/@babel/helper-compilation-targets/-/helper-compilation-targets-7.16.7.tgz", - "integrity": "sha512-mGojBwIWcwGD6rfqgRXVlVYmPAv7eOpIemUG3dGnDdCY4Pae70ROij3XmfrH6Fa1h1aiDylpglbZyktfzyo/hA==", + "version": "7.17.7", + "resolved": "https://registry.npmjs.org/@babel/helper-compilation-targets/-/helper-compilation-targets-7.17.7.tgz", + "integrity": "sha512-UFzlz2jjd8kroj0hmCFV5zr+tQPi1dpC2cRsDV/3IEW8bJfCPrPpmcSN6ZS8RqIq4LXcmpipCQFPddyFA5Yc7w==", "dev": true, "requires": { - "@babel/compat-data": "^7.16.4", + "@babel/compat-data": "^7.17.7", "@babel/helper-validator-option": "^7.16.7", "browserslist": "^4.17.5", "semver": "^6.3.0" @@ -6684,28 +6768,28 @@ } }, "@babel/helper-module-transforms": { - "version": "7.16.7", - "resolved": "https://registry.npmjs.org/@babel/helper-module-transforms/-/helper-module-transforms-7.16.7.tgz", - "integrity": "sha512-gaqtLDxJEFCeQbYp9aLAefjhkKdjKcdh6DB7jniIGU3Pz52WAmP268zK0VgPz9hUNkMSYeH976K2/Y6yPadpng==", + "version": "7.17.7", + "resolved": "https://registry.npmjs.org/@babel/helper-module-transforms/-/helper-module-transforms-7.17.7.tgz", + "integrity": "sha512-VmZD99F3gNTYB7fJRDTi+u6l/zxY0BE6OIxPSU7a50s6ZUQkHwSDmV92FfM+oCG0pZRVojGYhkR8I0OGeCVREw==", "dev": true, "requires": { "@babel/helper-environment-visitor": "^7.16.7", "@babel/helper-module-imports": "^7.16.7", - "@babel/helper-simple-access": "^7.16.7", + "@babel/helper-simple-access": "^7.17.7", "@babel/helper-split-export-declaration": "^7.16.7", "@babel/helper-validator-identifier": "^7.16.7", "@babel/template": "^7.16.7", - "@babel/traverse": "^7.16.7", - "@babel/types": "^7.16.7" + "@babel/traverse": "^7.17.3", + "@babel/types": "^7.17.0" } }, "@babel/helper-simple-access": { - "version": "7.16.7", - "resolved": "https://registry.npmjs.org/@babel/helper-simple-access/-/helper-simple-access-7.16.7.tgz", - "integrity": "sha512-ZIzHVyoeLMvXMN/vok/a4LWRy8G2v205mNP0XOuf9XRLyX5/u9CnVulUtDgUTama3lT+bf/UqucuZjqiGuTS1g==", + "version": "7.17.7", + "resolved": "https://registry.npmjs.org/@babel/helper-simple-access/-/helper-simple-access-7.17.7.tgz", + "integrity": "sha512-txyMCGroZ96i+Pxr3Je3lzEJjqwaRC9buMUgtomcrLe5Nd0+fk1h0LLA+ixUF5OW7AhHuQ7Es1WcQJZmZsz2XA==", "dev": true, "requires": { - "@babel/types": "^7.16.7" + "@babel/types": "^7.17.0" } }, "@babel/helper-split-export-declaration": { @@ -6730,14 +6814,14 @@ "dev": true }, "@babel/helpers": { - "version": "7.16.7", - "resolved": "https://registry.npmjs.org/@babel/helpers/-/helpers-7.16.7.tgz", - "integrity": "sha512-9ZDoqtfY7AuEOt3cxchfii6C7GDyyMBffktR5B2jvWv8u2+efwvpnVKXMWzNehqy68tKgAfSwfdw/lWpthS2bw==", + "version": "7.17.8", + "resolved": "https://registry.npmjs.org/@babel/helpers/-/helpers-7.17.8.tgz", + "integrity": "sha512-QcL86FGxpfSJwGtAvv4iG93UL6bmqBdmoVY0CMCU2g+oD2ezQse3PT5Pa+jiD6LJndBQi0EDlpzOWNlLuhz5gw==", "dev": true, "requires": { "@babel/template": "^7.16.7", - "@babel/traverse": "^7.16.7", - "@babel/types": "^7.16.7" + "@babel/traverse": "^7.17.3", + "@babel/types": "^7.17.0" } }, "@babel/highlight": { @@ -6804,9 +6888,9 @@ } }, "@babel/parser": { - "version": "7.16.12", - "resolved": "https://registry.npmjs.org/@babel/parser/-/parser-7.16.12.tgz", - "integrity": "sha512-VfaV15po8RiZssrkPweyvbGVSe4x2y+aciFCgn0n0/SJMR22cwofRV1mtnJQYcSB1wUTaA/X1LnA3es66MCO5A==", + "version": "7.17.8", + "resolved": "https://registry.npmjs.org/@babel/parser/-/parser-7.17.8.tgz", + "integrity": "sha512-BoHhDJrJXqcg+ZL16Xv39H9n+AqJ4pcDrQBGZN+wHxIysrLZ3/ECwCBUch/1zUNhnsXULcONU3Ei5Hmkfk6kiQ==", "dev": true }, "@babel/template": { @@ -6821,19 +6905,19 @@ } }, "@babel/traverse": { - "version": "7.16.10", - "resolved": "https://registry.npmjs.org/@babel/traverse/-/traverse-7.16.10.tgz", - "integrity": "sha512-yzuaYXoRJBGMlBhsMJoUW7G1UmSb/eXr/JHYM/MsOJgavJibLwASijW7oXBdw3NQ6T0bW7Ty5P/VarOs9cHmqw==", + "version": "7.17.3", + "resolved": "https://registry.npmjs.org/@babel/traverse/-/traverse-7.17.3.tgz", + "integrity": "sha512-5irClVky7TxRWIRtxlh2WPUUOLhcPN06AGgaQSB8AEwuyEBgJVuJ5imdHm5zxk8w0QS5T+tDfnDxAlhWjpb7cw==", "dev": true, "requires": { "@babel/code-frame": "^7.16.7", - "@babel/generator": "^7.16.8", + "@babel/generator": "^7.17.3", "@babel/helper-environment-visitor": "^7.16.7", "@babel/helper-function-name": "^7.16.7", "@babel/helper-hoist-variables": "^7.16.7", "@babel/helper-split-export-declaration": "^7.16.7", - "@babel/parser": "^7.16.10", - "@babel/types": "^7.16.8", + "@babel/parser": "^7.17.3", + "@babel/types": "^7.17.0", "debug": "^4.1.0", "globals": "^11.1.0" }, @@ -6847,9 +6931,9 @@ } }, "@babel/types": { - "version": "7.16.8", - "resolved": "https://registry.npmjs.org/@babel/types/-/types-7.16.8.tgz", - "integrity": "sha512-smN2DQc5s4M7fntyjGtyIPbRJv6wW4rU/94fmYJ7PKQuZkC0qGMHXJbg6sNGt12JmVr4k5YaptI/XtiLJBnmIg==", + "version": "7.17.0", + "resolved": "https://registry.npmjs.org/@babel/types/-/types-7.17.0.tgz", + "integrity": "sha512-TmKSNO4D5rzhL5bjWFcVHHLETzfQ/AmbKpKPOSjlP0WoHZ6L911fgoOKY4Alp/emzG4cHJdyN49zpgkbXFEHHw==", "dev": true, "requires": { "@babel/helper-validator-identifier": "^7.16.7", @@ -6872,28 +6956,22 @@ } }, "@eslint/eslintrc": { - "version": "1.0.5", - "resolved": "https://registry.npmjs.org/@eslint/eslintrc/-/eslintrc-1.0.5.tgz", - "integrity": "sha512-BLxsnmK3KyPunz5wmCCpqy0YelEoxxGmH73Is+Z74oOTMtExcjkr3dDR6quwrjh1YspA8DH9gnX1o069KiS9AQ==", + "version": "1.2.1", + "resolved": "https://registry.npmjs.org/@eslint/eslintrc/-/eslintrc-1.2.1.tgz", + "integrity": "sha512-bxvbYnBPN1Gibwyp6NrpnFzA3YtRL3BBAyEAFVIpNTm2Rn4Vy87GA5M4aSn3InRrlsbX5N0GW7XIx+U4SAEKdQ==", "dev": true, "requires": { "ajv": "^6.12.4", "debug": "^4.3.2", - "espree": "^9.2.0", + "espree": "^9.3.1", "globals": "^13.9.0", - "ignore": "^4.0.6", + "ignore": "^5.2.0", "import-fresh": "^3.2.1", "js-yaml": "^4.1.0", "minimatch": "^3.0.4", "strip-json-comments": "^3.1.1" }, "dependencies": { - "ignore": { - "version": "4.0.6", - "resolved": "https://registry.npmjs.org/ignore/-/ignore-4.0.6.tgz", - "integrity": "sha512-cyFDKrqc/YdcWFniJhzI42+AzS+gNwmUzOSFcRCQYwySuBBBy/KjuxWLZ/FHEH6Moq1NizMOBWyTcv8O4OZIMg==", - "dev": true - }, "strip-json-comments": { "version": "3.1.1", "resolved": "https://registry.npmjs.org/strip-json-comments/-/strip-json-comments-3.1.1.tgz", @@ -6903,9 +6981,9 @@ } }, "@humanwhocodes/config-array": { - "version": "0.9.3", - "resolved": "https://registry.npmjs.org/@humanwhocodes/config-array/-/config-array-0.9.3.tgz", - "integrity": "sha512-3xSMlXHh03hCcCmFc0rbKp3Ivt2PFEJnQUJDDMTJQ2wkECZWdq4GePs2ctc5H8zV+cHPaq8k2vU8mrQjA6iHdQ==", + "version": "0.9.5", + "resolved": "https://registry.npmjs.org/@humanwhocodes/config-array/-/config-array-0.9.5.tgz", + "integrity": "sha512-ObyMyWxZiCu/yTisA7uzx81s40xR2fD5Cg/2Kq7G02ajkNubJf6BopgDTmDyc3U7sXpNKM8cYOw7s7Tyr+DnCw==", "dev": true, "requires": { "@humanwhocodes/object-schema": "^1.2.1", @@ -6986,18 +7064,40 @@ "integrity": "sha512-ZXRY4jNvVgSVQ8DL3LTcakaAtXwTVUxE81hslsyD2AtoXW/wVob10HkOJ1X/pAlcI7D+2YoZKg5do8G/w6RYgA==", "dev": true }, + "@jridgewell/resolve-uri": { + "version": "3.0.5", + "resolved": "https://registry.npmjs.org/@jridgewell/resolve-uri/-/resolve-uri-3.0.5.tgz", + "integrity": "sha512-VPeQ7+wH0itvQxnG+lIzWgkysKIr3L9sslimFW55rHMdGu/qCQ5z5h9zq4gI8uBtqkpHhsF4Z/OwExufUCThew==", + "dev": true + }, + "@jridgewell/sourcemap-codec": { + "version": "1.4.11", + "resolved": "https://registry.npmjs.org/@jridgewell/sourcemap-codec/-/sourcemap-codec-1.4.11.tgz", + "integrity": "sha512-Fg32GrJo61m+VqYSdRSjRXMjQ06j8YIYfcTqndLYVAaHmroZHLJZCydsWBOTDqXS2v+mjxohBWEMfg97GXmYQg==", + "dev": true + }, + "@jridgewell/trace-mapping": { + "version": "0.3.4", + "resolved": "https://registry.npmjs.org/@jridgewell/trace-mapping/-/trace-mapping-0.3.4.tgz", + "integrity": "sha512-vFv9ttIedivx0ux3QSjhgtCVjPZd5l46ZOMDSCwnH1yUO2e964gO8LZGyv2QkqcgR6TnBU1v+1IFqmeoG+0UJQ==", + "dev": true, + "requires": { + "@jridgewell/resolve-uri": "^3.0.3", + "@jridgewell/sourcemap-codec": "^1.4.10" + } + }, "@node-redis/bloom": { "version": "file:packages/bloom", "requires": { "@istanbuljs/nyc-config-typescript": "^1.0.2", "@node-redis/test-utils": "*", - "@types/node": "^17.0.13", + "@types/node": "^17.0.23", "nyc": "^15.1.0", - "release-it": "^14.12.4", + "release-it": "^14.13.1", "source-map-support": "^0.5.21", - "ts-node": "^10.4.0", - "typedoc": "^0.22.11", - "typescript": "^4.5.5" + "ts-node": "^10.7.0", + "typedoc": "^0.22.13", + "typescript": "^4.6.3" } }, "@node-redis/client": { @@ -7005,23 +7105,23 @@ "requires": { "@istanbuljs/nyc-config-typescript": "^1.0.2", "@node-redis/test-utils": "*", - "@types/node": "^17.0.13", + "@types/node": "^17.0.23", "@types/redis-parser": "^3.0.0", - "@types/sinon": "^10.0.9", + "@types/sinon": "^10.0.11", "@types/yallist": "^4.0.1", - "@typescript-eslint/eslint-plugin": "^5.10.1", - "@typescript-eslint/parser": "^5.10.1", + "@typescript-eslint/eslint-plugin": "^5.16.0", + "@typescript-eslint/parser": "^5.16.0", "cluster-key-slot": "1.1.0", - "eslint": "^8.8.0", + "eslint": "^8.12.0", "generic-pool": "3.8.2", "nyc": "^15.1.0", "redis-parser": "3.0.0", - "release-it": "^14.12.4", - "sinon": "^13.0.0", + "release-it": "^14.13.1", + "sinon": "^13.0.1", "source-map-support": "^0.5.21", - "ts-node": "^10.4.0", - "typedoc": "^0.22.11", - "typescript": "^4.5.5", + "ts-node": "^10.7.0", + "typedoc": "^0.22.13", + "typescript": "^4.6.3", "yallist": "4.0.0" } }, @@ -7030,13 +7130,13 @@ "requires": { "@istanbuljs/nyc-config-typescript": "^1.0.2", "@node-redis/test-utils": "*", - "@types/node": "^17.0.13", + "@types/node": "^17.0.23", "nyc": "^15.1.0", - "release-it": "^14.12.4", + "release-it": "^14.13.1", "source-map-support": "^0.5.21", - "ts-node": "^10.4.0", - "typedoc": "^0.22.11", - "typescript": "^4.5.5" + "ts-node": "^10.7.0", + "typedoc": "^0.22.13", + "typescript": "^4.6.3" } }, "@node-redis/json": { @@ -7044,13 +7144,13 @@ "requires": { "@istanbuljs/nyc-config-typescript": "^1.0.2", "@node-redis/test-utils": "*", - "@types/node": "^17.0.13", + "@types/node": "^17.0.23", "nyc": "^15.1.0", - "release-it": "^14.12.4", + "release-it": "^14.13.1", "source-map-support": "^0.5.21", - "ts-node": "^10.4.0", - "typedoc": "^0.22.11", - "typescript": "^4.5.5" + "ts-node": "^10.7.0", + "typedoc": "^0.22.13", + "typescript": "^4.6.3" } }, "@node-redis/search": { @@ -7058,13 +7158,13 @@ "requires": { "@istanbuljs/nyc-config-typescript": "^1.0.2", "@node-redis/test-utils": "*", - "@types/node": "^17.0.13", + "@types/node": "^17.0.23", "nyc": "^15.1.0", - "release-it": "^14.12.4", + "release-it": "^14.13.1", "source-map-support": "^0.5.21", - "ts-node": "^10.4.0", - "typedoc": "^0.22.11", - "typescript": "^4.5.5" + "ts-node": "^10.7.0", + "typedoc": "^0.22.13", + "typescript": "^4.6.3" } }, "@node-redis/test-utils": { @@ -7072,14 +7172,14 @@ "requires": { "@istanbuljs/nyc-config-typescript": "^1.0.2", "@types/mocha": "^9.1.0", - "@types/node": "^17.0.13", - "@types/yargs": "^17.0.8", - "mocha": "^9.2.0", + "@types/node": "^17.0.23", + "@types/yargs": "^17.0.10", + "mocha": "^9.2.2", "nyc": "^15.1.0", "source-map-support": "^0.5.21", - "ts-node": "^10.4.0", - "typescript": "^4.5.5", - "yargs": "^17.3.1" + "ts-node": "^10.7.0", + "typescript": "^4.6.3", + "yargs": "^17.4.0" } }, "@node-redis/time-series": { @@ -7087,13 +7187,13 @@ "requires": { "@istanbuljs/nyc-config-typescript": "^1.0.2", "@node-redis/test-utils": "*", - "@types/node": "^17.0.13", + "@types/node": "^17.0.23", "nyc": "^15.1.0", - "release-it": "^14.12.4", + "release-it": "^14.13.1", "source-map-support": "^0.5.21", - "ts-node": "^10.4.0", - "typedoc": "^0.22.11", - "typescript": "^4.5.5" + "ts-node": "^10.7.0", + "typedoc": "^0.22.13", + "typescript": "^4.6.3" } }, "@nodelib/fs.scandir": { @@ -7132,14 +7232,14 @@ } }, "@octokit/core": { - "version": "3.5.1", - "resolved": "https://registry.npmjs.org/@octokit/core/-/core-3.5.1.tgz", - "integrity": "sha512-omncwpLVxMP+GLpLPgeGJBF6IWJFjXDS5flY5VbppePYX9XehevbDykRH9PdCdvqt9TS5AOTiDide7h0qrkHjw==", + "version": "3.6.0", + "resolved": "https://registry.npmjs.org/@octokit/core/-/core-3.6.0.tgz", + "integrity": "sha512-7RKRKuA4xTjMhY+eG3jthb3hlZCsOwg3rztWh75Xc+ShDWOfDDATWbeZpAHBNRpm4Tv9WgBMOy1zEJYXG6NJ7Q==", "dev": true, "requires": { "@octokit/auth-token": "^2.4.4", "@octokit/graphql": "^4.5.8", - "@octokit/request": "^5.6.0", + "@octokit/request": "^5.6.3", "@octokit/request-error": "^2.0.5", "@octokit/types": "^6.0.3", "before-after-hook": "^2.2.0", @@ -7247,9 +7347,9 @@ } }, "@sindresorhus/is": { - "version": "4.4.0", - "resolved": "https://registry.npmjs.org/@sindresorhus/is/-/is-4.4.0.tgz", - "integrity": "sha512-QppPM/8l3Mawvh4rn9CNEYIU9bxpXUCRMaX9yUpvBk1nMKusLKpfXGDEKExKaPhLzcn3lzil7pR6rnJ11HgeRQ==", + "version": "4.6.0", + "resolved": "https://registry.npmjs.org/@sindresorhus/is/-/is-4.6.0.tgz", + "integrity": "sha512-t09vSN3MdfsyCHoFcTRCH/iUtG7OJ0CsjzB8cjAmKc/va/kIgeDI/TxsigdncE/4be734m0cvIYwNaV4i2XqAw==", "dev": true }, "@sinonjs/commons": { @@ -7262,9 +7362,9 @@ } }, "@sinonjs/fake-timers": { - "version": "7.1.2", - "resolved": "https://registry.npmjs.org/@sinonjs/fake-timers/-/fake-timers-7.1.2.tgz", - "integrity": "sha512-iQADsW4LBMISqZ6Ci1dupJL9pprqwcVFTcOsEmQOEhW+KLCVn/Y4Jrvg2k19fIHCp+iFprriYPTdRcQR8NbUPg==", + "version": "9.1.1", + "resolved": "https://registry.npmjs.org/@sinonjs/fake-timers/-/fake-timers-9.1.1.tgz", + "integrity": "sha512-Wp5vwlZ0lOqpSYGKqr53INws9HLkt6JDc/pDZcPf7bchQnrXJMXPns8CXx0hFikMSGSWfvtvvpb2gtMVfkWagA==", "dev": true, "requires": { "@sinonjs/commons": "^1.7.0" @@ -7339,15 +7439,15 @@ "dev": true }, "@types/json-schema": { - "version": "7.0.9", - "resolved": "https://registry.npmjs.org/@types/json-schema/-/json-schema-7.0.9.tgz", - "integrity": "sha512-qcUXuemtEu+E5wZSJHNxUXeCZhAfXKQ41D+duX+VYPde7xyEVZci+/oXKJL13tnRs9lR2pr4fod59GT6/X1/yQ==", + "version": "7.0.11", + "resolved": "https://registry.npmjs.org/@types/json-schema/-/json-schema-7.0.11.tgz", + "integrity": "sha512-wOuvG1SN4Us4rez+tylwwwCV1psiNVOkJeM3AUWUNWg/jDQY2+HE/444y5gc+jBmRqASOm2Oeh5c1axHobwRKQ==", "dev": true }, "@types/keyv": { - "version": "3.1.3", - "resolved": "https://registry.npmjs.org/@types/keyv/-/keyv-3.1.3.tgz", - "integrity": "sha512-FXCJgyyN3ivVgRoml4h94G/p3kY+u/B86La+QptcqJaWtBWtmc6TtkNfS40n9bIvyLteHh7zXOtgbobORKPbDg==", + "version": "3.1.4", + "resolved": "https://registry.npmjs.org/@types/keyv/-/keyv-3.1.4.tgz", + "integrity": "sha512-BQ5aZNSCpj7D6K2ksrRCTmKRLEpnPvWDiLPfoGyhZ++8YtiK9d/3DBKPJgry359X/P1PfruyYwvnvwFjuEiEIg==", "dev": true, "requires": { "@types/node": "*" @@ -7360,9 +7460,9 @@ "dev": true }, "@types/node": { - "version": "17.0.13", - "resolved": "https://registry.npmjs.org/@types/node/-/node-17.0.13.tgz", - "integrity": "sha512-Y86MAxASe25hNzlDbsviXl8jQHb0RDvKt4c40ZJQ1Don0AAL0STLZSs4N+6gLEO55pedy7r2cLwS+ZDxPm/2Bw==", + "version": "17.0.23", + "resolved": "https://registry.npmjs.org/@types/node/-/node-17.0.23.tgz", + "integrity": "sha512-UxDxWn7dl97rKVeVS61vErvw086aCYhDLyvRQZ5Rk65rZKepaFdm53GeqXaKBuOhED4e9uWq34IC3TdSdJJ2Gw==", "dev": true }, "@types/parse-json": { @@ -7397,14 +7497,20 @@ } }, "@types/sinon": { - "version": "10.0.9", - "resolved": "https://registry.npmjs.org/@types/sinon/-/sinon-10.0.9.tgz", - "integrity": "sha512-xGZVAe61omKnVGedBdTbAveuJ5QyI0LrMIcp0hc1LmVI5IEjs5qG4fM0sv9GIBA2JVoKuf7332IjQX4y5qqMMQ==", + "version": "10.0.11", + "resolved": "https://registry.npmjs.org/@types/sinon/-/sinon-10.0.11.tgz", + "integrity": "sha512-dmZsHlBsKUtBpHriNjlK0ndlvEh8dcb9uV9Afsbt89QIyydpC7NcR+nWlAhASfy3GHnxTl4FX/aKE7XZUt/B4g==", "dev": true, "requires": { - "@sinonjs/fake-timers": "^7.1.0" + "@types/sinonjs__fake-timers": "*" } }, + "@types/sinonjs__fake-timers": { + "version": "8.1.2", + "resolved": "https://registry.npmjs.org/@types/sinonjs__fake-timers/-/sinonjs__fake-timers-8.1.2.tgz", + "integrity": "sha512-9GcLXF0/v3t80caGs5p2rRfkB+a8VBGLJZVih6CNFkx8IZ994wiKKLSRs9nuFwk1HevWs/1mnUmkApGrSGsShA==", + "dev": true + }, "@types/yallist": { "version": "4.0.1", "resolved": "https://registry.npmjs.org/@types/yallist/-/yallist-4.0.1.tgz", @@ -7412,29 +7518,29 @@ "dev": true }, "@types/yargs": { - "version": "17.0.8", - "resolved": "https://registry.npmjs.org/@types/yargs/-/yargs-17.0.8.tgz", - "integrity": "sha512-wDeUwiUmem9FzsyysEwRukaEdDNcwbROvQ9QGRKaLI6t+IltNzbn4/i4asmB10auvZGQCzSQ6t0GSczEThlUXw==", + "version": "17.0.10", + "resolved": "https://registry.npmjs.org/@types/yargs/-/yargs-17.0.10.tgz", + "integrity": "sha512-gmEaFwpj/7f/ROdtIlci1R1VYU1J4j95m8T+Tj3iBgiBFKg1foE/PSl93bBd5T9LDXNPo8UlNN6W0qwD8O5OaA==", "dev": true, "requires": { "@types/yargs-parser": "*" } }, "@types/yargs-parser": { - "version": "20.2.1", - "resolved": "https://registry.npmjs.org/@types/yargs-parser/-/yargs-parser-20.2.1.tgz", - "integrity": "sha512-7tFImggNeNBVMsn0vLrpn1H1uPrUBdnARPTpZoitY37ZrdJREzf7I16tMrlK3hen349gr1NYh8CmZQa7CTG6Aw==", + "version": "21.0.0", + "resolved": "https://registry.npmjs.org/@types/yargs-parser/-/yargs-parser-21.0.0.tgz", + "integrity": "sha512-iO9ZQHkZxHn4mSakYV0vFHAVDyEOIJQrV2uZ06HxEPcx+mt8swXoZHIbaaJ2crJYFfErySgktuTZ3BeLz+XmFA==", "dev": true }, "@typescript-eslint/eslint-plugin": { - "version": "5.10.1", - "resolved": "https://registry.npmjs.org/@typescript-eslint/eslint-plugin/-/eslint-plugin-5.10.1.tgz", - "integrity": "sha512-xN3CYqFlyE/qOcy978/L0xLR2HlcAGIyIK5sMOasxaaAPfQRj/MmMV6OC3I7NZO84oEUdWCOju34Z9W8E0pFDQ==", + "version": "5.16.0", + "resolved": "https://registry.npmjs.org/@typescript-eslint/eslint-plugin/-/eslint-plugin-5.16.0.tgz", + "integrity": "sha512-SJoba1edXvQRMmNI505Uo4XmGbxCK9ARQpkvOd00anxzri9RNQk0DDCxD+LIl+jYhkzOJiOMMKYEHnHEODjdCw==", "dev": true, "requires": { - "@typescript-eslint/scope-manager": "5.10.1", - "@typescript-eslint/type-utils": "5.10.1", - "@typescript-eslint/utils": "5.10.1", + "@typescript-eslint/scope-manager": "5.16.0", + "@typescript-eslint/type-utils": "5.16.0", + "@typescript-eslint/utils": "5.16.0", "debug": "^4.3.2", "functional-red-black-tree": "^1.0.1", "ignore": "^5.1.8", @@ -7455,52 +7561,52 @@ } }, "@typescript-eslint/parser": { - "version": "5.10.1", - "resolved": "https://registry.npmjs.org/@typescript-eslint/parser/-/parser-5.10.1.tgz", - "integrity": "sha512-GReo3tjNBwR5RnRO0K2wDIDN31cM3MmDtgyQ85oAxAmC5K3j/g85IjP+cDfcqDsDDBf1HNKQAD0WqOYL8jXqUA==", + "version": "5.16.0", + "resolved": "https://registry.npmjs.org/@typescript-eslint/parser/-/parser-5.16.0.tgz", + "integrity": "sha512-fkDq86F0zl8FicnJtdXakFs4lnuebH6ZADDw6CYQv0UZeIjHvmEw87m9/29nk2Dv5Lmdp0zQ3zDQhiMWQf/GbA==", "dev": true, "requires": { - "@typescript-eslint/scope-manager": "5.10.1", - "@typescript-eslint/types": "5.10.1", - "@typescript-eslint/typescript-estree": "5.10.1", + "@typescript-eslint/scope-manager": "5.16.0", + "@typescript-eslint/types": "5.16.0", + "@typescript-eslint/typescript-estree": "5.16.0", "debug": "^4.3.2" } }, "@typescript-eslint/scope-manager": { - "version": "5.10.1", - "resolved": "https://registry.npmjs.org/@typescript-eslint/scope-manager/-/scope-manager-5.10.1.tgz", - "integrity": "sha512-Lyvi559Gvpn94k7+ElXNMEnXu/iundV5uFmCUNnftbFrUbAJ1WBoaGgkbOBm07jVZa682oaBU37ao/NGGX4ZDg==", + "version": "5.16.0", + "resolved": "https://registry.npmjs.org/@typescript-eslint/scope-manager/-/scope-manager-5.16.0.tgz", + "integrity": "sha512-P+Yab2Hovg8NekLIR/mOElCDPyGgFZKhGoZA901Yax6WR6HVeGLbsqJkZ+Cvk5nts/dAlFKm8PfL43UZnWdpIQ==", "dev": true, "requires": { - "@typescript-eslint/types": "5.10.1", - "@typescript-eslint/visitor-keys": "5.10.1" + "@typescript-eslint/types": "5.16.0", + "@typescript-eslint/visitor-keys": "5.16.0" } }, "@typescript-eslint/type-utils": { - "version": "5.10.1", - "resolved": "https://registry.npmjs.org/@typescript-eslint/type-utils/-/type-utils-5.10.1.tgz", - "integrity": "sha512-AfVJkV8uck/UIoDqhu+ptEdBoQATON9GXnhOpPLzkQRJcSChkvD//qsz9JVffl2goxX+ybs5klvacE9vmrQyCw==", + "version": "5.16.0", + "resolved": "https://registry.npmjs.org/@typescript-eslint/type-utils/-/type-utils-5.16.0.tgz", + "integrity": "sha512-SKygICv54CCRl1Vq5ewwQUJV/8padIWvPgCxlWPGO/OgQLCijY9G7lDu6H+mqfQtbzDNlVjzVWQmeqbLMBLEwQ==", "dev": true, "requires": { - "@typescript-eslint/utils": "5.10.1", + "@typescript-eslint/utils": "5.16.0", "debug": "^4.3.2", "tsutils": "^3.21.0" } }, "@typescript-eslint/types": { - "version": "5.10.1", - "resolved": "https://registry.npmjs.org/@typescript-eslint/types/-/types-5.10.1.tgz", - "integrity": "sha512-ZvxQ2QMy49bIIBpTqFiOenucqUyjTQ0WNLhBM6X1fh1NNlYAC6Kxsx8bRTY3jdYsYg44a0Z/uEgQkohbR0H87Q==", + "version": "5.16.0", + "resolved": "https://registry.npmjs.org/@typescript-eslint/types/-/types-5.16.0.tgz", + "integrity": "sha512-oUorOwLj/3/3p/HFwrp6m/J2VfbLC8gjW5X3awpQJ/bSG+YRGFS4dpsvtQ8T2VNveV+LflQHjlLvB6v0R87z4g==", "dev": true }, "@typescript-eslint/typescript-estree": { - "version": "5.10.1", - "resolved": "https://registry.npmjs.org/@typescript-eslint/typescript-estree/-/typescript-estree-5.10.1.tgz", - "integrity": "sha512-PwIGnH7jIueXv4opcwEbVGDATjGPO1dx9RkUl5LlHDSe+FXxPwFL5W/qYd5/NHr7f6lo/vvTrAzd0KlQtRusJQ==", + "version": "5.16.0", + "resolved": "https://registry.npmjs.org/@typescript-eslint/typescript-estree/-/typescript-estree-5.16.0.tgz", + "integrity": "sha512-SE4VfbLWUZl9MR+ngLSARptUv2E8brY0luCdgmUevU6arZRY/KxYoLI/3V/yxaURR8tLRN7bmZtJdgmzLHI6pQ==", "dev": true, "requires": { - "@typescript-eslint/types": "5.10.1", - "@typescript-eslint/visitor-keys": "5.10.1", + "@typescript-eslint/types": "5.16.0", + "@typescript-eslint/visitor-keys": "5.16.0", "debug": "^4.3.2", "globby": "^11.0.4", "is-glob": "^4.0.3", @@ -7540,26 +7646,26 @@ } }, "@typescript-eslint/utils": { - "version": "5.10.1", - "resolved": "https://registry.npmjs.org/@typescript-eslint/utils/-/utils-5.10.1.tgz", - "integrity": "sha512-RRmlITiUbLuTRtn/gcPRi4202niF+q7ylFLCKu4c+O/PcpRvZ/nAUwQ2G00bZgpWkhrNLNnvhZLbDn8Ml0qsQw==", + "version": "5.16.0", + "resolved": "https://registry.npmjs.org/@typescript-eslint/utils/-/utils-5.16.0.tgz", + "integrity": "sha512-iYej2ER6AwmejLWMWzJIHy3nPJeGDuCqf8Jnb+jAQVoPpmWzwQOfa9hWVB8GIQE5gsCv/rfN4T+AYb/V06WseQ==", "dev": true, "requires": { "@types/json-schema": "^7.0.9", - "@typescript-eslint/scope-manager": "5.10.1", - "@typescript-eslint/types": "5.10.1", - "@typescript-eslint/typescript-estree": "5.10.1", + "@typescript-eslint/scope-manager": "5.16.0", + "@typescript-eslint/types": "5.16.0", + "@typescript-eslint/typescript-estree": "5.16.0", "eslint-scope": "^5.1.1", "eslint-utils": "^3.0.0" } }, "@typescript-eslint/visitor-keys": { - "version": "5.10.1", - "resolved": "https://registry.npmjs.org/@typescript-eslint/visitor-keys/-/visitor-keys-5.10.1.tgz", - "integrity": "sha512-NjQ0Xinhy9IL979tpoTRuLKxMc0zJC7QVSdeerXs2/QvOy2yRkzX5dRb10X5woNUdJgU8G3nYRDlI33sq1K4YQ==", + "version": "5.16.0", + "resolved": "https://registry.npmjs.org/@typescript-eslint/visitor-keys/-/visitor-keys-5.16.0.tgz", + "integrity": "sha512-jqxO8msp5vZDhikTwq9ubyMHqZ67UIvawohr4qF3KhlpL7gzSjOd+8471H3nh5LyABkaI85laEKKU8SnGUK5/g==", "dev": true, "requires": { - "@typescript-eslint/types": "5.10.1", + "@typescript-eslint/types": "5.16.0", "eslint-visitor-keys": "^3.0.0" } }, @@ -7810,15 +7916,15 @@ "dev": true }, "browserslist": { - "version": "4.19.1", - "resolved": "https://registry.npmjs.org/browserslist/-/browserslist-4.19.1.tgz", - "integrity": "sha512-u2tbbG5PdKRTUoctO3NBD8FQ5HdPh1ZXPHzp1rwaa5jTc+RV9/+RlWiAIKmjRPQF+xbGM9Kklj5bZQFa2s/38A==", + "version": "4.20.2", + "resolved": "https://registry.npmjs.org/browserslist/-/browserslist-4.20.2.tgz", + "integrity": "sha512-CQOBCqp/9pDvDbx3xfMi+86pr4KXIf2FDkTTdeuYw8OxS9t898LA1Khq57gtufFILXpfgsSx5woNgsBgvGjpsA==", "dev": true, "requires": { - "caniuse-lite": "^1.0.30001286", - "electron-to-chromium": "^1.4.17", + "caniuse-lite": "^1.0.30001317", + "electron-to-chromium": "^1.4.84", "escalade": "^3.1.1", - "node-releases": "^2.0.1", + "node-releases": "^2.0.2", "picocolors": "^1.0.0" } }, @@ -7905,9 +8011,9 @@ "dev": true }, "caniuse-lite": { - "version": "1.0.30001304", - "resolved": "https://registry.npmjs.org/caniuse-lite/-/caniuse-lite-1.0.30001304.tgz", - "integrity": "sha512-bdsfZd6K6ap87AGqSHJP/s1V+U6Z5lyrcbBu3ovbCCf8cSYpwTtGrCBObMpJqwxfTbLW6YTIdbb1jEeTelcpYQ==", + "version": "1.0.30001320", + "resolved": "https://registry.npmjs.org/caniuse-lite/-/caniuse-lite-1.0.30001320.tgz", + "integrity": "sha512-MWPzG54AGdo3nWx7zHZTefseM5Y1ccM7hlQKHRqJkPozUaw3hNbBTMmLn16GG2FUzjR13Cr3NPfhIieX5PzXDA==", "dev": true }, "chalk": { @@ -8247,9 +8353,9 @@ "dev": true }, "electron-to-chromium": { - "version": "1.4.57", - "resolved": "https://registry.npmjs.org/electron-to-chromium/-/electron-to-chromium-1.4.57.tgz", - "integrity": "sha512-FNC+P5K1n6pF+M0zIK+gFCoXcJhhzDViL3DRIGy2Fv5PohuSES1JHR7T+GlwxSxlzx4yYbsuzCZvHxcBSRCIOw==", + "version": "1.4.96", + "resolved": "https://registry.npmjs.org/electron-to-chromium/-/electron-to-chromium-1.4.96.tgz", + "integrity": "sha512-DPNjvNGPabv6FcyjzLAN4C0psN/GgD9rSGvMTuv81SeXG/EX3mCz0wiw9N1tUEnfQXYCJi3H8M0oFPRziZh7rw==", "dev": true }, "email-addresses": { @@ -8307,12 +8413,12 @@ "dev": true }, "eslint": { - "version": "8.8.0", - "resolved": "https://registry.npmjs.org/eslint/-/eslint-8.8.0.tgz", - "integrity": "sha512-H3KXAzQGBH1plhYS3okDix2ZthuYJlQQEGE5k0IKuEqUSiyu4AmxxlJ2MtTYeJ3xB4jDhcYCwGOg2TXYdnDXlQ==", + "version": "8.12.0", + "resolved": "https://registry.npmjs.org/eslint/-/eslint-8.12.0.tgz", + "integrity": "sha512-it1oBL9alZg1S8UycLm5YDMAkIhtH6FtAzuZs6YvoGVldWjbS08BkAdb/ymP9LlAyq8koANu32U7Ib/w+UNh8Q==", "dev": true, "requires": { - "@eslint/eslintrc": "^1.0.5", + "@eslint/eslintrc": "^1.2.1", "@humanwhocodes/config-array": "^0.9.2", "ajv": "^6.10.0", "chalk": "^4.0.0", @@ -8320,10 +8426,10 @@ "debug": "^4.3.2", "doctrine": "^3.0.0", "escape-string-regexp": "^4.0.0", - "eslint-scope": "^7.1.0", + "eslint-scope": "^7.1.1", "eslint-utils": "^3.0.0", - "eslint-visitor-keys": "^3.2.0", - "espree": "^9.3.0", + "eslint-visitor-keys": "^3.3.0", + "espree": "^9.3.1", "esquery": "^1.4.0", "esutils": "^2.0.2", "fast-deep-equal": "^3.1.3", @@ -8356,9 +8462,9 @@ "dev": true }, "eslint-scope": { - "version": "7.1.0", - "resolved": "https://registry.npmjs.org/eslint-scope/-/eslint-scope-7.1.0.tgz", - "integrity": "sha512-aWwkhnS0qAXqNOgKOK0dJ2nvzEbhEvpy8OlJ9kZ0FeZnA6zpjv1/Vei+puGFFX7zkPCkHHXb7IDX3A+7yPrRWg==", + "version": "7.1.1", + "resolved": "https://registry.npmjs.org/eslint-scope/-/eslint-scope-7.1.1.tgz", + "integrity": "sha512-QKQM/UXpIiHcLqJ5AOyIW7XZmzjkzQXYE54n1++wb0u9V/abW3l9uQnxX8Z5Xd18xyKIMTUAyQ0k1e8pz6LUrw==", "dev": true, "requires": { "esrecurse": "^4.3.0", @@ -8416,20 +8522,20 @@ } }, "eslint-visitor-keys": { - "version": "3.2.0", - "resolved": "https://registry.npmjs.org/eslint-visitor-keys/-/eslint-visitor-keys-3.2.0.tgz", - "integrity": "sha512-IOzT0X126zn7ALX0dwFiUQEdsfzrm4+ISsQS8nukaJXwEyYKRSnEIIDULYg1mCtGp7UUXgfGl7BIolXREQK+XQ==", + "version": "3.3.0", + "resolved": "https://registry.npmjs.org/eslint-visitor-keys/-/eslint-visitor-keys-3.3.0.tgz", + "integrity": "sha512-mQ+suqKJVyeuwGYHAdjMFqjCyfl8+Ldnxuyp3ldiMBFKkvytrXUZWaiPCEav8qDHKty44bD+qV1IP4T+w+xXRA==", "dev": true }, "espree": { - "version": "9.3.0", - "resolved": "https://registry.npmjs.org/espree/-/espree-9.3.0.tgz", - "integrity": "sha512-d/5nCsb0JcqsSEeQzFZ8DH1RmxPcglRWh24EFTlUEmCKoehXGdpsx0RkHDubqUI8LSAIKMQp4r9SzQ3n+sm4HQ==", + "version": "9.3.1", + "resolved": "https://registry.npmjs.org/espree/-/espree-9.3.1.tgz", + "integrity": "sha512-bvdyLmJMfwkV3NCRl5ZhJf22zBFo1y8bYh3VYb+bfzqNB4Je68P2sSuXyuFquzWLebHpNd2/d5uv7yoP9ISnGQ==", "dev": true, "requires": { "acorn": "^8.7.0", "acorn-jsx": "^5.3.1", - "eslint-visitor-keys": "^3.1.0" + "eslint-visitor-keys": "^3.3.0" } }, "esprima": { @@ -8815,9 +8921,9 @@ } }, "globals": { - "version": "13.12.0", - "resolved": "https://registry.npmjs.org/globals/-/globals-13.12.0.tgz", - "integrity": "sha512-uS8X6lSKN2JumVoXrbUz+uG4BYG+eiawqm3qFcT7ammfbUHeCBoJMlHcec/S3krSk73/AE/f0szYFmgAA3kYZg==", + "version": "13.13.0", + "resolved": "https://registry.npmjs.org/globals/-/globals-13.13.0.tgz", + "integrity": "sha512-EQ7Q18AJlPwp3vUDL4mKA0KXrXyNIQyWon6T6XQiBQF0XHvRsiCSrWmmeATpUzdJN2HhWZU6Pdl0a9zdep5p6A==", "dev": true, "requires": { "type-fest": "^0.20.2" @@ -8891,9 +8997,9 @@ "dev": true }, "has-symbols": { - "version": "1.0.2", - "resolved": "https://registry.npmjs.org/has-symbols/-/has-symbols-1.0.2.tgz", - "integrity": "sha512-chXa79rL/UC2KlX17jo3vRGz0azaWEx5tGqZg5pO3NUyEJVB17dMruQlzCCOfUvElghKcm5194+BCRvi2Rv/Gw==", + "version": "1.0.3", + "resolved": "https://registry.npmjs.org/has-symbols/-/has-symbols-1.0.3.tgz", + "integrity": "sha512-l3LCuF6MgDNwTDKkdYGEihYjt5pRPbEg46rtlmnSPlUbgmB8LOIrKJbYYFBSbnPaJexMKtiPO8hmeRjRz2Td+A==", "dev": true }, "has-yarn": { @@ -9332,9 +9438,9 @@ } }, "istanbul-reports": { - "version": "3.1.3", - "resolved": "https://registry.npmjs.org/istanbul-reports/-/istanbul-reports-3.1.3.tgz", - "integrity": "sha512-x9LtDVtfm/t1GFiLl3NffC7hz+I1ragvgX1P/Lg1NlIagifZDKUkuuaAxH/qpwj2IuEfD8G2Bs/UKp+sZ/pKkg==", + "version": "3.1.4", + "resolved": "https://registry.npmjs.org/istanbul-reports/-/istanbul-reports-3.1.4.tgz", + "integrity": "sha512-r1/DshN4KSE7xWEknZLLLLDn5CJybV3nw01VTkp6D5jzLuELlcbudfj/eSQFvrKsJuTVCGnePO7ho82Nw9zzfw==", "dev": true, "requires": { "html-escaper": "^2.0.0", @@ -9387,13 +9493,10 @@ "dev": true }, "json5": { - "version": "2.2.0", - "resolved": "https://registry.npmjs.org/json5/-/json5-2.2.0.tgz", - "integrity": "sha512-f+8cldu7X/y7RAJurMEJmdoKXGB/X550w2Nr3tTbezL6RwEE/iMcm+tZnXeoZtKuOq6ft8+CqzEkrIgx1fPoQA==", - "dev": true, - "requires": { - "minimist": "^1.2.5" - } + "version": "2.2.1", + "resolved": "https://registry.npmjs.org/json5/-/json5-2.2.1.tgz", + "integrity": "sha512-1hqLFMSrGHRHxav9q9gNjJ5EXznIxGVO09xQRrwplcS8qs28pZ8s8hupZAmqDwZUmVZ2Qb2jnyPOWcDH8m8dlA==", + "dev": true }, "jsonc-parser": { "version": "3.0.0", @@ -9417,9 +9520,9 @@ "dev": true }, "keyv": { - "version": "4.0.5", - "resolved": "https://registry.npmjs.org/keyv/-/keyv-4.0.5.tgz", - "integrity": "sha512-531pkGLqV3BMg0eDqqJFI0R1mkK1Nm5xIP2mM6keP5P8WfFtCkg2IOwplTUmlGoTgIg9yQYZ/kdihhz89XH3vA==", + "version": "4.1.1", + "resolved": "https://registry.npmjs.org/keyv/-/keyv-4.1.1.tgz", + "integrity": "sha512-tGv1yP6snQVDSM4X6yxrv2zzq/EvpW+oYiUz6aueW1u9CtS8RzUQYxxmFwgZlO2jSgCxQbchhxaqXXp2hnKGpQ==", "dev": true, "requires": { "json-buffer": "3.0.1" @@ -9554,13 +9657,13 @@ "dev": true }, "micromatch": { - "version": "4.0.4", - "resolved": "https://registry.npmjs.org/micromatch/-/micromatch-4.0.4.tgz", - "integrity": "sha512-pRmzw/XUcwXGpD9aI9q/0XOwLNygjETJ8y0ao0wdqprrzDa4YnxLcz7fQRZr8voh8V10kGhABbNcHVk5wHgWwg==", + "version": "4.0.5", + "resolved": "https://registry.npmjs.org/micromatch/-/micromatch-4.0.5.tgz", + "integrity": "sha512-DMy+ERcEW2q8Z2Po+WNXuw3c5YaUSFjAO5GsJqfEl7UjvtIuFKO6ZrKvcItdy98dwFI2N1tg3zNIdKaQT+aNdA==", "dev": true, "requires": { - "braces": "^3.0.1", - "picomatch": "^2.2.3" + "braces": "^3.0.2", + "picomatch": "^2.3.1" } }, "mime-db": { @@ -9591,24 +9694,24 @@ "dev": true }, "minimatch": { - "version": "3.0.4", - "resolved": "https://registry.npmjs.org/minimatch/-/minimatch-3.0.4.tgz", - "integrity": "sha512-yJHVQEhyqPLUTgt9B83PXu6W3rx4MvvHvSUvToogpwoGDOUQ+yDrR0HRot+yOCdCO7u4hX3pWft6kWBBcqh0UA==", + "version": "3.1.2", + "resolved": "https://registry.npmjs.org/minimatch/-/minimatch-3.1.2.tgz", + "integrity": "sha512-J7p63hRiAjw1NDEww1W7i37+ByIrOWO5XQQAzZ3VOcL0PNybwpfmV/N05zFAzwQ9USyEcX6t3UO+K5aqBQOIHw==", "dev": true, "requires": { "brace-expansion": "^1.1.7" } }, "minimist": { - "version": "1.2.5", - "resolved": "https://registry.npmjs.org/minimist/-/minimist-1.2.5.tgz", - "integrity": "sha512-FM9nNUYrRBAELZQT3xeZQ7fmMOBg6nWNmJKTcgsJeaLstP/UODVpGsr5OhXhhXg6f+qtJ8uiZ+PUxkDWcgIXLw==", + "version": "1.2.6", + "resolved": "https://registry.npmjs.org/minimist/-/minimist-1.2.6.tgz", + "integrity": "sha512-Jsjnk4bw3YJqYzbdyBiNsPWHPfO++UGG749Cxs6peCu5Xg4nrena6OVxOYxrQTqww0Jmwt+Ref8rggumkTLz9Q==", "dev": true }, "mocha": { - "version": "9.2.0", - "resolved": "https://registry.npmjs.org/mocha/-/mocha-9.2.0.tgz", - "integrity": "sha512-kNn7E8g2SzVcq0a77dkphPsDSN7P+iYkqE0ZsGCYWRsoiKjOt+NvXfaagik8vuDa6W5Zw3qxe8Jfpt5qKf+6/Q==", + "version": "9.2.2", + "resolved": "https://registry.npmjs.org/mocha/-/mocha-9.2.2.tgz", + "integrity": "sha512-L6XC3EdwT6YrIk0yXpavvLkn8h+EU+Y5UcCHKECyMbdUIxyMuZj4bX4U9e1nvnvUUvQVsV2VHQr5zLdcUkhW/g==", "dev": true, "requires": { "@ungap/promise-all-settled": "1.1.2", @@ -9624,9 +9727,9 @@ "he": "1.2.0", "js-yaml": "4.1.0", "log-symbols": "4.1.0", - "minimatch": "3.0.4", + "minimatch": "4.2.1", "ms": "2.1.3", - "nanoid": "3.2.0", + "nanoid": "3.3.1", "serialize-javascript": "6.0.0", "strip-json-comments": "3.1.1", "supports-color": "8.1.1", @@ -9662,6 +9765,15 @@ "p-locate": "^5.0.0" } }, + "minimatch": { + "version": "4.2.1", + "resolved": "https://registry.npmjs.org/minimatch/-/minimatch-4.2.1.tgz", + "integrity": "sha512-9Uq1ChtSZO+Mxa/CL1eGizn2vRn3MlLgzhT0Iz8zaY8NdvxvB0d5QdPFmCKf7JKA9Lerx5vRrnwO03jsSfGG9g==", + "dev": true, + "requires": { + "brace-expansion": "^1.1.7" + } + }, "ms": { "version": "2.1.3", "resolved": "https://registry.npmjs.org/ms/-/ms-2.1.3.tgz", @@ -9737,9 +9849,9 @@ "dev": true }, "nanoid": { - "version": "3.2.0", - "resolved": "https://registry.npmjs.org/nanoid/-/nanoid-3.2.0.tgz", - "integrity": "sha512-fmsZYa9lpn69Ad5eDn7FMcnnSR+8R34W9qJEijxYhTbfOWzr22n1QxCMzXLK+ODyW2973V3Fux959iQoUxzUIA==", + "version": "3.3.1", + "resolved": "https://registry.npmjs.org/nanoid/-/nanoid-3.3.1.tgz", + "integrity": "sha512-n6Vs/3KGyxPQd6uO0eH4Bv0ojGSUvuLlIHtC3Y0kEO23YRge8H9x1GCzLn28YX0H66pMkxuaeESFq4tKISKwdw==", "dev": true }, "natural-compare": { @@ -9766,13 +9878,13 @@ } }, "nise": { - "version": "5.1.0", - "resolved": "https://registry.npmjs.org/nise/-/nise-5.1.0.tgz", - "integrity": "sha512-W5WlHu+wvo3PaKLsJJkgPup2LrsXCcm7AWwyNZkUnn5rwPkuPBi3Iwk5SQtN0mv+K65k7nKKjwNQ30wg3wLAQQ==", + "version": "5.1.1", + "resolved": "https://registry.npmjs.org/nise/-/nise-5.1.1.tgz", + "integrity": "sha512-yr5kW2THW1AkxVmCnKEh4nbYkJdB3I7LUkiUgOvEkOp414mc2UMaHMA7pjq1nYowhdoJZGwEKGaQVbxfpWj10A==", "dev": true, "requires": { - "@sinonjs/commons": "^1.7.0", - "@sinonjs/fake-timers": "^7.0.4", + "@sinonjs/commons": "^1.8.3", + "@sinonjs/fake-timers": ">=5", "@sinonjs/text-encoding": "^0.7.1", "just-extend": "^4.0.2", "path-to-regexp": "^1.7.0" @@ -9797,9 +9909,9 @@ } }, "node-releases": { - "version": "2.0.1", - "resolved": "https://registry.npmjs.org/node-releases/-/node-releases-2.0.1.tgz", - "integrity": "sha512-CqyzN6z7Q6aMeF/ktcMVTzhAHCEpf8SOarwpzpf8pNBY2k5/oM34UHldUwp8VKI7uxct2HxSRdJjBaZeESzcxA==", + "version": "2.0.2", + "resolved": "https://registry.npmjs.org/node-releases/-/node-releases-2.0.2.tgz", + "integrity": "sha512-XxYDdcQ6eKqp/YjI+tb2C5WM2LgjnZrfYg4vgQt49EK268b6gYCHsBLrK2qvJo4FmCtqmKezb0WZFK4fkrZNsg==", "dev": true }, "normalize-path": { @@ -10524,9 +10636,9 @@ } }, "release-it": { - "version": "14.12.4", - "resolved": "https://registry.npmjs.org/release-it/-/release-it-14.12.4.tgz", - "integrity": "sha512-lqf9PMsj7ycCqFHGag8Uv7cE1hNsKa+yKUMe+Fkh9fdOfxu2F01On+YUefRCP0DuQthmr/WyLCYdrjThMEkWFQ==", + "version": "14.13.1", + "resolved": "https://registry.npmjs.org/release-it/-/release-it-14.13.1.tgz", + "integrity": "sha512-mrng5bqZDFMr/7oCH3kuflwjKpKki4dUp6yYGxs20scYCvvd8rHAI5pdQOJHwI5BKHAC/pad0UjAEycMWQnEIw==", "dev": true, "requires": { "@iarna/toml": "2.2.5", @@ -10555,6 +10667,7 @@ "update-notifier": "5.1.0", "url-join": "4.0.1", "uuid": "8.3.2", + "wildcard-match": "5.1.2", "yaml": "1.10.2", "yargs-parser": "20.2.9" }, @@ -10690,9 +10803,9 @@ } }, "rxjs": { - "version": "7.5.2", - "resolved": "https://registry.npmjs.org/rxjs/-/rxjs-7.5.2.tgz", - "integrity": "sha512-PwDt186XaL3QN5qXj/H9DGyHhP3/RYYgZZwqBv9Tv8rsAaiwFH1IsJJlcgD37J7UW5a6O67qX0KWKS3/pu0m4w==", + "version": "7.5.5", + "resolved": "https://registry.npmjs.org/rxjs/-/rxjs-7.5.5.tgz", + "integrity": "sha512-sy+H0pQofO95VDmFLzyaw9xNJU4KTRSwQIGM6+iG3SypAtCiLDzpeG8sJrNCWn2Up9km+KhkvTdbkrdy+yzZdw==", "dev": true, "requires": { "tslib": "^2.1.0" @@ -10767,9 +10880,9 @@ } }, "shiki": { - "version": "0.10.0", - "resolved": "https://registry.npmjs.org/shiki/-/shiki-0.10.0.tgz", - "integrity": "sha512-iczxaIYeBFHTFrQPb9DVy2SKgYxC4Wo7Iucm7C17cCh2Ge/refnvHscUOxM85u57MfLoNOtjoEFUWt9gBexblA==", + "version": "0.10.1", + "resolved": "https://registry.npmjs.org/shiki/-/shiki-0.10.1.tgz", + "integrity": "sha512-VsY7QJVzU51j5o1+DguUd+6vmCmZ5v/6gYu4vyYAhzjuNQU6P/vmSy4uQaOhvje031qQMiW0d2BwgMH52vqMng==", "dev": true, "requires": { "jsonc-parser": "^3.0.0", @@ -10789,34 +10902,23 @@ } }, "signal-exit": { - "version": "3.0.6", - "resolved": "https://registry.npmjs.org/signal-exit/-/signal-exit-3.0.6.tgz", - "integrity": "sha512-sDl4qMFpijcGw22U5w63KmD3cZJfBuFlVNbVMKje2keoKML7X2UzWbc4XrmEbDwg0NXJc3yv4/ox7b+JWb57kQ==", + "version": "3.0.7", + "resolved": "https://registry.npmjs.org/signal-exit/-/signal-exit-3.0.7.tgz", + "integrity": "sha512-wnD2ZE+l+SPC/uoS0vXeE9L1+0wuaMqKlfz9AMUo38JsyLSBWSFcHR1Rri62LZc12vLr1gb3jl7iwQhgwpAbGQ==", "dev": true }, "sinon": { - "version": "13.0.0", - "resolved": "https://registry.npmjs.org/sinon/-/sinon-13.0.0.tgz", - "integrity": "sha512-3tjMDB/tY04b06Bnb4aMKQfNrau2C9HET+R4HVWfv2KegDVLGg4wnBqjVepvxR7S7R1GTwDZzEv52tpFipt6yA==", + "version": "13.0.1", + "resolved": "https://registry.npmjs.org/sinon/-/sinon-13.0.1.tgz", + "integrity": "sha512-8yx2wIvkBjIq/MGY1D9h1LMraYW+z1X0mb648KZnKSdvLasvDu7maa0dFaNYdTDczFgbjNw2tOmWdTk9saVfwQ==", "dev": true, "requires": { "@sinonjs/commons": "^1.8.3", "@sinonjs/fake-timers": "^9.0.0", "@sinonjs/samsam": "^6.1.1", "diff": "^5.0.0", - "nise": "^5.1.0", + "nise": "^5.1.1", "supports-color": "^7.2.0" - }, - "dependencies": { - "@sinonjs/fake-timers": { - "version": "9.0.0", - "resolved": "https://registry.npmjs.org/@sinonjs/fake-timers/-/fake-timers-9.0.0.tgz", - "integrity": "sha512-+shXA2X7KNP7H7qNbQTJ3SA+NQc0pZDSBrdvFSRwF8sAo/ohw+ZQFD8Moc+gnz51+1eRXtEQBpKWPiQ4jsRC/w==", - "dev": true, - "requires": { - "@sinonjs/commons": "^1.7.0" - } - } } }, "slash": { @@ -11021,9 +11123,9 @@ } }, "ts-node": { - "version": "10.4.0", - "resolved": "https://registry.npmjs.org/ts-node/-/ts-node-10.4.0.tgz", - "integrity": "sha512-g0FlPvvCXSIO1JDF6S232P5jPYqBkRL9qly81ZgAOSU7rwI0stphCgd2kLiCrU9DjQCrJMWEqcNSjQL02s6d8A==", + "version": "10.7.0", + "resolved": "https://registry.npmjs.org/ts-node/-/ts-node-10.7.0.tgz", + "integrity": "sha512-TbIGS4xgJoX2i3do417KSaep1uRAW/Lu+WAL2doDHC0D6ummjirVOXU5/7aiZotbQ5p1Zp9tP7U6cYhA0O7M8A==", "dev": true, "requires": { "@cspotcode/source-map-support": "0.7.0", @@ -11037,6 +11139,7 @@ "create-require": "^1.1.0", "diff": "^4.0.1", "make-error": "^1.1.1", + "v8-compile-cache-lib": "^3.0.0", "yn": "3.1.1" }, "dependencies": { @@ -11102,22 +11205,42 @@ } }, "typedoc": { - "version": "0.22.11", - "resolved": "https://registry.npmjs.org/typedoc/-/typedoc-0.22.11.tgz", - "integrity": "sha512-pVr3hh6dkS3lPPaZz1fNpvcrqLdtEvXmXayN55czlamSgvEjh+57GUqfhAI1Xsuu/hNHUT1KNSx8LH2wBP/7SA==", + "version": "0.22.13", + "resolved": "https://registry.npmjs.org/typedoc/-/typedoc-0.22.13.tgz", + "integrity": "sha512-NHNI7Dr6JHa/I3+c62gdRNXBIyX7P33O9TafGLd07ur3MqzcKgwTvpg18EtvCLHJyfeSthAtCLpM7WkStUmDuQ==", "dev": true, "requires": { "glob": "^7.2.0", "lunr": "^2.3.9", - "marked": "^4.0.10", - "minimatch": "^3.0.4", - "shiki": "^0.10.0" + "marked": "^4.0.12", + "minimatch": "^5.0.1", + "shiki": "^0.10.1" + }, + "dependencies": { + "brace-expansion": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/brace-expansion/-/brace-expansion-2.0.1.tgz", + "integrity": "sha512-XnAIvQ8eM+kC6aULx6wuQiwVsnzsi9d3WxzV3FpWTGA19F621kwdbsAcFKXgKUHZWsy+mY6iL1sHTxWEFCytDA==", + "dev": true, + "requires": { + "balanced-match": "^1.0.0" + } + }, + "minimatch": { + "version": "5.0.1", + "resolved": "https://registry.npmjs.org/minimatch/-/minimatch-5.0.1.tgz", + "integrity": "sha512-nLDxIFRyhDblz3qMuq+SoRZED4+miJ/G+tdDrjkkkRnjAsBexeGpgjLEQ0blJy7rHhR2b93rhQY4SvyWu9v03g==", + "dev": true, + "requires": { + "brace-expansion": "^2.0.1" + } + } } }, "typescript": { - "version": "4.5.5", - "resolved": "https://registry.npmjs.org/typescript/-/typescript-4.5.5.tgz", - "integrity": "sha512-TCTIul70LyWe6IJWT8QSYeA54WQe8EjQFU4wY52Fasj5UKx88LNYKCgBEHcOMOrFF1rKGbD8v/xcNWVUq9SymA==", + "version": "4.6.3", + "resolved": "https://registry.npmjs.org/typescript/-/typescript-4.6.3.tgz", + "integrity": "sha512-yNIatDa5iaofVozS/uQJEl3JRWLKKGJKh6Yaiv0GLGSuhpFJe7P3SbHZ8/yjAHRQwKRoA6YZqlfjXWmVzoVSMw==", "dev": true }, "unique-string": { @@ -11231,10 +11354,16 @@ "integrity": "sha512-l8lCEmLcLYZh4nbunNZvQCJc5pv7+RCwa8q/LdUx8u7lsWvPDKmpodJAJNwkAhJC//dFY48KuIEmjtd4RViDrA==", "dev": true }, + "v8-compile-cache-lib": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/v8-compile-cache-lib/-/v8-compile-cache-lib-3.0.0.tgz", + "integrity": "sha512-mpSYqfsFvASnSn5qMiwrr4VKfumbPyONLCOPmsR3A6pTY/r0+tSaVbgPWSAIuzbk3lCTa+FForeTiO+wBQGkjA==", + "dev": true + }, "vscode-oniguruma": { - "version": "1.6.1", - "resolved": "https://registry.npmjs.org/vscode-oniguruma/-/vscode-oniguruma-1.6.1.tgz", - "integrity": "sha512-vc4WhSIaVpgJ0jJIejjYxPvURJavX6QG41vu0mGhqywMkQqulezEqEQ3cO3gc8GvcOpX6ycmKGqRoROEMBNXTQ==", + "version": "1.6.2", + "resolved": "https://registry.npmjs.org/vscode-oniguruma/-/vscode-oniguruma-1.6.2.tgz", + "integrity": "sha512-KH8+KKov5eS/9WhofZR8M8dMHWN2gTxjMsG4jd04YhpbPR91fUj7rYQ2/XjeHCJWbg7X++ApRIU9NUwM2vTvLA==", "dev": true }, "vscode-textmate": { @@ -11292,6 +11421,12 @@ "string-width": "^4.0.0" } }, + "wildcard-match": { + "version": "5.1.2", + "resolved": "https://registry.npmjs.org/wildcard-match/-/wildcard-match-5.1.2.tgz", + "integrity": "sha512-qNXwI591Z88c8bWxp+yjV60Ch4F8Riawe3iGxbzquhy8Xs9m+0+SLFBGb/0yCTIDElawtaImC37fYZ+dr32KqQ==", + "dev": true + }, "windows-release": { "version": "4.0.0", "resolved": "https://registry.npmjs.org/windows-release/-/windows-release-4.0.0.tgz", @@ -11400,9 +11535,9 @@ "dev": true }, "yargs": { - "version": "17.3.1", - "resolved": "https://registry.npmjs.org/yargs/-/yargs-17.3.1.tgz", - "integrity": "sha512-WUANQeVgjLbNsEmGk20f+nlHgOqzRFpiGWVaBrYGYIGANIIu3lWjoyi0fNlFmJkvfhCZ6BXINe7/W2O2bV4iaA==", + "version": "17.4.0", + "resolved": "https://registry.npmjs.org/yargs/-/yargs-17.4.0.tgz", + "integrity": "sha512-WJudfrk81yWFSOkZYpAZx4Nt7V4xp7S/uJkX0CnxovMCt1wCE8LNftPpNuF9X/u9gN5nsD7ycYtRcDf2pL3UiA==", "dev": true, "requires": { "cliui": "^7.0.2", @@ -11415,9 +11550,9 @@ }, "dependencies": { "yargs-parser": { - "version": "21.0.0", - "resolved": "https://registry.npmjs.org/yargs-parser/-/yargs-parser-21.0.0.tgz", - "integrity": "sha512-z9kApYUOCwoeZ78rfRYYWdiU/iNL6mwwYlkkZfJoyMR1xps+NEBX5X7XmRpxkZHhXJ6+Ey00IwKxBBSW9FIjyA==", + "version": "21.0.1", + "resolved": "https://registry.npmjs.org/yargs-parser/-/yargs-parser-21.0.1.tgz", + "integrity": "sha512-9BK1jFpLzJROCI5TzwZL/TU4gqjK5xiHV/RfWLOahrjAko/e4DJkRDZQXfvqAsiZzzYhgAzbgz6lg48jcm4GLg==", "dev": true } } diff --git a/package.json b/package.json index c1b4e3bd8f1..2d9f045b964 100644 --- a/package.json +++ b/package.json @@ -33,8 +33,8 @@ "devDependencies": { "@tsconfig/node12": "^1.0.9", "gh-pages": "^3.2.3", - "release-it": "^14.12.1", - "typescript": "^4.5.4" + "release-it": "^14.13.1", + "typescript": "^4.6.3" }, "repository": { "type": "git", diff --git a/packages/bloom/package.json b/packages/bloom/package.json index e16b512ec5f..a7a0a07a130 100644 --- a/packages/bloom/package.json +++ b/packages/bloom/package.json @@ -18,12 +18,12 @@ "devDependencies": { "@istanbuljs/nyc-config-typescript": "^1.0.2", "@node-redis/test-utils": "*", - "@types/node": "^17.0.13", + "@types/node": "^17.0.23", "nyc": "^15.1.0", - "release-it": "^14.12.4", + "release-it": "^14.13.1", "source-map-support": "^0.5.21", - "ts-node": "^10.4.0", - "typedoc": "^0.22.11", - "typescript": "^4.5.5" + "ts-node": "^10.7.0", + "typedoc": "^0.22.13", + "typescript": "^4.6.3" } } diff --git a/packages/client/lib/client/socket.spec.ts b/packages/client/lib/client/socket.spec.ts index 263320bbf72..4c5cfd1d9b3 100644 --- a/packages/client/lib/client/socket.spec.ts +++ b/packages/client/lib/client/socket.spec.ts @@ -6,7 +6,7 @@ describe('Socket', () => { describe('reconnectStrategy', () => { let clock: SinonFakeTimers; beforeEach(() => clock = useFakeTimers()); - afterEach(() => clock.uninstall()); + afterEach(() => clock.restore()); it('custom strategy', () => { const reconnectStrategy = spy((retries: number): number | Error => { diff --git a/packages/client/package.json b/packages/client/package.json index fb7f6a6af38..3a4db330f6e 100644 --- a/packages/client/package.json +++ b/packages/client/package.json @@ -22,20 +22,20 @@ "devDependencies": { "@istanbuljs/nyc-config-typescript": "^1.0.2", "@node-redis/test-utils": "*", - "@types/node": "^17.0.13", + "@types/node": "^17.0.23", "@types/redis-parser": "^3.0.0", - "@types/sinon": "^10.0.9", + "@types/sinon": "^10.0.11", "@types/yallist": "^4.0.1", - "@typescript-eslint/eslint-plugin": "^5.10.1", - "@typescript-eslint/parser": "^5.10.1", - "eslint": "^8.8.0", + "@typescript-eslint/eslint-plugin": "^5.16.0", + "@typescript-eslint/parser": "^5.16.0", + "eslint": "^8.12.0", "nyc": "^15.1.0", - "release-it": "^14.12.4", - "sinon": "^13.0.0", + "release-it": "^14.13.1", + "sinon": "^13.0.1", "source-map-support": "^0.5.21", - "ts-node": "^10.4.0", - "typedoc": "^0.22.11", - "typescript": "^4.5.5" + "ts-node": "^10.7.0", + "typedoc": "^0.22.13", + "typescript": "^4.6.3" }, "engines": { "node": ">=12" diff --git a/packages/graph/package.json b/packages/graph/package.json index ed4112766cf..66fccd689a1 100644 --- a/packages/graph/package.json +++ b/packages/graph/package.json @@ -18,12 +18,12 @@ "devDependencies": { "@istanbuljs/nyc-config-typescript": "^1.0.2", "@node-redis/test-utils": "*", - "@types/node": "^17.0.13", + "@types/node": "^17.0.23", "nyc": "^15.1.0", - "release-it": "^14.12.4", + "release-it": "^14.13.1", "source-map-support": "^0.5.21", - "ts-node": "^10.4.0", - "typedoc": "^0.22.11", - "typescript": "^4.5.5" + "ts-node": "^10.7.0", + "typedoc": "^0.22.13", + "typescript": "^4.6.3" } } diff --git a/packages/json/package.json b/packages/json/package.json index 361928215a7..0516a7b1b05 100644 --- a/packages/json/package.json +++ b/packages/json/package.json @@ -18,12 +18,12 @@ "devDependencies": { "@istanbuljs/nyc-config-typescript": "^1.0.2", "@node-redis/test-utils": "*", - "@types/node": "^17.0.13", + "@types/node": "^17.0.23", "nyc": "^15.1.0", - "release-it": "^14.12.4", + "release-it": "^14.13.1", "source-map-support": "^0.5.21", - "ts-node": "^10.4.0", - "typedoc": "^0.22.11", - "typescript": "^4.5.5" + "ts-node": "^10.7.0", + "typedoc": "^0.22.13", + "typescript": "^4.6.3" } } diff --git a/packages/search/package.json b/packages/search/package.json index 43daa4d7327..75036c23529 100644 --- a/packages/search/package.json +++ b/packages/search/package.json @@ -18,12 +18,12 @@ "devDependencies": { "@istanbuljs/nyc-config-typescript": "^1.0.2", "@node-redis/test-utils": "*", - "@types/node": "^17.0.13", + "@types/node": "^17.0.23", "nyc": "^15.1.0", - "release-it": "^14.12.4", + "release-it": "^14.13.1", "source-map-support": "^0.5.21", - "ts-node": "^10.4.0", - "typedoc": "^0.22.11", - "typescript": "^4.5.5" + "ts-node": "^10.7.0", + "typedoc": "^0.22.13", + "typescript": "^4.6.3" } } diff --git a/packages/test-utils/package.json b/packages/test-utils/package.json index 263b4cccff4..7d854187337 100644 --- a/packages/test-utils/package.json +++ b/packages/test-utils/package.json @@ -12,13 +12,13 @@ "devDependencies": { "@istanbuljs/nyc-config-typescript": "^1.0.2", "@types/mocha": "^9.1.0", - "@types/node": "^17.0.13", - "@types/yargs": "^17.0.8", - "mocha": "^9.2.0", + "@types/node": "^17.0.23", + "@types/yargs": "^17.0.10", + "mocha": "^9.2.2", "nyc": "^15.1.0", "source-map-support": "^0.5.21", - "ts-node": "^10.4.0", - "typescript": "^4.5.5", - "yargs": "^17.3.1" + "ts-node": "^10.7.0", + "typescript": "^4.6.3", + "yargs": "^17.4.0" } } diff --git a/packages/time-series/package.json b/packages/time-series/package.json index 8891fe61f2e..034cb56c3a5 100644 --- a/packages/time-series/package.json +++ b/packages/time-series/package.json @@ -18,12 +18,12 @@ "devDependencies": { "@istanbuljs/nyc-config-typescript": "^1.0.2", "@node-redis/test-utils": "*", - "@types/node": "^17.0.13", + "@types/node": "^17.0.23", "nyc": "^15.1.0", - "release-it": "^14.12.4", + "release-it": "^14.13.1", "source-map-support": "^0.5.21", - "ts-node": "^10.4.0", - "typedoc": "^0.22.11", - "typescript": "^4.5.5" + "ts-node": "^10.7.0", + "typedoc": "^0.22.13", + "typescript": "^4.6.3" } } From 5821fcbe4dbce5f3ccc21ca4fa790f12a72d55cd Mon Sep 17 00:00:00 2001 From: Avital Fine <98389525+Avital-Fine@users.noreply.github.com> Date: Tue, 29 Mar 2022 00:31:15 +0200 Subject: [PATCH 1168/1748] Support CLUSTER commands (#2015) * Support CLUSTER commands * add some client tests * remove only * delete cluster slaves * delete reset clietn test * SET SLOT * test with client * fix fail * Update CLUSTER_COUNTKEYSINSLOT.spec.ts * move commands to client/commands.ts * clusterNode * remove CLUSTER-SET-CONFIG-EPOCH test with client * clean code Co-authored-by: leibale --- packages/client/lib/client/commands.ts | 45 +++++++++++++++++-- .../client/lib/commands/CLUSTER_ADDSLOTS.ts | 16 +++---- .../lib/commands/CLUSTER_BUMPEPOCH.spec.ts | 19 ++++++++ .../client/lib/commands/CLUSTER_BUMPEPOCH.ts | 5 +++ .../CLUSTER_COUNT-FAILURE-REPORTS.spec.ts | 22 +++++++++ .../commands/CLUSTER_COUNT-FAILURE-REPORTS.ts | 5 +++ .../commands/CLUSTER_COUNTKEYSINSLOT.spec.ts | 19 ++++++++ .../lib/commands/CLUSTER_COUNTKEYSINSLOT.ts | 5 +++ .../lib/commands/CLUSTER_DELSLOTS.spec.ts | 20 +++++++++ .../client/lib/commands/CLUSTER_DELSLOTS.ts | 11 +++++ .../lib/commands/CLUSTER_FAILOVER.spec.ts | 20 +++++++++ .../client/lib/commands/CLUSTER_FAILOVER.ts | 16 +++++++ .../client/lib/commands/CLUSTER_FLUSHSLOTS.ts | 2 +- .../lib/commands/CLUSTER_FORGET.spec.ts | 11 +++++ .../client/lib/commands/CLUSTER_FORGET.ts | 5 +++ .../commands/CLUSTER_GETKEYSINSLOT.spec.ts | 9 ++++ .../lib/commands/CLUSTER_GETKEYSINSLOT.ts | 2 +- .../client/lib/commands/CLUSTER_INFO.spec.ts | 8 ++++ .../lib/commands/CLUSTER_KEYSLOT.spec.ts | 19 ++++++++ .../client/lib/commands/CLUSTER_KEYSLOT.ts | 5 +++ packages/client/lib/commands/CLUSTER_MEET.ts | 2 +- .../client/lib/commands/CLUSTER_MYID.spec.ts | 19 ++++++++ packages/client/lib/commands/CLUSTER_MYID.ts | 5 +++ .../lib/commands/CLUSTER_REPLICAS.spec.ts | 11 +++++ .../client/lib/commands/CLUSTER_REPLICAS.ts | 5 +++ .../lib/commands/CLUSTER_REPLICATE.spec.ts | 11 +++++ .../client/lib/commands/CLUSTER_REPLICATE.ts | 5 +++ .../client/lib/commands/CLUSTER_RESET.spec.ts | 9 +--- packages/client/lib/commands/CLUSTER_RESET.ts | 4 +- .../lib/commands/CLUSTER_SAVECONFIG.spec.ts | 19 ++++++++ .../client/lib/commands/CLUSTER_SAVECONFIG.ts | 5 +++ .../commands/CLUSTER_SET-CONFIG-EPOCH.spec.ts | 11 +++++ .../lib/commands/CLUSTER_SET-CONFIG-EPOCH.ts | 5 +++ .../client/lib/commands/CLUSTER_SETSLOT.ts | 8 +++- packages/client/lib/commands/CLUSTER_SLOTS.ts | 7 ++- .../lib/commands/generic-transformers.spec.ts | 17 +++++++ .../lib/commands/generic-transformers.ts | 15 +++++++ 37 files changed, 393 insertions(+), 29 deletions(-) create mode 100644 packages/client/lib/commands/CLUSTER_BUMPEPOCH.spec.ts create mode 100644 packages/client/lib/commands/CLUSTER_BUMPEPOCH.ts create mode 100644 packages/client/lib/commands/CLUSTER_COUNT-FAILURE-REPORTS.spec.ts create mode 100644 packages/client/lib/commands/CLUSTER_COUNT-FAILURE-REPORTS.ts create mode 100644 packages/client/lib/commands/CLUSTER_COUNTKEYSINSLOT.spec.ts create mode 100644 packages/client/lib/commands/CLUSTER_COUNTKEYSINSLOT.ts create mode 100644 packages/client/lib/commands/CLUSTER_DELSLOTS.spec.ts create mode 100644 packages/client/lib/commands/CLUSTER_DELSLOTS.ts create mode 100644 packages/client/lib/commands/CLUSTER_FAILOVER.spec.ts create mode 100644 packages/client/lib/commands/CLUSTER_FAILOVER.ts create mode 100644 packages/client/lib/commands/CLUSTER_FORGET.spec.ts create mode 100644 packages/client/lib/commands/CLUSTER_FORGET.ts create mode 100644 packages/client/lib/commands/CLUSTER_KEYSLOT.spec.ts create mode 100644 packages/client/lib/commands/CLUSTER_KEYSLOT.ts create mode 100644 packages/client/lib/commands/CLUSTER_MYID.spec.ts create mode 100644 packages/client/lib/commands/CLUSTER_MYID.ts create mode 100644 packages/client/lib/commands/CLUSTER_REPLICAS.spec.ts create mode 100644 packages/client/lib/commands/CLUSTER_REPLICAS.ts create mode 100644 packages/client/lib/commands/CLUSTER_REPLICATE.spec.ts create mode 100644 packages/client/lib/commands/CLUSTER_REPLICATE.ts create mode 100644 packages/client/lib/commands/CLUSTER_SAVECONFIG.spec.ts create mode 100644 packages/client/lib/commands/CLUSTER_SAVECONFIG.ts create mode 100644 packages/client/lib/commands/CLUSTER_SET-CONFIG-EPOCH.spec.ts create mode 100644 packages/client/lib/commands/CLUSTER_SET-CONFIG-EPOCH.ts diff --git a/packages/client/lib/client/commands.ts b/packages/client/lib/client/commands.ts index 41c0769e296..b03cc83230a 100644 --- a/packages/client/lib/client/commands.ts +++ b/packages/client/lib/client/commands.ts @@ -24,13 +24,26 @@ import * as CLIENT_SETNAME from '../commands/CLIENT_SETNAME'; import * as CLIENT_INFO from '../commands/CLIENT_INFO'; import * as CLUSTER_ADDSLOTS from '../commands/CLUSTER_ADDSLOTS'; import * as CLUSTER_ADDSLOTSRANGE from '../commands/CLUSTER_ADDSLOTSRANGE'; +import * as CLUSTER_BUMPEPOCH from '../commands/CLUSTER_BUMPEPOCH'; +import * as CLUSTER_COUNT_FAILURE_REPORTS from '../commands/CLUSTER_COUNT-FAILURE-REPORTS'; +import * as CLUSTER_COUNTKEYSINSLOT from '../commands/CLUSTER_COUNTKEYSINSLOT'; +import * as CLUSTER_DELSLOTS from '../commands/CLUSTER_DELSLOTS'; import * as CLUSTER_DELSLOTSRANGE from '../commands/CLUSTER_DELSLOTSRANGE'; +import * as CLUSTER_FAILOVER from '../commands/CLUSTER_FAILOVER'; import * as CLUSTER_FLUSHSLOTS from '../commands/CLUSTER_FLUSHSLOTS'; +import * as CLUSTER_FORGET from '../commands/CLUSTER_FORGET'; +import * as CLUSTER_GETKEYSINSLOT from '../commands/CLUSTER_GETKEYSINSLOT'; import * as CLUSTER_INFO from '../commands/CLUSTER_INFO'; +import * as CLUSTER_KEYSLOT from '../commands/CLUSTER_KEYSLOT'; import * as CLUSTER_LINKS from '../commands/CLUSTER_LINKS'; -import * as CLUSTER_NODES from '../commands/CLUSTER_NODES'; import * as CLUSTER_MEET from '../commands/CLUSTER_MEET'; +import * as CLUSTER_MYID from '../commands/CLUSTER_MYID'; +import * as CLUSTER_NODES from '../commands/CLUSTER_NODES'; +import * as CLUSTER_REPLICAS from '../commands/CLUSTER_REPLICAS'; +import * as CLUSTER_REPLICATE from '../commands/CLUSTER_REPLICATE'; import * as CLUSTER_RESET from '../commands/CLUSTER_RESET'; +import * as CLUSTER_SAVECONFIG from '../commands/CLUSTER_SAVECONFIG'; +import * as CLUSTER_SET_CONFIG_EPOCH from '../commands/CLUSTER_SET-CONFIG-EPOCH'; import * as CLUSTER_SETSLOT from '../commands/CLUSTER_SETSLOT'; import * as CLUSTER_SLOTS from '../commands/CLUSTER_SLOTS'; import * as COMMAND_COUNT from '../commands/COMMAND_COUNT'; @@ -138,20 +151,46 @@ export default { clusterAddSlots: CLUSTER_ADDSLOTS, CLUSTER_ADDSLOTSRANGE, clusterAddSlotsRange: CLUSTER_ADDSLOTSRANGE, + CLUSTER_BUMPEPOCH, + clusterBumpEpoch: CLUSTER_BUMPEPOCH, + CLUSTER_COUNT_FAILURE_REPORTS, + clusterCountFailureReports: CLUSTER_COUNT_FAILURE_REPORTS, + CLUSTER_COUNTKEYSINSLOT, + clusterCountKeysInSlot: CLUSTER_COUNTKEYSINSLOT, + CLUSTER_DELSLOTS, + clusterDelSlots: CLUSTER_DELSLOTS, CLUSTER_DELSLOTSRANGE, clusterDelSlotsRange: CLUSTER_DELSLOTSRANGE, + CLUSTER_FAILOVER, + clusterFailover: CLUSTER_FAILOVER, CLUSTER_FLUSHSLOTS, clusterFlushSlots: CLUSTER_FLUSHSLOTS, + CLUSTER_FORGET, + clusterForget: CLUSTER_FORGET, + CLUSTER_GETKEYSINSLOT, + clusterGetKeysInSlot: CLUSTER_GETKEYSINSLOT, CLUSTER_INFO, clusterInfo: CLUSTER_INFO, + CLUSTER_KEYSLOT, + clusterKeySlot: CLUSTER_KEYSLOT, CLUSTER_LINKS, clusterLinks: CLUSTER_LINKS, - CLUSTER_NODES, - clusterNodes: CLUSTER_NODES, CLUSTER_MEET, clusterMeet: CLUSTER_MEET, + CLUSTER_MYID, + clusterMyId: CLUSTER_MYID, + CLUSTER_NODES, + clusterNodes: CLUSTER_NODES, + CLUSTER_REPLICAS, + clusterReplicas: CLUSTER_REPLICAS, + CLUSTER_REPLICATE, + clusterReplicate: CLUSTER_REPLICATE, CLUSTER_RESET, clusterReset: CLUSTER_RESET, + CLUSTER_SAVECONFIG, + clusterSaveConfig: CLUSTER_SAVECONFIG, + CLUSTER_SET_CONFIG_EPOCH, + clusterSetConfigEpoch: CLUSTER_SET_CONFIG_EPOCH, CLUSTER_SETSLOT, clusterSetSlot: CLUSTER_SETSLOT, CLUSTER_SLOTS, diff --git a/packages/client/lib/commands/CLUSTER_ADDSLOTS.ts b/packages/client/lib/commands/CLUSTER_ADDSLOTS.ts index e458b8aab91..6cd357fb823 100644 --- a/packages/client/lib/commands/CLUSTER_ADDSLOTS.ts +++ b/packages/client/lib/commands/CLUSTER_ADDSLOTS.ts @@ -1,13 +1,11 @@ -export function transformArguments(slots: number | Array): Array { - const args = ['CLUSTER', 'ADDSLOTS']; +import { RedisCommandArguments } from '.'; +import { pushVerdictNumberArguments } from './generic-transformers'; - if (typeof slots === 'number') { - args.push(slots.toString()); - } else { - args.push(...slots.map(String)); - } - - return args; +export function transformArguments(slots: number | Array): RedisCommandArguments { + return pushVerdictNumberArguments( + ['CLUSTER', 'ADDSLOTS'], + slots + ); } export declare function transformReply(): string; diff --git a/packages/client/lib/commands/CLUSTER_BUMPEPOCH.spec.ts b/packages/client/lib/commands/CLUSTER_BUMPEPOCH.spec.ts new file mode 100644 index 00000000000..f9d2f5437b2 --- /dev/null +++ b/packages/client/lib/commands/CLUSTER_BUMPEPOCH.spec.ts @@ -0,0 +1,19 @@ +import { strict as assert } from 'assert'; +import testUtils, { GLOBAL } from '../test-utils'; +import { transformArguments } from './CLUSTER_BUMPEPOCH'; + +describe('CLUSTER BUMPEPOCH', () => { + it('transformArguments', () => { + assert.deepEqual( + transformArguments(), + ['CLUSTER', 'BUMPEPOCH'] + ); + }); + + testUtils.testWithCluster('clusterNode.clusterBumpEpoch', async cluster => { + assert.equal( + typeof await cluster.getSlotMaster(0).client.clusterBumpEpoch(), + 'string' + ); + }, GLOBAL.SERVERS.OPEN); +}); diff --git a/packages/client/lib/commands/CLUSTER_BUMPEPOCH.ts b/packages/client/lib/commands/CLUSTER_BUMPEPOCH.ts new file mode 100644 index 00000000000..7f81c8fdc42 --- /dev/null +++ b/packages/client/lib/commands/CLUSTER_BUMPEPOCH.ts @@ -0,0 +1,5 @@ +export function transformArguments(): Array { + return ['CLUSTER', 'BUMPEPOCH']; +} + +export declare function transformReply(): 'BUMPED' | 'STILL'; diff --git a/packages/client/lib/commands/CLUSTER_COUNT-FAILURE-REPORTS.spec.ts b/packages/client/lib/commands/CLUSTER_COUNT-FAILURE-REPORTS.spec.ts new file mode 100644 index 00000000000..d84687631bc --- /dev/null +++ b/packages/client/lib/commands/CLUSTER_COUNT-FAILURE-REPORTS.spec.ts @@ -0,0 +1,22 @@ +import { strict as assert } from 'assert'; +import testUtils, { GLOBAL } from '../test-utils'; +import { transformArguments } from './CLUSTER_COUNT-FAILURE-REPORTS'; + +describe('CLUSTER COUNT-FAILURE-REPORTS', () => { + it('transformArguments', () => { + assert.deepEqual( + transformArguments('0'), + ['CLUSTER', 'COUNT-FAILURE-REPORTS', '0'] + ); + }); + + testUtils.testWithCluster('clusterNode.clusterCountFailureReports', async cluster => { + const { client } = cluster.getSlotMaster(0); + assert.equal( + typeof await client.clusterCountFailureReports( + await client.clusterMyId() + ), + 'number' + ); + }, GLOBAL.CLUSTERS.OPEN); +}); diff --git a/packages/client/lib/commands/CLUSTER_COUNT-FAILURE-REPORTS.ts b/packages/client/lib/commands/CLUSTER_COUNT-FAILURE-REPORTS.ts new file mode 100644 index 00000000000..3fbc33052f8 --- /dev/null +++ b/packages/client/lib/commands/CLUSTER_COUNT-FAILURE-REPORTS.ts @@ -0,0 +1,5 @@ +export function transformArguments(nodeId: string): Array { + return ['CLUSTER', 'COUNT-FAILURE-REPORTS', nodeId]; +} + +export declare function transformReply(): number; diff --git a/packages/client/lib/commands/CLUSTER_COUNTKEYSINSLOT.spec.ts b/packages/client/lib/commands/CLUSTER_COUNTKEYSINSLOT.spec.ts new file mode 100644 index 00000000000..ecaed428cb7 --- /dev/null +++ b/packages/client/lib/commands/CLUSTER_COUNTKEYSINSLOT.spec.ts @@ -0,0 +1,19 @@ +import { strict as assert } from 'assert'; +import testUtils, { GLOBAL } from '../test-utils'; +import { transformArguments } from './CLUSTER_COUNTKEYSINSLOT'; + +describe('CLUSTER COUNTKEYSINSLOT', () => { + it('transformArguments', () => { + assert.deepEqual( + transformArguments(0), + ['CLUSTER', 'COUNTKEYSINSLOT', '0'] + ); + }); + + testUtils.testWithCluster('clusterNode.clusterCountKeysInSlot', async cluster => { + assert.equal( + typeof await cluster.getSlotMaster(0).client.clusterCountKeysInSlot(0), + 'number' + ); + }, GLOBAL.CLUSTERS.OPEN); +}); diff --git a/packages/client/lib/commands/CLUSTER_COUNTKEYSINSLOT.ts b/packages/client/lib/commands/CLUSTER_COUNTKEYSINSLOT.ts new file mode 100644 index 00000000000..a5ff75e58a9 --- /dev/null +++ b/packages/client/lib/commands/CLUSTER_COUNTKEYSINSLOT.ts @@ -0,0 +1,5 @@ +export function transformArguments(slot: number): Array { + return ['CLUSTER', 'COUNTKEYSINSLOT', slot.toString()]; +} + +export declare function transformReply(): number; diff --git a/packages/client/lib/commands/CLUSTER_DELSLOTS.spec.ts b/packages/client/lib/commands/CLUSTER_DELSLOTS.spec.ts new file mode 100644 index 00000000000..85d13f4ed3d --- /dev/null +++ b/packages/client/lib/commands/CLUSTER_DELSLOTS.spec.ts @@ -0,0 +1,20 @@ +import { strict as assert } from 'assert'; +import { transformArguments } from './CLUSTER_DELSLOTS'; + +describe('CLUSTER DELSLOTS', () => { + describe('transformArguments', () => { + it('single', () => { + assert.deepEqual( + transformArguments(0), + ['CLUSTER', 'DELSLOTS', '0'] + ); + }); + + it('multiple', () => { + assert.deepEqual( + transformArguments([0, 1]), + ['CLUSTER', 'DELSLOTS', '0', '1'] + ); + }); + }); +}); diff --git a/packages/client/lib/commands/CLUSTER_DELSLOTS.ts b/packages/client/lib/commands/CLUSTER_DELSLOTS.ts new file mode 100644 index 00000000000..bf8d9c18900 --- /dev/null +++ b/packages/client/lib/commands/CLUSTER_DELSLOTS.ts @@ -0,0 +1,11 @@ +import { RedisCommandArguments } from '.'; +import { pushVerdictNumberArguments } from './generic-transformers'; + +export function transformArguments(slots: number | Array): RedisCommandArguments { + return pushVerdictNumberArguments( + ['CLUSTER', 'DELSLOTS'], + slots + ); +} + +export declare function transformReply(): 'OK'; diff --git a/packages/client/lib/commands/CLUSTER_FAILOVER.spec.ts b/packages/client/lib/commands/CLUSTER_FAILOVER.spec.ts new file mode 100644 index 00000000000..578ff56b9cd --- /dev/null +++ b/packages/client/lib/commands/CLUSTER_FAILOVER.spec.ts @@ -0,0 +1,20 @@ +import { strict as assert } from 'assert'; +import { FailoverModes, transformArguments } from './CLUSTER_FAILOVER'; + +describe('CLUSTER FAILOVER', () => { + describe('transformArguments', () => { + it('simple', () => { + assert.deepEqual( + transformArguments(), + ['CLUSTER', 'FAILOVER'] + ); + }); + + it('with mode', () => { + assert.deepEqual( + transformArguments(FailoverModes.FORCE), + ['CLUSTER', 'FAILOVER', 'FORCE'] + ); + }); + }); +}); diff --git a/packages/client/lib/commands/CLUSTER_FAILOVER.ts b/packages/client/lib/commands/CLUSTER_FAILOVER.ts new file mode 100644 index 00000000000..9bc4b69f343 --- /dev/null +++ b/packages/client/lib/commands/CLUSTER_FAILOVER.ts @@ -0,0 +1,16 @@ +export enum FailoverModes { + FORCE = 'FORCE', + TAKEOVER = 'TAKEOVER' +} + +export function transformArguments(mode?: FailoverModes): Array { + const args = ['CLUSTER', 'FAILOVER']; + + if (mode) { + args.push(mode); + } + + return args; +} + +export declare function transformReply(): 'OK'; diff --git a/packages/client/lib/commands/CLUSTER_FLUSHSLOTS.ts b/packages/client/lib/commands/CLUSTER_FLUSHSLOTS.ts index 285c9fd26fe..dfb1e1ccde8 100644 --- a/packages/client/lib/commands/CLUSTER_FLUSHSLOTS.ts +++ b/packages/client/lib/commands/CLUSTER_FLUSHSLOTS.ts @@ -2,4 +2,4 @@ export function transformArguments(): Array { return ['CLUSTER', 'FLUSHSLOTS']; } -export declare function transformReply(): string; +export declare function transformReply(): 'OK'; diff --git a/packages/client/lib/commands/CLUSTER_FORGET.spec.ts b/packages/client/lib/commands/CLUSTER_FORGET.spec.ts new file mode 100644 index 00000000000..cadcdb678f3 --- /dev/null +++ b/packages/client/lib/commands/CLUSTER_FORGET.spec.ts @@ -0,0 +1,11 @@ +import { strict as assert } from 'assert'; +import { transformArguments } from './CLUSTER_FORGET'; + +describe('CLUSTER FORGET', () => { + it('transformArguments', () => { + assert.deepEqual( + transformArguments('0'), + ['CLUSTER', 'FORGET', '0'] + ); + }); +}); diff --git a/packages/client/lib/commands/CLUSTER_FORGET.ts b/packages/client/lib/commands/CLUSTER_FORGET.ts new file mode 100644 index 00000000000..fc557073aeb --- /dev/null +++ b/packages/client/lib/commands/CLUSTER_FORGET.ts @@ -0,0 +1,5 @@ +export function transformArguments(nodeId: string): Array { + return ['CLUSTER', 'FORGET', nodeId]; +} + +export declare function transformReply(): 'OK'; diff --git a/packages/client/lib/commands/CLUSTER_GETKEYSINSLOT.spec.ts b/packages/client/lib/commands/CLUSTER_GETKEYSINSLOT.spec.ts index bb20f7521de..7c156341301 100644 --- a/packages/client/lib/commands/CLUSTER_GETKEYSINSLOT.spec.ts +++ b/packages/client/lib/commands/CLUSTER_GETKEYSINSLOT.spec.ts @@ -1,4 +1,5 @@ import { strict as assert } from 'assert'; +import testUtils, { GLOBAL } from '../test-utils'; import { transformArguments } from './CLUSTER_GETKEYSINSLOT'; describe('CLUSTER GETKEYSINSLOT', () => { @@ -8,4 +9,12 @@ describe('CLUSTER GETKEYSINSLOT', () => { ['CLUSTER', 'GETKEYSINSLOT', '0', '10'] ); }); + + testUtils.testWithCluster('clusterNode.clusterGetKeysInSlot', async cluster => { + const reply = await cluster.getSlotMaster(0).client.clusterGetKeysInSlot(0, 1); + assert.ok(Array.isArray(reply)); + for (const item of reply) { + assert.equal(typeof item, 'string'); + } + }, GLOBAL.CLUSTERS.OPEN); }); diff --git a/packages/client/lib/commands/CLUSTER_GETKEYSINSLOT.ts b/packages/client/lib/commands/CLUSTER_GETKEYSINSLOT.ts index 67c5cbafb77..ec75b7b7336 100644 --- a/packages/client/lib/commands/CLUSTER_GETKEYSINSLOT.ts +++ b/packages/client/lib/commands/CLUSTER_GETKEYSINSLOT.ts @@ -2,4 +2,4 @@ export function transformArguments(slot: number, count: number): Array { return ['CLUSTER', 'GETKEYSINSLOT', slot.toString(), count.toString()]; } -export declare function transformReply(): string; +export declare function transformReply(): Array; diff --git a/packages/client/lib/commands/CLUSTER_INFO.spec.ts b/packages/client/lib/commands/CLUSTER_INFO.spec.ts index a4def45cb79..b770ed33616 100644 --- a/packages/client/lib/commands/CLUSTER_INFO.spec.ts +++ b/packages/client/lib/commands/CLUSTER_INFO.spec.ts @@ -1,4 +1,5 @@ import { strict as assert } from 'assert'; +import testUtils, { GLOBAL } from '../test-utils'; import { transformArguments, transformReply } from './CLUSTER_INFO'; describe('CLUSTER INFO', () => { @@ -43,4 +44,11 @@ describe('CLUSTER INFO', () => { } ); }); + + testUtils.testWithCluster('clusterNode.clusterInfo', async cluster => { + assert.notEqual( + await cluster.getSlotMaster(0).client.clusterInfo(), + null + ); + }, GLOBAL.CLUSTERS.OPEN); }); diff --git a/packages/client/lib/commands/CLUSTER_KEYSLOT.spec.ts b/packages/client/lib/commands/CLUSTER_KEYSLOT.spec.ts new file mode 100644 index 00000000000..a7a5ab9472f --- /dev/null +++ b/packages/client/lib/commands/CLUSTER_KEYSLOT.spec.ts @@ -0,0 +1,19 @@ +import { strict as assert } from 'assert'; +import testUtils, { GLOBAL } from '../test-utils'; +import { transformArguments } from './CLUSTER_KEYSLOT'; + +describe('CLUSTER KEYSLOT', () => { + it('transformArguments', () => { + assert.deepEqual( + transformArguments('key'), + ['CLUSTER', 'KEYSLOT', 'key'] + ); + }); + + testUtils.testWithCluster('clusterNode.clusterKeySlot', async cluster => { + assert.equal( + typeof await cluster.getSlotMaster(0).client.clusterKeySlot('key'), + 'number' + ); + }, GLOBAL.CLUSTERS.OPEN); +}); diff --git a/packages/client/lib/commands/CLUSTER_KEYSLOT.ts b/packages/client/lib/commands/CLUSTER_KEYSLOT.ts new file mode 100644 index 00000000000..0af524ff128 --- /dev/null +++ b/packages/client/lib/commands/CLUSTER_KEYSLOT.ts @@ -0,0 +1,5 @@ +export function transformArguments(key: string): Array { + return ['CLUSTER', 'KEYSLOT', key]; +} + +export declare function transformReply(): number; diff --git a/packages/client/lib/commands/CLUSTER_MEET.ts b/packages/client/lib/commands/CLUSTER_MEET.ts index 54a0bf708a8..e6ce1c1fce4 100644 --- a/packages/client/lib/commands/CLUSTER_MEET.ts +++ b/packages/client/lib/commands/CLUSTER_MEET.ts @@ -2,4 +2,4 @@ export function transformArguments(ip: string, port: number): Array { return ['CLUSTER', 'MEET', ip, port.toString()]; } -export declare function transformReply(): string; +export declare function transformReply(): 'OK'; diff --git a/packages/client/lib/commands/CLUSTER_MYID.spec.ts b/packages/client/lib/commands/CLUSTER_MYID.spec.ts new file mode 100644 index 00000000000..7781c374526 --- /dev/null +++ b/packages/client/lib/commands/CLUSTER_MYID.spec.ts @@ -0,0 +1,19 @@ +import { strict as assert } from 'assert'; +import testUtils, { GLOBAL } from '../test-utils'; +import { transformArguments } from './CLUSTER_MYID'; + +describe('CLUSTER MYID', () => { + it('transformArguments', () => { + assert.deepEqual( + transformArguments(), + ['CLUSTER', 'MYID'] + ); + }); + + testUtils.testWithCluster('clusterNode.clusterMyId', async cluster => { + assert.equal( + typeof await cluster.getSlotMaster(0).client.clusterMyId(), + 'string' + ); + }, GLOBAL.CLUSTERS.OPEN); +}); diff --git a/packages/client/lib/commands/CLUSTER_MYID.ts b/packages/client/lib/commands/CLUSTER_MYID.ts new file mode 100644 index 00000000000..2b61684634d --- /dev/null +++ b/packages/client/lib/commands/CLUSTER_MYID.ts @@ -0,0 +1,5 @@ +export function transformArguments(): Array { + return ['CLUSTER', 'MYID']; +} + +export declare function transformReply(): string; diff --git a/packages/client/lib/commands/CLUSTER_REPLICAS.spec.ts b/packages/client/lib/commands/CLUSTER_REPLICAS.spec.ts new file mode 100644 index 00000000000..6c902dc0d82 --- /dev/null +++ b/packages/client/lib/commands/CLUSTER_REPLICAS.spec.ts @@ -0,0 +1,11 @@ +import { strict as assert } from 'assert'; +import { transformArguments } from './CLUSTER_REPLICAS'; + +describe('CLUSTER REPLICAS', () => { + it('transformArguments', () => { + assert.deepEqual( + transformArguments('0'), + ['CLUSTER', 'REPLICAS', '0'] + ); + }); +}); diff --git a/packages/client/lib/commands/CLUSTER_REPLICAS.ts b/packages/client/lib/commands/CLUSTER_REPLICAS.ts new file mode 100644 index 00000000000..a4130125fbf --- /dev/null +++ b/packages/client/lib/commands/CLUSTER_REPLICAS.ts @@ -0,0 +1,5 @@ +export function transformArguments(nodeId: string): Array { + return ['CLUSTER', 'REPLICAS', nodeId]; +} + +export { transformReply } from './CLUSTER_NODES'; diff --git a/packages/client/lib/commands/CLUSTER_REPLICATE.spec.ts b/packages/client/lib/commands/CLUSTER_REPLICATE.spec.ts new file mode 100644 index 00000000000..926b7dd0a77 --- /dev/null +++ b/packages/client/lib/commands/CLUSTER_REPLICATE.spec.ts @@ -0,0 +1,11 @@ +import { strict as assert } from 'assert'; +import { transformArguments } from './CLUSTER_REPLICATE'; + +describe('CLUSTER REPLICATE', () => { + it('transformArguments', () => { + assert.deepEqual( + transformArguments('0'), + ['CLUSTER', 'REPLICATE', '0'] + ); + }); +}); diff --git a/packages/client/lib/commands/CLUSTER_REPLICATE.ts b/packages/client/lib/commands/CLUSTER_REPLICATE.ts new file mode 100644 index 00000000000..c74e1ec5960 --- /dev/null +++ b/packages/client/lib/commands/CLUSTER_REPLICATE.ts @@ -0,0 +1,5 @@ +export function transformArguments(nodeId: string): Array { + return ['CLUSTER', 'REPLICATE', nodeId]; +} + +export declare function transformReply(): 'OK'; diff --git a/packages/client/lib/commands/CLUSTER_RESET.spec.ts b/packages/client/lib/commands/CLUSTER_RESET.spec.ts index c077e7f8874..340da7457c1 100644 --- a/packages/client/lib/commands/CLUSTER_RESET.spec.ts +++ b/packages/client/lib/commands/CLUSTER_RESET.spec.ts @@ -10,18 +10,11 @@ describe('CLUSTER RESET', () => { ); }); - it('HARD', () => { + it('with mode', () => { assert.deepEqual( transformArguments('HARD'), ['CLUSTER', 'RESET', 'HARD'] ); }); - - it('SOFT', () => { - assert.deepEqual( - transformArguments('SOFT'), - ['CLUSTER', 'RESET', 'SOFT'] - ); - }); }); }); diff --git a/packages/client/lib/commands/CLUSTER_RESET.ts b/packages/client/lib/commands/CLUSTER_RESET.ts index 3e7c5bb52fb..c6901e045dc 100644 --- a/packages/client/lib/commands/CLUSTER_RESET.ts +++ b/packages/client/lib/commands/CLUSTER_RESET.ts @@ -1,6 +1,4 @@ -export type ClusterResetModes = 'HARD' | 'SOFT'; - -export function transformArguments(mode?: ClusterResetModes): Array { +export function transformArguments(mode?: 'HARD' | 'SOFT'): Array { const args = ['CLUSTER', 'RESET']; if (mode) { diff --git a/packages/client/lib/commands/CLUSTER_SAVECONFIG.spec.ts b/packages/client/lib/commands/CLUSTER_SAVECONFIG.spec.ts new file mode 100644 index 00000000000..bcdccd90919 --- /dev/null +++ b/packages/client/lib/commands/CLUSTER_SAVECONFIG.spec.ts @@ -0,0 +1,19 @@ +import { strict as assert } from 'assert'; +import testUtils, { GLOBAL } from '../test-utils'; +import { transformArguments } from './CLUSTER_SAVECONFIG'; + +describe('CLUSTER SAVECONFIG', () => { + it('transformArguments', () => { + assert.deepEqual( + transformArguments(), + ['CLUSTER', 'SAVECONFIG'] + ); + }); + + testUtils.testWithCluster('clusterNode.clusterSaveConfig', async cluster => { + assert.equal( + await cluster.getSlotMaster(0).client.clusterSaveConfig(), + 'OK' + ); + }, GLOBAL.CLUSTERS.OPEN); +}); diff --git a/packages/client/lib/commands/CLUSTER_SAVECONFIG.ts b/packages/client/lib/commands/CLUSTER_SAVECONFIG.ts new file mode 100644 index 00000000000..7e7fb181cc6 --- /dev/null +++ b/packages/client/lib/commands/CLUSTER_SAVECONFIG.ts @@ -0,0 +1,5 @@ +export function transformArguments(): Array { + return ['CLUSTER', 'SAVECONFIG']; +} + +export declare function transformReply(): 'OK'; diff --git a/packages/client/lib/commands/CLUSTER_SET-CONFIG-EPOCH.spec.ts b/packages/client/lib/commands/CLUSTER_SET-CONFIG-EPOCH.spec.ts new file mode 100644 index 00000000000..dd241574168 --- /dev/null +++ b/packages/client/lib/commands/CLUSTER_SET-CONFIG-EPOCH.spec.ts @@ -0,0 +1,11 @@ +import { strict as assert } from 'assert'; +import { transformArguments } from './CLUSTER_SET-CONFIG-EPOCH'; + +describe('CLUSTER SET-CONFIG-EPOCH', () => { + it('transformArguments', () => { + assert.deepEqual( + transformArguments(0), + ['CLUSTER', 'SET-CONFIG-EPOCH', '0'] + ); + }); +}); diff --git a/packages/client/lib/commands/CLUSTER_SET-CONFIG-EPOCH.ts b/packages/client/lib/commands/CLUSTER_SET-CONFIG-EPOCH.ts new file mode 100644 index 00000000000..c50a6b9d3a5 --- /dev/null +++ b/packages/client/lib/commands/CLUSTER_SET-CONFIG-EPOCH.ts @@ -0,0 +1,5 @@ +export function transformArguments(configEpoch: number): Array { + return ['CLUSTER', 'SET-CONFIG-EPOCH', configEpoch.toString()]; +} + +export declare function transformReply(): 'OK'; diff --git a/packages/client/lib/commands/CLUSTER_SETSLOT.ts b/packages/client/lib/commands/CLUSTER_SETSLOT.ts index 591b5fb9632..c01505c71a3 100644 --- a/packages/client/lib/commands/CLUSTER_SETSLOT.ts +++ b/packages/client/lib/commands/CLUSTER_SETSLOT.ts @@ -5,7 +5,11 @@ export enum ClusterSlotStates { NODE = 'NODE' } -export function transformArguments(slot: number, state: ClusterSlotStates, nodeId?: string): Array { +export function transformArguments( + slot: number, + state: ClusterSlotStates, + nodeId?: string +): Array { const args = ['CLUSTER', 'SETSLOT', slot.toString(), state]; if (nodeId) { @@ -15,4 +19,4 @@ export function transformArguments(slot: number, state: ClusterSlotStates, nodeI return args; } -export declare function transformReply(): string; +export declare function transformReply(): 'OK'; diff --git a/packages/client/lib/commands/CLUSTER_SLOTS.ts b/packages/client/lib/commands/CLUSTER_SLOTS.ts index afe1ebc83dd..7e1f5dcc964 100644 --- a/packages/client/lib/commands/CLUSTER_SLOTS.ts +++ b/packages/client/lib/commands/CLUSTER_SLOTS.ts @@ -6,7 +6,12 @@ export function transformArguments(): RedisCommandArguments { type ClusterSlotsRawNode = [ip: string, port: number, id: string]; -type ClusterSlotsRawReply = Array<[from: number, to: number, master: ClusterSlotsRawNode, ...replicas: Array]>; +type ClusterSlotsRawReply = Array<[ + from: number, + to: number, + master: ClusterSlotsRawNode, + ...replicas: Array +]>; type ClusterSlotsNode = { ip: string; diff --git a/packages/client/lib/commands/generic-transformers.spec.ts b/packages/client/lib/commands/generic-transformers.spec.ts index 6e286e284d9..301cab0a75c 100644 --- a/packages/client/lib/commands/generic-transformers.spec.ts +++ b/packages/client/lib/commands/generic-transformers.spec.ts @@ -19,6 +19,7 @@ import { transformPXAT, pushEvalArguments, pushVerdictArguments, + pushVerdictNumberArguments, pushVerdictArgument, pushOptionalVerdictArgument, transformCommandReply, @@ -579,6 +580,22 @@ describe('Generic Transformers', () => { }); }); + describe('pushVerdictNumberArguments', () => { + it('number', () => { + assert.deepEqual( + pushVerdictNumberArguments([], 0), + ['0'] + ); + }); + + it('array', () => { + assert.deepEqual( + pushVerdictNumberArguments([], [0, 1]), + ['0', '1'] + ); + }); + }); + describe('pushVerdictArgument', () => { it('string', () => { assert.deepEqual( diff --git a/packages/client/lib/commands/generic-transformers.ts b/packages/client/lib/commands/generic-transformers.ts index a7bc6805dde..f138cb0430b 100644 --- a/packages/client/lib/commands/generic-transformers.ts +++ b/packages/client/lib/commands/generic-transformers.ts @@ -322,6 +322,21 @@ export function pushVerdictArguments(args: RedisCommandArguments, value: RedisCo return args; } +export function pushVerdictNumberArguments( + args: RedisCommandArguments, + value: number | Array +): RedisCommandArguments { + if (Array.isArray(value)) { + for (const item of value) { + args.push(item.toString()); + } + } else { + args.push(value.toString()); + } + + return args; +} + export function pushVerdictArgument( args: RedisCommandArguments, value: RedisCommandArgument | Array From f6f645bdbb0998cd36d199e56bff3ea46d6d85cd Mon Sep 17 00:00:00 2001 From: Avital Fine <98389525+Avital-Fine@users.noreply.github.com> Date: Tue, 29 Mar 2022 00:36:47 +0200 Subject: [PATCH 1169/1748] Support SORT_RO (#2041) * Support SORT_RO * move pushSortReadOnlyArgs and SortReadOnlyOptions to generic-transformers * clean code Co-authored-by: leibale --- packages/client/lib/cluster/commands.ts | 6 ++ packages/client/lib/commands/SORT.spec.ts | 16 +-- packages/client/lib/commands/SORT.ts | 61 ++---------- packages/client/lib/commands/SORT_RO.spec.ts | 98 +++++++++++++++++++ packages/client/lib/commands/SORT_RO.ts | 15 +++ .../client/lib/commands/SORT_STORE.spec.ts | 96 ++++++++++++++++++ packages/client/lib/commands/SORT_STORE.ts | 17 ++++ .../lib/commands/generic-transformers.ts | 44 +++++++++ 8 files changed, 288 insertions(+), 65 deletions(-) create mode 100644 packages/client/lib/commands/SORT_RO.spec.ts create mode 100644 packages/client/lib/commands/SORT_RO.ts create mode 100644 packages/client/lib/commands/SORT_STORE.spec.ts create mode 100644 packages/client/lib/commands/SORT_STORE.ts diff --git a/packages/client/lib/cluster/commands.ts b/packages/client/lib/cluster/commands.ts index dd82e6f3147..4a2c7e8503b 100644 --- a/packages/client/lib/cluster/commands.ts +++ b/packages/client/lib/cluster/commands.ts @@ -110,6 +110,8 @@ import * as SISMEMBER from '../commands/SISMEMBER'; import * as SMEMBERS from '../commands/SMEMBERS'; import * as SMISMEMBER from '../commands/SMISMEMBER'; import * as SMOVE from '../commands/SMOVE'; +import * as SORT_RO from '../commands/SORT_RO'; +import * as SORT_STORE from '../commands/SORT_STORE'; import * as SORT from '../commands/SORT'; import * as SPOP from '../commands/SPOP'; import * as SRANDMEMBER_COUNT from '../commands/SRANDMEMBER_COUNT'; @@ -408,6 +410,10 @@ export default { smIsMember: SMISMEMBER, SMOVE, sMove: SMOVE, + SORT_RO, + sortRo: SORT_RO, + SORT_STORE, + sortStore: SORT_STORE, SORT, sort: SORT, SPOP, diff --git a/packages/client/lib/commands/SORT.spec.ts b/packages/client/lib/commands/SORT.spec.ts index 637f48876dc..4967b020ad5 100644 --- a/packages/client/lib/commands/SORT.spec.ts +++ b/packages/client/lib/commands/SORT.spec.ts @@ -70,16 +70,7 @@ describe('SORT', () => { ); }); - it('with STORE', () => { - assert.deepEqual( - transformArguments('key', { - STORE: 'destination' - }), - ['SORT', 'key', 'STORE', 'destination'] - ); - }); - - it('with BY, LIMIT, GET, DIRECTION, ALPHA, STORE', () => { + it('with BY, LIMIT, GET, DIRECTION, ALPHA', () => { assert.deepEqual( transformArguments('key', { BY: 'pattern', @@ -89,10 +80,9 @@ describe('SORT', () => { }, GET: 'pattern', DIRECTION: 'ASC', - ALPHA: true, - STORE: 'destination' + ALPHA: true }), - ['SORT', 'key', 'BY', 'pattern', 'LIMIT', '0', '1', 'GET', 'pattern', 'ASC', 'ALPHA', 'STORE', 'destination'] + ['SORT', 'key', 'BY', 'pattern', 'LIMIT', '0', '1', 'GET', 'pattern', 'ASC', 'ALPHA'] ); }); }); diff --git a/packages/client/lib/commands/SORT.ts b/packages/client/lib/commands/SORT.ts index dfa38dc7569..15e95bde677 100644 --- a/packages/client/lib/commands/SORT.ts +++ b/packages/client/lib/commands/SORT.ts @@ -1,56 +1,13 @@ -export const FIRST_KEY_INDEX = 1; - -export const IS_READ_ONLY = true; - -interface SortOptions { - BY?: string; - LIMIT?: { - offset: number; - count: number; - }, - GET?: string | Array; - DIRECTION?: 'ASC' | 'DESC'; - ALPHA?: true; - STORE?: string; -} - -export function transformArguments(key: string, options?: SortOptions): Array { - const args = ['SORT', key]; - - if (options?.BY) { - args.push('BY', options.BY); - } +import { RedisCommandArguments } from '.'; +import { pushSortArguments, SortOptions } from './generic-transformers'; - if (options?.LIMIT) { - args.push( - 'LIMIT', - options.LIMIT.offset.toString(), - options.LIMIT.count.toString() - ); - } - - if (options?.GET) { - for (const pattern of (typeof options.GET === 'string' ? [options.GET] : options.GET)) { - args.push('GET', pattern); - } - } - - if (options?.DIRECTION) { - args.push(options.DIRECTION); - } - - if (options?.ALPHA) { - args.push('ALPHA'); - } - - if (options?.STORE) { - args.push('STORE', options.STORE); - } +export const FIRST_KEY_INDEX = 1; - return args; +export function transformArguments( + key: string, + options?: SortOptions +): RedisCommandArguments { + return pushSortArguments(['SORT', key], options); } -// integer when using `STORE` -export function transformReply(reply: Array | number): Array | number { - return reply; -} +export declare function transformReply(): Array; diff --git a/packages/client/lib/commands/SORT_RO.spec.ts b/packages/client/lib/commands/SORT_RO.spec.ts new file mode 100644 index 00000000000..0cc57991b7f --- /dev/null +++ b/packages/client/lib/commands/SORT_RO.spec.ts @@ -0,0 +1,98 @@ +import { strict as assert } from 'assert'; +import testUtils, { GLOBAL } from '../test-utils'; +import { transformArguments } from './SORT_RO'; + +describe('SORT_RO', () => { + testUtils.isVersionGreaterThanHook([7, 0]); + + describe('transformArguments', () => { + it('simple', () => { + assert.deepEqual( + transformArguments('key'), + ['SORT_RO', 'key'] + ); + }); + + it('with BY', () => { + assert.deepEqual( + transformArguments('key', { + BY: 'pattern' + }), + ['SORT_RO', 'key', 'BY', 'pattern'] + ); + }); + + it('with LIMIT', () => { + assert.deepEqual( + transformArguments('key', { + LIMIT: { + offset: 0, + count: 1 + } + }), + ['SORT_RO', 'key', 'LIMIT', '0', '1'] + ); + }); + + describe('with GET', () => { + it('string', () => { + assert.deepEqual( + transformArguments('key', { + GET: 'pattern' + }), + ['SORT_RO', 'key', 'GET', 'pattern'] + ); + }); + + it('array', () => { + assert.deepEqual( + transformArguments('key', { + GET: ['1', '2'] + }), + ['SORT_RO', 'key', 'GET', '1', 'GET', '2'] + ); + }); + }); + + it('with DIRECTION', () => { + assert.deepEqual( + transformArguments('key', { + DIRECTION: 'ASC' + }), + ['SORT_RO', 'key', 'ASC'] + ); + }); + + it('with ALPHA', () => { + assert.deepEqual( + transformArguments('key', { + ALPHA: true + }), + ['SORT_RO', 'key', 'ALPHA'] + ); + }); + + it('with BY, LIMIT, GET, DIRECTION, ALPHA', () => { + assert.deepEqual( + transformArguments('key', { + BY: 'pattern', + LIMIT: { + offset: 0, + count: 1 + }, + GET: 'pattern', + DIRECTION: 'ASC', + ALPHA: true, + }), + ['SORT_RO', 'key', 'BY', 'pattern', 'LIMIT', '0', '1', 'GET', 'pattern', 'ASC', 'ALPHA'] + ); + }); + }); + + testUtils.testWithClient('client.sortRo', async client => { + assert.deepEqual( + await client.sortRo('key'), + [] + ); + }, GLOBAL.SERVERS.OPEN); +}); diff --git a/packages/client/lib/commands/SORT_RO.ts b/packages/client/lib/commands/SORT_RO.ts new file mode 100644 index 00000000000..4af7acd80d7 --- /dev/null +++ b/packages/client/lib/commands/SORT_RO.ts @@ -0,0 +1,15 @@ +import { RedisCommandArguments } from '.'; +import { pushSortArguments, SortOptions } from "./generic-transformers"; + +export const FIRST_KEY_INDEX = 1; + +export const IS_READ_ONLY = true; + +export function transformArguments( + key: string, + options?: SortOptions +): RedisCommandArguments { + return pushSortArguments(['SORT_RO', key], options); +} + +export declare function transformReply(): Array; diff --git a/packages/client/lib/commands/SORT_STORE.spec.ts b/packages/client/lib/commands/SORT_STORE.spec.ts new file mode 100644 index 00000000000..d078135255d --- /dev/null +++ b/packages/client/lib/commands/SORT_STORE.spec.ts @@ -0,0 +1,96 @@ +import { strict as assert } from 'assert'; +import testUtils, { GLOBAL } from '../test-utils'; +import { transformArguments } from './SORT_STORE'; + +describe('SORT STORE', () => { + describe('transformArguments', () => { + it('simple', () => { + assert.deepEqual( + transformArguments('source', 'destination'), + ['SORT', 'source', 'STORE', 'destination'] + ); + }); + + it('with BY', () => { + assert.deepEqual( + transformArguments('source', 'destination', { + BY: 'pattern' + }), + ['SORT', 'source', 'BY', 'pattern', 'STORE', 'destination'] + ); + }); + + it('with LIMIT', () => { + assert.deepEqual( + transformArguments('source', 'destination', { + LIMIT: { + offset: 0, + count: 1 + } + }), + ['SORT', 'source', 'LIMIT', '0', '1', 'STORE', 'destination'] + ); + }); + + describe('with GET', () => { + it('string', () => { + assert.deepEqual( + transformArguments('source', 'destination', { + GET: 'pattern' + }), + ['SORT', 'source', 'GET', 'pattern', 'STORE', 'destination'] + ); + }); + + it('array', () => { + assert.deepEqual( + transformArguments('source', 'destination', { + GET: ['1', '2'] + }), + ['SORT', 'source', 'GET', '1', 'GET', '2', 'STORE', 'destination'] + ); + }); + }); + + it('with DIRECTION', () => { + assert.deepEqual( + transformArguments('source', 'destination', { + DIRECTION: 'ASC' + }), + ['SORT', 'source', 'ASC', 'STORE', 'destination'] + ); + }); + + it('with ALPHA', () => { + assert.deepEqual( + transformArguments('source', 'destination', { + ALPHA: true + }), + ['SORT', 'source', 'ALPHA', 'STORE', 'destination'] + ); + }); + + it('with BY, LIMIT, GET, DIRECTION, ALPHA', () => { + assert.deepEqual( + transformArguments('source', 'destination', { + BY: 'pattern', + LIMIT: { + offset: 0, + count: 1 + }, + GET: 'pattern', + DIRECTION: 'ASC', + ALPHA: true + }), + ['SORT', 'source', 'BY', 'pattern', 'LIMIT', '0', '1', 'GET', 'pattern', 'ASC', 'ALPHA', 'STORE', 'destination'] + ); + }); + }); + + testUtils.testWithClient('client.sortStore', async client => { + assert.equal( + await client.sortStore('source', 'destination'), + 0 + ); + }, GLOBAL.SERVERS.OPEN); +}); diff --git a/packages/client/lib/commands/SORT_STORE.ts b/packages/client/lib/commands/SORT_STORE.ts new file mode 100644 index 00000000000..9acaf023175 --- /dev/null +++ b/packages/client/lib/commands/SORT_STORE.ts @@ -0,0 +1,17 @@ +import { RedisCommandArguments } from '.'; +import { SortOptions } from './generic-transformers'; +import { transformArguments as transformSortArguments } from './SORT'; + +export const FIRST_KEY_INDEX = 1; + +export function transformArguments( + source: string, + destination: string, + options?: SortOptions +): RedisCommandArguments { + const args = transformSortArguments(source, options); + args.push('STORE', destination); + return args; +} + +export declare function transformReply(): number; diff --git a/packages/client/lib/commands/generic-transformers.ts b/packages/client/lib/commands/generic-transformers.ts index f138cb0430b..7850d22ed41 100644 --- a/packages/client/lib/commands/generic-transformers.ts +++ b/packages/client/lib/commands/generic-transformers.ts @@ -438,6 +438,50 @@ export function transformCommandReply( }; } +export interface SortOptions { + BY?: string; + LIMIT?: { + offset: number; + count: number; + }, + GET?: string | Array; + DIRECTION?: 'ASC' | 'DESC'; + ALPHA?: true; +} + +export function pushSortArguments( + args: RedisCommandArguments, + options?: SortOptions +): RedisCommandArguments { + if (options?.BY) { + args.push('BY', options.BY); + } + + if (options?.LIMIT) { + args.push( + 'LIMIT', + options.LIMIT.offset.toString(), + options.LIMIT.count.toString() + ); + } + + if (options?.GET) { + for (const pattern of (typeof options.GET === 'string' ? [options.GET] : options.GET)) { + args.push('GET', pattern); + } + } + + if (options?.DIRECTION) { + args.push(options.DIRECTION); + } + + if (options?.ALPHA) { + args.push('ALPHA'); + } + + return args; +} + export interface SlotRange { start: number; end: number; From 18ec52d5eea208b46570294b10cbb11f84047ae3 Mon Sep 17 00:00:00 2001 From: Leibale Eidelman Date: Wed, 30 Mar 2022 06:30:43 -0400 Subject: [PATCH 1170/1748] fix #2044 - handle "isolated clients" errors (#2059) --- packages/client/lib/client/index.spec.ts | 11 +++++++++-- packages/client/lib/client/index.ts | 2 +- 2 files changed, 10 insertions(+), 3 deletions(-) diff --git a/packages/client/lib/client/index.spec.ts b/packages/client/lib/client/index.spec.ts index d52086da412..308ce1e9c49 100644 --- a/packages/client/lib/client/index.spec.ts +++ b/packages/client/lib/client/index.spec.ts @@ -477,8 +477,11 @@ describe('Client', () => { assert.ok(id !== isolatedId); }, GLOBAL.SERVERS.OPEN); - async function killClient(client: RedisClientType): Promise { - const onceErrorPromise = once(client, 'error'); + async function killClient( + client: RedisClientType, + errorClient: RedisClientType = client + ): Promise { + const onceErrorPromise = once(errorClient, 'error'); await client.sendCommand(['QUIT']); await Promise.all([ onceErrorPromise, @@ -503,6 +506,10 @@ describe('Client', () => { minimumDockerVersion: [6, 2] // CLIENT INFO }); + testUtils.testWithClient('should propagated errors from "isolated" clients', client => { + return client.executeIsolated(isolated => killClient(isolated, client)); + }, GLOBAL.SERVERS.OPEN); + testUtils.testWithClient('scanIterator', async client => { const promises = [], keys = new Set(); diff --git a/packages/client/lib/client/index.ts b/packages/client/lib/client/index.ts index da0b95bd420..1dd74fa1afe 100644 --- a/packages/client/lib/client/index.ts +++ b/packages/client/lib/client/index.ts @@ -189,7 +189,7 @@ export default class RedisClient create: async () => { const duplicate = this.duplicate({ isolationPoolOptions: undefined - }); + }).on('error', err => this.emit('error', err)); await duplicate.connect(); return duplicate; }, From b0fb3bd63f03fa8befce1113d313a04da459295c Mon Sep 17 00:00:00 2001 From: Avital Fine <98389525+Avital-Fine@users.noreply.github.com> Date: Wed, 30 Mar 2022 12:31:26 +0200 Subject: [PATCH 1171/1748] Support COMMAND GETKEYSANDFLAGS (#2039) * Support COMMAND GETKEYSANDFLAGS * Update COMMAND_GETKEYSANDFLAGS.spec.ts remove '.only' * clean code Co-authored-by: leibale --- packages/client/lib/client/commands.ts | 3 +++ .../commands/COMMAND_GETKEYSANDFLAGS.spec.ts | 24 +++++++++++++++++++ .../lib/commands/COMMAND_GETKEYSANDFLAGS.ts | 24 +++++++++++++++++++ 3 files changed, 51 insertions(+) create mode 100644 packages/client/lib/commands/COMMAND_GETKEYSANDFLAGS.spec.ts create mode 100644 packages/client/lib/commands/COMMAND_GETKEYSANDFLAGS.ts diff --git a/packages/client/lib/client/commands.ts b/packages/client/lib/client/commands.ts index b03cc83230a..eefa1346585 100644 --- a/packages/client/lib/client/commands.ts +++ b/packages/client/lib/client/commands.ts @@ -48,6 +48,7 @@ import * as CLUSTER_SETSLOT from '../commands/CLUSTER_SETSLOT'; import * as CLUSTER_SLOTS from '../commands/CLUSTER_SLOTS'; import * as COMMAND_COUNT from '../commands/COMMAND_COUNT'; import * as COMMAND_GETKEYS from '../commands/COMMAND_GETKEYS'; +import * as COMMAND_GETKEYSANDFLAGS from '../commands/COMMAND_GETKEYSANDFLAGS'; import * as COMMAND_INFO from '../commands/COMMAND_INFO'; import * as COMMAND_LIST from '../commands/COMMAND_LIST'; import * as COMMAND from '../commands/COMMAND'; @@ -199,6 +200,8 @@ export default { commandCount: COMMAND_COUNT, COMMAND_GETKEYS, commandGetKeys: COMMAND_GETKEYS, + COMMAND_GETKEYSANDFLAGS, + commandGetKeysAndFlags: COMMAND_GETKEYSANDFLAGS, COMMAND_INFO, commandInfo: COMMAND_INFO, COMMAND_LIST, diff --git a/packages/client/lib/commands/COMMAND_GETKEYSANDFLAGS.spec.ts b/packages/client/lib/commands/COMMAND_GETKEYSANDFLAGS.spec.ts new file mode 100644 index 00000000000..19bc27d5f99 --- /dev/null +++ b/packages/client/lib/commands/COMMAND_GETKEYSANDFLAGS.spec.ts @@ -0,0 +1,24 @@ +import { strict as assert } from 'assert'; +import testUtils, { GLOBAL } from '../test-utils'; +import { transformArguments } from './COMMAND_GETKEYSANDFLAGS'; + +describe('COMMAND GETKEYSANDFLAGS', () => { + testUtils.isVersionGreaterThanHook([7, 0]); + + it('transformArguments', () => { + assert.deepEqual( + transformArguments(['GET', 'key']), + ['COMMAND', 'GETKEYSANDFLAGS', 'GET', 'key'] + ); + }); + + testUtils.testWithClient('client.commandGetKeysAndFlags', async client => { + assert.deepEqual( + await client.commandGetKeysAndFlags(['GET', 'key']), + [{ + key: 'key', + flags: ['RO', 'access'] + }] + ); + }, GLOBAL.SERVERS.OPEN); +}); diff --git a/packages/client/lib/commands/COMMAND_GETKEYSANDFLAGS.ts b/packages/client/lib/commands/COMMAND_GETKEYSANDFLAGS.ts new file mode 100644 index 00000000000..96b28186ccd --- /dev/null +++ b/packages/client/lib/commands/COMMAND_GETKEYSANDFLAGS.ts @@ -0,0 +1,24 @@ +import { RedisCommandArgument, RedisCommandArguments } from '.'; + +export const IS_READ_ONLY = true; + +export function transformArguments(args: Array): RedisCommandArguments { + return ['COMMAND', 'GETKEYSANDFLAGS', ...args]; +} + +type KeysAndFlagsRawReply = Array<[ + RedisCommandArgument, + RedisCommandArguments +]>; + +type KeysAndFlagsReply = Array<{ + key: RedisCommandArgument; + flags: RedisCommandArguments; +}>; + +export function transformReply(reply: KeysAndFlagsRawReply): KeysAndFlagsReply { + return reply.map(([key, flags]) => ({ + key, + flags + })); +} From 33a3f3f6c6ae5e6a6877a8dbf3fd60878af687b3 Mon Sep 17 00:00:00 2001 From: Leibale Eidelman Date: Wed, 30 Mar 2022 08:12:21 -0400 Subject: [PATCH 1172/1748] run tests with redis 7 as well - copied from #2020 (#2062) * run tests on redis 7 as well - copied from #2020 * copy some changes from #2020 * clean BITCOUNT --- .github/workflows/tests.yml | 2 +- .../client/lib/commands/ACL_GETUSER.spec.ts | 36 ++++++++++++------ packages/client/lib/commands/ACL_GETUSER.ts | 30 ++++++++------- packages/client/lib/commands/BITCOUNT.ts | 2 +- .../client/lib/commands/COMMAND_INFO.spec.ts | 6 ++- packages/test-utils/lib/dockers.ts | 4 +- packages/test-utils/lib/index.ts | 38 ++++++++++++------- 7 files changed, 75 insertions(+), 43 deletions(-) diff --git a/.github/workflows/tests.yml b/.github/workflows/tests.yml index 0ce929fbc5b..a0c8223d663 100644 --- a/.github/workflows/tests.yml +++ b/.github/workflows/tests.yml @@ -17,7 +17,7 @@ jobs: fail-fast: false matrix: node-version: ['12', '14', '16'] - redis-version: ['5', '6.0', '6.2'] + redis-version: ['5', '6.0', '6.2', '7.0-rc2'] steps: - uses: actions/checkout@v2.3.4 with: diff --git a/packages/client/lib/commands/ACL_GETUSER.spec.ts b/packages/client/lib/commands/ACL_GETUSER.spec.ts index fcc10768e61..f91f2ff9e5c 100644 --- a/packages/client/lib/commands/ACL_GETUSER.spec.ts +++ b/packages/client/lib/commands/ACL_GETUSER.spec.ts @@ -13,20 +13,32 @@ describe('ACL GETUSER', () => { }); testUtils.testWithClient('client.aclGetUser', async client => { + const expectedReply: any = { + passwords: [], + commands: '+@all', + }; + + if (testUtils.isVersionGreaterThan([7])) { + expectedReply.flags = ['on', 'nopass']; + expectedReply.keys = '~*'; + expectedReply.channels = '&*'; + expectedReply.selectors = []; + } else { + expectedReply.keys = ['*']; + expectedReply.selectors = undefined; + + if (testUtils.isVersionGreaterThan([6, 2])) { + expectedReply.flags = ['on', 'allkeys', 'allchannels', 'allcommands', 'nopass']; + expectedReply.channels = ['*']; + } else { + expectedReply.flags = ['on', 'allkeys', 'allcommands', 'nopass']; + expectedReply.channels = undefined; + } + } + assert.deepEqual( await client.aclGetUser('default'), - { - passwords: [], - commands: '+@all', - keys: ['*'], - ...(testUtils.isVersionGreaterThan([6, 2]) ? { - flags: ['on', 'allkeys', 'allchannels', 'allcommands', 'nopass'], - channels: ['*'] - } : { - flags: ['on', 'allkeys', 'allcommands', 'nopass'], - channels: undefined - }) - } + expectedReply ); }, GLOBAL.SERVERS.OPEN); }); diff --git a/packages/client/lib/commands/ACL_GETUSER.ts b/packages/client/lib/commands/ACL_GETUSER.ts index cdb9f3aa783..818a945bac1 100644 --- a/packages/client/lib/commands/ACL_GETUSER.ts +++ b/packages/client/lib/commands/ACL_GETUSER.ts @@ -5,24 +5,27 @@ export function transformArguments(username: RedisCommandArgument): RedisCommand } type AclGetUserRawReply = [ - _: RedisCommandArgument, - flags: Array, - _: RedisCommandArgument, - passwords: Array, - _: RedisCommandArgument, - commands: RedisCommandArgument, - _: RedisCommandArgument, - keys: Array, - _: RedisCommandArgument, - channels: Array + 'flags', + Array, + 'passwords', + Array, + 'commands', + RedisCommandArgument, + 'keys', + Array | RedisCommandArgument, + 'channels', + Array | RedisCommandArgument, + 'selectors' | undefined, + Array> | undefined ]; interface AclUser { flags: Array; passwords: Array; commands: RedisCommandArgument; - keys: Array; - channels: Array + keys: Array | RedisCommandArgument; + channels: Array | RedisCommandArgument; + selectors?: Array>; } export function transformReply(reply: AclGetUserRawReply): AclUser { @@ -31,6 +34,7 @@ export function transformReply(reply: AclGetUserRawReply): AclUser { passwords: reply[3], commands: reply[5], keys: reply[7], - channels: reply[9] + channels: reply[9], + selectors: reply[11] }; } diff --git a/packages/client/lib/commands/BITCOUNT.ts b/packages/client/lib/commands/BITCOUNT.ts index d937af2b5dd..4bbd4f00911 100644 --- a/packages/client/lib/commands/BITCOUNT.ts +++ b/packages/client/lib/commands/BITCOUNT.ts @@ -22,7 +22,7 @@ export function transformArguments( range.end.toString() ); - if (range?.mode) { + if (range.mode) { args.push(range.mode); } } diff --git a/packages/client/lib/commands/COMMAND_INFO.spec.ts b/packages/client/lib/commands/COMMAND_INFO.spec.ts index d6488460484..c54a5d0aeb3 100644 --- a/packages/client/lib/commands/COMMAND_INFO.spec.ts +++ b/packages/client/lib/commands/COMMAND_INFO.spec.ts @@ -9,7 +9,11 @@ export function assertPingCommand(commandInfo: CommandReply | null | undefined): { name: 'ping', arity: -1, - flags: new Set([CommandFlags.STALE, CommandFlags.FAST]), + flags: new Set( + testUtils.isVersionGreaterThan([7]) ? + [CommandFlags.FAST] : + [CommandFlags.STALE, CommandFlags.FAST] + ), firstKeyIndex: 0, lastKeyIndex: 0, step: 0, diff --git a/packages/test-utils/lib/dockers.ts b/packages/test-utils/lib/dockers.ts index 3dd6dcf3ebf..0bf4f034bfc 100644 --- a/packages/test-utils/lib/dockers.ts +++ b/packages/test-utils/lib/dockers.ts @@ -38,7 +38,7 @@ const portIterator = (async function*(): AsyncIterableIterator { export interface RedisServerDockerConfig { image: string; - version: Array; + version: string; } export interface RedisServerDocker { @@ -54,7 +54,7 @@ async function spawnRedisServerDocker({ image, version }: RedisServerDockerConfi { stdout, stderr } = await execAsync( 'docker run -d --network host $(' + `docker build ${DOCKER_FODLER_PATH} -q ` + - `--build-arg IMAGE=${image}:${version.join('.')} ` + + `--build-arg IMAGE=${image}:${version} ` + `--build-arg REDIS_ARGUMENTS="--save --port ${port.toString()} ${serverArguments.join(' ')}"` + ')' ); diff --git a/packages/test-utils/lib/index.ts b/packages/test-utils/lib/index.ts index 56847ce84dd..9eaed1f8d3c 100644 --- a/packages/test-utils/lib/index.ts +++ b/packages/test-utils/lib/index.ts @@ -27,49 +27,61 @@ interface ClusterTestOptions ext numberOfNodes?: number; } +interface Version { + string: string; + numbers: Array; +} + export default class TestUtils { - static #getVersion(argumentName: string, defaultVersion: string): Array { + static #getVersion(argumentName: string, defaultVersion: string): Version { return yargs(hideBin(process.argv)) .option(argumentName, { type: 'string', default: defaultVersion }) .coerce(argumentName, (arg: string) => { - return arg.split('.').map(x => { - const value = Number(x); - if (Number.isNaN(value)) { - throw new TypeError(`${arg} is not a valid redis version`); - } + const indexOfDash = arg.indexOf('-'); + return { + string: arg, + numbers: (indexOfDash === -1 ? arg : arg.substring(0, indexOfDash)).split('.').map(x => { + const value = Number(x); + if (Number.isNaN(value)) { + throw new TypeError(`${arg} is not a valid redis version`); + } - return value; - }); + return value; + }) + }; }) .demandOption(argumentName) .parseSync()[argumentName]; } + readonly #VERSION_NUMBERS: Array; readonly #DOCKER_IMAGE: RedisServerDockerConfig; constructor(config: TestUtilsConfig) { + const { string, numbers } = TestUtils.#getVersion(config.dockerImageVersionArgument, config.defaultDockerVersion); + this.#VERSION_NUMBERS = numbers; this.#DOCKER_IMAGE = { image: config.dockerImageName, - version: TestUtils.#getVersion(config.dockerImageVersionArgument, config.defaultDockerVersion) + version: string }; } isVersionGreaterThan(minimumVersion: Array | undefined): boolean { if (minimumVersion === undefined) return true; - const lastIndex = Math.min(this.#DOCKER_IMAGE.version.length, minimumVersion.length) - 1; + const lastIndex = Math.min(this.#VERSION_NUMBERS.length, minimumVersion.length) - 1; for (let i = 0; i < lastIndex; i++) { - if (this.#DOCKER_IMAGE.version[i] > minimumVersion[i]) { + if (this.#VERSION_NUMBERS[i] > minimumVersion[i]) { return true; - } else if (minimumVersion[i] > this.#DOCKER_IMAGE.version[i]) { + } else if (minimumVersion[i] > this.#VERSION_NUMBERS[i]) { return false; } } - return this.#DOCKER_IMAGE.version[lastIndex] >= minimumVersion[lastIndex]; + return this.#VERSION_NUMBERS[lastIndex] >= minimumVersion[lastIndex]; } isVersionGreaterThanHook(minimumVersion: Array | undefined): void { From 4683e969b8fa4840b1d901c02bea300cf3be6922 Mon Sep 17 00:00:00 2001 From: Avital Fine <79420960+AvitalFineRedis@users.noreply.github.com> Date: Thu, 31 Mar 2022 13:13:06 +0200 Subject: [PATCH 1173/1748] Support Vector Similarity (#1785) * ft.alter * support paramas * remove only and skip * merge * fix imports * add Vector field * update version * push attributes * typo * test * version check * remove .only * remove unued import * add support for DIALECT * clean code Co-authored-by: Avital-Fine Co-authored-by: leibale --- package-lock.json | 180 ++++++------ .../search/lib/commands/AGGREGATE.spec.ts | 20 ++ packages/search/lib/commands/AGGREGATE.ts | 20 +- packages/search/lib/commands/CREATE.spec.ts | 48 +++- packages/search/lib/commands/EXPLAIN.spec.ts | 32 ++- packages/search/lib/commands/EXPLAIN.ts | 23 +- packages/search/lib/commands/INFO.spec.ts | 59 ++-- packages/search/lib/commands/INFO.ts | 271 +++++++++--------- .../search/lib/commands/PROFILE_SEARCH.ts | 6 +- packages/search/lib/commands/SEARCH.spec.ts | 117 ++++++-- packages/search/lib/commands/SEARCH.ts | 11 +- .../search/lib/commands/SPELLCHECK.spec.ts | 9 + packages/search/lib/commands/SPELLCHECK.ts | 5 + packages/search/lib/commands/index.ts | 130 ++++++++- packages/search/lib/index.ts | 4 +- packages/search/lib/test-utils.ts | 2 +- 16 files changed, 632 insertions(+), 305 deletions(-) diff --git a/package-lock.json b/package-lock.json index 641d253ef0c..2f4f2cb5ffb 100644 --- a/package-lock.json +++ b/package-lock.json @@ -978,14 +978,14 @@ "dev": true }, "node_modules/@typescript-eslint/eslint-plugin": { - "version": "5.16.0", - "resolved": "https://registry.npmjs.org/@typescript-eslint/eslint-plugin/-/eslint-plugin-5.16.0.tgz", - "integrity": "sha512-SJoba1edXvQRMmNI505Uo4XmGbxCK9ARQpkvOd00anxzri9RNQk0DDCxD+LIl+jYhkzOJiOMMKYEHnHEODjdCw==", + "version": "5.17.0", + "resolved": "https://registry.npmjs.org/@typescript-eslint/eslint-plugin/-/eslint-plugin-5.17.0.tgz", + "integrity": "sha512-qVstvQilEd89HJk3qcbKt/zZrfBZ+9h2ynpAGlWjWiizA7m/MtLT9RoX6gjtpE500vfIg8jogAkDzdCxbsFASQ==", "dev": true, "dependencies": { - "@typescript-eslint/scope-manager": "5.16.0", - "@typescript-eslint/type-utils": "5.16.0", - "@typescript-eslint/utils": "5.16.0", + "@typescript-eslint/scope-manager": "5.17.0", + "@typescript-eslint/type-utils": "5.17.0", + "@typescript-eslint/utils": "5.17.0", "debug": "^4.3.2", "functional-red-black-tree": "^1.0.1", "ignore": "^5.1.8", @@ -1026,14 +1026,14 @@ } }, "node_modules/@typescript-eslint/parser": { - "version": "5.16.0", - "resolved": "https://registry.npmjs.org/@typescript-eslint/parser/-/parser-5.16.0.tgz", - "integrity": "sha512-fkDq86F0zl8FicnJtdXakFs4lnuebH6ZADDw6CYQv0UZeIjHvmEw87m9/29nk2Dv5Lmdp0zQ3zDQhiMWQf/GbA==", + "version": "5.17.0", + "resolved": "https://registry.npmjs.org/@typescript-eslint/parser/-/parser-5.17.0.tgz", + "integrity": "sha512-aRzW9Jg5Rlj2t2/crzhA2f23SIYFlF9mchGudyP0uiD6SenIxzKoLjwzHbafgHn39dNV/TV7xwQkLfFTZlJ4ig==", "dev": true, "dependencies": { - "@typescript-eslint/scope-manager": "5.16.0", - "@typescript-eslint/types": "5.16.0", - "@typescript-eslint/typescript-estree": "5.16.0", + "@typescript-eslint/scope-manager": "5.17.0", + "@typescript-eslint/types": "5.17.0", + "@typescript-eslint/typescript-estree": "5.17.0", "debug": "^4.3.2" }, "engines": { @@ -1053,13 +1053,13 @@ } }, "node_modules/@typescript-eslint/scope-manager": { - "version": "5.16.0", - "resolved": "https://registry.npmjs.org/@typescript-eslint/scope-manager/-/scope-manager-5.16.0.tgz", - "integrity": "sha512-P+Yab2Hovg8NekLIR/mOElCDPyGgFZKhGoZA901Yax6WR6HVeGLbsqJkZ+Cvk5nts/dAlFKm8PfL43UZnWdpIQ==", + "version": "5.17.0", + "resolved": "https://registry.npmjs.org/@typescript-eslint/scope-manager/-/scope-manager-5.17.0.tgz", + "integrity": "sha512-062iCYQF/doQ9T2WWfJohQKKN1zmmXVfAcS3xaiialiw8ZUGy05Em6QVNYJGO34/sU1a7a+90U3dUNfqUDHr3w==", "dev": true, "dependencies": { - "@typescript-eslint/types": "5.16.0", - "@typescript-eslint/visitor-keys": "5.16.0" + "@typescript-eslint/types": "5.17.0", + "@typescript-eslint/visitor-keys": "5.17.0" }, "engines": { "node": "^12.22.0 || ^14.17.0 || >=16.0.0" @@ -1070,12 +1070,12 @@ } }, "node_modules/@typescript-eslint/type-utils": { - "version": "5.16.0", - "resolved": "https://registry.npmjs.org/@typescript-eslint/type-utils/-/type-utils-5.16.0.tgz", - "integrity": "sha512-SKygICv54CCRl1Vq5ewwQUJV/8padIWvPgCxlWPGO/OgQLCijY9G7lDu6H+mqfQtbzDNlVjzVWQmeqbLMBLEwQ==", + "version": "5.17.0", + "resolved": "https://registry.npmjs.org/@typescript-eslint/type-utils/-/type-utils-5.17.0.tgz", + "integrity": "sha512-3hU0RynUIlEuqMJA7dragb0/75gZmwNwFf/QJokWzPehTZousP/MNifVSgjxNcDCkM5HI2K22TjQWUmmHUINSg==", "dev": true, "dependencies": { - "@typescript-eslint/utils": "5.16.0", + "@typescript-eslint/utils": "5.17.0", "debug": "^4.3.2", "tsutils": "^3.21.0" }, @@ -1096,9 +1096,9 @@ } }, "node_modules/@typescript-eslint/types": { - "version": "5.16.0", - "resolved": "https://registry.npmjs.org/@typescript-eslint/types/-/types-5.16.0.tgz", - "integrity": "sha512-oUorOwLj/3/3p/HFwrp6m/J2VfbLC8gjW5X3awpQJ/bSG+YRGFS4dpsvtQ8T2VNveV+LflQHjlLvB6v0R87z4g==", + "version": "5.17.0", + "resolved": "https://registry.npmjs.org/@typescript-eslint/types/-/types-5.17.0.tgz", + "integrity": "sha512-AgQ4rWzmCxOZLioFEjlzOI3Ch8giDWx8aUDxyNw9iOeCvD3GEYAB7dxWGQy4T/rPVe8iPmu73jPHuaSqcjKvxw==", "dev": true, "engines": { "node": "^12.22.0 || ^14.17.0 || >=16.0.0" @@ -1109,13 +1109,13 @@ } }, "node_modules/@typescript-eslint/typescript-estree": { - "version": "5.16.0", - "resolved": "https://registry.npmjs.org/@typescript-eslint/typescript-estree/-/typescript-estree-5.16.0.tgz", - "integrity": "sha512-SE4VfbLWUZl9MR+ngLSARptUv2E8brY0luCdgmUevU6arZRY/KxYoLI/3V/yxaURR8tLRN7bmZtJdgmzLHI6pQ==", + "version": "5.17.0", + "resolved": "https://registry.npmjs.org/@typescript-eslint/typescript-estree/-/typescript-estree-5.17.0.tgz", + "integrity": "sha512-X1gtjEcmM7Je+qJRhq7ZAAaNXYhTgqMkR10euC4Si6PIjb+kwEQHSxGazXUQXFyqfEXdkGf6JijUu5R0uceQzg==", "dev": true, "dependencies": { - "@typescript-eslint/types": "5.16.0", - "@typescript-eslint/visitor-keys": "5.16.0", + "@typescript-eslint/types": "5.17.0", + "@typescript-eslint/visitor-keys": "5.17.0", "debug": "^4.3.2", "globby": "^11.0.4", "is-glob": "^4.0.3", @@ -1180,15 +1180,15 @@ } }, "node_modules/@typescript-eslint/utils": { - "version": "5.16.0", - "resolved": "https://registry.npmjs.org/@typescript-eslint/utils/-/utils-5.16.0.tgz", - "integrity": "sha512-iYej2ER6AwmejLWMWzJIHy3nPJeGDuCqf8Jnb+jAQVoPpmWzwQOfa9hWVB8GIQE5gsCv/rfN4T+AYb/V06WseQ==", + "version": "5.17.0", + "resolved": "https://registry.npmjs.org/@typescript-eslint/utils/-/utils-5.17.0.tgz", + "integrity": "sha512-DVvndq1QoxQH+hFv+MUQHrrWZ7gQ5KcJzyjhzcqB1Y2Xes1UQQkTRPUfRpqhS8mhTWsSb2+iyvDW1Lef5DD7vA==", "dev": true, "dependencies": { "@types/json-schema": "^7.0.9", - "@typescript-eslint/scope-manager": "5.16.0", - "@typescript-eslint/types": "5.16.0", - "@typescript-eslint/typescript-estree": "5.16.0", + "@typescript-eslint/scope-manager": "5.17.0", + "@typescript-eslint/types": "5.17.0", + "@typescript-eslint/typescript-estree": "5.17.0", "eslint-scope": "^5.1.1", "eslint-utils": "^3.0.0" }, @@ -1204,12 +1204,12 @@ } }, "node_modules/@typescript-eslint/visitor-keys": { - "version": "5.16.0", - "resolved": "https://registry.npmjs.org/@typescript-eslint/visitor-keys/-/visitor-keys-5.16.0.tgz", - "integrity": "sha512-jqxO8msp5vZDhikTwq9ubyMHqZ67UIvawohr4qF3KhlpL7gzSjOd+8471H3nh5LyABkaI85laEKKU8SnGUK5/g==", + "version": "5.17.0", + "resolved": "https://registry.npmjs.org/@typescript-eslint/visitor-keys/-/visitor-keys-5.17.0.tgz", + "integrity": "sha512-6K/zlc4OfCagUu7Am/BD5k8PSWQOgh34Nrv9Rxe2tBzlJ7uOeJ/h7ugCGDCeEZHT6k2CJBhbk9IsbkPI0uvUkA==", "dev": true, "dependencies": { - "@typescript-eslint/types": "5.16.0", + "@typescript-eslint/types": "5.17.0", "eslint-visitor-keys": "^3.0.0" }, "engines": { @@ -1695,9 +1695,9 @@ } }, "node_modules/caniuse-lite": { - "version": "1.0.30001320", - "resolved": "https://registry.npmjs.org/caniuse-lite/-/caniuse-lite-1.0.30001320.tgz", - "integrity": "sha512-MWPzG54AGdo3nWx7zHZTefseM5Y1ccM7hlQKHRqJkPozUaw3hNbBTMmLn16GG2FUzjR13Cr3NPfhIieX5PzXDA==", + "version": "1.0.30001322", + "resolved": "https://registry.npmjs.org/caniuse-lite/-/caniuse-lite-1.0.30001322.tgz", + "integrity": "sha512-neRmrmIrCGuMnxGSoh+x7zYtQFFgnSY2jaomjU56sCkTA6JINqQrxutF459JpWcWRajvoyn95sOXq4Pqrnyjew==", "dev": true, "funding": [ { @@ -2155,9 +2155,9 @@ "dev": true }, "node_modules/electron-to-chromium": { - "version": "1.4.96", - "resolved": "https://registry.npmjs.org/electron-to-chromium/-/electron-to-chromium-1.4.96.tgz", - "integrity": "sha512-DPNjvNGPabv6FcyjzLAN4C0psN/GgD9rSGvMTuv81SeXG/EX3mCz0wiw9N1tUEnfQXYCJi3H8M0oFPRziZh7rw==", + "version": "1.4.101", + "resolved": "https://registry.npmjs.org/electron-to-chromium/-/electron-to-chromium-1.4.101.tgz", + "integrity": "sha512-XJH+XmJjACx1S7ASl/b//KePcda5ocPnFH2jErztXcIS8LpP0SE6rX8ZxiY5/RaDPnaF1rj0fPaHfppzb0e2Aw==", "dev": true }, "node_modules/email-addresses": { @@ -7533,14 +7533,14 @@ "dev": true }, "@typescript-eslint/eslint-plugin": { - "version": "5.16.0", - "resolved": "https://registry.npmjs.org/@typescript-eslint/eslint-plugin/-/eslint-plugin-5.16.0.tgz", - "integrity": "sha512-SJoba1edXvQRMmNI505Uo4XmGbxCK9ARQpkvOd00anxzri9RNQk0DDCxD+LIl+jYhkzOJiOMMKYEHnHEODjdCw==", + "version": "5.17.0", + "resolved": "https://registry.npmjs.org/@typescript-eslint/eslint-plugin/-/eslint-plugin-5.17.0.tgz", + "integrity": "sha512-qVstvQilEd89HJk3qcbKt/zZrfBZ+9h2ynpAGlWjWiizA7m/MtLT9RoX6gjtpE500vfIg8jogAkDzdCxbsFASQ==", "dev": true, "requires": { - "@typescript-eslint/scope-manager": "5.16.0", - "@typescript-eslint/type-utils": "5.16.0", - "@typescript-eslint/utils": "5.16.0", + "@typescript-eslint/scope-manager": "5.17.0", + "@typescript-eslint/type-utils": "5.17.0", + "@typescript-eslint/utils": "5.17.0", "debug": "^4.3.2", "functional-red-black-tree": "^1.0.1", "ignore": "^5.1.8", @@ -7561,52 +7561,52 @@ } }, "@typescript-eslint/parser": { - "version": "5.16.0", - "resolved": "https://registry.npmjs.org/@typescript-eslint/parser/-/parser-5.16.0.tgz", - "integrity": "sha512-fkDq86F0zl8FicnJtdXakFs4lnuebH6ZADDw6CYQv0UZeIjHvmEw87m9/29nk2Dv5Lmdp0zQ3zDQhiMWQf/GbA==", + "version": "5.17.0", + "resolved": "https://registry.npmjs.org/@typescript-eslint/parser/-/parser-5.17.0.tgz", + "integrity": "sha512-aRzW9Jg5Rlj2t2/crzhA2f23SIYFlF9mchGudyP0uiD6SenIxzKoLjwzHbafgHn39dNV/TV7xwQkLfFTZlJ4ig==", "dev": true, "requires": { - "@typescript-eslint/scope-manager": "5.16.0", - "@typescript-eslint/types": "5.16.0", - "@typescript-eslint/typescript-estree": "5.16.0", + "@typescript-eslint/scope-manager": "5.17.0", + "@typescript-eslint/types": "5.17.0", + "@typescript-eslint/typescript-estree": "5.17.0", "debug": "^4.3.2" } }, "@typescript-eslint/scope-manager": { - "version": "5.16.0", - "resolved": "https://registry.npmjs.org/@typescript-eslint/scope-manager/-/scope-manager-5.16.0.tgz", - "integrity": "sha512-P+Yab2Hovg8NekLIR/mOElCDPyGgFZKhGoZA901Yax6WR6HVeGLbsqJkZ+Cvk5nts/dAlFKm8PfL43UZnWdpIQ==", + "version": "5.17.0", + "resolved": "https://registry.npmjs.org/@typescript-eslint/scope-manager/-/scope-manager-5.17.0.tgz", + "integrity": "sha512-062iCYQF/doQ9T2WWfJohQKKN1zmmXVfAcS3xaiialiw8ZUGy05Em6QVNYJGO34/sU1a7a+90U3dUNfqUDHr3w==", "dev": true, "requires": { - "@typescript-eslint/types": "5.16.0", - "@typescript-eslint/visitor-keys": "5.16.0" + "@typescript-eslint/types": "5.17.0", + "@typescript-eslint/visitor-keys": "5.17.0" } }, "@typescript-eslint/type-utils": { - "version": "5.16.0", - "resolved": "https://registry.npmjs.org/@typescript-eslint/type-utils/-/type-utils-5.16.0.tgz", - "integrity": "sha512-SKygICv54CCRl1Vq5ewwQUJV/8padIWvPgCxlWPGO/OgQLCijY9G7lDu6H+mqfQtbzDNlVjzVWQmeqbLMBLEwQ==", + "version": "5.17.0", + "resolved": "https://registry.npmjs.org/@typescript-eslint/type-utils/-/type-utils-5.17.0.tgz", + "integrity": "sha512-3hU0RynUIlEuqMJA7dragb0/75gZmwNwFf/QJokWzPehTZousP/MNifVSgjxNcDCkM5HI2K22TjQWUmmHUINSg==", "dev": true, "requires": { - "@typescript-eslint/utils": "5.16.0", + "@typescript-eslint/utils": "5.17.0", "debug": "^4.3.2", "tsutils": "^3.21.0" } }, "@typescript-eslint/types": { - "version": "5.16.0", - "resolved": "https://registry.npmjs.org/@typescript-eslint/types/-/types-5.16.0.tgz", - "integrity": "sha512-oUorOwLj/3/3p/HFwrp6m/J2VfbLC8gjW5X3awpQJ/bSG+YRGFS4dpsvtQ8T2VNveV+LflQHjlLvB6v0R87z4g==", + "version": "5.17.0", + "resolved": "https://registry.npmjs.org/@typescript-eslint/types/-/types-5.17.0.tgz", + "integrity": "sha512-AgQ4rWzmCxOZLioFEjlzOI3Ch8giDWx8aUDxyNw9iOeCvD3GEYAB7dxWGQy4T/rPVe8iPmu73jPHuaSqcjKvxw==", "dev": true }, "@typescript-eslint/typescript-estree": { - "version": "5.16.0", - "resolved": "https://registry.npmjs.org/@typescript-eslint/typescript-estree/-/typescript-estree-5.16.0.tgz", - "integrity": "sha512-SE4VfbLWUZl9MR+ngLSARptUv2E8brY0luCdgmUevU6arZRY/KxYoLI/3V/yxaURR8tLRN7bmZtJdgmzLHI6pQ==", + "version": "5.17.0", + "resolved": "https://registry.npmjs.org/@typescript-eslint/typescript-estree/-/typescript-estree-5.17.0.tgz", + "integrity": "sha512-X1gtjEcmM7Je+qJRhq7ZAAaNXYhTgqMkR10euC4Si6PIjb+kwEQHSxGazXUQXFyqfEXdkGf6JijUu5R0uceQzg==", "dev": true, "requires": { - "@typescript-eslint/types": "5.16.0", - "@typescript-eslint/visitor-keys": "5.16.0", + "@typescript-eslint/types": "5.17.0", + "@typescript-eslint/visitor-keys": "5.17.0", "debug": "^4.3.2", "globby": "^11.0.4", "is-glob": "^4.0.3", @@ -7646,26 +7646,26 @@ } }, "@typescript-eslint/utils": { - "version": "5.16.0", - "resolved": "https://registry.npmjs.org/@typescript-eslint/utils/-/utils-5.16.0.tgz", - "integrity": "sha512-iYej2ER6AwmejLWMWzJIHy3nPJeGDuCqf8Jnb+jAQVoPpmWzwQOfa9hWVB8GIQE5gsCv/rfN4T+AYb/V06WseQ==", + "version": "5.17.0", + "resolved": "https://registry.npmjs.org/@typescript-eslint/utils/-/utils-5.17.0.tgz", + "integrity": "sha512-DVvndq1QoxQH+hFv+MUQHrrWZ7gQ5KcJzyjhzcqB1Y2Xes1UQQkTRPUfRpqhS8mhTWsSb2+iyvDW1Lef5DD7vA==", "dev": true, "requires": { "@types/json-schema": "^7.0.9", - "@typescript-eslint/scope-manager": "5.16.0", - "@typescript-eslint/types": "5.16.0", - "@typescript-eslint/typescript-estree": "5.16.0", + "@typescript-eslint/scope-manager": "5.17.0", + "@typescript-eslint/types": "5.17.0", + "@typescript-eslint/typescript-estree": "5.17.0", "eslint-scope": "^5.1.1", "eslint-utils": "^3.0.0" } }, "@typescript-eslint/visitor-keys": { - "version": "5.16.0", - "resolved": "https://registry.npmjs.org/@typescript-eslint/visitor-keys/-/visitor-keys-5.16.0.tgz", - "integrity": "sha512-jqxO8msp5vZDhikTwq9ubyMHqZ67UIvawohr4qF3KhlpL7gzSjOd+8471H3nh5LyABkaI85laEKKU8SnGUK5/g==", + "version": "5.17.0", + "resolved": "https://registry.npmjs.org/@typescript-eslint/visitor-keys/-/visitor-keys-5.17.0.tgz", + "integrity": "sha512-6K/zlc4OfCagUu7Am/BD5k8PSWQOgh34Nrv9Rxe2tBzlJ7uOeJ/h7ugCGDCeEZHT6k2CJBhbk9IsbkPI0uvUkA==", "dev": true, "requires": { - "@typescript-eslint/types": "5.16.0", + "@typescript-eslint/types": "5.17.0", "eslint-visitor-keys": "^3.0.0" } }, @@ -8011,9 +8011,9 @@ "dev": true }, "caniuse-lite": { - "version": "1.0.30001320", - "resolved": "https://registry.npmjs.org/caniuse-lite/-/caniuse-lite-1.0.30001320.tgz", - "integrity": "sha512-MWPzG54AGdo3nWx7zHZTefseM5Y1ccM7hlQKHRqJkPozUaw3hNbBTMmLn16GG2FUzjR13Cr3NPfhIieX5PzXDA==", + "version": "1.0.30001322", + "resolved": "https://registry.npmjs.org/caniuse-lite/-/caniuse-lite-1.0.30001322.tgz", + "integrity": "sha512-neRmrmIrCGuMnxGSoh+x7zYtQFFgnSY2jaomjU56sCkTA6JINqQrxutF459JpWcWRajvoyn95sOXq4Pqrnyjew==", "dev": true }, "chalk": { @@ -8353,9 +8353,9 @@ "dev": true }, "electron-to-chromium": { - "version": "1.4.96", - "resolved": "https://registry.npmjs.org/electron-to-chromium/-/electron-to-chromium-1.4.96.tgz", - "integrity": "sha512-DPNjvNGPabv6FcyjzLAN4C0psN/GgD9rSGvMTuv81SeXG/EX3mCz0wiw9N1tUEnfQXYCJi3H8M0oFPRziZh7rw==", + "version": "1.4.101", + "resolved": "https://registry.npmjs.org/electron-to-chromium/-/electron-to-chromium-1.4.101.tgz", + "integrity": "sha512-XJH+XmJjACx1S7ASl/b//KePcda5ocPnFH2jErztXcIS8LpP0SE6rX8ZxiY5/RaDPnaF1rj0fPaHfppzb0e2Aw==", "dev": true }, "email-addresses": { diff --git a/packages/search/lib/commands/AGGREGATE.spec.ts b/packages/search/lib/commands/AGGREGATE.spec.ts index 24684d447dc..a2330076438 100644 --- a/packages/search/lib/commands/AGGREGATE.spec.ts +++ b/packages/search/lib/commands/AGGREGATE.spec.ts @@ -434,6 +434,26 @@ describe('AGGREGATE', () => { ); }); }); + + it('with PARAMS', () => { + assert.deepEqual( + transformArguments('index', '*', { + PARAMS: { + param: 'value' + } + }), + ['FT.AGGREGATE', 'index', '*', 'PARAMS', '2', 'param', 'value'] + ); + }); + + it('with DIALECT', () => { + assert.deepEqual( + transformArguments('index', '*', { + DIALECT: 1 + }), + ['FT.AGGREGATE', 'index', '*', 'DIALECT', '1'] + ); + }); }); testUtils.testWithClient('client.ft.aggregate', async client => { diff --git a/packages/search/lib/commands/AGGREGATE.ts b/packages/search/lib/commands/AGGREGATE.ts index 84a8f9a55a6..cc7e9cba40b 100644 --- a/packages/search/lib/commands/AGGREGATE.ts +++ b/packages/search/lib/commands/AGGREGATE.ts @@ -1,6 +1,6 @@ import { RedisCommandArgument, RedisCommandArguments } from '@node-redis/client/dist/lib/commands'; import { pushVerdictArgument, transformTuplesReply } from '@node-redis/client/dist/lib/commands/generic-transformers'; -import { PropertyName, pushArgumentsWithLength, pushSortByArguments, SortByProperty } from '.'; +import { Params, PropertyName, pushArgumentsWithLength, pushParamsArgs, pushSortByArguments, SortByProperty } from '.'; export enum AggregateSteps { GROUPBY = 'GROUPBY', @@ -122,6 +122,8 @@ export interface AggregateOptions { VERBATIM?: true; LOAD?: LoadField | Array; STEPS?: Array; + PARAMS?: Params; + DIALECT?: number; } export function transformArguments( @@ -129,17 +131,16 @@ export function transformArguments( query: string, options?: AggregateOptions ): RedisCommandArguments { - - const args = ['FT.AGGREGATE', index, query]; - pushAggregatehOptions(args, options); - return args; + return pushAggregatehOptions( + ['FT.AGGREGATE', index, query], + options + ); } export function pushAggregatehOptions( args: RedisCommandArguments, options?: AggregateOptions ): RedisCommandArguments { - if (options?.VERBATIM) { args.push('VERBATIM'); } @@ -202,6 +203,12 @@ export function pushAggregatehOptions( } } + pushParamsArgs(args, options?.PARAMS); + + if (options?.DIALECT) { + args.push('DIALECT', options.DIALECT.toString()); + } + return args; } @@ -257,7 +264,6 @@ function pushGroupByReducer(args: RedisCommandArguments, reducer: GroupByReducer } } }); - break; } diff --git a/packages/search/lib/commands/CREATE.spec.ts b/packages/search/lib/commands/CREATE.spec.ts index 35aec47a676..765e3dbf7d6 100644 --- a/packages/search/lib/commands/CREATE.spec.ts +++ b/packages/search/lib/commands/CREATE.spec.ts @@ -1,7 +1,7 @@ import { strict as assert } from 'assert'; import testUtils, { GLOBAL } from '../test-utils'; import { transformArguments } from './CREATE'; -import { SchemaFieldTypes, SchemaTextFieldPhonetics, RedisSearchLanguages } from '.'; +import { SchemaFieldTypes, SchemaTextFieldPhonetics, RedisSearchLanguages, VectorAlgorithms } from '.'; describe('CREATE', () => { describe('transformArguments', () => { @@ -126,6 +126,52 @@ describe('CREATE', () => { }); }); + describe('VECTOR', () => { + it('Flat algorithm', () => { + assert.deepEqual( + transformArguments('index', { + field: { + type: SchemaFieldTypes.VECTOR, + ALGORITHM: VectorAlgorithms.FLAT, + TYPE: 'FLOAT32', + DIM: 2, + DISTANCE_METRIC: 'L2', + INITIAL_CAP: 1000000, + BLOCK_SIZE: 1000 + } + }), + [ + 'FT.CREATE', 'index', 'SCHEMA', 'field', 'VECTOR', 'FLAT', '10', 'TYPE', + 'FLOAT32', 'DIM', '2', 'DISTANCE_METRIC', 'L2', 'INITIAL_CAP', '1000000', + 'BLOCK_SIZE', '1000' + ] + ); + }); + + it('HNSW algorithm', () => { + assert.deepEqual( + transformArguments('index', { + field: { + type: SchemaFieldTypes.VECTOR, + ALGORITHM: VectorAlgorithms.HNSW, + TYPE: 'FLOAT32', + DIM: 2, + DISTANCE_METRIC: 'L2', + INITIAL_CAP: 1000000, + M: 40, + EF_CONSTRUCTION: 250, + EF_RUNTIME: 20 + } + }), + [ + 'FT.CREATE', 'index', 'SCHEMA', 'field', 'VECTOR', 'HNSW', '14', 'TYPE', + 'FLOAT32', 'DIM', '2', 'DISTANCE_METRIC', 'L2', 'INITIAL_CAP', '1000000', + 'M', '40', 'EF_CONSTRUCTION', '250', 'EF_RUNTIME', '20' + ] + ); + }); + }); + describe('with generic options', () => { it('with AS', () => { assert.deepEqual( diff --git a/packages/search/lib/commands/EXPLAIN.spec.ts b/packages/search/lib/commands/EXPLAIN.spec.ts index dd55e038710..d24f5fe4ac5 100644 --- a/packages/search/lib/commands/EXPLAIN.spec.ts +++ b/packages/search/lib/commands/EXPLAIN.spec.ts @@ -2,10 +2,32 @@ import { strict as assert } from 'assert'; import { transformArguments } from './EXPLAIN'; describe('EXPLAIN', () => { - it('transformArguments', () => { - assert.deepEqual( - transformArguments('index', '*'), - ['FT.EXPLAIN', 'index', '*'] - ); + describe('transformArguments', () => { + it('simple', () => { + assert.deepEqual( + transformArguments('index', '*'), + ['FT.EXPLAIN', 'index', '*'] + ); + }); + + it('with PARAMS', () => { + assert.deepEqual( + transformArguments('index', '*', { + PARAMS: { + param: 'value' + } + }), + ['FT.EXPLAIN', 'index', '*', 'PARAMS', '2', 'param', 'value'] + ); + }); + + it('with DIALECT', () => { + assert.deepEqual( + transformArguments('index', '*', { + DIALECT: 1 + }), + ['FT.EXPLAIN', 'index', '*', 'DIALECT', '1'] + ); + }); }); }); diff --git a/packages/search/lib/commands/EXPLAIN.ts b/packages/search/lib/commands/EXPLAIN.ts index c41cd9a4aac..ab3935ff979 100644 --- a/packages/search/lib/commands/EXPLAIN.ts +++ b/packages/search/lib/commands/EXPLAIN.ts @@ -1,7 +1,26 @@ +import { Params, pushParamsArgs } from "."; + export const IS_READ_ONLY = true; -export function transformArguments(index: string, query: string): Array { - return ['FT.EXPLAIN', index, query]; +interface ExplainOptions { + PARAMS?: Params; + DIALECT?: number; +} + +export function transformArguments( + index: string, + query: string, + options?: ExplainOptions +): Array { + const args = ['FT.EXPLAIN', index, query]; + + pushParamsArgs(args, options?.PARAMS); + + if (options?.DIALECT) { + args.push('DIALECT', options.DIALECT.toString()); + } + + return args; } export declare function transformReply(): string; diff --git a/packages/search/lib/commands/INFO.spec.ts b/packages/search/lib/commands/INFO.spec.ts index 2ee3048c01d..e026b44e264 100644 --- a/packages/search/lib/commands/INFO.spec.ts +++ b/packages/search/lib/commands/INFO.spec.ts @@ -15,32 +15,56 @@ describe('INFO', () => { await client.ft.create('index', { field: SchemaFieldTypes.TEXT }); - assert.deepEqual( await client.ft.info('index'), { indexName: 'index', indexOptions: [], - indexDefinition: { - defaultScore: '1', - keyType: 'HASH', - prefixes: [''] - }, - attributes: [[ - 'identifier', - 'field', - 'attribute', - 'field', - 'type', - 'TEXT', - 'WEIGHT', - '1' - ]], + indexDefinition: Object.create(null, { + default_score: { + value: '1', + configurable: true, + enumerable: true + }, + key_type: { + value: 'HASH', + configurable: true, + enumerable: true + }, + prefixes: { + value: [''], + configurable: true, + enumerable: true + } + }), + attributes: [Object.create(null, { + identifier: { + value: 'field', + configurable: true, + enumerable: true + }, + attribute: { + value: 'field', + configurable: true, + enumerable: true + }, + type: { + value: 'TEXT', + configurable: true, + enumerable: true + }, + WEIGHT: { + value: '1', + configurable: true, + enumerable: true + } + })], numDocs: '0', maxDocId: '0', numTerms: '0', numRecords: '0', invertedSzMb: '0', + vectorIndexSzMb: '0', totalInvertedIndexBlocks: '0', offsetVectorsSzMb: '0', docTableSizeMb: '0', @@ -67,7 +91,8 @@ describe('INFO', () => { globalTotal: 0, indexCapacity: 128, idnexTotal: 0 - } + }, + stopWords: undefined } ); }, GLOBAL.SERVERS.OPEN); diff --git a/packages/search/lib/commands/INFO.ts b/packages/search/lib/commands/INFO.ts index 42451114c89..b4072aa759c 100644 --- a/packages/search/lib/commands/INFO.ts +++ b/packages/search/lib/commands/INFO.ts @@ -1,121 +1,118 @@ +import { RedisCommandArgument } from '@node-redis/client/dist/lib/commands'; +import { transformTuplesReply } from '@node-redis/client/dist/lib/commands/generic-transformers'; + export function transformArguments(index: string): Array { return ['FT.INFO', index]; } type InfoRawReply = [ - _: string, - indexName: string, - _: string, - indexOptions: Array, - _: string, - indexDefinition: [ - _: string, - keyType: string, - _: string, - prefixes: Array, - _: string, - defaultScore: string + 'index_name', + RedisCommandArgument, + 'index_options', + Array, + 'index_definition', + Array, + 'attributes', + Array>, + 'num_docs', + RedisCommandArgument, + 'max_doc_id', + RedisCommandArgument, + 'num_terms', + RedisCommandArgument, + 'num_records', + RedisCommandArgument, + 'inverted_sz_mb', + RedisCommandArgument, + 'vector_index_sz_mb', + RedisCommandArgument, + 'total_inverted_index_blocks', + RedisCommandArgument, + 'offset_vectors_sz_mb', + RedisCommandArgument, + 'doc_table_size_mb', + RedisCommandArgument, + 'sortable_values_size_mb', + RedisCommandArgument, + 'key_table_size_mb', + RedisCommandArgument, + 'records_per_doc_avg', + RedisCommandArgument, + 'bytes_per_record_avg', + RedisCommandArgument, + 'offsets_per_term_avg', + RedisCommandArgument, + 'offset_bits_per_record_avg', + RedisCommandArgument, + 'hash_indexing_failures', + RedisCommandArgument, + 'indexing', + RedisCommandArgument, + 'percent_indexed', + RedisCommandArgument, + 'gc_stats', + [ + 'bytes_collected', + RedisCommandArgument, + 'total_ms_run', + RedisCommandArgument, + 'total_cycles', + RedisCommandArgument, + 'average_cycle_time_ms', + RedisCommandArgument, + 'last_run_time_ms', + RedisCommandArgument, + 'gc_numeric_trees_missed', + RedisCommandArgument, + 'gc_blocks_denied', + RedisCommandArgument ], - _: string, - attributes: Array>, - _: string, - numDocs: string, - _: string, - maxDocId: string, - _: string, - numTerms: string, - _: string, - numRecords: string, - _: string, - invertedSzMb: string, - _: string, - totalInvertedIndexBlocks: string, - _: string, - offsetVectorsSzMb: string, - _: string, - docTableSizeMb: string, - _: string, - sortableValuesSizeMb: string, - _: string, - keyTableSizeMb: string, - _: string, - recordsPerDocAvg: string, - _: string, - bytesPerRecordAvg: string, - _: string, - offsetsPerTermAvg: string, - _: string, - offsetBitsPerRecordAvg: string, - _: string, - hashIndexingFailures: string, - _: string, - indexing: string, - _: string, - percentIndexed: string, - _: string, - gcStats: [ - _: string, - bytesCollected: string, - _: string, - totalMsRun: string, - _: string, - totalCycles: string, - _: string, - averageCycleTimeMs: string, - _: string, - lastRunTimeMs: string, - _: string, - gcNumericTreesMissed: string, - _: string, - gcBlocksDenied: string + 'cursor_stats', + [ + 'global_idle', + number, + 'global_total', + number, + 'index_capacity', + number, + 'index_total', + number ], - _: string, - cursorStats: [ - _: string, - globalIdle: number, - _: string, - globalTotal: number, - _: string, - indexCapacity: number, - _: string, - idnexTotal: number - ] + 'stopwords_list'?, + Array? ]; interface InfoReply { - indexName: string; - indexOptions: Array; - indexDefinition: { - keyType: string; - prefixes: Array; - defaultScore: string; - }; - attributes: Array>; - numDocs: string; - maxDocId: string; - numTerms: string; - numRecords: string; - invertedSzMb: string; - totalInvertedIndexBlocks: string; - offsetVectorsSzMb: string; - docTableSizeMb: string; - sortableValuesSizeMb: string; - keyTableSizeMb: string; - recordsPerDocAvg: string; - bytesPerRecordAvg: string; - offsetsPerTermAvg: string; - offsetBitsPerRecordAvg: string; - hashIndexingFailures: string; - indexing: string; - percentIndexed: string; + indexName: RedisCommandArgument; + indexOptions: Array; + indexDefinition: Record; + attributes: Array>; + numDocs: RedisCommandArgument; + maxDocId: RedisCommandArgument; + numTerms: RedisCommandArgument; + numRecords: RedisCommandArgument; + invertedSzMb: RedisCommandArgument; + vectorIndexSzMb: RedisCommandArgument; + totalInvertedIndexBlocks: RedisCommandArgument; + offsetVectorsSzMb: RedisCommandArgument; + docTableSizeMb: RedisCommandArgument; + sortableValuesSizeMb: RedisCommandArgument; + keyTableSizeMb: RedisCommandArgument; + recordsPerDocAvg: RedisCommandArgument; + bytesPerRecordAvg: RedisCommandArgument; + offsetsPerTermAvg: RedisCommandArgument; + offsetBitsPerRecordAvg: RedisCommandArgument; + hashIndexingFailures: RedisCommandArgument; + indexing: RedisCommandArgument; + percentIndexed: RedisCommandArgument; gcStats: { - bytesCollected: string; - totalMsRun: string; - totalCycles: string; - averageCycleTimeMs: string; - lastRunTimeMs: string; - gcNumericTreesMissed: string; - gcBlocksDenied: string; + bytesCollected: RedisCommandArgument; + totalMsRun: RedisCommandArgument; + totalCycles: RedisCommandArgument; + averageCycleTimeMs: RedisCommandArgument; + lastRunTimeMs: RedisCommandArgument; + gcNumericTreesMissed: RedisCommandArgument; + gcBlocksDenied: RedisCommandArgument; }; cursorStats: { globalIdle: number; @@ -123,49 +120,49 @@ interface InfoReply { indexCapacity: number; idnexTotal: number; }; + stopWords: Array | undefined; } export function transformReply(rawReply: InfoRawReply): InfoReply { + console.log(rawReply); return { indexName: rawReply[1], indexOptions: rawReply[3], - indexDefinition: { - keyType: rawReply[5][1], - prefixes: rawReply[5][3], - defaultScore: rawReply[5][5] - }, - attributes: rawReply[7], + indexDefinition: transformTuplesReply(rawReply[5]), + attributes: rawReply[7].map(attribute => transformTuplesReply(attribute)), numDocs: rawReply[9], maxDocId: rawReply[11], numTerms: rawReply[13], numRecords: rawReply[15], invertedSzMb: rawReply[17], - totalInvertedIndexBlocks: rawReply[19], - offsetVectorsSzMb: rawReply[21], - docTableSizeMb: rawReply[23], - sortableValuesSizeMb: rawReply[25], - keyTableSizeMb: rawReply[27], - recordsPerDocAvg: rawReply[29], - bytesPerRecordAvg: rawReply[31], - offsetsPerTermAvg: rawReply[33], - offsetBitsPerRecordAvg: rawReply[35], - hashIndexingFailures: rawReply[37], - indexing: rawReply[39], - percentIndexed: rawReply[41], + vectorIndexSzMb: rawReply[19], + totalInvertedIndexBlocks: rawReply[21], + offsetVectorsSzMb: rawReply[23], + docTableSizeMb: rawReply[25], + sortableValuesSizeMb: rawReply[27], + keyTableSizeMb: rawReply[29], + recordsPerDocAvg: rawReply[31], + bytesPerRecordAvg: rawReply[33], + offsetsPerTermAvg: rawReply[35], + offsetBitsPerRecordAvg: rawReply[37], + hashIndexingFailures: rawReply[39], + indexing: rawReply[41], + percentIndexed: rawReply[43], gcStats: { - bytesCollected: rawReply[43][1], - totalMsRun: rawReply[43][3], - totalCycles: rawReply[43][5], - averageCycleTimeMs: rawReply[43][7], - lastRunTimeMs: rawReply[43][9], - gcNumericTreesMissed: rawReply[43][11], - gcBlocksDenied: rawReply[43][13] + bytesCollected: rawReply[45][1], + totalMsRun: rawReply[45][3], + totalCycles: rawReply[45][5], + averageCycleTimeMs: rawReply[45][7], + lastRunTimeMs: rawReply[45][9], + gcNumericTreesMissed: rawReply[45][11], + gcBlocksDenied: rawReply[45][13] }, cursorStats: { - globalIdle: rawReply[45][1], - globalTotal: rawReply[45][3], - indexCapacity: rawReply[45][5], - idnexTotal: rawReply[45][7] - } + globalIdle: rawReply[47][1], + globalTotal: rawReply[47][3], + indexCapacity: rawReply[47][5], + idnexTotal: rawReply[47][7] + }, + stopWords: rawReply[49] }; } diff --git a/packages/search/lib/commands/PROFILE_SEARCH.ts b/packages/search/lib/commands/PROFILE_SEARCH.ts index bdb67ec3874..6e7cd536e72 100644 --- a/packages/search/lib/commands/PROFILE_SEARCH.ts +++ b/packages/search/lib/commands/PROFILE_SEARCH.ts @@ -1,5 +1,6 @@ import { SearchOptions, SearchRawReply, transformReply as transformSearchReply } from './SEARCH'; import { pushSearchOptions, ProfileOptions, ProfileRawReply, ProfileReply, transformProfile } from '.'; +import { RedisCommandArguments } from '@node-redis/client/dist/lib/commands'; export const IS_READ_ONLY = true; @@ -7,7 +8,7 @@ export function transformArguments( index: string, query: string, options?: ProfileOptions & SearchOptions -): Array { +): RedisCommandArguments { const args = ['FT.PROFILE', index, 'SEARCH']; if (options?.LIMITED) { @@ -15,8 +16,7 @@ export function transformArguments( } args.push('QUERY', query); - pushSearchOptions(args, options) - return args; + return pushSearchOptions(args, options); } type ProfileSearchRawReply = ProfileRawReply; diff --git a/packages/search/lib/commands/SEARCH.spec.ts b/packages/search/lib/commands/SEARCH.spec.ts index c2f2c295c8f..aecf6c8b1a6 100644 --- a/packages/search/lib/commands/SEARCH.spec.ts +++ b/packages/search/lib/commands/SEARCH.spec.ts @@ -213,31 +213,98 @@ describe('SEARCH', () => { ['FT.SEARCH', 'index', 'query', 'LIMIT', '0', '1'] ); }); + + it('with PARAMS', () => { + assert.deepEqual( + transformArguments('index', 'query', { + PARAMS: { + param: 'value' + } + }), + ['FT.SEARCH', 'index', 'query', 'PARAMS', '2', 'param', 'value'] + ); + }); + + it('with DIALECT', () => { + assert.deepEqual( + transformArguments('index', 'query', { + DIALECT: 1 + }), + ['FT.SEARCH', 'index', 'query', 'DIALECT', '1'] + ); + }); }); - testUtils.testWithClient('client.ft.search', async client => { - await Promise.all([ - client.ft.create('index', { - field: SchemaFieldTypes.NUMERIC - }), - client.hSet('1', 'field', '1') - ]); - - assert.deepEqual( - await client.ft.search('index', '*'), - { - total: 1, - documents: [{ - id: '1', - value: Object.create(null, { - field: { - value: '1', - configurable: true, - enumerable: true - } - }) - }] - } - ); - }, GLOBAL.SERVERS.OPEN); + describe('client.ft.search', () => { + testUtils.testWithClient('DIALECT 1', async client => { + await Promise.all([ + client.ft.create('index', { + field: SchemaFieldTypes.NUMERIC + }), + client.hSet('1', 'field', '1') + ]); + + assert.deepEqual( + await client.ft.search('index', '*', { + DIALECT: 1 + }), + { + total: 1, + documents: [{ + id: '1', + value: Object.create(null, { + field: { + value: '1', + configurable: true, + enumerable: true + } + }) + }] + } + ); + }, GLOBAL.SERVERS.OPEN); + + testUtils.testWithClient('DIALECT 2', async client => { + await Promise.all([ + client.ft.create('index', { + field: SchemaFieldTypes.NUMERIC + }), + client.hSet('1', 'field', '1'), + client.hSet('2', 'field', '2'), + client.hSet('3', 'field', '3') + ]); + + assert.deepEqual( + await client.ft.search('index', '@field:[$min $max]', { + PARAMS: { + min: 1, + max: 2 + }, + DIALECT: 2 + }), + { + total: 2, + documents: [{ + id: '1', + value: Object.create(null, { + field: { + value: '1', + configurable: true, + enumerable: true + } + }) + }, { + id: '2', + value: Object.create(null, { + field: { + value: '2', + configurable: true, + enumerable: true + } + }) + }] + } + ); + }, GLOBAL.SERVERS.OPEN); + }); }); diff --git a/packages/search/lib/commands/SEARCH.ts b/packages/search/lib/commands/SEARCH.ts index efbe24e1512..3a9899975c3 100644 --- a/packages/search/lib/commands/SEARCH.ts +++ b/packages/search/lib/commands/SEARCH.ts @@ -1,6 +1,6 @@ import { RedisCommandArguments } from '@node-redis/client/dist/lib/commands'; import { transformTuplesReply } from '@node-redis/client/dist/lib/commands/generic-transformers'; -import { pushSearchOptions, RedisSearchLanguages, PropertyName, SortByProperty, SearchReply } from '.'; +import { pushSearchOptions, RedisSearchLanguages, Params, PropertyName, SortByProperty, SearchReply } from '.'; export const FIRST_KEY_INDEX = 1; @@ -54,6 +54,8 @@ export interface SearchOptions { from: number | string; size: number | string; }; + PARAMS?: Params; + DIALECT?: number; } export function transformArguments( @@ -61,9 +63,10 @@ export function transformArguments( query: string, options?: SearchOptions ): RedisCommandArguments { - const args: RedisCommandArguments = ['FT.SEARCH', index, query]; - pushSearchOptions(args, options); - return args; + return pushSearchOptions( + ['FT.SEARCH', index, query], + options + ); } export type SearchRawReply = Array; diff --git a/packages/search/lib/commands/SPELLCHECK.spec.ts b/packages/search/lib/commands/SPELLCHECK.spec.ts index fe74f5910f5..acabbe8a87c 100644 --- a/packages/search/lib/commands/SPELLCHECK.spec.ts +++ b/packages/search/lib/commands/SPELLCHECK.spec.ts @@ -47,6 +47,15 @@ describe('SPELLCHECK', () => { ); }); }); + + it('with DIALECT', () => { + assert.deepEqual( + transformArguments('index', 'query', { + DIALECT: 1 + }), + ['FT.SPELLCHECK', 'index', 'query', 'DIALECT', '1'] + ); + }); }); testUtils.testWithClient('client.ft.spellCheck', async client => { diff --git a/packages/search/lib/commands/SPELLCHECK.ts b/packages/search/lib/commands/SPELLCHECK.ts index ae4cb3cdce1..c9317a8b4fe 100644 --- a/packages/search/lib/commands/SPELLCHECK.ts +++ b/packages/search/lib/commands/SPELLCHECK.ts @@ -6,6 +6,7 @@ interface SpellCheckTerms { interface SpellCheckOptions { DISTANCE?: number; TERMS?: SpellCheckTerms | Array; + DIALECT?: number; } export function transformArguments(index: string, query: string, options?: SpellCheckOptions): Array { @@ -25,6 +26,10 @@ export function transformArguments(index: string, query: string, options?: Spell } } + if (options?.DIALECT) { + args.push('DIALECT', options.DIALECT.toString()); + } + return args; } diff --git a/packages/search/lib/commands/index.ts b/packages/search/lib/commands/index.ts index 0379f9252d1..51ab50175e9 100644 --- a/packages/search/lib/commands/index.ts +++ b/packages/search/lib/commands/index.ts @@ -28,7 +28,7 @@ import * as SUGLEN from './SUGLEN'; import * as SYNDUMP from './SYNDUMP'; import * as SYNUPDATE from './SYNUPDATE'; import * as TAGVALS from './TAGVALS'; -import { RedisCommandArguments } from '@node-redis/client/dist/lib/commands'; +import { RedisCommandArgument, RedisCommandArguments } from '@node-redis/client/dist/lib/commands'; import { pushOptionalVerdictArgument, pushVerdictArgument } from '@node-redis/client/dist/lib/commands/generic-transformers'; import { SearchOptions } from './SEARCH'; @@ -172,16 +172,29 @@ export enum SchemaFieldTypes { TEXT = 'TEXT', NUMERIC = 'NUMERIC', GEO = 'GEO', - TAG = 'TAG' + TAG = 'TAG', + VECTOR = 'VECTOR' } -type CreateSchemaField> = T | ({ +type CreateSchemaField< + T extends SchemaFieldTypes, + E = Record +> = T | ({ type: T; AS?: string; - SORTABLE?: true | 'UNF'; - NOINDEX?: true; } & E); +type CreateSchemaCommonField< + T extends SchemaFieldTypes, + E = Record +> = CreateSchemaField< + T, + ({ + SORTABLE?: true | 'UNF'; + NOINDEX?: true; + } & E) +>; + export enum SchemaTextFieldPhonetics { DM_EN = 'dm:en', DM_FR = 'dm:fr', @@ -189,27 +202,55 @@ export enum SchemaTextFieldPhonetics { DM_ES = 'dm:es' } -type CreateSchemaTextField = CreateSchemaField; -type CreateSchemaNumericField = CreateSchemaField; +type CreateSchemaNumericField = CreateSchemaCommonField; -type CreateSchemaGeoField = CreateSchemaField; +type CreateSchemaGeoField = CreateSchemaCommonField; -type CreateSchemaTagField = CreateSchemaField; +export enum VectorAlgorithms { + FLAT = 'FLAT', + HNSW = 'HNSW' +} + +type CreateSchemaVectorField< + T extends VectorAlgorithms, + A extends Record +> = CreateSchemaField; + +type CreateSchemaFlatVectorField = CreateSchemaVectorField; + +type CreateSchemaHNSWVectorField = CreateSchemaVectorField; + export interface RediSearchSchema { [field: string]: CreateSchemaTextField | CreateSchemaNumericField | CreateSchemaGeoField | - CreateSchemaTagField; + CreateSchemaTagField | + CreateSchemaFlatVectorField | + CreateSchemaHNSWVectorField; } export function pushSchema(args: RedisCommandArguments, schema: RediSearchSchema) { @@ -257,6 +298,47 @@ export function pushSchema(args: RedisCommandArguments, schema: RediSearchSchema } break; + + case SchemaFieldTypes.VECTOR: + args.push(fieldOptions.ALGORITHM); + + pushArgumentsWithLength(args, () => { + args.push( + 'TYPE', fieldOptions.TYPE, + 'DIM', fieldOptions.DIM.toString(), + 'DISTANCE_METRIC', fieldOptions.DISTANCE_METRIC + ); + + if (fieldOptions.INITIAL_CAP) { + args.push('INITIAL_CAP', fieldOptions.INITIAL_CAP.toString()); + } + + switch (fieldOptions.ALGORITHM) { + case VectorAlgorithms.FLAT: + if (fieldOptions.BLOCK_SIZE) { + args.push('BLOCK_SIZE', fieldOptions.BLOCK_SIZE.toString()); + } + + break; + + case VectorAlgorithms.HNSW: + if (fieldOptions.M) { + args.push('M', fieldOptions.M.toString()); + } + + if (fieldOptions.EF_CONSTRUCTION) { + args.push('EF_CONSTRUCTION', fieldOptions.EF_CONSTRUCTION.toString()); + } + + if (fieldOptions.EF_RUNTIME) { + args.push('EF_RUNTIME', fieldOptions.EF_RUNTIME.toString()); + } + + break; + } + }); + + continue; // vector fields do not contain SORTABLE and NOINDEX options } if (fieldOptions.SORTABLE) { @@ -273,11 +355,27 @@ export function pushSchema(args: RedisCommandArguments, schema: RediSearchSchema } } +export type Params = Record; + +export function pushParamsArgs( + args: RedisCommandArguments, + params?: Params +): RedisCommandArguments { + if (params) { + const enrties = Object.entries(params); + args.push('PARAMS', (enrties.length * 2).toString()); + for (const [key, value] of enrties) { + args.push(key, value.toString()); + } + } + + return args; +} + export function pushSearchOptions( args: RedisCommandArguments, options?: SearchOptions ): RedisCommandArguments { - if (options?.VERBATIM) { args.push('VERBATIM'); } @@ -381,6 +479,16 @@ export function pushSearchOptions( ); } + if (options?.PARAMS) { + pushParamsArgs(args, options.PARAMS); + } + + if (options?.DIALECT) { + args.push('DIALECT', options.DIALECT.toString()); + } + + console.log('!@#', args); + return args; } diff --git a/packages/search/lib/index.ts b/packages/search/lib/index.ts index 93d1e9088fa..296136021ef 100644 --- a/packages/search/lib/index.ts +++ b/packages/search/lib/index.ts @@ -1,5 +1,5 @@ export { default } from './commands'; -export { RediSearchSchema, SchemaFieldTypes, SchemaTextFieldPhonetics, SearchReply } from './commands'; +export { RediSearchSchema, SchemaFieldTypes, SchemaTextFieldPhonetics, SearchReply, VectorAlgorithms } from './commands'; export { AggregateSteps, AggregateGroupByReducers } from './commands/AGGREGATE'; -export { SearchOptions } from './commands/SEARCH'; \ No newline at end of file +export { SearchOptions } from './commands/SEARCH'; diff --git a/packages/search/lib/test-utils.ts b/packages/search/lib/test-utils.ts index fd11951bb57..32649f729e0 100644 --- a/packages/search/lib/test-utils.ts +++ b/packages/search/lib/test-utils.ts @@ -4,7 +4,7 @@ import RediSearch from '.'; export default new TestUtils({ dockerImageName: 'redislabs/redisearch', dockerImageVersionArgument: 'redisearch-version', - defaultDockerVersion: '2.2.7' + defaultDockerVersion: '2.4.3' }); export const GLOBAL = { From 20b73b0a905b723ff20f271fbe31fca2d8c54f0a Mon Sep 17 00:00:00 2001 From: leibale Date: Thu, 31 Mar 2022 14:25:17 +0300 Subject: [PATCH 1174/1748] Release client@1.0.5 --- packages/client/package.json | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/packages/client/package.json b/packages/client/package.json index 3a4db330f6e..e2e13a896c3 100644 --- a/packages/client/package.json +++ b/packages/client/package.json @@ -1,6 +1,6 @@ { "name": "@node-redis/client", - "version": "1.0.4", + "version": "1.0.5", "license": "MIT", "main": "./dist/index.js", "types": "./dist/index.d.ts", From 57b5a9a7bd261d2e9933965505fa5bf069bf699f Mon Sep 17 00:00:00 2001 From: leibale Date: Thu, 31 Mar 2022 14:27:42 +0300 Subject: [PATCH 1175/1748] Release search@1.0.4 --- packages/search/package.json | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/packages/search/package.json b/packages/search/package.json index 75036c23529..48a0824e5c9 100644 --- a/packages/search/package.json +++ b/packages/search/package.json @@ -1,6 +1,6 @@ { "name": "@node-redis/search", - "version": "1.0.3", + "version": "1.0.4", "license": "MIT", "main": "./dist/index.js", "types": "./dist/index.d.ts", From 990103feabdefacca49bd818a72a8ed544e1175f Mon Sep 17 00:00:00 2001 From: leibale Date: Thu, 31 Mar 2022 14:30:48 +0300 Subject: [PATCH 1176/1748] upgrade deps --- package-lock.json | 188 +++++++++++++++++++++++----------------------- package.json | 4 +- 2 files changed, 96 insertions(+), 96 deletions(-) diff --git a/package-lock.json b/package-lock.json index 641d253ef0c..5d989b68f52 100644 --- a/package-lock.json +++ b/package-lock.json @@ -13,10 +13,10 @@ ], "dependencies": { "@node-redis/bloom": "1.0.1", - "@node-redis/client": "1.0.4", + "@node-redis/client": "1.0.5", "@node-redis/graph": "1.0.0", "@node-redis/json": "1.0.2", - "@node-redis/search": "1.0.3", + "@node-redis/search": "1.0.4", "@node-redis/time-series": "1.0.2" }, "devDependencies": { @@ -978,14 +978,14 @@ "dev": true }, "node_modules/@typescript-eslint/eslint-plugin": { - "version": "5.16.0", - "resolved": "https://registry.npmjs.org/@typescript-eslint/eslint-plugin/-/eslint-plugin-5.16.0.tgz", - "integrity": "sha512-SJoba1edXvQRMmNI505Uo4XmGbxCK9ARQpkvOd00anxzri9RNQk0DDCxD+LIl+jYhkzOJiOMMKYEHnHEODjdCw==", + "version": "5.17.0", + "resolved": "https://registry.npmjs.org/@typescript-eslint/eslint-plugin/-/eslint-plugin-5.17.0.tgz", + "integrity": "sha512-qVstvQilEd89HJk3qcbKt/zZrfBZ+9h2ynpAGlWjWiizA7m/MtLT9RoX6gjtpE500vfIg8jogAkDzdCxbsFASQ==", "dev": true, "dependencies": { - "@typescript-eslint/scope-manager": "5.16.0", - "@typescript-eslint/type-utils": "5.16.0", - "@typescript-eslint/utils": "5.16.0", + "@typescript-eslint/scope-manager": "5.17.0", + "@typescript-eslint/type-utils": "5.17.0", + "@typescript-eslint/utils": "5.17.0", "debug": "^4.3.2", "functional-red-black-tree": "^1.0.1", "ignore": "^5.1.8", @@ -1026,14 +1026,14 @@ } }, "node_modules/@typescript-eslint/parser": { - "version": "5.16.0", - "resolved": "https://registry.npmjs.org/@typescript-eslint/parser/-/parser-5.16.0.tgz", - "integrity": "sha512-fkDq86F0zl8FicnJtdXakFs4lnuebH6ZADDw6CYQv0UZeIjHvmEw87m9/29nk2Dv5Lmdp0zQ3zDQhiMWQf/GbA==", + "version": "5.17.0", + "resolved": "https://registry.npmjs.org/@typescript-eslint/parser/-/parser-5.17.0.tgz", + "integrity": "sha512-aRzW9Jg5Rlj2t2/crzhA2f23SIYFlF9mchGudyP0uiD6SenIxzKoLjwzHbafgHn39dNV/TV7xwQkLfFTZlJ4ig==", "dev": true, "dependencies": { - "@typescript-eslint/scope-manager": "5.16.0", - "@typescript-eslint/types": "5.16.0", - "@typescript-eslint/typescript-estree": "5.16.0", + "@typescript-eslint/scope-manager": "5.17.0", + "@typescript-eslint/types": "5.17.0", + "@typescript-eslint/typescript-estree": "5.17.0", "debug": "^4.3.2" }, "engines": { @@ -1053,13 +1053,13 @@ } }, "node_modules/@typescript-eslint/scope-manager": { - "version": "5.16.0", - "resolved": "https://registry.npmjs.org/@typescript-eslint/scope-manager/-/scope-manager-5.16.0.tgz", - "integrity": "sha512-P+Yab2Hovg8NekLIR/mOElCDPyGgFZKhGoZA901Yax6WR6HVeGLbsqJkZ+Cvk5nts/dAlFKm8PfL43UZnWdpIQ==", + "version": "5.17.0", + "resolved": "https://registry.npmjs.org/@typescript-eslint/scope-manager/-/scope-manager-5.17.0.tgz", + "integrity": "sha512-062iCYQF/doQ9T2WWfJohQKKN1zmmXVfAcS3xaiialiw8ZUGy05Em6QVNYJGO34/sU1a7a+90U3dUNfqUDHr3w==", "dev": true, "dependencies": { - "@typescript-eslint/types": "5.16.0", - "@typescript-eslint/visitor-keys": "5.16.0" + "@typescript-eslint/types": "5.17.0", + "@typescript-eslint/visitor-keys": "5.17.0" }, "engines": { "node": "^12.22.0 || ^14.17.0 || >=16.0.0" @@ -1070,12 +1070,12 @@ } }, "node_modules/@typescript-eslint/type-utils": { - "version": "5.16.0", - "resolved": "https://registry.npmjs.org/@typescript-eslint/type-utils/-/type-utils-5.16.0.tgz", - "integrity": "sha512-SKygICv54CCRl1Vq5ewwQUJV/8padIWvPgCxlWPGO/OgQLCijY9G7lDu6H+mqfQtbzDNlVjzVWQmeqbLMBLEwQ==", + "version": "5.17.0", + "resolved": "https://registry.npmjs.org/@typescript-eslint/type-utils/-/type-utils-5.17.0.tgz", + "integrity": "sha512-3hU0RynUIlEuqMJA7dragb0/75gZmwNwFf/QJokWzPehTZousP/MNifVSgjxNcDCkM5HI2K22TjQWUmmHUINSg==", "dev": true, "dependencies": { - "@typescript-eslint/utils": "5.16.0", + "@typescript-eslint/utils": "5.17.0", "debug": "^4.3.2", "tsutils": "^3.21.0" }, @@ -1096,9 +1096,9 @@ } }, "node_modules/@typescript-eslint/types": { - "version": "5.16.0", - "resolved": "https://registry.npmjs.org/@typescript-eslint/types/-/types-5.16.0.tgz", - "integrity": "sha512-oUorOwLj/3/3p/HFwrp6m/J2VfbLC8gjW5X3awpQJ/bSG+YRGFS4dpsvtQ8T2VNveV+LflQHjlLvB6v0R87z4g==", + "version": "5.17.0", + "resolved": "https://registry.npmjs.org/@typescript-eslint/types/-/types-5.17.0.tgz", + "integrity": "sha512-AgQ4rWzmCxOZLioFEjlzOI3Ch8giDWx8aUDxyNw9iOeCvD3GEYAB7dxWGQy4T/rPVe8iPmu73jPHuaSqcjKvxw==", "dev": true, "engines": { "node": "^12.22.0 || ^14.17.0 || >=16.0.0" @@ -1109,13 +1109,13 @@ } }, "node_modules/@typescript-eslint/typescript-estree": { - "version": "5.16.0", - "resolved": "https://registry.npmjs.org/@typescript-eslint/typescript-estree/-/typescript-estree-5.16.0.tgz", - "integrity": "sha512-SE4VfbLWUZl9MR+ngLSARptUv2E8brY0luCdgmUevU6arZRY/KxYoLI/3V/yxaURR8tLRN7bmZtJdgmzLHI6pQ==", + "version": "5.17.0", + "resolved": "https://registry.npmjs.org/@typescript-eslint/typescript-estree/-/typescript-estree-5.17.0.tgz", + "integrity": "sha512-X1gtjEcmM7Je+qJRhq7ZAAaNXYhTgqMkR10euC4Si6PIjb+kwEQHSxGazXUQXFyqfEXdkGf6JijUu5R0uceQzg==", "dev": true, "dependencies": { - "@typescript-eslint/types": "5.16.0", - "@typescript-eslint/visitor-keys": "5.16.0", + "@typescript-eslint/types": "5.17.0", + "@typescript-eslint/visitor-keys": "5.17.0", "debug": "^4.3.2", "globby": "^11.0.4", "is-glob": "^4.0.3", @@ -1180,15 +1180,15 @@ } }, "node_modules/@typescript-eslint/utils": { - "version": "5.16.0", - "resolved": "https://registry.npmjs.org/@typescript-eslint/utils/-/utils-5.16.0.tgz", - "integrity": "sha512-iYej2ER6AwmejLWMWzJIHy3nPJeGDuCqf8Jnb+jAQVoPpmWzwQOfa9hWVB8GIQE5gsCv/rfN4T+AYb/V06WseQ==", + "version": "5.17.0", + "resolved": "https://registry.npmjs.org/@typescript-eslint/utils/-/utils-5.17.0.tgz", + "integrity": "sha512-DVvndq1QoxQH+hFv+MUQHrrWZ7gQ5KcJzyjhzcqB1Y2Xes1UQQkTRPUfRpqhS8mhTWsSb2+iyvDW1Lef5DD7vA==", "dev": true, "dependencies": { "@types/json-schema": "^7.0.9", - "@typescript-eslint/scope-manager": "5.16.0", - "@typescript-eslint/types": "5.16.0", - "@typescript-eslint/typescript-estree": "5.16.0", + "@typescript-eslint/scope-manager": "5.17.0", + "@typescript-eslint/types": "5.17.0", + "@typescript-eslint/typescript-estree": "5.17.0", "eslint-scope": "^5.1.1", "eslint-utils": "^3.0.0" }, @@ -1204,12 +1204,12 @@ } }, "node_modules/@typescript-eslint/visitor-keys": { - "version": "5.16.0", - "resolved": "https://registry.npmjs.org/@typescript-eslint/visitor-keys/-/visitor-keys-5.16.0.tgz", - "integrity": "sha512-jqxO8msp5vZDhikTwq9ubyMHqZ67UIvawohr4qF3KhlpL7gzSjOd+8471H3nh5LyABkaI85laEKKU8SnGUK5/g==", + "version": "5.17.0", + "resolved": "https://registry.npmjs.org/@typescript-eslint/visitor-keys/-/visitor-keys-5.17.0.tgz", + "integrity": "sha512-6K/zlc4OfCagUu7Am/BD5k8PSWQOgh34Nrv9Rxe2tBzlJ7uOeJ/h7ugCGDCeEZHT6k2CJBhbk9IsbkPI0uvUkA==", "dev": true, "dependencies": { - "@typescript-eslint/types": "5.16.0", + "@typescript-eslint/types": "5.17.0", "eslint-visitor-keys": "^3.0.0" }, "engines": { @@ -1695,9 +1695,9 @@ } }, "node_modules/caniuse-lite": { - "version": "1.0.30001320", - "resolved": "https://registry.npmjs.org/caniuse-lite/-/caniuse-lite-1.0.30001320.tgz", - "integrity": "sha512-MWPzG54AGdo3nWx7zHZTefseM5Y1ccM7hlQKHRqJkPozUaw3hNbBTMmLn16GG2FUzjR13Cr3NPfhIieX5PzXDA==", + "version": "1.0.30001323", + "resolved": "https://registry.npmjs.org/caniuse-lite/-/caniuse-lite-1.0.30001323.tgz", + "integrity": "sha512-e4BF2RlCVELKx8+RmklSEIVub1TWrmdhvA5kEUueummz1XyySW0DVk+3x9HyhU9MuWTa2BhqLgEuEmUwASAdCA==", "dev": true, "funding": [ { @@ -2155,9 +2155,9 @@ "dev": true }, "node_modules/electron-to-chromium": { - "version": "1.4.96", - "resolved": "https://registry.npmjs.org/electron-to-chromium/-/electron-to-chromium-1.4.96.tgz", - "integrity": "sha512-DPNjvNGPabv6FcyjzLAN4C0psN/GgD9rSGvMTuv81SeXG/EX3mCz0wiw9N1tUEnfQXYCJi3H8M0oFPRziZh7rw==", + "version": "1.4.103", + "resolved": "https://registry.npmjs.org/electron-to-chromium/-/electron-to-chromium-1.4.103.tgz", + "integrity": "sha512-c/uKWR1Z/W30Wy/sx3dkZoj4BijbXX85QKWu9jJfjho3LBAXNEGAEW3oWiGb+dotA6C6BzCTxL2/aLes7jlUeg==", "dev": true }, "node_modules/email-addresses": { @@ -6529,7 +6529,7 @@ } }, "packages/client": { - "version": "1.0.4", + "version": "1.0.5", "license": "MIT", "dependencies": { "cluster-key-slot": "1.1.0", @@ -6596,7 +6596,7 @@ } }, "packages/search": { - "version": "1.0.3", + "version": "1.0.4", "license": "MIT", "devDependencies": { "@istanbuljs/nyc-config-typescript": "^1.0.2", @@ -7533,14 +7533,14 @@ "dev": true }, "@typescript-eslint/eslint-plugin": { - "version": "5.16.0", - "resolved": "https://registry.npmjs.org/@typescript-eslint/eslint-plugin/-/eslint-plugin-5.16.0.tgz", - "integrity": "sha512-SJoba1edXvQRMmNI505Uo4XmGbxCK9ARQpkvOd00anxzri9RNQk0DDCxD+LIl+jYhkzOJiOMMKYEHnHEODjdCw==", + "version": "5.17.0", + "resolved": "https://registry.npmjs.org/@typescript-eslint/eslint-plugin/-/eslint-plugin-5.17.0.tgz", + "integrity": "sha512-qVstvQilEd89HJk3qcbKt/zZrfBZ+9h2ynpAGlWjWiizA7m/MtLT9RoX6gjtpE500vfIg8jogAkDzdCxbsFASQ==", "dev": true, "requires": { - "@typescript-eslint/scope-manager": "5.16.0", - "@typescript-eslint/type-utils": "5.16.0", - "@typescript-eslint/utils": "5.16.0", + "@typescript-eslint/scope-manager": "5.17.0", + "@typescript-eslint/type-utils": "5.17.0", + "@typescript-eslint/utils": "5.17.0", "debug": "^4.3.2", "functional-red-black-tree": "^1.0.1", "ignore": "^5.1.8", @@ -7561,52 +7561,52 @@ } }, "@typescript-eslint/parser": { - "version": "5.16.0", - "resolved": "https://registry.npmjs.org/@typescript-eslint/parser/-/parser-5.16.0.tgz", - "integrity": "sha512-fkDq86F0zl8FicnJtdXakFs4lnuebH6ZADDw6CYQv0UZeIjHvmEw87m9/29nk2Dv5Lmdp0zQ3zDQhiMWQf/GbA==", + "version": "5.17.0", + "resolved": "https://registry.npmjs.org/@typescript-eslint/parser/-/parser-5.17.0.tgz", + "integrity": "sha512-aRzW9Jg5Rlj2t2/crzhA2f23SIYFlF9mchGudyP0uiD6SenIxzKoLjwzHbafgHn39dNV/TV7xwQkLfFTZlJ4ig==", "dev": true, "requires": { - "@typescript-eslint/scope-manager": "5.16.0", - "@typescript-eslint/types": "5.16.0", - "@typescript-eslint/typescript-estree": "5.16.0", + "@typescript-eslint/scope-manager": "5.17.0", + "@typescript-eslint/types": "5.17.0", + "@typescript-eslint/typescript-estree": "5.17.0", "debug": "^4.3.2" } }, "@typescript-eslint/scope-manager": { - "version": "5.16.0", - "resolved": "https://registry.npmjs.org/@typescript-eslint/scope-manager/-/scope-manager-5.16.0.tgz", - "integrity": "sha512-P+Yab2Hovg8NekLIR/mOElCDPyGgFZKhGoZA901Yax6WR6HVeGLbsqJkZ+Cvk5nts/dAlFKm8PfL43UZnWdpIQ==", + "version": "5.17.0", + "resolved": "https://registry.npmjs.org/@typescript-eslint/scope-manager/-/scope-manager-5.17.0.tgz", + "integrity": "sha512-062iCYQF/doQ9T2WWfJohQKKN1zmmXVfAcS3xaiialiw8ZUGy05Em6QVNYJGO34/sU1a7a+90U3dUNfqUDHr3w==", "dev": true, "requires": { - "@typescript-eslint/types": "5.16.0", - "@typescript-eslint/visitor-keys": "5.16.0" + "@typescript-eslint/types": "5.17.0", + "@typescript-eslint/visitor-keys": "5.17.0" } }, "@typescript-eslint/type-utils": { - "version": "5.16.0", - "resolved": "https://registry.npmjs.org/@typescript-eslint/type-utils/-/type-utils-5.16.0.tgz", - "integrity": "sha512-SKygICv54CCRl1Vq5ewwQUJV/8padIWvPgCxlWPGO/OgQLCijY9G7lDu6H+mqfQtbzDNlVjzVWQmeqbLMBLEwQ==", + "version": "5.17.0", + "resolved": "https://registry.npmjs.org/@typescript-eslint/type-utils/-/type-utils-5.17.0.tgz", + "integrity": "sha512-3hU0RynUIlEuqMJA7dragb0/75gZmwNwFf/QJokWzPehTZousP/MNifVSgjxNcDCkM5HI2K22TjQWUmmHUINSg==", "dev": true, "requires": { - "@typescript-eslint/utils": "5.16.0", + "@typescript-eslint/utils": "5.17.0", "debug": "^4.3.2", "tsutils": "^3.21.0" } }, "@typescript-eslint/types": { - "version": "5.16.0", - "resolved": "https://registry.npmjs.org/@typescript-eslint/types/-/types-5.16.0.tgz", - "integrity": "sha512-oUorOwLj/3/3p/HFwrp6m/J2VfbLC8gjW5X3awpQJ/bSG+YRGFS4dpsvtQ8T2VNveV+LflQHjlLvB6v0R87z4g==", + "version": "5.17.0", + "resolved": "https://registry.npmjs.org/@typescript-eslint/types/-/types-5.17.0.tgz", + "integrity": "sha512-AgQ4rWzmCxOZLioFEjlzOI3Ch8giDWx8aUDxyNw9iOeCvD3GEYAB7dxWGQy4T/rPVe8iPmu73jPHuaSqcjKvxw==", "dev": true }, "@typescript-eslint/typescript-estree": { - "version": "5.16.0", - "resolved": "https://registry.npmjs.org/@typescript-eslint/typescript-estree/-/typescript-estree-5.16.0.tgz", - "integrity": "sha512-SE4VfbLWUZl9MR+ngLSARptUv2E8brY0luCdgmUevU6arZRY/KxYoLI/3V/yxaURR8tLRN7bmZtJdgmzLHI6pQ==", + "version": "5.17.0", + "resolved": "https://registry.npmjs.org/@typescript-eslint/typescript-estree/-/typescript-estree-5.17.0.tgz", + "integrity": "sha512-X1gtjEcmM7Je+qJRhq7ZAAaNXYhTgqMkR10euC4Si6PIjb+kwEQHSxGazXUQXFyqfEXdkGf6JijUu5R0uceQzg==", "dev": true, "requires": { - "@typescript-eslint/types": "5.16.0", - "@typescript-eslint/visitor-keys": "5.16.0", + "@typescript-eslint/types": "5.17.0", + "@typescript-eslint/visitor-keys": "5.17.0", "debug": "^4.3.2", "globby": "^11.0.4", "is-glob": "^4.0.3", @@ -7646,26 +7646,26 @@ } }, "@typescript-eslint/utils": { - "version": "5.16.0", - "resolved": "https://registry.npmjs.org/@typescript-eslint/utils/-/utils-5.16.0.tgz", - "integrity": "sha512-iYej2ER6AwmejLWMWzJIHy3nPJeGDuCqf8Jnb+jAQVoPpmWzwQOfa9hWVB8GIQE5gsCv/rfN4T+AYb/V06WseQ==", + "version": "5.17.0", + "resolved": "https://registry.npmjs.org/@typescript-eslint/utils/-/utils-5.17.0.tgz", + "integrity": "sha512-DVvndq1QoxQH+hFv+MUQHrrWZ7gQ5KcJzyjhzcqB1Y2Xes1UQQkTRPUfRpqhS8mhTWsSb2+iyvDW1Lef5DD7vA==", "dev": true, "requires": { "@types/json-schema": "^7.0.9", - "@typescript-eslint/scope-manager": "5.16.0", - "@typescript-eslint/types": "5.16.0", - "@typescript-eslint/typescript-estree": "5.16.0", + "@typescript-eslint/scope-manager": "5.17.0", + "@typescript-eslint/types": "5.17.0", + "@typescript-eslint/typescript-estree": "5.17.0", "eslint-scope": "^5.1.1", "eslint-utils": "^3.0.0" } }, "@typescript-eslint/visitor-keys": { - "version": "5.16.0", - "resolved": "https://registry.npmjs.org/@typescript-eslint/visitor-keys/-/visitor-keys-5.16.0.tgz", - "integrity": "sha512-jqxO8msp5vZDhikTwq9ubyMHqZ67UIvawohr4qF3KhlpL7gzSjOd+8471H3nh5LyABkaI85laEKKU8SnGUK5/g==", + "version": "5.17.0", + "resolved": "https://registry.npmjs.org/@typescript-eslint/visitor-keys/-/visitor-keys-5.17.0.tgz", + "integrity": "sha512-6K/zlc4OfCagUu7Am/BD5k8PSWQOgh34Nrv9Rxe2tBzlJ7uOeJ/h7ugCGDCeEZHT6k2CJBhbk9IsbkPI0uvUkA==", "dev": true, "requires": { - "@typescript-eslint/types": "5.16.0", + "@typescript-eslint/types": "5.17.0", "eslint-visitor-keys": "^3.0.0" } }, @@ -8011,9 +8011,9 @@ "dev": true }, "caniuse-lite": { - "version": "1.0.30001320", - "resolved": "https://registry.npmjs.org/caniuse-lite/-/caniuse-lite-1.0.30001320.tgz", - "integrity": "sha512-MWPzG54AGdo3nWx7zHZTefseM5Y1ccM7hlQKHRqJkPozUaw3hNbBTMmLn16GG2FUzjR13Cr3NPfhIieX5PzXDA==", + "version": "1.0.30001323", + "resolved": "https://registry.npmjs.org/caniuse-lite/-/caniuse-lite-1.0.30001323.tgz", + "integrity": "sha512-e4BF2RlCVELKx8+RmklSEIVub1TWrmdhvA5kEUueummz1XyySW0DVk+3x9HyhU9MuWTa2BhqLgEuEmUwASAdCA==", "dev": true }, "chalk": { @@ -8353,9 +8353,9 @@ "dev": true }, "electron-to-chromium": { - "version": "1.4.96", - "resolved": "https://registry.npmjs.org/electron-to-chromium/-/electron-to-chromium-1.4.96.tgz", - "integrity": "sha512-DPNjvNGPabv6FcyjzLAN4C0psN/GgD9rSGvMTuv81SeXG/EX3mCz0wiw9N1tUEnfQXYCJi3H8M0oFPRziZh7rw==", + "version": "1.4.103", + "resolved": "https://registry.npmjs.org/electron-to-chromium/-/electron-to-chromium-1.4.103.tgz", + "integrity": "sha512-c/uKWR1Z/W30Wy/sx3dkZoj4BijbXX85QKWu9jJfjho3LBAXNEGAEW3oWiGb+dotA6C6BzCTxL2/aLes7jlUeg==", "dev": true }, "email-addresses": { diff --git a/package.json b/package.json index 2d9f045b964..52246495ad9 100644 --- a/package.json +++ b/package.json @@ -24,10 +24,10 @@ }, "dependencies": { "@node-redis/bloom": "1.0.1", - "@node-redis/client": "1.0.4", + "@node-redis/client": "1.0.5", "@node-redis/graph": "1.0.0", "@node-redis/json": "1.0.2", - "@node-redis/search": "1.0.3", + "@node-redis/search": "1.0.4", "@node-redis/time-series": "1.0.2" }, "devDependencies": { From 9c66e91e00d1f7a2a09a70c3f19ae740eed5a9cf Mon Sep 17 00:00:00 2001 From: leibale Date: Thu, 31 Mar 2022 14:31:38 +0300 Subject: [PATCH 1177/1748] Release redis@4.0.5 --- package-lock.json | 4 ++-- package.json | 2 +- 2 files changed, 3 insertions(+), 3 deletions(-) diff --git a/package-lock.json b/package-lock.json index 5d989b68f52..cf094eb2451 100644 --- a/package-lock.json +++ b/package-lock.json @@ -1,12 +1,12 @@ { "name": "redis", - "version": "4.0.4", + "version": "4.0.5", "lockfileVersion": 2, "requires": true, "packages": { "": { "name": "redis", - "version": "4.0.4", + "version": "4.0.5", "license": "MIT", "workspaces": [ "./packages/*" diff --git a/package.json b/package.json index 52246495ad9..50cb1b68700 100644 --- a/package.json +++ b/package.json @@ -1,7 +1,7 @@ { "name": "redis", "description": "A modern, high performance Redis client", - "version": "4.0.4", + "version": "4.0.5", "license": "MIT", "main": "./dist/index.js", "types": "./dist/index.d.ts", From 9b8f72a63c3b1c41e9da38bfac82c9e4978ae36f Mon Sep 17 00:00:00 2001 From: leibale Date: Thu, 31 Mar 2022 14:48:06 +0300 Subject: [PATCH 1178/1748] Release search@1.0.5 --- packages/search/package.json | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/packages/search/package.json b/packages/search/package.json index 48a0824e5c9..07ddeb72f45 100644 --- a/packages/search/package.json +++ b/packages/search/package.json @@ -1,6 +1,6 @@ { "name": "@node-redis/search", - "version": "1.0.4", + "version": "1.0.5", "license": "MIT", "main": "./dist/index.js", "types": "./dist/index.d.ts", From 738753cfe5536c21fe94fe0f5d5202afbc2e6a68 Mon Sep 17 00:00:00 2001 From: leibale Date: Thu, 31 Mar 2022 14:49:47 +0300 Subject: [PATCH 1179/1748] upgrade search --- package-lock.json | 4 ++-- package.json | 2 +- 2 files changed, 3 insertions(+), 3 deletions(-) diff --git a/package-lock.json b/package-lock.json index cf094eb2451..a67b3e90e88 100644 --- a/package-lock.json +++ b/package-lock.json @@ -16,7 +16,7 @@ "@node-redis/client": "1.0.5", "@node-redis/graph": "1.0.0", "@node-redis/json": "1.0.2", - "@node-redis/search": "1.0.4", + "@node-redis/search": "1.0.5", "@node-redis/time-series": "1.0.2" }, "devDependencies": { @@ -6596,7 +6596,7 @@ } }, "packages/search": { - "version": "1.0.4", + "version": "1.0.5", "license": "MIT", "devDependencies": { "@istanbuljs/nyc-config-typescript": "^1.0.2", diff --git a/package.json b/package.json index 50cb1b68700..821dd7d581c 100644 --- a/package.json +++ b/package.json @@ -27,7 +27,7 @@ "@node-redis/client": "1.0.5", "@node-redis/graph": "1.0.0", "@node-redis/json": "1.0.2", - "@node-redis/search": "1.0.4", + "@node-redis/search": "1.0.5", "@node-redis/time-series": "1.0.2" }, "devDependencies": { From a5d06937dbfd9da1e479c7ceeacecfa1ec39cbae Mon Sep 17 00:00:00 2001 From: leibale Date: Thu, 31 Mar 2022 14:50:22 +0300 Subject: [PATCH 1180/1748] Release redis@4.0.6 --- package-lock.json | 4 ++-- package.json | 2 +- 2 files changed, 3 insertions(+), 3 deletions(-) diff --git a/package-lock.json b/package-lock.json index a67b3e90e88..090ee912189 100644 --- a/package-lock.json +++ b/package-lock.json @@ -1,12 +1,12 @@ { "name": "redis", - "version": "4.0.5", + "version": "4.0.6", "lockfileVersion": 2, "requires": true, "packages": { "": { "name": "redis", - "version": "4.0.5", + "version": "4.0.6", "license": "MIT", "workspaces": [ "./packages/*" diff --git a/package.json b/package.json index 821dd7d581c..e8ce3380653 100644 --- a/package.json +++ b/package.json @@ -1,7 +1,7 @@ { "name": "redis", "description": "A modern, high performance Redis client", - "version": "4.0.5", + "version": "4.0.6", "license": "MIT", "main": "./dist/index.js", "types": "./dist/index.d.ts", From 0f65690e854baabba50e6c2d8aafd8c2fdea499e Mon Sep 17 00:00:00 2001 From: Leibale Eidelman Date: Sun, 3 Apr 2022 10:11:22 -0400 Subject: [PATCH 1181/1748] Remove console.log (#2068) --- packages/search/lib/commands/index.ts | 2 -- 1 file changed, 2 deletions(-) diff --git a/packages/search/lib/commands/index.ts b/packages/search/lib/commands/index.ts index 51ab50175e9..a2109191220 100644 --- a/packages/search/lib/commands/index.ts +++ b/packages/search/lib/commands/index.ts @@ -487,8 +487,6 @@ export function pushSearchOptions( args.push('DIALECT', options.DIALECT.toString()); } - console.log('!@#', args); - return args; } From d8e9da0f8e1b73e3f5af7b7f035b10904fc8255f Mon Sep 17 00:00:00 2001 From: Leibale Eidelman Date: Wed, 6 Apr 2022 12:33:50 +0300 Subject: [PATCH 1182/1748] support for buffers in redisearch params (#2073) --- packages/search/lib/commands/index.ts | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/packages/search/lib/commands/index.ts b/packages/search/lib/commands/index.ts index a2109191220..03ac0fe19f9 100644 --- a/packages/search/lib/commands/index.ts +++ b/packages/search/lib/commands/index.ts @@ -365,7 +365,7 @@ export function pushParamsArgs( const enrties = Object.entries(params); args.push('PARAMS', (enrties.length * 2).toString()); for (const [key, value] of enrties) { - args.push(key, value.toString()); + args.push(key, typeof value === 'number' ? value.toString() : value); } } From 329885b4ae3167d0092e856095b726e2adf89c97 Mon Sep 17 00:00:00 2001 From: Joe Price Date: Wed, 6 Apr 2022 08:43:32 -0700 Subject: [PATCH 1183/1748] Flatten array arguments in legacyMode multi commands (#2064) --- packages/client/lib/client/index.spec.ts | 14 ++++++++++++++ packages/client/lib/client/multi-command.ts | 2 +- 2 files changed, 15 insertions(+), 1 deletion(-) diff --git a/packages/client/lib/client/index.spec.ts b/packages/client/lib/client/index.spec.ts index 308ce1e9c49..c48505c7586 100644 --- a/packages/client/lib/client/index.spec.ts +++ b/packages/client/lib/client/index.spec.ts @@ -294,6 +294,20 @@ describe('Client', () => { } } }); + + testUtils.testWithClient('client.multi.{command}.exec should flatten array arguments', async client => { + assert.deepEqual( + await client.multi() + .sAdd('a', ['b', 'c']) + .v4.exec(), + [2]) + }, { + ...GLOBAL.SERVERS.OPEN, + clientOptions: { + legacyMode: true + } + }); + }); describe('events', () => { diff --git a/packages/client/lib/client/multi-command.ts b/packages/client/lib/client/multi-command.ts index cce0b515f1f..7b28637d676 100644 --- a/packages/client/lib/client/multi-command.ts +++ b/packages/client/lib/client/multi-command.ts @@ -87,7 +87,7 @@ export default class RedisClientMultiCommand { #defineLegacyCommand(name: string): void { this.v4[name] = (this as any)[name].bind(this.v4); (this as any)[name] = - (...args: Array): void => (this as any).addCommand(name, args); + (...args: Array): void => (this as any).addCommand(name, ...args); } commandsExecutor(command: RedisCommand, args: Array): this { From c473c5fcce3009dac6819ab50044f0dfed014041 Mon Sep 17 00:00:00 2001 From: Leibale Eidelman Date: Mon, 11 Apr 2022 13:28:03 -0400 Subject: [PATCH 1184/1748] call setKeepAlive after the connect event (#2074) --- packages/client/lib/client/socket.ts | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/packages/client/lib/client/socket.ts b/packages/client/lib/client/socket.ts index 269c52381f3..0366b2b86e1 100644 --- a/packages/client/lib/client/socket.ts +++ b/packages/client/lib/client/socket.ts @@ -170,11 +170,12 @@ export default class RedisSocket extends EventEmitter { socket .setNoDelay(this.#options.noDelay) - .setKeepAlive(this.#options.keepAlive !== false, this.#options.keepAlive || 0) .once('error', reject) .once(connectEvent, () => { socket .setTimeout(0) + // https://github.com/nodejs/node/issues/31663 + .setKeepAlive(this.#options.keepAlive !== false, this.#options.keepAlive || 0) .off('error', reject) .once('error', (err: Error) => this.#onSocketError(err)) .once('close', hadError => { From 4d1a86543b52afe8362cf67204890ea732b60589 Mon Sep 17 00:00:00 2001 From: Leibale Eidelman Date: Mon, 18 Apr 2022 12:28:36 -0400 Subject: [PATCH 1185/1748] remove console.log (#2083) --- packages/search/lib/commands/INFO.ts | 1 - 1 file changed, 1 deletion(-) diff --git a/packages/search/lib/commands/INFO.ts b/packages/search/lib/commands/INFO.ts index b4072aa759c..74929fad423 100644 --- a/packages/search/lib/commands/INFO.ts +++ b/packages/search/lib/commands/INFO.ts @@ -124,7 +124,6 @@ interface InfoReply { } export function transformReply(rawReply: InfoRawReply): InfoReply { - console.log(rawReply); return { indexName: rawReply[1], indexOptions: rawReply[3], From c5b706b385413751b0e5735cc8a8157db62b35a0 Mon Sep 17 00:00:00 2001 From: Guy Royse Date: Mon, 18 Apr 2022 12:28:53 -0400 Subject: [PATCH 1186/1748] exported GeoReplyWith (#2082) --- packages/client/index.ts | 2 ++ 1 file changed, 2 insertions(+) diff --git a/packages/client/index.ts b/packages/client/index.ts index 1a5d497aa9d..ac138a935e4 100644 --- a/packages/client/index.ts +++ b/packages/client/index.ts @@ -15,4 +15,6 @@ export const createCluster = RedisCluster.create; export { defineScript } from './lib/lua-script'; +export { GeoReplyWith } from './lib/commands/generic-transformers'; + export * from './lib/errors'; From 79ee8f2029ec927fcff0f5125f2d9c1012165987 Mon Sep 17 00:00:00 2001 From: Leibale Eidelman Date: Mon, 18 Apr 2022 12:29:01 -0400 Subject: [PATCH 1187/1748] upgrade dependencies (#2081) --- benchmark/package-lock.json | 357 +++-- benchmark/package.json | 8 +- package-lock.json | 2307 +++++++++++++++++------------ package.json | 4 +- packages/bloom/package.json | 4 +- packages/client/package.json | 10 +- packages/graph/package.json | 4 +- packages/json/package.json | 4 +- packages/search/package.json | 4 +- packages/test-utils/package.json | 2 +- packages/time-series/package.json | 4 +- 11 files changed, 1559 insertions(+), 1149 deletions(-) diff --git a/benchmark/package-lock.json b/benchmark/package-lock.json index af72486e0af..62a9608062e 100644 --- a/benchmark/package-lock.json +++ b/benchmark/package-lock.json @@ -6,17 +6,16 @@ "": { "name": "@node-redis/client-benchmark", "dependencies": { - "@node-redis/client-local": "../packages/client", - "@node-redis/client-production": "npm:@node-redis/client@1.0.0", - "hdr-histogram-js": "^2.0.1", - "ioredis": "4.28.1", - "redis-v3": "npm:redis@3.1.2", - "yargs": "17.3.0" + "@node-redis/client": "../packages/client", + "hdr-histogram-js": "3.0.0", + "ioredis": "5.0.4", + "redis-v3": "npm:redis@4.0.6", + "yargs": "17.4.1" } }, "../packages/client": { "name": "@node-redis/client", - "version": "1.0.0", + "version": "1.0.5", "license": "MIT", "dependencies": { "cluster-key-slot": "1.1.0", @@ -25,51 +24,79 @@ "yallist": "4.0.0" }, "devDependencies": { - "@istanbuljs/nyc-config-typescript": "^1.0.1", + "@istanbuljs/nyc-config-typescript": "^1.0.2", "@node-redis/test-utils": "*", - "@types/node": "^16.11.10", + "@types/node": "^17.0.23", "@types/redis-parser": "^3.0.0", - "@types/sinon": "^10.0.6", + "@types/sinon": "^10.0.11", "@types/yallist": "^4.0.1", - "@typescript-eslint/eslint-plugin": "^5.4.0", - "@typescript-eslint/parser": "^5.4.0", - "eslint": "^8.3.0", + "@typescript-eslint/eslint-plugin": "^5.19.0", + "@typescript-eslint/parser": "^5.19.0", + "eslint": "^8.13.0", "nyc": "^15.1.0", - "release-it": "^14.11.8", - "sinon": "^12.0.1", + "release-it": "^14.14.2", + "sinon": "^13.0.1", "source-map-support": "^0.5.21", - "ts-node": "^10.4.0", - "typedoc": "^0.22.10", - "typedoc-github-wiki-theme": "^0.6.0", - "typedoc-plugin-markdown": "^3.11.7", - "typescript": "^4.5.2" + "ts-node": "^10.7.0", + "typedoc": "^0.22.15", + "typescript": "^4.6.3" }, "engines": { "node": ">=12" } }, "node_modules/@assemblyscript/loader": { - "version": "0.10.1", - "resolved": "https://registry.npmjs.org/@assemblyscript/loader/-/loader-0.10.1.tgz", - "integrity": "sha512-H71nDOOL8Y7kWRLqf6Sums+01Q5msqBW2KhDUTemh1tvY04eSkSXrK0uj/4mmY0Xr16/3zyZmsrxN7CKuRbNRg==" + "version": "0.19.23", + "resolved": "https://registry.npmjs.org/@assemblyscript/loader/-/loader-0.19.23.tgz", + "integrity": "sha512-ulkCYfFbYj01ie1MDOyxv2F6SpRN1TOj7fQxbP07D6HmeR+gr2JLSmINKjga2emB+b1L2KGrFKBTc+e00p54nw==" + }, + "node_modules/@ioredis/commands": { + "version": "1.1.1", + "resolved": "https://registry.npmjs.org/@ioredis/commands/-/commands-1.1.1.tgz", + "integrity": "sha512-fsR4P/ROllzf/7lXYyElUJCheWdTJVJvOTps8v9IWKFATxR61ANOlnoPqhH099xYLrJGpc2ZQ28B3rMeUt5VQg==" + }, + "node_modules/@node-redis/bloom": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/@node-redis/bloom/-/bloom-1.0.1.tgz", + "integrity": "sha512-mXEBvEIgF4tUzdIN89LiYsbi6//EdpFA7L8M+DHCvePXg+bfHWi+ct5VI6nHUFQE5+ohm/9wmgihCH3HSkeKsw==", + "peerDependencies": { + "@node-redis/client": "^1.0.0" + } }, - "node_modules/@node-redis/client-local": { + "node_modules/@node-redis/client": { "resolved": "../packages/client", "link": true }, - "node_modules/@node-redis/client-production": { - "name": "@node-redis/client", + "node_modules/@node-redis/graph": { "version": "1.0.0", - "resolved": "https://registry.npmjs.org/@node-redis/client/-/client-1.0.0.tgz", - "integrity": "sha512-DWDMeZELXG3rOGzCKfJEHCkCP6rgiA1H+oqj2N0NR4Q0fQUYMxTsyoqt80GpdYLilUW6zoCiQl9yL3vJhGhiCA==", - "dependencies": { - "cluster-key-slot": "1.1.0", - "generic-pool": "3.8.2", - "redis-parser": "3.0.0", - "yallist": "4.0.0" - }, - "engines": { - "node": ">=12" + "resolved": "https://registry.npmjs.org/@node-redis/graph/-/graph-1.0.0.tgz", + "integrity": "sha512-mRSo8jEGC0cf+Rm7q8mWMKKKqkn6EAnA9IA2S3JvUv/gaWW/73vil7GLNwion2ihTptAm05I9LkepzfIXUKX5g==", + "peerDependencies": { + "@node-redis/client": "^1.0.0" + } + }, + "node_modules/@node-redis/json": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/@node-redis/json/-/json-1.0.2.tgz", + "integrity": "sha512-qVRgn8WfG46QQ08CghSbY4VhHFgaTY71WjpwRBGEuqGPfWwfRcIf3OqSpR7Q/45X+v3xd8mvYjywqh0wqJ8T+g==", + "peerDependencies": { + "@node-redis/client": "^1.0.0" + } + }, + "node_modules/@node-redis/search": { + "version": "1.0.5", + "resolved": "https://registry.npmjs.org/@node-redis/search/-/search-1.0.5.tgz", + "integrity": "sha512-MCOL8iCKq4v+3HgEQv8zGlSkZyXSXtERgrAJ4TSryIG/eLFy84b57KmNNa/V7M1Q2Wd2hgn2nPCGNcQtk1R1OQ==", + "peerDependencies": { + "@node-redis/client": "^1.0.0" + } + }, + "node_modules/@node-redis/time-series": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/@node-redis/time-series/-/time-series-1.0.2.tgz", + "integrity": "sha512-HGQ8YooJ8Mx7l28tD7XjtB3ImLEjlUxG1wC1PAjxu6hPJqjPshUZxAICzDqDjtIbhDTf48WXXUcx8TQJB1XTKA==", + "peerDependencies": { + "@node-redis/client": "^1.0.0" } }, "node_modules/ansi-regex": { @@ -148,9 +175,9 @@ "integrity": "sha512-dOy+3AuW3a2wNbZHIuMZpTcgjGuLU/uBL/ubcZF9OXbDo8ff4O8yVp5Bf0efS8uEoYo5q4Fx7dY9OgQGXgAsQA==" }, "node_modules/debug": { - "version": "4.3.3", - "resolved": "https://registry.npmjs.org/debug/-/debug-4.3.3.tgz", - "integrity": "sha512-/zxw5+vh1Tfv+4Qn7a5nsbcJKPaSvCDhojn6FEl9vupwK2VCSDtEiEtqr8DFtzYFOdz63LBkxec7DYuc2jon6Q==", + "version": "4.3.4", + "resolved": "https://registry.npmjs.org/debug/-/debug-4.3.4.tgz", + "integrity": "sha512-PRWFHuSU3eDtQJPvnNY7Jcket1j0t5OuOsFzPPzsekD52Zl8qUfFIPEiswXqIvHWGVHOgX+7G/vCNNhehwxfkQ==", "dependencies": { "ms": "2.1.2" }, @@ -164,9 +191,9 @@ } }, "node_modules/denque": { - "version": "1.5.1", - "resolved": "https://registry.npmjs.org/denque/-/denque-1.5.1.tgz", - "integrity": "sha512-XwE+iZ4D6ZUB7mfYRMb5wByE8L74HCn30FBN7sWnXksWc1LO1bPDl67pBR9o/kC4z/xSNAwkMYcGgqDV3BE3Hw==", + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/denque/-/denque-2.0.1.tgz", + "integrity": "sha512-tfiWc6BQLXNLpNiR5iGd0Ocu3P3VpxfzFiqubLgMfhfOw9WyvgJBd46CClNn9k3qfbjvT//0cf7AlYRX/OslMQ==", "engines": { "node": ">=0.10" } @@ -184,14 +211,6 @@ "node": ">=6" } }, - "node_modules/generic-pool": { - "version": "3.8.2", - "resolved": "https://registry.npmjs.org/generic-pool/-/generic-pool-3.8.2.tgz", - "integrity": "sha512-nGToKy6p3PAbYQ7p1UlWl6vSPwfwU6TMSWK7TTu+WUY4ZjyZQGniGGt2oNVvyNSpyZYSB43zMXVLcBm08MTMkg==", - "engines": { - "node": ">= 4" - } - }, "node_modules/get-caller-file": { "version": "2.0.5", "resolved": "https://registry.npmjs.org/get-caller-file/-/get-caller-file-2.0.5.tgz", @@ -201,34 +220,35 @@ } }, "node_modules/hdr-histogram-js": { - "version": "2.0.1", - "resolved": "https://registry.npmjs.org/hdr-histogram-js/-/hdr-histogram-js-2.0.1.tgz", - "integrity": "sha512-uPZxl1dAFnjUFHWLZmt93vUUvtHeaBay9nVNHu38SdOjMSF/4KqJUqa1Seuj08ptU1rEb6AHvB41X8n/zFZ74Q==", + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/hdr-histogram-js/-/hdr-histogram-js-3.0.0.tgz", + "integrity": "sha512-/EpvQI2/Z98mNFYEnlqJ8Ogful8OpArLG/6Tf2bPnkutBVLIeMVNHjk1ZDfshF2BUweipzbk+dB1hgSB7SIakw==", "dependencies": { - "@assemblyscript/loader": "^0.10.1", + "@assemblyscript/loader": "^0.19.21", "base64-js": "^1.2.0", "pako": "^1.0.3" + }, + "engines": { + "node": ">=14" } }, "node_modules/ioredis": { - "version": "4.28.1", - "resolved": "https://registry.npmjs.org/ioredis/-/ioredis-4.28.1.tgz", - "integrity": "sha512-7gcrUJEcPHWy+eEyq6wIZpXtfHt8crhbc5+z0sqrnHUkwBblXinygfamj+/jx83Qo+2LW3q87Nj2VsuH6BF2BA==", + "version": "5.0.4", + "resolved": "https://registry.npmjs.org/ioredis/-/ioredis-5.0.4.tgz", + "integrity": "sha512-qFJw3MnPNsJF1lcIOP3vztbsasOXK3nDdNAgjQj7t7/Bn/w10PGchTOpqylQNxjzPbLoYDu34LjeJtSWiKBntQ==", "dependencies": { + "@ioredis/commands": "^1.1.1", "cluster-key-slot": "^1.1.0", - "debug": "^4.3.1", - "denque": "^1.1.0", + "debug": "^4.3.4", + "denque": "^2.0.1", "lodash.defaults": "^4.2.0", - "lodash.flatten": "^4.4.0", "lodash.isarguments": "^3.1.0", - "p-map": "^2.1.0", - "redis-commands": "1.7.0", "redis-errors": "^1.2.0", "redis-parser": "^3.0.0", "standard-as-callback": "^2.1.0" }, "engines": { - "node": ">=6" + "node": ">=12.22.0" }, "funding": { "type": "opencollective", @@ -248,11 +268,6 @@ "resolved": "https://registry.npmjs.org/lodash.defaults/-/lodash.defaults-4.2.0.tgz", "integrity": "sha1-0JF4cW/+pN3p5ft7N/bwgCJ0WAw=" }, - "node_modules/lodash.flatten": { - "version": "4.4.0", - "resolved": "https://registry.npmjs.org/lodash.flatten/-/lodash.flatten-4.4.0.tgz", - "integrity": "sha1-8xwiIlqWMtK7+OSt2+8kCqdlph8=" - }, "node_modules/lodash.isarguments": { "version": "3.1.0", "resolved": "https://registry.npmjs.org/lodash.isarguments/-/lodash.isarguments-3.1.0.tgz", @@ -263,24 +278,11 @@ "resolved": "https://registry.npmjs.org/ms/-/ms-2.1.2.tgz", "integrity": "sha512-sGkPx+VjMtmA6MX27oA4FBFELFCZZ4S4XqeGOXCv68tT+jb3vk/RyaKWP0PTKyWtmLSM0b+adUTEvbs1PEaH2w==" }, - "node_modules/p-map": { - "version": "2.1.0", - "resolved": "https://registry.npmjs.org/p-map/-/p-map-2.1.0.tgz", - "integrity": "sha512-y3b8Kpd8OAN444hxfBbFfj1FY/RjtTd8tzYwhUqNYXx0fXx2iX4maP4Qr6qhIKbQXI02wTLAda4fYUbDagTUFw==", - "engines": { - "node": ">=6" - } - }, "node_modules/pako": { "version": "1.0.11", "resolved": "https://registry.npmjs.org/pako/-/pako-1.0.11.tgz", "integrity": "sha512-4hLB8Py4zZce5s4yd9XzopqwVv/yGNhV1Bl8NTmCq1763HeK2+EwVTv+leGeL13Dnh2wfbqowVPXCIO0z4taYw==" }, - "node_modules/redis-commands": { - "version": "1.7.0", - "resolved": "https://registry.npmjs.org/redis-commands/-/redis-commands-1.7.0.tgz", - "integrity": "sha512-nJWqw3bTFy21hX/CPKHth6sfhZbdiHP6bTawSgQBlKOVRG7EZkfHbbHwQJnrE4vsQf0CMNE+3gJ4Fmm16vdVlQ==" - }, "node_modules/redis-errors": { "version": "1.2.0", "resolved": "https://registry.npmjs.org/redis-errors/-/redis-errors-1.2.0.tgz", @@ -302,21 +304,16 @@ }, "node_modules/redis-v3": { "name": "redis", - "version": "3.1.2", - "resolved": "https://registry.npmjs.org/redis/-/redis-3.1.2.tgz", - "integrity": "sha512-grn5KoZLr/qrRQVwoSkmzdbw6pwF+/rwODtrOr6vuBRiR/f3rjSTGupbF90Zpqm2oenix8Do6RV7pYEkGwlKkw==", + "version": "4.0.6", + "resolved": "https://registry.npmjs.org/redis/-/redis-4.0.6.tgz", + "integrity": "sha512-IaPAxgF5dV0jx+A9l6yd6R9/PAChZIoAskDVRzUODeLDNhsMlq7OLLTmu0AwAr0xjrJ1bibW5xdpRwqIQ8Q0Xg==", "dependencies": { - "denque": "^1.5.0", - "redis-commands": "^1.7.0", - "redis-errors": "^1.2.0", - "redis-parser": "^3.0.0" - }, - "engines": { - "node": ">=10" - }, - "funding": { - "type": "opencollective", - "url": "https://opencollective.com/node-redis" + "@node-redis/bloom": "1.0.1", + "@node-redis/client": "1.0.5", + "@node-redis/graph": "1.0.0", + "@node-redis/json": "1.0.2", + "@node-redis/search": "1.0.5", + "@node-redis/time-series": "1.0.2" } }, "node_modules/require-directory": { @@ -380,15 +377,10 @@ "node": ">=10" } }, - "node_modules/yallist": { - "version": "4.0.0", - "resolved": "https://registry.npmjs.org/yallist/-/yallist-4.0.0.tgz", - "integrity": "sha512-3wdGidZyq5PB084XLES5TpOSRA3wjXAlIWMhum2kRcv/41Sn2emQ0dycQW4uZXLejwKvg6EsvbdlVL+FYEct7A==" - }, "node_modules/yargs": { - "version": "17.3.0", - "resolved": "https://registry.npmjs.org/yargs/-/yargs-17.3.0.tgz", - "integrity": "sha512-GQl1pWyDoGptFPJx9b9L6kmR33TGusZvXIZUT+BOz9f7X2L94oeAskFYLEg/FkhV06zZPBYLvLZRWeYId29lew==", + "version": "17.4.1", + "resolved": "https://registry.npmjs.org/yargs/-/yargs-17.4.1.tgz", + "integrity": "sha512-WSZD9jgobAg3ZKuCQZSa3g9QOJeCCqLoLAykiWgmXnDo9EPnn4RPf5qVTtzgOx66o6/oqhcA5tHtJXpG8pMt3g==", "dependencies": { "cliui": "^7.0.2", "escalade": "^3.1.1", @@ -413,47 +405,69 @@ }, "dependencies": { "@assemblyscript/loader": { - "version": "0.10.1", - "resolved": "https://registry.npmjs.org/@assemblyscript/loader/-/loader-0.10.1.tgz", - "integrity": "sha512-H71nDOOL8Y7kWRLqf6Sums+01Q5msqBW2KhDUTemh1tvY04eSkSXrK0uj/4mmY0Xr16/3zyZmsrxN7CKuRbNRg==" - }, - "@node-redis/client-local": { + "version": "0.19.23", + "resolved": "https://registry.npmjs.org/@assemblyscript/loader/-/loader-0.19.23.tgz", + "integrity": "sha512-ulkCYfFbYj01ie1MDOyxv2F6SpRN1TOj7fQxbP07D6HmeR+gr2JLSmINKjga2emB+b1L2KGrFKBTc+e00p54nw==" + }, + "@ioredis/commands": { + "version": "1.1.1", + "resolved": "https://registry.npmjs.org/@ioredis/commands/-/commands-1.1.1.tgz", + "integrity": "sha512-fsR4P/ROllzf/7lXYyElUJCheWdTJVJvOTps8v9IWKFATxR61ANOlnoPqhH099xYLrJGpc2ZQ28B3rMeUt5VQg==" + }, + "@node-redis/bloom": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/@node-redis/bloom/-/bloom-1.0.1.tgz", + "integrity": "sha512-mXEBvEIgF4tUzdIN89LiYsbi6//EdpFA7L8M+DHCvePXg+bfHWi+ct5VI6nHUFQE5+ohm/9wmgihCH3HSkeKsw==", + "requires": {} + }, + "@node-redis/client": { "version": "file:../packages/client", "requires": { - "@istanbuljs/nyc-config-typescript": "^1.0.1", + "@istanbuljs/nyc-config-typescript": "^1.0.2", "@node-redis/test-utils": "*", - "@types/node": "^16.11.10", + "@types/node": "^17.0.23", "@types/redis-parser": "^3.0.0", - "@types/sinon": "^10.0.6", + "@types/sinon": "^10.0.11", "@types/yallist": "^4.0.1", - "@typescript-eslint/eslint-plugin": "^5.4.0", - "@typescript-eslint/parser": "^5.4.0", + "@typescript-eslint/eslint-plugin": "^5.19.0", + "@typescript-eslint/parser": "^5.19.0", "cluster-key-slot": "1.1.0", - "eslint": "^8.3.0", + "eslint": "^8.13.0", "generic-pool": "3.8.2", "nyc": "^15.1.0", "redis-parser": "3.0.0", - "release-it": "^14.11.8", - "sinon": "^12.0.1", + "release-it": "^14.14.2", + "sinon": "^13.0.1", "source-map-support": "^0.5.21", - "ts-node": "^10.4.0", - "typedoc": "^0.22.10", - "typedoc-github-wiki-theme": "^0.6.0", - "typedoc-plugin-markdown": "^3.11.7", - "typescript": "^4.5.2", + "ts-node": "^10.7.0", + "typedoc": "^0.22.15", + "typescript": "^4.6.3", "yallist": "4.0.0" } }, - "@node-redis/client-production": { - "version": "npm:@node-redis/client@1.0.0", - "resolved": "https://registry.npmjs.org/@node-redis/client/-/client-1.0.0.tgz", - "integrity": "sha512-DWDMeZELXG3rOGzCKfJEHCkCP6rgiA1H+oqj2N0NR4Q0fQUYMxTsyoqt80GpdYLilUW6zoCiQl9yL3vJhGhiCA==", - "requires": { - "cluster-key-slot": "1.1.0", - "generic-pool": "3.8.2", - "redis-parser": "3.0.0", - "yallist": "4.0.0" - } + "@node-redis/graph": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/@node-redis/graph/-/graph-1.0.0.tgz", + "integrity": "sha512-mRSo8jEGC0cf+Rm7q8mWMKKKqkn6EAnA9IA2S3JvUv/gaWW/73vil7GLNwion2ihTptAm05I9LkepzfIXUKX5g==", + "requires": {} + }, + "@node-redis/json": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/@node-redis/json/-/json-1.0.2.tgz", + "integrity": "sha512-qVRgn8WfG46QQ08CghSbY4VhHFgaTY71WjpwRBGEuqGPfWwfRcIf3OqSpR7Q/45X+v3xd8mvYjywqh0wqJ8T+g==", + "requires": {} + }, + "@node-redis/search": { + "version": "1.0.5", + "resolved": "https://registry.npmjs.org/@node-redis/search/-/search-1.0.5.tgz", + "integrity": "sha512-MCOL8iCKq4v+3HgEQv8zGlSkZyXSXtERgrAJ4TSryIG/eLFy84b57KmNNa/V7M1Q2Wd2hgn2nPCGNcQtk1R1OQ==", + "requires": {} + }, + "@node-redis/time-series": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/@node-redis/time-series/-/time-series-1.0.2.tgz", + "integrity": "sha512-HGQ8YooJ8Mx7l28tD7XjtB3ImLEjlUxG1wC1PAjxu6hPJqjPshUZxAICzDqDjtIbhDTf48WXXUcx8TQJB1XTKA==", + "requires": {} }, "ansi-regex": { "version": "5.0.1", @@ -502,17 +516,17 @@ "integrity": "sha512-dOy+3AuW3a2wNbZHIuMZpTcgjGuLU/uBL/ubcZF9OXbDo8ff4O8yVp5Bf0efS8uEoYo5q4Fx7dY9OgQGXgAsQA==" }, "debug": { - "version": "4.3.3", - "resolved": "https://registry.npmjs.org/debug/-/debug-4.3.3.tgz", - "integrity": "sha512-/zxw5+vh1Tfv+4Qn7a5nsbcJKPaSvCDhojn6FEl9vupwK2VCSDtEiEtqr8DFtzYFOdz63LBkxec7DYuc2jon6Q==", + "version": "4.3.4", + "resolved": "https://registry.npmjs.org/debug/-/debug-4.3.4.tgz", + "integrity": "sha512-PRWFHuSU3eDtQJPvnNY7Jcket1j0t5OuOsFzPPzsekD52Zl8qUfFIPEiswXqIvHWGVHOgX+7G/vCNNhehwxfkQ==", "requires": { "ms": "2.1.2" } }, "denque": { - "version": "1.5.1", - "resolved": "https://registry.npmjs.org/denque/-/denque-1.5.1.tgz", - "integrity": "sha512-XwE+iZ4D6ZUB7mfYRMb5wByE8L74HCn30FBN7sWnXksWc1LO1bPDl67pBR9o/kC4z/xSNAwkMYcGgqDV3BE3Hw==" + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/denque/-/denque-2.0.1.tgz", + "integrity": "sha512-tfiWc6BQLXNLpNiR5iGd0Ocu3P3VpxfzFiqubLgMfhfOw9WyvgJBd46CClNn9k3qfbjvT//0cf7AlYRX/OslMQ==" }, "emoji-regex": { "version": "8.0.0", @@ -524,39 +538,32 @@ "resolved": "https://registry.npmjs.org/escalade/-/escalade-3.1.1.tgz", "integrity": "sha512-k0er2gUkLf8O0zKJiAhmkTnJlTvINGv7ygDNPbeIsX/TJjGJZHuh9B2UxbsaEkmlEo9MfhrSzmhIlhRlI2GXnw==" }, - "generic-pool": { - "version": "3.8.2", - "resolved": "https://registry.npmjs.org/generic-pool/-/generic-pool-3.8.2.tgz", - "integrity": "sha512-nGToKy6p3PAbYQ7p1UlWl6vSPwfwU6TMSWK7TTu+WUY4ZjyZQGniGGt2oNVvyNSpyZYSB43zMXVLcBm08MTMkg==" - }, "get-caller-file": { "version": "2.0.5", "resolved": "https://registry.npmjs.org/get-caller-file/-/get-caller-file-2.0.5.tgz", "integrity": "sha512-DyFP3BM/3YHTQOCUL/w0OZHR0lpKeGrxotcHWcqNEdnltqFwXVfhEBQ94eIo34AfQpo0rGki4cyIiftY06h2Fg==" }, "hdr-histogram-js": { - "version": "2.0.1", - "resolved": "https://registry.npmjs.org/hdr-histogram-js/-/hdr-histogram-js-2.0.1.tgz", - "integrity": "sha512-uPZxl1dAFnjUFHWLZmt93vUUvtHeaBay9nVNHu38SdOjMSF/4KqJUqa1Seuj08ptU1rEb6AHvB41X8n/zFZ74Q==", + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/hdr-histogram-js/-/hdr-histogram-js-3.0.0.tgz", + "integrity": "sha512-/EpvQI2/Z98mNFYEnlqJ8Ogful8OpArLG/6Tf2bPnkutBVLIeMVNHjk1ZDfshF2BUweipzbk+dB1hgSB7SIakw==", "requires": { - "@assemblyscript/loader": "^0.10.1", + "@assemblyscript/loader": "^0.19.21", "base64-js": "^1.2.0", "pako": "^1.0.3" } }, "ioredis": { - "version": "4.28.1", - "resolved": "https://registry.npmjs.org/ioredis/-/ioredis-4.28.1.tgz", - "integrity": "sha512-7gcrUJEcPHWy+eEyq6wIZpXtfHt8crhbc5+z0sqrnHUkwBblXinygfamj+/jx83Qo+2LW3q87Nj2VsuH6BF2BA==", + "version": "5.0.4", + "resolved": "https://registry.npmjs.org/ioredis/-/ioredis-5.0.4.tgz", + "integrity": "sha512-qFJw3MnPNsJF1lcIOP3vztbsasOXK3nDdNAgjQj7t7/Bn/w10PGchTOpqylQNxjzPbLoYDu34LjeJtSWiKBntQ==", "requires": { + "@ioredis/commands": "^1.1.1", "cluster-key-slot": "^1.1.0", - "debug": "^4.3.1", - "denque": "^1.1.0", + "debug": "^4.3.4", + "denque": "^2.0.1", "lodash.defaults": "^4.2.0", - "lodash.flatten": "^4.4.0", "lodash.isarguments": "^3.1.0", - "p-map": "^2.1.0", - "redis-commands": "1.7.0", "redis-errors": "^1.2.0", "redis-parser": "^3.0.0", "standard-as-callback": "^2.1.0" @@ -572,11 +579,6 @@ "resolved": "https://registry.npmjs.org/lodash.defaults/-/lodash.defaults-4.2.0.tgz", "integrity": "sha1-0JF4cW/+pN3p5ft7N/bwgCJ0WAw=" }, - "lodash.flatten": { - "version": "4.4.0", - "resolved": "https://registry.npmjs.org/lodash.flatten/-/lodash.flatten-4.4.0.tgz", - "integrity": "sha1-8xwiIlqWMtK7+OSt2+8kCqdlph8=" - }, "lodash.isarguments": { "version": "3.1.0", "resolved": "https://registry.npmjs.org/lodash.isarguments/-/lodash.isarguments-3.1.0.tgz", @@ -587,21 +589,11 @@ "resolved": "https://registry.npmjs.org/ms/-/ms-2.1.2.tgz", "integrity": "sha512-sGkPx+VjMtmA6MX27oA4FBFELFCZZ4S4XqeGOXCv68tT+jb3vk/RyaKWP0PTKyWtmLSM0b+adUTEvbs1PEaH2w==" }, - "p-map": { - "version": "2.1.0", - "resolved": "https://registry.npmjs.org/p-map/-/p-map-2.1.0.tgz", - "integrity": "sha512-y3b8Kpd8OAN444hxfBbFfj1FY/RjtTd8tzYwhUqNYXx0fXx2iX4maP4Qr6qhIKbQXI02wTLAda4fYUbDagTUFw==" - }, "pako": { "version": "1.0.11", "resolved": "https://registry.npmjs.org/pako/-/pako-1.0.11.tgz", "integrity": "sha512-4hLB8Py4zZce5s4yd9XzopqwVv/yGNhV1Bl8NTmCq1763HeK2+EwVTv+leGeL13Dnh2wfbqowVPXCIO0z4taYw==" }, - "redis-commands": { - "version": "1.7.0", - "resolved": "https://registry.npmjs.org/redis-commands/-/redis-commands-1.7.0.tgz", - "integrity": "sha512-nJWqw3bTFy21hX/CPKHth6sfhZbdiHP6bTawSgQBlKOVRG7EZkfHbbHwQJnrE4vsQf0CMNE+3gJ4Fmm16vdVlQ==" - }, "redis-errors": { "version": "1.2.0", "resolved": "https://registry.npmjs.org/redis-errors/-/redis-errors-1.2.0.tgz", @@ -616,14 +608,16 @@ } }, "redis-v3": { - "version": "npm:redis@3.1.2", - "resolved": "https://registry.npmjs.org/redis/-/redis-3.1.2.tgz", - "integrity": "sha512-grn5KoZLr/qrRQVwoSkmzdbw6pwF+/rwODtrOr6vuBRiR/f3rjSTGupbF90Zpqm2oenix8Do6RV7pYEkGwlKkw==", + "version": "npm:redis@4.0.6", + "resolved": "https://registry.npmjs.org/redis/-/redis-4.0.6.tgz", + "integrity": "sha512-IaPAxgF5dV0jx+A9l6yd6R9/PAChZIoAskDVRzUODeLDNhsMlq7OLLTmu0AwAr0xjrJ1bibW5xdpRwqIQ8Q0Xg==", "requires": { - "denque": "^1.5.0", - "redis-commands": "^1.7.0", - "redis-errors": "^1.2.0", - "redis-parser": "^3.0.0" + "@node-redis/bloom": "1.0.1", + "@node-redis/client": "1.0.5", + "@node-redis/graph": "1.0.0", + "@node-redis/json": "1.0.2", + "@node-redis/search": "1.0.5", + "@node-redis/time-series": "1.0.2" } }, "require-directory": { @@ -669,15 +663,10 @@ "resolved": "https://registry.npmjs.org/y18n/-/y18n-5.0.8.tgz", "integrity": "sha512-0pfFzegeDWJHJIAmTLRP2DwHjdF5s7jo9tuztdQxAhINCdvS+3nGINqPd00AphqJR/0LhANUS6/+7SCb98YOfA==" }, - "yallist": { - "version": "4.0.0", - "resolved": "https://registry.npmjs.org/yallist/-/yallist-4.0.0.tgz", - "integrity": "sha512-3wdGidZyq5PB084XLES5TpOSRA3wjXAlIWMhum2kRcv/41Sn2emQ0dycQW4uZXLejwKvg6EsvbdlVL+FYEct7A==" - }, "yargs": { - "version": "17.3.0", - "resolved": "https://registry.npmjs.org/yargs/-/yargs-17.3.0.tgz", - "integrity": "sha512-GQl1pWyDoGptFPJx9b9L6kmR33TGusZvXIZUT+BOz9f7X2L94oeAskFYLEg/FkhV06zZPBYLvLZRWeYId29lew==", + "version": "17.4.1", + "resolved": "https://registry.npmjs.org/yargs/-/yargs-17.4.1.tgz", + "integrity": "sha512-WSZD9jgobAg3ZKuCQZSa3g9QOJeCCqLoLAykiWgmXnDo9EPnn4RPf5qVTtzgOx66o6/oqhcA5tHtJXpG8pMt3g==", "requires": { "cliui": "^7.0.2", "escalade": "^3.1.1", diff --git a/benchmark/package.json b/benchmark/package.json index 114b9e82d33..3dc768edd38 100644 --- a/benchmark/package.json +++ b/benchmark/package.json @@ -8,9 +8,9 @@ }, "dependencies": { "@node-redis/client": "../packages/client", - "hdr-histogram-js": "2.0.1", - "ioredis": "4.28.1", - "redis-v3": "npm:redis@3.1.2", - "yargs": "17.3.0" + "hdr-histogram-js": "3.0.0", + "ioredis": "5.0.4", + "redis-v3": "npm:redis@4.0.6", + "yargs": "17.4.1" } } diff --git a/package-lock.json b/package-lock.json index 090ee912189..3f1e789db2c 100644 --- a/package-lock.json +++ b/package-lock.json @@ -13,7 +13,7 @@ ], "dependencies": { "@node-redis/bloom": "1.0.1", - "@node-redis/client": "1.0.5", + "@node-redis/client": "1.0.6", "@node-redis/graph": "1.0.0", "@node-redis/json": "1.0.2", "@node-redis/search": "1.0.5", @@ -22,7 +22,7 @@ "devDependencies": { "@tsconfig/node12": "^1.0.9", "gh-pages": "^3.2.3", - "release-it": "^14.13.1", + "release-it": "^14.14.2", "typescript": "^4.6.3" } }, @@ -60,25 +60,25 @@ } }, "node_modules/@babel/core": { - "version": "7.17.8", - "resolved": "https://registry.npmjs.org/@babel/core/-/core-7.17.8.tgz", - "integrity": "sha512-OdQDV/7cRBtJHLSOBqqbYNkOcydOgnX59TZx4puf41fzcVtN3e/4yqY8lMQsK+5X2lJtAdmA+6OHqsj1hBJ4IQ==", + "version": "7.17.9", + "resolved": "https://registry.npmjs.org/@babel/core/-/core-7.17.9.tgz", + "integrity": "sha512-5ug+SfZCpDAkVp9SFIZAzlW18rlzsOcJGaetCjkySnrXXDUw9AR8cDUm1iByTmdWM6yxX6/zycaV76w3YTF2gw==", "dev": true, "dependencies": { "@ampproject/remapping": "^2.1.0", "@babel/code-frame": "^7.16.7", - "@babel/generator": "^7.17.7", + "@babel/generator": "^7.17.9", "@babel/helper-compilation-targets": "^7.17.7", "@babel/helper-module-transforms": "^7.17.7", - "@babel/helpers": "^7.17.8", - "@babel/parser": "^7.17.8", + "@babel/helpers": "^7.17.9", + "@babel/parser": "^7.17.9", "@babel/template": "^7.16.7", - "@babel/traverse": "^7.17.3", + "@babel/traverse": "^7.17.9", "@babel/types": "^7.17.0", "convert-source-map": "^1.7.0", "debug": "^4.1.0", "gensync": "^1.0.0-beta.2", - "json5": "^2.1.2", + "json5": "^2.2.1", "semver": "^6.3.0" }, "engines": { @@ -90,9 +90,9 @@ } }, "node_modules/@babel/generator": { - "version": "7.17.7", - "resolved": "https://registry.npmjs.org/@babel/generator/-/generator-7.17.7.tgz", - "integrity": "sha512-oLcVCTeIFadUoArDTwpluncplrYBmTCCZZgXCbgNGvOBBiSDDK3eWO4b/+eOTli5tKv1lg+a5/NAXg+nTcei1w==", + "version": "7.17.9", + "resolved": "https://registry.npmjs.org/@babel/generator/-/generator-7.17.9.tgz", + "integrity": "sha512-rAdDousTwxbIxbz5I7GEQ3lUip+xVCXooZNbsydCWs3xA7ZsYOv+CFRdzGxRX78BmQHu9B1Eso59AOZQOJDEdQ==", "dev": true, "dependencies": { "@babel/types": "^7.17.0", @@ -134,26 +134,13 @@ } }, "node_modules/@babel/helper-function-name": { - "version": "7.16.7", - "resolved": "https://registry.npmjs.org/@babel/helper-function-name/-/helper-function-name-7.16.7.tgz", - "integrity": "sha512-QfDfEnIUyyBSR3HtrtGECuZ6DAyCkYFp7GHl75vFtTnn6pjKeK0T1DB5lLkFvBea8MdaiUABx3osbgLyInoejA==", + "version": "7.17.9", + "resolved": "https://registry.npmjs.org/@babel/helper-function-name/-/helper-function-name-7.17.9.tgz", + "integrity": "sha512-7cRisGlVtiVqZ0MW0/yFB4atgpGLWEHUVYnb448hZK4x+vih0YO5UoS11XIYtZYqHd0dIPMdUSv8q5K4LdMnIg==", "dev": true, "dependencies": { - "@babel/helper-get-function-arity": "^7.16.7", "@babel/template": "^7.16.7", - "@babel/types": "^7.16.7" - }, - "engines": { - "node": ">=6.9.0" - } - }, - "node_modules/@babel/helper-get-function-arity": { - "version": "7.16.7", - "resolved": "https://registry.npmjs.org/@babel/helper-get-function-arity/-/helper-get-function-arity-7.16.7.tgz", - "integrity": "sha512-flc+RLSOBXzNzVhcLu6ujeHUrD6tANAOU5ojrRx/as+tbzf8+stUCj7+IfRRoAbEZqj/ahXEMsjhOhgeZsrnTw==", - "dev": true, - "dependencies": { - "@babel/types": "^7.16.7" + "@babel/types": "^7.17.0" }, "engines": { "node": ">=6.9.0" @@ -245,13 +232,13 @@ } }, "node_modules/@babel/helpers": { - "version": "7.17.8", - "resolved": "https://registry.npmjs.org/@babel/helpers/-/helpers-7.17.8.tgz", - "integrity": "sha512-QcL86FGxpfSJwGtAvv4iG93UL6bmqBdmoVY0CMCU2g+oD2ezQse3PT5Pa+jiD6LJndBQi0EDlpzOWNlLuhz5gw==", + "version": "7.17.9", + "resolved": "https://registry.npmjs.org/@babel/helpers/-/helpers-7.17.9.tgz", + "integrity": "sha512-cPCt915ShDWUEzEp3+UNRktO2n6v49l5RSnG9M5pS24hA+2FAc5si+Pn1i4VVbQQ+jh+bIZhPFQOJOzbrOYY1Q==", "dev": true, "dependencies": { "@babel/template": "^7.16.7", - "@babel/traverse": "^7.17.3", + "@babel/traverse": "^7.17.9", "@babel/types": "^7.17.0" }, "engines": { @@ -259,9 +246,9 @@ } }, "node_modules/@babel/highlight": { - "version": "7.16.10", - "resolved": "https://registry.npmjs.org/@babel/highlight/-/highlight-7.16.10.tgz", - "integrity": "sha512-5FnTQLSLswEj6IkgVw5KusNUUFY9ZGqe/TRFnP/BKYHYgfh7tc+C7mwiy95/yNP7Dh9x580Vv8r7u7ZfTBFxdw==", + "version": "7.17.9", + "resolved": "https://registry.npmjs.org/@babel/highlight/-/highlight-7.17.9.tgz", + "integrity": "sha512-J9PfEKCbFIv2X5bjTMiZu6Vf341N05QIY+d6FvVKynkG1S7G0j3I0QoRtWIrXhZ+/Nlb5Q0MzqL7TokEJ5BNHg==", "dev": true, "dependencies": { "@babel/helper-validator-identifier": "^7.16.7", @@ -335,9 +322,9 @@ } }, "node_modules/@babel/parser": { - "version": "7.17.8", - "resolved": "https://registry.npmjs.org/@babel/parser/-/parser-7.17.8.tgz", - "integrity": "sha512-BoHhDJrJXqcg+ZL16Xv39H9n+AqJ4pcDrQBGZN+wHxIysrLZ3/ECwCBUch/1zUNhnsXULcONU3Ei5Hmkfk6kiQ==", + "version": "7.17.9", + "resolved": "https://registry.npmjs.org/@babel/parser/-/parser-7.17.9.tgz", + "integrity": "sha512-vqUSBLP8dQHFPdPi9bc5GK9vRkYHJ49fsZdtoJ8EQ8ibpwk5rPKfvNIwChB0KVXcIjcepEBBd2VHC5r9Gy8ueg==", "dev": true, "bin": { "parser": "bin/babel-parser.js" @@ -361,18 +348,18 @@ } }, "node_modules/@babel/traverse": { - "version": "7.17.3", - "resolved": "https://registry.npmjs.org/@babel/traverse/-/traverse-7.17.3.tgz", - "integrity": "sha512-5irClVky7TxRWIRtxlh2WPUUOLhcPN06AGgaQSB8AEwuyEBgJVuJ5imdHm5zxk8w0QS5T+tDfnDxAlhWjpb7cw==", + "version": "7.17.9", + "resolved": "https://registry.npmjs.org/@babel/traverse/-/traverse-7.17.9.tgz", + "integrity": "sha512-PQO8sDIJ8SIwipTPiR71kJQCKQYB5NGImbOviK8K+kg5xkNSYXLBupuX9QhatFowrsvo9Hj8WgArg3W7ijNAQw==", "dev": true, "dependencies": { "@babel/code-frame": "^7.16.7", - "@babel/generator": "^7.17.3", + "@babel/generator": "^7.17.9", "@babel/helper-environment-visitor": "^7.16.7", - "@babel/helper-function-name": "^7.16.7", + "@babel/helper-function-name": "^7.17.9", "@babel/helper-hoist-variables": "^7.16.7", "@babel/helper-split-export-declaration": "^7.16.7", - "@babel/parser": "^7.17.3", + "@babel/parser": "^7.17.9", "@babel/types": "^7.17.0", "debug": "^4.1.0", "globals": "^11.1.0" @@ -783,15 +770,12 @@ } }, "node_modules/@sindresorhus/is": { - "version": "4.6.0", - "resolved": "https://registry.npmjs.org/@sindresorhus/is/-/is-4.6.0.tgz", - "integrity": "sha512-t09vSN3MdfsyCHoFcTRCH/iUtG7OJ0CsjzB8cjAmKc/va/kIgeDI/TxsigdncE/4be734m0cvIYwNaV4i2XqAw==", + "version": "0.14.0", + "resolved": "https://registry.npmjs.org/@sindresorhus/is/-/is-0.14.0.tgz", + "integrity": "sha512-9NET910DNaIPngYnLLPeg+Ogzqsi9uM4mSboU5y6p8S5DzMTVEsJZrawi+BoDNUVBa2DhJqQYUFvMDfgU062LQ==", "dev": true, "engines": { - "node": ">=10" - }, - "funding": { - "url": "https://github.com/sindresorhus/is?sponsor=1" + "node": ">=6" } }, "node_modules/@sinonjs/commons": { @@ -804,9 +788,9 @@ } }, "node_modules/@sinonjs/fake-timers": { - "version": "9.1.1", - "resolved": "https://registry.npmjs.org/@sinonjs/fake-timers/-/fake-timers-9.1.1.tgz", - "integrity": "sha512-Wp5vwlZ0lOqpSYGKqr53INws9HLkt6JDc/pDZcPf7bchQnrXJMXPns8CXx0hFikMSGSWfvtvvpb2gtMVfkWagA==", + "version": "9.1.2", + "resolved": "https://registry.npmjs.org/@sinonjs/fake-timers/-/fake-timers-9.1.2.tgz", + "integrity": "sha512-BPS4ynJW/o92PUR4wgriz2Ud5gpST5vz6GQfMixEDK0Z8ZCUv2M7SkBLykH56T++Xs+8ln9zTGbOvNGIe02/jw==", "dev": true, "dependencies": { "@sinonjs/commons": "^1.7.0" @@ -830,15 +814,15 @@ "dev": true }, "node_modules/@szmarczak/http-timer": { - "version": "4.0.6", - "resolved": "https://registry.npmjs.org/@szmarczak/http-timer/-/http-timer-4.0.6.tgz", - "integrity": "sha512-4BAffykYOgO+5nzBWYwE3W90sBgLJoUPRWWcL8wlyiM8IB8ipJz3UMJ9KXQd1RKQXpKp8Tutn80HZtWsu2u76w==", + "version": "1.1.2", + "resolved": "https://registry.npmjs.org/@szmarczak/http-timer/-/http-timer-1.1.2.tgz", + "integrity": "sha512-XIB2XbzHTN6ieIjfIMV9hlVcfPU26s2vafYWQcZHWXHOxiaRZYEDKEwdl129Zyg50+foYV2jCgtrqSA6qNuNSA==", "dev": true, "dependencies": { - "defer-to-connect": "^2.0.0" + "defer-to-connect": "^1.0.1" }, "engines": { - "node": ">=10" + "node": ">=6" } }, "node_modules/@tsconfig/node10": { @@ -865,39 +849,12 @@ "integrity": "sha512-eZxlbI8GZscaGS7kkc/trHTT5xgrjH3/1n2JDwusC9iahPKWMRvRjJSAN5mCXviuTGQ/lHnhvv8Q1YTpnfz9gA==", "dev": true }, - "node_modules/@types/cacheable-request": { - "version": "6.0.2", - "resolved": "https://registry.npmjs.org/@types/cacheable-request/-/cacheable-request-6.0.2.tgz", - "integrity": "sha512-B3xVo+dlKM6nnKTcmm5ZtY/OL8bOAOd2Olee9M1zft65ox50OzjEHW91sDiU9j6cvW8Ejg1/Qkf4xd2kugApUA==", - "dev": true, - "dependencies": { - "@types/http-cache-semantics": "*", - "@types/keyv": "*", - "@types/node": "*", - "@types/responselike": "*" - } - }, - "node_modules/@types/http-cache-semantics": { - "version": "4.0.1", - "resolved": "https://registry.npmjs.org/@types/http-cache-semantics/-/http-cache-semantics-4.0.1.tgz", - "integrity": "sha512-SZs7ekbP8CN0txVG2xVRH6EgKmEm31BOxA07vkFaETzZz1xh+cbt8BcI0slpymvwhx5dlFnQG2rTlPVQn+iRPQ==", - "dev": true - }, "node_modules/@types/json-schema": { "version": "7.0.11", "resolved": "https://registry.npmjs.org/@types/json-schema/-/json-schema-7.0.11.tgz", "integrity": "sha512-wOuvG1SN4Us4rez+tylwwwCV1psiNVOkJeM3AUWUNWg/jDQY2+HE/444y5gc+jBmRqASOm2Oeh5c1axHobwRKQ==", "dev": true }, - "node_modules/@types/keyv": { - "version": "3.1.4", - "resolved": "https://registry.npmjs.org/@types/keyv/-/keyv-3.1.4.tgz", - "integrity": "sha512-BQ5aZNSCpj7D6K2ksrRCTmKRLEpnPvWDiLPfoGyhZ++8YtiK9d/3DBKPJgry359X/P1PfruyYwvnvwFjuEiEIg==", - "dev": true, - "dependencies": { - "@types/node": "*" - } - }, "node_modules/@types/mocha": { "version": "9.1.0", "resolved": "https://registry.npmjs.org/@types/mocha/-/mocha-9.1.0.tgz", @@ -932,15 +889,6 @@ "@types/redis-errors": "*" } }, - "node_modules/@types/responselike": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/@types/responselike/-/responselike-1.0.0.tgz", - "integrity": "sha512-85Y2BjiufFzaMIlvJDvTTB8Fxl2xfLo4HgmHzVBz08w4wDePCTjYw66PdrolO0kzli3yam/YCgRufyo1DdQVTA==", - "dev": true, - "dependencies": { - "@types/node": "*" - } - }, "node_modules/@types/sinon": { "version": "10.0.11", "resolved": "https://registry.npmjs.org/@types/sinon/-/sinon-10.0.11.tgz", @@ -978,14 +926,14 @@ "dev": true }, "node_modules/@typescript-eslint/eslint-plugin": { - "version": "5.17.0", - "resolved": "https://registry.npmjs.org/@typescript-eslint/eslint-plugin/-/eslint-plugin-5.17.0.tgz", - "integrity": "sha512-qVstvQilEd89HJk3qcbKt/zZrfBZ+9h2ynpAGlWjWiizA7m/MtLT9RoX6gjtpE500vfIg8jogAkDzdCxbsFASQ==", + "version": "5.19.0", + "resolved": "https://registry.npmjs.org/@typescript-eslint/eslint-plugin/-/eslint-plugin-5.19.0.tgz", + "integrity": "sha512-w59GpFqDYGnWFim9p6TGJz7a3qWeENJuAKCqjGSx+Hq/bwq3RZwXYqy98KIfN85yDqz9mq6QXiY5h0FjGQLyEg==", "dev": true, "dependencies": { - "@typescript-eslint/scope-manager": "5.17.0", - "@typescript-eslint/type-utils": "5.17.0", - "@typescript-eslint/utils": "5.17.0", + "@typescript-eslint/scope-manager": "5.19.0", + "@typescript-eslint/type-utils": "5.19.0", + "@typescript-eslint/utils": "5.19.0", "debug": "^4.3.2", "functional-red-black-tree": "^1.0.1", "ignore": "^5.1.8", @@ -1011,9 +959,9 @@ } }, "node_modules/@typescript-eslint/eslint-plugin/node_modules/semver": { - "version": "7.3.5", - "resolved": "https://registry.npmjs.org/semver/-/semver-7.3.5.tgz", - "integrity": "sha512-PoeGJYh8HK4BTO/a9Tf6ZG3veo/A7ZVsYrSA6J8ny9nb3B1VrpkuN+z9OE5wfE5p6H4LchYZsegiQgbJD94ZFQ==", + "version": "7.3.7", + "resolved": "https://registry.npmjs.org/semver/-/semver-7.3.7.tgz", + "integrity": "sha512-QlYTucUYOews+WeEujDoEGziz4K6c47V/Bd+LjSSYcA94p+DmINdf7ncaUinThfvZyu13lN9OY1XDxt8C0Tw0g==", "dev": true, "dependencies": { "lru-cache": "^6.0.0" @@ -1026,14 +974,14 @@ } }, "node_modules/@typescript-eslint/parser": { - "version": "5.17.0", - "resolved": "https://registry.npmjs.org/@typescript-eslint/parser/-/parser-5.17.0.tgz", - "integrity": "sha512-aRzW9Jg5Rlj2t2/crzhA2f23SIYFlF9mchGudyP0uiD6SenIxzKoLjwzHbafgHn39dNV/TV7xwQkLfFTZlJ4ig==", + "version": "5.19.0", + "resolved": "https://registry.npmjs.org/@typescript-eslint/parser/-/parser-5.19.0.tgz", + "integrity": "sha512-yhktJjMCJX8BSBczh1F/uY8wGRYrBeyn84kH6oyqdIJwTGKmzX5Qiq49LRQ0Jh0LXnWijEziSo6BRqny8nqLVQ==", "dev": true, "dependencies": { - "@typescript-eslint/scope-manager": "5.17.0", - "@typescript-eslint/types": "5.17.0", - "@typescript-eslint/typescript-estree": "5.17.0", + "@typescript-eslint/scope-manager": "5.19.0", + "@typescript-eslint/types": "5.19.0", + "@typescript-eslint/typescript-estree": "5.19.0", "debug": "^4.3.2" }, "engines": { @@ -1053,13 +1001,13 @@ } }, "node_modules/@typescript-eslint/scope-manager": { - "version": "5.17.0", - "resolved": "https://registry.npmjs.org/@typescript-eslint/scope-manager/-/scope-manager-5.17.0.tgz", - "integrity": "sha512-062iCYQF/doQ9T2WWfJohQKKN1zmmXVfAcS3xaiialiw8ZUGy05Em6QVNYJGO34/sU1a7a+90U3dUNfqUDHr3w==", + "version": "5.19.0", + "resolved": "https://registry.npmjs.org/@typescript-eslint/scope-manager/-/scope-manager-5.19.0.tgz", + "integrity": "sha512-Fz+VrjLmwq5fbQn5W7cIJZ066HxLMKvDEmf4eu1tZ8O956aoX45jAuBB76miAECMTODyUxH61AQM7q4/GOMQ5g==", "dev": true, "dependencies": { - "@typescript-eslint/types": "5.17.0", - "@typescript-eslint/visitor-keys": "5.17.0" + "@typescript-eslint/types": "5.19.0", + "@typescript-eslint/visitor-keys": "5.19.0" }, "engines": { "node": "^12.22.0 || ^14.17.0 || >=16.0.0" @@ -1070,12 +1018,12 @@ } }, "node_modules/@typescript-eslint/type-utils": { - "version": "5.17.0", - "resolved": "https://registry.npmjs.org/@typescript-eslint/type-utils/-/type-utils-5.17.0.tgz", - "integrity": "sha512-3hU0RynUIlEuqMJA7dragb0/75gZmwNwFf/QJokWzPehTZousP/MNifVSgjxNcDCkM5HI2K22TjQWUmmHUINSg==", + "version": "5.19.0", + "resolved": "https://registry.npmjs.org/@typescript-eslint/type-utils/-/type-utils-5.19.0.tgz", + "integrity": "sha512-O6XQ4RI4rQcBGshTQAYBUIGsKqrKeuIOz9v8bckXZnSeXjn/1+BDZndHLe10UplQeJLXDNbaZYrAytKNQO2T4Q==", "dev": true, "dependencies": { - "@typescript-eslint/utils": "5.17.0", + "@typescript-eslint/utils": "5.19.0", "debug": "^4.3.2", "tsutils": "^3.21.0" }, @@ -1096,9 +1044,9 @@ } }, "node_modules/@typescript-eslint/types": { - "version": "5.17.0", - "resolved": "https://registry.npmjs.org/@typescript-eslint/types/-/types-5.17.0.tgz", - "integrity": "sha512-AgQ4rWzmCxOZLioFEjlzOI3Ch8giDWx8aUDxyNw9iOeCvD3GEYAB7dxWGQy4T/rPVe8iPmu73jPHuaSqcjKvxw==", + "version": "5.19.0", + "resolved": "https://registry.npmjs.org/@typescript-eslint/types/-/types-5.19.0.tgz", + "integrity": "sha512-zR1ithF4Iyq1wLwkDcT+qFnhs8L5VUtjgac212ftiOP/ZZUOCuuF2DeGiZZGQXGoHA50OreZqLH5NjDcDqn34w==", "dev": true, "engines": { "node": "^12.22.0 || ^14.17.0 || >=16.0.0" @@ -1109,13 +1057,13 @@ } }, "node_modules/@typescript-eslint/typescript-estree": { - "version": "5.17.0", - "resolved": "https://registry.npmjs.org/@typescript-eslint/typescript-estree/-/typescript-estree-5.17.0.tgz", - "integrity": "sha512-X1gtjEcmM7Je+qJRhq7ZAAaNXYhTgqMkR10euC4Si6PIjb+kwEQHSxGazXUQXFyqfEXdkGf6JijUu5R0uceQzg==", + "version": "5.19.0", + "resolved": "https://registry.npmjs.org/@typescript-eslint/typescript-estree/-/typescript-estree-5.19.0.tgz", + "integrity": "sha512-dRPuD4ocXdaE1BM/dNR21elSEUPKaWgowCA0bqJ6YbYkvtrPVEvZ+zqcX5a8ECYn3q5iBSSUcBBD42ubaOp0Hw==", "dev": true, "dependencies": { - "@typescript-eslint/types": "5.17.0", - "@typescript-eslint/visitor-keys": "5.17.0", + "@typescript-eslint/types": "5.19.0", + "@typescript-eslint/visitor-keys": "5.19.0", "debug": "^4.3.2", "globby": "^11.0.4", "is-glob": "^4.0.3", @@ -1165,9 +1113,9 @@ } }, "node_modules/@typescript-eslint/typescript-estree/node_modules/semver": { - "version": "7.3.5", - "resolved": "https://registry.npmjs.org/semver/-/semver-7.3.5.tgz", - "integrity": "sha512-PoeGJYh8HK4BTO/a9Tf6ZG3veo/A7ZVsYrSA6J8ny9nb3B1VrpkuN+z9OE5wfE5p6H4LchYZsegiQgbJD94ZFQ==", + "version": "7.3.7", + "resolved": "https://registry.npmjs.org/semver/-/semver-7.3.7.tgz", + "integrity": "sha512-QlYTucUYOews+WeEujDoEGziz4K6c47V/Bd+LjSSYcA94p+DmINdf7ncaUinThfvZyu13lN9OY1XDxt8C0Tw0g==", "dev": true, "dependencies": { "lru-cache": "^6.0.0" @@ -1180,15 +1128,15 @@ } }, "node_modules/@typescript-eslint/utils": { - "version": "5.17.0", - "resolved": "https://registry.npmjs.org/@typescript-eslint/utils/-/utils-5.17.0.tgz", - "integrity": "sha512-DVvndq1QoxQH+hFv+MUQHrrWZ7gQ5KcJzyjhzcqB1Y2Xes1UQQkTRPUfRpqhS8mhTWsSb2+iyvDW1Lef5DD7vA==", + "version": "5.19.0", + "resolved": "https://registry.npmjs.org/@typescript-eslint/utils/-/utils-5.19.0.tgz", + "integrity": "sha512-ZuEckdupXpXamKvFz/Ql8YnePh2ZWcwz7APICzJL985Rp5C2AYcHO62oJzIqNhAMtMK6XvrlBTZeNG8n7gS3lQ==", "dev": true, "dependencies": { "@types/json-schema": "^7.0.9", - "@typescript-eslint/scope-manager": "5.17.0", - "@typescript-eslint/types": "5.17.0", - "@typescript-eslint/typescript-estree": "5.17.0", + "@typescript-eslint/scope-manager": "5.19.0", + "@typescript-eslint/types": "5.19.0", + "@typescript-eslint/typescript-estree": "5.19.0", "eslint-scope": "^5.1.1", "eslint-utils": "^3.0.0" }, @@ -1204,12 +1152,12 @@ } }, "node_modules/@typescript-eslint/visitor-keys": { - "version": "5.17.0", - "resolved": "https://registry.npmjs.org/@typescript-eslint/visitor-keys/-/visitor-keys-5.17.0.tgz", - "integrity": "sha512-6K/zlc4OfCagUu7Am/BD5k8PSWQOgh34Nrv9Rxe2tBzlJ7uOeJ/h7ugCGDCeEZHT6k2CJBhbk9IsbkPI0uvUkA==", + "version": "5.19.0", + "resolved": "https://registry.npmjs.org/@typescript-eslint/visitor-keys/-/visitor-keys-5.19.0.tgz", + "integrity": "sha512-Ym7zZoMDZcAKWsULi2s7UMLREdVQdScPQ/fKWMYefarCztWlHPFVJo8racf8R0Gc8FAEJ2eD4of8As1oFtnQlQ==", "dev": true, "dependencies": { - "@typescript-eslint/types": "5.17.0", + "@typescript-eslint/types": "5.19.0", "eslint-visitor-keys": "^3.0.0" }, "engines": { @@ -1406,6 +1354,25 @@ "node": ">=0.10.0" } }, + "node_modules/array.prototype.map": { + "version": "1.0.4", + "resolved": "https://registry.npmjs.org/array.prototype.map/-/array.prototype.map-1.0.4.tgz", + "integrity": "sha512-Qds9QnX7A0qISY7JT5WuJO0NJPE9CMlC6JzHQfhpqAAQQzufVRoeH7EzUY5GcPTx72voG8LV/5eo+b8Qi8hmhA==", + "dev": true, + "dependencies": { + "call-bind": "^1.0.2", + "define-properties": "^1.1.3", + "es-abstract": "^1.19.0", + "es-array-method-boxes-properly": "^1.0.0", + "is-string": "^1.0.7" + }, + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, "node_modules/async": { "version": "2.6.3", "resolved": "https://registry.npmjs.org/async/-/async-2.6.3.tgz", @@ -1603,28 +1570,19 @@ "integrity": "sha512-E+XQCRwSbaaiChtv6k6Dwgc+bx+Bs6vuKJHHl5kox/BaKbhiXzqQOwK4cO22yElGp2OCmjwVhT3HmxgyPGnJfQ==", "dev": true }, - "node_modules/cacheable-lookup": { - "version": "5.0.4", - "resolved": "https://registry.npmjs.org/cacheable-lookup/-/cacheable-lookup-5.0.4.tgz", - "integrity": "sha512-2/kNscPhpcxrOigMZzbiWF7dz8ilhb/nIHU3EyZiXWXpeq/au8qJ8VhdftMkty3n7Gj6HIGalQG8oiBNB3AJgA==", - "dev": true, - "engines": { - "node": ">=10.6.0" - } - }, "node_modules/cacheable-request": { - "version": "7.0.2", - "resolved": "https://registry.npmjs.org/cacheable-request/-/cacheable-request-7.0.2.tgz", - "integrity": "sha512-pouW8/FmiPQbuGpkXQ9BAPv/Mo5xDGANgSNXzTzJ8DrKGuXOssM4wIQRjfanNRh3Yu5cfYPvcorqbhg2KIJtew==", + "version": "6.1.0", + "resolved": "https://registry.npmjs.org/cacheable-request/-/cacheable-request-6.1.0.tgz", + "integrity": "sha512-Oj3cAGPCqOZX7Rz64Uny2GYAZNliQSqfbePrgAQ1wKAihYmCUnraBtJtKcGR4xz7wF+LoJC+ssFZvv5BgF9Igg==", "dev": true, "dependencies": { "clone-response": "^1.0.2", "get-stream": "^5.1.0", "http-cache-semantics": "^4.0.0", - "keyv": "^4.0.0", + "keyv": "^3.0.0", "lowercase-keys": "^2.0.0", - "normalize-url": "^6.0.1", - "responselike": "^2.0.0" + "normalize-url": "^4.1.0", + "responselike": "^1.0.2" }, "engines": { "node": ">=8" @@ -1645,6 +1603,15 @@ "url": "https://github.com/sponsors/sindresorhus" } }, + "node_modules/cacheable-request/node_modules/lowercase-keys": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/lowercase-keys/-/lowercase-keys-2.0.0.tgz", + "integrity": "sha512-tqNXrS78oMOE73NMxK4EMLQsQowWf8jKooH9g7xPavRT706R6bkQJ6DY2Te7QukaZsulxa30wQ7bk0pm4XiHmA==", + "dev": true, + "engines": { + "node": ">=8" + } + }, "node_modules/caching-transform": { "version": "4.0.0", "resolved": "https://registry.npmjs.org/caching-transform/-/caching-transform-4.0.0.tgz", @@ -1695,9 +1662,9 @@ } }, "node_modules/caniuse-lite": { - "version": "1.0.30001323", - "resolved": "https://registry.npmjs.org/caniuse-lite/-/caniuse-lite-1.0.30001323.tgz", - "integrity": "sha512-e4BF2RlCVELKx8+RmklSEIVub1TWrmdhvA5kEUueummz1XyySW0DVk+3x9HyhU9MuWTa2BhqLgEuEmUwASAdCA==", + "version": "1.0.30001331", + "resolved": "https://registry.npmjs.org/caniuse-lite/-/caniuse-lite-1.0.30001331.tgz", + "integrity": "sha512-Y1xk6paHpUXKP/P6YjQv1xqyTbgAP05ycHBcRdQjTcyXlWol868sJJPlmk5ylOekw2BrucWes5jk+LvVd7WZ5Q==", "dev": true, "funding": [ { @@ -1982,9 +1949,9 @@ } }, "node_modules/debug": { - "version": "4.3.3", - "resolved": "https://registry.npmjs.org/debug/-/debug-4.3.3.tgz", - "integrity": "sha512-/zxw5+vh1Tfv+4Qn7a5nsbcJKPaSvCDhojn6FEl9vupwK2VCSDtEiEtqr8DFtzYFOdz63LBkxec7DYuc2jon6Q==", + "version": "4.3.4", + "resolved": "https://registry.npmjs.org/debug/-/debug-4.3.4.tgz", + "integrity": "sha512-PRWFHuSU3eDtQJPvnNY7Jcket1j0t5OuOsFzPPzsekD52Zl8qUfFIPEiswXqIvHWGVHOgX+7G/vCNNhehwxfkQ==", "dev": true, "dependencies": { "ms": "2.1.2" @@ -2017,30 +1984,15 @@ } }, "node_modules/decompress-response": { - "version": "6.0.0", - "resolved": "https://registry.npmjs.org/decompress-response/-/decompress-response-6.0.0.tgz", - "integrity": "sha512-aW35yZM6Bb/4oJlZncMH2LCoZtJXTRxES17vE3hoRiowU2kWHaJKFkSBDnDR+cm9J+9QhXmREyIfv0pji9ejCQ==", + "version": "3.3.0", + "resolved": "https://registry.npmjs.org/decompress-response/-/decompress-response-3.3.0.tgz", + "integrity": "sha1-gKTdMjdIOEv6JICDYirt7Jgq3/M=", "dev": true, "dependencies": { - "mimic-response": "^3.1.0" - }, - "engines": { - "node": ">=10" + "mimic-response": "^1.0.0" }, - "funding": { - "url": "https://github.com/sponsors/sindresorhus" - } - }, - "node_modules/decompress-response/node_modules/mimic-response": { - "version": "3.1.0", - "resolved": "https://registry.npmjs.org/mimic-response/-/mimic-response-3.1.0.tgz", - "integrity": "sha512-z0yWI+4FDrrweS8Zmt4Ej5HdJmky15+L2e6Wgn3+iK5fWzb6T3fhNFq2+MeTRb064c6Wr4N/wv0DzQTjNzHNGQ==", - "dev": true, "engines": { - "node": ">=10" - }, - "funding": { - "url": "https://github.com/sponsors/sindresorhus" + "node": ">=4" } }, "node_modules/deep-extend": { @@ -2080,12 +2032,21 @@ } }, "node_modules/defer-to-connect": { - "version": "2.0.1", - "resolved": "https://registry.npmjs.org/defer-to-connect/-/defer-to-connect-2.0.1.tgz", - "integrity": "sha512-4tvttepXG1VaYGrRibk5EwJd1t4udunSOVMdLSAL6mId1ix438oPwPZMALY41FCijukO1L0twNcGsdzS7dHgDg==", + "version": "1.1.3", + "resolved": "https://registry.npmjs.org/defer-to-connect/-/defer-to-connect-1.1.3.tgz", + "integrity": "sha512-0ISdNousHvZT2EiFlZeZAHBUvSxmKswVCEf8hW7KWgG4a8MVEu/3Vb6uWYozkjylyCxe0JBIiRB1jV45S70WVQ==", + "dev": true + }, + "node_modules/define-properties": { + "version": "1.1.3", + "resolved": "https://registry.npmjs.org/define-properties/-/define-properties-1.1.3.tgz", + "integrity": "sha512-3MqfYKj2lLzdMSf8ZIZE/V+Zuy+BgD6f164e8K2w7dgnpKArBDerGYpM46IYYcjnkdPNMjPk9A6VFB8+3SKlXQ==", "dev": true, + "dependencies": { + "object-keys": "^1.0.12" + }, "engines": { - "node": ">=10" + "node": ">= 0.4" } }, "node_modules/delayed-stream": { @@ -2155,9 +2116,9 @@ "dev": true }, "node_modules/electron-to-chromium": { - "version": "1.4.103", - "resolved": "https://registry.npmjs.org/electron-to-chromium/-/electron-to-chromium-1.4.103.tgz", - "integrity": "sha512-c/uKWR1Z/W30Wy/sx3dkZoj4BijbXX85QKWu9jJfjho3LBAXNEGAEW3oWiGb+dotA6C6BzCTxL2/aLes7jlUeg==", + "version": "1.4.107", + "resolved": "https://registry.npmjs.org/electron-to-chromium/-/electron-to-chromium-1.4.107.tgz", + "integrity": "sha512-Huen6taaVrUrSy8o7mGStByba8PfOWWluHNxSHGBrCgEdFVLtvdQDBr9LBCF9Uci8SYxh28QNNMO0oC17wbGAg==", "dev": true }, "node_modules/email-addresses": { @@ -2190,6 +2151,82 @@ "is-arrayish": "^0.2.1" } }, + "node_modules/es-abstract": { + "version": "1.19.4", + "resolved": "https://registry.npmjs.org/es-abstract/-/es-abstract-1.19.4.tgz", + "integrity": "sha512-flV8e5g9/xulChMG48Fygk1ptpo4lQRJ0eJYtxJFgi7pklLx7EFcOJ34jnvr8pbWlaFN/AT1cZpe0hiFel9Hqg==", + "dev": true, + "dependencies": { + "call-bind": "^1.0.2", + "es-to-primitive": "^1.2.1", + "function-bind": "^1.1.1", + "get-intrinsic": "^1.1.1", + "get-symbol-description": "^1.0.0", + "has": "^1.0.3", + "has-symbols": "^1.0.3", + "internal-slot": "^1.0.3", + "is-callable": "^1.2.4", + "is-negative-zero": "^2.0.2", + "is-regex": "^1.1.4", + "is-shared-array-buffer": "^1.0.2", + "is-string": "^1.0.7", + "is-weakref": "^1.0.2", + "object-inspect": "^1.12.0", + "object-keys": "^1.1.1", + "object.assign": "^4.1.2", + "string.prototype.trimend": "^1.0.4", + "string.prototype.trimstart": "^1.0.4", + "unbox-primitive": "^1.0.1" + }, + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/es-array-method-boxes-properly": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/es-array-method-boxes-properly/-/es-array-method-boxes-properly-1.0.0.tgz", + "integrity": "sha512-wd6JXUmyHmt8T5a2xreUwKcGPq6f1f+WwIJkijUqiGcJz1qqnZgP6XIK+QyIWU5lT7imeNxUll48bziG+TSYcA==", + "dev": true + }, + "node_modules/es-get-iterator": { + "version": "1.1.2", + "resolved": "https://registry.npmjs.org/es-get-iterator/-/es-get-iterator-1.1.2.tgz", + "integrity": "sha512-+DTO8GYwbMCwbywjimwZMHp8AuYXOS2JZFWoi2AlPOS3ebnII9w/NLpNZtA7A0YLaVDw+O7KFCeoIV7OPvM7hQ==", + "dev": true, + "dependencies": { + "call-bind": "^1.0.2", + "get-intrinsic": "^1.1.0", + "has-symbols": "^1.0.1", + "is-arguments": "^1.1.0", + "is-map": "^2.0.2", + "is-set": "^2.0.2", + "is-string": "^1.0.5", + "isarray": "^2.0.5" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/es-to-primitive": { + "version": "1.2.1", + "resolved": "https://registry.npmjs.org/es-to-primitive/-/es-to-primitive-1.2.1.tgz", + "integrity": "sha512-QCOllgZJtaUo9miYBcLChTUaHNjJF3PYs1VidD7AwiEj1kYxKeQTctLAezAOH5ZKRH0g2IgPn6KwB4IT8iRpvA==", + "dev": true, + "dependencies": { + "is-callable": "^1.1.4", + "is-date-object": "^1.0.1", + "is-symbol": "^1.0.2" + }, + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, "node_modules/es6-error": { "version": "4.1.1", "resolved": "https://registry.npmjs.org/es6-error/-/es6-error-4.1.1.tgz", @@ -2224,9 +2261,9 @@ } }, "node_modules/eslint": { - "version": "8.12.0", - "resolved": "https://registry.npmjs.org/eslint/-/eslint-8.12.0.tgz", - "integrity": "sha512-it1oBL9alZg1S8UycLm5YDMAkIhtH6FtAzuZs6YvoGVldWjbS08BkAdb/ymP9LlAyq8koANu32U7Ib/w+UNh8Q==", + "version": "8.13.0", + "resolved": "https://registry.npmjs.org/eslint/-/eslint-8.13.0.tgz", + "integrity": "sha512-D+Xei61eInqauAyTJ6C0q6x9mx7kTUC1KZ0m0LSEexR0V+e94K12LmWX076ZIsldwfQ2RONdaJe0re0TRGQbRQ==", "dev": true, "dependencies": { "@eslint/eslintrc": "^1.2.1", @@ -2835,6 +2872,22 @@ "url": "https://github.com/sponsors/sindresorhus" } }, + "node_modules/get-symbol-description": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/get-symbol-description/-/get-symbol-description-1.0.0.tgz", + "integrity": "sha512-2EmdH1YvIQiZpltCNgkuiUnyukzxM/R6NDJX31Ke3BG1Nq5b0S2PhX59UKi9vZpPDQVdqn+1IcaAwnzTT5vCjw==", + "dev": true, + "dependencies": { + "call-bind": "^1.0.2", + "get-intrinsic": "^1.1.1" + }, + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, "node_modules/gh-pages": { "version": "3.2.3", "resolved": "https://registry.npmjs.org/gh-pages/-/gh-pages-3.2.3.tgz", @@ -2967,34 +3020,43 @@ } }, "node_modules/got": { - "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", - "@szmarczak/http-timer": "^4.0.5", - "@types/cacheable-request": "^6.0.1", - "@types/responselike": "^1.0.0", - "cacheable-lookup": "^5.0.3", - "cacheable-request": "^7.0.2", - "decompress-response": "^6.0.0", - "http2-wrapper": "^1.0.0-beta.5.2", - "lowercase-keys": "^2.0.0", - "p-cancelable": "^2.0.0", - "responselike": "^2.0.0" + "version": "9.6.0", + "resolved": "https://registry.npmjs.org/got/-/got-9.6.0.tgz", + "integrity": "sha512-R7eWptXuGYxwijs0eV+v3o6+XH1IqVK8dJOEecQfTmkncw9AV4dcw/Dhxi8MdlqPthxxpZyizMzyg8RTmEsG+Q==", + "dev": true, + "dependencies": { + "@sindresorhus/is": "^0.14.0", + "@szmarczak/http-timer": "^1.1.2", + "cacheable-request": "^6.0.0", + "decompress-response": "^3.3.0", + "duplexer3": "^0.1.4", + "get-stream": "^4.1.0", + "lowercase-keys": "^1.0.1", + "mimic-response": "^1.0.1", + "p-cancelable": "^1.0.0", + "to-readable-stream": "^1.0.0", + "url-parse-lax": "^3.0.0" }, "engines": { - "node": ">=10.19.0" + "node": ">=8.6" + } + }, + "node_modules/got/node_modules/get-stream": { + "version": "4.1.0", + "resolved": "https://registry.npmjs.org/get-stream/-/get-stream-4.1.0.tgz", + "integrity": "sha512-GMat4EJ5161kIy2HevLlr4luNjBgvmj413KaQA7jt4V8B4RDsfpHk7WQ9GVqfYyyx8OS/L66Kox+rJRNklLK7w==", + "dev": true, + "dependencies": { + "pump": "^3.0.0" }, - "funding": { - "url": "https://github.com/sindresorhus/got?sponsor=1" + "engines": { + "node": ">=6" } }, "node_modules/graceful-fs": { - "version": "4.2.9", - "resolved": "https://registry.npmjs.org/graceful-fs/-/graceful-fs-4.2.9.tgz", - "integrity": "sha512-NtNxqUcXgpW2iMrfqSfR73Glt39K+BLwWsPs94yR63v45T0Wbej7eRmL5cWfwEgqXnmjQp3zaJTshdRW/qC2ZQ==", + "version": "4.2.10", + "resolved": "https://registry.npmjs.org/graceful-fs/-/graceful-fs-4.2.10.tgz", + "integrity": "sha512-9ByhssR2fPVsNZj478qUUbKfmL0+t5BDVyjShtyZZLiK7ZDAArFFfopyOTj0M05wE2tJPisA4iTnnXl2YoPvOA==", "dev": true }, "node_modules/growl": { @@ -3018,6 +3080,15 @@ "node": ">= 0.4.0" } }, + "node_modules/has-bigints": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/has-bigints/-/has-bigints-1.0.1.tgz", + "integrity": "sha512-LSBS2LjbNBTf6287JEbEzvJgftkF5qFkmCo9hDRpAzKhUOlJ+hx8dd4USs00SgsUNwc4617J9ki5YtEClM2ffA==", + "dev": true, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, "node_modules/has-flag": { "version": "4.0.0", "resolved": "https://registry.npmjs.org/has-flag/-/has-flag-4.0.0.tgz", @@ -3039,6 +3110,21 @@ "url": "https://github.com/sponsors/ljharb" } }, + "node_modules/has-tostringtag": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/has-tostringtag/-/has-tostringtag-1.0.0.tgz", + "integrity": "sha512-kFjcSNhnlGV1kyoGk7OXKSawH5JOb/LzUc5w9B02hOTO0dfFRjbHQKvg1d6cf3HbeUmtU9VbbV3qzZ2Teh97WQ==", + "dev": true, + "dependencies": { + "has-symbols": "^1.0.2" + }, + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, "node_modules/has-yarn": { "version": "2.1.0", "resolved": "https://registry.npmjs.org/has-yarn/-/has-yarn-2.1.0.tgz", @@ -3094,19 +3180,6 @@ "integrity": "sha512-carPklcUh7ROWRK7Cv27RPtdhYhUsela/ue5/jKzjegVvXDqM2ILE9Q2BGn9JZJh1g87cp56su/FgQSzcWS8cQ==", "dev": true }, - "node_modules/http2-wrapper": { - "version": "1.0.3", - "resolved": "https://registry.npmjs.org/http2-wrapper/-/http2-wrapper-1.0.3.tgz", - "integrity": "sha512-V+23sDMr12Wnz7iTcDeJr3O6AIxlnvT/bmaAAAP/Xda35C90p9599p0F1eHR/N1KILWSoWVAiOMFjBBXaXSMxg==", - "dev": true, - "dependencies": { - "quick-lru": "^5.1.1", - "resolve-alpn": "^1.0.0" - }, - "engines": { - "node": ">=10.19.0" - } - }, "node_modules/human-signals": { "version": "2.1.0", "resolved": "https://registry.npmjs.org/human-signals/-/human-signals-2.1.0.tgz", @@ -3283,6 +3356,20 @@ "node": ">=8.0.0" } }, + "node_modules/internal-slot": { + "version": "1.0.3", + "resolved": "https://registry.npmjs.org/internal-slot/-/internal-slot-1.0.3.tgz", + "integrity": "sha512-O0DB1JC/sPyZl7cIo78n5dR7eUSwwpYPiXRhTzNxZVAMUuB8vlnRFyLxdrVToks6XPLVnFfbzaVd5WLjhgg+vA==", + "dev": true, + "dependencies": { + "get-intrinsic": "^1.1.0", + "has": "^1.0.3", + "side-channel": "^1.0.4" + }, + "engines": { + "node": ">= 0.4" + } + }, "node_modules/interpret": { "version": "1.4.0", "resolved": "https://registry.npmjs.org/interpret/-/interpret-1.4.0.tgz", @@ -3292,12 +3379,40 @@ "node": ">= 0.10" } }, + "node_modules/is-arguments": { + "version": "1.1.1", + "resolved": "https://registry.npmjs.org/is-arguments/-/is-arguments-1.1.1.tgz", + "integrity": "sha512-8Q7EARjzEnKpt/PCD7e1cgUS0a6X8u5tdSiMqXhojOdoV9TsMsiO+9VLC5vAmO8N7/GmXn7yjR8qnA6bVAEzfA==", + "dev": true, + "dependencies": { + "call-bind": "^1.0.2", + "has-tostringtag": "^1.0.0" + }, + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, "node_modules/is-arrayish": { "version": "0.2.1", "resolved": "https://registry.npmjs.org/is-arrayish/-/is-arrayish-0.2.1.tgz", "integrity": "sha1-d8mYQFJ6qOyxqLppe4BkWnqSap0=", "dev": true }, + "node_modules/is-bigint": { + "version": "1.0.4", + "resolved": "https://registry.npmjs.org/is-bigint/-/is-bigint-1.0.4.tgz", + "integrity": "sha512-zB9CruMamjym81i2JZ3UMn54PKGsQzsJeo6xvN3HJJ4CAsQNB6iRutp2To77OfCNuoxspsIhzaPoO1zyCEhFOg==", + "dev": true, + "dependencies": { + "has-bigints": "^1.0.1" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, "node_modules/is-binary-path": { "version": "2.1.0", "resolved": "https://registry.npmjs.org/is-binary-path/-/is-binary-path-2.1.0.tgz", @@ -3310,16 +3425,44 @@ "node": ">=8" } }, - "node_modules/is-ci": { - "version": "3.0.1", - "resolved": "https://registry.npmjs.org/is-ci/-/is-ci-3.0.1.tgz", - "integrity": "sha512-ZYvCgrefwqoQ6yTyYUbQu64HsITZ3NfKX1lzaEYdkTDcfKzzCI/wthRRYKkdjHKFVgNiXKAKm65Zo1pk2as/QQ==", + "node_modules/is-boolean-object": { + "version": "1.1.2", + "resolved": "https://registry.npmjs.org/is-boolean-object/-/is-boolean-object-1.1.2.tgz", + "integrity": "sha512-gDYaKHJmnj4aWxyj6YHyXVpdQawtVLHU5cb+eztPGczf6cjuTdwve5ZIEfgXqH4e57An1D1AKf8CZ3kYrQRqYA==", "dev": true, "dependencies": { - "ci-info": "^3.2.0" + "call-bind": "^1.0.2", + "has-tostringtag": "^1.0.0" }, - "bin": { - "is-ci": "bin.js" + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/is-callable": { + "version": "1.2.4", + "resolved": "https://registry.npmjs.org/is-callable/-/is-callable-1.2.4.tgz", + "integrity": "sha512-nsuwtxZfMX67Oryl9LCQ+upnC0Z0BgpwntpS89m1H/TLF0zNfzfLMV/9Wa/6MZsj0acpEjAO0KF1xT6ZdLl95w==", + "dev": true, + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/is-ci": { + "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.2.0" + }, + "bin": { + "is-ci": "bin.js" } }, "node_modules/is-core-module": { @@ -3334,6 +3477,21 @@ "url": "https://github.com/sponsors/ljharb" } }, + "node_modules/is-date-object": { + "version": "1.0.5", + "resolved": "https://registry.npmjs.org/is-date-object/-/is-date-object-1.0.5.tgz", + "integrity": "sha512-9YQaSxsAiSwcvS33MBk3wTCVnWK+HhF8VZR2jRxehM16QcVOdHqPn4VPHmRK4lSr38n9JriurInLcP90xsYNfQ==", + "dev": true, + "dependencies": { + "has-tostringtag": "^1.0.0" + }, + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, "node_modules/is-docker": { "version": "2.2.1", "resolved": "https://registry.npmjs.org/is-docker/-/is-docker-2.2.1.tgz", @@ -3404,6 +3562,27 @@ "node": ">=8" } }, + "node_modules/is-map": { + "version": "2.0.2", + "resolved": "https://registry.npmjs.org/is-map/-/is-map-2.0.2.tgz", + "integrity": "sha512-cOZFQQozTha1f4MxLFzlgKYPTyj26picdZTx82hbc/Xf4K/tZOOXSCkMvU4pKioRXGDLJRn0GM7Upe7kR721yg==", + "dev": true, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/is-negative-zero": { + "version": "2.0.2", + "resolved": "https://registry.npmjs.org/is-negative-zero/-/is-negative-zero-2.0.2.tgz", + "integrity": "sha512-dqJvarLawXsFbNDeJW7zAz8ItJ9cd28YufuuFzh0G8pNHjJMnY08Dv7sYX2uF5UpQOwieAeOExEYAWWfu7ZZUA==", + "dev": true, + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, "node_modules/is-npm": { "version": "5.0.0", "resolved": "https://registry.npmjs.org/is-npm/-/is-npm-5.0.0.tgz", @@ -3425,6 +3604,21 @@ "node": ">=0.12.0" } }, + "node_modules/is-number-object": { + "version": "1.0.7", + "resolved": "https://registry.npmjs.org/is-number-object/-/is-number-object-1.0.7.tgz", + "integrity": "sha512-k1U0IRzLMo7ZlYIfzRu23Oh6MiIFasgpb9X76eqfFZAqwH44UI4KTBvBYIZ1dSL9ZzChTB9ShHfLkR4pdW5krQ==", + "dev": true, + "dependencies": { + "has-tostringtag": "^1.0.0" + }, + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, "node_modules/is-obj": { "version": "2.0.0", "resolved": "https://registry.npmjs.org/is-obj/-/is-obj-2.0.0.tgz", @@ -3461,6 +3655,43 @@ "node": ">=0.10.0" } }, + "node_modules/is-regex": { + "version": "1.1.4", + "resolved": "https://registry.npmjs.org/is-regex/-/is-regex-1.1.4.tgz", + "integrity": "sha512-kvRdxDsxZjhzUX07ZnLydzS1TU/TJlTUHHY4YLL87e37oUA49DfkLqgy+VjFocowy29cKvcSiu+kIv728jTTVg==", + "dev": true, + "dependencies": { + "call-bind": "^1.0.2", + "has-tostringtag": "^1.0.0" + }, + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/is-set": { + "version": "2.0.2", + "resolved": "https://registry.npmjs.org/is-set/-/is-set-2.0.2.tgz", + "integrity": "sha512-+2cnTEZeY5z/iXGbLhPrOAaK/Mau5k5eXq9j14CpRTftq0pAJu2MwVRSZhyZWBzx3o6X795Lz6Bpb6R0GKf37g==", + "dev": true, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/is-shared-array-buffer": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/is-shared-array-buffer/-/is-shared-array-buffer-1.0.2.tgz", + "integrity": "sha512-sqN2UDu1/0y6uvXyStCOzyhAjCSlHceFoMKJW8W9EU9cvic/QdsZ0kEU93HEy3IUEFZIiH/3w+AH/UQbPHNdhA==", + "dev": true, + "dependencies": { + "call-bind": "^1.0.2" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, "node_modules/is-ssh": { "version": "1.3.3", "resolved": "https://registry.npmjs.org/is-ssh/-/is-ssh-1.3.3.tgz", @@ -3482,6 +3713,36 @@ "url": "https://github.com/sponsors/sindresorhus" } }, + "node_modules/is-string": { + "version": "1.0.7", + "resolved": "https://registry.npmjs.org/is-string/-/is-string-1.0.7.tgz", + "integrity": "sha512-tE2UXzivje6ofPW7l23cjDOMa09gb7xlAqG6jG5ej6uPV32TlWP3NKPigtaGeHNu9fohccRYvIiZMfOOnOYUtg==", + "dev": true, + "dependencies": { + "has-tostringtag": "^1.0.0" + }, + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/is-symbol": { + "version": "1.0.4", + "resolved": "https://registry.npmjs.org/is-symbol/-/is-symbol-1.0.4.tgz", + "integrity": "sha512-C/CPBqKWnvdcxqIARxyOh4v1UUEOCHpgDa0WYgpKDFMszcrPcffg5uhwSgPCLD2WWxmq6isisz87tzT01tuGhg==", + "dev": true, + "dependencies": { + "has-symbols": "^1.0.2" + }, + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, "node_modules/is-typedarray": { "version": "1.0.0", "resolved": "https://registry.npmjs.org/is-typedarray/-/is-typedarray-1.0.0.tgz", @@ -3500,6 +3761,18 @@ "url": "https://github.com/sponsors/sindresorhus" } }, + "node_modules/is-weakref": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/is-weakref/-/is-weakref-1.0.2.tgz", + "integrity": "sha512-qctsuLZmIQ0+vSSMfoVvyFe2+GSEvnmZ2ezTup1SBse9+twCCeial6EEi3Nc2KFcf6+qz2FBPnjXsk8xhKSaPQ==", + "dev": true, + "dependencies": { + "call-bind": "^1.0.2" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, "node_modules/is-windows": { "version": "1.0.2", "resolved": "https://registry.npmjs.org/is-windows/-/is-windows-1.0.2.tgz", @@ -3528,9 +3801,9 @@ "dev": true }, "node_modules/isarray": { - "version": "0.0.1", - "resolved": "https://registry.npmjs.org/isarray/-/isarray-0.0.1.tgz", - "integrity": "sha1-ihis/Kmo9Bd+Cav8YDiTmwXR7t8=", + "version": "2.0.5", + "resolved": "https://registry.npmjs.org/isarray/-/isarray-2.0.5.tgz", + "integrity": "sha512-xHjhDr3cNBK0BzdUJSPXZntQUx/mwMS5Rw4A7lPJ90XGAO6ISP/ePDNuo0vhqOZU+UD5JoodwCAAoZQd3FeAKw==", "dev": true }, "node_modules/isexe": { @@ -3653,6 +3926,28 @@ "node": ">=8" } }, + "node_modules/iterate-iterator": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/iterate-iterator/-/iterate-iterator-1.0.2.tgz", + "integrity": "sha512-t91HubM4ZDQ70M9wqp+pcNpu8OyJ9UAtXntT/Bcsvp5tZMnz9vRa+IunKXeI8AnfZMTv0jNuVEmGeLSMjVvfPw==", + "dev": true, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/iterate-value": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/iterate-value/-/iterate-value-1.0.2.tgz", + "integrity": "sha512-A6fMAio4D2ot2r/TYzr4yUWrmwNdsN5xL7+HUiyACE4DXm+q8HtPcnFTp+NnW3k4N05tZ7FVYFFb2CR13NxyHQ==", + "dev": true, + "dependencies": { + "es-get-iterator": "^1.0.2", + "iterate-iterator": "^1.0.1" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, "node_modules/js-tokens": { "version": "4.0.0", "resolved": "https://registry.npmjs.org/js-tokens/-/js-tokens-4.0.0.tgz", @@ -3684,9 +3979,9 @@ } }, "node_modules/json-buffer": { - "version": "3.0.1", - "resolved": "https://registry.npmjs.org/json-buffer/-/json-buffer-3.0.1.tgz", - "integrity": "sha512-4bV5BfR2mqfQTJm+V5tPPdf+ZpuhiIvTuAB5g8kcrXOZpTT/QwwVRWBywX1ozr6lEuPdbHxwaJlm9G6mI2sfSQ==", + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/json-buffer/-/json-buffer-3.0.0.tgz", + "integrity": "sha1-Wx85evx11ne96Lz8Dkfh+aPZqJg=", "dev": true }, "node_modules/json-parse-even-better-errors": { @@ -3741,12 +4036,12 @@ "dev": true }, "node_modules/keyv": { - "version": "4.1.1", - "resolved": "https://registry.npmjs.org/keyv/-/keyv-4.1.1.tgz", - "integrity": "sha512-tGv1yP6snQVDSM4X6yxrv2zzq/EvpW+oYiUz6aueW1u9CtS8RzUQYxxmFwgZlO2jSgCxQbchhxaqXXp2hnKGpQ==", + "version": "3.1.0", + "resolved": "https://registry.npmjs.org/keyv/-/keyv-3.1.0.tgz", + "integrity": "sha512-9ykJ/46SN/9KPM/sichzQ7OvXyGDYKGTaDlKMGCAlg2UK8KRy4jb0d8sFc+0Tt0YYnThq8X2RZgCg74RPxgcVA==", "dev": true, "dependencies": { - "json-buffer": "3.0.1" + "json-buffer": "3.0.0" } }, "node_modules/latest-version": { @@ -3833,12 +4128,12 @@ } }, "node_modules/lowercase-keys": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/lowercase-keys/-/lowercase-keys-2.0.0.tgz", - "integrity": "sha512-tqNXrS78oMOE73NMxK4EMLQsQowWf8jKooH9g7xPavRT706R6bkQJ6DY2Te7QukaZsulxa30wQ7bk0pm4XiHmA==", + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/lowercase-keys/-/lowercase-keys-1.0.1.tgz", + "integrity": "sha512-G2Lj61tXDnVFFOi8VZds+SoQjtQC3dgokKdDG2mTm1tx4m50NUHBOZSBwQQHyy0V12A0JTG4icfZQH+xPyh8VA==", "dev": true, "engines": { - "node": ">=8" + "node": ">=0.10.0" } }, "node_modules/lru-cache": { @@ -3893,9 +4188,9 @@ "dev": true }, "node_modules/marked": { - "version": "4.0.12", - "resolved": "https://registry.npmjs.org/marked/-/marked-4.0.12.tgz", - "integrity": "sha512-hgibXWrEDNBWgGiK18j/4lkS6ihTe9sxtV4Q1OQppb/0zzyPSzoFANBa5MfsG/zgsWklmNnhm0XACZOH/0HBiQ==", + "version": "4.0.14", + "resolved": "https://registry.npmjs.org/marked/-/marked-4.0.14.tgz", + "integrity": "sha512-HL5sSPE/LP6U9qKgngIIPTthuxC0jrfxpYMZ3LdGDD3vTnLs59m2Z7r6+LNDR3ToqEQdkKd6YaaEfJhodJmijQ==", "dev": true, "bin": { "marked": "bin/marked.js" @@ -3933,21 +4228,21 @@ } }, "node_modules/mime-db": { - "version": "1.51.0", - "resolved": "https://registry.npmjs.org/mime-db/-/mime-db-1.51.0.tgz", - "integrity": "sha512-5y8A56jg7XVQx2mbv1lu49NR4dokRnhZYTtL+KGfaa27uq4pSTXkwQkFJl4pkRMyNFz/EtYDSkiiEHx3F7UN6g==", + "version": "1.52.0", + "resolved": "https://registry.npmjs.org/mime-db/-/mime-db-1.52.0.tgz", + "integrity": "sha512-sPU4uV7dYlvtWJxwwxHD0PuihVNiE7TyAbQ5SWxDCB9mUYvOgroQOwYQQOKPJ8CIbE+1ETVlOoK1UC2nU3gYvg==", "dev": true, "engines": { "node": ">= 0.6" } }, "node_modules/mime-types": { - "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==", + "version": "2.1.35", + "resolved": "https://registry.npmjs.org/mime-types/-/mime-types-2.1.35.tgz", + "integrity": "sha512-ZDY+bPm5zTTF+YpCrAU9nK0UgICYPT0QtT1NZWFv4s++TNkcgVaT0g6+4R2uI4MjQjzysHB1zxuWL50hzaeXiw==", "dev": true, "dependencies": { - "mime-db": "1.51.0" + "mime-db": "1.52.0" }, "engines": { "node": ">= 0.6" @@ -4032,6 +4327,29 @@ "url": "https://opencollective.com/mochajs" } }, + "node_modules/mocha/node_modules/debug": { + "version": "4.3.3", + "resolved": "https://registry.npmjs.org/debug/-/debug-4.3.3.tgz", + "integrity": "sha512-/zxw5+vh1Tfv+4Qn7a5nsbcJKPaSvCDhojn6FEl9vupwK2VCSDtEiEtqr8DFtzYFOdz63LBkxec7DYuc2jon6Q==", + "dev": true, + "dependencies": { + "ms": "2.1.2" + }, + "engines": { + "node": ">=6.0" + }, + "peerDependenciesMeta": { + "supports-color": { + "optional": true + } + } + }, + "node_modules/mocha/node_modules/debug/node_modules/ms": { + "version": "2.1.2", + "resolved": "https://registry.npmjs.org/ms/-/ms-2.1.2.tgz", + "integrity": "sha512-sGkPx+VjMtmA6MX27oA4FBFELFCZZ4S4XqeGOXCv68tT+jb3vk/RyaKWP0PTKyWtmLSM0b+adUTEvbs1PEaH2w==", + "dev": true + }, "node_modules/mocha/node_modules/escape-string-regexp": { "version": "4.0.0", "resolved": "https://registry.npmjs.org/escape-string-regexp/-/escape-string-regexp-4.0.0.tgz", @@ -4274,9 +4592,9 @@ } }, "node_modules/node-releases": { - "version": "2.0.2", - "resolved": "https://registry.npmjs.org/node-releases/-/node-releases-2.0.2.tgz", - "integrity": "sha512-XxYDdcQ6eKqp/YjI+tb2C5WM2LgjnZrfYg4vgQt49EK268b6gYCHsBLrK2qvJo4FmCtqmKezb0WZFK4fkrZNsg==", + "version": "2.0.3", + "resolved": "https://registry.npmjs.org/node-releases/-/node-releases-2.0.3.tgz", + "integrity": "sha512-maHFz6OLqYxz+VQyCAtA3PTX4UP/53pa05fyDNc9CwjvJ0yEh6+xBwKsgCxMNhS8taUKBFYxfuiaD9U/55iFaw==", "dev": true }, "node_modules/normalize-path": { @@ -4289,15 +4607,12 @@ } }, "node_modules/normalize-url": { - "version": "6.1.0", - "resolved": "https://registry.npmjs.org/normalize-url/-/normalize-url-6.1.0.tgz", - "integrity": "sha512-DlL+XwOy3NxAQ8xuC0okPgK46iuVNAK01YN7RueYBqqFeGsBjV9XmCAzAdgt+667bCl5kPh9EqKKDwnaPG1I7A==", + "version": "4.5.1", + "resolved": "https://registry.npmjs.org/normalize-url/-/normalize-url-4.5.1.tgz", + "integrity": "sha512-9UZCFRHQdNrfTpGg8+1INIg93B6zE0aXMVFkw1WFwvO4SlZywU6aLg5Of0Ap/PgcbSw4LNxvMWXMeugwMCX0AA==", "dev": true, "engines": { - "node": ">=10" - }, - "funding": { - "url": "https://github.com/sponsors/sindresorhus" + "node": ">=8" } }, "node_modules/npm-run-path": { @@ -4455,6 +4770,33 @@ "url": "https://github.com/sponsors/ljharb" } }, + "node_modules/object-keys": { + "version": "1.1.1", + "resolved": "https://registry.npmjs.org/object-keys/-/object-keys-1.1.1.tgz", + "integrity": "sha512-NuAESUOUMrlIXOfHKzD6bpPu3tYt3xvjNdRIQ+FeT0lNb4K8WR70CaDxhuNguS2XG+GjkyMwOzsN5ZktImfhLA==", + "dev": true, + "engines": { + "node": ">= 0.4" + } + }, + "node_modules/object.assign": { + "version": "4.1.2", + "resolved": "https://registry.npmjs.org/object.assign/-/object.assign-4.1.2.tgz", + "integrity": "sha512-ixT2L5THXsApyiUPYKmW+2EHpXXe5Ii3M+f4e+aJFAHao5amFRW6J0OO6c/LU8Be47utCx2GL89hxGB6XSmKuQ==", + "dev": true, + "dependencies": { + "call-bind": "^1.0.0", + "define-properties": "^1.1.3", + "has-symbols": "^1.0.1", + "object-keys": "^1.1.1" + }, + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, "node_modules/once": { "version": "1.4.0", "resolved": "https://registry.npmjs.org/once/-/once-1.4.0.tgz", @@ -4561,12 +4903,12 @@ } }, "node_modules/p-cancelable": { - "version": "2.1.1", - "resolved": "https://registry.npmjs.org/p-cancelable/-/p-cancelable-2.1.1.tgz", - "integrity": "sha512-BZOr3nRQHOntUjTrH8+Lh54smKHoHyur8We1V8DSMVrl5A2malOOwuJRnKRDjSnkoeBh4at6BwEnb5I7Jl31wg==", + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/p-cancelable/-/p-cancelable-1.1.0.tgz", + "integrity": "sha512-s73XxOZ4zpt1edZYZzvhqFa6uvQc1vwUa0K0BdtIZgQMAJj9IbebH+JkgKZc9h+B05PKHLOTl4ajG1BmNrVZlw==", "dev": true, "engines": { - "node": ">=8" + "node": ">=6" } }, "node_modules/p-limit": { @@ -4593,224 +4935,58 @@ "p-limit": "^2.2.0" }, "engines": { - "node": ">=8" - } - }, - "node_modules/p-map": { - "version": "3.0.0", - "resolved": "https://registry.npmjs.org/p-map/-/p-map-3.0.0.tgz", - "integrity": "sha512-d3qXVTF/s+W+CdJ5A29wywV2n8CQQYahlgz2bFiA+4eVNJbHJodPZ+/gXwPGh0bOqA+j8S+6+ckmvLGPk1QpxQ==", - "dev": true, - "dependencies": { - "aggregate-error": "^3.0.0" - }, - "engines": { - "node": ">=8" - } - }, - "node_modules/p-try": { - "version": "2.2.0", - "resolved": "https://registry.npmjs.org/p-try/-/p-try-2.2.0.tgz", - "integrity": "sha512-R4nPAVTAU0B9D35/Gk3uJf/7XYbQcyohSKdvAxIRSNghFl4e71hVoGnBNQz9cWaXxO2I10KTC+3jMdvvoKw6dQ==", - "dev": true, - "engines": { - "node": ">=6" - } - }, - "node_modules/package-hash": { - "version": "4.0.0", - "resolved": "https://registry.npmjs.org/package-hash/-/package-hash-4.0.0.tgz", - "integrity": "sha512-whdkPIooSu/bASggZ96BWVvZTRMOFxnyUG5PnTSGKoJE2gd5mbVNmR2Nj20QFzxYYgAXpoqC+AiXzl+UMRh7zQ==", - "dev": true, - "dependencies": { - "graceful-fs": "^4.1.15", - "hasha": "^5.0.0", - "lodash.flattendeep": "^4.4.0", - "release-zalgo": "^1.0.0" - }, - "engines": { - "node": ">=8" - } - }, - "node_modules/package-json": { - "version": "6.5.0", - "resolved": "https://registry.npmjs.org/package-json/-/package-json-6.5.0.tgz", - "integrity": "sha512-k3bdm2n25tkyxcjSKzB5x8kfVxlMdgsbPr0GkZcwHsLpba6cBjqCt1KlcChKEvxHIcTB1FVMuwoijZ26xex5MQ==", - "dev": true, - "dependencies": { - "got": "^9.6.0", - "registry-auth-token": "^4.0.0", - "registry-url": "^5.0.0", - "semver": "^6.2.0" - }, - "engines": { - "node": ">=8" - } - }, - "node_modules/package-json/node_modules/@sindresorhus/is": { - "version": "0.14.0", - "resolved": "https://registry.npmjs.org/@sindresorhus/is/-/is-0.14.0.tgz", - "integrity": "sha512-9NET910DNaIPngYnLLPeg+Ogzqsi9uM4mSboU5y6p8S5DzMTVEsJZrawi+BoDNUVBa2DhJqQYUFvMDfgU062LQ==", - "dev": true, - "engines": { - "node": ">=6" - } - }, - "node_modules/package-json/node_modules/@szmarczak/http-timer": { - "version": "1.1.2", - "resolved": "https://registry.npmjs.org/@szmarczak/http-timer/-/http-timer-1.1.2.tgz", - "integrity": "sha512-XIB2XbzHTN6ieIjfIMV9hlVcfPU26s2vafYWQcZHWXHOxiaRZYEDKEwdl129Zyg50+foYV2jCgtrqSA6qNuNSA==", - "dev": true, - "dependencies": { - "defer-to-connect": "^1.0.1" - }, - "engines": { - "node": ">=6" - } - }, - "node_modules/package-json/node_modules/cacheable-request": { - "version": "6.1.0", - "resolved": "https://registry.npmjs.org/cacheable-request/-/cacheable-request-6.1.0.tgz", - "integrity": "sha512-Oj3cAGPCqOZX7Rz64Uny2GYAZNliQSqfbePrgAQ1wKAihYmCUnraBtJtKcGR4xz7wF+LoJC+ssFZvv5BgF9Igg==", - "dev": true, - "dependencies": { - "clone-response": "^1.0.2", - "get-stream": "^5.1.0", - "http-cache-semantics": "^4.0.0", - "keyv": "^3.0.0", - "lowercase-keys": "^2.0.0", - "normalize-url": "^4.1.0", - "responselike": "^1.0.2" - }, - "engines": { - "node": ">=8" - } - }, - "node_modules/package-json/node_modules/cacheable-request/node_modules/get-stream": { - "version": "5.2.0", - "resolved": "https://registry.npmjs.org/get-stream/-/get-stream-5.2.0.tgz", - "integrity": "sha512-nBF+F1rAZVCu/p7rjzgA+Yb4lfYXrpl7a6VmJrU8wF9I1CKvP/QwPNZHnOlwbTkY6dvtFIzFMSyQXbLoTQPRpA==", - "dev": true, - "dependencies": { - "pump": "^3.0.0" - }, - "engines": { - "node": ">=8" - }, - "funding": { - "url": "https://github.com/sponsors/sindresorhus" - } - }, - "node_modules/package-json/node_modules/decompress-response": { - "version": "3.3.0", - "resolved": "https://registry.npmjs.org/decompress-response/-/decompress-response-3.3.0.tgz", - "integrity": "sha1-gKTdMjdIOEv6JICDYirt7Jgq3/M=", - "dev": true, - "dependencies": { - "mimic-response": "^1.0.0" - }, - "engines": { - "node": ">=4" - } - }, - "node_modules/package-json/node_modules/defer-to-connect": { - "version": "1.1.3", - "resolved": "https://registry.npmjs.org/defer-to-connect/-/defer-to-connect-1.1.3.tgz", - "integrity": "sha512-0ISdNousHvZT2EiFlZeZAHBUvSxmKswVCEf8hW7KWgG4a8MVEu/3Vb6uWYozkjylyCxe0JBIiRB1jV45S70WVQ==", - "dev": true - }, - "node_modules/package-json/node_modules/get-stream": { - "version": "4.1.0", - "resolved": "https://registry.npmjs.org/get-stream/-/get-stream-4.1.0.tgz", - "integrity": "sha512-GMat4EJ5161kIy2HevLlr4luNjBgvmj413KaQA7jt4V8B4RDsfpHk7WQ9GVqfYyyx8OS/L66Kox+rJRNklLK7w==", - "dev": true, - "dependencies": { - "pump": "^3.0.0" - }, - "engines": { - "node": ">=6" - } - }, - "node_modules/package-json/node_modules/got": { - "version": "9.6.0", - "resolved": "https://registry.npmjs.org/got/-/got-9.6.0.tgz", - "integrity": "sha512-R7eWptXuGYxwijs0eV+v3o6+XH1IqVK8dJOEecQfTmkncw9AV4dcw/Dhxi8MdlqPthxxpZyizMzyg8RTmEsG+Q==", - "dev": true, - "dependencies": { - "@sindresorhus/is": "^0.14.0", - "@szmarczak/http-timer": "^1.1.2", - "cacheable-request": "^6.0.0", - "decompress-response": "^3.3.0", - "duplexer3": "^0.1.4", - "get-stream": "^4.1.0", - "lowercase-keys": "^1.0.1", - "mimic-response": "^1.0.1", - "p-cancelable": "^1.0.0", - "to-readable-stream": "^1.0.0", - "url-parse-lax": "^3.0.0" - }, - "engines": { - "node": ">=8.6" - } - }, - "node_modules/package-json/node_modules/got/node_modules/lowercase-keys": { - "version": "1.0.1", - "resolved": "https://registry.npmjs.org/lowercase-keys/-/lowercase-keys-1.0.1.tgz", - "integrity": "sha512-G2Lj61tXDnVFFOi8VZds+SoQjtQC3dgokKdDG2mTm1tx4m50NUHBOZSBwQQHyy0V12A0JTG4icfZQH+xPyh8VA==", - "dev": true, - "engines": { - "node": ">=0.10.0" - } - }, - "node_modules/package-json/node_modules/json-buffer": { - "version": "3.0.0", - "resolved": "https://registry.npmjs.org/json-buffer/-/json-buffer-3.0.0.tgz", - "integrity": "sha1-Wx85evx11ne96Lz8Dkfh+aPZqJg=", - "dev": true - }, - "node_modules/package-json/node_modules/keyv": { - "version": "3.1.0", - "resolved": "https://registry.npmjs.org/keyv/-/keyv-3.1.0.tgz", - "integrity": "sha512-9ykJ/46SN/9KPM/sichzQ7OvXyGDYKGTaDlKMGCAlg2UK8KRy4jb0d8sFc+0Tt0YYnThq8X2RZgCg74RPxgcVA==", - "dev": true, - "dependencies": { - "json-buffer": "3.0.0" + "node": ">=8" } }, - "node_modules/package-json/node_modules/normalize-url": { - "version": "4.5.1", - "resolved": "https://registry.npmjs.org/normalize-url/-/normalize-url-4.5.1.tgz", - "integrity": "sha512-9UZCFRHQdNrfTpGg8+1INIg93B6zE0aXMVFkw1WFwvO4SlZywU6aLg5Of0Ap/PgcbSw4LNxvMWXMeugwMCX0AA==", + "node_modules/p-map": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/p-map/-/p-map-3.0.0.tgz", + "integrity": "sha512-d3qXVTF/s+W+CdJ5A29wywV2n8CQQYahlgz2bFiA+4eVNJbHJodPZ+/gXwPGh0bOqA+j8S+6+ckmvLGPk1QpxQ==", "dev": true, + "dependencies": { + "aggregate-error": "^3.0.0" + }, "engines": { "node": ">=8" } }, - "node_modules/package-json/node_modules/p-cancelable": { - "version": "1.1.0", - "resolved": "https://registry.npmjs.org/p-cancelable/-/p-cancelable-1.1.0.tgz", - "integrity": "sha512-s73XxOZ4zpt1edZYZzvhqFa6uvQc1vwUa0K0BdtIZgQMAJj9IbebH+JkgKZc9h+B05PKHLOTl4ajG1BmNrVZlw==", + "node_modules/p-try": { + "version": "2.2.0", + "resolved": "https://registry.npmjs.org/p-try/-/p-try-2.2.0.tgz", + "integrity": "sha512-R4nPAVTAU0B9D35/Gk3uJf/7XYbQcyohSKdvAxIRSNghFl4e71hVoGnBNQz9cWaXxO2I10KTC+3jMdvvoKw6dQ==", "dev": true, "engines": { "node": ">=6" } }, - "node_modules/package-json/node_modules/responselike": { - "version": "1.0.2", - "resolved": "https://registry.npmjs.org/responselike/-/responselike-1.0.2.tgz", - "integrity": "sha1-kYcg7ztjHFZCvgaPFa3lpG9Loec=", + "node_modules/package-hash": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/package-hash/-/package-hash-4.0.0.tgz", + "integrity": "sha512-whdkPIooSu/bASggZ96BWVvZTRMOFxnyUG5PnTSGKoJE2gd5mbVNmR2Nj20QFzxYYgAXpoqC+AiXzl+UMRh7zQ==", "dev": true, "dependencies": { - "lowercase-keys": "^1.0.0" + "graceful-fs": "^4.1.15", + "hasha": "^5.0.0", + "lodash.flattendeep": "^4.4.0", + "release-zalgo": "^1.0.0" + }, + "engines": { + "node": ">=8" } }, - "node_modules/package-json/node_modules/responselike/node_modules/lowercase-keys": { - "version": "1.0.1", - "resolved": "https://registry.npmjs.org/lowercase-keys/-/lowercase-keys-1.0.1.tgz", - "integrity": "sha512-G2Lj61tXDnVFFOi8VZds+SoQjtQC3dgokKdDG2mTm1tx4m50NUHBOZSBwQQHyy0V12A0JTG4icfZQH+xPyh8VA==", + "node_modules/package-json": { + "version": "6.5.0", + "resolved": "https://registry.npmjs.org/package-json/-/package-json-6.5.0.tgz", + "integrity": "sha512-k3bdm2n25tkyxcjSKzB5x8kfVxlMdgsbPr0GkZcwHsLpba6cBjqCt1KlcChKEvxHIcTB1FVMuwoijZ26xex5MQ==", "dev": true, + "dependencies": { + "got": "^9.6.0", + "registry-auth-token": "^4.0.0", + "registry-url": "^5.0.0", + "semver": "^6.2.0" + }, "engines": { - "node": ">=0.10.0" + "node": ">=8" } }, "node_modules/parent-module": { @@ -4867,6 +5043,18 @@ "protocols": "^1.4.0" } }, + "node_modules/parse-url/node_modules/normalize-url": { + "version": "6.1.0", + "resolved": "https://registry.npmjs.org/normalize-url/-/normalize-url-6.1.0.tgz", + "integrity": "sha512-DlL+XwOy3NxAQ8xuC0okPgK46iuVNAK01YN7RueYBqqFeGsBjV9XmCAzAdgt+667bCl5kPh9EqKKDwnaPG1I7A==", + "dev": true, + "engines": { + "node": ">=10" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, "node_modules/path-exists": { "version": "4.0.0", "resolved": "https://registry.npmjs.org/path-exists/-/path-exists-4.0.0.tgz", @@ -4909,6 +5097,12 @@ "isarray": "0.0.1" } }, + "node_modules/path-to-regexp/node_modules/isarray": { + "version": "0.0.1", + "resolved": "https://registry.npmjs.org/isarray/-/isarray-0.0.1.tgz", + "integrity": "sha1-ihis/Kmo9Bd+Cav8YDiTmwXR7t8=", + "dev": true + }, "node_modules/path-type": { "version": "4.0.0", "resolved": "https://registry.npmjs.org/path-type/-/path-type-4.0.0.tgz", @@ -5008,6 +5202,26 @@ "node": ">=8" } }, + "node_modules/promise.allsettled": { + "version": "1.0.5", + "resolved": "https://registry.npmjs.org/promise.allsettled/-/promise.allsettled-1.0.5.tgz", + "integrity": "sha512-tVDqeZPoBC0SlzJHzWGZ2NKAguVq2oiYj7gbggbiTvH2itHohijTp7njOUA0aQ/nl+0lr/r6egmhoYu63UZ/pQ==", + "dev": true, + "dependencies": { + "array.prototype.map": "^1.0.4", + "call-bind": "^1.0.2", + "define-properties": "^1.1.3", + "es-abstract": "^1.19.1", + "get-intrinsic": "^1.1.1", + "iterate-value": "^1.0.2" + }, + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, "node_modules/protocols": { "version": "1.4.8", "resolved": "https://registry.npmjs.org/protocols/-/protocols-1.4.8.tgz", @@ -5098,18 +5312,6 @@ } ] }, - "node_modules/quick-lru": { - "version": "5.1.1", - "resolved": "https://registry.npmjs.org/quick-lru/-/quick-lru-5.1.1.tgz", - "integrity": "sha512-WuyALRjWPDGtt/wzJiadO5AXY+8hZ80hVpe6MyivgraREW751X3SbhRvG3eLKOYN+8VEvqLcf3wdnt44Z4S4SA==", - "dev": true, - "engines": { - "node": ">=10" - }, - "funding": { - "url": "https://github.com/sponsors/sindresorhus" - } - }, "node_modules/randombytes": { "version": "2.1.0", "resolved": "https://registry.npmjs.org/randombytes/-/randombytes-2.1.0.tgz", @@ -5234,9 +5436,9 @@ } }, "node_modules/release-it": { - "version": "14.13.1", - "resolved": "https://registry.npmjs.org/release-it/-/release-it-14.13.1.tgz", - "integrity": "sha512-mrng5bqZDFMr/7oCH3kuflwjKpKki4dUp6yYGxs20scYCvvd8rHAI5pdQOJHwI5BKHAC/pad0UjAEycMWQnEIw==", + "version": "14.14.2", + "resolved": "https://registry.npmjs.org/release-it/-/release-it-14.14.2.tgz", + "integrity": "sha512-+TE5Zg7x5BE/xw6i8SN9rmsGxCE2GVqH1v8Ay1L09nsLQx1HJhihAqUtCCDdgOuLvGpX0xgDMsSzakDlDLpOkA==", "dev": true, "dependencies": { "@iarna/toml": "2.2.5", @@ -5244,22 +5446,23 @@ "async-retry": "1.3.3", "chalk": "4.1.2", "cosmiconfig": "7.0.1", - "debug": "4.3.3", + "debug": "4.3.4", "execa": "5.1.1", "form-data": "4.0.0", "git-url-parse": "11.6.0", "globby": "11.0.4", - "got": "11.8.3", + "got": "9.6.0", "import-cwd": "3.0.0", "inquirer": "8.2.0", "is-ci": "3.0.1", "lodash": "4.17.21", - "mime-types": "2.1.34", + "mime-types": "2.1.35", "new-github-release-url": "1.0.0", "open": "7.4.2", "ora": "5.4.1", "os-name": "4.0.1", "parse-json": "5.2.0", + "promise.allsettled": "1.0.5", "semver": "7.3.5", "shelljs": "0.8.5", "update-notifier": "5.1.0", @@ -5364,12 +5567,6 @@ "url": "https://github.com/sponsors/ljharb" } }, - "node_modules/resolve-alpn": { - "version": "1.2.1", - "resolved": "https://registry.npmjs.org/resolve-alpn/-/resolve-alpn-1.2.1.tgz", - "integrity": "sha512-0a1F4l73/ZFZOakJnQ3FvkJ2+gSTQWz/r2KE5OdDY0TxPm5h4GkqkWWfM47T7HsbnOtcJVEF4epCVy6u7Q3K+g==", - "dev": true - }, "node_modules/resolve-from": { "version": "4.0.0", "resolved": "https://registry.npmjs.org/resolve-from/-/resolve-from-4.0.0.tgz", @@ -5380,12 +5577,12 @@ } }, "node_modules/responselike": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/responselike/-/responselike-2.0.0.tgz", - "integrity": "sha512-xH48u3FTB9VsZw7R+vvgaKeLKzT6jOogbQhEe/jewwnZgzPcnyWui2Av6JpoYZF/91uueC+lqhWqeURw5/qhCw==", + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/responselike/-/responselike-1.0.2.tgz", + "integrity": "sha1-kYcg7ztjHFZCvgaPFa3lpG9Loec=", "dev": true, "dependencies": { - "lowercase-keys": "^2.0.0" + "lowercase-keys": "^1.0.0" } }, "node_modules/restore-cursor": { @@ -5726,6 +5923,32 @@ "node": ">=8" } }, + "node_modules/string.prototype.trimend": { + "version": "1.0.4", + "resolved": "https://registry.npmjs.org/string.prototype.trimend/-/string.prototype.trimend-1.0.4.tgz", + "integrity": "sha512-y9xCjw1P23Awk8EvTpcyL2NIr1j7wJ39f+k6lvRnSMz+mz9CGz9NYPelDk42kOz6+ql8xjfK8oYzy3jAP5QU5A==", + "dev": true, + "dependencies": { + "call-bind": "^1.0.2", + "define-properties": "^1.1.3" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/string.prototype.trimstart": { + "version": "1.0.4", + "resolved": "https://registry.npmjs.org/string.prototype.trimstart/-/string.prototype.trimstart-1.0.4.tgz", + "integrity": "sha512-jh6e984OBfvxS50tdY2nRZnoC5/mLFKOREQfw8t5yytkoUsJRNxvI/E39qu1sD0OtWI3OC0XgKSmcWwziwYuZw==", + "dev": true, + "dependencies": { + "call-bind": "^1.0.2", + "define-properties": "^1.1.3" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, "node_modules/strip-ansi": { "version": "6.0.1", "resolved": "https://registry.npmjs.org/strip-ansi/-/strip-ansi-6.0.1.tgz", @@ -6009,9 +6232,9 @@ } }, "node_modules/typedoc": { - "version": "0.22.13", - "resolved": "https://registry.npmjs.org/typedoc/-/typedoc-0.22.13.tgz", - "integrity": "sha512-NHNI7Dr6JHa/I3+c62gdRNXBIyX7P33O9TafGLd07ur3MqzcKgwTvpg18EtvCLHJyfeSthAtCLpM7WkStUmDuQ==", + "version": "0.22.15", + "resolved": "https://registry.npmjs.org/typedoc/-/typedoc-0.22.15.tgz", + "integrity": "sha512-CMd1lrqQbFvbx6S9G6fL4HKp3GoIuhujJReWqlIvSb2T26vGai+8Os3Mde7Pn832pXYemd9BMuuYWhFpL5st0Q==", "dev": true, "dependencies": { "glob": "^7.2.0", @@ -6064,6 +6287,21 @@ "node": ">=4.2.0" } }, + "node_modules/unbox-primitive": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/unbox-primitive/-/unbox-primitive-1.0.1.tgz", + "integrity": "sha512-tZU/3NqK3dA5gpE1KtyiJUrEB0lxnGkMFHptJ7q6ewdZ8s12QrODwNbhIJStmJkd1QDXa1NRA8aF2A1zk/Ypyw==", + "dev": true, + "dependencies": { + "function-bind": "^1.1.1", + "has-bigints": "^1.0.1", + "has-symbols": "^1.0.2", + "which-boxed-primitive": "^1.0.2" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, "node_modules/unique-string": { "version": "2.0.0", "resolved": "https://registry.npmjs.org/unique-string/-/unique-string-2.0.0.tgz", @@ -6138,9 +6376,9 @@ } }, "node_modules/update-notifier/node_modules/semver": { - "version": "7.3.5", - "resolved": "https://registry.npmjs.org/semver/-/semver-7.3.5.tgz", - "integrity": "sha512-PoeGJYh8HK4BTO/a9Tf6ZG3veo/A7ZVsYrSA6J8ny9nb3B1VrpkuN+z9OE5wfE5p6H4LchYZsegiQgbJD94ZFQ==", + "version": "7.3.7", + "resolved": "https://registry.npmjs.org/semver/-/semver-7.3.7.tgz", + "integrity": "sha512-QlYTucUYOews+WeEujDoEGziz4K6c47V/Bd+LjSSYcA94p+DmINdf7ncaUinThfvZyu13lN9OY1XDxt8C0Tw0g==", "dev": true, "dependencies": { "lru-cache": "^6.0.0" @@ -6258,6 +6496,22 @@ "node": ">= 8" } }, + "node_modules/which-boxed-primitive": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/which-boxed-primitive/-/which-boxed-primitive-1.0.2.tgz", + "integrity": "sha512-bwZdv0AKLpplFY2KZRX6TvyuN7ojjr7lwkg6ml0roIy9YeuSr7JS372qlNW18UQYzgYK9ziGcerWqZOmEn9VNg==", + "dev": true, + "dependencies": { + "is-bigint": "^1.0.1", + "is-boolean-object": "^1.1.0", + "is-number-object": "^1.0.4", + "is-string": "^1.0.5", + "is-symbol": "^1.0.3" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, "node_modules/which-module": { "version": "2.0.0", "resolved": "https://registry.npmjs.org/which-module/-/which-module-2.0.0.tgz", @@ -6427,9 +6681,9 @@ } }, "node_modules/yargs": { - "version": "17.4.0", - "resolved": "https://registry.npmjs.org/yargs/-/yargs-17.4.0.tgz", - "integrity": "sha512-WJudfrk81yWFSOkZYpAZx4Nt7V4xp7S/uJkX0CnxovMCt1wCE8LNftPpNuF9X/u9gN5nsD7ycYtRcDf2pL3UiA==", + "version": "17.4.1", + "resolved": "https://registry.npmjs.org/yargs/-/yargs-17.4.1.tgz", + "integrity": "sha512-WSZD9jgobAg3ZKuCQZSa3g9QOJeCCqLoLAykiWgmXnDo9EPnn4RPf5qVTtzgOx66o6/oqhcA5tHtJXpG8pMt3g==", "dev": true, "dependencies": { "cliui": "^7.0.2", @@ -6511,6 +6765,7 @@ } }, "packages/bloom": { + "name": "@node-redis/bloom", "version": "1.0.1", "license": "MIT", "devDependencies": { @@ -6518,10 +6773,10 @@ "@node-redis/test-utils": "*", "@types/node": "^17.0.23", "nyc": "^15.1.0", - "release-it": "^14.13.1", + "release-it": "^14.14.2", "source-map-support": "^0.5.21", "ts-node": "^10.7.0", - "typedoc": "^0.22.13", + "typedoc": "^0.22.15", "typescript": "^4.6.3" }, "peerDependencies": { @@ -6529,6 +6784,7 @@ } }, "packages/client": { + "name": "@node-redis/client", "version": "1.0.5", "license": "MIT", "dependencies": { @@ -6544,15 +6800,15 @@ "@types/redis-parser": "^3.0.0", "@types/sinon": "^10.0.11", "@types/yallist": "^4.0.1", - "@typescript-eslint/eslint-plugin": "^5.16.0", - "@typescript-eslint/parser": "^5.16.0", - "eslint": "^8.12.0", + "@typescript-eslint/eslint-plugin": "^5.19.0", + "@typescript-eslint/parser": "^5.19.0", + "eslint": "^8.13.0", "nyc": "^15.1.0", - "release-it": "^14.13.1", + "release-it": "^14.14.2", "sinon": "^13.0.1", "source-map-support": "^0.5.21", "ts-node": "^10.7.0", - "typedoc": "^0.22.13", + "typedoc": "^0.22.15", "typescript": "^4.6.3" }, "engines": { @@ -6560,6 +6816,7 @@ } }, "packages/graph": { + "name": "@node-redis/graph", "version": "1.0.0", "license": "MIT", "devDependencies": { @@ -6567,10 +6824,10 @@ "@node-redis/test-utils": "*", "@types/node": "^17.0.23", "nyc": "^15.1.0", - "release-it": "^14.13.1", + "release-it": "^14.14.2", "source-map-support": "^0.5.21", "ts-node": "^10.7.0", - "typedoc": "^0.22.13", + "typedoc": "^0.22.15", "typescript": "^4.6.3" }, "peerDependencies": { @@ -6578,6 +6835,7 @@ } }, "packages/json": { + "name": "@node-redis/json", "version": "1.0.2", "license": "MIT", "devDependencies": { @@ -6585,10 +6843,10 @@ "@node-redis/test-utils": "*", "@types/node": "^17.0.23", "nyc": "^15.1.0", - "release-it": "^14.13.1", + "release-it": "^14.14.2", "source-map-support": "^0.5.21", "ts-node": "^10.7.0", - "typedoc": "^0.22.13", + "typedoc": "^0.22.15", "typescript": "^4.6.3" }, "peerDependencies": { @@ -6596,6 +6854,7 @@ } }, "packages/search": { + "name": "@node-redis/search", "version": "1.0.5", "license": "MIT", "devDependencies": { @@ -6603,10 +6862,10 @@ "@node-redis/test-utils": "*", "@types/node": "^17.0.23", "nyc": "^15.1.0", - "release-it": "^14.13.1", + "release-it": "^14.14.2", "source-map-support": "^0.5.21", "ts-node": "^10.7.0", - "typedoc": "^0.22.13", + "typedoc": "^0.22.15", "typescript": "^4.6.3" }, "peerDependencies": { @@ -6614,6 +6873,7 @@ } }, "packages/test-utils": { + "name": "@node-redis/test-utils", "devDependencies": { "@istanbuljs/nyc-config-typescript": "^1.0.2", "@types/mocha": "^9.1.0", @@ -6624,13 +6884,14 @@ "source-map-support": "^0.5.21", "ts-node": "^10.7.0", "typescript": "^4.6.3", - "yargs": "^17.4.0" + "yargs": "^17.4.1" }, "peerDependencies": { "@node-redis/client": "^1.0.0" } }, "packages/time-series": { + "name": "@node-redis/time-series", "version": "1.0.2", "license": "MIT", "devDependencies": { @@ -6638,10 +6899,10 @@ "@node-redis/test-utils": "*", "@types/node": "^17.0.23", "nyc": "^15.1.0", - "release-it": "^14.13.1", + "release-it": "^14.14.2", "source-map-support": "^0.5.21", "ts-node": "^10.7.0", - "typedoc": "^0.22.13", + "typedoc": "^0.22.15", "typescript": "^4.6.3" }, "peerDependencies": { @@ -6675,32 +6936,32 @@ "dev": true }, "@babel/core": { - "version": "7.17.8", - "resolved": "https://registry.npmjs.org/@babel/core/-/core-7.17.8.tgz", - "integrity": "sha512-OdQDV/7cRBtJHLSOBqqbYNkOcydOgnX59TZx4puf41fzcVtN3e/4yqY8lMQsK+5X2lJtAdmA+6OHqsj1hBJ4IQ==", + "version": "7.17.9", + "resolved": "https://registry.npmjs.org/@babel/core/-/core-7.17.9.tgz", + "integrity": "sha512-5ug+SfZCpDAkVp9SFIZAzlW18rlzsOcJGaetCjkySnrXXDUw9AR8cDUm1iByTmdWM6yxX6/zycaV76w3YTF2gw==", "dev": true, "requires": { "@ampproject/remapping": "^2.1.0", "@babel/code-frame": "^7.16.7", - "@babel/generator": "^7.17.7", + "@babel/generator": "^7.17.9", "@babel/helper-compilation-targets": "^7.17.7", "@babel/helper-module-transforms": "^7.17.7", - "@babel/helpers": "^7.17.8", - "@babel/parser": "^7.17.8", + "@babel/helpers": "^7.17.9", + "@babel/parser": "^7.17.9", "@babel/template": "^7.16.7", - "@babel/traverse": "^7.17.3", + "@babel/traverse": "^7.17.9", "@babel/types": "^7.17.0", "convert-source-map": "^1.7.0", "debug": "^4.1.0", "gensync": "^1.0.0-beta.2", - "json5": "^2.1.2", + "json5": "^2.2.1", "semver": "^6.3.0" } }, "@babel/generator": { - "version": "7.17.7", - "resolved": "https://registry.npmjs.org/@babel/generator/-/generator-7.17.7.tgz", - "integrity": "sha512-oLcVCTeIFadUoArDTwpluncplrYBmTCCZZgXCbgNGvOBBiSDDK3eWO4b/+eOTli5tKv1lg+a5/NAXg+nTcei1w==", + "version": "7.17.9", + "resolved": "https://registry.npmjs.org/@babel/generator/-/generator-7.17.9.tgz", + "integrity": "sha512-rAdDousTwxbIxbz5I7GEQ3lUip+xVCXooZNbsydCWs3xA7ZsYOv+CFRdzGxRX78BmQHu9B1Eso59AOZQOJDEdQ==", "dev": true, "requires": { "@babel/types": "^7.17.0", @@ -6730,23 +6991,13 @@ } }, "@babel/helper-function-name": { - "version": "7.16.7", - "resolved": "https://registry.npmjs.org/@babel/helper-function-name/-/helper-function-name-7.16.7.tgz", - "integrity": "sha512-QfDfEnIUyyBSR3HtrtGECuZ6DAyCkYFp7GHl75vFtTnn6pjKeK0T1DB5lLkFvBea8MdaiUABx3osbgLyInoejA==", + "version": "7.17.9", + "resolved": "https://registry.npmjs.org/@babel/helper-function-name/-/helper-function-name-7.17.9.tgz", + "integrity": "sha512-7cRisGlVtiVqZ0MW0/yFB4atgpGLWEHUVYnb448hZK4x+vih0YO5UoS11XIYtZYqHd0dIPMdUSv8q5K4LdMnIg==", "dev": true, "requires": { - "@babel/helper-get-function-arity": "^7.16.7", "@babel/template": "^7.16.7", - "@babel/types": "^7.16.7" - } - }, - "@babel/helper-get-function-arity": { - "version": "7.16.7", - "resolved": "https://registry.npmjs.org/@babel/helper-get-function-arity/-/helper-get-function-arity-7.16.7.tgz", - "integrity": "sha512-flc+RLSOBXzNzVhcLu6ujeHUrD6tANAOU5ojrRx/as+tbzf8+stUCj7+IfRRoAbEZqj/ahXEMsjhOhgeZsrnTw==", - "dev": true, - "requires": { - "@babel/types": "^7.16.7" + "@babel/types": "^7.17.0" } }, "@babel/helper-hoist-variables": { @@ -6814,20 +7065,20 @@ "dev": true }, "@babel/helpers": { - "version": "7.17.8", - "resolved": "https://registry.npmjs.org/@babel/helpers/-/helpers-7.17.8.tgz", - "integrity": "sha512-QcL86FGxpfSJwGtAvv4iG93UL6bmqBdmoVY0CMCU2g+oD2ezQse3PT5Pa+jiD6LJndBQi0EDlpzOWNlLuhz5gw==", + "version": "7.17.9", + "resolved": "https://registry.npmjs.org/@babel/helpers/-/helpers-7.17.9.tgz", + "integrity": "sha512-cPCt915ShDWUEzEp3+UNRktO2n6v49l5RSnG9M5pS24hA+2FAc5si+Pn1i4VVbQQ+jh+bIZhPFQOJOzbrOYY1Q==", "dev": true, "requires": { "@babel/template": "^7.16.7", - "@babel/traverse": "^7.17.3", + "@babel/traverse": "^7.17.9", "@babel/types": "^7.17.0" } }, "@babel/highlight": { - "version": "7.16.10", - "resolved": "https://registry.npmjs.org/@babel/highlight/-/highlight-7.16.10.tgz", - "integrity": "sha512-5FnTQLSLswEj6IkgVw5KusNUUFY9ZGqe/TRFnP/BKYHYgfh7tc+C7mwiy95/yNP7Dh9x580Vv8r7u7ZfTBFxdw==", + "version": "7.17.9", + "resolved": "https://registry.npmjs.org/@babel/highlight/-/highlight-7.17.9.tgz", + "integrity": "sha512-J9PfEKCbFIv2X5bjTMiZu6Vf341N05QIY+d6FvVKynkG1S7G0j3I0QoRtWIrXhZ+/Nlb5Q0MzqL7TokEJ5BNHg==", "dev": true, "requires": { "@babel/helper-validator-identifier": "^7.16.7", @@ -6888,9 +7139,9 @@ } }, "@babel/parser": { - "version": "7.17.8", - "resolved": "https://registry.npmjs.org/@babel/parser/-/parser-7.17.8.tgz", - "integrity": "sha512-BoHhDJrJXqcg+ZL16Xv39H9n+AqJ4pcDrQBGZN+wHxIysrLZ3/ECwCBUch/1zUNhnsXULcONU3Ei5Hmkfk6kiQ==", + "version": "7.17.9", + "resolved": "https://registry.npmjs.org/@babel/parser/-/parser-7.17.9.tgz", + "integrity": "sha512-vqUSBLP8dQHFPdPi9bc5GK9vRkYHJ49fsZdtoJ8EQ8ibpwk5rPKfvNIwChB0KVXcIjcepEBBd2VHC5r9Gy8ueg==", "dev": true }, "@babel/template": { @@ -6905,18 +7156,18 @@ } }, "@babel/traverse": { - "version": "7.17.3", - "resolved": "https://registry.npmjs.org/@babel/traverse/-/traverse-7.17.3.tgz", - "integrity": "sha512-5irClVky7TxRWIRtxlh2WPUUOLhcPN06AGgaQSB8AEwuyEBgJVuJ5imdHm5zxk8w0QS5T+tDfnDxAlhWjpb7cw==", + "version": "7.17.9", + "resolved": "https://registry.npmjs.org/@babel/traverse/-/traverse-7.17.9.tgz", + "integrity": "sha512-PQO8sDIJ8SIwipTPiR71kJQCKQYB5NGImbOviK8K+kg5xkNSYXLBupuX9QhatFowrsvo9Hj8WgArg3W7ijNAQw==", "dev": true, "requires": { "@babel/code-frame": "^7.16.7", - "@babel/generator": "^7.17.3", + "@babel/generator": "^7.17.9", "@babel/helper-environment-visitor": "^7.16.7", - "@babel/helper-function-name": "^7.16.7", + "@babel/helper-function-name": "^7.17.9", "@babel/helper-hoist-variables": "^7.16.7", "@babel/helper-split-export-declaration": "^7.16.7", - "@babel/parser": "^7.17.3", + "@babel/parser": "^7.17.9", "@babel/types": "^7.17.0", "debug": "^4.1.0", "globals": "^11.1.0" @@ -7093,10 +7344,10 @@ "@node-redis/test-utils": "*", "@types/node": "^17.0.23", "nyc": "^15.1.0", - "release-it": "^14.13.1", + "release-it": "^14.14.2", "source-map-support": "^0.5.21", "ts-node": "^10.7.0", - "typedoc": "^0.22.13", + "typedoc": "^0.22.15", "typescript": "^4.6.3" } }, @@ -7109,18 +7360,18 @@ "@types/redis-parser": "^3.0.0", "@types/sinon": "^10.0.11", "@types/yallist": "^4.0.1", - "@typescript-eslint/eslint-plugin": "^5.16.0", - "@typescript-eslint/parser": "^5.16.0", + "@typescript-eslint/eslint-plugin": "^5.19.0", + "@typescript-eslint/parser": "^5.19.0", "cluster-key-slot": "1.1.0", - "eslint": "^8.12.0", + "eslint": "^8.13.0", "generic-pool": "3.8.2", "nyc": "^15.1.0", "redis-parser": "3.0.0", - "release-it": "^14.13.1", + "release-it": "^14.14.2", "sinon": "^13.0.1", "source-map-support": "^0.5.21", "ts-node": "^10.7.0", - "typedoc": "^0.22.13", + "typedoc": "^0.22.15", "typescript": "^4.6.3", "yallist": "4.0.0" } @@ -7132,10 +7383,10 @@ "@node-redis/test-utils": "*", "@types/node": "^17.0.23", "nyc": "^15.1.0", - "release-it": "^14.13.1", + "release-it": "^14.14.2", "source-map-support": "^0.5.21", "ts-node": "^10.7.0", - "typedoc": "^0.22.13", + "typedoc": "^0.22.15", "typescript": "^4.6.3" } }, @@ -7146,10 +7397,10 @@ "@node-redis/test-utils": "*", "@types/node": "^17.0.23", "nyc": "^15.1.0", - "release-it": "^14.13.1", + "release-it": "^14.14.2", "source-map-support": "^0.5.21", "ts-node": "^10.7.0", - "typedoc": "^0.22.13", + "typedoc": "^0.22.15", "typescript": "^4.6.3" } }, @@ -7160,10 +7411,10 @@ "@node-redis/test-utils": "*", "@types/node": "^17.0.23", "nyc": "^15.1.0", - "release-it": "^14.13.1", + "release-it": "^14.14.2", "source-map-support": "^0.5.21", "ts-node": "^10.7.0", - "typedoc": "^0.22.13", + "typedoc": "^0.22.15", "typescript": "^4.6.3" } }, @@ -7179,7 +7430,7 @@ "source-map-support": "^0.5.21", "ts-node": "^10.7.0", "typescript": "^4.6.3", - "yargs": "^17.4.0" + "yargs": "^17.4.1" } }, "@node-redis/time-series": { @@ -7189,10 +7440,10 @@ "@node-redis/test-utils": "*", "@types/node": "^17.0.23", "nyc": "^15.1.0", - "release-it": "^14.13.1", + "release-it": "^14.14.2", "source-map-support": "^0.5.21", "ts-node": "^10.7.0", - "typedoc": "^0.22.13", + "typedoc": "^0.22.15", "typescript": "^4.6.3" } }, @@ -7347,9 +7598,9 @@ } }, "@sindresorhus/is": { - "version": "4.6.0", - "resolved": "https://registry.npmjs.org/@sindresorhus/is/-/is-4.6.0.tgz", - "integrity": "sha512-t09vSN3MdfsyCHoFcTRCH/iUtG7OJ0CsjzB8cjAmKc/va/kIgeDI/TxsigdncE/4be734m0cvIYwNaV4i2XqAw==", + "version": "0.14.0", + "resolved": "https://registry.npmjs.org/@sindresorhus/is/-/is-0.14.0.tgz", + "integrity": "sha512-9NET910DNaIPngYnLLPeg+Ogzqsi9uM4mSboU5y6p8S5DzMTVEsJZrawi+BoDNUVBa2DhJqQYUFvMDfgU062LQ==", "dev": true }, "@sinonjs/commons": { @@ -7362,9 +7613,9 @@ } }, "@sinonjs/fake-timers": { - "version": "9.1.1", - "resolved": "https://registry.npmjs.org/@sinonjs/fake-timers/-/fake-timers-9.1.1.tgz", - "integrity": "sha512-Wp5vwlZ0lOqpSYGKqr53INws9HLkt6JDc/pDZcPf7bchQnrXJMXPns8CXx0hFikMSGSWfvtvvpb2gtMVfkWagA==", + "version": "9.1.2", + "resolved": "https://registry.npmjs.org/@sinonjs/fake-timers/-/fake-timers-9.1.2.tgz", + "integrity": "sha512-BPS4ynJW/o92PUR4wgriz2Ud5gpST5vz6GQfMixEDK0Z8ZCUv2M7SkBLykH56T++Xs+8ln9zTGbOvNGIe02/jw==", "dev": true, "requires": { "@sinonjs/commons": "^1.7.0" @@ -7388,12 +7639,12 @@ "dev": true }, "@szmarczak/http-timer": { - "version": "4.0.6", - "resolved": "https://registry.npmjs.org/@szmarczak/http-timer/-/http-timer-4.0.6.tgz", - "integrity": "sha512-4BAffykYOgO+5nzBWYwE3W90sBgLJoUPRWWcL8wlyiM8IB8ipJz3UMJ9KXQd1RKQXpKp8Tutn80HZtWsu2u76w==", + "version": "1.1.2", + "resolved": "https://registry.npmjs.org/@szmarczak/http-timer/-/http-timer-1.1.2.tgz", + "integrity": "sha512-XIB2XbzHTN6ieIjfIMV9hlVcfPU26s2vafYWQcZHWXHOxiaRZYEDKEwdl129Zyg50+foYV2jCgtrqSA6qNuNSA==", "dev": true, "requires": { - "defer-to-connect": "^2.0.0" + "defer-to-connect": "^1.0.1" } }, "@tsconfig/node10": { @@ -7420,39 +7671,12 @@ "integrity": "sha512-eZxlbI8GZscaGS7kkc/trHTT5xgrjH3/1n2JDwusC9iahPKWMRvRjJSAN5mCXviuTGQ/lHnhvv8Q1YTpnfz9gA==", "dev": true }, - "@types/cacheable-request": { - "version": "6.0.2", - "resolved": "https://registry.npmjs.org/@types/cacheable-request/-/cacheable-request-6.0.2.tgz", - "integrity": "sha512-B3xVo+dlKM6nnKTcmm5ZtY/OL8bOAOd2Olee9M1zft65ox50OzjEHW91sDiU9j6cvW8Ejg1/Qkf4xd2kugApUA==", - "dev": true, - "requires": { - "@types/http-cache-semantics": "*", - "@types/keyv": "*", - "@types/node": "*", - "@types/responselike": "*" - } - }, - "@types/http-cache-semantics": { - "version": "4.0.1", - "resolved": "https://registry.npmjs.org/@types/http-cache-semantics/-/http-cache-semantics-4.0.1.tgz", - "integrity": "sha512-SZs7ekbP8CN0txVG2xVRH6EgKmEm31BOxA07vkFaETzZz1xh+cbt8BcI0slpymvwhx5dlFnQG2rTlPVQn+iRPQ==", - "dev": true - }, "@types/json-schema": { "version": "7.0.11", "resolved": "https://registry.npmjs.org/@types/json-schema/-/json-schema-7.0.11.tgz", "integrity": "sha512-wOuvG1SN4Us4rez+tylwwwCV1psiNVOkJeM3AUWUNWg/jDQY2+HE/444y5gc+jBmRqASOm2Oeh5c1axHobwRKQ==", "dev": true }, - "@types/keyv": { - "version": "3.1.4", - "resolved": "https://registry.npmjs.org/@types/keyv/-/keyv-3.1.4.tgz", - "integrity": "sha512-BQ5aZNSCpj7D6K2ksrRCTmKRLEpnPvWDiLPfoGyhZ++8YtiK9d/3DBKPJgry359X/P1PfruyYwvnvwFjuEiEIg==", - "dev": true, - "requires": { - "@types/node": "*" - } - }, "@types/mocha": { "version": "9.1.0", "resolved": "https://registry.npmjs.org/@types/mocha/-/mocha-9.1.0.tgz", @@ -7487,15 +7711,6 @@ "@types/redis-errors": "*" } }, - "@types/responselike": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/@types/responselike/-/responselike-1.0.0.tgz", - "integrity": "sha512-85Y2BjiufFzaMIlvJDvTTB8Fxl2xfLo4HgmHzVBz08w4wDePCTjYw66PdrolO0kzli3yam/YCgRufyo1DdQVTA==", - "dev": true, - "requires": { - "@types/node": "*" - } - }, "@types/sinon": { "version": "10.0.11", "resolved": "https://registry.npmjs.org/@types/sinon/-/sinon-10.0.11.tgz", @@ -7533,14 +7748,14 @@ "dev": true }, "@typescript-eslint/eslint-plugin": { - "version": "5.17.0", - "resolved": "https://registry.npmjs.org/@typescript-eslint/eslint-plugin/-/eslint-plugin-5.17.0.tgz", - "integrity": "sha512-qVstvQilEd89HJk3qcbKt/zZrfBZ+9h2ynpAGlWjWiizA7m/MtLT9RoX6gjtpE500vfIg8jogAkDzdCxbsFASQ==", + "version": "5.19.0", + "resolved": "https://registry.npmjs.org/@typescript-eslint/eslint-plugin/-/eslint-plugin-5.19.0.tgz", + "integrity": "sha512-w59GpFqDYGnWFim9p6TGJz7a3qWeENJuAKCqjGSx+Hq/bwq3RZwXYqy98KIfN85yDqz9mq6QXiY5h0FjGQLyEg==", "dev": true, "requires": { - "@typescript-eslint/scope-manager": "5.17.0", - "@typescript-eslint/type-utils": "5.17.0", - "@typescript-eslint/utils": "5.17.0", + "@typescript-eslint/scope-manager": "5.19.0", + "@typescript-eslint/type-utils": "5.19.0", + "@typescript-eslint/utils": "5.19.0", "debug": "^4.3.2", "functional-red-black-tree": "^1.0.1", "ignore": "^5.1.8", @@ -7550,9 +7765,9 @@ }, "dependencies": { "semver": { - "version": "7.3.5", - "resolved": "https://registry.npmjs.org/semver/-/semver-7.3.5.tgz", - "integrity": "sha512-PoeGJYh8HK4BTO/a9Tf6ZG3veo/A7ZVsYrSA6J8ny9nb3B1VrpkuN+z9OE5wfE5p6H4LchYZsegiQgbJD94ZFQ==", + "version": "7.3.7", + "resolved": "https://registry.npmjs.org/semver/-/semver-7.3.7.tgz", + "integrity": "sha512-QlYTucUYOews+WeEujDoEGziz4K6c47V/Bd+LjSSYcA94p+DmINdf7ncaUinThfvZyu13lN9OY1XDxt8C0Tw0g==", "dev": true, "requires": { "lru-cache": "^6.0.0" @@ -7561,52 +7776,52 @@ } }, "@typescript-eslint/parser": { - "version": "5.17.0", - "resolved": "https://registry.npmjs.org/@typescript-eslint/parser/-/parser-5.17.0.tgz", - "integrity": "sha512-aRzW9Jg5Rlj2t2/crzhA2f23SIYFlF9mchGudyP0uiD6SenIxzKoLjwzHbafgHn39dNV/TV7xwQkLfFTZlJ4ig==", + "version": "5.19.0", + "resolved": "https://registry.npmjs.org/@typescript-eslint/parser/-/parser-5.19.0.tgz", + "integrity": "sha512-yhktJjMCJX8BSBczh1F/uY8wGRYrBeyn84kH6oyqdIJwTGKmzX5Qiq49LRQ0Jh0LXnWijEziSo6BRqny8nqLVQ==", "dev": true, "requires": { - "@typescript-eslint/scope-manager": "5.17.0", - "@typescript-eslint/types": "5.17.0", - "@typescript-eslint/typescript-estree": "5.17.0", + "@typescript-eslint/scope-manager": "5.19.0", + "@typescript-eslint/types": "5.19.0", + "@typescript-eslint/typescript-estree": "5.19.0", "debug": "^4.3.2" } }, "@typescript-eslint/scope-manager": { - "version": "5.17.0", - "resolved": "https://registry.npmjs.org/@typescript-eslint/scope-manager/-/scope-manager-5.17.0.tgz", - "integrity": "sha512-062iCYQF/doQ9T2WWfJohQKKN1zmmXVfAcS3xaiialiw8ZUGy05Em6QVNYJGO34/sU1a7a+90U3dUNfqUDHr3w==", + "version": "5.19.0", + "resolved": "https://registry.npmjs.org/@typescript-eslint/scope-manager/-/scope-manager-5.19.0.tgz", + "integrity": "sha512-Fz+VrjLmwq5fbQn5W7cIJZ066HxLMKvDEmf4eu1tZ8O956aoX45jAuBB76miAECMTODyUxH61AQM7q4/GOMQ5g==", "dev": true, "requires": { - "@typescript-eslint/types": "5.17.0", - "@typescript-eslint/visitor-keys": "5.17.0" + "@typescript-eslint/types": "5.19.0", + "@typescript-eslint/visitor-keys": "5.19.0" } }, "@typescript-eslint/type-utils": { - "version": "5.17.0", - "resolved": "https://registry.npmjs.org/@typescript-eslint/type-utils/-/type-utils-5.17.0.tgz", - "integrity": "sha512-3hU0RynUIlEuqMJA7dragb0/75gZmwNwFf/QJokWzPehTZousP/MNifVSgjxNcDCkM5HI2K22TjQWUmmHUINSg==", + "version": "5.19.0", + "resolved": "https://registry.npmjs.org/@typescript-eslint/type-utils/-/type-utils-5.19.0.tgz", + "integrity": "sha512-O6XQ4RI4rQcBGshTQAYBUIGsKqrKeuIOz9v8bckXZnSeXjn/1+BDZndHLe10UplQeJLXDNbaZYrAytKNQO2T4Q==", "dev": true, "requires": { - "@typescript-eslint/utils": "5.17.0", + "@typescript-eslint/utils": "5.19.0", "debug": "^4.3.2", "tsutils": "^3.21.0" } }, "@typescript-eslint/types": { - "version": "5.17.0", - "resolved": "https://registry.npmjs.org/@typescript-eslint/types/-/types-5.17.0.tgz", - "integrity": "sha512-AgQ4rWzmCxOZLioFEjlzOI3Ch8giDWx8aUDxyNw9iOeCvD3GEYAB7dxWGQy4T/rPVe8iPmu73jPHuaSqcjKvxw==", + "version": "5.19.0", + "resolved": "https://registry.npmjs.org/@typescript-eslint/types/-/types-5.19.0.tgz", + "integrity": "sha512-zR1ithF4Iyq1wLwkDcT+qFnhs8L5VUtjgac212ftiOP/ZZUOCuuF2DeGiZZGQXGoHA50OreZqLH5NjDcDqn34w==", "dev": true }, "@typescript-eslint/typescript-estree": { - "version": "5.17.0", - "resolved": "https://registry.npmjs.org/@typescript-eslint/typescript-estree/-/typescript-estree-5.17.0.tgz", - "integrity": "sha512-X1gtjEcmM7Je+qJRhq7ZAAaNXYhTgqMkR10euC4Si6PIjb+kwEQHSxGazXUQXFyqfEXdkGf6JijUu5R0uceQzg==", + "version": "5.19.0", + "resolved": "https://registry.npmjs.org/@typescript-eslint/typescript-estree/-/typescript-estree-5.19.0.tgz", + "integrity": "sha512-dRPuD4ocXdaE1BM/dNR21elSEUPKaWgowCA0bqJ6YbYkvtrPVEvZ+zqcX5a8ECYn3q5iBSSUcBBD42ubaOp0Hw==", "dev": true, "requires": { - "@typescript-eslint/types": "5.17.0", - "@typescript-eslint/visitor-keys": "5.17.0", + "@typescript-eslint/types": "5.19.0", + "@typescript-eslint/visitor-keys": "5.19.0", "debug": "^4.3.2", "globby": "^11.0.4", "is-glob": "^4.0.3", @@ -7635,9 +7850,9 @@ } }, "semver": { - "version": "7.3.5", - "resolved": "https://registry.npmjs.org/semver/-/semver-7.3.5.tgz", - "integrity": "sha512-PoeGJYh8HK4BTO/a9Tf6ZG3veo/A7ZVsYrSA6J8ny9nb3B1VrpkuN+z9OE5wfE5p6H4LchYZsegiQgbJD94ZFQ==", + "version": "7.3.7", + "resolved": "https://registry.npmjs.org/semver/-/semver-7.3.7.tgz", + "integrity": "sha512-QlYTucUYOews+WeEujDoEGziz4K6c47V/Bd+LjSSYcA94p+DmINdf7ncaUinThfvZyu13lN9OY1XDxt8C0Tw0g==", "dev": true, "requires": { "lru-cache": "^6.0.0" @@ -7646,26 +7861,26 @@ } }, "@typescript-eslint/utils": { - "version": "5.17.0", - "resolved": "https://registry.npmjs.org/@typescript-eslint/utils/-/utils-5.17.0.tgz", - "integrity": "sha512-DVvndq1QoxQH+hFv+MUQHrrWZ7gQ5KcJzyjhzcqB1Y2Xes1UQQkTRPUfRpqhS8mhTWsSb2+iyvDW1Lef5DD7vA==", + "version": "5.19.0", + "resolved": "https://registry.npmjs.org/@typescript-eslint/utils/-/utils-5.19.0.tgz", + "integrity": "sha512-ZuEckdupXpXamKvFz/Ql8YnePh2ZWcwz7APICzJL985Rp5C2AYcHO62oJzIqNhAMtMK6XvrlBTZeNG8n7gS3lQ==", "dev": true, "requires": { "@types/json-schema": "^7.0.9", - "@typescript-eslint/scope-manager": "5.17.0", - "@typescript-eslint/types": "5.17.0", - "@typescript-eslint/typescript-estree": "5.17.0", + "@typescript-eslint/scope-manager": "5.19.0", + "@typescript-eslint/types": "5.19.0", + "@typescript-eslint/typescript-estree": "5.19.0", "eslint-scope": "^5.1.1", "eslint-utils": "^3.0.0" } }, "@typescript-eslint/visitor-keys": { - "version": "5.17.0", - "resolved": "https://registry.npmjs.org/@typescript-eslint/visitor-keys/-/visitor-keys-5.17.0.tgz", - "integrity": "sha512-6K/zlc4OfCagUu7Am/BD5k8PSWQOgh34Nrv9Rxe2tBzlJ7uOeJ/h7ugCGDCeEZHT6k2CJBhbk9IsbkPI0uvUkA==", + "version": "5.19.0", + "resolved": "https://registry.npmjs.org/@typescript-eslint/visitor-keys/-/visitor-keys-5.19.0.tgz", + "integrity": "sha512-Ym7zZoMDZcAKWsULi2s7UMLREdVQdScPQ/fKWMYefarCztWlHPFVJo8racf8R0Gc8FAEJ2eD4of8As1oFtnQlQ==", "dev": true, "requires": { - "@typescript-eslint/types": "5.17.0", + "@typescript-eslint/types": "5.19.0", "eslint-visitor-keys": "^3.0.0" } }, @@ -7807,6 +8022,19 @@ "integrity": "sha1-r2rId6Jcx/dOBYiUdThY39sk/bY=", "dev": true }, + "array.prototype.map": { + "version": "1.0.4", + "resolved": "https://registry.npmjs.org/array.prototype.map/-/array.prototype.map-1.0.4.tgz", + "integrity": "sha512-Qds9QnX7A0qISY7JT5WuJO0NJPE9CMlC6JzHQfhpqAAQQzufVRoeH7EzUY5GcPTx72voG8LV/5eo+b8Qi8hmhA==", + "dev": true, + "requires": { + "call-bind": "^1.0.2", + "define-properties": "^1.1.3", + "es-abstract": "^1.19.0", + "es-array-method-boxes-properly": "^1.0.0", + "is-string": "^1.0.7" + } + }, "async": { "version": "2.6.3", "resolved": "https://registry.npmjs.org/async/-/async-2.6.3.tgz", @@ -7944,25 +8172,19 @@ "integrity": "sha512-E+XQCRwSbaaiChtv6k6Dwgc+bx+Bs6vuKJHHl5kox/BaKbhiXzqQOwK4cO22yElGp2OCmjwVhT3HmxgyPGnJfQ==", "dev": true }, - "cacheable-lookup": { - "version": "5.0.4", - "resolved": "https://registry.npmjs.org/cacheable-lookup/-/cacheable-lookup-5.0.4.tgz", - "integrity": "sha512-2/kNscPhpcxrOigMZzbiWF7dz8ilhb/nIHU3EyZiXWXpeq/au8qJ8VhdftMkty3n7Gj6HIGalQG8oiBNB3AJgA==", - "dev": true - }, "cacheable-request": { - "version": "7.0.2", - "resolved": "https://registry.npmjs.org/cacheable-request/-/cacheable-request-7.0.2.tgz", - "integrity": "sha512-pouW8/FmiPQbuGpkXQ9BAPv/Mo5xDGANgSNXzTzJ8DrKGuXOssM4wIQRjfanNRh3Yu5cfYPvcorqbhg2KIJtew==", + "version": "6.1.0", + "resolved": "https://registry.npmjs.org/cacheable-request/-/cacheable-request-6.1.0.tgz", + "integrity": "sha512-Oj3cAGPCqOZX7Rz64Uny2GYAZNliQSqfbePrgAQ1wKAihYmCUnraBtJtKcGR4xz7wF+LoJC+ssFZvv5BgF9Igg==", "dev": true, "requires": { "clone-response": "^1.0.2", "get-stream": "^5.1.0", "http-cache-semantics": "^4.0.0", - "keyv": "^4.0.0", + "keyv": "^3.0.0", "lowercase-keys": "^2.0.0", - "normalize-url": "^6.0.1", - "responselike": "^2.0.0" + "normalize-url": "^4.1.0", + "responselike": "^1.0.2" }, "dependencies": { "get-stream": { @@ -7973,6 +8195,12 @@ "requires": { "pump": "^3.0.0" } + }, + "lowercase-keys": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/lowercase-keys/-/lowercase-keys-2.0.0.tgz", + "integrity": "sha512-tqNXrS78oMOE73NMxK4EMLQsQowWf8jKooH9g7xPavRT706R6bkQJ6DY2Te7QukaZsulxa30wQ7bk0pm4XiHmA==", + "dev": true } } }, @@ -8011,9 +8239,9 @@ "dev": true }, "caniuse-lite": { - "version": "1.0.30001323", - "resolved": "https://registry.npmjs.org/caniuse-lite/-/caniuse-lite-1.0.30001323.tgz", - "integrity": "sha512-e4BF2RlCVELKx8+RmklSEIVub1TWrmdhvA5kEUueummz1XyySW0DVk+3x9HyhU9MuWTa2BhqLgEuEmUwASAdCA==", + "version": "1.0.30001331", + "resolved": "https://registry.npmjs.org/caniuse-lite/-/caniuse-lite-1.0.30001331.tgz", + "integrity": "sha512-Y1xk6paHpUXKP/P6YjQv1xqyTbgAP05ycHBcRdQjTcyXlWol868sJJPlmk5ylOekw2BrucWes5jk+LvVd7WZ5Q==", "dev": true }, "chalk": { @@ -8228,9 +8456,9 @@ "dev": true }, "debug": { - "version": "4.3.3", - "resolved": "https://registry.npmjs.org/debug/-/debug-4.3.3.tgz", - "integrity": "sha512-/zxw5+vh1Tfv+4Qn7a5nsbcJKPaSvCDhojn6FEl9vupwK2VCSDtEiEtqr8DFtzYFOdz63LBkxec7DYuc2jon6Q==", + "version": "4.3.4", + "resolved": "https://registry.npmjs.org/debug/-/debug-4.3.4.tgz", + "integrity": "sha512-PRWFHuSU3eDtQJPvnNY7Jcket1j0t5OuOsFzPPzsekD52Zl8qUfFIPEiswXqIvHWGVHOgX+7G/vCNNhehwxfkQ==", "dev": true, "requires": { "ms": "2.1.2" @@ -8249,20 +8477,12 @@ "dev": true }, "decompress-response": { - "version": "6.0.0", - "resolved": "https://registry.npmjs.org/decompress-response/-/decompress-response-6.0.0.tgz", - "integrity": "sha512-aW35yZM6Bb/4oJlZncMH2LCoZtJXTRxES17vE3hoRiowU2kWHaJKFkSBDnDR+cm9J+9QhXmREyIfv0pji9ejCQ==", + "version": "3.3.0", + "resolved": "https://registry.npmjs.org/decompress-response/-/decompress-response-3.3.0.tgz", + "integrity": "sha1-gKTdMjdIOEv6JICDYirt7Jgq3/M=", "dev": true, "requires": { - "mimic-response": "^3.1.0" - }, - "dependencies": { - "mimic-response": { - "version": "3.1.0", - "resolved": "https://registry.npmjs.org/mimic-response/-/mimic-response-3.1.0.tgz", - "integrity": "sha512-z0yWI+4FDrrweS8Zmt4Ej5HdJmky15+L2e6Wgn3+iK5fWzb6T3fhNFq2+MeTRb064c6Wr4N/wv0DzQTjNzHNGQ==", - "dev": true - } + "mimic-response": "^1.0.0" } }, "deep-extend": { @@ -8296,11 +8516,20 @@ } }, "defer-to-connect": { - "version": "2.0.1", - "resolved": "https://registry.npmjs.org/defer-to-connect/-/defer-to-connect-2.0.1.tgz", - "integrity": "sha512-4tvttepXG1VaYGrRibk5EwJd1t4udunSOVMdLSAL6mId1ix438oPwPZMALY41FCijukO1L0twNcGsdzS7dHgDg==", + "version": "1.1.3", + "resolved": "https://registry.npmjs.org/defer-to-connect/-/defer-to-connect-1.1.3.tgz", + "integrity": "sha512-0ISdNousHvZT2EiFlZeZAHBUvSxmKswVCEf8hW7KWgG4a8MVEu/3Vb6uWYozkjylyCxe0JBIiRB1jV45S70WVQ==", "dev": true }, + "define-properties": { + "version": "1.1.3", + "resolved": "https://registry.npmjs.org/define-properties/-/define-properties-1.1.3.tgz", + "integrity": "sha512-3MqfYKj2lLzdMSf8ZIZE/V+Zuy+BgD6f164e8K2w7dgnpKArBDerGYpM46IYYcjnkdPNMjPk9A6VFB8+3SKlXQ==", + "dev": true, + "requires": { + "object-keys": "^1.0.12" + } + }, "delayed-stream": { "version": "1.0.0", "resolved": "https://registry.npmjs.org/delayed-stream/-/delayed-stream-1.0.0.tgz", @@ -8353,9 +8582,9 @@ "dev": true }, "electron-to-chromium": { - "version": "1.4.103", - "resolved": "https://registry.npmjs.org/electron-to-chromium/-/electron-to-chromium-1.4.103.tgz", - "integrity": "sha512-c/uKWR1Z/W30Wy/sx3dkZoj4BijbXX85QKWu9jJfjho3LBAXNEGAEW3oWiGb+dotA6C6BzCTxL2/aLes7jlUeg==", + "version": "1.4.107", + "resolved": "https://registry.npmjs.org/electron-to-chromium/-/electron-to-chromium-1.4.107.tgz", + "integrity": "sha512-Huen6taaVrUrSy8o7mGStByba8PfOWWluHNxSHGBrCgEdFVLtvdQDBr9LBCF9Uci8SYxh28QNNMO0oC17wbGAg==", "dev": true }, "email-addresses": { @@ -8388,6 +8617,67 @@ "is-arrayish": "^0.2.1" } }, + "es-abstract": { + "version": "1.19.4", + "resolved": "https://registry.npmjs.org/es-abstract/-/es-abstract-1.19.4.tgz", + "integrity": "sha512-flV8e5g9/xulChMG48Fygk1ptpo4lQRJ0eJYtxJFgi7pklLx7EFcOJ34jnvr8pbWlaFN/AT1cZpe0hiFel9Hqg==", + "dev": true, + "requires": { + "call-bind": "^1.0.2", + "es-to-primitive": "^1.2.1", + "function-bind": "^1.1.1", + "get-intrinsic": "^1.1.1", + "get-symbol-description": "^1.0.0", + "has": "^1.0.3", + "has-symbols": "^1.0.3", + "internal-slot": "^1.0.3", + "is-callable": "^1.2.4", + "is-negative-zero": "^2.0.2", + "is-regex": "^1.1.4", + "is-shared-array-buffer": "^1.0.2", + "is-string": "^1.0.7", + "is-weakref": "^1.0.2", + "object-inspect": "^1.12.0", + "object-keys": "^1.1.1", + "object.assign": "^4.1.2", + "string.prototype.trimend": "^1.0.4", + "string.prototype.trimstart": "^1.0.4", + "unbox-primitive": "^1.0.1" + } + }, + "es-array-method-boxes-properly": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/es-array-method-boxes-properly/-/es-array-method-boxes-properly-1.0.0.tgz", + "integrity": "sha512-wd6JXUmyHmt8T5a2xreUwKcGPq6f1f+WwIJkijUqiGcJz1qqnZgP6XIK+QyIWU5lT7imeNxUll48bziG+TSYcA==", + "dev": true + }, + "es-get-iterator": { + "version": "1.1.2", + "resolved": "https://registry.npmjs.org/es-get-iterator/-/es-get-iterator-1.1.2.tgz", + "integrity": "sha512-+DTO8GYwbMCwbywjimwZMHp8AuYXOS2JZFWoi2AlPOS3ebnII9w/NLpNZtA7A0YLaVDw+O7KFCeoIV7OPvM7hQ==", + "dev": true, + "requires": { + "call-bind": "^1.0.2", + "get-intrinsic": "^1.1.0", + "has-symbols": "^1.0.1", + "is-arguments": "^1.1.0", + "is-map": "^2.0.2", + "is-set": "^2.0.2", + "is-string": "^1.0.5", + "isarray": "^2.0.5" + } + }, + "es-to-primitive": { + "version": "1.2.1", + "resolved": "https://registry.npmjs.org/es-to-primitive/-/es-to-primitive-1.2.1.tgz", + "integrity": "sha512-QCOllgZJtaUo9miYBcLChTUaHNjJF3PYs1VidD7AwiEj1kYxKeQTctLAezAOH5ZKRH0g2IgPn6KwB4IT8iRpvA==", + "dev": true, + "requires": { + "is-callable": "^1.1.4", + "is-date-object": "^1.0.1", + "is-symbol": "^1.0.2" + } + }, "es6-error": { "version": "4.1.1", "resolved": "https://registry.npmjs.org/es6-error/-/es6-error-4.1.1.tgz", @@ -8413,9 +8703,9 @@ "dev": true }, "eslint": { - "version": "8.12.0", - "resolved": "https://registry.npmjs.org/eslint/-/eslint-8.12.0.tgz", - "integrity": "sha512-it1oBL9alZg1S8UycLm5YDMAkIhtH6FtAzuZs6YvoGVldWjbS08BkAdb/ymP9LlAyq8koANu32U7Ib/w+UNh8Q==", + "version": "8.13.0", + "resolved": "https://registry.npmjs.org/eslint/-/eslint-8.13.0.tgz", + "integrity": "sha512-D+Xei61eInqauAyTJ6C0q6x9mx7kTUC1KZ0m0LSEexR0V+e94K12LmWX076ZIsldwfQ2RONdaJe0re0TRGQbRQ==", "dev": true, "requires": { "@eslint/eslintrc": "^1.2.1", @@ -8854,6 +9144,16 @@ "integrity": "sha512-ts6Wi+2j3jQjqi70w5AlN8DFnkSwC+MqmxEzdEALB2qXZYV3X/b1CTfgPLGJNMeAWxdPfU8FO1ms3NUfaHCPYg==", "dev": true }, + "get-symbol-description": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/get-symbol-description/-/get-symbol-description-1.0.0.tgz", + "integrity": "sha512-2EmdH1YvIQiZpltCNgkuiUnyukzxM/R6NDJX31Ke3BG1Nq5b0S2PhX59UKi9vZpPDQVdqn+1IcaAwnzTT5vCjw==", + "dev": true, + "requires": { + "call-bind": "^1.0.2", + "get-intrinsic": "^1.1.1" + } + }, "gh-pages": { "version": "3.2.3", "resolved": "https://registry.npmjs.org/gh-pages/-/gh-pages-3.2.3.tgz", @@ -8951,28 +9251,39 @@ } }, "got": { - "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", - "@szmarczak/http-timer": "^4.0.5", - "@types/cacheable-request": "^6.0.1", - "@types/responselike": "^1.0.0", - "cacheable-lookup": "^5.0.3", - "cacheable-request": "^7.0.2", - "decompress-response": "^6.0.0", - "http2-wrapper": "^1.0.0-beta.5.2", - "lowercase-keys": "^2.0.0", - "p-cancelable": "^2.0.0", - "responselike": "^2.0.0" + "version": "9.6.0", + "resolved": "https://registry.npmjs.org/got/-/got-9.6.0.tgz", + "integrity": "sha512-R7eWptXuGYxwijs0eV+v3o6+XH1IqVK8dJOEecQfTmkncw9AV4dcw/Dhxi8MdlqPthxxpZyizMzyg8RTmEsG+Q==", + "dev": true, + "requires": { + "@sindresorhus/is": "^0.14.0", + "@szmarczak/http-timer": "^1.1.2", + "cacheable-request": "^6.0.0", + "decompress-response": "^3.3.0", + "duplexer3": "^0.1.4", + "get-stream": "^4.1.0", + "lowercase-keys": "^1.0.1", + "mimic-response": "^1.0.1", + "p-cancelable": "^1.0.0", + "to-readable-stream": "^1.0.0", + "url-parse-lax": "^3.0.0" + }, + "dependencies": { + "get-stream": { + "version": "4.1.0", + "resolved": "https://registry.npmjs.org/get-stream/-/get-stream-4.1.0.tgz", + "integrity": "sha512-GMat4EJ5161kIy2HevLlr4luNjBgvmj413KaQA7jt4V8B4RDsfpHk7WQ9GVqfYyyx8OS/L66Kox+rJRNklLK7w==", + "dev": true, + "requires": { + "pump": "^3.0.0" + } + } } }, "graceful-fs": { - "version": "4.2.9", - "resolved": "https://registry.npmjs.org/graceful-fs/-/graceful-fs-4.2.9.tgz", - "integrity": "sha512-NtNxqUcXgpW2iMrfqSfR73Glt39K+BLwWsPs94yR63v45T0Wbej7eRmL5cWfwEgqXnmjQp3zaJTshdRW/qC2ZQ==", + "version": "4.2.10", + "resolved": "https://registry.npmjs.org/graceful-fs/-/graceful-fs-4.2.10.tgz", + "integrity": "sha512-9ByhssR2fPVsNZj478qUUbKfmL0+t5BDVyjShtyZZLiK7ZDAArFFfopyOTj0M05wE2tJPisA4iTnnXl2YoPvOA==", "dev": true }, "growl": { @@ -8990,6 +9301,12 @@ "function-bind": "^1.1.1" } }, + "has-bigints": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/has-bigints/-/has-bigints-1.0.1.tgz", + "integrity": "sha512-LSBS2LjbNBTf6287JEbEzvJgftkF5qFkmCo9hDRpAzKhUOlJ+hx8dd4USs00SgsUNwc4617J9ki5YtEClM2ffA==", + "dev": true + }, "has-flag": { "version": "4.0.0", "resolved": "https://registry.npmjs.org/has-flag/-/has-flag-4.0.0.tgz", @@ -9002,6 +9319,15 @@ "integrity": "sha512-l3LCuF6MgDNwTDKkdYGEihYjt5pRPbEg46rtlmnSPlUbgmB8LOIrKJbYYFBSbnPaJexMKtiPO8hmeRjRz2Td+A==", "dev": true }, + "has-tostringtag": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/has-tostringtag/-/has-tostringtag-1.0.0.tgz", + "integrity": "sha512-kFjcSNhnlGV1kyoGk7OXKSawH5JOb/LzUc5w9B02hOTO0dfFRjbHQKvg1d6cf3HbeUmtU9VbbV3qzZ2Teh97WQ==", + "dev": true, + "requires": { + "has-symbols": "^1.0.2" + } + }, "has-yarn": { "version": "2.1.0", "resolved": "https://registry.npmjs.org/has-yarn/-/has-yarn-2.1.0.tgz", @@ -9044,16 +9370,6 @@ "integrity": "sha512-carPklcUh7ROWRK7Cv27RPtdhYhUsela/ue5/jKzjegVvXDqM2ILE9Q2BGn9JZJh1g87cp56su/FgQSzcWS8cQ==", "dev": true }, - "http2-wrapper": { - "version": "1.0.3", - "resolved": "https://registry.npmjs.org/http2-wrapper/-/http2-wrapper-1.0.3.tgz", - "integrity": "sha512-V+23sDMr12Wnz7iTcDeJr3O6AIxlnvT/bmaAAAP/Xda35C90p9599p0F1eHR/N1KILWSoWVAiOMFjBBXaXSMxg==", - "dev": true, - "requires": { - "quick-lru": "^5.1.1", - "resolve-alpn": "^1.0.0" - } - }, "human-signals": { "version": "2.1.0", "resolved": "https://registry.npmjs.org/human-signals/-/human-signals-2.1.0.tgz", @@ -9179,18 +9495,48 @@ "through": "^2.3.6" } }, + "internal-slot": { + "version": "1.0.3", + "resolved": "https://registry.npmjs.org/internal-slot/-/internal-slot-1.0.3.tgz", + "integrity": "sha512-O0DB1JC/sPyZl7cIo78n5dR7eUSwwpYPiXRhTzNxZVAMUuB8vlnRFyLxdrVToks6XPLVnFfbzaVd5WLjhgg+vA==", + "dev": true, + "requires": { + "get-intrinsic": "^1.1.0", + "has": "^1.0.3", + "side-channel": "^1.0.4" + } + }, "interpret": { "version": "1.4.0", "resolved": "https://registry.npmjs.org/interpret/-/interpret-1.4.0.tgz", "integrity": "sha512-agE4QfB2Lkp9uICn7BAqoscw4SZP9kTE2hxiFI3jBPmXJfdqiahTbUuKGsMoN2GtqL9AxhYioAcVvgsb1HvRbA==", "dev": true }, + "is-arguments": { + "version": "1.1.1", + "resolved": "https://registry.npmjs.org/is-arguments/-/is-arguments-1.1.1.tgz", + "integrity": "sha512-8Q7EARjzEnKpt/PCD7e1cgUS0a6X8u5tdSiMqXhojOdoV9TsMsiO+9VLC5vAmO8N7/GmXn7yjR8qnA6bVAEzfA==", + "dev": true, + "requires": { + "call-bind": "^1.0.2", + "has-tostringtag": "^1.0.0" + } + }, "is-arrayish": { "version": "0.2.1", "resolved": "https://registry.npmjs.org/is-arrayish/-/is-arrayish-0.2.1.tgz", "integrity": "sha1-d8mYQFJ6qOyxqLppe4BkWnqSap0=", "dev": true }, + "is-bigint": { + "version": "1.0.4", + "resolved": "https://registry.npmjs.org/is-bigint/-/is-bigint-1.0.4.tgz", + "integrity": "sha512-zB9CruMamjym81i2JZ3UMn54PKGsQzsJeo6xvN3HJJ4CAsQNB6iRutp2To77OfCNuoxspsIhzaPoO1zyCEhFOg==", + "dev": true, + "requires": { + "has-bigints": "^1.0.1" + } + }, "is-binary-path": { "version": "2.1.0", "resolved": "https://registry.npmjs.org/is-binary-path/-/is-binary-path-2.1.0.tgz", @@ -9200,6 +9546,22 @@ "binary-extensions": "^2.0.0" } }, + "is-boolean-object": { + "version": "1.1.2", + "resolved": "https://registry.npmjs.org/is-boolean-object/-/is-boolean-object-1.1.2.tgz", + "integrity": "sha512-gDYaKHJmnj4aWxyj6YHyXVpdQawtVLHU5cb+eztPGczf6cjuTdwve5ZIEfgXqH4e57An1D1AKf8CZ3kYrQRqYA==", + "dev": true, + "requires": { + "call-bind": "^1.0.2", + "has-tostringtag": "^1.0.0" + } + }, + "is-callable": { + "version": "1.2.4", + "resolved": "https://registry.npmjs.org/is-callable/-/is-callable-1.2.4.tgz", + "integrity": "sha512-nsuwtxZfMX67Oryl9LCQ+upnC0Z0BgpwntpS89m1H/TLF0zNfzfLMV/9Wa/6MZsj0acpEjAO0KF1xT6ZdLl95w==", + "dev": true + }, "is-ci": { "version": "3.0.1", "resolved": "https://registry.npmjs.org/is-ci/-/is-ci-3.0.1.tgz", @@ -9218,6 +9580,15 @@ "has": "^1.0.3" } }, + "is-date-object": { + "version": "1.0.5", + "resolved": "https://registry.npmjs.org/is-date-object/-/is-date-object-1.0.5.tgz", + "integrity": "sha512-9YQaSxsAiSwcvS33MBk3wTCVnWK+HhF8VZR2jRxehM16QcVOdHqPn4VPHmRK4lSr38n9JriurInLcP90xsYNfQ==", + "dev": true, + "requires": { + "has-tostringtag": "^1.0.0" + } + }, "is-docker": { "version": "2.2.1", "resolved": "https://registry.npmjs.org/is-docker/-/is-docker-2.2.1.tgz", @@ -9261,6 +9632,18 @@ "integrity": "sha512-2HvIEKRoqS62guEC+qBjpvRubdX910WCMuJTZ+I9yvqKU2/12eSL549HMwtabb4oupdj2sMP50k+XJfB/8JE6w==", "dev": true }, + "is-map": { + "version": "2.0.2", + "resolved": "https://registry.npmjs.org/is-map/-/is-map-2.0.2.tgz", + "integrity": "sha512-cOZFQQozTha1f4MxLFzlgKYPTyj26picdZTx82hbc/Xf4K/tZOOXSCkMvU4pKioRXGDLJRn0GM7Upe7kR721yg==", + "dev": true + }, + "is-negative-zero": { + "version": "2.0.2", + "resolved": "https://registry.npmjs.org/is-negative-zero/-/is-negative-zero-2.0.2.tgz", + "integrity": "sha512-dqJvarLawXsFbNDeJW7zAz8ItJ9cd28YufuuFzh0G8pNHjJMnY08Dv7sYX2uF5UpQOwieAeOExEYAWWfu7ZZUA==", + "dev": true + }, "is-npm": { "version": "5.0.0", "resolved": "https://registry.npmjs.org/is-npm/-/is-npm-5.0.0.tgz", @@ -9273,6 +9656,15 @@ "integrity": "sha512-41Cifkg6e8TylSpdtTpeLVMqvSBEVzTttHvERD741+pnZ8ANv0004MRL43QKPDlK9cGvNp6NZWZUBlbGXYxxng==", "dev": true }, + "is-number-object": { + "version": "1.0.7", + "resolved": "https://registry.npmjs.org/is-number-object/-/is-number-object-1.0.7.tgz", + "integrity": "sha512-k1U0IRzLMo7ZlYIfzRu23Oh6MiIFasgpb9X76eqfFZAqwH44UI4KTBvBYIZ1dSL9ZzChTB9ShHfLkR4pdW5krQ==", + "dev": true, + "requires": { + "has-tostringtag": "^1.0.0" + } + }, "is-obj": { "version": "2.0.0", "resolved": "https://registry.npmjs.org/is-obj/-/is-obj-2.0.0.tgz", @@ -9297,6 +9689,31 @@ "integrity": "sha512-VRSzKkbMm5jMDoKLbltAkFQ5Qr7VDiTFGXxYFXXowVj387GeGNOCsOH6Msy00SGZ3Fp84b1Naa1psqgcCIEP5Q==", "dev": true }, + "is-regex": { + "version": "1.1.4", + "resolved": "https://registry.npmjs.org/is-regex/-/is-regex-1.1.4.tgz", + "integrity": "sha512-kvRdxDsxZjhzUX07ZnLydzS1TU/TJlTUHHY4YLL87e37oUA49DfkLqgy+VjFocowy29cKvcSiu+kIv728jTTVg==", + "dev": true, + "requires": { + "call-bind": "^1.0.2", + "has-tostringtag": "^1.0.0" + } + }, + "is-set": { + "version": "2.0.2", + "resolved": "https://registry.npmjs.org/is-set/-/is-set-2.0.2.tgz", + "integrity": "sha512-+2cnTEZeY5z/iXGbLhPrOAaK/Mau5k5eXq9j14CpRTftq0pAJu2MwVRSZhyZWBzx3o6X795Lz6Bpb6R0GKf37g==", + "dev": true + }, + "is-shared-array-buffer": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/is-shared-array-buffer/-/is-shared-array-buffer-1.0.2.tgz", + "integrity": "sha512-sqN2UDu1/0y6uvXyStCOzyhAjCSlHceFoMKJW8W9EU9cvic/QdsZ0kEU93HEy3IUEFZIiH/3w+AH/UQbPHNdhA==", + "dev": true, + "requires": { + "call-bind": "^1.0.2" + } + }, "is-ssh": { "version": "1.3.3", "resolved": "https://registry.npmjs.org/is-ssh/-/is-ssh-1.3.3.tgz", @@ -9312,6 +9729,24 @@ "integrity": "sha512-hFoiJiTl63nn+kstHGBtewWSKnQLpyb155KHheA1l39uvtO9nWIop1p3udqPcUd/xbF1VLMO4n7OI6p7RbngDg==", "dev": true }, + "is-string": { + "version": "1.0.7", + "resolved": "https://registry.npmjs.org/is-string/-/is-string-1.0.7.tgz", + "integrity": "sha512-tE2UXzivje6ofPW7l23cjDOMa09gb7xlAqG6jG5ej6uPV32TlWP3NKPigtaGeHNu9fohccRYvIiZMfOOnOYUtg==", + "dev": true, + "requires": { + "has-tostringtag": "^1.0.0" + } + }, + "is-symbol": { + "version": "1.0.4", + "resolved": "https://registry.npmjs.org/is-symbol/-/is-symbol-1.0.4.tgz", + "integrity": "sha512-C/CPBqKWnvdcxqIARxyOh4v1UUEOCHpgDa0WYgpKDFMszcrPcffg5uhwSgPCLD2WWxmq6isisz87tzT01tuGhg==", + "dev": true, + "requires": { + "has-symbols": "^1.0.2" + } + }, "is-typedarray": { "version": "1.0.0", "resolved": "https://registry.npmjs.org/is-typedarray/-/is-typedarray-1.0.0.tgz", @@ -9324,6 +9759,15 @@ "integrity": "sha512-knxG2q4UC3u8stRGyAVJCOdxFmv5DZiRcdlIaAQXAbSfJya+OhopNotLQrstBhququ4ZpuKbDc/8S6mgXgPFPw==", "dev": true }, + "is-weakref": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/is-weakref/-/is-weakref-1.0.2.tgz", + "integrity": "sha512-qctsuLZmIQ0+vSSMfoVvyFe2+GSEvnmZ2ezTup1SBse9+twCCeial6EEi3Nc2KFcf6+qz2FBPnjXsk8xhKSaPQ==", + "dev": true, + "requires": { + "call-bind": "^1.0.2" + } + }, "is-windows": { "version": "1.0.2", "resolved": "https://registry.npmjs.org/is-windows/-/is-windows-1.0.2.tgz", @@ -9346,9 +9790,9 @@ "dev": true }, "isarray": { - "version": "0.0.1", - "resolved": "https://registry.npmjs.org/isarray/-/isarray-0.0.1.tgz", - "integrity": "sha1-ihis/Kmo9Bd+Cav8YDiTmwXR7t8=", + "version": "2.0.5", + "resolved": "https://registry.npmjs.org/isarray/-/isarray-2.0.5.tgz", + "integrity": "sha512-xHjhDr3cNBK0BzdUJSPXZntQUx/mwMS5Rw4A7lPJ90XGAO6ISP/ePDNuo0vhqOZU+UD5JoodwCAAoZQd3FeAKw==", "dev": true }, "isexe": { @@ -9447,6 +9891,22 @@ "istanbul-lib-report": "^3.0.0" } }, + "iterate-iterator": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/iterate-iterator/-/iterate-iterator-1.0.2.tgz", + "integrity": "sha512-t91HubM4ZDQ70M9wqp+pcNpu8OyJ9UAtXntT/Bcsvp5tZMnz9vRa+IunKXeI8AnfZMTv0jNuVEmGeLSMjVvfPw==", + "dev": true + }, + "iterate-value": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/iterate-value/-/iterate-value-1.0.2.tgz", + "integrity": "sha512-A6fMAio4D2ot2r/TYzr4yUWrmwNdsN5xL7+HUiyACE4DXm+q8HtPcnFTp+NnW3k4N05tZ7FVYFFb2CR13NxyHQ==", + "dev": true, + "requires": { + "es-get-iterator": "^1.0.2", + "iterate-iterator": "^1.0.1" + } + }, "js-tokens": { "version": "4.0.0", "resolved": "https://registry.npmjs.org/js-tokens/-/js-tokens-4.0.0.tgz", @@ -9469,9 +9929,9 @@ "dev": true }, "json-buffer": { - "version": "3.0.1", - "resolved": "https://registry.npmjs.org/json-buffer/-/json-buffer-3.0.1.tgz", - "integrity": "sha512-4bV5BfR2mqfQTJm+V5tPPdf+ZpuhiIvTuAB5g8kcrXOZpTT/QwwVRWBywX1ozr6lEuPdbHxwaJlm9G6mI2sfSQ==", + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/json-buffer/-/json-buffer-3.0.0.tgz", + "integrity": "sha1-Wx85evx11ne96Lz8Dkfh+aPZqJg=", "dev": true }, "json-parse-even-better-errors": { @@ -9520,12 +9980,12 @@ "dev": true }, "keyv": { - "version": "4.1.1", - "resolved": "https://registry.npmjs.org/keyv/-/keyv-4.1.1.tgz", - "integrity": "sha512-tGv1yP6snQVDSM4X6yxrv2zzq/EvpW+oYiUz6aueW1u9CtS8RzUQYxxmFwgZlO2jSgCxQbchhxaqXXp2hnKGpQ==", + "version": "3.1.0", + "resolved": "https://registry.npmjs.org/keyv/-/keyv-3.1.0.tgz", + "integrity": "sha512-9ykJ/46SN/9KPM/sichzQ7OvXyGDYKGTaDlKMGCAlg2UK8KRy4jb0d8sFc+0Tt0YYnThq8X2RZgCg74RPxgcVA==", "dev": true, "requires": { - "json-buffer": "3.0.1" + "json-buffer": "3.0.0" } }, "latest-version": { @@ -9597,9 +10057,9 @@ } }, "lowercase-keys": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/lowercase-keys/-/lowercase-keys-2.0.0.tgz", - "integrity": "sha512-tqNXrS78oMOE73NMxK4EMLQsQowWf8jKooH9g7xPavRT706R6bkQJ6DY2Te7QukaZsulxa30wQ7bk0pm4XiHmA==", + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/lowercase-keys/-/lowercase-keys-1.0.1.tgz", + "integrity": "sha512-G2Lj61tXDnVFFOi8VZds+SoQjtQC3dgokKdDG2mTm1tx4m50NUHBOZSBwQQHyy0V12A0JTG4icfZQH+xPyh8VA==", "dev": true }, "lru-cache": { @@ -9639,9 +10099,9 @@ "dev": true }, "marked": { - "version": "4.0.12", - "resolved": "https://registry.npmjs.org/marked/-/marked-4.0.12.tgz", - "integrity": "sha512-hgibXWrEDNBWgGiK18j/4lkS6ihTe9sxtV4Q1OQppb/0zzyPSzoFANBa5MfsG/zgsWklmNnhm0XACZOH/0HBiQ==", + "version": "4.0.14", + "resolved": "https://registry.npmjs.org/marked/-/marked-4.0.14.tgz", + "integrity": "sha512-HL5sSPE/LP6U9qKgngIIPTthuxC0jrfxpYMZ3LdGDD3vTnLs59m2Z7r6+LNDR3ToqEQdkKd6YaaEfJhodJmijQ==", "dev": true }, "merge-stream": { @@ -9667,18 +10127,18 @@ } }, "mime-db": { - "version": "1.51.0", - "resolved": "https://registry.npmjs.org/mime-db/-/mime-db-1.51.0.tgz", - "integrity": "sha512-5y8A56jg7XVQx2mbv1lu49NR4dokRnhZYTtL+KGfaa27uq4pSTXkwQkFJl4pkRMyNFz/EtYDSkiiEHx3F7UN6g==", + "version": "1.52.0", + "resolved": "https://registry.npmjs.org/mime-db/-/mime-db-1.52.0.tgz", + "integrity": "sha512-sPU4uV7dYlvtWJxwwxHD0PuihVNiE7TyAbQ5SWxDCB9mUYvOgroQOwYQQOKPJ8CIbE+1ETVlOoK1UC2nU3gYvg==", "dev": true }, "mime-types": { - "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==", + "version": "2.1.35", + "resolved": "https://registry.npmjs.org/mime-types/-/mime-types-2.1.35.tgz", + "integrity": "sha512-ZDY+bPm5zTTF+YpCrAU9nK0UgICYPT0QtT1NZWFv4s++TNkcgVaT0g6+4R2uI4MjQjzysHB1zxuWL50hzaeXiw==", "dev": true, "requires": { - "mime-db": "1.51.0" + "mime-db": "1.52.0" } }, "mimic-fn": { @@ -9740,6 +10200,23 @@ "yargs-unparser": "2.0.0" }, "dependencies": { + "debug": { + "version": "4.3.3", + "resolved": "https://registry.npmjs.org/debug/-/debug-4.3.3.tgz", + "integrity": "sha512-/zxw5+vh1Tfv+4Qn7a5nsbcJKPaSvCDhojn6FEl9vupwK2VCSDtEiEtqr8DFtzYFOdz63LBkxec7DYuc2jon6Q==", + "dev": true, + "requires": { + "ms": "2.1.2" + }, + "dependencies": { + "ms": { + "version": "2.1.2", + "resolved": "https://registry.npmjs.org/ms/-/ms-2.1.2.tgz", + "integrity": "sha512-sGkPx+VjMtmA6MX27oA4FBFELFCZZ4S4XqeGOXCv68tT+jb3vk/RyaKWP0PTKyWtmLSM0b+adUTEvbs1PEaH2w==", + "dev": true + } + } + }, "escape-string-regexp": { "version": "4.0.0", "resolved": "https://registry.npmjs.org/escape-string-regexp/-/escape-string-regexp-4.0.0.tgz", @@ -9909,9 +10386,9 @@ } }, "node-releases": { - "version": "2.0.2", - "resolved": "https://registry.npmjs.org/node-releases/-/node-releases-2.0.2.tgz", - "integrity": "sha512-XxYDdcQ6eKqp/YjI+tb2C5WM2LgjnZrfYg4vgQt49EK268b6gYCHsBLrK2qvJo4FmCtqmKezb0WZFK4fkrZNsg==", + "version": "2.0.3", + "resolved": "https://registry.npmjs.org/node-releases/-/node-releases-2.0.3.tgz", + "integrity": "sha512-maHFz6OLqYxz+VQyCAtA3PTX4UP/53pa05fyDNc9CwjvJ0yEh6+xBwKsgCxMNhS8taUKBFYxfuiaD9U/55iFaw==", "dev": true }, "normalize-path": { @@ -9921,9 +10398,9 @@ "dev": true }, "normalize-url": { - "version": "6.1.0", - "resolved": "https://registry.npmjs.org/normalize-url/-/normalize-url-6.1.0.tgz", - "integrity": "sha512-DlL+XwOy3NxAQ8xuC0okPgK46iuVNAK01YN7RueYBqqFeGsBjV9XmCAzAdgt+667bCl5kPh9EqKKDwnaPG1I7A==", + "version": "4.5.1", + "resolved": "https://registry.npmjs.org/normalize-url/-/normalize-url-4.5.1.tgz", + "integrity": "sha512-9UZCFRHQdNrfTpGg8+1INIg93B6zE0aXMVFkw1WFwvO4SlZywU6aLg5Of0Ap/PgcbSw4LNxvMWXMeugwMCX0AA==", "dev": true }, "npm-run-path": { @@ -10053,6 +10530,24 @@ "integrity": "sha512-Ho2z80bVIvJloH+YzRmpZVQe87+qASmBUKZDWgx9cu+KDrX2ZDH/3tMy+gXbZETVGs2M8YdxObOh7XAtim9Y0g==", "dev": true }, + "object-keys": { + "version": "1.1.1", + "resolved": "https://registry.npmjs.org/object-keys/-/object-keys-1.1.1.tgz", + "integrity": "sha512-NuAESUOUMrlIXOfHKzD6bpPu3tYt3xvjNdRIQ+FeT0lNb4K8WR70CaDxhuNguS2XG+GjkyMwOzsN5ZktImfhLA==", + "dev": true + }, + "object.assign": { + "version": "4.1.2", + "resolved": "https://registry.npmjs.org/object.assign/-/object.assign-4.1.2.tgz", + "integrity": "sha512-ixT2L5THXsApyiUPYKmW+2EHpXXe5Ii3M+f4e+aJFAHao5amFRW6J0OO6c/LU8Be47utCx2GL89hxGB6XSmKuQ==", + "dev": true, + "requires": { + "call-bind": "^1.0.0", + "define-properties": "^1.1.3", + "has-symbols": "^1.0.1", + "object-keys": "^1.1.1" + } + }, "once": { "version": "1.4.0", "resolved": "https://registry.npmjs.org/once/-/once-1.4.0.tgz", @@ -10129,9 +10624,9 @@ "dev": true }, "p-cancelable": { - "version": "2.1.1", - "resolved": "https://registry.npmjs.org/p-cancelable/-/p-cancelable-2.1.1.tgz", - "integrity": "sha512-BZOr3nRQHOntUjTrH8+Lh54smKHoHyur8We1V8DSMVrl5A2malOOwuJRnKRDjSnkoeBh4at6BwEnb5I7Jl31wg==", + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/p-cancelable/-/p-cancelable-1.1.0.tgz", + "integrity": "sha512-s73XxOZ4zpt1edZYZzvhqFa6uvQc1vwUa0K0BdtIZgQMAJj9IbebH+JkgKZc9h+B05PKHLOTl4ajG1BmNrVZlw==", "dev": true }, "p-limit": { @@ -10189,144 +10684,6 @@ "registry-auth-token": "^4.0.0", "registry-url": "^5.0.0", "semver": "^6.2.0" - }, - "dependencies": { - "@sindresorhus/is": { - "version": "0.14.0", - "resolved": "https://registry.npmjs.org/@sindresorhus/is/-/is-0.14.0.tgz", - "integrity": "sha512-9NET910DNaIPngYnLLPeg+Ogzqsi9uM4mSboU5y6p8S5DzMTVEsJZrawi+BoDNUVBa2DhJqQYUFvMDfgU062LQ==", - "dev": true - }, - "@szmarczak/http-timer": { - "version": "1.1.2", - "resolved": "https://registry.npmjs.org/@szmarczak/http-timer/-/http-timer-1.1.2.tgz", - "integrity": "sha512-XIB2XbzHTN6ieIjfIMV9hlVcfPU26s2vafYWQcZHWXHOxiaRZYEDKEwdl129Zyg50+foYV2jCgtrqSA6qNuNSA==", - "dev": true, - "requires": { - "defer-to-connect": "^1.0.1" - } - }, - "cacheable-request": { - "version": "6.1.0", - "resolved": "https://registry.npmjs.org/cacheable-request/-/cacheable-request-6.1.0.tgz", - "integrity": "sha512-Oj3cAGPCqOZX7Rz64Uny2GYAZNliQSqfbePrgAQ1wKAihYmCUnraBtJtKcGR4xz7wF+LoJC+ssFZvv5BgF9Igg==", - "dev": true, - "requires": { - "clone-response": "^1.0.2", - "get-stream": "^5.1.0", - "http-cache-semantics": "^4.0.0", - "keyv": "^3.0.0", - "lowercase-keys": "^2.0.0", - "normalize-url": "^4.1.0", - "responselike": "^1.0.2" - }, - "dependencies": { - "get-stream": { - "version": "5.2.0", - "resolved": "https://registry.npmjs.org/get-stream/-/get-stream-5.2.0.tgz", - "integrity": "sha512-nBF+F1rAZVCu/p7rjzgA+Yb4lfYXrpl7a6VmJrU8wF9I1CKvP/QwPNZHnOlwbTkY6dvtFIzFMSyQXbLoTQPRpA==", - "dev": true, - "requires": { - "pump": "^3.0.0" - } - } - } - }, - "decompress-response": { - "version": "3.3.0", - "resolved": "https://registry.npmjs.org/decompress-response/-/decompress-response-3.3.0.tgz", - "integrity": "sha1-gKTdMjdIOEv6JICDYirt7Jgq3/M=", - "dev": true, - "requires": { - "mimic-response": "^1.0.0" - } - }, - "defer-to-connect": { - "version": "1.1.3", - "resolved": "https://registry.npmjs.org/defer-to-connect/-/defer-to-connect-1.1.3.tgz", - "integrity": "sha512-0ISdNousHvZT2EiFlZeZAHBUvSxmKswVCEf8hW7KWgG4a8MVEu/3Vb6uWYozkjylyCxe0JBIiRB1jV45S70WVQ==", - "dev": true - }, - "get-stream": { - "version": "4.1.0", - "resolved": "https://registry.npmjs.org/get-stream/-/get-stream-4.1.0.tgz", - "integrity": "sha512-GMat4EJ5161kIy2HevLlr4luNjBgvmj413KaQA7jt4V8B4RDsfpHk7WQ9GVqfYyyx8OS/L66Kox+rJRNklLK7w==", - "dev": true, - "requires": { - "pump": "^3.0.0" - } - }, - "got": { - "version": "9.6.0", - "resolved": "https://registry.npmjs.org/got/-/got-9.6.0.tgz", - "integrity": "sha512-R7eWptXuGYxwijs0eV+v3o6+XH1IqVK8dJOEecQfTmkncw9AV4dcw/Dhxi8MdlqPthxxpZyizMzyg8RTmEsG+Q==", - "dev": true, - "requires": { - "@sindresorhus/is": "^0.14.0", - "@szmarczak/http-timer": "^1.1.2", - "cacheable-request": "^6.0.0", - "decompress-response": "^3.3.0", - "duplexer3": "^0.1.4", - "get-stream": "^4.1.0", - "lowercase-keys": "^1.0.1", - "mimic-response": "^1.0.1", - "p-cancelable": "^1.0.0", - "to-readable-stream": "^1.0.0", - "url-parse-lax": "^3.0.0" - }, - "dependencies": { - "lowercase-keys": { - "version": "1.0.1", - "resolved": "https://registry.npmjs.org/lowercase-keys/-/lowercase-keys-1.0.1.tgz", - "integrity": "sha512-G2Lj61tXDnVFFOi8VZds+SoQjtQC3dgokKdDG2mTm1tx4m50NUHBOZSBwQQHyy0V12A0JTG4icfZQH+xPyh8VA==", - "dev": true - } - } - }, - "json-buffer": { - "version": "3.0.0", - "resolved": "https://registry.npmjs.org/json-buffer/-/json-buffer-3.0.0.tgz", - "integrity": "sha1-Wx85evx11ne96Lz8Dkfh+aPZqJg=", - "dev": true - }, - "keyv": { - "version": "3.1.0", - "resolved": "https://registry.npmjs.org/keyv/-/keyv-3.1.0.tgz", - "integrity": "sha512-9ykJ/46SN/9KPM/sichzQ7OvXyGDYKGTaDlKMGCAlg2UK8KRy4jb0d8sFc+0Tt0YYnThq8X2RZgCg74RPxgcVA==", - "dev": true, - "requires": { - "json-buffer": "3.0.0" - } - }, - "normalize-url": { - "version": "4.5.1", - "resolved": "https://registry.npmjs.org/normalize-url/-/normalize-url-4.5.1.tgz", - "integrity": "sha512-9UZCFRHQdNrfTpGg8+1INIg93B6zE0aXMVFkw1WFwvO4SlZywU6aLg5Of0Ap/PgcbSw4LNxvMWXMeugwMCX0AA==", - "dev": true - }, - "p-cancelable": { - "version": "1.1.0", - "resolved": "https://registry.npmjs.org/p-cancelable/-/p-cancelable-1.1.0.tgz", - "integrity": "sha512-s73XxOZ4zpt1edZYZzvhqFa6uvQc1vwUa0K0BdtIZgQMAJj9IbebH+JkgKZc9h+B05PKHLOTl4ajG1BmNrVZlw==", - "dev": true - }, - "responselike": { - "version": "1.0.2", - "resolved": "https://registry.npmjs.org/responselike/-/responselike-1.0.2.tgz", - "integrity": "sha1-kYcg7ztjHFZCvgaPFa3lpG9Loec=", - "dev": true, - "requires": { - "lowercase-keys": "^1.0.0" - }, - "dependencies": { - "lowercase-keys": { - "version": "1.0.1", - "resolved": "https://registry.npmjs.org/lowercase-keys/-/lowercase-keys-1.0.1.tgz", - "integrity": "sha512-G2Lj61tXDnVFFOi8VZds+SoQjtQC3dgokKdDG2mTm1tx4m50NUHBOZSBwQQHyy0V12A0JTG4icfZQH+xPyh8VA==", - "dev": true - } - } - } } }, "parent-module": { @@ -10372,6 +10729,14 @@ "normalize-url": "^6.1.0", "parse-path": "^4.0.0", "protocols": "^1.4.0" + }, + "dependencies": { + "normalize-url": { + "version": "6.1.0", + "resolved": "https://registry.npmjs.org/normalize-url/-/normalize-url-6.1.0.tgz", + "integrity": "sha512-DlL+XwOy3NxAQ8xuC0okPgK46iuVNAK01YN7RueYBqqFeGsBjV9XmCAzAdgt+667bCl5kPh9EqKKDwnaPG1I7A==", + "dev": true + } } }, "path-exists": { @@ -10405,6 +10770,14 @@ "dev": true, "requires": { "isarray": "0.0.1" + }, + "dependencies": { + "isarray": { + "version": "0.0.1", + "resolved": "https://registry.npmjs.org/isarray/-/isarray-0.0.1.tgz", + "integrity": "sha1-ihis/Kmo9Bd+Cav8YDiTmwXR7t8=", + "dev": true + } } }, "path-type": { @@ -10476,6 +10849,20 @@ "fromentries": "^1.2.0" } }, + "promise.allsettled": { + "version": "1.0.5", + "resolved": "https://registry.npmjs.org/promise.allsettled/-/promise.allsettled-1.0.5.tgz", + "integrity": "sha512-tVDqeZPoBC0SlzJHzWGZ2NKAguVq2oiYj7gbggbiTvH2itHohijTp7njOUA0aQ/nl+0lr/r6egmhoYu63UZ/pQ==", + "dev": true, + "requires": { + "array.prototype.map": "^1.0.4", + "call-bind": "^1.0.2", + "define-properties": "^1.1.3", + "es-abstract": "^1.19.1", + "get-intrinsic": "^1.1.1", + "iterate-value": "^1.0.2" + } + }, "protocols": { "version": "1.4.8", "resolved": "https://registry.npmjs.org/protocols/-/protocols-1.4.8.tgz", @@ -10534,12 +10921,6 @@ "integrity": "sha512-NuaNSa6flKT5JaSYQzJok04JzTL1CA6aGhv5rfLW3PgqA+M2ChpZQnAC8h8i4ZFkBS8X5RqkDBHA7r4hej3K9A==", "dev": true }, - "quick-lru": { - "version": "5.1.1", - "resolved": "https://registry.npmjs.org/quick-lru/-/quick-lru-5.1.1.tgz", - "integrity": "sha512-WuyALRjWPDGtt/wzJiadO5AXY+8hZ80hVpe6MyivgraREW751X3SbhRvG3eLKOYN+8VEvqLcf3wdnt44Z4S4SA==", - "dev": true - }, "randombytes": { "version": "2.1.0", "resolved": "https://registry.npmjs.org/randombytes/-/randombytes-2.1.0.tgz", @@ -10636,9 +11017,9 @@ } }, "release-it": { - "version": "14.13.1", - "resolved": "https://registry.npmjs.org/release-it/-/release-it-14.13.1.tgz", - "integrity": "sha512-mrng5bqZDFMr/7oCH3kuflwjKpKki4dUp6yYGxs20scYCvvd8rHAI5pdQOJHwI5BKHAC/pad0UjAEycMWQnEIw==", + "version": "14.14.2", + "resolved": "https://registry.npmjs.org/release-it/-/release-it-14.14.2.tgz", + "integrity": "sha512-+TE5Zg7x5BE/xw6i8SN9rmsGxCE2GVqH1v8Ay1L09nsLQx1HJhihAqUtCCDdgOuLvGpX0xgDMsSzakDlDLpOkA==", "dev": true, "requires": { "@iarna/toml": "2.2.5", @@ -10646,22 +11027,23 @@ "async-retry": "1.3.3", "chalk": "4.1.2", "cosmiconfig": "7.0.1", - "debug": "4.3.3", + "debug": "4.3.4", "execa": "5.1.1", "form-data": "4.0.0", "git-url-parse": "11.6.0", "globby": "11.0.4", - "got": "11.8.3", + "got": "9.6.0", "import-cwd": "3.0.0", "inquirer": "8.2.0", "is-ci": "3.0.1", "lodash": "4.17.21", - "mime-types": "2.1.34", + "mime-types": "2.1.35", "new-github-release-url": "1.0.0", "open": "7.4.2", "ora": "5.4.1", "os-name": "4.0.1", "parse-json": "5.2.0", + "promise.allsettled": "1.0.5", "semver": "7.3.5", "shelljs": "0.8.5", "update-notifier": "5.1.0", @@ -10735,12 +11117,6 @@ "supports-preserve-symlinks-flag": "^1.0.0" } }, - "resolve-alpn": { - "version": "1.2.1", - "resolved": "https://registry.npmjs.org/resolve-alpn/-/resolve-alpn-1.2.1.tgz", - "integrity": "sha512-0a1F4l73/ZFZOakJnQ3FvkJ2+gSTQWz/r2KE5OdDY0TxPm5h4GkqkWWfM47T7HsbnOtcJVEF4epCVy6u7Q3K+g==", - "dev": true - }, "resolve-from": { "version": "4.0.0", "resolved": "https://registry.npmjs.org/resolve-from/-/resolve-from-4.0.0.tgz", @@ -10748,12 +11124,12 @@ "dev": true }, "responselike": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/responselike/-/responselike-2.0.0.tgz", - "integrity": "sha512-xH48u3FTB9VsZw7R+vvgaKeLKzT6jOogbQhEe/jewwnZgzPcnyWui2Av6JpoYZF/91uueC+lqhWqeURw5/qhCw==", + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/responselike/-/responselike-1.0.2.tgz", + "integrity": "sha1-kYcg7ztjHFZCvgaPFa3lpG9Loec=", "dev": true, "requires": { - "lowercase-keys": "^2.0.0" + "lowercase-keys": "^1.0.0" } }, "restore-cursor": { @@ -11003,6 +11379,26 @@ "strip-ansi": "^6.0.1" } }, + "string.prototype.trimend": { + "version": "1.0.4", + "resolved": "https://registry.npmjs.org/string.prototype.trimend/-/string.prototype.trimend-1.0.4.tgz", + "integrity": "sha512-y9xCjw1P23Awk8EvTpcyL2NIr1j7wJ39f+k6lvRnSMz+mz9CGz9NYPelDk42kOz6+ql8xjfK8oYzy3jAP5QU5A==", + "dev": true, + "requires": { + "call-bind": "^1.0.2", + "define-properties": "^1.1.3" + } + }, + "string.prototype.trimstart": { + "version": "1.0.4", + "resolved": "https://registry.npmjs.org/string.prototype.trimstart/-/string.prototype.trimstart-1.0.4.tgz", + "integrity": "sha512-jh6e984OBfvxS50tdY2nRZnoC5/mLFKOREQfw8t5yytkoUsJRNxvI/E39qu1sD0OtWI3OC0XgKSmcWwziwYuZw==", + "dev": true, + "requires": { + "call-bind": "^1.0.2", + "define-properties": "^1.1.3" + } + }, "strip-ansi": { "version": "6.0.1", "resolved": "https://registry.npmjs.org/strip-ansi/-/strip-ansi-6.0.1.tgz", @@ -11205,9 +11601,9 @@ } }, "typedoc": { - "version": "0.22.13", - "resolved": "https://registry.npmjs.org/typedoc/-/typedoc-0.22.13.tgz", - "integrity": "sha512-NHNI7Dr6JHa/I3+c62gdRNXBIyX7P33O9TafGLd07ur3MqzcKgwTvpg18EtvCLHJyfeSthAtCLpM7WkStUmDuQ==", + "version": "0.22.15", + "resolved": "https://registry.npmjs.org/typedoc/-/typedoc-0.22.15.tgz", + "integrity": "sha512-CMd1lrqQbFvbx6S9G6fL4HKp3GoIuhujJReWqlIvSb2T26vGai+8Os3Mde7Pn832pXYemd9BMuuYWhFpL5st0Q==", "dev": true, "requires": { "glob": "^7.2.0", @@ -11243,6 +11639,18 @@ "integrity": "sha512-yNIatDa5iaofVozS/uQJEl3JRWLKKGJKh6Yaiv0GLGSuhpFJe7P3SbHZ8/yjAHRQwKRoA6YZqlfjXWmVzoVSMw==", "dev": true }, + "unbox-primitive": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/unbox-primitive/-/unbox-primitive-1.0.1.tgz", + "integrity": "sha512-tZU/3NqK3dA5gpE1KtyiJUrEB0lxnGkMFHptJ7q6ewdZ8s12QrODwNbhIJStmJkd1QDXa1NRA8aF2A1zk/Ypyw==", + "dev": true, + "requires": { + "function-bind": "^1.1.1", + "has-bigints": "^1.0.1", + "has-symbols": "^1.0.2", + "which-boxed-primitive": "^1.0.2" + } + }, "unique-string": { "version": "2.0.0", "resolved": "https://registry.npmjs.org/unique-string/-/unique-string-2.0.0.tgz", @@ -11302,9 +11710,9 @@ } }, "semver": { - "version": "7.3.5", - "resolved": "https://registry.npmjs.org/semver/-/semver-7.3.5.tgz", - "integrity": "sha512-PoeGJYh8HK4BTO/a9Tf6ZG3veo/A7ZVsYrSA6J8ny9nb3B1VrpkuN+z9OE5wfE5p6H4LchYZsegiQgbJD94ZFQ==", + "version": "7.3.7", + "resolved": "https://registry.npmjs.org/semver/-/semver-7.3.7.tgz", + "integrity": "sha512-QlYTucUYOews+WeEujDoEGziz4K6c47V/Bd+LjSSYcA94p+DmINdf7ncaUinThfvZyu13lN9OY1XDxt8C0Tw0g==", "dev": true, "requires": { "lru-cache": "^6.0.0" @@ -11406,6 +11814,19 @@ "isexe": "^2.0.0" } }, + "which-boxed-primitive": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/which-boxed-primitive/-/which-boxed-primitive-1.0.2.tgz", + "integrity": "sha512-bwZdv0AKLpplFY2KZRX6TvyuN7ojjr7lwkg6ml0roIy9YeuSr7JS372qlNW18UQYzgYK9ziGcerWqZOmEn9VNg==", + "dev": true, + "requires": { + "is-bigint": "^1.0.1", + "is-boolean-object": "^1.1.0", + "is-number-object": "^1.0.4", + "is-string": "^1.0.5", + "is-symbol": "^1.0.3" + } + }, "which-module": { "version": "2.0.0", "resolved": "https://registry.npmjs.org/which-module/-/which-module-2.0.0.tgz", @@ -11535,9 +11956,9 @@ "dev": true }, "yargs": { - "version": "17.4.0", - "resolved": "https://registry.npmjs.org/yargs/-/yargs-17.4.0.tgz", - "integrity": "sha512-WJudfrk81yWFSOkZYpAZx4Nt7V4xp7S/uJkX0CnxovMCt1wCE8LNftPpNuF9X/u9gN5nsD7ycYtRcDf2pL3UiA==", + "version": "17.4.1", + "resolved": "https://registry.npmjs.org/yargs/-/yargs-17.4.1.tgz", + "integrity": "sha512-WSZD9jgobAg3ZKuCQZSa3g9QOJeCCqLoLAykiWgmXnDo9EPnn4RPf5qVTtzgOx66o6/oqhcA5tHtJXpG8pMt3g==", "dev": true, "requires": { "cliui": "^7.0.2", diff --git a/package.json b/package.json index e8ce3380653..7b834d0ee62 100644 --- a/package.json +++ b/package.json @@ -24,7 +24,7 @@ }, "dependencies": { "@node-redis/bloom": "1.0.1", - "@node-redis/client": "1.0.5", + "@node-redis/client": "1.0.6", "@node-redis/graph": "1.0.0", "@node-redis/json": "1.0.2", "@node-redis/search": "1.0.5", @@ -33,7 +33,7 @@ "devDependencies": { "@tsconfig/node12": "^1.0.9", "gh-pages": "^3.2.3", - "release-it": "^14.13.1", + "release-it": "^14.14.2", "typescript": "^4.6.3" }, "repository": { diff --git a/packages/bloom/package.json b/packages/bloom/package.json index a7a0a07a130..17d6591b5cd 100644 --- a/packages/bloom/package.json +++ b/packages/bloom/package.json @@ -20,10 +20,10 @@ "@node-redis/test-utils": "*", "@types/node": "^17.0.23", "nyc": "^15.1.0", - "release-it": "^14.13.1", + "release-it": "^14.14.2", "source-map-support": "^0.5.21", "ts-node": "^10.7.0", - "typedoc": "^0.22.13", + "typedoc": "^0.22.15", "typescript": "^4.6.3" } } diff --git a/packages/client/package.json b/packages/client/package.json index e2e13a896c3..7ce11bb6a6a 100644 --- a/packages/client/package.json +++ b/packages/client/package.json @@ -26,15 +26,15 @@ "@types/redis-parser": "^3.0.0", "@types/sinon": "^10.0.11", "@types/yallist": "^4.0.1", - "@typescript-eslint/eslint-plugin": "^5.16.0", - "@typescript-eslint/parser": "^5.16.0", - "eslint": "^8.12.0", + "@typescript-eslint/eslint-plugin": "^5.19.0", + "@typescript-eslint/parser": "^5.19.0", + "eslint": "^8.13.0", "nyc": "^15.1.0", - "release-it": "^14.13.1", + "release-it": "^14.14.2", "sinon": "^13.0.1", "source-map-support": "^0.5.21", "ts-node": "^10.7.0", - "typedoc": "^0.22.13", + "typedoc": "^0.22.15", "typescript": "^4.6.3" }, "engines": { diff --git a/packages/graph/package.json b/packages/graph/package.json index 66fccd689a1..75c24346ce5 100644 --- a/packages/graph/package.json +++ b/packages/graph/package.json @@ -20,10 +20,10 @@ "@node-redis/test-utils": "*", "@types/node": "^17.0.23", "nyc": "^15.1.0", - "release-it": "^14.13.1", + "release-it": "^14.14.2", "source-map-support": "^0.5.21", "ts-node": "^10.7.0", - "typedoc": "^0.22.13", + "typedoc": "^0.22.15", "typescript": "^4.6.3" } } diff --git a/packages/json/package.json b/packages/json/package.json index 0516a7b1b05..472cbfd5eaa 100644 --- a/packages/json/package.json +++ b/packages/json/package.json @@ -20,10 +20,10 @@ "@node-redis/test-utils": "*", "@types/node": "^17.0.23", "nyc": "^15.1.0", - "release-it": "^14.13.1", + "release-it": "^14.14.2", "source-map-support": "^0.5.21", "ts-node": "^10.7.0", - "typedoc": "^0.22.13", + "typedoc": "^0.22.15", "typescript": "^4.6.3" } } diff --git a/packages/search/package.json b/packages/search/package.json index 07ddeb72f45..fcc1f41b369 100644 --- a/packages/search/package.json +++ b/packages/search/package.json @@ -20,10 +20,10 @@ "@node-redis/test-utils": "*", "@types/node": "^17.0.23", "nyc": "^15.1.0", - "release-it": "^14.13.1", + "release-it": "^14.14.2", "source-map-support": "^0.5.21", "ts-node": "^10.7.0", - "typedoc": "^0.22.13", + "typedoc": "^0.22.15", "typescript": "^4.6.3" } } diff --git a/packages/test-utils/package.json b/packages/test-utils/package.json index 7d854187337..ad4d8e592a9 100644 --- a/packages/test-utils/package.json +++ b/packages/test-utils/package.json @@ -19,6 +19,6 @@ "source-map-support": "^0.5.21", "ts-node": "^10.7.0", "typescript": "^4.6.3", - "yargs": "^17.4.0" + "yargs": "^17.4.1" } } diff --git a/packages/time-series/package.json b/packages/time-series/package.json index 034cb56c3a5..866296044e9 100644 --- a/packages/time-series/package.json +++ b/packages/time-series/package.json @@ -20,10 +20,10 @@ "@node-redis/test-utils": "*", "@types/node": "^17.0.23", "nyc": "^15.1.0", - "release-it": "^14.13.1", + "release-it": "^14.14.2", "source-map-support": "^0.5.21", "ts-node": "^10.7.0", - "typedoc": "^0.22.13", + "typedoc": "^0.22.15", "typescript": "^4.6.3" } } From 8b5a5473a4dee10f07d42cc6a5f4d1355b4a4515 Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Wed, 20 Apr 2022 15:25:00 -0400 Subject: [PATCH 1188/1748] Bump async from 2.6.3 to 2.6.4 (#2087) Bumps [async](https://github.com/caolan/async) from 2.6.3 to 2.6.4. - [Release notes](https://github.com/caolan/async/releases) - [Changelog](https://github.com/caolan/async/blob/v2.6.4/CHANGELOG.md) - [Commits](https://github.com/caolan/async/compare/v2.6.3...v2.6.4) --- updated-dependencies: - dependency-name: async dependency-type: indirect ... Signed-off-by: dependabot[bot] Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com> --- package-lock.json | 12 ++++++------ 1 file changed, 6 insertions(+), 6 deletions(-) diff --git a/package-lock.json b/package-lock.json index 3f1e789db2c..2d3f22c5b58 100644 --- a/package-lock.json +++ b/package-lock.json @@ -1374,9 +1374,9 @@ } }, "node_modules/async": { - "version": "2.6.3", - "resolved": "https://registry.npmjs.org/async/-/async-2.6.3.tgz", - "integrity": "sha512-zflvls11DCy+dQWzTW2dzuilv8Z5X/pjfmZOWba6TNIVDm+2UDaJmXSOXlasHKfNBs8oo3M0aT50fDEWfKZjXg==", + "version": "2.6.4", + "resolved": "https://registry.npmjs.org/async/-/async-2.6.4.tgz", + "integrity": "sha512-mzo5dfJYwAn29PeiJ0zvwTo04zj8HDJj0Mn8TD7sno7q12prdbnasKJHhkm2c1LgrhlJ0teaea8860oxi51mGA==", "dev": true, "dependencies": { "lodash": "^4.17.14" @@ -8036,9 +8036,9 @@ } }, "async": { - "version": "2.6.3", - "resolved": "https://registry.npmjs.org/async/-/async-2.6.3.tgz", - "integrity": "sha512-zflvls11DCy+dQWzTW2dzuilv8Z5X/pjfmZOWba6TNIVDm+2UDaJmXSOXlasHKfNBs8oo3M0aT50fDEWfKZjXg==", + "version": "2.6.4", + "resolved": "https://registry.npmjs.org/async/-/async-2.6.4.tgz", + "integrity": "sha512-mzo5dfJYwAn29PeiJ0zvwTo04zj8HDJj0Mn8TD7sno7q12prdbnasKJHhkm2c1LgrhlJ0teaea8860oxi51mGA==", "dev": true, "requires": { "lodash": "^4.17.14" From e6de453fdd271adbd021eda8073c44d07e4f9807 Mon Sep 17 00:00:00 2001 From: Leibale Eidelman Date: Mon, 25 Apr 2022 05:47:51 -0400 Subject: [PATCH 1189/1748] fix socket error handlers (#2092) * fix socket error handlers, reset parser on error * fix #2080 - reset pubSubState on socket error * fix pubsub * fix "RedisSocketInitiator" --- packages/client/lib/client/commands-queue.ts | 88 +++++++++----------- packages/client/lib/client/index.spec.ts | 33 +------- packages/client/lib/client/index.ts | 6 +- packages/client/lib/client/socket.spec.ts | 11 ++- packages/client/lib/client/socket.ts | 64 ++++---------- packages/client/lib/errors.ts | 6 -- 6 files changed, 67 insertions(+), 141 deletions(-) diff --git a/packages/client/lib/client/commands-queue.ts b/packages/client/lib/client/commands-queue.ts index 8af200314b8..addc29e5afe 100644 --- a/packages/client/lib/client/commands-queue.ts +++ b/packages/client/lib/client/commands-queue.ts @@ -53,16 +53,6 @@ interface PubSubListeners { type PubSubListenersMap = Map; -interface PubSubState { - subscribing: number; - subscribed: number; - unsubscribing: number; - listeners: { - channels: PubSubListenersMap; - patterns: PubSubListenersMap; - }; -} - export default class RedisCommandsQueue { static #flushQueue(queue: LinkedList, err: Error): void { while (queue.length) { @@ -98,7 +88,16 @@ export default class RedisCommandsQueue { readonly #waitingForReply = new LinkedList(); - #pubSubState: PubSubState | undefined; + readonly #pubSubState = { + isActive: false, + subscribing: 0, + subscribed: 0, + unsubscribing: 0, + listeners: { + channels: new Map(), + patterns: new Map() + } + }; static readonly #PUB_SUB_MESSAGES = { message: Buffer.from('message'), @@ -111,7 +110,7 @@ export default class RedisCommandsQueue { readonly #parser = new RedisParser({ returnReply: (reply: unknown) => { - if (this.#pubSubState && Array.isArray(reply)) { + if (this.#pubSubState.isActive && Array.isArray(reply)) { if (RedisCommandsQueue.#PUB_SUB_MESSAGES.message.equals(reply[0])) { return RedisCommandsQueue.#emitPubSubMessage( this.#pubSubState.listeners.channels, @@ -150,7 +149,7 @@ export default class RedisCommandsQueue { } addCommand(args: RedisCommandArguments, options?: QueueCommandOptions): Promise { - if (this.#pubSubState && !options?.ignorePubSubMode) { + if (this.#pubSubState.isActive && !options?.ignorePubSubMode) { 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')); @@ -190,27 +189,16 @@ export default class RedisCommandsQueue { }); } - #initiatePubSubState(): PubSubState { - return this.#pubSubState ??= { - subscribed: 0, - subscribing: 0, - unsubscribing: 0, - listeners: { - channels: new Map(), - patterns: new Map() - } - }; - } - subscribe( command: PubSubSubscribeCommands, channels: RedisCommandArgument | Array, listener: PubSubListener, returnBuffers?: T ): Promise { - const pubSubState = this.#initiatePubSubState(), - channelsToSubscribe: Array = [], - listenersMap = command === PubSubSubscribeCommands.SUBSCRIBE ? pubSubState.listeners.channels : pubSubState.listeners.patterns; + const channelsToSubscribe: Array = [], + listenersMap = command === PubSubSubscribeCommands.SUBSCRIBE ? + this.#pubSubState.listeners.channels : + this.#pubSubState.listeners.patterns; for (const channel of (Array.isArray(channels) ? channels : [channels])) { const channelString = typeof channel === 'string' ? channel : channel.toString(); let listeners = listenersMap.get(channelString); @@ -230,6 +218,7 @@ export default class RedisCommandsQueue { if (!channelsToSubscribe.length) { return Promise.resolve(); } + return this.#pushPubSubCommand(command, channelsToSubscribe); } @@ -239,10 +228,6 @@ export default class RedisCommandsQueue { listener?: PubSubListener, returnBuffers?: T ): Promise { - if (!this.#pubSubState) { - return Promise.resolve(); - } - const listeners = command === PubSubUnsubscribeCommands.UNSUBSCRIBE ? this.#pubSubState.listeners.channels : this.#pubSubState.listeners.patterns; @@ -280,8 +265,7 @@ export default class RedisCommandsQueue { #pushPubSubCommand(command: PubSubSubscribeCommands | PubSubUnsubscribeCommands, channels: number | Array): Promise { return new Promise((resolve, reject) => { - const pubSubState = this.#initiatePubSubState(), - isSubscribe = command === PubSubSubscribeCommands.SUBSCRIBE || command === PubSubSubscribeCommands.PSUBSCRIBE, + const isSubscribe = command === PubSubSubscribeCommands.SUBSCRIBE || command === PubSubSubscribeCommands.PSUBSCRIBE, inProgressKey = isSubscribe ? 'subscribing' : 'unsubscribing', commandArgs: Array = [command]; @@ -293,38 +277,42 @@ export default class RedisCommandsQueue { channelsCounter = channels.length; } - pubSubState[inProgressKey] += channelsCounter; + this.#pubSubState.isActive = true; + this.#pubSubState[inProgressKey] += channelsCounter; this.#waitingToBeSent.push({ args: commandArgs, channelsCounter, returnBuffers: true, resolve: () => { - pubSubState[inProgressKey] -= channelsCounter; - if (isSubscribe) { - pubSubState.subscribed += channelsCounter; - } else { - pubSubState.subscribed -= channelsCounter; - if (!pubSubState.subscribed && !pubSubState.subscribing && !pubSubState.subscribed) { - this.#pubSubState = undefined; - } - } + this.#pubSubState[inProgressKey] -= channelsCounter; + this.#pubSubState.subscribed += channelsCounter * (isSubscribe ? 1 : -1); + this.#updatePubSubActiveState(); resolve(); }, reject: err => { - pubSubState[inProgressKey] -= channelsCounter * (isSubscribe ? 1 : -1); + this.#pubSubState[inProgressKey] -= channelsCounter * (isSubscribe ? 1 : -1); + this.#updatePubSubActiveState(); reject(err); } }); }); } - resubscribe(): Promise | undefined { - if (!this.#pubSubState) { - return; + #updatePubSubActiveState(): void { + if ( + !this.#pubSubState.subscribed && + !this.#pubSubState.subscribing && + !this.#pubSubState.subscribed + ) { + this.#pubSubState.isActive = false; } + } + resubscribe(): Promise | undefined { this.#pubSubState.subscribed = 0; + this.#pubSubState.subscribing = 0; + this.#pubSubState.unsubscribing = 0; const promises = [], { channels, patterns } = this.#pubSubState.listeners; @@ -369,8 +357,7 @@ export default class RedisCommandsQueue { #setReturnBuffers() { this.#parser.setReturnBuffers( !!this.#waitingForReply.head?.value.returnBuffers || - !!this.#pubSubState?.subscribed || - !!this.#pubSubState?.subscribing + !!this.#pubSubState.isActive ); } @@ -390,6 +377,7 @@ export default class RedisCommandsQueue { } flushWaitingForReply(err: Error): void { + this.#parser.reset(); RedisCommandsQueue.#flushQueue(this.#waitingForReply, err); if (!this.#chainInExecution) return; diff --git a/packages/client/lib/client/index.spec.ts b/packages/client/lib/client/index.spec.ts index c48505c7586..09b974c910b 100644 --- a/packages/client/lib/client/index.spec.ts +++ b/packages/client/lib/client/index.spec.ts @@ -3,7 +3,7 @@ import testUtils, { GLOBAL, waitTillBeenCalled } from '../test-utils'; import RedisClient, { RedisClientType } from '.'; import { RedisClientMultiCommandType } from './multi-command'; import { RedisCommandArguments, RedisCommandRawReply, RedisModules, RedisScripts } from '../commands'; -import { AbortError, AuthError, ClientClosedError, ConnectionTimeoutError, DisconnectsClientError, SocketClosedUnexpectedlyError, WatchError } from '../errors'; +import { AbortError, ClientClosedError, ConnectionTimeoutError, DisconnectsClientError, SocketClosedUnexpectedlyError, WatchError } from '../errors'; import { defineScript } from '../lua-script'; import { spy } from 'sinon'; import { once } from 'events'; @@ -87,30 +87,6 @@ describe('Client', () => { ); }, GLOBAL.SERVERS.PASSWORD); - testUtils.testWithClient('should not retry connecting if failed due to wrong auth', async client => { - let message; - if (testUtils.isVersionGreaterThan([6, 2])) { - message = 'WRONGPASS invalid username-password pair or user is disabled.'; - } else if (testUtils.isVersionGreaterThan([6])) { - message = 'WRONGPASS invalid username-password pair'; - } else { - message = 'ERR invalid password'; - } - - await assert.rejects( - client.connect(), - new AuthError(message) - ); - - assert.equal(client.isOpen, false); - }, { - ...GLOBAL.SERVERS.PASSWORD, - clientOptions: { - password: 'wrongpassword' - }, - disableClientSetup: true - }); - testUtils.testWithClient('should execute AUTH before SELECT', async client => { assert.equal( (await client.clientInfo()).db, @@ -300,7 +276,8 @@ describe('Client', () => { await client.multi() .sAdd('a', ['b', 'c']) .v4.exec(), - [2]) + [2] + ); }, { ...GLOBAL.SERVERS.OPEN, clientOptions: { @@ -681,10 +658,6 @@ describe('Client', () => { const listener = spy(); await subscriber.subscribe('channel', listener); - subscriber.on('error', err => { - console.error('subscriber err', err.message); - }); - await Promise.all([ once(subscriber, 'error'), publisher.clientKill({ diff --git a/packages/client/lib/client/index.ts b/packages/client/lib/client/index.ts index 1dd74fa1afe..25535e0728e 100644 --- a/packages/client/lib/client/index.ts +++ b/packages/client/lib/client/index.ts @@ -11,7 +11,7 @@ import { ScanCommandOptions } from '../commands/SCAN'; import { HScanTuple } from '../commands/HSCAN'; import { extendWithCommands, extendWithModulesAndScripts, transformCommandArguments, transformCommandReply } from '../commander'; import { Pool, Options as PoolOptions, createPool } from 'generic-pool'; -import { ClientClosedError, DisconnectsClientError, AuthError } from '../errors'; +import { ClientClosedError, DisconnectsClientError } from '../errors'; import { URL } from 'url'; import { TcpSocketConnectOpts } from 'net'; @@ -254,9 +254,7 @@ export default class RedisClient password: this.#options.password ?? '' }), { asap: true } - ).catch(err => { - throw new AuthError(err.message); - }) + ) ); } diff --git a/packages/client/lib/client/socket.spec.ts b/packages/client/lib/client/socket.spec.ts index 4c5cfd1d9b3..54f84eb9fe0 100644 --- a/packages/client/lib/client/socket.spec.ts +++ b/packages/client/lib/client/socket.spec.ts @@ -21,10 +21,13 @@ describe('Socket', () => { return time; }); - const socket = new RedisSocket(undefined, { - host: 'error', - reconnectStrategy - }); + const socket = new RedisSocket( + () => Promise.resolve(), + { + host: 'error', + reconnectStrategy + } + ); socket.on('error', () => { // ignore errors diff --git a/packages/client/lib/client/socket.ts b/packages/client/lib/client/socket.ts index 0366b2b86e1..b04950a0724 100644 --- a/packages/client/lib/client/socket.ts +++ b/packages/client/lib/client/socket.ts @@ -3,7 +3,7 @@ import * as net from 'net'; import * as tls from 'tls'; import { encodeCommand } from '../commander'; import { RedisCommandArguments } from '../commands'; -import { ConnectionTimeoutError, ClientClosedError, SocketClosedUnexpectedlyError, AuthError, ReconnectStrategyError } from '../errors'; +import { ConnectionTimeoutError, ClientClosedError, SocketClosedUnexpectedlyError, ReconnectStrategyError } from '../errors'; import { promiseTimeout } from '../utils'; export interface RedisSocketCommonOptions { @@ -53,7 +53,7 @@ export default class RedisSocket extends EventEmitter { return (options as RedisTlsSocketOptions).tls === true; } - readonly #initiator?: RedisSocketInitiator; + readonly #initiator: RedisSocketInitiator; readonly #options: RedisSocketOptions; @@ -79,7 +79,7 @@ export default class RedisSocket extends EventEmitter { return this.#writableNeedDrain; } - constructor(initiator?: RedisSocketInitiator, options?: RedisSocketOptions) { + constructor(initiator: RedisSocketInitiator, options?: RedisSocketOptions) { super(); this.#initiator = initiator; @@ -91,70 +91,40 @@ export default class RedisSocket extends EventEmitter { throw new Error('Socket already opened'); } - return this.#connect(); + return this.#connect(0); } - async #connect(hadError?: boolean): Promise { + async #connect(retries: number, hadError?: boolean): Promise { + if (retries > 0 || hadError) { + this.emit('reconnecting'); + } + try { this.#isOpen = true; - this.#socket = await this.#retryConnection(0, hadError); + this.#socket = await this.#createSocket(); this.#writableNeedDrain = false; - } catch (err) { - this.#isOpen = false; - this.emit('error', err); - this.emit('end'); - throw err; - } + this.emit('connect'); - if (!this.#isOpen) { - this.disconnect(); - return; - } - - this.emit('connect'); - - if (this.#initiator) { try { await this.#initiator(); } catch (err) { this.#socket.destroy(); this.#socket = undefined; - - if (err instanceof AuthError) { - this.#isOpen = false; - } - throw err; } - - if (!this.#isOpen) return; - } - - this.#isReady = true; - - this.emit('ready'); - } - - async #retryConnection(retries: number, hadError?: boolean): Promise { - if (retries > 0 || hadError) { - this.emit('reconnecting'); - } - - try { - return await this.#createSocket(); + this.#isReady = true; + this.emit('ready'); } catch (err) { - if (!this.#isOpen) { - throw err; - } + this.emit('error', err); const retryIn = (this.#options?.reconnectStrategy ?? RedisSocket.#defaultReconnectStrategy)(retries); if (retryIn instanceof Error) { + this.#isOpen = false; throw new ReconnectStrategyError(retryIn, err); } - this.emit('error', err); await promiseTimeout(retryIn); - return this.#retryConnection(retries + 1); + return this.#connect(retries + 1); } } @@ -212,7 +182,7 @@ export default class RedisSocket extends EventEmitter { this.#isReady = false; this.emit('error', err); - this.#connect(true).catch(() => { + this.#connect(0, true).catch(() => { // the error was already emitted, silently ignore it }); } diff --git a/packages/client/lib/errors.ts b/packages/client/lib/errors.ts index e43dbc81422..01dff992290 100644 --- a/packages/client/lib/errors.ts +++ b/packages/client/lib/errors.ts @@ -34,12 +34,6 @@ export class SocketClosedUnexpectedlyError extends Error { } } -export class AuthError extends Error { - constructor(message: string) { - super(message); - } -} - export class RootNodesUnavailableError extends Error { constructor() { super('All the root nodes are unavailable'); From 448ac9daae091490ae2a36765c8580ba90c1463c Mon Sep 17 00:00:00 2001 From: Leibale Eidelman Date: Mon, 25 Apr 2022 05:48:02 -0400 Subject: [PATCH 1190/1748] fix #2090 - topK.listWithCount (#2091) --- packages/bloom/lib/commands/top-k/LIST.ts | 2 ++ .../lib/commands/top-k/LIST_WITHCOUNT.spec.ts | 30 +++++++++++++++++++ .../lib/commands/top-k/LIST_WITHCOUNT.ts | 26 ++++++++++++++++ packages/bloom/lib/commands/top-k/RESERVE.ts | 2 ++ packages/bloom/lib/commands/top-k/index.ts | 3 ++ 5 files changed, 63 insertions(+) create mode 100644 packages/bloom/lib/commands/top-k/LIST_WITHCOUNT.spec.ts create mode 100644 packages/bloom/lib/commands/top-k/LIST_WITHCOUNT.ts diff --git a/packages/bloom/lib/commands/top-k/LIST.ts b/packages/bloom/lib/commands/top-k/LIST.ts index d8c16545593..8837b86f830 100644 --- a/packages/bloom/lib/commands/top-k/LIST.ts +++ b/packages/bloom/lib/commands/top-k/LIST.ts @@ -1,5 +1,7 @@ export const FIRST_KEY_INDEX = 1; +export const IS_READ_ONLY = true; + export function transformArguments(key: string): Array { return ['TOPK.LIST', key]; } diff --git a/packages/bloom/lib/commands/top-k/LIST_WITHCOUNT.spec.ts b/packages/bloom/lib/commands/top-k/LIST_WITHCOUNT.spec.ts new file mode 100644 index 00000000000..1e55239c243 --- /dev/null +++ b/packages/bloom/lib/commands/top-k/LIST_WITHCOUNT.spec.ts @@ -0,0 +1,30 @@ +import { strict as assert } from 'assert'; +import testUtils, { GLOBAL } from '../../test-utils'; +import { transformArguments } from './LIST_WITHCOUNT'; + +describe('TOPK LIST WITHCOUNT', () => { + testUtils.isVersionGreaterThanHook([2, 2, 9]); + + it('transformArguments', () => { + assert.deepEqual( + transformArguments('key'), + ['TOPK.LIST', 'key', 'WITHCOUNT'] + ); + }); + + testUtils.testWithClient('client.topK.listWithCount', async client => { + const [,, list] = await Promise.all([ + client.topK.reserve('key', 3), + client.topK.add('key', 'item'), + client.topK.listWithCount('key') + ]); + + assert.deepEqual( + list, + [{ + item: 'item', + count: 1 + }] + ); + }, GLOBAL.SERVERS.OPEN); +}); diff --git a/packages/bloom/lib/commands/top-k/LIST_WITHCOUNT.ts b/packages/bloom/lib/commands/top-k/LIST_WITHCOUNT.ts new file mode 100644 index 00000000000..47b7d3848ed --- /dev/null +++ b/packages/bloom/lib/commands/top-k/LIST_WITHCOUNT.ts @@ -0,0 +1,26 @@ +export const FIRST_KEY_INDEX = 1; + +export const IS_READ_ONLY = true; + +export function transformArguments(key: string): Array { + return ['TOPK.LIST', key, 'WITHCOUNT']; +} + +type ListWithCountRawReply = Array; + +type ListWithCountReply = Array<{ + item: string, + count: number +}>; + +export function transformReply(rawReply: ListWithCountRawReply): ListWithCountReply { + const reply: ListWithCountReply = []; + for (let i = 0; i < rawReply.length; i++) { + reply.push({ + item: rawReply[i] as string, + count: rawReply[++i] as number + }); + } + + return reply; +} \ No newline at end of file diff --git a/packages/bloom/lib/commands/top-k/RESERVE.ts b/packages/bloom/lib/commands/top-k/RESERVE.ts index 6512db51e0b..350d4cd8339 100644 --- a/packages/bloom/lib/commands/top-k/RESERVE.ts +++ b/packages/bloom/lib/commands/top-k/RESERVE.ts @@ -1,5 +1,7 @@ export const FIRST_KEY_INDEX = 1; +export const IS_READ_ONLY = true; + interface ReserveOptions { width: number; depth: number; diff --git a/packages/bloom/lib/commands/top-k/index.ts b/packages/bloom/lib/commands/top-k/index.ts index 05d6ba87eab..750c91dfa88 100644 --- a/packages/bloom/lib/commands/top-k/index.ts +++ b/packages/bloom/lib/commands/top-k/index.ts @@ -2,6 +2,7 @@ import * as ADD from './ADD'; import * as COUNT from './COUNT'; import * as INCRBY from './INCRBY'; import * as INFO from './INFO'; +import * as LIST_WITHCOUNT from './LIST_WITHCOUNT'; import * as LIST from './LIST'; import * as QUERY from './QUERY'; import * as RESERVE from './RESERVE'; @@ -15,6 +16,8 @@ export default { incrBy: INCRBY, INFO, info: INFO, + LIST_WITHCOUNT, + listWithCount: LIST_WITHCOUNT, LIST, list: LIST, QUERY, From 0f7ae937dff263eeb2615dea7332026dfb20e86b Mon Sep 17 00:00:00 2001 From: Simon Prickett Date: Mon, 25 Apr 2022 10:58:24 +0100 Subject: [PATCH 1191/1748] Updates topk example for new withcount option. (#2093) --- examples/topk.js | 18 ++++++++++++++++++ 1 file changed, 18 insertions(+) diff --git a/examples/topk.js b/examples/topk.js index 14129724341..024dc30ecb0 100644 --- a/examples/topk.js +++ b/examples/topk.js @@ -66,6 +66,24 @@ async function topK() { // ] console.log(top10); + // List out the top 10 with their counts (requires RedisBloom >=2.2.9) + const top10WithCounts = await client.topK.listWithCount('mytopk'); + console.log('The top 10 with counts:'); + console.log(top10WithCounts); + // top10WithCounts looks like this: + // [ + // { item: 'suze', count: 42363 }, + // { item: 'lance', count: 41982 }, + // { item: 'simon', count: 41831 }, + // { item: 'steve', count: 39237 }, + // { item: 'guy', count: 39078 }, + // { item: 'kyleb', count: 37338 }, + // { item: 'leibale', count: 34230 }, + // { item: 'kyleo', count: 33812 }, + // { item: 'alex', count: 33679 }, + // { item: 'nava', count: 32663 } + // ] + // Check if a few team members are in the top 10 with TOPK.QUERY: const [ steve, suze, leibale, frederick ] = await client.topK.query('mytopk', [ 'steve', From b1a0b48d2cd17cfa5a5de2bcd2ed05b3f7ef159a Mon Sep 17 00:00:00 2001 From: Avital Fine <98389525+Avital-Fine@users.noreply.github.com> Date: Mon, 25 Apr 2022 14:50:43 +0300 Subject: [PATCH 1192/1748] Support new muilti pop commands (#2051) * Support new muilti pop commands * remove .only * clean code * fix for 4558ec6a31d2904154c896e8325f09ae33a9ea32 * fix tests Co-authored-by: leibale --- packages/client/lib/cluster/commands.ts | 12 +++++ packages/client/lib/commands/BLMOVE.ts | 6 +-- packages/client/lib/commands/BLMPOP.spec.ts | 32 +++++++++++ packages/client/lib/commands/BLMPOP.ts | 20 +++++++ packages/client/lib/commands/BLPOP.spec.ts | 2 +- packages/client/lib/commands/BZMPOP.spec.ts | 32 +++++++++++ packages/client/lib/commands/BZMPOP.ts | 20 +++++++ packages/client/lib/commands/BZPOPMAX.spec.ts | 2 +- packages/client/lib/commands/BZPOPMIN.spec.ts | 2 +- packages/client/lib/commands/LMOVE.ts | 7 ++- packages/client/lib/commands/LMPOP.spec.ts | 32 +++++++++++ packages/client/lib/commands/LMPOP.ts | 22 ++++++++ packages/client/lib/commands/ZMPOP.spec.ts | 32 +++++++++++ packages/client/lib/commands/ZMPOP.ts | 34 ++++++++++++ .../lib/commands/generic-transformers.ts | 53 +++++++++++++++++++ 15 files changed, 298 insertions(+), 10 deletions(-) create mode 100644 packages/client/lib/commands/BLMPOP.spec.ts create mode 100644 packages/client/lib/commands/BLMPOP.ts create mode 100644 packages/client/lib/commands/BZMPOP.spec.ts create mode 100644 packages/client/lib/commands/BZMPOP.ts create mode 100644 packages/client/lib/commands/LMPOP.spec.ts create mode 100644 packages/client/lib/commands/LMPOP.ts create mode 100644 packages/client/lib/commands/ZMPOP.spec.ts create mode 100644 packages/client/lib/commands/ZMPOP.ts diff --git a/packages/client/lib/cluster/commands.ts b/packages/client/lib/cluster/commands.ts index 4a2c7e8503b..91e589e1275 100644 --- a/packages/client/lib/cluster/commands.ts +++ b/packages/client/lib/cluster/commands.ts @@ -6,9 +6,11 @@ import * as BITFIELD from '../commands/BITFIELD'; import * as BITOP from '../commands/BITOP'; import * as BITPOS from '../commands/BITPOS'; import * as BLMOVE from '../commands/BLMOVE'; +import * as BLMPOP from '../commands/BLMPOP'; import * as BLPOP from '../commands/BLPOP'; import * as BRPOP from '../commands/BRPOP'; import * as BRPOPLPUSH from '../commands/BRPOPLPUSH'; +import * as BZMPOP from '../commands/BZMPOP'; import * as BZPOPMAX from '../commands/BZPOPMAX'; import * as BZPOPMIN from '../commands/BZPOPMIN'; import * as COPY from '../commands/COPY'; @@ -59,6 +61,7 @@ import * as LINDEX from '../commands/LINDEX'; import * as LINSERT from '../commands/LINSERT'; import * as LLEN from '../commands/LLEN'; import * as LMOVE from '../commands/LMOVE'; +import * as LMPOP from '../commands/LMPOP'; import * as LPOP_COUNT from '../commands/LPOP_COUNT'; import * as LPOP from '../commands/LPOP'; import * as LPOS_COUNT from '../commands/LPOS_COUNT'; @@ -161,6 +164,7 @@ import * as ZINTER from '../commands/ZINTER'; import * as ZINTERCARD from '../commands/ZINTERCARD'; import * as ZINTERSTORE from '../commands/ZINTERSTORE'; import * as ZLEXCOUNT from '../commands/ZLEXCOUNT'; +import * as ZMPOP from '../commands/ZMPOP'; import * as ZMSCORE from '../commands/ZMSCORE'; import * as ZPOPMAX_COUNT from '../commands/ZPOPMAX_COUNT'; import * as ZPOPMAX from '../commands/ZPOPMAX'; @@ -202,12 +206,16 @@ export default { bitPos: BITPOS, BLMOVE, blMove: BLMOVE, + BLMPOP, + blmPop: BLMPOP, BLPOP, blPop: BLPOP, BRPOP, brPop: BRPOP, BRPOPLPUSH, brPopLPush: BRPOPLPUSH, + BZMPOP, + bzmPop: BZMPOP, BZPOPMAX, bzPopMax: BZPOPMAX, BZPOPMIN, @@ -308,6 +316,8 @@ export default { lLen: LLEN, LMOVE, lMove: LMOVE, + LMPOP, + lmPop: LMPOP, LPOP_COUNT, lPopCount: LPOP_COUNT, LPOP, @@ -512,6 +522,8 @@ export default { zInterStore: ZINTERSTORE, ZLEXCOUNT, zLexCount: ZLEXCOUNT, + ZMPOP, + zmPop: ZMPOP, ZMSCORE, zmScore: ZMSCORE, ZPOPMAX_COUNT, diff --git a/packages/client/lib/commands/BLMOVE.ts b/packages/client/lib/commands/BLMOVE.ts index 329192f634b..ee808e70fcc 100644 --- a/packages/client/lib/commands/BLMOVE.ts +++ b/packages/client/lib/commands/BLMOVE.ts @@ -1,13 +1,13 @@ import { RedisCommandArgument, RedisCommandArguments } from '.'; -import { LMoveSide } from './LMOVE'; +import { ListSide } from './generic-transformers'; export const FIRST_KEY_INDEX = 1; export function transformArguments( source: RedisCommandArgument, destination: RedisCommandArgument, - sourceDirection: LMoveSide, - destinationDirection: LMoveSide, + sourceDirection: ListSide, + destinationDirection: ListSide, timeout: number ): RedisCommandArguments { return [ diff --git a/packages/client/lib/commands/BLMPOP.spec.ts b/packages/client/lib/commands/BLMPOP.spec.ts new file mode 100644 index 00000000000..9a4a6c96a26 --- /dev/null +++ b/packages/client/lib/commands/BLMPOP.spec.ts @@ -0,0 +1,32 @@ +import { strict as assert } from 'assert'; +import testUtils, { GLOBAL } from '../test-utils'; +import { transformArguments } from './BLMPOP'; + +describe('BLMPOP', () => { + testUtils.isVersionGreaterThanHook([7, 0]); + + describe('transformArguments', () => { + it('simple', () => { + assert.deepEqual( + transformArguments(0, 'key', 'LEFT'), + ['BLMPOP', '0', '1', 'key', 'LEFT'] + ); + }); + + it('with COUNT', () => { + assert.deepEqual( + transformArguments(0, 'key', 'LEFT', { + COUNT: 2 + }), + ['BLMPOP', '0', '1', 'key', 'LEFT', 'COUNT', '2'] + ); + }); + }); + + testUtils.testWithClient('client.blmPop', async client => { + assert.deepEqual( + await client.blmPop(1, 'key', 'RIGHT'), + null + ); + }, GLOBAL.SERVERS.OPEN); +}); diff --git a/packages/client/lib/commands/BLMPOP.ts b/packages/client/lib/commands/BLMPOP.ts new file mode 100644 index 00000000000..11bfad8b99b --- /dev/null +++ b/packages/client/lib/commands/BLMPOP.ts @@ -0,0 +1,20 @@ +import { RedisCommandArgument, RedisCommandArguments } from '.'; +import { transformLMPopArguments, LMPopOptions, ListSide } from './generic-transformers'; + +export const FIRST_KEY_INDEX = 3; + +export function transformArguments( + timeout: number, + keys: RedisCommandArgument | Array, + side: ListSide, + options?: LMPopOptions +): RedisCommandArguments { + return transformLMPopArguments( + ['BLMPOP', timeout.toString()], + keys, + side, + options + ); +} + +export { transformReply } from './LMPOP'; diff --git a/packages/client/lib/commands/BLPOP.spec.ts b/packages/client/lib/commands/BLPOP.spec.ts index 4b93c0b43b8..84920c851e1 100644 --- a/packages/client/lib/commands/BLPOP.spec.ts +++ b/packages/client/lib/commands/BLPOP.spec.ts @@ -65,7 +65,7 @@ describe('BLPOP', () => { 'key', 1 ), - cluster.lPush('key', 'element'), + cluster.lPush('key', 'element') ]); assert.deepEqual( diff --git a/packages/client/lib/commands/BZMPOP.spec.ts b/packages/client/lib/commands/BZMPOP.spec.ts new file mode 100644 index 00000000000..b35d971f0a5 --- /dev/null +++ b/packages/client/lib/commands/BZMPOP.spec.ts @@ -0,0 +1,32 @@ +import { strict as assert } from 'assert'; +import testUtils, { GLOBAL } from '../test-utils'; +import { transformArguments } from './BZMPOP'; + +describe('BZMPOP', () => { + testUtils.isVersionGreaterThanHook([7, 0]); + + describe('transformArguments', () => { + it('simple', () => { + assert.deepEqual( + transformArguments(0, 'key', 'MIN'), + ['BZMPOP', '0', '1', 'key', 'MIN'] + ); + }); + + it('with COUNT', () => { + assert.deepEqual( + transformArguments(0, 'key', 'MIN', { + COUNT: 2 + }), + ['BZMPOP', '0', '1', 'key', 'MIN', 'COUNT', '2'] + ); + }); + }); + + testUtils.testWithClient('client.bzmPop', async client => { + assert.deepEqual( + await client.bzmPop(1, 'key', 'MAX'), + null + ); + }, GLOBAL.SERVERS.OPEN); +}); diff --git a/packages/client/lib/commands/BZMPOP.ts b/packages/client/lib/commands/BZMPOP.ts new file mode 100644 index 00000000000..e4e9699cbd4 --- /dev/null +++ b/packages/client/lib/commands/BZMPOP.ts @@ -0,0 +1,20 @@ +import { RedisCommandArgument, RedisCommandArguments } from '.'; +import { SortedSetSide, transformZMPopArguments, ZMPopOptions } from './generic-transformers'; + +export const FIRST_KEY_INDEX = 3; + +export function transformArguments( + timeout: number, + keys: RedisCommandArgument | Array, + side: SortedSetSide, + options?: ZMPopOptions +): RedisCommandArguments { + return transformZMPopArguments( + ['BZMPOP', timeout.toString()], + keys, + side, + options + ); +} + +export { transformReply } from './ZMPOP'; diff --git a/packages/client/lib/commands/BZPOPMAX.spec.ts b/packages/client/lib/commands/BZPOPMAX.spec.ts index e1c37478469..d5c17437122 100644 --- a/packages/client/lib/commands/BZPOPMAX.spec.ts +++ b/packages/client/lib/commands/BZPOPMAX.spec.ts @@ -45,7 +45,7 @@ describe('BZPOPMAX', () => { client.bzPopMax( commandOptions({ isolated: true }), 'key', - 0 + 1 ), client.zAdd('key', [{ value: '1', diff --git a/packages/client/lib/commands/BZPOPMIN.spec.ts b/packages/client/lib/commands/BZPOPMIN.spec.ts index 4cd1ec1b220..0573a4ac898 100644 --- a/packages/client/lib/commands/BZPOPMIN.spec.ts +++ b/packages/client/lib/commands/BZPOPMIN.spec.ts @@ -45,7 +45,7 @@ describe('BZPOPMIN', () => { client.bzPopMin( commandOptions({ isolated: true }), 'key', - 0 + 1 ), client.zAdd('key', [{ value: '1', diff --git a/packages/client/lib/commands/LMOVE.ts b/packages/client/lib/commands/LMOVE.ts index 7332d1a0075..849c6385f5a 100644 --- a/packages/client/lib/commands/LMOVE.ts +++ b/packages/client/lib/commands/LMOVE.ts @@ -1,14 +1,13 @@ import { RedisCommandArgument, RedisCommandArguments } from '.'; +import { ListSide } from './generic-transformers'; export const FIRST_KEY_INDEX = 1; -export type LMoveSide = 'LEFT' | 'RIGHT'; - export function transformArguments( source: RedisCommandArgument, destination: RedisCommandArgument, - sourceSide: LMoveSide, - destinationSide: LMoveSide + sourceSide: ListSide, + destinationSide: ListSide ): RedisCommandArguments { return [ 'LMOVE', diff --git a/packages/client/lib/commands/LMPOP.spec.ts b/packages/client/lib/commands/LMPOP.spec.ts new file mode 100644 index 00000000000..a3c36f90212 --- /dev/null +++ b/packages/client/lib/commands/LMPOP.spec.ts @@ -0,0 +1,32 @@ +import { strict as assert } from 'assert'; +import testUtils, { GLOBAL } from '../test-utils'; +import { transformArguments } from './LMPOP'; + +describe('LMPOP', () => { + testUtils.isVersionGreaterThanHook([7, 0]); + + describe('transformArguments', () => { + it('simple', () => { + assert.deepEqual( + transformArguments('key', 'LEFT'), + ['LMPOP', '1', 'key', 'LEFT'] + ); + }); + + it('with COUNT', () => { + assert.deepEqual( + transformArguments('key', 'LEFT', { + COUNT: 2 + }), + ['LMPOP', '1', 'key', 'LEFT', 'COUNT', '2'] + ); + }); + }); + + testUtils.testWithClient('client.lmPop', async client => { + assert.deepEqual( + await client.lmPop('key', 'RIGHT'), + null + ); + }, GLOBAL.SERVERS.OPEN); +}); diff --git a/packages/client/lib/commands/LMPOP.ts b/packages/client/lib/commands/LMPOP.ts new file mode 100644 index 00000000000..29d868b982f --- /dev/null +++ b/packages/client/lib/commands/LMPOP.ts @@ -0,0 +1,22 @@ +import { RedisCommandArgument, RedisCommandArguments } from '.'; +import { transformLMPopArguments, LMPopOptions, ListSide } from './generic-transformers'; + +export const FIRST_KEY_INDEX = 2; + +export function transformArguments( + keys: RedisCommandArgument | Array, + side: ListSide, + options?: LMPopOptions +): RedisCommandArguments { + return transformLMPopArguments( + ['LMPOP'], + keys, + side, + options + ); +} + +export declare function transformReply(): null | [ + key: string, + elements: Array +]; diff --git a/packages/client/lib/commands/ZMPOP.spec.ts b/packages/client/lib/commands/ZMPOP.spec.ts new file mode 100644 index 00000000000..84f51e67b7d --- /dev/null +++ b/packages/client/lib/commands/ZMPOP.spec.ts @@ -0,0 +1,32 @@ +import { strict as assert } from 'assert'; +import testUtils, { GLOBAL } from '../test-utils'; +import { transformArguments } from './ZMPOP'; + +describe('ZMPOP', () => { + testUtils.isVersionGreaterThanHook([7, 0]); + + describe('transformArguments', () => { + it('simple', () => { + assert.deepEqual( + transformArguments('key', 'MIN'), + ['ZMPOP', '1', 'key', 'MIN'] + ); + }); + + it('with score and count', () => { + assert.deepEqual( + transformArguments('key', 'MIN', { + COUNT: 2 + }), + ['ZMPOP', '1', 'key', 'MIN', 'COUNT', '2'] + ); + }); + }); + + testUtils.testWithClient('client.zmPop', async client => { + assert.deepEqual( + await client.zmPop('key', 'MIN'), + null + ); + }, GLOBAL.SERVERS.OPEN); +}); diff --git a/packages/client/lib/commands/ZMPOP.ts b/packages/client/lib/commands/ZMPOP.ts new file mode 100644 index 00000000000..0baa46bbf0b --- /dev/null +++ b/packages/client/lib/commands/ZMPOP.ts @@ -0,0 +1,34 @@ +import { RedisCommandArgument, RedisCommandArguments } from '.'; +import { SortedSetSide, transformSortedSetMemberReply, transformZMPopArguments, ZMember, ZMPopOptions } from './generic-transformers'; + +export const FIRST_KEY_INDEX = 2; + +export function transformArguments( + keys: RedisCommandArgument | Array, + side: SortedSetSide, + options?: ZMPopOptions +): RedisCommandArguments { + return transformZMPopArguments( + ['ZMPOP'], + keys, + side, + options + ); +} + +type ZMPopRawReply = null | [ + key: string, + elements: Array<[RedisCommandArgument, RedisCommandArgument]> +]; + +type ZMPopReply = null | { + key: string, + elements: Array +}; + +export function transformReply(reply: ZMPopRawReply): ZMPopReply { + return reply === null ? null : { + key: reply[0], + elements: reply[1].map(transformSortedSetMemberReply) + }; +} diff --git a/packages/client/lib/commands/generic-transformers.ts b/packages/client/lib/commands/generic-transformers.ts index 7850d22ed41..e881822fb40 100644 --- a/packages/client/lib/commands/generic-transformers.ts +++ b/packages/client/lib/commands/generic-transformers.ts @@ -131,6 +131,13 @@ export function transformSortedSetMemberNullReply( ): ZMember | null { if (!reply.length) return null; + return transformSortedSetMemberReply(reply); +} + +export function transformSortedSetMemberReply( + reply: [RedisCommandArgument, RedisCommandArgument] +): ZMember { + return { value: reply[0], score: transformNumberInfinityReply(reply[1]) @@ -150,6 +157,52 @@ export function transformSortedSetWithScoresReply(reply: Array, + side: SortedSetSide, + options?: ZMPopOptions +): RedisCommandArguments { + pushVerdictArgument(args, keys); + + args.push(side); + + if (options?.COUNT) { + args.push('COUNT', options.COUNT.toString()); + } + + return args; +} + +export type ListSide = 'LEFT' | 'RIGHT'; + +export interface LMPopOptions { + COUNT?: number; +} + +export function transformLMPopArguments( + args: RedisCommandArguments, + keys: RedisCommandArgument | Array, + side: ListSide, + options?: LMPopOptions +): RedisCommandArguments { + pushVerdictArgument(args, keys); + + args.push(side); + + if (options?.COUNT) { + args.push('COUNT', options.COUNT.toString()); + } + + return args; +} + type GeoCountArgument = number | { value: number; ANY?: true From 23b65133c9610c44f336199a1ea73e77a9b73bc7 Mon Sep 17 00:00:00 2001 From: Leibale Eidelman Date: Mon, 25 Apr 2022 08:24:33 -0400 Subject: [PATCH 1193/1748] New RESP2 parser (#1899) * parser * a new RESP parser :) * clean code * fix simple string and bulk string cursor * performance improvements * change typescript compiler target * do not use stream.Transform * Update decoder.ts * fix for 1d09acb * improve integer performance * revert 1d09acb * improve RESP2 decoder performance * improve performance * improve encode performance * remove unused import * upgrade benchmark deps * clean code * fix socket error handlers, reset parser on error * fix #2080 - reset pubSubState on socket error * reset decoder on socket error * fix pubsub * fix "RedisSocketInitiator" * fix returnStringsAsBuffers * fix merge --- benchmark/package-lock.json | 21 +- .../lib/client/RESP2/composers/buffer.spec.ts | 14 + .../lib/client/RESP2/composers/buffer.ts | 18 ++ .../lib/client/RESP2/composers/interface.ts | 7 + .../lib/client/RESP2/composers/string.spec.ts | 14 + .../lib/client/RESP2/composers/string.ts | 22 ++ .../client/lib/client/RESP2/decoder.spec.ts | 195 ++++++++++++++ packages/client/lib/client/RESP2/decoder.ts | 254 ++++++++++++++++++ .../client/lib/client/RESP2/encoder.spec.ts | 33 +++ packages/client/lib/client/RESP2/encoder.ts | 30 +++ packages/client/lib/client/commands-queue.ts | 138 +++++----- packages/client/lib/client/index.spec.ts | 2 +- packages/client/lib/client/index.ts | 18 +- packages/client/lib/client/multi-command.ts | 4 +- packages/client/lib/client/socket.ts | 5 +- packages/client/lib/commander.spec.ts | 36 --- packages/client/lib/commander.ts | 35 +-- packages/client/lib/errors.ts | 7 + packages/client/lib/test-utils.ts | 2 +- packages/client/package.json | 2 - packages/client/tsconfig.json | 3 + 21 files changed, 703 insertions(+), 157 deletions(-) create mode 100644 packages/client/lib/client/RESP2/composers/buffer.spec.ts create mode 100644 packages/client/lib/client/RESP2/composers/buffer.ts create mode 100644 packages/client/lib/client/RESP2/composers/interface.ts create mode 100644 packages/client/lib/client/RESP2/composers/string.spec.ts create mode 100644 packages/client/lib/client/RESP2/composers/string.ts create mode 100644 packages/client/lib/client/RESP2/decoder.spec.ts create mode 100644 packages/client/lib/client/RESP2/decoder.ts create mode 100644 packages/client/lib/client/RESP2/encoder.spec.ts create mode 100644 packages/client/lib/client/RESP2/encoder.ts delete mode 100644 packages/client/lib/commander.spec.ts diff --git a/benchmark/package-lock.json b/benchmark/package-lock.json index 62a9608062e..857b3c423ac 100644 --- a/benchmark/package-lock.json +++ b/benchmark/package-lock.json @@ -20,7 +20,6 @@ "dependencies": { "cluster-key-slot": "1.1.0", "generic-pool": "3.8.2", - "redis-parser": "3.0.0", "yallist": "4.0.0" }, "devDependencies": { @@ -316,6 +315,14 @@ "@node-redis/time-series": "1.0.2" } }, + "node_modules/redis-v3/node_modules/denque": { + "version": "1.5.1", + "resolved": "https://registry.npmjs.org/denque/-/denque-1.5.1.tgz", + "integrity": "sha512-XwE+iZ4D6ZUB7mfYRMb5wByE8L74HCn30FBN7sWnXksWc1LO1bPDl67pBR9o/kC4z/xSNAwkMYcGgqDV3BE3Hw==", + "engines": { + "node": ">=0.10" + } + }, "node_modules/require-directory": { "version": "2.1.1", "resolved": "https://registry.npmjs.org/require-directory/-/require-directory-2.1.1.tgz", @@ -395,9 +402,9 @@ } }, "node_modules/yargs-parser": { - "version": "21.0.0", - "resolved": "https://registry.npmjs.org/yargs-parser/-/yargs-parser-21.0.0.tgz", - "integrity": "sha512-z9kApYUOCwoeZ78rfRYYWdiU/iNL6mwwYlkkZfJoyMR1xps+NEBX5X7XmRpxkZHhXJ6+Ey00IwKxBBSW9FIjyA==", + "version": "21.0.1", + "resolved": "https://registry.npmjs.org/yargs-parser/-/yargs-parser-21.0.1.tgz", + "integrity": "sha512-9BK1jFpLzJROCI5TzwZL/TU4gqjK5xiHV/RfWLOahrjAko/e4DJkRDZQXfvqAsiZzzYhgAzbgz6lg48jcm4GLg==", "engines": { "node": ">=12" } @@ -678,9 +685,9 @@ } }, "yargs-parser": { - "version": "21.0.0", - "resolved": "https://registry.npmjs.org/yargs-parser/-/yargs-parser-21.0.0.tgz", - "integrity": "sha512-z9kApYUOCwoeZ78rfRYYWdiU/iNL6mwwYlkkZfJoyMR1xps+NEBX5X7XmRpxkZHhXJ6+Ey00IwKxBBSW9FIjyA==" + "version": "21.0.1", + "resolved": "https://registry.npmjs.org/yargs-parser/-/yargs-parser-21.0.1.tgz", + "integrity": "sha512-9BK1jFpLzJROCI5TzwZL/TU4gqjK5xiHV/RfWLOahrjAko/e4DJkRDZQXfvqAsiZzzYhgAzbgz6lg48jcm4GLg==" } } } diff --git a/packages/client/lib/client/RESP2/composers/buffer.spec.ts b/packages/client/lib/client/RESP2/composers/buffer.spec.ts new file mode 100644 index 00000000000..f57c369fecb --- /dev/null +++ b/packages/client/lib/client/RESP2/composers/buffer.spec.ts @@ -0,0 +1,14 @@ +import { strict as assert } from 'assert'; +import BufferComposer from './buffer'; + +describe('Buffer Composer', () => { + const composer = new BufferComposer(); + + it('should compose two buffers', () => { + composer.write(Buffer.from([0])); + assert.deepEqual( + composer.end(Buffer.from([1])), + Buffer.from([0, 1]) + ); + }); +}); diff --git a/packages/client/lib/client/RESP2/composers/buffer.ts b/packages/client/lib/client/RESP2/composers/buffer.ts new file mode 100644 index 00000000000..4affb4283e0 --- /dev/null +++ b/packages/client/lib/client/RESP2/composers/buffer.ts @@ -0,0 +1,18 @@ +import { Composer } from './interface'; + +export default class BufferComposer implements Composer { + private chunks: Array = []; + + write(buffer: Buffer): void { + this.chunks.push(buffer); + } + + end(buffer: Buffer): Buffer { + this.write(buffer); + return Buffer.concat(this.chunks.splice(0)); + } + + reset() { + this.chunks = []; + } +} diff --git a/packages/client/lib/client/RESP2/composers/interface.ts b/packages/client/lib/client/RESP2/composers/interface.ts new file mode 100644 index 00000000000..0fc8f031414 --- /dev/null +++ b/packages/client/lib/client/RESP2/composers/interface.ts @@ -0,0 +1,7 @@ +export interface Composer { + write(buffer: Buffer): void; + + end(buffer: Buffer): T; + + reset(): void; +} diff --git a/packages/client/lib/client/RESP2/composers/string.spec.ts b/packages/client/lib/client/RESP2/composers/string.spec.ts new file mode 100644 index 00000000000..9dd26aae021 --- /dev/null +++ b/packages/client/lib/client/RESP2/composers/string.spec.ts @@ -0,0 +1,14 @@ +import { strict as assert } from 'assert'; +import StringComposer from './string'; + +describe('String Composer', () => { + const composer = new StringComposer(); + + it('should compose two strings', () => { + composer.write(Buffer.from([0])); + assert.deepEqual( + composer.end(Buffer.from([1])), + Buffer.from([0, 1]).toString() + ); + }); +}); diff --git a/packages/client/lib/client/RESP2/composers/string.ts b/packages/client/lib/client/RESP2/composers/string.ts new file mode 100644 index 00000000000..0cd8f00e95c --- /dev/null +++ b/packages/client/lib/client/RESP2/composers/string.ts @@ -0,0 +1,22 @@ +import { StringDecoder } from 'string_decoder'; +import { Composer } from './interface'; + +export default class StringComposer implements Composer { + private decoder = new StringDecoder(); + + private string = ''; + + write(buffer: Buffer): void { + this.string += this.decoder.write(buffer); + } + + end(buffer: Buffer): string { + const string = this.string + this.decoder.end(buffer); + this.string = ''; + return string; + } + + reset() { + this.string = ''; + } +} diff --git a/packages/client/lib/client/RESP2/decoder.spec.ts b/packages/client/lib/client/RESP2/decoder.spec.ts new file mode 100644 index 00000000000..dcce9f60115 --- /dev/null +++ b/packages/client/lib/client/RESP2/decoder.spec.ts @@ -0,0 +1,195 @@ +import { strict as assert } from 'assert'; +import { SinonSpy, spy } from 'sinon'; +import RESP2Decoder from './decoder'; +import { ErrorReply } from '../../errors'; + +interface DecoderAndSpies { + decoder: RESP2Decoder; + returnStringsAsBuffersSpy: SinonSpy; + onReplySpy: SinonSpy; +} + +function createDecoderAndSpies(returnStringsAsBuffers: boolean): DecoderAndSpies { + const returnStringsAsBuffersSpy = spy(() => returnStringsAsBuffers), + onReplySpy = spy(); + + return { + decoder: new RESP2Decoder({ + returnStringsAsBuffers: returnStringsAsBuffersSpy, + onReply: onReplySpy + }), + returnStringsAsBuffersSpy, + onReplySpy + }; +} + +function writeChunks(stream: RESP2Decoder, buffer: Buffer) { + let i = 0; + while (i < buffer.length) { + stream.write(buffer.slice(i, ++i)); + } +} + +type Replies = Array>; + +interface TestsOptions { + toWrite: Buffer; + returnStringsAsBuffers: boolean; + replies: Replies; +} + +function generateTests({ + toWrite, + returnStringsAsBuffers, + replies +}: TestsOptions): void { + it('single chunk', () => { + const { decoder, returnStringsAsBuffersSpy, onReplySpy } = + createDecoderAndSpies(returnStringsAsBuffers); + decoder.write(toWrite); + assert.equal(returnStringsAsBuffersSpy.callCount, replies.length); + testReplies(onReplySpy, replies); + }); + + it('multiple chunks', () => { + const { decoder, returnStringsAsBuffersSpy, onReplySpy } = + createDecoderAndSpies(returnStringsAsBuffers); + writeChunks(decoder, toWrite); + assert.equal(returnStringsAsBuffersSpy.callCount, replies.length); + testReplies(onReplySpy, replies); + }); +} + +function testReplies(spy: SinonSpy, replies: Replies): void { + if (!replies) { + assert.equal(spy.callCount, 0); + return; + } + + assert.equal(spy.callCount, replies.length); + for (const [i, reply] of replies.entries()) { + assert.deepEqual( + spy.getCall(i).args, + reply + ); + } +} + +describe('RESP2Parser', () => { + describe('Simple String', () => { + describe('as strings', () => { + generateTests({ + toWrite: Buffer.from('+OK\r\n'), + returnStringsAsBuffers: false, + replies: [['OK']] + }); + }); + + describe('as buffers', () => { + generateTests({ + toWrite: Buffer.from('+OK\r\n'), + returnStringsAsBuffers: true, + replies: [[Buffer.from('OK')]] + }); + }); + }); + + describe('Error', () => { + generateTests({ + toWrite: Buffer.from('-ERR\r\n'), + returnStringsAsBuffers: false, + replies: [[new ErrorReply('ERR')]] + }); + }); + + describe('Integer', () => { + describe('-1', () => { + generateTests({ + toWrite: Buffer.from(':-1\r\n'), + returnStringsAsBuffers: false, + replies: [[-1]] + }); + }); + + describe('0', () => { + generateTests({ + toWrite: Buffer.from(':0\r\n'), + returnStringsAsBuffers: false, + replies: [[0]] + }); + }); + }); + + describe('Bulk String', () => { + describe('null', () => { + generateTests({ + toWrite: Buffer.from('$-1\r\n'), + returnStringsAsBuffers: false, + replies: [[null]] + }); + }); + + describe('as strings', () => { + generateTests({ + toWrite: Buffer.from('$2\r\naa\r\n'), + returnStringsAsBuffers: false, + replies: [['aa']] + }); + }); + + describe('as buffers', () => { + generateTests({ + toWrite: Buffer.from('$2\r\naa\r\n'), + returnStringsAsBuffers: true, + replies: [[Buffer.from('aa')]] + }); + }); + }); + + describe('Array', () => { + describe('null', () => { + generateTests({ + toWrite: Buffer.from('*-1\r\n'), + returnStringsAsBuffers: false, + replies: [[null]] + }); + }); + + const arrayBuffer = Buffer.from( + '*5\r\n' + + '+OK\r\n' + + '-ERR\r\n' + + ':0\r\n' + + '$1\r\na\r\n' + + '*0\r\n' + ); + + describe('as strings', () => { + generateTests({ + toWrite: arrayBuffer, + returnStringsAsBuffers: false, + replies: [[[ + 'OK', + new ErrorReply('ERR'), + 0, + 'a', + [] + ]]] + }); + }); + + describe('as buffers', () => { + generateTests({ + toWrite: arrayBuffer, + returnStringsAsBuffers: true, + replies: [[[ + Buffer.from('OK'), + new ErrorReply('ERR'), + 0, + Buffer.from('a'), + [] + ]]] + }); + }); + }); +}); diff --git a/packages/client/lib/client/RESP2/decoder.ts b/packages/client/lib/client/RESP2/decoder.ts new file mode 100644 index 00000000000..e9d2317deab --- /dev/null +++ b/packages/client/lib/client/RESP2/decoder.ts @@ -0,0 +1,254 @@ +import { ErrorReply } from '../../errors'; +import { Composer } from './composers/interface'; +import BufferComposer from './composers/buffer'; +import StringComposer from './composers/string'; + +// RESP2 specification +// https://redis.io/topics/protocol + +enum Types { + SIMPLE_STRING = 43, // + + ERROR = 45, // - + INTEGER = 58, // : + BULK_STRING = 36, // $ + ARRAY = 42 // * +} + +enum ASCII { + CR = 13, // \r + ZERO = 48, + MINUS = 45 +} + +export type Reply = string | Buffer | ErrorReply | number | null | Array; + +type ArrayReply = Array | null; + +export type ReturnStringsAsBuffers = () => boolean; + +interface RESP2Options { + returnStringsAsBuffers: ReturnStringsAsBuffers; + onReply(reply: Reply): unknown; +} + +interface ArrayInProcess { + array: Array; + pushCounter: number; +} + +// Using TypeScript `private` and not the build-in `#` to avoid __classPrivateFieldGet and __classPrivateFieldSet + +export default class RESP2Decoder { + constructor(private options: RESP2Options) {} + + private cursor = 0; + + private type?: Types; + + private bufferComposer = new BufferComposer(); + + private stringComposer = new StringComposer(); + + private currentStringComposer: BufferComposer | StringComposer = this.stringComposer; + + reset() { + this.cursor = 0; + this.type = undefined; + this.bufferComposer.reset(); + this.stringComposer.reset(); + this.currentStringComposer = this.stringComposer; + } + + write(chunk: Buffer): void { + while (this.cursor < chunk.length) { + if (!this.type) { + this.currentStringComposer = this.options.returnStringsAsBuffers() ? + this.bufferComposer : + this.stringComposer; + + this.type = chunk[this.cursor]; + if (++this.cursor >= chunk.length) break; + } + + const reply = this.parseType(chunk, this.type); + if (reply === undefined) break; + + this.type = undefined; + this.options.onReply(reply); + } + + this.cursor -= chunk.length; + } + + private parseType(chunk: Buffer, type: Types, arraysToKeep?: number): Reply | undefined { + switch (type) { + case Types.SIMPLE_STRING: + return this.parseSimpleString(chunk); + + case Types.ERROR: + return this.parseError(chunk); + + case Types.INTEGER: + return this.parseInteger(chunk); + + case Types.BULK_STRING: + return this.parseBulkString(chunk); + + case Types.ARRAY: + return this.parseArray(chunk, arraysToKeep); + } + } + + private compose< + C extends Composer, + T = C extends Composer ? TT : never + >( + chunk: Buffer, + composer: C + ): T | undefined { + for (let i = this.cursor; i < chunk.length; i++) { + if (chunk[i] === ASCII.CR) { + const reply = composer.end( + chunk.subarray(this.cursor, i) + ); + this.cursor = i + 2; + return reply; + } + } + + const toWrite = chunk.subarray(this.cursor); + composer.write(toWrite); + this.cursor = chunk.length; + } + + private parseSimpleString(chunk: Buffer): string | Buffer | undefined { + return this.compose(chunk, this.currentStringComposer); + } + + private parseError(chunk: Buffer): ErrorReply | undefined { + const message = this.compose(chunk, this.stringComposer); + if (message !== undefined) { + return new ErrorReply(message); + } + } + + private integer = 0; + + private isNegativeInteger?: boolean; + + private parseInteger(chunk: Buffer): number | undefined { + if (this.isNegativeInteger === undefined) { + this.isNegativeInteger = chunk[this.cursor] === ASCII.MINUS; + if (this.isNegativeInteger && ++this.cursor === chunk.length) return; + } + + do { + const byte = chunk[this.cursor]; + if (byte === ASCII.CR) { + const integer = this.isNegativeInteger ? -this.integer : this.integer; + this.integer = 0; + this.isNegativeInteger = undefined; + this.cursor += 2; + return integer; + } + + this.integer = this.integer * 10 + byte - ASCII.ZERO; + } while (++this.cursor < chunk.length); + } + + private bulkStringRemainingLength?: number; + + private parseBulkString(chunk: Buffer): string | Buffer | null | undefined { + if (this.bulkStringRemainingLength === undefined) { + const length = this.parseInteger(chunk); + if (length === undefined) return; + if (length === -1) return null; + + this.bulkStringRemainingLength = length; + + if (this.cursor >= chunk.length) return; + } + + const end = this.cursor + this.bulkStringRemainingLength; + if (chunk.length >= end) { + const reply = this.currentStringComposer.end( + chunk.subarray(this.cursor, end) + ); + this.bulkStringRemainingLength = undefined; + this.cursor = end + 2; + return reply; + } + + const toWrite = chunk.subarray(this.cursor); + this.currentStringComposer.write(toWrite); + this.bulkStringRemainingLength -= toWrite.length; + this.cursor = chunk.length; + } + + private arraysInProcess: Array = []; + + private initializeArray = false; + + private arrayItemType?: Types; + + private parseArray(chunk: Buffer, arraysToKeep = 0): ArrayReply | undefined { + if (this.initializeArray || this.arraysInProcess.length === arraysToKeep) { + const length = this.parseInteger(chunk); + if (length === undefined) { + this.initializeArray = true; + return undefined; + } + + this.initializeArray = false; + this.arrayItemType = undefined; + + if (length === -1) { + return this.returnArrayReply(null, arraysToKeep); + } else if (length === 0) { + return this.returnArrayReply([], arraysToKeep); + } + + this.arraysInProcess.push({ + array: new Array(length), + pushCounter: 0 + }); + } + + while (this.cursor < chunk.length) { + if (!this.arrayItemType) { + this.arrayItemType = chunk[this.cursor]; + + if (++this.cursor >= chunk.length) break; + } + + const item = this.parseType( + chunk, + this.arrayItemType, + arraysToKeep + 1 + ); + if (item === undefined) break; + + this.arrayItemType = undefined; + + const reply = this.pushArrayItem(item, arraysToKeep); + if (reply !== undefined) return reply; + } + } + + private returnArrayReply(reply: ArrayReply, arraysToKeep: number): ArrayReply | undefined { + if (this.arraysInProcess.length <= arraysToKeep) return reply; + + return this.pushArrayItem(reply, arraysToKeep); + } + + private pushArrayItem(item: Reply, arraysToKeep: number): ArrayReply | undefined { + const to = this.arraysInProcess[this.arraysInProcess.length - 1]!; + to.array[to.pushCounter] = item; + if (++to.pushCounter === to.array.length) { + return this.returnArrayReply( + this.arraysInProcess.pop()!.array, + arraysToKeep + ); + } + } +} diff --git a/packages/client/lib/client/RESP2/encoder.spec.ts b/packages/client/lib/client/RESP2/encoder.spec.ts new file mode 100644 index 00000000000..486259472a4 --- /dev/null +++ b/packages/client/lib/client/RESP2/encoder.spec.ts @@ -0,0 +1,33 @@ +import { strict as assert } from 'assert'; +import { describe } from 'mocha'; +import encodeCommand from './encoder'; + +describe('RESP2 Encoder', () => { + it('1 byte', () => { + assert.deepEqual( + encodeCommand(['a', 'z']), + ['*2\r\n$1\r\na\r\n$1\r\nz\r\n'] + ); + }); + + it('2 bytes', () => { + assert.deepEqual( + encodeCommand(['א', 'ת']), + ['*2\r\n$2\r\nא\r\n$2\r\nת\r\n'] + ); + }); + + it('4 bytes', () => { + assert.deepEqual( + [...encodeCommand(['🐣', '🐤'])], + ['*2\r\n$4\r\n🐣\r\n$4\r\n🐤\r\n'] + ); + }); + + it('buffer', () => { + assert.deepEqual( + encodeCommand([Buffer.from('string')]), + ['*1\r\n$6\r\n', Buffer.from('string'), '\r\n'] + ); + }); +}); diff --git a/packages/client/lib/client/RESP2/encoder.ts b/packages/client/lib/client/RESP2/encoder.ts new file mode 100644 index 00000000000..be48348a356 --- /dev/null +++ b/packages/client/lib/client/RESP2/encoder.ts @@ -0,0 +1,30 @@ +import { RedisCommandArgument, RedisCommandArguments } from '../../commands'; + +const CRLF = '\r\n'; + +export default function encodeCommand(args: RedisCommandArguments): Array { + const toWrite: Array = []; + + let strings = `*${args.length}${CRLF}`; + + for (let i = 0; i < args.length; i++) { + const arg = args[i]; + if (typeof arg === 'string') { + const byteLength = Buffer.byteLength(arg); + strings += `$${byteLength}${CRLF}`; + strings += arg; + } else if (arg instanceof Buffer) { + toWrite.push(`${strings}$${arg.length}${CRLF}`); + strings = ''; + toWrite.push(arg); + } else { + throw new TypeError('Invalid argument type'); + } + + strings += CRLF; + } + + toWrite.push(strings); + + return toWrite; +} diff --git a/packages/client/lib/client/commands-queue.ts b/packages/client/lib/client/commands-queue.ts index addc29e5afe..8cae914963e 100644 --- a/packages/client/lib/client/commands-queue.ts +++ b/packages/client/lib/client/commands-queue.ts @@ -1,11 +1,8 @@ import * as LinkedList from 'yallist'; -import { AbortError } from '../errors'; +import { AbortError, ErrorReply } from '../errors'; import { RedisCommandArgument, 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'); +import RESP2Decoder from './RESP2/decoder'; +import encodeCommand from './RESP2/encoder'; export interface QueueCommandOptions { asap?: boolean; @@ -85,7 +82,6 @@ export default class RedisCommandsQueue { readonly #maxLength: number | null | undefined; readonly #waitingToBeSent = new LinkedList(); - readonly #waitingForReply = new LinkedList(); readonly #pubSubState = { @@ -104,45 +100,32 @@ export default class RedisCommandsQueue { pMessage: Buffer.from('pmessage'), subscribe: Buffer.from('subscribe'), pSubscribe: Buffer.from('psubscribe'), - unsubscribe: Buffer.from('unsunscribe'), + unsubscribe: Buffer.from('unsubscribe'), pUnsubscribe: Buffer.from('punsubscribe') }; - readonly #parser = new RedisParser({ - returnReply: (reply: unknown) => { - if (this.#pubSubState.isActive && 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; - } - } + #chainInExecution: symbol | undefined; - this.#shiftWaitingForReply().resolve(reply); + #decoder = new RESP2Decoder({ + returnStringsAsBuffers: () => { + return !!this.#waitingForReply.head?.value.returnBuffers || + this.#pubSubState.isActive; }, - returnError: (err: Error) => this.#shiftWaitingForReply().reject(err) - }); + onReply: reply => { + if (this.#handlePubSubReply(reply)) { + return; + } else if (!this.#waitingForReply.length) { + throw new Error('Got an unexpected reply from Redis'); + } - #chainInExecution: symbol | undefined; + const { resolve, reject } = this.#waitingForReply.shift()!; + if (reply instanceof ErrorReply) { + reject(reply); + } else { + resolve(reply); + } + } + }); constructor(maxLength: number | null | undefined) { this.#maxLength = maxLength; @@ -257,9 +240,11 @@ export default class RedisCommandsQueue { listeners.delete(channel); } } + if (!channelsToUnsubscribe.length) { return Promise.resolve(); } + return this.#pushPubSubCommand(command, channelsToUnsubscribe); } @@ -342,42 +327,67 @@ export default class RedisCommandsQueue { getCommandToSend(): RedisCommandArguments | undefined { const toSend = this.#waitingToBeSent.shift(); - if (toSend) { - this.#waitingForReply.push({ - resolve: toSend.resolve, - reject: toSend.reject, - channelsCounter: toSend.channelsCounter, - returnBuffers: toSend.returnBuffers - }); + if (!toSend) return; + + let encoded: RedisCommandArguments; + try { + encoded = encodeCommand(toSend.args); + } catch (err) { + toSend.reject(err); + return; } - this.#chainInExecution = toSend?.chainId; - return toSend?.args; + + this.#waitingForReply.push({ + resolve: toSend.resolve, + reject: toSend.reject, + channelsCounter: toSend.channelsCounter, + returnBuffers: toSend.returnBuffers + }); + this.#chainInExecution = toSend.chainId; + return encoded; } - #setReturnBuffers() { - this.#parser.setReturnBuffers( - !!this.#waitingForReply.head?.value.returnBuffers || - !!this.#pubSubState.isActive - ); + rejectLastCommand(err: unknown): void { + this.#waitingForReply.pop()!.reject(err); } - parseResponse(data: Buffer): void { - this.#setReturnBuffers(); - this.#parser.execute(data); + onReplyChunk(chunk: Buffer): void { + this.#decoder.write(chunk); } - #shiftWaitingForReply(): CommandWaitingForReply { - if (!this.#waitingForReply.length) { - throw new Error('Got an unexpected reply from Redis'); + #handlePubSubReply(reply: any): boolean { + if (!this.#pubSubState.isActive || !Array.isArray(reply)) return false; + + if (RedisCommandsQueue.#PUB_SUB_MESSAGES.message.equals(reply[0])) { + RedisCommandsQueue.#emitPubSubMessage( + this.#pubSubState.listeners.channels, + reply[2], + reply[1] + ); + } else if (RedisCommandsQueue.#PUB_SUB_MESSAGES.pMessage.equals(reply[0])) { + 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.#waitingForReply.shift()!.resolve(); + } } - const waitingForReply = this.#waitingForReply.shift()!; - this.#setReturnBuffers(); - return waitingForReply; + return true; } flushWaitingForReply(err: Error): void { - this.#parser.reset(); + this.#decoder.reset(); + this.#pubSubState.isActive = false; RedisCommandsQueue.#flushQueue(this.#waitingForReply, err); if (!this.#chainInExecution) return; diff --git a/packages/client/lib/client/index.spec.ts b/packages/client/lib/client/index.spec.ts index 09b974c910b..a6b924d42ac 100644 --- a/packages/client/lib/client/index.spec.ts +++ b/packages/client/lib/client/index.spec.ts @@ -348,7 +348,7 @@ describe('Client', () => { testUtils.testWithClient('undefined and null should not break the client', async client => { await assert.rejects( client.sendCommand([null as any, undefined as any]), - 'ERR unknown command ``, with args beginning with: ``' + TypeError ); assert.equal( diff --git a/packages/client/lib/client/index.ts b/packages/client/lib/client/index.ts index 25535e0728e..242c590cc80 100644 --- a/packages/client/lib/client/index.ts +++ b/packages/client/lib/client/index.ts @@ -9,7 +9,7 @@ import { CommandOptions, commandOptions, isCommandOptions } from '../command-opt import { ScanOptions, ZMember } from '../commands/generic-transformers'; import { ScanCommandOptions } from '../commands/SCAN'; import { HScanTuple } from '../commands/HSCAN'; -import { extendWithCommands, extendWithModulesAndScripts, transformCommandArguments, transformCommandReply } from '../commander'; +import { extendWithCommands, extendWithModulesAndScripts, transformCommandArguments, transformCommandReply, transformLegacyCommandArguments } from '../commander'; import { Pool, Options as PoolOptions, createPool } from 'generic-pool'; import { ClientClosedError, DisconnectsClientError } from '../errors'; import { URL } from 'url'; @@ -158,8 +158,8 @@ export default class RedisClient } readonly #options?: RedisClientOptions; - readonly #socket: RedisSocket; readonly #queue: RedisCommandsQueue; + readonly #socket: RedisSocket; readonly #isolationPool: Pool>; readonly #v4: Record = {}; #selectedDB = 0; @@ -183,8 +183,8 @@ export default class RedisClient constructor(options?: RedisClientOptions) { super(); this.#options = this.#initiateOptions(options); - this.#socket = this.#initiateSocket(); this.#queue = this.#initiateQueue(); + this.#socket = this.#initiateSocket(); this.#isolationPool = createPool({ create: async () => { const duplicate = this.duplicate({ @@ -215,6 +215,10 @@ export default class RedisClient return options; } + #initiateQueue(): RedisCommandsQueue { + return new RedisCommandsQueue(this.#options?.commandsQueueMaxLength); + } + #initiateSocket(): RedisSocket { const socketInitiator = async (): Promise => { const promises = []; @@ -270,7 +274,7 @@ export default class RedisClient }; return new RedisSocket(socketInitiator, this.#options?.socket) - .on('data', data => this.#queue.parseResponse(data)) + .on('data', chunk => this.#queue.onReplyChunk(chunk)) .on('error', err => { this.emit('error', err); if (this.#socket.isOpen && !this.#options?.disableOfflineQueue) { @@ -289,10 +293,6 @@ export default class RedisClient .on('end', () => this.emit('end')); } - #initiateQueue(): RedisCommandsQueue { - return new RedisCommandsQueue(this.#options?.commandsQueueMaxLength); - } - #legacyMode(): void { if (!this.#options?.legacyMode) return; @@ -303,7 +303,7 @@ export default class RedisClient callback = args.pop() as ClientLegacyCallback; } - this.#sendCommand(args.flat()) + this.#sendCommand(transformLegacyCommandArguments(args)) .then((reply: RedisCommandRawReply) => { if (!callback) return; diff --git a/packages/client/lib/client/multi-command.ts b/packages/client/lib/client/multi-command.ts index 7b28637d676..a9409075caa 100644 --- a/packages/client/lib/client/multi-command.ts +++ b/packages/client/lib/client/multi-command.ts @@ -1,7 +1,7 @@ import COMMANDS from './commands'; import { RedisCommand, RedisCommandArguments, RedisCommandRawReply, RedisModules, RedisPlugins, RedisScript, RedisScripts } from '../commands'; import RedisMultiCommand, { RedisMultiQueuedCommand } from '../multi-command'; -import { extendWithCommands, extendWithModulesAndScripts } from '../commander'; +import { extendWithCommands, extendWithModulesAndScripts, transformLegacyCommandArguments } from '../commander'; import { ExcludeMappedString } from '.'; type RedisClientMultiCommandSignature = @@ -54,7 +54,7 @@ export default class RedisClientMultiCommand { #legacyMode(): void { this.v4.addCommand = this.addCommand.bind(this); (this as any).addCommand = (...args: Array): this => { - this.#multi.addCommand(args.flat()); + this.#multi.addCommand(transformLegacyCommandArguments(args)); return this; }; this.v4.exec = this.exec.bind(this); diff --git a/packages/client/lib/client/socket.ts b/packages/client/lib/client/socket.ts index b04950a0724..224eb3fa886 100644 --- a/packages/client/lib/client/socket.ts +++ b/packages/client/lib/client/socket.ts @@ -1,7 +1,6 @@ import { EventEmitter } from 'events'; import * as net from 'net'; import * as tls from 'tls'; -import { encodeCommand } from '../commander'; import { RedisCommandArguments } from '../commands'; import { ConnectionTimeoutError, ClientClosedError, SocketClosedUnexpectedlyError, ReconnectStrategyError } from '../errors'; import { promiseTimeout } from '../utils'; @@ -157,7 +156,7 @@ export default class RedisSocket extends EventEmitter { this.#writableNeedDrain = false; this.emit('drain'); }) - .on('data', (data: Buffer) => this.emit('data', data)); + .on('data', data => this.emit('data', data)); resolve(socket); }); @@ -192,7 +191,7 @@ export default class RedisSocket extends EventEmitter { throw new ClientClosedError(); } - for (const toWrite of encodeCommand(args)) { + for (const toWrite of args) { this.#writableNeedDrain = !this.#socket.write(toWrite); } } diff --git a/packages/client/lib/commander.spec.ts b/packages/client/lib/commander.spec.ts deleted file mode 100644 index f0690f37369..00000000000 --- a/packages/client/lib/commander.spec.ts +++ /dev/null @@ -1,36 +0,0 @@ -import { strict as assert } from 'assert'; -import { describe } from 'mocha'; -import { encodeCommand } from './commander'; - - -describe('Commander', () => { - describe('encodeCommand (see #1628)', () => { - it('1 byte', () => { - assert.deepEqual( - [...encodeCommand(['a', 'z'])], - ['*2\r\n$1\r\na\r\n$1\r\nz\r\n'] - ); - }); - - it('2 bytes', () => { - assert.deepEqual( - [...encodeCommand(['א', 'ת'])], - ['*2\r\n$2\r\nא\r\n$2\r\nת\r\n'] - ); - }); - - it('4 bytes', () => { - assert.deepEqual( - [...encodeCommand(['🐣', '🐤'])], - ['*2\r\n$4\r\n🐣\r\n$4\r\n🐤\r\n'] - ); - }); - - it('with a buffer', () => { - assert.deepEqual( - [...encodeCommand([Buffer.from('string')])], - ['*1\r\n$6\r\n', Buffer.from('string'), '\r\n'] - ); - }); - }); -}); diff --git a/packages/client/lib/commander.ts b/packages/client/lib/commander.ts index b3dfff0a99f..d70435e14ad 100644 --- a/packages/client/lib/commander.ts +++ b/packages/client/lib/commander.ts @@ -1,6 +1,6 @@ import { CommandOptions, isCommandOptions } from './command-options'; -import { RedisCommand, RedisCommandArgument, RedisCommandArguments, RedisCommandRawReply, RedisCommandReply, RedisCommands, RedisModules, RedisScript, RedisScripts } from './commands'; +import { RedisCommand, RedisCommandArguments, RedisCommandRawReply, RedisCommandReply, RedisCommands, RedisModules, RedisScript, RedisScripts } from './commands'; type Instantiable = new(...args: Array) => T; @@ -89,37 +89,8 @@ export function transformCommandArguments( }; } -const DELIMITER = '\r\n'; - -export function* encodeCommand(args: RedisCommandArguments): IterableIterator { - let strings = `*${args.length}${DELIMITER}`, - stringsLength = 0; - for (const arg of args) { - if (Buffer.isBuffer(arg)) { - yield `${strings}$${arg.length}${DELIMITER}`; - strings = ''; - stringsLength = 0; - yield arg; - } else { - const string = arg?.toString?.() ?? '', - byteLength = Buffer.byteLength(string); - strings += `$${byteLength}${DELIMITER}`; - - const totalLength = stringsLength + byteLength; - if (totalLength > 1024) { - yield strings; - strings = string; - stringsLength = byteLength; - } else { - strings += string; - stringsLength = totalLength; - } - } - - strings += DELIMITER; - } - - yield strings; +export function transformLegacyCommandArguments(args: Array): Array { + return args.flat().map(x => x?.toString?.()); } export function transformCommandReply( diff --git a/packages/client/lib/errors.ts b/packages/client/lib/errors.ts index 01dff992290..3f3b9624987 100644 --- a/packages/client/lib/errors.ts +++ b/packages/client/lib/errors.ts @@ -50,3 +50,10 @@ export class ReconnectStrategyError extends Error { this.socketError = socketError; } } + +export class ErrorReply extends Error { + constructor(message: string) { + super(message); + this.stack = undefined; + } +} diff --git a/packages/client/lib/test-utils.ts b/packages/client/lib/test-utils.ts index 321a9da63d5..fbed7698896 100644 --- a/packages/client/lib/test-utils.ts +++ b/packages/client/lib/test-utils.ts @@ -44,6 +44,6 @@ export async function waitTillBeenCalled(spy: SinonSpy): Promise { throw new Error('Waiting for more than 1 second'); } - await promiseTimeout(1); + await promiseTimeout(50); } while (spy.callCount === calls); } diff --git a/packages/client/package.json b/packages/client/package.json index 7ce11bb6a6a..c56d8d91fd8 100644 --- a/packages/client/package.json +++ b/packages/client/package.json @@ -16,14 +16,12 @@ "dependencies": { "cluster-key-slot": "1.1.0", "generic-pool": "3.8.2", - "redis-parser": "3.0.0", "yallist": "4.0.0" }, "devDependencies": { "@istanbuljs/nyc-config-typescript": "^1.0.2", "@node-redis/test-utils": "*", "@types/node": "^17.0.23", - "@types/redis-parser": "^3.0.0", "@types/sinon": "^10.0.11", "@types/yallist": "^4.0.1", "@typescript-eslint/eslint-plugin": "^5.19.0", diff --git a/packages/client/tsconfig.json b/packages/client/tsconfig.json index 5e044cbaa1e..3271cf400a2 100644 --- a/packages/client/tsconfig.json +++ b/packages/client/tsconfig.json @@ -11,6 +11,9 @@ "./lib/test-utils.ts", "./lib/**/*.spec.ts" ], + "ts-node": { + "transpileOnly": true + }, "typedocOptions": { "entryPoints": [ "./index.ts", From 11c6c24881b784038597e23bfead3cbdfbea021c Mon Sep 17 00:00:00 2001 From: Leibale Eidelman Date: Mon, 25 Apr 2022 09:09:23 -0400 Subject: [PATCH 1194/1748] Add support for redis functions (#2020) * fix #1906 - implement BITFIELD_RO * initial support for redis functions * fix test utils * redis functions commands and tests * upgrade deps * fix "Property 'uninstall' does not exist on type 'SinonFakeTimers'" * upgrade dockers version * Merge branch 'master' of github.com:redis/node-redis into functions * fix FUNCTION LIST WITHCODE and FUNCTION STATS * upgrade deps * set minimum version for FCALL and FCALL_RO * fix FUNCTION LOAD * FUNCTION LOAD * fix FUNCTION LOAD & FUNCTION LIST & FUNCTION LOAD WITHCODE * fix FUNCTION_LIST_WITHCODE test --- .github/workflows/tests.yml | 2 +- index.ts | 27 ++- package-lock.json | 45 +++- packages/client/index.ts | 2 +- packages/client/lib/client/commands.ts | 27 +++ packages/client/lib/client/index.spec.ts | 121 ++++++++--- packages/client/lib/client/index.ts | 193 ++++++++++-------- packages/client/lib/client/multi-command.ts | 94 ++++++--- packages/client/lib/cluster/cluster-slots.ts | 69 ++++--- packages/client/lib/cluster/commands.ts | 12 ++ packages/client/lib/cluster/index.ts | 112 +++++++--- packages/client/lib/cluster/multi-command.ts | 113 ++++++---- packages/client/lib/commander.ts | 136 ++++++++---- packages/client/lib/commands/EVAL.ts | 4 +- packages/client/lib/commands/EVALSHA.ts | 4 +- .../client/lib/commands/EVALSHA_RO.spec.ts | 17 ++ packages/client/lib/commands/EVALSHA_RO.ts | 9 + packages/client/lib/commands/EVAL_RO.spec.ts | 31 +++ packages/client/lib/commands/EVAL_RO.ts | 9 + packages/client/lib/commands/FCALL.spec.ts | 29 +++ packages/client/lib/commands/FCALL.ts | 7 + packages/client/lib/commands/FCALL_RO.spec.ts | 29 +++ packages/client/lib/commands/FCALL_RO.ts | 9 + .../lib/commands/FUNCTION_DELETE.spec.ts | 24 +++ .../client/lib/commands/FUNCTION_DELETE.ts | 7 + .../client/lib/commands/FUNCTION_DUMP.spec.ts | 21 ++ packages/client/lib/commands/FUNCTION_DUMP.ts | 7 + .../lib/commands/FUNCTION_FLUSH.spec.ts | 30 +++ .../client/lib/commands/FUNCTION_FLUSH.ts | 13 ++ .../client/lib/commands/FUNCTION_KILL.spec.ts | 14 ++ packages/client/lib/commands/FUNCTION_KILL.ts | 7 + .../client/lib/commands/FUNCTION_LIST.spec.ts | 41 ++++ packages/client/lib/commands/FUNCTION_LIST.ts | 16 ++ .../commands/FUNCTION_LIST_WITHCODE.spec.ts | 42 ++++ .../lib/commands/FUNCTION_LIST_WITHCODE.ts | 26 +++ .../client/lib/commands/FUNCTION_LOAD.spec.ts | 36 ++++ packages/client/lib/commands/FUNCTION_LOAD.ts | 22 ++ .../lib/commands/FUNCTION_RESTORE.spec.ts | 37 ++++ .../client/lib/commands/FUNCTION_RESTORE.ts | 16 ++ .../lib/commands/FUNCTION_STATS.spec.ts | 25 +++ .../client/lib/commands/FUNCTION_STATS.ts | 56 +++++ .../lib/commands/generic-transformers.ts | 49 +++++ packages/client/lib/commands/index.ts | 63 +++++- packages/client/lib/lua-script.ts | 2 +- packages/client/lib/multi-command.ts | 19 +- packages/client/lib/test-utils.ts | 2 +- packages/graph/lib/test-utils.ts | 2 +- packages/json/lib/test-utils.ts | 2 +- packages/test-utils/lib/dockers.ts | 8 +- packages/test-utils/lib/index.ts | 44 ++-- packages/time-series/lib/test-utils.ts | 2 +- 51 files changed, 1408 insertions(+), 326 deletions(-) create mode 100644 packages/client/lib/commands/EVALSHA_RO.spec.ts create mode 100644 packages/client/lib/commands/EVALSHA_RO.ts create mode 100644 packages/client/lib/commands/EVAL_RO.spec.ts create mode 100644 packages/client/lib/commands/EVAL_RO.ts create mode 100644 packages/client/lib/commands/FCALL.spec.ts create mode 100644 packages/client/lib/commands/FCALL.ts create mode 100644 packages/client/lib/commands/FCALL_RO.spec.ts create mode 100644 packages/client/lib/commands/FCALL_RO.ts create mode 100644 packages/client/lib/commands/FUNCTION_DELETE.spec.ts create mode 100644 packages/client/lib/commands/FUNCTION_DELETE.ts create mode 100644 packages/client/lib/commands/FUNCTION_DUMP.spec.ts create mode 100644 packages/client/lib/commands/FUNCTION_DUMP.ts create mode 100644 packages/client/lib/commands/FUNCTION_FLUSH.spec.ts create mode 100644 packages/client/lib/commands/FUNCTION_FLUSH.ts create mode 100644 packages/client/lib/commands/FUNCTION_KILL.spec.ts create mode 100644 packages/client/lib/commands/FUNCTION_KILL.ts create mode 100644 packages/client/lib/commands/FUNCTION_LIST.spec.ts create mode 100644 packages/client/lib/commands/FUNCTION_LIST.ts create mode 100644 packages/client/lib/commands/FUNCTION_LIST_WITHCODE.spec.ts create mode 100644 packages/client/lib/commands/FUNCTION_LIST_WITHCODE.ts create mode 100644 packages/client/lib/commands/FUNCTION_LOAD.spec.ts create mode 100644 packages/client/lib/commands/FUNCTION_LOAD.ts create mode 100644 packages/client/lib/commands/FUNCTION_RESTORE.spec.ts create mode 100644 packages/client/lib/commands/FUNCTION_RESTORE.ts create mode 100644 packages/client/lib/commands/FUNCTION_STATS.spec.ts create mode 100644 packages/client/lib/commands/FUNCTION_STATS.ts diff --git a/.github/workflows/tests.yml b/.github/workflows/tests.yml index a0c8223d663..ba11eaa2960 100644 --- a/.github/workflows/tests.yml +++ b/.github/workflows/tests.yml @@ -17,7 +17,7 @@ jobs: fail-fast: false matrix: node-version: ['12', '14', '16'] - redis-version: ['5', '6.0', '6.2', '7.0-rc2'] + redis-version: ['5', '6.0', '6.2', '7.0-rc3'] steps: - uses: actions/checkout@v2.3.4 with: diff --git a/index.ts b/index.ts index c948e5eac33..cb49e6f7f4d 100644 --- a/index.ts +++ b/index.ts @@ -1,5 +1,6 @@ import { RedisModules, + RedisFunctions, RedisScripts, createClient as _createClient, RedisClientOptions, @@ -33,12 +34,17 @@ export type RedisDefaultModules = typeof modules; export type RedisClientType< M extends RedisModules = RedisDefaultModules, + F extends RedisFunctions = Record, S extends RedisScripts = Record -> = _RedisClientType; +> = _RedisClientType; -export function createClient( - options?: RedisClientOptions -): _RedisClientType { +export function createClient< + M extends RedisModules, + F extends RedisFunctions, + S extends RedisScripts +>( + options?: RedisClientOptions +): _RedisClientType { return _createClient({ ...options, modules: { @@ -50,12 +56,17 @@ export function createClient( export type RedisClusterType< M extends RedisModules = RedisDefaultModules, + F extends RedisFunctions = Record, S extends RedisScripts = Record -> = _RedisClusterType; +> = _RedisClusterType; -export function createCluster( - options: RedisClusterOptions -): RedisClusterType { +export function createCluster< + M extends RedisModules, + F extends RedisFunctions, + S extends RedisScripts +>( + options: RedisClusterOptions +): RedisClusterType { return _createCluster({ ...options, modules: { diff --git a/package-lock.json b/package-lock.json index 2d3f22c5b58..9152f212fb8 100644 --- a/package-lock.json +++ b/package-lock.json @@ -1865,6 +1865,19 @@ "integrity": "sha1-3dgA2gxmEnOTzKWVDqloo6rxJTs=", "dev": true }, + "node_modules/compress-brotli": { + "version": "1.3.6", + "resolved": "https://registry.npmjs.org/compress-brotli/-/compress-brotli-1.3.6.tgz", + "integrity": "sha512-au99/GqZtUtiCBliqLFbWlhnCxn+XSYjwZ77q6mKN4La4qOXDoLVPZ50iXr0WmAyMxl8yqoq3Yq4OeQNPPkyeQ==", + "dev": true, + "dependencies": { + "@types/json-buffer": "~3.0.0", + "json-buffer": "~3.0.1" + }, + "engines": { + "node": ">= 12" + } + }, "node_modules/concat-map": { "version": "0.0.1", "resolved": "https://registry.npmjs.org/concat-map/-/concat-map-0.0.1.tgz", @@ -3332,9 +3345,9 @@ } }, "node_modules/inquirer": { - "version": "8.2.0", - "resolved": "https://registry.npmjs.org/inquirer/-/inquirer-8.2.0.tgz", - "integrity": "sha512-0crLweprevJ02tTuA6ThpoAERAGyVILC4sS74uib58Xf/zSr1/ZWtmm7D5CI+bSQEaA04f0K7idaHpQbSWgiVQ==", + "version": "8.2.2", + "resolved": "https://registry.npmjs.org/inquirer/-/inquirer-8.2.2.tgz", + "integrity": "sha512-pG7I/si6K/0X7p1qU+rfWnpTE1UIkTONN1wxtzh0d+dHXtT/JG6qBgLxoyHVsQa8cFABxAPh0pD6uUUHiAoaow==", "dev": true, "dependencies": { "ansi-escapes": "^4.2.1", @@ -3347,13 +3360,13 @@ "mute-stream": "0.0.8", "ora": "^5.4.1", "run-async": "^2.4.0", - "rxjs": "^7.2.0", + "rxjs": "^7.5.5", "string-width": "^4.1.0", "strip-ansi": "^6.0.0", "through": "^2.3.6" }, "engines": { - "node": ">=8.0.0" + "node": ">=12.0.0" } }, "node_modules/internal-slot": { @@ -5453,7 +5466,7 @@ "globby": "11.0.4", "got": "9.6.0", "import-cwd": "3.0.0", - "inquirer": "8.2.0", + "inquirer": "8.2.2", "is-ci": "3.0.1", "lodash": "4.17.21", "mime-types": "2.1.35", @@ -8382,6 +8395,16 @@ "integrity": "sha1-3dgA2gxmEnOTzKWVDqloo6rxJTs=", "dev": true }, + "compress-brotli": { + "version": "1.3.6", + "resolved": "https://registry.npmjs.org/compress-brotli/-/compress-brotli-1.3.6.tgz", + "integrity": "sha512-au99/GqZtUtiCBliqLFbWlhnCxn+XSYjwZ77q6mKN4La4qOXDoLVPZ50iXr0WmAyMxl8yqoq3Yq4OeQNPPkyeQ==", + "dev": true, + "requires": { + "@types/json-buffer": "~3.0.0", + "json-buffer": "~3.0.1" + } + }, "concat-map": { "version": "0.0.1", "resolved": "https://registry.npmjs.org/concat-map/-/concat-map-0.0.1.tgz", @@ -9474,9 +9497,9 @@ "dev": true }, "inquirer": { - "version": "8.2.0", - "resolved": "https://registry.npmjs.org/inquirer/-/inquirer-8.2.0.tgz", - "integrity": "sha512-0crLweprevJ02tTuA6ThpoAERAGyVILC4sS74uib58Xf/zSr1/ZWtmm7D5CI+bSQEaA04f0K7idaHpQbSWgiVQ==", + "version": "8.2.2", + "resolved": "https://registry.npmjs.org/inquirer/-/inquirer-8.2.2.tgz", + "integrity": "sha512-pG7I/si6K/0X7p1qU+rfWnpTE1UIkTONN1wxtzh0d+dHXtT/JG6qBgLxoyHVsQa8cFABxAPh0pD6uUUHiAoaow==", "dev": true, "requires": { "ansi-escapes": "^4.2.1", @@ -9489,7 +9512,7 @@ "mute-stream": "0.0.8", "ora": "^5.4.1", "run-async": "^2.4.0", - "rxjs": "^7.2.0", + "rxjs": "^7.5.5", "string-width": "^4.1.0", "strip-ansi": "^6.0.0", "through": "^2.3.6" @@ -11034,7 +11057,7 @@ "globby": "11.0.4", "got": "9.6.0", "import-cwd": "3.0.0", - "inquirer": "8.2.0", + "inquirer": "8.2.2", "is-ci": "3.0.1", "lodash": "4.17.21", "mime-types": "2.1.35", diff --git a/packages/client/index.ts b/packages/client/index.ts index ac138a935e4..734c15664df 100644 --- a/packages/client/index.ts +++ b/packages/client/index.ts @@ -3,7 +3,7 @@ import RedisCluster from './lib/cluster'; export { RedisClientType, RedisClientOptions } from './lib/client'; -export { RedisModules, RedisScripts } from './lib/commands'; +export { RedisModules, RedisFunctions, RedisScripts } from './lib/commands'; export const createClient = RedisClient.create; diff --git a/packages/client/lib/client/commands.ts b/packages/client/lib/client/commands.ts index eefa1346585..94653e476b1 100644 --- a/packages/client/lib/client/commands.ts +++ b/packages/client/lib/client/commands.ts @@ -62,6 +62,15 @@ import * as ECHO from '../commands/ECHO'; import * as FAILOVER from '../commands/FAILOVER'; import * as FLUSHALL from '../commands/FLUSHALL'; import * as FLUSHDB from '../commands/FLUSHDB'; +import * as FUNCTION_DELETE from '../commands/FUNCTION_DELETE'; +import * as FUNCTION_DUMP from '../commands/FUNCTION_DUMP'; +import * as FUNCTION_FLUSH from '../commands/FUNCTION_FLUSH'; +import * as FUNCTION_KILL from '../commands/FUNCTION_KILL'; +import * as FUNCTION_LIST_WITHCODE from '../commands/FUNCTION_LIST_WITHCODE'; +import * as FUNCTION_LIST from '../commands/FUNCTION_LIST'; +import * as FUNCTION_LOAD from '../commands/FUNCTION_LOAD'; +import * as FUNCTION_RESTORE from '../commands/FUNCTION_RESTORE'; +import * as FUNCTION_STATS from '../commands/FUNCTION_STATS'; import * as HELLO from '../commands/HELLO'; import * as INFO from '../commands/INFO'; import * as KEYS from '../commands/KEYS'; @@ -228,6 +237,24 @@ export default { flushAll: FLUSHALL, FLUSHDB, flushDb: FLUSHDB, + FUNCTION_DELETE, + functionDelete: FUNCTION_DELETE, + FUNCTION_DUMP, + functionDump: FUNCTION_DUMP, + FUNCTION_FLUSH, + functionFlush: FUNCTION_FLUSH, + FUNCTION_KILL, + functionKill: FUNCTION_KILL, + FUNCTION_LIST_WITHCODE, + functionListWithCode: FUNCTION_LIST_WITHCODE, + FUNCTION_LIST, + functionList: FUNCTION_LIST, + FUNCTION_LOAD, + functionLoad: FUNCTION_LOAD, + FUNCTION_RESTORE, + functionRestore: FUNCTION_RESTORE, + FUNCTION_STATS, + functionStats: FUNCTION_STATS, HELLO, hello: HELLO, INFO, diff --git a/packages/client/lib/client/index.spec.ts b/packages/client/lib/client/index.spec.ts index a6b924d42ac..349a567a304 100644 --- a/packages/client/lib/client/index.spec.ts +++ b/packages/client/lib/client/index.spec.ts @@ -2,7 +2,7 @@ import { strict as assert } from 'assert'; import testUtils, { GLOBAL, waitTillBeenCalled } from '../test-utils'; import RedisClient, { RedisClientType } from '.'; import { RedisClientMultiCommandType } from './multi-command'; -import { RedisCommandArguments, RedisCommandRawReply, RedisModules, RedisScripts } from '../commands'; +import { RedisCommandArguments, RedisCommandRawReply, RedisModules, RedisFunctions, RedisScripts } from '../commands'; import { AbortError, ClientClosedError, ConnectionTimeoutError, DisconnectsClientError, SocketClosedUnexpectedlyError, WatchError } from '../errors'; import { defineScript } from '../lua-script'; import { spy } from 'sinon'; @@ -10,16 +10,42 @@ import { once } from 'events'; import { ClientKillFilters } from '../commands/CLIENT_KILL'; export const SQUARE_SCRIPT = defineScript({ - NUMBER_OF_KEYS: 0, SCRIPT: 'return ARGV[1] * ARGV[1];', + NUMBER_OF_KEYS: 0, transformArguments(number: number): Array { return [number.toString()]; - }, - transformReply(reply: number): number { - return reply; } }); +export const MATH_FUNCTION = { + name: 'math', + engine: 'LUA', + code: `#!LUA name=math + redis.register_function{ + function_name = "square", + callback = function(keys, args) return args[1] * args[1] end, + flags = { "no-writes" } + }`, + library: { + square: { + NAME: 'square', + NUMBER_OF_KEYS: 0, + transformArguments(number: number): Array { + return [number.toString()]; + } + } + } +}; + +export async function loadMathFunction( + client: RedisClientType +): Promise { + await client.functionLoad( + MATH_FUNCTION.code, + { REPLACE: true } + ); +} + describe('Client', () => { describe('parseURL', () => { it('redis://user:secret@localhost:6379/0', () => { @@ -115,7 +141,14 @@ describe('Client', () => { }); describe('legacyMode', () => { - function sendCommandAsync(client: RedisClientType, args: RedisCommandArguments): Promise { + function sendCommandAsync< + M extends RedisModules, + F extends RedisFunctions, + S extends RedisScripts + >( + client: RedisClientType, + args: RedisCommandArguments + ): Promise { return new Promise((resolve, reject) => { (client as any).sendCommand(args, (err: Error | undefined, reply: RedisCommandRawReply) => { if (err) return reject(err); @@ -159,7 +192,14 @@ describe('Client', () => { } }); - function setAsync(client: RedisClientType, ...args: Array): Promise { + function setAsync< + M extends RedisModules, + F extends RedisFunctions, + S extends RedisScripts + >( + client: RedisClientType, + ...args: Array + ): Promise { return new Promise((resolve, reject) => { (client as any).set(...args, (err: Error | undefined, reply: RedisCommandRawReply) => { if (err) return reject(err); @@ -205,7 +245,11 @@ describe('Client', () => { } }); - function multiExecAsync(multi: RedisClientMultiCommandType): Promise> { + function multiExecAsync< + M extends RedisModules, + F extends RedisFunctions, + S extends RedisScripts + >(multi: RedisClientMultiCommandType): Promise> { return new Promise((resolve, reject) => { (multi as any).exec((err: Error | undefined, replies: Array) => { if (err) return reject(err); @@ -439,25 +483,44 @@ describe('Client', () => { } }); + const module = { + echo: { + transformArguments(message: string): Array { + return ['ECHO', message]; + }, + transformReply(reply: string): string { + return reply; + } + } + }; + testUtils.testWithClient('modules', async client => { - // assert.equal( - // await client.module.echo('message'), - // 'message' - // ); + assert.equal( + await client.module.echo('message'), + 'message' + ); }, { ...GLOBAL.SERVERS.OPEN, clientOptions: { modules: { - module: { - echo: { - transformArguments(message: string): Array { - return ['ECHO', message]; - }, - transformReply(reply: string): string { - return reply; - } - } - } + module + } + } + }); + + testUtils.testWithClient('functions', async client => { + await loadMathFunction(client); + + assert.equal( + await client.math.square(2), + 4 + ); + }, { + ...GLOBAL.SERVERS.OPEN, + minimumDockerVersion: [7, 0], + clientOptions: { + functions: { + math: MATH_FUNCTION.library } } }); @@ -468,9 +531,13 @@ describe('Client', () => { assert.ok(id !== isolatedId); }, GLOBAL.SERVERS.OPEN); - async function killClient( - client: RedisClientType, - errorClient: RedisClientType = client + async function killClient< + M extends RedisModules, + F extends RedisFunctions, + S extends RedisScripts + >( + client: RedisClientType, + errorClient: RedisClientType = client ): Promise { const onceErrorPromise = once(errorClient, 'error'); await client.sendCommand(['QUIT']); @@ -684,7 +751,9 @@ describe('Client', () => { try { await assert.doesNotReject(Promise.all([ - subscriber.subscribe('channel', () => {}), + subscriber.subscribe('channel', () => { + // noop + }), publisher.publish('channel', 'message') ])); } finally { diff --git a/packages/client/lib/client/index.ts b/packages/client/lib/client/index.ts index 242c590cc80..e78a491cc17 100644 --- a/packages/client/lib/client/index.ts +++ b/packages/client/lib/client/index.ts @@ -1,5 +1,5 @@ import COMMANDS from './commands'; -import { RedisCommand, RedisCommandArgument, RedisCommandArguments, RedisCommandRawReply, RedisCommandReply, RedisModules, RedisPlugins, RedisScript, RedisScripts } from '../commands'; +import { RedisCommand, RedisCommandArguments, RedisCommandRawReply, RedisCommandReply, RedisFunctions, RedisModules, RedisExtensions, RedisScript, RedisScripts, RedisCommandSignature, ConvertArgumentType, RedisFunction, ExcludeMappedString } from '../commands'; import RedisSocket, { RedisSocketOptions, RedisTlsSocketOptions } from './socket'; import RedisCommandsQueue, { PubSubListener, PubSubSubscribeCommands, PubSubUnsubscribeCommands, QueueCommandOptions } from './commands-queue'; import RedisClientMultiCommand, { RedisClientMultiCommandType } from './multi-command'; @@ -9,16 +9,17 @@ import { CommandOptions, commandOptions, isCommandOptions } from '../command-opt import { ScanOptions, ZMember } from '../commands/generic-transformers'; import { ScanCommandOptions } from '../commands/SCAN'; import { HScanTuple } from '../commands/HSCAN'; -import { extendWithCommands, extendWithModulesAndScripts, transformCommandArguments, transformCommandReply, transformLegacyCommandArguments } from '../commander'; +import { attachCommands, attachExtensions, fCallArguments, transformCommandArguments, transformCommandReply, transformLegacyCommandArguments } from '../commander'; import { Pool, Options as PoolOptions, createPool } from 'generic-pool'; import { ClientClosedError, DisconnectsClientError } from '../errors'; import { URL } from 'url'; import { TcpSocketConnectOpts } from 'net'; export interface RedisClientOptions< - M extends RedisModules = Record, - S extends RedisScripts = Record -> extends RedisPlugins { + M extends RedisModules = RedisModules, + F extends RedisFunctions = RedisFunctions, + S extends RedisScripts = RedisScripts +> extends RedisExtensions { url?: string; socket?: RedisSocketOptions; username?: string; @@ -32,58 +33,37 @@ export interface RedisClientOptions< isolationPoolOptions?: PoolOptions; } -type ConvertArgumentType = - Type extends RedisCommandArgument ? ( - Type extends (string & ToType) ? Type : ToType - ) : ( - Type extends Set ? Set> : ( - Type extends Map ? Map> : ( - Type extends Array ? Array> : ( - Type extends Date ? Type : ( - Type extends Record ? { - [Property in keyof Type]: ConvertArgumentType - } : Type - ) - ) - ) - ) - ); - -export type RedisClientCommandSignature< - Command extends RedisCommand, - Params extends Array = Parameters -> = >( - ...args: Params | [options: Options, ...rest: Params] -) => Promise< - ConvertArgumentType< - RedisCommandReply, - Options['returnBuffers'] extends true ? Buffer : string - > ->; - type WithCommands = { - [P in keyof typeof COMMANDS]: RedisClientCommandSignature<(typeof COMMANDS)[P]>; + [P in keyof typeof COMMANDS]: RedisCommandSignature<(typeof COMMANDS)[P]>; }; -export type ExcludeMappedString = string extends S ? never : S; - export type WithModules = { [P in keyof M as ExcludeMappedString

]: { - [C in keyof M[P] as ExcludeMappedString]: RedisClientCommandSignature; + [C in keyof M[P] as ExcludeMappedString]: RedisCommandSignature; + }; +}; + +export type WithFunctions = { + [P in keyof F as ExcludeMappedString

]: { + [FF in keyof F[P] as ExcludeMappedString]: RedisCommandSignature; }; }; export type WithScripts = { - [P in keyof S as ExcludeMappedString

]: RedisClientCommandSignature; + [P in keyof S as ExcludeMappedString

]: RedisCommandSignature; }; export type RedisClientType< M extends RedisModules = Record, + F extends RedisFunctions = Record, S extends RedisScripts = Record -> = RedisClient & WithCommands & WithModules & WithScripts; +> = RedisClient & WithCommands & WithModules & WithFunctions & WithScripts; -export type InstantiableRedisClient = - new (options?: RedisClientOptions) => RedisClientType; +export type InstantiableRedisClient< + M extends RedisModules, + F extends RedisFunctions, + S extends RedisScripts +> = new (options?: RedisClientOptions) => RedisClientType; export interface ClientCommandOptions extends QueueCommandOptions { isolated?: boolean; @@ -91,30 +71,44 @@ export interface ClientCommandOptions extends QueueCommandOptions { type ClientLegacyCallback = (err: Error | null, reply?: RedisCommandRawReply) => void; -export default class RedisClient extends EventEmitter { +export default class RedisClient< + M extends RedisModules, + F extends RedisFunctions, + S extends RedisScripts +> extends EventEmitter { static commandOptions(options: T): CommandOptions { return commandOptions(options); } commandOptions = RedisClient.commandOptions; - static extend(plugins?: RedisPlugins): InstantiableRedisClient { - const Client = extendWithModulesAndScripts({ + static extend< + M extends RedisModules, + F extends RedisFunctions, + S extends RedisScripts + >(extensions?: RedisExtensions): InstantiableRedisClient { + const Client = attachExtensions({ BaseClass: RedisClient, - modules: plugins?.modules, - modulesCommandsExecutor: RedisClient.prototype.commandsExecutor, - scripts: plugins?.scripts, - scriptsExecutor: RedisClient.prototype.scriptsExecutor + modulesExecutor: RedisClient.prototype.commandsExecutor, + modules: extensions?.modules, + functionsExecutor: RedisClient.prototype.functionsExecuter, + functions: extensions?.functions, + scriptsExecutor: RedisClient.prototype.scriptsExecuter, + scripts: extensions?.scripts }); if (Client !== RedisClient) { - Client.prototype.Multi = RedisClientMultiCommand.extend(plugins); + Client.prototype.Multi = RedisClientMultiCommand.extend(extensions); } return Client; } - static create(options?: RedisClientOptions): RedisClientType { + static create< + M extends RedisModules, + F extends RedisFunctions, + S extends RedisScripts + >(options?: RedisClientOptions): RedisClientType { return new (RedisClient.extend(options))(options); } @@ -157,14 +151,14 @@ export default class RedisClient return parsed; } - readonly #options?: RedisClientOptions; - readonly #queue: RedisCommandsQueue; + readonly #options?: RedisClientOptions; readonly #socket: RedisSocket; - readonly #isolationPool: Pool>; + readonly #queue: RedisCommandsQueue; + readonly #isolationPool: Pool>; readonly #v4: Record = {}; #selectedDB = 0; - get options(): RedisClientOptions | undefined { + get options(): RedisClientOptions | undefined { return this.#options; } @@ -180,7 +174,7 @@ export default class RedisClient return this.#v4; } - constructor(options?: RedisClientOptions) { + constructor(options?: RedisClientOptions) { super(); this.#options = this.#initiateOptions(options); this.#queue = this.#initiateQueue(); @@ -198,7 +192,7 @@ export default class RedisClient this.#legacyMode(); } - #initiateOptions(options?: RedisClientOptions): RedisClientOptions | undefined { + #initiateOptions(options?: RedisClientOptions): RedisClientOptions | undefined { if (options?.url) { const parsed = RedisClient.parseURL(options.url); if (options.socket) { @@ -350,7 +344,7 @@ export default class RedisClient (...args: Array): void => (this as any).sendCommand(name, ...args); } - duplicate(overrides?: Partial>): RedisClientType { + duplicate(overrides?: Partial>): RedisClientType { return new (Object.getPrototypeOf(this).constructor)({ ...this.#options, ...overrides @@ -361,9 +355,11 @@ export default class RedisClient await this.#socket.connect(); } - async commandsExecutor(command: RedisCommand, args: Array): Promise> { - const { args: redisArgs, options } = transformCommandArguments(command, args); - + async commandsExecutor( + command: C, + args: Array + ): Promise> { + const { args: redisArgs, options } = transformCommandArguments(command, args); return transformCommandReply( command, await this.#sendCommand(redisArgs, options), @@ -371,12 +367,18 @@ export default class RedisClient ); } - sendCommand(args: RedisCommandArguments, options?: ClientCommandOptions): Promise { + sendCommand( + args: RedisCommandArguments, + options?: ClientCommandOptions + ): Promise { return this.#sendCommand(args, options); } // using `#sendCommand` cause `sendCommand` is overwritten in legacy mode - #sendCommand(args: RedisCommandArguments, options?: ClientCommandOptions): Promise { + #sendCommand( + args: RedisCommandArguments, + options?: ClientCommandOptions + ): Promise { if (!this.#socket.isOpen) { return Promise.reject(new ClientClosedError()); } @@ -395,9 +397,34 @@ export default class RedisClient return promise; } - async scriptsExecutor(script: RedisScript, args: Array): Promise> { - const { args: redisArgs, options } = transformCommandArguments(script, args); + async functionsExecuter( + fn: F, + args: Array + ): Promise> { + const { args: redisArgs, options } = transformCommandArguments(fn, args); + return transformCommandReply( + fn, + await this.executeFunction(fn, redisArgs, options), + redisArgs.preserve + ); + } + executeFunction( + fn: RedisFunction, + args: RedisCommandArguments, + options?: ClientCommandOptions + ): Promise { + return this.#sendCommand( + fCallArguments(fn, args), + options + ); + } + + async scriptsExecuter( + script: S, + args: Array + ): Promise> { + const { args: redisArgs, options } = transformCommandArguments(script, args); return transformCommandReply( script, await this.executeScript(script, redisArgs, options), @@ -405,25 +432,29 @@ export default class RedisClient ); } - async executeScript(script: RedisScript, args: RedisCommandArguments, options?: ClientCommandOptions): Promise> { + async executeScript( + script: RedisScript, + args: RedisCommandArguments, + options?: ClientCommandOptions + ): Promise { + const redisArgs: RedisCommandArguments = ['EVALSHA', script.SHA1]; + + if (script.NUMBER_OF_KEYS !== undefined) { + redisArgs.push(script.NUMBER_OF_KEYS.toString()); + } + + redisArgs.push(...args); + try { - return await this.#sendCommand([ - 'EVALSHA', - script.SHA1, - script.NUMBER_OF_KEYS.toString(), - ...args - ], options); + return await this.#sendCommand(redisArgs, options); } catch (err: any) { if (!err?.message?.startsWith?.('NOSCRIPT')) { throw err; } - return await this.#sendCommand([ - 'EVAL', - script.SCRIPT, - script.NUMBER_OF_KEYS.toString(), - ...args - ], options); + redisArgs[0] = 'EVAL'; + redisArgs[1] = script.SCRIPT; + return this.#sendCommand(redisArgs, options); } } @@ -558,11 +589,11 @@ export default class RedisClient } } - executeIsolated(fn: (client: RedisClientType) => T | Promise): Promise { + executeIsolated(fn: (client: RedisClientType) => T | Promise): Promise { return this.#isolationPool.use(fn); } - multi(): RedisClientMultiCommandType { + multi(): RedisClientMultiCommandType { return new (this as any).Multi( this.multiExecutor.bind(this), this.#options?.legacyMode @@ -639,7 +670,7 @@ export default class RedisClient } } -extendWithCommands({ +attachCommands({ BaseClass: RedisClient, commands: COMMANDS, executor: RedisClient.prototype.commandsExecutor diff --git a/packages/client/lib/client/multi-command.ts b/packages/client/lib/client/multi-command.ts index a9409075caa..8fda702a317 100644 --- a/packages/client/lib/client/multi-command.ts +++ b/packages/client/lib/client/multi-command.ts @@ -1,28 +1,63 @@ import COMMANDS from './commands'; -import { RedisCommand, RedisCommandArguments, RedisCommandRawReply, RedisModules, RedisPlugins, RedisScript, RedisScripts } from '../commands'; +import { RedisCommand, RedisCommandArguments, RedisCommandRawReply, RedisFunctions, RedisModules, RedisExtensions, RedisScript, RedisScripts, ExcludeMappedString, RedisFunction } from '../commands'; import RedisMultiCommand, { RedisMultiQueuedCommand } from '../multi-command'; -import { extendWithCommands, extendWithModulesAndScripts, transformLegacyCommandArguments } from '../commander'; -import { ExcludeMappedString } from '.'; - -type RedisClientMultiCommandSignature = - (...args: Parameters) => RedisClientMultiCommandType; - -type WithCommands = { - [P in keyof typeof COMMANDS]: RedisClientMultiCommandSignature<(typeof COMMANDS)[P], M, S>; +import { attachCommands, attachExtensions, transformLegacyCommandArguments } from '../commander'; + +type CommandSignature< + C extends RedisCommand, + M extends RedisModules, + F extends RedisFunctions, + S extends RedisScripts +> = (...args: Parameters) => RedisClientMultiCommandType; + +type WithCommands< + M extends RedisModules, + F extends RedisFunctions, + S extends RedisScripts +> = { + [P in keyof typeof COMMANDS]: CommandSignature<(typeof COMMANDS)[P], M, F, S>; }; -type WithModules = { +type WithModules< + M extends RedisModules, + F extends RedisFunctions, + S extends RedisScripts +> = { [P in keyof M as ExcludeMappedString

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

]: { + [FF in keyof F[P] as ExcludeMappedString]: CommandSignature; }; }; -type WithScripts = { - [P in keyof S as ExcludeMappedString

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

]: CommandSignature; }; -export type RedisClientMultiCommandType = - RedisClientMultiCommand & WithCommands & WithModules & WithScripts; +export type RedisClientMultiCommandType< + M extends RedisModules, + F extends RedisFunctions, + S extends RedisScripts +> = RedisClientMultiCommand & WithCommands & WithModules & WithFunctions & WithScripts; + +type InstantiableRedisMultiCommand< + M extends RedisModules, + F extends RedisFunctions, + S extends RedisScripts +> = new (...args: ConstructorParameters) => RedisClientMultiCommandType; + export type RedisClientMultiExecutor = (queue: Array, chainId?: symbol) => Promise>; @@ -30,15 +65,19 @@ export default class RedisClientMultiCommand { readonly #multi = new RedisMultiCommand(); readonly #executor: RedisClientMultiExecutor; - static extend( - plugins?: RedisPlugins - ): new (...args: ConstructorParameters) => RedisClientMultiCommandType { - return extendWithModulesAndScripts({ + static extend< + M extends RedisModules, + F extends RedisFunctions, + S extends RedisScripts + >(extensions?: RedisExtensions): InstantiableRedisMultiCommand { + return attachExtensions({ BaseClass: RedisClientMultiCommand, - modules: plugins?.modules, - modulesCommandsExecutor: RedisClientMultiCommand.prototype.commandsExecutor, - scripts: plugins?.scripts, - scriptsExecutor: RedisClientMultiCommand.prototype.scriptsExecutor + modulesExecutor: RedisClientMultiCommand.prototype.commandsExecutor, + modules: extensions?.modules, + functionsExecutor: RedisClientMultiCommand.prototype.functionsExecutor, + functions: extensions?.functions, + scriptsExecutor: RedisClientMultiCommand.prototype.scriptsExecutor, + scripts: extensions?.scripts }); } @@ -102,6 +141,11 @@ export default class RedisClientMultiCommand { return this; } + functionsExecutor(fn: RedisFunction, args: Array): this { + this.#multi.addFunction(fn, args); + return this; + } + scriptsExecutor(script: RedisScript, args: Array): this { this.#multi.addScript(script, args); return this; @@ -123,15 +167,13 @@ export default class RedisClientMultiCommand { EXEC = this.exec; async execAsPipeline(): Promise> { - if (!this.#multi.queue.length) return []; - return this.#multi.transformReplies( await this.#executor(this.#multi.queue) ); } } -extendWithCommands({ +attachCommands({ BaseClass: RedisClientMultiCommand, commands: COMMANDS, executor: RedisClientMultiCommand.prototype.commandsExecutor diff --git a/packages/client/lib/cluster/cluster-slots.ts b/packages/client/lib/cluster/cluster-slots.ts index 872b5462ac5..d23ef569f30 100644 --- a/packages/client/lib/cluster/cluster-slots.ts +++ b/packages/client/lib/cluster/cluster-slots.ts @@ -1,7 +1,7 @@ import RedisClient, { InstantiableRedisClient, RedisClientType } from '../client'; import { RedisClusterMasterNode, RedisClusterReplicaNode } from '../commands/CLUSTER_NODES'; import { RedisClusterClientOptions, RedisClusterOptions } from '.'; -import { RedisCommandArgument, RedisModules, RedisScripts } from '../commands'; +import { RedisCommandArgument, RedisFunctions, RedisModules, RedisScripts } from '../commands'; import { RootNodesUnavailableError } from '../errors'; // We need to use 'require', because it's not possible with Typescript to import @@ -9,9 +9,13 @@ import { RootNodesUnavailableError } from '../errors'; // set to true. const calculateSlot = require('cluster-key-slot'); -export interface ClusterNode { +export interface ClusterNode< + M extends RedisModules, + F extends RedisFunctions, + S extends RedisScripts +> { id: string; - client: RedisClientType; + client: RedisClientType; } interface NodeAddress { @@ -23,22 +27,30 @@ export type NodeAddressMap = { [address: string]: NodeAddress; } | ((address: string) => NodeAddress | undefined); -interface SlotNodes { - master: ClusterNode; - replicas: Array>; - clientIterator: IterableIterator> | undefined; +interface SlotNodes< + M extends RedisModules, + F extends RedisFunctions, + S extends RedisScripts +> { + master: ClusterNode; + replicas: Array>; + clientIterator: IterableIterator> | undefined; } type OnError = (err: unknown) => void; -export default class RedisClusterSlots { - readonly #options: RedisClusterOptions; - readonly #Client: InstantiableRedisClient; +export default class RedisClusterSlots< + M extends RedisModules, + F extends RedisFunctions, + S extends RedisScripts +> { + readonly #options: RedisClusterOptions; + readonly #Client: InstantiableRedisClient; readonly #onError: OnError; - readonly #nodeByAddress = new Map>(); - readonly #slots: Array> = []; + readonly #nodeByAddress = new Map>(); + readonly #slots: Array> = []; - constructor(options: RedisClusterOptions, onError: OnError) { + constructor(options: RedisClusterOptions, onError: OnError) { this.#options = options; this.#Client = RedisClient.extend(options); this.#onError = onError; @@ -72,7 +84,7 @@ export default class RedisClusterSlots; - async rediscover(startWith: RedisClientType): Promise { + async rediscover(startWith: RedisClientType): Promise { if (!this.#runningRediscoverPromise) { this.#runningRediscoverPromise = this.#rediscover(startWith) .finally(() => this.#runningRediscoverPromise = undefined); @@ -81,7 +93,7 @@ export default class RedisClusterSlots): Promise { + async #rediscover(startWith: RedisClientType): Promise { if (await this.#discoverNodes(startWith.options)) return; for (const { client } of this.#nodeByAddress.values()) { @@ -137,7 +149,7 @@ export default class RedisClusterSlots { + #initiateClient(options?: RedisClusterClientOptions): RedisClientType { return new this.#Client(this.#clientOptionsDefaults(options)) .on('error', this.#onError); } @@ -152,7 +164,12 @@ export default class RedisClusterSlots, promises: Array>): ClusterNode { + #initiateClientForNode( + nodeData: RedisClusterMasterNode | RedisClusterReplicaNode, + readonly: boolean, + clientsInUse: Set, + promises: Array> + ): ClusterNode { const address = `${nodeData.host}:${nodeData.port}`; clientsInUse.add(address); @@ -175,11 +192,11 @@ export default class RedisClusterSlots { + getSlotMaster(slot: number): ClusterNode { return this.#slots[slot].master; } - *#slotClientIterator(slotNumber: number): IterableIterator> { + *#slotClientIterator(slotNumber: number): IterableIterator> { const slot = this.#slots[slotNumber]; yield slot.master.client; @@ -188,7 +205,7 @@ export default class RedisClusterSlots { + #getSlotClient(slotNumber: number): RedisClientType { const slot = this.#slots[slotNumber]; if (!slot.clientIterator) { slot.clientIterator = this.#slotClientIterator(slotNumber); @@ -203,9 +220,9 @@ export default class RedisClusterSlots>; + #randomClientIterator?: IterableIterator>; - #getRandomClient(): RedisClientType { + #getRandomClient(): RedisClientType { if (!this.#nodeByAddress.size) { throw new Error('Cluster is not connected'); } @@ -223,7 +240,7 @@ export default class RedisClusterSlots { + getClient(firstKey?: RedisCommandArgument, isReadonly?: boolean): RedisClientType { if (!firstKey) { return this.#getRandomClient(); } @@ -236,7 +253,7 @@ export default class RedisClusterSlots> { + getMasters(): Array> { const masters = []; for (const node of this.#nodeByAddress.values()) { if (node.client.options?.readonly) continue; @@ -247,7 +264,7 @@ export default class RedisClusterSlots | undefined { + getNodeByAddress(address: string): ClusterNode | undefined { const mappedAddress = this.#getNodeAddress(address); return this.#nodeByAddress.get( mappedAddress ? `${mappedAddress.host}:${mappedAddress.port}` : address @@ -262,7 +279,7 @@ export default class RedisClusterSlots client.disconnect()); } - async #destroy(fn: (client: RedisClientType) => Promise): Promise { + async #destroy(fn: (client: RedisClientType) => Promise): Promise { const promises = []; for (const { client } of this.#nodeByAddress.values()) { promises.push(fn(client)); diff --git a/packages/client/lib/cluster/commands.ts b/packages/client/lib/cluster/commands.ts index 91e589e1275..a8527cf235f 100644 --- a/packages/client/lib/cluster/commands.ts +++ b/packages/client/lib/cluster/commands.ts @@ -18,12 +18,16 @@ import * as DECR from '../commands/DECR'; import * as DECRBY from '../commands/DECRBY'; import * as DEL from '../commands/DEL'; import * as DUMP from '../commands/DUMP'; +import * as EVAL_RO from '../commands/EVAL_RO'; import * as EVAL from '../commands/EVAL'; +import * as EVALSHA_RO from '../commands/EVALSHA_RO'; import * as EVALSHA from '../commands/EVALSHA'; import * as EXISTS from '../commands/EXISTS'; import * as EXPIRE from '../commands/EXPIRE'; import * as EXPIREAT from '../commands/EXPIREAT'; import * as EXPIRETIME from '../commands/EXPIRETIME'; +import * as FCALL_RO from '../commands/FCALL_RO'; +import * as FCALL from '../commands/FCALL'; import * as GEOADD from '../commands/GEOADD'; import * as GEODIST from '../commands/GEODIST'; import * as GEOHASH from '../commands/GEOHASH'; @@ -230,10 +234,14 @@ export default { del: DEL, DUMP, dump: DUMP, + EVAL_RO, + evalRo: EVAL_RO, EVAL, eval: EVAL, EVALSHA, evalSha: EVALSHA, + EVALSHA_RO, + evalShaRo: EVALSHA_RO, EXISTS, exists: EXISTS, EXPIRE, @@ -242,6 +250,10 @@ export default { expireAt: EXPIREAT, EXPIRETIME, expireTime: EXPIRETIME, + FCALL_RO, + fCallRo: FCALL_RO, + FCALL, + fCall: FCALL, GEOADD, geoAdd: GEOADD, GEODIST, diff --git a/packages/client/lib/cluster/index.ts b/packages/client/lib/cluster/index.ts index 404d13b0512..a4e16f36719 100644 --- a/packages/client/lib/cluster/index.ts +++ b/packages/client/lib/cluster/index.ts @@ -1,18 +1,22 @@ import COMMANDS from './commands'; -import { RedisCommand, RedisCommandArgument, RedisCommandArguments, RedisCommandRawReply, RedisCommandReply, RedisModules, RedisPlugins, RedisScript, RedisScripts } from '../commands'; -import { ClientCommandOptions, RedisClientCommandSignature, RedisClientOptions, RedisClientType, WithModules, WithScripts } from '../client'; +import { RedisCommand, RedisCommandArgument, RedisCommandArguments, RedisCommandRawReply, RedisCommandReply, RedisFunctions, RedisModules, RedisExtensions, RedisScript, RedisScripts, RedisCommandSignature, RedisFunction } from '../commands'; +import { ClientCommandOptions, RedisClientOptions, RedisClientType, WithFunctions, WithModules, WithScripts } from '../client'; import RedisClusterSlots, { ClusterNode, NodeAddressMap } from './cluster-slots'; -import { extendWithModulesAndScripts, transformCommandArguments, transformCommandReply, extendWithCommands } from '../commander'; +import { attachExtensions, transformCommandReply, attachCommands, transformCommandArguments } from '../commander'; import { EventEmitter } from 'events'; -import RedisClusterMultiCommand, { RedisClusterMultiCommandType } from './multi-command'; +import RedisClusterMultiCommand, { InstantiableRedisClusterMultiCommandType, RedisClusterMultiCommandType } from './multi-command'; import { RedisMultiQueuedCommand } from '../multi-command'; -export type RedisClusterClientOptions = Omit; +export type RedisClusterClientOptions = Omit< + RedisClientOptions, + 'modules' | 'functions' | 'scripts' | 'database' +>; export interface RedisClusterOptions< M extends RedisModules = Record, + F extends RedisFunctions = Record, S extends RedisScripts = Record -> extends RedisPlugins { +> extends RedisExtensions { rootNodes: Array; defaults?: Partial; useReplicas?: boolean; @@ -21,16 +25,25 @@ export interface RedisClusterOptions< } type WithCommands = { - [P in keyof typeof COMMANDS]: RedisClientCommandSignature<(typeof COMMANDS)[P]>; + [P in keyof typeof COMMANDS]: RedisCommandSignature<(typeof COMMANDS)[P]>; }; export type RedisClusterType< M extends RedisModules = Record, + F extends RedisFunctions = Record, S extends RedisScripts = Record -> = RedisCluster & WithCommands & WithModules & WithScripts; +> = RedisCluster & WithCommands & WithModules & WithFunctions & WithScripts; -export default class RedisCluster extends EventEmitter { - static extractFirstKey(command: RedisCommand, originalArgs: Array, redisArgs: RedisCommandArguments): RedisCommandArgument | undefined { +export default class RedisCluster< + M extends RedisModules, + F extends RedisFunctions, + S extends RedisScripts +> extends EventEmitter { + static extractFirstKey( + command: RedisCommand, + originalArgs: Array, + redisArgs: RedisCommandArguments + ): RedisCommandArgument | undefined { if (command.FIRST_KEY_INDEX === undefined) { return undefined; } else if (typeof command.FIRST_KEY_INDEX === 'number') { @@ -40,21 +53,27 @@ export default class RedisCluster(options?: RedisClusterOptions): RedisClusterType { - return new (extendWithModulesAndScripts({ + static create< + M extends RedisModules, + F extends RedisFunctions, + S extends RedisScripts + >(options?: RedisClusterOptions): RedisClusterType { + return new (attachExtensions({ BaseClass: RedisCluster, + modulesExecutor: RedisCluster.prototype.commandsExecutor, modules: options?.modules, - modulesCommandsExecutor: RedisCluster.prototype.commandsExecutor, - scripts: options?.scripts, - scriptsExecutor: RedisCluster.prototype.scriptsExecutor + functionsExecutor: RedisCluster.prototype.functionsExecutor, + functions: options?.functions, + scriptsExecutor: RedisCluster.prototype.scriptsExecutor, + scripts: options?.scripts }))(options); } - readonly #options: RedisClusterOptions; - readonly #slots: RedisClusterSlots; - readonly #Multi: new (...args: ConstructorParameters) => RedisClusterMultiCommandType; + readonly #options: RedisClusterOptions; + readonly #slots: RedisClusterSlots; + readonly #Multi: InstantiableRedisClusterMultiCommandType; - constructor(options: RedisClusterOptions) { + constructor(options: RedisClusterOptions) { super(); this.#options = options; @@ -62,7 +81,7 @@ export default class RedisCluster>): RedisClusterType { + duplicate(overrides?: Partial>): RedisClusterType { return new (Object.getPrototypeOf(this).constructor)({ ...this.#options, ...overrides @@ -73,9 +92,11 @@ export default class RedisCluster): Promise> { - const { args: redisArgs, options } = transformCommandArguments(command, args); - + async commandsExecutor( + command: C, + args: Array + ): Promise> { + const { args: redisArgs, options } = transformCommandArguments(command, args); return transformCommandReply( command, await this.sendCommand( @@ -101,9 +122,38 @@ export default class RedisCluster): Promise> { - const { args: redisArgs, options } = transformCommandArguments(script, args); + async functionsExecutor( + fn: F, + args: Array + ): Promise> { + const { args: redisArgs, options } = transformCommandArguments(fn, args); + return transformCommandReply( + fn, + await this.executeFunction( + fn, + args, + redisArgs, + options + ), + redisArgs.preserve + ); + } + + async executeFunction( + fn: RedisFunction, + originalArgs: Array, + redisArgs: RedisCommandArguments, + options?: ClientCommandOptions + ): Promise { + return this.#execute( + RedisCluster.extractFirstKey(fn, originalArgs, redisArgs), + fn.IS_READ_ONLY, + client => client.executeFunction(fn, redisArgs, options) + ); + } + async scriptsExecutor(script: S, args: Array): Promise> { + const { args: redisArgs, options } = transformCommandArguments(script, args); return transformCommandReply( script, await this.executeScript( @@ -121,7 +171,7 @@ export default class RedisCluster, redisArgs: RedisCommandArguments, options?: ClientCommandOptions - ): Promise> { + ): Promise { return this.#execute( RedisCluster.extractFirstKey(script, originalArgs, redisArgs), script.IS_READ_ONLY, @@ -132,7 +182,7 @@ export default class RedisCluster( firstKey: RedisCommandArgument | undefined, isReadonly: boolean | undefined, - executor: (client: RedisClientType) => Promise + executor: (client: RedisClientType) => Promise ): Promise { const maxCommandRedirections = this.#options.maxCommandRedirections ?? 16; let client = this.#slots.getClient(firstKey, isReadonly); @@ -171,7 +221,7 @@ export default class RedisCluster { + multi(routing?: RedisCommandArgument): RedisClusterMultiCommandType { return new this.#Multi( (commands: Array, firstKey?: RedisCommandArgument, chainId?: symbol) => { return this.#execute( @@ -184,11 +234,11 @@ export default class RedisCluster> { + getMasters(): Array> { return this.#slots.getMasters(); } - getSlotMaster(slot: number): ClusterNode { + getSlotMaster(slot: number): ClusterNode { return this.#slots.getSlotMaster(slot); } @@ -201,7 +251,7 @@ export default class RedisCluster = - (...args: Parameters) => RedisClusterMultiCommandType; - -type WithCommands = { - [P in keyof typeof COMMANDS]: RedisClusterMultiCommandSignature<(typeof COMMANDS)[P], M, S> +type RedisClusterMultiCommandSignature< + C extends RedisCommand, + M extends RedisModules, + F extends RedisFunctions, + S extends RedisScripts +> = (...args: Parameters) => RedisClusterMultiCommandType; + +type WithCommands< + M extends RedisModules, + F extends RedisFunctions, + S extends RedisScripts +> = { + [P in keyof typeof COMMANDS]: RedisClusterMultiCommandSignature<(typeof COMMANDS)[P], M, F, S>; }; -type WithModules = { +type WithModules< + M extends RedisModules, + F extends RedisFunctions, + S extends RedisScripts +> = { [P in keyof M as ExcludeMappedString

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

]: { + [FF in keyof F[P] as ExcludeMappedString]: RedisClusterMultiCommandSignature; }; }; -type WithScripts = { - [P in keyof S as ExcludeMappedString

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

]: RedisClusterMultiCommandSignature; }; -export type RedisClusterMultiCommandType = - RedisClusterMultiCommand & WithCommands & WithModules & WithScripts; +export type RedisClusterMultiCommandType< + M extends RedisModules, + F extends RedisFunctions, + S extends RedisScripts +> = RedisClusterMultiCommand & WithCommands & WithModules & WithFunctions & WithScripts; + +export type InstantiableRedisClusterMultiCommandType< + M extends RedisModules, + F extends RedisFunctions, + S extends RedisScripts +> = new (...args: ConstructorParameters) => RedisClusterMultiCommandType; export type RedisClusterMultiExecutor = (queue: Array, firstKey?: RedisCommandArgument, chainId?: symbol) => Promise>; @@ -32,15 +66,19 @@ export default class RedisClusterMultiCommand { readonly #executor: RedisClusterMultiExecutor; #firstKey: RedisCommandArgument | undefined; - static extend( - plugins?: RedisPlugins - ): new (...args: ConstructorParameters) => RedisClusterMultiCommandType { - return extendWithModulesAndScripts({ + static extend< + M extends RedisModules, + F extends RedisFunctions, + S extends RedisScripts + >(extensions?: RedisExtensions): InstantiableRedisClusterMultiCommandType { + return attachExtensions({ BaseClass: RedisClusterMultiCommand, - modules: plugins?.modules, - modulesCommandsExecutor: RedisClusterMultiCommand.prototype.commandsExecutor, - scripts: plugins?.scripts, - scriptsExecutor: RedisClusterMultiCommand.prototype.scriptsExecutor + modulesExecutor: RedisClusterMultiCommand.prototype.commandsExecutor, + modules: extensions?.modules, + functionsExecutor: RedisClusterMultiCommand.prototype.functionsExecutor, + functions: extensions?.functions, + scriptsExecutor: RedisClusterMultiCommand.prototype.scriptsExecutor, + scripts: extensions?.scripts }); } @@ -51,15 +89,8 @@ export default class RedisClusterMultiCommand { commandsExecutor(command: RedisCommand, args: Array): this { const transformedArguments = command.transformArguments(...args); - if (!this.#firstKey) { - this.#firstKey = RedisCluster.extractFirstKey(command, args, transformedArguments); - } - - return this.addCommand( - undefined, - transformedArguments, - command.transformReply - ); + this.#firstKey ??= RedisCluster.extractFirstKey(command, args, transformedArguments); + return this.addCommand(undefined, transformedArguments, command.transformReply); } addCommand( @@ -67,21 +98,21 @@ export default class RedisClusterMultiCommand { args: RedisCommandArguments, transformReply?: RedisCommand['transformReply'] ): this { - if (!this.#firstKey) { - this.#firstKey = firstKey; - } - + this.#firstKey ??= firstKey; this.#multi.addCommand(args, transformReply); return this; } + functionsExecutor(fn: RedisFunction, args: Array): this { + const transformedArguments = this.#multi.addFunction(fn, args); + this.#firstKey ??= RedisCluster.extractFirstKey(fn, args, transformedArguments); + return this; + } + scriptsExecutor(script: RedisScript, args: Array): this { const transformedArguments = this.#multi.addScript(script, args); - if (!this.#firstKey) { - this.#firstKey = RedisCluster.extractFirstKey(script, args, transformedArguments); - } - - return this.addCommand(undefined, transformedArguments); + this.#firstKey ??= RedisCluster.extractFirstKey(script, args, transformedArguments); + return this; } async exec(execAsPipeline = false): Promise> { @@ -106,7 +137,7 @@ export default class RedisClusterMultiCommand { } } -extendWithCommands({ +attachCommands({ BaseClass: RedisClusterMultiCommand, commands: COMMANDS, executor: RedisClusterMultiCommand.prototype.commandsExecutor diff --git a/packages/client/lib/commander.ts b/packages/client/lib/commander.ts index d70435e14ad..f8cb74c3135 100644 --- a/packages/client/lib/commander.ts +++ b/packages/client/lib/commander.ts @@ -1,16 +1,23 @@ import { CommandOptions, isCommandOptions } from './command-options'; -import { RedisCommand, RedisCommandArguments, RedisCommandRawReply, RedisCommandReply, RedisCommands, RedisModules, RedisScript, RedisScripts } from './commands'; +import { RedisCommand, RedisCommandArguments, RedisCommandReply, RedisFunction, RedisFunctions, RedisModules, RedisScript, RedisScripts } from './commands'; -type Instantiable = new(...args: Array) => T; +type Instantiable = new (...args: Array) => T; -interface ExtendWithCommandsConfig { - BaseClass: T; - commands: RedisCommands; - executor(command: RedisCommand, args: Array): unknown; +type CommandsExecutor = + (command: C, args: Array) => unknown; + +interface AttachCommandsConfig { + BaseClass: Instantiable; + commands: Record; + executor: CommandsExecutor; } -export function extendWithCommands({ BaseClass, commands, executor }: ExtendWithCommandsConfig): void { +export function attachCommands({ + BaseClass, + commands, + executor +}: AttachCommandsConfig): void { for (const [name, command] of Object.entries(commands)) { BaseClass.prototype[name] = function (...args: Array): unknown { return executor.call(this, command, args); @@ -18,56 +25,82 @@ export function extendWithCommands({ BaseClass, commands } } -interface ExtendWithModulesAndScriptsConfig { +interface AttachExtensionsConfig { BaseClass: T; + modulesExecutor: CommandsExecutor; modules?: RedisModules; - modulesCommandsExecutor(this: InstanceType, command: RedisCommand, args: Array): unknown; + functionsExecutor: CommandsExecutor; + functions?: RedisFunctions; + scriptsExecutor: CommandsExecutor; scripts?: RedisScripts; - scriptsExecutor(this: InstanceType, script: RedisScript, args: Array): unknown; } -export function extendWithModulesAndScripts(config: ExtendWithModulesAndScriptsConfig): T { - let Commander: T | undefined; +export function attachExtensions(config: AttachExtensionsConfig): any { + let Commander; if (config.modules) { - Commander = class extends config.BaseClass { - constructor(...args: Array) { - super(...args); + Commander = attachWithNamespaces({ + BaseClass: config.BaseClass, + namespaces: config.modules, + executor: config.modulesExecutor + }); + } - for (const module of Object.keys(config.modules!)) { - this[module] = new this[module](this); - } - } - }; + if (config.functions) { + Commander = attachWithNamespaces({ + BaseClass: Commander ?? config.BaseClass, + namespaces: config.functions, + executor: config.functionsExecutor + }); + } - for (const [moduleName, module] of Object.entries(config.modules)) { - Commander.prototype[moduleName] = class { - readonly self: T; + if (config.scripts) { + Commander ??= class extends config.BaseClass {}; + attachCommands({ + BaseClass: Commander, + commands: config.scripts, + executor: config.scriptsExecutor + }); + } - constructor(self: InstanceType) { - this.self = self; - } - }; + return Commander ?? config.BaseClass; +} + +interface AttachWithNamespacesConfig { + BaseClass: Instantiable; + namespaces: Record>; + executor: CommandsExecutor; +} - for (const [commandName, command] of Object.entries(module)) { - Commander.prototype[moduleName].prototype[commandName] = function (...args: Array): unknown { - return config.modulesCommandsExecutor.call(this.self, command, args); - }; +function attachWithNamespaces({ + BaseClass, + namespaces, + executor +}: AttachWithNamespacesConfig): any { + const Commander = class extends BaseClass { + constructor(...args: Array) { + super(...args); + + for (const namespace of Object.keys(namespaces)) { + this[namespace] = Object.create(this[namespace], { + self: { + value: this + } + }); } } - } - - if (config.scripts) { - Commander ??= class extends config.BaseClass {}; + }; - for (const [name, script] of Object.entries(config.scripts)) { - Commander.prototype[name] = function (...args: Array): unknown { - return config.scriptsExecutor.call(this, script, args); + for (const [namespace, commands] of Object.entries(namespaces)) { + Commander.prototype[namespace] = {}; + for (const [name, command] of Object.entries(commands)) { + Commander.prototype[namespace][name] = function (...args: Array): unknown { + return executor.call(this.self, command, args); }; } } - return (Commander ?? config.BaseClass) as any; + return Commander; } export function transformCommandArguments( @@ -93,14 +126,29 @@ export function transformLegacyCommandArguments(args: Array): Array { return args.flat().map(x => x?.toString?.()); } -export function transformCommandReply( - command: RedisCommand, - rawReply: RedisCommandRawReply, +export function transformCommandReply( + command: C, + rawReply: unknown, preserved: unknown -): RedisCommandReply { +): RedisCommandReply { if (!command.transformReply) { - return rawReply; + return rawReply as RedisCommandReply; } return command.transformReply(rawReply, preserved); } + +export function fCallArguments(fn: RedisFunction, args: RedisCommandArguments): RedisCommandArguments { + const actualArgs: RedisCommandArguments = [ + fn.IS_READ_ONLY ? 'FCALL_RO' : 'FCALL', + fn.NAME + ]; + + if (fn.NUMBER_OF_KEYS !== undefined) { + actualArgs.push(fn.NUMBER_OF_KEYS.toString()); + } + + actualArgs.push(...args); + + return actualArgs; +} diff --git a/packages/client/lib/commands/EVAL.ts b/packages/client/lib/commands/EVAL.ts index f269815b7ec..a82f8bf0aad 100644 --- a/packages/client/lib/commands/EVAL.ts +++ b/packages/client/lib/commands/EVAL.ts @@ -1,4 +1,6 @@ -import { EvalOptions, pushEvalArguments } from './generic-transformers'; +import { evalFirstKeyIndex, EvalOptions, pushEvalArguments } from './generic-transformers'; + +export const FIRST_KEY_INDEX = evalFirstKeyIndex; export function transformArguments(script: string, options?: EvalOptions): Array { return pushEvalArguments(['EVAL', script], options); diff --git a/packages/client/lib/commands/EVALSHA.ts b/packages/client/lib/commands/EVALSHA.ts index 105784cf5f8..24f7060a052 100644 --- a/packages/client/lib/commands/EVALSHA.ts +++ b/packages/client/lib/commands/EVALSHA.ts @@ -1,4 +1,6 @@ -import { EvalOptions, pushEvalArguments } from './generic-transformers'; +import { evalFirstKeyIndex, EvalOptions, pushEvalArguments } from './generic-transformers'; + +export const FIRST_KEY_INDEX = evalFirstKeyIndex; export function transformArguments(sha1: string, options?: EvalOptions): Array { return pushEvalArguments(['EVALSHA', sha1], options); diff --git a/packages/client/lib/commands/EVALSHA_RO.spec.ts b/packages/client/lib/commands/EVALSHA_RO.spec.ts new file mode 100644 index 00000000000..6711f24fd80 --- /dev/null +++ b/packages/client/lib/commands/EVALSHA_RO.spec.ts @@ -0,0 +1,17 @@ +import { strict as assert } from 'assert'; +import testUtils from '../test-utils'; +import { transformArguments } from './EVALSHA_RO'; + +describe('EVALSHA_RO', () => { + testUtils.isVersionGreaterThanHook([7, 0]); + + it('transformArguments', () => { + assert.deepEqual( + transformArguments('sha1', { + keys: ['key'], + arguments: ['argument'] + }), + ['EVALSHA_RO', 'sha1', '1', 'key', 'argument'] + ); + }); +}); diff --git a/packages/client/lib/commands/EVALSHA_RO.ts b/packages/client/lib/commands/EVALSHA_RO.ts new file mode 100644 index 00000000000..c3fcc3dc9cf --- /dev/null +++ b/packages/client/lib/commands/EVALSHA_RO.ts @@ -0,0 +1,9 @@ +import { evalFirstKeyIndex, EvalOptions, pushEvalArguments } from './generic-transformers'; + +export const FIRST_KEY_INDEX = evalFirstKeyIndex; + +export const IS_READ_ONLY = true; + +export function transformArguments(sha1: string, options?: EvalOptions): Array { + return pushEvalArguments(['EVALSHA_RO', sha1], options); +} diff --git a/packages/client/lib/commands/EVAL_RO.spec.ts b/packages/client/lib/commands/EVAL_RO.spec.ts new file mode 100644 index 00000000000..e39fe82dd33 --- /dev/null +++ b/packages/client/lib/commands/EVAL_RO.spec.ts @@ -0,0 +1,31 @@ +import { strict as assert } from 'assert'; +import testUtils, { GLOBAL } from '../test-utils'; +import { transformArguments } from './EVAL_RO'; + +describe('EVAL_RO', () => { + testUtils.isVersionGreaterThanHook([7, 0]); + + it('transformArguments', () => { + assert.deepEqual( + transformArguments('return KEYS[1] + ARGV[1]', { + keys: ['key'], + arguments: ['argument'] + }), + ['EVAL_RO', 'return KEYS[1] + ARGV[1]', '1', 'key', 'argument'] + ); + }); + + testUtils.testWithClient('client.evalRo', async client => { + assert.equal( + await client.evalRo('return 1'), + 1 + ); + }, GLOBAL.SERVERS.OPEN); + + testUtils.testWithCluster('cluster.evalRo', async cluster => { + assert.equal( + await cluster.evalRo('return 1'), + 1 + ); + }, GLOBAL.CLUSTERS.OPEN); +}); diff --git a/packages/client/lib/commands/EVAL_RO.ts b/packages/client/lib/commands/EVAL_RO.ts new file mode 100644 index 00000000000..590c3af04f3 --- /dev/null +++ b/packages/client/lib/commands/EVAL_RO.ts @@ -0,0 +1,9 @@ +import { evalFirstKeyIndex, EvalOptions, pushEvalArguments } from './generic-transformers'; + +export const FIRST_KEY_INDEX = evalFirstKeyIndex; + +export const IS_READ_ONLY = true; + +export function transformArguments(script: string, options?: EvalOptions): Array { + return pushEvalArguments(['EVAL_RO', script], options); +} diff --git a/packages/client/lib/commands/FCALL.spec.ts b/packages/client/lib/commands/FCALL.spec.ts new file mode 100644 index 00000000000..25fbd51399f --- /dev/null +++ b/packages/client/lib/commands/FCALL.spec.ts @@ -0,0 +1,29 @@ +import { strict as assert } from 'assert'; +import testUtils, { GLOBAL } from '../test-utils'; +import { MATH_FUNCTION, loadMathFunction } from '../client/index.spec'; +import { transformArguments } from './FCALL'; + +describe('FCALL', () => { + testUtils.isVersionGreaterThanHook([7, 0]); + + it('transformArguments', () => { + assert.deepEqual( + transformArguments('function', { + keys: ['key'], + arguments: ['argument'] + }), + ['FCALL', 'function', '1', 'key', 'argument'] + ); + }); + + testUtils.testWithClient('client.fCall', async client => { + await loadMathFunction(client); + + assert.equal( + await client.fCall(MATH_FUNCTION.library.square.NAME, { + arguments: ['2'] + }), + 4 + ); + }, GLOBAL.SERVERS.OPEN); +}); diff --git a/packages/client/lib/commands/FCALL.ts b/packages/client/lib/commands/FCALL.ts new file mode 100644 index 00000000000..a4cadedb6f9 --- /dev/null +++ b/packages/client/lib/commands/FCALL.ts @@ -0,0 +1,7 @@ +import { evalFirstKeyIndex, EvalOptions, pushEvalArguments } from './generic-transformers'; + +export const FIRST_KEY_INDEX = evalFirstKeyIndex; + +export function transformArguments(fn: string, options?: EvalOptions): Array { + return pushEvalArguments(['FCALL', fn], options); +} diff --git a/packages/client/lib/commands/FCALL_RO.spec.ts b/packages/client/lib/commands/FCALL_RO.spec.ts new file mode 100644 index 00000000000..44332b9426c --- /dev/null +++ b/packages/client/lib/commands/FCALL_RO.spec.ts @@ -0,0 +1,29 @@ +import { strict as assert } from 'assert'; +import testUtils, { GLOBAL } from '../test-utils'; +import { MATH_FUNCTION, loadMathFunction } from '../client/index.spec'; +import { transformArguments } from './FCALL_RO'; + +describe('FCALL_RO', () => { + testUtils.isVersionGreaterThanHook([7, 0]); + + it('transformArguments', () => { + assert.deepEqual( + transformArguments('function', { + keys: ['key'], + arguments: ['argument'] + }), + ['FCALL_RO', 'function', '1', 'key', 'argument'] + ); + }); + + testUtils.testWithClient('client.fCallRo', async client => { + await loadMathFunction(client); + + assert.equal( + await client.fCallRo(MATH_FUNCTION.library.square.NAME, { + arguments: ['2'] + }), + 4 + ); + }, GLOBAL.SERVERS.OPEN); +}); diff --git a/packages/client/lib/commands/FCALL_RO.ts b/packages/client/lib/commands/FCALL_RO.ts new file mode 100644 index 00000000000..66b79aa8833 --- /dev/null +++ b/packages/client/lib/commands/FCALL_RO.ts @@ -0,0 +1,9 @@ +import { evalFirstKeyIndex, EvalOptions, pushEvalArguments } from './generic-transformers'; + +export const FIRST_KEY_INDEX = evalFirstKeyIndex; + +export const IS_READ_ONLY = true; + +export function transformArguments(fn: string, options?: EvalOptions): Array { + return pushEvalArguments(['FCALL_RO', fn], options); +} diff --git a/packages/client/lib/commands/FUNCTION_DELETE.spec.ts b/packages/client/lib/commands/FUNCTION_DELETE.spec.ts new file mode 100644 index 00000000000..fd7dca2b072 --- /dev/null +++ b/packages/client/lib/commands/FUNCTION_DELETE.spec.ts @@ -0,0 +1,24 @@ +import { strict as assert } from 'assert'; +import { MATH_FUNCTION, loadMathFunction } from '../client/index.spec'; +import testUtils, { GLOBAL } from '../test-utils'; +import { transformArguments } from './FUNCTION_DELETE'; + +describe('FUNCTION DELETE', () => { + testUtils.isVersionGreaterThanHook([7, 0]); + + it('transformArguments', () => { + assert.deepEqual( + transformArguments('library'), + ['FUNCTION', 'DELETE', 'library'] + ); + }); + + testUtils.testWithClient('client.functionDelete', async client => { + await loadMathFunction(client); + + assert.equal( + await client.functionDelete(MATH_FUNCTION.name), + 'OK' + ); + }, GLOBAL.SERVERS.OPEN); +}); diff --git a/packages/client/lib/commands/FUNCTION_DELETE.ts b/packages/client/lib/commands/FUNCTION_DELETE.ts new file mode 100644 index 00000000000..4aa6be40e12 --- /dev/null +++ b/packages/client/lib/commands/FUNCTION_DELETE.ts @@ -0,0 +1,7 @@ +import { RedisCommandArguments } from '.'; + +export function transformArguments(library: string): RedisCommandArguments { + return ['FUNCTION', 'DELETE', library]; +} + +export declare function transformReply(): 'OK'; diff --git a/packages/client/lib/commands/FUNCTION_DUMP.spec.ts b/packages/client/lib/commands/FUNCTION_DUMP.spec.ts new file mode 100644 index 00000000000..e3befa3dc54 --- /dev/null +++ b/packages/client/lib/commands/FUNCTION_DUMP.spec.ts @@ -0,0 +1,21 @@ +import { strict as assert } from 'assert'; +import testUtils, { GLOBAL } from '../test-utils'; +import { transformArguments } from './FUNCTION_DUMP'; + +describe('FUNCTION DUMP', () => { + testUtils.isVersionGreaterThanHook([7, 0]); + + it('transformArguments', () => { + assert.deepEqual( + transformArguments(), + ['FUNCTION', 'DUMP'] + ); + }); + + testUtils.testWithClient('client.functionDump', async client => { + assert.equal( + typeof await client.functionDump(), + 'string' + ); + }, GLOBAL.SERVERS.OPEN); +}); diff --git a/packages/client/lib/commands/FUNCTION_DUMP.ts b/packages/client/lib/commands/FUNCTION_DUMP.ts new file mode 100644 index 00000000000..f608e078c27 --- /dev/null +++ b/packages/client/lib/commands/FUNCTION_DUMP.ts @@ -0,0 +1,7 @@ +import { RedisCommandArgument, RedisCommandArguments } from '.'; + +export function transformArguments(): RedisCommandArguments { + return ['FUNCTION', 'DUMP']; +} + +export declare function transformReply(): RedisCommandArgument; diff --git a/packages/client/lib/commands/FUNCTION_FLUSH.spec.ts b/packages/client/lib/commands/FUNCTION_FLUSH.spec.ts new file mode 100644 index 00000000000..8447216baa8 --- /dev/null +++ b/packages/client/lib/commands/FUNCTION_FLUSH.spec.ts @@ -0,0 +1,30 @@ +import { strict as assert } from 'assert'; +import testUtils, { GLOBAL } from '../test-utils'; +import { transformArguments } from './FUNCTION_FLUSH'; + +describe('FUNCTION FLUSH', () => { + testUtils.isVersionGreaterThanHook([7, 0]); + + describe('transformArguments', () => { + it('simple', () => { + assert.deepEqual( + transformArguments(), + ['FUNCTION', 'FLUSH'] + ); + }); + + it('with mode', () => { + assert.deepEqual( + transformArguments('SYNC'), + ['FUNCTION', 'FLUSH', 'SYNC'] + ); + }); + }); + + testUtils.testWithClient('client.functionFlush', async client => { + assert.equal( + await client.functionFlush(), + 'OK' + ); + }, GLOBAL.SERVERS.OPEN); +}); diff --git a/packages/client/lib/commands/FUNCTION_FLUSH.ts b/packages/client/lib/commands/FUNCTION_FLUSH.ts new file mode 100644 index 00000000000..143282de97f --- /dev/null +++ b/packages/client/lib/commands/FUNCTION_FLUSH.ts @@ -0,0 +1,13 @@ +import { RedisCommandArguments } from '.'; + +export function transformArguments(mode?: 'ASYNC' | 'SYNC'): RedisCommandArguments { + const args = ['FUNCTION', 'FLUSH']; + + if (mode) { + args.push(mode); + } + + return args; +} + +export declare function transformReply(): 'OK'; diff --git a/packages/client/lib/commands/FUNCTION_KILL.spec.ts b/packages/client/lib/commands/FUNCTION_KILL.spec.ts new file mode 100644 index 00000000000..cf489a80229 --- /dev/null +++ b/packages/client/lib/commands/FUNCTION_KILL.spec.ts @@ -0,0 +1,14 @@ +import { strict as assert } from 'assert'; +import testUtils from '../test-utils'; +import { transformArguments } from './FUNCTION_KILL'; + +describe('FUNCTION KILL', () => { + testUtils.isVersionGreaterThanHook([7, 0]); + + it('transformArguments', () => { + assert.deepEqual( + transformArguments(), + ['FUNCTION', 'KILL'] + ); + }); +}); diff --git a/packages/client/lib/commands/FUNCTION_KILL.ts b/packages/client/lib/commands/FUNCTION_KILL.ts new file mode 100644 index 00000000000..517272e8376 --- /dev/null +++ b/packages/client/lib/commands/FUNCTION_KILL.ts @@ -0,0 +1,7 @@ +import { RedisCommandArguments } from '.'; + +export function transformArguments(): RedisCommandArguments { + return ['FUNCTION', 'KILL']; +} + +export declare function transformReply(): 'OK'; diff --git a/packages/client/lib/commands/FUNCTION_LIST.spec.ts b/packages/client/lib/commands/FUNCTION_LIST.spec.ts new file mode 100644 index 00000000000..41de2f40f40 --- /dev/null +++ b/packages/client/lib/commands/FUNCTION_LIST.spec.ts @@ -0,0 +1,41 @@ +import { strict as assert } from 'assert'; +import testUtils, { GLOBAL } from '../test-utils'; +import { MATH_FUNCTION, loadMathFunction } from '../client/index.spec'; +import { transformArguments } from './FUNCTION_LIST'; + +describe('FUNCTION LIST', () => { + testUtils.isVersionGreaterThanHook([7, 0]); + + describe('transformArguments', () => { + it('simple', () => { + assert.deepEqual( + transformArguments(), + ['FUNCTION', 'LIST'] + ); + }); + + it('with pattern', () => { + assert.deepEqual( + transformArguments('patter*'), + ['FUNCTION', 'LIST', 'patter*'] + ); + }); + }); + + testUtils.testWithClient('client.functionList', async client => { + await loadMathFunction(client); + + assert.deepEqual( + await client.functionList(), + [{ + libraryName: MATH_FUNCTION.name, + engine: MATH_FUNCTION.engine, + functions: [{ + name: MATH_FUNCTION.library.square.NAME, + description: null, + flags: ['no-writes'] + }] + }] + ); + }, GLOBAL.SERVERS.OPEN); +}); diff --git a/packages/client/lib/commands/FUNCTION_LIST.ts b/packages/client/lib/commands/FUNCTION_LIST.ts new file mode 100644 index 00000000000..d6a39dc726d --- /dev/null +++ b/packages/client/lib/commands/FUNCTION_LIST.ts @@ -0,0 +1,16 @@ +import { RedisCommandArguments } from '.'; +import { FunctionListItemReply, FunctionListRawItemReply, transformFunctionListItemReply } from './generic-transformers'; + +export function transformArguments(pattern?: string): RedisCommandArguments { + const args = ['FUNCTION', 'LIST']; + + if (pattern) { + args.push(pattern); + } + + return args; +} + +export function transformReply(reply: Array): Array { + return reply.map(transformFunctionListItemReply); +} diff --git a/packages/client/lib/commands/FUNCTION_LIST_WITHCODE.spec.ts b/packages/client/lib/commands/FUNCTION_LIST_WITHCODE.spec.ts new file mode 100644 index 00000000000..33c2ca6d915 --- /dev/null +++ b/packages/client/lib/commands/FUNCTION_LIST_WITHCODE.spec.ts @@ -0,0 +1,42 @@ +import { strict as assert } from 'assert'; +import testUtils, { GLOBAL } from '../test-utils'; +import { MATH_FUNCTION, loadMathFunction } from '../client/index.spec'; +import { transformArguments } from './FUNCTION_LIST_WITHCODE'; + +describe('FUNCTION LIST WITHCODE', () => { + testUtils.isVersionGreaterThanHook([7, 0]); + + describe('transformArguments', () => { + it('simple', () => { + assert.deepEqual( + transformArguments(), + ['FUNCTION', 'LIST', 'WITHCODE'] + ); + }); + + it('with pattern', () => { + assert.deepEqual( + transformArguments('patter*'), + ['FUNCTION', 'LIST', 'patter*', 'WITHCODE'] + ); + }); + }); + + testUtils.testWithClient('client.functionListWithCode', async client => { + await loadMathFunction(client); + + assert.deepEqual( + await client.functionListWithCode(), + [{ + libraryName: MATH_FUNCTION.name, + engine: MATH_FUNCTION.engine, + functions: [{ + name: MATH_FUNCTION.library.square.NAME, + description: null, + flags: ['no-writes'] + }], + libraryCode: MATH_FUNCTION.code + }] + ); + }, GLOBAL.SERVERS.OPEN); +}); diff --git a/packages/client/lib/commands/FUNCTION_LIST_WITHCODE.ts b/packages/client/lib/commands/FUNCTION_LIST_WITHCODE.ts new file mode 100644 index 00000000000..0d763301e87 --- /dev/null +++ b/packages/client/lib/commands/FUNCTION_LIST_WITHCODE.ts @@ -0,0 +1,26 @@ +import { RedisCommandArguments } from '.'; +import { transformArguments as transformFunctionListArguments } from './FUNCTION_LIST'; +import { FunctionListItemReply, FunctionListRawItemReply, transformFunctionListItemReply } from './generic-transformers'; + +export function transformArguments(pattern?: string): RedisCommandArguments { + const args = transformFunctionListArguments(pattern); + args.push('WITHCODE'); + return args; +} + +type FunctionListWithCodeRawItemReply = [ + ...FunctionListRawItemReply, + 'library_code', + string +]; + +interface FunctionListWithCodeItemReply extends FunctionListItemReply { + libraryCode: string; +} + +export function transformReply(reply: Array): Array { + return reply.map(library => ({ + ...transformFunctionListItemReply(library as unknown as FunctionListRawItemReply), + libraryCode: library[7] + })); +} diff --git a/packages/client/lib/commands/FUNCTION_LOAD.spec.ts b/packages/client/lib/commands/FUNCTION_LOAD.spec.ts new file mode 100644 index 00000000000..6beac45d0aa --- /dev/null +++ b/packages/client/lib/commands/FUNCTION_LOAD.spec.ts @@ -0,0 +1,36 @@ +import { strict as assert } from 'assert'; +import testUtils, { GLOBAL } from '../test-utils'; +import { MATH_FUNCTION } from '../client/index.spec'; +import { transformArguments } from './FUNCTION_LOAD'; + +describe('FUNCTION LOAD', () => { + testUtils.isVersionGreaterThanHook([7, 0]); + + describe('transformArguments', () => { + it('simple', () => { + assert.deepEqual( + transformArguments( 'code'), + ['FUNCTION', 'LOAD', 'code'] + ); + }); + + it('with REPLACE', () => { + assert.deepEqual( + transformArguments('code', { + REPLACE: true + }), + ['FUNCTION', 'LOAD', 'REPLACE', 'code'] + ); + }); + }); + + testUtils.testWithClient('client.functionLoad', async client => { + assert.equal( + await client.functionLoad( + MATH_FUNCTION.code, + { REPLACE: true } + ), + MATH_FUNCTION.name + ); + }, GLOBAL.SERVERS.OPEN); +}); diff --git a/packages/client/lib/commands/FUNCTION_LOAD.ts b/packages/client/lib/commands/FUNCTION_LOAD.ts new file mode 100644 index 00000000000..7ab58d58598 --- /dev/null +++ b/packages/client/lib/commands/FUNCTION_LOAD.ts @@ -0,0 +1,22 @@ +import { RedisCommandArguments } from '.'; + +interface FunctionLoadOptions { + REPLACE?: boolean; +} + +export function transformArguments( + code: string, + options?: FunctionLoadOptions +): RedisCommandArguments { + const args = ['FUNCTION', 'LOAD']; + + if (options?.REPLACE) { + args.push('REPLACE'); + } + + args.push(code); + + return args; +} + +export declare function transformReply(): string; diff --git a/packages/client/lib/commands/FUNCTION_RESTORE.spec.ts b/packages/client/lib/commands/FUNCTION_RESTORE.spec.ts new file mode 100644 index 00000000000..3ed4c2b3298 --- /dev/null +++ b/packages/client/lib/commands/FUNCTION_RESTORE.spec.ts @@ -0,0 +1,37 @@ +import { strict as assert } from 'assert'; +import testUtils, { GLOBAL } from '../test-utils'; +import { transformArguments } from './FUNCTION_RESTORE'; + +describe('FUNCTION RESTORE', () => { + testUtils.isVersionGreaterThanHook([7, 0]); + + describe('transformArguments', () => { + it('simple', () => { + assert.deepEqual( + transformArguments('dump'), + ['FUNCTION', 'RESTORE', 'dump'] + ); + }); + + it('with mode', () => { + assert.deepEqual( + transformArguments('dump', 'APPEND'), + ['FUNCTION', 'RESTORE', 'dump', 'APPEND'] + ); + }); + }); + + testUtils.testWithClient('client.functionRestore', async client => { + assert.equal( + await client.functionRestore( + await client.functionDump( + client.commandOptions({ + returnBuffers: true + }) + ), + 'FLUSH' + ), + 'OK' + ); + }, GLOBAL.SERVERS.OPEN); +}); diff --git a/packages/client/lib/commands/FUNCTION_RESTORE.ts b/packages/client/lib/commands/FUNCTION_RESTORE.ts new file mode 100644 index 00000000000..bc9c41e262d --- /dev/null +++ b/packages/client/lib/commands/FUNCTION_RESTORE.ts @@ -0,0 +1,16 @@ +import { RedisCommandArgument, RedisCommandArguments } from '.'; + +export function transformArguments( + dump: RedisCommandArgument, + mode?: 'FLUSH' | 'APPEND' | 'REPLACE' +): RedisCommandArguments { + const args = ['FUNCTION', 'RESTORE', dump]; + + if (mode) { + args.push(mode); + } + + return args; +} + +export declare function transformReply(): 'OK'; diff --git a/packages/client/lib/commands/FUNCTION_STATS.spec.ts b/packages/client/lib/commands/FUNCTION_STATS.spec.ts new file mode 100644 index 00000000000..c9f648fa5e0 --- /dev/null +++ b/packages/client/lib/commands/FUNCTION_STATS.spec.ts @@ -0,0 +1,25 @@ +import { strict as assert } from 'assert'; +import testUtils, { GLOBAL } from '../test-utils'; +import { transformArguments } from './FUNCTION_STATS'; + +describe('FUNCTION STATS', () => { + testUtils.isVersionGreaterThanHook([7, 0]); + + it('transformArguments', () => { + assert.deepEqual( + transformArguments(), + ['FUNCTION', 'STATS'] + ); + }); + + testUtils.testWithClient('client.functionStats', async client => { + const stats = await client.functionStats(); + assert.equal(stats.runningScript, null); + assert.equal(typeof stats.engines, 'object'); + for (const [engine, { librariesCount, functionsCount }] of Object.entries(stats.engines)) { + assert.equal(typeof engine, 'string'); + assert.equal(typeof librariesCount, 'number'); + assert.equal(typeof functionsCount, 'number'); + } + }, GLOBAL.SERVERS.OPEN); +}); diff --git a/packages/client/lib/commands/FUNCTION_STATS.ts b/packages/client/lib/commands/FUNCTION_STATS.ts new file mode 100644 index 00000000000..bb5c957e78b --- /dev/null +++ b/packages/client/lib/commands/FUNCTION_STATS.ts @@ -0,0 +1,56 @@ +import { RedisCommandArguments } from '.'; + +export function transformArguments(): RedisCommandArguments { + return ['FUNCTION', 'STATS']; +} + +type FunctionStatsRawReply = [ + 'running_script', + null | [ + 'name', + string, + 'command', + string, + 'duration_ms', + number + ], + 'engines', + Array // "flat tuples" (there is no way to type that) + // ...[string, [ + // 'libraries_count', + // number, + // 'functions_count', + // number + // ]] +]; + +interface FunctionStatsReply { + runningScript: null | { + name: string; + command: string; + durationMs: number; + }; + engines: Record; +} + +export function transformReply(reply: FunctionStatsRawReply): FunctionStatsReply { + const engines = Object.create(null); + for (let i = 0; i < reply[3].length; i++) { + engines[reply[3][i]] = { + librariesCount: reply[3][++i][1], + functionsCount: reply[3][i][3] + }; + } + + return { + runningScript: reply[1] === null ? null : { + name: reply[1][1], + command: reply[1][3], + durationMs: reply[1][5] + }, + engines + }; +} diff --git a/packages/client/lib/commands/generic-transformers.ts b/packages/client/lib/commands/generic-transformers.ts index e881822fb40..0477caffd45 100644 --- a/packages/client/lib/commands/generic-transformers.ts +++ b/packages/client/lib/commands/generic-transformers.ts @@ -348,6 +348,10 @@ export interface EvalOptions { arguments?: Array; } +export function evalFirstKeyIndex(options?: EvalOptions): string | undefined { + return options?.keys?.[0]; +} + export function pushEvalArguments(args: Array, options?: EvalOptions): Array { if (options?.keys) { args.push( @@ -491,6 +495,51 @@ export function transformCommandReply( }; } +export enum RedisFunctionFlags { + NO_WRITES = 'no-writes', + ALLOW_OOM = 'allow-oom', + ALLOW_STALE = 'allow-stale', + NO_CLUSTER = 'no-cluster' +} + +export type FunctionListRawItemReply = [ + 'library_name', + string, + 'engine', + string, + 'functions', + Array<[ + 'name', + string, + 'description', + string | null, + 'flags', + Array + ]> +]; + +export interface FunctionListItemReply { + libraryName: string; + engine: string; + functions: Array<{ + name: string; + description: string | null; + flags: Array; + }>; +} + +export function transformFunctionListItemReply(reply: FunctionListRawItemReply): FunctionListItemReply { + return { + libraryName: reply[1], + engine: reply[3], + functions: reply[5].map(fn => ({ + name: fn[1], + description: fn[3], + flags: fn[5] + })) + }; +} + export interface SortOptions { BY?: string; LIMIT?: { diff --git a/packages/client/lib/commands/index.ts b/packages/client/lib/commands/index.ts index d8cfe5332df..84043bcd63d 100644 --- a/packages/client/lib/commands/index.ts +++ b/packages/client/lib/commands/index.ts @@ -1,22 +1,51 @@ +import { ClientCommandOptions } from '../client'; +import { CommandOptions } from '../command-options'; import { RedisScriptConfig, SHA1 } from '../lua-script'; -// https://github.com/Microsoft/TypeScript/issues/3496#issuecomment-128553540 -// eslint-disable-next-line @typescript-eslint/no-empty-interface -interface RedisCommandRawReplyArray extends Array {} -export type RedisCommandRawReply = string | number | Buffer | null | undefined | RedisCommandRawReplyArray; +export type RedisCommandRawReply = string | number | Buffer | null | undefined | Array; export type RedisCommandArgument = string | Buffer; export type RedisCommandArguments = Array & { preserve?: unknown }; export interface RedisCommand { - FIRST_KEY_INDEX?: number | ((...args: Array) => RedisCommandArgument); + FIRST_KEY_INDEX?: number | ((...args: Array) => RedisCommandArgument | undefined); IS_READ_ONLY?: boolean; transformArguments(this: void, ...args: Array): RedisCommandArguments; transformReply?(this: void, reply: any, preserved?: any): any; } -export type RedisCommandReply = C['transformReply'] extends (...args: any) => infer T ? T : RedisCommandRawReply; +export type RedisCommandReply = + C['transformReply'] extends (...args: any) => infer T ? T : RedisCommandRawReply; + +export type ConvertArgumentType = + Type extends RedisCommandArgument ? ( + Type extends (string & ToType) ? Type : ToType + ) : ( + Type extends Set ? Set> : ( + Type extends Map ? Map> : ( + Type extends Array ? Array> : ( + Type extends Date ? Type : ( + Type extends Record ? { + [Property in keyof Type]: ConvertArgumentType + } : Type + ) + ) + ) + ) + ); + +export type RedisCommandSignature< + Command extends RedisCommand, + Params extends Array = Parameters +> = >( + ...args: Params | [options: Options, ...rest: Params] +) => Promise< + ConvertArgumentType< + RedisCommandReply, + Options['returnBuffers'] extends true ? Buffer : string + > +>; export interface RedisCommands { [command: string]: RedisCommand; @@ -30,13 +59,33 @@ export interface RedisModules { [module: string]: RedisModule; } +export interface RedisFunction extends RedisCommand { + NAME: string; + NUMBER_OF_KEYS?: number; +} + +export interface RedisFunctionLibrary { + [fn: string]: RedisFunction; +} + +export interface RedisFunctions { + [library: string]: RedisFunctionLibrary; +} + export type RedisScript = RedisScriptConfig & SHA1; export interface RedisScripts { [script: string]: RedisScript; } -export interface RedisPlugins { +export interface RedisExtensions< + M extends RedisModules = RedisModules, + F extends RedisFunctions = RedisFunctions, + S extends RedisScripts = RedisScripts +> { modules?: M; + functions?: F; scripts?: S; } + +export type ExcludeMappedString = string extends S ? never : S; diff --git a/packages/client/lib/lua-script.ts b/packages/client/lib/lua-script.ts index 8a0481364c4..da19417ec25 100644 --- a/packages/client/lib/lua-script.ts +++ b/packages/client/lib/lua-script.ts @@ -3,7 +3,7 @@ import { RedisCommand } from './commands'; export interface RedisScriptConfig extends RedisCommand { SCRIPT: string; - NUMBER_OF_KEYS: number; + NUMBER_OF_KEYS?: number; } export interface SHA1 { diff --git a/packages/client/lib/multi-command.ts b/packages/client/lib/multi-command.ts index d66974a5a21..10ab77ad19b 100644 --- a/packages/client/lib/multi-command.ts +++ b/packages/client/lib/multi-command.ts @@ -1,4 +1,5 @@ -import { RedisCommand, RedisCommandArguments, RedisCommandRawReply, RedisScript } from './commands'; +import { fCallArguments } from './commander'; +import { RedisCommand, RedisCommandArguments, RedisCommandRawReply, RedisFunction, RedisScript } from './commands'; import { WatchError } from './errors'; export interface RedisMultiQueuedCommand { @@ -22,6 +23,18 @@ export default class RedisMultiCommand { }); } + addFunction(fn: RedisFunction, args: Array): RedisCommandArguments { + const transformedArguments = fCallArguments( + fn, + fn.transformArguments(...args) + ); + this.queue.push({ + args: transformedArguments, + transformReply: fn.transformReply + }); + return transformedArguments; + } + addScript(script: RedisScript, args: Array): RedisCommandArguments { const transformedArguments: RedisCommandArguments = []; if (this.scriptsInUse.has(script.SHA1)) { @@ -37,7 +50,9 @@ export default class RedisMultiCommand { ); } - transformedArguments.push(script.NUMBER_OF_KEYS.toString()); + if (script.NUMBER_OF_KEYS !== undefined) { + transformedArguments.push(script.NUMBER_OF_KEYS.toString()); + } const scriptArguments = script.transformArguments(...args); transformedArguments.push(...scriptArguments); diff --git a/packages/client/lib/test-utils.ts b/packages/client/lib/test-utils.ts index fbed7698896..1ea7b590076 100644 --- a/packages/client/lib/test-utils.ts +++ b/packages/client/lib/test-utils.ts @@ -3,7 +3,7 @@ import { SinonSpy } from 'sinon'; import { promiseTimeout } from './utils'; export default new TestUtils({ - defaultDockerVersion: '6.2', + defaultDockerVersion: '7.0-rc3', dockerImageName: 'redis', dockerImageVersionArgument: 'redis-version' }); diff --git a/packages/graph/lib/test-utils.ts b/packages/graph/lib/test-utils.ts index ad6dc0fd190..da4fb0f78d9 100644 --- a/packages/graph/lib/test-utils.ts +++ b/packages/graph/lib/test-utils.ts @@ -4,7 +4,7 @@ import RedisGraph from '.'; export default new TestUtils({ dockerImageName: 'redislabs/redisgraph', dockerImageVersionArgument: 'redisgraph-version', - defaultDockerVersion: '2.8.7' + defaultDockerVersion: '2.8.9' }); export const GLOBAL = { diff --git a/packages/json/lib/test-utils.ts b/packages/json/lib/test-utils.ts index c41870567ef..a4d5bee7e9d 100644 --- a/packages/json/lib/test-utils.ts +++ b/packages/json/lib/test-utils.ts @@ -4,7 +4,7 @@ import RedisJSON from '.'; export default new TestUtils({ dockerImageName: 'redislabs/rejson', dockerImageVersionArgument: 'rejson-version', - defaultDockerVersion: '2.0.6' + defaultDockerVersion: '2.0.7' }); export const GLOBAL = { diff --git a/packages/test-utils/lib/dockers.ts b/packages/test-utils/lib/dockers.ts index 0bf4f034bfc..ad803a1fde4 100644 --- a/packages/test-utils/lib/dockers.ts +++ b/packages/test-utils/lib/dockers.ts @@ -1,6 +1,6 @@ import { createConnection } from 'net'; import { once } from 'events'; -import { RedisModules, RedisScripts } from '@node-redis/client/lib/commands'; +import { RedisModules, RedisFunctions, RedisScripts } from '@node-redis/client/lib/commands'; import RedisClient, { RedisClientType } from '@node-redis/client/lib/client'; import { promiseTimeout } from '@node-redis/client/lib/utils'; import * as path from 'path'; @@ -152,7 +152,11 @@ async function spawnRedisClusterNodeDocker( } } -async function waitForClusterState(client: RedisClientType): Promise { +async function waitForClusterState< + M extends RedisModules, + F extends RedisFunctions, + S extends RedisScripts +>(client: RedisClientType): Promise { while ((await client.clusterInfo()).state !== 'ok') { await promiseTimeout(500); } diff --git a/packages/test-utils/lib/index.ts b/packages/test-utils/lib/index.ts index 9eaed1f8d3c..7a83f2ad2e5 100644 --- a/packages/test-utils/lib/index.ts +++ b/packages/test-utils/lib/index.ts @@ -1,4 +1,4 @@ -import { RedisModules, RedisScripts } from '@node-redis/client/lib/commands'; +import { RedisModules, RedisFunctions, RedisScripts } from '@node-redis/client/lib/commands'; import RedisClient, { RedisClientOptions, RedisClientType } from '@node-redis/client/lib/client'; import RedisCluster, { RedisClusterOptions, RedisClusterType } from '@node-redis/client/lib/cluster'; import { RedisServerDockerConfig, spawnRedisServer, spawnRedisCluster } from './dockers'; @@ -15,15 +15,23 @@ interface CommonTestOptions { minimumDockerVersion?: Array; } -interface ClientTestOptions extends CommonTestOptions { +interface ClientTestOptions< + M extends RedisModules, + F extends RedisFunctions, + S extends RedisScripts +> extends CommonTestOptions { serverArguments: Array; - clientOptions?: Partial>; + clientOptions?: Partial>; disableClientSetup?: boolean; } -interface ClusterTestOptions extends CommonTestOptions { +interface ClusterTestOptions< + M extends RedisModules, + F extends RedisFunctions, + S extends RedisScripts +> extends CommonTestOptions { serverArguments: Array; - clusterConfiguration?: Partial>; + clusterConfiguration?: Partial>; numberOfNodes?: number; } @@ -93,10 +101,14 @@ export default class TestUtils { }); } - testWithClient( + testWithClient< + M extends RedisModules, + F extends RedisFunctions, + S extends RedisScripts + >( title: string, - fn: (client: RedisClientType) => Promise, - options: ClientTestOptions + fn: (client: RedisClientType) => Promise, + options: ClientTestOptions ): void { let dockerPromise: ReturnType; if (this.isVersionGreaterThan(options.minimumDockerVersion)) { @@ -138,16 +150,24 @@ export default class TestUtils { }); } - static async #clusterFlushAll(cluster: RedisClusterType): Promise { + static async #clusterFlushAll< + M extends RedisModules, + F extends RedisFunctions, + S extends RedisScripts + >(cluster: RedisClusterType): Promise { await Promise.all( cluster.getMasters().map(({ client }) => client.flushAll()) ); } - testWithCluster( + testWithCluster< + M extends RedisModules, + F extends RedisFunctions, + S extends RedisScripts + >( title: string, - fn: (cluster: RedisClusterType) => Promise, - options: ClusterTestOptions + fn: (cluster: RedisClusterType) => Promise, + options: ClusterTestOptions ): void { let dockersPromise: ReturnType; if (this.isVersionGreaterThan(options.minimumDockerVersion)) { diff --git a/packages/time-series/lib/test-utils.ts b/packages/time-series/lib/test-utils.ts index eebb1b416d8..c4639a98d49 100644 --- a/packages/time-series/lib/test-utils.ts +++ b/packages/time-series/lib/test-utils.ts @@ -4,7 +4,7 @@ import TimeSeries from '.'; export default new TestUtils({ dockerImageName: 'redislabs/redistimeseries', dockerImageVersionArgument: 'timeseries-version', - defaultDockerVersion: '1.6.8' + defaultDockerVersion: '1.6.9' }); export const GLOBAL = { From 432a7e3ebb2d59661fb193adfd6cda14bf7236c6 Mon Sep 17 00:00:00 2001 From: Leibale Eidelman Date: Mon, 25 Apr 2022 16:42:20 -0400 Subject: [PATCH 1195/1748] upgrade deps (#2098) --- benchmark/package-lock.json | 31 +- package-lock.json | 583 +++++++++++++----------------- packages/bloom/package.json | 2 +- packages/client/package.json | 10 +- packages/graph/package.json | 2 +- packages/json/package.json | 2 +- packages/search/package.json | 2 +- packages/test-utils/package.json | 4 +- packages/time-series/package.json | 2 +- 9 files changed, 281 insertions(+), 357 deletions(-) diff --git a/benchmark/package-lock.json b/benchmark/package-lock.json index 857b3c423ac..3b4e24af23c 100644 --- a/benchmark/package-lock.json +++ b/benchmark/package-lock.json @@ -25,16 +25,15 @@ "devDependencies": { "@istanbuljs/nyc-config-typescript": "^1.0.2", "@node-redis/test-utils": "*", - "@types/node": "^17.0.23", - "@types/redis-parser": "^3.0.0", + "@types/node": "^17.0.26", "@types/sinon": "^10.0.11", "@types/yallist": "^4.0.1", - "@typescript-eslint/eslint-plugin": "^5.19.0", - "@typescript-eslint/parser": "^5.19.0", - "eslint": "^8.13.0", + "@typescript-eslint/eslint-plugin": "^5.20.0", + "@typescript-eslint/parser": "^5.20.0", + "eslint": "^8.14.0", "nyc": "^15.1.0", "release-it": "^14.14.2", - "sinon": "^13.0.1", + "sinon": "^13.0.2", "source-map-support": "^0.5.21", "ts-node": "^10.7.0", "typedoc": "^0.22.15", @@ -315,14 +314,6 @@ "@node-redis/time-series": "1.0.2" } }, - "node_modules/redis-v3/node_modules/denque": { - "version": "1.5.1", - "resolved": "https://registry.npmjs.org/denque/-/denque-1.5.1.tgz", - "integrity": "sha512-XwE+iZ4D6ZUB7mfYRMb5wByE8L74HCn30FBN7sWnXksWc1LO1bPDl67pBR9o/kC4z/xSNAwkMYcGgqDV3BE3Hw==", - "engines": { - "node": ">=0.10" - } - }, "node_modules/require-directory": { "version": "2.1.1", "resolved": "https://registry.npmjs.org/require-directory/-/require-directory-2.1.1.tgz", @@ -432,19 +423,17 @@ "requires": { "@istanbuljs/nyc-config-typescript": "^1.0.2", "@node-redis/test-utils": "*", - "@types/node": "^17.0.23", - "@types/redis-parser": "^3.0.0", + "@types/node": "^17.0.26", "@types/sinon": "^10.0.11", "@types/yallist": "^4.0.1", - "@typescript-eslint/eslint-plugin": "^5.19.0", - "@typescript-eslint/parser": "^5.19.0", + "@typescript-eslint/eslint-plugin": "^5.20.0", + "@typescript-eslint/parser": "^5.20.0", "cluster-key-slot": "1.1.0", - "eslint": "^8.13.0", + "eslint": "^8.14.0", "generic-pool": "3.8.2", "nyc": "^15.1.0", - "redis-parser": "3.0.0", "release-it": "^14.14.2", - "sinon": "^13.0.1", + "sinon": "^13.0.2", "source-map-support": "^0.5.21", "ts-node": "^10.7.0", "typedoc": "^0.22.15", diff --git a/package-lock.json b/package-lock.json index 9152f212fb8..460a6313fb8 100644 --- a/package-lock.json +++ b/package-lock.json @@ -412,9 +412,9 @@ } }, "node_modules/@eslint/eslintrc": { - "version": "1.2.1", - "resolved": "https://registry.npmjs.org/@eslint/eslintrc/-/eslintrc-1.2.1.tgz", - "integrity": "sha512-bxvbYnBPN1Gibwyp6NrpnFzA3YtRL3BBAyEAFVIpNTm2Rn4Vy87GA5M4aSn3InRrlsbX5N0GW7XIx+U4SAEKdQ==", + "version": "1.2.2", + "resolved": "https://registry.npmjs.org/@eslint/eslintrc/-/eslintrc-1.2.2.tgz", + "integrity": "sha512-lTVWHs7O2hjBFZunXTZYnYqtB9GakA1lnxIf+gKq2nY5gxkkNi/lQvveW6t8gFdOHTg6nG50Xs95PrLqVpcaLg==", "dev": true, "dependencies": { "ajv": "^6.12.4", @@ -550,9 +550,9 @@ } }, "node_modules/@jridgewell/resolve-uri": { - "version": "3.0.5", - "resolved": "https://registry.npmjs.org/@jridgewell/resolve-uri/-/resolve-uri-3.0.5.tgz", - "integrity": "sha512-VPeQ7+wH0itvQxnG+lIzWgkysKIr3L9sslimFW55rHMdGu/qCQ5z5h9zq4gI8uBtqkpHhsF4Z/OwExufUCThew==", + "version": "3.0.6", + "resolved": "https://registry.npmjs.org/@jridgewell/resolve-uri/-/resolve-uri-3.0.6.tgz", + "integrity": "sha512-R7xHtBSNm+9SyvpJkdQl+qrM3Hm2fea3Ef197M3mUug+v+yR+Rhfbs7PBtcBUVnIWJ4JcAdjvij+c8hXS9p5aw==", "dev": true, "engines": { "node": ">=6.0.0" @@ -565,9 +565,9 @@ "dev": true }, "node_modules/@jridgewell/trace-mapping": { - "version": "0.3.4", - "resolved": "https://registry.npmjs.org/@jridgewell/trace-mapping/-/trace-mapping-0.3.4.tgz", - "integrity": "sha512-vFv9ttIedivx0ux3QSjhgtCVjPZd5l46ZOMDSCwnH1yUO2e964gO8LZGyv2QkqcgR6TnBU1v+1IFqmeoG+0UJQ==", + "version": "0.3.9", + "resolved": "https://registry.npmjs.org/@jridgewell/trace-mapping/-/trace-mapping-0.3.9.tgz", + "integrity": "sha512-3Belt6tdc8bPgAtbcmdtNJlirVoTmEb5e2gC94PnkwEW9jI6CAHUeoG85tjWP5WquqfavoMtMwiG4P926ZKKuQ==", "dev": true, "dependencies": { "@jridgewell/resolve-uri": "^3.0.3", @@ -856,15 +856,15 @@ "dev": true }, "node_modules/@types/mocha": { - "version": "9.1.0", - "resolved": "https://registry.npmjs.org/@types/mocha/-/mocha-9.1.0.tgz", - "integrity": "sha512-QCWHkbMv4Y5U9oW10Uxbr45qMMSzl4OzijsozynUAgx3kEHUdXB00udx2dWDQ7f2TU2a2uuiFaRZjCe3unPpeg==", + "version": "9.1.1", + "resolved": "https://registry.npmjs.org/@types/mocha/-/mocha-9.1.1.tgz", + "integrity": "sha512-Z61JK7DKDtdKTWwLeElSEBcWGRLY8g95ic5FoQqI9CMx0ns/Ghep3B4DfcEimiKMvtamNVULVNKEsiwV3aQmXw==", "dev": true }, "node_modules/@types/node": { - "version": "17.0.23", - "resolved": "https://registry.npmjs.org/@types/node/-/node-17.0.23.tgz", - "integrity": "sha512-UxDxWn7dl97rKVeVS61vErvw086aCYhDLyvRQZ5Rk65rZKepaFdm53GeqXaKBuOhED4e9uWq34IC3TdSdJJ2Gw==", + "version": "17.0.26", + "resolved": "https://registry.npmjs.org/@types/node/-/node-17.0.26.tgz", + "integrity": "sha512-z/FG/6DUO7pnze3AE3TBGIjGGKkvCcGcWINe1C7cADY8hKLJPDYpzsNE37uExQ4md5RFtTCvg+M8Mu1Enyeg2A==", "dev": true }, "node_modules/@types/parse-json": { @@ -873,22 +873,6 @@ "integrity": "sha512-//oorEZjL6sbPcKUaCdIGlIUeH26mgzimjBB77G6XRgnDl/L5wOnpyBGRe/Mmf5CVW3PwEBE1NjiMZ/ssFh4wA==", "dev": true }, - "node_modules/@types/redis-errors": { - "version": "1.2.1", - "resolved": "https://registry.npmjs.org/@types/redis-errors/-/redis-errors-1.2.1.tgz", - "integrity": "sha512-9p6SQPeDAIR4z3ZdrPsRH/sSRMeIA2fdRKqZ3Y1thQOLeDH4aLY+J4Ze32zjg4Dq7655Y0LonCoRrH5O7vtr4w==", - "dev": true - }, - "node_modules/@types/redis-parser": { - "version": "3.0.0", - "resolved": "https://registry.npmjs.org/@types/redis-parser/-/redis-parser-3.0.0.tgz", - "integrity": "sha512-TH9NnE980dXWKjFRkck6FIv9akrD6G+vX9XXONoBgG9+NfP4iZ+SbGkaN7S15c6+JeZ+zBN62bHt2lNyUYwqoA==", - "dev": true, - "dependencies": { - "@types/node": "*", - "@types/redis-errors": "*" - } - }, "node_modules/@types/sinon": { "version": "10.0.11", "resolved": "https://registry.npmjs.org/@types/sinon/-/sinon-10.0.11.tgz", @@ -926,14 +910,14 @@ "dev": true }, "node_modules/@typescript-eslint/eslint-plugin": { - "version": "5.19.0", - "resolved": "https://registry.npmjs.org/@typescript-eslint/eslint-plugin/-/eslint-plugin-5.19.0.tgz", - "integrity": "sha512-w59GpFqDYGnWFim9p6TGJz7a3qWeENJuAKCqjGSx+Hq/bwq3RZwXYqy98KIfN85yDqz9mq6QXiY5h0FjGQLyEg==", + "version": "5.20.0", + "resolved": "https://registry.npmjs.org/@typescript-eslint/eslint-plugin/-/eslint-plugin-5.20.0.tgz", + "integrity": "sha512-fapGzoxilCn3sBtC6NtXZX6+P/Hef7VDbyfGqTTpzYydwhlkevB+0vE0EnmHPVTVSy68GUncyJ/2PcrFBeCo5Q==", "dev": true, "dependencies": { - "@typescript-eslint/scope-manager": "5.19.0", - "@typescript-eslint/type-utils": "5.19.0", - "@typescript-eslint/utils": "5.19.0", + "@typescript-eslint/scope-manager": "5.20.0", + "@typescript-eslint/type-utils": "5.20.0", + "@typescript-eslint/utils": "5.20.0", "debug": "^4.3.2", "functional-red-black-tree": "^1.0.1", "ignore": "^5.1.8", @@ -974,14 +958,14 @@ } }, "node_modules/@typescript-eslint/parser": { - "version": "5.19.0", - "resolved": "https://registry.npmjs.org/@typescript-eslint/parser/-/parser-5.19.0.tgz", - "integrity": "sha512-yhktJjMCJX8BSBczh1F/uY8wGRYrBeyn84kH6oyqdIJwTGKmzX5Qiq49LRQ0Jh0LXnWijEziSo6BRqny8nqLVQ==", + "version": "5.20.0", + "resolved": "https://registry.npmjs.org/@typescript-eslint/parser/-/parser-5.20.0.tgz", + "integrity": "sha512-UWKibrCZQCYvobmu3/N8TWbEeo/EPQbS41Ux1F9XqPzGuV7pfg6n50ZrFo6hryynD8qOTTfLHtHjjdQtxJ0h/w==", "dev": true, "dependencies": { - "@typescript-eslint/scope-manager": "5.19.0", - "@typescript-eslint/types": "5.19.0", - "@typescript-eslint/typescript-estree": "5.19.0", + "@typescript-eslint/scope-manager": "5.20.0", + "@typescript-eslint/types": "5.20.0", + "@typescript-eslint/typescript-estree": "5.20.0", "debug": "^4.3.2" }, "engines": { @@ -1001,13 +985,13 @@ } }, "node_modules/@typescript-eslint/scope-manager": { - "version": "5.19.0", - "resolved": "https://registry.npmjs.org/@typescript-eslint/scope-manager/-/scope-manager-5.19.0.tgz", - "integrity": "sha512-Fz+VrjLmwq5fbQn5W7cIJZ066HxLMKvDEmf4eu1tZ8O956aoX45jAuBB76miAECMTODyUxH61AQM7q4/GOMQ5g==", + "version": "5.20.0", + "resolved": "https://registry.npmjs.org/@typescript-eslint/scope-manager/-/scope-manager-5.20.0.tgz", + "integrity": "sha512-h9KtuPZ4D/JuX7rpp1iKg3zOH0WNEa+ZIXwpW/KWmEFDxlA/HSfCMhiyF1HS/drTICjIbpA6OqkAhrP/zkCStg==", "dev": true, "dependencies": { - "@typescript-eslint/types": "5.19.0", - "@typescript-eslint/visitor-keys": "5.19.0" + "@typescript-eslint/types": "5.20.0", + "@typescript-eslint/visitor-keys": "5.20.0" }, "engines": { "node": "^12.22.0 || ^14.17.0 || >=16.0.0" @@ -1018,12 +1002,12 @@ } }, "node_modules/@typescript-eslint/type-utils": { - "version": "5.19.0", - "resolved": "https://registry.npmjs.org/@typescript-eslint/type-utils/-/type-utils-5.19.0.tgz", - "integrity": "sha512-O6XQ4RI4rQcBGshTQAYBUIGsKqrKeuIOz9v8bckXZnSeXjn/1+BDZndHLe10UplQeJLXDNbaZYrAytKNQO2T4Q==", + "version": "5.20.0", + "resolved": "https://registry.npmjs.org/@typescript-eslint/type-utils/-/type-utils-5.20.0.tgz", + "integrity": "sha512-WxNrCwYB3N/m8ceyoGCgbLmuZwupvzN0rE8NBuwnl7APgjv24ZJIjkNzoFBXPRCGzLNkoU/WfanW0exvp/+3Iw==", "dev": true, "dependencies": { - "@typescript-eslint/utils": "5.19.0", + "@typescript-eslint/utils": "5.20.0", "debug": "^4.3.2", "tsutils": "^3.21.0" }, @@ -1044,9 +1028,9 @@ } }, "node_modules/@typescript-eslint/types": { - "version": "5.19.0", - "resolved": "https://registry.npmjs.org/@typescript-eslint/types/-/types-5.19.0.tgz", - "integrity": "sha512-zR1ithF4Iyq1wLwkDcT+qFnhs8L5VUtjgac212ftiOP/ZZUOCuuF2DeGiZZGQXGoHA50OreZqLH5NjDcDqn34w==", + "version": "5.20.0", + "resolved": "https://registry.npmjs.org/@typescript-eslint/types/-/types-5.20.0.tgz", + "integrity": "sha512-+d8wprF9GyvPwtoB4CxBAR/s0rpP25XKgnOvMf/gMXYDvlUC3rPFHupdTQ/ow9vn7UDe5rX02ovGYQbv/IUCbg==", "dev": true, "engines": { "node": "^12.22.0 || ^14.17.0 || >=16.0.0" @@ -1057,13 +1041,13 @@ } }, "node_modules/@typescript-eslint/typescript-estree": { - "version": "5.19.0", - "resolved": "https://registry.npmjs.org/@typescript-eslint/typescript-estree/-/typescript-estree-5.19.0.tgz", - "integrity": "sha512-dRPuD4ocXdaE1BM/dNR21elSEUPKaWgowCA0bqJ6YbYkvtrPVEvZ+zqcX5a8ECYn3q5iBSSUcBBD42ubaOp0Hw==", + "version": "5.20.0", + "resolved": "https://registry.npmjs.org/@typescript-eslint/typescript-estree/-/typescript-estree-5.20.0.tgz", + "integrity": "sha512-36xLjP/+bXusLMrT9fMMYy1KJAGgHhlER2TqpUVDYUQg4w0q/NW/sg4UGAgVwAqb8V4zYg43KMUpM8vV2lve6w==", "dev": true, "dependencies": { - "@typescript-eslint/types": "5.19.0", - "@typescript-eslint/visitor-keys": "5.19.0", + "@typescript-eslint/types": "5.20.0", + "@typescript-eslint/visitor-keys": "5.20.0", "debug": "^4.3.2", "globby": "^11.0.4", "is-glob": "^4.0.3", @@ -1128,15 +1112,15 @@ } }, "node_modules/@typescript-eslint/utils": { - "version": "5.19.0", - "resolved": "https://registry.npmjs.org/@typescript-eslint/utils/-/utils-5.19.0.tgz", - "integrity": "sha512-ZuEckdupXpXamKvFz/Ql8YnePh2ZWcwz7APICzJL985Rp5C2AYcHO62oJzIqNhAMtMK6XvrlBTZeNG8n7gS3lQ==", + "version": "5.20.0", + "resolved": "https://registry.npmjs.org/@typescript-eslint/utils/-/utils-5.20.0.tgz", + "integrity": "sha512-lHONGJL1LIO12Ujyx8L8xKbwWSkoUKFSO+0wDAqGXiudWB2EO7WEUT+YZLtVbmOmSllAjLb9tpoIPwpRe5Tn6w==", "dev": true, "dependencies": { "@types/json-schema": "^7.0.9", - "@typescript-eslint/scope-manager": "5.19.0", - "@typescript-eslint/types": "5.19.0", - "@typescript-eslint/typescript-estree": "5.19.0", + "@typescript-eslint/scope-manager": "5.20.0", + "@typescript-eslint/types": "5.20.0", + "@typescript-eslint/typescript-estree": "5.20.0", "eslint-scope": "^5.1.1", "eslint-utils": "^3.0.0" }, @@ -1152,12 +1136,12 @@ } }, "node_modules/@typescript-eslint/visitor-keys": { - "version": "5.19.0", - "resolved": "https://registry.npmjs.org/@typescript-eslint/visitor-keys/-/visitor-keys-5.19.0.tgz", - "integrity": "sha512-Ym7zZoMDZcAKWsULi2s7UMLREdVQdScPQ/fKWMYefarCztWlHPFVJo8racf8R0Gc8FAEJ2eD4of8As1oFtnQlQ==", + "version": "5.20.0", + "resolved": "https://registry.npmjs.org/@typescript-eslint/visitor-keys/-/visitor-keys-5.20.0.tgz", + "integrity": "sha512-1flRpNF+0CAQkMNlTJ6L/Z5jiODG/e5+7mk6XwtPOUS3UrTz3UOiAg9jG2VtKsWI6rZQfy4C6a232QNRZTRGlg==", "dev": true, "dependencies": { - "@typescript-eslint/types": "5.19.0", + "@typescript-eslint/types": "5.20.0", "eslint-visitor-keys": "^3.0.0" }, "engines": { @@ -1512,9 +1496,9 @@ "dev": true }, "node_modules/browserslist": { - "version": "4.20.2", - "resolved": "https://registry.npmjs.org/browserslist/-/browserslist-4.20.2.tgz", - "integrity": "sha512-CQOBCqp/9pDvDbx3xfMi+86pr4KXIf2FDkTTdeuYw8OxS9t898LA1Khq57gtufFILXpfgsSx5woNgsBgvGjpsA==", + "version": "4.20.3", + "resolved": "https://registry.npmjs.org/browserslist/-/browserslist-4.20.3.tgz", + "integrity": "sha512-NBhymBQl1zM0Y5dQT/O+xiLP9/rzOIQdKM/eMJBAq7yBgaB6krIYLGejrwVYnSHZdqjscB1SPuAjHwxjvN6Wdg==", "dev": true, "funding": [ { @@ -1527,10 +1511,10 @@ } ], "dependencies": { - "caniuse-lite": "^1.0.30001317", - "electron-to-chromium": "^1.4.84", + "caniuse-lite": "^1.0.30001332", + "electron-to-chromium": "^1.4.118", "escalade": "^3.1.1", - "node-releases": "^2.0.2", + "node-releases": "^2.0.3", "picocolors": "^1.0.0" }, "bin": { @@ -1662,9 +1646,9 @@ } }, "node_modules/caniuse-lite": { - "version": "1.0.30001331", - "resolved": "https://registry.npmjs.org/caniuse-lite/-/caniuse-lite-1.0.30001331.tgz", - "integrity": "sha512-Y1xk6paHpUXKP/P6YjQv1xqyTbgAP05ycHBcRdQjTcyXlWol868sJJPlmk5ylOekw2BrucWes5jk+LvVd7WZ5Q==", + "version": "1.0.30001332", + "resolved": "https://registry.npmjs.org/caniuse-lite/-/caniuse-lite-1.0.30001332.tgz", + "integrity": "sha512-10T30NYOEQtN6C11YGg411yebhvpnC6Z102+B95eAsN0oB6KUs01ivE8u+G6FMIRtIrVlYXhL+LUwQ3/hXwDWw==", "dev": true, "funding": [ { @@ -1865,19 +1849,6 @@ "integrity": "sha1-3dgA2gxmEnOTzKWVDqloo6rxJTs=", "dev": true }, - "node_modules/compress-brotli": { - "version": "1.3.6", - "resolved": "https://registry.npmjs.org/compress-brotli/-/compress-brotli-1.3.6.tgz", - "integrity": "sha512-au99/GqZtUtiCBliqLFbWlhnCxn+XSYjwZ77q6mKN4La4qOXDoLVPZ50iXr0WmAyMxl8yqoq3Yq4OeQNPPkyeQ==", - "dev": true, - "dependencies": { - "@types/json-buffer": "~3.0.0", - "json-buffer": "~3.0.1" - }, - "engines": { - "node": ">= 12" - } - }, "node_modules/concat-map": { "version": "0.0.1", "resolved": "https://registry.npmjs.org/concat-map/-/concat-map-0.0.1.tgz", @@ -2051,15 +2022,19 @@ "dev": true }, "node_modules/define-properties": { - "version": "1.1.3", - "resolved": "https://registry.npmjs.org/define-properties/-/define-properties-1.1.3.tgz", - "integrity": "sha512-3MqfYKj2lLzdMSf8ZIZE/V+Zuy+BgD6f164e8K2w7dgnpKArBDerGYpM46IYYcjnkdPNMjPk9A6VFB8+3SKlXQ==", + "version": "1.1.4", + "resolved": "https://registry.npmjs.org/define-properties/-/define-properties-1.1.4.tgz", + "integrity": "sha512-uckOqKcfaVvtBdsVkdPv3XjveQJsNQqmhXgRi8uhvWWuPYZCNlzT8qAyblUgNoXdHdjMTzAqeGjAoli8f+bzPA==", "dev": true, "dependencies": { - "object-keys": "^1.0.12" + "has-property-descriptors": "^1.0.0", + "object-keys": "^1.1.1" }, "engines": { "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" } }, "node_modules/delayed-stream": { @@ -2129,9 +2104,9 @@ "dev": true }, "node_modules/electron-to-chromium": { - "version": "1.4.107", - "resolved": "https://registry.npmjs.org/electron-to-chromium/-/electron-to-chromium-1.4.107.tgz", - "integrity": "sha512-Huen6taaVrUrSy8o7mGStByba8PfOWWluHNxSHGBrCgEdFVLtvdQDBr9LBCF9Uci8SYxh28QNNMO0oC17wbGAg==", + "version": "1.4.118", + "resolved": "https://registry.npmjs.org/electron-to-chromium/-/electron-to-chromium-1.4.118.tgz", + "integrity": "sha512-maZIKjnYDvF7Fs35nvVcyr44UcKNwybr93Oba2n3HkKDFAtk0svERkLN/HyczJDS3Fo4wU9th9fUQd09ZLtj1w==", "dev": true }, "node_modules/email-addresses": { @@ -2165,9 +2140,9 @@ } }, "node_modules/es-abstract": { - "version": "1.19.4", - "resolved": "https://registry.npmjs.org/es-abstract/-/es-abstract-1.19.4.tgz", - "integrity": "sha512-flV8e5g9/xulChMG48Fygk1ptpo4lQRJ0eJYtxJFgi7pklLx7EFcOJ34jnvr8pbWlaFN/AT1cZpe0hiFel9Hqg==", + "version": "1.19.5", + "resolved": "https://registry.npmjs.org/es-abstract/-/es-abstract-1.19.5.tgz", + "integrity": "sha512-Aa2G2+Rd3b6kxEUKTF4TaW67czBLyAv3z7VOhYRU50YBx+bbsYZ9xQP4lMNazePuFlybXI0V4MruPos7qUo5fA==", "dev": true, "dependencies": { "call-bind": "^1.0.2", @@ -2274,12 +2249,12 @@ } }, "node_modules/eslint": { - "version": "8.13.0", - "resolved": "https://registry.npmjs.org/eslint/-/eslint-8.13.0.tgz", - "integrity": "sha512-D+Xei61eInqauAyTJ6C0q6x9mx7kTUC1KZ0m0LSEexR0V+e94K12LmWX076ZIsldwfQ2RONdaJe0re0TRGQbRQ==", + "version": "8.14.0", + "resolved": "https://registry.npmjs.org/eslint/-/eslint-8.14.0.tgz", + "integrity": "sha512-3/CE4aJX7LNEiE3i6FeodHmI/38GZtWCsAtsymScmzYapx8q1nVVb+eLcLSzATmCPXw5pT4TqVs1E0OmxAd9tw==", "dev": true, "dependencies": { - "@eslint/eslintrc": "^1.2.1", + "@eslint/eslintrc": "^1.2.2", "@humanwhocodes/config-array": "^0.9.2", "ajv": "^6.10.0", "chalk": "^4.0.0", @@ -3094,9 +3069,9 @@ } }, "node_modules/has-bigints": { - "version": "1.0.1", - "resolved": "https://registry.npmjs.org/has-bigints/-/has-bigints-1.0.1.tgz", - "integrity": "sha512-LSBS2LjbNBTf6287JEbEzvJgftkF5qFkmCo9hDRpAzKhUOlJ+hx8dd4USs00SgsUNwc4617J9ki5YtEClM2ffA==", + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/has-bigints/-/has-bigints-1.0.2.tgz", + "integrity": "sha512-tSvCKtBr9lkF0Ex0aQiP9N+OpV4zi2r/Nee5VkRDbaqv35RLYMzbwQfFSZZH0kR+Rd6302UJZ2p/bJCEoR3VoQ==", "dev": true, "funding": { "url": "https://github.com/sponsors/ljharb" @@ -3111,6 +3086,18 @@ "node": ">=8" } }, + "node_modules/has-property-descriptors": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/has-property-descriptors/-/has-property-descriptors-1.0.0.tgz", + "integrity": "sha512-62DVLZGoiEBDHQyqG4w9xCuZ7eJEwNmJRWw2VY84Oedb7WFcA27fiEVe8oUQx9hAUJ4ekurquucTGwsyO1XGdQ==", + "dev": true, + "dependencies": { + "get-intrinsic": "^1.1.1" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, "node_modules/has-symbols": { "version": "1.0.3", "resolved": "https://registry.npmjs.org/has-symbols/-/has-symbols-1.0.3.tgz", @@ -3345,9 +3332,9 @@ } }, "node_modules/inquirer": { - "version": "8.2.2", - "resolved": "https://registry.npmjs.org/inquirer/-/inquirer-8.2.2.tgz", - "integrity": "sha512-pG7I/si6K/0X7p1qU+rfWnpTE1UIkTONN1wxtzh0d+dHXtT/JG6qBgLxoyHVsQa8cFABxAPh0pD6uUUHiAoaow==", + "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", @@ -3360,13 +3347,13 @@ "mute-stream": "0.0.8", "ora": "^5.4.1", "run-async": "^2.4.0", - "rxjs": "^7.5.5", + "rxjs": "^7.2.0", "string-width": "^4.1.0", "strip-ansi": "^6.0.0", "through": "^2.3.6" }, "engines": { - "node": ">=12.0.0" + "node": ">=8.0.0" } }, "node_modules/internal-slot": { @@ -3479,9 +3466,9 @@ } }, "node_modules/is-core-module": { - "version": "2.8.1", - "resolved": "https://registry.npmjs.org/is-core-module/-/is-core-module-2.8.1.tgz", - "integrity": "sha512-SdNCUs284hr40hFTFP6l0IfZ/RSrMXF3qgoRHd3/79unUTvrFO/JoXwkGm+5J/Oe3E/b5GsnG330uUNgRpu1PA==", + "version": "2.9.0", + "resolved": "https://registry.npmjs.org/is-core-module/-/is-core-module-2.9.0.tgz", + "integrity": "sha512-+5FPy5PnwmO3lvfMb0AsoPaBG+5KHUI0wYFXOtYPnVVVspTFUuMZNfNaNVRt3FZadstu2c8x23vykRW/NBoU6A==", "dev": true, "dependencies": { "has": "^1.0.3" @@ -5393,25 +5380,6 @@ "node": ">= 0.10" } }, - "node_modules/redis-errors": { - "version": "1.2.0", - "resolved": "https://registry.npmjs.org/redis-errors/-/redis-errors-1.2.0.tgz", - "integrity": "sha1-62LSrbFeTq9GEMBK/hUpOEJQq60=", - "engines": { - "node": ">=4" - } - }, - "node_modules/redis-parser": { - "version": "3.0.0", - "resolved": "https://registry.npmjs.org/redis-parser/-/redis-parser-3.0.0.tgz", - "integrity": "sha1-tm2CjNyv5rS4pCin3vTGvKwxyLQ=", - "dependencies": { - "redis-errors": "^1.0.0" - }, - "engines": { - "node": ">=4" - } - }, "node_modules/regexpp": { "version": "3.2.0", "resolved": "https://registry.npmjs.org/regexpp/-/regexpp-3.2.0.tgz", @@ -5466,7 +5434,7 @@ "globby": "11.0.4", "got": "9.6.0", "import-cwd": "3.0.0", - "inquirer": "8.2.2", + "inquirer": "8.2.0", "is-ci": "3.0.1", "lodash": "4.17.21", "mime-types": "2.1.35", @@ -5818,13 +5786,13 @@ "dev": true }, "node_modules/sinon": { - "version": "13.0.1", - "resolved": "https://registry.npmjs.org/sinon/-/sinon-13.0.1.tgz", - "integrity": "sha512-8yx2wIvkBjIq/MGY1D9h1LMraYW+z1X0mb648KZnKSdvLasvDu7maa0dFaNYdTDczFgbjNw2tOmWdTk9saVfwQ==", + "version": "13.0.2", + "resolved": "https://registry.npmjs.org/sinon/-/sinon-13.0.2.tgz", + "integrity": "sha512-KvOrztAVqzSJWMDoxM4vM+GPys1df2VBoXm+YciyB/OLMamfS3VXh3oGh5WtrAGSzrgczNWFFY22oKb7Fi5eeA==", "dev": true, "dependencies": { "@sinonjs/commons": "^1.8.3", - "@sinonjs/fake-timers": "^9.0.0", + "@sinonjs/fake-timers": "^9.1.2", "@sinonjs/samsam": "^6.1.1", "diff": "^5.0.0", "nise": "^5.1.1", @@ -6176,9 +6144,9 @@ } }, "node_modules/tslib": { - "version": "2.3.1", - "resolved": "https://registry.npmjs.org/tslib/-/tslib-2.3.1.tgz", - "integrity": "sha512-77EbyPPpMz+FRFRuAFlWMtmgUWGe9UOG2Z25NqCwiIjRhOf5iKGuzSe5P2w1laq+FkRy4p+PCuVkJSGkzTEKVw==", + "version": "2.4.0", + "resolved": "https://registry.npmjs.org/tslib/-/tslib-2.4.0.tgz", + "integrity": "sha512-d6xOpEDfsi2CZVlPQzGeux8XMwLT9hssAsaPYExaQMuYskwb+x1x7J371tWlbBdWHroy99KnVB6qIkUbs5X3UQ==", "dev": true }, "node_modules/tsutils": { @@ -6301,14 +6269,14 @@ } }, "node_modules/unbox-primitive": { - "version": "1.0.1", - "resolved": "https://registry.npmjs.org/unbox-primitive/-/unbox-primitive-1.0.1.tgz", - "integrity": "sha512-tZU/3NqK3dA5gpE1KtyiJUrEB0lxnGkMFHptJ7q6ewdZ8s12QrODwNbhIJStmJkd1QDXa1NRA8aF2A1zk/Ypyw==", + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/unbox-primitive/-/unbox-primitive-1.0.2.tgz", + "integrity": "sha512-61pPlCD9h51VoreyJ0BReideM3MDKMKnh6+V9L08331ipq6Q8OFXZYiqP6n/tbHx4s5I9uRhcye6BrbkizkBDw==", "dev": true, "dependencies": { - "function-bind": "^1.1.1", - "has-bigints": "^1.0.1", - "has-symbols": "^1.0.2", + "call-bind": "^1.0.2", + "has-bigints": "^1.0.2", + "has-symbols": "^1.0.3", "which-boxed-primitive": "^1.0.2" }, "funding": { @@ -6452,9 +6420,9 @@ "dev": true }, "node_modules/v8-compile-cache-lib": { - "version": "3.0.0", - "resolved": "https://registry.npmjs.org/v8-compile-cache-lib/-/v8-compile-cache-lib-3.0.0.tgz", - "integrity": "sha512-mpSYqfsFvASnSn5qMiwrr4VKfumbPyONLCOPmsR3A6pTY/r0+tSaVbgPWSAIuzbk3lCTa+FForeTiO+wBQGkjA==", + "version": "3.0.1", + "resolved": "https://registry.npmjs.org/v8-compile-cache-lib/-/v8-compile-cache-lib-3.0.1.tgz", + "integrity": "sha512-wa7YjyUGfNZngI/vtK0UHAN+lgDCxBPCylVXGp0zu59Fz5aiGtNXaq3DhIov063MorB+VfufLh3JlF2KdTK3xg==", "dev": true }, "node_modules/vscode-oniguruma": { @@ -6784,7 +6752,7 @@ "devDependencies": { "@istanbuljs/nyc-config-typescript": "^1.0.2", "@node-redis/test-utils": "*", - "@types/node": "^17.0.23", + "@types/node": "^17.0.26", "nyc": "^15.1.0", "release-it": "^14.14.2", "source-map-support": "^0.5.21", @@ -6803,22 +6771,20 @@ "dependencies": { "cluster-key-slot": "1.1.0", "generic-pool": "3.8.2", - "redis-parser": "3.0.0", "yallist": "4.0.0" }, "devDependencies": { "@istanbuljs/nyc-config-typescript": "^1.0.2", "@node-redis/test-utils": "*", - "@types/node": "^17.0.23", - "@types/redis-parser": "^3.0.0", + "@types/node": "^17.0.26", "@types/sinon": "^10.0.11", "@types/yallist": "^4.0.1", - "@typescript-eslint/eslint-plugin": "^5.19.0", - "@typescript-eslint/parser": "^5.19.0", - "eslint": "^8.13.0", + "@typescript-eslint/eslint-plugin": "^5.20.0", + "@typescript-eslint/parser": "^5.20.0", + "eslint": "^8.14.0", "nyc": "^15.1.0", "release-it": "^14.14.2", - "sinon": "^13.0.1", + "sinon": "^13.0.2", "source-map-support": "^0.5.21", "ts-node": "^10.7.0", "typedoc": "^0.22.15", @@ -6835,7 +6801,7 @@ "devDependencies": { "@istanbuljs/nyc-config-typescript": "^1.0.2", "@node-redis/test-utils": "*", - "@types/node": "^17.0.23", + "@types/node": "^17.0.26", "nyc": "^15.1.0", "release-it": "^14.14.2", "source-map-support": "^0.5.21", @@ -6854,7 +6820,7 @@ "devDependencies": { "@istanbuljs/nyc-config-typescript": "^1.0.2", "@node-redis/test-utils": "*", - "@types/node": "^17.0.23", + "@types/node": "^17.0.26", "nyc": "^15.1.0", "release-it": "^14.14.2", "source-map-support": "^0.5.21", @@ -6873,7 +6839,7 @@ "devDependencies": { "@istanbuljs/nyc-config-typescript": "^1.0.2", "@node-redis/test-utils": "*", - "@types/node": "^17.0.23", + "@types/node": "^17.0.26", "nyc": "^15.1.0", "release-it": "^14.14.2", "source-map-support": "^0.5.21", @@ -6889,8 +6855,8 @@ "name": "@node-redis/test-utils", "devDependencies": { "@istanbuljs/nyc-config-typescript": "^1.0.2", - "@types/mocha": "^9.1.0", - "@types/node": "^17.0.23", + "@types/mocha": "^9.1.1", + "@types/node": "^17.0.26", "@types/yargs": "^17.0.10", "mocha": "^9.2.2", "nyc": "^15.1.0", @@ -6910,7 +6876,7 @@ "devDependencies": { "@istanbuljs/nyc-config-typescript": "^1.0.2", "@node-redis/test-utils": "*", - "@types/node": "^17.0.23", + "@types/node": "^17.0.26", "nyc": "^15.1.0", "release-it": "^14.14.2", "source-map-support": "^0.5.21", @@ -7220,9 +7186,9 @@ } }, "@eslint/eslintrc": { - "version": "1.2.1", - "resolved": "https://registry.npmjs.org/@eslint/eslintrc/-/eslintrc-1.2.1.tgz", - "integrity": "sha512-bxvbYnBPN1Gibwyp6NrpnFzA3YtRL3BBAyEAFVIpNTm2Rn4Vy87GA5M4aSn3InRrlsbX5N0GW7XIx+U4SAEKdQ==", + "version": "1.2.2", + "resolved": "https://registry.npmjs.org/@eslint/eslintrc/-/eslintrc-1.2.2.tgz", + "integrity": "sha512-lTVWHs7O2hjBFZunXTZYnYqtB9GakA1lnxIf+gKq2nY5gxkkNi/lQvveW6t8gFdOHTg6nG50Xs95PrLqVpcaLg==", "dev": true, "requires": { "ajv": "^6.12.4", @@ -7329,9 +7295,9 @@ "dev": true }, "@jridgewell/resolve-uri": { - "version": "3.0.5", - "resolved": "https://registry.npmjs.org/@jridgewell/resolve-uri/-/resolve-uri-3.0.5.tgz", - "integrity": "sha512-VPeQ7+wH0itvQxnG+lIzWgkysKIr3L9sslimFW55rHMdGu/qCQ5z5h9zq4gI8uBtqkpHhsF4Z/OwExufUCThew==", + "version": "3.0.6", + "resolved": "https://registry.npmjs.org/@jridgewell/resolve-uri/-/resolve-uri-3.0.6.tgz", + "integrity": "sha512-R7xHtBSNm+9SyvpJkdQl+qrM3Hm2fea3Ef197M3mUug+v+yR+Rhfbs7PBtcBUVnIWJ4JcAdjvij+c8hXS9p5aw==", "dev": true }, "@jridgewell/sourcemap-codec": { @@ -7341,9 +7307,9 @@ "dev": true }, "@jridgewell/trace-mapping": { - "version": "0.3.4", - "resolved": "https://registry.npmjs.org/@jridgewell/trace-mapping/-/trace-mapping-0.3.4.tgz", - "integrity": "sha512-vFv9ttIedivx0ux3QSjhgtCVjPZd5l46ZOMDSCwnH1yUO2e964gO8LZGyv2QkqcgR6TnBU1v+1IFqmeoG+0UJQ==", + "version": "0.3.9", + "resolved": "https://registry.npmjs.org/@jridgewell/trace-mapping/-/trace-mapping-0.3.9.tgz", + "integrity": "sha512-3Belt6tdc8bPgAtbcmdtNJlirVoTmEb5e2gC94PnkwEW9jI6CAHUeoG85tjWP5WquqfavoMtMwiG4P926ZKKuQ==", "dev": true, "requires": { "@jridgewell/resolve-uri": "^3.0.3", @@ -7355,7 +7321,7 @@ "requires": { "@istanbuljs/nyc-config-typescript": "^1.0.2", "@node-redis/test-utils": "*", - "@types/node": "^17.0.23", + "@types/node": "^17.0.26", "nyc": "^15.1.0", "release-it": "^14.14.2", "source-map-support": "^0.5.21", @@ -7369,19 +7335,17 @@ "requires": { "@istanbuljs/nyc-config-typescript": "^1.0.2", "@node-redis/test-utils": "*", - "@types/node": "^17.0.23", - "@types/redis-parser": "^3.0.0", + "@types/node": "^17.0.26", "@types/sinon": "^10.0.11", "@types/yallist": "^4.0.1", - "@typescript-eslint/eslint-plugin": "^5.19.0", - "@typescript-eslint/parser": "^5.19.0", + "@typescript-eslint/eslint-plugin": "^5.20.0", + "@typescript-eslint/parser": "^5.20.0", "cluster-key-slot": "1.1.0", - "eslint": "^8.13.0", + "eslint": "^8.14.0", "generic-pool": "3.8.2", "nyc": "^15.1.0", - "redis-parser": "3.0.0", "release-it": "^14.14.2", - "sinon": "^13.0.1", + "sinon": "^13.0.2", "source-map-support": "^0.5.21", "ts-node": "^10.7.0", "typedoc": "^0.22.15", @@ -7394,7 +7358,7 @@ "requires": { "@istanbuljs/nyc-config-typescript": "^1.0.2", "@node-redis/test-utils": "*", - "@types/node": "^17.0.23", + "@types/node": "^17.0.26", "nyc": "^15.1.0", "release-it": "^14.14.2", "source-map-support": "^0.5.21", @@ -7408,7 +7372,7 @@ "requires": { "@istanbuljs/nyc-config-typescript": "^1.0.2", "@node-redis/test-utils": "*", - "@types/node": "^17.0.23", + "@types/node": "^17.0.26", "nyc": "^15.1.0", "release-it": "^14.14.2", "source-map-support": "^0.5.21", @@ -7422,7 +7386,7 @@ "requires": { "@istanbuljs/nyc-config-typescript": "^1.0.2", "@node-redis/test-utils": "*", - "@types/node": "^17.0.23", + "@types/node": "^17.0.26", "nyc": "^15.1.0", "release-it": "^14.14.2", "source-map-support": "^0.5.21", @@ -7435,8 +7399,8 @@ "version": "file:packages/test-utils", "requires": { "@istanbuljs/nyc-config-typescript": "^1.0.2", - "@types/mocha": "^9.1.0", - "@types/node": "^17.0.23", + "@types/mocha": "^9.1.1", + "@types/node": "^17.0.26", "@types/yargs": "^17.0.10", "mocha": "^9.2.2", "nyc": "^15.1.0", @@ -7451,7 +7415,7 @@ "requires": { "@istanbuljs/nyc-config-typescript": "^1.0.2", "@node-redis/test-utils": "*", - "@types/node": "^17.0.23", + "@types/node": "^17.0.26", "nyc": "^15.1.0", "release-it": "^14.14.2", "source-map-support": "^0.5.21", @@ -7691,15 +7655,15 @@ "dev": true }, "@types/mocha": { - "version": "9.1.0", - "resolved": "https://registry.npmjs.org/@types/mocha/-/mocha-9.1.0.tgz", - "integrity": "sha512-QCWHkbMv4Y5U9oW10Uxbr45qMMSzl4OzijsozynUAgx3kEHUdXB00udx2dWDQ7f2TU2a2uuiFaRZjCe3unPpeg==", + "version": "9.1.1", + "resolved": "https://registry.npmjs.org/@types/mocha/-/mocha-9.1.1.tgz", + "integrity": "sha512-Z61JK7DKDtdKTWwLeElSEBcWGRLY8g95ic5FoQqI9CMx0ns/Ghep3B4DfcEimiKMvtamNVULVNKEsiwV3aQmXw==", "dev": true }, "@types/node": { - "version": "17.0.23", - "resolved": "https://registry.npmjs.org/@types/node/-/node-17.0.23.tgz", - "integrity": "sha512-UxDxWn7dl97rKVeVS61vErvw086aCYhDLyvRQZ5Rk65rZKepaFdm53GeqXaKBuOhED4e9uWq34IC3TdSdJJ2Gw==", + "version": "17.0.26", + "resolved": "https://registry.npmjs.org/@types/node/-/node-17.0.26.tgz", + "integrity": "sha512-z/FG/6DUO7pnze3AE3TBGIjGGKkvCcGcWINe1C7cADY8hKLJPDYpzsNE37uExQ4md5RFtTCvg+M8Mu1Enyeg2A==", "dev": true }, "@types/parse-json": { @@ -7708,22 +7672,6 @@ "integrity": "sha512-//oorEZjL6sbPcKUaCdIGlIUeH26mgzimjBB77G6XRgnDl/L5wOnpyBGRe/Mmf5CVW3PwEBE1NjiMZ/ssFh4wA==", "dev": true }, - "@types/redis-errors": { - "version": "1.2.1", - "resolved": "https://registry.npmjs.org/@types/redis-errors/-/redis-errors-1.2.1.tgz", - "integrity": "sha512-9p6SQPeDAIR4z3ZdrPsRH/sSRMeIA2fdRKqZ3Y1thQOLeDH4aLY+J4Ze32zjg4Dq7655Y0LonCoRrH5O7vtr4w==", - "dev": true - }, - "@types/redis-parser": { - "version": "3.0.0", - "resolved": "https://registry.npmjs.org/@types/redis-parser/-/redis-parser-3.0.0.tgz", - "integrity": "sha512-TH9NnE980dXWKjFRkck6FIv9akrD6G+vX9XXONoBgG9+NfP4iZ+SbGkaN7S15c6+JeZ+zBN62bHt2lNyUYwqoA==", - "dev": true, - "requires": { - "@types/node": "*", - "@types/redis-errors": "*" - } - }, "@types/sinon": { "version": "10.0.11", "resolved": "https://registry.npmjs.org/@types/sinon/-/sinon-10.0.11.tgz", @@ -7761,14 +7709,14 @@ "dev": true }, "@typescript-eslint/eslint-plugin": { - "version": "5.19.0", - "resolved": "https://registry.npmjs.org/@typescript-eslint/eslint-plugin/-/eslint-plugin-5.19.0.tgz", - "integrity": "sha512-w59GpFqDYGnWFim9p6TGJz7a3qWeENJuAKCqjGSx+Hq/bwq3RZwXYqy98KIfN85yDqz9mq6QXiY5h0FjGQLyEg==", + "version": "5.20.0", + "resolved": "https://registry.npmjs.org/@typescript-eslint/eslint-plugin/-/eslint-plugin-5.20.0.tgz", + "integrity": "sha512-fapGzoxilCn3sBtC6NtXZX6+P/Hef7VDbyfGqTTpzYydwhlkevB+0vE0EnmHPVTVSy68GUncyJ/2PcrFBeCo5Q==", "dev": true, "requires": { - "@typescript-eslint/scope-manager": "5.19.0", - "@typescript-eslint/type-utils": "5.19.0", - "@typescript-eslint/utils": "5.19.0", + "@typescript-eslint/scope-manager": "5.20.0", + "@typescript-eslint/type-utils": "5.20.0", + "@typescript-eslint/utils": "5.20.0", "debug": "^4.3.2", "functional-red-black-tree": "^1.0.1", "ignore": "^5.1.8", @@ -7789,52 +7737,52 @@ } }, "@typescript-eslint/parser": { - "version": "5.19.0", - "resolved": "https://registry.npmjs.org/@typescript-eslint/parser/-/parser-5.19.0.tgz", - "integrity": "sha512-yhktJjMCJX8BSBczh1F/uY8wGRYrBeyn84kH6oyqdIJwTGKmzX5Qiq49LRQ0Jh0LXnWijEziSo6BRqny8nqLVQ==", + "version": "5.20.0", + "resolved": "https://registry.npmjs.org/@typescript-eslint/parser/-/parser-5.20.0.tgz", + "integrity": "sha512-UWKibrCZQCYvobmu3/N8TWbEeo/EPQbS41Ux1F9XqPzGuV7pfg6n50ZrFo6hryynD8qOTTfLHtHjjdQtxJ0h/w==", "dev": true, "requires": { - "@typescript-eslint/scope-manager": "5.19.0", - "@typescript-eslint/types": "5.19.0", - "@typescript-eslint/typescript-estree": "5.19.0", + "@typescript-eslint/scope-manager": "5.20.0", + "@typescript-eslint/types": "5.20.0", + "@typescript-eslint/typescript-estree": "5.20.0", "debug": "^4.3.2" } }, "@typescript-eslint/scope-manager": { - "version": "5.19.0", - "resolved": "https://registry.npmjs.org/@typescript-eslint/scope-manager/-/scope-manager-5.19.0.tgz", - "integrity": "sha512-Fz+VrjLmwq5fbQn5W7cIJZ066HxLMKvDEmf4eu1tZ8O956aoX45jAuBB76miAECMTODyUxH61AQM7q4/GOMQ5g==", + "version": "5.20.0", + "resolved": "https://registry.npmjs.org/@typescript-eslint/scope-manager/-/scope-manager-5.20.0.tgz", + "integrity": "sha512-h9KtuPZ4D/JuX7rpp1iKg3zOH0WNEa+ZIXwpW/KWmEFDxlA/HSfCMhiyF1HS/drTICjIbpA6OqkAhrP/zkCStg==", "dev": true, "requires": { - "@typescript-eslint/types": "5.19.0", - "@typescript-eslint/visitor-keys": "5.19.0" + "@typescript-eslint/types": "5.20.0", + "@typescript-eslint/visitor-keys": "5.20.0" } }, "@typescript-eslint/type-utils": { - "version": "5.19.0", - "resolved": "https://registry.npmjs.org/@typescript-eslint/type-utils/-/type-utils-5.19.0.tgz", - "integrity": "sha512-O6XQ4RI4rQcBGshTQAYBUIGsKqrKeuIOz9v8bckXZnSeXjn/1+BDZndHLe10UplQeJLXDNbaZYrAytKNQO2T4Q==", + "version": "5.20.0", + "resolved": "https://registry.npmjs.org/@typescript-eslint/type-utils/-/type-utils-5.20.0.tgz", + "integrity": "sha512-WxNrCwYB3N/m8ceyoGCgbLmuZwupvzN0rE8NBuwnl7APgjv24ZJIjkNzoFBXPRCGzLNkoU/WfanW0exvp/+3Iw==", "dev": true, "requires": { - "@typescript-eslint/utils": "5.19.0", + "@typescript-eslint/utils": "5.20.0", "debug": "^4.3.2", "tsutils": "^3.21.0" } }, "@typescript-eslint/types": { - "version": "5.19.0", - "resolved": "https://registry.npmjs.org/@typescript-eslint/types/-/types-5.19.0.tgz", - "integrity": "sha512-zR1ithF4Iyq1wLwkDcT+qFnhs8L5VUtjgac212ftiOP/ZZUOCuuF2DeGiZZGQXGoHA50OreZqLH5NjDcDqn34w==", + "version": "5.20.0", + "resolved": "https://registry.npmjs.org/@typescript-eslint/types/-/types-5.20.0.tgz", + "integrity": "sha512-+d8wprF9GyvPwtoB4CxBAR/s0rpP25XKgnOvMf/gMXYDvlUC3rPFHupdTQ/ow9vn7UDe5rX02ovGYQbv/IUCbg==", "dev": true }, "@typescript-eslint/typescript-estree": { - "version": "5.19.0", - "resolved": "https://registry.npmjs.org/@typescript-eslint/typescript-estree/-/typescript-estree-5.19.0.tgz", - "integrity": "sha512-dRPuD4ocXdaE1BM/dNR21elSEUPKaWgowCA0bqJ6YbYkvtrPVEvZ+zqcX5a8ECYn3q5iBSSUcBBD42ubaOp0Hw==", + "version": "5.20.0", + "resolved": "https://registry.npmjs.org/@typescript-eslint/typescript-estree/-/typescript-estree-5.20.0.tgz", + "integrity": "sha512-36xLjP/+bXusLMrT9fMMYy1KJAGgHhlER2TqpUVDYUQg4w0q/NW/sg4UGAgVwAqb8V4zYg43KMUpM8vV2lve6w==", "dev": true, "requires": { - "@typescript-eslint/types": "5.19.0", - "@typescript-eslint/visitor-keys": "5.19.0", + "@typescript-eslint/types": "5.20.0", + "@typescript-eslint/visitor-keys": "5.20.0", "debug": "^4.3.2", "globby": "^11.0.4", "is-glob": "^4.0.3", @@ -7874,26 +7822,26 @@ } }, "@typescript-eslint/utils": { - "version": "5.19.0", - "resolved": "https://registry.npmjs.org/@typescript-eslint/utils/-/utils-5.19.0.tgz", - "integrity": "sha512-ZuEckdupXpXamKvFz/Ql8YnePh2ZWcwz7APICzJL985Rp5C2AYcHO62oJzIqNhAMtMK6XvrlBTZeNG8n7gS3lQ==", + "version": "5.20.0", + "resolved": "https://registry.npmjs.org/@typescript-eslint/utils/-/utils-5.20.0.tgz", + "integrity": "sha512-lHONGJL1LIO12Ujyx8L8xKbwWSkoUKFSO+0wDAqGXiudWB2EO7WEUT+YZLtVbmOmSllAjLb9tpoIPwpRe5Tn6w==", "dev": true, "requires": { "@types/json-schema": "^7.0.9", - "@typescript-eslint/scope-manager": "5.19.0", - "@typescript-eslint/types": "5.19.0", - "@typescript-eslint/typescript-estree": "5.19.0", + "@typescript-eslint/scope-manager": "5.20.0", + "@typescript-eslint/types": "5.20.0", + "@typescript-eslint/typescript-estree": "5.20.0", "eslint-scope": "^5.1.1", "eslint-utils": "^3.0.0" } }, "@typescript-eslint/visitor-keys": { - "version": "5.19.0", - "resolved": "https://registry.npmjs.org/@typescript-eslint/visitor-keys/-/visitor-keys-5.19.0.tgz", - "integrity": "sha512-Ym7zZoMDZcAKWsULi2s7UMLREdVQdScPQ/fKWMYefarCztWlHPFVJo8racf8R0Gc8FAEJ2eD4of8As1oFtnQlQ==", + "version": "5.20.0", + "resolved": "https://registry.npmjs.org/@typescript-eslint/visitor-keys/-/visitor-keys-5.20.0.tgz", + "integrity": "sha512-1flRpNF+0CAQkMNlTJ6L/Z5jiODG/e5+7mk6XwtPOUS3UrTz3UOiAg9jG2VtKsWI6rZQfy4C6a232QNRZTRGlg==", "dev": true, "requires": { - "@typescript-eslint/types": "5.19.0", + "@typescript-eslint/types": "5.20.0", "eslint-visitor-keys": "^3.0.0" } }, @@ -8157,15 +8105,15 @@ "dev": true }, "browserslist": { - "version": "4.20.2", - "resolved": "https://registry.npmjs.org/browserslist/-/browserslist-4.20.2.tgz", - "integrity": "sha512-CQOBCqp/9pDvDbx3xfMi+86pr4KXIf2FDkTTdeuYw8OxS9t898LA1Khq57gtufFILXpfgsSx5woNgsBgvGjpsA==", + "version": "4.20.3", + "resolved": "https://registry.npmjs.org/browserslist/-/browserslist-4.20.3.tgz", + "integrity": "sha512-NBhymBQl1zM0Y5dQT/O+xiLP9/rzOIQdKM/eMJBAq7yBgaB6krIYLGejrwVYnSHZdqjscB1SPuAjHwxjvN6Wdg==", "dev": true, "requires": { - "caniuse-lite": "^1.0.30001317", - "electron-to-chromium": "^1.4.84", + "caniuse-lite": "^1.0.30001332", + "electron-to-chromium": "^1.4.118", "escalade": "^3.1.1", - "node-releases": "^2.0.2", + "node-releases": "^2.0.3", "picocolors": "^1.0.0" } }, @@ -8252,9 +8200,9 @@ "dev": true }, "caniuse-lite": { - "version": "1.0.30001331", - "resolved": "https://registry.npmjs.org/caniuse-lite/-/caniuse-lite-1.0.30001331.tgz", - "integrity": "sha512-Y1xk6paHpUXKP/P6YjQv1xqyTbgAP05ycHBcRdQjTcyXlWol868sJJPlmk5ylOekw2BrucWes5jk+LvVd7WZ5Q==", + "version": "1.0.30001332", + "resolved": "https://registry.npmjs.org/caniuse-lite/-/caniuse-lite-1.0.30001332.tgz", + "integrity": "sha512-10T30NYOEQtN6C11YGg411yebhvpnC6Z102+B95eAsN0oB6KUs01ivE8u+G6FMIRtIrVlYXhL+LUwQ3/hXwDWw==", "dev": true }, "chalk": { @@ -8395,16 +8343,6 @@ "integrity": "sha1-3dgA2gxmEnOTzKWVDqloo6rxJTs=", "dev": true }, - "compress-brotli": { - "version": "1.3.6", - "resolved": "https://registry.npmjs.org/compress-brotli/-/compress-brotli-1.3.6.tgz", - "integrity": "sha512-au99/GqZtUtiCBliqLFbWlhnCxn+XSYjwZ77q6mKN4La4qOXDoLVPZ50iXr0WmAyMxl8yqoq3Yq4OeQNPPkyeQ==", - "dev": true, - "requires": { - "@types/json-buffer": "~3.0.0", - "json-buffer": "~3.0.1" - } - }, "concat-map": { "version": "0.0.1", "resolved": "https://registry.npmjs.org/concat-map/-/concat-map-0.0.1.tgz", @@ -8545,12 +8483,13 @@ "dev": true }, "define-properties": { - "version": "1.1.3", - "resolved": "https://registry.npmjs.org/define-properties/-/define-properties-1.1.3.tgz", - "integrity": "sha512-3MqfYKj2lLzdMSf8ZIZE/V+Zuy+BgD6f164e8K2w7dgnpKArBDerGYpM46IYYcjnkdPNMjPk9A6VFB8+3SKlXQ==", + "version": "1.1.4", + "resolved": "https://registry.npmjs.org/define-properties/-/define-properties-1.1.4.tgz", + "integrity": "sha512-uckOqKcfaVvtBdsVkdPv3XjveQJsNQqmhXgRi8uhvWWuPYZCNlzT8qAyblUgNoXdHdjMTzAqeGjAoli8f+bzPA==", "dev": true, "requires": { - "object-keys": "^1.0.12" + "has-property-descriptors": "^1.0.0", + "object-keys": "^1.1.1" } }, "delayed-stream": { @@ -8605,9 +8544,9 @@ "dev": true }, "electron-to-chromium": { - "version": "1.4.107", - "resolved": "https://registry.npmjs.org/electron-to-chromium/-/electron-to-chromium-1.4.107.tgz", - "integrity": "sha512-Huen6taaVrUrSy8o7mGStByba8PfOWWluHNxSHGBrCgEdFVLtvdQDBr9LBCF9Uci8SYxh28QNNMO0oC17wbGAg==", + "version": "1.4.118", + "resolved": "https://registry.npmjs.org/electron-to-chromium/-/electron-to-chromium-1.4.118.tgz", + "integrity": "sha512-maZIKjnYDvF7Fs35nvVcyr44UcKNwybr93Oba2n3HkKDFAtk0svERkLN/HyczJDS3Fo4wU9th9fUQd09ZLtj1w==", "dev": true }, "email-addresses": { @@ -8641,9 +8580,9 @@ } }, "es-abstract": { - "version": "1.19.4", - "resolved": "https://registry.npmjs.org/es-abstract/-/es-abstract-1.19.4.tgz", - "integrity": "sha512-flV8e5g9/xulChMG48Fygk1ptpo4lQRJ0eJYtxJFgi7pklLx7EFcOJ34jnvr8pbWlaFN/AT1cZpe0hiFel9Hqg==", + "version": "1.19.5", + "resolved": "https://registry.npmjs.org/es-abstract/-/es-abstract-1.19.5.tgz", + "integrity": "sha512-Aa2G2+Rd3b6kxEUKTF4TaW67czBLyAv3z7VOhYRU50YBx+bbsYZ9xQP4lMNazePuFlybXI0V4MruPos7qUo5fA==", "dev": true, "requires": { "call-bind": "^1.0.2", @@ -8726,12 +8665,12 @@ "dev": true }, "eslint": { - "version": "8.13.0", - "resolved": "https://registry.npmjs.org/eslint/-/eslint-8.13.0.tgz", - "integrity": "sha512-D+Xei61eInqauAyTJ6C0q6x9mx7kTUC1KZ0m0LSEexR0V+e94K12LmWX076ZIsldwfQ2RONdaJe0re0TRGQbRQ==", + "version": "8.14.0", + "resolved": "https://registry.npmjs.org/eslint/-/eslint-8.14.0.tgz", + "integrity": "sha512-3/CE4aJX7LNEiE3i6FeodHmI/38GZtWCsAtsymScmzYapx8q1nVVb+eLcLSzATmCPXw5pT4TqVs1E0OmxAd9tw==", "dev": true, "requires": { - "@eslint/eslintrc": "^1.2.1", + "@eslint/eslintrc": "^1.2.2", "@humanwhocodes/config-array": "^0.9.2", "ajv": "^6.10.0", "chalk": "^4.0.0", @@ -9325,9 +9264,9 @@ } }, "has-bigints": { - "version": "1.0.1", - "resolved": "https://registry.npmjs.org/has-bigints/-/has-bigints-1.0.1.tgz", - "integrity": "sha512-LSBS2LjbNBTf6287JEbEzvJgftkF5qFkmCo9hDRpAzKhUOlJ+hx8dd4USs00SgsUNwc4617J9ki5YtEClM2ffA==", + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/has-bigints/-/has-bigints-1.0.2.tgz", + "integrity": "sha512-tSvCKtBr9lkF0Ex0aQiP9N+OpV4zi2r/Nee5VkRDbaqv35RLYMzbwQfFSZZH0kR+Rd6302UJZ2p/bJCEoR3VoQ==", "dev": true }, "has-flag": { @@ -9336,6 +9275,15 @@ "integrity": "sha512-EykJT/Q1KjTWctppgIAgfSO0tKVuZUjhgMr17kqTumMl6Afv3EISleU7qZUzoXDFTAHTDC4NOoG/ZxU3EvlMPQ==", "dev": true }, + "has-property-descriptors": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/has-property-descriptors/-/has-property-descriptors-1.0.0.tgz", + "integrity": "sha512-62DVLZGoiEBDHQyqG4w9xCuZ7eJEwNmJRWw2VY84Oedb7WFcA27fiEVe8oUQx9hAUJ4ekurquucTGwsyO1XGdQ==", + "dev": true, + "requires": { + "get-intrinsic": "^1.1.1" + } + }, "has-symbols": { "version": "1.0.3", "resolved": "https://registry.npmjs.org/has-symbols/-/has-symbols-1.0.3.tgz", @@ -9497,9 +9445,9 @@ "dev": true }, "inquirer": { - "version": "8.2.2", - "resolved": "https://registry.npmjs.org/inquirer/-/inquirer-8.2.2.tgz", - "integrity": "sha512-pG7I/si6K/0X7p1qU+rfWnpTE1UIkTONN1wxtzh0d+dHXtT/JG6qBgLxoyHVsQa8cFABxAPh0pD6uUUHiAoaow==", + "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", @@ -9512,7 +9460,7 @@ "mute-stream": "0.0.8", "ora": "^5.4.1", "run-async": "^2.4.0", - "rxjs": "^7.5.5", + "rxjs": "^7.2.0", "string-width": "^4.1.0", "strip-ansi": "^6.0.0", "through": "^2.3.6" @@ -9595,9 +9543,9 @@ } }, "is-core-module": { - "version": "2.8.1", - "resolved": "https://registry.npmjs.org/is-core-module/-/is-core-module-2.8.1.tgz", - "integrity": "sha512-SdNCUs284hr40hFTFP6l0IfZ/RSrMXF3qgoRHd3/79unUTvrFO/JoXwkGm+5J/Oe3E/b5GsnG330uUNgRpu1PA==", + "version": "2.9.0", + "resolved": "https://registry.npmjs.org/is-core-module/-/is-core-module-2.9.0.tgz", + "integrity": "sha512-+5FPy5PnwmO3lvfMb0AsoPaBG+5KHUI0wYFXOtYPnVVVspTFUuMZNfNaNVRt3FZadstu2c8x23vykRW/NBoU6A==", "dev": true, "requires": { "has": "^1.0.3" @@ -11002,19 +10950,6 @@ "resolve": "^1.1.6" } }, - "redis-errors": { - "version": "1.2.0", - "resolved": "https://registry.npmjs.org/redis-errors/-/redis-errors-1.2.0.tgz", - "integrity": "sha1-62LSrbFeTq9GEMBK/hUpOEJQq60=" - }, - "redis-parser": { - "version": "3.0.0", - "resolved": "https://registry.npmjs.org/redis-parser/-/redis-parser-3.0.0.tgz", - "integrity": "sha1-tm2CjNyv5rS4pCin3vTGvKwxyLQ=", - "requires": { - "redis-errors": "^1.0.0" - } - }, "regexpp": { "version": "3.2.0", "resolved": "https://registry.npmjs.org/regexpp/-/regexpp-3.2.0.tgz", @@ -11057,7 +10992,7 @@ "globby": "11.0.4", "got": "9.6.0", "import-cwd": "3.0.0", - "inquirer": "8.2.2", + "inquirer": "8.2.0", "is-ci": "3.0.1", "lodash": "4.17.21", "mime-types": "2.1.35", @@ -11307,13 +11242,13 @@ "dev": true }, "sinon": { - "version": "13.0.1", - "resolved": "https://registry.npmjs.org/sinon/-/sinon-13.0.1.tgz", - "integrity": "sha512-8yx2wIvkBjIq/MGY1D9h1LMraYW+z1X0mb648KZnKSdvLasvDu7maa0dFaNYdTDczFgbjNw2tOmWdTk9saVfwQ==", + "version": "13.0.2", + "resolved": "https://registry.npmjs.org/sinon/-/sinon-13.0.2.tgz", + "integrity": "sha512-KvOrztAVqzSJWMDoxM4vM+GPys1df2VBoXm+YciyB/OLMamfS3VXh3oGh5WtrAGSzrgczNWFFY22oKb7Fi5eeA==", "dev": true, "requires": { "@sinonjs/commons": "^1.8.3", - "@sinonjs/fake-timers": "^9.0.0", + "@sinonjs/fake-timers": "^9.1.2", "@sinonjs/samsam": "^6.1.1", "diff": "^5.0.0", "nise": "^5.1.1", @@ -11571,9 +11506,9 @@ } }, "tslib": { - "version": "2.3.1", - "resolved": "https://registry.npmjs.org/tslib/-/tslib-2.3.1.tgz", - "integrity": "sha512-77EbyPPpMz+FRFRuAFlWMtmgUWGe9UOG2Z25NqCwiIjRhOf5iKGuzSe5P2w1laq+FkRy4p+PCuVkJSGkzTEKVw==", + "version": "2.4.0", + "resolved": "https://registry.npmjs.org/tslib/-/tslib-2.4.0.tgz", + "integrity": "sha512-d6xOpEDfsi2CZVlPQzGeux8XMwLT9hssAsaPYExaQMuYskwb+x1x7J371tWlbBdWHroy99KnVB6qIkUbs5X3UQ==", "dev": true }, "tsutils": { @@ -11663,14 +11598,14 @@ "dev": true }, "unbox-primitive": { - "version": "1.0.1", - "resolved": "https://registry.npmjs.org/unbox-primitive/-/unbox-primitive-1.0.1.tgz", - "integrity": "sha512-tZU/3NqK3dA5gpE1KtyiJUrEB0lxnGkMFHptJ7q6ewdZ8s12QrODwNbhIJStmJkd1QDXa1NRA8aF2A1zk/Ypyw==", + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/unbox-primitive/-/unbox-primitive-1.0.2.tgz", + "integrity": "sha512-61pPlCD9h51VoreyJ0BReideM3MDKMKnh6+V9L08331ipq6Q8OFXZYiqP6n/tbHx4s5I9uRhcye6BrbkizkBDw==", "dev": true, "requires": { - "function-bind": "^1.1.1", - "has-bigints": "^1.0.1", - "has-symbols": "^1.0.2", + "call-bind": "^1.0.2", + "has-bigints": "^1.0.2", + "has-symbols": "^1.0.3", "which-boxed-primitive": "^1.0.2" } }, @@ -11786,9 +11721,9 @@ "dev": true }, "v8-compile-cache-lib": { - "version": "3.0.0", - "resolved": "https://registry.npmjs.org/v8-compile-cache-lib/-/v8-compile-cache-lib-3.0.0.tgz", - "integrity": "sha512-mpSYqfsFvASnSn5qMiwrr4VKfumbPyONLCOPmsR3A6pTY/r0+tSaVbgPWSAIuzbk3lCTa+FForeTiO+wBQGkjA==", + "version": "3.0.1", + "resolved": "https://registry.npmjs.org/v8-compile-cache-lib/-/v8-compile-cache-lib-3.0.1.tgz", + "integrity": "sha512-wa7YjyUGfNZngI/vtK0UHAN+lgDCxBPCylVXGp0zu59Fz5aiGtNXaq3DhIov063MorB+VfufLh3JlF2KdTK3xg==", "dev": true }, "vscode-oniguruma": { diff --git a/packages/bloom/package.json b/packages/bloom/package.json index 17d6591b5cd..8a2ea2858a3 100644 --- a/packages/bloom/package.json +++ b/packages/bloom/package.json @@ -18,7 +18,7 @@ "devDependencies": { "@istanbuljs/nyc-config-typescript": "^1.0.2", "@node-redis/test-utils": "*", - "@types/node": "^17.0.23", + "@types/node": "^17.0.26", "nyc": "^15.1.0", "release-it": "^14.14.2", "source-map-support": "^0.5.21", diff --git a/packages/client/package.json b/packages/client/package.json index c56d8d91fd8..9d70d4f93e1 100644 --- a/packages/client/package.json +++ b/packages/client/package.json @@ -21,15 +21,15 @@ "devDependencies": { "@istanbuljs/nyc-config-typescript": "^1.0.2", "@node-redis/test-utils": "*", - "@types/node": "^17.0.23", + "@types/node": "^17.0.26", "@types/sinon": "^10.0.11", "@types/yallist": "^4.0.1", - "@typescript-eslint/eslint-plugin": "^5.19.0", - "@typescript-eslint/parser": "^5.19.0", - "eslint": "^8.13.0", + "@typescript-eslint/eslint-plugin": "^5.20.0", + "@typescript-eslint/parser": "^5.20.0", + "eslint": "^8.14.0", "nyc": "^15.1.0", "release-it": "^14.14.2", - "sinon": "^13.0.1", + "sinon": "^13.0.2", "source-map-support": "^0.5.21", "ts-node": "^10.7.0", "typedoc": "^0.22.15", diff --git a/packages/graph/package.json b/packages/graph/package.json index 75c24346ce5..dd0db983c24 100644 --- a/packages/graph/package.json +++ b/packages/graph/package.json @@ -18,7 +18,7 @@ "devDependencies": { "@istanbuljs/nyc-config-typescript": "^1.0.2", "@node-redis/test-utils": "*", - "@types/node": "^17.0.23", + "@types/node": "^17.0.26", "nyc": "^15.1.0", "release-it": "^14.14.2", "source-map-support": "^0.5.21", diff --git a/packages/json/package.json b/packages/json/package.json index 472cbfd5eaa..b53a9823737 100644 --- a/packages/json/package.json +++ b/packages/json/package.json @@ -18,7 +18,7 @@ "devDependencies": { "@istanbuljs/nyc-config-typescript": "^1.0.2", "@node-redis/test-utils": "*", - "@types/node": "^17.0.23", + "@types/node": "^17.0.26", "nyc": "^15.1.0", "release-it": "^14.14.2", "source-map-support": "^0.5.21", diff --git a/packages/search/package.json b/packages/search/package.json index fcc1f41b369..bba1382d1ab 100644 --- a/packages/search/package.json +++ b/packages/search/package.json @@ -18,7 +18,7 @@ "devDependencies": { "@istanbuljs/nyc-config-typescript": "^1.0.2", "@node-redis/test-utils": "*", - "@types/node": "^17.0.23", + "@types/node": "^17.0.26", "nyc": "^15.1.0", "release-it": "^14.14.2", "source-map-support": "^0.5.21", diff --git a/packages/test-utils/package.json b/packages/test-utils/package.json index ad4d8e592a9..e1c340ce2d5 100644 --- a/packages/test-utils/package.json +++ b/packages/test-utils/package.json @@ -11,8 +11,8 @@ }, "devDependencies": { "@istanbuljs/nyc-config-typescript": "^1.0.2", - "@types/mocha": "^9.1.0", - "@types/node": "^17.0.23", + "@types/mocha": "^9.1.1", + "@types/node": "^17.0.26", "@types/yargs": "^17.0.10", "mocha": "^9.2.2", "nyc": "^15.1.0", diff --git a/packages/time-series/package.json b/packages/time-series/package.json index 866296044e9..71adbea399c 100644 --- a/packages/time-series/package.json +++ b/packages/time-series/package.json @@ -18,7 +18,7 @@ "devDependencies": { "@istanbuljs/nyc-config-typescript": "^1.0.2", "@node-redis/test-utils": "*", - "@types/node": "^17.0.23", + "@types/node": "^17.0.26", "nyc": "^15.1.0", "release-it": "^14.14.2", "source-map-support": "^0.5.21", From 1e51680205f45fca93924c30d91a27e820c746ef Mon Sep 17 00:00:00 2001 From: Mikael Finstad Date: Mon, 25 Apr 2022 13:54:59 -0700 Subject: [PATCH 1196/1748] simplify example (#2072) top level await is supported in modules --- README.md | 12 +++++------- 1 file changed, 5 insertions(+), 7 deletions(-) diff --git a/README.md b/README.md index 974407fce48..ab949a199b3 100644 --- a/README.md +++ b/README.md @@ -36,16 +36,14 @@ npm install redis ```typescript import { createClient } from 'redis'; -(async () => { - const client = createClient(); +const client = createClient(); - client.on('error', (err) => console.log('Redis Client Error', err)); +client.on('error', (err) => console.log('Redis Client Error', err)); - await client.connect(); +await client.connect(); - await client.set('key', 'value'); - const value = await client.get('key'); -})(); +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]`: From b586ccb9d7def085369e406c808a6577b83fd15f Mon Sep 17 00:00:00 2001 From: Leibale Eidelman Date: Tue, 26 Apr 2022 09:04:21 -0400 Subject: [PATCH 1197/1748] fix #1904 - ACL DRYRUN (#2102) * fix #1904 - ACL DRYRUN * clean code * fix test --- packages/client/lib/client/commands.ts | 3 +++ .../client/lib/commands/ACL_DRYRUN.spec.ts | 21 +++++++++++++++++++ packages/client/lib/commands/ACL_DRYRUN.ts | 18 ++++++++++++++++ packages/client/lib/commands/BLMPOP.spec.ts | 2 +- packages/client/lib/commands/BZMPOP.spec.ts | 2 +- .../client/lib/commands/CLUSTER_LINKS.spec.ts | 2 +- .../commands/COMMAND_GETKEYSANDFLAGS.spec.ts | 2 +- .../client/lib/commands/COMMAND_LIST.spec.ts | 2 +- .../client/lib/commands/EVALSHA_RO.spec.ts | 2 +- packages/client/lib/commands/EVAL_RO.spec.ts | 2 +- .../client/lib/commands/EXPIRETIME.spec.ts | 4 ++-- packages/client/lib/commands/FCALL.spec.ts | 2 +- packages/client/lib/commands/FCALL_RO.spec.ts | 2 +- .../lib/commands/FUNCTION_DELETE.spec.ts | 2 +- .../client/lib/commands/FUNCTION_DUMP.spec.ts | 2 +- .../lib/commands/FUNCTION_FLUSH.spec.ts | 2 +- .../client/lib/commands/FUNCTION_KILL.spec.ts | 2 +- .../client/lib/commands/FUNCTION_LIST.spec.ts | 2 +- .../commands/FUNCTION_LIST_WITHCODE.spec.ts | 2 +- .../client/lib/commands/FUNCTION_LOAD.spec.ts | 2 +- .../lib/commands/FUNCTION_RESTORE.spec.ts | 2 +- .../lib/commands/FUNCTION_STATS.spec.ts | 2 +- packages/client/lib/commands/LMPOP.spec.ts | 2 +- .../client/lib/commands/PEXPIRETIME.spec.ts | 2 +- .../client/lib/commands/SINTERCARD.spec.ts | 2 +- packages/client/lib/commands/SORT_RO.spec.ts | 2 +- .../client/lib/commands/ZINTERCARD.spec.ts | 2 +- packages/client/lib/commands/ZMPOP.spec.ts | 2 +- 28 files changed, 68 insertions(+), 26 deletions(-) create mode 100644 packages/client/lib/commands/ACL_DRYRUN.spec.ts create mode 100644 packages/client/lib/commands/ACL_DRYRUN.ts diff --git a/packages/client/lib/client/commands.ts b/packages/client/lib/client/commands.ts index 94653e476b1..84728ec76c6 100644 --- a/packages/client/lib/client/commands.ts +++ b/packages/client/lib/client/commands.ts @@ -1,6 +1,7 @@ import CLUSTER_COMMANDS from '../cluster/commands'; import * as ACL_CAT from '../commands/ACL_CAT'; import * as ACL_DELUSER from '../commands/ACL_DELUSER'; +import * as ACL_DRYRUN from '../commands/ACL_DRYRUN'; import * as ACL_GENPASS from '../commands/ACL_GENPASS'; import * as ACL_GETUSER from '../commands/ACL_GETUSER'; import * as ACL_LIST from '../commands/ACL_LIST'; @@ -115,6 +116,8 @@ export default { aclCat: ACL_CAT, ACL_DELUSER, aclDelUser: ACL_DELUSER, + ACL_DRYRUN, + aclDryRun: ACL_DRYRUN, ACL_GENPASS, aclGenPass: ACL_GENPASS, ACL_GETUSER, diff --git a/packages/client/lib/commands/ACL_DRYRUN.spec.ts b/packages/client/lib/commands/ACL_DRYRUN.spec.ts new file mode 100644 index 00000000000..3154689c29e --- /dev/null +++ b/packages/client/lib/commands/ACL_DRYRUN.spec.ts @@ -0,0 +1,21 @@ +import { strict as assert } from 'assert'; +import testUtils, { GLOBAL } from '../test-utils'; +import { transformArguments } from './ACL_DRYRUN'; + +describe('ACL DRYRUN', () => { + testUtils.isVersionGreaterThanHook([7]); + + it('transformArguments', () => { + assert.deepEqual( + transformArguments('default', ['GET', 'key']), + ['ACL', 'DRYRUN', 'default', 'GET', 'key'] + ); + }); + + testUtils.testWithClient('client.aclDryRun', async client => { + assert.equal( + await client.aclDryRun('default', ['GET', 'key']), + 'OK' + ); + }, GLOBAL.SERVERS.OPEN); +}); diff --git a/packages/client/lib/commands/ACL_DRYRUN.ts b/packages/client/lib/commands/ACL_DRYRUN.ts new file mode 100644 index 00000000000..95eed95066c --- /dev/null +++ b/packages/client/lib/commands/ACL_DRYRUN.ts @@ -0,0 +1,18 @@ +import { RedisCommandArgument, RedisCommandArguments } from '.'; + +export const IS_READ_ONLY = true; + +export function transformArguments( + username: RedisCommandArgument, + command: Array +): RedisCommandArguments { + return [ + 'ACL', + 'DRYRUN', + username, + ...command + ]; +} + +export declare function transformReply(): RedisCommandArgument; + diff --git a/packages/client/lib/commands/BLMPOP.spec.ts b/packages/client/lib/commands/BLMPOP.spec.ts index 9a4a6c96a26..15853a771b0 100644 --- a/packages/client/lib/commands/BLMPOP.spec.ts +++ b/packages/client/lib/commands/BLMPOP.spec.ts @@ -3,7 +3,7 @@ import testUtils, { GLOBAL } from '../test-utils'; import { transformArguments } from './BLMPOP'; describe('BLMPOP', () => { - testUtils.isVersionGreaterThanHook([7, 0]); + testUtils.isVersionGreaterThanHook([7]); describe('transformArguments', () => { it('simple', () => { diff --git a/packages/client/lib/commands/BZMPOP.spec.ts b/packages/client/lib/commands/BZMPOP.spec.ts index b35d971f0a5..0e381c114f2 100644 --- a/packages/client/lib/commands/BZMPOP.spec.ts +++ b/packages/client/lib/commands/BZMPOP.spec.ts @@ -3,7 +3,7 @@ import testUtils, { GLOBAL } from '../test-utils'; import { transformArguments } from './BZMPOP'; describe('BZMPOP', () => { - testUtils.isVersionGreaterThanHook([7, 0]); + testUtils.isVersionGreaterThanHook([7]); describe('transformArguments', () => { it('simple', () => { diff --git a/packages/client/lib/commands/CLUSTER_LINKS.spec.ts b/packages/client/lib/commands/CLUSTER_LINKS.spec.ts index 242e9012fbc..a8b1663c3af 100644 --- a/packages/client/lib/commands/CLUSTER_LINKS.spec.ts +++ b/packages/client/lib/commands/CLUSTER_LINKS.spec.ts @@ -3,7 +3,7 @@ import testUtils, { GLOBAL } from '../test-utils'; import { transformArguments } from './CLUSTER_LINKS'; describe('CLUSTER LINKS', () => { - testUtils.isVersionGreaterThanHook([7, 0]); + testUtils.isVersionGreaterThanHook([7]); it('transformArguments', () => { assert.deepEqual( diff --git a/packages/client/lib/commands/COMMAND_GETKEYSANDFLAGS.spec.ts b/packages/client/lib/commands/COMMAND_GETKEYSANDFLAGS.spec.ts index 19bc27d5f99..d568ed0e508 100644 --- a/packages/client/lib/commands/COMMAND_GETKEYSANDFLAGS.spec.ts +++ b/packages/client/lib/commands/COMMAND_GETKEYSANDFLAGS.spec.ts @@ -3,7 +3,7 @@ import testUtils, { GLOBAL } from '../test-utils'; import { transformArguments } from './COMMAND_GETKEYSANDFLAGS'; describe('COMMAND GETKEYSANDFLAGS', () => { - testUtils.isVersionGreaterThanHook([7, 0]); + testUtils.isVersionGreaterThanHook([7]); it('transformArguments', () => { assert.deepEqual( diff --git a/packages/client/lib/commands/COMMAND_LIST.spec.ts b/packages/client/lib/commands/COMMAND_LIST.spec.ts index ac4fee20c71..eef747d9378 100644 --- a/packages/client/lib/commands/COMMAND_LIST.spec.ts +++ b/packages/client/lib/commands/COMMAND_LIST.spec.ts @@ -3,7 +3,7 @@ import testUtils, { GLOBAL } from '../test-utils'; import { transformArguments, FilterBy } from './COMMAND_LIST'; describe('COMMAND LIST', () => { - testUtils.isVersionGreaterThanHook([7, 0]); + testUtils.isVersionGreaterThanHook([7]); describe('transformArguments', () => { it('simple', () => { diff --git a/packages/client/lib/commands/EVALSHA_RO.spec.ts b/packages/client/lib/commands/EVALSHA_RO.spec.ts index 6711f24fd80..939a4a209cb 100644 --- a/packages/client/lib/commands/EVALSHA_RO.spec.ts +++ b/packages/client/lib/commands/EVALSHA_RO.spec.ts @@ -3,7 +3,7 @@ import testUtils from '../test-utils'; import { transformArguments } from './EVALSHA_RO'; describe('EVALSHA_RO', () => { - testUtils.isVersionGreaterThanHook([7, 0]); + testUtils.isVersionGreaterThanHook([7]); it('transformArguments', () => { assert.deepEqual( diff --git a/packages/client/lib/commands/EVAL_RO.spec.ts b/packages/client/lib/commands/EVAL_RO.spec.ts index e39fe82dd33..f71d0b2b24a 100644 --- a/packages/client/lib/commands/EVAL_RO.spec.ts +++ b/packages/client/lib/commands/EVAL_RO.spec.ts @@ -3,7 +3,7 @@ import testUtils, { GLOBAL } from '../test-utils'; import { transformArguments } from './EVAL_RO'; describe('EVAL_RO', () => { - testUtils.isVersionGreaterThanHook([7, 0]); + testUtils.isVersionGreaterThanHook([7]); it('transformArguments', () => { assert.deepEqual( diff --git a/packages/client/lib/commands/EXPIRETIME.spec.ts b/packages/client/lib/commands/EXPIRETIME.spec.ts index 3f33693d89c..1d63e759a5d 100644 --- a/packages/client/lib/commands/EXPIRETIME.spec.ts +++ b/packages/client/lib/commands/EXPIRETIME.spec.ts @@ -3,8 +3,8 @@ import testUtils, { GLOBAL } from '../test-utils'; import { transformArguments } from './EXPIRETIME'; describe('EXPIRETIME', () => { - testUtils.isVersionGreaterThanHook([7, 0]); - + testUtils.isVersionGreaterThanHook([7]); + it('transformArguments', () => { assert.deepEqual( transformArguments('key'), diff --git a/packages/client/lib/commands/FCALL.spec.ts b/packages/client/lib/commands/FCALL.spec.ts index 25fbd51399f..fd29f07527d 100644 --- a/packages/client/lib/commands/FCALL.spec.ts +++ b/packages/client/lib/commands/FCALL.spec.ts @@ -4,7 +4,7 @@ import { MATH_FUNCTION, loadMathFunction } from '../client/index.spec'; import { transformArguments } from './FCALL'; describe('FCALL', () => { - testUtils.isVersionGreaterThanHook([7, 0]); + testUtils.isVersionGreaterThanHook([7]); it('transformArguments', () => { assert.deepEqual( diff --git a/packages/client/lib/commands/FCALL_RO.spec.ts b/packages/client/lib/commands/FCALL_RO.spec.ts index 44332b9426c..18665f92aa6 100644 --- a/packages/client/lib/commands/FCALL_RO.spec.ts +++ b/packages/client/lib/commands/FCALL_RO.spec.ts @@ -4,7 +4,7 @@ import { MATH_FUNCTION, loadMathFunction } from '../client/index.spec'; import { transformArguments } from './FCALL_RO'; describe('FCALL_RO', () => { - testUtils.isVersionGreaterThanHook([7, 0]); + testUtils.isVersionGreaterThanHook([7]); it('transformArguments', () => { assert.deepEqual( diff --git a/packages/client/lib/commands/FUNCTION_DELETE.spec.ts b/packages/client/lib/commands/FUNCTION_DELETE.spec.ts index fd7dca2b072..563b9aa0a58 100644 --- a/packages/client/lib/commands/FUNCTION_DELETE.spec.ts +++ b/packages/client/lib/commands/FUNCTION_DELETE.spec.ts @@ -4,7 +4,7 @@ import testUtils, { GLOBAL } from '../test-utils'; import { transformArguments } from './FUNCTION_DELETE'; describe('FUNCTION DELETE', () => { - testUtils.isVersionGreaterThanHook([7, 0]); + testUtils.isVersionGreaterThanHook([7]); it('transformArguments', () => { assert.deepEqual( diff --git a/packages/client/lib/commands/FUNCTION_DUMP.spec.ts b/packages/client/lib/commands/FUNCTION_DUMP.spec.ts index e3befa3dc54..360ec6b7453 100644 --- a/packages/client/lib/commands/FUNCTION_DUMP.spec.ts +++ b/packages/client/lib/commands/FUNCTION_DUMP.spec.ts @@ -3,7 +3,7 @@ import testUtils, { GLOBAL } from '../test-utils'; import { transformArguments } from './FUNCTION_DUMP'; describe('FUNCTION DUMP', () => { - testUtils.isVersionGreaterThanHook([7, 0]); + testUtils.isVersionGreaterThanHook([7]); it('transformArguments', () => { assert.deepEqual( diff --git a/packages/client/lib/commands/FUNCTION_FLUSH.spec.ts b/packages/client/lib/commands/FUNCTION_FLUSH.spec.ts index 8447216baa8..12009d03363 100644 --- a/packages/client/lib/commands/FUNCTION_FLUSH.spec.ts +++ b/packages/client/lib/commands/FUNCTION_FLUSH.spec.ts @@ -3,7 +3,7 @@ import testUtils, { GLOBAL } from '../test-utils'; import { transformArguments } from './FUNCTION_FLUSH'; describe('FUNCTION FLUSH', () => { - testUtils.isVersionGreaterThanHook([7, 0]); + testUtils.isVersionGreaterThanHook([7]); describe('transformArguments', () => { it('simple', () => { diff --git a/packages/client/lib/commands/FUNCTION_KILL.spec.ts b/packages/client/lib/commands/FUNCTION_KILL.spec.ts index cf489a80229..df4848fc82e 100644 --- a/packages/client/lib/commands/FUNCTION_KILL.spec.ts +++ b/packages/client/lib/commands/FUNCTION_KILL.spec.ts @@ -3,7 +3,7 @@ import testUtils from '../test-utils'; import { transformArguments } from './FUNCTION_KILL'; describe('FUNCTION KILL', () => { - testUtils.isVersionGreaterThanHook([7, 0]); + testUtils.isVersionGreaterThanHook([7]); it('transformArguments', () => { assert.deepEqual( diff --git a/packages/client/lib/commands/FUNCTION_LIST.spec.ts b/packages/client/lib/commands/FUNCTION_LIST.spec.ts index 41de2f40f40..80723d070de 100644 --- a/packages/client/lib/commands/FUNCTION_LIST.spec.ts +++ b/packages/client/lib/commands/FUNCTION_LIST.spec.ts @@ -4,7 +4,7 @@ import { MATH_FUNCTION, loadMathFunction } from '../client/index.spec'; import { transformArguments } from './FUNCTION_LIST'; describe('FUNCTION LIST', () => { - testUtils.isVersionGreaterThanHook([7, 0]); + testUtils.isVersionGreaterThanHook([7]); describe('transformArguments', () => { it('simple', () => { diff --git a/packages/client/lib/commands/FUNCTION_LIST_WITHCODE.spec.ts b/packages/client/lib/commands/FUNCTION_LIST_WITHCODE.spec.ts index 33c2ca6d915..56e6102a4b4 100644 --- a/packages/client/lib/commands/FUNCTION_LIST_WITHCODE.spec.ts +++ b/packages/client/lib/commands/FUNCTION_LIST_WITHCODE.spec.ts @@ -4,7 +4,7 @@ import { MATH_FUNCTION, loadMathFunction } from '../client/index.spec'; import { transformArguments } from './FUNCTION_LIST_WITHCODE'; describe('FUNCTION LIST WITHCODE', () => { - testUtils.isVersionGreaterThanHook([7, 0]); + testUtils.isVersionGreaterThanHook([7]); describe('transformArguments', () => { it('simple', () => { diff --git a/packages/client/lib/commands/FUNCTION_LOAD.spec.ts b/packages/client/lib/commands/FUNCTION_LOAD.spec.ts index 6beac45d0aa..7be371c6b9c 100644 --- a/packages/client/lib/commands/FUNCTION_LOAD.spec.ts +++ b/packages/client/lib/commands/FUNCTION_LOAD.spec.ts @@ -4,7 +4,7 @@ import { MATH_FUNCTION } from '../client/index.spec'; import { transformArguments } from './FUNCTION_LOAD'; describe('FUNCTION LOAD', () => { - testUtils.isVersionGreaterThanHook([7, 0]); + testUtils.isVersionGreaterThanHook([7]); describe('transformArguments', () => { it('simple', () => { diff --git a/packages/client/lib/commands/FUNCTION_RESTORE.spec.ts b/packages/client/lib/commands/FUNCTION_RESTORE.spec.ts index 3ed4c2b3298..a5c2e2dcc72 100644 --- a/packages/client/lib/commands/FUNCTION_RESTORE.spec.ts +++ b/packages/client/lib/commands/FUNCTION_RESTORE.spec.ts @@ -3,7 +3,7 @@ import testUtils, { GLOBAL } from '../test-utils'; import { transformArguments } from './FUNCTION_RESTORE'; describe('FUNCTION RESTORE', () => { - testUtils.isVersionGreaterThanHook([7, 0]); + testUtils.isVersionGreaterThanHook([7]); describe('transformArguments', () => { it('simple', () => { diff --git a/packages/client/lib/commands/FUNCTION_STATS.spec.ts b/packages/client/lib/commands/FUNCTION_STATS.spec.ts index c9f648fa5e0..a5e26b5fecc 100644 --- a/packages/client/lib/commands/FUNCTION_STATS.spec.ts +++ b/packages/client/lib/commands/FUNCTION_STATS.spec.ts @@ -3,7 +3,7 @@ import testUtils, { GLOBAL } from '../test-utils'; import { transformArguments } from './FUNCTION_STATS'; describe('FUNCTION STATS', () => { - testUtils.isVersionGreaterThanHook([7, 0]); + testUtils.isVersionGreaterThanHook([7]); it('transformArguments', () => { assert.deepEqual( diff --git a/packages/client/lib/commands/LMPOP.spec.ts b/packages/client/lib/commands/LMPOP.spec.ts index a3c36f90212..5675ee9a285 100644 --- a/packages/client/lib/commands/LMPOP.spec.ts +++ b/packages/client/lib/commands/LMPOP.spec.ts @@ -3,7 +3,7 @@ import testUtils, { GLOBAL } from '../test-utils'; import { transformArguments } from './LMPOP'; describe('LMPOP', () => { - testUtils.isVersionGreaterThanHook([7, 0]); + testUtils.isVersionGreaterThanHook([7]); describe('transformArguments', () => { it('simple', () => { diff --git a/packages/client/lib/commands/PEXPIRETIME.spec.ts b/packages/client/lib/commands/PEXPIRETIME.spec.ts index 8874346296b..a2fd7f03f82 100644 --- a/packages/client/lib/commands/PEXPIRETIME.spec.ts +++ b/packages/client/lib/commands/PEXPIRETIME.spec.ts @@ -3,7 +3,7 @@ import testUtils, { GLOBAL } from '../test-utils'; import { transformArguments } from './PEXPIRETIME'; describe('PEXPIRETIME', () => { - testUtils.isVersionGreaterThanHook([7, 0]); + testUtils.isVersionGreaterThanHook([7]); it('transformArguments', () => { assert.deepEqual( diff --git a/packages/client/lib/commands/SINTERCARD.spec.ts b/packages/client/lib/commands/SINTERCARD.spec.ts index 24bd6cb8081..a93699f6a13 100644 --- a/packages/client/lib/commands/SINTERCARD.spec.ts +++ b/packages/client/lib/commands/SINTERCARD.spec.ts @@ -3,7 +3,7 @@ import testUtils, { GLOBAL } from '../test-utils'; import { transformArguments } from './SINTERCARD'; describe('SINTERCARD', () => { - testUtils.isVersionGreaterThanHook([7, 0]); + testUtils.isVersionGreaterThanHook([7]); describe('transformArguments', () => { it('simple', () => { diff --git a/packages/client/lib/commands/SORT_RO.spec.ts b/packages/client/lib/commands/SORT_RO.spec.ts index 0cc57991b7f..fe3ca1240d7 100644 --- a/packages/client/lib/commands/SORT_RO.spec.ts +++ b/packages/client/lib/commands/SORT_RO.spec.ts @@ -3,7 +3,7 @@ import testUtils, { GLOBAL } from '../test-utils'; import { transformArguments } from './SORT_RO'; describe('SORT_RO', () => { - testUtils.isVersionGreaterThanHook([7, 0]); + testUtils.isVersionGreaterThanHook([7]); describe('transformArguments', () => { it('simple', () => { diff --git a/packages/client/lib/commands/ZINTERCARD.spec.ts b/packages/client/lib/commands/ZINTERCARD.spec.ts index a2b70616d59..492c1a90433 100644 --- a/packages/client/lib/commands/ZINTERCARD.spec.ts +++ b/packages/client/lib/commands/ZINTERCARD.spec.ts @@ -3,7 +3,7 @@ import testUtils, { GLOBAL } from '../test-utils'; import { transformArguments } from './ZINTERCARD'; describe('ZINTERCARD', () => { - testUtils.isVersionGreaterThanHook([7, 0]); + testUtils.isVersionGreaterThanHook([7]); describe('transformArguments', () => { it('simple', () => { diff --git a/packages/client/lib/commands/ZMPOP.spec.ts b/packages/client/lib/commands/ZMPOP.spec.ts index 84f51e67b7d..9a0c9676c20 100644 --- a/packages/client/lib/commands/ZMPOP.spec.ts +++ b/packages/client/lib/commands/ZMPOP.spec.ts @@ -3,7 +3,7 @@ import testUtils, { GLOBAL } from '../test-utils'; import { transformArguments } from './ZMPOP'; describe('ZMPOP', () => { - testUtils.isVersionGreaterThanHook([7, 0]); + testUtils.isVersionGreaterThanHook([7]); describe('transformArguments', () => { it('simple', () => { From 225524fabf511f9db3cb83d2201ab38fdd352c60 Mon Sep 17 00:00:00 2001 From: Simon Prickett Date: Tue, 26 Apr 2022 14:04:44 +0100 Subject: [PATCH 1198/1748] Adds example of using a trim strategy with XADD. (#2105) * Adds example of using a trim strategy with XADD. * Update stream-producer.js Co-authored-by: Leibale Eidelman --- examples/stream-producer.js | 52 +++++++++++++++++++++++++++---------- 1 file changed, 39 insertions(+), 13 deletions(-) diff --git a/examples/stream-producer.js b/examples/stream-producer.js index 42c5d14bb22..00d6dab7ae6 100644 --- a/examples/stream-producer.js +++ b/examples/stream-producer.js @@ -1,25 +1,51 @@ // A sample stream producer using XADD. - import { createClient } from 'redis'; async function streamProducer() { const client = createClient(); await client.connect(); - - let num = 0; - - while (num < 1000) { - // * = Let Redis generate a timestamp ID for this new entry. - let id = await client.xAdd('mystream', '*', { - num: `${num}` - // Other name/value pairs can go here as required... - }); - - console.log(`Added ${id} to the stream.`); - num += 1; + + for (let i = 0; i < 10000; i++) { + await client.xAdd( + 'mystream', + '*', // * = Let Redis generate a timestamp ID for this new entry. + // Payload to add to the stream: + { + i: i.toString() + // Other name/value pairs can go here as required... + } + ); + + // Also add to a stream whose length we will cap at approximately + // 1000 entries using the MAXLEN trimming strategy: + // https://redis.io/commands/xadd/ + + await client.xAdd( + 'mytrimmedstream', + id, // Re-use the ID from the previous stream. + // Payload to add to the stream: + { + i: i.toString() + // Other name/value pairs can go here as required... + }, + // Specify a trimming strategy... + { + TRIM: { + strategy: 'MAXLEN', // Trim by length. + strategyModifier: '~', // Approximate trimming. + threshold: 1000 // Retain around 1000 entries. + } + } + ); } + // Take a look at how many entries are in the streams... + // Should be 10000: + console.log(`Length of mystream: ${await client.xLen('mystream')}.`); + // Should be approximately 1000: + console.log(`Length of mytrimmedstream: ${await client.xLen('mytrimmedstream')}.`); + await client.quit(); } From baf67fd87f1d872dd6b7b5d1e95ebafb43d54679 Mon Sep 17 00:00:00 2001 From: Leibale Eidelman Date: Tue, 26 Apr 2022 09:05:44 -0400 Subject: [PATCH 1199/1748] fix #1976 - XSETID (#2104) --- packages/client/lib/cluster/commands.ts | 3 +++ packages/client/lib/commands/XSETID.spec.ts | 0 packages/client/lib/commands/XSETID.ts | 28 +++++++++++++++++++++ 3 files changed, 31 insertions(+) create mode 100644 packages/client/lib/commands/XSETID.spec.ts create mode 100644 packages/client/lib/commands/XSETID.ts diff --git a/packages/client/lib/cluster/commands.ts b/packages/client/lib/cluster/commands.ts index a8527cf235f..48ca4b29872 100644 --- a/packages/client/lib/cluster/commands.ts +++ b/packages/client/lib/cluster/commands.ts @@ -155,6 +155,7 @@ import * as XRANGE from '../commands/XRANGE'; import * as XREAD from '../commands/XREAD'; import * as XREADGROUP from '../commands/XREADGROUP'; import * as XREVRANGE from '../commands/XREVRANGE'; +import * as XSETID from '../commands/XSETID'; import * as XTRIM from '../commands/XTRIM'; import * as ZADD from '../commands/ZADD'; import * as ZCARD from '../commands/ZCARD'; @@ -508,6 +509,8 @@ export default { xReadGroup: XREADGROUP, XREVRANGE, xRevRange: XREVRANGE, + XSETID, + xSetId: XSETID, XTRIM, xTrim: XTRIM, ZADD, diff --git a/packages/client/lib/commands/XSETID.spec.ts b/packages/client/lib/commands/XSETID.spec.ts new file mode 100644 index 00000000000..e69de29bb2d diff --git a/packages/client/lib/commands/XSETID.ts b/packages/client/lib/commands/XSETID.ts new file mode 100644 index 00000000000..76acc7ebab4 --- /dev/null +++ b/packages/client/lib/commands/XSETID.ts @@ -0,0 +1,28 @@ +import { RedisCommandArgument, RedisCommandArguments } from '.'; + +export const FIRST_KEY_INDEX = 1; + +interface XSetIdOptions { + ENTRIESADDED?: number; + MAXDELETEDID?: RedisCommandArgument; +} + +export function transformArguments( + key: RedisCommandArgument, + lastId: RedisCommandArgument, + options?: XSetIdOptions +): RedisCommandArguments { + const args = ['XSETID', key, lastId]; + + if (options?.ENTRIESADDED) { + args.push('ENTRIESADDED', options.ENTRIESADDED.toString()); + } + + if (options?.MAXDELETEDID) { + args.push('MAXDELETEDID', options.MAXDELETEDID); + } + + return args; +} + +export declare function transformReply(): 'OK'; From 0752f143a6dbc83df0a5db987907e8794aabe9db Mon Sep 17 00:00:00 2001 From: Leibale Eidelman Date: Mon, 2 May 2022 11:48:12 -0400 Subject: [PATCH 1200/1748] prepare 4.1.0 (#2111) * increase test coverage * @node-redis to @redis * ugprade deps * fix benchmark * use 7.0 docker (not rc), update readmes, clean code, fix @-redis import * update readme * fix function in cluster * update docs Co-authored-by: Chayim * Update clustering.md * add subpackages move warning * drop support for node 12 * upgrade deps * fix tsconfig.base.json Co-authored-by: Chayim --- .github/workflows/tests.yml | 4 +- README.md | 111 +- benchmark/lib/ping/v4.js | 2 +- benchmark/lib/set-get-delete-string/v4.js | 2 +- benchmark/package-lock.json | 164 +- benchmark/package.json | 6 +- docs/FAQ.md | 2 +- docs/client-configuration.md | 31 +- docs/clustering.md | 54 +- examples/time-series.js | 6 +- index.ts | 24 +- package-lock.json | 4986 ++++++++++++----- package.json | 18 +- packages/bloom/README.md | 2 +- packages/bloom/lib/commands/bloom/ADD.ts | 2 +- packages/bloom/lib/commands/bloom/EXISTS.ts | 2 +- packages/bloom/lib/commands/bloom/INSERT.ts | 4 +- .../bloom/lib/commands/bloom/LOADCHUNK.ts | 2 +- packages/bloom/lib/commands/bloom/MADD.ts | 2 +- packages/bloom/lib/commands/bloom/MEXISTS.ts | 2 +- .../lib/commands/count-min-sketch/QUERY.ts | 4 +- packages/bloom/lib/commands/cuckoo/ADD.ts | 2 +- packages/bloom/lib/commands/cuckoo/ADDNX.ts | 2 +- packages/bloom/lib/commands/cuckoo/DEL.ts | 2 +- packages/bloom/lib/commands/cuckoo/EXISTS.ts | 2 +- packages/bloom/lib/commands/cuckoo/INSERT.ts | 4 +- .../bloom/lib/commands/cuckoo/INSERTNX.ts | 4 +- .../bloom/lib/commands/cuckoo/LOADCHUNK.ts | 2 +- packages/bloom/lib/commands/cuckoo/index.ts | 4 +- packages/bloom/lib/commands/top-k/ADD.ts | 4 +- packages/bloom/lib/commands/top-k/COUNT.ts | 4 +- packages/bloom/lib/commands/top-k/QUERY.ts | 4 +- packages/bloom/lib/test-utils.ts | 2 +- packages/bloom/package.json | 12 +- packages/client/README.md | 2 +- packages/client/lib/client/commands-queue.ts | 4 - packages/client/lib/client/index.spec.ts | 11 +- packages/client/lib/client/index.ts | 8 +- packages/client/lib/client/multi-command.ts | 4 +- packages/client/lib/cluster/index.ts | 7 +- packages/client/lib/cluster/multi-command.ts | 4 +- packages/client/lib/commander.ts | 16 +- packages/client/lib/commands/index.ts | 1 - packages/client/lib/multi-command.ts | 3 +- packages/client/lib/test-utils.ts | 2 +- packages/client/package.json | 16 +- packages/graph/README.md | 2 +- packages/graph/lib/commands/QUERY.ts | 2 +- packages/graph/lib/commands/QUERY_RO.ts | 2 +- packages/graph/lib/commands/index.ts | 2 +- packages/graph/lib/test-utils.ts | 2 +- packages/graph/package.json | 12 +- packages/json/README.md | 2 +- packages/json/lib/commands/GET.ts | 2 +- packages/json/lib/test-utils.ts | 2 +- packages/json/package.json | 12 +- packages/search/README.md | 2 +- packages/search/lib/commands/AGGREGATE.ts | 4 +- packages/search/lib/commands/CREATE.ts | 2 +- packages/search/lib/commands/DICTADD.ts | 4 +- packages/search/lib/commands/DICTDEL.ts | 4 +- packages/search/lib/commands/INFO.ts | 4 +- .../search/lib/commands/PROFILE_SEARCH.ts | 2 +- packages/search/lib/commands/SEARCH.ts | 4 +- packages/search/lib/commands/SUGDEL.ts | 2 +- packages/search/lib/commands/SYNUPDATE.ts | 4 +- packages/search/lib/commands/index.ts | 4 +- packages/search/lib/test-utils.ts | 2 +- packages/search/package.json | 12 +- packages/test-utils/lib/dockers.ts | 6 +- packages/test-utils/lib/index.ts | 6 +- packages/test-utils/package.json | 10 +- packages/time-series/README.md | 2 +- packages/time-series/lib/commands/DECRBY.ts | 2 +- packages/time-series/lib/commands/DEL.ts | 2 +- packages/time-series/lib/commands/INCRBY.ts | 2 +- packages/time-series/lib/commands/MGET.ts | 2 +- packages/time-series/lib/commands/MRANGE.ts | 2 +- .../lib/commands/MRANGE_WITHLABELS.ts | 2 +- .../time-series/lib/commands/MREVRANGE.ts | 2 +- .../lib/commands/MREVRANGE_WITHLABELS.ts | 2 +- .../time-series/lib/commands/QUERYINDEX.ts | 4 +- packages/time-series/lib/commands/RANGE.ts | 2 +- packages/time-series/lib/commands/REVRANGE.ts | 2 +- .../time-series/lib/commands/index.spec.ts | 2 +- packages/time-series/lib/commands/index.ts | 4 +- packages/time-series/lib/test-utils.ts | 2 +- packages/time-series/package.json | 12 +- tsconfig.base.json | 2 +- 89 files changed, 3860 insertions(+), 1856 deletions(-) diff --git a/.github/workflows/tests.yml b/.github/workflows/tests.yml index ba11eaa2960..2ec99b7bba0 100644 --- a/.github/workflows/tests.yml +++ b/.github/workflows/tests.yml @@ -16,8 +16,8 @@ jobs: strategy: fail-fast: false matrix: - node-version: ['12', '14', '16'] - redis-version: ['5', '6.0', '6.2', '7.0-rc3'] + node-version: ['14', '16', '17', '18'] + redis-version: ['5', '6.0', '6.2', '7.0'] steps: - uses: actions/checkout@v2.3.4 with: diff --git a/README.md b/README.md index ab949a199b3..514a74221e8 100644 --- a/README.md +++ b/README.md @@ -4,22 +4,28 @@ [![Coverage](https://codecov.io/gh/redis/node-redis/branch/master/graph/badge.svg?token=xcfqHhJC37)](https://codecov.io/gh/redis/node-redis) [![License](https://img.shields.io/github/license/redis/node-redis.svg)](https://github.com/redis/node-redis/blob/master/LICENSE) [![LGTM alerts](https://img.shields.io/lgtm/alerts/g/redis/node-redis.svg?logo=LGTM)](https://lgtm.com/projects/g/redis/node-redis/alerts) -[![Chat](https://img.shields.io/discord/697882427875393627.svg)](https://discord.gg/redis) -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). +[![Discord](https://img.shields.io/discord/697882427875393627.svg?style=social&logo=discord)](https://discord.gg/redis) +[![Twitch](https://img.shields.io/twitch/status/redisinc?style=social)](https://www.twitch.tv/redisinc) +[![YouTube](https://img.shields.io/youtube/channel/views/UCD78lHSwYqMlyetR0_P4Vig?style=social)](https://www.youtube.com/redisinc) +[![Twitter](https://img.shields.io/twitter/follow/redisinc?style=social)](https://twitter.com/redisinc) + +node-redis is a modern, high performance [Redis](https://redis.io) client for Node.js. ## Packages -| Name | Description | -|---------------------------------------------------|----------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------| -| [redis](./) | [![Downloads](https://img.shields.io/npm/dm/redis.svg)](https://www.npmjs.com/package/redis) [![Version](https://img.shields.io/npm/v/redis.svg)](https://www.npmjs.com/package/redis) | -| [@node-redis/client](./packages/client) | [![Downloads](https://img.shields.io/npm/dm/@node-redis/client.svg)](https://www.npmjs.com/package/@node-redis/client) [![Version](https://img.shields.io/npm/v/@node-redis/client.svg)](https://www.npmjs.com/package/@node-redis/client) [![Docs](https://img.shields.io/badge/-documentation-dc382c)](https://redis.js.org/documentation/client/) | -| [@node-redis/bloom](./packages/bloom) | [![Downloads](https://img.shields.io/npm/dm/@node-redis/bloom.svg)](https://www.npmjs.com/package/@node-redis/bloom) [![Version](https://img.shields.io/npm/v/@node-redis/bloom.svg)](https://www.npmjs.com/package/@node-redis/bloom) [![Docs](https://img.shields.io/badge/-documentation-dc382c)](https://redis.js.org/documentation/bloom/) [Redis Bloom](https://oss.redis.com/redisbloom/) commands | -| [@node-redis/graph](./packages/graph) | [![Downloads](https://img.shields.io/npm/dm/@node-redis/graph.svg)](https://www.npmjs.com/package/@node-redis/graph) [![Version](https://img.shields.io/npm/v/@node-redis/graph.svg)](https://www.npmjs.com/package/@node-redis/graph) [![Docs](https://img.shields.io/badge/-documentation-dc382c)](https://redis.js.org/documentation/graph/) [Redis Graph](https://oss.redis.com/redisgraph/) commands | -| [@node-redis/json](./packages/json) | [![Downloads](https://img.shields.io/npm/dm/@node-redis/json.svg)](https://www.npmjs.com/package/@node-redis/json) [![Version](https://img.shields.io/npm/v/@node-redis/json.svg)](https://www.npmjs.com/package/@node-redis/json) [![Docs](https://img.shields.io/badge/-documentation-dc382c)](https://redis.js.org/documentation/json/) [Redis JSON](https://oss.redis.com/redisjson/) commands | -| [@node-redis/search](./packages/search) | [![Downloads](https://img.shields.io/npm/dm/@node-redis/search.svg)](https://www.npmjs.com/package/@node-redis/search) [![Version](https://img.shields.io/npm/v/@node-redis/search.svg)](https://www.npmjs.com/package/@node-redis/search) [![Docs](https://img.shields.io/badge/-documentation-dc382c)](https://redis.js.org/documentation/search/) [Redis Search](https://oss.redis.com/redisearch/) commands | -| [@node-redis/time-series](./packages/time-series) | [![Downloads](https://img.shields.io/npm/dm/@node-redis/time-series.svg)](https://www.npmjs.com/package/@node-redis/time-series) [![Version](https://img.shields.io/npm/v/@node-redis/time-series.svg)](https://www.npmjs.com/package/@node-redis/time-series) [![Docs](https://img.shields.io/badge/-documentation-dc382c)](https://redis.js.org/documentation/time-series/) [Redis Time-Series](https://oss.redis.com/redistimeseries/) commands | +| Name | Description | +|----------------------------------------------|--------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------| +| [redis](./) | [![Downloads](https://img.shields.io/npm/dm/redis.svg)](https://www.npmjs.com/package/redis) [![Version](https://img.shields.io/npm/v/redis.svg)](https://www.npmjs.com/package/redis) | +| [@redis/client](./packages/client) | [![Downloads](https://img.shields.io/npm/dm/@redis/client.svg)](https://www.npmjs.com/package/@redis/client) [![Version](https://img.shields.io/npm/v/@redis/client.svg)](https://www.npmjs.com/package/@redis/client) [![Docs](https://img.shields.io/badge/-documentation-dc382c)](https://redis.js.org/documentation/client/) | +| [@redis/bloom](./packages/bloom) | [![Downloads](https://img.shields.io/npm/dm/@redis/bloom.svg)](https://www.npmjs.com/package/@redis/bloom) [![Version](https://img.shields.io/npm/v/@redis/bloom.svg)](https://www.npmjs.com/package/@redis/bloom) [![Docs](https://img.shields.io/badge/-documentation-dc382c)](https://redis.js.org/documentation/bloom/) [Redis Bloom](https://oss.redis.com/redisbloom/) commands | +| [@redis/graph](./packages/graph) | [![Downloads](https://img.shields.io/npm/dm/@redis/graph.svg)](https://www.npmjs.com/package/@redis/graph) [![Version](https://img.shields.io/npm/v/@redis/graph.svg)](https://www.npmjs.com/package/@redis/graph) [![Docs](https://img.shields.io/badge/-documentation-dc382c)](https://redis.js.org/documentation/graph/) [Redis Graph](https://oss.redis.com/redisgraph/) commands | +| [@redis/json](./packages/json) | [![Downloads](https://img.shields.io/npm/dm/@redis/json.svg)](https://www.npmjs.com/package/@redis/json) [![Version](https://img.shields.io/npm/v/@redis/json.svg)](https://www.npmjs.com/package/@redis/json) [![Docs](https://img.shields.io/badge/-documentation-dc382c)](https://redis.js.org/documentation/json/) [Redis JSON](https://oss.redis.com/redisjson/) commands | +| [@redis/search](./packages/search) | [![Downloads](https://img.shields.io/npm/dm/@redis/search.svg)](https://www.npmjs.com/package/@redis/search) [![Version](https://img.shields.io/npm/v/@redis/search.svg)](https://www.npmjs.com/package/@redis/search) [![Docs](https://img.shields.io/badge/-documentation-dc382c)](https://redis.js.org/documentation/search/) [Redis Search](https://oss.redis.com/redisearch/) commands | +| [@redis/time-series](./packages/time-series) | [![Downloads](https://img.shields.io/npm/dm/@redis/time-series.svg)](https://www.npmjs.com/package/@redis/time-series) [![Version](https://img.shields.io/npm/v/@redis/time-series.svg)](https://www.npmjs.com/package/@redis/time-series) [![Docs](https://img.shields.io/badge/-documentation-dc382c)](https://redis.js.org/documentation/time-series/) [Redis Time-Series](https://oss.redis.com/redistimeseries/) commands | + +> :warning: In version 4.1.0 we moved our subpackages from `@node-redis` to `@redis`. If you're just using `npm install redis`, you don't need to do anything—it'll upgrade automatically. If you're using the subpackages directly, you'll need to point to the new scope (e.g. `@redis/client` instead of `@node-redis/client`). ## Installation @@ -219,36 +225,84 @@ client.scanIterator({ }); ``` -### Lua Scripts +### [Programmability](https://redis.io/docs/manual/programmability/) + +Redis provides a programming interface allowing code execution on the redis server. + +#### [Functions](https://redis.io/docs/manual/programmability/functions-intro/) + +The following example retrieves a key in redis, returning the value of the key, incremented by an integer. For example, if your key _foo_ has the value _17_ and we run `add('foo', 25)`, it returns the answer to Life, the Universe and Everything. + +```lua +#!lua name=library + +redis.register_function { + function_name = 'add', + callback = function(keys, args) return redis.call('GET', keys[1]) + args[1] end, + flags = { 'no-writes' } +} +``` + +Here is the same example, but in a format that can be pasted into the `redis-cli`. -Define new functions using [Lua scripts](https://redis.io/commands/eval) which execute on the Redis server: +``` +FUNCTION LOAD "#!lua name=library\nredis.register_function{function_name=\"add\", callback=function(keys, args) return redis.call('GET', keys[1])+args[1] end, flags={\"no-writes\"}}" +``` + +Load the prior redis function on the _redis server_ before running the example below. ```typescript -import { createClient, defineScript } from 'redis'; +import { createClient } from 'redis'; -(async () => { - const client = createClient({ - scripts: { - add: defineScript({ +const client = createClient({ + functions: { + library: { + add: { NUMBER_OF_KEYS: 1, - SCRIPT: - 'local val = redis.pcall("GET", KEYS[1]);' + - 'return val + ARGV[1];', transformArguments(key: string, toAdd: number): Array { return [key, toAdd.toString()]; }, transformReply(reply: number): number { return reply; } - }) + } } - }); + } +}); + +await client.connect(); + +await client.set('key', '1'); +await client.library.add('key', 2); // 3 +``` + +#### [Lua Scripts](https://redis.io/docs/manual/programmability/eval-intro/) - await client.connect(); +The following is an end-to-end example of the prior concept. + +```typescript +import { createClient, defineScript } from 'redis'; + +const client = createClient({ + scripts: { + add: defineScript({ + NUMBER_OF_KEYS: 1, + SCRIPT: + 'return redis.call("GET", KEYS[1]) + ARGV[1];', + transformArguments(key: string, toAdd: number): Array { + return [key, toAdd.toString()]; + }, + transformReply(reply: number): number { + return reply; + } + }) + } +}); + +await client.connect(); - await client.set('key', '1'); - await client.add('key', 2); // 3 -})(); +await client.set('key', '1'); +await client.add('key', 2); // 3 ``` ### Disconnecting @@ -323,9 +377,10 @@ Node Redis is supported with the following versions of Redis: | Version | Supported | |---------|--------------------| +| 7.0.z | :heavy_check_mark: | | 6.2.z | :heavy_check_mark: | | 6.0.z | :heavy_check_mark: | -| 5.y.z | :heavy_check_mark: | +| 5.0.z | :heavy_check_mark: | | < 5.0 | :x: | > Node Redis should work with older versions of Redis, but it is not fully tested and we cannot offer support. diff --git a/benchmark/lib/ping/v4.js b/benchmark/lib/ping/v4.js index fe778bc1280..69aa3c06929 100644 --- a/benchmark/lib/ping/v4.js +++ b/benchmark/lib/ping/v4.js @@ -1,4 +1,4 @@ -import { createClient } from '@node-redis/client'; +import { createClient } from '@redis/client'; export default async (host) => { const client = createClient({ diff --git a/benchmark/lib/set-get-delete-string/v4.js b/benchmark/lib/set-get-delete-string/v4.js index f14a69c6868..dd06b1f1036 100644 --- a/benchmark/lib/set-get-delete-string/v4.js +++ b/benchmark/lib/set-get-delete-string/v4.js @@ -1,4 +1,4 @@ -import { createClient } from '@node-redis/client'; +import { createClient } from '@redis/client'; export default async (host, { randomString }) => { const client = createClient({ diff --git a/benchmark/package-lock.json b/benchmark/package-lock.json index 3b4e24af23c..72db34dad13 100644 --- a/benchmark/package-lock.json +++ b/benchmark/package-lock.json @@ -1,20 +1,20 @@ { - "name": "@node-redis/client-benchmark", + "name": "@redis/client-benchmark", "lockfileVersion": 2, "requires": true, "packages": { "": { - "name": "@node-redis/client-benchmark", + "name": "@redis/client-benchmark", "dependencies": { - "@node-redis/client": "../packages/client", + "@redis/client": "../packages/client", "hdr-histogram-js": "3.0.0", "ioredis": "5.0.4", - "redis-v3": "npm:redis@4.0.6", + "redis-v3": "npm:redis@3.1.2", "yargs": "17.4.1" } }, "../packages/client": { - "name": "@node-redis/client", + "name": "@redis/client", "version": "1.0.5", "license": "MIT", "dependencies": { @@ -24,12 +24,12 @@ }, "devDependencies": { "@istanbuljs/nyc-config-typescript": "^1.0.2", - "@node-redis/test-utils": "*", - "@types/node": "^17.0.26", + "@redis/test-utils": "*", + "@types/node": "^17.0.29", "@types/sinon": "^10.0.11", "@types/yallist": "^4.0.1", - "@typescript-eslint/eslint-plugin": "^5.20.0", - "@typescript-eslint/parser": "^5.20.0", + "@typescript-eslint/eslint-plugin": "^5.21.0", + "@typescript-eslint/parser": "^5.21.0", "eslint": "^8.14.0", "nyc": "^15.1.0", "release-it": "^14.14.2", @@ -53,50 +53,10 @@ "resolved": "https://registry.npmjs.org/@ioredis/commands/-/commands-1.1.1.tgz", "integrity": "sha512-fsR4P/ROllzf/7lXYyElUJCheWdTJVJvOTps8v9IWKFATxR61ANOlnoPqhH099xYLrJGpc2ZQ28B3rMeUt5VQg==" }, - "node_modules/@node-redis/bloom": { - "version": "1.0.1", - "resolved": "https://registry.npmjs.org/@node-redis/bloom/-/bloom-1.0.1.tgz", - "integrity": "sha512-mXEBvEIgF4tUzdIN89LiYsbi6//EdpFA7L8M+DHCvePXg+bfHWi+ct5VI6nHUFQE5+ohm/9wmgihCH3HSkeKsw==", - "peerDependencies": { - "@node-redis/client": "^1.0.0" - } - }, - "node_modules/@node-redis/client": { + "node_modules/@redis/client": { "resolved": "../packages/client", "link": true }, - "node_modules/@node-redis/graph": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/@node-redis/graph/-/graph-1.0.0.tgz", - "integrity": "sha512-mRSo8jEGC0cf+Rm7q8mWMKKKqkn6EAnA9IA2S3JvUv/gaWW/73vil7GLNwion2ihTptAm05I9LkepzfIXUKX5g==", - "peerDependencies": { - "@node-redis/client": "^1.0.0" - } - }, - "node_modules/@node-redis/json": { - "version": "1.0.2", - "resolved": "https://registry.npmjs.org/@node-redis/json/-/json-1.0.2.tgz", - "integrity": "sha512-qVRgn8WfG46QQ08CghSbY4VhHFgaTY71WjpwRBGEuqGPfWwfRcIf3OqSpR7Q/45X+v3xd8mvYjywqh0wqJ8T+g==", - "peerDependencies": { - "@node-redis/client": "^1.0.0" - } - }, - "node_modules/@node-redis/search": { - "version": "1.0.5", - "resolved": "https://registry.npmjs.org/@node-redis/search/-/search-1.0.5.tgz", - "integrity": "sha512-MCOL8iCKq4v+3HgEQv8zGlSkZyXSXtERgrAJ4TSryIG/eLFy84b57KmNNa/V7M1Q2Wd2hgn2nPCGNcQtk1R1OQ==", - "peerDependencies": { - "@node-redis/client": "^1.0.0" - } - }, - "node_modules/@node-redis/time-series": { - "version": "1.0.2", - "resolved": "https://registry.npmjs.org/@node-redis/time-series/-/time-series-1.0.2.tgz", - "integrity": "sha512-HGQ8YooJ8Mx7l28tD7XjtB3ImLEjlUxG1wC1PAjxu6hPJqjPshUZxAICzDqDjtIbhDTf48WXXUcx8TQJB1XTKA==", - "peerDependencies": { - "@node-redis/client": "^1.0.0" - } - }, "node_modules/ansi-regex": { "version": "5.0.1", "resolved": "https://registry.npmjs.org/ansi-regex/-/ansi-regex-5.0.1.tgz", @@ -281,6 +241,11 @@ "resolved": "https://registry.npmjs.org/pako/-/pako-1.0.11.tgz", "integrity": "sha512-4hLB8Py4zZce5s4yd9XzopqwVv/yGNhV1Bl8NTmCq1763HeK2+EwVTv+leGeL13Dnh2wfbqowVPXCIO0z4taYw==" }, + "node_modules/redis-commands": { + "version": "1.7.0", + "resolved": "https://registry.npmjs.org/redis-commands/-/redis-commands-1.7.0.tgz", + "integrity": "sha512-nJWqw3bTFy21hX/CPKHth6sfhZbdiHP6bTawSgQBlKOVRG7EZkfHbbHwQJnrE4vsQf0CMNE+3gJ4Fmm16vdVlQ==" + }, "node_modules/redis-errors": { "version": "1.2.0", "resolved": "https://registry.npmjs.org/redis-errors/-/redis-errors-1.2.0.tgz", @@ -302,16 +267,29 @@ }, "node_modules/redis-v3": { "name": "redis", - "version": "4.0.6", - "resolved": "https://registry.npmjs.org/redis/-/redis-4.0.6.tgz", - "integrity": "sha512-IaPAxgF5dV0jx+A9l6yd6R9/PAChZIoAskDVRzUODeLDNhsMlq7OLLTmu0AwAr0xjrJ1bibW5xdpRwqIQ8Q0Xg==", + "version": "3.1.2", + "resolved": "https://registry.npmjs.org/redis/-/redis-3.1.2.tgz", + "integrity": "sha512-grn5KoZLr/qrRQVwoSkmzdbw6pwF+/rwODtrOr6vuBRiR/f3rjSTGupbF90Zpqm2oenix8Do6RV7pYEkGwlKkw==", "dependencies": { - "@node-redis/bloom": "1.0.1", - "@node-redis/client": "1.0.5", - "@node-redis/graph": "1.0.0", - "@node-redis/json": "1.0.2", - "@node-redis/search": "1.0.5", - "@node-redis/time-series": "1.0.2" + "denque": "^1.5.0", + "redis-commands": "^1.7.0", + "redis-errors": "^1.2.0", + "redis-parser": "^3.0.0" + }, + "engines": { + "node": ">=10" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/node-redis" + } + }, + "node_modules/redis-v3/node_modules/denque": { + "version": "1.5.1", + "resolved": "https://registry.npmjs.org/denque/-/denque-1.5.1.tgz", + "integrity": "sha512-XwE+iZ4D6ZUB7mfYRMb5wByE8L74HCn30FBN7sWnXksWc1LO1bPDl67pBR9o/kC4z/xSNAwkMYcGgqDV3BE3Hw==", + "engines": { + "node": ">=0.10" } }, "node_modules/require-directory": { @@ -412,22 +390,16 @@ "resolved": "https://registry.npmjs.org/@ioredis/commands/-/commands-1.1.1.tgz", "integrity": "sha512-fsR4P/ROllzf/7lXYyElUJCheWdTJVJvOTps8v9IWKFATxR61ANOlnoPqhH099xYLrJGpc2ZQ28B3rMeUt5VQg==" }, - "@node-redis/bloom": { - "version": "1.0.1", - "resolved": "https://registry.npmjs.org/@node-redis/bloom/-/bloom-1.0.1.tgz", - "integrity": "sha512-mXEBvEIgF4tUzdIN89LiYsbi6//EdpFA7L8M+DHCvePXg+bfHWi+ct5VI6nHUFQE5+ohm/9wmgihCH3HSkeKsw==", - "requires": {} - }, - "@node-redis/client": { + "@redis/client": { "version": "file:../packages/client", "requires": { "@istanbuljs/nyc-config-typescript": "^1.0.2", - "@node-redis/test-utils": "*", - "@types/node": "^17.0.26", + "@redis/test-utils": "*", + "@types/node": "^17.0.29", "@types/sinon": "^10.0.11", "@types/yallist": "^4.0.1", - "@typescript-eslint/eslint-plugin": "^5.20.0", - "@typescript-eslint/parser": "^5.20.0", + "@typescript-eslint/eslint-plugin": "^5.21.0", + "@typescript-eslint/parser": "^5.21.0", "cluster-key-slot": "1.1.0", "eslint": "^8.14.0", "generic-pool": "3.8.2", @@ -441,30 +413,6 @@ "yallist": "4.0.0" } }, - "@node-redis/graph": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/@node-redis/graph/-/graph-1.0.0.tgz", - "integrity": "sha512-mRSo8jEGC0cf+Rm7q8mWMKKKqkn6EAnA9IA2S3JvUv/gaWW/73vil7GLNwion2ihTptAm05I9LkepzfIXUKX5g==", - "requires": {} - }, - "@node-redis/json": { - "version": "1.0.2", - "resolved": "https://registry.npmjs.org/@node-redis/json/-/json-1.0.2.tgz", - "integrity": "sha512-qVRgn8WfG46QQ08CghSbY4VhHFgaTY71WjpwRBGEuqGPfWwfRcIf3OqSpR7Q/45X+v3xd8mvYjywqh0wqJ8T+g==", - "requires": {} - }, - "@node-redis/search": { - "version": "1.0.5", - "resolved": "https://registry.npmjs.org/@node-redis/search/-/search-1.0.5.tgz", - "integrity": "sha512-MCOL8iCKq4v+3HgEQv8zGlSkZyXSXtERgrAJ4TSryIG/eLFy84b57KmNNa/V7M1Q2Wd2hgn2nPCGNcQtk1R1OQ==", - "requires": {} - }, - "@node-redis/time-series": { - "version": "1.0.2", - "resolved": "https://registry.npmjs.org/@node-redis/time-series/-/time-series-1.0.2.tgz", - "integrity": "sha512-HGQ8YooJ8Mx7l28tD7XjtB3ImLEjlUxG1wC1PAjxu6hPJqjPshUZxAICzDqDjtIbhDTf48WXXUcx8TQJB1XTKA==", - "requires": {} - }, "ansi-regex": { "version": "5.0.1", "resolved": "https://registry.npmjs.org/ansi-regex/-/ansi-regex-5.0.1.tgz", @@ -590,6 +538,11 @@ "resolved": "https://registry.npmjs.org/pako/-/pako-1.0.11.tgz", "integrity": "sha512-4hLB8Py4zZce5s4yd9XzopqwVv/yGNhV1Bl8NTmCq1763HeK2+EwVTv+leGeL13Dnh2wfbqowVPXCIO0z4taYw==" }, + "redis-commands": { + "version": "1.7.0", + "resolved": "https://registry.npmjs.org/redis-commands/-/redis-commands-1.7.0.tgz", + "integrity": "sha512-nJWqw3bTFy21hX/CPKHth6sfhZbdiHP6bTawSgQBlKOVRG7EZkfHbbHwQJnrE4vsQf0CMNE+3gJ4Fmm16vdVlQ==" + }, "redis-errors": { "version": "1.2.0", "resolved": "https://registry.npmjs.org/redis-errors/-/redis-errors-1.2.0.tgz", @@ -604,16 +557,21 @@ } }, "redis-v3": { - "version": "npm:redis@4.0.6", - "resolved": "https://registry.npmjs.org/redis/-/redis-4.0.6.tgz", - "integrity": "sha512-IaPAxgF5dV0jx+A9l6yd6R9/PAChZIoAskDVRzUODeLDNhsMlq7OLLTmu0AwAr0xjrJ1bibW5xdpRwqIQ8Q0Xg==", + "version": "npm:redis@3.1.2", + "resolved": "https://registry.npmjs.org/redis/-/redis-3.1.2.tgz", + "integrity": "sha512-grn5KoZLr/qrRQVwoSkmzdbw6pwF+/rwODtrOr6vuBRiR/f3rjSTGupbF90Zpqm2oenix8Do6RV7pYEkGwlKkw==", "requires": { - "@node-redis/bloom": "1.0.1", - "@node-redis/client": "1.0.5", - "@node-redis/graph": "1.0.0", - "@node-redis/json": "1.0.2", - "@node-redis/search": "1.0.5", - "@node-redis/time-series": "1.0.2" + "denque": "^1.5.0", + "redis-commands": "^1.7.0", + "redis-errors": "^1.2.0", + "redis-parser": "^3.0.0" + }, + "dependencies": { + "denque": { + "version": "1.5.1", + "resolved": "https://registry.npmjs.org/denque/-/denque-1.5.1.tgz", + "integrity": "sha512-XwE+iZ4D6ZUB7mfYRMb5wByE8L74HCn30FBN7sWnXksWc1LO1bPDl67pBR9o/kC4z/xSNAwkMYcGgqDV3BE3Hw==" + } } }, "require-directory": { diff --git a/benchmark/package.json b/benchmark/package.json index 3dc768edd38..da177dbc7a1 100644 --- a/benchmark/package.json +++ b/benchmark/package.json @@ -1,5 +1,5 @@ { - "name": "@node-redis/client-benchmark", + "name": "@redis/client-benchmark", "private": true, "main": "./lib", "type": "module", @@ -7,10 +7,10 @@ "start": "node ." }, "dependencies": { - "@node-redis/client": "../packages/client", + "@redis/client": "../packages/client", "hdr-histogram-js": "3.0.0", "ioredis": "5.0.4", - "redis-v3": "npm:redis@4.0.6", + "redis-v3": "npm:redis@3.1.2", "yargs": "17.4.1" } } diff --git a/docs/FAQ.md b/docs/FAQ.md index 3babbb9d845..4b7710df3fc 100644 --- a/docs/FAQ.md +++ b/docs/FAQ.md @@ -19,7 +19,7 @@ If `socket.write()` returns `false`—meaning that ["all or part of the data was Redis has support for [modules](https://redis.io/modules) and running [Lua scripts](../README.md#lua-scripts) within the Redis context. To take advantage of typing within these scenarios, `RedisClient` and `RedisCluster` should be used with [typeof](https://www.typescriptlang.org/docs/handbook/2/typeof-types.html), rather than the base types `RedisClientType` and `RedisClusterType`. ```typescript -import { createClient } from '@node-redis/client'; +import { createClient } from '@redis/client'; export const client = createClient(); diff --git a/docs/client-configuration.md b/docs/client-configuration.md index 092f3d8eb83..6b7e7da7532 100644 --- a/docs/client-configuration.md +++ b/docs/client-configuration.md @@ -3,24 +3,25 @@ | Property | Default | Description | |--------------------------|------------------------------------------|-----------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------| | url | | `redis[s]://[[username][:password]@][host][:port][/db-number]` (see [`redis`](https://www.iana.org/assignments/uri-schemes/prov/redis) and [`rediss`](https://www.iana.org/assignments/uri-schemes/prov/rediss) IANA registration for more details) | -| socket | | Object defining socket connection properties. Any [`net.createConnection`](https://nodejs.org/api/net.html#netcreateconnectionoptions-connectlistener) option that is not listed here is supported as well | -| socket.port | `6379` | Port to connect to | -| socket.host | `'localhost'` | Hostname to connect to | -| socket.family | `0` | Version of IP stack. Must be `4 \| 6 \| 0`. The value `0` indicates that both IPv4 and IPv6 addresses are allowed. | -| socket.path | | UNIX Socket to connect to | -| socket.connectTimeout | `5000` | The timeout for connecting to the Redis Server (in milliseconds) | -| socket.noDelay | `true` | Enable/disable the use of [`Nagle's algorithm`](https://nodejs.org/api/net.html#net_socket_setnodelay_nodelay) | -| socket.keepAlive | `5000` | Enable/disable the [`keep-alive`](https://nodejs.org/api/net.html#net_socket_setkeepalive_enable_initialdelay) functionality | +| socket | | Socket connection properties. Unlisted [`net.connect`](https://nodejs.org/api/net.html#socketconnectoptions-connectlistener) properties (and [`tls.connect`](https://nodejs.org/api/tls.html#tlsconnectoptions-callback)) are also supported | +| socket.port | `6379` | Redis server port | +| socket.host | `'localhost'` | Redis server hostname | +| socket.family | `0` | IP Stack version (one of `4 \| 6 \| 0`) | +| socket.path | | Path to the UNIX Socket | +| socket.connectTimeout | `5000` | Connection Timeout (in milliseconds) | +| socket.noDelay | `true` | Toggle [`Nagle's algorithm`](https://nodejs.org/api/net.html#net_socket_setnodelay_nodelay) | +| socket.keepAlive | `5000` | Toggle [`keep-alive`](https://nodejs.org/api/net.html#net_socket_setkeepalive_enable_initialdelay) functionality | | socket.tls | | See explanation and examples [below](#TLS) | | socket.reconnectStrategy | `retries => Math.min(retries * 50, 500)` | A function containing the [Reconnect Strategy](#reconnect-strategy) logic | | username | | ACL username ([see ACL guide](https://redis.io/topics/acl)) | | password | | ACL password or the old "--requirepass" password | | name | | Connection name ([see `CLIENT SETNAME`](https://redis.io/commands/client-setname)) | -| database | | Database number to connect to (see [`SELECT`](https://redis.io/commands/select) command) | -| 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)) | +| database | | Redis database number (see [`SELECT`](https://redis.io/commands/select) command) | +| modules | | Included [Redis Modules](../README.md#packages) | +| scripts | | Script definitions (see [Lua Scripts](../README.md#lua-scripts)) | +| functions | | Function definitions (see [Functions](../README.md#functions)) | | commandsQueueMaxLength | | Maximum length of the client's internal command queue | -| disableOfflineQueue | `false` | Disables offline queuing, see the [FAQ](./FAQ.md#what-happens-when-the-network-goes-down) for details | +| disableOfflineQueue | `false` | Disables offline queuing, see [FAQ](./FAQ.md#what-happens-when-the-network-goes-down) | | 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)) | | isolationPoolOptions | | See the [Isolated Execution Guide](./isolated-execution.md) | @@ -31,12 +32,12 @@ You can implement a custom reconnect strategy as a function: - Receives the number of retries attempted so far. - Returns `number | Error`: - - `number`: the wait time in milliseconds prior attempting to reconnect. - - `Error`: closes the client and flushes the internal command queues. + - `number`: wait time in milliseconds prior to attempting a reconnect. + - `Error`: closes the client and flushes internal command queues. ## TLS -When creating a client, set `socket.tls` to `true` to enable TLS. Below are some basic examples. +To enable TLS, set `socket.tls` to `true`. Below are some basic examples. > For configuration options see [tls.connect](https://nodejs.org/api/tls.html#tlsconnectoptions-callback) and [tls.createSecureContext](https://nodejs.org/api/tls.html#tlscreatesecurecontextoptions), as those are the underlying functions used by this library. diff --git a/docs/clustering.md b/docs/clustering.md index bc7a5561ae0..bf88be6cdeb 100644 --- a/docs/clustering.md +++ b/docs/clustering.md @@ -7,25 +7,23 @@ Connecting to a cluster is a bit different. Create the client by specifying some ```typescript import { createCluster } from 'redis'; -(async () => { - const cluster = createCluster({ - rootNodes: [ - { - url: 'redis://10.0.0.1:30001' - }, - { - url: 'redis://10.0.0.2:30002' - } - ] - }); - - cluster.on('error', (err) => console.log('Redis Cluster Error', err)); - - await cluster.connect(); - - await cluster.set('key', 'value'); - const value = await cluster.get('key'); -})(); +const cluster = createCluster({ + rootNodes: [ + { + url: 'redis://10.0.0.1:30001' + }, + { + url: 'redis://10.0.0.2:30002' + } + ] +}); + +cluster.on('error', (err) => console.log('Redis Cluster Error', err)); + +await cluster.connect(); + +await cluster.set('key', 'value'); +const value = await cluster.get('key'); ``` ## `createCluster` configuration @@ -38,15 +36,15 @@ 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 | -| nodeAddressMap | | Object defining the [node address mapping](#node-address-map) | -| 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)) | +| nodeAddressMap | | Defines the [node address mapping](#node-address-map) | +| modules | | Included [Redis Modules](../README.md#packages) | +| scripts | | Script definitions (see [Lua Scripts](../README.md#lua-scripts)) | +| functions | | Function definitions (see [Functions](../README.md#functions)) | ## Node Address Map -Your cluster might be configured to work within an internal network that your local environment doesn't have access to. For example, your development machine could only have access to external addresses, but the cluster returns its internal addresses. In this scenario, it's useful to provide a map from those internal addresses to the external ones. - -The configuration for this is a simple mapping. Just provide a `nodeAddressMap` property mapping the internal addresses and ports to the external addresses and ports. Then, any address provided to `rootNodes` or returned from the cluster will be mapped accordingly: +A node address map is required when a redis cluster is configured with addresses that are inaccessible by the machine running the redis client. +This is a mapping of addresses and ports, with the values being the accessible address/port combination. Example: ```javascript createCluster({ @@ -68,12 +66,12 @@ createCluster({ ### Commands that operate on Redis Keys -Commands such as `GET`, `SET`, etc. will be routed by the first key, for instance `MGET 1 2 3` will be routed by the key `1`. +Commands such as `GET`, `SET`, etc. are routed by the first key, for instance `MGET 1 2 3` will be routed by the key `1`. ### [Server Commands](https://redis.io/commands#server) -Admin commands such as `MEMORY STATS`, `FLUSHALL`, etc. are not attached to the cluster, and should be executed on a specific node using `.getSlot()` or `.getAllMasters()`. +Admin commands such as `MEMORY STATS`, `FLUSHALL`, etc. are not attached to the cluster, and must be executed on a specific node via `.getSlotMaster()`. ### "Forwarded Commands" -Some commands (e.g. `PUBLISH`) are forwarded to other cluster nodes by the Redis server. The client will send these commands to a random node in order to spread the load across the cluster. +Certain commands (e.g. `PUBLISH`) are forwarded to other cluster nodes by the Redis server. This client sends these commands to a random node in order to spread the load across the cluster. diff --git a/examples/time-series.js b/examples/time-series.js index 2fd27e44117..9983a17e747 100644 --- a/examples/time-series.js +++ b/examples/time-series.js @@ -2,7 +2,7 @@ // Requires the RedisTimeSeries module: https://redistimeseries.io/ import { createClient } from 'redis'; -import { TimeSeriesDuplicatePolicies, TimeSeriesEncoding, TimeSeriesAggregationType } from '@node-redis/time-series'; +import { TimeSeriesDuplicatePolicies, TimeSeriesEncoding, TimeSeriesAggregationType } from '@redis/time-series'; async function timeSeries() { const client = createClient(); @@ -11,7 +11,7 @@ async function timeSeries() { await client.del('mytimeseries'); try { - // Create a timeseries + // Create a timeseries // https://oss.redis.com/redistimeseries/commands/#tscreate const created = await client.ts.create('mytimeseries', { RETENTION: 86400000, // 1 day in milliseconds @@ -97,7 +97,7 @@ async function timeSeries() { // Get some information about the state of the timeseries. // https://oss.redis.com/redistimeseries/commands/#tsinfo const tsInfo = await client.ts.info('mytimeseries'); - + // tsInfo looks like this: // { // totalSamples: 1440, diff --git a/index.ts b/index.ts index cb49e6f7f4d..5b5a6e81294 100644 --- a/index.ts +++ b/index.ts @@ -8,19 +8,19 @@ import { createCluster as _createCluster, RedisClusterOptions, RedisClusterType as _RedisClusterType -} from '@node-redis/client'; -import RedisBloomModules from '@node-redis/bloom'; -import RedisGraph from '@node-redis/graph'; -import RedisJSON from '@node-redis/json'; -import RediSearch from '@node-redis/search'; -import RedisTimeSeries from '@node-redis/time-series'; +} from '@redis/client'; +import RedisBloomModules from '@redis/bloom'; +import RedisGraph from '@redis/graph'; +import RedisJSON from '@redis/json'; +import RediSearch from '@redis/search'; +import RedisTimeSeries from '@redis/time-series'; -export * from '@node-redis/client'; -export * from '@node-redis/bloom'; -export * from '@node-redis/graph'; -export * from '@node-redis/json'; -export * from '@node-redis/search'; -export * from '@node-redis/time-series'; +export * from '@redis/client'; +export * from '@redis/bloom'; +export * from '@redis/graph'; +export * from '@redis/json'; +export * from '@redis/search'; +export * from '@redis/time-series'; const modules = { ...RedisBloomModules, diff --git a/package-lock.json b/package-lock.json index 460a6313fb8..376f417cf37 100644 --- a/package-lock.json +++ b/package-lock.json @@ -12,27 +12,28 @@ "./packages/*" ], "dependencies": { - "@node-redis/bloom": "1.0.1", - "@node-redis/client": "1.0.6", - "@node-redis/graph": "1.0.0", - "@node-redis/json": "1.0.2", - "@node-redis/search": "1.0.5", - "@node-redis/time-series": "1.0.2" + "@redis/bloom": "1.0.1", + "@redis/client": "1.0.6", + "@redis/graph": "1.0.0", + "@redis/json": "1.0.2", + "@redis/search": "1.0.5", + "@redis/time-series": "1.0.2" }, "devDependencies": { - "@tsconfig/node12": "^1.0.9", + "@tsconfig/node14": "^1.0.1", "gh-pages": "^3.2.3", - "release-it": "^14.14.2", - "typescript": "^4.6.3" + "release-it": "^15.0.0", + "typescript": "^4.6.4" } }, "node_modules/@ampproject/remapping": { - "version": "2.1.2", - "resolved": "https://registry.npmjs.org/@ampproject/remapping/-/remapping-2.1.2.tgz", - "integrity": "sha512-hoyByceqwKirw7w3Z7gnIIZC3Wx3J484Y3L/cMpXFbr7d9ZQj2mODrirNzcJa+SM3UlpWXYvKV4RlRpFXlWgXg==", + "version": "2.2.0", + "resolved": "https://registry.npmjs.org/@ampproject/remapping/-/remapping-2.2.0.tgz", + "integrity": "sha512-qRmjj8nj9qmLTQXXmaR1cck3UXSRMPrbsLJAasZpF+t3riI71BXed5ebIOYwQntykeZuhjsdweEc9BxH5Jc26w==", "dev": true, "dependencies": { - "@jridgewell/trace-mapping": "^0.3.0" + "@jridgewell/gen-mapping": "^0.1.0", + "@jridgewell/trace-mapping": "^0.3.9" }, "engines": { "node": ">=6.0.0" @@ -51,30 +52,30 @@ } }, "node_modules/@babel/compat-data": { - "version": "7.17.7", - "resolved": "https://registry.npmjs.org/@babel/compat-data/-/compat-data-7.17.7.tgz", - "integrity": "sha512-p8pdE6j0a29TNGebNm7NzYZWB3xVZJBZ7XGs42uAKzQo8VQ3F0By/cQCtUEABwIqw5zo6WA4NbmxsfzADzMKnQ==", + "version": "7.17.10", + "resolved": "https://registry.npmjs.org/@babel/compat-data/-/compat-data-7.17.10.tgz", + "integrity": "sha512-GZt/TCsG70Ms19gfZO1tM4CVnXsPgEPBCpJu+Qz3L0LUDsY5nZqFZglIoPC1kIYOtNBZlrnFT+klg12vFGZXrw==", "dev": true, "engines": { "node": ">=6.9.0" } }, "node_modules/@babel/core": { - "version": "7.17.9", - "resolved": "https://registry.npmjs.org/@babel/core/-/core-7.17.9.tgz", - "integrity": "sha512-5ug+SfZCpDAkVp9SFIZAzlW18rlzsOcJGaetCjkySnrXXDUw9AR8cDUm1iByTmdWM6yxX6/zycaV76w3YTF2gw==", + "version": "7.17.10", + "resolved": "https://registry.npmjs.org/@babel/core/-/core-7.17.10.tgz", + "integrity": "sha512-liKoppandF3ZcBnIYFjfSDHZLKdLHGJRkoWtG8zQyGJBQfIYobpnVGI5+pLBNtS6psFLDzyq8+h5HiVljW9PNA==", "dev": true, "dependencies": { "@ampproject/remapping": "^2.1.0", "@babel/code-frame": "^7.16.7", - "@babel/generator": "^7.17.9", - "@babel/helper-compilation-targets": "^7.17.7", + "@babel/generator": "^7.17.10", + "@babel/helper-compilation-targets": "^7.17.10", "@babel/helper-module-transforms": "^7.17.7", "@babel/helpers": "^7.17.9", - "@babel/parser": "^7.17.9", + "@babel/parser": "^7.17.10", "@babel/template": "^7.16.7", - "@babel/traverse": "^7.17.9", - "@babel/types": "^7.17.0", + "@babel/traverse": "^7.17.10", + "@babel/types": "^7.17.10", "convert-source-map": "^1.7.0", "debug": "^4.1.0", "gensync": "^1.0.0-beta.2", @@ -90,28 +91,28 @@ } }, "node_modules/@babel/generator": { - "version": "7.17.9", - "resolved": "https://registry.npmjs.org/@babel/generator/-/generator-7.17.9.tgz", - "integrity": "sha512-rAdDousTwxbIxbz5I7GEQ3lUip+xVCXooZNbsydCWs3xA7ZsYOv+CFRdzGxRX78BmQHu9B1Eso59AOZQOJDEdQ==", + "version": "7.17.10", + "resolved": "https://registry.npmjs.org/@babel/generator/-/generator-7.17.10.tgz", + "integrity": "sha512-46MJZZo9y3o4kmhBVc7zW7i8dtR1oIK/sdO5NcfcZRhTGYi+KKJRtHNgsU6c4VUcJmUNV/LQdebD/9Dlv4K+Tg==", "dev": true, "dependencies": { - "@babel/types": "^7.17.0", - "jsesc": "^2.5.1", - "source-map": "^0.5.0" + "@babel/types": "^7.17.10", + "@jridgewell/gen-mapping": "^0.1.0", + "jsesc": "^2.5.1" }, "engines": { "node": ">=6.9.0" } }, "node_modules/@babel/helper-compilation-targets": { - "version": "7.17.7", - "resolved": "https://registry.npmjs.org/@babel/helper-compilation-targets/-/helper-compilation-targets-7.17.7.tgz", - "integrity": "sha512-UFzlz2jjd8kroj0hmCFV5zr+tQPi1dpC2cRsDV/3IEW8bJfCPrPpmcSN6ZS8RqIq4LXcmpipCQFPddyFA5Yc7w==", + "version": "7.17.10", + "resolved": "https://registry.npmjs.org/@babel/helper-compilation-targets/-/helper-compilation-targets-7.17.10.tgz", + "integrity": "sha512-gh3RxjWbauw/dFiU/7whjd0qN9K6nPJMqe6+Er7rOavFh0CQUSwhAE3IcTho2rywPJFxej6TUUHDkWcYI6gGqQ==", "dev": true, "dependencies": { - "@babel/compat-data": "^7.17.7", + "@babel/compat-data": "^7.17.10", "@babel/helper-validator-option": "^7.16.7", - "browserslist": "^4.17.5", + "browserslist": "^4.20.2", "semver": "^6.3.0" }, "engines": { @@ -322,9 +323,9 @@ } }, "node_modules/@babel/parser": { - "version": "7.17.9", - "resolved": "https://registry.npmjs.org/@babel/parser/-/parser-7.17.9.tgz", - "integrity": "sha512-vqUSBLP8dQHFPdPi9bc5GK9vRkYHJ49fsZdtoJ8EQ8ibpwk5rPKfvNIwChB0KVXcIjcepEBBd2VHC5r9Gy8ueg==", + "version": "7.17.10", + "resolved": "https://registry.npmjs.org/@babel/parser/-/parser-7.17.10.tgz", + "integrity": "sha512-n2Q6i+fnJqzOaq2VkdXxy2TCPCWQZHiCo0XqmrCvDWcZQKRyZzYi4Z0yxlBuN0w+r2ZHmre+Q087DSrw3pbJDQ==", "dev": true, "bin": { "parser": "bin/babel-parser.js" @@ -348,19 +349,19 @@ } }, "node_modules/@babel/traverse": { - "version": "7.17.9", - "resolved": "https://registry.npmjs.org/@babel/traverse/-/traverse-7.17.9.tgz", - "integrity": "sha512-PQO8sDIJ8SIwipTPiR71kJQCKQYB5NGImbOviK8K+kg5xkNSYXLBupuX9QhatFowrsvo9Hj8WgArg3W7ijNAQw==", + "version": "7.17.10", + "resolved": "https://registry.npmjs.org/@babel/traverse/-/traverse-7.17.10.tgz", + "integrity": "sha512-VmbrTHQteIdUUQNTb+zE12SHS/xQVIShmBPhlNP12hD5poF2pbITW1Z4172d03HegaQWhLffdkRJYtAzp0AGcw==", "dev": true, "dependencies": { "@babel/code-frame": "^7.16.7", - "@babel/generator": "^7.17.9", + "@babel/generator": "^7.17.10", "@babel/helper-environment-visitor": "^7.16.7", "@babel/helper-function-name": "^7.17.9", "@babel/helper-hoist-variables": "^7.16.7", "@babel/helper-split-export-declaration": "^7.16.7", - "@babel/parser": "^7.17.9", - "@babel/types": "^7.17.0", + "@babel/parser": "^7.17.10", + "@babel/types": "^7.17.10", "debug": "^4.1.0", "globals": "^11.1.0" }, @@ -378,9 +379,9 @@ } }, "node_modules/@babel/types": { - "version": "7.17.0", - "resolved": "https://registry.npmjs.org/@babel/types/-/types-7.17.0.tgz", - "integrity": "sha512-TmKSNO4D5rzhL5bjWFcVHHLETzfQ/AmbKpKPOSjlP0WoHZ6L911fgoOKY4Alp/emzG4cHJdyN49zpgkbXFEHHw==", + "version": "7.17.10", + "resolved": "https://registry.npmjs.org/@babel/types/-/types-7.17.10.tgz", + "integrity": "sha512-9O26jG0mBYfGkUYCYZRnBwbVLd1UZOICEr2Em6InB6jVfsAv1GKgwXHmrSg+WFWDmeKTA6vyTZiN8tCSM5Oo3A==", "dev": true, "dependencies": { "@babel/helper-validator-identifier": "^7.16.7", @@ -431,18 +432,6 @@ "node": "^12.22.0 || ^14.17.0 || >=16.0.0" } }, - "node_modules/@eslint/eslintrc/node_modules/strip-json-comments": { - "version": "3.1.1", - "resolved": "https://registry.npmjs.org/strip-json-comments/-/strip-json-comments-3.1.1.tgz", - "integrity": "sha512-6fPc+R4ihwqP6N/aIv2f1gMH8lOVtWQHoqC4yK6oSDVVocumAsfCqjkXnqiYMhmMwS/mEHLp7Vehlt3ql6lEig==", - "dev": true, - "engines": { - "node": ">=8" - }, - "funding": { - "url": "https://github.com/sponsors/sindresorhus" - } - }, "node_modules/@humanwhocodes/config-array": { "version": "0.9.5", "resolved": "https://registry.npmjs.org/@humanwhocodes/config-array/-/config-array-0.9.5.tgz", @@ -494,15 +483,6 @@ "sprintf-js": "~1.0.2" } }, - "node_modules/@istanbuljs/load-nyc-config/node_modules/camelcase": { - "version": "5.3.1", - "resolved": "https://registry.npmjs.org/camelcase/-/camelcase-5.3.1.tgz", - "integrity": "sha512-L28STB170nwWS63UjtlEOE3dldQApaJXZkOI1uMFfzf3rRuPegHaHesyee+YxQ+W6SvRDQV6UrdOdRiR153wJg==", - "dev": true, - "engines": { - "node": ">=6" - } - }, "node_modules/@istanbuljs/load-nyc-config/node_modules/js-yaml": { "version": "3.14.1", "resolved": "https://registry.npmjs.org/js-yaml/-/js-yaml-3.14.1.tgz", @@ -549,6 +529,19 @@ "node": ">=8" } }, + "node_modules/@jridgewell/gen-mapping": { + "version": "0.1.1", + "resolved": "https://registry.npmjs.org/@jridgewell/gen-mapping/-/gen-mapping-0.1.1.tgz", + "integrity": "sha512-sQXCasFk+U8lWYEe66WxRDOE9PjVz4vSM51fTu3Hw+ClTpUSQb718772vH3pyS5pShp6lvQM7SxgIDXXXmOX7w==", + "dev": true, + "dependencies": { + "@jridgewell/set-array": "^1.0.0", + "@jridgewell/sourcemap-codec": "^1.4.10" + }, + "engines": { + "node": ">=6.0.0" + } + }, "node_modules/@jridgewell/resolve-uri": { "version": "3.0.6", "resolved": "https://registry.npmjs.org/@jridgewell/resolve-uri/-/resolve-uri-3.0.6.tgz", @@ -558,6 +551,15 @@ "node": ">=6.0.0" } }, + "node_modules/@jridgewell/set-array": { + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/@jridgewell/set-array/-/set-array-1.1.0.tgz", + "integrity": "sha512-SfJxIxNVYLTsKwzB3MoOQ1yxf4w/E6MdkvTgrgAt1bfxjSrLUoHMKrDOykwN14q65waezZIdqDneUIPh4/sKxg==", + "dev": true, + "engines": { + "node": ">=6.0.0" + } + }, "node_modules/@jridgewell/sourcemap-codec": { "version": "1.4.11", "resolved": "https://registry.npmjs.org/@jridgewell/sourcemap-codec/-/sourcemap-codec-1.4.11.tgz", @@ -574,34 +576,6 @@ "@jridgewell/sourcemap-codec": "^1.4.10" } }, - "node_modules/@node-redis/bloom": { - "resolved": "packages/bloom", - "link": true - }, - "node_modules/@node-redis/client": { - "resolved": "packages/client", - "link": true - }, - "node_modules/@node-redis/graph": { - "resolved": "packages/graph", - "link": true - }, - "node_modules/@node-redis/json": { - "resolved": "packages/json", - "link": true - }, - "node_modules/@node-redis/search": { - "resolved": "packages/search", - "link": true - }, - "node_modules/@node-redis/test-utils": { - "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", @@ -769,13 +743,44 @@ "@octokit/openapi-types": "^11.2.0" } }, + "node_modules/@redis/bloom": { + "resolved": "packages/bloom", + "link": true + }, + "node_modules/@redis/client": { + "resolved": "packages/client", + "link": true + }, + "node_modules/@redis/graph": { + "resolved": "packages/graph", + "link": true + }, + "node_modules/@redis/json": { + "resolved": "packages/json", + "link": true + }, + "node_modules/@redis/search": { + "resolved": "packages/search", + "link": true + }, + "node_modules/@redis/test-utils": { + "resolved": "packages/test-utils", + "link": true + }, + "node_modules/@redis/time-series": { + "resolved": "packages/time-series", + "link": true + }, "node_modules/@sindresorhus/is": { - "version": "0.14.0", - "resolved": "https://registry.npmjs.org/@sindresorhus/is/-/is-0.14.0.tgz", - "integrity": "sha512-9NET910DNaIPngYnLLPeg+Ogzqsi9uM4mSboU5y6p8S5DzMTVEsJZrawi+BoDNUVBa2DhJqQYUFvMDfgU062LQ==", + "version": "4.6.0", + "resolved": "https://registry.npmjs.org/@sindresorhus/is/-/is-4.6.0.tgz", + "integrity": "sha512-t09vSN3MdfsyCHoFcTRCH/iUtG7OJ0CsjzB8cjAmKc/va/kIgeDI/TxsigdncE/4be734m0cvIYwNaV4i2XqAw==", "dev": true, "engines": { - "node": ">=6" + "node": ">=10" + }, + "funding": { + "url": "https://github.com/sindresorhus/is?sponsor=1" } }, "node_modules/@sinonjs/commons": { @@ -814,15 +819,24 @@ "dev": true }, "node_modules/@szmarczak/http-timer": { - "version": "1.1.2", - "resolved": "https://registry.npmjs.org/@szmarczak/http-timer/-/http-timer-1.1.2.tgz", - "integrity": "sha512-XIB2XbzHTN6ieIjfIMV9hlVcfPU26s2vafYWQcZHWXHOxiaRZYEDKEwdl129Zyg50+foYV2jCgtrqSA6qNuNSA==", + "version": "5.0.1", + "resolved": "https://registry.npmjs.org/@szmarczak/http-timer/-/http-timer-5.0.1.tgz", + "integrity": "sha512-+PmQX0PiAYPMeVYe237LJAYvOMYW1j2rH5YROyS3b4CTVJum34HfRvKvAzozHAQG0TnHNdUfY9nCeUyRAs//cw==", "dev": true, "dependencies": { - "defer-to-connect": "^1.0.1" + "defer-to-connect": "^2.0.1" }, "engines": { - "node": ">=6" + "node": ">=14.16" + } + }, + "node_modules/@tootallnate/once": { + "version": "1.1.2", + "resolved": "https://registry.npmjs.org/@tootallnate/once/-/once-1.1.2.tgz", + "integrity": "sha512-RbzJvlNzmRq5c3O09UipeuXno4tA1FE6ikOjxZK0tuxVv3412l64l5t1W5pj4+rJq9vpkm/kwiR07aZXnsKPxw==", + "dev": true, + "engines": { + "node": ">= 6" } }, "node_modules/@tsconfig/node10": { @@ -849,12 +863,45 @@ "integrity": "sha512-eZxlbI8GZscaGS7kkc/trHTT5xgrjH3/1n2JDwusC9iahPKWMRvRjJSAN5mCXviuTGQ/lHnhvv8Q1YTpnfz9gA==", "dev": true }, + "node_modules/@types/cacheable-request": { + "version": "6.0.2", + "resolved": "https://registry.npmjs.org/@types/cacheable-request/-/cacheable-request-6.0.2.tgz", + "integrity": "sha512-B3xVo+dlKM6nnKTcmm5ZtY/OL8bOAOd2Olee9M1zft65ox50OzjEHW91sDiU9j6cvW8Ejg1/Qkf4xd2kugApUA==", + "dev": true, + "dependencies": { + "@types/http-cache-semantics": "*", + "@types/keyv": "*", + "@types/node": "*", + "@types/responselike": "*" + } + }, + "node_modules/@types/http-cache-semantics": { + "version": "4.0.1", + "resolved": "https://registry.npmjs.org/@types/http-cache-semantics/-/http-cache-semantics-4.0.1.tgz", + "integrity": "sha512-SZs7ekbP8CN0txVG2xVRH6EgKmEm31BOxA07vkFaETzZz1xh+cbt8BcI0slpymvwhx5dlFnQG2rTlPVQn+iRPQ==", + "dev": true + }, + "node_modules/@types/json-buffer": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/@types/json-buffer/-/json-buffer-3.0.0.tgz", + "integrity": "sha512-3YP80IxxFJB4b5tYC2SUPwkg0XQLiu0nWvhRgEatgjf+29IcWO9X1k8xRv5DGssJ/lCrjYTjQPcobJr2yWIVuQ==", + "dev": true + }, "node_modules/@types/json-schema": { "version": "7.0.11", "resolved": "https://registry.npmjs.org/@types/json-schema/-/json-schema-7.0.11.tgz", "integrity": "sha512-wOuvG1SN4Us4rez+tylwwwCV1psiNVOkJeM3AUWUNWg/jDQY2+HE/444y5gc+jBmRqASOm2Oeh5c1axHobwRKQ==", "dev": true }, + "node_modules/@types/keyv": { + "version": "3.1.4", + "resolved": "https://registry.npmjs.org/@types/keyv/-/keyv-3.1.4.tgz", + "integrity": "sha512-BQ5aZNSCpj7D6K2ksrRCTmKRLEpnPvWDiLPfoGyhZ++8YtiK9d/3DBKPJgry359X/P1PfruyYwvnvwFjuEiEIg==", + "dev": true, + "dependencies": { + "@types/node": "*" + } + }, "node_modules/@types/mocha": { "version": "9.1.1", "resolved": "https://registry.npmjs.org/@types/mocha/-/mocha-9.1.1.tgz", @@ -862,9 +909,9 @@ "dev": true }, "node_modules/@types/node": { - "version": "17.0.26", - "resolved": "https://registry.npmjs.org/@types/node/-/node-17.0.26.tgz", - "integrity": "sha512-z/FG/6DUO7pnze3AE3TBGIjGGKkvCcGcWINe1C7cADY8hKLJPDYpzsNE37uExQ4md5RFtTCvg+M8Mu1Enyeg2A==", + "version": "17.0.31", + "resolved": "https://registry.npmjs.org/@types/node/-/node-17.0.31.tgz", + "integrity": "sha512-AR0x5HbXGqkEx9CadRH3EBYx/VkiUgZIhP4wvPn/+5KIsgpNoyFaRlVe0Zlx9gRtg8fA06a9tskE2MSN7TcG4Q==", "dev": true }, "node_modules/@types/parse-json": { @@ -873,6 +920,15 @@ "integrity": "sha512-//oorEZjL6sbPcKUaCdIGlIUeH26mgzimjBB77G6XRgnDl/L5wOnpyBGRe/Mmf5CVW3PwEBE1NjiMZ/ssFh4wA==", "dev": true }, + "node_modules/@types/responselike": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/@types/responselike/-/responselike-1.0.0.tgz", + "integrity": "sha512-85Y2BjiufFzaMIlvJDvTTB8Fxl2xfLo4HgmHzVBz08w4wDePCTjYw66PdrolO0kzli3yam/YCgRufyo1DdQVTA==", + "dev": true, + "dependencies": { + "@types/node": "*" + } + }, "node_modules/@types/sinon": { "version": "10.0.11", "resolved": "https://registry.npmjs.org/@types/sinon/-/sinon-10.0.11.tgz", @@ -910,14 +966,14 @@ "dev": true }, "node_modules/@typescript-eslint/eslint-plugin": { - "version": "5.20.0", - "resolved": "https://registry.npmjs.org/@typescript-eslint/eslint-plugin/-/eslint-plugin-5.20.0.tgz", - "integrity": "sha512-fapGzoxilCn3sBtC6NtXZX6+P/Hef7VDbyfGqTTpzYydwhlkevB+0vE0EnmHPVTVSy68GUncyJ/2PcrFBeCo5Q==", + "version": "5.21.0", + "resolved": "https://registry.npmjs.org/@typescript-eslint/eslint-plugin/-/eslint-plugin-5.21.0.tgz", + "integrity": "sha512-fTU85q8v5ZLpoZEyn/u1S2qrFOhi33Edo2CZ0+q1gDaWWm0JuPh3bgOyU8lM0edIEYgKLDkPFiZX2MOupgjlyg==", "dev": true, "dependencies": { - "@typescript-eslint/scope-manager": "5.20.0", - "@typescript-eslint/type-utils": "5.20.0", - "@typescript-eslint/utils": "5.20.0", + "@typescript-eslint/scope-manager": "5.21.0", + "@typescript-eslint/type-utils": "5.21.0", + "@typescript-eslint/utils": "5.21.0", "debug": "^4.3.2", "functional-red-black-tree": "^1.0.1", "ignore": "^5.1.8", @@ -942,6 +998,18 @@ } } }, + "node_modules/@typescript-eslint/eslint-plugin/node_modules/lru-cache": { + "version": "6.0.0", + "resolved": "https://registry.npmjs.org/lru-cache/-/lru-cache-6.0.0.tgz", + "integrity": "sha512-Jo6dJ04CmSjuznwJSS3pUeWmd/H0ffTlkXXgwZi+eq1UCmqQwCh+eLsYOYCwY991i2Fah4h1BEMCx4qThGbsiA==", + "dev": true, + "dependencies": { + "yallist": "^4.0.0" + }, + "engines": { + "node": ">=10" + } + }, "node_modules/@typescript-eslint/eslint-plugin/node_modules/semver": { "version": "7.3.7", "resolved": "https://registry.npmjs.org/semver/-/semver-7.3.7.tgz", @@ -957,15 +1025,21 @@ "node": ">=10" } }, + "node_modules/@typescript-eslint/eslint-plugin/node_modules/yallist": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/yallist/-/yallist-4.0.0.tgz", + "integrity": "sha512-3wdGidZyq5PB084XLES5TpOSRA3wjXAlIWMhum2kRcv/41Sn2emQ0dycQW4uZXLejwKvg6EsvbdlVL+FYEct7A==", + "dev": true + }, "node_modules/@typescript-eslint/parser": { - "version": "5.20.0", - "resolved": "https://registry.npmjs.org/@typescript-eslint/parser/-/parser-5.20.0.tgz", - "integrity": "sha512-UWKibrCZQCYvobmu3/N8TWbEeo/EPQbS41Ux1F9XqPzGuV7pfg6n50ZrFo6hryynD8qOTTfLHtHjjdQtxJ0h/w==", + "version": "5.21.0", + "resolved": "https://registry.npmjs.org/@typescript-eslint/parser/-/parser-5.21.0.tgz", + "integrity": "sha512-8RUwTO77hstXUr3pZoWZbRQUxXcSXafZ8/5gpnQCfXvgmP9gpNlRGlWzvfbEQ14TLjmtU8eGnONkff8U2ui2Eg==", "dev": true, "dependencies": { - "@typescript-eslint/scope-manager": "5.20.0", - "@typescript-eslint/types": "5.20.0", - "@typescript-eslint/typescript-estree": "5.20.0", + "@typescript-eslint/scope-manager": "5.21.0", + "@typescript-eslint/types": "5.21.0", + "@typescript-eslint/typescript-estree": "5.21.0", "debug": "^4.3.2" }, "engines": { @@ -985,13 +1059,13 @@ } }, "node_modules/@typescript-eslint/scope-manager": { - "version": "5.20.0", - "resolved": "https://registry.npmjs.org/@typescript-eslint/scope-manager/-/scope-manager-5.20.0.tgz", - "integrity": "sha512-h9KtuPZ4D/JuX7rpp1iKg3zOH0WNEa+ZIXwpW/KWmEFDxlA/HSfCMhiyF1HS/drTICjIbpA6OqkAhrP/zkCStg==", + "version": "5.21.0", + "resolved": "https://registry.npmjs.org/@typescript-eslint/scope-manager/-/scope-manager-5.21.0.tgz", + "integrity": "sha512-XTX0g0IhvzcH/e3393SvjRCfYQxgxtYzL3UREteUneo72EFlt7UNoiYnikUtmGVobTbhUDByhJ4xRBNe+34kOQ==", "dev": true, "dependencies": { - "@typescript-eslint/types": "5.20.0", - "@typescript-eslint/visitor-keys": "5.20.0" + "@typescript-eslint/types": "5.21.0", + "@typescript-eslint/visitor-keys": "5.21.0" }, "engines": { "node": "^12.22.0 || ^14.17.0 || >=16.0.0" @@ -1002,12 +1076,12 @@ } }, "node_modules/@typescript-eslint/type-utils": { - "version": "5.20.0", - "resolved": "https://registry.npmjs.org/@typescript-eslint/type-utils/-/type-utils-5.20.0.tgz", - "integrity": "sha512-WxNrCwYB3N/m8ceyoGCgbLmuZwupvzN0rE8NBuwnl7APgjv24ZJIjkNzoFBXPRCGzLNkoU/WfanW0exvp/+3Iw==", + "version": "5.21.0", + "resolved": "https://registry.npmjs.org/@typescript-eslint/type-utils/-/type-utils-5.21.0.tgz", + "integrity": "sha512-MxmLZj0tkGlkcZCSE17ORaHl8Th3JQwBzyXL/uvC6sNmu128LsgjTX0NIzy+wdH2J7Pd02GN8FaoudJntFvSOw==", "dev": true, "dependencies": { - "@typescript-eslint/utils": "5.20.0", + "@typescript-eslint/utils": "5.21.0", "debug": "^4.3.2", "tsutils": "^3.21.0" }, @@ -1028,9 +1102,9 @@ } }, "node_modules/@typescript-eslint/types": { - "version": "5.20.0", - "resolved": "https://registry.npmjs.org/@typescript-eslint/types/-/types-5.20.0.tgz", - "integrity": "sha512-+d8wprF9GyvPwtoB4CxBAR/s0rpP25XKgnOvMf/gMXYDvlUC3rPFHupdTQ/ow9vn7UDe5rX02ovGYQbv/IUCbg==", + "version": "5.21.0", + "resolved": "https://registry.npmjs.org/@typescript-eslint/types/-/types-5.21.0.tgz", + "integrity": "sha512-XnOOo5Wc2cBlq8Lh5WNvAgHzpjnEzxn4CJBwGkcau7b/tZ556qrWXQz4DJyChYg8JZAD06kczrdgFPpEQZfDsA==", "dev": true, "engines": { "node": "^12.22.0 || ^14.17.0 || >=16.0.0" @@ -1041,13 +1115,13 @@ } }, "node_modules/@typescript-eslint/typescript-estree": { - "version": "5.20.0", - "resolved": "https://registry.npmjs.org/@typescript-eslint/typescript-estree/-/typescript-estree-5.20.0.tgz", - "integrity": "sha512-36xLjP/+bXusLMrT9fMMYy1KJAGgHhlER2TqpUVDYUQg4w0q/NW/sg4UGAgVwAqb8V4zYg43KMUpM8vV2lve6w==", + "version": "5.21.0", + "resolved": "https://registry.npmjs.org/@typescript-eslint/typescript-estree/-/typescript-estree-5.21.0.tgz", + "integrity": "sha512-Y8Y2T2FNvm08qlcoSMoNchh9y2Uj3QmjtwNMdRQkcFG7Muz//wfJBGBxh8R7HAGQFpgYpdHqUpEoPQk+q9Kjfg==", "dev": true, "dependencies": { - "@typescript-eslint/types": "5.20.0", - "@typescript-eslint/visitor-keys": "5.20.0", + "@typescript-eslint/types": "5.21.0", + "@typescript-eslint/visitor-keys": "5.21.0", "debug": "^4.3.2", "globby": "^11.0.4", "is-glob": "^4.0.3", @@ -1096,6 +1170,18 @@ "url": "https://github.com/sponsors/sindresorhus" } }, + "node_modules/@typescript-eslint/typescript-estree/node_modules/lru-cache": { + "version": "6.0.0", + "resolved": "https://registry.npmjs.org/lru-cache/-/lru-cache-6.0.0.tgz", + "integrity": "sha512-Jo6dJ04CmSjuznwJSS3pUeWmd/H0ffTlkXXgwZi+eq1UCmqQwCh+eLsYOYCwY991i2Fah4h1BEMCx4qThGbsiA==", + "dev": true, + "dependencies": { + "yallist": "^4.0.0" + }, + "engines": { + "node": ">=10" + } + }, "node_modules/@typescript-eslint/typescript-estree/node_modules/semver": { "version": "7.3.7", "resolved": "https://registry.npmjs.org/semver/-/semver-7.3.7.tgz", @@ -1111,16 +1197,22 @@ "node": ">=10" } }, + "node_modules/@typescript-eslint/typescript-estree/node_modules/yallist": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/yallist/-/yallist-4.0.0.tgz", + "integrity": "sha512-3wdGidZyq5PB084XLES5TpOSRA3wjXAlIWMhum2kRcv/41Sn2emQ0dycQW4uZXLejwKvg6EsvbdlVL+FYEct7A==", + "dev": true + }, "node_modules/@typescript-eslint/utils": { - "version": "5.20.0", - "resolved": "https://registry.npmjs.org/@typescript-eslint/utils/-/utils-5.20.0.tgz", - "integrity": "sha512-lHONGJL1LIO12Ujyx8L8xKbwWSkoUKFSO+0wDAqGXiudWB2EO7WEUT+YZLtVbmOmSllAjLb9tpoIPwpRe5Tn6w==", + "version": "5.21.0", + "resolved": "https://registry.npmjs.org/@typescript-eslint/utils/-/utils-5.21.0.tgz", + "integrity": "sha512-q/emogbND9wry7zxy7VYri+7ydawo2HDZhRZ5k6yggIvXa7PvBbAAZ4PFH/oZLem72ezC4Pr63rJvDK/sTlL8Q==", "dev": true, "dependencies": { "@types/json-schema": "^7.0.9", - "@typescript-eslint/scope-manager": "5.20.0", - "@typescript-eslint/types": "5.20.0", - "@typescript-eslint/typescript-estree": "5.20.0", + "@typescript-eslint/scope-manager": "5.21.0", + "@typescript-eslint/types": "5.21.0", + "@typescript-eslint/typescript-estree": "5.21.0", "eslint-scope": "^5.1.1", "eslint-utils": "^3.0.0" }, @@ -1136,12 +1228,12 @@ } }, "node_modules/@typescript-eslint/visitor-keys": { - "version": "5.20.0", - "resolved": "https://registry.npmjs.org/@typescript-eslint/visitor-keys/-/visitor-keys-5.20.0.tgz", - "integrity": "sha512-1flRpNF+0CAQkMNlTJ6L/Z5jiODG/e5+7mk6XwtPOUS3UrTz3UOiAg9jG2VtKsWI6rZQfy4C6a232QNRZTRGlg==", + "version": "5.21.0", + "resolved": "https://registry.npmjs.org/@typescript-eslint/visitor-keys/-/visitor-keys-5.21.0.tgz", + "integrity": "sha512-SX8jNN+iHqAF0riZQMkm7e8+POXa/fXw5cxL+gjpyP+FI+JVNhii53EmQgDAfDcBpFekYSlO0fGytMQwRiMQCA==", "dev": true, "dependencies": { - "@typescript-eslint/types": "5.20.0", + "@typescript-eslint/types": "5.21.0", "eslint-visitor-keys": "^3.0.0" }, "engines": { @@ -1159,9 +1251,9 @@ "dev": true }, "node_modules/acorn": { - "version": "8.7.0", - "resolved": "https://registry.npmjs.org/acorn/-/acorn-8.7.0.tgz", - "integrity": "sha512-V/LGr1APy+PXIwKebEWrkZPwoeoF+w1jiOBUmuxuiUIaOHtob8Qc9BTrYo7VuI5fR8tqsy+buA2WFooR5olqvQ==", + "version": "8.7.1", + "resolved": "https://registry.npmjs.org/acorn/-/acorn-8.7.1.tgz", + "integrity": "sha512-Xx54uLJQZ19lKygFXOWsscKUbsBZW0CPykPhVQdhIeIwrbPmJzqeASDInc8nKBnp/JT6igTs82qPXz069H8I/A==", "dev": true, "bin": { "acorn": "bin/acorn" @@ -1188,6 +1280,18 @@ "node": ">=0.4.0" } }, + "node_modules/agent-base": { + "version": "6.0.2", + "resolved": "https://registry.npmjs.org/agent-base/-/agent-base-6.0.2.tgz", + "integrity": "sha512-RZNwNclF7+MS/8bDg70amg32dyeZGZxiDuQmZxKLAlQjr3jGyLx+4Kkk58UO7D2QdgFIQCovuSuZESne6RG6XQ==", + "dev": true, + "dependencies": { + "debug": "4" + }, + "engines": { + "node": ">= 6.0.0" + } + }, "node_modules/aggregate-error": { "version": "3.1.0", "resolved": "https://registry.npmjs.org/aggregate-error/-/aggregate-error-3.1.0.tgz", @@ -1357,6 +1461,18 @@ "url": "https://github.com/sponsors/ljharb" } }, + "node_modules/ast-types": { + "version": "0.13.4", + "resolved": "https://registry.npmjs.org/ast-types/-/ast-types-0.13.4.tgz", + "integrity": "sha512-x1FCFnFifvYDDzTaLII71vG5uvDwgtmDTEVWAxrgeiR8VjMONcCXJx7E+USjDtHlwFmt9MysbqgF9b9Vjr6w+w==", + "dev": true, + "dependencies": { + "tslib": "^2.0.1" + }, + "engines": { + "node": ">=4" + } + }, "node_modules/async": { "version": "2.6.4", "resolved": "https://registry.npmjs.org/async/-/async-2.6.4.tgz", @@ -1423,12 +1539,12 @@ } }, "node_modules/bl": { - "version": "4.1.0", - "resolved": "https://registry.npmjs.org/bl/-/bl-4.1.0.tgz", - "integrity": "sha512-1W07cM9gS6DcLperZfFSj+bWLtaPGSOHWhPiGzXmvVJbRLdG82sH/Kn8EtW1VqWVA54AKf2h5k5BbnIbwF3h6w==", + "version": "5.0.0", + "resolved": "https://registry.npmjs.org/bl/-/bl-5.0.0.tgz", + "integrity": "sha512-8vxFNZ0pflFfi0WXA3WQXlj6CaMEwsmh63I1CNp0q+wWv8sD0ARx1KovSQd0l2GkwrMIOyedq0EF1FxI+RCZLQ==", "dev": true, "dependencies": { - "buffer": "^5.5.0", + "buffer": "^6.0.3", "inherits": "^2.0.4", "readable-stream": "^3.4.0" } @@ -1455,6 +1571,34 @@ "url": "https://github.com/sponsors/sindresorhus" } }, + "node_modules/boxen/node_modules/camelcase": { + "version": "6.3.0", + "resolved": "https://registry.npmjs.org/camelcase/-/camelcase-6.3.0.tgz", + "integrity": "sha512-Gmy6FhYlCY7uOElZUSbxo2UCDH8owEk996gkbrpsgGtrJLM3J7jGxl9Ic7Qwwj4ivOE5AWZWRMecDdF7hqGjFA==", + "dev": true, + "engines": { + "node": ">=10" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/boxen/node_modules/chalk": { + "version": "4.1.2", + "resolved": "https://registry.npmjs.org/chalk/-/chalk-4.1.2.tgz", + "integrity": "sha512-oKnbhFyRIXpUuez8iBMmyEa4nbj4IOQyuhc/wy9kY7/WVPcwIO9VA668Pu8RkO7+0G76SLROeyw9CpQ061i4mA==", + "dev": true, + "dependencies": { + "ansi-styles": "^4.1.0", + "supports-color": "^7.1.0" + }, + "engines": { + "node": ">=10" + }, + "funding": { + "url": "https://github.com/chalk/chalk?sponsor=1" + } + }, "node_modules/boxen/node_modules/type-fest": { "version": "0.20.2", "resolved": "https://registry.npmjs.org/type-fest/-/type-fest-0.20.2.tgz", @@ -1525,9 +1669,9 @@ } }, "node_modules/buffer": { - "version": "5.7.1", - "resolved": "https://registry.npmjs.org/buffer/-/buffer-5.7.1.tgz", - "integrity": "sha512-EHcyIPBQ4BSGlvjB16k5KgAJ27CIsHY/2JBmCRReo48y9rQ3MaUzWX3KVlBa4U7MyX02HdVj0K7C3WaB3ju7FQ==", + "version": "6.0.3", + "resolved": "https://registry.npmjs.org/buffer/-/buffer-6.0.3.tgz", + "integrity": "sha512-FTiCpNxtwiZZHEZbcbTIcZjERVICn9yq/pDFkTl95/AxzD1naBctN7YO68riM/gLSDY7sdrMby8hofADYuuqOA==", "dev": true, "funding": [ { @@ -1545,7 +1689,7 @@ ], "dependencies": { "base64-js": "^1.3.1", - "ieee754": "^1.1.13" + "ieee754": "^1.2.1" } }, "node_modules/buffer-from": { @@ -1554,19 +1698,37 @@ "integrity": "sha512-E+XQCRwSbaaiChtv6k6Dwgc+bx+Bs6vuKJHHl5kox/BaKbhiXzqQOwK4cO22yElGp2OCmjwVhT3HmxgyPGnJfQ==", "dev": true }, + "node_modules/bytes": { + "version": "3.1.2", + "resolved": "https://registry.npmjs.org/bytes/-/bytes-3.1.2.tgz", + "integrity": "sha512-/Nf7TyzTx6S3yRJObOAV7956r8cr2+Oj8AC5dt8wSP3BQAoeX58NoHyCU8P8zGkNXStjTSi6fzO6F0pBdcYbEg==", + "dev": true, + "engines": { + "node": ">= 0.8" + } + }, + "node_modules/cacheable-lookup": { + "version": "6.0.4", + "resolved": "https://registry.npmjs.org/cacheable-lookup/-/cacheable-lookup-6.0.4.tgz", + "integrity": "sha512-mbcDEZCkv2CZF4G01kr8eBd/5agkt9oCqz75tJMSIsquvRZ2sL6Hi5zGVKi/0OSC9oO1GHfJ2AV0ZIOY9vye0A==", + "dev": true, + "engines": { + "node": ">=10.6.0" + } + }, "node_modules/cacheable-request": { - "version": "6.1.0", - "resolved": "https://registry.npmjs.org/cacheable-request/-/cacheable-request-6.1.0.tgz", - "integrity": "sha512-Oj3cAGPCqOZX7Rz64Uny2GYAZNliQSqfbePrgAQ1wKAihYmCUnraBtJtKcGR4xz7wF+LoJC+ssFZvv5BgF9Igg==", + "version": "7.0.2", + "resolved": "https://registry.npmjs.org/cacheable-request/-/cacheable-request-7.0.2.tgz", + "integrity": "sha512-pouW8/FmiPQbuGpkXQ9BAPv/Mo5xDGANgSNXzTzJ8DrKGuXOssM4wIQRjfanNRh3Yu5cfYPvcorqbhg2KIJtew==", "dev": true, "dependencies": { "clone-response": "^1.0.2", "get-stream": "^5.1.0", "http-cache-semantics": "^4.0.0", - "keyv": "^3.0.0", + "keyv": "^4.0.0", "lowercase-keys": "^2.0.0", - "normalize-url": "^4.1.0", - "responselike": "^1.0.2" + "normalize-url": "^6.0.1", + "responselike": "^2.0.0" }, "engines": { "node": ">=8" @@ -1634,21 +1796,18 @@ } }, "node_modules/camelcase": { - "version": "6.3.0", - "resolved": "https://registry.npmjs.org/camelcase/-/camelcase-6.3.0.tgz", - "integrity": "sha512-Gmy6FhYlCY7uOElZUSbxo2UCDH8owEk996gkbrpsgGtrJLM3J7jGxl9Ic7Qwwj4ivOE5AWZWRMecDdF7hqGjFA==", + "version": "5.3.1", + "resolved": "https://registry.npmjs.org/camelcase/-/camelcase-5.3.1.tgz", + "integrity": "sha512-L28STB170nwWS63UjtlEOE3dldQApaJXZkOI1uMFfzf3rRuPegHaHesyee+YxQ+W6SvRDQV6UrdOdRiR153wJg==", "dev": true, "engines": { - "node": ">=10" - }, - "funding": { - "url": "https://github.com/sponsors/sindresorhus" + "node": ">=6" } }, "node_modules/caniuse-lite": { - "version": "1.0.30001332", - "resolved": "https://registry.npmjs.org/caniuse-lite/-/caniuse-lite-1.0.30001332.tgz", - "integrity": "sha512-10T30NYOEQtN6C11YGg411yebhvpnC6Z102+B95eAsN0oB6KUs01ivE8u+G6FMIRtIrVlYXhL+LUwQ3/hXwDWw==", + "version": "1.0.30001335", + "resolved": "https://registry.npmjs.org/caniuse-lite/-/caniuse-lite-1.0.30001335.tgz", + "integrity": "sha512-ddP1Tgm7z2iIxu6QTtbZUv6HJxSaV/PZeSrWFZtbY4JZ69tOeNhBCl3HyRQgeNZKE5AOn1kpV7fhljigy0Ty3w==", "dev": true, "funding": [ { @@ -1662,16 +1821,12 @@ ] }, "node_modules/chalk": { - "version": "4.1.2", - "resolved": "https://registry.npmjs.org/chalk/-/chalk-4.1.2.tgz", - "integrity": "sha512-oKnbhFyRIXpUuez8iBMmyEa4nbj4IOQyuhc/wy9kY7/WVPcwIO9VA668Pu8RkO7+0G76SLROeyw9CpQ061i4mA==", + "version": "5.0.1", + "resolved": "https://registry.npmjs.org/chalk/-/chalk-5.0.1.tgz", + "integrity": "sha512-Fo07WOYGqMfCWHOzSXOt2CxDbC6skS/jO9ynEcmpANMoPrD+W1r1K6Vx7iNm+AQmETU1Xr2t+n8nzkV9t6xh3w==", "dev": true, - "dependencies": { - "ansi-styles": "^4.1.0", - "supports-color": "^7.1.0" - }, "engines": { - "node": ">=10" + "node": "^12.17.0 || ^14.13 || >=16.0.0" }, "funding": { "url": "https://github.com/chalk/chalk?sponsor=1" @@ -1710,6 +1865,18 @@ "fsevents": "~2.3.2" } }, + "node_modules/chokidar/node_modules/glob-parent": { + "version": "5.1.2", + "resolved": "https://registry.npmjs.org/glob-parent/-/glob-parent-5.1.2.tgz", + "integrity": "sha512-AOIgSQCepiJYwP3ARnGx+5VnTu2HBYdzbGP45eLw1vr3zB3vZLeyed1sC9hnbcOc9/SrMyM5RPQrkGz4aS9Zow==", + "dev": true, + "dependencies": { + "is-glob": "^4.0.1" + }, + "engines": { + "node": ">= 6" + } + }, "node_modules/ci-info": { "version": "3.3.0", "resolved": "https://registry.npmjs.org/ci-info/-/ci-info-3.3.0.tgz", @@ -1771,14 +1938,28 @@ } }, "node_modules/cliui": { - "version": "7.0.4", - "resolved": "https://registry.npmjs.org/cliui/-/cliui-7.0.4.tgz", - "integrity": "sha512-OcRE68cOsVMXp1Yvonl/fzkQOyjLSu/8bhPDfQt0e0/Eb283TKP20Fs2MqoPsr9SwA595rRCA+QMzYc9nBP+JQ==", - "dev": true, - "dependencies": { + "version": "6.0.0", + "resolved": "https://registry.npmjs.org/cliui/-/cliui-6.0.0.tgz", + "integrity": "sha512-t6wbgtoCXvAzst7QgXxJYqPt0usEfbgQdftEPbLL/cvv6HPE5VgvqCuAIDR0NgU52ds6rFwqrgakNLrHEjCbrQ==", + "dev": true, + "dependencies": { "string-width": "^4.2.0", "strip-ansi": "^6.0.0", - "wrap-ansi": "^7.0.0" + "wrap-ansi": "^6.2.0" + } + }, + "node_modules/cliui/node_modules/wrap-ansi": { + "version": "6.2.0", + "resolved": "https://registry.npmjs.org/wrap-ansi/-/wrap-ansi-6.2.0.tgz", + "integrity": "sha512-r6lPcBGxZXlIcymEu7InxDMhdW0KDxpLgoFLcguasxCaJ/SOIZwINatK9KY/tf+ZrlywOKU0UDj3ATXUBfxJXA==", + "dev": true, + "dependencies": { + "ansi-styles": "^4.0.0", + "string-width": "^4.1.0", + "strip-ansi": "^6.0.0" + }, + "engines": { + "node": ">=8" } }, "node_modules/clone": { @@ -1849,6 +2030,19 @@ "integrity": "sha1-3dgA2gxmEnOTzKWVDqloo6rxJTs=", "dev": true }, + "node_modules/compress-brotli": { + "version": "1.3.6", + "resolved": "https://registry.npmjs.org/compress-brotli/-/compress-brotli-1.3.6.tgz", + "integrity": "sha512-au99/GqZtUtiCBliqLFbWlhnCxn+XSYjwZ77q6mKN4La4qOXDoLVPZ50iXr0WmAyMxl8yqoq3Yq4OeQNPPkyeQ==", + "dev": true, + "dependencies": { + "@types/json-buffer": "~3.0.0", + "json-buffer": "~3.0.1" + }, + "engines": { + "node": ">= 12" + } + }, "node_modules/concat-map": { "version": "0.0.1", "resolved": "https://registry.npmjs.org/concat-map/-/concat-map-0.0.1.tgz", @@ -1881,10 +2075,10 @@ "safe-buffer": "~5.1.1" } }, - "node_modules/convert-source-map/node_modules/safe-buffer": { - "version": "5.1.2", - "resolved": "https://registry.npmjs.org/safe-buffer/-/safe-buffer-5.1.2.tgz", - "integrity": "sha512-Gd2UZBJDkXlY7GbJxfsE8/nvKkUEU1G38c1siN6QP6a9PT9MmHB8GnpscSmMJSoF8LOIrt8ud/wPtojys4G6+g==", + "node_modules/core-util-is": { + "version": "1.0.3", + "resolved": "https://registry.npmjs.org/core-util-is/-/core-util-is-1.0.3.tgz", + "integrity": "sha512-ZQBvi1DcpJ4GDqanjucZ2Hj3wEO5pZDS89BWbkcrvdxksJorwUDDZamX9ldFkp9aw2lmBDLgkObEA4DWNJ9FYQ==", "dev": true }, "node_modules/cosmiconfig": { @@ -1932,6 +2126,15 @@ "node": ">=8" } }, + "node_modules/data-uri-to-buffer": { + "version": "3.0.1", + "resolved": "https://registry.npmjs.org/data-uri-to-buffer/-/data-uri-to-buffer-3.0.1.tgz", + "integrity": "sha512-WboRycPNsVw3B3TL559F7kuBUM4d8CgMEvk6xEJlOp7OBPjt6G7z8WMWlD2rOFZLk6OYfFIUGsCOWzcQH9K2og==", + "dev": true, + "engines": { + "node": ">= 6" + } + }, "node_modules/debug": { "version": "4.3.4", "resolved": "https://registry.npmjs.org/debug/-/debug-4.3.4.tgz", @@ -1968,15 +2171,30 @@ } }, "node_modules/decompress-response": { - "version": "3.3.0", - "resolved": "https://registry.npmjs.org/decompress-response/-/decompress-response-3.3.0.tgz", - "integrity": "sha1-gKTdMjdIOEv6JICDYirt7Jgq3/M=", + "version": "6.0.0", + "resolved": "https://registry.npmjs.org/decompress-response/-/decompress-response-6.0.0.tgz", + "integrity": "sha512-aW35yZM6Bb/4oJlZncMH2LCoZtJXTRxES17vE3hoRiowU2kWHaJKFkSBDnDR+cm9J+9QhXmREyIfv0pji9ejCQ==", "dev": true, "dependencies": { - "mimic-response": "^1.0.0" + "mimic-response": "^3.1.0" }, "engines": { - "node": ">=4" + "node": ">=10" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/decompress-response/node_modules/mimic-response": { + "version": "3.1.0", + "resolved": "https://registry.npmjs.org/mimic-response/-/mimic-response-3.1.0.tgz", + "integrity": "sha512-z0yWI+4FDrrweS8Zmt4Ej5HdJmky15+L2e6Wgn3+iK5fWzb6T3fhNFq2+MeTRb064c6Wr4N/wv0DzQTjNzHNGQ==", + "dev": true, + "engines": { + "node": ">=10" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" } }, "node_modules/deep-extend": { @@ -2016,10 +2234,22 @@ } }, "node_modules/defer-to-connect": { - "version": "1.1.3", - "resolved": "https://registry.npmjs.org/defer-to-connect/-/defer-to-connect-1.1.3.tgz", - "integrity": "sha512-0ISdNousHvZT2EiFlZeZAHBUvSxmKswVCEf8hW7KWgG4a8MVEu/3Vb6uWYozkjylyCxe0JBIiRB1jV45S70WVQ==", - "dev": true + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/defer-to-connect/-/defer-to-connect-2.0.1.tgz", + "integrity": "sha512-4tvttepXG1VaYGrRibk5EwJd1t4udunSOVMdLSAL6mId1ix438oPwPZMALY41FCijukO1L0twNcGsdzS7dHgDg==", + "dev": true, + "engines": { + "node": ">=10" + } + }, + "node_modules/define-lazy-prop": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/define-lazy-prop/-/define-lazy-prop-2.0.0.tgz", + "integrity": "sha512-Ds09qNh8yw3khSjiJjiUInaGX9xlqZDY7JVryGxdxV7NPeuqQfplOpQ66yJFZut3jLa5zOwkXw1g9EI2uKh4Og==", + "dev": true, + "engines": { + "node": ">=8" + } }, "node_modules/define-properties": { "version": "1.1.4", @@ -2037,6 +2267,21 @@ "url": "https://github.com/sponsors/ljharb" } }, + "node_modules/degenerator": { + "version": "3.0.2", + "resolved": "https://registry.npmjs.org/degenerator/-/degenerator-3.0.2.tgz", + "integrity": "sha512-c0mef3SNQo56t6urUU6tdQAs+ThoD0o9B9MJ8HEt7NQcGEILCRFqQb7ZbP9JAv+QF1Ky5plydhMR/IrqWDm+TQ==", + "dev": true, + "dependencies": { + "ast-types": "^0.13.2", + "escodegen": "^1.8.1", + "esprima": "^4.0.0", + "vm2": "^3.9.8" + }, + "engines": { + "node": ">= 6" + } + }, "node_modules/delayed-stream": { "version": "1.0.0", "resolved": "https://registry.npmjs.org/delayed-stream/-/delayed-stream-1.0.0.tgz", @@ -2046,6 +2291,15 @@ "node": ">=0.4.0" } }, + "node_modules/depd": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/depd/-/depd-2.0.0.tgz", + "integrity": "sha512-g7nH6P6dyDioJogAAGprGpCtVImJhpPk/roCzdb3fIh61/s/nPsfR6onyMwkCAR/OlC3yBC0lESvUoQEAssIrw==", + "dev": true, + "engines": { + "node": ">= 0.8" + } + }, "node_modules/deprecation": { "version": "2.3.1", "resolved": "https://registry.npmjs.org/deprecation/-/deprecation-2.3.1.tgz", @@ -2104,9 +2358,9 @@ "dev": true }, "node_modules/electron-to-chromium": { - "version": "1.4.118", - "resolved": "https://registry.npmjs.org/electron-to-chromium/-/electron-to-chromium-1.4.118.tgz", - "integrity": "sha512-maZIKjnYDvF7Fs35nvVcyr44UcKNwybr93Oba2n3HkKDFAtk0svERkLN/HyczJDS3Fo4wU9th9fUQd09ZLtj1w==", + "version": "1.4.129", + "resolved": "https://registry.npmjs.org/electron-to-chromium/-/electron-to-chromium-1.4.129.tgz", + "integrity": "sha512-GgtN6bsDtHdtXJtlMYZWGB/uOyjZWjmRDumXTas7dGBaB9zUyCjzHet1DY2KhyHN8R0GLbzZWqm4efeddqqyRQ==", "dev": true }, "node_modules/email-addresses": { @@ -2248,6 +2502,79 @@ "node": ">=0.8.0" } }, + "node_modules/escodegen": { + "version": "1.14.3", + "resolved": "https://registry.npmjs.org/escodegen/-/escodegen-1.14.3.tgz", + "integrity": "sha512-qFcX0XJkdg+PB3xjZZG/wKSuT1PnQWx57+TVSjIMmILd2yC/6ByYElPwJnslDsuWuSAp4AwJGumarAAmJch5Kw==", + "dev": true, + "dependencies": { + "esprima": "^4.0.1", + "estraverse": "^4.2.0", + "esutils": "^2.0.2", + "optionator": "^0.8.1" + }, + "bin": { + "escodegen": "bin/escodegen.js", + "esgenerate": "bin/esgenerate.js" + }, + "engines": { + "node": ">=4.0" + }, + "optionalDependencies": { + "source-map": "~0.6.1" + } + }, + "node_modules/escodegen/node_modules/levn": { + "version": "0.3.0", + "resolved": "https://registry.npmjs.org/levn/-/levn-0.3.0.tgz", + "integrity": "sha1-OwmSTt+fCDwEkP3UwLxEIeBHZO4=", + "dev": true, + "dependencies": { + "prelude-ls": "~1.1.2", + "type-check": "~0.3.2" + }, + "engines": { + "node": ">= 0.8.0" + } + }, + "node_modules/escodegen/node_modules/optionator": { + "version": "0.8.3", + "resolved": "https://registry.npmjs.org/optionator/-/optionator-0.8.3.tgz", + "integrity": "sha512-+IW9pACdk3XWmmTXG8m3upGUJst5XRGzxMRjXzAuJ1XnIFNvfhjjIuYkDvysnPQ7qzqVzLt78BCruntqRhWQbA==", + "dev": true, + "dependencies": { + "deep-is": "~0.1.3", + "fast-levenshtein": "~2.0.6", + "levn": "~0.3.0", + "prelude-ls": "~1.1.2", + "type-check": "~0.3.2", + "word-wrap": "~1.2.3" + }, + "engines": { + "node": ">= 0.8.0" + } + }, + "node_modules/escodegen/node_modules/prelude-ls": { + "version": "1.1.2", + "resolved": "https://registry.npmjs.org/prelude-ls/-/prelude-ls-1.1.2.tgz", + "integrity": "sha1-IZMqVJ9eUv/ZqCf1cOBL5iqX2lQ=", + "dev": true, + "engines": { + "node": ">= 0.8.0" + } + }, + "node_modules/escodegen/node_modules/type-check": { + "version": "0.3.2", + "resolved": "https://registry.npmjs.org/type-check/-/type-check-0.3.2.tgz", + "integrity": "sha1-WITKtRLPHTVeP7eE8wgEsrUg23I=", + "dev": true, + "dependencies": { + "prelude-ls": "~1.1.2" + }, + "engines": { + "node": ">= 0.8.0" + } + }, "node_modules/eslint": { "version": "8.14.0", "resolved": "https://registry.npmjs.org/eslint/-/eslint-8.14.0.tgz", @@ -2349,6 +2676,22 @@ "node": "^12.22.0 || ^14.17.0 || >=16.0.0" } }, + "node_modules/eslint/node_modules/chalk": { + "version": "4.1.2", + "resolved": "https://registry.npmjs.org/chalk/-/chalk-4.1.2.tgz", + "integrity": "sha512-oKnbhFyRIXpUuez8iBMmyEa4nbj4IOQyuhc/wy9kY7/WVPcwIO9VA668Pu8RkO7+0G76SLROeyw9CpQ061i4mA==", + "dev": true, + "dependencies": { + "ansi-styles": "^4.1.0", + "supports-color": "^7.1.0" + }, + "engines": { + "node": ">=10" + }, + "funding": { + "url": "https://github.com/chalk/chalk?sponsor=1" + } + }, "node_modules/eslint/node_modules/escape-string-regexp": { "version": "4.0.0", "resolved": "https://registry.npmjs.org/escape-string-regexp/-/escape-string-regexp-4.0.0.tgz", @@ -2383,30 +2726,6 @@ "node": ">=4.0" } }, - "node_modules/eslint/node_modules/glob-parent": { - "version": "6.0.2", - "resolved": "https://registry.npmjs.org/glob-parent/-/glob-parent-6.0.2.tgz", - "integrity": "sha512-XxwI8EOhVQgWp6iDL+3b0r86f4d6AX6zSU55HfB4ydCEuXLXc5FcYeOu+nnGftS4TEju/11rt4KJPTMgbfmv4A==", - "dev": true, - "dependencies": { - "is-glob": "^4.0.3" - }, - "engines": { - "node": ">=10.13.0" - } - }, - "node_modules/eslint/node_modules/strip-json-comments": { - "version": "3.1.1", - "resolved": "https://registry.npmjs.org/strip-json-comments/-/strip-json-comments-3.1.1.tgz", - "integrity": "sha512-6fPc+R4ihwqP6N/aIv2f1gMH8lOVtWQHoqC4yK6oSDVVocumAsfCqjkXnqiYMhmMwS/mEHLp7Vehlt3ql6lEig==", - "dev": true, - "engines": { - "node": ">=8" - }, - "funding": { - "url": "https://github.com/sponsors/sindresorhus" - } - }, "node_modules/espree": { "version": "9.3.1", "resolved": "https://registry.npmjs.org/espree/-/espree-9.3.1.tgz", @@ -2495,23 +2814,23 @@ } }, "node_modules/execa": { - "version": "5.1.1", - "resolved": "https://registry.npmjs.org/execa/-/execa-5.1.1.tgz", - "integrity": "sha512-8uSpZZocAZRBAPIEINJj3Lo9HyGitllczc27Eh5YYojjMFMn8yHMDMaUHE2Jqfq05D/wucwI4JGURyXt1vchyg==", + "version": "6.1.0", + "resolved": "https://registry.npmjs.org/execa/-/execa-6.1.0.tgz", + "integrity": "sha512-QVWlX2e50heYJcCPG0iWtf8r0xjEYfz/OYLGDYH+IyjWezzPNxz63qNFOu0l4YftGWuizFVZHHs8PrLU5p2IDA==", "dev": true, "dependencies": { "cross-spawn": "^7.0.3", - "get-stream": "^6.0.0", - "human-signals": "^2.1.0", - "is-stream": "^2.0.0", + "get-stream": "^6.0.1", + "human-signals": "^3.0.1", + "is-stream": "^3.0.0", "merge-stream": "^2.0.0", - "npm-run-path": "^4.0.1", - "onetime": "^5.1.2", - "signal-exit": "^3.0.3", - "strip-final-newline": "^2.0.0" + "npm-run-path": "^5.1.0", + "onetime": "^6.0.0", + "signal-exit": "^3.0.7", + "strip-final-newline": "^3.0.0" }, "engines": { - "node": ">=10" + "node": "^12.20.0 || ^14.13.1 || >=16.0.0" }, "funding": { "url": "https://github.com/sindresorhus/execa?sponsor=1" @@ -2553,6 +2872,18 @@ "node": ">=8.6.0" } }, + "node_modules/fast-glob/node_modules/glob-parent": { + "version": "5.1.2", + "resolved": "https://registry.npmjs.org/glob-parent/-/glob-parent-5.1.2.tgz", + "integrity": "sha512-AOIgSQCepiJYwP3ARnGx+5VnTu2HBYdzbGP45eLw1vr3zB3vZLeyed1sC9hnbcOc9/SrMyM5RPQrkGz4aS9Zow==", + "dev": true, + "dependencies": { + "is-glob": "^4.0.1" + }, + "engines": { + "node": ">= 6" + } + }, "node_modules/fast-json-stable-stringify": { "version": "2.1.0", "resolved": "https://registry.npmjs.org/fast-json-stable-stringify/-/fast-json-stable-stringify-2.1.0.tgz", @@ -2601,6 +2932,15 @@ "node": "^10.12.0 || >=12.0.0" } }, + "node_modules/file-uri-to-path": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/file-uri-to-path/-/file-uri-to-path-2.0.0.tgz", + "integrity": "sha512-hjPFI8oE/2iQPVe4gbrJ73Pp+Xfub2+WI2LlXDbsaJBwT5wuMh35WNWVYYTpnz895shtwfyutMFLFywpQAFdLg==", + "dev": true, + "engines": { + "node": ">= 6" + } + }, "node_modules/filename-reserved-regex": { "version": "2.0.0", "resolved": "https://registry.npmjs.org/filename-reserved-regex/-/filename-reserved-regex-2.0.0.tgz", @@ -2733,6 +3073,12 @@ "node": ">= 6" } }, + "node_modules/form-data-encoder": { + "version": "1.7.1", + "resolved": "https://registry.npmjs.org/form-data-encoder/-/form-data-encoder-1.7.1.tgz", + "integrity": "sha512-EFRDrsMm/kyqbTQocNvRXMLjc7Es2Vk+IQFx/YW7hkUH1eBl4J1fqiP34l74Yt0pFLCNpc06fkbVk00008mzjg==", + "dev": true + }, "node_modules/fromentries": { "version": "1.3.2", "resolved": "https://registry.npmjs.org/fromentries/-/fromentries-1.3.2.tgz", @@ -2773,20 +3119,43 @@ "integrity": "sha1-FQStJSMVjKpA20onh8sBQRmU6k8=", "dev": true }, - "node_modules/fsevents": { - "version": "2.3.2", - "resolved": "https://registry.npmjs.org/fsevents/-/fsevents-2.3.2.tgz", - "integrity": "sha512-xiqMQR4xAeHTuB9uWm+fFRcIOgKBMiOBP+eXiyT7jsgVCq1bkVygt00oASowB7EdtpOHaaPgKt812P9ab+DDKA==", + "node_modules/ftp": { + "version": "0.3.10", + "resolved": "https://registry.npmjs.org/ftp/-/ftp-0.3.10.tgz", + "integrity": "sha1-kZfYYa2BQvPmPVqDv+TFn3MwiF0=", "dev": true, - "hasInstallScript": true, - "optional": true, - "os": [ - "darwin" - ], + "dependencies": { + "readable-stream": "1.1.x", + "xregexp": "2.0.0" + }, "engines": { - "node": "^8.16.0 || ^10.6.0 || >=11.0.0" + "node": ">=0.8.0" + } + }, + "node_modules/ftp/node_modules/isarray": { + "version": "0.0.1", + "resolved": "https://registry.npmjs.org/isarray/-/isarray-0.0.1.tgz", + "integrity": "sha1-ihis/Kmo9Bd+Cav8YDiTmwXR7t8=", + "dev": true + }, + "node_modules/ftp/node_modules/readable-stream": { + "version": "1.1.14", + "resolved": "https://registry.npmjs.org/readable-stream/-/readable-stream-1.1.14.tgz", + "integrity": "sha1-fPTFTvZI44EwhMY23SB54WbAgdk=", + "dev": true, + "dependencies": { + "core-util-is": "~1.0.0", + "inherits": "~2.0.1", + "isarray": "0.0.1", + "string_decoder": "~0.10.x" } }, + "node_modules/ftp/node_modules/string_decoder": { + "version": "0.10.31", + "resolved": "https://registry.npmjs.org/string_decoder/-/string_decoder-0.10.31.tgz", + "integrity": "sha1-YuIDvEF2bGwoyfyEMB2rHFMQ+pQ=", + "dev": true + }, "node_modules/function-bind": { "version": "1.1.1", "resolved": "https://registry.npmjs.org/function-bind/-/function-bind-1.1.1.tgz", @@ -2876,6 +3245,23 @@ "url": "https://github.com/sponsors/ljharb" } }, + "node_modules/get-uri": { + "version": "3.0.2", + "resolved": "https://registry.npmjs.org/get-uri/-/get-uri-3.0.2.tgz", + "integrity": "sha512-+5s0SJbGoyiJTZZ2JTpFPLMPSch72KEqGOTvQsBqg0RBWvwhWUSYZFAtz3TPW0GXJuLBJPts1E241iHg+VRfhg==", + "dev": true, + "dependencies": { + "@tootallnate/once": "1", + "data-uri-to-buffer": "3", + "debug": "4", + "file-uri-to-path": "2", + "fs-extra": "^8.1.0", + "ftp": "^0.3.10" + }, + "engines": { + "node": ">= 6" + } + }, "node_modules/gh-pages": { "version": "3.2.3", "resolved": "https://registry.npmjs.org/gh-pages/-/gh-pages-3.2.3.tgz", @@ -2938,15 +3324,15 @@ } }, "node_modules/glob-parent": { - "version": "5.1.2", - "resolved": "https://registry.npmjs.org/glob-parent/-/glob-parent-5.1.2.tgz", - "integrity": "sha512-AOIgSQCepiJYwP3ARnGx+5VnTu2HBYdzbGP45eLw1vr3zB3vZLeyed1sC9hnbcOc9/SrMyM5RPQrkGz4aS9Zow==", + "version": "6.0.2", + "resolved": "https://registry.npmjs.org/glob-parent/-/glob-parent-6.0.2.tgz", + "integrity": "sha512-XxwI8EOhVQgWp6iDL+3b0r86f4d6AX6zSU55HfB4ydCEuXLXc5FcYeOu+nnGftS4TEju/11rt4KJPTMgbfmv4A==", "dev": true, "dependencies": { - "is-glob": "^4.0.1" + "is-glob": "^4.0.3" }, "engines": { - "node": ">= 6" + "node": ">=10.13.0" } }, "node_modules/global-dirs": { @@ -3008,37 +3394,30 @@ } }, "node_modules/got": { - "version": "9.6.0", - "resolved": "https://registry.npmjs.org/got/-/got-9.6.0.tgz", - "integrity": "sha512-R7eWptXuGYxwijs0eV+v3o6+XH1IqVK8dJOEecQfTmkncw9AV4dcw/Dhxi8MdlqPthxxpZyizMzyg8RTmEsG+Q==", + "version": "12.0.4", + "resolved": "https://registry.npmjs.org/got/-/got-12.0.4.tgz", + "integrity": "sha512-2Eyz4iU/ktq7wtMFXxzK7g5p35uNYLLdiZarZ5/Yn3IJlNEpBd5+dCgcAyxN8/8guZLszffwe3wVyw+DEVrpBg==", "dev": true, "dependencies": { - "@sindresorhus/is": "^0.14.0", - "@szmarczak/http-timer": "^1.1.2", - "cacheable-request": "^6.0.0", - "decompress-response": "^3.3.0", - "duplexer3": "^0.1.4", - "get-stream": "^4.1.0", - "lowercase-keys": "^1.0.1", - "mimic-response": "^1.0.1", - "p-cancelable": "^1.0.0", - "to-readable-stream": "^1.0.0", - "url-parse-lax": "^3.0.0" + "@sindresorhus/is": "^4.6.0", + "@szmarczak/http-timer": "^5.0.1", + "@types/cacheable-request": "^6.0.2", + "@types/responselike": "^1.0.0", + "cacheable-lookup": "^6.0.4", + "cacheable-request": "^7.0.2", + "decompress-response": "^6.0.0", + "form-data-encoder": "1.7.1", + "get-stream": "^6.0.1", + "http2-wrapper": "^2.1.10", + "lowercase-keys": "^3.0.0", + "p-cancelable": "^3.0.0", + "responselike": "^2.0.0" }, "engines": { - "node": ">=8.6" - } - }, - "node_modules/got/node_modules/get-stream": { - "version": "4.1.0", - "resolved": "https://registry.npmjs.org/get-stream/-/get-stream-4.1.0.tgz", - "integrity": "sha512-GMat4EJ5161kIy2HevLlr4luNjBgvmj413KaQA7jt4V8B4RDsfpHk7WQ9GVqfYyyx8OS/L66Kox+rJRNklLK7w==", - "dev": true, - "dependencies": { - "pump": "^3.0.0" + "node": ">=14.16" }, - "engines": { - "node": ">=6" + "funding": { + "url": "https://github.com/sindresorhus/got?sponsor=1" } }, "node_modules/graceful-fs": { @@ -3047,15 +3426,6 @@ "integrity": "sha512-9ByhssR2fPVsNZj478qUUbKfmL0+t5BDVyjShtyZZLiK7ZDAArFFfopyOTj0M05wE2tJPisA4iTnnXl2YoPvOA==", "dev": true }, - "node_modules/growl": { - "version": "1.10.5", - "resolved": "https://registry.npmjs.org/growl/-/growl-1.10.5.tgz", - "integrity": "sha512-qBr4OuELkhPenW6goKVXiv47US3clb3/IbuWF9KNKEijAy9oeHxU9IgzjvJhHkUzhaj7rOUD7+YGWqUjLp5oSA==", - "dev": true, - "engines": { - "node": ">=4.x" - } - }, "node_modules/has": { "version": "1.0.3", "resolved": "https://registry.npmjs.org/has/-/has-1.0.3.tgz", @@ -3150,6 +3520,18 @@ "url": "https://github.com/sponsors/sindresorhus" } }, + "node_modules/hasha/node_modules/is-stream": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/is-stream/-/is-stream-2.0.1.tgz", + "integrity": "sha512-hFoiJiTl63nn+kstHGBtewWSKnQLpyb155KHheA1l39uvtO9nWIop1p3udqPcUd/xbF1VLMO4n7OI6p7RbngDg==", + "dev": true, + "engines": { + "node": ">=8" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, "node_modules/hasha/node_modules/type-fest": { "version": "0.8.1", "resolved": "https://registry.npmjs.org/type-fest/-/type-fest-0.8.1.tgz", @@ -3180,13 +3562,69 @@ "integrity": "sha512-carPklcUh7ROWRK7Cv27RPtdhYhUsela/ue5/jKzjegVvXDqM2ILE9Q2BGn9JZJh1g87cp56su/FgQSzcWS8cQ==", "dev": true }, + "node_modules/http-errors": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/http-errors/-/http-errors-2.0.0.tgz", + "integrity": "sha512-FtwrG/euBzaEjYeRqOgly7G0qviiXoJWnvEH2Z1plBdXgbyjv34pHTSb9zoeHMyDy33+DWy5Wt9Wo+TURtOYSQ==", + "dev": true, + "dependencies": { + "depd": "2.0.0", + "inherits": "2.0.4", + "setprototypeof": "1.2.0", + "statuses": "2.0.1", + "toidentifier": "1.0.1" + }, + "engines": { + "node": ">= 0.8" + } + }, + "node_modules/http-proxy-agent": { + "version": "4.0.1", + "resolved": "https://registry.npmjs.org/http-proxy-agent/-/http-proxy-agent-4.0.1.tgz", + "integrity": "sha512-k0zdNgqWTGA6aeIRVpvfVob4fL52dTfaehylg0Y4UvSySvOq/Y+BOyPrgpUrA7HylqvU8vIZGsRuXmspskV0Tg==", + "dev": true, + "dependencies": { + "@tootallnate/once": "1", + "agent-base": "6", + "debug": "4" + }, + "engines": { + "node": ">= 6" + } + }, + "node_modules/http2-wrapper": { + "version": "2.1.11", + "resolved": "https://registry.npmjs.org/http2-wrapper/-/http2-wrapper-2.1.11.tgz", + "integrity": "sha512-aNAk5JzLturWEUiuhAN73Jcbq96R7rTitAoXV54FYMatvihnpD2+6PUgU4ce3D/m5VDbw+F5CsyKSF176ptitQ==", + "dev": true, + "dependencies": { + "quick-lru": "^5.1.1", + "resolve-alpn": "^1.2.0" + }, + "engines": { + "node": ">=10.19.0" + } + }, + "node_modules/https-proxy-agent": { + "version": "5.0.1", + "resolved": "https://registry.npmjs.org/https-proxy-agent/-/https-proxy-agent-5.0.1.tgz", + "integrity": "sha512-dFcAjpTQFgoLMzC2VwU+C/CbS7uRL0lWmxDITmqm7C+7F0Odmj6s9l6alZc6AELXhrnggM2CeWSXHGOdX2YtwA==", + "dev": true, + "dependencies": { + "agent-base": "6", + "debug": "4" + }, + "engines": { + "node": ">= 6" + } + }, "node_modules/human-signals": { - "version": "2.1.0", - "resolved": "https://registry.npmjs.org/human-signals/-/human-signals-2.1.0.tgz", - "integrity": "sha512-B4FFZ6q/T2jhhksgkbEW3HBvWIfDW85snkQgawt07S7J5QXTk6BkNV+0yAeZrM5QpMAdYlocGoljn0sJ/WQkFw==", + "version": "3.0.1", + "resolved": "https://registry.npmjs.org/human-signals/-/human-signals-3.0.1.tgz", + "integrity": "sha512-rQLskxnM/5OCldHo+wNXbpVgDn5A17CUoKX+7Sokwaknlq7CdSnphy0W39GU8dw59XiCXmFXDg4fRuckQRKewQ==", "dev": true, "engines": { - "node": ">=10.17.0" + "node": ">=12.20.0" } }, "node_modules/iconv-lite": { @@ -3230,18 +3668,6 @@ "node": ">= 4" } }, - "node_modules/import-cwd": { - "version": "3.0.0", - "resolved": "https://registry.npmjs.org/import-cwd/-/import-cwd-3.0.0.tgz", - "integrity": "sha512-4pnzH16plW+hgvRECbDWpQl3cqtvSofHWh44met7ESfZ8UZOWWddm8hEyDTqREJ9RbYHY8gi8DqmaelApoOGMg==", - "dev": true, - "dependencies": { - "import-from": "^3.0.0" - }, - "engines": { - "node": ">=8" - } - }, "node_modules/import-fresh": { "version": "3.3.0", "resolved": "https://registry.npmjs.org/import-fresh/-/import-fresh-3.3.0.tgz", @@ -3258,27 +3684,6 @@ "url": "https://github.com/sponsors/sindresorhus" } }, - "node_modules/import-from": { - "version": "3.0.0", - "resolved": "https://registry.npmjs.org/import-from/-/import-from-3.0.0.tgz", - "integrity": "sha512-CiuXOFFSzkU5x/CR0+z7T91Iht4CXgfCxVOFRhh2Zyhg5wOpWvvDLQUsWl+gcN+QscYBjez8hDCt85O7RLDttQ==", - "dev": true, - "dependencies": { - "resolve-from": "^5.0.0" - }, - "engines": { - "node": ">=8" - } - }, - "node_modules/import-from/node_modules/resolve-from": { - "version": "5.0.0", - "resolved": "https://registry.npmjs.org/resolve-from/-/resolve-from-5.0.0.tgz", - "integrity": "sha512-qYg9KP24dD5qka9J47d0aVky0N+b4fTU89LN9iDnjB5waksiC49rvMB0PrUJQGoTmH50XPiqOvAjDfaijGxYZw==", - "dev": true, - "engines": { - "node": ">=8" - } - }, "node_modules/import-lazy": { "version": "2.1.0", "resolved": "https://registry.npmjs.org/import-lazy/-/import-lazy-2.1.0.tgz", @@ -3332,9 +3737,9 @@ } }, "node_modules/inquirer": { - "version": "8.2.0", - "resolved": "https://registry.npmjs.org/inquirer/-/inquirer-8.2.0.tgz", - "integrity": "sha512-0crLweprevJ02tTuA6ThpoAERAGyVILC4sS74uib58Xf/zSr1/ZWtmm7D5CI+bSQEaA04f0K7idaHpQbSWgiVQ==", + "version": "8.2.4", + "resolved": "https://registry.npmjs.org/inquirer/-/inquirer-8.2.4.tgz", + "integrity": "sha512-nn4F01dxU8VeKfq192IjLsxu0/OmMZ4Lg3xKAns148rCaXP6ntAoEkVYZThWjwON8AlzdZZi6oqnhNbxUG9hVg==", "dev": true, "dependencies": { "ansi-escapes": "^4.2.1", @@ -3347,13 +3752,125 @@ "mute-stream": "0.0.8", "ora": "^5.4.1", "run-async": "^2.4.0", - "rxjs": "^7.2.0", + "rxjs": "^7.5.5", "string-width": "^4.1.0", "strip-ansi": "^6.0.0", - "through": "^2.3.6" + "through": "^2.3.6", + "wrap-ansi": "^7.0.0" }, "engines": { - "node": ">=8.0.0" + "node": ">=12.0.0" + } + }, + "node_modules/inquirer/node_modules/bl": { + "version": "4.1.0", + "resolved": "https://registry.npmjs.org/bl/-/bl-4.1.0.tgz", + "integrity": "sha512-1W07cM9gS6DcLperZfFSj+bWLtaPGSOHWhPiGzXmvVJbRLdG82sH/Kn8EtW1VqWVA54AKf2h5k5BbnIbwF3h6w==", + "dev": true, + "dependencies": { + "buffer": "^5.5.0", + "inherits": "^2.0.4", + "readable-stream": "^3.4.0" + } + }, + "node_modules/inquirer/node_modules/buffer": { + "version": "5.7.1", + "resolved": "https://registry.npmjs.org/buffer/-/buffer-5.7.1.tgz", + "integrity": "sha512-EHcyIPBQ4BSGlvjB16k5KgAJ27CIsHY/2JBmCRReo48y9rQ3MaUzWX3KVlBa4U7MyX02HdVj0K7C3WaB3ju7FQ==", + "dev": true, + "funding": [ + { + "type": "github", + "url": "https://github.com/sponsors/feross" + }, + { + "type": "patreon", + "url": "https://www.patreon.com/feross" + }, + { + "type": "consulting", + "url": "https://feross.org/support" + } + ], + "dependencies": { + "base64-js": "^1.3.1", + "ieee754": "^1.1.13" + } + }, + "node_modules/inquirer/node_modules/chalk": { + "version": "4.1.2", + "resolved": "https://registry.npmjs.org/chalk/-/chalk-4.1.2.tgz", + "integrity": "sha512-oKnbhFyRIXpUuez8iBMmyEa4nbj4IOQyuhc/wy9kY7/WVPcwIO9VA668Pu8RkO7+0G76SLROeyw9CpQ061i4mA==", + "dev": true, + "dependencies": { + "ansi-styles": "^4.1.0", + "supports-color": "^7.1.0" + }, + "engines": { + "node": ">=10" + }, + "funding": { + "url": "https://github.com/chalk/chalk?sponsor=1" + } + }, + "node_modules/inquirer/node_modules/is-interactive": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/is-interactive/-/is-interactive-1.0.0.tgz", + "integrity": "sha512-2HvIEKRoqS62guEC+qBjpvRubdX910WCMuJTZ+I9yvqKU2/12eSL549HMwtabb4oupdj2sMP50k+XJfB/8JE6w==", + "dev": true, + "engines": { + "node": ">=8" + } + }, + "node_modules/inquirer/node_modules/is-unicode-supported": { + "version": "0.1.0", + "resolved": "https://registry.npmjs.org/is-unicode-supported/-/is-unicode-supported-0.1.0.tgz", + "integrity": "sha512-knxG2q4UC3u8stRGyAVJCOdxFmv5DZiRcdlIaAQXAbSfJya+OhopNotLQrstBhququ4ZpuKbDc/8S6mgXgPFPw==", + "dev": true, + "engines": { + "node": ">=10" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/inquirer/node_modules/log-symbols": { + "version": "4.1.0", + "resolved": "https://registry.npmjs.org/log-symbols/-/log-symbols-4.1.0.tgz", + "integrity": "sha512-8XPvpAA8uyhfteu8pIvQxpJZ7SYYdpUivZpGy6sFsBuKRY/7rQGavedeB8aK+Zkyq6upMFVL/9AW6vOYzfRyLg==", + "dev": true, + "dependencies": { + "chalk": "^4.1.0", + "is-unicode-supported": "^0.1.0" + }, + "engines": { + "node": ">=10" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/inquirer/node_modules/ora": { + "version": "5.4.1", + "resolved": "https://registry.npmjs.org/ora/-/ora-5.4.1.tgz", + "integrity": "sha512-5b6Y85tPxZZ7QytO+BQzysW31HJku27cRIlkbAXaNx+BdcVi+LlRFmVXzeF6a7JCwJpyw5c4b+YSVImQIrBpuQ==", + "dev": true, + "dependencies": { + "bl": "^4.1.0", + "chalk": "^4.1.0", + "cli-cursor": "^3.1.0", + "cli-spinners": "^2.5.0", + "is-interactive": "^1.0.0", + "is-unicode-supported": "^0.1.0", + "log-symbols": "^4.1.0", + "strip-ansi": "^6.0.0", + "wcwidth": "^1.0.1" + }, + "engines": { + "node": ">=10" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" } }, "node_modules/internal-slot": { @@ -3379,6 +3896,12 @@ "node": ">= 0.10" } }, + "node_modules/ip": { + "version": "1.1.5", + "resolved": "https://registry.npmjs.org/ip/-/ip-1.1.5.tgz", + "integrity": "sha1-vd7XARQpCCjAoDnnLvJfWq7ENUo=", + "dev": true + }, "node_modules/is-arguments": { "version": "1.1.1", "resolved": "https://registry.npmjs.org/is-arguments/-/is-arguments-1.1.1.tgz", @@ -3554,12 +4077,15 @@ } }, "node_modules/is-interactive": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/is-interactive/-/is-interactive-1.0.0.tgz", - "integrity": "sha512-2HvIEKRoqS62guEC+qBjpvRubdX910WCMuJTZ+I9yvqKU2/12eSL549HMwtabb4oupdj2sMP50k+XJfB/8JE6w==", + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/is-interactive/-/is-interactive-2.0.0.tgz", + "integrity": "sha512-qP1vozQRI+BMOPcjFzrjXuQvdak2pHNUMZoeG2eRbiSqyvbEf/wQtEOTOX1guk6E3t36RkaqiSt8A/6YElNxLQ==", "dev": true, "engines": { - "node": ">=8" + "node": ">=12" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" } }, "node_modules/is-map": { @@ -3702,12 +4228,12 @@ } }, "node_modules/is-stream": { - "version": "2.0.1", - "resolved": "https://registry.npmjs.org/is-stream/-/is-stream-2.0.1.tgz", - "integrity": "sha512-hFoiJiTl63nn+kstHGBtewWSKnQLpyb155KHheA1l39uvtO9nWIop1p3udqPcUd/xbF1VLMO4n7OI6p7RbngDg==", + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/is-stream/-/is-stream-3.0.0.tgz", + "integrity": "sha512-LnQR4bZ9IADDRSkvpqMGvt/tEJWclzklNgSw48V5EAaAeDd6qGvN8ei6k5p0tvxSR171VmGyHuTiAOfxAbr8kA==", "dev": true, "engines": { - "node": ">=8" + "node": "^12.20.0 || ^14.13.1 || >=16.0.0" }, "funding": { "url": "https://github.com/sponsors/sindresorhus" @@ -3750,12 +4276,12 @@ "dev": true }, "node_modules/is-unicode-supported": { - "version": "0.1.0", - "resolved": "https://registry.npmjs.org/is-unicode-supported/-/is-unicode-supported-0.1.0.tgz", - "integrity": "sha512-knxG2q4UC3u8stRGyAVJCOdxFmv5DZiRcdlIaAQXAbSfJya+OhopNotLQrstBhququ4ZpuKbDc/8S6mgXgPFPw==", + "version": "1.2.0", + "resolved": "https://registry.npmjs.org/is-unicode-supported/-/is-unicode-supported-1.2.0.tgz", + "integrity": "sha512-wH+U77omcRzevfIG8dDhTS0V9zZyweakfD01FULl97+0EHiJTTZtJqxPSkIIo/SDPv/i07k/C9jAPY+jwLLeUQ==", "dev": true, "engines": { - "node": ">=10" + "node": ">=12" }, "funding": { "url": "https://github.com/sponsors/sindresorhus" @@ -3866,16 +4392,6 @@ "node": ">=8" } }, - "node_modules/istanbul-lib-processinfo/node_modules/uuid": { - "version": "3.4.0", - "resolved": "https://registry.npmjs.org/uuid/-/uuid-3.4.0.tgz", - "integrity": "sha512-HjSDRw6gZE5JMggctHBcjVak08+KEVhSIiDzFnT9S9aegmp85S/bReBVTb4QTFaRNptJ9kuYaNhnbNEOkbKb/A==", - "deprecated": "Please upgrade to version 7 or higher. Older versions may use Math.random() in certain circumstances, which is known to be problematic. See https://v8.dev/blog/math-random for details.", - "dev": true, - "bin": { - "uuid": "bin/uuid" - } - }, "node_modules/istanbul-lib-report": { "version": "3.0.0", "resolved": "https://registry.npmjs.org/istanbul-lib-report/-/istanbul-lib-report-3.0.0.tgz", @@ -3904,15 +4420,6 @@ "node": ">=10" } }, - "node_modules/istanbul-lib-source-maps/node_modules/source-map": { - "version": "0.6.1", - "resolved": "https://registry.npmjs.org/source-map/-/source-map-0.6.1.tgz", - "integrity": "sha512-UjgapumWlbMhkBgzT7Ykc5YXUT46F0iKu8SGXq0bcwP5dz/h0Plj6enJqjz1Zbq2l5WaqYnrVbwWOWMyF3F47g==", - "dev": true, - "engines": { - "node": ">=0.10.0" - } - }, "node_modules/istanbul-reports": { "version": "3.1.4", "resolved": "https://registry.npmjs.org/istanbul-reports/-/istanbul-reports-3.1.4.tgz", @@ -3979,9 +4486,9 @@ } }, "node_modules/json-buffer": { - "version": "3.0.0", - "resolved": "https://registry.npmjs.org/json-buffer/-/json-buffer-3.0.0.tgz", - "integrity": "sha1-Wx85evx11ne96Lz8Dkfh+aPZqJg=", + "version": "3.0.1", + "resolved": "https://registry.npmjs.org/json-buffer/-/json-buffer-3.0.1.tgz", + "integrity": "sha512-4bV5BfR2mqfQTJm+V5tPPdf+ZpuhiIvTuAB5g8kcrXOZpTT/QwwVRWBywX1ozr6lEuPdbHxwaJlm9G6mI2sfSQ==", "dev": true }, "node_modules/json-parse-even-better-errors": { @@ -4036,12 +4543,13 @@ "dev": true }, "node_modules/keyv": { - "version": "3.1.0", - "resolved": "https://registry.npmjs.org/keyv/-/keyv-3.1.0.tgz", - "integrity": "sha512-9ykJ/46SN/9KPM/sichzQ7OvXyGDYKGTaDlKMGCAlg2UK8KRy4jb0d8sFc+0Tt0YYnThq8X2RZgCg74RPxgcVA==", + "version": "4.2.2", + "resolved": "https://registry.npmjs.org/keyv/-/keyv-4.2.2.tgz", + "integrity": "sha512-uYS0vKTlBIjNCAUqrjlxmruxOEiZxZIHXyp32sdcGmP+ukFrmWUnE//RcPXJH3Vxrni1H2gsQbjHE0bH7MtMQQ==", "dev": true, "dependencies": { - "json-buffer": "3.0.0" + "compress-brotli": "^1.3.6", + "json-buffer": "3.0.1" } }, "node_modules/latest-version": { @@ -4112,40 +4620,40 @@ "dev": true }, "node_modules/log-symbols": { - "version": "4.1.0", - "resolved": "https://registry.npmjs.org/log-symbols/-/log-symbols-4.1.0.tgz", - "integrity": "sha512-8XPvpAA8uyhfteu8pIvQxpJZ7SYYdpUivZpGy6sFsBuKRY/7rQGavedeB8aK+Zkyq6upMFVL/9AW6vOYzfRyLg==", + "version": "5.1.0", + "resolved": "https://registry.npmjs.org/log-symbols/-/log-symbols-5.1.0.tgz", + "integrity": "sha512-l0x2DvrW294C9uDCoQe1VSU4gf529FkSZ6leBl4TiqZH/e+0R7hSfHQBNut2mNygDgHwvYHfFLn6Oxb3VWj2rA==", "dev": true, "dependencies": { - "chalk": "^4.1.0", - "is-unicode-supported": "^0.1.0" + "chalk": "^5.0.0", + "is-unicode-supported": "^1.1.0" }, "engines": { - "node": ">=10" + "node": ">=12" }, "funding": { "url": "https://github.com/sponsors/sindresorhus" } }, "node_modules/lowercase-keys": { - "version": "1.0.1", - "resolved": "https://registry.npmjs.org/lowercase-keys/-/lowercase-keys-1.0.1.tgz", - "integrity": "sha512-G2Lj61tXDnVFFOi8VZds+SoQjtQC3dgokKdDG2mTm1tx4m50NUHBOZSBwQQHyy0V12A0JTG4icfZQH+xPyh8VA==", + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/lowercase-keys/-/lowercase-keys-3.0.0.tgz", + "integrity": "sha512-ozCC6gdQ+glXOQsveKD0YsDy8DSQFjDTz4zyzEHNV5+JP5D62LmfDZ6o1cycFx9ouG940M5dE8C8CTewdj2YWQ==", "dev": true, "engines": { - "node": ">=0.10.0" + "node": "^12.20.0 || ^14.13.1 || >=16.0.0" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" } }, "node_modules/lru-cache": { - "version": "6.0.0", - "resolved": "https://registry.npmjs.org/lru-cache/-/lru-cache-6.0.0.tgz", - "integrity": "sha512-Jo6dJ04CmSjuznwJSS3pUeWmd/H0ffTlkXXgwZi+eq1UCmqQwCh+eLsYOYCwY991i2Fah4h1BEMCx4qThGbsiA==", + "version": "5.1.1", + "resolved": "https://registry.npmjs.org/lru-cache/-/lru-cache-5.1.1.tgz", + "integrity": "sha512-KpNARQA3Iwv+jTA0utUVVbrh+Jlrr1Fv0e56GGzAFOXN7dk/FviaDW8LHmK52DlcH4WP2n6gI8vN1aesBFgo9w==", "dev": true, "dependencies": { - "yallist": "^4.0.0" - }, - "engines": { - "node": ">=10" + "yallist": "^3.0.2" } }, "node_modules/lunr": { @@ -4155,12 +4663,12 @@ "dev": true }, "node_modules/macos-release": { - "version": "2.5.0", - "resolved": "https://registry.npmjs.org/macos-release/-/macos-release-2.5.0.tgz", - "integrity": "sha512-EIgv+QZ9r+814gjJj0Bt5vSLJLzswGmSUbUpbi9AIr/fsN2IWFBl2NucV9PAiek+U1STK468tEkxmVYUtuAN3g==", + "version": "3.0.1", + "resolved": "https://registry.npmjs.org/macos-release/-/macos-release-3.0.1.tgz", + "integrity": "sha512-3l6OrhdDg2H2SigtuN3jBh+5dRJRWxNKuJTPBbGeNJTsmt/pj9PO25wYaNb05NuNmAsl435j4rDP6rgNXz7s7g==", "dev": true, "engines": { - "node": ">=6" + "node": "^12.20.0 || ^14.13.1 || >=16.0.0" }, "funding": { "url": "https://github.com/sponsors/sindresorhus" @@ -4188,9 +4696,9 @@ "dev": true }, "node_modules/marked": { - "version": "4.0.14", - "resolved": "https://registry.npmjs.org/marked/-/marked-4.0.14.tgz", - "integrity": "sha512-HL5sSPE/LP6U9qKgngIIPTthuxC0jrfxpYMZ3LdGDD3vTnLs59m2Z7r6+LNDR3ToqEQdkKd6YaaEfJhodJmijQ==", + "version": "4.0.15", + "resolved": "https://registry.npmjs.org/marked/-/marked-4.0.15.tgz", + "integrity": "sha512-esX5lPdTfG4p8LDkv+obbRCyOKzB+820ZZyMOXJZygZBHrH9b3xXR64X4kT3sPe9Nx8qQXbmcz6kFSMt4Nfk6Q==", "dev": true, "bin": { "marked": "bin/marked.js" @@ -4249,12 +4757,15 @@ } }, "node_modules/mimic-fn": { - "version": "2.1.0", - "resolved": "https://registry.npmjs.org/mimic-fn/-/mimic-fn-2.1.0.tgz", - "integrity": "sha512-OqbOk5oEQeAZ8WXWydlu9HJjz9WVdEIvamMCcXmuqUYjTknH/sqsWvhQ3vgwKFRR1HpjvNBKQ37nbJgYzGqGcg==", + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/mimic-fn/-/mimic-fn-4.0.0.tgz", + "integrity": "sha512-vqiC06CuhBTUdZH+RYl8sFrL096vA45Ok5ISO6sE/Mr1jRbGH4Csnhi8f3wKVl7x8mO4Au7Ir9D3Oyv1VYMFJw==", "dev": true, "engines": { - "node": ">=6" + "node": ">=12" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" } }, "node_modules/mimic-response": { @@ -4285,70 +4796,93 @@ "dev": true }, "node_modules/mocha": { - "version": "9.2.2", - "resolved": "https://registry.npmjs.org/mocha/-/mocha-9.2.2.tgz", - "integrity": "sha512-L6XC3EdwT6YrIk0yXpavvLkn8h+EU+Y5UcCHKECyMbdUIxyMuZj4bX4U9e1nvnvUUvQVsV2VHQr5zLdcUkhW/g==", + "version": "10.0.0", + "resolved": "https://registry.npmjs.org/mocha/-/mocha-10.0.0.tgz", + "integrity": "sha512-0Wl+elVUD43Y0BqPZBzZt8Tnkw9CMUdNYnUsTfOM1vuhJVZL+kiesFYsqwBkEEuEixaiPe5ZQdqDgX2jddhmoA==", "dev": true, "dependencies": { "@ungap/promise-all-settled": "1.1.2", "ansi-colors": "4.1.1", "browser-stdout": "1.3.1", "chokidar": "3.5.3", - "debug": "4.3.3", + "debug": "4.3.4", "diff": "5.0.0", "escape-string-regexp": "4.0.0", "find-up": "5.0.0", "glob": "7.2.0", - "growl": "1.10.5", "he": "1.2.0", "js-yaml": "4.1.0", "log-symbols": "4.1.0", - "minimatch": "4.2.1", + "minimatch": "5.0.1", "ms": "2.1.3", - "nanoid": "3.3.1", + "nanoid": "3.3.3", "serialize-javascript": "6.0.0", "strip-json-comments": "3.1.1", "supports-color": "8.1.1", - "which": "2.0.2", - "workerpool": "6.2.0", + "workerpool": "6.2.1", "yargs": "16.2.0", "yargs-parser": "20.2.4", "yargs-unparser": "2.0.0" }, "bin": { "_mocha": "bin/_mocha", - "mocha": "bin/mocha" + "mocha": "bin/mocha.js" }, "engines": { - "node": ">= 12.0.0" + "node": ">= 14.0.0" }, "funding": { "type": "opencollective", "url": "https://opencollective.com/mochajs" } }, - "node_modules/mocha/node_modules/debug": { - "version": "4.3.3", - "resolved": "https://registry.npmjs.org/debug/-/debug-4.3.3.tgz", - "integrity": "sha512-/zxw5+vh1Tfv+4Qn7a5nsbcJKPaSvCDhojn6FEl9vupwK2VCSDtEiEtqr8DFtzYFOdz63LBkxec7DYuc2jon6Q==", + "node_modules/mocha/node_modules/brace-expansion": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/brace-expansion/-/brace-expansion-2.0.1.tgz", + "integrity": "sha512-XnAIvQ8eM+kC6aULx6wuQiwVsnzsi9d3WxzV3FpWTGA19F621kwdbsAcFKXgKUHZWsy+mY6iL1sHTxWEFCytDA==", + "dev": true, + "dependencies": { + "balanced-match": "^1.0.0" + } + }, + "node_modules/mocha/node_modules/chalk": { + "version": "4.1.2", + "resolved": "https://registry.npmjs.org/chalk/-/chalk-4.1.2.tgz", + "integrity": "sha512-oKnbhFyRIXpUuez8iBMmyEa4nbj4IOQyuhc/wy9kY7/WVPcwIO9VA668Pu8RkO7+0G76SLROeyw9CpQ061i4mA==", "dev": true, "dependencies": { - "ms": "2.1.2" + "ansi-styles": "^4.1.0", + "supports-color": "^7.1.0" }, "engines": { - "node": ">=6.0" + "node": ">=10" }, - "peerDependenciesMeta": { - "supports-color": { - "optional": true - } + "funding": { + "url": "https://github.com/chalk/chalk?sponsor=1" } }, - "node_modules/mocha/node_modules/debug/node_modules/ms": { - "version": "2.1.2", - "resolved": "https://registry.npmjs.org/ms/-/ms-2.1.2.tgz", - "integrity": "sha512-sGkPx+VjMtmA6MX27oA4FBFELFCZZ4S4XqeGOXCv68tT+jb3vk/RyaKWP0PTKyWtmLSM0b+adUTEvbs1PEaH2w==", - "dev": true + "node_modules/mocha/node_modules/chalk/node_modules/supports-color": { + "version": "7.2.0", + "resolved": "https://registry.npmjs.org/supports-color/-/supports-color-7.2.0.tgz", + "integrity": "sha512-qpCAvRl9stuOHveKsn7HncJRvv501qIacKzQlO/+Lwxc9+0q2wLyv4Dfvt80/DPn2pqOBsJdDiogXGR9+OvwRw==", + "dev": true, + "dependencies": { + "has-flag": "^4.0.0" + }, + "engines": { + "node": ">=8" + } + }, + "node_modules/mocha/node_modules/cliui": { + "version": "7.0.4", + "resolved": "https://registry.npmjs.org/cliui/-/cliui-7.0.4.tgz", + "integrity": "sha512-OcRE68cOsVMXp1Yvonl/fzkQOyjLSu/8bhPDfQt0e0/Eb283TKP20Fs2MqoPsr9SwA595rRCA+QMzYc9nBP+JQ==", + "dev": true, + "dependencies": { + "string-width": "^4.2.0", + "strip-ansi": "^6.0.0", + "wrap-ansi": "^7.0.0" + } }, "node_modules/mocha/node_modules/escape-string-regexp": { "version": "4.0.0", @@ -4378,6 +4912,18 @@ "url": "https://github.com/sponsors/sindresorhus" } }, + "node_modules/mocha/node_modules/is-unicode-supported": { + "version": "0.1.0", + "resolved": "https://registry.npmjs.org/is-unicode-supported/-/is-unicode-supported-0.1.0.tgz", + "integrity": "sha512-knxG2q4UC3u8stRGyAVJCOdxFmv5DZiRcdlIaAQXAbSfJya+OhopNotLQrstBhququ4ZpuKbDc/8S6mgXgPFPw==", + "dev": true, + "engines": { + "node": ">=10" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, "node_modules/mocha/node_modules/locate-path": { "version": "6.0.0", "resolved": "https://registry.npmjs.org/locate-path/-/locate-path-6.0.0.tgz", @@ -4393,13 +4939,29 @@ "url": "https://github.com/sponsors/sindresorhus" } }, + "node_modules/mocha/node_modules/log-symbols": { + "version": "4.1.0", + "resolved": "https://registry.npmjs.org/log-symbols/-/log-symbols-4.1.0.tgz", + "integrity": "sha512-8XPvpAA8uyhfteu8pIvQxpJZ7SYYdpUivZpGy6sFsBuKRY/7rQGavedeB8aK+Zkyq6upMFVL/9AW6vOYzfRyLg==", + "dev": true, + "dependencies": { + "chalk": "^4.1.0", + "is-unicode-supported": "^0.1.0" + }, + "engines": { + "node": ">=10" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, "node_modules/mocha/node_modules/minimatch": { - "version": "4.2.1", - "resolved": "https://registry.npmjs.org/minimatch/-/minimatch-4.2.1.tgz", - "integrity": "sha512-9Uq1ChtSZO+Mxa/CL1eGizn2vRn3MlLgzhT0Iz8zaY8NdvxvB0d5QdPFmCKf7JKA9Lerx5vRrnwO03jsSfGG9g==", + "version": "5.0.1", + "resolved": "https://registry.npmjs.org/minimatch/-/minimatch-5.0.1.tgz", + "integrity": "sha512-nLDxIFRyhDblz3qMuq+SoRZED4+miJ/G+tdDrjkkkRnjAsBexeGpgjLEQ0blJy7rHhR2b93rhQY4SvyWu9v03g==", "dev": true, "dependencies": { - "brace-expansion": "^1.1.7" + "brace-expansion": "^2.0.1" }, "engines": { "node": ">=10" @@ -4441,18 +5003,6 @@ "url": "https://github.com/sponsors/sindresorhus" } }, - "node_modules/mocha/node_modules/strip-json-comments": { - "version": "3.1.1", - "resolved": "https://registry.npmjs.org/strip-json-comments/-/strip-json-comments-3.1.1.tgz", - "integrity": "sha512-6fPc+R4ihwqP6N/aIv2f1gMH8lOVtWQHoqC4yK6oSDVVocumAsfCqjkXnqiYMhmMwS/mEHLp7Vehlt3ql6lEig==", - "dev": true, - "engines": { - "node": ">=8" - }, - "funding": { - "url": "https://github.com/sponsors/sindresorhus" - } - }, "node_modules/mocha/node_modules/supports-color": { "version": "8.1.1", "resolved": "https://registry.npmjs.org/supports-color/-/supports-color-8.1.1.tgz", @@ -4468,6 +5018,15 @@ "url": "https://github.com/chalk/supports-color?sponsor=1" } }, + "node_modules/mocha/node_modules/y18n": { + "version": "5.0.8", + "resolved": "https://registry.npmjs.org/y18n/-/y18n-5.0.8.tgz", + "integrity": "sha512-0pfFzegeDWJHJIAmTLRP2DwHjdF5s7jo9tuztdQxAhINCdvS+3nGINqPd00AphqJR/0LhANUS6/+7SCb98YOfA==", + "dev": true, + "engines": { + "node": ">=10" + } + }, "node_modules/mocha/node_modules/yargs": { "version": "16.2.0", "resolved": "https://registry.npmjs.org/yargs/-/yargs-16.2.0.tgz", @@ -4508,9 +5067,9 @@ "dev": true }, "node_modules/nanoid": { - "version": "3.3.1", - "resolved": "https://registry.npmjs.org/nanoid/-/nanoid-3.3.1.tgz", - "integrity": "sha512-n6Vs/3KGyxPQd6uO0eH4Bv0ojGSUvuLlIHtC3Y0kEO23YRge8H9x1GCzLn28YX0H66pMkxuaeESFq4tKISKwdw==", + "version": "3.3.3", + "resolved": "https://registry.npmjs.org/nanoid/-/nanoid-3.3.3.tgz", + "integrity": "sha512-p1sjXuopFs0xg+fPASzQ28agW1oHD7xDsd9Xkf3T15H3c/cifrFHVwrh74PdoklAPi+i7MdRsE47vm2r6JoB+w==", "dev": true, "bin": { "nanoid": "bin/nanoid.cjs" @@ -4525,25 +5084,40 @@ "integrity": "sha1-Sr6/7tdUHywnrPspvbvRXI1bpPc=", "dev": true }, + "node_modules/netmask": { + "version": "2.0.2", + "resolved": "https://registry.npmjs.org/netmask/-/netmask-2.0.2.tgz", + "integrity": "sha512-dBpDMdxv9Irdq66304OLfEmQ9tbNRFnFTuZiLo+bD+r332bBmMJ8GBLXklIXXgxd3+v9+KUnZaUR5PJMa75Gsg==", + "dev": true, + "engines": { + "node": ">= 0.4.0" + } + }, "node_modules/new-github-release-url": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/new-github-release-url/-/new-github-release-url-1.0.0.tgz", - "integrity": "sha512-dle7yf655IMjyFUqn6Nxkb18r4AOAkzRcgcZv6WZ0IqrOH4QCEZ8Sm6I7XX21zvHdBeeMeTkhR9qT2Z0EJDx6A==", + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/new-github-release-url/-/new-github-release-url-2.0.0.tgz", + "integrity": "sha512-NHDDGYudnvRutt/VhKFlX26IotXe1w0cmkDm6JGquh5bz/bDTw0LufSmH/GxTjEdpHEO+bVKFTwdrcGa/9XlKQ==", "dev": true, "dependencies": { - "type-fest": "^0.4.1" + "type-fest": "^2.5.1" }, "engines": { - "node": ">=10" + "node": "^12.20.0 || ^14.13.1 || >=16.0.0" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" } }, "node_modules/new-github-release-url/node_modules/type-fest": { - "version": "0.4.1", - "resolved": "https://registry.npmjs.org/type-fest/-/type-fest-0.4.1.tgz", - "integrity": "sha512-IwzA/LSfD2vC1/YDYMv/zHP4rDF1usCwllsDpbolT3D4fUepIO7f9K70jjmUewU/LmGUKJcwcVtDCpnKk4BPMw==", + "version": "2.12.2", + "resolved": "https://registry.npmjs.org/type-fest/-/type-fest-2.12.2.tgz", + "integrity": "sha512-qt6ylCGpLjZ7AaODxbpyBZSs9fCI9SkL3Z9q2oxMBQhs/uyY+VD8jHA8ULCGmWQJlBgqvO3EJeAngOHD8zQCrQ==", "dev": true, "engines": { - "node": ">=6" + "node": ">=12.20" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" } }, "node_modules/nise": { @@ -4592,9 +5166,9 @@ } }, "node_modules/node-releases": { - "version": "2.0.3", - "resolved": "https://registry.npmjs.org/node-releases/-/node-releases-2.0.3.tgz", - "integrity": "sha512-maHFz6OLqYxz+VQyCAtA3PTX4UP/53pa05fyDNc9CwjvJ0yEh6+xBwKsgCxMNhS8taUKBFYxfuiaD9U/55iFaw==", + "version": "2.0.4", + "resolved": "https://registry.npmjs.org/node-releases/-/node-releases-2.0.4.tgz", + "integrity": "sha512-gbMzqQtTtDz/00jQzZ21PQzdI9PyLYqUSvD0p3naOhX4odFji0ZxYdnVwPTxmSwkmxhcFImpozceidSG+AgoPQ==", "dev": true }, "node_modules/normalize-path": { @@ -4607,24 +5181,42 @@ } }, "node_modules/normalize-url": { - "version": "4.5.1", - "resolved": "https://registry.npmjs.org/normalize-url/-/normalize-url-4.5.1.tgz", - "integrity": "sha512-9UZCFRHQdNrfTpGg8+1INIg93B6zE0aXMVFkw1WFwvO4SlZywU6aLg5Of0Ap/PgcbSw4LNxvMWXMeugwMCX0AA==", + "version": "6.1.0", + "resolved": "https://registry.npmjs.org/normalize-url/-/normalize-url-6.1.0.tgz", + "integrity": "sha512-DlL+XwOy3NxAQ8xuC0okPgK46iuVNAK01YN7RueYBqqFeGsBjV9XmCAzAdgt+667bCl5kPh9EqKKDwnaPG1I7A==", "dev": true, "engines": { - "node": ">=8" + "node": ">=10" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" } }, "node_modules/npm-run-path": { - "version": "4.0.1", - "resolved": "https://registry.npmjs.org/npm-run-path/-/npm-run-path-4.0.1.tgz", - "integrity": "sha512-S48WzZW777zhNIrn7gxOlISNAqi9ZC/uQFnRdbeIHhZhCA6UqpkOT8T1G7BvfdgP4Er8gF4sUbaS0i7QvIfCWw==", + "version": "5.1.0", + "resolved": "https://registry.npmjs.org/npm-run-path/-/npm-run-path-5.1.0.tgz", + "integrity": "sha512-sJOdmRGrY2sjNTRMbSvluQqg+8X7ZK61yvzBEIDhz4f8z1TZFYABsqjjCBd/0PUNE9M6QDgHJXQkGUEm7Q+l9Q==", "dev": true, "dependencies": { - "path-key": "^3.0.0" + "path-key": "^4.0.0" }, "engines": { - "node": ">=8" + "node": "^12.20.0 || ^14.13.1 || >=16.0.0" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/npm-run-path/node_modules/path-key": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/path-key/-/path-key-4.0.0.tgz", + "integrity": "sha512-haREypq7xkM7ErfgIyA0z+Bj4AGKlMSdlQE2jvJo6huWD1EdkKYV+G/T4nq0YEF2vgTT8kqMFKo1uHn950r4SQ==", + "dev": true, + "engines": { + "node": ">=12" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" } }, "node_modules/nyc": { @@ -4668,26 +5260,6 @@ "node": ">=8.9" } }, - "node_modules/nyc/node_modules/camelcase": { - "version": "5.3.1", - "resolved": "https://registry.npmjs.org/camelcase/-/camelcase-5.3.1.tgz", - "integrity": "sha512-L28STB170nwWS63UjtlEOE3dldQApaJXZkOI1uMFfzf3rRuPegHaHesyee+YxQ+W6SvRDQV6UrdOdRiR153wJg==", - "dev": true, - "engines": { - "node": ">=6" - } - }, - "node_modules/nyc/node_modules/cliui": { - "version": "6.0.0", - "resolved": "https://registry.npmjs.org/cliui/-/cliui-6.0.0.tgz", - "integrity": "sha512-t6wbgtoCXvAzst7QgXxJYqPt0usEfbgQdftEPbLL/cvv6HPE5VgvqCuAIDR0NgU52ds6rFwqrgakNLrHEjCbrQ==", - "dev": true, - "dependencies": { - "string-width": "^4.2.0", - "strip-ansi": "^6.0.0", - "wrap-ansi": "^6.2.0" - } - }, "node_modules/nyc/node_modules/resolve-from": { "version": "5.0.0", "resolved": "https://registry.npmjs.org/resolve-from/-/resolve-from-5.0.0.tgz", @@ -4697,61 +5269,6 @@ "node": ">=8" } }, - "node_modules/nyc/node_modules/wrap-ansi": { - "version": "6.2.0", - "resolved": "https://registry.npmjs.org/wrap-ansi/-/wrap-ansi-6.2.0.tgz", - "integrity": "sha512-r6lPcBGxZXlIcymEu7InxDMhdW0KDxpLgoFLcguasxCaJ/SOIZwINatK9KY/tf+ZrlywOKU0UDj3ATXUBfxJXA==", - "dev": true, - "dependencies": { - "ansi-styles": "^4.0.0", - "string-width": "^4.1.0", - "strip-ansi": "^6.0.0" - }, - "engines": { - "node": ">=8" - } - }, - "node_modules/nyc/node_modules/y18n": { - "version": "4.0.3", - "resolved": "https://registry.npmjs.org/y18n/-/y18n-4.0.3.tgz", - "integrity": "sha512-JKhqTOwSrqNA1NY5lSztJ1GrBiUodLMmIZuLiDaMRJ+itFd+ABVE8XBjOvIWL+rSqNDC74LCSFmlb/U4UZ4hJQ==", - "dev": true - }, - "node_modules/nyc/node_modules/yargs": { - "version": "15.4.1", - "resolved": "https://registry.npmjs.org/yargs/-/yargs-15.4.1.tgz", - "integrity": "sha512-aePbxDmcYW++PaqBsJ+HYUFwCdv4LVvdnhBy78E57PIor8/OVvhMrADFFEDh8DHDFRv/O9i3lPhsENjO7QX0+A==", - "dev": true, - "dependencies": { - "cliui": "^6.0.0", - "decamelize": "^1.2.0", - "find-up": "^4.1.0", - "get-caller-file": "^2.0.1", - "require-directory": "^2.1.1", - "require-main-filename": "^2.0.0", - "set-blocking": "^2.0.0", - "string-width": "^4.2.0", - "which-module": "^2.0.0", - "y18n": "^4.0.0", - "yargs-parser": "^18.1.2" - }, - "engines": { - "node": ">=8" - } - }, - "node_modules/nyc/node_modules/yargs-parser": { - "version": "18.1.3", - "resolved": "https://registry.npmjs.org/yargs-parser/-/yargs-parser-18.1.3.tgz", - "integrity": "sha512-o50j0JeToy/4K6OZcaQmW6lyXXKhq7csREXcDwk2omFPJEwUNOVtJKvmDr9EI1fAJZUyZcRF7kxGBWmRXudrCQ==", - "dev": true, - "dependencies": { - "camelcase": "^5.0.0", - "decamelize": "^1.2.0" - }, - "engines": { - "node": ">=6" - } - }, "node_modules/object-assign": { "version": "4.1.1", "resolved": "https://registry.npmjs.org/object-assign/-/object-assign-4.1.1.tgz", @@ -4807,31 +5324,32 @@ } }, "node_modules/onetime": { - "version": "5.1.2", - "resolved": "https://registry.npmjs.org/onetime/-/onetime-5.1.2.tgz", - "integrity": "sha512-kbpaSSGJTWdAY5KPVeMOKXSrPtr8C8C7wodJbcsd51jRnmD+GZu8Y0VoU6Dm5Z4vWr0Ig/1NKuWRKf7j5aaYSg==", + "version": "6.0.0", + "resolved": "https://registry.npmjs.org/onetime/-/onetime-6.0.0.tgz", + "integrity": "sha512-1FlR+gjXK7X+AsAHso35MnyN5KqGwJRi/31ft6x0M194ht7S+rWAvd7PHss9xSKMzE0asv1pyIHaJYq+BbacAQ==", "dev": true, "dependencies": { - "mimic-fn": "^2.1.0" + "mimic-fn": "^4.0.0" }, "engines": { - "node": ">=6" + "node": ">=12" }, "funding": { "url": "https://github.com/sponsors/sindresorhus" } }, "node_modules/open": { - "version": "7.4.2", - "resolved": "https://registry.npmjs.org/open/-/open-7.4.2.tgz", - "integrity": "sha512-MVHddDVweXZF3awtlAS+6pgKLlm/JgxZ90+/NBurBoQctVOOB/zDdVjcyPzQ+0laDGbsWgrRkflI65sQeOgT9Q==", + "version": "8.4.0", + "resolved": "https://registry.npmjs.org/open/-/open-8.4.0.tgz", + "integrity": "sha512-XgFPPM+B28FtCCgSb9I+s9szOC1vZRSwgWsRUA5ylIxRTgKozqjOCrVOqGsYABPYK5qnfqClxZTFBa8PKt2v6Q==", "dev": true, "dependencies": { - "is-docker": "^2.0.0", - "is-wsl": "^2.1.1" + "define-lazy-prop": "^2.0.0", + "is-docker": "^2.1.1", + "is-wsl": "^2.2.0" }, "engines": { - "node": ">=8" + "node": ">=12" }, "funding": { "url": "https://github.com/sponsors/sindresorhus" @@ -4855,39 +5373,121 @@ } }, "node_modules/ora": { - "version": "5.4.1", - "resolved": "https://registry.npmjs.org/ora/-/ora-5.4.1.tgz", - "integrity": "sha512-5b6Y85tPxZZ7QytO+BQzysW31HJku27cRIlkbAXaNx+BdcVi+LlRFmVXzeF6a7JCwJpyw5c4b+YSVImQIrBpuQ==", + "version": "6.1.0", + "resolved": "https://registry.npmjs.org/ora/-/ora-6.1.0.tgz", + "integrity": "sha512-CxEP6845hLK+NHFWZ+LplGO4zfw4QSfxTlqMfvlJ988GoiUeZDMzCvqsZkFHv69sPICmJH1MDxZoQFOKXerAVw==", + "dev": true, + "dependencies": { + "bl": "^5.0.0", + "chalk": "^5.0.0", + "cli-cursor": "^4.0.0", + "cli-spinners": "^2.6.1", + "is-interactive": "^2.0.0", + "is-unicode-supported": "^1.1.0", + "log-symbols": "^5.1.0", + "strip-ansi": "^7.0.1", + "wcwidth": "^1.0.1" + }, + "engines": { + "node": "^12.20.0 || ^14.13.1 || >=16.0.0" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/ora/node_modules/ansi-regex": { + "version": "6.0.1", + "resolved": "https://registry.npmjs.org/ansi-regex/-/ansi-regex-6.0.1.tgz", + "integrity": "sha512-n5M855fKb2SsfMIiFFoVrABHJC8QtHwVx+mHWP3QcEqBHYienj5dHSgjbxtC0WEZXYt4wcD6zrQElDPhFuZgfA==", + "dev": true, + "engines": { + "node": ">=12" + }, + "funding": { + "url": "https://github.com/chalk/ansi-regex?sponsor=1" + } + }, + "node_modules/ora/node_modules/cli-cursor": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/cli-cursor/-/cli-cursor-4.0.0.tgz", + "integrity": "sha512-VGtlMu3x/4DOtIUwEkRezxUZ2lBacNJCHash0N0WeZDBS+7Ux1dm3XWAgWYxLJFMMdOeXMHXorshEFhbMSGelg==", "dev": true, "dependencies": { - "bl": "^4.1.0", - "chalk": "^4.1.0", - "cli-cursor": "^3.1.0", - "cli-spinners": "^2.5.0", - "is-interactive": "^1.0.0", - "is-unicode-supported": "^0.1.0", - "log-symbols": "^4.1.0", - "strip-ansi": "^6.0.0", - "wcwidth": "^1.0.1" + "restore-cursor": "^4.0.0" }, "engines": { - "node": ">=10" + "node": "^12.20.0 || ^14.13.1 || >=16.0.0" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/ora/node_modules/mimic-fn": { + "version": "2.1.0", + "resolved": "https://registry.npmjs.org/mimic-fn/-/mimic-fn-2.1.0.tgz", + "integrity": "sha512-OqbOk5oEQeAZ8WXWydlu9HJjz9WVdEIvamMCcXmuqUYjTknH/sqsWvhQ3vgwKFRR1HpjvNBKQ37nbJgYzGqGcg==", + "dev": true, + "engines": { + "node": ">=6" + } + }, + "node_modules/ora/node_modules/onetime": { + "version": "5.1.2", + "resolved": "https://registry.npmjs.org/onetime/-/onetime-5.1.2.tgz", + "integrity": "sha512-kbpaSSGJTWdAY5KPVeMOKXSrPtr8C8C7wodJbcsd51jRnmD+GZu8Y0VoU6Dm5Z4vWr0Ig/1NKuWRKf7j5aaYSg==", + "dev": true, + "dependencies": { + "mimic-fn": "^2.1.0" + }, + "engines": { + "node": ">=6" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/ora/node_modules/restore-cursor": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/restore-cursor/-/restore-cursor-4.0.0.tgz", + "integrity": "sha512-I9fPXU9geO9bHOt9pHHOhOkYerIMsmVaWB0rA2AI9ERh/+x/i7MV5HKBNrg+ljO5eoPVgCcnFuRjJ9uH6I/3eg==", + "dev": true, + "dependencies": { + "onetime": "^5.1.0", + "signal-exit": "^3.0.2" + }, + "engines": { + "node": "^12.20.0 || ^14.13.1 || >=16.0.0" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/ora/node_modules/strip-ansi": { + "version": "7.0.1", + "resolved": "https://registry.npmjs.org/strip-ansi/-/strip-ansi-7.0.1.tgz", + "integrity": "sha512-cXNxvT8dFNRVfhVME3JAe98mkXDYN2O1l7jmcwMnOslDeESg1rF/OZMtK0nRAhiari1unG5cD4jG3rapUAkLbw==", + "dev": true, + "dependencies": { + "ansi-regex": "^6.0.1" + }, + "engines": { + "node": ">=12" }, "funding": { - "url": "https://github.com/sponsors/sindresorhus" + "url": "https://github.com/chalk/strip-ansi?sponsor=1" } }, "node_modules/os-name": { - "version": "4.0.1", - "resolved": "https://registry.npmjs.org/os-name/-/os-name-4.0.1.tgz", - "integrity": "sha512-xl9MAoU97MH1Xt5K9ERft2YfCAoaO6msy1OBA0ozxEC0x0TmIoE6K3QvgJMMZA9yKGLmHXNY/YZoDbiGDj4zYw==", + "version": "5.0.1", + "resolved": "https://registry.npmjs.org/os-name/-/os-name-5.0.1.tgz", + "integrity": "sha512-0EQpaHUHq7olp2/YFUr+0vZi9tMpDTblHGz+Ch5RntKxiRXOAY0JOz1UlxhSjMSksHvkm13eD6elJj3M8Ht/kw==", "dev": true, "dependencies": { - "macos-release": "^2.5.0", - "windows-release": "^4.0.0" + "macos-release": "^3.0.1", + "windows-release": "^5.0.1" }, "engines": { - "node": ">=10" + "node": "^12.20.0 || ^14.13.1 || >=16.0.0" }, "funding": { "url": "https://github.com/sponsors/sindresorhus" @@ -4903,12 +5503,12 @@ } }, "node_modules/p-cancelable": { - "version": "1.1.0", - "resolved": "https://registry.npmjs.org/p-cancelable/-/p-cancelable-1.1.0.tgz", - "integrity": "sha512-s73XxOZ4zpt1edZYZzvhqFa6uvQc1vwUa0K0BdtIZgQMAJj9IbebH+JkgKZc9h+B05PKHLOTl4ajG1BmNrVZlw==", + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/p-cancelable/-/p-cancelable-3.0.0.tgz", + "integrity": "sha512-mlVgR3PGuzlo0MmTdk4cXqXWlwQDLnONTAg6sm62XkMJEiRxN3GL3SffkYvqwonbkJBcrI7Uvv5Zh9yjvn2iUw==", "dev": true, "engines": { - "node": ">=6" + "node": ">=12.20" } }, "node_modules/p-limit": { @@ -4959,6 +5559,40 @@ "node": ">=6" } }, + "node_modules/pac-proxy-agent": { + "version": "5.0.0", + "resolved": "https://registry.npmjs.org/pac-proxy-agent/-/pac-proxy-agent-5.0.0.tgz", + "integrity": "sha512-CcFG3ZtnxO8McDigozwE3AqAw15zDvGH+OjXO4kzf7IkEKkQ4gxQ+3sdF50WmhQ4P/bVusXcqNE2S3XrNURwzQ==", + "dev": true, + "dependencies": { + "@tootallnate/once": "1", + "agent-base": "6", + "debug": "4", + "get-uri": "3", + "http-proxy-agent": "^4.0.1", + "https-proxy-agent": "5", + "pac-resolver": "^5.0.0", + "raw-body": "^2.2.0", + "socks-proxy-agent": "5" + }, + "engines": { + "node": ">= 8" + } + }, + "node_modules/pac-resolver": { + "version": "5.0.0", + "resolved": "https://registry.npmjs.org/pac-resolver/-/pac-resolver-5.0.0.tgz", + "integrity": "sha512-H+/A6KitiHNNW+bxBKREk2MCGSxljfqRX76NjummWEYIat7ldVXRU3dhRIE3iXZ0nvGBk6smv3nntxKkzRL8NA==", + "dev": true, + "dependencies": { + "degenerator": "^3.0.1", + "ip": "^1.1.5", + "netmask": "^2.0.1" + }, + "engines": { + "node": ">= 8" + } + }, "node_modules/package-hash": { "version": "4.0.0", "resolved": "https://registry.npmjs.org/package-hash/-/package-hash-4.0.0.tgz", @@ -4989,6 +5623,172 @@ "node": ">=8" } }, + "node_modules/package-json/node_modules/@sindresorhus/is": { + "version": "0.14.0", + "resolved": "https://registry.npmjs.org/@sindresorhus/is/-/is-0.14.0.tgz", + "integrity": "sha512-9NET910DNaIPngYnLLPeg+Ogzqsi9uM4mSboU5y6p8S5DzMTVEsJZrawi+BoDNUVBa2DhJqQYUFvMDfgU062LQ==", + "dev": true, + "engines": { + "node": ">=6" + } + }, + "node_modules/package-json/node_modules/@szmarczak/http-timer": { + "version": "1.1.2", + "resolved": "https://registry.npmjs.org/@szmarczak/http-timer/-/http-timer-1.1.2.tgz", + "integrity": "sha512-XIB2XbzHTN6ieIjfIMV9hlVcfPU26s2vafYWQcZHWXHOxiaRZYEDKEwdl129Zyg50+foYV2jCgtrqSA6qNuNSA==", + "dev": true, + "dependencies": { + "defer-to-connect": "^1.0.1" + }, + "engines": { + "node": ">=6" + } + }, + "node_modules/package-json/node_modules/cacheable-request": { + "version": "6.1.0", + "resolved": "https://registry.npmjs.org/cacheable-request/-/cacheable-request-6.1.0.tgz", + "integrity": "sha512-Oj3cAGPCqOZX7Rz64Uny2GYAZNliQSqfbePrgAQ1wKAihYmCUnraBtJtKcGR4xz7wF+LoJC+ssFZvv5BgF9Igg==", + "dev": true, + "dependencies": { + "clone-response": "^1.0.2", + "get-stream": "^5.1.0", + "http-cache-semantics": "^4.0.0", + "keyv": "^3.0.0", + "lowercase-keys": "^2.0.0", + "normalize-url": "^4.1.0", + "responselike": "^1.0.2" + }, + "engines": { + "node": ">=8" + } + }, + "node_modules/package-json/node_modules/cacheable-request/node_modules/get-stream": { + "version": "5.2.0", + "resolved": "https://registry.npmjs.org/get-stream/-/get-stream-5.2.0.tgz", + "integrity": "sha512-nBF+F1rAZVCu/p7rjzgA+Yb4lfYXrpl7a6VmJrU8wF9I1CKvP/QwPNZHnOlwbTkY6dvtFIzFMSyQXbLoTQPRpA==", + "dev": true, + "dependencies": { + "pump": "^3.0.0" + }, + "engines": { + "node": ">=8" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/package-json/node_modules/cacheable-request/node_modules/lowercase-keys": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/lowercase-keys/-/lowercase-keys-2.0.0.tgz", + "integrity": "sha512-tqNXrS78oMOE73NMxK4EMLQsQowWf8jKooH9g7xPavRT706R6bkQJ6DY2Te7QukaZsulxa30wQ7bk0pm4XiHmA==", + "dev": true, + "engines": { + "node": ">=8" + } + }, + "node_modules/package-json/node_modules/decompress-response": { + "version": "3.3.0", + "resolved": "https://registry.npmjs.org/decompress-response/-/decompress-response-3.3.0.tgz", + "integrity": "sha1-gKTdMjdIOEv6JICDYirt7Jgq3/M=", + "dev": true, + "dependencies": { + "mimic-response": "^1.0.0" + }, + "engines": { + "node": ">=4" + } + }, + "node_modules/package-json/node_modules/defer-to-connect": { + "version": "1.1.3", + "resolved": "https://registry.npmjs.org/defer-to-connect/-/defer-to-connect-1.1.3.tgz", + "integrity": "sha512-0ISdNousHvZT2EiFlZeZAHBUvSxmKswVCEf8hW7KWgG4a8MVEu/3Vb6uWYozkjylyCxe0JBIiRB1jV45S70WVQ==", + "dev": true + }, + "node_modules/package-json/node_modules/get-stream": { + "version": "4.1.0", + "resolved": "https://registry.npmjs.org/get-stream/-/get-stream-4.1.0.tgz", + "integrity": "sha512-GMat4EJ5161kIy2HevLlr4luNjBgvmj413KaQA7jt4V8B4RDsfpHk7WQ9GVqfYyyx8OS/L66Kox+rJRNklLK7w==", + "dev": true, + "dependencies": { + "pump": "^3.0.0" + }, + "engines": { + "node": ">=6" + } + }, + "node_modules/package-json/node_modules/got": { + "version": "9.6.0", + "resolved": "https://registry.npmjs.org/got/-/got-9.6.0.tgz", + "integrity": "sha512-R7eWptXuGYxwijs0eV+v3o6+XH1IqVK8dJOEecQfTmkncw9AV4dcw/Dhxi8MdlqPthxxpZyizMzyg8RTmEsG+Q==", + "dev": true, + "dependencies": { + "@sindresorhus/is": "^0.14.0", + "@szmarczak/http-timer": "^1.1.2", + "cacheable-request": "^6.0.0", + "decompress-response": "^3.3.0", + "duplexer3": "^0.1.4", + "get-stream": "^4.1.0", + "lowercase-keys": "^1.0.1", + "mimic-response": "^1.0.1", + "p-cancelable": "^1.0.0", + "to-readable-stream": "^1.0.0", + "url-parse-lax": "^3.0.0" + }, + "engines": { + "node": ">=8.6" + } + }, + "node_modules/package-json/node_modules/json-buffer": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/json-buffer/-/json-buffer-3.0.0.tgz", + "integrity": "sha1-Wx85evx11ne96Lz8Dkfh+aPZqJg=", + "dev": true + }, + "node_modules/package-json/node_modules/keyv": { + "version": "3.1.0", + "resolved": "https://registry.npmjs.org/keyv/-/keyv-3.1.0.tgz", + "integrity": "sha512-9ykJ/46SN/9KPM/sichzQ7OvXyGDYKGTaDlKMGCAlg2UK8KRy4jb0d8sFc+0Tt0YYnThq8X2RZgCg74RPxgcVA==", + "dev": true, + "dependencies": { + "json-buffer": "3.0.0" + } + }, + "node_modules/package-json/node_modules/lowercase-keys": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/lowercase-keys/-/lowercase-keys-1.0.1.tgz", + "integrity": "sha512-G2Lj61tXDnVFFOi8VZds+SoQjtQC3dgokKdDG2mTm1tx4m50NUHBOZSBwQQHyy0V12A0JTG4icfZQH+xPyh8VA==", + "dev": true, + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/package-json/node_modules/normalize-url": { + "version": "4.5.1", + "resolved": "https://registry.npmjs.org/normalize-url/-/normalize-url-4.5.1.tgz", + "integrity": "sha512-9UZCFRHQdNrfTpGg8+1INIg93B6zE0aXMVFkw1WFwvO4SlZywU6aLg5Of0Ap/PgcbSw4LNxvMWXMeugwMCX0AA==", + "dev": true, + "engines": { + "node": ">=8" + } + }, + "node_modules/package-json/node_modules/p-cancelable": { + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/p-cancelable/-/p-cancelable-1.1.0.tgz", + "integrity": "sha512-s73XxOZ4zpt1edZYZzvhqFa6uvQc1vwUa0K0BdtIZgQMAJj9IbebH+JkgKZc9h+B05PKHLOTl4ajG1BmNrVZlw==", + "dev": true, + "engines": { + "node": ">=6" + } + }, + "node_modules/package-json/node_modules/responselike": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/responselike/-/responselike-1.0.2.tgz", + "integrity": "sha1-kYcg7ztjHFZCvgaPFa3lpG9Loec=", + "dev": true, + "dependencies": { + "lowercase-keys": "^1.0.0" + } + }, "node_modules/parent-module": { "version": "1.0.1", "resolved": "https://registry.npmjs.org/parent-module/-/parent-module-1.0.1.tgz", @@ -5043,18 +5843,6 @@ "protocols": "^1.4.0" } }, - "node_modules/parse-url/node_modules/normalize-url": { - "version": "6.1.0", - "resolved": "https://registry.npmjs.org/normalize-url/-/normalize-url-6.1.0.tgz", - "integrity": "sha512-DlL+XwOy3NxAQ8xuC0okPgK46iuVNAK01YN7RueYBqqFeGsBjV9XmCAzAdgt+667bCl5kPh9EqKKDwnaPG1I7A==", - "dev": true, - "engines": { - "node": ">=10" - }, - "funding": { - "url": "https://github.com/sponsors/sindresorhus" - } - }, "node_modules/path-exists": { "version": "4.0.0", "resolved": "https://registry.npmjs.org/path-exists/-/path-exists-4.0.0.tgz", @@ -5228,6 +6016,31 @@ "integrity": "sha512-IgjKyaUSjsROSO8/D49Ab7hP8mJgTYcqApOqdPhLoPxAplXmkp+zRvsrSQjFn5by0rhm4VH0GAUELIPpx7B1yg==", "dev": true }, + "node_modules/proxy-agent": { + "version": "5.0.0", + "resolved": "https://registry.npmjs.org/proxy-agent/-/proxy-agent-5.0.0.tgz", + "integrity": "sha512-gkH7BkvLVkSfX9Dk27W6TyNOWWZWRilRfk1XxGNWOYJ2TuedAv1yFpCaU9QSBmBe716XOTNpYNOzhysyw8xn7g==", + "dev": true, + "dependencies": { + "agent-base": "^6.0.0", + "debug": "4", + "http-proxy-agent": "^4.0.0", + "https-proxy-agent": "^5.0.0", + "lru-cache": "^5.1.1", + "pac-proxy-agent": "^5.0.0", + "proxy-from-env": "^1.0.0", + "socks-proxy-agent": "^5.0.0" + }, + "engines": { + "node": ">= 8" + } + }, + "node_modules/proxy-from-env": { + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/proxy-from-env/-/proxy-from-env-1.1.0.tgz", + "integrity": "sha512-D+zkORCbA9f1tdWRK0RaCR3GPv50cMxcrz4X8k5LTSUD1Dkw47mKJEZQNunItRTkWwgtaUSo1RVFRIG9ZXiFYg==", + "dev": true + }, "node_modules/pump": { "version": "3.0.0", "resolved": "https://registry.npmjs.org/pump/-/pump-3.0.0.tgz", @@ -5312,6 +6125,18 @@ } ] }, + "node_modules/quick-lru": { + "version": "5.1.1", + "resolved": "https://registry.npmjs.org/quick-lru/-/quick-lru-5.1.1.tgz", + "integrity": "sha512-WuyALRjWPDGtt/wzJiadO5AXY+8hZ80hVpe6MyivgraREW751X3SbhRvG3eLKOYN+8VEvqLcf3wdnt44Z4S4SA==", + "dev": true, + "engines": { + "node": ">=10" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, "node_modules/randombytes": { "version": "2.1.0", "resolved": "https://registry.npmjs.org/randombytes/-/randombytes-2.1.0.tgz", @@ -5321,6 +6146,21 @@ "safe-buffer": "^5.1.0" } }, + "node_modules/raw-body": { + "version": "2.5.1", + "resolved": "https://registry.npmjs.org/raw-body/-/raw-body-2.5.1.tgz", + "integrity": "sha512-qqJBtEyVgS0ZmPGdCFPWJ3FreoqvG4MVQln/kCgF7Olq95IbOp0/BWyMwbdtn4VTvkM8Y7khCQ2Xgk/tcrCXig==", + "dev": true, + "dependencies": { + "bytes": "3.1.2", + "http-errors": "2.0.0", + "iconv-lite": "0.4.24", + "unpipe": "1.0.0" + }, + "engines": { + "node": ">= 0.8" + } + }, "node_modules/rc": { "version": "1.2.8", "resolved": "https://registry.npmjs.org/rc/-/rc-1.2.8.tgz", @@ -5342,6 +6182,15 @@ "integrity": "sha512-JV/yugV2uzW5iMRSiZAyDtQd+nxtUnjeLt0acNdw98kKLrvuRVyB80tsREOE7yvGVgalhZ6RNXCmEHkUKBKxew==", "dev": true }, + "node_modules/rc/node_modules/strip-json-comments": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/strip-json-comments/-/strip-json-comments-2.0.1.tgz", + "integrity": "sha1-PFMZQukIwml8DsNEhYwobHygpgo=", + "dev": true, + "engines": { + "node": ">=0.10.0" + } + }, "node_modules/readable-stream": { "version": "3.6.0", "resolved": "https://registry.npmjs.org/readable-stream/-/readable-stream-3.6.0.tgz", @@ -5417,82 +6266,80 @@ } }, "node_modules/release-it": { - "version": "14.14.2", - "resolved": "https://registry.npmjs.org/release-it/-/release-it-14.14.2.tgz", - "integrity": "sha512-+TE5Zg7x5BE/xw6i8SN9rmsGxCE2GVqH1v8Ay1L09nsLQx1HJhihAqUtCCDdgOuLvGpX0xgDMsSzakDlDLpOkA==", + "version": "15.0.0", + "resolved": "https://registry.npmjs.org/release-it/-/release-it-15.0.0.tgz", + "integrity": "sha512-Dnio6p+1O88UdQZmPjdXqq+Nrrn5t0USZyOctTPK5M36kOOfQTdp8V1Wlagz9QYIYr93NwovEZ+f4wK0P/kHbw==", "dev": true, "dependencies": { "@iarna/toml": "2.2.5", "@octokit/rest": "18.12.0", "async-retry": "1.3.3", - "chalk": "4.1.2", + "chalk": "5.0.1", "cosmiconfig": "7.0.1", - "debug": "4.3.4", - "execa": "5.1.1", + "execa": "6.1.0", "form-data": "4.0.0", "git-url-parse": "11.6.0", - "globby": "11.0.4", - "got": "9.6.0", - "import-cwd": "3.0.0", - "inquirer": "8.2.0", + "globby": "13.1.1", + "got": "12.0.4", + "inquirer": "8.2.4", "is-ci": "3.0.1", "lodash": "4.17.21", "mime-types": "2.1.35", - "new-github-release-url": "1.0.0", - "open": "7.4.2", - "ora": "5.4.1", - "os-name": "4.0.1", - "parse-json": "5.2.0", + "new-github-release-url": "2.0.0", + "open": "8.4.0", + "ora": "6.1.0", + "os-name": "5.0.1", "promise.allsettled": "1.0.5", - "semver": "7.3.5", + "proxy-agent": "5.0.0", + "semver": "7.3.7", "shelljs": "0.8.5", "update-notifier": "5.1.0", - "url-join": "4.0.1", - "uuid": "8.3.2", + "url-join": "5.0.0", "wildcard-match": "5.1.2", - "yaml": "1.10.2", - "yargs-parser": "20.2.9" + "yargs-parser": "21.0.1" }, "bin": { "release-it": "bin/release-it.js" }, "engines": { - "node": ">=10" - } - }, - "node_modules/release-it/node_modules/array-union": { - "version": "2.1.0", - "resolved": "https://registry.npmjs.org/array-union/-/array-union-2.1.0.tgz", - "integrity": "sha512-HGyxoOTYUyCM6stUe6EJgnd4EoewAI7zMdfqO+kGjnlZmBDz/cR5pf8r/cR4Wq60sL/p0IkcjUEEPwS3GFrIyw==", - "dev": true, - "engines": { - "node": ">=8" + "node": ">=14.9" } }, "node_modules/release-it/node_modules/globby": { - "version": "11.0.4", - "resolved": "https://registry.npmjs.org/globby/-/globby-11.0.4.tgz", - "integrity": "sha512-9O4MVG9ioZJ08ffbcyVYyLOJLk5JQ688pJ4eMGLpdWLHq/Wr1D9BlriLQyL0E+jbkuePVZXYFj47QM/v093wHg==", + "version": "13.1.1", + "resolved": "https://registry.npmjs.org/globby/-/globby-13.1.1.tgz", + "integrity": "sha512-XMzoDZbGZ37tufiv7g0N4F/zp3zkwdFtVbV3EHsVl1KQr4RPLfNoT068/97RPshz2J5xYNEjLKKBKaGHifBd3Q==", "dev": true, "dependencies": { - "array-union": "^2.1.0", "dir-glob": "^3.0.1", - "fast-glob": "^3.1.1", - "ignore": "^5.1.4", - "merge2": "^1.3.0", - "slash": "^3.0.0" + "fast-glob": "^3.2.11", + "ignore": "^5.2.0", + "merge2": "^1.4.1", + "slash": "^4.0.0" }, "engines": { - "node": ">=10" + "node": "^12.20.0 || ^14.13.1 || >=16.0.0" }, "funding": { "url": "https://github.com/sponsors/sindresorhus" } }, + "node_modules/release-it/node_modules/lru-cache": { + "version": "6.0.0", + "resolved": "https://registry.npmjs.org/lru-cache/-/lru-cache-6.0.0.tgz", + "integrity": "sha512-Jo6dJ04CmSjuznwJSS3pUeWmd/H0ffTlkXXgwZi+eq1UCmqQwCh+eLsYOYCwY991i2Fah4h1BEMCx4qThGbsiA==", + "dev": true, + "dependencies": { + "yallist": "^4.0.0" + }, + "engines": { + "node": ">=10" + } + }, "node_modules/release-it/node_modules/semver": { - "version": "7.3.5", - "resolved": "https://registry.npmjs.org/semver/-/semver-7.3.5.tgz", - "integrity": "sha512-PoeGJYh8HK4BTO/a9Tf6ZG3veo/A7ZVsYrSA6J8ny9nb3B1VrpkuN+z9OE5wfE5p6H4LchYZsegiQgbJD94ZFQ==", + "version": "7.3.7", + "resolved": "https://registry.npmjs.org/semver/-/semver-7.3.7.tgz", + "integrity": "sha512-QlYTucUYOews+WeEujDoEGziz4K6c47V/Bd+LjSSYcA94p+DmINdf7ncaUinThfvZyu13lN9OY1XDxt8C0Tw0g==", "dev": true, "dependencies": { "lru-cache": "^6.0.0" @@ -5501,9 +6348,27 @@ "semver": "bin/semver.js" }, "engines": { - "node": ">=10" + "node": ">=10" + } + }, + "node_modules/release-it/node_modules/slash": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/slash/-/slash-4.0.0.tgz", + "integrity": "sha512-3dOsAHXXUkQTpOYcoAxLIorMTp4gIQr5IW3iVb7A7lFIp0VHhnynm9izx6TssdrIcVIESAlVjtnO2K8bg+Coew==", + "dev": true, + "engines": { + "node": ">=12" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" } }, + "node_modules/release-it/node_modules/yallist": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/yallist/-/yallist-4.0.0.tgz", + "integrity": "sha512-3wdGidZyq5PB084XLES5TpOSRA3wjXAlIWMhum2kRcv/41Sn2emQ0dycQW4uZXLejwKvg6EsvbdlVL+FYEct7A==", + "dev": true + }, "node_modules/release-zalgo": { "version": "1.0.0", "resolved": "https://registry.npmjs.org/release-zalgo/-/release-zalgo-1.0.0.tgz", @@ -5548,6 +6413,12 @@ "url": "https://github.com/sponsors/ljharb" } }, + "node_modules/resolve-alpn": { + "version": "1.2.1", + "resolved": "https://registry.npmjs.org/resolve-alpn/-/resolve-alpn-1.2.1.tgz", + "integrity": "sha512-0a1F4l73/ZFZOakJnQ3FvkJ2+gSTQWz/r2KE5OdDY0TxPm5h4GkqkWWfM47T7HsbnOtcJVEF4epCVy6u7Q3K+g==", + "dev": true + }, "node_modules/resolve-from": { "version": "4.0.0", "resolved": "https://registry.npmjs.org/resolve-from/-/resolve-from-4.0.0.tgz", @@ -5558,12 +6429,21 @@ } }, "node_modules/responselike": { - "version": "1.0.2", - "resolved": "https://registry.npmjs.org/responselike/-/responselike-1.0.2.tgz", - "integrity": "sha1-kYcg7ztjHFZCvgaPFa3lpG9Loec=", + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/responselike/-/responselike-2.0.0.tgz", + "integrity": "sha512-xH48u3FTB9VsZw7R+vvgaKeLKzT6jOogbQhEe/jewwnZgzPcnyWui2Av6JpoYZF/91uueC+lqhWqeURw5/qhCw==", "dev": true, "dependencies": { - "lowercase-keys": "^1.0.0" + "lowercase-keys": "^2.0.0" + } + }, + "node_modules/responselike/node_modules/lowercase-keys": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/lowercase-keys/-/lowercase-keys-2.0.0.tgz", + "integrity": "sha512-tqNXrS78oMOE73NMxK4EMLQsQowWf8jKooH9g7xPavRT706R6bkQJ6DY2Te7QukaZsulxa30wQ7bk0pm4XiHmA==", + "dev": true, + "engines": { + "node": ">=8" } }, "node_modules/restore-cursor": { @@ -5579,6 +6459,30 @@ "node": ">=8" } }, + "node_modules/restore-cursor/node_modules/mimic-fn": { + "version": "2.1.0", + "resolved": "https://registry.npmjs.org/mimic-fn/-/mimic-fn-2.1.0.tgz", + "integrity": "sha512-OqbOk5oEQeAZ8WXWydlu9HJjz9WVdEIvamMCcXmuqUYjTknH/sqsWvhQ3vgwKFRR1HpjvNBKQ37nbJgYzGqGcg==", + "dev": true, + "engines": { + "node": ">=6" + } + }, + "node_modules/restore-cursor/node_modules/onetime": { + "version": "5.1.2", + "resolved": "https://registry.npmjs.org/onetime/-/onetime-5.1.2.tgz", + "integrity": "sha512-kbpaSSGJTWdAY5KPVeMOKXSrPtr8C8C7wodJbcsd51jRnmD+GZu8Y0VoU6Dm5Z4vWr0Ig/1NKuWRKf7j5aaYSg==", + "dev": true, + "dependencies": { + "mimic-fn": "^2.1.0" + }, + "engines": { + "node": ">=6" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, "node_modules/retry": { "version": "0.13.1", "resolved": "https://registry.npmjs.org/retry/-/retry-0.13.1.tgz", @@ -5655,24 +6559,10 @@ } }, "node_modules/safe-buffer": { - "version": "5.2.1", - "resolved": "https://registry.npmjs.org/safe-buffer/-/safe-buffer-5.2.1.tgz", - "integrity": "sha512-rp3So07KcdmmKbGvgaNxQSJr7bGVSVk5S9Eq1F+ppbRo70+YeaDxkw5Dd8NPN+GD6bjnYm2VuPuCXmpuYvmCXQ==", - "dev": true, - "funding": [ - { - "type": "github", - "url": "https://github.com/sponsors/feross" - }, - { - "type": "patreon", - "url": "https://www.patreon.com/feross" - }, - { - "type": "consulting", - "url": "https://feross.org/support" - } - ] + "version": "5.1.2", + "resolved": "https://registry.npmjs.org/safe-buffer/-/safe-buffer-5.1.2.tgz", + "integrity": "sha512-Gd2UZBJDkXlY7GbJxfsE8/nvKkUEU1G38c1siN6QP6a9PT9MmHB8GnpscSmMJSoF8LOIrt8ud/wPtojys4G6+g==", + "dev": true }, "node_modules/safer-buffer": { "version": "2.1.2", @@ -5716,6 +6606,12 @@ "integrity": "sha1-BF+XgtARrppoA93TgrJDkrPYkPc=", "dev": true }, + "node_modules/setprototypeof": { + "version": "1.2.0", + "resolved": "https://registry.npmjs.org/setprototypeof/-/setprototypeof-1.2.0.tgz", + "integrity": "sha512-E5LDX7Wrp85Kil5bhZv46j8jOeboKq5JMmYM3gVGdGH8xFpPWXUMsNrlODCrkoxMEeNi/XZIwuRvY4XNwYMJpw==", + "dev": true + }, "node_modules/shebang-command": { "version": "2.0.0", "resolved": "https://registry.npmjs.org/shebang-command/-/shebang-command-2.0.0.tgz", @@ -5812,26 +6708,45 @@ "node": ">=8" } }, - "node_modules/source-map": { - "version": "0.5.7", - "resolved": "https://registry.npmjs.org/source-map/-/source-map-0.5.7.tgz", - "integrity": "sha1-igOdLRAh0i0eoUyA2OpGi6LvP8w=", + "node_modules/smart-buffer": { + "version": "4.2.0", + "resolved": "https://registry.npmjs.org/smart-buffer/-/smart-buffer-4.2.0.tgz", + "integrity": "sha512-94hK0Hh8rPqQl2xXc3HsaBoOXKV20MToPkcXvwbISWLEs+64sBq5kFgn2kJDHb1Pry9yrP0dxrCI9RRci7RXKg==", "dev": true, "engines": { - "node": ">=0.10.0" + "node": ">= 6.0.0", + "npm": ">= 3.0.0" } }, - "node_modules/source-map-support": { - "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==", + "node_modules/socks": { + "version": "2.6.2", + "resolved": "https://registry.npmjs.org/socks/-/socks-2.6.2.tgz", + "integrity": "sha512-zDZhHhZRY9PxRruRMR7kMhnf3I8hDs4S3f9RecfnGxvcBHQcKcIH/oUcEWffsfl1XxdYlA7nnlGbbTvPz9D8gA==", "dev": true, "dependencies": { - "buffer-from": "^1.0.0", - "source-map": "^0.6.0" + "ip": "^1.1.5", + "smart-buffer": "^4.2.0" + }, + "engines": { + "node": ">= 10.13.0", + "npm": ">= 3.0.0" + } + }, + "node_modules/socks-proxy-agent": { + "version": "5.0.1", + "resolved": "https://registry.npmjs.org/socks-proxy-agent/-/socks-proxy-agent-5.0.1.tgz", + "integrity": "sha512-vZdmnjb9a2Tz6WEQVIurybSwElwPxMZaIc7PzqbJTrezcKNznv6giT7J7tZDZ1BojVaa1jvO/UiUdhDVB0ACoQ==", + "dev": true, + "dependencies": { + "agent-base": "^6.0.2", + "debug": "4", + "socks": "^2.3.3" + }, + "engines": { + "node": ">= 6" } }, - "node_modules/source-map-support/node_modules/source-map": { + "node_modules/source-map": { "version": "0.6.1", "resolved": "https://registry.npmjs.org/source-map/-/source-map-0.6.1.tgz", "integrity": "sha512-UjgapumWlbMhkBgzT7Ykc5YXUT46F0iKu8SGXq0bcwP5dz/h0Plj6enJqjz1Zbq2l5WaqYnrVbwWOWMyF3F47g==", @@ -5840,6 +6755,16 @@ "node": ">=0.10.0" } }, + "node_modules/source-map-support": { + "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", + "source-map": "^0.6.0" + } + }, "node_modules/spawn-wrap": { "version": "2.0.0", "resolved": "https://registry.npmjs.org/spawn-wrap/-/spawn-wrap-2.0.0.tgz", @@ -5872,6 +6797,15 @@ "integrity": "sha1-BOaSb2YolTVPPdAVIDYzuFcpfiw=", "dev": true }, + "node_modules/statuses": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/statuses/-/statuses-2.0.1.tgz", + "integrity": "sha512-RwNA9Z/7PrK06rYLIzFMlaF+l73iwpzsqRIFgbMLbTcLD6cOao82TaWefPXQvB2fOC4AjuYSEndS7N/mTCbkdQ==", + "dev": true, + "engines": { + "node": ">= 0.8" + } + }, "node_modules/strict-uri-encode": { "version": "2.0.0", "resolved": "https://registry.npmjs.org/strict-uri-encode/-/strict-uri-encode-2.0.0.tgz", @@ -5890,6 +6824,26 @@ "safe-buffer": "~5.2.0" } }, + "node_modules/string_decoder/node_modules/safe-buffer": { + "version": "5.2.1", + "resolved": "https://registry.npmjs.org/safe-buffer/-/safe-buffer-5.2.1.tgz", + "integrity": "sha512-rp3So07KcdmmKbGvgaNxQSJr7bGVSVk5S9Eq1F+ppbRo70+YeaDxkw5Dd8NPN+GD6bjnYm2VuPuCXmpuYvmCXQ==", + "dev": true, + "funding": [ + { + "type": "github", + "url": "https://github.com/sponsors/feross" + }, + { + "type": "patreon", + "url": "https://www.patreon.com/feross" + }, + { + "type": "consulting", + "url": "https://feross.org/support" + } + ] + }, "node_modules/string-width": { "version": "4.2.3", "resolved": "https://registry.npmjs.org/string-width/-/string-width-4.2.3.tgz", @@ -5952,21 +6906,27 @@ } }, "node_modules/strip-final-newline": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/strip-final-newline/-/strip-final-newline-2.0.0.tgz", - "integrity": "sha512-BrpvfNAE3dcvq7ll3xVumzjKjZQ5tI1sEUIKr3Uoks0XUl45St3FlatVqef9prk4jRDzhW6WZg+3bk93y6pLjA==", + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/strip-final-newline/-/strip-final-newline-3.0.0.tgz", + "integrity": "sha512-dOESqjYr96iWYylGObzd39EuNTa5VJxyvVAEm5Jnh7KGo75V43Hk1odPQkNDyXNmUR6k+gEiDVXnjB8HJ3crXw==", "dev": true, "engines": { - "node": ">=6" + "node": ">=12" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" } }, "node_modules/strip-json-comments": { - "version": "2.0.1", - "resolved": "https://registry.npmjs.org/strip-json-comments/-/strip-json-comments-2.0.1.tgz", - "integrity": "sha1-PFMZQukIwml8DsNEhYwobHygpgo=", + "version": "3.1.1", + "resolved": "https://registry.npmjs.org/strip-json-comments/-/strip-json-comments-3.1.1.tgz", + "integrity": "sha512-6fPc+R4ihwqP6N/aIv2f1gMH8lOVtWQHoqC4yK6oSDVVocumAsfCqjkXnqiYMhmMwS/mEHLp7Vehlt3ql6lEig==", "dev": true, "engines": { - "node": ">=0.10.0" + "node": ">=8" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" } }, "node_modules/strip-outer": { @@ -6073,6 +7033,15 @@ "node": ">=8.0" } }, + "node_modules/toidentifier": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/toidentifier/-/toidentifier-1.0.1.tgz", + "integrity": "sha512-o5sSPKEkg/DIQNmH43V0/uerLrpzVedkUh8tGNvaeXpfpuwjKenlSox/2O/BTlZUtEe+JG7s5YhEz608PlAHRA==", + "dev": true, + "engines": { + "node": ">=0.6" + } + }, "node_modules/tr46": { "version": "0.0.3", "resolved": "https://registry.npmjs.org/tr46/-/tr46-0.0.3.tgz", @@ -6256,9 +7225,9 @@ } }, "node_modules/typescript": { - "version": "4.6.3", - "resolved": "https://registry.npmjs.org/typescript/-/typescript-4.6.3.tgz", - "integrity": "sha512-yNIatDa5iaofVozS/uQJEl3JRWLKKGJKh6Yaiv0GLGSuhpFJe7P3SbHZ8/yjAHRQwKRoA6YZqlfjXWmVzoVSMw==", + "version": "4.6.4", + "resolved": "https://registry.npmjs.org/typescript/-/typescript-4.6.4.tgz", + "integrity": "sha512-9ia/jWHIEbo49HfjrLGfKbZSuWo9iTMwXO+Ca3pRsSpbsMbc7/IU8NKdCZVRRBafVPGnoJeFL76ZOAA84I9fEg==", "dev": true, "bin": { "tsc": "bin/tsc", @@ -6310,6 +7279,15 @@ "node": ">= 4.0.0" } }, + "node_modules/unpipe": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/unpipe/-/unpipe-1.0.0.tgz", + "integrity": "sha1-sr9O6FFKrmFltIF4KdIbLvSZBOw=", + "dev": true, + "engines": { + "node": ">= 0.8" + } + }, "node_modules/update-notifier": { "version": "5.1.0", "resolved": "https://registry.npmjs.org/update-notifier/-/update-notifier-5.1.0.tgz", @@ -6338,6 +7316,22 @@ "url": "https://github.com/yeoman/update-notifier?sponsor=1" } }, + "node_modules/update-notifier/node_modules/chalk": { + "version": "4.1.2", + "resolved": "https://registry.npmjs.org/chalk/-/chalk-4.1.2.tgz", + "integrity": "sha512-oKnbhFyRIXpUuez8iBMmyEa4nbj4IOQyuhc/wy9kY7/WVPcwIO9VA668Pu8RkO7+0G76SLROeyw9CpQ061i4mA==", + "dev": true, + "dependencies": { + "ansi-styles": "^4.1.0", + "supports-color": "^7.1.0" + }, + "engines": { + "node": ">=10" + }, + "funding": { + "url": "https://github.com/chalk/chalk?sponsor=1" + } + }, "node_modules/update-notifier/node_modules/ci-info": { "version": "2.0.0", "resolved": "https://registry.npmjs.org/ci-info/-/ci-info-2.0.0.tgz", @@ -6356,6 +7350,18 @@ "is-ci": "bin.js" } }, + "node_modules/update-notifier/node_modules/lru-cache": { + "version": "6.0.0", + "resolved": "https://registry.npmjs.org/lru-cache/-/lru-cache-6.0.0.tgz", + "integrity": "sha512-Jo6dJ04CmSjuznwJSS3pUeWmd/H0ffTlkXXgwZi+eq1UCmqQwCh+eLsYOYCwY991i2Fah4h1BEMCx4qThGbsiA==", + "dev": true, + "dependencies": { + "yallist": "^4.0.0" + }, + "engines": { + "node": ">=10" + } + }, "node_modules/update-notifier/node_modules/semver": { "version": "7.3.7", "resolved": "https://registry.npmjs.org/semver/-/semver-7.3.7.tgz", @@ -6371,6 +7377,12 @@ "node": ">=10" } }, + "node_modules/update-notifier/node_modules/yallist": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/yallist/-/yallist-4.0.0.tgz", + "integrity": "sha512-3wdGidZyq5PB084XLES5TpOSRA3wjXAlIWMhum2kRcv/41Sn2emQ0dycQW4uZXLejwKvg6EsvbdlVL+FYEct7A==", + "dev": true + }, "node_modules/uri-js": { "version": "4.4.1", "resolved": "https://registry.npmjs.org/uri-js/-/uri-js-4.4.1.tgz", @@ -6381,10 +7393,13 @@ } }, "node_modules/url-join": { - "version": "4.0.1", - "resolved": "https://registry.npmjs.org/url-join/-/url-join-4.0.1.tgz", - "integrity": "sha512-jk1+QP6ZJqyOiuEI9AEWQfju/nB2Pw466kbA0LEZljHwKeMgd9WrAEgEGxjPDD2+TNbbb37rTyhEfrCXfuKXnA==", - "dev": true + "version": "5.0.0", + "resolved": "https://registry.npmjs.org/url-join/-/url-join-5.0.0.tgz", + "integrity": "sha512-n2huDr9h9yzd6exQVnH/jU5mr+Pfx08LRXXZhkLLetAMESRj+anQsTAh940iMrIetKAmry9coFuZQ2jY8/p3WA==", + "dev": true, + "engines": { + "node": "^12.20.0 || ^14.13.1 || >=16.0.0" + } }, "node_modules/url-parse-lax": { "version": "3.0.0", @@ -6405,12 +7420,13 @@ "dev": true }, "node_modules/uuid": { - "version": "8.3.2", - "resolved": "https://registry.npmjs.org/uuid/-/uuid-8.3.2.tgz", - "integrity": "sha512-+NYs2QeMWy+GWFOEm9xnn6HCDp0l7QBD7ml8zLUmJ+93Q5NF0NocErnwkTkXVFNiX3/fpC6afS8Dhb/gz7R7eg==", + "version": "3.4.0", + "resolved": "https://registry.npmjs.org/uuid/-/uuid-3.4.0.tgz", + "integrity": "sha512-HjSDRw6gZE5JMggctHBcjVak08+KEVhSIiDzFnT9S9aegmp85S/bReBVTb4QTFaRNptJ9kuYaNhnbNEOkbKb/A==", + "deprecated": "Please upgrade to version 7 or higher. Older versions may use Math.random() in certain circumstances, which is known to be problematic. See https://v8.dev/blog/math-random for details.", "dev": true, "bin": { - "uuid": "dist/bin/uuid" + "uuid": "bin/uuid" } }, "node_modules/v8-compile-cache": { @@ -6425,6 +7441,22 @@ "integrity": "sha512-wa7YjyUGfNZngI/vtK0UHAN+lgDCxBPCylVXGp0zu59Fz5aiGtNXaq3DhIov063MorB+VfufLh3JlF2KdTK3xg==", "dev": true }, + "node_modules/vm2": { + "version": "3.9.9", + "resolved": "https://registry.npmjs.org/vm2/-/vm2-3.9.9.tgz", + "integrity": "sha512-xwTm7NLh/uOjARRBs8/95H0e8fT3Ukw5D/JJWhxMbhKzNh1Nu981jQKvkep9iKYNxzlVrdzD0mlBGkDKZWprlw==", + "dev": true, + "dependencies": { + "acorn": "^8.7.0", + "acorn-walk": "^8.2.0" + }, + "bin": { + "vm2": "bin/vm2" + }, + "engines": { + "node": ">=6.0" + } + }, "node_modules/vscode-oniguruma": { "version": "1.6.2", "resolved": "https://registry.npmjs.org/vscode-oniguruma/-/vscode-oniguruma-1.6.2.tgz", @@ -6518,65 +7550,107 @@ "dev": true }, "node_modules/windows-release": { - "version": "4.0.0", - "resolved": "https://registry.npmjs.org/windows-release/-/windows-release-4.0.0.tgz", - "integrity": "sha512-OxmV4wzDKB1x7AZaZgXMVsdJ1qER1ed83ZrTYd5Bwq2HfJVg3DJS8nqlAG4sMoJ7mu8cuRmLEYyU13BKwctRAg==", + "version": "5.0.1", + "resolved": "https://registry.npmjs.org/windows-release/-/windows-release-5.0.1.tgz", + "integrity": "sha512-y1xFdFvdMiDXI3xiOhMbJwt1Y7dUxidha0CWPs1NgjZIjZANTcX7+7bMqNjuezhzb8s5JGEiBAbQjQQYYy7ulw==", + "dev": true, + "dependencies": { + "execa": "^5.1.1" + }, + "engines": { + "node": "^12.20.0 || ^14.13.1 || >=16.0.0" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/windows-release/node_modules/execa": { + "version": "5.1.1", + "resolved": "https://registry.npmjs.org/execa/-/execa-5.1.1.tgz", + "integrity": "sha512-8uSpZZocAZRBAPIEINJj3Lo9HyGitllczc27Eh5YYojjMFMn8yHMDMaUHE2Jqfq05D/wucwI4JGURyXt1vchyg==", + "dev": true, + "dependencies": { + "cross-spawn": "^7.0.3", + "get-stream": "^6.0.0", + "human-signals": "^2.1.0", + "is-stream": "^2.0.0", + "merge-stream": "^2.0.0", + "npm-run-path": "^4.0.1", + "onetime": "^5.1.2", + "signal-exit": "^3.0.3", + "strip-final-newline": "^2.0.0" + }, + "engines": { + "node": ">=10" + }, + "funding": { + "url": "https://github.com/sindresorhus/execa?sponsor=1" + } + }, + "node_modules/windows-release/node_modules/human-signals": { + "version": "2.1.0", + "resolved": "https://registry.npmjs.org/human-signals/-/human-signals-2.1.0.tgz", + "integrity": "sha512-B4FFZ6q/T2jhhksgkbEW3HBvWIfDW85snkQgawt07S7J5QXTk6BkNV+0yAeZrM5QpMAdYlocGoljn0sJ/WQkFw==", + "dev": true, + "engines": { + "node": ">=10.17.0" + } + }, + "node_modules/windows-release/node_modules/is-stream": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/is-stream/-/is-stream-2.0.1.tgz", + "integrity": "sha512-hFoiJiTl63nn+kstHGBtewWSKnQLpyb155KHheA1l39uvtO9nWIop1p3udqPcUd/xbF1VLMO4n7OI6p7RbngDg==", "dev": true, - "dependencies": { - "execa": "^4.0.2" - }, "engines": { - "node": ">=10" + "node": ">=8" }, "funding": { "url": "https://github.com/sponsors/sindresorhus" } }, - "node_modules/windows-release/node_modules/execa": { - "version": "4.1.0", - "resolved": "https://registry.npmjs.org/execa/-/execa-4.1.0.tgz", - "integrity": "sha512-j5W0//W7f8UxAn8hXVnwG8tLwdiUy4FJLcSupCg6maBYZDpyBvTApK7KyuI4bKj8KOh1r2YH+6ucuYtJv1bTZA==", + "node_modules/windows-release/node_modules/mimic-fn": { + "version": "2.1.0", + "resolved": "https://registry.npmjs.org/mimic-fn/-/mimic-fn-2.1.0.tgz", + "integrity": "sha512-OqbOk5oEQeAZ8WXWydlu9HJjz9WVdEIvamMCcXmuqUYjTknH/sqsWvhQ3vgwKFRR1HpjvNBKQ37nbJgYzGqGcg==", + "dev": true, + "engines": { + "node": ">=6" + } + }, + "node_modules/windows-release/node_modules/npm-run-path": { + "version": "4.0.1", + "resolved": "https://registry.npmjs.org/npm-run-path/-/npm-run-path-4.0.1.tgz", + "integrity": "sha512-S48WzZW777zhNIrn7gxOlISNAqi9ZC/uQFnRdbeIHhZhCA6UqpkOT8T1G7BvfdgP4Er8gF4sUbaS0i7QvIfCWw==", "dev": true, "dependencies": { - "cross-spawn": "^7.0.0", - "get-stream": "^5.0.0", - "human-signals": "^1.1.1", - "is-stream": "^2.0.0", - "merge-stream": "^2.0.0", - "npm-run-path": "^4.0.0", - "onetime": "^5.1.0", - "signal-exit": "^3.0.2", - "strip-final-newline": "^2.0.0" + "path-key": "^3.0.0" }, "engines": { - "node": ">=10" - }, - "funding": { - "url": "https://github.com/sindresorhus/execa?sponsor=1" + "node": ">=8" } }, - "node_modules/windows-release/node_modules/get-stream": { - "version": "5.2.0", - "resolved": "https://registry.npmjs.org/get-stream/-/get-stream-5.2.0.tgz", - "integrity": "sha512-nBF+F1rAZVCu/p7rjzgA+Yb4lfYXrpl7a6VmJrU8wF9I1CKvP/QwPNZHnOlwbTkY6dvtFIzFMSyQXbLoTQPRpA==", + "node_modules/windows-release/node_modules/onetime": { + "version": "5.1.2", + "resolved": "https://registry.npmjs.org/onetime/-/onetime-5.1.2.tgz", + "integrity": "sha512-kbpaSSGJTWdAY5KPVeMOKXSrPtr8C8C7wodJbcsd51jRnmD+GZu8Y0VoU6Dm5Z4vWr0Ig/1NKuWRKf7j5aaYSg==", "dev": true, "dependencies": { - "pump": "^3.0.0" + "mimic-fn": "^2.1.0" }, "engines": { - "node": ">=8" + "node": ">=6" }, "funding": { "url": "https://github.com/sponsors/sindresorhus" } }, - "node_modules/windows-release/node_modules/human-signals": { - "version": "1.1.1", - "resolved": "https://registry.npmjs.org/human-signals/-/human-signals-1.1.1.tgz", - "integrity": "sha512-SEQu7vl8KjNL2eoGBLF3+wAjpsNfA9XMlXAYj/3EdaNfAlxKthD1xjEQfGOUhllCGGJVNY34bRr6lPINhNjyZw==", + "node_modules/windows-release/node_modules/strip-final-newline": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/strip-final-newline/-/strip-final-newline-2.0.0.tgz", + "integrity": "sha512-BrpvfNAE3dcvq7ll3xVumzjKjZQ5tI1sEUIKr3Uoks0XUl45St3FlatVqef9prk4jRDzhW6WZg+3bk93y6pLjA==", "dev": true, "engines": { - "node": ">=8.12.0" + "node": ">=6" } }, "node_modules/word-wrap": { @@ -6589,9 +7663,9 @@ } }, "node_modules/workerpool": { - "version": "6.2.0", - "resolved": "https://registry.npmjs.org/workerpool/-/workerpool-6.2.0.tgz", - "integrity": "sha512-Rsk5qQHJ9eowMH28Jwhe8HEbmdYDX4lwoMWshiCXugjtHqMD9ZbiqSDLxcsfdqsETPzVUtX5s1Z5kStiIM6l4A==", + "version": "6.2.1", + "resolved": "https://registry.npmjs.org/workerpool/-/workerpool-6.2.1.tgz", + "integrity": "sha512-ILEIE97kDZvF9Wb9f6h5aXK4swSlKGUcOEGiIYb2OOu/IrDU9iwj0fD//SsA6E5ibwJxpEvhullJY4Sl4GcpAw==", "dev": true }, "node_modules/wrap-ansi": { @@ -6638,19 +7712,26 @@ "node": ">=8" } }, - "node_modules/y18n": { - "version": "5.0.8", - "resolved": "https://registry.npmjs.org/y18n/-/y18n-5.0.8.tgz", - "integrity": "sha512-0pfFzegeDWJHJIAmTLRP2DwHjdF5s7jo9tuztdQxAhINCdvS+3nGINqPd00AphqJR/0LhANUS6/+7SCb98YOfA==", + "node_modules/xregexp": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/xregexp/-/xregexp-2.0.0.tgz", + "integrity": "sha1-UqY+VsoLhKfzpfPWGHLxJq16WUM=", "dev": true, "engines": { - "node": ">=10" + "node": "*" } }, + "node_modules/y18n": { + "version": "4.0.3", + "resolved": "https://registry.npmjs.org/y18n/-/y18n-4.0.3.tgz", + "integrity": "sha512-JKhqTOwSrqNA1NY5lSztJ1GrBiUodLMmIZuLiDaMRJ+itFd+ABVE8XBjOvIWL+rSqNDC74LCSFmlb/U4UZ4hJQ==", + "dev": true + }, "node_modules/yallist": { - "version": "4.0.0", - "resolved": "https://registry.npmjs.org/yallist/-/yallist-4.0.0.tgz", - "integrity": "sha512-3wdGidZyq5PB084XLES5TpOSRA3wjXAlIWMhum2kRcv/41Sn2emQ0dycQW4uZXLejwKvg6EsvbdlVL+FYEct7A==" + "version": "3.1.1", + "resolved": "https://registry.npmjs.org/yallist/-/yallist-3.1.1.tgz", + "integrity": "sha512-a4UGQaWPH59mOXUYnAG2ewncQS4i4F43Tv3JoAM+s2VDAmS9NsK8GpDMLrCHPksFT7h3K6TOoUNn2pb7RoXx4g==", + "dev": true }, "node_modules/yaml": { "version": "1.10.2", @@ -6662,30 +7743,34 @@ } }, "node_modules/yargs": { - "version": "17.4.1", - "resolved": "https://registry.npmjs.org/yargs/-/yargs-17.4.1.tgz", - "integrity": "sha512-WSZD9jgobAg3ZKuCQZSa3g9QOJeCCqLoLAykiWgmXnDo9EPnn4RPf5qVTtzgOx66o6/oqhcA5tHtJXpG8pMt3g==", + "version": "15.4.1", + "resolved": "https://registry.npmjs.org/yargs/-/yargs-15.4.1.tgz", + "integrity": "sha512-aePbxDmcYW++PaqBsJ+HYUFwCdv4LVvdnhBy78E57PIor8/OVvhMrADFFEDh8DHDFRv/O9i3lPhsENjO7QX0+A==", "dev": true, "dependencies": { - "cliui": "^7.0.2", - "escalade": "^3.1.1", - "get-caller-file": "^2.0.5", + "cliui": "^6.0.0", + "decamelize": "^1.2.0", + "find-up": "^4.1.0", + "get-caller-file": "^2.0.1", "require-directory": "^2.1.1", - "string-width": "^4.2.3", - "y18n": "^5.0.5", - "yargs-parser": "^21.0.0" + "require-main-filename": "^2.0.0", + "set-blocking": "^2.0.0", + "string-width": "^4.2.0", + "which-module": "^2.0.0", + "y18n": "^4.0.0", + "yargs-parser": "^18.1.2" }, "engines": { - "node": ">=12" + "node": ">=8" } }, "node_modules/yargs-parser": { - "version": "20.2.9", - "resolved": "https://registry.npmjs.org/yargs-parser/-/yargs-parser-20.2.9.tgz", - "integrity": "sha512-y11nGElTIV+CT3Zv9t7VKl+Q3hTQoT9a1Qzezhhl6Rp21gJ/IVTW7Z3y9EWXhuUBC2Shnf+DX0antecpAwSP8w==", + "version": "21.0.1", + "resolved": "https://registry.npmjs.org/yargs-parser/-/yargs-parser-21.0.1.tgz", + "integrity": "sha512-9BK1jFpLzJROCI5TzwZL/TU4gqjK5xiHV/RfWLOahrjAko/e4DJkRDZQXfvqAsiZzzYhgAzbgz6lg48jcm4GLg==", "dev": true, "engines": { - "node": ">=10" + "node": ">=12" } }, "node_modules/yargs-unparser": { @@ -6703,6 +7788,18 @@ "node": ">=10" } }, + "node_modules/yargs-unparser/node_modules/camelcase": { + "version": "6.3.0", + "resolved": "https://registry.npmjs.org/camelcase/-/camelcase-6.3.0.tgz", + "integrity": "sha512-Gmy6FhYlCY7uOElZUSbxo2UCDH8owEk996gkbrpsgGtrJLM3J7jGxl9Ic7Qwwj4ivOE5AWZWRMecDdF7hqGjFA==", + "dev": true, + "engines": { + "node": ">=10" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, "node_modules/yargs-unparser/node_modules/decamelize": { "version": "4.0.0", "resolved": "https://registry.npmjs.org/decamelize/-/decamelize-4.0.0.tgz", @@ -6716,12 +7813,16 @@ } }, "node_modules/yargs/node_modules/yargs-parser": { - "version": "21.0.1", - "resolved": "https://registry.npmjs.org/yargs-parser/-/yargs-parser-21.0.1.tgz", - "integrity": "sha512-9BK1jFpLzJROCI5TzwZL/TU4gqjK5xiHV/RfWLOahrjAko/e4DJkRDZQXfvqAsiZzzYhgAzbgz6lg48jcm4GLg==", + "version": "18.1.3", + "resolved": "https://registry.npmjs.org/yargs-parser/-/yargs-parser-18.1.3.tgz", + "integrity": "sha512-o50j0JeToy/4K6OZcaQmW6lyXXKhq7csREXcDwk2omFPJEwUNOVtJKvmDr9EI1fAJZUyZcRF7kxGBWmRXudrCQ==", "dev": true, + "dependencies": { + "camelcase": "^5.0.0", + "decamelize": "^1.2.0" + }, "engines": { - "node": ">=12" + "node": ">=6" } }, "node_modules/yn": { @@ -6746,26 +7847,26 @@ } }, "packages/bloom": { - "name": "@node-redis/bloom", + "name": "@redis/bloom", "version": "1.0.1", "license": "MIT", "devDependencies": { "@istanbuljs/nyc-config-typescript": "^1.0.2", - "@node-redis/test-utils": "*", - "@types/node": "^17.0.26", + "@redis/test-utils": "*", + "@types/node": "^17.0.31", "nyc": "^15.1.0", - "release-it": "^14.14.2", + "release-it": "^15.0.0", "source-map-support": "^0.5.21", "ts-node": "^10.7.0", "typedoc": "^0.22.15", - "typescript": "^4.6.3" + "typescript": "^4.6.4" }, "peerDependencies": { - "@node-redis/client": "^1.0.0" + "@redis/client": "^1.0.0" } }, "packages/client": { - "name": "@node-redis/client", + "name": "@redis/client", "version": "1.0.5", "license": "MIT", "dependencies": { @@ -6775,128 +7876,172 @@ }, "devDependencies": { "@istanbuljs/nyc-config-typescript": "^1.0.2", - "@node-redis/test-utils": "*", - "@types/node": "^17.0.26", + "@redis/test-utils": "*", + "@types/node": "^17.0.31", "@types/sinon": "^10.0.11", "@types/yallist": "^4.0.1", - "@typescript-eslint/eslint-plugin": "^5.20.0", - "@typescript-eslint/parser": "^5.20.0", + "@typescript-eslint/eslint-plugin": "^5.21.0", + "@typescript-eslint/parser": "^5.21.0", "eslint": "^8.14.0", "nyc": "^15.1.0", - "release-it": "^14.14.2", + "release-it": "^15.0.0", "sinon": "^13.0.2", "source-map-support": "^0.5.21", "ts-node": "^10.7.0", "typedoc": "^0.22.15", - "typescript": "^4.6.3" + "typescript": "^4.6.4" }, "engines": { - "node": ">=12" + "node": ">=14" } }, + "packages/client/node_modules/yallist": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/yallist/-/yallist-4.0.0.tgz", + "integrity": "sha512-3wdGidZyq5PB084XLES5TpOSRA3wjXAlIWMhum2kRcv/41Sn2emQ0dycQW4uZXLejwKvg6EsvbdlVL+FYEct7A==" + }, "packages/graph": { - "name": "@node-redis/graph", + "name": "@redis/graph", "version": "1.0.0", "license": "MIT", "devDependencies": { "@istanbuljs/nyc-config-typescript": "^1.0.2", - "@node-redis/test-utils": "*", - "@types/node": "^17.0.26", + "@redis/test-utils": "*", + "@types/node": "^17.0.31", "nyc": "^15.1.0", - "release-it": "^14.14.2", + "release-it": "^15.0.0", "source-map-support": "^0.5.21", "ts-node": "^10.7.0", "typedoc": "^0.22.15", - "typescript": "^4.6.3" + "typescript": "^4.6.4" }, "peerDependencies": { - "@node-redis/client": "^1.0.0" + "@redis/client": "^1.0.0" } }, "packages/json": { - "name": "@node-redis/json", + "name": "@redis/json", "version": "1.0.2", "license": "MIT", "devDependencies": { "@istanbuljs/nyc-config-typescript": "^1.0.2", - "@node-redis/test-utils": "*", - "@types/node": "^17.0.26", + "@redis/test-utils": "*", + "@types/node": "^17.0.31", "nyc": "^15.1.0", - "release-it": "^14.14.2", + "release-it": "^15.0.0", "source-map-support": "^0.5.21", "ts-node": "^10.7.0", "typedoc": "^0.22.15", - "typescript": "^4.6.3" + "typescript": "^4.6.4" }, "peerDependencies": { - "@node-redis/client": "^1.0.0" + "@redis/client": "^1.0.0" } }, "packages/search": { - "name": "@node-redis/search", + "name": "@redis/search", "version": "1.0.5", "license": "MIT", "devDependencies": { "@istanbuljs/nyc-config-typescript": "^1.0.2", - "@node-redis/test-utils": "*", - "@types/node": "^17.0.26", + "@redis/test-utils": "*", + "@types/node": "^17.0.31", "nyc": "^15.1.0", - "release-it": "^14.14.2", + "release-it": "^15.0.0", "source-map-support": "^0.5.21", "ts-node": "^10.7.0", "typedoc": "^0.22.15", - "typescript": "^4.6.3" + "typescript": "^4.6.4" }, "peerDependencies": { - "@node-redis/client": "^1.0.0" + "@redis/client": "^1.0.0" } }, "packages/test-utils": { - "name": "@node-redis/test-utils", + "name": "@redis/test-utils", "devDependencies": { "@istanbuljs/nyc-config-typescript": "^1.0.2", "@types/mocha": "^9.1.1", - "@types/node": "^17.0.26", + "@types/node": "^17.0.31", "@types/yargs": "^17.0.10", - "mocha": "^9.2.2", + "mocha": "^10.0.0", "nyc": "^15.1.0", "source-map-support": "^0.5.21", "ts-node": "^10.7.0", - "typescript": "^4.6.3", + "typescript": "^4.6.4", "yargs": "^17.4.1" }, "peerDependencies": { - "@node-redis/client": "^1.0.0" + "@redis/client": "^1.0.0" + } + }, + "packages/test-utils/node_modules/cliui": { + "version": "7.0.4", + "resolved": "https://registry.npmjs.org/cliui/-/cliui-7.0.4.tgz", + "integrity": "sha512-OcRE68cOsVMXp1Yvonl/fzkQOyjLSu/8bhPDfQt0e0/Eb283TKP20Fs2MqoPsr9SwA595rRCA+QMzYc9nBP+JQ==", + "dev": true, + "dependencies": { + "string-width": "^4.2.0", + "strip-ansi": "^6.0.0", + "wrap-ansi": "^7.0.0" + } + }, + "packages/test-utils/node_modules/y18n": { + "version": "5.0.8", + "resolved": "https://registry.npmjs.org/y18n/-/y18n-5.0.8.tgz", + "integrity": "sha512-0pfFzegeDWJHJIAmTLRP2DwHjdF5s7jo9tuztdQxAhINCdvS+3nGINqPd00AphqJR/0LhANUS6/+7SCb98YOfA==", + "dev": true, + "engines": { + "node": ">=10" + } + }, + "packages/test-utils/node_modules/yargs": { + "version": "17.4.1", + "resolved": "https://registry.npmjs.org/yargs/-/yargs-17.4.1.tgz", + "integrity": "sha512-WSZD9jgobAg3ZKuCQZSa3g9QOJeCCqLoLAykiWgmXnDo9EPnn4RPf5qVTtzgOx66o6/oqhcA5tHtJXpG8pMt3g==", + "dev": true, + "dependencies": { + "cliui": "^7.0.2", + "escalade": "^3.1.1", + "get-caller-file": "^2.0.5", + "require-directory": "^2.1.1", + "string-width": "^4.2.3", + "y18n": "^5.0.5", + "yargs-parser": "^21.0.0" + }, + "engines": { + "node": ">=12" } }, "packages/time-series": { - "name": "@node-redis/time-series", + "name": "@redis/time-series", "version": "1.0.2", "license": "MIT", "devDependencies": { "@istanbuljs/nyc-config-typescript": "^1.0.2", - "@node-redis/test-utils": "*", - "@types/node": "^17.0.26", + "@redis/test-utils": "*", + "@types/node": "^17.0.31", "nyc": "^15.1.0", - "release-it": "^14.14.2", + "release-it": "^15.0.0", "source-map-support": "^0.5.21", "ts-node": "^10.7.0", "typedoc": "^0.22.15", - "typescript": "^4.6.3" + "typescript": "^4.6.4" }, "peerDependencies": { - "@node-redis/client": "^1.0.0" + "@redis/client": "^1.0.0" } } }, "dependencies": { "@ampproject/remapping": { - "version": "2.1.2", - "resolved": "https://registry.npmjs.org/@ampproject/remapping/-/remapping-2.1.2.tgz", - "integrity": "sha512-hoyByceqwKirw7w3Z7gnIIZC3Wx3J484Y3L/cMpXFbr7d9ZQj2mODrirNzcJa+SM3UlpWXYvKV4RlRpFXlWgXg==", + "version": "2.2.0", + "resolved": "https://registry.npmjs.org/@ampproject/remapping/-/remapping-2.2.0.tgz", + "integrity": "sha512-qRmjj8nj9qmLTQXXmaR1cck3UXSRMPrbsLJAasZpF+t3riI71BXed5ebIOYwQntykeZuhjsdweEc9BxH5Jc26w==", "dev": true, "requires": { - "@jridgewell/trace-mapping": "^0.3.0" + "@jridgewell/gen-mapping": "^0.1.0", + "@jridgewell/trace-mapping": "^0.3.9" } }, "@babel/code-frame": { @@ -6909,27 +8054,27 @@ } }, "@babel/compat-data": { - "version": "7.17.7", - "resolved": "https://registry.npmjs.org/@babel/compat-data/-/compat-data-7.17.7.tgz", - "integrity": "sha512-p8pdE6j0a29TNGebNm7NzYZWB3xVZJBZ7XGs42uAKzQo8VQ3F0By/cQCtUEABwIqw5zo6WA4NbmxsfzADzMKnQ==", + "version": "7.17.10", + "resolved": "https://registry.npmjs.org/@babel/compat-data/-/compat-data-7.17.10.tgz", + "integrity": "sha512-GZt/TCsG70Ms19gfZO1tM4CVnXsPgEPBCpJu+Qz3L0LUDsY5nZqFZglIoPC1kIYOtNBZlrnFT+klg12vFGZXrw==", "dev": true }, "@babel/core": { - "version": "7.17.9", - "resolved": "https://registry.npmjs.org/@babel/core/-/core-7.17.9.tgz", - "integrity": "sha512-5ug+SfZCpDAkVp9SFIZAzlW18rlzsOcJGaetCjkySnrXXDUw9AR8cDUm1iByTmdWM6yxX6/zycaV76w3YTF2gw==", + "version": "7.17.10", + "resolved": "https://registry.npmjs.org/@babel/core/-/core-7.17.10.tgz", + "integrity": "sha512-liKoppandF3ZcBnIYFjfSDHZLKdLHGJRkoWtG8zQyGJBQfIYobpnVGI5+pLBNtS6psFLDzyq8+h5HiVljW9PNA==", "dev": true, "requires": { "@ampproject/remapping": "^2.1.0", "@babel/code-frame": "^7.16.7", - "@babel/generator": "^7.17.9", - "@babel/helper-compilation-targets": "^7.17.7", + "@babel/generator": "^7.17.10", + "@babel/helper-compilation-targets": "^7.17.10", "@babel/helper-module-transforms": "^7.17.7", "@babel/helpers": "^7.17.9", - "@babel/parser": "^7.17.9", + "@babel/parser": "^7.17.10", "@babel/template": "^7.16.7", - "@babel/traverse": "^7.17.9", - "@babel/types": "^7.17.0", + "@babel/traverse": "^7.17.10", + "@babel/types": "^7.17.10", "convert-source-map": "^1.7.0", "debug": "^4.1.0", "gensync": "^1.0.0-beta.2", @@ -6938,25 +8083,25 @@ } }, "@babel/generator": { - "version": "7.17.9", - "resolved": "https://registry.npmjs.org/@babel/generator/-/generator-7.17.9.tgz", - "integrity": "sha512-rAdDousTwxbIxbz5I7GEQ3lUip+xVCXooZNbsydCWs3xA7ZsYOv+CFRdzGxRX78BmQHu9B1Eso59AOZQOJDEdQ==", + "version": "7.17.10", + "resolved": "https://registry.npmjs.org/@babel/generator/-/generator-7.17.10.tgz", + "integrity": "sha512-46MJZZo9y3o4kmhBVc7zW7i8dtR1oIK/sdO5NcfcZRhTGYi+KKJRtHNgsU6c4VUcJmUNV/LQdebD/9Dlv4K+Tg==", "dev": true, "requires": { - "@babel/types": "^7.17.0", - "jsesc": "^2.5.1", - "source-map": "^0.5.0" + "@babel/types": "^7.17.10", + "@jridgewell/gen-mapping": "^0.1.0", + "jsesc": "^2.5.1" } }, "@babel/helper-compilation-targets": { - "version": "7.17.7", - "resolved": "https://registry.npmjs.org/@babel/helper-compilation-targets/-/helper-compilation-targets-7.17.7.tgz", - "integrity": "sha512-UFzlz2jjd8kroj0hmCFV5zr+tQPi1dpC2cRsDV/3IEW8bJfCPrPpmcSN6ZS8RqIq4LXcmpipCQFPddyFA5Yc7w==", + "version": "7.17.10", + "resolved": "https://registry.npmjs.org/@babel/helper-compilation-targets/-/helper-compilation-targets-7.17.10.tgz", + "integrity": "sha512-gh3RxjWbauw/dFiU/7whjd0qN9K6nPJMqe6+Er7rOavFh0CQUSwhAE3IcTho2rywPJFxej6TUUHDkWcYI6gGqQ==", "dev": true, "requires": { - "@babel/compat-data": "^7.17.7", + "@babel/compat-data": "^7.17.10", "@babel/helper-validator-option": "^7.16.7", - "browserslist": "^4.17.5", + "browserslist": "^4.20.2", "semver": "^6.3.0" } }, @@ -7118,9 +8263,9 @@ } }, "@babel/parser": { - "version": "7.17.9", - "resolved": "https://registry.npmjs.org/@babel/parser/-/parser-7.17.9.tgz", - "integrity": "sha512-vqUSBLP8dQHFPdPi9bc5GK9vRkYHJ49fsZdtoJ8EQ8ibpwk5rPKfvNIwChB0KVXcIjcepEBBd2VHC5r9Gy8ueg==", + "version": "7.17.10", + "resolved": "https://registry.npmjs.org/@babel/parser/-/parser-7.17.10.tgz", + "integrity": "sha512-n2Q6i+fnJqzOaq2VkdXxy2TCPCWQZHiCo0XqmrCvDWcZQKRyZzYi4Z0yxlBuN0w+r2ZHmre+Q087DSrw3pbJDQ==", "dev": true }, "@babel/template": { @@ -7135,19 +8280,19 @@ } }, "@babel/traverse": { - "version": "7.17.9", - "resolved": "https://registry.npmjs.org/@babel/traverse/-/traverse-7.17.9.tgz", - "integrity": "sha512-PQO8sDIJ8SIwipTPiR71kJQCKQYB5NGImbOviK8K+kg5xkNSYXLBupuX9QhatFowrsvo9Hj8WgArg3W7ijNAQw==", + "version": "7.17.10", + "resolved": "https://registry.npmjs.org/@babel/traverse/-/traverse-7.17.10.tgz", + "integrity": "sha512-VmbrTHQteIdUUQNTb+zE12SHS/xQVIShmBPhlNP12hD5poF2pbITW1Z4172d03HegaQWhLffdkRJYtAzp0AGcw==", "dev": true, "requires": { "@babel/code-frame": "^7.16.7", - "@babel/generator": "^7.17.9", + "@babel/generator": "^7.17.10", "@babel/helper-environment-visitor": "^7.16.7", "@babel/helper-function-name": "^7.17.9", "@babel/helper-hoist-variables": "^7.16.7", "@babel/helper-split-export-declaration": "^7.16.7", - "@babel/parser": "^7.17.9", - "@babel/types": "^7.17.0", + "@babel/parser": "^7.17.10", + "@babel/types": "^7.17.10", "debug": "^4.1.0", "globals": "^11.1.0" }, @@ -7161,9 +8306,9 @@ } }, "@babel/types": { - "version": "7.17.0", - "resolved": "https://registry.npmjs.org/@babel/types/-/types-7.17.0.tgz", - "integrity": "sha512-TmKSNO4D5rzhL5bjWFcVHHLETzfQ/AmbKpKPOSjlP0WoHZ6L911fgoOKY4Alp/emzG4cHJdyN49zpgkbXFEHHw==", + "version": "7.17.10", + "resolved": "https://registry.npmjs.org/@babel/types/-/types-7.17.10.tgz", + "integrity": "sha512-9O26jG0mBYfGkUYCYZRnBwbVLd1UZOICEr2Em6InB6jVfsAv1GKgwXHmrSg+WFWDmeKTA6vyTZiN8tCSM5Oo3A==", "dev": true, "requires": { "@babel/helper-validator-identifier": "^7.16.7", @@ -7200,14 +8345,6 @@ "js-yaml": "^4.1.0", "minimatch": "^3.0.4", "strip-json-comments": "^3.1.1" - }, - "dependencies": { - "strip-json-comments": { - "version": "3.1.1", - "resolved": "https://registry.npmjs.org/strip-json-comments/-/strip-json-comments-3.1.1.tgz", - "integrity": "sha512-6fPc+R4ihwqP6N/aIv2f1gMH8lOVtWQHoqC4yK6oSDVVocumAsfCqjkXnqiYMhmMwS/mEHLp7Vehlt3ql6lEig==", - "dev": true - } } }, "@humanwhocodes/config-array": { @@ -7255,12 +8392,6 @@ "sprintf-js": "~1.0.2" } }, - "camelcase": { - "version": "5.3.1", - "resolved": "https://registry.npmjs.org/camelcase/-/camelcase-5.3.1.tgz", - "integrity": "sha512-L28STB170nwWS63UjtlEOE3dldQApaJXZkOI1uMFfzf3rRuPegHaHesyee+YxQ+W6SvRDQV6UrdOdRiR153wJg==", - "dev": true - }, "js-yaml": { "version": "3.14.1", "resolved": "https://registry.npmjs.org/js-yaml/-/js-yaml-3.14.1.tgz", @@ -7276,152 +8407,60 @@ "resolved": "https://registry.npmjs.org/resolve-from/-/resolve-from-5.0.0.tgz", "integrity": "sha512-qYg9KP24dD5qka9J47d0aVky0N+b4fTU89LN9iDnjB5waksiC49rvMB0PrUJQGoTmH50XPiqOvAjDfaijGxYZw==", "dev": true - } - } - }, - "@istanbuljs/nyc-config-typescript": { - "version": "1.0.2", - "resolved": "https://registry.npmjs.org/@istanbuljs/nyc-config-typescript/-/nyc-config-typescript-1.0.2.tgz", - "integrity": "sha512-iKGIyMoyJuFnJRSVTZ78POIRvNnwZaWIf8vG4ZS3rQq58MMDrqEX2nnzx0R28V2X8JvmKYiqY9FP2hlJsm8A0w==", - "dev": true, - "requires": { - "@istanbuljs/schema": "^0.1.2" - } - }, - "@istanbuljs/schema": { - "version": "0.1.3", - "resolved": "https://registry.npmjs.org/@istanbuljs/schema/-/schema-0.1.3.tgz", - "integrity": "sha512-ZXRY4jNvVgSVQ8DL3LTcakaAtXwTVUxE81hslsyD2AtoXW/wVob10HkOJ1X/pAlcI7D+2YoZKg5do8G/w6RYgA==", - "dev": true - }, - "@jridgewell/resolve-uri": { - "version": "3.0.6", - "resolved": "https://registry.npmjs.org/@jridgewell/resolve-uri/-/resolve-uri-3.0.6.tgz", - "integrity": "sha512-R7xHtBSNm+9SyvpJkdQl+qrM3Hm2fea3Ef197M3mUug+v+yR+Rhfbs7PBtcBUVnIWJ4JcAdjvij+c8hXS9p5aw==", - "dev": true - }, - "@jridgewell/sourcemap-codec": { - "version": "1.4.11", - "resolved": "https://registry.npmjs.org/@jridgewell/sourcemap-codec/-/sourcemap-codec-1.4.11.tgz", - "integrity": "sha512-Fg32GrJo61m+VqYSdRSjRXMjQ06j8YIYfcTqndLYVAaHmroZHLJZCydsWBOTDqXS2v+mjxohBWEMfg97GXmYQg==", - "dev": true - }, - "@jridgewell/trace-mapping": { - "version": "0.3.9", - "resolved": "https://registry.npmjs.org/@jridgewell/trace-mapping/-/trace-mapping-0.3.9.tgz", - "integrity": "sha512-3Belt6tdc8bPgAtbcmdtNJlirVoTmEb5e2gC94PnkwEW9jI6CAHUeoG85tjWP5WquqfavoMtMwiG4P926ZKKuQ==", - "dev": true, - "requires": { - "@jridgewell/resolve-uri": "^3.0.3", - "@jridgewell/sourcemap-codec": "^1.4.10" - } - }, - "@node-redis/bloom": { - "version": "file:packages/bloom", - "requires": { - "@istanbuljs/nyc-config-typescript": "^1.0.2", - "@node-redis/test-utils": "*", - "@types/node": "^17.0.26", - "nyc": "^15.1.0", - "release-it": "^14.14.2", - "source-map-support": "^0.5.21", - "ts-node": "^10.7.0", - "typedoc": "^0.22.15", - "typescript": "^4.6.3" - } - }, - "@node-redis/client": { - "version": "file:packages/client", - "requires": { - "@istanbuljs/nyc-config-typescript": "^1.0.2", - "@node-redis/test-utils": "*", - "@types/node": "^17.0.26", - "@types/sinon": "^10.0.11", - "@types/yallist": "^4.0.1", - "@typescript-eslint/eslint-plugin": "^5.20.0", - "@typescript-eslint/parser": "^5.20.0", - "cluster-key-slot": "1.1.0", - "eslint": "^8.14.0", - "generic-pool": "3.8.2", - "nyc": "^15.1.0", - "release-it": "^14.14.2", - "sinon": "^13.0.2", - "source-map-support": "^0.5.21", - "ts-node": "^10.7.0", - "typedoc": "^0.22.15", - "typescript": "^4.6.3", - "yallist": "4.0.0" - } - }, - "@node-redis/graph": { - "version": "file:packages/graph", - "requires": { - "@istanbuljs/nyc-config-typescript": "^1.0.2", - "@node-redis/test-utils": "*", - "@types/node": "^17.0.26", - "nyc": "^15.1.0", - "release-it": "^14.14.2", - "source-map-support": "^0.5.21", - "ts-node": "^10.7.0", - "typedoc": "^0.22.15", - "typescript": "^4.6.3" - } - }, - "@node-redis/json": { - "version": "file:packages/json", - "requires": { - "@istanbuljs/nyc-config-typescript": "^1.0.2", - "@node-redis/test-utils": "*", - "@types/node": "^17.0.26", - "nyc": "^15.1.0", - "release-it": "^14.14.2", - "source-map-support": "^0.5.21", - "ts-node": "^10.7.0", - "typedoc": "^0.22.15", - "typescript": "^4.6.3" + } } }, - "@node-redis/search": { - "version": "file:packages/search", + "@istanbuljs/nyc-config-typescript": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/@istanbuljs/nyc-config-typescript/-/nyc-config-typescript-1.0.2.tgz", + "integrity": "sha512-iKGIyMoyJuFnJRSVTZ78POIRvNnwZaWIf8vG4ZS3rQq58MMDrqEX2nnzx0R28V2X8JvmKYiqY9FP2hlJsm8A0w==", + "dev": true, "requires": { - "@istanbuljs/nyc-config-typescript": "^1.0.2", - "@node-redis/test-utils": "*", - "@types/node": "^17.0.26", - "nyc": "^15.1.0", - "release-it": "^14.14.2", - "source-map-support": "^0.5.21", - "ts-node": "^10.7.0", - "typedoc": "^0.22.15", - "typescript": "^4.6.3" + "@istanbuljs/schema": "^0.1.2" } }, - "@node-redis/test-utils": { - "version": "file:packages/test-utils", + "@istanbuljs/schema": { + "version": "0.1.3", + "resolved": "https://registry.npmjs.org/@istanbuljs/schema/-/schema-0.1.3.tgz", + "integrity": "sha512-ZXRY4jNvVgSVQ8DL3LTcakaAtXwTVUxE81hslsyD2AtoXW/wVob10HkOJ1X/pAlcI7D+2YoZKg5do8G/w6RYgA==", + "dev": true + }, + "@jridgewell/gen-mapping": { + "version": "0.1.1", + "resolved": "https://registry.npmjs.org/@jridgewell/gen-mapping/-/gen-mapping-0.1.1.tgz", + "integrity": "sha512-sQXCasFk+U8lWYEe66WxRDOE9PjVz4vSM51fTu3Hw+ClTpUSQb718772vH3pyS5pShp6lvQM7SxgIDXXXmOX7w==", + "dev": true, "requires": { - "@istanbuljs/nyc-config-typescript": "^1.0.2", - "@types/mocha": "^9.1.1", - "@types/node": "^17.0.26", - "@types/yargs": "^17.0.10", - "mocha": "^9.2.2", - "nyc": "^15.1.0", - "source-map-support": "^0.5.21", - "ts-node": "^10.7.0", - "typescript": "^4.6.3", - "yargs": "^17.4.1" + "@jridgewell/set-array": "^1.0.0", + "@jridgewell/sourcemap-codec": "^1.4.10" } }, - "@node-redis/time-series": { - "version": "file:packages/time-series", + "@jridgewell/resolve-uri": { + "version": "3.0.6", + "resolved": "https://registry.npmjs.org/@jridgewell/resolve-uri/-/resolve-uri-3.0.6.tgz", + "integrity": "sha512-R7xHtBSNm+9SyvpJkdQl+qrM3Hm2fea3Ef197M3mUug+v+yR+Rhfbs7PBtcBUVnIWJ4JcAdjvij+c8hXS9p5aw==", + "dev": true + }, + "@jridgewell/set-array": { + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/@jridgewell/set-array/-/set-array-1.1.0.tgz", + "integrity": "sha512-SfJxIxNVYLTsKwzB3MoOQ1yxf4w/E6MdkvTgrgAt1bfxjSrLUoHMKrDOykwN14q65waezZIdqDneUIPh4/sKxg==", + "dev": true + }, + "@jridgewell/sourcemap-codec": { + "version": "1.4.11", + "resolved": "https://registry.npmjs.org/@jridgewell/sourcemap-codec/-/sourcemap-codec-1.4.11.tgz", + "integrity": "sha512-Fg32GrJo61m+VqYSdRSjRXMjQ06j8YIYfcTqndLYVAaHmroZHLJZCydsWBOTDqXS2v+mjxohBWEMfg97GXmYQg==", + "dev": true + }, + "@jridgewell/trace-mapping": { + "version": "0.3.9", + "resolved": "https://registry.npmjs.org/@jridgewell/trace-mapping/-/trace-mapping-0.3.9.tgz", + "integrity": "sha512-3Belt6tdc8bPgAtbcmdtNJlirVoTmEb5e2gC94PnkwEW9jI6CAHUeoG85tjWP5WquqfavoMtMwiG4P926ZKKuQ==", + "dev": true, "requires": { - "@istanbuljs/nyc-config-typescript": "^1.0.2", - "@node-redis/test-utils": "*", - "@types/node": "^17.0.26", - "nyc": "^15.1.0", - "release-it": "^14.14.2", - "source-map-support": "^0.5.21", - "ts-node": "^10.7.0", - "typedoc": "^0.22.15", - "typescript": "^4.6.3" + "@jridgewell/resolve-uri": "^3.0.3", + "@jridgewell/sourcemap-codec": "^1.4.10" } }, "@nodelib/fs.scandir": { @@ -7574,10 +8613,159 @@ "@octokit/openapi-types": "^11.2.0" } }, + "@redis/bloom": { + "version": "file:packages/bloom", + "requires": { + "@istanbuljs/nyc-config-typescript": "^1.0.2", + "@redis/test-utils": "*", + "@types/node": "^17.0.31", + "nyc": "^15.1.0", + "release-it": "^15.0.0", + "source-map-support": "^0.5.21", + "ts-node": "^10.7.0", + "typedoc": "^0.22.15", + "typescript": "^4.6.4" + } + }, + "@redis/client": { + "version": "file:packages/client", + "requires": { + "@istanbuljs/nyc-config-typescript": "^1.0.2", + "@redis/test-utils": "*", + "@types/node": "^17.0.31", + "@types/sinon": "^10.0.11", + "@types/yallist": "^4.0.1", + "@typescript-eslint/eslint-plugin": "^5.21.0", + "@typescript-eslint/parser": "^5.21.0", + "cluster-key-slot": "1.1.0", + "eslint": "^8.14.0", + "generic-pool": "3.8.2", + "nyc": "^15.1.0", + "release-it": "^15.0.0", + "sinon": "^13.0.2", + "source-map-support": "^0.5.21", + "ts-node": "^10.7.0", + "typedoc": "^0.22.15", + "typescript": "^4.6.4", + "yallist": "4.0.0" + }, + "dependencies": { + "yallist": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/yallist/-/yallist-4.0.0.tgz", + "integrity": "sha512-3wdGidZyq5PB084XLES5TpOSRA3wjXAlIWMhum2kRcv/41Sn2emQ0dycQW4uZXLejwKvg6EsvbdlVL+FYEct7A==" + } + } + }, + "@redis/graph": { + "version": "file:packages/graph", + "requires": { + "@istanbuljs/nyc-config-typescript": "^1.0.2", + "@redis/test-utils": "*", + "@types/node": "^17.0.31", + "nyc": "^15.1.0", + "release-it": "^15.0.0", + "source-map-support": "^0.5.21", + "ts-node": "^10.7.0", + "typedoc": "^0.22.15", + "typescript": "^4.6.4" + } + }, + "@redis/json": { + "version": "file:packages/json", + "requires": { + "@istanbuljs/nyc-config-typescript": "^1.0.2", + "@redis/test-utils": "*", + "@types/node": "^17.0.31", + "nyc": "^15.1.0", + "release-it": "^15.0.0", + "source-map-support": "^0.5.21", + "ts-node": "^10.7.0", + "typedoc": "^0.22.15", + "typescript": "^4.6.4" + } + }, + "@redis/search": { + "version": "file:packages/search", + "requires": { + "@istanbuljs/nyc-config-typescript": "^1.0.2", + "@redis/test-utils": "*", + "@types/node": "^17.0.31", + "nyc": "^15.1.0", + "release-it": "^15.0.0", + "source-map-support": "^0.5.21", + "ts-node": "^10.7.0", + "typedoc": "^0.22.15", + "typescript": "^4.6.4" + } + }, + "@redis/test-utils": { + "version": "file:packages/test-utils", + "requires": { + "@istanbuljs/nyc-config-typescript": "^1.0.2", + "@types/mocha": "^9.1.1", + "@types/node": "^17.0.31", + "@types/yargs": "^17.0.10", + "mocha": "^10.0.0", + "nyc": "^15.1.0", + "source-map-support": "^0.5.21", + "ts-node": "^10.7.0", + "typescript": "^4.6.4", + "yargs": "^17.4.1" + }, + "dependencies": { + "cliui": { + "version": "7.0.4", + "resolved": "https://registry.npmjs.org/cliui/-/cliui-7.0.4.tgz", + "integrity": "sha512-OcRE68cOsVMXp1Yvonl/fzkQOyjLSu/8bhPDfQt0e0/Eb283TKP20Fs2MqoPsr9SwA595rRCA+QMzYc9nBP+JQ==", + "dev": true, + "requires": { + "string-width": "^4.2.0", + "strip-ansi": "^6.0.0", + "wrap-ansi": "^7.0.0" + } + }, + "y18n": { + "version": "5.0.8", + "resolved": "https://registry.npmjs.org/y18n/-/y18n-5.0.8.tgz", + "integrity": "sha512-0pfFzegeDWJHJIAmTLRP2DwHjdF5s7jo9tuztdQxAhINCdvS+3nGINqPd00AphqJR/0LhANUS6/+7SCb98YOfA==", + "dev": true + }, + "yargs": { + "version": "17.4.1", + "resolved": "https://registry.npmjs.org/yargs/-/yargs-17.4.1.tgz", + "integrity": "sha512-WSZD9jgobAg3ZKuCQZSa3g9QOJeCCqLoLAykiWgmXnDo9EPnn4RPf5qVTtzgOx66o6/oqhcA5tHtJXpG8pMt3g==", + "dev": true, + "requires": { + "cliui": "^7.0.2", + "escalade": "^3.1.1", + "get-caller-file": "^2.0.5", + "require-directory": "^2.1.1", + "string-width": "^4.2.3", + "y18n": "^5.0.5", + "yargs-parser": "^21.0.0" + } + } + } + }, + "@redis/time-series": { + "version": "file:packages/time-series", + "requires": { + "@istanbuljs/nyc-config-typescript": "^1.0.2", + "@redis/test-utils": "*", + "@types/node": "^17.0.31", + "nyc": "^15.1.0", + "release-it": "^15.0.0", + "source-map-support": "^0.5.21", + "ts-node": "^10.7.0", + "typedoc": "^0.22.15", + "typescript": "^4.6.4" + } + }, "@sindresorhus/is": { - "version": "0.14.0", - "resolved": "https://registry.npmjs.org/@sindresorhus/is/-/is-0.14.0.tgz", - "integrity": "sha512-9NET910DNaIPngYnLLPeg+Ogzqsi9uM4mSboU5y6p8S5DzMTVEsJZrawi+BoDNUVBa2DhJqQYUFvMDfgU062LQ==", + "version": "4.6.0", + "resolved": "https://registry.npmjs.org/@sindresorhus/is/-/is-4.6.0.tgz", + "integrity": "sha512-t09vSN3MdfsyCHoFcTRCH/iUtG7OJ0CsjzB8cjAmKc/va/kIgeDI/TxsigdncE/4be734m0cvIYwNaV4i2XqAw==", "dev": true }, "@sinonjs/commons": { @@ -7616,14 +8804,20 @@ "dev": true }, "@szmarczak/http-timer": { - "version": "1.1.2", - "resolved": "https://registry.npmjs.org/@szmarczak/http-timer/-/http-timer-1.1.2.tgz", - "integrity": "sha512-XIB2XbzHTN6ieIjfIMV9hlVcfPU26s2vafYWQcZHWXHOxiaRZYEDKEwdl129Zyg50+foYV2jCgtrqSA6qNuNSA==", + "version": "5.0.1", + "resolved": "https://registry.npmjs.org/@szmarczak/http-timer/-/http-timer-5.0.1.tgz", + "integrity": "sha512-+PmQX0PiAYPMeVYe237LJAYvOMYW1j2rH5YROyS3b4CTVJum34HfRvKvAzozHAQG0TnHNdUfY9nCeUyRAs//cw==", "dev": true, "requires": { - "defer-to-connect": "^1.0.1" + "defer-to-connect": "^2.0.1" } }, + "@tootallnate/once": { + "version": "1.1.2", + "resolved": "https://registry.npmjs.org/@tootallnate/once/-/once-1.1.2.tgz", + "integrity": "sha512-RbzJvlNzmRq5c3O09UipeuXno4tA1FE6ikOjxZK0tuxVv3412l64l5t1W5pj4+rJq9vpkm/kwiR07aZXnsKPxw==", + "dev": true + }, "@tsconfig/node10": { "version": "1.0.8", "resolved": "https://registry.npmjs.org/@tsconfig/node10/-/node10-1.0.8.tgz", @@ -7648,12 +8842,45 @@ "integrity": "sha512-eZxlbI8GZscaGS7kkc/trHTT5xgrjH3/1n2JDwusC9iahPKWMRvRjJSAN5mCXviuTGQ/lHnhvv8Q1YTpnfz9gA==", "dev": true }, + "@types/cacheable-request": { + "version": "6.0.2", + "resolved": "https://registry.npmjs.org/@types/cacheable-request/-/cacheable-request-6.0.2.tgz", + "integrity": "sha512-B3xVo+dlKM6nnKTcmm5ZtY/OL8bOAOd2Olee9M1zft65ox50OzjEHW91sDiU9j6cvW8Ejg1/Qkf4xd2kugApUA==", + "dev": true, + "requires": { + "@types/http-cache-semantics": "*", + "@types/keyv": "*", + "@types/node": "*", + "@types/responselike": "*" + } + }, + "@types/http-cache-semantics": { + "version": "4.0.1", + "resolved": "https://registry.npmjs.org/@types/http-cache-semantics/-/http-cache-semantics-4.0.1.tgz", + "integrity": "sha512-SZs7ekbP8CN0txVG2xVRH6EgKmEm31BOxA07vkFaETzZz1xh+cbt8BcI0slpymvwhx5dlFnQG2rTlPVQn+iRPQ==", + "dev": true + }, + "@types/json-buffer": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/@types/json-buffer/-/json-buffer-3.0.0.tgz", + "integrity": "sha512-3YP80IxxFJB4b5tYC2SUPwkg0XQLiu0nWvhRgEatgjf+29IcWO9X1k8xRv5DGssJ/lCrjYTjQPcobJr2yWIVuQ==", + "dev": true + }, "@types/json-schema": { "version": "7.0.11", "resolved": "https://registry.npmjs.org/@types/json-schema/-/json-schema-7.0.11.tgz", "integrity": "sha512-wOuvG1SN4Us4rez+tylwwwCV1psiNVOkJeM3AUWUNWg/jDQY2+HE/444y5gc+jBmRqASOm2Oeh5c1axHobwRKQ==", "dev": true }, + "@types/keyv": { + "version": "3.1.4", + "resolved": "https://registry.npmjs.org/@types/keyv/-/keyv-3.1.4.tgz", + "integrity": "sha512-BQ5aZNSCpj7D6K2ksrRCTmKRLEpnPvWDiLPfoGyhZ++8YtiK9d/3DBKPJgry359X/P1PfruyYwvnvwFjuEiEIg==", + "dev": true, + "requires": { + "@types/node": "*" + } + }, "@types/mocha": { "version": "9.1.1", "resolved": "https://registry.npmjs.org/@types/mocha/-/mocha-9.1.1.tgz", @@ -7661,9 +8888,9 @@ "dev": true }, "@types/node": { - "version": "17.0.26", - "resolved": "https://registry.npmjs.org/@types/node/-/node-17.0.26.tgz", - "integrity": "sha512-z/FG/6DUO7pnze3AE3TBGIjGGKkvCcGcWINe1C7cADY8hKLJPDYpzsNE37uExQ4md5RFtTCvg+M8Mu1Enyeg2A==", + "version": "17.0.31", + "resolved": "https://registry.npmjs.org/@types/node/-/node-17.0.31.tgz", + "integrity": "sha512-AR0x5HbXGqkEx9CadRH3EBYx/VkiUgZIhP4wvPn/+5KIsgpNoyFaRlVe0Zlx9gRtg8fA06a9tskE2MSN7TcG4Q==", "dev": true }, "@types/parse-json": { @@ -7672,6 +8899,15 @@ "integrity": "sha512-//oorEZjL6sbPcKUaCdIGlIUeH26mgzimjBB77G6XRgnDl/L5wOnpyBGRe/Mmf5CVW3PwEBE1NjiMZ/ssFh4wA==", "dev": true }, + "@types/responselike": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/@types/responselike/-/responselike-1.0.0.tgz", + "integrity": "sha512-85Y2BjiufFzaMIlvJDvTTB8Fxl2xfLo4HgmHzVBz08w4wDePCTjYw66PdrolO0kzli3yam/YCgRufyo1DdQVTA==", + "dev": true, + "requires": { + "@types/node": "*" + } + }, "@types/sinon": { "version": "10.0.11", "resolved": "https://registry.npmjs.org/@types/sinon/-/sinon-10.0.11.tgz", @@ -7709,14 +8945,14 @@ "dev": true }, "@typescript-eslint/eslint-plugin": { - "version": "5.20.0", - "resolved": "https://registry.npmjs.org/@typescript-eslint/eslint-plugin/-/eslint-plugin-5.20.0.tgz", - "integrity": "sha512-fapGzoxilCn3sBtC6NtXZX6+P/Hef7VDbyfGqTTpzYydwhlkevB+0vE0EnmHPVTVSy68GUncyJ/2PcrFBeCo5Q==", + "version": "5.21.0", + "resolved": "https://registry.npmjs.org/@typescript-eslint/eslint-plugin/-/eslint-plugin-5.21.0.tgz", + "integrity": "sha512-fTU85q8v5ZLpoZEyn/u1S2qrFOhi33Edo2CZ0+q1gDaWWm0JuPh3bgOyU8lM0edIEYgKLDkPFiZX2MOupgjlyg==", "dev": true, "requires": { - "@typescript-eslint/scope-manager": "5.20.0", - "@typescript-eslint/type-utils": "5.20.0", - "@typescript-eslint/utils": "5.20.0", + "@typescript-eslint/scope-manager": "5.21.0", + "@typescript-eslint/type-utils": "5.21.0", + "@typescript-eslint/utils": "5.21.0", "debug": "^4.3.2", "functional-red-black-tree": "^1.0.1", "ignore": "^5.1.8", @@ -7725,6 +8961,15 @@ "tsutils": "^3.21.0" }, "dependencies": { + "lru-cache": { + "version": "6.0.0", + "resolved": "https://registry.npmjs.org/lru-cache/-/lru-cache-6.0.0.tgz", + "integrity": "sha512-Jo6dJ04CmSjuznwJSS3pUeWmd/H0ffTlkXXgwZi+eq1UCmqQwCh+eLsYOYCwY991i2Fah4h1BEMCx4qThGbsiA==", + "dev": true, + "requires": { + "yallist": "^4.0.0" + } + }, "semver": { "version": "7.3.7", "resolved": "https://registry.npmjs.org/semver/-/semver-7.3.7.tgz", @@ -7733,56 +8978,62 @@ "requires": { "lru-cache": "^6.0.0" } + }, + "yallist": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/yallist/-/yallist-4.0.0.tgz", + "integrity": "sha512-3wdGidZyq5PB084XLES5TpOSRA3wjXAlIWMhum2kRcv/41Sn2emQ0dycQW4uZXLejwKvg6EsvbdlVL+FYEct7A==", + "dev": true } } }, "@typescript-eslint/parser": { - "version": "5.20.0", - "resolved": "https://registry.npmjs.org/@typescript-eslint/parser/-/parser-5.20.0.tgz", - "integrity": "sha512-UWKibrCZQCYvobmu3/N8TWbEeo/EPQbS41Ux1F9XqPzGuV7pfg6n50ZrFo6hryynD8qOTTfLHtHjjdQtxJ0h/w==", + "version": "5.21.0", + "resolved": "https://registry.npmjs.org/@typescript-eslint/parser/-/parser-5.21.0.tgz", + "integrity": "sha512-8RUwTO77hstXUr3pZoWZbRQUxXcSXafZ8/5gpnQCfXvgmP9gpNlRGlWzvfbEQ14TLjmtU8eGnONkff8U2ui2Eg==", "dev": true, "requires": { - "@typescript-eslint/scope-manager": "5.20.0", - "@typescript-eslint/types": "5.20.0", - "@typescript-eslint/typescript-estree": "5.20.0", + "@typescript-eslint/scope-manager": "5.21.0", + "@typescript-eslint/types": "5.21.0", + "@typescript-eslint/typescript-estree": "5.21.0", "debug": "^4.3.2" } }, "@typescript-eslint/scope-manager": { - "version": "5.20.0", - "resolved": "https://registry.npmjs.org/@typescript-eslint/scope-manager/-/scope-manager-5.20.0.tgz", - "integrity": "sha512-h9KtuPZ4D/JuX7rpp1iKg3zOH0WNEa+ZIXwpW/KWmEFDxlA/HSfCMhiyF1HS/drTICjIbpA6OqkAhrP/zkCStg==", + "version": "5.21.0", + "resolved": "https://registry.npmjs.org/@typescript-eslint/scope-manager/-/scope-manager-5.21.0.tgz", + "integrity": "sha512-XTX0g0IhvzcH/e3393SvjRCfYQxgxtYzL3UREteUneo72EFlt7UNoiYnikUtmGVobTbhUDByhJ4xRBNe+34kOQ==", "dev": true, "requires": { - "@typescript-eslint/types": "5.20.0", - "@typescript-eslint/visitor-keys": "5.20.0" + "@typescript-eslint/types": "5.21.0", + "@typescript-eslint/visitor-keys": "5.21.0" } }, "@typescript-eslint/type-utils": { - "version": "5.20.0", - "resolved": "https://registry.npmjs.org/@typescript-eslint/type-utils/-/type-utils-5.20.0.tgz", - "integrity": "sha512-WxNrCwYB3N/m8ceyoGCgbLmuZwupvzN0rE8NBuwnl7APgjv24ZJIjkNzoFBXPRCGzLNkoU/WfanW0exvp/+3Iw==", + "version": "5.21.0", + "resolved": "https://registry.npmjs.org/@typescript-eslint/type-utils/-/type-utils-5.21.0.tgz", + "integrity": "sha512-MxmLZj0tkGlkcZCSE17ORaHl8Th3JQwBzyXL/uvC6sNmu128LsgjTX0NIzy+wdH2J7Pd02GN8FaoudJntFvSOw==", "dev": true, "requires": { - "@typescript-eslint/utils": "5.20.0", + "@typescript-eslint/utils": "5.21.0", "debug": "^4.3.2", "tsutils": "^3.21.0" } }, "@typescript-eslint/types": { - "version": "5.20.0", - "resolved": "https://registry.npmjs.org/@typescript-eslint/types/-/types-5.20.0.tgz", - "integrity": "sha512-+d8wprF9GyvPwtoB4CxBAR/s0rpP25XKgnOvMf/gMXYDvlUC3rPFHupdTQ/ow9vn7UDe5rX02ovGYQbv/IUCbg==", + "version": "5.21.0", + "resolved": "https://registry.npmjs.org/@typescript-eslint/types/-/types-5.21.0.tgz", + "integrity": "sha512-XnOOo5Wc2cBlq8Lh5WNvAgHzpjnEzxn4CJBwGkcau7b/tZ556qrWXQz4DJyChYg8JZAD06kczrdgFPpEQZfDsA==", "dev": true }, "@typescript-eslint/typescript-estree": { - "version": "5.20.0", - "resolved": "https://registry.npmjs.org/@typescript-eslint/typescript-estree/-/typescript-estree-5.20.0.tgz", - "integrity": "sha512-36xLjP/+bXusLMrT9fMMYy1KJAGgHhlER2TqpUVDYUQg4w0q/NW/sg4UGAgVwAqb8V4zYg43KMUpM8vV2lve6w==", + "version": "5.21.0", + "resolved": "https://registry.npmjs.org/@typescript-eslint/typescript-estree/-/typescript-estree-5.21.0.tgz", + "integrity": "sha512-Y8Y2T2FNvm08qlcoSMoNchh9y2Uj3QmjtwNMdRQkcFG7Muz//wfJBGBxh8R7HAGQFpgYpdHqUpEoPQk+q9Kjfg==", "dev": true, "requires": { - "@typescript-eslint/types": "5.20.0", - "@typescript-eslint/visitor-keys": "5.20.0", + "@typescript-eslint/types": "5.21.0", + "@typescript-eslint/visitor-keys": "5.21.0", "debug": "^4.3.2", "globby": "^11.0.4", "is-glob": "^4.0.3", @@ -7810,6 +9061,15 @@ "slash": "^3.0.0" } }, + "lru-cache": { + "version": "6.0.0", + "resolved": "https://registry.npmjs.org/lru-cache/-/lru-cache-6.0.0.tgz", + "integrity": "sha512-Jo6dJ04CmSjuznwJSS3pUeWmd/H0ffTlkXXgwZi+eq1UCmqQwCh+eLsYOYCwY991i2Fah4h1BEMCx4qThGbsiA==", + "dev": true, + "requires": { + "yallist": "^4.0.0" + } + }, "semver": { "version": "7.3.7", "resolved": "https://registry.npmjs.org/semver/-/semver-7.3.7.tgz", @@ -7818,30 +9078,36 @@ "requires": { "lru-cache": "^6.0.0" } + }, + "yallist": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/yallist/-/yallist-4.0.0.tgz", + "integrity": "sha512-3wdGidZyq5PB084XLES5TpOSRA3wjXAlIWMhum2kRcv/41Sn2emQ0dycQW4uZXLejwKvg6EsvbdlVL+FYEct7A==", + "dev": true } } }, "@typescript-eslint/utils": { - "version": "5.20.0", - "resolved": "https://registry.npmjs.org/@typescript-eslint/utils/-/utils-5.20.0.tgz", - "integrity": "sha512-lHONGJL1LIO12Ujyx8L8xKbwWSkoUKFSO+0wDAqGXiudWB2EO7WEUT+YZLtVbmOmSllAjLb9tpoIPwpRe5Tn6w==", + "version": "5.21.0", + "resolved": "https://registry.npmjs.org/@typescript-eslint/utils/-/utils-5.21.0.tgz", + "integrity": "sha512-q/emogbND9wry7zxy7VYri+7ydawo2HDZhRZ5k6yggIvXa7PvBbAAZ4PFH/oZLem72ezC4Pr63rJvDK/sTlL8Q==", "dev": true, "requires": { "@types/json-schema": "^7.0.9", - "@typescript-eslint/scope-manager": "5.20.0", - "@typescript-eslint/types": "5.20.0", - "@typescript-eslint/typescript-estree": "5.20.0", + "@typescript-eslint/scope-manager": "5.21.0", + "@typescript-eslint/types": "5.21.0", + "@typescript-eslint/typescript-estree": "5.21.0", "eslint-scope": "^5.1.1", "eslint-utils": "^3.0.0" } }, "@typescript-eslint/visitor-keys": { - "version": "5.20.0", - "resolved": "https://registry.npmjs.org/@typescript-eslint/visitor-keys/-/visitor-keys-5.20.0.tgz", - "integrity": "sha512-1flRpNF+0CAQkMNlTJ6L/Z5jiODG/e5+7mk6XwtPOUS3UrTz3UOiAg9jG2VtKsWI6rZQfy4C6a232QNRZTRGlg==", + "version": "5.21.0", + "resolved": "https://registry.npmjs.org/@typescript-eslint/visitor-keys/-/visitor-keys-5.21.0.tgz", + "integrity": "sha512-SX8jNN+iHqAF0riZQMkm7e8+POXa/fXw5cxL+gjpyP+FI+JVNhii53EmQgDAfDcBpFekYSlO0fGytMQwRiMQCA==", "dev": true, "requires": { - "@typescript-eslint/types": "5.20.0", + "@typescript-eslint/types": "5.21.0", "eslint-visitor-keys": "^3.0.0" } }, @@ -7852,9 +9118,9 @@ "dev": true }, "acorn": { - "version": "8.7.0", - "resolved": "https://registry.npmjs.org/acorn/-/acorn-8.7.0.tgz", - "integrity": "sha512-V/LGr1APy+PXIwKebEWrkZPwoeoF+w1jiOBUmuxuiUIaOHtob8Qc9BTrYo7VuI5fR8tqsy+buA2WFooR5olqvQ==", + "version": "8.7.1", + "resolved": "https://registry.npmjs.org/acorn/-/acorn-8.7.1.tgz", + "integrity": "sha512-Xx54uLJQZ19lKygFXOWsscKUbsBZW0CPykPhVQdhIeIwrbPmJzqeASDInc8nKBnp/JT6igTs82qPXz069H8I/A==", "dev": true }, "acorn-jsx": { @@ -7870,6 +9136,15 @@ "integrity": "sha512-k+iyHEuPgSw6SbuDpGQM+06HQUa04DZ3o+F6CSzXMvvI5KMvnaEqXe+YVe555R9nn6GPt404fos4wcgpw12SDA==", "dev": true }, + "agent-base": { + "version": "6.0.2", + "resolved": "https://registry.npmjs.org/agent-base/-/agent-base-6.0.2.tgz", + "integrity": "sha512-RZNwNclF7+MS/8bDg70amg32dyeZGZxiDuQmZxKLAlQjr3jGyLx+4Kkk58UO7D2QdgFIQCovuSuZESne6RG6XQ==", + "dev": true, + "requires": { + "debug": "4" + } + }, "aggregate-error": { "version": "3.1.0", "resolved": "https://registry.npmjs.org/aggregate-error/-/aggregate-error-3.1.0.tgz", @@ -7996,6 +9271,15 @@ "is-string": "^1.0.7" } }, + "ast-types": { + "version": "0.13.4", + "resolved": "https://registry.npmjs.org/ast-types/-/ast-types-0.13.4.tgz", + "integrity": "sha512-x1FCFnFifvYDDzTaLII71vG5uvDwgtmDTEVWAxrgeiR8VjMONcCXJx7E+USjDtHlwFmt9MysbqgF9b9Vjr6w+w==", + "dev": true, + "requires": { + "tslib": "^2.0.1" + } + }, "async": { "version": "2.6.4", "resolved": "https://registry.npmjs.org/async/-/async-2.6.4.tgz", @@ -8045,12 +9329,12 @@ "dev": true }, "bl": { - "version": "4.1.0", - "resolved": "https://registry.npmjs.org/bl/-/bl-4.1.0.tgz", - "integrity": "sha512-1W07cM9gS6DcLperZfFSj+bWLtaPGSOHWhPiGzXmvVJbRLdG82sH/Kn8EtW1VqWVA54AKf2h5k5BbnIbwF3h6w==", + "version": "5.0.0", + "resolved": "https://registry.npmjs.org/bl/-/bl-5.0.0.tgz", + "integrity": "sha512-8vxFNZ0pflFfi0WXA3WQXlj6CaMEwsmh63I1CNp0q+wWv8sD0ARx1KovSQd0l2GkwrMIOyedq0EF1FxI+RCZLQ==", "dev": true, "requires": { - "buffer": "^5.5.0", + "buffer": "^6.0.3", "inherits": "^2.0.4", "readable-stream": "^3.4.0" } @@ -8071,6 +9355,22 @@ "wrap-ansi": "^7.0.0" }, "dependencies": { + "camelcase": { + "version": "6.3.0", + "resolved": "https://registry.npmjs.org/camelcase/-/camelcase-6.3.0.tgz", + "integrity": "sha512-Gmy6FhYlCY7uOElZUSbxo2UCDH8owEk996gkbrpsgGtrJLM3J7jGxl9Ic7Qwwj4ivOE5AWZWRMecDdF7hqGjFA==", + "dev": true + }, + "chalk": { + "version": "4.1.2", + "resolved": "https://registry.npmjs.org/chalk/-/chalk-4.1.2.tgz", + "integrity": "sha512-oKnbhFyRIXpUuez8iBMmyEa4nbj4IOQyuhc/wy9kY7/WVPcwIO9VA668Pu8RkO7+0G76SLROeyw9CpQ061i4mA==", + "dev": true, + "requires": { + "ansi-styles": "^4.1.0", + "supports-color": "^7.1.0" + } + }, "type-fest": { "version": "0.20.2", "resolved": "https://registry.npmjs.org/type-fest/-/type-fest-0.20.2.tgz", @@ -8118,13 +9418,13 @@ } }, "buffer": { - "version": "5.7.1", - "resolved": "https://registry.npmjs.org/buffer/-/buffer-5.7.1.tgz", - "integrity": "sha512-EHcyIPBQ4BSGlvjB16k5KgAJ27CIsHY/2JBmCRReo48y9rQ3MaUzWX3KVlBa4U7MyX02HdVj0K7C3WaB3ju7FQ==", + "version": "6.0.3", + "resolved": "https://registry.npmjs.org/buffer/-/buffer-6.0.3.tgz", + "integrity": "sha512-FTiCpNxtwiZZHEZbcbTIcZjERVICn9yq/pDFkTl95/AxzD1naBctN7YO68riM/gLSDY7sdrMby8hofADYuuqOA==", "dev": true, "requires": { "base64-js": "^1.3.1", - "ieee754": "^1.1.13" + "ieee754": "^1.2.1" } }, "buffer-from": { @@ -8133,19 +9433,31 @@ "integrity": "sha512-E+XQCRwSbaaiChtv6k6Dwgc+bx+Bs6vuKJHHl5kox/BaKbhiXzqQOwK4cO22yElGp2OCmjwVhT3HmxgyPGnJfQ==", "dev": true }, + "bytes": { + "version": "3.1.2", + "resolved": "https://registry.npmjs.org/bytes/-/bytes-3.1.2.tgz", + "integrity": "sha512-/Nf7TyzTx6S3yRJObOAV7956r8cr2+Oj8AC5dt8wSP3BQAoeX58NoHyCU8P8zGkNXStjTSi6fzO6F0pBdcYbEg==", + "dev": true + }, + "cacheable-lookup": { + "version": "6.0.4", + "resolved": "https://registry.npmjs.org/cacheable-lookup/-/cacheable-lookup-6.0.4.tgz", + "integrity": "sha512-mbcDEZCkv2CZF4G01kr8eBd/5agkt9oCqz75tJMSIsquvRZ2sL6Hi5zGVKi/0OSC9oO1GHfJ2AV0ZIOY9vye0A==", + "dev": true + }, "cacheable-request": { - "version": "6.1.0", - "resolved": "https://registry.npmjs.org/cacheable-request/-/cacheable-request-6.1.0.tgz", - "integrity": "sha512-Oj3cAGPCqOZX7Rz64Uny2GYAZNliQSqfbePrgAQ1wKAihYmCUnraBtJtKcGR4xz7wF+LoJC+ssFZvv5BgF9Igg==", + "version": "7.0.2", + "resolved": "https://registry.npmjs.org/cacheable-request/-/cacheable-request-7.0.2.tgz", + "integrity": "sha512-pouW8/FmiPQbuGpkXQ9BAPv/Mo5xDGANgSNXzTzJ8DrKGuXOssM4wIQRjfanNRh3Yu5cfYPvcorqbhg2KIJtew==", "dev": true, "requires": { "clone-response": "^1.0.2", "get-stream": "^5.1.0", "http-cache-semantics": "^4.0.0", - "keyv": "^3.0.0", + "keyv": "^4.0.0", "lowercase-keys": "^2.0.0", - "normalize-url": "^4.1.0", - "responselike": "^1.0.2" + "normalize-url": "^6.0.1", + "responselike": "^2.0.0" }, "dependencies": { "get-stream": { @@ -8194,26 +9506,22 @@ "dev": true }, "camelcase": { - "version": "6.3.0", - "resolved": "https://registry.npmjs.org/camelcase/-/camelcase-6.3.0.tgz", - "integrity": "sha512-Gmy6FhYlCY7uOElZUSbxo2UCDH8owEk996gkbrpsgGtrJLM3J7jGxl9Ic7Qwwj4ivOE5AWZWRMecDdF7hqGjFA==", + "version": "5.3.1", + "resolved": "https://registry.npmjs.org/camelcase/-/camelcase-5.3.1.tgz", + "integrity": "sha512-L28STB170nwWS63UjtlEOE3dldQApaJXZkOI1uMFfzf3rRuPegHaHesyee+YxQ+W6SvRDQV6UrdOdRiR153wJg==", "dev": true }, "caniuse-lite": { - "version": "1.0.30001332", - "resolved": "https://registry.npmjs.org/caniuse-lite/-/caniuse-lite-1.0.30001332.tgz", - "integrity": "sha512-10T30NYOEQtN6C11YGg411yebhvpnC6Z102+B95eAsN0oB6KUs01ivE8u+G6FMIRtIrVlYXhL+LUwQ3/hXwDWw==", + "version": "1.0.30001335", + "resolved": "https://registry.npmjs.org/caniuse-lite/-/caniuse-lite-1.0.30001335.tgz", + "integrity": "sha512-ddP1Tgm7z2iIxu6QTtbZUv6HJxSaV/PZeSrWFZtbY4JZ69tOeNhBCl3HyRQgeNZKE5AOn1kpV7fhljigy0Ty3w==", "dev": true }, - "chalk": { - "version": "4.1.2", - "resolved": "https://registry.npmjs.org/chalk/-/chalk-4.1.2.tgz", - "integrity": "sha512-oKnbhFyRIXpUuez8iBMmyEa4nbj4IOQyuhc/wy9kY7/WVPcwIO9VA668Pu8RkO7+0G76SLROeyw9CpQ061i4mA==", - "dev": true, - "requires": { - "ansi-styles": "^4.1.0", - "supports-color": "^7.1.0" - } + "chalk": { + "version": "5.0.1", + "resolved": "https://registry.npmjs.org/chalk/-/chalk-5.0.1.tgz", + "integrity": "sha512-Fo07WOYGqMfCWHOzSXOt2CxDbC6skS/jO9ynEcmpANMoPrD+W1r1K6Vx7iNm+AQmETU1Xr2t+n8nzkV9t6xh3w==", + "dev": true }, "chardet": { "version": "0.7.0", @@ -8235,6 +9543,17 @@ "is-glob": "~4.0.1", "normalize-path": "~3.0.0", "readdirp": "~3.6.0" + }, + "dependencies": { + "glob-parent": { + "version": "5.1.2", + "resolved": "https://registry.npmjs.org/glob-parent/-/glob-parent-5.1.2.tgz", + "integrity": "sha512-AOIgSQCepiJYwP3ARnGx+5VnTu2HBYdzbGP45eLw1vr3zB3vZLeyed1sC9hnbcOc9/SrMyM5RPQrkGz4aS9Zow==", + "dev": true, + "requires": { + "is-glob": "^4.0.1" + } + } } }, "ci-info": { @@ -8277,14 +9596,27 @@ "dev": true }, "cliui": { - "version": "7.0.4", - "resolved": "https://registry.npmjs.org/cliui/-/cliui-7.0.4.tgz", - "integrity": "sha512-OcRE68cOsVMXp1Yvonl/fzkQOyjLSu/8bhPDfQt0e0/Eb283TKP20Fs2MqoPsr9SwA595rRCA+QMzYc9nBP+JQ==", + "version": "6.0.0", + "resolved": "https://registry.npmjs.org/cliui/-/cliui-6.0.0.tgz", + "integrity": "sha512-t6wbgtoCXvAzst7QgXxJYqPt0usEfbgQdftEPbLL/cvv6HPE5VgvqCuAIDR0NgU52ds6rFwqrgakNLrHEjCbrQ==", "dev": true, "requires": { "string-width": "^4.2.0", "strip-ansi": "^6.0.0", - "wrap-ansi": "^7.0.0" + "wrap-ansi": "^6.2.0" + }, + "dependencies": { + "wrap-ansi": { + "version": "6.2.0", + "resolved": "https://registry.npmjs.org/wrap-ansi/-/wrap-ansi-6.2.0.tgz", + "integrity": "sha512-r6lPcBGxZXlIcymEu7InxDMhdW0KDxpLgoFLcguasxCaJ/SOIZwINatK9KY/tf+ZrlywOKU0UDj3ATXUBfxJXA==", + "dev": true, + "requires": { + "ansi-styles": "^4.0.0", + "string-width": "^4.1.0", + "strip-ansi": "^6.0.0" + } + } } }, "clone": { @@ -8343,6 +9675,16 @@ "integrity": "sha1-3dgA2gxmEnOTzKWVDqloo6rxJTs=", "dev": true }, + "compress-brotli": { + "version": "1.3.6", + "resolved": "https://registry.npmjs.org/compress-brotli/-/compress-brotli-1.3.6.tgz", + "integrity": "sha512-au99/GqZtUtiCBliqLFbWlhnCxn+XSYjwZ77q6mKN4La4qOXDoLVPZ50iXr0WmAyMxl8yqoq3Yq4OeQNPPkyeQ==", + "dev": true, + "requires": { + "@types/json-buffer": "~3.0.0", + "json-buffer": "~3.0.1" + } + }, "concat-map": { "version": "0.0.1", "resolved": "https://registry.npmjs.org/concat-map/-/concat-map-0.0.1.tgz", @@ -8370,16 +9712,14 @@ "dev": true, "requires": { "safe-buffer": "~5.1.1" - }, - "dependencies": { - "safe-buffer": { - "version": "5.1.2", - "resolved": "https://registry.npmjs.org/safe-buffer/-/safe-buffer-5.1.2.tgz", - "integrity": "sha512-Gd2UZBJDkXlY7GbJxfsE8/nvKkUEU1G38c1siN6QP6a9PT9MmHB8GnpscSmMJSoF8LOIrt8ud/wPtojys4G6+g==", - "dev": true - } } }, + "core-util-is": { + "version": "1.0.3", + "resolved": "https://registry.npmjs.org/core-util-is/-/core-util-is-1.0.3.tgz", + "integrity": "sha512-ZQBvi1DcpJ4GDqanjucZ2Hj3wEO5pZDS89BWbkcrvdxksJorwUDDZamX9ldFkp9aw2lmBDLgkObEA4DWNJ9FYQ==", + "dev": true + }, "cosmiconfig": { "version": "7.0.1", "resolved": "https://registry.npmjs.org/cosmiconfig/-/cosmiconfig-7.0.1.tgz", @@ -8416,6 +9756,12 @@ "integrity": "sha512-v1plID3y9r/lPhviJ1wrXpLeyUIGAZ2SHNYTEapm7/8A9nLPoyvVp3RK/EPFqn5kEznyWgYZNsRtYYIWbuG8KA==", "dev": true }, + "data-uri-to-buffer": { + "version": "3.0.1", + "resolved": "https://registry.npmjs.org/data-uri-to-buffer/-/data-uri-to-buffer-3.0.1.tgz", + "integrity": "sha512-WboRycPNsVw3B3TL559F7kuBUM4d8CgMEvk6xEJlOp7OBPjt6G7z8WMWlD2rOFZLk6OYfFIUGsCOWzcQH9K2og==", + "dev": true + }, "debug": { "version": "4.3.4", "resolved": "https://registry.npmjs.org/debug/-/debug-4.3.4.tgz", @@ -8438,12 +9784,20 @@ "dev": true }, "decompress-response": { - "version": "3.3.0", - "resolved": "https://registry.npmjs.org/decompress-response/-/decompress-response-3.3.0.tgz", - "integrity": "sha1-gKTdMjdIOEv6JICDYirt7Jgq3/M=", + "version": "6.0.0", + "resolved": "https://registry.npmjs.org/decompress-response/-/decompress-response-6.0.0.tgz", + "integrity": "sha512-aW35yZM6Bb/4oJlZncMH2LCoZtJXTRxES17vE3hoRiowU2kWHaJKFkSBDnDR+cm9J+9QhXmREyIfv0pji9ejCQ==", "dev": true, "requires": { - "mimic-response": "^1.0.0" + "mimic-response": "^3.1.0" + }, + "dependencies": { + "mimic-response": { + "version": "3.1.0", + "resolved": "https://registry.npmjs.org/mimic-response/-/mimic-response-3.1.0.tgz", + "integrity": "sha512-z0yWI+4FDrrweS8Zmt4Ej5HdJmky15+L2e6Wgn3+iK5fWzb6T3fhNFq2+MeTRb064c6Wr4N/wv0DzQTjNzHNGQ==", + "dev": true + } } }, "deep-extend": { @@ -8477,9 +9831,15 @@ } }, "defer-to-connect": { - "version": "1.1.3", - "resolved": "https://registry.npmjs.org/defer-to-connect/-/defer-to-connect-1.1.3.tgz", - "integrity": "sha512-0ISdNousHvZT2EiFlZeZAHBUvSxmKswVCEf8hW7KWgG4a8MVEu/3Vb6uWYozkjylyCxe0JBIiRB1jV45S70WVQ==", + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/defer-to-connect/-/defer-to-connect-2.0.1.tgz", + "integrity": "sha512-4tvttepXG1VaYGrRibk5EwJd1t4udunSOVMdLSAL6mId1ix438oPwPZMALY41FCijukO1L0twNcGsdzS7dHgDg==", + "dev": true + }, + "define-lazy-prop": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/define-lazy-prop/-/define-lazy-prop-2.0.0.tgz", + "integrity": "sha512-Ds09qNh8yw3khSjiJjiUInaGX9xlqZDY7JVryGxdxV7NPeuqQfplOpQ66yJFZut3jLa5zOwkXw1g9EI2uKh4Og==", "dev": true }, "define-properties": { @@ -8492,12 +9852,30 @@ "object-keys": "^1.1.1" } }, + "degenerator": { + "version": "3.0.2", + "resolved": "https://registry.npmjs.org/degenerator/-/degenerator-3.0.2.tgz", + "integrity": "sha512-c0mef3SNQo56t6urUU6tdQAs+ThoD0o9B9MJ8HEt7NQcGEILCRFqQb7ZbP9JAv+QF1Ky5plydhMR/IrqWDm+TQ==", + "dev": true, + "requires": { + "ast-types": "^0.13.2", + "escodegen": "^1.8.1", + "esprima": "^4.0.0", + "vm2": "^3.9.8" + } + }, "delayed-stream": { "version": "1.0.0", "resolved": "https://registry.npmjs.org/delayed-stream/-/delayed-stream-1.0.0.tgz", "integrity": "sha1-3zrhmayt+31ECqrgsp4icrJOxhk=", "dev": true }, + "depd": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/depd/-/depd-2.0.0.tgz", + "integrity": "sha512-g7nH6P6dyDioJogAAGprGpCtVImJhpPk/roCzdb3fIh61/s/nPsfR6onyMwkCAR/OlC3yBC0lESvUoQEAssIrw==", + "dev": true + }, "deprecation": { "version": "2.3.1", "resolved": "https://registry.npmjs.org/deprecation/-/deprecation-2.3.1.tgz", @@ -8544,9 +9922,9 @@ "dev": true }, "electron-to-chromium": { - "version": "1.4.118", - "resolved": "https://registry.npmjs.org/electron-to-chromium/-/electron-to-chromium-1.4.118.tgz", - "integrity": "sha512-maZIKjnYDvF7Fs35nvVcyr44UcKNwybr93Oba2n3HkKDFAtk0svERkLN/HyczJDS3Fo4wU9th9fUQd09ZLtj1w==", + "version": "1.4.129", + "resolved": "https://registry.npmjs.org/electron-to-chromium/-/electron-to-chromium-1.4.129.tgz", + "integrity": "sha512-GgtN6bsDtHdtXJtlMYZWGB/uOyjZWjmRDumXTas7dGBaB9zUyCjzHet1DY2KhyHN8R0GLbzZWqm4efeddqqyRQ==", "dev": true }, "email-addresses": { @@ -8664,6 +10042,60 @@ "integrity": "sha1-G2HAViGQqN/2rjuyzwIAyhMLhtQ=", "dev": true }, + "escodegen": { + "version": "1.14.3", + "resolved": "https://registry.npmjs.org/escodegen/-/escodegen-1.14.3.tgz", + "integrity": "sha512-qFcX0XJkdg+PB3xjZZG/wKSuT1PnQWx57+TVSjIMmILd2yC/6ByYElPwJnslDsuWuSAp4AwJGumarAAmJch5Kw==", + "dev": true, + "requires": { + "esprima": "^4.0.1", + "estraverse": "^4.2.0", + "esutils": "^2.0.2", + "optionator": "^0.8.1", + "source-map": "~0.6.1" + }, + "dependencies": { + "levn": { + "version": "0.3.0", + "resolved": "https://registry.npmjs.org/levn/-/levn-0.3.0.tgz", + "integrity": "sha1-OwmSTt+fCDwEkP3UwLxEIeBHZO4=", + "dev": true, + "requires": { + "prelude-ls": "~1.1.2", + "type-check": "~0.3.2" + } + }, + "optionator": { + "version": "0.8.3", + "resolved": "https://registry.npmjs.org/optionator/-/optionator-0.8.3.tgz", + "integrity": "sha512-+IW9pACdk3XWmmTXG8m3upGUJst5XRGzxMRjXzAuJ1XnIFNvfhjjIuYkDvysnPQ7qzqVzLt78BCruntqRhWQbA==", + "dev": true, + "requires": { + "deep-is": "~0.1.3", + "fast-levenshtein": "~2.0.6", + "levn": "~0.3.0", + "prelude-ls": "~1.1.2", + "type-check": "~0.3.2", + "word-wrap": "~1.2.3" + } + }, + "prelude-ls": { + "version": "1.1.2", + "resolved": "https://registry.npmjs.org/prelude-ls/-/prelude-ls-1.1.2.tgz", + "integrity": "sha1-IZMqVJ9eUv/ZqCf1cOBL5iqX2lQ=", + "dev": true + }, + "type-check": { + "version": "0.3.2", + "resolved": "https://registry.npmjs.org/type-check/-/type-check-0.3.2.tgz", + "integrity": "sha1-WITKtRLPHTVeP7eE8wgEsrUg23I=", + "dev": true, + "requires": { + "prelude-ls": "~1.1.2" + } + } + } + }, "eslint": { "version": "8.14.0", "resolved": "https://registry.npmjs.org/eslint/-/eslint-8.14.0.tgz", @@ -8707,6 +10139,16 @@ "v8-compile-cache": "^2.0.3" }, "dependencies": { + "chalk": { + "version": "4.1.2", + "resolved": "https://registry.npmjs.org/chalk/-/chalk-4.1.2.tgz", + "integrity": "sha512-oKnbhFyRIXpUuez8iBMmyEa4nbj4IOQyuhc/wy9kY7/WVPcwIO9VA668Pu8RkO7+0G76SLROeyw9CpQ061i4mA==", + "dev": true, + "requires": { + "ansi-styles": "^4.1.0", + "supports-color": "^7.1.0" + } + }, "escape-string-regexp": { "version": "4.0.0", "resolved": "https://registry.npmjs.org/escape-string-regexp/-/escape-string-regexp-4.0.0.tgz", @@ -8728,21 +10170,6 @@ "resolved": "https://registry.npmjs.org/estraverse/-/estraverse-5.3.0.tgz", "integrity": "sha512-MMdARuVEQziNTeJD8DgMqmhwR11BRQ/cBP+pLtYdSTnf3MIO8fFeiINEbX36ZdNlfU/7A9f3gUw49B3oQsvwBA==", "dev": true - }, - "glob-parent": { - "version": "6.0.2", - "resolved": "https://registry.npmjs.org/glob-parent/-/glob-parent-6.0.2.tgz", - "integrity": "sha512-XxwI8EOhVQgWp6iDL+3b0r86f4d6AX6zSU55HfB4ydCEuXLXc5FcYeOu+nnGftS4TEju/11rt4KJPTMgbfmv4A==", - "dev": true, - "requires": { - "is-glob": "^4.0.3" - } - }, - "strip-json-comments": { - "version": "3.1.1", - "resolved": "https://registry.npmjs.org/strip-json-comments/-/strip-json-comments-3.1.1.tgz", - "integrity": "sha512-6fPc+R4ihwqP6N/aIv2f1gMH8lOVtWQHoqC4yK6oSDVVocumAsfCqjkXnqiYMhmMwS/mEHLp7Vehlt3ql6lEig==", - "dev": true } } }, @@ -8843,20 +10270,20 @@ "dev": true }, "execa": { - "version": "5.1.1", - "resolved": "https://registry.npmjs.org/execa/-/execa-5.1.1.tgz", - "integrity": "sha512-8uSpZZocAZRBAPIEINJj3Lo9HyGitllczc27Eh5YYojjMFMn8yHMDMaUHE2Jqfq05D/wucwI4JGURyXt1vchyg==", + "version": "6.1.0", + "resolved": "https://registry.npmjs.org/execa/-/execa-6.1.0.tgz", + "integrity": "sha512-QVWlX2e50heYJcCPG0iWtf8r0xjEYfz/OYLGDYH+IyjWezzPNxz63qNFOu0l4YftGWuizFVZHHs8PrLU5p2IDA==", "dev": true, "requires": { "cross-spawn": "^7.0.3", - "get-stream": "^6.0.0", - "human-signals": "^2.1.0", - "is-stream": "^2.0.0", + "get-stream": "^6.0.1", + "human-signals": "^3.0.1", + "is-stream": "^3.0.0", "merge-stream": "^2.0.0", - "npm-run-path": "^4.0.1", - "onetime": "^5.1.2", - "signal-exit": "^3.0.3", - "strip-final-newline": "^2.0.0" + "npm-run-path": "^5.1.0", + "onetime": "^6.0.0", + "signal-exit": "^3.0.7", + "strip-final-newline": "^3.0.0" } }, "external-editor": { @@ -8887,6 +10314,17 @@ "glob-parent": "^5.1.2", "merge2": "^1.3.0", "micromatch": "^4.0.4" + }, + "dependencies": { + "glob-parent": { + "version": "5.1.2", + "resolved": "https://registry.npmjs.org/glob-parent/-/glob-parent-5.1.2.tgz", + "integrity": "sha512-AOIgSQCepiJYwP3ARnGx+5VnTu2HBYdzbGP45eLw1vr3zB3vZLeyed1sC9hnbcOc9/SrMyM5RPQrkGz4aS9Zow==", + "dev": true, + "requires": { + "is-glob": "^4.0.1" + } + } } }, "fast-json-stable-stringify": { @@ -8928,6 +10366,12 @@ "flat-cache": "^3.0.4" } }, + "file-uri-to-path": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/file-uri-to-path/-/file-uri-to-path-2.0.0.tgz", + "integrity": "sha512-hjPFI8oE/2iQPVe4gbrJ73Pp+Xfub2+WI2LlXDbsaJBwT5wuMh35WNWVYYTpnz895shtwfyutMFLFywpQAFdLg==", + "dev": true + }, "filename-reserved-regex": { "version": "2.0.0", "resolved": "https://registry.npmjs.org/filename-reserved-regex/-/filename-reserved-regex-2.0.0.tgz", @@ -9024,6 +10468,12 @@ "mime-types": "^2.1.12" } }, + "form-data-encoder": { + "version": "1.7.1", + "resolved": "https://registry.npmjs.org/form-data-encoder/-/form-data-encoder-1.7.1.tgz", + "integrity": "sha512-EFRDrsMm/kyqbTQocNvRXMLjc7Es2Vk+IQFx/YW7hkUH1eBl4J1fqiP34l74Yt0pFLCNpc06fkbVk00008mzjg==", + "dev": true + }, "fromentries": { "version": "1.3.2", "resolved": "https://registry.npmjs.org/fromentries/-/fromentries-1.3.2.tgz", @@ -9047,12 +10497,41 @@ "integrity": "sha1-FQStJSMVjKpA20onh8sBQRmU6k8=", "dev": true }, - "fsevents": { - "version": "2.3.2", - "resolved": "https://registry.npmjs.org/fsevents/-/fsevents-2.3.2.tgz", - "integrity": "sha512-xiqMQR4xAeHTuB9uWm+fFRcIOgKBMiOBP+eXiyT7jsgVCq1bkVygt00oASowB7EdtpOHaaPgKt812P9ab+DDKA==", + "ftp": { + "version": "0.3.10", + "resolved": "https://registry.npmjs.org/ftp/-/ftp-0.3.10.tgz", + "integrity": "sha1-kZfYYa2BQvPmPVqDv+TFn3MwiF0=", "dev": true, - "optional": true + "requires": { + "readable-stream": "1.1.x", + "xregexp": "2.0.0" + }, + "dependencies": { + "isarray": { + "version": "0.0.1", + "resolved": "https://registry.npmjs.org/isarray/-/isarray-0.0.1.tgz", + "integrity": "sha1-ihis/Kmo9Bd+Cav8YDiTmwXR7t8=", + "dev": true + }, + "readable-stream": { + "version": "1.1.14", + "resolved": "https://registry.npmjs.org/readable-stream/-/readable-stream-1.1.14.tgz", + "integrity": "sha1-fPTFTvZI44EwhMY23SB54WbAgdk=", + "dev": true, + "requires": { + "core-util-is": "~1.0.0", + "inherits": "~2.0.1", + "isarray": "0.0.1", + "string_decoder": "~0.10.x" + } + }, + "string_decoder": { + "version": "0.10.31", + "resolved": "https://registry.npmjs.org/string_decoder/-/string_decoder-0.10.31.tgz", + "integrity": "sha1-YuIDvEF2bGwoyfyEMB2rHFMQ+pQ=", + "dev": true + } + } }, "function-bind": { "version": "1.1.1", @@ -9116,6 +10595,20 @@ "get-intrinsic": "^1.1.1" } }, + "get-uri": { + "version": "3.0.2", + "resolved": "https://registry.npmjs.org/get-uri/-/get-uri-3.0.2.tgz", + "integrity": "sha512-+5s0SJbGoyiJTZZ2JTpFPLMPSch72KEqGOTvQsBqg0RBWvwhWUSYZFAtz3TPW0GXJuLBJPts1E241iHg+VRfhg==", + "dev": true, + "requires": { + "@tootallnate/once": "1", + "data-uri-to-buffer": "3", + "debug": "4", + "file-uri-to-path": "2", + "fs-extra": "^8.1.0", + "ftp": "^0.3.10" + } + }, "gh-pages": { "version": "3.2.3", "resolved": "https://registry.npmjs.org/gh-pages/-/gh-pages-3.2.3.tgz", @@ -9165,12 +10658,12 @@ } }, "glob-parent": { - "version": "5.1.2", - "resolved": "https://registry.npmjs.org/glob-parent/-/glob-parent-5.1.2.tgz", - "integrity": "sha512-AOIgSQCepiJYwP3ARnGx+5VnTu2HBYdzbGP45eLw1vr3zB3vZLeyed1sC9hnbcOc9/SrMyM5RPQrkGz4aS9Zow==", + "version": "6.0.2", + "resolved": "https://registry.npmjs.org/glob-parent/-/glob-parent-6.0.2.tgz", + "integrity": "sha512-XxwI8EOhVQgWp6iDL+3b0r86f4d6AX6zSU55HfB4ydCEuXLXc5FcYeOu+nnGftS4TEju/11rt4KJPTMgbfmv4A==", "dev": true, "requires": { - "is-glob": "^4.0.1" + "is-glob": "^4.0.3" } }, "global-dirs": { @@ -9213,33 +10706,24 @@ } }, "got": { - "version": "9.6.0", - "resolved": "https://registry.npmjs.org/got/-/got-9.6.0.tgz", - "integrity": "sha512-R7eWptXuGYxwijs0eV+v3o6+XH1IqVK8dJOEecQfTmkncw9AV4dcw/Dhxi8MdlqPthxxpZyizMzyg8RTmEsG+Q==", - "dev": true, - "requires": { - "@sindresorhus/is": "^0.14.0", - "@szmarczak/http-timer": "^1.1.2", - "cacheable-request": "^6.0.0", - "decompress-response": "^3.3.0", - "duplexer3": "^0.1.4", - "get-stream": "^4.1.0", - "lowercase-keys": "^1.0.1", - "mimic-response": "^1.0.1", - "p-cancelable": "^1.0.0", - "to-readable-stream": "^1.0.0", - "url-parse-lax": "^3.0.0" - }, - "dependencies": { - "get-stream": { - "version": "4.1.0", - "resolved": "https://registry.npmjs.org/get-stream/-/get-stream-4.1.0.tgz", - "integrity": "sha512-GMat4EJ5161kIy2HevLlr4luNjBgvmj413KaQA7jt4V8B4RDsfpHk7WQ9GVqfYyyx8OS/L66Kox+rJRNklLK7w==", - "dev": true, - "requires": { - "pump": "^3.0.0" - } - } + "version": "12.0.4", + "resolved": "https://registry.npmjs.org/got/-/got-12.0.4.tgz", + "integrity": "sha512-2Eyz4iU/ktq7wtMFXxzK7g5p35uNYLLdiZarZ5/Yn3IJlNEpBd5+dCgcAyxN8/8guZLszffwe3wVyw+DEVrpBg==", + "dev": true, + "requires": { + "@sindresorhus/is": "^4.6.0", + "@szmarczak/http-timer": "^5.0.1", + "@types/cacheable-request": "^6.0.2", + "@types/responselike": "^1.0.0", + "cacheable-lookup": "^6.0.4", + "cacheable-request": "^7.0.2", + "decompress-response": "^6.0.0", + "form-data-encoder": "1.7.1", + "get-stream": "^6.0.1", + "http2-wrapper": "^2.1.10", + "lowercase-keys": "^3.0.0", + "p-cancelable": "^3.0.0", + "responselike": "^2.0.0" } }, "graceful-fs": { @@ -9248,12 +10732,6 @@ "integrity": "sha512-9ByhssR2fPVsNZj478qUUbKfmL0+t5BDVyjShtyZZLiK7ZDAArFFfopyOTj0M05wE2tJPisA4iTnnXl2YoPvOA==", "dev": true }, - "growl": { - "version": "1.10.5", - "resolved": "https://registry.npmjs.org/growl/-/growl-1.10.5.tgz", - "integrity": "sha512-qBr4OuELkhPenW6goKVXiv47US3clb3/IbuWF9KNKEijAy9oeHxU9IgzjvJhHkUzhaj7rOUD7+YGWqUjLp5oSA==", - "dev": true - }, "has": { "version": "1.0.3", "resolved": "https://registry.npmjs.org/has/-/has-1.0.3.tgz", @@ -9315,6 +10793,12 @@ "type-fest": "^0.8.0" }, "dependencies": { + "is-stream": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/is-stream/-/is-stream-2.0.1.tgz", + "integrity": "sha512-hFoiJiTl63nn+kstHGBtewWSKnQLpyb155KHheA1l39uvtO9nWIop1p3udqPcUd/xbF1VLMO4n7OI6p7RbngDg==", + "dev": true + }, "type-fest": { "version": "0.8.1", "resolved": "https://registry.npmjs.org/type-fest/-/type-fest-0.8.1.tgz", @@ -9341,10 +10825,54 @@ "integrity": "sha512-carPklcUh7ROWRK7Cv27RPtdhYhUsela/ue5/jKzjegVvXDqM2ILE9Q2BGn9JZJh1g87cp56su/FgQSzcWS8cQ==", "dev": true }, + "http-errors": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/http-errors/-/http-errors-2.0.0.tgz", + "integrity": "sha512-FtwrG/euBzaEjYeRqOgly7G0qviiXoJWnvEH2Z1plBdXgbyjv34pHTSb9zoeHMyDy33+DWy5Wt9Wo+TURtOYSQ==", + "dev": true, + "requires": { + "depd": "2.0.0", + "inherits": "2.0.4", + "setprototypeof": "1.2.0", + "statuses": "2.0.1", + "toidentifier": "1.0.1" + } + }, + "http-proxy-agent": { + "version": "4.0.1", + "resolved": "https://registry.npmjs.org/http-proxy-agent/-/http-proxy-agent-4.0.1.tgz", + "integrity": "sha512-k0zdNgqWTGA6aeIRVpvfVob4fL52dTfaehylg0Y4UvSySvOq/Y+BOyPrgpUrA7HylqvU8vIZGsRuXmspskV0Tg==", + "dev": true, + "requires": { + "@tootallnate/once": "1", + "agent-base": "6", + "debug": "4" + } + }, + "http2-wrapper": { + "version": "2.1.11", + "resolved": "https://registry.npmjs.org/http2-wrapper/-/http2-wrapper-2.1.11.tgz", + "integrity": "sha512-aNAk5JzLturWEUiuhAN73Jcbq96R7rTitAoXV54FYMatvihnpD2+6PUgU4ce3D/m5VDbw+F5CsyKSF176ptitQ==", + "dev": true, + "requires": { + "quick-lru": "^5.1.1", + "resolve-alpn": "^1.2.0" + } + }, + "https-proxy-agent": { + "version": "5.0.1", + "resolved": "https://registry.npmjs.org/https-proxy-agent/-/https-proxy-agent-5.0.1.tgz", + "integrity": "sha512-dFcAjpTQFgoLMzC2VwU+C/CbS7uRL0lWmxDITmqm7C+7F0Odmj6s9l6alZc6AELXhrnggM2CeWSXHGOdX2YtwA==", + "dev": true, + "requires": { + "agent-base": "6", + "debug": "4" + } + }, "human-signals": { - "version": "2.1.0", - "resolved": "https://registry.npmjs.org/human-signals/-/human-signals-2.1.0.tgz", - "integrity": "sha512-B4FFZ6q/T2jhhksgkbEW3HBvWIfDW85snkQgawt07S7J5QXTk6BkNV+0yAeZrM5QpMAdYlocGoljn0sJ/WQkFw==", + "version": "3.0.1", + "resolved": "https://registry.npmjs.org/human-signals/-/human-signals-3.0.1.tgz", + "integrity": "sha512-rQLskxnM/5OCldHo+wNXbpVgDn5A17CUoKX+7Sokwaknlq7CdSnphy0W39GU8dw59XiCXmFXDg4fRuckQRKewQ==", "dev": true }, "iconv-lite": { @@ -9368,15 +10896,6 @@ "integrity": "sha512-CmxgYGiEPCLhfLnpPp1MoRmifwEIOgjcHXxOBjv7mY96c+eWScsOP9c112ZyLdWHi0FxHjI+4uVhKYp/gcdRmQ==", "dev": true }, - "import-cwd": { - "version": "3.0.0", - "resolved": "https://registry.npmjs.org/import-cwd/-/import-cwd-3.0.0.tgz", - "integrity": "sha512-4pnzH16plW+hgvRECbDWpQl3cqtvSofHWh44met7ESfZ8UZOWWddm8hEyDTqREJ9RbYHY8gi8DqmaelApoOGMg==", - "dev": true, - "requires": { - "import-from": "^3.0.0" - } - }, "import-fresh": { "version": "3.3.0", "resolved": "https://registry.npmjs.org/import-fresh/-/import-fresh-3.3.0.tgz", @@ -9387,23 +10906,6 @@ "resolve-from": "^4.0.0" } }, - "import-from": { - "version": "3.0.0", - "resolved": "https://registry.npmjs.org/import-from/-/import-from-3.0.0.tgz", - "integrity": "sha512-CiuXOFFSzkU5x/CR0+z7T91Iht4CXgfCxVOFRhh2Zyhg5wOpWvvDLQUsWl+gcN+QscYBjez8hDCt85O7RLDttQ==", - "dev": true, - "requires": { - "resolve-from": "^5.0.0" - }, - "dependencies": { - "resolve-from": { - "version": "5.0.0", - "resolved": "https://registry.npmjs.org/resolve-from/-/resolve-from-5.0.0.tgz", - "integrity": "sha512-qYg9KP24dD5qka9J47d0aVky0N+b4fTU89LN9iDnjB5waksiC49rvMB0PrUJQGoTmH50XPiqOvAjDfaijGxYZw==", - "dev": true - } - } - }, "import-lazy": { "version": "2.1.0", "resolved": "https://registry.npmjs.org/import-lazy/-/import-lazy-2.1.0.tgz", @@ -9445,9 +10947,9 @@ "dev": true }, "inquirer": { - "version": "8.2.0", - "resolved": "https://registry.npmjs.org/inquirer/-/inquirer-8.2.0.tgz", - "integrity": "sha512-0crLweprevJ02tTuA6ThpoAERAGyVILC4sS74uib58Xf/zSr1/ZWtmm7D5CI+bSQEaA04f0K7idaHpQbSWgiVQ==", + "version": "8.2.4", + "resolved": "https://registry.npmjs.org/inquirer/-/inquirer-8.2.4.tgz", + "integrity": "sha512-nn4F01dxU8VeKfq192IjLsxu0/OmMZ4Lg3xKAns148rCaXP6ntAoEkVYZThWjwON8AlzdZZi6oqnhNbxUG9hVg==", "dev": true, "requires": { "ansi-escapes": "^4.2.1", @@ -9460,10 +10962,83 @@ "mute-stream": "0.0.8", "ora": "^5.4.1", "run-async": "^2.4.0", - "rxjs": "^7.2.0", + "rxjs": "^7.5.5", "string-width": "^4.1.0", "strip-ansi": "^6.0.0", - "through": "^2.3.6" + "through": "^2.3.6", + "wrap-ansi": "^7.0.0" + }, + "dependencies": { + "bl": { + "version": "4.1.0", + "resolved": "https://registry.npmjs.org/bl/-/bl-4.1.0.tgz", + "integrity": "sha512-1W07cM9gS6DcLperZfFSj+bWLtaPGSOHWhPiGzXmvVJbRLdG82sH/Kn8EtW1VqWVA54AKf2h5k5BbnIbwF3h6w==", + "dev": true, + "requires": { + "buffer": "^5.5.0", + "inherits": "^2.0.4", + "readable-stream": "^3.4.0" + } + }, + "buffer": { + "version": "5.7.1", + "resolved": "https://registry.npmjs.org/buffer/-/buffer-5.7.1.tgz", + "integrity": "sha512-EHcyIPBQ4BSGlvjB16k5KgAJ27CIsHY/2JBmCRReo48y9rQ3MaUzWX3KVlBa4U7MyX02HdVj0K7C3WaB3ju7FQ==", + "dev": true, + "requires": { + "base64-js": "^1.3.1", + "ieee754": "^1.1.13" + } + }, + "chalk": { + "version": "4.1.2", + "resolved": "https://registry.npmjs.org/chalk/-/chalk-4.1.2.tgz", + "integrity": "sha512-oKnbhFyRIXpUuez8iBMmyEa4nbj4IOQyuhc/wy9kY7/WVPcwIO9VA668Pu8RkO7+0G76SLROeyw9CpQ061i4mA==", + "dev": true, + "requires": { + "ansi-styles": "^4.1.0", + "supports-color": "^7.1.0" + } + }, + "is-interactive": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/is-interactive/-/is-interactive-1.0.0.tgz", + "integrity": "sha512-2HvIEKRoqS62guEC+qBjpvRubdX910WCMuJTZ+I9yvqKU2/12eSL549HMwtabb4oupdj2sMP50k+XJfB/8JE6w==", + "dev": true + }, + "is-unicode-supported": { + "version": "0.1.0", + "resolved": "https://registry.npmjs.org/is-unicode-supported/-/is-unicode-supported-0.1.0.tgz", + "integrity": "sha512-knxG2q4UC3u8stRGyAVJCOdxFmv5DZiRcdlIaAQXAbSfJya+OhopNotLQrstBhququ4ZpuKbDc/8S6mgXgPFPw==", + "dev": true + }, + "log-symbols": { + "version": "4.1.0", + "resolved": "https://registry.npmjs.org/log-symbols/-/log-symbols-4.1.0.tgz", + "integrity": "sha512-8XPvpAA8uyhfteu8pIvQxpJZ7SYYdpUivZpGy6sFsBuKRY/7rQGavedeB8aK+Zkyq6upMFVL/9AW6vOYzfRyLg==", + "dev": true, + "requires": { + "chalk": "^4.1.0", + "is-unicode-supported": "^0.1.0" + } + }, + "ora": { + "version": "5.4.1", + "resolved": "https://registry.npmjs.org/ora/-/ora-5.4.1.tgz", + "integrity": "sha512-5b6Y85tPxZZ7QytO+BQzysW31HJku27cRIlkbAXaNx+BdcVi+LlRFmVXzeF6a7JCwJpyw5c4b+YSVImQIrBpuQ==", + "dev": true, + "requires": { + "bl": "^4.1.0", + "chalk": "^4.1.0", + "cli-cursor": "^3.1.0", + "cli-spinners": "^2.5.0", + "is-interactive": "^1.0.0", + "is-unicode-supported": "^0.1.0", + "log-symbols": "^4.1.0", + "strip-ansi": "^6.0.0", + "wcwidth": "^1.0.1" + } + } } }, "internal-slot": { @@ -9483,6 +11058,12 @@ "integrity": "sha512-agE4QfB2Lkp9uICn7BAqoscw4SZP9kTE2hxiFI3jBPmXJfdqiahTbUuKGsMoN2GtqL9AxhYioAcVvgsb1HvRbA==", "dev": true }, + "ip": { + "version": "1.1.5", + "resolved": "https://registry.npmjs.org/ip/-/ip-1.1.5.tgz", + "integrity": "sha1-vd7XARQpCCjAoDnnLvJfWq7ENUo=", + "dev": true + }, "is-arguments": { "version": "1.1.1", "resolved": "https://registry.npmjs.org/is-arguments/-/is-arguments-1.1.1.tgz", @@ -9598,9 +11179,9 @@ } }, "is-interactive": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/is-interactive/-/is-interactive-1.0.0.tgz", - "integrity": "sha512-2HvIEKRoqS62guEC+qBjpvRubdX910WCMuJTZ+I9yvqKU2/12eSL549HMwtabb4oupdj2sMP50k+XJfB/8JE6w==", + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/is-interactive/-/is-interactive-2.0.0.tgz", + "integrity": "sha512-qP1vozQRI+BMOPcjFzrjXuQvdak2pHNUMZoeG2eRbiSqyvbEf/wQtEOTOX1guk6E3t36RkaqiSt8A/6YElNxLQ==", "dev": true }, "is-map": { @@ -9695,9 +11276,9 @@ } }, "is-stream": { - "version": "2.0.1", - "resolved": "https://registry.npmjs.org/is-stream/-/is-stream-2.0.1.tgz", - "integrity": "sha512-hFoiJiTl63nn+kstHGBtewWSKnQLpyb155KHheA1l39uvtO9nWIop1p3udqPcUd/xbF1VLMO4n7OI6p7RbngDg==", + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/is-stream/-/is-stream-3.0.0.tgz", + "integrity": "sha512-LnQR4bZ9IADDRSkvpqMGvt/tEJWclzklNgSw48V5EAaAeDd6qGvN8ei6k5p0tvxSR171VmGyHuTiAOfxAbr8kA==", "dev": true }, "is-string": { @@ -9725,9 +11306,9 @@ "dev": true }, "is-unicode-supported": { - "version": "0.1.0", - "resolved": "https://registry.npmjs.org/is-unicode-supported/-/is-unicode-supported-0.1.0.tgz", - "integrity": "sha512-knxG2q4UC3u8stRGyAVJCOdxFmv5DZiRcdlIaAQXAbSfJya+OhopNotLQrstBhququ4ZpuKbDc/8S6mgXgPFPw==", + "version": "1.2.0", + "resolved": "https://registry.npmjs.org/is-unicode-supported/-/is-unicode-supported-1.2.0.tgz", + "integrity": "sha512-wH+U77omcRzevfIG8dDhTS0V9zZyweakfD01FULl97+0EHiJTTZtJqxPSkIIo/SDPv/i07k/C9jAPY+jwLLeUQ==", "dev": true }, "is-weakref": { @@ -9812,14 +11393,6 @@ "p-map": "^3.0.0", "rimraf": "^3.0.0", "uuid": "^3.3.3" - }, - "dependencies": { - "uuid": { - "version": "3.4.0", - "resolved": "https://registry.npmjs.org/uuid/-/uuid-3.4.0.tgz", - "integrity": "sha512-HjSDRw6gZE5JMggctHBcjVak08+KEVhSIiDzFnT9S9aegmp85S/bReBVTb4QTFaRNptJ9kuYaNhnbNEOkbKb/A==", - "dev": true - } } }, "istanbul-lib-report": { @@ -9842,14 +11415,6 @@ "debug": "^4.1.1", "istanbul-lib-coverage": "^3.0.0", "source-map": "^0.6.1" - }, - "dependencies": { - "source-map": { - "version": "0.6.1", - "resolved": "https://registry.npmjs.org/source-map/-/source-map-0.6.1.tgz", - "integrity": "sha512-UjgapumWlbMhkBgzT7Ykc5YXUT46F0iKu8SGXq0bcwP5dz/h0Plj6enJqjz1Zbq2l5WaqYnrVbwWOWMyF3F47g==", - "dev": true - } } }, "istanbul-reports": { @@ -9900,9 +11465,9 @@ "dev": true }, "json-buffer": { - "version": "3.0.0", - "resolved": "https://registry.npmjs.org/json-buffer/-/json-buffer-3.0.0.tgz", - "integrity": "sha1-Wx85evx11ne96Lz8Dkfh+aPZqJg=", + "version": "3.0.1", + "resolved": "https://registry.npmjs.org/json-buffer/-/json-buffer-3.0.1.tgz", + "integrity": "sha512-4bV5BfR2mqfQTJm+V5tPPdf+ZpuhiIvTuAB5g8kcrXOZpTT/QwwVRWBywX1ozr6lEuPdbHxwaJlm9G6mI2sfSQ==", "dev": true }, "json-parse-even-better-errors": { @@ -9951,12 +11516,13 @@ "dev": true }, "keyv": { - "version": "3.1.0", - "resolved": "https://registry.npmjs.org/keyv/-/keyv-3.1.0.tgz", - "integrity": "sha512-9ykJ/46SN/9KPM/sichzQ7OvXyGDYKGTaDlKMGCAlg2UK8KRy4jb0d8sFc+0Tt0YYnThq8X2RZgCg74RPxgcVA==", + "version": "4.2.2", + "resolved": "https://registry.npmjs.org/keyv/-/keyv-4.2.2.tgz", + "integrity": "sha512-uYS0vKTlBIjNCAUqrjlxmruxOEiZxZIHXyp32sdcGmP+ukFrmWUnE//RcPXJH3Vxrni1H2gsQbjHE0bH7MtMQQ==", "dev": true, "requires": { - "json-buffer": "3.0.0" + "compress-brotli": "^1.3.6", + "json-buffer": "3.0.1" } }, "latest-version": { @@ -10018,28 +11584,28 @@ "dev": true }, "log-symbols": { - "version": "4.1.0", - "resolved": "https://registry.npmjs.org/log-symbols/-/log-symbols-4.1.0.tgz", - "integrity": "sha512-8XPvpAA8uyhfteu8pIvQxpJZ7SYYdpUivZpGy6sFsBuKRY/7rQGavedeB8aK+Zkyq6upMFVL/9AW6vOYzfRyLg==", + "version": "5.1.0", + "resolved": "https://registry.npmjs.org/log-symbols/-/log-symbols-5.1.0.tgz", + "integrity": "sha512-l0x2DvrW294C9uDCoQe1VSU4gf529FkSZ6leBl4TiqZH/e+0R7hSfHQBNut2mNygDgHwvYHfFLn6Oxb3VWj2rA==", "dev": true, "requires": { - "chalk": "^4.1.0", - "is-unicode-supported": "^0.1.0" + "chalk": "^5.0.0", + "is-unicode-supported": "^1.1.0" } }, "lowercase-keys": { - "version": "1.0.1", - "resolved": "https://registry.npmjs.org/lowercase-keys/-/lowercase-keys-1.0.1.tgz", - "integrity": "sha512-G2Lj61tXDnVFFOi8VZds+SoQjtQC3dgokKdDG2mTm1tx4m50NUHBOZSBwQQHyy0V12A0JTG4icfZQH+xPyh8VA==", + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/lowercase-keys/-/lowercase-keys-3.0.0.tgz", + "integrity": "sha512-ozCC6gdQ+glXOQsveKD0YsDy8DSQFjDTz4zyzEHNV5+JP5D62LmfDZ6o1cycFx9ouG940M5dE8C8CTewdj2YWQ==", "dev": true }, "lru-cache": { - "version": "6.0.0", - "resolved": "https://registry.npmjs.org/lru-cache/-/lru-cache-6.0.0.tgz", - "integrity": "sha512-Jo6dJ04CmSjuznwJSS3pUeWmd/H0ffTlkXXgwZi+eq1UCmqQwCh+eLsYOYCwY991i2Fah4h1BEMCx4qThGbsiA==", + "version": "5.1.1", + "resolved": "https://registry.npmjs.org/lru-cache/-/lru-cache-5.1.1.tgz", + "integrity": "sha512-KpNARQA3Iwv+jTA0utUVVbrh+Jlrr1Fv0e56GGzAFOXN7dk/FviaDW8LHmK52DlcH4WP2n6gI8vN1aesBFgo9w==", "dev": true, "requires": { - "yallist": "^4.0.0" + "yallist": "^3.0.2" } }, "lunr": { @@ -10049,9 +11615,9 @@ "dev": true }, "macos-release": { - "version": "2.5.0", - "resolved": "https://registry.npmjs.org/macos-release/-/macos-release-2.5.0.tgz", - "integrity": "sha512-EIgv+QZ9r+814gjJj0Bt5vSLJLzswGmSUbUpbi9AIr/fsN2IWFBl2NucV9PAiek+U1STK468tEkxmVYUtuAN3g==", + "version": "3.0.1", + "resolved": "https://registry.npmjs.org/macos-release/-/macos-release-3.0.1.tgz", + "integrity": "sha512-3l6OrhdDg2H2SigtuN3jBh+5dRJRWxNKuJTPBbGeNJTsmt/pj9PO25wYaNb05NuNmAsl435j4rDP6rgNXz7s7g==", "dev": true }, "make-dir": { @@ -10070,9 +11636,9 @@ "dev": true }, "marked": { - "version": "4.0.14", - "resolved": "https://registry.npmjs.org/marked/-/marked-4.0.14.tgz", - "integrity": "sha512-HL5sSPE/LP6U9qKgngIIPTthuxC0jrfxpYMZ3LdGDD3vTnLs59m2Z7r6+LNDR3ToqEQdkKd6YaaEfJhodJmijQ==", + "version": "4.0.15", + "resolved": "https://registry.npmjs.org/marked/-/marked-4.0.15.tgz", + "integrity": "sha512-esX5lPdTfG4p8LDkv+obbRCyOKzB+820ZZyMOXJZygZBHrH9b3xXR64X4kT3sPe9Nx8qQXbmcz6kFSMt4Nfk6Q==", "dev": true }, "merge-stream": { @@ -10113,9 +11679,9 @@ } }, "mimic-fn": { - "version": "2.1.0", - "resolved": "https://registry.npmjs.org/mimic-fn/-/mimic-fn-2.1.0.tgz", - "integrity": "sha512-OqbOk5oEQeAZ8WXWydlu9HJjz9WVdEIvamMCcXmuqUYjTknH/sqsWvhQ3vgwKFRR1HpjvNBKQ37nbJgYzGqGcg==", + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/mimic-fn/-/mimic-fn-4.0.0.tgz", + "integrity": "sha512-vqiC06CuhBTUdZH+RYl8sFrL096vA45Ok5ISO6sE/Mr1jRbGH4Csnhi8f3wKVl7x8mO4Au7Ir9D3Oyv1VYMFJw==", "dev": true }, "mimic-response": { @@ -10140,54 +11706,76 @@ "dev": true }, "mocha": { - "version": "9.2.2", - "resolved": "https://registry.npmjs.org/mocha/-/mocha-9.2.2.tgz", - "integrity": "sha512-L6XC3EdwT6YrIk0yXpavvLkn8h+EU+Y5UcCHKECyMbdUIxyMuZj4bX4U9e1nvnvUUvQVsV2VHQr5zLdcUkhW/g==", + "version": "10.0.0", + "resolved": "https://registry.npmjs.org/mocha/-/mocha-10.0.0.tgz", + "integrity": "sha512-0Wl+elVUD43Y0BqPZBzZt8Tnkw9CMUdNYnUsTfOM1vuhJVZL+kiesFYsqwBkEEuEixaiPe5ZQdqDgX2jddhmoA==", "dev": true, "requires": { "@ungap/promise-all-settled": "1.1.2", "ansi-colors": "4.1.1", "browser-stdout": "1.3.1", "chokidar": "3.5.3", - "debug": "4.3.3", + "debug": "4.3.4", "diff": "5.0.0", "escape-string-regexp": "4.0.0", "find-up": "5.0.0", "glob": "7.2.0", - "growl": "1.10.5", "he": "1.2.0", "js-yaml": "4.1.0", "log-symbols": "4.1.0", - "minimatch": "4.2.1", + "minimatch": "5.0.1", "ms": "2.1.3", - "nanoid": "3.3.1", + "nanoid": "3.3.3", "serialize-javascript": "6.0.0", "strip-json-comments": "3.1.1", "supports-color": "8.1.1", - "which": "2.0.2", - "workerpool": "6.2.0", + "workerpool": "6.2.1", "yargs": "16.2.0", "yargs-parser": "20.2.4", "yargs-unparser": "2.0.0" }, "dependencies": { - "debug": { - "version": "4.3.3", - "resolved": "https://registry.npmjs.org/debug/-/debug-4.3.3.tgz", - "integrity": "sha512-/zxw5+vh1Tfv+4Qn7a5nsbcJKPaSvCDhojn6FEl9vupwK2VCSDtEiEtqr8DFtzYFOdz63LBkxec7DYuc2jon6Q==", + "brace-expansion": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/brace-expansion/-/brace-expansion-2.0.1.tgz", + "integrity": "sha512-XnAIvQ8eM+kC6aULx6wuQiwVsnzsi9d3WxzV3FpWTGA19F621kwdbsAcFKXgKUHZWsy+mY6iL1sHTxWEFCytDA==", + "dev": true, + "requires": { + "balanced-match": "^1.0.0" + } + }, + "chalk": { + "version": "4.1.2", + "resolved": "https://registry.npmjs.org/chalk/-/chalk-4.1.2.tgz", + "integrity": "sha512-oKnbhFyRIXpUuez8iBMmyEa4nbj4IOQyuhc/wy9kY7/WVPcwIO9VA668Pu8RkO7+0G76SLROeyw9CpQ061i4mA==", "dev": true, "requires": { - "ms": "2.1.2" + "ansi-styles": "^4.1.0", + "supports-color": "^7.1.0" }, "dependencies": { - "ms": { - "version": "2.1.2", - "resolved": "https://registry.npmjs.org/ms/-/ms-2.1.2.tgz", - "integrity": "sha512-sGkPx+VjMtmA6MX27oA4FBFELFCZZ4S4XqeGOXCv68tT+jb3vk/RyaKWP0PTKyWtmLSM0b+adUTEvbs1PEaH2w==", - "dev": true + "supports-color": { + "version": "7.2.0", + "resolved": "https://registry.npmjs.org/supports-color/-/supports-color-7.2.0.tgz", + "integrity": "sha512-qpCAvRl9stuOHveKsn7HncJRvv501qIacKzQlO/+Lwxc9+0q2wLyv4Dfvt80/DPn2pqOBsJdDiogXGR9+OvwRw==", + "dev": true, + "requires": { + "has-flag": "^4.0.0" + } } } }, + "cliui": { + "version": "7.0.4", + "resolved": "https://registry.npmjs.org/cliui/-/cliui-7.0.4.tgz", + "integrity": "sha512-OcRE68cOsVMXp1Yvonl/fzkQOyjLSu/8bhPDfQt0e0/Eb283TKP20Fs2MqoPsr9SwA595rRCA+QMzYc9nBP+JQ==", + "dev": true, + "requires": { + "string-width": "^4.2.0", + "strip-ansi": "^6.0.0", + "wrap-ansi": "^7.0.0" + } + }, "escape-string-regexp": { "version": "4.0.0", "resolved": "https://registry.npmjs.org/escape-string-regexp/-/escape-string-regexp-4.0.0.tgz", @@ -10204,6 +11792,12 @@ "path-exists": "^4.0.0" } }, + "is-unicode-supported": { + "version": "0.1.0", + "resolved": "https://registry.npmjs.org/is-unicode-supported/-/is-unicode-supported-0.1.0.tgz", + "integrity": "sha512-knxG2q4UC3u8stRGyAVJCOdxFmv5DZiRcdlIaAQXAbSfJya+OhopNotLQrstBhququ4ZpuKbDc/8S6mgXgPFPw==", + "dev": true + }, "locate-path": { "version": "6.0.0", "resolved": "https://registry.npmjs.org/locate-path/-/locate-path-6.0.0.tgz", @@ -10213,13 +11807,23 @@ "p-locate": "^5.0.0" } }, + "log-symbols": { + "version": "4.1.0", + "resolved": "https://registry.npmjs.org/log-symbols/-/log-symbols-4.1.0.tgz", + "integrity": "sha512-8XPvpAA8uyhfteu8pIvQxpJZ7SYYdpUivZpGy6sFsBuKRY/7rQGavedeB8aK+Zkyq6upMFVL/9AW6vOYzfRyLg==", + "dev": true, + "requires": { + "chalk": "^4.1.0", + "is-unicode-supported": "^0.1.0" + } + }, "minimatch": { - "version": "4.2.1", - "resolved": "https://registry.npmjs.org/minimatch/-/minimatch-4.2.1.tgz", - "integrity": "sha512-9Uq1ChtSZO+Mxa/CL1eGizn2vRn3MlLgzhT0Iz8zaY8NdvxvB0d5QdPFmCKf7JKA9Lerx5vRrnwO03jsSfGG9g==", + "version": "5.0.1", + "resolved": "https://registry.npmjs.org/minimatch/-/minimatch-5.0.1.tgz", + "integrity": "sha512-nLDxIFRyhDblz3qMuq+SoRZED4+miJ/G+tdDrjkkkRnjAsBexeGpgjLEQ0blJy7rHhR2b93rhQY4SvyWu9v03g==", "dev": true, "requires": { - "brace-expansion": "^1.1.7" + "brace-expansion": "^2.0.1" } }, "ms": { @@ -10246,12 +11850,6 @@ "p-limit": "^3.0.2" } }, - "strip-json-comments": { - "version": "3.1.1", - "resolved": "https://registry.npmjs.org/strip-json-comments/-/strip-json-comments-3.1.1.tgz", - "integrity": "sha512-6fPc+R4ihwqP6N/aIv2f1gMH8lOVtWQHoqC4yK6oSDVVocumAsfCqjkXnqiYMhmMwS/mEHLp7Vehlt3ql6lEig==", - "dev": true - }, "supports-color": { "version": "8.1.1", "resolved": "https://registry.npmjs.org/supports-color/-/supports-color-8.1.1.tgz", @@ -10261,6 +11859,12 @@ "has-flag": "^4.0.0" } }, + "y18n": { + "version": "5.0.8", + "resolved": "https://registry.npmjs.org/y18n/-/y18n-5.0.8.tgz", + "integrity": "sha512-0pfFzegeDWJHJIAmTLRP2DwHjdF5s7jo9tuztdQxAhINCdvS+3nGINqPd00AphqJR/0LhANUS6/+7SCb98YOfA==", + "dev": true + }, "yargs": { "version": "16.2.0", "resolved": "https://registry.npmjs.org/yargs/-/yargs-16.2.0.tgz", @@ -10297,9 +11901,9 @@ "dev": true }, "nanoid": { - "version": "3.3.1", - "resolved": "https://registry.npmjs.org/nanoid/-/nanoid-3.3.1.tgz", - "integrity": "sha512-n6Vs/3KGyxPQd6uO0eH4Bv0ojGSUvuLlIHtC3Y0kEO23YRge8H9x1GCzLn28YX0H66pMkxuaeESFq4tKISKwdw==", + "version": "3.3.3", + "resolved": "https://registry.npmjs.org/nanoid/-/nanoid-3.3.3.tgz", + "integrity": "sha512-p1sjXuopFs0xg+fPASzQ28agW1oHD7xDsd9Xkf3T15H3c/cifrFHVwrh74PdoklAPi+i7MdRsE47vm2r6JoB+w==", "dev": true }, "natural-compare": { @@ -10308,19 +11912,25 @@ "integrity": "sha1-Sr6/7tdUHywnrPspvbvRXI1bpPc=", "dev": true }, + "netmask": { + "version": "2.0.2", + "resolved": "https://registry.npmjs.org/netmask/-/netmask-2.0.2.tgz", + "integrity": "sha512-dBpDMdxv9Irdq66304OLfEmQ9tbNRFnFTuZiLo+bD+r332bBmMJ8GBLXklIXXgxd3+v9+KUnZaUR5PJMa75Gsg==", + "dev": true + }, "new-github-release-url": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/new-github-release-url/-/new-github-release-url-1.0.0.tgz", - "integrity": "sha512-dle7yf655IMjyFUqn6Nxkb18r4AOAkzRcgcZv6WZ0IqrOH4QCEZ8Sm6I7XX21zvHdBeeMeTkhR9qT2Z0EJDx6A==", + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/new-github-release-url/-/new-github-release-url-2.0.0.tgz", + "integrity": "sha512-NHDDGYudnvRutt/VhKFlX26IotXe1w0cmkDm6JGquh5bz/bDTw0LufSmH/GxTjEdpHEO+bVKFTwdrcGa/9XlKQ==", "dev": true, "requires": { - "type-fest": "^0.4.1" + "type-fest": "^2.5.1" }, "dependencies": { "type-fest": { - "version": "0.4.1", - "resolved": "https://registry.npmjs.org/type-fest/-/type-fest-0.4.1.tgz", - "integrity": "sha512-IwzA/LSfD2vC1/YDYMv/zHP4rDF1usCwllsDpbolT3D4fUepIO7f9K70jjmUewU/LmGUKJcwcVtDCpnKk4BPMw==", + "version": "2.12.2", + "resolved": "https://registry.npmjs.org/type-fest/-/type-fest-2.12.2.tgz", + "integrity": "sha512-qt6ylCGpLjZ7AaODxbpyBZSs9fCI9SkL3Z9q2oxMBQhs/uyY+VD8jHA8ULCGmWQJlBgqvO3EJeAngOHD8zQCrQ==", "dev": true } } @@ -10357,9 +11967,9 @@ } }, "node-releases": { - "version": "2.0.3", - "resolved": "https://registry.npmjs.org/node-releases/-/node-releases-2.0.3.tgz", - "integrity": "sha512-maHFz6OLqYxz+VQyCAtA3PTX4UP/53pa05fyDNc9CwjvJ0yEh6+xBwKsgCxMNhS8taUKBFYxfuiaD9U/55iFaw==", + "version": "2.0.4", + "resolved": "https://registry.npmjs.org/node-releases/-/node-releases-2.0.4.tgz", + "integrity": "sha512-gbMzqQtTtDz/00jQzZ21PQzdI9PyLYqUSvD0p3naOhX4odFji0ZxYdnVwPTxmSwkmxhcFImpozceidSG+AgoPQ==", "dev": true }, "normalize-path": { @@ -10369,18 +11979,26 @@ "dev": true }, "normalize-url": { - "version": "4.5.1", - "resolved": "https://registry.npmjs.org/normalize-url/-/normalize-url-4.5.1.tgz", - "integrity": "sha512-9UZCFRHQdNrfTpGg8+1INIg93B6zE0aXMVFkw1WFwvO4SlZywU6aLg5Of0Ap/PgcbSw4LNxvMWXMeugwMCX0AA==", + "version": "6.1.0", + "resolved": "https://registry.npmjs.org/normalize-url/-/normalize-url-6.1.0.tgz", + "integrity": "sha512-DlL+XwOy3NxAQ8xuC0okPgK46iuVNAK01YN7RueYBqqFeGsBjV9XmCAzAdgt+667bCl5kPh9EqKKDwnaPG1I7A==", "dev": true }, "npm-run-path": { - "version": "4.0.1", - "resolved": "https://registry.npmjs.org/npm-run-path/-/npm-run-path-4.0.1.tgz", - "integrity": "sha512-S48WzZW777zhNIrn7gxOlISNAqi9ZC/uQFnRdbeIHhZhCA6UqpkOT8T1G7BvfdgP4Er8gF4sUbaS0i7QvIfCWw==", + "version": "5.1.0", + "resolved": "https://registry.npmjs.org/npm-run-path/-/npm-run-path-5.1.0.tgz", + "integrity": "sha512-sJOdmRGrY2sjNTRMbSvluQqg+8X7ZK61yvzBEIDhz4f8z1TZFYABsqjjCBd/0PUNE9M6QDgHJXQkGUEm7Q+l9Q==", "dev": true, "requires": { - "path-key": "^3.0.0" + "path-key": "^4.0.0" + }, + "dependencies": { + "path-key": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/path-key/-/path-key-4.0.0.tgz", + "integrity": "sha512-haREypq7xkM7ErfgIyA0z+Bj4AGKlMSdlQE2jvJo6huWD1EdkKYV+G/T4nq0YEF2vgTT8kqMFKo1uHn950r4SQ==", + "dev": true + } } }, "nyc": { @@ -10418,74 +12036,11 @@ "yargs": "^15.0.2" }, "dependencies": { - "camelcase": { - "version": "5.3.1", - "resolved": "https://registry.npmjs.org/camelcase/-/camelcase-5.3.1.tgz", - "integrity": "sha512-L28STB170nwWS63UjtlEOE3dldQApaJXZkOI1uMFfzf3rRuPegHaHesyee+YxQ+W6SvRDQV6UrdOdRiR153wJg==", - "dev": true - }, - "cliui": { - "version": "6.0.0", - "resolved": "https://registry.npmjs.org/cliui/-/cliui-6.0.0.tgz", - "integrity": "sha512-t6wbgtoCXvAzst7QgXxJYqPt0usEfbgQdftEPbLL/cvv6HPE5VgvqCuAIDR0NgU52ds6rFwqrgakNLrHEjCbrQ==", - "dev": true, - "requires": { - "string-width": "^4.2.0", - "strip-ansi": "^6.0.0", - "wrap-ansi": "^6.2.0" - } - }, "resolve-from": { "version": "5.0.0", "resolved": "https://registry.npmjs.org/resolve-from/-/resolve-from-5.0.0.tgz", "integrity": "sha512-qYg9KP24dD5qka9J47d0aVky0N+b4fTU89LN9iDnjB5waksiC49rvMB0PrUJQGoTmH50XPiqOvAjDfaijGxYZw==", "dev": true - }, - "wrap-ansi": { - "version": "6.2.0", - "resolved": "https://registry.npmjs.org/wrap-ansi/-/wrap-ansi-6.2.0.tgz", - "integrity": "sha512-r6lPcBGxZXlIcymEu7InxDMhdW0KDxpLgoFLcguasxCaJ/SOIZwINatK9KY/tf+ZrlywOKU0UDj3ATXUBfxJXA==", - "dev": true, - "requires": { - "ansi-styles": "^4.0.0", - "string-width": "^4.1.0", - "strip-ansi": "^6.0.0" - } - }, - "y18n": { - "version": "4.0.3", - "resolved": "https://registry.npmjs.org/y18n/-/y18n-4.0.3.tgz", - "integrity": "sha512-JKhqTOwSrqNA1NY5lSztJ1GrBiUodLMmIZuLiDaMRJ+itFd+ABVE8XBjOvIWL+rSqNDC74LCSFmlb/U4UZ4hJQ==", - "dev": true - }, - "yargs": { - "version": "15.4.1", - "resolved": "https://registry.npmjs.org/yargs/-/yargs-15.4.1.tgz", - "integrity": "sha512-aePbxDmcYW++PaqBsJ+HYUFwCdv4LVvdnhBy78E57PIor8/OVvhMrADFFEDh8DHDFRv/O9i3lPhsENjO7QX0+A==", - "dev": true, - "requires": { - "cliui": "^6.0.0", - "decamelize": "^1.2.0", - "find-up": "^4.1.0", - "get-caller-file": "^2.0.1", - "require-directory": "^2.1.1", - "require-main-filename": "^2.0.0", - "set-blocking": "^2.0.0", - "string-width": "^4.2.0", - "which-module": "^2.0.0", - "y18n": "^4.0.0", - "yargs-parser": "^18.1.2" - } - }, - "yargs-parser": { - "version": "18.1.3", - "resolved": "https://registry.npmjs.org/yargs-parser/-/yargs-parser-18.1.3.tgz", - "integrity": "sha512-o50j0JeToy/4K6OZcaQmW6lyXXKhq7csREXcDwk2omFPJEwUNOVtJKvmDr9EI1fAJZUyZcRF7kxGBWmRXudrCQ==", - "dev": true, - "requires": { - "camelcase": "^5.0.0", - "decamelize": "^1.2.0" - } } } }, @@ -10529,22 +12084,23 @@ } }, "onetime": { - "version": "5.1.2", - "resolved": "https://registry.npmjs.org/onetime/-/onetime-5.1.2.tgz", - "integrity": "sha512-kbpaSSGJTWdAY5KPVeMOKXSrPtr8C8C7wodJbcsd51jRnmD+GZu8Y0VoU6Dm5Z4vWr0Ig/1NKuWRKf7j5aaYSg==", + "version": "6.0.0", + "resolved": "https://registry.npmjs.org/onetime/-/onetime-6.0.0.tgz", + "integrity": "sha512-1FlR+gjXK7X+AsAHso35MnyN5KqGwJRi/31ft6x0M194ht7S+rWAvd7PHss9xSKMzE0asv1pyIHaJYq+BbacAQ==", "dev": true, "requires": { - "mimic-fn": "^2.1.0" + "mimic-fn": "^4.0.0" } }, "open": { - "version": "7.4.2", - "resolved": "https://registry.npmjs.org/open/-/open-7.4.2.tgz", - "integrity": "sha512-MVHddDVweXZF3awtlAS+6pgKLlm/JgxZ90+/NBurBoQctVOOB/zDdVjcyPzQ+0laDGbsWgrRkflI65sQeOgT9Q==", + "version": "8.4.0", + "resolved": "https://registry.npmjs.org/open/-/open-8.4.0.tgz", + "integrity": "sha512-XgFPPM+B28FtCCgSb9I+s9szOC1vZRSwgWsRUA5ylIxRTgKozqjOCrVOqGsYABPYK5qnfqClxZTFBa8PKt2v6Q==", "dev": true, "requires": { - "is-docker": "^2.0.0", - "is-wsl": "^2.1.1" + "define-lazy-prop": "^2.0.0", + "is-docker": "^2.1.1", + "is-wsl": "^2.2.0" } }, "optionator": { @@ -10562,30 +12118,81 @@ } }, "ora": { - "version": "5.4.1", - "resolved": "https://registry.npmjs.org/ora/-/ora-5.4.1.tgz", - "integrity": "sha512-5b6Y85tPxZZ7QytO+BQzysW31HJku27cRIlkbAXaNx+BdcVi+LlRFmVXzeF6a7JCwJpyw5c4b+YSVImQIrBpuQ==", - "dev": true, - "requires": { - "bl": "^4.1.0", - "chalk": "^4.1.0", - "cli-cursor": "^3.1.0", - "cli-spinners": "^2.5.0", - "is-interactive": "^1.0.0", - "is-unicode-supported": "^0.1.0", - "log-symbols": "^4.1.0", - "strip-ansi": "^6.0.0", + "version": "6.1.0", + "resolved": "https://registry.npmjs.org/ora/-/ora-6.1.0.tgz", + "integrity": "sha512-CxEP6845hLK+NHFWZ+LplGO4zfw4QSfxTlqMfvlJ988GoiUeZDMzCvqsZkFHv69sPICmJH1MDxZoQFOKXerAVw==", + "dev": true, + "requires": { + "bl": "^5.0.0", + "chalk": "^5.0.0", + "cli-cursor": "^4.0.0", + "cli-spinners": "^2.6.1", + "is-interactive": "^2.0.0", + "is-unicode-supported": "^1.1.0", + "log-symbols": "^5.1.0", + "strip-ansi": "^7.0.1", "wcwidth": "^1.0.1" + }, + "dependencies": { + "ansi-regex": { + "version": "6.0.1", + "resolved": "https://registry.npmjs.org/ansi-regex/-/ansi-regex-6.0.1.tgz", + "integrity": "sha512-n5M855fKb2SsfMIiFFoVrABHJC8QtHwVx+mHWP3QcEqBHYienj5dHSgjbxtC0WEZXYt4wcD6zrQElDPhFuZgfA==", + "dev": true + }, + "cli-cursor": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/cli-cursor/-/cli-cursor-4.0.0.tgz", + "integrity": "sha512-VGtlMu3x/4DOtIUwEkRezxUZ2lBacNJCHash0N0WeZDBS+7Ux1dm3XWAgWYxLJFMMdOeXMHXorshEFhbMSGelg==", + "dev": true, + "requires": { + "restore-cursor": "^4.0.0" + } + }, + "mimic-fn": { + "version": "2.1.0", + "resolved": "https://registry.npmjs.org/mimic-fn/-/mimic-fn-2.1.0.tgz", + "integrity": "sha512-OqbOk5oEQeAZ8WXWydlu9HJjz9WVdEIvamMCcXmuqUYjTknH/sqsWvhQ3vgwKFRR1HpjvNBKQ37nbJgYzGqGcg==", + "dev": true + }, + "onetime": { + "version": "5.1.2", + "resolved": "https://registry.npmjs.org/onetime/-/onetime-5.1.2.tgz", + "integrity": "sha512-kbpaSSGJTWdAY5KPVeMOKXSrPtr8C8C7wodJbcsd51jRnmD+GZu8Y0VoU6Dm5Z4vWr0Ig/1NKuWRKf7j5aaYSg==", + "dev": true, + "requires": { + "mimic-fn": "^2.1.0" + } + }, + "restore-cursor": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/restore-cursor/-/restore-cursor-4.0.0.tgz", + "integrity": "sha512-I9fPXU9geO9bHOt9pHHOhOkYerIMsmVaWB0rA2AI9ERh/+x/i7MV5HKBNrg+ljO5eoPVgCcnFuRjJ9uH6I/3eg==", + "dev": true, + "requires": { + "onetime": "^5.1.0", + "signal-exit": "^3.0.2" + } + }, + "strip-ansi": { + "version": "7.0.1", + "resolved": "https://registry.npmjs.org/strip-ansi/-/strip-ansi-7.0.1.tgz", + "integrity": "sha512-cXNxvT8dFNRVfhVME3JAe98mkXDYN2O1l7jmcwMnOslDeESg1rF/OZMtK0nRAhiari1unG5cD4jG3rapUAkLbw==", + "dev": true, + "requires": { + "ansi-regex": "^6.0.1" + } + } } }, "os-name": { - "version": "4.0.1", - "resolved": "https://registry.npmjs.org/os-name/-/os-name-4.0.1.tgz", - "integrity": "sha512-xl9MAoU97MH1Xt5K9ERft2YfCAoaO6msy1OBA0ozxEC0x0TmIoE6K3QvgJMMZA9yKGLmHXNY/YZoDbiGDj4zYw==", + "version": "5.0.1", + "resolved": "https://registry.npmjs.org/os-name/-/os-name-5.0.1.tgz", + "integrity": "sha512-0EQpaHUHq7olp2/YFUr+0vZi9tMpDTblHGz+Ch5RntKxiRXOAY0JOz1UlxhSjMSksHvkm13eD6elJj3M8Ht/kw==", "dev": true, "requires": { - "macos-release": "^2.5.0", - "windows-release": "^4.0.0" + "macos-release": "^3.0.1", + "windows-release": "^5.0.1" } }, "os-tmpdir": { @@ -10595,9 +12202,9 @@ "dev": true }, "p-cancelable": { - "version": "1.1.0", - "resolved": "https://registry.npmjs.org/p-cancelable/-/p-cancelable-1.1.0.tgz", - "integrity": "sha512-s73XxOZ4zpt1edZYZzvhqFa6uvQc1vwUa0K0BdtIZgQMAJj9IbebH+JkgKZc9h+B05PKHLOTl4ajG1BmNrVZlw==", + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/p-cancelable/-/p-cancelable-3.0.0.tgz", + "integrity": "sha512-mlVgR3PGuzlo0MmTdk4cXqXWlwQDLnONTAg6sm62XkMJEiRxN3GL3SffkYvqwonbkJBcrI7Uvv5Zh9yjvn2iUw==", "dev": true }, "p-limit": { @@ -10633,6 +12240,34 @@ "integrity": "sha512-R4nPAVTAU0B9D35/Gk3uJf/7XYbQcyohSKdvAxIRSNghFl4e71hVoGnBNQz9cWaXxO2I10KTC+3jMdvvoKw6dQ==", "dev": true }, + "pac-proxy-agent": { + "version": "5.0.0", + "resolved": "https://registry.npmjs.org/pac-proxy-agent/-/pac-proxy-agent-5.0.0.tgz", + "integrity": "sha512-CcFG3ZtnxO8McDigozwE3AqAw15zDvGH+OjXO4kzf7IkEKkQ4gxQ+3sdF50WmhQ4P/bVusXcqNE2S3XrNURwzQ==", + "dev": true, + "requires": { + "@tootallnate/once": "1", + "agent-base": "6", + "debug": "4", + "get-uri": "3", + "http-proxy-agent": "^4.0.1", + "https-proxy-agent": "5", + "pac-resolver": "^5.0.0", + "raw-body": "^2.2.0", + "socks-proxy-agent": "5" + } + }, + "pac-resolver": { + "version": "5.0.0", + "resolved": "https://registry.npmjs.org/pac-resolver/-/pac-resolver-5.0.0.tgz", + "integrity": "sha512-H+/A6KitiHNNW+bxBKREk2MCGSxljfqRX76NjummWEYIat7ldVXRU3dhRIE3iXZ0nvGBk6smv3nntxKkzRL8NA==", + "dev": true, + "requires": { + "degenerator": "^3.0.1", + "ip": "^1.1.5", + "netmask": "^2.0.1" + } + }, "package-hash": { "version": "4.0.0", "resolved": "https://registry.npmjs.org/package-hash/-/package-hash-4.0.0.tgz", @@ -10655,6 +12290,140 @@ "registry-auth-token": "^4.0.0", "registry-url": "^5.0.0", "semver": "^6.2.0" + }, + "dependencies": { + "@sindresorhus/is": { + "version": "0.14.0", + "resolved": "https://registry.npmjs.org/@sindresorhus/is/-/is-0.14.0.tgz", + "integrity": "sha512-9NET910DNaIPngYnLLPeg+Ogzqsi9uM4mSboU5y6p8S5DzMTVEsJZrawi+BoDNUVBa2DhJqQYUFvMDfgU062LQ==", + "dev": true + }, + "@szmarczak/http-timer": { + "version": "1.1.2", + "resolved": "https://registry.npmjs.org/@szmarczak/http-timer/-/http-timer-1.1.2.tgz", + "integrity": "sha512-XIB2XbzHTN6ieIjfIMV9hlVcfPU26s2vafYWQcZHWXHOxiaRZYEDKEwdl129Zyg50+foYV2jCgtrqSA6qNuNSA==", + "dev": true, + "requires": { + "defer-to-connect": "^1.0.1" + } + }, + "cacheable-request": { + "version": "6.1.0", + "resolved": "https://registry.npmjs.org/cacheable-request/-/cacheable-request-6.1.0.tgz", + "integrity": "sha512-Oj3cAGPCqOZX7Rz64Uny2GYAZNliQSqfbePrgAQ1wKAihYmCUnraBtJtKcGR4xz7wF+LoJC+ssFZvv5BgF9Igg==", + "dev": true, + "requires": { + "clone-response": "^1.0.2", + "get-stream": "^5.1.0", + "http-cache-semantics": "^4.0.0", + "keyv": "^3.0.0", + "lowercase-keys": "^2.0.0", + "normalize-url": "^4.1.0", + "responselike": "^1.0.2" + }, + "dependencies": { + "get-stream": { + "version": "5.2.0", + "resolved": "https://registry.npmjs.org/get-stream/-/get-stream-5.2.0.tgz", + "integrity": "sha512-nBF+F1rAZVCu/p7rjzgA+Yb4lfYXrpl7a6VmJrU8wF9I1CKvP/QwPNZHnOlwbTkY6dvtFIzFMSyQXbLoTQPRpA==", + "dev": true, + "requires": { + "pump": "^3.0.0" + } + }, + "lowercase-keys": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/lowercase-keys/-/lowercase-keys-2.0.0.tgz", + "integrity": "sha512-tqNXrS78oMOE73NMxK4EMLQsQowWf8jKooH9g7xPavRT706R6bkQJ6DY2Te7QukaZsulxa30wQ7bk0pm4XiHmA==", + "dev": true + } + } + }, + "decompress-response": { + "version": "3.3.0", + "resolved": "https://registry.npmjs.org/decompress-response/-/decompress-response-3.3.0.tgz", + "integrity": "sha1-gKTdMjdIOEv6JICDYirt7Jgq3/M=", + "dev": true, + "requires": { + "mimic-response": "^1.0.0" + } + }, + "defer-to-connect": { + "version": "1.1.3", + "resolved": "https://registry.npmjs.org/defer-to-connect/-/defer-to-connect-1.1.3.tgz", + "integrity": "sha512-0ISdNousHvZT2EiFlZeZAHBUvSxmKswVCEf8hW7KWgG4a8MVEu/3Vb6uWYozkjylyCxe0JBIiRB1jV45S70WVQ==", + "dev": true + }, + "get-stream": { + "version": "4.1.0", + "resolved": "https://registry.npmjs.org/get-stream/-/get-stream-4.1.0.tgz", + "integrity": "sha512-GMat4EJ5161kIy2HevLlr4luNjBgvmj413KaQA7jt4V8B4RDsfpHk7WQ9GVqfYyyx8OS/L66Kox+rJRNklLK7w==", + "dev": true, + "requires": { + "pump": "^3.0.0" + } + }, + "got": { + "version": "9.6.0", + "resolved": "https://registry.npmjs.org/got/-/got-9.6.0.tgz", + "integrity": "sha512-R7eWptXuGYxwijs0eV+v3o6+XH1IqVK8dJOEecQfTmkncw9AV4dcw/Dhxi8MdlqPthxxpZyizMzyg8RTmEsG+Q==", + "dev": true, + "requires": { + "@sindresorhus/is": "^0.14.0", + "@szmarczak/http-timer": "^1.1.2", + "cacheable-request": "^6.0.0", + "decompress-response": "^3.3.0", + "duplexer3": "^0.1.4", + "get-stream": "^4.1.0", + "lowercase-keys": "^1.0.1", + "mimic-response": "^1.0.1", + "p-cancelable": "^1.0.0", + "to-readable-stream": "^1.0.0", + "url-parse-lax": "^3.0.0" + } + }, + "json-buffer": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/json-buffer/-/json-buffer-3.0.0.tgz", + "integrity": "sha1-Wx85evx11ne96Lz8Dkfh+aPZqJg=", + "dev": true + }, + "keyv": { + "version": "3.1.0", + "resolved": "https://registry.npmjs.org/keyv/-/keyv-3.1.0.tgz", + "integrity": "sha512-9ykJ/46SN/9KPM/sichzQ7OvXyGDYKGTaDlKMGCAlg2UK8KRy4jb0d8sFc+0Tt0YYnThq8X2RZgCg74RPxgcVA==", + "dev": true, + "requires": { + "json-buffer": "3.0.0" + } + }, + "lowercase-keys": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/lowercase-keys/-/lowercase-keys-1.0.1.tgz", + "integrity": "sha512-G2Lj61tXDnVFFOi8VZds+SoQjtQC3dgokKdDG2mTm1tx4m50NUHBOZSBwQQHyy0V12A0JTG4icfZQH+xPyh8VA==", + "dev": true + }, + "normalize-url": { + "version": "4.5.1", + "resolved": "https://registry.npmjs.org/normalize-url/-/normalize-url-4.5.1.tgz", + "integrity": "sha512-9UZCFRHQdNrfTpGg8+1INIg93B6zE0aXMVFkw1WFwvO4SlZywU6aLg5Of0Ap/PgcbSw4LNxvMWXMeugwMCX0AA==", + "dev": true + }, + "p-cancelable": { + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/p-cancelable/-/p-cancelable-1.1.0.tgz", + "integrity": "sha512-s73XxOZ4zpt1edZYZzvhqFa6uvQc1vwUa0K0BdtIZgQMAJj9IbebH+JkgKZc9h+B05PKHLOTl4ajG1BmNrVZlw==", + "dev": true + }, + "responselike": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/responselike/-/responselike-1.0.2.tgz", + "integrity": "sha1-kYcg7ztjHFZCvgaPFa3lpG9Loec=", + "dev": true, + "requires": { + "lowercase-keys": "^1.0.0" + } + } } }, "parent-module": { @@ -10700,14 +12469,6 @@ "normalize-url": "^6.1.0", "parse-path": "^4.0.0", "protocols": "^1.4.0" - }, - "dependencies": { - "normalize-url": { - "version": "6.1.0", - "resolved": "https://registry.npmjs.org/normalize-url/-/normalize-url-6.1.0.tgz", - "integrity": "sha512-DlL+XwOy3NxAQ8xuC0okPgK46iuVNAK01YN7RueYBqqFeGsBjV9XmCAzAdgt+667bCl5kPh9EqKKDwnaPG1I7A==", - "dev": true - } } }, "path-exists": { @@ -10840,6 +12601,28 @@ "integrity": "sha512-IgjKyaUSjsROSO8/D49Ab7hP8mJgTYcqApOqdPhLoPxAplXmkp+zRvsrSQjFn5by0rhm4VH0GAUELIPpx7B1yg==", "dev": true }, + "proxy-agent": { + "version": "5.0.0", + "resolved": "https://registry.npmjs.org/proxy-agent/-/proxy-agent-5.0.0.tgz", + "integrity": "sha512-gkH7BkvLVkSfX9Dk27W6TyNOWWZWRilRfk1XxGNWOYJ2TuedAv1yFpCaU9QSBmBe716XOTNpYNOzhysyw8xn7g==", + "dev": true, + "requires": { + "agent-base": "^6.0.0", + "debug": "4", + "http-proxy-agent": "^4.0.0", + "https-proxy-agent": "^5.0.0", + "lru-cache": "^5.1.1", + "pac-proxy-agent": "^5.0.0", + "proxy-from-env": "^1.0.0", + "socks-proxy-agent": "^5.0.0" + } + }, + "proxy-from-env": { + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/proxy-from-env/-/proxy-from-env-1.1.0.tgz", + "integrity": "sha512-D+zkORCbA9f1tdWRK0RaCR3GPv50cMxcrz4X8k5LTSUD1Dkw47mKJEZQNunItRTkWwgtaUSo1RVFRIG9ZXiFYg==", + "dev": true + }, "pump": { "version": "3.0.0", "resolved": "https://registry.npmjs.org/pump/-/pump-3.0.0.tgz", @@ -10892,6 +12675,12 @@ "integrity": "sha512-NuaNSa6flKT5JaSYQzJok04JzTL1CA6aGhv5rfLW3PgqA+M2ChpZQnAC8h8i4ZFkBS8X5RqkDBHA7r4hej3K9A==", "dev": true }, + "quick-lru": { + "version": "5.1.1", + "resolved": "https://registry.npmjs.org/quick-lru/-/quick-lru-5.1.1.tgz", + "integrity": "sha512-WuyALRjWPDGtt/wzJiadO5AXY+8hZ80hVpe6MyivgraREW751X3SbhRvG3eLKOYN+8VEvqLcf3wdnt44Z4S4SA==", + "dev": true + }, "randombytes": { "version": "2.1.0", "resolved": "https://registry.npmjs.org/randombytes/-/randombytes-2.1.0.tgz", @@ -10901,6 +12690,18 @@ "safe-buffer": "^5.1.0" } }, + "raw-body": { + "version": "2.5.1", + "resolved": "https://registry.npmjs.org/raw-body/-/raw-body-2.5.1.tgz", + "integrity": "sha512-qqJBtEyVgS0ZmPGdCFPWJ3FreoqvG4MVQln/kCgF7Olq95IbOp0/BWyMwbdtn4VTvkM8Y7khCQ2Xgk/tcrCXig==", + "dev": true, + "requires": { + "bytes": "3.1.2", + "http-errors": "2.0.0", + "iconv-lite": "0.4.24", + "unpipe": "1.0.0" + } + }, "rc": { "version": "1.2.8", "resolved": "https://registry.npmjs.org/rc/-/rc-1.2.8.tgz", @@ -10918,6 +12719,12 @@ "resolved": "https://registry.npmjs.org/ini/-/ini-1.3.8.tgz", "integrity": "sha512-JV/yugV2uzW5iMRSiZAyDtQd+nxtUnjeLt0acNdw98kKLrvuRVyB80tsREOE7yvGVgalhZ6RNXCmEHkUKBKxew==", "dev": true + }, + "strip-json-comments": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/strip-json-comments/-/strip-json-comments-2.0.1.tgz", + "integrity": "sha1-PFMZQukIwml8DsNEhYwobHygpgo=", + "dev": true } } }, @@ -10975,71 +12782,81 @@ } }, "release-it": { - "version": "14.14.2", - "resolved": "https://registry.npmjs.org/release-it/-/release-it-14.14.2.tgz", - "integrity": "sha512-+TE5Zg7x5BE/xw6i8SN9rmsGxCE2GVqH1v8Ay1L09nsLQx1HJhihAqUtCCDdgOuLvGpX0xgDMsSzakDlDLpOkA==", + "version": "15.0.0", + "resolved": "https://registry.npmjs.org/release-it/-/release-it-15.0.0.tgz", + "integrity": "sha512-Dnio6p+1O88UdQZmPjdXqq+Nrrn5t0USZyOctTPK5M36kOOfQTdp8V1Wlagz9QYIYr93NwovEZ+f4wK0P/kHbw==", "dev": true, "requires": { "@iarna/toml": "2.2.5", "@octokit/rest": "18.12.0", "async-retry": "1.3.3", - "chalk": "4.1.2", + "chalk": "5.0.1", "cosmiconfig": "7.0.1", - "debug": "4.3.4", - "execa": "5.1.1", + "execa": "6.1.0", "form-data": "4.0.0", "git-url-parse": "11.6.0", - "globby": "11.0.4", - "got": "9.6.0", - "import-cwd": "3.0.0", - "inquirer": "8.2.0", + "globby": "13.1.1", + "got": "12.0.4", + "inquirer": "8.2.4", "is-ci": "3.0.1", "lodash": "4.17.21", "mime-types": "2.1.35", - "new-github-release-url": "1.0.0", - "open": "7.4.2", - "ora": "5.4.1", - "os-name": "4.0.1", - "parse-json": "5.2.0", + "new-github-release-url": "2.0.0", + "open": "8.4.0", + "ora": "6.1.0", + "os-name": "5.0.1", "promise.allsettled": "1.0.5", - "semver": "7.3.5", + "proxy-agent": "5.0.0", + "semver": "7.3.7", "shelljs": "0.8.5", "update-notifier": "5.1.0", - "url-join": "4.0.1", - "uuid": "8.3.2", + "url-join": "5.0.0", "wildcard-match": "5.1.2", - "yaml": "1.10.2", - "yargs-parser": "20.2.9" + "yargs-parser": "21.0.1" }, "dependencies": { - "array-union": { - "version": "2.1.0", - "resolved": "https://registry.npmjs.org/array-union/-/array-union-2.1.0.tgz", - "integrity": "sha512-HGyxoOTYUyCM6stUe6EJgnd4EoewAI7zMdfqO+kGjnlZmBDz/cR5pf8r/cR4Wq60sL/p0IkcjUEEPwS3GFrIyw==", - "dev": true - }, "globby": { - "version": "11.0.4", - "resolved": "https://registry.npmjs.org/globby/-/globby-11.0.4.tgz", - "integrity": "sha512-9O4MVG9ioZJ08ffbcyVYyLOJLk5JQ688pJ4eMGLpdWLHq/Wr1D9BlriLQyL0E+jbkuePVZXYFj47QM/v093wHg==", + "version": "13.1.1", + "resolved": "https://registry.npmjs.org/globby/-/globby-13.1.1.tgz", + "integrity": "sha512-XMzoDZbGZ37tufiv7g0N4F/zp3zkwdFtVbV3EHsVl1KQr4RPLfNoT068/97RPshz2J5xYNEjLKKBKaGHifBd3Q==", "dev": true, "requires": { - "array-union": "^2.1.0", "dir-glob": "^3.0.1", - "fast-glob": "^3.1.1", - "ignore": "^5.1.4", - "merge2": "^1.3.0", - "slash": "^3.0.0" + "fast-glob": "^3.2.11", + "ignore": "^5.2.0", + "merge2": "^1.4.1", + "slash": "^4.0.0" + } + }, + "lru-cache": { + "version": "6.0.0", + "resolved": "https://registry.npmjs.org/lru-cache/-/lru-cache-6.0.0.tgz", + "integrity": "sha512-Jo6dJ04CmSjuznwJSS3pUeWmd/H0ffTlkXXgwZi+eq1UCmqQwCh+eLsYOYCwY991i2Fah4h1BEMCx4qThGbsiA==", + "dev": true, + "requires": { + "yallist": "^4.0.0" } }, "semver": { - "version": "7.3.5", - "resolved": "https://registry.npmjs.org/semver/-/semver-7.3.5.tgz", - "integrity": "sha512-PoeGJYh8HK4BTO/a9Tf6ZG3veo/A7ZVsYrSA6J8ny9nb3B1VrpkuN+z9OE5wfE5p6H4LchYZsegiQgbJD94ZFQ==", + "version": "7.3.7", + "resolved": "https://registry.npmjs.org/semver/-/semver-7.3.7.tgz", + "integrity": "sha512-QlYTucUYOews+WeEujDoEGziz4K6c47V/Bd+LjSSYcA94p+DmINdf7ncaUinThfvZyu13lN9OY1XDxt8C0Tw0g==", "dev": true, "requires": { "lru-cache": "^6.0.0" } + }, + "slash": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/slash/-/slash-4.0.0.tgz", + "integrity": "sha512-3dOsAHXXUkQTpOYcoAxLIorMTp4gIQr5IW3iVb7A7lFIp0VHhnynm9izx6TssdrIcVIESAlVjtnO2K8bg+Coew==", + "dev": true + }, + "yallist": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/yallist/-/yallist-4.0.0.tgz", + "integrity": "sha512-3wdGidZyq5PB084XLES5TpOSRA3wjXAlIWMhum2kRcv/41Sn2emQ0dycQW4uZXLejwKvg6EsvbdlVL+FYEct7A==", + "dev": true } } }, @@ -11075,6 +12892,12 @@ "supports-preserve-symlinks-flag": "^1.0.0" } }, + "resolve-alpn": { + "version": "1.2.1", + "resolved": "https://registry.npmjs.org/resolve-alpn/-/resolve-alpn-1.2.1.tgz", + "integrity": "sha512-0a1F4l73/ZFZOakJnQ3FvkJ2+gSTQWz/r2KE5OdDY0TxPm5h4GkqkWWfM47T7HsbnOtcJVEF4epCVy6u7Q3K+g==", + "dev": true + }, "resolve-from": { "version": "4.0.0", "resolved": "https://registry.npmjs.org/resolve-from/-/resolve-from-4.0.0.tgz", @@ -11082,12 +12905,20 @@ "dev": true }, "responselike": { - "version": "1.0.2", - "resolved": "https://registry.npmjs.org/responselike/-/responselike-1.0.2.tgz", - "integrity": "sha1-kYcg7ztjHFZCvgaPFa3lpG9Loec=", + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/responselike/-/responselike-2.0.0.tgz", + "integrity": "sha512-xH48u3FTB9VsZw7R+vvgaKeLKzT6jOogbQhEe/jewwnZgzPcnyWui2Av6JpoYZF/91uueC+lqhWqeURw5/qhCw==", "dev": true, "requires": { - "lowercase-keys": "^1.0.0" + "lowercase-keys": "^2.0.0" + }, + "dependencies": { + "lowercase-keys": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/lowercase-keys/-/lowercase-keys-2.0.0.tgz", + "integrity": "sha512-tqNXrS78oMOE73NMxK4EMLQsQowWf8jKooH9g7xPavRT706R6bkQJ6DY2Te7QukaZsulxa30wQ7bk0pm4XiHmA==", + "dev": true + } } }, "restore-cursor": { @@ -11098,6 +12929,23 @@ "requires": { "onetime": "^5.1.0", "signal-exit": "^3.0.2" + }, + "dependencies": { + "mimic-fn": { + "version": "2.1.0", + "resolved": "https://registry.npmjs.org/mimic-fn/-/mimic-fn-2.1.0.tgz", + "integrity": "sha512-OqbOk5oEQeAZ8WXWydlu9HJjz9WVdEIvamMCcXmuqUYjTknH/sqsWvhQ3vgwKFRR1HpjvNBKQ37nbJgYzGqGcg==", + "dev": true + }, + "onetime": { + "version": "5.1.2", + "resolved": "https://registry.npmjs.org/onetime/-/onetime-5.1.2.tgz", + "integrity": "sha512-kbpaSSGJTWdAY5KPVeMOKXSrPtr8C8C7wodJbcsd51jRnmD+GZu8Y0VoU6Dm5Z4vWr0Ig/1NKuWRKf7j5aaYSg==", + "dev": true, + "requires": { + "mimic-fn": "^2.1.0" + } + } } }, "retry": { @@ -11146,9 +12994,9 @@ } }, "safe-buffer": { - "version": "5.2.1", - "resolved": "https://registry.npmjs.org/safe-buffer/-/safe-buffer-5.2.1.tgz", - "integrity": "sha512-rp3So07KcdmmKbGvgaNxQSJr7bGVSVk5S9Eq1F+ppbRo70+YeaDxkw5Dd8NPN+GD6bjnYm2VuPuCXmpuYvmCXQ==", + "version": "5.1.2", + "resolved": "https://registry.npmjs.org/safe-buffer/-/safe-buffer-5.1.2.tgz", + "integrity": "sha512-Gd2UZBJDkXlY7GbJxfsE8/nvKkUEU1G38c1siN6QP6a9PT9MmHB8GnpscSmMJSoF8LOIrt8ud/wPtojys4G6+g==", "dev": true }, "safer-buffer": { @@ -11187,6 +13035,12 @@ "integrity": "sha1-BF+XgtARrppoA93TgrJDkrPYkPc=", "dev": true }, + "setprototypeof": { + "version": "1.2.0", + "resolved": "https://registry.npmjs.org/setprototypeof/-/setprototypeof-1.2.0.tgz", + "integrity": "sha512-E5LDX7Wrp85Kil5bhZv46j8jOeboKq5JMmYM3gVGdGH8xFpPWXUMsNrlODCrkoxMEeNi/XZIwuRvY4XNwYMJpw==", + "dev": true + }, "shebang-command": { "version": "2.0.0", "resolved": "https://registry.npmjs.org/shebang-command/-/shebang-command-2.0.0.tgz", @@ -11261,10 +13115,37 @@ "integrity": "sha512-g9Q1haeby36OSStwb4ntCGGGaKsaVSjQ68fBxoQcutl5fS1vuY18H3wSt3jFyFtrkx+Kz0V1G85A4MyAdDMi2Q==", "dev": true }, + "smart-buffer": { + "version": "4.2.0", + "resolved": "https://registry.npmjs.org/smart-buffer/-/smart-buffer-4.2.0.tgz", + "integrity": "sha512-94hK0Hh8rPqQl2xXc3HsaBoOXKV20MToPkcXvwbISWLEs+64sBq5kFgn2kJDHb1Pry9yrP0dxrCI9RRci7RXKg==", + "dev": true + }, + "socks": { + "version": "2.6.2", + "resolved": "https://registry.npmjs.org/socks/-/socks-2.6.2.tgz", + "integrity": "sha512-zDZhHhZRY9PxRruRMR7kMhnf3I8hDs4S3f9RecfnGxvcBHQcKcIH/oUcEWffsfl1XxdYlA7nnlGbbTvPz9D8gA==", + "dev": true, + "requires": { + "ip": "^1.1.5", + "smart-buffer": "^4.2.0" + } + }, + "socks-proxy-agent": { + "version": "5.0.1", + "resolved": "https://registry.npmjs.org/socks-proxy-agent/-/socks-proxy-agent-5.0.1.tgz", + "integrity": "sha512-vZdmnjb9a2Tz6WEQVIurybSwElwPxMZaIc7PzqbJTrezcKNznv6giT7J7tZDZ1BojVaa1jvO/UiUdhDVB0ACoQ==", + "dev": true, + "requires": { + "agent-base": "^6.0.2", + "debug": "4", + "socks": "^2.3.3" + } + }, "source-map": { - "version": "0.5.7", - "resolved": "https://registry.npmjs.org/source-map/-/source-map-0.5.7.tgz", - "integrity": "sha1-igOdLRAh0i0eoUyA2OpGi6LvP8w=", + "version": "0.6.1", + "resolved": "https://registry.npmjs.org/source-map/-/source-map-0.6.1.tgz", + "integrity": "sha512-UjgapumWlbMhkBgzT7Ykc5YXUT46F0iKu8SGXq0bcwP5dz/h0Plj6enJqjz1Zbq2l5WaqYnrVbwWOWMyF3F47g==", "dev": true }, "source-map-support": { @@ -11275,14 +13156,6 @@ "requires": { "buffer-from": "^1.0.0", "source-map": "^0.6.0" - }, - "dependencies": { - "source-map": { - "version": "0.6.1", - "resolved": "https://registry.npmjs.org/source-map/-/source-map-0.6.1.tgz", - "integrity": "sha512-UjgapumWlbMhkBgzT7Ykc5YXUT46F0iKu8SGXq0bcwP5dz/h0Plj6enJqjz1Zbq2l5WaqYnrVbwWOWMyF3F47g==", - "dev": true - } } }, "spawn-wrap": { @@ -11311,6 +13184,12 @@ "integrity": "sha1-BOaSb2YolTVPPdAVIDYzuFcpfiw=", "dev": true }, + "statuses": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/statuses/-/statuses-2.0.1.tgz", + "integrity": "sha512-RwNA9Z/7PrK06rYLIzFMlaF+l73iwpzsqRIFgbMLbTcLD6cOao82TaWefPXQvB2fOC4AjuYSEndS7N/mTCbkdQ==", + "dev": true + }, "strict-uri-encode": { "version": "2.0.0", "resolved": "https://registry.npmjs.org/strict-uri-encode/-/strict-uri-encode-2.0.0.tgz", @@ -11324,6 +13203,14 @@ "dev": true, "requires": { "safe-buffer": "~5.2.0" + }, + "dependencies": { + "safe-buffer": { + "version": "5.2.1", + "resolved": "https://registry.npmjs.org/safe-buffer/-/safe-buffer-5.2.1.tgz", + "integrity": "sha512-rp3So07KcdmmKbGvgaNxQSJr7bGVSVk5S9Eq1F+ppbRo70+YeaDxkw5Dd8NPN+GD6bjnYm2VuPuCXmpuYvmCXQ==", + "dev": true + } } }, "string-width": { @@ -11373,15 +13260,15 @@ "dev": true }, "strip-final-newline": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/strip-final-newline/-/strip-final-newline-2.0.0.tgz", - "integrity": "sha512-BrpvfNAE3dcvq7ll3xVumzjKjZQ5tI1sEUIKr3Uoks0XUl45St3FlatVqef9prk4jRDzhW6WZg+3bk93y6pLjA==", + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/strip-final-newline/-/strip-final-newline-3.0.0.tgz", + "integrity": "sha512-dOESqjYr96iWYylGObzd39EuNTa5VJxyvVAEm5Jnh7KGo75V43Hk1odPQkNDyXNmUR6k+gEiDVXnjB8HJ3crXw==", "dev": true }, "strip-json-comments": { - "version": "2.0.1", - "resolved": "https://registry.npmjs.org/strip-json-comments/-/strip-json-comments-2.0.1.tgz", - "integrity": "sha1-PFMZQukIwml8DsNEhYwobHygpgo=", + "version": "3.1.1", + "resolved": "https://registry.npmjs.org/strip-json-comments/-/strip-json-comments-3.1.1.tgz", + "integrity": "sha512-6fPc+R4ihwqP6N/aIv2f1gMH8lOVtWQHoqC4yK6oSDVVocumAsfCqjkXnqiYMhmMwS/mEHLp7Vehlt3ql6lEig==", "dev": true }, "strip-outer": { @@ -11461,6 +13348,12 @@ "is-number": "^7.0.0" } }, + "toidentifier": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/toidentifier/-/toidentifier-1.0.1.tgz", + "integrity": "sha512-o5sSPKEkg/DIQNmH43V0/uerLrpzVedkUh8tGNvaeXpfpuwjKenlSox/2O/BTlZUtEe+JG7s5YhEz608PlAHRA==", + "dev": true + }, "tr46": { "version": "0.0.3", "resolved": "https://registry.npmjs.org/tr46/-/tr46-0.0.3.tgz", @@ -11592,9 +13485,9 @@ } }, "typescript": { - "version": "4.6.3", - "resolved": "https://registry.npmjs.org/typescript/-/typescript-4.6.3.tgz", - "integrity": "sha512-yNIatDa5iaofVozS/uQJEl3JRWLKKGJKh6Yaiv0GLGSuhpFJe7P3SbHZ8/yjAHRQwKRoA6YZqlfjXWmVzoVSMw==", + "version": "4.6.4", + "resolved": "https://registry.npmjs.org/typescript/-/typescript-4.6.4.tgz", + "integrity": "sha512-9ia/jWHIEbo49HfjrLGfKbZSuWo9iTMwXO+Ca3pRsSpbsMbc7/IU8NKdCZVRRBafVPGnoJeFL76ZOAA84I9fEg==", "dev": true }, "unbox-primitive": { @@ -11630,6 +13523,12 @@ "integrity": "sha512-rBJeI5CXAlmy1pV+617WB9J63U6XcazHHF2f2dbJix4XzpUF0RS3Zbj0FGIOCAva5P/d/GBOYaACQ1w+0azUkg==", "dev": true }, + "unpipe": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/unpipe/-/unpipe-1.0.0.tgz", + "integrity": "sha1-sr9O6FFKrmFltIF4KdIbLvSZBOw=", + "dev": true + }, "update-notifier": { "version": "5.1.0", "resolved": "https://registry.npmjs.org/update-notifier/-/update-notifier-5.1.0.tgz", @@ -11652,6 +13551,16 @@ "xdg-basedir": "^4.0.0" }, "dependencies": { + "chalk": { + "version": "4.1.2", + "resolved": "https://registry.npmjs.org/chalk/-/chalk-4.1.2.tgz", + "integrity": "sha512-oKnbhFyRIXpUuez8iBMmyEa4nbj4IOQyuhc/wy9kY7/WVPcwIO9VA668Pu8RkO7+0G76SLROeyw9CpQ061i4mA==", + "dev": true, + "requires": { + "ansi-styles": "^4.1.0", + "supports-color": "^7.1.0" + } + }, "ci-info": { "version": "2.0.0", "resolved": "https://registry.npmjs.org/ci-info/-/ci-info-2.0.0.tgz", @@ -11667,6 +13576,15 @@ "ci-info": "^2.0.0" } }, + "lru-cache": { + "version": "6.0.0", + "resolved": "https://registry.npmjs.org/lru-cache/-/lru-cache-6.0.0.tgz", + "integrity": "sha512-Jo6dJ04CmSjuznwJSS3pUeWmd/H0ffTlkXXgwZi+eq1UCmqQwCh+eLsYOYCwY991i2Fah4h1BEMCx4qThGbsiA==", + "dev": true, + "requires": { + "yallist": "^4.0.0" + } + }, "semver": { "version": "7.3.7", "resolved": "https://registry.npmjs.org/semver/-/semver-7.3.7.tgz", @@ -11675,6 +13593,12 @@ "requires": { "lru-cache": "^6.0.0" } + }, + "yallist": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/yallist/-/yallist-4.0.0.tgz", + "integrity": "sha512-3wdGidZyq5PB084XLES5TpOSRA3wjXAlIWMhum2kRcv/41Sn2emQ0dycQW4uZXLejwKvg6EsvbdlVL+FYEct7A==", + "dev": true } } }, @@ -11688,9 +13612,9 @@ } }, "url-join": { - "version": "4.0.1", - "resolved": "https://registry.npmjs.org/url-join/-/url-join-4.0.1.tgz", - "integrity": "sha512-jk1+QP6ZJqyOiuEI9AEWQfju/nB2Pw466kbA0LEZljHwKeMgd9WrAEgEGxjPDD2+TNbbb37rTyhEfrCXfuKXnA==", + "version": "5.0.0", + "resolved": "https://registry.npmjs.org/url-join/-/url-join-5.0.0.tgz", + "integrity": "sha512-n2huDr9h9yzd6exQVnH/jU5mr+Pfx08LRXXZhkLLetAMESRj+anQsTAh940iMrIetKAmry9coFuZQ2jY8/p3WA==", "dev": true }, "url-parse-lax": { @@ -11709,9 +13633,9 @@ "dev": true }, "uuid": { - "version": "8.3.2", - "resolved": "https://registry.npmjs.org/uuid/-/uuid-8.3.2.tgz", - "integrity": "sha512-+NYs2QeMWy+GWFOEm9xnn6HCDp0l7QBD7ml8zLUmJ+93Q5NF0NocErnwkTkXVFNiX3/fpC6afS8Dhb/gz7R7eg==", + "version": "3.4.0", + "resolved": "https://registry.npmjs.org/uuid/-/uuid-3.4.0.tgz", + "integrity": "sha512-HjSDRw6gZE5JMggctHBcjVak08+KEVhSIiDzFnT9S9aegmp85S/bReBVTb4QTFaRNptJ9kuYaNhnbNEOkbKb/A==", "dev": true }, "v8-compile-cache": { @@ -11726,6 +13650,16 @@ "integrity": "sha512-wa7YjyUGfNZngI/vtK0UHAN+lgDCxBPCylVXGp0zu59Fz5aiGtNXaq3DhIov063MorB+VfufLh3JlF2KdTK3xg==", "dev": true }, + "vm2": { + "version": "3.9.9", + "resolved": "https://registry.npmjs.org/vm2/-/vm2-3.9.9.tgz", + "integrity": "sha512-xwTm7NLh/uOjARRBs8/95H0e8fT3Ukw5D/JJWhxMbhKzNh1Nu981jQKvkep9iKYNxzlVrdzD0mlBGkDKZWprlw==", + "dev": true, + "requires": { + "acorn": "^8.7.0", + "acorn-walk": "^8.2.0" + } + }, "vscode-oniguruma": { "version": "1.6.2", "resolved": "https://registry.npmjs.org/vscode-oniguruma/-/vscode-oniguruma-1.6.2.tgz", @@ -11807,44 +13741,71 @@ "dev": true }, "windows-release": { - "version": "4.0.0", - "resolved": "https://registry.npmjs.org/windows-release/-/windows-release-4.0.0.tgz", - "integrity": "sha512-OxmV4wzDKB1x7AZaZgXMVsdJ1qER1ed83ZrTYd5Bwq2HfJVg3DJS8nqlAG4sMoJ7mu8cuRmLEYyU13BKwctRAg==", + "version": "5.0.1", + "resolved": "https://registry.npmjs.org/windows-release/-/windows-release-5.0.1.tgz", + "integrity": "sha512-y1xFdFvdMiDXI3xiOhMbJwt1Y7dUxidha0CWPs1NgjZIjZANTcX7+7bMqNjuezhzb8s5JGEiBAbQjQQYYy7ulw==", "dev": true, "requires": { - "execa": "^4.0.2" + "execa": "^5.1.1" }, "dependencies": { "execa": { - "version": "4.1.0", - "resolved": "https://registry.npmjs.org/execa/-/execa-4.1.0.tgz", - "integrity": "sha512-j5W0//W7f8UxAn8hXVnwG8tLwdiUy4FJLcSupCg6maBYZDpyBvTApK7KyuI4bKj8KOh1r2YH+6ucuYtJv1bTZA==", + "version": "5.1.1", + "resolved": "https://registry.npmjs.org/execa/-/execa-5.1.1.tgz", + "integrity": "sha512-8uSpZZocAZRBAPIEINJj3Lo9HyGitllczc27Eh5YYojjMFMn8yHMDMaUHE2Jqfq05D/wucwI4JGURyXt1vchyg==", "dev": true, "requires": { - "cross-spawn": "^7.0.0", - "get-stream": "^5.0.0", - "human-signals": "^1.1.1", + "cross-spawn": "^7.0.3", + "get-stream": "^6.0.0", + "human-signals": "^2.1.0", "is-stream": "^2.0.0", "merge-stream": "^2.0.0", - "npm-run-path": "^4.0.0", - "onetime": "^5.1.0", - "signal-exit": "^3.0.2", + "npm-run-path": "^4.0.1", + "onetime": "^5.1.2", + "signal-exit": "^3.0.3", "strip-final-newline": "^2.0.0" } }, - "get-stream": { - "version": "5.2.0", - "resolved": "https://registry.npmjs.org/get-stream/-/get-stream-5.2.0.tgz", - "integrity": "sha512-nBF+F1rAZVCu/p7rjzgA+Yb4lfYXrpl7a6VmJrU8wF9I1CKvP/QwPNZHnOlwbTkY6dvtFIzFMSyQXbLoTQPRpA==", + "human-signals": { + "version": "2.1.0", + "resolved": "https://registry.npmjs.org/human-signals/-/human-signals-2.1.0.tgz", + "integrity": "sha512-B4FFZ6q/T2jhhksgkbEW3HBvWIfDW85snkQgawt07S7J5QXTk6BkNV+0yAeZrM5QpMAdYlocGoljn0sJ/WQkFw==", + "dev": true + }, + "is-stream": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/is-stream/-/is-stream-2.0.1.tgz", + "integrity": "sha512-hFoiJiTl63nn+kstHGBtewWSKnQLpyb155KHheA1l39uvtO9nWIop1p3udqPcUd/xbF1VLMO4n7OI6p7RbngDg==", + "dev": true + }, + "mimic-fn": { + "version": "2.1.0", + "resolved": "https://registry.npmjs.org/mimic-fn/-/mimic-fn-2.1.0.tgz", + "integrity": "sha512-OqbOk5oEQeAZ8WXWydlu9HJjz9WVdEIvamMCcXmuqUYjTknH/sqsWvhQ3vgwKFRR1HpjvNBKQ37nbJgYzGqGcg==", + "dev": true + }, + "npm-run-path": { + "version": "4.0.1", + "resolved": "https://registry.npmjs.org/npm-run-path/-/npm-run-path-4.0.1.tgz", + "integrity": "sha512-S48WzZW777zhNIrn7gxOlISNAqi9ZC/uQFnRdbeIHhZhCA6UqpkOT8T1G7BvfdgP4Er8gF4sUbaS0i7QvIfCWw==", "dev": true, "requires": { - "pump": "^3.0.0" + "path-key": "^3.0.0" } }, - "human-signals": { - "version": "1.1.1", - "resolved": "https://registry.npmjs.org/human-signals/-/human-signals-1.1.1.tgz", - "integrity": "sha512-SEQu7vl8KjNL2eoGBLF3+wAjpsNfA9XMlXAYj/3EdaNfAlxKthD1xjEQfGOUhllCGGJVNY34bRr6lPINhNjyZw==", + "onetime": { + "version": "5.1.2", + "resolved": "https://registry.npmjs.org/onetime/-/onetime-5.1.2.tgz", + "integrity": "sha512-kbpaSSGJTWdAY5KPVeMOKXSrPtr8C8C7wodJbcsd51jRnmD+GZu8Y0VoU6Dm5Z4vWr0Ig/1NKuWRKf7j5aaYSg==", + "dev": true, + "requires": { + "mimic-fn": "^2.1.0" + } + }, + "strip-final-newline": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/strip-final-newline/-/strip-final-newline-2.0.0.tgz", + "integrity": "sha512-BrpvfNAE3dcvq7ll3xVumzjKjZQ5tI1sEUIKr3Uoks0XUl45St3FlatVqef9prk4jRDzhW6WZg+3bk93y6pLjA==", "dev": true } } @@ -11856,9 +13817,9 @@ "dev": true }, "workerpool": { - "version": "6.2.0", - "resolved": "https://registry.npmjs.org/workerpool/-/workerpool-6.2.0.tgz", - "integrity": "sha512-Rsk5qQHJ9eowMH28Jwhe8HEbmdYDX4lwoMWshiCXugjtHqMD9ZbiqSDLxcsfdqsETPzVUtX5s1Z5kStiIM6l4A==", + "version": "6.2.1", + "resolved": "https://registry.npmjs.org/workerpool/-/workerpool-6.2.1.tgz", + "integrity": "sha512-ILEIE97kDZvF9Wb9f6h5aXK4swSlKGUcOEGiIYb2OOu/IrDU9iwj0fD//SsA6E5ibwJxpEvhullJY4Sl4GcpAw==", "dev": true }, "wrap-ansi": { @@ -11896,16 +13857,23 @@ "integrity": "sha512-PSNhEJDejZYV7h50BohL09Er9VaIefr2LMAf3OEmpCkjOi34eYyQYAXUTjEQtZJTKcF0E2UKTh+osDLsgNim9Q==", "dev": true }, + "xregexp": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/xregexp/-/xregexp-2.0.0.tgz", + "integrity": "sha1-UqY+VsoLhKfzpfPWGHLxJq16WUM=", + "dev": true + }, "y18n": { - "version": "5.0.8", - "resolved": "https://registry.npmjs.org/y18n/-/y18n-5.0.8.tgz", - "integrity": "sha512-0pfFzegeDWJHJIAmTLRP2DwHjdF5s7jo9tuztdQxAhINCdvS+3nGINqPd00AphqJR/0LhANUS6/+7SCb98YOfA==", + "version": "4.0.3", + "resolved": "https://registry.npmjs.org/y18n/-/y18n-4.0.3.tgz", + "integrity": "sha512-JKhqTOwSrqNA1NY5lSztJ1GrBiUodLMmIZuLiDaMRJ+itFd+ABVE8XBjOvIWL+rSqNDC74LCSFmlb/U4UZ4hJQ==", "dev": true }, "yallist": { - "version": "4.0.0", - "resolved": "https://registry.npmjs.org/yallist/-/yallist-4.0.0.tgz", - "integrity": "sha512-3wdGidZyq5PB084XLES5TpOSRA3wjXAlIWMhum2kRcv/41Sn2emQ0dycQW4uZXLejwKvg6EsvbdlVL+FYEct7A==" + "version": "3.1.1", + "resolved": "https://registry.npmjs.org/yallist/-/yallist-3.1.1.tgz", + "integrity": "sha512-a4UGQaWPH59mOXUYnAG2ewncQS4i4F43Tv3JoAM+s2VDAmS9NsK8GpDMLrCHPksFT7h3K6TOoUNn2pb7RoXx4g==", + "dev": true }, "yaml": { "version": "1.10.2", @@ -11914,32 +13882,40 @@ "dev": true }, "yargs": { - "version": "17.4.1", - "resolved": "https://registry.npmjs.org/yargs/-/yargs-17.4.1.tgz", - "integrity": "sha512-WSZD9jgobAg3ZKuCQZSa3g9QOJeCCqLoLAykiWgmXnDo9EPnn4RPf5qVTtzgOx66o6/oqhcA5tHtJXpG8pMt3g==", + "version": "15.4.1", + "resolved": "https://registry.npmjs.org/yargs/-/yargs-15.4.1.tgz", + "integrity": "sha512-aePbxDmcYW++PaqBsJ+HYUFwCdv4LVvdnhBy78E57PIor8/OVvhMrADFFEDh8DHDFRv/O9i3lPhsENjO7QX0+A==", "dev": true, "requires": { - "cliui": "^7.0.2", - "escalade": "^3.1.1", - "get-caller-file": "^2.0.5", + "cliui": "^6.0.0", + "decamelize": "^1.2.0", + "find-up": "^4.1.0", + "get-caller-file": "^2.0.1", "require-directory": "^2.1.1", - "string-width": "^4.2.3", - "y18n": "^5.0.5", - "yargs-parser": "^21.0.0" + "require-main-filename": "^2.0.0", + "set-blocking": "^2.0.0", + "string-width": "^4.2.0", + "which-module": "^2.0.0", + "y18n": "^4.0.0", + "yargs-parser": "^18.1.2" }, "dependencies": { "yargs-parser": { - "version": "21.0.1", - "resolved": "https://registry.npmjs.org/yargs-parser/-/yargs-parser-21.0.1.tgz", - "integrity": "sha512-9BK1jFpLzJROCI5TzwZL/TU4gqjK5xiHV/RfWLOahrjAko/e4DJkRDZQXfvqAsiZzzYhgAzbgz6lg48jcm4GLg==", - "dev": true + "version": "18.1.3", + "resolved": "https://registry.npmjs.org/yargs-parser/-/yargs-parser-18.1.3.tgz", + "integrity": "sha512-o50j0JeToy/4K6OZcaQmW6lyXXKhq7csREXcDwk2omFPJEwUNOVtJKvmDr9EI1fAJZUyZcRF7kxGBWmRXudrCQ==", + "dev": true, + "requires": { + "camelcase": "^5.0.0", + "decamelize": "^1.2.0" + } } } }, "yargs-parser": { - "version": "20.2.9", - "resolved": "https://registry.npmjs.org/yargs-parser/-/yargs-parser-20.2.9.tgz", - "integrity": "sha512-y11nGElTIV+CT3Zv9t7VKl+Q3hTQoT9a1Qzezhhl6Rp21gJ/IVTW7Z3y9EWXhuUBC2Shnf+DX0antecpAwSP8w==", + "version": "21.0.1", + "resolved": "https://registry.npmjs.org/yargs-parser/-/yargs-parser-21.0.1.tgz", + "integrity": "sha512-9BK1jFpLzJROCI5TzwZL/TU4gqjK5xiHV/RfWLOahrjAko/e4DJkRDZQXfvqAsiZzzYhgAzbgz6lg48jcm4GLg==", "dev": true }, "yargs-unparser": { @@ -11954,6 +13930,12 @@ "is-plain-obj": "^2.1.0" }, "dependencies": { + "camelcase": { + "version": "6.3.0", + "resolved": "https://registry.npmjs.org/camelcase/-/camelcase-6.3.0.tgz", + "integrity": "sha512-Gmy6FhYlCY7uOElZUSbxo2UCDH8owEk996gkbrpsgGtrJLM3J7jGxl9Ic7Qwwj4ivOE5AWZWRMecDdF7hqGjFA==", + "dev": true + }, "decamelize": { "version": "4.0.0", "resolved": "https://registry.npmjs.org/decamelize/-/decamelize-4.0.0.tgz", diff --git a/package.json b/package.json index 7b834d0ee62..4ebce5d2b49 100644 --- a/package.json +++ b/package.json @@ -23,18 +23,18 @@ "gh-pages": "gh-pages -d ./documentation -e ./documentation -u 'documentation-bot '" }, "dependencies": { - "@node-redis/bloom": "1.0.1", - "@node-redis/client": "1.0.6", - "@node-redis/graph": "1.0.0", - "@node-redis/json": "1.0.2", - "@node-redis/search": "1.0.5", - "@node-redis/time-series": "1.0.2" + "@redis/bloom": "1.0.1", + "@redis/client": "1.0.6", + "@redis/graph": "1.0.0", + "@redis/json": "1.0.2", + "@redis/search": "1.0.5", + "@redis/time-series": "1.0.2" }, "devDependencies": { - "@tsconfig/node12": "^1.0.9", + "@tsconfig/node14": "^1.0.1", "gh-pages": "^3.2.3", - "release-it": "^14.14.2", - "typescript": "^4.6.3" + "release-it": "^15.0.0", + "typescript": "^4.6.4" }, "repository": { "type": "git", diff --git a/packages/bloom/README.md b/packages/bloom/README.md index b4dba2a6cca..8eb1445d188 100644 --- a/packages/bloom/README.md +++ b/packages/bloom/README.md @@ -1,4 +1,4 @@ -# @node-redis/bloom +# @redis/bloom This package provides support for the [RedisBloom](https://redisbloom.io) module, which adds additional probabilistic data structures to Redis. It extends the [Node Redis client](https://github.com/redis/node-redis) to include functions for each of the RediBloom commands. diff --git a/packages/bloom/lib/commands/bloom/ADD.ts b/packages/bloom/lib/commands/bloom/ADD.ts index 83dbc23c111..d8938f4c2b0 100644 --- a/packages/bloom/lib/commands/bloom/ADD.ts +++ b/packages/bloom/lib/commands/bloom/ADD.ts @@ -4,4 +4,4 @@ export function transformArguments(key: string, item: string): Array { return ['BF.ADD', key, item]; } -export { transformBooleanReply as transformReply } from '@node-redis/client/dist/lib/commands/generic-transformers'; +export { transformBooleanReply as transformReply } from '@redis/client/dist/lib/commands/generic-transformers'; diff --git a/packages/bloom/lib/commands/bloom/EXISTS.ts b/packages/bloom/lib/commands/bloom/EXISTS.ts index 2f06e60b9b5..d044207e244 100644 --- a/packages/bloom/lib/commands/bloom/EXISTS.ts +++ b/packages/bloom/lib/commands/bloom/EXISTS.ts @@ -6,4 +6,4 @@ export function transformArguments(key: string, item: string): Array { return ['BF.EXISTS', key, item]; } -export { transformBooleanReply as transformReply } from '@node-redis/client/dist/lib/commands/generic-transformers'; +export { transformBooleanReply as transformReply } from '@redis/client/dist/lib/commands/generic-transformers'; diff --git a/packages/bloom/lib/commands/bloom/INSERT.ts b/packages/bloom/lib/commands/bloom/INSERT.ts index 6bdb5fdf918..59fe1dabbdc 100644 --- a/packages/bloom/lib/commands/bloom/INSERT.ts +++ b/packages/bloom/lib/commands/bloom/INSERT.ts @@ -1,4 +1,4 @@ -import { pushVerdictArguments } from '@node-redis/client/dist/lib/commands/generic-transformers'; +import { pushVerdictArguments } from '@redis/client/dist/lib/commands/generic-transformers'; export const FIRST_KEY_INDEX = 1; @@ -43,4 +43,4 @@ export function transformArguments( return args; } -export { transformBooleanArrayReply as transformReply } from '@node-redis/client/dist/lib/commands/generic-transformers'; +export { transformBooleanArrayReply as transformReply } from '@redis/client/dist/lib/commands/generic-transformers'; diff --git a/packages/bloom/lib/commands/bloom/LOADCHUNK.ts b/packages/bloom/lib/commands/bloom/LOADCHUNK.ts index b9c486c73c5..491f572a49e 100644 --- a/packages/bloom/lib/commands/bloom/LOADCHUNK.ts +++ b/packages/bloom/lib/commands/bloom/LOADCHUNK.ts @@ -1,4 +1,4 @@ -import { RedisCommandArgument, RedisCommandArguments } from '@node-redis/client/dist/lib/commands'; +import { RedisCommandArgument, RedisCommandArguments } from '@redis/client/dist/lib/commands'; export const FIRST_KEY_INDEX = 1; diff --git a/packages/bloom/lib/commands/bloom/MADD.ts b/packages/bloom/lib/commands/bloom/MADD.ts index 7a81a3a08a5..056c4a1c1c2 100644 --- a/packages/bloom/lib/commands/bloom/MADD.ts +++ b/packages/bloom/lib/commands/bloom/MADD.ts @@ -4,4 +4,4 @@ export function transformArguments(key: string, items: Array): Array): Array { return ['CF.ADD', key, item]; } -export { transformBooleanReply as transformReply } from '@node-redis/client/dist/lib/commands/generic-transformers'; +export { transformBooleanReply as transformReply } from '@redis/client/dist/lib/commands/generic-transformers'; diff --git a/packages/bloom/lib/commands/cuckoo/ADDNX.ts b/packages/bloom/lib/commands/cuckoo/ADDNX.ts index 238d156700c..789003a3a57 100644 --- a/packages/bloom/lib/commands/cuckoo/ADDNX.ts +++ b/packages/bloom/lib/commands/cuckoo/ADDNX.ts @@ -4,4 +4,4 @@ export function transformArguments(key: string, item: string): Array { return ['CF.ADDNX', key, item]; } -export { transformBooleanReply as transformReply } from '@node-redis/client/dist/lib/commands/generic-transformers'; +export { transformBooleanReply as transformReply } from '@redis/client/dist/lib/commands/generic-transformers'; diff --git a/packages/bloom/lib/commands/cuckoo/DEL.ts b/packages/bloom/lib/commands/cuckoo/DEL.ts index d621bd0a0f3..1c395a515a8 100644 --- a/packages/bloom/lib/commands/cuckoo/DEL.ts +++ b/packages/bloom/lib/commands/cuckoo/DEL.ts @@ -4,4 +4,4 @@ export function transformArguments(key: string, item: string): Array { return ['CF.DEL', key, item]; } -export { transformBooleanReply as transformReply } from '@node-redis/client/dist/lib/commands/generic-transformers'; +export { transformBooleanReply as transformReply } from '@redis/client/dist/lib/commands/generic-transformers'; diff --git a/packages/bloom/lib/commands/cuckoo/EXISTS.ts b/packages/bloom/lib/commands/cuckoo/EXISTS.ts index 0a43ae55ff4..b50a1e25a87 100644 --- a/packages/bloom/lib/commands/cuckoo/EXISTS.ts +++ b/packages/bloom/lib/commands/cuckoo/EXISTS.ts @@ -6,4 +6,4 @@ export function transformArguments(key: string, item: string): Array { return ['CF.EXISTS', key, item]; } -export { transformBooleanReply as transformReply } from '@node-redis/client/dist/lib/commands/generic-transformers'; +export { transformBooleanReply as transformReply } from '@redis/client/dist/lib/commands/generic-transformers'; diff --git a/packages/bloom/lib/commands/cuckoo/INSERT.ts b/packages/bloom/lib/commands/cuckoo/INSERT.ts index c2e56b5c35c..bcfd4f13a88 100644 --- a/packages/bloom/lib/commands/cuckoo/INSERT.ts +++ b/packages/bloom/lib/commands/cuckoo/INSERT.ts @@ -1,4 +1,4 @@ -import { RedisCommandArguments } from '@node-redis/client/dist/lib/commands'; +import { RedisCommandArguments } from '@redis/client/dist/lib/commands'; import { InsertOptions, pushInsertOptions } from "."; export const FIRST_KEY_INDEX = 1; @@ -15,4 +15,4 @@ export function transformArguments( ); } -export { transformBooleanArrayReply as transformReply } from '@node-redis/client/dist/lib/commands/generic-transformers'; +export { transformBooleanArrayReply as transformReply } from '@redis/client/dist/lib/commands/generic-transformers'; diff --git a/packages/bloom/lib/commands/cuckoo/INSERTNX.ts b/packages/bloom/lib/commands/cuckoo/INSERTNX.ts index e7104ea4b17..17009e35a42 100644 --- a/packages/bloom/lib/commands/cuckoo/INSERTNX.ts +++ b/packages/bloom/lib/commands/cuckoo/INSERTNX.ts @@ -1,4 +1,4 @@ -import { RedisCommandArguments } from '@node-redis/client/dist/lib/commands'; +import { RedisCommandArguments } from '@redis/client/dist/lib/commands'; import { InsertOptions, pushInsertOptions } from "."; export const FIRST_KEY_INDEX = 1; @@ -15,4 +15,4 @@ export function transformArguments( ); } -export { transformBooleanArrayReply as transformReply } from '@node-redis/client/dist/lib/commands/generic-transformers'; +export { transformBooleanArrayReply as transformReply } from '@redis/client/dist/lib/commands/generic-transformers'; diff --git a/packages/bloom/lib/commands/cuckoo/LOADCHUNK.ts b/packages/bloom/lib/commands/cuckoo/LOADCHUNK.ts index 5b739f67ccd..6d960c014e2 100644 --- a/packages/bloom/lib/commands/cuckoo/LOADCHUNK.ts +++ b/packages/bloom/lib/commands/cuckoo/LOADCHUNK.ts @@ -1,4 +1,4 @@ -import { RedisCommandArgument, RedisCommandArguments } from '@node-redis/client/dist/lib/commands'; +import { RedisCommandArgument, RedisCommandArguments } from '@redis/client/dist/lib/commands'; export const FIRST_KEY_INDEX = 1; diff --git a/packages/bloom/lib/commands/cuckoo/index.ts b/packages/bloom/lib/commands/cuckoo/index.ts index 30fa9609992..96b4453bc39 100644 --- a/packages/bloom/lib/commands/cuckoo/index.ts +++ b/packages/bloom/lib/commands/cuckoo/index.ts @@ -10,8 +10,8 @@ import * as INSERTNX from './INSERTNX'; import * as LOADCHUNK from './LOADCHUNK'; import * as RESERVE from './RESERVE'; import * as SCANDUMP from './SCANDUMP'; -import { pushVerdictArguments } from '@node-redis/client/dist/lib/commands/generic-transformers'; -import { RedisCommandArguments } from '@node-redis/client/dist/lib/commands'; +import { pushVerdictArguments } from '@redis/client/dist/lib/commands/generic-transformers'; +import { RedisCommandArguments } from '@redis/client/dist/lib/commands'; export default { ADD, diff --git a/packages/bloom/lib/commands/top-k/ADD.ts b/packages/bloom/lib/commands/top-k/ADD.ts index 250b75ae045..beee3a2206c 100644 --- a/packages/bloom/lib/commands/top-k/ADD.ts +++ b/packages/bloom/lib/commands/top-k/ADD.ts @@ -1,5 +1,5 @@ -import { RedisCommandArguments } from '@node-redis/client/dist/lib/commands'; -import { pushVerdictArguments } from '@node-redis/client/dist/lib/commands/generic-transformers'; +import { RedisCommandArguments } from '@redis/client/dist/lib/commands'; +import { pushVerdictArguments } from '@redis/client/dist/lib/commands/generic-transformers'; export const FIRST_KEY_INDEX = 1; diff --git a/packages/bloom/lib/commands/top-k/COUNT.ts b/packages/bloom/lib/commands/top-k/COUNT.ts index 854d50d8973..fc8cf557dca 100644 --- a/packages/bloom/lib/commands/top-k/COUNT.ts +++ b/packages/bloom/lib/commands/top-k/COUNT.ts @@ -1,5 +1,5 @@ -import { RedisCommandArguments } from '@node-redis/client/dist/lib/commands'; -import { pushVerdictArguments } from '@node-redis/client/dist/lib/commands/generic-transformers'; +import { RedisCommandArguments } from '@redis/client/dist/lib/commands'; +import { pushVerdictArguments } from '@redis/client/dist/lib/commands/generic-transformers'; export const FIRST_KEY_INDEX = 1; diff --git a/packages/bloom/lib/commands/top-k/QUERY.ts b/packages/bloom/lib/commands/top-k/QUERY.ts index 7b261f35b80..94943a26fd7 100644 --- a/packages/bloom/lib/commands/top-k/QUERY.ts +++ b/packages/bloom/lib/commands/top-k/QUERY.ts @@ -1,5 +1,5 @@ -import { RedisCommandArguments } from '@node-redis/client/dist/lib/commands'; -import { pushVerdictArguments } from '@node-redis/client/dist/lib/commands/generic-transformers'; +import { RedisCommandArguments } from '@redis/client/dist/lib/commands'; +import { pushVerdictArguments } from '@redis/client/dist/lib/commands/generic-transformers'; export const FIRST_KEY_INDEX = 1; diff --git a/packages/bloom/lib/test-utils.ts b/packages/bloom/lib/test-utils.ts index 7c5780a3cd4..4bcebad93ae 100644 --- a/packages/bloom/lib/test-utils.ts +++ b/packages/bloom/lib/test-utils.ts @@ -1,4 +1,4 @@ -import TestUtils from '@node-redis/test-utils'; +import TestUtils from '@redis/test-utils'; import RedisBloomModules from '.'; export default new TestUtils({ diff --git a/packages/bloom/package.json b/packages/bloom/package.json index 8a2ea2858a3..bfdda92d793 100644 --- a/packages/bloom/package.json +++ b/packages/bloom/package.json @@ -1,5 +1,5 @@ { - "name": "@node-redis/bloom", + "name": "@redis/bloom", "version": "1.0.1", "license": "MIT", "main": "./dist/index.js", @@ -13,17 +13,17 @@ "documentation": "typedoc" }, "peerDependencies": { - "@node-redis/client": "^1.0.0" + "@redis/client": "^1.0.0" }, "devDependencies": { "@istanbuljs/nyc-config-typescript": "^1.0.2", - "@node-redis/test-utils": "*", - "@types/node": "^17.0.26", + "@redis/test-utils": "*", + "@types/node": "^17.0.31", "nyc": "^15.1.0", - "release-it": "^14.14.2", + "release-it": "^15.0.0", "source-map-support": "^0.5.21", "ts-node": "^10.7.0", "typedoc": "^0.22.15", - "typescript": "^4.6.3" + "typescript": "^4.6.4" } } diff --git a/packages/client/README.md b/packages/client/README.md index 63b31402292..4b5d15088fa 100644 --- a/packages/client/README.md +++ b/packages/client/README.md @@ -1,3 +1,3 @@ -# @node-redis/client +# @redis/client The source code and documentation for this package are in the main [node-redis](https://github.com/redis/node-redis) repo. diff --git a/packages/client/lib/client/commands-queue.ts b/packages/client/lib/client/commands-queue.ts index 8cae914963e..f951cd6f845 100644 --- a/packages/client/lib/client/commands-queue.ts +++ b/packages/client/lib/client/commands-queue.ts @@ -347,10 +347,6 @@ export default class RedisCommandsQueue { return encoded; } - rejectLastCommand(err: unknown): void { - this.#waitingForReply.pop()!.reject(err); - } - onReplyChunk(chunk: Buffer): void { this.#decoder.write(chunk); } diff --git a/packages/client/lib/client/index.spec.ts b/packages/client/lib/client/index.spec.ts index 349a567a304..0b5fd05b9b2 100644 --- a/packages/client/lib/client/index.spec.ts +++ b/packages/client/lib/client/index.spec.ts @@ -29,6 +29,7 @@ export const MATH_FUNCTION = { library: { square: { NAME: 'square', + IS_READ_ONLY: true, NUMBER_OF_KEYS: 0, transformArguments(number: number): Array { return [number.toString()]; @@ -722,8 +723,11 @@ describe('Client', () => { await subscriber.connect(); try { - const listener = spy(); - await subscriber.subscribe('channel', listener); + const channelListener = spy(); + await subscriber.subscribe('channel', channelListener); + + const patternListener = spy(); + await subscriber.pSubscribe('channe*', patternListener); await Promise.all([ once(subscriber, 'error'), @@ -736,7 +740,8 @@ describe('Client', () => { await once(subscriber, 'ready'); await Promise.all([ - waitTillBeenCalled(listener), + waitTillBeenCalled(channelListener), + waitTillBeenCalled(patternListener), publisher.publish('channel', 'message') ]); } finally { diff --git a/packages/client/lib/client/index.ts b/packages/client/lib/client/index.ts index e78a491cc17..836f7908416 100644 --- a/packages/client/lib/client/index.ts +++ b/packages/client/lib/client/index.ts @@ -399,23 +399,25 @@ export default class RedisClient< async functionsExecuter( fn: F, - args: Array + args: Array, + name: string ): Promise> { const { args: redisArgs, options } = transformCommandArguments(fn, args); return transformCommandReply( fn, - await this.executeFunction(fn, redisArgs, options), + await this.executeFunction(name, fn, redisArgs, options), redisArgs.preserve ); } executeFunction( + name: string, fn: RedisFunction, args: RedisCommandArguments, options?: ClientCommandOptions ): Promise { return this.#sendCommand( - fCallArguments(fn, args), + fCallArguments(name, fn, args), options ); } diff --git a/packages/client/lib/client/multi-command.ts b/packages/client/lib/client/multi-command.ts index 8fda702a317..2eea429abec 100644 --- a/packages/client/lib/client/multi-command.ts +++ b/packages/client/lib/client/multi-command.ts @@ -141,8 +141,8 @@ export default class RedisClientMultiCommand { return this; } - functionsExecutor(fn: RedisFunction, args: Array): this { - this.#multi.addFunction(fn, args); + functionsExecutor(fn: RedisFunction, args: Array, name: string): this { + this.#multi.addFunction(name, fn, args); return this; } diff --git a/packages/client/lib/cluster/index.ts b/packages/client/lib/cluster/index.ts index a4e16f36719..f1a4cb42af3 100644 --- a/packages/client/lib/cluster/index.ts +++ b/packages/client/lib/cluster/index.ts @@ -124,12 +124,14 @@ export default class RedisCluster< async functionsExecutor( fn: F, - args: Array + args: Array, + name: string, ): Promise> { const { args: redisArgs, options } = transformCommandArguments(fn, args); return transformCommandReply( fn, await this.executeFunction( + name, fn, args, redisArgs, @@ -140,6 +142,7 @@ export default class RedisCluster< } async executeFunction( + name: string, fn: RedisFunction, originalArgs: Array, redisArgs: RedisCommandArguments, @@ -148,7 +151,7 @@ export default class RedisCluster< return this.#execute( RedisCluster.extractFirstKey(fn, originalArgs, redisArgs), fn.IS_READ_ONLY, - client => client.executeFunction(fn, redisArgs, options) + client => client.executeFunction(name, fn, redisArgs, options) ); } diff --git a/packages/client/lib/cluster/multi-command.ts b/packages/client/lib/cluster/multi-command.ts index 0bcf106e6c9..52ab6eb0e23 100644 --- a/packages/client/lib/cluster/multi-command.ts +++ b/packages/client/lib/cluster/multi-command.ts @@ -103,8 +103,8 @@ export default class RedisClusterMultiCommand { return this; } - functionsExecutor(fn: RedisFunction, args: Array): this { - const transformedArguments = this.#multi.addFunction(fn, args); + functionsExecutor(fn: RedisFunction, args: Array, name: string): this { + const transformedArguments = this.#multi.addFunction(name, fn, args); this.#firstKey ??= RedisCluster.extractFirstKey(fn, args, transformedArguments); return this; } diff --git a/packages/client/lib/commander.ts b/packages/client/lib/commander.ts index f8cb74c3135..3ab8b997d93 100644 --- a/packages/client/lib/commander.ts +++ b/packages/client/lib/commander.ts @@ -1,11 +1,11 @@ import { CommandOptions, isCommandOptions } from './command-options'; -import { RedisCommand, RedisCommandArguments, RedisCommandReply, RedisFunction, RedisFunctions, RedisModules, RedisScript, RedisScripts } from './commands'; +import { RedisCommand, RedisCommandArgument, RedisCommandArguments, RedisCommandReply, RedisFunction, RedisFunctions, RedisModules, RedisScript, RedisScripts } from './commands'; type Instantiable = new (...args: Array) => T; type CommandsExecutor = - (command: C, args: Array) => unknown; + (command: C, args: Array, name: string) => unknown; interface AttachCommandsConfig { BaseClass: Instantiable; @@ -20,7 +20,7 @@ export function attachCommands({ }: AttachCommandsConfig): void { for (const [name, command] of Object.entries(commands)) { BaseClass.prototype[name] = function (...args: Array): unknown { - return executor.call(this, command, args); + return executor.call(this, command, args, name); }; } } @@ -95,7 +95,7 @@ function attachWithNamespaces({ Commander.prototype[namespace] = {}; for (const [name, command] of Object.entries(commands)) { Commander.prototype[namespace][name] = function (...args: Array): unknown { - return executor.call(this.self, command, args); + return executor.call(this.self, command, args, name); }; } } @@ -138,10 +138,14 @@ export function transformCommandReply( return command.transformReply(rawReply, preserved); } -export function fCallArguments(fn: RedisFunction, args: RedisCommandArguments): RedisCommandArguments { +export function fCallArguments( + name: RedisCommandArgument, + fn: RedisFunction, + args: RedisCommandArguments +): RedisCommandArguments { const actualArgs: RedisCommandArguments = [ fn.IS_READ_ONLY ? 'FCALL_RO' : 'FCALL', - fn.NAME + name ]; if (fn.NUMBER_OF_KEYS !== undefined) { diff --git a/packages/client/lib/commands/index.ts b/packages/client/lib/commands/index.ts index 84043bcd63d..5e62ea20de2 100644 --- a/packages/client/lib/commands/index.ts +++ b/packages/client/lib/commands/index.ts @@ -60,7 +60,6 @@ export interface RedisModules { } export interface RedisFunction extends RedisCommand { - NAME: string; NUMBER_OF_KEYS?: number; } diff --git a/packages/client/lib/multi-command.ts b/packages/client/lib/multi-command.ts index 10ab77ad19b..8865cc8e002 100644 --- a/packages/client/lib/multi-command.ts +++ b/packages/client/lib/multi-command.ts @@ -23,8 +23,9 @@ export default class RedisMultiCommand { }); } - addFunction(fn: RedisFunction, args: Array): RedisCommandArguments { + addFunction(name: string, fn: RedisFunction, args: Array): RedisCommandArguments { const transformedArguments = fCallArguments( + name, fn, fn.transformArguments(...args) ); diff --git a/packages/client/lib/test-utils.ts b/packages/client/lib/test-utils.ts index 1ea7b590076..57903321484 100644 --- a/packages/client/lib/test-utils.ts +++ b/packages/client/lib/test-utils.ts @@ -1,4 +1,4 @@ -import TestUtils from '@node-redis/test-utils'; +import TestUtils from '@redis/test-utils'; import { SinonSpy } from 'sinon'; import { promiseTimeout } from './utils'; diff --git a/packages/client/package.json b/packages/client/package.json index 9d70d4f93e1..b2cd6996484 100644 --- a/packages/client/package.json +++ b/packages/client/package.json @@ -1,5 +1,5 @@ { - "name": "@node-redis/client", + "name": "@redis/client", "version": "1.0.5", "license": "MIT", "main": "./dist/index.js", @@ -20,23 +20,23 @@ }, "devDependencies": { "@istanbuljs/nyc-config-typescript": "^1.0.2", - "@node-redis/test-utils": "*", - "@types/node": "^17.0.26", + "@redis/test-utils": "*", + "@types/node": "^17.0.31", "@types/sinon": "^10.0.11", "@types/yallist": "^4.0.1", - "@typescript-eslint/eslint-plugin": "^5.20.0", - "@typescript-eslint/parser": "^5.20.0", + "@typescript-eslint/eslint-plugin": "^5.21.0", + "@typescript-eslint/parser": "^5.21.0", "eslint": "^8.14.0", "nyc": "^15.1.0", - "release-it": "^14.14.2", + "release-it": "^15.0.0", "sinon": "^13.0.2", "source-map-support": "^0.5.21", "ts-node": "^10.7.0", "typedoc": "^0.22.15", - "typescript": "^4.6.3" + "typescript": "^4.6.4" }, "engines": { - "node": ">=12" + "node": ">=14" }, "repository": { "type": "git", diff --git a/packages/graph/README.md b/packages/graph/README.md index 2313f474d5a..420d18851d7 100644 --- a/packages/graph/README.md +++ b/packages/graph/README.md @@ -1 +1 @@ -# @node-redis/graph +# @redis/graph diff --git a/packages/graph/lib/commands/QUERY.ts b/packages/graph/lib/commands/QUERY.ts index 06436e5e74d..408443186d5 100644 --- a/packages/graph/lib/commands/QUERY.ts +++ b/packages/graph/lib/commands/QUERY.ts @@ -1,4 +1,4 @@ -import { RedisCommandArgument, RedisCommandArguments } from '@node-redis/client/dist/lib/commands/index'; +import { RedisCommandArgument, RedisCommandArguments } from '@redis/client/dist/lib/commands/index'; import { pushQueryArguments } from '.'; export const FIRST_KEY_INDEX = 1; diff --git a/packages/graph/lib/commands/QUERY_RO.ts b/packages/graph/lib/commands/QUERY_RO.ts index 9da471adcce..2090f593c72 100644 --- a/packages/graph/lib/commands/QUERY_RO.ts +++ b/packages/graph/lib/commands/QUERY_RO.ts @@ -1,4 +1,4 @@ -import { RedisCommandArgument, RedisCommandArguments } from '@node-redis/client/dist/lib/commands'; +import { RedisCommandArgument, RedisCommandArguments } from '@redis/client/dist/lib/commands'; import { pushQueryArguments } from '.'; export { FIRST_KEY_INDEX } from './QUERY'; diff --git a/packages/graph/lib/commands/index.ts b/packages/graph/lib/commands/index.ts index 0b6180e1752..afc025e68cf 100644 --- a/packages/graph/lib/commands/index.ts +++ b/packages/graph/lib/commands/index.ts @@ -7,7 +7,7 @@ import * as PROFILE from './PROFILE'; import * as QUERY_RO from './QUERY_RO'; import * as QUERY from './QUERY'; import * as SLOWLOG from './SLOWLOG'; -import { RedisCommandArgument, RedisCommandArguments } from '@node-redis/client/dist/lib/commands'; +import { RedisCommandArgument, RedisCommandArguments } from '@redis/client/dist/lib/commands'; export default { CONFIG_GET, diff --git a/packages/graph/lib/test-utils.ts b/packages/graph/lib/test-utils.ts index da4fb0f78d9..161285d957c 100644 --- a/packages/graph/lib/test-utils.ts +++ b/packages/graph/lib/test-utils.ts @@ -1,4 +1,4 @@ -import TestUtils from '@node-redis/test-utils'; +import TestUtils from '@redis/test-utils'; import RedisGraph from '.'; export default new TestUtils({ diff --git a/packages/graph/package.json b/packages/graph/package.json index dd0db983c24..a813d11434f 100644 --- a/packages/graph/package.json +++ b/packages/graph/package.json @@ -1,5 +1,5 @@ { - "name": "@node-redis/graph", + "name": "@redis/graph", "version": "1.0.0", "license": "MIT", "main": "./dist/index.js", @@ -13,17 +13,17 @@ "documentation": "typedoc" }, "peerDependencies": { - "@node-redis/client": "^1.0.0" + "@redis/client": "^1.0.0" }, "devDependencies": { "@istanbuljs/nyc-config-typescript": "^1.0.2", - "@node-redis/test-utils": "*", - "@types/node": "^17.0.26", + "@redis/test-utils": "*", + "@types/node": "^17.0.31", "nyc": "^15.1.0", - "release-it": "^14.14.2", + "release-it": "^15.0.0", "source-map-support": "^0.5.21", "ts-node": "^10.7.0", "typedoc": "^0.22.15", - "typescript": "^4.6.3" + "typescript": "^4.6.4" } } diff --git a/packages/json/README.md b/packages/json/README.md index 5b6d5ba8ce4..adc125eb980 100644 --- a/packages/json/README.md +++ b/packages/json/README.md @@ -1,4 +1,4 @@ -# @node-redis/json +# @redis/json 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. diff --git a/packages/json/lib/commands/GET.ts b/packages/json/lib/commands/GET.ts index 8e897bb67d2..36bb9bc4e4c 100644 --- a/packages/json/lib/commands/GET.ts +++ b/packages/json/lib/commands/GET.ts @@ -1,4 +1,4 @@ -import { pushVerdictArguments } from '@node-redis/client/dist/lib/commands/generic-transformers'; +import { pushVerdictArguments } from '@redis/client/dist/lib/commands/generic-transformers'; export const FIRST_KEY_INDEX = 1; diff --git a/packages/json/lib/test-utils.ts b/packages/json/lib/test-utils.ts index a4d5bee7e9d..c809b945773 100644 --- a/packages/json/lib/test-utils.ts +++ b/packages/json/lib/test-utils.ts @@ -1,4 +1,4 @@ -import TestUtils from '@node-redis/test-utils'; +import TestUtils from '@redis/test-utils'; import RedisJSON from '.'; export default new TestUtils({ diff --git a/packages/json/package.json b/packages/json/package.json index b53a9823737..8fc11235b3f 100644 --- a/packages/json/package.json +++ b/packages/json/package.json @@ -1,5 +1,5 @@ { - "name": "@node-redis/json", + "name": "@redis/json", "version": "1.0.2", "license": "MIT", "main": "./dist/index.js", @@ -13,17 +13,17 @@ "documentation": "typedoc" }, "peerDependencies": { - "@node-redis/client": "^1.0.0" + "@redis/client": "^1.0.0" }, "devDependencies": { "@istanbuljs/nyc-config-typescript": "^1.0.2", - "@node-redis/test-utils": "*", - "@types/node": "^17.0.26", + "@redis/test-utils": "*", + "@types/node": "^17.0.31", "nyc": "^15.1.0", - "release-it": "^14.14.2", + "release-it": "^15.0.0", "source-map-support": "^0.5.21", "ts-node": "^10.7.0", "typedoc": "^0.22.15", - "typescript": "^4.6.3" + "typescript": "^4.6.4" } } diff --git a/packages/search/README.md b/packages/search/README.md index f54316d3c18..e4b05fe7cfc 100644 --- a/packages/search/README.md +++ b/packages/search/README.md @@ -1,4 +1,4 @@ -# @node-redis/search +# @redis/search 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. diff --git a/packages/search/lib/commands/AGGREGATE.ts b/packages/search/lib/commands/AGGREGATE.ts index cc7e9cba40b..72d814cc030 100644 --- a/packages/search/lib/commands/AGGREGATE.ts +++ b/packages/search/lib/commands/AGGREGATE.ts @@ -1,5 +1,5 @@ -import { RedisCommandArgument, RedisCommandArguments } from '@node-redis/client/dist/lib/commands'; -import { pushVerdictArgument, transformTuplesReply } from '@node-redis/client/dist/lib/commands/generic-transformers'; +import { RedisCommandArgument, RedisCommandArguments } from '@redis/client/dist/lib/commands'; +import { pushVerdictArgument, transformTuplesReply } from '@redis/client/dist/lib/commands/generic-transformers'; import { Params, PropertyName, pushArgumentsWithLength, pushParamsArgs, pushSortByArguments, SortByProperty } from '.'; export enum AggregateSteps { diff --git a/packages/search/lib/commands/CREATE.ts b/packages/search/lib/commands/CREATE.ts index 7578d94ede3..21662c28d7d 100644 --- a/packages/search/lib/commands/CREATE.ts +++ b/packages/search/lib/commands/CREATE.ts @@ -1,4 +1,4 @@ -import { pushOptionalVerdictArgument } from '@node-redis/client/dist/lib/commands/generic-transformers'; +import { pushOptionalVerdictArgument } from '@redis/client/dist/lib/commands/generic-transformers'; import { RedisSearchLanguages, PropertyName, RediSearchSchema, pushSchema } from '.'; interface CreateOptions { diff --git a/packages/search/lib/commands/DICTADD.ts b/packages/search/lib/commands/DICTADD.ts index b3f993395fa..60af11fd41f 100644 --- a/packages/search/lib/commands/DICTADD.ts +++ b/packages/search/lib/commands/DICTADD.ts @@ -1,5 +1,5 @@ -import { RedisCommandArguments } from '@node-redis/client/dist/lib/commands'; -import { pushVerdictArguments } from '@node-redis/client/dist/lib/commands/generic-transformers'; +import { RedisCommandArguments } from '@redis/client/dist/lib/commands'; +import { pushVerdictArguments } from '@redis/client/dist/lib/commands/generic-transformers'; export function transformArguments(dictionary: string, term: string | Array): RedisCommandArguments { return pushVerdictArguments(['FT.DICTADD', dictionary], term); diff --git a/packages/search/lib/commands/DICTDEL.ts b/packages/search/lib/commands/DICTDEL.ts index bd047a5031d..a1b728f1926 100644 --- a/packages/search/lib/commands/DICTDEL.ts +++ b/packages/search/lib/commands/DICTDEL.ts @@ -1,5 +1,5 @@ -import { RedisCommandArguments } from '@node-redis/client/dist/lib/commands'; -import { pushVerdictArguments } from '@node-redis/client/dist/lib/commands/generic-transformers'; +import { RedisCommandArguments } from '@redis/client/dist/lib/commands'; +import { pushVerdictArguments } from '@redis/client/dist/lib/commands/generic-transformers'; export function transformArguments(dictionary: string, term: string | Array): RedisCommandArguments { return pushVerdictArguments(['FT.DICTDEL', dictionary], term); diff --git a/packages/search/lib/commands/INFO.ts b/packages/search/lib/commands/INFO.ts index 74929fad423..269d12d51cf 100644 --- a/packages/search/lib/commands/INFO.ts +++ b/packages/search/lib/commands/INFO.ts @@ -1,5 +1,5 @@ -import { RedisCommandArgument } from '@node-redis/client/dist/lib/commands'; -import { transformTuplesReply } from '@node-redis/client/dist/lib/commands/generic-transformers'; +import { RedisCommandArgument } from '@redis/client/dist/lib/commands'; +import { transformTuplesReply } from '@redis/client/dist/lib/commands/generic-transformers'; export function transformArguments(index: string): Array { return ['FT.INFO', index]; diff --git a/packages/search/lib/commands/PROFILE_SEARCH.ts b/packages/search/lib/commands/PROFILE_SEARCH.ts index 6e7cd536e72..8040ccb5e05 100644 --- a/packages/search/lib/commands/PROFILE_SEARCH.ts +++ b/packages/search/lib/commands/PROFILE_SEARCH.ts @@ -1,6 +1,6 @@ import { SearchOptions, SearchRawReply, transformReply as transformSearchReply } from './SEARCH'; import { pushSearchOptions, ProfileOptions, ProfileRawReply, ProfileReply, transformProfile } from '.'; -import { RedisCommandArguments } from '@node-redis/client/dist/lib/commands'; +import { RedisCommandArguments } from '@redis/client/dist/lib/commands'; export const IS_READ_ONLY = true; diff --git a/packages/search/lib/commands/SEARCH.ts b/packages/search/lib/commands/SEARCH.ts index 3a9899975c3..4590997b24a 100644 --- a/packages/search/lib/commands/SEARCH.ts +++ b/packages/search/lib/commands/SEARCH.ts @@ -1,5 +1,5 @@ -import { RedisCommandArguments } from '@node-redis/client/dist/lib/commands'; -import { transformTuplesReply } from '@node-redis/client/dist/lib/commands/generic-transformers'; +import { RedisCommandArguments } from '@redis/client/dist/lib/commands'; +import { transformTuplesReply } from '@redis/client/dist/lib/commands/generic-transformers'; import { pushSearchOptions, RedisSearchLanguages, Params, PropertyName, SortByProperty, SearchReply } from '.'; export const FIRST_KEY_INDEX = 1; diff --git a/packages/search/lib/commands/SUGDEL.ts b/packages/search/lib/commands/SUGDEL.ts index c6b236f4963..b522acdfd47 100644 --- a/packages/search/lib/commands/SUGDEL.ts +++ b/packages/search/lib/commands/SUGDEL.ts @@ -2,4 +2,4 @@ export function transformArguments(key: string, string: string): Array { return ['FT.SUGDEL', key, string]; } -export { transformBooleanReply as transformReply } from '@node-redis/client/dist/lib/commands/generic-transformers'; +export { transformBooleanReply as transformReply } from '@redis/client/dist/lib/commands/generic-transformers'; diff --git a/packages/search/lib/commands/SYNUPDATE.ts b/packages/search/lib/commands/SYNUPDATE.ts index b59d35617fa..3384ea59d94 100644 --- a/packages/search/lib/commands/SYNUPDATE.ts +++ b/packages/search/lib/commands/SYNUPDATE.ts @@ -1,5 +1,5 @@ -import { pushVerdictArguments } from '@node-redis/client/dist/lib/commands/generic-transformers'; -import { RedisCommandArguments } from '@node-redis/client/dist/lib/commands'; +import { pushVerdictArguments } from '@redis/client/dist/lib/commands/generic-transformers'; +import { RedisCommandArguments } from '@redis/client/dist/lib/commands'; interface SynUpdateOptions { SKIPINITIALSCAN?: true; diff --git a/packages/search/lib/commands/index.ts b/packages/search/lib/commands/index.ts index 03ac0fe19f9..f977f39315f 100644 --- a/packages/search/lib/commands/index.ts +++ b/packages/search/lib/commands/index.ts @@ -28,8 +28,8 @@ import * as SUGLEN from './SUGLEN'; import * as SYNDUMP from './SYNDUMP'; import * as SYNUPDATE from './SYNUPDATE'; import * as TAGVALS from './TAGVALS'; -import { RedisCommandArgument, RedisCommandArguments } from '@node-redis/client/dist/lib/commands'; -import { pushOptionalVerdictArgument, pushVerdictArgument } from '@node-redis/client/dist/lib/commands/generic-transformers'; +import { RedisCommandArgument, RedisCommandArguments } from '@redis/client/dist/lib/commands'; +import { pushOptionalVerdictArgument, pushVerdictArgument } from '@redis/client/dist/lib/commands/generic-transformers'; import { SearchOptions } from './SEARCH'; export default { diff --git a/packages/search/lib/test-utils.ts b/packages/search/lib/test-utils.ts index 32649f729e0..1df5f32e76b 100644 --- a/packages/search/lib/test-utils.ts +++ b/packages/search/lib/test-utils.ts @@ -1,4 +1,4 @@ -import TestUtils from '@node-redis/test-utils'; +import TestUtils from '@redis/test-utils'; import RediSearch from '.'; export default new TestUtils({ diff --git a/packages/search/package.json b/packages/search/package.json index bba1382d1ab..8668ab94bb6 100644 --- a/packages/search/package.json +++ b/packages/search/package.json @@ -1,5 +1,5 @@ { - "name": "@node-redis/search", + "name": "@redis/search", "version": "1.0.5", "license": "MIT", "main": "./dist/index.js", @@ -13,17 +13,17 @@ "documentation": "typedoc" }, "peerDependencies": { - "@node-redis/client": "^1.0.0" + "@redis/client": "^1.0.0" }, "devDependencies": { "@istanbuljs/nyc-config-typescript": "^1.0.2", - "@node-redis/test-utils": "*", - "@types/node": "^17.0.26", + "@redis/test-utils": "*", + "@types/node": "^17.0.31", "nyc": "^15.1.0", - "release-it": "^14.14.2", + "release-it": "^15.0.0", "source-map-support": "^0.5.21", "ts-node": "^10.7.0", "typedoc": "^0.22.15", - "typescript": "^4.6.3" + "typescript": "^4.6.4" } } diff --git a/packages/test-utils/lib/dockers.ts b/packages/test-utils/lib/dockers.ts index ad803a1fde4..43b11b2bd2a 100644 --- a/packages/test-utils/lib/dockers.ts +++ b/packages/test-utils/lib/dockers.ts @@ -1,8 +1,8 @@ import { createConnection } from 'net'; import { once } from 'events'; -import { RedisModules, RedisFunctions, RedisScripts } from '@node-redis/client/lib/commands'; -import RedisClient, { RedisClientType } from '@node-redis/client/lib/client'; -import { promiseTimeout } from '@node-redis/client/lib/utils'; +import { RedisModules, RedisFunctions, RedisScripts } from '@redis/client/lib/commands'; +import RedisClient, { RedisClientType } from '@redis/client/lib/client'; +import { promiseTimeout } from '@redis/client/lib/utils'; import * as path from 'path'; import { promisify } from 'util'; import { exec } from 'child_process'; diff --git a/packages/test-utils/lib/index.ts b/packages/test-utils/lib/index.ts index 7a83f2ad2e5..ac7dad72b3b 100644 --- a/packages/test-utils/lib/index.ts +++ b/packages/test-utils/lib/index.ts @@ -1,6 +1,6 @@ -import { RedisModules, RedisFunctions, RedisScripts } from '@node-redis/client/lib/commands'; -import RedisClient, { RedisClientOptions, RedisClientType } from '@node-redis/client/lib/client'; -import RedisCluster, { RedisClusterOptions, RedisClusterType } from '@node-redis/client/lib/cluster'; +import { RedisModules, RedisFunctions, RedisScripts } from '@redis/client/lib/commands'; +import RedisClient, { RedisClientOptions, RedisClientType } from '@redis/client/lib/client'; +import RedisCluster, { RedisClusterOptions, RedisClusterType } from '@redis/client/lib/cluster'; import { RedisServerDockerConfig, spawnRedisServer, spawnRedisCluster } from './dockers'; import yargs from 'yargs'; import { hideBin } from 'yargs/helpers'; diff --git a/packages/test-utils/package.json b/packages/test-utils/package.json index e1c340ce2d5..5ccf685f541 100644 --- a/packages/test-utils/package.json +++ b/packages/test-utils/package.json @@ -1,5 +1,5 @@ { - "name": "@node-redis/test-utils", + "name": "@redis/test-utils", "private": true, "main": "./dist/index.js", "types": "./dist/index.d.ts", @@ -7,18 +7,18 @@ "build": "tsc" }, "peerDependencies": { - "@node-redis/client": "^1.0.0" + "@redis/client": "^1.0.0" }, "devDependencies": { "@istanbuljs/nyc-config-typescript": "^1.0.2", "@types/mocha": "^9.1.1", - "@types/node": "^17.0.26", + "@types/node": "^17.0.31", "@types/yargs": "^17.0.10", - "mocha": "^9.2.2", + "mocha": "^10.0.0", "nyc": "^15.1.0", "source-map-support": "^0.5.21", "ts-node": "^10.7.0", - "typescript": "^4.6.3", + "typescript": "^4.6.4", "yargs": "^17.4.1" } } diff --git a/packages/time-series/README.md b/packages/time-series/README.md index 5572fdb07ee..a9c8c081810 100644 --- a/packages/time-series/README.md +++ b/packages/time-series/README.md @@ -1,4 +1,4 @@ -# @node-redis/time-series +# @redis/time-series This package provides support for the [RedisTimeSeries](https://redistimeseries.io) module, which adds a time series data structure to Redis. It extends the [Node Redis client](https://github.com/redis/node-redis) to include functions for each of the RedisTimeSeries commands. diff --git a/packages/time-series/lib/commands/DECRBY.ts b/packages/time-series/lib/commands/DECRBY.ts index b7fab3702d0..07b5b6f45c0 100644 --- a/packages/time-series/lib/commands/DECRBY.ts +++ b/packages/time-series/lib/commands/DECRBY.ts @@ -1,4 +1,4 @@ -import { RedisCommandArguments } from '@node-redis/client/dist/lib/commands'; +import { RedisCommandArguments } from '@redis/client/dist/lib/commands'; import { IncrDecrOptions, transformIncrDecrArguments } from '.'; export const FIRST_KEY_INDEX = 1; diff --git a/packages/time-series/lib/commands/DEL.ts b/packages/time-series/lib/commands/DEL.ts index ae9a1e9fef3..347954c21de 100644 --- a/packages/time-series/lib/commands/DEL.ts +++ b/packages/time-series/lib/commands/DEL.ts @@ -1,4 +1,4 @@ -import { RedisCommandArguments } from '@node-redis/client/dist/lib/commands'; +import { RedisCommandArguments } from '@redis/client/dist/lib/commands'; import { Timestamp, transformTimestampArgument } from '.'; export const FIRTS_KEY_INDEX = 1; diff --git a/packages/time-series/lib/commands/INCRBY.ts b/packages/time-series/lib/commands/INCRBY.ts index 28267c57cc7..1f96801305f 100644 --- a/packages/time-series/lib/commands/INCRBY.ts +++ b/packages/time-series/lib/commands/INCRBY.ts @@ -1,4 +1,4 @@ -import { RedisCommandArguments } from '@node-redis/client/dist/lib/commands'; +import { RedisCommandArguments } from '@redis/client/dist/lib/commands'; import { IncrDecrOptions, transformIncrDecrArguments } from '.'; export const FIRST_KEY_INDEX = 1; diff --git a/packages/time-series/lib/commands/MGET.ts b/packages/time-series/lib/commands/MGET.ts index 1dbd077db3c..45d970ec810 100644 --- a/packages/time-series/lib/commands/MGET.ts +++ b/packages/time-series/lib/commands/MGET.ts @@ -1,4 +1,4 @@ -import { RedisCommandArguments } from '@node-redis/client/dist/lib/commands'; +import { RedisCommandArguments } from '@redis/client/dist/lib/commands'; import { Filter, pushFilterArgument, RawLabels, SampleRawReply, SampleReply, transformSampleReply } from '.'; export const IS_READ_ONLY = true; diff --git a/packages/time-series/lib/commands/MRANGE.ts b/packages/time-series/lib/commands/MRANGE.ts index 340201419f9..d589ac0332a 100644 --- a/packages/time-series/lib/commands/MRANGE.ts +++ b/packages/time-series/lib/commands/MRANGE.ts @@ -1,4 +1,4 @@ -import { RedisCommandArguments } from '@node-redis/client/dist/lib/commands'; +import { RedisCommandArguments } from '@redis/client/dist/lib/commands'; import { MRangeOptions, Timestamp, pushMRangeArguments, Filter } from '.'; export const IS_READ_ONLY = true; diff --git a/packages/time-series/lib/commands/MRANGE_WITHLABELS.ts b/packages/time-series/lib/commands/MRANGE_WITHLABELS.ts index f4ce2542355..16b7920e82c 100644 --- a/packages/time-series/lib/commands/MRANGE_WITHLABELS.ts +++ b/packages/time-series/lib/commands/MRANGE_WITHLABELS.ts @@ -1,4 +1,4 @@ -import { RedisCommandArguments } from '@node-redis/client/dist/lib/commands'; +import { RedisCommandArguments } from '@redis/client/dist/lib/commands'; import { Timestamp, MRangeWithLabelsOptions, pushMRangeWithLabelsArguments } from '.'; export const IS_READ_ONLY = true; diff --git a/packages/time-series/lib/commands/MREVRANGE.ts b/packages/time-series/lib/commands/MREVRANGE.ts index f31677fdb32..127c052ffe0 100644 --- a/packages/time-series/lib/commands/MREVRANGE.ts +++ b/packages/time-series/lib/commands/MREVRANGE.ts @@ -1,4 +1,4 @@ -import { RedisCommandArguments } from '@node-redis/client/dist/lib/commands'; +import { RedisCommandArguments } from '@redis/client/dist/lib/commands'; import { MRangeOptions, Timestamp, pushMRangeArguments, Filter } from '.'; export const IS_READ_ONLY = true; diff --git a/packages/time-series/lib/commands/MREVRANGE_WITHLABELS.ts b/packages/time-series/lib/commands/MREVRANGE_WITHLABELS.ts index 34109e00a68..21a0ebc69c3 100644 --- a/packages/time-series/lib/commands/MREVRANGE_WITHLABELS.ts +++ b/packages/time-series/lib/commands/MREVRANGE_WITHLABELS.ts @@ -1,4 +1,4 @@ -import { RedisCommandArguments } from '@node-redis/client/dist/lib/commands'; +import { RedisCommandArguments } from '@redis/client/dist/lib/commands'; import { Timestamp, MRangeWithLabelsOptions, pushMRangeWithLabelsArguments, Filter } from '.'; export const IS_READ_ONLY = true; diff --git a/packages/time-series/lib/commands/QUERYINDEX.ts b/packages/time-series/lib/commands/QUERYINDEX.ts index 8872605fa61..46eb5647040 100644 --- a/packages/time-series/lib/commands/QUERYINDEX.ts +++ b/packages/time-series/lib/commands/QUERYINDEX.ts @@ -1,5 +1,5 @@ -import { RedisCommandArguments } from '@node-redis/client/dist/lib/commands'; -import { pushVerdictArguments } from '@node-redis/client/dist/lib/commands/generic-transformers'; +import { RedisCommandArguments } from '@redis/client/dist/lib/commands'; +import { pushVerdictArguments } from '@redis/client/dist/lib/commands/generic-transformers'; import { Filter } from '.'; export const IS_READ_ONLY = true; diff --git a/packages/time-series/lib/commands/RANGE.ts b/packages/time-series/lib/commands/RANGE.ts index 73dfa90d399..e6ce256bbe6 100644 --- a/packages/time-series/lib/commands/RANGE.ts +++ b/packages/time-series/lib/commands/RANGE.ts @@ -1,4 +1,4 @@ -import { RedisCommandArguments } from '@node-redis/client/dist/lib/commands'; +import { RedisCommandArguments } from '@redis/client/dist/lib/commands'; import { RangeOptions, Timestamp, pushRangeArguments, SampleRawReply, SampleReply, transformRangeReply } from '.'; export const FIRST_KEY_INDEX = 1; diff --git a/packages/time-series/lib/commands/REVRANGE.ts b/packages/time-series/lib/commands/REVRANGE.ts index f2bfcb1cb50..9179756b5de 100644 --- a/packages/time-series/lib/commands/REVRANGE.ts +++ b/packages/time-series/lib/commands/REVRANGE.ts @@ -1,4 +1,4 @@ -import { RedisCommandArguments } from '@node-redis/client/dist/lib/commands'; +import { RedisCommandArguments } from '@redis/client/dist/lib/commands'; import { RangeOptions, Timestamp, pushRangeArguments, SampleRawReply, SampleReply, transformRangeReply } from '.'; export const FIRST_KEY_INDEX = 1; diff --git a/packages/time-series/lib/commands/index.spec.ts b/packages/time-series/lib/commands/index.spec.ts index b9d1eaaef29..8914b1fb131 100644 --- a/packages/time-series/lib/commands/index.spec.ts +++ b/packages/time-series/lib/commands/index.spec.ts @@ -1,4 +1,4 @@ -import { RedisCommandArguments } from '@node-redis/client/dist/lib/commands'; +import { RedisCommandArguments } from '@redis/client/dist/lib/commands'; import { strict as assert } from 'assert'; import { transformTimestampArgument, diff --git a/packages/time-series/lib/commands/index.ts b/packages/time-series/lib/commands/index.ts index 5836f4aa33c..4cc638a4249 100644 --- a/packages/time-series/lib/commands/index.ts +++ b/packages/time-series/lib/commands/index.ts @@ -19,8 +19,8 @@ import * as MRANGE from './MRANGE'; import * as MRANGE_WITHLABELS from './MRANGE_WITHLABELS'; import * as MREVRANGE from './MREVRANGE'; import * as MREVRANGE_WITHLABELS from './MREVRANGE_WITHLABELS'; -import { RedisCommandArguments } from '@node-redis/client/dist/lib/commands'; -import { pushVerdictArguments } from '@node-redis/client/dist/lib/commands/generic-transformers'; +import { RedisCommandArguments } from '@redis/client/dist/lib/commands'; +import { pushVerdictArguments } from '@redis/client/dist/lib/commands/generic-transformers'; export default { ADD, diff --git a/packages/time-series/lib/test-utils.ts b/packages/time-series/lib/test-utils.ts index c4639a98d49..96b2e04cbe2 100644 --- a/packages/time-series/lib/test-utils.ts +++ b/packages/time-series/lib/test-utils.ts @@ -1,4 +1,4 @@ -import TestUtils from '@node-redis/test-utils'; +import TestUtils from '@redis/test-utils'; import TimeSeries from '.'; export default new TestUtils({ diff --git a/packages/time-series/package.json b/packages/time-series/package.json index 71adbea399c..5a02d201d0b 100644 --- a/packages/time-series/package.json +++ b/packages/time-series/package.json @@ -1,5 +1,5 @@ { - "name": "@node-redis/time-series", + "name": "@redis/time-series", "version": "1.0.2", "license": "MIT", "main": "./dist/index.js", @@ -13,17 +13,17 @@ "documentation": "typedoc" }, "peerDependencies": { - "@node-redis/client": "^1.0.0" + "@redis/client": "^1.0.0" }, "devDependencies": { "@istanbuljs/nyc-config-typescript": "^1.0.2", - "@node-redis/test-utils": "*", - "@types/node": "^17.0.26", + "@redis/test-utils": "*", + "@types/node": "^17.0.31", "nyc": "^15.1.0", - "release-it": "^14.14.2", + "release-it": "^15.0.0", "source-map-support": "^0.5.21", "ts-node": "^10.7.0", "typedoc": "^0.22.15", - "typescript": "^4.6.3" + "typescript": "^4.6.4" } } diff --git a/tsconfig.base.json b/tsconfig.base.json index 35daa1df657..68325e51dcc 100644 --- a/tsconfig.base.json +++ b/tsconfig.base.json @@ -1,5 +1,5 @@ { - "extends": "@tsconfig/node12/tsconfig.json", + "extends": "@tsconfig/node14/tsconfig.json", "compilerOptions": { "declaration": true, "allowJs": true, From e5aaf38531c6e0cc17815b1d0e3a4cada8860d01 Mon Sep 17 00:00:00 2001 From: leibale Date: Mon, 2 May 2022 11:55:18 -0400 Subject: [PATCH 1201/1748] Release client@1.0.6 --- packages/client/package.json | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/packages/client/package.json b/packages/client/package.json index b2cd6996484..eaf0058168a 100644 --- a/packages/client/package.json +++ b/packages/client/package.json @@ -1,6 +1,6 @@ { "name": "@redis/client", - "version": "1.0.5", + "version": "1.0.6", "license": "MIT", "main": "./dist/index.js", "types": "./dist/index.d.ts", From 14973f15f041ce20c00f9c6b350b0e8cb3cfe4e1 Mon Sep 17 00:00:00 2001 From: leibale Date: Mon, 2 May 2022 11:56:09 -0400 Subject: [PATCH 1202/1748] Release bloom@1.0.2 --- packages/bloom/package.json | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/packages/bloom/package.json b/packages/bloom/package.json index bfdda92d793..8a1eee281f8 100644 --- a/packages/bloom/package.json +++ b/packages/bloom/package.json @@ -1,6 +1,6 @@ { "name": "@redis/bloom", - "version": "1.0.1", + "version": "1.0.2", "license": "MIT", "main": "./dist/index.js", "types": "./dist/index.d.ts", From 6dc9c83d9945d2bad26a5c49f79a15ad951ad21a Mon Sep 17 00:00:00 2001 From: leibale Date: Mon, 2 May 2022 11:56:43 -0400 Subject: [PATCH 1203/1748] Release graph@1.0.1 --- packages/graph/package.json | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/packages/graph/package.json b/packages/graph/package.json index a813d11434f..06bac79f0a0 100644 --- a/packages/graph/package.json +++ b/packages/graph/package.json @@ -1,6 +1,6 @@ { "name": "@redis/graph", - "version": "1.0.0", + "version": "1.0.1", "license": "MIT", "main": "./dist/index.js", "types": "./dist/index.d.ts", From f2a4e76d31acb63e9e9d20334cdaf10bdfbb3c09 Mon Sep 17 00:00:00 2001 From: leibale Date: Mon, 2 May 2022 11:57:06 -0400 Subject: [PATCH 1204/1748] Release json@1.0.3 --- packages/json/package.json | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/packages/json/package.json b/packages/json/package.json index 8fc11235b3f..227b27c44de 100644 --- a/packages/json/package.json +++ b/packages/json/package.json @@ -1,6 +1,6 @@ { "name": "@redis/json", - "version": "1.0.2", + "version": "1.0.3", "license": "MIT", "main": "./dist/index.js", "types": "./dist/index.d.ts", From ffc12344f5947d4cb4f4d48601c0d64d152b5f0a Mon Sep 17 00:00:00 2001 From: leibale Date: Mon, 2 May 2022 11:57:42 -0400 Subject: [PATCH 1205/1748] Release search@1.0.6 --- packages/search/package.json | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/packages/search/package.json b/packages/search/package.json index 8668ab94bb6..e098ec735b3 100644 --- a/packages/search/package.json +++ b/packages/search/package.json @@ -1,6 +1,6 @@ { "name": "@redis/search", - "version": "1.0.5", + "version": "1.0.6", "license": "MIT", "main": "./dist/index.js", "types": "./dist/index.d.ts", From c990a5c0e29081f920d8562c7e4d8d4713267141 Mon Sep 17 00:00:00 2001 From: leibale Date: Mon, 2 May 2022 11:58:18 -0400 Subject: [PATCH 1206/1748] Release time-series@1.0.3 --- packages/time-series/package.json | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/packages/time-series/package.json b/packages/time-series/package.json index 5a02d201d0b..da13eb2a6ae 100644 --- a/packages/time-series/package.json +++ b/packages/time-series/package.json @@ -1,6 +1,6 @@ { "name": "@redis/time-series", - "version": "1.0.2", + "version": "1.0.3", "license": "MIT", "main": "./dist/index.js", "types": "./dist/index.d.ts", From 9a62f61536716726274436175bfd94e96c6eb89e Mon Sep 17 00:00:00 2001 From: leibale Date: Mon, 2 May 2022 11:58:47 -0400 Subject: [PATCH 1207/1748] Release client@1.1.0 --- packages/client/package.json | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/packages/client/package.json b/packages/client/package.json index eaf0058168a..93d84ee7516 100644 --- a/packages/client/package.json +++ b/packages/client/package.json @@ -1,6 +1,6 @@ { "name": "@redis/client", - "version": "1.0.6", + "version": "1.1.0", "license": "MIT", "main": "./dist/index.js", "types": "./dist/index.d.ts", From eff2362d3a59bfb91de0f81e2b3aa064e82527b0 Mon Sep 17 00:00:00 2001 From: leibale Date: Mon, 2 May 2022 12:03:48 -0400 Subject: [PATCH 1208/1748] update @redis deps --- package-lock.json | 24 ++++++++++++------------ package.json | 12 ++++++------ 2 files changed, 18 insertions(+), 18 deletions(-) diff --git a/package-lock.json b/package-lock.json index 376f417cf37..d572148cf0e 100644 --- a/package-lock.json +++ b/package-lock.json @@ -12,12 +12,12 @@ "./packages/*" ], "dependencies": { - "@redis/bloom": "1.0.1", - "@redis/client": "1.0.6", - "@redis/graph": "1.0.0", - "@redis/json": "1.0.2", - "@redis/search": "1.0.5", - "@redis/time-series": "1.0.2" + "@redis/bloom": "1.0.2", + "@redis/client": "1.1.0", + "@redis/graph": "1.0.1", + "@redis/json": "1.0.3", + "@redis/search": "1.0.6", + "@redis/time-series": "1.0.3" }, "devDependencies": { "@tsconfig/node14": "^1.0.1", @@ -7848,7 +7848,7 @@ }, "packages/bloom": { "name": "@redis/bloom", - "version": "1.0.1", + "version": "1.0.2", "license": "MIT", "devDependencies": { "@istanbuljs/nyc-config-typescript": "^1.0.2", @@ -7867,7 +7867,7 @@ }, "packages/client": { "name": "@redis/client", - "version": "1.0.5", + "version": "1.1.0", "license": "MIT", "dependencies": { "cluster-key-slot": "1.1.0", @@ -7902,7 +7902,7 @@ }, "packages/graph": { "name": "@redis/graph", - "version": "1.0.0", + "version": "1.0.1", "license": "MIT", "devDependencies": { "@istanbuljs/nyc-config-typescript": "^1.0.2", @@ -7921,7 +7921,7 @@ }, "packages/json": { "name": "@redis/json", - "version": "1.0.2", + "version": "1.0.3", "license": "MIT", "devDependencies": { "@istanbuljs/nyc-config-typescript": "^1.0.2", @@ -7940,7 +7940,7 @@ }, "packages/search": { "name": "@redis/search", - "version": "1.0.5", + "version": "1.0.6", "license": "MIT", "devDependencies": { "@istanbuljs/nyc-config-typescript": "^1.0.2", @@ -8015,7 +8015,7 @@ }, "packages/time-series": { "name": "@redis/time-series", - "version": "1.0.2", + "version": "1.0.3", "license": "MIT", "devDependencies": { "@istanbuljs/nyc-config-typescript": "^1.0.2", diff --git a/package.json b/package.json index 4ebce5d2b49..e721dbb4244 100644 --- a/package.json +++ b/package.json @@ -23,12 +23,12 @@ "gh-pages": "gh-pages -d ./documentation -e ./documentation -u 'documentation-bot '" }, "dependencies": { - "@redis/bloom": "1.0.1", - "@redis/client": "1.0.6", - "@redis/graph": "1.0.0", - "@redis/json": "1.0.2", - "@redis/search": "1.0.5", - "@redis/time-series": "1.0.2" + "@redis/bloom": "1.0.2", + "@redis/client": "1.1.0", + "@redis/graph": "1.0.1", + "@redis/json": "1.0.3", + "@redis/search": "1.0.6", + "@redis/time-series": "1.0.3" }, "devDependencies": { "@tsconfig/node14": "^1.0.1", From 2ca1ba4f09c987b97215ac21e9d05b729331d985 Mon Sep 17 00:00:00 2001 From: leibale Date: Mon, 2 May 2022 12:04:08 -0400 Subject: [PATCH 1209/1748] Release redis@4.1.0 --- package-lock.json | 4 ++-- package.json | 2 +- 2 files changed, 3 insertions(+), 3 deletions(-) diff --git a/package-lock.json b/package-lock.json index d572148cf0e..ce69f95d53d 100644 --- a/package-lock.json +++ b/package-lock.json @@ -1,12 +1,12 @@ { "name": "redis", - "version": "4.0.6", + "version": "4.1.0", "lockfileVersion": 2, "requires": true, "packages": { "": { "name": "redis", - "version": "4.0.6", + "version": "4.1.0", "license": "MIT", "workspaces": [ "./packages/*" diff --git a/package.json b/package.json index e721dbb4244..bb6d21efb3e 100644 --- a/package.json +++ b/package.json @@ -1,7 +1,7 @@ { "name": "redis", "description": "A modern, high performance Redis client", - "version": "4.0.6", + "version": "4.1.0", "license": "MIT", "main": "./dist/index.js", "types": "./dist/index.d.ts", From a3b33d0177926b1e48f0826439910ee7b7bd2611 Mon Sep 17 00:00:00 2001 From: Adam Freidin Date: Wed, 11 May 2022 06:35:24 -0700 Subject: [PATCH 1210/1748] nit: spelling fix memoery to memory (#2132) --- packages/client/lib/client/commands.ts | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/packages/client/lib/client/commands.ts b/packages/client/lib/client/commands.ts index 84728ec76c6..f591ec20414 100644 --- a/packages/client/lib/client/commands.ts +++ b/packages/client/lib/client/commands.ts @@ -77,7 +77,7 @@ import * as INFO from '../commands/INFO'; import * as KEYS from '../commands/KEYS'; import * as LASTSAVE from '../commands/LASTSAVE'; import * as LOLWUT from '../commands/LOLWUT'; -import * as MEMOERY_DOCTOR from '../commands/MEMORY_DOCTOR'; +import * as MEMORY_DOCTOR from '../commands/MEMORY_DOCTOR'; import * as MEMORY_MALLOC_STATS from '../commands/MEMORY_MALLOC-STATS'; import * as MEMORY_PURGE from '../commands/MEMORY_PURGE'; import * as MEMORY_STATS from '../commands/MEMORY_STATS'; @@ -270,8 +270,8 @@ export default { latencyDoctor: LATENCY_DOCTOR, LOLWUT, lolwut: LOLWUT, - MEMOERY_DOCTOR, - memoryDoctor: MEMOERY_DOCTOR, + MEMORY_DOCTOR, + memoryDoctor: MEMORY_DOCTOR, 'MEMORY_MALLOC-STATS': MEMORY_MALLOC_STATS, memoryMallocStats: MEMORY_MALLOC_STATS, MEMORY_PURGE, From 5c9f31f2442bedb5643b93412b366d10b062f525 Mon Sep 17 00:00:00 2001 From: Leibale Eidelman Date: Wed, 11 May 2022 09:35:36 -0400 Subject: [PATCH 1211/1748] fix #2123 - expose is ready (#2130) --- packages/client/lib/client/index.ts | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/packages/client/lib/client/index.ts b/packages/client/lib/client/index.ts index 836f7908416..fb86908bd2f 100644 --- a/packages/client/lib/client/index.ts +++ b/packages/client/lib/client/index.ts @@ -166,6 +166,10 @@ export default class RedisClient< return this.#socket.isOpen; } + get isReady(): boolean { + return this.#socket.isReady; + } + get v4(): Record { if (!this.#options?.legacyMode) { throw new Error('the client is not in "legacy mode"'); From 06c1d2c2439cff4ce2cc8db77578be15fddc73cd Mon Sep 17 00:00:00 2001 From: Avital Fine <98389525+Avital-Fine@users.noreply.github.com> Date: Wed, 11 May 2022 16:36:12 +0300 Subject: [PATCH 1212/1748] Support all GEORADIUS Commands (#2017) * Support all GEORADIUS Commands * move store bool to options * simplify transformReply for store commands * clean code Co-authored-by: leibale --- packages/client/lib/cluster/commands.ts | 30 ++++++++++ .../client/lib/commands/GEORADIUS.spec.ts | 35 ++++++++++++ packages/client/lib/commands/GEORADIUS.ts | 25 ++++++++ .../lib/commands/GEORADIUSBYMEMBER.spec.ts | 26 +++++++++ .../client/lib/commands/GEORADIUSBYMEMBER.ts | 25 ++++++++ .../commands/GEORADIUSBYMEMBERSTORE.spec.ts | 53 +++++++++++++++++ .../lib/commands/GEORADIUSBYMEMBERSTORE.ts | 25 ++++++++ .../lib/commands/GEORADIUSBYMEMBER_RO.spec.ts | 26 +++++++++ .../lib/commands/GEORADIUSBYMEMBER_RO.ts | 25 ++++++++ .../GEORADIUSBYMEMBER_RO_WITH.spec.ts | 31 ++++++++++ .../lib/commands/GEORADIUSBYMEMBER_RO_WITH.ts | 30 ++++++++++ .../commands/GEORADIUSBYMEMBER_WITH.spec.ts | 31 ++++++++++ .../lib/commands/GEORADIUSBYMEMBER_WITH.ts | 30 ++++++++++ .../lib/commands/GEORADIUSSTORE.spec.ts | 53 +++++++++++++++++ .../client/lib/commands/GEORADIUSSTORE.ts | 25 ++++++++ .../client/lib/commands/GEORADIUS_RO.spec.ts | 35 ++++++++++++ packages/client/lib/commands/GEORADIUS_RO.ts | 25 ++++++++ .../lib/commands/GEORADIUS_RO_WITH.spec.ts | 40 +++++++++++++ .../client/lib/commands/GEORADIUS_RO_WITH.ts | 30 ++++++++++ .../lib/commands/GEORADIUS_WITH.spec.ts | 40 +++++++++++++ .../client/lib/commands/GEORADIUS_WITH.ts | 30 ++++++++++ .../client/lib/commands/GEOSEARCHSTORE.ts | 4 +- .../lib/commands/generic-transformers.ts | 57 +++++++++++++++++++ 23 files changed, 728 insertions(+), 3 deletions(-) create mode 100644 packages/client/lib/commands/GEORADIUS.spec.ts create mode 100644 packages/client/lib/commands/GEORADIUS.ts create mode 100644 packages/client/lib/commands/GEORADIUSBYMEMBER.spec.ts create mode 100644 packages/client/lib/commands/GEORADIUSBYMEMBER.ts create mode 100644 packages/client/lib/commands/GEORADIUSBYMEMBERSTORE.spec.ts create mode 100644 packages/client/lib/commands/GEORADIUSBYMEMBERSTORE.ts create mode 100644 packages/client/lib/commands/GEORADIUSBYMEMBER_RO.spec.ts create mode 100644 packages/client/lib/commands/GEORADIUSBYMEMBER_RO.ts create mode 100644 packages/client/lib/commands/GEORADIUSBYMEMBER_RO_WITH.spec.ts create mode 100644 packages/client/lib/commands/GEORADIUSBYMEMBER_RO_WITH.ts create mode 100644 packages/client/lib/commands/GEORADIUSBYMEMBER_WITH.spec.ts create mode 100644 packages/client/lib/commands/GEORADIUSBYMEMBER_WITH.ts create mode 100644 packages/client/lib/commands/GEORADIUSSTORE.spec.ts create mode 100644 packages/client/lib/commands/GEORADIUSSTORE.ts create mode 100644 packages/client/lib/commands/GEORADIUS_RO.spec.ts create mode 100644 packages/client/lib/commands/GEORADIUS_RO.ts create mode 100644 packages/client/lib/commands/GEORADIUS_RO_WITH.spec.ts create mode 100644 packages/client/lib/commands/GEORADIUS_RO_WITH.ts create mode 100644 packages/client/lib/commands/GEORADIUS_WITH.spec.ts create mode 100644 packages/client/lib/commands/GEORADIUS_WITH.ts diff --git a/packages/client/lib/cluster/commands.ts b/packages/client/lib/cluster/commands.ts index 48ca4b29872..dcce3629a1c 100644 --- a/packages/client/lib/cluster/commands.ts +++ b/packages/client/lib/cluster/commands.ts @@ -32,6 +32,16 @@ import * as GEOADD from '../commands/GEOADD'; import * as GEODIST from '../commands/GEODIST'; import * as GEOHASH from '../commands/GEOHASH'; import * as GEOPOS from '../commands/GEOPOS'; +import * as GEORADIUS_RO_WITH from '../commands/GEORADIUS_RO_WITH'; +import * as GEORADIUS_RO from '../commands/GEORADIUS_RO'; +import * as GEORADIUS_WITH from '../commands/GEORADIUS_WITH'; +import * as GEORADIUS from '../commands/GEORADIUS'; +import * as GEORADIUSBYMEMBER_RO_WITH from '../commands/GEORADIUSBYMEMBER_RO_WITH'; +import * as GEORADIUSBYMEMBER_RO from '../commands/GEORADIUSBYMEMBER_RO'; +import * as GEORADIUSBYMEMBER_WITH from '../commands/GEORADIUSBYMEMBER_WITH'; +import * as GEORADIUSBYMEMBER from '../commands/GEORADIUSBYMEMBER'; +import * as GEORADIUSBYMEMBERSTORE from '../commands/GEORADIUSBYMEMBERSTORE'; +import * as GEORADIUSSTORE from '../commands/GEORADIUSSTORE'; import * as GEOSEARCH_WITH from '../commands/GEOSEARCH_WITH'; import * as GEOSEARCH from '../commands/GEOSEARCH'; import * as GEOSEARCHSTORE from '../commands/GEOSEARCHSTORE'; @@ -263,6 +273,26 @@ export default { geoHash: GEOHASH, GEOPOS, geoPos: GEOPOS, + GEORADIUS_RO_WITH, + geoRadiusRoWith: GEORADIUS_RO_WITH, + GEORADIUS_RO, + geoRadiusRo: GEORADIUS_RO, + GEORADIUS_WITH, + geoRadiusWith: GEORADIUS_WITH, + GEORADIUS, + geoRadius: GEORADIUS, + GEORADIUSBYMEMBER_RO_WITH, + geoRadiusByMemberRoWith: GEORADIUSBYMEMBER_RO_WITH, + GEORADIUSBYMEMBER_RO, + geoRadiusByMemberRo: GEORADIUSBYMEMBER_RO, + GEORADIUSBYMEMBER_WITH, + geoRadiusByMemberWith: GEORADIUSBYMEMBER_WITH, + GEORADIUSBYMEMBER, + geoRadiusByMember: GEORADIUSBYMEMBER, + GEORADIUSBYMEMBERSTORE, + geoRadiusByMemberStore: GEORADIUSBYMEMBERSTORE, + GEORADIUSSTORE, + geoRadiusStore: GEORADIUSSTORE, GEOSEARCH_WITH, geoSearchWith: GEOSEARCH_WITH, GEOSEARCH, diff --git a/packages/client/lib/commands/GEORADIUS.spec.ts b/packages/client/lib/commands/GEORADIUS.spec.ts new file mode 100644 index 00000000000..786b2665029 --- /dev/null +++ b/packages/client/lib/commands/GEORADIUS.spec.ts @@ -0,0 +1,35 @@ +import { strict as assert } from 'assert'; +import testUtils, { GLOBAL } from '../test-utils'; +import { transformArguments } from './GEORADIUS'; + +describe('GEORADIUS', () => { + it('transformArguments', () => { + assert.deepEqual( + transformArguments('key', { + longitude: 1, + latitude: 2 + }, 3 , 'm'), + ['GEORADIUS', 'key', '1', '2', '3', 'm'] + ); + }); + + testUtils.testWithClient('client.geoRadius', async client => { + assert.deepEqual( + await client.geoRadius('key', { + longitude: 1, + latitude: 2 + }, 3 , 'm'), + [] + ); + }, GLOBAL.SERVERS.OPEN); + + testUtils.testWithCluster('cluster.geoRadius', async cluster => { + assert.deepEqual( + await cluster.geoRadius('key', { + longitude: 1, + latitude: 2 + }, 3 , 'm'), + [] + ); + }, GLOBAL.CLUSTERS.OPEN); +}); diff --git a/packages/client/lib/commands/GEORADIUS.ts b/packages/client/lib/commands/GEORADIUS.ts new file mode 100644 index 00000000000..f47cf508848 --- /dev/null +++ b/packages/client/lib/commands/GEORADIUS.ts @@ -0,0 +1,25 @@ +import { RedisCommandArgument, RedisCommandArguments } from '.'; +import { GeoSearchOptions, GeoCoordinates, pushGeoRadiusArguments, GeoUnits } from './generic-transformers'; + +export const FIRST_KEY_INDEX = 1; + +export const IS_READ_ONLY = true; + +export function transformArguments( + key: RedisCommandArgument, + coordinates: GeoCoordinates, + radius: number, + unit: GeoUnits, + options?: GeoSearchOptions +): RedisCommandArguments { + return pushGeoRadiusArguments( + ['GEORADIUS'], + key, + coordinates, + radius, + unit, + options + ); +} + +export declare function transformReply(): Array; diff --git a/packages/client/lib/commands/GEORADIUSBYMEMBER.spec.ts b/packages/client/lib/commands/GEORADIUSBYMEMBER.spec.ts new file mode 100644 index 00000000000..8cc4212c839 --- /dev/null +++ b/packages/client/lib/commands/GEORADIUSBYMEMBER.spec.ts @@ -0,0 +1,26 @@ +import { strict as assert } from 'assert'; +import testUtils, { GLOBAL } from '../test-utils'; +import { transformArguments } from './GEORADIUSBYMEMBER'; + +describe('GEORADIUSBYMEMBER', () => { + it('transformArguments', () => { + assert.deepEqual( + transformArguments('key', 'member', 3 , 'm'), + ['GEORADIUSBYMEMBER', 'key', 'member', '3', 'm'] + ); + }); + + testUtils.testWithClient('client.geoRadiusByMember', async client => { + assert.deepEqual( + await client.geoRadiusByMember('key', 'member', 3 , 'm'), + [] + ); + }, GLOBAL.SERVERS.OPEN); + + testUtils.testWithCluster('cluster.geoRadiusByMember', async cluster => { + assert.deepEqual( + await cluster.geoRadiusByMember('key', 'member', 3 , 'm'), + [] + ); + }, GLOBAL.CLUSTERS.OPEN); +}); diff --git a/packages/client/lib/commands/GEORADIUSBYMEMBER.ts b/packages/client/lib/commands/GEORADIUSBYMEMBER.ts new file mode 100644 index 00000000000..96bb622fb85 --- /dev/null +++ b/packages/client/lib/commands/GEORADIUSBYMEMBER.ts @@ -0,0 +1,25 @@ +import { RedisCommandArgument, RedisCommandArguments } from '.'; +import { GeoSearchOptions, pushGeoRadiusArguments, GeoUnits } from './generic-transformers'; + +export const FIRST_KEY_INDEX = 1; + +export const IS_READ_ONLY = true; + +export function transformArguments( + key: RedisCommandArgument, + member: string, + radius: number, + unit: GeoUnits, + options?: GeoSearchOptions +): RedisCommandArguments { + return pushGeoRadiusArguments( + ['GEORADIUSBYMEMBER'], + key, + member, + radius, + unit, + options + ); +} + +export declare function transformReply(): Array; diff --git a/packages/client/lib/commands/GEORADIUSBYMEMBERSTORE.spec.ts b/packages/client/lib/commands/GEORADIUSBYMEMBERSTORE.spec.ts new file mode 100644 index 00000000000..100ecc03368 --- /dev/null +++ b/packages/client/lib/commands/GEORADIUSBYMEMBERSTORE.spec.ts @@ -0,0 +1,53 @@ +import { strict as assert } from 'assert'; +import testUtils, { GLOBAL } from '../test-utils'; +import { transformArguments, transformReply } from './GEORADIUSBYMEMBERSTORE'; + +describe('GEORADIUSBYMEMBERSTORE', () => { + describe('transformArguments', () => { + it('STORE', () => { + assert.deepEqual( + transformArguments('key', 'member', 3 , 'm', 'dest', { + SORT: 'ASC', + COUNT: { + value: 1, + ANY: true + } + }), + ['GEORADIUSBYMEMBER', 'key', 'member', '3', 'm', 'ASC', 'COUNT', '1', 'ANY', 'STORE', 'dest'] + ); + }); + + it('STOREDIST', () => { + assert.deepEqual( + transformArguments('key', 'member', 3 , 'm', 'dest', { STOREDIST: true }), + ['GEORADIUSBYMEMBER', 'key', 'member', '3', 'm', 'STOREDIST', 'dest'] + ); + }); + }); + + testUtils.testWithClient('client.geoRadiusByMemberStore', async client => { + await client.geoAdd('source', { + longitude: 1, + latitude: 1, + member: 'member' + }); + + assert.equal( + await client.geoRadiusByMemberStore('source', 'member', 3 , 'm', 'dest'), + 1 + ); + }, GLOBAL.SERVERS.OPEN); + + testUtils.testWithCluster('cluster.geoRadiusByMemberStore', async cluster => { + await cluster.geoAdd('{tag}source', { + longitude: 1, + latitude: 1, + member: 'member' + }); + + assert.equal( + await cluster.geoRadiusByMemberStore('{tag}source', 'member', 3 , 'm','{tag}destination'), + 1 + ); + }, GLOBAL.CLUSTERS.OPEN); +}); diff --git a/packages/client/lib/commands/GEORADIUSBYMEMBERSTORE.ts b/packages/client/lib/commands/GEORADIUSBYMEMBERSTORE.ts new file mode 100644 index 00000000000..28f3c25fac9 --- /dev/null +++ b/packages/client/lib/commands/GEORADIUSBYMEMBERSTORE.ts @@ -0,0 +1,25 @@ +import { RedisCommandArgument, RedisCommandArguments } from '.'; +import { GeoUnits, GeoRadiusStoreOptions, pushGeoRadiusStoreArguments } from './generic-transformers'; + +export { FIRST_KEY_INDEX, IS_READ_ONLY } from './GEORADIUSBYMEMBER'; + +export function transformArguments( + key: RedisCommandArgument, + member: string, + radius: number, + unit: GeoUnits, + destination: RedisCommandArgument, + options?: GeoRadiusStoreOptions, +): RedisCommandArguments { + return pushGeoRadiusStoreArguments( + ['GEORADIUSBYMEMBER'], + key, + member, + radius, + unit, + destination, + options + ); +} + +export declare function transformReply(): number diff --git a/packages/client/lib/commands/GEORADIUSBYMEMBER_RO.spec.ts b/packages/client/lib/commands/GEORADIUSBYMEMBER_RO.spec.ts new file mode 100644 index 00000000000..f3a47856e86 --- /dev/null +++ b/packages/client/lib/commands/GEORADIUSBYMEMBER_RO.spec.ts @@ -0,0 +1,26 @@ +import { strict as assert } from 'assert'; +import testUtils, { GLOBAL } from '../test-utils'; +import { transformArguments } from './GEORADIUSBYMEMBER_RO'; + +describe('GEORADIUSBYMEMBER_RO', () => { + it('transformArguments', () => { + assert.deepEqual( + transformArguments('key', 'member', 3 , 'm'), + ['GEORADIUSBYMEMBER_RO', 'key', 'member', '3', 'm'] + ); + }); + + testUtils.testWithClient('client.geoRadiusByMemberRo', async client => { + assert.deepEqual( + await client.geoRadiusByMemberRo('key', 'member', 3 , 'm'), + [] + ); + }, GLOBAL.SERVERS.OPEN); + + testUtils.testWithCluster('cluster.geoRadiusByMemberRo', async cluster => { + assert.deepEqual( + await cluster.geoRadiusByMemberRo('key', 'member', 3 , 'm'), + [] + ); + }, GLOBAL.CLUSTERS.OPEN); +}); diff --git a/packages/client/lib/commands/GEORADIUSBYMEMBER_RO.ts b/packages/client/lib/commands/GEORADIUSBYMEMBER_RO.ts new file mode 100644 index 00000000000..63f29ae65b5 --- /dev/null +++ b/packages/client/lib/commands/GEORADIUSBYMEMBER_RO.ts @@ -0,0 +1,25 @@ +import { RedisCommandArgument, RedisCommandArguments } from '.'; +import { GeoSearchOptions, pushGeoRadiusArguments, GeoUnits } from './generic-transformers'; + +export const FIRST_KEY_INDEX = 1; + +export const IS_READ_ONLY = true; + +export function transformArguments( + key: RedisCommandArgument, + member: string, + radius: number, + unit: GeoUnits, + options?: GeoSearchOptions +): RedisCommandArguments { + return pushGeoRadiusArguments( + ['GEORADIUSBYMEMBER_RO'], + key, + member, + radius, + unit, + options + ); +} + +export declare function transformReply(): Array; diff --git a/packages/client/lib/commands/GEORADIUSBYMEMBER_RO_WITH.spec.ts b/packages/client/lib/commands/GEORADIUSBYMEMBER_RO_WITH.spec.ts new file mode 100644 index 00000000000..7904a763998 --- /dev/null +++ b/packages/client/lib/commands/GEORADIUSBYMEMBER_RO_WITH.spec.ts @@ -0,0 +1,31 @@ +import { strict as assert } from 'assert'; +import testUtils, { GLOBAL } from '../test-utils'; +import { RedisCommandArguments } from '.'; +import { GeoReplyWith } from './generic-transformers'; +import { transformArguments } from './GEORADIUSBYMEMBER_RO_WITH'; + +describe('GEORADIUSBYMEMBER_RO WITH', () => { + it('transformArguments', () => { + const expectedReply: RedisCommandArguments = ['GEORADIUSBYMEMBER_RO', 'key', 'member', '3', 'm', 'WITHDIST']; + expectedReply.preserve = ['WITHDIST']; + + assert.deepEqual( + transformArguments('key', 'member', 3 , 'm', [GeoReplyWith.DISTANCE]), + expectedReply + ); + }); + + testUtils.testWithClient('client.geoRadiusByMemberRoWith', async client => { + assert.deepEqual( + await client.geoRadiusByMemberRoWith('key', 'member', 3 , 'm', [GeoReplyWith.DISTANCE]), + [] + ); + }, GLOBAL.SERVERS.OPEN); + + testUtils.testWithCluster('cluster.geoRadiusByMemberRoWith', async cluster => { + assert.deepEqual( + await cluster.geoRadiusByMemberRoWith('key', 'member', 3 , 'm', [GeoReplyWith.DISTANCE]), + [] + ); + }, GLOBAL.CLUSTERS.OPEN); +}); diff --git a/packages/client/lib/commands/GEORADIUSBYMEMBER_RO_WITH.ts b/packages/client/lib/commands/GEORADIUSBYMEMBER_RO_WITH.ts new file mode 100644 index 00000000000..6061be734b5 --- /dev/null +++ b/packages/client/lib/commands/GEORADIUSBYMEMBER_RO_WITH.ts @@ -0,0 +1,30 @@ +import { RedisCommandArgument, RedisCommandArguments } from '.'; +import { GeoReplyWith, GeoSearchOptions, GeoUnits } from './generic-transformers'; +import { transformArguments as geoRadiusTransformArguments } from './GEORADIUSBYMEMBER_RO'; + +export { FIRST_KEY_INDEX, IS_READ_ONLY } from './GEORADIUSBYMEMBER_RO'; + +export function transformArguments( + key: RedisCommandArgument, + member: string, + radius: number, + unit: GeoUnits, + replyWith: Array, + options?: GeoSearchOptions +): RedisCommandArguments { + const args: RedisCommandArguments = geoRadiusTransformArguments( + key, + member, + radius, + unit, + options + ); + + args.push(...replyWith); + + args.preserve = replyWith; + + return args; +} + +export { transformGeoMembersWithReply as transformReply } from './generic-transformers'; diff --git a/packages/client/lib/commands/GEORADIUSBYMEMBER_WITH.spec.ts b/packages/client/lib/commands/GEORADIUSBYMEMBER_WITH.spec.ts new file mode 100644 index 00000000000..24bffd9e89f --- /dev/null +++ b/packages/client/lib/commands/GEORADIUSBYMEMBER_WITH.spec.ts @@ -0,0 +1,31 @@ +import { strict as assert } from 'assert'; +import testUtils, { GLOBAL } from '../test-utils'; +import { RedisCommandArguments } from '.'; +import { GeoReplyWith } from './generic-transformers'; +import { transformArguments } from './GEORADIUSBYMEMBER_WITH'; + +describe('GEORADIUSBYMEMBER WITH', () => { + it('transformArguments', () => { + const expectedReply: RedisCommandArguments = ['GEORADIUSBYMEMBER', 'key', 'member', '3', 'm', 'WITHDIST']; + expectedReply.preserve = ['WITHDIST']; + + assert.deepEqual( + transformArguments('key', 'member', 3 , 'm', [GeoReplyWith.DISTANCE]), + expectedReply + ); + }); + + testUtils.testWithClient('client.geoRadiusByMemberWith', async client => { + assert.deepEqual( + await client.geoRadiusByMemberWith('key', 'member', 3 , 'm', [GeoReplyWith.DISTANCE]), + [] + ); + }, GLOBAL.SERVERS.OPEN); + + testUtils.testWithCluster('cluster.geoRadiusByMemberWith', async cluster => { + assert.deepEqual( + await cluster.geoRadiusByMemberWith('key', 'member', 3 , 'm', [GeoReplyWith.DISTANCE]), + [] + ); + }, GLOBAL.CLUSTERS.OPEN); +}); diff --git a/packages/client/lib/commands/GEORADIUSBYMEMBER_WITH.ts b/packages/client/lib/commands/GEORADIUSBYMEMBER_WITH.ts new file mode 100644 index 00000000000..7d7dbe06a54 --- /dev/null +++ b/packages/client/lib/commands/GEORADIUSBYMEMBER_WITH.ts @@ -0,0 +1,30 @@ +import { RedisCommandArgument, RedisCommandArguments } from '.'; +import { GeoReplyWith, GeoSearchOptions, GeoUnits } from './generic-transformers'; +import { transformArguments as transformGeoRadiusArguments } from './GEORADIUSBYMEMBER'; + +export { FIRST_KEY_INDEX, IS_READ_ONLY } from './GEORADIUSBYMEMBER'; + +export function transformArguments( + key: RedisCommandArgument, + member: string, + radius: number, + unit: GeoUnits, + replyWith: Array, + options?: GeoSearchOptions +): RedisCommandArguments { + const args: RedisCommandArguments = transformGeoRadiusArguments( + key, + member, + radius, + unit, + options + ); + + args.push(...replyWith); + + args.preserve = replyWith; + + return args; +} + +export { transformGeoMembersWithReply as transformReply } from './generic-transformers'; diff --git a/packages/client/lib/commands/GEORADIUSSTORE.spec.ts b/packages/client/lib/commands/GEORADIUSSTORE.spec.ts new file mode 100644 index 00000000000..4c6372732e5 --- /dev/null +++ b/packages/client/lib/commands/GEORADIUSSTORE.spec.ts @@ -0,0 +1,53 @@ +import { strict as assert } from 'assert'; +import testUtils, { GLOBAL } from '../test-utils'; +import { transformArguments, transformReply } from './GEORADIUSSTORE'; + +describe('GEORADIUSSTORE', () => { + describe('transformArguments', () => { + it('STORE', () => { + assert.deepEqual( + transformArguments('key', {longitude: 1, latitude: 2}, 3 , 'm', 'dest', { + SORT: 'ASC', + COUNT: { + value: 1, + ANY: true + } + }), + ['GEORADIUS', 'key', '1', '2', '3', 'm', 'ASC', 'COUNT', '1', 'ANY', 'STORE', 'dest'] + ); + }); + + it('STOREDIST', () => { + assert.deepEqual( + transformArguments('key', {longitude: 1, latitude: 2}, 3 , 'm', 'dest', { STOREDIST: true }), + ['GEORADIUS', 'key', '1', '2', '3', 'm', 'STOREDIST', 'dest'] + ); + }); + }); + + testUtils.testWithClient('client.geoRadiusStore', async client => { + await client.geoAdd('source', { + longitude: 1, + latitude: 1, + member: 'member' + }); + + assert.equal( + await client.geoRadiusStore('source', {longitude: 1, latitude: 1}, 3 , 'm', 'dest'), + 1 + ); + }, GLOBAL.SERVERS.OPEN); + + testUtils.testWithCluster('cluster.geoRadiusStore', async cluster => { + await cluster.geoAdd('{tag}source', { + longitude: 1, + latitude: 1, + member: 'member' + }); + + assert.equal( + await cluster.geoRadiusStore('{tag}source', {longitude: 1, latitude: 1}, 3 , 'm', '{tag}destination'), + 1 + ); + }, GLOBAL.CLUSTERS.OPEN); +}); diff --git a/packages/client/lib/commands/GEORADIUSSTORE.ts b/packages/client/lib/commands/GEORADIUSSTORE.ts new file mode 100644 index 00000000000..ad2317aa3af --- /dev/null +++ b/packages/client/lib/commands/GEORADIUSSTORE.ts @@ -0,0 +1,25 @@ +import { RedisCommandArgument, RedisCommandArguments } from '.'; +import { GeoCoordinates, GeoUnits, GeoRadiusStoreOptions, pushGeoRadiusStoreArguments } from './generic-transformers'; + +export { FIRST_KEY_INDEX, IS_READ_ONLY } from './GEORADIUS'; + +export function transformArguments( + key: RedisCommandArgument, + coordinates: GeoCoordinates, + radius: number, + unit: GeoUnits, + destination: RedisCommandArgument, + options?: GeoRadiusStoreOptions, +): RedisCommandArguments { + return pushGeoRadiusStoreArguments( + ['GEORADIUS'], + key, + coordinates, + radius, + unit, + destination, + options + ); +} + +export declare function transformReply(): number; diff --git a/packages/client/lib/commands/GEORADIUS_RO.spec.ts b/packages/client/lib/commands/GEORADIUS_RO.spec.ts new file mode 100644 index 00000000000..b3cdca18d3f --- /dev/null +++ b/packages/client/lib/commands/GEORADIUS_RO.spec.ts @@ -0,0 +1,35 @@ +import { strict as assert } from 'assert'; +import testUtils, { GLOBAL } from '../test-utils'; +import { transformArguments } from './GEORADIUS_RO'; + +describe('GEORADIUS_RO', () => { + it('transformArguments', () => { + assert.deepEqual( + transformArguments('key', { + longitude: 1, + latitude: 2 + }, 3 , 'm'), + ['GEORADIUS_RO', 'key', '1', '2', '3', 'm'] + ); + }); + + testUtils.testWithClient('client.geoRadiusRo', async client => { + assert.deepEqual( + await client.geoRadiusRo('key', { + longitude: 1, + latitude: 2 + }, 3 , 'm'), + [] + ); + }, GLOBAL.SERVERS.OPEN); + + testUtils.testWithCluster('cluster.geoRadiusRo', async cluster => { + assert.deepEqual( + await cluster.geoRadiusRo('key', { + longitude: 1, + latitude: 2 + }, 3 , 'm'), + [] + ); + }, GLOBAL.CLUSTERS.OPEN); +}); diff --git a/packages/client/lib/commands/GEORADIUS_RO.ts b/packages/client/lib/commands/GEORADIUS_RO.ts new file mode 100644 index 00000000000..ac378a5150b --- /dev/null +++ b/packages/client/lib/commands/GEORADIUS_RO.ts @@ -0,0 +1,25 @@ +import { RedisCommandArgument, RedisCommandArguments } from '.'; +import { GeoSearchOptions, GeoCoordinates, pushGeoRadiusArguments, GeoUnits } from './generic-transformers'; + +export const FIRST_KEY_INDEX = 1; + +export const IS_READ_ONLY = true; + +export function transformArguments( + key: RedisCommandArgument, + coordinates: GeoCoordinates, + radius: number, + unit: GeoUnits, + options?: GeoSearchOptions +): RedisCommandArguments { + return pushGeoRadiusArguments( + ['GEORADIUS_RO'], + key, + coordinates, + radius, + unit, + options + ); +} + +export declare function transformReply(): Array; diff --git a/packages/client/lib/commands/GEORADIUS_RO_WITH.spec.ts b/packages/client/lib/commands/GEORADIUS_RO_WITH.spec.ts new file mode 100644 index 00000000000..21b00ff90b8 --- /dev/null +++ b/packages/client/lib/commands/GEORADIUS_RO_WITH.spec.ts @@ -0,0 +1,40 @@ +import { strict as assert } from 'assert'; +import testUtils, { GLOBAL } from '../test-utils'; +import { RedisCommandArguments } from '.'; +import { GeoReplyWith } from './generic-transformers'; +import { transformArguments } from './GEORADIUS_RO_WITH'; + +describe('GEORADIUS_RO WITH', () => { + it('transformArguments', () => { + const expectedReply: RedisCommandArguments = ['GEORADIUS_RO', 'key', '1', '2', '3', 'm', 'WITHDIST']; + expectedReply.preserve = ['WITHDIST']; + + assert.deepEqual( + transformArguments('key', { + longitude: 1, + latitude: 2 + }, 3 , 'm', [GeoReplyWith.DISTANCE]), + expectedReply + ); + }); + + testUtils.testWithClient('client.geoRadiusRoWith', async client => { + assert.deepEqual( + await client.geoRadiusRoWith('key', { + longitude: 1, + latitude: 2 + }, 3 , 'm', [GeoReplyWith.DISTANCE]), + [] + ); + }, GLOBAL.SERVERS.OPEN); + + testUtils.testWithCluster('cluster.geoRadiusReadOnlyWith', async cluster => { + assert.deepEqual( + await cluster.geoRadiusRoWith('key', { + longitude: 1, + latitude: 2 + }, 3 , 'm', [GeoReplyWith.DISTANCE]), + [] + ); + }, GLOBAL.CLUSTERS.OPEN); +}); diff --git a/packages/client/lib/commands/GEORADIUS_RO_WITH.ts b/packages/client/lib/commands/GEORADIUS_RO_WITH.ts new file mode 100644 index 00000000000..424e5fcd998 --- /dev/null +++ b/packages/client/lib/commands/GEORADIUS_RO_WITH.ts @@ -0,0 +1,30 @@ +import { RedisCommandArgument, RedisCommandArguments } from '.'; +import { GeoReplyWith, GeoSearchOptions, GeoCoordinates, GeoUnits } from './generic-transformers'; +import { transformArguments as transformGeoRadiusRoArguments } from './GEORADIUS_RO'; + +export { FIRST_KEY_INDEX, IS_READ_ONLY } from './GEORADIUS_RO'; + +export function transformArguments( + key: RedisCommandArgument, + coordinates: GeoCoordinates, + radius: number, + unit: GeoUnits, + replyWith: Array, + options?: GeoSearchOptions +): RedisCommandArguments { + const args: RedisCommandArguments = transformGeoRadiusRoArguments( + key, + coordinates, + radius, + unit, + options + ); + + args.push(...replyWith); + + args.preserve = replyWith; + + return args; +} + +export { transformGeoMembersWithReply as transformReply } from './generic-transformers'; diff --git a/packages/client/lib/commands/GEORADIUS_WITH.spec.ts b/packages/client/lib/commands/GEORADIUS_WITH.spec.ts new file mode 100644 index 00000000000..44366198beb --- /dev/null +++ b/packages/client/lib/commands/GEORADIUS_WITH.spec.ts @@ -0,0 +1,40 @@ +import { strict as assert } from 'assert'; +import testUtils, { GLOBAL } from '../test-utils'; +import { RedisCommandArguments } from '.'; +import { GeoReplyWith } from './generic-transformers'; +import { transformArguments } from './GEORADIUS_WITH'; + +describe('GEORADIUS WITH', () => { + it('transformArguments', () => { + const expectedReply: RedisCommandArguments = ['GEORADIUS', 'key', '1', '2', '3', 'm', 'WITHDIST']; + expectedReply.preserve = ['WITHDIST']; + + assert.deepEqual( + transformArguments('key', { + longitude: 1, + latitude: 2 + }, 3 , 'm', [GeoReplyWith.DISTANCE]), + expectedReply + ); + }); + + testUtils.testWithClient('client.geoRadiusWith', async client => { + assert.deepEqual( + await client.geoRadiusWith('key', { + longitude: 1, + latitude: 2 + }, 3 , 'm', [GeoReplyWith.DISTANCE]), + [] + ); + }, GLOBAL.SERVERS.OPEN); + + testUtils.testWithCluster('cluster.geoRadiusWith', async cluster => { + assert.deepEqual( + await cluster.geoRadiusWith('key', { + longitude: 1, + latitude: 2 + }, 3 , 'm', [GeoReplyWith.DISTANCE]), + [] + ); + }, GLOBAL.CLUSTERS.OPEN); +}); diff --git a/packages/client/lib/commands/GEORADIUS_WITH.ts b/packages/client/lib/commands/GEORADIUS_WITH.ts new file mode 100644 index 00000000000..dc3f4288f01 --- /dev/null +++ b/packages/client/lib/commands/GEORADIUS_WITH.ts @@ -0,0 +1,30 @@ +import { RedisCommandArgument, RedisCommandArguments } from '.'; +import { GeoReplyWith, GeoSearchOptions, GeoCoordinates, GeoUnits } from './generic-transformers'; +import { transformArguments as transformGeoRadiusArguments } from './GEORADIUS'; + +export { FIRST_KEY_INDEX, IS_READ_ONLY } from './GEORADIUS'; + +export function transformArguments( + key: RedisCommandArgument, + coordinates: GeoCoordinates, + radius: number, + unit: GeoUnits, + replyWith: Array, + options?: GeoSearchOptions +): RedisCommandArguments { + const args: RedisCommandArguments = transformGeoRadiusArguments( + key, + coordinates, + radius, + unit, + options + ); + + args.push(...replyWith); + + args.preserve = replyWith; + + return args; +} + +export { transformGeoMembersWithReply as transformReply } from './generic-transformers'; diff --git a/packages/client/lib/commands/GEOSEARCHSTORE.ts b/packages/client/lib/commands/GEOSEARCHSTORE.ts index bc06659ef09..7a91450cd9e 100644 --- a/packages/client/lib/commands/GEOSEARCHSTORE.ts +++ b/packages/client/lib/commands/GEOSEARCHSTORE.ts @@ -1,9 +1,7 @@ import { RedisCommandArgument, RedisCommandArguments } from '.'; import { GeoSearchFrom, GeoSearchBy, GeoSearchOptions, pushGeoSearchArguments } from './generic-transformers'; -export const FIRST_KEY_INDEX = 1; - -export const IS_READ_ONLY = true; +export { FIRST_KEY_INDEX, IS_READ_ONLY } from './GEOSEARCH'; interface GeoSearchStoreOptions extends GeoSearchOptions { STOREDIST?: true; diff --git a/packages/client/lib/commands/generic-transformers.ts b/packages/client/lib/commands/generic-transformers.ts index 0477caffd45..697dbcfa4e8 100644 --- a/packages/client/lib/commands/generic-transformers.ts +++ b/packages/client/lib/commands/generic-transformers.ts @@ -286,6 +286,63 @@ export function pushGeoSearchArguments( return args; } +export function pushGeoRadiusArguments( + args: RedisCommandArguments, + key: RedisCommandArgument, + from: GeoSearchFrom, + radius: number, + unit: GeoUnits, + options?: GeoSearchOptions +): RedisCommandArguments { + args.push(key); + + if (typeof from === 'string') { + args.push(from); + } else { + args.push( + from.longitude.toString(), + from.latitude.toString() + ); + } + + args.push( + radius.toString(), + unit + ); + + if (options?.SORT) { + args.push(options.SORT); + } + + pushGeoCountArgument(args, options?.COUNT); + + return args; +} + +export interface GeoRadiusStoreOptions extends GeoSearchOptions { + STOREDIST?: boolean; +} + +export function pushGeoRadiusStoreArguments( + args: RedisCommandArguments, + key: RedisCommandArgument, + from: GeoSearchFrom, + radius: number, + unit: GeoUnits, + destination: RedisCommandArgument, + options?: GeoRadiusStoreOptions +): RedisCommandArguments { + pushGeoRadiusArguments(args, key, from, radius, unit, options); + + if (options?.STOREDIST) { + args.push('STOREDIST', destination); + } else { + args.push('STORE', destination); + } + + return args; +} + export enum GeoReplyWith { DISTANCE = 'WITHDIST', HASH = 'WITHHASH', From 53a96ccce4508b4435e8832626e6acccf4243fda Mon Sep 17 00:00:00 2001 From: Leibale Eidelman Date: Wed, 11 May 2022 09:36:23 -0400 Subject: [PATCH 1213/1748] fix #1911 - CLIENT NO-EVICT (#2124) --- packages/client/lib/client/commands.ts | 3 ++ .../lib/commands/CLIENT_NO-EVICT.spec.ts | 30 +++++++++++++++++++ .../client/lib/commands/CLIENT_NO-EVICT.ts | 11 +++++++ 3 files changed, 44 insertions(+) create mode 100644 packages/client/lib/commands/CLIENT_NO-EVICT.spec.ts create mode 100644 packages/client/lib/commands/CLIENT_NO-EVICT.ts diff --git a/packages/client/lib/client/commands.ts b/packages/client/lib/client/commands.ts index f591ec20414..c5c11ceaa28 100644 --- a/packages/client/lib/client/commands.ts +++ b/packages/client/lib/client/commands.ts @@ -21,6 +21,7 @@ import * as CLIENT_GETNAME from '../commands/CLIENT_GETNAME'; import * as CLIENT_GETREDIR from '../commands/CLIENT_GETREDIR'; import * as CLIENT_ID from '../commands/CLIENT_ID'; import * as CLIENT_KILL from '../commands/CLIENT_KILL'; +import * as CLIENT_NO_EVICT from '../commands/CLIENT_NO-EVICT'; import * as CLIENT_SETNAME from '../commands/CLIENT_SETNAME'; import * as CLIENT_INFO from '../commands/CLIENT_INFO'; import * as CLUSTER_ADDSLOTS from '../commands/CLUSTER_ADDSLOTS'; @@ -158,6 +159,8 @@ export default { clientKill: CLIENT_KILL, CLIENT_SETNAME, clientSetName: CLIENT_SETNAME, + 'CLIENT_NO-EVICT': CLIENT_NO_EVICT, + clientNoEvict: CLIENT_NO_EVICT, CLIENT_INFO, clientInfo: CLIENT_INFO, CLUSTER_ADDSLOTS, diff --git a/packages/client/lib/commands/CLIENT_NO-EVICT.spec.ts b/packages/client/lib/commands/CLIENT_NO-EVICT.spec.ts new file mode 100644 index 00000000000..df8903f0646 --- /dev/null +++ b/packages/client/lib/commands/CLIENT_NO-EVICT.spec.ts @@ -0,0 +1,30 @@ +import { strict as assert } from 'assert'; +import testUtils, { GLOBAL } from '../test-utils'; +import { transformArguments } from './CLIENT_NO-EVICT'; + +describe('CLIENT NO-EVICT', () => { + testUtils.isVersionGreaterThanHook([7]); + + describe('transformArguments', () => { + it('true', () => { + assert.deepEqual( + transformArguments(true), + ['CLIENT', 'NO-EVICT', 'ON'] + ); + }); + + it('false', () => { + assert.deepEqual( + transformArguments(false), + ['CLIENT', 'NO-EVICT', 'OFF'] + ); + }); + }); + + testUtils.testWithClient('client.clientNoEvict', async client => { + assert.equal( + await client.clientNoEvict(true), + 'OK' + ); + }, GLOBAL.SERVERS.OPEN); +}); diff --git a/packages/client/lib/commands/CLIENT_NO-EVICT.ts b/packages/client/lib/commands/CLIENT_NO-EVICT.ts new file mode 100644 index 00000000000..86edbde1d23 --- /dev/null +++ b/packages/client/lib/commands/CLIENT_NO-EVICT.ts @@ -0,0 +1,11 @@ +import { RedisCommandArguments } from '.'; + +export function transformArguments(value: boolean): RedisCommandArguments { + return [ + 'CLIENT', + 'NO-EVICT', + value ? 'ON' : 'OFF' + ]; +} + +export declare function transformReply(): 'OK' | Buffer; From 94dbcc847b0558c18b2f269ffe17caebe96ffed6 Mon Sep 17 00:00:00 2001 From: Leibale Eidelman Date: Wed, 11 May 2022 10:02:29 -0400 Subject: [PATCH 1214/1748] fix #1912 - CLIENT PAUSE (#2125) * fix #1912 - CLIENT PAUSE * fix client pause * Update commands.ts --- packages/client/lib/client/commands.ts | 7 +++-- .../client/lib/commands/CLIENT_PAUSE.spec.ts | 28 +++++++++++++++++++ packages/client/lib/commands/CLIENT_PAUSE.ts | 20 +++++++++++++ 3 files changed, 53 insertions(+), 2 deletions(-) create mode 100644 packages/client/lib/commands/CLIENT_PAUSE.spec.ts create mode 100644 packages/client/lib/commands/CLIENT_PAUSE.ts diff --git a/packages/client/lib/client/commands.ts b/packages/client/lib/client/commands.ts index c5c11ceaa28..06334a1103e 100644 --- a/packages/client/lib/client/commands.ts +++ b/packages/client/lib/client/commands.ts @@ -22,6 +22,7 @@ import * as CLIENT_GETREDIR from '../commands/CLIENT_GETREDIR'; import * as CLIENT_ID from '../commands/CLIENT_ID'; import * as CLIENT_KILL from '../commands/CLIENT_KILL'; import * as CLIENT_NO_EVICT from '../commands/CLIENT_NO-EVICT'; +import * as CLIENT_PAUSE from '../commands/CLIENT_PAUSE'; import * as CLIENT_SETNAME from '../commands/CLIENT_SETNAME'; import * as CLIENT_INFO from '../commands/CLIENT_INFO'; import * as CLUSTER_ADDSLOTS from '../commands/CLUSTER_ADDSLOTS'; @@ -157,10 +158,12 @@ export default { clientId: CLIENT_ID, CLIENT_KILL, clientKill: CLIENT_KILL, - CLIENT_SETNAME, - clientSetName: CLIENT_SETNAME, 'CLIENT_NO-EVICT': CLIENT_NO_EVICT, clientNoEvict: CLIENT_NO_EVICT, + CLIENT_PAUSE, + clientPause: CLIENT_PAUSE, + CLIENT_SETNAME, + clientSetName: CLIENT_SETNAME, CLIENT_INFO, clientInfo: CLIENT_INFO, CLUSTER_ADDSLOTS, diff --git a/packages/client/lib/commands/CLIENT_PAUSE.spec.ts b/packages/client/lib/commands/CLIENT_PAUSE.spec.ts new file mode 100644 index 00000000000..1376ff41eed --- /dev/null +++ b/packages/client/lib/commands/CLIENT_PAUSE.spec.ts @@ -0,0 +1,28 @@ +import { strict as assert } from 'assert'; +import testUtils, { GLOBAL } from '../test-utils'; +import { transformArguments } from './CLIENT_PAUSE'; + +describe('CLIENT PAUSE', () => { + describe('transformArguments', () => { + it('simple', () => { + assert.deepEqual( + transformArguments(0), + ['CLIENT', 'PAUSE', '0'] + ); + }); + + it('with mode', () => { + assert.deepEqual( + transformArguments(0, 'ALL'), + ['CLIENT', 'PAUSE', '0', 'ALL'] + ); + }); + }); + + testUtils.testWithClient('client.clientPause', async client => { + assert.equal( + await client.clientPause(0), + 'OK' + ); + }, GLOBAL.SERVERS.OPEN); +}); diff --git a/packages/client/lib/commands/CLIENT_PAUSE.ts b/packages/client/lib/commands/CLIENT_PAUSE.ts new file mode 100644 index 00000000000..090002272c9 --- /dev/null +++ b/packages/client/lib/commands/CLIENT_PAUSE.ts @@ -0,0 +1,20 @@ +import { RedisCommandArguments } from '.'; + +export function transformArguments( + timeout: number, + mode?: 'WRITE' | 'ALL' +): RedisCommandArguments { + const args = [ + 'CLIENT', + 'PAUSE', + timeout.toString() + ]; + + if (mode) { + args.push(mode); + } + + return args; +} + +export declare function transformReply(): 'OK' | Buffer; From 3ec17e31b32bd919400ef271c39fab28e0809b0d Mon Sep 17 00:00:00 2001 From: Leibale Eidelman Date: Wed, 11 May 2022 14:20:26 -0400 Subject: [PATCH 1215/1748] fix legacy mode resp encoder (#2118) * fix legacy mode resp encoder * Update encoder.ts --- packages/client/lib/client/RESP2/encoder.ts | 16 +++++++--------- packages/client/lib/commander.ts | 6 +++++- 2 files changed, 12 insertions(+), 10 deletions(-) diff --git a/packages/client/lib/client/RESP2/encoder.ts b/packages/client/lib/client/RESP2/encoder.ts index be48348a356..217fbc714bb 100644 --- a/packages/client/lib/client/RESP2/encoder.ts +++ b/packages/client/lib/client/RESP2/encoder.ts @@ -5,23 +5,21 @@ const CRLF = '\r\n'; export default function encodeCommand(args: RedisCommandArguments): Array { const toWrite: Array = []; - let strings = `*${args.length}${CRLF}`; + let strings = '*' + args.length + CRLF; for (let i = 0; i < args.length; i++) { const arg = args[i]; if (typeof arg === 'string') { - const byteLength = Buffer.byteLength(arg); - strings += `$${byteLength}${CRLF}`; - strings += arg; + strings += '$' + Buffer.byteLength(arg) + CRLF + arg + CRLF; } else if (arg instanceof Buffer) { - toWrite.push(`${strings}$${arg.length}${CRLF}`); - strings = ''; - toWrite.push(arg); + toWrite.push( + strings + '$' + arg.length.toString() + CRLF, + arg + ); + strings = CRLF; } else { throw new TypeError('Invalid argument type'); } - - strings += CRLF; } toWrite.push(strings); diff --git a/packages/client/lib/commander.ts b/packages/client/lib/commander.ts index 3ab8b997d93..661e53cb9b5 100644 --- a/packages/client/lib/commander.ts +++ b/packages/client/lib/commander.ts @@ -123,7 +123,11 @@ export function transformCommandArguments( } export function transformLegacyCommandArguments(args: Array): Array { - return args.flat().map(x => x?.toString?.()); + return args.flat().map(arg => { + return typeof arg === 'number' || arg instanceof Date ? + arg.toString() : + arg; + }); } export function transformCommandReply( From 24c2c867f8a459fbbab39fe00c997cdf5d370969 Mon Sep 17 00:00:00 2001 From: Leibale Eidelman Date: Wed, 11 May 2022 14:20:52 -0400 Subject: [PATCH 1216/1748] fix #1917 - CLIENT UNPAUSE (#2128) * fix #1917 - CLIENT UNPAUSE * fix CLIENT UNPAUSE --- packages/client/lib/client/commands.ts | 3 +++ .../lib/commands/CLIENT_UNPAUSE.spec.ts | 21 +++++++++++++++++++ .../client/lib/commands/CLIENT_UNPAUSE.ts | 7 +++++++ 3 files changed, 31 insertions(+) create mode 100644 packages/client/lib/commands/CLIENT_UNPAUSE.spec.ts create mode 100644 packages/client/lib/commands/CLIENT_UNPAUSE.ts diff --git a/packages/client/lib/client/commands.ts b/packages/client/lib/client/commands.ts index 06334a1103e..e2b0a8cd27c 100644 --- a/packages/client/lib/client/commands.ts +++ b/packages/client/lib/client/commands.ts @@ -24,6 +24,7 @@ import * as CLIENT_KILL from '../commands/CLIENT_KILL'; import * as CLIENT_NO_EVICT from '../commands/CLIENT_NO-EVICT'; import * as CLIENT_PAUSE from '../commands/CLIENT_PAUSE'; import * as CLIENT_SETNAME from '../commands/CLIENT_SETNAME'; +import * as CLIENT_UNPAUSE from '../commands/CLIENT_UNPAUSE'; import * as CLIENT_INFO from '../commands/CLIENT_INFO'; import * as CLUSTER_ADDSLOTS from '../commands/CLUSTER_ADDSLOTS'; import * as CLUSTER_ADDSLOTSRANGE from '../commands/CLUSTER_ADDSLOTSRANGE'; @@ -164,6 +165,8 @@ export default { clientPause: CLIENT_PAUSE, CLIENT_SETNAME, clientSetName: CLIENT_SETNAME, + CLIENT_UNPAUSE, + clientUnpause: CLIENT_UNPAUSE, CLIENT_INFO, clientInfo: CLIENT_INFO, CLUSTER_ADDSLOTS, diff --git a/packages/client/lib/commands/CLIENT_UNPAUSE.spec.ts b/packages/client/lib/commands/CLIENT_UNPAUSE.spec.ts new file mode 100644 index 00000000000..73c731ee87f --- /dev/null +++ b/packages/client/lib/commands/CLIENT_UNPAUSE.spec.ts @@ -0,0 +1,21 @@ +import { strict as assert } from 'assert'; +import testUtils, { GLOBAL } from '../test-utils'; +import { transformArguments } from './CLIENT_UNPAUSE'; + +describe('CLIENT UNPAUSE', () => { + testUtils.isVersionGreaterThanHook([6, 2]); + + it('transformArguments', () => { + assert.deepEqual( + transformArguments(), + ['CLIENT', 'UNPAUSE'] + ); + }); + + testUtils.testWithClient('client.unpause', async client => { + assert.equal( + await client.clientUnpause(), + 'OK' + ); + }, GLOBAL.SERVERS.OPEN); +}); diff --git a/packages/client/lib/commands/CLIENT_UNPAUSE.ts b/packages/client/lib/commands/CLIENT_UNPAUSE.ts new file mode 100644 index 00000000000..e139436d004 --- /dev/null +++ b/packages/client/lib/commands/CLIENT_UNPAUSE.ts @@ -0,0 +1,7 @@ +import { RedisCommandArguments } from '.'; + +export function transformArguments(): RedisCommandArguments { + return ['CLIENT', 'UNPAUSE']; +} + +export declare function transformReply(): 'OK' | Buffer; From 429b11e0201b4aef451d6fb0921ea41c33b7bfdc Mon Sep 17 00:00:00 2001 From: Leibale Eidelman Date: Wed, 11 May 2022 14:24:07 -0400 Subject: [PATCH 1217/1748] fix #1959 - LCS (#2129) * fix #1959 - LCS * newlines at end of files --- packages/client/lib/cluster/commands.ts | 12 +++++ packages/client/lib/commands/LCS.spec.ts | 28 ++++++++++++ packages/client/lib/commands/LCS.ts | 18 ++++++++ packages/client/lib/commands/LCS_IDX.spec.ts | 41 +++++++++++++++++ packages/client/lib/commands/LCS_IDX.ts | 42 +++++++++++++++++ .../lib/commands/LCS_IDX_WITHMATCHLEN.spec.ts | 42 +++++++++++++++++ .../lib/commands/LCS_IDX_WITHMATCHLEN.ts | 45 +++++++++++++++++++ packages/client/lib/commands/LCS_LEN.spec.ts | 28 ++++++++++++ packages/client/lib/commands/LCS_LEN.ts | 15 +++++++ .../lib/commands/generic-transformers.ts | 17 +++++++ 10 files changed, 288 insertions(+) create mode 100644 packages/client/lib/commands/LCS.spec.ts create mode 100644 packages/client/lib/commands/LCS.ts create mode 100644 packages/client/lib/commands/LCS_IDX.spec.ts create mode 100644 packages/client/lib/commands/LCS_IDX.ts create mode 100644 packages/client/lib/commands/LCS_IDX_WITHMATCHLEN.spec.ts create mode 100644 packages/client/lib/commands/LCS_IDX_WITHMATCHLEN.ts create mode 100644 packages/client/lib/commands/LCS_LEN.spec.ts create mode 100644 packages/client/lib/commands/LCS_LEN.ts diff --git a/packages/client/lib/cluster/commands.ts b/packages/client/lib/cluster/commands.ts index dcce3629a1c..8edbd1e3891 100644 --- a/packages/client/lib/cluster/commands.ts +++ b/packages/client/lib/cluster/commands.ts @@ -71,6 +71,10 @@ import * as HVALS from '../commands/HVALS'; import * as INCR from '../commands/INCR'; import * as INCRBY from '../commands/INCRBY'; import * as INCRBYFLOAT from '../commands/INCRBYFLOAT'; +import * as LCS_IDX_WITHMATCHLEN from '../commands/LCS_IDX_WITHMATCHLEN'; +import * as LCS_IDX from '../commands/LCS_IDX'; +import * as LCS_LEN from '../commands/LCS_LEN'; +import * as LCS from '../commands/LCS'; import * as LINDEX from '../commands/LINDEX'; import * as LINSERT from '../commands/LINSERT'; import * as LLEN from '../commands/LLEN'; @@ -351,6 +355,14 @@ export default { incrBy: INCRBY, INCRBYFLOAT, incrByFloat: INCRBYFLOAT, + LCS_IDX_WITHMATCHLEN, + lcsIdxWithMatchLen: LCS_IDX_WITHMATCHLEN, + LCS_IDX, + lcsIdx: LCS_IDX, + LCS_LEN, + lcsLen: LCS_LEN, + LCS, + lcs: LCS, LINDEX, lIndex: LINDEX, LINSERT, diff --git a/packages/client/lib/commands/LCS.spec.ts b/packages/client/lib/commands/LCS.spec.ts new file mode 100644 index 00000000000..a4d9035571e --- /dev/null +++ b/packages/client/lib/commands/LCS.spec.ts @@ -0,0 +1,28 @@ +import { strict as assert } from 'assert'; +import testUtils, { GLOBAL } from '../test-utils'; +import { transformArguments } from './LCS'; + +describe('LCS', () => { + testUtils.isVersionGreaterThanHook([7]); + + it('transformArguments', () => { + assert.deepEqual( + transformArguments('1', '2'), + ['LCS', '1', '2'] + ); + }); + + testUtils.testWithClient('client.lcs', async client => { + assert.equal( + await client.lcs('1', '2'), + '' + ); + }, GLOBAL.SERVERS.OPEN); + + testUtils.testWithCluster('cluster.lcs', async cluster => { + assert.equal( + await cluster.lcs('{tag}1', '{tag}2'), + '' + ); + }, GLOBAL.CLUSTERS.OPEN); +}); diff --git a/packages/client/lib/commands/LCS.ts b/packages/client/lib/commands/LCS.ts new file mode 100644 index 00000000000..b075b73e8a8 --- /dev/null +++ b/packages/client/lib/commands/LCS.ts @@ -0,0 +1,18 @@ +import { RedisCommandArgument, RedisCommandArguments } from '.'; + +export const FIRST_KEY_INDEX = 1; + +export const IS_READ_ONLY = true; + +export function transformArguments( + key1: RedisCommandArgument, + key2: RedisCommandArgument +): RedisCommandArguments { + return [ + 'LCS', + key1, + key2 + ]; +} + +export declare function transformReply(): string | Buffer; diff --git a/packages/client/lib/commands/LCS_IDX.spec.ts b/packages/client/lib/commands/LCS_IDX.spec.ts new file mode 100644 index 00000000000..fc3ee54f7c0 --- /dev/null +++ b/packages/client/lib/commands/LCS_IDX.spec.ts @@ -0,0 +1,41 @@ +import { strict as assert } from 'assert'; +import testUtils, { GLOBAL } from '../test-utils'; +import { transformArguments } from './LCS_IDX'; + +describe('LCS_IDX', () => { + testUtils.isVersionGreaterThanHook([7]); + + it('transformArguments', () => { + assert.deepEqual( + transformArguments('1', '2'), + ['LCS', '1', '2', 'IDX'] + ); + }); + + testUtils.testWithClient('client.lcsIdx', async client => { + const [, reply] = await Promise.all([ + client.mSet({ + '1': 'abc', + '2': 'bc' + }), + client.lcsIdx('1', '2') + ]); + + assert.deepEqual( + reply, + { + matches: [{ + key1: { + start: 1, + end: 2 + }, + key2: { + start: 0, + end: 1 + } + }], + length: 2 + } + ); + }, GLOBAL.SERVERS.OPEN); +}); diff --git a/packages/client/lib/commands/LCS_IDX.ts b/packages/client/lib/commands/LCS_IDX.ts new file mode 100644 index 00000000000..262a02ba4c6 --- /dev/null +++ b/packages/client/lib/commands/LCS_IDX.ts @@ -0,0 +1,42 @@ +import { RedisCommandArgument, RedisCommandArguments } from '.'; +import { RangeReply, RawRangeReply, transformRangeReply } from './generic-transformers'; +import { transformArguments as transformLcsArguments } from './LCS'; + +export { FIRST_KEY_INDEX, IS_READ_ONLY } from './LCS'; + +export function transformArguments( + key1: RedisCommandArgument, + key2: RedisCommandArgument +): RedisCommandArguments { + const args = transformLcsArguments(key1, key2); + args.push('IDX'); + return args; +} + +type RawReply = [ + 'matches', + Array<[ + key1: RawRangeReply, + key2: RawRangeReply + ]>, + 'len', + number +]; + +interface Reply { + matches: Array<{ + key1: RangeReply; + key2: RangeReply; + }>; + length: number; +} + +export function transformReply(reply: RawReply): Reply { + return { + matches: reply[1].map(([key1, key2]) => ({ + key1: transformRangeReply(key1), + key2: transformRangeReply(key2) + })), + length: reply[3] + }; +} diff --git a/packages/client/lib/commands/LCS_IDX_WITHMATCHLEN.spec.ts b/packages/client/lib/commands/LCS_IDX_WITHMATCHLEN.spec.ts new file mode 100644 index 00000000000..8be9b993135 --- /dev/null +++ b/packages/client/lib/commands/LCS_IDX_WITHMATCHLEN.spec.ts @@ -0,0 +1,42 @@ +import { strict as assert } from 'assert'; +import testUtils, { GLOBAL } from '../test-utils'; +import { transformArguments } from './LCS_IDX_WITHMATCHLEN'; + +describe('LCS_IDX_WITHMATCHLEN', () => { + testUtils.isVersionGreaterThanHook([7]); + + it('transformArguments', () => { + assert.deepEqual( + transformArguments('1', '2'), + ['LCS', '1', '2', 'IDX', 'WITHMATCHLEN'] + ); + }); + + testUtils.testWithClient('client.lcsIdxWithMatchLen', async client => { + const [, reply] = await Promise.all([ + client.mSet({ + '1': 'abc', + '2': 'bc' + }), + client.lcsIdxWithMatchLen('1', '2') + ]); + + assert.deepEqual( + reply, + { + matches: [{ + key1: { + start: 1, + end: 2 + }, + key2: { + start: 0, + end: 1 + }, + length: 2 + }], + length: 2 + } + ); + }, GLOBAL.SERVERS.OPEN); +}); diff --git a/packages/client/lib/commands/LCS_IDX_WITHMATCHLEN.ts b/packages/client/lib/commands/LCS_IDX_WITHMATCHLEN.ts new file mode 100644 index 00000000000..989870d6ca2 --- /dev/null +++ b/packages/client/lib/commands/LCS_IDX_WITHMATCHLEN.ts @@ -0,0 +1,45 @@ +import { RedisCommandArgument, RedisCommandArguments } from '.'; +import { RangeReply, RawRangeReply, transformRangeReply } from './generic-transformers'; +import { transformArguments as transformLcsArguments } from './LCS'; + +export { FIRST_KEY_INDEX, IS_READ_ONLY } from './LCS'; + +export function transformArguments( + key1: RedisCommandArgument, + key2: RedisCommandArgument +): RedisCommandArguments { + const args = transformLcsArguments(key1, key2); + args.push('IDX', 'WITHMATCHLEN'); + return args; +} + +type RawReply = [ + 'matches', + Array<[ + key1: RawRangeReply, + key2: RawRangeReply, + length: number + ]>, + 'len', + number +]; + +interface Reply { + matches: Array<{ + key1: RangeReply; + key2: RangeReply; + length: number; + }>; + length: number; +} + +export function transformReply(reply: RawReply): Reply { + return { + matches: reply[1].map(([key1, key2, length]) => ({ + key1: transformRangeReply(key1), + key2: transformRangeReply(key2), + length + })), + length: reply[3] + }; +} diff --git a/packages/client/lib/commands/LCS_LEN.spec.ts b/packages/client/lib/commands/LCS_LEN.spec.ts new file mode 100644 index 00000000000..bf4eefd3301 --- /dev/null +++ b/packages/client/lib/commands/LCS_LEN.spec.ts @@ -0,0 +1,28 @@ +import { strict as assert } from 'assert'; +import testUtils, { GLOBAL } from '../test-utils'; +import { transformArguments } from './LCS_LEN'; + +describe('LCS_LEN', () => { + testUtils.isVersionGreaterThanHook([7]); + + it('transformArguments', () => { + assert.deepEqual( + transformArguments('1', '2'), + ['LCS', '1', '2', 'LEN'] + ); + }); + + testUtils.testWithClient('client.lcsLen', async client => { + assert.equal( + await client.lcsLen('1', '2'), + 0 + ); + }, GLOBAL.SERVERS.OPEN); + + testUtils.testWithCluster('cluster.lcsLen', async cluster => { + assert.equal( + await cluster.lcsLen('{tag}1', '{tag}2'), + 0 + ); + }, GLOBAL.CLUSTERS.OPEN); +}); diff --git a/packages/client/lib/commands/LCS_LEN.ts b/packages/client/lib/commands/LCS_LEN.ts new file mode 100644 index 00000000000..a5121e4c13f --- /dev/null +++ b/packages/client/lib/commands/LCS_LEN.ts @@ -0,0 +1,15 @@ +import { RedisCommandArgument, RedisCommandArguments } from '.'; +import { transformArguments as transformLcsArguments } from './LCS'; + +export { FIRST_KEY_INDEX, IS_READ_ONLY } from './LCS'; + +export function transformArguments( + key1: RedisCommandArgument, + key2: RedisCommandArgument +): RedisCommandArguments { + const args = transformLcsArguments(key1, key2); + args.push('LEN'); + return args; +} + +export declare function transformReply(): number; diff --git a/packages/client/lib/commands/generic-transformers.ts b/packages/client/lib/commands/generic-transformers.ts index 697dbcfa4e8..728378bb27b 100644 --- a/packages/client/lib/commands/generic-transformers.ts +++ b/packages/client/lib/commands/generic-transformers.ts @@ -670,3 +670,20 @@ export function pushSlotRangesArguments( return args; } + +export type RawRangeReply = [ + start: number, + end: number +]; + +export interface RangeReply { + start: number; + end: number; +} + +export function transformRangeReply([start, end]: RawRangeReply): RangeReply { + return { + start, + end + }; +} From d8db97498025555a9493b452d0ed288198e5cf9f Mon Sep 17 00:00:00 2001 From: Leibale Eidelman Date: Thu, 12 May 2022 12:56:09 -0400 Subject: [PATCH 1218/1748] fix #1914 - CLIENT TRACKING (#2126) --- packages/client/lib/client/commands.ts | 3 + .../lib/commands/CLIENT_TRACKING.spec.ts | 101 ++++++++++++++++++ .../client/lib/commands/CLIENT_TRACKING.ts | 83 ++++++++++++++ 3 files changed, 187 insertions(+) create mode 100644 packages/client/lib/commands/CLIENT_TRACKING.spec.ts create mode 100644 packages/client/lib/commands/CLIENT_TRACKING.ts diff --git a/packages/client/lib/client/commands.ts b/packages/client/lib/client/commands.ts index e2b0a8cd27c..0ec33583bd3 100644 --- a/packages/client/lib/client/commands.ts +++ b/packages/client/lib/client/commands.ts @@ -24,6 +24,7 @@ import * as CLIENT_KILL from '../commands/CLIENT_KILL'; import * as CLIENT_NO_EVICT from '../commands/CLIENT_NO-EVICT'; import * as CLIENT_PAUSE from '../commands/CLIENT_PAUSE'; import * as CLIENT_SETNAME from '../commands/CLIENT_SETNAME'; +import * as CLIENT_TRACKING from '../commands/CLIENT_TRACKING'; import * as CLIENT_UNPAUSE from '../commands/CLIENT_UNPAUSE'; import * as CLIENT_INFO from '../commands/CLIENT_INFO'; import * as CLUSTER_ADDSLOTS from '../commands/CLUSTER_ADDSLOTS'; @@ -165,6 +166,8 @@ export default { clientPause: CLIENT_PAUSE, CLIENT_SETNAME, clientSetName: CLIENT_SETNAME, + CLIENT_TRACKING, + clientTracking: CLIENT_TRACKING, CLIENT_UNPAUSE, clientUnpause: CLIENT_UNPAUSE, CLIENT_INFO, diff --git a/packages/client/lib/commands/CLIENT_TRACKING.spec.ts b/packages/client/lib/commands/CLIENT_TRACKING.spec.ts new file mode 100644 index 00000000000..bbd0b13e777 --- /dev/null +++ b/packages/client/lib/commands/CLIENT_TRACKING.spec.ts @@ -0,0 +1,101 @@ +import { strict as assert } from 'assert'; +import testUtils, { GLOBAL } from '../test-utils'; +import { transformArguments } from './CLIENT_TRACKING'; + +describe('CLIENT TRACKING', () => { + testUtils.isVersionGreaterThanHook([6]); + + describe('transformArguments', () => { + describe('true', () => { + it('simple', () => { + assert.deepEqual( + transformArguments(true), + ['CLIENT', 'TRACKING', 'ON'] + ); + }); + + it('with REDIRECT', () => { + assert.deepEqual( + transformArguments(true, { + REDIRECT: 1 + }), + ['CLIENT', 'TRACKING', 'ON', 'REDIRECT', '1'] + ); + }); + + describe('with BCAST', () => { + it('simple', () => { + assert.deepEqual( + transformArguments(true, { + BCAST: true + }), + ['CLIENT', 'TRACKING', 'ON', 'BCAST'] + ); + }); + + describe('with PREFIX', () => { + it('string', () => { + assert.deepEqual( + transformArguments(true, { + BCAST: true, + PREFIX: 'prefix' + }), + ['CLIENT', 'TRACKING', 'ON', 'BCAST', 'PREFIX', 'prefix'] + ); + }); + + it('array', () => { + assert.deepEqual( + transformArguments(true, { + BCAST: true, + PREFIX: ['1', '2'] + }), + ['CLIENT', 'TRACKING', 'ON', 'BCAST', 'PREFIX', '1', 'PREFIX', '2'] + ); + }); + }); + }); + + it('with OPTIN', () => { + assert.deepEqual( + transformArguments(true, { + OPTIN: true + }), + ['CLIENT', 'TRACKING', 'ON', 'OPTIN'] + ); + }); + + it('with OPTOUT', () => { + assert.deepEqual( + transformArguments(true, { + OPTOUT: true + }), + ['CLIENT', 'TRACKING', 'ON', 'OPTOUT'] + ); + }); + + it('with NOLOOP', () => { + assert.deepEqual( + transformArguments(true, { + NOLOOP: true + }), + ['CLIENT', 'TRACKING', 'ON', 'NOLOOP'] + ); + }); + }); + + it('false', () => { + assert.deepEqual( + transformArguments(false), + ['CLIENT', 'TRACKING', 'OFF'] + ); + }); + }); + + testUtils.testWithClient('client.clientTracking', async client => { + assert.equal( + await client.clientTracking(false), + 'OK' + ); + }, GLOBAL.SERVERS.OPEN); +}); diff --git a/packages/client/lib/commands/CLIENT_TRACKING.ts b/packages/client/lib/commands/CLIENT_TRACKING.ts new file mode 100644 index 00000000000..c70702706e4 --- /dev/null +++ b/packages/client/lib/commands/CLIENT_TRACKING.ts @@ -0,0 +1,83 @@ +import { RedisCommandArgument, RedisCommandArguments } from '.'; + +interface CommonOptions { + REDIRECT?: number; + NOLOOP?: boolean; +} + +interface BroadcastOptions { + BCAST?: boolean; + PREFIX?: RedisCommandArgument | Array; +} + +interface OptInOptions { + OPTIN?: boolean; +} + +interface OptOutOptions { + OPTOUT?: boolean; +} + +type ClientTrackingOptions = CommonOptions & ( + BroadcastOptions | + OptInOptions | + OptOutOptions +); + +export function transformArguments( + mode: M, + options?: M extends true ? ClientTrackingOptions : undefined +): RedisCommandArguments { + const args: RedisCommandArguments = [ + 'CLIENT', + 'TRACKING', + mode ? 'ON' : 'OFF' + ]; + + if (mode) { + if (options?.REDIRECT) { + args.push( + 'REDIRECT', + options.REDIRECT.toString() + ); + } + + if (isBroadcast(options)) { + args.push('BCAST'); + + if (options?.PREFIX) { + if (Array.isArray(options.PREFIX)) { + for (const prefix of options.PREFIX) { + args.push('PREFIX', prefix); + } + } else { + args.push('PREFIX', options.PREFIX); + } + } + } else if (isOptIn(options)) { + args.push('OPTIN'); + } else if (isOptOut(options)) { + args.push('OPTOUT'); + } + + if (options?.NOLOOP) { + args.push('NOLOOP'); + } + } + + return args; +} + +function isBroadcast(options?: ClientTrackingOptions): options is BroadcastOptions { + return (options as BroadcastOptions)?.BCAST === true; +} + +function isOptIn(options?: ClientTrackingOptions): options is OptInOptions { + return (options as OptInOptions)?.OPTIN === true; +} + +function isOptOut(options?: ClientTrackingOptions): options is OptOutOptions { + return (options as OptOutOptions)?.OPTOUT === true; +} + +export declare function transformReply(): 'OK' | Buffer; From 7196b907e512c521f06c3e064982d0389578d0d0 Mon Sep 17 00:00:00 2001 From: Leibale Eidelman Date: Thu, 12 May 2022 13:05:27 -0400 Subject: [PATCH 1219/1748] fix #1915 - CLIENT TRACKINGINFO (#2127) * fix #1915 - CLIENT TRACKINGINFO * remove .only --- packages/client/lib/client/commands.ts | 3 ++ .../lib/commands/CLIENT_TRACKINGINFO.spec.ts | 25 +++++++++++++++++ .../lib/commands/CLIENT_TRACKINGINFO.ts | 28 +++++++++++++++++++ 3 files changed, 56 insertions(+) create mode 100644 packages/client/lib/commands/CLIENT_TRACKINGINFO.spec.ts create mode 100644 packages/client/lib/commands/CLIENT_TRACKINGINFO.ts diff --git a/packages/client/lib/client/commands.ts b/packages/client/lib/client/commands.ts index 0ec33583bd3..e164639ae91 100644 --- a/packages/client/lib/client/commands.ts +++ b/packages/client/lib/client/commands.ts @@ -25,6 +25,7 @@ import * as CLIENT_NO_EVICT from '../commands/CLIENT_NO-EVICT'; import * as CLIENT_PAUSE from '../commands/CLIENT_PAUSE'; import * as CLIENT_SETNAME from '../commands/CLIENT_SETNAME'; import * as CLIENT_TRACKING from '../commands/CLIENT_TRACKING'; +import * as CLIENT_TRACKINGINFO from '../commands/CLIENT_TRACKINGINFO'; import * as CLIENT_UNPAUSE from '../commands/CLIENT_UNPAUSE'; import * as CLIENT_INFO from '../commands/CLIENT_INFO'; import * as CLUSTER_ADDSLOTS from '../commands/CLUSTER_ADDSLOTS'; @@ -168,6 +169,8 @@ export default { clientSetName: CLIENT_SETNAME, CLIENT_TRACKING, clientTracking: CLIENT_TRACKING, + CLIENT_TRACKINGINFO, + clientTrackingInfo: CLIENT_TRACKINGINFO, CLIENT_UNPAUSE, clientUnpause: CLIENT_UNPAUSE, CLIENT_INFO, diff --git a/packages/client/lib/commands/CLIENT_TRACKINGINFO.spec.ts b/packages/client/lib/commands/CLIENT_TRACKINGINFO.spec.ts new file mode 100644 index 00000000000..49bffe7612d --- /dev/null +++ b/packages/client/lib/commands/CLIENT_TRACKINGINFO.spec.ts @@ -0,0 +1,25 @@ +import { strict as assert } from 'assert'; +import testUtils, { GLOBAL } from '../test-utils'; +import { transformArguments } from './CLIENT_TRACKINGINFO'; + +describe('CLIENT TRACKINGINFO', () => { + testUtils.isVersionGreaterThanHook([6, 2]); + + it('transformArguments', () => { + assert.deepEqual( + transformArguments(), + ['CLIENT', 'TRACKINGINFO'] + ); + }); + + testUtils.testWithClient('client.clientTrackingInfo', async client => { + assert.deepEqual( + await client.clientTrackingInfo(), + { + flags: new Set(['off']), + redirect: -1, + prefixes: [] + } + ); + }, GLOBAL.SERVERS.OPEN); +}); diff --git a/packages/client/lib/commands/CLIENT_TRACKINGINFO.ts b/packages/client/lib/commands/CLIENT_TRACKINGINFO.ts new file mode 100644 index 00000000000..7c883fc6997 --- /dev/null +++ b/packages/client/lib/commands/CLIENT_TRACKINGINFO.ts @@ -0,0 +1,28 @@ +import { RedisCommandArguments } from '.'; + +export function transformArguments(): RedisCommandArguments { + return ['CLIENT', 'TRACKINGINFO']; +} + +type RawReply = [ + 'flags', + Array, + 'redirect', + number, + 'prefixes', + Array +]; + +interface Reply { + flags: Set; + redirect: number; + prefixes: Array; +} + +export function transformReply(reply: RawReply): Reply { + return { + flags: new Set(reply[1]), + redirect: reply[3], + prefixes: reply[5] + }; +} From bf80c163b1305a859baa3dbc0bd6488c300e7a4f Mon Sep 17 00:00:00 2001 From: Leibale Eidelman Date: Thu, 26 May 2022 09:55:47 -0400 Subject: [PATCH 1220/1748] fix #2046 - add support for multi in select (#2133) * fix #2046 - add support for multi in select * fix "Argument of type 'symbol | undefined' is not assignable to parameter of type 'number | undefined'" --- packages/client/lib/client/index.spec.ts | 14 ++++++++++ packages/client/lib/client/index.ts | 18 ++++++++---- packages/client/lib/client/multi-command.ts | 31 ++++++++++++++++----- packages/client/lib/cluster/index.ts | 2 +- 4 files changed, 52 insertions(+), 13 deletions(-) diff --git a/packages/client/lib/client/index.spec.ts b/packages/client/lib/client/index.spec.ts index 0b5fd05b9b2..442b10ef521 100644 --- a/packages/client/lib/client/index.spec.ts +++ b/packages/client/lib/client/index.spec.ts @@ -468,6 +468,20 @@ describe('Client', () => { ['PONG'] ); }, GLOBAL.SERVERS.OPEN); + + testUtils.testWithClient('should remember selected db', async client => { + await client.multi() + .select(1) + .exec(); + await killClient(client); + assert.equal( + (await client.clientInfo()).db, + 1 + ); + }, { + ...GLOBAL.SERVERS.OPEN, + minimumDockerVersion: [6, 2] // CLIENT INFO + }); }); testUtils.testWithClient('scripts', async client => { diff --git a/packages/client/lib/client/index.ts b/packages/client/lib/client/index.ts index fb86908bd2f..9664b3645b1 100644 --- a/packages/client/lib/client/index.ts +++ b/packages/client/lib/client/index.ts @@ -606,18 +606,26 @@ export default class RedisClient< ); } - multiExecutor(commands: Array, chainId?: symbol): Promise> { + async multiExecutor( + commands: Array, + selectedDB?: number, + chainId?: symbol + ): Promise> { const promise = Promise.all( commands.map(({ args }) => { - return this.#queue.addCommand(args, RedisClient.commandOptions({ - chainId - })); + return this.#queue.addCommand(args, { chainId }); }) ); this.#tick(); - return promise; + const results = await promise; + + if (selectedDB !== undefined) { + this.#selectedDB = selectedDB; + } + + return results; } async* scanIterator(options?: ScanCommandOptions): AsyncIterable { diff --git a/packages/client/lib/client/multi-command.ts b/packages/client/lib/client/multi-command.ts index 2eea429abec..1d6df1a483e 100644 --- a/packages/client/lib/client/multi-command.ts +++ b/packages/client/lib/client/multi-command.ts @@ -58,13 +58,13 @@ type InstantiableRedisMultiCommand< S extends RedisScripts > = new (...args: ConstructorParameters) => RedisClientMultiCommandType; - -export type RedisClientMultiExecutor = (queue: Array, chainId?: symbol) => Promise>; +export type RedisClientMultiExecutor = ( + queue: Array, + selectedDB?: number, + chainId?: symbol +) => Promise>; export default class RedisClientMultiCommand { - readonly #multi = new RedisMultiCommand(); - readonly #executor: RedisClientMultiExecutor; - static extend< M extends RedisModules, F extends RedisFunctions, @@ -81,7 +81,10 @@ export default class RedisClientMultiCommand { }); } + readonly #multi = new RedisMultiCommand(); + readonly #executor: RedisClientMultiExecutor; readonly v4: Record = {}; + #selectedDB?: number; constructor(executor: RedisClientMultiExecutor, legacyMode = false) { this.#executor = executor; @@ -136,6 +139,13 @@ export default class RedisClientMultiCommand { ); } + SELECT(db: number, transformReply?: RedisCommand['transformReply']): this { + this.#selectedDB = db; + return this.addCommand(['SELECT', db.toString()], transformReply); + } + + select = this.SELECT; + addCommand(args: RedisCommandArguments, transformReply?: RedisCommand['transformReply']): this { this.#multi.addCommand(args, transformReply); return this; @@ -160,7 +170,11 @@ export default class RedisClientMultiCommand { if (!commands) return []; return this.#multi.handleExecReplies( - await this.#executor(commands, RedisMultiCommand.generateChainId()) + await this.#executor( + commands, + this.#selectedDB, + RedisMultiCommand.generateChainId() + ) ); } @@ -168,7 +182,10 @@ export default class RedisClientMultiCommand { async execAsPipeline(): Promise> { return this.#multi.transformReplies( - await this.#executor(this.#multi.queue) + await this.#executor( + this.#multi.queue, + this.#selectedDB + ) ); } } diff --git a/packages/client/lib/cluster/index.ts b/packages/client/lib/cluster/index.ts index f1a4cb42af3..57ec6ff7050 100644 --- a/packages/client/lib/cluster/index.ts +++ b/packages/client/lib/cluster/index.ts @@ -230,7 +230,7 @@ export default class RedisCluster< return this.#execute( firstKey, false, - client => client.multiExecutor(commands, chainId) + client => client.multiExecutor(commands, undefined, chainId) ); }, routing From 82f43d9a53c3c98e09297b04bf2ba59071d83806 Mon Sep 17 00:00:00 2001 From: Tom Graham Date: Thu, 26 May 2022 23:56:10 +1000 Subject: [PATCH 1221/1748] Fix issue with buffers in objects using hSet (#2139) * Fix issue with buffers in objects using hSet When using hSet with an object, any buffer values inside the object are converted to strings instead of left as buffers. This fix specifically handles the special case of buffers, whilst casting everything else strings (to continue "gracefully" handling the case where the value not a valid type). * Update HSET.ts * Update HSET.spec.ts Co-authored-by: Leibale Eidelman --- packages/client/lib/commands/HSET.spec.ts | 21 +++++++++++----- packages/client/lib/commands/HSET.ts | 29 +++++++++++++---------- 2 files changed, 32 insertions(+), 18 deletions(-) diff --git a/packages/client/lib/commands/HSET.spec.ts b/packages/client/lib/commands/HSET.spec.ts index e4160810810..73bc966f87a 100644 --- a/packages/client/lib/commands/HSET.spec.ts +++ b/packages/client/lib/commands/HSET.spec.ts @@ -41,11 +41,20 @@ describe('HSET', () => { ); }); - it('Object', () => { - assert.deepEqual( - transformArguments('key', { field: 'value' }), - ['HSET', 'key', 'field', 'value'] - ); + describe('Object', () => { + it('string', () => { + assert.deepEqual( + transformArguments('key', { field: 'value' }), + ['HSET', 'key', 'field', 'value'] + ); + }); + + it('Buffer', () => { + assert.deepEqual( + transformArguments('key', { field: Buffer.from('value') }), + ['HSET', 'key', 'field', Buffer.from('value')] + ); + }); }); }); @@ -62,4 +71,4 @@ describe('HSET', () => { 1 ); }, GLOBAL.CLUSTERS.OPEN); -}); \ No newline at end of file +}); diff --git a/packages/client/lib/commands/HSET.ts b/packages/client/lib/commands/HSET.ts index 1fe1743b6a2..261ef98c779 100644 --- a/packages/client/lib/commands/HSET.ts +++ b/packages/client/lib/commands/HSET.ts @@ -20,8 +20,10 @@ export function transformArguments(...[ key, value, fieldValue ]: SingleFieldArg const args: RedisCommandArguments = ['HSET', key]; if (typeof value === 'string' || typeof value === 'number' || Buffer.isBuffer(value)) { - pushValue(args, value); - pushValue(args, fieldValue!); + args.push( + convertValue(value), + convertValue(fieldValue!) + ); } else if (value instanceof Map) { pushMap(args, value); } else if (Array.isArray(value)) { @@ -35,8 +37,10 @@ export function transformArguments(...[ key, value, fieldValue ]: SingleFieldArg function pushMap(args: RedisCommandArguments, map: HSETMap): void { for (const [key, value] of map.entries()) { - pushValue(args, key); - pushValue(args, value); + args.push( + convertValue(key), + convertValue(value) + ); } } @@ -47,22 +51,23 @@ function pushTuples(args: RedisCommandArguments, tuples: HSETTuples): void { continue; } - pushValue(args, tuple); + args.push(convertValue(tuple)); } } function pushObject(args: RedisCommandArguments, object: HSETObject): void { for (const key of Object.keys(object)) { - args.push(key.toString(), object[key].toString()); + args.push( + convertValue(key), + convertValue(object[key]) + ); } } -function pushValue(args: RedisCommandArguments, value: Types): void { - args.push( - typeof value === 'number' ? - value.toString() : - value - ); +function convertValue(value: Types): RedisCommandArgument { + return typeof value === 'number' ? + value.toString() : + value; } export declare function transformReply(): number; From f269319f426708138267a913a8cbdd700907c2c0 Mon Sep 17 00:00:00 2001 From: Simon Prickett Date: Thu, 26 May 2022 14:57:08 +0100 Subject: [PATCH 1222/1748] Updated search example to show sorting. (#2148) * Updated search example to show sorting. * Fixed example response. --- examples/search-hashes.js | 37 +++++++++++++++++++++++-------------- 1 file changed, 23 insertions(+), 14 deletions(-) diff --git a/examples/search-hashes.js b/examples/search-hashes.js index ded4b0edb95..0acde8cbe20 100644 --- a/examples/search-hashes.js +++ b/examples/search-hashes.js @@ -40,29 +40,38 @@ async function searchHashes() { client.hSet('noderedis:animals:4', {name: 'Fido', species: 'dog', age: 7}) ]); - // Perform a search query, find all the dogs... + // Perform a search query, find all the dogs... sort by age, descending. // 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}'); + const results = await client.ft.search( + 'idx:animals', + '@species:{dog}', + { + SORTBY: { + BY: 'age', + DIRECTION: 'DESC' // or 'ASC' (default if DIRECTION is not present) + } + } + ); // results: // { // total: 2, // documents: [ - // { - // id: 'noderedis:animals:4', + // { + // id: 'noderedis:animals:3', // value: { - // name: 'Fido', - // species: 'dog', - // age: '7' + // age: '9', + // name: 'Rover', + // species: 'dog' // } // }, // { - // id: 'noderedis:animals:3', + // id: 'noderedis:animals:4', // value: { - // name: 'Rover', - // species: 'dog', - // age: '9' + // age: '7', + // name: 'Fido', + // species: 'dog' // } // } // ] @@ -71,9 +80,9 @@ async function searchHashes() { 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}`); + // noderedis:animals:3: Rover, 9 years old. + // noderedis:animals:4: Fido, 7 years old. + console.log(`${doc.id}: ${doc.value.name}, ${doc.value.age} years old.`); } await client.quit(); From e1c13f874a764077f24559de6414edad1dd66ffb Mon Sep 17 00:00:00 2001 From: Simon Prickett Date: Wed, 15 Jun 2022 20:18:36 +0100 Subject: [PATCH 1223/1748] Fixed erroneous reference to Top-K (#2158) --- examples/count-min-sketch.js | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/examples/count-min-sketch.js b/examples/count-min-sketch.js index 63c8808cc24..7deb644355d 100644 --- a/examples/count-min-sketch.js +++ b/examples/count-min-sketch.js @@ -15,7 +15,7 @@ async function countMinSketch() { // https://oss.redis.com/redisbloom/CountMinSketch_Commands/#cmsinitbyprob try { await client.cms.initByProb('mycms', 0.001, 0.01); - console.log('Reserved Top K.'); + console.log('Initialized Count-Min Sketch.'); } catch (e) { console.log('Error, maybe RedisBloom is not installed?:'); console.log(e); From 5fefcca8dfbe2c4a1fbb1b2815bd2d8424cb2f5f Mon Sep 17 00:00:00 2001 From: Simon Prickett Date: Thu, 30 Jun 2022 17:53:26 +0100 Subject: [PATCH 1224/1748] Fixed a small typo. (#2168) --- examples/search-hashes.js | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/examples/search-hashes.js b/examples/search-hashes.js index 0acde8cbe20..46d5e598cf8 100644 --- a/examples/search-hashes.js +++ b/examples/search-hashes.js @@ -42,7 +42,7 @@ async function searchHashes() { // Perform a search query, find all the dogs... sort by age, descending. // Documentation: https://oss.redis.com/redisearch/Commands/#ftsearch - // Query synatax: https://oss.redis.com/redisearch/Query_Syntax/ + // Query syntax: https://oss.redis.com/redisearch/Query_Syntax/ const results = await client.ft.search( 'idx:animals', '@species:{dog}', @@ -88,4 +88,4 @@ async function searchHashes() { await client.quit(); } -searchHashes(); \ No newline at end of file +searchHashes(); From 6a850d36ae3971f135532f90831997e392b59ed6 Mon Sep 17 00:00:00 2001 From: Leibale Eidelman Date: Thu, 30 Jun 2022 13:07:29 -0400 Subject: [PATCH 1225/1748] upgrade deps, fix "invalid save argument" error (#2174) --- benchmark/package-lock.json | 72 +- benchmark/package.json | 4 +- package-lock.json | 2551 ++++++++++++------------ packages/bloom/package.json | 10 +- packages/client/lib/test-utils.ts | 2 +- packages/client/package.json | 20 +- packages/graph/lib/test-utils.ts | 2 +- packages/graph/package.json | 10 +- packages/json/lib/test-utils.ts | 2 +- packages/json/package.json | 10 +- packages/search/lib/test-utils.ts | 2 +- packages/search/package.json | 10 +- packages/test-utils/lib/dockers.ts | 2 +- packages/test-utils/package.json | 8 +- packages/time-series/lib/test-utils.ts | 2 +- packages/time-series/package.json | 10 +- 16 files changed, 1318 insertions(+), 1399 deletions(-) diff --git a/benchmark/package-lock.json b/benchmark/package-lock.json index 72db34dad13..ee9db27c5b3 100644 --- a/benchmark/package-lock.json +++ b/benchmark/package-lock.json @@ -8,14 +8,14 @@ "dependencies": { "@redis/client": "../packages/client", "hdr-histogram-js": "3.0.0", - "ioredis": "5.0.4", + "ioredis": "5.1.0", "redis-v3": "npm:redis@3.1.2", - "yargs": "17.4.1" + "yargs": "17.5.1" } }, "../packages/client": { "name": "@redis/client", - "version": "1.0.5", + "version": "1.1.0", "license": "MIT", "dependencies": { "cluster-key-slot": "1.1.0", @@ -25,22 +25,22 @@ "devDependencies": { "@istanbuljs/nyc-config-typescript": "^1.0.2", "@redis/test-utils": "*", - "@types/node": "^17.0.29", - "@types/sinon": "^10.0.11", + "@types/node": "^18.0.0", + "@types/sinon": "^10.0.12", "@types/yallist": "^4.0.1", - "@typescript-eslint/eslint-plugin": "^5.21.0", - "@typescript-eslint/parser": "^5.21.0", - "eslint": "^8.14.0", + "@typescript-eslint/eslint-plugin": "^5.30.0", + "@typescript-eslint/parser": "^5.30.0", + "eslint": "^8.18.0", "nyc": "^15.1.0", - "release-it": "^14.14.2", - "sinon": "^13.0.2", + "release-it": "^15.1.1", + "sinon": "^14.0.0", "source-map-support": "^0.5.21", - "ts-node": "^10.7.0", - "typedoc": "^0.22.15", - "typescript": "^4.6.3" + "ts-node": "^10.8.1", + "typedoc": "^0.23.2", + "typescript": "^4.7.4" }, "engines": { - "node": ">=12" + "node": ">=14" } }, "node_modules/@assemblyscript/loader": { @@ -191,9 +191,9 @@ } }, "node_modules/ioredis": { - "version": "5.0.4", - "resolved": "https://registry.npmjs.org/ioredis/-/ioredis-5.0.4.tgz", - "integrity": "sha512-qFJw3MnPNsJF1lcIOP3vztbsasOXK3nDdNAgjQj7t7/Bn/w10PGchTOpqylQNxjzPbLoYDu34LjeJtSWiKBntQ==", + "version": "5.1.0", + "resolved": "https://registry.npmjs.org/ioredis/-/ioredis-5.1.0.tgz", + "integrity": "sha512-HYHnvwxFwefeUBj0hZFejLvd8Q/YNAfnZlZG/hSRxkRhXMs1H8soMEVccHd1WlLrKkynorXBsAtqDGskOdAfVQ==", "dependencies": { "@ioredis/commands": "^1.1.1", "cluster-key-slot": "^1.1.0", @@ -354,9 +354,9 @@ } }, "node_modules/yargs": { - "version": "17.4.1", - "resolved": "https://registry.npmjs.org/yargs/-/yargs-17.4.1.tgz", - "integrity": "sha512-WSZD9jgobAg3ZKuCQZSa3g9QOJeCCqLoLAykiWgmXnDo9EPnn4RPf5qVTtzgOx66o6/oqhcA5tHtJXpG8pMt3g==", + "version": "17.5.1", + "resolved": "https://registry.npmjs.org/yargs/-/yargs-17.5.1.tgz", + "integrity": "sha512-t6YAJcxDkNX7NFYiVtKvWUz8l+PaKTLiL63mJYWR2GnHq2gjEWISzsLp9wg3aY36dY1j+gfIEL3pIF+XlJJfbA==", "dependencies": { "cliui": "^7.0.2", "escalade": "^3.1.1", @@ -395,21 +395,21 @@ "requires": { "@istanbuljs/nyc-config-typescript": "^1.0.2", "@redis/test-utils": "*", - "@types/node": "^17.0.29", - "@types/sinon": "^10.0.11", + "@types/node": "^18.0.0", + "@types/sinon": "^10.0.12", "@types/yallist": "^4.0.1", - "@typescript-eslint/eslint-plugin": "^5.21.0", - "@typescript-eslint/parser": "^5.21.0", + "@typescript-eslint/eslint-plugin": "^5.30.0", + "@typescript-eslint/parser": "^5.30.0", "cluster-key-slot": "1.1.0", - "eslint": "^8.14.0", + "eslint": "^8.18.0", "generic-pool": "3.8.2", "nyc": "^15.1.0", - "release-it": "^14.14.2", - "sinon": "^13.0.2", + "release-it": "^15.1.1", + "sinon": "^14.0.0", "source-map-support": "^0.5.21", - "ts-node": "^10.7.0", - "typedoc": "^0.22.15", - "typescript": "^4.6.3", + "ts-node": "^10.8.1", + "typedoc": "^0.23.2", + "typescript": "^4.7.4", "yallist": "4.0.0" } }, @@ -498,9 +498,9 @@ } }, "ioredis": { - "version": "5.0.4", - "resolved": "https://registry.npmjs.org/ioredis/-/ioredis-5.0.4.tgz", - "integrity": "sha512-qFJw3MnPNsJF1lcIOP3vztbsasOXK3nDdNAgjQj7t7/Bn/w10PGchTOpqylQNxjzPbLoYDu34LjeJtSWiKBntQ==", + "version": "5.1.0", + "resolved": "https://registry.npmjs.org/ioredis/-/ioredis-5.1.0.tgz", + "integrity": "sha512-HYHnvwxFwefeUBj0hZFejLvd8Q/YNAfnZlZG/hSRxkRhXMs1H8soMEVccHd1WlLrKkynorXBsAtqDGskOdAfVQ==", "requires": { "@ioredis/commands": "^1.1.1", "cluster-key-slot": "^1.1.0", @@ -618,9 +618,9 @@ "integrity": "sha512-0pfFzegeDWJHJIAmTLRP2DwHjdF5s7jo9tuztdQxAhINCdvS+3nGINqPd00AphqJR/0LhANUS6/+7SCb98YOfA==" }, "yargs": { - "version": "17.4.1", - "resolved": "https://registry.npmjs.org/yargs/-/yargs-17.4.1.tgz", - "integrity": "sha512-WSZD9jgobAg3ZKuCQZSa3g9QOJeCCqLoLAykiWgmXnDo9EPnn4RPf5qVTtzgOx66o6/oqhcA5tHtJXpG8pMt3g==", + "version": "17.5.1", + "resolved": "https://registry.npmjs.org/yargs/-/yargs-17.5.1.tgz", + "integrity": "sha512-t6YAJcxDkNX7NFYiVtKvWUz8l+PaKTLiL63mJYWR2GnHq2gjEWISzsLp9wg3aY36dY1j+gfIEL3pIF+XlJJfbA==", "requires": { "cliui": "^7.0.2", "escalade": "^3.1.1", diff --git a/benchmark/package.json b/benchmark/package.json index da177dbc7a1..3d5b2ed030e 100644 --- a/benchmark/package.json +++ b/benchmark/package.json @@ -9,8 +9,8 @@ "dependencies": { "@redis/client": "../packages/client", "hdr-histogram-js": "3.0.0", - "ioredis": "5.0.4", + "ioredis": "5.1.0", "redis-v3": "npm:redis@3.1.2", - "yargs": "17.4.1" + "yargs": "17.5.1" } } diff --git a/package-lock.json b/package-lock.json index ce69f95d53d..b4781815daf 100644 --- a/package-lock.json +++ b/package-lock.json @@ -391,41 +391,32 @@ "node": ">=6.9.0" } }, - "node_modules/@cspotcode/source-map-consumer": { - "version": "0.8.0", - "resolved": "https://registry.npmjs.org/@cspotcode/source-map-consumer/-/source-map-consumer-0.8.0.tgz", - "integrity": "sha512-41qniHzTU8yAGbCp04ohlmSrZf8bkf/iJsl3V0dRGsQN/5GFfx+LbCSsCpp2gqrqjTVg/K6O8ycoV35JIwAzAg==", - "dev": true, - "engines": { - "node": ">= 12" - } - }, "node_modules/@cspotcode/source-map-support": { - "version": "0.7.0", - "resolved": "https://registry.npmjs.org/@cspotcode/source-map-support/-/source-map-support-0.7.0.tgz", - "integrity": "sha512-X4xqRHqN8ACt2aHVe51OxeA2HjbcL4MqFqXkrmQszJ1NOUuUu5u6Vqx/0lZSVNku7velL5FC/s5uEAj1lsBMhA==", + "version": "0.8.1", + "resolved": "https://registry.npmjs.org/@cspotcode/source-map-support/-/source-map-support-0.8.1.tgz", + "integrity": "sha512-IchNf6dN4tHoMFIn/7OE8LWZ19Y6q/67Bmf6vnGREv8RSbBVb9LPJxEcnwrcwX6ixSvaiGoomAUvu4YSxXrVgw==", "dev": true, "dependencies": { - "@cspotcode/source-map-consumer": "0.8.0" + "@jridgewell/trace-mapping": "0.3.9" }, "engines": { "node": ">=12" } }, "node_modules/@eslint/eslintrc": { - "version": "1.2.2", - "resolved": "https://registry.npmjs.org/@eslint/eslintrc/-/eslintrc-1.2.2.tgz", - "integrity": "sha512-lTVWHs7O2hjBFZunXTZYnYqtB9GakA1lnxIf+gKq2nY5gxkkNi/lQvveW6t8gFdOHTg6nG50Xs95PrLqVpcaLg==", + "version": "1.3.0", + "resolved": "https://registry.npmjs.org/@eslint/eslintrc/-/eslintrc-1.3.0.tgz", + "integrity": "sha512-UWW0TMTmk2d7hLcWD1/e2g5HDM/HQ3csaLSqXCfqwh4uNDuNqlaKWXmEsL4Cs41Z0KnILNvwbHAah3C2yt06kw==", "dev": true, "dependencies": { "ajv": "^6.12.4", "debug": "^4.3.2", - "espree": "^9.3.1", - "globals": "^13.9.0", + "espree": "^9.3.2", + "globals": "^13.15.0", "ignore": "^5.2.0", "import-fresh": "^3.2.1", "js-yaml": "^4.1.0", - "minimatch": "^3.0.4", + "minimatch": "^3.1.2", "strip-json-comments": "^3.1.1" }, "engines": { @@ -743,6 +734,31 @@ "@octokit/openapi-types": "^11.2.0" } }, + "node_modules/@pnpm/network.ca-file": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/@pnpm/network.ca-file/-/network.ca-file-1.0.1.tgz", + "integrity": "sha512-gkINruT2KUhZLTaiHxwCOh1O4NVnFT0wLjWFBHmTz9vpKag/C/noIMJXBxFe4F0mYpUVX2puLwAieLYFg2NvoA==", + "dev": true, + "dependencies": { + "graceful-fs": "4.2.10" + }, + "engines": { + "node": ">=12.22.0" + } + }, + "node_modules/@pnpm/npm-conf": { + "version": "1.0.4", + "resolved": "https://registry.npmjs.org/@pnpm/npm-conf/-/npm-conf-1.0.4.tgz", + "integrity": "sha512-o5YFq/+ksEJMbSzzkaQDHlp00aonLDU5xNPVTRL12hTWBbVSSeWXxPukq75h+mvXnoOWT95vV2u1HSTw2C4XOw==", + "dev": true, + "dependencies": { + "@pnpm/network.ca-file": "^1.0.1", + "config-chain": "^1.1.11" + }, + "engines": { + "node": ">=12" + } + }, "node_modules/@redis/bloom": { "resolved": "packages/bloom", "link": true @@ -909,9 +925,9 @@ "dev": true }, "node_modules/@types/node": { - "version": "17.0.31", - "resolved": "https://registry.npmjs.org/@types/node/-/node-17.0.31.tgz", - "integrity": "sha512-AR0x5HbXGqkEx9CadRH3EBYx/VkiUgZIhP4wvPn/+5KIsgpNoyFaRlVe0Zlx9gRtg8fA06a9tskE2MSN7TcG4Q==", + "version": "18.0.0", + "resolved": "https://registry.npmjs.org/@types/node/-/node-18.0.0.tgz", + "integrity": "sha512-cHlGmko4gWLVI27cGJntjs/Sj8th9aYwplmZFwmmgYQQvL5NUsgVJG7OddLvNfLqYS31KFN0s3qlaD9qCaxACA==", "dev": true }, "node_modules/@types/parse-json": { @@ -930,9 +946,9 @@ } }, "node_modules/@types/sinon": { - "version": "10.0.11", - "resolved": "https://registry.npmjs.org/@types/sinon/-/sinon-10.0.11.tgz", - "integrity": "sha512-dmZsHlBsKUtBpHriNjlK0ndlvEh8dcb9uV9Afsbt89QIyydpC7NcR+nWlAhASfy3GHnxTl4FX/aKE7XZUt/B4g==", + "version": "10.0.12", + "resolved": "https://registry.npmjs.org/@types/sinon/-/sinon-10.0.12.tgz", + "integrity": "sha512-uWf4QJ4oky/GckJ1MYQxU52cgVDcXwBhDkpvLbi4EKoLPqLE4MOH6T/ttM33l3hi0oZ882G6oIzWv/oupRYSxQ==", "dev": true, "dependencies": { "@types/sinonjs__fake-timers": "*" @@ -966,19 +982,19 @@ "dev": true }, "node_modules/@typescript-eslint/eslint-plugin": { - "version": "5.21.0", - "resolved": "https://registry.npmjs.org/@typescript-eslint/eslint-plugin/-/eslint-plugin-5.21.0.tgz", - "integrity": "sha512-fTU85q8v5ZLpoZEyn/u1S2qrFOhi33Edo2CZ0+q1gDaWWm0JuPh3bgOyU8lM0edIEYgKLDkPFiZX2MOupgjlyg==", + "version": "5.30.0", + "resolved": "https://registry.npmjs.org/@typescript-eslint/eslint-plugin/-/eslint-plugin-5.30.0.tgz", + "integrity": "sha512-lvhRJ2pGe2V9MEU46ELTdiHgiAFZPKtLhiU5wlnaYpMc2+c1R8fh8i80ZAa665drvjHKUJyRRGg3gEm1If54ow==", "dev": true, "dependencies": { - "@typescript-eslint/scope-manager": "5.21.0", - "@typescript-eslint/type-utils": "5.21.0", - "@typescript-eslint/utils": "5.21.0", - "debug": "^4.3.2", + "@typescript-eslint/scope-manager": "5.30.0", + "@typescript-eslint/type-utils": "5.30.0", + "@typescript-eslint/utils": "5.30.0", + "debug": "^4.3.4", "functional-red-black-tree": "^1.0.1", - "ignore": "^5.1.8", + "ignore": "^5.2.0", "regexpp": "^3.2.0", - "semver": "^7.3.5", + "semver": "^7.3.7", "tsutils": "^3.21.0" }, "engines": { @@ -1032,15 +1048,15 @@ "dev": true }, "node_modules/@typescript-eslint/parser": { - "version": "5.21.0", - "resolved": "https://registry.npmjs.org/@typescript-eslint/parser/-/parser-5.21.0.tgz", - "integrity": "sha512-8RUwTO77hstXUr3pZoWZbRQUxXcSXafZ8/5gpnQCfXvgmP9gpNlRGlWzvfbEQ14TLjmtU8eGnONkff8U2ui2Eg==", + "version": "5.30.0", + "resolved": "https://registry.npmjs.org/@typescript-eslint/parser/-/parser-5.30.0.tgz", + "integrity": "sha512-2oYYUws5o2liX6SrFQ5RB88+PuRymaM2EU02/9Ppoyu70vllPnHVO7ioxDdq/ypXHA277R04SVjxvwI8HmZpzA==", "dev": true, "dependencies": { - "@typescript-eslint/scope-manager": "5.21.0", - "@typescript-eslint/types": "5.21.0", - "@typescript-eslint/typescript-estree": "5.21.0", - "debug": "^4.3.2" + "@typescript-eslint/scope-manager": "5.30.0", + "@typescript-eslint/types": "5.30.0", + "@typescript-eslint/typescript-estree": "5.30.0", + "debug": "^4.3.4" }, "engines": { "node": "^12.22.0 || ^14.17.0 || >=16.0.0" @@ -1059,13 +1075,13 @@ } }, "node_modules/@typescript-eslint/scope-manager": { - "version": "5.21.0", - "resolved": "https://registry.npmjs.org/@typescript-eslint/scope-manager/-/scope-manager-5.21.0.tgz", - "integrity": "sha512-XTX0g0IhvzcH/e3393SvjRCfYQxgxtYzL3UREteUneo72EFlt7UNoiYnikUtmGVobTbhUDByhJ4xRBNe+34kOQ==", + "version": "5.30.0", + "resolved": "https://registry.npmjs.org/@typescript-eslint/scope-manager/-/scope-manager-5.30.0.tgz", + "integrity": "sha512-3TZxvlQcK5fhTBw5solQucWSJvonXf5yua5nx8OqK94hxdrT7/6W3/CS42MLd/f1BmlmmbGEgQcTHHCktUX5bQ==", "dev": true, "dependencies": { - "@typescript-eslint/types": "5.21.0", - "@typescript-eslint/visitor-keys": "5.21.0" + "@typescript-eslint/types": "5.30.0", + "@typescript-eslint/visitor-keys": "5.30.0" }, "engines": { "node": "^12.22.0 || ^14.17.0 || >=16.0.0" @@ -1076,13 +1092,13 @@ } }, "node_modules/@typescript-eslint/type-utils": { - "version": "5.21.0", - "resolved": "https://registry.npmjs.org/@typescript-eslint/type-utils/-/type-utils-5.21.0.tgz", - "integrity": "sha512-MxmLZj0tkGlkcZCSE17ORaHl8Th3JQwBzyXL/uvC6sNmu128LsgjTX0NIzy+wdH2J7Pd02GN8FaoudJntFvSOw==", + "version": "5.30.0", + "resolved": "https://registry.npmjs.org/@typescript-eslint/type-utils/-/type-utils-5.30.0.tgz", + "integrity": "sha512-GF8JZbZqSS+azehzlv/lmQQ3EU3VfWYzCczdZjJRxSEeXDQkqFhCBgFhallLDbPwQOEQ4MHpiPfkjKk7zlmeNg==", "dev": true, "dependencies": { - "@typescript-eslint/utils": "5.21.0", - "debug": "^4.3.2", + "@typescript-eslint/utils": "5.30.0", + "debug": "^4.3.4", "tsutils": "^3.21.0" }, "engines": { @@ -1102,9 +1118,9 @@ } }, "node_modules/@typescript-eslint/types": { - "version": "5.21.0", - "resolved": "https://registry.npmjs.org/@typescript-eslint/types/-/types-5.21.0.tgz", - "integrity": "sha512-XnOOo5Wc2cBlq8Lh5WNvAgHzpjnEzxn4CJBwGkcau7b/tZ556qrWXQz4DJyChYg8JZAD06kczrdgFPpEQZfDsA==", + "version": "5.30.0", + "resolved": "https://registry.npmjs.org/@typescript-eslint/types/-/types-5.30.0.tgz", + "integrity": "sha512-vfqcBrsRNWw/LBXyncMF/KrUTYYzzygCSsVqlZ1qGu1QtGs6vMkt3US0VNSQ05grXi5Yadp3qv5XZdYLjpp8ag==", "dev": true, "engines": { "node": "^12.22.0 || ^14.17.0 || >=16.0.0" @@ -1115,17 +1131,17 @@ } }, "node_modules/@typescript-eslint/typescript-estree": { - "version": "5.21.0", - "resolved": "https://registry.npmjs.org/@typescript-eslint/typescript-estree/-/typescript-estree-5.21.0.tgz", - "integrity": "sha512-Y8Y2T2FNvm08qlcoSMoNchh9y2Uj3QmjtwNMdRQkcFG7Muz//wfJBGBxh8R7HAGQFpgYpdHqUpEoPQk+q9Kjfg==", + "version": "5.30.0", + "resolved": "https://registry.npmjs.org/@typescript-eslint/typescript-estree/-/typescript-estree-5.30.0.tgz", + "integrity": "sha512-hDEawogreZB4n1zoqcrrtg/wPyyiCxmhPLpZ6kmWfKF5M5G0clRLaEexpuWr31fZ42F96SlD/5xCt1bT5Qm4Nw==", "dev": true, "dependencies": { - "@typescript-eslint/types": "5.21.0", - "@typescript-eslint/visitor-keys": "5.21.0", - "debug": "^4.3.2", - "globby": "^11.0.4", + "@typescript-eslint/types": "5.30.0", + "@typescript-eslint/visitor-keys": "5.30.0", + "debug": "^4.3.4", + "globby": "^11.1.0", "is-glob": "^4.0.3", - "semver": "^7.3.5", + "semver": "^7.3.7", "tsutils": "^3.21.0" }, "engines": { @@ -1204,15 +1220,15 @@ "dev": true }, "node_modules/@typescript-eslint/utils": { - "version": "5.21.0", - "resolved": "https://registry.npmjs.org/@typescript-eslint/utils/-/utils-5.21.0.tgz", - "integrity": "sha512-q/emogbND9wry7zxy7VYri+7ydawo2HDZhRZ5k6yggIvXa7PvBbAAZ4PFH/oZLem72ezC4Pr63rJvDK/sTlL8Q==", + "version": "5.30.0", + "resolved": "https://registry.npmjs.org/@typescript-eslint/utils/-/utils-5.30.0.tgz", + "integrity": "sha512-0bIgOgZflLKIcZsWvfklsaQTM3ZUbmtH0rJ1hKyV3raoUYyeZwcjQ8ZUJTzS7KnhNcsVT1Rxs7zeeMHEhGlltw==", "dev": true, "dependencies": { "@types/json-schema": "^7.0.9", - "@typescript-eslint/scope-manager": "5.21.0", - "@typescript-eslint/types": "5.21.0", - "@typescript-eslint/typescript-estree": "5.21.0", + "@typescript-eslint/scope-manager": "5.30.0", + "@typescript-eslint/types": "5.30.0", + "@typescript-eslint/typescript-estree": "5.30.0", "eslint-scope": "^5.1.1", "eslint-utils": "^3.0.0" }, @@ -1228,13 +1244,13 @@ } }, "node_modules/@typescript-eslint/visitor-keys": { - "version": "5.21.0", - "resolved": "https://registry.npmjs.org/@typescript-eslint/visitor-keys/-/visitor-keys-5.21.0.tgz", - "integrity": "sha512-SX8jNN+iHqAF0riZQMkm7e8+POXa/fXw5cxL+gjpyP+FI+JVNhii53EmQgDAfDcBpFekYSlO0fGytMQwRiMQCA==", + "version": "5.30.0", + "resolved": "https://registry.npmjs.org/@typescript-eslint/visitor-keys/-/visitor-keys-5.30.0.tgz", + "integrity": "sha512-6WcIeRk2DQ3pHKxU1Ni0qMXJkjO/zLjBymlYBy/53qxe7yjEFSvzKLDToJjURUhSl2Fzhkl4SMXQoETauF74cw==", "dev": true, "dependencies": { - "@typescript-eslint/types": "5.21.0", - "eslint-visitor-keys": "^3.0.0" + "@typescript-eslint/types": "5.30.0", + "eslint-visitor-keys": "^3.3.0" }, "engines": { "node": "^12.22.0 || ^14.17.0 || >=16.0.0" @@ -1340,15 +1356,27 @@ } }, "node_modules/ansi-escapes": { - "version": "4.3.2", - "resolved": "https://registry.npmjs.org/ansi-escapes/-/ansi-escapes-4.3.2.tgz", - "integrity": "sha512-gKXj5ALrKWQLsYG9jlTRmR/xKluxHV+Z9QEwNIgCfM1/uwPMCuzVVnh5mwTd+OuBZcwSIMbqssNWRm1lE51QaQ==", + "version": "5.0.0", + "resolved": "https://registry.npmjs.org/ansi-escapes/-/ansi-escapes-5.0.0.tgz", + "integrity": "sha512-5GFMVX8HqE/TB+FuBJGuO5XG0WrsA6ptUqoODaT/n9mmUaZFkqnBueB4leqGBCmrUHnCnC4PCZTCd0E7QQ83bA==", "dev": true, "dependencies": { - "type-fest": "^0.21.3" + "type-fest": "^1.0.2" }, "engines": { - "node": ">=8" + "node": ">=12" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/ansi-escapes/node_modules/type-fest": { + "version": "1.4.0", + "resolved": "https://registry.npmjs.org/type-fest/-/type-fest-1.4.0.tgz", + "integrity": "sha512-yGSza74xk0UG8k+pLh5oeoYirvIiWo5t0/o3zHHAO2tRDiZcxWP7fywNlXhqb6/r6sWvwi+RsyQMWhVLe4BVuA==", + "dev": true, + "engines": { + "node": ">=10" }, "funding": { "url": "https://github.com/sponsors/sindresorhus" @@ -1550,67 +1578,130 @@ } }, "node_modules/boxen": { - "version": "5.1.2", - "resolved": "https://registry.npmjs.org/boxen/-/boxen-5.1.2.tgz", - "integrity": "sha512-9gYgQKXx+1nP8mP7CzFyaUARhg7D3n1dF/FnErWmu9l6JvGpNUN278h0aSb+QjoiKSWG+iZ3uHrcqk0qrY9RQQ==", + "version": "7.0.0", + "resolved": "https://registry.npmjs.org/boxen/-/boxen-7.0.0.tgz", + "integrity": "sha512-j//dBVuyacJbvW+tvZ9HuH03fZ46QcaKvvhZickZqtB271DxJ7SNRSNxrV/dZX0085m7hISRZWbzWlJvx/rHSg==", "dev": true, "dependencies": { - "ansi-align": "^3.0.0", - "camelcase": "^6.2.0", - "chalk": "^4.1.0", - "cli-boxes": "^2.2.1", - "string-width": "^4.2.2", - "type-fest": "^0.20.2", - "widest-line": "^3.1.0", - "wrap-ansi": "^7.0.0" + "ansi-align": "^3.0.1", + "camelcase": "^7.0.0", + "chalk": "^5.0.1", + "cli-boxes": "^3.0.0", + "string-width": "^5.1.2", + "type-fest": "^2.13.0", + "widest-line": "^4.0.1", + "wrap-ansi": "^8.0.1" }, "engines": { - "node": ">=10" + "node": ">=14.16" }, "funding": { "url": "https://github.com/sponsors/sindresorhus" } }, + "node_modules/boxen/node_modules/ansi-regex": { + "version": "6.0.1", + "resolved": "https://registry.npmjs.org/ansi-regex/-/ansi-regex-6.0.1.tgz", + "integrity": "sha512-n5M855fKb2SsfMIiFFoVrABHJC8QtHwVx+mHWP3QcEqBHYienj5dHSgjbxtC0WEZXYt4wcD6zrQElDPhFuZgfA==", + "dev": true, + "engines": { + "node": ">=12" + }, + "funding": { + "url": "https://github.com/chalk/ansi-regex?sponsor=1" + } + }, + "node_modules/boxen/node_modules/ansi-styles": { + "version": "6.1.0", + "resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-6.1.0.tgz", + "integrity": "sha512-VbqNsoz55SYGczauuup0MFUyXNQviSpFTj1RQtFzmQLk18qbVSpTFFGMT293rmDaQuKCT6InmbuEyUne4mTuxQ==", + "dev": true, + "engines": { + "node": ">=12" + }, + "funding": { + "url": "https://github.com/chalk/ansi-styles?sponsor=1" + } + }, "node_modules/boxen/node_modules/camelcase": { - "version": "6.3.0", - "resolved": "https://registry.npmjs.org/camelcase/-/camelcase-6.3.0.tgz", - "integrity": "sha512-Gmy6FhYlCY7uOElZUSbxo2UCDH8owEk996gkbrpsgGtrJLM3J7jGxl9Ic7Qwwj4ivOE5AWZWRMecDdF7hqGjFA==", + "version": "7.0.0", + "resolved": "https://registry.npmjs.org/camelcase/-/camelcase-7.0.0.tgz", + "integrity": "sha512-JToIvOmz6nhGsUhAYScbo2d6Py5wojjNfoxoc2mEVLUdJ70gJK2gnd+ABY1Tc3sVMyK7QDPtN0T/XdlCQWITyQ==", "dev": true, "engines": { - "node": ">=10" + "node": ">=14.16" }, "funding": { "url": "https://github.com/sponsors/sindresorhus" } }, - "node_modules/boxen/node_modules/chalk": { - "version": "4.1.2", - "resolved": "https://registry.npmjs.org/chalk/-/chalk-4.1.2.tgz", - "integrity": "sha512-oKnbhFyRIXpUuez8iBMmyEa4nbj4IOQyuhc/wy9kY7/WVPcwIO9VA668Pu8RkO7+0G76SLROeyw9CpQ061i4mA==", + "node_modules/boxen/node_modules/emoji-regex": { + "version": "9.2.2", + "resolved": "https://registry.npmjs.org/emoji-regex/-/emoji-regex-9.2.2.tgz", + "integrity": "sha512-L18DaJsXSUk2+42pv8mLs5jJT2hqFkFE4j21wOmgbUqsZ2hL72NsUU785g9RXgo3s0ZNgVl42TiHp3ZtOv/Vyg==", + "dev": true + }, + "node_modules/boxen/node_modules/string-width": { + "version": "5.1.2", + "resolved": "https://registry.npmjs.org/string-width/-/string-width-5.1.2.tgz", + "integrity": "sha512-HnLOCR3vjcY8beoNLtcjZ5/nxn2afmME6lhrDrebokqMap+XbeW8n9TXpPDOqdGK5qcI3oT0GKTW6wC7EMiVqA==", "dev": true, "dependencies": { - "ansi-styles": "^4.1.0", - "supports-color": "^7.1.0" + "eastasianwidth": "^0.2.0", + "emoji-regex": "^9.2.2", + "strip-ansi": "^7.0.1" }, "engines": { - "node": ">=10" + "node": ">=12" }, "funding": { - "url": "https://github.com/chalk/chalk?sponsor=1" + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/boxen/node_modules/strip-ansi": { + "version": "7.0.1", + "resolved": "https://registry.npmjs.org/strip-ansi/-/strip-ansi-7.0.1.tgz", + "integrity": "sha512-cXNxvT8dFNRVfhVME3JAe98mkXDYN2O1l7jmcwMnOslDeESg1rF/OZMtK0nRAhiari1unG5cD4jG3rapUAkLbw==", + "dev": true, + "dependencies": { + "ansi-regex": "^6.0.1" + }, + "engines": { + "node": ">=12" + }, + "funding": { + "url": "https://github.com/chalk/strip-ansi?sponsor=1" } }, "node_modules/boxen/node_modules/type-fest": { - "version": "0.20.2", - "resolved": "https://registry.npmjs.org/type-fest/-/type-fest-0.20.2.tgz", - "integrity": "sha512-Ne+eE4r0/iWnpAxD852z3A+N0Bt5RN//NjJwRd2VFHEmrywxf5vsZlh4R6lixl6B+wz/8d+maTSAkN1FIkI3LQ==", + "version": "2.14.0", + "resolved": "https://registry.npmjs.org/type-fest/-/type-fest-2.14.0.tgz", + "integrity": "sha512-hQnTQkFjL5ik6HF2fTAM8ycbr94UbQXK364wF930VHb0dfBJ5JBP8qwrR8TaK9zwUEk7meruo2JAUDMwvuxd/w==", "dev": true, "engines": { - "node": ">=10" + "node": ">=12.20" }, "funding": { "url": "https://github.com/sponsors/sindresorhus" } }, + "node_modules/boxen/node_modules/wrap-ansi": { + "version": "8.0.1", + "resolved": "https://registry.npmjs.org/wrap-ansi/-/wrap-ansi-8.0.1.tgz", + "integrity": "sha512-QFF+ufAqhoYHvoHdajT/Po7KoXVBPXS2bgjIam5isfWJPfIOnQZ50JtUiVvCv/sjgacf3yRrt2ZKUZ/V4itN4g==", + "dev": true, + "dependencies": { + "ansi-styles": "^6.1.0", + "string-width": "^5.0.1", + "strip-ansi": "^7.0.1" + }, + "engines": { + "node": ">=12" + }, + "funding": { + "url": "https://github.com/chalk/wrap-ansi?sponsor=1" + } + }, "node_modules/brace-expansion": { "version": "1.1.11", "resolved": "https://registry.npmjs.org/brace-expansion/-/brace-expansion-1.1.11.tgz", @@ -1893,27 +1984,30 @@ } }, "node_modules/cli-boxes": { - "version": "2.2.1", - "resolved": "https://registry.npmjs.org/cli-boxes/-/cli-boxes-2.2.1.tgz", - "integrity": "sha512-y4coMcylgSCdVinjiDBuR8PCC2bLjyGTwEmPb9NHR/QaNU6EUOXcTY/s6VjGMD6ENSEaeQYHCY0GNGS5jfMwPw==", + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/cli-boxes/-/cli-boxes-3.0.0.tgz", + "integrity": "sha512-/lzGpEWL/8PfI0BmBOPRwp0c/wFNX1RdUML3jK/RcSBA9T8mZDdQpqYBKtCFTOfQbwPqWEOpjqW+Fnayc0969g==", "dev": true, "engines": { - "node": ">=6" + "node": ">=10" }, "funding": { "url": "https://github.com/sponsors/sindresorhus" } }, "node_modules/cli-cursor": { - "version": "3.1.0", - "resolved": "https://registry.npmjs.org/cli-cursor/-/cli-cursor-3.1.0.tgz", - "integrity": "sha512-I/zHAwsKf9FqGoXM4WWRACob9+SNukZTd94DWF57E4toouRulbCxcUh6RKUEOQlYTHJnzkPMySvPNaaSLNfLZw==", + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/cli-cursor/-/cli-cursor-4.0.0.tgz", + "integrity": "sha512-VGtlMu3x/4DOtIUwEkRezxUZ2lBacNJCHash0N0WeZDBS+7Ux1dm3XWAgWYxLJFMMdOeXMHXorshEFhbMSGelg==", "dev": true, "dependencies": { - "restore-cursor": "^3.1.0" + "restore-cursor": "^4.0.0" }, "engines": { - "node": ">=8" + "node": "^12.20.0 || ^14.13.1 || >=16.0.0" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" } }, "node_modules/cli-spinners": { @@ -1929,12 +2023,12 @@ } }, "node_modules/cli-width": { - "version": "3.0.0", - "resolved": "https://registry.npmjs.org/cli-width/-/cli-width-3.0.0.tgz", - "integrity": "sha512-FxqpkPPwu1HjuN93Omfm4h8uIanXofW0RxVEW3k5RKx+mJJYSthzNhp32Kzxxy3YAEZ/Dc/EWN1vZRY0+kOhbw==", + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/cli-width/-/cli-width-4.0.0.tgz", + "integrity": "sha512-ZksGS2xpa/bYkNzN3BAw1wEjsLV/ZKOf/CCrJ/QOBsxx6fOARIkwTutxp1XIOIohi6HKmOFjMoK/XaqDVUpEEw==", "dev": true, "engines": { - "node": ">= 10" + "node": ">= 12" } }, "node_modules/cliui": { @@ -1965,7 +2059,7 @@ "node_modules/clone": { "version": "1.0.4", "resolved": "https://registry.npmjs.org/clone/-/clone-1.0.4.tgz", - "integrity": "sha1-2jCcwmPfFZlMaIypAheco8fNfH4=", + "integrity": "sha512-JQHZ2QMW6l3aH/j6xCqQThY/9OH4D/9ls34cgkUBiEeocRTU04tHfKPBsUK1PqZCUQM7GiA0IIXJSuXHI64Kbg==", "dev": true, "engines": { "node": ">=0.8" @@ -1974,7 +2068,7 @@ "node_modules/clone-response": { "version": "1.0.2", "resolved": "https://registry.npmjs.org/clone-response/-/clone-response-1.0.2.tgz", - "integrity": "sha1-0dyXOSAxTfZ/vrlCI7TuNQI56Ws=", + "integrity": "sha512-yjLXh88P599UOyPTFX0POsd7WxnbsVsGohcwzHOLspIhhpalPw1BcqED8NblyZLKcGrL8dTgMlcaZxV2jAD41Q==", "dev": true, "dependencies": { "mimic-response": "^1.0.0" @@ -2031,9 +2125,9 @@ "dev": true }, "node_modules/compress-brotli": { - "version": "1.3.6", - "resolved": "https://registry.npmjs.org/compress-brotli/-/compress-brotli-1.3.6.tgz", - "integrity": "sha512-au99/GqZtUtiCBliqLFbWlhnCxn+XSYjwZ77q6mKN4La4qOXDoLVPZ50iXr0WmAyMxl8yqoq3Yq4OeQNPPkyeQ==", + "version": "1.3.8", + "resolved": "https://registry.npmjs.org/compress-brotli/-/compress-brotli-1.3.8.tgz", + "integrity": "sha512-lVcQsjhxhIXsuupfy9fmZUFtAIdBmXA7EGY6GBdgZ++qkM9zG4YFT8iU7FoBxzryNDMOpD1HIFHUSX4D87oqhQ==", "dev": true, "dependencies": { "@types/json-buffer": "~3.0.0", @@ -2049,21 +2143,39 @@ "integrity": "sha1-2Klr13/Wjfd5OnMDajug1UBdR3s=", "dev": true }, + "node_modules/config-chain": { + "version": "1.1.13", + "resolved": "https://registry.npmjs.org/config-chain/-/config-chain-1.1.13.tgz", + "integrity": "sha512-qj+f8APARXHrM0hraqXYb2/bOVSV4PvJQlNZ/DVj0QrmNM2q2euizkeuVckQ57J+W0mRH6Hvi+k50M4Jul2VRQ==", + "dev": true, + "dependencies": { + "ini": "^1.3.4", + "proto-list": "~1.2.1" + } + }, + "node_modules/config-chain/node_modules/ini": { + "version": "1.3.8", + "resolved": "https://registry.npmjs.org/ini/-/ini-1.3.8.tgz", + "integrity": "sha512-JV/yugV2uzW5iMRSiZAyDtQd+nxtUnjeLt0acNdw98kKLrvuRVyB80tsREOE7yvGVgalhZ6RNXCmEHkUKBKxew==", + "dev": true + }, "node_modules/configstore": { - "version": "5.0.1", - "resolved": "https://registry.npmjs.org/configstore/-/configstore-5.0.1.tgz", - "integrity": "sha512-aMKprgk5YhBNyH25hj8wGt2+D52Sw1DRRIzqBwLp2Ya9mFmY8KPvvtvmna8SxVR9JMZ4kzMD68N22vlaRpkeFA==", + "version": "6.0.0", + "resolved": "https://registry.npmjs.org/configstore/-/configstore-6.0.0.tgz", + "integrity": "sha512-cD31W1v3GqUlQvbBCGcXmd2Nj9SvLDOP1oQ0YFuLETufzSPaKp11rYBsSOm7rCsW3OnIRAFM3OxRhceaXNYHkA==", "dev": true, "dependencies": { - "dot-prop": "^5.2.0", - "graceful-fs": "^4.1.2", - "make-dir": "^3.0.0", - "unique-string": "^2.0.0", - "write-file-atomic": "^3.0.0", - "xdg-basedir": "^4.0.0" + "dot-prop": "^6.0.1", + "graceful-fs": "^4.2.6", + "unique-string": "^3.0.0", + "write-file-atomic": "^3.0.3", + "xdg-basedir": "^5.0.1" }, "engines": { - "node": ">=8" + "node": ">=12" + }, + "funding": { + "url": "https://github.com/yeoman/configstore?sponsor=1" } }, "node_modules/convert-source-map": { @@ -2118,12 +2230,30 @@ } }, "node_modules/crypto-random-string": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/crypto-random-string/-/crypto-random-string-2.0.0.tgz", - "integrity": "sha512-v1plID3y9r/lPhviJ1wrXpLeyUIGAZ2SHNYTEapm7/8A9nLPoyvVp3RK/EPFqn5kEznyWgYZNsRtYYIWbuG8KA==", + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/crypto-random-string/-/crypto-random-string-4.0.0.tgz", + "integrity": "sha512-x8dy3RnvYdlUcPOjkEHqozhiwzKNSq7GcPuXFbnyMOCHxX8V3OgIg/pYuabl2sbUPfIJaeAQB7PMOK8DFIdoRA==", "dev": true, + "dependencies": { + "type-fest": "^1.0.1" + }, "engines": { - "node": ">=8" + "node": ">=12" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/crypto-random-string/node_modules/type-fest": { + "version": "1.4.0", + "resolved": "https://registry.npmjs.org/type-fest/-/type-fest-1.4.0.tgz", + "integrity": "sha512-yGSza74xk0UG8k+pLh5oeoYirvIiWo5t0/o3zHHAO2tRDiZcxWP7fywNlXhqb6/r6sWvwi+RsyQMWhVLe4BVuA==", + "dev": true, + "engines": { + "node": ">=10" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" } }, "node_modules/data-uri-to-buffer": { @@ -2227,7 +2357,7 @@ "node_modules/defaults": { "version": "1.0.3", "resolved": "https://registry.npmjs.org/defaults/-/defaults-1.0.3.tgz", - "integrity": "sha1-xlYFHpgX2f8I7YgUd/P+QBnz730=", + "integrity": "sha512-s82itHOnYrN0Ib8r+z7laQz3sdE+4FP3d9Q7VLO7U+KRT+CR0GsWuyHxzdAY82I7cXv0G/twrqomTJLOssO5HA==", "dev": true, "dependencies": { "clone": "^1.0.2" @@ -2340,21 +2470,24 @@ } }, "node_modules/dot-prop": { - "version": "5.3.0", - "resolved": "https://registry.npmjs.org/dot-prop/-/dot-prop-5.3.0.tgz", - "integrity": "sha512-QM8q3zDe58hqUqjraQOmzZ1LIH9SWQJTlEKCH4kJ2oQvLZk7RbQXvtDM2XEq3fwkV9CCvvH4LA0AV+ogFsBM2Q==", + "version": "6.0.1", + "resolved": "https://registry.npmjs.org/dot-prop/-/dot-prop-6.0.1.tgz", + "integrity": "sha512-tE7ztYzXHIeyvc7N+hR3oi7FIbf/NIjVP9hmAt3yMXzrQ072/fpjGLx2GxNxGxUl5V73MEqYzioOMoVhGMJ5cA==", "dev": true, "dependencies": { "is-obj": "^2.0.0" }, "engines": { - "node": ">=8" + "node": ">=10" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" } }, - "node_modules/duplexer3": { - "version": "0.1.4", - "resolved": "https://registry.npmjs.org/duplexer3/-/duplexer3-0.1.4.tgz", - "integrity": "sha1-7gHdHKwO08vH/b6jfcCo8c4ALOI=", + "node_modules/eastasianwidth": { + "version": "0.2.0", + "resolved": "https://registry.npmjs.org/eastasianwidth/-/eastasianwidth-0.2.0.tgz", + "integrity": "sha512-I88TYZWc9XiYHRQ4/3c5rjjfgkjhLyW2luGIheGERbNQ6OY7yTybanSpDXZa8y7VUP9YmDcYa+eyq4ca7iLqWA==", "dev": true }, "node_modules/electron-to-chromium": { @@ -2485,12 +2618,15 @@ } }, "node_modules/escape-goat": { - "version": "2.1.1", - "resolved": "https://registry.npmjs.org/escape-goat/-/escape-goat-2.1.1.tgz", - "integrity": "sha512-8/uIhbG12Csjy2JEW7D9pHbreaVaS/OpN3ycnyvElTdwM5n6GY6W6e2IPemfvGZeUMqZ9A/3GqIZMgKnBhAw/Q==", + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/escape-goat/-/escape-goat-4.0.0.tgz", + "integrity": "sha512-2Sd4ShcWxbx6OY1IHyla/CVNwvg7XwZVoXZHcSu9w9SReNP1EzzD5T8NWKIR38fIqEns9kDWKUQTXXAmlDrdPg==", "dev": true, "engines": { - "node": ">=8" + "node": ">=12" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" } }, "node_modules/escape-string-regexp": { @@ -2576,12 +2712,12 @@ } }, "node_modules/eslint": { - "version": "8.14.0", - "resolved": "https://registry.npmjs.org/eslint/-/eslint-8.14.0.tgz", - "integrity": "sha512-3/CE4aJX7LNEiE3i6FeodHmI/38GZtWCsAtsymScmzYapx8q1nVVb+eLcLSzATmCPXw5pT4TqVs1E0OmxAd9tw==", + "version": "8.18.0", + "resolved": "https://registry.npmjs.org/eslint/-/eslint-8.18.0.tgz", + "integrity": "sha512-As1EfFMVk7Xc6/CvhssHUjsAQSkpfXvUGMFC3ce8JDe6WvqCgRrLOBQbVpsBFr1X1V+RACOadnzVvcUS5ni2bA==", "dev": true, "dependencies": { - "@eslint/eslintrc": "^1.2.2", + "@eslint/eslintrc": "^1.3.0", "@humanwhocodes/config-array": "^0.9.2", "ajv": "^6.10.0", "chalk": "^4.0.0", @@ -2592,14 +2728,14 @@ "eslint-scope": "^7.1.1", "eslint-utils": "^3.0.0", "eslint-visitor-keys": "^3.3.0", - "espree": "^9.3.1", + "espree": "^9.3.2", "esquery": "^1.4.0", "esutils": "^2.0.2", "fast-deep-equal": "^3.1.3", "file-entry-cache": "^6.0.1", "functional-red-black-tree": "^1.0.1", "glob-parent": "^6.0.1", - "globals": "^13.6.0", + "globals": "^13.15.0", "ignore": "^5.2.0", "import-fresh": "^3.0.0", "imurmurhash": "^0.1.4", @@ -2608,7 +2744,7 @@ "json-stable-stringify-without-jsonify": "^1.0.1", "levn": "^0.4.1", "lodash.merge": "^4.6.2", - "minimatch": "^3.0.4", + "minimatch": "^3.1.2", "natural-compare": "^1.4.0", "optionator": "^0.9.1", "regexpp": "^3.2.0", @@ -2727,13 +2863,13 @@ } }, "node_modules/espree": { - "version": "9.3.1", - "resolved": "https://registry.npmjs.org/espree/-/espree-9.3.1.tgz", - "integrity": "sha512-bvdyLmJMfwkV3NCRl5ZhJf22zBFo1y8bYh3VYb+bfzqNB4Je68P2sSuXyuFquzWLebHpNd2/d5uv7yoP9ISnGQ==", + "version": "9.3.2", + "resolved": "https://registry.npmjs.org/espree/-/espree-9.3.2.tgz", + "integrity": "sha512-D211tC7ZwouTIuY5x9XnS0E9sWNChB7IYKX/Xp5eQj3nFXhqmiUDB9q27y76oFl8jTg3pXcQx/bpxMfs3CIZbA==", "dev": true, "dependencies": { - "acorn": "^8.7.0", - "acorn-jsx": "^5.3.1", + "acorn": "^8.7.1", + "acorn-jsx": "^5.3.2", "eslint-visitor-keys": "^3.3.0" }, "engines": { @@ -2906,15 +3042,28 @@ } }, "node_modules/figures": { - "version": "3.2.0", - "resolved": "https://registry.npmjs.org/figures/-/figures-3.2.0.tgz", - "integrity": "sha512-yaduQFRKLXYOGgEn6AZau90j3ggSOyiqXU0F9JZfeXYhNa+Jk4X+s45A2zg5jns87GAFa34BBm2kXw4XpNcbdg==", + "version": "4.0.1", + "resolved": "https://registry.npmjs.org/figures/-/figures-4.0.1.tgz", + "integrity": "sha512-rElJwkA/xS04Vfg+CaZodpso7VqBknOYbzi6I76hI4X80RUjkSxO2oAyPmGbuXUppywjqndOrQDl817hDnI++w==", "dev": true, "dependencies": { - "escape-string-regexp": "^1.0.5" + "escape-string-regexp": "^5.0.0", + "is-unicode-supported": "^1.2.0" }, "engines": { - "node": ">=8" + "node": ">=12" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/figures/node_modules/escape-string-regexp": { + "version": "5.0.0", + "resolved": "https://registry.npmjs.org/escape-string-regexp/-/escape-string-regexp-5.0.0.tgz", + "integrity": "sha512-/veY75JbMK4j1yjvuUxuVsiS/hr/4iHs9FTT6cgTexxdE0Ly/glccBAkloH/DofkjRbZU3bnoj38mOmhkZ0lHw==", + "dev": true, + "engines": { + "node": ">=12" }, "funding": { "url": "https://github.com/sponsors/sindresorhus" @@ -3351,9 +3500,9 @@ } }, "node_modules/globals": { - "version": "13.13.0", - "resolved": "https://registry.npmjs.org/globals/-/globals-13.13.0.tgz", - "integrity": "sha512-EQ7Q18AJlPwp3vUDL4mKA0KXrXyNIQyWon6T6XQiBQF0XHvRsiCSrWmmeATpUzdJN2HhWZU6Pdl0a9zdep5p6A==", + "version": "13.15.0", + "resolved": "https://registry.npmjs.org/globals/-/globals-13.15.0.tgz", + "integrity": "sha512-bpzcOlgDhMG070Av0Vy5Owklpv1I6+j96GhUI7Rh7IzDCKLzboflLrrfqMu8NquDbiR4EOQk7XzJwqVJxicxog==", "dev": true, "dependencies": { "type-fest": "^0.20.2" @@ -3365,18 +3514,6 @@ "url": "https://github.com/sponsors/sindresorhus" } }, - "node_modules/globals/node_modules/type-fest": { - "version": "0.20.2", - "resolved": "https://registry.npmjs.org/type-fest/-/type-fest-0.20.2.tgz", - "integrity": "sha512-Ne+eE4r0/iWnpAxD852z3A+N0Bt5RN//NjJwRd2VFHEmrywxf5vsZlh4R6lixl6B+wz/8d+maTSAkN1FIkI3LQ==", - "dev": true, - "engines": { - "node": ">=10" - }, - "funding": { - "url": "https://github.com/sponsors/sindresorhus" - } - }, "node_modules/globby": { "version": "6.1.0", "resolved": "https://registry.npmjs.org/globby/-/globby-6.1.0.tgz", @@ -3394,9 +3531,9 @@ } }, "node_modules/got": { - "version": "12.0.4", - "resolved": "https://registry.npmjs.org/got/-/got-12.0.4.tgz", - "integrity": "sha512-2Eyz4iU/ktq7wtMFXxzK7g5p35uNYLLdiZarZ5/Yn3IJlNEpBd5+dCgcAyxN8/8guZLszffwe3wVyw+DEVrpBg==", + "version": "12.1.0", + "resolved": "https://registry.npmjs.org/got/-/got-12.1.0.tgz", + "integrity": "sha512-hBv2ty9QN2RdbJJMK3hesmSkFTjVIHyIDDbssCKnSmq62edGgImJWD10Eb1k77TiV1bxloxqcFAVK8+9pkhOig==", "dev": true, "dependencies": { "@sindresorhus/is": "^4.6.0", @@ -3496,12 +3633,15 @@ } }, "node_modules/has-yarn": { - "version": "2.1.0", - "resolved": "https://registry.npmjs.org/has-yarn/-/has-yarn-2.1.0.tgz", - "integrity": "sha512-UqBRqi4ju7T+TqGNdqAO0PaSVGsDGJUBQvk9eUWNGRY1CFGDzYhLWoM7JQEemnlvVcv/YEmc2wNW8BC24EnUsw==", + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/has-yarn/-/has-yarn-3.0.0.tgz", + "integrity": "sha512-IrsVwUHhEULx3R8f/aA8AHuEzAorplsab/v8HBzEiIukwq5i/EC+xmOW+HfP1OaDP+2JkgT1yILHN2O3UFIbcA==", "dev": true, "engines": { - "node": ">=8" + "node": "^12.20.0 || ^14.13.1 || >=16.0.0" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" } }, "node_modules/hasha": { @@ -3685,12 +3825,12 @@ } }, "node_modules/import-lazy": { - "version": "2.1.0", - "resolved": "https://registry.npmjs.org/import-lazy/-/import-lazy-2.1.0.tgz", - "integrity": "sha1-BWmOPUXIjo1+nZLLBYTnfwlvPkM=", + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/import-lazy/-/import-lazy-4.0.0.tgz", + "integrity": "sha512-rKtvo6a868b5Hu3heneU+L4yEQ4jYKLtjpnPeUdK7h0yzXGmyBTypknlkCvHFBqfX9YlorEiMM6Dnq/5atfHkw==", "dev": true, "engines": { - "node": ">=4" + "node": ">=8" } }, "node_modules/imurmurhash": { @@ -3737,140 +3877,108 @@ } }, "node_modules/inquirer": { - "version": "8.2.4", - "resolved": "https://registry.npmjs.org/inquirer/-/inquirer-8.2.4.tgz", - "integrity": "sha512-nn4F01dxU8VeKfq192IjLsxu0/OmMZ4Lg3xKAns148rCaXP6ntAoEkVYZThWjwON8AlzdZZi6oqnhNbxUG9hVg==", + "version": "9.0.0", + "resolved": "https://registry.npmjs.org/inquirer/-/inquirer-9.0.0.tgz", + "integrity": "sha512-eYTDdTYr/YPwRenOzLZTvaJUDXDW8GQgxvzBppuXLj/kauTRLfV8bCPVbGh2staP7edrqL+rGwjaOa+JVxBWsg==", "dev": true, "dependencies": { - "ansi-escapes": "^4.2.1", - "chalk": "^4.1.1", - "cli-cursor": "^3.1.0", - "cli-width": "^3.0.0", + "ansi-escapes": "^5.0.0", + "chalk": "^5.0.1", + "cli-cursor": "^4.0.0", + "cli-width": "^4.0.0", "external-editor": "^3.0.3", - "figures": "^3.0.0", + "figures": "^4.0.1", "lodash": "^4.17.21", "mute-stream": "0.0.8", - "ora": "^5.4.1", + "ora": "^6.1.0", "run-async": "^2.4.0", "rxjs": "^7.5.5", - "string-width": "^4.1.0", - "strip-ansi": "^6.0.0", + "string-width": "^5.1.2", + "strip-ansi": "^7.0.1", "through": "^2.3.6", - "wrap-ansi": "^7.0.0" + "wrap-ansi": "^8.0.1" }, "engines": { "node": ">=12.0.0" } }, - "node_modules/inquirer/node_modules/bl": { - "version": "4.1.0", - "resolved": "https://registry.npmjs.org/bl/-/bl-4.1.0.tgz", - "integrity": "sha512-1W07cM9gS6DcLperZfFSj+bWLtaPGSOHWhPiGzXmvVJbRLdG82sH/Kn8EtW1VqWVA54AKf2h5k5BbnIbwF3h6w==", + "node_modules/inquirer/node_modules/ansi-regex": { + "version": "6.0.1", + "resolved": "https://registry.npmjs.org/ansi-regex/-/ansi-regex-6.0.1.tgz", + "integrity": "sha512-n5M855fKb2SsfMIiFFoVrABHJC8QtHwVx+mHWP3QcEqBHYienj5dHSgjbxtC0WEZXYt4wcD6zrQElDPhFuZgfA==", "dev": true, - "dependencies": { - "buffer": "^5.5.0", - "inherits": "^2.0.4", - "readable-stream": "^3.4.0" + "engines": { + "node": ">=12" + }, + "funding": { + "url": "https://github.com/chalk/ansi-regex?sponsor=1" } }, - "node_modules/inquirer/node_modules/buffer": { - "version": "5.7.1", - "resolved": "https://registry.npmjs.org/buffer/-/buffer-5.7.1.tgz", - "integrity": "sha512-EHcyIPBQ4BSGlvjB16k5KgAJ27CIsHY/2JBmCRReo48y9rQ3MaUzWX3KVlBa4U7MyX02HdVj0K7C3WaB3ju7FQ==", + "node_modules/inquirer/node_modules/ansi-styles": { + "version": "6.1.0", + "resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-6.1.0.tgz", + "integrity": "sha512-VbqNsoz55SYGczauuup0MFUyXNQviSpFTj1RQtFzmQLk18qbVSpTFFGMT293rmDaQuKCT6InmbuEyUne4mTuxQ==", "dev": true, - "funding": [ - { - "type": "github", - "url": "https://github.com/sponsors/feross" - }, - { - "type": "patreon", - "url": "https://www.patreon.com/feross" - }, - { - "type": "consulting", - "url": "https://feross.org/support" - } - ], - "dependencies": { - "base64-js": "^1.3.1", - "ieee754": "^1.1.13" - } - }, - "node_modules/inquirer/node_modules/chalk": { - "version": "4.1.2", - "resolved": "https://registry.npmjs.org/chalk/-/chalk-4.1.2.tgz", - "integrity": "sha512-oKnbhFyRIXpUuez8iBMmyEa4nbj4IOQyuhc/wy9kY7/WVPcwIO9VA668Pu8RkO7+0G76SLROeyw9CpQ061i4mA==", - "dev": true, - "dependencies": { - "ansi-styles": "^4.1.0", - "supports-color": "^7.1.0" - }, "engines": { - "node": ">=10" + "node": ">=12" }, "funding": { - "url": "https://github.com/chalk/chalk?sponsor=1" + "url": "https://github.com/chalk/ansi-styles?sponsor=1" } }, - "node_modules/inquirer/node_modules/is-interactive": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/is-interactive/-/is-interactive-1.0.0.tgz", - "integrity": "sha512-2HvIEKRoqS62guEC+qBjpvRubdX910WCMuJTZ+I9yvqKU2/12eSL549HMwtabb4oupdj2sMP50k+XJfB/8JE6w==", - "dev": true, - "engines": { - "node": ">=8" - } + "node_modules/inquirer/node_modules/emoji-regex": { + "version": "9.2.2", + "resolved": "https://registry.npmjs.org/emoji-regex/-/emoji-regex-9.2.2.tgz", + "integrity": "sha512-L18DaJsXSUk2+42pv8mLs5jJT2hqFkFE4j21wOmgbUqsZ2hL72NsUU785g9RXgo3s0ZNgVl42TiHp3ZtOv/Vyg==", + "dev": true }, - "node_modules/inquirer/node_modules/is-unicode-supported": { - "version": "0.1.0", - "resolved": "https://registry.npmjs.org/is-unicode-supported/-/is-unicode-supported-0.1.0.tgz", - "integrity": "sha512-knxG2q4UC3u8stRGyAVJCOdxFmv5DZiRcdlIaAQXAbSfJya+OhopNotLQrstBhququ4ZpuKbDc/8S6mgXgPFPw==", + "node_modules/inquirer/node_modules/string-width": { + "version": "5.1.2", + "resolved": "https://registry.npmjs.org/string-width/-/string-width-5.1.2.tgz", + "integrity": "sha512-HnLOCR3vjcY8beoNLtcjZ5/nxn2afmME6lhrDrebokqMap+XbeW8n9TXpPDOqdGK5qcI3oT0GKTW6wC7EMiVqA==", "dev": true, + "dependencies": { + "eastasianwidth": "^0.2.0", + "emoji-regex": "^9.2.2", + "strip-ansi": "^7.0.1" + }, "engines": { - "node": ">=10" + "node": ">=12" }, "funding": { "url": "https://github.com/sponsors/sindresorhus" } }, - "node_modules/inquirer/node_modules/log-symbols": { - "version": "4.1.0", - "resolved": "https://registry.npmjs.org/log-symbols/-/log-symbols-4.1.0.tgz", - "integrity": "sha512-8XPvpAA8uyhfteu8pIvQxpJZ7SYYdpUivZpGy6sFsBuKRY/7rQGavedeB8aK+Zkyq6upMFVL/9AW6vOYzfRyLg==", + "node_modules/inquirer/node_modules/strip-ansi": { + "version": "7.0.1", + "resolved": "https://registry.npmjs.org/strip-ansi/-/strip-ansi-7.0.1.tgz", + "integrity": "sha512-cXNxvT8dFNRVfhVME3JAe98mkXDYN2O1l7jmcwMnOslDeESg1rF/OZMtK0nRAhiari1unG5cD4jG3rapUAkLbw==", "dev": true, "dependencies": { - "chalk": "^4.1.0", - "is-unicode-supported": "^0.1.0" + "ansi-regex": "^6.0.1" }, "engines": { - "node": ">=10" + "node": ">=12" }, "funding": { - "url": "https://github.com/sponsors/sindresorhus" + "url": "https://github.com/chalk/strip-ansi?sponsor=1" } }, - "node_modules/inquirer/node_modules/ora": { - "version": "5.4.1", - "resolved": "https://registry.npmjs.org/ora/-/ora-5.4.1.tgz", - "integrity": "sha512-5b6Y85tPxZZ7QytO+BQzysW31HJku27cRIlkbAXaNx+BdcVi+LlRFmVXzeF6a7JCwJpyw5c4b+YSVImQIrBpuQ==", + "node_modules/inquirer/node_modules/wrap-ansi": { + "version": "8.0.1", + "resolved": "https://registry.npmjs.org/wrap-ansi/-/wrap-ansi-8.0.1.tgz", + "integrity": "sha512-QFF+ufAqhoYHvoHdajT/Po7KoXVBPXS2bgjIam5isfWJPfIOnQZ50JtUiVvCv/sjgacf3yRrt2ZKUZ/V4itN4g==", "dev": true, "dependencies": { - "bl": "^4.1.0", - "chalk": "^4.1.0", - "cli-cursor": "^3.1.0", - "cli-spinners": "^2.5.0", - "is-interactive": "^1.0.0", - "is-unicode-supported": "^0.1.0", - "log-symbols": "^4.1.0", - "strip-ansi": "^6.0.0", - "wcwidth": "^1.0.1" + "ansi-styles": "^6.1.0", + "string-width": "^5.0.1", + "strip-ansi": "^7.0.1" }, "engines": { - "node": ">=10" + "node": ">=12" }, "funding": { - "url": "https://github.com/sponsors/sindresorhus" + "url": "https://github.com/chalk/wrap-ansi?sponsor=1" } }, "node_modules/internal-slot": { @@ -4110,12 +4218,12 @@ } }, "node_modules/is-npm": { - "version": "5.0.0", - "resolved": "https://registry.npmjs.org/is-npm/-/is-npm-5.0.0.tgz", - "integrity": "sha512-WW/rQLOazUq+ST/bCAVBp/2oMERWLsR7OrKyt052dNDk4DHcDE0/7QSXITlmi+VBcV13DfIbysG3tZJm5RfdBA==", + "version": "6.0.0", + "resolved": "https://registry.npmjs.org/is-npm/-/is-npm-6.0.0.tgz", + "integrity": "sha512-JEjxbSmtPSt1c8XTkVrlujcXdKV1/tvuQ7GwKcAlyiVLeYFQ2VHat8xfrDJsIkhCdF/tZ7CiIR3sy141c6+gPQ==", "dev": true, "engines": { - "node": ">=10" + "node": "^12.20.0 || ^14.13.1 || >=16.0.0" }, "funding": { "url": "https://github.com/sponsors/sindresorhus" @@ -4321,10 +4429,13 @@ } }, "node_modules/is-yarn-global": { - "version": "0.3.0", - "resolved": "https://registry.npmjs.org/is-yarn-global/-/is-yarn-global-0.3.0.tgz", - "integrity": "sha512-VjSeb/lHmkoyd8ryPVIKvOCn4D1koMqY+vqyjjUfc3xyKtP4dYOxM44sZrnqQSzSds3xyOrUTLTC9LVCVgLngw==", - "dev": true + "version": "0.4.0", + "resolved": "https://registry.npmjs.org/is-yarn-global/-/is-yarn-global-0.4.0.tgz", + "integrity": "sha512-HneQBCrXGBy15QnaDfcn6OLoU8AQPAa0Qn0IeJR/QCo4E8dNZaGGwxpCwWyEBQC5QvFonP8d6t60iGpAHVAfNA==", + "dev": true, + "engines": { + "node": ">=12" + } }, "node_modules/isarray": { "version": "2.0.5", @@ -4543,25 +4654,28 @@ "dev": true }, "node_modules/keyv": { - "version": "4.2.2", - "resolved": "https://registry.npmjs.org/keyv/-/keyv-4.2.2.tgz", - "integrity": "sha512-uYS0vKTlBIjNCAUqrjlxmruxOEiZxZIHXyp32sdcGmP+ukFrmWUnE//RcPXJH3Vxrni1H2gsQbjHE0bH7MtMQQ==", + "version": "4.3.2", + "resolved": "https://registry.npmjs.org/keyv/-/keyv-4.3.2.tgz", + "integrity": "sha512-kn8WmodVBe12lmHpA6W8OY7SNh6wVR+Z+wZESF4iF5FCazaVXGWOtnbnvX0tMQ1bO+/TmOD9LziuYMvrIIs0xw==", "dev": true, "dependencies": { - "compress-brotli": "^1.3.6", + "compress-brotli": "^1.3.8", "json-buffer": "3.0.1" } }, "node_modules/latest-version": { - "version": "5.1.0", - "resolved": "https://registry.npmjs.org/latest-version/-/latest-version-5.1.0.tgz", - "integrity": "sha512-weT+r0kTkRQdCdYCNtkMwWXQTMEswKrFBkm4ckQOMVhhqhIMI1UT2hMj+1iigIhgSZm5gTmrRXBNoGUgaTY1xA==", + "version": "7.0.0", + "resolved": "https://registry.npmjs.org/latest-version/-/latest-version-7.0.0.tgz", + "integrity": "sha512-KvNT4XqAMzdcL6ka6Tl3i2lYeFDgXNCuIX+xNx6ZMVR1dFq+idXd9FLKNMOIx0t9mJ9/HudyX4oZWXZQ0UJHeg==", "dev": true, "dependencies": { - "package-json": "^6.3.0" + "package-json": "^8.1.0" }, "engines": { - "node": ">=8" + "node": ">=14.16" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" } }, "node_modules/levn": { @@ -4696,9 +4810,9 @@ "dev": true }, "node_modules/marked": { - "version": "4.0.15", - "resolved": "https://registry.npmjs.org/marked/-/marked-4.0.15.tgz", - "integrity": "sha512-esX5lPdTfG4p8LDkv+obbRCyOKzB+820ZZyMOXJZygZBHrH9b3xXR64X4kT3sPe9Nx8qQXbmcz6kFSMt4Nfk6Q==", + "version": "4.0.17", + "resolved": "https://registry.npmjs.org/marked/-/marked-4.0.17.tgz", + "integrity": "sha512-Wfk0ATOK5iPxM4ptrORkFemqroz0ZDxp5MWfYA7H/F+wO17NRWV5Ypxi6p3g2Xmw2bKeiYOl6oVnLHKxBA0VhA==", "dev": true, "bin": { "marked": "bin/marked.js" @@ -5373,9 +5487,9 @@ } }, "node_modules/ora": { - "version": "6.1.0", - "resolved": "https://registry.npmjs.org/ora/-/ora-6.1.0.tgz", - "integrity": "sha512-CxEP6845hLK+NHFWZ+LplGO4zfw4QSfxTlqMfvlJ988GoiUeZDMzCvqsZkFHv69sPICmJH1MDxZoQFOKXerAVw==", + "version": "6.1.2", + "resolved": "https://registry.npmjs.org/ora/-/ora-6.1.2.tgz", + "integrity": "sha512-EJQ3NiP5Xo94wJXIzAyOtSb0QEIAUu7m8t6UZ9krbz0vAJqr92JpcK/lEXg91q6B9pEGqrykkd2EQplnifDSBw==", "dev": true, "dependencies": { "bl": "^5.0.0", @@ -5407,61 +5521,6 @@ "url": "https://github.com/chalk/ansi-regex?sponsor=1" } }, - "node_modules/ora/node_modules/cli-cursor": { - "version": "4.0.0", - "resolved": "https://registry.npmjs.org/cli-cursor/-/cli-cursor-4.0.0.tgz", - "integrity": "sha512-VGtlMu3x/4DOtIUwEkRezxUZ2lBacNJCHash0N0WeZDBS+7Ux1dm3XWAgWYxLJFMMdOeXMHXorshEFhbMSGelg==", - "dev": true, - "dependencies": { - "restore-cursor": "^4.0.0" - }, - "engines": { - "node": "^12.20.0 || ^14.13.1 || >=16.0.0" - }, - "funding": { - "url": "https://github.com/sponsors/sindresorhus" - } - }, - "node_modules/ora/node_modules/mimic-fn": { - "version": "2.1.0", - "resolved": "https://registry.npmjs.org/mimic-fn/-/mimic-fn-2.1.0.tgz", - "integrity": "sha512-OqbOk5oEQeAZ8WXWydlu9HJjz9WVdEIvamMCcXmuqUYjTknH/sqsWvhQ3vgwKFRR1HpjvNBKQ37nbJgYzGqGcg==", - "dev": true, - "engines": { - "node": ">=6" - } - }, - "node_modules/ora/node_modules/onetime": { - "version": "5.1.2", - "resolved": "https://registry.npmjs.org/onetime/-/onetime-5.1.2.tgz", - "integrity": "sha512-kbpaSSGJTWdAY5KPVeMOKXSrPtr8C8C7wodJbcsd51jRnmD+GZu8Y0VoU6Dm5Z4vWr0Ig/1NKuWRKf7j5aaYSg==", - "dev": true, - "dependencies": { - "mimic-fn": "^2.1.0" - }, - "engines": { - "node": ">=6" - }, - "funding": { - "url": "https://github.com/sponsors/sindresorhus" - } - }, - "node_modules/ora/node_modules/restore-cursor": { - "version": "4.0.0", - "resolved": "https://registry.npmjs.org/restore-cursor/-/restore-cursor-4.0.0.tgz", - "integrity": "sha512-I9fPXU9geO9bHOt9pHHOhOkYerIMsmVaWB0rA2AI9ERh/+x/i7MV5HKBNrg+ljO5eoPVgCcnFuRjJ9uH6I/3eg==", - "dev": true, - "dependencies": { - "onetime": "^5.1.0", - "signal-exit": "^3.0.2" - }, - "engines": { - "node": "^12.20.0 || ^14.13.1 || >=16.0.0" - }, - "funding": { - "url": "https://github.com/sponsors/sindresorhus" - } - }, "node_modules/ora/node_modules/strip-ansi": { "version": "7.0.1", "resolved": "https://registry.npmjs.org/strip-ansi/-/strip-ansi-7.0.1.tgz", @@ -5496,7 +5555,7 @@ "node_modules/os-tmpdir": { "version": "1.0.2", "resolved": "https://registry.npmjs.org/os-tmpdir/-/os-tmpdir-1.0.2.tgz", - "integrity": "sha1-u+Z0BseaqFxc/sdm/lc0VV36EnQ=", + "integrity": "sha512-D2FR03Vir7FIu45XBY20mTb+/ZSWB00sjU9jdQXt83gDrI4Ztz5Fs7/yy74g2N5SVQY4xY1qDr4rNddwYRVX0g==", "dev": true, "engines": { "node": ">=0.10.0" @@ -5609,186 +5668,56 @@ } }, "node_modules/package-json": { - "version": "6.5.0", - "resolved": "https://registry.npmjs.org/package-json/-/package-json-6.5.0.tgz", - "integrity": "sha512-k3bdm2n25tkyxcjSKzB5x8kfVxlMdgsbPr0GkZcwHsLpba6cBjqCt1KlcChKEvxHIcTB1FVMuwoijZ26xex5MQ==", - "dev": true, - "dependencies": { - "got": "^9.6.0", - "registry-auth-token": "^4.0.0", - "registry-url": "^5.0.0", - "semver": "^6.2.0" - }, - "engines": { - "node": ">=8" - } - }, - "node_modules/package-json/node_modules/@sindresorhus/is": { - "version": "0.14.0", - "resolved": "https://registry.npmjs.org/@sindresorhus/is/-/is-0.14.0.tgz", - "integrity": "sha512-9NET910DNaIPngYnLLPeg+Ogzqsi9uM4mSboU5y6p8S5DzMTVEsJZrawi+BoDNUVBa2DhJqQYUFvMDfgU062LQ==", - "dev": true, - "engines": { - "node": ">=6" - } - }, - "node_modules/package-json/node_modules/@szmarczak/http-timer": { - "version": "1.1.2", - "resolved": "https://registry.npmjs.org/@szmarczak/http-timer/-/http-timer-1.1.2.tgz", - "integrity": "sha512-XIB2XbzHTN6ieIjfIMV9hlVcfPU26s2vafYWQcZHWXHOxiaRZYEDKEwdl129Zyg50+foYV2jCgtrqSA6qNuNSA==", - "dev": true, - "dependencies": { - "defer-to-connect": "^1.0.1" - }, - "engines": { - "node": ">=6" - } - }, - "node_modules/package-json/node_modules/cacheable-request": { - "version": "6.1.0", - "resolved": "https://registry.npmjs.org/cacheable-request/-/cacheable-request-6.1.0.tgz", - "integrity": "sha512-Oj3cAGPCqOZX7Rz64Uny2GYAZNliQSqfbePrgAQ1wKAihYmCUnraBtJtKcGR4xz7wF+LoJC+ssFZvv5BgF9Igg==", - "dev": true, - "dependencies": { - "clone-response": "^1.0.2", - "get-stream": "^5.1.0", - "http-cache-semantics": "^4.0.0", - "keyv": "^3.0.0", - "lowercase-keys": "^2.0.0", - "normalize-url": "^4.1.0", - "responselike": "^1.0.2" - }, - "engines": { - "node": ">=8" - } - }, - "node_modules/package-json/node_modules/cacheable-request/node_modules/get-stream": { - "version": "5.2.0", - "resolved": "https://registry.npmjs.org/get-stream/-/get-stream-5.2.0.tgz", - "integrity": "sha512-nBF+F1rAZVCu/p7rjzgA+Yb4lfYXrpl7a6VmJrU8wF9I1CKvP/QwPNZHnOlwbTkY6dvtFIzFMSyQXbLoTQPRpA==", + "version": "8.1.0", + "resolved": "https://registry.npmjs.org/package-json/-/package-json-8.1.0.tgz", + "integrity": "sha512-hySwcV8RAWeAfPsXb9/HGSPn8lwDnv6fabH+obUZKX169QknRkRhPxd1yMubpKDskLFATkl3jHpNtVtDPFA0Wg==", "dev": true, "dependencies": { - "pump": "^3.0.0" + "got": "^12.1.0", + "registry-auth-token": "^5.0.1", + "registry-url": "^6.0.0", + "semver": "^7.3.7" }, "engines": { - "node": ">=8" + "node": ">=14.16" }, "funding": { "url": "https://github.com/sponsors/sindresorhus" } }, - "node_modules/package-json/node_modules/cacheable-request/node_modules/lowercase-keys": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/lowercase-keys/-/lowercase-keys-2.0.0.tgz", - "integrity": "sha512-tqNXrS78oMOE73NMxK4EMLQsQowWf8jKooH9g7xPavRT706R6bkQJ6DY2Te7QukaZsulxa30wQ7bk0pm4XiHmA==", - "dev": true, - "engines": { - "node": ">=8" - } - }, - "node_modules/package-json/node_modules/decompress-response": { - "version": "3.3.0", - "resolved": "https://registry.npmjs.org/decompress-response/-/decompress-response-3.3.0.tgz", - "integrity": "sha1-gKTdMjdIOEv6JICDYirt7Jgq3/M=", + "node_modules/package-json/node_modules/lru-cache": { + "version": "6.0.0", + "resolved": "https://registry.npmjs.org/lru-cache/-/lru-cache-6.0.0.tgz", + "integrity": "sha512-Jo6dJ04CmSjuznwJSS3pUeWmd/H0ffTlkXXgwZi+eq1UCmqQwCh+eLsYOYCwY991i2Fah4h1BEMCx4qThGbsiA==", "dev": true, "dependencies": { - "mimic-response": "^1.0.0" + "yallist": "^4.0.0" }, "engines": { - "node": ">=4" + "node": ">=10" } }, - "node_modules/package-json/node_modules/defer-to-connect": { - "version": "1.1.3", - "resolved": "https://registry.npmjs.org/defer-to-connect/-/defer-to-connect-1.1.3.tgz", - "integrity": "sha512-0ISdNousHvZT2EiFlZeZAHBUvSxmKswVCEf8hW7KWgG4a8MVEu/3Vb6uWYozkjylyCxe0JBIiRB1jV45S70WVQ==", - "dev": true - }, - "node_modules/package-json/node_modules/get-stream": { - "version": "4.1.0", - "resolved": "https://registry.npmjs.org/get-stream/-/get-stream-4.1.0.tgz", - "integrity": "sha512-GMat4EJ5161kIy2HevLlr4luNjBgvmj413KaQA7jt4V8B4RDsfpHk7WQ9GVqfYyyx8OS/L66Kox+rJRNklLK7w==", + "node_modules/package-json/node_modules/semver": { + "version": "7.3.7", + "resolved": "https://registry.npmjs.org/semver/-/semver-7.3.7.tgz", + "integrity": "sha512-QlYTucUYOews+WeEujDoEGziz4K6c47V/Bd+LjSSYcA94p+DmINdf7ncaUinThfvZyu13lN9OY1XDxt8C0Tw0g==", "dev": true, "dependencies": { - "pump": "^3.0.0" + "lru-cache": "^6.0.0" }, - "engines": { - "node": ">=6" - } - }, - "node_modules/package-json/node_modules/got": { - "version": "9.6.0", - "resolved": "https://registry.npmjs.org/got/-/got-9.6.0.tgz", - "integrity": "sha512-R7eWptXuGYxwijs0eV+v3o6+XH1IqVK8dJOEecQfTmkncw9AV4dcw/Dhxi8MdlqPthxxpZyizMzyg8RTmEsG+Q==", - "dev": true, - "dependencies": { - "@sindresorhus/is": "^0.14.0", - "@szmarczak/http-timer": "^1.1.2", - "cacheable-request": "^6.0.0", - "decompress-response": "^3.3.0", - "duplexer3": "^0.1.4", - "get-stream": "^4.1.0", - "lowercase-keys": "^1.0.1", - "mimic-response": "^1.0.1", - "p-cancelable": "^1.0.0", - "to-readable-stream": "^1.0.0", - "url-parse-lax": "^3.0.0" + "bin": { + "semver": "bin/semver.js" }, "engines": { - "node": ">=8.6" + "node": ">=10" } }, - "node_modules/package-json/node_modules/json-buffer": { - "version": "3.0.0", - "resolved": "https://registry.npmjs.org/json-buffer/-/json-buffer-3.0.0.tgz", - "integrity": "sha1-Wx85evx11ne96Lz8Dkfh+aPZqJg=", + "node_modules/package-json/node_modules/yallist": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/yallist/-/yallist-4.0.0.tgz", + "integrity": "sha512-3wdGidZyq5PB084XLES5TpOSRA3wjXAlIWMhum2kRcv/41Sn2emQ0dycQW4uZXLejwKvg6EsvbdlVL+FYEct7A==", "dev": true }, - "node_modules/package-json/node_modules/keyv": { - "version": "3.1.0", - "resolved": "https://registry.npmjs.org/keyv/-/keyv-3.1.0.tgz", - "integrity": "sha512-9ykJ/46SN/9KPM/sichzQ7OvXyGDYKGTaDlKMGCAlg2UK8KRy4jb0d8sFc+0Tt0YYnThq8X2RZgCg74RPxgcVA==", - "dev": true, - "dependencies": { - "json-buffer": "3.0.0" - } - }, - "node_modules/package-json/node_modules/lowercase-keys": { - "version": "1.0.1", - "resolved": "https://registry.npmjs.org/lowercase-keys/-/lowercase-keys-1.0.1.tgz", - "integrity": "sha512-G2Lj61tXDnVFFOi8VZds+SoQjtQC3dgokKdDG2mTm1tx4m50NUHBOZSBwQQHyy0V12A0JTG4icfZQH+xPyh8VA==", - "dev": true, - "engines": { - "node": ">=0.10.0" - } - }, - "node_modules/package-json/node_modules/normalize-url": { - "version": "4.5.1", - "resolved": "https://registry.npmjs.org/normalize-url/-/normalize-url-4.5.1.tgz", - "integrity": "sha512-9UZCFRHQdNrfTpGg8+1INIg93B6zE0aXMVFkw1WFwvO4SlZywU6aLg5Of0Ap/PgcbSw4LNxvMWXMeugwMCX0AA==", - "dev": true, - "engines": { - "node": ">=8" - } - }, - "node_modules/package-json/node_modules/p-cancelable": { - "version": "1.1.0", - "resolved": "https://registry.npmjs.org/p-cancelable/-/p-cancelable-1.1.0.tgz", - "integrity": "sha512-s73XxOZ4zpt1edZYZzvhqFa6uvQc1vwUa0K0BdtIZgQMAJj9IbebH+JkgKZc9h+B05PKHLOTl4ajG1BmNrVZlw==", - "dev": true, - "engines": { - "node": ">=6" - } - }, - "node_modules/package-json/node_modules/responselike": { - "version": "1.0.2", - "resolved": "https://registry.npmjs.org/responselike/-/responselike-1.0.2.tgz", - "integrity": "sha1-kYcg7ztjHFZCvgaPFa3lpG9Loec=", - "dev": true, - "dependencies": { - "lowercase-keys": "^1.0.0" - } - }, "node_modules/parent-module": { "version": "1.0.1", "resolved": "https://registry.npmjs.org/parent-module/-/parent-module-1.0.1.tgz", @@ -5969,15 +5898,6 @@ "node": ">= 0.8.0" } }, - "node_modules/prepend-http": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/prepend-http/-/prepend-http-2.0.0.tgz", - "integrity": "sha1-6SQ0v6XqjBn0HN/UAddBo8gZ2Jc=", - "dev": true, - "engines": { - "node": ">=4" - } - }, "node_modules/process-on-spawn": { "version": "1.0.0", "resolved": "https://registry.npmjs.org/process-on-spawn/-/process-on-spawn-1.0.0.tgz", @@ -6010,6 +5930,12 @@ "url": "https://github.com/sponsors/ljharb" } }, + "node_modules/proto-list": { + "version": "1.2.4", + "resolved": "https://registry.npmjs.org/proto-list/-/proto-list-1.2.4.tgz", + "integrity": "sha512-vtK/94akxsTMhe0/cbfpR+syPuszcuwhqVjJq26CuNDgFGj682oRBXOP5MJpv2r7JtE8MsiepGIqvvOTBwn2vA==", + "dev": true + }, "node_modules/protocols": { "version": "1.4.8", "resolved": "https://registry.npmjs.org/protocols/-/protocols-1.4.8.tgz", @@ -6061,15 +5987,18 @@ } }, "node_modules/pupa": { - "version": "2.1.1", - "resolved": "https://registry.npmjs.org/pupa/-/pupa-2.1.1.tgz", - "integrity": "sha512-l1jNAspIBSFqbT+y+5FosojNpVpF94nlI+wDUpqP9enwOTfHx9f0gh5nB96vl+6yTpsJsypeNrwfzPrKuHB41A==", + "version": "3.1.0", + "resolved": "https://registry.npmjs.org/pupa/-/pupa-3.1.0.tgz", + "integrity": "sha512-FLpr4flz5xZTSJxSeaheeMKN/EDzMdK7b8PTOC6a5PYFKTucWbdqjgqaEyH0shFiSJrVB1+Qqi4Tk19ccU6Aug==", "dev": true, "dependencies": { - "escape-goat": "^2.0.0" + "escape-goat": "^4.0.0" }, "engines": { - "node": ">=8" + "node": ">=12.20" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" } }, "node_modules/qs": { @@ -6185,7 +6114,7 @@ "node_modules/rc/node_modules/strip-json-comments": { "version": "2.0.1", "resolved": "https://registry.npmjs.org/strip-json-comments/-/strip-json-comments-2.0.1.tgz", - "integrity": "sha1-PFMZQukIwml8DsNEhYwobHygpgo=", + "integrity": "sha512-4gB8na07fecVVkOI6Rs4e7T6NOTki5EmL7TUduTs6bu3EdnSycntVJ4re8kgZA+wx9IueI2Y11bfbgwtzuE0KQ==", "dev": true, "engines": { "node": ">=0.10.0" @@ -6242,33 +6171,36 @@ } }, "node_modules/registry-auth-token": { - "version": "4.2.1", - "resolved": "https://registry.npmjs.org/registry-auth-token/-/registry-auth-token-4.2.1.tgz", - "integrity": "sha512-6gkSb4U6aWJB4SF2ZvLb76yCBjcvufXBqvvEx1HbmKPkutswjW1xNVRY0+daljIYRbogN7O0etYSlbiaEQyMyw==", + "version": "5.0.1", + "resolved": "https://registry.npmjs.org/registry-auth-token/-/registry-auth-token-5.0.1.tgz", + "integrity": "sha512-UfxVOj8seK1yaIOiieV4FIP01vfBDLsY0H9sQzi9EbbUdJiuuBjJgLa1DpImXMNPnVkBD4eVxTEXcrZA6kfpJA==", "dev": true, "dependencies": { - "rc": "^1.2.8" + "@pnpm/npm-conf": "^1.0.4" }, "engines": { - "node": ">=6.0.0" + "node": ">=14" } }, "node_modules/registry-url": { - "version": "5.1.0", - "resolved": "https://registry.npmjs.org/registry-url/-/registry-url-5.1.0.tgz", - "integrity": "sha512-8acYXXTI0AkQv6RAOjE3vOaIXZkT9wo4LOFbBKYQEEnnMNBpKqdUrI6S4NT0KPIo/WVvJ5tE/X5LF/TQUf0ekw==", + "version": "6.0.1", + "resolved": "https://registry.npmjs.org/registry-url/-/registry-url-6.0.1.tgz", + "integrity": "sha512-+crtS5QjFRqFCoQmvGduwYWEBng99ZvmFvF+cUJkGYF1L1BfU8C6Zp9T7f5vPAwyLkUExpvK+ANVZmGU49qi4Q==", "dev": true, "dependencies": { - "rc": "^1.2.8" + "rc": "1.2.8" }, "engines": { - "node": ">=8" + "node": ">=12" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" } }, "node_modules/release-it": { - "version": "15.0.0", - "resolved": "https://registry.npmjs.org/release-it/-/release-it-15.0.0.tgz", - "integrity": "sha512-Dnio6p+1O88UdQZmPjdXqq+Nrrn5t0USZyOctTPK5M36kOOfQTdp8V1Wlagz9QYIYr93NwovEZ+f4wK0P/kHbw==", + "version": "15.1.1", + "resolved": "https://registry.npmjs.org/release-it/-/release-it-15.1.1.tgz", + "integrity": "sha512-c+9G8Vy1LsRIaHbV+cd8o5pEo6dkPlrOr/E7cNeWdglEbdeRJiygCyaf2F3gzNAtH8v52ntHAInG6ZevwH0KEA==", "dev": true, "dependencies": { "@iarna/toml": "2.2.5", @@ -6279,21 +6211,21 @@ "execa": "6.1.0", "form-data": "4.0.0", "git-url-parse": "11.6.0", - "globby": "13.1.1", - "got": "12.0.4", - "inquirer": "8.2.4", + "globby": "13.1.2", + "got": "12.1.0", + "inquirer": "9.0.0", "is-ci": "3.0.1", "lodash": "4.17.21", "mime-types": "2.1.35", "new-github-release-url": "2.0.0", "open": "8.4.0", - "ora": "6.1.0", + "ora": "6.1.2", "os-name": "5.0.1", "promise.allsettled": "1.0.5", "proxy-agent": "5.0.0", "semver": "7.3.7", "shelljs": "0.8.5", - "update-notifier": "5.1.0", + "update-notifier": "6.0.2", "url-join": "5.0.0", "wildcard-match": "5.1.2", "yargs-parser": "21.0.1" @@ -6306,9 +6238,9 @@ } }, "node_modules/release-it/node_modules/globby": { - "version": "13.1.1", - "resolved": "https://registry.npmjs.org/globby/-/globby-13.1.1.tgz", - "integrity": "sha512-XMzoDZbGZ37tufiv7g0N4F/zp3zkwdFtVbV3EHsVl1KQr4RPLfNoT068/97RPshz2J5xYNEjLKKBKaGHifBd3Q==", + "version": "13.1.2", + "resolved": "https://registry.npmjs.org/globby/-/globby-13.1.2.tgz", + "integrity": "sha512-LKSDZXToac40u8Q1PQtZihbNdTYSNMuWe+K5l+oa6KgDzSvVrHXlJy40hUP522RjAIoNLJYBJi7ow+rbFpIhHQ==", "dev": true, "dependencies": { "dir-glob": "^3.0.1", @@ -6447,16 +6379,19 @@ } }, "node_modules/restore-cursor": { - "version": "3.1.0", - "resolved": "https://registry.npmjs.org/restore-cursor/-/restore-cursor-3.1.0.tgz", - "integrity": "sha512-l+sSefzHpj5qimhFSE5a8nufZYAM3sBSVMAPtYkmC+4EH2anSGaEMXSD0izRQbu9nfyQ9y5JrVmp7E8oZrUjvA==", + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/restore-cursor/-/restore-cursor-4.0.0.tgz", + "integrity": "sha512-I9fPXU9geO9bHOt9pHHOhOkYerIMsmVaWB0rA2AI9ERh/+x/i7MV5HKBNrg+ljO5eoPVgCcnFuRjJ9uH6I/3eg==", "dev": true, "dependencies": { "onetime": "^5.1.0", "signal-exit": "^3.0.2" }, "engines": { - "node": ">=8" + "node": "^12.20.0 || ^14.13.1 || >=16.0.0" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" } }, "node_modules/restore-cursor/node_modules/mimic-fn": { @@ -6580,17 +6515,53 @@ } }, "node_modules/semver-diff": { - "version": "3.1.1", - "resolved": "https://registry.npmjs.org/semver-diff/-/semver-diff-3.1.1.tgz", - "integrity": "sha512-GX0Ix/CJcHyB8c4ykpHGIAvLyOwOobtM/8d+TQkAd81/bEjgPHrfba41Vpesr7jX/t8Uh+R3EX9eAS5be+jQYg==", + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/semver-diff/-/semver-diff-4.0.0.tgz", + "integrity": "sha512-0Ju4+6A8iOnpL/Thra7dZsSlOHYAHIeMxfhWQRI1/VLcT3WDBZKKtQt/QkBOsiIN9ZpuvHE6cGZ0x4glCMmfiA==", "dev": true, "dependencies": { - "semver": "^6.3.0" + "semver": "^7.3.5" }, "engines": { - "node": ">=8" + "node": ">=12" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" } }, + "node_modules/semver-diff/node_modules/lru-cache": { + "version": "6.0.0", + "resolved": "https://registry.npmjs.org/lru-cache/-/lru-cache-6.0.0.tgz", + "integrity": "sha512-Jo6dJ04CmSjuznwJSS3pUeWmd/H0ffTlkXXgwZi+eq1UCmqQwCh+eLsYOYCwY991i2Fah4h1BEMCx4qThGbsiA==", + "dev": true, + "dependencies": { + "yallist": "^4.0.0" + }, + "engines": { + "node": ">=10" + } + }, + "node_modules/semver-diff/node_modules/semver": { + "version": "7.3.7", + "resolved": "https://registry.npmjs.org/semver/-/semver-7.3.7.tgz", + "integrity": "sha512-QlYTucUYOews+WeEujDoEGziz4K6c47V/Bd+LjSSYcA94p+DmINdf7ncaUinThfvZyu13lN9OY1XDxt8C0Tw0g==", + "dev": true, + "dependencies": { + "lru-cache": "^6.0.0" + }, + "bin": { + "semver": "bin/semver.js" + }, + "engines": { + "node": ">=10" + } + }, + "node_modules/semver-diff/node_modules/yallist": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/yallist/-/yallist-4.0.0.tgz", + "integrity": "sha512-3wdGidZyq5PB084XLES5TpOSRA3wjXAlIWMhum2kRcv/41Sn2emQ0dycQW4uZXLejwKvg6EsvbdlVL+FYEct7A==", + "dev": true + }, "node_modules/serialize-javascript": { "version": "6.0.0", "resolved": "https://registry.npmjs.org/serialize-javascript/-/serialize-javascript-6.0.0.tgz", @@ -6682,9 +6653,9 @@ "dev": true }, "node_modules/sinon": { - "version": "13.0.2", - "resolved": "https://registry.npmjs.org/sinon/-/sinon-13.0.2.tgz", - "integrity": "sha512-KvOrztAVqzSJWMDoxM4vM+GPys1df2VBoXm+YciyB/OLMamfS3VXh3oGh5WtrAGSzrgczNWFFY22oKb7Fi5eeA==", + "version": "14.0.0", + "resolved": "https://registry.npmjs.org/sinon/-/sinon-14.0.0.tgz", + "integrity": "sha512-ugA6BFmE+WrJdh0owRZHToLd32Uw3Lxq6E6LtNRU+xTVBefx632h03Q7apXWRsRdZAJ41LB8aUfn2+O4jsDNMw==", "dev": true, "dependencies": { "@sinonjs/commons": "^1.8.3", @@ -6988,7 +6959,7 @@ "node_modules/through": { "version": "2.3.8", "resolved": "https://registry.npmjs.org/through/-/through-2.3.8.tgz", - "integrity": "sha1-DdTJ/6q8NXlgsbckEV1+Doai4fU=", + "integrity": "sha512-w89qg7PI8wAdvX60bMDP+bFoD5Dvhm9oLheFp5O4a2QF0cSBGsBX4qZmadPMvVqlLJBBci+WqGGOAPvcDeNSVg==", "dev": true }, "node_modules/tmp": { @@ -7012,15 +6983,6 @@ "node": ">=4" } }, - "node_modules/to-readable-stream": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/to-readable-stream/-/to-readable-stream-1.0.0.tgz", - "integrity": "sha512-Iq25XBt6zD5npPhlLVXGFN3/gyR2/qODcKNNyTMd4vbm39HUaOiAM4PMq0eMVC/Tkxz+Zjdsc55g9yyz+Yq00Q==", - "dev": true, - "engines": { - "node": ">=6" - } - }, "node_modules/to-regex-range": { "version": "5.0.1", "resolved": "https://registry.npmjs.org/to-regex-range/-/to-regex-range-5.0.1.tgz", @@ -7061,12 +7023,12 @@ } }, "node_modules/ts-node": { - "version": "10.7.0", - "resolved": "https://registry.npmjs.org/ts-node/-/ts-node-10.7.0.tgz", - "integrity": "sha512-TbIGS4xgJoX2i3do417KSaep1uRAW/Lu+WAL2doDHC0D6ummjirVOXU5/7aiZotbQ5p1Zp9tP7U6cYhA0O7M8A==", + "version": "10.8.1", + "resolved": "https://registry.npmjs.org/ts-node/-/ts-node-10.8.1.tgz", + "integrity": "sha512-Wwsnao4DQoJsN034wePSg5nZiw4YKXf56mPIAeD6wVmiv+RytNSWqc2f3fKvcUoV+Yn2+yocD71VOfQHbmVX4g==", "dev": true, "dependencies": { - "@cspotcode/source-map-support": "0.7.0", + "@cspotcode/source-map-support": "^0.8.0", "@tsconfig/node10": "^1.0.7", "@tsconfig/node12": "^1.0.7", "@tsconfig/node14": "^1.0.0", @@ -7077,7 +7039,7 @@ "create-require": "^1.1.0", "diff": "^4.0.1", "make-error": "^1.1.1", - "v8-compile-cache-lib": "^3.0.0", + "v8-compile-cache-lib": "^3.0.1", "yn": "3.1.1" }, "bin": { @@ -7161,9 +7123,9 @@ } }, "node_modules/type-fest": { - "version": "0.21.3", - "resolved": "https://registry.npmjs.org/type-fest/-/type-fest-0.21.3.tgz", - "integrity": "sha512-t0rzBq87m3fVcduHDUFhKmyyX+9eo6WQjZvf51Ea/M0Q7+T374Jp1aUiyUl0GKxp8M/OETVHSDvmkyPgvX+X2w==", + "version": "0.20.2", + "resolved": "https://registry.npmjs.org/type-fest/-/type-fest-0.20.2.tgz", + "integrity": "sha512-Ne+eE4r0/iWnpAxD852z3A+N0Bt5RN//NjJwRd2VFHEmrywxf5vsZlh4R6lixl6B+wz/8d+maTSAkN1FIkI3LQ==", "dev": true, "engines": { "node": ">=10" @@ -7182,25 +7144,24 @@ } }, "node_modules/typedoc": { - "version": "0.22.15", - "resolved": "https://registry.npmjs.org/typedoc/-/typedoc-0.22.15.tgz", - "integrity": "sha512-CMd1lrqQbFvbx6S9G6fL4HKp3GoIuhujJReWqlIvSb2T26vGai+8Os3Mde7Pn832pXYemd9BMuuYWhFpL5st0Q==", + "version": "0.23.2", + "resolved": "https://registry.npmjs.org/typedoc/-/typedoc-0.23.2.tgz", + "integrity": "sha512-THpC4vtb3wu1yck6YHzEc4ck6W4lScf8TD0Rg7XAetDih8BzP+ErYO0/6DtdzYcZyKkDwEoujkMeWW7CffCbrQ==", "dev": true, "dependencies": { - "glob": "^7.2.0", "lunr": "^2.3.9", - "marked": "^4.0.12", - "minimatch": "^5.0.1", + "marked": "^4.0.16", + "minimatch": "^5.1.0", "shiki": "^0.10.1" }, "bin": { "typedoc": "bin/typedoc" }, "engines": { - "node": ">= 12.10.0" + "node": ">= 14.14" }, "peerDependencies": { - "typescript": "4.0.x || 4.1.x || 4.2.x || 4.3.x || 4.4.x || 4.5.x || 4.6.x" + "typescript": "4.6.x || 4.7.x" } }, "node_modules/typedoc/node_modules/brace-expansion": { @@ -7213,9 +7174,9 @@ } }, "node_modules/typedoc/node_modules/minimatch": { - "version": "5.0.1", - "resolved": "https://registry.npmjs.org/minimatch/-/minimatch-5.0.1.tgz", - "integrity": "sha512-nLDxIFRyhDblz3qMuq+SoRZED4+miJ/G+tdDrjkkkRnjAsBexeGpgjLEQ0blJy7rHhR2b93rhQY4SvyWu9v03g==", + "version": "5.1.0", + "resolved": "https://registry.npmjs.org/minimatch/-/minimatch-5.1.0.tgz", + "integrity": "sha512-9TPBGGak4nHfGZsPBohm9AWg6NoT7QTCehS3BIJABslyZbzxfV78QM2Y6+i741OPZIafFAaiiEMh5OyIrJPgtg==", "dev": true, "dependencies": { "brace-expansion": "^2.0.1" @@ -7225,9 +7186,9 @@ } }, "node_modules/typescript": { - "version": "4.6.4", - "resolved": "https://registry.npmjs.org/typescript/-/typescript-4.6.4.tgz", - "integrity": "sha512-9ia/jWHIEbo49HfjrLGfKbZSuWo9iTMwXO+Ca3pRsSpbsMbc7/IU8NKdCZVRRBafVPGnoJeFL76ZOAA84I9fEg==", + "version": "4.7.4", + "resolved": "https://registry.npmjs.org/typescript/-/typescript-4.7.4.tgz", + "integrity": "sha512-C0WQT0gezHuw6AdY1M2jxUO83Rjf0HP7Sk1DtXj6j1EwkQNZrHAg2XPWlq62oqEhYvONq5pkC2Y9oPljWToLmQ==", "dev": true, "bin": { "tsc": "bin/tsc", @@ -7253,15 +7214,18 @@ } }, "node_modules/unique-string": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/unique-string/-/unique-string-2.0.0.tgz", - "integrity": "sha512-uNaeirEPvpZWSgzwsPGtU2zVSTrn/8L5q/IexZmH0eH6SA73CmAA5U4GwORTxQAZs95TAXLNqeLoPPNO5gZfWg==", + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/unique-string/-/unique-string-3.0.0.tgz", + "integrity": "sha512-VGXBUVwxKMBUznyffQweQABPRRW1vHZAbadFZud4pLFAqRGvv/96vafgjWFqzourzr8YonlQiPgH0YCJfawoGQ==", "dev": true, "dependencies": { - "crypto-random-string": "^2.0.0" + "crypto-random-string": "^4.0.0" }, "engines": { - "node": ">=8" + "node": ">=12" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" } }, "node_modules/universal-user-agent": { @@ -7289,67 +7253,33 @@ } }, "node_modules/update-notifier": { - "version": "5.1.0", - "resolved": "https://registry.npmjs.org/update-notifier/-/update-notifier-5.1.0.tgz", - "integrity": "sha512-ItnICHbeMh9GqUy31hFPrD1kcuZ3rpxDZbf4KUDavXwS0bW5m7SLbDQpGX3UYr072cbrF5hFUs3r5tUsPwjfHw==", + "version": "6.0.2", + "resolved": "https://registry.npmjs.org/update-notifier/-/update-notifier-6.0.2.tgz", + "integrity": "sha512-EDxhTEVPZZRLWYcJ4ZXjGFN0oP7qYvbXWzEgRm/Yql4dHX5wDbvh89YHP6PK1lzZJYrMtXUuZZz8XGK+U6U1og==", "dev": true, "dependencies": { - "boxen": "^5.0.0", - "chalk": "^4.1.0", - "configstore": "^5.0.1", - "has-yarn": "^2.1.0", - "import-lazy": "^2.1.0", - "is-ci": "^2.0.0", + "boxen": "^7.0.0", + "chalk": "^5.0.1", + "configstore": "^6.0.0", + "has-yarn": "^3.0.0", + "import-lazy": "^4.0.0", + "is-ci": "^3.0.1", "is-installed-globally": "^0.4.0", - "is-npm": "^5.0.0", - "is-yarn-global": "^0.3.0", - "latest-version": "^5.1.0", - "pupa": "^2.1.1", - "semver": "^7.3.4", - "semver-diff": "^3.1.1", - "xdg-basedir": "^4.0.0" + "is-npm": "^6.0.0", + "is-yarn-global": "^0.4.0", + "latest-version": "^7.0.0", + "pupa": "^3.1.0", + "semver": "^7.3.7", + "semver-diff": "^4.0.0", + "xdg-basedir": "^5.1.0" }, "engines": { - "node": ">=10" + "node": ">=14.16" }, "funding": { "url": "https://github.com/yeoman/update-notifier?sponsor=1" } }, - "node_modules/update-notifier/node_modules/chalk": { - "version": "4.1.2", - "resolved": "https://registry.npmjs.org/chalk/-/chalk-4.1.2.tgz", - "integrity": "sha512-oKnbhFyRIXpUuez8iBMmyEa4nbj4IOQyuhc/wy9kY7/WVPcwIO9VA668Pu8RkO7+0G76SLROeyw9CpQ061i4mA==", - "dev": true, - "dependencies": { - "ansi-styles": "^4.1.0", - "supports-color": "^7.1.0" - }, - "engines": { - "node": ">=10" - }, - "funding": { - "url": "https://github.com/chalk/chalk?sponsor=1" - } - }, - "node_modules/update-notifier/node_modules/ci-info": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/ci-info/-/ci-info-2.0.0.tgz", - "integrity": "sha512-5tK7EtrZ0N+OLFMthtqOj4fI2Jeb88C4CAZPu25LDVUgXJ0A3Js4PMGqrn0JU1W0Mh1/Z8wZzYPxqUrXeBboCQ==", - "dev": true - }, - "node_modules/update-notifier/node_modules/is-ci": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/is-ci/-/is-ci-2.0.0.tgz", - "integrity": "sha512-YfJT7rkpQB0updsdHLGWrvhBJfcfzNNawYDNIyQXJz0IViGf75O8EBPKSdvw2rF+LGCsX4FZ8tcr3b19LcZq4w==", - "dev": true, - "dependencies": { - "ci-info": "^2.0.0" - }, - "bin": { - "is-ci": "bin.js" - } - }, "node_modules/update-notifier/node_modules/lru-cache": { "version": "6.0.0", "resolved": "https://registry.npmjs.org/lru-cache/-/lru-cache-6.0.0.tgz", @@ -7401,22 +7331,10 @@ "node": "^12.20.0 || ^14.13.1 || >=16.0.0" } }, - "node_modules/url-parse-lax": { - "version": "3.0.0", - "resolved": "https://registry.npmjs.org/url-parse-lax/-/url-parse-lax-3.0.0.tgz", - "integrity": "sha1-FrXK/Afb42dsGxmZF3gj1lA6yww=", - "dev": true, - "dependencies": { - "prepend-http": "^2.0.0" - }, - "engines": { - "node": ">=4" - } - }, "node_modules/util-deprecate": { "version": "1.0.2", "resolved": "https://registry.npmjs.org/util-deprecate/-/util-deprecate-1.0.2.tgz", - "integrity": "sha1-RQ1Nyfpw3nMnYvvS1KKJgUGaDM8=", + "integrity": "sha512-EPD5q1uXyFxJpCrLnCc1nHnq3gOa6DZBocAIiI2TaSCA7VCJ1UJDMagCzIkXNsUYfD1daK//LTEQ8xiIbrHtcw==", "dev": true }, "node_modules/uuid": { @@ -7472,7 +7390,7 @@ "node_modules/wcwidth": { "version": "1.0.1", "resolved": "https://registry.npmjs.org/wcwidth/-/wcwidth-1.0.1.tgz", - "integrity": "sha1-8LDc+RW8X/FSivrbLA4XtTLaL+g=", + "integrity": "sha512-XHPEwS0q6TaxcvG85+8EYkbiCux2XtWG2mkc47Ng2A77BQu9+DqIOJldST4HgPkuea7dvKSj5VgX3P1d4rW8Tg==", "dev": true, "dependencies": { "defaults": "^1.0.3" @@ -7532,15 +7450,68 @@ "dev": true }, "node_modules/widest-line": { - "version": "3.1.0", - "resolved": "https://registry.npmjs.org/widest-line/-/widest-line-3.1.0.tgz", - "integrity": "sha512-NsmoXalsWVDMGupxZ5R08ka9flZjjiLvHVAWYOKtiKM8ujtZWr9cRffak+uSE48+Ob8ObalXpwyeUiyDD6QFgg==", + "version": "4.0.1", + "resolved": "https://registry.npmjs.org/widest-line/-/widest-line-4.0.1.tgz", + "integrity": "sha512-o0cyEG0e8GPzT4iGHphIOh0cJOV8fivsXxddQasHPHfoZf1ZexrfeA21w2NaEN1RHE+fXlfISmOE8R9N3u3Qig==", "dev": true, "dependencies": { - "string-width": "^4.0.0" + "string-width": "^5.0.1" }, "engines": { - "node": ">=8" + "node": ">=12" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/widest-line/node_modules/ansi-regex": { + "version": "6.0.1", + "resolved": "https://registry.npmjs.org/ansi-regex/-/ansi-regex-6.0.1.tgz", + "integrity": "sha512-n5M855fKb2SsfMIiFFoVrABHJC8QtHwVx+mHWP3QcEqBHYienj5dHSgjbxtC0WEZXYt4wcD6zrQElDPhFuZgfA==", + "dev": true, + "engines": { + "node": ">=12" + }, + "funding": { + "url": "https://github.com/chalk/ansi-regex?sponsor=1" + } + }, + "node_modules/widest-line/node_modules/emoji-regex": { + "version": "9.2.2", + "resolved": "https://registry.npmjs.org/emoji-regex/-/emoji-regex-9.2.2.tgz", + "integrity": "sha512-L18DaJsXSUk2+42pv8mLs5jJT2hqFkFE4j21wOmgbUqsZ2hL72NsUU785g9RXgo3s0ZNgVl42TiHp3ZtOv/Vyg==", + "dev": true + }, + "node_modules/widest-line/node_modules/string-width": { + "version": "5.1.2", + "resolved": "https://registry.npmjs.org/string-width/-/string-width-5.1.2.tgz", + "integrity": "sha512-HnLOCR3vjcY8beoNLtcjZ5/nxn2afmME6lhrDrebokqMap+XbeW8n9TXpPDOqdGK5qcI3oT0GKTW6wC7EMiVqA==", + "dev": true, + "dependencies": { + "eastasianwidth": "^0.2.0", + "emoji-regex": "^9.2.2", + "strip-ansi": "^7.0.1" + }, + "engines": { + "node": ">=12" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/widest-line/node_modules/strip-ansi": { + "version": "7.0.1", + "resolved": "https://registry.npmjs.org/strip-ansi/-/strip-ansi-7.0.1.tgz", + "integrity": "sha512-cXNxvT8dFNRVfhVME3JAe98mkXDYN2O1l7jmcwMnOslDeESg1rF/OZMtK0nRAhiari1unG5cD4jG3rapUAkLbw==", + "dev": true, + "dependencies": { + "ansi-regex": "^6.0.1" + }, + "engines": { + "node": ">=12" + }, + "funding": { + "url": "https://github.com/chalk/strip-ansi?sponsor=1" } }, "node_modules/wildcard-match": { @@ -7704,12 +7675,15 @@ } }, "node_modules/xdg-basedir": { - "version": "4.0.0", - "resolved": "https://registry.npmjs.org/xdg-basedir/-/xdg-basedir-4.0.0.tgz", - "integrity": "sha512-PSNhEJDejZYV7h50BohL09Er9VaIefr2LMAf3OEmpCkjOi34eYyQYAXUTjEQtZJTKcF0E2UKTh+osDLsgNim9Q==", + "version": "5.1.0", + "resolved": "https://registry.npmjs.org/xdg-basedir/-/xdg-basedir-5.1.0.tgz", + "integrity": "sha512-GCPAHLvrIH13+c0SuacwvRYj2SxJXQ4kaVTT5xgL3kPrz56XxkF21IGhjSE1+W0aw7gpBWRGXLCPnPby6lSpmQ==", "dev": true, "engines": { - "node": ">=8" + "node": ">=12" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" } }, "node_modules/xregexp": { @@ -7853,13 +7827,13 @@ "devDependencies": { "@istanbuljs/nyc-config-typescript": "^1.0.2", "@redis/test-utils": "*", - "@types/node": "^17.0.31", + "@types/node": "^18.0.0", "nyc": "^15.1.0", - "release-it": "^15.0.0", + "release-it": "^15.1.1", "source-map-support": "^0.5.21", - "ts-node": "^10.7.0", - "typedoc": "^0.22.15", - "typescript": "^4.6.4" + "ts-node": "^10.8.1", + "typedoc": "^0.23.2", + "typescript": "^4.7.4" }, "peerDependencies": { "@redis/client": "^1.0.0" @@ -7877,19 +7851,19 @@ "devDependencies": { "@istanbuljs/nyc-config-typescript": "^1.0.2", "@redis/test-utils": "*", - "@types/node": "^17.0.31", - "@types/sinon": "^10.0.11", + "@types/node": "^18.0.0", + "@types/sinon": "^10.0.12", "@types/yallist": "^4.0.1", - "@typescript-eslint/eslint-plugin": "^5.21.0", - "@typescript-eslint/parser": "^5.21.0", - "eslint": "^8.14.0", + "@typescript-eslint/eslint-plugin": "^5.30.0", + "@typescript-eslint/parser": "^5.30.0", + "eslint": "^8.18.0", "nyc": "^15.1.0", - "release-it": "^15.0.0", - "sinon": "^13.0.2", + "release-it": "^15.1.1", + "sinon": "^14.0.0", "source-map-support": "^0.5.21", - "ts-node": "^10.7.0", - "typedoc": "^0.22.15", - "typescript": "^4.6.4" + "ts-node": "^10.8.1", + "typedoc": "^0.23.2", + "typescript": "^4.7.4" }, "engines": { "node": ">=14" @@ -7907,13 +7881,13 @@ "devDependencies": { "@istanbuljs/nyc-config-typescript": "^1.0.2", "@redis/test-utils": "*", - "@types/node": "^17.0.31", + "@types/node": "^18.0.0", "nyc": "^15.1.0", - "release-it": "^15.0.0", + "release-it": "^15.1.1", "source-map-support": "^0.5.21", - "ts-node": "^10.7.0", - "typedoc": "^0.22.15", - "typescript": "^4.6.4" + "ts-node": "^10.8.1", + "typedoc": "^0.23.2", + "typescript": "^4.7.4" }, "peerDependencies": { "@redis/client": "^1.0.0" @@ -7926,13 +7900,13 @@ "devDependencies": { "@istanbuljs/nyc-config-typescript": "^1.0.2", "@redis/test-utils": "*", - "@types/node": "^17.0.31", + "@types/node": "^18.0.0", "nyc": "^15.1.0", - "release-it": "^15.0.0", + "release-it": "^15.1.1", "source-map-support": "^0.5.21", - "ts-node": "^10.7.0", - "typedoc": "^0.22.15", - "typescript": "^4.6.4" + "ts-node": "^10.8.1", + "typedoc": "^0.23.2", + "typescript": "^4.7.4" }, "peerDependencies": { "@redis/client": "^1.0.0" @@ -7945,13 +7919,13 @@ "devDependencies": { "@istanbuljs/nyc-config-typescript": "^1.0.2", "@redis/test-utils": "*", - "@types/node": "^17.0.31", + "@types/node": "^18.0.0", "nyc": "^15.1.0", - "release-it": "^15.0.0", + "release-it": "^15.1.1", "source-map-support": "^0.5.21", - "ts-node": "^10.7.0", - "typedoc": "^0.22.15", - "typescript": "^4.6.4" + "ts-node": "^10.8.1", + "typedoc": "^0.23.2", + "typescript": "^4.7.4" }, "peerDependencies": { "@redis/client": "^1.0.0" @@ -7962,14 +7936,14 @@ "devDependencies": { "@istanbuljs/nyc-config-typescript": "^1.0.2", "@types/mocha": "^9.1.1", - "@types/node": "^17.0.31", + "@types/node": "^18.0.0", "@types/yargs": "^17.0.10", "mocha": "^10.0.0", "nyc": "^15.1.0", "source-map-support": "^0.5.21", - "ts-node": "^10.7.0", - "typescript": "^4.6.4", - "yargs": "^17.4.1" + "ts-node": "^10.8.1", + "typescript": "^4.7.4", + "yargs": "^17.5.1" }, "peerDependencies": { "@redis/client": "^1.0.0" @@ -7996,9 +7970,9 @@ } }, "packages/test-utils/node_modules/yargs": { - "version": "17.4.1", - "resolved": "https://registry.npmjs.org/yargs/-/yargs-17.4.1.tgz", - "integrity": "sha512-WSZD9jgobAg3ZKuCQZSa3g9QOJeCCqLoLAykiWgmXnDo9EPnn4RPf5qVTtzgOx66o6/oqhcA5tHtJXpG8pMt3g==", + "version": "17.5.1", + "resolved": "https://registry.npmjs.org/yargs/-/yargs-17.5.1.tgz", + "integrity": "sha512-t6YAJcxDkNX7NFYiVtKvWUz8l+PaKTLiL63mJYWR2GnHq2gjEWISzsLp9wg3aY36dY1j+gfIEL3pIF+XlJJfbA==", "dev": true, "dependencies": { "cliui": "^7.0.2", @@ -8020,13 +7994,13 @@ "devDependencies": { "@istanbuljs/nyc-config-typescript": "^1.0.2", "@redis/test-utils": "*", - "@types/node": "^17.0.31", + "@types/node": "^18.0.0", "nyc": "^15.1.0", - "release-it": "^15.0.0", + "release-it": "^15.1.1", "source-map-support": "^0.5.21", - "ts-node": "^10.7.0", - "typedoc": "^0.22.15", - "typescript": "^4.6.4" + "ts-node": "^10.8.1", + "typedoc": "^0.23.2", + "typescript": "^4.7.4" }, "peerDependencies": { "@redis/client": "^1.0.0" @@ -8315,35 +8289,29 @@ "to-fast-properties": "^2.0.0" } }, - "@cspotcode/source-map-consumer": { - "version": "0.8.0", - "resolved": "https://registry.npmjs.org/@cspotcode/source-map-consumer/-/source-map-consumer-0.8.0.tgz", - "integrity": "sha512-41qniHzTU8yAGbCp04ohlmSrZf8bkf/iJsl3V0dRGsQN/5GFfx+LbCSsCpp2gqrqjTVg/K6O8ycoV35JIwAzAg==", - "dev": true - }, "@cspotcode/source-map-support": { - "version": "0.7.0", - "resolved": "https://registry.npmjs.org/@cspotcode/source-map-support/-/source-map-support-0.7.0.tgz", - "integrity": "sha512-X4xqRHqN8ACt2aHVe51OxeA2HjbcL4MqFqXkrmQszJ1NOUuUu5u6Vqx/0lZSVNku7velL5FC/s5uEAj1lsBMhA==", + "version": "0.8.1", + "resolved": "https://registry.npmjs.org/@cspotcode/source-map-support/-/source-map-support-0.8.1.tgz", + "integrity": "sha512-IchNf6dN4tHoMFIn/7OE8LWZ19Y6q/67Bmf6vnGREv8RSbBVb9LPJxEcnwrcwX6ixSvaiGoomAUvu4YSxXrVgw==", "dev": true, "requires": { - "@cspotcode/source-map-consumer": "0.8.0" + "@jridgewell/trace-mapping": "0.3.9" } }, "@eslint/eslintrc": { - "version": "1.2.2", - "resolved": "https://registry.npmjs.org/@eslint/eslintrc/-/eslintrc-1.2.2.tgz", - "integrity": "sha512-lTVWHs7O2hjBFZunXTZYnYqtB9GakA1lnxIf+gKq2nY5gxkkNi/lQvveW6t8gFdOHTg6nG50Xs95PrLqVpcaLg==", + "version": "1.3.0", + "resolved": "https://registry.npmjs.org/@eslint/eslintrc/-/eslintrc-1.3.0.tgz", + "integrity": "sha512-UWW0TMTmk2d7hLcWD1/e2g5HDM/HQ3csaLSqXCfqwh4uNDuNqlaKWXmEsL4Cs41Z0KnILNvwbHAah3C2yt06kw==", "dev": true, "requires": { "ajv": "^6.12.4", "debug": "^4.3.2", - "espree": "^9.3.1", - "globals": "^13.9.0", + "espree": "^9.3.2", + "globals": "^13.15.0", "ignore": "^5.2.0", "import-fresh": "^3.2.1", "js-yaml": "^4.1.0", - "minimatch": "^3.0.4", + "minimatch": "^3.1.2", "strip-json-comments": "^3.1.1" } }, @@ -8613,18 +8581,37 @@ "@octokit/openapi-types": "^11.2.0" } }, + "@pnpm/network.ca-file": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/@pnpm/network.ca-file/-/network.ca-file-1.0.1.tgz", + "integrity": "sha512-gkINruT2KUhZLTaiHxwCOh1O4NVnFT0wLjWFBHmTz9vpKag/C/noIMJXBxFe4F0mYpUVX2puLwAieLYFg2NvoA==", + "dev": true, + "requires": { + "graceful-fs": "4.2.10" + } + }, + "@pnpm/npm-conf": { + "version": "1.0.4", + "resolved": "https://registry.npmjs.org/@pnpm/npm-conf/-/npm-conf-1.0.4.tgz", + "integrity": "sha512-o5YFq/+ksEJMbSzzkaQDHlp00aonLDU5xNPVTRL12hTWBbVSSeWXxPukq75h+mvXnoOWT95vV2u1HSTw2C4XOw==", + "dev": true, + "requires": { + "@pnpm/network.ca-file": "^1.0.1", + "config-chain": "^1.1.11" + } + }, "@redis/bloom": { "version": "file:packages/bloom", "requires": { "@istanbuljs/nyc-config-typescript": "^1.0.2", "@redis/test-utils": "*", - "@types/node": "^17.0.31", + "@types/node": "^18.0.0", "nyc": "^15.1.0", - "release-it": "^15.0.0", + "release-it": "^15.1.1", "source-map-support": "^0.5.21", - "ts-node": "^10.7.0", - "typedoc": "^0.22.15", - "typescript": "^4.6.4" + "ts-node": "^10.8.1", + "typedoc": "^0.23.2", + "typescript": "^4.7.4" } }, "@redis/client": { @@ -8632,21 +8619,21 @@ "requires": { "@istanbuljs/nyc-config-typescript": "^1.0.2", "@redis/test-utils": "*", - "@types/node": "^17.0.31", - "@types/sinon": "^10.0.11", + "@types/node": "^18.0.0", + "@types/sinon": "^10.0.12", "@types/yallist": "^4.0.1", - "@typescript-eslint/eslint-plugin": "^5.21.0", - "@typescript-eslint/parser": "^5.21.0", + "@typescript-eslint/eslint-plugin": "^5.30.0", + "@typescript-eslint/parser": "^5.30.0", "cluster-key-slot": "1.1.0", - "eslint": "^8.14.0", + "eslint": "^8.18.0", "generic-pool": "3.8.2", "nyc": "^15.1.0", - "release-it": "^15.0.0", - "sinon": "^13.0.2", + "release-it": "^15.1.1", + "sinon": "^14.0.0", "source-map-support": "^0.5.21", - "ts-node": "^10.7.0", - "typedoc": "^0.22.15", - "typescript": "^4.6.4", + "ts-node": "^10.8.1", + "typedoc": "^0.23.2", + "typescript": "^4.7.4", "yallist": "4.0.0" }, "dependencies": { @@ -8662,13 +8649,13 @@ "requires": { "@istanbuljs/nyc-config-typescript": "^1.0.2", "@redis/test-utils": "*", - "@types/node": "^17.0.31", + "@types/node": "^18.0.0", "nyc": "^15.1.0", - "release-it": "^15.0.0", + "release-it": "^15.1.1", "source-map-support": "^0.5.21", - "ts-node": "^10.7.0", - "typedoc": "^0.22.15", - "typescript": "^4.6.4" + "ts-node": "^10.8.1", + "typedoc": "^0.23.2", + "typescript": "^4.7.4" } }, "@redis/json": { @@ -8676,13 +8663,13 @@ "requires": { "@istanbuljs/nyc-config-typescript": "^1.0.2", "@redis/test-utils": "*", - "@types/node": "^17.0.31", + "@types/node": "^18.0.0", "nyc": "^15.1.0", - "release-it": "^15.0.0", + "release-it": "^15.1.1", "source-map-support": "^0.5.21", - "ts-node": "^10.7.0", - "typedoc": "^0.22.15", - "typescript": "^4.6.4" + "ts-node": "^10.8.1", + "typedoc": "^0.23.2", + "typescript": "^4.7.4" } }, "@redis/search": { @@ -8690,13 +8677,13 @@ "requires": { "@istanbuljs/nyc-config-typescript": "^1.0.2", "@redis/test-utils": "*", - "@types/node": "^17.0.31", + "@types/node": "^18.0.0", "nyc": "^15.1.0", - "release-it": "^15.0.0", + "release-it": "^15.1.1", "source-map-support": "^0.5.21", - "ts-node": "^10.7.0", - "typedoc": "^0.22.15", - "typescript": "^4.6.4" + "ts-node": "^10.8.1", + "typedoc": "^0.23.2", + "typescript": "^4.7.4" } }, "@redis/test-utils": { @@ -8704,14 +8691,14 @@ "requires": { "@istanbuljs/nyc-config-typescript": "^1.0.2", "@types/mocha": "^9.1.1", - "@types/node": "^17.0.31", + "@types/node": "^18.0.0", "@types/yargs": "^17.0.10", "mocha": "^10.0.0", "nyc": "^15.1.0", "source-map-support": "^0.5.21", - "ts-node": "^10.7.0", - "typescript": "^4.6.4", - "yargs": "^17.4.1" + "ts-node": "^10.8.1", + "typescript": "^4.7.4", + "yargs": "^17.5.1" }, "dependencies": { "cliui": { @@ -8732,9 +8719,9 @@ "dev": true }, "yargs": { - "version": "17.4.1", - "resolved": "https://registry.npmjs.org/yargs/-/yargs-17.4.1.tgz", - "integrity": "sha512-WSZD9jgobAg3ZKuCQZSa3g9QOJeCCqLoLAykiWgmXnDo9EPnn4RPf5qVTtzgOx66o6/oqhcA5tHtJXpG8pMt3g==", + "version": "17.5.1", + "resolved": "https://registry.npmjs.org/yargs/-/yargs-17.5.1.tgz", + "integrity": "sha512-t6YAJcxDkNX7NFYiVtKvWUz8l+PaKTLiL63mJYWR2GnHq2gjEWISzsLp9wg3aY36dY1j+gfIEL3pIF+XlJJfbA==", "dev": true, "requires": { "cliui": "^7.0.2", @@ -8753,13 +8740,13 @@ "requires": { "@istanbuljs/nyc-config-typescript": "^1.0.2", "@redis/test-utils": "*", - "@types/node": "^17.0.31", + "@types/node": "^18.0.0", "nyc": "^15.1.0", - "release-it": "^15.0.0", + "release-it": "^15.1.1", "source-map-support": "^0.5.21", - "ts-node": "^10.7.0", - "typedoc": "^0.22.15", - "typescript": "^4.6.4" + "ts-node": "^10.8.1", + "typedoc": "^0.23.2", + "typescript": "^4.7.4" } }, "@sindresorhus/is": { @@ -8888,9 +8875,9 @@ "dev": true }, "@types/node": { - "version": "17.0.31", - "resolved": "https://registry.npmjs.org/@types/node/-/node-17.0.31.tgz", - "integrity": "sha512-AR0x5HbXGqkEx9CadRH3EBYx/VkiUgZIhP4wvPn/+5KIsgpNoyFaRlVe0Zlx9gRtg8fA06a9tskE2MSN7TcG4Q==", + "version": "18.0.0", + "resolved": "https://registry.npmjs.org/@types/node/-/node-18.0.0.tgz", + "integrity": "sha512-cHlGmko4gWLVI27cGJntjs/Sj8th9aYwplmZFwmmgYQQvL5NUsgVJG7OddLvNfLqYS31KFN0s3qlaD9qCaxACA==", "dev": true }, "@types/parse-json": { @@ -8909,9 +8896,9 @@ } }, "@types/sinon": { - "version": "10.0.11", - "resolved": "https://registry.npmjs.org/@types/sinon/-/sinon-10.0.11.tgz", - "integrity": "sha512-dmZsHlBsKUtBpHriNjlK0ndlvEh8dcb9uV9Afsbt89QIyydpC7NcR+nWlAhASfy3GHnxTl4FX/aKE7XZUt/B4g==", + "version": "10.0.12", + "resolved": "https://registry.npmjs.org/@types/sinon/-/sinon-10.0.12.tgz", + "integrity": "sha512-uWf4QJ4oky/GckJ1MYQxU52cgVDcXwBhDkpvLbi4EKoLPqLE4MOH6T/ttM33l3hi0oZ882G6oIzWv/oupRYSxQ==", "dev": true, "requires": { "@types/sinonjs__fake-timers": "*" @@ -8945,19 +8932,19 @@ "dev": true }, "@typescript-eslint/eslint-plugin": { - "version": "5.21.0", - "resolved": "https://registry.npmjs.org/@typescript-eslint/eslint-plugin/-/eslint-plugin-5.21.0.tgz", - "integrity": "sha512-fTU85q8v5ZLpoZEyn/u1S2qrFOhi33Edo2CZ0+q1gDaWWm0JuPh3bgOyU8lM0edIEYgKLDkPFiZX2MOupgjlyg==", + "version": "5.30.0", + "resolved": "https://registry.npmjs.org/@typescript-eslint/eslint-plugin/-/eslint-plugin-5.30.0.tgz", + "integrity": "sha512-lvhRJ2pGe2V9MEU46ELTdiHgiAFZPKtLhiU5wlnaYpMc2+c1R8fh8i80ZAa665drvjHKUJyRRGg3gEm1If54ow==", "dev": true, "requires": { - "@typescript-eslint/scope-manager": "5.21.0", - "@typescript-eslint/type-utils": "5.21.0", - "@typescript-eslint/utils": "5.21.0", - "debug": "^4.3.2", + "@typescript-eslint/scope-manager": "5.30.0", + "@typescript-eslint/type-utils": "5.30.0", + "@typescript-eslint/utils": "5.30.0", + "debug": "^4.3.4", "functional-red-black-tree": "^1.0.1", - "ignore": "^5.1.8", + "ignore": "^5.2.0", "regexpp": "^3.2.0", - "semver": "^7.3.5", + "semver": "^7.3.7", "tsutils": "^3.21.0" }, "dependencies": { @@ -8988,56 +8975,56 @@ } }, "@typescript-eslint/parser": { - "version": "5.21.0", - "resolved": "https://registry.npmjs.org/@typescript-eslint/parser/-/parser-5.21.0.tgz", - "integrity": "sha512-8RUwTO77hstXUr3pZoWZbRQUxXcSXafZ8/5gpnQCfXvgmP9gpNlRGlWzvfbEQ14TLjmtU8eGnONkff8U2ui2Eg==", + "version": "5.30.0", + "resolved": "https://registry.npmjs.org/@typescript-eslint/parser/-/parser-5.30.0.tgz", + "integrity": "sha512-2oYYUws5o2liX6SrFQ5RB88+PuRymaM2EU02/9Ppoyu70vllPnHVO7ioxDdq/ypXHA277R04SVjxvwI8HmZpzA==", "dev": true, "requires": { - "@typescript-eslint/scope-manager": "5.21.0", - "@typescript-eslint/types": "5.21.0", - "@typescript-eslint/typescript-estree": "5.21.0", - "debug": "^4.3.2" + "@typescript-eslint/scope-manager": "5.30.0", + "@typescript-eslint/types": "5.30.0", + "@typescript-eslint/typescript-estree": "5.30.0", + "debug": "^4.3.4" } }, "@typescript-eslint/scope-manager": { - "version": "5.21.0", - "resolved": "https://registry.npmjs.org/@typescript-eslint/scope-manager/-/scope-manager-5.21.0.tgz", - "integrity": "sha512-XTX0g0IhvzcH/e3393SvjRCfYQxgxtYzL3UREteUneo72EFlt7UNoiYnikUtmGVobTbhUDByhJ4xRBNe+34kOQ==", + "version": "5.30.0", + "resolved": "https://registry.npmjs.org/@typescript-eslint/scope-manager/-/scope-manager-5.30.0.tgz", + "integrity": "sha512-3TZxvlQcK5fhTBw5solQucWSJvonXf5yua5nx8OqK94hxdrT7/6W3/CS42MLd/f1BmlmmbGEgQcTHHCktUX5bQ==", "dev": true, "requires": { - "@typescript-eslint/types": "5.21.0", - "@typescript-eslint/visitor-keys": "5.21.0" + "@typescript-eslint/types": "5.30.0", + "@typescript-eslint/visitor-keys": "5.30.0" } }, "@typescript-eslint/type-utils": { - "version": "5.21.0", - "resolved": "https://registry.npmjs.org/@typescript-eslint/type-utils/-/type-utils-5.21.0.tgz", - "integrity": "sha512-MxmLZj0tkGlkcZCSE17ORaHl8Th3JQwBzyXL/uvC6sNmu128LsgjTX0NIzy+wdH2J7Pd02GN8FaoudJntFvSOw==", + "version": "5.30.0", + "resolved": "https://registry.npmjs.org/@typescript-eslint/type-utils/-/type-utils-5.30.0.tgz", + "integrity": "sha512-GF8JZbZqSS+azehzlv/lmQQ3EU3VfWYzCczdZjJRxSEeXDQkqFhCBgFhallLDbPwQOEQ4MHpiPfkjKk7zlmeNg==", "dev": true, "requires": { - "@typescript-eslint/utils": "5.21.0", - "debug": "^4.3.2", + "@typescript-eslint/utils": "5.30.0", + "debug": "^4.3.4", "tsutils": "^3.21.0" } }, "@typescript-eslint/types": { - "version": "5.21.0", - "resolved": "https://registry.npmjs.org/@typescript-eslint/types/-/types-5.21.0.tgz", - "integrity": "sha512-XnOOo5Wc2cBlq8Lh5WNvAgHzpjnEzxn4CJBwGkcau7b/tZ556qrWXQz4DJyChYg8JZAD06kczrdgFPpEQZfDsA==", + "version": "5.30.0", + "resolved": "https://registry.npmjs.org/@typescript-eslint/types/-/types-5.30.0.tgz", + "integrity": "sha512-vfqcBrsRNWw/LBXyncMF/KrUTYYzzygCSsVqlZ1qGu1QtGs6vMkt3US0VNSQ05grXi5Yadp3qv5XZdYLjpp8ag==", "dev": true }, "@typescript-eslint/typescript-estree": { - "version": "5.21.0", - "resolved": "https://registry.npmjs.org/@typescript-eslint/typescript-estree/-/typescript-estree-5.21.0.tgz", - "integrity": "sha512-Y8Y2T2FNvm08qlcoSMoNchh9y2Uj3QmjtwNMdRQkcFG7Muz//wfJBGBxh8R7HAGQFpgYpdHqUpEoPQk+q9Kjfg==", + "version": "5.30.0", + "resolved": "https://registry.npmjs.org/@typescript-eslint/typescript-estree/-/typescript-estree-5.30.0.tgz", + "integrity": "sha512-hDEawogreZB4n1zoqcrrtg/wPyyiCxmhPLpZ6kmWfKF5M5G0clRLaEexpuWr31fZ42F96SlD/5xCt1bT5Qm4Nw==", "dev": true, "requires": { - "@typescript-eslint/types": "5.21.0", - "@typescript-eslint/visitor-keys": "5.21.0", - "debug": "^4.3.2", - "globby": "^11.0.4", + "@typescript-eslint/types": "5.30.0", + "@typescript-eslint/visitor-keys": "5.30.0", + "debug": "^4.3.4", + "globby": "^11.1.0", "is-glob": "^4.0.3", - "semver": "^7.3.5", + "semver": "^7.3.7", "tsutils": "^3.21.0" }, "dependencies": { @@ -9088,27 +9075,27 @@ } }, "@typescript-eslint/utils": { - "version": "5.21.0", - "resolved": "https://registry.npmjs.org/@typescript-eslint/utils/-/utils-5.21.0.tgz", - "integrity": "sha512-q/emogbND9wry7zxy7VYri+7ydawo2HDZhRZ5k6yggIvXa7PvBbAAZ4PFH/oZLem72ezC4Pr63rJvDK/sTlL8Q==", + "version": "5.30.0", + "resolved": "https://registry.npmjs.org/@typescript-eslint/utils/-/utils-5.30.0.tgz", + "integrity": "sha512-0bIgOgZflLKIcZsWvfklsaQTM3ZUbmtH0rJ1hKyV3raoUYyeZwcjQ8ZUJTzS7KnhNcsVT1Rxs7zeeMHEhGlltw==", "dev": true, "requires": { "@types/json-schema": "^7.0.9", - "@typescript-eslint/scope-manager": "5.21.0", - "@typescript-eslint/types": "5.21.0", - "@typescript-eslint/typescript-estree": "5.21.0", + "@typescript-eslint/scope-manager": "5.30.0", + "@typescript-eslint/types": "5.30.0", + "@typescript-eslint/typescript-estree": "5.30.0", "eslint-scope": "^5.1.1", "eslint-utils": "^3.0.0" } }, "@typescript-eslint/visitor-keys": { - "version": "5.21.0", - "resolved": "https://registry.npmjs.org/@typescript-eslint/visitor-keys/-/visitor-keys-5.21.0.tgz", - "integrity": "sha512-SX8jNN+iHqAF0riZQMkm7e8+POXa/fXw5cxL+gjpyP+FI+JVNhii53EmQgDAfDcBpFekYSlO0fGytMQwRiMQCA==", + "version": "5.30.0", + "resolved": "https://registry.npmjs.org/@typescript-eslint/visitor-keys/-/visitor-keys-5.30.0.tgz", + "integrity": "sha512-6WcIeRk2DQ3pHKxU1Ni0qMXJkjO/zLjBymlYBy/53qxe7yjEFSvzKLDToJjURUhSl2Fzhkl4SMXQoETauF74cw==", "dev": true, "requires": { - "@typescript-eslint/types": "5.21.0", - "eslint-visitor-keys": "^3.0.0" + "@typescript-eslint/types": "5.30.0", + "eslint-visitor-keys": "^3.3.0" } }, "@ungap/promise-all-settled": { @@ -9183,12 +9170,20 @@ "dev": true }, "ansi-escapes": { - "version": "4.3.2", - "resolved": "https://registry.npmjs.org/ansi-escapes/-/ansi-escapes-4.3.2.tgz", - "integrity": "sha512-gKXj5ALrKWQLsYG9jlTRmR/xKluxHV+Z9QEwNIgCfM1/uwPMCuzVVnh5mwTd+OuBZcwSIMbqssNWRm1lE51QaQ==", + "version": "5.0.0", + "resolved": "https://registry.npmjs.org/ansi-escapes/-/ansi-escapes-5.0.0.tgz", + "integrity": "sha512-5GFMVX8HqE/TB+FuBJGuO5XG0WrsA6ptUqoODaT/n9mmUaZFkqnBueB4leqGBCmrUHnCnC4PCZTCd0E7QQ83bA==", "dev": true, "requires": { - "type-fest": "^0.21.3" + "type-fest": "^1.0.2" + }, + "dependencies": { + "type-fest": { + "version": "1.4.0", + "resolved": "https://registry.npmjs.org/type-fest/-/type-fest-1.4.0.tgz", + "integrity": "sha512-yGSza74xk0UG8k+pLh5oeoYirvIiWo5t0/o3zHHAO2tRDiZcxWP7fywNlXhqb6/r6sWvwi+RsyQMWhVLe4BVuA==", + "dev": true + } } }, "ansi-regex": { @@ -9340,42 +9335,81 @@ } }, "boxen": { - "version": "5.1.2", - "resolved": "https://registry.npmjs.org/boxen/-/boxen-5.1.2.tgz", - "integrity": "sha512-9gYgQKXx+1nP8mP7CzFyaUARhg7D3n1dF/FnErWmu9l6JvGpNUN278h0aSb+QjoiKSWG+iZ3uHrcqk0qrY9RQQ==", + "version": "7.0.0", + "resolved": "https://registry.npmjs.org/boxen/-/boxen-7.0.0.tgz", + "integrity": "sha512-j//dBVuyacJbvW+tvZ9HuH03fZ46QcaKvvhZickZqtB271DxJ7SNRSNxrV/dZX0085m7hISRZWbzWlJvx/rHSg==", "dev": true, "requires": { - "ansi-align": "^3.0.0", - "camelcase": "^6.2.0", - "chalk": "^4.1.0", - "cli-boxes": "^2.2.1", - "string-width": "^4.2.2", - "type-fest": "^0.20.2", - "widest-line": "^3.1.0", - "wrap-ansi": "^7.0.0" + "ansi-align": "^3.0.1", + "camelcase": "^7.0.0", + "chalk": "^5.0.1", + "cli-boxes": "^3.0.0", + "string-width": "^5.1.2", + "type-fest": "^2.13.0", + "widest-line": "^4.0.1", + "wrap-ansi": "^8.0.1" }, "dependencies": { + "ansi-regex": { + "version": "6.0.1", + "resolved": "https://registry.npmjs.org/ansi-regex/-/ansi-regex-6.0.1.tgz", + "integrity": "sha512-n5M855fKb2SsfMIiFFoVrABHJC8QtHwVx+mHWP3QcEqBHYienj5dHSgjbxtC0WEZXYt4wcD6zrQElDPhFuZgfA==", + "dev": true + }, + "ansi-styles": { + "version": "6.1.0", + "resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-6.1.0.tgz", + "integrity": "sha512-VbqNsoz55SYGczauuup0MFUyXNQviSpFTj1RQtFzmQLk18qbVSpTFFGMT293rmDaQuKCT6InmbuEyUne4mTuxQ==", + "dev": true + }, "camelcase": { - "version": "6.3.0", - "resolved": "https://registry.npmjs.org/camelcase/-/camelcase-6.3.0.tgz", - "integrity": "sha512-Gmy6FhYlCY7uOElZUSbxo2UCDH8owEk996gkbrpsgGtrJLM3J7jGxl9Ic7Qwwj4ivOE5AWZWRMecDdF7hqGjFA==", + "version": "7.0.0", + "resolved": "https://registry.npmjs.org/camelcase/-/camelcase-7.0.0.tgz", + "integrity": "sha512-JToIvOmz6nhGsUhAYScbo2d6Py5wojjNfoxoc2mEVLUdJ70gJK2gnd+ABY1Tc3sVMyK7QDPtN0T/XdlCQWITyQ==", "dev": true }, - "chalk": { - "version": "4.1.2", - "resolved": "https://registry.npmjs.org/chalk/-/chalk-4.1.2.tgz", - "integrity": "sha512-oKnbhFyRIXpUuez8iBMmyEa4nbj4IOQyuhc/wy9kY7/WVPcwIO9VA668Pu8RkO7+0G76SLROeyw9CpQ061i4mA==", + "emoji-regex": { + "version": "9.2.2", + "resolved": "https://registry.npmjs.org/emoji-regex/-/emoji-regex-9.2.2.tgz", + "integrity": "sha512-L18DaJsXSUk2+42pv8mLs5jJT2hqFkFE4j21wOmgbUqsZ2hL72NsUU785g9RXgo3s0ZNgVl42TiHp3ZtOv/Vyg==", + "dev": true + }, + "string-width": { + "version": "5.1.2", + "resolved": "https://registry.npmjs.org/string-width/-/string-width-5.1.2.tgz", + "integrity": "sha512-HnLOCR3vjcY8beoNLtcjZ5/nxn2afmME6lhrDrebokqMap+XbeW8n9TXpPDOqdGK5qcI3oT0GKTW6wC7EMiVqA==", "dev": true, "requires": { - "ansi-styles": "^4.1.0", - "supports-color": "^7.1.0" + "eastasianwidth": "^0.2.0", + "emoji-regex": "^9.2.2", + "strip-ansi": "^7.0.1" + } + }, + "strip-ansi": { + "version": "7.0.1", + "resolved": "https://registry.npmjs.org/strip-ansi/-/strip-ansi-7.0.1.tgz", + "integrity": "sha512-cXNxvT8dFNRVfhVME3JAe98mkXDYN2O1l7jmcwMnOslDeESg1rF/OZMtK0nRAhiari1unG5cD4jG3rapUAkLbw==", + "dev": true, + "requires": { + "ansi-regex": "^6.0.1" } }, "type-fest": { - "version": "0.20.2", - "resolved": "https://registry.npmjs.org/type-fest/-/type-fest-0.20.2.tgz", - "integrity": "sha512-Ne+eE4r0/iWnpAxD852z3A+N0Bt5RN//NjJwRd2VFHEmrywxf5vsZlh4R6lixl6B+wz/8d+maTSAkN1FIkI3LQ==", + "version": "2.14.0", + "resolved": "https://registry.npmjs.org/type-fest/-/type-fest-2.14.0.tgz", + "integrity": "sha512-hQnTQkFjL5ik6HF2fTAM8ycbr94UbQXK364wF930VHb0dfBJ5JBP8qwrR8TaK9zwUEk7meruo2JAUDMwvuxd/w==", "dev": true + }, + "wrap-ansi": { + "version": "8.0.1", + "resolved": "https://registry.npmjs.org/wrap-ansi/-/wrap-ansi-8.0.1.tgz", + "integrity": "sha512-QFF+ufAqhoYHvoHdajT/Po7KoXVBPXS2bgjIam5isfWJPfIOnQZ50JtUiVvCv/sjgacf3yRrt2ZKUZ/V4itN4g==", + "dev": true, + "requires": { + "ansi-styles": "^6.1.0", + "string-width": "^5.0.1", + "strip-ansi": "^7.0.1" + } } } }, @@ -9569,18 +9603,18 @@ "dev": true }, "cli-boxes": { - "version": "2.2.1", - "resolved": "https://registry.npmjs.org/cli-boxes/-/cli-boxes-2.2.1.tgz", - "integrity": "sha512-y4coMcylgSCdVinjiDBuR8PCC2bLjyGTwEmPb9NHR/QaNU6EUOXcTY/s6VjGMD6ENSEaeQYHCY0GNGS5jfMwPw==", + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/cli-boxes/-/cli-boxes-3.0.0.tgz", + "integrity": "sha512-/lzGpEWL/8PfI0BmBOPRwp0c/wFNX1RdUML3jK/RcSBA9T8mZDdQpqYBKtCFTOfQbwPqWEOpjqW+Fnayc0969g==", "dev": true }, "cli-cursor": { - "version": "3.1.0", - "resolved": "https://registry.npmjs.org/cli-cursor/-/cli-cursor-3.1.0.tgz", - "integrity": "sha512-I/zHAwsKf9FqGoXM4WWRACob9+SNukZTd94DWF57E4toouRulbCxcUh6RKUEOQlYTHJnzkPMySvPNaaSLNfLZw==", + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/cli-cursor/-/cli-cursor-4.0.0.tgz", + "integrity": "sha512-VGtlMu3x/4DOtIUwEkRezxUZ2lBacNJCHash0N0WeZDBS+7Ux1dm3XWAgWYxLJFMMdOeXMHXorshEFhbMSGelg==", "dev": true, "requires": { - "restore-cursor": "^3.1.0" + "restore-cursor": "^4.0.0" } }, "cli-spinners": { @@ -9590,9 +9624,9 @@ "dev": true }, "cli-width": { - "version": "3.0.0", - "resolved": "https://registry.npmjs.org/cli-width/-/cli-width-3.0.0.tgz", - "integrity": "sha512-FxqpkPPwu1HjuN93Omfm4h8uIanXofW0RxVEW3k5RKx+mJJYSthzNhp32Kzxxy3YAEZ/Dc/EWN1vZRY0+kOhbw==", + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/cli-width/-/cli-width-4.0.0.tgz", + "integrity": "sha512-ZksGS2xpa/bYkNzN3BAw1wEjsLV/ZKOf/CCrJ/QOBsxx6fOARIkwTutxp1XIOIohi6HKmOFjMoK/XaqDVUpEEw==", "dev": true }, "cliui": { @@ -9622,13 +9656,13 @@ "clone": { "version": "1.0.4", "resolved": "https://registry.npmjs.org/clone/-/clone-1.0.4.tgz", - "integrity": "sha1-2jCcwmPfFZlMaIypAheco8fNfH4=", + "integrity": "sha512-JQHZ2QMW6l3aH/j6xCqQThY/9OH4D/9ls34cgkUBiEeocRTU04tHfKPBsUK1PqZCUQM7GiA0IIXJSuXHI64Kbg==", "dev": true }, "clone-response": { "version": "1.0.2", "resolved": "https://registry.npmjs.org/clone-response/-/clone-response-1.0.2.tgz", - "integrity": "sha1-0dyXOSAxTfZ/vrlCI7TuNQI56Ws=", + "integrity": "sha512-yjLXh88P599UOyPTFX0POsd7WxnbsVsGohcwzHOLspIhhpalPw1BcqED8NblyZLKcGrL8dTgMlcaZxV2jAD41Q==", "dev": true, "requires": { "mimic-response": "^1.0.0" @@ -9676,9 +9710,9 @@ "dev": true }, "compress-brotli": { - "version": "1.3.6", - "resolved": "https://registry.npmjs.org/compress-brotli/-/compress-brotli-1.3.6.tgz", - "integrity": "sha512-au99/GqZtUtiCBliqLFbWlhnCxn+XSYjwZ77q6mKN4La4qOXDoLVPZ50iXr0WmAyMxl8yqoq3Yq4OeQNPPkyeQ==", + "version": "1.3.8", + "resolved": "https://registry.npmjs.org/compress-brotli/-/compress-brotli-1.3.8.tgz", + "integrity": "sha512-lVcQsjhxhIXsuupfy9fmZUFtAIdBmXA7EGY6GBdgZ++qkM9zG4YFT8iU7FoBxzryNDMOpD1HIFHUSX4D87oqhQ==", "dev": true, "requires": { "@types/json-buffer": "~3.0.0", @@ -9691,18 +9725,35 @@ "integrity": "sha1-2Klr13/Wjfd5OnMDajug1UBdR3s=", "dev": true }, + "config-chain": { + "version": "1.1.13", + "resolved": "https://registry.npmjs.org/config-chain/-/config-chain-1.1.13.tgz", + "integrity": "sha512-qj+f8APARXHrM0hraqXYb2/bOVSV4PvJQlNZ/DVj0QrmNM2q2euizkeuVckQ57J+W0mRH6Hvi+k50M4Jul2VRQ==", + "dev": true, + "requires": { + "ini": "^1.3.4", + "proto-list": "~1.2.1" + }, + "dependencies": { + "ini": { + "version": "1.3.8", + "resolved": "https://registry.npmjs.org/ini/-/ini-1.3.8.tgz", + "integrity": "sha512-JV/yugV2uzW5iMRSiZAyDtQd+nxtUnjeLt0acNdw98kKLrvuRVyB80tsREOE7yvGVgalhZ6RNXCmEHkUKBKxew==", + "dev": true + } + } + }, "configstore": { - "version": "5.0.1", - "resolved": "https://registry.npmjs.org/configstore/-/configstore-5.0.1.tgz", - "integrity": "sha512-aMKprgk5YhBNyH25hj8wGt2+D52Sw1DRRIzqBwLp2Ya9mFmY8KPvvtvmna8SxVR9JMZ4kzMD68N22vlaRpkeFA==", + "version": "6.0.0", + "resolved": "https://registry.npmjs.org/configstore/-/configstore-6.0.0.tgz", + "integrity": "sha512-cD31W1v3GqUlQvbBCGcXmd2Nj9SvLDOP1oQ0YFuLETufzSPaKp11rYBsSOm7rCsW3OnIRAFM3OxRhceaXNYHkA==", "dev": true, "requires": { - "dot-prop": "^5.2.0", - "graceful-fs": "^4.1.2", - "make-dir": "^3.0.0", - "unique-string": "^2.0.0", - "write-file-atomic": "^3.0.0", - "xdg-basedir": "^4.0.0" + "dot-prop": "^6.0.1", + "graceful-fs": "^4.2.6", + "unique-string": "^3.0.0", + "write-file-atomic": "^3.0.3", + "xdg-basedir": "^5.0.1" } }, "convert-source-map": { @@ -9751,10 +9802,21 @@ } }, "crypto-random-string": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/crypto-random-string/-/crypto-random-string-2.0.0.tgz", - "integrity": "sha512-v1plID3y9r/lPhviJ1wrXpLeyUIGAZ2SHNYTEapm7/8A9nLPoyvVp3RK/EPFqn5kEznyWgYZNsRtYYIWbuG8KA==", - "dev": true + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/crypto-random-string/-/crypto-random-string-4.0.0.tgz", + "integrity": "sha512-x8dy3RnvYdlUcPOjkEHqozhiwzKNSq7GcPuXFbnyMOCHxX8V3OgIg/pYuabl2sbUPfIJaeAQB7PMOK8DFIdoRA==", + "dev": true, + "requires": { + "type-fest": "^1.0.1" + }, + "dependencies": { + "type-fest": { + "version": "1.4.0", + "resolved": "https://registry.npmjs.org/type-fest/-/type-fest-1.4.0.tgz", + "integrity": "sha512-yGSza74xk0UG8k+pLh5oeoYirvIiWo5t0/o3zHHAO2tRDiZcxWP7fywNlXhqb6/r6sWvwi+RsyQMWhVLe4BVuA==", + "dev": true + } + } }, "data-uri-to-buffer": { "version": "3.0.1", @@ -9824,7 +9886,7 @@ "defaults": { "version": "1.0.3", "resolved": "https://registry.npmjs.org/defaults/-/defaults-1.0.3.tgz", - "integrity": "sha1-xlYFHpgX2f8I7YgUd/P+QBnz730=", + "integrity": "sha512-s82itHOnYrN0Ib8r+z7laQz3sdE+4FP3d9Q7VLO7U+KRT+CR0GsWuyHxzdAY82I7cXv0G/twrqomTJLOssO5HA==", "dev": true, "requires": { "clone": "^1.0.2" @@ -9907,18 +9969,18 @@ } }, "dot-prop": { - "version": "5.3.0", - "resolved": "https://registry.npmjs.org/dot-prop/-/dot-prop-5.3.0.tgz", - "integrity": "sha512-QM8q3zDe58hqUqjraQOmzZ1LIH9SWQJTlEKCH4kJ2oQvLZk7RbQXvtDM2XEq3fwkV9CCvvH4LA0AV+ogFsBM2Q==", + "version": "6.0.1", + "resolved": "https://registry.npmjs.org/dot-prop/-/dot-prop-6.0.1.tgz", + "integrity": "sha512-tE7ztYzXHIeyvc7N+hR3oi7FIbf/NIjVP9hmAt3yMXzrQ072/fpjGLx2GxNxGxUl5V73MEqYzioOMoVhGMJ5cA==", "dev": true, "requires": { "is-obj": "^2.0.0" } }, - "duplexer3": { - "version": "0.1.4", - "resolved": "https://registry.npmjs.org/duplexer3/-/duplexer3-0.1.4.tgz", - "integrity": "sha1-7gHdHKwO08vH/b6jfcCo8c4ALOI=", + "eastasianwidth": { + "version": "0.2.0", + "resolved": "https://registry.npmjs.org/eastasianwidth/-/eastasianwidth-0.2.0.tgz", + "integrity": "sha512-I88TYZWc9XiYHRQ4/3c5rjjfgkjhLyW2luGIheGERbNQ6OY7yTybanSpDXZa8y7VUP9YmDcYa+eyq4ca7iLqWA==", "dev": true }, "electron-to-chromium": { @@ -10031,9 +10093,9 @@ "dev": true }, "escape-goat": { - "version": "2.1.1", - "resolved": "https://registry.npmjs.org/escape-goat/-/escape-goat-2.1.1.tgz", - "integrity": "sha512-8/uIhbG12Csjy2JEW7D9pHbreaVaS/OpN3ycnyvElTdwM5n6GY6W6e2IPemfvGZeUMqZ9A/3GqIZMgKnBhAw/Q==", + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/escape-goat/-/escape-goat-4.0.0.tgz", + "integrity": "sha512-2Sd4ShcWxbx6OY1IHyla/CVNwvg7XwZVoXZHcSu9w9SReNP1EzzD5T8NWKIR38fIqEns9kDWKUQTXXAmlDrdPg==", "dev": true }, "escape-string-regexp": { @@ -10097,12 +10159,12 @@ } }, "eslint": { - "version": "8.14.0", - "resolved": "https://registry.npmjs.org/eslint/-/eslint-8.14.0.tgz", - "integrity": "sha512-3/CE4aJX7LNEiE3i6FeodHmI/38GZtWCsAtsymScmzYapx8q1nVVb+eLcLSzATmCPXw5pT4TqVs1E0OmxAd9tw==", + "version": "8.18.0", + "resolved": "https://registry.npmjs.org/eslint/-/eslint-8.18.0.tgz", + "integrity": "sha512-As1EfFMVk7Xc6/CvhssHUjsAQSkpfXvUGMFC3ce8JDe6WvqCgRrLOBQbVpsBFr1X1V+RACOadnzVvcUS5ni2bA==", "dev": true, "requires": { - "@eslint/eslintrc": "^1.2.2", + "@eslint/eslintrc": "^1.3.0", "@humanwhocodes/config-array": "^0.9.2", "ajv": "^6.10.0", "chalk": "^4.0.0", @@ -10113,14 +10175,14 @@ "eslint-scope": "^7.1.1", "eslint-utils": "^3.0.0", "eslint-visitor-keys": "^3.3.0", - "espree": "^9.3.1", + "espree": "^9.3.2", "esquery": "^1.4.0", "esutils": "^2.0.2", "fast-deep-equal": "^3.1.3", "file-entry-cache": "^6.0.1", "functional-red-black-tree": "^1.0.1", "glob-parent": "^6.0.1", - "globals": "^13.6.0", + "globals": "^13.15.0", "ignore": "^5.2.0", "import-fresh": "^3.0.0", "imurmurhash": "^0.1.4", @@ -10129,7 +10191,7 @@ "json-stable-stringify-without-jsonify": "^1.0.1", "levn": "^0.4.1", "lodash.merge": "^4.6.2", - "minimatch": "^3.0.4", + "minimatch": "^3.1.2", "natural-compare": "^1.4.0", "optionator": "^0.9.1", "regexpp": "^3.2.0", @@ -10207,13 +10269,13 @@ "dev": true }, "espree": { - "version": "9.3.1", - "resolved": "https://registry.npmjs.org/espree/-/espree-9.3.1.tgz", - "integrity": "sha512-bvdyLmJMfwkV3NCRl5ZhJf22zBFo1y8bYh3VYb+bfzqNB4Je68P2sSuXyuFquzWLebHpNd2/d5uv7yoP9ISnGQ==", + "version": "9.3.2", + "resolved": "https://registry.npmjs.org/espree/-/espree-9.3.2.tgz", + "integrity": "sha512-D211tC7ZwouTIuY5x9XnS0E9sWNChB7IYKX/Xp5eQj3nFXhqmiUDB9q27y76oFl8jTg3pXcQx/bpxMfs3CIZbA==", "dev": true, "requires": { - "acorn": "^8.7.0", - "acorn-jsx": "^5.3.1", + "acorn": "^8.7.1", + "acorn-jsx": "^5.3.2", "eslint-visitor-keys": "^3.3.0" } }, @@ -10349,12 +10411,21 @@ } }, "figures": { - "version": "3.2.0", - "resolved": "https://registry.npmjs.org/figures/-/figures-3.2.0.tgz", - "integrity": "sha512-yaduQFRKLXYOGgEn6AZau90j3ggSOyiqXU0F9JZfeXYhNa+Jk4X+s45A2zg5jns87GAFa34BBm2kXw4XpNcbdg==", + "version": "4.0.1", + "resolved": "https://registry.npmjs.org/figures/-/figures-4.0.1.tgz", + "integrity": "sha512-rElJwkA/xS04Vfg+CaZodpso7VqBknOYbzi6I76hI4X80RUjkSxO2oAyPmGbuXUppywjqndOrQDl817hDnI++w==", "dev": true, "requires": { - "escape-string-regexp": "^1.0.5" + "escape-string-regexp": "^5.0.0", + "is-unicode-supported": "^1.2.0" + }, + "dependencies": { + "escape-string-regexp": { + "version": "5.0.0", + "resolved": "https://registry.npmjs.org/escape-string-regexp/-/escape-string-regexp-5.0.0.tgz", + "integrity": "sha512-/veY75JbMK4j1yjvuUxuVsiS/hr/4iHs9FTT6cgTexxdE0Ly/glccBAkloH/DofkjRbZU3bnoj38mOmhkZ0lHw==", + "dev": true + } } }, "file-entry-cache": { @@ -10676,20 +10747,12 @@ } }, "globals": { - "version": "13.13.0", - "resolved": "https://registry.npmjs.org/globals/-/globals-13.13.0.tgz", - "integrity": "sha512-EQ7Q18AJlPwp3vUDL4mKA0KXrXyNIQyWon6T6XQiBQF0XHvRsiCSrWmmeATpUzdJN2HhWZU6Pdl0a9zdep5p6A==", + "version": "13.15.0", + "resolved": "https://registry.npmjs.org/globals/-/globals-13.15.0.tgz", + "integrity": "sha512-bpzcOlgDhMG070Av0Vy5Owklpv1I6+j96GhUI7Rh7IzDCKLzboflLrrfqMu8NquDbiR4EOQk7XzJwqVJxicxog==", "dev": true, "requires": { "type-fest": "^0.20.2" - }, - "dependencies": { - "type-fest": { - "version": "0.20.2", - "resolved": "https://registry.npmjs.org/type-fest/-/type-fest-0.20.2.tgz", - "integrity": "sha512-Ne+eE4r0/iWnpAxD852z3A+N0Bt5RN//NjJwRd2VFHEmrywxf5vsZlh4R6lixl6B+wz/8d+maTSAkN1FIkI3LQ==", - "dev": true - } } }, "globby": { @@ -10706,9 +10769,9 @@ } }, "got": { - "version": "12.0.4", - "resolved": "https://registry.npmjs.org/got/-/got-12.0.4.tgz", - "integrity": "sha512-2Eyz4iU/ktq7wtMFXxzK7g5p35uNYLLdiZarZ5/Yn3IJlNEpBd5+dCgcAyxN8/8guZLszffwe3wVyw+DEVrpBg==", + "version": "12.1.0", + "resolved": "https://registry.npmjs.org/got/-/got-12.1.0.tgz", + "integrity": "sha512-hBv2ty9QN2RdbJJMK3hesmSkFTjVIHyIDDbssCKnSmq62edGgImJWD10Eb1k77TiV1bxloxqcFAVK8+9pkhOig==", "dev": true, "requires": { "@sindresorhus/is": "^4.6.0", @@ -10778,9 +10841,9 @@ } }, "has-yarn": { - "version": "2.1.0", - "resolved": "https://registry.npmjs.org/has-yarn/-/has-yarn-2.1.0.tgz", - "integrity": "sha512-UqBRqi4ju7T+TqGNdqAO0PaSVGsDGJUBQvk9eUWNGRY1CFGDzYhLWoM7JQEemnlvVcv/YEmc2wNW8BC24EnUsw==", + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/has-yarn/-/has-yarn-3.0.0.tgz", + "integrity": "sha512-IrsVwUHhEULx3R8f/aA8AHuEzAorplsab/v8HBzEiIukwq5i/EC+xmOW+HfP1OaDP+2JkgT1yILHN2O3UFIbcA==", "dev": true }, "hasha": { @@ -10907,9 +10970,9 @@ } }, "import-lazy": { - "version": "2.1.0", - "resolved": "https://registry.npmjs.org/import-lazy/-/import-lazy-2.1.0.tgz", - "integrity": "sha1-BWmOPUXIjo1+nZLLBYTnfwlvPkM=", + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/import-lazy/-/import-lazy-4.0.0.tgz", + "integrity": "sha512-rKtvo6a868b5Hu3heneU+L4yEQ4jYKLtjpnPeUdK7h0yzXGmyBTypknlkCvHFBqfX9YlorEiMM6Dnq/5atfHkw==", "dev": true }, "imurmurhash": { @@ -10947,96 +11010,75 @@ "dev": true }, "inquirer": { - "version": "8.2.4", - "resolved": "https://registry.npmjs.org/inquirer/-/inquirer-8.2.4.tgz", - "integrity": "sha512-nn4F01dxU8VeKfq192IjLsxu0/OmMZ4Lg3xKAns148rCaXP6ntAoEkVYZThWjwON8AlzdZZi6oqnhNbxUG9hVg==", + "version": "9.0.0", + "resolved": "https://registry.npmjs.org/inquirer/-/inquirer-9.0.0.tgz", + "integrity": "sha512-eYTDdTYr/YPwRenOzLZTvaJUDXDW8GQgxvzBppuXLj/kauTRLfV8bCPVbGh2staP7edrqL+rGwjaOa+JVxBWsg==", "dev": true, "requires": { - "ansi-escapes": "^4.2.1", - "chalk": "^4.1.1", - "cli-cursor": "^3.1.0", - "cli-width": "^3.0.0", + "ansi-escapes": "^5.0.0", + "chalk": "^5.0.1", + "cli-cursor": "^4.0.0", + "cli-width": "^4.0.0", "external-editor": "^3.0.3", - "figures": "^3.0.0", + "figures": "^4.0.1", "lodash": "^4.17.21", "mute-stream": "0.0.8", - "ora": "^5.4.1", + "ora": "^6.1.0", "run-async": "^2.4.0", "rxjs": "^7.5.5", - "string-width": "^4.1.0", - "strip-ansi": "^6.0.0", + "string-width": "^5.1.2", + "strip-ansi": "^7.0.1", "through": "^2.3.6", - "wrap-ansi": "^7.0.0" + "wrap-ansi": "^8.0.1" }, "dependencies": { - "bl": { - "version": "4.1.0", - "resolved": "https://registry.npmjs.org/bl/-/bl-4.1.0.tgz", - "integrity": "sha512-1W07cM9gS6DcLperZfFSj+bWLtaPGSOHWhPiGzXmvVJbRLdG82sH/Kn8EtW1VqWVA54AKf2h5k5BbnIbwF3h6w==", - "dev": true, - "requires": { - "buffer": "^5.5.0", - "inherits": "^2.0.4", - "readable-stream": "^3.4.0" - } + "ansi-regex": { + "version": "6.0.1", + "resolved": "https://registry.npmjs.org/ansi-regex/-/ansi-regex-6.0.1.tgz", + "integrity": "sha512-n5M855fKb2SsfMIiFFoVrABHJC8QtHwVx+mHWP3QcEqBHYienj5dHSgjbxtC0WEZXYt4wcD6zrQElDPhFuZgfA==", + "dev": true }, - "buffer": { - "version": "5.7.1", - "resolved": "https://registry.npmjs.org/buffer/-/buffer-5.7.1.tgz", - "integrity": "sha512-EHcyIPBQ4BSGlvjB16k5KgAJ27CIsHY/2JBmCRReo48y9rQ3MaUzWX3KVlBa4U7MyX02HdVj0K7C3WaB3ju7FQ==", - "dev": true, - "requires": { - "base64-js": "^1.3.1", - "ieee754": "^1.1.13" - } + "ansi-styles": { + "version": "6.1.0", + "resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-6.1.0.tgz", + "integrity": "sha512-VbqNsoz55SYGczauuup0MFUyXNQviSpFTj1RQtFzmQLk18qbVSpTFFGMT293rmDaQuKCT6InmbuEyUne4mTuxQ==", + "dev": true }, - "chalk": { - "version": "4.1.2", - "resolved": "https://registry.npmjs.org/chalk/-/chalk-4.1.2.tgz", - "integrity": "sha512-oKnbhFyRIXpUuez8iBMmyEa4nbj4IOQyuhc/wy9kY7/WVPcwIO9VA668Pu8RkO7+0G76SLROeyw9CpQ061i4mA==", + "emoji-regex": { + "version": "9.2.2", + "resolved": "https://registry.npmjs.org/emoji-regex/-/emoji-regex-9.2.2.tgz", + "integrity": "sha512-L18DaJsXSUk2+42pv8mLs5jJT2hqFkFE4j21wOmgbUqsZ2hL72NsUU785g9RXgo3s0ZNgVl42TiHp3ZtOv/Vyg==", + "dev": true + }, + "string-width": { + "version": "5.1.2", + "resolved": "https://registry.npmjs.org/string-width/-/string-width-5.1.2.tgz", + "integrity": "sha512-HnLOCR3vjcY8beoNLtcjZ5/nxn2afmME6lhrDrebokqMap+XbeW8n9TXpPDOqdGK5qcI3oT0GKTW6wC7EMiVqA==", "dev": true, "requires": { - "ansi-styles": "^4.1.0", - "supports-color": "^7.1.0" + "eastasianwidth": "^0.2.0", + "emoji-regex": "^9.2.2", + "strip-ansi": "^7.0.1" } }, - "is-interactive": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/is-interactive/-/is-interactive-1.0.0.tgz", - "integrity": "sha512-2HvIEKRoqS62guEC+qBjpvRubdX910WCMuJTZ+I9yvqKU2/12eSL549HMwtabb4oupdj2sMP50k+XJfB/8JE6w==", - "dev": true - }, - "is-unicode-supported": { - "version": "0.1.0", - "resolved": "https://registry.npmjs.org/is-unicode-supported/-/is-unicode-supported-0.1.0.tgz", - "integrity": "sha512-knxG2q4UC3u8stRGyAVJCOdxFmv5DZiRcdlIaAQXAbSfJya+OhopNotLQrstBhququ4ZpuKbDc/8S6mgXgPFPw==", - "dev": true - }, - "log-symbols": { - "version": "4.1.0", - "resolved": "https://registry.npmjs.org/log-symbols/-/log-symbols-4.1.0.tgz", - "integrity": "sha512-8XPvpAA8uyhfteu8pIvQxpJZ7SYYdpUivZpGy6sFsBuKRY/7rQGavedeB8aK+Zkyq6upMFVL/9AW6vOYzfRyLg==", + "strip-ansi": { + "version": "7.0.1", + "resolved": "https://registry.npmjs.org/strip-ansi/-/strip-ansi-7.0.1.tgz", + "integrity": "sha512-cXNxvT8dFNRVfhVME3JAe98mkXDYN2O1l7jmcwMnOslDeESg1rF/OZMtK0nRAhiari1unG5cD4jG3rapUAkLbw==", "dev": true, "requires": { - "chalk": "^4.1.0", - "is-unicode-supported": "^0.1.0" + "ansi-regex": "^6.0.1" } }, - "ora": { - "version": "5.4.1", - "resolved": "https://registry.npmjs.org/ora/-/ora-5.4.1.tgz", - "integrity": "sha512-5b6Y85tPxZZ7QytO+BQzysW31HJku27cRIlkbAXaNx+BdcVi+LlRFmVXzeF6a7JCwJpyw5c4b+YSVImQIrBpuQ==", + "wrap-ansi": { + "version": "8.0.1", + "resolved": "https://registry.npmjs.org/wrap-ansi/-/wrap-ansi-8.0.1.tgz", + "integrity": "sha512-QFF+ufAqhoYHvoHdajT/Po7KoXVBPXS2bgjIam5isfWJPfIOnQZ50JtUiVvCv/sjgacf3yRrt2ZKUZ/V4itN4g==", "dev": true, "requires": { - "bl": "^4.1.0", - "chalk": "^4.1.0", - "cli-cursor": "^3.1.0", - "cli-spinners": "^2.5.0", - "is-interactive": "^1.0.0", - "is-unicode-supported": "^0.1.0", - "log-symbols": "^4.1.0", - "strip-ansi": "^6.0.0", - "wcwidth": "^1.0.1" + "ansi-styles": "^6.1.0", + "string-width": "^5.0.1", + "strip-ansi": "^7.0.1" } } } @@ -11197,9 +11239,9 @@ "dev": true }, "is-npm": { - "version": "5.0.0", - "resolved": "https://registry.npmjs.org/is-npm/-/is-npm-5.0.0.tgz", - "integrity": "sha512-WW/rQLOazUq+ST/bCAVBp/2oMERWLsR7OrKyt052dNDk4DHcDE0/7QSXITlmi+VBcV13DfIbysG3tZJm5RfdBA==", + "version": "6.0.0", + "resolved": "https://registry.npmjs.org/is-npm/-/is-npm-6.0.0.tgz", + "integrity": "sha512-JEjxbSmtPSt1c8XTkVrlujcXdKV1/tvuQ7GwKcAlyiVLeYFQ2VHat8xfrDJsIkhCdF/tZ7CiIR3sy141c6+gPQ==", "dev": true }, "is-number": { @@ -11336,9 +11378,9 @@ } }, "is-yarn-global": { - "version": "0.3.0", - "resolved": "https://registry.npmjs.org/is-yarn-global/-/is-yarn-global-0.3.0.tgz", - "integrity": "sha512-VjSeb/lHmkoyd8ryPVIKvOCn4D1koMqY+vqyjjUfc3xyKtP4dYOxM44sZrnqQSzSds3xyOrUTLTC9LVCVgLngw==", + "version": "0.4.0", + "resolved": "https://registry.npmjs.org/is-yarn-global/-/is-yarn-global-0.4.0.tgz", + "integrity": "sha512-HneQBCrXGBy15QnaDfcn6OLoU8AQPAa0Qn0IeJR/QCo4E8dNZaGGwxpCwWyEBQC5QvFonP8d6t60iGpAHVAfNA==", "dev": true }, "isarray": { @@ -11516,22 +11558,22 @@ "dev": true }, "keyv": { - "version": "4.2.2", - "resolved": "https://registry.npmjs.org/keyv/-/keyv-4.2.2.tgz", - "integrity": "sha512-uYS0vKTlBIjNCAUqrjlxmruxOEiZxZIHXyp32sdcGmP+ukFrmWUnE//RcPXJH3Vxrni1H2gsQbjHE0bH7MtMQQ==", + "version": "4.3.2", + "resolved": "https://registry.npmjs.org/keyv/-/keyv-4.3.2.tgz", + "integrity": "sha512-kn8WmodVBe12lmHpA6W8OY7SNh6wVR+Z+wZESF4iF5FCazaVXGWOtnbnvX0tMQ1bO+/TmOD9LziuYMvrIIs0xw==", "dev": true, "requires": { - "compress-brotli": "^1.3.6", + "compress-brotli": "^1.3.8", "json-buffer": "3.0.1" } }, "latest-version": { - "version": "5.1.0", - "resolved": "https://registry.npmjs.org/latest-version/-/latest-version-5.1.0.tgz", - "integrity": "sha512-weT+r0kTkRQdCdYCNtkMwWXQTMEswKrFBkm4ckQOMVhhqhIMI1UT2hMj+1iigIhgSZm5gTmrRXBNoGUgaTY1xA==", + "version": "7.0.0", + "resolved": "https://registry.npmjs.org/latest-version/-/latest-version-7.0.0.tgz", + "integrity": "sha512-KvNT4XqAMzdcL6ka6Tl3i2lYeFDgXNCuIX+xNx6ZMVR1dFq+idXd9FLKNMOIx0t9mJ9/HudyX4oZWXZQ0UJHeg==", "dev": true, "requires": { - "package-json": "^6.3.0" + "package-json": "^8.1.0" } }, "levn": { @@ -11636,9 +11678,9 @@ "dev": true }, "marked": { - "version": "4.0.15", - "resolved": "https://registry.npmjs.org/marked/-/marked-4.0.15.tgz", - "integrity": "sha512-esX5lPdTfG4p8LDkv+obbRCyOKzB+820ZZyMOXJZygZBHrH9b3xXR64X4kT3sPe9Nx8qQXbmcz6kFSMt4Nfk6Q==", + "version": "4.0.17", + "resolved": "https://registry.npmjs.org/marked/-/marked-4.0.17.tgz", + "integrity": "sha512-Wfk0ATOK5iPxM4ptrORkFemqroz0ZDxp5MWfYA7H/F+wO17NRWV5Ypxi6p3g2Xmw2bKeiYOl6oVnLHKxBA0VhA==", "dev": true }, "merge-stream": { @@ -12118,9 +12160,9 @@ } }, "ora": { - "version": "6.1.0", - "resolved": "https://registry.npmjs.org/ora/-/ora-6.1.0.tgz", - "integrity": "sha512-CxEP6845hLK+NHFWZ+LplGO4zfw4QSfxTlqMfvlJ988GoiUeZDMzCvqsZkFHv69sPICmJH1MDxZoQFOKXerAVw==", + "version": "6.1.2", + "resolved": "https://registry.npmjs.org/ora/-/ora-6.1.2.tgz", + "integrity": "sha512-EJQ3NiP5Xo94wJXIzAyOtSb0QEIAUu7m8t6UZ9krbz0vAJqr92JpcK/lEXg91q6B9pEGqrykkd2EQplnifDSBw==", "dev": true, "requires": { "bl": "^5.0.0", @@ -12140,40 +12182,6 @@ "integrity": "sha512-n5M855fKb2SsfMIiFFoVrABHJC8QtHwVx+mHWP3QcEqBHYienj5dHSgjbxtC0WEZXYt4wcD6zrQElDPhFuZgfA==", "dev": true }, - "cli-cursor": { - "version": "4.0.0", - "resolved": "https://registry.npmjs.org/cli-cursor/-/cli-cursor-4.0.0.tgz", - "integrity": "sha512-VGtlMu3x/4DOtIUwEkRezxUZ2lBacNJCHash0N0WeZDBS+7Ux1dm3XWAgWYxLJFMMdOeXMHXorshEFhbMSGelg==", - "dev": true, - "requires": { - "restore-cursor": "^4.0.0" - } - }, - "mimic-fn": { - "version": "2.1.0", - "resolved": "https://registry.npmjs.org/mimic-fn/-/mimic-fn-2.1.0.tgz", - "integrity": "sha512-OqbOk5oEQeAZ8WXWydlu9HJjz9WVdEIvamMCcXmuqUYjTknH/sqsWvhQ3vgwKFRR1HpjvNBKQ37nbJgYzGqGcg==", - "dev": true - }, - "onetime": { - "version": "5.1.2", - "resolved": "https://registry.npmjs.org/onetime/-/onetime-5.1.2.tgz", - "integrity": "sha512-kbpaSSGJTWdAY5KPVeMOKXSrPtr8C8C7wodJbcsd51jRnmD+GZu8Y0VoU6Dm5Z4vWr0Ig/1NKuWRKf7j5aaYSg==", - "dev": true, - "requires": { - "mimic-fn": "^2.1.0" - } - }, - "restore-cursor": { - "version": "4.0.0", - "resolved": "https://registry.npmjs.org/restore-cursor/-/restore-cursor-4.0.0.tgz", - "integrity": "sha512-I9fPXU9geO9bHOt9pHHOhOkYerIMsmVaWB0rA2AI9ERh/+x/i7MV5HKBNrg+ljO5eoPVgCcnFuRjJ9uH6I/3eg==", - "dev": true, - "requires": { - "onetime": "^5.1.0", - "signal-exit": "^3.0.2" - } - }, "strip-ansi": { "version": "7.0.1", "resolved": "https://registry.npmjs.org/strip-ansi/-/strip-ansi-7.0.1.tgz", @@ -12198,7 +12206,7 @@ "os-tmpdir": { "version": "1.0.2", "resolved": "https://registry.npmjs.org/os-tmpdir/-/os-tmpdir-1.0.2.tgz", - "integrity": "sha1-u+Z0BseaqFxc/sdm/lc0VV36EnQ=", + "integrity": "sha512-D2FR03Vir7FIu45XBY20mTb+/ZSWB00sjU9jdQXt83gDrI4Ztz5Fs7/yy74g2N5SVQY4xY1qDr4rNddwYRVX0g==", "dev": true }, "p-cancelable": { @@ -12281,148 +12289,40 @@ } }, "package-json": { - "version": "6.5.0", - "resolved": "https://registry.npmjs.org/package-json/-/package-json-6.5.0.tgz", - "integrity": "sha512-k3bdm2n25tkyxcjSKzB5x8kfVxlMdgsbPr0GkZcwHsLpba6cBjqCt1KlcChKEvxHIcTB1FVMuwoijZ26xex5MQ==", + "version": "8.1.0", + "resolved": "https://registry.npmjs.org/package-json/-/package-json-8.1.0.tgz", + "integrity": "sha512-hySwcV8RAWeAfPsXb9/HGSPn8lwDnv6fabH+obUZKX169QknRkRhPxd1yMubpKDskLFATkl3jHpNtVtDPFA0Wg==", "dev": true, "requires": { - "got": "^9.6.0", - "registry-auth-token": "^4.0.0", - "registry-url": "^5.0.0", - "semver": "^6.2.0" + "got": "^12.1.0", + "registry-auth-token": "^5.0.1", + "registry-url": "^6.0.0", + "semver": "^7.3.7" }, "dependencies": { - "@sindresorhus/is": { - "version": "0.14.0", - "resolved": "https://registry.npmjs.org/@sindresorhus/is/-/is-0.14.0.tgz", - "integrity": "sha512-9NET910DNaIPngYnLLPeg+Ogzqsi9uM4mSboU5y6p8S5DzMTVEsJZrawi+BoDNUVBa2DhJqQYUFvMDfgU062LQ==", - "dev": true - }, - "@szmarczak/http-timer": { - "version": "1.1.2", - "resolved": "https://registry.npmjs.org/@szmarczak/http-timer/-/http-timer-1.1.2.tgz", - "integrity": "sha512-XIB2XbzHTN6ieIjfIMV9hlVcfPU26s2vafYWQcZHWXHOxiaRZYEDKEwdl129Zyg50+foYV2jCgtrqSA6qNuNSA==", - "dev": true, - "requires": { - "defer-to-connect": "^1.0.1" - } - }, - "cacheable-request": { - "version": "6.1.0", - "resolved": "https://registry.npmjs.org/cacheable-request/-/cacheable-request-6.1.0.tgz", - "integrity": "sha512-Oj3cAGPCqOZX7Rz64Uny2GYAZNliQSqfbePrgAQ1wKAihYmCUnraBtJtKcGR4xz7wF+LoJC+ssFZvv5BgF9Igg==", - "dev": true, - "requires": { - "clone-response": "^1.0.2", - "get-stream": "^5.1.0", - "http-cache-semantics": "^4.0.0", - "keyv": "^3.0.0", - "lowercase-keys": "^2.0.0", - "normalize-url": "^4.1.0", - "responselike": "^1.0.2" - }, - "dependencies": { - "get-stream": { - "version": "5.2.0", - "resolved": "https://registry.npmjs.org/get-stream/-/get-stream-5.2.0.tgz", - "integrity": "sha512-nBF+F1rAZVCu/p7rjzgA+Yb4lfYXrpl7a6VmJrU8wF9I1CKvP/QwPNZHnOlwbTkY6dvtFIzFMSyQXbLoTQPRpA==", - "dev": true, - "requires": { - "pump": "^3.0.0" - } - }, - "lowercase-keys": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/lowercase-keys/-/lowercase-keys-2.0.0.tgz", - "integrity": "sha512-tqNXrS78oMOE73NMxK4EMLQsQowWf8jKooH9g7xPavRT706R6bkQJ6DY2Te7QukaZsulxa30wQ7bk0pm4XiHmA==", - "dev": true - } - } - }, - "decompress-response": { - "version": "3.3.0", - "resolved": "https://registry.npmjs.org/decompress-response/-/decompress-response-3.3.0.tgz", - "integrity": "sha1-gKTdMjdIOEv6JICDYirt7Jgq3/M=", - "dev": true, - "requires": { - "mimic-response": "^1.0.0" - } - }, - "defer-to-connect": { - "version": "1.1.3", - "resolved": "https://registry.npmjs.org/defer-to-connect/-/defer-to-connect-1.1.3.tgz", - "integrity": "sha512-0ISdNousHvZT2EiFlZeZAHBUvSxmKswVCEf8hW7KWgG4a8MVEu/3Vb6uWYozkjylyCxe0JBIiRB1jV45S70WVQ==", - "dev": true - }, - "get-stream": { - "version": "4.1.0", - "resolved": "https://registry.npmjs.org/get-stream/-/get-stream-4.1.0.tgz", - "integrity": "sha512-GMat4EJ5161kIy2HevLlr4luNjBgvmj413KaQA7jt4V8B4RDsfpHk7WQ9GVqfYyyx8OS/L66Kox+rJRNklLK7w==", - "dev": true, - "requires": { - "pump": "^3.0.0" - } - }, - "got": { - "version": "9.6.0", - "resolved": "https://registry.npmjs.org/got/-/got-9.6.0.tgz", - "integrity": "sha512-R7eWptXuGYxwijs0eV+v3o6+XH1IqVK8dJOEecQfTmkncw9AV4dcw/Dhxi8MdlqPthxxpZyizMzyg8RTmEsG+Q==", + "lru-cache": { + "version": "6.0.0", + "resolved": "https://registry.npmjs.org/lru-cache/-/lru-cache-6.0.0.tgz", + "integrity": "sha512-Jo6dJ04CmSjuznwJSS3pUeWmd/H0ffTlkXXgwZi+eq1UCmqQwCh+eLsYOYCwY991i2Fah4h1BEMCx4qThGbsiA==", "dev": true, "requires": { - "@sindresorhus/is": "^0.14.0", - "@szmarczak/http-timer": "^1.1.2", - "cacheable-request": "^6.0.0", - "decompress-response": "^3.3.0", - "duplexer3": "^0.1.4", - "get-stream": "^4.1.0", - "lowercase-keys": "^1.0.1", - "mimic-response": "^1.0.1", - "p-cancelable": "^1.0.0", - "to-readable-stream": "^1.0.0", - "url-parse-lax": "^3.0.0" + "yallist": "^4.0.0" } }, - "json-buffer": { - "version": "3.0.0", - "resolved": "https://registry.npmjs.org/json-buffer/-/json-buffer-3.0.0.tgz", - "integrity": "sha1-Wx85evx11ne96Lz8Dkfh+aPZqJg=", - "dev": true - }, - "keyv": { - "version": "3.1.0", - "resolved": "https://registry.npmjs.org/keyv/-/keyv-3.1.0.tgz", - "integrity": "sha512-9ykJ/46SN/9KPM/sichzQ7OvXyGDYKGTaDlKMGCAlg2UK8KRy4jb0d8sFc+0Tt0YYnThq8X2RZgCg74RPxgcVA==", + "semver": { + "version": "7.3.7", + "resolved": "https://registry.npmjs.org/semver/-/semver-7.3.7.tgz", + "integrity": "sha512-QlYTucUYOews+WeEujDoEGziz4K6c47V/Bd+LjSSYcA94p+DmINdf7ncaUinThfvZyu13lN9OY1XDxt8C0Tw0g==", "dev": true, "requires": { - "json-buffer": "3.0.0" + "lru-cache": "^6.0.0" } }, - "lowercase-keys": { - "version": "1.0.1", - "resolved": "https://registry.npmjs.org/lowercase-keys/-/lowercase-keys-1.0.1.tgz", - "integrity": "sha512-G2Lj61tXDnVFFOi8VZds+SoQjtQC3dgokKdDG2mTm1tx4m50NUHBOZSBwQQHyy0V12A0JTG4icfZQH+xPyh8VA==", - "dev": true - }, - "normalize-url": { - "version": "4.5.1", - "resolved": "https://registry.npmjs.org/normalize-url/-/normalize-url-4.5.1.tgz", - "integrity": "sha512-9UZCFRHQdNrfTpGg8+1INIg93B6zE0aXMVFkw1WFwvO4SlZywU6aLg5Of0Ap/PgcbSw4LNxvMWXMeugwMCX0AA==", - "dev": true - }, - "p-cancelable": { - "version": "1.1.0", - "resolved": "https://registry.npmjs.org/p-cancelable/-/p-cancelable-1.1.0.tgz", - "integrity": "sha512-s73XxOZ4zpt1edZYZzvhqFa6uvQc1vwUa0K0BdtIZgQMAJj9IbebH+JkgKZc9h+B05PKHLOTl4ajG1BmNrVZlw==", + "yallist": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/yallist/-/yallist-4.0.0.tgz", + "integrity": "sha512-3wdGidZyq5PB084XLES5TpOSRA3wjXAlIWMhum2kRcv/41Sn2emQ0dycQW4uZXLejwKvg6EsvbdlVL+FYEct7A==", "dev": true - }, - "responselike": { - "version": "1.0.2", - "resolved": "https://registry.npmjs.org/responselike/-/responselike-1.0.2.tgz", - "integrity": "sha1-kYcg7ztjHFZCvgaPFa3lpG9Loec=", - "dev": true, - "requires": { - "lowercase-keys": "^1.0.0" - } } } }, @@ -12566,12 +12466,6 @@ "integrity": "sha512-vkcDPrRZo1QZLbn5RLGPpg/WmIQ65qoWWhcGKf/b5eplkkarX0m9z8ppCat4mlOqUsWpyNuYgO3VRyrYHSzX5g==", "dev": true }, - "prepend-http": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/prepend-http/-/prepend-http-2.0.0.tgz", - "integrity": "sha1-6SQ0v6XqjBn0HN/UAddBo8gZ2Jc=", - "dev": true - }, "process-on-spawn": { "version": "1.0.0", "resolved": "https://registry.npmjs.org/process-on-spawn/-/process-on-spawn-1.0.0.tgz", @@ -12595,6 +12489,12 @@ "iterate-value": "^1.0.2" } }, + "proto-list": { + "version": "1.2.4", + "resolved": "https://registry.npmjs.org/proto-list/-/proto-list-1.2.4.tgz", + "integrity": "sha512-vtK/94akxsTMhe0/cbfpR+syPuszcuwhqVjJq26CuNDgFGj682oRBXOP5MJpv2r7JtE8MsiepGIqvvOTBwn2vA==", + "dev": true + }, "protocols": { "version": "1.4.8", "resolved": "https://registry.npmjs.org/protocols/-/protocols-1.4.8.tgz", @@ -12640,12 +12540,12 @@ "dev": true }, "pupa": { - "version": "2.1.1", - "resolved": "https://registry.npmjs.org/pupa/-/pupa-2.1.1.tgz", - "integrity": "sha512-l1jNAspIBSFqbT+y+5FosojNpVpF94nlI+wDUpqP9enwOTfHx9f0gh5nB96vl+6yTpsJsypeNrwfzPrKuHB41A==", + "version": "3.1.0", + "resolved": "https://registry.npmjs.org/pupa/-/pupa-3.1.0.tgz", + "integrity": "sha512-FLpr4flz5xZTSJxSeaheeMKN/EDzMdK7b8PTOC6a5PYFKTucWbdqjgqaEyH0shFiSJrVB1+Qqi4Tk19ccU6Aug==", "dev": true, "requires": { - "escape-goat": "^2.0.0" + "escape-goat": "^4.0.0" } }, "qs": { @@ -12723,7 +12623,7 @@ "strip-json-comments": { "version": "2.0.1", "resolved": "https://registry.npmjs.org/strip-json-comments/-/strip-json-comments-2.0.1.tgz", - "integrity": "sha1-PFMZQukIwml8DsNEhYwobHygpgo=", + "integrity": "sha512-4gB8na07fecVVkOI6Rs4e7T6NOTki5EmL7TUduTs6bu3EdnSycntVJ4re8kgZA+wx9IueI2Y11bfbgwtzuE0KQ==", "dev": true } } @@ -12764,27 +12664,27 @@ "dev": true }, "registry-auth-token": { - "version": "4.2.1", - "resolved": "https://registry.npmjs.org/registry-auth-token/-/registry-auth-token-4.2.1.tgz", - "integrity": "sha512-6gkSb4U6aWJB4SF2ZvLb76yCBjcvufXBqvvEx1HbmKPkutswjW1xNVRY0+daljIYRbogN7O0etYSlbiaEQyMyw==", + "version": "5.0.1", + "resolved": "https://registry.npmjs.org/registry-auth-token/-/registry-auth-token-5.0.1.tgz", + "integrity": "sha512-UfxVOj8seK1yaIOiieV4FIP01vfBDLsY0H9sQzi9EbbUdJiuuBjJgLa1DpImXMNPnVkBD4eVxTEXcrZA6kfpJA==", "dev": true, "requires": { - "rc": "^1.2.8" + "@pnpm/npm-conf": "^1.0.4" } }, "registry-url": { - "version": "5.1.0", - "resolved": "https://registry.npmjs.org/registry-url/-/registry-url-5.1.0.tgz", - "integrity": "sha512-8acYXXTI0AkQv6RAOjE3vOaIXZkT9wo4LOFbBKYQEEnnMNBpKqdUrI6S4NT0KPIo/WVvJ5tE/X5LF/TQUf0ekw==", + "version": "6.0.1", + "resolved": "https://registry.npmjs.org/registry-url/-/registry-url-6.0.1.tgz", + "integrity": "sha512-+crtS5QjFRqFCoQmvGduwYWEBng99ZvmFvF+cUJkGYF1L1BfU8C6Zp9T7f5vPAwyLkUExpvK+ANVZmGU49qi4Q==", "dev": true, "requires": { - "rc": "^1.2.8" + "rc": "1.2.8" } }, "release-it": { - "version": "15.0.0", - "resolved": "https://registry.npmjs.org/release-it/-/release-it-15.0.0.tgz", - "integrity": "sha512-Dnio6p+1O88UdQZmPjdXqq+Nrrn5t0USZyOctTPK5M36kOOfQTdp8V1Wlagz9QYIYr93NwovEZ+f4wK0P/kHbw==", + "version": "15.1.1", + "resolved": "https://registry.npmjs.org/release-it/-/release-it-15.1.1.tgz", + "integrity": "sha512-c+9G8Vy1LsRIaHbV+cd8o5pEo6dkPlrOr/E7cNeWdglEbdeRJiygCyaf2F3gzNAtH8v52ntHAInG6ZevwH0KEA==", "dev": true, "requires": { "@iarna/toml": "2.2.5", @@ -12795,30 +12695,30 @@ "execa": "6.1.0", "form-data": "4.0.0", "git-url-parse": "11.6.0", - "globby": "13.1.1", - "got": "12.0.4", - "inquirer": "8.2.4", + "globby": "13.1.2", + "got": "12.1.0", + "inquirer": "9.0.0", "is-ci": "3.0.1", "lodash": "4.17.21", "mime-types": "2.1.35", "new-github-release-url": "2.0.0", "open": "8.4.0", - "ora": "6.1.0", + "ora": "6.1.2", "os-name": "5.0.1", "promise.allsettled": "1.0.5", "proxy-agent": "5.0.0", "semver": "7.3.7", "shelljs": "0.8.5", - "update-notifier": "5.1.0", + "update-notifier": "6.0.2", "url-join": "5.0.0", "wildcard-match": "5.1.2", "yargs-parser": "21.0.1" }, "dependencies": { "globby": { - "version": "13.1.1", - "resolved": "https://registry.npmjs.org/globby/-/globby-13.1.1.tgz", - "integrity": "sha512-XMzoDZbGZ37tufiv7g0N4F/zp3zkwdFtVbV3EHsVl1KQr4RPLfNoT068/97RPshz2J5xYNEjLKKBKaGHifBd3Q==", + "version": "13.1.2", + "resolved": "https://registry.npmjs.org/globby/-/globby-13.1.2.tgz", + "integrity": "sha512-LKSDZXToac40u8Q1PQtZihbNdTYSNMuWe+K5l+oa6KgDzSvVrHXlJy40hUP522RjAIoNLJYBJi7ow+rbFpIhHQ==", "dev": true, "requires": { "dir-glob": "^3.0.1", @@ -12922,9 +12822,9 @@ } }, "restore-cursor": { - "version": "3.1.0", - "resolved": "https://registry.npmjs.org/restore-cursor/-/restore-cursor-3.1.0.tgz", - "integrity": "sha512-l+sSefzHpj5qimhFSE5a8nufZYAM3sBSVMAPtYkmC+4EH2anSGaEMXSD0izRQbu9nfyQ9y5JrVmp7E8oZrUjvA==", + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/restore-cursor/-/restore-cursor-4.0.0.tgz", + "integrity": "sha512-I9fPXU9geO9bHOt9pHHOhOkYerIMsmVaWB0rA2AI9ERh/+x/i7MV5HKBNrg+ljO5eoPVgCcnFuRjJ9uH6I/3eg==", "dev": true, "requires": { "onetime": "^5.1.0", @@ -13012,12 +12912,38 @@ "dev": true }, "semver-diff": { - "version": "3.1.1", - "resolved": "https://registry.npmjs.org/semver-diff/-/semver-diff-3.1.1.tgz", - "integrity": "sha512-GX0Ix/CJcHyB8c4ykpHGIAvLyOwOobtM/8d+TQkAd81/bEjgPHrfba41Vpesr7jX/t8Uh+R3EX9eAS5be+jQYg==", + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/semver-diff/-/semver-diff-4.0.0.tgz", + "integrity": "sha512-0Ju4+6A8iOnpL/Thra7dZsSlOHYAHIeMxfhWQRI1/VLcT3WDBZKKtQt/QkBOsiIN9ZpuvHE6cGZ0x4glCMmfiA==", "dev": true, "requires": { - "semver": "^6.3.0" + "semver": "^7.3.5" + }, + "dependencies": { + "lru-cache": { + "version": "6.0.0", + "resolved": "https://registry.npmjs.org/lru-cache/-/lru-cache-6.0.0.tgz", + "integrity": "sha512-Jo6dJ04CmSjuznwJSS3pUeWmd/H0ffTlkXXgwZi+eq1UCmqQwCh+eLsYOYCwY991i2Fah4h1BEMCx4qThGbsiA==", + "dev": true, + "requires": { + "yallist": "^4.0.0" + } + }, + "semver": { + "version": "7.3.7", + "resolved": "https://registry.npmjs.org/semver/-/semver-7.3.7.tgz", + "integrity": "sha512-QlYTucUYOews+WeEujDoEGziz4K6c47V/Bd+LjSSYcA94p+DmINdf7ncaUinThfvZyu13lN9OY1XDxt8C0Tw0g==", + "dev": true, + "requires": { + "lru-cache": "^6.0.0" + } + }, + "yallist": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/yallist/-/yallist-4.0.0.tgz", + "integrity": "sha512-3wdGidZyq5PB084XLES5TpOSRA3wjXAlIWMhum2kRcv/41Sn2emQ0dycQW4uZXLejwKvg6EsvbdlVL+FYEct7A==", + "dev": true + } } }, "serialize-javascript": { @@ -13096,9 +13022,9 @@ "dev": true }, "sinon": { - "version": "13.0.2", - "resolved": "https://registry.npmjs.org/sinon/-/sinon-13.0.2.tgz", - "integrity": "sha512-KvOrztAVqzSJWMDoxM4vM+GPys1df2VBoXm+YciyB/OLMamfS3VXh3oGh5WtrAGSzrgczNWFFY22oKb7Fi5eeA==", + "version": "14.0.0", + "resolved": "https://registry.npmjs.org/sinon/-/sinon-14.0.0.tgz", + "integrity": "sha512-ugA6BFmE+WrJdh0owRZHToLd32Uw3Lxq6E6LtNRU+xTVBefx632h03Q7apXWRsRdZAJ41LB8aUfn2+O4jsDNMw==", "dev": true, "requires": { "@sinonjs/commons": "^1.8.3", @@ -13315,7 +13241,7 @@ "through": { "version": "2.3.8", "resolved": "https://registry.npmjs.org/through/-/through-2.3.8.tgz", - "integrity": "sha1-DdTJ/6q8NXlgsbckEV1+Doai4fU=", + "integrity": "sha512-w89qg7PI8wAdvX60bMDP+bFoD5Dvhm9oLheFp5O4a2QF0cSBGsBX4qZmadPMvVqlLJBBci+WqGGOAPvcDeNSVg==", "dev": true }, "tmp": { @@ -13333,12 +13259,6 @@ "integrity": "sha1-3F5pjL0HkmW8c+A3doGk5Og/YW4=", "dev": true }, - "to-readable-stream": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/to-readable-stream/-/to-readable-stream-1.0.0.tgz", - "integrity": "sha512-Iq25XBt6zD5npPhlLVXGFN3/gyR2/qODcKNNyTMd4vbm39HUaOiAM4PMq0eMVC/Tkxz+Zjdsc55g9yyz+Yq00Q==", - "dev": true - }, "to-regex-range": { "version": "5.0.1", "resolved": "https://registry.npmjs.org/to-regex-range/-/to-regex-range-5.0.1.tgz", @@ -13370,12 +13290,12 @@ } }, "ts-node": { - "version": "10.7.0", - "resolved": "https://registry.npmjs.org/ts-node/-/ts-node-10.7.0.tgz", - "integrity": "sha512-TbIGS4xgJoX2i3do417KSaep1uRAW/Lu+WAL2doDHC0D6ummjirVOXU5/7aiZotbQ5p1Zp9tP7U6cYhA0O7M8A==", + "version": "10.8.1", + "resolved": "https://registry.npmjs.org/ts-node/-/ts-node-10.8.1.tgz", + "integrity": "sha512-Wwsnao4DQoJsN034wePSg5nZiw4YKXf56mPIAeD6wVmiv+RytNSWqc2f3fKvcUoV+Yn2+yocD71VOfQHbmVX4g==", "dev": true, "requires": { - "@cspotcode/source-map-support": "0.7.0", + "@cspotcode/source-map-support": "^0.8.0", "@tsconfig/node10": "^1.0.7", "@tsconfig/node12": "^1.0.7", "@tsconfig/node14": "^1.0.0", @@ -13386,7 +13306,7 @@ "create-require": "^1.1.0", "diff": "^4.0.1", "make-error": "^1.1.1", - "v8-compile-cache-lib": "^3.0.0", + "v8-compile-cache-lib": "^3.0.1", "yn": "3.1.1" }, "dependencies": { @@ -13437,9 +13357,9 @@ "dev": true }, "type-fest": { - "version": "0.21.3", - "resolved": "https://registry.npmjs.org/type-fest/-/type-fest-0.21.3.tgz", - "integrity": "sha512-t0rzBq87m3fVcduHDUFhKmyyX+9eo6WQjZvf51Ea/M0Q7+T374Jp1aUiyUl0GKxp8M/OETVHSDvmkyPgvX+X2w==", + "version": "0.20.2", + "resolved": "https://registry.npmjs.org/type-fest/-/type-fest-0.20.2.tgz", + "integrity": "sha512-Ne+eE4r0/iWnpAxD852z3A+N0Bt5RN//NjJwRd2VFHEmrywxf5vsZlh4R6lixl6B+wz/8d+maTSAkN1FIkI3LQ==", "dev": true }, "typedarray-to-buffer": { @@ -13452,15 +13372,14 @@ } }, "typedoc": { - "version": "0.22.15", - "resolved": "https://registry.npmjs.org/typedoc/-/typedoc-0.22.15.tgz", - "integrity": "sha512-CMd1lrqQbFvbx6S9G6fL4HKp3GoIuhujJReWqlIvSb2T26vGai+8Os3Mde7Pn832pXYemd9BMuuYWhFpL5st0Q==", + "version": "0.23.2", + "resolved": "https://registry.npmjs.org/typedoc/-/typedoc-0.23.2.tgz", + "integrity": "sha512-THpC4vtb3wu1yck6YHzEc4ck6W4lScf8TD0Rg7XAetDih8BzP+ErYO0/6DtdzYcZyKkDwEoujkMeWW7CffCbrQ==", "dev": true, "requires": { - "glob": "^7.2.0", "lunr": "^2.3.9", - "marked": "^4.0.12", - "minimatch": "^5.0.1", + "marked": "^4.0.16", + "minimatch": "^5.1.0", "shiki": "^0.10.1" }, "dependencies": { @@ -13474,9 +13393,9 @@ } }, "minimatch": { - "version": "5.0.1", - "resolved": "https://registry.npmjs.org/minimatch/-/minimatch-5.0.1.tgz", - "integrity": "sha512-nLDxIFRyhDblz3qMuq+SoRZED4+miJ/G+tdDrjkkkRnjAsBexeGpgjLEQ0blJy7rHhR2b93rhQY4SvyWu9v03g==", + "version": "5.1.0", + "resolved": "https://registry.npmjs.org/minimatch/-/minimatch-5.1.0.tgz", + "integrity": "sha512-9TPBGGak4nHfGZsPBohm9AWg6NoT7QTCehS3BIJABslyZbzxfV78QM2Y6+i741OPZIafFAaiiEMh5OyIrJPgtg==", "dev": true, "requires": { "brace-expansion": "^2.0.1" @@ -13485,9 +13404,9 @@ } }, "typescript": { - "version": "4.6.4", - "resolved": "https://registry.npmjs.org/typescript/-/typescript-4.6.4.tgz", - "integrity": "sha512-9ia/jWHIEbo49HfjrLGfKbZSuWo9iTMwXO+Ca3pRsSpbsMbc7/IU8NKdCZVRRBafVPGnoJeFL76ZOAA84I9fEg==", + "version": "4.7.4", + "resolved": "https://registry.npmjs.org/typescript/-/typescript-4.7.4.tgz", + "integrity": "sha512-C0WQT0gezHuw6AdY1M2jxUO83Rjf0HP7Sk1DtXj6j1EwkQNZrHAg2XPWlq62oqEhYvONq5pkC2Y9oPljWToLmQ==", "dev": true }, "unbox-primitive": { @@ -13503,12 +13422,12 @@ } }, "unique-string": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/unique-string/-/unique-string-2.0.0.tgz", - "integrity": "sha512-uNaeirEPvpZWSgzwsPGtU2zVSTrn/8L5q/IexZmH0eH6SA73CmAA5U4GwORTxQAZs95TAXLNqeLoPPNO5gZfWg==", + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/unique-string/-/unique-string-3.0.0.tgz", + "integrity": "sha512-VGXBUVwxKMBUznyffQweQABPRRW1vHZAbadFZud4pLFAqRGvv/96vafgjWFqzourzr8YonlQiPgH0YCJfawoGQ==", "dev": true, "requires": { - "crypto-random-string": "^2.0.0" + "crypto-random-string": "^4.0.0" } }, "universal-user-agent": { @@ -13530,52 +13449,27 @@ "dev": true }, "update-notifier": { - "version": "5.1.0", - "resolved": "https://registry.npmjs.org/update-notifier/-/update-notifier-5.1.0.tgz", - "integrity": "sha512-ItnICHbeMh9GqUy31hFPrD1kcuZ3rpxDZbf4KUDavXwS0bW5m7SLbDQpGX3UYr072cbrF5hFUs3r5tUsPwjfHw==", + "version": "6.0.2", + "resolved": "https://registry.npmjs.org/update-notifier/-/update-notifier-6.0.2.tgz", + "integrity": "sha512-EDxhTEVPZZRLWYcJ4ZXjGFN0oP7qYvbXWzEgRm/Yql4dHX5wDbvh89YHP6PK1lzZJYrMtXUuZZz8XGK+U6U1og==", "dev": true, "requires": { - "boxen": "^5.0.0", - "chalk": "^4.1.0", - "configstore": "^5.0.1", - "has-yarn": "^2.1.0", - "import-lazy": "^2.1.0", - "is-ci": "^2.0.0", + "boxen": "^7.0.0", + "chalk": "^5.0.1", + "configstore": "^6.0.0", + "has-yarn": "^3.0.0", + "import-lazy": "^4.0.0", + "is-ci": "^3.0.1", "is-installed-globally": "^0.4.0", - "is-npm": "^5.0.0", - "is-yarn-global": "^0.3.0", - "latest-version": "^5.1.0", - "pupa": "^2.1.1", - "semver": "^7.3.4", - "semver-diff": "^3.1.1", - "xdg-basedir": "^4.0.0" + "is-npm": "^6.0.0", + "is-yarn-global": "^0.4.0", + "latest-version": "^7.0.0", + "pupa": "^3.1.0", + "semver": "^7.3.7", + "semver-diff": "^4.0.0", + "xdg-basedir": "^5.1.0" }, "dependencies": { - "chalk": { - "version": "4.1.2", - "resolved": "https://registry.npmjs.org/chalk/-/chalk-4.1.2.tgz", - "integrity": "sha512-oKnbhFyRIXpUuez8iBMmyEa4nbj4IOQyuhc/wy9kY7/WVPcwIO9VA668Pu8RkO7+0G76SLROeyw9CpQ061i4mA==", - "dev": true, - "requires": { - "ansi-styles": "^4.1.0", - "supports-color": "^7.1.0" - } - }, - "ci-info": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/ci-info/-/ci-info-2.0.0.tgz", - "integrity": "sha512-5tK7EtrZ0N+OLFMthtqOj4fI2Jeb88C4CAZPu25LDVUgXJ0A3Js4PMGqrn0JU1W0Mh1/Z8wZzYPxqUrXeBboCQ==", - "dev": true - }, - "is-ci": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/is-ci/-/is-ci-2.0.0.tgz", - "integrity": "sha512-YfJT7rkpQB0updsdHLGWrvhBJfcfzNNawYDNIyQXJz0IViGf75O8EBPKSdvw2rF+LGCsX4FZ8tcr3b19LcZq4w==", - "dev": true, - "requires": { - "ci-info": "^2.0.0" - } - }, "lru-cache": { "version": "6.0.0", "resolved": "https://registry.npmjs.org/lru-cache/-/lru-cache-6.0.0.tgz", @@ -13617,19 +13511,10 @@ "integrity": "sha512-n2huDr9h9yzd6exQVnH/jU5mr+Pfx08LRXXZhkLLetAMESRj+anQsTAh940iMrIetKAmry9coFuZQ2jY8/p3WA==", "dev": true }, - "url-parse-lax": { - "version": "3.0.0", - "resolved": "https://registry.npmjs.org/url-parse-lax/-/url-parse-lax-3.0.0.tgz", - "integrity": "sha1-FrXK/Afb42dsGxmZF3gj1lA6yww=", - "dev": true, - "requires": { - "prepend-http": "^2.0.0" - } - }, "util-deprecate": { "version": "1.0.2", "resolved": "https://registry.npmjs.org/util-deprecate/-/util-deprecate-1.0.2.tgz", - "integrity": "sha1-RQ1Nyfpw3nMnYvvS1KKJgUGaDM8=", + "integrity": "sha512-EPD5q1uXyFxJpCrLnCc1nHnq3gOa6DZBocAIiI2TaSCA7VCJ1UJDMagCzIkXNsUYfD1daK//LTEQ8xiIbrHtcw==", "dev": true }, "uuid": { @@ -13675,7 +13560,7 @@ "wcwidth": { "version": "1.0.1", "resolved": "https://registry.npmjs.org/wcwidth/-/wcwidth-1.0.1.tgz", - "integrity": "sha1-8LDc+RW8X/FSivrbLA4XtTLaL+g=", + "integrity": "sha512-XHPEwS0q6TaxcvG85+8EYkbiCux2XtWG2mkc47Ng2A77BQu9+DqIOJldST4HgPkuea7dvKSj5VgX3P1d4rW8Tg==", "dev": true, "requires": { "defaults": "^1.0.3" @@ -13726,12 +13611,46 @@ "dev": true }, "widest-line": { - "version": "3.1.0", - "resolved": "https://registry.npmjs.org/widest-line/-/widest-line-3.1.0.tgz", - "integrity": "sha512-NsmoXalsWVDMGupxZ5R08ka9flZjjiLvHVAWYOKtiKM8ujtZWr9cRffak+uSE48+Ob8ObalXpwyeUiyDD6QFgg==", + "version": "4.0.1", + "resolved": "https://registry.npmjs.org/widest-line/-/widest-line-4.0.1.tgz", + "integrity": "sha512-o0cyEG0e8GPzT4iGHphIOh0cJOV8fivsXxddQasHPHfoZf1ZexrfeA21w2NaEN1RHE+fXlfISmOE8R9N3u3Qig==", "dev": true, "requires": { - "string-width": "^4.0.0" + "string-width": "^5.0.1" + }, + "dependencies": { + "ansi-regex": { + "version": "6.0.1", + "resolved": "https://registry.npmjs.org/ansi-regex/-/ansi-regex-6.0.1.tgz", + "integrity": "sha512-n5M855fKb2SsfMIiFFoVrABHJC8QtHwVx+mHWP3QcEqBHYienj5dHSgjbxtC0WEZXYt4wcD6zrQElDPhFuZgfA==", + "dev": true + }, + "emoji-regex": { + "version": "9.2.2", + "resolved": "https://registry.npmjs.org/emoji-regex/-/emoji-regex-9.2.2.tgz", + "integrity": "sha512-L18DaJsXSUk2+42pv8mLs5jJT2hqFkFE4j21wOmgbUqsZ2hL72NsUU785g9RXgo3s0ZNgVl42TiHp3ZtOv/Vyg==", + "dev": true + }, + "string-width": { + "version": "5.1.2", + "resolved": "https://registry.npmjs.org/string-width/-/string-width-5.1.2.tgz", + "integrity": "sha512-HnLOCR3vjcY8beoNLtcjZ5/nxn2afmME6lhrDrebokqMap+XbeW8n9TXpPDOqdGK5qcI3oT0GKTW6wC7EMiVqA==", + "dev": true, + "requires": { + "eastasianwidth": "^0.2.0", + "emoji-regex": "^9.2.2", + "strip-ansi": "^7.0.1" + } + }, + "strip-ansi": { + "version": "7.0.1", + "resolved": "https://registry.npmjs.org/strip-ansi/-/strip-ansi-7.0.1.tgz", + "integrity": "sha512-cXNxvT8dFNRVfhVME3JAe98mkXDYN2O1l7jmcwMnOslDeESg1rF/OZMtK0nRAhiari1unG5cD4jG3rapUAkLbw==", + "dev": true, + "requires": { + "ansi-regex": "^6.0.1" + } + } } }, "wildcard-match": { @@ -13852,9 +13771,9 @@ } }, "xdg-basedir": { - "version": "4.0.0", - "resolved": "https://registry.npmjs.org/xdg-basedir/-/xdg-basedir-4.0.0.tgz", - "integrity": "sha512-PSNhEJDejZYV7h50BohL09Er9VaIefr2LMAf3OEmpCkjOi34eYyQYAXUTjEQtZJTKcF0E2UKTh+osDLsgNim9Q==", + "version": "5.1.0", + "resolved": "https://registry.npmjs.org/xdg-basedir/-/xdg-basedir-5.1.0.tgz", + "integrity": "sha512-GCPAHLvrIH13+c0SuacwvRYj2SxJXQ4kaVTT5xgL3kPrz56XxkF21IGhjSE1+W0aw7gpBWRGXLCPnPby6lSpmQ==", "dev": true }, "xregexp": { diff --git a/packages/bloom/package.json b/packages/bloom/package.json index 8a1eee281f8..7b2a7f5c68e 100644 --- a/packages/bloom/package.json +++ b/packages/bloom/package.json @@ -18,12 +18,12 @@ "devDependencies": { "@istanbuljs/nyc-config-typescript": "^1.0.2", "@redis/test-utils": "*", - "@types/node": "^17.0.31", + "@types/node": "^18.0.0", "nyc": "^15.1.0", - "release-it": "^15.0.0", + "release-it": "^15.1.1", "source-map-support": "^0.5.21", - "ts-node": "^10.7.0", - "typedoc": "^0.22.15", - "typescript": "^4.6.4" + "ts-node": "^10.8.1", + "typedoc": "^0.23.2", + "typescript": "^4.7.4" } } diff --git a/packages/client/lib/test-utils.ts b/packages/client/lib/test-utils.ts index 57903321484..d2e33b4abf3 100644 --- a/packages/client/lib/test-utils.ts +++ b/packages/client/lib/test-utils.ts @@ -3,7 +3,7 @@ import { SinonSpy } from 'sinon'; import { promiseTimeout } from './utils'; export default new TestUtils({ - defaultDockerVersion: '7.0-rc3', + defaultDockerVersion: '7.0.2', dockerImageName: 'redis', dockerImageVersionArgument: 'redis-version' }); diff --git a/packages/client/package.json b/packages/client/package.json index 93d84ee7516..517a05bec1e 100644 --- a/packages/client/package.json +++ b/packages/client/package.json @@ -21,19 +21,19 @@ "devDependencies": { "@istanbuljs/nyc-config-typescript": "^1.0.2", "@redis/test-utils": "*", - "@types/node": "^17.0.31", - "@types/sinon": "^10.0.11", + "@types/node": "^18.0.0", + "@types/sinon": "^10.0.12", "@types/yallist": "^4.0.1", - "@typescript-eslint/eslint-plugin": "^5.21.0", - "@typescript-eslint/parser": "^5.21.0", - "eslint": "^8.14.0", + "@typescript-eslint/eslint-plugin": "^5.30.0", + "@typescript-eslint/parser": "^5.30.0", + "eslint": "^8.18.0", "nyc": "^15.1.0", - "release-it": "^15.0.0", - "sinon": "^13.0.2", + "release-it": "^15.1.1", + "sinon": "^14.0.0", "source-map-support": "^0.5.21", - "ts-node": "^10.7.0", - "typedoc": "^0.22.15", - "typescript": "^4.6.4" + "ts-node": "^10.8.1", + "typedoc": "^0.23.2", + "typescript": "^4.7.4" }, "engines": { "node": ">=14" diff --git a/packages/graph/lib/test-utils.ts b/packages/graph/lib/test-utils.ts index 161285d957c..4ae0e0a2695 100644 --- a/packages/graph/lib/test-utils.ts +++ b/packages/graph/lib/test-utils.ts @@ -4,7 +4,7 @@ import RedisGraph from '.'; export default new TestUtils({ dockerImageName: 'redislabs/redisgraph', dockerImageVersionArgument: 'redisgraph-version', - defaultDockerVersion: '2.8.9' + defaultDockerVersion: '2.8.15' }); export const GLOBAL = { diff --git a/packages/graph/package.json b/packages/graph/package.json index 06bac79f0a0..9a256fe3732 100644 --- a/packages/graph/package.json +++ b/packages/graph/package.json @@ -18,12 +18,12 @@ "devDependencies": { "@istanbuljs/nyc-config-typescript": "^1.0.2", "@redis/test-utils": "*", - "@types/node": "^17.0.31", + "@types/node": "^18.0.0", "nyc": "^15.1.0", - "release-it": "^15.0.0", + "release-it": "^15.1.1", "source-map-support": "^0.5.21", - "ts-node": "^10.7.0", - "typedoc": "^0.22.15", - "typescript": "^4.6.4" + "ts-node": "^10.8.1", + "typedoc": "^0.23.2", + "typescript": "^4.7.4" } } diff --git a/packages/json/lib/test-utils.ts b/packages/json/lib/test-utils.ts index c809b945773..fa150e4b7db 100644 --- a/packages/json/lib/test-utils.ts +++ b/packages/json/lib/test-utils.ts @@ -4,7 +4,7 @@ import RedisJSON from '.'; export default new TestUtils({ dockerImageName: 'redislabs/rejson', dockerImageVersionArgument: 'rejson-version', - defaultDockerVersion: '2.0.7' + defaultDockerVersion: '2.0.9' }); export const GLOBAL = { diff --git a/packages/json/package.json b/packages/json/package.json index 227b27c44de..86f4db507c0 100644 --- a/packages/json/package.json +++ b/packages/json/package.json @@ -18,12 +18,12 @@ "devDependencies": { "@istanbuljs/nyc-config-typescript": "^1.0.2", "@redis/test-utils": "*", - "@types/node": "^17.0.31", + "@types/node": "^18.0.0", "nyc": "^15.1.0", - "release-it": "^15.0.0", + "release-it": "^15.1.1", "source-map-support": "^0.5.21", - "ts-node": "^10.7.0", - "typedoc": "^0.22.15", - "typescript": "^4.6.4" + "ts-node": "^10.8.1", + "typedoc": "^0.23.2", + "typescript": "^4.7.4" } } diff --git a/packages/search/lib/test-utils.ts b/packages/search/lib/test-utils.ts index 1df5f32e76b..9e0af209103 100644 --- a/packages/search/lib/test-utils.ts +++ b/packages/search/lib/test-utils.ts @@ -4,7 +4,7 @@ import RediSearch from '.'; export default new TestUtils({ dockerImageName: 'redislabs/redisearch', dockerImageVersionArgument: 'redisearch-version', - defaultDockerVersion: '2.4.3' + defaultDockerVersion: '2.4.9' }); export const GLOBAL = { diff --git a/packages/search/package.json b/packages/search/package.json index e098ec735b3..20afcdf0d92 100644 --- a/packages/search/package.json +++ b/packages/search/package.json @@ -18,12 +18,12 @@ "devDependencies": { "@istanbuljs/nyc-config-typescript": "^1.0.2", "@redis/test-utils": "*", - "@types/node": "^17.0.31", + "@types/node": "^18.0.0", "nyc": "^15.1.0", - "release-it": "^15.0.0", + "release-it": "^15.1.1", "source-map-support": "^0.5.21", - "ts-node": "^10.7.0", - "typedoc": "^0.22.15", - "typescript": "^4.6.4" + "ts-node": "^10.8.1", + "typedoc": "^0.23.2", + "typescript": "^4.7.4" } } diff --git a/packages/test-utils/lib/dockers.ts b/packages/test-utils/lib/dockers.ts index 43b11b2bd2a..d6da977d93f 100644 --- a/packages/test-utils/lib/dockers.ts +++ b/packages/test-utils/lib/dockers.ts @@ -55,7 +55,7 @@ async function spawnRedisServerDocker({ image, version }: RedisServerDockerConfi 'docker run -d --network host $(' + `docker build ${DOCKER_FODLER_PATH} -q ` + `--build-arg IMAGE=${image}:${version} ` + - `--build-arg REDIS_ARGUMENTS="--save --port ${port.toString()} ${serverArguments.join(' ')}"` + + `--build-arg REDIS_ARGUMENTS="--save '' --port ${port.toString()} ${serverArguments.join(' ')}"` + ')' ); diff --git a/packages/test-utils/package.json b/packages/test-utils/package.json index 5ccf685f541..6abd771503a 100644 --- a/packages/test-utils/package.json +++ b/packages/test-utils/package.json @@ -12,13 +12,13 @@ "devDependencies": { "@istanbuljs/nyc-config-typescript": "^1.0.2", "@types/mocha": "^9.1.1", - "@types/node": "^17.0.31", + "@types/node": "^18.0.0", "@types/yargs": "^17.0.10", "mocha": "^10.0.0", "nyc": "^15.1.0", "source-map-support": "^0.5.21", - "ts-node": "^10.7.0", - "typescript": "^4.6.4", - "yargs": "^17.4.1" + "ts-node": "^10.8.1", + "typescript": "^4.7.4", + "yargs": "^17.5.1" } } diff --git a/packages/time-series/lib/test-utils.ts b/packages/time-series/lib/test-utils.ts index 96b2e04cbe2..20e24091812 100644 --- a/packages/time-series/lib/test-utils.ts +++ b/packages/time-series/lib/test-utils.ts @@ -4,7 +4,7 @@ import TimeSeries from '.'; export default new TestUtils({ dockerImageName: 'redislabs/redistimeseries', dockerImageVersionArgument: 'timeseries-version', - defaultDockerVersion: '1.6.9' + defaultDockerVersion: '1.6.16' }); export const GLOBAL = { diff --git a/packages/time-series/package.json b/packages/time-series/package.json index da13eb2a6ae..6801fe3f019 100644 --- a/packages/time-series/package.json +++ b/packages/time-series/package.json @@ -18,12 +18,12 @@ "devDependencies": { "@istanbuljs/nyc-config-typescript": "^1.0.2", "@redis/test-utils": "*", - "@types/node": "^17.0.31", + "@types/node": "^18.0.0", "nyc": "^15.1.0", - "release-it": "^15.0.0", + "release-it": "^15.1.1", "source-map-support": "^0.5.21", - "ts-node": "^10.7.0", - "typedoc": "^0.22.15", - "typescript": "^4.6.4" + "ts-node": "^10.8.1", + "typedoc": "^0.23.2", + "typescript": "^4.7.4" } } From 11e6d495b08eb109df38ae45e46e1820a64ec8c7 Mon Sep 17 00:00:00 2001 From: leibale Date: Thu, 30 Jun 2022 13:29:56 -0400 Subject: [PATCH 1226/1748] Release client@1.1.1 --- packages/client/package.json | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/packages/client/package.json b/packages/client/package.json index 93d84ee7516..127ccf0acd3 100644 --- a/packages/client/package.json +++ b/packages/client/package.json @@ -1,6 +1,6 @@ { "name": "@redis/client", - "version": "1.1.0", + "version": "1.1.1", "license": "MIT", "main": "./dist/index.js", "types": "./dist/index.d.ts", From 3efe1b42a493443e2186dc93853e3ed4f2c6b8c3 Mon Sep 17 00:00:00 2001 From: leibale Date: Thu, 30 Jun 2022 13:41:05 -0400 Subject: [PATCH 1227/1748] upgrade deps --- package-lock.json | 2797 ++++++++++++++++++++++++++------------------- package.json | 10 +- 2 files changed, 1628 insertions(+), 1179 deletions(-) diff --git a/package-lock.json b/package-lock.json index b4781815daf..167e9a82894 100644 --- a/package-lock.json +++ b/package-lock.json @@ -13,17 +13,17 @@ ], "dependencies": { "@redis/bloom": "1.0.2", - "@redis/client": "1.1.0", + "@redis/client": "1.1.1", "@redis/graph": "1.0.1", "@redis/json": "1.0.3", "@redis/search": "1.0.6", "@redis/time-series": "1.0.3" }, "devDependencies": { - "@tsconfig/node14": "^1.0.1", - "gh-pages": "^3.2.3", - "release-it": "^15.0.0", - "typescript": "^4.6.4" + "@tsconfig/node14": "^1.0.3", + "gh-pages": "^4.0.0", + "release-it": "^15.1.1", + "typescript": "^4.7.4" } }, "node_modules/@ampproject/remapping": { @@ -40,42 +40,42 @@ } }, "node_modules/@babel/code-frame": { - "version": "7.16.7", - "resolved": "https://registry.npmjs.org/@babel/code-frame/-/code-frame-7.16.7.tgz", - "integrity": "sha512-iAXqUn8IIeBTNd72xsFlgaXHkMBMt6y4HJp1tIaK465CWLT/fG1aqB7ykr95gHHmlBdGbFeWWfyB4NJJ0nmeIg==", + "version": "7.18.6", + "resolved": "https://registry.npmjs.org/@babel/code-frame/-/code-frame-7.18.6.tgz", + "integrity": "sha512-TDCmlK5eOvH+eH7cdAFlNXeVJqWIQ7gW9tY1GJIpUtFb6CmjVyq2VM3u71bOyR8CRihcCgMUYoDNyLXao3+70Q==", "dev": true, "dependencies": { - "@babel/highlight": "^7.16.7" + "@babel/highlight": "^7.18.6" }, "engines": { "node": ">=6.9.0" } }, "node_modules/@babel/compat-data": { - "version": "7.17.10", - "resolved": "https://registry.npmjs.org/@babel/compat-data/-/compat-data-7.17.10.tgz", - "integrity": "sha512-GZt/TCsG70Ms19gfZO1tM4CVnXsPgEPBCpJu+Qz3L0LUDsY5nZqFZglIoPC1kIYOtNBZlrnFT+klg12vFGZXrw==", + "version": "7.18.6", + "resolved": "https://registry.npmjs.org/@babel/compat-data/-/compat-data-7.18.6.tgz", + "integrity": "sha512-tzulrgDT0QD6U7BJ4TKVk2SDDg7wlP39P9yAx1RfLy7vP/7rsDRlWVfbWxElslu56+r7QOhB2NSDsabYYruoZQ==", "dev": true, "engines": { "node": ">=6.9.0" } }, "node_modules/@babel/core": { - "version": "7.17.10", - "resolved": "https://registry.npmjs.org/@babel/core/-/core-7.17.10.tgz", - "integrity": "sha512-liKoppandF3ZcBnIYFjfSDHZLKdLHGJRkoWtG8zQyGJBQfIYobpnVGI5+pLBNtS6psFLDzyq8+h5HiVljW9PNA==", + "version": "7.18.6", + "resolved": "https://registry.npmjs.org/@babel/core/-/core-7.18.6.tgz", + "integrity": "sha512-cQbWBpxcbbs/IUredIPkHiAGULLV8iwgNRMFzvbhEXISp4f3rUUXE5+TIw6KwUWUR3DwyI6gmBRnmAtYaWehwQ==", "dev": true, "dependencies": { "@ampproject/remapping": "^2.1.0", - "@babel/code-frame": "^7.16.7", - "@babel/generator": "^7.17.10", - "@babel/helper-compilation-targets": "^7.17.10", - "@babel/helper-module-transforms": "^7.17.7", - "@babel/helpers": "^7.17.9", - "@babel/parser": "^7.17.10", - "@babel/template": "^7.16.7", - "@babel/traverse": "^7.17.10", - "@babel/types": "^7.17.10", + "@babel/code-frame": "^7.18.6", + "@babel/generator": "^7.18.6", + "@babel/helper-compilation-targets": "^7.18.6", + "@babel/helper-module-transforms": "^7.18.6", + "@babel/helpers": "^7.18.6", + "@babel/parser": "^7.18.6", + "@babel/template": "^7.18.6", + "@babel/traverse": "^7.18.6", + "@babel/types": "^7.18.6", "convert-source-map": "^1.7.0", "debug": "^4.1.0", "gensync": "^1.0.0-beta.2", @@ -91,27 +91,41 @@ } }, "node_modules/@babel/generator": { - "version": "7.17.10", - "resolved": "https://registry.npmjs.org/@babel/generator/-/generator-7.17.10.tgz", - "integrity": "sha512-46MJZZo9y3o4kmhBVc7zW7i8dtR1oIK/sdO5NcfcZRhTGYi+KKJRtHNgsU6c4VUcJmUNV/LQdebD/9Dlv4K+Tg==", + "version": "7.18.7", + "resolved": "https://registry.npmjs.org/@babel/generator/-/generator-7.18.7.tgz", + "integrity": "sha512-shck+7VLlY72a2w9c3zYWuE1pwOKEiQHV7GTUbSnhyl5eu3i04t30tBY82ZRWrDfo3gkakCFtevExnxbkf2a3A==", "dev": true, "dependencies": { - "@babel/types": "^7.17.10", - "@jridgewell/gen-mapping": "^0.1.0", + "@babel/types": "^7.18.7", + "@jridgewell/gen-mapping": "^0.3.2", "jsesc": "^2.5.1" }, "engines": { "node": ">=6.9.0" } }, + "node_modules/@babel/generator/node_modules/@jridgewell/gen-mapping": { + "version": "0.3.2", + "resolved": "https://registry.npmjs.org/@jridgewell/gen-mapping/-/gen-mapping-0.3.2.tgz", + "integrity": "sha512-mh65xKQAzI6iBcFzwv28KVWSmCkdRBWoOh+bYQGW3+6OZvbbN3TqMGo5hqYxQniRcH9F2VZIoJCm4pa3BPDK/A==", + "dev": true, + "dependencies": { + "@jridgewell/set-array": "^1.0.1", + "@jridgewell/sourcemap-codec": "^1.4.10", + "@jridgewell/trace-mapping": "^0.3.9" + }, + "engines": { + "node": ">=6.0.0" + } + }, "node_modules/@babel/helper-compilation-targets": { - "version": "7.17.10", - "resolved": "https://registry.npmjs.org/@babel/helper-compilation-targets/-/helper-compilation-targets-7.17.10.tgz", - "integrity": "sha512-gh3RxjWbauw/dFiU/7whjd0qN9K6nPJMqe6+Er7rOavFh0CQUSwhAE3IcTho2rywPJFxej6TUUHDkWcYI6gGqQ==", + "version": "7.18.6", + "resolved": "https://registry.npmjs.org/@babel/helper-compilation-targets/-/helper-compilation-targets-7.18.6.tgz", + "integrity": "sha512-vFjbfhNCzqdeAtZflUFrG5YIFqGTqsctrtkZ1D/NB0mDW9TwW3GmmUepYY4G9wCET5rY5ugz4OGTcLd614IzQg==", "dev": true, "dependencies": { - "@babel/compat-data": "^7.17.10", - "@babel/helper-validator-option": "^7.16.7", + "@babel/compat-data": "^7.18.6", + "@babel/helper-validator-option": "^7.18.6", "browserslist": "^4.20.2", "semver": "^6.3.0" }, @@ -123,136 +137,133 @@ } }, "node_modules/@babel/helper-environment-visitor": { - "version": "7.16.7", - "resolved": "https://registry.npmjs.org/@babel/helper-environment-visitor/-/helper-environment-visitor-7.16.7.tgz", - "integrity": "sha512-SLLb0AAn6PkUeAfKJCCOl9e1R53pQlGAfc4y4XuMRZfqeMYLE0dM1LMhqbGAlGQY0lfw5/ohoYWAe9V1yibRag==", + "version": "7.18.6", + "resolved": "https://registry.npmjs.org/@babel/helper-environment-visitor/-/helper-environment-visitor-7.18.6.tgz", + "integrity": "sha512-8n6gSfn2baOY+qlp+VSzsosjCVGFqWKmDF0cCWOybh52Dw3SEyoWR1KrhMJASjLwIEkkAufZ0xvr+SxLHSpy2Q==", "dev": true, - "dependencies": { - "@babel/types": "^7.16.7" - }, "engines": { "node": ">=6.9.0" } }, "node_modules/@babel/helper-function-name": { - "version": "7.17.9", - "resolved": "https://registry.npmjs.org/@babel/helper-function-name/-/helper-function-name-7.17.9.tgz", - "integrity": "sha512-7cRisGlVtiVqZ0MW0/yFB4atgpGLWEHUVYnb448hZK4x+vih0YO5UoS11XIYtZYqHd0dIPMdUSv8q5K4LdMnIg==", + "version": "7.18.6", + "resolved": "https://registry.npmjs.org/@babel/helper-function-name/-/helper-function-name-7.18.6.tgz", + "integrity": "sha512-0mWMxV1aC97dhjCah5U5Ua7668r5ZmSC2DLfH2EZnf9c3/dHZKiFa5pRLMH5tjSl471tY6496ZWk/kjNONBxhw==", "dev": true, "dependencies": { - "@babel/template": "^7.16.7", - "@babel/types": "^7.17.0" + "@babel/template": "^7.18.6", + "@babel/types": "^7.18.6" }, "engines": { "node": ">=6.9.0" } }, "node_modules/@babel/helper-hoist-variables": { - "version": "7.16.7", - "resolved": "https://registry.npmjs.org/@babel/helper-hoist-variables/-/helper-hoist-variables-7.16.7.tgz", - "integrity": "sha512-m04d/0Op34H5v7pbZw6pSKP7weA6lsMvfiIAMeIvkY/R4xQtBSMFEigu9QTZ2qB/9l22vsxtM8a+Q8CzD255fg==", + "version": "7.18.6", + "resolved": "https://registry.npmjs.org/@babel/helper-hoist-variables/-/helper-hoist-variables-7.18.6.tgz", + "integrity": "sha512-UlJQPkFqFULIcyW5sbzgbkxn2FKRgwWiRexcuaR8RNJRy8+LLveqPjwZV/bwrLZCN0eUHD/x8D0heK1ozuoo6Q==", "dev": true, "dependencies": { - "@babel/types": "^7.16.7" + "@babel/types": "^7.18.6" }, "engines": { "node": ">=6.9.0" } }, "node_modules/@babel/helper-module-imports": { - "version": "7.16.7", - "resolved": "https://registry.npmjs.org/@babel/helper-module-imports/-/helper-module-imports-7.16.7.tgz", - "integrity": "sha512-LVtS6TqjJHFc+nYeITRo6VLXve70xmq7wPhWTqDJusJEgGmkAACWwMiTNrvfoQo6hEhFwAIixNkvB0jPXDL8Wg==", + "version": "7.18.6", + "resolved": "https://registry.npmjs.org/@babel/helper-module-imports/-/helper-module-imports-7.18.6.tgz", + "integrity": "sha512-0NFvs3VkuSYbFi1x2Vd6tKrywq+z/cLeYC/RJNFrIX/30Bf5aiGYbtvGXolEktzJH8o5E5KJ3tT+nkxuuZFVlA==", "dev": true, "dependencies": { - "@babel/types": "^7.16.7" + "@babel/types": "^7.18.6" }, "engines": { "node": ">=6.9.0" } }, "node_modules/@babel/helper-module-transforms": { - "version": "7.17.7", - "resolved": "https://registry.npmjs.org/@babel/helper-module-transforms/-/helper-module-transforms-7.17.7.tgz", - "integrity": "sha512-VmZD99F3gNTYB7fJRDTi+u6l/zxY0BE6OIxPSU7a50s6ZUQkHwSDmV92FfM+oCG0pZRVojGYhkR8I0OGeCVREw==", + "version": "7.18.6", + "resolved": "https://registry.npmjs.org/@babel/helper-module-transforms/-/helper-module-transforms-7.18.6.tgz", + "integrity": "sha512-L//phhB4al5uucwzlimruukHB3jRd5JGClwRMD/ROrVjXfLqovYnvQrK/JK36WYyVwGGO7OD3kMyVTjx+WVPhw==", "dev": true, "dependencies": { - "@babel/helper-environment-visitor": "^7.16.7", - "@babel/helper-module-imports": "^7.16.7", - "@babel/helper-simple-access": "^7.17.7", - "@babel/helper-split-export-declaration": "^7.16.7", - "@babel/helper-validator-identifier": "^7.16.7", - "@babel/template": "^7.16.7", - "@babel/traverse": "^7.17.3", - "@babel/types": "^7.17.0" + "@babel/helper-environment-visitor": "^7.18.6", + "@babel/helper-module-imports": "^7.18.6", + "@babel/helper-simple-access": "^7.18.6", + "@babel/helper-split-export-declaration": "^7.18.6", + "@babel/helper-validator-identifier": "^7.18.6", + "@babel/template": "^7.18.6", + "@babel/traverse": "^7.18.6", + "@babel/types": "^7.18.6" }, "engines": { "node": ">=6.9.0" } }, "node_modules/@babel/helper-simple-access": { - "version": "7.17.7", - "resolved": "https://registry.npmjs.org/@babel/helper-simple-access/-/helper-simple-access-7.17.7.tgz", - "integrity": "sha512-txyMCGroZ96i+Pxr3Je3lzEJjqwaRC9buMUgtomcrLe5Nd0+fk1h0LLA+ixUF5OW7AhHuQ7Es1WcQJZmZsz2XA==", + "version": "7.18.6", + "resolved": "https://registry.npmjs.org/@babel/helper-simple-access/-/helper-simple-access-7.18.6.tgz", + "integrity": "sha512-iNpIgTgyAvDQpDj76POqg+YEt8fPxx3yaNBg3S30dxNKm2SWfYhD0TGrK/Eu9wHpUW63VQU894TsTg+GLbUa1g==", "dev": true, "dependencies": { - "@babel/types": "^7.17.0" + "@babel/types": "^7.18.6" }, "engines": { "node": ">=6.9.0" } }, "node_modules/@babel/helper-split-export-declaration": { - "version": "7.16.7", - "resolved": "https://registry.npmjs.org/@babel/helper-split-export-declaration/-/helper-split-export-declaration-7.16.7.tgz", - "integrity": "sha512-xbWoy/PFoxSWazIToT9Sif+jJTlrMcndIsaOKvTA6u7QEo7ilkRZpjew18/W3c7nm8fXdUDXh02VXTbZ0pGDNw==", + "version": "7.18.6", + "resolved": "https://registry.npmjs.org/@babel/helper-split-export-declaration/-/helper-split-export-declaration-7.18.6.tgz", + "integrity": "sha512-bde1etTx6ZyTmobl9LLMMQsaizFVZrquTEHOqKeQESMKo4PlObf+8+JA25ZsIpZhT/WEd39+vOdLXAFG/nELpA==", "dev": true, "dependencies": { - "@babel/types": "^7.16.7" + "@babel/types": "^7.18.6" }, "engines": { "node": ">=6.9.0" } }, "node_modules/@babel/helper-validator-identifier": { - "version": "7.16.7", - "resolved": "https://registry.npmjs.org/@babel/helper-validator-identifier/-/helper-validator-identifier-7.16.7.tgz", - "integrity": "sha512-hsEnFemeiW4D08A5gUAZxLBTXpZ39P+a+DGDsHw1yxqyQ/jzFEnxf5uTEGp+3bzAbNOxU1paTgYS4ECU/IgfDw==", + "version": "7.18.6", + "resolved": "https://registry.npmjs.org/@babel/helper-validator-identifier/-/helper-validator-identifier-7.18.6.tgz", + "integrity": "sha512-MmetCkz9ej86nJQV+sFCxoGGrUbU3q02kgLciwkrt9QqEB7cP39oKEY0PakknEO0Gu20SskMRi+AYZ3b1TpN9g==", "dev": true, "engines": { "node": ">=6.9.0" } }, "node_modules/@babel/helper-validator-option": { - "version": "7.16.7", - "resolved": "https://registry.npmjs.org/@babel/helper-validator-option/-/helper-validator-option-7.16.7.tgz", - "integrity": "sha512-TRtenOuRUVo9oIQGPC5G9DgK4743cdxvtOw0weQNpZXaS16SCBi5MNjZF8vba3ETURjZpTbVn7Vvcf2eAwFozQ==", + "version": "7.18.6", + "resolved": "https://registry.npmjs.org/@babel/helper-validator-option/-/helper-validator-option-7.18.6.tgz", + "integrity": "sha512-XO7gESt5ouv/LRJdrVjkShckw6STTaB7l9BrpBaAHDeF5YZT+01PCwmR0SJHnkW6i8OwW/EVWRShfi4j2x+KQw==", "dev": true, "engines": { "node": ">=6.9.0" } }, "node_modules/@babel/helpers": { - "version": "7.17.9", - "resolved": "https://registry.npmjs.org/@babel/helpers/-/helpers-7.17.9.tgz", - "integrity": "sha512-cPCt915ShDWUEzEp3+UNRktO2n6v49l5RSnG9M5pS24hA+2FAc5si+Pn1i4VVbQQ+jh+bIZhPFQOJOzbrOYY1Q==", + "version": "7.18.6", + "resolved": "https://registry.npmjs.org/@babel/helpers/-/helpers-7.18.6.tgz", + "integrity": "sha512-vzSiiqbQOghPngUYt/zWGvK3LAsPhz55vc9XNN0xAl2gV4ieShI2OQli5duxWHD+72PZPTKAcfcZDE1Cwc5zsQ==", "dev": true, "dependencies": { - "@babel/template": "^7.16.7", - "@babel/traverse": "^7.17.9", - "@babel/types": "^7.17.0" + "@babel/template": "^7.18.6", + "@babel/traverse": "^7.18.6", + "@babel/types": "^7.18.6" }, "engines": { "node": ">=6.9.0" } }, "node_modules/@babel/highlight": { - "version": "7.17.9", - "resolved": "https://registry.npmjs.org/@babel/highlight/-/highlight-7.17.9.tgz", - "integrity": "sha512-J9PfEKCbFIv2X5bjTMiZu6Vf341N05QIY+d6FvVKynkG1S7G0j3I0QoRtWIrXhZ+/Nlb5Q0MzqL7TokEJ5BNHg==", + "version": "7.18.6", + "resolved": "https://registry.npmjs.org/@babel/highlight/-/highlight-7.18.6.tgz", + "integrity": "sha512-u7stbOuYjaPezCuLj29hNW1v64M2Md2qupEKP1fHc7WdOA3DgLh37suiSrZYY7haUB7iBeQZ9P1uiRF359do3g==", "dev": true, "dependencies": { - "@babel/helper-validator-identifier": "^7.16.7", + "@babel/helper-validator-identifier": "^7.18.6", "chalk": "^2.0.0", "js-tokens": "^4.0.0" }, @@ -298,13 +309,22 @@ "node_modules/@babel/highlight/node_modules/color-name": { "version": "1.1.3", "resolved": "https://registry.npmjs.org/color-name/-/color-name-1.1.3.tgz", - "integrity": "sha1-p9BVi9icQveV3UIyj3QIMcpTvCU=", + "integrity": "sha512-72fSenhMw2HZMTVHeCA9KCmpEIbzWiQsjN+BHcBbS9vr1mtt+vJjPdksIBNUmKAW8TFUDPJK5SUU3QhE9NEXDw==", "dev": true }, + "node_modules/@babel/highlight/node_modules/escape-string-regexp": { + "version": "1.0.5", + "resolved": "https://registry.npmjs.org/escape-string-regexp/-/escape-string-regexp-1.0.5.tgz", + "integrity": "sha512-vbRorB5FUQWvla16U8R/qgaFIya2qGzwDrNmCZuYKrbdSUMG6I1ZCGQRefkRVhuOkIGVne7BQ35DSfo1qvJqFg==", + "dev": true, + "engines": { + "node": ">=0.8.0" + } + }, "node_modules/@babel/highlight/node_modules/has-flag": { "version": "3.0.0", "resolved": "https://registry.npmjs.org/has-flag/-/has-flag-3.0.0.tgz", - "integrity": "sha1-tdRU3CGZriJWmfNGfloH87lVuv0=", + "integrity": "sha512-sKJf1+ceQBr4SMkvQnBDNDtf4TXpVhVGateu0t918bl30FnbE2m4vNLX+VWe/dpjlb+HugGYzW7uQXH98HPEYw==", "dev": true, "engines": { "node": ">=4" @@ -323,9 +343,9 @@ } }, "node_modules/@babel/parser": { - "version": "7.17.10", - "resolved": "https://registry.npmjs.org/@babel/parser/-/parser-7.17.10.tgz", - "integrity": "sha512-n2Q6i+fnJqzOaq2VkdXxy2TCPCWQZHiCo0XqmrCvDWcZQKRyZzYi4Z0yxlBuN0w+r2ZHmre+Q087DSrw3pbJDQ==", + "version": "7.18.6", + "resolved": "https://registry.npmjs.org/@babel/parser/-/parser-7.18.6.tgz", + "integrity": "sha512-uQVSa9jJUe/G/304lXspfWVpKpK4euFLgGiMQFOCpM/bgcAdeoHwi/OQz23O9GK2osz26ZiXRRV9aV+Yl1O8tw==", "dev": true, "bin": { "parser": "bin/babel-parser.js" @@ -335,33 +355,33 @@ } }, "node_modules/@babel/template": { - "version": "7.16.7", - "resolved": "https://registry.npmjs.org/@babel/template/-/template-7.16.7.tgz", - "integrity": "sha512-I8j/x8kHUrbYRTUxXrrMbfCa7jxkE7tZre39x3kjr9hvI82cK1FfqLygotcWN5kdPGWcLdWMHpSBavse5tWw3w==", + "version": "7.18.6", + "resolved": "https://registry.npmjs.org/@babel/template/-/template-7.18.6.tgz", + "integrity": "sha512-JoDWzPe+wgBsTTgdnIma3iHNFC7YVJoPssVBDjiHfNlyt4YcunDtcDOUmfVDfCK5MfdsaIoX9PkijPhjH3nYUw==", "dev": true, "dependencies": { - "@babel/code-frame": "^7.16.7", - "@babel/parser": "^7.16.7", - "@babel/types": "^7.16.7" + "@babel/code-frame": "^7.18.6", + "@babel/parser": "^7.18.6", + "@babel/types": "^7.18.6" }, "engines": { "node": ">=6.9.0" } }, "node_modules/@babel/traverse": { - "version": "7.17.10", - "resolved": "https://registry.npmjs.org/@babel/traverse/-/traverse-7.17.10.tgz", - "integrity": "sha512-VmbrTHQteIdUUQNTb+zE12SHS/xQVIShmBPhlNP12hD5poF2pbITW1Z4172d03HegaQWhLffdkRJYtAzp0AGcw==", - "dev": true, - "dependencies": { - "@babel/code-frame": "^7.16.7", - "@babel/generator": "^7.17.10", - "@babel/helper-environment-visitor": "^7.16.7", - "@babel/helper-function-name": "^7.17.9", - "@babel/helper-hoist-variables": "^7.16.7", - "@babel/helper-split-export-declaration": "^7.16.7", - "@babel/parser": "^7.17.10", - "@babel/types": "^7.17.10", + "version": "7.18.6", + "resolved": "https://registry.npmjs.org/@babel/traverse/-/traverse-7.18.6.tgz", + "integrity": "sha512-zS/OKyqmD7lslOtFqbscH6gMLFYOfG1YPqCKfAW5KrTeolKqvB8UelR49Fpr6y93kYkW2Ik00mT1LOGiAGvizw==", + "dev": true, + "dependencies": { + "@babel/code-frame": "^7.18.6", + "@babel/generator": "^7.18.6", + "@babel/helper-environment-visitor": "^7.18.6", + "@babel/helper-function-name": "^7.18.6", + "@babel/helper-hoist-variables": "^7.18.6", + "@babel/helper-split-export-declaration": "^7.18.6", + "@babel/parser": "^7.18.6", + "@babel/types": "^7.18.6", "debug": "^4.1.0", "globals": "^11.1.0" }, @@ -379,12 +399,12 @@ } }, "node_modules/@babel/types": { - "version": "7.17.10", - "resolved": "https://registry.npmjs.org/@babel/types/-/types-7.17.10.tgz", - "integrity": "sha512-9O26jG0mBYfGkUYCYZRnBwbVLd1UZOICEr2Em6InB6jVfsAv1GKgwXHmrSg+WFWDmeKTA6vyTZiN8tCSM5Oo3A==", + "version": "7.18.7", + "resolved": "https://registry.npmjs.org/@babel/types/-/types-7.18.7.tgz", + "integrity": "sha512-QG3yxTcTIBoAcQmkCs+wAPYZhu7Dk9rXKacINfNbdJDNERTbLQbHGyVG8q/YGMPeCJRIhSY0+fTc5+xuh6WPSQ==", "dev": true, "dependencies": { - "@babel/helper-validator-identifier": "^7.16.7", + "@babel/helper-validator-identifier": "^7.18.6", "to-fast-properties": "^2.0.0" }, "engines": { @@ -403,6 +423,16 @@ "node": ">=12" } }, + "node_modules/@cspotcode/source-map-support/node_modules/@jridgewell/trace-mapping": { + "version": "0.3.9", + "resolved": "https://registry.npmjs.org/@jridgewell/trace-mapping/-/trace-mapping-0.3.9.tgz", + "integrity": "sha512-3Belt6tdc8bPgAtbcmdtNJlirVoTmEb5e2gC94PnkwEW9jI6CAHUeoG85tjWP5WquqfavoMtMwiG4P926ZKKuQ==", + "dev": true, + "dependencies": { + "@jridgewell/resolve-uri": "^3.0.3", + "@jridgewell/sourcemap-codec": "^1.4.10" + } + }, "node_modules/@eslint/eslintrc": { "version": "1.3.0", "resolved": "https://registry.npmjs.org/@eslint/eslintrc/-/eslintrc-1.3.0.tgz", @@ -534,33 +564,33 @@ } }, "node_modules/@jridgewell/resolve-uri": { - "version": "3.0.6", - "resolved": "https://registry.npmjs.org/@jridgewell/resolve-uri/-/resolve-uri-3.0.6.tgz", - "integrity": "sha512-R7xHtBSNm+9SyvpJkdQl+qrM3Hm2fea3Ef197M3mUug+v+yR+Rhfbs7PBtcBUVnIWJ4JcAdjvij+c8hXS9p5aw==", + "version": "3.0.8", + "resolved": "https://registry.npmjs.org/@jridgewell/resolve-uri/-/resolve-uri-3.0.8.tgz", + "integrity": "sha512-YK5G9LaddzGbcucK4c8h5tWFmMPBvRZ/uyWmN1/SbBdIvqGUdWGkJ5BAaccgs6XbzVLsqbPJrBSFwKv3kT9i7w==", "dev": true, "engines": { "node": ">=6.0.0" } }, "node_modules/@jridgewell/set-array": { - "version": "1.1.0", - "resolved": "https://registry.npmjs.org/@jridgewell/set-array/-/set-array-1.1.0.tgz", - "integrity": "sha512-SfJxIxNVYLTsKwzB3MoOQ1yxf4w/E6MdkvTgrgAt1bfxjSrLUoHMKrDOykwN14q65waezZIdqDneUIPh4/sKxg==", + "version": "1.1.2", + "resolved": "https://registry.npmjs.org/@jridgewell/set-array/-/set-array-1.1.2.tgz", + "integrity": "sha512-xnkseuNADM0gt2bs+BvhO0p78Mk762YnZdsuzFV018NoG1Sj1SCQvpSqa7XUaTam5vAGasABV9qXASMKnFMwMw==", "dev": true, "engines": { "node": ">=6.0.0" } }, "node_modules/@jridgewell/sourcemap-codec": { - "version": "1.4.11", - "resolved": "https://registry.npmjs.org/@jridgewell/sourcemap-codec/-/sourcemap-codec-1.4.11.tgz", - "integrity": "sha512-Fg32GrJo61m+VqYSdRSjRXMjQ06j8YIYfcTqndLYVAaHmroZHLJZCydsWBOTDqXS2v+mjxohBWEMfg97GXmYQg==", + "version": "1.4.14", + "resolved": "https://registry.npmjs.org/@jridgewell/sourcemap-codec/-/sourcemap-codec-1.4.14.tgz", + "integrity": "sha512-XPSJHWmi394fuUuzDnGz1wiKqWfo1yXecHQMRf2l6hztTO+nPru658AyDngaBe7isIxEkRsPR3FZh+s7iVa4Uw==", "dev": true }, "node_modules/@jridgewell/trace-mapping": { - "version": "0.3.9", - "resolved": "https://registry.npmjs.org/@jridgewell/trace-mapping/-/trace-mapping-0.3.9.tgz", - "integrity": "sha512-3Belt6tdc8bPgAtbcmdtNJlirVoTmEb5e2gC94PnkwEW9jI6CAHUeoG85tjWP5WquqfavoMtMwiG4P926ZKKuQ==", + "version": "0.3.14", + "resolved": "https://registry.npmjs.org/@jridgewell/trace-mapping/-/trace-mapping-0.3.14.tgz", + "integrity": "sha512-bJWEfQ9lPTvm3SneWwRFVLzrh6nhjwqw7TUFFBEMzwvg7t7PCDenf2lDwqo4NQXzdpgBXyFgDWnQA+2vkruksQ==", "dev": true, "dependencies": { "@jridgewell/resolve-uri": "^3.0.3", @@ -649,18 +679,18 @@ } }, "node_modules/@octokit/openapi-types": { - "version": "11.2.0", - "resolved": "https://registry.npmjs.org/@octokit/openapi-types/-/openapi-types-11.2.0.tgz", - "integrity": "sha512-PBsVO+15KSlGmiI8QAzaqvsNlZlrDlyAJYcrXBCvVUxCp7VnXjkwPoFHgjEJXx3WF9BAwkA6nfCUA7i9sODzKA==", + "version": "12.5.0", + "resolved": "https://registry.npmjs.org/@octokit/openapi-types/-/openapi-types-12.5.0.tgz", + "integrity": "sha512-VatvE5wtRkJq6hAWGTBZ62WkrdlCiy0G0u27cVOYTfAWVZi7QqTurVcjpsyc5+9hXLPRP5O/DaNEs4TgAp4Mqg==", "dev": true }, "node_modules/@octokit/plugin-paginate-rest": { - "version": "2.17.0", - "resolved": "https://registry.npmjs.org/@octokit/plugin-paginate-rest/-/plugin-paginate-rest-2.17.0.tgz", - "integrity": "sha512-tzMbrbnam2Mt4AhuyCHvpRkS0oZ5MvwwcQPYGtMv4tUa5kkzG58SVB0fcsLulOZQeRnOgdkZWkRUiyBlh0Bkyw==", + "version": "2.21.0", + "resolved": "https://registry.npmjs.org/@octokit/plugin-paginate-rest/-/plugin-paginate-rest-2.21.0.tgz", + "integrity": "sha512-MoGEKjvDpTOCVb5gbeiW7kZm/cRfT256UJwHEuy+y+gTUuKziyXaiOkt5rM/4nzhp8UxVgvok9Tu7dMMpUybiQ==", "dev": true, "dependencies": { - "@octokit/types": "^6.34.0" + "@octokit/types": "^6.38.1" }, "peerDependencies": { "@octokit/core": ">=2" @@ -676,12 +706,12 @@ } }, "node_modules/@octokit/plugin-rest-endpoint-methods": { - "version": "5.13.0", - "resolved": "https://registry.npmjs.org/@octokit/plugin-rest-endpoint-methods/-/plugin-rest-endpoint-methods-5.13.0.tgz", - "integrity": "sha512-uJjMTkN1KaOIgNtUPMtIXDOjx6dGYysdIFhgA52x4xSadQCz3b/zJexvITDVpANnfKPW/+E0xkOvLntqMYpviA==", + "version": "5.16.0", + "resolved": "https://registry.npmjs.org/@octokit/plugin-rest-endpoint-methods/-/plugin-rest-endpoint-methods-5.16.0.tgz", + "integrity": "sha512-mvdwq+LvhR2GRDY82FgSZ52xX6wkOCpjiI3amiKbzKHd9nyKeFdXLsIQ3Go12tWRtvo+HwqoypLHDjRrgMFDQA==", "dev": true, "dependencies": { - "@octokit/types": "^6.34.0", + "@octokit/types": "^6.38.0", "deprecation": "^2.3.1" }, "peerDependencies": { @@ -726,12 +756,12 @@ } }, "node_modules/@octokit/types": { - "version": "6.34.0", - "resolved": "https://registry.npmjs.org/@octokit/types/-/types-6.34.0.tgz", - "integrity": "sha512-s1zLBjWhdEI2zwaoSgyOFoKSl109CUcVBCc7biPJ3aAf6LGLU6szDvi31JPU7bxfla2lqfhjbbg/5DdFNxOwHw==", + "version": "6.38.1", + "resolved": "https://registry.npmjs.org/@octokit/types/-/types-6.38.1.tgz", + "integrity": "sha512-kWMohLCIvnwApRmxRFDOqve7puiNNdtVfgwdDOm6QyJNorWOgKv2/AodCcGqx63o28kF7Dr4/nJCatrwwqhULg==", "dev": true, "dependencies": { - "@octokit/openapi-types": "^11.2.0" + "@octokit/openapi-types": "^12.5.0" } }, "node_modules/@pnpm/network.ca-file": { @@ -856,27 +886,27 @@ } }, "node_modules/@tsconfig/node10": { - "version": "1.0.8", - "resolved": "https://registry.npmjs.org/@tsconfig/node10/-/node10-1.0.8.tgz", - "integrity": "sha512-6XFfSQmMgq0CFLY1MslA/CPUfhIL919M1rMsa5lP2P097N2Wd1sSX0tx1u4olM16fLNhtHZpRhedZJphNJqmZg==", + "version": "1.0.9", + "resolved": "https://registry.npmjs.org/@tsconfig/node10/-/node10-1.0.9.tgz", + "integrity": "sha512-jNsYVVxU8v5g43Erja32laIDHXeoNvFEpX33OK4d6hljo3jDhCBDhx5dhCCTMWUojscpAagGiRkBKxpdl9fxqA==", "dev": true }, "node_modules/@tsconfig/node12": { - "version": "1.0.9", - "resolved": "https://registry.npmjs.org/@tsconfig/node12/-/node12-1.0.9.tgz", - "integrity": "sha512-/yBMcem+fbvhSREH+s14YJi18sp7J9jpuhYByADT2rypfajMZZN4WQ6zBGgBKp53NKmqI36wFYDb3yaMPurITw==", + "version": "1.0.11", + "resolved": "https://registry.npmjs.org/@tsconfig/node12/-/node12-1.0.11.tgz", + "integrity": "sha512-cqefuRsh12pWyGsIoBKJA9luFu3mRxCA+ORZvA4ktLSzIuCUtWVxGIuXigEwO5/ywWFMZ2QEGKWvkZG1zDMTag==", "dev": true }, "node_modules/@tsconfig/node14": { - "version": "1.0.1", - "resolved": "https://registry.npmjs.org/@tsconfig/node14/-/node14-1.0.1.tgz", - "integrity": "sha512-509r2+yARFfHHE7T6Puu2jjkoycftovhXRqW328PDXTVGKihlb1P8Z9mMZH04ebyajfRY7dedfGynlrFHJUQCg==", + "version": "1.0.3", + "resolved": "https://registry.npmjs.org/@tsconfig/node14/-/node14-1.0.3.tgz", + "integrity": "sha512-ysT8mhdixWK6Hw3i1V2AeRqZ5WfXg1G43mqoYlM2nc6388Fq5jcXyr5mRsqViLx/GJYdoL0bfXD8nmF+Zn/Iow==", "dev": true }, "node_modules/@tsconfig/node16": { - "version": "1.0.2", - "resolved": "https://registry.npmjs.org/@tsconfig/node16/-/node16-1.0.2.tgz", - "integrity": "sha512-eZxlbI8GZscaGS7kkc/trHTT5xgrjH3/1n2JDwusC9iahPKWMRvRjJSAN5mCXviuTGQ/lHnhvv8Q1YTpnfz9gA==", + "version": "1.0.3", + "resolved": "https://registry.npmjs.org/@tsconfig/node16/-/node16-1.0.3.tgz", + "integrity": "sha512-yOlFc+7UtL/89t2ZhjPvvB/DeAr3r+Dq58IgzsFkOAvVC6NMJXmCGjbptdXdR9qsX7pKcTL+s87FtYREi2dEEQ==", "dev": true }, "node_modules/@types/cacheable-request": { @@ -1346,6 +1376,47 @@ "string-width": "^4.1.0" } }, + "node_modules/ansi-align/node_modules/ansi-regex": { + "version": "5.0.1", + "resolved": "https://registry.npmjs.org/ansi-regex/-/ansi-regex-5.0.1.tgz", + "integrity": "sha512-quJQXlTSUGL2LH9SUXo8VwsY4soanhgo6LNSm84E1LBcE8s3O0wpdiRzyR9z/ZZJMlMWv37qOOb9pdJlMUEKFQ==", + "dev": true, + "engines": { + "node": ">=8" + } + }, + "node_modules/ansi-align/node_modules/emoji-regex": { + "version": "8.0.0", + "resolved": "https://registry.npmjs.org/emoji-regex/-/emoji-regex-8.0.0.tgz", + "integrity": "sha512-MSjYzcWNOA0ewAHpz0MxpYFvwg6yjy1NG3xteoqz644VCo/RPgnr1/GGt+ic3iJTzQ8Eu3TdM14SawnVUmGE6A==", + "dev": true + }, + "node_modules/ansi-align/node_modules/string-width": { + "version": "4.2.3", + "resolved": "https://registry.npmjs.org/string-width/-/string-width-4.2.3.tgz", + "integrity": "sha512-wKyQRQpjJ0sIp62ErSZdGsjMJWsap5oRNihHhu6G7JVO/9jIB6UyevL+tXuOqrng8j/cxKTWyWUwvSTriiZz/g==", + "dev": true, + "dependencies": { + "emoji-regex": "^8.0.0", + "is-fullwidth-code-point": "^3.0.0", + "strip-ansi": "^6.0.1" + }, + "engines": { + "node": ">=8" + } + }, + "node_modules/ansi-align/node_modules/strip-ansi": { + "version": "6.0.1", + "resolved": "https://registry.npmjs.org/strip-ansi/-/strip-ansi-6.0.1.tgz", + "integrity": "sha512-Y38VPSHcqkFrCpFnQ9vuSXmquuv5oXOKpGeT6aGrr3o3Gc9AlVa6JBfUSOCnbxGGZF+/0ooI7KrPuUSztUdU5A==", + "dev": true, + "dependencies": { + "ansi-regex": "^5.0.1" + }, + "engines": { + "node": ">=8" + } + }, "node_modules/ansi-colors": { "version": "4.1.1", "resolved": "https://registry.npmjs.org/ansi-colors/-/ansi-colors-4.1.1.tgz", @@ -1370,37 +1441,25 @@ "url": "https://github.com/sponsors/sindresorhus" } }, - "node_modules/ansi-escapes/node_modules/type-fest": { - "version": "1.4.0", - "resolved": "https://registry.npmjs.org/type-fest/-/type-fest-1.4.0.tgz", - "integrity": "sha512-yGSza74xk0UG8k+pLh5oeoYirvIiWo5t0/o3zHHAO2tRDiZcxWP7fywNlXhqb6/r6sWvwi+RsyQMWhVLe4BVuA==", + "node_modules/ansi-regex": { + "version": "6.0.1", + "resolved": "https://registry.npmjs.org/ansi-regex/-/ansi-regex-6.0.1.tgz", + "integrity": "sha512-n5M855fKb2SsfMIiFFoVrABHJC8QtHwVx+mHWP3QcEqBHYienj5dHSgjbxtC0WEZXYt4wcD6zrQElDPhFuZgfA==", "dev": true, "engines": { - "node": ">=10" + "node": ">=12" }, "funding": { - "url": "https://github.com/sponsors/sindresorhus" - } - }, - "node_modules/ansi-regex": { - "version": "5.0.1", - "resolved": "https://registry.npmjs.org/ansi-regex/-/ansi-regex-5.0.1.tgz", - "integrity": "sha512-quJQXlTSUGL2LH9SUXo8VwsY4soanhgo6LNSm84E1LBcE8s3O0wpdiRzyR9z/ZZJMlMWv37qOOb9pdJlMUEKFQ==", - "dev": true, - "engines": { - "node": ">=8" + "url": "https://github.com/chalk/ansi-regex?sponsor=1" } }, "node_modules/ansi-styles": { - "version": "4.3.0", - "resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-4.3.0.tgz", - "integrity": "sha512-zbB9rCJAT1rbjiVDb2hqKFHNYLxgtk8NURxZ3IZwD3F6NtxbXZQCnnSi1Lkx+IDohdPlFp222wVALIheZJQSEg==", + "version": "6.1.0", + "resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-6.1.0.tgz", + "integrity": "sha512-VbqNsoz55SYGczauuup0MFUyXNQviSpFTj1RQtFzmQLk18qbVSpTFFGMT293rmDaQuKCT6InmbuEyUne4mTuxQ==", "dev": true, - "dependencies": { - "color-convert": "^2.0.1" - }, "engines": { - "node": ">=8" + "node": ">=12" }, "funding": { "url": "https://github.com/chalk/ansi-styles?sponsor=1" @@ -1434,7 +1493,7 @@ "node_modules/archy": { "version": "1.0.0", "resolved": "https://registry.npmjs.org/archy/-/archy-1.0.0.tgz", - "integrity": "sha1-+cjBN1fMHde8N5rHeyxipcKGjEA=", + "integrity": "sha512-Xg+9RwCg/0p32teKdGMPTPnVXKD0w3DfHnFTficozsAgsvq2XenPJq/MYpzzQ/v8zrOyJn6Ds39VA4JIDwFfqw==", "dev": true }, "node_modules/arg": { @@ -1452,7 +1511,7 @@ "node_modules/array-union": { "version": "1.0.2", "resolved": "https://registry.npmjs.org/array-union/-/array-union-1.0.2.tgz", - "integrity": "sha1-mjRBDk9OPaI96jdb5b5w8kd47Dk=", + "integrity": "sha512-Dxr6QJj/RdU/hCaBjOfxW+q6lyuVE6JFWIrAUpuOOhoJJoQ99cUn3igRaHVB5P9WrgFVN0FfArM3x0cueOU8ng==", "dev": true, "dependencies": { "array-uniq": "^1.0.1" @@ -1464,7 +1523,7 @@ "node_modules/array-uniq": { "version": "1.0.3", "resolved": "https://registry.npmjs.org/array-uniq/-/array-uniq-1.0.3.tgz", - "integrity": "sha1-r2rId6Jcx/dOBYiUdThY39sk/bY=", + "integrity": "sha512-MNha4BWQ6JbwhFhj03YK552f7cb3AzoE8SzeljgChvL1dl3IcvggXVz1DilzySZkCja+CXuZbdW7yATchWn8/Q==", "dev": true, "engines": { "node": ">=0.10.0" @@ -1522,7 +1581,7 @@ "node_modules/asynckit": { "version": "0.4.0", "resolved": "https://registry.npmjs.org/asynckit/-/asynckit-0.4.0.tgz", - "integrity": "sha1-x57Zf380y48robyXkLzDZkdLS3k=", + "integrity": "sha512-Oei9OH4tRh0YqU3GxhX79dM/mwVgvbZJaSNaRk+bshkj0S5cfHcgYakreBjrHwatXKbz+IoIdYLxrKim2MjW0Q==", "dev": true }, "node_modules/balanced-match": { @@ -1599,30 +1658,6 @@ "url": "https://github.com/sponsors/sindresorhus" } }, - "node_modules/boxen/node_modules/ansi-regex": { - "version": "6.0.1", - "resolved": "https://registry.npmjs.org/ansi-regex/-/ansi-regex-6.0.1.tgz", - "integrity": "sha512-n5M855fKb2SsfMIiFFoVrABHJC8QtHwVx+mHWP3QcEqBHYienj5dHSgjbxtC0WEZXYt4wcD6zrQElDPhFuZgfA==", - "dev": true, - "engines": { - "node": ">=12" - }, - "funding": { - "url": "https://github.com/chalk/ansi-regex?sponsor=1" - } - }, - "node_modules/boxen/node_modules/ansi-styles": { - "version": "6.1.0", - "resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-6.1.0.tgz", - "integrity": "sha512-VbqNsoz55SYGczauuup0MFUyXNQviSpFTj1RQtFzmQLk18qbVSpTFFGMT293rmDaQuKCT6InmbuEyUne4mTuxQ==", - "dev": true, - "engines": { - "node": ">=12" - }, - "funding": { - "url": "https://github.com/chalk/ansi-styles?sponsor=1" - } - }, "node_modules/boxen/node_modules/camelcase": { "version": "7.0.0", "resolved": "https://registry.npmjs.org/camelcase/-/camelcase-7.0.0.tgz", @@ -1635,48 +1670,10 @@ "url": "https://github.com/sponsors/sindresorhus" } }, - "node_modules/boxen/node_modules/emoji-regex": { - "version": "9.2.2", - "resolved": "https://registry.npmjs.org/emoji-regex/-/emoji-regex-9.2.2.tgz", - "integrity": "sha512-L18DaJsXSUk2+42pv8mLs5jJT2hqFkFE4j21wOmgbUqsZ2hL72NsUU785g9RXgo3s0ZNgVl42TiHp3ZtOv/Vyg==", - "dev": true - }, - "node_modules/boxen/node_modules/string-width": { - "version": "5.1.2", - "resolved": "https://registry.npmjs.org/string-width/-/string-width-5.1.2.tgz", - "integrity": "sha512-HnLOCR3vjcY8beoNLtcjZ5/nxn2afmME6lhrDrebokqMap+XbeW8n9TXpPDOqdGK5qcI3oT0GKTW6wC7EMiVqA==", - "dev": true, - "dependencies": { - "eastasianwidth": "^0.2.0", - "emoji-regex": "^9.2.2", - "strip-ansi": "^7.0.1" - }, - "engines": { - "node": ">=12" - }, - "funding": { - "url": "https://github.com/sponsors/sindresorhus" - } - }, - "node_modules/boxen/node_modules/strip-ansi": { - "version": "7.0.1", - "resolved": "https://registry.npmjs.org/strip-ansi/-/strip-ansi-7.0.1.tgz", - "integrity": "sha512-cXNxvT8dFNRVfhVME3JAe98mkXDYN2O1l7jmcwMnOslDeESg1rF/OZMtK0nRAhiari1unG5cD4jG3rapUAkLbw==", - "dev": true, - "dependencies": { - "ansi-regex": "^6.0.1" - }, - "engines": { - "node": ">=12" - }, - "funding": { - "url": "https://github.com/chalk/strip-ansi?sponsor=1" - } - }, "node_modules/boxen/node_modules/type-fest": { - "version": "2.14.0", - "resolved": "https://registry.npmjs.org/type-fest/-/type-fest-2.14.0.tgz", - "integrity": "sha512-hQnTQkFjL5ik6HF2fTAM8ycbr94UbQXK364wF930VHb0dfBJ5JBP8qwrR8TaK9zwUEk7meruo2JAUDMwvuxd/w==", + "version": "2.15.0", + "resolved": "https://registry.npmjs.org/type-fest/-/type-fest-2.15.0.tgz", + "integrity": "sha512-hpsXfQZrAiusX8KMY5HXSEV7xqGAGxFQoNDT+iW0yJE/bdYG0uMlRaUG0kNAUbF5p6Cq5Kuf69lm4M569QtRGw==", "dev": true, "engines": { "node": ">=12.20" @@ -1685,23 +1682,6 @@ "url": "https://github.com/sponsors/sindresorhus" } }, - "node_modules/boxen/node_modules/wrap-ansi": { - "version": "8.0.1", - "resolved": "https://registry.npmjs.org/wrap-ansi/-/wrap-ansi-8.0.1.tgz", - "integrity": "sha512-QFF+ufAqhoYHvoHdajT/Po7KoXVBPXS2bgjIam5isfWJPfIOnQZ50JtUiVvCv/sjgacf3yRrt2ZKUZ/V4itN4g==", - "dev": true, - "dependencies": { - "ansi-styles": "^6.1.0", - "string-width": "^5.0.1", - "strip-ansi": "^7.0.1" - }, - "engines": { - "node": ">=12" - }, - "funding": { - "url": "https://github.com/chalk/wrap-ansi?sponsor=1" - } - }, "node_modules/brace-expansion": { "version": "1.1.11", "resolved": "https://registry.npmjs.org/brace-expansion/-/brace-expansion-1.1.11.tgz", @@ -1731,9 +1711,9 @@ "dev": true }, "node_modules/browserslist": { - "version": "4.20.3", - "resolved": "https://registry.npmjs.org/browserslist/-/browserslist-4.20.3.tgz", - "integrity": "sha512-NBhymBQl1zM0Y5dQT/O+xiLP9/rzOIQdKM/eMJBAq7yBgaB6krIYLGejrwVYnSHZdqjscB1SPuAjHwxjvN6Wdg==", + "version": "4.21.1", + "resolved": "https://registry.npmjs.org/browserslist/-/browserslist-4.21.1.tgz", + "integrity": "sha512-Nq8MFCSrnJXSc88yliwlzQe3qNe3VntIjhsArW9IJOEPSHNx23FalwApUVbzAWABLhYJJ7y8AynWI/XM8OdfjQ==", "dev": true, "funding": [ { @@ -1746,11 +1726,10 @@ } ], "dependencies": { - "caniuse-lite": "^1.0.30001332", - "electron-to-chromium": "^1.4.118", - "escalade": "^3.1.1", - "node-releases": "^2.0.3", - "picocolors": "^1.0.0" + "caniuse-lite": "^1.0.30001359", + "electron-to-chromium": "^1.4.172", + "node-releases": "^2.0.5", + "update-browserslist-db": "^1.0.4" }, "bin": { "browserslist": "cli.js" @@ -1896,9 +1875,9 @@ } }, "node_modules/caniuse-lite": { - "version": "1.0.30001335", - "resolved": "https://registry.npmjs.org/caniuse-lite/-/caniuse-lite-1.0.30001335.tgz", - "integrity": "sha512-ddP1Tgm7z2iIxu6QTtbZUv6HJxSaV/PZeSrWFZtbY4JZ69tOeNhBCl3HyRQgeNZKE5AOn1kpV7fhljigy0Ty3w==", + "version": "1.0.30001361", + "resolved": "https://registry.npmjs.org/caniuse-lite/-/caniuse-lite-1.0.30001361.tgz", + "integrity": "sha512-ybhCrjNtkFji1/Wto6SSJKkWk6kZgVQsDq5QI83SafsF6FXv2JB4df9eEdH6g8sdGgqTXrFLjAxqBGgYoU3azQ==", "dev": true, "funding": [ { @@ -1969,9 +1948,9 @@ } }, "node_modules/ci-info": { - "version": "3.3.0", - "resolved": "https://registry.npmjs.org/ci-info/-/ci-info-3.3.0.tgz", - "integrity": "sha512-riT/3vI5YpVH6/qomlDnJow6TBee2PBKSEpx3O32EGPYbWGIRsIlGRms3Sm74wYE1JMo8RnO04Hb12+v1J5ICw==", + "version": "3.3.2", + "resolved": "https://registry.npmjs.org/ci-info/-/ci-info-3.3.2.tgz", + "integrity": "sha512-xmDt/QIAdeZ9+nfdPsaBCpMvHNLFiLdjj59qjqn+6iPe6YmHGQ35sBnQ8uslRBXFmXkiZQOJRjvQeoGppoTjjg==", "dev": true }, "node_modules/clean-stack": { @@ -2042,6 +2021,62 @@ "wrap-ansi": "^6.2.0" } }, + "node_modules/cliui/node_modules/ansi-regex": { + "version": "5.0.1", + "resolved": "https://registry.npmjs.org/ansi-regex/-/ansi-regex-5.0.1.tgz", + "integrity": "sha512-quJQXlTSUGL2LH9SUXo8VwsY4soanhgo6LNSm84E1LBcE8s3O0wpdiRzyR9z/ZZJMlMWv37qOOb9pdJlMUEKFQ==", + "dev": true, + "engines": { + "node": ">=8" + } + }, + "node_modules/cliui/node_modules/ansi-styles": { + "version": "4.3.0", + "resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-4.3.0.tgz", + "integrity": "sha512-zbB9rCJAT1rbjiVDb2hqKFHNYLxgtk8NURxZ3IZwD3F6NtxbXZQCnnSi1Lkx+IDohdPlFp222wVALIheZJQSEg==", + "dev": true, + "dependencies": { + "color-convert": "^2.0.1" + }, + "engines": { + "node": ">=8" + }, + "funding": { + "url": "https://github.com/chalk/ansi-styles?sponsor=1" + } + }, + "node_modules/cliui/node_modules/emoji-regex": { + "version": "8.0.0", + "resolved": "https://registry.npmjs.org/emoji-regex/-/emoji-regex-8.0.0.tgz", + "integrity": "sha512-MSjYzcWNOA0ewAHpz0MxpYFvwg6yjy1NG3xteoqz644VCo/RPgnr1/GGt+ic3iJTzQ8Eu3TdM14SawnVUmGE6A==", + "dev": true + }, + "node_modules/cliui/node_modules/string-width": { + "version": "4.2.3", + "resolved": "https://registry.npmjs.org/string-width/-/string-width-4.2.3.tgz", + "integrity": "sha512-wKyQRQpjJ0sIp62ErSZdGsjMJWsap5oRNihHhu6G7JVO/9jIB6UyevL+tXuOqrng8j/cxKTWyWUwvSTriiZz/g==", + "dev": true, + "dependencies": { + "emoji-regex": "^8.0.0", + "is-fullwidth-code-point": "^3.0.0", + "strip-ansi": "^6.0.1" + }, + "engines": { + "node": ">=8" + } + }, + "node_modules/cliui/node_modules/strip-ansi": { + "version": "6.0.1", + "resolved": "https://registry.npmjs.org/strip-ansi/-/strip-ansi-6.0.1.tgz", + "integrity": "sha512-Y38VPSHcqkFrCpFnQ9vuSXmquuv5oXOKpGeT6aGrr3o3Gc9AlVa6JBfUSOCnbxGGZF+/0ooI7KrPuUSztUdU5A==", + "dev": true, + "dependencies": { + "ansi-regex": "^5.0.1" + }, + "engines": { + "node": ">=8" + } + }, "node_modules/cliui/node_modules/wrap-ansi": { "version": "6.2.0", "resolved": "https://registry.npmjs.org/wrap-ansi/-/wrap-ansi-6.2.0.tgz", @@ -2121,7 +2156,7 @@ "node_modules/commondir": { "version": "1.0.1", "resolved": "https://registry.npmjs.org/commondir/-/commondir-1.0.1.tgz", - "integrity": "sha1-3dgA2gxmEnOTzKWVDqloo6rxJTs=", + "integrity": "sha512-W9pAhw0ja1Edb5GVdIF1mjZw/ASI0AlShXM83UUGe2DVr5TdAPEA1OA8m/g8zWp9x6On7gqufY+FatDbC3MDQg==", "dev": true }, "node_modules/compress-brotli": { @@ -2140,7 +2175,7 @@ "node_modules/concat-map": { "version": "0.0.1", "resolved": "https://registry.npmjs.org/concat-map/-/concat-map-0.0.1.tgz", - "integrity": "sha1-2Klr13/Wjfd5OnMDajug1UBdR3s=", + "integrity": "sha512-/Srv4dswyQNBfohGpz9o6Yb3Gz3SrUDqBH5rTuhGR7ahtlbYKnVxw2bCFMRljaA7EXHaXZ8wsHdodFvbkhKmqg==", "dev": true }, "node_modules/config-chain": { @@ -2244,18 +2279,6 @@ "url": "https://github.com/sponsors/sindresorhus" } }, - "node_modules/crypto-random-string/node_modules/type-fest": { - "version": "1.4.0", - "resolved": "https://registry.npmjs.org/type-fest/-/type-fest-1.4.0.tgz", - "integrity": "sha512-yGSza74xk0UG8k+pLh5oeoYirvIiWo5t0/o3zHHAO2tRDiZcxWP7fywNlXhqb6/r6sWvwi+RsyQMWhVLe4BVuA==", - "dev": true, - "engines": { - "node": ">=10" - }, - "funding": { - "url": "https://github.com/sponsors/sindresorhus" - } - }, "node_modules/data-uri-to-buffer": { "version": "3.0.1", "resolved": "https://registry.npmjs.org/data-uri-to-buffer/-/data-uri-to-buffer-3.0.1.tgz", @@ -2285,7 +2308,7 @@ "node_modules/decamelize": { "version": "1.2.0", "resolved": "https://registry.npmjs.org/decamelize/-/decamelize-1.2.0.tgz", - "integrity": "sha1-9lNNFRSCabIDUue+4m9QH5oZEpA=", + "integrity": "sha512-z2S+W9X73hAUUki+N+9Za2lBlun89zigOyGrsax+KUQ6wKW4ZoWpEYBkGhQjwAjjDCkWxhY0VKEhk8wzY7F5cA==", "dev": true, "engines": { "node": ">=0.10.0" @@ -2294,7 +2317,7 @@ "node_modules/decode-uri-component": { "version": "0.2.0", "resolved": "https://registry.npmjs.org/decode-uri-component/-/decode-uri-component-0.2.0.tgz", - "integrity": "sha1-6zkTMzRYd1y4TNGh+uBiEGu4dUU=", + "integrity": "sha512-hjf+xovcEn31w/EUYdTXQh/8smFL/dzYjohQGEIgjyNavaJfBY2p5F527Bo1VPATxv0VYTUC2bOcXvqFwk78Og==", "dev": true, "engines": { "node": ">=0.10" @@ -2415,7 +2438,7 @@ "node_modules/delayed-stream": { "version": "1.0.0", "resolved": "https://registry.npmjs.org/delayed-stream/-/delayed-stream-1.0.0.tgz", - "integrity": "sha1-3zrhmayt+31ECqrgsp4icrJOxhk=", + "integrity": "sha512-ZySD7Nf91aLB0RxL4KGrKHBXl7Eds1DAmEdcoVawXnLD7SDhpNgtuII2aAkg7a7QS41jxPSZ17p4VdGnMHk3MQ==", "dev": true, "engines": { "node": ">=0.4.0" @@ -2491,9 +2514,9 @@ "dev": true }, "node_modules/electron-to-chromium": { - "version": "1.4.129", - "resolved": "https://registry.npmjs.org/electron-to-chromium/-/electron-to-chromium-1.4.129.tgz", - "integrity": "sha512-GgtN6bsDtHdtXJtlMYZWGB/uOyjZWjmRDumXTas7dGBaB9zUyCjzHet1DY2KhyHN8R0GLbzZWqm4efeddqqyRQ==", + "version": "1.4.174", + "resolved": "https://registry.npmjs.org/electron-to-chromium/-/electron-to-chromium-1.4.174.tgz", + "integrity": "sha512-JER+w+9MV2MBVFOXxP036bLlNOnzbYAWrWU8sNUwoOO69T3w4564WhM5H5atd8VVS8U4vpi0i0kdoYzm1NPQgQ==", "dev": true }, "node_modules/email-addresses": { @@ -2503,9 +2526,9 @@ "dev": true }, "node_modules/emoji-regex": { - "version": "8.0.0", - "resolved": "https://registry.npmjs.org/emoji-regex/-/emoji-regex-8.0.0.tgz", - "integrity": "sha512-MSjYzcWNOA0ewAHpz0MxpYFvwg6yjy1NG3xteoqz644VCo/RPgnr1/GGt+ic3iJTzQ8Eu3TdM14SawnVUmGE6A==", + "version": "9.2.2", + "resolved": "https://registry.npmjs.org/emoji-regex/-/emoji-regex-9.2.2.tgz", + "integrity": "sha512-L18DaJsXSUk2+42pv8mLs5jJT2hqFkFE4j21wOmgbUqsZ2hL72NsUU785g9RXgo3s0ZNgVl42TiHp3ZtOv/Vyg==", "dev": true }, "node_modules/end-of-stream": { @@ -2527,17 +2550,19 @@ } }, "node_modules/es-abstract": { - "version": "1.19.5", - "resolved": "https://registry.npmjs.org/es-abstract/-/es-abstract-1.19.5.tgz", - "integrity": "sha512-Aa2G2+Rd3b6kxEUKTF4TaW67czBLyAv3z7VOhYRU50YBx+bbsYZ9xQP4lMNazePuFlybXI0V4MruPos7qUo5fA==", + "version": "1.20.1", + "resolved": "https://registry.npmjs.org/es-abstract/-/es-abstract-1.20.1.tgz", + "integrity": "sha512-WEm2oBhfoI2sImeM4OF2zE2V3BYdSF+KnSi9Sidz51fQHd7+JuF8Xgcj9/0o+OWeIeIS/MiuNnlruQrJf16GQA==", "dev": true, "dependencies": { "call-bind": "^1.0.2", "es-to-primitive": "^1.2.1", "function-bind": "^1.1.1", + "function.prototype.name": "^1.1.5", "get-intrinsic": "^1.1.1", "get-symbol-description": "^1.0.0", "has": "^1.0.3", + "has-property-descriptors": "^1.0.0", "has-symbols": "^1.0.3", "internal-slot": "^1.0.3", "is-callable": "^1.2.4", @@ -2549,9 +2574,10 @@ "object-inspect": "^1.12.0", "object-keys": "^1.1.1", "object.assign": "^4.1.2", - "string.prototype.trimend": "^1.0.4", - "string.prototype.trimstart": "^1.0.4", - "unbox-primitive": "^1.0.1" + "regexp.prototype.flags": "^1.4.3", + "string.prototype.trimend": "^1.0.5", + "string.prototype.trimstart": "^1.0.5", + "unbox-primitive": "^1.0.2" }, "engines": { "node": ">= 0.4" @@ -2630,12 +2656,15 @@ } }, "node_modules/escape-string-regexp": { - "version": "1.0.5", - "resolved": "https://registry.npmjs.org/escape-string-regexp/-/escape-string-regexp-1.0.5.tgz", - "integrity": "sha1-G2HAViGQqN/2rjuyzwIAyhMLhtQ=", + "version": "5.0.0", + "resolved": "https://registry.npmjs.org/escape-string-regexp/-/escape-string-regexp-5.0.0.tgz", + "integrity": "sha512-/veY75JbMK4j1yjvuUxuVsiS/hr/4iHs9FTT6cgTexxdE0Ly/glccBAkloH/DofkjRbZU3bnoj38mOmhkZ0lHw==", "dev": true, "engines": { - "node": ">=0.8.0" + "node": ">=12" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" } }, "node_modules/escodegen": { @@ -2663,7 +2692,7 @@ "node_modules/escodegen/node_modules/levn": { "version": "0.3.0", "resolved": "https://registry.npmjs.org/levn/-/levn-0.3.0.tgz", - "integrity": "sha1-OwmSTt+fCDwEkP3UwLxEIeBHZO4=", + "integrity": "sha512-0OO4y2iOHix2W6ujICbKIaEQXvFQHue65vUG3pb5EUomzPI90z9hsA1VsO/dbIIpC53J8gxM9Q4Oho0jrCM/yA==", "dev": true, "dependencies": { "prelude-ls": "~1.1.2", @@ -2693,7 +2722,7 @@ "node_modules/escodegen/node_modules/prelude-ls": { "version": "1.1.2", "resolved": "https://registry.npmjs.org/prelude-ls/-/prelude-ls-1.1.2.tgz", - "integrity": "sha1-IZMqVJ9eUv/ZqCf1cOBL5iqX2lQ=", + "integrity": "sha512-ESF23V4SKG6lVSGZgYNpbsiaAkdab6ZgOxe52p7+Kid3W3u3bxR4Vfd/o21dmN7jSt0IwgZ4v5MUd26FEtXE9w==", "dev": true, "engines": { "node": ">= 0.8.0" @@ -2702,7 +2731,7 @@ "node_modules/escodegen/node_modules/type-check": { "version": "0.3.2", "resolved": "https://registry.npmjs.org/type-check/-/type-check-0.3.2.tgz", - "integrity": "sha1-WITKtRLPHTVeP7eE8wgEsrUg23I=", + "integrity": "sha512-ZCmOJdvOWDBYJlzAoFkC+Q0+bUyEOS1ltgp1MGU03fqHG+dbi9tBFU2Rd9QKiDZFAYrhPh2JUf7rZRIuHRKtOg==", "dev": true, "dependencies": { "prelude-ls": "~1.1.2" @@ -2812,6 +2841,30 @@ "node": "^12.22.0 || ^14.17.0 || >=16.0.0" } }, + "node_modules/eslint/node_modules/ansi-regex": { + "version": "5.0.1", + "resolved": "https://registry.npmjs.org/ansi-regex/-/ansi-regex-5.0.1.tgz", + "integrity": "sha512-quJQXlTSUGL2LH9SUXo8VwsY4soanhgo6LNSm84E1LBcE8s3O0wpdiRzyR9z/ZZJMlMWv37qOOb9pdJlMUEKFQ==", + "dev": true, + "engines": { + "node": ">=8" + } + }, + "node_modules/eslint/node_modules/ansi-styles": { + "version": "4.3.0", + "resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-4.3.0.tgz", + "integrity": "sha512-zbB9rCJAT1rbjiVDb2hqKFHNYLxgtk8NURxZ3IZwD3F6NtxbXZQCnnSi1Lkx+IDohdPlFp222wVALIheZJQSEg==", + "dev": true, + "dependencies": { + "color-convert": "^2.0.1" + }, + "engines": { + "node": ">=8" + }, + "funding": { + "url": "https://github.com/chalk/ansi-styles?sponsor=1" + } + }, "node_modules/eslint/node_modules/chalk": { "version": "4.1.2", "resolved": "https://registry.npmjs.org/chalk/-/chalk-4.1.2.tgz", @@ -2862,6 +2915,18 @@ "node": ">=4.0" } }, + "node_modules/eslint/node_modules/strip-ansi": { + "version": "6.0.1", + "resolved": "https://registry.npmjs.org/strip-ansi/-/strip-ansi-6.0.1.tgz", + "integrity": "sha512-Y38VPSHcqkFrCpFnQ9vuSXmquuv5oXOKpGeT6aGrr3o3Gc9AlVa6JBfUSOCnbxGGZF+/0ooI7KrPuUSztUdU5A==", + "dev": true, + "dependencies": { + "ansi-regex": "^5.0.1" + }, + "engines": { + "node": ">=8" + } + }, "node_modules/espree": { "version": "9.3.2", "resolved": "https://registry.npmjs.org/espree/-/espree-9.3.2.tgz", @@ -3029,7 +3094,7 @@ "node_modules/fast-levenshtein": { "version": "2.0.6", "resolved": "https://registry.npmjs.org/fast-levenshtein/-/fast-levenshtein-2.0.6.tgz", - "integrity": "sha1-PYpcZog6FqMMqGQ+hR8Zuqd5eRc=", + "integrity": "sha512-DCXu6Ifhqcks7TZKY3Hxp3y6qphY5SJZmrWMDrKcERSOXWQdMhU9Ig/PYrzyw/ul9jOIyh0N4M0tbC5hodg8dw==", "dev": true }, "node_modules/fastq": { @@ -3057,18 +3122,6 @@ "url": "https://github.com/sponsors/sindresorhus" } }, - "node_modules/figures/node_modules/escape-string-regexp": { - "version": "5.0.0", - "resolved": "https://registry.npmjs.org/escape-string-regexp/-/escape-string-regexp-5.0.0.tgz", - "integrity": "sha512-/veY75JbMK4j1yjvuUxuVsiS/hr/4iHs9FTT6cgTexxdE0Ly/glccBAkloH/DofkjRbZU3bnoj38mOmhkZ0lHw==", - "dev": true, - "engines": { - "node": ">=12" - }, - "funding": { - "url": "https://github.com/sponsors/sindresorhus" - } - }, "node_modules/file-entry-cache": { "version": "6.0.1", "resolved": "https://registry.npmjs.org/file-entry-cache/-/file-entry-cache-6.0.1.tgz", @@ -3093,7 +3146,7 @@ "node_modules/filename-reserved-regex": { "version": "2.0.0", "resolved": "https://registry.npmjs.org/filename-reserved-regex/-/filename-reserved-regex-2.0.0.tgz", - "integrity": "sha1-q/c9+rc10EVECr/qLZHzieu/oik=", + "integrity": "sha512-lc1bnsSr4L4Bdif8Xb/qrtokGbq5zlsms/CYH8PP+WtCkGNF65DPiQY8vG3SakEdRn8Dlnm+gW/qWKKjS5sZzQ==", "dev": true, "engines": { "node": ">=4" @@ -3131,7 +3184,7 @@ "node_modules/filter-obj": { "version": "1.1.0", "resolved": "https://registry.npmjs.org/filter-obj/-/filter-obj-1.1.0.tgz", - "integrity": "sha1-mzERErxsYSehbgFsbF1/GeCAXFs=", + "integrity": "sha512-8rXg1ZnX7xzy2NGDVkBVaAy+lSlPNwad13BtgSlLuxfIslyt5Vg64U7tFcCt4WS1R0hvtnQybT/IyCkGZ3DpXQ==", "dev": true, "engines": { "node": ">=0.10.0" @@ -3190,9 +3243,9 @@ } }, "node_modules/flatted": { - "version": "3.2.5", - "resolved": "https://registry.npmjs.org/flatted/-/flatted-3.2.5.tgz", - "integrity": "sha512-WIWGi2L3DyTUvUrwRKgGi9TwxQMUEqPOPQBVi71R96jZXJdFskXEmf54BoZaS1kknGODoIGASGEzBUYdyMCBJg==", + "version": "3.2.6", + "resolved": "https://registry.npmjs.org/flatted/-/flatted-3.2.6.tgz", + "integrity": "sha512-0sQoMh9s0BYsm+12Huy/rkKxVu4R1+r96YX5cG44rHV0pQ6iC3Q+mkoMFaGWObMFYQxCVT+ssG1ksneA2MI9KQ==", "dev": true }, "node_modules/foreground-child": { @@ -3265,13 +3318,27 @@ "node_modules/fs.realpath": { "version": "1.0.0", "resolved": "https://registry.npmjs.org/fs.realpath/-/fs.realpath-1.0.0.tgz", - "integrity": "sha1-FQStJSMVjKpA20onh8sBQRmU6k8=", + "integrity": "sha512-OO0pH2lK6a0hZnAdau5ItzHPI6pUlvI7jMVnxUQRtw4owF2wk8lOSabtGDCTP4Ggrg2MbGnWO9X8K1t4+fGMDw==", "dev": true }, + "node_modules/fsevents": { + "version": "2.3.2", + "resolved": "https://registry.npmjs.org/fsevents/-/fsevents-2.3.2.tgz", + "integrity": "sha512-xiqMQR4xAeHTuB9uWm+fFRcIOgKBMiOBP+eXiyT7jsgVCq1bkVygt00oASowB7EdtpOHaaPgKt812P9ab+DDKA==", + "dev": true, + "hasInstallScript": true, + "optional": true, + "os": [ + "darwin" + ], + "engines": { + "node": "^8.16.0 || ^10.6.0 || >=11.0.0" + } + }, "node_modules/ftp": { "version": "0.3.10", "resolved": "https://registry.npmjs.org/ftp/-/ftp-0.3.10.tgz", - "integrity": "sha1-kZfYYa2BQvPmPVqDv+TFn3MwiF0=", + "integrity": "sha512-faFVML1aBx2UoDStmLwv2Wptt4vw5x03xxX172nhA5Y5HBshW5JweqQ2W4xL4dezQTG8inJsuYcpPHHU3X5OTQ==", "dev": true, "dependencies": { "readable-stream": "1.1.x", @@ -3284,13 +3351,13 @@ "node_modules/ftp/node_modules/isarray": { "version": "0.0.1", "resolved": "https://registry.npmjs.org/isarray/-/isarray-0.0.1.tgz", - "integrity": "sha1-ihis/Kmo9Bd+Cav8YDiTmwXR7t8=", + "integrity": "sha512-D2S+3GLxWH+uhrNEcoh/fnmYeP8E8/zHl644d/jdA0g2uyXvy3sb0qxotE+ne0LtccHknQzWwZEzhak7oJ0COQ==", "dev": true }, "node_modules/ftp/node_modules/readable-stream": { "version": "1.1.14", "resolved": "https://registry.npmjs.org/readable-stream/-/readable-stream-1.1.14.tgz", - "integrity": "sha1-fPTFTvZI44EwhMY23SB54WbAgdk=", + "integrity": "sha512-+MeVjFf4L44XUkhM1eYbD8fyEsxcV81pqMSR5gblfcLCHfZvbrqy4/qYHE+/R5HoBUT11WV5O08Cr1n3YXkWVQ==", "dev": true, "dependencies": { "core-util-is": "~1.0.0", @@ -3302,7 +3369,7 @@ "node_modules/ftp/node_modules/string_decoder": { "version": "0.10.31", "resolved": "https://registry.npmjs.org/string_decoder/-/string_decoder-0.10.31.tgz", - "integrity": "sha1-YuIDvEF2bGwoyfyEMB2rHFMQ+pQ=", + "integrity": "sha512-ev2QzSzWPYmy9GuqfIVildA4OdcGLeFZQrq5ys6RtiuF+RQQiZWr8TZNyAcuVXyQRYfEO+MsoB/1BuQVhOJuoQ==", "dev": true }, "node_modules/function-bind": { @@ -3311,12 +3378,39 @@ "integrity": "sha512-yIovAzMX49sF8Yl58fSCWJ5svSLuaibPxXQJFLmBObTuCr0Mf1KiPopGM9NiFjiYBCbfaa2Fh6breQ6ANVTI0A==", "dev": true }, + "node_modules/function.prototype.name": { + "version": "1.1.5", + "resolved": "https://registry.npmjs.org/function.prototype.name/-/function.prototype.name-1.1.5.tgz", + "integrity": "sha512-uN7m/BzVKQnCUF/iW8jYea67v++2u7m5UgENbHRtdDVclOUP+FMPlCNdmk0h/ysGyo2tavMJEDqJAkJdRa1vMA==", + "dev": true, + "dependencies": { + "call-bind": "^1.0.2", + "define-properties": "^1.1.3", + "es-abstract": "^1.19.0", + "functions-have-names": "^1.2.2" + }, + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, "node_modules/functional-red-black-tree": { "version": "1.0.1", "resolved": "https://registry.npmjs.org/functional-red-black-tree/-/functional-red-black-tree-1.0.1.tgz", - "integrity": "sha1-GwqzvVU7Kg1jmdKcDj6gslIHgyc=", + "integrity": "sha512-dsKNQNdj6xA3T+QlADDA7mOSlX0qiMINjn0cgr+eGHGsbSHzTabcIogz2+p/iqP1Xs6EP/sS2SbqH+brGTbq0g==", "dev": true }, + "node_modules/functions-have-names": { + "version": "1.2.3", + "resolved": "https://registry.npmjs.org/functions-have-names/-/functions-have-names-1.2.3.tgz", + "integrity": "sha512-xckBUXyTIqT97tq2x2AMb+g163b5JFysYk0x4qxNFwbfQkmNZoiRHb6sPzI9/QV33WeuvVYBUIiD4NzNIyqaRQ==", + "dev": true, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, "node_modules/generic-pool": { "version": "3.8.2", "resolved": "https://registry.npmjs.org/generic-pool/-/generic-pool-3.8.2.tgz", @@ -3344,14 +3438,14 @@ } }, "node_modules/get-intrinsic": { - "version": "1.1.1", - "resolved": "https://registry.npmjs.org/get-intrinsic/-/get-intrinsic-1.1.1.tgz", - "integrity": "sha512-kWZrnVM42QCiEA2Ig1bG8zjoIMOgxWwYCEeNdwY6Tv/cOSeGpcoX4pXHfKUxNKVoArnrEr2e9srnAxxGIraS9Q==", + "version": "1.1.2", + "resolved": "https://registry.npmjs.org/get-intrinsic/-/get-intrinsic-1.1.2.tgz", + "integrity": "sha512-Jfm3OyCxHh9DJyc28qGk+JmfkpO41A4XkneDSujN9MDXrm4oDKdHvndhZ2dN94+ERNfkYJWDclW6k2L/ZGHjXA==", "dev": true, "dependencies": { "function-bind": "^1.1.1", "has": "^1.0.3", - "has-symbols": "^1.0.1" + "has-symbols": "^1.0.3" }, "funding": { "url": "https://github.com/sponsors/ljharb" @@ -3412,9 +3506,9 @@ } }, "node_modules/gh-pages": { - "version": "3.2.3", - "resolved": "https://registry.npmjs.org/gh-pages/-/gh-pages-3.2.3.tgz", - "integrity": "sha512-jA1PbapQ1jqzacECfjUaO9gV8uBgU6XNMV0oXLtfCX3haGLe5Atq8BxlrADhbD6/UdG9j6tZLWAkAybndOXTJg==", + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/gh-pages/-/gh-pages-4.0.0.tgz", + "integrity": "sha512-p8S0T3aGJc68MtwOcZusul5qPSNZCalap3NWbhRUZYu1YOdp+EjZ+4kPmRM8h3NNRdqw00yuevRjlkuSzCn7iQ==", "dev": true, "dependencies": { "async": "^2.6.1", @@ -3453,15 +3547,15 @@ } }, "node_modules/glob": { - "version": "7.2.0", - "resolved": "https://registry.npmjs.org/glob/-/glob-7.2.0.tgz", - "integrity": "sha512-lmLf6gtyrPq8tTjSmrO94wBeQbFR3HbLHbuyD69wuyQkImp2hWqMGB47OX65FBkPffO641IP9jWa1z4ivqG26Q==", + "version": "7.2.3", + "resolved": "https://registry.npmjs.org/glob/-/glob-7.2.3.tgz", + "integrity": "sha512-nFR0zLpU2YCaRxwoCJvL6UvCH2JFyFVIvwTLsIf21AuHlMskA1hhTdk+LlYJtOlYt9v6dvszD2BGRqBL+iQK9Q==", "dev": true, "dependencies": { "fs.realpath": "^1.0.0", "inflight": "^1.0.4", "inherits": "2", - "minimatch": "^3.0.4", + "minimatch": "^3.1.1", "once": "^1.3.0", "path-is-absolute": "^1.0.0" }, @@ -3514,10 +3608,22 @@ "url": "https://github.com/sponsors/sindresorhus" } }, + "node_modules/globals/node_modules/type-fest": { + "version": "0.20.2", + "resolved": "https://registry.npmjs.org/type-fest/-/type-fest-0.20.2.tgz", + "integrity": "sha512-Ne+eE4r0/iWnpAxD852z3A+N0Bt5RN//NjJwRd2VFHEmrywxf5vsZlh4R6lixl6B+wz/8d+maTSAkN1FIkI3LQ==", + "dev": true, + "engines": { + "node": ">=10" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, "node_modules/globby": { "version": "6.1.0", "resolved": "https://registry.npmjs.org/globby/-/globby-6.1.0.tgz", - "integrity": "sha1-9abXDoOV4hyFj7BInWTfAkJNUGw=", + "integrity": "sha512-KVbFv2TQtbzCoxAnfD6JcHZTYCzyliEaaeM/gH8qQdkKr5s0OP9scEgvdcngyk7AVdY6YVW/TJHd+lQ/Df3Daw==", "dev": true, "dependencies": { "array-union": "^1.0.1", @@ -3836,7 +3942,7 @@ "node_modules/imurmurhash": { "version": "0.1.4", "resolved": "https://registry.npmjs.org/imurmurhash/-/imurmurhash-0.1.4.tgz", - "integrity": "sha1-khi5srkoojixPcT7a21XbyMUU+o=", + "integrity": "sha512-JmXMZ6wuvDmLiHEml9ykzqO6lwFbof0GG4IkcGaENdCRDDmMVnny7s5HsIgHCbaq0w2MyPhDqkhTUgS2LU2PHA==", "dev": true, "engines": { "node": ">=0.8.19" @@ -3854,7 +3960,7 @@ "node_modules/inflight": { "version": "1.0.6", "resolved": "https://registry.npmjs.org/inflight/-/inflight-1.0.6.tgz", - "integrity": "sha1-Sb1jMdfQLQwJvJEKEHW6gWW1bfk=", + "integrity": "sha512-k92I/b08q4wvFscXCLvqfsHCrjrF7yiXsQuIVvVE7N82W3+aqpzuUdBbfhWcy/FZR3/4IgflMgKLOsvPDrGCJA==", "dev": true, "dependencies": { "once": "^1.3.0", @@ -3902,85 +4008,6 @@ "node": ">=12.0.0" } }, - "node_modules/inquirer/node_modules/ansi-regex": { - "version": "6.0.1", - "resolved": "https://registry.npmjs.org/ansi-regex/-/ansi-regex-6.0.1.tgz", - "integrity": "sha512-n5M855fKb2SsfMIiFFoVrABHJC8QtHwVx+mHWP3QcEqBHYienj5dHSgjbxtC0WEZXYt4wcD6zrQElDPhFuZgfA==", - "dev": true, - "engines": { - "node": ">=12" - }, - "funding": { - "url": "https://github.com/chalk/ansi-regex?sponsor=1" - } - }, - "node_modules/inquirer/node_modules/ansi-styles": { - "version": "6.1.0", - "resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-6.1.0.tgz", - "integrity": "sha512-VbqNsoz55SYGczauuup0MFUyXNQviSpFTj1RQtFzmQLk18qbVSpTFFGMT293rmDaQuKCT6InmbuEyUne4mTuxQ==", - "dev": true, - "engines": { - "node": ">=12" - }, - "funding": { - "url": "https://github.com/chalk/ansi-styles?sponsor=1" - } - }, - "node_modules/inquirer/node_modules/emoji-regex": { - "version": "9.2.2", - "resolved": "https://registry.npmjs.org/emoji-regex/-/emoji-regex-9.2.2.tgz", - "integrity": "sha512-L18DaJsXSUk2+42pv8mLs5jJT2hqFkFE4j21wOmgbUqsZ2hL72NsUU785g9RXgo3s0ZNgVl42TiHp3ZtOv/Vyg==", - "dev": true - }, - "node_modules/inquirer/node_modules/string-width": { - "version": "5.1.2", - "resolved": "https://registry.npmjs.org/string-width/-/string-width-5.1.2.tgz", - "integrity": "sha512-HnLOCR3vjcY8beoNLtcjZ5/nxn2afmME6lhrDrebokqMap+XbeW8n9TXpPDOqdGK5qcI3oT0GKTW6wC7EMiVqA==", - "dev": true, - "dependencies": { - "eastasianwidth": "^0.2.0", - "emoji-regex": "^9.2.2", - "strip-ansi": "^7.0.1" - }, - "engines": { - "node": ">=12" - }, - "funding": { - "url": "https://github.com/sponsors/sindresorhus" - } - }, - "node_modules/inquirer/node_modules/strip-ansi": { - "version": "7.0.1", - "resolved": "https://registry.npmjs.org/strip-ansi/-/strip-ansi-7.0.1.tgz", - "integrity": "sha512-cXNxvT8dFNRVfhVME3JAe98mkXDYN2O1l7jmcwMnOslDeESg1rF/OZMtK0nRAhiari1unG5cD4jG3rapUAkLbw==", - "dev": true, - "dependencies": { - "ansi-regex": "^6.0.1" - }, - "engines": { - "node": ">=12" - }, - "funding": { - "url": "https://github.com/chalk/strip-ansi?sponsor=1" - } - }, - "node_modules/inquirer/node_modules/wrap-ansi": { - "version": "8.0.1", - "resolved": "https://registry.npmjs.org/wrap-ansi/-/wrap-ansi-8.0.1.tgz", - "integrity": "sha512-QFF+ufAqhoYHvoHdajT/Po7KoXVBPXS2bgjIam5isfWJPfIOnQZ50JtUiVvCv/sjgacf3yRrt2ZKUZ/V4itN4g==", - "dev": true, - "dependencies": { - "ansi-styles": "^6.1.0", - "string-width": "^5.0.1", - "strip-ansi": "^7.0.1" - }, - "engines": { - "node": ">=12" - }, - "funding": { - "url": "https://github.com/chalk/wrap-ansi?sponsor=1" - } - }, "node_modules/internal-slot": { "version": "1.0.3", "resolved": "https://registry.npmjs.org/internal-slot/-/internal-slot-1.0.3.tgz", @@ -4005,9 +4032,9 @@ } }, "node_modules/ip": { - "version": "1.1.5", - "resolved": "https://registry.npmjs.org/ip/-/ip-1.1.5.tgz", - "integrity": "sha1-vd7XARQpCCjAoDnnLvJfWq7ENUo=", + "version": "1.1.8", + "resolved": "https://registry.npmjs.org/ip/-/ip-1.1.8.tgz", + "integrity": "sha512-PuExPYUiu6qMBQb4l06ecm6T6ujzhmh+MeJcW9wa89PoAz5pvd4zPgN5WJV104mb6S2T1AwNIAaB70JNrLQWhg==", "dev": true }, "node_modules/is-arguments": { @@ -4029,7 +4056,7 @@ "node_modules/is-arrayish": { "version": "0.2.1", "resolved": "https://registry.npmjs.org/is-arrayish/-/is-arrayish-0.2.1.tgz", - "integrity": "sha1-d8mYQFJ6qOyxqLppe4BkWnqSap0=", + "integrity": "sha512-zz06S8t0ozoDXMG+ube26zeCTNXcKIPJZJi8hBrF4idCLms4CG9QtK7qBl1boi5ODzFpjswb5JPmHCbMpjaYzg==", "dev": true }, "node_modules/is-bigint": { @@ -4141,7 +4168,7 @@ "node_modules/is-extglob": { "version": "2.1.1", "resolved": "https://registry.npmjs.org/is-extglob/-/is-extglob-2.1.1.tgz", - "integrity": "sha1-qIwCU1eR8C7TfHahueqXc8gz+MI=", + "integrity": "sha512-SbKbANkN603Vi4jEZv49LeVJMn4yGwsbzZworEoyEiutsN3nJYdbO36zfhGJ6QEDpOZIFkDtnq5JRxmvl3jsoQ==", "dev": true, "engines": { "node": ">=0.10.0" @@ -4327,12 +4354,12 @@ } }, "node_modules/is-ssh": { - "version": "1.3.3", - "resolved": "https://registry.npmjs.org/is-ssh/-/is-ssh-1.3.3.tgz", - "integrity": "sha512-NKzJmQzJfEEma3w5cJNcUMxoXfDjz0Zj0eyCalHn2E6VOwlzjZo0yuO2fcBSf8zhFuVCL/82/r5gRcoi6aEPVQ==", + "version": "1.4.0", + "resolved": "https://registry.npmjs.org/is-ssh/-/is-ssh-1.4.0.tgz", + "integrity": "sha512-x7+VxdxOdlV3CYpjvRLBv5Lo9OJerlYanjwFrPR9fuGPjCiNiCzFgAWpiLAohSbsnH4ZAys3SBh+hq5rJosxUQ==", "dev": true, "dependencies": { - "protocols": "^1.1.0" + "protocols": "^2.0.1" } }, "node_modules/is-stream": { @@ -4380,7 +4407,7 @@ "node_modules/is-typedarray": { "version": "1.0.0", "resolved": "https://registry.npmjs.org/is-typedarray/-/is-typedarray-1.0.0.tgz", - "integrity": "sha1-5HnICFjfDBsR3dppQPlgEfzaSpo=", + "integrity": "sha512-cyA56iCMHAh5CdzjJIa4aohJyeO1YbwLi3Jc35MmRU6poroFjIGZzUzupGiRPOjgHg9TLu43xbpwXk523fMxKA==", "dev": true }, "node_modules/is-unicode-supported": { @@ -4446,7 +4473,7 @@ "node_modules/isexe": { "version": "2.0.0", "resolved": "https://registry.npmjs.org/isexe/-/isexe-2.0.0.tgz", - "integrity": "sha1-6PvzdNxVb/iUehDcsFctYz8s+hA=", + "integrity": "sha512-RHxMLp9lnKHGHRng9QFhRCMbYAcVpn69smSGcq3f36xjgVVWThj4qqLbTLlq7Ssj8B+fIQ1EuCEGI2lKsyQeIw==", "dev": true }, "node_modules/istanbul-lib-coverage": { @@ -4486,18 +4513,17 @@ } }, "node_modules/istanbul-lib-processinfo": { - "version": "2.0.2", - "resolved": "https://registry.npmjs.org/istanbul-lib-processinfo/-/istanbul-lib-processinfo-2.0.2.tgz", - "integrity": "sha512-kOwpa7z9hme+IBPZMzQ5vdQj8srYgAtaRqeI48NGmAQ+/5yKiHLV0QbYqQpxsdEF0+w14SoB8YbnHKcXE2KnYw==", + "version": "2.0.3", + "resolved": "https://registry.npmjs.org/istanbul-lib-processinfo/-/istanbul-lib-processinfo-2.0.3.tgz", + "integrity": "sha512-NkwHbo3E00oybX6NGJi6ar0B29vxyvNwoC7eJ4G4Yq28UfY758Hgn/heV8VRFhevPED4LXfFz0DQ8z/0kw9zMg==", "dev": true, "dependencies": { "archy": "^1.0.0", - "cross-spawn": "^7.0.0", - "istanbul-lib-coverage": "^3.0.0-alpha.1", - "make-dir": "^3.0.0", + "cross-spawn": "^7.0.3", + "istanbul-lib-coverage": "^3.2.0", "p-map": "^3.0.0", "rimraf": "^3.0.0", - "uuid": "^3.3.3" + "uuid": "^8.3.2" }, "engines": { "node": ">=8" @@ -4617,7 +4643,7 @@ "node_modules/json-stable-stringify-without-jsonify": { "version": "1.0.1", "resolved": "https://registry.npmjs.org/json-stable-stringify-without-jsonify/-/json-stable-stringify-without-jsonify-1.0.1.tgz", - "integrity": "sha1-nbe1lJatPzz+8wp1FC0tkwrXJlE=", + "integrity": "sha512-Bdboy+l7tA3OGW6FjyFHWkP5LuByj1Tk33Ljyq0axyzdk9//JSi2u3fP1QSmd1KNwq6VOKYGlAu87CisVir6Pw==", "dev": true }, "node_modules/json5": { @@ -4641,7 +4667,7 @@ "node_modules/jsonfile": { "version": "4.0.0", "resolved": "https://registry.npmjs.org/jsonfile/-/jsonfile-4.0.0.tgz", - "integrity": "sha1-h3Gq4HmbZAdrdmQPygWPnBDjPss=", + "integrity": "sha512-m6F1R3z8jjlf2imQHS2Qez5sjKWQzbuuhuJ/FKYFRZvPE3PuHcSMVZzfsLhGVOkfd20obL5SWEBew5ShlquNxg==", "dev": true, "optionalDependencies": { "graceful-fs": "^4.1.6" @@ -4718,13 +4744,13 @@ "node_modules/lodash.flattendeep": { "version": "4.4.0", "resolved": "https://registry.npmjs.org/lodash.flattendeep/-/lodash.flattendeep-4.4.0.tgz", - "integrity": "sha1-+wMJF/hqMTTlvJvsDWngAT3f7bI=", + "integrity": "sha512-uHaJFihxmJcEX3kT4I23ABqKKalJ/zDrDg0lsFtc1h+3uw49SIJ5beyhx5ExVRti3AvKoOJngIj7xz3oylPdWQ==", "dev": true }, "node_modules/lodash.get": { "version": "4.4.2", "resolved": "https://registry.npmjs.org/lodash.get/-/lodash.get-4.4.2.tgz", - "integrity": "sha1-LRd/ZS+jHpObRDjVNBSZ36OCXpk=", + "integrity": "sha512-z+Uw/vLuy6gQe8cfaFWD7p0wVv8fJl3mbzXh33RS+0oW2wvUqiRXiQ69gLWSLpgB5/6sU+r6BlQR0MBILadqTQ==", "dev": true }, "node_modules/lodash.merge": { @@ -4777,9 +4803,9 @@ "dev": true }, "node_modules/macos-release": { - "version": "3.0.1", - "resolved": "https://registry.npmjs.org/macos-release/-/macos-release-3.0.1.tgz", - "integrity": "sha512-3l6OrhdDg2H2SigtuN3jBh+5dRJRWxNKuJTPBbGeNJTsmt/pj9PO25wYaNb05NuNmAsl435j4rDP6rgNXz7s7g==", + "version": "3.1.0", + "resolved": "https://registry.npmjs.org/macos-release/-/macos-release-3.1.0.tgz", + "integrity": "sha512-/M/R0gCDgM+Cv1IuBG1XGdfTFnMEG6PZeT+KGWHO/OG+imqmaD9CH5vHBTycEM3+Kc4uG2Il+tFAuUWLqQOeUA==", "dev": true, "engines": { "node": "^12.20.0 || ^14.13.1 || >=16.0.0" @@ -4950,13 +4976,28 @@ "url": "https://opencollective.com/mochajs" } }, - "node_modules/mocha/node_modules/brace-expansion": { - "version": "2.0.1", - "resolved": "https://registry.npmjs.org/brace-expansion/-/brace-expansion-2.0.1.tgz", - "integrity": "sha512-XnAIvQ8eM+kC6aULx6wuQiwVsnzsi9d3WxzV3FpWTGA19F621kwdbsAcFKXgKUHZWsy+mY6iL1sHTxWEFCytDA==", + "node_modules/mocha/node_modules/ansi-regex": { + "version": "5.0.1", + "resolved": "https://registry.npmjs.org/ansi-regex/-/ansi-regex-5.0.1.tgz", + "integrity": "sha512-quJQXlTSUGL2LH9SUXo8VwsY4soanhgo6LNSm84E1LBcE8s3O0wpdiRzyR9z/ZZJMlMWv37qOOb9pdJlMUEKFQ==", + "dev": true, + "engines": { + "node": ">=8" + } + }, + "node_modules/mocha/node_modules/ansi-styles": { + "version": "4.3.0", + "resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-4.3.0.tgz", + "integrity": "sha512-zbB9rCJAT1rbjiVDb2hqKFHNYLxgtk8NURxZ3IZwD3F6NtxbXZQCnnSi1Lkx+IDohdPlFp222wVALIheZJQSEg==", "dev": true, "dependencies": { - "balanced-match": "^1.0.0" + "color-convert": "^2.0.1" + }, + "engines": { + "node": ">=8" + }, + "funding": { + "url": "https://github.com/chalk/ansi-styles?sponsor=1" } }, "node_modules/mocha/node_modules/chalk": { @@ -4998,6 +5039,12 @@ "wrap-ansi": "^7.0.0" } }, + "node_modules/mocha/node_modules/emoji-regex": { + "version": "8.0.0", + "resolved": "https://registry.npmjs.org/emoji-regex/-/emoji-regex-8.0.0.tgz", + "integrity": "sha512-MSjYzcWNOA0ewAHpz0MxpYFvwg6yjy1NG3xteoqz644VCo/RPgnr1/GGt+ic3iJTzQ8Eu3TdM14SawnVUmGE6A==", + "dev": true + }, "node_modules/mocha/node_modules/escape-string-regexp": { "version": "4.0.0", "resolved": "https://registry.npmjs.org/escape-string-regexp/-/escape-string-regexp-4.0.0.tgz", @@ -5026,6 +5073,38 @@ "url": "https://github.com/sponsors/sindresorhus" } }, + "node_modules/mocha/node_modules/glob": { + "version": "7.2.0", + "resolved": "https://registry.npmjs.org/glob/-/glob-7.2.0.tgz", + "integrity": "sha512-lmLf6gtyrPq8tTjSmrO94wBeQbFR3HbLHbuyD69wuyQkImp2hWqMGB47OX65FBkPffO641IP9jWa1z4ivqG26Q==", + "dev": true, + "dependencies": { + "fs.realpath": "^1.0.0", + "inflight": "^1.0.4", + "inherits": "2", + "minimatch": "^3.0.4", + "once": "^1.3.0", + "path-is-absolute": "^1.0.0" + }, + "engines": { + "node": "*" + }, + "funding": { + "url": "https://github.com/sponsors/isaacs" + } + }, + "node_modules/mocha/node_modules/glob/node_modules/minimatch": { + "version": "3.1.2", + "resolved": "https://registry.npmjs.org/minimatch/-/minimatch-3.1.2.tgz", + "integrity": "sha512-J7p63hRiAjw1NDEww1W7i37+ByIrOWO5XQQAzZ3VOcL0PNybwpfmV/N05zFAzwQ9USyEcX6t3UO+K5aqBQOIHw==", + "dev": true, + "dependencies": { + "brace-expansion": "^1.1.7" + }, + "engines": { + "node": "*" + } + }, "node_modules/mocha/node_modules/is-unicode-supported": { "version": "0.1.0", "resolved": "https://registry.npmjs.org/is-unicode-supported/-/is-unicode-supported-0.1.0.tgz", @@ -5081,6 +5160,15 @@ "node": ">=10" } }, + "node_modules/mocha/node_modules/minimatch/node_modules/brace-expansion": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/brace-expansion/-/brace-expansion-2.0.1.tgz", + "integrity": "sha512-XnAIvQ8eM+kC6aULx6wuQiwVsnzsi9d3WxzV3FpWTGA19F621kwdbsAcFKXgKUHZWsy+mY6iL1sHTxWEFCytDA==", + "dev": true, + "dependencies": { + "balanced-match": "^1.0.0" + } + }, "node_modules/mocha/node_modules/ms": { "version": "2.1.3", "resolved": "https://registry.npmjs.org/ms/-/ms-2.1.3.tgz", @@ -5117,13 +5205,39 @@ "url": "https://github.com/sponsors/sindresorhus" } }, - "node_modules/mocha/node_modules/supports-color": { - "version": "8.1.1", - "resolved": "https://registry.npmjs.org/supports-color/-/supports-color-8.1.1.tgz", - "integrity": "sha512-MpUEN2OodtUzxvKQl72cUF7RQ5EiHsGvSsVG0ia9c5RbWGL2CI4C7EpPS8UTBIplnlzZiNuV56w+FuNxy3ty2Q==", + "node_modules/mocha/node_modules/string-width": { + "version": "4.2.3", + "resolved": "https://registry.npmjs.org/string-width/-/string-width-4.2.3.tgz", + "integrity": "sha512-wKyQRQpjJ0sIp62ErSZdGsjMJWsap5oRNihHhu6G7JVO/9jIB6UyevL+tXuOqrng8j/cxKTWyWUwvSTriiZz/g==", "dev": true, "dependencies": { - "has-flag": "^4.0.0" + "emoji-regex": "^8.0.0", + "is-fullwidth-code-point": "^3.0.0", + "strip-ansi": "^6.0.1" + }, + "engines": { + "node": ">=8" + } + }, + "node_modules/mocha/node_modules/strip-ansi": { + "version": "6.0.1", + "resolved": "https://registry.npmjs.org/strip-ansi/-/strip-ansi-6.0.1.tgz", + "integrity": "sha512-Y38VPSHcqkFrCpFnQ9vuSXmquuv5oXOKpGeT6aGrr3o3Gc9AlVa6JBfUSOCnbxGGZF+/0ooI7KrPuUSztUdU5A==", + "dev": true, + "dependencies": { + "ansi-regex": "^5.0.1" + }, + "engines": { + "node": ">=8" + } + }, + "node_modules/mocha/node_modules/supports-color": { + "version": "8.1.1", + "resolved": "https://registry.npmjs.org/supports-color/-/supports-color-8.1.1.tgz", + "integrity": "sha512-MpUEN2OodtUzxvKQl72cUF7RQ5EiHsGvSsVG0ia9c5RbWGL2CI4C7EpPS8UTBIplnlzZiNuV56w+FuNxy3ty2Q==", + "dev": true, + "dependencies": { + "has-flag": "^4.0.0" }, "engines": { "node": ">=10" @@ -5132,6 +5246,23 @@ "url": "https://github.com/chalk/supports-color?sponsor=1" } }, + "node_modules/mocha/node_modules/wrap-ansi": { + "version": "7.0.0", + "resolved": "https://registry.npmjs.org/wrap-ansi/-/wrap-ansi-7.0.0.tgz", + "integrity": "sha512-YVGIj2kamLSTxw6NsZjoBxfSwsn0ycdesmc4p+Q21c5zPuZ1pl+NfxVdxPtdHvmNVOQ6XSYG4AUtyt/Fi7D16Q==", + "dev": true, + "dependencies": { + "ansi-styles": "^4.0.0", + "string-width": "^4.1.0", + "strip-ansi": "^6.0.0" + }, + "engines": { + "node": ">=10" + }, + "funding": { + "url": "https://github.com/chalk/wrap-ansi?sponsor=1" + } + }, "node_modules/mocha/node_modules/y18n": { "version": "5.0.8", "resolved": "https://registry.npmjs.org/y18n/-/y18n-5.0.8.tgz", @@ -5195,7 +5326,7 @@ "node_modules/natural-compare": { "version": "1.4.0", "resolved": "https://registry.npmjs.org/natural-compare/-/natural-compare-1.4.0.tgz", - "integrity": "sha1-Sr6/7tdUHywnrPspvbvRXI1bpPc=", + "integrity": "sha512-OWND8ei3VtNC9h7V60qff3SVobHr996CTwgxubgyQYEpg290h9J0buyECNNJexkFm5sOajh5G116RYA1c8ZMSw==", "dev": true }, "node_modules/netmask": { @@ -5223,9 +5354,9 @@ } }, "node_modules/new-github-release-url/node_modules/type-fest": { - "version": "2.12.2", - "resolved": "https://registry.npmjs.org/type-fest/-/type-fest-2.12.2.tgz", - "integrity": "sha512-qt6ylCGpLjZ7AaODxbpyBZSs9fCI9SkL3Z9q2oxMBQhs/uyY+VD8jHA8ULCGmWQJlBgqvO3EJeAngOHD8zQCrQ==", + "version": "2.15.0", + "resolved": "https://registry.npmjs.org/type-fest/-/type-fest-2.15.0.tgz", + "integrity": "sha512-hpsXfQZrAiusX8KMY5HXSEV7xqGAGxFQoNDT+iW0yJE/bdYG0uMlRaUG0kNAUbF5p6Cq5Kuf69lm4M569QtRGw==", "dev": true, "engines": { "node": ">=12.20" @@ -5280,9 +5411,9 @@ } }, "node_modules/node-releases": { - "version": "2.0.4", - "resolved": "https://registry.npmjs.org/node-releases/-/node-releases-2.0.4.tgz", - "integrity": "sha512-gbMzqQtTtDz/00jQzZ21PQzdI9PyLYqUSvD0p3naOhX4odFji0ZxYdnVwPTxmSwkmxhcFImpozceidSG+AgoPQ==", + "version": "2.0.5", + "resolved": "https://registry.npmjs.org/node-releases/-/node-releases-2.0.5.tgz", + "integrity": "sha512-U9h1NLROZTq9uE1SNffn6WuPDg8icmi3ns4rEl/oTfIle4iLjTliCzgTsbaIFMq/Xn078/lfY/BL0GWZ+psK4Q==", "dev": true }, "node_modules/normalize-path": { @@ -5386,16 +5517,16 @@ "node_modules/object-assign": { "version": "4.1.1", "resolved": "https://registry.npmjs.org/object-assign/-/object-assign-4.1.1.tgz", - "integrity": "sha1-IQmtx5ZYh8/AXLvUQsrIv7s2CGM=", + "integrity": "sha512-rJgTQnkUnH1sFw8yT6VSU3zD3sWmu6sZhIseY8VX+GRu3P6F7Fu+JNDoXfklElbLJSnc3FUQHVe4cU5hj+BcUg==", "dev": true, "engines": { "node": ">=0.10.0" } }, "node_modules/object-inspect": { - "version": "1.12.0", - "resolved": "https://registry.npmjs.org/object-inspect/-/object-inspect-1.12.0.tgz", - "integrity": "sha512-Ho2z80bVIvJloH+YzRmpZVQe87+qASmBUKZDWgx9cu+KDrX2ZDH/3tMy+gXbZETVGs2M8YdxObOh7XAtim9Y0g==", + "version": "1.12.2", + "resolved": "https://registry.npmjs.org/object-inspect/-/object-inspect-1.12.2.tgz", + "integrity": "sha512-z+cPxW0QGUp0mcqcsgQyLVRDoXFQbXOwBaqyF7VIgI4TWNQsDHrBpUQslRmIfAoYWdYzs6UlKJtB2XJpTaNSpQ==", "dev": true, "funding": { "url": "https://github.com/sponsors/ljharb" @@ -5431,7 +5562,7 @@ "node_modules/once": { "version": "1.4.0", "resolved": "https://registry.npmjs.org/once/-/once-1.4.0.tgz", - "integrity": "sha1-WDsap3WWHUsROsF9nFC6753Xa9E=", + "integrity": "sha512-lNaJgI+2Q5URQBkccEKHTQOPaXdUxnZZElQTZY0MFUAuaEqe1E+Nyvgdz/aIyNi6Z9MzO5dv1H8n58/GELp3+w==", "dev": true, "dependencies": { "wrappy": "1" @@ -5509,33 +5640,6 @@ "url": "https://github.com/sponsors/sindresorhus" } }, - "node_modules/ora/node_modules/ansi-regex": { - "version": "6.0.1", - "resolved": "https://registry.npmjs.org/ansi-regex/-/ansi-regex-6.0.1.tgz", - "integrity": "sha512-n5M855fKb2SsfMIiFFoVrABHJC8QtHwVx+mHWP3QcEqBHYienj5dHSgjbxtC0WEZXYt4wcD6zrQElDPhFuZgfA==", - "dev": true, - "engines": { - "node": ">=12" - }, - "funding": { - "url": "https://github.com/chalk/ansi-regex?sponsor=1" - } - }, - "node_modules/ora/node_modules/strip-ansi": { - "version": "7.0.1", - "resolved": "https://registry.npmjs.org/strip-ansi/-/strip-ansi-7.0.1.tgz", - "integrity": "sha512-cXNxvT8dFNRVfhVME3JAe98mkXDYN2O1l7jmcwMnOslDeESg1rF/OZMtK0nRAhiari1unG5cD4jG3rapUAkLbw==", - "dev": true, - "dependencies": { - "ansi-regex": "^6.0.1" - }, - "engines": { - "node": ">=12" - }, - "funding": { - "url": "https://github.com/chalk/strip-ansi?sponsor=1" - } - }, "node_modules/os-name": { "version": "5.0.1", "resolved": "https://registry.npmjs.org/os-name/-/os-name-5.0.1.tgz", @@ -5639,14 +5743,14 @@ } }, "node_modules/pac-resolver": { - "version": "5.0.0", - "resolved": "https://registry.npmjs.org/pac-resolver/-/pac-resolver-5.0.0.tgz", - "integrity": "sha512-H+/A6KitiHNNW+bxBKREk2MCGSxljfqRX76NjummWEYIat7ldVXRU3dhRIE3iXZ0nvGBk6smv3nntxKkzRL8NA==", + "version": "5.0.1", + "resolved": "https://registry.npmjs.org/pac-resolver/-/pac-resolver-5.0.1.tgz", + "integrity": "sha512-cy7u00ko2KVgBAjuhevqpPeHIkCIqPe1v24cydhWjmeuzaBfmUWFCZJ1iAh5TuVzVZoUzXIW7K8sMYOZ84uZ9Q==", "dev": true, "dependencies": { - "degenerator": "^3.0.1", + "degenerator": "^3.0.2", "ip": "^1.1.5", - "netmask": "^2.0.1" + "netmask": "^2.0.2" }, "engines": { "node": ">= 8" @@ -5749,9 +5853,9 @@ } }, "node_modules/parse-path": { - "version": "4.0.3", - "resolved": "https://registry.npmjs.org/parse-path/-/parse-path-4.0.3.tgz", - "integrity": "sha512-9Cepbp2asKnWTJ9x2kpw6Fe8y9JDbqwahGCTvklzd/cEq5C5JC59x2Xb0Kx+x0QZ8bvNquGO8/BWP0cwBHzSAA==", + "version": "4.0.4", + "resolved": "https://registry.npmjs.org/parse-path/-/parse-path-4.0.4.tgz", + "integrity": "sha512-Z2lWUis7jlmXC1jeOG9giRO2+FsuyNipeQ43HAjqAZjwSe3SEf+q/84FGPHoso3kyntbxa4c4i77t3m6fGf8cw==", "dev": true, "dependencies": { "is-ssh": "^1.3.0", @@ -5760,18 +5864,30 @@ "query-string": "^6.13.8" } }, + "node_modules/parse-path/node_modules/protocols": { + "version": "1.4.8", + "resolved": "https://registry.npmjs.org/protocols/-/protocols-1.4.8.tgz", + "integrity": "sha512-IgjKyaUSjsROSO8/D49Ab7hP8mJgTYcqApOqdPhLoPxAplXmkp+zRvsrSQjFn5by0rhm4VH0GAUELIPpx7B1yg==", + "dev": true + }, "node_modules/parse-url": { - "version": "6.0.0", - "resolved": "https://registry.npmjs.org/parse-url/-/parse-url-6.0.0.tgz", - "integrity": "sha512-cYyojeX7yIIwuJzledIHeLUBVJ6COVLeT4eF+2P6aKVzwvgKQPndCBv3+yQ7pcWjqToYwaligxzSYNNmGoMAvw==", + "version": "6.0.2", + "resolved": "https://registry.npmjs.org/parse-url/-/parse-url-6.0.2.tgz", + "integrity": "sha512-uCSjOvD3T+6B/sPWhR+QowAZcU/o4bjPrVBQBGFxcDF6J6FraCGIaDBsdoQawiaaAVdHvtqBe3w3vKlfBKySOQ==", "dev": true, "dependencies": { "is-ssh": "^1.3.0", "normalize-url": "^6.1.0", - "parse-path": "^4.0.0", + "parse-path": "^4.0.4", "protocols": "^1.4.0" } }, + "node_modules/parse-url/node_modules/protocols": { + "version": "1.4.8", + "resolved": "https://registry.npmjs.org/protocols/-/protocols-1.4.8.tgz", + "integrity": "sha512-IgjKyaUSjsROSO8/D49Ab7hP8mJgTYcqApOqdPhLoPxAplXmkp+zRvsrSQjFn5by0rhm4VH0GAUELIPpx7B1yg==", + "dev": true + }, "node_modules/path-exists": { "version": "4.0.0", "resolved": "https://registry.npmjs.org/path-exists/-/path-exists-4.0.0.tgz", @@ -5784,7 +5900,7 @@ "node_modules/path-is-absolute": { "version": "1.0.1", "resolved": "https://registry.npmjs.org/path-is-absolute/-/path-is-absolute-1.0.1.tgz", - "integrity": "sha1-F0uSaHNVNP+8es5r9TpanhtcX18=", + "integrity": "sha512-AVbw3UJ2e9bq64vSaS9Am0fje1Pa8pbGqTTsmXfaIiMpnr5DlDhfJOuLj9Sf95ZPVDAUerDfEk88MPmPe7UCQg==", "dev": true, "engines": { "node": ">=0.10.0" @@ -5817,7 +5933,7 @@ "node_modules/path-to-regexp/node_modules/isarray": { "version": "0.0.1", "resolved": "https://registry.npmjs.org/isarray/-/isarray-0.0.1.tgz", - "integrity": "sha1-ihis/Kmo9Bd+Cav8YDiTmwXR7t8=", + "integrity": "sha512-D2S+3GLxWH+uhrNEcoh/fnmYeP8E8/zHl644d/jdA0g2uyXvy3sb0qxotE+ne0LtccHknQzWwZEzhak7oJ0COQ==", "dev": true }, "node_modules/path-type": { @@ -5850,7 +5966,7 @@ "node_modules/pify": { "version": "2.3.0", "resolved": "https://registry.npmjs.org/pify/-/pify-2.3.0.tgz", - "integrity": "sha1-7RQaasBDqEnqWISY59yosVMw6Qw=", + "integrity": "sha512-udgsAY+fTnvv7kI7aaxbqwWNb0AHiB0qBO89PZKPkoTmGOgdbrHDKD+0B2X4uTfJ/FT1R09r9gTsjUjNJotuog==", "dev": true, "engines": { "node": ">=0.10.0" @@ -5859,7 +5975,7 @@ "node_modules/pinkie": { "version": "2.0.4", "resolved": "https://registry.npmjs.org/pinkie/-/pinkie-2.0.4.tgz", - "integrity": "sha1-clVrgM+g1IqXToDnckjoDtT3+HA=", + "integrity": "sha512-MnUuEycAemtSaeFSjXKW/aroV7akBbY+Sv+RkyqFjgAe73F+MR0TBWKBRDkmfWq/HiFmdavfZ1G7h4SPZXaCSg==", "dev": true, "engines": { "node": ">=0.10.0" @@ -5868,7 +5984,7 @@ "node_modules/pinkie-promise": { "version": "2.0.1", "resolved": "https://registry.npmjs.org/pinkie-promise/-/pinkie-promise-2.0.1.tgz", - "integrity": "sha1-ITXW36ejWMBprJsXh3YogihFD/o=", + "integrity": "sha512-0Gni6D4UcLTbv9c57DfxDGdr41XfgUjqWZu492f0cIGr16zDU06BWP/RAEvOuo7CQ0CNjHaLlM59YJJFm3NWlw==", "dev": true, "dependencies": { "pinkie": "^2.0.0" @@ -5937,9 +6053,9 @@ "dev": true }, "node_modules/protocols": { - "version": "1.4.8", - "resolved": "https://registry.npmjs.org/protocols/-/protocols-1.4.8.tgz", - "integrity": "sha512-IgjKyaUSjsROSO8/D49Ab7hP8mJgTYcqApOqdPhLoPxAplXmkp+zRvsrSQjFn5by0rhm4VH0GAUELIPpx7B1yg==", + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/protocols/-/protocols-2.0.1.tgz", + "integrity": "sha512-/XJ368cyBJ7fzLMwLKv1e4vLxOju2MNAIokcr7meSaNcVbWz/CPcW22cP04mwxOErdA5mwjA8Q6w/cdAQxVn7Q==", "dev": true }, "node_modules/proxy-agent": { @@ -6002,9 +6118,9 @@ } }, "node_modules/qs": { - "version": "6.10.3", - "resolved": "https://registry.npmjs.org/qs/-/qs-6.10.3.tgz", - "integrity": "sha512-wr7M2E0OFRfIfJZjKGieI8lBKb7fRCH4Fv5KNPEs7gJ8jadvotdsS08PzOKR7opXhZ/Xkjtt3WF9g38drmyRqQ==", + "version": "6.11.0", + "resolved": "https://registry.npmjs.org/qs/-/qs-6.11.0.tgz", + "integrity": "sha512-MvjoMCJwEarSbUYk5O+nmoSzSutSsTwF85zcHPQ9OrlFoZOYIjaqBAJIqIXjptyD5vThxGq52Xu/MaJzRkIk4Q==", "dev": true, "dependencies": { "side-channel": "^1.0.4" @@ -6149,7 +6265,7 @@ "node_modules/rechoir": { "version": "0.6.2", "resolved": "https://registry.npmjs.org/rechoir/-/rechoir-0.6.2.tgz", - "integrity": "sha1-hSBLVNuoLVdC4oyWdW70OvUOM4Q=", + "integrity": "sha512-HFM8rkZ+i3zrV+4LQjwQ0W+ez98pApMGM3HUrN04j3CqzPOzl9nmP15Y8YXNm8QHGv/eacOVEjqhmWpkRV0NAw==", "dev": true, "dependencies": { "resolve": "^1.1.6" @@ -6158,6 +6274,23 @@ "node": ">= 0.10" } }, + "node_modules/regexp.prototype.flags": { + "version": "1.4.3", + "resolved": "https://registry.npmjs.org/regexp.prototype.flags/-/regexp.prototype.flags-1.4.3.tgz", + "integrity": "sha512-fjggEOO3slI6Wvgjwflkc4NFRCTZAu5CnNfBd5qOMYhWdn67nJBBu34/TkD++eeFmd8C9r9jfXJ27+nSiRkSUA==", + "dev": true, + "dependencies": { + "call-bind": "^1.0.2", + "define-properties": "^1.1.3", + "functions-have-names": "^1.2.2" + }, + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, "node_modules/regexpp": { "version": "3.2.0", "resolved": "https://registry.npmjs.org/regexpp/-/regexpp-3.2.0.tgz", @@ -6304,7 +6437,7 @@ "node_modules/release-zalgo": { "version": "1.0.0", "resolved": "https://registry.npmjs.org/release-zalgo/-/release-zalgo-1.0.0.tgz", - "integrity": "sha1-CXALflB0Mpc5Mw5TXFqQ+2eFFzA=", + "integrity": "sha512-gUAyHVHPPC5wdqX/LG4LWtRYtgjxyX78oanFNTMMyFEfOqdC54s3eE82imuWKbOeqYht2CrNf64Qb8vgmmtZGA==", "dev": true, "dependencies": { "es6-error": "^4.0.1" @@ -6316,7 +6449,7 @@ "node_modules/require-directory": { "version": "2.1.1", "resolved": "https://registry.npmjs.org/require-directory/-/require-directory-2.1.1.tgz", - "integrity": "sha1-jGStX9MNqxyXbiNE/+f3kqam30I=", + "integrity": "sha512-fGxEI7+wsG9xrvdjsrlmL22OMTTiHRwAMroiEeMgq8gzoLC/PQr7RsRDSTLUg/bZAZtF+TVIkHc6/4RIKrui+Q==", "dev": true, "engines": { "node": ">=0.10.0" @@ -6329,12 +6462,12 @@ "dev": true }, "node_modules/resolve": { - "version": "1.22.0", - "resolved": "https://registry.npmjs.org/resolve/-/resolve-1.22.0.tgz", - "integrity": "sha512-Hhtrw0nLeSrFQ7phPp4OOcVjLPIeMnRlr5mcnVuMe7M/7eBn98A3hmFRLoFo3DLZkivSYwhRUJTyPyWAk56WLw==", + "version": "1.22.1", + "resolved": "https://registry.npmjs.org/resolve/-/resolve-1.22.1.tgz", + "integrity": "sha512-nBpuuYuY5jFsli/JIs1oldw6fOQCBioohqWZg/2hiaOybXOft4lonv85uDOKXdf8rhyK159cxU5cDcK/NKk8zw==", "dev": true, "dependencies": { - "is-core-module": "^2.8.1", + "is-core-module": "^2.9.0", "path-parse": "^1.0.7", "supports-preserve-symlinks-flag": "^1.0.0" }, @@ -6574,7 +6707,7 @@ "node_modules/set-blocking": { "version": "2.0.0", "resolved": "https://registry.npmjs.org/set-blocking/-/set-blocking-2.0.0.tgz", - "integrity": "sha1-BF+XgtARrppoA93TgrJDkrPYkPc=", + "integrity": "sha512-KiKBS8AnWGEyLzofFfmvKwpdPzqiy16LvQfK3yv/fVH7Bj13/wl3JSR1J+rfgRE9q7xUJK4qvgS8raSOeLUehw==", "dev": true }, "node_modules/setprototypeof": { @@ -6765,7 +6898,7 @@ "node_modules/sprintf-js": { "version": "1.0.3", "resolved": "https://registry.npmjs.org/sprintf-js/-/sprintf-js-1.0.3.tgz", - "integrity": "sha1-BOaSb2YolTVPPdAVIDYzuFcpfiw=", + "integrity": "sha512-D9cPgkvLlV3t3IzL0D0YLvGA9Ahk4PcvVwUbN0dSGr1aP0Nrt4AEnTUbuGvquEC0mA64Gqt1fzirlRs5ibXx8g==", "dev": true }, "node_modules/statuses": { @@ -6780,7 +6913,7 @@ "node_modules/strict-uri-encode": { "version": "2.0.0", "resolved": "https://registry.npmjs.org/strict-uri-encode/-/strict-uri-encode-2.0.0.tgz", - "integrity": "sha1-ucczDHBChi9rFC3CdLvMWGbONUY=", + "integrity": "sha512-QwiXZgpRcKkhTj2Scnn++4PKtWsH0kpzZ62L2R6c/LUVYv7hVnZqcg2+sMuT6R7Jusu1vviK/MFsu6kNJfWlEQ==", "dev": true, "engines": { "node": ">=4" @@ -6816,55 +6949,63 @@ ] }, "node_modules/string-width": { - "version": "4.2.3", - "resolved": "https://registry.npmjs.org/string-width/-/string-width-4.2.3.tgz", - "integrity": "sha512-wKyQRQpjJ0sIp62ErSZdGsjMJWsap5oRNihHhu6G7JVO/9jIB6UyevL+tXuOqrng8j/cxKTWyWUwvSTriiZz/g==", + "version": "5.1.2", + "resolved": "https://registry.npmjs.org/string-width/-/string-width-5.1.2.tgz", + "integrity": "sha512-HnLOCR3vjcY8beoNLtcjZ5/nxn2afmME6lhrDrebokqMap+XbeW8n9TXpPDOqdGK5qcI3oT0GKTW6wC7EMiVqA==", "dev": true, "dependencies": { - "emoji-regex": "^8.0.0", - "is-fullwidth-code-point": "^3.0.0", - "strip-ansi": "^6.0.1" + "eastasianwidth": "^0.2.0", + "emoji-regex": "^9.2.2", + "strip-ansi": "^7.0.1" }, "engines": { - "node": ">=8" + "node": ">=12" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" } }, "node_modules/string.prototype.trimend": { - "version": "1.0.4", - "resolved": "https://registry.npmjs.org/string.prototype.trimend/-/string.prototype.trimend-1.0.4.tgz", - "integrity": "sha512-y9xCjw1P23Awk8EvTpcyL2NIr1j7wJ39f+k6lvRnSMz+mz9CGz9NYPelDk42kOz6+ql8xjfK8oYzy3jAP5QU5A==", + "version": "1.0.5", + "resolved": "https://registry.npmjs.org/string.prototype.trimend/-/string.prototype.trimend-1.0.5.tgz", + "integrity": "sha512-I7RGvmjV4pJ7O3kdf+LXFpVfdNOxtCW/2C8f6jNiW4+PQchwxkCDzlk1/7p+Wl4bqFIZeF47qAHXLuHHWKAxog==", "dev": true, "dependencies": { "call-bind": "^1.0.2", - "define-properties": "^1.1.3" + "define-properties": "^1.1.4", + "es-abstract": "^1.19.5" }, "funding": { "url": "https://github.com/sponsors/ljharb" } }, "node_modules/string.prototype.trimstart": { - "version": "1.0.4", - "resolved": "https://registry.npmjs.org/string.prototype.trimstart/-/string.prototype.trimstart-1.0.4.tgz", - "integrity": "sha512-jh6e984OBfvxS50tdY2nRZnoC5/mLFKOREQfw8t5yytkoUsJRNxvI/E39qu1sD0OtWI3OC0XgKSmcWwziwYuZw==", + "version": "1.0.5", + "resolved": "https://registry.npmjs.org/string.prototype.trimstart/-/string.prototype.trimstart-1.0.5.tgz", + "integrity": "sha512-THx16TJCGlsN0o6dl2o6ncWUsdgnLRSA23rRE5pyGBw/mLr3Ej/R2LaqCtgP8VNMGZsvMWnf9ooZPyY2bHvUFg==", "dev": true, "dependencies": { "call-bind": "^1.0.2", - "define-properties": "^1.1.3" + "define-properties": "^1.1.4", + "es-abstract": "^1.19.5" }, "funding": { "url": "https://github.com/sponsors/ljharb" } }, "node_modules/strip-ansi": { - "version": "6.0.1", - "resolved": "https://registry.npmjs.org/strip-ansi/-/strip-ansi-6.0.1.tgz", - "integrity": "sha512-Y38VPSHcqkFrCpFnQ9vuSXmquuv5oXOKpGeT6aGrr3o3Gc9AlVa6JBfUSOCnbxGGZF+/0ooI7KrPuUSztUdU5A==", + "version": "7.0.1", + "resolved": "https://registry.npmjs.org/strip-ansi/-/strip-ansi-7.0.1.tgz", + "integrity": "sha512-cXNxvT8dFNRVfhVME3JAe98mkXDYN2O1l7jmcwMnOslDeESg1rF/OZMtK0nRAhiari1unG5cD4jG3rapUAkLbw==", "dev": true, "dependencies": { - "ansi-regex": "^5.0.1" + "ansi-regex": "^6.0.1" }, "engines": { - "node": ">=8" + "node": ">=12" + }, + "funding": { + "url": "https://github.com/chalk/strip-ansi?sponsor=1" } }, "node_modules/strip-bom": { @@ -6912,6 +7053,15 @@ "node": ">=0.10.0" } }, + "node_modules/strip-outer/node_modules/escape-string-regexp": { + "version": "1.0.5", + "resolved": "https://registry.npmjs.org/escape-string-regexp/-/escape-string-regexp-1.0.5.tgz", + "integrity": "sha512-vbRorB5FUQWvla16U8R/qgaFIya2qGzwDrNmCZuYKrbdSUMG6I1ZCGQRefkRVhuOkIGVne7BQ35DSfo1qvJqFg==", + "dev": true, + "engines": { + "node": ">=0.8.0" + } + }, "node_modules/supports-color": { "version": "7.2.0", "resolved": "https://registry.npmjs.org/supports-color/-/supports-color-7.2.0.tgz", @@ -6953,7 +7103,7 @@ "node_modules/text-table": { "version": "0.2.0", "resolved": "https://registry.npmjs.org/text-table/-/text-table-0.2.0.tgz", - "integrity": "sha1-f17oI66AUgfACvLfSoTsP8+lcLQ=", + "integrity": "sha512-N+8UisAXDGk8PFXP4HAzVR9nbfmVJ3zYLAWiTIoqC5v5isinhr+r5uaO8+7r3BMfuNIufIsA7RdpVgacC2cSpw==", "dev": true }, "node_modules/through": { @@ -6977,7 +7127,7 @@ "node_modules/to-fast-properties": { "version": "2.0.0", "resolved": "https://registry.npmjs.org/to-fast-properties/-/to-fast-properties-2.0.0.tgz", - "integrity": "sha1-3F5pjL0HkmW8c+A3doGk5Og/YW4=", + "integrity": "sha512-/OaKK0xYrs3DmxRYqL/yDc+FxFUVYhDlXMhRmv3z915w2HF1tnN1omB354j8VUGO/hbRzyD6Y3sA7v7GS/ceog==", "dev": true, "engines": { "node": ">=4" @@ -7007,13 +7157,13 @@ "node_modules/tr46": { "version": "0.0.3", "resolved": "https://registry.npmjs.org/tr46/-/tr46-0.0.3.tgz", - "integrity": "sha1-gYT9NH2snNwYWZLzpmIuFLnZq2o=", + "integrity": "sha512-N3WMsuqV66lT30CrXNbEjx4GEwlow3v6rr4mCcv6prnfwhS01rkgyFdjPNBYd9br7LpXV1+Emh01fHnq2Gdgrw==", "dev": true }, "node_modules/trim-repeated": { "version": "1.0.0", "resolved": "https://registry.npmjs.org/trim-repeated/-/trim-repeated-1.0.0.tgz", - "integrity": "sha1-42RqLqTokTEr9+rObPsFOAvAHCE=", + "integrity": "sha512-pkonvlKk8/ZuR0D5tLW8ljt5I8kmxp2XKymhepUeOdCEfKpZaktSArkLHZt76OB1ZvO9bssUsDty4SWhLvZpLg==", "dev": true, "dependencies": { "escape-string-regexp": "^1.0.2" @@ -7022,6 +7172,15 @@ "node": ">=0.10.0" } }, + "node_modules/trim-repeated/node_modules/escape-string-regexp": { + "version": "1.0.5", + "resolved": "https://registry.npmjs.org/escape-string-regexp/-/escape-string-regexp-1.0.5.tgz", + "integrity": "sha512-vbRorB5FUQWvla16U8R/qgaFIya2qGzwDrNmCZuYKrbdSUMG6I1ZCGQRefkRVhuOkIGVne7BQ35DSfo1qvJqFg==", + "dev": true, + "engines": { + "node": ">=0.8.0" + } + }, "node_modules/ts-node": { "version": "10.8.1", "resolved": "https://registry.npmjs.org/ts-node/-/ts-node-10.8.1.tgz", @@ -7123,9 +7282,9 @@ } }, "node_modules/type-fest": { - "version": "0.20.2", - "resolved": "https://registry.npmjs.org/type-fest/-/type-fest-0.20.2.tgz", - "integrity": "sha512-Ne+eE4r0/iWnpAxD852z3A+N0Bt5RN//NjJwRd2VFHEmrywxf5vsZlh4R6lixl6B+wz/8d+maTSAkN1FIkI3LQ==", + "version": "1.4.0", + "resolved": "https://registry.npmjs.org/type-fest/-/type-fest-1.4.0.tgz", + "integrity": "sha512-yGSza74xk0UG8k+pLh5oeoYirvIiWo5t0/o3zHHAO2tRDiZcxWP7fywNlXhqb6/r6sWvwi+RsyQMWhVLe4BVuA==", "dev": true, "engines": { "node": ">=10" @@ -7246,12 +7405,38 @@ "node_modules/unpipe": { "version": "1.0.0", "resolved": "https://registry.npmjs.org/unpipe/-/unpipe-1.0.0.tgz", - "integrity": "sha1-sr9O6FFKrmFltIF4KdIbLvSZBOw=", + "integrity": "sha512-pjy2bYhSsufwWlKwPc+l3cN7+wuJlK6uz0YdJEOlQDbl6jo/YlPi4mb8agUkVC8BF7V8NuzeyPNqRksA3hztKQ==", "dev": true, "engines": { "node": ">= 0.8" } }, + "node_modules/update-browserslist-db": { + "version": "1.0.4", + "resolved": "https://registry.npmjs.org/update-browserslist-db/-/update-browserslist-db-1.0.4.tgz", + "integrity": "sha512-jnmO2BEGUjsMOe/Fg9u0oczOe/ppIDZPebzccl1yDWGLFP16Pa1/RM5wEoKYPG2zstNcDuAStejyxsOuKINdGA==", + "dev": true, + "funding": [ + { + "type": "opencollective", + "url": "https://opencollective.com/browserslist" + }, + { + "type": "tidelift", + "url": "https://tidelift.com/funding/github/npm/browserslist" + } + ], + "dependencies": { + "escalade": "^3.1.1", + "picocolors": "^1.0.0" + }, + "bin": { + "browserslist-lint": "cli.js" + }, + "peerDependencies": { + "browserslist": ">= 4.21.0" + } + }, "node_modules/update-notifier": { "version": "6.0.2", "resolved": "https://registry.npmjs.org/update-notifier/-/update-notifier-6.0.2.tgz", @@ -7338,13 +7523,12 @@ "dev": true }, "node_modules/uuid": { - "version": "3.4.0", - "resolved": "https://registry.npmjs.org/uuid/-/uuid-3.4.0.tgz", - "integrity": "sha512-HjSDRw6gZE5JMggctHBcjVak08+KEVhSIiDzFnT9S9aegmp85S/bReBVTb4QTFaRNptJ9kuYaNhnbNEOkbKb/A==", - "deprecated": "Please upgrade to version 7 or higher. Older versions may use Math.random() in certain circumstances, which is known to be problematic. See https://v8.dev/blog/math-random for details.", + "version": "8.3.2", + "resolved": "https://registry.npmjs.org/uuid/-/uuid-8.3.2.tgz", + "integrity": "sha512-+NYs2QeMWy+GWFOEm9xnn6HCDp0l7QBD7ml8zLUmJ+93Q5NF0NocErnwkTkXVFNiX3/fpC6afS8Dhb/gz7R7eg==", "dev": true, "bin": { - "uuid": "bin/uuid" + "uuid": "dist/bin/uuid" } }, "node_modules/v8-compile-cache": { @@ -7399,13 +7583,13 @@ "node_modules/webidl-conversions": { "version": "3.0.1", "resolved": "https://registry.npmjs.org/webidl-conversions/-/webidl-conversions-3.0.1.tgz", - "integrity": "sha1-JFNCdeKnvGvnvIZhHMFq4KVlSHE=", + "integrity": "sha512-2JAn3z8AR6rjK8Sm8orRC0h/bcl/DqL7tRPdGZ4I1CjdF+EaMLmYxBHyXuKL849eucPFhvBoxMsflfOb8kxaeQ==", "dev": true }, "node_modules/whatwg-url": { "version": "5.0.0", "resolved": "https://registry.npmjs.org/whatwg-url/-/whatwg-url-5.0.0.tgz", - "integrity": "sha1-lmRU6HZUYuN2RNNib2dCzotwll0=", + "integrity": "sha512-saE57nupxk6v3HY35+jzBwYa0rKSy0XR8JSxZPwgLr7ys0IBzhGviA1/TUGJLmSVqs8pb9AnvICXEuOHLprYTw==", "dev": true, "dependencies": { "tr46": "~0.0.3", @@ -7446,7 +7630,7 @@ "node_modules/which-module": { "version": "2.0.0", "resolved": "https://registry.npmjs.org/which-module/-/which-module-2.0.0.tgz", - "integrity": "sha1-2e8H3Od7mQK4o6j6SzHD4/fm6Ho=", + "integrity": "sha512-B+enWhmw6cjfVC7kS8Pj9pCrKSc5txArRyaYGe088shv/FGWH+0Rjx/xPgtsWfsUtS27FkP697E4DDhgrgoc0Q==", "dev": true }, "node_modules/widest-line": { @@ -7464,56 +7648,6 @@ "url": "https://github.com/sponsors/sindresorhus" } }, - "node_modules/widest-line/node_modules/ansi-regex": { - "version": "6.0.1", - "resolved": "https://registry.npmjs.org/ansi-regex/-/ansi-regex-6.0.1.tgz", - "integrity": "sha512-n5M855fKb2SsfMIiFFoVrABHJC8QtHwVx+mHWP3QcEqBHYienj5dHSgjbxtC0WEZXYt4wcD6zrQElDPhFuZgfA==", - "dev": true, - "engines": { - "node": ">=12" - }, - "funding": { - "url": "https://github.com/chalk/ansi-regex?sponsor=1" - } - }, - "node_modules/widest-line/node_modules/emoji-regex": { - "version": "9.2.2", - "resolved": "https://registry.npmjs.org/emoji-regex/-/emoji-regex-9.2.2.tgz", - "integrity": "sha512-L18DaJsXSUk2+42pv8mLs5jJT2hqFkFE4j21wOmgbUqsZ2hL72NsUU785g9RXgo3s0ZNgVl42TiHp3ZtOv/Vyg==", - "dev": true - }, - "node_modules/widest-line/node_modules/string-width": { - "version": "5.1.2", - "resolved": "https://registry.npmjs.org/string-width/-/string-width-5.1.2.tgz", - "integrity": "sha512-HnLOCR3vjcY8beoNLtcjZ5/nxn2afmME6lhrDrebokqMap+XbeW8n9TXpPDOqdGK5qcI3oT0GKTW6wC7EMiVqA==", - "dev": true, - "dependencies": { - "eastasianwidth": "^0.2.0", - "emoji-regex": "^9.2.2", - "strip-ansi": "^7.0.1" - }, - "engines": { - "node": ">=12" - }, - "funding": { - "url": "https://github.com/sponsors/sindresorhus" - } - }, - "node_modules/widest-line/node_modules/strip-ansi": { - "version": "7.0.1", - "resolved": "https://registry.npmjs.org/strip-ansi/-/strip-ansi-7.0.1.tgz", - "integrity": "sha512-cXNxvT8dFNRVfhVME3JAe98mkXDYN2O1l7jmcwMnOslDeESg1rF/OZMtK0nRAhiari1unG5cD4jG3rapUAkLbw==", - "dev": true, - "dependencies": { - "ansi-regex": "^6.0.1" - }, - "engines": { - "node": ">=12" - }, - "funding": { - "url": "https://github.com/chalk/strip-ansi?sponsor=1" - } - }, "node_modules/wildcard-match": { "version": "5.1.2", "resolved": "https://registry.npmjs.org/wildcard-match/-/wildcard-match-5.1.2.tgz", @@ -7640,17 +7774,17 @@ "dev": true }, "node_modules/wrap-ansi": { - "version": "7.0.0", - "resolved": "https://registry.npmjs.org/wrap-ansi/-/wrap-ansi-7.0.0.tgz", - "integrity": "sha512-YVGIj2kamLSTxw6NsZjoBxfSwsn0ycdesmc4p+Q21c5zPuZ1pl+NfxVdxPtdHvmNVOQ6XSYG4AUtyt/Fi7D16Q==", + "version": "8.0.1", + "resolved": "https://registry.npmjs.org/wrap-ansi/-/wrap-ansi-8.0.1.tgz", + "integrity": "sha512-QFF+ufAqhoYHvoHdajT/Po7KoXVBPXS2bgjIam5isfWJPfIOnQZ50JtUiVvCv/sjgacf3yRrt2ZKUZ/V4itN4g==", "dev": true, "dependencies": { - "ansi-styles": "^4.0.0", - "string-width": "^4.1.0", - "strip-ansi": "^6.0.0" + "ansi-styles": "^6.1.0", + "string-width": "^5.0.1", + "strip-ansi": "^7.0.1" }, "engines": { - "node": ">=10" + "node": ">=12" }, "funding": { "url": "https://github.com/chalk/wrap-ansi?sponsor=1" @@ -7659,7 +7793,7 @@ "node_modules/wrappy": { "version": "1.0.2", "resolved": "https://registry.npmjs.org/wrappy/-/wrappy-1.0.2.tgz", - "integrity": "sha1-tSQ9jz7BqjXxNkYFvA0QNuMKtp8=", + "integrity": "sha512-l4Sp/DRseor9wL6EvV2+TuQn63dMkPjZ/sp9XkghTEbV9KlPS1xUsZ3u7/IQO4wxtcFB4bgpQPRcR3QCvezPcQ==", "dev": true }, "node_modules/write-file-atomic": { @@ -7689,7 +7823,7 @@ "node_modules/xregexp": { "version": "2.0.0", "resolved": "https://registry.npmjs.org/xregexp/-/xregexp-2.0.0.tgz", - "integrity": "sha1-UqY+VsoLhKfzpfPWGHLxJq16WUM=", + "integrity": "sha512-xl/50/Cf32VsGq/1R8jJE5ajH1yMCQkpmoS10QbFZWl2Oor4H0Me64Pu2yxvsRWK3m6soJbmGfzSR7BYmDcWAA==", "dev": true, "engines": { "node": "*" @@ -7786,30 +7920,71 @@ "url": "https://github.com/sponsors/sindresorhus" } }, - "node_modules/yargs/node_modules/yargs-parser": { - "version": "18.1.3", - "resolved": "https://registry.npmjs.org/yargs-parser/-/yargs-parser-18.1.3.tgz", - "integrity": "sha512-o50j0JeToy/4K6OZcaQmW6lyXXKhq7csREXcDwk2omFPJEwUNOVtJKvmDr9EI1fAJZUyZcRF7kxGBWmRXudrCQ==", - "dev": true, - "dependencies": { - "camelcase": "^5.0.0", - "decamelize": "^1.2.0" - }, - "engines": { - "node": ">=6" - } - }, - "node_modules/yn": { - "version": "3.1.1", - "resolved": "https://registry.npmjs.org/yn/-/yn-3.1.1.tgz", - "integrity": "sha512-Ux4ygGWsu2c7isFWe8Yu1YluJmqVhxqK2cLXNQA5AcC3QfbGNpM7fu0Y8b/z16pXLnFxZYvWhd3fhBY9DLmC6Q==", + "node_modules/yargs/node_modules/ansi-regex": { + "version": "5.0.1", + "resolved": "https://registry.npmjs.org/ansi-regex/-/ansi-regex-5.0.1.tgz", + "integrity": "sha512-quJQXlTSUGL2LH9SUXo8VwsY4soanhgo6LNSm84E1LBcE8s3O0wpdiRzyR9z/ZZJMlMWv37qOOb9pdJlMUEKFQ==", "dev": true, "engines": { - "node": ">=6" + "node": ">=8" } }, - "node_modules/yocto-queue": { - "version": "0.1.0", + "node_modules/yargs/node_modules/emoji-regex": { + "version": "8.0.0", + "resolved": "https://registry.npmjs.org/emoji-regex/-/emoji-regex-8.0.0.tgz", + "integrity": "sha512-MSjYzcWNOA0ewAHpz0MxpYFvwg6yjy1NG3xteoqz644VCo/RPgnr1/GGt+ic3iJTzQ8Eu3TdM14SawnVUmGE6A==", + "dev": true + }, + "node_modules/yargs/node_modules/string-width": { + "version": "4.2.3", + "resolved": "https://registry.npmjs.org/string-width/-/string-width-4.2.3.tgz", + "integrity": "sha512-wKyQRQpjJ0sIp62ErSZdGsjMJWsap5oRNihHhu6G7JVO/9jIB6UyevL+tXuOqrng8j/cxKTWyWUwvSTriiZz/g==", + "dev": true, + "dependencies": { + "emoji-regex": "^8.0.0", + "is-fullwidth-code-point": "^3.0.0", + "strip-ansi": "^6.0.1" + }, + "engines": { + "node": ">=8" + } + }, + "node_modules/yargs/node_modules/strip-ansi": { + "version": "6.0.1", + "resolved": "https://registry.npmjs.org/strip-ansi/-/strip-ansi-6.0.1.tgz", + "integrity": "sha512-Y38VPSHcqkFrCpFnQ9vuSXmquuv5oXOKpGeT6aGrr3o3Gc9AlVa6JBfUSOCnbxGGZF+/0ooI7KrPuUSztUdU5A==", + "dev": true, + "dependencies": { + "ansi-regex": "^5.0.1" + }, + "engines": { + "node": ">=8" + } + }, + "node_modules/yargs/node_modules/yargs-parser": { + "version": "18.1.3", + "resolved": "https://registry.npmjs.org/yargs-parser/-/yargs-parser-18.1.3.tgz", + "integrity": "sha512-o50j0JeToy/4K6OZcaQmW6lyXXKhq7csREXcDwk2omFPJEwUNOVtJKvmDr9EI1fAJZUyZcRF7kxGBWmRXudrCQ==", + "dev": true, + "dependencies": { + "camelcase": "^5.0.0", + "decamelize": "^1.2.0" + }, + "engines": { + "node": ">=6" + } + }, + "node_modules/yn": { + "version": "3.1.1", + "resolved": "https://registry.npmjs.org/yn/-/yn-3.1.1.tgz", + "integrity": "sha512-Ux4ygGWsu2c7isFWe8Yu1YluJmqVhxqK2cLXNQA5AcC3QfbGNpM7fu0Y8b/z16pXLnFxZYvWhd3fhBY9DLmC6Q==", + "dev": true, + "engines": { + "node": ">=6" + } + }, + "node_modules/yocto-queue": { + "version": "0.1.0", "resolved": "https://registry.npmjs.org/yocto-queue/-/yocto-queue-0.1.0.tgz", "integrity": "sha512-rVksvsnNCdJ/ohGc6xgPwyN8eheCxsiLM8mxuE/t/mOVqJewPuO1miLpTHQiRgTKCLexL4MeAFVagts7HmNZ2Q==", "dev": true, @@ -7841,7 +8016,7 @@ }, "packages/client": { "name": "@redis/client", - "version": "1.1.0", + "version": "1.1.1", "license": "MIT", "dependencies": { "cluster-key-slot": "1.1.0", @@ -7949,6 +8124,30 @@ "@redis/client": "^1.0.0" } }, + "packages/test-utils/node_modules/ansi-regex": { + "version": "5.0.1", + "resolved": "https://registry.npmjs.org/ansi-regex/-/ansi-regex-5.0.1.tgz", + "integrity": "sha512-quJQXlTSUGL2LH9SUXo8VwsY4soanhgo6LNSm84E1LBcE8s3O0wpdiRzyR9z/ZZJMlMWv37qOOb9pdJlMUEKFQ==", + "dev": true, + "engines": { + "node": ">=8" + } + }, + "packages/test-utils/node_modules/ansi-styles": { + "version": "4.3.0", + "resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-4.3.0.tgz", + "integrity": "sha512-zbB9rCJAT1rbjiVDb2hqKFHNYLxgtk8NURxZ3IZwD3F6NtxbXZQCnnSi1Lkx+IDohdPlFp222wVALIheZJQSEg==", + "dev": true, + "dependencies": { + "color-convert": "^2.0.1" + }, + "engines": { + "node": ">=8" + }, + "funding": { + "url": "https://github.com/chalk/ansi-styles?sponsor=1" + } + }, "packages/test-utils/node_modules/cliui": { "version": "7.0.4", "resolved": "https://registry.npmjs.org/cliui/-/cliui-7.0.4.tgz", @@ -7960,6 +8159,55 @@ "wrap-ansi": "^7.0.0" } }, + "packages/test-utils/node_modules/emoji-regex": { + "version": "8.0.0", + "resolved": "https://registry.npmjs.org/emoji-regex/-/emoji-regex-8.0.0.tgz", + "integrity": "sha512-MSjYzcWNOA0ewAHpz0MxpYFvwg6yjy1NG3xteoqz644VCo/RPgnr1/GGt+ic3iJTzQ8Eu3TdM14SawnVUmGE6A==", + "dev": true + }, + "packages/test-utils/node_modules/string-width": { + "version": "4.2.3", + "resolved": "https://registry.npmjs.org/string-width/-/string-width-4.2.3.tgz", + "integrity": "sha512-wKyQRQpjJ0sIp62ErSZdGsjMJWsap5oRNihHhu6G7JVO/9jIB6UyevL+tXuOqrng8j/cxKTWyWUwvSTriiZz/g==", + "dev": true, + "dependencies": { + "emoji-regex": "^8.0.0", + "is-fullwidth-code-point": "^3.0.0", + "strip-ansi": "^6.0.1" + }, + "engines": { + "node": ">=8" + } + }, + "packages/test-utils/node_modules/strip-ansi": { + "version": "6.0.1", + "resolved": "https://registry.npmjs.org/strip-ansi/-/strip-ansi-6.0.1.tgz", + "integrity": "sha512-Y38VPSHcqkFrCpFnQ9vuSXmquuv5oXOKpGeT6aGrr3o3Gc9AlVa6JBfUSOCnbxGGZF+/0ooI7KrPuUSztUdU5A==", + "dev": true, + "dependencies": { + "ansi-regex": "^5.0.1" + }, + "engines": { + "node": ">=8" + } + }, + "packages/test-utils/node_modules/wrap-ansi": { + "version": "7.0.0", + "resolved": "https://registry.npmjs.org/wrap-ansi/-/wrap-ansi-7.0.0.tgz", + "integrity": "sha512-YVGIj2kamLSTxw6NsZjoBxfSwsn0ycdesmc4p+Q21c5zPuZ1pl+NfxVdxPtdHvmNVOQ6XSYG4AUtyt/Fi7D16Q==", + "dev": true, + "dependencies": { + "ansi-styles": "^4.0.0", + "string-width": "^4.1.0", + "strip-ansi": "^6.0.0" + }, + "engines": { + "node": ">=10" + }, + "funding": { + "url": "https://github.com/chalk/wrap-ansi?sponsor=1" + } + }, "packages/test-utils/node_modules/y18n": { "version": "5.0.8", "resolved": "https://registry.npmjs.org/y18n/-/y18n-5.0.8.tgz", @@ -8019,36 +8267,36 @@ } }, "@babel/code-frame": { - "version": "7.16.7", - "resolved": "https://registry.npmjs.org/@babel/code-frame/-/code-frame-7.16.7.tgz", - "integrity": "sha512-iAXqUn8IIeBTNd72xsFlgaXHkMBMt6y4HJp1tIaK465CWLT/fG1aqB7ykr95gHHmlBdGbFeWWfyB4NJJ0nmeIg==", + "version": "7.18.6", + "resolved": "https://registry.npmjs.org/@babel/code-frame/-/code-frame-7.18.6.tgz", + "integrity": "sha512-TDCmlK5eOvH+eH7cdAFlNXeVJqWIQ7gW9tY1GJIpUtFb6CmjVyq2VM3u71bOyR8CRihcCgMUYoDNyLXao3+70Q==", "dev": true, "requires": { - "@babel/highlight": "^7.16.7" + "@babel/highlight": "^7.18.6" } }, "@babel/compat-data": { - "version": "7.17.10", - "resolved": "https://registry.npmjs.org/@babel/compat-data/-/compat-data-7.17.10.tgz", - "integrity": "sha512-GZt/TCsG70Ms19gfZO1tM4CVnXsPgEPBCpJu+Qz3L0LUDsY5nZqFZglIoPC1kIYOtNBZlrnFT+klg12vFGZXrw==", + "version": "7.18.6", + "resolved": "https://registry.npmjs.org/@babel/compat-data/-/compat-data-7.18.6.tgz", + "integrity": "sha512-tzulrgDT0QD6U7BJ4TKVk2SDDg7wlP39P9yAx1RfLy7vP/7rsDRlWVfbWxElslu56+r7QOhB2NSDsabYYruoZQ==", "dev": true }, "@babel/core": { - "version": "7.17.10", - "resolved": "https://registry.npmjs.org/@babel/core/-/core-7.17.10.tgz", - "integrity": "sha512-liKoppandF3ZcBnIYFjfSDHZLKdLHGJRkoWtG8zQyGJBQfIYobpnVGI5+pLBNtS6psFLDzyq8+h5HiVljW9PNA==", + "version": "7.18.6", + "resolved": "https://registry.npmjs.org/@babel/core/-/core-7.18.6.tgz", + "integrity": "sha512-cQbWBpxcbbs/IUredIPkHiAGULLV8iwgNRMFzvbhEXISp4f3rUUXE5+TIw6KwUWUR3DwyI6gmBRnmAtYaWehwQ==", "dev": true, "requires": { "@ampproject/remapping": "^2.1.0", - "@babel/code-frame": "^7.16.7", - "@babel/generator": "^7.17.10", - "@babel/helper-compilation-targets": "^7.17.10", - "@babel/helper-module-transforms": "^7.17.7", - "@babel/helpers": "^7.17.9", - "@babel/parser": "^7.17.10", - "@babel/template": "^7.16.7", - "@babel/traverse": "^7.17.10", - "@babel/types": "^7.17.10", + "@babel/code-frame": "^7.18.6", + "@babel/generator": "^7.18.6", + "@babel/helper-compilation-targets": "^7.18.6", + "@babel/helper-module-transforms": "^7.18.6", + "@babel/helpers": "^7.18.6", + "@babel/parser": "^7.18.6", + "@babel/template": "^7.18.6", + "@babel/traverse": "^7.18.6", + "@babel/types": "^7.18.6", "convert-source-map": "^1.7.0", "debug": "^4.1.0", "gensync": "^1.0.0-beta.2", @@ -8057,129 +8305,139 @@ } }, "@babel/generator": { - "version": "7.17.10", - "resolved": "https://registry.npmjs.org/@babel/generator/-/generator-7.17.10.tgz", - "integrity": "sha512-46MJZZo9y3o4kmhBVc7zW7i8dtR1oIK/sdO5NcfcZRhTGYi+KKJRtHNgsU6c4VUcJmUNV/LQdebD/9Dlv4K+Tg==", + "version": "7.18.7", + "resolved": "https://registry.npmjs.org/@babel/generator/-/generator-7.18.7.tgz", + "integrity": "sha512-shck+7VLlY72a2w9c3zYWuE1pwOKEiQHV7GTUbSnhyl5eu3i04t30tBY82ZRWrDfo3gkakCFtevExnxbkf2a3A==", "dev": true, "requires": { - "@babel/types": "^7.17.10", - "@jridgewell/gen-mapping": "^0.1.0", + "@babel/types": "^7.18.7", + "@jridgewell/gen-mapping": "^0.3.2", "jsesc": "^2.5.1" + }, + "dependencies": { + "@jridgewell/gen-mapping": { + "version": "0.3.2", + "resolved": "https://registry.npmjs.org/@jridgewell/gen-mapping/-/gen-mapping-0.3.2.tgz", + "integrity": "sha512-mh65xKQAzI6iBcFzwv28KVWSmCkdRBWoOh+bYQGW3+6OZvbbN3TqMGo5hqYxQniRcH9F2VZIoJCm4pa3BPDK/A==", + "dev": true, + "requires": { + "@jridgewell/set-array": "^1.0.1", + "@jridgewell/sourcemap-codec": "^1.4.10", + "@jridgewell/trace-mapping": "^0.3.9" + } + } } }, "@babel/helper-compilation-targets": { - "version": "7.17.10", - "resolved": "https://registry.npmjs.org/@babel/helper-compilation-targets/-/helper-compilation-targets-7.17.10.tgz", - "integrity": "sha512-gh3RxjWbauw/dFiU/7whjd0qN9K6nPJMqe6+Er7rOavFh0CQUSwhAE3IcTho2rywPJFxej6TUUHDkWcYI6gGqQ==", + "version": "7.18.6", + "resolved": "https://registry.npmjs.org/@babel/helper-compilation-targets/-/helper-compilation-targets-7.18.6.tgz", + "integrity": "sha512-vFjbfhNCzqdeAtZflUFrG5YIFqGTqsctrtkZ1D/NB0mDW9TwW3GmmUepYY4G9wCET5rY5ugz4OGTcLd614IzQg==", "dev": true, "requires": { - "@babel/compat-data": "^7.17.10", - "@babel/helper-validator-option": "^7.16.7", + "@babel/compat-data": "^7.18.6", + "@babel/helper-validator-option": "^7.18.6", "browserslist": "^4.20.2", "semver": "^6.3.0" } }, "@babel/helper-environment-visitor": { - "version": "7.16.7", - "resolved": "https://registry.npmjs.org/@babel/helper-environment-visitor/-/helper-environment-visitor-7.16.7.tgz", - "integrity": "sha512-SLLb0AAn6PkUeAfKJCCOl9e1R53pQlGAfc4y4XuMRZfqeMYLE0dM1LMhqbGAlGQY0lfw5/ohoYWAe9V1yibRag==", - "dev": true, - "requires": { - "@babel/types": "^7.16.7" - } + "version": "7.18.6", + "resolved": "https://registry.npmjs.org/@babel/helper-environment-visitor/-/helper-environment-visitor-7.18.6.tgz", + "integrity": "sha512-8n6gSfn2baOY+qlp+VSzsosjCVGFqWKmDF0cCWOybh52Dw3SEyoWR1KrhMJASjLwIEkkAufZ0xvr+SxLHSpy2Q==", + "dev": true }, "@babel/helper-function-name": { - "version": "7.17.9", - "resolved": "https://registry.npmjs.org/@babel/helper-function-name/-/helper-function-name-7.17.9.tgz", - "integrity": "sha512-7cRisGlVtiVqZ0MW0/yFB4atgpGLWEHUVYnb448hZK4x+vih0YO5UoS11XIYtZYqHd0dIPMdUSv8q5K4LdMnIg==", + "version": "7.18.6", + "resolved": "https://registry.npmjs.org/@babel/helper-function-name/-/helper-function-name-7.18.6.tgz", + "integrity": "sha512-0mWMxV1aC97dhjCah5U5Ua7668r5ZmSC2DLfH2EZnf9c3/dHZKiFa5pRLMH5tjSl471tY6496ZWk/kjNONBxhw==", "dev": true, "requires": { - "@babel/template": "^7.16.7", - "@babel/types": "^7.17.0" + "@babel/template": "^7.18.6", + "@babel/types": "^7.18.6" } }, "@babel/helper-hoist-variables": { - "version": "7.16.7", - "resolved": "https://registry.npmjs.org/@babel/helper-hoist-variables/-/helper-hoist-variables-7.16.7.tgz", - "integrity": "sha512-m04d/0Op34H5v7pbZw6pSKP7weA6lsMvfiIAMeIvkY/R4xQtBSMFEigu9QTZ2qB/9l22vsxtM8a+Q8CzD255fg==", + "version": "7.18.6", + "resolved": "https://registry.npmjs.org/@babel/helper-hoist-variables/-/helper-hoist-variables-7.18.6.tgz", + "integrity": "sha512-UlJQPkFqFULIcyW5sbzgbkxn2FKRgwWiRexcuaR8RNJRy8+LLveqPjwZV/bwrLZCN0eUHD/x8D0heK1ozuoo6Q==", "dev": true, "requires": { - "@babel/types": "^7.16.7" + "@babel/types": "^7.18.6" } }, "@babel/helper-module-imports": { - "version": "7.16.7", - "resolved": "https://registry.npmjs.org/@babel/helper-module-imports/-/helper-module-imports-7.16.7.tgz", - "integrity": "sha512-LVtS6TqjJHFc+nYeITRo6VLXve70xmq7wPhWTqDJusJEgGmkAACWwMiTNrvfoQo6hEhFwAIixNkvB0jPXDL8Wg==", + "version": "7.18.6", + "resolved": "https://registry.npmjs.org/@babel/helper-module-imports/-/helper-module-imports-7.18.6.tgz", + "integrity": "sha512-0NFvs3VkuSYbFi1x2Vd6tKrywq+z/cLeYC/RJNFrIX/30Bf5aiGYbtvGXolEktzJH8o5E5KJ3tT+nkxuuZFVlA==", "dev": true, "requires": { - "@babel/types": "^7.16.7" + "@babel/types": "^7.18.6" } }, "@babel/helper-module-transforms": { - "version": "7.17.7", - "resolved": "https://registry.npmjs.org/@babel/helper-module-transforms/-/helper-module-transforms-7.17.7.tgz", - "integrity": "sha512-VmZD99F3gNTYB7fJRDTi+u6l/zxY0BE6OIxPSU7a50s6ZUQkHwSDmV92FfM+oCG0pZRVojGYhkR8I0OGeCVREw==", + "version": "7.18.6", + "resolved": "https://registry.npmjs.org/@babel/helper-module-transforms/-/helper-module-transforms-7.18.6.tgz", + "integrity": "sha512-L//phhB4al5uucwzlimruukHB3jRd5JGClwRMD/ROrVjXfLqovYnvQrK/JK36WYyVwGGO7OD3kMyVTjx+WVPhw==", "dev": true, "requires": { - "@babel/helper-environment-visitor": "^7.16.7", - "@babel/helper-module-imports": "^7.16.7", - "@babel/helper-simple-access": "^7.17.7", - "@babel/helper-split-export-declaration": "^7.16.7", - "@babel/helper-validator-identifier": "^7.16.7", - "@babel/template": "^7.16.7", - "@babel/traverse": "^7.17.3", - "@babel/types": "^7.17.0" + "@babel/helper-environment-visitor": "^7.18.6", + "@babel/helper-module-imports": "^7.18.6", + "@babel/helper-simple-access": "^7.18.6", + "@babel/helper-split-export-declaration": "^7.18.6", + "@babel/helper-validator-identifier": "^7.18.6", + "@babel/template": "^7.18.6", + "@babel/traverse": "^7.18.6", + "@babel/types": "^7.18.6" } }, "@babel/helper-simple-access": { - "version": "7.17.7", - "resolved": "https://registry.npmjs.org/@babel/helper-simple-access/-/helper-simple-access-7.17.7.tgz", - "integrity": "sha512-txyMCGroZ96i+Pxr3Je3lzEJjqwaRC9buMUgtomcrLe5Nd0+fk1h0LLA+ixUF5OW7AhHuQ7Es1WcQJZmZsz2XA==", + "version": "7.18.6", + "resolved": "https://registry.npmjs.org/@babel/helper-simple-access/-/helper-simple-access-7.18.6.tgz", + "integrity": "sha512-iNpIgTgyAvDQpDj76POqg+YEt8fPxx3yaNBg3S30dxNKm2SWfYhD0TGrK/Eu9wHpUW63VQU894TsTg+GLbUa1g==", "dev": true, "requires": { - "@babel/types": "^7.17.0" + "@babel/types": "^7.18.6" } }, "@babel/helper-split-export-declaration": { - "version": "7.16.7", - "resolved": "https://registry.npmjs.org/@babel/helper-split-export-declaration/-/helper-split-export-declaration-7.16.7.tgz", - "integrity": "sha512-xbWoy/PFoxSWazIToT9Sif+jJTlrMcndIsaOKvTA6u7QEo7ilkRZpjew18/W3c7nm8fXdUDXh02VXTbZ0pGDNw==", + "version": "7.18.6", + "resolved": "https://registry.npmjs.org/@babel/helper-split-export-declaration/-/helper-split-export-declaration-7.18.6.tgz", + "integrity": "sha512-bde1etTx6ZyTmobl9LLMMQsaizFVZrquTEHOqKeQESMKo4PlObf+8+JA25ZsIpZhT/WEd39+vOdLXAFG/nELpA==", "dev": true, "requires": { - "@babel/types": "^7.16.7" + "@babel/types": "^7.18.6" } }, "@babel/helper-validator-identifier": { - "version": "7.16.7", - "resolved": "https://registry.npmjs.org/@babel/helper-validator-identifier/-/helper-validator-identifier-7.16.7.tgz", - "integrity": "sha512-hsEnFemeiW4D08A5gUAZxLBTXpZ39P+a+DGDsHw1yxqyQ/jzFEnxf5uTEGp+3bzAbNOxU1paTgYS4ECU/IgfDw==", + "version": "7.18.6", + "resolved": "https://registry.npmjs.org/@babel/helper-validator-identifier/-/helper-validator-identifier-7.18.6.tgz", + "integrity": "sha512-MmetCkz9ej86nJQV+sFCxoGGrUbU3q02kgLciwkrt9QqEB7cP39oKEY0PakknEO0Gu20SskMRi+AYZ3b1TpN9g==", "dev": true }, "@babel/helper-validator-option": { - "version": "7.16.7", - "resolved": "https://registry.npmjs.org/@babel/helper-validator-option/-/helper-validator-option-7.16.7.tgz", - "integrity": "sha512-TRtenOuRUVo9oIQGPC5G9DgK4743cdxvtOw0weQNpZXaS16SCBi5MNjZF8vba3ETURjZpTbVn7Vvcf2eAwFozQ==", + "version": "7.18.6", + "resolved": "https://registry.npmjs.org/@babel/helper-validator-option/-/helper-validator-option-7.18.6.tgz", + "integrity": "sha512-XO7gESt5ouv/LRJdrVjkShckw6STTaB7l9BrpBaAHDeF5YZT+01PCwmR0SJHnkW6i8OwW/EVWRShfi4j2x+KQw==", "dev": true }, "@babel/helpers": { - "version": "7.17.9", - "resolved": "https://registry.npmjs.org/@babel/helpers/-/helpers-7.17.9.tgz", - "integrity": "sha512-cPCt915ShDWUEzEp3+UNRktO2n6v49l5RSnG9M5pS24hA+2FAc5si+Pn1i4VVbQQ+jh+bIZhPFQOJOzbrOYY1Q==", + "version": "7.18.6", + "resolved": "https://registry.npmjs.org/@babel/helpers/-/helpers-7.18.6.tgz", + "integrity": "sha512-vzSiiqbQOghPngUYt/zWGvK3LAsPhz55vc9XNN0xAl2gV4ieShI2OQli5duxWHD+72PZPTKAcfcZDE1Cwc5zsQ==", "dev": true, "requires": { - "@babel/template": "^7.16.7", - "@babel/traverse": "^7.17.9", - "@babel/types": "^7.17.0" + "@babel/template": "^7.18.6", + "@babel/traverse": "^7.18.6", + "@babel/types": "^7.18.6" } }, "@babel/highlight": { - "version": "7.17.9", - "resolved": "https://registry.npmjs.org/@babel/highlight/-/highlight-7.17.9.tgz", - "integrity": "sha512-J9PfEKCbFIv2X5bjTMiZu6Vf341N05QIY+d6FvVKynkG1S7G0j3I0QoRtWIrXhZ+/Nlb5Q0MzqL7TokEJ5BNHg==", + "version": "7.18.6", + "resolved": "https://registry.npmjs.org/@babel/highlight/-/highlight-7.18.6.tgz", + "integrity": "sha512-u7stbOuYjaPezCuLj29hNW1v64M2Md2qupEKP1fHc7WdOA3DgLh37suiSrZYY7haUB7iBeQZ9P1uiRF359do3g==", "dev": true, "requires": { - "@babel/helper-validator-identifier": "^7.16.7", + "@babel/helper-validator-identifier": "^7.18.6", "chalk": "^2.0.0", "js-tokens": "^4.0.0" }, @@ -8216,13 +8474,19 @@ "color-name": { "version": "1.1.3", "resolved": "https://registry.npmjs.org/color-name/-/color-name-1.1.3.tgz", - "integrity": "sha1-p9BVi9icQveV3UIyj3QIMcpTvCU=", + "integrity": "sha512-72fSenhMw2HZMTVHeCA9KCmpEIbzWiQsjN+BHcBbS9vr1mtt+vJjPdksIBNUmKAW8TFUDPJK5SUU3QhE9NEXDw==", + "dev": true + }, + "escape-string-regexp": { + "version": "1.0.5", + "resolved": "https://registry.npmjs.org/escape-string-regexp/-/escape-string-regexp-1.0.5.tgz", + "integrity": "sha512-vbRorB5FUQWvla16U8R/qgaFIya2qGzwDrNmCZuYKrbdSUMG6I1ZCGQRefkRVhuOkIGVne7BQ35DSfo1qvJqFg==", "dev": true }, "has-flag": { "version": "3.0.0", "resolved": "https://registry.npmjs.org/has-flag/-/has-flag-3.0.0.tgz", - "integrity": "sha1-tdRU3CGZriJWmfNGfloH87lVuv0=", + "integrity": "sha512-sKJf1+ceQBr4SMkvQnBDNDtf4TXpVhVGateu0t918bl30FnbE2m4vNLX+VWe/dpjlb+HugGYzW7uQXH98HPEYw==", "dev": true }, "supports-color": { @@ -8237,36 +8501,36 @@ } }, "@babel/parser": { - "version": "7.17.10", - "resolved": "https://registry.npmjs.org/@babel/parser/-/parser-7.17.10.tgz", - "integrity": "sha512-n2Q6i+fnJqzOaq2VkdXxy2TCPCWQZHiCo0XqmrCvDWcZQKRyZzYi4Z0yxlBuN0w+r2ZHmre+Q087DSrw3pbJDQ==", + "version": "7.18.6", + "resolved": "https://registry.npmjs.org/@babel/parser/-/parser-7.18.6.tgz", + "integrity": "sha512-uQVSa9jJUe/G/304lXspfWVpKpK4euFLgGiMQFOCpM/bgcAdeoHwi/OQz23O9GK2osz26ZiXRRV9aV+Yl1O8tw==", "dev": true }, "@babel/template": { - "version": "7.16.7", - "resolved": "https://registry.npmjs.org/@babel/template/-/template-7.16.7.tgz", - "integrity": "sha512-I8j/x8kHUrbYRTUxXrrMbfCa7jxkE7tZre39x3kjr9hvI82cK1FfqLygotcWN5kdPGWcLdWMHpSBavse5tWw3w==", + "version": "7.18.6", + "resolved": "https://registry.npmjs.org/@babel/template/-/template-7.18.6.tgz", + "integrity": "sha512-JoDWzPe+wgBsTTgdnIma3iHNFC7YVJoPssVBDjiHfNlyt4YcunDtcDOUmfVDfCK5MfdsaIoX9PkijPhjH3nYUw==", "dev": true, "requires": { - "@babel/code-frame": "^7.16.7", - "@babel/parser": "^7.16.7", - "@babel/types": "^7.16.7" + "@babel/code-frame": "^7.18.6", + "@babel/parser": "^7.18.6", + "@babel/types": "^7.18.6" } }, "@babel/traverse": { - "version": "7.17.10", - "resolved": "https://registry.npmjs.org/@babel/traverse/-/traverse-7.17.10.tgz", - "integrity": "sha512-VmbrTHQteIdUUQNTb+zE12SHS/xQVIShmBPhlNP12hD5poF2pbITW1Z4172d03HegaQWhLffdkRJYtAzp0AGcw==", - "dev": true, - "requires": { - "@babel/code-frame": "^7.16.7", - "@babel/generator": "^7.17.10", - "@babel/helper-environment-visitor": "^7.16.7", - "@babel/helper-function-name": "^7.17.9", - "@babel/helper-hoist-variables": "^7.16.7", - "@babel/helper-split-export-declaration": "^7.16.7", - "@babel/parser": "^7.17.10", - "@babel/types": "^7.17.10", + "version": "7.18.6", + "resolved": "https://registry.npmjs.org/@babel/traverse/-/traverse-7.18.6.tgz", + "integrity": "sha512-zS/OKyqmD7lslOtFqbscH6gMLFYOfG1YPqCKfAW5KrTeolKqvB8UelR49Fpr6y93kYkW2Ik00mT1LOGiAGvizw==", + "dev": true, + "requires": { + "@babel/code-frame": "^7.18.6", + "@babel/generator": "^7.18.6", + "@babel/helper-environment-visitor": "^7.18.6", + "@babel/helper-function-name": "^7.18.6", + "@babel/helper-hoist-variables": "^7.18.6", + "@babel/helper-split-export-declaration": "^7.18.6", + "@babel/parser": "^7.18.6", + "@babel/types": "^7.18.6", "debug": "^4.1.0", "globals": "^11.1.0" }, @@ -8280,12 +8544,12 @@ } }, "@babel/types": { - "version": "7.17.10", - "resolved": "https://registry.npmjs.org/@babel/types/-/types-7.17.10.tgz", - "integrity": "sha512-9O26jG0mBYfGkUYCYZRnBwbVLd1UZOICEr2Em6InB6jVfsAv1GKgwXHmrSg+WFWDmeKTA6vyTZiN8tCSM5Oo3A==", + "version": "7.18.7", + "resolved": "https://registry.npmjs.org/@babel/types/-/types-7.18.7.tgz", + "integrity": "sha512-QG3yxTcTIBoAcQmkCs+wAPYZhu7Dk9rXKacINfNbdJDNERTbLQbHGyVG8q/YGMPeCJRIhSY0+fTc5+xuh6WPSQ==", "dev": true, "requires": { - "@babel/helper-validator-identifier": "^7.16.7", + "@babel/helper-validator-identifier": "^7.18.6", "to-fast-properties": "^2.0.0" } }, @@ -8296,6 +8560,18 @@ "dev": true, "requires": { "@jridgewell/trace-mapping": "0.3.9" + }, + "dependencies": { + "@jridgewell/trace-mapping": { + "version": "0.3.9", + "resolved": "https://registry.npmjs.org/@jridgewell/trace-mapping/-/trace-mapping-0.3.9.tgz", + "integrity": "sha512-3Belt6tdc8bPgAtbcmdtNJlirVoTmEb5e2gC94PnkwEW9jI6CAHUeoG85tjWP5WquqfavoMtMwiG4P926ZKKuQ==", + "dev": true, + "requires": { + "@jridgewell/resolve-uri": "^3.0.3", + "@jridgewell/sourcemap-codec": "^1.4.10" + } + } } }, "@eslint/eslintrc": { @@ -8404,27 +8680,27 @@ } }, "@jridgewell/resolve-uri": { - "version": "3.0.6", - "resolved": "https://registry.npmjs.org/@jridgewell/resolve-uri/-/resolve-uri-3.0.6.tgz", - "integrity": "sha512-R7xHtBSNm+9SyvpJkdQl+qrM3Hm2fea3Ef197M3mUug+v+yR+Rhfbs7PBtcBUVnIWJ4JcAdjvij+c8hXS9p5aw==", + "version": "3.0.8", + "resolved": "https://registry.npmjs.org/@jridgewell/resolve-uri/-/resolve-uri-3.0.8.tgz", + "integrity": "sha512-YK5G9LaddzGbcucK4c8h5tWFmMPBvRZ/uyWmN1/SbBdIvqGUdWGkJ5BAaccgs6XbzVLsqbPJrBSFwKv3kT9i7w==", "dev": true }, "@jridgewell/set-array": { - "version": "1.1.0", - "resolved": "https://registry.npmjs.org/@jridgewell/set-array/-/set-array-1.1.0.tgz", - "integrity": "sha512-SfJxIxNVYLTsKwzB3MoOQ1yxf4w/E6MdkvTgrgAt1bfxjSrLUoHMKrDOykwN14q65waezZIdqDneUIPh4/sKxg==", + "version": "1.1.2", + "resolved": "https://registry.npmjs.org/@jridgewell/set-array/-/set-array-1.1.2.tgz", + "integrity": "sha512-xnkseuNADM0gt2bs+BvhO0p78Mk762YnZdsuzFV018NoG1Sj1SCQvpSqa7XUaTam5vAGasABV9qXASMKnFMwMw==", "dev": true }, "@jridgewell/sourcemap-codec": { - "version": "1.4.11", - "resolved": "https://registry.npmjs.org/@jridgewell/sourcemap-codec/-/sourcemap-codec-1.4.11.tgz", - "integrity": "sha512-Fg32GrJo61m+VqYSdRSjRXMjQ06j8YIYfcTqndLYVAaHmroZHLJZCydsWBOTDqXS2v+mjxohBWEMfg97GXmYQg==", + "version": "1.4.14", + "resolved": "https://registry.npmjs.org/@jridgewell/sourcemap-codec/-/sourcemap-codec-1.4.14.tgz", + "integrity": "sha512-XPSJHWmi394fuUuzDnGz1wiKqWfo1yXecHQMRf2l6hztTO+nPru658AyDngaBe7isIxEkRsPR3FZh+s7iVa4Uw==", "dev": true }, "@jridgewell/trace-mapping": { - "version": "0.3.9", - "resolved": "https://registry.npmjs.org/@jridgewell/trace-mapping/-/trace-mapping-0.3.9.tgz", - "integrity": "sha512-3Belt6tdc8bPgAtbcmdtNJlirVoTmEb5e2gC94PnkwEW9jI6CAHUeoG85tjWP5WquqfavoMtMwiG4P926ZKKuQ==", + "version": "0.3.14", + "resolved": "https://registry.npmjs.org/@jridgewell/trace-mapping/-/trace-mapping-0.3.14.tgz", + "integrity": "sha512-bJWEfQ9lPTvm3SneWwRFVLzrh6nhjwqw7TUFFBEMzwvg7t7PCDenf2lDwqo4NQXzdpgBXyFgDWnQA+2vkruksQ==", "dev": true, "requires": { "@jridgewell/resolve-uri": "^3.0.3", @@ -8504,18 +8780,18 @@ } }, "@octokit/openapi-types": { - "version": "11.2.0", - "resolved": "https://registry.npmjs.org/@octokit/openapi-types/-/openapi-types-11.2.0.tgz", - "integrity": "sha512-PBsVO+15KSlGmiI8QAzaqvsNlZlrDlyAJYcrXBCvVUxCp7VnXjkwPoFHgjEJXx3WF9BAwkA6nfCUA7i9sODzKA==", + "version": "12.5.0", + "resolved": "https://registry.npmjs.org/@octokit/openapi-types/-/openapi-types-12.5.0.tgz", + "integrity": "sha512-VatvE5wtRkJq6hAWGTBZ62WkrdlCiy0G0u27cVOYTfAWVZi7QqTurVcjpsyc5+9hXLPRP5O/DaNEs4TgAp4Mqg==", "dev": true }, "@octokit/plugin-paginate-rest": { - "version": "2.17.0", - "resolved": "https://registry.npmjs.org/@octokit/plugin-paginate-rest/-/plugin-paginate-rest-2.17.0.tgz", - "integrity": "sha512-tzMbrbnam2Mt4AhuyCHvpRkS0oZ5MvwwcQPYGtMv4tUa5kkzG58SVB0fcsLulOZQeRnOgdkZWkRUiyBlh0Bkyw==", + "version": "2.21.0", + "resolved": "https://registry.npmjs.org/@octokit/plugin-paginate-rest/-/plugin-paginate-rest-2.21.0.tgz", + "integrity": "sha512-MoGEKjvDpTOCVb5gbeiW7kZm/cRfT256UJwHEuy+y+gTUuKziyXaiOkt5rM/4nzhp8UxVgvok9Tu7dMMpUybiQ==", "dev": true, "requires": { - "@octokit/types": "^6.34.0" + "@octokit/types": "^6.38.1" } }, "@octokit/plugin-request-log": { @@ -8526,12 +8802,12 @@ "requires": {} }, "@octokit/plugin-rest-endpoint-methods": { - "version": "5.13.0", - "resolved": "https://registry.npmjs.org/@octokit/plugin-rest-endpoint-methods/-/plugin-rest-endpoint-methods-5.13.0.tgz", - "integrity": "sha512-uJjMTkN1KaOIgNtUPMtIXDOjx6dGYysdIFhgA52x4xSadQCz3b/zJexvITDVpANnfKPW/+E0xkOvLntqMYpviA==", + "version": "5.16.0", + "resolved": "https://registry.npmjs.org/@octokit/plugin-rest-endpoint-methods/-/plugin-rest-endpoint-methods-5.16.0.tgz", + "integrity": "sha512-mvdwq+LvhR2GRDY82FgSZ52xX6wkOCpjiI3amiKbzKHd9nyKeFdXLsIQ3Go12tWRtvo+HwqoypLHDjRrgMFDQA==", "dev": true, "requires": { - "@octokit/types": "^6.34.0", + "@octokit/types": "^6.38.0", "deprecation": "^2.3.1" } }, @@ -8573,12 +8849,12 @@ } }, "@octokit/types": { - "version": "6.34.0", - "resolved": "https://registry.npmjs.org/@octokit/types/-/types-6.34.0.tgz", - "integrity": "sha512-s1zLBjWhdEI2zwaoSgyOFoKSl109CUcVBCc7biPJ3aAf6LGLU6szDvi31JPU7bxfla2lqfhjbbg/5DdFNxOwHw==", + "version": "6.38.1", + "resolved": "https://registry.npmjs.org/@octokit/types/-/types-6.38.1.tgz", + "integrity": "sha512-kWMohLCIvnwApRmxRFDOqve7puiNNdtVfgwdDOm6QyJNorWOgKv2/AodCcGqx63o28kF7Dr4/nJCatrwwqhULg==", "dev": true, "requires": { - "@octokit/openapi-types": "^11.2.0" + "@octokit/openapi-types": "^12.5.0" } }, "@pnpm/network.ca-file": { @@ -8701,6 +8977,21 @@ "yargs": "^17.5.1" }, "dependencies": { + "ansi-regex": { + "version": "5.0.1", + "resolved": "https://registry.npmjs.org/ansi-regex/-/ansi-regex-5.0.1.tgz", + "integrity": "sha512-quJQXlTSUGL2LH9SUXo8VwsY4soanhgo6LNSm84E1LBcE8s3O0wpdiRzyR9z/ZZJMlMWv37qOOb9pdJlMUEKFQ==", + "dev": true + }, + "ansi-styles": { + "version": "4.3.0", + "resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-4.3.0.tgz", + "integrity": "sha512-zbB9rCJAT1rbjiVDb2hqKFHNYLxgtk8NURxZ3IZwD3F6NtxbXZQCnnSi1Lkx+IDohdPlFp222wVALIheZJQSEg==", + "dev": true, + "requires": { + "color-convert": "^2.0.1" + } + }, "cliui": { "version": "7.0.4", "resolved": "https://registry.npmjs.org/cliui/-/cliui-7.0.4.tgz", @@ -8712,6 +9003,43 @@ "wrap-ansi": "^7.0.0" } }, + "emoji-regex": { + "version": "8.0.0", + "resolved": "https://registry.npmjs.org/emoji-regex/-/emoji-regex-8.0.0.tgz", + "integrity": "sha512-MSjYzcWNOA0ewAHpz0MxpYFvwg6yjy1NG3xteoqz644VCo/RPgnr1/GGt+ic3iJTzQ8Eu3TdM14SawnVUmGE6A==", + "dev": true + }, + "string-width": { + "version": "4.2.3", + "resolved": "https://registry.npmjs.org/string-width/-/string-width-4.2.3.tgz", + "integrity": "sha512-wKyQRQpjJ0sIp62ErSZdGsjMJWsap5oRNihHhu6G7JVO/9jIB6UyevL+tXuOqrng8j/cxKTWyWUwvSTriiZz/g==", + "dev": true, + "requires": { + "emoji-regex": "^8.0.0", + "is-fullwidth-code-point": "^3.0.0", + "strip-ansi": "^6.0.1" + } + }, + "strip-ansi": { + "version": "6.0.1", + "resolved": "https://registry.npmjs.org/strip-ansi/-/strip-ansi-6.0.1.tgz", + "integrity": "sha512-Y38VPSHcqkFrCpFnQ9vuSXmquuv5oXOKpGeT6aGrr3o3Gc9AlVa6JBfUSOCnbxGGZF+/0ooI7KrPuUSztUdU5A==", + "dev": true, + "requires": { + "ansi-regex": "^5.0.1" + } + }, + "wrap-ansi": { + "version": "7.0.0", + "resolved": "https://registry.npmjs.org/wrap-ansi/-/wrap-ansi-7.0.0.tgz", + "integrity": "sha512-YVGIj2kamLSTxw6NsZjoBxfSwsn0ycdesmc4p+Q21c5zPuZ1pl+NfxVdxPtdHvmNVOQ6XSYG4AUtyt/Fi7D16Q==", + "dev": true, + "requires": { + "ansi-styles": "^4.0.0", + "string-width": "^4.1.0", + "strip-ansi": "^6.0.0" + } + }, "y18n": { "version": "5.0.8", "resolved": "https://registry.npmjs.org/y18n/-/y18n-5.0.8.tgz", @@ -8806,27 +9134,27 @@ "dev": true }, "@tsconfig/node10": { - "version": "1.0.8", - "resolved": "https://registry.npmjs.org/@tsconfig/node10/-/node10-1.0.8.tgz", - "integrity": "sha512-6XFfSQmMgq0CFLY1MslA/CPUfhIL919M1rMsa5lP2P097N2Wd1sSX0tx1u4olM16fLNhtHZpRhedZJphNJqmZg==", + "version": "1.0.9", + "resolved": "https://registry.npmjs.org/@tsconfig/node10/-/node10-1.0.9.tgz", + "integrity": "sha512-jNsYVVxU8v5g43Erja32laIDHXeoNvFEpX33OK4d6hljo3jDhCBDhx5dhCCTMWUojscpAagGiRkBKxpdl9fxqA==", "dev": true }, "@tsconfig/node12": { - "version": "1.0.9", - "resolved": "https://registry.npmjs.org/@tsconfig/node12/-/node12-1.0.9.tgz", - "integrity": "sha512-/yBMcem+fbvhSREH+s14YJi18sp7J9jpuhYByADT2rypfajMZZN4WQ6zBGgBKp53NKmqI36wFYDb3yaMPurITw==", + "version": "1.0.11", + "resolved": "https://registry.npmjs.org/@tsconfig/node12/-/node12-1.0.11.tgz", + "integrity": "sha512-cqefuRsh12pWyGsIoBKJA9luFu3mRxCA+ORZvA4ktLSzIuCUtWVxGIuXigEwO5/ywWFMZ2QEGKWvkZG1zDMTag==", "dev": true }, "@tsconfig/node14": { - "version": "1.0.1", - "resolved": "https://registry.npmjs.org/@tsconfig/node14/-/node14-1.0.1.tgz", - "integrity": "sha512-509r2+yARFfHHE7T6Puu2jjkoycftovhXRqW328PDXTVGKihlb1P8Z9mMZH04ebyajfRY7dedfGynlrFHJUQCg==", + "version": "1.0.3", + "resolved": "https://registry.npmjs.org/@tsconfig/node14/-/node14-1.0.3.tgz", + "integrity": "sha512-ysT8mhdixWK6Hw3i1V2AeRqZ5WfXg1G43mqoYlM2nc6388Fq5jcXyr5mRsqViLx/GJYdoL0bfXD8nmF+Zn/Iow==", "dev": true }, "@tsconfig/node16": { - "version": "1.0.2", - "resolved": "https://registry.npmjs.org/@tsconfig/node16/-/node16-1.0.2.tgz", - "integrity": "sha512-eZxlbI8GZscaGS7kkc/trHTT5xgrjH3/1n2JDwusC9iahPKWMRvRjJSAN5mCXviuTGQ/lHnhvv8Q1YTpnfz9gA==", + "version": "1.0.3", + "resolved": "https://registry.npmjs.org/@tsconfig/node16/-/node16-1.0.3.tgz", + "integrity": "sha512-yOlFc+7UtL/89t2ZhjPvvB/DeAr3r+Dq58IgzsFkOAvVC6NMJXmCGjbptdXdR9qsX7pKcTL+s87FtYREi2dEEQ==", "dev": true }, "@types/cacheable-request": { @@ -9161,6 +9489,40 @@ "dev": true, "requires": { "string-width": "^4.1.0" + }, + "dependencies": { + "ansi-regex": { + "version": "5.0.1", + "resolved": "https://registry.npmjs.org/ansi-regex/-/ansi-regex-5.0.1.tgz", + "integrity": "sha512-quJQXlTSUGL2LH9SUXo8VwsY4soanhgo6LNSm84E1LBcE8s3O0wpdiRzyR9z/ZZJMlMWv37qOOb9pdJlMUEKFQ==", + "dev": true + }, + "emoji-regex": { + "version": "8.0.0", + "resolved": "https://registry.npmjs.org/emoji-regex/-/emoji-regex-8.0.0.tgz", + "integrity": "sha512-MSjYzcWNOA0ewAHpz0MxpYFvwg6yjy1NG3xteoqz644VCo/RPgnr1/GGt+ic3iJTzQ8Eu3TdM14SawnVUmGE6A==", + "dev": true + }, + "string-width": { + "version": "4.2.3", + "resolved": "https://registry.npmjs.org/string-width/-/string-width-4.2.3.tgz", + "integrity": "sha512-wKyQRQpjJ0sIp62ErSZdGsjMJWsap5oRNihHhu6G7JVO/9jIB6UyevL+tXuOqrng8j/cxKTWyWUwvSTriiZz/g==", + "dev": true, + "requires": { + "emoji-regex": "^8.0.0", + "is-fullwidth-code-point": "^3.0.0", + "strip-ansi": "^6.0.1" + } + }, + "strip-ansi": { + "version": "6.0.1", + "resolved": "https://registry.npmjs.org/strip-ansi/-/strip-ansi-6.0.1.tgz", + "integrity": "sha512-Y38VPSHcqkFrCpFnQ9vuSXmquuv5oXOKpGeT6aGrr3o3Gc9AlVa6JBfUSOCnbxGGZF+/0ooI7KrPuUSztUdU5A==", + "dev": true, + "requires": { + "ansi-regex": "^5.0.1" + } + } } }, "ansi-colors": { @@ -9176,30 +9538,19 @@ "dev": true, "requires": { "type-fest": "^1.0.2" - }, - "dependencies": { - "type-fest": { - "version": "1.4.0", - "resolved": "https://registry.npmjs.org/type-fest/-/type-fest-1.4.0.tgz", - "integrity": "sha512-yGSza74xk0UG8k+pLh5oeoYirvIiWo5t0/o3zHHAO2tRDiZcxWP7fywNlXhqb6/r6sWvwi+RsyQMWhVLe4BVuA==", - "dev": true - } } }, "ansi-regex": { - "version": "5.0.1", - "resolved": "https://registry.npmjs.org/ansi-regex/-/ansi-regex-5.0.1.tgz", - "integrity": "sha512-quJQXlTSUGL2LH9SUXo8VwsY4soanhgo6LNSm84E1LBcE8s3O0wpdiRzyR9z/ZZJMlMWv37qOOb9pdJlMUEKFQ==", + "version": "6.0.1", + "resolved": "https://registry.npmjs.org/ansi-regex/-/ansi-regex-6.0.1.tgz", + "integrity": "sha512-n5M855fKb2SsfMIiFFoVrABHJC8QtHwVx+mHWP3QcEqBHYienj5dHSgjbxtC0WEZXYt4wcD6zrQElDPhFuZgfA==", "dev": true }, "ansi-styles": { - "version": "4.3.0", - "resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-4.3.0.tgz", - "integrity": "sha512-zbB9rCJAT1rbjiVDb2hqKFHNYLxgtk8NURxZ3IZwD3F6NtxbXZQCnnSi1Lkx+IDohdPlFp222wVALIheZJQSEg==", - "dev": true, - "requires": { - "color-convert": "^2.0.1" - } + "version": "6.1.0", + "resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-6.1.0.tgz", + "integrity": "sha512-VbqNsoz55SYGczauuup0MFUyXNQviSpFTj1RQtFzmQLk18qbVSpTFFGMT293rmDaQuKCT6InmbuEyUne4mTuxQ==", + "dev": true }, "anymatch": { "version": "3.1.2", @@ -9223,7 +9574,7 @@ "archy": { "version": "1.0.0", "resolved": "https://registry.npmjs.org/archy/-/archy-1.0.0.tgz", - "integrity": "sha1-+cjBN1fMHde8N5rHeyxipcKGjEA=", + "integrity": "sha512-Xg+9RwCg/0p32teKdGMPTPnVXKD0w3DfHnFTficozsAgsvq2XenPJq/MYpzzQ/v8zrOyJn6Ds39VA4JIDwFfqw==", "dev": true }, "arg": { @@ -9241,7 +9592,7 @@ "array-union": { "version": "1.0.2", "resolved": "https://registry.npmjs.org/array-union/-/array-union-1.0.2.tgz", - "integrity": "sha1-mjRBDk9OPaI96jdb5b5w8kd47Dk=", + "integrity": "sha512-Dxr6QJj/RdU/hCaBjOfxW+q6lyuVE6JFWIrAUpuOOhoJJoQ99cUn3igRaHVB5P9WrgFVN0FfArM3x0cueOU8ng==", "dev": true, "requires": { "array-uniq": "^1.0.1" @@ -9250,7 +9601,7 @@ "array-uniq": { "version": "1.0.3", "resolved": "https://registry.npmjs.org/array-uniq/-/array-uniq-1.0.3.tgz", - "integrity": "sha1-r2rId6Jcx/dOBYiUdThY39sk/bY=", + "integrity": "sha512-MNha4BWQ6JbwhFhj03YK552f7cb3AzoE8SzeljgChvL1dl3IcvggXVz1DilzySZkCja+CXuZbdW7yATchWn8/Q==", "dev": true }, "array.prototype.map": { @@ -9296,7 +9647,7 @@ "asynckit": { "version": "0.4.0", "resolved": "https://registry.npmjs.org/asynckit/-/asynckit-0.4.0.tgz", - "integrity": "sha1-x57Zf380y48robyXkLzDZkdLS3k=", + "integrity": "sha512-Oei9OH4tRh0YqU3GxhX79dM/mwVgvbZJaSNaRk+bshkj0S5cfHcgYakreBjrHwatXKbz+IoIdYLxrKim2MjW0Q==", "dev": true }, "balanced-match": { @@ -9350,66 +9701,17 @@ "wrap-ansi": "^8.0.1" }, "dependencies": { - "ansi-regex": { - "version": "6.0.1", - "resolved": "https://registry.npmjs.org/ansi-regex/-/ansi-regex-6.0.1.tgz", - "integrity": "sha512-n5M855fKb2SsfMIiFFoVrABHJC8QtHwVx+mHWP3QcEqBHYienj5dHSgjbxtC0WEZXYt4wcD6zrQElDPhFuZgfA==", - "dev": true - }, - "ansi-styles": { - "version": "6.1.0", - "resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-6.1.0.tgz", - "integrity": "sha512-VbqNsoz55SYGczauuup0MFUyXNQviSpFTj1RQtFzmQLk18qbVSpTFFGMT293rmDaQuKCT6InmbuEyUne4mTuxQ==", - "dev": true - }, "camelcase": { "version": "7.0.0", "resolved": "https://registry.npmjs.org/camelcase/-/camelcase-7.0.0.tgz", "integrity": "sha512-JToIvOmz6nhGsUhAYScbo2d6Py5wojjNfoxoc2mEVLUdJ70gJK2gnd+ABY1Tc3sVMyK7QDPtN0T/XdlCQWITyQ==", "dev": true }, - "emoji-regex": { - "version": "9.2.2", - "resolved": "https://registry.npmjs.org/emoji-regex/-/emoji-regex-9.2.2.tgz", - "integrity": "sha512-L18DaJsXSUk2+42pv8mLs5jJT2hqFkFE4j21wOmgbUqsZ2hL72NsUU785g9RXgo3s0ZNgVl42TiHp3ZtOv/Vyg==", - "dev": true - }, - "string-width": { - "version": "5.1.2", - "resolved": "https://registry.npmjs.org/string-width/-/string-width-5.1.2.tgz", - "integrity": "sha512-HnLOCR3vjcY8beoNLtcjZ5/nxn2afmME6lhrDrebokqMap+XbeW8n9TXpPDOqdGK5qcI3oT0GKTW6wC7EMiVqA==", - "dev": true, - "requires": { - "eastasianwidth": "^0.2.0", - "emoji-regex": "^9.2.2", - "strip-ansi": "^7.0.1" - } - }, - "strip-ansi": { - "version": "7.0.1", - "resolved": "https://registry.npmjs.org/strip-ansi/-/strip-ansi-7.0.1.tgz", - "integrity": "sha512-cXNxvT8dFNRVfhVME3JAe98mkXDYN2O1l7jmcwMnOslDeESg1rF/OZMtK0nRAhiari1unG5cD4jG3rapUAkLbw==", - "dev": true, - "requires": { - "ansi-regex": "^6.0.1" - } - }, "type-fest": { - "version": "2.14.0", - "resolved": "https://registry.npmjs.org/type-fest/-/type-fest-2.14.0.tgz", - "integrity": "sha512-hQnTQkFjL5ik6HF2fTAM8ycbr94UbQXK364wF930VHb0dfBJ5JBP8qwrR8TaK9zwUEk7meruo2JAUDMwvuxd/w==", + "version": "2.15.0", + "resolved": "https://registry.npmjs.org/type-fest/-/type-fest-2.15.0.tgz", + "integrity": "sha512-hpsXfQZrAiusX8KMY5HXSEV7xqGAGxFQoNDT+iW0yJE/bdYG0uMlRaUG0kNAUbF5p6Cq5Kuf69lm4M569QtRGw==", "dev": true - }, - "wrap-ansi": { - "version": "8.0.1", - "resolved": "https://registry.npmjs.org/wrap-ansi/-/wrap-ansi-8.0.1.tgz", - "integrity": "sha512-QFF+ufAqhoYHvoHdajT/Po7KoXVBPXS2bgjIam5isfWJPfIOnQZ50JtUiVvCv/sjgacf3yRrt2ZKUZ/V4itN4g==", - "dev": true, - "requires": { - "ansi-styles": "^6.1.0", - "string-width": "^5.0.1", - "strip-ansi": "^7.0.1" - } } } }, @@ -9439,16 +9741,15 @@ "dev": true }, "browserslist": { - "version": "4.20.3", - "resolved": "https://registry.npmjs.org/browserslist/-/browserslist-4.20.3.tgz", - "integrity": "sha512-NBhymBQl1zM0Y5dQT/O+xiLP9/rzOIQdKM/eMJBAq7yBgaB6krIYLGejrwVYnSHZdqjscB1SPuAjHwxjvN6Wdg==", + "version": "4.21.1", + "resolved": "https://registry.npmjs.org/browserslist/-/browserslist-4.21.1.tgz", + "integrity": "sha512-Nq8MFCSrnJXSc88yliwlzQe3qNe3VntIjhsArW9IJOEPSHNx23FalwApUVbzAWABLhYJJ7y8AynWI/XM8OdfjQ==", "dev": true, "requires": { - "caniuse-lite": "^1.0.30001332", - "electron-to-chromium": "^1.4.118", - "escalade": "^3.1.1", - "node-releases": "^2.0.3", - "picocolors": "^1.0.0" + "caniuse-lite": "^1.0.30001359", + "electron-to-chromium": "^1.4.172", + "node-releases": "^2.0.5", + "update-browserslist-db": "^1.0.4" } }, "buffer": { @@ -9546,9 +9847,9 @@ "dev": true }, "caniuse-lite": { - "version": "1.0.30001335", - "resolved": "https://registry.npmjs.org/caniuse-lite/-/caniuse-lite-1.0.30001335.tgz", - "integrity": "sha512-ddP1Tgm7z2iIxu6QTtbZUv6HJxSaV/PZeSrWFZtbY4JZ69tOeNhBCl3HyRQgeNZKE5AOn1kpV7fhljigy0Ty3w==", + "version": "1.0.30001361", + "resolved": "https://registry.npmjs.org/caniuse-lite/-/caniuse-lite-1.0.30001361.tgz", + "integrity": "sha512-ybhCrjNtkFji1/Wto6SSJKkWk6kZgVQsDq5QI83SafsF6FXv2JB4df9eEdH6g8sdGgqTXrFLjAxqBGgYoU3azQ==", "dev": true }, "chalk": { @@ -9591,9 +9892,9 @@ } }, "ci-info": { - "version": "3.3.0", - "resolved": "https://registry.npmjs.org/ci-info/-/ci-info-3.3.0.tgz", - "integrity": "sha512-riT/3vI5YpVH6/qomlDnJow6TBee2PBKSEpx3O32EGPYbWGIRsIlGRms3Sm74wYE1JMo8RnO04Hb12+v1J5ICw==", + "version": "3.3.2", + "resolved": "https://registry.npmjs.org/ci-info/-/ci-info-3.3.2.tgz", + "integrity": "sha512-xmDt/QIAdeZ9+nfdPsaBCpMvHNLFiLdjj59qjqn+6iPe6YmHGQ35sBnQ8uslRBXFmXkiZQOJRjvQeoGppoTjjg==", "dev": true }, "clean-stack": { @@ -9640,6 +9941,47 @@ "wrap-ansi": "^6.2.0" }, "dependencies": { + "ansi-regex": { + "version": "5.0.1", + "resolved": "https://registry.npmjs.org/ansi-regex/-/ansi-regex-5.0.1.tgz", + "integrity": "sha512-quJQXlTSUGL2LH9SUXo8VwsY4soanhgo6LNSm84E1LBcE8s3O0wpdiRzyR9z/ZZJMlMWv37qOOb9pdJlMUEKFQ==", + "dev": true + }, + "ansi-styles": { + "version": "4.3.0", + "resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-4.3.0.tgz", + "integrity": "sha512-zbB9rCJAT1rbjiVDb2hqKFHNYLxgtk8NURxZ3IZwD3F6NtxbXZQCnnSi1Lkx+IDohdPlFp222wVALIheZJQSEg==", + "dev": true, + "requires": { + "color-convert": "^2.0.1" + } + }, + "emoji-regex": { + "version": "8.0.0", + "resolved": "https://registry.npmjs.org/emoji-regex/-/emoji-regex-8.0.0.tgz", + "integrity": "sha512-MSjYzcWNOA0ewAHpz0MxpYFvwg6yjy1NG3xteoqz644VCo/RPgnr1/GGt+ic3iJTzQ8Eu3TdM14SawnVUmGE6A==", + "dev": true + }, + "string-width": { + "version": "4.2.3", + "resolved": "https://registry.npmjs.org/string-width/-/string-width-4.2.3.tgz", + "integrity": "sha512-wKyQRQpjJ0sIp62ErSZdGsjMJWsap5oRNihHhu6G7JVO/9jIB6UyevL+tXuOqrng8j/cxKTWyWUwvSTriiZz/g==", + "dev": true, + "requires": { + "emoji-regex": "^8.0.0", + "is-fullwidth-code-point": "^3.0.0", + "strip-ansi": "^6.0.1" + } + }, + "strip-ansi": { + "version": "6.0.1", + "resolved": "https://registry.npmjs.org/strip-ansi/-/strip-ansi-6.0.1.tgz", + "integrity": "sha512-Y38VPSHcqkFrCpFnQ9vuSXmquuv5oXOKpGeT6aGrr3o3Gc9AlVa6JBfUSOCnbxGGZF+/0ooI7KrPuUSztUdU5A==", + "dev": true, + "requires": { + "ansi-regex": "^5.0.1" + } + }, "wrap-ansi": { "version": "6.2.0", "resolved": "https://registry.npmjs.org/wrap-ansi/-/wrap-ansi-6.2.0.tgz", @@ -9706,7 +10048,7 @@ "commondir": { "version": "1.0.1", "resolved": "https://registry.npmjs.org/commondir/-/commondir-1.0.1.tgz", - "integrity": "sha1-3dgA2gxmEnOTzKWVDqloo6rxJTs=", + "integrity": "sha512-W9pAhw0ja1Edb5GVdIF1mjZw/ASI0AlShXM83UUGe2DVr5TdAPEA1OA8m/g8zWp9x6On7gqufY+FatDbC3MDQg==", "dev": true }, "compress-brotli": { @@ -9722,7 +10064,7 @@ "concat-map": { "version": "0.0.1", "resolved": "https://registry.npmjs.org/concat-map/-/concat-map-0.0.1.tgz", - "integrity": "sha1-2Klr13/Wjfd5OnMDajug1UBdR3s=", + "integrity": "sha512-/Srv4dswyQNBfohGpz9o6Yb3Gz3SrUDqBH5rTuhGR7ahtlbYKnVxw2bCFMRljaA7EXHaXZ8wsHdodFvbkhKmqg==", "dev": true }, "config-chain": { @@ -9808,14 +10150,6 @@ "dev": true, "requires": { "type-fest": "^1.0.1" - }, - "dependencies": { - "type-fest": { - "version": "1.4.0", - "resolved": "https://registry.npmjs.org/type-fest/-/type-fest-1.4.0.tgz", - "integrity": "sha512-yGSza74xk0UG8k+pLh5oeoYirvIiWo5t0/o3zHHAO2tRDiZcxWP7fywNlXhqb6/r6sWvwi+RsyQMWhVLe4BVuA==", - "dev": true - } } }, "data-uri-to-buffer": { @@ -9836,13 +10170,13 @@ "decamelize": { "version": "1.2.0", "resolved": "https://registry.npmjs.org/decamelize/-/decamelize-1.2.0.tgz", - "integrity": "sha1-9lNNFRSCabIDUue+4m9QH5oZEpA=", + "integrity": "sha512-z2S+W9X73hAUUki+N+9Za2lBlun89zigOyGrsax+KUQ6wKW4ZoWpEYBkGhQjwAjjDCkWxhY0VKEhk8wzY7F5cA==", "dev": true }, "decode-uri-component": { "version": "0.2.0", "resolved": "https://registry.npmjs.org/decode-uri-component/-/decode-uri-component-0.2.0.tgz", - "integrity": "sha1-6zkTMzRYd1y4TNGh+uBiEGu4dUU=", + "integrity": "sha512-hjf+xovcEn31w/EUYdTXQh/8smFL/dzYjohQGEIgjyNavaJfBY2p5F527Bo1VPATxv0VYTUC2bOcXvqFwk78Og==", "dev": true }, "decompress-response": { @@ -9929,7 +10263,7 @@ "delayed-stream": { "version": "1.0.0", "resolved": "https://registry.npmjs.org/delayed-stream/-/delayed-stream-1.0.0.tgz", - "integrity": "sha1-3zrhmayt+31ECqrgsp4icrJOxhk=", + "integrity": "sha512-ZySD7Nf91aLB0RxL4KGrKHBXl7Eds1DAmEdcoVawXnLD7SDhpNgtuII2aAkg7a7QS41jxPSZ17p4VdGnMHk3MQ==", "dev": true }, "depd": { @@ -9984,9 +10318,9 @@ "dev": true }, "electron-to-chromium": { - "version": "1.4.129", - "resolved": "https://registry.npmjs.org/electron-to-chromium/-/electron-to-chromium-1.4.129.tgz", - "integrity": "sha512-GgtN6bsDtHdtXJtlMYZWGB/uOyjZWjmRDumXTas7dGBaB9zUyCjzHet1DY2KhyHN8R0GLbzZWqm4efeddqqyRQ==", + "version": "1.4.174", + "resolved": "https://registry.npmjs.org/electron-to-chromium/-/electron-to-chromium-1.4.174.tgz", + "integrity": "sha512-JER+w+9MV2MBVFOXxP036bLlNOnzbYAWrWU8sNUwoOO69T3w4564WhM5H5atd8VVS8U4vpi0i0kdoYzm1NPQgQ==", "dev": true }, "email-addresses": { @@ -9996,9 +10330,9 @@ "dev": true }, "emoji-regex": { - "version": "8.0.0", - "resolved": "https://registry.npmjs.org/emoji-regex/-/emoji-regex-8.0.0.tgz", - "integrity": "sha512-MSjYzcWNOA0ewAHpz0MxpYFvwg6yjy1NG3xteoqz644VCo/RPgnr1/GGt+ic3iJTzQ8Eu3TdM14SawnVUmGE6A==", + "version": "9.2.2", + "resolved": "https://registry.npmjs.org/emoji-regex/-/emoji-regex-9.2.2.tgz", + "integrity": "sha512-L18DaJsXSUk2+42pv8mLs5jJT2hqFkFE4j21wOmgbUqsZ2hL72NsUU785g9RXgo3s0ZNgVl42TiHp3ZtOv/Vyg==", "dev": true }, "end-of-stream": { @@ -10020,17 +10354,19 @@ } }, "es-abstract": { - "version": "1.19.5", - "resolved": "https://registry.npmjs.org/es-abstract/-/es-abstract-1.19.5.tgz", - "integrity": "sha512-Aa2G2+Rd3b6kxEUKTF4TaW67czBLyAv3z7VOhYRU50YBx+bbsYZ9xQP4lMNazePuFlybXI0V4MruPos7qUo5fA==", + "version": "1.20.1", + "resolved": "https://registry.npmjs.org/es-abstract/-/es-abstract-1.20.1.tgz", + "integrity": "sha512-WEm2oBhfoI2sImeM4OF2zE2V3BYdSF+KnSi9Sidz51fQHd7+JuF8Xgcj9/0o+OWeIeIS/MiuNnlruQrJf16GQA==", "dev": true, "requires": { "call-bind": "^1.0.2", "es-to-primitive": "^1.2.1", "function-bind": "^1.1.1", + "function.prototype.name": "^1.1.5", "get-intrinsic": "^1.1.1", "get-symbol-description": "^1.0.0", "has": "^1.0.3", + "has-property-descriptors": "^1.0.0", "has-symbols": "^1.0.3", "internal-slot": "^1.0.3", "is-callable": "^1.2.4", @@ -10042,9 +10378,10 @@ "object-inspect": "^1.12.0", "object-keys": "^1.1.1", "object.assign": "^4.1.2", - "string.prototype.trimend": "^1.0.4", - "string.prototype.trimstart": "^1.0.4", - "unbox-primitive": "^1.0.1" + "regexp.prototype.flags": "^1.4.3", + "string.prototype.trimend": "^1.0.5", + "string.prototype.trimstart": "^1.0.5", + "unbox-primitive": "^1.0.2" } }, "es-array-method-boxes-properly": { @@ -10099,9 +10436,9 @@ "dev": true }, "escape-string-regexp": { - "version": "1.0.5", - "resolved": "https://registry.npmjs.org/escape-string-regexp/-/escape-string-regexp-1.0.5.tgz", - "integrity": "sha1-G2HAViGQqN/2rjuyzwIAyhMLhtQ=", + "version": "5.0.0", + "resolved": "https://registry.npmjs.org/escape-string-regexp/-/escape-string-regexp-5.0.0.tgz", + "integrity": "sha512-/veY75JbMK4j1yjvuUxuVsiS/hr/4iHs9FTT6cgTexxdE0Ly/glccBAkloH/DofkjRbZU3bnoj38mOmhkZ0lHw==", "dev": true }, "escodegen": { @@ -10120,7 +10457,7 @@ "levn": { "version": "0.3.0", "resolved": "https://registry.npmjs.org/levn/-/levn-0.3.0.tgz", - "integrity": "sha1-OwmSTt+fCDwEkP3UwLxEIeBHZO4=", + "integrity": "sha512-0OO4y2iOHix2W6ujICbKIaEQXvFQHue65vUG3pb5EUomzPI90z9hsA1VsO/dbIIpC53J8gxM9Q4Oho0jrCM/yA==", "dev": true, "requires": { "prelude-ls": "~1.1.2", @@ -10144,13 +10481,13 @@ "prelude-ls": { "version": "1.1.2", "resolved": "https://registry.npmjs.org/prelude-ls/-/prelude-ls-1.1.2.tgz", - "integrity": "sha1-IZMqVJ9eUv/ZqCf1cOBL5iqX2lQ=", + "integrity": "sha512-ESF23V4SKG6lVSGZgYNpbsiaAkdab6ZgOxe52p7+Kid3W3u3bxR4Vfd/o21dmN7jSt0IwgZ4v5MUd26FEtXE9w==", "dev": true }, "type-check": { "version": "0.3.2", "resolved": "https://registry.npmjs.org/type-check/-/type-check-0.3.2.tgz", - "integrity": "sha1-WITKtRLPHTVeP7eE8wgEsrUg23I=", + "integrity": "sha512-ZCmOJdvOWDBYJlzAoFkC+Q0+bUyEOS1ltgp1MGU03fqHG+dbi9tBFU2Rd9QKiDZFAYrhPh2JUf7rZRIuHRKtOg==", "dev": true, "requires": { "prelude-ls": "~1.1.2" @@ -10201,6 +10538,21 @@ "v8-compile-cache": "^2.0.3" }, "dependencies": { + "ansi-regex": { + "version": "5.0.1", + "resolved": "https://registry.npmjs.org/ansi-regex/-/ansi-regex-5.0.1.tgz", + "integrity": "sha512-quJQXlTSUGL2LH9SUXo8VwsY4soanhgo6LNSm84E1LBcE8s3O0wpdiRzyR9z/ZZJMlMWv37qOOb9pdJlMUEKFQ==", + "dev": true + }, + "ansi-styles": { + "version": "4.3.0", + "resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-4.3.0.tgz", + "integrity": "sha512-zbB9rCJAT1rbjiVDb2hqKFHNYLxgtk8NURxZ3IZwD3F6NtxbXZQCnnSi1Lkx+IDohdPlFp222wVALIheZJQSEg==", + "dev": true, + "requires": { + "color-convert": "^2.0.1" + } + }, "chalk": { "version": "4.1.2", "resolved": "https://registry.npmjs.org/chalk/-/chalk-4.1.2.tgz", @@ -10232,6 +10584,15 @@ "resolved": "https://registry.npmjs.org/estraverse/-/estraverse-5.3.0.tgz", "integrity": "sha512-MMdARuVEQziNTeJD8DgMqmhwR11BRQ/cBP+pLtYdSTnf3MIO8fFeiINEbX36ZdNlfU/7A9f3gUw49B3oQsvwBA==", "dev": true + }, + "strip-ansi": { + "version": "6.0.1", + "resolved": "https://registry.npmjs.org/strip-ansi/-/strip-ansi-6.0.1.tgz", + "integrity": "sha512-Y38VPSHcqkFrCpFnQ9vuSXmquuv5oXOKpGeT6aGrr3o3Gc9AlVa6JBfUSOCnbxGGZF+/0ooI7KrPuUSztUdU5A==", + "dev": true, + "requires": { + "ansi-regex": "^5.0.1" + } } } }, @@ -10398,7 +10759,7 @@ "fast-levenshtein": { "version": "2.0.6", "resolved": "https://registry.npmjs.org/fast-levenshtein/-/fast-levenshtein-2.0.6.tgz", - "integrity": "sha1-PYpcZog6FqMMqGQ+hR8Zuqd5eRc=", + "integrity": "sha512-DCXu6Ifhqcks7TZKY3Hxp3y6qphY5SJZmrWMDrKcERSOXWQdMhU9Ig/PYrzyw/ul9jOIyh0N4M0tbC5hodg8dw==", "dev": true }, "fastq": { @@ -10418,14 +10779,6 @@ "requires": { "escape-string-regexp": "^5.0.0", "is-unicode-supported": "^1.2.0" - }, - "dependencies": { - "escape-string-regexp": { - "version": "5.0.0", - "resolved": "https://registry.npmjs.org/escape-string-regexp/-/escape-string-regexp-5.0.0.tgz", - "integrity": "sha512-/veY75JbMK4j1yjvuUxuVsiS/hr/4iHs9FTT6cgTexxdE0Ly/glccBAkloH/DofkjRbZU3bnoj38mOmhkZ0lHw==", - "dev": true - } } }, "file-entry-cache": { @@ -10446,7 +10799,7 @@ "filename-reserved-regex": { "version": "2.0.0", "resolved": "https://registry.npmjs.org/filename-reserved-regex/-/filename-reserved-regex-2.0.0.tgz", - "integrity": "sha1-q/c9+rc10EVECr/qLZHzieu/oik=", + "integrity": "sha512-lc1bnsSr4L4Bdif8Xb/qrtokGbq5zlsms/CYH8PP+WtCkGNF65DPiQY8vG3SakEdRn8Dlnm+gW/qWKKjS5sZzQ==", "dev": true }, "filenamify": { @@ -10472,7 +10825,7 @@ "filter-obj": { "version": "1.1.0", "resolved": "https://registry.npmjs.org/filter-obj/-/filter-obj-1.1.0.tgz", - "integrity": "sha1-mzERErxsYSehbgFsbF1/GeCAXFs=", + "integrity": "sha512-8rXg1ZnX7xzy2NGDVkBVaAy+lSlPNwad13BtgSlLuxfIslyt5Vg64U7tFcCt4WS1R0hvtnQybT/IyCkGZ3DpXQ==", "dev": true }, "find-cache-dir": { @@ -10513,9 +10866,9 @@ } }, "flatted": { - "version": "3.2.5", - "resolved": "https://registry.npmjs.org/flatted/-/flatted-3.2.5.tgz", - "integrity": "sha512-WIWGi2L3DyTUvUrwRKgGi9TwxQMUEqPOPQBVi71R96jZXJdFskXEmf54BoZaS1kknGODoIGASGEzBUYdyMCBJg==", + "version": "3.2.6", + "resolved": "https://registry.npmjs.org/flatted/-/flatted-3.2.6.tgz", + "integrity": "sha512-0sQoMh9s0BYsm+12Huy/rkKxVu4R1+r96YX5cG44rHV0pQ6iC3Q+mkoMFaGWObMFYQxCVT+ssG1ksneA2MI9KQ==", "dev": true }, "foreground-child": { @@ -10565,13 +10918,20 @@ "fs.realpath": { "version": "1.0.0", "resolved": "https://registry.npmjs.org/fs.realpath/-/fs.realpath-1.0.0.tgz", - "integrity": "sha1-FQStJSMVjKpA20onh8sBQRmU6k8=", + "integrity": "sha512-OO0pH2lK6a0hZnAdau5ItzHPI6pUlvI7jMVnxUQRtw4owF2wk8lOSabtGDCTP4Ggrg2MbGnWO9X8K1t4+fGMDw==", "dev": true }, + "fsevents": { + "version": "2.3.2", + "resolved": "https://registry.npmjs.org/fsevents/-/fsevents-2.3.2.tgz", + "integrity": "sha512-xiqMQR4xAeHTuB9uWm+fFRcIOgKBMiOBP+eXiyT7jsgVCq1bkVygt00oASowB7EdtpOHaaPgKt812P9ab+DDKA==", + "dev": true, + "optional": true + }, "ftp": { "version": "0.3.10", "resolved": "https://registry.npmjs.org/ftp/-/ftp-0.3.10.tgz", - "integrity": "sha1-kZfYYa2BQvPmPVqDv+TFn3MwiF0=", + "integrity": "sha512-faFVML1aBx2UoDStmLwv2Wptt4vw5x03xxX172nhA5Y5HBshW5JweqQ2W4xL4dezQTG8inJsuYcpPHHU3X5OTQ==", "dev": true, "requires": { "readable-stream": "1.1.x", @@ -10581,13 +10941,13 @@ "isarray": { "version": "0.0.1", "resolved": "https://registry.npmjs.org/isarray/-/isarray-0.0.1.tgz", - "integrity": "sha1-ihis/Kmo9Bd+Cav8YDiTmwXR7t8=", + "integrity": "sha512-D2S+3GLxWH+uhrNEcoh/fnmYeP8E8/zHl644d/jdA0g2uyXvy3sb0qxotE+ne0LtccHknQzWwZEzhak7oJ0COQ==", "dev": true }, "readable-stream": { "version": "1.1.14", "resolved": "https://registry.npmjs.org/readable-stream/-/readable-stream-1.1.14.tgz", - "integrity": "sha1-fPTFTvZI44EwhMY23SB54WbAgdk=", + "integrity": "sha512-+MeVjFf4L44XUkhM1eYbD8fyEsxcV81pqMSR5gblfcLCHfZvbrqy4/qYHE+/R5HoBUT11WV5O08Cr1n3YXkWVQ==", "dev": true, "requires": { "core-util-is": "~1.0.0", @@ -10599,7 +10959,7 @@ "string_decoder": { "version": "0.10.31", "resolved": "https://registry.npmjs.org/string_decoder/-/string_decoder-0.10.31.tgz", - "integrity": "sha1-YuIDvEF2bGwoyfyEMB2rHFMQ+pQ=", + "integrity": "sha512-ev2QzSzWPYmy9GuqfIVildA4OdcGLeFZQrq5ys6RtiuF+RQQiZWr8TZNyAcuVXyQRYfEO+MsoB/1BuQVhOJuoQ==", "dev": true } } @@ -10610,10 +10970,28 @@ "integrity": "sha512-yIovAzMX49sF8Yl58fSCWJ5svSLuaibPxXQJFLmBObTuCr0Mf1KiPopGM9NiFjiYBCbfaa2Fh6breQ6ANVTI0A==", "dev": true }, + "function.prototype.name": { + "version": "1.1.5", + "resolved": "https://registry.npmjs.org/function.prototype.name/-/function.prototype.name-1.1.5.tgz", + "integrity": "sha512-uN7m/BzVKQnCUF/iW8jYea67v++2u7m5UgENbHRtdDVclOUP+FMPlCNdmk0h/ysGyo2tavMJEDqJAkJdRa1vMA==", + "dev": true, + "requires": { + "call-bind": "^1.0.2", + "define-properties": "^1.1.3", + "es-abstract": "^1.19.0", + "functions-have-names": "^1.2.2" + } + }, "functional-red-black-tree": { "version": "1.0.1", "resolved": "https://registry.npmjs.org/functional-red-black-tree/-/functional-red-black-tree-1.0.1.tgz", - "integrity": "sha1-GwqzvVU7Kg1jmdKcDj6gslIHgyc=", + "integrity": "sha512-dsKNQNdj6xA3T+QlADDA7mOSlX0qiMINjn0cgr+eGHGsbSHzTabcIogz2+p/iqP1Xs6EP/sS2SbqH+brGTbq0g==", + "dev": true + }, + "functions-have-names": { + "version": "1.2.3", + "resolved": "https://registry.npmjs.org/functions-have-names/-/functions-have-names-1.2.3.tgz", + "integrity": "sha512-xckBUXyTIqT97tq2x2AMb+g163b5JFysYk0x4qxNFwbfQkmNZoiRHb6sPzI9/QV33WeuvVYBUIiD4NzNIyqaRQ==", "dev": true }, "generic-pool": { @@ -10634,14 +11012,14 @@ "dev": true }, "get-intrinsic": { - "version": "1.1.1", - "resolved": "https://registry.npmjs.org/get-intrinsic/-/get-intrinsic-1.1.1.tgz", - "integrity": "sha512-kWZrnVM42QCiEA2Ig1bG8zjoIMOgxWwYCEeNdwY6Tv/cOSeGpcoX4pXHfKUxNKVoArnrEr2e9srnAxxGIraS9Q==", + "version": "1.1.2", + "resolved": "https://registry.npmjs.org/get-intrinsic/-/get-intrinsic-1.1.2.tgz", + "integrity": "sha512-Jfm3OyCxHh9DJyc28qGk+JmfkpO41A4XkneDSujN9MDXrm4oDKdHvndhZ2dN94+ERNfkYJWDclW6k2L/ZGHjXA==", "dev": true, "requires": { "function-bind": "^1.1.1", "has": "^1.0.3", - "has-symbols": "^1.0.1" + "has-symbols": "^1.0.3" } }, "get-package-type": { @@ -10681,9 +11059,9 @@ } }, "gh-pages": { - "version": "3.2.3", - "resolved": "https://registry.npmjs.org/gh-pages/-/gh-pages-3.2.3.tgz", - "integrity": "sha512-jA1PbapQ1jqzacECfjUaO9gV8uBgU6XNMV0oXLtfCX3haGLe5Atq8BxlrADhbD6/UdG9j6tZLWAkAybndOXTJg==", + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/gh-pages/-/gh-pages-4.0.0.tgz", + "integrity": "sha512-p8S0T3aGJc68MtwOcZusul5qPSNZCalap3NWbhRUZYu1YOdp+EjZ+4kPmRM8h3NNRdqw00yuevRjlkuSzCn7iQ==", "dev": true, "requires": { "async": "^2.6.1", @@ -10715,15 +11093,15 @@ } }, "glob": { - "version": "7.2.0", - "resolved": "https://registry.npmjs.org/glob/-/glob-7.2.0.tgz", - "integrity": "sha512-lmLf6gtyrPq8tTjSmrO94wBeQbFR3HbLHbuyD69wuyQkImp2hWqMGB47OX65FBkPffO641IP9jWa1z4ivqG26Q==", + "version": "7.2.3", + "resolved": "https://registry.npmjs.org/glob/-/glob-7.2.3.tgz", + "integrity": "sha512-nFR0zLpU2YCaRxwoCJvL6UvCH2JFyFVIvwTLsIf21AuHlMskA1hhTdk+LlYJtOlYt9v6dvszD2BGRqBL+iQK9Q==", "dev": true, "requires": { "fs.realpath": "^1.0.0", "inflight": "^1.0.4", "inherits": "2", - "minimatch": "^3.0.4", + "minimatch": "^3.1.1", "once": "^1.3.0", "path-is-absolute": "^1.0.0" } @@ -10753,12 +11131,20 @@ "dev": true, "requires": { "type-fest": "^0.20.2" + }, + "dependencies": { + "type-fest": { + "version": "0.20.2", + "resolved": "https://registry.npmjs.org/type-fest/-/type-fest-0.20.2.tgz", + "integrity": "sha512-Ne+eE4r0/iWnpAxD852z3A+N0Bt5RN//NjJwRd2VFHEmrywxf5vsZlh4R6lixl6B+wz/8d+maTSAkN1FIkI3LQ==", + "dev": true + } } }, "globby": { "version": "6.1.0", "resolved": "https://registry.npmjs.org/globby/-/globby-6.1.0.tgz", - "integrity": "sha1-9abXDoOV4hyFj7BInWTfAkJNUGw=", + "integrity": "sha512-KVbFv2TQtbzCoxAnfD6JcHZTYCzyliEaaeM/gH8qQdkKr5s0OP9scEgvdcngyk7AVdY6YVW/TJHd+lQ/Df3Daw==", "dev": true, "requires": { "array-union": "^1.0.1", @@ -10978,7 +11364,7 @@ "imurmurhash": { "version": "0.1.4", "resolved": "https://registry.npmjs.org/imurmurhash/-/imurmurhash-0.1.4.tgz", - "integrity": "sha1-khi5srkoojixPcT7a21XbyMUU+o=", + "integrity": "sha512-JmXMZ6wuvDmLiHEml9ykzqO6lwFbof0GG4IkcGaENdCRDDmMVnny7s5HsIgHCbaq0w2MyPhDqkhTUgS2LU2PHA==", "dev": true }, "indent-string": { @@ -10990,7 +11376,7 @@ "inflight": { "version": "1.0.6", "resolved": "https://registry.npmjs.org/inflight/-/inflight-1.0.6.tgz", - "integrity": "sha1-Sb1jMdfQLQwJvJEKEHW6gWW1bfk=", + "integrity": "sha512-k92I/b08q4wvFscXCLvqfsHCrjrF7yiXsQuIVvVE7N82W3+aqpzuUdBbfhWcy/FZR3/4IgflMgKLOsvPDrGCJA==", "dev": true, "requires": { "once": "^1.3.0", @@ -11030,57 +11416,6 @@ "strip-ansi": "^7.0.1", "through": "^2.3.6", "wrap-ansi": "^8.0.1" - }, - "dependencies": { - "ansi-regex": { - "version": "6.0.1", - "resolved": "https://registry.npmjs.org/ansi-regex/-/ansi-regex-6.0.1.tgz", - "integrity": "sha512-n5M855fKb2SsfMIiFFoVrABHJC8QtHwVx+mHWP3QcEqBHYienj5dHSgjbxtC0WEZXYt4wcD6zrQElDPhFuZgfA==", - "dev": true - }, - "ansi-styles": { - "version": "6.1.0", - "resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-6.1.0.tgz", - "integrity": "sha512-VbqNsoz55SYGczauuup0MFUyXNQviSpFTj1RQtFzmQLk18qbVSpTFFGMT293rmDaQuKCT6InmbuEyUne4mTuxQ==", - "dev": true - }, - "emoji-regex": { - "version": "9.2.2", - "resolved": "https://registry.npmjs.org/emoji-regex/-/emoji-regex-9.2.2.tgz", - "integrity": "sha512-L18DaJsXSUk2+42pv8mLs5jJT2hqFkFE4j21wOmgbUqsZ2hL72NsUU785g9RXgo3s0ZNgVl42TiHp3ZtOv/Vyg==", - "dev": true - }, - "string-width": { - "version": "5.1.2", - "resolved": "https://registry.npmjs.org/string-width/-/string-width-5.1.2.tgz", - "integrity": "sha512-HnLOCR3vjcY8beoNLtcjZ5/nxn2afmME6lhrDrebokqMap+XbeW8n9TXpPDOqdGK5qcI3oT0GKTW6wC7EMiVqA==", - "dev": true, - "requires": { - "eastasianwidth": "^0.2.0", - "emoji-regex": "^9.2.2", - "strip-ansi": "^7.0.1" - } - }, - "strip-ansi": { - "version": "7.0.1", - "resolved": "https://registry.npmjs.org/strip-ansi/-/strip-ansi-7.0.1.tgz", - "integrity": "sha512-cXNxvT8dFNRVfhVME3JAe98mkXDYN2O1l7jmcwMnOslDeESg1rF/OZMtK0nRAhiari1unG5cD4jG3rapUAkLbw==", - "dev": true, - "requires": { - "ansi-regex": "^6.0.1" - } - }, - "wrap-ansi": { - "version": "8.0.1", - "resolved": "https://registry.npmjs.org/wrap-ansi/-/wrap-ansi-8.0.1.tgz", - "integrity": "sha512-QFF+ufAqhoYHvoHdajT/Po7KoXVBPXS2bgjIam5isfWJPfIOnQZ50JtUiVvCv/sjgacf3yRrt2ZKUZ/V4itN4g==", - "dev": true, - "requires": { - "ansi-styles": "^6.1.0", - "string-width": "^5.0.1", - "strip-ansi": "^7.0.1" - } - } } }, "internal-slot": { @@ -11101,9 +11436,9 @@ "dev": true }, "ip": { - "version": "1.1.5", - "resolved": "https://registry.npmjs.org/ip/-/ip-1.1.5.tgz", - "integrity": "sha1-vd7XARQpCCjAoDnnLvJfWq7ENUo=", + "version": "1.1.8", + "resolved": "https://registry.npmjs.org/ip/-/ip-1.1.8.tgz", + "integrity": "sha512-PuExPYUiu6qMBQb4l06ecm6T6ujzhmh+MeJcW9wa89PoAz5pvd4zPgN5WJV104mb6S2T1AwNIAaB70JNrLQWhg==", "dev": true }, "is-arguments": { @@ -11119,7 +11454,7 @@ "is-arrayish": { "version": "0.2.1", "resolved": "https://registry.npmjs.org/is-arrayish/-/is-arrayish-0.2.1.tgz", - "integrity": "sha1-d8mYQFJ6qOyxqLppe4BkWnqSap0=", + "integrity": "sha512-zz06S8t0ozoDXMG+ube26zeCTNXcKIPJZJi8hBrF4idCLms4CG9QtK7qBl1boi5ODzFpjswb5JPmHCbMpjaYzg==", "dev": true }, "is-bigint": { @@ -11192,7 +11527,7 @@ "is-extglob": { "version": "2.1.1", "resolved": "https://registry.npmjs.org/is-extglob/-/is-extglob-2.1.1.tgz", - "integrity": "sha1-qIwCU1eR8C7TfHahueqXc8gz+MI=", + "integrity": "sha512-SbKbANkN603Vi4jEZv49LeVJMn4yGwsbzZworEoyEiutsN3nJYdbO36zfhGJ6QEDpOZIFkDtnq5JRxmvl3jsoQ==", "dev": true }, "is-fullwidth-code-point": { @@ -11309,12 +11644,12 @@ } }, "is-ssh": { - "version": "1.3.3", - "resolved": "https://registry.npmjs.org/is-ssh/-/is-ssh-1.3.3.tgz", - "integrity": "sha512-NKzJmQzJfEEma3w5cJNcUMxoXfDjz0Zj0eyCalHn2E6VOwlzjZo0yuO2fcBSf8zhFuVCL/82/r5gRcoi6aEPVQ==", + "version": "1.4.0", + "resolved": "https://registry.npmjs.org/is-ssh/-/is-ssh-1.4.0.tgz", + "integrity": "sha512-x7+VxdxOdlV3CYpjvRLBv5Lo9OJerlYanjwFrPR9fuGPjCiNiCzFgAWpiLAohSbsnH4ZAys3SBh+hq5rJosxUQ==", "dev": true, "requires": { - "protocols": "^1.1.0" + "protocols": "^2.0.1" } }, "is-stream": { @@ -11344,7 +11679,7 @@ "is-typedarray": { "version": "1.0.0", "resolved": "https://registry.npmjs.org/is-typedarray/-/is-typedarray-1.0.0.tgz", - "integrity": "sha1-5HnICFjfDBsR3dppQPlgEfzaSpo=", + "integrity": "sha512-cyA56iCMHAh5CdzjJIa4aohJyeO1YbwLi3Jc35MmRU6poroFjIGZzUzupGiRPOjgHg9TLu43xbpwXk523fMxKA==", "dev": true }, "is-unicode-supported": { @@ -11392,7 +11727,7 @@ "isexe": { "version": "2.0.0", "resolved": "https://registry.npmjs.org/isexe/-/isexe-2.0.0.tgz", - "integrity": "sha1-6PvzdNxVb/iUehDcsFctYz8s+hA=", + "integrity": "sha512-RHxMLp9lnKHGHRng9QFhRCMbYAcVpn69smSGcq3f36xjgVVWThj4qqLbTLlq7Ssj8B+fIQ1EuCEGI2lKsyQeIw==", "dev": true }, "istanbul-lib-coverage": { @@ -11423,18 +11758,17 @@ } }, "istanbul-lib-processinfo": { - "version": "2.0.2", - "resolved": "https://registry.npmjs.org/istanbul-lib-processinfo/-/istanbul-lib-processinfo-2.0.2.tgz", - "integrity": "sha512-kOwpa7z9hme+IBPZMzQ5vdQj8srYgAtaRqeI48NGmAQ+/5yKiHLV0QbYqQpxsdEF0+w14SoB8YbnHKcXE2KnYw==", + "version": "2.0.3", + "resolved": "https://registry.npmjs.org/istanbul-lib-processinfo/-/istanbul-lib-processinfo-2.0.3.tgz", + "integrity": "sha512-NkwHbo3E00oybX6NGJi6ar0B29vxyvNwoC7eJ4G4Yq28UfY758Hgn/heV8VRFhevPED4LXfFz0DQ8z/0kw9zMg==", "dev": true, "requires": { "archy": "^1.0.0", - "cross-spawn": "^7.0.0", - "istanbul-lib-coverage": "^3.0.0-alpha.1", - "make-dir": "^3.0.0", + "cross-spawn": "^7.0.3", + "istanbul-lib-coverage": "^3.2.0", "p-map": "^3.0.0", "rimraf": "^3.0.0", - "uuid": "^3.3.3" + "uuid": "^8.3.2" } }, "istanbul-lib-report": { @@ -11527,7 +11861,7 @@ "json-stable-stringify-without-jsonify": { "version": "1.0.1", "resolved": "https://registry.npmjs.org/json-stable-stringify-without-jsonify/-/json-stable-stringify-without-jsonify-1.0.1.tgz", - "integrity": "sha1-nbe1lJatPzz+8wp1FC0tkwrXJlE=", + "integrity": "sha512-Bdboy+l7tA3OGW6FjyFHWkP5LuByj1Tk33Ljyq0axyzdk9//JSi2u3fP1QSmd1KNwq6VOKYGlAu87CisVir6Pw==", "dev": true }, "json5": { @@ -11545,7 +11879,7 @@ "jsonfile": { "version": "4.0.0", "resolved": "https://registry.npmjs.org/jsonfile/-/jsonfile-4.0.0.tgz", - "integrity": "sha1-h3Gq4HmbZAdrdmQPygWPnBDjPss=", + "integrity": "sha512-m6F1R3z8jjlf2imQHS2Qez5sjKWQzbuuhuJ/FKYFRZvPE3PuHcSMVZzfsLhGVOkfd20obL5SWEBew5ShlquNxg==", "dev": true, "requires": { "graceful-fs": "^4.1.6" @@ -11610,13 +11944,13 @@ "lodash.flattendeep": { "version": "4.4.0", "resolved": "https://registry.npmjs.org/lodash.flattendeep/-/lodash.flattendeep-4.4.0.tgz", - "integrity": "sha1-+wMJF/hqMTTlvJvsDWngAT3f7bI=", + "integrity": "sha512-uHaJFihxmJcEX3kT4I23ABqKKalJ/zDrDg0lsFtc1h+3uw49SIJ5beyhx5ExVRti3AvKoOJngIj7xz3oylPdWQ==", "dev": true }, "lodash.get": { "version": "4.4.2", "resolved": "https://registry.npmjs.org/lodash.get/-/lodash.get-4.4.2.tgz", - "integrity": "sha1-LRd/ZS+jHpObRDjVNBSZ36OCXpk=", + "integrity": "sha512-z+Uw/vLuy6gQe8cfaFWD7p0wVv8fJl3mbzXh33RS+0oW2wvUqiRXiQ69gLWSLpgB5/6sU+r6BlQR0MBILadqTQ==", "dev": true }, "lodash.merge": { @@ -11657,9 +11991,9 @@ "dev": true }, "macos-release": { - "version": "3.0.1", - "resolved": "https://registry.npmjs.org/macos-release/-/macos-release-3.0.1.tgz", - "integrity": "sha512-3l6OrhdDg2H2SigtuN3jBh+5dRJRWxNKuJTPBbGeNJTsmt/pj9PO25wYaNb05NuNmAsl435j4rDP6rgNXz7s7g==", + "version": "3.1.0", + "resolved": "https://registry.npmjs.org/macos-release/-/macos-release-3.1.0.tgz", + "integrity": "sha512-/M/R0gCDgM+Cv1IuBG1XGdfTFnMEG6PZeT+KGWHO/OG+imqmaD9CH5vHBTycEM3+Kc4uG2Il+tFAuUWLqQOeUA==", "dev": true }, "make-dir": { @@ -11777,13 +12111,19 @@ "yargs-unparser": "2.0.0" }, "dependencies": { - "brace-expansion": { - "version": "2.0.1", - "resolved": "https://registry.npmjs.org/brace-expansion/-/brace-expansion-2.0.1.tgz", - "integrity": "sha512-XnAIvQ8eM+kC6aULx6wuQiwVsnzsi9d3WxzV3FpWTGA19F621kwdbsAcFKXgKUHZWsy+mY6iL1sHTxWEFCytDA==", + "ansi-regex": { + "version": "5.0.1", + "resolved": "https://registry.npmjs.org/ansi-regex/-/ansi-regex-5.0.1.tgz", + "integrity": "sha512-quJQXlTSUGL2LH9SUXo8VwsY4soanhgo6LNSm84E1LBcE8s3O0wpdiRzyR9z/ZZJMlMWv37qOOb9pdJlMUEKFQ==", + "dev": true + }, + "ansi-styles": { + "version": "4.3.0", + "resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-4.3.0.tgz", + "integrity": "sha512-zbB9rCJAT1rbjiVDb2hqKFHNYLxgtk8NURxZ3IZwD3F6NtxbXZQCnnSi1Lkx+IDohdPlFp222wVALIheZJQSEg==", "dev": true, "requires": { - "balanced-match": "^1.0.0" + "color-convert": "^2.0.1" } }, "chalk": { @@ -11818,6 +12158,12 @@ "wrap-ansi": "^7.0.0" } }, + "emoji-regex": { + "version": "8.0.0", + "resolved": "https://registry.npmjs.org/emoji-regex/-/emoji-regex-8.0.0.tgz", + "integrity": "sha512-MSjYzcWNOA0ewAHpz0MxpYFvwg6yjy1NG3xteoqz644VCo/RPgnr1/GGt+ic3iJTzQ8Eu3TdM14SawnVUmGE6A==", + "dev": true + }, "escape-string-regexp": { "version": "4.0.0", "resolved": "https://registry.npmjs.org/escape-string-regexp/-/escape-string-regexp-4.0.0.tgz", @@ -11834,6 +12180,31 @@ "path-exists": "^4.0.0" } }, + "glob": { + "version": "7.2.0", + "resolved": "https://registry.npmjs.org/glob/-/glob-7.2.0.tgz", + "integrity": "sha512-lmLf6gtyrPq8tTjSmrO94wBeQbFR3HbLHbuyD69wuyQkImp2hWqMGB47OX65FBkPffO641IP9jWa1z4ivqG26Q==", + "dev": true, + "requires": { + "fs.realpath": "^1.0.0", + "inflight": "^1.0.4", + "inherits": "2", + "minimatch": "^3.0.4", + "once": "^1.3.0", + "path-is-absolute": "^1.0.0" + }, + "dependencies": { + "minimatch": { + "version": "3.1.2", + "resolved": "https://registry.npmjs.org/minimatch/-/minimatch-3.1.2.tgz", + "integrity": "sha512-J7p63hRiAjw1NDEww1W7i37+ByIrOWO5XQQAzZ3VOcL0PNybwpfmV/N05zFAzwQ9USyEcX6t3UO+K5aqBQOIHw==", + "dev": true, + "requires": { + "brace-expansion": "^1.1.7" + } + } + } + }, "is-unicode-supported": { "version": "0.1.0", "resolved": "https://registry.npmjs.org/is-unicode-supported/-/is-unicode-supported-0.1.0.tgz", @@ -11866,6 +12237,17 @@ "dev": true, "requires": { "brace-expansion": "^2.0.1" + }, + "dependencies": { + "brace-expansion": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/brace-expansion/-/brace-expansion-2.0.1.tgz", + "integrity": "sha512-XnAIvQ8eM+kC6aULx6wuQiwVsnzsi9d3WxzV3FpWTGA19F621kwdbsAcFKXgKUHZWsy+mY6iL1sHTxWEFCytDA==", + "dev": true, + "requires": { + "balanced-match": "^1.0.0" + } + } } }, "ms": { @@ -11892,6 +12274,26 @@ "p-limit": "^3.0.2" } }, + "string-width": { + "version": "4.2.3", + "resolved": "https://registry.npmjs.org/string-width/-/string-width-4.2.3.tgz", + "integrity": "sha512-wKyQRQpjJ0sIp62ErSZdGsjMJWsap5oRNihHhu6G7JVO/9jIB6UyevL+tXuOqrng8j/cxKTWyWUwvSTriiZz/g==", + "dev": true, + "requires": { + "emoji-regex": "^8.0.0", + "is-fullwidth-code-point": "^3.0.0", + "strip-ansi": "^6.0.1" + } + }, + "strip-ansi": { + "version": "6.0.1", + "resolved": "https://registry.npmjs.org/strip-ansi/-/strip-ansi-6.0.1.tgz", + "integrity": "sha512-Y38VPSHcqkFrCpFnQ9vuSXmquuv5oXOKpGeT6aGrr3o3Gc9AlVa6JBfUSOCnbxGGZF+/0ooI7KrPuUSztUdU5A==", + "dev": true, + "requires": { + "ansi-regex": "^5.0.1" + } + }, "supports-color": { "version": "8.1.1", "resolved": "https://registry.npmjs.org/supports-color/-/supports-color-8.1.1.tgz", @@ -11901,6 +12303,17 @@ "has-flag": "^4.0.0" } }, + "wrap-ansi": { + "version": "7.0.0", + "resolved": "https://registry.npmjs.org/wrap-ansi/-/wrap-ansi-7.0.0.tgz", + "integrity": "sha512-YVGIj2kamLSTxw6NsZjoBxfSwsn0ycdesmc4p+Q21c5zPuZ1pl+NfxVdxPtdHvmNVOQ6XSYG4AUtyt/Fi7D16Q==", + "dev": true, + "requires": { + "ansi-styles": "^4.0.0", + "string-width": "^4.1.0", + "strip-ansi": "^6.0.0" + } + }, "y18n": { "version": "5.0.8", "resolved": "https://registry.npmjs.org/y18n/-/y18n-5.0.8.tgz", @@ -11951,7 +12364,7 @@ "natural-compare": { "version": "1.4.0", "resolved": "https://registry.npmjs.org/natural-compare/-/natural-compare-1.4.0.tgz", - "integrity": "sha1-Sr6/7tdUHywnrPspvbvRXI1bpPc=", + "integrity": "sha512-OWND8ei3VtNC9h7V60qff3SVobHr996CTwgxubgyQYEpg290h9J0buyECNNJexkFm5sOajh5G116RYA1c8ZMSw==", "dev": true }, "netmask": { @@ -11970,9 +12383,9 @@ }, "dependencies": { "type-fest": { - "version": "2.12.2", - "resolved": "https://registry.npmjs.org/type-fest/-/type-fest-2.12.2.tgz", - "integrity": "sha512-qt6ylCGpLjZ7AaODxbpyBZSs9fCI9SkL3Z9q2oxMBQhs/uyY+VD8jHA8ULCGmWQJlBgqvO3EJeAngOHD8zQCrQ==", + "version": "2.15.0", + "resolved": "https://registry.npmjs.org/type-fest/-/type-fest-2.15.0.tgz", + "integrity": "sha512-hpsXfQZrAiusX8KMY5HXSEV7xqGAGxFQoNDT+iW0yJE/bdYG0uMlRaUG0kNAUbF5p6Cq5Kuf69lm4M569QtRGw==", "dev": true } } @@ -12009,9 +12422,9 @@ } }, "node-releases": { - "version": "2.0.4", - "resolved": "https://registry.npmjs.org/node-releases/-/node-releases-2.0.4.tgz", - "integrity": "sha512-gbMzqQtTtDz/00jQzZ21PQzdI9PyLYqUSvD0p3naOhX4odFji0ZxYdnVwPTxmSwkmxhcFImpozceidSG+AgoPQ==", + "version": "2.0.5", + "resolved": "https://registry.npmjs.org/node-releases/-/node-releases-2.0.5.tgz", + "integrity": "sha512-U9h1NLROZTq9uE1SNffn6WuPDg8icmi3ns4rEl/oTfIle4iLjTliCzgTsbaIFMq/Xn078/lfY/BL0GWZ+psK4Q==", "dev": true }, "normalize-path": { @@ -12089,13 +12502,13 @@ "object-assign": { "version": "4.1.1", "resolved": "https://registry.npmjs.org/object-assign/-/object-assign-4.1.1.tgz", - "integrity": "sha1-IQmtx5ZYh8/AXLvUQsrIv7s2CGM=", + "integrity": "sha512-rJgTQnkUnH1sFw8yT6VSU3zD3sWmu6sZhIseY8VX+GRu3P6F7Fu+JNDoXfklElbLJSnc3FUQHVe4cU5hj+BcUg==", "dev": true }, "object-inspect": { - "version": "1.12.0", - "resolved": "https://registry.npmjs.org/object-inspect/-/object-inspect-1.12.0.tgz", - "integrity": "sha512-Ho2z80bVIvJloH+YzRmpZVQe87+qASmBUKZDWgx9cu+KDrX2ZDH/3tMy+gXbZETVGs2M8YdxObOh7XAtim9Y0g==", + "version": "1.12.2", + "resolved": "https://registry.npmjs.org/object-inspect/-/object-inspect-1.12.2.tgz", + "integrity": "sha512-z+cPxW0QGUp0mcqcsgQyLVRDoXFQbXOwBaqyF7VIgI4TWNQsDHrBpUQslRmIfAoYWdYzs6UlKJtB2XJpTaNSpQ==", "dev": true }, "object-keys": { @@ -12119,7 +12532,7 @@ "once": { "version": "1.4.0", "resolved": "https://registry.npmjs.org/once/-/once-1.4.0.tgz", - "integrity": "sha1-WDsap3WWHUsROsF9nFC6753Xa9E=", + "integrity": "sha512-lNaJgI+2Q5URQBkccEKHTQOPaXdUxnZZElQTZY0MFUAuaEqe1E+Nyvgdz/aIyNi6Z9MzO5dv1H8n58/GELp3+w==", "dev": true, "requires": { "wrappy": "1" @@ -12174,23 +12587,6 @@ "log-symbols": "^5.1.0", "strip-ansi": "^7.0.1", "wcwidth": "^1.0.1" - }, - "dependencies": { - "ansi-regex": { - "version": "6.0.1", - "resolved": "https://registry.npmjs.org/ansi-regex/-/ansi-regex-6.0.1.tgz", - "integrity": "sha512-n5M855fKb2SsfMIiFFoVrABHJC8QtHwVx+mHWP3QcEqBHYienj5dHSgjbxtC0WEZXYt4wcD6zrQElDPhFuZgfA==", - "dev": true - }, - "strip-ansi": { - "version": "7.0.1", - "resolved": "https://registry.npmjs.org/strip-ansi/-/strip-ansi-7.0.1.tgz", - "integrity": "sha512-cXNxvT8dFNRVfhVME3JAe98mkXDYN2O1l7jmcwMnOslDeESg1rF/OZMtK0nRAhiari1unG5cD4jG3rapUAkLbw==", - "dev": true, - "requires": { - "ansi-regex": "^6.0.1" - } - } } }, "os-name": { @@ -12266,14 +12662,14 @@ } }, "pac-resolver": { - "version": "5.0.0", - "resolved": "https://registry.npmjs.org/pac-resolver/-/pac-resolver-5.0.0.tgz", - "integrity": "sha512-H+/A6KitiHNNW+bxBKREk2MCGSxljfqRX76NjummWEYIat7ldVXRU3dhRIE3iXZ0nvGBk6smv3nntxKkzRL8NA==", + "version": "5.0.1", + "resolved": "https://registry.npmjs.org/pac-resolver/-/pac-resolver-5.0.1.tgz", + "integrity": "sha512-cy7u00ko2KVgBAjuhevqpPeHIkCIqPe1v24cydhWjmeuzaBfmUWFCZJ1iAh5TuVzVZoUzXIW7K8sMYOZ84uZ9Q==", "dev": true, "requires": { - "degenerator": "^3.0.1", + "degenerator": "^3.0.2", "ip": "^1.1.5", - "netmask": "^2.0.1" + "netmask": "^2.0.2" } }, "package-hash": { @@ -12348,27 +12744,43 @@ } }, "parse-path": { - "version": "4.0.3", - "resolved": "https://registry.npmjs.org/parse-path/-/parse-path-4.0.3.tgz", - "integrity": "sha512-9Cepbp2asKnWTJ9x2kpw6Fe8y9JDbqwahGCTvklzd/cEq5C5JC59x2Xb0Kx+x0QZ8bvNquGO8/BWP0cwBHzSAA==", + "version": "4.0.4", + "resolved": "https://registry.npmjs.org/parse-path/-/parse-path-4.0.4.tgz", + "integrity": "sha512-Z2lWUis7jlmXC1jeOG9giRO2+FsuyNipeQ43HAjqAZjwSe3SEf+q/84FGPHoso3kyntbxa4c4i77t3m6fGf8cw==", "dev": true, "requires": { "is-ssh": "^1.3.0", "protocols": "^1.4.0", "qs": "^6.9.4", "query-string": "^6.13.8" + }, + "dependencies": { + "protocols": { + "version": "1.4.8", + "resolved": "https://registry.npmjs.org/protocols/-/protocols-1.4.8.tgz", + "integrity": "sha512-IgjKyaUSjsROSO8/D49Ab7hP8mJgTYcqApOqdPhLoPxAplXmkp+zRvsrSQjFn5by0rhm4VH0GAUELIPpx7B1yg==", + "dev": true + } } }, "parse-url": { - "version": "6.0.0", - "resolved": "https://registry.npmjs.org/parse-url/-/parse-url-6.0.0.tgz", - "integrity": "sha512-cYyojeX7yIIwuJzledIHeLUBVJ6COVLeT4eF+2P6aKVzwvgKQPndCBv3+yQ7pcWjqToYwaligxzSYNNmGoMAvw==", + "version": "6.0.2", + "resolved": "https://registry.npmjs.org/parse-url/-/parse-url-6.0.2.tgz", + "integrity": "sha512-uCSjOvD3T+6B/sPWhR+QowAZcU/o4bjPrVBQBGFxcDF6J6FraCGIaDBsdoQawiaaAVdHvtqBe3w3vKlfBKySOQ==", "dev": true, "requires": { "is-ssh": "^1.3.0", "normalize-url": "^6.1.0", - "parse-path": "^4.0.0", + "parse-path": "^4.0.4", "protocols": "^1.4.0" + }, + "dependencies": { + "protocols": { + "version": "1.4.8", + "resolved": "https://registry.npmjs.org/protocols/-/protocols-1.4.8.tgz", + "integrity": "sha512-IgjKyaUSjsROSO8/D49Ab7hP8mJgTYcqApOqdPhLoPxAplXmkp+zRvsrSQjFn5by0rhm4VH0GAUELIPpx7B1yg==", + "dev": true + } } }, "path-exists": { @@ -12380,7 +12792,7 @@ "path-is-absolute": { "version": "1.0.1", "resolved": "https://registry.npmjs.org/path-is-absolute/-/path-is-absolute-1.0.1.tgz", - "integrity": "sha1-F0uSaHNVNP+8es5r9TpanhtcX18=", + "integrity": "sha512-AVbw3UJ2e9bq64vSaS9Am0fje1Pa8pbGqTTsmXfaIiMpnr5DlDhfJOuLj9Sf95ZPVDAUerDfEk88MPmPe7UCQg==", "dev": true }, "path-key": { @@ -12407,7 +12819,7 @@ "isarray": { "version": "0.0.1", "resolved": "https://registry.npmjs.org/isarray/-/isarray-0.0.1.tgz", - "integrity": "sha1-ihis/Kmo9Bd+Cav8YDiTmwXR7t8=", + "integrity": "sha512-D2S+3GLxWH+uhrNEcoh/fnmYeP8E8/zHl644d/jdA0g2uyXvy3sb0qxotE+ne0LtccHknQzWwZEzhak7oJ0COQ==", "dev": true } } @@ -12433,19 +12845,19 @@ "pify": { "version": "2.3.0", "resolved": "https://registry.npmjs.org/pify/-/pify-2.3.0.tgz", - "integrity": "sha1-7RQaasBDqEnqWISY59yosVMw6Qw=", + "integrity": "sha512-udgsAY+fTnvv7kI7aaxbqwWNb0AHiB0qBO89PZKPkoTmGOgdbrHDKD+0B2X4uTfJ/FT1R09r9gTsjUjNJotuog==", "dev": true }, "pinkie": { "version": "2.0.4", "resolved": "https://registry.npmjs.org/pinkie/-/pinkie-2.0.4.tgz", - "integrity": "sha1-clVrgM+g1IqXToDnckjoDtT3+HA=", + "integrity": "sha512-MnUuEycAemtSaeFSjXKW/aroV7akBbY+Sv+RkyqFjgAe73F+MR0TBWKBRDkmfWq/HiFmdavfZ1G7h4SPZXaCSg==", "dev": true }, "pinkie-promise": { "version": "2.0.1", "resolved": "https://registry.npmjs.org/pinkie-promise/-/pinkie-promise-2.0.1.tgz", - "integrity": "sha1-ITXW36ejWMBprJsXh3YogihFD/o=", + "integrity": "sha512-0Gni6D4UcLTbv9c57DfxDGdr41XfgUjqWZu492f0cIGr16zDU06BWP/RAEvOuo7CQ0CNjHaLlM59YJJFm3NWlw==", "dev": true, "requires": { "pinkie": "^2.0.0" @@ -12496,9 +12908,9 @@ "dev": true }, "protocols": { - "version": "1.4.8", - "resolved": "https://registry.npmjs.org/protocols/-/protocols-1.4.8.tgz", - "integrity": "sha512-IgjKyaUSjsROSO8/D49Ab7hP8mJgTYcqApOqdPhLoPxAplXmkp+zRvsrSQjFn5by0rhm4VH0GAUELIPpx7B1yg==", + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/protocols/-/protocols-2.0.1.tgz", + "integrity": "sha512-/XJ368cyBJ7fzLMwLKv1e4vLxOju2MNAIokcr7meSaNcVbWz/CPcW22cP04mwxOErdA5mwjA8Q6w/cdAQxVn7Q==", "dev": true }, "proxy-agent": { @@ -12549,9 +12961,9 @@ } }, "qs": { - "version": "6.10.3", - "resolved": "https://registry.npmjs.org/qs/-/qs-6.10.3.tgz", - "integrity": "sha512-wr7M2E0OFRfIfJZjKGieI8lBKb7fRCH4Fv5KNPEs7gJ8jadvotdsS08PzOKR7opXhZ/Xkjtt3WF9g38drmyRqQ==", + "version": "6.11.0", + "resolved": "https://registry.npmjs.org/qs/-/qs-6.11.0.tgz", + "integrity": "sha512-MvjoMCJwEarSbUYk5O+nmoSzSutSsTwF85zcHPQ9OrlFoZOYIjaqBAJIqIXjptyD5vThxGq52Xu/MaJzRkIk4Q==", "dev": true, "requires": { "side-channel": "^1.0.4" @@ -12651,12 +13063,23 @@ "rechoir": { "version": "0.6.2", "resolved": "https://registry.npmjs.org/rechoir/-/rechoir-0.6.2.tgz", - "integrity": "sha1-hSBLVNuoLVdC4oyWdW70OvUOM4Q=", + "integrity": "sha512-HFM8rkZ+i3zrV+4LQjwQ0W+ez98pApMGM3HUrN04j3CqzPOzl9nmP15Y8YXNm8QHGv/eacOVEjqhmWpkRV0NAw==", "dev": true, "requires": { "resolve": "^1.1.6" } }, + "regexp.prototype.flags": { + "version": "1.4.3", + "resolved": "https://registry.npmjs.org/regexp.prototype.flags/-/regexp.prototype.flags-1.4.3.tgz", + "integrity": "sha512-fjggEOO3slI6Wvgjwflkc4NFRCTZAu5CnNfBd5qOMYhWdn67nJBBu34/TkD++eeFmd8C9r9jfXJ27+nSiRkSUA==", + "dev": true, + "requires": { + "call-bind": "^1.0.2", + "define-properties": "^1.1.3", + "functions-have-names": "^1.2.2" + } + }, "regexpp": { "version": "3.2.0", "resolved": "https://registry.npmjs.org/regexpp/-/regexpp-3.2.0.tgz", @@ -12763,7 +13186,7 @@ "release-zalgo": { "version": "1.0.0", "resolved": "https://registry.npmjs.org/release-zalgo/-/release-zalgo-1.0.0.tgz", - "integrity": "sha1-CXALflB0Mpc5Mw5TXFqQ+2eFFzA=", + "integrity": "sha512-gUAyHVHPPC5wdqX/LG4LWtRYtgjxyX78oanFNTMMyFEfOqdC54s3eE82imuWKbOeqYht2CrNf64Qb8vgmmtZGA==", "dev": true, "requires": { "es6-error": "^4.0.1" @@ -12772,7 +13195,7 @@ "require-directory": { "version": "2.1.1", "resolved": "https://registry.npmjs.org/require-directory/-/require-directory-2.1.1.tgz", - "integrity": "sha1-jGStX9MNqxyXbiNE/+f3kqam30I=", + "integrity": "sha512-fGxEI7+wsG9xrvdjsrlmL22OMTTiHRwAMroiEeMgq8gzoLC/PQr7RsRDSTLUg/bZAZtF+TVIkHc6/4RIKrui+Q==", "dev": true }, "require-main-filename": { @@ -12782,12 +13205,12 @@ "dev": true }, "resolve": { - "version": "1.22.0", - "resolved": "https://registry.npmjs.org/resolve/-/resolve-1.22.0.tgz", - "integrity": "sha512-Hhtrw0nLeSrFQ7phPp4OOcVjLPIeMnRlr5mcnVuMe7M/7eBn98A3hmFRLoFo3DLZkivSYwhRUJTyPyWAk56WLw==", + "version": "1.22.1", + "resolved": "https://registry.npmjs.org/resolve/-/resolve-1.22.1.tgz", + "integrity": "sha512-nBpuuYuY5jFsli/JIs1oldw6fOQCBioohqWZg/2hiaOybXOft4lonv85uDOKXdf8rhyK159cxU5cDcK/NKk8zw==", "dev": true, "requires": { - "is-core-module": "^2.8.1", + "is-core-module": "^2.9.0", "path-parse": "^1.0.7", "supports-preserve-symlinks-flag": "^1.0.0" } @@ -12958,7 +13381,7 @@ "set-blocking": { "version": "2.0.0", "resolved": "https://registry.npmjs.org/set-blocking/-/set-blocking-2.0.0.tgz", - "integrity": "sha1-BF+XgtARrppoA93TgrJDkrPYkPc=", + "integrity": "sha512-KiKBS8AnWGEyLzofFfmvKwpdPzqiy16LvQfK3yv/fVH7Bj13/wl3JSR1J+rfgRE9q7xUJK4qvgS8raSOeLUehw==", "dev": true }, "setprototypeof": { @@ -13107,7 +13530,7 @@ "sprintf-js": { "version": "1.0.3", "resolved": "https://registry.npmjs.org/sprintf-js/-/sprintf-js-1.0.3.tgz", - "integrity": "sha1-BOaSb2YolTVPPdAVIDYzuFcpfiw=", + "integrity": "sha512-D9cPgkvLlV3t3IzL0D0YLvGA9Ahk4PcvVwUbN0dSGr1aP0Nrt4AEnTUbuGvquEC0mA64Gqt1fzirlRs5ibXx8g==", "dev": true }, "statuses": { @@ -13119,7 +13542,7 @@ "strict-uri-encode": { "version": "2.0.0", "resolved": "https://registry.npmjs.org/strict-uri-encode/-/strict-uri-encode-2.0.0.tgz", - "integrity": "sha1-ucczDHBChi9rFC3CdLvMWGbONUY=", + "integrity": "sha512-QwiXZgpRcKkhTj2Scnn++4PKtWsH0kpzZ62L2R6c/LUVYv7hVnZqcg2+sMuT6R7Jusu1vviK/MFsu6kNJfWlEQ==", "dev": true }, "string_decoder": { @@ -13140,43 +13563,45 @@ } }, "string-width": { - "version": "4.2.3", - "resolved": "https://registry.npmjs.org/string-width/-/string-width-4.2.3.tgz", - "integrity": "sha512-wKyQRQpjJ0sIp62ErSZdGsjMJWsap5oRNihHhu6G7JVO/9jIB6UyevL+tXuOqrng8j/cxKTWyWUwvSTriiZz/g==", + "version": "5.1.2", + "resolved": "https://registry.npmjs.org/string-width/-/string-width-5.1.2.tgz", + "integrity": "sha512-HnLOCR3vjcY8beoNLtcjZ5/nxn2afmME6lhrDrebokqMap+XbeW8n9TXpPDOqdGK5qcI3oT0GKTW6wC7EMiVqA==", "dev": true, "requires": { - "emoji-regex": "^8.0.0", - "is-fullwidth-code-point": "^3.0.0", - "strip-ansi": "^6.0.1" + "eastasianwidth": "^0.2.0", + "emoji-regex": "^9.2.2", + "strip-ansi": "^7.0.1" } }, "string.prototype.trimend": { - "version": "1.0.4", - "resolved": "https://registry.npmjs.org/string.prototype.trimend/-/string.prototype.trimend-1.0.4.tgz", - "integrity": "sha512-y9xCjw1P23Awk8EvTpcyL2NIr1j7wJ39f+k6lvRnSMz+mz9CGz9NYPelDk42kOz6+ql8xjfK8oYzy3jAP5QU5A==", + "version": "1.0.5", + "resolved": "https://registry.npmjs.org/string.prototype.trimend/-/string.prototype.trimend-1.0.5.tgz", + "integrity": "sha512-I7RGvmjV4pJ7O3kdf+LXFpVfdNOxtCW/2C8f6jNiW4+PQchwxkCDzlk1/7p+Wl4bqFIZeF47qAHXLuHHWKAxog==", "dev": true, "requires": { "call-bind": "^1.0.2", - "define-properties": "^1.1.3" + "define-properties": "^1.1.4", + "es-abstract": "^1.19.5" } }, "string.prototype.trimstart": { - "version": "1.0.4", - "resolved": "https://registry.npmjs.org/string.prototype.trimstart/-/string.prototype.trimstart-1.0.4.tgz", - "integrity": "sha512-jh6e984OBfvxS50tdY2nRZnoC5/mLFKOREQfw8t5yytkoUsJRNxvI/E39qu1sD0OtWI3OC0XgKSmcWwziwYuZw==", + "version": "1.0.5", + "resolved": "https://registry.npmjs.org/string.prototype.trimstart/-/string.prototype.trimstart-1.0.5.tgz", + "integrity": "sha512-THx16TJCGlsN0o6dl2o6ncWUsdgnLRSA23rRE5pyGBw/mLr3Ej/R2LaqCtgP8VNMGZsvMWnf9ooZPyY2bHvUFg==", "dev": true, "requires": { "call-bind": "^1.0.2", - "define-properties": "^1.1.3" + "define-properties": "^1.1.4", + "es-abstract": "^1.19.5" } }, "strip-ansi": { - "version": "6.0.1", - "resolved": "https://registry.npmjs.org/strip-ansi/-/strip-ansi-6.0.1.tgz", - "integrity": "sha512-Y38VPSHcqkFrCpFnQ9vuSXmquuv5oXOKpGeT6aGrr3o3Gc9AlVa6JBfUSOCnbxGGZF+/0ooI7KrPuUSztUdU5A==", + "version": "7.0.1", + "resolved": "https://registry.npmjs.org/strip-ansi/-/strip-ansi-7.0.1.tgz", + "integrity": "sha512-cXNxvT8dFNRVfhVME3JAe98mkXDYN2O1l7jmcwMnOslDeESg1rF/OZMtK0nRAhiari1unG5cD4jG3rapUAkLbw==", "dev": true, "requires": { - "ansi-regex": "^5.0.1" + "ansi-regex": "^6.0.1" } }, "strip-bom": { @@ -13204,6 +13629,14 @@ "dev": true, "requires": { "escape-string-regexp": "^1.0.2" + }, + "dependencies": { + "escape-string-regexp": { + "version": "1.0.5", + "resolved": "https://registry.npmjs.org/escape-string-regexp/-/escape-string-regexp-1.0.5.tgz", + "integrity": "sha512-vbRorB5FUQWvla16U8R/qgaFIya2qGzwDrNmCZuYKrbdSUMG6I1ZCGQRefkRVhuOkIGVne7BQ35DSfo1qvJqFg==", + "dev": true + } } }, "supports-color": { @@ -13235,7 +13668,7 @@ "text-table": { "version": "0.2.0", "resolved": "https://registry.npmjs.org/text-table/-/text-table-0.2.0.tgz", - "integrity": "sha1-f17oI66AUgfACvLfSoTsP8+lcLQ=", + "integrity": "sha512-N+8UisAXDGk8PFXP4HAzVR9nbfmVJ3zYLAWiTIoqC5v5isinhr+r5uaO8+7r3BMfuNIufIsA7RdpVgacC2cSpw==", "dev": true }, "through": { @@ -13256,7 +13689,7 @@ "to-fast-properties": { "version": "2.0.0", "resolved": "https://registry.npmjs.org/to-fast-properties/-/to-fast-properties-2.0.0.tgz", - "integrity": "sha1-3F5pjL0HkmW8c+A3doGk5Og/YW4=", + "integrity": "sha512-/OaKK0xYrs3DmxRYqL/yDc+FxFUVYhDlXMhRmv3z915w2HF1tnN1omB354j8VUGO/hbRzyD6Y3sA7v7GS/ceog==", "dev": true }, "to-regex-range": { @@ -13277,16 +13710,24 @@ "tr46": { "version": "0.0.3", "resolved": "https://registry.npmjs.org/tr46/-/tr46-0.0.3.tgz", - "integrity": "sha1-gYT9NH2snNwYWZLzpmIuFLnZq2o=", + "integrity": "sha512-N3WMsuqV66lT30CrXNbEjx4GEwlow3v6rr4mCcv6prnfwhS01rkgyFdjPNBYd9br7LpXV1+Emh01fHnq2Gdgrw==", "dev": true }, "trim-repeated": { "version": "1.0.0", "resolved": "https://registry.npmjs.org/trim-repeated/-/trim-repeated-1.0.0.tgz", - "integrity": "sha1-42RqLqTokTEr9+rObPsFOAvAHCE=", + "integrity": "sha512-pkonvlKk8/ZuR0D5tLW8ljt5I8kmxp2XKymhepUeOdCEfKpZaktSArkLHZt76OB1ZvO9bssUsDty4SWhLvZpLg==", "dev": true, "requires": { "escape-string-regexp": "^1.0.2" + }, + "dependencies": { + "escape-string-regexp": { + "version": "1.0.5", + "resolved": "https://registry.npmjs.org/escape-string-regexp/-/escape-string-regexp-1.0.5.tgz", + "integrity": "sha512-vbRorB5FUQWvla16U8R/qgaFIya2qGzwDrNmCZuYKrbdSUMG6I1ZCGQRefkRVhuOkIGVne7BQ35DSfo1qvJqFg==", + "dev": true + } } }, "ts-node": { @@ -13357,9 +13798,9 @@ "dev": true }, "type-fest": { - "version": "0.20.2", - "resolved": "https://registry.npmjs.org/type-fest/-/type-fest-0.20.2.tgz", - "integrity": "sha512-Ne+eE4r0/iWnpAxD852z3A+N0Bt5RN//NjJwRd2VFHEmrywxf5vsZlh4R6lixl6B+wz/8d+maTSAkN1FIkI3LQ==", + "version": "1.4.0", + "resolved": "https://registry.npmjs.org/type-fest/-/type-fest-1.4.0.tgz", + "integrity": "sha512-yGSza74xk0UG8k+pLh5oeoYirvIiWo5t0/o3zHHAO2tRDiZcxWP7fywNlXhqb6/r6sWvwi+RsyQMWhVLe4BVuA==", "dev": true }, "typedarray-to-buffer": { @@ -13445,9 +13886,19 @@ "unpipe": { "version": "1.0.0", "resolved": "https://registry.npmjs.org/unpipe/-/unpipe-1.0.0.tgz", - "integrity": "sha1-sr9O6FFKrmFltIF4KdIbLvSZBOw=", + "integrity": "sha512-pjy2bYhSsufwWlKwPc+l3cN7+wuJlK6uz0YdJEOlQDbl6jo/YlPi4mb8agUkVC8BF7V8NuzeyPNqRksA3hztKQ==", "dev": true }, + "update-browserslist-db": { + "version": "1.0.4", + "resolved": "https://registry.npmjs.org/update-browserslist-db/-/update-browserslist-db-1.0.4.tgz", + "integrity": "sha512-jnmO2BEGUjsMOe/Fg9u0oczOe/ppIDZPebzccl1yDWGLFP16Pa1/RM5wEoKYPG2zstNcDuAStejyxsOuKINdGA==", + "dev": true, + "requires": { + "escalade": "^3.1.1", + "picocolors": "^1.0.0" + } + }, "update-notifier": { "version": "6.0.2", "resolved": "https://registry.npmjs.org/update-notifier/-/update-notifier-6.0.2.tgz", @@ -13518,9 +13969,9 @@ "dev": true }, "uuid": { - "version": "3.4.0", - "resolved": "https://registry.npmjs.org/uuid/-/uuid-3.4.0.tgz", - "integrity": "sha512-HjSDRw6gZE5JMggctHBcjVak08+KEVhSIiDzFnT9S9aegmp85S/bReBVTb4QTFaRNptJ9kuYaNhnbNEOkbKb/A==", + "version": "8.3.2", + "resolved": "https://registry.npmjs.org/uuid/-/uuid-8.3.2.tgz", + "integrity": "sha512-+NYs2QeMWy+GWFOEm9xnn6HCDp0l7QBD7ml8zLUmJ+93Q5NF0NocErnwkTkXVFNiX3/fpC6afS8Dhb/gz7R7eg==", "dev": true }, "v8-compile-cache": { @@ -13569,13 +14020,13 @@ "webidl-conversions": { "version": "3.0.1", "resolved": "https://registry.npmjs.org/webidl-conversions/-/webidl-conversions-3.0.1.tgz", - "integrity": "sha1-JFNCdeKnvGvnvIZhHMFq4KVlSHE=", + "integrity": "sha512-2JAn3z8AR6rjK8Sm8orRC0h/bcl/DqL7tRPdGZ4I1CjdF+EaMLmYxBHyXuKL849eucPFhvBoxMsflfOb8kxaeQ==", "dev": true }, "whatwg-url": { "version": "5.0.0", "resolved": "https://registry.npmjs.org/whatwg-url/-/whatwg-url-5.0.0.tgz", - "integrity": "sha1-lmRU6HZUYuN2RNNib2dCzotwll0=", + "integrity": "sha512-saE57nupxk6v3HY35+jzBwYa0rKSy0XR8JSxZPwgLr7ys0IBzhGviA1/TUGJLmSVqs8pb9AnvICXEuOHLprYTw==", "dev": true, "requires": { "tr46": "~0.0.3", @@ -13607,7 +14058,7 @@ "which-module": { "version": "2.0.0", "resolved": "https://registry.npmjs.org/which-module/-/which-module-2.0.0.tgz", - "integrity": "sha1-2e8H3Od7mQK4o6j6SzHD4/fm6Ho=", + "integrity": "sha512-B+enWhmw6cjfVC7kS8Pj9pCrKSc5txArRyaYGe088shv/FGWH+0Rjx/xPgtsWfsUtS27FkP697E4DDhgrgoc0Q==", "dev": true }, "widest-line": { @@ -13617,40 +14068,6 @@ "dev": true, "requires": { "string-width": "^5.0.1" - }, - "dependencies": { - "ansi-regex": { - "version": "6.0.1", - "resolved": "https://registry.npmjs.org/ansi-regex/-/ansi-regex-6.0.1.tgz", - "integrity": "sha512-n5M855fKb2SsfMIiFFoVrABHJC8QtHwVx+mHWP3QcEqBHYienj5dHSgjbxtC0WEZXYt4wcD6zrQElDPhFuZgfA==", - "dev": true - }, - "emoji-regex": { - "version": "9.2.2", - "resolved": "https://registry.npmjs.org/emoji-regex/-/emoji-regex-9.2.2.tgz", - "integrity": "sha512-L18DaJsXSUk2+42pv8mLs5jJT2hqFkFE4j21wOmgbUqsZ2hL72NsUU785g9RXgo3s0ZNgVl42TiHp3ZtOv/Vyg==", - "dev": true - }, - "string-width": { - "version": "5.1.2", - "resolved": "https://registry.npmjs.org/string-width/-/string-width-5.1.2.tgz", - "integrity": "sha512-HnLOCR3vjcY8beoNLtcjZ5/nxn2afmME6lhrDrebokqMap+XbeW8n9TXpPDOqdGK5qcI3oT0GKTW6wC7EMiVqA==", - "dev": true, - "requires": { - "eastasianwidth": "^0.2.0", - "emoji-regex": "^9.2.2", - "strip-ansi": "^7.0.1" - } - }, - "strip-ansi": { - "version": "7.0.1", - "resolved": "https://registry.npmjs.org/strip-ansi/-/strip-ansi-7.0.1.tgz", - "integrity": "sha512-cXNxvT8dFNRVfhVME3JAe98mkXDYN2O1l7jmcwMnOslDeESg1rF/OZMtK0nRAhiari1unG5cD4jG3rapUAkLbw==", - "dev": true, - "requires": { - "ansi-regex": "^6.0.1" - } - } } }, "wildcard-match": { @@ -13742,20 +14159,20 @@ "dev": true }, "wrap-ansi": { - "version": "7.0.0", - "resolved": "https://registry.npmjs.org/wrap-ansi/-/wrap-ansi-7.0.0.tgz", - "integrity": "sha512-YVGIj2kamLSTxw6NsZjoBxfSwsn0ycdesmc4p+Q21c5zPuZ1pl+NfxVdxPtdHvmNVOQ6XSYG4AUtyt/Fi7D16Q==", + "version": "8.0.1", + "resolved": "https://registry.npmjs.org/wrap-ansi/-/wrap-ansi-8.0.1.tgz", + "integrity": "sha512-QFF+ufAqhoYHvoHdajT/Po7KoXVBPXS2bgjIam5isfWJPfIOnQZ50JtUiVvCv/sjgacf3yRrt2ZKUZ/V4itN4g==", "dev": true, "requires": { - "ansi-styles": "^4.0.0", - "string-width": "^4.1.0", - "strip-ansi": "^6.0.0" + "ansi-styles": "^6.1.0", + "string-width": "^5.0.1", + "strip-ansi": "^7.0.1" } }, "wrappy": { "version": "1.0.2", "resolved": "https://registry.npmjs.org/wrappy/-/wrappy-1.0.2.tgz", - "integrity": "sha1-tSQ9jz7BqjXxNkYFvA0QNuMKtp8=", + "integrity": "sha512-l4Sp/DRseor9wL6EvV2+TuQn63dMkPjZ/sp9XkghTEbV9KlPS1xUsZ3u7/IQO4wxtcFB4bgpQPRcR3QCvezPcQ==", "dev": true }, "write-file-atomic": { @@ -13779,7 +14196,7 @@ "xregexp": { "version": "2.0.0", "resolved": "https://registry.npmjs.org/xregexp/-/xregexp-2.0.0.tgz", - "integrity": "sha1-UqY+VsoLhKfzpfPWGHLxJq16WUM=", + "integrity": "sha512-xl/50/Cf32VsGq/1R8jJE5ajH1yMCQkpmoS10QbFZWl2Oor4H0Me64Pu2yxvsRWK3m6soJbmGfzSR7BYmDcWAA==", "dev": true }, "y18n": { @@ -13819,6 +14236,38 @@ "yargs-parser": "^18.1.2" }, "dependencies": { + "ansi-regex": { + "version": "5.0.1", + "resolved": "https://registry.npmjs.org/ansi-regex/-/ansi-regex-5.0.1.tgz", + "integrity": "sha512-quJQXlTSUGL2LH9SUXo8VwsY4soanhgo6LNSm84E1LBcE8s3O0wpdiRzyR9z/ZZJMlMWv37qOOb9pdJlMUEKFQ==", + "dev": true + }, + "emoji-regex": { + "version": "8.0.0", + "resolved": "https://registry.npmjs.org/emoji-regex/-/emoji-regex-8.0.0.tgz", + "integrity": "sha512-MSjYzcWNOA0ewAHpz0MxpYFvwg6yjy1NG3xteoqz644VCo/RPgnr1/GGt+ic3iJTzQ8Eu3TdM14SawnVUmGE6A==", + "dev": true + }, + "string-width": { + "version": "4.2.3", + "resolved": "https://registry.npmjs.org/string-width/-/string-width-4.2.3.tgz", + "integrity": "sha512-wKyQRQpjJ0sIp62ErSZdGsjMJWsap5oRNihHhu6G7JVO/9jIB6UyevL+tXuOqrng8j/cxKTWyWUwvSTriiZz/g==", + "dev": true, + "requires": { + "emoji-regex": "^8.0.0", + "is-fullwidth-code-point": "^3.0.0", + "strip-ansi": "^6.0.1" + } + }, + "strip-ansi": { + "version": "6.0.1", + "resolved": "https://registry.npmjs.org/strip-ansi/-/strip-ansi-6.0.1.tgz", + "integrity": "sha512-Y38VPSHcqkFrCpFnQ9vuSXmquuv5oXOKpGeT6aGrr3o3Gc9AlVa6JBfUSOCnbxGGZF+/0ooI7KrPuUSztUdU5A==", + "dev": true, + "requires": { + "ansi-regex": "^5.0.1" + } + }, "yargs-parser": { "version": "18.1.3", "resolved": "https://registry.npmjs.org/yargs-parser/-/yargs-parser-18.1.3.tgz", diff --git a/package.json b/package.json index bb6d21efb3e..59dc4776d06 100644 --- a/package.json +++ b/package.json @@ -24,17 +24,17 @@ }, "dependencies": { "@redis/bloom": "1.0.2", - "@redis/client": "1.1.0", + "@redis/client": "1.1.1", "@redis/graph": "1.0.1", "@redis/json": "1.0.3", "@redis/search": "1.0.6", "@redis/time-series": "1.0.3" }, "devDependencies": { - "@tsconfig/node14": "^1.0.1", - "gh-pages": "^3.2.3", - "release-it": "^15.0.0", - "typescript": "^4.6.4" + "@tsconfig/node14": "^1.0.3", + "gh-pages": "^4.0.0", + "release-it": "^15.1.1", + "typescript": "^4.7.4" }, "repository": { "type": "git", From 01edb7c3057460a384892d89abf15ee12c9592a4 Mon Sep 17 00:00:00 2001 From: leibale Date: Thu, 30 Jun 2022 13:42:11 -0400 Subject: [PATCH 1228/1748] Release redis@4.1.1 --- package-lock.json | 4 ++-- package.json | 2 +- 2 files changed, 3 insertions(+), 3 deletions(-) diff --git a/package-lock.json b/package-lock.json index 167e9a82894..57b44e30b1c 100644 --- a/package-lock.json +++ b/package-lock.json @@ -1,12 +1,12 @@ { "name": "redis", - "version": "4.1.0", + "version": "4.1.1", "lockfileVersion": 2, "requires": true, "packages": { "": { "name": "redis", - "version": "4.1.0", + "version": "4.1.1", "license": "MIT", "workspaces": [ "./packages/*" diff --git a/package.json b/package.json index 59dc4776d06..acbcbefc9ce 100644 --- a/package.json +++ b/package.json @@ -1,7 +1,7 @@ { "name": "redis", "description": "A modern, high performance Redis client", - "version": "4.1.0", + "version": "4.1.1", "license": "MIT", "main": "./dist/index.js", "types": "./dist/index.d.ts", From 9ffc30c7271dfd6739178e57161f8d47733c0505 Mon Sep 17 00:00:00 2001 From: leibale Date: Tue, 5 Jul 2022 11:42:43 -0400 Subject: [PATCH 1229/1748] upgrade deps --- package-lock.json | 428 +++++++++++++++--------------- packages/bloom/package.json | 6 +- packages/client/package.json | 12 +- packages/graph/package.json | 6 +- packages/json/package.json | 6 +- packages/search/package.json | 6 +- packages/test-utils/package.json | 4 +- packages/time-series/package.json | 6 +- 8 files changed, 237 insertions(+), 237 deletions(-) diff --git a/package-lock.json b/package-lock.json index 57b44e30b1c..c9489520e52 100644 --- a/package-lock.json +++ b/package-lock.json @@ -679,18 +679,18 @@ } }, "node_modules/@octokit/openapi-types": { - "version": "12.5.0", - "resolved": "https://registry.npmjs.org/@octokit/openapi-types/-/openapi-types-12.5.0.tgz", - "integrity": "sha512-VatvE5wtRkJq6hAWGTBZ62WkrdlCiy0G0u27cVOYTfAWVZi7QqTurVcjpsyc5+9hXLPRP5O/DaNEs4TgAp4Mqg==", + "version": "12.7.0", + "resolved": "https://registry.npmjs.org/@octokit/openapi-types/-/openapi-types-12.7.0.tgz", + "integrity": "sha512-vWXEPETgvltt9jEYdNtQTM8xnsQ7loEbBaLV26V7Tx8ovoN8P7R3XvhFeWiboqNhlXuBsIg1QI979WElB5mGXw==", "dev": true }, "node_modules/@octokit/plugin-paginate-rest": { - "version": "2.21.0", - "resolved": "https://registry.npmjs.org/@octokit/plugin-paginate-rest/-/plugin-paginate-rest-2.21.0.tgz", - "integrity": "sha512-MoGEKjvDpTOCVb5gbeiW7kZm/cRfT256UJwHEuy+y+gTUuKziyXaiOkt5rM/4nzhp8UxVgvok9Tu7dMMpUybiQ==", + "version": "2.21.1", + "resolved": "https://registry.npmjs.org/@octokit/plugin-paginate-rest/-/plugin-paginate-rest-2.21.1.tgz", + "integrity": "sha512-NVNTK63yoTFp07GqISWK+uDfGH1CAPhQXS7LzsJBvaK5W+UlvG549pLZC55FK0FqANVl6q/9ra3SR5c97xF/sw==", "dev": true, "dependencies": { - "@octokit/types": "^6.38.1" + "@octokit/types": "^6.38.2" }, "peerDependencies": { "@octokit/core": ">=2" @@ -706,12 +706,12 @@ } }, "node_modules/@octokit/plugin-rest-endpoint-methods": { - "version": "5.16.0", - "resolved": "https://registry.npmjs.org/@octokit/plugin-rest-endpoint-methods/-/plugin-rest-endpoint-methods-5.16.0.tgz", - "integrity": "sha512-mvdwq+LvhR2GRDY82FgSZ52xX6wkOCpjiI3amiKbzKHd9nyKeFdXLsIQ3Go12tWRtvo+HwqoypLHDjRrgMFDQA==", + "version": "5.16.1", + "resolved": "https://registry.npmjs.org/@octokit/plugin-rest-endpoint-methods/-/plugin-rest-endpoint-methods-5.16.1.tgz", + "integrity": "sha512-RMHD3aJZvOpjR2fGzD2an6eU7LG8MsknhUHvP+wRUnKdbt7eDdhTMLQsZ4xsHZcLNsxPO/K4DDIZPhI2s571Ag==", "dev": true, "dependencies": { - "@octokit/types": "^6.38.0", + "@octokit/types": "^6.38.2", "deprecation": "^2.3.1" }, "peerDependencies": { @@ -756,12 +756,12 @@ } }, "node_modules/@octokit/types": { - "version": "6.38.1", - "resolved": "https://registry.npmjs.org/@octokit/types/-/types-6.38.1.tgz", - "integrity": "sha512-kWMohLCIvnwApRmxRFDOqve7puiNNdtVfgwdDOm6QyJNorWOgKv2/AodCcGqx63o28kF7Dr4/nJCatrwwqhULg==", + "version": "6.39.0", + "resolved": "https://registry.npmjs.org/@octokit/types/-/types-6.39.0.tgz", + "integrity": "sha512-Mq4N9sOAYCitTsBtDdRVrBE80lIrMBhL9Jbrw0d+j96BAzlq4V+GLHFJbHokEsVvO/9tQupQdoFdgVYhD2C8UQ==", "dev": true, "dependencies": { - "@octokit/openapi-types": "^12.5.0" + "@octokit/openapi-types": "^12.7.0" } }, "node_modules/@pnpm/network.ca-file": { @@ -955,9 +955,9 @@ "dev": true }, "node_modules/@types/node": { - "version": "18.0.0", - "resolved": "https://registry.npmjs.org/@types/node/-/node-18.0.0.tgz", - "integrity": "sha512-cHlGmko4gWLVI27cGJntjs/Sj8th9aYwplmZFwmmgYQQvL5NUsgVJG7OddLvNfLqYS31KFN0s3qlaD9qCaxACA==", + "version": "18.0.1", + "resolved": "https://registry.npmjs.org/@types/node/-/node-18.0.1.tgz", + "integrity": "sha512-CmR8+Tsy95hhwtZBKJBs0/FFq4XX7sDZHlGGf+0q+BRZfMbOTkzkj0AFAuTyXbObDIoanaBBW0+KEW+m3N16Wg==", "dev": true }, "node_modules/@types/parse-json": { @@ -1012,14 +1012,14 @@ "dev": true }, "node_modules/@typescript-eslint/eslint-plugin": { - "version": "5.30.0", - "resolved": "https://registry.npmjs.org/@typescript-eslint/eslint-plugin/-/eslint-plugin-5.30.0.tgz", - "integrity": "sha512-lvhRJ2pGe2V9MEU46ELTdiHgiAFZPKtLhiU5wlnaYpMc2+c1R8fh8i80ZAa665drvjHKUJyRRGg3gEm1If54ow==", + "version": "5.30.5", + "resolved": "https://registry.npmjs.org/@typescript-eslint/eslint-plugin/-/eslint-plugin-5.30.5.tgz", + "integrity": "sha512-lftkqRoBvc28VFXEoRgyZuztyVUQ04JvUnATSPtIRFAccbXTWL6DEtXGYMcbg998kXw1NLUJm7rTQ9eUt+q6Ig==", "dev": true, "dependencies": { - "@typescript-eslint/scope-manager": "5.30.0", - "@typescript-eslint/type-utils": "5.30.0", - "@typescript-eslint/utils": "5.30.0", + "@typescript-eslint/scope-manager": "5.30.5", + "@typescript-eslint/type-utils": "5.30.5", + "@typescript-eslint/utils": "5.30.5", "debug": "^4.3.4", "functional-red-black-tree": "^1.0.1", "ignore": "^5.2.0", @@ -1078,14 +1078,14 @@ "dev": true }, "node_modules/@typescript-eslint/parser": { - "version": "5.30.0", - "resolved": "https://registry.npmjs.org/@typescript-eslint/parser/-/parser-5.30.0.tgz", - "integrity": "sha512-2oYYUws5o2liX6SrFQ5RB88+PuRymaM2EU02/9Ppoyu70vllPnHVO7ioxDdq/ypXHA277R04SVjxvwI8HmZpzA==", + "version": "5.30.5", + "resolved": "https://registry.npmjs.org/@typescript-eslint/parser/-/parser-5.30.5.tgz", + "integrity": "sha512-zj251pcPXI8GO9NDKWWmygP6+UjwWmrdf9qMW/L/uQJBM/0XbU2inxe5io/234y/RCvwpKEYjZ6c1YrXERkK4Q==", "dev": true, "dependencies": { - "@typescript-eslint/scope-manager": "5.30.0", - "@typescript-eslint/types": "5.30.0", - "@typescript-eslint/typescript-estree": "5.30.0", + "@typescript-eslint/scope-manager": "5.30.5", + "@typescript-eslint/types": "5.30.5", + "@typescript-eslint/typescript-estree": "5.30.5", "debug": "^4.3.4" }, "engines": { @@ -1105,13 +1105,13 @@ } }, "node_modules/@typescript-eslint/scope-manager": { - "version": "5.30.0", - "resolved": "https://registry.npmjs.org/@typescript-eslint/scope-manager/-/scope-manager-5.30.0.tgz", - "integrity": "sha512-3TZxvlQcK5fhTBw5solQucWSJvonXf5yua5nx8OqK94hxdrT7/6W3/CS42MLd/f1BmlmmbGEgQcTHHCktUX5bQ==", + "version": "5.30.5", + "resolved": "https://registry.npmjs.org/@typescript-eslint/scope-manager/-/scope-manager-5.30.5.tgz", + "integrity": "sha512-NJ6F+YHHFT/30isRe2UTmIGGAiXKckCyMnIV58cE3JkHmaD6e5zyEYm5hBDv0Wbin+IC0T1FWJpD3YqHUG/Ydg==", "dev": true, "dependencies": { - "@typescript-eslint/types": "5.30.0", - "@typescript-eslint/visitor-keys": "5.30.0" + "@typescript-eslint/types": "5.30.5", + "@typescript-eslint/visitor-keys": "5.30.5" }, "engines": { "node": "^12.22.0 || ^14.17.0 || >=16.0.0" @@ -1122,12 +1122,12 @@ } }, "node_modules/@typescript-eslint/type-utils": { - "version": "5.30.0", - "resolved": "https://registry.npmjs.org/@typescript-eslint/type-utils/-/type-utils-5.30.0.tgz", - "integrity": "sha512-GF8JZbZqSS+azehzlv/lmQQ3EU3VfWYzCczdZjJRxSEeXDQkqFhCBgFhallLDbPwQOEQ4MHpiPfkjKk7zlmeNg==", + "version": "5.30.5", + "resolved": "https://registry.npmjs.org/@typescript-eslint/type-utils/-/type-utils-5.30.5.tgz", + "integrity": "sha512-k9+ejlv1GgwN1nN7XjVtyCgE0BTzhzT1YsQF0rv4Vfj2U9xnslBgMYYvcEYAFVdvhuEscELJsB7lDkN7WusErw==", "dev": true, "dependencies": { - "@typescript-eslint/utils": "5.30.0", + "@typescript-eslint/utils": "5.30.5", "debug": "^4.3.4", "tsutils": "^3.21.0" }, @@ -1148,9 +1148,9 @@ } }, "node_modules/@typescript-eslint/types": { - "version": "5.30.0", - "resolved": "https://registry.npmjs.org/@typescript-eslint/types/-/types-5.30.0.tgz", - "integrity": "sha512-vfqcBrsRNWw/LBXyncMF/KrUTYYzzygCSsVqlZ1qGu1QtGs6vMkt3US0VNSQ05grXi5Yadp3qv5XZdYLjpp8ag==", + "version": "5.30.5", + "resolved": "https://registry.npmjs.org/@typescript-eslint/types/-/types-5.30.5.tgz", + "integrity": "sha512-kZ80w/M2AvsbRvOr3PjaNh6qEW1LFqs2pLdo2s5R38B2HYXG8Z0PP48/4+j1QHJFL3ssHIbJ4odPRS8PlHrFfw==", "dev": true, "engines": { "node": "^12.22.0 || ^14.17.0 || >=16.0.0" @@ -1161,13 +1161,13 @@ } }, "node_modules/@typescript-eslint/typescript-estree": { - "version": "5.30.0", - "resolved": "https://registry.npmjs.org/@typescript-eslint/typescript-estree/-/typescript-estree-5.30.0.tgz", - "integrity": "sha512-hDEawogreZB4n1zoqcrrtg/wPyyiCxmhPLpZ6kmWfKF5M5G0clRLaEexpuWr31fZ42F96SlD/5xCt1bT5Qm4Nw==", + "version": "5.30.5", + "resolved": "https://registry.npmjs.org/@typescript-eslint/typescript-estree/-/typescript-estree-5.30.5.tgz", + "integrity": "sha512-qGTc7QZC801kbYjAr4AgdOfnokpwStqyhSbiQvqGBLixniAKyH+ib2qXIVo4P9NgGzwyfD9I0nlJN7D91E1VpQ==", "dev": true, "dependencies": { - "@typescript-eslint/types": "5.30.0", - "@typescript-eslint/visitor-keys": "5.30.0", + "@typescript-eslint/types": "5.30.5", + "@typescript-eslint/visitor-keys": "5.30.5", "debug": "^4.3.4", "globby": "^11.1.0", "is-glob": "^4.0.3", @@ -1250,15 +1250,15 @@ "dev": true }, "node_modules/@typescript-eslint/utils": { - "version": "5.30.0", - "resolved": "https://registry.npmjs.org/@typescript-eslint/utils/-/utils-5.30.0.tgz", - "integrity": "sha512-0bIgOgZflLKIcZsWvfklsaQTM3ZUbmtH0rJ1hKyV3raoUYyeZwcjQ8ZUJTzS7KnhNcsVT1Rxs7zeeMHEhGlltw==", + "version": "5.30.5", + "resolved": "https://registry.npmjs.org/@typescript-eslint/utils/-/utils-5.30.5.tgz", + "integrity": "sha512-o4SSUH9IkuA7AYIfAvatldovurqTAHrfzPApOZvdUq01hHojZojCFXx06D/aFpKCgWbMPRdJBWAC3sWp3itwTA==", "dev": true, "dependencies": { "@types/json-schema": "^7.0.9", - "@typescript-eslint/scope-manager": "5.30.0", - "@typescript-eslint/types": "5.30.0", - "@typescript-eslint/typescript-estree": "5.30.0", + "@typescript-eslint/scope-manager": "5.30.5", + "@typescript-eslint/types": "5.30.5", + "@typescript-eslint/typescript-estree": "5.30.5", "eslint-scope": "^5.1.1", "eslint-utils": "^3.0.0" }, @@ -1274,12 +1274,12 @@ } }, "node_modules/@typescript-eslint/visitor-keys": { - "version": "5.30.0", - "resolved": "https://registry.npmjs.org/@typescript-eslint/visitor-keys/-/visitor-keys-5.30.0.tgz", - "integrity": "sha512-6WcIeRk2DQ3pHKxU1Ni0qMXJkjO/zLjBymlYBy/53qxe7yjEFSvzKLDToJjURUhSl2Fzhkl4SMXQoETauF74cw==", + "version": "5.30.5", + "resolved": "https://registry.npmjs.org/@typescript-eslint/visitor-keys/-/visitor-keys-5.30.5.tgz", + "integrity": "sha512-D+xtGo9HUMELzWIUqcQc0p2PO4NyvTrgIOK/VnSH083+8sq0tiLozNRKuLarwHYGRuA6TVBQSuuLwJUDWd3aaA==", "dev": true, "dependencies": { - "@typescript-eslint/types": "5.30.0", + "@typescript-eslint/types": "5.30.5", "eslint-visitor-keys": "^3.3.0" }, "engines": { @@ -1671,9 +1671,9 @@ } }, "node_modules/boxen/node_modules/type-fest": { - "version": "2.15.0", - "resolved": "https://registry.npmjs.org/type-fest/-/type-fest-2.15.0.tgz", - "integrity": "sha512-hpsXfQZrAiusX8KMY5HXSEV7xqGAGxFQoNDT+iW0yJE/bdYG0uMlRaUG0kNAUbF5p6Cq5Kuf69lm4M569QtRGw==", + "version": "2.16.0", + "resolved": "https://registry.npmjs.org/type-fest/-/type-fest-2.16.0.tgz", + "integrity": "sha512-qpaThT2HQkFb83gMOrdKVsfCN7LKxP26Yq+smPzY1FqoHRjqmjqHXA7n5Gkxi8efirtbeEUxzfEdePthQWCuHw==", "dev": true, "engines": { "node": ">=12.20" @@ -1875,9 +1875,9 @@ } }, "node_modules/caniuse-lite": { - "version": "1.0.30001361", - "resolved": "https://registry.npmjs.org/caniuse-lite/-/caniuse-lite-1.0.30001361.tgz", - "integrity": "sha512-ybhCrjNtkFji1/Wto6SSJKkWk6kZgVQsDq5QI83SafsF6FXv2JB4df9eEdH6g8sdGgqTXrFLjAxqBGgYoU3azQ==", + "version": "1.0.30001363", + "resolved": "https://registry.npmjs.org/caniuse-lite/-/caniuse-lite-1.0.30001363.tgz", + "integrity": "sha512-HpQhpzTGGPVMnCjIomjt+jvyUu8vNFo3TaDiZ/RcoTrlOq/5+tC8zHdsbgFB6MxmaY+jCpsH09aD80Bb4Ow3Sg==", "dev": true, "funding": [ { @@ -2514,9 +2514,9 @@ "dev": true }, "node_modules/electron-to-chromium": { - "version": "1.4.174", - "resolved": "https://registry.npmjs.org/electron-to-chromium/-/electron-to-chromium-1.4.174.tgz", - "integrity": "sha512-JER+w+9MV2MBVFOXxP036bLlNOnzbYAWrWU8sNUwoOO69T3w4564WhM5H5atd8VVS8U4vpi0i0kdoYzm1NPQgQ==", + "version": "1.4.179", + "resolved": "https://registry.npmjs.org/electron-to-chromium/-/electron-to-chromium-1.4.179.tgz", + "integrity": "sha512-1XeTb/U/8Xgh2YgPOqhakLYsvCcU4U7jUjTMbEnhIJoIWd/Qt3yC8y0cbG+fHzn4zUNF99Ey1xiPf20bwgLO3Q==", "dev": true }, "node_modules/email-addresses": { @@ -2741,9 +2741,9 @@ } }, "node_modules/eslint": { - "version": "8.18.0", - "resolved": "https://registry.npmjs.org/eslint/-/eslint-8.18.0.tgz", - "integrity": "sha512-As1EfFMVk7Xc6/CvhssHUjsAQSkpfXvUGMFC3ce8JDe6WvqCgRrLOBQbVpsBFr1X1V+RACOadnzVvcUS5ni2bA==", + "version": "8.19.0", + "resolved": "https://registry.npmjs.org/eslint/-/eslint-8.19.0.tgz", + "integrity": "sha512-SXOPj3x9VKvPe81TjjUJCYlV4oJjQw68Uek+AM0X4p+33dj2HY5bpTZOgnQHcG2eAm1mtCU9uNMnJi7exU/kYw==", "dev": true, "dependencies": { "@eslint/eslintrc": "^1.3.0", @@ -3594,9 +3594,9 @@ } }, "node_modules/globals": { - "version": "13.15.0", - "resolved": "https://registry.npmjs.org/globals/-/globals-13.15.0.tgz", - "integrity": "sha512-bpzcOlgDhMG070Av0Vy5Owklpv1I6+j96GhUI7Rh7IzDCKLzboflLrrfqMu8NquDbiR4EOQk7XzJwqVJxicxog==", + "version": "13.16.0", + "resolved": "https://registry.npmjs.org/globals/-/globals-13.16.0.tgz", + "integrity": "sha512-A1lrQfpNF+McdPOnnFqY3kSN0AFTy485bTi1bkLk4mVPODIUEcSfhHgRqA+QdXPksrSTTztYXx37NFV+GpGk3Q==", "dev": true, "dependencies": { "type-fest": "^0.20.2" @@ -5354,9 +5354,9 @@ } }, "node_modules/new-github-release-url/node_modules/type-fest": { - "version": "2.15.0", - "resolved": "https://registry.npmjs.org/type-fest/-/type-fest-2.15.0.tgz", - "integrity": "sha512-hpsXfQZrAiusX8KMY5HXSEV7xqGAGxFQoNDT+iW0yJE/bdYG0uMlRaUG0kNAUbF5p6Cq5Kuf69lm4M569QtRGw==", + "version": "2.16.0", + "resolved": "https://registry.npmjs.org/type-fest/-/type-fest-2.16.0.tgz", + "integrity": "sha512-qpaThT2HQkFb83gMOrdKVsfCN7LKxP26Yq+smPzY1FqoHRjqmjqHXA7n5Gkxi8efirtbeEUxzfEdePthQWCuHw==", "dev": true, "engines": { "node": ">=12.20" @@ -7182,9 +7182,9 @@ } }, "node_modules/ts-node": { - "version": "10.8.1", - "resolved": "https://registry.npmjs.org/ts-node/-/ts-node-10.8.1.tgz", - "integrity": "sha512-Wwsnao4DQoJsN034wePSg5nZiw4YKXf56mPIAeD6wVmiv+RytNSWqc2f3fKvcUoV+Yn2+yocD71VOfQHbmVX4g==", + "version": "10.8.2", + "resolved": "https://registry.npmjs.org/ts-node/-/ts-node-10.8.2.tgz", + "integrity": "sha512-LYdGnoGddf1D6v8REPtIH+5iq/gTDuZqv2/UJUU7tKjuEU8xVZorBM+buCGNjj+pGEud+sOoM4CX3/YzINpENA==", "dev": true, "dependencies": { "@cspotcode/source-map-support": "^0.8.0", @@ -7303,9 +7303,9 @@ } }, "node_modules/typedoc": { - "version": "0.23.2", - "resolved": "https://registry.npmjs.org/typedoc/-/typedoc-0.23.2.tgz", - "integrity": "sha512-THpC4vtb3wu1yck6YHzEc4ck6W4lScf8TD0Rg7XAetDih8BzP+ErYO0/6DtdzYcZyKkDwEoujkMeWW7CffCbrQ==", + "version": "0.23.5", + "resolved": "https://registry.npmjs.org/typedoc/-/typedoc-0.23.5.tgz", + "integrity": "sha512-5ydWUOe4E9Z3a/r33cC5X5CJPLnFDKIondHYtdnEnO0sa/s8f+Nrfe+LBGOk/UTkV2IPYyL1Gm1PtUKIihklyw==", "dev": true, "dependencies": { "lunr": "^2.3.9", @@ -7544,9 +7544,9 @@ "dev": true }, "node_modules/vm2": { - "version": "3.9.9", - "resolved": "https://registry.npmjs.org/vm2/-/vm2-3.9.9.tgz", - "integrity": "sha512-xwTm7NLh/uOjARRBs8/95H0e8fT3Ukw5D/JJWhxMbhKzNh1Nu981jQKvkep9iKYNxzlVrdzD0mlBGkDKZWprlw==", + "version": "3.9.10", + "resolved": "https://registry.npmjs.org/vm2/-/vm2-3.9.10.tgz", + "integrity": "sha512-AuECTSvwu2OHLAZYhG716YzwodKCIJxB6u1zG7PgSQwIgAlEaoXH52bxdcvT8GkGjnYK7r7yWDW0m0sOsPuBjQ==", "dev": true, "dependencies": { "acorn": "^8.7.0", @@ -8002,12 +8002,12 @@ "devDependencies": { "@istanbuljs/nyc-config-typescript": "^1.0.2", "@redis/test-utils": "*", - "@types/node": "^18.0.0", + "@types/node": "^18.0.1", "nyc": "^15.1.0", "release-it": "^15.1.1", "source-map-support": "^0.5.21", - "ts-node": "^10.8.1", - "typedoc": "^0.23.2", + "ts-node": "^10.8.2", + "typedoc": "^0.23.5", "typescript": "^4.7.4" }, "peerDependencies": { @@ -8026,18 +8026,18 @@ "devDependencies": { "@istanbuljs/nyc-config-typescript": "^1.0.2", "@redis/test-utils": "*", - "@types/node": "^18.0.0", + "@types/node": "^18.0.1", "@types/sinon": "^10.0.12", "@types/yallist": "^4.0.1", - "@typescript-eslint/eslint-plugin": "^5.30.0", - "@typescript-eslint/parser": "^5.30.0", - "eslint": "^8.18.0", + "@typescript-eslint/eslint-plugin": "^5.30.5", + "@typescript-eslint/parser": "^5.30.5", + "eslint": "^8.19.0", "nyc": "^15.1.0", "release-it": "^15.1.1", "sinon": "^14.0.0", "source-map-support": "^0.5.21", - "ts-node": "^10.8.1", - "typedoc": "^0.23.2", + "ts-node": "^10.8.2", + "typedoc": "^0.23.5", "typescript": "^4.7.4" }, "engines": { @@ -8056,12 +8056,12 @@ "devDependencies": { "@istanbuljs/nyc-config-typescript": "^1.0.2", "@redis/test-utils": "*", - "@types/node": "^18.0.0", + "@types/node": "^18.0.1", "nyc": "^15.1.0", "release-it": "^15.1.1", "source-map-support": "^0.5.21", - "ts-node": "^10.8.1", - "typedoc": "^0.23.2", + "ts-node": "^10.8.2", + "typedoc": "^0.23.5", "typescript": "^4.7.4" }, "peerDependencies": { @@ -8075,12 +8075,12 @@ "devDependencies": { "@istanbuljs/nyc-config-typescript": "^1.0.2", "@redis/test-utils": "*", - "@types/node": "^18.0.0", + "@types/node": "^18.0.1", "nyc": "^15.1.0", "release-it": "^15.1.1", "source-map-support": "^0.5.21", - "ts-node": "^10.8.1", - "typedoc": "^0.23.2", + "ts-node": "^10.8.2", + "typedoc": "^0.23.5", "typescript": "^4.7.4" }, "peerDependencies": { @@ -8094,12 +8094,12 @@ "devDependencies": { "@istanbuljs/nyc-config-typescript": "^1.0.2", "@redis/test-utils": "*", - "@types/node": "^18.0.0", + "@types/node": "^18.0.1", "nyc": "^15.1.0", "release-it": "^15.1.1", "source-map-support": "^0.5.21", - "ts-node": "^10.8.1", - "typedoc": "^0.23.2", + "ts-node": "^10.8.2", + "typedoc": "^0.23.5", "typescript": "^4.7.4" }, "peerDependencies": { @@ -8111,12 +8111,12 @@ "devDependencies": { "@istanbuljs/nyc-config-typescript": "^1.0.2", "@types/mocha": "^9.1.1", - "@types/node": "^18.0.0", + "@types/node": "^18.0.1", "@types/yargs": "^17.0.10", "mocha": "^10.0.0", "nyc": "^15.1.0", "source-map-support": "^0.5.21", - "ts-node": "^10.8.1", + "ts-node": "^10.8.2", "typescript": "^4.7.4", "yargs": "^17.5.1" }, @@ -8242,12 +8242,12 @@ "devDependencies": { "@istanbuljs/nyc-config-typescript": "^1.0.2", "@redis/test-utils": "*", - "@types/node": "^18.0.0", + "@types/node": "^18.0.1", "nyc": "^15.1.0", "release-it": "^15.1.1", "source-map-support": "^0.5.21", - "ts-node": "^10.8.1", - "typedoc": "^0.23.2", + "ts-node": "^10.8.2", + "typedoc": "^0.23.5", "typescript": "^4.7.4" }, "peerDependencies": { @@ -8780,18 +8780,18 @@ } }, "@octokit/openapi-types": { - "version": "12.5.0", - "resolved": "https://registry.npmjs.org/@octokit/openapi-types/-/openapi-types-12.5.0.tgz", - "integrity": "sha512-VatvE5wtRkJq6hAWGTBZ62WkrdlCiy0G0u27cVOYTfAWVZi7QqTurVcjpsyc5+9hXLPRP5O/DaNEs4TgAp4Mqg==", + "version": "12.7.0", + "resolved": "https://registry.npmjs.org/@octokit/openapi-types/-/openapi-types-12.7.0.tgz", + "integrity": "sha512-vWXEPETgvltt9jEYdNtQTM8xnsQ7loEbBaLV26V7Tx8ovoN8P7R3XvhFeWiboqNhlXuBsIg1QI979WElB5mGXw==", "dev": true }, "@octokit/plugin-paginate-rest": { - "version": "2.21.0", - "resolved": "https://registry.npmjs.org/@octokit/plugin-paginate-rest/-/plugin-paginate-rest-2.21.0.tgz", - "integrity": "sha512-MoGEKjvDpTOCVb5gbeiW7kZm/cRfT256UJwHEuy+y+gTUuKziyXaiOkt5rM/4nzhp8UxVgvok9Tu7dMMpUybiQ==", + "version": "2.21.1", + "resolved": "https://registry.npmjs.org/@octokit/plugin-paginate-rest/-/plugin-paginate-rest-2.21.1.tgz", + "integrity": "sha512-NVNTK63yoTFp07GqISWK+uDfGH1CAPhQXS7LzsJBvaK5W+UlvG549pLZC55FK0FqANVl6q/9ra3SR5c97xF/sw==", "dev": true, "requires": { - "@octokit/types": "^6.38.1" + "@octokit/types": "^6.38.2" } }, "@octokit/plugin-request-log": { @@ -8802,12 +8802,12 @@ "requires": {} }, "@octokit/plugin-rest-endpoint-methods": { - "version": "5.16.0", - "resolved": "https://registry.npmjs.org/@octokit/plugin-rest-endpoint-methods/-/plugin-rest-endpoint-methods-5.16.0.tgz", - "integrity": "sha512-mvdwq+LvhR2GRDY82FgSZ52xX6wkOCpjiI3amiKbzKHd9nyKeFdXLsIQ3Go12tWRtvo+HwqoypLHDjRrgMFDQA==", + "version": "5.16.1", + "resolved": "https://registry.npmjs.org/@octokit/plugin-rest-endpoint-methods/-/plugin-rest-endpoint-methods-5.16.1.tgz", + "integrity": "sha512-RMHD3aJZvOpjR2fGzD2an6eU7LG8MsknhUHvP+wRUnKdbt7eDdhTMLQsZ4xsHZcLNsxPO/K4DDIZPhI2s571Ag==", "dev": true, "requires": { - "@octokit/types": "^6.38.0", + "@octokit/types": "^6.38.2", "deprecation": "^2.3.1" } }, @@ -8849,12 +8849,12 @@ } }, "@octokit/types": { - "version": "6.38.1", - "resolved": "https://registry.npmjs.org/@octokit/types/-/types-6.38.1.tgz", - "integrity": "sha512-kWMohLCIvnwApRmxRFDOqve7puiNNdtVfgwdDOm6QyJNorWOgKv2/AodCcGqx63o28kF7Dr4/nJCatrwwqhULg==", + "version": "6.39.0", + "resolved": "https://registry.npmjs.org/@octokit/types/-/types-6.39.0.tgz", + "integrity": "sha512-Mq4N9sOAYCitTsBtDdRVrBE80lIrMBhL9Jbrw0d+j96BAzlq4V+GLHFJbHokEsVvO/9tQupQdoFdgVYhD2C8UQ==", "dev": true, "requires": { - "@octokit/openapi-types": "^12.5.0" + "@octokit/openapi-types": "^12.7.0" } }, "@pnpm/network.ca-file": { @@ -8881,12 +8881,12 @@ "requires": { "@istanbuljs/nyc-config-typescript": "^1.0.2", "@redis/test-utils": "*", - "@types/node": "^18.0.0", + "@types/node": "^18.0.1", "nyc": "^15.1.0", "release-it": "^15.1.1", "source-map-support": "^0.5.21", - "ts-node": "^10.8.1", - "typedoc": "^0.23.2", + "ts-node": "^10.8.2", + "typedoc": "^0.23.5", "typescript": "^4.7.4" } }, @@ -8895,20 +8895,20 @@ "requires": { "@istanbuljs/nyc-config-typescript": "^1.0.2", "@redis/test-utils": "*", - "@types/node": "^18.0.0", + "@types/node": "^18.0.1", "@types/sinon": "^10.0.12", "@types/yallist": "^4.0.1", - "@typescript-eslint/eslint-plugin": "^5.30.0", - "@typescript-eslint/parser": "^5.30.0", + "@typescript-eslint/eslint-plugin": "^5.30.5", + "@typescript-eslint/parser": "^5.30.5", "cluster-key-slot": "1.1.0", - "eslint": "^8.18.0", + "eslint": "^8.19.0", "generic-pool": "3.8.2", "nyc": "^15.1.0", "release-it": "^15.1.1", "sinon": "^14.0.0", "source-map-support": "^0.5.21", - "ts-node": "^10.8.1", - "typedoc": "^0.23.2", + "ts-node": "^10.8.2", + "typedoc": "^0.23.5", "typescript": "^4.7.4", "yallist": "4.0.0" }, @@ -8925,12 +8925,12 @@ "requires": { "@istanbuljs/nyc-config-typescript": "^1.0.2", "@redis/test-utils": "*", - "@types/node": "^18.0.0", + "@types/node": "^18.0.1", "nyc": "^15.1.0", "release-it": "^15.1.1", "source-map-support": "^0.5.21", - "ts-node": "^10.8.1", - "typedoc": "^0.23.2", + "ts-node": "^10.8.2", + "typedoc": "^0.23.5", "typescript": "^4.7.4" } }, @@ -8939,12 +8939,12 @@ "requires": { "@istanbuljs/nyc-config-typescript": "^1.0.2", "@redis/test-utils": "*", - "@types/node": "^18.0.0", + "@types/node": "^18.0.1", "nyc": "^15.1.0", "release-it": "^15.1.1", "source-map-support": "^0.5.21", - "ts-node": "^10.8.1", - "typedoc": "^0.23.2", + "ts-node": "^10.8.2", + "typedoc": "^0.23.5", "typescript": "^4.7.4" } }, @@ -8953,12 +8953,12 @@ "requires": { "@istanbuljs/nyc-config-typescript": "^1.0.2", "@redis/test-utils": "*", - "@types/node": "^18.0.0", + "@types/node": "^18.0.1", "nyc": "^15.1.0", "release-it": "^15.1.1", "source-map-support": "^0.5.21", - "ts-node": "^10.8.1", - "typedoc": "^0.23.2", + "ts-node": "^10.8.2", + "typedoc": "^0.23.5", "typescript": "^4.7.4" } }, @@ -8967,12 +8967,12 @@ "requires": { "@istanbuljs/nyc-config-typescript": "^1.0.2", "@types/mocha": "^9.1.1", - "@types/node": "^18.0.0", + "@types/node": "^18.0.1", "@types/yargs": "^17.0.10", "mocha": "^10.0.0", "nyc": "^15.1.0", "source-map-support": "^0.5.21", - "ts-node": "^10.8.1", + "ts-node": "^10.8.2", "typescript": "^4.7.4", "yargs": "^17.5.1" }, @@ -9068,12 +9068,12 @@ "requires": { "@istanbuljs/nyc-config-typescript": "^1.0.2", "@redis/test-utils": "*", - "@types/node": "^18.0.0", + "@types/node": "^18.0.1", "nyc": "^15.1.0", "release-it": "^15.1.1", "source-map-support": "^0.5.21", - "ts-node": "^10.8.1", - "typedoc": "^0.23.2", + "ts-node": "^10.8.2", + "typedoc": "^0.23.5", "typescript": "^4.7.4" } }, @@ -9203,9 +9203,9 @@ "dev": true }, "@types/node": { - "version": "18.0.0", - "resolved": "https://registry.npmjs.org/@types/node/-/node-18.0.0.tgz", - "integrity": "sha512-cHlGmko4gWLVI27cGJntjs/Sj8th9aYwplmZFwmmgYQQvL5NUsgVJG7OddLvNfLqYS31KFN0s3qlaD9qCaxACA==", + "version": "18.0.1", + "resolved": "https://registry.npmjs.org/@types/node/-/node-18.0.1.tgz", + "integrity": "sha512-CmR8+Tsy95hhwtZBKJBs0/FFq4XX7sDZHlGGf+0q+BRZfMbOTkzkj0AFAuTyXbObDIoanaBBW0+KEW+m3N16Wg==", "dev": true }, "@types/parse-json": { @@ -9260,14 +9260,14 @@ "dev": true }, "@typescript-eslint/eslint-plugin": { - "version": "5.30.0", - "resolved": "https://registry.npmjs.org/@typescript-eslint/eslint-plugin/-/eslint-plugin-5.30.0.tgz", - "integrity": "sha512-lvhRJ2pGe2V9MEU46ELTdiHgiAFZPKtLhiU5wlnaYpMc2+c1R8fh8i80ZAa665drvjHKUJyRRGg3gEm1If54ow==", + "version": "5.30.5", + "resolved": "https://registry.npmjs.org/@typescript-eslint/eslint-plugin/-/eslint-plugin-5.30.5.tgz", + "integrity": "sha512-lftkqRoBvc28VFXEoRgyZuztyVUQ04JvUnATSPtIRFAccbXTWL6DEtXGYMcbg998kXw1NLUJm7rTQ9eUt+q6Ig==", "dev": true, "requires": { - "@typescript-eslint/scope-manager": "5.30.0", - "@typescript-eslint/type-utils": "5.30.0", - "@typescript-eslint/utils": "5.30.0", + "@typescript-eslint/scope-manager": "5.30.5", + "@typescript-eslint/type-utils": "5.30.5", + "@typescript-eslint/utils": "5.30.5", "debug": "^4.3.4", "functional-red-black-tree": "^1.0.1", "ignore": "^5.2.0", @@ -9303,52 +9303,52 @@ } }, "@typescript-eslint/parser": { - "version": "5.30.0", - "resolved": "https://registry.npmjs.org/@typescript-eslint/parser/-/parser-5.30.0.tgz", - "integrity": "sha512-2oYYUws5o2liX6SrFQ5RB88+PuRymaM2EU02/9Ppoyu70vllPnHVO7ioxDdq/ypXHA277R04SVjxvwI8HmZpzA==", + "version": "5.30.5", + "resolved": "https://registry.npmjs.org/@typescript-eslint/parser/-/parser-5.30.5.tgz", + "integrity": "sha512-zj251pcPXI8GO9NDKWWmygP6+UjwWmrdf9qMW/L/uQJBM/0XbU2inxe5io/234y/RCvwpKEYjZ6c1YrXERkK4Q==", "dev": true, "requires": { - "@typescript-eslint/scope-manager": "5.30.0", - "@typescript-eslint/types": "5.30.0", - "@typescript-eslint/typescript-estree": "5.30.0", + "@typescript-eslint/scope-manager": "5.30.5", + "@typescript-eslint/types": "5.30.5", + "@typescript-eslint/typescript-estree": "5.30.5", "debug": "^4.3.4" } }, "@typescript-eslint/scope-manager": { - "version": "5.30.0", - "resolved": "https://registry.npmjs.org/@typescript-eslint/scope-manager/-/scope-manager-5.30.0.tgz", - "integrity": "sha512-3TZxvlQcK5fhTBw5solQucWSJvonXf5yua5nx8OqK94hxdrT7/6W3/CS42MLd/f1BmlmmbGEgQcTHHCktUX5bQ==", + "version": "5.30.5", + "resolved": "https://registry.npmjs.org/@typescript-eslint/scope-manager/-/scope-manager-5.30.5.tgz", + "integrity": "sha512-NJ6F+YHHFT/30isRe2UTmIGGAiXKckCyMnIV58cE3JkHmaD6e5zyEYm5hBDv0Wbin+IC0T1FWJpD3YqHUG/Ydg==", "dev": true, "requires": { - "@typescript-eslint/types": "5.30.0", - "@typescript-eslint/visitor-keys": "5.30.0" + "@typescript-eslint/types": "5.30.5", + "@typescript-eslint/visitor-keys": "5.30.5" } }, "@typescript-eslint/type-utils": { - "version": "5.30.0", - "resolved": "https://registry.npmjs.org/@typescript-eslint/type-utils/-/type-utils-5.30.0.tgz", - "integrity": "sha512-GF8JZbZqSS+azehzlv/lmQQ3EU3VfWYzCczdZjJRxSEeXDQkqFhCBgFhallLDbPwQOEQ4MHpiPfkjKk7zlmeNg==", + "version": "5.30.5", + "resolved": "https://registry.npmjs.org/@typescript-eslint/type-utils/-/type-utils-5.30.5.tgz", + "integrity": "sha512-k9+ejlv1GgwN1nN7XjVtyCgE0BTzhzT1YsQF0rv4Vfj2U9xnslBgMYYvcEYAFVdvhuEscELJsB7lDkN7WusErw==", "dev": true, "requires": { - "@typescript-eslint/utils": "5.30.0", + "@typescript-eslint/utils": "5.30.5", "debug": "^4.3.4", "tsutils": "^3.21.0" } }, "@typescript-eslint/types": { - "version": "5.30.0", - "resolved": "https://registry.npmjs.org/@typescript-eslint/types/-/types-5.30.0.tgz", - "integrity": "sha512-vfqcBrsRNWw/LBXyncMF/KrUTYYzzygCSsVqlZ1qGu1QtGs6vMkt3US0VNSQ05grXi5Yadp3qv5XZdYLjpp8ag==", + "version": "5.30.5", + "resolved": "https://registry.npmjs.org/@typescript-eslint/types/-/types-5.30.5.tgz", + "integrity": "sha512-kZ80w/M2AvsbRvOr3PjaNh6qEW1LFqs2pLdo2s5R38B2HYXG8Z0PP48/4+j1QHJFL3ssHIbJ4odPRS8PlHrFfw==", "dev": true }, "@typescript-eslint/typescript-estree": { - "version": "5.30.0", - "resolved": "https://registry.npmjs.org/@typescript-eslint/typescript-estree/-/typescript-estree-5.30.0.tgz", - "integrity": "sha512-hDEawogreZB4n1zoqcrrtg/wPyyiCxmhPLpZ6kmWfKF5M5G0clRLaEexpuWr31fZ42F96SlD/5xCt1bT5Qm4Nw==", + "version": "5.30.5", + "resolved": "https://registry.npmjs.org/@typescript-eslint/typescript-estree/-/typescript-estree-5.30.5.tgz", + "integrity": "sha512-qGTc7QZC801kbYjAr4AgdOfnokpwStqyhSbiQvqGBLixniAKyH+ib2qXIVo4P9NgGzwyfD9I0nlJN7D91E1VpQ==", "dev": true, "requires": { - "@typescript-eslint/types": "5.30.0", - "@typescript-eslint/visitor-keys": "5.30.0", + "@typescript-eslint/types": "5.30.5", + "@typescript-eslint/visitor-keys": "5.30.5", "debug": "^4.3.4", "globby": "^11.1.0", "is-glob": "^4.0.3", @@ -9403,26 +9403,26 @@ } }, "@typescript-eslint/utils": { - "version": "5.30.0", - "resolved": "https://registry.npmjs.org/@typescript-eslint/utils/-/utils-5.30.0.tgz", - "integrity": "sha512-0bIgOgZflLKIcZsWvfklsaQTM3ZUbmtH0rJ1hKyV3raoUYyeZwcjQ8ZUJTzS7KnhNcsVT1Rxs7zeeMHEhGlltw==", + "version": "5.30.5", + "resolved": "https://registry.npmjs.org/@typescript-eslint/utils/-/utils-5.30.5.tgz", + "integrity": "sha512-o4SSUH9IkuA7AYIfAvatldovurqTAHrfzPApOZvdUq01hHojZojCFXx06D/aFpKCgWbMPRdJBWAC3sWp3itwTA==", "dev": true, "requires": { "@types/json-schema": "^7.0.9", - "@typescript-eslint/scope-manager": "5.30.0", - "@typescript-eslint/types": "5.30.0", - "@typescript-eslint/typescript-estree": "5.30.0", + "@typescript-eslint/scope-manager": "5.30.5", + "@typescript-eslint/types": "5.30.5", + "@typescript-eslint/typescript-estree": "5.30.5", "eslint-scope": "^5.1.1", "eslint-utils": "^3.0.0" } }, "@typescript-eslint/visitor-keys": { - "version": "5.30.0", - "resolved": "https://registry.npmjs.org/@typescript-eslint/visitor-keys/-/visitor-keys-5.30.0.tgz", - "integrity": "sha512-6WcIeRk2DQ3pHKxU1Ni0qMXJkjO/zLjBymlYBy/53qxe7yjEFSvzKLDToJjURUhSl2Fzhkl4SMXQoETauF74cw==", + "version": "5.30.5", + "resolved": "https://registry.npmjs.org/@typescript-eslint/visitor-keys/-/visitor-keys-5.30.5.tgz", + "integrity": "sha512-D+xtGo9HUMELzWIUqcQc0p2PO4NyvTrgIOK/VnSH083+8sq0tiLozNRKuLarwHYGRuA6TVBQSuuLwJUDWd3aaA==", "dev": true, "requires": { - "@typescript-eslint/types": "5.30.0", + "@typescript-eslint/types": "5.30.5", "eslint-visitor-keys": "^3.3.0" } }, @@ -9708,9 +9708,9 @@ "dev": true }, "type-fest": { - "version": "2.15.0", - "resolved": "https://registry.npmjs.org/type-fest/-/type-fest-2.15.0.tgz", - "integrity": "sha512-hpsXfQZrAiusX8KMY5HXSEV7xqGAGxFQoNDT+iW0yJE/bdYG0uMlRaUG0kNAUbF5p6Cq5Kuf69lm4M569QtRGw==", + "version": "2.16.0", + "resolved": "https://registry.npmjs.org/type-fest/-/type-fest-2.16.0.tgz", + "integrity": "sha512-qpaThT2HQkFb83gMOrdKVsfCN7LKxP26Yq+smPzY1FqoHRjqmjqHXA7n5Gkxi8efirtbeEUxzfEdePthQWCuHw==", "dev": true } } @@ -9847,9 +9847,9 @@ "dev": true }, "caniuse-lite": { - "version": "1.0.30001361", - "resolved": "https://registry.npmjs.org/caniuse-lite/-/caniuse-lite-1.0.30001361.tgz", - "integrity": "sha512-ybhCrjNtkFji1/Wto6SSJKkWk6kZgVQsDq5QI83SafsF6FXv2JB4df9eEdH6g8sdGgqTXrFLjAxqBGgYoU3azQ==", + "version": "1.0.30001363", + "resolved": "https://registry.npmjs.org/caniuse-lite/-/caniuse-lite-1.0.30001363.tgz", + "integrity": "sha512-HpQhpzTGGPVMnCjIomjt+jvyUu8vNFo3TaDiZ/RcoTrlOq/5+tC8zHdsbgFB6MxmaY+jCpsH09aD80Bb4Ow3Sg==", "dev": true }, "chalk": { @@ -10318,9 +10318,9 @@ "dev": true }, "electron-to-chromium": { - "version": "1.4.174", - "resolved": "https://registry.npmjs.org/electron-to-chromium/-/electron-to-chromium-1.4.174.tgz", - "integrity": "sha512-JER+w+9MV2MBVFOXxP036bLlNOnzbYAWrWU8sNUwoOO69T3w4564WhM5H5atd8VVS8U4vpi0i0kdoYzm1NPQgQ==", + "version": "1.4.179", + "resolved": "https://registry.npmjs.org/electron-to-chromium/-/electron-to-chromium-1.4.179.tgz", + "integrity": "sha512-1XeTb/U/8Xgh2YgPOqhakLYsvCcU4U7jUjTMbEnhIJoIWd/Qt3yC8y0cbG+fHzn4zUNF99Ey1xiPf20bwgLO3Q==", "dev": true }, "email-addresses": { @@ -10496,9 +10496,9 @@ } }, "eslint": { - "version": "8.18.0", - "resolved": "https://registry.npmjs.org/eslint/-/eslint-8.18.0.tgz", - "integrity": "sha512-As1EfFMVk7Xc6/CvhssHUjsAQSkpfXvUGMFC3ce8JDe6WvqCgRrLOBQbVpsBFr1X1V+RACOadnzVvcUS5ni2bA==", + "version": "8.19.0", + "resolved": "https://registry.npmjs.org/eslint/-/eslint-8.19.0.tgz", + "integrity": "sha512-SXOPj3x9VKvPe81TjjUJCYlV4oJjQw68Uek+AM0X4p+33dj2HY5bpTZOgnQHcG2eAm1mtCU9uNMnJi7exU/kYw==", "dev": true, "requires": { "@eslint/eslintrc": "^1.3.0", @@ -11125,9 +11125,9 @@ } }, "globals": { - "version": "13.15.0", - "resolved": "https://registry.npmjs.org/globals/-/globals-13.15.0.tgz", - "integrity": "sha512-bpzcOlgDhMG070Av0Vy5Owklpv1I6+j96GhUI7Rh7IzDCKLzboflLrrfqMu8NquDbiR4EOQk7XzJwqVJxicxog==", + "version": "13.16.0", + "resolved": "https://registry.npmjs.org/globals/-/globals-13.16.0.tgz", + "integrity": "sha512-A1lrQfpNF+McdPOnnFqY3kSN0AFTy485bTi1bkLk4mVPODIUEcSfhHgRqA+QdXPksrSTTztYXx37NFV+GpGk3Q==", "dev": true, "requires": { "type-fest": "^0.20.2" @@ -12383,9 +12383,9 @@ }, "dependencies": { "type-fest": { - "version": "2.15.0", - "resolved": "https://registry.npmjs.org/type-fest/-/type-fest-2.15.0.tgz", - "integrity": "sha512-hpsXfQZrAiusX8KMY5HXSEV7xqGAGxFQoNDT+iW0yJE/bdYG0uMlRaUG0kNAUbF5p6Cq5Kuf69lm4M569QtRGw==", + "version": "2.16.0", + "resolved": "https://registry.npmjs.org/type-fest/-/type-fest-2.16.0.tgz", + "integrity": "sha512-qpaThT2HQkFb83gMOrdKVsfCN7LKxP26Yq+smPzY1FqoHRjqmjqHXA7n5Gkxi8efirtbeEUxzfEdePthQWCuHw==", "dev": true } } @@ -13731,9 +13731,9 @@ } }, "ts-node": { - "version": "10.8.1", - "resolved": "https://registry.npmjs.org/ts-node/-/ts-node-10.8.1.tgz", - "integrity": "sha512-Wwsnao4DQoJsN034wePSg5nZiw4YKXf56mPIAeD6wVmiv+RytNSWqc2f3fKvcUoV+Yn2+yocD71VOfQHbmVX4g==", + "version": "10.8.2", + "resolved": "https://registry.npmjs.org/ts-node/-/ts-node-10.8.2.tgz", + "integrity": "sha512-LYdGnoGddf1D6v8REPtIH+5iq/gTDuZqv2/UJUU7tKjuEU8xVZorBM+buCGNjj+pGEud+sOoM4CX3/YzINpENA==", "dev": true, "requires": { "@cspotcode/source-map-support": "^0.8.0", @@ -13813,9 +13813,9 @@ } }, "typedoc": { - "version": "0.23.2", - "resolved": "https://registry.npmjs.org/typedoc/-/typedoc-0.23.2.tgz", - "integrity": "sha512-THpC4vtb3wu1yck6YHzEc4ck6W4lScf8TD0Rg7XAetDih8BzP+ErYO0/6DtdzYcZyKkDwEoujkMeWW7CffCbrQ==", + "version": "0.23.5", + "resolved": "https://registry.npmjs.org/typedoc/-/typedoc-0.23.5.tgz", + "integrity": "sha512-5ydWUOe4E9Z3a/r33cC5X5CJPLnFDKIondHYtdnEnO0sa/s8f+Nrfe+LBGOk/UTkV2IPYyL1Gm1PtUKIihklyw==", "dev": true, "requires": { "lunr": "^2.3.9", @@ -13987,9 +13987,9 @@ "dev": true }, "vm2": { - "version": "3.9.9", - "resolved": "https://registry.npmjs.org/vm2/-/vm2-3.9.9.tgz", - "integrity": "sha512-xwTm7NLh/uOjARRBs8/95H0e8fT3Ukw5D/JJWhxMbhKzNh1Nu981jQKvkep9iKYNxzlVrdzD0mlBGkDKZWprlw==", + "version": "3.9.10", + "resolved": "https://registry.npmjs.org/vm2/-/vm2-3.9.10.tgz", + "integrity": "sha512-AuECTSvwu2OHLAZYhG716YzwodKCIJxB6u1zG7PgSQwIgAlEaoXH52bxdcvT8GkGjnYK7r7yWDW0m0sOsPuBjQ==", "dev": true, "requires": { "acorn": "^8.7.0", diff --git a/packages/bloom/package.json b/packages/bloom/package.json index 7b2a7f5c68e..5325f3f039a 100644 --- a/packages/bloom/package.json +++ b/packages/bloom/package.json @@ -18,12 +18,12 @@ "devDependencies": { "@istanbuljs/nyc-config-typescript": "^1.0.2", "@redis/test-utils": "*", - "@types/node": "^18.0.0", + "@types/node": "^18.0.1", "nyc": "^15.1.0", "release-it": "^15.1.1", "source-map-support": "^0.5.21", - "ts-node": "^10.8.1", - "typedoc": "^0.23.2", + "ts-node": "^10.8.2", + "typedoc": "^0.23.5", "typescript": "^4.7.4" } } diff --git a/packages/client/package.json b/packages/client/package.json index a5d2ff7b3d5..b1ebb4aa8f1 100644 --- a/packages/client/package.json +++ b/packages/client/package.json @@ -21,18 +21,18 @@ "devDependencies": { "@istanbuljs/nyc-config-typescript": "^1.0.2", "@redis/test-utils": "*", - "@types/node": "^18.0.0", + "@types/node": "^18.0.1", "@types/sinon": "^10.0.12", "@types/yallist": "^4.0.1", - "@typescript-eslint/eslint-plugin": "^5.30.0", - "@typescript-eslint/parser": "^5.30.0", - "eslint": "^8.18.0", + "@typescript-eslint/eslint-plugin": "^5.30.5", + "@typescript-eslint/parser": "^5.30.5", + "eslint": "^8.19.0", "nyc": "^15.1.0", "release-it": "^15.1.1", "sinon": "^14.0.0", "source-map-support": "^0.5.21", - "ts-node": "^10.8.1", - "typedoc": "^0.23.2", + "ts-node": "^10.8.2", + "typedoc": "^0.23.5", "typescript": "^4.7.4" }, "engines": { diff --git a/packages/graph/package.json b/packages/graph/package.json index 9a256fe3732..cdcd9cf100f 100644 --- a/packages/graph/package.json +++ b/packages/graph/package.json @@ -18,12 +18,12 @@ "devDependencies": { "@istanbuljs/nyc-config-typescript": "^1.0.2", "@redis/test-utils": "*", - "@types/node": "^18.0.0", + "@types/node": "^18.0.1", "nyc": "^15.1.0", "release-it": "^15.1.1", "source-map-support": "^0.5.21", - "ts-node": "^10.8.1", - "typedoc": "^0.23.2", + "ts-node": "^10.8.2", + "typedoc": "^0.23.5", "typescript": "^4.7.4" } } diff --git a/packages/json/package.json b/packages/json/package.json index 86f4db507c0..a8e0dc18970 100644 --- a/packages/json/package.json +++ b/packages/json/package.json @@ -18,12 +18,12 @@ "devDependencies": { "@istanbuljs/nyc-config-typescript": "^1.0.2", "@redis/test-utils": "*", - "@types/node": "^18.0.0", + "@types/node": "^18.0.1", "nyc": "^15.1.0", "release-it": "^15.1.1", "source-map-support": "^0.5.21", - "ts-node": "^10.8.1", - "typedoc": "^0.23.2", + "ts-node": "^10.8.2", + "typedoc": "^0.23.5", "typescript": "^4.7.4" } } diff --git a/packages/search/package.json b/packages/search/package.json index 20afcdf0d92..b7fa3a00a13 100644 --- a/packages/search/package.json +++ b/packages/search/package.json @@ -18,12 +18,12 @@ "devDependencies": { "@istanbuljs/nyc-config-typescript": "^1.0.2", "@redis/test-utils": "*", - "@types/node": "^18.0.0", + "@types/node": "^18.0.1", "nyc": "^15.1.0", "release-it": "^15.1.1", "source-map-support": "^0.5.21", - "ts-node": "^10.8.1", - "typedoc": "^0.23.2", + "ts-node": "^10.8.2", + "typedoc": "^0.23.5", "typescript": "^4.7.4" } } diff --git a/packages/test-utils/package.json b/packages/test-utils/package.json index 6abd771503a..7d5797fe723 100644 --- a/packages/test-utils/package.json +++ b/packages/test-utils/package.json @@ -12,12 +12,12 @@ "devDependencies": { "@istanbuljs/nyc-config-typescript": "^1.0.2", "@types/mocha": "^9.1.1", - "@types/node": "^18.0.0", + "@types/node": "^18.0.1", "@types/yargs": "^17.0.10", "mocha": "^10.0.0", "nyc": "^15.1.0", "source-map-support": "^0.5.21", - "ts-node": "^10.8.1", + "ts-node": "^10.8.2", "typescript": "^4.7.4", "yargs": "^17.5.1" } diff --git a/packages/time-series/package.json b/packages/time-series/package.json index 6801fe3f019..3f5a70ab6ad 100644 --- a/packages/time-series/package.json +++ b/packages/time-series/package.json @@ -18,12 +18,12 @@ "devDependencies": { "@istanbuljs/nyc-config-typescript": "^1.0.2", "@redis/test-utils": "*", - "@types/node": "^18.0.0", + "@types/node": "^18.0.1", "nyc": "^15.1.0", "release-it": "^15.1.1", "source-map-support": "^0.5.21", - "ts-node": "^10.8.1", - "typedoc": "^0.23.2", + "ts-node": "^10.8.2", + "typedoc": "^0.23.5", "typescript": "^4.7.4" } } From 704cf5ad34ee0ae017c028b1777a2d041a630844 Mon Sep 17 00:00:00 2001 From: leibale Date: Tue, 5 Jul 2022 11:44:06 -0400 Subject: [PATCH 1230/1748] Release client@1.2.0 --- packages/client/package.json | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/packages/client/package.json b/packages/client/package.json index b1ebb4aa8f1..9f31eb8582b 100644 --- a/packages/client/package.json +++ b/packages/client/package.json @@ -1,6 +1,6 @@ { "name": "@redis/client", - "version": "1.1.1", + "version": "1.2.0", "license": "MIT", "main": "./dist/index.js", "types": "./dist/index.d.ts", From 5f48d1f34069543dc809544c2dd6b16679866ee4 Mon Sep 17 00:00:00 2001 From: leibale Date: Tue, 5 Jul 2022 11:45:00 -0400 Subject: [PATCH 1231/1748] upgrade deps --- package-lock.json | 4 ++-- package.json | 2 +- 2 files changed, 3 insertions(+), 3 deletions(-) diff --git a/package-lock.json b/package-lock.json index c9489520e52..2a9f47a6b86 100644 --- a/package-lock.json +++ b/package-lock.json @@ -13,7 +13,7 @@ ], "dependencies": { "@redis/bloom": "1.0.2", - "@redis/client": "1.1.1", + "@redis/client": "1.2.0", "@redis/graph": "1.0.1", "@redis/json": "1.0.3", "@redis/search": "1.0.6", @@ -8016,7 +8016,7 @@ }, "packages/client": { "name": "@redis/client", - "version": "1.1.1", + "version": "1.2.0", "license": "MIT", "dependencies": { "cluster-key-slot": "1.1.0", diff --git a/package.json b/package.json index acbcbefc9ce..0718e382b76 100644 --- a/package.json +++ b/package.json @@ -24,7 +24,7 @@ }, "dependencies": { "@redis/bloom": "1.0.2", - "@redis/client": "1.1.1", + "@redis/client": "1.2.0", "@redis/graph": "1.0.1", "@redis/json": "1.0.3", "@redis/search": "1.0.6", From d2bba1de38676926c804cf208f55d7eeae777ba3 Mon Sep 17 00:00:00 2001 From: leibale Date: Tue, 5 Jul 2022 11:46:34 -0400 Subject: [PATCH 1232/1748] Release redis@4.2.0 --- package-lock.json | 4 ++-- package.json | 2 +- 2 files changed, 3 insertions(+), 3 deletions(-) diff --git a/package-lock.json b/package-lock.json index 2a9f47a6b86..8f11ff757a9 100644 --- a/package-lock.json +++ b/package-lock.json @@ -1,12 +1,12 @@ { "name": "redis", - "version": "4.1.1", + "version": "4.2.0", "lockfileVersion": 2, "requires": true, "packages": { "": { "name": "redis", - "version": "4.1.1", + "version": "4.2.0", "license": "MIT", "workspaces": [ "./packages/*" diff --git a/package.json b/package.json index 0718e382b76..3d9bad2e9c3 100644 --- a/package.json +++ b/package.json @@ -1,7 +1,7 @@ { "name": "redis", "description": "A modern, high performance Redis client", - "version": "4.1.1", + "version": "4.2.0", "license": "MIT", "main": "./dist/index.js", "types": "./dist/index.d.ts", From f9f5e494dddef863f514ada4221ddab13c2e3a52 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=C8=98tefan=20Cenu=C8=99=C4=83?= Date: Thu, 7 Jul 2022 21:33:58 +0300 Subject: [PATCH 1233/1748] fixed MIGRATE function when key is array (#2184) * fix: 2163 push empty string as arg * fix: updated migrate test with keys array --- packages/client/lib/commands/MIGRATE.spec.ts | 2 +- packages/client/lib/commands/MIGRATE.ts | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/packages/client/lib/commands/MIGRATE.spec.ts b/packages/client/lib/commands/MIGRATE.spec.ts index eb233f22904..ca7ceb48b39 100644 --- a/packages/client/lib/commands/MIGRATE.spec.ts +++ b/packages/client/lib/commands/MIGRATE.spec.ts @@ -13,7 +13,7 @@ describe('MIGRATE', () => { it('multiple keys', () => { assert.deepEqual( transformArguments('127.0.0.1', 6379, ['1', '2'], 0, 10), - ['MIGRATE', '127.0.0.1', '6379', '""', '0', '10', 'KEYS', '1', '2'] + ['MIGRATE', '127.0.0.1', '6379', '', '0', '10', 'KEYS', '1', '2'] ); }); diff --git a/packages/client/lib/commands/MIGRATE.ts b/packages/client/lib/commands/MIGRATE.ts index d5e5977df8c..aaff3164081 100644 --- a/packages/client/lib/commands/MIGRATE.ts +++ b/packages/client/lib/commands/MIGRATE.ts @@ -19,7 +19,7 @@ export function transformArguments( isKeyArray = Array.isArray(key); if (isKeyArray) { - args.push('""'); + args.push(''); } else { args.push(key); } From 71d582368a4fb5e2d58e540c94bfa594128128aa Mon Sep 17 00:00:00 2001 From: Leibale Eidelman Date: Wed, 13 Jul 2022 07:01:20 -0400 Subject: [PATCH 1234/1748] Add support for `client.unref()` and `client.ref()` (#2188) * close #2185 - add support for client.unref() and client.ref() * allow sync tests --- packages/client/lib/client/index.spec.ts | 5 +++++ packages/client/lib/client/index.ts | 8 ++++++++ packages/client/lib/client/socket.ts | 16 ++++++++++++++++ packages/test-utils/lib/index.ts | 4 ++-- 4 files changed, 31 insertions(+), 2 deletions(-) diff --git a/packages/client/lib/client/index.spec.ts b/packages/client/lib/client/index.spec.ts index 442b10ef521..d27bcc2b78d 100644 --- a/packages/client/lib/client/index.spec.ts +++ b/packages/client/lib/client/index.spec.ts @@ -844,4 +844,9 @@ describe('Client', () => { await client.disconnect(); await client.connect(); }, GLOBAL.SERVERS.OPEN); + + testUtils.testWithClient('should be able to use ref and unref', client => { + client.unref(); + client.ref(); + }, GLOBAL.SERVERS.OPEN); }); diff --git a/packages/client/lib/client/index.ts b/packages/client/lib/client/index.ts index 9664b3645b1..2032c4fb714 100644 --- a/packages/client/lib/client/index.ts +++ b/packages/client/lib/client/index.ts @@ -682,6 +682,14 @@ export default class RedisClient< await this.#isolationPool.drain(); await this.#isolationPool.clear(); } + + ref(): void { + this.#socket.ref(); + } + + unref(): void { + this.#socket.unref(); + } } attachCommands({ diff --git a/packages/client/lib/client/socket.ts b/packages/client/lib/client/socket.ts index 224eb3fa886..5fab5c5601b 100644 --- a/packages/client/lib/client/socket.ts +++ b/packages/client/lib/client/socket.ts @@ -78,6 +78,8 @@ export default class RedisSocket extends EventEmitter { return this.#writableNeedDrain; } + #isSocketUnrefed = false; + constructor(initiator: RedisSocketInitiator, options?: RedisSocketOptions) { super(); @@ -137,6 +139,10 @@ export default class RedisSocket extends EventEmitter { socket.setTimeout(this.#options.connectTimeout, () => socket.destroy(new ConnectionTimeoutError())); } + if (this.#isSocketUnrefed) { + socket.unref(); + } + socket .setNoDelay(this.#options.noDelay) .once('error', reject) @@ -233,4 +239,14 @@ export default class RedisSocket extends EventEmitter { this.#isCorked = false; }); } + + ref(): void { + this.#isSocketUnrefed = false; + this.#socket?.ref(); + } + + unref(): void { + this.#isSocketUnrefed = true; + this.#socket?.unref(); + } } diff --git a/packages/test-utils/lib/index.ts b/packages/test-utils/lib/index.ts index ac7dad72b3b..1e814c29746 100644 --- a/packages/test-utils/lib/index.ts +++ b/packages/test-utils/lib/index.ts @@ -107,7 +107,7 @@ export default class TestUtils { S extends RedisScripts >( title: string, - fn: (client: RedisClientType) => Promise, + fn: (client: RedisClientType) => unknown, options: ClientTestOptions ): void { let dockerPromise: ReturnType; @@ -166,7 +166,7 @@ export default class TestUtils { S extends RedisScripts >( title: string, - fn: (cluster: RedisClusterType) => Promise, + fn: (cluster: RedisClusterType) => unknown, options: ClusterTestOptions ): void { let dockersPromise: ReturnType; From ac032d82a00c98b7b1efe1e1c4a49deda8f44581 Mon Sep 17 00:00:00 2001 From: Brandon Everett Date: Wed, 13 Jul 2022 07:01:35 -0400 Subject: [PATCH 1235/1748] fix: loop over arguments instead of spreading (#2160) * fix: loop over arguments instead of spreading * update to use concat * use the returned array from pushVerdictArguments (instead of assuming it'll push to the original array) * fix "Type 'RedisCommandArguments' is not assignable to type 'string[]'." * fix "Argument of type 'RedisCommandArgument | RedisCommandArguments[]' is not assignable to parameter of type 'RedisCommandArgument | RedisCommandArgument[]'" * fix "Type 'RedisCommandArguments' is not assignable to type 'string[]'" Co-authored-by: Leibale Eidelman --- packages/bloom/lib/commands/bloom/INSERT.ts | 9 ++++---- packages/client/lib/commands/PUBSUB_NUMSUB.ts | 9 ++++---- packages/client/lib/commands/XCLAIM.ts | 7 ++++--- .../lib/commands/generic-transformers.ts | 3 ++- packages/json/lib/commands/GET.ts | 7 ++++--- .../lib/commands/MGET_WITHLABELS.ts | 12 ++++------- packages/time-series/lib/commands/index.ts | 21 ++++++++----------- 7 files changed, 32 insertions(+), 36 deletions(-) diff --git a/packages/bloom/lib/commands/bloom/INSERT.ts b/packages/bloom/lib/commands/bloom/INSERT.ts index 59fe1dabbdc..f6deb7a8612 100644 --- a/packages/bloom/lib/commands/bloom/INSERT.ts +++ b/packages/bloom/lib/commands/bloom/INSERT.ts @@ -1,4 +1,5 @@ import { pushVerdictArguments } from '@redis/client/dist/lib/commands/generic-transformers'; +import { RedisCommandArgument, RedisCommandArguments } from '@redis/client/dist/lib/commands'; export const FIRST_KEY_INDEX = 1; @@ -12,9 +13,9 @@ interface InsertOptions { export function transformArguments( key: string, - items: string | Array, + items: RedisCommandArgument | Array, options?: InsertOptions -): Array { +): RedisCommandArguments { const args = ['BF.INSERT', key]; if (options?.CAPACITY) { @@ -38,9 +39,7 @@ export function transformArguments( } args.push('ITEMS'); - pushVerdictArguments(args, items); - - return args; + return pushVerdictArguments(args, items); } export { transformBooleanArrayReply as transformReply } from '@redis/client/dist/lib/commands/generic-transformers'; diff --git a/packages/client/lib/commands/PUBSUB_NUMSUB.ts b/packages/client/lib/commands/PUBSUB_NUMSUB.ts index c68b0d9a7f1..f47238f8882 100644 --- a/packages/client/lib/commands/PUBSUB_NUMSUB.ts +++ b/packages/client/lib/commands/PUBSUB_NUMSUB.ts @@ -1,13 +1,14 @@ import { pushVerdictArguments } from './generic-transformers'; +import { RedisCommandArgument, RedisCommandArguments } from '.'; export const IS_READ_ONLY = true; -export function transformArguments(channels?: Array | string): Array { +export function transformArguments( + channels?: Array | RedisCommandArgument +): RedisCommandArguments { const args = ['PUBSUB', 'NUMSUB']; - if (channels) { - pushVerdictArguments(args, channels); - } + if (channels) return pushVerdictArguments(args, channels); return args; } diff --git a/packages/client/lib/commands/XCLAIM.ts b/packages/client/lib/commands/XCLAIM.ts index c87d2547546..bc38f9b9e95 100644 --- a/packages/client/lib/commands/XCLAIM.ts +++ b/packages/client/lib/commands/XCLAIM.ts @@ -18,9 +18,10 @@ export function transformArguments( id: RedisCommandArgument | Array, options?: XClaimOptions ): RedisCommandArguments { - const args = ['XCLAIM', key, group, consumer, minIdleTime.toString()]; - - pushVerdictArguments(args, id); + const args = pushVerdictArguments( + ['XCLAIM', key, group, consumer, minIdleTime.toString()], + id + ); if (options?.IDLE) { args.push('IDLE', options.IDLE.toString()); diff --git a/packages/client/lib/commands/generic-transformers.ts b/packages/client/lib/commands/generic-transformers.ts index 728378bb27b..d3a57a9346b 100644 --- a/packages/client/lib/commands/generic-transformers.ts +++ b/packages/client/lib/commands/generic-transformers.ts @@ -428,7 +428,8 @@ export function pushEvalArguments(args: Array, options?: EvalOptions): A export function pushVerdictArguments(args: RedisCommandArguments, value: RedisCommandArgument | Array): RedisCommandArguments { if (Array.isArray(value)) { - args.push(...value); + // https://github.com/redis/node-redis/pull/2160 + args = args.concat(value); } else { args.push(value); } diff --git a/packages/json/lib/commands/GET.ts b/packages/json/lib/commands/GET.ts index 36bb9bc4e4c..21bad09568b 100644 --- a/packages/json/lib/commands/GET.ts +++ b/packages/json/lib/commands/GET.ts @@ -1,4 +1,5 @@ import { pushVerdictArguments } from '@redis/client/dist/lib/commands/generic-transformers'; +import { RedisCommandArguments } from '@redis/client/dist/lib/commands'; export const FIRST_KEY_INDEX = 1; @@ -12,11 +13,11 @@ interface GetOptions { NOESCAPE?: true; } -export function transformArguments(key: string, options?: GetOptions): Array { - const args = ['JSON.GET', key]; +export function transformArguments(key: string, options?: GetOptions): RedisCommandArguments { + let args: RedisCommandArguments = ['JSON.GET', key]; if (options?.path) { - pushVerdictArguments(args, options.path); + args = pushVerdictArguments(args, options.path); } if (options?.INDENT) { diff --git a/packages/time-series/lib/commands/MGET_WITHLABELS.ts b/packages/time-series/lib/commands/MGET_WITHLABELS.ts index cf83f4bcd16..b0875cefe0e 100644 --- a/packages/time-series/lib/commands/MGET_WITHLABELS.ts +++ b/packages/time-series/lib/commands/MGET_WITHLABELS.ts @@ -8,6 +8,7 @@ import { pushFilterArgument } from '.'; import { MGetRawReply, MGetReply } from './MGET'; +import { RedisCommandArguments } from '@redis/client/dist/lib/commands'; export const IS_READ_ONLY = true; @@ -18,14 +19,9 @@ interface MGetWithLabelsOptions { export function transformArguments( filter: Filter, options?: MGetWithLabelsOptions -): Array { - const args = ['TS.MGET']; - - pushWithLabelsArgument(args, options?.SELECTED_LABELS); - - pushFilterArgument(args, filter); - - return args; +): RedisCommandArguments { + const args = pushWithLabelsArgument(['TS.MGET'], options?.SELECTED_LABELS); + return pushFilterArgument(args, filter); } export interface MGetWithLabelsReply extends MGetReply { diff --git a/packages/time-series/lib/commands/index.ts b/packages/time-series/lib/commands/index.ts index 4cc638a4249..aba3ae2de9f 100644 --- a/packages/time-series/lib/commands/index.ts +++ b/packages/time-series/lib/commands/index.ts @@ -313,8 +313,7 @@ export type Filter = string | Array; export function pushFilterArgument(args: RedisCommandArguments, filter: string | Array): RedisCommandArguments { args.push('FILTER'); - pushVerdictArguments(args, filter); - return args; + return pushVerdictArguments(args, filter); } export interface MRangeOptions extends RangeOptions { @@ -328,10 +327,9 @@ export function pushMRangeArguments( filter: Filter, options?: MRangeOptions ): RedisCommandArguments { - pushRangeArguments(args, fromTimestamp, toTimestamp, options); - pushFilterArgument(args, filter); - pushMRangeGroupByArguments(args, options?.GROUPBY); - return args; + args = pushRangeArguments(args, fromTimestamp, toTimestamp, options); + args = pushFilterArgument(args, filter); + return pushMRangeGroupByArguments(args, options?.GROUPBY); } export type SelectedLabels = string | Array; @@ -341,7 +339,7 @@ export function pushWithLabelsArgument(args: RedisCommandArguments, selectedLabe args.push('WITHLABELS'); } else { args.push('SELECTED_LABELS'); - pushVerdictArguments(args, selectedLabels); + args = pushVerdictArguments(args, selectedLabels); } return args; @@ -358,11 +356,10 @@ export function pushMRangeWithLabelsArguments( filter: Filter, options?: MRangeWithLabelsOptions ): RedisCommandArguments { - pushRangeArguments(args, fromTimestamp, toTimestamp, options); - pushWithLabelsArgument(args, options?.SELECTED_LABELS); - pushFilterArgument(args, filter); - pushMRangeGroupByArguments(args, options?.GROUPBY); - return args; + args = pushRangeArguments(args, fromTimestamp, toTimestamp, options); + args = pushWithLabelsArgument(args, options?.SELECTED_LABELS); + args = pushFilterArgument(args, filter); + return pushMRangeGroupByArguments(args, options?.GROUPBY); } export function transformRangeReply(reply: Array): Array { From c1fd86778a71072a805cbb0cf238bc38f387eea2 Mon Sep 17 00:00:00 2001 From: Chayim Date: Thu, 14 Jul 2022 16:57:54 +0300 Subject: [PATCH 1236/1748] adding link to om client (#2180) --- README.md | 2 ++ 1 file changed, 2 insertions(+) diff --git a/README.md b/README.md index 514a74221e8..9d48bdd6b2a 100644 --- a/README.md +++ b/README.md @@ -35,6 +35,8 @@ npm install redis > :warning: The new interface is clean and cool, but if you have an existing codebase, you'll want to read the [migration guide](./docs/v3-to-v4.md). +Looking for a high-level library to handle object mapping? See [redis-om-node](https://github.com/redis/redis-om-node)! + ## Usage ### Basic Example From 60ad6aab0b4ef985303a559b3af8a37a17ed2363 Mon Sep 17 00:00:00 2001 From: Leibale Eidelman Date: Thu, 28 Jul 2022 13:29:57 -0400 Subject: [PATCH 1237/1748] close #2210 - add support for FT.CREATE WITHSUFFIXTRIE (#2212) --- packages/search/lib/commands/CREATE.spec.ts | 24 +++++++++++++++++++++ packages/search/lib/commands/index.ts | 10 +++++++++ 2 files changed, 34 insertions(+) diff --git a/packages/search/lib/commands/CREATE.spec.ts b/packages/search/lib/commands/CREATE.spec.ts index 765e3dbf7d6..1a0a4f244bd 100644 --- a/packages/search/lib/commands/CREATE.spec.ts +++ b/packages/search/lib/commands/CREATE.spec.ts @@ -58,6 +58,18 @@ describe('CREATE', () => { ['FT.CREATE', 'index', 'SCHEMA', 'field', 'TEXT', 'PHONETIC', SchemaTextFieldPhonetics.DM_EN] ); }); + + it('with WITHSUFFIXTRIE', () => { + assert.deepEqual( + transformArguments('index', { + field: { + type: SchemaFieldTypes.TEXT, + WITHSUFFIXTRIE: true + } + }), + ['FT.CREATE', 'index', 'SCHEMA', 'field', 'TEXT', 'WITHSUFFIXTRIE'] + ); + }); }); it('NUMERIC', () => { @@ -124,6 +136,18 @@ describe('CREATE', () => { ['FT.CREATE', 'index', 'SCHEMA', 'field', 'TAG', 'CASESENSITIVE'] ); }); + + it('with WITHSUFFIXTRIE', () => { + assert.deepEqual( + transformArguments('index', { + field: { + type: SchemaFieldTypes.TAG, + WITHSUFFIXTRIE: true + } + }), + ['FT.CREATE', 'index', 'SCHEMA', 'field', 'TAG', 'WITHSUFFIXTRIE'] + ); + }); }); describe('VECTOR', () => { diff --git a/packages/search/lib/commands/index.ts b/packages/search/lib/commands/index.ts index f977f39315f..34741440ba5 100644 --- a/packages/search/lib/commands/index.ts +++ b/packages/search/lib/commands/index.ts @@ -206,6 +206,7 @@ type CreateSchemaTextField = CreateSchemaCommonField; type CreateSchemaNumericField = CreateSchemaCommonField; @@ -215,6 +216,7 @@ type CreateSchemaGeoField = CreateSchemaCommonField; type CreateSchemaTagField = CreateSchemaCommonField; export enum VectorAlgorithms { @@ -282,6 +284,10 @@ export function pushSchema(args: RedisCommandArguments, schema: RediSearchSchema args.push('PHONETIC', fieldOptions.PHONETIC); } + if (fieldOptions.WITHSUFFIXTRIE) { + args.push('WITHSUFFIXTRIE'); + } + break; // case SchemaFieldTypes.NUMERIC: @@ -297,6 +303,10 @@ export function pushSchema(args: RedisCommandArguments, schema: RediSearchSchema args.push('CASESENSITIVE'); } + if (fieldOptions.WITHSUFFIXTRIE) { + args.push('WITHSUFFIXTRIE'); + } + break; case SchemaFieldTypes.VECTOR: From f3462abf33e0e502953d68e3a86f0c0c98a4a33d Mon Sep 17 00:00:00 2001 From: Simon Prickett Date: Mon, 15 Aug 2022 13:39:28 +0100 Subject: [PATCH 1238/1748] Updates examples. (#2219) * Updates examples. * Added command link for hset. --- examples/README.md | 15 +- examples/blocking-list-pop.js | 32 ++-- examples/bloom-filter.js | 127 +++++++------- examples/command-with-modifiers.js | 33 ++-- examples/connect-as-acl-user.js | 28 ++- examples/count-min-sketch.js | 129 +++++++------- examples/cuckoo-filter.js | 130 +++++++------- examples/get-server-time.js | 16 +- examples/lua-multi-incr.js | 42 ++--- examples/managing-json.js | 127 +++++++------- examples/search-hashes.js | 147 ++++++++-------- examples/search-json.js | 267 +++++++++++++++-------------- examples/set-scan.js | 16 +- examples/sorted-set.js | 48 +++--- examples/stream-consumer-group.js | 139 +++++++-------- examples/stream-consumer.js | 99 ++++++----- examples/stream-producer.js | 89 +++++----- examples/time-series.js | 218 ++++++++++++----------- examples/topk.js | 191 +++++++++++---------- 19 files changed, 923 insertions(+), 970 deletions(-) diff --git a/examples/README.md b/examples/README.md index a026bc0b1c8..089096d719b 100644 --- a/examples/README.md +++ b/examples/README.md @@ -53,7 +53,7 @@ When adding a new example, please follow these guidelines: * Use semicolons * Use `async` and `await` * Use single quotes, `'hello'` not `"hello"` -* Place your example code in a single `async` function where possible, named according to the file name e.g. `add-to-stream.js` would contain `const addtoStream = async () => { ... };`, and call this function at the end of the file e.g. `addToStream();` +* Use [template literals](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Template_literals) when embedding expressions in strings * Unless your example requires a connection string, assume Redis is on the default localhost port 6379 with no password * Use meaningful example data, let's not use `foo`, `bar`, `baz` etc! * Leave an empty line at the end of your `.js` file @@ -71,18 +71,17 @@ Here's a starter template for adding a new example, imagine this is stored in `d // Set up the data in redis-cli using these commands: // +// +// Alternatively, add code that sets up the data. import { createClient } from 'redis'; -async function doSomething() { - const client = createClient(); +const client = createClient(); - await client.connect(); +await client.connect(); - // Add your example code here... +// Add your example code here... - await client.quit(); -} +await client.quit(); -doSomething(); ``` diff --git a/examples/blocking-list-pop.js b/examples/blocking-list-pop.js index 59dde3274a3..099c73a2a96 100644 --- a/examples/blocking-list-pop.js +++ b/examples/blocking-list-pop.js @@ -6,27 +6,25 @@ import { createClient, commandOptions } from 'redis'; -async function blockingListPop() { - const client = createClient(); +const client = createClient(); - await client.connect(); +await client.connect(); - const keyName = 'keyName'; +const keyName = 'keyName'; - const blpopPromise = client.blPop( - commandOptions({ isolated: true }), - keyName, - 0 - ); +const blpopPromise = client.blPop( + commandOptions({ isolated: true }), + keyName, + 0 +); - await client.lPush(keyName, 'value'); +await client.lPush(keyName, 'value'); - await blpopPromise; +const listItem = await blpopPromise; - console.log('blpopPromise resolved'); - console.log(keyName); +console.log('blpopPromise resolved'); +// listItem will be: +// {"key":"keyName","element":"value"} +console.log(`listItem is '${JSON.stringify(listItem)}'`); - await client.quit(); -} - -blockingListPop(); +await client.quit(); diff --git a/examples/bloom-filter.js b/examples/bloom-filter.js index db55c519c33..cf5f1940b3e 100644 --- a/examples/bloom-filter.js +++ b/examples/bloom-filter.js @@ -1,79 +1,80 @@ // This example demonstrates the use of the Bloom Filter -// in the RedisBloom module (https://redisbloom.io/) +// in the RedisBloom module (https://redis.io/docs/stack/bloom/) import { createClient } from 'redis'; -async function bloomFilter() { - const client = createClient(); +const client = createClient(); - await client.connect(); +await client.connect(); - // Delete any pre-existing Bloom Filter. - await client.del('mybloom'); +// Delete any pre-existing Bloom Filter. +await client.del('mybloom'); - // Reserve a Bloom Filter with configurable error rate and capacity. - // https://oss.redis.com/redisbloom/Bloom_Commands/#bfreserve - try { - await client.bf.reserve('mybloom', 0.01, 1000); - console.log('Reserved Bloom Filter.'); - } catch (e) { - if (e.message.endsWith('item exists')) { - console.log('Bloom Filter already reserved.'); - } else { - console.log('Error, maybe RedisBloom is not installed?:'); - console.log(e); - } +// Reserve a Bloom Filter with configurable error rate and capacity. +// https://redis.io/commands/bf.reserve/ +try { + await client.bf.reserve('mybloom', 0.01, 1000); + console.log('Reserved Bloom Filter.'); +} catch (e) { + if (e.message.endsWith('item exists')) { + console.log('Bloom Filter already reserved.'); + } else { + console.log('Error, maybe RedisBloom is not installed?:'); + console.log(e); } +} - // Add items to Bloom Filter individually with BF.ADD command. - await Promise.all([ - client.bf.add('mybloom', 'leibale'), - client.bf.add('mybloom', 'simon'), - client.bf.add('mybloom', 'guy'), - client.bf.add('mybloom', 'suze'), - client.bf.add('mybloom', 'brian'), - client.bf.add('mybloom', 'steve'), - client.bf.add('mybloom', 'kyle'), - client.bf.add('mybloom', 'josefin'), - client.bf.add('mybloom', 'alex'), - client.bf.add('mybloom', 'nava'), - ]); - - // Add multiple items to Bloom Filter at once with BF.MADD command. - await client.bf.mAdd('mybloom', [ - 'kaitlyn', - 'rachel' - ]); +// Add items to Bloom Filter individually with BF.ADD command. +// https://redis.io/commands/bf.add/ +await Promise.all([ + client.bf.add('mybloom', 'leibale'), + client.bf.add('mybloom', 'simon'), + client.bf.add('mybloom', 'guy'), + client.bf.add('mybloom', 'suze'), + client.bf.add('mybloom', 'brian'), + client.bf.add('mybloom', 'steve'), + client.bf.add('mybloom', 'kyle'), + client.bf.add('mybloom', 'josefin'), + client.bf.add('mybloom', 'alex'), + client.bf.add('mybloom', 'nava'), +]); - console.log('Added members to Bloom Filter.'); +// Add multiple items to Bloom Filter at once with BF.MADD command. +// https://redis.io/commands/bf.madd/ +await client.bf.mAdd('mybloom', [ + 'kaitlyn', + 'rachel' +]); - // Check whether a member exists with the BF.EXISTS command. - const simonExists = await client.bf.exists('mybloom', 'simon'); - console.log(`simon ${simonExists ? 'may' : 'does not'} exist in the Bloom Filter.`); +console.log('Added members to Bloom Filter.'); - // Check whether multiple members exist with the BF.MEXISTS command: - const [ lanceExists, leibaleExists ] = await client.bf.mExists('mybloom', [ - 'lance', - 'leibale' - ]); +// Check whether a member exists with the BF.EXISTS command. +// https://redis.io/commands/bf.exists/ +const simonExists = await client.bf.exists('mybloom', 'simon'); +console.log(`simon ${simonExists ? 'may' : 'does not'} exist in the Bloom Filter.`); - console.log(`lance ${lanceExists ? 'may' : 'does not'} exist in the Bloom Filter.`); - console.log(`leibale ${leibaleExists ? 'may' : 'does not'} exist in the Bloom Filter.`); +// Check whether multiple members exist with the BF.MEXISTS command. +// https://redis.io/commands/bf.mexists/ +const [ lanceExists, leibaleExists ] = await client.bf.mExists('mybloom', [ + 'lance', + 'leibale' +]); - // Get stats for the Bloom Filter with the BF.INFO command: - const info = await client.bf.info('mybloom'); - // info looks like this: - // - // { - // capacity: 1000, - // size: 1531, - // numberOfFilters: 1, - // numberOfInsertedItems: 12, - // expansionRate: 2 - // } - console.log(info); +console.log(`lance ${lanceExists ? 'may' : 'does not'} exist in the Bloom Filter.`); +console.log(`leibale ${leibaleExists ? 'may' : 'does not'} exist in the Bloom Filter.`); - await client.quit(); -} +// Get stats for the Bloom Filter with the BF.INFO command. +// https://redis.io/commands/bf.info/ +const info = await client.bf.info('mybloom'); +// info looks like this: +// +// { +// capacity: 1000, +// size: 1531, +// numberOfFilters: 1, +// numberOfInsertedItems: 12, +// expansionRate: 2 +// } +console.log(info); -bloomFilter(); +await client.quit(); diff --git a/examples/command-with-modifiers.js b/examples/command-with-modifiers.js index 31940233815..974f78dc5d8 100644 --- a/examples/command-with-modifiers.js +++ b/examples/command-with-modifiers.js @@ -3,28 +3,23 @@ import { createClient } from 'redis'; -async function commandWithModifiers() { - const client = createClient(); +const client = createClient(); - await client.connect(); - await client.del('mykey'); +await client.connect(); +await client.del('mykey'); - let result = await client.set('mykey', 'myvalue', { - EX: 60, - GET: true - }); +let result = await client.set('mykey', 'myvalue', { + EX: 60, + GET: true +}); - console.log(result); //nil +console.log(result); //null - result = await client.set('mykey', 'newvalue', { - EX: 60, - GET: true - }); +result = await client.set('mykey', 'newvalue', { + EX: 60, + GET: true +}); - console.log(result); //myvalue - - await client.quit(); -} - -commandWithModifiers(); +console.log(result); //myvalue +await client.quit(); diff --git a/examples/connect-as-acl-user.js b/examples/connect-as-acl-user.js index 26e1e443b0a..df46aa1e288 100644 --- a/examples/connect-as-acl-user.js +++ b/examples/connect-as-acl-user.js @@ -7,24 +7,20 @@ import { createClient } from 'redis'; -async function connectWithACLUser() { - const client = createClient({ - url: 'redis://testuser:testpassword@127.0.0.1:6379' - }); +const client = createClient({ + url: 'redis://testuser:testpassword@127.0.0.1:6379' +}); - await client.connect(); +await client.connect(); - // Returns PONG - console.log(`Response from PING command: ${await client.ping()}`); +// Returns PONG +console.log(`Response from PING command: ${await client.ping()}`); - try { - // This will error as this user is not allowed to run this command... - console.log(`Response from GET command: ${await client.get('somekey')}`); - } catch (e) { - console.log(`GET command failed: ${e.message}`); - } - - await client.quit(); +try { + // This will error as this user is not allowed to run this command... + console.log(`Response from GET command: ${await client.get('somekey')}`); +} catch (e) { + console.log(`GET command failed: ${e.message}`); } -connectWithACLUser(); +await client.quit(); diff --git a/examples/count-min-sketch.js b/examples/count-min-sketch.js index 7deb644355d..f88a148986f 100644 --- a/examples/count-min-sketch.js +++ b/examples/count-min-sketch.js @@ -1,83 +1,80 @@ // This example demonstrates the use of the Count-Min Sketch -// in the RedisBloom module (https://redisbloom.io/) +// in the RedisBloom module (https://redis.io/docs/stack/bloom/) import { createClient } from 'redis'; -async function countMinSketch() { - const client = createClient(); +const client = createClient(); - await client.connect(); +await client.connect(); - // Delete any pre-existing Count-Min Sketch. - await client.del('mycms'); +// Delete any pre-existing Count-Min Sketch. +await client.del('mycms'); - // Initialize a Count-Min Sketch with error rate and probability: - // https://oss.redis.com/redisbloom/CountMinSketch_Commands/#cmsinitbyprob - try { - await client.cms.initByProb('mycms', 0.001, 0.01); - console.log('Initialized Count-Min Sketch.'); - } catch (e) { - console.log('Error, maybe RedisBloom is not installed?:'); - console.log(e); - } +// Initialize a Count-Min Sketch with error rate and probability: +// https://redis.io/commands/cms.initbyprob/ +try { + await client.cms.initByProb('mycms', 0.001, 0.01); + console.log('Reserved Count Min Sketch.'); +} catch (e) { + console.log('Error, maybe RedisBloom is not installed?:'); + console.log(e); +} - const teamMembers = [ - 'leibale', - 'simon', - 'guy', - 'suze', - 'brian', - 'steve', - 'kyleb', - 'kyleo', - 'josefin', - 'alex', - 'nava', - 'lance', - 'rachel', - 'kaitlyn' - ]; +const teamMembers = [ + 'leibale', + 'simon', + 'guy', + 'suze', + 'brian', + 'steve', + 'kyleb', + 'kyleo', + 'josefin', + 'alex', + 'nava', + 'lance', + 'rachel', + 'kaitlyn' +]; - // Store actual counts for comparison with CMS. - let actualCounts = {}; +// Store actual counts for comparison with CMS. +let actualCounts = {}; - // Randomly emit a team member and count them with the CMS. - for (let n = 0; n < 1000; n++) { - const teamMember = teamMembers[Math.floor(Math.random() * teamMembers.length)]; - await client.cms.incrBy('mycms', { - item: teamMember, - incrementBy: 1 - }); +// Randomly emit a team member and count them with the CMS. +// https://redis.io/commands/cms.incrby/ +for (let n = 0; n < 1000; n++) { + const teamMember = teamMembers[Math.floor(Math.random() * teamMembers.length)]; + await client.cms.incrBy('mycms', { + item: teamMember, + incrementBy: 1 + }); - actualCounts[teamMember] = actualCounts[teamMember] ? actualCounts[teamMember] + 1 : 1; + actualCounts[teamMember] = actualCounts[teamMember] ? actualCounts[teamMember] + 1 : 1; - console.log(`Incremented score for ${teamMember}.`); - } + console.log(`Incremented score for ${teamMember}.`); +} - // Get count estimate for some team members: - // https://oss.redis.com/redisbloom/CountMinSketch_Commands/#cmsquery - const [ alexCount, rachelCount ] = await client.cms.query('mycms', [ - 'simon', - 'lance' - ]); +// Get count estimate for some team members: +// https://redis.io/commands/cms.query/ +const [ alexCount, rachelCount ] = await client.cms.query('mycms', [ + 'alex', + 'rachel' +]); - console.log(`Count estimate for alex: ${alexCount} (actual ${actualCounts.alex}).`); - console.log(`Count estimate for rachel: ${rachelCount} (actual ${actualCounts.rachel}).`); +console.log(`Count estimate for alex: ${alexCount} (actual ${actualCounts.alex}).`); +console.log(`Count estimate for rachel: ${rachelCount} (actual ${actualCounts.rachel}).`); - // Get overall information about the Count-Min Sketch: - // https://oss.redis.com/redisbloom/CountMinSketch_Commands/#cmsinfo - const info = await client.cms.info('mycms'); - console.log('Count-Min Sketch info:'); +// Get overall information about the Count-Min Sketch: +// https://redis.io/commands/cms.info/ +const info = await client.cms.info('mycms'); +console.log('Count-Min Sketch info:'); - // info looks like this: - // { - // width: 2000, - // depth: 7, - // count: 1000 - // } - console.log(info); - - await client.quit(); -} +// info looks like this: +// { +// width: 2000, +// depth: 7, +// count: 1000 +// } +console.log(info); -countMinSketch(); +await client.quit(); diff --git a/examples/cuckoo-filter.js b/examples/cuckoo-filter.js index e2262a9f026..87976f3fefb 100644 --- a/examples/cuckoo-filter.js +++ b/examples/cuckoo-filter.js @@ -1,81 +1,79 @@ // This example demonstrates the use of the Cuckoo Filter -// in the RedisBloom module (https://redisbloom.io/) +// in the RedisBloom module (https://redis.io/docs/stack/bloom/) import { createClient } from 'redis'; -async function cuckooFilter() { - const client = createClient(); +const client = createClient(); - await client.connect(); +await client.connect(); - // Delete any pre-existing Cuckoo Filter. - await client.del('mycuckoo'); +// Delete any pre-existing Cuckoo Filter. +await client.del('mycuckoo'); - // Reserve a Cuckoo Filter with a capacity of 10000 items. - // https://oss.redis.com/redisbloom/Cuckoo_Commands/#cfreserve - try { - await client.cf.reserve('mycuckoo', 10000); - console.log('Reserved Cuckoo Filter.'); - } catch (e) { - console.log('Error, maybe RedisBloom is not installed?:'); - console.log(e); - } - - // Add items to Cuckoo Filter individually with CF.ADD command. - https://oss.redis.com/redisbloom/Cuckoo_Commands/#cfadd - await Promise.all([ - client.cf.add('mycuckoo', 'leibale'), - client.cf.add('mycuckoo', 'simon'), - client.cf.add('mycuckoo', 'guy'), - client.cf.add('mycuckoo', 'suze'), - client.cf.add('mycuckoo', 'brian'), - client.cf.add('mycuckoo', 'steve'), - client.cf.add('mycuckoo', 'kyle'), - client.cf.add('mycuckoo', 'josefin'), - client.cf.add('mycuckoo', 'alex'), - client.cf.add('mycuckoo', 'nava'), - ]); +// Reserve a Cuckoo Filter with a capacity of 10000 items. +// https://redis.io/commands/cf.reserve/ +try { + await client.cf.reserve('mycuckoo', 10000); + console.log('Reserved Cuckoo Filter.'); +} catch (e) { + console.log('Error, maybe RedisBloom is not installed?:'); + console.log(e); +} - // Add items to the Cuckoo Filter only if they don't exist in it... - // https://oss.redis.com/redisbloom/Cuckoo_Commands/#cfaddnx - const nxReply = await Promise.all([ - client.cf.addNX('mycuckoo', 'kaitlyn'), // New - client.cf.addNX('mycuckoo', 'rachel'), // New - client.cf.addNX('mycuckoo', 'brian') // Previously added - ]); +// Add items to Cuckoo Filter individually with CF.ADD command. +// https://redis.io/commands/cf.add/ +await Promise.all([ + client.cf.add('mycuckoo', 'leibale'), + client.cf.add('mycuckoo', 'simon'), + client.cf.add('mycuckoo', 'guy'), + client.cf.add('mycuckoo', 'suze'), + client.cf.add('mycuckoo', 'brian'), + client.cf.add('mycuckoo', 'steve'), + client.cf.add('mycuckoo', 'kyle'), + client.cf.add('mycuckoo', 'josefin'), + client.cf.add('mycuckoo', 'alex'), + client.cf.add('mycuckoo', 'nava'), +]); - console.log('Added members to Cuckoo Filter.'); - console.log('nxReply:'); +// Add items to the Cuckoo Filter only if they don't exist in it... +// https://redis.io/commands/cf.addnx/ +const nxReply = await Promise.all([ + client.cf.addNX('mycuckoo', 'kaitlyn'), // New + client.cf.addNX('mycuckoo', 'rachel'), // New + client.cf.addNX('mycuckoo', 'brian') // Previously added +]); - // nxReply looks like this: - // [ - // true, - // true, - // false - // ] - console.log(nxReply); +console.log('Added members to Cuckoo Filter.'); +console.log('nxReply:'); - // Check whether a member exists with the CF.EXISTS command. - const simonExists = await client.bf.exists('mycuckoo', 'simon'); - console.log(`simon ${simonExists ? 'may' : 'does not'} exist in the Cuckoo Filter.`); +// nxReply looks like this: +// [ +// true, +// true, +// false +// ] +console.log(nxReply); - // Get stats for the Cuckoo Filter with the CF.INFO command: - const info = await client.cf.info('mycuckoo'); +// Check whether a member exists with the CF.EXISTS command. +// https://redis.io/commands/cf.exists/ +const simonExists = await client.bf.exists('mycuckoo', 'simon'); +console.log(`simon ${simonExists ? 'may' : 'does not'} exist in the Cuckoo Filter.`); - // info looks like this: - // { - // size: 16440, - // numberOfBuckets: 8192, - // numberOfFilters: 1, - // numberOfInsertedItems: 12, - // numberOfDeletedItems: 0, - // bucketSize: 2, - // expansionRate: 1, - // maxIteration: 20 - // } - console.log(info); +// Get stats for the Cuckoo Filter with the CF.INFO command: +// https://redis.io/commands/cf.info/ +const info = await client.cf.info('mycuckoo'); - await client.quit(); -} +// info looks like this: +// { +// size: 16440, +// numberOfBuckets: 8192, +// numberOfFilters: 1, +// numberOfInsertedItems: 12, +// numberOfDeletedItems: 0, +// bucketSize: 2, +// expansionRate: 1, +// maxIteration: 20 +// } +console.log(info); -cuckooFilter(); +await client.quit(); diff --git a/examples/get-server-time.js b/examples/get-server-time.js index c6b7956c831..967859f0136 100644 --- a/examples/get-server-time.js +++ b/examples/get-server-time.js @@ -2,15 +2,11 @@ import { createClient } from 'redis'; -async function getServerTime() { - const client = createClient(); - await client.connect(); +const client = createClient(); +await client.connect(); - const serverTime = await client.time(); - // 2022-02-25T12:57:40.000Z { microseconds: 351346 } - console.log(serverTime); +const serverTime = await client.time(); +// 2022-02-25T12:57:40.000Z { microseconds: 351346 } +console.log(serverTime); - await client.quit(); -} - -getServerTime(); +await client.quit(); diff --git a/examples/lua-multi-incr.js b/examples/lua-multi-incr.js index ec433c27ea2..8eb1092c295 100644 --- a/examples/lua-multi-incr.js +++ b/examples/lua-multi-incr.js @@ -3,29 +3,25 @@ import { createClient, defineScript } from 'redis'; -async function luaMultiIncr() { - const client = createClient({ - scripts: { - mincr: defineScript({ - NUMBER_OF_KEYS: 2, - SCRIPT: - 'return {' + - 'redis.pcall("INCRBY", KEYS[1], ARGV[1]),' + - 'redis.pcall("INCRBY", KEYS[2], ARGV[1])' + - '}', - transformArguments(key1, key2, increment) { - return [key1, key2, increment.toString()]; - }, - }), - }, - }); +const client = createClient({ + scripts: { + mincr: defineScript({ + NUMBER_OF_KEYS: 2, + SCRIPT: + 'return {' + + 'redis.pcall("INCRBY", KEYS[1], ARGV[1]),' + + 'redis.pcall("INCRBY", KEYS[2], ARGV[1])' + + '}', + transformArguments(key1, key2, increment) { + return [key1, key2, increment.toString()]; + }, + }), + }, +}); - await client.connect(); +await client.connect(); - await client.set('mykey', '5'); - console.log(await client.mincr('mykey', 'myotherkey', 10)); // [ 15, 10 ] +await client.set('mykey', '5'); +console.log(await client.mincr('mykey', 'myotherkey', 10)); // [ 15, 10 ] - await client.quit(); -} - -luaMultiIncr(); +await client.quit(); diff --git a/examples/managing-json.js b/examples/managing-json.js index 44978a94f05..81949d5c222 100644 --- a/examples/managing-json.js +++ b/examples/managing-json.js @@ -2,80 +2,75 @@ import { createClient } from 'redis'; -async function managingJSON() { - const client = createClient(); +const client = createClient(); - await client.connect(); - await client.del('noderedis:jsondata'); +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' +// 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' - ] - }); +// 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); +// { '$.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 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.`); +// Goldie is 3 years old now. +console.log(`Goldie is ${JSON.stringify(results[0])} years old now.`); - // Add a new pet... - await client.json.arrAppend('noderedis:jsondata', '.pets', { - name: 'Robin', - species: 'bird', - isMammal: false, - age: 1 - }); +// 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'); +// 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(); +// We now have 4 pets. +console.log(`We now have ${numPets} pets.`); +await client.quit(); diff --git a/examples/search-hashes.js b/examples/search-hashes.js index 46d5e598cf8..85e6106a99a 100644 --- a/examples/search-hashes.js +++ b/examples/search-hashes.js @@ -3,89 +3,86 @@ import { createClient, SchemaFieldTypes } from 'redis'; -async function searchHashes() { - const client = createClient(); +const client = createClient(); - await client.connect(); +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); - } +// Create an index... +try { + // Documentation: https://redis.io/commands/ft.create/ + 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}) - ]); +// Add some sample data... +// https://redis.io/commands/hset/ +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... sort by age, descending. - // Documentation: https://oss.redis.com/redisearch/Commands/#ftsearch - // Query syntax: https://oss.redis.com/redisearch/Query_Syntax/ - const results = await client.ft.search( - 'idx:animals', - '@species:{dog}', - { - SORTBY: { - BY: 'age', - DIRECTION: 'DESC' // or 'ASC' (default if DIRECTION is not present) - } +// Perform a search query, find all the dogs... sort by age, descending. +// Documentation: https://redis.io/commands/ft.search/ +// Query syntax: https://redis.io/docs/stack/search/reference/query_syntax/ +const results = await client.ft.search( + 'idx:animals', + '@species:{dog}', + { + SORTBY: { + BY: 'age', + DIRECTION: 'DESC' // or 'ASC (default if DIRECTION is not present) } - ); + } +); - // results: - // { - // total: 2, - // documents: [ - // { - // id: 'noderedis:animals:3', - // value: { - // age: '9', - // name: 'Rover', - // species: 'dog' - // } - // }, - // { - // id: 'noderedis:animals:4', - // value: { - // age: '7', - // name: 'Fido', - // species: 'dog' - // } - // } - // ] - // } - - console.log(`Results found: ${results.total}.`); +// results: +// { +// total: 2, +// documents: [ +// { +// id: 'noderedis:animals:3', +// value: { +// name: 'Rover', +// species: 'dog', +// age: '9' +// } +// }, +// { +// id: 'noderedis:animals:4', +// value: { +// name: 'Fido', +// species: 'dog', +// age: '7' +// } +// } +// ] +// } - for (const doc of results.documents) { - // noderedis:animals:3: Rover, 9 years old. - // noderedis:animals:4: Fido, 7 years old. - console.log(`${doc.id}: ${doc.value.name}, ${doc.value.age} years old.`); - } +console.log(`Results found: ${results.total}.`); - await client.quit(); +for (const doc of results.documents) { + // noderedis:animals:3: Rover, 9 years old. + // noderedis:animals:4: Fido, 7 years old. + console.log(`${doc.id}: ${doc.value.name}, ${doc.value.age} years old.`); } -searchHashes(); +await client.quit(); diff --git a/examples/search-json.js b/examples/search-json.js index a0a29c69296..6205a1f110e 100644 --- a/examples/search-json.js +++ b/examples/search-json.js @@ -1,147 +1,148 @@ // This example demonstrates how to use RediSearch and RedisJSON together. +// Requires both the RediSearch and RedisJSON modules: +// https://redis.io/docs/stack/search/ +// https://redis.io/docs/stack/json/ import { createClient, SchemaFieldTypes, AggregateGroupByReducers, AggregateSteps } from 'redis'; -async function searchJSON() { - const client = createClient(); +const client = createClient(); - await client.connect(); +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' - }, - '$.email': { - type: SchemaFieldTypes.TAG, - AS: 'email' - } - }, { - 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); +// Create an index. +// https://redis.io/commands/ft.create/ +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' + }, + '$.email': { + type: SchemaFieldTypes.TAG, + AS: 'email' } + }, { + 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, - email: 'alice@nonexist.com' - }), - client.json.set('noderedis:users:2', '$', { - name: 'Bob', - age: 23, - coins: 15, - email: 'bob@somewhere.gov' - }) - ]); +// Add some users. +// https://redis.io/commands/json.set/ +await Promise.all([ + client.json.set('noderedis:users:1', '$', { + name: 'Alice', + age: 32, + coins: 100, + email: 'alice@nonexist.com' + }), + client.json.set('noderedis:users:2', '$', { + name: 'Bob', + age: 23, + coins: 15, + email: 'bob@somewhere.gov' + }) +]); - // Search all users under 30 - console.log('Users under 30 years old:'); - console.log( - // https://oss.redis.com/redisearch/Commands/#ftsearch - JSON.stringify( - await client.ft.search('idx:users', '@age:[0 30]'), - null, - 2 - ) - ); - // { - // "total": 1, - // "documents": [ - // { - // "id": "noderedis:users:2", - // "value": { - // "name": "Bob", - // "age": 23, - // "coins": 15, - // "email": "bob@somewhere.gov" - // } - // } - // ] - // } +// Search all users under 30 +console.log('Users under 30 years old:'); +console.log( + // https://redis.io/commands/ft.search/ + JSON.stringify( + await client.ft.search('idx:users', '@age:[0 30]'), + null, + 2 + ) +); +// { +// "total": 1, +// "documents": [ +// { +// "id": "noderedis:users:2", +// "value": { +// "name": "Bob", +// "age": 23, +// "coins": 15, +// "email": "bob@somewhere.gov" +// } +// } +// ] +// } - // Find a user by email - note we need to escape . and @ characters - // in the email address. This applies for other punctuation too. - // https://oss.redis.com/redisearch/Tags/#including_punctuation_in_tags - console.log('Users with email "bob@somewhere.gov":'); - const emailAddress = 'bob@somewhere.gov'.replace(/[.@]/g, '\\$&'); - console.log( - JSON.stringify( - await client.ft.search('idx:users', `@email:{${emailAddress}}`), - null, - 2 - ) - ); - // { - // "total": 1, - // "documents": [ - // { - // "id": "noderedis:users:2", - // "value": { - // "name": "Bob", - // "age": 23, - // "coins": 15, - // "email": "bob@somewhere.gov" - // } - // } - // ] - // } +// Find a user by email - note we need to escape . and @ characters +// in the email address. This applies for other punctuation too. +// https://redis.io/docs/stack/search/reference/tags/#including-punctuation-in-tags +console.log('Users with email "bob@somewhere.gov":'); +const emailAddress = 'bob@somewhere.gov'.replace(/[.@]/g, '\\$&'); +console.log( + JSON.stringify( + await client.ft.search('idx:users', `@email:{${emailAddress}}`), + null, + 2 + ) +); +// { +// "total": 1, +// "documents": [ +// { +// "id": "noderedis:users:2", +// "value": { +// "name": "Bob", +// "age": 23, +// "coins": 15, +// "email": "bob@somewhere.gov" +// } +// } +// ] +// } - // Some aggregrations, what's the average age and total number of coins... - // https://oss.redis.com/redisearch/Commands/#ftaggregate - console.log('Aggregation Demo:'); - console.log( - JSON.stringify( - 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' - }] +// Some aggregrations, what's the average age and total number of coins... +// https://redis.io/commands/ft.aggregate/ +console.log('Aggregation Demo:'); +console.log( + JSON.stringify( + 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' }] - }), - null, - 2 - ) - ); - // { - // "total": 1, - // "results": [ - // { - // "averageAge": "27.5", - // "totalCoins": "115" - // } - // ] - // } - - await client.quit(); -} + }] + }), + null, + 2 + ) +); +// { +// "total": 1, +// "results": [ +// { +// "averageAge": "27.5", +// "totalCoins": "115" +// } +// ] +// } -searchJSON(); +await client.quit(); diff --git a/examples/set-scan.js b/examples/set-scan.js index 3cca05b152d..73f6c443444 100644 --- a/examples/set-scan.js +++ b/examples/set-scan.js @@ -4,16 +4,12 @@ import { createClient } from 'redis'; -async function setScan() { - const client = createClient(); - await client.connect(); +const client = createClient(); +await client.connect(); - const setName = 'setName'; - for await (const member of client.sScanIterator(setName)) { - console.log(member); - } - - await client.quit(); +const setName = 'setName'; +for await (const member of client.sScanIterator(setName)) { + console.log(member); } -setScan(); +await client.quit(); diff --git a/examples/sorted-set.js b/examples/sorted-set.js index e2c4f145116..eb1f82867c1 100644 --- a/examples/sorted-set.js +++ b/examples/sorted-set.js @@ -3,33 +3,29 @@ import { createClient } from 'redis'; -async function addToSortedSet() { - const client = createClient(); - await client.connect(); +const client = createClient(); +await client.connect(); - await client.zAdd('mysortedset', [ - { - score: 99, - value: 'Ninety Nine' - }, - { - score: 100, - value: 'One Hundred' - }, - { - score: 101, - value: 'One Hundred and One' - } - ]); - - // Get all of the values/scores from the sorted set using - // the scan approach: - // https://redis.io/commands/zscan - for await (const memberWithScore of client.zScanIterator('mysortedset')) { - console.log(memberWithScore); +await client.zAdd('mysortedset', [ + { + score: 99, + value: 'Ninety Nine' + }, + { + score: 100, + value: 'One Hundred' + }, + { + score: 101, + value: 'One Hundred and One' } - - await client.quit(); +]); + +// Get all of the values/scores from the sorted set using +// the scan approach: +// https://redis.io/commands/zscan +for await (const memberWithScore of client.zScanIterator('mysortedset')) { + console.log(memberWithScore); } -addToSortedSet(); +await client.quit(); diff --git a/examples/stream-consumer-group.js b/examples/stream-consumer-group.js index 0dc8ff20fef..0161b5b4d32 100644 --- a/examples/stream-consumer-group.js +++ b/examples/stream-consumer-group.js @@ -1,4 +1,6 @@ // A sample stream consumer using the blocking variant of XREADGROUP. +// https://redis.io/commands/xreadgroup/ + // This consumer works in collaboration with other instances of itself // in the same consumer group such that the group as a whole receives // every entry from the stream. @@ -20,85 +22,84 @@ import { createClient, commandOptions } from 'redis'; -async function streamConsumerGroup() { - const client = createClient(); +const client = createClient(); - if (process.argv.length !== 3) { - console.log(`usage: node stream-consumer-group.js `); - process.exit(1); - } +if (process.argv.length !== 3) { + console.log(`usage: node stream-consumer-group.js `); + process.exit(1); +} - const consumerName = process.argv[2]; +const consumerName = process.argv[2]; - await client.connect(); +await client.connect(); - // Create the consumer group (and stream) if needed... - try { - await client.xGroupCreate('mystream', 'myconsumergroup', '0', { - MKSTREAM: true - }); - console.log('Created consumer group.'); - } catch (e) { - console.log('Consumer group already exists, skipped creation.'); - } +// Create the consumer group (and stream) if needed... +try { + // https://redis.io/commands/xgroup-create/ + await client.xGroupCreate('mystream', 'myconsumergroup', '0', { + MKSTREAM: true + }); + console.log('Created consumer group.'); +} catch (e) { + console.log('Consumer group already exists, skipped creation.'); +} - console.log(`Starting consumer ${consumerName}.`); +console.log(`Starting consumer ${consumerName}.`); - while (true) { - try { - let response = await client.xReadGroup( - commandOptions({ - isolated: true - }), - 'myconsumergroup', - consumerName, [ - // XREADGROUP can read from multiple streams, starting at a - // different ID for each... - { - key: 'mystream', - id: '>' // Next entry ID that no consumer in this group has read - } - ], { - // Read 1 entry at a time, block for 5 seconds if there are none. - COUNT: 1, - BLOCK: 5000 +while (true) { + try { + // https://redis.io/commands/xreadgroup/ + let response = await client.xReadGroup( + commandOptions({ + isolated: true + }), + 'myconsumergroup', + consumerName, [ + // XREADGROUP can read from multiple streams, starting at a + // different ID for each... + { + key: 'mystream', + id: '>' // Next entry ID that no consumer in this group has read } - ); + ], { + // Read 1 entry at a time, block for 5 seconds if there are none. + COUNT: 1, + BLOCK: 5000 + } + ); - if (response) { - // Response is an array of streams, each containing an array of - // entries: - // - // [ - // { - // "name": "mystream", - // "messages": [ - // { - // "id": "1642088708425-0", - // "message": { - // "num": "999" - // } - // } - // ] - // } - // ] - console.log(JSON.stringify(response)); + if (response) { + // Response is an array of streams, each containing an array of + // entries: + // + // [ + // { + // "name": "mystream", + // "messages": [ + // { + // "id": "1642088708425-0", + // "message": { + // "num": "999" + // } + // } + // ] + // } + // ] + console.log(JSON.stringify(response)); - // Use XACK to acknowledge successful processing of this - // stream entry. - const entryId = response[0].messages[0].id; - await client.xAck('mystream', 'myconsumergroup', entryId); + // Use XACK to acknowledge successful processing of this + // stream entry. + // https://redis.io/commands/xack/ + const entryId = response[0].messages[0].id; + await client.xAck('mystream', 'myconsumergroup', entryId); - console.log(`Acknowledged processing of entry ${entryId}.`); - } else { - // Response is null, we have read everything that is - // in the stream right now... - console.log('No new stream entries.'); - } - } catch (err) { - console.error(err); + console.log(`Acknowledged processing of entry ${entryId}.`); + } else { + // Response is null, we have read everything that is + // in the stream right now... + console.log('No new stream entries.'); } + } catch (err) { + console.error(err); } } - -streamConsumerGroup(); diff --git a/examples/stream-consumer.js b/examples/stream-consumer.js index f6ab16421cd..3d5fc20fb43 100644 --- a/examples/stream-consumer.js +++ b/examples/stream-consumer.js @@ -1,64 +1,61 @@ // A sample stream consumer using the blocking variant of XREAD. +// https://redis.io/commands/xread/ // This consumes entries from a stream created by stream-producer.js import { createClient, commandOptions } from 'redis'; -async function streamConsumer() { - const client = createClient(); +const client = createClient(); - await client.connect(); +await client.connect(); - let currentId = '0-0'; // Start at lowest possible stream ID +let currentId = '0-0'; // Start at lowest possible stream ID - while (true) { - try { - let response = await client.xRead( - commandOptions({ - isolated: true - }), [ - // XREAD can read from multiple streams, starting at a - // different ID for each... - { - key: 'mystream', - id: currentId - } - ], { - // Read 1 entry at a time, block for 5 seconds if there are none. - COUNT: 1, - BLOCK: 5000 +while (true) { + try { + let response = await client.xRead( + commandOptions({ + isolated: true + }), [ + // XREAD can read from multiple streams, starting at a + // different ID for each... + { + key: 'mystream', + id: currentId } - ); - - if (response) { - // Response is an array of streams, each containing an array of - // entries: - // [ - // { - // "name": "mystream", - // "messages": [ - // { - // "id": "1642088708425-0", - // "message": { - // "num": "999" - // } - // } - // ] - // } - // ] - console.log(JSON.stringify(response)); - - // Get the ID of the first (only) entry returned. - currentId = response[0].messages[0].id; - console.log(currentId); - } else { - // Response is null, we have read everything that is - // in the stream right now... - console.log('No new stream entries.'); + ], { + // Read 1 entry at a time, block for 5 seconds if there are none. + COUNT: 1, + BLOCK: 5000 } - } catch (err) { - console.error(err); + ); + + if (response) { + // Response is an array of streams, each containing an array of + // entries: + // [ + // { + // "name": "mystream", + // "messages": [ + // { + // "id": "1642088708425-0", + // "message": { + // "num": "999" + // } + // } + // ] + // } + // ] + console.log(JSON.stringify(response)); + + // Get the ID of the first (only) entry returned. + currentId = response[0].messages[0].id; + console.log(currentId); + } else { + // Response is null, we have read everything that is + // in the stream right now... + console.log('No new stream entries.'); } + } catch (err) { + console.error(err); } } - -streamConsumer(); diff --git a/examples/stream-producer.js b/examples/stream-producer.js index 00d6dab7ae6..f81931e5197 100644 --- a/examples/stream-producer.js +++ b/examples/stream-producer.js @@ -1,53 +1,50 @@ // A sample stream producer using XADD. +// https://redis.io/commands/xadd/ import { createClient } from 'redis'; -async function streamProducer() { - const client = createClient(); - - await client.connect(); - - for (let i = 0; i < 10000; i++) { - await client.xAdd( - 'mystream', - '*', // * = Let Redis generate a timestamp ID for this new entry. - // Payload to add to the stream: - { - i: i.toString() - // Other name/value pairs can go here as required... - } - ); - - // Also add to a stream whose length we will cap at approximately - // 1000 entries using the MAXLEN trimming strategy: - // https://redis.io/commands/xadd/ - - await client.xAdd( - 'mytrimmedstream', - id, // Re-use the ID from the previous stream. - // Payload to add to the stream: - { - i: i.toString() - // Other name/value pairs can go here as required... - }, - // Specify a trimming strategy... - { - TRIM: { - strategy: 'MAXLEN', // Trim by length. - strategyModifier: '~', // Approximate trimming. - threshold: 1000 // Retain around 1000 entries. - } +const client = createClient(); + +await client.connect(); + +for (let i = 0; i < 10000; i++) { + await client.xAdd( + 'mystream', + '*', // * = Let Redis generate a timestamp ID for this new entry. + // Payload to add to the stream: + { + i: i.toString() + // Other name/value pairs can go here as required... + } + ); + + // Also add to a stream whose length we will cap at approximately + // 1000 entries using the MAXLEN trimming strategy: + // https://redis.io/commands/xadd/ + + await client.xAdd( + 'mytrimmedstream', + '*', + // Payload to add to the stream: + { + i: i.toString() + // Other name/value pairs can go here as required... + }, + // Specify a trimming strategy... + { + TRIM: { + strategy: 'MAXLEN', // Trim by length. + strategyModifier: '~', // Approximate trimming. + threshold: 1000 // Retain around 1000 entries. } - ); - } - - // Take a look at how many entries are in the streams... - // Should be 10000: - console.log(`Length of mystream: ${await client.xLen('mystream')}.`); - // Should be approximately 1000: - console.log(`Length of mytrimmedstream: ${await client.xLen('mytrimmedstream')}.`); - - await client.quit(); + } + ); } -streamProducer(); +// Take a look at how many entries are in the streams... +// https://redis.io/commands/xlen/ +// Should be 10000: +console.log(`Length of mystream: ${await client.xLen('mystream')}.`); +// Should be approximately 1000: +console.log(`Length of mytrimmedstream: ${await client.xLen('mytrimmedstream')}.`); +await client.quit(); diff --git a/examples/time-series.js b/examples/time-series.js index 9983a17e747..2f2ac598032 100644 --- a/examples/time-series.js +++ b/examples/time-series.js @@ -1,126 +1,122 @@ // Add data to a Redis TimeSeries and query it. -// Requires the RedisTimeSeries module: https://redistimeseries.io/ +// Requires the RedisTimeSeries module: https://redis.io/docs/stack/timeseries/ import { createClient } from 'redis'; import { TimeSeriesDuplicatePolicies, TimeSeriesEncoding, TimeSeriesAggregationType } from '@redis/time-series'; -async function timeSeries() { - const client = createClient(); - - await client.connect(); - await client.del('mytimeseries'); - - try { - // Create a timeseries - // https://oss.redis.com/redistimeseries/commands/#tscreate - const created = await client.ts.create('mytimeseries', { - RETENTION: 86400000, // 1 day in milliseconds - ENCODING: TimeSeriesEncoding.UNCOMPRESSED, // No compression - DUPLICATE_POLICY: TimeSeriesDuplicatePolicies.BLOCK // No duplicates - }); - - if (created === 'OK') { - console.log('Created timeseries.'); - } else { - console.log('Error creating timeseries :('); - process.exit(1); - } +const client = createClient(); + +await client.connect(); +await client.del('mytimeseries'); + +try { + // Create a timeseries + // https://redis.io/commands/ts.create/ + const created = await client.ts.create('mytimeseries', { + RETENTION: 86400000, // 1 day in milliseconds + ENCODING: TimeSeriesEncoding.UNCOMPRESSED, // No compression + DUPLICATE_POLICY: TimeSeriesDuplicatePolicies.BLOCK // No duplicates + }); + + if (created === 'OK') { + console.log('Created timeseries.'); + } else { + console.log('Error creating timeseries :('); + process.exit(1); + } - let value = Math.floor(Math.random() * 1000) + 1; // Random data point value - let currentTimestamp = 1640995200000; // Jan 1 2022 00:00:00 - let num = 0; + let value = Math.floor(Math.random() * 1000) + 1; // Random data point value + let currentTimestamp = 1640995200000; // Jan 1 2022 00:00:00 + let num = 0; - while (num < 10000) { - // Add a new value to the timeseries, providing our own timestamp: - // https://oss.redis.com/redistimeseries/commands/#tsadd - await client.ts.add('mytimeseries', currentTimestamp, value); - console.log(`Added timestamp ${currentTimestamp}, value ${value}.`); + while (num < 10000) { + // Add a new value to the timeseries, providing our own timestamp: + // https://redis.io/commands/ts.add/ + await client.ts.add('mytimeseries', currentTimestamp, value); + console.log(`Added timestamp ${currentTimestamp}, value ${value}.`); - num += 1; - value = Math.floor(Math.random() * 1000) + 1; // Get another random value - currentTimestamp += 1000; // Move on one second. - } - - // Add multiple values to the timeseries in round trip to the server: - // https://oss.redis.com/redistimeseries/commands/#tsmadd - const response = await client.ts.mAdd([{ - key: 'mytimeseries', - timestamp: currentTimestamp + 60000, - value: Math.floor(Math.random() * 1000) + 1 - }, { - key: 'mytimeseries', - timestamp: currentTimestamp + 120000, - value: Math.floor(Math.random() * 1000) + 1 - }]); - - // response = array of timestamps added by TS.MADD command. - if (response.length === 2) { - console.log('Added 2 entries to timeseries with TS.MADD.'); - } + num += 1; + value = Math.floor(Math.random() * 1000) + 1; // Get another random value + currentTimestamp += 1000; // Move on one second. + } - // Update timeseries retention with TS.ALTER: - // https://oss.redis.com/redistimeseries/commands/#tsalter - const alterResponse = await client.ts.alter('mytimeseries', { - RETENTION: 0 // Keep the entries forever - }); + // Add multiple values to the timeseries in round trip to the server: + // https://redis.io/commands/ts.madd/ + const response = await client.ts.mAdd([{ + key: 'mytimeseries', + timestamp: currentTimestamp + 60000, + value: Math.floor(Math.random() * 1000) + 1 + }, { + key: 'mytimeseries', + timestamp: currentTimestamp + 120000, + value: Math.floor(Math.random() * 1000) + 1 + }]); + + // response = array of timestamps added by TS.MADD command. + if (response.length === 2) { + console.log('Added 2 entries to timeseries with TS.MADD.'); + } - if (alterResponse === 'OK') { - console.log('Timeseries retention settings altered successfully.'); - } + // Update timeseries retention with TS.ALTER: + // https://redis.io/commands/ts.alter/ + const alterResponse = await client.ts.alter('mytimeseries', { + RETENTION: 0 // Keep the entries forever + }); - // Query the timeseries with TS.RANGE: - // https://oss.redis.com/redistimeseries/commands/#tsrangetsrevrange - const fromTimestamp = 1640995200000; // Jan 1 2022 00:00:00 - const toTimestamp = 1640995260000; // Jan 1 2022 00:01:00 - const rangeResponse = await client.ts.range('mytimeseries', fromTimestamp, toTimestamp, { - // Group into 10 second averages. - AGGREGATION: { - type: TimeSeriesAggregationType.AVERAGE, - timeBucket: 10000 - } - }); - - console.log('RANGE RESPONSE:'); - // rangeResponse looks like: - // [ - // { timestamp: 1640995200000, value: 356.8 }, - // { timestamp: 1640995210000, value: 534.8 }, - // { timestamp: 1640995220000, value: 481.3 }, - // { timestamp: 1640995230000, value: 437 }, - // { timestamp: 1640995240000, value: 507.3 }, - // { timestamp: 1640995250000, value: 581.2 }, - // { timestamp: 1640995260000, value: 600 } - // ] - - console.log(rangeResponse); - - // Get some information about the state of the timeseries. - // https://oss.redis.com/redistimeseries/commands/#tsinfo - const tsInfo = await client.ts.info('mytimeseries'); - - // tsInfo looks like this: - // { - // totalSamples: 1440, - // memoryUsage: 28904, - // firstTimestamp: 1641508920000, - // lastTimestamp: 1641595320000, - // retentionTime: 86400000, - // chunkCount: 7, - // chunkSize: 4096, - // chunkType: 'uncompressed', - // duplicatePolicy: 'block', - // labels: [], - // sourceKey: null, - // rules: [] - // } - - console.log('Timeseries info:'); - console.log(tsInfo); - } catch (e) { - console.error(e); + if (alterResponse === 'OK') { + console.log('Timeseries retention settings altered successfully.'); } - await client.quit(); + // Query the timeseries with TS.RANGE: + // https://redis.io/commands/ts.range/ + const fromTimestamp = 1640995200000; // Jan 1 2022 00:00:00 + const toTimestamp = 1640995260000; // Jan 1 2022 00:01:00 + const rangeResponse = await client.ts.range('mytimeseries', fromTimestamp, toTimestamp, { + // Group into 10 second averages. + AGGREGATION: { + type: TimeSeriesAggregationType.AVERAGE, + timeBucket: 10000 + } + }); + + console.log('RANGE RESPONSE:'); + // rangeResponse looks like: + // [ + // { timestamp: 1640995200000, value: 356.8 }, + // { timestamp: 1640995210000, value: 534.8 }, + // { timestamp: 1640995220000, value: 481.3 }, + // { timestamp: 1640995230000, value: 437 }, + // { timestamp: 1640995240000, value: 507.3 }, + // { timestamp: 1640995250000, value: 581.2 }, + // { timestamp: 1640995260000, value: 600 } + // ] + + console.log(rangeResponse); + + // Get some information about the state of the timeseries. + // https://redis.io/commands/ts.info/ + const tsInfo = await client.ts.info('mytimeseries'); + + // tsInfo looks like this: + // { + // totalSamples: 1440, + // memoryUsage: 28904, + // firstTimestamp: 1641508920000, + // lastTimestamp: 1641595320000, + // retentionTime: 86400000, + // chunkCount: 7, + // chunkSize: 4096, + // chunkType: 'uncompressed', + // duplicatePolicy: 'block', + // labels: [], + // sourceKey: null, + // rules: [] + // } + + console.log('Timeseries info:'); + console.log(tsInfo); +} catch (e) { + console.error(e); } -timeSeries(); +await client.quit(); diff --git a/examples/topk.js b/examples/topk.js index 024dc30ecb0..35cdc4a8500 100644 --- a/examples/topk.js +++ b/examples/topk.js @@ -1,112 +1,113 @@ // This example demonstrates the use of the Top K -// in the RedisBloom module (https://redisbloom.io/) +// in the RedisBloom module (https://redis.io/docs/stack/bloom/) import { createClient } from 'redis'; -async function topK() { - const client = createClient(); +const client = createClient(); - await client.connect(); +await client.connect(); - // Delete any pre-existing Top K. - await client.del('mytopk'); +// Delete any pre-existing Top K. +await client.del('mytopk'); - // Reserve a Top K to track the 10 most common items. - // https://oss.redis.com/redisbloom/TopK_Commands/#topkreserve - try { - await client.topK.reserve('mytopk', 10); - console.log('Reserved Top K.'); - } catch (e) { - if (e.message.endsWith('key already exists')) { - console.log('Top K already reserved.'); - } else { - console.log('Error, maybe RedisBloom is not installed?:'); - console.log(e); - } +// Reserve a Top K to track the 10 most common items. +// https://redis.io/commands/topk.reserve/ +try { + await client.topK.reserve('mytopk', 10, { width: 400, depth: 10, decay: 0.9 }); + console.log('Reserved Top K.'); +} catch (e) { + if (e.message.endsWith('key already exists')) { + console.log('Top K already reserved.'); + } else { + console.log('Error, maybe RedisBloom is not installed?:'); + console.log(e); } +} - const teamMembers = [ - 'leibale', - 'simon', - 'guy', - 'suze', - 'brian', - 'steve', - 'kyleb', - 'kyleo', - 'josefin', - 'alex', - 'nava', - 'lance', - 'rachel', - 'kaitlyn' - ]; +const teamMembers = [ + 'leibale', + 'simon', + 'guy', + 'suze', + 'brian', + 'steve', + 'kyleb', + 'kyleo', + 'josefin', + 'alex', + 'nava', + 'lance', + 'rachel', + 'kaitlyn' +]; - // Add random counts for random team members with TOPK.INCRBY - for (let n = 0; n < 1000; n++) { - const teamMember = teamMembers[Math.floor(Math.random() * teamMembers.length)]; - const points = Math.floor(Math.random() * 1000) + 1; - await client.topK.incrBy('mytopk', { - item: teamMember, - incrementBy: points - }); - console.log(`Added ${points} points for ${teamMember}.`); - } +// Add random counts for random team members with TOPK.INCRBY +// https://redis.io/commands/topk.incrby/ +for (let n = 0; n < 1000; n++) { + const teamMember = teamMembers[Math.floor(Math.random() * teamMembers.length)]; + const points = Math.floor(Math.random() * 1000) + 1; + await client.topK.incrBy('mytopk', { + item: teamMember, + incrementBy: points + }); + console.log(`Added ${points} points for ${teamMember}.`); +} - // List out the top 10 with TOPK.LIST - const top10 = await client.topK.list('mytopk'); - console.log('The top 10:'); - // top10 looks like this: - // [ - // 'guy', 'nava', - // 'kaitlyn', 'brian', - // 'simon', 'suze', - // 'lance', 'alex', - // 'steve', 'kyleo' - // ] - console.log(top10); +// List out the top 10 with TOPK.LIST +// https://redis.io/commands/topk.list/ +const top10 = await client.topK.list('mytopk'); +console.log('The top 10:'); +// top10 looks like this: +// [ +// 'guy', 'nava', +// 'kaitlyn', 'brian', +// 'simon', 'suze', +// 'lance', 'alex', +// 'steve', 'kyleo' +// ] +console.log(top10); - // List out the top 10 with their counts (requires RedisBloom >=2.2.9) - const top10WithCounts = await client.topK.listWithCount('mytopk'); - console.log('The top 10 with counts:'); - console.log(top10WithCounts); - // top10WithCounts looks like this: - // [ - // { item: 'suze', count: 42363 }, - // { item: 'lance', count: 41982 }, - // { item: 'simon', count: 41831 }, - // { item: 'steve', count: 39237 }, - // { item: 'guy', count: 39078 }, - // { item: 'kyleb', count: 37338 }, - // { item: 'leibale', count: 34230 }, - // { item: 'kyleo', count: 33812 }, - // { item: 'alex', count: 33679 }, - // { item: 'nava', count: 32663 } - // ] +// List out the top 10 with their counts (requires RedisBloom >=2.2.9) +// https://redis.io/commands/topk.list/ +const top10WithCounts = await client.topK.listWithCount('mytopk'); +console.log('The top 10 with counts:'); +console.log(top10WithCounts); +// top10WithCounts looks like this: +// [ +// { item: 'suze', count: 42363 }, +// { item: 'lance', count: 41982 }, +// { item: 'simon', count: 41831 }, +// { item: 'steve', count: 39237 }, +// { item: 'guy', count: 39078 }, +// { item: 'kyleb', count: 37338 }, +// { item: 'leibale', count: 34230 }, +// { item: 'kyleo', count: 33812 }, +// { item: 'alex', count: 33679 }, +// { item: 'nava', count: 32663 } +// ] - // Check if a few team members are in the top 10 with TOPK.QUERY: - const [ steve, suze, leibale, frederick ] = await client.topK.query('mytopk', [ - 'steve', - 'suze', - 'leibale', - 'frederick' - ]); +// Check if a few team members are in the top 10 with TOPK.QUERY: +// https://redis.io/commands/topk.query/ +const [ steve, suze, leibale, frederick ] = await client.topK.query('mytopk', [ + 'steve', + 'suze', + 'leibale', + 'frederick' +]); - console.log(`steve ${steve === 1 ? 'is': 'is not'} in the top 10.`); - console.log(`suze ${suze === 1 ? 'is': 'is not'} in the top 10.`); - console.log(`leibale ${leibale === 1 ? 'is': 'is not'} in the top 10.`); - console.log(`frederick ${frederick === 1 ? 'is': 'is not'} in the top 10.`); +console.log(`steve ${steve === 1 ? 'is': 'is not'} in the top 10.`); +console.log(`suze ${suze === 1 ? 'is': 'is not'} in the top 10.`); +console.log(`leibale ${leibale === 1 ? 'is': 'is not'} in the top 10.`); +console.log(`frederick ${frederick === 1 ? 'is': 'is not'} in the top 10.`); - // Get count estimate for some team members: - const [ simonCount, lanceCount ] = await client.topK.count('mytopk', [ - 'simon', - 'lance' - ]); +// Get count estimate for some team members with TOPK.COUNT: +// https://redis.io/commands/topk.count/ +const [ simonCount, lanceCount ] = await client.topK.count('mytopk', [ + 'simon', + 'lance' +]); - console.log(`Count estimate for simon: ${simonCount}.`); - console.log(`Count estimate for lance: ${lanceCount}.`); - - await client.quit(); -} +console.log(`Count estimate for simon: ${simonCount}.`); +console.log(`Count estimate for lance: ${lanceCount}.`); -topK(); +await client.quit(); From 7b7d0d2a369385345d346c66a655641ed1e4083c Mon Sep 17 00:00:00 2001 From: Leibale Eidelman Date: Mon, 15 Aug 2022 08:39:44 -0400 Subject: [PATCH 1239/1748] fix `nodeAddressMap` docs (#2228) --- docs/clustering.md | 14 ++++++++++---- 1 file changed, 10 insertions(+), 4 deletions(-) diff --git a/docs/clustering.md b/docs/clustering.md index bf88be6cdeb..b1b5b82021a 100644 --- a/docs/clustering.md +++ b/docs/clustering.md @@ -49,13 +49,19 @@ This is a mapping of addresses and ports, with the values being the accessible a ```javascript createCluster({ rootNodes: [{ - url: '10.0.0.1:30001' + url: 'external-host-1.io:30001' }, { - url: '10.0.0.2:30002' + url: 'external-host-2.io:30002' }], nodeAddressMap: { - '10.0.0.1:30001': 'external-host-1.io:30001', - '10.0.0.2:30002': 'external-host-2.io:30002' + '10.0.0.1:30001': { + host: 'external-host-1.io', + port: 30001 + }, + '10.0.0.2:30002': { + host: 'external-host-2.io', + port: 30002 + } } }); ``` From 1fdee05dd2f1a48e5abf5a3046606d6b6a51e447 Mon Sep 17 00:00:00 2001 From: Leibale Eidelman Date: Mon, 15 Aug 2022 08:40:03 -0400 Subject: [PATCH 1240/1748] close #2166 - add support for cursor api (#2217) --- packages/search/lib/commands/AGGREGATE.ts | 4 ++ .../lib/commands/AGGREGATE_WITHCURSOR.spec.ts | 37 ++++++++++++++++ .../lib/commands/AGGREGATE_WITHCURSOR.ts | 44 +++++++++++++++++++ .../search/lib/commands/CURSOR_DEL.spec.ts | 33 ++++++++++++++ packages/search/lib/commands/CURSOR_DEL.ts | 14 ++++++ .../search/lib/commands/CURSOR_READ.spec.ts | 36 +++++++++++++++ packages/search/lib/commands/CURSOR_READ.ts | 19 ++++++++ packages/search/lib/commands/index.ts | 9 ++++ 8 files changed, 196 insertions(+) create mode 100644 packages/search/lib/commands/AGGREGATE_WITHCURSOR.spec.ts create mode 100644 packages/search/lib/commands/AGGREGATE_WITHCURSOR.ts create mode 100644 packages/search/lib/commands/CURSOR_DEL.spec.ts create mode 100644 packages/search/lib/commands/CURSOR_DEL.ts create mode 100644 packages/search/lib/commands/CURSOR_READ.spec.ts create mode 100644 packages/search/lib/commands/CURSOR_READ.ts diff --git a/packages/search/lib/commands/AGGREGATE.ts b/packages/search/lib/commands/AGGREGATE.ts index 72d814cc030..c32d20b0b1c 100644 --- a/packages/search/lib/commands/AGGREGATE.ts +++ b/packages/search/lib/commands/AGGREGATE.ts @@ -126,6 +126,10 @@ export interface AggregateOptions { DIALECT?: number; } +export const FIRST_KEY_INDEX = 1; + +export const IS_READ_ONLY = true; + export function transformArguments( index: string, query: string, diff --git a/packages/search/lib/commands/AGGREGATE_WITHCURSOR.spec.ts b/packages/search/lib/commands/AGGREGATE_WITHCURSOR.spec.ts new file mode 100644 index 00000000000..65396f3f790 --- /dev/null +++ b/packages/search/lib/commands/AGGREGATE_WITHCURSOR.spec.ts @@ -0,0 +1,37 @@ +import { strict as assert } from 'assert'; +import testUtils, { GLOBAL } from '../test-utils'; +import { transformArguments } from './AGGREGATE_WITHCURSOR'; +import { SchemaFieldTypes } from '.'; + +describe('AGGREGATE WITHCURSOR', () => { + describe('transformArguments', () => { + it('without options', () => { + assert.deepEqual( + transformArguments('index', '*'), + ['FT.AGGREGATE', 'index', '*', 'WITHCURSOR'] + ); + }); + + it('with COUNT', () => { + assert.deepEqual( + transformArguments('index', '*', { COUNT: 1 }), + ['FT.AGGREGATE', 'index', '*', 'WITHCURSOR', 'COUNT', '1'] + ); + }); + }); + + testUtils.testWithClient('client.ft.aggregateWithCursor', async client => { + await client.ft.create('index', { + field: SchemaFieldTypes.NUMERIC + }); + + assert.deepEqual( + await client.ft.aggregateWithCursor('index', '*'), + { + total: 0, + results: [], + cursor: 0 + } + ); + }, GLOBAL.SERVERS.OPEN); +}); diff --git a/packages/search/lib/commands/AGGREGATE_WITHCURSOR.ts b/packages/search/lib/commands/AGGREGATE_WITHCURSOR.ts new file mode 100644 index 00000000000..63f6ee8f187 --- /dev/null +++ b/packages/search/lib/commands/AGGREGATE_WITHCURSOR.ts @@ -0,0 +1,44 @@ +import { + AggregateOptions, + AggregateRawReply, + AggregateReply, + transformArguments as transformAggregateArguments, + transformReply as transformAggregateReply +} from './AGGREGATE'; + +export { FIRST_KEY_INDEX, IS_READ_ONLY } from './AGGREGATE'; + +interface AggregateWithCursorOptions extends AggregateOptions { + COUNT?: number; +} + +export function transformArguments( + index: string, + query: string, + options?: AggregateWithCursorOptions +) { + const args = transformAggregateArguments(index, query, options); + + args.push('WITHCURSOR'); + if (options?.COUNT) { + args.push('COUNT', options.COUNT.toString()); + } + + return args; +} + +type AggregateWithCursorRawReply = [ + result: AggregateRawReply, + cursor: number +]; + +interface AggregateWithCursorReply extends AggregateReply { + cursor: number; +} + +export function transformReply(reply: AggregateWithCursorRawReply): AggregateWithCursorReply { + return { + ...transformAggregateReply(reply[0]), + cursor: reply[1] + }; +} diff --git a/packages/search/lib/commands/CURSOR_DEL.spec.ts b/packages/search/lib/commands/CURSOR_DEL.spec.ts new file mode 100644 index 00000000000..d89725ef80d --- /dev/null +++ b/packages/search/lib/commands/CURSOR_DEL.spec.ts @@ -0,0 +1,33 @@ +import { strict as assert } from 'assert'; +import { SchemaFieldTypes } from '.'; +import testUtils, { GLOBAL } from '../test-utils'; +import { transformArguments } from './CURSOR_DEL'; + +describe('CURSOR DEL', () => { + it('transformArguments', () => { + assert.deepEqual( + transformArguments('index', 0), + ['FT.CURSOR', 'DEL', 'index', '0'] + ); + }); + + testUtils.testWithClient('client.ft.cursorDel', async client => { + const [ ,, { cursor } ] = await Promise.all([ + client.ft.create('idx', { + field: { + type: SchemaFieldTypes.TEXT + } + }), + client.hSet('key', 'field', 'value'), + client.ft.aggregateWithCursor('idx', '*', { + COUNT: 1 + }) + ]); + + + assert.equal( + await client.ft.cursorDel('idx', cursor), + 'OK' + ); + }, GLOBAL.SERVERS.OPEN); +}); diff --git a/packages/search/lib/commands/CURSOR_DEL.ts b/packages/search/lib/commands/CURSOR_DEL.ts new file mode 100644 index 00000000000..22c850f2a89 --- /dev/null +++ b/packages/search/lib/commands/CURSOR_DEL.ts @@ -0,0 +1,14 @@ +import { RedisCommandArgument } from '@redis/client/dist/lib/commands'; + +export const FIRST_KEY_INDEX = 1; + +export function transformArguments(index: RedisCommandArgument, cursorId: number) { + return [ + 'FT.CURSOR', + 'DEL', + index, + cursorId.toString() + ]; +} + +export declare function transformReply(): 'OK'; diff --git a/packages/search/lib/commands/CURSOR_READ.spec.ts b/packages/search/lib/commands/CURSOR_READ.spec.ts new file mode 100644 index 00000000000..5b4f4122d49 --- /dev/null +++ b/packages/search/lib/commands/CURSOR_READ.spec.ts @@ -0,0 +1,36 @@ +import { strict as assert } from 'assert'; +import { SchemaFieldTypes } from '.'; +import testUtils, { GLOBAL } from '../test-utils'; +import { transformArguments } from './CURSOR_READ'; + +describe('CURSOR READ', () => { + it('transformArguments', () => { + assert.deepEqual( + transformArguments('index', 0), + ['FT.CURSOR', 'READ', 'index', '0'] + ); + }); + + testUtils.testWithClient('client.ft.cursorRead', async client => { + const [ ,, { cursor } ] = await Promise.all([ + client.ft.create('idx', { + field: { + type: SchemaFieldTypes.TEXT + } + }), + client.hSet('key', 'field', 'value'), + client.ft.aggregateWithCursor('idx', '*', { + COUNT: 1 + }) + ]); + + assert.deepEqual( + await client.ft.cursorRead('idx', cursor), + { + total: 0, + results: [], + cursor: 0 + } + ); + }, GLOBAL.SERVERS.OPEN); +}); diff --git a/packages/search/lib/commands/CURSOR_READ.ts b/packages/search/lib/commands/CURSOR_READ.ts new file mode 100644 index 00000000000..1e828cc3e46 --- /dev/null +++ b/packages/search/lib/commands/CURSOR_READ.ts @@ -0,0 +1,19 @@ +import { RedisCommandArgument, RedisCommandArguments } from '@redis/client/dist/lib/commands'; + +export const FIRST_KEY_INDEX = 1; + +export const IS_READ_ONLY = true; + +export function transformArguments( + index: RedisCommandArgument, + cursor: number +): RedisCommandArguments { + return [ + 'FT.CURSOR', + 'READ', + index, + cursor.toString() + ]; +} + +export { transformReply } from './AGGREGATE_WITHCURSOR'; diff --git a/packages/search/lib/commands/index.ts b/packages/search/lib/commands/index.ts index 34741440ba5..bc8eaf140fb 100644 --- a/packages/search/lib/commands/index.ts +++ b/packages/search/lib/commands/index.ts @@ -1,5 +1,6 @@ import * as _LIST from './_LIST'; import * as ALTER from './ALTER'; +import * as AGGREGATE_WITHCURSOR from './AGGREGATE_WITHCURSOR'; import * as AGGREGATE from './AGGREGATE'; import * as ALIASADD from './ALIASADD'; import * as ALIASDEL from './ALIASDEL'; @@ -7,6 +8,8 @@ import * as ALIASUPDATE from './ALIASUPDATE'; import * as CONFIG_GET from './CONFIG_GET'; import * as CONFIG_SET from './CONFIG_SET'; import * as CREATE from './CREATE'; +import * as CURSOR_DEL from './CURSOR_DEL'; +import * as CURSOR_READ from './CURSOR_READ'; import * as DICTADD from './DICTADD'; import * as DICTDEL from './DICTDEL'; import * as DICTDUMP from './DICTDUMP'; @@ -37,6 +40,8 @@ export default { _list: _LIST, ALTER, alter: ALTER, + AGGREGATE_WITHCURSOR, + aggregateWithCursor: AGGREGATE_WITHCURSOR, AGGREGATE, aggregate: AGGREGATE, ALIASADD, @@ -51,6 +56,10 @@ export default { configSet: CONFIG_SET, CREATE, create: CREATE, + CURSOR_DEL, + cursorDel: CURSOR_DEL, + CURSOR_READ, + cursorRead: CURSOR_READ, DICTADD, dictAdd: DICTADD, DICTDEL, From 942de1f0b4868f0f6464b2e0702b621a3373c4ee Mon Sep 17 00:00:00 2001 From: Leibale Eidelman Date: Mon, 22 Aug 2022 17:59:45 -0400 Subject: [PATCH 1241/1748] Handle unhandled errors in `socket.reconnectStrategry` (#2226) * handle errors in reconnect strategy * add test * fix retries typo * fix #2237 - flush queues on reconnect strategy error * Update socket.ts * Update socket.ts --- packages/client/lib/client/socket.spec.ts | 56 ++++++++++++++++------- packages/client/lib/client/socket.ts | 27 ++++++++--- 2 files changed, 59 insertions(+), 24 deletions(-) diff --git a/packages/client/lib/client/socket.spec.ts b/packages/client/lib/client/socket.spec.ts index 54f84eb9fe0..1b2d050c012 100644 --- a/packages/client/lib/client/socket.spec.ts +++ b/packages/client/lib/client/socket.spec.ts @@ -1,41 +1,63 @@ import { strict as assert } from 'assert'; import { SinonFakeTimers, useFakeTimers, spy } from 'sinon'; -import RedisSocket from './socket'; +import RedisSocket, { RedisSocketOptions } from './socket'; describe('Socket', () => { + function createSocket(options: RedisSocketOptions): RedisSocket { + const socket = new RedisSocket( + () => Promise.resolve(), + options + ); + + socket.on('error', (err) => { + // ignore errors + console.log(err); + }); + + return socket; + } + describe('reconnectStrategy', () => { let clock: SinonFakeTimers; beforeEach(() => clock = useFakeTimers()); afterEach(() => clock.restore()); - it('custom strategy', () => { - const reconnectStrategy = spy((retries: number): number | Error => { + it('custom strategy', async () => { + const reconnectStrategy = spy((retries: number) => { assert.equal(retries + 1, reconnectStrategy.callCount); - if (retries === 50) { - return Error('50'); - } + if (retries === 50) return new Error('50'); const time = retries * 2; queueMicrotask(() => clock.tick(time)); return time; }); - const socket = new RedisSocket( - () => Promise.resolve(), - { - host: 'error', - reconnectStrategy - } - ); - - socket.on('error', () => { - // ignore errors + const socket = createSocket({ + host: 'error', + reconnectStrategy }); - return assert.rejects(socket.connect(), { + await assert.rejects(socket.connect(), { message: '50' }); + + assert.equal(socket.isOpen, false); + }); + + it('should handle errors', async () => { + const socket = createSocket({ + host: 'error', + reconnectStrategy(retries: number) { + if (retries === 1) return new Error('done'); + queueMicrotask(() => clock.tick(500)); + throw new Error(); + } + }); + + await assert.rejects(socket.connect()); + + assert.equal(socket.isOpen, false); }); }); }); diff --git a/packages/client/lib/client/socket.ts b/packages/client/lib/client/socket.ts index 5fab5c5601b..2a955159323 100644 --- a/packages/client/lib/client/socket.ts +++ b/packages/client/lib/client/socket.ts @@ -44,10 +44,6 @@ export default class RedisSocket extends EventEmitter { return options; } - static #defaultReconnectStrategy(retries: number): number { - return Math.min(retries * 50, 500); - } - static #isTlsSocket(options: RedisSocketOptions): options is RedisTlsSocketOptions { return (options as RedisTlsSocketOptions).tls === true; } @@ -87,6 +83,23 @@ export default class RedisSocket extends EventEmitter { this.#options = RedisSocket.#initiateOptions(options); } + reconnectStrategy(retries: number): number | Error { + if (this.#options.reconnectStrategy) { + try { + const retryIn = this.#options.reconnectStrategy(retries); + if (typeof retryIn !== 'number' && !(retryIn instanceof Error)) { + throw new TypeError('Reconnect strategy should return `number | Error`'); + } + + return retryIn; + } catch (err) { + this.emit('error', err); + } + } + + return Math.min(retries * 50, 500); + } + async connect(): Promise { if (this.#isOpen) { throw new Error('Socket already opened'); @@ -116,14 +129,14 @@ export default class RedisSocket extends EventEmitter { this.#isReady = true; this.emit('ready'); } catch (err) { - this.emit('error', err); - - const retryIn = (this.#options?.reconnectStrategy ?? RedisSocket.#defaultReconnectStrategy)(retries); + const retryIn = this.reconnectStrategy(retries); if (retryIn instanceof Error) { this.#isOpen = false; + this.emit('error', err); throw new ReconnectStrategyError(retryIn, err); } + this.emit('error', err); await promiseTimeout(retryIn); return this.#connect(retries + 1); } From 35be671332b56d9274f9050eaf358f6c54246c01 Mon Sep 17 00:00:00 2001 From: Leibale Eidelman Date: Mon, 22 Aug 2022 18:23:43 -0400 Subject: [PATCH 1242/1748] upgrade dependencies (#2242) --- benchmark/package-lock.json | 48 +- benchmark/package.json | 2 +- package-lock.json | 1995 ++++++++++++++++------------- package.json | 2 +- packages/bloom/package.json | 8 +- packages/client/package.json | 16 +- packages/graph/package.json | 8 +- packages/json/package.json | 8 +- packages/search/package.json | 8 +- packages/test-utils/package.json | 6 +- packages/time-series/package.json | 8 +- 11 files changed, 1195 insertions(+), 914 deletions(-) diff --git a/benchmark/package-lock.json b/benchmark/package-lock.json index ee9db27c5b3..23781c0085c 100644 --- a/benchmark/package-lock.json +++ b/benchmark/package-lock.json @@ -8,14 +8,14 @@ "dependencies": { "@redis/client": "../packages/client", "hdr-histogram-js": "3.0.0", - "ioredis": "5.1.0", + "ioredis": "5.2.2", "redis-v3": "npm:redis@3.1.2", "yargs": "17.5.1" } }, "../packages/client": { "name": "@redis/client", - "version": "1.1.0", + "version": "1.2.0", "license": "MIT", "dependencies": { "cluster-key-slot": "1.1.0", @@ -25,18 +25,18 @@ "devDependencies": { "@istanbuljs/nyc-config-typescript": "^1.0.2", "@redis/test-utils": "*", - "@types/node": "^18.0.0", - "@types/sinon": "^10.0.12", + "@types/node": "^18.7.10", + "@types/sinon": "^10.0.13", "@types/yallist": "^4.0.1", - "@typescript-eslint/eslint-plugin": "^5.30.0", - "@typescript-eslint/parser": "^5.30.0", - "eslint": "^8.18.0", + "@typescript-eslint/eslint-plugin": "^5.34.0", + "@typescript-eslint/parser": "^5.34.0", + "eslint": "^8.22.0", "nyc": "^15.1.0", - "release-it": "^15.1.1", + "release-it": "^15.3.0", "sinon": "^14.0.0", "source-map-support": "^0.5.21", - "ts-node": "^10.8.1", - "typedoc": "^0.23.2", + "ts-node": "^10.9.1", + "typedoc": "^0.23.10", "typescript": "^4.7.4" }, "engines": { @@ -191,9 +191,9 @@ } }, "node_modules/ioredis": { - "version": "5.1.0", - "resolved": "https://registry.npmjs.org/ioredis/-/ioredis-5.1.0.tgz", - "integrity": "sha512-HYHnvwxFwefeUBj0hZFejLvd8Q/YNAfnZlZG/hSRxkRhXMs1H8soMEVccHd1WlLrKkynorXBsAtqDGskOdAfVQ==", + "version": "5.2.2", + "resolved": "https://registry.npmjs.org/ioredis/-/ioredis-5.2.2.tgz", + "integrity": "sha512-wryKc1ur8PcCmNwfcGkw5evouzpbDXxxkMkzPK8wl4xQfQf7lHe11Jotell5ikMVAtikXJEu/OJVaoV51BggRQ==", "dependencies": { "@ioredis/commands": "^1.1.1", "cluster-key-slot": "^1.1.0", @@ -395,20 +395,20 @@ "requires": { "@istanbuljs/nyc-config-typescript": "^1.0.2", "@redis/test-utils": "*", - "@types/node": "^18.0.0", - "@types/sinon": "^10.0.12", + "@types/node": "^18.7.10", + "@types/sinon": "^10.0.13", "@types/yallist": "^4.0.1", - "@typescript-eslint/eslint-plugin": "^5.30.0", - "@typescript-eslint/parser": "^5.30.0", + "@typescript-eslint/eslint-plugin": "^5.34.0", + "@typescript-eslint/parser": "^5.34.0", "cluster-key-slot": "1.1.0", - "eslint": "^8.18.0", + "eslint": "^8.22.0", "generic-pool": "3.8.2", "nyc": "^15.1.0", - "release-it": "^15.1.1", + "release-it": "^15.3.0", "sinon": "^14.0.0", "source-map-support": "^0.5.21", - "ts-node": "^10.8.1", - "typedoc": "^0.23.2", + "ts-node": "^10.9.1", + "typedoc": "^0.23.10", "typescript": "^4.7.4", "yallist": "4.0.0" } @@ -498,9 +498,9 @@ } }, "ioredis": { - "version": "5.1.0", - "resolved": "https://registry.npmjs.org/ioredis/-/ioredis-5.1.0.tgz", - "integrity": "sha512-HYHnvwxFwefeUBj0hZFejLvd8Q/YNAfnZlZG/hSRxkRhXMs1H8soMEVccHd1WlLrKkynorXBsAtqDGskOdAfVQ==", + "version": "5.2.2", + "resolved": "https://registry.npmjs.org/ioredis/-/ioredis-5.2.2.tgz", + "integrity": "sha512-wryKc1ur8PcCmNwfcGkw5evouzpbDXxxkMkzPK8wl4xQfQf7lHe11Jotell5ikMVAtikXJEu/OJVaoV51BggRQ==", "requires": { "@ioredis/commands": "^1.1.1", "cluster-key-slot": "^1.1.0", diff --git a/benchmark/package.json b/benchmark/package.json index 3d5b2ed030e..d5eac40b1ab 100644 --- a/benchmark/package.json +++ b/benchmark/package.json @@ -9,7 +9,7 @@ "dependencies": { "@redis/client": "../packages/client", "hdr-histogram-js": "3.0.0", - "ioredis": "5.1.0", + "ioredis": "5.2.2", "redis-v3": "npm:redis@3.1.2", "yargs": "17.5.1" } diff --git a/package-lock.json b/package-lock.json index 8f11ff757a9..c3eeda8cdae 100644 --- a/package-lock.json +++ b/package-lock.json @@ -22,7 +22,7 @@ "devDependencies": { "@tsconfig/node14": "^1.0.3", "gh-pages": "^4.0.0", - "release-it": "^15.1.1", + "release-it": "^15.3.0", "typescript": "^4.7.4" } }, @@ -52,30 +52,30 @@ } }, "node_modules/@babel/compat-data": { - "version": "7.18.6", - "resolved": "https://registry.npmjs.org/@babel/compat-data/-/compat-data-7.18.6.tgz", - "integrity": "sha512-tzulrgDT0QD6U7BJ4TKVk2SDDg7wlP39P9yAx1RfLy7vP/7rsDRlWVfbWxElslu56+r7QOhB2NSDsabYYruoZQ==", + "version": "7.18.13", + "resolved": "https://registry.npmjs.org/@babel/compat-data/-/compat-data-7.18.13.tgz", + "integrity": "sha512-5yUzC5LqyTFp2HLmDoxGQelcdYgSpP9xsnMWBphAscOdFrHSAVbLNzWiy32sVNDqJRDiJK6klfDnAgu6PAGSHw==", "dev": true, "engines": { "node": ">=6.9.0" } }, "node_modules/@babel/core": { - "version": "7.18.6", - "resolved": "https://registry.npmjs.org/@babel/core/-/core-7.18.6.tgz", - "integrity": "sha512-cQbWBpxcbbs/IUredIPkHiAGULLV8iwgNRMFzvbhEXISp4f3rUUXE5+TIw6KwUWUR3DwyI6gmBRnmAtYaWehwQ==", + "version": "7.18.13", + "resolved": "https://registry.npmjs.org/@babel/core/-/core-7.18.13.tgz", + "integrity": "sha512-ZisbOvRRusFktksHSG6pjj1CSvkPkcZq/KHD45LAkVP/oiHJkNBZWfpvlLmX8OtHDG8IuzsFlVRWo08w7Qxn0A==", "dev": true, "dependencies": { "@ampproject/remapping": "^2.1.0", "@babel/code-frame": "^7.18.6", - "@babel/generator": "^7.18.6", - "@babel/helper-compilation-targets": "^7.18.6", - "@babel/helper-module-transforms": "^7.18.6", - "@babel/helpers": "^7.18.6", - "@babel/parser": "^7.18.6", - "@babel/template": "^7.18.6", - "@babel/traverse": "^7.18.6", - "@babel/types": "^7.18.6", + "@babel/generator": "^7.18.13", + "@babel/helper-compilation-targets": "^7.18.9", + "@babel/helper-module-transforms": "^7.18.9", + "@babel/helpers": "^7.18.9", + "@babel/parser": "^7.18.13", + "@babel/template": "^7.18.10", + "@babel/traverse": "^7.18.13", + "@babel/types": "^7.18.13", "convert-source-map": "^1.7.0", "debug": "^4.1.0", "gensync": "^1.0.0-beta.2", @@ -91,12 +91,12 @@ } }, "node_modules/@babel/generator": { - "version": "7.18.7", - "resolved": "https://registry.npmjs.org/@babel/generator/-/generator-7.18.7.tgz", - "integrity": "sha512-shck+7VLlY72a2w9c3zYWuE1pwOKEiQHV7GTUbSnhyl5eu3i04t30tBY82ZRWrDfo3gkakCFtevExnxbkf2a3A==", + "version": "7.18.13", + "resolved": "https://registry.npmjs.org/@babel/generator/-/generator-7.18.13.tgz", + "integrity": "sha512-CkPg8ySSPuHTYPJYo7IRALdqyjM9HCbt/3uOBEFbzyGVP6Mn8bwFPB0jX6982JVNBlYzM1nnPkfjuXSOPtQeEQ==", "dev": true, "dependencies": { - "@babel/types": "^7.18.7", + "@babel/types": "^7.18.13", "@jridgewell/gen-mapping": "^0.3.2", "jsesc": "^2.5.1" }, @@ -119,12 +119,12 @@ } }, "node_modules/@babel/helper-compilation-targets": { - "version": "7.18.6", - "resolved": "https://registry.npmjs.org/@babel/helper-compilation-targets/-/helper-compilation-targets-7.18.6.tgz", - "integrity": "sha512-vFjbfhNCzqdeAtZflUFrG5YIFqGTqsctrtkZ1D/NB0mDW9TwW3GmmUepYY4G9wCET5rY5ugz4OGTcLd614IzQg==", + "version": "7.18.9", + "resolved": "https://registry.npmjs.org/@babel/helper-compilation-targets/-/helper-compilation-targets-7.18.9.tgz", + "integrity": "sha512-tzLCyVmqUiFlcFoAPLA/gL9TeYrF61VLNtb+hvkuVaB5SUjW7jcfrglBIX1vUIoT7CLP3bBlIMeyEsIl2eFQNg==", "dev": true, "dependencies": { - "@babel/compat-data": "^7.18.6", + "@babel/compat-data": "^7.18.8", "@babel/helper-validator-option": "^7.18.6", "browserslist": "^4.20.2", "semver": "^6.3.0" @@ -137,22 +137,22 @@ } }, "node_modules/@babel/helper-environment-visitor": { - "version": "7.18.6", - "resolved": "https://registry.npmjs.org/@babel/helper-environment-visitor/-/helper-environment-visitor-7.18.6.tgz", - "integrity": "sha512-8n6gSfn2baOY+qlp+VSzsosjCVGFqWKmDF0cCWOybh52Dw3SEyoWR1KrhMJASjLwIEkkAufZ0xvr+SxLHSpy2Q==", + "version": "7.18.9", + "resolved": "https://registry.npmjs.org/@babel/helper-environment-visitor/-/helper-environment-visitor-7.18.9.tgz", + "integrity": "sha512-3r/aACDJ3fhQ/EVgFy0hpj8oHyHpQc+LPtJoY9SzTThAsStm4Ptegq92vqKoE3vD706ZVFWITnMnxucw+S9Ipg==", "dev": true, "engines": { "node": ">=6.9.0" } }, "node_modules/@babel/helper-function-name": { - "version": "7.18.6", - "resolved": "https://registry.npmjs.org/@babel/helper-function-name/-/helper-function-name-7.18.6.tgz", - "integrity": "sha512-0mWMxV1aC97dhjCah5U5Ua7668r5ZmSC2DLfH2EZnf9c3/dHZKiFa5pRLMH5tjSl471tY6496ZWk/kjNONBxhw==", + "version": "7.18.9", + "resolved": "https://registry.npmjs.org/@babel/helper-function-name/-/helper-function-name-7.18.9.tgz", + "integrity": "sha512-fJgWlZt7nxGksJS9a0XdSaI4XvpExnNIgRP+rVefWh5U7BL8pPuir6SJUmFKRfjWQ51OtWSzwOxhaH/EBWWc0A==", "dev": true, "dependencies": { "@babel/template": "^7.18.6", - "@babel/types": "^7.18.6" + "@babel/types": "^7.18.9" }, "engines": { "node": ">=6.9.0" @@ -183,19 +183,19 @@ } }, "node_modules/@babel/helper-module-transforms": { - "version": "7.18.6", - "resolved": "https://registry.npmjs.org/@babel/helper-module-transforms/-/helper-module-transforms-7.18.6.tgz", - "integrity": "sha512-L//phhB4al5uucwzlimruukHB3jRd5JGClwRMD/ROrVjXfLqovYnvQrK/JK36WYyVwGGO7OD3kMyVTjx+WVPhw==", + "version": "7.18.9", + "resolved": "https://registry.npmjs.org/@babel/helper-module-transforms/-/helper-module-transforms-7.18.9.tgz", + "integrity": "sha512-KYNqY0ICwfv19b31XzvmI/mfcylOzbLtowkw+mfvGPAQ3kfCnMLYbED3YecL5tPd8nAYFQFAd6JHp2LxZk/J1g==", "dev": true, "dependencies": { - "@babel/helper-environment-visitor": "^7.18.6", + "@babel/helper-environment-visitor": "^7.18.9", "@babel/helper-module-imports": "^7.18.6", "@babel/helper-simple-access": "^7.18.6", "@babel/helper-split-export-declaration": "^7.18.6", "@babel/helper-validator-identifier": "^7.18.6", "@babel/template": "^7.18.6", - "@babel/traverse": "^7.18.6", - "@babel/types": "^7.18.6" + "@babel/traverse": "^7.18.9", + "@babel/types": "^7.18.9" }, "engines": { "node": ">=6.9.0" @@ -225,6 +225,15 @@ "node": ">=6.9.0" } }, + "node_modules/@babel/helper-string-parser": { + "version": "7.18.10", + "resolved": "https://registry.npmjs.org/@babel/helper-string-parser/-/helper-string-parser-7.18.10.tgz", + "integrity": "sha512-XtIfWmeNY3i4t7t4D2t02q50HvqHybPqW2ki1kosnvWCwuCMeo81Jf0gwr85jy/neUdg5XDdeFE/80DXiO+njw==", + "dev": true, + "engines": { + "node": ">=6.9.0" + } + }, "node_modules/@babel/helper-validator-identifier": { "version": "7.18.6", "resolved": "https://registry.npmjs.org/@babel/helper-validator-identifier/-/helper-validator-identifier-7.18.6.tgz", @@ -244,14 +253,14 @@ } }, "node_modules/@babel/helpers": { - "version": "7.18.6", - "resolved": "https://registry.npmjs.org/@babel/helpers/-/helpers-7.18.6.tgz", - "integrity": "sha512-vzSiiqbQOghPngUYt/zWGvK3LAsPhz55vc9XNN0xAl2gV4ieShI2OQli5duxWHD+72PZPTKAcfcZDE1Cwc5zsQ==", + "version": "7.18.9", + "resolved": "https://registry.npmjs.org/@babel/helpers/-/helpers-7.18.9.tgz", + "integrity": "sha512-Jf5a+rbrLoR4eNdUmnFu8cN5eNJT6qdTdOg5IHIzq87WwyRw9PwguLFOWYgktN/60IP4fgDUawJvs7PjQIzELQ==", "dev": true, "dependencies": { "@babel/template": "^7.18.6", - "@babel/traverse": "^7.18.6", - "@babel/types": "^7.18.6" + "@babel/traverse": "^7.18.9", + "@babel/types": "^7.18.9" }, "engines": { "node": ">=6.9.0" @@ -343,9 +352,9 @@ } }, "node_modules/@babel/parser": { - "version": "7.18.6", - "resolved": "https://registry.npmjs.org/@babel/parser/-/parser-7.18.6.tgz", - "integrity": "sha512-uQVSa9jJUe/G/304lXspfWVpKpK4euFLgGiMQFOCpM/bgcAdeoHwi/OQz23O9GK2osz26ZiXRRV9aV+Yl1O8tw==", + "version": "7.18.13", + "resolved": "https://registry.npmjs.org/@babel/parser/-/parser-7.18.13.tgz", + "integrity": "sha512-dgXcIfMuQ0kgzLB2b9tRZs7TTFFaGM2AbtA4fJgUUYukzGH4jwsS7hzQHEGs67jdehpm22vkgKwvbU+aEflgwg==", "dev": true, "bin": { "parser": "bin/babel-parser.js" @@ -355,33 +364,33 @@ } }, "node_modules/@babel/template": { - "version": "7.18.6", - "resolved": "https://registry.npmjs.org/@babel/template/-/template-7.18.6.tgz", - "integrity": "sha512-JoDWzPe+wgBsTTgdnIma3iHNFC7YVJoPssVBDjiHfNlyt4YcunDtcDOUmfVDfCK5MfdsaIoX9PkijPhjH3nYUw==", + "version": "7.18.10", + "resolved": "https://registry.npmjs.org/@babel/template/-/template-7.18.10.tgz", + "integrity": "sha512-TI+rCtooWHr3QJ27kJxfjutghu44DLnasDMwpDqCXVTal9RLp3RSYNh4NdBrRP2cQAoG9A8juOQl6P6oZG4JxA==", "dev": true, "dependencies": { "@babel/code-frame": "^7.18.6", - "@babel/parser": "^7.18.6", - "@babel/types": "^7.18.6" + "@babel/parser": "^7.18.10", + "@babel/types": "^7.18.10" }, "engines": { "node": ">=6.9.0" } }, "node_modules/@babel/traverse": { - "version": "7.18.6", - "resolved": "https://registry.npmjs.org/@babel/traverse/-/traverse-7.18.6.tgz", - "integrity": "sha512-zS/OKyqmD7lslOtFqbscH6gMLFYOfG1YPqCKfAW5KrTeolKqvB8UelR49Fpr6y93kYkW2Ik00mT1LOGiAGvizw==", + "version": "7.18.13", + "resolved": "https://registry.npmjs.org/@babel/traverse/-/traverse-7.18.13.tgz", + "integrity": "sha512-N6kt9X1jRMLPxxxPYWi7tgvJRH/rtoU+dbKAPDM44RFHiMH8igdsaSBgFeskhSl/kLWLDUvIh1RXCrTmg0/zvA==", "dev": true, "dependencies": { "@babel/code-frame": "^7.18.6", - "@babel/generator": "^7.18.6", - "@babel/helper-environment-visitor": "^7.18.6", - "@babel/helper-function-name": "^7.18.6", + "@babel/generator": "^7.18.13", + "@babel/helper-environment-visitor": "^7.18.9", + "@babel/helper-function-name": "^7.18.9", "@babel/helper-hoist-variables": "^7.18.6", "@babel/helper-split-export-declaration": "^7.18.6", - "@babel/parser": "^7.18.6", - "@babel/types": "^7.18.6", + "@babel/parser": "^7.18.13", + "@babel/types": "^7.18.13", "debug": "^4.1.0", "globals": "^11.1.0" }, @@ -399,11 +408,12 @@ } }, "node_modules/@babel/types": { - "version": "7.18.7", - "resolved": "https://registry.npmjs.org/@babel/types/-/types-7.18.7.tgz", - "integrity": "sha512-QG3yxTcTIBoAcQmkCs+wAPYZhu7Dk9rXKacINfNbdJDNERTbLQbHGyVG8q/YGMPeCJRIhSY0+fTc5+xuh6WPSQ==", + "version": "7.18.13", + "resolved": "https://registry.npmjs.org/@babel/types/-/types-7.18.13.tgz", + "integrity": "sha512-ePqfTihzW0W6XAU+aMw2ykilisStJfDnsejDCXRchCcMJ4O0+8DhPXf2YUbZ6wjBlsEmZwLK/sPweWtu8hcJYQ==", "dev": true, "dependencies": { + "@babel/helper-string-parser": "^7.18.10", "@babel/helper-validator-identifier": "^7.18.6", "to-fast-properties": "^2.0.0" }, @@ -454,9 +464,9 @@ } }, "node_modules/@humanwhocodes/config-array": { - "version": "0.9.5", - "resolved": "https://registry.npmjs.org/@humanwhocodes/config-array/-/config-array-0.9.5.tgz", - "integrity": "sha512-ObyMyWxZiCu/yTisA7uzx81s40xR2fD5Cg/2Kq7G02ajkNubJf6BopgDTmDyc3U7sXpNKM8cYOw7s7Tyr+DnCw==", + "version": "0.10.4", + "resolved": "https://registry.npmjs.org/@humanwhocodes/config-array/-/config-array-0.10.4.tgz", + "integrity": "sha512-mXAIHxZT3Vcpg83opl1wGlVZ9xydbfZO3r5YfRSH6Gpp2J/PfdBP0wbDa2sO6/qRbcalpoevVyW6A/fI6LfeMw==", "dev": true, "dependencies": { "@humanwhocodes/object-schema": "^1.2.1", @@ -467,6 +477,16 @@ "node": ">=10.10.0" } }, + "node_modules/@humanwhocodes/gitignore-to-minimatch": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/@humanwhocodes/gitignore-to-minimatch/-/gitignore-to-minimatch-1.0.2.tgz", + "integrity": "sha512-rSqmMJDdLFUsyxR6FMtD00nfQKKLFb1kv+qBbOVKqErvloEIJLo5bDTJTQNTYgeyp78JsA7u/NPi5jT1GR/MuA==", + "dev": true, + "funding": { + "type": "github", + "url": "https://github.com/sponsors/nzakas" + } + }, "node_modules/@humanwhocodes/object-schema": { "version": "1.2.1", "resolved": "https://registry.npmjs.org/@humanwhocodes/object-schema/-/object-schema-1.2.1.tgz", @@ -564,9 +584,9 @@ } }, "node_modules/@jridgewell/resolve-uri": { - "version": "3.0.8", - "resolved": "https://registry.npmjs.org/@jridgewell/resolve-uri/-/resolve-uri-3.0.8.tgz", - "integrity": "sha512-YK5G9LaddzGbcucK4c8h5tWFmMPBvRZ/uyWmN1/SbBdIvqGUdWGkJ5BAaccgs6XbzVLsqbPJrBSFwKv3kT9i7w==", + "version": "3.1.0", + "resolved": "https://registry.npmjs.org/@jridgewell/resolve-uri/-/resolve-uri-3.1.0.tgz", + "integrity": "sha512-F2msla3tad+Mfht5cJq7LSXcdudKTWCVYUgw6pLFOOHSTtZlj6SWNYAp+AhuqLmWdBO2X5hPrLcu8cVP8fy28w==", "dev": true, "engines": { "node": ">=6.0.0" @@ -588,9 +608,9 @@ "dev": true }, "node_modules/@jridgewell/trace-mapping": { - "version": "0.3.14", - "resolved": "https://registry.npmjs.org/@jridgewell/trace-mapping/-/trace-mapping-0.3.14.tgz", - "integrity": "sha512-bJWEfQ9lPTvm3SneWwRFVLzrh6nhjwqw7TUFFBEMzwvg7t7PCDenf2lDwqo4NQXzdpgBXyFgDWnQA+2vkruksQ==", + "version": "0.3.15", + "resolved": "https://registry.npmjs.org/@jridgewell/trace-mapping/-/trace-mapping-0.3.15.tgz", + "integrity": "sha512-oWZNOULl+UbhsgB51uuZzglikfIKSUBO/M9W2OfEjn7cmqoAiCgmv9lyACTUacZwBz0ITnJ2NqjU8Tx0DHL88g==", "dev": true, "dependencies": { "@jridgewell/resolve-uri": "^3.0.3", @@ -633,67 +653,97 @@ } }, "node_modules/@octokit/auth-token": { - "version": "2.5.0", - "resolved": "https://registry.npmjs.org/@octokit/auth-token/-/auth-token-2.5.0.tgz", - "integrity": "sha512-r5FVUJCOLl19AxiuZD2VRZ/ORjp/4IN98Of6YJoJOkY75CIBuYfmiNHGrDwXr+aLGG55igl9QrxX3hbiXlLb+g==", + "version": "3.0.1", + "resolved": "https://registry.npmjs.org/@octokit/auth-token/-/auth-token-3.0.1.tgz", + "integrity": "sha512-/USkK4cioY209wXRpund6HZzHo9GmjakpV9ycOkpMcMxMk7QVcVFVyCMtzvXYiHsB2crgDgrtNYSELYFBXhhaA==", "dev": true, "dependencies": { - "@octokit/types": "^6.0.3" + "@octokit/types": "^7.0.0" + }, + "engines": { + "node": ">= 14" } }, "node_modules/@octokit/core": { - "version": "3.6.0", - "resolved": "https://registry.npmjs.org/@octokit/core/-/core-3.6.0.tgz", - "integrity": "sha512-7RKRKuA4xTjMhY+eG3jthb3hlZCsOwg3rztWh75Xc+ShDWOfDDATWbeZpAHBNRpm4Tv9WgBMOy1zEJYXG6NJ7Q==", + "version": "4.0.5", + "resolved": "https://registry.npmjs.org/@octokit/core/-/core-4.0.5.tgz", + "integrity": "sha512-4R3HeHTYVHCfzSAi0C6pbGXV8UDI5Rk+k3G7kLVNckswN9mvpOzW9oENfjfH3nEmzg8y3AmKmzs8Sg6pLCeOCA==", "dev": true, "dependencies": { - "@octokit/auth-token": "^2.4.4", - "@octokit/graphql": "^4.5.8", - "@octokit/request": "^5.6.3", - "@octokit/request-error": "^2.0.5", - "@octokit/types": "^6.0.3", + "@octokit/auth-token": "^3.0.0", + "@octokit/graphql": "^5.0.0", + "@octokit/request": "^6.0.0", + "@octokit/request-error": "^3.0.0", + "@octokit/types": "^7.0.0", "before-after-hook": "^2.2.0", "universal-user-agent": "^6.0.0" + }, + "engines": { + "node": ">= 14" } }, "node_modules/@octokit/endpoint": { - "version": "6.0.12", - "resolved": "https://registry.npmjs.org/@octokit/endpoint/-/endpoint-6.0.12.tgz", - "integrity": "sha512-lF3puPwkQWGfkMClXb4k/eUT/nZKQfxinRWJrdZaJO85Dqwo/G0yOC434Jr2ojwafWJMYqFGFa5ms4jJUgujdA==", + "version": "7.0.1", + "resolved": "https://registry.npmjs.org/@octokit/endpoint/-/endpoint-7.0.1.tgz", + "integrity": "sha512-/wTXAJwt0HzJ2IeE4kQXO+mBScfzyCkI0hMtkIaqyXd9zg76OpOfNQfHL9FlaxAV2RsNiOXZibVWloy8EexENg==", "dev": true, "dependencies": { - "@octokit/types": "^6.0.3", + "@octokit/types": "^7.0.0", "is-plain-object": "^5.0.0", "universal-user-agent": "^6.0.0" + }, + "engines": { + "node": ">= 14" } }, "node_modules/@octokit/graphql": { - "version": "4.8.0", - "resolved": "https://registry.npmjs.org/@octokit/graphql/-/graphql-4.8.0.tgz", - "integrity": "sha512-0gv+qLSBLKF0z8TKaSKTsS39scVKF9dbMxJpj3U0vC7wjNWFuIpL/z76Qe2fiuCbDRcJSavkXsVtMS6/dtQQsg==", + "version": "5.0.1", + "resolved": "https://registry.npmjs.org/@octokit/graphql/-/graphql-5.0.1.tgz", + "integrity": "sha512-sxmnewSwAixkP1TrLdE6yRG53eEhHhDTYUykUwdV9x8f91WcbhunIHk9x1PZLALdBZKRPUO2HRcm4kezZ79HoA==", "dev": true, "dependencies": { - "@octokit/request": "^5.6.0", - "@octokit/types": "^6.0.3", + "@octokit/request": "^6.0.0", + "@octokit/types": "^7.0.0", "universal-user-agent": "^6.0.0" + }, + "engines": { + "node": ">= 14" } }, "node_modules/@octokit/openapi-types": { - "version": "12.7.0", - "resolved": "https://registry.npmjs.org/@octokit/openapi-types/-/openapi-types-12.7.0.tgz", - "integrity": "sha512-vWXEPETgvltt9jEYdNtQTM8xnsQ7loEbBaLV26V7Tx8ovoN8P7R3XvhFeWiboqNhlXuBsIg1QI979WElB5mGXw==", + "version": "13.4.0", + "resolved": "https://registry.npmjs.org/@octokit/openapi-types/-/openapi-types-13.4.0.tgz", + "integrity": "sha512-2mVzW0X1+HDO3jF80/+QFZNzJiTefELKbhMu6yaBYbp/1gSMkVDm4rT472gJljTokWUlXaaE63m7WrWENhMDLw==", "dev": true }, "node_modules/@octokit/plugin-paginate-rest": { - "version": "2.21.1", - "resolved": "https://registry.npmjs.org/@octokit/plugin-paginate-rest/-/plugin-paginate-rest-2.21.1.tgz", - "integrity": "sha512-NVNTK63yoTFp07GqISWK+uDfGH1CAPhQXS7LzsJBvaK5W+UlvG549pLZC55FK0FqANVl6q/9ra3SR5c97xF/sw==", + "version": "3.1.0", + "resolved": "https://registry.npmjs.org/@octokit/plugin-paginate-rest/-/plugin-paginate-rest-3.1.0.tgz", + "integrity": "sha512-+cfc40pMzWcLkoDcLb1KXqjX0jTGYXjKuQdFQDc6UAknISJHnZTiBqld6HDwRJvD4DsouDKrWXNbNV0lE/3AXA==", "dev": true, "dependencies": { - "@octokit/types": "^6.38.2" + "@octokit/types": "^6.41.0" + }, + "engines": { + "node": ">= 14" }, "peerDependencies": { - "@octokit/core": ">=2" + "@octokit/core": ">=4" + } + }, + "node_modules/@octokit/plugin-paginate-rest/node_modules/@octokit/openapi-types": { + "version": "12.11.0", + "resolved": "https://registry.npmjs.org/@octokit/openapi-types/-/openapi-types-12.11.0.tgz", + "integrity": "sha512-VsXyi8peyRq9PqIz/tpqiL2w3w80OgVMwBHltTml3LmVvXiphgeqmY9mvBw9Wu7e0QWk/fqD37ux8yP5uVekyQ==", + "dev": true + }, + "node_modules/@octokit/plugin-paginate-rest/node_modules/@octokit/types": { + "version": "6.41.0", + "resolved": "https://registry.npmjs.org/@octokit/types/-/types-6.41.0.tgz", + "integrity": "sha512-eJ2jbzjdijiL3B4PrSQaSjuF2sPEQPVCPzBvTHJD9Nz+9dw2SGH4K4xeQJ77YfTq5bRQ+bD8wT11JbeDPmxmGg==", + "dev": true, + "dependencies": { + "@octokit/openapi-types": "^12.11.0" } }, "node_modules/@octokit/plugin-request-log": { @@ -706,62 +756,94 @@ } }, "node_modules/@octokit/plugin-rest-endpoint-methods": { - "version": "5.16.1", - "resolved": "https://registry.npmjs.org/@octokit/plugin-rest-endpoint-methods/-/plugin-rest-endpoint-methods-5.16.1.tgz", - "integrity": "sha512-RMHD3aJZvOpjR2fGzD2an6eU7LG8MsknhUHvP+wRUnKdbt7eDdhTMLQsZ4xsHZcLNsxPO/K4DDIZPhI2s571Ag==", + "version": "6.3.0", + "resolved": "https://registry.npmjs.org/@octokit/plugin-rest-endpoint-methods/-/plugin-rest-endpoint-methods-6.3.0.tgz", + "integrity": "sha512-qEu2wn6E7hqluZwIEUnDxWROvKjov3zMIAi4H4d7cmKWNMeBprEXZzJe8pE5eStUYC1ysGhD0B7L6IeG1Rfb+g==", "dev": true, "dependencies": { - "@octokit/types": "^6.38.2", + "@octokit/types": "^7.0.0", "deprecation": "^2.3.1" }, + "engines": { + "node": ">= 14" + }, "peerDependencies": { "@octokit/core": ">=3" } }, "node_modules/@octokit/request": { - "version": "5.6.3", - "resolved": "https://registry.npmjs.org/@octokit/request/-/request-5.6.3.tgz", - "integrity": "sha512-bFJl0I1KVc9jYTe9tdGGpAMPy32dLBXXo1dS/YwSCTL/2nd9XeHsY616RE3HPXDVk+a+dBuzyz5YdlXwcDTr2A==", + "version": "6.2.1", + "resolved": "https://registry.npmjs.org/@octokit/request/-/request-6.2.1.tgz", + "integrity": "sha512-gYKRCia3cpajRzDSU+3pt1q2OcuC6PK8PmFIyxZDWCzRXRSIBH8jXjFJ8ZceoygBIm0KsEUg4x1+XcYBz7dHPQ==", "dev": true, "dependencies": { - "@octokit/endpoint": "^6.0.1", - "@octokit/request-error": "^2.1.0", - "@octokit/types": "^6.16.1", + "@octokit/endpoint": "^7.0.0", + "@octokit/request-error": "^3.0.0", + "@octokit/types": "^7.0.0", "is-plain-object": "^5.0.0", "node-fetch": "^2.6.7", "universal-user-agent": "^6.0.0" + }, + "engines": { + "node": ">= 14" } }, "node_modules/@octokit/request-error": { - "version": "2.1.0", - "resolved": "https://registry.npmjs.org/@octokit/request-error/-/request-error-2.1.0.tgz", - "integrity": "sha512-1VIvgXxs9WHSjicsRwq8PlR2LR2x6DwsJAaFgzdi0JfJoGSO8mYI/cHJQ+9FbN21aa+DrgNLnwObmyeSC8Rmpg==", + "version": "3.0.1", + "resolved": "https://registry.npmjs.org/@octokit/request-error/-/request-error-3.0.1.tgz", + "integrity": "sha512-ym4Bp0HTP7F3VFssV88WD1ZyCIRoE8H35pXSKwLeMizcdZAYc/t6N9X9Yr9n6t3aG9IH75XDnZ6UeZph0vHMWQ==", "dev": true, "dependencies": { - "@octokit/types": "^6.0.3", + "@octokit/types": "^7.0.0", "deprecation": "^2.0.0", "once": "^1.4.0" + }, + "engines": { + "node": ">= 14" + } + }, + "node_modules/@octokit/request/node_modules/node-fetch": { + "version": "2.6.7", + "resolved": "https://registry.npmjs.org/node-fetch/-/node-fetch-2.6.7.tgz", + "integrity": "sha512-ZjMPFEfVx5j+y2yF35Kzx5sF7kDzxuDj6ziH4FFbOp87zKDZNx8yExJIb05OGF4Nlt9IHFIMBkRl41VdvcNdbQ==", + "dev": true, + "dependencies": { + "whatwg-url": "^5.0.0" + }, + "engines": { + "node": "4.x || >=6.0.0" + }, + "peerDependencies": { + "encoding": "^0.1.0" + }, + "peerDependenciesMeta": { + "encoding": { + "optional": true + } } }, "node_modules/@octokit/rest": { - "version": "18.12.0", - "resolved": "https://registry.npmjs.org/@octokit/rest/-/rest-18.12.0.tgz", - "integrity": "sha512-gDPiOHlyGavxr72y0guQEhLsemgVjwRePayJ+FcKc2SJqKUbxbkvf5kAZEWA/MKvsfYlQAMVzNJE3ezQcxMJ2Q==", + "version": "19.0.3", + "resolved": "https://registry.npmjs.org/@octokit/rest/-/rest-19.0.3.tgz", + "integrity": "sha512-5arkTsnnRT7/sbI4fqgSJ35KiFaN7zQm0uQiQtivNQLI8RQx8EHwJCajcTUwmaCMNDg7tdCvqAnc7uvHHPxrtQ==", "dev": true, "dependencies": { - "@octokit/core": "^3.5.1", - "@octokit/plugin-paginate-rest": "^2.16.8", + "@octokit/core": "^4.0.0", + "@octokit/plugin-paginate-rest": "^3.0.0", "@octokit/plugin-request-log": "^1.0.4", - "@octokit/plugin-rest-endpoint-methods": "^5.12.0" + "@octokit/plugin-rest-endpoint-methods": "^6.0.0" + }, + "engines": { + "node": ">= 14" } }, "node_modules/@octokit/types": { - "version": "6.39.0", - "resolved": "https://registry.npmjs.org/@octokit/types/-/types-6.39.0.tgz", - "integrity": "sha512-Mq4N9sOAYCitTsBtDdRVrBE80lIrMBhL9Jbrw0d+j96BAzlq4V+GLHFJbHokEsVvO/9tQupQdoFdgVYhD2C8UQ==", + "version": "7.1.1", + "resolved": "https://registry.npmjs.org/@octokit/types/-/types-7.1.1.tgz", + "integrity": "sha512-Dx6cNTORyVaKY0Yeb9MbHksk79L8GXsihbG6PtWqTpkyA2TY1qBWE26EQXVG3dHwY9Femdd/WEeRUEiD0+H3TQ==", "dev": true, "dependencies": { - "@octokit/openapi-types": "^12.7.0" + "@octokit/openapi-types": "^13.4.0" } }, "node_modules/@pnpm/network.ca-file": { @@ -777,9 +859,9 @@ } }, "node_modules/@pnpm/npm-conf": { - "version": "1.0.4", - "resolved": "https://registry.npmjs.org/@pnpm/npm-conf/-/npm-conf-1.0.4.tgz", - "integrity": "sha512-o5YFq/+ksEJMbSzzkaQDHlp00aonLDU5xNPVTRL12hTWBbVSSeWXxPukq75h+mvXnoOWT95vV2u1HSTw2C4XOw==", + "version": "1.0.5", + "resolved": "https://registry.npmjs.org/@pnpm/npm-conf/-/npm-conf-1.0.5.tgz", + "integrity": "sha512-hD8ml183638O3R6/Txrh0L8VzGOrFXgRtRDG4qQC4tONdZ5Z1M+tlUUDUvrjYdmK6G+JTBTeaCLMna11cXzi8A==", "dev": true, "dependencies": { "@pnpm/network.ca-file": "^1.0.1", @@ -818,12 +900,12 @@ "link": true }, "node_modules/@sindresorhus/is": { - "version": "4.6.0", - "resolved": "https://registry.npmjs.org/@sindresorhus/is/-/is-4.6.0.tgz", - "integrity": "sha512-t09vSN3MdfsyCHoFcTRCH/iUtG7OJ0CsjzB8cjAmKc/va/kIgeDI/TxsigdncE/4be734m0cvIYwNaV4i2XqAw==", + "version": "5.3.0", + "resolved": "https://registry.npmjs.org/@sindresorhus/is/-/is-5.3.0.tgz", + "integrity": "sha512-CX6t4SYQ37lzxicAqsBtxA3OseeoVrh9cSJ5PFYam0GksYlupRfy1A+Q4aYD3zvcfECLc0zO2u+ZnR2UYKvCrw==", "dev": true, "engines": { - "node": ">=10" + "node": ">=14.16" }, "funding": { "url": "https://github.com/sindresorhus/is?sponsor=1" @@ -859,9 +941,9 @@ } }, "node_modules/@sinonjs/text-encoding": { - "version": "0.7.1", - "resolved": "https://registry.npmjs.org/@sinonjs/text-encoding/-/text-encoding-0.7.1.tgz", - "integrity": "sha512-+iTbntw2IZPb/anVDbypzfQa+ay64MW0Zo8aJ8gZPWMMK6/OubMVb6lUPMagqjOPnmtauXnFCACVl3O7ogjeqQ==", + "version": "0.7.2", + "resolved": "https://registry.npmjs.org/@sinonjs/text-encoding/-/text-encoding-0.7.2.tgz", + "integrity": "sha512-sXXKG+uL9IrKqViTtao2Ws6dy0znu9sOaP1di/jKGW1M6VssO8vlpXCQcpZ+jisQ1tTFAC5Jo/EOzFbggBagFQ==", "dev": true }, "node_modules/@szmarczak/http-timer": { @@ -955,9 +1037,9 @@ "dev": true }, "node_modules/@types/node": { - "version": "18.0.1", - "resolved": "https://registry.npmjs.org/@types/node/-/node-18.0.1.tgz", - "integrity": "sha512-CmR8+Tsy95hhwtZBKJBs0/FFq4XX7sDZHlGGf+0q+BRZfMbOTkzkj0AFAuTyXbObDIoanaBBW0+KEW+m3N16Wg==", + "version": "18.7.10", + "resolved": "https://registry.npmjs.org/@types/node/-/node-18.7.10.tgz", + "integrity": "sha512-SST7B//wF7xlGoLo1IEVB0cQ4e7BgXlDk5IaPgb5s0DlYLjb4if07h8+0zbQIvahfPNXs6e7zyT0EH1MqaS+5g==", "dev": true }, "node_modules/@types/parse-json": { @@ -976,9 +1058,9 @@ } }, "node_modules/@types/sinon": { - "version": "10.0.12", - "resolved": "https://registry.npmjs.org/@types/sinon/-/sinon-10.0.12.tgz", - "integrity": "sha512-uWf4QJ4oky/GckJ1MYQxU52cgVDcXwBhDkpvLbi4EKoLPqLE4MOH6T/ttM33l3hi0oZ882G6oIzWv/oupRYSxQ==", + "version": "10.0.13", + "resolved": "https://registry.npmjs.org/@types/sinon/-/sinon-10.0.13.tgz", + "integrity": "sha512-UVjDqJblVNQYvVNUsj0PuYYw0ELRmgt1Nt5Vk0pT5f16ROGfcKJY8o1HVuMOJOpD727RrGB9EGvoaTQE5tgxZQ==", "dev": true, "dependencies": { "@types/sinonjs__fake-timers": "*" @@ -997,9 +1079,9 @@ "dev": true }, "node_modules/@types/yargs": { - "version": "17.0.10", - "resolved": "https://registry.npmjs.org/@types/yargs/-/yargs-17.0.10.tgz", - "integrity": "sha512-gmEaFwpj/7f/ROdtIlci1R1VYU1J4j95m8T+Tj3iBgiBFKg1foE/PSl93bBd5T9LDXNPo8UlNN6W0qwD8O5OaA==", + "version": "17.0.11", + "resolved": "https://registry.npmjs.org/@types/yargs/-/yargs-17.0.11.tgz", + "integrity": "sha512-aB4y9UDUXTSMxmM4MH+YnuR0g5Cph3FLQBoWoMB21DSvFVAxRVEHEMx3TLh+zUZYMCQtKiqazz0Q4Rre31f/OA==", "dev": true, "dependencies": { "@types/yargs-parser": "*" @@ -1012,14 +1094,14 @@ "dev": true }, "node_modules/@typescript-eslint/eslint-plugin": { - "version": "5.30.5", - "resolved": "https://registry.npmjs.org/@typescript-eslint/eslint-plugin/-/eslint-plugin-5.30.5.tgz", - "integrity": "sha512-lftkqRoBvc28VFXEoRgyZuztyVUQ04JvUnATSPtIRFAccbXTWL6DEtXGYMcbg998kXw1NLUJm7rTQ9eUt+q6Ig==", + "version": "5.34.0", + "resolved": "https://registry.npmjs.org/@typescript-eslint/eslint-plugin/-/eslint-plugin-5.34.0.tgz", + "integrity": "sha512-eRfPPcasO39iwjlUAMtjeueRGuIrW3TQ9WseIDl7i5UWuFbf83yYaU7YPs4j8+4CxUMIsj1k+4kV+E+G+6ypDQ==", "dev": true, "dependencies": { - "@typescript-eslint/scope-manager": "5.30.5", - "@typescript-eslint/type-utils": "5.30.5", - "@typescript-eslint/utils": "5.30.5", + "@typescript-eslint/scope-manager": "5.34.0", + "@typescript-eslint/type-utils": "5.34.0", + "@typescript-eslint/utils": "5.34.0", "debug": "^4.3.4", "functional-red-black-tree": "^1.0.1", "ignore": "^5.2.0", @@ -1078,14 +1160,14 @@ "dev": true }, "node_modules/@typescript-eslint/parser": { - "version": "5.30.5", - "resolved": "https://registry.npmjs.org/@typescript-eslint/parser/-/parser-5.30.5.tgz", - "integrity": "sha512-zj251pcPXI8GO9NDKWWmygP6+UjwWmrdf9qMW/L/uQJBM/0XbU2inxe5io/234y/RCvwpKEYjZ6c1YrXERkK4Q==", + "version": "5.34.0", + "resolved": "https://registry.npmjs.org/@typescript-eslint/parser/-/parser-5.34.0.tgz", + "integrity": "sha512-SZ3NEnK4usd2CXkoV3jPa/vo1mWX1fqRyIVUQZR4As1vyp4fneknBNJj+OFtV8WAVgGf+rOHMSqQbs2Qn3nFZQ==", "dev": true, "dependencies": { - "@typescript-eslint/scope-manager": "5.30.5", - "@typescript-eslint/types": "5.30.5", - "@typescript-eslint/typescript-estree": "5.30.5", + "@typescript-eslint/scope-manager": "5.34.0", + "@typescript-eslint/types": "5.34.0", + "@typescript-eslint/typescript-estree": "5.34.0", "debug": "^4.3.4" }, "engines": { @@ -1105,13 +1187,13 @@ } }, "node_modules/@typescript-eslint/scope-manager": { - "version": "5.30.5", - "resolved": "https://registry.npmjs.org/@typescript-eslint/scope-manager/-/scope-manager-5.30.5.tgz", - "integrity": "sha512-NJ6F+YHHFT/30isRe2UTmIGGAiXKckCyMnIV58cE3JkHmaD6e5zyEYm5hBDv0Wbin+IC0T1FWJpD3YqHUG/Ydg==", + "version": "5.34.0", + "resolved": "https://registry.npmjs.org/@typescript-eslint/scope-manager/-/scope-manager-5.34.0.tgz", + "integrity": "sha512-HNvASMQlah5RsBW6L6c7IJ0vsm+8Sope/wu5sEAf7joJYWNb1LDbJipzmdhdUOnfrDFE6LR1j57x1EYVxrY4ow==", "dev": true, "dependencies": { - "@typescript-eslint/types": "5.30.5", - "@typescript-eslint/visitor-keys": "5.30.5" + "@typescript-eslint/types": "5.34.0", + "@typescript-eslint/visitor-keys": "5.34.0" }, "engines": { "node": "^12.22.0 || ^14.17.0 || >=16.0.0" @@ -1122,12 +1204,12 @@ } }, "node_modules/@typescript-eslint/type-utils": { - "version": "5.30.5", - "resolved": "https://registry.npmjs.org/@typescript-eslint/type-utils/-/type-utils-5.30.5.tgz", - "integrity": "sha512-k9+ejlv1GgwN1nN7XjVtyCgE0BTzhzT1YsQF0rv4Vfj2U9xnslBgMYYvcEYAFVdvhuEscELJsB7lDkN7WusErw==", + "version": "5.34.0", + "resolved": "https://registry.npmjs.org/@typescript-eslint/type-utils/-/type-utils-5.34.0.tgz", + "integrity": "sha512-Pxlno9bjsQ7hs1pdWRUv9aJijGYPYsHpwMeCQ/Inavhym3/XaKt1ZKAA8FIw4odTBfowBdZJDMxf2aavyMDkLg==", "dev": true, "dependencies": { - "@typescript-eslint/utils": "5.30.5", + "@typescript-eslint/utils": "5.34.0", "debug": "^4.3.4", "tsutils": "^3.21.0" }, @@ -1148,9 +1230,9 @@ } }, "node_modules/@typescript-eslint/types": { - "version": "5.30.5", - "resolved": "https://registry.npmjs.org/@typescript-eslint/types/-/types-5.30.5.tgz", - "integrity": "sha512-kZ80w/M2AvsbRvOr3PjaNh6qEW1LFqs2pLdo2s5R38B2HYXG8Z0PP48/4+j1QHJFL3ssHIbJ4odPRS8PlHrFfw==", + "version": "5.34.0", + "resolved": "https://registry.npmjs.org/@typescript-eslint/types/-/types-5.34.0.tgz", + "integrity": "sha512-49fm3xbbUPuzBIOcy2CDpYWqy/X7VBkxVN+DC21e0zIm3+61Z0NZi6J9mqPmSW1BDVk9FIOvuCFyUPjXz93sjA==", "dev": true, "engines": { "node": "^12.22.0 || ^14.17.0 || >=16.0.0" @@ -1161,13 +1243,13 @@ } }, "node_modules/@typescript-eslint/typescript-estree": { - "version": "5.30.5", - "resolved": "https://registry.npmjs.org/@typescript-eslint/typescript-estree/-/typescript-estree-5.30.5.tgz", - "integrity": "sha512-qGTc7QZC801kbYjAr4AgdOfnokpwStqyhSbiQvqGBLixniAKyH+ib2qXIVo4P9NgGzwyfD9I0nlJN7D91E1VpQ==", + "version": "5.34.0", + "resolved": "https://registry.npmjs.org/@typescript-eslint/typescript-estree/-/typescript-estree-5.34.0.tgz", + "integrity": "sha512-mXHAqapJJDVzxauEkfJI96j3D10sd567LlqroyCeJaHnu42sDbjxotGb3XFtGPYKPD9IyLjhsoULML1oI3M86A==", "dev": true, "dependencies": { - "@typescript-eslint/types": "5.30.5", - "@typescript-eslint/visitor-keys": "5.30.5", + "@typescript-eslint/types": "5.34.0", + "@typescript-eslint/visitor-keys": "5.34.0", "debug": "^4.3.4", "globby": "^11.1.0", "is-glob": "^4.0.3", @@ -1250,15 +1332,15 @@ "dev": true }, "node_modules/@typescript-eslint/utils": { - "version": "5.30.5", - "resolved": "https://registry.npmjs.org/@typescript-eslint/utils/-/utils-5.30.5.tgz", - "integrity": "sha512-o4SSUH9IkuA7AYIfAvatldovurqTAHrfzPApOZvdUq01hHojZojCFXx06D/aFpKCgWbMPRdJBWAC3sWp3itwTA==", + "version": "5.34.0", + "resolved": "https://registry.npmjs.org/@typescript-eslint/utils/-/utils-5.34.0.tgz", + "integrity": "sha512-kWRYybU4Rn++7lm9yu8pbuydRyQsHRoBDIo11k7eqBWTldN4xUdVUMCsHBiE7aoEkFzrUEaZy3iH477vr4xHAQ==", "dev": true, "dependencies": { "@types/json-schema": "^7.0.9", - "@typescript-eslint/scope-manager": "5.30.5", - "@typescript-eslint/types": "5.30.5", - "@typescript-eslint/typescript-estree": "5.30.5", + "@typescript-eslint/scope-manager": "5.34.0", + "@typescript-eslint/types": "5.34.0", + "@typescript-eslint/typescript-estree": "5.34.0", "eslint-scope": "^5.1.1", "eslint-utils": "^3.0.0" }, @@ -1274,12 +1356,12 @@ } }, "node_modules/@typescript-eslint/visitor-keys": { - "version": "5.30.5", - "resolved": "https://registry.npmjs.org/@typescript-eslint/visitor-keys/-/visitor-keys-5.30.5.tgz", - "integrity": "sha512-D+xtGo9HUMELzWIUqcQc0p2PO4NyvTrgIOK/VnSH083+8sq0tiLozNRKuLarwHYGRuA6TVBQSuuLwJUDWd3aaA==", + "version": "5.34.0", + "resolved": "https://registry.npmjs.org/@typescript-eslint/visitor-keys/-/visitor-keys-5.34.0.tgz", + "integrity": "sha512-O1moYjOSrab0a2fUvFpsJe0QHtvTC+cR+ovYpgKrAVXzqQyc74mv76TgY6z+aEtjQE2vgZux3CQVtGryqdcOAw==", "dev": true, "dependencies": { - "@typescript-eslint/types": "5.30.5", + "@typescript-eslint/types": "5.34.0", "eslint-visitor-keys": "^3.3.0" }, "engines": { @@ -1297,9 +1379,9 @@ "dev": true }, "node_modules/acorn": { - "version": "8.7.1", - "resolved": "https://registry.npmjs.org/acorn/-/acorn-8.7.1.tgz", - "integrity": "sha512-Xx54uLJQZ19lKygFXOWsscKUbsBZW0CPykPhVQdhIeIwrbPmJzqeASDInc8nKBnp/JT6igTs82qPXz069H8I/A==", + "version": "8.8.0", + "resolved": "https://registry.npmjs.org/acorn/-/acorn-8.8.0.tgz", + "integrity": "sha512-QOxyigPVrpZ2GXT+PFyZTl6TtOFc5egxHIP9IlQ+RbupQuX4RkT/Bee4/kQuC02Xkzg84JcT7oLYtDIQxp+v7w==", "dev": true, "bin": { "acorn": "bin/acorn" @@ -1671,9 +1753,9 @@ } }, "node_modules/boxen/node_modules/type-fest": { - "version": "2.16.0", - "resolved": "https://registry.npmjs.org/type-fest/-/type-fest-2.16.0.tgz", - "integrity": "sha512-qpaThT2HQkFb83gMOrdKVsfCN7LKxP26Yq+smPzY1FqoHRjqmjqHXA7n5Gkxi8efirtbeEUxzfEdePthQWCuHw==", + "version": "2.19.0", + "resolved": "https://registry.npmjs.org/type-fest/-/type-fest-2.19.0.tgz", + "integrity": "sha512-RAH822pAdBgcNMAfWnCBU3CFZcfZ/i1eZjwFU/dsLKumyuuP3niueg2UAukXYF0E2AAoc82ZSSf9J0WQBinzHA==", "dev": true, "engines": { "node": ">=12.20" @@ -1711,9 +1793,9 @@ "dev": true }, "node_modules/browserslist": { - "version": "4.21.1", - "resolved": "https://registry.npmjs.org/browserslist/-/browserslist-4.21.1.tgz", - "integrity": "sha512-Nq8MFCSrnJXSc88yliwlzQe3qNe3VntIjhsArW9IJOEPSHNx23FalwApUVbzAWABLhYJJ7y8AynWI/XM8OdfjQ==", + "version": "4.21.3", + "resolved": "https://registry.npmjs.org/browserslist/-/browserslist-4.21.3.tgz", + "integrity": "sha512-898rgRXLAyRkM1GryrrBHGkqA5hlpkV5MhtZwg9QXeiyLUYs2k00Un05aX5l2/yJIOObYKOpS2JNo8nJDE7fWQ==", "dev": true, "funding": [ { @@ -1726,10 +1808,10 @@ } ], "dependencies": { - "caniuse-lite": "^1.0.30001359", - "electron-to-chromium": "^1.4.172", - "node-releases": "^2.0.5", - "update-browserslist-db": "^1.0.4" + "caniuse-lite": "^1.0.30001370", + "electron-to-chromium": "^1.4.202", + "node-releases": "^2.0.6", + "update-browserslist-db": "^1.0.5" }, "bin": { "browserslist": "cli.js" @@ -1778,9 +1860,9 @@ } }, "node_modules/cacheable-lookup": { - "version": "6.0.4", - "resolved": "https://registry.npmjs.org/cacheable-lookup/-/cacheable-lookup-6.0.4.tgz", - "integrity": "sha512-mbcDEZCkv2CZF4G01kr8eBd/5agkt9oCqz75tJMSIsquvRZ2sL6Hi5zGVKi/0OSC9oO1GHfJ2AV0ZIOY9vye0A==", + "version": "6.1.0", + "resolved": "https://registry.npmjs.org/cacheable-lookup/-/cacheable-lookup-6.1.0.tgz", + "integrity": "sha512-KJ/Dmo1lDDhmW2XDPMo+9oiy/CeqosPguPCrgcVzKyZrL6pM1gU2GmPY/xo6OQPTUaA/c0kwHuywB4E6nmT9ww==", "dev": true, "engines": { "node": ">=10.6.0" @@ -1875,9 +1957,9 @@ } }, "node_modules/caniuse-lite": { - "version": "1.0.30001363", - "resolved": "https://registry.npmjs.org/caniuse-lite/-/caniuse-lite-1.0.30001363.tgz", - "integrity": "sha512-HpQhpzTGGPVMnCjIomjt+jvyUu8vNFo3TaDiZ/RcoTrlOq/5+tC8zHdsbgFB6MxmaY+jCpsH09aD80Bb4Ow3Sg==", + "version": "1.0.30001381", + "resolved": "https://registry.npmjs.org/caniuse-lite/-/caniuse-lite-1.0.30001381.tgz", + "integrity": "sha512-fEnkDOKpvp6qc+olg7+NzE1SqyfiyKf4uci7fAU38M3zxs0YOyKOxW/nMZ2l9sJbt7KZHcDIxUnbI0Iime7V4w==", "dev": true, "funding": [ { @@ -1990,9 +2072,9 @@ } }, "node_modules/cli-spinners": { - "version": "2.6.1", - "resolved": "https://registry.npmjs.org/cli-spinners/-/cli-spinners-2.6.1.tgz", - "integrity": "sha512-x/5fWmGMnbKQAaNwN+UZlV79qBLM9JFnJuJ03gIi5whrob0xV0ofNVHy9DhwGdsMJQc2OKv0oGmLzvaqvAVv+g==", + "version": "2.7.0", + "resolved": "https://registry.npmjs.org/cli-spinners/-/cli-spinners-2.7.0.tgz", + "integrity": "sha512-qu3pN8Y3qHNgE2AFweciB1IfMnmZ/fsNTEE+NOFjmGB2F/7rLhnhzppvpCnN4FovtP26k8lHyy9ptEbNwWFLzw==", "dev": true, "engines": { "node": ">=6" @@ -2101,12 +2183,15 @@ } }, "node_modules/clone-response": { - "version": "1.0.2", - "resolved": "https://registry.npmjs.org/clone-response/-/clone-response-1.0.2.tgz", - "integrity": "sha512-yjLXh88P599UOyPTFX0POsd7WxnbsVsGohcwzHOLspIhhpalPw1BcqED8NblyZLKcGrL8dTgMlcaZxV2jAD41Q==", + "version": "1.0.3", + "resolved": "https://registry.npmjs.org/clone-response/-/clone-response-1.0.3.tgz", + "integrity": "sha512-ROoL94jJH2dUVML2Y/5PEDNaSHgeOdSDicUyS7izcF63G6sTc/FTjLub4b8Il9S8S0beOfYt0TaA5qvFK+w0wA==", "dev": true, "dependencies": { "mimic-response": "^1.0.0" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" } }, "node_modules/cluster-key-slot": { @@ -2280,12 +2365,12 @@ } }, "node_modules/data-uri-to-buffer": { - "version": "3.0.1", - "resolved": "https://registry.npmjs.org/data-uri-to-buffer/-/data-uri-to-buffer-3.0.1.tgz", - "integrity": "sha512-WboRycPNsVw3B3TL559F7kuBUM4d8CgMEvk6xEJlOp7OBPjt6G7z8WMWlD2rOFZLk6OYfFIUGsCOWzcQH9K2og==", + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/data-uri-to-buffer/-/data-uri-to-buffer-4.0.0.tgz", + "integrity": "sha512-Vr3mLBA8qWmcuschSLAOogKgQ/Jwxulv3RNE4FXnYWRGujzrRWQI4m12fQqRkwX06C0KanhLr4hK+GydchZsaA==", "dev": true, "engines": { - "node": ">= 6" + "node": ">= 12" } }, "node_modules/debug": { @@ -2314,15 +2399,6 @@ "node": ">=0.10.0" } }, - "node_modules/decode-uri-component": { - "version": "0.2.0", - "resolved": "https://registry.npmjs.org/decode-uri-component/-/decode-uri-component-0.2.0.tgz", - "integrity": "sha512-hjf+xovcEn31w/EUYdTXQh/8smFL/dzYjohQGEIgjyNavaJfBY2p5F527Bo1VPATxv0VYTUC2bOcXvqFwk78Og==", - "dev": true, - "engines": { - "node": ">=0.10" - } - }, "node_modules/decompress-response": { "version": "6.0.0", "resolved": "https://registry.npmjs.org/decompress-response/-/decompress-response-6.0.0.tgz", @@ -2514,9 +2590,9 @@ "dev": true }, "node_modules/electron-to-chromium": { - "version": "1.4.179", - "resolved": "https://registry.npmjs.org/electron-to-chromium/-/electron-to-chromium-1.4.179.tgz", - "integrity": "sha512-1XeTb/U/8Xgh2YgPOqhakLYsvCcU4U7jUjTMbEnhIJoIWd/Qt3yC8y0cbG+fHzn4zUNF99Ey1xiPf20bwgLO3Q==", + "version": "1.4.225", + "resolved": "https://registry.npmjs.org/electron-to-chromium/-/electron-to-chromium-1.4.225.tgz", + "integrity": "sha512-ICHvGaCIQR3P88uK8aRtx8gmejbVJyC6bB4LEC3anzBrIzdzC7aiZHY4iFfXhN4st6I7lMO0x4sgBHf/7kBvRw==", "dev": true }, "node_modules/email-addresses": { @@ -2741,13 +2817,14 @@ } }, "node_modules/eslint": { - "version": "8.19.0", - "resolved": "https://registry.npmjs.org/eslint/-/eslint-8.19.0.tgz", - "integrity": "sha512-SXOPj3x9VKvPe81TjjUJCYlV4oJjQw68Uek+AM0X4p+33dj2HY5bpTZOgnQHcG2eAm1mtCU9uNMnJi7exU/kYw==", + "version": "8.22.0", + "resolved": "https://registry.npmjs.org/eslint/-/eslint-8.22.0.tgz", + "integrity": "sha512-ci4t0sz6vSRKdmkOGmprBo6fmI4PrphDFMy5JEq/fNS0gQkJM3rLmrqcp8ipMcdobH3KtUP40KniAE9W19S4wA==", "dev": true, "dependencies": { "@eslint/eslintrc": "^1.3.0", - "@humanwhocodes/config-array": "^0.9.2", + "@humanwhocodes/config-array": "^0.10.4", + "@humanwhocodes/gitignore-to-minimatch": "^1.0.2", "ajv": "^6.10.0", "chalk": "^4.0.0", "cross-spawn": "^7.0.2", @@ -2757,14 +2834,17 @@ "eslint-scope": "^7.1.1", "eslint-utils": "^3.0.0", "eslint-visitor-keys": "^3.3.0", - "espree": "^9.3.2", + "espree": "^9.3.3", "esquery": "^1.4.0", "esutils": "^2.0.2", "fast-deep-equal": "^3.1.3", "file-entry-cache": "^6.0.1", + "find-up": "^5.0.0", "functional-red-black-tree": "^1.0.1", "glob-parent": "^6.0.1", "globals": "^13.15.0", + "globby": "^11.1.0", + "grapheme-splitter": "^1.0.4", "ignore": "^5.2.0", "import-fresh": "^3.0.0", "imurmurhash": "^0.1.4", @@ -2865,6 +2945,15 @@ "url": "https://github.com/chalk/ansi-styles?sponsor=1" } }, + "node_modules/eslint/node_modules/array-union": { + "version": "2.1.0", + "resolved": "https://registry.npmjs.org/array-union/-/array-union-2.1.0.tgz", + "integrity": "sha512-HGyxoOTYUyCM6stUe6EJgnd4EoewAI7zMdfqO+kGjnlZmBDz/cR5pf8r/cR4Wq60sL/p0IkcjUEEPwS3GFrIyw==", + "dev": true, + "engines": { + "node": ">=8" + } + }, "node_modules/eslint/node_modules/chalk": { "version": "4.1.2", "resolved": "https://registry.npmjs.org/chalk/-/chalk-4.1.2.tgz", @@ -2915,6 +3004,87 @@ "node": ">=4.0" } }, + "node_modules/eslint/node_modules/find-up": { + "version": "5.0.0", + "resolved": "https://registry.npmjs.org/find-up/-/find-up-5.0.0.tgz", + "integrity": "sha512-78/PXT1wlLLDgTzDs7sjq9hzz0vXD+zn+7wypEe4fXQxCmdmqfGsEPQxmiCSQI3ajFV91bVSsvNtrJRiW6nGng==", + "dev": true, + "dependencies": { + "locate-path": "^6.0.0", + "path-exists": "^4.0.0" + }, + "engines": { + "node": ">=10" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/eslint/node_modules/globby": { + "version": "11.1.0", + "resolved": "https://registry.npmjs.org/globby/-/globby-11.1.0.tgz", + "integrity": "sha512-jhIXaOzy1sb8IyocaruWSn1TjmnBVs8Ayhcy83rmxNJ8q2uWKCAj3CnJY+KpGSXCueAPc0i05kVvVKtP1t9S3g==", + "dev": true, + "dependencies": { + "array-union": "^2.1.0", + "dir-glob": "^3.0.1", + "fast-glob": "^3.2.9", + "ignore": "^5.2.0", + "merge2": "^1.4.1", + "slash": "^3.0.0" + }, + "engines": { + "node": ">=10" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/eslint/node_modules/locate-path": { + "version": "6.0.0", + "resolved": "https://registry.npmjs.org/locate-path/-/locate-path-6.0.0.tgz", + "integrity": "sha512-iPZK6eYjbxRu3uB4/WZ3EsEIMJFMqAoopl3R+zuq0UjcAm/MO6KCweDgPfP3elTztoKP3KtnVHxTn2NHBSDVUw==", + "dev": true, + "dependencies": { + "p-locate": "^5.0.0" + }, + "engines": { + "node": ">=10" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/eslint/node_modules/p-limit": { + "version": "3.1.0", + "resolved": "https://registry.npmjs.org/p-limit/-/p-limit-3.1.0.tgz", + "integrity": "sha512-TYOanM3wGwNGsZN2cVTYPArw454xnXj5qmWF1bEoAc4+cU/ol7GVh7odevjp1FNHduHc3KZMcFduxU5Xc6uJRQ==", + "dev": true, + "dependencies": { + "yocto-queue": "^0.1.0" + }, + "engines": { + "node": ">=10" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/eslint/node_modules/p-locate": { + "version": "5.0.0", + "resolved": "https://registry.npmjs.org/p-locate/-/p-locate-5.0.0.tgz", + "integrity": "sha512-LaNjtRWUBY++zB5nE/NwcaoMylSPk+S+ZHNB1TzdbMJMny6dynpAGt7X/tl/QYq3TIeE6nxHppbo2LGymrG5Pw==", + "dev": true, + "dependencies": { + "p-limit": "^3.0.2" + }, + "engines": { + "node": ">=10" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, "node_modules/eslint/node_modules/strip-ansi": { "version": "6.0.1", "resolved": "https://registry.npmjs.org/strip-ansi/-/strip-ansi-6.0.1.tgz", @@ -2928,17 +3098,20 @@ } }, "node_modules/espree": { - "version": "9.3.2", - "resolved": "https://registry.npmjs.org/espree/-/espree-9.3.2.tgz", - "integrity": "sha512-D211tC7ZwouTIuY5x9XnS0E9sWNChB7IYKX/Xp5eQj3nFXhqmiUDB9q27y76oFl8jTg3pXcQx/bpxMfs3CIZbA==", + "version": "9.3.3", + "resolved": "https://registry.npmjs.org/espree/-/espree-9.3.3.tgz", + "integrity": "sha512-ORs1Rt/uQTqUKjDdGCyrtYxbazf5umATSf/K4qxjmZHORR6HJk+2s/2Pqe+Kk49HHINC/xNIrGfgh8sZcll0ng==", "dev": true, "dependencies": { - "acorn": "^8.7.1", + "acorn": "^8.8.0", "acorn-jsx": "^5.3.2", "eslint-visitor-keys": "^3.3.0" }, "engines": { "node": "^12.22.0 || ^14.17.0 || >=16.0.0" + }, + "funding": { + "url": "https://opencollective.com/eslint" } }, "node_modules/esprima": { @@ -3106,6 +3279,29 @@ "reusify": "^1.0.4" } }, + "node_modules/fetch-blob": { + "version": "3.2.0", + "resolved": "https://registry.npmjs.org/fetch-blob/-/fetch-blob-3.2.0.tgz", + "integrity": "sha512-7yAQpD2UMJzLi1Dqv7qFYnPbaPx7ZfFK6PiIxQ4PfkGPyNyl2Ugx+a/umUonmKqjhM4DnfbMvdX6otXq83soQQ==", + "dev": true, + "funding": [ + { + "type": "github", + "url": "https://github.com/sponsors/jimmywarting" + }, + { + "type": "paypal", + "url": "https://paypal.me/jimmywarting" + } + ], + "dependencies": { + "node-domexception": "^1.0.0", + "web-streams-polyfill": "^3.0.3" + }, + "engines": { + "node": "^12.20 || >= 14.13" + } + }, "node_modules/figures": { "version": "4.0.1", "resolved": "https://registry.npmjs.org/figures/-/figures-4.0.1.tgz", @@ -3181,15 +3377,6 @@ "node": ">=8" } }, - "node_modules/filter-obj": { - "version": "1.1.0", - "resolved": "https://registry.npmjs.org/filter-obj/-/filter-obj-1.1.0.tgz", - "integrity": "sha512-8rXg1ZnX7xzy2NGDVkBVaAy+lSlPNwad13BtgSlLuxfIslyt5Vg64U7tFcCt4WS1R0hvtnQybT/IyCkGZ3DpXQ==", - "dev": true, - "engines": { - "node": ">=0.10.0" - } - }, "node_modules/find-cache-dir": { "version": "3.3.2", "resolved": "https://registry.npmjs.org/find-cache-dir/-/find-cache-dir-3.3.2.tgz", @@ -3243,9 +3430,9 @@ } }, "node_modules/flatted": { - "version": "3.2.6", - "resolved": "https://registry.npmjs.org/flatted/-/flatted-3.2.6.tgz", - "integrity": "sha512-0sQoMh9s0BYsm+12Huy/rkKxVu4R1+r96YX5cG44rHV0pQ6iC3Q+mkoMFaGWObMFYQxCVT+ssG1ksneA2MI9KQ==", + "version": "3.2.7", + "resolved": "https://registry.npmjs.org/flatted/-/flatted-3.2.7.tgz", + "integrity": "sha512-5nqDSxl8nn5BSNxyR3n4I6eDmbolI6WT+QqR547RwxQapgjQBmtktdP+HTBb/a/zLsbzERTONyUB5pefh5TtjQ==", "dev": true }, "node_modules/foreground-child": { @@ -3276,10 +3463,25 @@ } }, "node_modules/form-data-encoder": { - "version": "1.7.1", - "resolved": "https://registry.npmjs.org/form-data-encoder/-/form-data-encoder-1.7.1.tgz", - "integrity": "sha512-EFRDrsMm/kyqbTQocNvRXMLjc7Es2Vk+IQFx/YW7hkUH1eBl4J1fqiP34l74Yt0pFLCNpc06fkbVk00008mzjg==", - "dev": true + "version": "2.1.0", + "resolved": "https://registry.npmjs.org/form-data-encoder/-/form-data-encoder-2.1.0.tgz", + "integrity": "sha512-njK60LnfhfDWy+AEUIf9ZQNRAcmXCdDfiNOm2emuPtzwh7U9k/mo9F3S54aPiaZ3vhqUjikVLfcPg2KuBddskQ==", + "dev": true, + "engines": { + "node": ">= 14.17" + } + }, + "node_modules/formdata-polyfill": { + "version": "4.0.10", + "resolved": "https://registry.npmjs.org/formdata-polyfill/-/formdata-polyfill-4.0.10.tgz", + "integrity": "sha512-buewHzMvYL29jdeQTVILecSaZKnt/RJWjoZCF5OW60Z67/GmSLBkOFM7qh1PI3zFNtJbaZL5eQu1vLfazOwj4g==", + "dev": true, + "dependencies": { + "fetch-blob": "^3.1.2" + }, + "engines": { + "node": ">=12.20.0" + } }, "node_modules/fromentries": { "version": "1.3.2", @@ -3505,6 +3707,15 @@ "node": ">= 6" } }, + "node_modules/get-uri/node_modules/data-uri-to-buffer": { + "version": "3.0.1", + "resolved": "https://registry.npmjs.org/data-uri-to-buffer/-/data-uri-to-buffer-3.0.1.tgz", + "integrity": "sha512-WboRycPNsVw3B3TL559F7kuBUM4d8CgMEvk6xEJlOp7OBPjt6G7z8WMWlD2rOFZLk6OYfFIUGsCOWzcQH9K2og==", + "dev": true, + "engines": { + "node": ">= 6" + } + }, "node_modules/gh-pages": { "version": "4.0.0", "resolved": "https://registry.npmjs.org/gh-pages/-/gh-pages-4.0.0.tgz", @@ -3528,22 +3739,22 @@ } }, "node_modules/git-up": { - "version": "4.0.5", - "resolved": "https://registry.npmjs.org/git-up/-/git-up-4.0.5.tgz", - "integrity": "sha512-YUvVDg/vX3d0syBsk/CKUTib0srcQME0JyHkL5BaYdwLsiCslPWmDSi8PUMo9pXYjrryMcmsCoCgsTpSCJEQaA==", + "version": "6.0.0", + "resolved": "https://registry.npmjs.org/git-up/-/git-up-6.0.0.tgz", + "integrity": "sha512-6RUFSNd1c/D0xtGnyWN2sxza2bZtZ/EmI9448n6rCZruFwV/ezeEn2fJP7XnUQGwf0RAtd/mmUCbtH6JPYA2SA==", "dev": true, "dependencies": { - "is-ssh": "^1.3.0", - "parse-url": "^6.0.0" + "is-ssh": "^1.4.0", + "parse-url": "^7.0.2" } }, "node_modules/git-url-parse": { - "version": "11.6.0", - "resolved": "https://registry.npmjs.org/git-url-parse/-/git-url-parse-11.6.0.tgz", - "integrity": "sha512-WWUxvJs5HsyHL6L08wOusa/IXYtMuCAhrMmnTjQPpBU0TTHyDhnOATNH3xNQz7YOQUsqIIPTGr4xiVti1Hsk5g==", + "version": "12.0.0", + "resolved": "https://registry.npmjs.org/git-url-parse/-/git-url-parse-12.0.0.tgz", + "integrity": "sha512-I6LMWsxV87vysX1WfsoglXsXg6GjQRKq7+Dgiseo+h0skmp5Hp2rzmcEIRQot9CPA+uzU7x1x7jZdqvTFGnB+Q==", "dev": true, "dependencies": { - "git-up": "^4.0.0" + "git-up": "^6.0.0" } }, "node_modules/glob": { @@ -3594,9 +3805,9 @@ } }, "node_modules/globals": { - "version": "13.16.0", - "resolved": "https://registry.npmjs.org/globals/-/globals-13.16.0.tgz", - "integrity": "sha512-A1lrQfpNF+McdPOnnFqY3kSN0AFTy485bTi1bkLk4mVPODIUEcSfhHgRqA+QdXPksrSTTztYXx37NFV+GpGk3Q==", + "version": "13.17.0", + "resolved": "https://registry.npmjs.org/globals/-/globals-13.17.0.tgz", + "integrity": "sha512-1C+6nQRb1GwGMKm2dH/E7enFAMxGTmGI7/dEdhy/DNelv85w9B72t3uc5frtMNXIbzrarJJ/lTCjcaZwbLJmyw==", "dev": true, "dependencies": { "type-fest": "^0.20.2" @@ -3637,19 +3848,19 @@ } }, "node_modules/got": { - "version": "12.1.0", - "resolved": "https://registry.npmjs.org/got/-/got-12.1.0.tgz", - "integrity": "sha512-hBv2ty9QN2RdbJJMK3hesmSkFTjVIHyIDDbssCKnSmq62edGgImJWD10Eb1k77TiV1bxloxqcFAVK8+9pkhOig==", + "version": "12.3.1", + "resolved": "https://registry.npmjs.org/got/-/got-12.3.1.tgz", + "integrity": "sha512-tS6+JMhBh4iXMSXF6KkIsRxmloPln31QHDlcb6Ec3bzxjjFJFr/8aXdpyuLmVc9I4i2HyBHYw1QU5K1ruUdpkw==", "dev": true, "dependencies": { - "@sindresorhus/is": "^4.6.0", + "@sindresorhus/is": "^5.2.0", "@szmarczak/http-timer": "^5.0.1", "@types/cacheable-request": "^6.0.2", "@types/responselike": "^1.0.0", "cacheable-lookup": "^6.0.4", "cacheable-request": "^7.0.2", "decompress-response": "^6.0.0", - "form-data-encoder": "1.7.1", + "form-data-encoder": "^2.0.1", "get-stream": "^6.0.1", "http2-wrapper": "^2.1.10", "lowercase-keys": "^3.0.0", @@ -3669,6 +3880,12 @@ "integrity": "sha512-9ByhssR2fPVsNZj478qUUbKfmL0+t5BDVyjShtyZZLiK7ZDAArFFfopyOTj0M05wE2tJPisA4iTnnXl2YoPvOA==", "dev": true }, + "node_modules/grapheme-splitter": { + "version": "1.0.4", + "resolved": "https://registry.npmjs.org/grapheme-splitter/-/grapheme-splitter-1.0.4.tgz", + "integrity": "sha512-bzh50DW9kTPM00T8y4o8vQg89Di9oLJVLW/KaOGIXJWP/iqCN6WKYkbNOF04vFLJhwcpYUh9ydh/+5vpOqV4YQ==", + "dev": true + }, "node_modules/has": { "version": "1.0.3", "resolved": "https://registry.npmjs.org/has/-/has-1.0.3.tgz", @@ -3983,9 +4200,9 @@ } }, "node_modules/inquirer": { - "version": "9.0.0", - "resolved": "https://registry.npmjs.org/inquirer/-/inquirer-9.0.0.tgz", - "integrity": "sha512-eYTDdTYr/YPwRenOzLZTvaJUDXDW8GQgxvzBppuXLj/kauTRLfV8bCPVbGh2staP7edrqL+rGwjaOa+JVxBWsg==", + "version": "9.1.0", + "resolved": "https://registry.npmjs.org/inquirer/-/inquirer-9.1.0.tgz", + "integrity": "sha512-eukdjrBljg9t55ZnvJjvGi1OyYEzVBFsO/8o5d2MV3mc28u3x4X2kS4eJ/+9U10KiREfPkEBSeCrU/S2G/uRtw==", "dev": true, "dependencies": { "ansi-escapes": "^5.0.0", @@ -3996,9 +4213,9 @@ "figures": "^4.0.1", "lodash": "^4.17.21", "mute-stream": "0.0.8", - "ora": "^6.1.0", + "ora": "^6.1.2", "run-async": "^2.4.0", - "rxjs": "^7.5.5", + "rxjs": "^7.5.6", "string-width": "^5.1.2", "strip-ansi": "^7.0.1", "through": "^2.3.6", @@ -4124,9 +4341,9 @@ } }, "node_modules/is-core-module": { - "version": "2.9.0", - "resolved": "https://registry.npmjs.org/is-core-module/-/is-core-module-2.9.0.tgz", - "integrity": "sha512-+5FPy5PnwmO3lvfMb0AsoPaBG+5KHUI0wYFXOtYPnVVVspTFUuMZNfNaNVRt3FZadstu2c8x23vykRW/NBoU6A==", + "version": "2.10.0", + "resolved": "https://registry.npmjs.org/is-core-module/-/is-core-module-2.10.0.tgz", + "integrity": "sha512-Erxj2n/LDAZ7H8WNJXd9tw38GYM3dv8rk8Zcs+jJuxYTW7sozH+SS8NtrSjVL1/vpLvWi1hxy96IzjJ3EHTJJg==", "dev": true, "dependencies": { "has": "^1.0.3" @@ -4558,9 +4775,9 @@ } }, "node_modules/istanbul-reports": { - "version": "3.1.4", - "resolved": "https://registry.npmjs.org/istanbul-reports/-/istanbul-reports-3.1.4.tgz", - "integrity": "sha512-r1/DshN4KSE7xWEknZLLLLDn5CJybV3nw01VTkp6D5jzLuELlcbudfj/eSQFvrKsJuTVCGnePO7ho82Nw9zzfw==", + "version": "3.1.5", + "resolved": "https://registry.npmjs.org/istanbul-reports/-/istanbul-reports-3.1.5.tgz", + "integrity": "sha512-nUsEMa9pBt/NOHqbcbeJEgqIlY/K7rVWUX6Lql2orY5e9roQOthbR3vtY4zzf2orPELg80fnxxk9zUyPlgwD1w==", "dev": true, "dependencies": { "html-escaper": "^2.0.0", @@ -4659,9 +4876,9 @@ } }, "node_modules/jsonc-parser": { - "version": "3.0.0", - "resolved": "https://registry.npmjs.org/jsonc-parser/-/jsonc-parser-3.0.0.tgz", - "integrity": "sha512-fQzRfAbIBnR0IQvftw9FJveWiHp72Fg20giDrHz6TdfB12UH/uue0D3hm57UB5KgAVuniLMCaS8P1IMj9NR7cA==", + "version": "3.1.0", + "resolved": "https://registry.npmjs.org/jsonc-parser/-/jsonc-parser-3.1.0.tgz", + "integrity": "sha512-DRf0QjnNeCUds3xTjKlQQ3DpJD51GvDjJfnxUVWg6PZTo2otSm+slzNAxU/35hF8/oJIKoG9slq30JYOsF2azg==", "dev": true }, "node_modules/jsonfile": { @@ -4680,9 +4897,9 @@ "dev": true }, "node_modules/keyv": { - "version": "4.3.2", - "resolved": "https://registry.npmjs.org/keyv/-/keyv-4.3.2.tgz", - "integrity": "sha512-kn8WmodVBe12lmHpA6W8OY7SNh6wVR+Z+wZESF4iF5FCazaVXGWOtnbnvX0tMQ1bO+/TmOD9LziuYMvrIIs0xw==", + "version": "4.4.1", + "resolved": "https://registry.npmjs.org/keyv/-/keyv-4.4.1.tgz", + "integrity": "sha512-PzByhNxfBLnSBW2MZi1DF+W5+qB/7BMpOokewqIvqS8GFtP7xHm2oeGU72Y1fhtfOv/FiEnI4+nyViYDmUChnw==", "dev": true, "dependencies": { "compress-brotli": "^1.3.8", @@ -4836,9 +5053,9 @@ "dev": true }, "node_modules/marked": { - "version": "4.0.17", - "resolved": "https://registry.npmjs.org/marked/-/marked-4.0.17.tgz", - "integrity": "sha512-Wfk0ATOK5iPxM4ptrORkFemqroz0ZDxp5MWfYA7H/F+wO17NRWV5Ypxi6p3g2Xmw2bKeiYOl6oVnLHKxBA0VhA==", + "version": "4.0.19", + "resolved": "https://registry.npmjs.org/marked/-/marked-4.0.19.tgz", + "integrity": "sha512-rgQF/OxOiLcvgUAj1Q1tAf4Bgxn5h5JZTp04Fx4XUkVhs7B+7YA9JEWJhJpoO8eJt8MkZMwqLCNeNqj1bCREZQ==", "dev": true, "bin": { "marked": "bin/marked.js" @@ -5354,9 +5571,9 @@ } }, "node_modules/new-github-release-url/node_modules/type-fest": { - "version": "2.16.0", - "resolved": "https://registry.npmjs.org/type-fest/-/type-fest-2.16.0.tgz", - "integrity": "sha512-qpaThT2HQkFb83gMOrdKVsfCN7LKxP26Yq+smPzY1FqoHRjqmjqHXA7n5Gkxi8efirtbeEUxzfEdePthQWCuHw==", + "version": "2.19.0", + "resolved": "https://registry.npmjs.org/type-fest/-/type-fest-2.19.0.tgz", + "integrity": "sha512-RAH822pAdBgcNMAfWnCBU3CFZcfZ/i1eZjwFU/dsLKumyuuP3niueg2UAukXYF0E2AAoc82ZSSf9J0WQBinzHA==", "dev": true, "engines": { "node": ">=12.20" @@ -5378,24 +5595,41 @@ "path-to-regexp": "^1.7.0" } }, + "node_modules/node-domexception": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/node-domexception/-/node-domexception-1.0.0.tgz", + "integrity": "sha512-/jKZoMpw0F8GRwl4/eLROPA3cfcXtLApP0QzLmUT/HuPCZWyB7IY9ZrMeKw2O/nFIqPQB3PVM9aYm0F312AXDQ==", + "dev": true, + "funding": [ + { + "type": "github", + "url": "https://github.com/sponsors/jimmywarting" + }, + { + "type": "github", + "url": "https://paypal.me/jimmywarting" + } + ], + "engines": { + "node": ">=10.5.0" + } + }, "node_modules/node-fetch": { - "version": "2.6.7", - "resolved": "https://registry.npmjs.org/node-fetch/-/node-fetch-2.6.7.tgz", - "integrity": "sha512-ZjMPFEfVx5j+y2yF35Kzx5sF7kDzxuDj6ziH4FFbOp87zKDZNx8yExJIb05OGF4Nlt9IHFIMBkRl41VdvcNdbQ==", + "version": "3.2.10", + "resolved": "https://registry.npmjs.org/node-fetch/-/node-fetch-3.2.10.tgz", + "integrity": "sha512-MhuzNwdURnZ1Cp4XTazr69K0BTizsBroX7Zx3UgDSVcZYKF/6p0CBe4EUb/hLqmzVhl0UpYfgRljQ4yxE+iCxA==", "dev": true, "dependencies": { - "whatwg-url": "^5.0.0" + "data-uri-to-buffer": "^4.0.0", + "fetch-blob": "^3.1.4", + "formdata-polyfill": "^4.0.10" }, "engines": { - "node": "4.x || >=6.0.0" - }, - "peerDependencies": { - "encoding": "^0.1.0" + "node": "^12.20.0 || ^14.13.1 || >=16.0.0" }, - "peerDependenciesMeta": { - "encoding": { - "optional": true - } + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/node-fetch" } }, "node_modules/node-preload": { @@ -5411,9 +5645,9 @@ } }, "node_modules/node-releases": { - "version": "2.0.5", - "resolved": "https://registry.npmjs.org/node-releases/-/node-releases-2.0.5.tgz", - "integrity": "sha512-U9h1NLROZTq9uE1SNffn6WuPDg8icmi3ns4rEl/oTfIle4iLjTliCzgTsbaIFMq/Xn078/lfY/BL0GWZ+psK4Q==", + "version": "2.0.6", + "resolved": "https://registry.npmjs.org/node-releases/-/node-releases-2.0.6.tgz", + "integrity": "sha512-PiVXnNuFm5+iYkLBNeq5211hvO38y63T0i2KKh2KnUs3RpzJ+JtODFjkD8yjLwnDkTYF1eKXheUwdssR+NRZdg==", "dev": true }, "node_modules/normalize-path": { @@ -5542,14 +5776,14 @@ } }, "node_modules/object.assign": { - "version": "4.1.2", - "resolved": "https://registry.npmjs.org/object.assign/-/object.assign-4.1.2.tgz", - "integrity": "sha512-ixT2L5THXsApyiUPYKmW+2EHpXXe5Ii3M+f4e+aJFAHao5amFRW6J0OO6c/LU8Be47utCx2GL89hxGB6XSmKuQ==", + "version": "4.1.4", + "resolved": "https://registry.npmjs.org/object.assign/-/object.assign-4.1.4.tgz", + "integrity": "sha512-1mxKf0e58bvyjSCtKYY4sRe9itRk3PJpquJOjeIkz885CczcI4IvJJDLPS72oowuSh+pBxUFROpX+TU++hxhZQ==", "dev": true, "dependencies": { - "call-bind": "^1.0.0", - "define-properties": "^1.1.3", - "has-symbols": "^1.0.1", + "call-bind": "^1.0.2", + "define-properties": "^1.1.4", + "has-symbols": "^1.0.3", "object-keys": "^1.1.1" }, "engines": { @@ -5853,41 +6087,26 @@ } }, "node_modules/parse-path": { - "version": "4.0.4", - "resolved": "https://registry.npmjs.org/parse-path/-/parse-path-4.0.4.tgz", - "integrity": "sha512-Z2lWUis7jlmXC1jeOG9giRO2+FsuyNipeQ43HAjqAZjwSe3SEf+q/84FGPHoso3kyntbxa4c4i77t3m6fGf8cw==", + "version": "5.0.0", + "resolved": "https://registry.npmjs.org/parse-path/-/parse-path-5.0.0.tgz", + "integrity": "sha512-qOpH55/+ZJ4jUu/oLO+ifUKjFPNZGfnPJtzvGzKN/4oLMil5m9OH4VpOj6++9/ytJcfks4kzH2hhi87GL/OU9A==", "dev": true, "dependencies": { - "is-ssh": "^1.3.0", - "protocols": "^1.4.0", - "qs": "^6.9.4", - "query-string": "^6.13.8" + "protocols": "^2.0.0" } }, - "node_modules/parse-path/node_modules/protocols": { - "version": "1.4.8", - "resolved": "https://registry.npmjs.org/protocols/-/protocols-1.4.8.tgz", - "integrity": "sha512-IgjKyaUSjsROSO8/D49Ab7hP8mJgTYcqApOqdPhLoPxAplXmkp+zRvsrSQjFn5by0rhm4VH0GAUELIPpx7B1yg==", - "dev": true - }, "node_modules/parse-url": { - "version": "6.0.2", - "resolved": "https://registry.npmjs.org/parse-url/-/parse-url-6.0.2.tgz", - "integrity": "sha512-uCSjOvD3T+6B/sPWhR+QowAZcU/o4bjPrVBQBGFxcDF6J6FraCGIaDBsdoQawiaaAVdHvtqBe3w3vKlfBKySOQ==", + "version": "7.0.2", + "resolved": "https://registry.npmjs.org/parse-url/-/parse-url-7.0.2.tgz", + "integrity": "sha512-PqO4Z0eCiQ08Wj6QQmrmp5YTTxpYfONdOEamrtvK63AmzXpcavIVQubGHxOEwiIoDZFb8uDOoQFS0NCcjqIYQg==", "dev": true, "dependencies": { - "is-ssh": "^1.3.0", + "is-ssh": "^1.4.0", "normalize-url": "^6.1.0", - "parse-path": "^4.0.4", - "protocols": "^1.4.0" + "parse-path": "^5.0.0", + "protocols": "^2.0.1" } }, - "node_modules/parse-url/node_modules/protocols": { - "version": "1.4.8", - "resolved": "https://registry.npmjs.org/protocols/-/protocols-1.4.8.tgz", - "integrity": "sha512-IgjKyaUSjsROSO8/D49Ab7hP8mJgTYcqApOqdPhLoPxAplXmkp+zRvsrSQjFn5by0rhm4VH0GAUELIPpx7B1yg==", - "dev": true - }, "node_modules/path-exists": { "version": "4.0.0", "resolved": "https://registry.npmjs.org/path-exists/-/path-exists-4.0.0.tgz", @@ -6117,39 +6336,6 @@ "url": "https://github.com/sponsors/sindresorhus" } }, - "node_modules/qs": { - "version": "6.11.0", - "resolved": "https://registry.npmjs.org/qs/-/qs-6.11.0.tgz", - "integrity": "sha512-MvjoMCJwEarSbUYk5O+nmoSzSutSsTwF85zcHPQ9OrlFoZOYIjaqBAJIqIXjptyD5vThxGq52Xu/MaJzRkIk4Q==", - "dev": true, - "dependencies": { - "side-channel": "^1.0.4" - }, - "engines": { - "node": ">=0.6" - }, - "funding": { - "url": "https://github.com/sponsors/ljharb" - } - }, - "node_modules/query-string": { - "version": "6.14.1", - "resolved": "https://registry.npmjs.org/query-string/-/query-string-6.14.1.tgz", - "integrity": "sha512-XDxAeVmpfu1/6IjyT/gXHOl+S0vQ9owggJ30hhWKdHAsNPOcasn5o9BW0eejZqL2e4vMjhAxoW3jVHcD6mbcYw==", - "dev": true, - "dependencies": { - "decode-uri-component": "^0.2.0", - "filter-obj": "^1.1.0", - "split-on-first": "^1.0.0", - "strict-uri-encode": "^2.0.0" - }, - "engines": { - "node": ">=6" - }, - "funding": { - "url": "https://github.com/sponsors/sindresorhus" - } - }, "node_modules/queue-microtask": { "version": "1.2.3", "resolved": "https://registry.npmjs.org/queue-microtask/-/queue-microtask-1.2.3.tgz", @@ -6331,26 +6517,27 @@ } }, "node_modules/release-it": { - "version": "15.1.1", - "resolved": "https://registry.npmjs.org/release-it/-/release-it-15.1.1.tgz", - "integrity": "sha512-c+9G8Vy1LsRIaHbV+cd8o5pEo6dkPlrOr/E7cNeWdglEbdeRJiygCyaf2F3gzNAtH8v52ntHAInG6ZevwH0KEA==", + "version": "15.3.0", + "resolved": "https://registry.npmjs.org/release-it/-/release-it-15.3.0.tgz", + "integrity": "sha512-MI4EBGca+y4SskgBkWNIakFp/GvXfpZEMWkmqmsysPcou/L+E+sKd0oy33ovGCyLic+9SI2rv/lQ3ACgonmqdQ==", "dev": true, "dependencies": { "@iarna/toml": "2.2.5", - "@octokit/rest": "18.12.0", + "@octokit/rest": "19.0.3", "async-retry": "1.3.3", "chalk": "5.0.1", "cosmiconfig": "7.0.1", "execa": "6.1.0", "form-data": "4.0.0", - "git-url-parse": "11.6.0", + "git-url-parse": "12.0.0", "globby": "13.1.2", - "got": "12.1.0", - "inquirer": "9.0.0", + "got": "12.3.1", + "inquirer": "9.1.0", "is-ci": "3.0.1", "lodash": "4.17.21", "mime-types": "2.1.35", "new-github-release-url": "2.0.0", + "node-fetch": "3.2.10", "open": "8.4.0", "ora": "6.1.2", "os-name": "5.0.1", @@ -6361,7 +6548,7 @@ "update-notifier": "6.0.2", "url-join": "5.0.0", "wildcard-match": "5.1.2", - "yargs-parser": "21.0.1" + "yargs-parser": "21.1.1" }, "bin": { "release-it": "bin/release-it.js" @@ -6494,12 +6681,15 @@ } }, "node_modules/responselike": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/responselike/-/responselike-2.0.0.tgz", - "integrity": "sha512-xH48u3FTB9VsZw7R+vvgaKeLKzT6jOogbQhEe/jewwnZgzPcnyWui2Av6JpoYZF/91uueC+lqhWqeURw5/qhCw==", + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/responselike/-/responselike-2.0.1.tgz", + "integrity": "sha512-4gl03wn3hj1HP3yzgdI7d3lCkF95F21Pz4BPGvKHinyQzALR5CapwC8yIi0Rh58DEMQ/SguC03wFj2k0M/mHhw==", "dev": true, "dependencies": { "lowercase-keys": "^2.0.0" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" } }, "node_modules/responselike/node_modules/lowercase-keys": { @@ -6618,9 +6808,9 @@ } }, "node_modules/rxjs": { - "version": "7.5.5", - "resolved": "https://registry.npmjs.org/rxjs/-/rxjs-7.5.5.tgz", - "integrity": "sha512-sy+H0pQofO95VDmFLzyaw9xNJU4KTRSwQIGM6+iG3SypAtCiLDzpeG8sJrNCWn2Up9km+KhkvTdbkrdy+yzZdw==", + "version": "7.5.6", + "resolved": "https://registry.npmjs.org/rxjs/-/rxjs-7.5.6.tgz", + "integrity": "sha512-dnyv2/YsXhnm461G+R/Pe5bWP41Nm6LBXEYWI6eiFP4fiwx6WRI/CD0zbdVAudd9xwLEF2IDcKXLHit0FYjUzw==", "dev": true, "dependencies": { "tslib": "^2.1.0" @@ -6823,12 +7013,12 @@ } }, "node_modules/socks": { - "version": "2.6.2", - "resolved": "https://registry.npmjs.org/socks/-/socks-2.6.2.tgz", - "integrity": "sha512-zDZhHhZRY9PxRruRMR7kMhnf3I8hDs4S3f9RecfnGxvcBHQcKcIH/oUcEWffsfl1XxdYlA7nnlGbbTvPz9D8gA==", + "version": "2.7.0", + "resolved": "https://registry.npmjs.org/socks/-/socks-2.7.0.tgz", + "integrity": "sha512-scnOe9y4VuiNUULJN72GrM26BNOjVsfPXI+j+98PkyEfsIXroa5ofyjT+FzGvn/xHs73U2JtoBYAVx9Hl4quSA==", "dev": true, "dependencies": { - "ip": "^1.1.5", + "ip": "^2.0.0", "smart-buffer": "^4.2.0" }, "engines": { @@ -6850,6 +7040,12 @@ "node": ">= 6" } }, + "node_modules/socks/node_modules/ip": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/ip/-/ip-2.0.0.tgz", + "integrity": "sha512-WKa+XuLG1A1R0UWhl2+1XQSi+fZWMsYKffMZTTYsiZaUD8k2yDAj5atimTUD2TZkyCkNEeYE5NhFZmupOGtjYQ==", + "dev": true + }, "node_modules/source-map": { "version": "0.6.1", "resolved": "https://registry.npmjs.org/source-map/-/source-map-0.6.1.tgz", @@ -6886,15 +7082,6 @@ "node": ">=8" } }, - "node_modules/split-on-first": { - "version": "1.1.0", - "resolved": "https://registry.npmjs.org/split-on-first/-/split-on-first-1.1.0.tgz", - "integrity": "sha512-43ZssAJaMusuKWL8sKUBQXHWOpq8d6CfN/u1p4gUzfJkM05C8rxTmYrkIPTXapZpORA6LkkzcUulJ8FqA7Uudw==", - "dev": true, - "engines": { - "node": ">=6" - } - }, "node_modules/sprintf-js": { "version": "1.0.3", "resolved": "https://registry.npmjs.org/sprintf-js/-/sprintf-js-1.0.3.tgz", @@ -6910,15 +7097,6 @@ "node": ">= 0.8" } }, - "node_modules/strict-uri-encode": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/strict-uri-encode/-/strict-uri-encode-2.0.0.tgz", - "integrity": "sha512-QwiXZgpRcKkhTj2Scnn++4PKtWsH0kpzZ62L2R6c/LUVYv7hVnZqcg2+sMuT6R7Jusu1vviK/MFsu6kNJfWlEQ==", - "dev": true, - "engines": { - "node": ">=4" - } - }, "node_modules/string_decoder": { "version": "1.3.0", "resolved": "https://registry.npmjs.org/string_decoder/-/string_decoder-1.3.0.tgz", @@ -7182,9 +7360,9 @@ } }, "node_modules/ts-node": { - "version": "10.8.2", - "resolved": "https://registry.npmjs.org/ts-node/-/ts-node-10.8.2.tgz", - "integrity": "sha512-LYdGnoGddf1D6v8REPtIH+5iq/gTDuZqv2/UJUU7tKjuEU8xVZorBM+buCGNjj+pGEud+sOoM4CX3/YzINpENA==", + "version": "10.9.1", + "resolved": "https://registry.npmjs.org/ts-node/-/ts-node-10.9.1.tgz", + "integrity": "sha512-NtVysVPkxxrwFGUUxGYhfux8k78pQB3JqYBXlLRZgdGUqTO5wU/UyHop5p70iEbGhB7q5KmiZiU0Y3KlJrScEw==", "dev": true, "dependencies": { "@cspotcode/source-map-support": "^0.8.0", @@ -7303,13 +7481,13 @@ } }, "node_modules/typedoc": { - "version": "0.23.5", - "resolved": "https://registry.npmjs.org/typedoc/-/typedoc-0.23.5.tgz", - "integrity": "sha512-5ydWUOe4E9Z3a/r33cC5X5CJPLnFDKIondHYtdnEnO0sa/s8f+Nrfe+LBGOk/UTkV2IPYyL1Gm1PtUKIihklyw==", + "version": "0.23.10", + "resolved": "https://registry.npmjs.org/typedoc/-/typedoc-0.23.10.tgz", + "integrity": "sha512-03EUiu/ZuScUBMnY6p0lY+HTH8SwhzvRE3gImoemdPDWXPXlks83UGTx++lyquWeB1MTwm9D9Ca8RIjkK3AFfQ==", "dev": true, "dependencies": { "lunr": "^2.3.9", - "marked": "^4.0.16", + "marked": "^4.0.18", "minimatch": "^5.1.0", "shiki": "^0.10.1" }, @@ -7412,9 +7590,9 @@ } }, "node_modules/update-browserslist-db": { - "version": "1.0.4", - "resolved": "https://registry.npmjs.org/update-browserslist-db/-/update-browserslist-db-1.0.4.tgz", - "integrity": "sha512-jnmO2BEGUjsMOe/Fg9u0oczOe/ppIDZPebzccl1yDWGLFP16Pa1/RM5wEoKYPG2zstNcDuAStejyxsOuKINdGA==", + "version": "1.0.5", + "resolved": "https://registry.npmjs.org/update-browserslist-db/-/update-browserslist-db-1.0.5.tgz", + "integrity": "sha512-dteFFpCyvuDdr9S/ff1ISkKt/9YZxKjI9WlRR99c180GaztJtRa/fn18FdxGVKVsnPY7/a/FDN68mcvUmP4U7Q==", "dev": true, "funding": [ { @@ -7580,6 +7758,15 @@ "defaults": "^1.0.3" } }, + "node_modules/web-streams-polyfill": { + "version": "3.2.1", + "resolved": "https://registry.npmjs.org/web-streams-polyfill/-/web-streams-polyfill-3.2.1.tgz", + "integrity": "sha512-e0MO3wdXWKrLbL0DgGnUV7WHVuw9OUvL4hjgnPkIeEvESk74gAITi5G606JtZPp39cd8HA9VQzCIvA49LpPN5Q==", + "dev": true, + "engines": { + "node": ">= 8" + } + }, "node_modules/webidl-conversions": { "version": "3.0.1", "resolved": "https://registry.npmjs.org/webidl-conversions/-/webidl-conversions-3.0.1.tgz", @@ -7873,9 +8060,9 @@ } }, "node_modules/yargs-parser": { - "version": "21.0.1", - "resolved": "https://registry.npmjs.org/yargs-parser/-/yargs-parser-21.0.1.tgz", - "integrity": "sha512-9BK1jFpLzJROCI5TzwZL/TU4gqjK5xiHV/RfWLOahrjAko/e4DJkRDZQXfvqAsiZzzYhgAzbgz6lg48jcm4GLg==", + "version": "21.1.1", + "resolved": "https://registry.npmjs.org/yargs-parser/-/yargs-parser-21.1.1.tgz", + "integrity": "sha512-tVpsJW7DdjecAiFpbIB1e3qxIQsE6NoPc5/eTdrbbIC4h0LVsWhnoa3g+m2HclBIujHzsxZ4VJVA+GUuc2/LBw==", "dev": true, "engines": { "node": ">=12" @@ -8002,12 +8189,12 @@ "devDependencies": { "@istanbuljs/nyc-config-typescript": "^1.0.2", "@redis/test-utils": "*", - "@types/node": "^18.0.1", + "@types/node": "^18.7.10", "nyc": "^15.1.0", - "release-it": "^15.1.1", + "release-it": "^15.3.0", "source-map-support": "^0.5.21", - "ts-node": "^10.8.2", - "typedoc": "^0.23.5", + "ts-node": "^10.9.1", + "typedoc": "^0.23.10", "typescript": "^4.7.4" }, "peerDependencies": { @@ -8026,18 +8213,18 @@ "devDependencies": { "@istanbuljs/nyc-config-typescript": "^1.0.2", "@redis/test-utils": "*", - "@types/node": "^18.0.1", - "@types/sinon": "^10.0.12", + "@types/node": "^18.7.10", + "@types/sinon": "^10.0.13", "@types/yallist": "^4.0.1", - "@typescript-eslint/eslint-plugin": "^5.30.5", - "@typescript-eslint/parser": "^5.30.5", - "eslint": "^8.19.0", + "@typescript-eslint/eslint-plugin": "^5.34.0", + "@typescript-eslint/parser": "^5.34.0", + "eslint": "^8.22.0", "nyc": "^15.1.0", - "release-it": "^15.1.1", + "release-it": "^15.3.0", "sinon": "^14.0.0", "source-map-support": "^0.5.21", - "ts-node": "^10.8.2", - "typedoc": "^0.23.5", + "ts-node": "^10.9.1", + "typedoc": "^0.23.10", "typescript": "^4.7.4" }, "engines": { @@ -8056,12 +8243,12 @@ "devDependencies": { "@istanbuljs/nyc-config-typescript": "^1.0.2", "@redis/test-utils": "*", - "@types/node": "^18.0.1", + "@types/node": "^18.7.10", "nyc": "^15.1.0", - "release-it": "^15.1.1", + "release-it": "^15.3.0", "source-map-support": "^0.5.21", - "ts-node": "^10.8.2", - "typedoc": "^0.23.5", + "ts-node": "^10.9.1", + "typedoc": "^0.23.10", "typescript": "^4.7.4" }, "peerDependencies": { @@ -8075,12 +8262,12 @@ "devDependencies": { "@istanbuljs/nyc-config-typescript": "^1.0.2", "@redis/test-utils": "*", - "@types/node": "^18.0.1", + "@types/node": "^18.7.10", "nyc": "^15.1.0", - "release-it": "^15.1.1", + "release-it": "^15.3.0", "source-map-support": "^0.5.21", - "ts-node": "^10.8.2", - "typedoc": "^0.23.5", + "ts-node": "^10.9.1", + "typedoc": "^0.23.10", "typescript": "^4.7.4" }, "peerDependencies": { @@ -8094,12 +8281,12 @@ "devDependencies": { "@istanbuljs/nyc-config-typescript": "^1.0.2", "@redis/test-utils": "*", - "@types/node": "^18.0.1", + "@types/node": "^18.7.10", "nyc": "^15.1.0", - "release-it": "^15.1.1", + "release-it": "^15.3.0", "source-map-support": "^0.5.21", - "ts-node": "^10.8.2", - "typedoc": "^0.23.5", + "ts-node": "^10.9.1", + "typedoc": "^0.23.10", "typescript": "^4.7.4" }, "peerDependencies": { @@ -8111,12 +8298,12 @@ "devDependencies": { "@istanbuljs/nyc-config-typescript": "^1.0.2", "@types/mocha": "^9.1.1", - "@types/node": "^18.0.1", - "@types/yargs": "^17.0.10", + "@types/node": "^18.7.10", + "@types/yargs": "^17.0.11", "mocha": "^10.0.0", "nyc": "^15.1.0", "source-map-support": "^0.5.21", - "ts-node": "^10.8.2", + "ts-node": "^10.9.1", "typescript": "^4.7.4", "yargs": "^17.5.1" }, @@ -8242,12 +8429,12 @@ "devDependencies": { "@istanbuljs/nyc-config-typescript": "^1.0.2", "@redis/test-utils": "*", - "@types/node": "^18.0.1", + "@types/node": "^18.7.10", "nyc": "^15.1.0", - "release-it": "^15.1.1", + "release-it": "^15.3.0", "source-map-support": "^0.5.21", - "ts-node": "^10.8.2", - "typedoc": "^0.23.5", + "ts-node": "^10.9.1", + "typedoc": "^0.23.10", "typescript": "^4.7.4" }, "peerDependencies": { @@ -8276,27 +8463,27 @@ } }, "@babel/compat-data": { - "version": "7.18.6", - "resolved": "https://registry.npmjs.org/@babel/compat-data/-/compat-data-7.18.6.tgz", - "integrity": "sha512-tzulrgDT0QD6U7BJ4TKVk2SDDg7wlP39P9yAx1RfLy7vP/7rsDRlWVfbWxElslu56+r7QOhB2NSDsabYYruoZQ==", + "version": "7.18.13", + "resolved": "https://registry.npmjs.org/@babel/compat-data/-/compat-data-7.18.13.tgz", + "integrity": "sha512-5yUzC5LqyTFp2HLmDoxGQelcdYgSpP9xsnMWBphAscOdFrHSAVbLNzWiy32sVNDqJRDiJK6klfDnAgu6PAGSHw==", "dev": true }, "@babel/core": { - "version": "7.18.6", - "resolved": "https://registry.npmjs.org/@babel/core/-/core-7.18.6.tgz", - "integrity": "sha512-cQbWBpxcbbs/IUredIPkHiAGULLV8iwgNRMFzvbhEXISp4f3rUUXE5+TIw6KwUWUR3DwyI6gmBRnmAtYaWehwQ==", + "version": "7.18.13", + "resolved": "https://registry.npmjs.org/@babel/core/-/core-7.18.13.tgz", + "integrity": "sha512-ZisbOvRRusFktksHSG6pjj1CSvkPkcZq/KHD45LAkVP/oiHJkNBZWfpvlLmX8OtHDG8IuzsFlVRWo08w7Qxn0A==", "dev": true, "requires": { "@ampproject/remapping": "^2.1.0", "@babel/code-frame": "^7.18.6", - "@babel/generator": "^7.18.6", - "@babel/helper-compilation-targets": "^7.18.6", - "@babel/helper-module-transforms": "^7.18.6", - "@babel/helpers": "^7.18.6", - "@babel/parser": "^7.18.6", - "@babel/template": "^7.18.6", - "@babel/traverse": "^7.18.6", - "@babel/types": "^7.18.6", + "@babel/generator": "^7.18.13", + "@babel/helper-compilation-targets": "^7.18.9", + "@babel/helper-module-transforms": "^7.18.9", + "@babel/helpers": "^7.18.9", + "@babel/parser": "^7.18.13", + "@babel/template": "^7.18.10", + "@babel/traverse": "^7.18.13", + "@babel/types": "^7.18.13", "convert-source-map": "^1.7.0", "debug": "^4.1.0", "gensync": "^1.0.0-beta.2", @@ -8305,12 +8492,12 @@ } }, "@babel/generator": { - "version": "7.18.7", - "resolved": "https://registry.npmjs.org/@babel/generator/-/generator-7.18.7.tgz", - "integrity": "sha512-shck+7VLlY72a2w9c3zYWuE1pwOKEiQHV7GTUbSnhyl5eu3i04t30tBY82ZRWrDfo3gkakCFtevExnxbkf2a3A==", + "version": "7.18.13", + "resolved": "https://registry.npmjs.org/@babel/generator/-/generator-7.18.13.tgz", + "integrity": "sha512-CkPg8ySSPuHTYPJYo7IRALdqyjM9HCbt/3uOBEFbzyGVP6Mn8bwFPB0jX6982JVNBlYzM1nnPkfjuXSOPtQeEQ==", "dev": true, "requires": { - "@babel/types": "^7.18.7", + "@babel/types": "^7.18.13", "@jridgewell/gen-mapping": "^0.3.2", "jsesc": "^2.5.1" }, @@ -8329,31 +8516,31 @@ } }, "@babel/helper-compilation-targets": { - "version": "7.18.6", - "resolved": "https://registry.npmjs.org/@babel/helper-compilation-targets/-/helper-compilation-targets-7.18.6.tgz", - "integrity": "sha512-vFjbfhNCzqdeAtZflUFrG5YIFqGTqsctrtkZ1D/NB0mDW9TwW3GmmUepYY4G9wCET5rY5ugz4OGTcLd614IzQg==", + "version": "7.18.9", + "resolved": "https://registry.npmjs.org/@babel/helper-compilation-targets/-/helper-compilation-targets-7.18.9.tgz", + "integrity": "sha512-tzLCyVmqUiFlcFoAPLA/gL9TeYrF61VLNtb+hvkuVaB5SUjW7jcfrglBIX1vUIoT7CLP3bBlIMeyEsIl2eFQNg==", "dev": true, "requires": { - "@babel/compat-data": "^7.18.6", + "@babel/compat-data": "^7.18.8", "@babel/helper-validator-option": "^7.18.6", "browserslist": "^4.20.2", "semver": "^6.3.0" } }, "@babel/helper-environment-visitor": { - "version": "7.18.6", - "resolved": "https://registry.npmjs.org/@babel/helper-environment-visitor/-/helper-environment-visitor-7.18.6.tgz", - "integrity": "sha512-8n6gSfn2baOY+qlp+VSzsosjCVGFqWKmDF0cCWOybh52Dw3SEyoWR1KrhMJASjLwIEkkAufZ0xvr+SxLHSpy2Q==", + "version": "7.18.9", + "resolved": "https://registry.npmjs.org/@babel/helper-environment-visitor/-/helper-environment-visitor-7.18.9.tgz", + "integrity": "sha512-3r/aACDJ3fhQ/EVgFy0hpj8oHyHpQc+LPtJoY9SzTThAsStm4Ptegq92vqKoE3vD706ZVFWITnMnxucw+S9Ipg==", "dev": true }, "@babel/helper-function-name": { - "version": "7.18.6", - "resolved": "https://registry.npmjs.org/@babel/helper-function-name/-/helper-function-name-7.18.6.tgz", - "integrity": "sha512-0mWMxV1aC97dhjCah5U5Ua7668r5ZmSC2DLfH2EZnf9c3/dHZKiFa5pRLMH5tjSl471tY6496ZWk/kjNONBxhw==", + "version": "7.18.9", + "resolved": "https://registry.npmjs.org/@babel/helper-function-name/-/helper-function-name-7.18.9.tgz", + "integrity": "sha512-fJgWlZt7nxGksJS9a0XdSaI4XvpExnNIgRP+rVefWh5U7BL8pPuir6SJUmFKRfjWQ51OtWSzwOxhaH/EBWWc0A==", "dev": true, "requires": { "@babel/template": "^7.18.6", - "@babel/types": "^7.18.6" + "@babel/types": "^7.18.9" } }, "@babel/helper-hoist-variables": { @@ -8375,19 +8562,19 @@ } }, "@babel/helper-module-transforms": { - "version": "7.18.6", - "resolved": "https://registry.npmjs.org/@babel/helper-module-transforms/-/helper-module-transforms-7.18.6.tgz", - "integrity": "sha512-L//phhB4al5uucwzlimruukHB3jRd5JGClwRMD/ROrVjXfLqovYnvQrK/JK36WYyVwGGO7OD3kMyVTjx+WVPhw==", + "version": "7.18.9", + "resolved": "https://registry.npmjs.org/@babel/helper-module-transforms/-/helper-module-transforms-7.18.9.tgz", + "integrity": "sha512-KYNqY0ICwfv19b31XzvmI/mfcylOzbLtowkw+mfvGPAQ3kfCnMLYbED3YecL5tPd8nAYFQFAd6JHp2LxZk/J1g==", "dev": true, "requires": { - "@babel/helper-environment-visitor": "^7.18.6", + "@babel/helper-environment-visitor": "^7.18.9", "@babel/helper-module-imports": "^7.18.6", "@babel/helper-simple-access": "^7.18.6", "@babel/helper-split-export-declaration": "^7.18.6", "@babel/helper-validator-identifier": "^7.18.6", "@babel/template": "^7.18.6", - "@babel/traverse": "^7.18.6", - "@babel/types": "^7.18.6" + "@babel/traverse": "^7.18.9", + "@babel/types": "^7.18.9" } }, "@babel/helper-simple-access": { @@ -8408,6 +8595,12 @@ "@babel/types": "^7.18.6" } }, + "@babel/helper-string-parser": { + "version": "7.18.10", + "resolved": "https://registry.npmjs.org/@babel/helper-string-parser/-/helper-string-parser-7.18.10.tgz", + "integrity": "sha512-XtIfWmeNY3i4t7t4D2t02q50HvqHybPqW2ki1kosnvWCwuCMeo81Jf0gwr85jy/neUdg5XDdeFE/80DXiO+njw==", + "dev": true + }, "@babel/helper-validator-identifier": { "version": "7.18.6", "resolved": "https://registry.npmjs.org/@babel/helper-validator-identifier/-/helper-validator-identifier-7.18.6.tgz", @@ -8421,14 +8614,14 @@ "dev": true }, "@babel/helpers": { - "version": "7.18.6", - "resolved": "https://registry.npmjs.org/@babel/helpers/-/helpers-7.18.6.tgz", - "integrity": "sha512-vzSiiqbQOghPngUYt/zWGvK3LAsPhz55vc9XNN0xAl2gV4ieShI2OQli5duxWHD+72PZPTKAcfcZDE1Cwc5zsQ==", + "version": "7.18.9", + "resolved": "https://registry.npmjs.org/@babel/helpers/-/helpers-7.18.9.tgz", + "integrity": "sha512-Jf5a+rbrLoR4eNdUmnFu8cN5eNJT6qdTdOg5IHIzq87WwyRw9PwguLFOWYgktN/60IP4fgDUawJvs7PjQIzELQ==", "dev": true, "requires": { "@babel/template": "^7.18.6", - "@babel/traverse": "^7.18.6", - "@babel/types": "^7.18.6" + "@babel/traverse": "^7.18.9", + "@babel/types": "^7.18.9" } }, "@babel/highlight": { @@ -8501,36 +8694,36 @@ } }, "@babel/parser": { - "version": "7.18.6", - "resolved": "https://registry.npmjs.org/@babel/parser/-/parser-7.18.6.tgz", - "integrity": "sha512-uQVSa9jJUe/G/304lXspfWVpKpK4euFLgGiMQFOCpM/bgcAdeoHwi/OQz23O9GK2osz26ZiXRRV9aV+Yl1O8tw==", + "version": "7.18.13", + "resolved": "https://registry.npmjs.org/@babel/parser/-/parser-7.18.13.tgz", + "integrity": "sha512-dgXcIfMuQ0kgzLB2b9tRZs7TTFFaGM2AbtA4fJgUUYukzGH4jwsS7hzQHEGs67jdehpm22vkgKwvbU+aEflgwg==", "dev": true }, "@babel/template": { - "version": "7.18.6", - "resolved": "https://registry.npmjs.org/@babel/template/-/template-7.18.6.tgz", - "integrity": "sha512-JoDWzPe+wgBsTTgdnIma3iHNFC7YVJoPssVBDjiHfNlyt4YcunDtcDOUmfVDfCK5MfdsaIoX9PkijPhjH3nYUw==", + "version": "7.18.10", + "resolved": "https://registry.npmjs.org/@babel/template/-/template-7.18.10.tgz", + "integrity": "sha512-TI+rCtooWHr3QJ27kJxfjutghu44DLnasDMwpDqCXVTal9RLp3RSYNh4NdBrRP2cQAoG9A8juOQl6P6oZG4JxA==", "dev": true, "requires": { "@babel/code-frame": "^7.18.6", - "@babel/parser": "^7.18.6", - "@babel/types": "^7.18.6" + "@babel/parser": "^7.18.10", + "@babel/types": "^7.18.10" } }, "@babel/traverse": { - "version": "7.18.6", - "resolved": "https://registry.npmjs.org/@babel/traverse/-/traverse-7.18.6.tgz", - "integrity": "sha512-zS/OKyqmD7lslOtFqbscH6gMLFYOfG1YPqCKfAW5KrTeolKqvB8UelR49Fpr6y93kYkW2Ik00mT1LOGiAGvizw==", + "version": "7.18.13", + "resolved": "https://registry.npmjs.org/@babel/traverse/-/traverse-7.18.13.tgz", + "integrity": "sha512-N6kt9X1jRMLPxxxPYWi7tgvJRH/rtoU+dbKAPDM44RFHiMH8igdsaSBgFeskhSl/kLWLDUvIh1RXCrTmg0/zvA==", "dev": true, "requires": { "@babel/code-frame": "^7.18.6", - "@babel/generator": "^7.18.6", - "@babel/helper-environment-visitor": "^7.18.6", - "@babel/helper-function-name": "^7.18.6", + "@babel/generator": "^7.18.13", + "@babel/helper-environment-visitor": "^7.18.9", + "@babel/helper-function-name": "^7.18.9", "@babel/helper-hoist-variables": "^7.18.6", "@babel/helper-split-export-declaration": "^7.18.6", - "@babel/parser": "^7.18.6", - "@babel/types": "^7.18.6", + "@babel/parser": "^7.18.13", + "@babel/types": "^7.18.13", "debug": "^4.1.0", "globals": "^11.1.0" }, @@ -8544,11 +8737,12 @@ } }, "@babel/types": { - "version": "7.18.7", - "resolved": "https://registry.npmjs.org/@babel/types/-/types-7.18.7.tgz", - "integrity": "sha512-QG3yxTcTIBoAcQmkCs+wAPYZhu7Dk9rXKacINfNbdJDNERTbLQbHGyVG8q/YGMPeCJRIhSY0+fTc5+xuh6WPSQ==", + "version": "7.18.13", + "resolved": "https://registry.npmjs.org/@babel/types/-/types-7.18.13.tgz", + "integrity": "sha512-ePqfTihzW0W6XAU+aMw2ykilisStJfDnsejDCXRchCcMJ4O0+8DhPXf2YUbZ6wjBlsEmZwLK/sPweWtu8hcJYQ==", "dev": true, "requires": { + "@babel/helper-string-parser": "^7.18.10", "@babel/helper-validator-identifier": "^7.18.6", "to-fast-properties": "^2.0.0" } @@ -8592,9 +8786,9 @@ } }, "@humanwhocodes/config-array": { - "version": "0.9.5", - "resolved": "https://registry.npmjs.org/@humanwhocodes/config-array/-/config-array-0.9.5.tgz", - "integrity": "sha512-ObyMyWxZiCu/yTisA7uzx81s40xR2fD5Cg/2Kq7G02ajkNubJf6BopgDTmDyc3U7sXpNKM8cYOw7s7Tyr+DnCw==", + "version": "0.10.4", + "resolved": "https://registry.npmjs.org/@humanwhocodes/config-array/-/config-array-0.10.4.tgz", + "integrity": "sha512-mXAIHxZT3Vcpg83opl1wGlVZ9xydbfZO3r5YfRSH6Gpp2J/PfdBP0wbDa2sO6/qRbcalpoevVyW6A/fI6LfeMw==", "dev": true, "requires": { "@humanwhocodes/object-schema": "^1.2.1", @@ -8602,6 +8796,12 @@ "minimatch": "^3.0.4" } }, + "@humanwhocodes/gitignore-to-minimatch": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/@humanwhocodes/gitignore-to-minimatch/-/gitignore-to-minimatch-1.0.2.tgz", + "integrity": "sha512-rSqmMJDdLFUsyxR6FMtD00nfQKKLFb1kv+qBbOVKqErvloEIJLo5bDTJTQNTYgeyp78JsA7u/NPi5jT1GR/MuA==", + "dev": true + }, "@humanwhocodes/object-schema": { "version": "1.2.1", "resolved": "https://registry.npmjs.org/@humanwhocodes/object-schema/-/object-schema-1.2.1.tgz", @@ -8680,9 +8880,9 @@ } }, "@jridgewell/resolve-uri": { - "version": "3.0.8", - "resolved": "https://registry.npmjs.org/@jridgewell/resolve-uri/-/resolve-uri-3.0.8.tgz", - "integrity": "sha512-YK5G9LaddzGbcucK4c8h5tWFmMPBvRZ/uyWmN1/SbBdIvqGUdWGkJ5BAaccgs6XbzVLsqbPJrBSFwKv3kT9i7w==", + "version": "3.1.0", + "resolved": "https://registry.npmjs.org/@jridgewell/resolve-uri/-/resolve-uri-3.1.0.tgz", + "integrity": "sha512-F2msla3tad+Mfht5cJq7LSXcdudKTWCVYUgw6pLFOOHSTtZlj6SWNYAp+AhuqLmWdBO2X5hPrLcu8cVP8fy28w==", "dev": true }, "@jridgewell/set-array": { @@ -8698,9 +8898,9 @@ "dev": true }, "@jridgewell/trace-mapping": { - "version": "0.3.14", - "resolved": "https://registry.npmjs.org/@jridgewell/trace-mapping/-/trace-mapping-0.3.14.tgz", - "integrity": "sha512-bJWEfQ9lPTvm3SneWwRFVLzrh6nhjwqw7TUFFBEMzwvg7t7PCDenf2lDwqo4NQXzdpgBXyFgDWnQA+2vkruksQ==", + "version": "0.3.15", + "resolved": "https://registry.npmjs.org/@jridgewell/trace-mapping/-/trace-mapping-0.3.15.tgz", + "integrity": "sha512-oWZNOULl+UbhsgB51uuZzglikfIKSUBO/M9W2OfEjn7cmqoAiCgmv9lyACTUacZwBz0ITnJ2NqjU8Tx0DHL88g==", "dev": true, "requires": { "@jridgewell/resolve-uri": "^3.0.3", @@ -8734,64 +8934,81 @@ } }, "@octokit/auth-token": { - "version": "2.5.0", - "resolved": "https://registry.npmjs.org/@octokit/auth-token/-/auth-token-2.5.0.tgz", - "integrity": "sha512-r5FVUJCOLl19AxiuZD2VRZ/ORjp/4IN98Of6YJoJOkY75CIBuYfmiNHGrDwXr+aLGG55igl9QrxX3hbiXlLb+g==", + "version": "3.0.1", + "resolved": "https://registry.npmjs.org/@octokit/auth-token/-/auth-token-3.0.1.tgz", + "integrity": "sha512-/USkK4cioY209wXRpund6HZzHo9GmjakpV9ycOkpMcMxMk7QVcVFVyCMtzvXYiHsB2crgDgrtNYSELYFBXhhaA==", "dev": true, "requires": { - "@octokit/types": "^6.0.3" + "@octokit/types": "^7.0.0" } }, "@octokit/core": { - "version": "3.6.0", - "resolved": "https://registry.npmjs.org/@octokit/core/-/core-3.6.0.tgz", - "integrity": "sha512-7RKRKuA4xTjMhY+eG3jthb3hlZCsOwg3rztWh75Xc+ShDWOfDDATWbeZpAHBNRpm4Tv9WgBMOy1zEJYXG6NJ7Q==", + "version": "4.0.5", + "resolved": "https://registry.npmjs.org/@octokit/core/-/core-4.0.5.tgz", + "integrity": "sha512-4R3HeHTYVHCfzSAi0C6pbGXV8UDI5Rk+k3G7kLVNckswN9mvpOzW9oENfjfH3nEmzg8y3AmKmzs8Sg6pLCeOCA==", "dev": true, "requires": { - "@octokit/auth-token": "^2.4.4", - "@octokit/graphql": "^4.5.8", - "@octokit/request": "^5.6.3", - "@octokit/request-error": "^2.0.5", - "@octokit/types": "^6.0.3", + "@octokit/auth-token": "^3.0.0", + "@octokit/graphql": "^5.0.0", + "@octokit/request": "^6.0.0", + "@octokit/request-error": "^3.0.0", + "@octokit/types": "^7.0.0", "before-after-hook": "^2.2.0", "universal-user-agent": "^6.0.0" } }, "@octokit/endpoint": { - "version": "6.0.12", - "resolved": "https://registry.npmjs.org/@octokit/endpoint/-/endpoint-6.0.12.tgz", - "integrity": "sha512-lF3puPwkQWGfkMClXb4k/eUT/nZKQfxinRWJrdZaJO85Dqwo/G0yOC434Jr2ojwafWJMYqFGFa5ms4jJUgujdA==", + "version": "7.0.1", + "resolved": "https://registry.npmjs.org/@octokit/endpoint/-/endpoint-7.0.1.tgz", + "integrity": "sha512-/wTXAJwt0HzJ2IeE4kQXO+mBScfzyCkI0hMtkIaqyXd9zg76OpOfNQfHL9FlaxAV2RsNiOXZibVWloy8EexENg==", "dev": true, "requires": { - "@octokit/types": "^6.0.3", + "@octokit/types": "^7.0.0", "is-plain-object": "^5.0.0", "universal-user-agent": "^6.0.0" } }, "@octokit/graphql": { - "version": "4.8.0", - "resolved": "https://registry.npmjs.org/@octokit/graphql/-/graphql-4.8.0.tgz", - "integrity": "sha512-0gv+qLSBLKF0z8TKaSKTsS39scVKF9dbMxJpj3U0vC7wjNWFuIpL/z76Qe2fiuCbDRcJSavkXsVtMS6/dtQQsg==", + "version": "5.0.1", + "resolved": "https://registry.npmjs.org/@octokit/graphql/-/graphql-5.0.1.tgz", + "integrity": "sha512-sxmnewSwAixkP1TrLdE6yRG53eEhHhDTYUykUwdV9x8f91WcbhunIHk9x1PZLALdBZKRPUO2HRcm4kezZ79HoA==", "dev": true, "requires": { - "@octokit/request": "^5.6.0", - "@octokit/types": "^6.0.3", + "@octokit/request": "^6.0.0", + "@octokit/types": "^7.0.0", "universal-user-agent": "^6.0.0" } }, "@octokit/openapi-types": { - "version": "12.7.0", - "resolved": "https://registry.npmjs.org/@octokit/openapi-types/-/openapi-types-12.7.0.tgz", - "integrity": "sha512-vWXEPETgvltt9jEYdNtQTM8xnsQ7loEbBaLV26V7Tx8ovoN8P7R3XvhFeWiboqNhlXuBsIg1QI979WElB5mGXw==", + "version": "13.4.0", + "resolved": "https://registry.npmjs.org/@octokit/openapi-types/-/openapi-types-13.4.0.tgz", + "integrity": "sha512-2mVzW0X1+HDO3jF80/+QFZNzJiTefELKbhMu6yaBYbp/1gSMkVDm4rT472gJljTokWUlXaaE63m7WrWENhMDLw==", "dev": true }, "@octokit/plugin-paginate-rest": { - "version": "2.21.1", - "resolved": "https://registry.npmjs.org/@octokit/plugin-paginate-rest/-/plugin-paginate-rest-2.21.1.tgz", - "integrity": "sha512-NVNTK63yoTFp07GqISWK+uDfGH1CAPhQXS7LzsJBvaK5W+UlvG549pLZC55FK0FqANVl6q/9ra3SR5c97xF/sw==", + "version": "3.1.0", + "resolved": "https://registry.npmjs.org/@octokit/plugin-paginate-rest/-/plugin-paginate-rest-3.1.0.tgz", + "integrity": "sha512-+cfc40pMzWcLkoDcLb1KXqjX0jTGYXjKuQdFQDc6UAknISJHnZTiBqld6HDwRJvD4DsouDKrWXNbNV0lE/3AXA==", "dev": true, "requires": { - "@octokit/types": "^6.38.2" + "@octokit/types": "^6.41.0" + }, + "dependencies": { + "@octokit/openapi-types": { + "version": "12.11.0", + "resolved": "https://registry.npmjs.org/@octokit/openapi-types/-/openapi-types-12.11.0.tgz", + "integrity": "sha512-VsXyi8peyRq9PqIz/tpqiL2w3w80OgVMwBHltTml3LmVvXiphgeqmY9mvBw9Wu7e0QWk/fqD37ux8yP5uVekyQ==", + "dev": true + }, + "@octokit/types": { + "version": "6.41.0", + "resolved": "https://registry.npmjs.org/@octokit/types/-/types-6.41.0.tgz", + "integrity": "sha512-eJ2jbzjdijiL3B4PrSQaSjuF2sPEQPVCPzBvTHJD9Nz+9dw2SGH4K4xeQJ77YfTq5bRQ+bD8wT11JbeDPmxmGg==", + "dev": true, + "requires": { + "@octokit/openapi-types": "^12.11.0" + } + } } }, "@octokit/plugin-request-log": { @@ -8802,59 +9019,70 @@ "requires": {} }, "@octokit/plugin-rest-endpoint-methods": { - "version": "5.16.1", - "resolved": "https://registry.npmjs.org/@octokit/plugin-rest-endpoint-methods/-/plugin-rest-endpoint-methods-5.16.1.tgz", - "integrity": "sha512-RMHD3aJZvOpjR2fGzD2an6eU7LG8MsknhUHvP+wRUnKdbt7eDdhTMLQsZ4xsHZcLNsxPO/K4DDIZPhI2s571Ag==", + "version": "6.3.0", + "resolved": "https://registry.npmjs.org/@octokit/plugin-rest-endpoint-methods/-/plugin-rest-endpoint-methods-6.3.0.tgz", + "integrity": "sha512-qEu2wn6E7hqluZwIEUnDxWROvKjov3zMIAi4H4d7cmKWNMeBprEXZzJe8pE5eStUYC1ysGhD0B7L6IeG1Rfb+g==", "dev": true, "requires": { - "@octokit/types": "^6.38.2", + "@octokit/types": "^7.0.0", "deprecation": "^2.3.1" } }, "@octokit/request": { - "version": "5.6.3", - "resolved": "https://registry.npmjs.org/@octokit/request/-/request-5.6.3.tgz", - "integrity": "sha512-bFJl0I1KVc9jYTe9tdGGpAMPy32dLBXXo1dS/YwSCTL/2nd9XeHsY616RE3HPXDVk+a+dBuzyz5YdlXwcDTr2A==", + "version": "6.2.1", + "resolved": "https://registry.npmjs.org/@octokit/request/-/request-6.2.1.tgz", + "integrity": "sha512-gYKRCia3cpajRzDSU+3pt1q2OcuC6PK8PmFIyxZDWCzRXRSIBH8jXjFJ8ZceoygBIm0KsEUg4x1+XcYBz7dHPQ==", "dev": true, "requires": { - "@octokit/endpoint": "^6.0.1", - "@octokit/request-error": "^2.1.0", - "@octokit/types": "^6.16.1", + "@octokit/endpoint": "^7.0.0", + "@octokit/request-error": "^3.0.0", + "@octokit/types": "^7.0.0", "is-plain-object": "^5.0.0", "node-fetch": "^2.6.7", "universal-user-agent": "^6.0.0" + }, + "dependencies": { + "node-fetch": { + "version": "2.6.7", + "resolved": "https://registry.npmjs.org/node-fetch/-/node-fetch-2.6.7.tgz", + "integrity": "sha512-ZjMPFEfVx5j+y2yF35Kzx5sF7kDzxuDj6ziH4FFbOp87zKDZNx8yExJIb05OGF4Nlt9IHFIMBkRl41VdvcNdbQ==", + "dev": true, + "requires": { + "whatwg-url": "^5.0.0" + } + } } }, "@octokit/request-error": { - "version": "2.1.0", - "resolved": "https://registry.npmjs.org/@octokit/request-error/-/request-error-2.1.0.tgz", - "integrity": "sha512-1VIvgXxs9WHSjicsRwq8PlR2LR2x6DwsJAaFgzdi0JfJoGSO8mYI/cHJQ+9FbN21aa+DrgNLnwObmyeSC8Rmpg==", + "version": "3.0.1", + "resolved": "https://registry.npmjs.org/@octokit/request-error/-/request-error-3.0.1.tgz", + "integrity": "sha512-ym4Bp0HTP7F3VFssV88WD1ZyCIRoE8H35pXSKwLeMizcdZAYc/t6N9X9Yr9n6t3aG9IH75XDnZ6UeZph0vHMWQ==", "dev": true, "requires": { - "@octokit/types": "^6.0.3", + "@octokit/types": "^7.0.0", "deprecation": "^2.0.0", "once": "^1.4.0" } }, "@octokit/rest": { - "version": "18.12.0", - "resolved": "https://registry.npmjs.org/@octokit/rest/-/rest-18.12.0.tgz", - "integrity": "sha512-gDPiOHlyGavxr72y0guQEhLsemgVjwRePayJ+FcKc2SJqKUbxbkvf5kAZEWA/MKvsfYlQAMVzNJE3ezQcxMJ2Q==", + "version": "19.0.3", + "resolved": "https://registry.npmjs.org/@octokit/rest/-/rest-19.0.3.tgz", + "integrity": "sha512-5arkTsnnRT7/sbI4fqgSJ35KiFaN7zQm0uQiQtivNQLI8RQx8EHwJCajcTUwmaCMNDg7tdCvqAnc7uvHHPxrtQ==", "dev": true, "requires": { - "@octokit/core": "^3.5.1", - "@octokit/plugin-paginate-rest": "^2.16.8", + "@octokit/core": "^4.0.0", + "@octokit/plugin-paginate-rest": "^3.0.0", "@octokit/plugin-request-log": "^1.0.4", - "@octokit/plugin-rest-endpoint-methods": "^5.12.0" + "@octokit/plugin-rest-endpoint-methods": "^6.0.0" } }, "@octokit/types": { - "version": "6.39.0", - "resolved": "https://registry.npmjs.org/@octokit/types/-/types-6.39.0.tgz", - "integrity": "sha512-Mq4N9sOAYCitTsBtDdRVrBE80lIrMBhL9Jbrw0d+j96BAzlq4V+GLHFJbHokEsVvO/9tQupQdoFdgVYhD2C8UQ==", + "version": "7.1.1", + "resolved": "https://registry.npmjs.org/@octokit/types/-/types-7.1.1.tgz", + "integrity": "sha512-Dx6cNTORyVaKY0Yeb9MbHksk79L8GXsihbG6PtWqTpkyA2TY1qBWE26EQXVG3dHwY9Femdd/WEeRUEiD0+H3TQ==", "dev": true, "requires": { - "@octokit/openapi-types": "^12.7.0" + "@octokit/openapi-types": "^13.4.0" } }, "@pnpm/network.ca-file": { @@ -8867,9 +9095,9 @@ } }, "@pnpm/npm-conf": { - "version": "1.0.4", - "resolved": "https://registry.npmjs.org/@pnpm/npm-conf/-/npm-conf-1.0.4.tgz", - "integrity": "sha512-o5YFq/+ksEJMbSzzkaQDHlp00aonLDU5xNPVTRL12hTWBbVSSeWXxPukq75h+mvXnoOWT95vV2u1HSTw2C4XOw==", + "version": "1.0.5", + "resolved": "https://registry.npmjs.org/@pnpm/npm-conf/-/npm-conf-1.0.5.tgz", + "integrity": "sha512-hD8ml183638O3R6/Txrh0L8VzGOrFXgRtRDG4qQC4tONdZ5Z1M+tlUUDUvrjYdmK6G+JTBTeaCLMna11cXzi8A==", "dev": true, "requires": { "@pnpm/network.ca-file": "^1.0.1", @@ -8881,12 +9109,12 @@ "requires": { "@istanbuljs/nyc-config-typescript": "^1.0.2", "@redis/test-utils": "*", - "@types/node": "^18.0.1", + "@types/node": "^18.7.10", "nyc": "^15.1.0", - "release-it": "^15.1.1", + "release-it": "^15.3.0", "source-map-support": "^0.5.21", - "ts-node": "^10.8.2", - "typedoc": "^0.23.5", + "ts-node": "^10.9.1", + "typedoc": "^0.23.10", "typescript": "^4.7.4" } }, @@ -8895,20 +9123,20 @@ "requires": { "@istanbuljs/nyc-config-typescript": "^1.0.2", "@redis/test-utils": "*", - "@types/node": "^18.0.1", - "@types/sinon": "^10.0.12", + "@types/node": "^18.7.10", + "@types/sinon": "^10.0.13", "@types/yallist": "^4.0.1", - "@typescript-eslint/eslint-plugin": "^5.30.5", - "@typescript-eslint/parser": "^5.30.5", + "@typescript-eslint/eslint-plugin": "^5.34.0", + "@typescript-eslint/parser": "^5.34.0", "cluster-key-slot": "1.1.0", - "eslint": "^8.19.0", + "eslint": "^8.22.0", "generic-pool": "3.8.2", "nyc": "^15.1.0", - "release-it": "^15.1.1", + "release-it": "^15.3.0", "sinon": "^14.0.0", "source-map-support": "^0.5.21", - "ts-node": "^10.8.2", - "typedoc": "^0.23.5", + "ts-node": "^10.9.1", + "typedoc": "^0.23.10", "typescript": "^4.7.4", "yallist": "4.0.0" }, @@ -8925,12 +9153,12 @@ "requires": { "@istanbuljs/nyc-config-typescript": "^1.0.2", "@redis/test-utils": "*", - "@types/node": "^18.0.1", + "@types/node": "^18.7.10", "nyc": "^15.1.0", - "release-it": "^15.1.1", + "release-it": "^15.3.0", "source-map-support": "^0.5.21", - "ts-node": "^10.8.2", - "typedoc": "^0.23.5", + "ts-node": "^10.9.1", + "typedoc": "^0.23.10", "typescript": "^4.7.4" } }, @@ -8939,12 +9167,12 @@ "requires": { "@istanbuljs/nyc-config-typescript": "^1.0.2", "@redis/test-utils": "*", - "@types/node": "^18.0.1", + "@types/node": "^18.7.10", "nyc": "^15.1.0", - "release-it": "^15.1.1", + "release-it": "^15.3.0", "source-map-support": "^0.5.21", - "ts-node": "^10.8.2", - "typedoc": "^0.23.5", + "ts-node": "^10.9.1", + "typedoc": "^0.23.10", "typescript": "^4.7.4" } }, @@ -8953,12 +9181,12 @@ "requires": { "@istanbuljs/nyc-config-typescript": "^1.0.2", "@redis/test-utils": "*", - "@types/node": "^18.0.1", + "@types/node": "^18.7.10", "nyc": "^15.1.0", - "release-it": "^15.1.1", + "release-it": "^15.3.0", "source-map-support": "^0.5.21", - "ts-node": "^10.8.2", - "typedoc": "^0.23.5", + "ts-node": "^10.9.1", + "typedoc": "^0.23.10", "typescript": "^4.7.4" } }, @@ -8967,12 +9195,12 @@ "requires": { "@istanbuljs/nyc-config-typescript": "^1.0.2", "@types/mocha": "^9.1.1", - "@types/node": "^18.0.1", - "@types/yargs": "^17.0.10", + "@types/node": "^18.7.10", + "@types/yargs": "^17.0.11", "mocha": "^10.0.0", "nyc": "^15.1.0", "source-map-support": "^0.5.21", - "ts-node": "^10.8.2", + "ts-node": "^10.9.1", "typescript": "^4.7.4", "yargs": "^17.5.1" }, @@ -9068,19 +9296,19 @@ "requires": { "@istanbuljs/nyc-config-typescript": "^1.0.2", "@redis/test-utils": "*", - "@types/node": "^18.0.1", + "@types/node": "^18.7.10", "nyc": "^15.1.0", - "release-it": "^15.1.1", + "release-it": "^15.3.0", "source-map-support": "^0.5.21", - "ts-node": "^10.8.2", - "typedoc": "^0.23.5", + "ts-node": "^10.9.1", + "typedoc": "^0.23.10", "typescript": "^4.7.4" } }, "@sindresorhus/is": { - "version": "4.6.0", - "resolved": "https://registry.npmjs.org/@sindresorhus/is/-/is-4.6.0.tgz", - "integrity": "sha512-t09vSN3MdfsyCHoFcTRCH/iUtG7OJ0CsjzB8cjAmKc/va/kIgeDI/TxsigdncE/4be734m0cvIYwNaV4i2XqAw==", + "version": "5.3.0", + "resolved": "https://registry.npmjs.org/@sindresorhus/is/-/is-5.3.0.tgz", + "integrity": "sha512-CX6t4SYQ37lzxicAqsBtxA3OseeoVrh9cSJ5PFYam0GksYlupRfy1A+Q4aYD3zvcfECLc0zO2u+ZnR2UYKvCrw==", "dev": true }, "@sinonjs/commons": { @@ -9113,9 +9341,9 @@ } }, "@sinonjs/text-encoding": { - "version": "0.7.1", - "resolved": "https://registry.npmjs.org/@sinonjs/text-encoding/-/text-encoding-0.7.1.tgz", - "integrity": "sha512-+iTbntw2IZPb/anVDbypzfQa+ay64MW0Zo8aJ8gZPWMMK6/OubMVb6lUPMagqjOPnmtauXnFCACVl3O7ogjeqQ==", + "version": "0.7.2", + "resolved": "https://registry.npmjs.org/@sinonjs/text-encoding/-/text-encoding-0.7.2.tgz", + "integrity": "sha512-sXXKG+uL9IrKqViTtao2Ws6dy0znu9sOaP1di/jKGW1M6VssO8vlpXCQcpZ+jisQ1tTFAC5Jo/EOzFbggBagFQ==", "dev": true }, "@szmarczak/http-timer": { @@ -9203,9 +9431,9 @@ "dev": true }, "@types/node": { - "version": "18.0.1", - "resolved": "https://registry.npmjs.org/@types/node/-/node-18.0.1.tgz", - "integrity": "sha512-CmR8+Tsy95hhwtZBKJBs0/FFq4XX7sDZHlGGf+0q+BRZfMbOTkzkj0AFAuTyXbObDIoanaBBW0+KEW+m3N16Wg==", + "version": "18.7.10", + "resolved": "https://registry.npmjs.org/@types/node/-/node-18.7.10.tgz", + "integrity": "sha512-SST7B//wF7xlGoLo1IEVB0cQ4e7BgXlDk5IaPgb5s0DlYLjb4if07h8+0zbQIvahfPNXs6e7zyT0EH1MqaS+5g==", "dev": true }, "@types/parse-json": { @@ -9224,9 +9452,9 @@ } }, "@types/sinon": { - "version": "10.0.12", - "resolved": "https://registry.npmjs.org/@types/sinon/-/sinon-10.0.12.tgz", - "integrity": "sha512-uWf4QJ4oky/GckJ1MYQxU52cgVDcXwBhDkpvLbi4EKoLPqLE4MOH6T/ttM33l3hi0oZ882G6oIzWv/oupRYSxQ==", + "version": "10.0.13", + "resolved": "https://registry.npmjs.org/@types/sinon/-/sinon-10.0.13.tgz", + "integrity": "sha512-UVjDqJblVNQYvVNUsj0PuYYw0ELRmgt1Nt5Vk0pT5f16ROGfcKJY8o1HVuMOJOpD727RrGB9EGvoaTQE5tgxZQ==", "dev": true, "requires": { "@types/sinonjs__fake-timers": "*" @@ -9245,9 +9473,9 @@ "dev": true }, "@types/yargs": { - "version": "17.0.10", - "resolved": "https://registry.npmjs.org/@types/yargs/-/yargs-17.0.10.tgz", - "integrity": "sha512-gmEaFwpj/7f/ROdtIlci1R1VYU1J4j95m8T+Tj3iBgiBFKg1foE/PSl93bBd5T9LDXNPo8UlNN6W0qwD8O5OaA==", + "version": "17.0.11", + "resolved": "https://registry.npmjs.org/@types/yargs/-/yargs-17.0.11.tgz", + "integrity": "sha512-aB4y9UDUXTSMxmM4MH+YnuR0g5Cph3FLQBoWoMB21DSvFVAxRVEHEMx3TLh+zUZYMCQtKiqazz0Q4Rre31f/OA==", "dev": true, "requires": { "@types/yargs-parser": "*" @@ -9260,14 +9488,14 @@ "dev": true }, "@typescript-eslint/eslint-plugin": { - "version": "5.30.5", - "resolved": "https://registry.npmjs.org/@typescript-eslint/eslint-plugin/-/eslint-plugin-5.30.5.tgz", - "integrity": "sha512-lftkqRoBvc28VFXEoRgyZuztyVUQ04JvUnATSPtIRFAccbXTWL6DEtXGYMcbg998kXw1NLUJm7rTQ9eUt+q6Ig==", + "version": "5.34.0", + "resolved": "https://registry.npmjs.org/@typescript-eslint/eslint-plugin/-/eslint-plugin-5.34.0.tgz", + "integrity": "sha512-eRfPPcasO39iwjlUAMtjeueRGuIrW3TQ9WseIDl7i5UWuFbf83yYaU7YPs4j8+4CxUMIsj1k+4kV+E+G+6ypDQ==", "dev": true, "requires": { - "@typescript-eslint/scope-manager": "5.30.5", - "@typescript-eslint/type-utils": "5.30.5", - "@typescript-eslint/utils": "5.30.5", + "@typescript-eslint/scope-manager": "5.34.0", + "@typescript-eslint/type-utils": "5.34.0", + "@typescript-eslint/utils": "5.34.0", "debug": "^4.3.4", "functional-red-black-tree": "^1.0.1", "ignore": "^5.2.0", @@ -9303,52 +9531,52 @@ } }, "@typescript-eslint/parser": { - "version": "5.30.5", - "resolved": "https://registry.npmjs.org/@typescript-eslint/parser/-/parser-5.30.5.tgz", - "integrity": "sha512-zj251pcPXI8GO9NDKWWmygP6+UjwWmrdf9qMW/L/uQJBM/0XbU2inxe5io/234y/RCvwpKEYjZ6c1YrXERkK4Q==", + "version": "5.34.0", + "resolved": "https://registry.npmjs.org/@typescript-eslint/parser/-/parser-5.34.0.tgz", + "integrity": "sha512-SZ3NEnK4usd2CXkoV3jPa/vo1mWX1fqRyIVUQZR4As1vyp4fneknBNJj+OFtV8WAVgGf+rOHMSqQbs2Qn3nFZQ==", "dev": true, "requires": { - "@typescript-eslint/scope-manager": "5.30.5", - "@typescript-eslint/types": "5.30.5", - "@typescript-eslint/typescript-estree": "5.30.5", + "@typescript-eslint/scope-manager": "5.34.0", + "@typescript-eslint/types": "5.34.0", + "@typescript-eslint/typescript-estree": "5.34.0", "debug": "^4.3.4" } }, "@typescript-eslint/scope-manager": { - "version": "5.30.5", - "resolved": "https://registry.npmjs.org/@typescript-eslint/scope-manager/-/scope-manager-5.30.5.tgz", - "integrity": "sha512-NJ6F+YHHFT/30isRe2UTmIGGAiXKckCyMnIV58cE3JkHmaD6e5zyEYm5hBDv0Wbin+IC0T1FWJpD3YqHUG/Ydg==", + "version": "5.34.0", + "resolved": "https://registry.npmjs.org/@typescript-eslint/scope-manager/-/scope-manager-5.34.0.tgz", + "integrity": "sha512-HNvASMQlah5RsBW6L6c7IJ0vsm+8Sope/wu5sEAf7joJYWNb1LDbJipzmdhdUOnfrDFE6LR1j57x1EYVxrY4ow==", "dev": true, "requires": { - "@typescript-eslint/types": "5.30.5", - "@typescript-eslint/visitor-keys": "5.30.5" + "@typescript-eslint/types": "5.34.0", + "@typescript-eslint/visitor-keys": "5.34.0" } }, "@typescript-eslint/type-utils": { - "version": "5.30.5", - "resolved": "https://registry.npmjs.org/@typescript-eslint/type-utils/-/type-utils-5.30.5.tgz", - "integrity": "sha512-k9+ejlv1GgwN1nN7XjVtyCgE0BTzhzT1YsQF0rv4Vfj2U9xnslBgMYYvcEYAFVdvhuEscELJsB7lDkN7WusErw==", + "version": "5.34.0", + "resolved": "https://registry.npmjs.org/@typescript-eslint/type-utils/-/type-utils-5.34.0.tgz", + "integrity": "sha512-Pxlno9bjsQ7hs1pdWRUv9aJijGYPYsHpwMeCQ/Inavhym3/XaKt1ZKAA8FIw4odTBfowBdZJDMxf2aavyMDkLg==", "dev": true, "requires": { - "@typescript-eslint/utils": "5.30.5", + "@typescript-eslint/utils": "5.34.0", "debug": "^4.3.4", "tsutils": "^3.21.0" } }, "@typescript-eslint/types": { - "version": "5.30.5", - "resolved": "https://registry.npmjs.org/@typescript-eslint/types/-/types-5.30.5.tgz", - "integrity": "sha512-kZ80w/M2AvsbRvOr3PjaNh6qEW1LFqs2pLdo2s5R38B2HYXG8Z0PP48/4+j1QHJFL3ssHIbJ4odPRS8PlHrFfw==", + "version": "5.34.0", + "resolved": "https://registry.npmjs.org/@typescript-eslint/types/-/types-5.34.0.tgz", + "integrity": "sha512-49fm3xbbUPuzBIOcy2CDpYWqy/X7VBkxVN+DC21e0zIm3+61Z0NZi6J9mqPmSW1BDVk9FIOvuCFyUPjXz93sjA==", "dev": true }, "@typescript-eslint/typescript-estree": { - "version": "5.30.5", - "resolved": "https://registry.npmjs.org/@typescript-eslint/typescript-estree/-/typescript-estree-5.30.5.tgz", - "integrity": "sha512-qGTc7QZC801kbYjAr4AgdOfnokpwStqyhSbiQvqGBLixniAKyH+ib2qXIVo4P9NgGzwyfD9I0nlJN7D91E1VpQ==", + "version": "5.34.0", + "resolved": "https://registry.npmjs.org/@typescript-eslint/typescript-estree/-/typescript-estree-5.34.0.tgz", + "integrity": "sha512-mXHAqapJJDVzxauEkfJI96j3D10sd567LlqroyCeJaHnu42sDbjxotGb3XFtGPYKPD9IyLjhsoULML1oI3M86A==", "dev": true, "requires": { - "@typescript-eslint/types": "5.30.5", - "@typescript-eslint/visitor-keys": "5.30.5", + "@typescript-eslint/types": "5.34.0", + "@typescript-eslint/visitor-keys": "5.34.0", "debug": "^4.3.4", "globby": "^11.1.0", "is-glob": "^4.0.3", @@ -9403,26 +9631,26 @@ } }, "@typescript-eslint/utils": { - "version": "5.30.5", - "resolved": "https://registry.npmjs.org/@typescript-eslint/utils/-/utils-5.30.5.tgz", - "integrity": "sha512-o4SSUH9IkuA7AYIfAvatldovurqTAHrfzPApOZvdUq01hHojZojCFXx06D/aFpKCgWbMPRdJBWAC3sWp3itwTA==", + "version": "5.34.0", + "resolved": "https://registry.npmjs.org/@typescript-eslint/utils/-/utils-5.34.0.tgz", + "integrity": "sha512-kWRYybU4Rn++7lm9yu8pbuydRyQsHRoBDIo11k7eqBWTldN4xUdVUMCsHBiE7aoEkFzrUEaZy3iH477vr4xHAQ==", "dev": true, "requires": { "@types/json-schema": "^7.0.9", - "@typescript-eslint/scope-manager": "5.30.5", - "@typescript-eslint/types": "5.30.5", - "@typescript-eslint/typescript-estree": "5.30.5", + "@typescript-eslint/scope-manager": "5.34.0", + "@typescript-eslint/types": "5.34.0", + "@typescript-eslint/typescript-estree": "5.34.0", "eslint-scope": "^5.1.1", "eslint-utils": "^3.0.0" } }, "@typescript-eslint/visitor-keys": { - "version": "5.30.5", - "resolved": "https://registry.npmjs.org/@typescript-eslint/visitor-keys/-/visitor-keys-5.30.5.tgz", - "integrity": "sha512-D+xtGo9HUMELzWIUqcQc0p2PO4NyvTrgIOK/VnSH083+8sq0tiLozNRKuLarwHYGRuA6TVBQSuuLwJUDWd3aaA==", + "version": "5.34.0", + "resolved": "https://registry.npmjs.org/@typescript-eslint/visitor-keys/-/visitor-keys-5.34.0.tgz", + "integrity": "sha512-O1moYjOSrab0a2fUvFpsJe0QHtvTC+cR+ovYpgKrAVXzqQyc74mv76TgY6z+aEtjQE2vgZux3CQVtGryqdcOAw==", "dev": true, "requires": { - "@typescript-eslint/types": "5.30.5", + "@typescript-eslint/types": "5.34.0", "eslint-visitor-keys": "^3.3.0" } }, @@ -9433,9 +9661,9 @@ "dev": true }, "acorn": { - "version": "8.7.1", - "resolved": "https://registry.npmjs.org/acorn/-/acorn-8.7.1.tgz", - "integrity": "sha512-Xx54uLJQZ19lKygFXOWsscKUbsBZW0CPykPhVQdhIeIwrbPmJzqeASDInc8nKBnp/JT6igTs82qPXz069H8I/A==", + "version": "8.8.0", + "resolved": "https://registry.npmjs.org/acorn/-/acorn-8.8.0.tgz", + "integrity": "sha512-QOxyigPVrpZ2GXT+PFyZTl6TtOFc5egxHIP9IlQ+RbupQuX4RkT/Bee4/kQuC02Xkzg84JcT7oLYtDIQxp+v7w==", "dev": true }, "acorn-jsx": { @@ -9708,9 +9936,9 @@ "dev": true }, "type-fest": { - "version": "2.16.0", - "resolved": "https://registry.npmjs.org/type-fest/-/type-fest-2.16.0.tgz", - "integrity": "sha512-qpaThT2HQkFb83gMOrdKVsfCN7LKxP26Yq+smPzY1FqoHRjqmjqHXA7n5Gkxi8efirtbeEUxzfEdePthQWCuHw==", + "version": "2.19.0", + "resolved": "https://registry.npmjs.org/type-fest/-/type-fest-2.19.0.tgz", + "integrity": "sha512-RAH822pAdBgcNMAfWnCBU3CFZcfZ/i1eZjwFU/dsLKumyuuP3niueg2UAukXYF0E2AAoc82ZSSf9J0WQBinzHA==", "dev": true } } @@ -9741,15 +9969,15 @@ "dev": true }, "browserslist": { - "version": "4.21.1", - "resolved": "https://registry.npmjs.org/browserslist/-/browserslist-4.21.1.tgz", - "integrity": "sha512-Nq8MFCSrnJXSc88yliwlzQe3qNe3VntIjhsArW9IJOEPSHNx23FalwApUVbzAWABLhYJJ7y8AynWI/XM8OdfjQ==", + "version": "4.21.3", + "resolved": "https://registry.npmjs.org/browserslist/-/browserslist-4.21.3.tgz", + "integrity": "sha512-898rgRXLAyRkM1GryrrBHGkqA5hlpkV5MhtZwg9QXeiyLUYs2k00Un05aX5l2/yJIOObYKOpS2JNo8nJDE7fWQ==", "dev": true, "requires": { - "caniuse-lite": "^1.0.30001359", - "electron-to-chromium": "^1.4.172", - "node-releases": "^2.0.5", - "update-browserslist-db": "^1.0.4" + "caniuse-lite": "^1.0.30001370", + "electron-to-chromium": "^1.4.202", + "node-releases": "^2.0.6", + "update-browserslist-db": "^1.0.5" } }, "buffer": { @@ -9775,9 +10003,9 @@ "dev": true }, "cacheable-lookup": { - "version": "6.0.4", - "resolved": "https://registry.npmjs.org/cacheable-lookup/-/cacheable-lookup-6.0.4.tgz", - "integrity": "sha512-mbcDEZCkv2CZF4G01kr8eBd/5agkt9oCqz75tJMSIsquvRZ2sL6Hi5zGVKi/0OSC9oO1GHfJ2AV0ZIOY9vye0A==", + "version": "6.1.0", + "resolved": "https://registry.npmjs.org/cacheable-lookup/-/cacheable-lookup-6.1.0.tgz", + "integrity": "sha512-KJ/Dmo1lDDhmW2XDPMo+9oiy/CeqosPguPCrgcVzKyZrL6pM1gU2GmPY/xo6OQPTUaA/c0kwHuywB4E6nmT9ww==", "dev": true }, "cacheable-request": { @@ -9847,9 +10075,9 @@ "dev": true }, "caniuse-lite": { - "version": "1.0.30001363", - "resolved": "https://registry.npmjs.org/caniuse-lite/-/caniuse-lite-1.0.30001363.tgz", - "integrity": "sha512-HpQhpzTGGPVMnCjIomjt+jvyUu8vNFo3TaDiZ/RcoTrlOq/5+tC8zHdsbgFB6MxmaY+jCpsH09aD80Bb4Ow3Sg==", + "version": "1.0.30001381", + "resolved": "https://registry.npmjs.org/caniuse-lite/-/caniuse-lite-1.0.30001381.tgz", + "integrity": "sha512-fEnkDOKpvp6qc+olg7+NzE1SqyfiyKf4uci7fAU38M3zxs0YOyKOxW/nMZ2l9sJbt7KZHcDIxUnbI0Iime7V4w==", "dev": true }, "chalk": { @@ -9919,9 +10147,9 @@ } }, "cli-spinners": { - "version": "2.6.1", - "resolved": "https://registry.npmjs.org/cli-spinners/-/cli-spinners-2.6.1.tgz", - "integrity": "sha512-x/5fWmGMnbKQAaNwN+UZlV79qBLM9JFnJuJ03gIi5whrob0xV0ofNVHy9DhwGdsMJQc2OKv0oGmLzvaqvAVv+g==", + "version": "2.7.0", + "resolved": "https://registry.npmjs.org/cli-spinners/-/cli-spinners-2.7.0.tgz", + "integrity": "sha512-qu3pN8Y3qHNgE2AFweciB1IfMnmZ/fsNTEE+NOFjmGB2F/7rLhnhzppvpCnN4FovtP26k8lHyy9ptEbNwWFLzw==", "dev": true }, "cli-width": { @@ -10002,9 +10230,9 @@ "dev": true }, "clone-response": { - "version": "1.0.2", - "resolved": "https://registry.npmjs.org/clone-response/-/clone-response-1.0.2.tgz", - "integrity": "sha512-yjLXh88P599UOyPTFX0POsd7WxnbsVsGohcwzHOLspIhhpalPw1BcqED8NblyZLKcGrL8dTgMlcaZxV2jAD41Q==", + "version": "1.0.3", + "resolved": "https://registry.npmjs.org/clone-response/-/clone-response-1.0.3.tgz", + "integrity": "sha512-ROoL94jJH2dUVML2Y/5PEDNaSHgeOdSDicUyS7izcF63G6sTc/FTjLub4b8Il9S8S0beOfYt0TaA5qvFK+w0wA==", "dev": true, "requires": { "mimic-response": "^1.0.0" @@ -10153,9 +10381,9 @@ } }, "data-uri-to-buffer": { - "version": "3.0.1", - "resolved": "https://registry.npmjs.org/data-uri-to-buffer/-/data-uri-to-buffer-3.0.1.tgz", - "integrity": "sha512-WboRycPNsVw3B3TL559F7kuBUM4d8CgMEvk6xEJlOp7OBPjt6G7z8WMWlD2rOFZLk6OYfFIUGsCOWzcQH9K2og==", + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/data-uri-to-buffer/-/data-uri-to-buffer-4.0.0.tgz", + "integrity": "sha512-Vr3mLBA8qWmcuschSLAOogKgQ/Jwxulv3RNE4FXnYWRGujzrRWQI4m12fQqRkwX06C0KanhLr4hK+GydchZsaA==", "dev": true }, "debug": { @@ -10173,12 +10401,6 @@ "integrity": "sha512-z2S+W9X73hAUUki+N+9Za2lBlun89zigOyGrsax+KUQ6wKW4ZoWpEYBkGhQjwAjjDCkWxhY0VKEhk8wzY7F5cA==", "dev": true }, - "decode-uri-component": { - "version": "0.2.0", - "resolved": "https://registry.npmjs.org/decode-uri-component/-/decode-uri-component-0.2.0.tgz", - "integrity": "sha512-hjf+xovcEn31w/EUYdTXQh/8smFL/dzYjohQGEIgjyNavaJfBY2p5F527Bo1VPATxv0VYTUC2bOcXvqFwk78Og==", - "dev": true - }, "decompress-response": { "version": "6.0.0", "resolved": "https://registry.npmjs.org/decompress-response/-/decompress-response-6.0.0.tgz", @@ -10318,9 +10540,9 @@ "dev": true }, "electron-to-chromium": { - "version": "1.4.179", - "resolved": "https://registry.npmjs.org/electron-to-chromium/-/electron-to-chromium-1.4.179.tgz", - "integrity": "sha512-1XeTb/U/8Xgh2YgPOqhakLYsvCcU4U7jUjTMbEnhIJoIWd/Qt3yC8y0cbG+fHzn4zUNF99Ey1xiPf20bwgLO3Q==", + "version": "1.4.225", + "resolved": "https://registry.npmjs.org/electron-to-chromium/-/electron-to-chromium-1.4.225.tgz", + "integrity": "sha512-ICHvGaCIQR3P88uK8aRtx8gmejbVJyC6bB4LEC3anzBrIzdzC7aiZHY4iFfXhN4st6I7lMO0x4sgBHf/7kBvRw==", "dev": true }, "email-addresses": { @@ -10496,13 +10718,14 @@ } }, "eslint": { - "version": "8.19.0", - "resolved": "https://registry.npmjs.org/eslint/-/eslint-8.19.0.tgz", - "integrity": "sha512-SXOPj3x9VKvPe81TjjUJCYlV4oJjQw68Uek+AM0X4p+33dj2HY5bpTZOgnQHcG2eAm1mtCU9uNMnJi7exU/kYw==", + "version": "8.22.0", + "resolved": "https://registry.npmjs.org/eslint/-/eslint-8.22.0.tgz", + "integrity": "sha512-ci4t0sz6vSRKdmkOGmprBo6fmI4PrphDFMy5JEq/fNS0gQkJM3rLmrqcp8ipMcdobH3KtUP40KniAE9W19S4wA==", "dev": true, "requires": { "@eslint/eslintrc": "^1.3.0", - "@humanwhocodes/config-array": "^0.9.2", + "@humanwhocodes/config-array": "^0.10.4", + "@humanwhocodes/gitignore-to-minimatch": "^1.0.2", "ajv": "^6.10.0", "chalk": "^4.0.0", "cross-spawn": "^7.0.2", @@ -10512,14 +10735,17 @@ "eslint-scope": "^7.1.1", "eslint-utils": "^3.0.0", "eslint-visitor-keys": "^3.3.0", - "espree": "^9.3.2", + "espree": "^9.3.3", "esquery": "^1.4.0", "esutils": "^2.0.2", "fast-deep-equal": "^3.1.3", "file-entry-cache": "^6.0.1", + "find-up": "^5.0.0", "functional-red-black-tree": "^1.0.1", "glob-parent": "^6.0.1", "globals": "^13.15.0", + "globby": "^11.1.0", + "grapheme-splitter": "^1.0.4", "ignore": "^5.2.0", "import-fresh": "^3.0.0", "imurmurhash": "^0.1.4", @@ -10553,6 +10779,12 @@ "color-convert": "^2.0.1" } }, + "array-union": { + "version": "2.1.0", + "resolved": "https://registry.npmjs.org/array-union/-/array-union-2.1.0.tgz", + "integrity": "sha512-HGyxoOTYUyCM6stUe6EJgnd4EoewAI7zMdfqO+kGjnlZmBDz/cR5pf8r/cR4Wq60sL/p0IkcjUEEPwS3GFrIyw==", + "dev": true + }, "chalk": { "version": "4.1.2", "resolved": "https://registry.npmjs.org/chalk/-/chalk-4.1.2.tgz", @@ -10585,6 +10817,57 @@ "integrity": "sha512-MMdARuVEQziNTeJD8DgMqmhwR11BRQ/cBP+pLtYdSTnf3MIO8fFeiINEbX36ZdNlfU/7A9f3gUw49B3oQsvwBA==", "dev": true }, + "find-up": { + "version": "5.0.0", + "resolved": "https://registry.npmjs.org/find-up/-/find-up-5.0.0.tgz", + "integrity": "sha512-78/PXT1wlLLDgTzDs7sjq9hzz0vXD+zn+7wypEe4fXQxCmdmqfGsEPQxmiCSQI3ajFV91bVSsvNtrJRiW6nGng==", + "dev": true, + "requires": { + "locate-path": "^6.0.0", + "path-exists": "^4.0.0" + } + }, + "globby": { + "version": "11.1.0", + "resolved": "https://registry.npmjs.org/globby/-/globby-11.1.0.tgz", + "integrity": "sha512-jhIXaOzy1sb8IyocaruWSn1TjmnBVs8Ayhcy83rmxNJ8q2uWKCAj3CnJY+KpGSXCueAPc0i05kVvVKtP1t9S3g==", + "dev": true, + "requires": { + "array-union": "^2.1.0", + "dir-glob": "^3.0.1", + "fast-glob": "^3.2.9", + "ignore": "^5.2.0", + "merge2": "^1.4.1", + "slash": "^3.0.0" + } + }, + "locate-path": { + "version": "6.0.0", + "resolved": "https://registry.npmjs.org/locate-path/-/locate-path-6.0.0.tgz", + "integrity": "sha512-iPZK6eYjbxRu3uB4/WZ3EsEIMJFMqAoopl3R+zuq0UjcAm/MO6KCweDgPfP3elTztoKP3KtnVHxTn2NHBSDVUw==", + "dev": true, + "requires": { + "p-locate": "^5.0.0" + } + }, + "p-limit": { + "version": "3.1.0", + "resolved": "https://registry.npmjs.org/p-limit/-/p-limit-3.1.0.tgz", + "integrity": "sha512-TYOanM3wGwNGsZN2cVTYPArw454xnXj5qmWF1bEoAc4+cU/ol7GVh7odevjp1FNHduHc3KZMcFduxU5Xc6uJRQ==", + "dev": true, + "requires": { + "yocto-queue": "^0.1.0" + } + }, + "p-locate": { + "version": "5.0.0", + "resolved": "https://registry.npmjs.org/p-locate/-/p-locate-5.0.0.tgz", + "integrity": "sha512-LaNjtRWUBY++zB5nE/NwcaoMylSPk+S+ZHNB1TzdbMJMny6dynpAGt7X/tl/QYq3TIeE6nxHppbo2LGymrG5Pw==", + "dev": true, + "requires": { + "p-limit": "^3.0.2" + } + }, "strip-ansi": { "version": "6.0.1", "resolved": "https://registry.npmjs.org/strip-ansi/-/strip-ansi-6.0.1.tgz", @@ -10630,12 +10913,12 @@ "dev": true }, "espree": { - "version": "9.3.2", - "resolved": "https://registry.npmjs.org/espree/-/espree-9.3.2.tgz", - "integrity": "sha512-D211tC7ZwouTIuY5x9XnS0E9sWNChB7IYKX/Xp5eQj3nFXhqmiUDB9q27y76oFl8jTg3pXcQx/bpxMfs3CIZbA==", + "version": "9.3.3", + "resolved": "https://registry.npmjs.org/espree/-/espree-9.3.3.tgz", + "integrity": "sha512-ORs1Rt/uQTqUKjDdGCyrtYxbazf5umATSf/K4qxjmZHORR6HJk+2s/2Pqe+Kk49HHINC/xNIrGfgh8sZcll0ng==", "dev": true, "requires": { - "acorn": "^8.7.1", + "acorn": "^8.8.0", "acorn-jsx": "^5.3.2", "eslint-visitor-keys": "^3.3.0" } @@ -10771,6 +11054,16 @@ "reusify": "^1.0.4" } }, + "fetch-blob": { + "version": "3.2.0", + "resolved": "https://registry.npmjs.org/fetch-blob/-/fetch-blob-3.2.0.tgz", + "integrity": "sha512-7yAQpD2UMJzLi1Dqv7qFYnPbaPx7ZfFK6PiIxQ4PfkGPyNyl2Ugx+a/umUonmKqjhM4DnfbMvdX6otXq83soQQ==", + "dev": true, + "requires": { + "node-domexception": "^1.0.0", + "web-streams-polyfill": "^3.0.3" + } + }, "figures": { "version": "4.0.1", "resolved": "https://registry.npmjs.org/figures/-/figures-4.0.1.tgz", @@ -10822,12 +11115,6 @@ "to-regex-range": "^5.0.1" } }, - "filter-obj": { - "version": "1.1.0", - "resolved": "https://registry.npmjs.org/filter-obj/-/filter-obj-1.1.0.tgz", - "integrity": "sha512-8rXg1ZnX7xzy2NGDVkBVaAy+lSlPNwad13BtgSlLuxfIslyt5Vg64U7tFcCt4WS1R0hvtnQybT/IyCkGZ3DpXQ==", - "dev": true - }, "find-cache-dir": { "version": "3.3.2", "resolved": "https://registry.npmjs.org/find-cache-dir/-/find-cache-dir-3.3.2.tgz", @@ -10866,9 +11153,9 @@ } }, "flatted": { - "version": "3.2.6", - "resolved": "https://registry.npmjs.org/flatted/-/flatted-3.2.6.tgz", - "integrity": "sha512-0sQoMh9s0BYsm+12Huy/rkKxVu4R1+r96YX5cG44rHV0pQ6iC3Q+mkoMFaGWObMFYQxCVT+ssG1ksneA2MI9KQ==", + "version": "3.2.7", + "resolved": "https://registry.npmjs.org/flatted/-/flatted-3.2.7.tgz", + "integrity": "sha512-5nqDSxl8nn5BSNxyR3n4I6eDmbolI6WT+QqR547RwxQapgjQBmtktdP+HTBb/a/zLsbzERTONyUB5pefh5TtjQ==", "dev": true }, "foreground-child": { @@ -10893,11 +11180,20 @@ } }, "form-data-encoder": { - "version": "1.7.1", - "resolved": "https://registry.npmjs.org/form-data-encoder/-/form-data-encoder-1.7.1.tgz", - "integrity": "sha512-EFRDrsMm/kyqbTQocNvRXMLjc7Es2Vk+IQFx/YW7hkUH1eBl4J1fqiP34l74Yt0pFLCNpc06fkbVk00008mzjg==", + "version": "2.1.0", + "resolved": "https://registry.npmjs.org/form-data-encoder/-/form-data-encoder-2.1.0.tgz", + "integrity": "sha512-njK60LnfhfDWy+AEUIf9ZQNRAcmXCdDfiNOm2emuPtzwh7U9k/mo9F3S54aPiaZ3vhqUjikVLfcPg2KuBddskQ==", "dev": true }, + "formdata-polyfill": { + "version": "4.0.10", + "resolved": "https://registry.npmjs.org/formdata-polyfill/-/formdata-polyfill-4.0.10.tgz", + "integrity": "sha512-buewHzMvYL29jdeQTVILecSaZKnt/RJWjoZCF5OW60Z67/GmSLBkOFM7qh1PI3zFNtJbaZL5eQu1vLfazOwj4g==", + "dev": true, + "requires": { + "fetch-blob": "^3.1.2" + } + }, "fromentries": { "version": "1.3.2", "resolved": "https://registry.npmjs.org/fromentries/-/fromentries-1.3.2.tgz", @@ -11056,6 +11352,14 @@ "file-uri-to-path": "2", "fs-extra": "^8.1.0", "ftp": "^0.3.10" + }, + "dependencies": { + "data-uri-to-buffer": { + "version": "3.0.1", + "resolved": "https://registry.npmjs.org/data-uri-to-buffer/-/data-uri-to-buffer-3.0.1.tgz", + "integrity": "sha512-WboRycPNsVw3B3TL559F7kuBUM4d8CgMEvk6xEJlOp7OBPjt6G7z8WMWlD2rOFZLk6OYfFIUGsCOWzcQH9K2og==", + "dev": true + } } }, "gh-pages": { @@ -11074,22 +11378,22 @@ } }, "git-up": { - "version": "4.0.5", - "resolved": "https://registry.npmjs.org/git-up/-/git-up-4.0.5.tgz", - "integrity": "sha512-YUvVDg/vX3d0syBsk/CKUTib0srcQME0JyHkL5BaYdwLsiCslPWmDSi8PUMo9pXYjrryMcmsCoCgsTpSCJEQaA==", + "version": "6.0.0", + "resolved": "https://registry.npmjs.org/git-up/-/git-up-6.0.0.tgz", + "integrity": "sha512-6RUFSNd1c/D0xtGnyWN2sxza2bZtZ/EmI9448n6rCZruFwV/ezeEn2fJP7XnUQGwf0RAtd/mmUCbtH6JPYA2SA==", "dev": true, "requires": { - "is-ssh": "^1.3.0", - "parse-url": "^6.0.0" + "is-ssh": "^1.4.0", + "parse-url": "^7.0.2" } }, "git-url-parse": { - "version": "11.6.0", - "resolved": "https://registry.npmjs.org/git-url-parse/-/git-url-parse-11.6.0.tgz", - "integrity": "sha512-WWUxvJs5HsyHL6L08wOusa/IXYtMuCAhrMmnTjQPpBU0TTHyDhnOATNH3xNQz7YOQUsqIIPTGr4xiVti1Hsk5g==", + "version": "12.0.0", + "resolved": "https://registry.npmjs.org/git-url-parse/-/git-url-parse-12.0.0.tgz", + "integrity": "sha512-I6LMWsxV87vysX1WfsoglXsXg6GjQRKq7+Dgiseo+h0skmp5Hp2rzmcEIRQot9CPA+uzU7x1x7jZdqvTFGnB+Q==", "dev": true, "requires": { - "git-up": "^4.0.0" + "git-up": "^6.0.0" } }, "glob": { @@ -11125,9 +11429,9 @@ } }, "globals": { - "version": "13.16.0", - "resolved": "https://registry.npmjs.org/globals/-/globals-13.16.0.tgz", - "integrity": "sha512-A1lrQfpNF+McdPOnnFqY3kSN0AFTy485bTi1bkLk4mVPODIUEcSfhHgRqA+QdXPksrSTTztYXx37NFV+GpGk3Q==", + "version": "13.17.0", + "resolved": "https://registry.npmjs.org/globals/-/globals-13.17.0.tgz", + "integrity": "sha512-1C+6nQRb1GwGMKm2dH/E7enFAMxGTmGI7/dEdhy/DNelv85w9B72t3uc5frtMNXIbzrarJJ/lTCjcaZwbLJmyw==", "dev": true, "requires": { "type-fest": "^0.20.2" @@ -11155,19 +11459,19 @@ } }, "got": { - "version": "12.1.0", - "resolved": "https://registry.npmjs.org/got/-/got-12.1.0.tgz", - "integrity": "sha512-hBv2ty9QN2RdbJJMK3hesmSkFTjVIHyIDDbssCKnSmq62edGgImJWD10Eb1k77TiV1bxloxqcFAVK8+9pkhOig==", + "version": "12.3.1", + "resolved": "https://registry.npmjs.org/got/-/got-12.3.1.tgz", + "integrity": "sha512-tS6+JMhBh4iXMSXF6KkIsRxmloPln31QHDlcb6Ec3bzxjjFJFr/8aXdpyuLmVc9I4i2HyBHYw1QU5K1ruUdpkw==", "dev": true, "requires": { - "@sindresorhus/is": "^4.6.0", + "@sindresorhus/is": "^5.2.0", "@szmarczak/http-timer": "^5.0.1", "@types/cacheable-request": "^6.0.2", "@types/responselike": "^1.0.0", "cacheable-lookup": "^6.0.4", "cacheable-request": "^7.0.2", "decompress-response": "^6.0.0", - "form-data-encoder": "1.7.1", + "form-data-encoder": "^2.0.1", "get-stream": "^6.0.1", "http2-wrapper": "^2.1.10", "lowercase-keys": "^3.0.0", @@ -11181,6 +11485,12 @@ "integrity": "sha512-9ByhssR2fPVsNZj478qUUbKfmL0+t5BDVyjShtyZZLiK7ZDAArFFfopyOTj0M05wE2tJPisA4iTnnXl2YoPvOA==", "dev": true }, + "grapheme-splitter": { + "version": "1.0.4", + "resolved": "https://registry.npmjs.org/grapheme-splitter/-/grapheme-splitter-1.0.4.tgz", + "integrity": "sha512-bzh50DW9kTPM00T8y4o8vQg89Di9oLJVLW/KaOGIXJWP/iqCN6WKYkbNOF04vFLJhwcpYUh9ydh/+5vpOqV4YQ==", + "dev": true + }, "has": { "version": "1.0.3", "resolved": "https://registry.npmjs.org/has/-/has-1.0.3.tgz", @@ -11396,9 +11706,9 @@ "dev": true }, "inquirer": { - "version": "9.0.0", - "resolved": "https://registry.npmjs.org/inquirer/-/inquirer-9.0.0.tgz", - "integrity": "sha512-eYTDdTYr/YPwRenOzLZTvaJUDXDW8GQgxvzBppuXLj/kauTRLfV8bCPVbGh2staP7edrqL+rGwjaOa+JVxBWsg==", + "version": "9.1.0", + "resolved": "https://registry.npmjs.org/inquirer/-/inquirer-9.1.0.tgz", + "integrity": "sha512-eukdjrBljg9t55ZnvJjvGi1OyYEzVBFsO/8o5d2MV3mc28u3x4X2kS4eJ/+9U10KiREfPkEBSeCrU/S2G/uRtw==", "dev": true, "requires": { "ansi-escapes": "^5.0.0", @@ -11409,9 +11719,9 @@ "figures": "^4.0.1", "lodash": "^4.17.21", "mute-stream": "0.0.8", - "ora": "^6.1.0", + "ora": "^6.1.2", "run-async": "^2.4.0", - "rxjs": "^7.5.5", + "rxjs": "^7.5.6", "string-width": "^5.1.2", "strip-ansi": "^7.0.1", "through": "^2.3.6", @@ -11501,9 +11811,9 @@ } }, "is-core-module": { - "version": "2.9.0", - "resolved": "https://registry.npmjs.org/is-core-module/-/is-core-module-2.9.0.tgz", - "integrity": "sha512-+5FPy5PnwmO3lvfMb0AsoPaBG+5KHUI0wYFXOtYPnVVVspTFUuMZNfNaNVRt3FZadstu2c8x23vykRW/NBoU6A==", + "version": "2.10.0", + "resolved": "https://registry.npmjs.org/is-core-module/-/is-core-module-2.10.0.tgz", + "integrity": "sha512-Erxj2n/LDAZ7H8WNJXd9tw38GYM3dv8rk8Zcs+jJuxYTW7sozH+SS8NtrSjVL1/vpLvWi1hxy96IzjJ3EHTJJg==", "dev": true, "requires": { "has": "^1.0.3" @@ -11794,9 +12104,9 @@ } }, "istanbul-reports": { - "version": "3.1.4", - "resolved": "https://registry.npmjs.org/istanbul-reports/-/istanbul-reports-3.1.4.tgz", - "integrity": "sha512-r1/DshN4KSE7xWEknZLLLLDn5CJybV3nw01VTkp6D5jzLuELlcbudfj/eSQFvrKsJuTVCGnePO7ho82Nw9zzfw==", + "version": "3.1.5", + "resolved": "https://registry.npmjs.org/istanbul-reports/-/istanbul-reports-3.1.5.tgz", + "integrity": "sha512-nUsEMa9pBt/NOHqbcbeJEgqIlY/K7rVWUX6Lql2orY5e9roQOthbR3vtY4zzf2orPELg80fnxxk9zUyPlgwD1w==", "dev": true, "requires": { "html-escaper": "^2.0.0", @@ -11871,9 +12181,9 @@ "dev": true }, "jsonc-parser": { - "version": "3.0.0", - "resolved": "https://registry.npmjs.org/jsonc-parser/-/jsonc-parser-3.0.0.tgz", - "integrity": "sha512-fQzRfAbIBnR0IQvftw9FJveWiHp72Fg20giDrHz6TdfB12UH/uue0D3hm57UB5KgAVuniLMCaS8P1IMj9NR7cA==", + "version": "3.1.0", + "resolved": "https://registry.npmjs.org/jsonc-parser/-/jsonc-parser-3.1.0.tgz", + "integrity": "sha512-DRf0QjnNeCUds3xTjKlQQ3DpJD51GvDjJfnxUVWg6PZTo2otSm+slzNAxU/35hF8/oJIKoG9slq30JYOsF2azg==", "dev": true }, "jsonfile": { @@ -11892,9 +12202,9 @@ "dev": true }, "keyv": { - "version": "4.3.2", - "resolved": "https://registry.npmjs.org/keyv/-/keyv-4.3.2.tgz", - "integrity": "sha512-kn8WmodVBe12lmHpA6W8OY7SNh6wVR+Z+wZESF4iF5FCazaVXGWOtnbnvX0tMQ1bO+/TmOD9LziuYMvrIIs0xw==", + "version": "4.4.1", + "resolved": "https://registry.npmjs.org/keyv/-/keyv-4.4.1.tgz", + "integrity": "sha512-PzByhNxfBLnSBW2MZi1DF+W5+qB/7BMpOokewqIvqS8GFtP7xHm2oeGU72Y1fhtfOv/FiEnI4+nyViYDmUChnw==", "dev": true, "requires": { "compress-brotli": "^1.3.8", @@ -12012,9 +12322,9 @@ "dev": true }, "marked": { - "version": "4.0.17", - "resolved": "https://registry.npmjs.org/marked/-/marked-4.0.17.tgz", - "integrity": "sha512-Wfk0ATOK5iPxM4ptrORkFemqroz0ZDxp5MWfYA7H/F+wO17NRWV5Ypxi6p3g2Xmw2bKeiYOl6oVnLHKxBA0VhA==", + "version": "4.0.19", + "resolved": "https://registry.npmjs.org/marked/-/marked-4.0.19.tgz", + "integrity": "sha512-rgQF/OxOiLcvgUAj1Q1tAf4Bgxn5h5JZTp04Fx4XUkVhs7B+7YA9JEWJhJpoO8eJt8MkZMwqLCNeNqj1bCREZQ==", "dev": true }, "merge-stream": { @@ -12383,9 +12693,9 @@ }, "dependencies": { "type-fest": { - "version": "2.16.0", - "resolved": "https://registry.npmjs.org/type-fest/-/type-fest-2.16.0.tgz", - "integrity": "sha512-qpaThT2HQkFb83gMOrdKVsfCN7LKxP26Yq+smPzY1FqoHRjqmjqHXA7n5Gkxi8efirtbeEUxzfEdePthQWCuHw==", + "version": "2.19.0", + "resolved": "https://registry.npmjs.org/type-fest/-/type-fest-2.19.0.tgz", + "integrity": "sha512-RAH822pAdBgcNMAfWnCBU3CFZcfZ/i1eZjwFU/dsLKumyuuP3niueg2UAukXYF0E2AAoc82ZSSf9J0WQBinzHA==", "dev": true } } @@ -12403,13 +12713,21 @@ "path-to-regexp": "^1.7.0" } }, + "node-domexception": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/node-domexception/-/node-domexception-1.0.0.tgz", + "integrity": "sha512-/jKZoMpw0F8GRwl4/eLROPA3cfcXtLApP0QzLmUT/HuPCZWyB7IY9ZrMeKw2O/nFIqPQB3PVM9aYm0F312AXDQ==", + "dev": true + }, "node-fetch": { - "version": "2.6.7", - "resolved": "https://registry.npmjs.org/node-fetch/-/node-fetch-2.6.7.tgz", - "integrity": "sha512-ZjMPFEfVx5j+y2yF35Kzx5sF7kDzxuDj6ziH4FFbOp87zKDZNx8yExJIb05OGF4Nlt9IHFIMBkRl41VdvcNdbQ==", + "version": "3.2.10", + "resolved": "https://registry.npmjs.org/node-fetch/-/node-fetch-3.2.10.tgz", + "integrity": "sha512-MhuzNwdURnZ1Cp4XTazr69K0BTizsBroX7Zx3UgDSVcZYKF/6p0CBe4EUb/hLqmzVhl0UpYfgRljQ4yxE+iCxA==", "dev": true, "requires": { - "whatwg-url": "^5.0.0" + "data-uri-to-buffer": "^4.0.0", + "fetch-blob": "^3.1.4", + "formdata-polyfill": "^4.0.10" } }, "node-preload": { @@ -12422,9 +12740,9 @@ } }, "node-releases": { - "version": "2.0.5", - "resolved": "https://registry.npmjs.org/node-releases/-/node-releases-2.0.5.tgz", - "integrity": "sha512-U9h1NLROZTq9uE1SNffn6WuPDg8icmi3ns4rEl/oTfIle4iLjTliCzgTsbaIFMq/Xn078/lfY/BL0GWZ+psK4Q==", + "version": "2.0.6", + "resolved": "https://registry.npmjs.org/node-releases/-/node-releases-2.0.6.tgz", + "integrity": "sha512-PiVXnNuFm5+iYkLBNeq5211hvO38y63T0i2KKh2KnUs3RpzJ+JtODFjkD8yjLwnDkTYF1eKXheUwdssR+NRZdg==", "dev": true }, "normalize-path": { @@ -12518,14 +12836,14 @@ "dev": true }, "object.assign": { - "version": "4.1.2", - "resolved": "https://registry.npmjs.org/object.assign/-/object.assign-4.1.2.tgz", - "integrity": "sha512-ixT2L5THXsApyiUPYKmW+2EHpXXe5Ii3M+f4e+aJFAHao5amFRW6J0OO6c/LU8Be47utCx2GL89hxGB6XSmKuQ==", + "version": "4.1.4", + "resolved": "https://registry.npmjs.org/object.assign/-/object.assign-4.1.4.tgz", + "integrity": "sha512-1mxKf0e58bvyjSCtKYY4sRe9itRk3PJpquJOjeIkz885CczcI4IvJJDLPS72oowuSh+pBxUFROpX+TU++hxhZQ==", "dev": true, "requires": { - "call-bind": "^1.0.0", - "define-properties": "^1.1.3", - "has-symbols": "^1.0.1", + "call-bind": "^1.0.2", + "define-properties": "^1.1.4", + "has-symbols": "^1.0.3", "object-keys": "^1.1.1" } }, @@ -12744,43 +13062,24 @@ } }, "parse-path": { - "version": "4.0.4", - "resolved": "https://registry.npmjs.org/parse-path/-/parse-path-4.0.4.tgz", - "integrity": "sha512-Z2lWUis7jlmXC1jeOG9giRO2+FsuyNipeQ43HAjqAZjwSe3SEf+q/84FGPHoso3kyntbxa4c4i77t3m6fGf8cw==", + "version": "5.0.0", + "resolved": "https://registry.npmjs.org/parse-path/-/parse-path-5.0.0.tgz", + "integrity": "sha512-qOpH55/+ZJ4jUu/oLO+ifUKjFPNZGfnPJtzvGzKN/4oLMil5m9OH4VpOj6++9/ytJcfks4kzH2hhi87GL/OU9A==", "dev": true, "requires": { - "is-ssh": "^1.3.0", - "protocols": "^1.4.0", - "qs": "^6.9.4", - "query-string": "^6.13.8" - }, - "dependencies": { - "protocols": { - "version": "1.4.8", - "resolved": "https://registry.npmjs.org/protocols/-/protocols-1.4.8.tgz", - "integrity": "sha512-IgjKyaUSjsROSO8/D49Ab7hP8mJgTYcqApOqdPhLoPxAplXmkp+zRvsrSQjFn5by0rhm4VH0GAUELIPpx7B1yg==", - "dev": true - } + "protocols": "^2.0.0" } }, "parse-url": { - "version": "6.0.2", - "resolved": "https://registry.npmjs.org/parse-url/-/parse-url-6.0.2.tgz", - "integrity": "sha512-uCSjOvD3T+6B/sPWhR+QowAZcU/o4bjPrVBQBGFxcDF6J6FraCGIaDBsdoQawiaaAVdHvtqBe3w3vKlfBKySOQ==", + "version": "7.0.2", + "resolved": "https://registry.npmjs.org/parse-url/-/parse-url-7.0.2.tgz", + "integrity": "sha512-PqO4Z0eCiQ08Wj6QQmrmp5YTTxpYfONdOEamrtvK63AmzXpcavIVQubGHxOEwiIoDZFb8uDOoQFS0NCcjqIYQg==", "dev": true, "requires": { - "is-ssh": "^1.3.0", + "is-ssh": "^1.4.0", "normalize-url": "^6.1.0", - "parse-path": "^4.0.4", - "protocols": "^1.4.0" - }, - "dependencies": { - "protocols": { - "version": "1.4.8", - "resolved": "https://registry.npmjs.org/protocols/-/protocols-1.4.8.tgz", - "integrity": "sha512-IgjKyaUSjsROSO8/D49Ab7hP8mJgTYcqApOqdPhLoPxAplXmkp+zRvsrSQjFn5by0rhm4VH0GAUELIPpx7B1yg==", - "dev": true - } + "parse-path": "^5.0.0", + "protocols": "^2.0.1" } }, "path-exists": { @@ -12960,27 +13259,6 @@ "escape-goat": "^4.0.0" } }, - "qs": { - "version": "6.11.0", - "resolved": "https://registry.npmjs.org/qs/-/qs-6.11.0.tgz", - "integrity": "sha512-MvjoMCJwEarSbUYk5O+nmoSzSutSsTwF85zcHPQ9OrlFoZOYIjaqBAJIqIXjptyD5vThxGq52Xu/MaJzRkIk4Q==", - "dev": true, - "requires": { - "side-channel": "^1.0.4" - } - }, - "query-string": { - "version": "6.14.1", - "resolved": "https://registry.npmjs.org/query-string/-/query-string-6.14.1.tgz", - "integrity": "sha512-XDxAeVmpfu1/6IjyT/gXHOl+S0vQ9owggJ30hhWKdHAsNPOcasn5o9BW0eejZqL2e4vMjhAxoW3jVHcD6mbcYw==", - "dev": true, - "requires": { - "decode-uri-component": "^0.2.0", - "filter-obj": "^1.1.0", - "split-on-first": "^1.0.0", - "strict-uri-encode": "^2.0.0" - } - }, "queue-microtask": { "version": "1.2.3", "resolved": "https://registry.npmjs.org/queue-microtask/-/queue-microtask-1.2.3.tgz", @@ -13105,26 +13383,27 @@ } }, "release-it": { - "version": "15.1.1", - "resolved": "https://registry.npmjs.org/release-it/-/release-it-15.1.1.tgz", - "integrity": "sha512-c+9G8Vy1LsRIaHbV+cd8o5pEo6dkPlrOr/E7cNeWdglEbdeRJiygCyaf2F3gzNAtH8v52ntHAInG6ZevwH0KEA==", + "version": "15.3.0", + "resolved": "https://registry.npmjs.org/release-it/-/release-it-15.3.0.tgz", + "integrity": "sha512-MI4EBGca+y4SskgBkWNIakFp/GvXfpZEMWkmqmsysPcou/L+E+sKd0oy33ovGCyLic+9SI2rv/lQ3ACgonmqdQ==", "dev": true, "requires": { "@iarna/toml": "2.2.5", - "@octokit/rest": "18.12.0", + "@octokit/rest": "19.0.3", "async-retry": "1.3.3", "chalk": "5.0.1", "cosmiconfig": "7.0.1", "execa": "6.1.0", "form-data": "4.0.0", - "git-url-parse": "11.6.0", + "git-url-parse": "12.0.0", "globby": "13.1.2", - "got": "12.1.0", - "inquirer": "9.0.0", + "got": "12.3.1", + "inquirer": "9.1.0", "is-ci": "3.0.1", "lodash": "4.17.21", "mime-types": "2.1.35", "new-github-release-url": "2.0.0", + "node-fetch": "3.2.10", "open": "8.4.0", "ora": "6.1.2", "os-name": "5.0.1", @@ -13135,7 +13414,7 @@ "update-notifier": "6.0.2", "url-join": "5.0.0", "wildcard-match": "5.1.2", - "yargs-parser": "21.0.1" + "yargs-parser": "21.1.1" }, "dependencies": { "globby": { @@ -13228,9 +13507,9 @@ "dev": true }, "responselike": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/responselike/-/responselike-2.0.0.tgz", - "integrity": "sha512-xH48u3FTB9VsZw7R+vvgaKeLKzT6jOogbQhEe/jewwnZgzPcnyWui2Av6JpoYZF/91uueC+lqhWqeURw5/qhCw==", + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/responselike/-/responselike-2.0.1.tgz", + "integrity": "sha512-4gl03wn3hj1HP3yzgdI7d3lCkF95F21Pz4BPGvKHinyQzALR5CapwC8yIi0Rh58DEMQ/SguC03wFj2k0M/mHhw==", "dev": true, "requires": { "lowercase-keys": "^2.0.0" @@ -13308,9 +13587,9 @@ } }, "rxjs": { - "version": "7.5.5", - "resolved": "https://registry.npmjs.org/rxjs/-/rxjs-7.5.5.tgz", - "integrity": "sha512-sy+H0pQofO95VDmFLzyaw9xNJU4KTRSwQIGM6+iG3SypAtCiLDzpeG8sJrNCWn2Up9km+KhkvTdbkrdy+yzZdw==", + "version": "7.5.6", + "resolved": "https://registry.npmjs.org/rxjs/-/rxjs-7.5.6.tgz", + "integrity": "sha512-dnyv2/YsXhnm461G+R/Pe5bWP41Nm6LBXEYWI6eiFP4fiwx6WRI/CD0zbdVAudd9xwLEF2IDcKXLHit0FYjUzw==", "dev": true, "requires": { "tslib": "^2.1.0" @@ -13471,13 +13750,21 @@ "dev": true }, "socks": { - "version": "2.6.2", - "resolved": "https://registry.npmjs.org/socks/-/socks-2.6.2.tgz", - "integrity": "sha512-zDZhHhZRY9PxRruRMR7kMhnf3I8hDs4S3f9RecfnGxvcBHQcKcIH/oUcEWffsfl1XxdYlA7nnlGbbTvPz9D8gA==", + "version": "2.7.0", + "resolved": "https://registry.npmjs.org/socks/-/socks-2.7.0.tgz", + "integrity": "sha512-scnOe9y4VuiNUULJN72GrM26BNOjVsfPXI+j+98PkyEfsIXroa5ofyjT+FzGvn/xHs73U2JtoBYAVx9Hl4quSA==", "dev": true, "requires": { - "ip": "^1.1.5", + "ip": "^2.0.0", "smart-buffer": "^4.2.0" + }, + "dependencies": { + "ip": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/ip/-/ip-2.0.0.tgz", + "integrity": "sha512-WKa+XuLG1A1R0UWhl2+1XQSi+fZWMsYKffMZTTYsiZaUD8k2yDAj5atimTUD2TZkyCkNEeYE5NhFZmupOGtjYQ==", + "dev": true + } } }, "socks-proxy-agent": { @@ -13521,12 +13808,6 @@ "which": "^2.0.1" } }, - "split-on-first": { - "version": "1.1.0", - "resolved": "https://registry.npmjs.org/split-on-first/-/split-on-first-1.1.0.tgz", - "integrity": "sha512-43ZssAJaMusuKWL8sKUBQXHWOpq8d6CfN/u1p4gUzfJkM05C8rxTmYrkIPTXapZpORA6LkkzcUulJ8FqA7Uudw==", - "dev": true - }, "sprintf-js": { "version": "1.0.3", "resolved": "https://registry.npmjs.org/sprintf-js/-/sprintf-js-1.0.3.tgz", @@ -13539,12 +13820,6 @@ "integrity": "sha512-RwNA9Z/7PrK06rYLIzFMlaF+l73iwpzsqRIFgbMLbTcLD6cOao82TaWefPXQvB2fOC4AjuYSEndS7N/mTCbkdQ==", "dev": true }, - "strict-uri-encode": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/strict-uri-encode/-/strict-uri-encode-2.0.0.tgz", - "integrity": "sha512-QwiXZgpRcKkhTj2Scnn++4PKtWsH0kpzZ62L2R6c/LUVYv7hVnZqcg2+sMuT6R7Jusu1vviK/MFsu6kNJfWlEQ==", - "dev": true - }, "string_decoder": { "version": "1.3.0", "resolved": "https://registry.npmjs.org/string_decoder/-/string_decoder-1.3.0.tgz", @@ -13731,9 +14006,9 @@ } }, "ts-node": { - "version": "10.8.2", - "resolved": "https://registry.npmjs.org/ts-node/-/ts-node-10.8.2.tgz", - "integrity": "sha512-LYdGnoGddf1D6v8REPtIH+5iq/gTDuZqv2/UJUU7tKjuEU8xVZorBM+buCGNjj+pGEud+sOoM4CX3/YzINpENA==", + "version": "10.9.1", + "resolved": "https://registry.npmjs.org/ts-node/-/ts-node-10.9.1.tgz", + "integrity": "sha512-NtVysVPkxxrwFGUUxGYhfux8k78pQB3JqYBXlLRZgdGUqTO5wU/UyHop5p70iEbGhB7q5KmiZiU0Y3KlJrScEw==", "dev": true, "requires": { "@cspotcode/source-map-support": "^0.8.0", @@ -13813,13 +14088,13 @@ } }, "typedoc": { - "version": "0.23.5", - "resolved": "https://registry.npmjs.org/typedoc/-/typedoc-0.23.5.tgz", - "integrity": "sha512-5ydWUOe4E9Z3a/r33cC5X5CJPLnFDKIondHYtdnEnO0sa/s8f+Nrfe+LBGOk/UTkV2IPYyL1Gm1PtUKIihklyw==", + "version": "0.23.10", + "resolved": "https://registry.npmjs.org/typedoc/-/typedoc-0.23.10.tgz", + "integrity": "sha512-03EUiu/ZuScUBMnY6p0lY+HTH8SwhzvRE3gImoemdPDWXPXlks83UGTx++lyquWeB1MTwm9D9Ca8RIjkK3AFfQ==", "dev": true, "requires": { "lunr": "^2.3.9", - "marked": "^4.0.16", + "marked": "^4.0.18", "minimatch": "^5.1.0", "shiki": "^0.10.1" }, @@ -13890,9 +14165,9 @@ "dev": true }, "update-browserslist-db": { - "version": "1.0.4", - "resolved": "https://registry.npmjs.org/update-browserslist-db/-/update-browserslist-db-1.0.4.tgz", - "integrity": "sha512-jnmO2BEGUjsMOe/Fg9u0oczOe/ppIDZPebzccl1yDWGLFP16Pa1/RM5wEoKYPG2zstNcDuAStejyxsOuKINdGA==", + "version": "1.0.5", + "resolved": "https://registry.npmjs.org/update-browserslist-db/-/update-browserslist-db-1.0.5.tgz", + "integrity": "sha512-dteFFpCyvuDdr9S/ff1ISkKt/9YZxKjI9WlRR99c180GaztJtRa/fn18FdxGVKVsnPY7/a/FDN68mcvUmP4U7Q==", "dev": true, "requires": { "escalade": "^3.1.1", @@ -14017,6 +14292,12 @@ "defaults": "^1.0.3" } }, + "web-streams-polyfill": { + "version": "3.2.1", + "resolved": "https://registry.npmjs.org/web-streams-polyfill/-/web-streams-polyfill-3.2.1.tgz", + "integrity": "sha512-e0MO3wdXWKrLbL0DgGnUV7WHVuw9OUvL4hjgnPkIeEvESk74gAITi5G606JtZPp39cd8HA9VQzCIvA49LpPN5Q==", + "dev": true + }, "webidl-conversions": { "version": "3.0.1", "resolved": "https://registry.npmjs.org/webidl-conversions/-/webidl-conversions-3.0.1.tgz", @@ -14281,9 +14562,9 @@ } }, "yargs-parser": { - "version": "21.0.1", - "resolved": "https://registry.npmjs.org/yargs-parser/-/yargs-parser-21.0.1.tgz", - "integrity": "sha512-9BK1jFpLzJROCI5TzwZL/TU4gqjK5xiHV/RfWLOahrjAko/e4DJkRDZQXfvqAsiZzzYhgAzbgz6lg48jcm4GLg==", + "version": "21.1.1", + "resolved": "https://registry.npmjs.org/yargs-parser/-/yargs-parser-21.1.1.tgz", + "integrity": "sha512-tVpsJW7DdjecAiFpbIB1e3qxIQsE6NoPc5/eTdrbbIC4h0LVsWhnoa3g+m2HclBIujHzsxZ4VJVA+GUuc2/LBw==", "dev": true }, "yargs-unparser": { diff --git a/package.json b/package.json index 3d9bad2e9c3..0375c888933 100644 --- a/package.json +++ b/package.json @@ -33,7 +33,7 @@ "devDependencies": { "@tsconfig/node14": "^1.0.3", "gh-pages": "^4.0.0", - "release-it": "^15.1.1", + "release-it": "^15.3.0", "typescript": "^4.7.4" }, "repository": { diff --git a/packages/bloom/package.json b/packages/bloom/package.json index 5325f3f039a..733723dcf95 100644 --- a/packages/bloom/package.json +++ b/packages/bloom/package.json @@ -18,12 +18,12 @@ "devDependencies": { "@istanbuljs/nyc-config-typescript": "^1.0.2", "@redis/test-utils": "*", - "@types/node": "^18.0.1", + "@types/node": "^18.7.10", "nyc": "^15.1.0", - "release-it": "^15.1.1", + "release-it": "^15.3.0", "source-map-support": "^0.5.21", - "ts-node": "^10.8.2", - "typedoc": "^0.23.5", + "ts-node": "^10.9.1", + "typedoc": "^0.23.10", "typescript": "^4.7.4" } } diff --git a/packages/client/package.json b/packages/client/package.json index 9f31eb8582b..d3325a1ccb6 100644 --- a/packages/client/package.json +++ b/packages/client/package.json @@ -21,18 +21,18 @@ "devDependencies": { "@istanbuljs/nyc-config-typescript": "^1.0.2", "@redis/test-utils": "*", - "@types/node": "^18.0.1", - "@types/sinon": "^10.0.12", + "@types/node": "^18.7.10", + "@types/sinon": "^10.0.13", "@types/yallist": "^4.0.1", - "@typescript-eslint/eslint-plugin": "^5.30.5", - "@typescript-eslint/parser": "^5.30.5", - "eslint": "^8.19.0", + "@typescript-eslint/eslint-plugin": "^5.34.0", + "@typescript-eslint/parser": "^5.34.0", + "eslint": "^8.22.0", "nyc": "^15.1.0", - "release-it": "^15.1.1", + "release-it": "^15.3.0", "sinon": "^14.0.0", "source-map-support": "^0.5.21", - "ts-node": "^10.8.2", - "typedoc": "^0.23.5", + "ts-node": "^10.9.1", + "typedoc": "^0.23.10", "typescript": "^4.7.4" }, "engines": { diff --git a/packages/graph/package.json b/packages/graph/package.json index cdcd9cf100f..0213c511a2c 100644 --- a/packages/graph/package.json +++ b/packages/graph/package.json @@ -18,12 +18,12 @@ "devDependencies": { "@istanbuljs/nyc-config-typescript": "^1.0.2", "@redis/test-utils": "*", - "@types/node": "^18.0.1", + "@types/node": "^18.7.10", "nyc": "^15.1.0", - "release-it": "^15.1.1", + "release-it": "^15.3.0", "source-map-support": "^0.5.21", - "ts-node": "^10.8.2", - "typedoc": "^0.23.5", + "ts-node": "^10.9.1", + "typedoc": "^0.23.10", "typescript": "^4.7.4" } } diff --git a/packages/json/package.json b/packages/json/package.json index a8e0dc18970..eab3313c98d 100644 --- a/packages/json/package.json +++ b/packages/json/package.json @@ -18,12 +18,12 @@ "devDependencies": { "@istanbuljs/nyc-config-typescript": "^1.0.2", "@redis/test-utils": "*", - "@types/node": "^18.0.1", + "@types/node": "^18.7.10", "nyc": "^15.1.0", - "release-it": "^15.1.1", + "release-it": "^15.3.0", "source-map-support": "^0.5.21", - "ts-node": "^10.8.2", - "typedoc": "^0.23.5", + "ts-node": "^10.9.1", + "typedoc": "^0.23.10", "typescript": "^4.7.4" } } diff --git a/packages/search/package.json b/packages/search/package.json index b7fa3a00a13..56546999b54 100644 --- a/packages/search/package.json +++ b/packages/search/package.json @@ -18,12 +18,12 @@ "devDependencies": { "@istanbuljs/nyc-config-typescript": "^1.0.2", "@redis/test-utils": "*", - "@types/node": "^18.0.1", + "@types/node": "^18.7.10", "nyc": "^15.1.0", - "release-it": "^15.1.1", + "release-it": "^15.3.0", "source-map-support": "^0.5.21", - "ts-node": "^10.8.2", - "typedoc": "^0.23.5", + "ts-node": "^10.9.1", + "typedoc": "^0.23.10", "typescript": "^4.7.4" } } diff --git a/packages/test-utils/package.json b/packages/test-utils/package.json index 7d5797fe723..ae50fe1b2be 100644 --- a/packages/test-utils/package.json +++ b/packages/test-utils/package.json @@ -12,12 +12,12 @@ "devDependencies": { "@istanbuljs/nyc-config-typescript": "^1.0.2", "@types/mocha": "^9.1.1", - "@types/node": "^18.0.1", - "@types/yargs": "^17.0.10", + "@types/node": "^18.7.10", + "@types/yargs": "^17.0.11", "mocha": "^10.0.0", "nyc": "^15.1.0", "source-map-support": "^0.5.21", - "ts-node": "^10.8.2", + "ts-node": "^10.9.1", "typescript": "^4.7.4", "yargs": "^17.5.1" } diff --git a/packages/time-series/package.json b/packages/time-series/package.json index 3f5a70ab6ad..01b35066373 100644 --- a/packages/time-series/package.json +++ b/packages/time-series/package.json @@ -18,12 +18,12 @@ "devDependencies": { "@istanbuljs/nyc-config-typescript": "^1.0.2", "@redis/test-utils": "*", - "@types/node": "^18.0.1", + "@types/node": "^18.7.10", "nyc": "^15.1.0", - "release-it": "^15.1.1", + "release-it": "^15.3.0", "source-map-support": "^0.5.21", - "ts-node": "^10.8.2", - "typedoc": "^0.23.5", + "ts-node": "^10.9.1", + "typedoc": "^0.23.10", "typescript": "^4.7.4" } } From c73c3ef6fdc400c925e06b6b920d8288e0a5bc9e Mon Sep 17 00:00:00 2001 From: leibale Date: Mon, 22 Aug 2022 18:28:28 -0400 Subject: [PATCH 1243/1748] Release client@1.3.0 --- packages/client/package.json | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/packages/client/package.json b/packages/client/package.json index d3325a1ccb6..d348b484aab 100644 --- a/packages/client/package.json +++ b/packages/client/package.json @@ -1,6 +1,6 @@ { "name": "@redis/client", - "version": "1.2.0", + "version": "1.3.0", "license": "MIT", "main": "./dist/index.js", "types": "./dist/index.d.ts", From 3c3914adb8ea19c2e73c7d0b15142b49f00206e8 Mon Sep 17 00:00:00 2001 From: leibale Date: Mon, 22 Aug 2022 18:32:13 -0400 Subject: [PATCH 1244/1748] Release search@1.1.0 --- packages/search/package.json | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/packages/search/package.json b/packages/search/package.json index 56546999b54..fdebb0b1ea5 100644 --- a/packages/search/package.json +++ b/packages/search/package.json @@ -1,6 +1,6 @@ { "name": "@redis/search", - "version": "1.0.6", + "version": "1.1.0", "license": "MIT", "main": "./dist/index.js", "types": "./dist/index.d.ts", From dea8002dfe8ce5327cad499ccc8323de2025c944 Mon Sep 17 00:00:00 2001 From: leibale Date: Mon, 22 Aug 2022 18:37:00 -0400 Subject: [PATCH 1245/1748] upgrade sub packages --- package-lock.json | 8 ++++---- package.json | 4 ++-- 2 files changed, 6 insertions(+), 6 deletions(-) diff --git a/package-lock.json b/package-lock.json index c3eeda8cdae..54e01fc6415 100644 --- a/package-lock.json +++ b/package-lock.json @@ -13,10 +13,10 @@ ], "dependencies": { "@redis/bloom": "1.0.2", - "@redis/client": "1.2.0", + "@redis/client": "1.3.0", "@redis/graph": "1.0.1", "@redis/json": "1.0.3", - "@redis/search": "1.0.6", + "@redis/search": "1.1.0", "@redis/time-series": "1.0.3" }, "devDependencies": { @@ -8203,7 +8203,7 @@ }, "packages/client": { "name": "@redis/client", - "version": "1.2.0", + "version": "1.3.0", "license": "MIT", "dependencies": { "cluster-key-slot": "1.1.0", @@ -8276,7 +8276,7 @@ }, "packages/search": { "name": "@redis/search", - "version": "1.0.6", + "version": "1.1.0", "license": "MIT", "devDependencies": { "@istanbuljs/nyc-config-typescript": "^1.0.2", diff --git a/package.json b/package.json index 0375c888933..e5266c200bb 100644 --- a/package.json +++ b/package.json @@ -24,10 +24,10 @@ }, "dependencies": { "@redis/bloom": "1.0.2", - "@redis/client": "1.2.0", + "@redis/client": "1.3.0", "@redis/graph": "1.0.1", "@redis/json": "1.0.3", - "@redis/search": "1.0.6", + "@redis/search": "1.1.0", "@redis/time-series": "1.0.3" }, "devDependencies": { From b10a6567dc84803baff6e34ed9efa1199ac3c283 Mon Sep 17 00:00:00 2001 From: leibale Date: Mon, 22 Aug 2022 18:37:23 -0400 Subject: [PATCH 1246/1748] Release redis@4.3.0 --- package-lock.json | 4 ++-- package.json | 2 +- 2 files changed, 3 insertions(+), 3 deletions(-) diff --git a/package-lock.json b/package-lock.json index 54e01fc6415..77e9230eba0 100644 --- a/package-lock.json +++ b/package-lock.json @@ -1,12 +1,12 @@ { "name": "redis", - "version": "4.2.0", + "version": "4.3.0", "lockfileVersion": 2, "requires": true, "packages": { "": { "name": "redis", - "version": "4.2.0", + "version": "4.3.0", "license": "MIT", "workspaces": [ "./packages/*" diff --git a/package.json b/package.json index e5266c200bb..403948c0c77 100644 --- a/package.json +++ b/package.json @@ -1,7 +1,7 @@ { "name": "redis", "description": "A modern, high performance Redis client", - "version": "4.2.0", + "version": "4.3.0", "license": "MIT", "main": "./dist/index.js", "types": "./dist/index.d.ts", From 5dd7d3149a633b762ece0ce8006ecbb4311a547f Mon Sep 17 00:00:00 2001 From: Leibale Eidelman Date: Wed, 31 Aug 2022 09:25:13 -0400 Subject: [PATCH 1247/1748] close #2192 close #2193 close #2194 close #2195 close #2196 close #2197 close #2198 - support for TimeSeries 1.8 (#2200) --- .../time-series/lib/commands/ALTER.spec.ts | 27 ++++- packages/time-series/lib/commands/ALTER.ts | 8 +- packages/time-series/lib/commands/CREATE.ts | 10 +- .../lib/commands/CREATERULE.spec.ts | 19 +++- .../time-series/lib/commands/CREATERULE.ts | 13 ++- .../time-series/lib/commands/DELETERULE.ts | 2 +- packages/time-series/lib/commands/GET.spec.ts | 21 +++- packages/time-series/lib/commands/GET.ts | 11 ++- .../time-series/lib/commands/INFO.spec.ts | 55 ++++++----- packages/time-series/lib/commands/INFO.ts | 48 ++++----- .../lib/commands/INFO_DEBUG.spec.ts | 47 +++------ .../time-series/lib/commands/INFO_DEBUG.ts | 32 +++--- .../time-series/lib/commands/MGET.spec.ts | 21 +++- packages/time-series/lib/commands/MGET.ts | 11 ++- .../lib/commands/MGET_WITHLABELS.ts | 4 +- .../time-series/lib/commands/MRANGE.spec.ts | 4 +- .../lib/commands/MRANGE_WITHLABELS.spec.ts | 4 +- .../lib/commands/MREVRANGE.spec.ts | 4 +- .../lib/commands/MREVRANGE_WITHLABELS.spec.ts | 4 +- .../time-series/lib/commands/RANGE.spec.ts | 2 +- .../time-series/lib/commands/REVRANGE.spec.ts | 4 +- .../time-series/lib/commands/index.spec.ts | 98 ++++++++++++++++--- packages/time-series/lib/commands/index.ts | 91 ++++++++++++++--- packages/time-series/lib/index.ts | 8 +- packages/time-series/lib/test-utils.ts | 2 +- 25 files changed, 369 insertions(+), 181 deletions(-) diff --git a/packages/time-series/lib/commands/ALTER.spec.ts b/packages/time-series/lib/commands/ALTER.spec.ts index 989a0365353..cd066533aa4 100644 --- a/packages/time-series/lib/commands/ALTER.spec.ts +++ b/packages/time-series/lib/commands/ALTER.spec.ts @@ -1,4 +1,5 @@ import { strict as assert } from 'assert'; +import { TimeSeriesDuplicatePolicies } from '.'; import testUtils, { GLOBAL } from '../test-utils'; import { transformArguments } from './ALTER'; @@ -20,6 +21,24 @@ describe('ALTER', () => { ); }); + it('with CHUNK_SIZE', () => { + assert.deepEqual( + transformArguments('key', { + CHUNK_SIZE: 1 + }), + ['TS.ALTER', 'key', 'CHUNK_SIZE', '1'] + ); + }); + + it('with DUPLICATE_POLICY', () => { + assert.deepEqual( + transformArguments('key', { + DUPLICATE_POLICY: TimeSeriesDuplicatePolicies.BLOCK + }), + ['TS.ALTER', 'key', 'DUPLICATE_POLICY', 'BLOCK'] + ); + }); + it('with LABELS', () => { assert.deepEqual( transformArguments('key', { @@ -29,19 +48,21 @@ describe('ALTER', () => { ); }); - it('with RETENTION, LABELS', () => { + it('with RETENTION, CHUNK_SIZE, DUPLICATE_POLICY, LABELS', () => { assert.deepEqual( transformArguments('key', { RETENTION: 1, + CHUNK_SIZE: 1, + DUPLICATE_POLICY: TimeSeriesDuplicatePolicies.BLOCK, LABELS: { label: 'value' } }), - ['TS.ALTER', 'key', 'RETENTION', '1', 'LABELS', 'label', 'value'] + ['TS.ALTER', 'key', 'RETENTION', '1', 'CHUNK_SIZE', '1', 'DUPLICATE_POLICY', 'BLOCK', 'LABELS', 'label', 'value'] ); }); }); testUtils.testWithClient('client.ts.alter', async client => { - await client.ts.create('key'); + await client.ts.create('key'); assert.equal( await client.ts.alter('key', { RETENTION: 1 }), diff --git a/packages/time-series/lib/commands/ALTER.ts b/packages/time-series/lib/commands/ALTER.ts index c2c6b350522..7b9e1e774c6 100644 --- a/packages/time-series/lib/commands/ALTER.ts +++ b/packages/time-series/lib/commands/ALTER.ts @@ -1,9 +1,11 @@ -import { pushRetentionArgument, Labels, pushLabelsArgument } from '.'; +import { pushRetentionArgument, Labels, pushLabelsArgument, TimeSeriesDuplicatePolicies, pushChunkSizeArgument, pushDuplicatePolicy } from '.'; export const FIRST_KEY_INDEX = 1; interface AlterOptions { RETENTION?: number; + CHUNK_SIZE?: number; + DUPLICATE_POLICY?: TimeSeriesDuplicatePolicies; LABELS?: Labels; } @@ -12,6 +14,10 @@ export function transformArguments(key: string, options?: AlterOptions): Array { - it('transformArguments', () => { - assert.deepEqual( - transformArguments('source', 'destination', TimeSeriesAggregationType.AVERAGE, 1), - ['TS.CREATERULE', 'source', 'destination', 'AGGREGATION', 'avg', '1'] - ); + describe('transformArguments', () => { + it('without options', () => { + assert.deepEqual( + transformArguments('source', 'destination', TimeSeriesAggregationType.AVERAGE, 1), + ['TS.CREATERULE', 'source', 'destination', 'AGGREGATION', 'AVG', '1'] + ); + }); + + it('with alignTimestamp', () => { + assert.deepEqual( + transformArguments('source', 'destination', TimeSeriesAggregationType.AVERAGE, 1, 1), + ['TS.CREATERULE', 'source', 'destination', 'AGGREGATION', 'AVG', '1', '1'] + ); + }); }); testUtils.testWithClient('client.ts.createRule', async client => { diff --git a/packages/time-series/lib/commands/CREATERULE.ts b/packages/time-series/lib/commands/CREATERULE.ts index 9e3d25a4c8b..87b8579a6ee 100644 --- a/packages/time-series/lib/commands/CREATERULE.ts +++ b/packages/time-series/lib/commands/CREATERULE.ts @@ -6,16 +6,23 @@ export function transformArguments( sourceKey: string, destinationKey: string, aggregationType: TimeSeriesAggregationType, - timeBucket: number + bucketDuration: number, + alignTimestamp?: number ): Array { - return [ + const args = [ 'TS.CREATERULE', sourceKey, destinationKey, 'AGGREGATION', aggregationType, - timeBucket.toString() + bucketDuration.toString() ]; + + if (alignTimestamp) { + args.push(alignTimestamp.toString()); + } + + return args; } export declare function transformReply(): 'OK'; diff --git a/packages/time-series/lib/commands/DELETERULE.ts b/packages/time-series/lib/commands/DELETERULE.ts index 1714d510932..7d2cfaeed94 100644 --- a/packages/time-series/lib/commands/DELETERULE.ts +++ b/packages/time-series/lib/commands/DELETERULE.ts @@ -4,7 +4,7 @@ export function transformArguments(sourceKey: string, destinationKey: string): A return [ 'TS.DELETERULE', sourceKey, - destinationKey, + destinationKey ]; } diff --git a/packages/time-series/lib/commands/GET.spec.ts b/packages/time-series/lib/commands/GET.spec.ts index 0c0113f35d7..29634cd775a 100644 --- a/packages/time-series/lib/commands/GET.spec.ts +++ b/packages/time-series/lib/commands/GET.spec.ts @@ -3,11 +3,22 @@ import testUtils, { GLOBAL } from '../test-utils'; import { transformArguments } from './GET'; describe('GET', () => { - it('transformArguments', () => { - assert.deepEqual( - transformArguments('key'), - ['TS.GET', 'key'] - ); + describe('transformArguments', () => { + it('without options', () => { + assert.deepEqual( + transformArguments('key'), + ['TS.GET', 'key'] + ); + }); + + it('with LATEST', () => { + assert.deepEqual( + transformArguments('key', { + LATEST: true + }), + ['TS.GET', 'key', 'LATEST'] + ); + }); }); describe('client.ts.get', () => { diff --git a/packages/time-series/lib/commands/GET.ts b/packages/time-series/lib/commands/GET.ts index ec3b1f5f803..6d74f97c9cd 100644 --- a/packages/time-series/lib/commands/GET.ts +++ b/packages/time-series/lib/commands/GET.ts @@ -1,11 +1,16 @@ -import { SampleRawReply, SampleReply, transformSampleReply } from '.'; +import { RedisCommandArguments } from '@redis/client/dist/lib/commands'; +import { pushLatestArgument, SampleRawReply, SampleReply, transformSampleReply } from '.'; export const FIRST_KEY_INDEX = 1; export const IS_READ_ONLY = true; -export function transformArguments(key: string): Array { - return ['TS.GET', key]; +interface GetOptions { + LATEST?: boolean; +} + +export function transformArguments(key: string, options?: GetOptions): RedisCommandArguments { + return pushLatestArgument(['TS.GET', key], options?.LATEST); } export function transformReply(reply: [] | SampleRawReply): null | SampleReply { diff --git a/packages/time-series/lib/commands/INFO.spec.ts b/packages/time-series/lib/commands/INFO.spec.ts index 83bad872c68..c02cdd6da4d 100644 --- a/packages/time-series/lib/commands/INFO.spec.ts +++ b/packages/time-series/lib/commands/INFO.spec.ts @@ -1,7 +1,7 @@ import { strict as assert } from 'assert'; import { TimeSeriesAggregationType, TimeSeriesDuplicatePolicies } from '.'; import testUtils, { GLOBAL } from '../test-utils'; -import { transformArguments } from './INFO'; +import { InfoReply, transformArguments } from './INFO'; describe('INFO', () => { it('transformArguments', () => { @@ -14,7 +14,7 @@ describe('INFO', () => { testUtils.testWithClient('client.ts.info', async client => { await Promise.all([ client.ts.create('key', { - LABELS: { id: "2" }, + LABELS: { id: '1' }, DUPLICATE_POLICY: TimeSeriesDuplicatePolicies.LAST }), client.ts.create('key2'), @@ -22,29 +22,32 @@ describe('INFO', () => { client.ts.add('key', 1, 10) ]); - assert.deepEqual( - await client.ts.info('key'), - { - totalSamples: 1, - memoryUsage: 4261, - firstTimestamp: 1, - lastTimestamp: 1, - retentionTime: 0, - chunkCount: 1, - chunkSize: 4096, - chunkType: 'compressed', - duplicatePolicy: 'last', - labels: [{ - name: 'id', - value: '2' - }], - rules: [{ - aggregationType: 'COUNT', - key: 'key2', - timeBucket: 5 - }], - sourceKey: null - } - ); + assertInfo(await client.ts.info('key')); }, GLOBAL.SERVERS.OPEN); }); + +export function assertInfo(info: InfoReply): void { + assert.equal(typeof info.totalSamples, 'number'); + assert.equal(typeof info.memoryUsage, 'number'); + assert.equal(typeof info.firstTimestamp, 'number'); + assert.equal(typeof info.lastTimestamp, 'number'); + assert.equal(typeof info.retentionTime, 'number'); + assert.equal(typeof info.chunkCount, 'number'); + assert.equal(typeof info.chunkSize, 'number'); + assert.equal(typeof info.chunkType, 'string'); + assert.equal(typeof info.duplicatePolicy, 'string'); + assert.ok(Array.isArray(info.labels)); + for (const label of info.labels) { + assert.equal(typeof label, 'object'); + assert.equal(typeof label.name, 'string'); + assert.equal(typeof label.value, 'string'); + } + assert.ok(Array.isArray(info.rules)); + for (const rule of info.rules) { + assert.equal(typeof rule, 'object'); + assert.equal(typeof rule.aggregationType, 'string'); + assert.equal(typeof rule.key, 'string'); + assert.equal(typeof rule.timeBucket, 'number'); + } + assert.ok(info.sourceKey === null || typeof info.sourceKey === 'string'); +} diff --git a/packages/time-series/lib/commands/INFO.ts b/packages/time-series/lib/commands/INFO.ts index 00e04a1985a..25ce3ef54ea 100644 --- a/packages/time-series/lib/commands/INFO.ts +++ b/packages/time-series/lib/commands/INFO.ts @@ -9,30 +9,30 @@ export function transformArguments(key: string): Array { } export type InfoRawReply = [ - _: string, - totalSamples: number, - _: string, - memoryUsage: number, - _: string, - firstTimestamp: number, - _: string, - lastTimestamp: number, - _: string, - retentionTime: number, - _: string, - chunkCount: number, - _: string, - chunkSize: number, - _: string, - chunkType: string, - _: string, - duplicatePolicy: TimeSeriesDuplicatePolicies | null, - _: string, - labels: Array<[name: string, value: string]>, - _: string, - sourceKey: string | null, - _: string, - rules: Array<[key: string, timeBucket: number, aggregationType: TimeSeriesAggregationType]> + 'totalSamples', + number, + 'memoryUsage', + number, + 'firstTimestamp', + number, + 'lastTimestamp', + number, + 'retentionTime', + number, + 'chunkCount', + number, + 'chunkSize', + number, + 'chunkType', + string, + 'duplicatePolicy', + TimeSeriesDuplicatePolicies | null, + 'labels', + Array<[name: string, value: string]>, + 'sourceKey', + string | null, + 'rules', + Array<[key: string, timeBucket: number, aggregationType: TimeSeriesAggregationType]> ]; export interface InfoReply { diff --git a/packages/time-series/lib/commands/INFO_DEBUG.spec.ts b/packages/time-series/lib/commands/INFO_DEBUG.spec.ts index 00101d980aa..666689f5194 100644 --- a/packages/time-series/lib/commands/INFO_DEBUG.spec.ts +++ b/packages/time-series/lib/commands/INFO_DEBUG.spec.ts @@ -1,6 +1,7 @@ import { strict as assert } from 'assert'; import { TimeSeriesAggregationType, TimeSeriesDuplicatePolicies } from '.'; import testUtils, { GLOBAL } from '../test-utils'; +import { assertInfo } from './INFO.spec'; import { transformArguments } from './INFO_DEBUG'; describe('INFO_DEBUG', () => { @@ -14,7 +15,7 @@ describe('INFO_DEBUG', () => { testUtils.testWithClient('client.ts.get', async client => { await Promise.all([ client.ts.create('key', { - LABELS: { id: "2" }, + LABELS: { id: '1' }, DUPLICATE_POLICY: TimeSeriesDuplicatePolicies.LAST }), client.ts.create('key2'), @@ -22,37 +23,17 @@ describe('INFO_DEBUG', () => { client.ts.add('key', 1, 10) ]); - assert.deepEqual( - await client.ts.infoDebug('key'), - { - totalSamples: 1, - memoryUsage: 4261, - firstTimestamp: 1, - lastTimestamp: 1, - retentionTime: 0, - chunkCount: 1, - chunkSize: 4096, - chunkType: 'compressed', - duplicatePolicy: 'last', - labels: [{ - name: 'id', - value: '2' - }], - sourceKey: null, - rules: [{ - aggregationType: 'COUNT', - key: 'key2', - timeBucket: 5 - }], - keySelfName: 'key', - chunks: [{ - startTimestamp: 1, - endTimestamp: 1, - samples: 1, - size: 4096, - bytesPerSample: '4096' - }] - } - ); + const infoDebug = await client.ts.infoDebug('key'); + assertInfo(infoDebug); + assert.equal(typeof infoDebug.keySelfName, 'string'); + assert.ok(Array.isArray(infoDebug.chunks)); + for (const chunk of infoDebug.chunks) { + assert.equal(typeof chunk, 'object'); + assert.equal(typeof chunk.startTimestamp, 'number'); + assert.equal(typeof chunk.endTimestamp, 'number'); + assert.equal(typeof chunk.samples, 'number'); + assert.equal(typeof chunk.size, 'number'); + assert.equal(typeof chunk.bytesPerSample, 'string'); + } }, GLOBAL.SERVERS.OPEN); }); diff --git a/packages/time-series/lib/commands/INFO_DEBUG.ts b/packages/time-series/lib/commands/INFO_DEBUG.ts index ad9522d2a60..20d6ff5e242 100644 --- a/packages/time-series/lib/commands/INFO_DEBUG.ts +++ b/packages/time-series/lib/commands/INFO_DEBUG.ts @@ -14,23 +14,23 @@ export function transformArguments(key: string): Array { } type InfoDebugRawReply = [ - ...infoArgs: InfoRawReply, - _: string, - keySelfName: string, - _: string, - chunks: Array<[ - _: string, - startTimestamp: number, - _: string, - endTimestamp: number, - _: string, - samples: number, - _: string, - size: number, - _: string, - bytesPerSample: string + ...InfoRawReply, + 'keySelfName', + string, + 'chunks', + Array<[ + 'startTimestamp', + number, + 'endTimestamp', + number, + 'samples', + number, + 'size', + number, + 'bytesPerSample', + string ]> -] +]; interface InfoDebugReply extends InfoReply { keySelfName: string; diff --git a/packages/time-series/lib/commands/MGET.spec.ts b/packages/time-series/lib/commands/MGET.spec.ts index 7c6c32927d8..61da3b96383 100644 --- a/packages/time-series/lib/commands/MGET.spec.ts +++ b/packages/time-series/lib/commands/MGET.spec.ts @@ -3,11 +3,22 @@ import testUtils, { GLOBAL } from '../test-utils'; import { transformArguments } from './MGET'; describe('MGET', () => { - it('transformArguments', () => { - assert.deepEqual( - transformArguments('label=value'), - ['TS.MGET', 'FILTER', 'label=value'] - ); + describe('transformArguments', () => { + it('without options', () => { + assert.deepEqual( + transformArguments('label=value'), + ['TS.MGET', 'FILTER', 'label=value'] + ); + }); + + it('with LATEST', () => { + assert.deepEqual( + transformArguments('label=value', { + LATEST: true + }), + ['TS.MGET', 'LATEST', 'FILTER', 'label=value'] + ); + }); }); testUtils.testWithClient('client.ts.mGet', async client => { diff --git a/packages/time-series/lib/commands/MGET.ts b/packages/time-series/lib/commands/MGET.ts index 45d970ec810..67315722eb6 100644 --- a/packages/time-series/lib/commands/MGET.ts +++ b/packages/time-series/lib/commands/MGET.ts @@ -1,10 +1,15 @@ import { RedisCommandArguments } from '@redis/client/dist/lib/commands'; -import { Filter, pushFilterArgument, RawLabels, SampleRawReply, SampleReply, transformSampleReply } from '.'; +import { Filter, pushFilterArgument, pushLatestArgument, RawLabels, SampleRawReply, SampleReply, transformSampleReply } from '.'; export const IS_READ_ONLY = true; -export function transformArguments(filter: Filter): RedisCommandArguments { - return pushFilterArgument(['TS.MGET'], filter); +export interface MGetOptions { + LATEST?: boolean; +} + +export function transformArguments(filter: Filter, options?: MGetOptions): RedisCommandArguments { + const args = pushLatestArgument(['TS.MGET'], options?.LATEST); + return pushFilterArgument(args, filter); } export type MGetRawReply = Array<[ diff --git a/packages/time-series/lib/commands/MGET_WITHLABELS.ts b/packages/time-series/lib/commands/MGET_WITHLABELS.ts index b0875cefe0e..232c17a0ada 100644 --- a/packages/time-series/lib/commands/MGET_WITHLABELS.ts +++ b/packages/time-series/lib/commands/MGET_WITHLABELS.ts @@ -7,12 +7,12 @@ import { Filter, pushFilterArgument } from '.'; -import { MGetRawReply, MGetReply } from './MGET'; +import { MGetOptions, MGetRawReply, MGetReply } from './MGET'; import { RedisCommandArguments } from '@redis/client/dist/lib/commands'; export const IS_READ_ONLY = true; -interface MGetWithLabelsOptions { +interface MGetWithLabelsOptions extends MGetOptions { SELECTED_LABELS?: SelectedLabels; } diff --git a/packages/time-series/lib/commands/MRANGE.spec.ts b/packages/time-series/lib/commands/MRANGE.spec.ts index 1913576ce6f..4228cc06fb7 100644 --- a/packages/time-series/lib/commands/MRANGE.spec.ts +++ b/packages/time-series/lib/commands/MRANGE.spec.ts @@ -24,8 +24,8 @@ describe('MRANGE', () => { }, }), ['TS.MRANGE', '-', '+', 'FILTER_BY_TS', '0', 'FILTER_BY_VALUE', '0', '1', - 'COUNT', '1', 'ALIGN', '-', 'AGGREGATION', 'avg', '1', 'FILTER', 'label=value', - 'GROUPBY', 'label', 'REDUCE', 'sum'] + 'COUNT', '1', 'ALIGN', '-', 'AGGREGATION', 'AVG', '1', 'FILTER', 'label=value', + 'GROUPBY', 'label', 'REDUCE', 'SUM'] ); }); diff --git a/packages/time-series/lib/commands/MRANGE_WITHLABELS.spec.ts b/packages/time-series/lib/commands/MRANGE_WITHLABELS.spec.ts index 441124ccc90..983114f840e 100644 --- a/packages/time-series/lib/commands/MRANGE_WITHLABELS.spec.ts +++ b/packages/time-series/lib/commands/MRANGE_WITHLABELS.spec.ts @@ -25,8 +25,8 @@ describe('MRANGE_WITHLABELS', () => { }, }), ['TS.MRANGE', '-', '+', 'FILTER_BY_TS', '0', 'FILTER_BY_VALUE', '0', '1', - 'COUNT', '1', 'ALIGN', '-', 'AGGREGATION', 'avg', '1', 'SELECTED_LABELS', 'label', - 'FILTER', 'label=value', 'GROUPBY', 'label', 'REDUCE', 'sum'] + 'COUNT', '1', 'ALIGN', '-', 'AGGREGATION', 'AVG', '1', 'SELECTED_LABELS', 'label', + 'FILTER', 'label=value', 'GROUPBY', 'label', 'REDUCE', 'SUM'] ); }); diff --git a/packages/time-series/lib/commands/MREVRANGE.spec.ts b/packages/time-series/lib/commands/MREVRANGE.spec.ts index 764fbb4845e..6e5825d36d6 100644 --- a/packages/time-series/lib/commands/MREVRANGE.spec.ts +++ b/packages/time-series/lib/commands/MREVRANGE.spec.ts @@ -24,8 +24,8 @@ describe('MREVRANGE', () => { }, }), ['TS.MREVRANGE', '-', '+', 'FILTER_BY_TS', '0', 'FILTER_BY_VALUE', '0', '1', - 'COUNT', '1', 'ALIGN', '-', 'AGGREGATION', 'avg', '1', 'FILTER', 'label=value', - 'GROUPBY', 'label', 'REDUCE', 'sum'] + 'COUNT', '1', 'ALIGN', '-', 'AGGREGATION', 'AVG', '1', 'FILTER', 'label=value', + 'GROUPBY', 'label', 'REDUCE', 'SUM'] ); }); diff --git a/packages/time-series/lib/commands/MREVRANGE_WITHLABELS.spec.ts b/packages/time-series/lib/commands/MREVRANGE_WITHLABELS.spec.ts index 279bcf65283..7e80e965d4e 100644 --- a/packages/time-series/lib/commands/MREVRANGE_WITHLABELS.spec.ts +++ b/packages/time-series/lib/commands/MREVRANGE_WITHLABELS.spec.ts @@ -25,8 +25,8 @@ describe('MREVRANGE_WITHLABELS', () => { }, }), ['TS.MREVRANGE', '-', '+', 'FILTER_BY_TS', '0', 'FILTER_BY_VALUE', '0', '1', - 'COUNT', '1', 'ALIGN', '-', 'AGGREGATION', 'avg', '1', 'SELECTED_LABELS', 'label', - 'FILTER', 'label=value', 'GROUPBY', 'label', 'REDUCE', 'sum'] + 'COUNT', '1', 'ALIGN', '-', 'AGGREGATION', 'AVG', '1', 'SELECTED_LABELS', 'label', + 'FILTER', 'label=value', 'GROUPBY', 'label', 'REDUCE', 'SUM'] ); }); diff --git a/packages/time-series/lib/commands/RANGE.spec.ts b/packages/time-series/lib/commands/RANGE.spec.ts index 10b2f452bd2..1e6a9958806 100644 --- a/packages/time-series/lib/commands/RANGE.spec.ts +++ b/packages/time-series/lib/commands/RANGE.spec.ts @@ -20,7 +20,7 @@ describe('RANGE', () => { } }), ['TS.RANGE', 'key', '-', '+', 'FILTER_BY_TS', '0', 'FILTER_BY_VALUE', - '1', '2', 'COUNT', '1', 'ALIGN', '-', 'AGGREGATION', 'avg', '1'] + '1', '2', 'COUNT', '1', 'ALIGN', '-', 'AGGREGATION', 'AVG', '1'] ); }); diff --git a/packages/time-series/lib/commands/REVRANGE.spec.ts b/packages/time-series/lib/commands/REVRANGE.spec.ts index ae6722dbf45..ffd90268c81 100644 --- a/packages/time-series/lib/commands/REVRANGE.spec.ts +++ b/packages/time-series/lib/commands/REVRANGE.spec.ts @@ -59,7 +59,7 @@ describe('REVRANGE', () => { timeBucket: 1 } }), - ['TS.REVRANGE', 'key', '-', '+', 'AGGREGATION', 'avg', '1'] + ['TS.REVRANGE', 'key', '-', '+', 'AGGREGATION', 'AVG', '1'] ); }); @@ -80,7 +80,7 @@ describe('REVRANGE', () => { }), [ 'TS.REVRANGE', 'key', '-', '+', 'FILTER_BY_TS', '0', 'FILTER_BY_VALUE', - '1', '2', 'COUNT', '1', 'ALIGN', '-', 'AGGREGATION', 'avg', '1' + '1', '2', 'COUNT', '1', 'ALIGN', '-', 'AGGREGATION', 'AVG', '1' ] ); }); diff --git a/packages/time-series/lib/commands/index.spec.ts b/packages/time-series/lib/commands/index.spec.ts index 8914b1fb131..a29eefe860a 100644 --- a/packages/time-series/lib/commands/index.spec.ts +++ b/packages/time-series/lib/commands/index.spec.ts @@ -6,6 +6,7 @@ import { TimeSeriesEncoding, pushEncodingArgument, pushChunkSizeArgument, + pushDuplicatePolicy, pushLabelsArgument, transformIncrDecrArguments, transformSampleReply, @@ -19,7 +20,10 @@ import { pushMRangeWithLabelsArguments, transformRangeReply, transformMRangeReply, - transformMRangeWithLabelsReply + transformMRangeWithLabelsReply, + TimeSeriesDuplicatePolicies, + pushLatestArgument, + TimeSeriesBucketTimestamp } from '.'; describe('transformTimestampArgument', () => { @@ -87,6 +91,17 @@ describe('pushChunkSizeArgument', () => { }); }); +describe('pushDuplicatePolicy', () => { + testOptionalArgument(pushDuplicatePolicy); + + it('BLOCK', () => { + assert.deepEqual( + pushDuplicatePolicy([], TimeSeriesDuplicatePolicies.BLOCK), + ['DUPLICATE_POLICY', 'BLOCK'] + ); + }); +}); + describe('pushLabelsArgument', () => { testOptionalArgument(pushLabelsArgument); @@ -202,16 +217,44 @@ describe('pushRangeArguments', () => { ); }); - it('with AGGREGATION', () => { - assert.deepEqual( - pushRangeArguments([], '-', '+', { - AGGREGATION: { - type: TimeSeriesAggregationType.FIRST, - timeBucket: 1 - } - }), - ['-', '+', 'AGGREGATION', 'first', '1'] - ); + describe('with AGGREGATION', () => { + it('without options', () => { + assert.deepEqual( + pushRangeArguments([], '-', '+', { + AGGREGATION: { + type: TimeSeriesAggregationType.FIRST, + timeBucket: 1 + } + }), + ['-', '+', 'AGGREGATION', 'FIRST', '1'] + ); + }); + + it('with BUCKETTIMESTAMP', () => { + assert.deepEqual( + pushRangeArguments([], '-', '+', { + AGGREGATION: { + type: TimeSeriesAggregationType.FIRST, + timeBucket: 1, + BUCKETTIMESTAMP: TimeSeriesBucketTimestamp.LOW + } + }), + ['-', '+', 'AGGREGATION', 'FIRST', '1', 'BUCKETTIMESTAMP', '-'] + ); + }); + + it('with BUCKETTIMESTAMP', () => { + assert.deepEqual( + pushRangeArguments([], '-', '+', { + AGGREGATION: { + type: TimeSeriesAggregationType.FIRST, + timeBucket: 1, + EMPTY: true + } + }), + ['-', '+', 'AGGREGATION', 'FIRST', '1', 'EMPTY'] + ); + }); }); it('with FILTER_BY_TS, FILTER_BY_VALUE, COUNT, ALIGN, AGGREGATION', () => { @@ -226,11 +269,13 @@ describe('pushRangeArguments', () => { ALIGN: 1, AGGREGATION: { type: TimeSeriesAggregationType.FIRST, - timeBucket: 1 + timeBucket: 1, + BUCKETTIMESTAMP: TimeSeriesBucketTimestamp.LOW, + EMPTY: true } }), ['-', '+', 'FILTER_BY_TS', 'ts', 'FILTER_BY_VALUE', '1', '2', - 'COUNT', '1', 'ALIGN', '1', 'AGGREGATION', 'first', '1'] + 'COUNT', '1', 'ALIGN', '1', 'AGGREGATION', 'FIRST', '1', 'BUCKETTIMESTAMP', '-', 'EMPTY'] ); }); }); @@ -249,7 +294,7 @@ describe('pushMRangeGroupByArguments', () => { label: 'label', reducer: TimeSeriesReducers.MAXIMUM }), - ['GROUPBY', 'label', 'REDUCE', 'max'] + ['GROUPBY', 'label', 'REDUCE', 'MAX'] ); }); }); @@ -286,7 +331,7 @@ describe('pushMRangeArguments', () => { reducer: TimeSeriesReducers.MAXIMUM } }), - ['-', '+', 'FILTER', 'label=value', 'GROUPBY', 'label', 'REDUCE', 'max'] + ['-', '+', 'FILTER', 'label=value', 'GROUPBY', 'label', 'REDUCE', 'MAX'] ); }); }); @@ -369,3 +414,26 @@ describe('transformMRangeWithLabelsReply', () => { }] ); }); + +describe('pushLatestArgument', () => { + it('undefined', () => { + assert.deepEqual( + pushLatestArgument([]), + [] + ); + }); + + it('false', () => { + assert.deepEqual( + pushLatestArgument([], false), + [] + ); + }); + + it('true', () => { + assert.deepEqual( + pushLatestArgument([], true), + ['LATEST'] + ); + }); +}) diff --git a/packages/time-series/lib/commands/index.ts b/packages/time-series/lib/commands/index.ts index aba3ae2de9f..19cd075ba49 100644 --- a/packages/time-series/lib/commands/index.ts +++ b/packages/time-series/lib/commands/index.ts @@ -68,18 +68,25 @@ export default { }; export enum TimeSeriesAggregationType { - AVERAGE = 'avg', - SUM = 'sum', - MINIMUM = 'min', - MAXIMUM = 'max', - RANGE = 'range', - COUNT = 'count', - FIRST = 'first', - LAST = 'last', - STD_P = 'std.p', - STD_S = 'std.s', - VAR_P = 'var.p', - VAR_S = 'var.s' + AVG = 'AVG', + // @deprecated + AVERAGE = 'AVG', + FIRST = 'FIRST', + LAST = 'LAST', + MIN = 'MIN', + // @deprecated + MINIMUM = 'MIN', + MAX = 'MAX', + // @deprecated + MAXIMUM = 'MAX', + SUM = 'SUM', + RANGE = 'RANGE', + COUNT = 'COUNT', + STD_P = 'STD.P', + STD_S = 'STD.S', + VAR_P = 'VAR.P', + VAR_S = 'VAR.S', + TWA = 'TWA' } export enum TimeSeriesDuplicatePolicies { @@ -92,9 +99,20 @@ export enum TimeSeriesDuplicatePolicies { } export enum TimeSeriesReducers { - SUM = 'sum', - MINIMUM = 'min', - MAXIMUM = 'max', + AVG = 'AVG', + SUM = 'SUM', + MIN = 'MIN', + // @deprecated + MINIMUM = 'MIN', + MAX = 'MAX', + // @deprecated + MAXIMUM = 'MAX', + RANGE = 'range', + COUNT = 'COUNT', + STD_P = 'STD.P', + STD_S = 'STD.S', + VAR_P = 'VAR.P', + VAR_S = 'VAR.S', } export type Timestamp = number | Date | string; @@ -147,6 +165,17 @@ export function pushChunkSizeArgument(args: RedisCommandArguments, chunkSize?: n return args; } +export function pushDuplicatePolicy(args: RedisCommandArguments, duplicatePolicy?: TimeSeriesDuplicatePolicies): RedisCommandArguments { + if (duplicatePolicy) { + args.push( + 'DUPLICATE_POLICY', + duplicatePolicy + ); + } + + return args; +} + export type RawLabels = Array<[label: string, value: string]>; export type Labels = { @@ -226,7 +255,14 @@ export function transformSampleReply(reply: SampleRawReply): SampleReply { }; } +export enum TimeSeriesBucketTimestamp { + LOW = '-', + HIGH = '+', + MID = '~' +} + export interface RangeOptions { + LATEST?: boolean; FILTER_BY_TS?: Array; FILTER_BY_VALUE?: { min: number; @@ -237,6 +273,8 @@ export interface RangeOptions { AGGREGATION?: { type: TimeSeriesAggregationType; timeBucket: Timestamp; + BUCKETTIMESTAMP?: TimeSeriesBucketTimestamp; + EMPTY?: boolean; }; } @@ -251,9 +289,11 @@ export function pushRangeArguments( transformTimestampArgument(toTimestamp) ); + pushLatestArgument(args, options?.LATEST); + if (options?.FILTER_BY_TS) { args.push('FILTER_BY_TS'); - for(const ts of options.FILTER_BY_TS) { + for (const ts of options.FILTER_BY_TS) { args.push(transformTimestampArgument(ts)); } } @@ -286,6 +326,17 @@ export function pushRangeArguments( options.AGGREGATION.type, transformTimestampArgument(options.AGGREGATION.timeBucket) ); + + if (options.AGGREGATION.BUCKETTIMESTAMP) { + args.push( + 'BUCKETTIMESTAMP', + options.AGGREGATION.BUCKETTIMESTAMP + ); + } + + if (options.AGGREGATION.EMPTY) { + args.push('EMPTY'); + } } return args; @@ -406,3 +457,11 @@ export function transformMRangeWithLabelsReply(reply: MRangeRawReply): Array Date: Tue, 6 Sep 2022 03:40:40 -0400 Subject: [PATCH 1248/1748] Release json@1.0.4 --- packages/json/package.json | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/packages/json/package.json b/packages/json/package.json index eab3313c98d..4d0f0048bbb 100644 --- a/packages/json/package.json +++ b/packages/json/package.json @@ -1,6 +1,6 @@ { "name": "@redis/json", - "version": "1.0.3", + "version": "1.0.4", "license": "MIT", "main": "./dist/index.js", "types": "./dist/index.d.ts", From 0a37e475b4ec23282fc6dac9f59ecc9912b72f86 Mon Sep 17 00:00:00 2001 From: leibale Date: Tue, 6 Sep 2022 03:42:52 -0400 Subject: [PATCH 1249/1748] upgrade json --- package-lock.json | 4 ++-- package.json | 2 +- 2 files changed, 3 insertions(+), 3 deletions(-) diff --git a/package-lock.json b/package-lock.json index 77e9230eba0..a4a835e57f1 100644 --- a/package-lock.json +++ b/package-lock.json @@ -15,7 +15,7 @@ "@redis/bloom": "1.0.2", "@redis/client": "1.3.0", "@redis/graph": "1.0.1", - "@redis/json": "1.0.3", + "@redis/json": "1.0.4", "@redis/search": "1.1.0", "@redis/time-series": "1.0.3" }, @@ -8257,7 +8257,7 @@ }, "packages/json": { "name": "@redis/json", - "version": "1.0.3", + "version": "1.0.4", "license": "MIT", "devDependencies": { "@istanbuljs/nyc-config-typescript": "^1.0.2", diff --git a/package.json b/package.json index 403948c0c77..59d525edff0 100644 --- a/package.json +++ b/package.json @@ -26,7 +26,7 @@ "@redis/bloom": "1.0.2", "@redis/client": "1.3.0", "@redis/graph": "1.0.1", - "@redis/json": "1.0.3", + "@redis/json": "1.0.4", "@redis/search": "1.1.0", "@redis/time-series": "1.0.3" }, From def9f161e5d63657b5793e66d0db451020781d17 Mon Sep 17 00:00:00 2001 From: leibale Date: Tue, 6 Sep 2022 03:43:21 -0400 Subject: [PATCH 1250/1748] Release redis@4.3.1 --- package-lock.json | 4 ++-- package.json | 2 +- 2 files changed, 3 insertions(+), 3 deletions(-) diff --git a/package-lock.json b/package-lock.json index a4a835e57f1..27a340a5cfc 100644 --- a/package-lock.json +++ b/package-lock.json @@ -1,12 +1,12 @@ { "name": "redis", - "version": "4.3.0", + "version": "4.3.1", "lockfileVersion": 2, "requires": true, "packages": { "": { "name": "redis", - "version": "4.3.0", + "version": "4.3.1", "license": "MIT", "workspaces": [ "./packages/*" diff --git a/package.json b/package.json index 59d525edff0..dc70e250b2e 100644 --- a/package.json +++ b/package.json @@ -1,7 +1,7 @@ { "name": "redis", "description": "A modern, high performance Redis client", - "version": "4.3.0", + "version": "4.3.1", "license": "MIT", "main": "./dist/index.js", "types": "./dist/index.d.ts", From e1c0580d65bede850c55318bcd9c62a68ad9c75e Mon Sep 17 00:00:00 2001 From: Roman Poleguev <30798101+Pomkaize@users.noreply.github.com> Date: Mon, 19 Sep 2022 20:31:21 +0300 Subject: [PATCH 1251/1748] Fix CLUSTER_NODES ipv6 address parsing (#2269) --- .../client/lib/commands/CLUSTER_NODES.spec.ts | 25 +++++++++++++++++++ packages/client/lib/commands/CLUSTER_NODES.ts | 2 +- 2 files changed, 26 insertions(+), 1 deletion(-) diff --git a/packages/client/lib/commands/CLUSTER_NODES.spec.ts b/packages/client/lib/commands/CLUSTER_NODES.spec.ts index 8e0efc6b1ca..5c6cb74d6cb 100644 --- a/packages/client/lib/commands/CLUSTER_NODES.spec.ts +++ b/packages/client/lib/commands/CLUSTER_NODES.spec.ts @@ -73,6 +73,31 @@ describe('CLUSTER NODES', () => { ); }); + it('should support ipv6 addresses', () => { + assert.deepEqual( + transformReply( + 'id 2a02:6b8:c21:330d:0:1589:ebbe:b1a0:6379@16379 master - 0 0 0 connected 0-549\n' + ), + [{ + id: 'id', + address: '2a02:6b8:c21:330d:0:1589:ebbe:b1a0:6379@16379', + host: '2a02:6b8:c21:330d:0:1589:ebbe:b1a0', + port: 6379, + cport: 16379, + flags: ['master'], + pingSent: 0, + pongRecv: 0, + configEpoch: 0, + linkState: RedisClusterNodeLinkStates.CONNECTED, + slots: [{ + from: 0, + to: 549 + }], + replicas: [] + }] + ); + }); + it.skip('with importing slots', () => { assert.deepEqual( transformReply( diff --git a/packages/client/lib/commands/CLUSTER_NODES.ts b/packages/client/lib/commands/CLUSTER_NODES.ts index 846bbccf10d..7c433da5f12 100644 --- a/packages/client/lib/commands/CLUSTER_NODES.ts +++ b/packages/client/lib/commands/CLUSTER_NODES.ts @@ -85,7 +85,7 @@ export function transformReply(reply: string): Array { } function transformNodeAddress(address: string): RedisClusterNodeAddress { - const indexOfColon = address.indexOf(':'), + const indexOfColon = address.lastIndexOf(':'), indexOfAt = address.indexOf('@', indexOfColon), host = address.substring(0, indexOfColon); From 2a8e11a51d4f965b2d902bd8e6b041f04d984182 Mon Sep 17 00:00:00 2001 From: Kien Dang Date: Thu, 22 Sep 2022 04:05:16 +0700 Subject: [PATCH 1252/1748] Export SetOptions type in redis/client command (#2268) * Export setOptions type * Export setOptions type in commands * Revert "Export setOptions type in commands" * Export SetOptions type in redis client * Export SetOptions type in @redis/client * Fix lint --- packages/client/index.ts | 2 ++ packages/client/lib/commands/SET.ts | 2 +- 2 files changed, 3 insertions(+), 1 deletion(-) diff --git a/packages/client/index.ts b/packages/client/index.ts index 734c15664df..7e2d25f6d1b 100644 --- a/packages/client/index.ts +++ b/packages/client/index.ts @@ -18,3 +18,5 @@ export { defineScript } from './lib/lua-script'; export { GeoReplyWith } from './lib/commands/generic-transformers'; export * from './lib/errors'; + +export { SetOptions } from "./lib/commands/SET"; diff --git a/packages/client/lib/commands/SET.ts b/packages/client/lib/commands/SET.ts index 3f9f568f23a..7c679b7084e 100644 --- a/packages/client/lib/commands/SET.ts +++ b/packages/client/lib/commands/SET.ts @@ -22,7 +22,7 @@ interface SetCommonOptions { GET?: true; } -type SetOptions = SetTTL & SetGuards & SetCommonOptions; +export type SetOptions = SetTTL & SetGuards & SetCommonOptions; export function transformArguments( key: RedisCommandArgument, From 9398e5d05e5d0ac920e0b8ce12f56499def20029 Mon Sep 17 00:00:00 2001 From: ade1705 Date: Fri, 14 Oct 2022 15:34:06 -0400 Subject: [PATCH 1253/1748] =?UTF-8?q?#2285-Add-example-scripts-hyperloglog?= =?UTF-8?q?=20-=20Added=20hyperloglog=20examples=20to=E2=80=A6=20(#2289)?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit * #2285-Add-example-scripts-hyperloglog - Added hyperloglog examples to address Issue #2285 * #2285-Add-example-scripts-hyperloglog - Added the results as comments * #2285-Add-example-scripts-hyperloglog - Changes from review * #2285-Add-example-scripts-hyperloglog - Changes from review --- examples/README.md | 1 + examples/hyperloglog.js | 51 +++++++++++++++++++++++++++++++++++++++++ 2 files changed, 52 insertions(+) create mode 100644 examples/hyperloglog.js diff --git a/examples/README.md b/examples/README.md index 089096d719b..93839c88251 100644 --- a/examples/README.md +++ b/examples/README.md @@ -11,6 +11,7 @@ This folder contains example scripts showing how to use Node Redis in different | `count-min-sketch.js` | Estimate the frequency of a given event using the [RedisBloom](https://redisbloom.io) Count-Min Sketch | | `cuckoo-filter.js` | Space efficient set membership checks with a [Cuckoo Filter](https://en.wikipedia.org/wiki/Cuckoo_filter) using [RedisBloom](https://redisbloom.io) | | `get-server-time.js` | Get the time from the Redis server | +| `hyperloglog.js` | Showing use of Hyperloglog commands [PFADD, PFCOUNT and PFMERGE](https://redis.io/commands/?group=hyperloglog) | | `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 | diff --git a/examples/hyperloglog.js b/examples/hyperloglog.js new file mode 100644 index 00000000000..4ac9b575f96 --- /dev/null +++ b/examples/hyperloglog.js @@ -0,0 +1,51 @@ +// Example to log traffic data at intersections for the city of San Francisco. +// Log license plates of each car scanned at each intersection and add to the intersections Hyperloglog. +// Reference: https://www.youtube.com/watch?v=MunL8nnwscQ + +import { createClient } from 'redis'; + +const client = createClient(); + +await client.connect(); + +// Use `pfAdd` to add an element to a Hyperloglog, creating the Hyperloglog if necessary. +// await client.pfAdd(key, value) + +// To get a count, the `pfCount` method is used. +// await client.pfCount(key) + +try { + // Corner of Market Street (ID: 12) and 10th street (ID:27). + await client.pfAdd('count:sf:12:27', 'GHN34X'); + await client.pfAdd('count:sf:12:27', 'ECN94Y'); + await client.pfAdd('count:sf:12:27', 'VJL12V'); + await client.pfAdd('count:sf:12:27', 'ORV87O'); + + // To get the count of Corner of Market Street (ID: 12) and 10th street (ID:27). + const countForMarket10thStreet = await client.pfCount('count:sf:12:27'); + console.log(`Count for Market Street & 10th Street is ${countForMarket10thStreet}`); + // Count for Market Street & 10th Street is 4. + + // Corner of Market Street (ID: 12) and 11 street (ID:26). + await client.pfAdd('count:sf:12:26', 'GHN34X'); + await client.pfAdd('count:sf:12:26', 'ECN94Y'); + await client.pfAdd('count:sf:12:26', 'IRV84E'); + await client.pfAdd('count:sf:12:26', 'ORV87O'); + await client.pfAdd('count:sf:12:26', 'TEY34S'); + + // To get the count of Corner of Market Street (ID: 12) and 11th street (ID:26). + const countForMarket11thStreet = await client.pfCount('count:sf:12:26'); + console.log(`Count for Market Street & 11th Street is ${countForMarket11thStreet}`); + // Count for Market Street & 11th Street is 5. + + // To merge the Hyperloglogs `count:sf:12:26` and `count:sf:12:27`. + await client.pfMerge('count:merge', ['count:sf:12:27', 'count:sf:12:26']); + const countMerge = await client.pfCount('count:merge'); + console.log(`Count for the merge is ${countMerge}`); + // Count for the merge is 6. +} catch (e) { + // something went wrong. + console.error(e); +} + +await client.quit(); From d0bfa771a85b61fbdfda0401bc422d98ea1103a9 Mon Sep 17 00:00:00 2001 From: Mark <79023646+con-mark@users.noreply.github.com> Date: Fri, 14 Oct 2022 23:07:28 +0100 Subject: [PATCH 1254/1748] #2287 Add example scripts showing pub/sub usage. (#2288) * #2287 Add example scripts showing pub/sub usage. https://github.com/redis/node-redis/issues/2287 * #2287 Add example scripts showing pub/sub usage. Fixing comments requested Adding client.connect() to pubsub-subscriber.js Reformatting Readme updating logging in pubsub-publisher.js * #2287 Add example scripts showing pub/sub usage. Fix publish and subscriber Update tidy up comments * Update examples/pubsub-subscriber.js Making suggested changes Co-authored-by: Simon Prickett Co-authored-by: Simon Prickett Closes #2287. --- examples/README.md | 2 ++ examples/pubsub-publisher.js | 20 +++++++++++++++++ examples/pubsub-subscriber.js | 41 +++++++++++++++++++++++++++++++++++ 3 files changed, 63 insertions(+) create mode 100644 examples/pubsub-publisher.js create mode 100644 examples/pubsub-subscriber.js diff --git a/examples/README.md b/examples/README.md index 93839c88251..83bbe75438e 100644 --- a/examples/README.md +++ b/examples/README.md @@ -14,6 +14,8 @@ This folder contains example scripts showing how to use Node Redis in different | `hyperloglog.js` | Showing use of Hyperloglog commands [PFADD, PFCOUNT and PFMERGE](https://redis.io/commands/?group=hyperloglog) | | `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/) | +| `pubsub-publisher.js` | Adds multiple messages on 2 different channels messages to Redis | +| `pubsub-subscriber.js` | Reads messages from channels using `PSUBSCRIBE` command | | `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 | diff --git a/examples/pubsub-publisher.js b/examples/pubsub-publisher.js new file mode 100644 index 00000000000..354e0ae2f0a --- /dev/null +++ b/examples/pubsub-publisher.js @@ -0,0 +1,20 @@ +// A sample publisher using the publish function to put message on different channels. +// https://redis.io/commands/publish/ +import { createClient } from 'redis'; + +const client = createClient(); + +await client.connect(); + +// Declare constant variables for the name of the clients we will publish to as they will be required for logging. +const channel1 = 'chan1nel'; +const channel2 = 'chan2nel'; + +for (let i = 0; i < 10000; i++) { + // 1st channel created to publish 10000 messages. + await client.publish(channel1, `channel1_message_${i}`); + console.log(`publishing message on ${channel1}`); + // 2nd channel created to publish 10000 messages. + await client.publish(channel2, `channel2_message_${i}`); + console.log(`publishing message on ${channel2}`); +} diff --git a/examples/pubsub-subscriber.js b/examples/pubsub-subscriber.js new file mode 100644 index 00000000000..ff4c05f083e --- /dev/null +++ b/examples/pubsub-subscriber.js @@ -0,0 +1,41 @@ +// A sample subscriber showing how the subscribe method and pSubscribe method work. +// https://redis.io/commands/subscribe/ +// https://redis.io/commands/pSubscribe/ +// This consumes messages published by pubsub-publisher.js + +import { createClient} from 'redis'; + +// Create and connect client before executing any Redis commands. +const client = createClient(); +await client.connect(); + +// Each subscriber needs to connect individually therefore we duplicate the client. +const channel1Sub = client.duplicate(); +const channel2Sub = client.duplicate(); +const noChannelsSub = client.duplicate(); +const allChannelsSub = client.duplicate(); + +await channel1Sub.connect(); +await channel2Sub.connect(); +await noChannelsSub.connect(); +await allChannelsSub.connect(); + +// This subscriber only will receive messages from channel 1 as they are using the subscribe method and subscribed to chan1nel. +await channel1Sub.subscribe('chan1nel', (message) => { + console.log(`Channel1 subscriber collected message: ${message}`); +},true); + +// This subscriber only will receive messages from channel 2 as they are using the subscribe method and subscribed to chan2nel. +await channel2Sub.subscribe('chan2nel', (message) => { + console.log(`Channel2 subscriber collected message: ${message}`); +},true); + +// This subscriber will not receive any messages as its channel does not exist. +await noChannelsSub.subscribe('chan*nel', (message) => { + console.log(`This message will never be seen as we are not using pSubscribe here. ${message}`); +},true); + +// This subscriber receive messages from both channel 1 and channel 2 using the pSubscribe method. +await allChannelsSub.pSubscribe('chan*nel', (message, channel) => { + console.log(`Channel ${channel} sent message: ${message}`); +},true); \ No newline at end of file From 1eed12ec65d5bb8b606e41ff5993f40a5847957e Mon Sep 17 00:00:00 2001 From: Varad Karpe Date: Wed, 19 Oct 2022 21:02:07 +0900 Subject: [PATCH 1255/1748] Connecting to cluster example (#2298) * Connecting to cluster example * Connecting to cluster example * Making changes according to review * Adding example to Readme.md in alphabetical order * Fixed order of scripts so they are alphabetic. Co-authored-by: Simon Prickett Closes #2281. --- examples/README.md | 1 + examples/connect-to-cluster.js | 33 +++++++++++++++++++++++++++++++++ 2 files changed, 34 insertions(+) create mode 100644 examples/connect-to-cluster.js diff --git a/examples/README.md b/examples/README.md index 83bbe75438e..b972549202f 100644 --- a/examples/README.md +++ b/examples/README.md @@ -8,6 +8,7 @@ This folder contains example scripts showing how to use Node Redis in different | `bloom-filter.js` | Space efficient set membership checks with a [Bloom Filter](https://en.wikipedia.org/wiki/Bloom_filter) using [RedisBloom](https://redisbloom.io) | | `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 | +| `connect-to-cluster.js` | Connect to Redis cluster | | `count-min-sketch.js` | Estimate the frequency of a given event using the [RedisBloom](https://redisbloom.io) Count-Min Sketch | | `cuckoo-filter.js` | Space efficient set membership checks with a [Cuckoo Filter](https://en.wikipedia.org/wiki/Cuckoo_filter) using [RedisBloom](https://redisbloom.io) | | `get-server-time.js` | Get the time from the Redis server | diff --git a/examples/connect-to-cluster.js b/examples/connect-to-cluster.js new file mode 100644 index 00000000000..98655497c9e --- /dev/null +++ b/examples/connect-to-cluster.js @@ -0,0 +1,33 @@ +// This is an example script to connect to a running cluster. +// After connecting to the cluster the code sets and get a value. + +// To setup this cluster you can follow the guide here: +// https://redis.io/docs/manual/scaling/ +// In this guide the ports which are being used are 7000 - 7005 + + +import { createCluster } from 'redis'; + +const cluster = createCluster({ + rootNodes : [ + { + url : 'redis://127.0.0.1:7001' + }, + { + url : 'redis://127.0.0.1:7002' + }, + { + url : 'redis://127.0.0.1:7003' + } + ] +}); + +cluster.on('error', (err) => console.log('Redis Client Error', err)); + +await cluster.connect(); + +await cluster.set('hello', 'cluster'); +const value = await cluster.get('hello'); +console.log(value); + +await cluster.quit(); From 64e982d2bfd3458e160e79d96e85130e60c14d4a Mon Sep 17 00:00:00 2001 From: Sandeep Parmar Date: Wed, 19 Oct 2022 22:20:57 +0530 Subject: [PATCH 1256/1748] Adds transaction with watched key example script. (#2297) * transction example improved * readme fixed * delay added for watched key changes * pooling error fixed on recursion * Minor comment update. Co-authored-by: Ajay Co-authored-by: Simon Prickett Closes #2280. --- examples/README.md | 38 ++++++++++++++-------------- examples/transaction-with-watch.js | 40 ++++++++++++++++++++++++++++++ 2 files changed, 59 insertions(+), 19 deletions(-) create mode 100644 examples/transaction-with-watch.js diff --git a/examples/README.md b/examples/README.md index b972549202f..7de209d2c85 100644 --- a/examples/README.md +++ b/examples/README.md @@ -3,7 +3,7 @@ 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 | | `bloom-filter.js` | Space efficient set membership checks with a [Bloom Filter](https://en.wikipedia.org/wiki/Bloom_filter) using [RedisBloom](https://redisbloom.io) | | `command-with-modifiers.js` | Define a script that allows to run a command with several modifiers | @@ -12,7 +12,7 @@ This folder contains example scripts showing how to use Node Redis in different | `count-min-sketch.js` | Estimate the frequency of a given event using the [RedisBloom](https://redisbloom.io) Count-Min Sketch | | `cuckoo-filter.js` | Space efficient set membership checks with a [Cuckoo Filter](https://en.wikipedia.org/wiki/Cuckoo_filter) using [RedisBloom](https://redisbloom.io) | | `get-server-time.js` | Get the time from the Redis server | -| `hyperloglog.js` | Showing use of Hyperloglog commands [PFADD, PFCOUNT and PFMERGE](https://redis.io/commands/?group=hyperloglog) | +| `hyperloglog.js` | Showing use of Hyperloglog commands [PFADD, PFCOUNT and PFMERGE](https://redis.io/commands/?group=hyperloglog) | | `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/) | | `pubsub-publisher.js` | Adds multiple messages on 2 different channels messages to Redis | @@ -26,10 +26,11 @@ This folder contains example scripts showing how to use Node Redis in different | `time-series.js` | Create, populate and query timeseries data with [Redis Timeseries](https://redistimeseries.io) | | `topk.js` | Use the [RedisBloom](https://redisbloom.io) TopK to track the most frequently seen items. | | `stream-consumer-group.js` | Reads entties from a [Redis Stream](https://redis.io/topics/streams-intro) as part of a consumer group using the blocking `XREADGROUP` command | +| `transaction-with-watch.js` | An Example of [Redis transaction](https://redis.io/docs/manual/transactions) with `WATCH` command on isolated connection with optimistic locking | ## Contributing -We'd love to see more examples here. If you have an idea that you'd like to see included here, submit a Pull Request and we'll be sure to review it! Don't forget to check out our [contributing guide](../CONTRIBUTING.md). +We'd love to see more examples here. If you have an idea that you'd like to see included here, submit a Pull Request and we'll be sure to review it! Don't forget to check out our [contributing guide](../CONTRIBUTING.md). ## Setup @@ -47,21 +48,21 @@ $ npm install When adding a new example, please follow these guidelines: -* Add your code in a single JavaScript or TypeScript file per example, directly in the `examples` folder -* Do not introduce other dependencies in your example -* Give your `.js` file a meaningful name using `-` separators e.g. `adding-to-a-stream.js` / `adding-to-a-stream.ts` -* Indent your code using 2 spaces -* Use the single line `//` comment style and comment your code -* Add a comment at the top of your `.js` / `.ts` file describing what your example does -* Add a comment at the top of your `.js` / `.ts` file describing any Redis commands that need to be run to set up data for your example (try and keep this minimal) -* Use semicolons -* Use `async` and `await` -* Use single quotes, `'hello'` not `"hello"` -* Use [template literals](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Template_literals) when embedding expressions in strings -* Unless your example requires a connection string, assume Redis is on the default localhost port 6379 with no password -* Use meaningful example data, let's not use `foo`, `bar`, `baz` etc! -* Leave an empty line at the end of your `.js` file -* Update this `README.md` file to add your example to the table +- Add your code in a single JavaScript or TypeScript file per example, directly in the `examples` folder +- Do not introduce other dependencies in your example +- Give your `.js` file a meaningful name using `-` separators e.g. `adding-to-a-stream.js` / `adding-to-a-stream.ts` +- Indent your code using 2 spaces +- Use the single line `//` comment style and comment your code +- Add a comment at the top of your `.js` / `.ts` file describing what your example does +- Add a comment at the top of your `.js` / `.ts` file describing any Redis commands that need to be run to set up data for your example (try and keep this minimal) +- Use semicolons +- Use `async` and `await` +- Use single quotes, `'hello'` not `"hello"` +- Use [template literals](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Template_literals) when embedding expressions in strings +- Unless your example requires a connection string, assume Redis is on the default localhost port 6379 with no password +- Use meaningful example data, let's not use `foo`, `bar`, `baz` etc! +- Leave an empty line at the end of your `.js` file +- Update this `README.md` file to add your example to the table Use [connect-as-acl-user.js](./connect-as-acl-user.js) as a guide to develop a well formatted example script. @@ -87,5 +88,4 @@ await client.connect(); // Add your example code here... await client.quit(); - ``` diff --git a/examples/transaction-with-watch.js b/examples/transaction-with-watch.js new file mode 100644 index 00000000000..d92b910dfa3 --- /dev/null +++ b/examples/transaction-with-watch.js @@ -0,0 +1,40 @@ +import { createClient, WatchError } from 'redis'; + +const delay = (ms) => new Promise((resolve) => setTimeout(resolve, ms)); +const client = createClient(); +await client.connect(); + +function restrictFunctionCalls(fn, maxCalls) { + let count = 1; + return function (...args) { + return count++ < maxCalls ? fn(...args) : false; + }; +} + +const fn = restrictFunctionCalls(transaction, 4); + +async function transaction() { + try { + await client.executeIsolated(async (isolatedClient) => { + await isolatedClient.watch('paymentId:1259'); + const multi = isolatedClient + .multi() + .set('paymentId:1259', 'Payment Successfully Completed!') + .set('paymentId:1260', 'Refund Processed Successfully!'); + await delay(5000); // Do some changes to the watched key during this time... + await multi.exec(); + console.log('Transaction completed Successfully!'); + client.quit(); + }); + } catch (error) { + if (error instanceof WatchError) { + console.log('Transaction Failed Due To Concurrent Modification!'); + fn(); + } else { + console.log(`Error: ${error}`); + client.quit(); + } + } +} + +transaction(); From 4cfad3dab23ced5c5733b74b291df0e543243751 Mon Sep 17 00:00:00 2001 From: Jay Koontz Date: Tue, 25 Oct 2022 06:20:55 -0500 Subject: [PATCH 1257/1748] Update RedisGraph README.md (#2239) * Update README.md Simple example using Cypher to CREATE a graph with relationships and then MATCH on that graph * Update README.md Co-authored-by: Leibale Eidelman --- packages/graph/README.md | 34 ++++++++++++++++++++++++++++++++++ 1 file changed, 34 insertions(+) diff --git a/packages/graph/README.md b/packages/graph/README.md index 420d18851d7..595e0226b25 100644 --- a/packages/graph/README.md +++ b/packages/graph/README.md @@ -1 +1,35 @@ # @redis/graph + +Example usage: +```javascript +import { createClient } from 'redis'; + +const client = createClient(); +client.on('error', (err) => console.log('Redis Client Error', err)); + +await client.connect(); + +await client.graph.query( + 'graph', + "CREATE (:Rider { name: 'Buzz Aldrin' })-[:rides]->(:Team { name: 'Apollo' })" +); + +const result = await client.graph.query( + 'graph', + `MATCH (r:Rider)-[:rides]->(t:Team) WHERE t.name = 'Apollo' RETURN r.name, t.name` +); + +console.log(result); +``` + +Output from console log: +```json +{ + headers: [ 'r.name', 't.name' ], + data: [ [ 'Buzz Aldrin', 'Apollo' ] ], + metadata: [ + 'Cached execution: 0', + 'Query internal execution time: 0.431700 milliseconds' + ] +} +``` From 29f734f60e73878b96dec819c557dc6bfd81570c Mon Sep 17 00:00:00 2001 From: Leibale Eidelman Date: Wed, 26 Oct 2022 14:47:10 -0400 Subject: [PATCH 1258/1748] upgrade dependencies (#2306) --- package-lock.json | 5016 +++++++++++++---------------- packages/bloom/package.json | 8 +- packages/client/package.json | 20 +- packages/graph/package.json | 8 +- packages/json/package.json | 8 +- packages/search/package.json | 8 +- packages/test-utils/package.json | 12 +- packages/time-series/package.json | 8 +- 8 files changed, 2293 insertions(+), 2795 deletions(-) diff --git a/package-lock.json b/package-lock.json index 27a340a5cfc..ef18533b4f0 100644 --- a/package-lock.json +++ b/package-lock.json @@ -52,30 +52,30 @@ } }, "node_modules/@babel/compat-data": { - "version": "7.18.13", - "resolved": "https://registry.npmjs.org/@babel/compat-data/-/compat-data-7.18.13.tgz", - "integrity": "sha512-5yUzC5LqyTFp2HLmDoxGQelcdYgSpP9xsnMWBphAscOdFrHSAVbLNzWiy32sVNDqJRDiJK6klfDnAgu6PAGSHw==", + "version": "7.19.4", + "resolved": "https://registry.npmjs.org/@babel/compat-data/-/compat-data-7.19.4.tgz", + "integrity": "sha512-CHIGpJcUQ5lU9KrPHTjBMhVwQG6CQjxfg36fGXl3qk/Gik1WwWachaXFuo0uCWJT/mStOKtcbFJCaVLihC1CMw==", "dev": true, "engines": { "node": ">=6.9.0" } }, "node_modules/@babel/core": { - "version": "7.18.13", - "resolved": "https://registry.npmjs.org/@babel/core/-/core-7.18.13.tgz", - "integrity": "sha512-ZisbOvRRusFktksHSG6pjj1CSvkPkcZq/KHD45LAkVP/oiHJkNBZWfpvlLmX8OtHDG8IuzsFlVRWo08w7Qxn0A==", + "version": "7.19.6", + "resolved": "https://registry.npmjs.org/@babel/core/-/core-7.19.6.tgz", + "integrity": "sha512-D2Ue4KHpc6Ys2+AxpIx1BZ8+UegLLLE2p3KJEuJRKmokHOtl49jQ5ny1773KsGLZs8MQvBidAF6yWUJxRqtKtg==", "dev": true, "dependencies": { "@ampproject/remapping": "^2.1.0", "@babel/code-frame": "^7.18.6", - "@babel/generator": "^7.18.13", - "@babel/helper-compilation-targets": "^7.18.9", - "@babel/helper-module-transforms": "^7.18.9", - "@babel/helpers": "^7.18.9", - "@babel/parser": "^7.18.13", + "@babel/generator": "^7.19.6", + "@babel/helper-compilation-targets": "^7.19.3", + "@babel/helper-module-transforms": "^7.19.6", + "@babel/helpers": "^7.19.4", + "@babel/parser": "^7.19.6", "@babel/template": "^7.18.10", - "@babel/traverse": "^7.18.13", - "@babel/types": "^7.18.13", + "@babel/traverse": "^7.19.6", + "@babel/types": "^7.19.4", "convert-source-map": "^1.7.0", "debug": "^4.1.0", "gensync": "^1.0.0-beta.2", @@ -90,13 +90,22 @@ "url": "https://opencollective.com/babel" } }, + "node_modules/@babel/core/node_modules/semver": { + "version": "6.3.0", + "resolved": "https://registry.npmjs.org/semver/-/semver-6.3.0.tgz", + "integrity": "sha512-b39TBaTSfV6yBrapU89p5fKekE2m/NwnDocOVruQFS1/veMgdzuPcnOM34M6CwxW8jH/lxEa5rBoDeUwu5HHTw==", + "dev": true, + "bin": { + "semver": "bin/semver.js" + } + }, "node_modules/@babel/generator": { - "version": "7.18.13", - "resolved": "https://registry.npmjs.org/@babel/generator/-/generator-7.18.13.tgz", - "integrity": "sha512-CkPg8ySSPuHTYPJYo7IRALdqyjM9HCbt/3uOBEFbzyGVP6Mn8bwFPB0jX6982JVNBlYzM1nnPkfjuXSOPtQeEQ==", + "version": "7.19.6", + "resolved": "https://registry.npmjs.org/@babel/generator/-/generator-7.19.6.tgz", + "integrity": "sha512-oHGRUQeoX1QrKeJIKVe0hwjGqNnVYsM5Nep5zo0uE0m42sLH+Fsd2pStJ5sRM1bNyTUUoz0pe2lTeMJrb/taTA==", "dev": true, "dependencies": { - "@babel/types": "^7.18.13", + "@babel/types": "^7.19.4", "@jridgewell/gen-mapping": "^0.3.2", "jsesc": "^2.5.1" }, @@ -119,14 +128,14 @@ } }, "node_modules/@babel/helper-compilation-targets": { - "version": "7.18.9", - "resolved": "https://registry.npmjs.org/@babel/helper-compilation-targets/-/helper-compilation-targets-7.18.9.tgz", - "integrity": "sha512-tzLCyVmqUiFlcFoAPLA/gL9TeYrF61VLNtb+hvkuVaB5SUjW7jcfrglBIX1vUIoT7CLP3bBlIMeyEsIl2eFQNg==", + "version": "7.19.3", + "resolved": "https://registry.npmjs.org/@babel/helper-compilation-targets/-/helper-compilation-targets-7.19.3.tgz", + "integrity": "sha512-65ESqLGyGmLvgR0mst5AdW1FkNlj9rQsCKduzEoEPhBCDFGXvz2jW6bXFG6i0/MrV2s7hhXjjb2yAzcPuQlLwg==", "dev": true, "dependencies": { - "@babel/compat-data": "^7.18.8", + "@babel/compat-data": "^7.19.3", "@babel/helper-validator-option": "^7.18.6", - "browserslist": "^4.20.2", + "browserslist": "^4.21.3", "semver": "^6.3.0" }, "engines": { @@ -136,6 +145,15 @@ "@babel/core": "^7.0.0" } }, + "node_modules/@babel/helper-compilation-targets/node_modules/semver": { + "version": "6.3.0", + "resolved": "https://registry.npmjs.org/semver/-/semver-6.3.0.tgz", + "integrity": "sha512-b39TBaTSfV6yBrapU89p5fKekE2m/NwnDocOVruQFS1/veMgdzuPcnOM34M6CwxW8jH/lxEa5rBoDeUwu5HHTw==", + "dev": true, + "bin": { + "semver": "bin/semver.js" + } + }, "node_modules/@babel/helper-environment-visitor": { "version": "7.18.9", "resolved": "https://registry.npmjs.org/@babel/helper-environment-visitor/-/helper-environment-visitor-7.18.9.tgz", @@ -146,13 +164,13 @@ } }, "node_modules/@babel/helper-function-name": { - "version": "7.18.9", - "resolved": "https://registry.npmjs.org/@babel/helper-function-name/-/helper-function-name-7.18.9.tgz", - "integrity": "sha512-fJgWlZt7nxGksJS9a0XdSaI4XvpExnNIgRP+rVefWh5U7BL8pPuir6SJUmFKRfjWQ51OtWSzwOxhaH/EBWWc0A==", + "version": "7.19.0", + "resolved": "https://registry.npmjs.org/@babel/helper-function-name/-/helper-function-name-7.19.0.tgz", + "integrity": "sha512-WAwHBINyrpqywkUH0nTnNgI5ina5TFn85HKS0pbPDfxFfhyR/aNQEn4hGi1P1JyT//I0t4OgXUlofzWILRvS5w==", "dev": true, "dependencies": { - "@babel/template": "^7.18.6", - "@babel/types": "^7.18.9" + "@babel/template": "^7.18.10", + "@babel/types": "^7.19.0" }, "engines": { "node": ">=6.9.0" @@ -183,31 +201,31 @@ } }, "node_modules/@babel/helper-module-transforms": { - "version": "7.18.9", - "resolved": "https://registry.npmjs.org/@babel/helper-module-transforms/-/helper-module-transforms-7.18.9.tgz", - "integrity": "sha512-KYNqY0ICwfv19b31XzvmI/mfcylOzbLtowkw+mfvGPAQ3kfCnMLYbED3YecL5tPd8nAYFQFAd6JHp2LxZk/J1g==", + "version": "7.19.6", + "resolved": "https://registry.npmjs.org/@babel/helper-module-transforms/-/helper-module-transforms-7.19.6.tgz", + "integrity": "sha512-fCmcfQo/KYr/VXXDIyd3CBGZ6AFhPFy1TfSEJ+PilGVlQT6jcbqtHAM4C1EciRqMza7/TpOUZliuSH+U6HAhJw==", "dev": true, "dependencies": { "@babel/helper-environment-visitor": "^7.18.9", "@babel/helper-module-imports": "^7.18.6", - "@babel/helper-simple-access": "^7.18.6", + "@babel/helper-simple-access": "^7.19.4", "@babel/helper-split-export-declaration": "^7.18.6", - "@babel/helper-validator-identifier": "^7.18.6", - "@babel/template": "^7.18.6", - "@babel/traverse": "^7.18.9", - "@babel/types": "^7.18.9" + "@babel/helper-validator-identifier": "^7.19.1", + "@babel/template": "^7.18.10", + "@babel/traverse": "^7.19.6", + "@babel/types": "^7.19.4" }, "engines": { "node": ">=6.9.0" } }, "node_modules/@babel/helper-simple-access": { - "version": "7.18.6", - "resolved": "https://registry.npmjs.org/@babel/helper-simple-access/-/helper-simple-access-7.18.6.tgz", - "integrity": "sha512-iNpIgTgyAvDQpDj76POqg+YEt8fPxx3yaNBg3S30dxNKm2SWfYhD0TGrK/Eu9wHpUW63VQU894TsTg+GLbUa1g==", + "version": "7.19.4", + "resolved": "https://registry.npmjs.org/@babel/helper-simple-access/-/helper-simple-access-7.19.4.tgz", + "integrity": "sha512-f9Xq6WqBFqaDfbCzn2w85hwklswz5qsKlh7f08w4Y9yhJHpnNC0QemtSkK5YyOY8kPGvyiwdzZksGUhnGdaUIg==", "dev": true, "dependencies": { - "@babel/types": "^7.18.6" + "@babel/types": "^7.19.4" }, "engines": { "node": ">=6.9.0" @@ -226,18 +244,18 @@ } }, "node_modules/@babel/helper-string-parser": { - "version": "7.18.10", - "resolved": "https://registry.npmjs.org/@babel/helper-string-parser/-/helper-string-parser-7.18.10.tgz", - "integrity": "sha512-XtIfWmeNY3i4t7t4D2t02q50HvqHybPqW2ki1kosnvWCwuCMeo81Jf0gwr85jy/neUdg5XDdeFE/80DXiO+njw==", + "version": "7.19.4", + "resolved": "https://registry.npmjs.org/@babel/helper-string-parser/-/helper-string-parser-7.19.4.tgz", + "integrity": "sha512-nHtDoQcuqFmwYNYPz3Rah5ph2p8PFeFCsZk9A/48dPc/rGocJ5J3hAAZ7pb76VWX3fZKu+uEr/FhH5jLx7umrw==", "dev": true, "engines": { "node": ">=6.9.0" } }, "node_modules/@babel/helper-validator-identifier": { - "version": "7.18.6", - "resolved": "https://registry.npmjs.org/@babel/helper-validator-identifier/-/helper-validator-identifier-7.18.6.tgz", - "integrity": "sha512-MmetCkz9ej86nJQV+sFCxoGGrUbU3q02kgLciwkrt9QqEB7cP39oKEY0PakknEO0Gu20SskMRi+AYZ3b1TpN9g==", + "version": "7.19.1", + "resolved": "https://registry.npmjs.org/@babel/helper-validator-identifier/-/helper-validator-identifier-7.19.1.tgz", + "integrity": "sha512-awrNfaMtnHUr653GgGEs++LlAvW6w+DcPrOliSMXWCKo597CwL5Acf/wWdNkf/tfEQE3mjkeD1YOVZOUV/od1w==", "dev": true, "engines": { "node": ">=6.9.0" @@ -253,14 +271,14 @@ } }, "node_modules/@babel/helpers": { - "version": "7.18.9", - "resolved": "https://registry.npmjs.org/@babel/helpers/-/helpers-7.18.9.tgz", - "integrity": "sha512-Jf5a+rbrLoR4eNdUmnFu8cN5eNJT6qdTdOg5IHIzq87WwyRw9PwguLFOWYgktN/60IP4fgDUawJvs7PjQIzELQ==", + "version": "7.19.4", + "resolved": "https://registry.npmjs.org/@babel/helpers/-/helpers-7.19.4.tgz", + "integrity": "sha512-G+z3aOx2nfDHwX/kyVii5fJq+bgscg89/dJNWpYeKeBv3v9xX8EIabmx1k6u9LS04H7nROFVRVK+e3k0VHp+sw==", "dev": true, "dependencies": { - "@babel/template": "^7.18.6", - "@babel/traverse": "^7.18.9", - "@babel/types": "^7.18.9" + "@babel/template": "^7.18.10", + "@babel/traverse": "^7.19.4", + "@babel/types": "^7.19.4" }, "engines": { "node": ">=6.9.0" @@ -352,9 +370,9 @@ } }, "node_modules/@babel/parser": { - "version": "7.18.13", - "resolved": "https://registry.npmjs.org/@babel/parser/-/parser-7.18.13.tgz", - "integrity": "sha512-dgXcIfMuQ0kgzLB2b9tRZs7TTFFaGM2AbtA4fJgUUYukzGH4jwsS7hzQHEGs67jdehpm22vkgKwvbU+aEflgwg==", + "version": "7.19.6", + "resolved": "https://registry.npmjs.org/@babel/parser/-/parser-7.19.6.tgz", + "integrity": "sha512-h1IUp81s2JYJ3mRkdxJgs4UvmSsRvDrx5ICSJbPvtWYv5i1nTBGcBpnog+89rAFMwvvru6E5NUHdBe01UeSzYA==", "dev": true, "bin": { "parser": "bin/babel-parser.js" @@ -378,19 +396,19 @@ } }, "node_modules/@babel/traverse": { - "version": "7.18.13", - "resolved": "https://registry.npmjs.org/@babel/traverse/-/traverse-7.18.13.tgz", - "integrity": "sha512-N6kt9X1jRMLPxxxPYWi7tgvJRH/rtoU+dbKAPDM44RFHiMH8igdsaSBgFeskhSl/kLWLDUvIh1RXCrTmg0/zvA==", + "version": "7.19.6", + "resolved": "https://registry.npmjs.org/@babel/traverse/-/traverse-7.19.6.tgz", + "integrity": "sha512-6l5HrUCzFM04mfbG09AagtYyR2P0B71B1wN7PfSPiksDPz2k5H9CBC1tcZpz2M8OxbKTPccByoOJ22rUKbpmQQ==", "dev": true, "dependencies": { "@babel/code-frame": "^7.18.6", - "@babel/generator": "^7.18.13", + "@babel/generator": "^7.19.6", "@babel/helper-environment-visitor": "^7.18.9", - "@babel/helper-function-name": "^7.18.9", + "@babel/helper-function-name": "^7.19.0", "@babel/helper-hoist-variables": "^7.18.6", "@babel/helper-split-export-declaration": "^7.18.6", - "@babel/parser": "^7.18.13", - "@babel/types": "^7.18.13", + "@babel/parser": "^7.19.6", + "@babel/types": "^7.19.4", "debug": "^4.1.0", "globals": "^11.1.0" }, @@ -408,13 +426,13 @@ } }, "node_modules/@babel/types": { - "version": "7.18.13", - "resolved": "https://registry.npmjs.org/@babel/types/-/types-7.18.13.tgz", - "integrity": "sha512-ePqfTihzW0W6XAU+aMw2ykilisStJfDnsejDCXRchCcMJ4O0+8DhPXf2YUbZ6wjBlsEmZwLK/sPweWtu8hcJYQ==", + "version": "7.19.4", + "resolved": "https://registry.npmjs.org/@babel/types/-/types-7.19.4.tgz", + "integrity": "sha512-M5LK7nAeS6+9j7hAq+b3fQs+pNfUtTGq+yFFfHnauFA8zQtLRfmuipmsKDKKLuyG+wC8ABW43A153YNawNTEtw==", "dev": true, "dependencies": { - "@babel/helper-string-parser": "^7.18.10", - "@babel/helper-validator-identifier": "^7.18.6", + "@babel/helper-string-parser": "^7.19.4", + "@babel/helper-validator-identifier": "^7.19.1", "to-fast-properties": "^2.0.0" }, "engines": { @@ -444,14 +462,14 @@ } }, "node_modules/@eslint/eslintrc": { - "version": "1.3.0", - "resolved": "https://registry.npmjs.org/@eslint/eslintrc/-/eslintrc-1.3.0.tgz", - "integrity": "sha512-UWW0TMTmk2d7hLcWD1/e2g5HDM/HQ3csaLSqXCfqwh4uNDuNqlaKWXmEsL4Cs41Z0KnILNvwbHAah3C2yt06kw==", + "version": "1.3.3", + "resolved": "https://registry.npmjs.org/@eslint/eslintrc/-/eslintrc-1.3.3.tgz", + "integrity": "sha512-uj3pT6Mg+3t39fvLrj8iuCIJ38zKO9FpGtJ4BBJebJhEwjoT+KLVNCcHT5QC9NGRIEi7fZ0ZR8YRb884auB4Lg==", "dev": true, "dependencies": { "ajv": "^6.12.4", "debug": "^4.3.2", - "espree": "^9.3.2", + "espree": "^9.4.0", "globals": "^13.15.0", "ignore": "^5.2.0", "import-fresh": "^3.2.1", @@ -461,12 +479,15 @@ }, "engines": { "node": "^12.22.0 || ^14.17.0 || >=16.0.0" + }, + "funding": { + "url": "https://opencollective.com/eslint" } }, "node_modules/@humanwhocodes/config-array": { - "version": "0.10.4", - "resolved": "https://registry.npmjs.org/@humanwhocodes/config-array/-/config-array-0.10.4.tgz", - "integrity": "sha512-mXAIHxZT3Vcpg83opl1wGlVZ9xydbfZO3r5YfRSH6Gpp2J/PfdBP0wbDa2sO6/qRbcalpoevVyW6A/fI6LfeMw==", + "version": "0.11.6", + "resolved": "https://registry.npmjs.org/@humanwhocodes/config-array/-/config-array-0.11.6.tgz", + "integrity": "sha512-jJr+hPTJYKyDILJfhNSHsjiwXYf26Flsz8DvNndOsHs5pwSnpGUEy8yzF0JYhCEvTDdV2vuOK5tt8BVhwO5/hg==", "dev": true, "dependencies": { "@humanwhocodes/object-schema": "^1.2.1", @@ -477,11 +498,14 @@ "node": ">=10.10.0" } }, - "node_modules/@humanwhocodes/gitignore-to-minimatch": { - "version": "1.0.2", - "resolved": "https://registry.npmjs.org/@humanwhocodes/gitignore-to-minimatch/-/gitignore-to-minimatch-1.0.2.tgz", - "integrity": "sha512-rSqmMJDdLFUsyxR6FMtD00nfQKKLFb1kv+qBbOVKqErvloEIJLo5bDTJTQNTYgeyp78JsA7u/NPi5jT1GR/MuA==", + "node_modules/@humanwhocodes/module-importer": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/@humanwhocodes/module-importer/-/module-importer-1.0.1.tgz", + "integrity": "sha512-bxveV4V8v5Yb4ncFTT3rPSgZBOpCkjfK0y4oVVVJwIuDVBRMDXrPyXRL988i5ap9m9bnyEEjWfm5WkBmtffLfA==", "dev": true, + "engines": { + "node": ">=12.22" + }, "funding": { "type": "github", "url": "https://github.com/sponsors/nzakas" @@ -524,6 +548,19 @@ "sprintf-js": "~1.0.2" } }, + "node_modules/@istanbuljs/load-nyc-config/node_modules/find-up": { + "version": "4.1.0", + "resolved": "https://registry.npmjs.org/find-up/-/find-up-4.1.0.tgz", + "integrity": "sha512-PpOwAdQ/YlXQ2vj8a3h8IipDuYRi3wceVQQGYWxNINccq40Anw7BlsEXCMbt1Zt+OLA6Fq9suIpIWD0OsnISlw==", + "dev": true, + "dependencies": { + "locate-path": "^5.0.0", + "path-exists": "^4.0.0" + }, + "engines": { + "node": ">=8" + } + }, "node_modules/@istanbuljs/load-nyc-config/node_modules/js-yaml": { "version": "3.14.1", "resolved": "https://registry.npmjs.org/js-yaml/-/js-yaml-3.14.1.tgz", @@ -537,6 +574,45 @@ "js-yaml": "bin/js-yaml.js" } }, + "node_modules/@istanbuljs/load-nyc-config/node_modules/locate-path": { + "version": "5.0.0", + "resolved": "https://registry.npmjs.org/locate-path/-/locate-path-5.0.0.tgz", + "integrity": "sha512-t7hw9pI+WvuwNJXwk5zVHpyhIqzg2qTlklJOf0mVxGSbe3Fp2VieZcduNYjaLDoy6p9uGpQEGWG87WpMKlNq8g==", + "dev": true, + "dependencies": { + "p-locate": "^4.1.0" + }, + "engines": { + "node": ">=8" + } + }, + "node_modules/@istanbuljs/load-nyc-config/node_modules/p-limit": { + "version": "2.3.0", + "resolved": "https://registry.npmjs.org/p-limit/-/p-limit-2.3.0.tgz", + "integrity": "sha512-//88mFWSJx8lxCzwdAABTJL2MyWB12+eIY7MDL2SqLmAkeKU9qxRvWuSyTjm3FUmpBEMuFfckAIqEaVGUDxb6w==", + "dev": true, + "dependencies": { + "p-try": "^2.0.0" + }, + "engines": { + "node": ">=6" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/@istanbuljs/load-nyc-config/node_modules/p-locate": { + "version": "4.1.0", + "resolved": "https://registry.npmjs.org/p-locate/-/p-locate-4.1.0.tgz", + "integrity": "sha512-R79ZZ/0wAxKGu3oYMlz8jy/kbhsNrS7SKZ7PxEHBgJ5+F2mtFW2fK2cOtBh1cHYkQsbzFV7I+EoRKe6Yt0oK7A==", + "dev": true, + "dependencies": { + "p-limit": "^2.2.0" + }, + "engines": { + "node": ">=8" + } + }, "node_modules/@istanbuljs/load-nyc-config/node_modules/resolve-from": { "version": "5.0.0", "resolved": "https://registry.npmjs.org/resolve-from/-/resolve-from-5.0.0.tgz", @@ -608,13 +684,13 @@ "dev": true }, "node_modules/@jridgewell/trace-mapping": { - "version": "0.3.15", - "resolved": "https://registry.npmjs.org/@jridgewell/trace-mapping/-/trace-mapping-0.3.15.tgz", - "integrity": "sha512-oWZNOULl+UbhsgB51uuZzglikfIKSUBO/M9W2OfEjn7cmqoAiCgmv9lyACTUacZwBz0ITnJ2NqjU8Tx0DHL88g==", + "version": "0.3.17", + "resolved": "https://registry.npmjs.org/@jridgewell/trace-mapping/-/trace-mapping-0.3.17.tgz", + "integrity": "sha512-MCNzAp77qzKca9+W/+I0+sEpaUnZoeasnghNeVc41VZCEKaCH73Vq3BZZ/SzWIgrqE4H4ceI+p+b6C0mHf9T4g==", "dev": true, "dependencies": { - "@jridgewell/resolve-uri": "^3.0.3", - "@jridgewell/sourcemap-codec": "^1.4.10" + "@jridgewell/resolve-uri": "3.1.0", + "@jridgewell/sourcemap-codec": "1.4.14" } }, "node_modules/@nodelib/fs.scandir": { @@ -653,28 +729,28 @@ } }, "node_modules/@octokit/auth-token": { - "version": "3.0.1", - "resolved": "https://registry.npmjs.org/@octokit/auth-token/-/auth-token-3.0.1.tgz", - "integrity": "sha512-/USkK4cioY209wXRpund6HZzHo9GmjakpV9ycOkpMcMxMk7QVcVFVyCMtzvXYiHsB2crgDgrtNYSELYFBXhhaA==", + "version": "3.0.2", + "resolved": "https://registry.npmjs.org/@octokit/auth-token/-/auth-token-3.0.2.tgz", + "integrity": "sha512-pq7CwIMV1kmzkFTimdwjAINCXKTajZErLB4wMLYapR2nuB/Jpr66+05wOTZMSCBXP6n4DdDWT2W19Bm17vU69Q==", "dev": true, "dependencies": { - "@octokit/types": "^7.0.0" + "@octokit/types": "^8.0.0" }, "engines": { "node": ">= 14" } }, "node_modules/@octokit/core": { - "version": "4.0.5", - "resolved": "https://registry.npmjs.org/@octokit/core/-/core-4.0.5.tgz", - "integrity": "sha512-4R3HeHTYVHCfzSAi0C6pbGXV8UDI5Rk+k3G7kLVNckswN9mvpOzW9oENfjfH3nEmzg8y3AmKmzs8Sg6pLCeOCA==", + "version": "4.1.0", + "resolved": "https://registry.npmjs.org/@octokit/core/-/core-4.1.0.tgz", + "integrity": "sha512-Czz/59VefU+kKDy+ZfDwtOIYIkFjExOKf+HA92aiTZJ6EfWpFzYQWw0l54ji8bVmyhc+mGaLUbSUmXazG7z5OQ==", "dev": true, "dependencies": { "@octokit/auth-token": "^3.0.0", "@octokit/graphql": "^5.0.0", "@octokit/request": "^6.0.0", "@octokit/request-error": "^3.0.0", - "@octokit/types": "^7.0.0", + "@octokit/types": "^8.0.0", "before-after-hook": "^2.2.0", "universal-user-agent": "^6.0.0" }, @@ -683,12 +759,12 @@ } }, "node_modules/@octokit/endpoint": { - "version": "7.0.1", - "resolved": "https://registry.npmjs.org/@octokit/endpoint/-/endpoint-7.0.1.tgz", - "integrity": "sha512-/wTXAJwt0HzJ2IeE4kQXO+mBScfzyCkI0hMtkIaqyXd9zg76OpOfNQfHL9FlaxAV2RsNiOXZibVWloy8EexENg==", + "version": "7.0.3", + "resolved": "https://registry.npmjs.org/@octokit/endpoint/-/endpoint-7.0.3.tgz", + "integrity": "sha512-57gRlb28bwTsdNXq+O3JTQ7ERmBTuik9+LelgcLIVfYwf235VHbN9QNo4kXExtp/h8T423cR5iJThKtFYxC7Lw==", "dev": true, "dependencies": { - "@octokit/types": "^7.0.0", + "@octokit/types": "^8.0.0", "is-plain-object": "^5.0.0", "universal-user-agent": "^6.0.0" }, @@ -697,13 +773,13 @@ } }, "node_modules/@octokit/graphql": { - "version": "5.0.1", - "resolved": "https://registry.npmjs.org/@octokit/graphql/-/graphql-5.0.1.tgz", - "integrity": "sha512-sxmnewSwAixkP1TrLdE6yRG53eEhHhDTYUykUwdV9x8f91WcbhunIHk9x1PZLALdBZKRPUO2HRcm4kezZ79HoA==", + "version": "5.0.4", + "resolved": "https://registry.npmjs.org/@octokit/graphql/-/graphql-5.0.4.tgz", + "integrity": "sha512-amO1M5QUQgYQo09aStR/XO7KAl13xpigcy/kI8/N1PnZYSS69fgte+xA4+c2DISKqUZfsh0wwjc2FaCt99L41A==", "dev": true, "dependencies": { "@octokit/request": "^6.0.0", - "@octokit/types": "^7.0.0", + "@octokit/types": "^8.0.0", "universal-user-agent": "^6.0.0" }, "engines": { @@ -711,18 +787,18 @@ } }, "node_modules/@octokit/openapi-types": { - "version": "13.4.0", - "resolved": "https://registry.npmjs.org/@octokit/openapi-types/-/openapi-types-13.4.0.tgz", - "integrity": "sha512-2mVzW0X1+HDO3jF80/+QFZNzJiTefELKbhMu6yaBYbp/1gSMkVDm4rT472gJljTokWUlXaaE63m7WrWENhMDLw==", + "version": "14.0.0", + "resolved": "https://registry.npmjs.org/@octokit/openapi-types/-/openapi-types-14.0.0.tgz", + "integrity": "sha512-HNWisMYlR8VCnNurDU6os2ikx0s0VyEjDYHNS/h4cgb8DeOxQ0n72HyinUtdDVxJhFy3FWLGl0DJhfEWk3P5Iw==", "dev": true }, "node_modules/@octokit/plugin-paginate-rest": { - "version": "3.1.0", - "resolved": "https://registry.npmjs.org/@octokit/plugin-paginate-rest/-/plugin-paginate-rest-3.1.0.tgz", - "integrity": "sha512-+cfc40pMzWcLkoDcLb1KXqjX0jTGYXjKuQdFQDc6UAknISJHnZTiBqld6HDwRJvD4DsouDKrWXNbNV0lE/3AXA==", + "version": "4.3.1", + "resolved": "https://registry.npmjs.org/@octokit/plugin-paginate-rest/-/plugin-paginate-rest-4.3.1.tgz", + "integrity": "sha512-h8KKxESmSFTcXX409CAxlaOYscEDvN2KGQRsLCGT1NSqRW+D6EXLVQ8vuHhFznS9MuH9QYw1GfsUN30bg8hjVA==", "dev": true, "dependencies": { - "@octokit/types": "^6.41.0" + "@octokit/types": "^7.5.0" }, "engines": { "node": ">= 14" @@ -732,18 +808,18 @@ } }, "node_modules/@octokit/plugin-paginate-rest/node_modules/@octokit/openapi-types": { - "version": "12.11.0", - "resolved": "https://registry.npmjs.org/@octokit/openapi-types/-/openapi-types-12.11.0.tgz", - "integrity": "sha512-VsXyi8peyRq9PqIz/tpqiL2w3w80OgVMwBHltTml3LmVvXiphgeqmY9mvBw9Wu7e0QWk/fqD37ux8yP5uVekyQ==", + "version": "13.13.1", + "resolved": "https://registry.npmjs.org/@octokit/openapi-types/-/openapi-types-13.13.1.tgz", + "integrity": "sha512-4EuKSk3N95UBWFau3Bz9b3pheQ8jQYbKmBL5+GSuY8YDPDwu03J4BjI+66yNi8aaX/3h1qDpb0mbBkLdr+cfGQ==", "dev": true }, "node_modules/@octokit/plugin-paginate-rest/node_modules/@octokit/types": { - "version": "6.41.0", - "resolved": "https://registry.npmjs.org/@octokit/types/-/types-6.41.0.tgz", - "integrity": "sha512-eJ2jbzjdijiL3B4PrSQaSjuF2sPEQPVCPzBvTHJD9Nz+9dw2SGH4K4xeQJ77YfTq5bRQ+bD8wT11JbeDPmxmGg==", + "version": "7.5.1", + "resolved": "https://registry.npmjs.org/@octokit/types/-/types-7.5.1.tgz", + "integrity": "sha512-Zk4OUMLCSpXNI8KZZn47lVLJSsgMyCimsWWQI5hyjZg7hdYm0kjotaIkbG0Pp8SfU2CofMBzonboTqvzn3FrJA==", "dev": true, "dependencies": { - "@octokit/openapi-types": "^12.11.0" + "@octokit/openapi-types": "^13.11.0" } }, "node_modules/@octokit/plugin-request-log": { @@ -756,12 +832,12 @@ } }, "node_modules/@octokit/plugin-rest-endpoint-methods": { - "version": "6.3.0", - "resolved": "https://registry.npmjs.org/@octokit/plugin-rest-endpoint-methods/-/plugin-rest-endpoint-methods-6.3.0.tgz", - "integrity": "sha512-qEu2wn6E7hqluZwIEUnDxWROvKjov3zMIAi4H4d7cmKWNMeBprEXZzJe8pE5eStUYC1ysGhD0B7L6IeG1Rfb+g==", + "version": "6.7.0", + "resolved": "https://registry.npmjs.org/@octokit/plugin-rest-endpoint-methods/-/plugin-rest-endpoint-methods-6.7.0.tgz", + "integrity": "sha512-orxQ0fAHA7IpYhG2flD2AygztPlGYNAdlzYz8yrD8NDgelPfOYoRPROfEyIe035PlxvbYrgkfUZIhSBKju/Cvw==", "dev": true, "dependencies": { - "@octokit/types": "^7.0.0", + "@octokit/types": "^8.0.0", "deprecation": "^2.3.1" }, "engines": { @@ -772,14 +848,14 @@ } }, "node_modules/@octokit/request": { - "version": "6.2.1", - "resolved": "https://registry.npmjs.org/@octokit/request/-/request-6.2.1.tgz", - "integrity": "sha512-gYKRCia3cpajRzDSU+3pt1q2OcuC6PK8PmFIyxZDWCzRXRSIBH8jXjFJ8ZceoygBIm0KsEUg4x1+XcYBz7dHPQ==", + "version": "6.2.2", + "resolved": "https://registry.npmjs.org/@octokit/request/-/request-6.2.2.tgz", + "integrity": "sha512-6VDqgj0HMc2FUX2awIs+sM6OwLgwHvAi4KCK3mT2H2IKRt6oH9d0fej5LluF5mck1lRR/rFWN0YIDSYXYSylbw==", "dev": true, "dependencies": { "@octokit/endpoint": "^7.0.0", "@octokit/request-error": "^3.0.0", - "@octokit/types": "^7.0.0", + "@octokit/types": "^8.0.0", "is-plain-object": "^5.0.0", "node-fetch": "^2.6.7", "universal-user-agent": "^6.0.0" @@ -789,12 +865,12 @@ } }, "node_modules/@octokit/request-error": { - "version": "3.0.1", - "resolved": "https://registry.npmjs.org/@octokit/request-error/-/request-error-3.0.1.tgz", - "integrity": "sha512-ym4Bp0HTP7F3VFssV88WD1ZyCIRoE8H35pXSKwLeMizcdZAYc/t6N9X9Yr9n6t3aG9IH75XDnZ6UeZph0vHMWQ==", + "version": "3.0.2", + "resolved": "https://registry.npmjs.org/@octokit/request-error/-/request-error-3.0.2.tgz", + "integrity": "sha512-WMNOFYrSaX8zXWoJg9u/pKgWPo94JXilMLb2VManNOby9EZxrQaBe/QSC4a1TzpAlpxofg2X/jMnCyZgL6y7eg==", "dev": true, "dependencies": { - "@octokit/types": "^7.0.0", + "@octokit/types": "^8.0.0", "deprecation": "^2.0.0", "once": "^1.4.0" }, @@ -823,13 +899,13 @@ } }, "node_modules/@octokit/rest": { - "version": "19.0.3", - "resolved": "https://registry.npmjs.org/@octokit/rest/-/rest-19.0.3.tgz", - "integrity": "sha512-5arkTsnnRT7/sbI4fqgSJ35KiFaN7zQm0uQiQtivNQLI8RQx8EHwJCajcTUwmaCMNDg7tdCvqAnc7uvHHPxrtQ==", + "version": "19.0.4", + "resolved": "https://registry.npmjs.org/@octokit/rest/-/rest-19.0.4.tgz", + "integrity": "sha512-LwG668+6lE8zlSYOfwPj4FxWdv/qFXYBpv79TWIQEpBLKA9D/IMcWsF/U9RGpA3YqMVDiTxpgVpEW3zTFfPFTA==", "dev": true, "dependencies": { "@octokit/core": "^4.0.0", - "@octokit/plugin-paginate-rest": "^3.0.0", + "@octokit/plugin-paginate-rest": "^4.0.0", "@octokit/plugin-request-log": "^1.0.4", "@octokit/plugin-rest-endpoint-methods": "^6.0.0" }, @@ -838,12 +914,12 @@ } }, "node_modules/@octokit/types": { - "version": "7.1.1", - "resolved": "https://registry.npmjs.org/@octokit/types/-/types-7.1.1.tgz", - "integrity": "sha512-Dx6cNTORyVaKY0Yeb9MbHksk79L8GXsihbG6PtWqTpkyA2TY1qBWE26EQXVG3dHwY9Femdd/WEeRUEiD0+H3TQ==", + "version": "8.0.0", + "resolved": "https://registry.npmjs.org/@octokit/types/-/types-8.0.0.tgz", + "integrity": "sha512-65/TPpOJP1i3K4lBJMnWqPUJ6zuOtzhtagDvydAWbEXpbFYA0oMKKyLb95NFZZP0lSh/4b6K+DQlzvYQJQQePg==", "dev": true, "dependencies": { - "@octokit/openapi-types": "^13.4.0" + "@octokit/openapi-types": "^14.0.0" } }, "node_modules/@pnpm/network.ca-file": { @@ -991,55 +1067,28 @@ "integrity": "sha512-yOlFc+7UtL/89t2ZhjPvvB/DeAr3r+Dq58IgzsFkOAvVC6NMJXmCGjbptdXdR9qsX7pKcTL+s87FtYREi2dEEQ==", "dev": true }, - "node_modules/@types/cacheable-request": { - "version": "6.0.2", - "resolved": "https://registry.npmjs.org/@types/cacheable-request/-/cacheable-request-6.0.2.tgz", - "integrity": "sha512-B3xVo+dlKM6nnKTcmm5ZtY/OL8bOAOd2Olee9M1zft65ox50OzjEHW91sDiU9j6cvW8Ejg1/Qkf4xd2kugApUA==", - "dev": true, - "dependencies": { - "@types/http-cache-semantics": "*", - "@types/keyv": "*", - "@types/node": "*", - "@types/responselike": "*" - } - }, "node_modules/@types/http-cache-semantics": { "version": "4.0.1", "resolved": "https://registry.npmjs.org/@types/http-cache-semantics/-/http-cache-semantics-4.0.1.tgz", "integrity": "sha512-SZs7ekbP8CN0txVG2xVRH6EgKmEm31BOxA07vkFaETzZz1xh+cbt8BcI0slpymvwhx5dlFnQG2rTlPVQn+iRPQ==", "dev": true }, - "node_modules/@types/json-buffer": { - "version": "3.0.0", - "resolved": "https://registry.npmjs.org/@types/json-buffer/-/json-buffer-3.0.0.tgz", - "integrity": "sha512-3YP80IxxFJB4b5tYC2SUPwkg0XQLiu0nWvhRgEatgjf+29IcWO9X1k8xRv5DGssJ/lCrjYTjQPcobJr2yWIVuQ==", - "dev": true - }, "node_modules/@types/json-schema": { "version": "7.0.11", "resolved": "https://registry.npmjs.org/@types/json-schema/-/json-schema-7.0.11.tgz", "integrity": "sha512-wOuvG1SN4Us4rez+tylwwwCV1psiNVOkJeM3AUWUNWg/jDQY2+HE/444y5gc+jBmRqASOm2Oeh5c1axHobwRKQ==", "dev": true }, - "node_modules/@types/keyv": { - "version": "3.1.4", - "resolved": "https://registry.npmjs.org/@types/keyv/-/keyv-3.1.4.tgz", - "integrity": "sha512-BQ5aZNSCpj7D6K2ksrRCTmKRLEpnPvWDiLPfoGyhZ++8YtiK9d/3DBKPJgry359X/P1PfruyYwvnvwFjuEiEIg==", - "dev": true, - "dependencies": { - "@types/node": "*" - } - }, "node_modules/@types/mocha": { - "version": "9.1.1", - "resolved": "https://registry.npmjs.org/@types/mocha/-/mocha-9.1.1.tgz", - "integrity": "sha512-Z61JK7DKDtdKTWwLeElSEBcWGRLY8g95ic5FoQqI9CMx0ns/Ghep3B4DfcEimiKMvtamNVULVNKEsiwV3aQmXw==", + "version": "10.0.0", + "resolved": "https://registry.npmjs.org/@types/mocha/-/mocha-10.0.0.tgz", + "integrity": "sha512-rADY+HtTOA52l9VZWtgQfn4p+UDVM2eDVkMZT1I6syp0YKxW2F9v+0pbRZLsvskhQv/vMb6ZfCay81GHbz5SHg==", "dev": true }, "node_modules/@types/node": { - "version": "18.7.10", - "resolved": "https://registry.npmjs.org/@types/node/-/node-18.7.10.tgz", - "integrity": "sha512-SST7B//wF7xlGoLo1IEVB0cQ4e7BgXlDk5IaPgb5s0DlYLjb4if07h8+0zbQIvahfPNXs6e7zyT0EH1MqaS+5g==", + "version": "18.11.6", + "resolved": "https://registry.npmjs.org/@types/node/-/node-18.11.6.tgz", + "integrity": "sha512-j3CEDa2vd96K0AXF8Wur7UucACvnjkk8hYyQAHhUNciabZLDl9nfAEVUSwmh245OOZV15bRA3Y590Gi5jUcDJg==", "dev": true }, "node_modules/@types/parse-json": { @@ -1048,14 +1097,11 @@ "integrity": "sha512-//oorEZjL6sbPcKUaCdIGlIUeH26mgzimjBB77G6XRgnDl/L5wOnpyBGRe/Mmf5CVW3PwEBE1NjiMZ/ssFh4wA==", "dev": true }, - "node_modules/@types/responselike": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/@types/responselike/-/responselike-1.0.0.tgz", - "integrity": "sha512-85Y2BjiufFzaMIlvJDvTTB8Fxl2xfLo4HgmHzVBz08w4wDePCTjYw66PdrolO0kzli3yam/YCgRufyo1DdQVTA==", - "dev": true, - "dependencies": { - "@types/node": "*" - } + "node_modules/@types/semver": { + "version": "7.3.12", + "resolved": "https://registry.npmjs.org/@types/semver/-/semver-7.3.12.tgz", + "integrity": "sha512-WwA1MW0++RfXmCr12xeYOOC5baSC9mSb0ZqCquFzKhcoF4TvHu5MKOuXsncgZcpVFhB1pXd5hZmM0ryAoCp12A==", + "dev": true }, "node_modules/@types/sinon": { "version": "10.0.13", @@ -1079,9 +1125,9 @@ "dev": true }, "node_modules/@types/yargs": { - "version": "17.0.11", - "resolved": "https://registry.npmjs.org/@types/yargs/-/yargs-17.0.11.tgz", - "integrity": "sha512-aB4y9UDUXTSMxmM4MH+YnuR0g5Cph3FLQBoWoMB21DSvFVAxRVEHEMx3TLh+zUZYMCQtKiqazz0Q4Rre31f/OA==", + "version": "17.0.13", + "resolved": "https://registry.npmjs.org/@types/yargs/-/yargs-17.0.13.tgz", + "integrity": "sha512-9sWaruZk2JGxIQU+IhI1fhPYRcQ0UuTNuKuCW9bR5fp7qi2Llf7WDzNa17Cy7TKnh3cdxDOiyTu6gaLS0eDatg==", "dev": true, "dependencies": { "@types/yargs-parser": "*" @@ -1094,16 +1140,15 @@ "dev": true }, "node_modules/@typescript-eslint/eslint-plugin": { - "version": "5.34.0", - "resolved": "https://registry.npmjs.org/@typescript-eslint/eslint-plugin/-/eslint-plugin-5.34.0.tgz", - "integrity": "sha512-eRfPPcasO39iwjlUAMtjeueRGuIrW3TQ9WseIDl7i5UWuFbf83yYaU7YPs4j8+4CxUMIsj1k+4kV+E+G+6ypDQ==", + "version": "5.41.0", + "resolved": "https://registry.npmjs.org/@typescript-eslint/eslint-plugin/-/eslint-plugin-5.41.0.tgz", + "integrity": "sha512-DXUS22Y57/LAFSg3x7Vi6RNAuLpTXwxB9S2nIA7msBb/Zt8p7XqMwdpdc1IU7CkOQUPgAqR5fWvxuKCbneKGmA==", "dev": true, "dependencies": { - "@typescript-eslint/scope-manager": "5.34.0", - "@typescript-eslint/type-utils": "5.34.0", - "@typescript-eslint/utils": "5.34.0", + "@typescript-eslint/scope-manager": "5.41.0", + "@typescript-eslint/type-utils": "5.41.0", + "@typescript-eslint/utils": "5.41.0", "debug": "^4.3.4", - "functional-red-black-tree": "^1.0.1", "ignore": "^5.2.0", "regexpp": "^3.2.0", "semver": "^7.3.7", @@ -1126,48 +1171,15 @@ } } }, - "node_modules/@typescript-eslint/eslint-plugin/node_modules/lru-cache": { - "version": "6.0.0", - "resolved": "https://registry.npmjs.org/lru-cache/-/lru-cache-6.0.0.tgz", - "integrity": "sha512-Jo6dJ04CmSjuznwJSS3pUeWmd/H0ffTlkXXgwZi+eq1UCmqQwCh+eLsYOYCwY991i2Fah4h1BEMCx4qThGbsiA==", - "dev": true, - "dependencies": { - "yallist": "^4.0.0" - }, - "engines": { - "node": ">=10" - } - }, - "node_modules/@typescript-eslint/eslint-plugin/node_modules/semver": { - "version": "7.3.7", - "resolved": "https://registry.npmjs.org/semver/-/semver-7.3.7.tgz", - "integrity": "sha512-QlYTucUYOews+WeEujDoEGziz4K6c47V/Bd+LjSSYcA94p+DmINdf7ncaUinThfvZyu13lN9OY1XDxt8C0Tw0g==", - "dev": true, - "dependencies": { - "lru-cache": "^6.0.0" - }, - "bin": { - "semver": "bin/semver.js" - }, - "engines": { - "node": ">=10" - } - }, - "node_modules/@typescript-eslint/eslint-plugin/node_modules/yallist": { - "version": "4.0.0", - "resolved": "https://registry.npmjs.org/yallist/-/yallist-4.0.0.tgz", - "integrity": "sha512-3wdGidZyq5PB084XLES5TpOSRA3wjXAlIWMhum2kRcv/41Sn2emQ0dycQW4uZXLejwKvg6EsvbdlVL+FYEct7A==", - "dev": true - }, "node_modules/@typescript-eslint/parser": { - "version": "5.34.0", - "resolved": "https://registry.npmjs.org/@typescript-eslint/parser/-/parser-5.34.0.tgz", - "integrity": "sha512-SZ3NEnK4usd2CXkoV3jPa/vo1mWX1fqRyIVUQZR4As1vyp4fneknBNJj+OFtV8WAVgGf+rOHMSqQbs2Qn3nFZQ==", + "version": "5.41.0", + "resolved": "https://registry.npmjs.org/@typescript-eslint/parser/-/parser-5.41.0.tgz", + "integrity": "sha512-HQVfix4+RL5YRWZboMD1pUfFN8MpRH4laziWkkAzyO1fvNOY/uinZcvo3QiFJVS/siNHupV8E5+xSwQZrl6PZA==", "dev": true, "dependencies": { - "@typescript-eslint/scope-manager": "5.34.0", - "@typescript-eslint/types": "5.34.0", - "@typescript-eslint/typescript-estree": "5.34.0", + "@typescript-eslint/scope-manager": "5.41.0", + "@typescript-eslint/types": "5.41.0", + "@typescript-eslint/typescript-estree": "5.41.0", "debug": "^4.3.4" }, "engines": { @@ -1187,13 +1199,13 @@ } }, "node_modules/@typescript-eslint/scope-manager": { - "version": "5.34.0", - "resolved": "https://registry.npmjs.org/@typescript-eslint/scope-manager/-/scope-manager-5.34.0.tgz", - "integrity": "sha512-HNvASMQlah5RsBW6L6c7IJ0vsm+8Sope/wu5sEAf7joJYWNb1LDbJipzmdhdUOnfrDFE6LR1j57x1EYVxrY4ow==", + "version": "5.41.0", + "resolved": "https://registry.npmjs.org/@typescript-eslint/scope-manager/-/scope-manager-5.41.0.tgz", + "integrity": "sha512-xOxPJCnuktUkY2xoEZBKXO5DBCugFzjrVndKdUnyQr3+9aDWZReKq9MhaoVnbL+maVwWJu/N0SEtrtEUNb62QQ==", "dev": true, "dependencies": { - "@typescript-eslint/types": "5.34.0", - "@typescript-eslint/visitor-keys": "5.34.0" + "@typescript-eslint/types": "5.41.0", + "@typescript-eslint/visitor-keys": "5.41.0" }, "engines": { "node": "^12.22.0 || ^14.17.0 || >=16.0.0" @@ -1204,12 +1216,13 @@ } }, "node_modules/@typescript-eslint/type-utils": { - "version": "5.34.0", - "resolved": "https://registry.npmjs.org/@typescript-eslint/type-utils/-/type-utils-5.34.0.tgz", - "integrity": "sha512-Pxlno9bjsQ7hs1pdWRUv9aJijGYPYsHpwMeCQ/Inavhym3/XaKt1ZKAA8FIw4odTBfowBdZJDMxf2aavyMDkLg==", + "version": "5.41.0", + "resolved": "https://registry.npmjs.org/@typescript-eslint/type-utils/-/type-utils-5.41.0.tgz", + "integrity": "sha512-L30HNvIG6A1Q0R58e4hu4h+fZqaO909UcnnPbwKiN6Rc3BUEx6ez2wgN7aC0cBfcAjZfwkzE+E2PQQ9nEuoqfA==", "dev": true, "dependencies": { - "@typescript-eslint/utils": "5.34.0", + "@typescript-eslint/typescript-estree": "5.41.0", + "@typescript-eslint/utils": "5.41.0", "debug": "^4.3.4", "tsutils": "^3.21.0" }, @@ -1230,9 +1243,9 @@ } }, "node_modules/@typescript-eslint/types": { - "version": "5.34.0", - "resolved": "https://registry.npmjs.org/@typescript-eslint/types/-/types-5.34.0.tgz", - "integrity": "sha512-49fm3xbbUPuzBIOcy2CDpYWqy/X7VBkxVN+DC21e0zIm3+61Z0NZi6J9mqPmSW1BDVk9FIOvuCFyUPjXz93sjA==", + "version": "5.41.0", + "resolved": "https://registry.npmjs.org/@typescript-eslint/types/-/types-5.41.0.tgz", + "integrity": "sha512-5BejraMXMC+2UjefDvrH0Fo/eLwZRV6859SXRg+FgbhA0R0l6lDqDGAQYhKbXhPN2ofk2kY5sgGyLNL907UXpA==", "dev": true, "engines": { "node": "^12.22.0 || ^14.17.0 || >=16.0.0" @@ -1243,13 +1256,13 @@ } }, "node_modules/@typescript-eslint/typescript-estree": { - "version": "5.34.0", - "resolved": "https://registry.npmjs.org/@typescript-eslint/typescript-estree/-/typescript-estree-5.34.0.tgz", - "integrity": "sha512-mXHAqapJJDVzxauEkfJI96j3D10sd567LlqroyCeJaHnu42sDbjxotGb3XFtGPYKPD9IyLjhsoULML1oI3M86A==", + "version": "5.41.0", + "resolved": "https://registry.npmjs.org/@typescript-eslint/typescript-estree/-/typescript-estree-5.41.0.tgz", + "integrity": "sha512-SlzFYRwFSvswzDSQ/zPkIWcHv8O5y42YUskko9c4ki+fV6HATsTODUPbRbcGDFYP86gaJL5xohUEytvyNNcXWg==", "dev": true, "dependencies": { - "@typescript-eslint/types": "5.34.0", - "@typescript-eslint/visitor-keys": "5.34.0", + "@typescript-eslint/types": "5.41.0", + "@typescript-eslint/visitor-keys": "5.41.0", "debug": "^4.3.4", "globby": "^11.1.0", "is-glob": "^4.0.3", @@ -1269,80 +1282,20 @@ } } }, - "node_modules/@typescript-eslint/typescript-estree/node_modules/array-union": { - "version": "2.1.0", - "resolved": "https://registry.npmjs.org/array-union/-/array-union-2.1.0.tgz", - "integrity": "sha512-HGyxoOTYUyCM6stUe6EJgnd4EoewAI7zMdfqO+kGjnlZmBDz/cR5pf8r/cR4Wq60sL/p0IkcjUEEPwS3GFrIyw==", - "dev": true, - "engines": { - "node": ">=8" - } - }, - "node_modules/@typescript-eslint/typescript-estree/node_modules/globby": { - "version": "11.1.0", - "resolved": "https://registry.npmjs.org/globby/-/globby-11.1.0.tgz", - "integrity": "sha512-jhIXaOzy1sb8IyocaruWSn1TjmnBVs8Ayhcy83rmxNJ8q2uWKCAj3CnJY+KpGSXCueAPc0i05kVvVKtP1t9S3g==", - "dev": true, - "dependencies": { - "array-union": "^2.1.0", - "dir-glob": "^3.0.1", - "fast-glob": "^3.2.9", - "ignore": "^5.2.0", - "merge2": "^1.4.1", - "slash": "^3.0.0" - }, - "engines": { - "node": ">=10" - }, - "funding": { - "url": "https://github.com/sponsors/sindresorhus" - } - }, - "node_modules/@typescript-eslint/typescript-estree/node_modules/lru-cache": { - "version": "6.0.0", - "resolved": "https://registry.npmjs.org/lru-cache/-/lru-cache-6.0.0.tgz", - "integrity": "sha512-Jo6dJ04CmSjuznwJSS3pUeWmd/H0ffTlkXXgwZi+eq1UCmqQwCh+eLsYOYCwY991i2Fah4h1BEMCx4qThGbsiA==", - "dev": true, - "dependencies": { - "yallist": "^4.0.0" - }, - "engines": { - "node": ">=10" - } - }, - "node_modules/@typescript-eslint/typescript-estree/node_modules/semver": { - "version": "7.3.7", - "resolved": "https://registry.npmjs.org/semver/-/semver-7.3.7.tgz", - "integrity": "sha512-QlYTucUYOews+WeEujDoEGziz4K6c47V/Bd+LjSSYcA94p+DmINdf7ncaUinThfvZyu13lN9OY1XDxt8C0Tw0g==", - "dev": true, - "dependencies": { - "lru-cache": "^6.0.0" - }, - "bin": { - "semver": "bin/semver.js" - }, - "engines": { - "node": ">=10" - } - }, - "node_modules/@typescript-eslint/typescript-estree/node_modules/yallist": { - "version": "4.0.0", - "resolved": "https://registry.npmjs.org/yallist/-/yallist-4.0.0.tgz", - "integrity": "sha512-3wdGidZyq5PB084XLES5TpOSRA3wjXAlIWMhum2kRcv/41Sn2emQ0dycQW4uZXLejwKvg6EsvbdlVL+FYEct7A==", - "dev": true - }, "node_modules/@typescript-eslint/utils": { - "version": "5.34.0", - "resolved": "https://registry.npmjs.org/@typescript-eslint/utils/-/utils-5.34.0.tgz", - "integrity": "sha512-kWRYybU4Rn++7lm9yu8pbuydRyQsHRoBDIo11k7eqBWTldN4xUdVUMCsHBiE7aoEkFzrUEaZy3iH477vr4xHAQ==", + "version": "5.41.0", + "resolved": "https://registry.npmjs.org/@typescript-eslint/utils/-/utils-5.41.0.tgz", + "integrity": "sha512-QlvfwaN9jaMga9EBazQ+5DDx/4sAdqDkcs05AsQHMaopluVCUyu1bTRUVKzXbgjDlrRAQrYVoi/sXJ9fmG+KLQ==", "dev": true, "dependencies": { "@types/json-schema": "^7.0.9", - "@typescript-eslint/scope-manager": "5.34.0", - "@typescript-eslint/types": "5.34.0", - "@typescript-eslint/typescript-estree": "5.34.0", + "@types/semver": "^7.3.12", + "@typescript-eslint/scope-manager": "5.41.0", + "@typescript-eslint/types": "5.41.0", + "@typescript-eslint/typescript-estree": "5.41.0", "eslint-scope": "^5.1.1", - "eslint-utils": "^3.0.0" + "eslint-utils": "^3.0.0", + "semver": "^7.3.7" }, "engines": { "node": "^12.22.0 || ^14.17.0 || >=16.0.0" @@ -1356,12 +1309,12 @@ } }, "node_modules/@typescript-eslint/visitor-keys": { - "version": "5.34.0", - "resolved": "https://registry.npmjs.org/@typescript-eslint/visitor-keys/-/visitor-keys-5.34.0.tgz", - "integrity": "sha512-O1moYjOSrab0a2fUvFpsJe0QHtvTC+cR+ovYpgKrAVXzqQyc74mv76TgY6z+aEtjQE2vgZux3CQVtGryqdcOAw==", + "version": "5.41.0", + "resolved": "https://registry.npmjs.org/@typescript-eslint/visitor-keys/-/visitor-keys-5.41.0.tgz", + "integrity": "sha512-vilqeHj267v8uzzakbm13HkPMl7cbYpKVjgFWZPIOHIJHZtinvypUhJ5xBXfWYg4eFKqztbMMpOgFpT9Gfx4fw==", "dev": true, "dependencies": { - "@typescript-eslint/types": "5.34.0", + "@typescript-eslint/types": "5.41.0", "eslint-visitor-keys": "^3.3.0" }, "engines": { @@ -1372,16 +1325,10 @@ "url": "https://opencollective.com/typescript-eslint" } }, - "node_modules/@ungap/promise-all-settled": { - "version": "1.1.2", - "resolved": "https://registry.npmjs.org/@ungap/promise-all-settled/-/promise-all-settled-1.1.2.tgz", - "integrity": "sha512-sL/cEvJWAnClXw0wHk85/2L0G6Sj8UB0Ctc1TEMbKSsmpRosqhwj9gWgFRZSrBr2f9tiXISwNhCPmlfqUqyb9Q==", - "dev": true - }, "node_modules/acorn": { - "version": "8.8.0", - "resolved": "https://registry.npmjs.org/acorn/-/acorn-8.8.0.tgz", - "integrity": "sha512-QOxyigPVrpZ2GXT+PFyZTl6TtOFc5egxHIP9IlQ+RbupQuX4RkT/Bee4/kQuC02Xkzg84JcT7oLYtDIQxp+v7w==", + "version": "8.8.1", + "resolved": "https://registry.npmjs.org/acorn/-/acorn-8.8.1.tgz", + "integrity": "sha512-7zFpHzhnqYKrkYdUjF1HI1bzd0VygEGX8lFk4k5zVMqHEoES+P+7TKI+EvLO9WVMJ8eekdO0aDEK044xTXwPPA==", "dev": true, "bin": { "acorn": "bin/acorn" @@ -1458,15 +1405,6 @@ "string-width": "^4.1.0" } }, - "node_modules/ansi-align/node_modules/ansi-regex": { - "version": "5.0.1", - "resolved": "https://registry.npmjs.org/ansi-regex/-/ansi-regex-5.0.1.tgz", - "integrity": "sha512-quJQXlTSUGL2LH9SUXo8VwsY4soanhgo6LNSm84E1LBcE8s3O0wpdiRzyR9z/ZZJMlMWv37qOOb9pdJlMUEKFQ==", - "dev": true, - "engines": { - "node": ">=8" - } - }, "node_modules/ansi-align/node_modules/emoji-regex": { "version": "8.0.0", "resolved": "https://registry.npmjs.org/emoji-regex/-/emoji-regex-8.0.0.tgz", @@ -1487,18 +1425,6 @@ "node": ">=8" } }, - "node_modules/ansi-align/node_modules/strip-ansi": { - "version": "6.0.1", - "resolved": "https://registry.npmjs.org/strip-ansi/-/strip-ansi-6.0.1.tgz", - "integrity": "sha512-Y38VPSHcqkFrCpFnQ9vuSXmquuv5oXOKpGeT6aGrr3o3Gc9AlVa6JBfUSOCnbxGGZF+/0ooI7KrPuUSztUdU5A==", - "dev": true, - "dependencies": { - "ansi-regex": "^5.0.1" - }, - "engines": { - "node": ">=8" - } - }, "node_modules/ansi-colors": { "version": "4.1.1", "resolved": "https://registry.npmjs.org/ansi-colors/-/ansi-colors-4.1.1.tgz", @@ -1523,28 +1449,40 @@ "url": "https://github.com/sponsors/sindresorhus" } }, - "node_modules/ansi-regex": { - "version": "6.0.1", - "resolved": "https://registry.npmjs.org/ansi-regex/-/ansi-regex-6.0.1.tgz", - "integrity": "sha512-n5M855fKb2SsfMIiFFoVrABHJC8QtHwVx+mHWP3QcEqBHYienj5dHSgjbxtC0WEZXYt4wcD6zrQElDPhFuZgfA==", + "node_modules/ansi-escapes/node_modules/type-fest": { + "version": "1.4.0", + "resolved": "https://registry.npmjs.org/type-fest/-/type-fest-1.4.0.tgz", + "integrity": "sha512-yGSza74xk0UG8k+pLh5oeoYirvIiWo5t0/o3zHHAO2tRDiZcxWP7fywNlXhqb6/r6sWvwi+RsyQMWhVLe4BVuA==", "dev": true, "engines": { - "node": ">=12" + "node": ">=10" }, "funding": { - "url": "https://github.com/chalk/ansi-regex?sponsor=1" + "url": "https://github.com/sponsors/sindresorhus" } }, - "node_modules/ansi-styles": { - "version": "6.1.0", - "resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-6.1.0.tgz", - "integrity": "sha512-VbqNsoz55SYGczauuup0MFUyXNQviSpFTj1RQtFzmQLk18qbVSpTFFGMT293rmDaQuKCT6InmbuEyUne4mTuxQ==", + "node_modules/ansi-regex": { + "version": "5.0.1", + "resolved": "https://registry.npmjs.org/ansi-regex/-/ansi-regex-5.0.1.tgz", + "integrity": "sha512-quJQXlTSUGL2LH9SUXo8VwsY4soanhgo6LNSm84E1LBcE8s3O0wpdiRzyR9z/ZZJMlMWv37qOOb9pdJlMUEKFQ==", "dev": true, "engines": { - "node": ">=12" - }, - "funding": { - "url": "https://github.com/chalk/ansi-styles?sponsor=1" + "node": ">=8" + } + }, + "node_modules/ansi-styles": { + "version": "4.3.0", + "resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-4.3.0.tgz", + "integrity": "sha512-zbB9rCJAT1rbjiVDb2hqKFHNYLxgtk8NURxZ3IZwD3F6NtxbXZQCnnSi1Lkx+IDohdPlFp222wVALIheZJQSEg==", + "dev": true, + "dependencies": { + "color-convert": "^2.0.1" + }, + "engines": { + "node": ">=8" + }, + "funding": { + "url": "https://github.com/chalk/ansi-styles?sponsor=1" } }, "node_modules/anymatch": { @@ -1591,15 +1529,12 @@ "dev": true }, "node_modules/array-union": { - "version": "1.0.2", - "resolved": "https://registry.npmjs.org/array-union/-/array-union-1.0.2.tgz", - "integrity": "sha512-Dxr6QJj/RdU/hCaBjOfxW+q6lyuVE6JFWIrAUpuOOhoJJoQ99cUn3igRaHVB5P9WrgFVN0FfArM3x0cueOU8ng==", + "version": "2.1.0", + "resolved": "https://registry.npmjs.org/array-union/-/array-union-2.1.0.tgz", + "integrity": "sha512-HGyxoOTYUyCM6stUe6EJgnd4EoewAI7zMdfqO+kGjnlZmBDz/cR5pf8r/cR4Wq60sL/p0IkcjUEEPwS3GFrIyw==", "dev": true, - "dependencies": { - "array-uniq": "^1.0.1" - }, "engines": { - "node": ">=0.10.0" + "node": ">=8" } }, "node_modules/array-uniq": { @@ -1693,9 +1628,9 @@ ] }, "node_modules/before-after-hook": { - "version": "2.2.2", - "resolved": "https://registry.npmjs.org/before-after-hook/-/before-after-hook-2.2.2.tgz", - "integrity": "sha512-3pZEU3NT5BFUo/AD5ERPWOgQOCZITni6iavr5AUw5AUwQjMlI0kzu5btnyD39AF0gUEsDPwJT+oY1ORBJijPjQ==", + "version": "2.2.3", + "resolved": "https://registry.npmjs.org/before-after-hook/-/before-after-hook-2.2.3.tgz", + "integrity": "sha512-NzUnlZexiaH/46WDhANlyR2bXRopNg4F/zuSA3OpZnllCUgRaOF2znDioDWrmbNVsuZk6l9pMquQB38cfBZwkQ==", "dev": true }, "node_modules/binary-extensions": { @@ -1708,9 +1643,9 @@ } }, "node_modules/bl": { - "version": "5.0.0", - "resolved": "https://registry.npmjs.org/bl/-/bl-5.0.0.tgz", - "integrity": "sha512-8vxFNZ0pflFfi0WXA3WQXlj6CaMEwsmh63I1CNp0q+wWv8sD0ARx1KovSQd0l2GkwrMIOyedq0EF1FxI+RCZLQ==", + "version": "5.1.0", + "resolved": "https://registry.npmjs.org/bl/-/bl-5.1.0.tgz", + "integrity": "sha512-tv1ZJHLfTDnXE6tMHv73YgSJaWR2AFuPwMntBe7XL/GBFHnT0CLnsHMogfk5+GzCDC5ZWarSCYaIGATZt9dNsQ==", "dev": true, "dependencies": { "buffer": "^6.0.3", @@ -1752,6 +1687,18 @@ "url": "https://github.com/sponsors/sindresorhus" } }, + "node_modules/boxen/node_modules/chalk": { + "version": "5.1.2", + "resolved": "https://registry.npmjs.org/chalk/-/chalk-5.1.2.tgz", + "integrity": "sha512-E5CkT4jWURs1Vy5qGJye+XwCkNj7Od3Af7CP6SujMetSMkLs8Do2RWJK5yx1wamHV/op8Rz+9rltjaTQWDnEFQ==", + "dev": true, + "engines": { + "node": "^12.17.0 || ^14.13 || >=16.0.0" + }, + "funding": { + "url": "https://github.com/chalk/chalk?sponsor=1" + } + }, "node_modules/boxen/node_modules/type-fest": { "version": "2.19.0", "resolved": "https://registry.npmjs.org/type-fest/-/type-fest-2.19.0.tgz", @@ -1793,9 +1740,9 @@ "dev": true }, "node_modules/browserslist": { - "version": "4.21.3", - "resolved": "https://registry.npmjs.org/browserslist/-/browserslist-4.21.3.tgz", - "integrity": "sha512-898rgRXLAyRkM1GryrrBHGkqA5hlpkV5MhtZwg9QXeiyLUYs2k00Un05aX5l2/yJIOObYKOpS2JNo8nJDE7fWQ==", + "version": "4.21.4", + "resolved": "https://registry.npmjs.org/browserslist/-/browserslist-4.21.4.tgz", + "integrity": "sha512-CBHJJdDmgjl3daYjN5Cp5kbTf1mUhZoS+beLklHIvkOWscs83YAhLlF3Wsh/lciQYAcbBJgTOD44VtG31ZM4Hw==", "dev": true, "funding": [ { @@ -1808,10 +1755,10 @@ } ], "dependencies": { - "caniuse-lite": "^1.0.30001370", - "electron-to-chromium": "^1.4.202", + "caniuse-lite": "^1.0.30001400", + "electron-to-chromium": "^1.4.251", "node-releases": "^2.0.6", - "update-browserslist-db": "^1.0.5" + "update-browserslist-db": "^1.0.9" }, "bin": { "browserslist": "cli.js" @@ -1860,54 +1807,30 @@ } }, "node_modules/cacheable-lookup": { - "version": "6.1.0", - "resolved": "https://registry.npmjs.org/cacheable-lookup/-/cacheable-lookup-6.1.0.tgz", - "integrity": "sha512-KJ/Dmo1lDDhmW2XDPMo+9oiy/CeqosPguPCrgcVzKyZrL6pM1gU2GmPY/xo6OQPTUaA/c0kwHuywB4E6nmT9ww==", + "version": "7.0.0", + "resolved": "https://registry.npmjs.org/cacheable-lookup/-/cacheable-lookup-7.0.0.tgz", + "integrity": "sha512-+qJyx4xiKra8mZrcwhjMRMUhD5NR1R8esPkzIYxX96JiecFoxAXFuz/GpR3+ev4PE1WamHip78wV0vcmPQtp8w==", "dev": true, "engines": { - "node": ">=10.6.0" + "node": ">=14.16" } }, "node_modules/cacheable-request": { - "version": "7.0.2", - "resolved": "https://registry.npmjs.org/cacheable-request/-/cacheable-request-7.0.2.tgz", - "integrity": "sha512-pouW8/FmiPQbuGpkXQ9BAPv/Mo5xDGANgSNXzTzJ8DrKGuXOssM4wIQRjfanNRh3Yu5cfYPvcorqbhg2KIJtew==", - "dev": true, - "dependencies": { - "clone-response": "^1.0.2", - "get-stream": "^5.1.0", - "http-cache-semantics": "^4.0.0", - "keyv": "^4.0.0", - "lowercase-keys": "^2.0.0", - "normalize-url": "^6.0.1", - "responselike": "^2.0.0" - }, - "engines": { - "node": ">=8" - } - }, - "node_modules/cacheable-request/node_modules/get-stream": { - "version": "5.2.0", - "resolved": "https://registry.npmjs.org/get-stream/-/get-stream-5.2.0.tgz", - "integrity": "sha512-nBF+F1rAZVCu/p7rjzgA+Yb4lfYXrpl7a6VmJrU8wF9I1CKvP/QwPNZHnOlwbTkY6dvtFIzFMSyQXbLoTQPRpA==", + "version": "10.2.2", + "resolved": "https://registry.npmjs.org/cacheable-request/-/cacheable-request-10.2.2.tgz", + "integrity": "sha512-KxjQZM3UIo7/J6W4sLpwFvu1GB3Whv8NtZ8ZrUL284eiQjiXeeqWTdhixNrp/NLZ/JNuFBo6BD4ZaO8ZJ5BN8Q==", "dev": true, "dependencies": { - "pump": "^3.0.0" - }, - "engines": { - "node": ">=8" + "@types/http-cache-semantics": "^4.0.1", + "get-stream": "^6.0.1", + "http-cache-semantics": "^4.1.0", + "keyv": "^4.5.0", + "mimic-response": "^4.0.0", + "normalize-url": "^7.2.0", + "responselike": "^3.0.0" }, - "funding": { - "url": "https://github.com/sponsors/sindresorhus" - } - }, - "node_modules/cacheable-request/node_modules/lowercase-keys": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/lowercase-keys/-/lowercase-keys-2.0.0.tgz", - "integrity": "sha512-tqNXrS78oMOE73NMxK4EMLQsQowWf8jKooH9g7xPavRT706R6bkQJ6DY2Te7QukaZsulxa30wQ7bk0pm4XiHmA==", - "dev": true, "engines": { - "node": ">=8" + "node": ">=14.16" } }, "node_modules/caching-transform": { @@ -1957,9 +1880,9 @@ } }, "node_modules/caniuse-lite": { - "version": "1.0.30001381", - "resolved": "https://registry.npmjs.org/caniuse-lite/-/caniuse-lite-1.0.30001381.tgz", - "integrity": "sha512-fEnkDOKpvp6qc+olg7+NzE1SqyfiyKf4uci7fAU38M3zxs0YOyKOxW/nMZ2l9sJbt7KZHcDIxUnbI0Iime7V4w==", + "version": "1.0.30001425", + "resolved": "https://registry.npmjs.org/caniuse-lite/-/caniuse-lite-1.0.30001425.tgz", + "integrity": "sha512-/pzFv0OmNG6W0ym80P3NtapU0QEiDS3VuYAZMGoLLqiC7f6FJFe1MjpQDREGApeenD9wloeytmVDj+JLXPC6qw==", "dev": true, "funding": [ { @@ -1973,12 +1896,16 @@ ] }, "node_modules/chalk": { - "version": "5.0.1", - "resolved": "https://registry.npmjs.org/chalk/-/chalk-5.0.1.tgz", - "integrity": "sha512-Fo07WOYGqMfCWHOzSXOt2CxDbC6skS/jO9ynEcmpANMoPrD+W1r1K6Vx7iNm+AQmETU1Xr2t+n8nzkV9t6xh3w==", + "version": "4.1.2", + "resolved": "https://registry.npmjs.org/chalk/-/chalk-4.1.2.tgz", + "integrity": "sha512-oKnbhFyRIXpUuez8iBMmyEa4nbj4IOQyuhc/wy9kY7/WVPcwIO9VA668Pu8RkO7+0G76SLROeyw9CpQ061i4mA==", "dev": true, + "dependencies": { + "ansi-styles": "^4.1.0", + "supports-color": "^7.1.0" + }, "engines": { - "node": "^12.17.0 || ^14.13 || >=16.0.0" + "node": ">=10" }, "funding": { "url": "https://github.com/chalk/chalk?sponsor=1" @@ -2030,9 +1957,9 @@ } }, "node_modules/ci-info": { - "version": "3.3.2", - "resolved": "https://registry.npmjs.org/ci-info/-/ci-info-3.3.2.tgz", - "integrity": "sha512-xmDt/QIAdeZ9+nfdPsaBCpMvHNLFiLdjj59qjqn+6iPe6YmHGQ35sBnQ8uslRBXFmXkiZQOJRjvQeoGppoTjjg==", + "version": "3.5.0", + "resolved": "https://registry.npmjs.org/ci-info/-/ci-info-3.5.0.tgz", + "integrity": "sha512-yH4RezKOGlOhxkmhbeNuC4eYZKAUsEaGtBuBzDDP1eFUKiccDWzBABxBfOx31IDwDIXMTxWuwAxUGModvkbuVw==", "dev": true }, "node_modules/clean-stack": { @@ -2093,38 +2020,17 @@ } }, "node_modules/cliui": { - "version": "6.0.0", - "resolved": "https://registry.npmjs.org/cliui/-/cliui-6.0.0.tgz", - "integrity": "sha512-t6wbgtoCXvAzst7QgXxJYqPt0usEfbgQdftEPbLL/cvv6HPE5VgvqCuAIDR0NgU52ds6rFwqrgakNLrHEjCbrQ==", + "version": "8.0.1", + "resolved": "https://registry.npmjs.org/cliui/-/cliui-8.0.1.tgz", + "integrity": "sha512-BSeNnyus75C4//NQ9gQt1/csTXyo/8Sb+afLAkzAptFuMsod9HFokGNudZpi/oQV73hnVK+sR+5PVRMd+Dr7YQ==", "dev": true, "dependencies": { "string-width": "^4.2.0", - "strip-ansi": "^6.0.0", - "wrap-ansi": "^6.2.0" - } - }, - "node_modules/cliui/node_modules/ansi-regex": { - "version": "5.0.1", - "resolved": "https://registry.npmjs.org/ansi-regex/-/ansi-regex-5.0.1.tgz", - "integrity": "sha512-quJQXlTSUGL2LH9SUXo8VwsY4soanhgo6LNSm84E1LBcE8s3O0wpdiRzyR9z/ZZJMlMWv37qOOb9pdJlMUEKFQ==", - "dev": true, - "engines": { - "node": ">=8" - } - }, - "node_modules/cliui/node_modules/ansi-styles": { - "version": "4.3.0", - "resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-4.3.0.tgz", - "integrity": "sha512-zbB9rCJAT1rbjiVDb2hqKFHNYLxgtk8NURxZ3IZwD3F6NtxbXZQCnnSi1Lkx+IDohdPlFp222wVALIheZJQSEg==", - "dev": true, - "dependencies": { - "color-convert": "^2.0.1" + "strip-ansi": "^6.0.1", + "wrap-ansi": "^7.0.0" }, "engines": { - "node": ">=8" - }, - "funding": { - "url": "https://github.com/chalk/ansi-styles?sponsor=1" + "node": ">=12" } }, "node_modules/cliui/node_modules/emoji-regex": { @@ -2147,22 +2053,10 @@ "node": ">=8" } }, - "node_modules/cliui/node_modules/strip-ansi": { - "version": "6.0.1", - "resolved": "https://registry.npmjs.org/strip-ansi/-/strip-ansi-6.0.1.tgz", - "integrity": "sha512-Y38VPSHcqkFrCpFnQ9vuSXmquuv5oXOKpGeT6aGrr3o3Gc9AlVa6JBfUSOCnbxGGZF+/0ooI7KrPuUSztUdU5A==", - "dev": true, - "dependencies": { - "ansi-regex": "^5.0.1" - }, - "engines": { - "node": ">=8" - } - }, "node_modules/cliui/node_modules/wrap-ansi": { - "version": "6.2.0", - "resolved": "https://registry.npmjs.org/wrap-ansi/-/wrap-ansi-6.2.0.tgz", - "integrity": "sha512-r6lPcBGxZXlIcymEu7InxDMhdW0KDxpLgoFLcguasxCaJ/SOIZwINatK9KY/tf+ZrlywOKU0UDj3ATXUBfxJXA==", + "version": "7.0.0", + "resolved": "https://registry.npmjs.org/wrap-ansi/-/wrap-ansi-7.0.0.tgz", + "integrity": "sha512-YVGIj2kamLSTxw6NsZjoBxfSwsn0ycdesmc4p+Q21c5zPuZ1pl+NfxVdxPtdHvmNVOQ6XSYG4AUtyt/Fi7D16Q==", "dev": true, "dependencies": { "ansi-styles": "^4.0.0", @@ -2170,7 +2064,10 @@ "strip-ansi": "^6.0.0" }, "engines": { - "node": ">=8" + "node": ">=10" + }, + "funding": { + "url": "https://github.com/chalk/wrap-ansi?sponsor=1" } }, "node_modules/clone": { @@ -2182,22 +2079,10 @@ "node": ">=0.8" } }, - "node_modules/clone-response": { - "version": "1.0.3", - "resolved": "https://registry.npmjs.org/clone-response/-/clone-response-1.0.3.tgz", - "integrity": "sha512-ROoL94jJH2dUVML2Y/5PEDNaSHgeOdSDicUyS7izcF63G6sTc/FTjLub4b8Il9S8S0beOfYt0TaA5qvFK+w0wA==", - "dev": true, - "dependencies": { - "mimic-response": "^1.0.0" - }, - "funding": { - "url": "https://github.com/sponsors/sindresorhus" - } - }, "node_modules/cluster-key-slot": { - "version": "1.1.0", - "resolved": "https://registry.npmjs.org/cluster-key-slot/-/cluster-key-slot-1.1.0.tgz", - "integrity": "sha512-2Nii8p3RwAPiFwsnZvukotvow2rIHM+yQ6ZcBXGHdniadkYGZYiGmkHJIbZPIV9nfv7m/U1IPMVVcAhoWFeklw==", + "version": "1.1.1", + "resolved": "https://registry.npmjs.org/cluster-key-slot/-/cluster-key-slot-1.1.1.tgz", + "integrity": "sha512-rwHwUfXL40Chm1r08yrhU3qpUvdVlgkKNeyeGPOxnW8/SyVDvgRaed/Uz54AqWNaTCAThlj6QAs3TZcKI0xDEw==", "engines": { "node": ">=0.10.0" } @@ -2244,19 +2129,6 @@ "integrity": "sha512-W9pAhw0ja1Edb5GVdIF1mjZw/ASI0AlShXM83UUGe2DVr5TdAPEA1OA8m/g8zWp9x6On7gqufY+FatDbC3MDQg==", "dev": true }, - "node_modules/compress-brotli": { - "version": "1.3.8", - "resolved": "https://registry.npmjs.org/compress-brotli/-/compress-brotli-1.3.8.tgz", - "integrity": "sha512-lVcQsjhxhIXsuupfy9fmZUFtAIdBmXA7EGY6GBdgZ++qkM9zG4YFT8iU7FoBxzryNDMOpD1HIFHUSX4D87oqhQ==", - "dev": true, - "dependencies": { - "@types/json-buffer": "~3.0.0", - "json-buffer": "~3.0.1" - }, - "engines": { - "node": ">= 12" - } - }, "node_modules/concat-map": { "version": "0.0.1", "resolved": "https://registry.npmjs.org/concat-map/-/concat-map-0.0.1.tgz", @@ -2299,13 +2171,10 @@ } }, "node_modules/convert-source-map": { - "version": "1.8.0", - "resolved": "https://registry.npmjs.org/convert-source-map/-/convert-source-map-1.8.0.tgz", - "integrity": "sha512-+OQdjP49zViI/6i7nIJpA8rAl4sV/JdPfU9nZs3VqOwGIgizICvuN2ru6fMd+4llL0tar18UYJXfZ/TWtmhUjA==", - "dev": true, - "dependencies": { - "safe-buffer": "~5.1.1" - } + "version": "1.9.0", + "resolved": "https://registry.npmjs.org/convert-source-map/-/convert-source-map-1.9.0.tgz", + "integrity": "sha512-ASFBup0Mz1uyiIjANan1jzLQami9z1PoYSZCiiYW2FczPbenXc45FZdBZLzOT+r6+iciuEModtmCti+hjaAk0A==", + "dev": true }, "node_modules/core-util-is": { "version": "1.0.3", @@ -2364,6 +2233,18 @@ "url": "https://github.com/sponsors/sindresorhus" } }, + "node_modules/crypto-random-string/node_modules/type-fest": { + "version": "1.4.0", + "resolved": "https://registry.npmjs.org/type-fest/-/type-fest-1.4.0.tgz", + "integrity": "sha512-yGSza74xk0UG8k+pLh5oeoYirvIiWo5t0/o3zHHAO2tRDiZcxWP7fywNlXhqb6/r6sWvwi+RsyQMWhVLe4BVuA==", + "dev": true, + "engines": { + "node": ">=10" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, "node_modules/data-uri-to-buffer": { "version": "4.0.0", "resolved": "https://registry.npmjs.org/data-uri-to-buffer/-/data-uri-to-buffer-4.0.0.tgz", @@ -2442,24 +2323,30 @@ "dev": true }, "node_modules/default-require-extensions": { - "version": "3.0.0", - "resolved": "https://registry.npmjs.org/default-require-extensions/-/default-require-extensions-3.0.0.tgz", - "integrity": "sha512-ek6DpXq/SCpvjhpFsLFRVtIxJCRw6fUR42lYMVZuUMK7n8eMz4Uh5clckdBjEpLhn/gEBZo7hDJnJcwdKLKQjg==", + "version": "3.0.1", + "resolved": "https://registry.npmjs.org/default-require-extensions/-/default-require-extensions-3.0.1.tgz", + "integrity": "sha512-eXTJmRbm2TIt9MgWTsOH1wEuhew6XGZcMeGKCtLedIg/NCsg1iBePXkceTdK4Fii7pzmN9tGsZhKzZ4h7O/fxw==", "dev": true, "dependencies": { "strip-bom": "^4.0.0" }, "engines": { "node": ">=8" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" } }, "node_modules/defaults": { - "version": "1.0.3", - "resolved": "https://registry.npmjs.org/defaults/-/defaults-1.0.3.tgz", - "integrity": "sha512-s82itHOnYrN0Ib8r+z7laQz3sdE+4FP3d9Q7VLO7U+KRT+CR0GsWuyHxzdAY82I7cXv0G/twrqomTJLOssO5HA==", + "version": "1.0.4", + "resolved": "https://registry.npmjs.org/defaults/-/defaults-1.0.4.tgz", + "integrity": "sha512-eFuaLoy/Rxalv2kr+lqMlUnrDWV+3j4pljOIJgLIhI058IQfWJ7vXhyEIHu+HtC738klGALYxOKDO0bQP3tg8A==", "dev": true, "dependencies": { "clone": "^1.0.2" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" } }, "node_modules/defer-to-connect": { @@ -2590,9 +2477,9 @@ "dev": true }, "node_modules/electron-to-chromium": { - "version": "1.4.225", - "resolved": "https://registry.npmjs.org/electron-to-chromium/-/electron-to-chromium-1.4.225.tgz", - "integrity": "sha512-ICHvGaCIQR3P88uK8aRtx8gmejbVJyC6bB4LEC3anzBrIzdzC7aiZHY4iFfXhN4st6I7lMO0x4sgBHf/7kBvRw==", + "version": "1.4.284", + "resolved": "https://registry.npmjs.org/electron-to-chromium/-/electron-to-chromium-1.4.284.tgz", + "integrity": "sha512-M8WEXFuKXMYMVr45fo8mq0wUrrJHheiKZf6BArTKk9ZBYCKJEOU5H8cdWgDT+qCVZf7Na4lVUaZsA+h6uA9+PA==", "dev": true }, "node_modules/email-addresses": { @@ -2607,15 +2494,6 @@ "integrity": "sha512-L18DaJsXSUk2+42pv8mLs5jJT2hqFkFE4j21wOmgbUqsZ2hL72NsUU785g9RXgo3s0ZNgVl42TiHp3ZtOv/Vyg==", "dev": true }, - "node_modules/end-of-stream": { - "version": "1.4.4", - "resolved": "https://registry.npmjs.org/end-of-stream/-/end-of-stream-1.4.4.tgz", - "integrity": "sha512-+uw1inIHVPQoaVuHzRyXd21icM+cnt4CzD5rW+NC1wjOUSTOs+Te7FOv7AhN7vS9x/oIyhLP5PR1H+phQAHu5Q==", - "dev": true, - "dependencies": { - "once": "^1.4.0" - } - }, "node_modules/error-ex": { "version": "1.3.2", "resolved": "https://registry.npmjs.org/error-ex/-/error-ex-1.3.2.tgz", @@ -2626,31 +2504,32 @@ } }, "node_modules/es-abstract": { - "version": "1.20.1", - "resolved": "https://registry.npmjs.org/es-abstract/-/es-abstract-1.20.1.tgz", - "integrity": "sha512-WEm2oBhfoI2sImeM4OF2zE2V3BYdSF+KnSi9Sidz51fQHd7+JuF8Xgcj9/0o+OWeIeIS/MiuNnlruQrJf16GQA==", + "version": "1.20.4", + "resolved": "https://registry.npmjs.org/es-abstract/-/es-abstract-1.20.4.tgz", + "integrity": "sha512-0UtvRN79eMe2L+UNEF1BwRe364sj/DXhQ/k5FmivgoSdpM90b8Jc0mDzKMGo7QS0BVbOP/bTwBKNnDc9rNzaPA==", "dev": true, "dependencies": { "call-bind": "^1.0.2", "es-to-primitive": "^1.2.1", "function-bind": "^1.1.1", "function.prototype.name": "^1.1.5", - "get-intrinsic": "^1.1.1", + "get-intrinsic": "^1.1.3", "get-symbol-description": "^1.0.0", "has": "^1.0.3", "has-property-descriptors": "^1.0.0", "has-symbols": "^1.0.3", "internal-slot": "^1.0.3", - "is-callable": "^1.2.4", + "is-callable": "^1.2.7", "is-negative-zero": "^2.0.2", "is-regex": "^1.1.4", "is-shared-array-buffer": "^1.0.2", "is-string": "^1.0.7", "is-weakref": "^1.0.2", - "object-inspect": "^1.12.0", + "object-inspect": "^1.12.2", "object-keys": "^1.1.1", - "object.assign": "^4.1.2", + "object.assign": "^4.1.4", "regexp.prototype.flags": "^1.4.3", + "safe-regex-test": "^1.0.0", "string.prototype.trimend": "^1.0.5", "string.prototype.trimstart": "^1.0.5", "unbox-primitive": "^1.0.2" @@ -2732,12 +2611,12 @@ } }, "node_modules/escape-string-regexp": { - "version": "5.0.0", - "resolved": "https://registry.npmjs.org/escape-string-regexp/-/escape-string-regexp-5.0.0.tgz", - "integrity": "sha512-/veY75JbMK4j1yjvuUxuVsiS/hr/4iHs9FTT6cgTexxdE0Ly/glccBAkloH/DofkjRbZU3bnoj38mOmhkZ0lHw==", + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/escape-string-regexp/-/escape-string-regexp-4.0.0.tgz", + "integrity": "sha512-TtpcNJ3XAzx3Gq8sWRzJaVajRs0uVxA2YAkdb1jm2YkPz4G6egUFAyA3n5vtEIZefPk5Wa4UXbKuS5fKkJWdgA==", "dev": true, "engines": { - "node": ">=12" + "node": ">=10" }, "funding": { "url": "https://github.com/sponsors/sindresorhus" @@ -2817,14 +2696,15 @@ } }, "node_modules/eslint": { - "version": "8.22.0", - "resolved": "https://registry.npmjs.org/eslint/-/eslint-8.22.0.tgz", - "integrity": "sha512-ci4t0sz6vSRKdmkOGmprBo6fmI4PrphDFMy5JEq/fNS0gQkJM3rLmrqcp8ipMcdobH3KtUP40KniAE9W19S4wA==", + "version": "8.26.0", + "resolved": "https://registry.npmjs.org/eslint/-/eslint-8.26.0.tgz", + "integrity": "sha512-kzJkpaw1Bfwheq4VXUezFriD1GxszX6dUekM7Z3aC2o4hju+tsR/XyTC3RcoSD7jmy9VkPU3+N6YjVU2e96Oyg==", "dev": true, "dependencies": { - "@eslint/eslintrc": "^1.3.0", - "@humanwhocodes/config-array": "^0.10.4", - "@humanwhocodes/gitignore-to-minimatch": "^1.0.2", + "@eslint/eslintrc": "^1.3.3", + "@humanwhocodes/config-array": "^0.11.6", + "@humanwhocodes/module-importer": "^1.0.1", + "@nodelib/fs.walk": "^1.2.8", "ajv": "^6.10.0", "chalk": "^4.0.0", "cross-spawn": "^7.0.2", @@ -2834,21 +2714,21 @@ "eslint-scope": "^7.1.1", "eslint-utils": "^3.0.0", "eslint-visitor-keys": "^3.3.0", - "espree": "^9.3.3", + "espree": "^9.4.0", "esquery": "^1.4.0", "esutils": "^2.0.2", "fast-deep-equal": "^3.1.3", "file-entry-cache": "^6.0.1", "find-up": "^5.0.0", - "functional-red-black-tree": "^1.0.1", - "glob-parent": "^6.0.1", + "glob-parent": "^6.0.2", "globals": "^13.15.0", - "globby": "^11.1.0", "grapheme-splitter": "^1.0.4", "ignore": "^5.2.0", "import-fresh": "^3.0.0", "imurmurhash": "^0.1.4", "is-glob": "^4.0.0", + "is-path-inside": "^3.0.3", + "js-sdsl": "^4.1.4", "js-yaml": "^4.1.0", "json-stable-stringify-without-jsonify": "^1.0.1", "levn": "^0.4.1", @@ -2859,8 +2739,7 @@ "regexpp": "^3.2.0", "strip-ansi": "^6.0.1", "strip-json-comments": "^3.1.0", - "text-table": "^0.2.0", - "v8-compile-cache": "^2.0.3" + "text-table": "^0.2.0" }, "bin": { "eslint": "bin/eslint.js" @@ -2921,67 +2800,6 @@ "node": "^12.22.0 || ^14.17.0 || >=16.0.0" } }, - "node_modules/eslint/node_modules/ansi-regex": { - "version": "5.0.1", - "resolved": "https://registry.npmjs.org/ansi-regex/-/ansi-regex-5.0.1.tgz", - "integrity": "sha512-quJQXlTSUGL2LH9SUXo8VwsY4soanhgo6LNSm84E1LBcE8s3O0wpdiRzyR9z/ZZJMlMWv37qOOb9pdJlMUEKFQ==", - "dev": true, - "engines": { - "node": ">=8" - } - }, - "node_modules/eslint/node_modules/ansi-styles": { - "version": "4.3.0", - "resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-4.3.0.tgz", - "integrity": "sha512-zbB9rCJAT1rbjiVDb2hqKFHNYLxgtk8NURxZ3IZwD3F6NtxbXZQCnnSi1Lkx+IDohdPlFp222wVALIheZJQSEg==", - "dev": true, - "dependencies": { - "color-convert": "^2.0.1" - }, - "engines": { - "node": ">=8" - }, - "funding": { - "url": "https://github.com/chalk/ansi-styles?sponsor=1" - } - }, - "node_modules/eslint/node_modules/array-union": { - "version": "2.1.0", - "resolved": "https://registry.npmjs.org/array-union/-/array-union-2.1.0.tgz", - "integrity": "sha512-HGyxoOTYUyCM6stUe6EJgnd4EoewAI7zMdfqO+kGjnlZmBDz/cR5pf8r/cR4Wq60sL/p0IkcjUEEPwS3GFrIyw==", - "dev": true, - "engines": { - "node": ">=8" - } - }, - "node_modules/eslint/node_modules/chalk": { - "version": "4.1.2", - "resolved": "https://registry.npmjs.org/chalk/-/chalk-4.1.2.tgz", - "integrity": "sha512-oKnbhFyRIXpUuez8iBMmyEa4nbj4IOQyuhc/wy9kY7/WVPcwIO9VA668Pu8RkO7+0G76SLROeyw9CpQ061i4mA==", - "dev": true, - "dependencies": { - "ansi-styles": "^4.1.0", - "supports-color": "^7.1.0" - }, - "engines": { - "node": ">=10" - }, - "funding": { - "url": "https://github.com/chalk/chalk?sponsor=1" - } - }, - "node_modules/eslint/node_modules/escape-string-regexp": { - "version": "4.0.0", - "resolved": "https://registry.npmjs.org/escape-string-regexp/-/escape-string-regexp-4.0.0.tgz", - "integrity": "sha512-TtpcNJ3XAzx3Gq8sWRzJaVajRs0uVxA2YAkdb1jm2YkPz4G6egUFAyA3n5vtEIZefPk5Wa4UXbKuS5fKkJWdgA==", - "dev": true, - "engines": { - "node": ">=10" - }, - "funding": { - "url": "https://github.com/sponsors/sindresorhus" - } - }, "node_modules/eslint/node_modules/eslint-scope": { "version": "7.1.1", "resolved": "https://registry.npmjs.org/eslint-scope/-/eslint-scope-7.1.1.tgz", @@ -3004,114 +2822,21 @@ "node": ">=4.0" } }, - "node_modules/eslint/node_modules/find-up": { - "version": "5.0.0", - "resolved": "https://registry.npmjs.org/find-up/-/find-up-5.0.0.tgz", - "integrity": "sha512-78/PXT1wlLLDgTzDs7sjq9hzz0vXD+zn+7wypEe4fXQxCmdmqfGsEPQxmiCSQI3ajFV91bVSsvNtrJRiW6nGng==", + "node_modules/espree": { + "version": "9.4.0", + "resolved": "https://registry.npmjs.org/espree/-/espree-9.4.0.tgz", + "integrity": "sha512-DQmnRpLj7f6TgN/NYb0MTzJXL+vJF9h3pHy4JhCIs3zwcgez8xmGg3sXHcEO97BrmO2OSvCwMdfdlyl+E9KjOw==", "dev": true, "dependencies": { - "locate-path": "^6.0.0", - "path-exists": "^4.0.0" + "acorn": "^8.8.0", + "acorn-jsx": "^5.3.2", + "eslint-visitor-keys": "^3.3.0" }, "engines": { - "node": ">=10" + "node": "^12.22.0 || ^14.17.0 || >=16.0.0" }, "funding": { - "url": "https://github.com/sponsors/sindresorhus" - } - }, - "node_modules/eslint/node_modules/globby": { - "version": "11.1.0", - "resolved": "https://registry.npmjs.org/globby/-/globby-11.1.0.tgz", - "integrity": "sha512-jhIXaOzy1sb8IyocaruWSn1TjmnBVs8Ayhcy83rmxNJ8q2uWKCAj3CnJY+KpGSXCueAPc0i05kVvVKtP1t9S3g==", - "dev": true, - "dependencies": { - "array-union": "^2.1.0", - "dir-glob": "^3.0.1", - "fast-glob": "^3.2.9", - "ignore": "^5.2.0", - "merge2": "^1.4.1", - "slash": "^3.0.0" - }, - "engines": { - "node": ">=10" - }, - "funding": { - "url": "https://github.com/sponsors/sindresorhus" - } - }, - "node_modules/eslint/node_modules/locate-path": { - "version": "6.0.0", - "resolved": "https://registry.npmjs.org/locate-path/-/locate-path-6.0.0.tgz", - "integrity": "sha512-iPZK6eYjbxRu3uB4/WZ3EsEIMJFMqAoopl3R+zuq0UjcAm/MO6KCweDgPfP3elTztoKP3KtnVHxTn2NHBSDVUw==", - "dev": true, - "dependencies": { - "p-locate": "^5.0.0" - }, - "engines": { - "node": ">=10" - }, - "funding": { - "url": "https://github.com/sponsors/sindresorhus" - } - }, - "node_modules/eslint/node_modules/p-limit": { - "version": "3.1.0", - "resolved": "https://registry.npmjs.org/p-limit/-/p-limit-3.1.0.tgz", - "integrity": "sha512-TYOanM3wGwNGsZN2cVTYPArw454xnXj5qmWF1bEoAc4+cU/ol7GVh7odevjp1FNHduHc3KZMcFduxU5Xc6uJRQ==", - "dev": true, - "dependencies": { - "yocto-queue": "^0.1.0" - }, - "engines": { - "node": ">=10" - }, - "funding": { - "url": "https://github.com/sponsors/sindresorhus" - } - }, - "node_modules/eslint/node_modules/p-locate": { - "version": "5.0.0", - "resolved": "https://registry.npmjs.org/p-locate/-/p-locate-5.0.0.tgz", - "integrity": "sha512-LaNjtRWUBY++zB5nE/NwcaoMylSPk+S+ZHNB1TzdbMJMny6dynpAGt7X/tl/QYq3TIeE6nxHppbo2LGymrG5Pw==", - "dev": true, - "dependencies": { - "p-limit": "^3.0.2" - }, - "engines": { - "node": ">=10" - }, - "funding": { - "url": "https://github.com/sponsors/sindresorhus" - } - }, - "node_modules/eslint/node_modules/strip-ansi": { - "version": "6.0.1", - "resolved": "https://registry.npmjs.org/strip-ansi/-/strip-ansi-6.0.1.tgz", - "integrity": "sha512-Y38VPSHcqkFrCpFnQ9vuSXmquuv5oXOKpGeT6aGrr3o3Gc9AlVa6JBfUSOCnbxGGZF+/0ooI7KrPuUSztUdU5A==", - "dev": true, - "dependencies": { - "ansi-regex": "^5.0.1" - }, - "engines": { - "node": ">=8" - } - }, - "node_modules/espree": { - "version": "9.3.3", - "resolved": "https://registry.npmjs.org/espree/-/espree-9.3.3.tgz", - "integrity": "sha512-ORs1Rt/uQTqUKjDdGCyrtYxbazf5umATSf/K4qxjmZHORR6HJk+2s/2Pqe+Kk49HHINC/xNIrGfgh8sZcll0ng==", - "dev": true, - "dependencies": { - "acorn": "^8.8.0", - "acorn-jsx": "^5.3.2", - "eslint-visitor-keys": "^3.3.0" - }, - "engines": { - "node": "^12.22.0 || ^14.17.0 || >=16.0.0" - }, - "funding": { - "url": "https://opencollective.com/eslint" + "url": "https://opencollective.com/eslint" } }, "node_modules/esprima": { @@ -3210,6 +2935,18 @@ "url": "https://github.com/sindresorhus/execa?sponsor=1" } }, + "node_modules/execa/node_modules/is-stream": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/is-stream/-/is-stream-3.0.0.tgz", + "integrity": "sha512-LnQR4bZ9IADDRSkvpqMGvt/tEJWclzklNgSw48V5EAaAeDd6qGvN8ei6k5p0tvxSR171VmGyHuTiAOfxAbr8kA==", + "dev": true, + "engines": { + "node": "^12.20.0 || ^14.13.1 || >=16.0.0" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, "node_modules/external-editor": { "version": "3.1.0", "resolved": "https://registry.npmjs.org/external-editor/-/external-editor-3.1.0.tgz", @@ -3231,9 +2968,9 @@ "dev": true }, "node_modules/fast-glob": { - "version": "3.2.11", - "resolved": "https://registry.npmjs.org/fast-glob/-/fast-glob-3.2.11.tgz", - "integrity": "sha512-xrO3+1bxSo3ZVHAnqzyuewYT6aMFHRAd4Kcs92MAonjwQZLsK9d0SF1IyQ3k5PoirxTW0Oe/RqFgMQ6TcNE5Ew==", + "version": "3.2.12", + "resolved": "https://registry.npmjs.org/fast-glob/-/fast-glob-3.2.12.tgz", + "integrity": "sha512-DVj4CQIYYow0BlaelwK1pHl5n5cRSJfM60UA0zK891sVInoPri2Ekj7+e1CT3/3qxXenpI+nBBmQAcJPJgaj4w==", "dev": true, "dependencies": { "@nodelib/fs.stat": "^2.0.2", @@ -3303,14 +3040,38 @@ } }, "node_modules/figures": { - "version": "4.0.1", - "resolved": "https://registry.npmjs.org/figures/-/figures-4.0.1.tgz", - "integrity": "sha512-rElJwkA/xS04Vfg+CaZodpso7VqBknOYbzi6I76hI4X80RUjkSxO2oAyPmGbuXUppywjqndOrQDl817hDnI++w==", + "version": "5.0.0", + "resolved": "https://registry.npmjs.org/figures/-/figures-5.0.0.tgz", + "integrity": "sha512-ej8ksPF4x6e5wvK9yevct0UCXh8TTFlWGVLlgjZuoBH1HwjIfKE/IdL5mq89sFA7zELi1VhKpmtDnrs7zWyeyg==", "dev": true, "dependencies": { "escape-string-regexp": "^5.0.0", "is-unicode-supported": "^1.2.0" }, + "engines": { + "node": ">=14" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/figures/node_modules/escape-string-regexp": { + "version": "5.0.0", + "resolved": "https://registry.npmjs.org/escape-string-regexp/-/escape-string-regexp-5.0.0.tgz", + "integrity": "sha512-/veY75JbMK4j1yjvuUxuVsiS/hr/4iHs9FTT6cgTexxdE0Ly/glccBAkloH/DofkjRbZU3bnoj38mOmhkZ0lHw==", + "dev": true, + "engines": { + "node": ">=12" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/figures/node_modules/is-unicode-supported": { + "version": "1.3.0", + "resolved": "https://registry.npmjs.org/is-unicode-supported/-/is-unicode-supported-1.3.0.tgz", + "integrity": "sha512-43r2mRvz+8JRIKnWJ+3j8JtjRKZ6GmjzfaE/qiBJnikNnYv/6bagRJ1kUhNk8R5EX/GkobD+r+sfxCPJsiKBLQ==", + "dev": true, "engines": { "node": ">=12" }, @@ -3395,16 +3156,19 @@ } }, "node_modules/find-up": { - "version": "4.1.0", - "resolved": "https://registry.npmjs.org/find-up/-/find-up-4.1.0.tgz", - "integrity": "sha512-PpOwAdQ/YlXQ2vj8a3h8IipDuYRi3wceVQQGYWxNINccq40Anw7BlsEXCMbt1Zt+OLA6Fq9suIpIWD0OsnISlw==", + "version": "5.0.0", + "resolved": "https://registry.npmjs.org/find-up/-/find-up-5.0.0.tgz", + "integrity": "sha512-78/PXT1wlLLDgTzDs7sjq9hzz0vXD+zn+7wypEe4fXQxCmdmqfGsEPQxmiCSQI3ajFV91bVSsvNtrJRiW6nGng==", "dev": true, "dependencies": { - "locate-path": "^5.0.0", + "locate-path": "^6.0.0", "path-exists": "^4.0.0" }, "engines": { - "node": ">=8" + "node": ">=10" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" } }, "node_modules/flat": { @@ -3463,9 +3227,9 @@ } }, "node_modules/form-data-encoder": { - "version": "2.1.0", - "resolved": "https://registry.npmjs.org/form-data-encoder/-/form-data-encoder-2.1.0.tgz", - "integrity": "sha512-njK60LnfhfDWy+AEUIf9ZQNRAcmXCdDfiNOm2emuPtzwh7U9k/mo9F3S54aPiaZ3vhqUjikVLfcPg2KuBddskQ==", + "version": "2.1.3", + "resolved": "https://registry.npmjs.org/form-data-encoder/-/form-data-encoder-2.1.3.tgz", + "integrity": "sha512-KqU0nnPMgIJcCOFTNJFEA8epcseEaoox4XZffTgy8jlI6pL/5EFyR54NRG7CnCJN0biY7q52DO3MH6/sJ/TKlQ==", "dev": true, "engines": { "node": ">= 14.17" @@ -3598,12 +3362,6 @@ "url": "https://github.com/sponsors/ljharb" } }, - "node_modules/functional-red-black-tree": { - "version": "1.0.1", - "resolved": "https://registry.npmjs.org/functional-red-black-tree/-/functional-red-black-tree-1.0.1.tgz", - "integrity": "sha512-dsKNQNdj6xA3T+QlADDA7mOSlX0qiMINjn0cgr+eGHGsbSHzTabcIogz2+p/iqP1Xs6EP/sS2SbqH+brGTbq0g==", - "dev": true - }, "node_modules/functions-have-names": { "version": "1.2.3", "resolved": "https://registry.npmjs.org/functions-have-names/-/functions-have-names-1.2.3.tgz", @@ -3614,9 +3372,9 @@ } }, "node_modules/generic-pool": { - "version": "3.8.2", - "resolved": "https://registry.npmjs.org/generic-pool/-/generic-pool-3.8.2.tgz", - "integrity": "sha512-nGToKy6p3PAbYQ7p1UlWl6vSPwfwU6TMSWK7TTu+WUY4ZjyZQGniGGt2oNVvyNSpyZYSB43zMXVLcBm08MTMkg==", + "version": "3.9.0", + "resolved": "https://registry.npmjs.org/generic-pool/-/generic-pool-3.9.0.tgz", + "integrity": "sha512-hymDOu5B53XvN4QT9dBmZxPX4CWhBPPLguTZ9MMFeFa/Kg0xWVfylOVNlJji/E7yTZWFd/q9GO5TxDLq156D7g==", "engines": { "node": ">= 4" } @@ -3640,9 +3398,9 @@ } }, "node_modules/get-intrinsic": { - "version": "1.1.2", - "resolved": "https://registry.npmjs.org/get-intrinsic/-/get-intrinsic-1.1.2.tgz", - "integrity": "sha512-Jfm3OyCxHh9DJyc28qGk+JmfkpO41A4XkneDSujN9MDXrm4oDKdHvndhZ2dN94+ERNfkYJWDclW6k2L/ZGHjXA==", + "version": "1.1.3", + "resolved": "https://registry.npmjs.org/get-intrinsic/-/get-intrinsic-1.1.3.tgz", + "integrity": "sha512-QJVz1Tj7MS099PevUG5jvnt9tSkXN8K14dxQlikJuPt4uD9hHAHjLyLBiLR5zELelBdD9QNRAXZzsJx0WaDL9A==", "dev": true, "dependencies": { "function-bind": "^1.1.1", @@ -3738,35 +3496,63 @@ "node": ">=10" } }, + "node_modules/gh-pages/node_modules/array-union": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/array-union/-/array-union-1.0.2.tgz", + "integrity": "sha512-Dxr6QJj/RdU/hCaBjOfxW+q6lyuVE6JFWIrAUpuOOhoJJoQ99cUn3igRaHVB5P9WrgFVN0FfArM3x0cueOU8ng==", + "dev": true, + "dependencies": { + "array-uniq": "^1.0.1" + }, + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/gh-pages/node_modules/globby": { + "version": "6.1.0", + "resolved": "https://registry.npmjs.org/globby/-/globby-6.1.0.tgz", + "integrity": "sha512-KVbFv2TQtbzCoxAnfD6JcHZTYCzyliEaaeM/gH8qQdkKr5s0OP9scEgvdcngyk7AVdY6YVW/TJHd+lQ/Df3Daw==", + "dev": true, + "dependencies": { + "array-union": "^1.0.1", + "glob": "^7.0.3", + "object-assign": "^4.0.1", + "pify": "^2.0.0", + "pinkie-promise": "^2.0.0" + }, + "engines": { + "node": ">=0.10.0" + } + }, "node_modules/git-up": { - "version": "6.0.0", - "resolved": "https://registry.npmjs.org/git-up/-/git-up-6.0.0.tgz", - "integrity": "sha512-6RUFSNd1c/D0xtGnyWN2sxza2bZtZ/EmI9448n6rCZruFwV/ezeEn2fJP7XnUQGwf0RAtd/mmUCbtH6JPYA2SA==", + "version": "7.0.0", + "resolved": "https://registry.npmjs.org/git-up/-/git-up-7.0.0.tgz", + "integrity": "sha512-ONdIrbBCFusq1Oy0sC71F5azx8bVkvtZtMJAsv+a6lz5YAmbNnLD6HAB4gptHZVLPR8S2/kVN6Gab7lryq5+lQ==", "dev": true, "dependencies": { "is-ssh": "^1.4.0", - "parse-url": "^7.0.2" + "parse-url": "^8.1.0" } }, "node_modules/git-url-parse": { - "version": "12.0.0", - "resolved": "https://registry.npmjs.org/git-url-parse/-/git-url-parse-12.0.0.tgz", - "integrity": "sha512-I6LMWsxV87vysX1WfsoglXsXg6GjQRKq7+Dgiseo+h0skmp5Hp2rzmcEIRQot9CPA+uzU7x1x7jZdqvTFGnB+Q==", + "version": "13.1.0", + "resolved": "https://registry.npmjs.org/git-url-parse/-/git-url-parse-13.1.0.tgz", + "integrity": "sha512-5FvPJP/70WkIprlUZ33bm4UAaFdjcLkJLpWft1BeZKqwR0uhhNGoKwlUaPtVb4LxCSQ++erHapRak9kWGj+FCA==", "dev": true, "dependencies": { - "git-up": "^6.0.0" + "git-up": "^7.0.0" } }, "node_modules/glob": { - "version": "7.2.3", - "resolved": "https://registry.npmjs.org/glob/-/glob-7.2.3.tgz", - "integrity": "sha512-nFR0zLpU2YCaRxwoCJvL6UvCH2JFyFVIvwTLsIf21AuHlMskA1hhTdk+LlYJtOlYt9v6dvszD2BGRqBL+iQK9Q==", + "version": "7.2.0", + "resolved": "https://registry.npmjs.org/glob/-/glob-7.2.0.tgz", + "integrity": "sha512-lmLf6gtyrPq8tTjSmrO94wBeQbFR3HbLHbuyD69wuyQkImp2hWqMGB47OX65FBkPffO641IP9jWa1z4ivqG26Q==", "dev": true, "dependencies": { "fs.realpath": "^1.0.0", "inflight": "^1.0.4", "inherits": "2", - "minimatch": "^3.1.1", + "minimatch": "^3.0.4", "once": "^1.3.0", "path-is-absolute": "^1.0.0" }, @@ -3819,11 +3605,19 @@ "url": "https://github.com/sponsors/sindresorhus" } }, - "node_modules/globals/node_modules/type-fest": { - "version": "0.20.2", - "resolved": "https://registry.npmjs.org/type-fest/-/type-fest-0.20.2.tgz", - "integrity": "sha512-Ne+eE4r0/iWnpAxD852z3A+N0Bt5RN//NjJwRd2VFHEmrywxf5vsZlh4R6lixl6B+wz/8d+maTSAkN1FIkI3LQ==", + "node_modules/globby": { + "version": "11.1.0", + "resolved": "https://registry.npmjs.org/globby/-/globby-11.1.0.tgz", + "integrity": "sha512-jhIXaOzy1sb8IyocaruWSn1TjmnBVs8Ayhcy83rmxNJ8q2uWKCAj3CnJY+KpGSXCueAPc0i05kVvVKtP1t9S3g==", "dev": true, + "dependencies": { + "array-union": "^2.1.0", + "dir-glob": "^3.0.1", + "fast-glob": "^3.2.9", + "ignore": "^5.2.0", + "merge2": "^1.4.1", + "slash": "^3.0.0" + }, "engines": { "node": ">=10" }, @@ -3831,41 +3625,23 @@ "url": "https://github.com/sponsors/sindresorhus" } }, - "node_modules/globby": { - "version": "6.1.0", - "resolved": "https://registry.npmjs.org/globby/-/globby-6.1.0.tgz", - "integrity": "sha512-KVbFv2TQtbzCoxAnfD6JcHZTYCzyliEaaeM/gH8qQdkKr5s0OP9scEgvdcngyk7AVdY6YVW/TJHd+lQ/Df3Daw==", - "dev": true, - "dependencies": { - "array-union": "^1.0.1", - "glob": "^7.0.3", - "object-assign": "^4.0.1", - "pify": "^2.0.0", - "pinkie-promise": "^2.0.0" - }, - "engines": { - "node": ">=0.10.0" - } - }, "node_modules/got": { - "version": "12.3.1", - "resolved": "https://registry.npmjs.org/got/-/got-12.3.1.tgz", - "integrity": "sha512-tS6+JMhBh4iXMSXF6KkIsRxmloPln31QHDlcb6Ec3bzxjjFJFr/8aXdpyuLmVc9I4i2HyBHYw1QU5K1ruUdpkw==", + "version": "12.5.1", + "resolved": "https://registry.npmjs.org/got/-/got-12.5.1.tgz", + "integrity": "sha512-sD16AK8cCyUoPtKr/NMvLTFFa+T3i3S+zoiuvhq0HP2YiqBZA9AtlBjAdsQBsLBK7slPuvmfE0OxhGi7N5dD4w==", "dev": true, "dependencies": { "@sindresorhus/is": "^5.2.0", "@szmarczak/http-timer": "^5.0.1", - "@types/cacheable-request": "^6.0.2", - "@types/responselike": "^1.0.0", - "cacheable-lookup": "^6.0.4", - "cacheable-request": "^7.0.2", + "cacheable-lookup": "^7.0.0", + "cacheable-request": "^10.2.1", "decompress-response": "^6.0.0", - "form-data-encoder": "^2.0.1", + "form-data-encoder": "^2.1.2", "get-stream": "^6.0.1", "http2-wrapper": "^2.1.10", "lowercase-keys": "^3.0.0", "p-cancelable": "^3.0.0", - "responselike": "^2.0.0" + "responselike": "^3.0.0" }, "engines": { "node": ">=14.16" @@ -3983,18 +3759,6 @@ "url": "https://github.com/sponsors/sindresorhus" } }, - "node_modules/hasha/node_modules/is-stream": { - "version": "2.0.1", - "resolved": "https://registry.npmjs.org/is-stream/-/is-stream-2.0.1.tgz", - "integrity": "sha512-hFoiJiTl63nn+kstHGBtewWSKnQLpyb155KHheA1l39uvtO9nWIop1p3udqPcUd/xbF1VLMO4n7OI6p7RbngDg==", - "dev": true, - "engines": { - "node": ">=8" - }, - "funding": { - "url": "https://github.com/sponsors/sindresorhus" - } - }, "node_modules/hasha/node_modules/type-fest": { "version": "0.8.1", "resolved": "https://registry.npmjs.org/type-fest/-/type-fest-0.8.1.tgz", @@ -4200,9 +3964,9 @@ } }, "node_modules/inquirer": { - "version": "9.1.0", - "resolved": "https://registry.npmjs.org/inquirer/-/inquirer-9.1.0.tgz", - "integrity": "sha512-eukdjrBljg9t55ZnvJjvGi1OyYEzVBFsO/8o5d2MV3mc28u3x4X2kS4eJ/+9U10KiREfPkEBSeCrU/S2G/uRtw==", + "version": "9.1.2", + "resolved": "https://registry.npmjs.org/inquirer/-/inquirer-9.1.2.tgz", + "integrity": "sha512-Hj2Ml1WpxKJU2npP2Rj0OURGkHV+GtNW2CwFdHDiXlqUBAUrWTcZHxCkFywX/XHzOS7wrG/kExgJFbUkVgyHzg==", "dev": true, "dependencies": { "ansi-escapes": "^5.0.0", @@ -4210,7 +3974,7 @@ "cli-cursor": "^4.0.0", "cli-width": "^4.0.0", "external-editor": "^3.0.3", - "figures": "^4.0.1", + "figures": "^5.0.0", "lodash": "^4.17.21", "mute-stream": "0.0.8", "ora": "^6.1.2", @@ -4225,6 +3989,45 @@ "node": ">=12.0.0" } }, + "node_modules/inquirer/node_modules/ansi-regex": { + "version": "6.0.1", + "resolved": "https://registry.npmjs.org/ansi-regex/-/ansi-regex-6.0.1.tgz", + "integrity": "sha512-n5M855fKb2SsfMIiFFoVrABHJC8QtHwVx+mHWP3QcEqBHYienj5dHSgjbxtC0WEZXYt4wcD6zrQElDPhFuZgfA==", + "dev": true, + "engines": { + "node": ">=12" + }, + "funding": { + "url": "https://github.com/chalk/ansi-regex?sponsor=1" + } + }, + "node_modules/inquirer/node_modules/chalk": { + "version": "5.1.2", + "resolved": "https://registry.npmjs.org/chalk/-/chalk-5.1.2.tgz", + "integrity": "sha512-E5CkT4jWURs1Vy5qGJye+XwCkNj7Od3Af7CP6SujMetSMkLs8Do2RWJK5yx1wamHV/op8Rz+9rltjaTQWDnEFQ==", + "dev": true, + "engines": { + "node": "^12.17.0 || ^14.13 || >=16.0.0" + }, + "funding": { + "url": "https://github.com/chalk/chalk?sponsor=1" + } + }, + "node_modules/inquirer/node_modules/strip-ansi": { + "version": "7.0.1", + "resolved": "https://registry.npmjs.org/strip-ansi/-/strip-ansi-7.0.1.tgz", + "integrity": "sha512-cXNxvT8dFNRVfhVME3JAe98mkXDYN2O1l7jmcwMnOslDeESg1rF/OZMtK0nRAhiari1unG5cD4jG3rapUAkLbw==", + "dev": true, + "dependencies": { + "ansi-regex": "^6.0.1" + }, + "engines": { + "node": ">=12" + }, + "funding": { + "url": "https://github.com/chalk/strip-ansi?sponsor=1" + } + }, "node_modules/internal-slot": { "version": "1.0.3", "resolved": "https://registry.npmjs.org/internal-slot/-/internal-slot-1.0.3.tgz", @@ -4317,9 +4120,9 @@ } }, "node_modules/is-callable": { - "version": "1.2.4", - "resolved": "https://registry.npmjs.org/is-callable/-/is-callable-1.2.4.tgz", - "integrity": "sha512-nsuwtxZfMX67Oryl9LCQ+upnC0Z0BgpwntpS89m1H/TLF0zNfzfLMV/9Wa/6MZsj0acpEjAO0KF1xT6ZdLl95w==", + "version": "1.2.7", + "resolved": "https://registry.npmjs.org/is-callable/-/is-callable-1.2.7.tgz", + "integrity": "sha512-1BC0BVFhS/p0qtw6enp8e+8OD0UrK0oFLztSjNzhcKA3WDuJxxAPXzPuPtKkjEY9UUoEWlX/8fgKeu2S8i9JTA==", "dev": true, "engines": { "node": ">= 0.4" @@ -4341,9 +4144,9 @@ } }, "node_modules/is-core-module": { - "version": "2.10.0", - "resolved": "https://registry.npmjs.org/is-core-module/-/is-core-module-2.10.0.tgz", - "integrity": "sha512-Erxj2n/LDAZ7H8WNJXd9tw38GYM3dv8rk8Zcs+jJuxYTW7sozH+SS8NtrSjVL1/vpLvWi1hxy96IzjJ3EHTJJg==", + "version": "2.11.0", + "resolved": "https://registry.npmjs.org/is-core-module/-/is-core-module-2.11.0.tgz", + "integrity": "sha512-RRjxlvLDkD1YJwDbroBHMb+cukurkDWNyHx7D3oNB5x9rb5ogcksMC5wHCadcXoo67gVr/+3GFySh3134zi6rw==", "dev": true, "dependencies": { "has": "^1.0.3" @@ -4580,12 +4383,12 @@ } }, "node_modules/is-stream": { - "version": "3.0.0", - "resolved": "https://registry.npmjs.org/is-stream/-/is-stream-3.0.0.tgz", - "integrity": "sha512-LnQR4bZ9IADDRSkvpqMGvt/tEJWclzklNgSw48V5EAaAeDd6qGvN8ei6k5p0tvxSR171VmGyHuTiAOfxAbr8kA==", + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/is-stream/-/is-stream-2.0.1.tgz", + "integrity": "sha512-hFoiJiTl63nn+kstHGBtewWSKnQLpyb155KHheA1l39uvtO9nWIop1p3udqPcUd/xbF1VLMO4n7OI6p7RbngDg==", "dev": true, "engines": { - "node": "^12.20.0 || ^14.13.1 || >=16.0.0" + "node": ">=8" }, "funding": { "url": "https://github.com/sponsors/sindresorhus" @@ -4628,12 +4431,12 @@ "dev": true }, "node_modules/is-unicode-supported": { - "version": "1.2.0", - "resolved": "https://registry.npmjs.org/is-unicode-supported/-/is-unicode-supported-1.2.0.tgz", - "integrity": "sha512-wH+U77omcRzevfIG8dDhTS0V9zZyweakfD01FULl97+0EHiJTTZtJqxPSkIIo/SDPv/i07k/C9jAPY+jwLLeUQ==", - "dev": true, - "engines": { - "node": ">=12" + "version": "0.1.0", + "resolved": "https://registry.npmjs.org/is-unicode-supported/-/is-unicode-supported-0.1.0.tgz", + "integrity": "sha512-knxG2q4UC3u8stRGyAVJCOdxFmv5DZiRcdlIaAQXAbSfJya+OhopNotLQrstBhququ4ZpuKbDc/8S6mgXgPFPw==", + "dev": true, + "engines": { + "node": ">=10" }, "funding": { "url": "https://github.com/sponsors/sindresorhus" @@ -4729,6 +4532,15 @@ "node": ">=8" } }, + "node_modules/istanbul-lib-instrument/node_modules/semver": { + "version": "6.3.0", + "resolved": "https://registry.npmjs.org/semver/-/semver-6.3.0.tgz", + "integrity": "sha512-b39TBaTSfV6yBrapU89p5fKekE2m/NwnDocOVruQFS1/veMgdzuPcnOM34M6CwxW8jH/lxEa5rBoDeUwu5HHTw==", + "dev": true, + "bin": { + "semver": "bin/semver.js" + } + }, "node_modules/istanbul-lib-processinfo": { "version": "2.0.3", "resolved": "https://registry.npmjs.org/istanbul-lib-processinfo/-/istanbul-lib-processinfo-2.0.3.tgz", @@ -4809,6 +4621,12 @@ "url": "https://github.com/sponsors/ljharb" } }, + "node_modules/js-sdsl": { + "version": "4.1.5", + "resolved": "https://registry.npmjs.org/js-sdsl/-/js-sdsl-4.1.5.tgz", + "integrity": "sha512-08bOAKweV2NUC1wqTtf3qZlnpOX/R2DU9ikpjOHs0H+ibQv3zpncVQg6um4uYtRtrwIX8M4Nh3ytK4HGlYAq7Q==", + "dev": true + }, "node_modules/js-tokens": { "version": "4.0.0", "resolved": "https://registry.npmjs.org/js-tokens/-/js-tokens-4.0.0.tgz", @@ -4876,9 +4694,9 @@ } }, "node_modules/jsonc-parser": { - "version": "3.1.0", - "resolved": "https://registry.npmjs.org/jsonc-parser/-/jsonc-parser-3.1.0.tgz", - "integrity": "sha512-DRf0QjnNeCUds3xTjKlQQ3DpJD51GvDjJfnxUVWg6PZTo2otSm+slzNAxU/35hF8/oJIKoG9slq30JYOsF2azg==", + "version": "3.2.0", + "resolved": "https://registry.npmjs.org/jsonc-parser/-/jsonc-parser-3.2.0.tgz", + "integrity": "sha512-gfFQZrcTc8CnKXp6Y4/CBT3fTc0OVuDofpre4aEeEpSBPV5X5v4+Vmx+8snU7RLPrNHPKSgLxGo9YuQzz20o+w==", "dev": true }, "node_modules/jsonfile": { @@ -4897,12 +4715,11 @@ "dev": true }, "node_modules/keyv": { - "version": "4.4.1", - "resolved": "https://registry.npmjs.org/keyv/-/keyv-4.4.1.tgz", - "integrity": "sha512-PzByhNxfBLnSBW2MZi1DF+W5+qB/7BMpOokewqIvqS8GFtP7xHm2oeGU72Y1fhtfOv/FiEnI4+nyViYDmUChnw==", + "version": "4.5.0", + "resolved": "https://registry.npmjs.org/keyv/-/keyv-4.5.0.tgz", + "integrity": "sha512-2YvuMsA+jnFGtBareKqgANOEKe1mk3HKiXu2fRmAfyxG0MJAywNhi5ttWA3PMjl4NmpyjZNbFifR2vNjW1znfA==", "dev": true, "dependencies": { - "compress-brotli": "^1.3.8", "json-buffer": "3.0.1" } }, @@ -4941,15 +4758,18 @@ "dev": true }, "node_modules/locate-path": { - "version": "5.0.0", - "resolved": "https://registry.npmjs.org/locate-path/-/locate-path-5.0.0.tgz", - "integrity": "sha512-t7hw9pI+WvuwNJXwk5zVHpyhIqzg2qTlklJOf0mVxGSbe3Fp2VieZcduNYjaLDoy6p9uGpQEGWG87WpMKlNq8g==", + "version": "6.0.0", + "resolved": "https://registry.npmjs.org/locate-path/-/locate-path-6.0.0.tgz", + "integrity": "sha512-iPZK6eYjbxRu3uB4/WZ3EsEIMJFMqAoopl3R+zuq0UjcAm/MO6KCweDgPfP3elTztoKP3KtnVHxTn2NHBSDVUw==", "dev": true, "dependencies": { - "p-locate": "^4.1.0" + "p-locate": "^5.0.0" }, "engines": { - "node": ">=8" + "node": ">=10" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" } }, "node_modules/lodash": { @@ -4977,16 +4797,16 @@ "dev": true }, "node_modules/log-symbols": { - "version": "5.1.0", - "resolved": "https://registry.npmjs.org/log-symbols/-/log-symbols-5.1.0.tgz", - "integrity": "sha512-l0x2DvrW294C9uDCoQe1VSU4gf529FkSZ6leBl4TiqZH/e+0R7hSfHQBNut2mNygDgHwvYHfFLn6Oxb3VWj2rA==", + "version": "4.1.0", + "resolved": "https://registry.npmjs.org/log-symbols/-/log-symbols-4.1.0.tgz", + "integrity": "sha512-8XPvpAA8uyhfteu8pIvQxpJZ7SYYdpUivZpGy6sFsBuKRY/7rQGavedeB8aK+Zkyq6upMFVL/9AW6vOYzfRyLg==", "dev": true, "dependencies": { - "chalk": "^5.0.0", - "is-unicode-supported": "^1.1.0" + "chalk": "^4.1.0", + "is-unicode-supported": "^0.1.0" }, "engines": { - "node": ">=12" + "node": ">=10" }, "funding": { "url": "https://github.com/sponsors/sindresorhus" @@ -5046,6 +4866,15 @@ "url": "https://github.com/sponsors/sindresorhus" } }, + "node_modules/make-dir/node_modules/semver": { + "version": "6.3.0", + "resolved": "https://registry.npmjs.org/semver/-/semver-6.3.0.tgz", + "integrity": "sha512-b39TBaTSfV6yBrapU89p5fKekE2m/NwnDocOVruQFS1/veMgdzuPcnOM34M6CwxW8jH/lxEa5rBoDeUwu5HHTw==", + "dev": true, + "bin": { + "semver": "bin/semver.js" + } + }, "node_modules/make-error": { "version": "1.3.6", "resolved": "https://registry.npmjs.org/make-error/-/make-error-1.3.6.tgz", @@ -5053,9 +4882,9 @@ "dev": true }, "node_modules/marked": { - "version": "4.0.19", - "resolved": "https://registry.npmjs.org/marked/-/marked-4.0.19.tgz", - "integrity": "sha512-rgQF/OxOiLcvgUAj1Q1tAf4Bgxn5h5JZTp04Fx4XUkVhs7B+7YA9JEWJhJpoO8eJt8MkZMwqLCNeNqj1bCREZQ==", + "version": "4.1.1", + "resolved": "https://registry.npmjs.org/marked/-/marked-4.1.1.tgz", + "integrity": "sha512-0cNMnTcUJPxbA6uWmCmjWz4NJRe/0Xfk2NhXCUHjew9qJzFN20krFnsUe7QynwqOwa5m1fZ4UDg0ycKFVC0ccw==", "dev": true, "bin": { "marked": "bin/marked.js" @@ -5126,12 +4955,15 @@ } }, "node_modules/mimic-response": { - "version": "1.0.1", - "resolved": "https://registry.npmjs.org/mimic-response/-/mimic-response-1.0.1.tgz", - "integrity": "sha512-j5EctnkH7amfV/q5Hgmoal1g2QHFJRraOtmx0JpIqkxhBhI/lJSl1nMpQ45hVarwNETOoWEimndZ4QK0RHxuxQ==", + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/mimic-response/-/mimic-response-4.0.0.tgz", + "integrity": "sha512-e5ISH9xMYU0DzrT+jl8q2ze9D6eWBto+I8CNpe+VI+K2J/F/k3PdkdTdz4wvGVH4NTpo+NRYTVIuMQEMMcsLqg==", "dev": true, "engines": { - "node": ">=4" + "node": "^12.20.0 || ^14.13.1 || >=16.0.0" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" } }, "node_modules/minimatch": { @@ -5147,18 +4979,20 @@ } }, "node_modules/minimist": { - "version": "1.2.6", - "resolved": "https://registry.npmjs.org/minimist/-/minimist-1.2.6.tgz", - "integrity": "sha512-Jsjnk4bw3YJqYzbdyBiNsPWHPfO++UGG749Cxs6peCu5Xg4nrena6OVxOYxrQTqww0Jmwt+Ref8rggumkTLz9Q==", - "dev": true + "version": "1.2.7", + "resolved": "https://registry.npmjs.org/minimist/-/minimist-1.2.7.tgz", + "integrity": "sha512-bzfL1YUZsP41gmu/qjrEk0Q6i2ix/cVeAhbCbqH9u3zYutS1cLg00qhrD0M2MVdCcx4Sc0UpP2eBWo9rotpq6g==", + "dev": true, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } }, "node_modules/mocha": { - "version": "10.0.0", - "resolved": "https://registry.npmjs.org/mocha/-/mocha-10.0.0.tgz", - "integrity": "sha512-0Wl+elVUD43Y0BqPZBzZt8Tnkw9CMUdNYnUsTfOM1vuhJVZL+kiesFYsqwBkEEuEixaiPe5ZQdqDgX2jddhmoA==", + "version": "10.1.0", + "resolved": "https://registry.npmjs.org/mocha/-/mocha-10.1.0.tgz", + "integrity": "sha512-vUF7IYxEoN7XhQpFLxQAEMtE4W91acW4B6En9l97MwE9stL1A9gusXfoHZCLVHDUJ/7V5+lbCM6yMqzo5vNymg==", "dev": true, "dependencies": { - "@ungap/promise-all-settled": "1.1.2", "ansi-colors": "4.1.1", "browser-stdout": "1.3.1", "chokidar": "3.5.3", @@ -5193,56 +5027,13 @@ "url": "https://opencollective.com/mochajs" } }, - "node_modules/mocha/node_modules/ansi-regex": { - "version": "5.0.1", - "resolved": "https://registry.npmjs.org/ansi-regex/-/ansi-regex-5.0.1.tgz", - "integrity": "sha512-quJQXlTSUGL2LH9SUXo8VwsY4soanhgo6LNSm84E1LBcE8s3O0wpdiRzyR9z/ZZJMlMWv37qOOb9pdJlMUEKFQ==", - "dev": true, - "engines": { - "node": ">=8" - } - }, - "node_modules/mocha/node_modules/ansi-styles": { - "version": "4.3.0", - "resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-4.3.0.tgz", - "integrity": "sha512-zbB9rCJAT1rbjiVDb2hqKFHNYLxgtk8NURxZ3IZwD3F6NtxbXZQCnnSi1Lkx+IDohdPlFp222wVALIheZJQSEg==", - "dev": true, - "dependencies": { - "color-convert": "^2.0.1" - }, - "engines": { - "node": ">=8" - }, - "funding": { - "url": "https://github.com/chalk/ansi-styles?sponsor=1" - } - }, - "node_modules/mocha/node_modules/chalk": { - "version": "4.1.2", - "resolved": "https://registry.npmjs.org/chalk/-/chalk-4.1.2.tgz", - "integrity": "sha512-oKnbhFyRIXpUuez8iBMmyEa4nbj4IOQyuhc/wy9kY7/WVPcwIO9VA668Pu8RkO7+0G76SLROeyw9CpQ061i4mA==", - "dev": true, - "dependencies": { - "ansi-styles": "^4.1.0", - "supports-color": "^7.1.0" - }, - "engines": { - "node": ">=10" - }, - "funding": { - "url": "https://github.com/chalk/chalk?sponsor=1" - } - }, - "node_modules/mocha/node_modules/chalk/node_modules/supports-color": { - "version": "7.2.0", - "resolved": "https://registry.npmjs.org/supports-color/-/supports-color-7.2.0.tgz", - "integrity": "sha512-qpCAvRl9stuOHveKsn7HncJRvv501qIacKzQlO/+Lwxc9+0q2wLyv4Dfvt80/DPn2pqOBsJdDiogXGR9+OvwRw==", + "node_modules/mocha/node_modules/brace-expansion": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/brace-expansion/-/brace-expansion-2.0.1.tgz", + "integrity": "sha512-XnAIvQ8eM+kC6aULx6wuQiwVsnzsi9d3WxzV3FpWTGA19F621kwdbsAcFKXgKUHZWsy+mY6iL1sHTxWEFCytDA==", "dev": true, "dependencies": { - "has-flag": "^4.0.0" - }, - "engines": { - "node": ">=8" + "balanced-match": "^1.0.0" } }, "node_modules/mocha/node_modules/cliui": { @@ -5262,309 +5053,137 @@ "integrity": "sha512-MSjYzcWNOA0ewAHpz0MxpYFvwg6yjy1NG3xteoqz644VCo/RPgnr1/GGt+ic3iJTzQ8Eu3TdM14SawnVUmGE6A==", "dev": true }, - "node_modules/mocha/node_modules/escape-string-regexp": { - "version": "4.0.0", - "resolved": "https://registry.npmjs.org/escape-string-regexp/-/escape-string-regexp-4.0.0.tgz", - "integrity": "sha512-TtpcNJ3XAzx3Gq8sWRzJaVajRs0uVxA2YAkdb1jm2YkPz4G6egUFAyA3n5vtEIZefPk5Wa4UXbKuS5fKkJWdgA==", + "node_modules/mocha/node_modules/minimatch": { + "version": "5.0.1", + "resolved": "https://registry.npmjs.org/minimatch/-/minimatch-5.0.1.tgz", + "integrity": "sha512-nLDxIFRyhDblz3qMuq+SoRZED4+miJ/G+tdDrjkkkRnjAsBexeGpgjLEQ0blJy7rHhR2b93rhQY4SvyWu9v03g==", "dev": true, + "dependencies": { + "brace-expansion": "^2.0.1" + }, "engines": { "node": ">=10" + } + }, + "node_modules/mocha/node_modules/ms": { + "version": "2.1.3", + "resolved": "https://registry.npmjs.org/ms/-/ms-2.1.3.tgz", + "integrity": "sha512-6FlzubTLZG3J2a/NVCAleEhjzq5oxgHyaCU9yYXvcLsvoVaHJq/s5xXI6/XXP6tz7R9xAOtHnSO/tXtF3WRTlA==", + "dev": true + }, + "node_modules/mocha/node_modules/string-width": { + "version": "4.2.3", + "resolved": "https://registry.npmjs.org/string-width/-/string-width-4.2.3.tgz", + "integrity": "sha512-wKyQRQpjJ0sIp62ErSZdGsjMJWsap5oRNihHhu6G7JVO/9jIB6UyevL+tXuOqrng8j/cxKTWyWUwvSTriiZz/g==", + "dev": true, + "dependencies": { + "emoji-regex": "^8.0.0", + "is-fullwidth-code-point": "^3.0.0", + "strip-ansi": "^6.0.1" }, - "funding": { - "url": "https://github.com/sponsors/sindresorhus" + "engines": { + "node": ">=8" } }, - "node_modules/mocha/node_modules/find-up": { - "version": "5.0.0", - "resolved": "https://registry.npmjs.org/find-up/-/find-up-5.0.0.tgz", - "integrity": "sha512-78/PXT1wlLLDgTzDs7sjq9hzz0vXD+zn+7wypEe4fXQxCmdmqfGsEPQxmiCSQI3ajFV91bVSsvNtrJRiW6nGng==", + "node_modules/mocha/node_modules/supports-color": { + "version": "8.1.1", + "resolved": "https://registry.npmjs.org/supports-color/-/supports-color-8.1.1.tgz", + "integrity": "sha512-MpUEN2OodtUzxvKQl72cUF7RQ5EiHsGvSsVG0ia9c5RbWGL2CI4C7EpPS8UTBIplnlzZiNuV56w+FuNxy3ty2Q==", "dev": true, "dependencies": { - "locate-path": "^6.0.0", - "path-exists": "^4.0.0" + "has-flag": "^4.0.0" }, "engines": { "node": ">=10" }, "funding": { - "url": "https://github.com/sponsors/sindresorhus" + "url": "https://github.com/chalk/supports-color?sponsor=1" } }, - "node_modules/mocha/node_modules/glob": { - "version": "7.2.0", - "resolved": "https://registry.npmjs.org/glob/-/glob-7.2.0.tgz", - "integrity": "sha512-lmLf6gtyrPq8tTjSmrO94wBeQbFR3HbLHbuyD69wuyQkImp2hWqMGB47OX65FBkPffO641IP9jWa1z4ivqG26Q==", + "node_modules/mocha/node_modules/wrap-ansi": { + "version": "7.0.0", + "resolved": "https://registry.npmjs.org/wrap-ansi/-/wrap-ansi-7.0.0.tgz", + "integrity": "sha512-YVGIj2kamLSTxw6NsZjoBxfSwsn0ycdesmc4p+Q21c5zPuZ1pl+NfxVdxPtdHvmNVOQ6XSYG4AUtyt/Fi7D16Q==", "dev": true, "dependencies": { - "fs.realpath": "^1.0.0", - "inflight": "^1.0.4", - "inherits": "2", - "minimatch": "^3.0.4", - "once": "^1.3.0", - "path-is-absolute": "^1.0.0" + "ansi-styles": "^4.0.0", + "string-width": "^4.1.0", + "strip-ansi": "^6.0.0" }, "engines": { - "node": "*" + "node": ">=10" }, "funding": { - "url": "https://github.com/sponsors/isaacs" + "url": "https://github.com/chalk/wrap-ansi?sponsor=1" } }, - "node_modules/mocha/node_modules/glob/node_modules/minimatch": { - "version": "3.1.2", - "resolved": "https://registry.npmjs.org/minimatch/-/minimatch-3.1.2.tgz", - "integrity": "sha512-J7p63hRiAjw1NDEww1W7i37+ByIrOWO5XQQAzZ3VOcL0PNybwpfmV/N05zFAzwQ9USyEcX6t3UO+K5aqBQOIHw==", + "node_modules/mocha/node_modules/yargs": { + "version": "16.2.0", + "resolved": "https://registry.npmjs.org/yargs/-/yargs-16.2.0.tgz", + "integrity": "sha512-D1mvvtDG0L5ft/jGWkLpG1+m0eQxOfaBvTNELraWj22wSVUMWxZUvYgJYcKh6jGGIkJFhH4IZPQhR4TKpc8mBw==", "dev": true, "dependencies": { - "brace-expansion": "^1.1.7" + "cliui": "^7.0.2", + "escalade": "^3.1.1", + "get-caller-file": "^2.0.5", + "require-directory": "^2.1.1", + "string-width": "^4.2.0", + "y18n": "^5.0.5", + "yargs-parser": "^20.2.2" }, "engines": { - "node": "*" + "node": ">=10" } }, - "node_modules/mocha/node_modules/is-unicode-supported": { - "version": "0.1.0", - "resolved": "https://registry.npmjs.org/is-unicode-supported/-/is-unicode-supported-0.1.0.tgz", - "integrity": "sha512-knxG2q4UC3u8stRGyAVJCOdxFmv5DZiRcdlIaAQXAbSfJya+OhopNotLQrstBhququ4ZpuKbDc/8S6mgXgPFPw==", + "node_modules/ms": { + "version": "2.1.2", + "resolved": "https://registry.npmjs.org/ms/-/ms-2.1.2.tgz", + "integrity": "sha512-sGkPx+VjMtmA6MX27oA4FBFELFCZZ4S4XqeGOXCv68tT+jb3vk/RyaKWP0PTKyWtmLSM0b+adUTEvbs1PEaH2w==", + "dev": true + }, + "node_modules/mute-stream": { + "version": "0.0.8", + "resolved": "https://registry.npmjs.org/mute-stream/-/mute-stream-0.0.8.tgz", + "integrity": "sha512-nnbWWOkoWyUsTjKrhgD0dcz22mdkSnpYqbEjIm2nhwhuxlSkpywJmBo8h0ZqJdkp73mb90SssHkN4rsRaBAfAA==", + "dev": true + }, + "node_modules/nanoid": { + "version": "3.3.3", + "resolved": "https://registry.npmjs.org/nanoid/-/nanoid-3.3.3.tgz", + "integrity": "sha512-p1sjXuopFs0xg+fPASzQ28agW1oHD7xDsd9Xkf3T15H3c/cifrFHVwrh74PdoklAPi+i7MdRsE47vm2r6JoB+w==", "dev": true, - "engines": { - "node": ">=10" + "bin": { + "nanoid": "bin/nanoid.cjs" }, - "funding": { - "url": "https://github.com/sponsors/sindresorhus" + "engines": { + "node": "^10 || ^12 || ^13.7 || ^14 || >=15.0.1" } }, - "node_modules/mocha/node_modules/locate-path": { - "version": "6.0.0", - "resolved": "https://registry.npmjs.org/locate-path/-/locate-path-6.0.0.tgz", - "integrity": "sha512-iPZK6eYjbxRu3uB4/WZ3EsEIMJFMqAoopl3R+zuq0UjcAm/MO6KCweDgPfP3elTztoKP3KtnVHxTn2NHBSDVUw==", + "node_modules/natural-compare": { + "version": "1.4.0", + "resolved": "https://registry.npmjs.org/natural-compare/-/natural-compare-1.4.0.tgz", + "integrity": "sha512-OWND8ei3VtNC9h7V60qff3SVobHr996CTwgxubgyQYEpg290h9J0buyECNNJexkFm5sOajh5G116RYA1c8ZMSw==", + "dev": true + }, + "node_modules/netmask": { + "version": "2.0.2", + "resolved": "https://registry.npmjs.org/netmask/-/netmask-2.0.2.tgz", + "integrity": "sha512-dBpDMdxv9Irdq66304OLfEmQ9tbNRFnFTuZiLo+bD+r332bBmMJ8GBLXklIXXgxd3+v9+KUnZaUR5PJMa75Gsg==", "dev": true, - "dependencies": { - "p-locate": "^5.0.0" - }, "engines": { - "node": ">=10" - }, - "funding": { - "url": "https://github.com/sponsors/sindresorhus" + "node": ">= 0.4.0" } }, - "node_modules/mocha/node_modules/log-symbols": { - "version": "4.1.0", - "resolved": "https://registry.npmjs.org/log-symbols/-/log-symbols-4.1.0.tgz", - "integrity": "sha512-8XPvpAA8uyhfteu8pIvQxpJZ7SYYdpUivZpGy6sFsBuKRY/7rQGavedeB8aK+Zkyq6upMFVL/9AW6vOYzfRyLg==", + "node_modules/new-github-release-url": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/new-github-release-url/-/new-github-release-url-2.0.0.tgz", + "integrity": "sha512-NHDDGYudnvRutt/VhKFlX26IotXe1w0cmkDm6JGquh5bz/bDTw0LufSmH/GxTjEdpHEO+bVKFTwdrcGa/9XlKQ==", "dev": true, "dependencies": { - "chalk": "^4.1.0", - "is-unicode-supported": "^0.1.0" + "type-fest": "^2.5.1" }, "engines": { - "node": ">=10" - }, - "funding": { - "url": "https://github.com/sponsors/sindresorhus" - } - }, - "node_modules/mocha/node_modules/minimatch": { - "version": "5.0.1", - "resolved": "https://registry.npmjs.org/minimatch/-/minimatch-5.0.1.tgz", - "integrity": "sha512-nLDxIFRyhDblz3qMuq+SoRZED4+miJ/G+tdDrjkkkRnjAsBexeGpgjLEQ0blJy7rHhR2b93rhQY4SvyWu9v03g==", - "dev": true, - "dependencies": { - "brace-expansion": "^2.0.1" - }, - "engines": { - "node": ">=10" - } - }, - "node_modules/mocha/node_modules/minimatch/node_modules/brace-expansion": { - "version": "2.0.1", - "resolved": "https://registry.npmjs.org/brace-expansion/-/brace-expansion-2.0.1.tgz", - "integrity": "sha512-XnAIvQ8eM+kC6aULx6wuQiwVsnzsi9d3WxzV3FpWTGA19F621kwdbsAcFKXgKUHZWsy+mY6iL1sHTxWEFCytDA==", - "dev": true, - "dependencies": { - "balanced-match": "^1.0.0" - } - }, - "node_modules/mocha/node_modules/ms": { - "version": "2.1.3", - "resolved": "https://registry.npmjs.org/ms/-/ms-2.1.3.tgz", - "integrity": "sha512-6FlzubTLZG3J2a/NVCAleEhjzq5oxgHyaCU9yYXvcLsvoVaHJq/s5xXI6/XXP6tz7R9xAOtHnSO/tXtF3WRTlA==", - "dev": true - }, - "node_modules/mocha/node_modules/p-limit": { - "version": "3.1.0", - "resolved": "https://registry.npmjs.org/p-limit/-/p-limit-3.1.0.tgz", - "integrity": "sha512-TYOanM3wGwNGsZN2cVTYPArw454xnXj5qmWF1bEoAc4+cU/ol7GVh7odevjp1FNHduHc3KZMcFduxU5Xc6uJRQ==", - "dev": true, - "dependencies": { - "yocto-queue": "^0.1.0" - }, - "engines": { - "node": ">=10" - }, - "funding": { - "url": "https://github.com/sponsors/sindresorhus" - } - }, - "node_modules/mocha/node_modules/p-locate": { - "version": "5.0.0", - "resolved": "https://registry.npmjs.org/p-locate/-/p-locate-5.0.0.tgz", - "integrity": "sha512-LaNjtRWUBY++zB5nE/NwcaoMylSPk+S+ZHNB1TzdbMJMny6dynpAGt7X/tl/QYq3TIeE6nxHppbo2LGymrG5Pw==", - "dev": true, - "dependencies": { - "p-limit": "^3.0.2" - }, - "engines": { - "node": ">=10" - }, - "funding": { - "url": "https://github.com/sponsors/sindresorhus" - } - }, - "node_modules/mocha/node_modules/string-width": { - "version": "4.2.3", - "resolved": "https://registry.npmjs.org/string-width/-/string-width-4.2.3.tgz", - "integrity": "sha512-wKyQRQpjJ0sIp62ErSZdGsjMJWsap5oRNihHhu6G7JVO/9jIB6UyevL+tXuOqrng8j/cxKTWyWUwvSTriiZz/g==", - "dev": true, - "dependencies": { - "emoji-regex": "^8.0.0", - "is-fullwidth-code-point": "^3.0.0", - "strip-ansi": "^6.0.1" - }, - "engines": { - "node": ">=8" - } - }, - "node_modules/mocha/node_modules/strip-ansi": { - "version": "6.0.1", - "resolved": "https://registry.npmjs.org/strip-ansi/-/strip-ansi-6.0.1.tgz", - "integrity": "sha512-Y38VPSHcqkFrCpFnQ9vuSXmquuv5oXOKpGeT6aGrr3o3Gc9AlVa6JBfUSOCnbxGGZF+/0ooI7KrPuUSztUdU5A==", - "dev": true, - "dependencies": { - "ansi-regex": "^5.0.1" - }, - "engines": { - "node": ">=8" - } - }, - "node_modules/mocha/node_modules/supports-color": { - "version": "8.1.1", - "resolved": "https://registry.npmjs.org/supports-color/-/supports-color-8.1.1.tgz", - "integrity": "sha512-MpUEN2OodtUzxvKQl72cUF7RQ5EiHsGvSsVG0ia9c5RbWGL2CI4C7EpPS8UTBIplnlzZiNuV56w+FuNxy3ty2Q==", - "dev": true, - "dependencies": { - "has-flag": "^4.0.0" - }, - "engines": { - "node": ">=10" - }, - "funding": { - "url": "https://github.com/chalk/supports-color?sponsor=1" - } - }, - "node_modules/mocha/node_modules/wrap-ansi": { - "version": "7.0.0", - "resolved": "https://registry.npmjs.org/wrap-ansi/-/wrap-ansi-7.0.0.tgz", - "integrity": "sha512-YVGIj2kamLSTxw6NsZjoBxfSwsn0ycdesmc4p+Q21c5zPuZ1pl+NfxVdxPtdHvmNVOQ6XSYG4AUtyt/Fi7D16Q==", - "dev": true, - "dependencies": { - "ansi-styles": "^4.0.0", - "string-width": "^4.1.0", - "strip-ansi": "^6.0.0" - }, - "engines": { - "node": ">=10" - }, - "funding": { - "url": "https://github.com/chalk/wrap-ansi?sponsor=1" - } - }, - "node_modules/mocha/node_modules/y18n": { - "version": "5.0.8", - "resolved": "https://registry.npmjs.org/y18n/-/y18n-5.0.8.tgz", - "integrity": "sha512-0pfFzegeDWJHJIAmTLRP2DwHjdF5s7jo9tuztdQxAhINCdvS+3nGINqPd00AphqJR/0LhANUS6/+7SCb98YOfA==", - "dev": true, - "engines": { - "node": ">=10" - } - }, - "node_modules/mocha/node_modules/yargs": { - "version": "16.2.0", - "resolved": "https://registry.npmjs.org/yargs/-/yargs-16.2.0.tgz", - "integrity": "sha512-D1mvvtDG0L5ft/jGWkLpG1+m0eQxOfaBvTNELraWj22wSVUMWxZUvYgJYcKh6jGGIkJFhH4IZPQhR4TKpc8mBw==", - "dev": true, - "dependencies": { - "cliui": "^7.0.2", - "escalade": "^3.1.1", - "get-caller-file": "^2.0.5", - "require-directory": "^2.1.1", - "string-width": "^4.2.0", - "y18n": "^5.0.5", - "yargs-parser": "^20.2.2" - }, - "engines": { - "node": ">=10" - } - }, - "node_modules/mocha/node_modules/yargs-parser": { - "version": "20.2.4", - "resolved": "https://registry.npmjs.org/yargs-parser/-/yargs-parser-20.2.4.tgz", - "integrity": "sha512-WOkpgNhPTlE73h4VFAFsOnomJVaovO8VqLDzy5saChRBFQFBoMYirowyW+Q9HB4HFF4Z7VZTiG3iSzJJA29yRA==", - "dev": true, - "engines": { - "node": ">=10" - } - }, - "node_modules/ms": { - "version": "2.1.2", - "resolved": "https://registry.npmjs.org/ms/-/ms-2.1.2.tgz", - "integrity": "sha512-sGkPx+VjMtmA6MX27oA4FBFELFCZZ4S4XqeGOXCv68tT+jb3vk/RyaKWP0PTKyWtmLSM0b+adUTEvbs1PEaH2w==", - "dev": true - }, - "node_modules/mute-stream": { - "version": "0.0.8", - "resolved": "https://registry.npmjs.org/mute-stream/-/mute-stream-0.0.8.tgz", - "integrity": "sha512-nnbWWOkoWyUsTjKrhgD0dcz22mdkSnpYqbEjIm2nhwhuxlSkpywJmBo8h0ZqJdkp73mb90SssHkN4rsRaBAfAA==", - "dev": true - }, - "node_modules/nanoid": { - "version": "3.3.3", - "resolved": "https://registry.npmjs.org/nanoid/-/nanoid-3.3.3.tgz", - "integrity": "sha512-p1sjXuopFs0xg+fPASzQ28agW1oHD7xDsd9Xkf3T15H3c/cifrFHVwrh74PdoklAPi+i7MdRsE47vm2r6JoB+w==", - "dev": true, - "bin": { - "nanoid": "bin/nanoid.cjs" - }, - "engines": { - "node": "^10 || ^12 || ^13.7 || ^14 || >=15.0.1" - } - }, - "node_modules/natural-compare": { - "version": "1.4.0", - "resolved": "https://registry.npmjs.org/natural-compare/-/natural-compare-1.4.0.tgz", - "integrity": "sha512-OWND8ei3VtNC9h7V60qff3SVobHr996CTwgxubgyQYEpg290h9J0buyECNNJexkFm5sOajh5G116RYA1c8ZMSw==", - "dev": true - }, - "node_modules/netmask": { - "version": "2.0.2", - "resolved": "https://registry.npmjs.org/netmask/-/netmask-2.0.2.tgz", - "integrity": "sha512-dBpDMdxv9Irdq66304OLfEmQ9tbNRFnFTuZiLo+bD+r332bBmMJ8GBLXklIXXgxd3+v9+KUnZaUR5PJMa75Gsg==", - "dev": true, - "engines": { - "node": ">= 0.4.0" - } - }, - "node_modules/new-github-release-url": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/new-github-release-url/-/new-github-release-url-2.0.0.tgz", - "integrity": "sha512-NHDDGYudnvRutt/VhKFlX26IotXe1w0cmkDm6JGquh5bz/bDTw0LufSmH/GxTjEdpHEO+bVKFTwdrcGa/9XlKQ==", - "dev": true, - "dependencies": { - "type-fest": "^2.5.1" - }, - "engines": { - "node": "^12.20.0 || ^14.13.1 || >=16.0.0" + "node": "^12.20.0 || ^14.13.1 || >=16.0.0" }, "funding": { "url": "https://github.com/sponsors/sindresorhus" @@ -5660,12 +5279,12 @@ } }, "node_modules/normalize-url": { - "version": "6.1.0", - "resolved": "https://registry.npmjs.org/normalize-url/-/normalize-url-6.1.0.tgz", - "integrity": "sha512-DlL+XwOy3NxAQ8xuC0okPgK46iuVNAK01YN7RueYBqqFeGsBjV9XmCAzAdgt+667bCl5kPh9EqKKDwnaPG1I7A==", + "version": "7.2.0", + "resolved": "https://registry.npmjs.org/normalize-url/-/normalize-url-7.2.0.tgz", + "integrity": "sha512-uhXOdZry0L6M2UIo9BTt7FdpBDiAGN/7oItedQwPKh8jh31ZlvC8U9Xl/EJ3aijDHaywXTW3QbZ6LuCocur1YA==", "dev": true, "engines": { - "node": ">=10" + "node": ">=12.20" }, "funding": { "url": "https://github.com/sponsors/sindresorhus" @@ -5739,6 +5358,75 @@ "node": ">=8.9" } }, + "node_modules/nyc/node_modules/cliui": { + "version": "6.0.0", + "resolved": "https://registry.npmjs.org/cliui/-/cliui-6.0.0.tgz", + "integrity": "sha512-t6wbgtoCXvAzst7QgXxJYqPt0usEfbgQdftEPbLL/cvv6HPE5VgvqCuAIDR0NgU52ds6rFwqrgakNLrHEjCbrQ==", + "dev": true, + "dependencies": { + "string-width": "^4.2.0", + "strip-ansi": "^6.0.0", + "wrap-ansi": "^6.2.0" + } + }, + "node_modules/nyc/node_modules/emoji-regex": { + "version": "8.0.0", + "resolved": "https://registry.npmjs.org/emoji-regex/-/emoji-regex-8.0.0.tgz", + "integrity": "sha512-MSjYzcWNOA0ewAHpz0MxpYFvwg6yjy1NG3xteoqz644VCo/RPgnr1/GGt+ic3iJTzQ8Eu3TdM14SawnVUmGE6A==", + "dev": true + }, + "node_modules/nyc/node_modules/find-up": { + "version": "4.1.0", + "resolved": "https://registry.npmjs.org/find-up/-/find-up-4.1.0.tgz", + "integrity": "sha512-PpOwAdQ/YlXQ2vj8a3h8IipDuYRi3wceVQQGYWxNINccq40Anw7BlsEXCMbt1Zt+OLA6Fq9suIpIWD0OsnISlw==", + "dev": true, + "dependencies": { + "locate-path": "^5.0.0", + "path-exists": "^4.0.0" + }, + "engines": { + "node": ">=8" + } + }, + "node_modules/nyc/node_modules/locate-path": { + "version": "5.0.0", + "resolved": "https://registry.npmjs.org/locate-path/-/locate-path-5.0.0.tgz", + "integrity": "sha512-t7hw9pI+WvuwNJXwk5zVHpyhIqzg2qTlklJOf0mVxGSbe3Fp2VieZcduNYjaLDoy6p9uGpQEGWG87WpMKlNq8g==", + "dev": true, + "dependencies": { + "p-locate": "^4.1.0" + }, + "engines": { + "node": ">=8" + } + }, + "node_modules/nyc/node_modules/p-limit": { + "version": "2.3.0", + "resolved": "https://registry.npmjs.org/p-limit/-/p-limit-2.3.0.tgz", + "integrity": "sha512-//88mFWSJx8lxCzwdAABTJL2MyWB12+eIY7MDL2SqLmAkeKU9qxRvWuSyTjm3FUmpBEMuFfckAIqEaVGUDxb6w==", + "dev": true, + "dependencies": { + "p-try": "^2.0.0" + }, + "engines": { + "node": ">=6" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/nyc/node_modules/p-locate": { + "version": "4.1.0", + "resolved": "https://registry.npmjs.org/p-locate/-/p-locate-4.1.0.tgz", + "integrity": "sha512-R79ZZ/0wAxKGu3oYMlz8jy/kbhsNrS7SKZ7PxEHBgJ5+F2mtFW2fK2cOtBh1cHYkQsbzFV7I+EoRKe6Yt0oK7A==", + "dev": true, + "dependencies": { + "p-limit": "^2.2.0" + }, + "engines": { + "node": ">=8" + } + }, "node_modules/nyc/node_modules/resolve-from": { "version": "5.0.0", "resolved": "https://registry.npmjs.org/resolve-from/-/resolve-from-5.0.0.tgz", @@ -5748,6 +5436,75 @@ "node": ">=8" } }, + "node_modules/nyc/node_modules/string-width": { + "version": "4.2.3", + "resolved": "https://registry.npmjs.org/string-width/-/string-width-4.2.3.tgz", + "integrity": "sha512-wKyQRQpjJ0sIp62ErSZdGsjMJWsap5oRNihHhu6G7JVO/9jIB6UyevL+tXuOqrng8j/cxKTWyWUwvSTriiZz/g==", + "dev": true, + "dependencies": { + "emoji-regex": "^8.0.0", + "is-fullwidth-code-point": "^3.0.0", + "strip-ansi": "^6.0.1" + }, + "engines": { + "node": ">=8" + } + }, + "node_modules/nyc/node_modules/wrap-ansi": { + "version": "6.2.0", + "resolved": "https://registry.npmjs.org/wrap-ansi/-/wrap-ansi-6.2.0.tgz", + "integrity": "sha512-r6lPcBGxZXlIcymEu7InxDMhdW0KDxpLgoFLcguasxCaJ/SOIZwINatK9KY/tf+ZrlywOKU0UDj3ATXUBfxJXA==", + "dev": true, + "dependencies": { + "ansi-styles": "^4.0.0", + "string-width": "^4.1.0", + "strip-ansi": "^6.0.0" + }, + "engines": { + "node": ">=8" + } + }, + "node_modules/nyc/node_modules/y18n": { + "version": "4.0.3", + "resolved": "https://registry.npmjs.org/y18n/-/y18n-4.0.3.tgz", + "integrity": "sha512-JKhqTOwSrqNA1NY5lSztJ1GrBiUodLMmIZuLiDaMRJ+itFd+ABVE8XBjOvIWL+rSqNDC74LCSFmlb/U4UZ4hJQ==", + "dev": true + }, + "node_modules/nyc/node_modules/yargs": { + "version": "15.4.1", + "resolved": "https://registry.npmjs.org/yargs/-/yargs-15.4.1.tgz", + "integrity": "sha512-aePbxDmcYW++PaqBsJ+HYUFwCdv4LVvdnhBy78E57PIor8/OVvhMrADFFEDh8DHDFRv/O9i3lPhsENjO7QX0+A==", + "dev": true, + "dependencies": { + "cliui": "^6.0.0", + "decamelize": "^1.2.0", + "find-up": "^4.1.0", + "get-caller-file": "^2.0.1", + "require-directory": "^2.1.1", + "require-main-filename": "^2.0.0", + "set-blocking": "^2.0.0", + "string-width": "^4.2.0", + "which-module": "^2.0.0", + "y18n": "^4.0.0", + "yargs-parser": "^18.1.2" + }, + "engines": { + "node": ">=8" + } + }, + "node_modules/nyc/node_modules/yargs-parser": { + "version": "18.1.3", + "resolved": "https://registry.npmjs.org/yargs-parser/-/yargs-parser-18.1.3.tgz", + "integrity": "sha512-o50j0JeToy/4K6OZcaQmW6lyXXKhq7csREXcDwk2omFPJEwUNOVtJKvmDr9EI1fAJZUyZcRF7kxGBWmRXudrCQ==", + "dev": true, + "dependencies": { + "camelcase": "^5.0.0", + "decamelize": "^1.2.0" + }, + "engines": { + "node": ">=6" + } + }, "node_modules/object-assign": { "version": "4.1.1", "resolved": "https://registry.npmjs.org/object-assign/-/object-assign-4.1.1.tgz", @@ -5874,6 +5631,73 @@ "url": "https://github.com/sponsors/sindresorhus" } }, + "node_modules/ora/node_modules/ansi-regex": { + "version": "6.0.1", + "resolved": "https://registry.npmjs.org/ansi-regex/-/ansi-regex-6.0.1.tgz", + "integrity": "sha512-n5M855fKb2SsfMIiFFoVrABHJC8QtHwVx+mHWP3QcEqBHYienj5dHSgjbxtC0WEZXYt4wcD6zrQElDPhFuZgfA==", + "dev": true, + "engines": { + "node": ">=12" + }, + "funding": { + "url": "https://github.com/chalk/ansi-regex?sponsor=1" + } + }, + "node_modules/ora/node_modules/chalk": { + "version": "5.1.2", + "resolved": "https://registry.npmjs.org/chalk/-/chalk-5.1.2.tgz", + "integrity": "sha512-E5CkT4jWURs1Vy5qGJye+XwCkNj7Od3Af7CP6SujMetSMkLs8Do2RWJK5yx1wamHV/op8Rz+9rltjaTQWDnEFQ==", + "dev": true, + "engines": { + "node": "^12.17.0 || ^14.13 || >=16.0.0" + }, + "funding": { + "url": "https://github.com/chalk/chalk?sponsor=1" + } + }, + "node_modules/ora/node_modules/is-unicode-supported": { + "version": "1.3.0", + "resolved": "https://registry.npmjs.org/is-unicode-supported/-/is-unicode-supported-1.3.0.tgz", + "integrity": "sha512-43r2mRvz+8JRIKnWJ+3j8JtjRKZ6GmjzfaE/qiBJnikNnYv/6bagRJ1kUhNk8R5EX/GkobD+r+sfxCPJsiKBLQ==", + "dev": true, + "engines": { + "node": ">=12" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/ora/node_modules/log-symbols": { + "version": "5.1.0", + "resolved": "https://registry.npmjs.org/log-symbols/-/log-symbols-5.1.0.tgz", + "integrity": "sha512-l0x2DvrW294C9uDCoQe1VSU4gf529FkSZ6leBl4TiqZH/e+0R7hSfHQBNut2mNygDgHwvYHfFLn6Oxb3VWj2rA==", + "dev": true, + "dependencies": { + "chalk": "^5.0.0", + "is-unicode-supported": "^1.1.0" + }, + "engines": { + "node": ">=12" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/ora/node_modules/strip-ansi": { + "version": "7.0.1", + "resolved": "https://registry.npmjs.org/strip-ansi/-/strip-ansi-7.0.1.tgz", + "integrity": "sha512-cXNxvT8dFNRVfhVME3JAe98mkXDYN2O1l7jmcwMnOslDeESg1rF/OZMtK0nRAhiari1unG5cD4jG3rapUAkLbw==", + "dev": true, + "dependencies": { + "ansi-regex": "^6.0.1" + }, + "engines": { + "node": ">=12" + }, + "funding": { + "url": "https://github.com/chalk/strip-ansi?sponsor=1" + } + }, "node_modules/os-name": { "version": "5.0.1", "resolved": "https://registry.npmjs.org/os-name/-/os-name-5.0.1.tgz", @@ -5909,30 +5733,33 @@ } }, "node_modules/p-limit": { - "version": "2.3.0", - "resolved": "https://registry.npmjs.org/p-limit/-/p-limit-2.3.0.tgz", - "integrity": "sha512-//88mFWSJx8lxCzwdAABTJL2MyWB12+eIY7MDL2SqLmAkeKU9qxRvWuSyTjm3FUmpBEMuFfckAIqEaVGUDxb6w==", + "version": "3.1.0", + "resolved": "https://registry.npmjs.org/p-limit/-/p-limit-3.1.0.tgz", + "integrity": "sha512-TYOanM3wGwNGsZN2cVTYPArw454xnXj5qmWF1bEoAc4+cU/ol7GVh7odevjp1FNHduHc3KZMcFduxU5Xc6uJRQ==", "dev": true, "dependencies": { - "p-try": "^2.0.0" + "yocto-queue": "^0.1.0" }, "engines": { - "node": ">=6" + "node": ">=10" }, "funding": { "url": "https://github.com/sponsors/sindresorhus" } }, "node_modules/p-locate": { - "version": "4.1.0", - "resolved": "https://registry.npmjs.org/p-locate/-/p-locate-4.1.0.tgz", - "integrity": "sha512-R79ZZ/0wAxKGu3oYMlz8jy/kbhsNrS7SKZ7PxEHBgJ5+F2mtFW2fK2cOtBh1cHYkQsbzFV7I+EoRKe6Yt0oK7A==", + "version": "5.0.0", + "resolved": "https://registry.npmjs.org/p-locate/-/p-locate-5.0.0.tgz", + "integrity": "sha512-LaNjtRWUBY++zB5nE/NwcaoMylSPk+S+ZHNB1TzdbMJMny6dynpAGt7X/tl/QYq3TIeE6nxHppbo2LGymrG5Pw==", "dev": true, "dependencies": { - "p-limit": "^2.2.0" + "p-limit": "^3.0.2" }, "engines": { - "node": ">=8" + "node": ">=10" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" } }, "node_modules/p-map": { @@ -6023,39 +5850,6 @@ "url": "https://github.com/sponsors/sindresorhus" } }, - "node_modules/package-json/node_modules/lru-cache": { - "version": "6.0.0", - "resolved": "https://registry.npmjs.org/lru-cache/-/lru-cache-6.0.0.tgz", - "integrity": "sha512-Jo6dJ04CmSjuznwJSS3pUeWmd/H0ffTlkXXgwZi+eq1UCmqQwCh+eLsYOYCwY991i2Fah4h1BEMCx4qThGbsiA==", - "dev": true, - "dependencies": { - "yallist": "^4.0.0" - }, - "engines": { - "node": ">=10" - } - }, - "node_modules/package-json/node_modules/semver": { - "version": "7.3.7", - "resolved": "https://registry.npmjs.org/semver/-/semver-7.3.7.tgz", - "integrity": "sha512-QlYTucUYOews+WeEujDoEGziz4K6c47V/Bd+LjSSYcA94p+DmINdf7ncaUinThfvZyu13lN9OY1XDxt8C0Tw0g==", - "dev": true, - "dependencies": { - "lru-cache": "^6.0.0" - }, - "bin": { - "semver": "bin/semver.js" - }, - "engines": { - "node": ">=10" - } - }, - "node_modules/package-json/node_modules/yallist": { - "version": "4.0.0", - "resolved": "https://registry.npmjs.org/yallist/-/yallist-4.0.0.tgz", - "integrity": "sha512-3wdGidZyq5PB084XLES5TpOSRA3wjXAlIWMhum2kRcv/41Sn2emQ0dycQW4uZXLejwKvg6EsvbdlVL+FYEct7A==", - "dev": true - }, "node_modules/parent-module": { "version": "1.0.1", "resolved": "https://registry.npmjs.org/parent-module/-/parent-module-1.0.1.tgz", @@ -6087,24 +5881,21 @@ } }, "node_modules/parse-path": { - "version": "5.0.0", - "resolved": "https://registry.npmjs.org/parse-path/-/parse-path-5.0.0.tgz", - "integrity": "sha512-qOpH55/+ZJ4jUu/oLO+ifUKjFPNZGfnPJtzvGzKN/4oLMil5m9OH4VpOj6++9/ytJcfks4kzH2hhi87GL/OU9A==", + "version": "7.0.0", + "resolved": "https://registry.npmjs.org/parse-path/-/parse-path-7.0.0.tgz", + "integrity": "sha512-Euf9GG8WT9CdqwuWJGdf3RkUcTBArppHABkO7Lm8IzRQp0e2r/kkFnmhu4TSK30Wcu5rVAZLmfPKSBBi9tWFog==", "dev": true, "dependencies": { "protocols": "^2.0.0" } }, "node_modules/parse-url": { - "version": "7.0.2", - "resolved": "https://registry.npmjs.org/parse-url/-/parse-url-7.0.2.tgz", - "integrity": "sha512-PqO4Z0eCiQ08Wj6QQmrmp5YTTxpYfONdOEamrtvK63AmzXpcavIVQubGHxOEwiIoDZFb8uDOoQFS0NCcjqIYQg==", + "version": "8.1.0", + "resolved": "https://registry.npmjs.org/parse-url/-/parse-url-8.1.0.tgz", + "integrity": "sha512-xDvOoLU5XRrcOZvnI6b8zA6n9O9ejNk/GExuz1yBuWUGn9KA97GI6HTs6u02wKara1CeVmZhH+0TZFdWScR89w==", "dev": true, "dependencies": { - "is-ssh": "^1.4.0", - "normalize-url": "^6.1.0", - "parse-path": "^5.0.0", - "protocols": "^2.0.1" + "parse-path": "^7.0.0" } }, "node_modules/path-exists": { @@ -6224,6 +6015,58 @@ "node": ">=8" } }, + "node_modules/pkg-dir/node_modules/find-up": { + "version": "4.1.0", + "resolved": "https://registry.npmjs.org/find-up/-/find-up-4.1.0.tgz", + "integrity": "sha512-PpOwAdQ/YlXQ2vj8a3h8IipDuYRi3wceVQQGYWxNINccq40Anw7BlsEXCMbt1Zt+OLA6Fq9suIpIWD0OsnISlw==", + "dev": true, + "dependencies": { + "locate-path": "^5.0.0", + "path-exists": "^4.0.0" + }, + "engines": { + "node": ">=8" + } + }, + "node_modules/pkg-dir/node_modules/locate-path": { + "version": "5.0.0", + "resolved": "https://registry.npmjs.org/locate-path/-/locate-path-5.0.0.tgz", + "integrity": "sha512-t7hw9pI+WvuwNJXwk5zVHpyhIqzg2qTlklJOf0mVxGSbe3Fp2VieZcduNYjaLDoy6p9uGpQEGWG87WpMKlNq8g==", + "dev": true, + "dependencies": { + "p-locate": "^4.1.0" + }, + "engines": { + "node": ">=8" + } + }, + "node_modules/pkg-dir/node_modules/p-limit": { + "version": "2.3.0", + "resolved": "https://registry.npmjs.org/p-limit/-/p-limit-2.3.0.tgz", + "integrity": "sha512-//88mFWSJx8lxCzwdAABTJL2MyWB12+eIY7MDL2SqLmAkeKU9qxRvWuSyTjm3FUmpBEMuFfckAIqEaVGUDxb6w==", + "dev": true, + "dependencies": { + "p-try": "^2.0.0" + }, + "engines": { + "node": ">=6" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/pkg-dir/node_modules/p-locate": { + "version": "4.1.0", + "resolved": "https://registry.npmjs.org/p-locate/-/p-locate-4.1.0.tgz", + "integrity": "sha512-R79ZZ/0wAxKGu3oYMlz8jy/kbhsNrS7SKZ7PxEHBgJ5+F2mtFW2fK2cOtBh1cHYkQsbzFV7I+EoRKe6Yt0oK7A==", + "dev": true, + "dependencies": { + "p-limit": "^2.2.0" + }, + "engines": { + "node": ">=8" + } + }, "node_modules/prelude-ls": { "version": "1.2.1", "resolved": "https://registry.npmjs.org/prelude-ls/-/prelude-ls-1.2.1.tgz", @@ -6302,16 +6145,6 @@ "integrity": "sha512-D+zkORCbA9f1tdWRK0RaCR3GPv50cMxcrz4X8k5LTSUD1Dkw47mKJEZQNunItRTkWwgtaUSo1RVFRIG9ZXiFYg==", "dev": true }, - "node_modules/pump": { - "version": "3.0.0", - "resolved": "https://registry.npmjs.org/pump/-/pump-3.0.0.tgz", - "integrity": "sha512-LwZy+p3SFs1Pytd/jYct4wpv49HiYCqd9Rlc5ZVdk0V+8Yzv6jR5Blk3TRmPL1ft69TxP0IMZGJ+WPFU2BFhww==", - "dev": true, - "dependencies": { - "end-of-stream": "^1.1.0", - "once": "^1.3.1" - } - }, "node_modules/punycode": { "version": "2.1.1", "resolved": "https://registry.npmjs.org/punycode/-/punycode-2.1.1.tgz", @@ -6517,22 +6350,22 @@ } }, "node_modules/release-it": { - "version": "15.3.0", - "resolved": "https://registry.npmjs.org/release-it/-/release-it-15.3.0.tgz", - "integrity": "sha512-MI4EBGca+y4SskgBkWNIakFp/GvXfpZEMWkmqmsysPcou/L+E+sKd0oy33ovGCyLic+9SI2rv/lQ3ACgonmqdQ==", + "version": "15.5.0", + "resolved": "https://registry.npmjs.org/release-it/-/release-it-15.5.0.tgz", + "integrity": "sha512-/pQo/PwEXAWRBgVGLE+3IQ3hUoeiDZMGAo/Egin1envCyLyjzrU7+0P2w4iZ1Xv5OxhC2AcaPaN5eY1ql47cBA==", "dev": true, "dependencies": { "@iarna/toml": "2.2.5", - "@octokit/rest": "19.0.3", + "@octokit/rest": "19.0.4", "async-retry": "1.3.3", "chalk": "5.0.1", "cosmiconfig": "7.0.1", "execa": "6.1.0", "form-data": "4.0.0", - "git-url-parse": "12.0.0", + "git-url-parse": "13.1.0", "globby": "13.1.2", - "got": "12.3.1", - "inquirer": "9.1.0", + "got": "12.5.1", + "inquirer": "9.1.2", "is-ci": "3.0.1", "lodash": "4.17.21", "mime-types": "2.1.35", @@ -6557,6 +6390,18 @@ "node": ">=14.9" } }, + "node_modules/release-it/node_modules/chalk": { + "version": "5.0.1", + "resolved": "https://registry.npmjs.org/chalk/-/chalk-5.0.1.tgz", + "integrity": "sha512-Fo07WOYGqMfCWHOzSXOt2CxDbC6skS/jO9ynEcmpANMoPrD+W1r1K6Vx7iNm+AQmETU1Xr2t+n8nzkV9t6xh3w==", + "dev": true, + "engines": { + "node": "^12.17.0 || ^14.13 || >=16.0.0" + }, + "funding": { + "url": "https://github.com/chalk/chalk?sponsor=1" + } + }, "node_modules/release-it/node_modules/globby": { "version": "13.1.2", "resolved": "https://registry.npmjs.org/globby/-/globby-13.1.2.tgz", @@ -6621,6 +6466,15 @@ "integrity": "sha512-3wdGidZyq5PB084XLES5TpOSRA3wjXAlIWMhum2kRcv/41Sn2emQ0dycQW4uZXLejwKvg6EsvbdlVL+FYEct7A==", "dev": true }, + "node_modules/release-it/node_modules/yargs-parser": { + "version": "21.1.1", + "resolved": "https://registry.npmjs.org/yargs-parser/-/yargs-parser-21.1.1.tgz", + "integrity": "sha512-tVpsJW7DdjecAiFpbIB1e3qxIQsE6NoPc5/eTdrbbIC4h0LVsWhnoa3g+m2HclBIujHzsxZ4VJVA+GUuc2/LBw==", + "dev": true, + "engines": { + "node": ">=12" + } + }, "node_modules/release-zalgo": { "version": "1.0.0", "resolved": "https://registry.npmjs.org/release-zalgo/-/release-zalgo-1.0.0.tgz", @@ -6681,26 +6535,20 @@ } }, "node_modules/responselike": { - "version": "2.0.1", - "resolved": "https://registry.npmjs.org/responselike/-/responselike-2.0.1.tgz", - "integrity": "sha512-4gl03wn3hj1HP3yzgdI7d3lCkF95F21Pz4BPGvKHinyQzALR5CapwC8yIi0Rh58DEMQ/SguC03wFj2k0M/mHhw==", + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/responselike/-/responselike-3.0.0.tgz", + "integrity": "sha512-40yHxbNcl2+rzXvZuVkrYohathsSJlMTXKryG5y8uciHv1+xDLHQpgjG64JUO9nrEq2jGLH6IZ8BcZyw3wrweg==", "dev": true, "dependencies": { - "lowercase-keys": "^2.0.0" + "lowercase-keys": "^3.0.0" + }, + "engines": { + "node": ">=14.16" }, "funding": { "url": "https://github.com/sponsors/sindresorhus" } }, - "node_modules/responselike/node_modules/lowercase-keys": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/lowercase-keys/-/lowercase-keys-2.0.0.tgz", - "integrity": "sha512-tqNXrS78oMOE73NMxK4EMLQsQowWf8jKooH9g7xPavRT706R6bkQJ6DY2Te7QukaZsulxa30wQ7bk0pm4XiHmA==", - "dev": true, - "engines": { - "node": ">=8" - } - }, "node_modules/restore-cursor": { "version": "4.0.0", "resolved": "https://registry.npmjs.org/restore-cursor/-/restore-cursor-4.0.0.tgz", @@ -6808,19 +6656,47 @@ } }, "node_modules/rxjs": { - "version": "7.5.6", - "resolved": "https://registry.npmjs.org/rxjs/-/rxjs-7.5.6.tgz", - "integrity": "sha512-dnyv2/YsXhnm461G+R/Pe5bWP41Nm6LBXEYWI6eiFP4fiwx6WRI/CD0zbdVAudd9xwLEF2IDcKXLHit0FYjUzw==", + "version": "7.5.7", + "resolved": "https://registry.npmjs.org/rxjs/-/rxjs-7.5.7.tgz", + "integrity": "sha512-z9MzKh/UcOqB3i20H6rtrlaE/CgjLOvheWK/9ILrbhROGTweAi1BaFsTT9FbwZi5Trr1qNRs+MXkhmR06awzQA==", "dev": true, "dependencies": { "tslib": "^2.1.0" } }, "node_modules/safe-buffer": { - "version": "5.1.2", - "resolved": "https://registry.npmjs.org/safe-buffer/-/safe-buffer-5.1.2.tgz", - "integrity": "sha512-Gd2UZBJDkXlY7GbJxfsE8/nvKkUEU1G38c1siN6QP6a9PT9MmHB8GnpscSmMJSoF8LOIrt8ud/wPtojys4G6+g==", - "dev": true + "version": "5.2.1", + "resolved": "https://registry.npmjs.org/safe-buffer/-/safe-buffer-5.2.1.tgz", + "integrity": "sha512-rp3So07KcdmmKbGvgaNxQSJr7bGVSVk5S9Eq1F+ppbRo70+YeaDxkw5Dd8NPN+GD6bjnYm2VuPuCXmpuYvmCXQ==", + "dev": true, + "funding": [ + { + "type": "github", + "url": "https://github.com/sponsors/feross" + }, + { + "type": "patreon", + "url": "https://www.patreon.com/feross" + }, + { + "type": "consulting", + "url": "https://feross.org/support" + } + ] + }, + "node_modules/safe-regex-test": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/safe-regex-test/-/safe-regex-test-1.0.0.tgz", + "integrity": "sha512-JBUUzyOgEwXQY1NuPtvcj/qcBDbDmEvWufhlnXZIm75DEHp+afM1r1ujJpJsV/gSM4t59tpDyPi1sd6ZaPFfsA==", + "dev": true, + "dependencies": { + "call-bind": "^1.0.2", + "get-intrinsic": "^1.1.3", + "is-regex": "^1.1.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } }, "node_modules/safer-buffer": { "version": "2.1.2", @@ -6829,12 +6705,18 @@ "dev": true }, "node_modules/semver": { - "version": "6.3.0", - "resolved": "https://registry.npmjs.org/semver/-/semver-6.3.0.tgz", - "integrity": "sha512-b39TBaTSfV6yBrapU89p5fKekE2m/NwnDocOVruQFS1/veMgdzuPcnOM34M6CwxW8jH/lxEa5rBoDeUwu5HHTw==", + "version": "7.3.8", + "resolved": "https://registry.npmjs.org/semver/-/semver-7.3.8.tgz", + "integrity": "sha512-NB1ctGL5rlHrPJtFDVIVzTyQylMLu9N9VICA6HSFJo8MCGVTMW6gfpicwKmmK/dAjTOrqu5l63JJOpDSrAis3A==", "dev": true, + "dependencies": { + "lru-cache": "^6.0.0" + }, "bin": { "semver": "bin/semver.js" + }, + "engines": { + "node": ">=10" } }, "node_modules/semver-diff": { @@ -6852,7 +6734,7 @@ "url": "https://github.com/sponsors/sindresorhus" } }, - "node_modules/semver-diff/node_modules/lru-cache": { + "node_modules/semver/node_modules/lru-cache": { "version": "6.0.0", "resolved": "https://registry.npmjs.org/lru-cache/-/lru-cache-6.0.0.tgz", "integrity": "sha512-Jo6dJ04CmSjuznwJSS3pUeWmd/H0ffTlkXXgwZi+eq1UCmqQwCh+eLsYOYCwY991i2Fah4h1BEMCx4qThGbsiA==", @@ -6864,22 +6746,7 @@ "node": ">=10" } }, - "node_modules/semver-diff/node_modules/semver": { - "version": "7.3.7", - "resolved": "https://registry.npmjs.org/semver/-/semver-7.3.7.tgz", - "integrity": "sha512-QlYTucUYOews+WeEujDoEGziz4K6c47V/Bd+LjSSYcA94p+DmINdf7ncaUinThfvZyu13lN9OY1XDxt8C0Tw0g==", - "dev": true, - "dependencies": { - "lru-cache": "^6.0.0" - }, - "bin": { - "semver": "bin/semver.js" - }, - "engines": { - "node": ">=10" - } - }, - "node_modules/semver-diff/node_modules/yallist": { + "node_modules/semver/node_modules/yallist": { "version": "4.0.0", "resolved": "https://registry.npmjs.org/yallist/-/yallist-4.0.0.tgz", "integrity": "sha512-3wdGidZyq5PB084XLES5TpOSRA3wjXAlIWMhum2kRcv/41Sn2emQ0dycQW4uZXLejwKvg6EsvbdlVL+FYEct7A==", @@ -6945,14 +6812,14 @@ } }, "node_modules/shiki": { - "version": "0.10.1", - "resolved": "https://registry.npmjs.org/shiki/-/shiki-0.10.1.tgz", - "integrity": "sha512-VsY7QJVzU51j5o1+DguUd+6vmCmZ5v/6gYu4vyYAhzjuNQU6P/vmSy4uQaOhvje031qQMiW0d2BwgMH52vqMng==", + "version": "0.11.1", + "resolved": "https://registry.npmjs.org/shiki/-/shiki-0.11.1.tgz", + "integrity": "sha512-EugY9VASFuDqOexOgXR18ZV+TbFrQHeCpEYaXamO+SZlsnT/2LxuLBX25GGtIrwaEVFXUAbUQ601SWE2rMwWHA==", "dev": true, "dependencies": { "jsonc-parser": "^3.0.0", "vscode-oniguruma": "^1.6.1", - "vscode-textmate": "5.2.0" + "vscode-textmate": "^6.0.0" } }, "node_modules/side-channel": { @@ -6976,9 +6843,9 @@ "dev": true }, "node_modules/sinon": { - "version": "14.0.0", - "resolved": "https://registry.npmjs.org/sinon/-/sinon-14.0.0.tgz", - "integrity": "sha512-ugA6BFmE+WrJdh0owRZHToLd32Uw3Lxq6E6LtNRU+xTVBefx632h03Q7apXWRsRdZAJ41LB8aUfn2+O4jsDNMw==", + "version": "14.0.1", + "resolved": "https://registry.npmjs.org/sinon/-/sinon-14.0.1.tgz", + "integrity": "sha512-JhJ0jCiyBWVAHDS+YSjgEbDn7Wgz9iIjA1/RK+eseJN0vAAWIWiXBdrnb92ELPyjsfreCYntD1ORtLSfIrlvSQ==", "dev": true, "dependencies": { "@sinonjs/commons": "^1.8.3", @@ -7013,9 +6880,9 @@ } }, "node_modules/socks": { - "version": "2.7.0", - "resolved": "https://registry.npmjs.org/socks/-/socks-2.7.0.tgz", - "integrity": "sha512-scnOe9y4VuiNUULJN72GrM26BNOjVsfPXI+j+98PkyEfsIXroa5ofyjT+FzGvn/xHs73U2JtoBYAVx9Hl4quSA==", + "version": "2.7.1", + "resolved": "https://registry.npmjs.org/socks/-/socks-2.7.1.tgz", + "integrity": "sha512-7maUZy1N7uo6+WVEX6psASxtNlKaNVMlGQKkG/63nEDdLOWNbiUMoLK7X4uYoLhQstau72mLgfEWcXcwsaHbYQ==", "dev": true, "dependencies": { "ip": "^2.0.0", @@ -7106,26 +6973,6 @@ "safe-buffer": "~5.2.0" } }, - "node_modules/string_decoder/node_modules/safe-buffer": { - "version": "5.2.1", - "resolved": "https://registry.npmjs.org/safe-buffer/-/safe-buffer-5.2.1.tgz", - "integrity": "sha512-rp3So07KcdmmKbGvgaNxQSJr7bGVSVk5S9Eq1F+ppbRo70+YeaDxkw5Dd8NPN+GD6bjnYm2VuPuCXmpuYvmCXQ==", - "dev": true, - "funding": [ - { - "type": "github", - "url": "https://github.com/sponsors/feross" - }, - { - "type": "patreon", - "url": "https://www.patreon.com/feross" - }, - { - "type": "consulting", - "url": "https://feross.org/support" - } - ] - }, "node_modules/string-width": { "version": "5.1.2", "resolved": "https://registry.npmjs.org/string-width/-/string-width-5.1.2.tgz", @@ -7143,6 +6990,33 @@ "url": "https://github.com/sponsors/sindresorhus" } }, + "node_modules/string-width/node_modules/ansi-regex": { + "version": "6.0.1", + "resolved": "https://registry.npmjs.org/ansi-regex/-/ansi-regex-6.0.1.tgz", + "integrity": "sha512-n5M855fKb2SsfMIiFFoVrABHJC8QtHwVx+mHWP3QcEqBHYienj5dHSgjbxtC0WEZXYt4wcD6zrQElDPhFuZgfA==", + "dev": true, + "engines": { + "node": ">=12" + }, + "funding": { + "url": "https://github.com/chalk/ansi-regex?sponsor=1" + } + }, + "node_modules/string-width/node_modules/strip-ansi": { + "version": "7.0.1", + "resolved": "https://registry.npmjs.org/strip-ansi/-/strip-ansi-7.0.1.tgz", + "integrity": "sha512-cXNxvT8dFNRVfhVME3JAe98mkXDYN2O1l7jmcwMnOslDeESg1rF/OZMtK0nRAhiari1unG5cD4jG3rapUAkLbw==", + "dev": true, + "dependencies": { + "ansi-regex": "^6.0.1" + }, + "engines": { + "node": ">=12" + }, + "funding": { + "url": "https://github.com/chalk/strip-ansi?sponsor=1" + } + }, "node_modules/string.prototype.trimend": { "version": "1.0.5", "resolved": "https://registry.npmjs.org/string.prototype.trimend/-/string.prototype.trimend-1.0.5.tgz", @@ -7172,18 +7046,15 @@ } }, "node_modules/strip-ansi": { - "version": "7.0.1", - "resolved": "https://registry.npmjs.org/strip-ansi/-/strip-ansi-7.0.1.tgz", - "integrity": "sha512-cXNxvT8dFNRVfhVME3JAe98mkXDYN2O1l7jmcwMnOslDeESg1rF/OZMtK0nRAhiari1unG5cD4jG3rapUAkLbw==", + "version": "6.0.1", + "resolved": "https://registry.npmjs.org/strip-ansi/-/strip-ansi-6.0.1.tgz", + "integrity": "sha512-Y38VPSHcqkFrCpFnQ9vuSXmquuv5oXOKpGeT6aGrr3o3Gc9AlVa6JBfUSOCnbxGGZF+/0ooI7KrPuUSztUdU5A==", "dev": true, "dependencies": { - "ansi-regex": "^6.0.1" + "ansi-regex": "^5.0.1" }, "engines": { - "node": ">=12" - }, - "funding": { - "url": "https://github.com/chalk/strip-ansi?sponsor=1" + "node": ">=8" } }, "node_modules/strip-bom": { @@ -7460,9 +7331,9 @@ } }, "node_modules/type-fest": { - "version": "1.4.0", - "resolved": "https://registry.npmjs.org/type-fest/-/type-fest-1.4.0.tgz", - "integrity": "sha512-yGSza74xk0UG8k+pLh5oeoYirvIiWo5t0/o3zHHAO2tRDiZcxWP7fywNlXhqb6/r6sWvwi+RsyQMWhVLe4BVuA==", + "version": "0.20.2", + "resolved": "https://registry.npmjs.org/type-fest/-/type-fest-0.20.2.tgz", + "integrity": "sha512-Ne+eE4r0/iWnpAxD852z3A+N0Bt5RN//NjJwRd2VFHEmrywxf5vsZlh4R6lixl6B+wz/8d+maTSAkN1FIkI3LQ==", "dev": true, "engines": { "node": ">=10" @@ -7481,15 +7352,15 @@ } }, "node_modules/typedoc": { - "version": "0.23.10", - "resolved": "https://registry.npmjs.org/typedoc/-/typedoc-0.23.10.tgz", - "integrity": "sha512-03EUiu/ZuScUBMnY6p0lY+HTH8SwhzvRE3gImoemdPDWXPXlks83UGTx++lyquWeB1MTwm9D9Ca8RIjkK3AFfQ==", + "version": "0.23.18", + "resolved": "https://registry.npmjs.org/typedoc/-/typedoc-0.23.18.tgz", + "integrity": "sha512-0Tq/uFkUuWyRYyjOShTkhsOm6u5E8wf0i6L76/k5znEaxvWKHGeT2ywZThGrDrryV/skO/REM824D1gm8ccQuA==", "dev": true, "dependencies": { "lunr": "^2.3.9", - "marked": "^4.0.18", + "marked": "^4.0.19", "minimatch": "^5.1.0", - "shiki": "^0.10.1" + "shiki": "^0.11.1" }, "bin": { "typedoc": "bin/typedoc" @@ -7498,7 +7369,7 @@ "node": ">= 14.14" }, "peerDependencies": { - "typescript": "4.6.x || 4.7.x" + "typescript": "4.6.x || 4.7.x || 4.8.x" } }, "node_modules/typedoc/node_modules/brace-expansion": { @@ -7523,9 +7394,9 @@ } }, "node_modules/typescript": { - "version": "4.7.4", - "resolved": "https://registry.npmjs.org/typescript/-/typescript-4.7.4.tgz", - "integrity": "sha512-C0WQT0gezHuw6AdY1M2jxUO83Rjf0HP7Sk1DtXj6j1EwkQNZrHAg2XPWlq62oqEhYvONq5pkC2Y9oPljWToLmQ==", + "version": "4.8.4", + "resolved": "https://registry.npmjs.org/typescript/-/typescript-4.8.4.tgz", + "integrity": "sha512-QCh+85mCy+h0IGff8r5XWzOVSbBO+KfeYrMQh7NJ58QujwcE22u+NUSmUxqF+un70P9GXKxa2HCNiTTMJknyjQ==", "dev": true, "bin": { "tsc": "bin/tsc", @@ -7590,9 +7461,9 @@ } }, "node_modules/update-browserslist-db": { - "version": "1.0.5", - "resolved": "https://registry.npmjs.org/update-browserslist-db/-/update-browserslist-db-1.0.5.tgz", - "integrity": "sha512-dteFFpCyvuDdr9S/ff1ISkKt/9YZxKjI9WlRR99c180GaztJtRa/fn18FdxGVKVsnPY7/a/FDN68mcvUmP4U7Q==", + "version": "1.0.10", + "resolved": "https://registry.npmjs.org/update-browserslist-db/-/update-browserslist-db-1.0.10.tgz", + "integrity": "sha512-OztqDenkfFkbSG+tRxBeAnCVPckDBcvibKd35yDONx6OU8N7sqgwc7rCbkJ/WcYtVRZ4ba68d6byhC21GFh7sQ==", "dev": true, "funding": [ { @@ -7637,45 +7508,24 @@ "xdg-basedir": "^5.1.0" }, "engines": { - "node": ">=14.16" - }, - "funding": { - "url": "https://github.com/yeoman/update-notifier?sponsor=1" - } - }, - "node_modules/update-notifier/node_modules/lru-cache": { - "version": "6.0.0", - "resolved": "https://registry.npmjs.org/lru-cache/-/lru-cache-6.0.0.tgz", - "integrity": "sha512-Jo6dJ04CmSjuznwJSS3pUeWmd/H0ffTlkXXgwZi+eq1UCmqQwCh+eLsYOYCwY991i2Fah4h1BEMCx4qThGbsiA==", - "dev": true, - "dependencies": { - "yallist": "^4.0.0" - }, - "engines": { - "node": ">=10" + "node": ">=14.16" + }, + "funding": { + "url": "https://github.com/yeoman/update-notifier?sponsor=1" } }, - "node_modules/update-notifier/node_modules/semver": { - "version": "7.3.7", - "resolved": "https://registry.npmjs.org/semver/-/semver-7.3.7.tgz", - "integrity": "sha512-QlYTucUYOews+WeEujDoEGziz4K6c47V/Bd+LjSSYcA94p+DmINdf7ncaUinThfvZyu13lN9OY1XDxt8C0Tw0g==", + "node_modules/update-notifier/node_modules/chalk": { + "version": "5.1.2", + "resolved": "https://registry.npmjs.org/chalk/-/chalk-5.1.2.tgz", + "integrity": "sha512-E5CkT4jWURs1Vy5qGJye+XwCkNj7Od3Af7CP6SujMetSMkLs8Do2RWJK5yx1wamHV/op8Rz+9rltjaTQWDnEFQ==", "dev": true, - "dependencies": { - "lru-cache": "^6.0.0" - }, - "bin": { - "semver": "bin/semver.js" - }, "engines": { - "node": ">=10" + "node": "^12.17.0 || ^14.13 || >=16.0.0" + }, + "funding": { + "url": "https://github.com/chalk/chalk?sponsor=1" } }, - "node_modules/update-notifier/node_modules/yallist": { - "version": "4.0.0", - "resolved": "https://registry.npmjs.org/yallist/-/yallist-4.0.0.tgz", - "integrity": "sha512-3wdGidZyq5PB084XLES5TpOSRA3wjXAlIWMhum2kRcv/41Sn2emQ0dycQW4uZXLejwKvg6EsvbdlVL+FYEct7A==", - "dev": true - }, "node_modules/uri-js": { "version": "4.4.1", "resolved": "https://registry.npmjs.org/uri-js/-/uri-js-4.4.1.tgz", @@ -7709,12 +7559,6 @@ "uuid": "dist/bin/uuid" } }, - "node_modules/v8-compile-cache": { - "version": "2.3.0", - "resolved": "https://registry.npmjs.org/v8-compile-cache/-/v8-compile-cache-2.3.0.tgz", - "integrity": "sha512-l8lCEmLcLYZh4nbunNZvQCJc5pv7+RCwa8q/LdUx8u7lsWvPDKmpodJAJNwkAhJC//dFY48KuIEmjtd4RViDrA==", - "dev": true - }, "node_modules/v8-compile-cache-lib": { "version": "3.0.1", "resolved": "https://registry.npmjs.org/v8-compile-cache-lib/-/v8-compile-cache-lib-3.0.1.tgz", @@ -7722,9 +7566,9 @@ "dev": true }, "node_modules/vm2": { - "version": "3.9.10", - "resolved": "https://registry.npmjs.org/vm2/-/vm2-3.9.10.tgz", - "integrity": "sha512-AuECTSvwu2OHLAZYhG716YzwodKCIJxB6u1zG7PgSQwIgAlEaoXH52bxdcvT8GkGjnYK7r7yWDW0m0sOsPuBjQ==", + "version": "3.9.11", + "resolved": "https://registry.npmjs.org/vm2/-/vm2-3.9.11.tgz", + "integrity": "sha512-PFG8iJRSjvvBdisowQ7iVF580DXb1uCIiGaXgm7tynMR1uTBlv7UJlB1zdv5KJ+Tmq1f0Upnj3fayoEOPpCBKg==", "dev": true, "dependencies": { "acorn": "^8.7.0", @@ -7744,9 +7588,9 @@ "dev": true }, "node_modules/vscode-textmate": { - "version": "5.2.0", - "resolved": "https://registry.npmjs.org/vscode-textmate/-/vscode-textmate-5.2.0.tgz", - "integrity": "sha512-Uw5ooOQxRASHgu6C7GVvUxisKXfSgW4oFlO+aa+PAkgmH89O3CXxEEzNRNtHSqtXFTl0nAC1uYj0GMSH27uwtQ==", + "version": "6.0.0", + "resolved": "https://registry.npmjs.org/vscode-textmate/-/vscode-textmate-6.0.0.tgz", + "integrity": "sha512-gu73tuZfJgu+mvCSy4UZwd2JXykjK9zAZsfmDeut5dx/1a7FeTk0XwJsSuqQn+cuMCGVbIBfl+s53X4T19DnzQ==", "dev": true }, "node_modules/wcwidth": { @@ -7888,18 +7732,6 @@ "node": ">=10.17.0" } }, - "node_modules/windows-release/node_modules/is-stream": { - "version": "2.0.1", - "resolved": "https://registry.npmjs.org/is-stream/-/is-stream-2.0.1.tgz", - "integrity": "sha512-hFoiJiTl63nn+kstHGBtewWSKnQLpyb155KHheA1l39uvtO9nWIop1p3udqPcUd/xbF1VLMO4n7OI6p7RbngDg==", - "dev": true, - "engines": { - "node": ">=8" - }, - "funding": { - "url": "https://github.com/sponsors/sindresorhus" - } - }, "node_modules/windows-release/node_modules/mimic-fn": { "version": "2.1.0", "resolved": "https://registry.npmjs.org/mimic-fn/-/mimic-fn-2.1.0.tgz", @@ -7977,6 +7809,45 @@ "url": "https://github.com/chalk/wrap-ansi?sponsor=1" } }, + "node_modules/wrap-ansi/node_modules/ansi-regex": { + "version": "6.0.1", + "resolved": "https://registry.npmjs.org/ansi-regex/-/ansi-regex-6.0.1.tgz", + "integrity": "sha512-n5M855fKb2SsfMIiFFoVrABHJC8QtHwVx+mHWP3QcEqBHYienj5dHSgjbxtC0WEZXYt4wcD6zrQElDPhFuZgfA==", + "dev": true, + "engines": { + "node": ">=12" + }, + "funding": { + "url": "https://github.com/chalk/ansi-regex?sponsor=1" + } + }, + "node_modules/wrap-ansi/node_modules/ansi-styles": { + "version": "6.2.1", + "resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-6.2.1.tgz", + "integrity": "sha512-bN798gFfQX+viw3R7yrGWRqnrN2oRkEkUjjl4JNn4E8GxxbjtG3FbrEIIY3l8/hrwUwIeCZvi4QuOTP4MErVug==", + "dev": true, + "engines": { + "node": ">=12" + }, + "funding": { + "url": "https://github.com/chalk/ansi-styles?sponsor=1" + } + }, + "node_modules/wrap-ansi/node_modules/strip-ansi": { + "version": "7.0.1", + "resolved": "https://registry.npmjs.org/strip-ansi/-/strip-ansi-7.0.1.tgz", + "integrity": "sha512-cXNxvT8dFNRVfhVME3JAe98mkXDYN2O1l7jmcwMnOslDeESg1rF/OZMtK0nRAhiari1unG5cD4jG3rapUAkLbw==", + "dev": true, + "dependencies": { + "ansi-regex": "^6.0.1" + }, + "engines": { + "node": ">=12" + }, + "funding": { + "url": "https://github.com/chalk/strip-ansi?sponsor=1" + } + }, "node_modules/wrappy": { "version": "1.0.2", "resolved": "https://registry.npmjs.org/wrappy/-/wrappy-1.0.2.tgz", @@ -8017,10 +7888,13 @@ } }, "node_modules/y18n": { - "version": "4.0.3", - "resolved": "https://registry.npmjs.org/y18n/-/y18n-4.0.3.tgz", - "integrity": "sha512-JKhqTOwSrqNA1NY5lSztJ1GrBiUodLMmIZuLiDaMRJ+itFd+ABVE8XBjOvIWL+rSqNDC74LCSFmlb/U4UZ4hJQ==", - "dev": true + "version": "5.0.8", + "resolved": "https://registry.npmjs.org/y18n/-/y18n-5.0.8.tgz", + "integrity": "sha512-0pfFzegeDWJHJIAmTLRP2DwHjdF5s7jo9tuztdQxAhINCdvS+3nGINqPd00AphqJR/0LhANUS6/+7SCb98YOfA==", + "dev": true, + "engines": { + "node": ">=10" + } }, "node_modules/yallist": { "version": "3.1.1", @@ -8038,34 +7912,30 @@ } }, "node_modules/yargs": { - "version": "15.4.1", - "resolved": "https://registry.npmjs.org/yargs/-/yargs-15.4.1.tgz", - "integrity": "sha512-aePbxDmcYW++PaqBsJ+HYUFwCdv4LVvdnhBy78E57PIor8/OVvhMrADFFEDh8DHDFRv/O9i3lPhsENjO7QX0+A==", + "version": "17.6.0", + "resolved": "https://registry.npmjs.org/yargs/-/yargs-17.6.0.tgz", + "integrity": "sha512-8H/wTDqlSwoSnScvV2N/JHfLWOKuh5MVla9hqLjK3nsfyy6Y4kDSYSvkU5YCUEPOSnRXfIyx3Sq+B/IWudTo4g==", "dev": true, "dependencies": { - "cliui": "^6.0.0", - "decamelize": "^1.2.0", - "find-up": "^4.1.0", - "get-caller-file": "^2.0.1", + "cliui": "^8.0.1", + "escalade": "^3.1.1", + "get-caller-file": "^2.0.5", "require-directory": "^2.1.1", - "require-main-filename": "^2.0.0", - "set-blocking": "^2.0.0", - "string-width": "^4.2.0", - "which-module": "^2.0.0", - "y18n": "^4.0.0", - "yargs-parser": "^18.1.2" + "string-width": "^4.2.3", + "y18n": "^5.0.5", + "yargs-parser": "^21.0.0" }, "engines": { - "node": ">=8" + "node": ">=12" } }, "node_modules/yargs-parser": { - "version": "21.1.1", - "resolved": "https://registry.npmjs.org/yargs-parser/-/yargs-parser-21.1.1.tgz", - "integrity": "sha512-tVpsJW7DdjecAiFpbIB1e3qxIQsE6NoPc5/eTdrbbIC4h0LVsWhnoa3g+m2HclBIujHzsxZ4VJVA+GUuc2/LBw==", + "version": "20.2.4", + "resolved": "https://registry.npmjs.org/yargs-parser/-/yargs-parser-20.2.4.tgz", + "integrity": "sha512-WOkpgNhPTlE73h4VFAFsOnomJVaovO8VqLDzy5saChRBFQFBoMYirowyW+Q9HB4HFF4Z7VZTiG3iSzJJA29yRA==", "dev": true, "engines": { - "node": ">=12" + "node": ">=10" } }, "node_modules/yargs-unparser": { @@ -8107,15 +7977,6 @@ "url": "https://github.com/sponsors/sindresorhus" } }, - "node_modules/yargs/node_modules/ansi-regex": { - "version": "5.0.1", - "resolved": "https://registry.npmjs.org/ansi-regex/-/ansi-regex-5.0.1.tgz", - "integrity": "sha512-quJQXlTSUGL2LH9SUXo8VwsY4soanhgo6LNSm84E1LBcE8s3O0wpdiRzyR9z/ZZJMlMWv37qOOb9pdJlMUEKFQ==", - "dev": true, - "engines": { - "node": ">=8" - } - }, "node_modules/yargs/node_modules/emoji-regex": { "version": "8.0.0", "resolved": "https://registry.npmjs.org/emoji-regex/-/emoji-regex-8.0.0.tgz", @@ -8136,29 +7997,13 @@ "node": ">=8" } }, - "node_modules/yargs/node_modules/strip-ansi": { - "version": "6.0.1", - "resolved": "https://registry.npmjs.org/strip-ansi/-/strip-ansi-6.0.1.tgz", - "integrity": "sha512-Y38VPSHcqkFrCpFnQ9vuSXmquuv5oXOKpGeT6aGrr3o3Gc9AlVa6JBfUSOCnbxGGZF+/0ooI7KrPuUSztUdU5A==", - "dev": true, - "dependencies": { - "ansi-regex": "^5.0.1" - }, - "engines": { - "node": ">=8" - } - }, "node_modules/yargs/node_modules/yargs-parser": { - "version": "18.1.3", - "resolved": "https://registry.npmjs.org/yargs-parser/-/yargs-parser-18.1.3.tgz", - "integrity": "sha512-o50j0JeToy/4K6OZcaQmW6lyXXKhq7csREXcDwk2omFPJEwUNOVtJKvmDr9EI1fAJZUyZcRF7kxGBWmRXudrCQ==", + "version": "21.1.1", + "resolved": "https://registry.npmjs.org/yargs-parser/-/yargs-parser-21.1.1.tgz", + "integrity": "sha512-tVpsJW7DdjecAiFpbIB1e3qxIQsE6NoPc5/eTdrbbIC4h0LVsWhnoa3g+m2HclBIujHzsxZ4VJVA+GUuc2/LBw==", "dev": true, - "dependencies": { - "camelcase": "^5.0.0", - "decamelize": "^1.2.0" - }, "engines": { - "node": ">=6" + "node": ">=12" } }, "node_modules/yn": { @@ -8189,13 +8034,13 @@ "devDependencies": { "@istanbuljs/nyc-config-typescript": "^1.0.2", "@redis/test-utils": "*", - "@types/node": "^18.7.10", + "@types/node": "^18.11.6", "nyc": "^15.1.0", - "release-it": "^15.3.0", + "release-it": "^15.5.0", "source-map-support": "^0.5.21", "ts-node": "^10.9.1", - "typedoc": "^0.23.10", - "typescript": "^4.7.4" + "typedoc": "^0.23.18", + "typescript": "^4.8.4" }, "peerDependencies": { "@redis/client": "^1.0.0" @@ -8206,26 +8051,26 @@ "version": "1.3.0", "license": "MIT", "dependencies": { - "cluster-key-slot": "1.1.0", - "generic-pool": "3.8.2", + "cluster-key-slot": "1.1.1", + "generic-pool": "3.9.0", "yallist": "4.0.0" }, "devDependencies": { "@istanbuljs/nyc-config-typescript": "^1.0.2", "@redis/test-utils": "*", - "@types/node": "^18.7.10", + "@types/node": "^18.11.6", "@types/sinon": "^10.0.13", "@types/yallist": "^4.0.1", - "@typescript-eslint/eslint-plugin": "^5.34.0", - "@typescript-eslint/parser": "^5.34.0", - "eslint": "^8.22.0", + "@typescript-eslint/eslint-plugin": "^5.41.0", + "@typescript-eslint/parser": "^5.41.0", + "eslint": "^8.26.0", "nyc": "^15.1.0", - "release-it": "^15.3.0", - "sinon": "^14.0.0", + "release-it": "^15.5.0", + "sinon": "^14.0.1", "source-map-support": "^0.5.21", "ts-node": "^10.9.1", - "typedoc": "^0.23.10", - "typescript": "^4.7.4" + "typedoc": "^0.23.18", + "typescript": "^4.8.4" }, "engines": { "node": ">=14" @@ -8233,8 +8078,7 @@ }, "packages/client/node_modules/yallist": { "version": "4.0.0", - "resolved": "https://registry.npmjs.org/yallist/-/yallist-4.0.0.tgz", - "integrity": "sha512-3wdGidZyq5PB084XLES5TpOSRA3wjXAlIWMhum2kRcv/41Sn2emQ0dycQW4uZXLejwKvg6EsvbdlVL+FYEct7A==" + "license": "ISC" }, "packages/graph": { "name": "@redis/graph", @@ -8243,13 +8087,13 @@ "devDependencies": { "@istanbuljs/nyc-config-typescript": "^1.0.2", "@redis/test-utils": "*", - "@types/node": "^18.7.10", + "@types/node": "^18.11.6", "nyc": "^15.1.0", - "release-it": "^15.3.0", + "release-it": "^15.5.0", "source-map-support": "^0.5.21", "ts-node": "^10.9.1", - "typedoc": "^0.23.10", - "typescript": "^4.7.4" + "typedoc": "^0.23.18", + "typescript": "^4.8.4" }, "peerDependencies": { "@redis/client": "^1.0.0" @@ -8262,13 +8106,13 @@ "devDependencies": { "@istanbuljs/nyc-config-typescript": "^1.0.2", "@redis/test-utils": "*", - "@types/node": "^18.7.10", + "@types/node": "^18.11.6", "nyc": "^15.1.0", - "release-it": "^15.3.0", + "release-it": "^15.5.0", "source-map-support": "^0.5.21", "ts-node": "^10.9.1", - "typedoc": "^0.23.10", - "typescript": "^4.7.4" + "typedoc": "^0.23.18", + "typescript": "^4.8.4" }, "peerDependencies": { "@redis/client": "^1.0.0" @@ -8281,13 +8125,13 @@ "devDependencies": { "@istanbuljs/nyc-config-typescript": "^1.0.2", "@redis/test-utils": "*", - "@types/node": "^18.7.10", + "@types/node": "^18.11.6", "nyc": "^15.1.0", - "release-it": "^15.3.0", + "release-it": "^15.5.0", "source-map-support": "^0.5.21", "ts-node": "^10.9.1", - "typedoc": "^0.23.10", - "typescript": "^4.7.4" + "typedoc": "^0.23.18", + "typescript": "^4.8.4" }, "peerDependencies": { "@redis/client": "^1.0.0" @@ -8297,129 +8141,18 @@ "name": "@redis/test-utils", "devDependencies": { "@istanbuljs/nyc-config-typescript": "^1.0.2", - "@types/mocha": "^9.1.1", - "@types/node": "^18.7.10", - "@types/yargs": "^17.0.11", - "mocha": "^10.0.0", + "@types/mocha": "^10.0.0", + "@types/node": "^18.11.6", + "@types/yargs": "^17.0.13", + "mocha": "^10.1.0", "nyc": "^15.1.0", "source-map-support": "^0.5.21", "ts-node": "^10.9.1", - "typescript": "^4.7.4", - "yargs": "^17.5.1" + "typescript": "^4.8.4", + "yargs": "^17.6.0" }, "peerDependencies": { - "@redis/client": "^1.0.0" - } - }, - "packages/test-utils/node_modules/ansi-regex": { - "version": "5.0.1", - "resolved": "https://registry.npmjs.org/ansi-regex/-/ansi-regex-5.0.1.tgz", - "integrity": "sha512-quJQXlTSUGL2LH9SUXo8VwsY4soanhgo6LNSm84E1LBcE8s3O0wpdiRzyR9z/ZZJMlMWv37qOOb9pdJlMUEKFQ==", - "dev": true, - "engines": { - "node": ">=8" - } - }, - "packages/test-utils/node_modules/ansi-styles": { - "version": "4.3.0", - "resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-4.3.0.tgz", - "integrity": "sha512-zbB9rCJAT1rbjiVDb2hqKFHNYLxgtk8NURxZ3IZwD3F6NtxbXZQCnnSi1Lkx+IDohdPlFp222wVALIheZJQSEg==", - "dev": true, - "dependencies": { - "color-convert": "^2.0.1" - }, - "engines": { - "node": ">=8" - }, - "funding": { - "url": "https://github.com/chalk/ansi-styles?sponsor=1" - } - }, - "packages/test-utils/node_modules/cliui": { - "version": "7.0.4", - "resolved": "https://registry.npmjs.org/cliui/-/cliui-7.0.4.tgz", - "integrity": "sha512-OcRE68cOsVMXp1Yvonl/fzkQOyjLSu/8bhPDfQt0e0/Eb283TKP20Fs2MqoPsr9SwA595rRCA+QMzYc9nBP+JQ==", - "dev": true, - "dependencies": { - "string-width": "^4.2.0", - "strip-ansi": "^6.0.0", - "wrap-ansi": "^7.0.0" - } - }, - "packages/test-utils/node_modules/emoji-regex": { - "version": "8.0.0", - "resolved": "https://registry.npmjs.org/emoji-regex/-/emoji-regex-8.0.0.tgz", - "integrity": "sha512-MSjYzcWNOA0ewAHpz0MxpYFvwg6yjy1NG3xteoqz644VCo/RPgnr1/GGt+ic3iJTzQ8Eu3TdM14SawnVUmGE6A==", - "dev": true - }, - "packages/test-utils/node_modules/string-width": { - "version": "4.2.3", - "resolved": "https://registry.npmjs.org/string-width/-/string-width-4.2.3.tgz", - "integrity": "sha512-wKyQRQpjJ0sIp62ErSZdGsjMJWsap5oRNihHhu6G7JVO/9jIB6UyevL+tXuOqrng8j/cxKTWyWUwvSTriiZz/g==", - "dev": true, - "dependencies": { - "emoji-regex": "^8.0.0", - "is-fullwidth-code-point": "^3.0.0", - "strip-ansi": "^6.0.1" - }, - "engines": { - "node": ">=8" - } - }, - "packages/test-utils/node_modules/strip-ansi": { - "version": "6.0.1", - "resolved": "https://registry.npmjs.org/strip-ansi/-/strip-ansi-6.0.1.tgz", - "integrity": "sha512-Y38VPSHcqkFrCpFnQ9vuSXmquuv5oXOKpGeT6aGrr3o3Gc9AlVa6JBfUSOCnbxGGZF+/0ooI7KrPuUSztUdU5A==", - "dev": true, - "dependencies": { - "ansi-regex": "^5.0.1" - }, - "engines": { - "node": ">=8" - } - }, - "packages/test-utils/node_modules/wrap-ansi": { - "version": "7.0.0", - "resolved": "https://registry.npmjs.org/wrap-ansi/-/wrap-ansi-7.0.0.tgz", - "integrity": "sha512-YVGIj2kamLSTxw6NsZjoBxfSwsn0ycdesmc4p+Q21c5zPuZ1pl+NfxVdxPtdHvmNVOQ6XSYG4AUtyt/Fi7D16Q==", - "dev": true, - "dependencies": { - "ansi-styles": "^4.0.0", - "string-width": "^4.1.0", - "strip-ansi": "^6.0.0" - }, - "engines": { - "node": ">=10" - }, - "funding": { - "url": "https://github.com/chalk/wrap-ansi?sponsor=1" - } - }, - "packages/test-utils/node_modules/y18n": { - "version": "5.0.8", - "resolved": "https://registry.npmjs.org/y18n/-/y18n-5.0.8.tgz", - "integrity": "sha512-0pfFzegeDWJHJIAmTLRP2DwHjdF5s7jo9tuztdQxAhINCdvS+3nGINqPd00AphqJR/0LhANUS6/+7SCb98YOfA==", - "dev": true, - "engines": { - "node": ">=10" - } - }, - "packages/test-utils/node_modules/yargs": { - "version": "17.5.1", - "resolved": "https://registry.npmjs.org/yargs/-/yargs-17.5.1.tgz", - "integrity": "sha512-t6YAJcxDkNX7NFYiVtKvWUz8l+PaKTLiL63mJYWR2GnHq2gjEWISzsLp9wg3aY36dY1j+gfIEL3pIF+XlJJfbA==", - "dev": true, - "dependencies": { - "cliui": "^7.0.2", - "escalade": "^3.1.1", - "get-caller-file": "^2.0.5", - "require-directory": "^2.1.1", - "string-width": "^4.2.3", - "y18n": "^5.0.5", - "yargs-parser": "^21.0.0" - }, - "engines": { - "node": ">=12" + "@redis/client": "^1.0.0" } }, "packages/time-series": { @@ -8429,13 +8162,13 @@ "devDependencies": { "@istanbuljs/nyc-config-typescript": "^1.0.2", "@redis/test-utils": "*", - "@types/node": "^18.7.10", + "@types/node": "^18.11.6", "nyc": "^15.1.0", - "release-it": "^15.3.0", + "release-it": "^15.5.0", "source-map-support": "^0.5.21", "ts-node": "^10.9.1", - "typedoc": "^0.23.10", - "typescript": "^4.7.4" + "typedoc": "^0.23.18", + "typescript": "^4.8.4" }, "peerDependencies": { "@redis/client": "^1.0.0" @@ -8463,41 +8196,49 @@ } }, "@babel/compat-data": { - "version": "7.18.13", - "resolved": "https://registry.npmjs.org/@babel/compat-data/-/compat-data-7.18.13.tgz", - "integrity": "sha512-5yUzC5LqyTFp2HLmDoxGQelcdYgSpP9xsnMWBphAscOdFrHSAVbLNzWiy32sVNDqJRDiJK6klfDnAgu6PAGSHw==", + "version": "7.19.4", + "resolved": "https://registry.npmjs.org/@babel/compat-data/-/compat-data-7.19.4.tgz", + "integrity": "sha512-CHIGpJcUQ5lU9KrPHTjBMhVwQG6CQjxfg36fGXl3qk/Gik1WwWachaXFuo0uCWJT/mStOKtcbFJCaVLihC1CMw==", "dev": true }, "@babel/core": { - "version": "7.18.13", - "resolved": "https://registry.npmjs.org/@babel/core/-/core-7.18.13.tgz", - "integrity": "sha512-ZisbOvRRusFktksHSG6pjj1CSvkPkcZq/KHD45LAkVP/oiHJkNBZWfpvlLmX8OtHDG8IuzsFlVRWo08w7Qxn0A==", + "version": "7.19.6", + "resolved": "https://registry.npmjs.org/@babel/core/-/core-7.19.6.tgz", + "integrity": "sha512-D2Ue4KHpc6Ys2+AxpIx1BZ8+UegLLLE2p3KJEuJRKmokHOtl49jQ5ny1773KsGLZs8MQvBidAF6yWUJxRqtKtg==", "dev": true, "requires": { "@ampproject/remapping": "^2.1.0", "@babel/code-frame": "^7.18.6", - "@babel/generator": "^7.18.13", - "@babel/helper-compilation-targets": "^7.18.9", - "@babel/helper-module-transforms": "^7.18.9", - "@babel/helpers": "^7.18.9", - "@babel/parser": "^7.18.13", + "@babel/generator": "^7.19.6", + "@babel/helper-compilation-targets": "^7.19.3", + "@babel/helper-module-transforms": "^7.19.6", + "@babel/helpers": "^7.19.4", + "@babel/parser": "^7.19.6", "@babel/template": "^7.18.10", - "@babel/traverse": "^7.18.13", - "@babel/types": "^7.18.13", + "@babel/traverse": "^7.19.6", + "@babel/types": "^7.19.4", "convert-source-map": "^1.7.0", "debug": "^4.1.0", "gensync": "^1.0.0-beta.2", "json5": "^2.2.1", "semver": "^6.3.0" + }, + "dependencies": { + "semver": { + "version": "6.3.0", + "resolved": "https://registry.npmjs.org/semver/-/semver-6.3.0.tgz", + "integrity": "sha512-b39TBaTSfV6yBrapU89p5fKekE2m/NwnDocOVruQFS1/veMgdzuPcnOM34M6CwxW8jH/lxEa5rBoDeUwu5HHTw==", + "dev": true + } } }, "@babel/generator": { - "version": "7.18.13", - "resolved": "https://registry.npmjs.org/@babel/generator/-/generator-7.18.13.tgz", - "integrity": "sha512-CkPg8ySSPuHTYPJYo7IRALdqyjM9HCbt/3uOBEFbzyGVP6Mn8bwFPB0jX6982JVNBlYzM1nnPkfjuXSOPtQeEQ==", + "version": "7.19.6", + "resolved": "https://registry.npmjs.org/@babel/generator/-/generator-7.19.6.tgz", + "integrity": "sha512-oHGRUQeoX1QrKeJIKVe0hwjGqNnVYsM5Nep5zo0uE0m42sLH+Fsd2pStJ5sRM1bNyTUUoz0pe2lTeMJrb/taTA==", "dev": true, "requires": { - "@babel/types": "^7.18.13", + "@babel/types": "^7.19.4", "@jridgewell/gen-mapping": "^0.3.2", "jsesc": "^2.5.1" }, @@ -8516,15 +8257,23 @@ } }, "@babel/helper-compilation-targets": { - "version": "7.18.9", - "resolved": "https://registry.npmjs.org/@babel/helper-compilation-targets/-/helper-compilation-targets-7.18.9.tgz", - "integrity": "sha512-tzLCyVmqUiFlcFoAPLA/gL9TeYrF61VLNtb+hvkuVaB5SUjW7jcfrglBIX1vUIoT7CLP3bBlIMeyEsIl2eFQNg==", + "version": "7.19.3", + "resolved": "https://registry.npmjs.org/@babel/helper-compilation-targets/-/helper-compilation-targets-7.19.3.tgz", + "integrity": "sha512-65ESqLGyGmLvgR0mst5AdW1FkNlj9rQsCKduzEoEPhBCDFGXvz2jW6bXFG6i0/MrV2s7hhXjjb2yAzcPuQlLwg==", "dev": true, "requires": { - "@babel/compat-data": "^7.18.8", + "@babel/compat-data": "^7.19.3", "@babel/helper-validator-option": "^7.18.6", - "browserslist": "^4.20.2", + "browserslist": "^4.21.3", "semver": "^6.3.0" + }, + "dependencies": { + "semver": { + "version": "6.3.0", + "resolved": "https://registry.npmjs.org/semver/-/semver-6.3.0.tgz", + "integrity": "sha512-b39TBaTSfV6yBrapU89p5fKekE2m/NwnDocOVruQFS1/veMgdzuPcnOM34M6CwxW8jH/lxEa5rBoDeUwu5HHTw==", + "dev": true + } } }, "@babel/helper-environment-visitor": { @@ -8534,13 +8283,13 @@ "dev": true }, "@babel/helper-function-name": { - "version": "7.18.9", - "resolved": "https://registry.npmjs.org/@babel/helper-function-name/-/helper-function-name-7.18.9.tgz", - "integrity": "sha512-fJgWlZt7nxGksJS9a0XdSaI4XvpExnNIgRP+rVefWh5U7BL8pPuir6SJUmFKRfjWQ51OtWSzwOxhaH/EBWWc0A==", + "version": "7.19.0", + "resolved": "https://registry.npmjs.org/@babel/helper-function-name/-/helper-function-name-7.19.0.tgz", + "integrity": "sha512-WAwHBINyrpqywkUH0nTnNgI5ina5TFn85HKS0pbPDfxFfhyR/aNQEn4hGi1P1JyT//I0t4OgXUlofzWILRvS5w==", "dev": true, "requires": { - "@babel/template": "^7.18.6", - "@babel/types": "^7.18.9" + "@babel/template": "^7.18.10", + "@babel/types": "^7.19.0" } }, "@babel/helper-hoist-variables": { @@ -8562,28 +8311,28 @@ } }, "@babel/helper-module-transforms": { - "version": "7.18.9", - "resolved": "https://registry.npmjs.org/@babel/helper-module-transforms/-/helper-module-transforms-7.18.9.tgz", - "integrity": "sha512-KYNqY0ICwfv19b31XzvmI/mfcylOzbLtowkw+mfvGPAQ3kfCnMLYbED3YecL5tPd8nAYFQFAd6JHp2LxZk/J1g==", + "version": "7.19.6", + "resolved": "https://registry.npmjs.org/@babel/helper-module-transforms/-/helper-module-transforms-7.19.6.tgz", + "integrity": "sha512-fCmcfQo/KYr/VXXDIyd3CBGZ6AFhPFy1TfSEJ+PilGVlQT6jcbqtHAM4C1EciRqMza7/TpOUZliuSH+U6HAhJw==", "dev": true, "requires": { "@babel/helper-environment-visitor": "^7.18.9", "@babel/helper-module-imports": "^7.18.6", - "@babel/helper-simple-access": "^7.18.6", + "@babel/helper-simple-access": "^7.19.4", "@babel/helper-split-export-declaration": "^7.18.6", - "@babel/helper-validator-identifier": "^7.18.6", - "@babel/template": "^7.18.6", - "@babel/traverse": "^7.18.9", - "@babel/types": "^7.18.9" + "@babel/helper-validator-identifier": "^7.19.1", + "@babel/template": "^7.18.10", + "@babel/traverse": "^7.19.6", + "@babel/types": "^7.19.4" } }, "@babel/helper-simple-access": { - "version": "7.18.6", - "resolved": "https://registry.npmjs.org/@babel/helper-simple-access/-/helper-simple-access-7.18.6.tgz", - "integrity": "sha512-iNpIgTgyAvDQpDj76POqg+YEt8fPxx3yaNBg3S30dxNKm2SWfYhD0TGrK/Eu9wHpUW63VQU894TsTg+GLbUa1g==", + "version": "7.19.4", + "resolved": "https://registry.npmjs.org/@babel/helper-simple-access/-/helper-simple-access-7.19.4.tgz", + "integrity": "sha512-f9Xq6WqBFqaDfbCzn2w85hwklswz5qsKlh7f08w4Y9yhJHpnNC0QemtSkK5YyOY8kPGvyiwdzZksGUhnGdaUIg==", "dev": true, "requires": { - "@babel/types": "^7.18.6" + "@babel/types": "^7.19.4" } }, "@babel/helper-split-export-declaration": { @@ -8596,15 +8345,15 @@ } }, "@babel/helper-string-parser": { - "version": "7.18.10", - "resolved": "https://registry.npmjs.org/@babel/helper-string-parser/-/helper-string-parser-7.18.10.tgz", - "integrity": "sha512-XtIfWmeNY3i4t7t4D2t02q50HvqHybPqW2ki1kosnvWCwuCMeo81Jf0gwr85jy/neUdg5XDdeFE/80DXiO+njw==", + "version": "7.19.4", + "resolved": "https://registry.npmjs.org/@babel/helper-string-parser/-/helper-string-parser-7.19.4.tgz", + "integrity": "sha512-nHtDoQcuqFmwYNYPz3Rah5ph2p8PFeFCsZk9A/48dPc/rGocJ5J3hAAZ7pb76VWX3fZKu+uEr/FhH5jLx7umrw==", "dev": true }, "@babel/helper-validator-identifier": { - "version": "7.18.6", - "resolved": "https://registry.npmjs.org/@babel/helper-validator-identifier/-/helper-validator-identifier-7.18.6.tgz", - "integrity": "sha512-MmetCkz9ej86nJQV+sFCxoGGrUbU3q02kgLciwkrt9QqEB7cP39oKEY0PakknEO0Gu20SskMRi+AYZ3b1TpN9g==", + "version": "7.19.1", + "resolved": "https://registry.npmjs.org/@babel/helper-validator-identifier/-/helper-validator-identifier-7.19.1.tgz", + "integrity": "sha512-awrNfaMtnHUr653GgGEs++LlAvW6w+DcPrOliSMXWCKo597CwL5Acf/wWdNkf/tfEQE3mjkeD1YOVZOUV/od1w==", "dev": true }, "@babel/helper-validator-option": { @@ -8614,14 +8363,14 @@ "dev": true }, "@babel/helpers": { - "version": "7.18.9", - "resolved": "https://registry.npmjs.org/@babel/helpers/-/helpers-7.18.9.tgz", - "integrity": "sha512-Jf5a+rbrLoR4eNdUmnFu8cN5eNJT6qdTdOg5IHIzq87WwyRw9PwguLFOWYgktN/60IP4fgDUawJvs7PjQIzELQ==", + "version": "7.19.4", + "resolved": "https://registry.npmjs.org/@babel/helpers/-/helpers-7.19.4.tgz", + "integrity": "sha512-G+z3aOx2nfDHwX/kyVii5fJq+bgscg89/dJNWpYeKeBv3v9xX8EIabmx1k6u9LS04H7nROFVRVK+e3k0VHp+sw==", "dev": true, "requires": { - "@babel/template": "^7.18.6", - "@babel/traverse": "^7.18.9", - "@babel/types": "^7.18.9" + "@babel/template": "^7.18.10", + "@babel/traverse": "^7.19.4", + "@babel/types": "^7.19.4" } }, "@babel/highlight": { @@ -8694,9 +8443,9 @@ } }, "@babel/parser": { - "version": "7.18.13", - "resolved": "https://registry.npmjs.org/@babel/parser/-/parser-7.18.13.tgz", - "integrity": "sha512-dgXcIfMuQ0kgzLB2b9tRZs7TTFFaGM2AbtA4fJgUUYukzGH4jwsS7hzQHEGs67jdehpm22vkgKwvbU+aEflgwg==", + "version": "7.19.6", + "resolved": "https://registry.npmjs.org/@babel/parser/-/parser-7.19.6.tgz", + "integrity": "sha512-h1IUp81s2JYJ3mRkdxJgs4UvmSsRvDrx5ICSJbPvtWYv5i1nTBGcBpnog+89rAFMwvvru6E5NUHdBe01UeSzYA==", "dev": true }, "@babel/template": { @@ -8711,19 +8460,19 @@ } }, "@babel/traverse": { - "version": "7.18.13", - "resolved": "https://registry.npmjs.org/@babel/traverse/-/traverse-7.18.13.tgz", - "integrity": "sha512-N6kt9X1jRMLPxxxPYWi7tgvJRH/rtoU+dbKAPDM44RFHiMH8igdsaSBgFeskhSl/kLWLDUvIh1RXCrTmg0/zvA==", + "version": "7.19.6", + "resolved": "https://registry.npmjs.org/@babel/traverse/-/traverse-7.19.6.tgz", + "integrity": "sha512-6l5HrUCzFM04mfbG09AagtYyR2P0B71B1wN7PfSPiksDPz2k5H9CBC1tcZpz2M8OxbKTPccByoOJ22rUKbpmQQ==", "dev": true, "requires": { "@babel/code-frame": "^7.18.6", - "@babel/generator": "^7.18.13", + "@babel/generator": "^7.19.6", "@babel/helper-environment-visitor": "^7.18.9", - "@babel/helper-function-name": "^7.18.9", + "@babel/helper-function-name": "^7.19.0", "@babel/helper-hoist-variables": "^7.18.6", "@babel/helper-split-export-declaration": "^7.18.6", - "@babel/parser": "^7.18.13", - "@babel/types": "^7.18.13", + "@babel/parser": "^7.19.6", + "@babel/types": "^7.19.4", "debug": "^4.1.0", "globals": "^11.1.0" }, @@ -8737,13 +8486,13 @@ } }, "@babel/types": { - "version": "7.18.13", - "resolved": "https://registry.npmjs.org/@babel/types/-/types-7.18.13.tgz", - "integrity": "sha512-ePqfTihzW0W6XAU+aMw2ykilisStJfDnsejDCXRchCcMJ4O0+8DhPXf2YUbZ6wjBlsEmZwLK/sPweWtu8hcJYQ==", + "version": "7.19.4", + "resolved": "https://registry.npmjs.org/@babel/types/-/types-7.19.4.tgz", + "integrity": "sha512-M5LK7nAeS6+9j7hAq+b3fQs+pNfUtTGq+yFFfHnauFA8zQtLRfmuipmsKDKKLuyG+wC8ABW43A153YNawNTEtw==", "dev": true, "requires": { - "@babel/helper-string-parser": "^7.18.10", - "@babel/helper-validator-identifier": "^7.18.6", + "@babel/helper-string-parser": "^7.19.4", + "@babel/helper-validator-identifier": "^7.19.1", "to-fast-properties": "^2.0.0" } }, @@ -8769,14 +8518,14 @@ } }, "@eslint/eslintrc": { - "version": "1.3.0", - "resolved": "https://registry.npmjs.org/@eslint/eslintrc/-/eslintrc-1.3.0.tgz", - "integrity": "sha512-UWW0TMTmk2d7hLcWD1/e2g5HDM/HQ3csaLSqXCfqwh4uNDuNqlaKWXmEsL4Cs41Z0KnILNvwbHAah3C2yt06kw==", + "version": "1.3.3", + "resolved": "https://registry.npmjs.org/@eslint/eslintrc/-/eslintrc-1.3.3.tgz", + "integrity": "sha512-uj3pT6Mg+3t39fvLrj8iuCIJ38zKO9FpGtJ4BBJebJhEwjoT+KLVNCcHT5QC9NGRIEi7fZ0ZR8YRb884auB4Lg==", "dev": true, "requires": { "ajv": "^6.12.4", "debug": "^4.3.2", - "espree": "^9.3.2", + "espree": "^9.4.0", "globals": "^13.15.0", "ignore": "^5.2.0", "import-fresh": "^3.2.1", @@ -8786,9 +8535,9 @@ } }, "@humanwhocodes/config-array": { - "version": "0.10.4", - "resolved": "https://registry.npmjs.org/@humanwhocodes/config-array/-/config-array-0.10.4.tgz", - "integrity": "sha512-mXAIHxZT3Vcpg83opl1wGlVZ9xydbfZO3r5YfRSH6Gpp2J/PfdBP0wbDa2sO6/qRbcalpoevVyW6A/fI6LfeMw==", + "version": "0.11.6", + "resolved": "https://registry.npmjs.org/@humanwhocodes/config-array/-/config-array-0.11.6.tgz", + "integrity": "sha512-jJr+hPTJYKyDILJfhNSHsjiwXYf26Flsz8DvNndOsHs5pwSnpGUEy8yzF0JYhCEvTDdV2vuOK5tt8BVhwO5/hg==", "dev": true, "requires": { "@humanwhocodes/object-schema": "^1.2.1", @@ -8796,10 +8545,10 @@ "minimatch": "^3.0.4" } }, - "@humanwhocodes/gitignore-to-minimatch": { - "version": "1.0.2", - "resolved": "https://registry.npmjs.org/@humanwhocodes/gitignore-to-minimatch/-/gitignore-to-minimatch-1.0.2.tgz", - "integrity": "sha512-rSqmMJDdLFUsyxR6FMtD00nfQKKLFb1kv+qBbOVKqErvloEIJLo5bDTJTQNTYgeyp78JsA7u/NPi5jT1GR/MuA==", + "@humanwhocodes/module-importer": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/@humanwhocodes/module-importer/-/module-importer-1.0.1.tgz", + "integrity": "sha512-bxveV4V8v5Yb4ncFTT3rPSgZBOpCkjfK0y4oVVVJwIuDVBRMDXrPyXRL988i5ap9m9bnyEEjWfm5WkBmtffLfA==", "dev": true }, "@humanwhocodes/object-schema": { @@ -8836,6 +8585,16 @@ "sprintf-js": "~1.0.2" } }, + "find-up": { + "version": "4.1.0", + "resolved": "https://registry.npmjs.org/find-up/-/find-up-4.1.0.tgz", + "integrity": "sha512-PpOwAdQ/YlXQ2vj8a3h8IipDuYRi3wceVQQGYWxNINccq40Anw7BlsEXCMbt1Zt+OLA6Fq9suIpIWD0OsnISlw==", + "dev": true, + "requires": { + "locate-path": "^5.0.0", + "path-exists": "^4.0.0" + } + }, "js-yaml": { "version": "3.14.1", "resolved": "https://registry.npmjs.org/js-yaml/-/js-yaml-3.14.1.tgz", @@ -8846,6 +8605,33 @@ "esprima": "^4.0.0" } }, + "locate-path": { + "version": "5.0.0", + "resolved": "https://registry.npmjs.org/locate-path/-/locate-path-5.0.0.tgz", + "integrity": "sha512-t7hw9pI+WvuwNJXwk5zVHpyhIqzg2qTlklJOf0mVxGSbe3Fp2VieZcduNYjaLDoy6p9uGpQEGWG87WpMKlNq8g==", + "dev": true, + "requires": { + "p-locate": "^4.1.0" + } + }, + "p-limit": { + "version": "2.3.0", + "resolved": "https://registry.npmjs.org/p-limit/-/p-limit-2.3.0.tgz", + "integrity": "sha512-//88mFWSJx8lxCzwdAABTJL2MyWB12+eIY7MDL2SqLmAkeKU9qxRvWuSyTjm3FUmpBEMuFfckAIqEaVGUDxb6w==", + "dev": true, + "requires": { + "p-try": "^2.0.0" + } + }, + "p-locate": { + "version": "4.1.0", + "resolved": "https://registry.npmjs.org/p-locate/-/p-locate-4.1.0.tgz", + "integrity": "sha512-R79ZZ/0wAxKGu3oYMlz8jy/kbhsNrS7SKZ7PxEHBgJ5+F2mtFW2fK2cOtBh1cHYkQsbzFV7I+EoRKe6Yt0oK7A==", + "dev": true, + "requires": { + "p-limit": "^2.2.0" + } + }, "resolve-from": { "version": "5.0.0", "resolved": "https://registry.npmjs.org/resolve-from/-/resolve-from-5.0.0.tgz", @@ -8898,13 +8684,13 @@ "dev": true }, "@jridgewell/trace-mapping": { - "version": "0.3.15", - "resolved": "https://registry.npmjs.org/@jridgewell/trace-mapping/-/trace-mapping-0.3.15.tgz", - "integrity": "sha512-oWZNOULl+UbhsgB51uuZzglikfIKSUBO/M9W2OfEjn7cmqoAiCgmv9lyACTUacZwBz0ITnJ2NqjU8Tx0DHL88g==", + "version": "0.3.17", + "resolved": "https://registry.npmjs.org/@jridgewell/trace-mapping/-/trace-mapping-0.3.17.tgz", + "integrity": "sha512-MCNzAp77qzKca9+W/+I0+sEpaUnZoeasnghNeVc41VZCEKaCH73Vq3BZZ/SzWIgrqE4H4ceI+p+b6C0mHf9T4g==", "dev": true, "requires": { - "@jridgewell/resolve-uri": "^3.0.3", - "@jridgewell/sourcemap-codec": "^1.4.10" + "@jridgewell/resolve-uri": "3.1.0", + "@jridgewell/sourcemap-codec": "1.4.14" } }, "@nodelib/fs.scandir": { @@ -8934,79 +8720,79 @@ } }, "@octokit/auth-token": { - "version": "3.0.1", - "resolved": "https://registry.npmjs.org/@octokit/auth-token/-/auth-token-3.0.1.tgz", - "integrity": "sha512-/USkK4cioY209wXRpund6HZzHo9GmjakpV9ycOkpMcMxMk7QVcVFVyCMtzvXYiHsB2crgDgrtNYSELYFBXhhaA==", + "version": "3.0.2", + "resolved": "https://registry.npmjs.org/@octokit/auth-token/-/auth-token-3.0.2.tgz", + "integrity": "sha512-pq7CwIMV1kmzkFTimdwjAINCXKTajZErLB4wMLYapR2nuB/Jpr66+05wOTZMSCBXP6n4DdDWT2W19Bm17vU69Q==", "dev": true, "requires": { - "@octokit/types": "^7.0.0" + "@octokit/types": "^8.0.0" } }, "@octokit/core": { - "version": "4.0.5", - "resolved": "https://registry.npmjs.org/@octokit/core/-/core-4.0.5.tgz", - "integrity": "sha512-4R3HeHTYVHCfzSAi0C6pbGXV8UDI5Rk+k3G7kLVNckswN9mvpOzW9oENfjfH3nEmzg8y3AmKmzs8Sg6pLCeOCA==", + "version": "4.1.0", + "resolved": "https://registry.npmjs.org/@octokit/core/-/core-4.1.0.tgz", + "integrity": "sha512-Czz/59VefU+kKDy+ZfDwtOIYIkFjExOKf+HA92aiTZJ6EfWpFzYQWw0l54ji8bVmyhc+mGaLUbSUmXazG7z5OQ==", "dev": true, "requires": { "@octokit/auth-token": "^3.0.0", "@octokit/graphql": "^5.0.0", "@octokit/request": "^6.0.0", "@octokit/request-error": "^3.0.0", - "@octokit/types": "^7.0.0", + "@octokit/types": "^8.0.0", "before-after-hook": "^2.2.0", "universal-user-agent": "^6.0.0" } }, "@octokit/endpoint": { - "version": "7.0.1", - "resolved": "https://registry.npmjs.org/@octokit/endpoint/-/endpoint-7.0.1.tgz", - "integrity": "sha512-/wTXAJwt0HzJ2IeE4kQXO+mBScfzyCkI0hMtkIaqyXd9zg76OpOfNQfHL9FlaxAV2RsNiOXZibVWloy8EexENg==", + "version": "7.0.3", + "resolved": "https://registry.npmjs.org/@octokit/endpoint/-/endpoint-7.0.3.tgz", + "integrity": "sha512-57gRlb28bwTsdNXq+O3JTQ7ERmBTuik9+LelgcLIVfYwf235VHbN9QNo4kXExtp/h8T423cR5iJThKtFYxC7Lw==", "dev": true, "requires": { - "@octokit/types": "^7.0.0", + "@octokit/types": "^8.0.0", "is-plain-object": "^5.0.0", "universal-user-agent": "^6.0.0" } }, "@octokit/graphql": { - "version": "5.0.1", - "resolved": "https://registry.npmjs.org/@octokit/graphql/-/graphql-5.0.1.tgz", - "integrity": "sha512-sxmnewSwAixkP1TrLdE6yRG53eEhHhDTYUykUwdV9x8f91WcbhunIHk9x1PZLALdBZKRPUO2HRcm4kezZ79HoA==", + "version": "5.0.4", + "resolved": "https://registry.npmjs.org/@octokit/graphql/-/graphql-5.0.4.tgz", + "integrity": "sha512-amO1M5QUQgYQo09aStR/XO7KAl13xpigcy/kI8/N1PnZYSS69fgte+xA4+c2DISKqUZfsh0wwjc2FaCt99L41A==", "dev": true, "requires": { "@octokit/request": "^6.0.0", - "@octokit/types": "^7.0.0", + "@octokit/types": "^8.0.0", "universal-user-agent": "^6.0.0" } }, "@octokit/openapi-types": { - "version": "13.4.0", - "resolved": "https://registry.npmjs.org/@octokit/openapi-types/-/openapi-types-13.4.0.tgz", - "integrity": "sha512-2mVzW0X1+HDO3jF80/+QFZNzJiTefELKbhMu6yaBYbp/1gSMkVDm4rT472gJljTokWUlXaaE63m7WrWENhMDLw==", + "version": "14.0.0", + "resolved": "https://registry.npmjs.org/@octokit/openapi-types/-/openapi-types-14.0.0.tgz", + "integrity": "sha512-HNWisMYlR8VCnNurDU6os2ikx0s0VyEjDYHNS/h4cgb8DeOxQ0n72HyinUtdDVxJhFy3FWLGl0DJhfEWk3P5Iw==", "dev": true }, "@octokit/plugin-paginate-rest": { - "version": "3.1.0", - "resolved": "https://registry.npmjs.org/@octokit/plugin-paginate-rest/-/plugin-paginate-rest-3.1.0.tgz", - "integrity": "sha512-+cfc40pMzWcLkoDcLb1KXqjX0jTGYXjKuQdFQDc6UAknISJHnZTiBqld6HDwRJvD4DsouDKrWXNbNV0lE/3AXA==", + "version": "4.3.1", + "resolved": "https://registry.npmjs.org/@octokit/plugin-paginate-rest/-/plugin-paginate-rest-4.3.1.tgz", + "integrity": "sha512-h8KKxESmSFTcXX409CAxlaOYscEDvN2KGQRsLCGT1NSqRW+D6EXLVQ8vuHhFznS9MuH9QYw1GfsUN30bg8hjVA==", "dev": true, "requires": { - "@octokit/types": "^6.41.0" + "@octokit/types": "^7.5.0" }, "dependencies": { "@octokit/openapi-types": { - "version": "12.11.0", - "resolved": "https://registry.npmjs.org/@octokit/openapi-types/-/openapi-types-12.11.0.tgz", - "integrity": "sha512-VsXyi8peyRq9PqIz/tpqiL2w3w80OgVMwBHltTml3LmVvXiphgeqmY9mvBw9Wu7e0QWk/fqD37ux8yP5uVekyQ==", + "version": "13.13.1", + "resolved": "https://registry.npmjs.org/@octokit/openapi-types/-/openapi-types-13.13.1.tgz", + "integrity": "sha512-4EuKSk3N95UBWFau3Bz9b3pheQ8jQYbKmBL5+GSuY8YDPDwu03J4BjI+66yNi8aaX/3h1qDpb0mbBkLdr+cfGQ==", "dev": true }, "@octokit/types": { - "version": "6.41.0", - "resolved": "https://registry.npmjs.org/@octokit/types/-/types-6.41.0.tgz", - "integrity": "sha512-eJ2jbzjdijiL3B4PrSQaSjuF2sPEQPVCPzBvTHJD9Nz+9dw2SGH4K4xeQJ77YfTq5bRQ+bD8wT11JbeDPmxmGg==", + "version": "7.5.1", + "resolved": "https://registry.npmjs.org/@octokit/types/-/types-7.5.1.tgz", + "integrity": "sha512-Zk4OUMLCSpXNI8KZZn47lVLJSsgMyCimsWWQI5hyjZg7hdYm0kjotaIkbG0Pp8SfU2CofMBzonboTqvzn3FrJA==", "dev": true, "requires": { - "@octokit/openapi-types": "^12.11.0" + "@octokit/openapi-types": "^13.11.0" } } } @@ -9019,24 +8805,24 @@ "requires": {} }, "@octokit/plugin-rest-endpoint-methods": { - "version": "6.3.0", - "resolved": "https://registry.npmjs.org/@octokit/plugin-rest-endpoint-methods/-/plugin-rest-endpoint-methods-6.3.0.tgz", - "integrity": "sha512-qEu2wn6E7hqluZwIEUnDxWROvKjov3zMIAi4H4d7cmKWNMeBprEXZzJe8pE5eStUYC1ysGhD0B7L6IeG1Rfb+g==", + "version": "6.7.0", + "resolved": "https://registry.npmjs.org/@octokit/plugin-rest-endpoint-methods/-/plugin-rest-endpoint-methods-6.7.0.tgz", + "integrity": "sha512-orxQ0fAHA7IpYhG2flD2AygztPlGYNAdlzYz8yrD8NDgelPfOYoRPROfEyIe035PlxvbYrgkfUZIhSBKju/Cvw==", "dev": true, "requires": { - "@octokit/types": "^7.0.0", + "@octokit/types": "^8.0.0", "deprecation": "^2.3.1" } }, "@octokit/request": { - "version": "6.2.1", - "resolved": "https://registry.npmjs.org/@octokit/request/-/request-6.2.1.tgz", - "integrity": "sha512-gYKRCia3cpajRzDSU+3pt1q2OcuC6PK8PmFIyxZDWCzRXRSIBH8jXjFJ8ZceoygBIm0KsEUg4x1+XcYBz7dHPQ==", + "version": "6.2.2", + "resolved": "https://registry.npmjs.org/@octokit/request/-/request-6.2.2.tgz", + "integrity": "sha512-6VDqgj0HMc2FUX2awIs+sM6OwLgwHvAi4KCK3mT2H2IKRt6oH9d0fej5LluF5mck1lRR/rFWN0YIDSYXYSylbw==", "dev": true, "requires": { "@octokit/endpoint": "^7.0.0", "@octokit/request-error": "^3.0.0", - "@octokit/types": "^7.0.0", + "@octokit/types": "^8.0.0", "is-plain-object": "^5.0.0", "node-fetch": "^2.6.7", "universal-user-agent": "^6.0.0" @@ -9054,35 +8840,35 @@ } }, "@octokit/request-error": { - "version": "3.0.1", - "resolved": "https://registry.npmjs.org/@octokit/request-error/-/request-error-3.0.1.tgz", - "integrity": "sha512-ym4Bp0HTP7F3VFssV88WD1ZyCIRoE8H35pXSKwLeMizcdZAYc/t6N9X9Yr9n6t3aG9IH75XDnZ6UeZph0vHMWQ==", + "version": "3.0.2", + "resolved": "https://registry.npmjs.org/@octokit/request-error/-/request-error-3.0.2.tgz", + "integrity": "sha512-WMNOFYrSaX8zXWoJg9u/pKgWPo94JXilMLb2VManNOby9EZxrQaBe/QSC4a1TzpAlpxofg2X/jMnCyZgL6y7eg==", "dev": true, "requires": { - "@octokit/types": "^7.0.0", + "@octokit/types": "^8.0.0", "deprecation": "^2.0.0", "once": "^1.4.0" } }, "@octokit/rest": { - "version": "19.0.3", - "resolved": "https://registry.npmjs.org/@octokit/rest/-/rest-19.0.3.tgz", - "integrity": "sha512-5arkTsnnRT7/sbI4fqgSJ35KiFaN7zQm0uQiQtivNQLI8RQx8EHwJCajcTUwmaCMNDg7tdCvqAnc7uvHHPxrtQ==", + "version": "19.0.4", + "resolved": "https://registry.npmjs.org/@octokit/rest/-/rest-19.0.4.tgz", + "integrity": "sha512-LwG668+6lE8zlSYOfwPj4FxWdv/qFXYBpv79TWIQEpBLKA9D/IMcWsF/U9RGpA3YqMVDiTxpgVpEW3zTFfPFTA==", "dev": true, "requires": { "@octokit/core": "^4.0.0", - "@octokit/plugin-paginate-rest": "^3.0.0", + "@octokit/plugin-paginate-rest": "^4.0.0", "@octokit/plugin-request-log": "^1.0.4", "@octokit/plugin-rest-endpoint-methods": "^6.0.0" } }, "@octokit/types": { - "version": "7.1.1", - "resolved": "https://registry.npmjs.org/@octokit/types/-/types-7.1.1.tgz", - "integrity": "sha512-Dx6cNTORyVaKY0Yeb9MbHksk79L8GXsihbG6PtWqTpkyA2TY1qBWE26EQXVG3dHwY9Femdd/WEeRUEiD0+H3TQ==", + "version": "8.0.0", + "resolved": "https://registry.npmjs.org/@octokit/types/-/types-8.0.0.tgz", + "integrity": "sha512-65/TPpOJP1i3K4lBJMnWqPUJ6zuOtzhtagDvydAWbEXpbFYA0oMKKyLb95NFZZP0lSh/4b6K+DQlzvYQJQQePg==", "dev": true, "requires": { - "@octokit/openapi-types": "^13.4.0" + "@octokit/openapi-types": "^14.0.0" } }, "@pnpm/network.ca-file": { @@ -9109,13 +8895,13 @@ "requires": { "@istanbuljs/nyc-config-typescript": "^1.0.2", "@redis/test-utils": "*", - "@types/node": "^18.7.10", + "@types/node": "^18.11.6", "nyc": "^15.1.0", - "release-it": "^15.3.0", + "release-it": "^15.5.0", "source-map-support": "^0.5.21", "ts-node": "^10.9.1", - "typedoc": "^0.23.10", - "typescript": "^4.7.4" + "typedoc": "^0.23.18", + "typescript": "^4.8.4" } }, "@redis/client": { @@ -9123,28 +8909,26 @@ "requires": { "@istanbuljs/nyc-config-typescript": "^1.0.2", "@redis/test-utils": "*", - "@types/node": "^18.7.10", + "@types/node": "^18.11.6", "@types/sinon": "^10.0.13", "@types/yallist": "^4.0.1", - "@typescript-eslint/eslint-plugin": "^5.34.0", - "@typescript-eslint/parser": "^5.34.0", - "cluster-key-slot": "1.1.0", - "eslint": "^8.22.0", - "generic-pool": "3.8.2", + "@typescript-eslint/eslint-plugin": "^5.41.0", + "@typescript-eslint/parser": "^5.41.0", + "cluster-key-slot": "1.1.1", + "eslint": "^8.26.0", + "generic-pool": "3.9.0", "nyc": "^15.1.0", - "release-it": "^15.3.0", - "sinon": "^14.0.0", + "release-it": "^15.5.0", + "sinon": "^14.0.1", "source-map-support": "^0.5.21", "ts-node": "^10.9.1", - "typedoc": "^0.23.10", - "typescript": "^4.7.4", + "typedoc": "^0.23.18", + "typescript": "^4.8.4", "yallist": "4.0.0" }, "dependencies": { "yallist": { - "version": "4.0.0", - "resolved": "https://registry.npmjs.org/yallist/-/yallist-4.0.0.tgz", - "integrity": "sha512-3wdGidZyq5PB084XLES5TpOSRA3wjXAlIWMhum2kRcv/41Sn2emQ0dycQW4uZXLejwKvg6EsvbdlVL+FYEct7A==" + "version": "4.0.0" } } }, @@ -9153,13 +8937,13 @@ "requires": { "@istanbuljs/nyc-config-typescript": "^1.0.2", "@redis/test-utils": "*", - "@types/node": "^18.7.10", + "@types/node": "^18.11.6", "nyc": "^15.1.0", - "release-it": "^15.3.0", + "release-it": "^15.5.0", "source-map-support": "^0.5.21", "ts-node": "^10.9.1", - "typedoc": "^0.23.10", - "typescript": "^4.7.4" + "typedoc": "^0.23.18", + "typescript": "^4.8.4" } }, "@redis/json": { @@ -9167,13 +8951,13 @@ "requires": { "@istanbuljs/nyc-config-typescript": "^1.0.2", "@redis/test-utils": "*", - "@types/node": "^18.7.10", + "@types/node": "^18.11.6", "nyc": "^15.1.0", - "release-it": "^15.3.0", + "release-it": "^15.5.0", "source-map-support": "^0.5.21", "ts-node": "^10.9.1", - "typedoc": "^0.23.10", - "typescript": "^4.7.4" + "typedoc": "^0.23.18", + "typescript": "^4.8.4" } }, "@redis/search": { @@ -9181,114 +8965,28 @@ "requires": { "@istanbuljs/nyc-config-typescript": "^1.0.2", "@redis/test-utils": "*", - "@types/node": "^18.7.10", + "@types/node": "^18.11.6", "nyc": "^15.1.0", - "release-it": "^15.3.0", + "release-it": "^15.5.0", "source-map-support": "^0.5.21", "ts-node": "^10.9.1", - "typedoc": "^0.23.10", - "typescript": "^4.7.4" + "typedoc": "^0.23.18", + "typescript": "^4.8.4" } }, "@redis/test-utils": { "version": "file:packages/test-utils", "requires": { "@istanbuljs/nyc-config-typescript": "^1.0.2", - "@types/mocha": "^9.1.1", - "@types/node": "^18.7.10", - "@types/yargs": "^17.0.11", - "mocha": "^10.0.0", + "@types/mocha": "^10.0.0", + "@types/node": "^18.11.6", + "@types/yargs": "^17.0.13", + "mocha": "^10.1.0", "nyc": "^15.1.0", "source-map-support": "^0.5.21", "ts-node": "^10.9.1", - "typescript": "^4.7.4", - "yargs": "^17.5.1" - }, - "dependencies": { - "ansi-regex": { - "version": "5.0.1", - "resolved": "https://registry.npmjs.org/ansi-regex/-/ansi-regex-5.0.1.tgz", - "integrity": "sha512-quJQXlTSUGL2LH9SUXo8VwsY4soanhgo6LNSm84E1LBcE8s3O0wpdiRzyR9z/ZZJMlMWv37qOOb9pdJlMUEKFQ==", - "dev": true - }, - "ansi-styles": { - "version": "4.3.0", - "resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-4.3.0.tgz", - "integrity": "sha512-zbB9rCJAT1rbjiVDb2hqKFHNYLxgtk8NURxZ3IZwD3F6NtxbXZQCnnSi1Lkx+IDohdPlFp222wVALIheZJQSEg==", - "dev": true, - "requires": { - "color-convert": "^2.0.1" - } - }, - "cliui": { - "version": "7.0.4", - "resolved": "https://registry.npmjs.org/cliui/-/cliui-7.0.4.tgz", - "integrity": "sha512-OcRE68cOsVMXp1Yvonl/fzkQOyjLSu/8bhPDfQt0e0/Eb283TKP20Fs2MqoPsr9SwA595rRCA+QMzYc9nBP+JQ==", - "dev": true, - "requires": { - "string-width": "^4.2.0", - "strip-ansi": "^6.0.0", - "wrap-ansi": "^7.0.0" - } - }, - "emoji-regex": { - "version": "8.0.0", - "resolved": "https://registry.npmjs.org/emoji-regex/-/emoji-regex-8.0.0.tgz", - "integrity": "sha512-MSjYzcWNOA0ewAHpz0MxpYFvwg6yjy1NG3xteoqz644VCo/RPgnr1/GGt+ic3iJTzQ8Eu3TdM14SawnVUmGE6A==", - "dev": true - }, - "string-width": { - "version": "4.2.3", - "resolved": "https://registry.npmjs.org/string-width/-/string-width-4.2.3.tgz", - "integrity": "sha512-wKyQRQpjJ0sIp62ErSZdGsjMJWsap5oRNihHhu6G7JVO/9jIB6UyevL+tXuOqrng8j/cxKTWyWUwvSTriiZz/g==", - "dev": true, - "requires": { - "emoji-regex": "^8.0.0", - "is-fullwidth-code-point": "^3.0.0", - "strip-ansi": "^6.0.1" - } - }, - "strip-ansi": { - "version": "6.0.1", - "resolved": "https://registry.npmjs.org/strip-ansi/-/strip-ansi-6.0.1.tgz", - "integrity": "sha512-Y38VPSHcqkFrCpFnQ9vuSXmquuv5oXOKpGeT6aGrr3o3Gc9AlVa6JBfUSOCnbxGGZF+/0ooI7KrPuUSztUdU5A==", - "dev": true, - "requires": { - "ansi-regex": "^5.0.1" - } - }, - "wrap-ansi": { - "version": "7.0.0", - "resolved": "https://registry.npmjs.org/wrap-ansi/-/wrap-ansi-7.0.0.tgz", - "integrity": "sha512-YVGIj2kamLSTxw6NsZjoBxfSwsn0ycdesmc4p+Q21c5zPuZ1pl+NfxVdxPtdHvmNVOQ6XSYG4AUtyt/Fi7D16Q==", - "dev": true, - "requires": { - "ansi-styles": "^4.0.0", - "string-width": "^4.1.0", - "strip-ansi": "^6.0.0" - } - }, - "y18n": { - "version": "5.0.8", - "resolved": "https://registry.npmjs.org/y18n/-/y18n-5.0.8.tgz", - "integrity": "sha512-0pfFzegeDWJHJIAmTLRP2DwHjdF5s7jo9tuztdQxAhINCdvS+3nGINqPd00AphqJR/0LhANUS6/+7SCb98YOfA==", - "dev": true - }, - "yargs": { - "version": "17.5.1", - "resolved": "https://registry.npmjs.org/yargs/-/yargs-17.5.1.tgz", - "integrity": "sha512-t6YAJcxDkNX7NFYiVtKvWUz8l+PaKTLiL63mJYWR2GnHq2gjEWISzsLp9wg3aY36dY1j+gfIEL3pIF+XlJJfbA==", - "dev": true, - "requires": { - "cliui": "^7.0.2", - "escalade": "^3.1.1", - "get-caller-file": "^2.0.5", - "require-directory": "^2.1.1", - "string-width": "^4.2.3", - "y18n": "^5.0.5", - "yargs-parser": "^21.0.0" - } - } + "typescript": "^4.8.4", + "yargs": "^17.6.0" } }, "@redis/time-series": { @@ -9296,13 +8994,13 @@ "requires": { "@istanbuljs/nyc-config-typescript": "^1.0.2", "@redis/test-utils": "*", - "@types/node": "^18.7.10", + "@types/node": "^18.11.6", "nyc": "^15.1.0", - "release-it": "^15.3.0", + "release-it": "^15.5.0", "source-map-support": "^0.5.21", "ts-node": "^10.9.1", - "typedoc": "^0.23.10", - "typescript": "^4.7.4" + "typedoc": "^0.23.18", + "typescript": "^4.8.4" } }, "@sindresorhus/is": { @@ -9385,55 +9083,28 @@ "integrity": "sha512-yOlFc+7UtL/89t2ZhjPvvB/DeAr3r+Dq58IgzsFkOAvVC6NMJXmCGjbptdXdR9qsX7pKcTL+s87FtYREi2dEEQ==", "dev": true }, - "@types/cacheable-request": { - "version": "6.0.2", - "resolved": "https://registry.npmjs.org/@types/cacheable-request/-/cacheable-request-6.0.2.tgz", - "integrity": "sha512-B3xVo+dlKM6nnKTcmm5ZtY/OL8bOAOd2Olee9M1zft65ox50OzjEHW91sDiU9j6cvW8Ejg1/Qkf4xd2kugApUA==", - "dev": true, - "requires": { - "@types/http-cache-semantics": "*", - "@types/keyv": "*", - "@types/node": "*", - "@types/responselike": "*" - } - }, "@types/http-cache-semantics": { "version": "4.0.1", "resolved": "https://registry.npmjs.org/@types/http-cache-semantics/-/http-cache-semantics-4.0.1.tgz", "integrity": "sha512-SZs7ekbP8CN0txVG2xVRH6EgKmEm31BOxA07vkFaETzZz1xh+cbt8BcI0slpymvwhx5dlFnQG2rTlPVQn+iRPQ==", "dev": true }, - "@types/json-buffer": { - "version": "3.0.0", - "resolved": "https://registry.npmjs.org/@types/json-buffer/-/json-buffer-3.0.0.tgz", - "integrity": "sha512-3YP80IxxFJB4b5tYC2SUPwkg0XQLiu0nWvhRgEatgjf+29IcWO9X1k8xRv5DGssJ/lCrjYTjQPcobJr2yWIVuQ==", - "dev": true - }, "@types/json-schema": { "version": "7.0.11", "resolved": "https://registry.npmjs.org/@types/json-schema/-/json-schema-7.0.11.tgz", "integrity": "sha512-wOuvG1SN4Us4rez+tylwwwCV1psiNVOkJeM3AUWUNWg/jDQY2+HE/444y5gc+jBmRqASOm2Oeh5c1axHobwRKQ==", "dev": true }, - "@types/keyv": { - "version": "3.1.4", - "resolved": "https://registry.npmjs.org/@types/keyv/-/keyv-3.1.4.tgz", - "integrity": "sha512-BQ5aZNSCpj7D6K2ksrRCTmKRLEpnPvWDiLPfoGyhZ++8YtiK9d/3DBKPJgry359X/P1PfruyYwvnvwFjuEiEIg==", - "dev": true, - "requires": { - "@types/node": "*" - } - }, "@types/mocha": { - "version": "9.1.1", - "resolved": "https://registry.npmjs.org/@types/mocha/-/mocha-9.1.1.tgz", - "integrity": "sha512-Z61JK7DKDtdKTWwLeElSEBcWGRLY8g95ic5FoQqI9CMx0ns/Ghep3B4DfcEimiKMvtamNVULVNKEsiwV3aQmXw==", + "version": "10.0.0", + "resolved": "https://registry.npmjs.org/@types/mocha/-/mocha-10.0.0.tgz", + "integrity": "sha512-rADY+HtTOA52l9VZWtgQfn4p+UDVM2eDVkMZT1I6syp0YKxW2F9v+0pbRZLsvskhQv/vMb6ZfCay81GHbz5SHg==", "dev": true }, "@types/node": { - "version": "18.7.10", - "resolved": "https://registry.npmjs.org/@types/node/-/node-18.7.10.tgz", - "integrity": "sha512-SST7B//wF7xlGoLo1IEVB0cQ4e7BgXlDk5IaPgb5s0DlYLjb4if07h8+0zbQIvahfPNXs6e7zyT0EH1MqaS+5g==", + "version": "18.11.6", + "resolved": "https://registry.npmjs.org/@types/node/-/node-18.11.6.tgz", + "integrity": "sha512-j3CEDa2vd96K0AXF8Wur7UucACvnjkk8hYyQAHhUNciabZLDl9nfAEVUSwmh245OOZV15bRA3Y590Gi5jUcDJg==", "dev": true }, "@types/parse-json": { @@ -9442,14 +9113,11 @@ "integrity": "sha512-//oorEZjL6sbPcKUaCdIGlIUeH26mgzimjBB77G6XRgnDl/L5wOnpyBGRe/Mmf5CVW3PwEBE1NjiMZ/ssFh4wA==", "dev": true }, - "@types/responselike": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/@types/responselike/-/responselike-1.0.0.tgz", - "integrity": "sha512-85Y2BjiufFzaMIlvJDvTTB8Fxl2xfLo4HgmHzVBz08w4wDePCTjYw66PdrolO0kzli3yam/YCgRufyo1DdQVTA==", - "dev": true, - "requires": { - "@types/node": "*" - } + "@types/semver": { + "version": "7.3.12", + "resolved": "https://registry.npmjs.org/@types/semver/-/semver-7.3.12.tgz", + "integrity": "sha512-WwA1MW0++RfXmCr12xeYOOC5baSC9mSb0ZqCquFzKhcoF4TvHu5MKOuXsncgZcpVFhB1pXd5hZmM0ryAoCp12A==", + "dev": true }, "@types/sinon": { "version": "10.0.13", @@ -9473,9 +9141,9 @@ "dev": true }, "@types/yargs": { - "version": "17.0.11", - "resolved": "https://registry.npmjs.org/@types/yargs/-/yargs-17.0.11.tgz", - "integrity": "sha512-aB4y9UDUXTSMxmM4MH+YnuR0g5Cph3FLQBoWoMB21DSvFVAxRVEHEMx3TLh+zUZYMCQtKiqazz0Q4Rre31f/OA==", + "version": "17.0.13", + "resolved": "https://registry.npmjs.org/@types/yargs/-/yargs-17.0.13.tgz", + "integrity": "sha512-9sWaruZk2JGxIQU+IhI1fhPYRcQ0UuTNuKuCW9bR5fp7qi2Llf7WDzNa17Cy7TKnh3cdxDOiyTu6gaLS0eDatg==", "dev": true, "requires": { "@types/yargs-parser": "*" @@ -9488,182 +9156,106 @@ "dev": true }, "@typescript-eslint/eslint-plugin": { - "version": "5.34.0", - "resolved": "https://registry.npmjs.org/@typescript-eslint/eslint-plugin/-/eslint-plugin-5.34.0.tgz", - "integrity": "sha512-eRfPPcasO39iwjlUAMtjeueRGuIrW3TQ9WseIDl7i5UWuFbf83yYaU7YPs4j8+4CxUMIsj1k+4kV+E+G+6ypDQ==", + "version": "5.41.0", + "resolved": "https://registry.npmjs.org/@typescript-eslint/eslint-plugin/-/eslint-plugin-5.41.0.tgz", + "integrity": "sha512-DXUS22Y57/LAFSg3x7Vi6RNAuLpTXwxB9S2nIA7msBb/Zt8p7XqMwdpdc1IU7CkOQUPgAqR5fWvxuKCbneKGmA==", "dev": true, "requires": { - "@typescript-eslint/scope-manager": "5.34.0", - "@typescript-eslint/type-utils": "5.34.0", - "@typescript-eslint/utils": "5.34.0", + "@typescript-eslint/scope-manager": "5.41.0", + "@typescript-eslint/type-utils": "5.41.0", + "@typescript-eslint/utils": "5.41.0", "debug": "^4.3.4", - "functional-red-black-tree": "^1.0.1", "ignore": "^5.2.0", "regexpp": "^3.2.0", "semver": "^7.3.7", "tsutils": "^3.21.0" - }, - "dependencies": { - "lru-cache": { - "version": "6.0.0", - "resolved": "https://registry.npmjs.org/lru-cache/-/lru-cache-6.0.0.tgz", - "integrity": "sha512-Jo6dJ04CmSjuznwJSS3pUeWmd/H0ffTlkXXgwZi+eq1UCmqQwCh+eLsYOYCwY991i2Fah4h1BEMCx4qThGbsiA==", - "dev": true, - "requires": { - "yallist": "^4.0.0" - } - }, - "semver": { - "version": "7.3.7", - "resolved": "https://registry.npmjs.org/semver/-/semver-7.3.7.tgz", - "integrity": "sha512-QlYTucUYOews+WeEujDoEGziz4K6c47V/Bd+LjSSYcA94p+DmINdf7ncaUinThfvZyu13lN9OY1XDxt8C0Tw0g==", - "dev": true, - "requires": { - "lru-cache": "^6.0.0" - } - }, - "yallist": { - "version": "4.0.0", - "resolved": "https://registry.npmjs.org/yallist/-/yallist-4.0.0.tgz", - "integrity": "sha512-3wdGidZyq5PB084XLES5TpOSRA3wjXAlIWMhum2kRcv/41Sn2emQ0dycQW4uZXLejwKvg6EsvbdlVL+FYEct7A==", - "dev": true - } } }, "@typescript-eslint/parser": { - "version": "5.34.0", - "resolved": "https://registry.npmjs.org/@typescript-eslint/parser/-/parser-5.34.0.tgz", - "integrity": "sha512-SZ3NEnK4usd2CXkoV3jPa/vo1mWX1fqRyIVUQZR4As1vyp4fneknBNJj+OFtV8WAVgGf+rOHMSqQbs2Qn3nFZQ==", + "version": "5.41.0", + "resolved": "https://registry.npmjs.org/@typescript-eslint/parser/-/parser-5.41.0.tgz", + "integrity": "sha512-HQVfix4+RL5YRWZboMD1pUfFN8MpRH4laziWkkAzyO1fvNOY/uinZcvo3QiFJVS/siNHupV8E5+xSwQZrl6PZA==", "dev": true, "requires": { - "@typescript-eslint/scope-manager": "5.34.0", - "@typescript-eslint/types": "5.34.0", - "@typescript-eslint/typescript-estree": "5.34.0", + "@typescript-eslint/scope-manager": "5.41.0", + "@typescript-eslint/types": "5.41.0", + "@typescript-eslint/typescript-estree": "5.41.0", "debug": "^4.3.4" } }, "@typescript-eslint/scope-manager": { - "version": "5.34.0", - "resolved": "https://registry.npmjs.org/@typescript-eslint/scope-manager/-/scope-manager-5.34.0.tgz", - "integrity": "sha512-HNvASMQlah5RsBW6L6c7IJ0vsm+8Sope/wu5sEAf7joJYWNb1LDbJipzmdhdUOnfrDFE6LR1j57x1EYVxrY4ow==", + "version": "5.41.0", + "resolved": "https://registry.npmjs.org/@typescript-eslint/scope-manager/-/scope-manager-5.41.0.tgz", + "integrity": "sha512-xOxPJCnuktUkY2xoEZBKXO5DBCugFzjrVndKdUnyQr3+9aDWZReKq9MhaoVnbL+maVwWJu/N0SEtrtEUNb62QQ==", "dev": true, "requires": { - "@typescript-eslint/types": "5.34.0", - "@typescript-eslint/visitor-keys": "5.34.0" + "@typescript-eslint/types": "5.41.0", + "@typescript-eslint/visitor-keys": "5.41.0" } }, "@typescript-eslint/type-utils": { - "version": "5.34.0", - "resolved": "https://registry.npmjs.org/@typescript-eslint/type-utils/-/type-utils-5.34.0.tgz", - "integrity": "sha512-Pxlno9bjsQ7hs1pdWRUv9aJijGYPYsHpwMeCQ/Inavhym3/XaKt1ZKAA8FIw4odTBfowBdZJDMxf2aavyMDkLg==", + "version": "5.41.0", + "resolved": "https://registry.npmjs.org/@typescript-eslint/type-utils/-/type-utils-5.41.0.tgz", + "integrity": "sha512-L30HNvIG6A1Q0R58e4hu4h+fZqaO909UcnnPbwKiN6Rc3BUEx6ez2wgN7aC0cBfcAjZfwkzE+E2PQQ9nEuoqfA==", "dev": true, "requires": { - "@typescript-eslint/utils": "5.34.0", + "@typescript-eslint/typescript-estree": "5.41.0", + "@typescript-eslint/utils": "5.41.0", "debug": "^4.3.4", "tsutils": "^3.21.0" } }, "@typescript-eslint/types": { - "version": "5.34.0", - "resolved": "https://registry.npmjs.org/@typescript-eslint/types/-/types-5.34.0.tgz", - "integrity": "sha512-49fm3xbbUPuzBIOcy2CDpYWqy/X7VBkxVN+DC21e0zIm3+61Z0NZi6J9mqPmSW1BDVk9FIOvuCFyUPjXz93sjA==", + "version": "5.41.0", + "resolved": "https://registry.npmjs.org/@typescript-eslint/types/-/types-5.41.0.tgz", + "integrity": "sha512-5BejraMXMC+2UjefDvrH0Fo/eLwZRV6859SXRg+FgbhA0R0l6lDqDGAQYhKbXhPN2ofk2kY5sgGyLNL907UXpA==", "dev": true }, "@typescript-eslint/typescript-estree": { - "version": "5.34.0", - "resolved": "https://registry.npmjs.org/@typescript-eslint/typescript-estree/-/typescript-estree-5.34.0.tgz", - "integrity": "sha512-mXHAqapJJDVzxauEkfJI96j3D10sd567LlqroyCeJaHnu42sDbjxotGb3XFtGPYKPD9IyLjhsoULML1oI3M86A==", + "version": "5.41.0", + "resolved": "https://registry.npmjs.org/@typescript-eslint/typescript-estree/-/typescript-estree-5.41.0.tgz", + "integrity": "sha512-SlzFYRwFSvswzDSQ/zPkIWcHv8O5y42YUskko9c4ki+fV6HATsTODUPbRbcGDFYP86gaJL5xohUEytvyNNcXWg==", "dev": true, "requires": { - "@typescript-eslint/types": "5.34.0", - "@typescript-eslint/visitor-keys": "5.34.0", + "@typescript-eslint/types": "5.41.0", + "@typescript-eslint/visitor-keys": "5.41.0", "debug": "^4.3.4", "globby": "^11.1.0", "is-glob": "^4.0.3", "semver": "^7.3.7", "tsutils": "^3.21.0" - }, - "dependencies": { - "array-union": { - "version": "2.1.0", - "resolved": "https://registry.npmjs.org/array-union/-/array-union-2.1.0.tgz", - "integrity": "sha512-HGyxoOTYUyCM6stUe6EJgnd4EoewAI7zMdfqO+kGjnlZmBDz/cR5pf8r/cR4Wq60sL/p0IkcjUEEPwS3GFrIyw==", - "dev": true - }, - "globby": { - "version": "11.1.0", - "resolved": "https://registry.npmjs.org/globby/-/globby-11.1.0.tgz", - "integrity": "sha512-jhIXaOzy1sb8IyocaruWSn1TjmnBVs8Ayhcy83rmxNJ8q2uWKCAj3CnJY+KpGSXCueAPc0i05kVvVKtP1t9S3g==", - "dev": true, - "requires": { - "array-union": "^2.1.0", - "dir-glob": "^3.0.1", - "fast-glob": "^3.2.9", - "ignore": "^5.2.0", - "merge2": "^1.4.1", - "slash": "^3.0.0" - } - }, - "lru-cache": { - "version": "6.0.0", - "resolved": "https://registry.npmjs.org/lru-cache/-/lru-cache-6.0.0.tgz", - "integrity": "sha512-Jo6dJ04CmSjuznwJSS3pUeWmd/H0ffTlkXXgwZi+eq1UCmqQwCh+eLsYOYCwY991i2Fah4h1BEMCx4qThGbsiA==", - "dev": true, - "requires": { - "yallist": "^4.0.0" - } - }, - "semver": { - "version": "7.3.7", - "resolved": "https://registry.npmjs.org/semver/-/semver-7.3.7.tgz", - "integrity": "sha512-QlYTucUYOews+WeEujDoEGziz4K6c47V/Bd+LjSSYcA94p+DmINdf7ncaUinThfvZyu13lN9OY1XDxt8C0Tw0g==", - "dev": true, - "requires": { - "lru-cache": "^6.0.0" - } - }, - "yallist": { - "version": "4.0.0", - "resolved": "https://registry.npmjs.org/yallist/-/yallist-4.0.0.tgz", - "integrity": "sha512-3wdGidZyq5PB084XLES5TpOSRA3wjXAlIWMhum2kRcv/41Sn2emQ0dycQW4uZXLejwKvg6EsvbdlVL+FYEct7A==", - "dev": true - } } }, "@typescript-eslint/utils": { - "version": "5.34.0", - "resolved": "https://registry.npmjs.org/@typescript-eslint/utils/-/utils-5.34.0.tgz", - "integrity": "sha512-kWRYybU4Rn++7lm9yu8pbuydRyQsHRoBDIo11k7eqBWTldN4xUdVUMCsHBiE7aoEkFzrUEaZy3iH477vr4xHAQ==", + "version": "5.41.0", + "resolved": "https://registry.npmjs.org/@typescript-eslint/utils/-/utils-5.41.0.tgz", + "integrity": "sha512-QlvfwaN9jaMga9EBazQ+5DDx/4sAdqDkcs05AsQHMaopluVCUyu1bTRUVKzXbgjDlrRAQrYVoi/sXJ9fmG+KLQ==", "dev": true, "requires": { "@types/json-schema": "^7.0.9", - "@typescript-eslint/scope-manager": "5.34.0", - "@typescript-eslint/types": "5.34.0", - "@typescript-eslint/typescript-estree": "5.34.0", + "@types/semver": "^7.3.12", + "@typescript-eslint/scope-manager": "5.41.0", + "@typescript-eslint/types": "5.41.0", + "@typescript-eslint/typescript-estree": "5.41.0", "eslint-scope": "^5.1.1", - "eslint-utils": "^3.0.0" + "eslint-utils": "^3.0.0", + "semver": "^7.3.7" } }, "@typescript-eslint/visitor-keys": { - "version": "5.34.0", - "resolved": "https://registry.npmjs.org/@typescript-eslint/visitor-keys/-/visitor-keys-5.34.0.tgz", - "integrity": "sha512-O1moYjOSrab0a2fUvFpsJe0QHtvTC+cR+ovYpgKrAVXzqQyc74mv76TgY6z+aEtjQE2vgZux3CQVtGryqdcOAw==", + "version": "5.41.0", + "resolved": "https://registry.npmjs.org/@typescript-eslint/visitor-keys/-/visitor-keys-5.41.0.tgz", + "integrity": "sha512-vilqeHj267v8uzzakbm13HkPMl7cbYpKVjgFWZPIOHIJHZtinvypUhJ5xBXfWYg4eFKqztbMMpOgFpT9Gfx4fw==", "dev": true, "requires": { - "@typescript-eslint/types": "5.34.0", + "@typescript-eslint/types": "5.41.0", "eslint-visitor-keys": "^3.3.0" } }, - "@ungap/promise-all-settled": { - "version": "1.1.2", - "resolved": "https://registry.npmjs.org/@ungap/promise-all-settled/-/promise-all-settled-1.1.2.tgz", - "integrity": "sha512-sL/cEvJWAnClXw0wHk85/2L0G6Sj8UB0Ctc1TEMbKSsmpRosqhwj9gWgFRZSrBr2f9tiXISwNhCPmlfqUqyb9Q==", - "dev": true - }, "acorn": { - "version": "8.8.0", - "resolved": "https://registry.npmjs.org/acorn/-/acorn-8.8.0.tgz", - "integrity": "sha512-QOxyigPVrpZ2GXT+PFyZTl6TtOFc5egxHIP9IlQ+RbupQuX4RkT/Bee4/kQuC02Xkzg84JcT7oLYtDIQxp+v7w==", + "version": "8.8.1", + "resolved": "https://registry.npmjs.org/acorn/-/acorn-8.8.1.tgz", + "integrity": "sha512-7zFpHzhnqYKrkYdUjF1HI1bzd0VygEGX8lFk4k5zVMqHEoES+P+7TKI+EvLO9WVMJ8eekdO0aDEK044xTXwPPA==", "dev": true }, "acorn-jsx": { @@ -9719,12 +9311,6 @@ "string-width": "^4.1.0" }, "dependencies": { - "ansi-regex": { - "version": "5.0.1", - "resolved": "https://registry.npmjs.org/ansi-regex/-/ansi-regex-5.0.1.tgz", - "integrity": "sha512-quJQXlTSUGL2LH9SUXo8VwsY4soanhgo6LNSm84E1LBcE8s3O0wpdiRzyR9z/ZZJMlMWv37qOOb9pdJlMUEKFQ==", - "dev": true - }, "emoji-regex": { "version": "8.0.0", "resolved": "https://registry.npmjs.org/emoji-regex/-/emoji-regex-8.0.0.tgz", @@ -9741,15 +9327,6 @@ "is-fullwidth-code-point": "^3.0.0", "strip-ansi": "^6.0.1" } - }, - "strip-ansi": { - "version": "6.0.1", - "resolved": "https://registry.npmjs.org/strip-ansi/-/strip-ansi-6.0.1.tgz", - "integrity": "sha512-Y38VPSHcqkFrCpFnQ9vuSXmquuv5oXOKpGeT6aGrr3o3Gc9AlVa6JBfUSOCnbxGGZF+/0ooI7KrPuUSztUdU5A==", - "dev": true, - "requires": { - "ansi-regex": "^5.0.1" - } } } }, @@ -9766,19 +9343,30 @@ "dev": true, "requires": { "type-fest": "^1.0.2" + }, + "dependencies": { + "type-fest": { + "version": "1.4.0", + "resolved": "https://registry.npmjs.org/type-fest/-/type-fest-1.4.0.tgz", + "integrity": "sha512-yGSza74xk0UG8k+pLh5oeoYirvIiWo5t0/o3zHHAO2tRDiZcxWP7fywNlXhqb6/r6sWvwi+RsyQMWhVLe4BVuA==", + "dev": true + } } }, "ansi-regex": { - "version": "6.0.1", - "resolved": "https://registry.npmjs.org/ansi-regex/-/ansi-regex-6.0.1.tgz", - "integrity": "sha512-n5M855fKb2SsfMIiFFoVrABHJC8QtHwVx+mHWP3QcEqBHYienj5dHSgjbxtC0WEZXYt4wcD6zrQElDPhFuZgfA==", + "version": "5.0.1", + "resolved": "https://registry.npmjs.org/ansi-regex/-/ansi-regex-5.0.1.tgz", + "integrity": "sha512-quJQXlTSUGL2LH9SUXo8VwsY4soanhgo6LNSm84E1LBcE8s3O0wpdiRzyR9z/ZZJMlMWv37qOOb9pdJlMUEKFQ==", "dev": true }, "ansi-styles": { - "version": "6.1.0", - "resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-6.1.0.tgz", - "integrity": "sha512-VbqNsoz55SYGczauuup0MFUyXNQviSpFTj1RQtFzmQLk18qbVSpTFFGMT293rmDaQuKCT6InmbuEyUne4mTuxQ==", - "dev": true + "version": "4.3.0", + "resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-4.3.0.tgz", + "integrity": "sha512-zbB9rCJAT1rbjiVDb2hqKFHNYLxgtk8NURxZ3IZwD3F6NtxbXZQCnnSi1Lkx+IDohdPlFp222wVALIheZJQSEg==", + "dev": true, + "requires": { + "color-convert": "^2.0.1" + } }, "anymatch": { "version": "3.1.2", @@ -9818,13 +9406,10 @@ "dev": true }, "array-union": { - "version": "1.0.2", - "resolved": "https://registry.npmjs.org/array-union/-/array-union-1.0.2.tgz", - "integrity": "sha512-Dxr6QJj/RdU/hCaBjOfxW+q6lyuVE6JFWIrAUpuOOhoJJoQ99cUn3igRaHVB5P9WrgFVN0FfArM3x0cueOU8ng==", - "dev": true, - "requires": { - "array-uniq": "^1.0.1" - } + "version": "2.1.0", + "resolved": "https://registry.npmjs.org/array-union/-/array-union-2.1.0.tgz", + "integrity": "sha512-HGyxoOTYUyCM6stUe6EJgnd4EoewAI7zMdfqO+kGjnlZmBDz/cR5pf8r/cR4Wq60sL/p0IkcjUEEPwS3GFrIyw==", + "dev": true }, "array-uniq": { "version": "1.0.3", @@ -9891,9 +9476,9 @@ "dev": true }, "before-after-hook": { - "version": "2.2.2", - "resolved": "https://registry.npmjs.org/before-after-hook/-/before-after-hook-2.2.2.tgz", - "integrity": "sha512-3pZEU3NT5BFUo/AD5ERPWOgQOCZITni6iavr5AUw5AUwQjMlI0kzu5btnyD39AF0gUEsDPwJT+oY1ORBJijPjQ==", + "version": "2.2.3", + "resolved": "https://registry.npmjs.org/before-after-hook/-/before-after-hook-2.2.3.tgz", + "integrity": "sha512-NzUnlZexiaH/46WDhANlyR2bXRopNg4F/zuSA3OpZnllCUgRaOF2znDioDWrmbNVsuZk6l9pMquQB38cfBZwkQ==", "dev": true }, "binary-extensions": { @@ -9903,9 +9488,9 @@ "dev": true }, "bl": { - "version": "5.0.0", - "resolved": "https://registry.npmjs.org/bl/-/bl-5.0.0.tgz", - "integrity": "sha512-8vxFNZ0pflFfi0WXA3WQXlj6CaMEwsmh63I1CNp0q+wWv8sD0ARx1KovSQd0l2GkwrMIOyedq0EF1FxI+RCZLQ==", + "version": "5.1.0", + "resolved": "https://registry.npmjs.org/bl/-/bl-5.1.0.tgz", + "integrity": "sha512-tv1ZJHLfTDnXE6tMHv73YgSJaWR2AFuPwMntBe7XL/GBFHnT0CLnsHMogfk5+GzCDC5ZWarSCYaIGATZt9dNsQ==", "dev": true, "requires": { "buffer": "^6.0.3", @@ -9935,6 +9520,12 @@ "integrity": "sha512-JToIvOmz6nhGsUhAYScbo2d6Py5wojjNfoxoc2mEVLUdJ70gJK2gnd+ABY1Tc3sVMyK7QDPtN0T/XdlCQWITyQ==", "dev": true }, + "chalk": { + "version": "5.1.2", + "resolved": "https://registry.npmjs.org/chalk/-/chalk-5.1.2.tgz", + "integrity": "sha512-E5CkT4jWURs1Vy5qGJye+XwCkNj7Od3Af7CP6SujMetSMkLs8Do2RWJK5yx1wamHV/op8Rz+9rltjaTQWDnEFQ==", + "dev": true + }, "type-fest": { "version": "2.19.0", "resolved": "https://registry.npmjs.org/type-fest/-/type-fest-2.19.0.tgz", @@ -9969,15 +9560,15 @@ "dev": true }, "browserslist": { - "version": "4.21.3", - "resolved": "https://registry.npmjs.org/browserslist/-/browserslist-4.21.3.tgz", - "integrity": "sha512-898rgRXLAyRkM1GryrrBHGkqA5hlpkV5MhtZwg9QXeiyLUYs2k00Un05aX5l2/yJIOObYKOpS2JNo8nJDE7fWQ==", + "version": "4.21.4", + "resolved": "https://registry.npmjs.org/browserslist/-/browserslist-4.21.4.tgz", + "integrity": "sha512-CBHJJdDmgjl3daYjN5Cp5kbTf1mUhZoS+beLklHIvkOWscs83YAhLlF3Wsh/lciQYAcbBJgTOD44VtG31ZM4Hw==", "dev": true, "requires": { - "caniuse-lite": "^1.0.30001370", - "electron-to-chromium": "^1.4.202", + "caniuse-lite": "^1.0.30001400", + "electron-to-chromium": "^1.4.251", "node-releases": "^2.0.6", - "update-browserslist-db": "^1.0.5" + "update-browserslist-db": "^1.0.9" } }, "buffer": { @@ -10003,41 +9594,24 @@ "dev": true }, "cacheable-lookup": { - "version": "6.1.0", - "resolved": "https://registry.npmjs.org/cacheable-lookup/-/cacheable-lookup-6.1.0.tgz", - "integrity": "sha512-KJ/Dmo1lDDhmW2XDPMo+9oiy/CeqosPguPCrgcVzKyZrL6pM1gU2GmPY/xo6OQPTUaA/c0kwHuywB4E6nmT9ww==", + "version": "7.0.0", + "resolved": "https://registry.npmjs.org/cacheable-lookup/-/cacheable-lookup-7.0.0.tgz", + "integrity": "sha512-+qJyx4xiKra8mZrcwhjMRMUhD5NR1R8esPkzIYxX96JiecFoxAXFuz/GpR3+ev4PE1WamHip78wV0vcmPQtp8w==", "dev": true }, "cacheable-request": { - "version": "7.0.2", - "resolved": "https://registry.npmjs.org/cacheable-request/-/cacheable-request-7.0.2.tgz", - "integrity": "sha512-pouW8/FmiPQbuGpkXQ9BAPv/Mo5xDGANgSNXzTzJ8DrKGuXOssM4wIQRjfanNRh3Yu5cfYPvcorqbhg2KIJtew==", + "version": "10.2.2", + "resolved": "https://registry.npmjs.org/cacheable-request/-/cacheable-request-10.2.2.tgz", + "integrity": "sha512-KxjQZM3UIo7/J6W4sLpwFvu1GB3Whv8NtZ8ZrUL284eiQjiXeeqWTdhixNrp/NLZ/JNuFBo6BD4ZaO8ZJ5BN8Q==", "dev": true, "requires": { - "clone-response": "^1.0.2", - "get-stream": "^5.1.0", - "http-cache-semantics": "^4.0.0", - "keyv": "^4.0.0", - "lowercase-keys": "^2.0.0", - "normalize-url": "^6.0.1", - "responselike": "^2.0.0" - }, - "dependencies": { - "get-stream": { - "version": "5.2.0", - "resolved": "https://registry.npmjs.org/get-stream/-/get-stream-5.2.0.tgz", - "integrity": "sha512-nBF+F1rAZVCu/p7rjzgA+Yb4lfYXrpl7a6VmJrU8wF9I1CKvP/QwPNZHnOlwbTkY6dvtFIzFMSyQXbLoTQPRpA==", - "dev": true, - "requires": { - "pump": "^3.0.0" - } - }, - "lowercase-keys": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/lowercase-keys/-/lowercase-keys-2.0.0.tgz", - "integrity": "sha512-tqNXrS78oMOE73NMxK4EMLQsQowWf8jKooH9g7xPavRT706R6bkQJ6DY2Te7QukaZsulxa30wQ7bk0pm4XiHmA==", - "dev": true - } + "@types/http-cache-semantics": "^4.0.1", + "get-stream": "^6.0.1", + "http-cache-semantics": "^4.1.0", + "keyv": "^4.5.0", + "mimic-response": "^4.0.0", + "normalize-url": "^7.2.0", + "responselike": "^3.0.0" } }, "caching-transform": { @@ -10075,16 +9649,20 @@ "dev": true }, "caniuse-lite": { - "version": "1.0.30001381", - "resolved": "https://registry.npmjs.org/caniuse-lite/-/caniuse-lite-1.0.30001381.tgz", - "integrity": "sha512-fEnkDOKpvp6qc+olg7+NzE1SqyfiyKf4uci7fAU38M3zxs0YOyKOxW/nMZ2l9sJbt7KZHcDIxUnbI0Iime7V4w==", + "version": "1.0.30001425", + "resolved": "https://registry.npmjs.org/caniuse-lite/-/caniuse-lite-1.0.30001425.tgz", + "integrity": "sha512-/pzFv0OmNG6W0ym80P3NtapU0QEiDS3VuYAZMGoLLqiC7f6FJFe1MjpQDREGApeenD9wloeytmVDj+JLXPC6qw==", "dev": true }, "chalk": { - "version": "5.0.1", - "resolved": "https://registry.npmjs.org/chalk/-/chalk-5.0.1.tgz", - "integrity": "sha512-Fo07WOYGqMfCWHOzSXOt2CxDbC6skS/jO9ynEcmpANMoPrD+W1r1K6Vx7iNm+AQmETU1Xr2t+n8nzkV9t6xh3w==", - "dev": true + "version": "4.1.2", + "resolved": "https://registry.npmjs.org/chalk/-/chalk-4.1.2.tgz", + "integrity": "sha512-oKnbhFyRIXpUuez8iBMmyEa4nbj4IOQyuhc/wy9kY7/WVPcwIO9VA668Pu8RkO7+0G76SLROeyw9CpQ061i4mA==", + "dev": true, + "requires": { + "ansi-styles": "^4.1.0", + "supports-color": "^7.1.0" + } }, "chardet": { "version": "0.7.0", @@ -10120,9 +9698,9 @@ } }, "ci-info": { - "version": "3.3.2", - "resolved": "https://registry.npmjs.org/ci-info/-/ci-info-3.3.2.tgz", - "integrity": "sha512-xmDt/QIAdeZ9+nfdPsaBCpMvHNLFiLdjj59qjqn+6iPe6YmHGQ35sBnQ8uslRBXFmXkiZQOJRjvQeoGppoTjjg==", + "version": "3.5.0", + "resolved": "https://registry.npmjs.org/ci-info/-/ci-info-3.5.0.tgz", + "integrity": "sha512-yH4RezKOGlOhxkmhbeNuC4eYZKAUsEaGtBuBzDDP1eFUKiccDWzBABxBfOx31IDwDIXMTxWuwAxUGModvkbuVw==", "dev": true }, "clean-stack": { @@ -10159,31 +9737,16 @@ "dev": true }, "cliui": { - "version": "6.0.0", - "resolved": "https://registry.npmjs.org/cliui/-/cliui-6.0.0.tgz", - "integrity": "sha512-t6wbgtoCXvAzst7QgXxJYqPt0usEfbgQdftEPbLL/cvv6HPE5VgvqCuAIDR0NgU52ds6rFwqrgakNLrHEjCbrQ==", + "version": "8.0.1", + "resolved": "https://registry.npmjs.org/cliui/-/cliui-8.0.1.tgz", + "integrity": "sha512-BSeNnyus75C4//NQ9gQt1/csTXyo/8Sb+afLAkzAptFuMsod9HFokGNudZpi/oQV73hnVK+sR+5PVRMd+Dr7YQ==", "dev": true, "requires": { "string-width": "^4.2.0", - "strip-ansi": "^6.0.0", - "wrap-ansi": "^6.2.0" + "strip-ansi": "^6.0.1", + "wrap-ansi": "^7.0.0" }, "dependencies": { - "ansi-regex": { - "version": "5.0.1", - "resolved": "https://registry.npmjs.org/ansi-regex/-/ansi-regex-5.0.1.tgz", - "integrity": "sha512-quJQXlTSUGL2LH9SUXo8VwsY4soanhgo6LNSm84E1LBcE8s3O0wpdiRzyR9z/ZZJMlMWv37qOOb9pdJlMUEKFQ==", - "dev": true - }, - "ansi-styles": { - "version": "4.3.0", - "resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-4.3.0.tgz", - "integrity": "sha512-zbB9rCJAT1rbjiVDb2hqKFHNYLxgtk8NURxZ3IZwD3F6NtxbXZQCnnSi1Lkx+IDohdPlFp222wVALIheZJQSEg==", - "dev": true, - "requires": { - "color-convert": "^2.0.1" - } - }, "emoji-regex": { "version": "8.0.0", "resolved": "https://registry.npmjs.org/emoji-regex/-/emoji-regex-8.0.0.tgz", @@ -10201,19 +9764,10 @@ "strip-ansi": "^6.0.1" } }, - "strip-ansi": { - "version": "6.0.1", - "resolved": "https://registry.npmjs.org/strip-ansi/-/strip-ansi-6.0.1.tgz", - "integrity": "sha512-Y38VPSHcqkFrCpFnQ9vuSXmquuv5oXOKpGeT6aGrr3o3Gc9AlVa6JBfUSOCnbxGGZF+/0ooI7KrPuUSztUdU5A==", - "dev": true, - "requires": { - "ansi-regex": "^5.0.1" - } - }, "wrap-ansi": { - "version": "6.2.0", - "resolved": "https://registry.npmjs.org/wrap-ansi/-/wrap-ansi-6.2.0.tgz", - "integrity": "sha512-r6lPcBGxZXlIcymEu7InxDMhdW0KDxpLgoFLcguasxCaJ/SOIZwINatK9KY/tf+ZrlywOKU0UDj3ATXUBfxJXA==", + "version": "7.0.0", + "resolved": "https://registry.npmjs.org/wrap-ansi/-/wrap-ansi-7.0.0.tgz", + "integrity": "sha512-YVGIj2kamLSTxw6NsZjoBxfSwsn0ycdesmc4p+Q21c5zPuZ1pl+NfxVdxPtdHvmNVOQ6XSYG4AUtyt/Fi7D16Q==", "dev": true, "requires": { "ansi-styles": "^4.0.0", @@ -10229,19 +9783,10 @@ "integrity": "sha512-JQHZ2QMW6l3aH/j6xCqQThY/9OH4D/9ls34cgkUBiEeocRTU04tHfKPBsUK1PqZCUQM7GiA0IIXJSuXHI64Kbg==", "dev": true }, - "clone-response": { - "version": "1.0.3", - "resolved": "https://registry.npmjs.org/clone-response/-/clone-response-1.0.3.tgz", - "integrity": "sha512-ROoL94jJH2dUVML2Y/5PEDNaSHgeOdSDicUyS7izcF63G6sTc/FTjLub4b8Il9S8S0beOfYt0TaA5qvFK+w0wA==", - "dev": true, - "requires": { - "mimic-response": "^1.0.0" - } - }, "cluster-key-slot": { - "version": "1.1.0", - "resolved": "https://registry.npmjs.org/cluster-key-slot/-/cluster-key-slot-1.1.0.tgz", - "integrity": "sha512-2Nii8p3RwAPiFwsnZvukotvow2rIHM+yQ6ZcBXGHdniadkYGZYiGmkHJIbZPIV9nfv7m/U1IPMVVcAhoWFeklw==" + "version": "1.1.1", + "resolved": "https://registry.npmjs.org/cluster-key-slot/-/cluster-key-slot-1.1.1.tgz", + "integrity": "sha512-rwHwUfXL40Chm1r08yrhU3qpUvdVlgkKNeyeGPOxnW8/SyVDvgRaed/Uz54AqWNaTCAThlj6QAs3TZcKI0xDEw==" }, "color-convert": { "version": "2.0.1", @@ -10279,16 +9824,6 @@ "integrity": "sha512-W9pAhw0ja1Edb5GVdIF1mjZw/ASI0AlShXM83UUGe2DVr5TdAPEA1OA8m/g8zWp9x6On7gqufY+FatDbC3MDQg==", "dev": true }, - "compress-brotli": { - "version": "1.3.8", - "resolved": "https://registry.npmjs.org/compress-brotli/-/compress-brotli-1.3.8.tgz", - "integrity": "sha512-lVcQsjhxhIXsuupfy9fmZUFtAIdBmXA7EGY6GBdgZ++qkM9zG4YFT8iU7FoBxzryNDMOpD1HIFHUSX4D87oqhQ==", - "dev": true, - "requires": { - "@types/json-buffer": "~3.0.0", - "json-buffer": "~3.0.1" - } - }, "concat-map": { "version": "0.0.1", "resolved": "https://registry.npmjs.org/concat-map/-/concat-map-0.0.1.tgz", @@ -10327,13 +9862,10 @@ } }, "convert-source-map": { - "version": "1.8.0", - "resolved": "https://registry.npmjs.org/convert-source-map/-/convert-source-map-1.8.0.tgz", - "integrity": "sha512-+OQdjP49zViI/6i7nIJpA8rAl4sV/JdPfU9nZs3VqOwGIgizICvuN2ru6fMd+4llL0tar18UYJXfZ/TWtmhUjA==", - "dev": true, - "requires": { - "safe-buffer": "~5.1.1" - } + "version": "1.9.0", + "resolved": "https://registry.npmjs.org/convert-source-map/-/convert-source-map-1.9.0.tgz", + "integrity": "sha512-ASFBup0Mz1uyiIjANan1jzLQami9z1PoYSZCiiYW2FczPbenXc45FZdBZLzOT+r6+iciuEModtmCti+hjaAk0A==", + "dev": true }, "core-util-is": { "version": "1.0.3", @@ -10378,6 +9910,14 @@ "dev": true, "requires": { "type-fest": "^1.0.1" + }, + "dependencies": { + "type-fest": { + "version": "1.4.0", + "resolved": "https://registry.npmjs.org/type-fest/-/type-fest-1.4.0.tgz", + "integrity": "sha512-yGSza74xk0UG8k+pLh5oeoYirvIiWo5t0/o3zHHAO2tRDiZcxWP7fywNlXhqb6/r6sWvwi+RsyQMWhVLe4BVuA==", + "dev": true + } } }, "data-uri-to-buffer": { @@ -10431,18 +9971,18 @@ "dev": true }, "default-require-extensions": { - "version": "3.0.0", - "resolved": "https://registry.npmjs.org/default-require-extensions/-/default-require-extensions-3.0.0.tgz", - "integrity": "sha512-ek6DpXq/SCpvjhpFsLFRVtIxJCRw6fUR42lYMVZuUMK7n8eMz4Uh5clckdBjEpLhn/gEBZo7hDJnJcwdKLKQjg==", + "version": "3.0.1", + "resolved": "https://registry.npmjs.org/default-require-extensions/-/default-require-extensions-3.0.1.tgz", + "integrity": "sha512-eXTJmRbm2TIt9MgWTsOH1wEuhew6XGZcMeGKCtLedIg/NCsg1iBePXkceTdK4Fii7pzmN9tGsZhKzZ4h7O/fxw==", "dev": true, "requires": { "strip-bom": "^4.0.0" } }, "defaults": { - "version": "1.0.3", - "resolved": "https://registry.npmjs.org/defaults/-/defaults-1.0.3.tgz", - "integrity": "sha512-s82itHOnYrN0Ib8r+z7laQz3sdE+4FP3d9Q7VLO7U+KRT+CR0GsWuyHxzdAY82I7cXv0G/twrqomTJLOssO5HA==", + "version": "1.0.4", + "resolved": "https://registry.npmjs.org/defaults/-/defaults-1.0.4.tgz", + "integrity": "sha512-eFuaLoy/Rxalv2kr+lqMlUnrDWV+3j4pljOIJgLIhI058IQfWJ7vXhyEIHu+HtC738klGALYxOKDO0bQP3tg8A==", "dev": true, "requires": { "clone": "^1.0.2" @@ -10540,9 +10080,9 @@ "dev": true }, "electron-to-chromium": { - "version": "1.4.225", - "resolved": "https://registry.npmjs.org/electron-to-chromium/-/electron-to-chromium-1.4.225.tgz", - "integrity": "sha512-ICHvGaCIQR3P88uK8aRtx8gmejbVJyC6bB4LEC3anzBrIzdzC7aiZHY4iFfXhN4st6I7lMO0x4sgBHf/7kBvRw==", + "version": "1.4.284", + "resolved": "https://registry.npmjs.org/electron-to-chromium/-/electron-to-chromium-1.4.284.tgz", + "integrity": "sha512-M8WEXFuKXMYMVr45fo8mq0wUrrJHheiKZf6BArTKk9ZBYCKJEOU5H8cdWgDT+qCVZf7Na4lVUaZsA+h6uA9+PA==", "dev": true }, "email-addresses": { @@ -10557,15 +10097,6 @@ "integrity": "sha512-L18DaJsXSUk2+42pv8mLs5jJT2hqFkFE4j21wOmgbUqsZ2hL72NsUU785g9RXgo3s0ZNgVl42TiHp3ZtOv/Vyg==", "dev": true }, - "end-of-stream": { - "version": "1.4.4", - "resolved": "https://registry.npmjs.org/end-of-stream/-/end-of-stream-1.4.4.tgz", - "integrity": "sha512-+uw1inIHVPQoaVuHzRyXd21icM+cnt4CzD5rW+NC1wjOUSTOs+Te7FOv7AhN7vS9x/oIyhLP5PR1H+phQAHu5Q==", - "dev": true, - "requires": { - "once": "^1.4.0" - } - }, "error-ex": { "version": "1.3.2", "resolved": "https://registry.npmjs.org/error-ex/-/error-ex-1.3.2.tgz", @@ -10576,31 +10107,32 @@ } }, "es-abstract": { - "version": "1.20.1", - "resolved": "https://registry.npmjs.org/es-abstract/-/es-abstract-1.20.1.tgz", - "integrity": "sha512-WEm2oBhfoI2sImeM4OF2zE2V3BYdSF+KnSi9Sidz51fQHd7+JuF8Xgcj9/0o+OWeIeIS/MiuNnlruQrJf16GQA==", + "version": "1.20.4", + "resolved": "https://registry.npmjs.org/es-abstract/-/es-abstract-1.20.4.tgz", + "integrity": "sha512-0UtvRN79eMe2L+UNEF1BwRe364sj/DXhQ/k5FmivgoSdpM90b8Jc0mDzKMGo7QS0BVbOP/bTwBKNnDc9rNzaPA==", "dev": true, "requires": { "call-bind": "^1.0.2", "es-to-primitive": "^1.2.1", "function-bind": "^1.1.1", "function.prototype.name": "^1.1.5", - "get-intrinsic": "^1.1.1", + "get-intrinsic": "^1.1.3", "get-symbol-description": "^1.0.0", "has": "^1.0.3", "has-property-descriptors": "^1.0.0", "has-symbols": "^1.0.3", "internal-slot": "^1.0.3", - "is-callable": "^1.2.4", + "is-callable": "^1.2.7", "is-negative-zero": "^2.0.2", "is-regex": "^1.1.4", "is-shared-array-buffer": "^1.0.2", "is-string": "^1.0.7", "is-weakref": "^1.0.2", - "object-inspect": "^1.12.0", + "object-inspect": "^1.12.2", "object-keys": "^1.1.1", - "object.assign": "^4.1.2", + "object.assign": "^4.1.4", "regexp.prototype.flags": "^1.4.3", + "safe-regex-test": "^1.0.0", "string.prototype.trimend": "^1.0.5", "string.prototype.trimstart": "^1.0.5", "unbox-primitive": "^1.0.2" @@ -10658,9 +10190,9 @@ "dev": true }, "escape-string-regexp": { - "version": "5.0.0", - "resolved": "https://registry.npmjs.org/escape-string-regexp/-/escape-string-regexp-5.0.0.tgz", - "integrity": "sha512-/veY75JbMK4j1yjvuUxuVsiS/hr/4iHs9FTT6cgTexxdE0Ly/glccBAkloH/DofkjRbZU3bnoj38mOmhkZ0lHw==", + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/escape-string-regexp/-/escape-string-regexp-4.0.0.tgz", + "integrity": "sha512-TtpcNJ3XAzx3Gq8sWRzJaVajRs0uVxA2YAkdb1jm2YkPz4G6egUFAyA3n5vtEIZefPk5Wa4UXbKuS5fKkJWdgA==", "dev": true }, "escodegen": { @@ -10718,14 +10250,15 @@ } }, "eslint": { - "version": "8.22.0", - "resolved": "https://registry.npmjs.org/eslint/-/eslint-8.22.0.tgz", - "integrity": "sha512-ci4t0sz6vSRKdmkOGmprBo6fmI4PrphDFMy5JEq/fNS0gQkJM3rLmrqcp8ipMcdobH3KtUP40KniAE9W19S4wA==", + "version": "8.26.0", + "resolved": "https://registry.npmjs.org/eslint/-/eslint-8.26.0.tgz", + "integrity": "sha512-kzJkpaw1Bfwheq4VXUezFriD1GxszX6dUekM7Z3aC2o4hju+tsR/XyTC3RcoSD7jmy9VkPU3+N6YjVU2e96Oyg==", "dev": true, "requires": { - "@eslint/eslintrc": "^1.3.0", - "@humanwhocodes/config-array": "^0.10.4", - "@humanwhocodes/gitignore-to-minimatch": "^1.0.2", + "@eslint/eslintrc": "^1.3.3", + "@humanwhocodes/config-array": "^0.11.6", + "@humanwhocodes/module-importer": "^1.0.1", + "@nodelib/fs.walk": "^1.2.8", "ajv": "^6.10.0", "chalk": "^4.0.0", "cross-spawn": "^7.0.2", @@ -10735,21 +10268,21 @@ "eslint-scope": "^7.1.1", "eslint-utils": "^3.0.0", "eslint-visitor-keys": "^3.3.0", - "espree": "^9.3.3", + "espree": "^9.4.0", "esquery": "^1.4.0", "esutils": "^2.0.2", "fast-deep-equal": "^3.1.3", "file-entry-cache": "^6.0.1", "find-up": "^5.0.0", - "functional-red-black-tree": "^1.0.1", - "glob-parent": "^6.0.1", + "glob-parent": "^6.0.2", "globals": "^13.15.0", - "globby": "^11.1.0", "grapheme-splitter": "^1.0.4", "ignore": "^5.2.0", "import-fresh": "^3.0.0", "imurmurhash": "^0.1.4", "is-glob": "^4.0.0", + "is-path-inside": "^3.0.3", + "js-sdsl": "^4.1.4", "js-yaml": "^4.1.0", "json-stable-stringify-without-jsonify": "^1.0.1", "levn": "^0.4.1", @@ -10760,47 +10293,9 @@ "regexpp": "^3.2.0", "strip-ansi": "^6.0.1", "strip-json-comments": "^3.1.0", - "text-table": "^0.2.0", - "v8-compile-cache": "^2.0.3" + "text-table": "^0.2.0" }, "dependencies": { - "ansi-regex": { - "version": "5.0.1", - "resolved": "https://registry.npmjs.org/ansi-regex/-/ansi-regex-5.0.1.tgz", - "integrity": "sha512-quJQXlTSUGL2LH9SUXo8VwsY4soanhgo6LNSm84E1LBcE8s3O0wpdiRzyR9z/ZZJMlMWv37qOOb9pdJlMUEKFQ==", - "dev": true - }, - "ansi-styles": { - "version": "4.3.0", - "resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-4.3.0.tgz", - "integrity": "sha512-zbB9rCJAT1rbjiVDb2hqKFHNYLxgtk8NURxZ3IZwD3F6NtxbXZQCnnSi1Lkx+IDohdPlFp222wVALIheZJQSEg==", - "dev": true, - "requires": { - "color-convert": "^2.0.1" - } - }, - "array-union": { - "version": "2.1.0", - "resolved": "https://registry.npmjs.org/array-union/-/array-union-2.1.0.tgz", - "integrity": "sha512-HGyxoOTYUyCM6stUe6EJgnd4EoewAI7zMdfqO+kGjnlZmBDz/cR5pf8r/cR4Wq60sL/p0IkcjUEEPwS3GFrIyw==", - "dev": true - }, - "chalk": { - "version": "4.1.2", - "resolved": "https://registry.npmjs.org/chalk/-/chalk-4.1.2.tgz", - "integrity": "sha512-oKnbhFyRIXpUuez8iBMmyEa4nbj4IOQyuhc/wy9kY7/WVPcwIO9VA668Pu8RkO7+0G76SLROeyw9CpQ061i4mA==", - "dev": true, - "requires": { - "ansi-styles": "^4.1.0", - "supports-color": "^7.1.0" - } - }, - "escape-string-regexp": { - "version": "4.0.0", - "resolved": "https://registry.npmjs.org/escape-string-regexp/-/escape-string-regexp-4.0.0.tgz", - "integrity": "sha512-TtpcNJ3XAzx3Gq8sWRzJaVajRs0uVxA2YAkdb1jm2YkPz4G6egUFAyA3n5vtEIZefPk5Wa4UXbKuS5fKkJWdgA==", - "dev": true - }, "eslint-scope": { "version": "7.1.1", "resolved": "https://registry.npmjs.org/eslint-scope/-/eslint-scope-7.1.1.tgz", @@ -10816,66 +10311,6 @@ "resolved": "https://registry.npmjs.org/estraverse/-/estraverse-5.3.0.tgz", "integrity": "sha512-MMdARuVEQziNTeJD8DgMqmhwR11BRQ/cBP+pLtYdSTnf3MIO8fFeiINEbX36ZdNlfU/7A9f3gUw49B3oQsvwBA==", "dev": true - }, - "find-up": { - "version": "5.0.0", - "resolved": "https://registry.npmjs.org/find-up/-/find-up-5.0.0.tgz", - "integrity": "sha512-78/PXT1wlLLDgTzDs7sjq9hzz0vXD+zn+7wypEe4fXQxCmdmqfGsEPQxmiCSQI3ajFV91bVSsvNtrJRiW6nGng==", - "dev": true, - "requires": { - "locate-path": "^6.0.0", - "path-exists": "^4.0.0" - } - }, - "globby": { - "version": "11.1.0", - "resolved": "https://registry.npmjs.org/globby/-/globby-11.1.0.tgz", - "integrity": "sha512-jhIXaOzy1sb8IyocaruWSn1TjmnBVs8Ayhcy83rmxNJ8q2uWKCAj3CnJY+KpGSXCueAPc0i05kVvVKtP1t9S3g==", - "dev": true, - "requires": { - "array-union": "^2.1.0", - "dir-glob": "^3.0.1", - "fast-glob": "^3.2.9", - "ignore": "^5.2.0", - "merge2": "^1.4.1", - "slash": "^3.0.0" - } - }, - "locate-path": { - "version": "6.0.0", - "resolved": "https://registry.npmjs.org/locate-path/-/locate-path-6.0.0.tgz", - "integrity": "sha512-iPZK6eYjbxRu3uB4/WZ3EsEIMJFMqAoopl3R+zuq0UjcAm/MO6KCweDgPfP3elTztoKP3KtnVHxTn2NHBSDVUw==", - "dev": true, - "requires": { - "p-locate": "^5.0.0" - } - }, - "p-limit": { - "version": "3.1.0", - "resolved": "https://registry.npmjs.org/p-limit/-/p-limit-3.1.0.tgz", - "integrity": "sha512-TYOanM3wGwNGsZN2cVTYPArw454xnXj5qmWF1bEoAc4+cU/ol7GVh7odevjp1FNHduHc3KZMcFduxU5Xc6uJRQ==", - "dev": true, - "requires": { - "yocto-queue": "^0.1.0" - } - }, - "p-locate": { - "version": "5.0.0", - "resolved": "https://registry.npmjs.org/p-locate/-/p-locate-5.0.0.tgz", - "integrity": "sha512-LaNjtRWUBY++zB5nE/NwcaoMylSPk+S+ZHNB1TzdbMJMny6dynpAGt7X/tl/QYq3TIeE6nxHppbo2LGymrG5Pw==", - "dev": true, - "requires": { - "p-limit": "^3.0.2" - } - }, - "strip-ansi": { - "version": "6.0.1", - "resolved": "https://registry.npmjs.org/strip-ansi/-/strip-ansi-6.0.1.tgz", - "integrity": "sha512-Y38VPSHcqkFrCpFnQ9vuSXmquuv5oXOKpGeT6aGrr3o3Gc9AlVa6JBfUSOCnbxGGZF+/0ooI7KrPuUSztUdU5A==", - "dev": true, - "requires": { - "ansi-regex": "^5.0.1" - } } } }, @@ -10913,9 +10348,9 @@ "dev": true }, "espree": { - "version": "9.3.3", - "resolved": "https://registry.npmjs.org/espree/-/espree-9.3.3.tgz", - "integrity": "sha512-ORs1Rt/uQTqUKjDdGCyrtYxbazf5umATSf/K4qxjmZHORR6HJk+2s/2Pqe+Kk49HHINC/xNIrGfgh8sZcll0ng==", + "version": "9.4.0", + "resolved": "https://registry.npmjs.org/espree/-/espree-9.4.0.tgz", + "integrity": "sha512-DQmnRpLj7f6TgN/NYb0MTzJXL+vJF9h3pHy4JhCIs3zwcgez8xmGg3sXHcEO97BrmO2OSvCwMdfdlyl+E9KjOw==", "dev": true, "requires": { "acorn": "^8.8.0", @@ -10990,6 +10425,14 @@ "onetime": "^6.0.0", "signal-exit": "^3.0.7", "strip-final-newline": "^3.0.0" + }, + "dependencies": { + "is-stream": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/is-stream/-/is-stream-3.0.0.tgz", + "integrity": "sha512-LnQR4bZ9IADDRSkvpqMGvt/tEJWclzklNgSw48V5EAaAeDd6qGvN8ei6k5p0tvxSR171VmGyHuTiAOfxAbr8kA==", + "dev": true + } } }, "external-editor": { @@ -11010,9 +10453,9 @@ "dev": true }, "fast-glob": { - "version": "3.2.11", - "resolved": "https://registry.npmjs.org/fast-glob/-/fast-glob-3.2.11.tgz", - "integrity": "sha512-xrO3+1bxSo3ZVHAnqzyuewYT6aMFHRAd4Kcs92MAonjwQZLsK9d0SF1IyQ3k5PoirxTW0Oe/RqFgMQ6TcNE5Ew==", + "version": "3.2.12", + "resolved": "https://registry.npmjs.org/fast-glob/-/fast-glob-3.2.12.tgz", + "integrity": "sha512-DVj4CQIYYow0BlaelwK1pHl5n5cRSJfM60UA0zK891sVInoPri2Ekj7+e1CT3/3qxXenpI+nBBmQAcJPJgaj4w==", "dev": true, "requires": { "@nodelib/fs.stat": "^2.0.2", @@ -11065,13 +10508,27 @@ } }, "figures": { - "version": "4.0.1", - "resolved": "https://registry.npmjs.org/figures/-/figures-4.0.1.tgz", - "integrity": "sha512-rElJwkA/xS04Vfg+CaZodpso7VqBknOYbzi6I76hI4X80RUjkSxO2oAyPmGbuXUppywjqndOrQDl817hDnI++w==", + "version": "5.0.0", + "resolved": "https://registry.npmjs.org/figures/-/figures-5.0.0.tgz", + "integrity": "sha512-ej8ksPF4x6e5wvK9yevct0UCXh8TTFlWGVLlgjZuoBH1HwjIfKE/IdL5mq89sFA7zELi1VhKpmtDnrs7zWyeyg==", "dev": true, "requires": { "escape-string-regexp": "^5.0.0", "is-unicode-supported": "^1.2.0" + }, + "dependencies": { + "escape-string-regexp": { + "version": "5.0.0", + "resolved": "https://registry.npmjs.org/escape-string-regexp/-/escape-string-regexp-5.0.0.tgz", + "integrity": "sha512-/veY75JbMK4j1yjvuUxuVsiS/hr/4iHs9FTT6cgTexxdE0Ly/glccBAkloH/DofkjRbZU3bnoj38mOmhkZ0lHw==", + "dev": true + }, + "is-unicode-supported": { + "version": "1.3.0", + "resolved": "https://registry.npmjs.org/is-unicode-supported/-/is-unicode-supported-1.3.0.tgz", + "integrity": "sha512-43r2mRvz+8JRIKnWJ+3j8JtjRKZ6GmjzfaE/qiBJnikNnYv/6bagRJ1kUhNk8R5EX/GkobD+r+sfxCPJsiKBLQ==", + "dev": true + } } }, "file-entry-cache": { @@ -11127,12 +10584,12 @@ } }, "find-up": { - "version": "4.1.0", - "resolved": "https://registry.npmjs.org/find-up/-/find-up-4.1.0.tgz", - "integrity": "sha512-PpOwAdQ/YlXQ2vj8a3h8IipDuYRi3wceVQQGYWxNINccq40Anw7BlsEXCMbt1Zt+OLA6Fq9suIpIWD0OsnISlw==", + "version": "5.0.0", + "resolved": "https://registry.npmjs.org/find-up/-/find-up-5.0.0.tgz", + "integrity": "sha512-78/PXT1wlLLDgTzDs7sjq9hzz0vXD+zn+7wypEe4fXQxCmdmqfGsEPQxmiCSQI3ajFV91bVSsvNtrJRiW6nGng==", "dev": true, "requires": { - "locate-path": "^5.0.0", + "locate-path": "^6.0.0", "path-exists": "^4.0.0" } }, @@ -11180,9 +10637,9 @@ } }, "form-data-encoder": { - "version": "2.1.0", - "resolved": "https://registry.npmjs.org/form-data-encoder/-/form-data-encoder-2.1.0.tgz", - "integrity": "sha512-njK60LnfhfDWy+AEUIf9ZQNRAcmXCdDfiNOm2emuPtzwh7U9k/mo9F3S54aPiaZ3vhqUjikVLfcPg2KuBddskQ==", + "version": "2.1.3", + "resolved": "https://registry.npmjs.org/form-data-encoder/-/form-data-encoder-2.1.3.tgz", + "integrity": "sha512-KqU0nnPMgIJcCOFTNJFEA8epcseEaoox4XZffTgy8jlI6pL/5EFyR54NRG7CnCJN0biY7q52DO3MH6/sJ/TKlQ==", "dev": true }, "formdata-polyfill": { @@ -11278,12 +10735,6 @@ "functions-have-names": "^1.2.2" } }, - "functional-red-black-tree": { - "version": "1.0.1", - "resolved": "https://registry.npmjs.org/functional-red-black-tree/-/functional-red-black-tree-1.0.1.tgz", - "integrity": "sha512-dsKNQNdj6xA3T+QlADDA7mOSlX0qiMINjn0cgr+eGHGsbSHzTabcIogz2+p/iqP1Xs6EP/sS2SbqH+brGTbq0g==", - "dev": true - }, "functions-have-names": { "version": "1.2.3", "resolved": "https://registry.npmjs.org/functions-have-names/-/functions-have-names-1.2.3.tgz", @@ -11291,9 +10742,9 @@ "dev": true }, "generic-pool": { - "version": "3.8.2", - "resolved": "https://registry.npmjs.org/generic-pool/-/generic-pool-3.8.2.tgz", - "integrity": "sha512-nGToKy6p3PAbYQ7p1UlWl6vSPwfwU6TMSWK7TTu+WUY4ZjyZQGniGGt2oNVvyNSpyZYSB43zMXVLcBm08MTMkg==" + "version": "3.9.0", + "resolved": "https://registry.npmjs.org/generic-pool/-/generic-pool-3.9.0.tgz", + "integrity": "sha512-hymDOu5B53XvN4QT9dBmZxPX4CWhBPPLguTZ9MMFeFa/Kg0xWVfylOVNlJji/E7yTZWFd/q9GO5TxDLq156D7g==" }, "gensync": { "version": "1.0.0-beta.2", @@ -11308,9 +10759,9 @@ "dev": true }, "get-intrinsic": { - "version": "1.1.2", - "resolved": "https://registry.npmjs.org/get-intrinsic/-/get-intrinsic-1.1.2.tgz", - "integrity": "sha512-Jfm3OyCxHh9DJyc28qGk+JmfkpO41A4XkneDSujN9MDXrm4oDKdHvndhZ2dN94+ERNfkYJWDclW6k2L/ZGHjXA==", + "version": "1.1.3", + "resolved": "https://registry.npmjs.org/get-intrinsic/-/get-intrinsic-1.1.3.tgz", + "integrity": "sha512-QJVz1Tj7MS099PevUG5jvnt9tSkXN8K14dxQlikJuPt4uD9hHAHjLyLBiLR5zELelBdD9QNRAXZzsJx0WaDL9A==", "dev": true, "requires": { "function-bind": "^1.1.1", @@ -11375,37 +10826,61 @@ "find-cache-dir": "^3.3.1", "fs-extra": "^8.1.0", "globby": "^6.1.0" + }, + "dependencies": { + "array-union": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/array-union/-/array-union-1.0.2.tgz", + "integrity": "sha512-Dxr6QJj/RdU/hCaBjOfxW+q6lyuVE6JFWIrAUpuOOhoJJoQ99cUn3igRaHVB5P9WrgFVN0FfArM3x0cueOU8ng==", + "dev": true, + "requires": { + "array-uniq": "^1.0.1" + } + }, + "globby": { + "version": "6.1.0", + "resolved": "https://registry.npmjs.org/globby/-/globby-6.1.0.tgz", + "integrity": "sha512-KVbFv2TQtbzCoxAnfD6JcHZTYCzyliEaaeM/gH8qQdkKr5s0OP9scEgvdcngyk7AVdY6YVW/TJHd+lQ/Df3Daw==", + "dev": true, + "requires": { + "array-union": "^1.0.1", + "glob": "^7.0.3", + "object-assign": "^4.0.1", + "pify": "^2.0.0", + "pinkie-promise": "^2.0.0" + } + } } }, "git-up": { - "version": "6.0.0", - "resolved": "https://registry.npmjs.org/git-up/-/git-up-6.0.0.tgz", - "integrity": "sha512-6RUFSNd1c/D0xtGnyWN2sxza2bZtZ/EmI9448n6rCZruFwV/ezeEn2fJP7XnUQGwf0RAtd/mmUCbtH6JPYA2SA==", + "version": "7.0.0", + "resolved": "https://registry.npmjs.org/git-up/-/git-up-7.0.0.tgz", + "integrity": "sha512-ONdIrbBCFusq1Oy0sC71F5azx8bVkvtZtMJAsv+a6lz5YAmbNnLD6HAB4gptHZVLPR8S2/kVN6Gab7lryq5+lQ==", "dev": true, "requires": { "is-ssh": "^1.4.0", - "parse-url": "^7.0.2" + "parse-url": "^8.1.0" } }, "git-url-parse": { - "version": "12.0.0", - "resolved": "https://registry.npmjs.org/git-url-parse/-/git-url-parse-12.0.0.tgz", - "integrity": "sha512-I6LMWsxV87vysX1WfsoglXsXg6GjQRKq7+Dgiseo+h0skmp5Hp2rzmcEIRQot9CPA+uzU7x1x7jZdqvTFGnB+Q==", + "version": "13.1.0", + "resolved": "https://registry.npmjs.org/git-url-parse/-/git-url-parse-13.1.0.tgz", + "integrity": "sha512-5FvPJP/70WkIprlUZ33bm4UAaFdjcLkJLpWft1BeZKqwR0uhhNGoKwlUaPtVb4LxCSQ++erHapRak9kWGj+FCA==", "dev": true, "requires": { - "git-up": "^6.0.0" + "git-up": "^7.0.0" } }, "glob": { - "version": "7.2.3", - "resolved": "https://registry.npmjs.org/glob/-/glob-7.2.3.tgz", - "integrity": "sha512-nFR0zLpU2YCaRxwoCJvL6UvCH2JFyFVIvwTLsIf21AuHlMskA1hhTdk+LlYJtOlYt9v6dvszD2BGRqBL+iQK9Q==", + "version": "7.2.0", + "resolved": "https://registry.npmjs.org/glob/-/glob-7.2.0.tgz", + "integrity": "sha512-lmLf6gtyrPq8tTjSmrO94wBeQbFR3HbLHbuyD69wuyQkImp2hWqMGB47OX65FBkPffO641IP9jWa1z4ivqG26Q==", "dev": true, "requires": { "fs.realpath": "^1.0.0", "inflight": "^1.0.4", "inherits": "2", - "minimatch": "^3.1.1", + "minimatch": "^3.0.4", "once": "^1.3.0", "path-is-absolute": "^1.0.0" } @@ -11435,48 +10910,39 @@ "dev": true, "requires": { "type-fest": "^0.20.2" - }, - "dependencies": { - "type-fest": { - "version": "0.20.2", - "resolved": "https://registry.npmjs.org/type-fest/-/type-fest-0.20.2.tgz", - "integrity": "sha512-Ne+eE4r0/iWnpAxD852z3A+N0Bt5RN//NjJwRd2VFHEmrywxf5vsZlh4R6lixl6B+wz/8d+maTSAkN1FIkI3LQ==", - "dev": true - } } }, "globby": { - "version": "6.1.0", - "resolved": "https://registry.npmjs.org/globby/-/globby-6.1.0.tgz", - "integrity": "sha512-KVbFv2TQtbzCoxAnfD6JcHZTYCzyliEaaeM/gH8qQdkKr5s0OP9scEgvdcngyk7AVdY6YVW/TJHd+lQ/Df3Daw==", + "version": "11.1.0", + "resolved": "https://registry.npmjs.org/globby/-/globby-11.1.0.tgz", + "integrity": "sha512-jhIXaOzy1sb8IyocaruWSn1TjmnBVs8Ayhcy83rmxNJ8q2uWKCAj3CnJY+KpGSXCueAPc0i05kVvVKtP1t9S3g==", "dev": true, - "requires": { - "array-union": "^1.0.1", - "glob": "^7.0.3", - "object-assign": "^4.0.1", - "pify": "^2.0.0", - "pinkie-promise": "^2.0.0" + "requires": { + "array-union": "^2.1.0", + "dir-glob": "^3.0.1", + "fast-glob": "^3.2.9", + "ignore": "^5.2.0", + "merge2": "^1.4.1", + "slash": "^3.0.0" } }, "got": { - "version": "12.3.1", - "resolved": "https://registry.npmjs.org/got/-/got-12.3.1.tgz", - "integrity": "sha512-tS6+JMhBh4iXMSXF6KkIsRxmloPln31QHDlcb6Ec3bzxjjFJFr/8aXdpyuLmVc9I4i2HyBHYw1QU5K1ruUdpkw==", + "version": "12.5.1", + "resolved": "https://registry.npmjs.org/got/-/got-12.5.1.tgz", + "integrity": "sha512-sD16AK8cCyUoPtKr/NMvLTFFa+T3i3S+zoiuvhq0HP2YiqBZA9AtlBjAdsQBsLBK7slPuvmfE0OxhGi7N5dD4w==", "dev": true, "requires": { "@sindresorhus/is": "^5.2.0", "@szmarczak/http-timer": "^5.0.1", - "@types/cacheable-request": "^6.0.2", - "@types/responselike": "^1.0.0", - "cacheable-lookup": "^6.0.4", - "cacheable-request": "^7.0.2", + "cacheable-lookup": "^7.0.0", + "cacheable-request": "^10.2.1", "decompress-response": "^6.0.0", - "form-data-encoder": "^2.0.1", + "form-data-encoder": "^2.1.2", "get-stream": "^6.0.1", "http2-wrapper": "^2.1.10", "lowercase-keys": "^3.0.0", "p-cancelable": "^3.0.0", - "responselike": "^2.0.0" + "responselike": "^3.0.0" } }, "graceful-fs": { @@ -11552,12 +11018,6 @@ "type-fest": "^0.8.0" }, "dependencies": { - "is-stream": { - "version": "2.0.1", - "resolved": "https://registry.npmjs.org/is-stream/-/is-stream-2.0.1.tgz", - "integrity": "sha512-hFoiJiTl63nn+kstHGBtewWSKnQLpyb155KHheA1l39uvtO9nWIop1p3udqPcUd/xbF1VLMO4n7OI6p7RbngDg==", - "dev": true - }, "type-fest": { "version": "0.8.1", "resolved": "https://registry.npmjs.org/type-fest/-/type-fest-0.8.1.tgz", @@ -11706,9 +11166,9 @@ "dev": true }, "inquirer": { - "version": "9.1.0", - "resolved": "https://registry.npmjs.org/inquirer/-/inquirer-9.1.0.tgz", - "integrity": "sha512-eukdjrBljg9t55ZnvJjvGi1OyYEzVBFsO/8o5d2MV3mc28u3x4X2kS4eJ/+9U10KiREfPkEBSeCrU/S2G/uRtw==", + "version": "9.1.2", + "resolved": "https://registry.npmjs.org/inquirer/-/inquirer-9.1.2.tgz", + "integrity": "sha512-Hj2Ml1WpxKJU2npP2Rj0OURGkHV+GtNW2CwFdHDiXlqUBAUrWTcZHxCkFywX/XHzOS7wrG/kExgJFbUkVgyHzg==", "dev": true, "requires": { "ansi-escapes": "^5.0.0", @@ -11716,7 +11176,7 @@ "cli-cursor": "^4.0.0", "cli-width": "^4.0.0", "external-editor": "^3.0.3", - "figures": "^4.0.1", + "figures": "^5.0.0", "lodash": "^4.17.21", "mute-stream": "0.0.8", "ora": "^6.1.2", @@ -11726,6 +11186,29 @@ "strip-ansi": "^7.0.1", "through": "^2.3.6", "wrap-ansi": "^8.0.1" + }, + "dependencies": { + "ansi-regex": { + "version": "6.0.1", + "resolved": "https://registry.npmjs.org/ansi-regex/-/ansi-regex-6.0.1.tgz", + "integrity": "sha512-n5M855fKb2SsfMIiFFoVrABHJC8QtHwVx+mHWP3QcEqBHYienj5dHSgjbxtC0WEZXYt4wcD6zrQElDPhFuZgfA==", + "dev": true + }, + "chalk": { + "version": "5.1.2", + "resolved": "https://registry.npmjs.org/chalk/-/chalk-5.1.2.tgz", + "integrity": "sha512-E5CkT4jWURs1Vy5qGJye+XwCkNj7Od3Af7CP6SujMetSMkLs8Do2RWJK5yx1wamHV/op8Rz+9rltjaTQWDnEFQ==", + "dev": true + }, + "strip-ansi": { + "version": "7.0.1", + "resolved": "https://registry.npmjs.org/strip-ansi/-/strip-ansi-7.0.1.tgz", + "integrity": "sha512-cXNxvT8dFNRVfhVME3JAe98mkXDYN2O1l7jmcwMnOslDeESg1rF/OZMtK0nRAhiari1unG5cD4jG3rapUAkLbw==", + "dev": true, + "requires": { + "ansi-regex": "^6.0.1" + } + } } }, "internal-slot": { @@ -11796,9 +11279,9 @@ } }, "is-callable": { - "version": "1.2.4", - "resolved": "https://registry.npmjs.org/is-callable/-/is-callable-1.2.4.tgz", - "integrity": "sha512-nsuwtxZfMX67Oryl9LCQ+upnC0Z0BgpwntpS89m1H/TLF0zNfzfLMV/9Wa/6MZsj0acpEjAO0KF1xT6ZdLl95w==", + "version": "1.2.7", + "resolved": "https://registry.npmjs.org/is-callable/-/is-callable-1.2.7.tgz", + "integrity": "sha512-1BC0BVFhS/p0qtw6enp8e+8OD0UrK0oFLztSjNzhcKA3WDuJxxAPXzPuPtKkjEY9UUoEWlX/8fgKeu2S8i9JTA==", "dev": true }, "is-ci": { @@ -11811,9 +11294,9 @@ } }, "is-core-module": { - "version": "2.10.0", - "resolved": "https://registry.npmjs.org/is-core-module/-/is-core-module-2.10.0.tgz", - "integrity": "sha512-Erxj2n/LDAZ7H8WNJXd9tw38GYM3dv8rk8Zcs+jJuxYTW7sozH+SS8NtrSjVL1/vpLvWi1hxy96IzjJ3EHTJJg==", + "version": "2.11.0", + "resolved": "https://registry.npmjs.org/is-core-module/-/is-core-module-2.11.0.tgz", + "integrity": "sha512-RRjxlvLDkD1YJwDbroBHMb+cukurkDWNyHx7D3oNB5x9rb5ogcksMC5wHCadcXoo67gVr/+3GFySh3134zi6rw==", "dev": true, "requires": { "has": "^1.0.3" @@ -11963,9 +11446,9 @@ } }, "is-stream": { - "version": "3.0.0", - "resolved": "https://registry.npmjs.org/is-stream/-/is-stream-3.0.0.tgz", - "integrity": "sha512-LnQR4bZ9IADDRSkvpqMGvt/tEJWclzklNgSw48V5EAaAeDd6qGvN8ei6k5p0tvxSR171VmGyHuTiAOfxAbr8kA==", + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/is-stream/-/is-stream-2.0.1.tgz", + "integrity": "sha512-hFoiJiTl63nn+kstHGBtewWSKnQLpyb155KHheA1l39uvtO9nWIop1p3udqPcUd/xbF1VLMO4n7OI6p7RbngDg==", "dev": true }, "is-string": { @@ -11993,9 +11476,9 @@ "dev": true }, "is-unicode-supported": { - "version": "1.2.0", - "resolved": "https://registry.npmjs.org/is-unicode-supported/-/is-unicode-supported-1.2.0.tgz", - "integrity": "sha512-wH+U77omcRzevfIG8dDhTS0V9zZyweakfD01FULl97+0EHiJTTZtJqxPSkIIo/SDPv/i07k/C9jAPY+jwLLeUQ==", + "version": "0.1.0", + "resolved": "https://registry.npmjs.org/is-unicode-supported/-/is-unicode-supported-0.1.0.tgz", + "integrity": "sha512-knxG2q4UC3u8stRGyAVJCOdxFmv5DZiRcdlIaAQXAbSfJya+OhopNotLQrstBhququ4ZpuKbDc/8S6mgXgPFPw==", "dev": true }, "is-weakref": { @@ -12065,6 +11548,14 @@ "@istanbuljs/schema": "^0.1.2", "istanbul-lib-coverage": "^3.0.0", "semver": "^6.3.0" + }, + "dependencies": { + "semver": { + "version": "6.3.0", + "resolved": "https://registry.npmjs.org/semver/-/semver-6.3.0.tgz", + "integrity": "sha512-b39TBaTSfV6yBrapU89p5fKekE2m/NwnDocOVruQFS1/veMgdzuPcnOM34M6CwxW8jH/lxEa5rBoDeUwu5HHTw==", + "dev": true + } } }, "istanbul-lib-processinfo": { @@ -12129,6 +11620,12 @@ "iterate-iterator": "^1.0.1" } }, + "js-sdsl": { + "version": "4.1.5", + "resolved": "https://registry.npmjs.org/js-sdsl/-/js-sdsl-4.1.5.tgz", + "integrity": "sha512-08bOAKweV2NUC1wqTtf3qZlnpOX/R2DU9ikpjOHs0H+ibQv3zpncVQg6um4uYtRtrwIX8M4Nh3ytK4HGlYAq7Q==", + "dev": true + }, "js-tokens": { "version": "4.0.0", "resolved": "https://registry.npmjs.org/js-tokens/-/js-tokens-4.0.0.tgz", @@ -12181,9 +11678,9 @@ "dev": true }, "jsonc-parser": { - "version": "3.1.0", - "resolved": "https://registry.npmjs.org/jsonc-parser/-/jsonc-parser-3.1.0.tgz", - "integrity": "sha512-DRf0QjnNeCUds3xTjKlQQ3DpJD51GvDjJfnxUVWg6PZTo2otSm+slzNAxU/35hF8/oJIKoG9slq30JYOsF2azg==", + "version": "3.2.0", + "resolved": "https://registry.npmjs.org/jsonc-parser/-/jsonc-parser-3.2.0.tgz", + "integrity": "sha512-gfFQZrcTc8CnKXp6Y4/CBT3fTc0OVuDofpre4aEeEpSBPV5X5v4+Vmx+8snU7RLPrNHPKSgLxGo9YuQzz20o+w==", "dev": true }, "jsonfile": { @@ -12202,12 +11699,11 @@ "dev": true }, "keyv": { - "version": "4.4.1", - "resolved": "https://registry.npmjs.org/keyv/-/keyv-4.4.1.tgz", - "integrity": "sha512-PzByhNxfBLnSBW2MZi1DF+W5+qB/7BMpOokewqIvqS8GFtP7xHm2oeGU72Y1fhtfOv/FiEnI4+nyViYDmUChnw==", + "version": "4.5.0", + "resolved": "https://registry.npmjs.org/keyv/-/keyv-4.5.0.tgz", + "integrity": "sha512-2YvuMsA+jnFGtBareKqgANOEKe1mk3HKiXu2fRmAfyxG0MJAywNhi5ttWA3PMjl4NmpyjZNbFifR2vNjW1znfA==", "dev": true, "requires": { - "compress-brotli": "^1.3.8", "json-buffer": "3.0.1" } }, @@ -12237,12 +11733,12 @@ "dev": true }, "locate-path": { - "version": "5.0.0", - "resolved": "https://registry.npmjs.org/locate-path/-/locate-path-5.0.0.tgz", - "integrity": "sha512-t7hw9pI+WvuwNJXwk5zVHpyhIqzg2qTlklJOf0mVxGSbe3Fp2VieZcduNYjaLDoy6p9uGpQEGWG87WpMKlNq8g==", + "version": "6.0.0", + "resolved": "https://registry.npmjs.org/locate-path/-/locate-path-6.0.0.tgz", + "integrity": "sha512-iPZK6eYjbxRu3uB4/WZ3EsEIMJFMqAoopl3R+zuq0UjcAm/MO6KCweDgPfP3elTztoKP3KtnVHxTn2NHBSDVUw==", "dev": true, "requires": { - "p-locate": "^4.1.0" + "p-locate": "^5.0.0" } }, "lodash": { @@ -12270,13 +11766,13 @@ "dev": true }, "log-symbols": { - "version": "5.1.0", - "resolved": "https://registry.npmjs.org/log-symbols/-/log-symbols-5.1.0.tgz", - "integrity": "sha512-l0x2DvrW294C9uDCoQe1VSU4gf529FkSZ6leBl4TiqZH/e+0R7hSfHQBNut2mNygDgHwvYHfFLn6Oxb3VWj2rA==", + "version": "4.1.0", + "resolved": "https://registry.npmjs.org/log-symbols/-/log-symbols-4.1.0.tgz", + "integrity": "sha512-8XPvpAA8uyhfteu8pIvQxpJZ7SYYdpUivZpGy6sFsBuKRY/7rQGavedeB8aK+Zkyq6upMFVL/9AW6vOYzfRyLg==", "dev": true, "requires": { - "chalk": "^5.0.0", - "is-unicode-supported": "^1.1.0" + "chalk": "^4.1.0", + "is-unicode-supported": "^0.1.0" } }, "lowercase-keys": { @@ -12313,6 +11809,14 @@ "dev": true, "requires": { "semver": "^6.0.0" + }, + "dependencies": { + "semver": { + "version": "6.3.0", + "resolved": "https://registry.npmjs.org/semver/-/semver-6.3.0.tgz", + "integrity": "sha512-b39TBaTSfV6yBrapU89p5fKekE2m/NwnDocOVruQFS1/veMgdzuPcnOM34M6CwxW8jH/lxEa5rBoDeUwu5HHTw==", + "dev": true + } } }, "make-error": { @@ -12322,9 +11826,9 @@ "dev": true }, "marked": { - "version": "4.0.19", - "resolved": "https://registry.npmjs.org/marked/-/marked-4.0.19.tgz", - "integrity": "sha512-rgQF/OxOiLcvgUAj1Q1tAf4Bgxn5h5JZTp04Fx4XUkVhs7B+7YA9JEWJhJpoO8eJt8MkZMwqLCNeNqj1bCREZQ==", + "version": "4.1.1", + "resolved": "https://registry.npmjs.org/marked/-/marked-4.1.1.tgz", + "integrity": "sha512-0cNMnTcUJPxbA6uWmCmjWz4NJRe/0Xfk2NhXCUHjew9qJzFN20krFnsUe7QynwqOwa5m1fZ4UDg0ycKFVC0ccw==", "dev": true }, "merge-stream": { @@ -12371,9 +11875,9 @@ "dev": true }, "mimic-response": { - "version": "1.0.1", - "resolved": "https://registry.npmjs.org/mimic-response/-/mimic-response-1.0.1.tgz", - "integrity": "sha512-j5EctnkH7amfV/q5Hgmoal1g2QHFJRraOtmx0JpIqkxhBhI/lJSl1nMpQ45hVarwNETOoWEimndZ4QK0RHxuxQ==", + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/mimic-response/-/mimic-response-4.0.0.tgz", + "integrity": "sha512-e5ISH9xMYU0DzrT+jl8q2ze9D6eWBto+I8CNpe+VI+K2J/F/k3PdkdTdz4wvGVH4NTpo+NRYTVIuMQEMMcsLqg==", "dev": true }, "minimatch": { @@ -12386,18 +11890,17 @@ } }, "minimist": { - "version": "1.2.6", - "resolved": "https://registry.npmjs.org/minimist/-/minimist-1.2.6.tgz", - "integrity": "sha512-Jsjnk4bw3YJqYzbdyBiNsPWHPfO++UGG749Cxs6peCu5Xg4nrena6OVxOYxrQTqww0Jmwt+Ref8rggumkTLz9Q==", + "version": "1.2.7", + "resolved": "https://registry.npmjs.org/minimist/-/minimist-1.2.7.tgz", + "integrity": "sha512-bzfL1YUZsP41gmu/qjrEk0Q6i2ix/cVeAhbCbqH9u3zYutS1cLg00qhrD0M2MVdCcx4Sc0UpP2eBWo9rotpq6g==", "dev": true }, "mocha": { - "version": "10.0.0", - "resolved": "https://registry.npmjs.org/mocha/-/mocha-10.0.0.tgz", - "integrity": "sha512-0Wl+elVUD43Y0BqPZBzZt8Tnkw9CMUdNYnUsTfOM1vuhJVZL+kiesFYsqwBkEEuEixaiPe5ZQdqDgX2jddhmoA==", + "version": "10.1.0", + "resolved": "https://registry.npmjs.org/mocha/-/mocha-10.1.0.tgz", + "integrity": "sha512-vUF7IYxEoN7XhQpFLxQAEMtE4W91acW4B6En9l97MwE9stL1A9gusXfoHZCLVHDUJ/7V5+lbCM6yMqzo5vNymg==", "dev": true, "requires": { - "@ungap/promise-all-settled": "1.1.2", "ansi-colors": "4.1.1", "browser-stdout": "1.3.1", "chokidar": "3.5.3", @@ -12421,40 +11924,13 @@ "yargs-unparser": "2.0.0" }, "dependencies": { - "ansi-regex": { - "version": "5.0.1", - "resolved": "https://registry.npmjs.org/ansi-regex/-/ansi-regex-5.0.1.tgz", - "integrity": "sha512-quJQXlTSUGL2LH9SUXo8VwsY4soanhgo6LNSm84E1LBcE8s3O0wpdiRzyR9z/ZZJMlMWv37qOOb9pdJlMUEKFQ==", - "dev": true - }, - "ansi-styles": { - "version": "4.3.0", - "resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-4.3.0.tgz", - "integrity": "sha512-zbB9rCJAT1rbjiVDb2hqKFHNYLxgtk8NURxZ3IZwD3F6NtxbXZQCnnSi1Lkx+IDohdPlFp222wVALIheZJQSEg==", - "dev": true, - "requires": { - "color-convert": "^2.0.1" - } - }, - "chalk": { - "version": "4.1.2", - "resolved": "https://registry.npmjs.org/chalk/-/chalk-4.1.2.tgz", - "integrity": "sha512-oKnbhFyRIXpUuez8iBMmyEa4nbj4IOQyuhc/wy9kY7/WVPcwIO9VA668Pu8RkO7+0G76SLROeyw9CpQ061i4mA==", + "brace-expansion": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/brace-expansion/-/brace-expansion-2.0.1.tgz", + "integrity": "sha512-XnAIvQ8eM+kC6aULx6wuQiwVsnzsi9d3WxzV3FpWTGA19F621kwdbsAcFKXgKUHZWsy+mY6iL1sHTxWEFCytDA==", "dev": true, "requires": { - "ansi-styles": "^4.1.0", - "supports-color": "^7.1.0" - }, - "dependencies": { - "supports-color": { - "version": "7.2.0", - "resolved": "https://registry.npmjs.org/supports-color/-/supports-color-7.2.0.tgz", - "integrity": "sha512-qpCAvRl9stuOHveKsn7HncJRvv501qIacKzQlO/+Lwxc9+0q2wLyv4Dfvt80/DPn2pqOBsJdDiogXGR9+OvwRw==", - "dev": true, - "requires": { - "has-flag": "^4.0.0" - } - } + "balanced-match": "^1.0.0" } }, "cliui": { @@ -12474,72 +11950,6 @@ "integrity": "sha512-MSjYzcWNOA0ewAHpz0MxpYFvwg6yjy1NG3xteoqz644VCo/RPgnr1/GGt+ic3iJTzQ8Eu3TdM14SawnVUmGE6A==", "dev": true }, - "escape-string-regexp": { - "version": "4.0.0", - "resolved": "https://registry.npmjs.org/escape-string-regexp/-/escape-string-regexp-4.0.0.tgz", - "integrity": "sha512-TtpcNJ3XAzx3Gq8sWRzJaVajRs0uVxA2YAkdb1jm2YkPz4G6egUFAyA3n5vtEIZefPk5Wa4UXbKuS5fKkJWdgA==", - "dev": true - }, - "find-up": { - "version": "5.0.0", - "resolved": "https://registry.npmjs.org/find-up/-/find-up-5.0.0.tgz", - "integrity": "sha512-78/PXT1wlLLDgTzDs7sjq9hzz0vXD+zn+7wypEe4fXQxCmdmqfGsEPQxmiCSQI3ajFV91bVSsvNtrJRiW6nGng==", - "dev": true, - "requires": { - "locate-path": "^6.0.0", - "path-exists": "^4.0.0" - } - }, - "glob": { - "version": "7.2.0", - "resolved": "https://registry.npmjs.org/glob/-/glob-7.2.0.tgz", - "integrity": "sha512-lmLf6gtyrPq8tTjSmrO94wBeQbFR3HbLHbuyD69wuyQkImp2hWqMGB47OX65FBkPffO641IP9jWa1z4ivqG26Q==", - "dev": true, - "requires": { - "fs.realpath": "^1.0.0", - "inflight": "^1.0.4", - "inherits": "2", - "minimatch": "^3.0.4", - "once": "^1.3.0", - "path-is-absolute": "^1.0.0" - }, - "dependencies": { - "minimatch": { - "version": "3.1.2", - "resolved": "https://registry.npmjs.org/minimatch/-/minimatch-3.1.2.tgz", - "integrity": "sha512-J7p63hRiAjw1NDEww1W7i37+ByIrOWO5XQQAzZ3VOcL0PNybwpfmV/N05zFAzwQ9USyEcX6t3UO+K5aqBQOIHw==", - "dev": true, - "requires": { - "brace-expansion": "^1.1.7" - } - } - } - }, - "is-unicode-supported": { - "version": "0.1.0", - "resolved": "https://registry.npmjs.org/is-unicode-supported/-/is-unicode-supported-0.1.0.tgz", - "integrity": "sha512-knxG2q4UC3u8stRGyAVJCOdxFmv5DZiRcdlIaAQXAbSfJya+OhopNotLQrstBhququ4ZpuKbDc/8S6mgXgPFPw==", - "dev": true - }, - "locate-path": { - "version": "6.0.0", - "resolved": "https://registry.npmjs.org/locate-path/-/locate-path-6.0.0.tgz", - "integrity": "sha512-iPZK6eYjbxRu3uB4/WZ3EsEIMJFMqAoopl3R+zuq0UjcAm/MO6KCweDgPfP3elTztoKP3KtnVHxTn2NHBSDVUw==", - "dev": true, - "requires": { - "p-locate": "^5.0.0" - } - }, - "log-symbols": { - "version": "4.1.0", - "resolved": "https://registry.npmjs.org/log-symbols/-/log-symbols-4.1.0.tgz", - "integrity": "sha512-8XPvpAA8uyhfteu8pIvQxpJZ7SYYdpUivZpGy6sFsBuKRY/7rQGavedeB8aK+Zkyq6upMFVL/9AW6vOYzfRyLg==", - "dev": true, - "requires": { - "chalk": "^4.1.0", - "is-unicode-supported": "^0.1.0" - } - }, "minimatch": { "version": "5.0.1", "resolved": "https://registry.npmjs.org/minimatch/-/minimatch-5.0.1.tgz", @@ -12547,17 +11957,6 @@ "dev": true, "requires": { "brace-expansion": "^2.0.1" - }, - "dependencies": { - "brace-expansion": { - "version": "2.0.1", - "resolved": "https://registry.npmjs.org/brace-expansion/-/brace-expansion-2.0.1.tgz", - "integrity": "sha512-XnAIvQ8eM+kC6aULx6wuQiwVsnzsi9d3WxzV3FpWTGA19F621kwdbsAcFKXgKUHZWsy+mY6iL1sHTxWEFCytDA==", - "dev": true, - "requires": { - "balanced-match": "^1.0.0" - } - } } }, "ms": { @@ -12566,24 +11965,6 @@ "integrity": "sha512-6FlzubTLZG3J2a/NVCAleEhjzq5oxgHyaCU9yYXvcLsvoVaHJq/s5xXI6/XXP6tz7R9xAOtHnSO/tXtF3WRTlA==", "dev": true }, - "p-limit": { - "version": "3.1.0", - "resolved": "https://registry.npmjs.org/p-limit/-/p-limit-3.1.0.tgz", - "integrity": "sha512-TYOanM3wGwNGsZN2cVTYPArw454xnXj5qmWF1bEoAc4+cU/ol7GVh7odevjp1FNHduHc3KZMcFduxU5Xc6uJRQ==", - "dev": true, - "requires": { - "yocto-queue": "^0.1.0" - } - }, - "p-locate": { - "version": "5.0.0", - "resolved": "https://registry.npmjs.org/p-locate/-/p-locate-5.0.0.tgz", - "integrity": "sha512-LaNjtRWUBY++zB5nE/NwcaoMylSPk+S+ZHNB1TzdbMJMny6dynpAGt7X/tl/QYq3TIeE6nxHppbo2LGymrG5Pw==", - "dev": true, - "requires": { - "p-limit": "^3.0.2" - } - }, "string-width": { "version": "4.2.3", "resolved": "https://registry.npmjs.org/string-width/-/string-width-4.2.3.tgz", @@ -12595,15 +11976,6 @@ "strip-ansi": "^6.0.1" } }, - "strip-ansi": { - "version": "6.0.1", - "resolved": "https://registry.npmjs.org/strip-ansi/-/strip-ansi-6.0.1.tgz", - "integrity": "sha512-Y38VPSHcqkFrCpFnQ9vuSXmquuv5oXOKpGeT6aGrr3o3Gc9AlVa6JBfUSOCnbxGGZF+/0ooI7KrPuUSztUdU5A==", - "dev": true, - "requires": { - "ansi-regex": "^5.0.1" - } - }, "supports-color": { "version": "8.1.1", "resolved": "https://registry.npmjs.org/supports-color/-/supports-color-8.1.1.tgz", @@ -12624,12 +11996,6 @@ "strip-ansi": "^6.0.0" } }, - "y18n": { - "version": "5.0.8", - "resolved": "https://registry.npmjs.org/y18n/-/y18n-5.0.8.tgz", - "integrity": "sha512-0pfFzegeDWJHJIAmTLRP2DwHjdF5s7jo9tuztdQxAhINCdvS+3nGINqPd00AphqJR/0LhANUS6/+7SCb98YOfA==", - "dev": true - }, "yargs": { "version": "16.2.0", "resolved": "https://registry.npmjs.org/yargs/-/yargs-16.2.0.tgz", @@ -12644,12 +12010,6 @@ "y18n": "^5.0.5", "yargs-parser": "^20.2.2" } - }, - "yargs-parser": { - "version": "20.2.4", - "resolved": "https://registry.npmjs.org/yargs-parser/-/yargs-parser-20.2.4.tgz", - "integrity": "sha512-WOkpgNhPTlE73h4VFAFsOnomJVaovO8VqLDzy5saChRBFQFBoMYirowyW+Q9HB4HFF4Z7VZTiG3iSzJJA29yRA==", - "dev": true } } }, @@ -12752,9 +12112,9 @@ "dev": true }, "normalize-url": { - "version": "6.1.0", - "resolved": "https://registry.npmjs.org/normalize-url/-/normalize-url-6.1.0.tgz", - "integrity": "sha512-DlL+XwOy3NxAQ8xuC0okPgK46iuVNAK01YN7RueYBqqFeGsBjV9XmCAzAdgt+667bCl5kPh9EqKKDwnaPG1I7A==", + "version": "7.2.0", + "resolved": "https://registry.npmjs.org/normalize-url/-/normalize-url-7.2.0.tgz", + "integrity": "sha512-uhXOdZry0L6M2UIo9BTt7FdpBDiAGN/7oItedQwPKh8jh31ZlvC8U9Xl/EJ3aijDHaywXTW3QbZ6LuCocur1YA==", "dev": true }, "npm-run-path": { @@ -12809,11 +12169,122 @@ "yargs": "^15.0.2" }, "dependencies": { + "cliui": { + "version": "6.0.0", + "resolved": "https://registry.npmjs.org/cliui/-/cliui-6.0.0.tgz", + "integrity": "sha512-t6wbgtoCXvAzst7QgXxJYqPt0usEfbgQdftEPbLL/cvv6HPE5VgvqCuAIDR0NgU52ds6rFwqrgakNLrHEjCbrQ==", + "dev": true, + "requires": { + "string-width": "^4.2.0", + "strip-ansi": "^6.0.0", + "wrap-ansi": "^6.2.0" + } + }, + "emoji-regex": { + "version": "8.0.0", + "resolved": "https://registry.npmjs.org/emoji-regex/-/emoji-regex-8.0.0.tgz", + "integrity": "sha512-MSjYzcWNOA0ewAHpz0MxpYFvwg6yjy1NG3xteoqz644VCo/RPgnr1/GGt+ic3iJTzQ8Eu3TdM14SawnVUmGE6A==", + "dev": true + }, + "find-up": { + "version": "4.1.0", + "resolved": "https://registry.npmjs.org/find-up/-/find-up-4.1.0.tgz", + "integrity": "sha512-PpOwAdQ/YlXQ2vj8a3h8IipDuYRi3wceVQQGYWxNINccq40Anw7BlsEXCMbt1Zt+OLA6Fq9suIpIWD0OsnISlw==", + "dev": true, + "requires": { + "locate-path": "^5.0.0", + "path-exists": "^4.0.0" + } + }, + "locate-path": { + "version": "5.0.0", + "resolved": "https://registry.npmjs.org/locate-path/-/locate-path-5.0.0.tgz", + "integrity": "sha512-t7hw9pI+WvuwNJXwk5zVHpyhIqzg2qTlklJOf0mVxGSbe3Fp2VieZcduNYjaLDoy6p9uGpQEGWG87WpMKlNq8g==", + "dev": true, + "requires": { + "p-locate": "^4.1.0" + } + }, + "p-limit": { + "version": "2.3.0", + "resolved": "https://registry.npmjs.org/p-limit/-/p-limit-2.3.0.tgz", + "integrity": "sha512-//88mFWSJx8lxCzwdAABTJL2MyWB12+eIY7MDL2SqLmAkeKU9qxRvWuSyTjm3FUmpBEMuFfckAIqEaVGUDxb6w==", + "dev": true, + "requires": { + "p-try": "^2.0.0" + } + }, + "p-locate": { + "version": "4.1.0", + "resolved": "https://registry.npmjs.org/p-locate/-/p-locate-4.1.0.tgz", + "integrity": "sha512-R79ZZ/0wAxKGu3oYMlz8jy/kbhsNrS7SKZ7PxEHBgJ5+F2mtFW2fK2cOtBh1cHYkQsbzFV7I+EoRKe6Yt0oK7A==", + "dev": true, + "requires": { + "p-limit": "^2.2.0" + } + }, "resolve-from": { "version": "5.0.0", "resolved": "https://registry.npmjs.org/resolve-from/-/resolve-from-5.0.0.tgz", "integrity": "sha512-qYg9KP24dD5qka9J47d0aVky0N+b4fTU89LN9iDnjB5waksiC49rvMB0PrUJQGoTmH50XPiqOvAjDfaijGxYZw==", "dev": true + }, + "string-width": { + "version": "4.2.3", + "resolved": "https://registry.npmjs.org/string-width/-/string-width-4.2.3.tgz", + "integrity": "sha512-wKyQRQpjJ0sIp62ErSZdGsjMJWsap5oRNihHhu6G7JVO/9jIB6UyevL+tXuOqrng8j/cxKTWyWUwvSTriiZz/g==", + "dev": true, + "requires": { + "emoji-regex": "^8.0.0", + "is-fullwidth-code-point": "^3.0.0", + "strip-ansi": "^6.0.1" + } + }, + "wrap-ansi": { + "version": "6.2.0", + "resolved": "https://registry.npmjs.org/wrap-ansi/-/wrap-ansi-6.2.0.tgz", + "integrity": "sha512-r6lPcBGxZXlIcymEu7InxDMhdW0KDxpLgoFLcguasxCaJ/SOIZwINatK9KY/tf+ZrlywOKU0UDj3ATXUBfxJXA==", + "dev": true, + "requires": { + "ansi-styles": "^4.0.0", + "string-width": "^4.1.0", + "strip-ansi": "^6.0.0" + } + }, + "y18n": { + "version": "4.0.3", + "resolved": "https://registry.npmjs.org/y18n/-/y18n-4.0.3.tgz", + "integrity": "sha512-JKhqTOwSrqNA1NY5lSztJ1GrBiUodLMmIZuLiDaMRJ+itFd+ABVE8XBjOvIWL+rSqNDC74LCSFmlb/U4UZ4hJQ==", + "dev": true + }, + "yargs": { + "version": "15.4.1", + "resolved": "https://registry.npmjs.org/yargs/-/yargs-15.4.1.tgz", + "integrity": "sha512-aePbxDmcYW++PaqBsJ+HYUFwCdv4LVvdnhBy78E57PIor8/OVvhMrADFFEDh8DHDFRv/O9i3lPhsENjO7QX0+A==", + "dev": true, + "requires": { + "cliui": "^6.0.0", + "decamelize": "^1.2.0", + "find-up": "^4.1.0", + "get-caller-file": "^2.0.1", + "require-directory": "^2.1.1", + "require-main-filename": "^2.0.0", + "set-blocking": "^2.0.0", + "string-width": "^4.2.0", + "which-module": "^2.0.0", + "y18n": "^4.0.0", + "yargs-parser": "^18.1.2" + } + }, + "yargs-parser": { + "version": "18.1.3", + "resolved": "https://registry.npmjs.org/yargs-parser/-/yargs-parser-18.1.3.tgz", + "integrity": "sha512-o50j0JeToy/4K6OZcaQmW6lyXXKhq7csREXcDwk2omFPJEwUNOVtJKvmDr9EI1fAJZUyZcRF7kxGBWmRXudrCQ==", + "dev": true, + "requires": { + "camelcase": "^5.0.0", + "decamelize": "^1.2.0" + } } } }, @@ -12905,6 +12376,45 @@ "log-symbols": "^5.1.0", "strip-ansi": "^7.0.1", "wcwidth": "^1.0.1" + }, + "dependencies": { + "ansi-regex": { + "version": "6.0.1", + "resolved": "https://registry.npmjs.org/ansi-regex/-/ansi-regex-6.0.1.tgz", + "integrity": "sha512-n5M855fKb2SsfMIiFFoVrABHJC8QtHwVx+mHWP3QcEqBHYienj5dHSgjbxtC0WEZXYt4wcD6zrQElDPhFuZgfA==", + "dev": true + }, + "chalk": { + "version": "5.1.2", + "resolved": "https://registry.npmjs.org/chalk/-/chalk-5.1.2.tgz", + "integrity": "sha512-E5CkT4jWURs1Vy5qGJye+XwCkNj7Od3Af7CP6SujMetSMkLs8Do2RWJK5yx1wamHV/op8Rz+9rltjaTQWDnEFQ==", + "dev": true + }, + "is-unicode-supported": { + "version": "1.3.0", + "resolved": "https://registry.npmjs.org/is-unicode-supported/-/is-unicode-supported-1.3.0.tgz", + "integrity": "sha512-43r2mRvz+8JRIKnWJ+3j8JtjRKZ6GmjzfaE/qiBJnikNnYv/6bagRJ1kUhNk8R5EX/GkobD+r+sfxCPJsiKBLQ==", + "dev": true + }, + "log-symbols": { + "version": "5.1.0", + "resolved": "https://registry.npmjs.org/log-symbols/-/log-symbols-5.1.0.tgz", + "integrity": "sha512-l0x2DvrW294C9uDCoQe1VSU4gf529FkSZ6leBl4TiqZH/e+0R7hSfHQBNut2mNygDgHwvYHfFLn6Oxb3VWj2rA==", + "dev": true, + "requires": { + "chalk": "^5.0.0", + "is-unicode-supported": "^1.1.0" + } + }, + "strip-ansi": { + "version": "7.0.1", + "resolved": "https://registry.npmjs.org/strip-ansi/-/strip-ansi-7.0.1.tgz", + "integrity": "sha512-cXNxvT8dFNRVfhVME3JAe98mkXDYN2O1l7jmcwMnOslDeESg1rF/OZMtK0nRAhiari1unG5cD4jG3rapUAkLbw==", + "dev": true, + "requires": { + "ansi-regex": "^6.0.1" + } + } } }, "os-name": { @@ -12930,21 +12440,21 @@ "dev": true }, "p-limit": { - "version": "2.3.0", - "resolved": "https://registry.npmjs.org/p-limit/-/p-limit-2.3.0.tgz", - "integrity": "sha512-//88mFWSJx8lxCzwdAABTJL2MyWB12+eIY7MDL2SqLmAkeKU9qxRvWuSyTjm3FUmpBEMuFfckAIqEaVGUDxb6w==", + "version": "3.1.0", + "resolved": "https://registry.npmjs.org/p-limit/-/p-limit-3.1.0.tgz", + "integrity": "sha512-TYOanM3wGwNGsZN2cVTYPArw454xnXj5qmWF1bEoAc4+cU/ol7GVh7odevjp1FNHduHc3KZMcFduxU5Xc6uJRQ==", "dev": true, "requires": { - "p-try": "^2.0.0" + "yocto-queue": "^0.1.0" } }, "p-locate": { - "version": "4.1.0", - "resolved": "https://registry.npmjs.org/p-locate/-/p-locate-4.1.0.tgz", - "integrity": "sha512-R79ZZ/0wAxKGu3oYMlz8jy/kbhsNrS7SKZ7PxEHBgJ5+F2mtFW2fK2cOtBh1cHYkQsbzFV7I+EoRKe6Yt0oK7A==", + "version": "5.0.0", + "resolved": "https://registry.npmjs.org/p-locate/-/p-locate-5.0.0.tgz", + "integrity": "sha512-LaNjtRWUBY++zB5nE/NwcaoMylSPk+S+ZHNB1TzdbMJMny6dynpAGt7X/tl/QYq3TIeE6nxHppbo2LGymrG5Pw==", "dev": true, "requires": { - "p-limit": "^2.2.0" + "p-limit": "^3.0.2" } }, "p-map": { @@ -13012,32 +12522,6 @@ "registry-auth-token": "^5.0.1", "registry-url": "^6.0.0", "semver": "^7.3.7" - }, - "dependencies": { - "lru-cache": { - "version": "6.0.0", - "resolved": "https://registry.npmjs.org/lru-cache/-/lru-cache-6.0.0.tgz", - "integrity": "sha512-Jo6dJ04CmSjuznwJSS3pUeWmd/H0ffTlkXXgwZi+eq1UCmqQwCh+eLsYOYCwY991i2Fah4h1BEMCx4qThGbsiA==", - "dev": true, - "requires": { - "yallist": "^4.0.0" - } - }, - "semver": { - "version": "7.3.7", - "resolved": "https://registry.npmjs.org/semver/-/semver-7.3.7.tgz", - "integrity": "sha512-QlYTucUYOews+WeEujDoEGziz4K6c47V/Bd+LjSSYcA94p+DmINdf7ncaUinThfvZyu13lN9OY1XDxt8C0Tw0g==", - "dev": true, - "requires": { - "lru-cache": "^6.0.0" - } - }, - "yallist": { - "version": "4.0.0", - "resolved": "https://registry.npmjs.org/yallist/-/yallist-4.0.0.tgz", - "integrity": "sha512-3wdGidZyq5PB084XLES5TpOSRA3wjXAlIWMhum2kRcv/41Sn2emQ0dycQW4uZXLejwKvg6EsvbdlVL+FYEct7A==", - "dev": true - } } }, "parent-module": { @@ -13062,24 +12546,21 @@ } }, "parse-path": { - "version": "5.0.0", - "resolved": "https://registry.npmjs.org/parse-path/-/parse-path-5.0.0.tgz", - "integrity": "sha512-qOpH55/+ZJ4jUu/oLO+ifUKjFPNZGfnPJtzvGzKN/4oLMil5m9OH4VpOj6++9/ytJcfks4kzH2hhi87GL/OU9A==", + "version": "7.0.0", + "resolved": "https://registry.npmjs.org/parse-path/-/parse-path-7.0.0.tgz", + "integrity": "sha512-Euf9GG8WT9CdqwuWJGdf3RkUcTBArppHABkO7Lm8IzRQp0e2r/kkFnmhu4TSK30Wcu5rVAZLmfPKSBBi9tWFog==", "dev": true, "requires": { "protocols": "^2.0.0" } }, "parse-url": { - "version": "7.0.2", - "resolved": "https://registry.npmjs.org/parse-url/-/parse-url-7.0.2.tgz", - "integrity": "sha512-PqO4Z0eCiQ08Wj6QQmrmp5YTTxpYfONdOEamrtvK63AmzXpcavIVQubGHxOEwiIoDZFb8uDOoQFS0NCcjqIYQg==", + "version": "8.1.0", + "resolved": "https://registry.npmjs.org/parse-url/-/parse-url-8.1.0.tgz", + "integrity": "sha512-xDvOoLU5XRrcOZvnI6b8zA6n9O9ejNk/GExuz1yBuWUGn9KA97GI6HTs6u02wKara1CeVmZhH+0TZFdWScR89w==", "dev": true, "requires": { - "is-ssh": "^1.4.0", - "normalize-url": "^6.1.0", - "parse-path": "^5.0.0", - "protocols": "^2.0.1" + "parse-path": "^7.0.0" } }, "path-exists": { @@ -13169,6 +12650,45 @@ "dev": true, "requires": { "find-up": "^4.0.0" + }, + "dependencies": { + "find-up": { + "version": "4.1.0", + "resolved": "https://registry.npmjs.org/find-up/-/find-up-4.1.0.tgz", + "integrity": "sha512-PpOwAdQ/YlXQ2vj8a3h8IipDuYRi3wceVQQGYWxNINccq40Anw7BlsEXCMbt1Zt+OLA6Fq9suIpIWD0OsnISlw==", + "dev": true, + "requires": { + "locate-path": "^5.0.0", + "path-exists": "^4.0.0" + } + }, + "locate-path": { + "version": "5.0.0", + "resolved": "https://registry.npmjs.org/locate-path/-/locate-path-5.0.0.tgz", + "integrity": "sha512-t7hw9pI+WvuwNJXwk5zVHpyhIqzg2qTlklJOf0mVxGSbe3Fp2VieZcduNYjaLDoy6p9uGpQEGWG87WpMKlNq8g==", + "dev": true, + "requires": { + "p-locate": "^4.1.0" + } + }, + "p-limit": { + "version": "2.3.0", + "resolved": "https://registry.npmjs.org/p-limit/-/p-limit-2.3.0.tgz", + "integrity": "sha512-//88mFWSJx8lxCzwdAABTJL2MyWB12+eIY7MDL2SqLmAkeKU9qxRvWuSyTjm3FUmpBEMuFfckAIqEaVGUDxb6w==", + "dev": true, + "requires": { + "p-try": "^2.0.0" + } + }, + "p-locate": { + "version": "4.1.0", + "resolved": "https://registry.npmjs.org/p-locate/-/p-locate-4.1.0.tgz", + "integrity": "sha512-R79ZZ/0wAxKGu3oYMlz8jy/kbhsNrS7SKZ7PxEHBgJ5+F2mtFW2fK2cOtBh1cHYkQsbzFV7I+EoRKe6Yt0oK7A==", + "dev": true, + "requires": { + "p-limit": "^2.2.0" + } + } } }, "prelude-ls": { @@ -13234,16 +12754,6 @@ "integrity": "sha512-D+zkORCbA9f1tdWRK0RaCR3GPv50cMxcrz4X8k5LTSUD1Dkw47mKJEZQNunItRTkWwgtaUSo1RVFRIG9ZXiFYg==", "dev": true }, - "pump": { - "version": "3.0.0", - "resolved": "https://registry.npmjs.org/pump/-/pump-3.0.0.tgz", - "integrity": "sha512-LwZy+p3SFs1Pytd/jYct4wpv49HiYCqd9Rlc5ZVdk0V+8Yzv6jR5Blk3TRmPL1ft69TxP0IMZGJ+WPFU2BFhww==", - "dev": true, - "requires": { - "end-of-stream": "^1.1.0", - "once": "^1.3.1" - } - }, "punycode": { "version": "2.1.1", "resolved": "https://registry.npmjs.org/punycode/-/punycode-2.1.1.tgz", @@ -13383,22 +12893,22 @@ } }, "release-it": { - "version": "15.3.0", - "resolved": "https://registry.npmjs.org/release-it/-/release-it-15.3.0.tgz", - "integrity": "sha512-MI4EBGca+y4SskgBkWNIakFp/GvXfpZEMWkmqmsysPcou/L+E+sKd0oy33ovGCyLic+9SI2rv/lQ3ACgonmqdQ==", + "version": "15.5.0", + "resolved": "https://registry.npmjs.org/release-it/-/release-it-15.5.0.tgz", + "integrity": "sha512-/pQo/PwEXAWRBgVGLE+3IQ3hUoeiDZMGAo/Egin1envCyLyjzrU7+0P2w4iZ1Xv5OxhC2AcaPaN5eY1ql47cBA==", "dev": true, "requires": { "@iarna/toml": "2.2.5", - "@octokit/rest": "19.0.3", + "@octokit/rest": "19.0.4", "async-retry": "1.3.3", "chalk": "5.0.1", "cosmiconfig": "7.0.1", "execa": "6.1.0", "form-data": "4.0.0", - "git-url-parse": "12.0.0", + "git-url-parse": "13.1.0", "globby": "13.1.2", - "got": "12.3.1", - "inquirer": "9.1.0", + "got": "12.5.1", + "inquirer": "9.1.2", "is-ci": "3.0.1", "lodash": "4.17.21", "mime-types": "2.1.35", @@ -13417,6 +12927,12 @@ "yargs-parser": "21.1.1" }, "dependencies": { + "chalk": { + "version": "5.0.1", + "resolved": "https://registry.npmjs.org/chalk/-/chalk-5.0.1.tgz", + "integrity": "sha512-Fo07WOYGqMfCWHOzSXOt2CxDbC6skS/jO9ynEcmpANMoPrD+W1r1K6Vx7iNm+AQmETU1Xr2t+n8nzkV9t6xh3w==", + "dev": true + }, "globby": { "version": "13.1.2", "resolved": "https://registry.npmjs.org/globby/-/globby-13.1.2.tgz", @@ -13459,6 +12975,12 @@ "resolved": "https://registry.npmjs.org/yallist/-/yallist-4.0.0.tgz", "integrity": "sha512-3wdGidZyq5PB084XLES5TpOSRA3wjXAlIWMhum2kRcv/41Sn2emQ0dycQW4uZXLejwKvg6EsvbdlVL+FYEct7A==", "dev": true + }, + "yargs-parser": { + "version": "21.1.1", + "resolved": "https://registry.npmjs.org/yargs-parser/-/yargs-parser-21.1.1.tgz", + "integrity": "sha512-tVpsJW7DdjecAiFpbIB1e3qxIQsE6NoPc5/eTdrbbIC4h0LVsWhnoa3g+m2HclBIujHzsxZ4VJVA+GUuc2/LBw==", + "dev": true } } }, @@ -13507,20 +13029,12 @@ "dev": true }, "responselike": { - "version": "2.0.1", - "resolved": "https://registry.npmjs.org/responselike/-/responselike-2.0.1.tgz", - "integrity": "sha512-4gl03wn3hj1HP3yzgdI7d3lCkF95F21Pz4BPGvKHinyQzALR5CapwC8yIi0Rh58DEMQ/SguC03wFj2k0M/mHhw==", + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/responselike/-/responselike-3.0.0.tgz", + "integrity": "sha512-40yHxbNcl2+rzXvZuVkrYohathsSJlMTXKryG5y8uciHv1+xDLHQpgjG64JUO9nrEq2jGLH6IZ8BcZyw3wrweg==", "dev": true, "requires": { - "lowercase-keys": "^2.0.0" - }, - "dependencies": { - "lowercase-keys": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/lowercase-keys/-/lowercase-keys-2.0.0.tgz", - "integrity": "sha512-tqNXrS78oMOE73NMxK4EMLQsQowWf8jKooH9g7xPavRT706R6bkQJ6DY2Te7QukaZsulxa30wQ7bk0pm4XiHmA==", - "dev": true - } + "lowercase-keys": "^3.0.0" } }, "restore-cursor": { @@ -13587,20 +13101,31 @@ } }, "rxjs": { - "version": "7.5.6", - "resolved": "https://registry.npmjs.org/rxjs/-/rxjs-7.5.6.tgz", - "integrity": "sha512-dnyv2/YsXhnm461G+R/Pe5bWP41Nm6LBXEYWI6eiFP4fiwx6WRI/CD0zbdVAudd9xwLEF2IDcKXLHit0FYjUzw==", + "version": "7.5.7", + "resolved": "https://registry.npmjs.org/rxjs/-/rxjs-7.5.7.tgz", + "integrity": "sha512-z9MzKh/UcOqB3i20H6rtrlaE/CgjLOvheWK/9ILrbhROGTweAi1BaFsTT9FbwZi5Trr1qNRs+MXkhmR06awzQA==", "dev": true, "requires": { "tslib": "^2.1.0" } }, "safe-buffer": { - "version": "5.1.2", - "resolved": "https://registry.npmjs.org/safe-buffer/-/safe-buffer-5.1.2.tgz", - "integrity": "sha512-Gd2UZBJDkXlY7GbJxfsE8/nvKkUEU1G38c1siN6QP6a9PT9MmHB8GnpscSmMJSoF8LOIrt8ud/wPtojys4G6+g==", + "version": "5.2.1", + "resolved": "https://registry.npmjs.org/safe-buffer/-/safe-buffer-5.2.1.tgz", + "integrity": "sha512-rp3So07KcdmmKbGvgaNxQSJr7bGVSVk5S9Eq1F+ppbRo70+YeaDxkw5Dd8NPN+GD6bjnYm2VuPuCXmpuYvmCXQ==", "dev": true }, + "safe-regex-test": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/safe-regex-test/-/safe-regex-test-1.0.0.tgz", + "integrity": "sha512-JBUUzyOgEwXQY1NuPtvcj/qcBDbDmEvWufhlnXZIm75DEHp+afM1r1ujJpJsV/gSM4t59tpDyPi1sd6ZaPFfsA==", + "dev": true, + "requires": { + "call-bind": "^1.0.2", + "get-intrinsic": "^1.1.3", + "is-regex": "^1.1.4" + } + }, "safer-buffer": { "version": "2.1.2", "resolved": "https://registry.npmjs.org/safer-buffer/-/safer-buffer-2.1.2.tgz", @@ -13608,18 +13133,12 @@ "dev": true }, "semver": { - "version": "6.3.0", - "resolved": "https://registry.npmjs.org/semver/-/semver-6.3.0.tgz", - "integrity": "sha512-b39TBaTSfV6yBrapU89p5fKekE2m/NwnDocOVruQFS1/veMgdzuPcnOM34M6CwxW8jH/lxEa5rBoDeUwu5HHTw==", - "dev": true - }, - "semver-diff": { - "version": "4.0.0", - "resolved": "https://registry.npmjs.org/semver-diff/-/semver-diff-4.0.0.tgz", - "integrity": "sha512-0Ju4+6A8iOnpL/Thra7dZsSlOHYAHIeMxfhWQRI1/VLcT3WDBZKKtQt/QkBOsiIN9ZpuvHE6cGZ0x4glCMmfiA==", + "version": "7.3.8", + "resolved": "https://registry.npmjs.org/semver/-/semver-7.3.8.tgz", + "integrity": "sha512-NB1ctGL5rlHrPJtFDVIVzTyQylMLu9N9VICA6HSFJo8MCGVTMW6gfpicwKmmK/dAjTOrqu5l63JJOpDSrAis3A==", "dev": true, "requires": { - "semver": "^7.3.5" + "lru-cache": "^6.0.0" }, "dependencies": { "lru-cache": { @@ -13631,15 +13150,6 @@ "yallist": "^4.0.0" } }, - "semver": { - "version": "7.3.7", - "resolved": "https://registry.npmjs.org/semver/-/semver-7.3.7.tgz", - "integrity": "sha512-QlYTucUYOews+WeEujDoEGziz4K6c47V/Bd+LjSSYcA94p+DmINdf7ncaUinThfvZyu13lN9OY1XDxt8C0Tw0g==", - "dev": true, - "requires": { - "lru-cache": "^6.0.0" - } - }, "yallist": { "version": "4.0.0", "resolved": "https://registry.npmjs.org/yallist/-/yallist-4.0.0.tgz", @@ -13648,6 +13158,15 @@ } } }, + "semver-diff": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/semver-diff/-/semver-diff-4.0.0.tgz", + "integrity": "sha512-0Ju4+6A8iOnpL/Thra7dZsSlOHYAHIeMxfhWQRI1/VLcT3WDBZKKtQt/QkBOsiIN9ZpuvHE6cGZ0x4glCMmfiA==", + "dev": true, + "requires": { + "semver": "^7.3.5" + } + }, "serialize-javascript": { "version": "6.0.0", "resolved": "https://registry.npmjs.org/serialize-javascript/-/serialize-javascript-6.0.0.tgz", @@ -13696,14 +13215,14 @@ } }, "shiki": { - "version": "0.10.1", - "resolved": "https://registry.npmjs.org/shiki/-/shiki-0.10.1.tgz", - "integrity": "sha512-VsY7QJVzU51j5o1+DguUd+6vmCmZ5v/6gYu4vyYAhzjuNQU6P/vmSy4uQaOhvje031qQMiW0d2BwgMH52vqMng==", + "version": "0.11.1", + "resolved": "https://registry.npmjs.org/shiki/-/shiki-0.11.1.tgz", + "integrity": "sha512-EugY9VASFuDqOexOgXR18ZV+TbFrQHeCpEYaXamO+SZlsnT/2LxuLBX25GGtIrwaEVFXUAbUQ601SWE2rMwWHA==", "dev": true, "requires": { "jsonc-parser": "^3.0.0", "vscode-oniguruma": "^1.6.1", - "vscode-textmate": "5.2.0" + "vscode-textmate": "^6.0.0" } }, "side-channel": { @@ -13724,9 +13243,9 @@ "dev": true }, "sinon": { - "version": "14.0.0", - "resolved": "https://registry.npmjs.org/sinon/-/sinon-14.0.0.tgz", - "integrity": "sha512-ugA6BFmE+WrJdh0owRZHToLd32Uw3Lxq6E6LtNRU+xTVBefx632h03Q7apXWRsRdZAJ41LB8aUfn2+O4jsDNMw==", + "version": "14.0.1", + "resolved": "https://registry.npmjs.org/sinon/-/sinon-14.0.1.tgz", + "integrity": "sha512-JhJ0jCiyBWVAHDS+YSjgEbDn7Wgz9iIjA1/RK+eseJN0vAAWIWiXBdrnb92ELPyjsfreCYntD1ORtLSfIrlvSQ==", "dev": true, "requires": { "@sinonjs/commons": "^1.8.3", @@ -13750,9 +13269,9 @@ "dev": true }, "socks": { - "version": "2.7.0", - "resolved": "https://registry.npmjs.org/socks/-/socks-2.7.0.tgz", - "integrity": "sha512-scnOe9y4VuiNUULJN72GrM26BNOjVsfPXI+j+98PkyEfsIXroa5ofyjT+FzGvn/xHs73U2JtoBYAVx9Hl4quSA==", + "version": "2.7.1", + "resolved": "https://registry.npmjs.org/socks/-/socks-2.7.1.tgz", + "integrity": "sha512-7maUZy1N7uo6+WVEX6psASxtNlKaNVMlGQKkG/63nEDdLOWNbiUMoLK7X4uYoLhQstau72mLgfEWcXcwsaHbYQ==", "dev": true, "requires": { "ip": "^2.0.0", @@ -13827,14 +13346,6 @@ "dev": true, "requires": { "safe-buffer": "~5.2.0" - }, - "dependencies": { - "safe-buffer": { - "version": "5.2.1", - "resolved": "https://registry.npmjs.org/safe-buffer/-/safe-buffer-5.2.1.tgz", - "integrity": "sha512-rp3So07KcdmmKbGvgaNxQSJr7bGVSVk5S9Eq1F+ppbRo70+YeaDxkw5Dd8NPN+GD6bjnYm2VuPuCXmpuYvmCXQ==", - "dev": true - } } }, "string-width": { @@ -13846,6 +13357,23 @@ "eastasianwidth": "^0.2.0", "emoji-regex": "^9.2.2", "strip-ansi": "^7.0.1" + }, + "dependencies": { + "ansi-regex": { + "version": "6.0.1", + "resolved": "https://registry.npmjs.org/ansi-regex/-/ansi-regex-6.0.1.tgz", + "integrity": "sha512-n5M855fKb2SsfMIiFFoVrABHJC8QtHwVx+mHWP3QcEqBHYienj5dHSgjbxtC0WEZXYt4wcD6zrQElDPhFuZgfA==", + "dev": true + }, + "strip-ansi": { + "version": "7.0.1", + "resolved": "https://registry.npmjs.org/strip-ansi/-/strip-ansi-7.0.1.tgz", + "integrity": "sha512-cXNxvT8dFNRVfhVME3JAe98mkXDYN2O1l7jmcwMnOslDeESg1rF/OZMtK0nRAhiari1unG5cD4jG3rapUAkLbw==", + "dev": true, + "requires": { + "ansi-regex": "^6.0.1" + } + } } }, "string.prototype.trimend": { @@ -13871,12 +13399,12 @@ } }, "strip-ansi": { - "version": "7.0.1", - "resolved": "https://registry.npmjs.org/strip-ansi/-/strip-ansi-7.0.1.tgz", - "integrity": "sha512-cXNxvT8dFNRVfhVME3JAe98mkXDYN2O1l7jmcwMnOslDeESg1rF/OZMtK0nRAhiari1unG5cD4jG3rapUAkLbw==", + "version": "6.0.1", + "resolved": "https://registry.npmjs.org/strip-ansi/-/strip-ansi-6.0.1.tgz", + "integrity": "sha512-Y38VPSHcqkFrCpFnQ9vuSXmquuv5oXOKpGeT6aGrr3o3Gc9AlVa6JBfUSOCnbxGGZF+/0ooI7KrPuUSztUdU5A==", "dev": true, "requires": { - "ansi-regex": "^6.0.1" + "ansi-regex": "^5.0.1" } }, "strip-bom": { @@ -14073,9 +13601,9 @@ "dev": true }, "type-fest": { - "version": "1.4.0", - "resolved": "https://registry.npmjs.org/type-fest/-/type-fest-1.4.0.tgz", - "integrity": "sha512-yGSza74xk0UG8k+pLh5oeoYirvIiWo5t0/o3zHHAO2tRDiZcxWP7fywNlXhqb6/r6sWvwi+RsyQMWhVLe4BVuA==", + "version": "0.20.2", + "resolved": "https://registry.npmjs.org/type-fest/-/type-fest-0.20.2.tgz", + "integrity": "sha512-Ne+eE4r0/iWnpAxD852z3A+N0Bt5RN//NjJwRd2VFHEmrywxf5vsZlh4R6lixl6B+wz/8d+maTSAkN1FIkI3LQ==", "dev": true }, "typedarray-to-buffer": { @@ -14088,15 +13616,15 @@ } }, "typedoc": { - "version": "0.23.10", - "resolved": "https://registry.npmjs.org/typedoc/-/typedoc-0.23.10.tgz", - "integrity": "sha512-03EUiu/ZuScUBMnY6p0lY+HTH8SwhzvRE3gImoemdPDWXPXlks83UGTx++lyquWeB1MTwm9D9Ca8RIjkK3AFfQ==", + "version": "0.23.18", + "resolved": "https://registry.npmjs.org/typedoc/-/typedoc-0.23.18.tgz", + "integrity": "sha512-0Tq/uFkUuWyRYyjOShTkhsOm6u5E8wf0i6L76/k5znEaxvWKHGeT2ywZThGrDrryV/skO/REM824D1gm8ccQuA==", "dev": true, "requires": { "lunr": "^2.3.9", - "marked": "^4.0.18", + "marked": "^4.0.19", "minimatch": "^5.1.0", - "shiki": "^0.10.1" + "shiki": "^0.11.1" }, "dependencies": { "brace-expansion": { @@ -14120,9 +13648,9 @@ } }, "typescript": { - "version": "4.7.4", - "resolved": "https://registry.npmjs.org/typescript/-/typescript-4.7.4.tgz", - "integrity": "sha512-C0WQT0gezHuw6AdY1M2jxUO83Rjf0HP7Sk1DtXj6j1EwkQNZrHAg2XPWlq62oqEhYvONq5pkC2Y9oPljWToLmQ==", + "version": "4.8.4", + "resolved": "https://registry.npmjs.org/typescript/-/typescript-4.8.4.tgz", + "integrity": "sha512-QCh+85mCy+h0IGff8r5XWzOVSbBO+KfeYrMQh7NJ58QujwcE22u+NUSmUxqF+un70P9GXKxa2HCNiTTMJknyjQ==", "dev": true }, "unbox-primitive": { @@ -14165,9 +13693,9 @@ "dev": true }, "update-browserslist-db": { - "version": "1.0.5", - "resolved": "https://registry.npmjs.org/update-browserslist-db/-/update-browserslist-db-1.0.5.tgz", - "integrity": "sha512-dteFFpCyvuDdr9S/ff1ISkKt/9YZxKjI9WlRR99c180GaztJtRa/fn18FdxGVKVsnPY7/a/FDN68mcvUmP4U7Q==", + "version": "1.0.10", + "resolved": "https://registry.npmjs.org/update-browserslist-db/-/update-browserslist-db-1.0.10.tgz", + "integrity": "sha512-OztqDenkfFkbSG+tRxBeAnCVPckDBcvibKd35yDONx6OU8N7sqgwc7rCbkJ/WcYtVRZ4ba68d6byhC21GFh7sQ==", "dev": true, "requires": { "escalade": "^3.1.1", @@ -14196,28 +13724,10 @@ "xdg-basedir": "^5.1.0" }, "dependencies": { - "lru-cache": { - "version": "6.0.0", - "resolved": "https://registry.npmjs.org/lru-cache/-/lru-cache-6.0.0.tgz", - "integrity": "sha512-Jo6dJ04CmSjuznwJSS3pUeWmd/H0ffTlkXXgwZi+eq1UCmqQwCh+eLsYOYCwY991i2Fah4h1BEMCx4qThGbsiA==", - "dev": true, - "requires": { - "yallist": "^4.0.0" - } - }, - "semver": { - "version": "7.3.7", - "resolved": "https://registry.npmjs.org/semver/-/semver-7.3.7.tgz", - "integrity": "sha512-QlYTucUYOews+WeEujDoEGziz4K6c47V/Bd+LjSSYcA94p+DmINdf7ncaUinThfvZyu13lN9OY1XDxt8C0Tw0g==", - "dev": true, - "requires": { - "lru-cache": "^6.0.0" - } - }, - "yallist": { - "version": "4.0.0", - "resolved": "https://registry.npmjs.org/yallist/-/yallist-4.0.0.tgz", - "integrity": "sha512-3wdGidZyq5PB084XLES5TpOSRA3wjXAlIWMhum2kRcv/41Sn2emQ0dycQW4uZXLejwKvg6EsvbdlVL+FYEct7A==", + "chalk": { + "version": "5.1.2", + "resolved": "https://registry.npmjs.org/chalk/-/chalk-5.1.2.tgz", + "integrity": "sha512-E5CkT4jWURs1Vy5qGJye+XwCkNj7Od3Af7CP6SujMetSMkLs8Do2RWJK5yx1wamHV/op8Rz+9rltjaTQWDnEFQ==", "dev": true } } @@ -14249,12 +13759,6 @@ "integrity": "sha512-+NYs2QeMWy+GWFOEm9xnn6HCDp0l7QBD7ml8zLUmJ+93Q5NF0NocErnwkTkXVFNiX3/fpC6afS8Dhb/gz7R7eg==", "dev": true }, - "v8-compile-cache": { - "version": "2.3.0", - "resolved": "https://registry.npmjs.org/v8-compile-cache/-/v8-compile-cache-2.3.0.tgz", - "integrity": "sha512-l8lCEmLcLYZh4nbunNZvQCJc5pv7+RCwa8q/LdUx8u7lsWvPDKmpodJAJNwkAhJC//dFY48KuIEmjtd4RViDrA==", - "dev": true - }, "v8-compile-cache-lib": { "version": "3.0.1", "resolved": "https://registry.npmjs.org/v8-compile-cache-lib/-/v8-compile-cache-lib-3.0.1.tgz", @@ -14262,9 +13766,9 @@ "dev": true }, "vm2": { - "version": "3.9.10", - "resolved": "https://registry.npmjs.org/vm2/-/vm2-3.9.10.tgz", - "integrity": "sha512-AuECTSvwu2OHLAZYhG716YzwodKCIJxB6u1zG7PgSQwIgAlEaoXH52bxdcvT8GkGjnYK7r7yWDW0m0sOsPuBjQ==", + "version": "3.9.11", + "resolved": "https://registry.npmjs.org/vm2/-/vm2-3.9.11.tgz", + "integrity": "sha512-PFG8iJRSjvvBdisowQ7iVF580DXb1uCIiGaXgm7tynMR1uTBlv7UJlB1zdv5KJ+Tmq1f0Upnj3fayoEOPpCBKg==", "dev": true, "requires": { "acorn": "^8.7.0", @@ -14278,9 +13782,9 @@ "dev": true }, "vscode-textmate": { - "version": "5.2.0", - "resolved": "https://registry.npmjs.org/vscode-textmate/-/vscode-textmate-5.2.0.tgz", - "integrity": "sha512-Uw5ooOQxRASHgu6C7GVvUxisKXfSgW4oFlO+aa+PAkgmH89O3CXxEEzNRNtHSqtXFTl0nAC1uYj0GMSH27uwtQ==", + "version": "6.0.0", + "resolved": "https://registry.npmjs.org/vscode-textmate/-/vscode-textmate-6.0.0.tgz", + "integrity": "sha512-gu73tuZfJgu+mvCSy4UZwd2JXykjK9zAZsfmDeut5dx/1a7FeTk0XwJsSuqQn+cuMCGVbIBfl+s53X4T19DnzQ==", "dev": true }, "wcwidth": { @@ -14389,12 +13893,6 @@ "integrity": "sha512-B4FFZ6q/T2jhhksgkbEW3HBvWIfDW85snkQgawt07S7J5QXTk6BkNV+0yAeZrM5QpMAdYlocGoljn0sJ/WQkFw==", "dev": true }, - "is-stream": { - "version": "2.0.1", - "resolved": "https://registry.npmjs.org/is-stream/-/is-stream-2.0.1.tgz", - "integrity": "sha512-hFoiJiTl63nn+kstHGBtewWSKnQLpyb155KHheA1l39uvtO9nWIop1p3udqPcUd/xbF1VLMO4n7OI6p7RbngDg==", - "dev": true - }, "mimic-fn": { "version": "2.1.0", "resolved": "https://registry.npmjs.org/mimic-fn/-/mimic-fn-2.1.0.tgz", @@ -14448,6 +13946,29 @@ "ansi-styles": "^6.1.0", "string-width": "^5.0.1", "strip-ansi": "^7.0.1" + }, + "dependencies": { + "ansi-regex": { + "version": "6.0.1", + "resolved": "https://registry.npmjs.org/ansi-regex/-/ansi-regex-6.0.1.tgz", + "integrity": "sha512-n5M855fKb2SsfMIiFFoVrABHJC8QtHwVx+mHWP3QcEqBHYienj5dHSgjbxtC0WEZXYt4wcD6zrQElDPhFuZgfA==", + "dev": true + }, + "ansi-styles": { + "version": "6.2.1", + "resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-6.2.1.tgz", + "integrity": "sha512-bN798gFfQX+viw3R7yrGWRqnrN2oRkEkUjjl4JNn4E8GxxbjtG3FbrEIIY3l8/hrwUwIeCZvi4QuOTP4MErVug==", + "dev": true + }, + "strip-ansi": { + "version": "7.0.1", + "resolved": "https://registry.npmjs.org/strip-ansi/-/strip-ansi-7.0.1.tgz", + "integrity": "sha512-cXNxvT8dFNRVfhVME3JAe98mkXDYN2O1l7jmcwMnOslDeESg1rF/OZMtK0nRAhiari1unG5cD4jG3rapUAkLbw==", + "dev": true, + "requires": { + "ansi-regex": "^6.0.1" + } + } } }, "wrappy": { @@ -14481,9 +14002,9 @@ "dev": true }, "y18n": { - "version": "4.0.3", - "resolved": "https://registry.npmjs.org/y18n/-/y18n-4.0.3.tgz", - "integrity": "sha512-JKhqTOwSrqNA1NY5lSztJ1GrBiUodLMmIZuLiDaMRJ+itFd+ABVE8XBjOvIWL+rSqNDC74LCSFmlb/U4UZ4hJQ==", + "version": "5.0.8", + "resolved": "https://registry.npmjs.org/y18n/-/y18n-5.0.8.tgz", + "integrity": "sha512-0pfFzegeDWJHJIAmTLRP2DwHjdF5s7jo9tuztdQxAhINCdvS+3nGINqPd00AphqJR/0LhANUS6/+7SCb98YOfA==", "dev": true }, "yallist": { @@ -14499,30 +14020,20 @@ "dev": true }, "yargs": { - "version": "15.4.1", - "resolved": "https://registry.npmjs.org/yargs/-/yargs-15.4.1.tgz", - "integrity": "sha512-aePbxDmcYW++PaqBsJ+HYUFwCdv4LVvdnhBy78E57PIor8/OVvhMrADFFEDh8DHDFRv/O9i3lPhsENjO7QX0+A==", + "version": "17.6.0", + "resolved": "https://registry.npmjs.org/yargs/-/yargs-17.6.0.tgz", + "integrity": "sha512-8H/wTDqlSwoSnScvV2N/JHfLWOKuh5MVla9hqLjK3nsfyy6Y4kDSYSvkU5YCUEPOSnRXfIyx3Sq+B/IWudTo4g==", "dev": true, "requires": { - "cliui": "^6.0.0", - "decamelize": "^1.2.0", - "find-up": "^4.1.0", - "get-caller-file": "^2.0.1", + "cliui": "^8.0.1", + "escalade": "^3.1.1", + "get-caller-file": "^2.0.5", "require-directory": "^2.1.1", - "require-main-filename": "^2.0.0", - "set-blocking": "^2.0.0", - "string-width": "^4.2.0", - "which-module": "^2.0.0", - "y18n": "^4.0.0", - "yargs-parser": "^18.1.2" + "string-width": "^4.2.3", + "y18n": "^5.0.5", + "yargs-parser": "^21.0.0" }, "dependencies": { - "ansi-regex": { - "version": "5.0.1", - "resolved": "https://registry.npmjs.org/ansi-regex/-/ansi-regex-5.0.1.tgz", - "integrity": "sha512-quJQXlTSUGL2LH9SUXo8VwsY4soanhgo6LNSm84E1LBcE8s3O0wpdiRzyR9z/ZZJMlMWv37qOOb9pdJlMUEKFQ==", - "dev": true - }, "emoji-regex": { "version": "8.0.0", "resolved": "https://registry.npmjs.org/emoji-regex/-/emoji-regex-8.0.0.tgz", @@ -14540,31 +14051,18 @@ "strip-ansi": "^6.0.1" } }, - "strip-ansi": { - "version": "6.0.1", - "resolved": "https://registry.npmjs.org/strip-ansi/-/strip-ansi-6.0.1.tgz", - "integrity": "sha512-Y38VPSHcqkFrCpFnQ9vuSXmquuv5oXOKpGeT6aGrr3o3Gc9AlVa6JBfUSOCnbxGGZF+/0ooI7KrPuUSztUdU5A==", - "dev": true, - "requires": { - "ansi-regex": "^5.0.1" - } - }, "yargs-parser": { - "version": "18.1.3", - "resolved": "https://registry.npmjs.org/yargs-parser/-/yargs-parser-18.1.3.tgz", - "integrity": "sha512-o50j0JeToy/4K6OZcaQmW6lyXXKhq7csREXcDwk2omFPJEwUNOVtJKvmDr9EI1fAJZUyZcRF7kxGBWmRXudrCQ==", - "dev": true, - "requires": { - "camelcase": "^5.0.0", - "decamelize": "^1.2.0" - } + "version": "21.1.1", + "resolved": "https://registry.npmjs.org/yargs-parser/-/yargs-parser-21.1.1.tgz", + "integrity": "sha512-tVpsJW7DdjecAiFpbIB1e3qxIQsE6NoPc5/eTdrbbIC4h0LVsWhnoa3g+m2HclBIujHzsxZ4VJVA+GUuc2/LBw==", + "dev": true } } }, "yargs-parser": { - "version": "21.1.1", - "resolved": "https://registry.npmjs.org/yargs-parser/-/yargs-parser-21.1.1.tgz", - "integrity": "sha512-tVpsJW7DdjecAiFpbIB1e3qxIQsE6NoPc5/eTdrbbIC4h0LVsWhnoa3g+m2HclBIujHzsxZ4VJVA+GUuc2/LBw==", + "version": "20.2.4", + "resolved": "https://registry.npmjs.org/yargs-parser/-/yargs-parser-20.2.4.tgz", + "integrity": "sha512-WOkpgNhPTlE73h4VFAFsOnomJVaovO8VqLDzy5saChRBFQFBoMYirowyW+Q9HB4HFF4Z7VZTiG3iSzJJA29yRA==", "dev": true }, "yargs-unparser": { diff --git a/packages/bloom/package.json b/packages/bloom/package.json index 733723dcf95..4c55a28dfb9 100644 --- a/packages/bloom/package.json +++ b/packages/bloom/package.json @@ -18,12 +18,12 @@ "devDependencies": { "@istanbuljs/nyc-config-typescript": "^1.0.2", "@redis/test-utils": "*", - "@types/node": "^18.7.10", + "@types/node": "^18.11.6", "nyc": "^15.1.0", - "release-it": "^15.3.0", + "release-it": "^15.5.0", "source-map-support": "^0.5.21", "ts-node": "^10.9.1", - "typedoc": "^0.23.10", - "typescript": "^4.7.4" + "typedoc": "^0.23.18", + "typescript": "^4.8.4" } } diff --git a/packages/client/package.json b/packages/client/package.json index d348b484aab..10013b3c66f 100644 --- a/packages/client/package.json +++ b/packages/client/package.json @@ -14,26 +14,26 @@ "documentation": "typedoc" }, "dependencies": { - "cluster-key-slot": "1.1.0", - "generic-pool": "3.8.2", + "cluster-key-slot": "1.1.1", + "generic-pool": "3.9.0", "yallist": "4.0.0" }, "devDependencies": { "@istanbuljs/nyc-config-typescript": "^1.0.2", "@redis/test-utils": "*", - "@types/node": "^18.7.10", + "@types/node": "^18.11.6", "@types/sinon": "^10.0.13", "@types/yallist": "^4.0.1", - "@typescript-eslint/eslint-plugin": "^5.34.0", - "@typescript-eslint/parser": "^5.34.0", - "eslint": "^8.22.0", + "@typescript-eslint/eslint-plugin": "^5.41.0", + "@typescript-eslint/parser": "^5.41.0", + "eslint": "^8.26.0", "nyc": "^15.1.0", - "release-it": "^15.3.0", - "sinon": "^14.0.0", + "release-it": "^15.5.0", + "sinon": "^14.0.1", "source-map-support": "^0.5.21", "ts-node": "^10.9.1", - "typedoc": "^0.23.10", - "typescript": "^4.7.4" + "typedoc": "^0.23.18", + "typescript": "^4.8.4" }, "engines": { "node": ">=14" diff --git a/packages/graph/package.json b/packages/graph/package.json index 0213c511a2c..e1749eaa482 100644 --- a/packages/graph/package.json +++ b/packages/graph/package.json @@ -18,12 +18,12 @@ "devDependencies": { "@istanbuljs/nyc-config-typescript": "^1.0.2", "@redis/test-utils": "*", - "@types/node": "^18.7.10", + "@types/node": "^18.11.6", "nyc": "^15.1.0", - "release-it": "^15.3.0", + "release-it": "^15.5.0", "source-map-support": "^0.5.21", "ts-node": "^10.9.1", - "typedoc": "^0.23.10", - "typescript": "^4.7.4" + "typedoc": "^0.23.18", + "typescript": "^4.8.4" } } diff --git a/packages/json/package.json b/packages/json/package.json index 4d0f0048bbb..11dd7cae780 100644 --- a/packages/json/package.json +++ b/packages/json/package.json @@ -18,12 +18,12 @@ "devDependencies": { "@istanbuljs/nyc-config-typescript": "^1.0.2", "@redis/test-utils": "*", - "@types/node": "^18.7.10", + "@types/node": "^18.11.6", "nyc": "^15.1.0", - "release-it": "^15.3.0", + "release-it": "^15.5.0", "source-map-support": "^0.5.21", "ts-node": "^10.9.1", - "typedoc": "^0.23.10", - "typescript": "^4.7.4" + "typedoc": "^0.23.18", + "typescript": "^4.8.4" } } diff --git a/packages/search/package.json b/packages/search/package.json index fdebb0b1ea5..64591968715 100644 --- a/packages/search/package.json +++ b/packages/search/package.json @@ -18,12 +18,12 @@ "devDependencies": { "@istanbuljs/nyc-config-typescript": "^1.0.2", "@redis/test-utils": "*", - "@types/node": "^18.7.10", + "@types/node": "^18.11.6", "nyc": "^15.1.0", - "release-it": "^15.3.0", + "release-it": "^15.5.0", "source-map-support": "^0.5.21", "ts-node": "^10.9.1", - "typedoc": "^0.23.10", - "typescript": "^4.7.4" + "typedoc": "^0.23.18", + "typescript": "^4.8.4" } } diff --git a/packages/test-utils/package.json b/packages/test-utils/package.json index ae50fe1b2be..4549aa71a81 100644 --- a/packages/test-utils/package.json +++ b/packages/test-utils/package.json @@ -11,14 +11,14 @@ }, "devDependencies": { "@istanbuljs/nyc-config-typescript": "^1.0.2", - "@types/mocha": "^9.1.1", - "@types/node": "^18.7.10", - "@types/yargs": "^17.0.11", - "mocha": "^10.0.0", + "@types/mocha": "^10.0.0", + "@types/node": "^18.11.6", + "@types/yargs": "^17.0.13", + "mocha": "^10.1.0", "nyc": "^15.1.0", "source-map-support": "^0.5.21", "ts-node": "^10.9.1", - "typescript": "^4.7.4", - "yargs": "^17.5.1" + "typescript": "^4.8.4", + "yargs": "^17.6.0" } } diff --git a/packages/time-series/package.json b/packages/time-series/package.json index 01b35066373..b5e93316343 100644 --- a/packages/time-series/package.json +++ b/packages/time-series/package.json @@ -18,12 +18,12 @@ "devDependencies": { "@istanbuljs/nyc-config-typescript": "^1.0.2", "@redis/test-utils": "*", - "@types/node": "^18.7.10", + "@types/node": "^18.11.6", "nyc": "^15.1.0", - "release-it": "^15.3.0", + "release-it": "^15.5.0", "source-map-support": "^0.5.21", "ts-node": "^10.9.1", - "typedoc": "^0.23.10", - "typescript": "^4.7.4" + "typedoc": "^0.23.18", + "typescript": "^4.8.4" } } From e129d109529f2c2090a1c0b573e4eb6cb848a042 Mon Sep 17 00:00:00 2001 From: Simon Prickett Date: Wed, 26 Oct 2022 20:29:20 +0100 Subject: [PATCH 1259/1748] Fixes typo Redis Search -> RediSearch (#2278) --- README.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/README.md b/README.md index 9d48bdd6b2a..0f95142da7a 100644 --- a/README.md +++ b/README.md @@ -22,7 +22,7 @@ node-redis is a modern, high performance [Redis](https://redis.io) client for No | [@redis/bloom](./packages/bloom) | [![Downloads](https://img.shields.io/npm/dm/@redis/bloom.svg)](https://www.npmjs.com/package/@redis/bloom) [![Version](https://img.shields.io/npm/v/@redis/bloom.svg)](https://www.npmjs.com/package/@redis/bloom) [![Docs](https://img.shields.io/badge/-documentation-dc382c)](https://redis.js.org/documentation/bloom/) [Redis Bloom](https://oss.redis.com/redisbloom/) commands | | [@redis/graph](./packages/graph) | [![Downloads](https://img.shields.io/npm/dm/@redis/graph.svg)](https://www.npmjs.com/package/@redis/graph) [![Version](https://img.shields.io/npm/v/@redis/graph.svg)](https://www.npmjs.com/package/@redis/graph) [![Docs](https://img.shields.io/badge/-documentation-dc382c)](https://redis.js.org/documentation/graph/) [Redis Graph](https://oss.redis.com/redisgraph/) commands | | [@redis/json](./packages/json) | [![Downloads](https://img.shields.io/npm/dm/@redis/json.svg)](https://www.npmjs.com/package/@redis/json) [![Version](https://img.shields.io/npm/v/@redis/json.svg)](https://www.npmjs.com/package/@redis/json) [![Docs](https://img.shields.io/badge/-documentation-dc382c)](https://redis.js.org/documentation/json/) [Redis JSON](https://oss.redis.com/redisjson/) commands | -| [@redis/search](./packages/search) | [![Downloads](https://img.shields.io/npm/dm/@redis/search.svg)](https://www.npmjs.com/package/@redis/search) [![Version](https://img.shields.io/npm/v/@redis/search.svg)](https://www.npmjs.com/package/@redis/search) [![Docs](https://img.shields.io/badge/-documentation-dc382c)](https://redis.js.org/documentation/search/) [Redis Search](https://oss.redis.com/redisearch/) commands | +| [@redis/search](./packages/search) | [![Downloads](https://img.shields.io/npm/dm/@redis/search.svg)](https://www.npmjs.com/package/@redis/search) [![Version](https://img.shields.io/npm/v/@redis/search.svg)](https://www.npmjs.com/package/@redis/search) [![Docs](https://img.shields.io/badge/-documentation-dc382c)](https://redis.js.org/documentation/search/) [RediSearch](https://oss.redis.com/redisearch/) commands | | [@redis/time-series](./packages/time-series) | [![Downloads](https://img.shields.io/npm/dm/@redis/time-series.svg)](https://www.npmjs.com/package/@redis/time-series) [![Version](https://img.shields.io/npm/v/@redis/time-series.svg)](https://www.npmjs.com/package/@redis/time-series) [![Docs](https://img.shields.io/badge/-documentation-dc382c)](https://redis.js.org/documentation/time-series/) [Redis Time-Series](https://oss.redis.com/redistimeseries/) commands | > :warning: In version 4.1.0 we moved our subpackages from `@node-redis` to `@redis`. If you're just using `npm install redis`, you don't need to do anything—it'll upgrade automatically. If you're using the subpackages directly, you'll need to point to the new scope (e.g. `@redis/client` instead of `@node-redis/client`). From 72c0e7bf6702db64ecfde8c2acf226657197eea8 Mon Sep 17 00:00:00 2001 From: Simon Prickett Date: Wed, 26 Oct 2022 20:29:34 +0100 Subject: [PATCH 1260/1748] Fixed redis -> Redis in a couple places. (#2279) --- docs/clustering.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/docs/clustering.md b/docs/clustering.md index b1b5b82021a..26acfa0a791 100644 --- a/docs/clustering.md +++ b/docs/clustering.md @@ -43,7 +43,7 @@ const value = await cluster.get('key'); ## Node Address Map -A node address map is required when a redis cluster is configured with addresses that are inaccessible by the machine running the redis client. +A node address map is required when a Redis cluster is configured with addresses that are inaccessible by the machine running the Redis client. This is a mapping of addresses and ports, with the values being the accessible address/port combination. Example: ```javascript From 0abd950f03388ec5db7ee96edc74cb677e58cc38 Mon Sep 17 00:00:00 2001 From: Leibale Eidelman Date: Wed, 26 Oct 2022 15:32:19 -0400 Subject: [PATCH 1261/1748] fix tsbuild (#2307) * fix tsbuild * fix tsbuild * fix tsbuild --- packages/client/lib/commander.ts | 3 ++- packages/test-utils/lib/index.ts | 3 ++- 2 files changed, 4 insertions(+), 2 deletions(-) diff --git a/packages/client/lib/commander.ts b/packages/client/lib/commander.ts index 661e53cb9b5..1407a80344c 100644 --- a/packages/client/lib/commander.ts +++ b/packages/client/lib/commander.ts @@ -1,4 +1,5 @@ +import { ClientCommandOptions } from './client'; import { CommandOptions, isCommandOptions } from './command-options'; import { RedisCommand, RedisCommandArgument, RedisCommandArguments, RedisCommandReply, RedisFunction, RedisFunctions, RedisModules, RedisScript, RedisScripts } from './commands'; @@ -103,7 +104,7 @@ function attachWithNamespaces({ return Commander; } -export function transformCommandArguments( +export function transformCommandArguments( command: RedisCommand, args: Array ): { diff --git a/packages/test-utils/lib/index.ts b/packages/test-utils/lib/index.ts index 1e814c29746..3e33971fddb 100644 --- a/packages/test-utils/lib/index.ts +++ b/packages/test-utils/lib/index.ts @@ -1,6 +1,7 @@ import { RedisModules, RedisFunctions, RedisScripts } from '@redis/client/lib/commands'; import RedisClient, { RedisClientOptions, RedisClientType } from '@redis/client/lib/client'; import RedisCluster, { RedisClusterOptions, RedisClusterType } from '@redis/client/lib/cluster'; +import { RedisSocketCommonOptions } from '@redis/client/lib/client/socket'; import { RedisServerDockerConfig, spawnRedisServer, spawnRedisCluster } from './dockers'; import yargs from 'yargs'; import { hideBin } from 'yargs/helpers'; @@ -21,7 +22,7 @@ interface ClientTestOptions< S extends RedisScripts > extends CommonTestOptions { serverArguments: Array; - clientOptions?: Partial>; + clientOptions?: Partial, 'socket'> & { socket: RedisSocketCommonOptions }>; disableClientSetup?: boolean; } From c413657357663acba84be585e6ad937d72219c79 Mon Sep 17 00:00:00 2001 From: Matthijs Dabroek Date: Wed, 26 Oct 2022 22:40:14 +0200 Subject: [PATCH 1262/1748] Reject multi.exec() promise with `ClientClosedError` after client disconnect (#2293) * Add reject multi chain on client disconnect assertion to client test suite * Reject multi chain exec with client closed error after client disconnect --- packages/client/lib/client/index.spec.ts | 13 +++++++++++++ packages/client/lib/client/index.ts | 4 ++++ 2 files changed, 17 insertions(+) diff --git a/packages/client/lib/client/index.spec.ts b/packages/client/lib/client/index.spec.ts index d27bcc2b78d..27cb86d657e 100644 --- a/packages/client/lib/client/index.spec.ts +++ b/packages/client/lib/client/index.spec.ts @@ -425,6 +425,19 @@ describe('Client', () => { ); }, GLOBAL.SERVERS.OPEN); + testUtils.testWithClient('should reject the whole chain upon client disconnect', async client => { + await client.disconnect(); + + return assert.rejects( + client.multi() + .ping() + .set('key', 'value') + .get('key') + .exec(), + ClientClosedError + ); + }, GLOBAL.SERVERS.OPEN); + testUtils.testWithClient('with script', async client => { assert.deepEqual( await client.multi() diff --git a/packages/client/lib/client/index.ts b/packages/client/lib/client/index.ts index 2032c4fb714..52895c73c3e 100644 --- a/packages/client/lib/client/index.ts +++ b/packages/client/lib/client/index.ts @@ -611,6 +611,10 @@ export default class RedisClient< selectedDB?: number, chainId?: symbol ): Promise> { + if (!this.#socket.isOpen) { + return Promise.reject(new ClientClosedError()); + } + const promise = Promise.all( commands.map(({ args }) => { return this.#queue.addCommand(args, { chainId }); From 252c2192eab1f9a7d6b5654b00a61d03cad2f6d5 Mon Sep 17 00:00:00 2001 From: Jonas Faure <47354296+JonasFaure@users.noreply.github.com> Date: Wed, 26 Oct 2022 22:42:52 +0200 Subject: [PATCH 1263/1748] fix(client): Avoids infinite promise-chaining when socket's creation fails (#2295) * fix(client): timeout issues during tests * fix(client): avoiding infinite Promise chaining while socket creation fails * fix(client): Added missing semicolons * clean test Co-authored-by: leibale --- packages/client/lib/client/socket.spec.ts | 19 +++---- packages/client/lib/client/socket.ts | 63 ++++++++++++----------- 2 files changed, 41 insertions(+), 41 deletions(-) diff --git a/packages/client/lib/client/socket.spec.ts b/packages/client/lib/client/socket.spec.ts index 1b2d050c012..c5862130cf5 100644 --- a/packages/client/lib/client/socket.spec.ts +++ b/packages/client/lib/client/socket.spec.ts @@ -1,5 +1,5 @@ import { strict as assert } from 'assert'; -import { SinonFakeTimers, useFakeTimers, spy } from 'sinon'; +import { spy } from 'sinon'; import RedisSocket, { RedisSocketOptions } from './socket'; describe('Socket', () => { @@ -9,37 +9,34 @@ describe('Socket', () => { options ); - socket.on('error', (err) => { + socket.on('error', () => { // ignore errors - console.log(err); }); return socket; } describe('reconnectStrategy', () => { - let clock: SinonFakeTimers; - beforeEach(() => clock = useFakeTimers()); - afterEach(() => clock.restore()); - it('custom strategy', async () => { + const numberOfRetries = 10; + const reconnectStrategy = spy((retries: number) => { assert.equal(retries + 1, reconnectStrategy.callCount); - if (retries === 50) return new Error('50'); + if (retries === numberOfRetries) return new Error(`${numberOfRetries}`); const time = retries * 2; - queueMicrotask(() => clock.tick(time)); return time; }); const socket = createSocket({ host: 'error', + connectTimeout: 1, reconnectStrategy }); await assert.rejects(socket.connect(), { - message: '50' + message: `${numberOfRetries}` }); assert.equal(socket.isOpen, false); @@ -48,9 +45,9 @@ describe('Socket', () => { it('should handle errors', async () => { const socket = createSocket({ host: 'error', + connectTimeout: 1, reconnectStrategy(retries: number) { if (retries === 1) return new Error('done'); - queueMicrotask(() => clock.tick(500)); throw new Error(); } }); diff --git a/packages/client/lib/client/socket.ts b/packages/client/lib/client/socket.ts index 2a955159323..fabc22038d0 100644 --- a/packages/client/lib/client/socket.ts +++ b/packages/client/lib/client/socket.ts @@ -105,46 +105,49 @@ export default class RedisSocket extends EventEmitter { throw new Error('Socket already opened'); } - return this.#connect(0); + return this.#connect(); } - async #connect(retries: number, hadError?: boolean): Promise { - if (retries > 0 || hadError) { - this.emit('reconnecting'); - } - - try { - this.#isOpen = true; - this.#socket = await this.#createSocket(); - this.#writableNeedDrain = false; - this.emit('connect'); + async #connect(hadError?: boolean): Promise { + let retries = 0; + do { + if (retries > 0 || hadError) { + this.emit('reconnecting'); + } try { - await this.#initiator(); + this.#isOpen = true; + this.#socket = await this.#createSocket(); + this.#writableNeedDrain = false; + this.emit('connect'); + + try { + await this.#initiator(); + } catch (err) { + this.#socket.destroy(); + this.#socket = undefined; + throw err; + } + this.#isReady = true; + this.emit('ready'); } catch (err) { - this.#socket.destroy(); - this.#socket = undefined; - throw err; - } - this.#isReady = true; - this.emit('ready'); - } catch (err) { - const retryIn = this.reconnectStrategy(retries); - if (retryIn instanceof Error) { - this.#isOpen = false; + const retryIn = this.reconnectStrategy(retries); + if (retryIn instanceof Error) { + this.#isOpen = false; + this.emit('error', err); + throw new ReconnectStrategyError(retryIn, err); + } + this.emit('error', err); - throw new ReconnectStrategyError(retryIn, err); + await promiseTimeout(retryIn); } - - this.emit('error', err); - await promiseTimeout(retryIn); - return this.#connect(retries + 1); - } + retries++; + } while (!this.#isReady); } #createSocket(): Promise { return new Promise((resolve, reject) => { - const {connectEvent, socket} = RedisSocket.#isTlsSocket(this.#options) ? + const { connectEvent, socket } = RedisSocket.#isTlsSocket(this.#options) ? this.#createTlsSocket() : this.#createNetSocket(); @@ -200,7 +203,7 @@ export default class RedisSocket extends EventEmitter { this.#isReady = false; this.emit('error', err); - this.#connect(0, true).catch(() => { + this.#connect(true).catch(() => { // the error was already emitted, silently ignore it }); } From 5a25078831018be3f3ecbeb241d79ac5ee3e2b3f Mon Sep 17 00:00:00 2001 From: Leibale Eidelman Date: Mon, 31 Oct 2022 09:44:30 -0400 Subject: [PATCH 1264/1748] Update search-json.js (#2317) --- examples/search-json.js | 14 +++++++------- 1 file changed, 7 insertions(+), 7 deletions(-) diff --git a/examples/search-json.js b/examples/search-json.js index 6205a1f110e..75de0221a1e 100644 --- a/examples/search-json.js +++ b/examples/search-json.js @@ -65,8 +65,8 @@ console.log('Users under 30 years old:'); console.log( // https://redis.io/commands/ft.search/ JSON.stringify( - await client.ft.search('idx:users', '@age:[0 30]'), - null, + await client.ft.search('idx:users', '@age:[0 30]'), + null, 2 ) ); @@ -89,11 +89,11 @@ console.log( // in the email address. This applies for other punctuation too. // https://redis.io/docs/stack/search/reference/tags/#including-punctuation-in-tags console.log('Users with email "bob@somewhere.gov":'); -const emailAddress = 'bob@somewhere.gov'.replace(/[.@]/g, '\\$&'); +const emailAddress = 'bob@somewhere.gov'.replace(/[.@\\]/g, '\\$&'); console.log( JSON.stringify( - await client.ft.search('idx:users', `@email:{${emailAddress}}`), - null, + await client.ft.search('idx:users', `@email:{${emailAddress}}`), + null, 2 ) ); @@ -130,8 +130,8 @@ console.log( AS: 'totalCoins' }] }] - }), - null, + }), + null, 2 ) ); From 59256f8f1970efb85a3bfb4a494d8897696ed204 Mon Sep 17 00:00:00 2001 From: Leibale Eidelman Date: Mon, 31 Oct 2022 09:46:46 -0400 Subject: [PATCH 1265/1748] test with node 19 instead of 17 (#2316) --- .github/workflows/tests.yml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.github/workflows/tests.yml b/.github/workflows/tests.yml index 2ec99b7bba0..365e9f31d3c 100644 --- a/.github/workflows/tests.yml +++ b/.github/workflows/tests.yml @@ -16,7 +16,7 @@ jobs: strategy: fail-fast: false matrix: - node-version: ['14', '16', '17', '18'] + node-version: ['14', '16', '18', '19'] redis-version: ['5', '6.0', '6.2', '7.0'] steps: - uses: actions/checkout@v2.3.4 From 64f86d6a0013b70594ee94591396d17334118054 Mon Sep 17 00:00:00 2001 From: reviewher <24845478+reviewher@users.noreply.github.com> Date: Mon, 31 Oct 2022 09:47:53 -0400 Subject: [PATCH 1266/1748] disconnect in basic example (#2235) --- README.md | 1 + 1 file changed, 1 insertion(+) diff --git a/README.md b/README.md index 0f95142da7a..8ea495bba7b 100644 --- a/README.md +++ b/README.md @@ -52,6 +52,7 @@ await client.connect(); await client.set('key', 'value'); const value = await client.get('key'); +await client.disconnect(); ``` The above code connects to localhost on port 6379. To connect to a different host or port, use a connection string in the format `redis[s]://[[username][:password]@][host][:port][/db-number]`: From 1c6d74ffcb776af858d4be9c60658ef75436addc Mon Sep 17 00:00:00 2001 From: Leibale Eidelman Date: Tue, 1 Nov 2022 15:45:35 -0400 Subject: [PATCH 1267/1748] fix #2189 - add graph --compact support (#2305) * fix #2189 - add graph --compact support * clean code * fix graph string param escaping * fix "is not assignable to parameter of type 'GraphClientType'" * fix README --- packages/graph/README.md | 37 +- packages/graph/lib/commands/QUERY.spec.ts | 13 +- packages/graph/lib/commands/QUERY.ts | 30 +- packages/graph/lib/commands/QUERY_RO.spec.ts | 22 -- packages/graph/lib/commands/RO_QUERY.spec.ts | 17 + .../lib/commands/{QUERY_RO.ts => RO_QUERY.ts} | 8 +- packages/graph/lib/commands/index.spec.ts | 62 +++ packages/graph/lib/commands/index.ts | 87 ++++- packages/graph/lib/graph.spec.ts | 148 ++++++++ packages/graph/lib/graph.ts | 359 ++++++++++++++++++ packages/graph/lib/index.ts | 1 + packages/test-utils/lib/dockers.ts | 6 +- 12 files changed, 713 insertions(+), 77 deletions(-) delete mode 100644 packages/graph/lib/commands/QUERY_RO.spec.ts create mode 100644 packages/graph/lib/commands/RO_QUERY.spec.ts rename packages/graph/lib/commands/{QUERY_RO.ts => RO_QUERY.ts} (72%) create mode 100644 packages/graph/lib/commands/index.spec.ts create mode 100644 packages/graph/lib/graph.spec.ts create mode 100644 packages/graph/lib/graph.ts diff --git a/packages/graph/README.md b/packages/graph/README.md index 595e0226b25..3beb08f0ecd 100644 --- a/packages/graph/README.md +++ b/packages/graph/README.md @@ -2,34 +2,31 @@ Example usage: ```javascript -import { createClient } from 'redis'; +import { createClient, Graph } from 'redis'; const client = createClient(); client.on('error', (err) => console.log('Redis Client Error', err)); await client.connect(); -await client.graph.query( - 'graph', - "CREATE (:Rider { name: 'Buzz Aldrin' })-[:rides]->(:Team { name: 'Apollo' })" -); +const graph = new Graph(client, 'graph'); -const result = await client.graph.query( - 'graph', - `MATCH (r:Rider)-[:rides]->(t:Team) WHERE t.name = 'Apollo' RETURN r.name, t.name` +await graph.query( + 'CREATE (:Rider { name: $riderName })-[:rides]->(:Team { name: $teamName })', + { + params: { + riderName: 'Buzz Aldrin', + teamName: 'Apollo' + } + } ); -console.log(result); -``` +const result = await graph.roQuery( + 'MATCH (r:Rider)-[:rides]->(t:Team { name: $name }) RETURN r.name AS name', + { + name: 'Apollo' + } +); -Output from console log: -```json -{ - headers: [ 'r.name', 't.name' ], - data: [ [ 'Buzz Aldrin', 'Apollo' ] ], - metadata: [ - 'Cached execution: 0', - 'Query internal execution time: 0.431700 milliseconds' - ] -} +console.log(result.data); // [{ name: 'Buzz Aldrin' }] ``` diff --git a/packages/graph/lib/commands/QUERY.spec.ts b/packages/graph/lib/commands/QUERY.spec.ts index 44492d75d27..c8a9a20372b 100644 --- a/packages/graph/lib/commands/QUERY.spec.ts +++ b/packages/graph/lib/commands/QUERY.spec.ts @@ -5,18 +5,13 @@ import { transformArguments } from './QUERY'; describe('QUERY', () => { it('transformArguments', () => { assert.deepEqual( - transformArguments('key', '*', 100), - ['GRAPH.QUERY', 'key', '*', '100'] + transformArguments('key', 'query'), + ['GRAPH.QUERY', 'key', 'query'] ); }); testUtils.testWithClient('client.graph.query', async client => { - await client.graph.query('key', - "CREATE (r:human {name:'roi', age:34}), (a:human {name:'amit', age:32}), (r)-[:knows]->(a)" - ); - const reply = await client.graph.query('key', - "MATCH (r:human)-[:knows]->(a:human) RETURN r.age, r.name" - ); - assert.equal(reply.data.length, 1); + const { data } = await client.graph.query('key', 'RETURN 0'); + assert.deepEqual(data, [[0]]); }, GLOBAL.SERVERS.OPEN); }); diff --git a/packages/graph/lib/commands/QUERY.ts b/packages/graph/lib/commands/QUERY.ts index 408443186d5..741cc6a3601 100644 --- a/packages/graph/lib/commands/QUERY.ts +++ b/packages/graph/lib/commands/QUERY.ts @@ -1,24 +1,26 @@ import { RedisCommandArgument, RedisCommandArguments } from '@redis/client/dist/lib/commands/index'; -import { pushQueryArguments } from '.'; +import { pushQueryArguments, QueryOptionsBackwardCompatible } from '.'; export const FIRST_KEY_INDEX = 1; export function transformArguments( graph: RedisCommandArgument, query: RedisCommandArgument, - timeout?: number + options?: QueryOptionsBackwardCompatible, + compact?: boolean ): RedisCommandArguments { return pushQueryArguments( ['GRAPH.QUERY'], graph, query, - timeout + options, + compact ); } type Headers = Array; -type Data = Array>; +type Data = Array; type Metadata = Array; @@ -26,16 +28,26 @@ type QueryRawReply = [ headers: Headers, data: Data, metadata: Metadata +] | [ + metadata: Metadata ]; -interface QueryReply { - headers: Headers, - data: Data, - metadata: Metadata +export type QueryReply = { + headers: undefined; + data: undefined; + metadata: Metadata; +} | { + headers: Headers; + data: Data; + metadata: Metadata; }; export function transformReply(reply: QueryRawReply): QueryReply { - return { + return reply.length === 1 ? { + headers: undefined, + data: undefined, + metadata: reply[0] + } : { headers: reply[0], data: reply[1], metadata: reply[2] diff --git a/packages/graph/lib/commands/QUERY_RO.spec.ts b/packages/graph/lib/commands/QUERY_RO.spec.ts deleted file mode 100644 index 78814603aca..00000000000 --- a/packages/graph/lib/commands/QUERY_RO.spec.ts +++ /dev/null @@ -1,22 +0,0 @@ -import { strict as assert } from 'assert'; -import testUtils, { GLOBAL } from '../test-utils'; -import { transformArguments } from './QUERY_RO'; - -describe('QUERY_RO', () => { - it('transformArguments', () => { - assert.deepEqual( - transformArguments('key', '*', 100), - ['GRAPH.RO_QUERY', 'key', '*', '100'] - ); - }); - - testUtils.testWithClient('client.graph.queryRo', async client => { - await client.graph.query('key', - "CREATE (r:human {name:'roi', age:34}), (a:human {name:'amit', age:32}), (r)-[:knows]->(a)" - ); - const reply = await client.graph.queryRo('key', - "MATCH (r:human)-[:knows]->(a:human) RETURN r.age, r.name" - ); - assert.equal(reply.data.length, 1); - }, GLOBAL.SERVERS.OPEN); -}); \ No newline at end of file diff --git a/packages/graph/lib/commands/RO_QUERY.spec.ts b/packages/graph/lib/commands/RO_QUERY.spec.ts new file mode 100644 index 00000000000..0fbaeb69537 --- /dev/null +++ b/packages/graph/lib/commands/RO_QUERY.spec.ts @@ -0,0 +1,17 @@ +import { strict as assert } from 'assert'; +import testUtils, { GLOBAL } from '../test-utils'; +import { transformArguments } from './RO_QUERY'; + +describe('RO_QUERY', () => { + it('transformArguments', () => { + assert.deepEqual( + transformArguments('key', 'query'), + ['GRAPH.RO_QUERY', 'key', 'query'] + ); + }); + + testUtils.testWithClient('client.graph.roQuery', async client => { + const { data } = await client.graph.roQuery('key', 'RETURN 0'); + assert.deepEqual(data, [[0]]); + }, GLOBAL.SERVERS.OPEN); +}); \ No newline at end of file diff --git a/packages/graph/lib/commands/QUERY_RO.ts b/packages/graph/lib/commands/RO_QUERY.ts similarity index 72% rename from packages/graph/lib/commands/QUERY_RO.ts rename to packages/graph/lib/commands/RO_QUERY.ts index 2090f593c72..d4dda9dee27 100644 --- a/packages/graph/lib/commands/QUERY_RO.ts +++ b/packages/graph/lib/commands/RO_QUERY.ts @@ -1,5 +1,5 @@ import { RedisCommandArgument, RedisCommandArguments } from '@redis/client/dist/lib/commands'; -import { pushQueryArguments } from '.'; +import { pushQueryArguments, QueryOptionsBackwardCompatible } from '.'; export { FIRST_KEY_INDEX } from './QUERY'; @@ -8,13 +8,15 @@ export const IS_READ_ONLY = true; export function transformArguments( graph: RedisCommandArgument, query: RedisCommandArgument, - timeout?: number + options?: QueryOptionsBackwardCompatible, + compact?: boolean ): RedisCommandArguments { return pushQueryArguments( ['GRAPH.RO_QUERY'], graph, query, - timeout + options, + compact ); } diff --git a/packages/graph/lib/commands/index.spec.ts b/packages/graph/lib/commands/index.spec.ts new file mode 100644 index 00000000000..a688c49dd39 --- /dev/null +++ b/packages/graph/lib/commands/index.spec.ts @@ -0,0 +1,62 @@ +import { strict as assert } from 'assert'; +import { pushQueryArguments } from '.'; + +describe('pushQueryArguments', () => { + it('simple', () => { + assert.deepEqual( + pushQueryArguments(['GRAPH.QUERY'], 'graph', 'query'), + ['GRAPH.QUERY', 'graph', 'query'] + ); + }); + + describe('params', () => { + it('all types', () => { + assert.deepEqual( + pushQueryArguments(['GRAPH.QUERY'], 'graph', 'query', { + params: { + null: null, + string: '"\\', + number: 0, + boolean: false, + array: [0], + object: {a: 0} + } + }), + ['GRAPH.QUERY', 'graph', 'CYPHER null=null string="\\"\\\\" number=0 boolean=false array=[0] object={a:0} query'] + ); + }); + + it('TypeError', () => { + assert.throws(() => { + pushQueryArguments(['GRAPH.QUERY'], 'graph', 'query', { + params: { + a: undefined as any + } + }) + }, TypeError); + }); + }); + + it('TIMEOUT backward compatible', () => { + assert.deepEqual( + pushQueryArguments(['GRAPH.QUERY'], 'graph', 'query', 1), + ['GRAPH.QUERY', 'graph', 'query', 'TIMEOUT', '1'] + ); + }); + + it('TIMEOUT', () => { + assert.deepEqual( + pushQueryArguments(['GRAPH.QUERY'], 'graph', 'query', { + TIMEOUT: 1 + }), + ['GRAPH.QUERY', 'graph', 'query', 'TIMEOUT', '1'] + ); + }); + + it('compact', () => { + assert.deepEqual( + pushQueryArguments(['GRAPH.QUERY'], 'graph', 'query', undefined, true), + ['GRAPH.QUERY', 'graph', 'query', '--compact'] + ); + }); +}); diff --git a/packages/graph/lib/commands/index.ts b/packages/graph/lib/commands/index.ts index afc025e68cf..2acf9089ee6 100644 --- a/packages/graph/lib/commands/index.ts +++ b/packages/graph/lib/commands/index.ts @@ -4,8 +4,8 @@ import * as DELETE from './DELETE'; import * as EXPLAIN from './EXPLAIN'; import * as LIST from './LIST'; import * as PROFILE from './PROFILE'; -import * as QUERY_RO from './QUERY_RO'; import * as QUERY from './QUERY'; +import * as RO_QUERY from './RO_QUERY'; import * as SLOWLOG from './SLOWLOG'; import { RedisCommandArgument, RedisCommandArguments } from '@redis/client/dist/lib/commands'; @@ -22,28 +22,93 @@ export default { list: LIST, PROFILE, profile: PROFILE, - QUERY_RO, - queryRo: QUERY_RO, QUERY, query: QUERY, + RO_QUERY, + roQuery: RO_QUERY, SLOWLOG, slowLog: SLOWLOG }; +type QueryParam = null | string | number | boolean | QueryParams | Array; + +type QueryParams = { + [key: string]: QueryParam; +}; + +export interface QueryOptions { + params?: QueryParams; + TIMEOUT?: number; +} + +export type QueryOptionsBackwardCompatible = QueryOptions | number; + export function pushQueryArguments( args: RedisCommandArguments, graph: RedisCommandArgument, query: RedisCommandArgument, - timeout?: number + options?: QueryOptionsBackwardCompatible, + compact?: boolean ): RedisCommandArguments { - args.push( - graph, - query - ); + args.push(graph); - if (timeout !== undefined) { - args.push(timeout.toString()); + if (typeof options === 'number') { + args.push(query); + pushTimeout(args, options); + } else { + args.push( + options?.params ? + `CYPHER ${queryParamsToString(options.params)} ${query}` : + query + ); + + if (options?.TIMEOUT !== undefined) { + pushTimeout(args, options.TIMEOUT); + } + } + + if (compact) { + args.push('--compact'); } return args; -} \ No newline at end of file +} + +function pushTimeout(args: RedisCommandArguments, timeout: number): void { + args.push('TIMEOUT', timeout.toString()); +} + +function queryParamsToString(params: QueryParams): string { + const parts = []; + for (const [key, value] of Object.entries(params)) { + parts.push(`${key}=${queryParamToString(value)}`); + } + return parts.join(' '); +} + +function queryParamToString(param: QueryParam): string { + if (param === null) { + return 'null'; + } + + switch (typeof param) { + case 'string': + return `"${param.replace(/["\\]/g, '\\$&')}"`; + + case 'number': + case 'boolean': + return param.toString(); + } + + if (Array.isArray(param)) { + return `[${param.map(queryParamToString).join(',')}]`; + } else if (typeof param === 'object') { + const body = []; + for (const [key, value] of Object.entries(param)) { + body.push(`${key}:${queryParamToString(value)}`); + } + return `{${body.join(',')}}`; + } else { + throw new TypeError(`Unexpected param type ${typeof param} ${param}`) + } +} diff --git a/packages/graph/lib/graph.spec.ts b/packages/graph/lib/graph.spec.ts new file mode 100644 index 00000000000..51912356d3a --- /dev/null +++ b/packages/graph/lib/graph.spec.ts @@ -0,0 +1,148 @@ +import { strict as assert } from 'assert'; +import testUtils, { GLOBAL } from './test-utils'; +import Graph from './graph'; + +describe('Graph', () => { + testUtils.testWithClient('null', async client => { + const graph = new Graph(client as any, 'graph'), + { data } = await graph.roQuery('RETURN null AS key'); + + assert.deepEqual( + data, + [{ key: null }] + ); + }, GLOBAL.SERVERS.OPEN); + + testUtils.testWithClient('string', async client => { + const graph = new Graph(client as any, 'graph'), + { data } = await graph.roQuery('RETURN "string" AS key'); + + assert.deepEqual( + data, + [{ key: 'string' }] + ); + }, GLOBAL.SERVERS.OPEN); + + testUtils.testWithClient('integer', async client => { + const graph = new Graph(client as any, 'graph'), + { data } = await graph.roQuery('RETURN 0 AS key'); + + assert.deepEqual( + data, + [{ key: 0 }] + ); + }, GLOBAL.SERVERS.OPEN); + + testUtils.testWithClient('boolean', async client => { + const graph = new Graph(client as any, 'graph'), + { data } = await graph.roQuery('RETURN false AS key'); + + assert.deepEqual( + data, + [{ key: false }] + ); + }, GLOBAL.SERVERS.OPEN); + + testUtils.testWithClient('double', async client => { + const graph = new Graph(client as any, 'graph'), + { data } = await graph.roQuery('RETURN 0.1 AS key'); + + assert.deepEqual( + data, + [{ key: 0.1 }] + ); + }, GLOBAL.SERVERS.OPEN); + + testUtils.testWithClient('array', async client => { + const graph = new Graph(client as any, 'graph'), + { data } = await graph.roQuery('RETURN [null] AS key'); + + assert.deepEqual( + data, + [{ key: [null] }] + ); + }, GLOBAL.SERVERS.OPEN); + + testUtils.testWithClient('edge', async client => { + const graph = new Graph(client as any, 'graph'); + + // check with and without metadata cache + for (let i = 0; i < 2; i++) { + const { data } = await graph.query('CREATE ()-[edge :edge]->() RETURN edge'); + assert.ok(Array.isArray(data)); + assert.equal(data.length, 1); + assert.equal(typeof data[0].edge.id, 'number'); + assert.equal(data[0].edge.relationshipType, 'edge'); + assert.equal(typeof data[0].edge.sourceId, 'number'); + assert.equal(typeof data[0].edge.destinationId, 'number'); + assert.deepEqual(data[0].edge.properties, {}); + } + + }, GLOBAL.SERVERS.OPEN); + + testUtils.testWithClient('node', async client => { + const graph = new Graph(client as any, 'graph'); + + // check with and without metadata cache + for (let i = 0; i < 2; i++) { + const { data } = await graph.query('CREATE (node :node { p: 0 }) RETURN node'); + assert.ok(Array.isArray(data)); + assert.equal(data.length, 1); + assert.equal(typeof data[0].node.id, 'number'); + assert.deepEqual(data[0].node.labels, ['node']); + assert.deepEqual(data[0].node.properties, { p: 0 }); + } + }, GLOBAL.SERVERS.OPEN); + + testUtils.testWithClient('path', async client => { + const graph = new Graph(client as any, 'graph'), + [, { data }] = await Promise.all([ + await graph.query('CREATE ()-[:edge]->()'), + await graph.roQuery('MATCH path = ()-[:edge]->() RETURN path') + ]); + + assert.ok(Array.isArray(data)); + assert.equal(data.length, 1); + + assert.ok(Array.isArray(data[0].path.nodes)); + assert.equal(data[0].path.nodes.length, 2); + for (const node of data[0].path.nodes) { + assert.equal(typeof node.id, 'number'); + assert.deepEqual(node.labels, []); + assert.deepEqual(node.properties, {}); + } + + assert.ok(Array.isArray(data[0].path.edges)); + assert.equal(data[0].path.edges.length, 1); + for (const edge of data[0].path.edges) { + assert.equal(typeof edge.id, 'number'); + assert.equal(edge.relationshipType, 'edge'); + assert.equal(typeof edge.sourceId, 'number'); + assert.equal(typeof edge.destinationId, 'number'); + assert.deepEqual(edge.properties, {}); + } + }, GLOBAL.SERVERS.OPEN); + + testUtils.testWithClient('map', async client => { + const graph = new Graph(client as any, 'graph'), + { data } = await graph.roQuery('RETURN { key: "value" } AS map'); + + assert.deepEqual(data, [{ + map: { + key: 'value' + } + }]); + }, GLOBAL.SERVERS.OPEN); + + testUtils.testWithClient('point', async client => { + const graph = new Graph(client as any, 'graph'), + { data } = await graph.roQuery('RETURN point({ latitude: 1, longitude: 2 }) AS point'); + + assert.deepEqual(data, [{ + point: { + latitude: 1, + longitude: 2 + } + }]); + }, GLOBAL.SERVERS.OPEN); +}); diff --git a/packages/graph/lib/graph.ts b/packages/graph/lib/graph.ts new file mode 100644 index 00000000000..5baff1dae29 --- /dev/null +++ b/packages/graph/lib/graph.ts @@ -0,0 +1,359 @@ +import { RedisClientType } from '@redis/client/dist/lib/client/index'; +import { RedisCommandArgument, RedisFunctions, RedisScripts } from '@redis/client/dist/lib/commands'; +import { QueryOptions } from './commands'; +import { QueryReply } from './commands/QUERY'; + +interface GraphMetadata { + labels: Array; + relationshipTypes: Array; + propertyKeys: Array; +} + +// https://github.com/RedisGraph/RedisGraph/blob/master/src/resultset/formatters/resultset_formatter.h#L20 +enum GraphValueTypes { + UNKNOWN = 0, + NULL = 1, + STRING = 2, + INTEGER = 3, + BOOLEAN = 4, + DOUBLE = 5, + ARRAY = 6, + EDGE = 7, + NODE = 8, + PATH = 9, + MAP = 10, + POINT = 11 +} + +type GraphEntityRawProperties = Array<[ + id: number, + ...value: GraphRawValue +]>; + +type GraphEdgeRawValue = [ + GraphValueTypes.EDGE, + [ + id: number, + relationshipTypeId: number, + sourceId: number, + destinationId: number, + properties: GraphEntityRawProperties + ] +]; + +type GraphNodeRawValue = [ + GraphValueTypes.NODE, + [ + id: number, + labelIds: Array, + properties: GraphEntityRawProperties + ] +]; + +type GraphPathRawValue = [ + GraphValueTypes.PATH, + [ + nodes: [ + GraphValueTypes.ARRAY, + Array + ], + edges: [ + GraphValueTypes.ARRAY, + Array + ] + ] +]; + +type GraphMapRawValue = [ + GraphValueTypes.MAP, + Array +]; + +type GraphRawValue = [ + GraphValueTypes.NULL, + null +] | [ + GraphValueTypes.STRING, + string +] | [ + GraphValueTypes.INTEGER, + number +] | [ + GraphValueTypes.BOOLEAN, + string +] | [ + GraphValueTypes.DOUBLE, + string +] | [ + GraphValueTypes.ARRAY, + Array +] | GraphEdgeRawValue | GraphNodeRawValue | GraphPathRawValue | GraphMapRawValue | [ + GraphValueTypes.POINT, + [ + latitude: string, + longitude: string + ] +]; + +type GraphEntityProperties = Record; + +interface GraphEdge { + id: number; + relationshipType: string; + sourceId: number; + destinationId: number; + properties: GraphEntityProperties; +} + +interface GraphNode { + id: number; + labels: Array; + properties: GraphEntityProperties; +} + +interface GraphPath { + nodes: Array; + edges: Array; +} + +type GraphMap = { + [key: string]: GraphValue; +}; + +type GraphValue = null | string | number | boolean | Array | { +} | GraphEdge | GraphNode | GraphPath | GraphMap | { + latitude: string; + longitude: string; +}; + +type GraphReply = Omit & { + data?: Array; +}; + +type GraphClientType = RedisClientType<{ + graph: { + query: typeof import('./commands/QUERY'), + roQuery: typeof import('./commands/RO_QUERY') + } +}, RedisFunctions, RedisScripts>; + +export default class Graph { + #client: GraphClientType; + #name: string; + #metadata?: GraphMetadata; + + constructor( + client: GraphClientType, + name: string + ) { + this.#client = client; + this.#name = name; + } + + async query( + query: RedisCommandArgument, + options?: QueryOptions + ) { + return this.#parseReply( + await this.#client.graph.query( + this.#name, + query, + options, + true + ) + ); + } + + async roQuery( + query: RedisCommandArgument, + options?: QueryOptions + ) { + return this.#parseReply( + await this.#client.graph.roQuery( + this.#name, + query, + options, + true + ) + ); + } + + #setMetadataPromise?: Promise; + + #updateMetadata(): Promise { + this.#setMetadataPromise ??= this.#setMetadata() + .finally(() => this.#setMetadataPromise = undefined); + return this.#setMetadataPromise; + } + + // DO NOT use directly, use #updateMetadata instead + async #setMetadata(): Promise { + const [labels, relationshipTypes, propertyKeys] = await Promise.all([ + this.#client.graph.roQuery(this.#name, 'CALL db.labels()'), + this.#client.graph.roQuery(this.#name, 'CALL db.relationshipTypes()'), + this.#client.graph.roQuery(this.#name, 'CALL db.propertyKeys()') + ]); + + this.#metadata = { + labels: this.#cleanMetadataArray(labels.data as Array<[string]>), + relationshipTypes: this.#cleanMetadataArray(relationshipTypes.data as Array<[string]>), + propertyKeys: this.#cleanMetadataArray(propertyKeys.data as Array<[string]>) + }; + + return this.#metadata; + } + + #cleanMetadataArray(arr: Array<[string]>): Array { + return arr.map(([value]) => value); + } + + #getMetadata( + key: T, + id: number + ): GraphMetadata[T][number] | Promise { + return this.#metadata?.[key][id] ?? this.#getMetadataAsync(key, id); + } + + // DO NOT use directly, use #getMetadata instead + async #getMetadataAsync( + key: T, + id: number + ): Promise { + const value = (await this.#updateMetadata())[key][id]; + if (value === undefined) throw new Error(`Cannot find value from ${key}[${id}]`); + return value; + } + + async #parseReply(reply: QueryReply): Promise> { + if (!reply.data) return reply; + + const promises: Array> = [], + parsed = { + metadata: reply.metadata, + data: reply.data!.map((row: any) => { + const data: Record = {}; + for (let i = 0; i < row.length; i++) { + data[reply.headers[i][1]] = this.#parseValue(row[i], promises); + } + + return data as unknown as T; + }) + }; + + if (promises.length) await Promise.all(promises); + + return parsed; + } + + #parseValue([valueType, value]: GraphRawValue, promises: Array>): GraphValue { + switch (valueType) { + case GraphValueTypes.NULL: + return null; + + case GraphValueTypes.STRING: + case GraphValueTypes.INTEGER: + return value; + + case GraphValueTypes.BOOLEAN: + return value === 'true'; + + case GraphValueTypes.DOUBLE: + return parseFloat(value); + + case GraphValueTypes.ARRAY: + return value.map(x => this.#parseValue(x, promises)); + + case GraphValueTypes.EDGE: + return this.#parseEdge(value, promises); + + case GraphValueTypes.NODE: + return this.#parseNode(value, promises); + + case GraphValueTypes.PATH: + return { + nodes: value[0][1].map(([, node]) => this.#parseNode(node, promises)), + edges: value[1][1].map(([, edge]) => this.#parseEdge(edge, promises)) + }; + + case GraphValueTypes.MAP: + const map: GraphMap = {}; + for (let i = 0; i < value.length; i++) { + map[value[i++] as string] = this.#parseValue(value[i] as GraphRawValue, promises); + } + + return map; + + case GraphValueTypes.POINT: + return { + latitude: parseFloat(value[0]), + longitude: parseFloat(value[1]) + }; + + default: + throw new Error(`unknown scalar type: ${valueType}`); + } + } + + #parseEdge([ + id, + relationshipTypeId, + sourceId, + destinationId, + properties + ]: GraphEdgeRawValue[1], promises: Array>): GraphEdge { + const edge = { + id, + sourceId, + destinationId, + properties: this.#parseProperties(properties, promises) + } as GraphEdge; + + const relationshipType = this.#getMetadata('relationshipTypes', relationshipTypeId); + if (relationshipType instanceof Promise) { + promises.push( + relationshipType.then(value => edge.relationshipType = value) + ); + } else { + edge.relationshipType = relationshipType; + } + + return edge; + } + + #parseNode([ + id, + labelIds, + properties + ]: GraphNodeRawValue[1], promises: Array>): GraphNode { + const labels = new Array(labelIds.length); + for (let i = 0; i < labelIds.length; i++) { + const value = this.#getMetadata('labels', labelIds[i]); + if (value instanceof Promise) { + promises.push(value.then(value => labels[i] = value)); + } else { + labels[i] = value; + } + } + + return { + id, + labels, + properties: this.#parseProperties(properties, promises) + }; + } + + #parseProperties(raw: GraphEntityRawProperties, promises: Array>): GraphEntityProperties { + const parsed: GraphEntityProperties = {}; + for (const [id, type, value] of raw) { + const parsedValue = this.#parseValue([type, value] as GraphRawValue, promises), + key = this.#getMetadata('propertyKeys', id); + if (key instanceof Promise) { + promises.push(key.then(key => parsed[key] = parsedValue)); + } else { + parsed[key] = parsedValue; + } + } + + return parsed; + } +} diff --git a/packages/graph/lib/index.ts b/packages/graph/lib/index.ts index bc0e103e8c8..e9f15ab1fd9 100644 --- a/packages/graph/lib/index.ts +++ b/packages/graph/lib/index.ts @@ -1 +1,2 @@ export { default } from './commands'; +export { default as Graph } from './graph'; diff --git a/packages/test-utils/lib/dockers.ts b/packages/test-utils/lib/dockers.ts index d6da977d93f..8f0be95b094 100644 --- a/packages/test-utils/lib/dockers.ts +++ b/packages/test-utils/lib/dockers.ts @@ -1,8 +1,8 @@ import { createConnection } from 'net'; import { once } from 'events'; -import { RedisModules, RedisFunctions, RedisScripts } from '@redis/client/lib/commands'; -import RedisClient, { RedisClientType } from '@redis/client/lib/client'; -import { promiseTimeout } from '@redis/client/lib/utils'; +import { RedisModules, RedisFunctions, RedisScripts } from '@redis/client/dist/lib/commands'; +import RedisClient, { RedisClientType } from '@redis/client/dist/lib/client'; +import { promiseTimeout } from '@redis/client/dist/lib/utils'; import * as path from 'path'; import { promisify } from 'util'; import { exec } from 'child_process'; From be90e623609685916b15b514bc373d813495110c Mon Sep 17 00:00:00 2001 From: Leibale Eidelman Date: Tue, 1 Nov 2022 15:45:47 -0400 Subject: [PATCH 1268/1748] Add support for T-Digest (#2214) * wip * close #2216 - add support for TDIGEST.MERGESTORE and make compression optional on TDIGEST.CREATE * fix some tdigest commands, use bloom edge docker * fix index.ts * 2.4-RC2 (v2.4.1) * fix some commands and tests * clean code --- packages/bloom/lib/commands/index.ts | 18 +++-- .../bloom/lib/commands/t-digest/ADD.spec.ts | 21 +++++ packages/bloom/lib/commands/t-digest/ADD.ts | 17 ++++ .../lib/commands/t-digest/BYRANK.spec.ts | 21 +++++ .../bloom/lib/commands/t-digest/BYRANK.ts | 19 +++++ .../lib/commands/t-digest/BYREVRANK.spec.ts | 21 +++++ .../bloom/lib/commands/t-digest/BYREVRANK.ts | 19 +++++ .../bloom/lib/commands/t-digest/CDF.spec.ts | 21 +++++ packages/bloom/lib/commands/t-digest/CDF.ts | 19 +++++ .../lib/commands/t-digest/CREATE.spec.ts | 30 +++++++ .../bloom/lib/commands/t-digest/CREATE.ts | 16 ++++ .../bloom/lib/commands/t-digest/INFO.spec.ts | 25 ++++++ packages/bloom/lib/commands/t-digest/INFO.ts | 51 ++++++++++++ .../bloom/lib/commands/t-digest/MAX.spec.ts | 21 +++++ packages/bloom/lib/commands/t-digest/MAX.ts | 14 ++++ .../bloom/lib/commands/t-digest/MERGE.spec.ts | 50 ++++++++++++ packages/bloom/lib/commands/t-digest/MERGE.ts | 30 +++++++ .../bloom/lib/commands/t-digest/MIN.spec.ts | 21 +++++ packages/bloom/lib/commands/t-digest/MIN.ts | 14 ++++ .../lib/commands/t-digest/QUANTILE.spec.ts | 24 ++++++ .../bloom/lib/commands/t-digest/QUANTILE.ts | 23 ++++++ .../bloom/lib/commands/t-digest/RANK.spec.ts | 21 +++++ packages/bloom/lib/commands/t-digest/RANK.ts | 19 +++++ .../bloom/lib/commands/t-digest/RESET.spec.ts | 21 +++++ packages/bloom/lib/commands/t-digest/RESET.ts | 9 +++ .../lib/commands/t-digest/REVRANK.spec.ts | 21 +++++ .../bloom/lib/commands/t-digest/REVRANK.ts | 19 +++++ .../commands/t-digest/TRIMMED_MEAN.spec.ts | 21 +++++ .../lib/commands/t-digest/TRIMMED_MEAN.ts | 20 +++++ .../bloom/lib/commands/t-digest/index.spec.ts | 55 +++++++++++++ packages/bloom/lib/commands/t-digest/index.ts | 81 +++++++++++++++++++ packages/bloom/lib/test-utils.ts | 2 +- packages/test-utils/lib/index.ts | 30 ++++--- 33 files changed, 794 insertions(+), 20 deletions(-) create mode 100644 packages/bloom/lib/commands/t-digest/ADD.spec.ts create mode 100644 packages/bloom/lib/commands/t-digest/ADD.ts create mode 100644 packages/bloom/lib/commands/t-digest/BYRANK.spec.ts create mode 100644 packages/bloom/lib/commands/t-digest/BYRANK.ts create mode 100644 packages/bloom/lib/commands/t-digest/BYREVRANK.spec.ts create mode 100644 packages/bloom/lib/commands/t-digest/BYREVRANK.ts create mode 100644 packages/bloom/lib/commands/t-digest/CDF.spec.ts create mode 100644 packages/bloom/lib/commands/t-digest/CDF.ts create mode 100644 packages/bloom/lib/commands/t-digest/CREATE.spec.ts create mode 100644 packages/bloom/lib/commands/t-digest/CREATE.ts create mode 100644 packages/bloom/lib/commands/t-digest/INFO.spec.ts create mode 100644 packages/bloom/lib/commands/t-digest/INFO.ts create mode 100644 packages/bloom/lib/commands/t-digest/MAX.spec.ts create mode 100644 packages/bloom/lib/commands/t-digest/MAX.ts create mode 100644 packages/bloom/lib/commands/t-digest/MERGE.spec.ts create mode 100644 packages/bloom/lib/commands/t-digest/MERGE.ts create mode 100644 packages/bloom/lib/commands/t-digest/MIN.spec.ts create mode 100644 packages/bloom/lib/commands/t-digest/MIN.ts create mode 100644 packages/bloom/lib/commands/t-digest/QUANTILE.spec.ts create mode 100644 packages/bloom/lib/commands/t-digest/QUANTILE.ts create mode 100644 packages/bloom/lib/commands/t-digest/RANK.spec.ts create mode 100644 packages/bloom/lib/commands/t-digest/RANK.ts create mode 100644 packages/bloom/lib/commands/t-digest/RESET.spec.ts create mode 100644 packages/bloom/lib/commands/t-digest/RESET.ts create mode 100644 packages/bloom/lib/commands/t-digest/REVRANK.spec.ts create mode 100644 packages/bloom/lib/commands/t-digest/REVRANK.ts create mode 100644 packages/bloom/lib/commands/t-digest/TRIMMED_MEAN.spec.ts create mode 100644 packages/bloom/lib/commands/t-digest/TRIMMED_MEAN.ts create mode 100644 packages/bloom/lib/commands/t-digest/index.spec.ts create mode 100644 packages/bloom/lib/commands/t-digest/index.ts diff --git a/packages/bloom/lib/commands/index.ts b/packages/bloom/lib/commands/index.ts index 665664a75b0..cea55b2a7c0 100644 --- a/packages/bloom/lib/commands/index.ts +++ b/packages/bloom/lib/commands/index.ts @@ -1,11 +1,13 @@ -import Bloom from './bloom'; -import CountMinSketch from './count-min-sketch'; -import Cuckoo from './cuckoo'; -import TopK from './top-k'; +import bf from './bloom'; +import cms from './count-min-sketch'; +import cf from './cuckoo'; +import tDigest from './t-digest'; +import topK from './top-k'; export default { - bf: Bloom, - cms: CountMinSketch, - cf: Cuckoo, - topK: TopK + bf, + cms, + cf, + tDigest, + topK }; diff --git a/packages/bloom/lib/commands/t-digest/ADD.spec.ts b/packages/bloom/lib/commands/t-digest/ADD.spec.ts new file mode 100644 index 00000000000..3e1dbff7f27 --- /dev/null +++ b/packages/bloom/lib/commands/t-digest/ADD.spec.ts @@ -0,0 +1,21 @@ +import { strict as assert } from 'assert'; +import testUtils, { GLOBAL } from '../../test-utils'; +import { transformArguments } from './ADD'; + +describe('TDIGEST.ADD', () => { + it('transformArguments', () => { + assert.deepEqual( + transformArguments('key', [1, 2]), + ['TDIGEST.ADD', 'key', '1', '2'] + ); + }); + + testUtils.testWithClient('client.tDigest.add', async client => { + const [ , reply ] = await Promise.all([ + client.tDigest.create('key'), + client.tDigest.add('key', [1]) + ]); + + assert.equal(reply, 'OK'); + }, GLOBAL.SERVERS.OPEN); +}); diff --git a/packages/bloom/lib/commands/t-digest/ADD.ts b/packages/bloom/lib/commands/t-digest/ADD.ts new file mode 100644 index 00000000000..941e8531003 --- /dev/null +++ b/packages/bloom/lib/commands/t-digest/ADD.ts @@ -0,0 +1,17 @@ +import { RedisCommandArgument, RedisCommandArguments } from '@redis/client/dist/lib/commands'; + +export const FIRST_KEY_INDEX = 1; + +export function transformArguments( + key: RedisCommandArgument, + values: Array +): RedisCommandArguments { + const args = ['TDIGEST.ADD', key]; + for (const item of values) { + args.push(item.toString()); + } + + return args; +} + +export declare function transformReply(): 'OK'; diff --git a/packages/bloom/lib/commands/t-digest/BYRANK.spec.ts b/packages/bloom/lib/commands/t-digest/BYRANK.spec.ts new file mode 100644 index 00000000000..083f09d22af --- /dev/null +++ b/packages/bloom/lib/commands/t-digest/BYRANK.spec.ts @@ -0,0 +1,21 @@ +import { strict as assert } from 'assert'; +import testUtils, { GLOBAL } from '../../test-utils'; +import { transformArguments } from './BYRANK'; + +describe('TDIGEST.BYRANK', () => { + it('transformArguments', () => { + assert.deepEqual( + transformArguments('key', [1, 2]), + ['TDIGEST.BYRANK', 'key', '1', '2'] + ); + }); + + testUtils.testWithClient('client.tDigest.byRank', async client => { + const [ , reply ] = await Promise.all([ + client.tDigest.create('key'), + client.tDigest.byRank('key', [1]) + ]); + + assert.deepEqual(reply, [NaN]); + }, GLOBAL.SERVERS.OPEN); +}); diff --git a/packages/bloom/lib/commands/t-digest/BYRANK.ts b/packages/bloom/lib/commands/t-digest/BYRANK.ts new file mode 100644 index 00000000000..5684385b4d3 --- /dev/null +++ b/packages/bloom/lib/commands/t-digest/BYRANK.ts @@ -0,0 +1,19 @@ +import { RedisCommandArgument, RedisCommandArguments } from '@redis/client/dist/lib/commands'; + +export const FIRST_KEY_INDEX = 1; + +export const IS_READ_ONLY = true; + +export function transformArguments( + key: RedisCommandArgument, + ranks: Array +): RedisCommandArguments { + const args = ['TDIGEST.BYRANK', key]; + for (const rank of ranks) { + args.push(rank.toString()); + } + + return args; +} + +export { transformDoublesReply as transformReply } from '.'; diff --git a/packages/bloom/lib/commands/t-digest/BYREVRANK.spec.ts b/packages/bloom/lib/commands/t-digest/BYREVRANK.spec.ts new file mode 100644 index 00000000000..c094f36e71d --- /dev/null +++ b/packages/bloom/lib/commands/t-digest/BYREVRANK.spec.ts @@ -0,0 +1,21 @@ +import { strict as assert } from 'assert'; +import testUtils, { GLOBAL } from '../../test-utils'; +import { transformArguments } from './BYREVRANK'; + +describe('TDIGEST.BYREVRANK', () => { + it('transformArguments', () => { + assert.deepEqual( + transformArguments('key', [1, 2]), + ['TDIGEST.BYREVRANK', 'key', '1', '2'] + ); + }); + + testUtils.testWithClient('client.tDigest.byRevRank', async client => { + const [ , reply ] = await Promise.all([ + client.tDigest.create('key'), + client.tDigest.byRevRank('key', [1]) + ]); + + assert.deepEqual(reply, [NaN]); + }, GLOBAL.SERVERS.OPEN); +}); diff --git a/packages/bloom/lib/commands/t-digest/BYREVRANK.ts b/packages/bloom/lib/commands/t-digest/BYREVRANK.ts new file mode 100644 index 00000000000..3dcf3a973c4 --- /dev/null +++ b/packages/bloom/lib/commands/t-digest/BYREVRANK.ts @@ -0,0 +1,19 @@ +import { RedisCommandArgument, RedisCommandArguments } from '@redis/client/dist/lib/commands'; + +export const FIRST_KEY_INDEX = 1; + +export const IS_READ_ONLY = true; + +export function transformArguments( + key: RedisCommandArgument, + ranks: Array +): RedisCommandArguments { + const args = ['TDIGEST.BYREVRANK', key]; + for (const rank of ranks) { + args.push(rank.toString()); + } + + return args; +} + +export { transformDoublesReply as transformReply } from '.'; diff --git a/packages/bloom/lib/commands/t-digest/CDF.spec.ts b/packages/bloom/lib/commands/t-digest/CDF.spec.ts new file mode 100644 index 00000000000..36d3564f62c --- /dev/null +++ b/packages/bloom/lib/commands/t-digest/CDF.spec.ts @@ -0,0 +1,21 @@ +import { strict as assert } from 'assert'; +import testUtils, { GLOBAL } from '../../test-utils'; +import { transformArguments } from './CDF'; + +describe('TDIGEST.CDF', () => { + it('transformArguments', () => { + assert.deepEqual( + transformArguments('key', [1, 2]), + ['TDIGEST.CDF', 'key', '1', '2'] + ); + }); + + testUtils.testWithClient('client.tDigest.cdf', async client => { + const [ , reply ] = await Promise.all([ + client.tDigest.create('key'), + client.tDigest.cdf('key', [1]) + ]); + + assert.deepEqual(reply, [NaN]); + }, GLOBAL.SERVERS.OPEN); +}); diff --git a/packages/bloom/lib/commands/t-digest/CDF.ts b/packages/bloom/lib/commands/t-digest/CDF.ts new file mode 100644 index 00000000000..fe7ece59d76 --- /dev/null +++ b/packages/bloom/lib/commands/t-digest/CDF.ts @@ -0,0 +1,19 @@ +import { RedisCommandArgument, RedisCommandArguments } from '@redis/client/dist/lib/commands'; + +export const FIRST_KEY_INDEX = 1; + +export const IS_READ_ONLY = true; + +export function transformArguments( + key: RedisCommandArgument, + values: Array +): RedisCommandArguments { + const args = ['TDIGEST.CDF', key]; + for (const item of values) { + args.push(item.toString()); + } + + return args; +} + +export { transformDoublesReply as transformReply } from '.'; diff --git a/packages/bloom/lib/commands/t-digest/CREATE.spec.ts b/packages/bloom/lib/commands/t-digest/CREATE.spec.ts new file mode 100644 index 00000000000..4d329cc81ae --- /dev/null +++ b/packages/bloom/lib/commands/t-digest/CREATE.spec.ts @@ -0,0 +1,30 @@ +import { strict as assert } from 'assert'; +import testUtils, { GLOBAL } from '../../test-utils'; +import { transformArguments } from './CREATE'; + +describe('TDIGEST.CREATE', () => { + describe('transformArguments', () => { + it('without options', () => { + assert.deepEqual( + transformArguments('key'), + ['TDIGEST.CREATE', 'key'] + ); + }); + + it('with COMPRESSION', () => { + assert.deepEqual( + transformArguments('key', { + COMPRESSION: 100 + }), + ['TDIGEST.CREATE', 'key', 'COMPRESSION', '100'] + ); + }); + }); + + testUtils.testWithClient('client.tDigest.create', async client => { + assert.equal( + await client.tDigest.create('key'), + 'OK' + ); + }, GLOBAL.SERVERS.OPEN); +}); diff --git a/packages/bloom/lib/commands/t-digest/CREATE.ts b/packages/bloom/lib/commands/t-digest/CREATE.ts new file mode 100644 index 00000000000..1935d2973dc --- /dev/null +++ b/packages/bloom/lib/commands/t-digest/CREATE.ts @@ -0,0 +1,16 @@ +import { RedisCommandArgument, RedisCommandArguments } from '@redis/client/dist/lib/commands'; +import { CompressionOption, pushCompressionArgument } from '.'; + +export const FIRST_KEY_INDEX = 1; + +export function transformArguments( + key: RedisCommandArgument, + options?: CompressionOption +): RedisCommandArguments { + return pushCompressionArgument( + ['TDIGEST.CREATE', key], + options + ); +} + +export declare function transformReply(): 'OK'; diff --git a/packages/bloom/lib/commands/t-digest/INFO.spec.ts b/packages/bloom/lib/commands/t-digest/INFO.spec.ts new file mode 100644 index 00000000000..992fda6ea05 --- /dev/null +++ b/packages/bloom/lib/commands/t-digest/INFO.spec.ts @@ -0,0 +1,25 @@ +import { strict as assert } from 'assert'; +import testUtils, { GLOBAL } from '../../test-utils'; +import { transformArguments } from './INFO'; + +describe('TDIGEST.INFO', () => { + it('transformArguments', () => { + assert.deepEqual( + transformArguments('key'), + ['TDIGEST.INFO', 'key'] + ); + }); + + testUtils.testWithClient('client.tDigest.info', async client => { + await client.tDigest.create('key'); + + const info = await client.tDigest.info('key'); + assert(typeof info.capacity, 'number'); + assert(typeof info.mergedNodes, 'number'); + assert(typeof info.unmergedNodes, 'number'); + assert(typeof info.mergedWeight, 'number'); + assert(typeof info.unmergedWeight, 'number'); + assert(typeof info.totalCompression, 'number'); + assert(typeof info.totalCompression, 'number'); + }, GLOBAL.SERVERS.OPEN); +}); diff --git a/packages/bloom/lib/commands/t-digest/INFO.ts b/packages/bloom/lib/commands/t-digest/INFO.ts new file mode 100644 index 00000000000..44d2083524f --- /dev/null +++ b/packages/bloom/lib/commands/t-digest/INFO.ts @@ -0,0 +1,51 @@ +import { RedisCommandArgument, RedisCommandArguments } from '@redis/client/dist/lib/commands'; + +export const FIRST_KEY_INDEX = 1; + +export const IS_READ_ONLY = true; + +export function transformArguments(key: RedisCommandArgument): RedisCommandArguments { + return [ + 'TDIGEST.INFO', + key + ]; +} + +type InfoRawReply = [ + 'Compression', + number, + 'Capacity', + number, + 'Merged nodes', + number, + 'Unmerged nodes', + number, + 'Merged weight', + string, + 'Unmerged weight', + string, + 'Total compressions', + number +]; + +interface InfoReply { + comperssion: number; + capacity: number; + mergedNodes: number; + unmergedNodes: number; + mergedWeight: number; + unmergedWeight: number; + totalCompression: number; +} + +export function transformReply(reply: InfoRawReply): InfoReply { + return { + comperssion: reply[1], + capacity: reply[3], + mergedNodes: reply[5], + unmergedNodes: reply[7], + mergedWeight: Number(reply[9]), + unmergedWeight: Number(reply[11]), + totalCompression: reply[13] + }; +} \ No newline at end of file diff --git a/packages/bloom/lib/commands/t-digest/MAX.spec.ts b/packages/bloom/lib/commands/t-digest/MAX.spec.ts new file mode 100644 index 00000000000..bf850cbfd83 --- /dev/null +++ b/packages/bloom/lib/commands/t-digest/MAX.spec.ts @@ -0,0 +1,21 @@ +import { strict as assert } from 'assert'; +import testUtils, { GLOBAL } from '../../test-utils'; +import { transformArguments, transformReply } from './MAX'; + +describe('TDIGEST.MAX', () => { + it('transformArguments', () => { + assert.deepEqual( + transformArguments('key'), + ['TDIGEST.MAX', 'key'] + ); + }); + + testUtils.testWithClient('client.tDigest.max', async client => { + const [ , reply ] = await Promise.all([ + client.tDigest.create('key'), + client.tDigest.max('key') + ]); + + assert.deepEqual(reply, NaN); + }, GLOBAL.SERVERS.OPEN); +}); diff --git a/packages/bloom/lib/commands/t-digest/MAX.ts b/packages/bloom/lib/commands/t-digest/MAX.ts new file mode 100644 index 00000000000..90c42ec6067 --- /dev/null +++ b/packages/bloom/lib/commands/t-digest/MAX.ts @@ -0,0 +1,14 @@ +import { RedisCommandArgument, RedisCommandArguments } from '@redis/client/dist/lib/commands'; + +export const FIRST_KEY_INDEX = 1; + +export const IS_READ_ONLY = true; + +export function transformArguments(key: RedisCommandArgument): RedisCommandArguments { + return [ + 'TDIGEST.MAX', + key + ]; +} + +export { transformDoubleReply as transformReply } from '.'; diff --git a/packages/bloom/lib/commands/t-digest/MERGE.spec.ts b/packages/bloom/lib/commands/t-digest/MERGE.spec.ts new file mode 100644 index 00000000000..1205cdd9216 --- /dev/null +++ b/packages/bloom/lib/commands/t-digest/MERGE.spec.ts @@ -0,0 +1,50 @@ +import { strict as assert } from 'assert'; +import testUtils, { GLOBAL } from '../../test-utils'; +import { transformArguments, transformReply } from './MERGE'; + +describe('TDIGEST.MERGE', () => { + describe('transformArguments', () => { + describe('srcKeys', () => { + it('string', () => { + assert.deepEqual( + transformArguments('dest', 'src'), + ['TDIGEST.MERGE', 'dest', '1', 'src'] + ); + }); + + it('Array', () => { + assert.deepEqual( + transformArguments('dest', ['1', '2']), + ['TDIGEST.MERGE', 'dest', '2', '1', '2'] + ); + }); + }); + + it('with COMPRESSION', () => { + assert.deepEqual( + transformArguments('dest', 'src', { + COMPRESSION: 100 + }), + ['TDIGEST.MERGE', 'dest', '1', 'src', 'COMPRESSION', '100'] + ); + }); + + it('with OVERRIDE', () => { + assert.deepEqual( + transformArguments('dest', 'src', { + OVERRIDE: true + }), + ['TDIGEST.MERGE', 'dest', '1', 'src', 'OVERRIDE'] + ); + }); + }); + + testUtils.testWithClient('client.tDigest.merge', async client => { + const [ , reply ] = await Promise.all([ + client.tDigest.create('src'), + client.tDigest.merge('dest', 'src') + ]); + + assert.equal(reply, 'OK'); + }, GLOBAL.SERVERS.OPEN); +}); diff --git a/packages/bloom/lib/commands/t-digest/MERGE.ts b/packages/bloom/lib/commands/t-digest/MERGE.ts new file mode 100644 index 00000000000..5119d0b9e18 --- /dev/null +++ b/packages/bloom/lib/commands/t-digest/MERGE.ts @@ -0,0 +1,30 @@ +import { RedisCommandArgument, RedisCommandArguments } from '@redis/client/dist/lib/commands'; +import { pushVerdictArgument } from '@redis/client/dist/lib/commands/generic-transformers'; +import { CompressionOption, pushCompressionArgument } from '.'; + +export const FIRST_KEY_INDEX = 1; + +interface MergeOptions extends CompressionOption { + OVERRIDE?: boolean; +} + +export function transformArguments( + destKey: RedisCommandArgument, + srcKeys: RedisCommandArgument | Array, + options?: MergeOptions +): RedisCommandArguments { + const args = pushVerdictArgument( + ['TDIGEST.MERGE', destKey], + srcKeys + ); + + pushCompressionArgument(args, options); + + if (options?.OVERRIDE) { + args.push('OVERRIDE'); + } + + return args; +} + +export declare function transformReply(): 'OK'; diff --git a/packages/bloom/lib/commands/t-digest/MIN.spec.ts b/packages/bloom/lib/commands/t-digest/MIN.spec.ts new file mode 100644 index 00000000000..d48deaca7fb --- /dev/null +++ b/packages/bloom/lib/commands/t-digest/MIN.spec.ts @@ -0,0 +1,21 @@ +import { strict as assert } from 'assert'; +import testUtils, { GLOBAL } from '../../test-utils'; +import { transformArguments, transformReply } from './MIN'; + +describe('TDIGEST.MIN', () => { + it('transformArguments', () => { + assert.deepEqual( + transformArguments('key'), + ['TDIGEST.MIN', 'key'] + ); + }); + + testUtils.testWithClient('client.tDigest.min', async client => { + const [ , reply ] = await Promise.all([ + client.tDigest.create('key'), + client.tDigest.min('key') + ]); + + assert.equal(reply, NaN); + }, GLOBAL.SERVERS.OPEN); +}); diff --git a/packages/bloom/lib/commands/t-digest/MIN.ts b/packages/bloom/lib/commands/t-digest/MIN.ts new file mode 100644 index 00000000000..d8be8722b60 --- /dev/null +++ b/packages/bloom/lib/commands/t-digest/MIN.ts @@ -0,0 +1,14 @@ +import { RedisCommandArgument, RedisCommandArguments } from '@redis/client/dist/lib/commands'; + +export const FIRST_KEY_INDEX = 1; + +export const IS_READ_ONLY = true; + +export function transformArguments(key: RedisCommandArgument): RedisCommandArguments { + return [ + 'TDIGEST.MIN', + key + ]; +} + +export { transformDoubleReply as transformReply } from '.'; diff --git a/packages/bloom/lib/commands/t-digest/QUANTILE.spec.ts b/packages/bloom/lib/commands/t-digest/QUANTILE.spec.ts new file mode 100644 index 00000000000..7790debf0de --- /dev/null +++ b/packages/bloom/lib/commands/t-digest/QUANTILE.spec.ts @@ -0,0 +1,24 @@ +import { strict as assert } from 'assert'; +import testUtils, { GLOBAL } from '../../test-utils'; +import { transformArguments } from './QUANTILE'; + +describe('TDIGEST.QUANTILE', () => { + it('transformArguments', () => { + assert.deepEqual( + transformArguments('key', [1, 2]), + ['TDIGEST.QUANTILE', 'key', '1', '2'] + ); + }); + + testUtils.testWithClient('client.tDigest.quantile', async client => { + const [, reply] = await Promise.all([ + client.tDigest.create('key'), + client.tDigest.quantile('key', [1]) + ]); + + assert.deepEqual( + reply, + [NaN] + ); + }, GLOBAL.SERVERS.OPEN); +}); diff --git a/packages/bloom/lib/commands/t-digest/QUANTILE.ts b/packages/bloom/lib/commands/t-digest/QUANTILE.ts new file mode 100644 index 00000000000..2289ffc6f55 --- /dev/null +++ b/packages/bloom/lib/commands/t-digest/QUANTILE.ts @@ -0,0 +1,23 @@ +import { RedisCommandArgument, RedisCommandArguments } from '@redis/client/dist/lib/commands'; + +export const FIRST_KEY_INDEX = 1; + +export const IS_READ_ONLY = true; + +export function transformArguments( + key: RedisCommandArgument, + quantiles: Array +): RedisCommandArguments { + const args = [ + 'TDIGEST.QUANTILE', + key + ]; + + for (const quantile of quantiles) { + args.push(quantile.toString()); + } + + return args; +} + +export { transformDoublesReply as transformReply } from '.'; diff --git a/packages/bloom/lib/commands/t-digest/RANK.spec.ts b/packages/bloom/lib/commands/t-digest/RANK.spec.ts new file mode 100644 index 00000000000..258bedf3491 --- /dev/null +++ b/packages/bloom/lib/commands/t-digest/RANK.spec.ts @@ -0,0 +1,21 @@ +import { strict as assert } from 'assert'; +import testUtils, { GLOBAL } from '../../test-utils'; +import { transformArguments } from './RANK'; + +describe('TDIGEST.RANK', () => { + it('transformArguments', () => { + assert.deepEqual( + transformArguments('key', [1, 2]), + ['TDIGEST.RANK', 'key', '1', '2'] + ); + }); + + testUtils.testWithClient('client.tDigest.rank', async client => { + const [ , reply ] = await Promise.all([ + client.tDigest.create('key'), + client.tDigest.rank('key', [1]) + ]); + + assert.deepEqual(reply, [-2]); + }, GLOBAL.SERVERS.OPEN); +}); diff --git a/packages/bloom/lib/commands/t-digest/RANK.ts b/packages/bloom/lib/commands/t-digest/RANK.ts new file mode 100644 index 00000000000..1a6c84bbd4d --- /dev/null +++ b/packages/bloom/lib/commands/t-digest/RANK.ts @@ -0,0 +1,19 @@ +import { RedisCommandArgument, RedisCommandArguments } from '@redis/client/dist/lib/commands'; + +export const FIRST_KEY_INDEX = 1; + +export const IS_READ_ONLY = true; + +export function transformArguments( + key: RedisCommandArgument, + values: Array +): RedisCommandArguments { + const args = ['TDIGEST.RANK', key]; + for (const item of values) { + args.push(item.toString()); + } + + return args; +} + +export declare function transformReply(): Array; diff --git a/packages/bloom/lib/commands/t-digest/RESET.spec.ts b/packages/bloom/lib/commands/t-digest/RESET.spec.ts new file mode 100644 index 00000000000..036fbebc8cc --- /dev/null +++ b/packages/bloom/lib/commands/t-digest/RESET.spec.ts @@ -0,0 +1,21 @@ +import { strict as assert } from 'assert'; +import testUtils, { GLOBAL } from '../../test-utils'; +import { transformArguments } from './RESET'; + +describe('TDIGEST.RESET', () => { + it('transformArguments', () => { + assert.deepEqual( + transformArguments('key'), + ['TDIGEST.RESET', 'key'] + ); + }); + + testUtils.testWithClient('client.tDigest.reset', async client => { + const [, reply] = await Promise.all([ + client.tDigest.create('key'), + client.tDigest.reset('key') + ]); + + assert.equal(reply, 'OK'); + }, GLOBAL.SERVERS.OPEN); +}); diff --git a/packages/bloom/lib/commands/t-digest/RESET.ts b/packages/bloom/lib/commands/t-digest/RESET.ts new file mode 100644 index 00000000000..6c700e6b932 --- /dev/null +++ b/packages/bloom/lib/commands/t-digest/RESET.ts @@ -0,0 +1,9 @@ +import { RedisCommandArgument, RedisCommandArguments } from '@redis/client/dist/lib/commands'; + +export const FIRST_KEY_INDEX = 1; + +export function transformArguments(key: RedisCommandArgument): RedisCommandArguments { + return ['TDIGEST.RESET', key]; +} + +export declare function transformReply(): 'OK'; diff --git a/packages/bloom/lib/commands/t-digest/REVRANK.spec.ts b/packages/bloom/lib/commands/t-digest/REVRANK.spec.ts new file mode 100644 index 00000000000..21d16661dfe --- /dev/null +++ b/packages/bloom/lib/commands/t-digest/REVRANK.spec.ts @@ -0,0 +1,21 @@ +import { strict as assert } from 'assert'; +import testUtils, { GLOBAL } from '../../test-utils'; +import { transformArguments } from './REVRANK'; + +describe('TDIGEST.REVRANK', () => { + it('transformArguments', () => { + assert.deepEqual( + transformArguments('key', [1, 2]), + ['TDIGEST.REVRANK', 'key', '1', '2'] + ); + }); + + testUtils.testWithClient('client.tDigest.revRank', async client => { + const [ , reply ] = await Promise.all([ + client.tDigest.create('key'), + client.tDigest.revRank('key', [1]) + ]); + + assert.deepEqual(reply, [-2]); + }, GLOBAL.SERVERS.OPEN); +}); diff --git a/packages/bloom/lib/commands/t-digest/REVRANK.ts b/packages/bloom/lib/commands/t-digest/REVRANK.ts new file mode 100644 index 00000000000..a2465052774 --- /dev/null +++ b/packages/bloom/lib/commands/t-digest/REVRANK.ts @@ -0,0 +1,19 @@ +import { RedisCommandArgument, RedisCommandArguments } from '@redis/client/dist/lib/commands'; + +export const FIRST_KEY_INDEX = 1; + +export const IS_READ_ONLY = true; + +export function transformArguments( + key: RedisCommandArgument, + values: Array +): RedisCommandArguments { + const args = ['TDIGEST.REVRANK', key]; + for (const item of values) { + args.push(item.toString()); + } + + return args; +} + +export declare function transformReply(): Array; diff --git a/packages/bloom/lib/commands/t-digest/TRIMMED_MEAN.spec.ts b/packages/bloom/lib/commands/t-digest/TRIMMED_MEAN.spec.ts new file mode 100644 index 00000000000..dd07f325c8d --- /dev/null +++ b/packages/bloom/lib/commands/t-digest/TRIMMED_MEAN.spec.ts @@ -0,0 +1,21 @@ +import { strict as assert } from 'assert'; +import testUtils, { GLOBAL } from '../../test-utils'; +import { transformArguments, transformReply } from './TRIMMED_MEAN'; + +describe('TDIGEST.RESET', () => { + it('transformArguments', () => { + assert.deepEqual( + transformArguments('key', 0, 1), + ['TDIGEST.TRIMMED_MEAN', 'key', '0', '1'] + ); + }); + + testUtils.testWithClient('client.tDigest.trimmedMean', async client => { + const [, reply] = await Promise.all([ + client.tDigest.create('key'), + client.tDigest.trimmedMean('key', 0, 1) + ]); + + assert.equal(reply, NaN); + }, GLOBAL.SERVERS.OPEN); +}); diff --git a/packages/bloom/lib/commands/t-digest/TRIMMED_MEAN.ts b/packages/bloom/lib/commands/t-digest/TRIMMED_MEAN.ts new file mode 100644 index 00000000000..6de80ba7c7c --- /dev/null +++ b/packages/bloom/lib/commands/t-digest/TRIMMED_MEAN.ts @@ -0,0 +1,20 @@ +import { RedisCommandArgument, RedisCommandArguments } from '@redis/client/dist/lib/commands'; + +export const FIRST_KEY_INDEX = 1; + +export const IS_READ_ONLY = true; + +export function transformArguments( + key: RedisCommandArgument, + lowCutPercentile: number, + highCutPercentile: number +): RedisCommandArguments { + return [ + 'TDIGEST.TRIMMED_MEAN', + key, + lowCutPercentile.toString(), + highCutPercentile.toString() + ]; +} + +export { transformDoubleReply as transformReply } from '.'; diff --git a/packages/bloom/lib/commands/t-digest/index.spec.ts b/packages/bloom/lib/commands/t-digest/index.spec.ts new file mode 100644 index 00000000000..5bef6df04b2 --- /dev/null +++ b/packages/bloom/lib/commands/t-digest/index.spec.ts @@ -0,0 +1,55 @@ +import { strict as assert } from 'assert'; +import { pushCompressionArgument, transformDoubleReply, transformDoublesReply } from '.'; + +describe('pushCompressionArgument', () => { + it('undefined', () => { + assert.deepEqual( + pushCompressionArgument([]), + [] + ); + }); + + it('100', () => { + assert.deepEqual( + pushCompressionArgument([], { COMPRESSION: 100 }), + ['COMPRESSION', '100'] + ); + }); +}); + +describe('transformDoubleReply', () => { + it('inf', () => { + assert.equal( + transformDoubleReply('inf'), + Infinity + ); + }); + + it('-inf', () => { + assert.equal( + transformDoubleReply('-inf'), + -Infinity + ); + }); + + it('nan', () => { + assert.equal( + transformDoubleReply('nan'), + NaN + ); + }); + + it('0', () => { + assert.equal( + transformDoubleReply('0'), + 0 + ); + }); +}); + +it('transformDoublesReply', () => { + assert.deepEqual( + transformDoublesReply(['inf', '-inf', 'nan', '0']), + [Infinity, -Infinity, NaN, 0] + ); +}); diff --git a/packages/bloom/lib/commands/t-digest/index.ts b/packages/bloom/lib/commands/t-digest/index.ts new file mode 100644 index 00000000000..da3b37464d2 --- /dev/null +++ b/packages/bloom/lib/commands/t-digest/index.ts @@ -0,0 +1,81 @@ +import { RedisCommandArguments } from '@redis/client/dist/lib/commands'; +import * as ADD from './ADD'; +import * as BYRANK from './BYRANK'; +import * as BYREVRANK from './BYREVRANK'; +import * as CDF from './CDF'; +import * as CREATE from './CREATE'; +import * as INFO from './INFO'; +import * as MAX from './MAX'; +import * as MERGE from './MERGE'; +import * as MIN from './MIN'; +import * as QUANTILE from './QUANTILE'; +import * as RANK from './RANK'; +import * as RESET from './RESET'; +import * as REVRANK from './REVRANK'; +import * as TRIMMED_MEAN from './TRIMMED_MEAN'; + +export default { + ADD, + add: ADD, + BYRANK, + byRank: BYRANK, + BYREVRANK, + byRevRank: BYREVRANK, + CDF, + cdf: CDF, + CREATE, + create: CREATE, + INFO, + info: INFO, + MAX, + max: MAX, + MERGE, + merge: MERGE, + MIN, + min: MIN, + QUANTILE, + quantile: QUANTILE, + RANK, + rank: RANK, + RESET, + reset: RESET, + REVRANK, + revRank: REVRANK, + TRIMMED_MEAN, + trimmedMean: TRIMMED_MEAN +}; + +export interface CompressionOption { + COMPRESSION?: number; +} + +export function pushCompressionArgument( + args: RedisCommandArguments, + options?: CompressionOption +): RedisCommandArguments { + if (options?.COMPRESSION) { + args.push('COMPRESSION', options.COMPRESSION.toString()); + } + + return args; +} + +export function transformDoubleReply(reply: string): number { + switch (reply) { + case 'inf': + return Infinity; + + case '-inf': + return -Infinity; + + case 'nan': + return NaN; + + default: + return parseFloat(reply); + } +} + +export function transformDoublesReply(reply: Array): Array { + return reply.map(transformDoubleReply); +} diff --git a/packages/bloom/lib/test-utils.ts b/packages/bloom/lib/test-utils.ts index 4bcebad93ae..a2e059b3b99 100644 --- a/packages/bloom/lib/test-utils.ts +++ b/packages/bloom/lib/test-utils.ts @@ -4,7 +4,7 @@ import RedisBloomModules from '.'; export default new TestUtils({ dockerImageName: 'redislabs/rebloom', dockerImageVersionArgument: 'redisbloom-version', - defaultDockerVersion: '2.2.9' + defaultDockerVersion: 'edge' }); export const GLOBAL = { diff --git a/packages/test-utils/lib/index.ts b/packages/test-utils/lib/index.ts index 3e33971fddb..7b9494c8d66 100644 --- a/packages/test-utils/lib/index.ts +++ b/packages/test-utils/lib/index.ts @@ -42,24 +42,32 @@ interface Version { } export default class TestUtils { + static #parseVersionNumber(version: string): Array { + if (version === 'edge') return [Infinity]; + + const dashIndex = version.indexOf('-'); + return (dashIndex === -1 ? version : version.substring(0, dashIndex)) + .split('.') + .map(x => { + const value = Number(x); + if (Number.isNaN(value)) { + throw new TypeError(`${version} is not a valid redis version`); + } + + return value; + }); + } + static #getVersion(argumentName: string, defaultVersion: string): Version { return yargs(hideBin(process.argv)) .option(argumentName, { type: 'string', default: defaultVersion }) - .coerce(argumentName, (arg: string) => { - const indexOfDash = arg.indexOf('-'); + .coerce(argumentName, (version: string) => { return { - string: arg, - numbers: (indexOfDash === -1 ? arg : arg.substring(0, indexOfDash)).split('.').map(x => { - const value = Number(x); - if (Number.isNaN(value)) { - throw new TypeError(`${arg} is not a valid redis version`); - } - - return value; - }) + string: version, + numbers: TestUtils.#parseVersionNumber(version) }; }) .demandOption(argumentName) From 597112bf29fec26476aa943a28a3c48c5a5791e7 Mon Sep 17 00:00:00 2001 From: Leibale Eidelman Date: Tue, 1 Nov 2022 15:54:29 -0400 Subject: [PATCH 1269/1748] Update README.md --- packages/graph/README.md | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/packages/graph/README.md b/packages/graph/README.md index 3beb08f0ecd..4c712bfd820 100644 --- a/packages/graph/README.md +++ b/packages/graph/README.md @@ -24,7 +24,9 @@ await graph.query( const result = await graph.roQuery( 'MATCH (r:Rider)-[:rides]->(t:Team { name: $name }) RETURN r.name AS name', { - name: 'Apollo' + params: { + name: 'Apollo' + } } ); From aa869b72d4ef50af209c52f1810a09ac9fc3aaf9 Mon Sep 17 00:00:00 2001 From: Leibale Eidelman Date: Tue, 1 Nov 2022 16:12:15 -0400 Subject: [PATCH 1270/1748] Release client@1.3.1 --- packages/client/package.json | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/packages/client/package.json b/packages/client/package.json index 10013b3c66f..e8aeb5d14bd 100644 --- a/packages/client/package.json +++ b/packages/client/package.json @@ -1,6 +1,6 @@ { "name": "@redis/client", - "version": "1.3.0", + "version": "1.3.1", "license": "MIT", "main": "./dist/index.js", "types": "./dist/index.d.ts", From 66abec6c3a000a33dd41058299ed290d2393d360 Mon Sep 17 00:00:00 2001 From: Leibale Eidelman Date: Tue, 1 Nov 2022 16:15:08 -0400 Subject: [PATCH 1271/1748] Release bloom@1.1.0 --- packages/bloom/package.json | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/packages/bloom/package.json b/packages/bloom/package.json index 4c55a28dfb9..ce79d3ccf7a 100644 --- a/packages/bloom/package.json +++ b/packages/bloom/package.json @@ -1,6 +1,6 @@ { "name": "@redis/bloom", - "version": "1.0.2", + "version": "1.1.0", "license": "MIT", "main": "./dist/index.js", "types": "./dist/index.d.ts", From c322f3164dc28e964d14b17a66221602fe46a22d Mon Sep 17 00:00:00 2001 From: Leibale Eidelman Date: Tue, 1 Nov 2022 16:17:17 -0400 Subject: [PATCH 1272/1748] Release graph@1.1.0 --- packages/graph/package.json | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/packages/graph/package.json b/packages/graph/package.json index e1749eaa482..36697ec3c84 100644 --- a/packages/graph/package.json +++ b/packages/graph/package.json @@ -1,6 +1,6 @@ { "name": "@redis/graph", - "version": "1.0.1", + "version": "1.1.0", "license": "MIT", "main": "./dist/index.js", "types": "./dist/index.d.ts", From 0550199715c0fb8def061916412a3022baebd850 Mon Sep 17 00:00:00 2001 From: Leibale Eidelman Date: Tue, 1 Nov 2022 16:20:03 -0400 Subject: [PATCH 1273/1748] upgrade deps --- package-lock.json | 12 ++++++------ package.json | 6 +++--- 2 files changed, 9 insertions(+), 9 deletions(-) diff --git a/package-lock.json b/package-lock.json index ef18533b4f0..8b06bcf05be 100644 --- a/package-lock.json +++ b/package-lock.json @@ -12,9 +12,9 @@ "./packages/*" ], "dependencies": { - "@redis/bloom": "1.0.2", - "@redis/client": "1.3.0", - "@redis/graph": "1.0.1", + "@redis/bloom": "1.1.0", + "@redis/client": "1.3.1", + "@redis/graph": "1.1.0", "@redis/json": "1.0.4", "@redis/search": "1.1.0", "@redis/time-series": "1.0.3" @@ -8029,7 +8029,7 @@ }, "packages/bloom": { "name": "@redis/bloom", - "version": "1.0.2", + "version": "1.1.0", "license": "MIT", "devDependencies": { "@istanbuljs/nyc-config-typescript": "^1.0.2", @@ -8048,7 +8048,7 @@ }, "packages/client": { "name": "@redis/client", - "version": "1.3.0", + "version": "1.3.1", "license": "MIT", "dependencies": { "cluster-key-slot": "1.1.1", @@ -8082,7 +8082,7 @@ }, "packages/graph": { "name": "@redis/graph", - "version": "1.0.1", + "version": "1.1.0", "license": "MIT", "devDependencies": { "@istanbuljs/nyc-config-typescript": "^1.0.2", diff --git a/package.json b/package.json index dc70e250b2e..df5058c3e71 100644 --- a/package.json +++ b/package.json @@ -23,9 +23,9 @@ "gh-pages": "gh-pages -d ./documentation -e ./documentation -u 'documentation-bot '" }, "dependencies": { - "@redis/bloom": "1.0.2", - "@redis/client": "1.3.0", - "@redis/graph": "1.0.1", + "@redis/bloom": "1.1.0", + "@redis/client": "1.3.1", + "@redis/graph": "1.1.0", "@redis/json": "1.0.4", "@redis/search": "1.1.0", "@redis/time-series": "1.0.3" From 2a5dc751c0657967c7996464b0944230e38f3df0 Mon Sep 17 00:00:00 2001 From: Leibale Eidelman Date: Tue, 1 Nov 2022 16:20:39 -0400 Subject: [PATCH 1274/1748] Release redis@4.4.0 --- package-lock.json | 4 ++-- package.json | 2 +- 2 files changed, 3 insertions(+), 3 deletions(-) diff --git a/package-lock.json b/package-lock.json index 8b06bcf05be..5014d198f8b 100644 --- a/package-lock.json +++ b/package-lock.json @@ -1,12 +1,12 @@ { "name": "redis", - "version": "4.3.1", + "version": "4.4.0", "lockfileVersion": 2, "requires": true, "packages": { "": { "name": "redis", - "version": "4.3.1", + "version": "4.4.0", "license": "MIT", "workspaces": [ "./packages/*" diff --git a/package.json b/package.json index df5058c3e71..1b66d78e85d 100644 --- a/package.json +++ b/package.json @@ -1,7 +1,7 @@ { "name": "redis", "description": "A modern, high performance Redis client", - "version": "4.3.1", + "version": "4.4.0", "license": "MIT", "main": "./dist/index.js", "types": "./dist/index.d.ts", From e0e96aef2acd0a622a7668627b66f819ecd88aac Mon Sep 17 00:00:00 2001 From: alphabetkrish Date: Sun, 6 Nov 2022 12:30:35 +0530 Subject: [PATCH 1275/1748] fix(#2231): created doc for using timeseries (#2312) * fix(doc): created doc for using timeseries * fix(doc): created doc for using timeseries * Apply suggestions from code review Co-authored-by: Simon Prickett Co-authored-by: Simon Prickett --- packages/time-series/README.md | 146 ++++++++++++++++++++++++++++++++- 1 file changed, 145 insertions(+), 1 deletion(-) diff --git a/packages/time-series/README.md b/packages/time-series/README.md index a9c8c081810..5923979cd48 100644 --- a/packages/time-series/README.md +++ b/packages/time-series/README.md @@ -4,4 +4,148 @@ This package provides support for the [RedisTimeSeries](https://redistimeseries. To use these extra commands, your Redis server must have the RedisTimeSeries module installed. -For an example of how to add values to a time series, query a time series, and perform aggregated queries against a time series, see `time-series.js` in the Node Redis examples folder. +## Usage + +For a complete example, see [`time-series.js`](https://github.com/redis/node-redis/blob/master/examples/time-series.js) in the Node Redis examples folder. + +### Creating Time Series data structure in Redis + +The [`TS.CREATE`](https://oss.redis.com/redistimeseries/commands/#tscreate) command creates a new time series. + +Here, we'll create a new time series "`temperature`": + +```javascript + +import { createClient } from 'redis'; +import { TimeSeriesDuplicatePolicies, TimeSeriesEncoding, TimeSeriesAggregationType } from '@redis/time-series'; + +... + + const created = await client.ts.create('temperature', { + RETENTION: 86400000, // 1 day in milliseconds + ENCODING: TimeSeriesEncoding.UNCOMPRESSED, // No compression - When not specified, the option is set to COMPRESSED + DUPLICATE_POLICY: TimeSeriesDuplicatePolicies.BLOCK, // No duplicates - When not specified: set to the global DUPLICATE_POLICY configuration of the database (which by default, is BLOCK). + }); + + if (created === 'OK') { + console.log('Created timeseries.'); + } else { + console.log('Error creating timeseries :('); + process.exit(1); + } + +``` + +### Adding new value to a Time Series data structure in Redis + +With RedisTimeSeries, we can add a single value to time series data structure using the [`TS.ADD`](https://redis.io/commands/ts.add/) command and if we would like to add multiple values we can use the [`TS.MADD`](https://redis.io/commands/ts.madd/) command. + +```javascript + +let value = Math.floor(Math.random() * 1000) + 1; // Random data point value + let currentTimestamp = 1640995200000; // Jan 1 2022 00:00:00 + let num = 0; + + while (num < 10000) { + // Add a new value to the timeseries, providing our own timestamp: + // https://redis.io/commands/ts.add/ + await client.ts.add('temperature', currentTimestamp, value); + console.log(`Added timestamp ${currentTimestamp}, value ${value}.`); + + num += 1; + value = Math.floor(Math.random() * 1000) + 1; // Get another random value + currentTimestamp += 1000; // Move on one second. + } + + // Add multiple values to the timeseries in round trip to the server: + // https://redis.io/commands/ts.madd/ + const response = await client.ts.mAdd([{ + key: 'temperature', + timestamp: currentTimestamp + 60000, + value: Math.floor(Math.random() * 1000) + 1 + }, { + key: 'temperature', + timestamp: currentTimestamp + 120000, + value: Math.floor(Math.random() * 1000) + 1 + }]); + + +``` + +### Retrieving Time Series data from Redis + +With RedisTimeSeries, we can retrieve the time series data using the [`TS.RANGE`](https://redis.io/commands/ts.range/) command by passing the criteria as follows: + +```javascript + +// Query the timeseries with TS.RANGE: + // https://redis.io/commands/ts.range/ + const fromTimestamp = 1640995200000; // Jan 1 2022 00:00:00 + const toTimestamp = 1640995260000; // Jan 1 2022 00:01:00 + const rangeResponse = await client.ts.range('temperature', fromTimestamp, toTimestamp, { + // Group into 10 second averages. + AGGREGATION: { + type: TimeSeriesAggregationType.AVERAGE, + timeBucket: 10000 + } + }); + + console.log('RANGE RESPONSE:'); + // rangeResponse looks like: + // [ + // { timestamp: 1640995200000, value: 356.8 }, + // { timestamp: 1640995210000, value: 534.8 }, + // { timestamp: 1640995220000, value: 481.3 }, + // { timestamp: 1640995230000, value: 437 }, + // { timestamp: 1640995240000, value: 507.3 }, + // { timestamp: 1640995250000, value: 581.2 }, + // { timestamp: 1640995260000, value: 600 } + // ] + +``` + +### Altering Time Series data Stored in Redis + +RedisTimeSeries includes commands that can update values in a time series data structure. + +Using the [`TS.ALTER`](https://redis.io/commands/ts.alter/) command, we can update time series retention like this: + +```javascript + + // https://redis.io/commands/ts.alter/ + const alterResponse = await client.ts.alter('temperature', { + RETENTION: 0 // Keep the entries forever + }); + +``` + +### Retrieving Information about the timeseries Stored in Redis + +RedisTimeSeries also includes commands that can help to view the information on the state of a time series. + +Using the [`TS.INFO`](https://redis.io/commands/ts.info/) command, we can view timeseries information like this: + +```javascript + + // Get some information about the state of the timeseries. + // https://redis.io/commands/ts.info/ + const tsInfo = await client.ts.info('temperature'); + + // tsInfo looks like this: + // { + // totalSamples: 1440, + // memoryUsage: 28904, + // firstTimestamp: 1641508920000, + // lastTimestamp: 1641595320000, + // retentionTime: 86400000, + // chunkCount: 7, + // chunkSize: 4096, + // chunkType: 'uncompressed', + // duplicatePolicy: 'block', + // labels: [], + // sourceKey: null, + // rules: [] + // } + +``` + From 3c2f7ab92a3665d144d0617121ac4ebcccf78aed Mon Sep 17 00:00:00 2001 From: Leibale Eidelman Date: Thu, 10 Nov 2022 03:49:56 -0500 Subject: [PATCH 1276/1748] Ping interval (#2321) * fix #1598 fix #2276 - add `pingInterval` to client config * setPingTimer on ready (instead of on connect) * use isReady (instead of isOpen) and fix test * Update client-configuration.md --- docs/client-configuration.md | 1 + packages/client/lib/client/index.spec.ts | 12 ++++++++++++ packages/client/lib/client/index.ts | 22 +++++++++++++++++++++- 3 files changed, 34 insertions(+), 1 deletion(-) diff --git a/docs/client-configuration.md b/docs/client-configuration.md index 6b7e7da7532..a67cef462ac 100644 --- a/docs/client-configuration.md +++ b/docs/client-configuration.md @@ -25,6 +25,7 @@ | 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)) | | isolationPoolOptions | | See the [Isolated Execution Guide](./isolated-execution.md) | +| pingInterval | | Send `PING` command at interval (in ms). Useful with "[Azure Cache for Redis](https://learn.microsoft.com/en-us/azure/azure-cache-for-redis/cache-best-practices-connection#idle-timeout)" | ## Reconnect Strategy diff --git a/packages/client/lib/client/index.spec.ts b/packages/client/lib/client/index.spec.ts index 27cb86d657e..6294e155a44 100644 --- a/packages/client/lib/client/index.spec.ts +++ b/packages/client/lib/client/index.spec.ts @@ -862,4 +862,16 @@ describe('Client', () => { client.unref(); client.ref(); }, GLOBAL.SERVERS.OPEN); + + testUtils.testWithClient('pingInterval', async client => { + assert.deepEqual( + await once(client, 'ping-interval'), + ['PONG'] + ); + }, { + ...GLOBAL.SERVERS.OPEN, + clientOptions: { + pingInterval: 1 + } + }); }); diff --git a/packages/client/lib/client/index.ts b/packages/client/lib/client/index.ts index 52895c73c3e..e6f1fef10e5 100644 --- a/packages/client/lib/client/index.ts +++ b/packages/client/lib/client/index.ts @@ -31,6 +31,7 @@ export interface RedisClientOptions< readonly?: boolean; legacyMode?: boolean; isolationPoolOptions?: PoolOptions; + pingInterval?: number; } type WithCommands = { @@ -281,9 +282,12 @@ export default class RedisClient< this.#queue.flushAll(err); } }) - .on('connect', () => this.emit('connect')) + .on('connect', () => { + this.emit('connect'); + }) .on('ready', () => { this.emit('ready'); + this.#setPingTimer(); this.#tick(); }) .on('reconnecting', () => this.emit('reconnecting')) @@ -348,6 +352,22 @@ export default class RedisClient< (...args: Array): void => (this as any).sendCommand(name, ...args); } + #pingTimer?: NodeJS.Timer; + + #setPingTimer(): void { + if (!this.#options?.pingInterval || !this.#socket.isReady) return; + clearTimeout(this.#pingTimer); + + this.#pingTimer = setTimeout(() => { + if (!this.#socket.isReady) return; + + (this as unknown as RedisClientType).ping() + .then(reply => this.emit('ping-interval', reply)) + .catch(err => this.emit('error', err)) + .finally(() => this.#setPingTimer()); + }, this.#options.pingInterval); + } + duplicate(overrides?: Partial>): RedisClientType { return new (Object.getPrototypeOf(this).constructor)({ ...this.#options, From e8dfa6dcb88d32bb04c088a0832a7082b94b4c24 Mon Sep 17 00:00:00 2001 From: Leibale Eidelman Date: Thu, 10 Nov 2022 03:52:29 -0500 Subject: [PATCH 1277/1748] Release time-series@1.0.4 --- packages/time-series/package.json | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/packages/time-series/package.json b/packages/time-series/package.json index b5e93316343..8f0530fb486 100644 --- a/packages/time-series/package.json +++ b/packages/time-series/package.json @@ -1,6 +1,6 @@ { "name": "@redis/time-series", - "version": "1.0.3", + "version": "1.0.4", "license": "MIT", "main": "./dist/index.js", "types": "./dist/index.d.ts", From 2e6fdee0525756459079b421b4e5d4576fe32a21 Mon Sep 17 00:00:00 2001 From: Leibale Eidelman Date: Thu, 10 Nov 2022 03:56:23 -0500 Subject: [PATCH 1278/1748] Release client@1.4.0 --- packages/client/package.json | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/packages/client/package.json b/packages/client/package.json index e8aeb5d14bd..849839aaa1e 100644 --- a/packages/client/package.json +++ b/packages/client/package.json @@ -1,6 +1,6 @@ { "name": "@redis/client", - "version": "1.3.1", + "version": "1.4.0", "license": "MIT", "main": "./dist/index.js", "types": "./dist/index.d.ts", From 787afc9c0a6d3c8d562d46feb663743522410ac1 Mon Sep 17 00:00:00 2001 From: Leibale Eidelman Date: Thu, 10 Nov 2022 03:59:00 -0500 Subject: [PATCH 1279/1748] upgrade @redis deps --- package-lock.json | 8 ++++---- package.json | 4 ++-- 2 files changed, 6 insertions(+), 6 deletions(-) diff --git a/package-lock.json b/package-lock.json index 5014d198f8b..789faae3087 100644 --- a/package-lock.json +++ b/package-lock.json @@ -13,11 +13,11 @@ ], "dependencies": { "@redis/bloom": "1.1.0", - "@redis/client": "1.3.1", + "@redis/client": "1.4.0", "@redis/graph": "1.1.0", "@redis/json": "1.0.4", "@redis/search": "1.1.0", - "@redis/time-series": "1.0.3" + "@redis/time-series": "1.0.4" }, "devDependencies": { "@tsconfig/node14": "^1.0.3", @@ -8048,7 +8048,7 @@ }, "packages/client": { "name": "@redis/client", - "version": "1.3.1", + "version": "1.4.0", "license": "MIT", "dependencies": { "cluster-key-slot": "1.1.1", @@ -8157,7 +8157,7 @@ }, "packages/time-series": { "name": "@redis/time-series", - "version": "1.0.3", + "version": "1.0.4", "license": "MIT", "devDependencies": { "@istanbuljs/nyc-config-typescript": "^1.0.2", diff --git a/package.json b/package.json index 1b66d78e85d..f91234878e2 100644 --- a/package.json +++ b/package.json @@ -24,11 +24,11 @@ }, "dependencies": { "@redis/bloom": "1.1.0", - "@redis/client": "1.3.1", + "@redis/client": "1.4.0", "@redis/graph": "1.1.0", "@redis/json": "1.0.4", "@redis/search": "1.1.0", - "@redis/time-series": "1.0.3" + "@redis/time-series": "1.0.4" }, "devDependencies": { "@tsconfig/node14": "^1.0.3", From d09732280b1ed1e41cb53b687ed04a6be0fff8ab Mon Sep 17 00:00:00 2001 From: Leibale Eidelman Date: Thu, 10 Nov 2022 03:59:32 -0500 Subject: [PATCH 1280/1748] Release redis@4.5.0 --- package-lock.json | 4 ++-- package.json | 2 +- 2 files changed, 3 insertions(+), 3 deletions(-) diff --git a/package-lock.json b/package-lock.json index 789faae3087..9667eeb8a11 100644 --- a/package-lock.json +++ b/package-lock.json @@ -1,12 +1,12 @@ { "name": "redis", - "version": "4.4.0", + "version": "4.5.0", "lockfileVersion": 2, "requires": true, "packages": { "": { "name": "redis", - "version": "4.4.0", + "version": "4.5.0", "license": "MIT", "workspaces": [ "./packages/*" diff --git a/package.json b/package.json index f91234878e2..f5d2bed426a 100644 --- a/package.json +++ b/package.json @@ -1,7 +1,7 @@ { "name": "redis", "description": "A modern, high performance Redis client", - "version": "4.4.0", + "version": "4.5.0", "license": "MIT", "main": "./dist/index.js", "types": "./dist/index.d.ts", From e5532706cfece84462c5aa539fa11b402ff4d9e1 Mon Sep 17 00:00:00 2001 From: Leibale Eidelman Date: Wed, 16 Nov 2022 16:27:36 -0500 Subject: [PATCH 1281/1748] fix #2205 - reject commands in connect phase when `disableOfflineQueue` is `true` --- packages/client/lib/client/index.spec.ts | 18 +++++++++++++++++- packages/client/lib/client/index.ts | 10 +++++----- packages/client/lib/errors.ts | 6 ++++++ 3 files changed, 28 insertions(+), 6 deletions(-) diff --git a/packages/client/lib/client/index.spec.ts b/packages/client/lib/client/index.spec.ts index 6294e155a44..4eee7076295 100644 --- a/packages/client/lib/client/index.spec.ts +++ b/packages/client/lib/client/index.spec.ts @@ -3,7 +3,7 @@ import testUtils, { GLOBAL, waitTillBeenCalled } from '../test-utils'; import RedisClient, { RedisClientType } from '.'; import { RedisClientMultiCommandType } from './multi-command'; import { RedisCommandArguments, RedisCommandRawReply, RedisModules, RedisFunctions, RedisScripts } from '../commands'; -import { AbortError, ClientClosedError, ConnectionTimeoutError, DisconnectsClientError, SocketClosedUnexpectedlyError, WatchError } from '../errors'; +import { AbortError, ClientClosedError, ClientOfflineError, ConnectionTimeoutError, DisconnectsClientError, SocketClosedUnexpectedlyError, WatchError } from '../errors'; import { defineScript } from '../lua-script'; import { spy } from 'sinon'; import { once } from 'events'; @@ -874,4 +874,20 @@ describe('Client', () => { pingInterval: 1 } }); + + testUtils.testWithClient('should reject commands in connect phase when `disableOfflineQueue`', async client => { + const connectPromise = client.connect(); + await assert.rejects( + client.ping(), + ClientOfflineError + ); + await connectPromise; + await client.disconnect(); + }, { + ...GLOBAL.SERVERS.OPEN, + clientOptions: { + disableOfflineQueue: true + }, + disableClientSetup: true + }); }); diff --git a/packages/client/lib/client/index.ts b/packages/client/lib/client/index.ts index e6f1fef10e5..c4259f72b82 100644 --- a/packages/client/lib/client/index.ts +++ b/packages/client/lib/client/index.ts @@ -11,7 +11,7 @@ import { ScanCommandOptions } from '../commands/SCAN'; import { HScanTuple } from '../commands/HSCAN'; import { attachCommands, attachExtensions, fCallArguments, transformCommandArguments, transformCommandReply, transformLegacyCommandArguments } from '../commander'; import { Pool, Options as PoolOptions, createPool } from 'generic-pool'; -import { ClientClosedError, DisconnectsClientError } from '../errors'; +import { ClientClosedError, ClientOfflineError, DisconnectsClientError } from '../errors'; import { URL } from 'url'; import { TcpSocketConnectOpts } from 'net'; @@ -405,16 +405,16 @@ export default class RedisClient< ): Promise { if (!this.#socket.isOpen) { return Promise.reject(new ClientClosedError()); - } - - if (options?.isolated) { + } else if (options?.isolated) { return this.executeIsolated(isolatedClient => isolatedClient.sendCommand(args, { ...options, isolated: false }) ); - } + } else if (!this.#socket.isReady && this.#options?.disableOfflineQueue) { + return Promise.reject(new ClientOfflineError()); + } const promise = this.#queue.addCommand(args, options); this.#tick(); diff --git a/packages/client/lib/errors.ts b/packages/client/lib/errors.ts index 3f3b9624987..30709703153 100644 --- a/packages/client/lib/errors.ts +++ b/packages/client/lib/errors.ts @@ -22,6 +22,12 @@ export class ClientClosedError extends Error { } } +export class ClientOfflineError extends Error { + constructor() { + super('The client is offline'); + } +} + export class DisconnectsClientError extends Error { constructor() { super('Disconnects client'); From d923f7127ac72b84c3234159f0fec09de1ba196d Mon Sep 17 00:00:00 2001 From: Leibale Eidelman Date: Thu, 24 Nov 2022 14:01:09 -0500 Subject: [PATCH 1282/1748] fix #2205 - reject commands in connect phase when `disableOfflineQueue` is `true` (#2328) --- packages/client/lib/client/index.spec.ts | 18 +++++++++++++++++- packages/client/lib/client/index.ts | 10 +++++----- packages/client/lib/errors.ts | 6 ++++++ 3 files changed, 28 insertions(+), 6 deletions(-) diff --git a/packages/client/lib/client/index.spec.ts b/packages/client/lib/client/index.spec.ts index 6294e155a44..4eee7076295 100644 --- a/packages/client/lib/client/index.spec.ts +++ b/packages/client/lib/client/index.spec.ts @@ -3,7 +3,7 @@ import testUtils, { GLOBAL, waitTillBeenCalled } from '../test-utils'; import RedisClient, { RedisClientType } from '.'; import { RedisClientMultiCommandType } from './multi-command'; import { RedisCommandArguments, RedisCommandRawReply, RedisModules, RedisFunctions, RedisScripts } from '../commands'; -import { AbortError, ClientClosedError, ConnectionTimeoutError, DisconnectsClientError, SocketClosedUnexpectedlyError, WatchError } from '../errors'; +import { AbortError, ClientClosedError, ClientOfflineError, ConnectionTimeoutError, DisconnectsClientError, SocketClosedUnexpectedlyError, WatchError } from '../errors'; import { defineScript } from '../lua-script'; import { spy } from 'sinon'; import { once } from 'events'; @@ -874,4 +874,20 @@ describe('Client', () => { pingInterval: 1 } }); + + testUtils.testWithClient('should reject commands in connect phase when `disableOfflineQueue`', async client => { + const connectPromise = client.connect(); + await assert.rejects( + client.ping(), + ClientOfflineError + ); + await connectPromise; + await client.disconnect(); + }, { + ...GLOBAL.SERVERS.OPEN, + clientOptions: { + disableOfflineQueue: true + }, + disableClientSetup: true + }); }); diff --git a/packages/client/lib/client/index.ts b/packages/client/lib/client/index.ts index e6f1fef10e5..c4259f72b82 100644 --- a/packages/client/lib/client/index.ts +++ b/packages/client/lib/client/index.ts @@ -11,7 +11,7 @@ import { ScanCommandOptions } from '../commands/SCAN'; import { HScanTuple } from '../commands/HSCAN'; import { attachCommands, attachExtensions, fCallArguments, transformCommandArguments, transformCommandReply, transformLegacyCommandArguments } from '../commander'; import { Pool, Options as PoolOptions, createPool } from 'generic-pool'; -import { ClientClosedError, DisconnectsClientError } from '../errors'; +import { ClientClosedError, ClientOfflineError, DisconnectsClientError } from '../errors'; import { URL } from 'url'; import { TcpSocketConnectOpts } from 'net'; @@ -405,16 +405,16 @@ export default class RedisClient< ): Promise { if (!this.#socket.isOpen) { return Promise.reject(new ClientClosedError()); - } - - if (options?.isolated) { + } else if (options?.isolated) { return this.executeIsolated(isolatedClient => isolatedClient.sendCommand(args, { ...options, isolated: false }) ); - } + } else if (!this.#socket.isReady && this.#options?.disableOfflineQueue) { + return Promise.reject(new ClientOfflineError()); + } const promise = this.#queue.addCommand(args, options); this.#tick(); diff --git a/packages/client/lib/errors.ts b/packages/client/lib/errors.ts index 3f3b9624987..30709703153 100644 --- a/packages/client/lib/errors.ts +++ b/packages/client/lib/errors.ts @@ -22,6 +22,12 @@ export class ClientClosedError extends Error { } } +export class ClientOfflineError extends Error { + constructor() { + super('The client is offline'); + } +} + export class DisconnectsClientError extends Error { constructor() { super('Disconnects client'); From 28b9701543b548ad0520ac4dcda7f057caa08491 Mon Sep 17 00:00:00 2001 From: Leibale Eidelman Date: Thu, 24 Nov 2022 14:01:43 -0500 Subject: [PATCH 1283/1748] fix #2318 - add MULTI (uppercase) (#2324) --- packages/client/lib/client/index.ts | 4 +++- packages/client/lib/cluster/index.ts | 4 +++- 2 files changed, 6 insertions(+), 2 deletions(-) diff --git a/packages/client/lib/client/index.ts b/packages/client/lib/client/index.ts index c4259f72b82..e1ddb64b9a7 100644 --- a/packages/client/lib/client/index.ts +++ b/packages/client/lib/client/index.ts @@ -619,13 +619,15 @@ export default class RedisClient< return this.#isolationPool.use(fn); } - multi(): RedisClientMultiCommandType { + MULTI(): RedisClientMultiCommandType { return new (this as any).Multi( this.multiExecutor.bind(this), this.#options?.legacyMode ); } + multi = this.MULTI; + async multiExecutor( commands: Array, selectedDB?: number, diff --git a/packages/client/lib/cluster/index.ts b/packages/client/lib/cluster/index.ts index 57ec6ff7050..6eafdda86ce 100644 --- a/packages/client/lib/cluster/index.ts +++ b/packages/client/lib/cluster/index.ts @@ -224,7 +224,7 @@ export default class RedisCluster< } } - multi(routing?: RedisCommandArgument): RedisClusterMultiCommandType { + MULTI(routing?: RedisCommandArgument): RedisClusterMultiCommandType { return new this.#Multi( (commands: Array, firstKey?: RedisCommandArgument, chainId?: symbol) => { return this.#execute( @@ -237,6 +237,8 @@ export default class RedisCluster< ); } + multi = this.MULTI; + getMasters(): Array> { return this.#slots.getMasters(); } From 13ad249ae626f76bb5d2ec7fe02b5dcfd37b150f Mon Sep 17 00:00:00 2001 From: Leibale Eidelman Date: Thu, 24 Nov 2022 14:03:34 -0500 Subject: [PATCH 1284/1748] fix #2010 - stop reconnect after .disconnect() (#2323) * fix #2010 - stop reconnect after .disconnect() * fix quit --- packages/client/lib/client/socket.ts | 26 ++++++++++++++++++-------- 1 file changed, 18 insertions(+), 8 deletions(-) diff --git a/packages/client/lib/client/socket.ts b/packages/client/lib/client/socket.ts index fabc22038d0..cc9d04c7b2f 100644 --- a/packages/client/lib/client/socket.ts +++ b/packages/client/lib/client/socket.ts @@ -105,6 +105,7 @@ export default class RedisSocket extends EventEmitter { throw new Error('Socket already opened'); } + this.#isOpen = true; return this.#connect(); } @@ -116,7 +117,6 @@ export default class RedisSocket extends EventEmitter { } try { - this.#isOpen = true; this.#socket = await this.#createSocket(); this.#writableNeedDrain = false; this.emit('connect'); @@ -142,7 +142,7 @@ export default class RedisSocket extends EventEmitter { await promiseTimeout(retryIn); } retries++; - } while (!this.#isReady); + } while (this.#isOpen && !this.#isReady); } #createSocket(): Promise { @@ -203,6 +203,8 @@ export default class RedisSocket extends EventEmitter { this.#isReady = false; this.emit('error', err); + if (!this.#isOpen) return; + this.#connect(true).catch(() => { // the error was already emitted, silently ignore it }); @@ -219,14 +221,22 @@ export default class RedisSocket extends EventEmitter { } disconnect(): void { - if (!this.#socket) { + if (!this.#isOpen) { throw new ClientClosedError(); - } else { - this.#isOpen = this.#isReady = false; } - this.#socket.destroy(); - this.#socket = undefined; + this.#isOpen = false; + this.#disconnect(); + } + + #disconnect(): void { + this.#isReady = false; + + if (this.#socket) { + this.#socket.destroy(); + this.#socket = undefined; + } + this.emit('end'); } @@ -237,7 +247,7 @@ export default class RedisSocket extends EventEmitter { this.#isOpen = false; await fn(); - this.disconnect(); + this.#disconnect(); } #isCorked = false; From 3b36963986121b5a593670a6fa973a0bba696866 Mon Sep 17 00:00:00 2001 From: Leibale Eidelman Date: Thu, 24 Nov 2022 14:05:42 -0500 Subject: [PATCH 1285/1748] Release client@1.4.1 --- packages/client/package.json | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/packages/client/package.json b/packages/client/package.json index 849839aaa1e..9074edf9e77 100644 --- a/packages/client/package.json +++ b/packages/client/package.json @@ -1,6 +1,6 @@ { "name": "@redis/client", - "version": "1.4.0", + "version": "1.4.1", "license": "MIT", "main": "./dist/index.js", "types": "./dist/index.d.ts", From 1557164b1c717297a330210a4386f7da6ec53266 Mon Sep 17 00:00:00 2001 From: Leibale Eidelman Date: Thu, 24 Nov 2022 14:10:38 -0500 Subject: [PATCH 1286/1748] Release client@1.4.2 --- packages/client/package.json | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/packages/client/package.json b/packages/client/package.json index 9074edf9e77..43e70381361 100644 --- a/packages/client/package.json +++ b/packages/client/package.json @@ -1,6 +1,6 @@ { "name": "@redis/client", - "version": "1.4.1", + "version": "1.4.2", "license": "MIT", "main": "./dist/index.js", "types": "./dist/index.d.ts", From ec57a9a77a007a7cc335e8fac010403a293df0d6 Mon Sep 17 00:00:00 2001 From: Leibale Eidelman Date: Thu, 24 Nov 2022 14:12:49 -0500 Subject: [PATCH 1287/1748] upgrade subpackages --- package-lock.json | 4 ++-- package.json | 2 +- 2 files changed, 3 insertions(+), 3 deletions(-) diff --git a/package-lock.json b/package-lock.json index 9667eeb8a11..5c4e78c72c0 100644 --- a/package-lock.json +++ b/package-lock.json @@ -13,7 +13,7 @@ ], "dependencies": { "@redis/bloom": "1.1.0", - "@redis/client": "1.4.0", + "@redis/client": "1.4.2", "@redis/graph": "1.1.0", "@redis/json": "1.0.4", "@redis/search": "1.1.0", @@ -8048,7 +8048,7 @@ }, "packages/client": { "name": "@redis/client", - "version": "1.4.0", + "version": "1.4.2", "license": "MIT", "dependencies": { "cluster-key-slot": "1.1.1", diff --git a/package.json b/package.json index f5d2bed426a..64b6f98688d 100644 --- a/package.json +++ b/package.json @@ -24,7 +24,7 @@ }, "dependencies": { "@redis/bloom": "1.1.0", - "@redis/client": "1.4.0", + "@redis/client": "1.4.2", "@redis/graph": "1.1.0", "@redis/json": "1.0.4", "@redis/search": "1.1.0", From 55a3a5165decdd2b9b048a3138fef2b3ecc42969 Mon Sep 17 00:00:00 2001 From: Leibale Eidelman Date: Thu, 24 Nov 2022 14:13:40 -0500 Subject: [PATCH 1288/1748] Release redis@4.5.1 --- package-lock.json | 4 ++-- package.json | 2 +- 2 files changed, 3 insertions(+), 3 deletions(-) diff --git a/package-lock.json b/package-lock.json index 5c4e78c72c0..884a670f840 100644 --- a/package-lock.json +++ b/package-lock.json @@ -1,12 +1,12 @@ { "name": "redis", - "version": "4.5.0", + "version": "4.5.1", "lockfileVersion": 2, "requires": true, "packages": { "": { "name": "redis", - "version": "4.5.0", + "version": "4.5.1", "license": "MIT", "workspaces": [ "./packages/*" diff --git a/package.json b/package.json index 64b6f98688d..a6f01ce76fb 100644 --- a/package.json +++ b/package.json @@ -1,7 +1,7 @@ { "name": "redis", "description": "A modern, high performance Redis client", - "version": "4.5.0", + "version": "4.5.1", "license": "MIT", "main": "./dist/index.js", "types": "./dist/index.d.ts", From 72cbceedfd177aea328448ac2255b56e8be97fe3 Mon Sep 17 00:00:00 2001 From: Chayim Date: Wed, 30 Nov 2022 23:27:41 +0200 Subject: [PATCH 1289/1748] Removing LGTM as the service is going offline shortly (#2335) --- README.md | 1 - 1 file changed, 1 deletion(-) diff --git a/README.md b/README.md index 8ea495bba7b..e1c3bad6a2f 100644 --- a/README.md +++ b/README.md @@ -3,7 +3,6 @@ [![Tests](https://img.shields.io/github/workflow/status/redis/node-redis/Tests/master.svg?label=tests)](https://github.com/redis/node-redis/actions/workflows/tests.yml) [![Coverage](https://codecov.io/gh/redis/node-redis/branch/master/graph/badge.svg?token=xcfqHhJC37)](https://codecov.io/gh/redis/node-redis) [![License](https://img.shields.io/github/license/redis/node-redis.svg)](https://github.com/redis/node-redis/blob/master/LICENSE) -[![LGTM alerts](https://img.shields.io/lgtm/alerts/g/redis/node-redis.svg?logo=LGTM)](https://lgtm.com/projects/g/redis/node-redis/alerts) [![Discord](https://img.shields.io/discord/697882427875393627.svg?style=social&logo=discord)](https://discord.gg/redis) [![Twitch](https://img.shields.io/twitch/status/redisinc?style=social)](https://www.twitch.tv/redisinc) From 118dc111918fb0ae342bc25408d95209c4b20e53 Mon Sep 17 00:00:00 2001 From: Leibale Eidelman Date: Wed, 30 Nov 2022 16:30:31 -0500 Subject: [PATCH 1290/1748] Create codeql.yml (#2336) --- .github/workflows/codeql.yml | 74 ++++++++++++++++++++++++++++++++++++ 1 file changed, 74 insertions(+) create mode 100644 .github/workflows/codeql.yml diff --git a/.github/workflows/codeql.yml b/.github/workflows/codeql.yml new file mode 100644 index 00000000000..3004337c278 --- /dev/null +++ b/.github/workflows/codeql.yml @@ -0,0 +1,74 @@ +# For most projects, this workflow file will not need changing; you simply need +# to commit it to your repository. +# +# You may wish to alter this file to override the set of languages analyzed, +# or to provide custom queries or build logic. +# +# ******** NOTE ******** +# We have attempted to detect the languages in your repository. Please check +# the `language` matrix defined below to confirm you have the correct set of +# supported CodeQL languages. +# +name: "CodeQL" + +on: + push: + branches: [ "master" ] + pull_request: + # The branches below must be a subset of the branches above + branches: [ "master" ] + schedule: + - cron: '43 20 * * 1' + +jobs: + analyze: + name: Analyze + runs-on: ubuntu-latest + permissions: + actions: read + contents: read + security-events: write + + strategy: + fail-fast: false + matrix: + language: [ 'TypeScript' ] + # CodeQL supports [ 'cpp', 'csharp', 'go', 'java', 'javascript', 'python', 'ruby' ] + # Learn more about CodeQL language support at https://aka.ms/codeql-docs/language-support + + steps: + - name: Checkout repository + uses: actions/checkout@v3 + + # Initializes the CodeQL tools for scanning. + - name: Initialize CodeQL + uses: github/codeql-action/init@v2 + with: + languages: ${{ matrix.language }} + # If you wish to specify custom queries, you can do so here or in a config file. + # By default, queries listed here will override any specified in a config file. + # Prefix the list here with "+" to use these queries and those in the config file. + + # Details on CodeQL's query packs refer to : https://docs.github.com/en/code-security/code-scanning/automatically-scanning-your-code-for-vulnerabilities-and-errors/configuring-code-scanning#using-queries-in-ql-packs + # queries: security-extended,security-and-quality + + + # Autobuild attempts to build any compiled languages (C/C++, C#, Go, or Java). + # If this step fails, then you should remove it and run the build manually (see below) + - name: Autobuild + uses: github/codeql-action/autobuild@v2 + + # ℹ️ Command-line programs to run using the OS shell. + # 📚 See https://docs.github.com/en/actions/using-workflows/workflow-syntax-for-github-actions#jobsjob_idstepsrun + + # If the Autobuild fails above, remove it and uncomment the following three lines. + # modify them (or add more) to build your code if your project, please refer to the EXAMPLE below for guidance. + + # - run: | + # echo "Run, Build Application using script" + # ./location_of_script_within_repo/buildscript.sh + + - name: Perform CodeQL Analysis + uses: github/codeql-action/analyze@v2 + with: + category: "/language:${{matrix.language}}" From da3face95180d5ab801716de1ff4ba97e6cbb6ab Mon Sep 17 00:00:00 2001 From: Simon Prickett Date: Tue, 13 Dec 2022 20:19:45 +0000 Subject: [PATCH 1291/1748] Add connection status check example and documentation. (#2340) * Adds example for transactions with arbitrary commands. * Formatting. * Adds isReady doc and example for isReady and isOpen. * Improved example. * Added isOpen explanation. * Removed example from a different PR. --- README.md | 2 ++ examples/README.md | 52 +++++++++++++++-------------- examples/check-connection-status.js | 28 ++++++++++++++++ 3 files changed, 57 insertions(+), 25 deletions(-) create mode 100644 examples/check-connection-status.js diff --git a/README.md b/README.md index e1c3bad6a2f..f3893eecca3 100644 --- a/README.md +++ b/README.md @@ -64,6 +64,8 @@ createClient({ You can also use discrete parameters, UNIX sockets, and even TLS to connect. Details can be found in the [client configuration guide](./docs/client-configuration.md). +To check if the the client is connected and ready to send commands, use `client.isReady` which returns a boolean. `client.isOpen` is also available. This returns `true` when the client's underlying socket is open, and `false` when it isn't (for example when the client is still connecting or reconnecting after a network error). + ### Redis Commands There is built-in support for all of the [out-of-the-box Redis commands](https://redis.io/commands). They are exposed using the raw Redis command names (`HSET`, `HGETALL`, etc.) and a friendlier camel-cased version (`hSet`, `hGetAll`, etc.): diff --git a/examples/README.md b/examples/README.md index 7de209d2c85..ce3489fb598 100644 --- a/examples/README.md +++ b/examples/README.md @@ -2,31 +2,33 @@ 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 | -| `bloom-filter.js` | Space efficient set membership checks with a [Bloom Filter](https://en.wikipedia.org/wiki/Bloom_filter) using [RedisBloom](https://redisbloom.io) | -| `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 | -| `connect-to-cluster.js` | Connect to Redis cluster | -| `count-min-sketch.js` | Estimate the frequency of a given event using the [RedisBloom](https://redisbloom.io) Count-Min Sketch | -| `cuckoo-filter.js` | Space efficient set membership checks with a [Cuckoo Filter](https://en.wikipedia.org/wiki/Cuckoo_filter) using [RedisBloom](https://redisbloom.io) | -| `get-server-time.js` | Get the time from the Redis server | -| `hyperloglog.js` | Showing use of Hyperloglog commands [PFADD, PFCOUNT and PFMERGE](https://redis.io/commands/?group=hyperloglog) | -| `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/) | -| `pubsub-publisher.js` | Adds multiple messages on 2 different channels messages to Redis | -| `pubsub-subscriber.js` | Reads messages from channels using `PSUBSCRIBE` command | -| `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 | -| `sorted-set.js` | Add members with scores to a Sorted Set and retrieve them using the ZSCAN iteractor functionality | -| `stream-producer.js` | Adds entries to a [Redis Stream](https://redis.io/topics/streams-intro) using the `XADD` command | -| `stream-consumer.js` | Reads entries from a [Redis Stream](https://redis.io/topics/streams-intro) using the blocking `XREAD` command | -| `time-series.js` | Create, populate and query timeseries data with [Redis Timeseries](https://redistimeseries.io) | -| `topk.js` | Use the [RedisBloom](https://redisbloom.io) TopK to track the most frequently seen items. | -| `stream-consumer-group.js` | Reads entties from a [Redis Stream](https://redis.io/topics/streams-intro) as part of a consumer group using the blocking `XREADGROUP` command | -| `transaction-with-watch.js` | An Example of [Redis transaction](https://redis.io/docs/manual/transactions) with `WATCH` command on isolated connection with optimistic locking | +| File Name | Description | +| ---------------------------------------- | ---------------------------------------------------------------------------------------------------------------------------------------------------- | +| `blocking-list-pop.js` | Block until an element is pushed to a list. | +| `bloom-filter.js` | Space efficient set membership checks with a [Bloom Filter](https://en.wikipedia.org/wiki/Bloom_filter) using [RedisBloom](https://redisbloom.io). | +| `check-connection-status.js` | Check the client's connection status. | +| `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. | +| `connect-to-cluster.js` | Connect to a Redis cluster. | +| `count-min-sketch.js` | Estimate the frequency of a given event using the [RedisBloom](https://redisbloom.io) Count-Min Sketch. | +| `cuckoo-filter.js` | Space efficient set membership checks with a [Cuckoo Filter](https://en.wikipedia.org/wiki/Cuckoo_filter) using [RedisBloom](https://redisbloom.io). | +| `get-server-time.js` | Get the time from the Redis server. | +| `hyperloglog.js` | Showing use of Hyperloglog commands [PFADD, PFCOUNT and PFMERGE](https://redis.io/commands/?group=hyperloglog). | +| `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/). | +| `pubsub-publisher.js` | Adds multiple messages on 2 different channels messages to Redis. | +| `pubsub-subscriber.js` | Reads messages from channels using `PSUBSCRIBE` command. | +| `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. | +| `sorted-set.js` | Add members with scores to a Sorted Set and retrieve them using the ZSCAN iteractor functionality. | +| `stream-producer.js` | Adds entries to a [Redis Stream](https://redis.io/topics/streams-intro) using the `XADD` command. | +| `stream-consumer.js` | Reads entries from a [Redis Stream](https://redis.io/topics/streams-intro) using the blocking `XREAD` command. | +| `time-series.js` | Create, populate and query timeseries data with [Redis Timeseries](https://redistimeseries.io). | +| `topk.js` | Use the [RedisBloom](https://redisbloom.io) TopK to track the most frequently seen items. | +| `stream-consumer-group.js` | Reads entries from a [Redis Stream](https://redis.io/topics/streams-intro) as part of a consumer group using the blocking `XREADGROUP` command. | +| `tranaaction-with-arbitrary-commands.js` | Mix and match supported commands with arbitrary command strings in a Redis transaction. | +| `transaction-with-watch.js` | An Example of [Redis transaction](https://redis.io/docs/manual/transactions) with `WATCH` command on isolated connection with optimistic locking. | ## Contributing diff --git a/examples/check-connection-status.js b/examples/check-connection-status.js new file mode 100644 index 00000000000..0ccf8ff5e21 --- /dev/null +++ b/examples/check-connection-status.js @@ -0,0 +1,28 @@ +// Check the connection status of the Redis client instance. +import { createClient } from 'redis'; + +const client = createClient(); + +console.log('Before client.connect()...'); + +// isOpen will return False here as the client's socket is not open yet. +// isReady will return False here, client is not yet ready to use. +console.log(`client.isOpen: ${client.isOpen}, client.isReady: ${client.isReady}`); + +// Begin connection process... +const connectPromise = client.connect(); + +console.log('After client.connect()...'); + +// isOpen will return True here as the client's socket is open now. +// isReady will return False here as the promise hasn't resolved yet. +console.log(`client.isOpen: ${client.isOpen}, client.isReady: ${client.isReady}`); + +await connectPromise; +console.log('Afer connectPromise has resolved...'); + +// isOpen will return True here as the client's socket is open now. +// isReady will return True here, client is ready to use. +console.log(`client.isOpen: ${client.isOpen}, client.isReady: ${client.isReady}`); + +await client.quit(); From ab69c81c363998f3e15b567e8fef45b34abed41f Mon Sep 17 00:00:00 2001 From: Vladimir Chuprazov <82871772+VladimirChuprazov@users.noreply.github.com> Date: Thu, 15 Dec 2022 00:03:30 +0200 Subject: [PATCH 1292/1748] fix(search): fix types for sort (#2343) --- packages/search/lib/commands/index.ts | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/packages/search/lib/commands/index.ts b/packages/search/lib/commands/index.ts index bc8eaf140fb..6eca34a4a0f 100644 --- a/packages/search/lib/commands/index.ts +++ b/packages/search/lib/commands/index.ts @@ -134,8 +134,8 @@ export enum RedisSearchLanguages { export type PropertyName = `${'@' | '$.'}${string}`; -export type SortByProperty = PropertyName | { - BY: PropertyName; +export type SortByProperty = string | { + BY: string; DIRECTION?: 'ASC' | 'DESC'; }; From ce1b8f7f4eaa1b3b461e4bca3f390d1d91bee24a Mon Sep 17 00:00:00 2001 From: Leibale Eidelman Date: Wed, 14 Dec 2022 17:11:28 -0500 Subject: [PATCH 1293/1748] ref #1982 - fix redisearch schema types (#2348) --- packages/search/lib/commands/index.ts | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/packages/search/lib/commands/index.ts b/packages/search/lib/commands/index.ts index 6eca34a4a0f..74a49cb1bba 100644 --- a/packages/search/lib/commands/index.ts +++ b/packages/search/lib/commands/index.ts @@ -187,7 +187,7 @@ export enum SchemaFieldTypes { type CreateSchemaField< T extends SchemaFieldTypes, - E = Record + E = Record > = T | ({ type: T; AS?: string; @@ -195,7 +195,7 @@ type CreateSchemaField< type CreateSchemaCommonField< T extends SchemaFieldTypes, - E = Record + E = Record > = CreateSchemaField< T, ({ From f6093b7b0fc3e598e5de913162537f84af5f4447 Mon Sep 17 00:00:00 2001 From: Vojtech Novak Date: Thu, 15 Dec 2022 18:04:13 +0100 Subject: [PATCH 1294/1748] fix: zero ttl is ignored (#2349) * fix: zero ttl is ignored * Update SET.ts * Update SET.ts Co-authored-by: Leibale Eidelman --- packages/client/lib/commands/SET.spec.ts | 20 ++++++++++---------- packages/client/lib/commands/SET.ts | 8 ++++---- 2 files changed, 14 insertions(+), 14 deletions(-) diff --git a/packages/client/lib/commands/SET.spec.ts b/packages/client/lib/commands/SET.spec.ts index f09f8621f28..0b3331fd3a4 100644 --- a/packages/client/lib/commands/SET.spec.ts +++ b/packages/client/lib/commands/SET.spec.ts @@ -13,8 +13,8 @@ describe('SET', () => { it('number', () => { assert.deepEqual( - transformArguments('key', 1), - ['SET', 'key', '1'] + transformArguments('key', 0), + ['SET', 'key', '0'] ); }); @@ -22,36 +22,36 @@ describe('SET', () => { it('with EX', () => { assert.deepEqual( transformArguments('key', 'value', { - EX: 1 + EX: 0 }), - ['SET', 'key', 'value', 'EX', '1'] + ['SET', 'key', 'value', 'EX', '0'] ); }); it('with PX', () => { assert.deepEqual( transformArguments('key', 'value', { - PX: 1 + PX: 0 }), - ['SET', 'key', 'value', 'PX', '1'] + ['SET', 'key', 'value', 'PX', '0'] ); }); it('with EXAT', () => { assert.deepEqual( transformArguments('key', 'value', { - EXAT: 1 + EXAT: 0 }), - ['SET', 'key', 'value', 'EXAT', '1'] + ['SET', 'key', 'value', 'EXAT', '0'] ); }); it('with PXAT', () => { assert.deepEqual( transformArguments('key', 'value', { - PXAT: 1 + PXAT: 0 }), - ['SET', 'key', 'value', 'PXAT', '1'] + ['SET', 'key', 'value', 'PXAT', '0'] ); }); diff --git a/packages/client/lib/commands/SET.ts b/packages/client/lib/commands/SET.ts index 7c679b7084e..08ae56552b9 100644 --- a/packages/client/lib/commands/SET.ts +++ b/packages/client/lib/commands/SET.ts @@ -35,13 +35,13 @@ export function transformArguments( typeof value === 'number' ? value.toString() : value ]; - if (options?.EX) { + if (options?.EX !== undefined) { args.push('EX', options.EX.toString()); - } else if (options?.PX) { + } else if (options?.PX !== undefined) { args.push('PX', options.PX.toString()); - } else if (options?.EXAT) { + } else if (options?.EXAT !== undefined) { args.push('EXAT', options.EXAT.toString()); - } else if (options?.PXAT) { + } else if (options?.PXAT !== undefined) { args.push('PXAT', options.PXAT.toString()); } else if (options?.KEEPTTL) { args.push('KEEPTTL'); From 5a41d6d60b02db29c602d7bba3ab1ce509560882 Mon Sep 17 00:00:00 2001 From: Leibale Eidelman Date: Mon, 19 Dec 2022 16:15:19 -0500 Subject: [PATCH 1295/1748] Update README.md https://github.com/badges/shields/issues/8671 --- README.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/README.md b/README.md index f3893eecca3..416928c688a 100644 --- a/README.md +++ b/README.md @@ -1,6 +1,6 @@ # Node-Redis -[![Tests](https://img.shields.io/github/workflow/status/redis/node-redis/Tests/master.svg?label=tests)](https://github.com/redis/node-redis/actions/workflows/tests.yml) +[![Tests](https://img.shields.io/github/actions/workflow/status/redis/node-redis/tests.yml?branch=master)](https://github.com/redis/node-redis/actions/workflows/tests.yml) [![Coverage](https://codecov.io/gh/redis/node-redis/branch/master/graph/badge.svg?token=xcfqHhJC37)](https://codecov.io/gh/redis/node-redis) [![License](https://img.shields.io/github/license/redis/node-redis.svg)](https://github.com/redis/node-redis/blob/master/LICENSE) From 9dccd9a678af1318166e75e956bb557800ada561 Mon Sep 17 00:00:00 2001 From: Leibale Eidelman Date: Mon, 26 Dec 2022 05:22:01 -0500 Subject: [PATCH 1296/1748] Update README.md --- packages/search/README.md | 15 +++++++-------- 1 file changed, 7 insertions(+), 8 deletions(-) diff --git a/packages/search/README.md b/packages/search/README.md index e4b05fe7cfc..e7d99cf75e1 100644 --- a/packages/search/README.md +++ b/packages/search/README.md @@ -18,15 +18,14 @@ Before we can perform any searches, we need to tell RediSearch how to index our await client.ft.create('idx:animals', { name: { type: SchemaFieldTypes.TEXT, - sortable: true + SORTABLE: true }, - species: SchemaFieldTypes.TAG, - age: SchemaFieldTypes.NUMERIC - }, { - ON: 'HASH', - PREFIX: 'noderedis:animals' - } -); + 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. From c5b6f77c338c84a43f6dd4924277010ea029552a Mon Sep 17 00:00:00 2001 From: Leibale Eidelman Date: Mon, 26 Dec 2022 13:35:37 -0500 Subject: [PATCH 1297/1748] exclude dist from coverage report --- packages/bloom/.nycrc.json | 2 +- packages/client/.nycrc.json | 2 +- packages/graph/.nycrc.json | 2 +- packages/json/.nycrc.json | 2 +- packages/search/.nycrc.json | 2 +- packages/time-series/.nycrc.json | 2 +- 6 files changed, 6 insertions(+), 6 deletions(-) diff --git a/packages/bloom/.nycrc.json b/packages/bloom/.nycrc.json index b4e671e178f..367a89ad32c 100644 --- a/packages/bloom/.nycrc.json +++ b/packages/bloom/.nycrc.json @@ -1,4 +1,4 @@ { "extends": "@istanbuljs/nyc-config-typescript", - "exclude": ["**/*.spec.ts", "lib/test-utils.ts"] + "exclude": ["dist", "**/*.spec.ts", "lib/test-utils.ts"] } diff --git a/packages/client/.nycrc.json b/packages/client/.nycrc.json index dd42463d9cb..51e807f9cef 100644 --- a/packages/client/.nycrc.json +++ b/packages/client/.nycrc.json @@ -1,4 +1,4 @@ { "extends": "@istanbuljs/nyc-config-typescript", - "exclude": ["**/*.spec.ts", "lib/test-utils.ts", "examples/*"] + "exclude": ["dist", "**/*.spec.ts", "lib/test-utils.ts", "examples/*"] } diff --git a/packages/graph/.nycrc.json b/packages/graph/.nycrc.json index b4e671e178f..367a89ad32c 100644 --- a/packages/graph/.nycrc.json +++ b/packages/graph/.nycrc.json @@ -1,4 +1,4 @@ { "extends": "@istanbuljs/nyc-config-typescript", - "exclude": ["**/*.spec.ts", "lib/test-utils.ts"] + "exclude": ["dist", "**/*.spec.ts", "lib/test-utils.ts"] } diff --git a/packages/json/.nycrc.json b/packages/json/.nycrc.json index b4e671e178f..367a89ad32c 100644 --- a/packages/json/.nycrc.json +++ b/packages/json/.nycrc.json @@ -1,4 +1,4 @@ { "extends": "@istanbuljs/nyc-config-typescript", - "exclude": ["**/*.spec.ts", "lib/test-utils.ts"] + "exclude": ["dist", "**/*.spec.ts", "lib/test-utils.ts"] } diff --git a/packages/search/.nycrc.json b/packages/search/.nycrc.json index b4e671e178f..367a89ad32c 100644 --- a/packages/search/.nycrc.json +++ b/packages/search/.nycrc.json @@ -1,4 +1,4 @@ { "extends": "@istanbuljs/nyc-config-typescript", - "exclude": ["**/*.spec.ts", "lib/test-utils.ts"] + "exclude": ["dist", "**/*.spec.ts", "lib/test-utils.ts"] } diff --git a/packages/time-series/.nycrc.json b/packages/time-series/.nycrc.json index b4e671e178f..367a89ad32c 100644 --- a/packages/time-series/.nycrc.json +++ b/packages/time-series/.nycrc.json @@ -1,4 +1,4 @@ { "extends": "@istanbuljs/nyc-config-typescript", - "exclude": ["**/*.spec.ts", "lib/test-utils.ts"] + "exclude": ["dist", "**/*.spec.ts", "lib/test-utils.ts"] } From a55fbafb889ebd7e8463bfa364005d806455abbb Mon Sep 17 00:00:00 2001 From: shacharPash <93581407+shacharPash@users.noreply.github.com> Date: Wed, 18 Jan 2023 19:53:00 +0200 Subject: [PATCH 1298/1748] Add support for BF.CARD command (#2376) * Add support for BF.CARD command * Update index.ts * Update CARD.ts * Update CARD.spec.ts Co-authored-by: Leibale Eidelman --- .../bloom/lib/commands/bloom/CARD.spec.ts | 19 +++++++++++++++++++ packages/bloom/lib/commands/bloom/CARD.ts | 9 +++++++++ packages/bloom/lib/commands/bloom/index.ts | 3 +++ 3 files changed, 31 insertions(+) create mode 100644 packages/bloom/lib/commands/bloom/CARD.spec.ts create mode 100644 packages/bloom/lib/commands/bloom/CARD.ts diff --git a/packages/bloom/lib/commands/bloom/CARD.spec.ts b/packages/bloom/lib/commands/bloom/CARD.spec.ts new file mode 100644 index 00000000000..4d5620ea196 --- /dev/null +++ b/packages/bloom/lib/commands/bloom/CARD.spec.ts @@ -0,0 +1,19 @@ +import { strict as assert } from 'assert'; +import testUtils, { GLOBAL } from '../../test-utils'; +import { transformArguments } from './CARD'; + +describe('BF CARD', () => { + it('transformArguments', () => { + assert.deepEqual( + transformArguments('bloom'), + ['BF.CARD', 'bloom'] + ); + }); + + testUtils.testWithClient('client.bf.card', async client => { + assert.equal( + await client.bf.card('key'), + 0 + ); + }, GLOBAL.SERVERS.OPEN); +}); diff --git a/packages/bloom/lib/commands/bloom/CARD.ts b/packages/bloom/lib/commands/bloom/CARD.ts new file mode 100644 index 00000000000..530284c3f60 --- /dev/null +++ b/packages/bloom/lib/commands/bloom/CARD.ts @@ -0,0 +1,9 @@ +export const FIRST_KEY_INDEX = 1; + +export const IS_READ_ONLY = true; + +export function transformArguments(key: string): Array { + return ['BF.CARD', key]; +} + +export declare function transformReply(): number; diff --git a/packages/bloom/lib/commands/bloom/index.ts b/packages/bloom/lib/commands/bloom/index.ts index c57b0f79534..f18b8f71095 100644 --- a/packages/bloom/lib/commands/bloom/index.ts +++ b/packages/bloom/lib/commands/bloom/index.ts @@ -1,4 +1,5 @@ import * as ADD from './ADD'; +import * as CARD from './CARD'; import * as EXISTS from './EXISTS'; import * as INFO from './INFO'; import * as INSERT from './INSERT'; @@ -11,6 +12,8 @@ import * as SCANDUMP from './SCANDUMP'; export default { ADD, add: ADD, + CARD, + card: CARD, EXISTS, exists: EXISTS, INFO, From e895fa1d71f497e9fffdba79e7cb1834a50a16dd Mon Sep 17 00:00:00 2001 From: Chayim Date: Wed, 18 Jan 2023 19:53:09 +0200 Subject: [PATCH 1299/1748] Adding redis-stack-docker (#2380) --- README.md | 8 ++++++++ 1 file changed, 8 insertions(+) diff --git a/README.md b/README.md index 416928c688a..2a894f50c5e 100644 --- a/README.md +++ b/README.md @@ -28,6 +28,14 @@ node-redis is a modern, high performance [Redis](https://redis.io) client for No ## Installation +Start a redis via docker: + +``` bash +docker run -p 6379:6379 -it redis/redis-stack-server:latest +``` + +To install node-redis, simply: + ```bash npm install redis ``` From aa75ee49c6600e805003ac52055a6d45782514e4 Mon Sep 17 00:00:00 2001 From: Brandon <29527680+ChronicLynx@users.noreply.github.com> Date: Wed, 18 Jan 2023 10:54:29 -0700 Subject: [PATCH 1300/1748] #2038 Resolve legacy mode hGetAll returning in the wrong format compared to v3 results (#2367) * Ensure that transformReply is optionally passed through to commands in legacy mode within multi * Execute transformReply on legacy #sendCommand * Scope transform changes to hGetAll * Extensible method of transforming legacy replies, expands RedisCommand interface * check `TRANSFORM_LEGACY_REPLY` on client creation (rather then on command exec), add tests Co-authored-by: Leibale Eidelman --- packages/client/lib/client/index.spec.ts | 87 ++++++++++++--------- packages/client/lib/client/index.ts | 61 ++++++++------- packages/client/lib/client/multi-command.ts | 24 +++--- packages/client/lib/commands/HGETALL.ts | 2 + packages/client/lib/commands/index.ts | 1 + 5 files changed, 97 insertions(+), 78 deletions(-) diff --git a/packages/client/lib/client/index.spec.ts b/packages/client/lib/client/index.spec.ts index 4eee7076295..5ad3ace3fe4 100644 --- a/packages/client/lib/client/index.spec.ts +++ b/packages/client/lib/client/index.spec.ts @@ -8,6 +8,7 @@ import { defineScript } from '../lua-script'; import { spy } from 'sinon'; import { once } from 'events'; import { ClientKillFilters } from '../commands/CLIENT_KILL'; +import { promisify } from 'util'; export const SQUARE_SCRIPT = defineScript({ SCRIPT: 'return ARGV[1] * ARGV[1];', @@ -142,26 +143,9 @@ describe('Client', () => { }); describe('legacyMode', () => { - function sendCommandAsync< - M extends RedisModules, - F extends RedisFunctions, - S extends RedisScripts - >( - client: RedisClientType, - args: RedisCommandArguments - ): Promise { - return new Promise((resolve, reject) => { - (client as any).sendCommand(args, (err: Error | undefined, reply: RedisCommandRawReply) => { - if (err) return reject(err); - - resolve(reply); - }); - }); - } - testUtils.testWithClient('client.sendCommand should call the callback', async client => { assert.equal( - await sendCommandAsync(client, ['PING']), + await promisify(client.sendCommand).call(client, 'PING'), 'PONG' ); }, { @@ -193,26 +177,9 @@ describe('Client', () => { } }); - function setAsync< - M extends RedisModules, - F extends RedisFunctions, - S extends RedisScripts - >( - client: RedisClientType, - ...args: Array - ): Promise { - return new Promise((resolve, reject) => { - (client as any).set(...args, (err: Error | undefined, reply: RedisCommandRawReply) => { - if (err) return reject(err); - - resolve(reply); - }); - }); - } - testUtils.testWithClient('client.{command} should accept vardict arguments', async client => { assert.equal( - await setAsync(client, 'a', 'b'), + await promisify(client.set).call(client, 'a', 'b'), 'OK' ); }, { @@ -224,7 +191,7 @@ describe('Client', () => { testUtils.testWithClient('client.{command} should accept arguments array', async client => { assert.equal( - await setAsync(client, ['a', 'b']), + await promisify(client.set).call(client, ['a', 'b']), 'OK' ); }, { @@ -236,7 +203,7 @@ describe('Client', () => { testUtils.testWithClient('client.{command} should accept mix of arrays and arguments', async client => { assert.equal( - await setAsync(client, ['a'], 'b', ['EX', 1]), + await promisify(client.set).call(client, ['a'], 'b', ['EX', 1]), 'OK' ); }, { @@ -246,6 +213,26 @@ describe('Client', () => { } }); + testUtils.testWithClient('client.hGetAll should return object', async client => { + await client.v4.hSet('key', 'field', 'value'); + + assert.deepEqual( + await promisify(client.hGetAll).call(client, 'key'), + Object.create(null, { + field: { + value: 'value', + configurable: true, + enumerable: true + } + }) + ); + }, { + ...GLOBAL.SERVERS.OPEN, + clientOptions: { + legacyMode: true + } + }); + function multiExecAsync< M extends RedisModules, F extends RedisFunctions, @@ -330,6 +317,30 @@ describe('Client', () => { } }); + testUtils.testWithClient('client.multi.hGetAll should return object', async client => { + assert.deepEqual( + await multiExecAsync( + client.multi() + .hSet('key', 'field', 'value') + .hGetAll('key') + ), + [ + 1, + Object.create(null, { + field: { + value: 'value', + configurable: true, + enumerable: true + } + }) + ] + ); + }, { + ...GLOBAL.SERVERS.OPEN, + clientOptions: { + legacyMode: true + } + }); }); describe('events', () => { diff --git a/packages/client/lib/client/index.ts b/packages/client/lib/client/index.ts index e1ddb64b9a7..02baa9c8d03 100644 --- a/packages/client/lib/client/index.ts +++ b/packages/client/lib/client/index.ts @@ -1,5 +1,5 @@ import COMMANDS from './commands'; -import { RedisCommand, RedisCommandArguments, RedisCommandRawReply, RedisCommandReply, RedisFunctions, RedisModules, RedisExtensions, RedisScript, RedisScripts, RedisCommandSignature, ConvertArgumentType, RedisFunction, ExcludeMappedString } from '../commands'; +import { RedisCommand, RedisCommandArguments, RedisCommandRawReply, RedisCommandReply, RedisFunctions, RedisModules, RedisExtensions, RedisScript, RedisScripts, RedisCommandSignature, ConvertArgumentType, RedisFunction, ExcludeMappedString, RedisCommands } from '../commands'; import RedisSocket, { RedisSocketOptions, RedisTlsSocketOptions } from './socket'; import RedisCommandsQueue, { PubSubListener, PubSubSubscribeCommands, PubSubUnsubscribeCommands, QueueCommandOptions } from './commands-queue'; import RedisClientMultiCommand, { RedisClientMultiCommandType } from './multi-command'; @@ -300,34 +300,14 @@ export default class RedisClient< (this as any).#v4.sendCommand = this.#sendCommand.bind(this); (this as any).sendCommand = (...args: Array): void => { - let callback: ClientLegacyCallback; - if (typeof args[args.length - 1] === 'function') { - callback = args.pop() as ClientLegacyCallback; + const result = this.#legacySendCommand(...args); + if (result) { + result.promise.then(reply => result.callback(null, reply)); } - - this.#sendCommand(transformLegacyCommandArguments(args)) - .then((reply: RedisCommandRawReply) => { - if (!callback) return; - - // https://github.com/NodeRedis/node-redis#commands:~:text=minimal%20parsing - - callback(null, reply); - }) - .catch((err: Error) => { - if (!callback) { - this.emit('error', err); - return; - } - - callback(err); - }); }; - for (const name of Object.keys(COMMANDS)) { - this.#defineLegacyCommand(name); - } - - for (const name of Object.keys(COMMANDS)) { + for (const [ name, command ] of Object.entries(COMMANDS as RedisCommands)) { + this.#defineLegacyCommand(name, command); (this as any)[name.toLowerCase()] = (this as any)[name]; } @@ -346,10 +326,31 @@ export default class RedisClient< this.#defineLegacyCommand('quit'); } - #defineLegacyCommand(name: string): void { - this.#v4[name] = (this as any)[name].bind(this); - (this as any)[name] = - (...args: Array): void => (this as any).sendCommand(name, ...args); + #legacySendCommand(...args: Array) { + const callback = typeof args[args.length - 1] === 'function' ? + args.pop() as ClientLegacyCallback : + undefined; + + const promise = this.#sendCommand(transformLegacyCommandArguments(args)); + if (callback) return { + promise, + callback + }; + promise.catch(err => this.emit('error', err)); + } + + #defineLegacyCommand(this: any, name: string, command?: RedisCommand): void { + this.#v4[name] = this[name].bind(this); + this[name] = command && command.TRANSFORM_LEGACY_REPLY && command.transformReply ? + (...args: Array) => { + const result = this.#legacySendCommand(name, ...args); + if (result) { + result.promise.then((reply: any) => { + result.callback(null, command.transformReply!(reply)); + }); + } + } : + (...args: Array) => this.sendCommand(name, ...args); } #pingTimer?: NodeJS.Timer; diff --git a/packages/client/lib/client/multi-command.ts b/packages/client/lib/client/multi-command.ts index 1d6df1a483e..4a3b668b758 100644 --- a/packages/client/lib/client/multi-command.ts +++ b/packages/client/lib/client/multi-command.ts @@ -1,5 +1,5 @@ import COMMANDS from './commands'; -import { RedisCommand, RedisCommandArguments, RedisCommandRawReply, RedisFunctions, RedisModules, RedisExtensions, RedisScript, RedisScripts, ExcludeMappedString, RedisFunction } from '../commands'; +import { RedisCommand, RedisCommandArguments, RedisCommandRawReply, RedisFunctions, RedisModules, RedisExtensions, RedisScript, RedisScripts, ExcludeMappedString, RedisFunction, RedisCommands } from '../commands'; import RedisMultiCommand, { RedisMultiQueuedCommand } from '../multi-command'; import { attachCommands, attachExtensions, transformLegacyCommandArguments } from '../commander'; @@ -117,19 +117,23 @@ export default class RedisClientMultiCommand { }); }; - for (const name of Object.keys(COMMANDS)) { - this.#defineLegacyCommand(name); - } - - for (const name of Object.keys(COMMANDS)) { + for (const [ name, command ] of Object.entries(COMMANDS as RedisCommands)) { + this.#defineLegacyCommand(name, command); (this as any)[name.toLowerCase()] = (this as any)[name]; } } - #defineLegacyCommand(name: string): void { - this.v4[name] = (this as any)[name].bind(this.v4); - (this as any)[name] = - (...args: Array): void => (this as any).addCommand(name, ...args); + #defineLegacyCommand(this: any, name: string, command?: RedisCommand): void { + this.v4[name] = this[name].bind(this.v4); + this[name] = command && command.TRANSFORM_LEGACY_REPLY && command.transformReply ? + (...args: Array) => { + this.#multi.addCommand( + [name, ...transformLegacyCommandArguments(args)], + command.transformReply + ); + return this; + } : + (...args: Array) => this.addCommand(name, ...args); } commandsExecutor(command: RedisCommand, args: Array): this { diff --git a/packages/client/lib/commands/HGETALL.ts b/packages/client/lib/commands/HGETALL.ts index 1ea702080b7..bf51760ff0e 100644 --- a/packages/client/lib/commands/HGETALL.ts +++ b/packages/client/lib/commands/HGETALL.ts @@ -4,6 +4,8 @@ export const FIRST_KEY_INDEX = 1; export const IS_READ_ONLY = true; +export const TRANSFORM_LEGACY_REPLY = true; + export function transformArguments(key: RedisCommandArgument): RedisCommandArguments { return ['HGETALL', key]; } diff --git a/packages/client/lib/commands/index.ts b/packages/client/lib/commands/index.ts index 5e62ea20de2..60f9720c8d1 100644 --- a/packages/client/lib/commands/index.ts +++ b/packages/client/lib/commands/index.ts @@ -11,6 +11,7 @@ export type RedisCommandArguments = Array & { preserve?: u export interface RedisCommand { FIRST_KEY_INDEX?: number | ((...args: Array) => RedisCommandArgument | undefined); IS_READ_ONLY?: boolean; + TRANSFORM_LEGACY_REPLY?: boolean; transformArguments(this: void, ...args: Array): RedisCommandArguments; transformReply?(this: void, reply: any, preserved?: any): any; } From a1dfa22517c8a4d9d7ad5da418b3827f472a275c Mon Sep 17 00:00:00 2001 From: Leibale Eidelman Date: Wed, 18 Jan 2023 12:54:42 -0500 Subject: [PATCH 1301/1748] fix #2364 - fix FT.SEARCH RETURN [] (#2366) * fix #2364 - fix FT.SEARCH RETURN [] * remove console.log --- packages/search/lib/commands/SEARCH.spec.ts | 39 +++++---------------- packages/search/lib/commands/SEARCH.ts | 34 ++++++++++++++---- 2 files changed, 35 insertions(+), 38 deletions(-) diff --git a/packages/search/lib/commands/SEARCH.spec.ts b/packages/search/lib/commands/SEARCH.spec.ts index aecf6c8b1a6..a5d8ae9e6c4 100644 --- a/packages/search/lib/commands/SEARCH.spec.ts +++ b/packages/search/lib/commands/SEARCH.spec.ts @@ -236,7 +236,7 @@ describe('SEARCH', () => { }); describe('client.ft.search', () => { - testUtils.testWithClient('DIALECT 1', async client => { + testUtils.testWithClient('without optional options', async client => { await Promise.all([ client.ft.create('index', { field: SchemaFieldTypes.NUMERIC @@ -245,9 +245,7 @@ describe('SEARCH', () => { ]); assert.deepEqual( - await client.ft.search('index', '*', { - DIALECT: 1 - }), + await client.ft.search('index', '*'), { total: 1, documents: [{ @@ -264,44 +262,23 @@ describe('SEARCH', () => { ); }, GLOBAL.SERVERS.OPEN); - testUtils.testWithClient('DIALECT 2', async client => { + testUtils.testWithClient('RETURN []', async client => { await Promise.all([ client.ft.create('index', { field: SchemaFieldTypes.NUMERIC }), - client.hSet('1', 'field', '1'), - client.hSet('2', 'field', '2'), - client.hSet('3', 'field', '3') + client.hSet('1', 'field', '1') ]); assert.deepEqual( - await client.ft.search('index', '@field:[$min $max]', { - PARAMS: { - min: 1, - max: 2 - }, - DIALECT: 2 + await client.ft.search('index', '*', { + RETURN: [] }), { - total: 2, + total: 1, documents: [{ id: '1', - value: Object.create(null, { - field: { - value: '1', - configurable: true, - enumerable: true - } - }) - }, { - id: '2', - value: Object.create(null, { - field: { - value: '2', - configurable: true, - enumerable: true - } - }) + value: Object.create(null) }] } ); diff --git a/packages/search/lib/commands/SEARCH.ts b/packages/search/lib/commands/SEARCH.ts index 4590997b24a..bed06e22c36 100644 --- a/packages/search/lib/commands/SEARCH.ts +++ b/packages/search/lib/commands/SEARCH.ts @@ -1,5 +1,4 @@ import { RedisCommandArguments } from '@redis/client/dist/lib/commands'; -import { transformTuplesReply } from '@redis/client/dist/lib/commands/generic-transformers'; import { pushSearchOptions, RedisSearchLanguages, Params, PropertyName, SortByProperty, SearchReply } from '.'; export const FIRST_KEY_INDEX = 1; @@ -73,13 +72,11 @@ export type SearchRawReply = Array; export function transformReply(reply: SearchRawReply): SearchReply { const documents = []; - for (let i = 1; i < reply.length; i += 2) { - const tuples = reply[i + 1]; + let i = 1; + while (i < reply.length) { documents.push({ - id: reply[i], - value: tuples.length === 2 && tuples[0] === '$' ? - JSON.parse(tuples[1]) : - transformTuplesReply(tuples) + id: reply[i++], + value: documentValue(reply[i++]) }); } @@ -88,3 +85,26 @@ export function transformReply(reply: SearchRawReply): SearchReply { documents }; } + +function documentValue(tuples: any) { + const message = Object.create(null); + if (tuples === undefined) return message; + + let i = 0; + while (i < tuples.length) { + const key = tuples[i++], + value = tuples[i++]; + if (key === '$') { // might be a JSON reply + try { + Object.assign(message, JSON.parse(value)); + continue; + } catch { + // set as a regular property if not a valid JSON + } + } + + message[key] = value; + } + + return message; +} From 2042a67f3c063626ffb6725adf3b0557114d5cbc Mon Sep 17 00:00:00 2001 From: Curran Kelleher <68416+curran@users.noreply.github.com> Date: Wed, 18 Jan 2023 12:54:51 -0500 Subject: [PATCH 1302/1748] KNN example (#2352) * Sketch KNN example * Got KNN example to work. Closes #2351 * clean example Co-authored-by: Leibale Eidelman --- examples/search-knn.js | 91 ++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 91 insertions(+) create mode 100644 examples/search-knn.js diff --git a/examples/search-knn.js b/examples/search-knn.js new file mode 100644 index 00000000000..ea20f52e3fe --- /dev/null +++ b/examples/search-knn.js @@ -0,0 +1,91 @@ +// This example demonstrates how to use RediSearch to index and query data +// stored in Redis hashes using vector similarity search. +// +// Inspired by RediSearch Python tests: +// https://github.com/RediSearch/RediSearch/blob/06e36d48946ea08bd0d8b76394a4e82eeb919d78/tests/pytests/test_vecsim.py#L96 + +import { createClient, SchemaFieldTypes, VectorAlgorithms } from 'redis'; + +const client = createClient(); + +await client.connect(); + +// Create an index... +try { + // Documentation: https://redis.io/docs/stack/search/reference/vectors/ + await client.ft.create('idx:knn-example', { + v: { + type: SchemaFieldTypes.VECTOR, + ALGORITHM: VectorAlgorithms.HNSW, + TYPE: 'FLOAT32', + DIM: 2, + DISTANCE_METRIC: 'COSINE' + } + }, { + ON: 'HASH', + PREFIX: 'noderedis:knn' + }); +} 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); + } +} + +function float32Buffer(arr) { + return Buffer.from(new Float32Array(arr).buffer); +} + +// Add some sample data... +// https://redis.io/commands/hset/ +await Promise.all([ + client.hSet('noderedis:knn:a', { v: float32Buffer([0.1, 0.1]) }), + client.hSet('noderedis:knn:b', { v: float32Buffer([0.1, 0.2]) }), + client.hSet('noderedis:knn:c', { v: float32Buffer([0.1, 0.3]) }), + client.hSet('noderedis:knn:d', { v: float32Buffer([0.1, 0.4]) }), +]); +// Perform a K-Nearest Neighbors vector similarity search +// Documentation: https://redis.io/docs/stack/search/reference/vectors/#pure-knn-queries +const results = await client.ft.search('idx:knn-example', '*=>[KNN 4 @v $BLOB AS dist]', { + PARAMS: { + BLOB: float32Buffer([0.1, 0.1]) + }, + SORTBY: 'dist', + DIALECT: 2, + RETURN: ['dist'] +}); +console.log(JSON.stringify(results, null, 2)); +// results: +// { +// "total": 4, +// "documents": [ +// { +// "id": "noderedis:knn:a", +// "value": { +// "dist": "5.96046447754e-08" +// } +// }, +// { +// "id": "noderedis:knn:b", +// "value": { +// "dist": "0.0513167381287" +// } +// }, +// { +// "id": "noderedis:knn:c", +// "value": { +// "dist": "0.10557281971" +// } +// }, +// { +// "id": "noderedis:knn:d", +// "value": { +// "dist": "0.142507016659" +// } +// } +// ] +// } +await client.quit(); From fad23973a5384006587bd9bc46711ff6e5bdd5ab Mon Sep 17 00:00:00 2001 From: Leibale Eidelman Date: Wed, 18 Jan 2023 12:55:11 -0500 Subject: [PATCH 1303/1748] fix #2333 - fix quit reply (#2346) --- packages/client/lib/client/index.spec.ts | 5 +++-- packages/client/lib/client/index.ts | 9 +++++---- packages/client/lib/client/socket.ts | 5 +++-- 3 files changed, 11 insertions(+), 8 deletions(-) diff --git a/packages/client/lib/client/index.spec.ts b/packages/client/lib/client/index.spec.ts index 5ad3ace3fe4..63200bf82d6 100644 --- a/packages/client/lib/client/index.spec.ts +++ b/packages/client/lib/client/index.spec.ts @@ -841,13 +841,14 @@ describe('Client', () => { quitPromise = client.quit(); assert.equal(client.isOpen, false); - const [ping] = await Promise.all([ + const [ping, quit] = await Promise.all([ pingPromise, - assert.doesNotReject(quitPromise), + quitPromise, assert.rejects(client.ping(), ClientClosedError) ]); assert.equal(ping, 'PONG'); + assert.equal(quit, 'OK'); }, { ...GLOBAL.SERVERS.OPEN, disableClientSetup: true diff --git a/packages/client/lib/client/index.ts b/packages/client/lib/client/index.ts index 02baa9c8d03..8c5a23db448 100644 --- a/packages/client/lib/client/index.ts +++ b/packages/client/lib/client/index.ts @@ -586,16 +586,17 @@ export default class RedisClient< pUnsubscribe = this.PUNSUBSCRIBE; - QUIT(): Promise { - return this.#socket.quit(() => { - const quitPromise = this.#queue.addCommand(['QUIT'], { + QUIT(): Promise { + return this.#socket.quit(async () => { + const quitPromise = this.#queue.addCommand(['QUIT'], { ignorePubSubMode: true }); this.#tick(); - return Promise.all([ + const [reply] = await Promise.all([ quitPromise, this.#destroyIsolationPool() ]); + return reply; }); } diff --git a/packages/client/lib/client/socket.ts b/packages/client/lib/client/socket.ts index cc9d04c7b2f..345ac1d3e38 100644 --- a/packages/client/lib/client/socket.ts +++ b/packages/client/lib/client/socket.ts @@ -240,14 +240,15 @@ export default class RedisSocket extends EventEmitter { this.emit('end'); } - async quit(fn: () => Promise): Promise { + async quit(fn: () => Promise): Promise { if (!this.#isOpen) { throw new ClientClosedError(); } this.#isOpen = false; - await fn(); + const reply = await fn(); this.#disconnect(); + return reply; } #isCorked = false; From 63511e5b9b8e51dad604bcfee5f09def301d6b4b Mon Sep 17 00:00:00 2001 From: Ananda Date: Wed, 18 Jan 2023 18:55:38 +0100 Subject: [PATCH 1304/1748] Add latency graph command (#2359) * add latency graph command * fix coding style * Clean code * use "enable-debug-command" is redis 7+ only * Update LATENCY_GRAPH.spec.ts Co-authored-by: Leibale Eidelman --- packages/client/lib/client/commands.ts | 5 ++- .../client/lib/commands/LATENCY_GRAPH.spec.ts | 32 +++++++++++++++++++ packages/client/lib/commands/LATENCY_GRAPH.ts | 25 +++++++++++++++ 3 files changed, 61 insertions(+), 1 deletion(-) create mode 100644 packages/client/lib/commands/LATENCY_GRAPH.spec.ts create mode 100644 packages/client/lib/commands/LATENCY_GRAPH.ts diff --git a/packages/client/lib/client/commands.ts b/packages/client/lib/client/commands.ts index e164639ae91..f4eb1f1e172 100644 --- a/packages/client/lib/client/commands.ts +++ b/packages/client/lib/client/commands.ts @@ -81,6 +81,8 @@ import * as HELLO from '../commands/HELLO'; import * as INFO from '../commands/INFO'; import * as KEYS from '../commands/KEYS'; import * as LASTSAVE from '../commands/LASTSAVE'; +import * as LATENCY_DOCTOR from '../commands/LATENCY_DOCTOR'; +import * as LATENCY_GRAPH from '../commands/LATENCY_GRAPH'; import * as LOLWUT from '../commands/LOLWUT'; import * as MEMORY_DOCTOR from '../commands/MEMORY_DOCTOR'; import * as MEMORY_MALLOC_STATS from '../commands/MEMORY_MALLOC-STATS'; @@ -113,7 +115,6 @@ import * as SWAPDB from '../commands/SWAPDB'; import * as TIME from '../commands/TIME'; import * as UNWATCH from '../commands/UNWATCH'; import * as WAIT from '../commands/WAIT'; -import * as LATENCY_DOCTOR from '../commands/LATENCY_DOCTOR'; export default { ...CLUSTER_COMMANDS, @@ -283,6 +284,8 @@ export default { lastSave: LASTSAVE, LATENCY_DOCTOR, latencyDoctor: LATENCY_DOCTOR, + LATENCY_GRAPH, + latencyGraph: LATENCY_GRAPH, LOLWUT, lolwut: LOLWUT, MEMORY_DOCTOR, diff --git a/packages/client/lib/commands/LATENCY_GRAPH.spec.ts b/packages/client/lib/commands/LATENCY_GRAPH.spec.ts new file mode 100644 index 00000000000..df4d5d466ab --- /dev/null +++ b/packages/client/lib/commands/LATENCY_GRAPH.spec.ts @@ -0,0 +1,32 @@ +import { strict as assert } from 'assert'; +import testUtils, { GLOBAL } from '../test-utils'; +import { transformArguments } from './LATENCY_GRAPH'; + +describe('LATENCY GRAPH', () => { + it('transformArguments', () => { + assert.deepEqual( + transformArguments('command'), + [ + 'LATENCY', + 'GRAPH', + 'command' + ] + ); + }); + + testUtils.testWithClient('client.latencyGraph', async client => { + await Promise.all([ + client.configSet('latency-monitor-threshold', '1'), + client.sendCommand(['DEBUG', 'SLEEP', '0.001']) + ]); + + assert.equal( + typeof await client.latencyGraph('command'), + 'string' + ); + }, { + serverArguments: testUtils.isVersionGreaterThan([7]) ? + ['--enable-debug-command', 'yes'] : + GLOBAL.SERVERS.OPEN.serverArguments + }); +}); diff --git a/packages/client/lib/commands/LATENCY_GRAPH.ts b/packages/client/lib/commands/LATENCY_GRAPH.ts new file mode 100644 index 00000000000..e4e078b90f2 --- /dev/null +++ b/packages/client/lib/commands/LATENCY_GRAPH.ts @@ -0,0 +1,25 @@ +import { RedisCommandArguments } from '.'; + +export type EventType = + 'active-defrag-cycle' + | 'aof-fsync-always' + | 'aof-stat' + | 'aof-rewrite-diff-write' + | 'aof-rename' + | 'aof-write' + | 'aof-write-active-child' + | 'aof-write-alone' + | 'aof-write-pending-fsync' + | 'command' + | 'expire-cycle' + | 'eviction-cycle' + | 'eviction-del' + | 'fast-command' + | 'fork' + | 'rdb-unlink-temp-file'; + +export function transformArguments(event: EventType): RedisCommandArguments { + return ['LATENCY', 'GRAPH', event]; +} + +export declare function transformReply(): string; From fa47b572d4ea64c4a1f97814fef5d88737dee6e2 Mon Sep 17 00:00:00 2001 From: Simon Prickett Date: Wed, 18 Jan 2023 17:59:04 +0000 Subject: [PATCH 1305/1748] Adds example for mixing arbitrary and supported commands in a transaction context. (#2315) * Adds example for transactions with arbitrary commands. * Formatting. * Update transaction-with-arbitrary-commands.js Co-authored-by: Leibale Eidelman --- examples/README.md | 2 +- .../transaction-with-arbitrary-commands.js | 40 +++++++++++++++++++ 2 files changed, 41 insertions(+), 1 deletion(-) create mode 100644 examples/transaction-with-arbitrary-commands.js diff --git a/examples/README.md b/examples/README.md index ce3489fb598..ef6afe9dc1a 100644 --- a/examples/README.md +++ b/examples/README.md @@ -27,7 +27,7 @@ This folder contains example scripts showing how to use Node Redis in different | `time-series.js` | Create, populate and query timeseries data with [Redis Timeseries](https://redistimeseries.io). | | `topk.js` | Use the [RedisBloom](https://redisbloom.io) TopK to track the most frequently seen items. | | `stream-consumer-group.js` | Reads entries from a [Redis Stream](https://redis.io/topics/streams-intro) as part of a consumer group using the blocking `XREADGROUP` command. | -| `tranaaction-with-arbitrary-commands.js` | Mix and match supported commands with arbitrary command strings in a Redis transaction. | +| `transaction-with-arbitrary-commands.js` | Mix and match supported commands with arbitrary command strings in a Redis transaction. | | `transaction-with-watch.js` | An Example of [Redis transaction](https://redis.io/docs/manual/transactions) with `WATCH` command on isolated connection with optimistic locking. | ## Contributing diff --git a/examples/transaction-with-arbitrary-commands.js b/examples/transaction-with-arbitrary-commands.js new file mode 100644 index 00000000000..274a362d57e --- /dev/null +++ b/examples/transaction-with-arbitrary-commands.js @@ -0,0 +1,40 @@ +// How to mix and match supported commands that have named functions with +// commands sent as arbitrary strings in the same transaction context. +// Use this when working with new Redis commands that haven't been added to +// node-redis yet, or when working with commands that have been added to Redis +// by modules other than those directly supported by node-redis. + +import { createClient } from 'redis'; + +const client = createClient(); + +await client.connect(); + +// Build some data fixtures. +await Promise.all([ + client.hSet('hash1', { name: 'hash1', number: 1}), + client.hSet('hash2', { name: 'hash2', number: 1}), + client.hSet('hash3', { name: 'hash3', number: 3}) +]); + +// Outside of a transaction, use sendCommand to send arbitrary commands. +await client.sendCommand(['hset', 'hash2', 'number', '3']); + +// In a transaction context, use addCommand to send arbitrary commands. +// addCommand can be mixed and matched with named command functions as +// shown. +const responses = await client + .multi() + .hGetAll('hash2') + .addCommand(['hset', 'hash3', 'number', '4']) + .hGet('hash3', 'number') + .exec(); + +// responses will be: +// [ [Object: null prototype] { name: 'hash2', number: '3' }, 0, '4' ] +console.log(responses); + +// Clean up fixtures. +await client.del(['hash1', 'hash2', 'hash3']); + +await client.quit(); From abf2b4bc8216c972e40fd369238c84b06e06ce16 Mon Sep 17 00:00:00 2001 From: Leibale Eidelman Date: Wed, 18 Jan 2023 14:36:57 -0500 Subject: [PATCH 1306/1748] update examples/README.md - add search-knn to the table --- examples/README.md | 1 + 1 file changed, 1 insertion(+) diff --git a/examples/README.md b/examples/README.md index ef6afe9dc1a..19e9df31f90 100644 --- a/examples/README.md +++ b/examples/README.md @@ -20,6 +20,7 @@ This folder contains example scripts showing how to use Node Redis in different | `pubsub-subscriber.js` | Reads messages from channels using `PSUBSCRIBE` command. | | `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. | +| `search-knn.js` | Uses [RediSearch vector similarity]([https://redisearch.io/](https://redis.io/docs/stack/search/reference/vectors/)) to index and run KNN queries. | | `set-scan.js` | An example script that shows how to use the SSCAN iterator functionality. | | `sorted-set.js` | Add members with scores to a Sorted Set and retrieve them using the ZSCAN iteractor functionality. | | `stream-producer.js` | Adds entries to a [Redis Stream](https://redis.io/topics/streams-intro) using the `XADD` command. | From 6642278f96503a393371740373bbb9f9df9823e7 Mon Sep 17 00:00:00 2001 From: Samuel CHEMLA <43315561+phpbg@users.noreply.github.com> Date: Tue, 24 Jan 2023 15:15:14 +0100 Subject: [PATCH 1307/1748] Add 'Network error handling' section to documentation (#2250) * Add 'Network error handling' section to documentation * Merge 'Network error handling' section with existing doc * typo * Update README.md * typos Co-authored-by: Samuel CHEMLA Co-authored-by: Leibale Eidelman --- README.md | 2 +- docs/client-configuration.md | 18 +++++++++++++++++- 2 files changed, 18 insertions(+), 2 deletions(-) diff --git a/README.md b/README.md index 2a894f50c5e..b06b7f3cfdb 100644 --- a/README.md +++ b/README.md @@ -53,7 +53,7 @@ import { createClient } from 'redis'; const client = createClient(); -client.on('error', (err) => console.log('Redis Client Error', err)); +client.on('error', err => console.log('Redis Client Error', err)); await client.connect(); diff --git a/docs/client-configuration.md b/docs/client-configuration.md index a67cef462ac..d57d0c5dd3a 100644 --- a/docs/client-configuration.md +++ b/docs/client-configuration.md @@ -29,13 +29,29 @@ ## Reconnect Strategy -You can implement a custom reconnect strategy as a function: +When a network error occurs the client will automatically try to reconnect, following a default linear strategy (the more attempts, the more waiting before trying to reconnect). + +This strategy can be overridden by providing a `socket.reconnectStrategy` option during the client's creation. + +The `socket.reconnectStrategy` is a function that: - Receives the number of retries attempted so far. - Returns `number | Error`: - `number`: wait time in milliseconds prior to attempting a reconnect. - `Error`: closes the client and flushes internal command queues. +The example below shows the default `reconnectStrategy` and how to override it. + +```typescript +import { createClient } from 'redis'; + +const client = createClient({ + socket: { + reconnectStrategy: (retries) => Math.min(retries * 50, 500) + } +}); +``` + ## TLS To enable TLS, set `socket.tls` to `true`. Below are some basic examples. From d77be811bed11a4cf4cecafc23a0740dd8d4d906 Mon Sep 17 00:00:00 2001 From: Leibale Eidelman Date: Tue, 24 Jan 2023 13:02:08 -0500 Subject: [PATCH 1308/1748] add keywords in root package.json (#2389) --- package.json | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) diff --git a/package.json b/package.json index a6f01ce76fb..a5a5cbe99fc 100644 --- a/package.json +++ b/package.json @@ -43,5 +43,8 @@ "bugs": { "url": "https://github.com/redis/node-redis/issues" }, - "homepage": "https://github.com/redis/node-redis" + "homepage": "https://github.com/redis/node-redis", + "keywords": [ + "redis" + ] } From 29a2dc5027685ba0b3512fa9a6038977aba2ae8b Mon Sep 17 00:00:00 2001 From: Agung Sundoro Date: Wed, 25 Jan 2023 05:31:21 +0700 Subject: [PATCH 1309/1748] update issue templates and add new documentation issue template (#2357) * feat: update issue templates and add new documentation issue template * update templates * refine templates Co-authored-by: Leibale --- .github/ISSUE_TEMPLATE/BUG-REPORT.yml | 39 ++++++++++++++++++++++ .github/ISSUE_TEMPLATE/DOCUMENTATION.yml | 11 ++++++ .github/ISSUE_TEMPLATE/FEATURE-REQUEST.yml | 19 +++++++++++ .github/ISSUE_TEMPLATE/bug-report.md | 15 --------- .github/ISSUE_TEMPLATE/feature-request.md | 7 ---- 5 files changed, 69 insertions(+), 22 deletions(-) create mode 100644 .github/ISSUE_TEMPLATE/BUG-REPORT.yml create mode 100644 .github/ISSUE_TEMPLATE/DOCUMENTATION.yml create mode 100644 .github/ISSUE_TEMPLATE/FEATURE-REQUEST.yml delete mode 100644 .github/ISSUE_TEMPLATE/bug-report.md delete mode 100644 .github/ISSUE_TEMPLATE/feature-request.md diff --git a/.github/ISSUE_TEMPLATE/BUG-REPORT.yml b/.github/ISSUE_TEMPLATE/BUG-REPORT.yml new file mode 100644 index 00000000000..2e8db33a0c6 --- /dev/null +++ b/.github/ISSUE_TEMPLATE/BUG-REPORT.yml @@ -0,0 +1,39 @@ +name: Bug Report +description: Tell us about something that isn't working as expected +labels: [Bug] +body: + - type: textarea + id: description + attributes: + label: Description + description: Please enter a detailed description of your issue. If possible, please provide example code to reproduce the issue. + validations: + required: true + - type: input + id: node-js-version + attributes: + label: Node.js Version + description: Please enter your Node.js version `node --version` + - type: input + id: redis-server-version + attributes: + label: Redis Server Version + description: Please enter your Redis server version ([`INFO server`](https://redis.io/commands/info/)) + - type: input + id: node-redis-version + attributes: + label: Node Redis Version + description: Please enter your node redis version `npm ls redis` + - type: input + id: platform + attributes: + label: Platform + description: Please enter the platform you are using e.g. Linux, macOS, Windows + - type: textarea + id: logs + attributes: + label: Logs + description: Please copy and paste any relevant log output. This will be automatically formatted into code, so no need for backticks. + render: bash + validations: + required: false diff --git a/.github/ISSUE_TEMPLATE/DOCUMENTATION.yml b/.github/ISSUE_TEMPLATE/DOCUMENTATION.yml new file mode 100644 index 00000000000..b5ece5aeca2 --- /dev/null +++ b/.github/ISSUE_TEMPLATE/DOCUMENTATION.yml @@ -0,0 +1,11 @@ +name: Documentation +description: Any questions or issues relating to the project documentation. +labels: [Documentation] +body: + - type: textarea + id: description + attributes: + label: Description + description: Ask your question or describe your issue here. + validations: + required: true diff --git a/.github/ISSUE_TEMPLATE/FEATURE-REQUEST.yml b/.github/ISSUE_TEMPLATE/FEATURE-REQUEST.yml new file mode 100644 index 00000000000..ae10cbd7b7a --- /dev/null +++ b/.github/ISSUE_TEMPLATE/FEATURE-REQUEST.yml @@ -0,0 +1,19 @@ +name: Feature Request +description: Suggest an idea for this project +labels: [Feature] +body: + - type: textarea + id: motivation + attributes: + label: Motivation + description: How would Node Redis users benefit from this feature? + validations: + required: true + - type: textarea + id: basic-code-example + attributes: + label: Basic Code Example + description: Provide examples of how you imagine the API for this feature might be implemented. This will be automatically formatted into code, so no need for backticks. + render: JavaScript + validations: + required: false diff --git a/.github/ISSUE_TEMPLATE/bug-report.md b/.github/ISSUE_TEMPLATE/bug-report.md deleted file mode 100644 index a7fae8eeb11..00000000000 --- a/.github/ISSUE_TEMPLATE/bug-report.md +++ /dev/null @@ -1,15 +0,0 @@ ---- -name: Bug report -about: Create a report to help us improve -title: '' -labels: Bug -assignees: '' ---- - - - -**Environment:** - - **Node.js Version**: - - **Redis Server Version**: - - **Node Redis Version**: - - **Platform**: diff --git a/.github/ISSUE_TEMPLATE/feature-request.md b/.github/ISSUE_TEMPLATE/feature-request.md deleted file mode 100644 index 0645d6e1a83..00000000000 --- a/.github/ISSUE_TEMPLATE/feature-request.md +++ /dev/null @@ -1,7 +0,0 @@ ---- -name: Feature request -about: Suggest an idea for this project -title: '' -labels: Bug -assignees: '' ---- From 85bf97bb390a53d3f4aec846956479775243c263 Mon Sep 17 00:00:00 2001 From: "bodong.ybd" Date: Wed, 25 Jan 2023 06:52:15 +0800 Subject: [PATCH 1310/1748] doc: add defaults option example to cluster documentation (#2377) * doc: add defaults option example to cluster documentation * Formatting update. * Formatting. Co-authored-by: Simon Prickett --- docs/clustering.md | 16 ++++++++++++++++ 1 file changed, 16 insertions(+) diff --git a/docs/clustering.md b/docs/clustering.md index 26acfa0a791..48fc98640c5 100644 --- a/docs/clustering.md +++ b/docs/clustering.md @@ -40,6 +40,22 @@ const value = await cluster.get('key'); | modules | | Included [Redis Modules](../README.md#packages) | | scripts | | Script definitions (see [Lua Scripts](../README.md#lua-scripts)) | | functions | | Function definitions (see [Functions](../README.md#functions)) | +## Auth with password and username + +Specifying the password in the URL or a root node will only affect the connection to that specific node. In case you want to set the password for all the connections being created from a cluster instance, use the `defaults` option. +```javascript +createCluster({ + rootNodes: [{ + url: 'redis://10.0.0.1:30001' + }, { + url: 'redis://10.0.0.2:30002' + }], + defaults: { + username: 'username', + password: 'password' + } +}); +``` ## Node Address Map From 2287efdd1ed35cf54b92e4066c33335c5a9cdd9d Mon Sep 17 00:00:00 2001 From: Benjie Date: Wed, 25 Jan 2023 01:11:48 +0000 Subject: [PATCH 1311/1748] Fix legacyMode/pingInterval issue (#2386) * Add a test for legacyMode pingInterval * Apply patch to fix legacy mode ping interval * use this.#sendCommand instead of this.#v4 Co-authored-by: Leibale --- packages/client/lib/client/index.spec.ts | 13 +++++++++++++ packages/client/lib/client/index.ts | 3 ++- 2 files changed, 15 insertions(+), 1 deletion(-) diff --git a/packages/client/lib/client/index.spec.ts b/packages/client/lib/client/index.spec.ts index 63200bf82d6..a6cd7fd4b80 100644 --- a/packages/client/lib/client/index.spec.ts +++ b/packages/client/lib/client/index.spec.ts @@ -341,6 +341,19 @@ describe('Client', () => { legacyMode: true } }); + + testUtils.testWithClient('pingInterval', async client => { + assert.deepEqual( + await once(client, 'ping-interval'), + ['PONG'] + ); + }, { + ...GLOBAL.SERVERS.OPEN, + clientOptions: { + legacyMode: true, + pingInterval: 1 + } + }); }); describe('events', () => { diff --git a/packages/client/lib/client/index.ts b/packages/client/lib/client/index.ts index 8c5a23db448..f1e83edb417 100644 --- a/packages/client/lib/client/index.ts +++ b/packages/client/lib/client/index.ts @@ -362,7 +362,8 @@ export default class RedisClient< this.#pingTimer = setTimeout(() => { if (!this.#socket.isReady) return; - (this as unknown as RedisClientType).ping() + // using #sendCommand to support legacy mode + this.#sendCommand(['PING']) .then(reply => this.emit('ping-interval', reply)) .catch(err => this.emit('error', err)) .finally(() => this.#setPingTimer()); From e75a5db3e43d3ed309d943992eb2bb3aed497eda Mon Sep 17 00:00:00 2001 From: Ananda Date: Wed, 25 Jan 2023 16:52:59 +0100 Subject: [PATCH 1312/1748] Add CLIENT LIST command and fix CLIENT INFO (#2368) * fix client info * add client list * fix key validation in transformClientInfoReply * fix issue with field in CLIENT LIST reply * clean code * fix multimem * fix qbufFree argvMem totMem multiMem Co-authored-by: Leibale --- packages/client/lib/client/commands.ts | 3 + .../client/lib/commands/CLIENT_INFO.spec.ts | 68 +++++----- packages/client/lib/commands/CLIENT_INFO.ts | 118 +++++++++--------- .../client/lib/commands/CLIENT_LIST.spec.ts | 78 ++++++++++++ packages/client/lib/commands/CLIENT_LIST.ts | 43 +++++++ 5 files changed, 223 insertions(+), 87 deletions(-) create mode 100644 packages/client/lib/commands/CLIENT_LIST.spec.ts create mode 100644 packages/client/lib/commands/CLIENT_LIST.ts diff --git a/packages/client/lib/client/commands.ts b/packages/client/lib/client/commands.ts index f4eb1f1e172..8b5320a5d80 100644 --- a/packages/client/lib/client/commands.ts +++ b/packages/client/lib/client/commands.ts @@ -21,6 +21,7 @@ import * as CLIENT_GETNAME from '../commands/CLIENT_GETNAME'; import * as CLIENT_GETREDIR from '../commands/CLIENT_GETREDIR'; import * as CLIENT_ID from '../commands/CLIENT_ID'; import * as CLIENT_KILL from '../commands/CLIENT_KILL'; +import * as CLIENT_LIST from '../commands/CLIENT_LIST'; import * as CLIENT_NO_EVICT from '../commands/CLIENT_NO-EVICT'; import * as CLIENT_PAUSE from '../commands/CLIENT_PAUSE'; import * as CLIENT_SETNAME from '../commands/CLIENT_SETNAME'; @@ -164,6 +165,8 @@ export default { clientKill: CLIENT_KILL, 'CLIENT_NO-EVICT': CLIENT_NO_EVICT, clientNoEvict: CLIENT_NO_EVICT, + CLIENT_LIST, + clientList: CLIENT_LIST, CLIENT_PAUSE, clientPause: CLIENT_PAUSE, CLIENT_SETNAME, diff --git a/packages/client/lib/commands/CLIENT_INFO.spec.ts b/packages/client/lib/commands/CLIENT_INFO.spec.ts index ee87df4a199..ccb99017cf3 100644 --- a/packages/client/lib/commands/CLIENT_INFO.spec.ts +++ b/packages/client/lib/commands/CLIENT_INFO.spec.ts @@ -1,7 +1,10 @@ import { strict as assert } from 'assert'; import { transformArguments, transformReply } from './CLIENT_INFO'; +import testUtils, { GLOBAL } from '../test-utils'; describe('CLIENT INFO', () => { + testUtils.isVersionGreaterThanHook([6, 2]); + it('transformArguments', () => { assert.deepEqual( transformArguments(), @@ -9,34 +12,39 @@ describe('CLIENT INFO', () => { ); }); - it('transformReply', () => { - assert.deepEqual( - transformReply('id=526512 addr=127.0.0.1:36244 laddr=127.0.0.1:6379 fd=8 name= age=11213 idle=0 flags=N db=0 sub=0 psub=0 multi=-1 qbuf=26 qbuf-free=40928 argv-mem=10 obl=0 oll=0 omem=0 tot-mem=61466 events=r cmd=client user=default redir=-1\n'), - { - id: 526512, - addr: '127.0.0.1:36244', - laddr: '127.0.0.1:6379', - fd: 8, - name: '', - age: 11213, - idle: 0, - flags: 'N', - db: 0, - sub: 0, - psub: 0, - multi: -1, - qbuf: 26, - qbufFree: 40928, - argvMem: 10, - obl: 0, - oll: 0, - omem: 0, - totMem: 61466, - events: 'r', - cmd: 'client', - user: 'default', - redir: -1 - } - ); - }); + testUtils.testWithClient('client.clientInfo', async client => { + const reply = await client.clientInfo(); + assert.equal(typeof reply.id, 'number'); + assert.equal(typeof reply.addr, 'string'); + assert.equal(typeof reply.laddr, 'string'); + assert.equal(typeof reply.fd, 'number'); + assert.equal(typeof reply.name, 'string'); + assert.equal(typeof reply.age, 'number'); + assert.equal(typeof reply.idle, 'number'); + assert.equal(typeof reply.flags, 'string'); + assert.equal(typeof reply.db, 'number'); + assert.equal(typeof reply.sub, 'number'); + assert.equal(typeof reply.psub, 'number'); + assert.equal(typeof reply.multi, 'number'); + assert.equal(typeof reply.qbuf, 'number'); + assert.equal(typeof reply.qbufFree, 'number'); + assert.equal(typeof reply.argvMem, 'number'); + assert.equal(typeof reply.obl, 'number'); + assert.equal(typeof reply.oll, 'number'); + assert.equal(typeof reply.omem, 'number'); + assert.equal(typeof reply.totMem, 'number'); + assert.equal(typeof reply.events, 'string'); + assert.equal(typeof reply.cmd, 'string'); + assert.equal(typeof reply.user, 'string'); + assert.equal(typeof reply.redir, 'number'); + + if (testUtils.isVersionGreaterThan([7, 0])) { + assert.equal(typeof reply.multiMem, 'number'); + assert.equal(typeof reply.resp, 'number'); + } + + if (testUtils.isVersionGreaterThan([7, 0, 3])) { + assert.equal(typeof reply.ssub, 'number'); + } + }, GLOBAL.SERVERS.OPEN); }); diff --git a/packages/client/lib/commands/CLIENT_INFO.ts b/packages/client/lib/commands/CLIENT_INFO.ts index 8dd30b70590..7f6b6e1884e 100644 --- a/packages/client/lib/commands/CLIENT_INFO.ts +++ b/packages/client/lib/commands/CLIENT_INFO.ts @@ -1,11 +1,13 @@ +export const IS_READ_ONLY = true; + export function transformArguments(): Array { return ['CLIENT', 'INFO']; } -interface ClientInfoReply { +export interface ClientInfoReply { id: number; addr: string; - laddr: string; + laddr?: string; // 6.2 fd: number; name: string; age: number; @@ -14,72 +16,74 @@ interface ClientInfoReply { db: number; sub: number; psub: number; + ssub?: number; // 7.0.3 multi: number; qbuf: number; qbufFree: number; - argvMem: number; + argvMem?: number; // 6.0 + multiMem?: number; // 7.0 obl: number; oll: number; omem: number; - totMem: number; + totMem?: number; // 6.0 events: string; cmd: string; - user: string; - redir: number; + user?: string; // 6.0 + redir?: number; // 6.2 + resp?: number; // 7.0 } -const REGEX = /=([^\s]*)/g; +const CLIENT_INFO_REGEX = /([^\s=]+)=([^\s]*)/g; -export function transformReply(reply: string): ClientInfoReply { - const [ - [, id], - [, addr], - [, laddr], - [, fd], - [, name], - [, age], - [, idle], - [, flags], - [, db], - [, sub], - [, psub], - [, multi], - [, qbuf], - [, qbufFree], - [, argvMem], - [, obl], - [, oll], - [, omem], - [, totMem], - [, events], - [, cmd], - [, user], - [, redir] - ] = [...reply.matchAll(REGEX)]; +export function transformReply(rawReply: string): ClientInfoReply { + const map: Record = {}; + for (const item of rawReply.matchAll(CLIENT_INFO_REGEX)) { + map[item[1]] = item[2]; + } - return { - id: Number(id), - addr, - laddr, - fd: Number(fd), - name, - age: Number(age), - idle: Number(idle), - flags, - db: Number(db), - sub: Number(sub), - psub: Number(psub), - multi: Number(multi), - qbuf: Number(qbuf), - qbufFree: Number(qbufFree), - argvMem: Number(argvMem), - obl: Number(obl), - oll: Number(oll), - omem: Number(omem), - totMem: Number(totMem), - events, - cmd, - user, - redir: Number(redir) + const reply: ClientInfoReply = { + id: Number(map.id), + addr: map.addr, + fd: Number(map.fd), + name: map.name, + age: Number(map.age), + idle: Number(map.idle), + flags: map.flags, + db: Number(map.db), + sub: Number(map.sub), + psub: Number(map.psub), + multi: Number(map.multi), + qbuf: Number(map.qbuf), + qbufFree: Number(map['qbuf-free']), + argvMem: Number(map['argv-mem']), + obl: Number(map.obl), + oll: Number(map.oll), + omem: Number(map.omem), + totMem: Number(map['tot-mem']), + events: map.events, + cmd: map.cmd, + user: map.user }; + + if (map.laddr !== undefined) { + reply.laddr = map.laddr; + } + + if (map.redir !== undefined) { + reply.redir = Number(map.redir); + } + + if (map.ssub !== undefined) { + reply.ssub = Number(map.ssub); + } + + if (map['multi-mem'] !== undefined) { + reply.multiMem = Number(map['multi-mem']); + } + + if (map.resp !== undefined) { + reply.resp = Number(map.resp); + } + + return reply; } diff --git a/packages/client/lib/commands/CLIENT_LIST.spec.ts b/packages/client/lib/commands/CLIENT_LIST.spec.ts new file mode 100644 index 00000000000..c9c720e12ef --- /dev/null +++ b/packages/client/lib/commands/CLIENT_LIST.spec.ts @@ -0,0 +1,78 @@ +import { strict as assert } from 'assert'; +import { transformArguments, transformReply } from './CLIENT_LIST'; +import testUtils, { GLOBAL } from '../test-utils'; + +describe('CLIENT LIST', () => { + describe('transformArguments', () => { + it('simple', () => { + assert.deepEqual( + transformArguments(), + ['CLIENT', 'LIST'] + ); + }); + + it('with TYPE', () => { + assert.deepEqual( + transformArguments({ + TYPE: 'NORMAL' + }), + ['CLIENT', 'LIST', 'TYPE', 'NORMAL'] + ); + }); + + it('with ID', () => { + assert.deepEqual( + transformArguments({ + ID: ['1', '2'] + }), + ['CLIENT', 'LIST', 'ID', '1', '2'] + ); + }); + }); + + testUtils.testWithClient('client.clientList', async client => { + const reply = await client.clientList(); + assert.ok(Array.isArray(reply)); + + for (const item of reply) { + assert.equal(typeof item.id, 'number'); + assert.equal(typeof item.addr, 'string'); + assert.equal(typeof item.fd, 'number'); + assert.equal(typeof item.name, 'string'); + assert.equal(typeof item.age, 'number'); + assert.equal(typeof item.idle, 'number'); + assert.equal(typeof item.flags, 'string'); + assert.equal(typeof item.db, 'number'); + assert.equal(typeof item.sub, 'number'); + assert.equal(typeof item.psub, 'number'); + assert.equal(typeof item.multi, 'number'); + assert.equal(typeof item.qbuf, 'number'); + assert.equal(typeof item.qbufFree, 'number'); + assert.equal(typeof item.obl, 'number'); + assert.equal(typeof item.oll, 'number'); + assert.equal(typeof item.omem, 'number'); + assert.equal(typeof item.events, 'string'); + assert.equal(typeof item.cmd, 'string'); + + if (testUtils.isVersionGreaterThan([6, 0])) { + assert.equal(typeof item.argvMem, 'number'); + assert.equal(typeof item.totMem, 'number'); + assert.equal(typeof item.user, 'string'); + } + + if (testUtils.isVersionGreaterThan([6, 2])) { + assert.equal(typeof item.redir, 'number'); + assert.equal(typeof item.laddr, 'string'); + } + + if (testUtils.isVersionGreaterThan([7, 0])) { + assert.equal(typeof item.multiMem, 'number'); + assert.equal(typeof item.resp, 'number'); + } + + if (testUtils.isVersionGreaterThan([7, 0, 3])) { + assert.equal(typeof item.ssub, 'number'); + } + } + }, GLOBAL.SERVERS.OPEN); +}); diff --git a/packages/client/lib/commands/CLIENT_LIST.ts b/packages/client/lib/commands/CLIENT_LIST.ts new file mode 100644 index 00000000000..6f71dc7d999 --- /dev/null +++ b/packages/client/lib/commands/CLIENT_LIST.ts @@ -0,0 +1,43 @@ +import { RedisCommandArguments, RedisCommandArgument } from '.'; +import { pushVerdictArguments } from './generic-transformers'; +import { transformReply as transformClientInfoReply, ClientInfoReply } from './CLIENT_INFO'; + +interface ListFilterType { + TYPE: 'NORMAL' | 'MASTER' | 'REPLICA' | 'PUBSUB'; + ID?: never; +} + +interface ListFilterId { + ID: Array; + TYPE?: never; +} + +export type ListFilter = ListFilterType | ListFilterId; + +export const IS_READ_ONLY = true; + +export function transformArguments(filter?: ListFilter): RedisCommandArguments { + let args: RedisCommandArguments = ['CLIENT', 'LIST']; + + if (filter) { + if (filter.TYPE !== undefined) { + args.push('TYPE', filter.TYPE); + } else { + args.push('ID'); + args = pushVerdictArguments(args, filter.ID); + } + } + + return args; +} + +export function transformReply(rawReply: string): Array { + const split = rawReply.split('\n'), + length = split.length - 1, + reply: Array = []; + for (let i = 0; i < length; i++) { + reply.push(transformClientInfoReply(split[i])); + } + + return reply; +} From 3b1bad229674b421b2bc6424155b20d4d3e45bd1 Mon Sep 17 00:00:00 2001 From: Leibale Eidelman Date: Wed, 25 Jan 2023 11:00:39 -0500 Subject: [PATCH 1313/1748] Add support for sharded PubSub (#2373) MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit * refactor pubsub, add support for sharded pub sub * run tests in redis 7 only, fix PUBSUB SHARDCHANNELS test * add some comments and fix some bugs * PubSubType, not PubSubTypes 🤦‍♂️ * remove test.txt * fix some bugs, add tests * add some tests * fix #2345 - allow PING in PubSub mode (remove client side validation) * remove .only * revert changes in cluster/index.ts * fix tests minimum version * handle server sunsubscribe * add 'sharded-channel-moved' event to docs, improve the events section in the main README (fix #2302) * exit "resubscribe" if pubsub not active * Update commands-queue.ts * Release client@1.5.0-rc.0 * WIP * use `node:util` instead of `node:util/types` (to support node 14) * run PubSub resharding test with Redis 7+ * fix inconsistency in live resharding test * add some tests * fix iterateAllNodes when starting from a replica * fix iterateAllNodes random * fix slotNodesIterator * fix slotNodesIterator * clear pubSubNode when node in use * wait for all nodes cluster state to be ok before testing * `cluster.minimizeConections` tests * `client.reconnectStrategry = false | 0` tests * sharded pubsub + cluster 🎉 * add minimum version to sharded pubsub tests * add cluster sharded pubsub live reshard test, use stable dockers for tests, make sure to close pubsub clients when a node disconnects from the cluster * fix "ssubscribe & sunsubscribe" test * lock search docker to 2.4.9 * change numberOfMasters default to 2 * use edge for bloom * add tests * add back getMasters and getSlotMaster as deprecated functions * add some tests * fix reconnect strategy + docs * sharded pubsub docs * Update pub-sub.md * some jsdoc, docs, cluster topology test * clean pub-sub docs Co-authored-by: Simon Prickett * reconnect startegy docs and bug fix Co-authored-by: Simon Prickett * refine jsdoc and some docs Co-authored-by: Simon Prickett * I'm stupid * fix cluster topology test * fix cluster topology test * Update README.md * Update clustering.md * Update pub-sub.md Co-authored-by: Simon Prickett --- README.md | 61 +- docs/client-configuration.md | 36 +- docs/clustering.md | 37 +- docs/pub-sub.md | 86 +++ packages/client/lib/client/commands-queue.ts | 319 +++------ packages/client/lib/client/commands.ts | 3 + packages/client/lib/client/index.spec.ts | 155 +++- packages/client/lib/client/index.ts | 186 +++-- packages/client/lib/client/pub-sub.spec.ts | 151 ++++ packages/client/lib/client/pub-sub.ts | 408 +++++++++++ packages/client/lib/client/socket.spec.ts | 35 +- packages/client/lib/client/socket.ts | 72 +- packages/client/lib/cluster/cluster-slots.ts | 660 +++++++++++++----- packages/client/lib/cluster/commands.ts | 3 + packages/client/lib/cluster/index.spec.ts | 305 +++++++- packages/client/lib/cluster/index.ts | 195 +++++- .../lib/commands/CLUSTER_BUMPEPOCH.spec.ts | 3 +- .../CLUSTER_COUNT-FAILURE-REPORTS.spec.ts | 2 +- .../commands/CLUSTER_COUNTKEYSINSLOT.spec.ts | 3 +- .../commands/CLUSTER_GETKEYSINSLOT.spec.ts | 3 +- .../client/lib/commands/CLUSTER_INFO.spec.ts | 3 +- .../lib/commands/CLUSTER_KEYSLOT.spec.ts | 3 +- .../client/lib/commands/CLUSTER_LINKS.spec.ts | 3 +- .../client/lib/commands/CLUSTER_MYID.spec.ts | 6 +- .../lib/commands/CLUSTER_SAVECONFIG.spec.ts | 3 +- packages/client/lib/commands/CLUSTER_SLOTS.ts | 2 +- packages/client/lib/commands/PING.spec.ts | 20 +- packages/client/lib/commands/PING.ts | 11 +- packages/client/lib/commands/PUBLISH.ts | 2 + .../lib/commands/PUBSUB_SHARDCHANNELS.spec.ts | 30 + .../lib/commands/PUBSUB_SHARDCHANNELS.ts | 13 + packages/client/lib/commands/SPUBLISH.spec.ts | 21 + packages/client/lib/commands/SPUBLISH.ts | 14 + .../lib/commands/generic-transformers.ts | 1 - packages/client/lib/test-utils.ts | 9 +- packages/client/package.json | 2 +- packages/graph/lib/test-utils.ts | 3 +- packages/json/lib/test-utils.ts | 3 +- packages/test-utils/lib/dockers.ts | 164 +++-- packages/test-utils/lib/index.ts | 28 +- packages/time-series/lib/test-utils.ts | 3 +- 41 files changed, 2357 insertions(+), 710 deletions(-) create mode 100644 docs/pub-sub.md create mode 100644 packages/client/lib/client/pub-sub.spec.ts create mode 100644 packages/client/lib/client/pub-sub.ts create mode 100644 packages/client/lib/commands/PUBSUB_SHARDCHANNELS.spec.ts create mode 100644 packages/client/lib/commands/PUBSUB_SHARDCHANNELS.ts create mode 100644 packages/client/lib/commands/SPUBLISH.spec.ts create mode 100644 packages/client/lib/commands/SPUBLISH.ts diff --git a/README.md b/README.md index b06b7f3cfdb..4fca8d405d4 100644 --- a/README.md +++ b/README.md @@ -166,47 +166,7 @@ To learn more about isolated execution, check out the [guide](./docs/isolated-ex ### 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); // -}, true); - -await subscriber.pSubscribe('channe*', (message, channel) => { - console.log(message, channel); // , -}, true); -``` +See the [Pub/Sub overview](./docs/pub-sub.md). ### Scan Iterator @@ -373,15 +333,18 @@ Check out the [Clustering Guide](./docs/clustering.md) when using Node Redis to The Node Redis client class is an Nodejs EventEmitter and it emits an event each time the network status changes: -| Event name | Scenes | Arguments to be passed to the listener | -|----------------|-------------------------------------------------------------------------------------------------------------------|------------------------------------------------------------------------------------------------------------------------------------------------| -| `connect` | The client is initiating a connection to the server. | _No argument_ | -| `ready` | The client successfully initiated the connection to the server. | _No argument_ | -| `end` | The client disconnected the connection to the server via `.quit()` or `.disconnect()`. | _No argument_ | -| `error` | When a network error has occurred, such as unable to connect to the server or the connection closed unexpectedly. | 1 argument: The error object, such as `SocketClosedUnexpectedlyError: Socket closed unexpectedly` or `Error: connect ECONNREFUSED [IP]:[PORT]` | -| `reconnecting` | The client is trying to reconnect to the server. | _No argument_ | +| Name | When | Listener arguments | +|-------------------------|------------------------------------------------------------------------------------|------------------------------------------------------------| +| `connect` | Initiating a connection to the server | *No arguments* | +| `ready` | Client is ready to use | *No arguments* | +| `end` | Connection has been closed (via `.quit()` or `.disconnect()`) | *No arguments* | +| `error` | An error has occurred—usually a network issue such as "Socket closed unexpectedly" | `(error: Error)` | +| `reconnecting` | Client is trying to reconnect to the server | *No arguments* | +| `sharded-channel-moved` | See [here](./docs/pub-sub.md#sharded-channel-moved-event) | See [here](./docs/pub-sub.md#sharded-channel-moved-event) | + +> :warning: You **MUST** listen to `error` events. If a client doesn't have at least one `error` listener registered and an `error` occurs, that error will be thrown and the Node.js process will exit. See the [`EventEmitter` docs](https://nodejs.org/api/events.html#events_error_events) for more details. -The client will not emit [any other events](./docs/v3-to-v4.md#all-the-removed-events) beyond those listed above. +> The client will not emit [any other events](./docs/v3-to-v4.md#all-the-removed-events) beyond those listed above. ## Supported Redis versions diff --git a/docs/client-configuration.md b/docs/client-configuration.md index d57d0c5dd3a..1854f07158a 100644 --- a/docs/client-configuration.md +++ b/docs/client-configuration.md @@ -15,7 +15,7 @@ | socket.reconnectStrategy | `retries => Math.min(retries * 50, 500)` | A function containing the [Reconnect Strategy](#reconnect-strategy) logic | | username | | ACL username ([see ACL guide](https://redis.io/topics/acl)) | | password | | ACL password or the old "--requirepass" password | -| name | | Connection name ([see `CLIENT SETNAME`](https://redis.io/commands/client-setname)) | +| name | | Client name ([see `CLIENT SETNAME`](https://redis.io/commands/client-setname)) | | database | | Redis database number (see [`SELECT`](https://redis.io/commands/select) command) | | modules | | Included [Redis Modules](../README.md#packages) | | scripts | | Script definitions (see [Lua Scripts](../README.md#lua-scripts)) | @@ -25,30 +25,22 @@ | 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)) | | isolationPoolOptions | | See the [Isolated Execution Guide](./isolated-execution.md) | -| pingInterval | | Send `PING` command at interval (in ms). Useful with "[Azure Cache for Redis](https://learn.microsoft.com/en-us/azure/azure-cache-for-redis/cache-best-practices-connection#idle-timeout)" | +| pingInterval | | Send `PING` command at interval (in ms). Useful with ["Azure Cache for Redis"](https://learn.microsoft.com/en-us/azure/azure-cache-for-redis/cache-best-practices-connection#idle-timeout) | ## Reconnect Strategy -When a network error occurs the client will automatically try to reconnect, following a default linear strategy (the more attempts, the more waiting before trying to reconnect). +When the socket closes unexpectedly (without calling `.quit()`/`.disconnect()`), the client uses `reconnectStrategy` to decide what to do. The following values are supported: +1. `false` -> do not reconnect, close the client and flush the command queue. +2. `number` -> wait for `X` milliseconds before reconnecting. +3. `(retries: number, cause: Error) => false | number | Error` -> `number` is the same as configuring a `number` directly, `Error` is the same as `false`, but with a custom error. -This strategy can be overridden by providing a `socket.reconnectStrategy` option during the client's creation. +By default the strategy is `Math.min(retries * 50, 500)`, but it can be overwritten like so: -The `socket.reconnectStrategy` is a function that: - -- Receives the number of retries attempted so far. -- Returns `number | Error`: - - `number`: wait time in milliseconds prior to attempting a reconnect. - - `Error`: closes the client and flushes internal command queues. - -The example below shows the default `reconnectStrategy` and how to override it. - -```typescript -import { createClient } from 'redis'; - -const client = createClient({ - socket: { - reconnectStrategy: (retries) => Math.min(retries * 50, 500) - } +```javascript +createClient({ + socket: { + reconnectStrategy: retries => Math.min(retries * 50, 1000) + } }); ``` @@ -60,7 +52,7 @@ To enable TLS, set `socket.tls` to `true`. Below are some basic examples. ### Create a SSL client -```typescript +```javascript createClient({ socket: { tls: true, @@ -72,7 +64,7 @@ createClient({ ### Create a SSL client using a self-signed certificate -```typescript +```javascript createClient({ socket: { tls: true, diff --git a/docs/clustering.md b/docs/clustering.md index 48fc98640c5..28ea0e2964c 100644 --- a/docs/clustering.md +++ b/docs/clustering.md @@ -35,6 +35,7 @@ const value = await cluster.get('key'); | rootNodes | | An array of root nodes that are part of the cluster, which will be used to get the cluster topology. Each element in the array is a client configuration object. There is no need to specify every node in the cluster, 3 should be enough to reliably connect and obtain the cluster configuration from the server | | defaults | | The default configuration values for every client in the cluster. Use this for example when specifying an ACL user to connect with | | useReplicas | `false` | When `true`, distribute load by executing readonly commands (such as `GET`, `GEOSEARCH`, etc.) across all cluster nodes. When `false`, only use master nodes | +| minimizeConnections | `false` | When `true`, `.connect()` will only discover the cluster topology, without actually connecting to all the nodes. Useful for short-term or Pub/Sub-only connections. | | maxCommandRedirections | `16` | The maximum number of times a command will be redirected due to `MOVED` or `ASK` errors | | nodeAddressMap | | Defines the [node address mapping](#node-address-map) | | modules | | Included [Redis Modules](../README.md#packages) | @@ -59,27 +60,45 @@ createCluster({ ## Node Address Map -A node address map is required when a Redis cluster is configured with addresses that are inaccessible by the machine running the Redis client. -This is a mapping of addresses and ports, with the values being the accessible address/port combination. Example: +A mapping between the addresses in the cluster (see `CLUSTER SHARDS`) and the addresses the client should connect to. +Useful when the cluster is running on a different network to the client. ```javascript +const rootNodes = [{ + url: 'external-host-1.io:30001' +}, { + url: 'external-host-2.io:30002' +}]; + +// Use either a static mapping: createCluster({ - rootNodes: [{ - url: 'external-host-1.io:30001' - }, { - url: 'external-host-2.io:30002' - }], + rootNodes, nodeAddressMap: { '10.0.0.1:30001': { - host: 'external-host-1.io', + host: 'external-host.io', port: 30001 }, '10.0.0.2:30002': { - host: 'external-host-2.io', + host: 'external-host.io', port: 30002 } } }); + +// or create the mapping dynamically, as a function: +createCluster({ + rootNodes, + nodeAddressMap(address) { + const indexOfDash = address.lastIndexOf('-'), + indexOfDot = address.indexOf('.', indexOfDash), + indexOfColons = address.indexOf(':', indexOfDot); + + return { + host: `external-host-${address.substring(indexOfDash + 1, indexOfDot)}.io`, + port: Number(address.substring(indexOfColons + 1)) + }; + } +}); ``` > This is a common problem when using ElastiCache. See [Accessing ElastiCache from outside AWS](https://docs.aws.amazon.com/AmazonElastiCache/latest/red-ug/accessing-elasticache.html) for more information on that. diff --git a/docs/pub-sub.md b/docs/pub-sub.md new file mode 100644 index 00000000000..1428dca77ff --- /dev/null +++ b/docs/pub-sub.md @@ -0,0 +1,86 @@ +# Pub/Sub + +The Pub/Sub API is implemented by `RedisClient` and `RedisCluster`. + +## Pub/Sub with `RedisClient` + +Pub/Sub requires a dedicated stand-alone client. You can easily get one by `.duplicate()`ing an existing `RedisClient`: + +```typescript +const subscriber = client.duplicate(); +subscribe.on('error', err => console.error(err)); +await subscriber.connect(); +``` + +When working with a `RedisCluster`, this is handled automatically for you. + +### `sharded-channel-moved` event + +`RedisClient` emits the `sharded-channel-moved` event when the ["cluster slot"](https://redis.io/docs/reference/cluster-spec/#key-distribution-model) of a subscribed [Sharded Pub/Sub](https://redis.io/docs/manual/pubsub/#sharded-pubsub) channel has been moved to another shard. + +The event listener signature is as follows: +```typescript +( + channel: string, + listeners: { + buffers: Set; + strings: Set; + } +)`. +``` + +## Subscribing + +```javascript +const listener = (message, channel) => console.log(message, channel); +await client.subscribe('channel', listener); +await client.pSubscribe('channe*', listener); +// Use sSubscribe for sharded Pub/Sub: +await client.sSubscribe('channel', listener); +``` + +## Publishing + +```javascript +await client.publish('channel', 'message'); +// Use sPublish for sharded Pub/Sub: +await client.sPublish('channel', 'message'); +``` + +## Unsubscribing + +The code below unsubscribes all listeners from all channels. + +```javascript +await client.unsubscribe(); +await client.pUnsubscribe(); +// Use sUnsubscribe for sharded Pub/Sub: +await client.sUnsubscribe(); +``` + +To unsubscribe from specific channels: + +```javascript +await client.unsubscribe('channel'); +await client.unsubscribe(['1', '2']); +``` + +To unsubscribe a specific listener: + +```javascript +await client.unsubscribe('channel', listener); +``` + +## Buffers + +Publishing and subscribing using `Buffer`s is also supported: + +```javascript +await subscriber.subscribe('channel', message => { + console.log(message); // +}, true); // true = subscribe in `Buffer` mode. + +await subscriber.publish(Buffer.from('channel'), Buffer.from('message')); +``` + +> NOTE: Buffers and strings are supported both for the channel name and the message. You can mix and match these as desired. diff --git a/packages/client/lib/client/commands-queue.ts b/packages/client/lib/client/commands-queue.ts index f951cd6f845..7fffed86580 100644 --- a/packages/client/lib/client/commands-queue.ts +++ b/packages/client/lib/client/commands-queue.ts @@ -1,18 +1,18 @@ import * as LinkedList from 'yallist'; import { AbortError, ErrorReply } from '../errors'; -import { RedisCommandArgument, RedisCommandArguments, RedisCommandRawReply } from '../commands'; +import { RedisCommandArguments, RedisCommandRawReply } from '../commands'; import RESP2Decoder from './RESP2/decoder'; import encodeCommand from './RESP2/encoder'; +import { ChannelListeners, PubSub, PubSubCommand, PubSubListener, PubSubType, PubSubTypeListeners } from './pub-sub'; export interface QueueCommandOptions { asap?: boolean; chainId?: symbol; signal?: AbortSignal; returnBuffers?: boolean; - ignorePubSubMode?: boolean; } -interface CommandWaitingToBeSent extends CommandWaitingForReply { +export interface CommandWaitingToBeSent extends CommandWaitingForReply { args: RedisCommandArguments; chainId?: symbol; abort?: { @@ -28,27 +28,9 @@ interface CommandWaitingForReply { returnBuffers?: boolean; } -export enum PubSubSubscribeCommands { - SUBSCRIBE = 'SUBSCRIBE', - PSUBSCRIBE = 'PSUBSCRIBE' -} - -export enum PubSubUnsubscribeCommands { - UNSUBSCRIBE = 'UNSUBSCRIBE', - PUNSUBSCRIBE = 'PUNSUBSCRIBE' -} - -export type PubSubListener< - RETURN_BUFFERS extends boolean = false, - T = RETURN_BUFFERS extends true ? Buffer : string -> = (message: T, channel: T) => unknown; +const PONG = Buffer.from('pong'); -interface PubSubListeners { - buffers: Set>; - strings: Set>; -} - -type PubSubListenersMap = Map; +export type OnShardedChannelMoved = (channel: string, listeners: ChannelListeners) => void; export default class RedisCommandsQueue { static #flushQueue(queue: LinkedList, err: Error): void { @@ -57,67 +39,54 @@ export default class RedisCommandsQueue { } } - static #emitPubSubMessage(listenersMap: PubSubListenersMap, message: Buffer, channel: Buffer, pattern?: Buffer): void { - const keyString = (pattern ?? channel).toString(), - listeners = listenersMap.get(keyString); - - if (!listeners) return; - - for (const listener of listeners.buffers) { - listener(message, channel); - } - - if (!listeners.strings.size) return; - - const channelString = pattern ? channel.toString() : keyString, - messageString = channelString === '__redis__:invalidate' ? - // https://github.com/redis/redis/pull/7469 - // https://github.com/redis/redis/issues/7463 - (message === null ? null : (message as any as Array).map(x => x.toString())) as any : - message.toString(); - for (const listener of listeners.strings) { - listener(messageString, channelString); - } - } - readonly #maxLength: number | null | undefined; readonly #waitingToBeSent = new LinkedList(); readonly #waitingForReply = new LinkedList(); + readonly #onShardedChannelMoved: OnShardedChannelMoved; - readonly #pubSubState = { - isActive: false, - subscribing: 0, - subscribed: 0, - unsubscribing: 0, - listeners: { - channels: new Map(), - patterns: new Map() - } - }; + readonly #pubSub = new PubSub(); - static readonly #PUB_SUB_MESSAGES = { - message: Buffer.from('message'), - pMessage: Buffer.from('pmessage'), - subscribe: Buffer.from('subscribe'), - pSubscribe: Buffer.from('psubscribe'), - unsubscribe: Buffer.from('unsubscribe'), - pUnsubscribe: Buffer.from('punsubscribe') - }; + get isPubSubActive() { + return this.#pubSub.isActive; + } #chainInExecution: symbol | undefined; #decoder = new RESP2Decoder({ returnStringsAsBuffers: () => { return !!this.#waitingForReply.head?.value.returnBuffers || - this.#pubSubState.isActive; + this.#pubSub.isActive; }, onReply: reply => { - if (this.#handlePubSubReply(reply)) { - return; - } else if (!this.#waitingForReply.length) { - throw new Error('Got an unexpected reply from Redis'); + if (this.#pubSub.isActive && Array.isArray(reply)) { + if (this.#pubSub.handleMessageReply(reply as Array)) return; + + const isShardedUnsubscribe = PubSub.isShardedUnsubscribe(reply as Array); + if (isShardedUnsubscribe && !this.#waitingForReply.length) { + const channel = (reply[1] as Buffer).toString(); + this.#onShardedChannelMoved( + channel, + this.#pubSub.removeShardedListeners(channel) + ); + return; + } else if (isShardedUnsubscribe || PubSub.isStatusReply(reply as Array)) { + const head = this.#waitingForReply.head!.value; + if ( + (Number.isNaN(head.channelsCounter!) && reply[2] === 0) || + --head.channelsCounter! === 0 + ) { + this.#waitingForReply.shift()!.resolve(); + } + return; + } + if (PONG.equals(reply[0] as Buffer)) { + const { resolve, returnBuffers } = this.#waitingForReply.shift()!, + buffer = ((reply[1] as Buffer).length === 0 ? reply[0] : reply[1]) as Buffer; + resolve(returnBuffers ? buffer : buffer.toString()); + return; + } } - + const { resolve, reject } = this.#waitingForReply.shift()!; if (reply instanceof ErrorReply) { reject(reply); @@ -127,14 +96,16 @@ export default class RedisCommandsQueue { } }); - constructor(maxLength: number | null | undefined) { + constructor( + maxLength: number | null | undefined, + onShardedChannelMoved: OnShardedChannelMoved + ) { this.#maxLength = maxLength; + this.#onShardedChannelMoved = onShardedChannelMoved; } addCommand(args: RedisCommandArguments, options?: QueueCommandOptions): Promise { - if (this.#pubSubState.isActive && !options?.ignorePubSubMode) { - return Promise.reject(new Error('Cannot send commands in PubSub mode')); - } else if (this.#maxLength && this.#waitingToBeSent.length + this.#waitingForReply.length >= this.#maxLength) { + 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()); @@ -173,158 +144,76 @@ export default class RedisCommandsQueue { } subscribe( - command: PubSubSubscribeCommands, - channels: RedisCommandArgument | Array, + type: PubSubType, + channels: string | Array, listener: PubSubListener, returnBuffers?: T - ): Promise { - const channelsToSubscribe: Array = [], - listenersMap = command === PubSubSubscribeCommands.SUBSCRIBE ? - this.#pubSubState.listeners.channels : - this.#pubSubState.listeners.patterns; - for (const channel of (Array.isArray(channels) ? channels : [channels])) { - 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); - } - - // https://github.com/microsoft/TypeScript/issues/23132 - (returnBuffers ? listeners.buffers : listeners.strings).add(listener as any); - } - - if (!channelsToSubscribe.length) { - return Promise.resolve(); - } - - return this.#pushPubSubCommand(command, channelsToSubscribe); + ) { + return this.#pushPubSubCommand( + this.#pubSub.subscribe(type, channels, listener, returnBuffers) + ); } unsubscribe( - command: PubSubUnsubscribeCommands, + type: PubSubType, channels?: string | Array, listener?: PubSubListener, returnBuffers?: T - ): Promise { - const listeners = command === PubSubUnsubscribeCommands.UNSUBSCRIBE ? - this.#pubSubState.listeners.channels : - this.#pubSubState.listeners.patterns; - - if (!channels) { - const size = listeners.size; - listeners.clear(); - return this.#pushPubSubCommand(command, size); - } - - const channelsToUnsubscribe = []; - for (const channel of (Array.isArray(channels) ? channels : [channels])) { - const sets = listeners.get(channel); - if (!sets) continue; - - let shouldUnsubscribe; - if (listener) { - // https://github.com/microsoft/TypeScript/issues/23132 - (returnBuffers ? sets.buffers : sets.strings).delete(listener as any); - shouldUnsubscribe = !sets.buffers.size && !sets.strings.size; - } else { - shouldUnsubscribe = true; - } + ) { + return this.#pushPubSubCommand( + this.#pubSub.unsubscribe(type, channels, listener, returnBuffers) + ); + } - if (shouldUnsubscribe) { - channelsToUnsubscribe.push(channel); - listeners.delete(channel); - } - } + resubscribe(): Promise | undefined { + const commands = this.#pubSub.resubscribe(); + if (!commands.length) return; - if (!channelsToUnsubscribe.length) { - return Promise.resolve(); - } + return Promise.all( + commands.map(command => this.#pushPubSubCommand(command)) + ); + } - return this.#pushPubSubCommand(command, channelsToUnsubscribe); + extendPubSubChannelListeners( + type: PubSubType, + channel: string, + listeners: ChannelListeners + ) { + return this.#pushPubSubCommand( + this.#pubSub.extendChannelListeners(type, channel, listeners) + ); } - #pushPubSubCommand(command: PubSubSubscribeCommands | PubSubUnsubscribeCommands, channels: number | Array): Promise { - return new Promise((resolve, reject) => { - const isSubscribe = command === PubSubSubscribeCommands.SUBSCRIBE || command === PubSubSubscribeCommands.PSUBSCRIBE, - inProgressKey = isSubscribe ? 'subscribing' : 'unsubscribing', - commandArgs: Array = [command]; + extendPubSubListeners(type: PubSubType, listeners: PubSubTypeListeners) { + return this.#pushPubSubCommand( + this.#pubSub.extendTypeListeners(type, listeners) + ); + } - let channelsCounter: number; - if (typeof channels === 'number') { // unsubscribe only - channelsCounter = channels; - } else { - commandArgs.push(...channels); - channelsCounter = channels.length; - } + getPubSubListeners(type: PubSubType) { + return this.#pubSub.getTypeListeners(type); + } - this.#pubSubState.isActive = true; - this.#pubSubState[inProgressKey] += channelsCounter; + #pushPubSubCommand(command: PubSubCommand) { + if (command === undefined) return; + return new Promise((resolve, reject) => { this.#waitingToBeSent.push({ - args: commandArgs, - channelsCounter, + args: command.args, + channelsCounter: command.channelsCounter, returnBuffers: true, resolve: () => { - this.#pubSubState[inProgressKey] -= channelsCounter; - this.#pubSubState.subscribed += channelsCounter * (isSubscribe ? 1 : -1); - this.#updatePubSubActiveState(); + command.resolve(); resolve(); }, reject: err => { - this.#pubSubState[inProgressKey] -= channelsCounter * (isSubscribe ? 1 : -1); - this.#updatePubSubActiveState(); + command.reject?.(); reject(err); } }); }); } - #updatePubSubActiveState(): void { - if ( - !this.#pubSubState.subscribed && - !this.#pubSubState.subscribing && - !this.#pubSubState.subscribed - ) { - this.#pubSubState.isActive = false; - } - } - - resubscribe(): Promise | undefined { - this.#pubSubState.subscribed = 0; - this.#pubSubState.subscribing = 0; - this.#pubSubState.unsubscribing = 0; - - const promises = [], - { channels, patterns } = this.#pubSubState.listeners; - - if (channels.size) { - promises.push( - this.#pushPubSubCommand( - PubSubSubscribeCommands.SUBSCRIBE, - [...channels.keys()] - ) - ); - } - - if (patterns.size) { - promises.push( - this.#pushPubSubCommand( - PubSubSubscribeCommands.PSUBSCRIBE, - [...patterns.keys()] - ) - ); - } - - if (promises.length) { - return Promise.all(promises); - } - } - getCommandToSend(): RedisCommandArguments | undefined { const toSend = this.#waitingToBeSent.shift(); if (!toSend) return; @@ -351,39 +240,9 @@ export default class RedisCommandsQueue { this.#decoder.write(chunk); } - #handlePubSubReply(reply: any): boolean { - if (!this.#pubSubState.isActive || !Array.isArray(reply)) return false; - - if (RedisCommandsQueue.#PUB_SUB_MESSAGES.message.equals(reply[0])) { - RedisCommandsQueue.#emitPubSubMessage( - this.#pubSubState.listeners.channels, - reply[2], - reply[1] - ); - } else if (RedisCommandsQueue.#PUB_SUB_MESSAGES.pMessage.equals(reply[0])) { - 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.#waitingForReply.shift()!.resolve(); - } - } - - return true; - } - flushWaitingForReply(err: Error): void { this.#decoder.reset(); - this.#pubSubState.isActive = false; + this.#pubSub.reset(); RedisCommandsQueue.#flushQueue(this.#waitingForReply, err); if (!this.#chainInExecution) return; @@ -396,6 +255,8 @@ export default class RedisCommandsQueue { } flushAll(err: Error): void { + this.#decoder.reset(); + this.#pubSub.reset(); RedisCommandsQueue.#flushQueue(this.#waitingForReply, err); RedisCommandsQueue.#flushQueue(this.#waitingToBeSent, err); } diff --git a/packages/client/lib/client/commands.ts b/packages/client/lib/client/commands.ts index 8b5320a5d80..2605962432a 100644 --- a/packages/client/lib/client/commands.ts +++ b/packages/client/lib/client/commands.ts @@ -98,6 +98,7 @@ import * as PING from '../commands/PING'; import * as PUBSUB_CHANNELS from '../commands/PUBSUB_CHANNELS'; import * as PUBSUB_NUMPAT from '../commands/PUBSUB_NUMPAT'; import * as PUBSUB_NUMSUB from '../commands/PUBSUB_NUMSUB'; +import * as PUBSUB_SHARDCHANNELS from '../commands/PUBSUB_SHARDCHANNELS'; import * as RANDOMKEY from '../commands/RANDOMKEY'; import * as READONLY from '../commands/READONLY'; import * as READWRITE from '../commands/READWRITE'; @@ -317,6 +318,8 @@ export default { pubSubNumPat: PUBSUB_NUMPAT, PUBSUB_NUMSUB, pubSubNumSub: PUBSUB_NUMSUB, + PUBSUB_SHARDCHANNELS, + pubSubShardChannels: PUBSUB_SHARDCHANNELS, RANDOMKEY, randomKey: RANDOMKEY, READONLY, diff --git a/packages/client/lib/client/index.spec.ts b/packages/client/lib/client/index.spec.ts index a6cd7fd4b80..aadf823e572 100644 --- a/packages/client/lib/client/index.spec.ts +++ b/packages/client/lib/client/index.spec.ts @@ -2,14 +2,20 @@ import { strict as assert } from 'assert'; import testUtils, { GLOBAL, waitTillBeenCalled } from '../test-utils'; import RedisClient, { RedisClientType } from '.'; import { RedisClientMultiCommandType } from './multi-command'; -import { RedisCommandArguments, RedisCommandRawReply, RedisModules, RedisFunctions, RedisScripts } from '../commands'; -import { AbortError, ClientClosedError, ClientOfflineError, ConnectionTimeoutError, DisconnectsClientError, SocketClosedUnexpectedlyError, WatchError } from '../errors'; +import { RedisCommandRawReply, RedisModules, RedisFunctions, RedisScripts } from '../commands'; +import { AbortError, ClientClosedError, ClientOfflineError, ConnectionTimeoutError, DisconnectsClientError, ErrorReply, SocketClosedUnexpectedlyError, WatchError } from '../errors'; import { defineScript } from '../lua-script'; import { spy } from 'sinon'; import { once } from 'events'; import { ClientKillFilters } from '../commands/CLIENT_KILL'; +import { ClusterSlotStates } from '../commands/CLUSTER_SETSLOT'; import { promisify } from 'util'; +// We need to use 'require', because it's not possible with Typescript to import +// function that are exported as 'module.exports = function`, without esModuleInterop +// set to true. +const calculateSlot = require('cluster-key-slot'); + export const SQUARE_SCRIPT = defineScript({ SCRIPT: 'return ARGV[1] * ARGV[1];', NUMBER_OF_KEYS: 0, @@ -817,7 +823,34 @@ describe('Client', () => { } }, GLOBAL.SERVERS.OPEN); - testUtils.testWithClient('should be able to quit in PubSub mode', async client => { + testUtils.testWithClient('should be able to PING in PubSub mode', async client => { + await client.connect(); + + try { + await client.subscribe('channel', () => { + // noop + }); + + const [string, buffer, customString, customBuffer] = await Promise.all([ + client.ping(), + client.ping(client.commandOptions({ returnBuffers: true })), + client.ping('custom'), + client.ping(client.commandOptions({ returnBuffers: true }), 'custom') + ]); + + assert.equal(string, 'pong'); + assert.deepEqual(buffer, Buffer.from('pong')); + assert.equal(customString, 'custom'); + assert.deepEqual(customBuffer, Buffer.from('custom')); + } finally { + await client.disconnect(); + } + }, { + ...GLOBAL.SERVERS.OPEN, + disableClientSetup: true + }); + + testUtils.testWithClient('should be able to QUIT in PubSub mode', async client => { await client.subscribe('channel', () => { // noop }); @@ -826,6 +859,122 @@ describe('Client', () => { assert.equal(client.isOpen, false); }, GLOBAL.SERVERS.OPEN); + + testUtils.testWithClient('should reject GET in PubSub mode', async client => { + await client.connect(); + + try { + await client.subscribe('channel', () => { + // noop + }); + + await assert.rejects(client.get('key'), ErrorReply); + } finally { + await client.disconnect(); + } + }, { + ...GLOBAL.SERVERS.OPEN, + disableClientSetup: true + }); + + describe('shareded PubSub', () => { + testUtils.isVersionGreaterThanHook([7]); + + testUtils.testWithClient('should be able to receive messages', async publisher => { + const subscriber = publisher.duplicate(); + + await subscriber.connect(); + + try { + const listener = spy(); + await subscriber.sSubscribe('channel', listener); + + await Promise.all([ + waitTillBeenCalled(listener), + publisher.sPublish('channel', 'message') + ]); + + assert.ok(listener.calledOnceWithExactly('message', 'channel')); + + await subscriber.sUnsubscribe(); + + // should be able to send commands + await assert.doesNotReject(subscriber.ping()); + } finally { + await subscriber.disconnect(); + } + }, { + ...GLOBAL.SERVERS.OPEN + }); + + testUtils.testWithClient('should emit sharded-channel-moved event', async publisher => { + await publisher.clusterAddSlotsRange({ start: 0, end: 16383 }); + + const subscriber = publisher.duplicate(); + + await subscriber.connect(); + + try { + await subscriber.sSubscribe('channel', () => {}); + + await Promise.all([ + publisher.clusterSetSlot( + calculateSlot('channel'), + ClusterSlotStates.NODE, + await publisher.clusterMyId() + ), + once(subscriber, 'sharded-channel-moved') + ]); + + assert.equal( + await subscriber.ping(), + 'PONG' + ); + } finally { + await subscriber.disconnect(); + } + }, { + serverArguments: ['--cluster-enabled', 'yes'] + }); + }); + + testUtils.testWithClient('should handle errors in SUBSCRIBE', async publisher => { + const subscriber = publisher.duplicate(); + + await subscriber.connect(); + + try { + const listener1 = spy(); + await subscriber.subscribe('1', listener1); + + await publisher.aclSetUser('default', 'resetchannels'); + + + const listener2 = spy(); + await assert.rejects(subscriber.subscribe('2', listener2)); + + await Promise.all([ + waitTillBeenCalled(listener1), + publisher.aclSetUser('default', 'allchannels'), + publisher.publish('1', 'message'), + ]); + assert.ok(listener1.calledOnceWithExactly('message', '1')); + + await subscriber.subscribe('2', listener2); + + await Promise.all([ + waitTillBeenCalled(listener2), + publisher.publish('2', 'message'), + ]); + assert.ok(listener2.calledOnceWithExactly('message', '2')); + } finally { + await subscriber.disconnect(); + } + }, { + // this test change ACL rules, running in isolated server + serverArguments: [], + minimumDockerVersion: [6 ,2] // ACL PubSub rules were added in Redis 6.2 + }); }); testUtils.testWithClient('ConnectionTimeoutError', async client => { diff --git a/packages/client/lib/client/index.ts b/packages/client/lib/client/index.ts index f1e83edb417..ae5e2fe5e84 100644 --- a/packages/client/lib/client/index.ts +++ b/packages/client/lib/client/index.ts @@ -1,7 +1,7 @@ import COMMANDS from './commands'; import { RedisCommand, RedisCommandArguments, RedisCommandRawReply, RedisCommandReply, RedisFunctions, RedisModules, RedisExtensions, RedisScript, RedisScripts, RedisCommandSignature, ConvertArgumentType, RedisFunction, ExcludeMappedString, RedisCommands } from '../commands'; import RedisSocket, { RedisSocketOptions, RedisTlsSocketOptions } from './socket'; -import RedisCommandsQueue, { PubSubListener, PubSubSubscribeCommands, PubSubUnsubscribeCommands, QueueCommandOptions } from './commands-queue'; +import RedisCommandsQueue, { QueueCommandOptions } from './commands-queue'; import RedisClientMultiCommand, { RedisClientMultiCommandType } from './multi-command'; import { RedisMultiQueuedCommand } from '../multi-command'; import { EventEmitter } from 'events'; @@ -14,23 +14,57 @@ import { Pool, Options as PoolOptions, createPool } from 'generic-pool'; import { ClientClosedError, ClientOfflineError, DisconnectsClientError } from '../errors'; import { URL } from 'url'; import { TcpSocketConnectOpts } from 'net'; +import { PubSubType, PubSubListener, PubSubTypeListeners, ChannelListeners } from './pub-sub'; export interface RedisClientOptions< M extends RedisModules = RedisModules, F extends RedisFunctions = RedisFunctions, S extends RedisScripts = RedisScripts > extends RedisExtensions { + /** + * `redis[s]://[[username][:password]@][host][:port][/db-number]` + * See [`redis`](https://www.iana.org/assignments/uri-schemes/prov/redis) and [`rediss`](https://www.iana.org/assignments/uri-schemes/prov/rediss) IANA registration for more details + */ url?: string; + /** + * Socket connection properties + */ socket?: RedisSocketOptions; + /** + * ACL username ([see ACL guide](https://redis.io/topics/acl)) + */ username?: string; + /** + * ACL password or the old "--requirepass" password + */ password?: string; + /** + * Client name ([see `CLIENT SETNAME`](https://redis.io/commands/client-setname)) + */ name?: string; + /** + * Redis database number (see [`SELECT`](https://redis.io/commands/select) command) + */ database?: number; + /** + * Maximum length of the client's internal command queue + */ commandsQueueMaxLength?: number; + /** + * When `true`, commands are rejected when the client is reconnecting. + * When `false`, commands are queued for execution after reconnection. + */ disableOfflineQueue?: boolean; + /** + * Connect in [`READONLY`](https://redis.io/commands/readonly) mode + */ readonly?: boolean; legacyMode?: boolean; isolationPoolOptions?: PoolOptions; + /** + * Send `PING` command at interval (in ms). + * Useful with Redis deployments that do not use TCP Keep-Alive. + */ pingInterval?: number; } @@ -171,6 +205,10 @@ export default class RedisClient< return this.#socket.isReady; } + get isPubSubActive() { + return this.#queue.isPubSubActive; + } + get v4(): Record { if (!this.#options?.legacyMode) { throw new Error('the client is not in "legacy mode"'); @@ -215,7 +253,10 @@ export default class RedisClient< } #initiateQueue(): RedisCommandsQueue { - return new RedisCommandsQueue(this.#options?.commandsQueueMaxLength); + return new RedisCommandsQueue( + this.#options?.commandsQueueMaxLength, + (channel, listeners) => this.emit('sharded-channel-moved', channel, listeners) + ); } #initiateSocket(): RedisSocket { @@ -377,8 +418,8 @@ export default class RedisClient< }); } - async connect(): Promise { - await this.#socket.connect(); + connect(): Promise { + return this.#socket.connect(); } async commandsExecutor( @@ -500,18 +541,9 @@ export default class RedisClient< select = this.SELECT; - #subscribe( - command: PubSubSubscribeCommands, - channels: string | Array, - listener: PubSubListener, - bufferMode?: T - ): Promise { - const promise = this.#queue.subscribe( - command, - channels, - listener, - bufferMode - ); + #pubSubCommand(promise: Promise | undefined) { + if (promise === undefined) return Promise.resolve(); + this.#tick(); return promise; } @@ -521,77 +553,127 @@ export default class RedisClient< listener: PubSubListener, bufferMode?: T ): Promise { - return this.#subscribe( - PubSubSubscribeCommands.SUBSCRIBE, - channels, - listener, - bufferMode + return this.#pubSubCommand( + this.#queue.subscribe( + PubSubType.CHANNELS, + channels, + listener, + bufferMode + ) ); } subscribe = this.SUBSCRIBE; + + UNSUBSCRIBE( + channels?: string | Array, + listener?: PubSubListener, + bufferMode?: T + ): Promise { + return this.#pubSubCommand( + this.#queue.unsubscribe( + PubSubType.CHANNELS, + channels, + listener, + bufferMode + ) + ); + } + + unsubscribe = this.UNSUBSCRIBE; + PSUBSCRIBE( patterns: string | Array, listener: PubSubListener, bufferMode?: T ): Promise { - return this.#subscribe( - PubSubSubscribeCommands.PSUBSCRIBE, - patterns, - listener, - bufferMode + return this.#pubSubCommand( + this.#queue.subscribe( + PubSubType.PATTERNS, + patterns, + listener, + bufferMode + ) ); } pSubscribe = this.PSUBSCRIBE; - #unsubscribe( - command: PubSubUnsubscribeCommands, - channels?: string | Array, + PUNSUBSCRIBE( + patterns?: string | Array, listener?: PubSubListener, bufferMode?: T ): Promise { - const promise = this.#queue.unsubscribe(command, channels, listener, bufferMode); - this.#tick(); - return promise; + return this.#pubSubCommand( + this.#queue.unsubscribe( + PubSubType.PATTERNS, + patterns, + listener, + bufferMode + ) + ); } - UNSUBSCRIBE( - channels?: string | Array, - listener?: PubSubListener, + pUnsubscribe = this.PUNSUBSCRIBE; + + SSUBSCRIBE( + channels: string | Array, + listener: PubSubListener, bufferMode?: T ): Promise { - return this.#unsubscribe( - PubSubUnsubscribeCommands.UNSUBSCRIBE, - channels, - listener, - bufferMode + return this.#pubSubCommand( + this.#queue.subscribe( + PubSubType.SHARDED, + channels, + listener, + bufferMode + ) ); } - unsubscribe = this.UNSUBSCRIBE; + sSubscribe = this.SSUBSCRIBE; - PUNSUBSCRIBE( - patterns?: string | Array, + SUNSUBSCRIBE( + channels?: string | Array, listener?: PubSubListener, bufferMode?: T ): Promise { - return this.#unsubscribe( - PubSubUnsubscribeCommands.PUNSUBSCRIBE, - patterns, - listener, - bufferMode + return this.#pubSubCommand( + this.#queue.unsubscribe( + PubSubType.SHARDED, + channels, + listener, + bufferMode + ) ); } - pUnsubscribe = this.PUNSUBSCRIBE; + sUnsubscribe = this.SUNSUBSCRIBE; + + getPubSubListeners(type: PubSubType) { + return this.#queue.getPubSubListeners(type); + } + + extendPubSubChannelListeners( + type: PubSubType, + channel: string, + listeners: ChannelListeners + ) { + return this.#pubSubCommand( + this.#queue.extendPubSubChannelListeners(type, channel, listeners) + ); + } + + extendPubSubListeners(type: PubSubType, listeners: PubSubTypeListeners) { + return this.#pubSubCommand( + this.#queue.extendPubSubListeners(type, listeners) + ); + } QUIT(): Promise { return this.#socket.quit(async () => { - const quitPromise = this.#queue.addCommand(['QUIT'], { - ignorePubSubMode: true - }); + const quitPromise = this.#queue.addCommand(['QUIT']); this.#tick(); const [reply] = await Promise.all([ quitPromise, diff --git a/packages/client/lib/client/pub-sub.spec.ts b/packages/client/lib/client/pub-sub.spec.ts new file mode 100644 index 00000000000..8b9f16732cb --- /dev/null +++ b/packages/client/lib/client/pub-sub.spec.ts @@ -0,0 +1,151 @@ +import { strict as assert } from 'assert'; +import { PubSub, PubSubType } from './pub-sub'; + +describe('PubSub', () => { + const TYPE = PubSubType.CHANNELS, + CHANNEL = 'channel', + LISTENER = () => {}; + + describe('subscribe to new channel', () => { + function createAndSubscribe() { + const pubSub = new PubSub(), + command = pubSub.subscribe(TYPE, CHANNEL, LISTENER); + + assert.equal(pubSub.isActive, true); + assert.ok(command); + assert.equal(command.channelsCounter, 1); + + return { + pubSub, + command + }; + } + + it('resolve', () => { + const { pubSub, command } = createAndSubscribe(); + + command.resolve(); + + assert.equal(pubSub.isActive, true); + }); + + it('reject', () => { + const { pubSub, command } = createAndSubscribe(); + + assert.ok(command.reject); + command.reject(); + + assert.equal(pubSub.isActive, false); + }); + }); + + it('subscribe to already subscribed channel', () => { + const pubSub = new PubSub(), + firstSubscribe = pubSub.subscribe(TYPE, CHANNEL, LISTENER); + assert.ok(firstSubscribe); + + const secondSubscribe = pubSub.subscribe(TYPE, CHANNEL, LISTENER); + assert.ok(secondSubscribe); + + firstSubscribe.resolve(); + + assert.equal( + pubSub.subscribe(TYPE, CHANNEL, LISTENER), + undefined + ); + }); + + it('unsubscribe all', () => { + const pubSub = new PubSub(); + + const subscribe = pubSub.subscribe(TYPE, CHANNEL, LISTENER); + assert.ok(subscribe); + subscribe.resolve(); + assert.equal(pubSub.isActive, true); + + const unsubscribe = pubSub.unsubscribe(TYPE); + assert.equal(pubSub.isActive, true); + assert.ok(unsubscribe); + unsubscribe.resolve(); + assert.equal(pubSub.isActive, false); + }); + + describe('unsubscribe from channel', () => { + it('when not subscribed', () => { + const pubSub = new PubSub(), + unsubscribe = pubSub.unsubscribe(TYPE, CHANNEL); + assert.ok(unsubscribe); + unsubscribe.resolve(); + assert.equal(pubSub.isActive, false); + }); + + it('when already subscribed', () => { + const pubSub = new PubSub(), + subscribe = pubSub.subscribe(TYPE, CHANNEL, LISTENER); + assert.ok(subscribe); + subscribe.resolve(); + assert.equal(pubSub.isActive, true); + + const unsubscribe = pubSub.unsubscribe(TYPE, CHANNEL); + assert.equal(pubSub.isActive, true); + assert.ok(unsubscribe); + unsubscribe.resolve(); + assert.equal(pubSub.isActive, false); + }); + }); + + describe('unsubscribe from listener', () => { + it('when it\'s the only listener', () => { + const pubSub = new PubSub(), + subscribe = pubSub.subscribe(TYPE, CHANNEL, LISTENER); + assert.ok(subscribe); + subscribe.resolve(); + assert.equal(pubSub.isActive, true); + + const unsubscribe = pubSub.unsubscribe(TYPE, CHANNEL, LISTENER); + assert.ok(unsubscribe); + unsubscribe.resolve(); + assert.equal(pubSub.isActive, false); + }); + + it('when there are more listeners', () => { + const pubSub = new PubSub(), + subscribe = pubSub.subscribe(TYPE, CHANNEL, LISTENER); + assert.ok(subscribe); + subscribe.resolve(); + assert.equal(pubSub.isActive, true); + + assert.equal( + pubSub.subscribe(TYPE, CHANNEL, () => {}), + undefined + ); + + assert.equal( + pubSub.unsubscribe(TYPE, CHANNEL, LISTENER), + undefined + ); + }); + + describe('non-existing listener', () => { + it('on subscribed channel', () => { + const pubSub = new PubSub(), + subscribe = pubSub.subscribe(TYPE, CHANNEL, LISTENER); + assert.ok(subscribe); + subscribe.resolve(); + assert.equal(pubSub.isActive, true); + + assert.equal( + pubSub.unsubscribe(TYPE, CHANNEL, () => {}), + undefined + ); + assert.equal(pubSub.isActive, true); + }); + + it('on unsubscribed channel', () => { + const pubSub = new PubSub(); + assert.ok(pubSub.unsubscribe(TYPE, CHANNEL, () => {})); + assert.equal(pubSub.isActive, false); + }); + }); + }); +}); diff --git a/packages/client/lib/client/pub-sub.ts b/packages/client/lib/client/pub-sub.ts new file mode 100644 index 00000000000..8efc9d8a2e2 --- /dev/null +++ b/packages/client/lib/client/pub-sub.ts @@ -0,0 +1,408 @@ +import { RedisCommandArgument } from "../commands"; + +export enum PubSubType { + CHANNELS = 'CHANNELS', + PATTERNS = 'PATTERNS', + SHARDED = 'SHARDED' +} + +const COMMANDS = { + [PubSubType.CHANNELS]: { + subscribe: Buffer.from('subscribe'), + unsubscribe: Buffer.from('unsubscribe'), + message: Buffer.from('message') + }, + [PubSubType.PATTERNS]: { + subscribe: Buffer.from('psubscribe'), + unsubscribe: Buffer.from('punsubscribe'), + message: Buffer.from('pmessage') + }, + [PubSubType.SHARDED]: { + subscribe: Buffer.from('ssubscribe'), + unsubscribe: Buffer.from('sunsubscribe'), + message: Buffer.from('smessage') + } +}; + +export type PubSubListener< + RETURN_BUFFERS extends boolean = false +> = (message: T, channel: T) => unknown; + +export interface ChannelListeners { + unsubscribing: boolean; + buffers: Set>; + strings: Set>; +} + +export type PubSubTypeListeners = Map; + +type Listeners = Record; + +export type PubSubCommand = ReturnType< + typeof PubSub.prototype.subscribe | + typeof PubSub.prototype.unsubscribe | + typeof PubSub.prototype.extendTypeListeners +>; + +export class PubSub { + static isStatusReply(reply: Array): boolean { + return ( + COMMANDS[PubSubType.CHANNELS].subscribe.equals(reply[0]) || + COMMANDS[PubSubType.CHANNELS].unsubscribe.equals(reply[0]) || + COMMANDS[PubSubType.PATTERNS].subscribe.equals(reply[0]) || + COMMANDS[PubSubType.PATTERNS].unsubscribe.equals(reply[0]) || + COMMANDS[PubSubType.SHARDED].subscribe.equals(reply[0]) + ); + } + + static isShardedUnsubscribe(reply: Array): boolean { + return COMMANDS[PubSubType.SHARDED].unsubscribe.equals(reply[0]); + } + + static #channelsArray(channels: string | Array) { + return (Array.isArray(channels) ? channels : [channels]); + } + + static #listenersSet( + listeners: ChannelListeners, + returnBuffers?: T + ) { + return (returnBuffers ? listeners.buffers : listeners.strings); + } + + #subscribing = 0; + + #isActive = false; + + get isActive() { + return this.#isActive; + } + + #listeners: Listeners = { + [PubSubType.CHANNELS]: new Map(), + [PubSubType.PATTERNS]: new Map(), + [PubSubType.SHARDED]: new Map() + }; + + subscribe( + type: PubSubType, + channels: string | Array, + listener: PubSubListener, + returnBuffers?: T + ) { + const args: Array = [COMMANDS[type].subscribe], + channelsArray = PubSub.#channelsArray(channels); + for (const channel of channelsArray) { + let channelListeners = this.#listeners[type].get(channel); + if (!channelListeners || channelListeners.unsubscribing) { + args.push(channel); + } + } + + if (args.length === 1) { + // all channels are already subscribed, add listeners without issuing a command + for (const channel of channelsArray) { + PubSub.#listenersSet( + this.#listeners[type].get(channel)!, + returnBuffers + ).add(listener); + } + return; + } + + this.#isActive = true; + this.#subscribing++; + return { + args, + channelsCounter: args.length - 1, + resolve: () => { + this.#subscribing--; + for (const channel of channelsArray) { + let listeners = this.#listeners[type].get(channel); + if (!listeners) { + listeners = { + unsubscribing: false, + buffers: new Set(), + strings: new Set() + }; + this.#listeners[type].set(channel, listeners); + } + + PubSub.#listenersSet(listeners, returnBuffers).add(listener); + } + }, + reject: () => { + this.#subscribing--; + this.#updateIsActive(); + } + }; + } + + extendChannelListeners( + type: PubSubType, + channel: string, + listeners: ChannelListeners + ) { + if (!this.#extendChannelListeners(type, channel, listeners)) return; + + this.#isActive = true; + this.#subscribing++; + return { + args: [ + COMMANDS[type].subscribe, + channel + ], + channelsCounter: 1, + resolve: () => this.#subscribing--, + reject: () => { + this.#subscribing--; + this.#updateIsActive(); + } + }; + } + + #extendChannelListeners( + type: PubSubType, + channel: string, + listeners: ChannelListeners + ) { + const existingListeners = this.#listeners[type].get(channel); + if (!existingListeners) { + this.#listeners[type].set(channel, listeners); + return true; + } + + for (const listener of listeners.buffers) { + existingListeners.buffers.add(listener); + } + + for (const listener of listeners.strings) { + existingListeners.strings.add(listener); + } + + return false; + } + + extendTypeListeners(type: PubSubType, listeners: PubSubTypeListeners) { + const args: Array = [COMMANDS[type].subscribe]; + for (const [channel, channelListeners] of listeners) { + if (this.#extendChannelListeners(type, channel, channelListeners)) { + args.push(channel); + } + } + + if (args.length === 1) return; + + this.#isActive = true; + this.#subscribing++; + return { + args, + channelsCounter: args.length - 1, + resolve: () => this.#subscribing--, + reject: () => { + this.#subscribing--; + this.#updateIsActive(); + } + }; + } + + unsubscribe( + type: PubSubType, + channels?: string | Array, + listener?: PubSubListener, + returnBuffers?: T + ) { + const listeners = this.#listeners[type]; + if (!channels) { + return this.#unsubscribeCommand( + [COMMANDS[type].unsubscribe], + // cannot use `this.#subscribed` because there might be some `SUBSCRIBE` commands in the queue + // cannot use `this.#subscribed + this.#subscribing` because some `SUBSCRIBE` commands might fail + NaN, + () => listeners.clear() + ); + } + + const channelsArray = PubSub.#channelsArray(channels); + if (!listener) { + return this.#unsubscribeCommand( + [COMMANDS[type].unsubscribe, ...channelsArray], + channelsArray.length, + () => { + for (const channel of channelsArray) { + listeners.delete(channel); + } + } + ); + } + + const args: Array = [COMMANDS[type].unsubscribe]; + for (const channel of channelsArray) { + const sets = listeners.get(channel); + if (sets) { + let current, + other; + if (returnBuffers) { + current = sets.buffers; + other = sets.strings; + } else { + current = sets.strings; + other = sets.buffers; + } + + const currentSize = current.has(listener) ? current.size - 1 : current.size; + if (currentSize !== 0 || other.size !== 0) continue; + sets.unsubscribing = true; + } + + args.push(channel); + } + + if (args.length === 1) { + // all channels has other listeners, + // delete the listeners without issuing a command + for (const channel of channelsArray) { + PubSub.#listenersSet( + listeners.get(channel)!, + returnBuffers + ).delete(listener); + } + return; + } + + return this.#unsubscribeCommand( + args, + args.length - 1, + () => { + for (const channel of channelsArray) { + const sets = listeners.get(channel); + if (!sets) continue; + + (returnBuffers ? sets.buffers : sets.strings).delete(listener); + if (sets.buffers.size === 0 && sets.strings.size === 0) { + listeners.delete(channel); + } + } + } + ); + } + + #unsubscribeCommand( + args: Array, + channelsCounter: number, + removeListeners: () => void + ) { + return { + args, + channelsCounter, + resolve: () => { + removeListeners(); + this.#updateIsActive(); + }, + reject: undefined // use the same structure as `subscribe` + }; + } + + #updateIsActive() { + this.#isActive = ( + this.#listeners[PubSubType.CHANNELS].size !== 0 || + this.#listeners[PubSubType.PATTERNS].size !== 0 || + this.#listeners[PubSubType.CHANNELS].size !== 0 || + this.#subscribing !== 0 + ); + } + + reset() { + this.#isActive = false; + this.#subscribing = 0; + } + + resubscribe(): Array { + const commands = []; + for (const [type, listeners] of Object.entries(this.#listeners)) { + if (!listeners.size) continue; + + this.#isActive = true; + this.#subscribing++; + const callback = () => this.#subscribing--; + commands.push({ + args: [ + COMMANDS[type as PubSubType].subscribe, + ...listeners.keys() + ], + channelsCounter: listeners.size, + resolve: callback, + reject: callback + }); + } + + return commands; + } + + handleMessageReply(reply: Array): boolean { + if (COMMANDS[PubSubType.CHANNELS].message.equals(reply[0])) { + this.#emitPubSubMessage( + PubSubType.CHANNELS, + reply[2], + reply[1] + ); + return true; + } else if (COMMANDS[PubSubType.PATTERNS].message.equals(reply[0])) { + this.#emitPubSubMessage( + PubSubType.PATTERNS, + reply[3], + reply[2], + reply[1] + ); + return true; + } else if (COMMANDS[PubSubType.SHARDED].message.equals(reply[0])) { + this.#emitPubSubMessage( + PubSubType.SHARDED, + reply[2], + reply[1] + ); + return true; + } + + return false; + } + + removeShardedListeners(channel: string): ChannelListeners { + const listeners = this.#listeners[PubSubType.SHARDED].get(channel)!; + this.#listeners[PubSubType.SHARDED].delete(channel); + this.#updateIsActive(); + return listeners; + } + + #emitPubSubMessage( + type: PubSubType, + message: Buffer, + channel: Buffer, + pattern?: Buffer + ): void { + const keyString = (pattern ?? channel).toString(), + listeners = this.#listeners[type].get(keyString); + + if (!listeners) return; + + for (const listener of listeners.buffers) { + listener(message, channel); + } + + if (!listeners.strings.size) return; + + const channelString = pattern ? channel.toString() : keyString, + messageString = channelString === '__redis__:invalidate' ? + // https://github.com/redis/redis/pull/7469 + // https://github.com/redis/redis/issues/7463 + (message === null ? null : (message as any as Array).map(x => x.toString())) as any : + message.toString(); + for (const listener of listeners.strings) { + listener(messageString, channelString); + } + } + + getTypeListeners(type: PubSubType): PubSubTypeListeners { + return this.#listeners[type]; + } +} diff --git a/packages/client/lib/client/socket.spec.ts b/packages/client/lib/client/socket.spec.ts index c5862130cf5..86929b227a1 100644 --- a/packages/client/lib/client/socket.spec.ts +++ b/packages/client/lib/client/socket.spec.ts @@ -1,5 +1,6 @@ -import { strict as assert } from 'assert'; +import { strict as assert } from 'node:assert'; import { spy } from 'sinon'; +import { once } from 'node:events'; import RedisSocket, { RedisSocketOptions } from './socket'; describe('Socket', () => { @@ -17,16 +18,42 @@ describe('Socket', () => { } describe('reconnectStrategy', () => { + it('false', async () => { + const socket = createSocket({ + host: 'error', + connectTimeout: 1, + reconnectStrategy: false + }); + + await assert.rejects(socket.connect()); + + assert.equal(socket.isOpen, false); + }); + + it('0', async () => { + const socket = createSocket({ + host: 'error', + connectTimeout: 1, + reconnectStrategy: 0 + }); + + socket.connect(); + await once(socket, 'error'); + assert.equal(socket.isOpen, true); + assert.equal(socket.isReady, false); + socket.disconnect(); + assert.equal(socket.isOpen, false); + }); + it('custom strategy', async () => { - const numberOfRetries = 10; + const numberOfRetries = 3; const reconnectStrategy = spy((retries: number) => { assert.equal(retries + 1, reconnectStrategy.callCount); if (retries === numberOfRetries) return new Error(`${numberOfRetries}`); - const time = retries * 2; - return time; + return 0; }); const socket = createSocket({ diff --git a/packages/client/lib/client/socket.ts b/packages/client/lib/client/socket.ts index 345ac1d3e38..4c64f899559 100644 --- a/packages/client/lib/client/socket.ts +++ b/packages/client/lib/client/socket.ts @@ -6,10 +6,26 @@ import { ConnectionTimeoutError, ClientClosedError, SocketClosedUnexpectedlyErro import { promiseTimeout } from '../utils'; export interface RedisSocketCommonOptions { + /** + * Connection Timeout (in milliseconds) + */ connectTimeout?: number; + /** + * Toggle [`Nagle's algorithm`](https://nodejs.org/api/net.html#net_socket_setnodelay_nodelay) + */ noDelay?: boolean; + /** + * Toggle [`keep-alive`](https://nodejs.org/api/net.html#net_socket_setkeepalive_enable_initialdelay) + */ keepAlive?: number | false; - reconnectStrategy?(retries: number): number | Error; + /** + * When the socket closes unexpectedly (without calling `.quit()`/`.disconnect()`), the client uses `reconnectStrategy` to decide what to do. The following values are supported: + * 1. `false` -> do not reconnect, close the client and flush the command queue. + * 2. `number` -> wait for `X` milliseconds before reconnecting. + * 3. `(retries: number, cause: Error) => false | number | Error` -> `number` is the same as configuring a `number` directly, `Error` is the same as `false`, but with a custom error. + * Defaults to `retries => Math.min(retries * 50, 500)` + */ + reconnectStrategy?: false | number | ((retries: number, cause: Error) => false | Error | number); } type RedisNetSocketOptions = Partial & { @@ -83,23 +99,42 @@ export default class RedisSocket extends EventEmitter { this.#options = RedisSocket.#initiateOptions(options); } - reconnectStrategy(retries: number): number | Error { - if (this.#options.reconnectStrategy) { + #reconnectStrategy(retries: number, cause: Error) { + if (this.#options.reconnectStrategy === false) { + return false; + } else if (typeof this.#options.reconnectStrategy === 'number') { + return this.#options.reconnectStrategy; + } else if (this.#options.reconnectStrategy) { try { - const retryIn = this.#options.reconnectStrategy(retries); - if (typeof retryIn !== 'number' && !(retryIn instanceof Error)) { - throw new TypeError('Reconnect strategy should return `number | Error`'); + const retryIn = this.#options.reconnectStrategy(retries, cause); + if (retryIn !== false && !(retryIn instanceof Error) && typeof retryIn !== 'number') { + throw new TypeError(`Reconnect strategy should return \`false | Error | number\`, got ${retryIn} instead`); } return retryIn; } catch (err) { - this.emit('error', err); + this.emit('error', err); } } return Math.min(retries * 50, 500); } + #shouldReconnect(retries: number, cause: Error) { + const retryIn = this.#reconnectStrategy(retries, cause); + if (retryIn === false) { + this.#isOpen = false; + this.emit('error', cause); + return cause; + } else if (retryIn instanceof Error) { + this.#isOpen = false; + this.emit('error', cause); + return new ReconnectStrategyError(retryIn, cause); + } + + return retryIn; + } + async connect(): Promise { if (this.#isOpen) { throw new Error('Socket already opened'); @@ -109,13 +144,9 @@ export default class RedisSocket extends EventEmitter { return this.#connect(); } - async #connect(hadError?: boolean): Promise { + async #connect(): Promise { let retries = 0; do { - if (retries > 0 || hadError) { - this.emit('reconnecting'); - } - try { this.#socket = await this.#createSocket(); this.#writableNeedDrain = false; @@ -131,17 +162,17 @@ export default class RedisSocket extends EventEmitter { this.#isReady = true; this.emit('ready'); } catch (err) { - const retryIn = this.reconnectStrategy(retries); - if (retryIn instanceof Error) { - this.#isOpen = false; - this.emit('error', err); - throw new ReconnectStrategyError(retryIn, err); + const retryIn = this.#shouldReconnect(retries, err as Error); + if (typeof retryIn !== 'number') { + throw retryIn; } this.emit('error', err); await promiseTimeout(retryIn); } + retries++; + this.emit('reconnecting'); } while (this.#isOpen && !this.#isReady); } @@ -203,9 +234,10 @@ export default class RedisSocket extends EventEmitter { this.#isReady = false; this.emit('error', err); - if (!this.#isOpen) return; - - this.#connect(true).catch(() => { + if (!this.#isOpen || typeof this.#shouldReconnect(0, err) !== 'number') return; + + this.emit('reconnecting'); + this.#connect().catch(() => { // the error was already emitted, silently ignore it }); } diff --git a/packages/client/lib/cluster/cluster-slots.ts b/packages/client/lib/cluster/cluster-slots.ts index d23ef569f30..2804f499f59 100644 --- a/packages/client/lib/cluster/cluster-slots.ts +++ b/packages/client/lib/cluster/cluster-slots.ts @@ -1,157 +1,254 @@ import RedisClient, { InstantiableRedisClient, RedisClientType } from '../client'; -import { RedisClusterMasterNode, RedisClusterReplicaNode } from '../commands/CLUSTER_NODES'; import { RedisClusterClientOptions, RedisClusterOptions } from '.'; import { RedisCommandArgument, RedisFunctions, RedisModules, RedisScripts } from '../commands'; import { RootNodesUnavailableError } from '../errors'; +import { ClusterSlotsNode } from '../commands/CLUSTER_SLOTS'; +import { types } from 'node:util'; +import { ChannelListeners, PubSubType, PubSubTypeListeners } from '../client/pub-sub'; +import { EventEmitter } from 'node:stream'; // We need to use 'require', because it's not possible with Typescript to import // function that are exported as 'module.exports = function`, without esModuleInterop // set to true. const calculateSlot = require('cluster-key-slot'); -export interface ClusterNode< +interface NodeAddress { + host: string; + port: number; +} + +export type NodeAddressMap = { + [address: string]: NodeAddress; +} | ((address: string) => NodeAddress | undefined); + +type ValueOrPromise = T | Promise; + +type ClientOrPromise< + M extends RedisModules, + F extends RedisFunctions, + S extends RedisScripts +> = ValueOrPromise>; + +export interface Node< M extends RedisModules, F extends RedisFunctions, S extends RedisScripts > { - id: string; - client: RedisClientType; + address: string; + client?: ClientOrPromise; } -interface NodeAddress { +export interface ShardNode< + M extends RedisModules, + F extends RedisFunctions, + S extends RedisScripts +> extends Node { + id: string; host: string; port: number; + readonly: boolean; } -export type NodeAddressMap = { - [address: string]: NodeAddress; -} | ((address: string) => NodeAddress | undefined); +export interface MasterNode< + M extends RedisModules, + F extends RedisFunctions, + S extends RedisScripts +> extends ShardNode { + pubSubClient?: ClientOrPromise; +} -interface SlotNodes< +export interface Shard< M extends RedisModules, F extends RedisFunctions, S extends RedisScripts > { - master: ClusterNode; - replicas: Array>; - clientIterator: IterableIterator> | undefined; + master: MasterNode; + replicas?: Array>; + nodesIterator?: IterableIterator>; } -type OnError = (err: unknown) => void; +type ShardWithReplicas< + M extends RedisModules, + F extends RedisFunctions, + S extends RedisScripts +> = Shard & Required, 'replicas'>>; + +export type PubSubNode< + M extends RedisModules, + F extends RedisFunctions, + S extends RedisScripts +> = Required>; + +type PubSubToResubscribe = Record< + PubSubType.CHANNELS | PubSubType.PATTERNS, + PubSubTypeListeners +>; + +export type OnShardedChannelMovedError = ( + err: unknown, + channel: string, + listeners?: ChannelListeners +) => void; export default class RedisClusterSlots< M extends RedisModules, F extends RedisFunctions, S extends RedisScripts > { + static #SLOTS = 16384; + readonly #options: RedisClusterOptions; readonly #Client: InstantiableRedisClient; - readonly #onError: OnError; - readonly #nodeByAddress = new Map>(); - readonly #slots: Array> = []; + readonly #emit: EventEmitter['emit']; + slots = new Array>(RedisClusterSlots.#SLOTS); + shards = new Array>(); + masters = new Array>(); + replicas = new Array>(); + readonly nodeByAddress = new Map | ShardNode>(); + pubSubNode?: PubSubNode; + + #isOpen = false; + + get isOpen() { + return this.#isOpen; + } - constructor(options: RedisClusterOptions, onError: OnError) { + constructor( + options: RedisClusterOptions, + emit: EventEmitter['emit'] + ) { this.#options = options; this.#Client = RedisClient.extend(options); - this.#onError = onError; + this.#emit = emit; } - async connect(): Promise { - for (const rootNode of this.#options.rootNodes) { - if (await this.#discoverNodes(rootNode)) return; + async connect() { + if (this.#isOpen) { + throw new Error('Cluster already open'); } - throw new RootNodesUnavailableError(); - } - - async #discoverNodes(clientOptions?: RedisClusterClientOptions): Promise { - const client = this.#initiateClient(clientOptions); - - await client.connect(); - + this.#isOpen = true; try { - await this.#reset(await client.clusterNodes()); - return true; + await this.#discoverWithRootNodes(); } catch (err) { - this.#onError(err); - return false; - } finally { - if (client.isOpen) { - await client.disconnect(); - } + this.#isOpen = false; + throw err; } } - #runningRediscoverPromise?: Promise; + async #discoverWithRootNodes() { + let start = Math.floor(Math.random() * this.#options.rootNodes.length); + for (let i = start; i < this.#options.rootNodes.length; i++) { + if (await this.#discover(this.#options.rootNodes[i])) return; + } - async rediscover(startWith: RedisClientType): Promise { - if (!this.#runningRediscoverPromise) { - this.#runningRediscoverPromise = this.#rediscover(startWith) - .finally(() => this.#runningRediscoverPromise = undefined); + for (let i = 0; i < start; i++) { + if (await this.#discover(this.#options.rootNodes[i])) return; } - return this.#runningRediscoverPromise; + throw new RootNodesUnavailableError(); } - async #rediscover(startWith: RedisClientType): Promise { - if (await this.#discoverNodes(startWith.options)) return; + #resetSlots() { + this.slots = new Array(RedisClusterSlots.#SLOTS); + this.shards = []; + this.masters = []; + this.replicas = []; + this.#randomNodeIterator = undefined; + } - for (const { client } of this.#nodeByAddress.values()) { - if (client === startWith) continue; + async #discover(rootNode?: RedisClusterClientOptions) { + this.#resetSlots(); + const addressesInUse = new Set(); - if (await this.#discoverNodes(client.options)) return; - } - - throw new Error('None of the cluster nodes is available'); - } + try { + const shards = await this.#getShards(rootNode), + promises: Array> = [], + eagerConnect = this.#options.minimizeConnections !== true; + for (const { from, to, master, replicas } of shards) { + const shard: Shard = { + master: this.#initiateSlotNode(master, false, eagerConnect, addressesInUse, promises) + }; + + if (this.#options.useReplicas) { + shard.replicas = replicas.map(replica => + this.#initiateSlotNode(replica, true, eagerConnect, addressesInUse, promises) + ); + } - async #reset(masters: Array): Promise { - // Override this.#slots and add not existing clients to this.#nodeByAddress - const promises: Array> = [], - clientsInUse = new Set(); - for (const master of masters) { - const slot = { - master: this.#initiateClientForNode(master, false, clientsInUse, promises), - replicas: this.#options.useReplicas ? - master.replicas.map(replica => this.#initiateClientForNode(replica, true, clientsInUse, promises)) : - [], - clientIterator: undefined // will be initiated in use - }; + this.shards.push(shard); - for (const { from, to } of master.slots) { for (let i = from; i <= to; i++) { - this.#slots[i] = slot; + this.slots[i] = shard; } } - } - // Remove unused clients from this.#nodeByAddress using clientsInUse - for (const [address, { client }] of this.#nodeByAddress.entries()) { - if (clientsInUse.has(address)) continue; + if (this.pubSubNode && !addressesInUse.has(this.pubSubNode.address)) { + if (types.isPromise(this.pubSubNode.client)) { + promises.push( + this.pubSubNode.client.then(client => client.disconnect()) + ); + this.pubSubNode = undefined; + } else { + promises.push(this.pubSubNode.client.disconnect()); + + const channelsListeners = this.pubSubNode.client.getPubSubListeners(PubSubType.CHANNELS), + patternsListeners = this.pubSubNode.client.getPubSubListeners(PubSubType.PATTERNS); + + if (channelsListeners.size || patternsListeners.size) { + promises.push( + this.#initiatePubSubClient({ + [PubSubType.CHANNELS]: channelsListeners, + [PubSubType.PATTERNS]: patternsListeners + }) + ); + } + } + } - promises.push(client.disconnect()); - this.#nodeByAddress.delete(address); - } + for (const [address, node] of this.nodeByAddress.entries()) { + if (addressesInUse.has(address)) continue; - await Promise.all(promises); - } + if (node.client) { + promises.push( + this.#execOnNodeClient(node.client, client => client.disconnect()) + ); + } + + const { pubSubClient } = node as MasterNode; + if (pubSubClient) { + promises.push( + this.#execOnNodeClient(pubSubClient, client => client.disconnect()) + ); + } - #clientOptionsDefaults(options?: RedisClusterClientOptions): RedisClusterClientOptions | undefined { - if (!this.#options.defaults) return options; + this.nodeByAddress.delete(address); + } - return { - ...this.#options.defaults, - ...options, - socket: this.#options.defaults.socket && options?.socket ? { - ...this.#options.defaults.socket, - ...options.socket - } : this.#options.defaults.socket ?? options?.socket - }; + await Promise.all(promises); + + return true; + } catch (err) { + this.#emit('error', err); + return false; + } } - #initiateClient(options?: RedisClusterClientOptions): RedisClientType { - return new this.#Client(this.#clientOptionsDefaults(options)) - .on('error', this.#onError); + async #getShards(rootNode?: RedisClusterClientOptions) { + const client = new this.#Client( + this.#clientOptionsDefaults(rootNode, true) + ); + + client.on('error', err => this.#emit('error', err)); + + await client.connect(); + + try { + // using `CLUSTER SLOTS` and not `CLUSTER SHARDS` to support older versions + return await client.clusterSlots(); + } finally { + await client.disconnect(); + } } #getNodeAddress(address: string): NodeAddress | undefined { @@ -164,130 +261,361 @@ export default class RedisClusterSlots< } } - #initiateClientForNode( - nodeData: RedisClusterMasterNode | RedisClusterReplicaNode, - readonly: boolean, - clientsInUse: Set, - promises: Array> - ): ClusterNode { - const address = `${nodeData.host}:${nodeData.port}`; - clientsInUse.add(address); + #clientOptionsDefaults( + options?: RedisClusterClientOptions, + disableReconnect?: boolean + ): RedisClusterClientOptions | undefined { + let result: RedisClusterClientOptions | undefined; + if (this.#options.defaults) { + let socket; + if (this.#options.defaults.socket) { + socket = options?.socket ? { + ...this.#options.defaults.socket, + ...options.socket + } : this.#options.defaults.socket; + } else { + socket = options?.socket; + } - let node = this.#nodeByAddress.get(address); + result = { + ...this.#options.defaults, + ...options, + socket + }; + } else { + result = options; + } + + if (disableReconnect) { + result ??= {}; + result.socket ??= {}; + result.socket.reconnectStrategy = false; + } + + return result; + } + + #initiateSlotNode( + { id, ip, port }: ClusterSlotsNode, + readonly: boolean, + eagerConnent: boolean, + addressesInUse: Set, + promises: Array> + ) { + const address = `${ip}:${port}`; + addressesInUse.add(address); + + let node = this.nodeByAddress.get(address); if (!node) { node = { - id: nodeData.id, - client: this.#initiateClient({ - socket: this.#getNodeAddress(address) ?? { - host: nodeData.host, - port: nodeData.port - }, - readonly - }) + id, + host: ip, + port, + address, + readonly, + client: undefined }; - promises.push(node.client.connect()); - this.#nodeByAddress.set(address, node); + + if (eagerConnent) { + promises.push(this.#createNodeClient(node)); + } + + this.nodeByAddress.set(address, node); } + (readonly ? this.replicas : this.masters).push(node); + return node; } - getSlotMaster(slot: number): ClusterNode { - return this.#slots[slot].master; + async #createClient( + node: ShardNode, + readonly = node.readonly + ) { + const client = new this.#Client( + this.#clientOptionsDefaults({ + socket: this.#getNodeAddress(node.address) ?? { + host: node.host, + port: node.port + }, + readonly + }) + ); + client.on('error', err => this.#emit('error', err)); + + await client.connect(); + + return client; } - *#slotClientIterator(slotNumber: number): IterableIterator> { - const slot = this.#slots[slotNumber]; - yield slot.master.client; + #createNodeClient(node: ShardNode) { + const promise = this.#createClient(node) + .then(client => { + node.client = client; + return client; + }) + .catch(err => { + node.client = undefined; + throw err; + }); + node.client = promise; + return promise; + } - for (const replica of slot.replicas) { - yield replica.client; - } + nodeClient(node: ShardNode) { + return node.client ?? this.#createNodeClient(node); } - #getSlotClient(slotNumber: number): RedisClientType { - const slot = this.#slots[slotNumber]; - if (!slot.clientIterator) { - slot.clientIterator = this.#slotClientIterator(slotNumber); - } + #runningRediscoverPromise?: Promise; - const {done, value} = slot.clientIterator.next(); - if (done) { - slot.clientIterator = undefined; - return this.#getSlotClient(slotNumber); - } + async rediscover(startWith: RedisClientType): Promise { + this.#runningRediscoverPromise ??= this.#rediscover(startWith) + .finally(() => this.#runningRediscoverPromise = undefined); + return this.#runningRediscoverPromise; + } + + async #rediscover(startWith: RedisClientType): Promise { + if (await this.#discover(startWith.options)) return; - return value; + return this.#discoverWithRootNodes(); } - #randomClientIterator?: IterableIterator>; + quit(): Promise { + return this.#destroy(client => client.quit()); + } - #getRandomClient(): RedisClientType { - if (!this.#nodeByAddress.size) { - throw new Error('Cluster is not connected'); - } + disconnect(): Promise { + return this.#destroy(client => client.disconnect()); + } + + async #destroy(fn: (client: RedisClientType) => Promise): Promise { + this.#isOpen = false; + + const promises = []; + for (const { master, replicas } of this.shards) { + if (master.client) { + promises.push( + this.#execOnNodeClient(master.client, fn) + ); + } - if (!this.#randomClientIterator) { - this.#randomClientIterator = this.#nodeByAddress.values(); + if (master.pubSubClient) { + promises.push( + this.#execOnNodeClient(master.pubSubClient, fn) + ); + } + + if (replicas) { + for (const { client } of replicas) { + if (client) { + promises.push( + this.#execOnNodeClient(client, fn) + ); + } + } + } } - const {done, value} = this.#randomClientIterator.next(); - if (done) { - this.#randomClientIterator = undefined; - return this.#getRandomClient(); + if (this.pubSubNode) { + promises.push(this.#execOnNodeClient(this.pubSubNode.client, fn)); + this.pubSubNode = undefined; } - return value.client; + this.#resetSlots(); + this.nodeByAddress.clear(); + + await Promise.allSettled(promises); + } + + #execOnNodeClient( + client: ClientOrPromise, + fn: (client: RedisClientType) => Promise + ) { + return types.isPromise(client) ? + client.then(fn) : + fn(client); } - getClient(firstKey?: RedisCommandArgument, isReadonly?: boolean): RedisClientType { + getClient( + firstKey: RedisCommandArgument | undefined, + isReadonly: boolean | undefined + ): ClientOrPromise { if (!firstKey) { - return this.#getRandomClient(); + return this.nodeClient(this.getRandomNode()); } - const slot = calculateSlot(firstKey); - if (!isReadonly || !this.#options.useReplicas) { - return this.getSlotMaster(slot).client; + const slotNumber = calculateSlot(firstKey); + if (!isReadonly) { + return this.nodeClient(this.slots[slotNumber].master); } - return this.#getSlotClient(slot); + return this.nodeClient(this.getSlotRandomNode(slotNumber)); } - getMasters(): Array> { - const masters = []; - for (const node of this.#nodeByAddress.values()) { - if (node.client.options?.readonly) continue; + *#iterateAllNodes() { + let i = Math.floor(Math.random() * (this.masters.length + this.replicas.length)); + if (i < this.masters.length) { + do { + yield this.masters[i]; + } while (++i < this.masters.length); - masters.push(node); + for (const replica of this.replicas) { + yield replica; + } + } else { + i -= this.masters.length; + do { + yield this.replicas[i]; + } while (++i < this.replicas.length); } - return masters; + while (true) { + for (const master of this.masters) { + yield master; + } + + for (const replica of this.replicas) { + yield replica; + } + } } - getNodeByAddress(address: string): ClusterNode | undefined { - const mappedAddress = this.#getNodeAddress(address); - return this.#nodeByAddress.get( - mappedAddress ? `${mappedAddress.host}:${mappedAddress.port}` : address - ); + #randomNodeIterator?: IterableIterator>; + + getRandomNode() { + this.#randomNodeIterator ??= this.#iterateAllNodes(); + return this.#randomNodeIterator.next().value as ShardNode; } - quit(): Promise { - return this.#destroy(client => client.quit()); + *#slotNodesIterator(slot: ShardWithReplicas) { + let i = Math.floor(Math.random() * (1 + slot.replicas.length)); + if (i < slot.replicas.length) { + do { + yield slot.replicas[i]; + } while (++i < slot.replicas.length); + } + + while (true) { + yield slot.master; + + for (const replica of slot.replicas) { + yield replica; + } + } } - disconnect(): Promise { - return this.#destroy(client => client.disconnect()); + getSlotRandomNode(slotNumber: number) { + const slot = this.slots[slotNumber]; + if (!slot.replicas?.length) { + return slot.master; + } + + slot.nodesIterator ??= this.#slotNodesIterator(slot as ShardWithReplicas); + return slot.nodesIterator.next().value as ShardNode; } - async #destroy(fn: (client: RedisClientType) => Promise): Promise { - const promises = []; - for (const { client } of this.#nodeByAddress.values()) { - promises.push(fn(client)); + getMasterByAddress(address: string) { + const master = this.nodeByAddress.get(address); + if (!master) return; + + return this.nodeClient(master); + } + + getPubSubClient() { + return this.pubSubNode ? + this.pubSubNode.client : + this.#initiatePubSubClient(); + } + + async #initiatePubSubClient(toResubscribe?: PubSubToResubscribe) { + const index = Math.floor(Math.random() * (this.masters.length + this.replicas.length)), + node = index < this.masters.length ? + this.masters[index] : + this.replicas[index - this.masters.length]; + + this.pubSubNode = { + address: node.address, + client: this.#createClient(node, true) + .then(async client => { + if (toResubscribe) { + await Promise.all([ + client.extendPubSubListeners(PubSubType.CHANNELS, toResubscribe[PubSubType.CHANNELS]), + client.extendPubSubListeners(PubSubType.PATTERNS, toResubscribe[PubSubType.PATTERNS]) + ]); + } + + this.pubSubNode!.client = client; + return client; + }) + .catch(err => { + this.pubSubNode = undefined; + throw err; + }) + }; + + return this.pubSubNode.client as Promise>; + } + + async executeUnsubscribeCommand( + unsubscribe: (client: RedisClientType) => Promise + ): Promise { + const client = await this.getPubSubClient(); + await unsubscribe(client); + + if (!client.isPubSubActive) { + await client.disconnect(); + this.pubSubNode = undefined; } + } + + getShardedPubSubClient(channel: string) { + const { master } = this.slots[calculateSlot(channel)]; + return master.pubSubClient ?? this.#initiateShardedPubSubClient(master); + } + + #initiateShardedPubSubClient(master: MasterNode) { + const promise = this.#createClient(master, true) + .then(client => { + client.on('server-sunsubscribe', async (channel, listeners) => { + try { + await this.rediscover(client); + const redirectTo = await this.getShardedPubSubClient(channel); + redirectTo.extendPubSubChannelListeners( + PubSubType.SHARDED, + channel, + listeners + ); + } catch (err) { + this.#emit('sharded-shannel-moved-error', err, channel, listeners); + } + }); + + master.pubSubClient = client; + return client; + }) + .catch(err => { + master.pubSubClient = undefined; + throw err; + }); + + master.pubSubClient = promise; + + return promise; + } - await Promise.all(promises); + async executeShardedUnsubscribeCommand( + channel: string, + unsubscribe: (client: RedisClientType) => Promise + ): Promise { + const { master } = this.slots[calculateSlot(channel)]; + if (!master.pubSubClient) return Promise.resolve(); - this.#nodeByAddress.clear(); - this.#slots.splice(0); + const client = await master.pubSubClient; + await unsubscribe(client); + + if (!client.isPubSubActive) { + await client.disconnect(); + master.pubSubClient = undefined; + } } } diff --git a/packages/client/lib/cluster/commands.ts b/packages/client/lib/cluster/commands.ts index 8edbd1e3891..58fa651be1b 100644 --- a/packages/client/lib/cluster/commands.ts +++ b/packages/client/lib/cluster/commands.ts @@ -135,6 +135,7 @@ import * as SORT_RO from '../commands/SORT_RO'; import * as SORT_STORE from '../commands/SORT_STORE'; import * as SORT from '../commands/SORT'; import * as SPOP from '../commands/SPOP'; +import * as SPUBLISH from '../commands/SPUBLISH'; import * as SRANDMEMBER_COUNT from '../commands/SRANDMEMBER_COUNT'; import * as SRANDMEMBER from '../commands/SRANDMEMBER'; import * as SREM from '../commands/SREM'; @@ -483,6 +484,8 @@ export default { sort: SORT, SPOP, sPop: SPOP, + SPUBLISH, + sPublish: SPUBLISH, SRANDMEMBER_COUNT, sRandMemberCount: SRANDMEMBER_COUNT, SRANDMEMBER, diff --git a/packages/client/lib/cluster/index.spec.ts b/packages/client/lib/cluster/index.spec.ts index a2981d824e3..6b9d35f75fb 100644 --- a/packages/client/lib/cluster/index.spec.ts +++ b/packages/client/lib/cluster/index.spec.ts @@ -1,25 +1,29 @@ import { strict as assert } from 'assert'; -import testUtils, { GLOBAL } from '../test-utils'; +import testUtils, { GLOBAL, waitTillBeenCalled } from '../test-utils'; import RedisCluster from '.'; import { ClusterSlotStates } from '../commands/CLUSTER_SETSLOT'; import { SQUARE_SCRIPT } from '../client/index.spec'; import { RootNodesUnavailableError } from '../errors'; - -// We need to use 'require', because it's not possible with Typescript to import -// function that are exported as 'module.exports = function`, without esModuleInterop -// set to true. -const calculateSlot = require('cluster-key-slot'); +import { spy } from 'sinon'; +import { promiseTimeout } from '../utils'; +import RedisClient from '../client'; describe('Cluster', () => { testUtils.testWithCluster('sendCommand', async cluster => { - await cluster.publish('channel', 'message'); - await cluster.set('a', 'b'); - await cluster.set('a{a}', 'bb'); - await cluster.set('aa', 'bb'); - await cluster.get('aa'); - await cluster.get('aa'); - await cluster.get('aa'); - await cluster.get('aa'); + assert.equal( + await cluster.sendCommand(undefined, true, ['PING']), + 'PONG' + ); + }, GLOBAL.CLUSTERS.OPEN); + + testUtils.testWithCluster('isOpen', async cluster => { + assert.equal(cluster.isOpen, true); + await cluster.disconnect(); + assert.equal(cluster.isOpen, false); + }, GLOBAL.CLUSTERS.OPEN); + + testUtils.testWithCluster('connect should throw if already connected', async cluster => { + await assert.rejects(cluster.connect()); }, GLOBAL.CLUSTERS.OPEN); testUtils.testWithCluster('multi', async cluster => { @@ -64,54 +68,279 @@ describe('Cluster', () => { }); testUtils.testWithCluster('should handle live resharding', async cluster => { - const key = 'key', + const slot = 12539, + key = 'key', value = 'value'; await cluster.set(key, value); - const slot = calculateSlot(key), - source = cluster.getSlotMaster(slot), - destination = cluster.getMasters().find(node => node.id !== source.id)!; + const importing = cluster.slots[0].master, + migrating = cluster.slots[slot].master, + [ importingClient, migratingClient ] = await Promise.all([ + cluster.nodeClient(importing), + cluster.nodeClient(migrating) + ]); await Promise.all([ - source.client.clusterSetSlot(slot, ClusterSlotStates.MIGRATING, destination.id), - destination.client.clusterSetSlot(slot, ClusterSlotStates.IMPORTING, destination.id) + importingClient.clusterSetSlot(slot, ClusterSlotStates.IMPORTING, migrating.id), + migratingClient.clusterSetSlot(slot, ClusterSlotStates.MIGRATING, importing.id) ]); - // should be able to get the key from the source node using "ASKING" + // should be able to get the key from the migrating node assert.equal( await cluster.get(key), value ); - await Promise.all([ - source.client.migrate( - '127.0.0.1', - (destination.client.options).socket.port, - key, - 0, - 10 - ) - ]); + await migratingClient.migrate( + importing.host, + importing.port, + key, + 0, + 10 + ); - // should be able to get the key from the destination node using the "ASKING" command + // should be able to get the key from the importing node using `ASKING` assert.equal( await cluster.get(key), value ); - await Promise.all( - cluster.getMasters().map(({ client }) => { - return client.clusterSetSlot(slot, ClusterSlotStates.NODE, destination.id); - }) - ); + await Promise.all([ + importingClient.clusterSetSlot(slot, ClusterSlotStates.NODE, importing.id), + migratingClient.clusterSetSlot(slot, ClusterSlotStates.NODE, importing.id), + ]); - // should handle "MOVED" errors + // should handle `MOVED` errors assert.equal( await cluster.get(key), value ); }, { serverArguments: [], - numberOfNodes: 2 + numberOfMasters: 2 + }); + + testUtils.testWithCluster('getRandomNode should spread the the load evenly', async cluster => { + const totalNodes = cluster.masters.length + cluster.replicas.length, + ids = new Set(); + for (let i = 0; i < totalNodes; i++) { + ids.add(cluster.getRandomNode().id); + } + + assert.equal(ids.size, totalNodes); + }, GLOBAL.CLUSTERS.WITH_REPLICAS); + + testUtils.testWithCluster('getSlotRandomNode should spread the the load evenly', async cluster => { + const totalNodes = 1 + cluster.slots[0].replicas!.length, + ids = new Set(); + for (let i = 0; i < totalNodes; i++) { + ids.add(cluster.getSlotRandomNode(0).id); + } + + assert.equal(ids.size, totalNodes); + }, GLOBAL.CLUSTERS.WITH_REPLICAS); + + testUtils.testWithCluster('cluster topology', async cluster => { + assert.equal(cluster.slots.length, 16384); + const { numberOfMasters, numberOfReplicas } = GLOBAL.CLUSTERS.WITH_REPLICAS; + assert.equal(cluster.shards.length, numberOfMasters); + assert.equal(cluster.masters.length, numberOfMasters); + assert.equal(cluster.replicas.length, numberOfReplicas * numberOfMasters); + assert.equal(cluster.nodeByAddress.size, numberOfMasters + numberOfMasters * numberOfReplicas); + }, GLOBAL.CLUSTERS.WITH_REPLICAS); + + testUtils.testWithCluster('getMasters should be backwards competiable (without `minimizeConnections`)', async cluster => { + const masters = cluster.getMasters(); + assert.ok(Array.isArray(masters)); + for (const master of masters) { + assert.equal(typeof master.id, 'string'); + assert.ok(master.client instanceof RedisClient); + } + }, { + ...GLOBAL.CLUSTERS.OPEN, + clusterConfiguration: { + minimizeConnections: undefined // reset to default + } + }); + + testUtils.testWithCluster('getSlotMaster should be backwards competiable (without `minimizeConnections`)', async cluster => { + const master = cluster.getSlotMaster(0); + assert.equal(typeof master.id, 'string'); + assert.ok(master.client instanceof RedisClient); + }, { + ...GLOBAL.CLUSTERS.OPEN, + clusterConfiguration: { + minimizeConnections: undefined // reset to default + } + }); + + testUtils.testWithCluster('should throw CROSSSLOT error', async cluster => { + await assert.rejects(cluster.mGet(['a', 'b'])); + }, GLOBAL.CLUSTERS.OPEN); + + describe('minimizeConnections', () => { + testUtils.testWithCluster('false', async cluster => { + for (const master of cluster.masters) { + assert.ok(master.client instanceof RedisClient); + } + }, { + ...GLOBAL.CLUSTERS.OPEN, + clusterConfiguration: { + minimizeConnections: false + } + }); + + testUtils.testWithCluster('true', async cluster => { + for (const master of cluster.masters) { + assert.equal(master.client, undefined); + } + }, { + ...GLOBAL.CLUSTERS.OPEN, + clusterConfiguration: { + minimizeConnections: true + } + }); + }); + + describe('PubSub', () => { + testUtils.testWithCluster('subscribe & unsubscribe', async cluster => { + const listener = spy(); + + await cluster.subscribe('channel', listener); + + await Promise.all([ + waitTillBeenCalled(listener), + cluster.publish('channel', 'message') + ]); + + assert.ok(listener.calledOnceWithExactly('message', 'channel')); + + await cluster.unsubscribe('channel', listener); + + assert.equal(cluster.pubSubNode, undefined); + }, GLOBAL.CLUSTERS.OPEN); + + testUtils.testWithCluster('psubscribe & punsubscribe', async cluster => { + const listener = spy(); + + await cluster.pSubscribe('channe*', listener); + + await Promise.all([ + waitTillBeenCalled(listener), + cluster.publish('channel', 'message') + ]); + + assert.ok(listener.calledOnceWithExactly('message', 'channel')); + + await cluster.pUnsubscribe('channe*', listener); + + assert.equal(cluster.pubSubNode, undefined); + }, GLOBAL.CLUSTERS.OPEN); + + testUtils.testWithCluster('should move listeners when PubSub node disconnects from the cluster', async cluster => { + const listener = spy(); + await cluster.subscribe('channel', listener); + + assert.ok(cluster.pubSubNode); + const [ migrating, importing ] = cluster.masters[0].address === cluster.pubSubNode.address ? + cluster.masters : + [cluster.masters[1], cluster.masters[0]], + [ migratingClient, importingClient ] = await Promise.all([ + cluster.nodeClient(migrating), + cluster.nodeClient(importing) + ]); + + const range = cluster.slots[0].master === migrating ? { + key: 'bar', // 5061 + start: 0, + end: 8191 + } : { + key: 'foo', // 12182 + start: 8192, + end: 16383 + }; + + await Promise.all([ + migratingClient.clusterDelSlotsRange(range), + importingClient.clusterDelSlotsRange(range), + importingClient.clusterAddSlotsRange(range) + ]); + + // wait for migrating node to be notified about the new topology + while ((await migratingClient.clusterInfo()).state !== 'ok') { + await promiseTimeout(50); + } + + // make sure to cause `MOVED` error + await cluster.get(range.key); + + await Promise.all([ + cluster.publish('channel', 'message'), + waitTillBeenCalled(listener) + ]); + + assert.ok(listener.calledOnceWithExactly('message', 'channel')); + }, { + serverArguments: [], + numberOfMasters: 2, + minimumDockerVersion: [7] + }); + + testUtils.testWithCluster('ssubscribe & sunsubscribe', async cluster => { + const listener = spy(); + + await cluster.sSubscribe('channel', listener); + + await Promise.all([ + waitTillBeenCalled(listener), + cluster.sPublish('channel', 'message') + ]); + + assert.ok(listener.calledOnceWithExactly('message', 'channel')); + + await cluster.sUnsubscribe('channel', listener); + + // 10328 is the slot of `channel` + assert.equal(cluster.slots[10328].master.pubSubClient, undefined); + }, { + ...GLOBAL.CLUSTERS.OPEN, + minimumDockerVersion: [7] + }); + + testUtils.testWithCluster('should handle sharded-channel-moved events', async cluster => { + const SLOT = 10328, + migrating = cluster.slots[SLOT].master, + importing = cluster.masters.find(master => master !== migrating)!, + [ migratingClient, importingClient ] = await Promise.all([ + cluster.nodeClient(migrating), + cluster.nodeClient(importing) + ]); + + await Promise.all([ + migratingClient.clusterDelSlots(SLOT), + importingClient.clusterDelSlots(SLOT), + importingClient.clusterAddSlots(SLOT) + ]); + + // wait for migrating node to be notified about the new topology + while ((await migratingClient.clusterInfo()).state !== 'ok') { + await promiseTimeout(50); + } + + const listener = spy(); + + // will trigger `MOVED` error + await cluster.sSubscribe('channel', listener); + + await Promise.all([ + waitTillBeenCalled(listener), + cluster.sPublish('channel', 'message') + ]); + + assert.ok(listener.calledOnceWithExactly('message', 'channel')); + }, { + serverArguments: [], + minimumDockerVersion: [7] + }); }); }); diff --git a/packages/client/lib/cluster/index.ts b/packages/client/lib/cluster/index.ts index 6eafdda86ce..818930c8c82 100644 --- a/packages/client/lib/cluster/index.ts +++ b/packages/client/lib/cluster/index.ts @@ -1,11 +1,13 @@ import COMMANDS from './commands'; import { RedisCommand, RedisCommandArgument, RedisCommandArguments, RedisCommandRawReply, RedisCommandReply, RedisFunctions, RedisModules, RedisExtensions, RedisScript, RedisScripts, RedisCommandSignature, RedisFunction } from '../commands'; import { ClientCommandOptions, RedisClientOptions, RedisClientType, WithFunctions, WithModules, WithScripts } from '../client'; -import RedisClusterSlots, { ClusterNode, NodeAddressMap } from './cluster-slots'; +import RedisClusterSlots, { NodeAddressMap, ShardNode } from './cluster-slots'; import { attachExtensions, transformCommandReply, attachCommands, transformCommandArguments } from '../commander'; import { EventEmitter } from 'events'; import RedisClusterMultiCommand, { InstantiableRedisClusterMultiCommandType, RedisClusterMultiCommandType } from './multi-command'; import { RedisMultiQueuedCommand } from '../multi-command'; +import { PubSubListener } from '../client/pub-sub'; +import { ErrorReply } from '../errors'; export type RedisClusterClientOptions = Omit< RedisClientOptions, @@ -17,10 +19,34 @@ export interface RedisClusterOptions< F extends RedisFunctions = Record, S extends RedisScripts = Record > extends RedisExtensions { + /** + * Should contain details for some of the cluster nodes that the client will use to discover + * the "cluster topology". We recommend including details for at least 3 nodes here. + */ rootNodes: Array; + /** + * Default values used for every client in the cluster. Use this to specify global values, + * for example: ACL credentials, timeouts, TLS configuration etc. + */ defaults?: Partial; + /** + * When `true`, `.connect()` will only discover the cluster topology, without actually connecting to all the nodes. + * Useful for short-term or PubSub-only connections. + */ + minimizeConnections?: boolean; + /** + * When `true`, distribute load by executing readonly commands (such as `GET`, `GEOSEARCH`, etc.) across all cluster nodes. When `false`, only use master nodes. + */ useReplicas?: boolean; + /** + * The maximum number of times a command will be redirected due to `MOVED` or `ASK` errors. + */ maxCommandRedirections?: number; + /** + * Mapping between the addresses in the cluster (see `CLUSTER SHARDS`) and the addresses the client should connect to + * Useful when the cluster is running on another network + * + */ nodeAddressMap?: NodeAddressMap; } @@ -70,14 +96,44 @@ export default class RedisCluster< } readonly #options: RedisClusterOptions; + readonly #slots: RedisClusterSlots; + + get slots() { + return this.#slots.slots; + } + + get shards() { + return this.#slots.shards; + } + + get masters() { + return this.#slots.masters; + } + + get replicas() { + return this.#slots.replicas; + } + + get nodeByAddress() { + return this.#slots.nodeByAddress; + } + + get pubSubNode() { + return this.#slots.pubSubNode; + } + readonly #Multi: InstantiableRedisClusterMultiCommandType; + get isOpen() { + return this.#slots.isOpen; + } + constructor(options: RedisClusterOptions) { super(); this.#options = options; - this.#slots = new RedisClusterSlots(options, err => this.emit('error', err)); + this.#slots = new RedisClusterSlots(options, this.emit.bind(this)); this.#Multi = RedisClusterMultiCommand.extend(options); } @@ -88,7 +144,7 @@ export default class RedisCluster< }); } - async connect(): Promise { + connect() { return this.#slots.connect(); } @@ -188,34 +244,33 @@ export default class RedisCluster< executor: (client: RedisClientType) => Promise ): Promise { const maxCommandRedirections = this.#options.maxCommandRedirections ?? 16; - let client = this.#slots.getClient(firstKey, isReadonly); + let client = await this.#slots.getClient(firstKey, isReadonly); for (let i = 0;; i++) { try { return await executor(client); } catch (err) { - if (++i > maxCommandRedirections || !(err instanceof Error)) { + if (++i > maxCommandRedirections || !(err instanceof ErrorReply)) { throw err; } if (err.message.startsWith('ASK')) { const address = err.message.substring(err.message.lastIndexOf(' ') + 1); - if (this.#slots.getNodeByAddress(address)?.client === client) { - await client.asking(); - continue; + let redirectTo = await this.#slots.getMasterByAddress(address); + if (!redirectTo) { + await this.#slots.rediscover(client); + redirectTo = await this.#slots.getMasterByAddress(address); } - await this.#slots.rediscover(client); - const redirectTo = this.#slots.getNodeByAddress(address); if (!redirectTo) { throw new Error(`Cannot find node ${address}`); } - await redirectTo.client.asking(); - client = redirectTo.client; + await redirectTo.asking(); + client = redirectTo; continue; } else if (err.message.startsWith('MOVED')) { await this.#slots.rediscover(client); - client = this.#slots.getClient(firstKey, isReadonly); + client = await this.#slots.getClient(firstKey, isReadonly); continue; } @@ -239,14 +294,94 @@ export default class RedisCluster< multi = this.MULTI; - getMasters(): Array> { - return this.#slots.getMasters(); + async SUBSCRIBE( + channels: string | Array, + listener: PubSubListener, + bufferMode?: T + ) { + return (await this.#slots.getPubSubClient()) + .SUBSCRIBE(channels, listener, bufferMode); + } + + subscribe = this.SUBSCRIBE; + + async UNSUBSCRIBE( + channels?: string | Array, + listener?: PubSubListener, + bufferMode?: T + ) { + return this.#slots.executeUnsubscribeCommand(client => + client.UNSUBSCRIBE(channels, listener, bufferMode) + ); + } + + unsubscribe = this.UNSUBSCRIBE; + + async PSUBSCRIBE( + patterns: string | Array, + listener: PubSubListener, + bufferMode?: T + ) { + return (await this.#slots.getPubSubClient()) + .PSUBSCRIBE(patterns, listener, bufferMode); } - getSlotMaster(slot: number): ClusterNode { - return this.#slots.getSlotMaster(slot); + pSubscribe = this.PSUBSCRIBE; + + async PUNSUBSCRIBE( + patterns?: string | Array, + listener?: PubSubListener, + bufferMode?: T + ) { + return this.#slots.executeUnsubscribeCommand(client => + client.PUNSUBSCRIBE(patterns, listener, bufferMode) + ); } + pUnsubscribe = this.PUNSUBSCRIBE; + + async SSUBSCRIBE( + channels: string | Array, + listener: PubSubListener, + bufferMode?: T + ) { + const maxCommandRedirections = this.#options.maxCommandRedirections ?? 16, + firstChannel = Array.isArray(channels) ? channels[0] : channels; + let client = await this.#slots.getShardedPubSubClient(firstChannel); + for (let i = 0;; i++) { + try { + return await client.SSUBSCRIBE(channels, listener, bufferMode); + } catch (err) { + if (++i > maxCommandRedirections || !(err instanceof ErrorReply)) { + throw err; + } + + if (err.message.startsWith('MOVED')) { + await this.#slots.rediscover(client); + client = await this.#slots.getShardedPubSubClient(firstChannel); + continue; + } + + throw err; + } + } + } + + sSubscribe = this.SSUBSCRIBE; + + SUNSUBSCRIBE( + channels: string | Array, + listener: PubSubListener, + bufferMode?: T + ) { + return this.#slots.executeShardedUnsubscribeCommand( + Array.isArray(channels) ? channels[0] : channels, + client => client.SUNSUBSCRIBE(channels, listener, bufferMode) + ); + } + + sUnsubscribe = this.SUNSUBSCRIBE; + quit(): Promise { return this.#slots.quit(); } @@ -254,6 +389,32 @@ export default class RedisCluster< disconnect(): Promise { return this.#slots.disconnect(); } + + nodeClient(node: ShardNode) { + return this.#slots.nodeClient(node); + } + + getRandomNode() { + return this.#slots.getRandomNode(); + } + + getSlotRandomNode(slot: number) { + return this.#slots.getSlotRandomNode(slot); + } + + /** + * @deprecated use `.masters` instead + */ + getMasters() { + return this.masters; + } + + /** + * @deprecated use `.slots[]` instead + */ + getSlotMaster(slot: number) { + return this.slots[slot].master; + } } attachCommands({ diff --git a/packages/client/lib/commands/CLUSTER_BUMPEPOCH.spec.ts b/packages/client/lib/commands/CLUSTER_BUMPEPOCH.spec.ts index f9d2f5437b2..edb68b3b3b0 100644 --- a/packages/client/lib/commands/CLUSTER_BUMPEPOCH.spec.ts +++ b/packages/client/lib/commands/CLUSTER_BUMPEPOCH.spec.ts @@ -11,8 +11,9 @@ describe('CLUSTER BUMPEPOCH', () => { }); testUtils.testWithCluster('clusterNode.clusterBumpEpoch', async cluster => { + const client = await cluster.nodeClient(cluster.masters[0]); assert.equal( - typeof await cluster.getSlotMaster(0).client.clusterBumpEpoch(), + typeof await client.clusterBumpEpoch(), 'string' ); }, GLOBAL.SERVERS.OPEN); diff --git a/packages/client/lib/commands/CLUSTER_COUNT-FAILURE-REPORTS.spec.ts b/packages/client/lib/commands/CLUSTER_COUNT-FAILURE-REPORTS.spec.ts index d84687631bc..558110d0a28 100644 --- a/packages/client/lib/commands/CLUSTER_COUNT-FAILURE-REPORTS.spec.ts +++ b/packages/client/lib/commands/CLUSTER_COUNT-FAILURE-REPORTS.spec.ts @@ -11,7 +11,7 @@ describe('CLUSTER COUNT-FAILURE-REPORTS', () => { }); testUtils.testWithCluster('clusterNode.clusterCountFailureReports', async cluster => { - const { client } = cluster.getSlotMaster(0); + const client = await cluster.nodeClient(cluster.masters[0]); assert.equal( typeof await client.clusterCountFailureReports( await client.clusterMyId() diff --git a/packages/client/lib/commands/CLUSTER_COUNTKEYSINSLOT.spec.ts b/packages/client/lib/commands/CLUSTER_COUNTKEYSINSLOT.spec.ts index ecaed428cb7..27ecbcfffa3 100644 --- a/packages/client/lib/commands/CLUSTER_COUNTKEYSINSLOT.spec.ts +++ b/packages/client/lib/commands/CLUSTER_COUNTKEYSINSLOT.spec.ts @@ -11,8 +11,9 @@ describe('CLUSTER COUNTKEYSINSLOT', () => { }); testUtils.testWithCluster('clusterNode.clusterCountKeysInSlot', async cluster => { + const client = await cluster.nodeClient(cluster.masters[0]); assert.equal( - typeof await cluster.getSlotMaster(0).client.clusterCountKeysInSlot(0), + typeof await client.clusterCountKeysInSlot(0), 'number' ); }, GLOBAL.CLUSTERS.OPEN); diff --git a/packages/client/lib/commands/CLUSTER_GETKEYSINSLOT.spec.ts b/packages/client/lib/commands/CLUSTER_GETKEYSINSLOT.spec.ts index 7c156341301..957b7de20cb 100644 --- a/packages/client/lib/commands/CLUSTER_GETKEYSINSLOT.spec.ts +++ b/packages/client/lib/commands/CLUSTER_GETKEYSINSLOT.spec.ts @@ -11,7 +11,8 @@ describe('CLUSTER GETKEYSINSLOT', () => { }); testUtils.testWithCluster('clusterNode.clusterGetKeysInSlot', async cluster => { - const reply = await cluster.getSlotMaster(0).client.clusterGetKeysInSlot(0, 1); + const client = await cluster.nodeClient(cluster.masters[0]), + reply = await client.clusterGetKeysInSlot(0, 1); assert.ok(Array.isArray(reply)); for (const item of reply) { assert.equal(typeof item, 'string'); diff --git a/packages/client/lib/commands/CLUSTER_INFO.spec.ts b/packages/client/lib/commands/CLUSTER_INFO.spec.ts index b770ed33616..69d5c4a8c56 100644 --- a/packages/client/lib/commands/CLUSTER_INFO.spec.ts +++ b/packages/client/lib/commands/CLUSTER_INFO.spec.ts @@ -46,8 +46,9 @@ describe('CLUSTER INFO', () => { }); testUtils.testWithCluster('clusterNode.clusterInfo', async cluster => { + const client = await cluster.nodeClient(cluster.masters[0]); assert.notEqual( - await cluster.getSlotMaster(0).client.clusterInfo(), + await client.clusterInfo(), null ); }, GLOBAL.CLUSTERS.OPEN); diff --git a/packages/client/lib/commands/CLUSTER_KEYSLOT.spec.ts b/packages/client/lib/commands/CLUSTER_KEYSLOT.spec.ts index a7a5ab9472f..3bbc9f9cb2d 100644 --- a/packages/client/lib/commands/CLUSTER_KEYSLOT.spec.ts +++ b/packages/client/lib/commands/CLUSTER_KEYSLOT.spec.ts @@ -11,8 +11,9 @@ describe('CLUSTER KEYSLOT', () => { }); testUtils.testWithCluster('clusterNode.clusterKeySlot', async cluster => { + const client = await cluster.nodeClient(cluster.masters[0]); assert.equal( - typeof await cluster.getSlotMaster(0).client.clusterKeySlot('key'), + typeof await client.clusterKeySlot('key'), 'number' ); }, GLOBAL.CLUSTERS.OPEN); diff --git a/packages/client/lib/commands/CLUSTER_LINKS.spec.ts b/packages/client/lib/commands/CLUSTER_LINKS.spec.ts index a8b1663c3af..982973e8ea5 100644 --- a/packages/client/lib/commands/CLUSTER_LINKS.spec.ts +++ b/packages/client/lib/commands/CLUSTER_LINKS.spec.ts @@ -13,7 +13,8 @@ describe('CLUSTER LINKS', () => { }); testUtils.testWithCluster('clusterNode.clusterLinks', async cluster => { - const links = await cluster.getSlotMaster(0).client.clusterLinks(); + const client = await cluster.nodeClient(cluster.masters[0]), + links = await client.clusterLinks(); assert.ok(Array.isArray(links)); for (const link of links) { assert.equal(typeof link.direction, 'string'); diff --git a/packages/client/lib/commands/CLUSTER_MYID.spec.ts b/packages/client/lib/commands/CLUSTER_MYID.spec.ts index 7781c374526..f427d7058e2 100644 --- a/packages/client/lib/commands/CLUSTER_MYID.spec.ts +++ b/packages/client/lib/commands/CLUSTER_MYID.spec.ts @@ -11,9 +11,11 @@ describe('CLUSTER MYID', () => { }); testUtils.testWithCluster('clusterNode.clusterMyId', async cluster => { + const [master] = cluster.masters, + client = await cluster.nodeClient(master); assert.equal( - typeof await cluster.getSlotMaster(0).client.clusterMyId(), - 'string' + await client.clusterMyId(), + master.id ); }, GLOBAL.CLUSTERS.OPEN); }); diff --git a/packages/client/lib/commands/CLUSTER_SAVECONFIG.spec.ts b/packages/client/lib/commands/CLUSTER_SAVECONFIG.spec.ts index bcdccd90919..81ba4aa2509 100644 --- a/packages/client/lib/commands/CLUSTER_SAVECONFIG.spec.ts +++ b/packages/client/lib/commands/CLUSTER_SAVECONFIG.spec.ts @@ -11,8 +11,9 @@ describe('CLUSTER SAVECONFIG', () => { }); testUtils.testWithCluster('clusterNode.clusterSaveConfig', async cluster => { + const client = await cluster.nodeClient(cluster.masters[0]); assert.equal( - await cluster.getSlotMaster(0).client.clusterSaveConfig(), + await client.clusterSaveConfig(), 'OK' ); }, GLOBAL.CLUSTERS.OPEN); diff --git a/packages/client/lib/commands/CLUSTER_SLOTS.ts b/packages/client/lib/commands/CLUSTER_SLOTS.ts index 7e1f5dcc964..20d9782dd9e 100644 --- a/packages/client/lib/commands/CLUSTER_SLOTS.ts +++ b/packages/client/lib/commands/CLUSTER_SLOTS.ts @@ -13,7 +13,7 @@ type ClusterSlotsRawReply = Array<[ ...replicas: Array ]>; -type ClusterSlotsNode = { +export interface ClusterSlotsNode { ip: string; port: number; id: string; diff --git a/packages/client/lib/commands/PING.spec.ts b/packages/client/lib/commands/PING.spec.ts index fae349176d7..06cbae43a13 100644 --- a/packages/client/lib/commands/PING.spec.ts +++ b/packages/client/lib/commands/PING.spec.ts @@ -1,8 +1,24 @@ import { strict as assert } from 'assert'; import testUtils, { GLOBAL } from '../test-utils'; -import RedisClient from '../client'; +import { transformArguments } from './PING'; describe('PING', () => { + describe('transformArguments', () => { + it('default', () => { + assert.deepEqual( + transformArguments(), + ['PING'] + ); + }); + + it('with message', () => { + assert.deepEqual( + transformArguments('message'), + ['PING', 'message'] + ); + }); + }); + describe('client.ping', () => { testUtils.testWithClient('string', async client => { assert.equal( @@ -13,7 +29,7 @@ describe('PING', () => { testUtils.testWithClient('buffer', async client => { assert.deepEqual( - await client.ping(RedisClient.commandOptions({ returnBuffers: true })), + await client.ping(client.commandOptions({ returnBuffers: true })), Buffer.from('PONG') ); }, GLOBAL.SERVERS.OPEN); diff --git a/packages/client/lib/commands/PING.ts b/packages/client/lib/commands/PING.ts index 10ab01f7bdf..95fa006122d 100644 --- a/packages/client/lib/commands/PING.ts +++ b/packages/client/lib/commands/PING.ts @@ -1,7 +1,12 @@ -import { RedisCommandArgument } from '.'; +import { RedisCommandArgument, RedisCommandArguments } from '.'; -export function transformArguments(): Array { - return ['PING']; +export function transformArguments(message?: RedisCommandArgument): RedisCommandArguments { + const args: RedisCommandArguments = ['PING']; + if (message) { + args.push(message); + } + + return args; } export declare function transformReply(): RedisCommandArgument; diff --git a/packages/client/lib/commands/PUBLISH.ts b/packages/client/lib/commands/PUBLISH.ts index 93a8016900e..7862a0936cb 100644 --- a/packages/client/lib/commands/PUBLISH.ts +++ b/packages/client/lib/commands/PUBLISH.ts @@ -1,5 +1,7 @@ import { RedisCommandArgument, RedisCommandArguments } from '.'; +export const IS_READ_ONLY = true; + export function transformArguments( channel: RedisCommandArgument, message: RedisCommandArgument diff --git a/packages/client/lib/commands/PUBSUB_SHARDCHANNELS.spec.ts b/packages/client/lib/commands/PUBSUB_SHARDCHANNELS.spec.ts new file mode 100644 index 00000000000..1e5f2292b39 --- /dev/null +++ b/packages/client/lib/commands/PUBSUB_SHARDCHANNELS.spec.ts @@ -0,0 +1,30 @@ +import { strict as assert } from 'assert'; +import testUtils, { GLOBAL } from '../test-utils'; +import { transformArguments } from './PUBSUB_SHARDCHANNELS'; + +describe('PUBSUB SHARDCHANNELS', () => { + testUtils.isVersionGreaterThanHook([7]); + + describe('transformArguments', () => { + it('without pattern', () => { + assert.deepEqual( + transformArguments(), + ['PUBSUB', 'SHARDCHANNELS'] + ); + }); + + it('with pattern', () => { + assert.deepEqual( + transformArguments('patter*'), + ['PUBSUB', 'SHARDCHANNELS', 'patter*'] + ); + }); + }); + + testUtils.testWithClient('client.pubSubShardChannels', async client => { + assert.deepEqual( + await client.pubSubShardChannels(), + [] + ); + }, GLOBAL.SERVERS.OPEN); +}); diff --git a/packages/client/lib/commands/PUBSUB_SHARDCHANNELS.ts b/packages/client/lib/commands/PUBSUB_SHARDCHANNELS.ts new file mode 100644 index 00000000000..e998677848a --- /dev/null +++ b/packages/client/lib/commands/PUBSUB_SHARDCHANNELS.ts @@ -0,0 +1,13 @@ +import { RedisCommandArgument, RedisCommandArguments } from '.'; + +export const IS_READ_ONLY = true; + +export function transformArguments( + pattern?: RedisCommandArgument +): RedisCommandArguments { + const args: RedisCommandArguments = ['PUBSUB', 'SHARDCHANNELS']; + if (pattern) args.push(pattern); + return args; +} + +export declare function transformReply(): Array; diff --git a/packages/client/lib/commands/SPUBLISH.spec.ts b/packages/client/lib/commands/SPUBLISH.spec.ts new file mode 100644 index 00000000000..60b6ce2dad0 --- /dev/null +++ b/packages/client/lib/commands/SPUBLISH.spec.ts @@ -0,0 +1,21 @@ +import { strict as assert } from 'assert'; +import testUtils, { GLOBAL } from '../test-utils'; +import { transformArguments } from './SPUBLISH'; + +describe('SPUBLISH', () => { + testUtils.isVersionGreaterThanHook([7]); + + it('transformArguments', () => { + assert.deepEqual( + transformArguments('channel', 'message'), + ['SPUBLISH', 'channel', 'message'] + ); + }); + + testUtils.testWithClient('client.sPublish', async client => { + assert.equal( + await client.sPublish('channel', 'message'), + 0 + ); + }, GLOBAL.SERVERS.OPEN); +}); diff --git a/packages/client/lib/commands/SPUBLISH.ts b/packages/client/lib/commands/SPUBLISH.ts new file mode 100644 index 00000000000..42a7ab49072 --- /dev/null +++ b/packages/client/lib/commands/SPUBLISH.ts @@ -0,0 +1,14 @@ +import { RedisCommandArgument, RedisCommandArguments } from '.'; + +export const IS_READ_ONLY = true; + +export const FIRST_KEY_INDEX = 1; + +export function transformArguments( + channel: RedisCommandArgument, + message: RedisCommandArgument +): RedisCommandArguments { + return ['SPUBLISH', channel, message]; +} + +export declare function transformReply(): number; diff --git a/packages/client/lib/commands/generic-transformers.ts b/packages/client/lib/commands/generic-transformers.ts index d3a57a9346b..5048de9399a 100644 --- a/packages/client/lib/commands/generic-transformers.ts +++ b/packages/client/lib/commands/generic-transformers.ts @@ -137,7 +137,6 @@ export function transformSortedSetMemberNullReply( export function transformSortedSetMemberReply( reply: [RedisCommandArgument, RedisCommandArgument] ): ZMember { - return { value: reply[0], score: transformNumberInfinityReply(reply[1]) diff --git a/packages/client/lib/test-utils.ts b/packages/client/lib/test-utils.ts index d2e33b4abf3..9032f1dd344 100644 --- a/packages/client/lib/test-utils.ts +++ b/packages/client/lib/test-utils.ts @@ -3,7 +3,6 @@ import { SinonSpy } from 'sinon'; import { promiseTimeout } from './utils'; export default new TestUtils({ - defaultDockerVersion: '7.0.2', dockerImageName: 'redis', dockerImageVersionArgument: 'redis-version' }); @@ -31,6 +30,14 @@ export const GLOBAL = { password: 'password' } } + }, + WITH_REPLICAS: { + serverArguments: [], + numberOfMasters: 2, + numberOfReplicas: 1, + clusterConfiguration: { + useReplicas: true + } } } }; diff --git a/packages/client/package.json b/packages/client/package.json index 43e70381361..42a512b644c 100644 --- a/packages/client/package.json +++ b/packages/client/package.json @@ -1,6 +1,6 @@ { "name": "@redis/client", - "version": "1.4.2", + "version": "1.5.0", "license": "MIT", "main": "./dist/index.js", "types": "./dist/index.d.ts", diff --git a/packages/graph/lib/test-utils.ts b/packages/graph/lib/test-utils.ts index 4ae0e0a2695..56c0af56a2e 100644 --- a/packages/graph/lib/test-utils.ts +++ b/packages/graph/lib/test-utils.ts @@ -3,8 +3,7 @@ import RedisGraph from '.'; export default new TestUtils({ dockerImageName: 'redislabs/redisgraph', - dockerImageVersionArgument: 'redisgraph-version', - defaultDockerVersion: '2.8.15' + dockerImageVersionArgument: 'redisgraph-version' }); export const GLOBAL = { diff --git a/packages/json/lib/test-utils.ts b/packages/json/lib/test-utils.ts index fa150e4b7db..f4c4e4eb201 100644 --- a/packages/json/lib/test-utils.ts +++ b/packages/json/lib/test-utils.ts @@ -3,8 +3,7 @@ import RedisJSON from '.'; export default new TestUtils({ dockerImageName: 'redislabs/rejson', - dockerImageVersionArgument: 'rejson-version', - defaultDockerVersion: '2.0.9' + dockerImageVersionArgument: 'rejson-version' }); export const GLOBAL = { diff --git a/packages/test-utils/lib/dockers.ts b/packages/test-utils/lib/dockers.ts index 8f0be95b094..a7e1c610eee 100644 --- a/packages/test-utils/lib/dockers.ts +++ b/packages/test-utils/lib/dockers.ts @@ -1,8 +1,8 @@ import { createConnection } from 'net'; import { once } from 'events'; -import { RedisModules, RedisFunctions, RedisScripts } from '@redis/client/dist/lib/commands'; -import RedisClient, { RedisClientType } from '@redis/client/dist/lib/client'; +import RedisClient from '@redis/client/dist/lib/client'; import { promiseTimeout } from '@redis/client/dist/lib/utils'; +import { ClusterSlotsReply } from '@redis/client/dist/lib/commands/CLUSTER_SLOTS'; import * as path from 'path'; import { promisify } from 'util'; import { exec } from 'child_process'; @@ -64,7 +64,7 @@ async function spawnRedisServerDocker({ image, version }: RedisServerDockerConfi } while (await isPortAvailable(port)) { - await promiseTimeout(500); + await promiseTimeout(50); } return { @@ -102,17 +102,65 @@ after(() => { }); export interface RedisClusterDockersConfig extends RedisServerDockerConfig { - numberOfNodes?: number; + numberOfMasters?: number; + numberOfReplicas?: number; } -async function spawnRedisClusterNodeDocker( +async function spawnRedisClusterNodeDockers( dockersConfig: RedisClusterDockersConfig, serverArguments: Array, fromSlot: number, - toSlot: number, - waitForState: boolean, - meetPort?: number -): Promise { + toSlot: number +) { + const range: Array = []; + for (let i = fromSlot; i < toSlot; i++) { + range.push(i); + } + + const master = await spawnRedisClusterNodeDocker( + dockersConfig, + serverArguments + ); + + await master.client.clusterAddSlots(range); + + if (!dockersConfig.numberOfReplicas) return [master]; + + const replicasPromises: Array> = []; + for (let i = 0; i < (dockersConfig.numberOfReplicas ?? 0); i++) { + replicasPromises.push( + spawnRedisClusterNodeDocker(dockersConfig, [ + ...serverArguments, + '--cluster-enabled', + 'yes', + '--cluster-node-timeout', + '5000' + ]).then(async replica => { + await replica.client.clusterMeet('127.0.0.1', master.docker.port); + + while ((await replica.client.clusterSlots()).length === 0) { + await promiseTimeout(50); + } + + await replica.client.clusterReplicate( + await master.client.clusterMyId() + ); + + return replica; + }) + ); + } + + return [ + master, + ...await Promise.all(replicasPromises) + ]; +} + +async function spawnRedisClusterNodeDocker( + dockersConfig: RedisClusterDockersConfig, + serverArguments: Array +) { const docker = await spawnRedisServerDocker(dockersConfig, [ ...serverArguments, '--cluster-enabled', @@ -128,78 +176,64 @@ async function spawnRedisClusterNodeDocker( await client.connect(); - try { - const range = []; - for (let i = fromSlot; i < toSlot; i++) { - range.push(i); - } - - const promises: Array> = [client.clusterAddSlots(range)]; - - if (meetPort) { - promises.push(client.clusterMeet('127.0.0.1', meetPort)); - } - - if (waitForState) { - promises.push(waitForClusterState(client)); - } - - await Promise.all(promises); - - return docker; - } finally { - await client.disconnect(); - } -} - -async function waitForClusterState< - M extends RedisModules, - F extends RedisFunctions, - S extends RedisScripts ->(client: RedisClientType): Promise { - while ((await client.clusterInfo()).state !== 'ok') { - await promiseTimeout(500); - } + return { + docker, + client + }; } const SLOTS = 16384; -async function spawnRedisClusterDockers(dockersConfig: RedisClusterDockersConfig, serverArguments: Array): Promise> { - const numberOfNodes = dockersConfig.numberOfNodes ?? 3, - slotsPerNode = Math.floor(SLOTS / numberOfNodes), - dockers: Array = []; - for (let i = 0; i < numberOfNodes; i++) { +async function spawnRedisClusterDockers( + dockersConfig: RedisClusterDockersConfig, + serverArguments: Array +): Promise> { + const numberOfMasters = dockersConfig.numberOfMasters ?? 2, + slotsPerNode = Math.floor(SLOTS / numberOfMasters), + spawnPromises: Array> = []; + for (let i = 0; i < numberOfMasters; i++) { const fromSlot = i * slotsPerNode, - [ toSlot, waitForState ] = i === numberOfNodes - 1 ? [SLOTS, true] : [fromSlot + slotsPerNode, false]; - dockers.push( - await spawnRedisClusterNodeDocker( + toSlot = i === numberOfMasters - 1 ? SLOTS : fromSlot + slotsPerNode; + spawnPromises.push( + spawnRedisClusterNodeDockers( dockersConfig, serverArguments, fromSlot, - toSlot, - waitForState, - i === 0 ? undefined : dockers[i - 1].port + toSlot ) ); } - const client = RedisClient.create({ - socket: { - port: dockers[0].port - } - }); + const nodes = (await Promise.all(spawnPromises)).flat(), + meetPromises: Array> = []; + for (let i = 1; i < nodes.length; i++) { + meetPromises.push( + nodes[i].client.clusterMeet('127.0.0.1', nodes[0].docker.port) + ); + } - await client.connect(); + await Promise.all(meetPromises); - try { - while ((await client.clusterInfo()).state !== 'ok') { - await promiseTimeout(500); - } - } finally { - await client.disconnect(); + await Promise.all( + nodes.map(async ({ client }) => { + while (totalNodes(await client.clusterSlots()) !== nodes.length) { + await promiseTimeout(50); + } + + return client.disconnect(); + }) + ); + + return nodes.map(({ docker }) => docker); +} + +function totalNodes(slots: ClusterSlotsReply) { + let total = slots.length; + for (const slot of slots) { + total += slot.replicas.length; } - return dockers; + return total; } const RUNNING_CLUSTERS = new Map, ReturnType>(); diff --git a/packages/test-utils/lib/index.ts b/packages/test-utils/lib/index.ts index 7b9494c8d66..b9195c5717a 100644 --- a/packages/test-utils/lib/index.ts +++ b/packages/test-utils/lib/index.ts @@ -9,7 +9,7 @@ import { hideBin } from 'yargs/helpers'; interface TestUtilsConfig { dockerImageName: string; dockerImageVersionArgument: string; - defaultDockerVersion: string; + defaultDockerVersion?: string; } interface CommonTestOptions { @@ -33,7 +33,8 @@ interface ClusterTestOptions< > extends CommonTestOptions { serverArguments: Array; clusterConfiguration?: Partial>; - numberOfNodes?: number; + numberOfMasters?: number; + numberOfReplicas?: number; } interface Version { @@ -43,7 +44,7 @@ interface Version { export default class TestUtils { static #parseVersionNumber(version: string): Array { - if (version === 'edge') return [Infinity]; + if (version === 'latest' || version === 'edge') return [Infinity]; const dashIndex = version.indexOf('-'); return (dashIndex === -1 ? version : version.substring(0, dashIndex)) @@ -58,7 +59,7 @@ export default class TestUtils { }); } - static #getVersion(argumentName: string, defaultVersion: string): Version { + static #getVersion(argumentName: string, defaultVersion = 'latest'): Version { return yargs(hideBin(process.argv)) .option(argumentName, { type: 'string', @@ -163,9 +164,13 @@ export default class TestUtils { M extends RedisModules, F extends RedisFunctions, S extends RedisScripts - >(cluster: RedisClusterType): Promise { - await Promise.all( - cluster.getMasters().map(({ client }) => client.flushAll()) + >(cluster: RedisClusterType): Promise { + return Promise.all( + cluster.masters.map(async ({ client }) => { + if (client) { + await (await client).flushAll(); + } + }) ); } @@ -186,7 +191,8 @@ export default class TestUtils { dockersPromise = spawnRedisCluster({ ...dockerImage, - numberOfNodes: options?.numberOfNodes + numberOfMasters: options?.numberOfMasters, + numberOfReplicas: options?.numberOfReplicas }, options.serverArguments); return dockersPromise; }); @@ -197,15 +203,15 @@ export default class TestUtils { const dockers = await dockersPromise, cluster = RedisCluster.create({ - ...options.clusterConfiguration, rootNodes: dockers.map(({ port }) => ({ socket: { port } - })) + })), + minimizeConnections: true, + ...options.clusterConfiguration }); - await cluster.connect(); try { diff --git a/packages/time-series/lib/test-utils.ts b/packages/time-series/lib/test-utils.ts index da883906028..6d534ccccef 100644 --- a/packages/time-series/lib/test-utils.ts +++ b/packages/time-series/lib/test-utils.ts @@ -3,8 +3,7 @@ import TimeSeries from '.'; export default new TestUtils({ dockerImageName: 'redislabs/redistimeseries', - dockerImageVersionArgument: 'timeseries-version', - defaultDockerVersion: '1.8.0' + dockerImageVersionArgument: 'timeseries-version' }); export const GLOBAL = { From 0a46fcbdafd98f2f6b74a8885153372bc272acd9 Mon Sep 17 00:00:00 2001 From: Leibale Eidelman Date: Wed, 25 Jan 2023 13:05:42 -0500 Subject: [PATCH 1314/1748] upgrade dependencies (#2381) * upgrade deps * upgrade dependencies * fix v3 benchmark * update package-lock.json --- benchmark/package-lock.json | 182 +- benchmark/package.json | 4 +- package-lock.json | 5013 ++++++++++++++++------------- package.json | 6 +- packages/bloom/package.json | 8 +- packages/client/package.json | 18 +- packages/graph/package.json | 8 +- packages/json/package.json | 8 +- packages/search/package.json | 8 +- packages/test-utils/package.json | 12 +- packages/time-series/package.json | 8 +- 11 files changed, 2881 insertions(+), 2394 deletions(-) diff --git a/benchmark/package-lock.json b/benchmark/package-lock.json index 23781c0085c..828b42803eb 100644 --- a/benchmark/package-lock.json +++ b/benchmark/package-lock.json @@ -8,39 +8,9 @@ "dependencies": { "@redis/client": "../packages/client", "hdr-histogram-js": "3.0.0", - "ioredis": "5.2.2", + "ioredis": "5.3.0", "redis-v3": "npm:redis@3.1.2", - "yargs": "17.5.1" - } - }, - "../packages/client": { - "name": "@redis/client", - "version": "1.2.0", - "license": "MIT", - "dependencies": { - "cluster-key-slot": "1.1.0", - "generic-pool": "3.8.2", - "yallist": "4.0.0" - }, - "devDependencies": { - "@istanbuljs/nyc-config-typescript": "^1.0.2", - "@redis/test-utils": "*", - "@types/node": "^18.7.10", - "@types/sinon": "^10.0.13", - "@types/yallist": "^4.0.1", - "@typescript-eslint/eslint-plugin": "^5.34.0", - "@typescript-eslint/parser": "^5.34.0", - "eslint": "^8.22.0", - "nyc": "^15.1.0", - "release-it": "^15.3.0", - "sinon": "^14.0.0", - "source-map-support": "^0.5.21", - "ts-node": "^10.9.1", - "typedoc": "^0.23.10", - "typescript": "^4.7.4" - }, - "engines": { - "node": ">=14" + "yargs": "17.6.2" } }, "node_modules/@assemblyscript/loader": { @@ -54,8 +24,17 @@ "integrity": "sha512-fsR4P/ROllzf/7lXYyElUJCheWdTJVJvOTps8v9IWKFATxR61ANOlnoPqhH099xYLrJGpc2ZQ28B3rMeUt5VQg==" }, "node_modules/@redis/client": { - "resolved": "../packages/client", - "link": true + "version": "1.5.0", + "resolved": "file:../packages/client", + "license": "MIT", + "dependencies": { + "cluster-key-slot": "1.1.2", + "generic-pool": "3.9.0", + "yallist": "4.0.0" + }, + "engines": { + "node": ">=14" + } }, "node_modules/ansi-regex": { "version": "5.0.1", @@ -99,19 +78,22 @@ ] }, "node_modules/cliui": { - "version": "7.0.4", - "resolved": "https://registry.npmjs.org/cliui/-/cliui-7.0.4.tgz", - "integrity": "sha512-OcRE68cOsVMXp1Yvonl/fzkQOyjLSu/8bhPDfQt0e0/Eb283TKP20Fs2MqoPsr9SwA595rRCA+QMzYc9nBP+JQ==", + "version": "8.0.1", + "resolved": "https://registry.npmjs.org/cliui/-/cliui-8.0.1.tgz", + "integrity": "sha512-BSeNnyus75C4//NQ9gQt1/csTXyo/8Sb+afLAkzAptFuMsod9HFokGNudZpi/oQV73hnVK+sR+5PVRMd+Dr7YQ==", "dependencies": { "string-width": "^4.2.0", - "strip-ansi": "^6.0.0", + "strip-ansi": "^6.0.1", "wrap-ansi": "^7.0.0" + }, + "engines": { + "node": ">=12" } }, "node_modules/cluster-key-slot": { - "version": "1.1.0", - "resolved": "https://registry.npmjs.org/cluster-key-slot/-/cluster-key-slot-1.1.0.tgz", - "integrity": "sha512-2Nii8p3RwAPiFwsnZvukotvow2rIHM+yQ6ZcBXGHdniadkYGZYiGmkHJIbZPIV9nfv7m/U1IPMVVcAhoWFeklw==", + "version": "1.1.2", + "resolved": "https://registry.npmjs.org/cluster-key-slot/-/cluster-key-slot-1.1.2.tgz", + "integrity": "sha512-RMr0FhtfXemyinomL4hrWcYJxmX6deFdCxpJzhDttxgO1+bcCnkk+9drydLVDmAMG7NE6aN/fl4F7ucU/90gAA==", "engines": { "node": ">=0.10.0" } @@ -149,9 +131,9 @@ } }, "node_modules/denque": { - "version": "2.0.1", - "resolved": "https://registry.npmjs.org/denque/-/denque-2.0.1.tgz", - "integrity": "sha512-tfiWc6BQLXNLpNiR5iGd0Ocu3P3VpxfzFiqubLgMfhfOw9WyvgJBd46CClNn9k3qfbjvT//0cf7AlYRX/OslMQ==", + "version": "2.1.0", + "resolved": "https://registry.npmjs.org/denque/-/denque-2.1.0.tgz", + "integrity": "sha512-HVQE3AAb/pxF8fQAoiqpvg9i3evqug3hoiwakOyZAwJm+6vZehbkYXZ0l4JxS+I3QxM97v5aaRNhj8v5oBhekw==", "engines": { "node": ">=0.10" } @@ -169,6 +151,14 @@ "node": ">=6" } }, + "node_modules/generic-pool": { + "version": "3.9.0", + "resolved": "https://registry.npmjs.org/generic-pool/-/generic-pool-3.9.0.tgz", + "integrity": "sha512-hymDOu5B53XvN4QT9dBmZxPX4CWhBPPLguTZ9MMFeFa/Kg0xWVfylOVNlJji/E7yTZWFd/q9GO5TxDLq156D7g==", + "engines": { + "node": ">= 4" + } + }, "node_modules/get-caller-file": { "version": "2.0.5", "resolved": "https://registry.npmjs.org/get-caller-file/-/get-caller-file-2.0.5.tgz", @@ -191,14 +181,14 @@ } }, "node_modules/ioredis": { - "version": "5.2.2", - "resolved": "https://registry.npmjs.org/ioredis/-/ioredis-5.2.2.tgz", - "integrity": "sha512-wryKc1ur8PcCmNwfcGkw5evouzpbDXxxkMkzPK8wl4xQfQf7lHe11Jotell5ikMVAtikXJEu/OJVaoV51BggRQ==", + "version": "5.3.0", + "resolved": "https://registry.npmjs.org/ioredis/-/ioredis-5.3.0.tgz", + "integrity": "sha512-Id9jKHhsILuIZpHc61QkagfVdUj2Rag5GzG1TGEvRNeM7dtTOjICgjC+tvqYxi//PuX2wjQ+Xjva2ONBuf92Pw==", "dependencies": { "@ioredis/commands": "^1.1.1", "cluster-key-slot": "^1.1.0", "debug": "^4.3.4", - "denque": "^2.0.1", + "denque": "^2.1.0", "lodash.defaults": "^4.2.0", "lodash.isarguments": "^3.1.0", "redis-errors": "^1.2.0", @@ -353,27 +343,32 @@ "node": ">=10" } }, + "node_modules/yallist": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/yallist/-/yallist-4.0.0.tgz", + "integrity": "sha512-3wdGidZyq5PB084XLES5TpOSRA3wjXAlIWMhum2kRcv/41Sn2emQ0dycQW4uZXLejwKvg6EsvbdlVL+FYEct7A==" + }, "node_modules/yargs": { - "version": "17.5.1", - "resolved": "https://registry.npmjs.org/yargs/-/yargs-17.5.1.tgz", - "integrity": "sha512-t6YAJcxDkNX7NFYiVtKvWUz8l+PaKTLiL63mJYWR2GnHq2gjEWISzsLp9wg3aY36dY1j+gfIEL3pIF+XlJJfbA==", + "version": "17.6.2", + "resolved": "https://registry.npmjs.org/yargs/-/yargs-17.6.2.tgz", + "integrity": "sha512-1/9UrdHjDZc0eOU0HxOHoS78C69UD3JRMvzlJ7S79S2nTaWRA/whGCTV8o9e/N/1Va9YIV7Q4sOxD8VV4pCWOw==", "dependencies": { - "cliui": "^7.0.2", + "cliui": "^8.0.1", "escalade": "^3.1.1", "get-caller-file": "^2.0.5", "require-directory": "^2.1.1", "string-width": "^4.2.3", "y18n": "^5.0.5", - "yargs-parser": "^21.0.0" + "yargs-parser": "^21.1.1" }, "engines": { "node": ">=12" } }, "node_modules/yargs-parser": { - "version": "21.0.1", - "resolved": "https://registry.npmjs.org/yargs-parser/-/yargs-parser-21.0.1.tgz", - "integrity": "sha512-9BK1jFpLzJROCI5TzwZL/TU4gqjK5xiHV/RfWLOahrjAko/e4DJkRDZQXfvqAsiZzzYhgAzbgz6lg48jcm4GLg==", + "version": "21.1.1", + "resolved": "https://registry.npmjs.org/yargs-parser/-/yargs-parser-21.1.1.tgz", + "integrity": "sha512-tVpsJW7DdjecAiFpbIB1e3qxIQsE6NoPc5/eTdrbbIC4h0LVsWhnoa3g+m2HclBIujHzsxZ4VJVA+GUuc2/LBw==", "engines": { "node": ">=12" } @@ -391,25 +386,10 @@ "integrity": "sha512-fsR4P/ROllzf/7lXYyElUJCheWdTJVJvOTps8v9IWKFATxR61ANOlnoPqhH099xYLrJGpc2ZQ28B3rMeUt5VQg==" }, "@redis/client": { - "version": "file:../packages/client", + "version": "1.5.0", "requires": { - "@istanbuljs/nyc-config-typescript": "^1.0.2", - "@redis/test-utils": "*", - "@types/node": "^18.7.10", - "@types/sinon": "^10.0.13", - "@types/yallist": "^4.0.1", - "@typescript-eslint/eslint-plugin": "^5.34.0", - "@typescript-eslint/parser": "^5.34.0", - "cluster-key-slot": "1.1.0", - "eslint": "^8.22.0", - "generic-pool": "3.8.2", - "nyc": "^15.1.0", - "release-it": "^15.3.0", - "sinon": "^14.0.0", - "source-map-support": "^0.5.21", - "ts-node": "^10.9.1", - "typedoc": "^0.23.10", - "typescript": "^4.7.4", + "cluster-key-slot": "1.1.2", + "generic-pool": "3.9.0", "yallist": "4.0.0" } }, @@ -432,19 +412,19 @@ "integrity": "sha512-AKpaYlHn8t4SVbOHCy+b5+KKgvR4vrsD8vbvrbiQJps7fKDTkjkDry6ji0rUJjC0kzbNePLwzxq8iypo41qeWA==" }, "cliui": { - "version": "7.0.4", - "resolved": "https://registry.npmjs.org/cliui/-/cliui-7.0.4.tgz", - "integrity": "sha512-OcRE68cOsVMXp1Yvonl/fzkQOyjLSu/8bhPDfQt0e0/Eb283TKP20Fs2MqoPsr9SwA595rRCA+QMzYc9nBP+JQ==", + "version": "8.0.1", + "resolved": "https://registry.npmjs.org/cliui/-/cliui-8.0.1.tgz", + "integrity": "sha512-BSeNnyus75C4//NQ9gQt1/csTXyo/8Sb+afLAkzAptFuMsod9HFokGNudZpi/oQV73hnVK+sR+5PVRMd+Dr7YQ==", "requires": { "string-width": "^4.2.0", - "strip-ansi": "^6.0.0", + "strip-ansi": "^6.0.1", "wrap-ansi": "^7.0.0" } }, "cluster-key-slot": { - "version": "1.1.0", - "resolved": "https://registry.npmjs.org/cluster-key-slot/-/cluster-key-slot-1.1.0.tgz", - "integrity": "sha512-2Nii8p3RwAPiFwsnZvukotvow2rIHM+yQ6ZcBXGHdniadkYGZYiGmkHJIbZPIV9nfv7m/U1IPMVVcAhoWFeklw==" + "version": "1.1.2", + "resolved": "https://registry.npmjs.org/cluster-key-slot/-/cluster-key-slot-1.1.2.tgz", + "integrity": "sha512-RMr0FhtfXemyinomL4hrWcYJxmX6deFdCxpJzhDttxgO1+bcCnkk+9drydLVDmAMG7NE6aN/fl4F7ucU/90gAA==" }, "color-convert": { "version": "2.0.1", @@ -468,9 +448,9 @@ } }, "denque": { - "version": "2.0.1", - "resolved": "https://registry.npmjs.org/denque/-/denque-2.0.1.tgz", - "integrity": "sha512-tfiWc6BQLXNLpNiR5iGd0Ocu3P3VpxfzFiqubLgMfhfOw9WyvgJBd46CClNn9k3qfbjvT//0cf7AlYRX/OslMQ==" + "version": "2.1.0", + "resolved": "https://registry.npmjs.org/denque/-/denque-2.1.0.tgz", + "integrity": "sha512-HVQE3AAb/pxF8fQAoiqpvg9i3evqug3hoiwakOyZAwJm+6vZehbkYXZ0l4JxS+I3QxM97v5aaRNhj8v5oBhekw==" }, "emoji-regex": { "version": "8.0.0", @@ -482,6 +462,11 @@ "resolved": "https://registry.npmjs.org/escalade/-/escalade-3.1.1.tgz", "integrity": "sha512-k0er2gUkLf8O0zKJiAhmkTnJlTvINGv7ygDNPbeIsX/TJjGJZHuh9B2UxbsaEkmlEo9MfhrSzmhIlhRlI2GXnw==" }, + "generic-pool": { + "version": "3.9.0", + "resolved": "https://registry.npmjs.org/generic-pool/-/generic-pool-3.9.0.tgz", + "integrity": "sha512-hymDOu5B53XvN4QT9dBmZxPX4CWhBPPLguTZ9MMFeFa/Kg0xWVfylOVNlJji/E7yTZWFd/q9GO5TxDLq156D7g==" + }, "get-caller-file": { "version": "2.0.5", "resolved": "https://registry.npmjs.org/get-caller-file/-/get-caller-file-2.0.5.tgz", @@ -498,14 +483,14 @@ } }, "ioredis": { - "version": "5.2.2", - "resolved": "https://registry.npmjs.org/ioredis/-/ioredis-5.2.2.tgz", - "integrity": "sha512-wryKc1ur8PcCmNwfcGkw5evouzpbDXxxkMkzPK8wl4xQfQf7lHe11Jotell5ikMVAtikXJEu/OJVaoV51BggRQ==", + "version": "5.3.0", + "resolved": "https://registry.npmjs.org/ioredis/-/ioredis-5.3.0.tgz", + "integrity": "sha512-Id9jKHhsILuIZpHc61QkagfVdUj2Rag5GzG1TGEvRNeM7dtTOjICgjC+tvqYxi//PuX2wjQ+Xjva2ONBuf92Pw==", "requires": { "@ioredis/commands": "^1.1.1", "cluster-key-slot": "^1.1.0", "debug": "^4.3.4", - "denque": "^2.0.1", + "denque": "^2.1.0", "lodash.defaults": "^4.2.0", "lodash.isarguments": "^3.1.0", "redis-errors": "^1.2.0", @@ -617,24 +602,29 @@ "resolved": "https://registry.npmjs.org/y18n/-/y18n-5.0.8.tgz", "integrity": "sha512-0pfFzegeDWJHJIAmTLRP2DwHjdF5s7jo9tuztdQxAhINCdvS+3nGINqPd00AphqJR/0LhANUS6/+7SCb98YOfA==" }, + "yallist": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/yallist/-/yallist-4.0.0.tgz", + "integrity": "sha512-3wdGidZyq5PB084XLES5TpOSRA3wjXAlIWMhum2kRcv/41Sn2emQ0dycQW4uZXLejwKvg6EsvbdlVL+FYEct7A==" + }, "yargs": { - "version": "17.5.1", - "resolved": "https://registry.npmjs.org/yargs/-/yargs-17.5.1.tgz", - "integrity": "sha512-t6YAJcxDkNX7NFYiVtKvWUz8l+PaKTLiL63mJYWR2GnHq2gjEWISzsLp9wg3aY36dY1j+gfIEL3pIF+XlJJfbA==", + "version": "17.6.2", + "resolved": "https://registry.npmjs.org/yargs/-/yargs-17.6.2.tgz", + "integrity": "sha512-1/9UrdHjDZc0eOU0HxOHoS78C69UD3JRMvzlJ7S79S2nTaWRA/whGCTV8o9e/N/1Va9YIV7Q4sOxD8VV4pCWOw==", "requires": { - "cliui": "^7.0.2", + "cliui": "^8.0.1", "escalade": "^3.1.1", "get-caller-file": "^2.0.5", "require-directory": "^2.1.1", "string-width": "^4.2.3", "y18n": "^5.0.5", - "yargs-parser": "^21.0.0" + "yargs-parser": "^21.1.1" } }, "yargs-parser": { - "version": "21.0.1", - "resolved": "https://registry.npmjs.org/yargs-parser/-/yargs-parser-21.0.1.tgz", - "integrity": "sha512-9BK1jFpLzJROCI5TzwZL/TU4gqjK5xiHV/RfWLOahrjAko/e4DJkRDZQXfvqAsiZzzYhgAzbgz6lg48jcm4GLg==" + "version": "21.1.1", + "resolved": "https://registry.npmjs.org/yargs-parser/-/yargs-parser-21.1.1.tgz", + "integrity": "sha512-tVpsJW7DdjecAiFpbIB1e3qxIQsE6NoPc5/eTdrbbIC4h0LVsWhnoa3g+m2HclBIujHzsxZ4VJVA+GUuc2/LBw==" } } } diff --git a/benchmark/package.json b/benchmark/package.json index d5eac40b1ab..79b0facda83 100644 --- a/benchmark/package.json +++ b/benchmark/package.json @@ -9,8 +9,8 @@ "dependencies": { "@redis/client": "../packages/client", "hdr-histogram-js": "3.0.0", - "ioredis": "5.2.2", + "ioredis": "5.3.0", "redis-v3": "npm:redis@3.1.2", - "yargs": "17.5.1" + "yargs": "17.6.2" } } diff --git a/package-lock.json b/package-lock.json index 884a670f840..4e7db229b8d 100644 --- a/package-lock.json +++ b/package-lock.json @@ -21,9 +21,9 @@ }, "devDependencies": { "@tsconfig/node14": "^1.0.3", - "gh-pages": "^4.0.0", - "release-it": "^15.3.0", - "typescript": "^4.7.4" + "gh-pages": "^5.0.0", + "release-it": "^15.6.0", + "typescript": "^4.9.4" } }, "node_modules/@ampproject/remapping": { @@ -52,34 +52,34 @@ } }, "node_modules/@babel/compat-data": { - "version": "7.19.4", - "resolved": "https://registry.npmjs.org/@babel/compat-data/-/compat-data-7.19.4.tgz", - "integrity": "sha512-CHIGpJcUQ5lU9KrPHTjBMhVwQG6CQjxfg36fGXl3qk/Gik1WwWachaXFuo0uCWJT/mStOKtcbFJCaVLihC1CMw==", + "version": "7.20.10", + "resolved": "https://registry.npmjs.org/@babel/compat-data/-/compat-data-7.20.10.tgz", + "integrity": "sha512-sEnuDPpOJR/fcafHMjpcpGN5M2jbUGUHwmuWKM/YdPzeEDJg8bgmbcWQFUfE32MQjti1koACvoPVsDe8Uq+idg==", "dev": true, "engines": { "node": ">=6.9.0" } }, "node_modules/@babel/core": { - "version": "7.19.6", - "resolved": "https://registry.npmjs.org/@babel/core/-/core-7.19.6.tgz", - "integrity": "sha512-D2Ue4KHpc6Ys2+AxpIx1BZ8+UegLLLE2p3KJEuJRKmokHOtl49jQ5ny1773KsGLZs8MQvBidAF6yWUJxRqtKtg==", + "version": "7.20.12", + "resolved": "https://registry.npmjs.org/@babel/core/-/core-7.20.12.tgz", + "integrity": "sha512-XsMfHovsUYHFMdrIHkZphTN/2Hzzi78R08NuHfDBehym2VsPDL6Zn/JAD/JQdnRvbSsbQc4mVaU1m6JgtTEElg==", "dev": true, "dependencies": { "@ampproject/remapping": "^2.1.0", "@babel/code-frame": "^7.18.6", - "@babel/generator": "^7.19.6", - "@babel/helper-compilation-targets": "^7.19.3", - "@babel/helper-module-transforms": "^7.19.6", - "@babel/helpers": "^7.19.4", - "@babel/parser": "^7.19.6", - "@babel/template": "^7.18.10", - "@babel/traverse": "^7.19.6", - "@babel/types": "^7.19.4", + "@babel/generator": "^7.20.7", + "@babel/helper-compilation-targets": "^7.20.7", + "@babel/helper-module-transforms": "^7.20.11", + "@babel/helpers": "^7.20.7", + "@babel/parser": "^7.20.7", + "@babel/template": "^7.20.7", + "@babel/traverse": "^7.20.12", + "@babel/types": "^7.20.7", "convert-source-map": "^1.7.0", "debug": "^4.1.0", "gensync": "^1.0.0-beta.2", - "json5": "^2.2.1", + "json5": "^2.2.2", "semver": "^6.3.0" }, "engines": { @@ -90,22 +90,13 @@ "url": "https://opencollective.com/babel" } }, - "node_modules/@babel/core/node_modules/semver": { - "version": "6.3.0", - "resolved": "https://registry.npmjs.org/semver/-/semver-6.3.0.tgz", - "integrity": "sha512-b39TBaTSfV6yBrapU89p5fKekE2m/NwnDocOVruQFS1/veMgdzuPcnOM34M6CwxW8jH/lxEa5rBoDeUwu5HHTw==", - "dev": true, - "bin": { - "semver": "bin/semver.js" - } - }, "node_modules/@babel/generator": { - "version": "7.19.6", - "resolved": "https://registry.npmjs.org/@babel/generator/-/generator-7.19.6.tgz", - "integrity": "sha512-oHGRUQeoX1QrKeJIKVe0hwjGqNnVYsM5Nep5zo0uE0m42sLH+Fsd2pStJ5sRM1bNyTUUoz0pe2lTeMJrb/taTA==", + "version": "7.20.7", + "resolved": "https://registry.npmjs.org/@babel/generator/-/generator-7.20.7.tgz", + "integrity": "sha512-7wqMOJq8doJMZmP4ApXTzLxSr7+oO2jroJURrVEp6XShrQUObV8Tq/D0NCcoYg2uHqUrjzO0zwBjoYzelxK+sw==", "dev": true, "dependencies": { - "@babel/types": "^7.19.4", + "@babel/types": "^7.20.7", "@jridgewell/gen-mapping": "^0.3.2", "jsesc": "^2.5.1" }, @@ -128,14 +119,15 @@ } }, "node_modules/@babel/helper-compilation-targets": { - "version": "7.19.3", - "resolved": "https://registry.npmjs.org/@babel/helper-compilation-targets/-/helper-compilation-targets-7.19.3.tgz", - "integrity": "sha512-65ESqLGyGmLvgR0mst5AdW1FkNlj9rQsCKduzEoEPhBCDFGXvz2jW6bXFG6i0/MrV2s7hhXjjb2yAzcPuQlLwg==", + "version": "7.20.7", + "resolved": "https://registry.npmjs.org/@babel/helper-compilation-targets/-/helper-compilation-targets-7.20.7.tgz", + "integrity": "sha512-4tGORmfQcrc+bvrjb5y3dG9Mx1IOZjsHqQVUz7XCNHO+iTmqxWnVg3KRygjGmpRLJGdQSKuvFinbIb0CnZwHAQ==", "dev": true, "dependencies": { - "@babel/compat-data": "^7.19.3", + "@babel/compat-data": "^7.20.5", "@babel/helper-validator-option": "^7.18.6", "browserslist": "^4.21.3", + "lru-cache": "^5.1.1", "semver": "^6.3.0" }, "engines": { @@ -145,15 +137,6 @@ "@babel/core": "^7.0.0" } }, - "node_modules/@babel/helper-compilation-targets/node_modules/semver": { - "version": "6.3.0", - "resolved": "https://registry.npmjs.org/semver/-/semver-6.3.0.tgz", - "integrity": "sha512-b39TBaTSfV6yBrapU89p5fKekE2m/NwnDocOVruQFS1/veMgdzuPcnOM34M6CwxW8jH/lxEa5rBoDeUwu5HHTw==", - "dev": true, - "bin": { - "semver": "bin/semver.js" - } - }, "node_modules/@babel/helper-environment-visitor": { "version": "7.18.9", "resolved": "https://registry.npmjs.org/@babel/helper-environment-visitor/-/helper-environment-visitor-7.18.9.tgz", @@ -201,31 +184,31 @@ } }, "node_modules/@babel/helper-module-transforms": { - "version": "7.19.6", - "resolved": "https://registry.npmjs.org/@babel/helper-module-transforms/-/helper-module-transforms-7.19.6.tgz", - "integrity": "sha512-fCmcfQo/KYr/VXXDIyd3CBGZ6AFhPFy1TfSEJ+PilGVlQT6jcbqtHAM4C1EciRqMza7/TpOUZliuSH+U6HAhJw==", + "version": "7.20.11", + "resolved": "https://registry.npmjs.org/@babel/helper-module-transforms/-/helper-module-transforms-7.20.11.tgz", + "integrity": "sha512-uRy78kN4psmji1s2QtbtcCSaj/LILFDp0f/ymhpQH5QY3nljUZCaNWz9X1dEj/8MBdBEFECs7yRhKn8i7NjZgg==", "dev": true, "dependencies": { "@babel/helper-environment-visitor": "^7.18.9", "@babel/helper-module-imports": "^7.18.6", - "@babel/helper-simple-access": "^7.19.4", + "@babel/helper-simple-access": "^7.20.2", "@babel/helper-split-export-declaration": "^7.18.6", "@babel/helper-validator-identifier": "^7.19.1", - "@babel/template": "^7.18.10", - "@babel/traverse": "^7.19.6", - "@babel/types": "^7.19.4" + "@babel/template": "^7.20.7", + "@babel/traverse": "^7.20.10", + "@babel/types": "^7.20.7" }, "engines": { "node": ">=6.9.0" } }, "node_modules/@babel/helper-simple-access": { - "version": "7.19.4", - "resolved": "https://registry.npmjs.org/@babel/helper-simple-access/-/helper-simple-access-7.19.4.tgz", - "integrity": "sha512-f9Xq6WqBFqaDfbCzn2w85hwklswz5qsKlh7f08w4Y9yhJHpnNC0QemtSkK5YyOY8kPGvyiwdzZksGUhnGdaUIg==", + "version": "7.20.2", + "resolved": "https://registry.npmjs.org/@babel/helper-simple-access/-/helper-simple-access-7.20.2.tgz", + "integrity": "sha512-+0woI/WPq59IrqDYbVGfshjT5Dmk/nnbdpcF8SnMhhXObpTq2KNBdLFRFrkVdbDOyUmHBCxzm5FHV1rACIkIbA==", "dev": true, "dependencies": { - "@babel/types": "^7.19.4" + "@babel/types": "^7.20.2" }, "engines": { "node": ">=6.9.0" @@ -271,14 +254,14 @@ } }, "node_modules/@babel/helpers": { - "version": "7.19.4", - "resolved": "https://registry.npmjs.org/@babel/helpers/-/helpers-7.19.4.tgz", - "integrity": "sha512-G+z3aOx2nfDHwX/kyVii5fJq+bgscg89/dJNWpYeKeBv3v9xX8EIabmx1k6u9LS04H7nROFVRVK+e3k0VHp+sw==", + "version": "7.20.7", + "resolved": "https://registry.npmjs.org/@babel/helpers/-/helpers-7.20.7.tgz", + "integrity": "sha512-PBPjs5BppzsGaxHQCDKnZ6Gd9s6xl8bBCluz3vEInLGRJmnZan4F6BYCeqtyXqkk4W5IlPmjK4JlOuZkpJ3xZA==", "dev": true, "dependencies": { - "@babel/template": "^7.18.10", - "@babel/traverse": "^7.19.4", - "@babel/types": "^7.19.4" + "@babel/template": "^7.20.7", + "@babel/traverse": "^7.20.7", + "@babel/types": "^7.20.7" }, "engines": { "node": ">=6.9.0" @@ -370,9 +353,9 @@ } }, "node_modules/@babel/parser": { - "version": "7.19.6", - "resolved": "https://registry.npmjs.org/@babel/parser/-/parser-7.19.6.tgz", - "integrity": "sha512-h1IUp81s2JYJ3mRkdxJgs4UvmSsRvDrx5ICSJbPvtWYv5i1nTBGcBpnog+89rAFMwvvru6E5NUHdBe01UeSzYA==", + "version": "7.20.7", + "resolved": "https://registry.npmjs.org/@babel/parser/-/parser-7.20.7.tgz", + "integrity": "sha512-T3Z9oHybU+0vZlY9CiDSJQTD5ZapcW18ZctFMi0MOAl/4BjFF4ul7NVSARLdbGO5vDqy9eQiGTV0LtKfvCYvcg==", "dev": true, "bin": { "parser": "bin/babel-parser.js" @@ -382,33 +365,33 @@ } }, "node_modules/@babel/template": { - "version": "7.18.10", - "resolved": "https://registry.npmjs.org/@babel/template/-/template-7.18.10.tgz", - "integrity": "sha512-TI+rCtooWHr3QJ27kJxfjutghu44DLnasDMwpDqCXVTal9RLp3RSYNh4NdBrRP2cQAoG9A8juOQl6P6oZG4JxA==", + "version": "7.20.7", + "resolved": "https://registry.npmjs.org/@babel/template/-/template-7.20.7.tgz", + "integrity": "sha512-8SegXApWe6VoNw0r9JHpSteLKTpTiLZ4rMlGIm9JQ18KiCtyQiAMEazujAHrUS5flrcqYZa75ukev3P6QmUwUw==", "dev": true, "dependencies": { "@babel/code-frame": "^7.18.6", - "@babel/parser": "^7.18.10", - "@babel/types": "^7.18.10" + "@babel/parser": "^7.20.7", + "@babel/types": "^7.20.7" }, "engines": { "node": ">=6.9.0" } }, "node_modules/@babel/traverse": { - "version": "7.19.6", - "resolved": "https://registry.npmjs.org/@babel/traverse/-/traverse-7.19.6.tgz", - "integrity": "sha512-6l5HrUCzFM04mfbG09AagtYyR2P0B71B1wN7PfSPiksDPz2k5H9CBC1tcZpz2M8OxbKTPccByoOJ22rUKbpmQQ==", + "version": "7.20.12", + "resolved": "https://registry.npmjs.org/@babel/traverse/-/traverse-7.20.12.tgz", + "integrity": "sha512-MsIbFN0u+raeja38qboyF8TIT7K0BFzz/Yd/77ta4MsUsmP2RAnidIlwq7d5HFQrH/OZJecGV6B71C4zAgpoSQ==", "dev": true, "dependencies": { "@babel/code-frame": "^7.18.6", - "@babel/generator": "^7.19.6", + "@babel/generator": "^7.20.7", "@babel/helper-environment-visitor": "^7.18.9", "@babel/helper-function-name": "^7.19.0", "@babel/helper-hoist-variables": "^7.18.6", "@babel/helper-split-export-declaration": "^7.18.6", - "@babel/parser": "^7.19.6", - "@babel/types": "^7.19.4", + "@babel/parser": "^7.20.7", + "@babel/types": "^7.20.7", "debug": "^4.1.0", "globals": "^11.1.0" }, @@ -426,9 +409,9 @@ } }, "node_modules/@babel/types": { - "version": "7.19.4", - "resolved": "https://registry.npmjs.org/@babel/types/-/types-7.19.4.tgz", - "integrity": "sha512-M5LK7nAeS6+9j7hAq+b3fQs+pNfUtTGq+yFFfHnauFA8zQtLRfmuipmsKDKKLuyG+wC8ABW43A153YNawNTEtw==", + "version": "7.20.7", + "resolved": "https://registry.npmjs.org/@babel/types/-/types-7.20.7.tgz", + "integrity": "sha512-69OnhBxSSgK0OzTJai4kyPDiKTIe3j+ctaHdIGVbRahTLAT7L3R9oeXHC2aVSuGYt3cVnoAMDmOCgJ2yaiLMvg==", "dev": true, "dependencies": { "@babel/helper-string-parser": "^7.19.4", @@ -462,15 +445,15 @@ } }, "node_modules/@eslint/eslintrc": { - "version": "1.3.3", - "resolved": "https://registry.npmjs.org/@eslint/eslintrc/-/eslintrc-1.3.3.tgz", - "integrity": "sha512-uj3pT6Mg+3t39fvLrj8iuCIJ38zKO9FpGtJ4BBJebJhEwjoT+KLVNCcHT5QC9NGRIEi7fZ0ZR8YRb884auB4Lg==", + "version": "1.4.1", + "resolved": "https://registry.npmjs.org/@eslint/eslintrc/-/eslintrc-1.4.1.tgz", + "integrity": "sha512-XXrH9Uarn0stsyldqDYq8r++mROmWRI1xKMXa640Bb//SY1+ECYX6VzT6Lcx5frD0V30XieqJ0oX9I2Xj5aoMA==", "dev": true, "dependencies": { "ajv": "^6.12.4", "debug": "^4.3.2", "espree": "^9.4.0", - "globals": "^13.15.0", + "globals": "^13.19.0", "ignore": "^5.2.0", "import-fresh": "^3.2.1", "js-yaml": "^4.1.0", @@ -485,14 +468,14 @@ } }, "node_modules/@humanwhocodes/config-array": { - "version": "0.11.6", - "resolved": "https://registry.npmjs.org/@humanwhocodes/config-array/-/config-array-0.11.6.tgz", - "integrity": "sha512-jJr+hPTJYKyDILJfhNSHsjiwXYf26Flsz8DvNndOsHs5pwSnpGUEy8yzF0JYhCEvTDdV2vuOK5tt8BVhwO5/hg==", + "version": "0.11.8", + "resolved": "https://registry.npmjs.org/@humanwhocodes/config-array/-/config-array-0.11.8.tgz", + "integrity": "sha512-UybHIJzJnR5Qc/MsD9Kr+RpO2h+/P1GhOwdiLPXK5TWk5sgTdu88bTD9UP+CKbPPh5Rni1u0GjAdYQLemG8g+g==", "dev": true, "dependencies": { "@humanwhocodes/object-schema": "^1.2.1", "debug": "^4.1.1", - "minimatch": "^3.0.4" + "minimatch": "^3.0.5" }, "engines": { "node": ">=10.10.0" @@ -548,19 +531,6 @@ "sprintf-js": "~1.0.2" } }, - "node_modules/@istanbuljs/load-nyc-config/node_modules/find-up": { - "version": "4.1.0", - "resolved": "https://registry.npmjs.org/find-up/-/find-up-4.1.0.tgz", - "integrity": "sha512-PpOwAdQ/YlXQ2vj8a3h8IipDuYRi3wceVQQGYWxNINccq40Anw7BlsEXCMbt1Zt+OLA6Fq9suIpIWD0OsnISlw==", - "dev": true, - "dependencies": { - "locate-path": "^5.0.0", - "path-exists": "^4.0.0" - }, - "engines": { - "node": ">=8" - } - }, "node_modules/@istanbuljs/load-nyc-config/node_modules/js-yaml": { "version": "3.14.1", "resolved": "https://registry.npmjs.org/js-yaml/-/js-yaml-3.14.1.tgz", @@ -574,45 +544,6 @@ "js-yaml": "bin/js-yaml.js" } }, - "node_modules/@istanbuljs/load-nyc-config/node_modules/locate-path": { - "version": "5.0.0", - "resolved": "https://registry.npmjs.org/locate-path/-/locate-path-5.0.0.tgz", - "integrity": "sha512-t7hw9pI+WvuwNJXwk5zVHpyhIqzg2qTlklJOf0mVxGSbe3Fp2VieZcduNYjaLDoy6p9uGpQEGWG87WpMKlNq8g==", - "dev": true, - "dependencies": { - "p-locate": "^4.1.0" - }, - "engines": { - "node": ">=8" - } - }, - "node_modules/@istanbuljs/load-nyc-config/node_modules/p-limit": { - "version": "2.3.0", - "resolved": "https://registry.npmjs.org/p-limit/-/p-limit-2.3.0.tgz", - "integrity": "sha512-//88mFWSJx8lxCzwdAABTJL2MyWB12+eIY7MDL2SqLmAkeKU9qxRvWuSyTjm3FUmpBEMuFfckAIqEaVGUDxb6w==", - "dev": true, - "dependencies": { - "p-try": "^2.0.0" - }, - "engines": { - "node": ">=6" - }, - "funding": { - "url": "https://github.com/sponsors/sindresorhus" - } - }, - "node_modules/@istanbuljs/load-nyc-config/node_modules/p-locate": { - "version": "4.1.0", - "resolved": "https://registry.npmjs.org/p-locate/-/p-locate-4.1.0.tgz", - "integrity": "sha512-R79ZZ/0wAxKGu3oYMlz8jy/kbhsNrS7SKZ7PxEHBgJ5+F2mtFW2fK2cOtBh1cHYkQsbzFV7I+EoRKe6Yt0oK7A==", - "dev": true, - "dependencies": { - "p-limit": "^2.2.0" - }, - "engines": { - "node": ">=8" - } - }, "node_modules/@istanbuljs/load-nyc-config/node_modules/resolve-from": { "version": "5.0.0", "resolved": "https://registry.npmjs.org/resolve-from/-/resolve-from-5.0.0.tgz", @@ -793,12 +724,12 @@ "dev": true }, "node_modules/@octokit/plugin-paginate-rest": { - "version": "4.3.1", - "resolved": "https://registry.npmjs.org/@octokit/plugin-paginate-rest/-/plugin-paginate-rest-4.3.1.tgz", - "integrity": "sha512-h8KKxESmSFTcXX409CAxlaOYscEDvN2KGQRsLCGT1NSqRW+D6EXLVQ8vuHhFznS9MuH9QYw1GfsUN30bg8hjVA==", + "version": "5.0.1", + "resolved": "https://registry.npmjs.org/@octokit/plugin-paginate-rest/-/plugin-paginate-rest-5.0.1.tgz", + "integrity": "sha512-7A+rEkS70pH36Z6JivSlR7Zqepz3KVucEFVDnSrgHXzG7WLAzYwcHZbKdfTXHwuTHbkT1vKvz7dHl1+HNf6Qyw==", "dev": true, "dependencies": { - "@octokit/types": "^7.5.0" + "@octokit/types": "^8.0.0" }, "engines": { "node": ">= 14" @@ -807,21 +738,6 @@ "@octokit/core": ">=4" } }, - "node_modules/@octokit/plugin-paginate-rest/node_modules/@octokit/openapi-types": { - "version": "13.13.1", - "resolved": "https://registry.npmjs.org/@octokit/openapi-types/-/openapi-types-13.13.1.tgz", - "integrity": "sha512-4EuKSk3N95UBWFau3Bz9b3pheQ8jQYbKmBL5+GSuY8YDPDwu03J4BjI+66yNi8aaX/3h1qDpb0mbBkLdr+cfGQ==", - "dev": true - }, - "node_modules/@octokit/plugin-paginate-rest/node_modules/@octokit/types": { - "version": "7.5.1", - "resolved": "https://registry.npmjs.org/@octokit/types/-/types-7.5.1.tgz", - "integrity": "sha512-Zk4OUMLCSpXNI8KZZn47lVLJSsgMyCimsWWQI5hyjZg7hdYm0kjotaIkbG0Pp8SfU2CofMBzonboTqvzn3FrJA==", - "dev": true, - "dependencies": { - "@octokit/openapi-types": "^13.11.0" - } - }, "node_modules/@octokit/plugin-request-log": { "version": "1.0.4", "resolved": "https://registry.npmjs.org/@octokit/plugin-request-log/-/plugin-request-log-1.0.4.tgz", @@ -879,9 +795,9 @@ } }, "node_modules/@octokit/request/node_modules/node-fetch": { - "version": "2.6.7", - "resolved": "https://registry.npmjs.org/node-fetch/-/node-fetch-2.6.7.tgz", - "integrity": "sha512-ZjMPFEfVx5j+y2yF35Kzx5sF7kDzxuDj6ziH4FFbOp87zKDZNx8yExJIb05OGF4Nlt9IHFIMBkRl41VdvcNdbQ==", + "version": "2.6.8", + "resolved": "https://registry.npmjs.org/node-fetch/-/node-fetch-2.6.8.tgz", + "integrity": "sha512-RZ6dBYuj8dRSfxpUSu+NsdF1dpPpluJxwOp+6IoDp/sH2QNDSvurYsAa+F1WxY2RjA1iP93xhcsUoYbF2XBqVg==", "dev": true, "dependencies": { "whatwg-url": "^5.0.0" @@ -899,33 +815,33 @@ } }, "node_modules/@octokit/rest": { - "version": "19.0.4", - "resolved": "https://registry.npmjs.org/@octokit/rest/-/rest-19.0.4.tgz", - "integrity": "sha512-LwG668+6lE8zlSYOfwPj4FxWdv/qFXYBpv79TWIQEpBLKA9D/IMcWsF/U9RGpA3YqMVDiTxpgVpEW3zTFfPFTA==", + "version": "19.0.5", + "resolved": "https://registry.npmjs.org/@octokit/rest/-/rest-19.0.5.tgz", + "integrity": "sha512-+4qdrUFq2lk7Va+Qff3ofREQWGBeoTKNqlJO+FGjFP35ZahP+nBenhZiGdu8USSgmq4Ky3IJ/i4u0xbLqHaeow==", "dev": true, "dependencies": { - "@octokit/core": "^4.0.0", - "@octokit/plugin-paginate-rest": "^4.0.0", + "@octokit/core": "^4.1.0", + "@octokit/plugin-paginate-rest": "^5.0.0", "@octokit/plugin-request-log": "^1.0.4", - "@octokit/plugin-rest-endpoint-methods": "^6.0.0" + "@octokit/plugin-rest-endpoint-methods": "^6.7.0" }, "engines": { "node": ">= 14" } }, "node_modules/@octokit/types": { - "version": "8.0.0", - "resolved": "https://registry.npmjs.org/@octokit/types/-/types-8.0.0.tgz", - "integrity": "sha512-65/TPpOJP1i3K4lBJMnWqPUJ6zuOtzhtagDvydAWbEXpbFYA0oMKKyLb95NFZZP0lSh/4b6K+DQlzvYQJQQePg==", + "version": "8.1.1", + "resolved": "https://registry.npmjs.org/@octokit/types/-/types-8.1.1.tgz", + "integrity": "sha512-7tjk+6DyhYAmei8FOEwPfGKc0VE1x56CKPJ+eE44zhDbOyMT+9yan8apfQFxo8oEFsy+0O7PiBtH8w0Yo0Y9Kw==", "dev": true, "dependencies": { "@octokit/openapi-types": "^14.0.0" } }, "node_modules/@pnpm/network.ca-file": { - "version": "1.0.1", - "resolved": "https://registry.npmjs.org/@pnpm/network.ca-file/-/network.ca-file-1.0.1.tgz", - "integrity": "sha512-gkINruT2KUhZLTaiHxwCOh1O4NVnFT0wLjWFBHmTz9vpKag/C/noIMJXBxFe4F0mYpUVX2puLwAieLYFg2NvoA==", + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/@pnpm/network.ca-file/-/network.ca-file-1.0.2.tgz", + "integrity": "sha512-YcPQ8a0jwYU9bTdJDpXjMi7Brhkr1mXsXrUJvjqM2mQDgkRiz8jFaQGOdaLxgjtUfQgZhKy/O3cG/YwmgKaxLA==", "dev": true, "dependencies": { "graceful-fs": "4.2.10" @@ -988,30 +904,30 @@ } }, "node_modules/@sinonjs/commons": { - "version": "1.8.3", - "resolved": "https://registry.npmjs.org/@sinonjs/commons/-/commons-1.8.3.tgz", - "integrity": "sha512-xkNcLAn/wZaX14RPlwizcKicDk9G3F8m2nU3L7Ukm5zBgTwiT0wsoFAHx9Jq56fJA1z/7uKGtCRu16sOUCLIHQ==", + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/@sinonjs/commons/-/commons-2.0.0.tgz", + "integrity": "sha512-uLa0j859mMrg2slwQYdO/AkrOfmH+X6LTVmNTS9CqexuE2IvVORIkSpJLqePAbEnKJ77aMmCwr1NUZ57120Xcg==", "dev": true, "dependencies": { "type-detect": "4.0.8" } }, "node_modules/@sinonjs/fake-timers": { - "version": "9.1.2", - "resolved": "https://registry.npmjs.org/@sinonjs/fake-timers/-/fake-timers-9.1.2.tgz", - "integrity": "sha512-BPS4ynJW/o92PUR4wgriz2Ud5gpST5vz6GQfMixEDK0Z8ZCUv2M7SkBLykH56T++Xs+8ln9zTGbOvNGIe02/jw==", + "version": "10.0.2", + "resolved": "https://registry.npmjs.org/@sinonjs/fake-timers/-/fake-timers-10.0.2.tgz", + "integrity": "sha512-SwUDyjWnah1AaNl7kxsa7cfLhlTYoiyhDAIgyh+El30YvXs/o7OLXpYH88Zdhyx9JExKrmHDJ+10bwIcY80Jmw==", "dev": true, "dependencies": { - "@sinonjs/commons": "^1.7.0" + "@sinonjs/commons": "^2.0.0" } }, "node_modules/@sinonjs/samsam": { - "version": "6.1.1", - "resolved": "https://registry.npmjs.org/@sinonjs/samsam/-/samsam-6.1.1.tgz", - "integrity": "sha512-cZ7rKJTLiE7u7Wi/v9Hc2fs3Ucc3jrWeMgPHbbTCeVAB2S0wOBbYlkJVeNSL04i7fdhT8wIbDq1zhC/PXTD2SA==", + "version": "7.0.1", + "resolved": "https://registry.npmjs.org/@sinonjs/samsam/-/samsam-7.0.1.tgz", + "integrity": "sha512-zsAk2Jkiq89mhZovB2LLOdTCxJF4hqqTToGP0ASWlhp4I1hqOjcfmZGafXntCN7MDC6yySH0mFHrYtHceOeLmw==", "dev": true, "dependencies": { - "@sinonjs/commons": "^1.6.0", + "@sinonjs/commons": "^2.0.0", "lodash.get": "^4.4.2", "type-detect": "^4.0.8" } @@ -1080,27 +996,21 @@ "dev": true }, "node_modules/@types/mocha": { - "version": "10.0.0", - "resolved": "https://registry.npmjs.org/@types/mocha/-/mocha-10.0.0.tgz", - "integrity": "sha512-rADY+HtTOA52l9VZWtgQfn4p+UDVM2eDVkMZT1I6syp0YKxW2F9v+0pbRZLsvskhQv/vMb6ZfCay81GHbz5SHg==", + "version": "10.0.1", + "resolved": "https://registry.npmjs.org/@types/mocha/-/mocha-10.0.1.tgz", + "integrity": "sha512-/fvYntiO1GeICvqbQ3doGDIP97vWmvFt83GKguJ6prmQM2iXZfFcq6YE8KteFyRtX2/h5Hf91BYvPodJKFYv5Q==", "dev": true }, "node_modules/@types/node": { - "version": "18.11.6", - "resolved": "https://registry.npmjs.org/@types/node/-/node-18.11.6.tgz", - "integrity": "sha512-j3CEDa2vd96K0AXF8Wur7UucACvnjkk8hYyQAHhUNciabZLDl9nfAEVUSwmh245OOZV15bRA3Y590Gi5jUcDJg==", - "dev": true - }, - "node_modules/@types/parse-json": { - "version": "4.0.0", - "resolved": "https://registry.npmjs.org/@types/parse-json/-/parse-json-4.0.0.tgz", - "integrity": "sha512-//oorEZjL6sbPcKUaCdIGlIUeH26mgzimjBB77G6XRgnDl/L5wOnpyBGRe/Mmf5CVW3PwEBE1NjiMZ/ssFh4wA==", + "version": "18.11.18", + "resolved": "https://registry.npmjs.org/@types/node/-/node-18.11.18.tgz", + "integrity": "sha512-DHQpWGjyQKSHj3ebjFI/wRKcqQcdR+MoFBygntYOZytCqNfkd2ZC4ARDJ2DQqhjH5p85Nnd3jhUJIXrszFX/JA==", "dev": true }, "node_modules/@types/semver": { - "version": "7.3.12", - "resolved": "https://registry.npmjs.org/@types/semver/-/semver-7.3.12.tgz", - "integrity": "sha512-WwA1MW0++RfXmCr12xeYOOC5baSC9mSb0ZqCquFzKhcoF4TvHu5MKOuXsncgZcpVFhB1pXd5hZmM0ryAoCp12A==", + "version": "7.3.13", + "resolved": "https://registry.npmjs.org/@types/semver/-/semver-7.3.13.tgz", + "integrity": "sha512-21cFJr9z3g5dW8B0CVI9g2O9beqaThGQ6ZFBqHfwhzLDKUxaqTIy3vnfah/UPkfOiF2pLq+tGz+W8RyCskuslw==", "dev": true }, "node_modules/@types/sinon": { @@ -1125,9 +1035,9 @@ "dev": true }, "node_modules/@types/yargs": { - "version": "17.0.13", - "resolved": "https://registry.npmjs.org/@types/yargs/-/yargs-17.0.13.tgz", - "integrity": "sha512-9sWaruZk2JGxIQU+IhI1fhPYRcQ0UuTNuKuCW9bR5fp7qi2Llf7WDzNa17Cy7TKnh3cdxDOiyTu6gaLS0eDatg==", + "version": "17.0.20", + "resolved": "https://registry.npmjs.org/@types/yargs/-/yargs-17.0.20.tgz", + "integrity": "sha512-eknWrTHofQuPk2iuqDm1waA7V6xPlbgBoaaXEgYkClhLOnB0TtbW+srJaOToAgawPxPlHQzwypFA2bhZaUGP5A==", "dev": true, "dependencies": { "@types/yargs-parser": "*" @@ -1140,16 +1050,17 @@ "dev": true }, "node_modules/@typescript-eslint/eslint-plugin": { - "version": "5.41.0", - "resolved": "https://registry.npmjs.org/@typescript-eslint/eslint-plugin/-/eslint-plugin-5.41.0.tgz", - "integrity": "sha512-DXUS22Y57/LAFSg3x7Vi6RNAuLpTXwxB9S2nIA7msBb/Zt8p7XqMwdpdc1IU7CkOQUPgAqR5fWvxuKCbneKGmA==", + "version": "5.49.0", + "resolved": "https://registry.npmjs.org/@typescript-eslint/eslint-plugin/-/eslint-plugin-5.49.0.tgz", + "integrity": "sha512-IhxabIpcf++TBaBa1h7jtOWyon80SXPRLDq0dVz5SLFC/eW6tofkw/O7Ar3lkx5z5U6wzbKDrl2larprp5kk5Q==", "dev": true, "dependencies": { - "@typescript-eslint/scope-manager": "5.41.0", - "@typescript-eslint/type-utils": "5.41.0", - "@typescript-eslint/utils": "5.41.0", + "@typescript-eslint/scope-manager": "5.49.0", + "@typescript-eslint/type-utils": "5.49.0", + "@typescript-eslint/utils": "5.49.0", "debug": "^4.3.4", "ignore": "^5.2.0", + "natural-compare-lite": "^1.4.0", "regexpp": "^3.2.0", "semver": "^7.3.7", "tsutils": "^3.21.0" @@ -1171,15 +1082,48 @@ } } }, + "node_modules/@typescript-eslint/eslint-plugin/node_modules/lru-cache": { + "version": "6.0.0", + "resolved": "https://registry.npmjs.org/lru-cache/-/lru-cache-6.0.0.tgz", + "integrity": "sha512-Jo6dJ04CmSjuznwJSS3pUeWmd/H0ffTlkXXgwZi+eq1UCmqQwCh+eLsYOYCwY991i2Fah4h1BEMCx4qThGbsiA==", + "dev": true, + "dependencies": { + "yallist": "^4.0.0" + }, + "engines": { + "node": ">=10" + } + }, + "node_modules/@typescript-eslint/eslint-plugin/node_modules/semver": { + "version": "7.3.8", + "resolved": "https://registry.npmjs.org/semver/-/semver-7.3.8.tgz", + "integrity": "sha512-NB1ctGL5rlHrPJtFDVIVzTyQylMLu9N9VICA6HSFJo8MCGVTMW6gfpicwKmmK/dAjTOrqu5l63JJOpDSrAis3A==", + "dev": true, + "dependencies": { + "lru-cache": "^6.0.0" + }, + "bin": { + "semver": "bin/semver.js" + }, + "engines": { + "node": ">=10" + } + }, + "node_modules/@typescript-eslint/eslint-plugin/node_modules/yallist": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/yallist/-/yallist-4.0.0.tgz", + "integrity": "sha512-3wdGidZyq5PB084XLES5TpOSRA3wjXAlIWMhum2kRcv/41Sn2emQ0dycQW4uZXLejwKvg6EsvbdlVL+FYEct7A==", + "dev": true + }, "node_modules/@typescript-eslint/parser": { - "version": "5.41.0", - "resolved": "https://registry.npmjs.org/@typescript-eslint/parser/-/parser-5.41.0.tgz", - "integrity": "sha512-HQVfix4+RL5YRWZboMD1pUfFN8MpRH4laziWkkAzyO1fvNOY/uinZcvo3QiFJVS/siNHupV8E5+xSwQZrl6PZA==", + "version": "5.49.0", + "resolved": "https://registry.npmjs.org/@typescript-eslint/parser/-/parser-5.49.0.tgz", + "integrity": "sha512-veDlZN9mUhGqU31Qiv2qEp+XrJj5fgZpJ8PW30sHU+j/8/e5ruAhLaVDAeznS7A7i4ucb/s8IozpDtt9NqCkZg==", "dev": true, "dependencies": { - "@typescript-eslint/scope-manager": "5.41.0", - "@typescript-eslint/types": "5.41.0", - "@typescript-eslint/typescript-estree": "5.41.0", + "@typescript-eslint/scope-manager": "5.49.0", + "@typescript-eslint/types": "5.49.0", + "@typescript-eslint/typescript-estree": "5.49.0", "debug": "^4.3.4" }, "engines": { @@ -1199,13 +1143,13 @@ } }, "node_modules/@typescript-eslint/scope-manager": { - "version": "5.41.0", - "resolved": "https://registry.npmjs.org/@typescript-eslint/scope-manager/-/scope-manager-5.41.0.tgz", - "integrity": "sha512-xOxPJCnuktUkY2xoEZBKXO5DBCugFzjrVndKdUnyQr3+9aDWZReKq9MhaoVnbL+maVwWJu/N0SEtrtEUNb62QQ==", + "version": "5.49.0", + "resolved": "https://registry.npmjs.org/@typescript-eslint/scope-manager/-/scope-manager-5.49.0.tgz", + "integrity": "sha512-clpROBOiMIzpbWNxCe1xDK14uPZh35u4QaZO1GddilEzoCLAEz4szb51rBpdgurs5k2YzPtJeTEN3qVbG+LRUQ==", "dev": true, "dependencies": { - "@typescript-eslint/types": "5.41.0", - "@typescript-eslint/visitor-keys": "5.41.0" + "@typescript-eslint/types": "5.49.0", + "@typescript-eslint/visitor-keys": "5.49.0" }, "engines": { "node": "^12.22.0 || ^14.17.0 || >=16.0.0" @@ -1216,13 +1160,13 @@ } }, "node_modules/@typescript-eslint/type-utils": { - "version": "5.41.0", - "resolved": "https://registry.npmjs.org/@typescript-eslint/type-utils/-/type-utils-5.41.0.tgz", - "integrity": "sha512-L30HNvIG6A1Q0R58e4hu4h+fZqaO909UcnnPbwKiN6Rc3BUEx6ez2wgN7aC0cBfcAjZfwkzE+E2PQQ9nEuoqfA==", + "version": "5.49.0", + "resolved": "https://registry.npmjs.org/@typescript-eslint/type-utils/-/type-utils-5.49.0.tgz", + "integrity": "sha512-eUgLTYq0tR0FGU5g1YHm4rt5H/+V2IPVkP0cBmbhRyEmyGe4XvJ2YJ6sYTmONfjmdMqyMLad7SB8GvblbeESZA==", "dev": true, "dependencies": { - "@typescript-eslint/typescript-estree": "5.41.0", - "@typescript-eslint/utils": "5.41.0", + "@typescript-eslint/typescript-estree": "5.49.0", + "@typescript-eslint/utils": "5.49.0", "debug": "^4.3.4", "tsutils": "^3.21.0" }, @@ -1243,9 +1187,9 @@ } }, "node_modules/@typescript-eslint/types": { - "version": "5.41.0", - "resolved": "https://registry.npmjs.org/@typescript-eslint/types/-/types-5.41.0.tgz", - "integrity": "sha512-5BejraMXMC+2UjefDvrH0Fo/eLwZRV6859SXRg+FgbhA0R0l6lDqDGAQYhKbXhPN2ofk2kY5sgGyLNL907UXpA==", + "version": "5.49.0", + "resolved": "https://registry.npmjs.org/@typescript-eslint/types/-/types-5.49.0.tgz", + "integrity": "sha512-7If46kusG+sSnEpu0yOz2xFv5nRz158nzEXnJFCGVEHWnuzolXKwrH5Bsf9zsNlOQkyZuk0BZKKoJQI+1JPBBg==", "dev": true, "engines": { "node": "^12.22.0 || ^14.17.0 || >=16.0.0" @@ -1256,13 +1200,13 @@ } }, "node_modules/@typescript-eslint/typescript-estree": { - "version": "5.41.0", - "resolved": "https://registry.npmjs.org/@typescript-eslint/typescript-estree/-/typescript-estree-5.41.0.tgz", - "integrity": "sha512-SlzFYRwFSvswzDSQ/zPkIWcHv8O5y42YUskko9c4ki+fV6HATsTODUPbRbcGDFYP86gaJL5xohUEytvyNNcXWg==", + "version": "5.49.0", + "resolved": "https://registry.npmjs.org/@typescript-eslint/typescript-estree/-/typescript-estree-5.49.0.tgz", + "integrity": "sha512-PBdx+V7deZT/3GjNYPVQv1Nc0U46dAHbIuOG8AZ3on3vuEKiPDwFE/lG1snN2eUB9IhF7EyF7K1hmTcLztNIsA==", "dev": true, "dependencies": { - "@typescript-eslint/types": "5.41.0", - "@typescript-eslint/visitor-keys": "5.41.0", + "@typescript-eslint/types": "5.49.0", + "@typescript-eslint/visitor-keys": "5.49.0", "debug": "^4.3.4", "globby": "^11.1.0", "is-glob": "^4.0.3", @@ -1282,17 +1226,79 @@ } } }, + "node_modules/@typescript-eslint/typescript-estree/node_modules/array-union": { + "version": "2.1.0", + "resolved": "https://registry.npmjs.org/array-union/-/array-union-2.1.0.tgz", + "integrity": "sha512-HGyxoOTYUyCM6stUe6EJgnd4EoewAI7zMdfqO+kGjnlZmBDz/cR5pf8r/cR4Wq60sL/p0IkcjUEEPwS3GFrIyw==", + "dev": true, + "engines": { + "node": ">=8" + } + }, + "node_modules/@typescript-eslint/typescript-estree/node_modules/globby": { + "version": "11.1.0", + "resolved": "https://registry.npmjs.org/globby/-/globby-11.1.0.tgz", + "integrity": "sha512-jhIXaOzy1sb8IyocaruWSn1TjmnBVs8Ayhcy83rmxNJ8q2uWKCAj3CnJY+KpGSXCueAPc0i05kVvVKtP1t9S3g==", + "dev": true, + "dependencies": { + "array-union": "^2.1.0", + "dir-glob": "^3.0.1", + "fast-glob": "^3.2.9", + "ignore": "^5.2.0", + "merge2": "^1.4.1", + "slash": "^3.0.0" + }, + "engines": { + "node": ">=10" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/@typescript-eslint/typescript-estree/node_modules/lru-cache": { + "version": "6.0.0", + "resolved": "https://registry.npmjs.org/lru-cache/-/lru-cache-6.0.0.tgz", + "integrity": "sha512-Jo6dJ04CmSjuznwJSS3pUeWmd/H0ffTlkXXgwZi+eq1UCmqQwCh+eLsYOYCwY991i2Fah4h1BEMCx4qThGbsiA==", + "dev": true, + "dependencies": { + "yallist": "^4.0.0" + }, + "engines": { + "node": ">=10" + } + }, + "node_modules/@typescript-eslint/typescript-estree/node_modules/semver": { + "version": "7.3.8", + "resolved": "https://registry.npmjs.org/semver/-/semver-7.3.8.tgz", + "integrity": "sha512-NB1ctGL5rlHrPJtFDVIVzTyQylMLu9N9VICA6HSFJo8MCGVTMW6gfpicwKmmK/dAjTOrqu5l63JJOpDSrAis3A==", + "dev": true, + "dependencies": { + "lru-cache": "^6.0.0" + }, + "bin": { + "semver": "bin/semver.js" + }, + "engines": { + "node": ">=10" + } + }, + "node_modules/@typescript-eslint/typescript-estree/node_modules/yallist": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/yallist/-/yallist-4.0.0.tgz", + "integrity": "sha512-3wdGidZyq5PB084XLES5TpOSRA3wjXAlIWMhum2kRcv/41Sn2emQ0dycQW4uZXLejwKvg6EsvbdlVL+FYEct7A==", + "dev": true + }, "node_modules/@typescript-eslint/utils": { - "version": "5.41.0", - "resolved": "https://registry.npmjs.org/@typescript-eslint/utils/-/utils-5.41.0.tgz", - "integrity": "sha512-QlvfwaN9jaMga9EBazQ+5DDx/4sAdqDkcs05AsQHMaopluVCUyu1bTRUVKzXbgjDlrRAQrYVoi/sXJ9fmG+KLQ==", + "version": "5.49.0", + "resolved": "https://registry.npmjs.org/@typescript-eslint/utils/-/utils-5.49.0.tgz", + "integrity": "sha512-cPJue/4Si25FViIb74sHCLtM4nTSBXtLx1d3/QT6mirQ/c65bV8arBEebBJJizfq8W2YyMoPI/WWPFWitmNqnQ==", "dev": true, "dependencies": { "@types/json-schema": "^7.0.9", "@types/semver": "^7.3.12", - "@typescript-eslint/scope-manager": "5.41.0", - "@typescript-eslint/types": "5.41.0", - "@typescript-eslint/typescript-estree": "5.41.0", + "@typescript-eslint/scope-manager": "5.49.0", + "@typescript-eslint/types": "5.49.0", + "@typescript-eslint/typescript-estree": "5.49.0", "eslint-scope": "^5.1.1", "eslint-utils": "^3.0.0", "semver": "^7.3.7" @@ -1308,13 +1314,46 @@ "eslint": "^6.0.0 || ^7.0.0 || ^8.0.0" } }, + "node_modules/@typescript-eslint/utils/node_modules/lru-cache": { + "version": "6.0.0", + "resolved": "https://registry.npmjs.org/lru-cache/-/lru-cache-6.0.0.tgz", + "integrity": "sha512-Jo6dJ04CmSjuznwJSS3pUeWmd/H0ffTlkXXgwZi+eq1UCmqQwCh+eLsYOYCwY991i2Fah4h1BEMCx4qThGbsiA==", + "dev": true, + "dependencies": { + "yallist": "^4.0.0" + }, + "engines": { + "node": ">=10" + } + }, + "node_modules/@typescript-eslint/utils/node_modules/semver": { + "version": "7.3.8", + "resolved": "https://registry.npmjs.org/semver/-/semver-7.3.8.tgz", + "integrity": "sha512-NB1ctGL5rlHrPJtFDVIVzTyQylMLu9N9VICA6HSFJo8MCGVTMW6gfpicwKmmK/dAjTOrqu5l63JJOpDSrAis3A==", + "dev": true, + "dependencies": { + "lru-cache": "^6.0.0" + }, + "bin": { + "semver": "bin/semver.js" + }, + "engines": { + "node": ">=10" + } + }, + "node_modules/@typescript-eslint/utils/node_modules/yallist": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/yallist/-/yallist-4.0.0.tgz", + "integrity": "sha512-3wdGidZyq5PB084XLES5TpOSRA3wjXAlIWMhum2kRcv/41Sn2emQ0dycQW4uZXLejwKvg6EsvbdlVL+FYEct7A==", + "dev": true + }, "node_modules/@typescript-eslint/visitor-keys": { - "version": "5.41.0", - "resolved": "https://registry.npmjs.org/@typescript-eslint/visitor-keys/-/visitor-keys-5.41.0.tgz", - "integrity": "sha512-vilqeHj267v8uzzakbm13HkPMl7cbYpKVjgFWZPIOHIJHZtinvypUhJ5xBXfWYg4eFKqztbMMpOgFpT9Gfx4fw==", + "version": "5.49.0", + "resolved": "https://registry.npmjs.org/@typescript-eslint/visitor-keys/-/visitor-keys-5.49.0.tgz", + "integrity": "sha512-v9jBMjpNWyn8B6k/Mjt6VbUS4J1GvUlR4x3Y+ibnP1z7y7V4n0WRz+50DY6+Myj0UaXVSuUlHohO+eZ8IJEnkg==", "dev": true, "dependencies": { - "@typescript-eslint/types": "5.41.0", + "@typescript-eslint/types": "5.49.0", "eslint-visitor-keys": "^3.3.0" }, "engines": { @@ -1405,6 +1444,15 @@ "string-width": "^4.1.0" } }, + "node_modules/ansi-align/node_modules/ansi-regex": { + "version": "5.0.1", + "resolved": "https://registry.npmjs.org/ansi-regex/-/ansi-regex-5.0.1.tgz", + "integrity": "sha512-quJQXlTSUGL2LH9SUXo8VwsY4soanhgo6LNSm84E1LBcE8s3O0wpdiRzyR9z/ZZJMlMWv37qOOb9pdJlMUEKFQ==", + "dev": true, + "engines": { + "node": ">=8" + } + }, "node_modules/ansi-align/node_modules/emoji-regex": { "version": "8.0.0", "resolved": "https://registry.npmjs.org/emoji-regex/-/emoji-regex-8.0.0.tgz", @@ -1425,6 +1473,18 @@ "node": ">=8" } }, + "node_modules/ansi-align/node_modules/strip-ansi": { + "version": "6.0.1", + "resolved": "https://registry.npmjs.org/strip-ansi/-/strip-ansi-6.0.1.tgz", + "integrity": "sha512-Y38VPSHcqkFrCpFnQ9vuSXmquuv5oXOKpGeT6aGrr3o3Gc9AlVa6JBfUSOCnbxGGZF+/0ooI7KrPuUSztUdU5A==", + "dev": true, + "dependencies": { + "ansi-regex": "^5.0.1" + }, + "engines": { + "node": ">=8" + } + }, "node_modules/ansi-colors": { "version": "4.1.1", "resolved": "https://registry.npmjs.org/ansi-colors/-/ansi-colors-4.1.1.tgz", @@ -1435,60 +1495,48 @@ } }, "node_modules/ansi-escapes": { - "version": "5.0.0", - "resolved": "https://registry.npmjs.org/ansi-escapes/-/ansi-escapes-5.0.0.tgz", - "integrity": "sha512-5GFMVX8HqE/TB+FuBJGuO5XG0WrsA6ptUqoODaT/n9mmUaZFkqnBueB4leqGBCmrUHnCnC4PCZTCd0E7QQ83bA==", + "version": "6.0.0", + "resolved": "https://registry.npmjs.org/ansi-escapes/-/ansi-escapes-6.0.0.tgz", + "integrity": "sha512-IG23inYII3dWlU2EyiAiGj6Bwal5GzsgPMwjYGvc1HPE2dgbj4ZB5ToWBKSquKw74nB3TIuOwaI6/jSULzfgrw==", "dev": true, "dependencies": { - "type-fest": "^1.0.2" + "type-fest": "^3.0.0" }, "engines": { - "node": ">=12" + "node": ">=14.16" }, "funding": { "url": "https://github.com/sponsors/sindresorhus" } }, - "node_modules/ansi-escapes/node_modules/type-fest": { - "version": "1.4.0", - "resolved": "https://registry.npmjs.org/type-fest/-/type-fest-1.4.0.tgz", - "integrity": "sha512-yGSza74xk0UG8k+pLh5oeoYirvIiWo5t0/o3zHHAO2tRDiZcxWP7fywNlXhqb6/r6sWvwi+RsyQMWhVLe4BVuA==", + "node_modules/ansi-regex": { + "version": "6.0.1", + "resolved": "https://registry.npmjs.org/ansi-regex/-/ansi-regex-6.0.1.tgz", + "integrity": "sha512-n5M855fKb2SsfMIiFFoVrABHJC8QtHwVx+mHWP3QcEqBHYienj5dHSgjbxtC0WEZXYt4wcD6zrQElDPhFuZgfA==", "dev": true, "engines": { - "node": ">=10" + "node": ">=12" }, "funding": { - "url": "https://github.com/sponsors/sindresorhus" + "url": "https://github.com/chalk/ansi-regex?sponsor=1" } }, - "node_modules/ansi-regex": { - "version": "5.0.1", - "resolved": "https://registry.npmjs.org/ansi-regex/-/ansi-regex-5.0.1.tgz", - "integrity": "sha512-quJQXlTSUGL2LH9SUXo8VwsY4soanhgo6LNSm84E1LBcE8s3O0wpdiRzyR9z/ZZJMlMWv37qOOb9pdJlMUEKFQ==", + "node_modules/ansi-styles": { + "version": "6.2.1", + "resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-6.2.1.tgz", + "integrity": "sha512-bN798gFfQX+viw3R7yrGWRqnrN2oRkEkUjjl4JNn4E8GxxbjtG3FbrEIIY3l8/hrwUwIeCZvi4QuOTP4MErVug==", "dev": true, "engines": { - "node": ">=8" - } - }, - "node_modules/ansi-styles": { - "version": "4.3.0", - "resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-4.3.0.tgz", - "integrity": "sha512-zbB9rCJAT1rbjiVDb2hqKFHNYLxgtk8NURxZ3IZwD3F6NtxbXZQCnnSi1Lkx+IDohdPlFp222wVALIheZJQSEg==", - "dev": true, - "dependencies": { - "color-convert": "^2.0.1" - }, - "engines": { - "node": ">=8" - }, - "funding": { - "url": "https://github.com/chalk/ansi-styles?sponsor=1" + "node": ">=12" + }, + "funding": { + "url": "https://github.com/chalk/ansi-styles?sponsor=1" } }, "node_modules/anymatch": { - "version": "3.1.2", - "resolved": "https://registry.npmjs.org/anymatch/-/anymatch-3.1.2.tgz", - "integrity": "sha512-P43ePfOAIupkguHUycrc4qJ9kz8ZiuOUijaETwX7THt0Y/GNK7v0aa8rY816xWjZ7rJdA5XdMcpVFTKMq+RvWg==", + "version": "3.1.3", + "resolved": "https://registry.npmjs.org/anymatch/-/anymatch-3.1.3.tgz", + "integrity": "sha512-KMReFUr0B4t+D+OBkjR3KYqvocp2XaSzO55UcB6mgQMd3KbcE+mWTyvVV7D/zsdEbNnV6acZUutkiHQXvTr1Rw==", "dev": true, "dependencies": { "normalize-path": "^3.0.0", @@ -1529,12 +1577,15 @@ "dev": true }, "node_modules/array-union": { - "version": "2.1.0", - "resolved": "https://registry.npmjs.org/array-union/-/array-union-2.1.0.tgz", - "integrity": "sha512-HGyxoOTYUyCM6stUe6EJgnd4EoewAI7zMdfqO+kGjnlZmBDz/cR5pf8r/cR4Wq60sL/p0IkcjUEEPwS3GFrIyw==", + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/array-union/-/array-union-1.0.2.tgz", + "integrity": "sha512-Dxr6QJj/RdU/hCaBjOfxW+q6lyuVE6JFWIrAUpuOOhoJJoQ99cUn3igRaHVB5P9WrgFVN0FfArM3x0cueOU8ng==", "dev": true, + "dependencies": { + "array-uniq": "^1.0.1" + }, "engines": { - "node": ">=8" + "node": ">=0.10.0" } }, "node_modules/array-uniq": { @@ -1547,14 +1598,14 @@ } }, "node_modules/array.prototype.map": { - "version": "1.0.4", - "resolved": "https://registry.npmjs.org/array.prototype.map/-/array.prototype.map-1.0.4.tgz", - "integrity": "sha512-Qds9QnX7A0qISY7JT5WuJO0NJPE9CMlC6JzHQfhpqAAQQzufVRoeH7EzUY5GcPTx72voG8LV/5eo+b8Qi8hmhA==", + "version": "1.0.5", + "resolved": "https://registry.npmjs.org/array.prototype.map/-/array.prototype.map-1.0.5.tgz", + "integrity": "sha512-gfaKntvwqYIuC7mLLyv2wzZIJqrRhn5PZ9EfFejSx6a78sV7iDsGpG9P+3oUPtm1Rerqm6nrKS4FYuTIvWfo3g==", "dev": true, "dependencies": { "call-bind": "^1.0.2", - "define-properties": "^1.1.3", - "es-abstract": "^1.19.0", + "define-properties": "^1.1.4", + "es-abstract": "^1.20.4", "es-array-method-boxes-properly": "^1.0.0", "is-string": "^1.0.7" }, @@ -1578,13 +1629,10 @@ } }, "node_modules/async": { - "version": "2.6.4", - "resolved": "https://registry.npmjs.org/async/-/async-2.6.4.tgz", - "integrity": "sha512-mzo5dfJYwAn29PeiJ0zvwTo04zj8HDJj0Mn8TD7sno7q12prdbnasKJHhkm2c1LgrhlJ0teaea8860oxi51mGA==", - "dev": true, - "dependencies": { - "lodash": "^4.17.14" - } + "version": "3.2.4", + "resolved": "https://registry.npmjs.org/async/-/async-3.2.4.tgz", + "integrity": "sha512-iAB+JbDEGXhyIUavoDl9WP/Jj106Kz9DEn1DPgYw5ruDn0e3Wgi3sKFm55sASdGBNOQB8F59d9qQ7deqrHA8wQ==", + "dev": true }, "node_modules/async-retry": { "version": "1.3.3", @@ -1595,11 +1643,17 @@ "retry": "0.13.1" } }, - "node_modules/asynckit": { - "version": "0.4.0", - "resolved": "https://registry.npmjs.org/asynckit/-/asynckit-0.4.0.tgz", - "integrity": "sha512-Oei9OH4tRh0YqU3GxhX79dM/mwVgvbZJaSNaRk+bshkj0S5cfHcgYakreBjrHwatXKbz+IoIdYLxrKim2MjW0Q==", - "dev": true + "node_modules/available-typed-arrays": { + "version": "1.0.5", + "resolved": "https://registry.npmjs.org/available-typed-arrays/-/available-typed-arrays-1.0.5.tgz", + "integrity": "sha512-DMD0KiN46eipeziST1LPP/STfDU0sufISXmjSgvVsoU2tqxctQeASejWcfNtxYKqETM1UxQ8sp2OrSBWpHY6sw==", + "dev": true, + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } }, "node_modules/balanced-match": { "version": "1.0.2", @@ -1654,9 +1708,9 @@ } }, "node_modules/boxen": { - "version": "7.0.0", - "resolved": "https://registry.npmjs.org/boxen/-/boxen-7.0.0.tgz", - "integrity": "sha512-j//dBVuyacJbvW+tvZ9HuH03fZ46QcaKvvhZickZqtB271DxJ7SNRSNxrV/dZX0085m7hISRZWbzWlJvx/rHSg==", + "version": "7.0.1", + "resolved": "https://registry.npmjs.org/boxen/-/boxen-7.0.1.tgz", + "integrity": "sha512-8k2eH6SRAK00NDl1iX5q17RJ8rfl53TajdYxE3ssMLehbg487dEVgsad4pIsZb/QqBgYWIl6JOauMTLGX2Kpkw==", "dev": true, "dependencies": { "ansi-align": "^3.0.1", @@ -1676,9 +1730,9 @@ } }, "node_modules/boxen/node_modules/camelcase": { - "version": "7.0.0", - "resolved": "https://registry.npmjs.org/camelcase/-/camelcase-7.0.0.tgz", - "integrity": "sha512-JToIvOmz6nhGsUhAYScbo2d6Py5wojjNfoxoc2mEVLUdJ70gJK2gnd+ABY1Tc3sVMyK7QDPtN0T/XdlCQWITyQ==", + "version": "7.0.1", + "resolved": "https://registry.npmjs.org/camelcase/-/camelcase-7.0.1.tgz", + "integrity": "sha512-xlx1yCK2Oc1APsPXDL2LdlNP6+uu8OCDdhOBSVT279M/S+y75O30C2VuD8T2ogdePBBl7PfPF4504tnLgX3zfw==", "dev": true, "engines": { "node": ">=14.16" @@ -1687,18 +1741,6 @@ "url": "https://github.com/sponsors/sindresorhus" } }, - "node_modules/boxen/node_modules/chalk": { - "version": "5.1.2", - "resolved": "https://registry.npmjs.org/chalk/-/chalk-5.1.2.tgz", - "integrity": "sha512-E5CkT4jWURs1Vy5qGJye+XwCkNj7Od3Af7CP6SujMetSMkLs8Do2RWJK5yx1wamHV/op8Rz+9rltjaTQWDnEFQ==", - "dev": true, - "engines": { - "node": "^12.17.0 || ^14.13 || >=16.0.0" - }, - "funding": { - "url": "https://github.com/chalk/chalk?sponsor=1" - } - }, "node_modules/boxen/node_modules/type-fest": { "version": "2.19.0", "resolved": "https://registry.npmjs.org/type-fest/-/type-fest-2.19.0.tgz", @@ -1816,17 +1858,17 @@ } }, "node_modules/cacheable-request": { - "version": "10.2.2", - "resolved": "https://registry.npmjs.org/cacheable-request/-/cacheable-request-10.2.2.tgz", - "integrity": "sha512-KxjQZM3UIo7/J6W4sLpwFvu1GB3Whv8NtZ8ZrUL284eiQjiXeeqWTdhixNrp/NLZ/JNuFBo6BD4ZaO8ZJ5BN8Q==", + "version": "10.2.5", + "resolved": "https://registry.npmjs.org/cacheable-request/-/cacheable-request-10.2.5.tgz", + "integrity": "sha512-5RwYYCfzjNPsyJxb/QpaM0bfzx+kw5/YpDhZPm9oMIDntHFQ9YXeyV47ZvzlTE0XrrrbyO2UITJH4GF9eRLdXQ==", "dev": true, "dependencies": { "@types/http-cache-semantics": "^4.0.1", "get-stream": "^6.0.1", "http-cache-semantics": "^4.1.0", - "keyv": "^4.5.0", + "keyv": "^4.5.2", "mimic-response": "^4.0.0", - "normalize-url": "^7.2.0", + "normalize-url": "^8.0.0", "responselike": "^3.0.0" }, "engines": { @@ -1880,9 +1922,9 @@ } }, "node_modules/caniuse-lite": { - "version": "1.0.30001425", - "resolved": "https://registry.npmjs.org/caniuse-lite/-/caniuse-lite-1.0.30001425.tgz", - "integrity": "sha512-/pzFv0OmNG6W0ym80P3NtapU0QEiDS3VuYAZMGoLLqiC7f6FJFe1MjpQDREGApeenD9wloeytmVDj+JLXPC6qw==", + "version": "1.0.30001445", + "resolved": "https://registry.npmjs.org/caniuse-lite/-/caniuse-lite-1.0.30001445.tgz", + "integrity": "sha512-8sdQIdMztYmzfTMO6KfLny878Ln9c2M0fc7EH60IjlP4Dc4PiCy7K2Vl3ITmWgOyPgVQKa5x+UP/KqFsxj4mBg==", "dev": true, "funding": [ { @@ -1896,16 +1938,12 @@ ] }, "node_modules/chalk": { - "version": "4.1.2", - "resolved": "https://registry.npmjs.org/chalk/-/chalk-4.1.2.tgz", - "integrity": "sha512-oKnbhFyRIXpUuez8iBMmyEa4nbj4IOQyuhc/wy9kY7/WVPcwIO9VA668Pu8RkO7+0G76SLROeyw9CpQ061i4mA==", + "version": "5.1.2", + "resolved": "https://registry.npmjs.org/chalk/-/chalk-5.1.2.tgz", + "integrity": "sha512-E5CkT4jWURs1Vy5qGJye+XwCkNj7Od3Af7CP6SujMetSMkLs8Do2RWJK5yx1wamHV/op8Rz+9rltjaTQWDnEFQ==", "dev": true, - "dependencies": { - "ansi-styles": "^4.1.0", - "supports-color": "^7.1.0" - }, "engines": { - "node": ">=10" + "node": "^12.17.0 || ^14.13 || >=16.0.0" }, "funding": { "url": "https://github.com/chalk/chalk?sponsor=1" @@ -1957,10 +1995,19 @@ } }, "node_modules/ci-info": { - "version": "3.5.0", - "resolved": "https://registry.npmjs.org/ci-info/-/ci-info-3.5.0.tgz", - "integrity": "sha512-yH4RezKOGlOhxkmhbeNuC4eYZKAUsEaGtBuBzDDP1eFUKiccDWzBABxBfOx31IDwDIXMTxWuwAxUGModvkbuVw==", - "dev": true + "version": "3.7.1", + "resolved": "https://registry.npmjs.org/ci-info/-/ci-info-3.7.1.tgz", + "integrity": "sha512-4jYS4MOAaCIStSRwiuxc4B8MYhIe676yO1sYGzARnjXkWpmzZMMYxY6zu8WYWDhSuth5zhrQ1rhNSibyyvv4/w==", + "dev": true, + "funding": [ + { + "type": "github", + "url": "https://github.com/sponsors/sibiraj-s" + } + ], + "engines": { + "node": ">=8" + } }, "node_modules/clean-stack": { "version": "2.2.0", @@ -2020,17 +2067,38 @@ } }, "node_modules/cliui": { - "version": "8.0.1", - "resolved": "https://registry.npmjs.org/cliui/-/cliui-8.0.1.tgz", - "integrity": "sha512-BSeNnyus75C4//NQ9gQt1/csTXyo/8Sb+afLAkzAptFuMsod9HFokGNudZpi/oQV73hnVK+sR+5PVRMd+Dr7YQ==", + "version": "6.0.0", + "resolved": "https://registry.npmjs.org/cliui/-/cliui-6.0.0.tgz", + "integrity": "sha512-t6wbgtoCXvAzst7QgXxJYqPt0usEfbgQdftEPbLL/cvv6HPE5VgvqCuAIDR0NgU52ds6rFwqrgakNLrHEjCbrQ==", "dev": true, "dependencies": { "string-width": "^4.2.0", - "strip-ansi": "^6.0.1", - "wrap-ansi": "^7.0.0" + "strip-ansi": "^6.0.0", + "wrap-ansi": "^6.2.0" + } + }, + "node_modules/cliui/node_modules/ansi-regex": { + "version": "5.0.1", + "resolved": "https://registry.npmjs.org/ansi-regex/-/ansi-regex-5.0.1.tgz", + "integrity": "sha512-quJQXlTSUGL2LH9SUXo8VwsY4soanhgo6LNSm84E1LBcE8s3O0wpdiRzyR9z/ZZJMlMWv37qOOb9pdJlMUEKFQ==", + "dev": true, + "engines": { + "node": ">=8" + } + }, + "node_modules/cliui/node_modules/ansi-styles": { + "version": "4.3.0", + "resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-4.3.0.tgz", + "integrity": "sha512-zbB9rCJAT1rbjiVDb2hqKFHNYLxgtk8NURxZ3IZwD3F6NtxbXZQCnnSi1Lkx+IDohdPlFp222wVALIheZJQSEg==", + "dev": true, + "dependencies": { + "color-convert": "^2.0.1" }, "engines": { - "node": ">=12" + "node": ">=8" + }, + "funding": { + "url": "https://github.com/chalk/ansi-styles?sponsor=1" } }, "node_modules/cliui/node_modules/emoji-regex": { @@ -2053,10 +2121,22 @@ "node": ">=8" } }, + "node_modules/cliui/node_modules/strip-ansi": { + "version": "6.0.1", + "resolved": "https://registry.npmjs.org/strip-ansi/-/strip-ansi-6.0.1.tgz", + "integrity": "sha512-Y38VPSHcqkFrCpFnQ9vuSXmquuv5oXOKpGeT6aGrr3o3Gc9AlVa6JBfUSOCnbxGGZF+/0ooI7KrPuUSztUdU5A==", + "dev": true, + "dependencies": { + "ansi-regex": "^5.0.1" + }, + "engines": { + "node": ">=8" + } + }, "node_modules/cliui/node_modules/wrap-ansi": { - "version": "7.0.0", - "resolved": "https://registry.npmjs.org/wrap-ansi/-/wrap-ansi-7.0.0.tgz", - "integrity": "sha512-YVGIj2kamLSTxw6NsZjoBxfSwsn0ycdesmc4p+Q21c5zPuZ1pl+NfxVdxPtdHvmNVOQ6XSYG4AUtyt/Fi7D16Q==", + "version": "6.2.0", + "resolved": "https://registry.npmjs.org/wrap-ansi/-/wrap-ansi-6.2.0.tgz", + "integrity": "sha512-r6lPcBGxZXlIcymEu7InxDMhdW0KDxpLgoFLcguasxCaJ/SOIZwINatK9KY/tf+ZrlywOKU0UDj3ATXUBfxJXA==", "dev": true, "dependencies": { "ansi-styles": "^4.0.0", @@ -2064,10 +2144,7 @@ "strip-ansi": "^6.0.0" }, "engines": { - "node": ">=10" - }, - "funding": { - "url": "https://github.com/chalk/wrap-ansi?sponsor=1" + "node": ">=8" } }, "node_modules/clone": { @@ -2080,9 +2157,9 @@ } }, "node_modules/cluster-key-slot": { - "version": "1.1.1", - "resolved": "https://registry.npmjs.org/cluster-key-slot/-/cluster-key-slot-1.1.1.tgz", - "integrity": "sha512-rwHwUfXL40Chm1r08yrhU3qpUvdVlgkKNeyeGPOxnW8/SyVDvgRaed/Uz54AqWNaTCAThlj6QAs3TZcKI0xDEw==", + "version": "1.1.2", + "resolved": "https://registry.npmjs.org/cluster-key-slot/-/cluster-key-slot-1.1.2.tgz", + "integrity": "sha512-RMr0FhtfXemyinomL4hrWcYJxmX6deFdCxpJzhDttxgO1+bcCnkk+9drydLVDmAMG7NE6aN/fl4F7ucU/90gAA==", "engines": { "node": ">=0.10.0" } @@ -2105,18 +2182,6 @@ "integrity": "sha512-dOy+3AuW3a2wNbZHIuMZpTcgjGuLU/uBL/ubcZF9OXbDo8ff4O8yVp5Bf0efS8uEoYo5q4Fx7dY9OgQGXgAsQA==", "dev": true }, - "node_modules/combined-stream": { - "version": "1.0.8", - "resolved": "https://registry.npmjs.org/combined-stream/-/combined-stream-1.0.8.tgz", - "integrity": "sha512-FQN4MRfuJeHf7cBbBMJFXhKSDq+2kAArBlmRBvcvFE5BB1HZKXtSFASDhdlz9zOYwxh8lDdnvmMOe/+5cdoEdg==", - "dev": true, - "dependencies": { - "delayed-stream": "~1.0.0" - }, - "engines": { - "node": ">= 0.8" - } - }, "node_modules/commander": { "version": "2.20.3", "resolved": "https://registry.npmjs.org/commander/-/commander-2.20.3.tgz", @@ -2183,19 +2248,18 @@ "dev": true }, "node_modules/cosmiconfig": { - "version": "7.0.1", - "resolved": "https://registry.npmjs.org/cosmiconfig/-/cosmiconfig-7.0.1.tgz", - "integrity": "sha512-a1YWNUV2HwGimB7dU2s1wUMurNKjpx60HxBB6xUM8Re+2s1g1IIfJvFR0/iCF+XHdE0GMTKTuLR32UQff4TEyQ==", + "version": "8.0.0", + "resolved": "https://registry.npmjs.org/cosmiconfig/-/cosmiconfig-8.0.0.tgz", + "integrity": "sha512-da1EafcpH6b/TD8vDRaWV7xFINlHlF6zKsGwS1TsuVJTZRkquaS5HTMq7uq6h31619QjbsYl21gVDOm32KM1vQ==", "dev": true, "dependencies": { - "@types/parse-json": "^4.0.0", "import-fresh": "^3.2.1", + "js-yaml": "^4.1.0", "parse-json": "^5.0.0", - "path-type": "^4.0.0", - "yaml": "^1.10.0" + "path-type": "^4.0.0" }, "engines": { - "node": ">=10" + "node": ">=14" } }, "node_modules/create-require": { @@ -2246,9 +2310,9 @@ } }, "node_modules/data-uri-to-buffer": { - "version": "4.0.0", - "resolved": "https://registry.npmjs.org/data-uri-to-buffer/-/data-uri-to-buffer-4.0.0.tgz", - "integrity": "sha512-Vr3mLBA8qWmcuschSLAOogKgQ/Jwxulv3RNE4FXnYWRGujzrRWQI4m12fQqRkwX06C0KanhLr4hK+GydchZsaA==", + "version": "4.0.1", + "resolved": "https://registry.npmjs.org/data-uri-to-buffer/-/data-uri-to-buffer-4.0.1.tgz", + "integrity": "sha512-0R9ikRb668HB7QDxT1vkpuUBtqc53YyAwMwGeUFKRojY/NWKvdZ+9UYtRfGmhqNbRkTSVpMbmyhXipFFv2cb/A==", "dev": true, "engines": { "node": ">= 12" @@ -2398,15 +2462,6 @@ "node": ">= 6" } }, - "node_modules/delayed-stream": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/delayed-stream/-/delayed-stream-1.0.0.tgz", - "integrity": "sha512-ZySD7Nf91aLB0RxL4KGrKHBXl7Eds1DAmEdcoVawXnLD7SDhpNgtuII2aAkg7a7QS41jxPSZ17p4VdGnMHk3MQ==", - "dev": true, - "engines": { - "node": ">=0.4.0" - } - }, "node_modules/depd": { "version": "2.0.0", "resolved": "https://registry.npmjs.org/depd/-/depd-2.0.0.tgz", @@ -2483,9 +2538,9 @@ "dev": true }, "node_modules/email-addresses": { - "version": "3.1.0", - "resolved": "https://registry.npmjs.org/email-addresses/-/email-addresses-3.1.0.tgz", - "integrity": "sha512-k0/r7GrWVL32kZlGwfPNgB2Y/mMXVTq/decgLczm/j34whdaspNrZO8CnXPf1laaHxI6ptUlsnAxN+UAPw+fzg==", + "version": "5.0.0", + "resolved": "https://registry.npmjs.org/email-addresses/-/email-addresses-5.0.0.tgz", + "integrity": "sha512-4OIPYlA6JXqtVn8zpHpGiI7vE6EQOAg16aGnDMIAlZVinnoZ8208tW1hAbjWydgN/4PLTT9q+O1K6AH/vALJGw==", "dev": true }, "node_modules/emoji-regex": { @@ -2504,35 +2559,44 @@ } }, "node_modules/es-abstract": { - "version": "1.20.4", - "resolved": "https://registry.npmjs.org/es-abstract/-/es-abstract-1.20.4.tgz", - "integrity": "sha512-0UtvRN79eMe2L+UNEF1BwRe364sj/DXhQ/k5FmivgoSdpM90b8Jc0mDzKMGo7QS0BVbOP/bTwBKNnDc9rNzaPA==", + "version": "1.21.1", + "resolved": "https://registry.npmjs.org/es-abstract/-/es-abstract-1.21.1.tgz", + "integrity": "sha512-QudMsPOz86xYz/1dG1OuGBKOELjCh99IIWHLzy5znUB6j8xG2yMA7bfTV86VSqKF+Y/H08vQPR+9jyXpuC6hfg==", "dev": true, "dependencies": { + "available-typed-arrays": "^1.0.5", "call-bind": "^1.0.2", + "es-set-tostringtag": "^2.0.1", "es-to-primitive": "^1.2.1", "function-bind": "^1.1.1", "function.prototype.name": "^1.1.5", "get-intrinsic": "^1.1.3", "get-symbol-description": "^1.0.0", + "globalthis": "^1.0.3", + "gopd": "^1.0.1", "has": "^1.0.3", "has-property-descriptors": "^1.0.0", + "has-proto": "^1.0.1", "has-symbols": "^1.0.3", - "internal-slot": "^1.0.3", + "internal-slot": "^1.0.4", + "is-array-buffer": "^3.0.1", "is-callable": "^1.2.7", "is-negative-zero": "^2.0.2", "is-regex": "^1.1.4", "is-shared-array-buffer": "^1.0.2", "is-string": "^1.0.7", + "is-typed-array": "^1.1.10", "is-weakref": "^1.0.2", "object-inspect": "^1.12.2", "object-keys": "^1.1.1", "object.assign": "^4.1.4", "regexp.prototype.flags": "^1.4.3", "safe-regex-test": "^1.0.0", - "string.prototype.trimend": "^1.0.5", - "string.prototype.trimstart": "^1.0.5", - "unbox-primitive": "^1.0.2" + "string.prototype.trimend": "^1.0.6", + "string.prototype.trimstart": "^1.0.6", + "typed-array-length": "^1.0.4", + "unbox-primitive": "^1.0.2", + "which-typed-array": "^1.1.9" }, "engines": { "node": ">= 0.4" @@ -2548,24 +2612,39 @@ "dev": true }, "node_modules/es-get-iterator": { - "version": "1.1.2", - "resolved": "https://registry.npmjs.org/es-get-iterator/-/es-get-iterator-1.1.2.tgz", - "integrity": "sha512-+DTO8GYwbMCwbywjimwZMHp8AuYXOS2JZFWoi2AlPOS3ebnII9w/NLpNZtA7A0YLaVDw+O7KFCeoIV7OPvM7hQ==", + "version": "1.1.3", + "resolved": "https://registry.npmjs.org/es-get-iterator/-/es-get-iterator-1.1.3.tgz", + "integrity": "sha512-sPZmqHBe6JIiTfN5q2pEi//TwxmAFHwj/XEuYjTuse78i8KxaqMTTzxPoFKuzRpDpTJ+0NAbpfenkmH2rePtuw==", "dev": true, "dependencies": { "call-bind": "^1.0.2", - "get-intrinsic": "^1.1.0", - "has-symbols": "^1.0.1", - "is-arguments": "^1.1.0", + "get-intrinsic": "^1.1.3", + "has-symbols": "^1.0.3", + "is-arguments": "^1.1.1", "is-map": "^2.0.2", "is-set": "^2.0.2", - "is-string": "^1.0.5", - "isarray": "^2.0.5" + "is-string": "^1.0.7", + "isarray": "^2.0.5", + "stop-iteration-iterator": "^1.0.0" }, "funding": { "url": "https://github.com/sponsors/ljharb" } }, + "node_modules/es-set-tostringtag": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/es-set-tostringtag/-/es-set-tostringtag-2.0.1.tgz", + "integrity": "sha512-g3OMbtlwY3QewlqAiMLI47KywjWZoEytKr8pf6iTC8uJq5bIAH52Z9pnQ8pVL6whrCto53JZDuUIsifGeLorTg==", + "dev": true, + "dependencies": { + "get-intrinsic": "^1.1.3", + "has": "^1.0.3", + "has-tostringtag": "^1.0.0" + }, + "engines": { + "node": ">= 0.4" + } + }, "node_modules/es-to-primitive": { "version": "1.2.1", "resolved": "https://registry.npmjs.org/es-to-primitive/-/es-to-primitive-1.2.1.tgz", @@ -2611,12 +2690,12 @@ } }, "node_modules/escape-string-regexp": { - "version": "4.0.0", - "resolved": "https://registry.npmjs.org/escape-string-regexp/-/escape-string-regexp-4.0.0.tgz", - "integrity": "sha512-TtpcNJ3XAzx3Gq8sWRzJaVajRs0uVxA2YAkdb1jm2YkPz4G6egUFAyA3n5vtEIZefPk5Wa4UXbKuS5fKkJWdgA==", + "version": "5.0.0", + "resolved": "https://registry.npmjs.org/escape-string-regexp/-/escape-string-regexp-5.0.0.tgz", + "integrity": "sha512-/veY75JbMK4j1yjvuUxuVsiS/hr/4iHs9FTT6cgTexxdE0Ly/glccBAkloH/DofkjRbZU3bnoj38mOmhkZ0lHw==", "dev": true, "engines": { - "node": ">=10" + "node": ">=12" }, "funding": { "url": "https://github.com/sponsors/sindresorhus" @@ -2696,13 +2775,13 @@ } }, "node_modules/eslint": { - "version": "8.26.0", - "resolved": "https://registry.npmjs.org/eslint/-/eslint-8.26.0.tgz", - "integrity": "sha512-kzJkpaw1Bfwheq4VXUezFriD1GxszX6dUekM7Z3aC2o4hju+tsR/XyTC3RcoSD7jmy9VkPU3+N6YjVU2e96Oyg==", + "version": "8.32.0", + "resolved": "https://registry.npmjs.org/eslint/-/eslint-8.32.0.tgz", + "integrity": "sha512-nETVXpnthqKPFyuY2FNjz/bEd6nbosRgKbkgS/y1C7LJop96gYHWpiguLecMHQ2XCPxn77DS0P+68WzG6vkZSQ==", "dev": true, "dependencies": { - "@eslint/eslintrc": "^1.3.3", - "@humanwhocodes/config-array": "^0.11.6", + "@eslint/eslintrc": "^1.4.1", + "@humanwhocodes/config-array": "^0.11.8", "@humanwhocodes/module-importer": "^1.0.1", "@nodelib/fs.walk": "^1.2.8", "ajv": "^6.10.0", @@ -2721,7 +2800,7 @@ "file-entry-cache": "^6.0.1", "find-up": "^5.0.0", "glob-parent": "^6.0.2", - "globals": "^13.15.0", + "globals": "^13.19.0", "grapheme-splitter": "^1.0.4", "ignore": "^5.2.0", "import-fresh": "^3.0.0", @@ -2800,6 +2879,58 @@ "node": "^12.22.0 || ^14.17.0 || >=16.0.0" } }, + "node_modules/eslint/node_modules/ansi-regex": { + "version": "5.0.1", + "resolved": "https://registry.npmjs.org/ansi-regex/-/ansi-regex-5.0.1.tgz", + "integrity": "sha512-quJQXlTSUGL2LH9SUXo8VwsY4soanhgo6LNSm84E1LBcE8s3O0wpdiRzyR9z/ZZJMlMWv37qOOb9pdJlMUEKFQ==", + "dev": true, + "engines": { + "node": ">=8" + } + }, + "node_modules/eslint/node_modules/ansi-styles": { + "version": "4.3.0", + "resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-4.3.0.tgz", + "integrity": "sha512-zbB9rCJAT1rbjiVDb2hqKFHNYLxgtk8NURxZ3IZwD3F6NtxbXZQCnnSi1Lkx+IDohdPlFp222wVALIheZJQSEg==", + "dev": true, + "dependencies": { + "color-convert": "^2.0.1" + }, + "engines": { + "node": ">=8" + }, + "funding": { + "url": "https://github.com/chalk/ansi-styles?sponsor=1" + } + }, + "node_modules/eslint/node_modules/chalk": { + "version": "4.1.2", + "resolved": "https://registry.npmjs.org/chalk/-/chalk-4.1.2.tgz", + "integrity": "sha512-oKnbhFyRIXpUuez8iBMmyEa4nbj4IOQyuhc/wy9kY7/WVPcwIO9VA668Pu8RkO7+0G76SLROeyw9CpQ061i4mA==", + "dev": true, + "dependencies": { + "ansi-styles": "^4.1.0", + "supports-color": "^7.1.0" + }, + "engines": { + "node": ">=10" + }, + "funding": { + "url": "https://github.com/chalk/chalk?sponsor=1" + } + }, + "node_modules/eslint/node_modules/escape-string-regexp": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/escape-string-regexp/-/escape-string-regexp-4.0.0.tgz", + "integrity": "sha512-TtpcNJ3XAzx3Gq8sWRzJaVajRs0uVxA2YAkdb1jm2YkPz4G6egUFAyA3n5vtEIZefPk5Wa4UXbKuS5fKkJWdgA==", + "dev": true, + "engines": { + "node": ">=10" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, "node_modules/eslint/node_modules/eslint-scope": { "version": "7.1.1", "resolved": "https://registry.npmjs.org/eslint-scope/-/eslint-scope-7.1.1.tgz", @@ -2822,29 +2953,102 @@ "node": ">=4.0" } }, - "node_modules/espree": { - "version": "9.4.0", - "resolved": "https://registry.npmjs.org/espree/-/espree-9.4.0.tgz", - "integrity": "sha512-DQmnRpLj7f6TgN/NYb0MTzJXL+vJF9h3pHy4JhCIs3zwcgez8xmGg3sXHcEO97BrmO2OSvCwMdfdlyl+E9KjOw==", + "node_modules/eslint/node_modules/find-up": { + "version": "5.0.0", + "resolved": "https://registry.npmjs.org/find-up/-/find-up-5.0.0.tgz", + "integrity": "sha512-78/PXT1wlLLDgTzDs7sjq9hzz0vXD+zn+7wypEe4fXQxCmdmqfGsEPQxmiCSQI3ajFV91bVSsvNtrJRiW6nGng==", "dev": true, "dependencies": { - "acorn": "^8.8.0", - "acorn-jsx": "^5.3.2", - "eslint-visitor-keys": "^3.3.0" + "locate-path": "^6.0.0", + "path-exists": "^4.0.0" }, "engines": { - "node": "^12.22.0 || ^14.17.0 || >=16.0.0" + "node": ">=10" }, "funding": { - "url": "https://opencollective.com/eslint" + "url": "https://github.com/sponsors/sindresorhus" } }, - "node_modules/esprima": { - "version": "4.0.1", - "resolved": "https://registry.npmjs.org/esprima/-/esprima-4.0.1.tgz", - "integrity": "sha512-eGuFFw7Upda+g4p+QHvnW0RyTX/SVeJBDM/gCtMARO0cLuT2HcEKnTPvhjV6aGeqrCB/sbNop0Kszm0jsaWU4A==", + "node_modules/eslint/node_modules/locate-path": { + "version": "6.0.0", + "resolved": "https://registry.npmjs.org/locate-path/-/locate-path-6.0.0.tgz", + "integrity": "sha512-iPZK6eYjbxRu3uB4/WZ3EsEIMJFMqAoopl3R+zuq0UjcAm/MO6KCweDgPfP3elTztoKP3KtnVHxTn2NHBSDVUw==", "dev": true, - "bin": { + "dependencies": { + "p-locate": "^5.0.0" + }, + "engines": { + "node": ">=10" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/eslint/node_modules/p-limit": { + "version": "3.1.0", + "resolved": "https://registry.npmjs.org/p-limit/-/p-limit-3.1.0.tgz", + "integrity": "sha512-TYOanM3wGwNGsZN2cVTYPArw454xnXj5qmWF1bEoAc4+cU/ol7GVh7odevjp1FNHduHc3KZMcFduxU5Xc6uJRQ==", + "dev": true, + "dependencies": { + "yocto-queue": "^0.1.0" + }, + "engines": { + "node": ">=10" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/eslint/node_modules/p-locate": { + "version": "5.0.0", + "resolved": "https://registry.npmjs.org/p-locate/-/p-locate-5.0.0.tgz", + "integrity": "sha512-LaNjtRWUBY++zB5nE/NwcaoMylSPk+S+ZHNB1TzdbMJMny6dynpAGt7X/tl/QYq3TIeE6nxHppbo2LGymrG5Pw==", + "dev": true, + "dependencies": { + "p-limit": "^3.0.2" + }, + "engines": { + "node": ">=10" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/eslint/node_modules/strip-ansi": { + "version": "6.0.1", + "resolved": "https://registry.npmjs.org/strip-ansi/-/strip-ansi-6.0.1.tgz", + "integrity": "sha512-Y38VPSHcqkFrCpFnQ9vuSXmquuv5oXOKpGeT6aGrr3o3Gc9AlVa6JBfUSOCnbxGGZF+/0ooI7KrPuUSztUdU5A==", + "dev": true, + "dependencies": { + "ansi-regex": "^5.0.1" + }, + "engines": { + "node": ">=8" + } + }, + "node_modules/espree": { + "version": "9.4.1", + "resolved": "https://registry.npmjs.org/espree/-/espree-9.4.1.tgz", + "integrity": "sha512-XwctdmTO6SIvCzd9810yyNzIrOrqNYV9Koizx4C/mRhf9uq0o4yHoCEU/670pOxOL/MSraektvSAji79kX90Vg==", + "dev": true, + "dependencies": { + "acorn": "^8.8.0", + "acorn-jsx": "^5.3.2", + "eslint-visitor-keys": "^3.3.0" + }, + "engines": { + "node": "^12.22.0 || ^14.17.0 || >=16.0.0" + }, + "funding": { + "url": "https://opencollective.com/eslint" + } + }, + "node_modules/esprima": { + "version": "4.0.1", + "resolved": "https://registry.npmjs.org/esprima/-/esprima-4.0.1.tgz", + "integrity": "sha512-eGuFFw7Upda+g4p+QHvnW0RyTX/SVeJBDM/gCtMARO0cLuT2HcEKnTPvhjV6aGeqrCB/sbNop0Kszm0jsaWU4A==", + "dev": true, + "bin": { "esparse": "bin/esparse.js", "esvalidate": "bin/esvalidate.js" }, @@ -2935,18 +3139,6 @@ "url": "https://github.com/sindresorhus/execa?sponsor=1" } }, - "node_modules/execa/node_modules/is-stream": { - "version": "3.0.0", - "resolved": "https://registry.npmjs.org/is-stream/-/is-stream-3.0.0.tgz", - "integrity": "sha512-LnQR4bZ9IADDRSkvpqMGvt/tEJWclzklNgSw48V5EAaAeDd6qGvN8ei6k5p0tvxSR171VmGyHuTiAOfxAbr8kA==", - "dev": true, - "engines": { - "node": "^12.20.0 || ^14.13.1 || >=16.0.0" - }, - "funding": { - "url": "https://github.com/sponsors/sindresorhus" - } - }, "node_modules/external-editor": { "version": "3.1.0", "resolved": "https://registry.npmjs.org/external-editor/-/external-editor-3.1.0.tgz", @@ -3008,9 +3200,9 @@ "dev": true }, "node_modules/fastq": { - "version": "1.13.0", - "resolved": "https://registry.npmjs.org/fastq/-/fastq-1.13.0.tgz", - "integrity": "sha512-YpkpUnK8od0o1hmeSc7UUs/eB/vIPWJYjKck2QKIzAf71Vm1AAQ3EbuZB3g2JIy+pg+ERD0vqI79KyZiB2e2Nw==", + "version": "1.15.0", + "resolved": "https://registry.npmjs.org/fastq/-/fastq-1.15.0.tgz", + "integrity": "sha512-wBrocU2LCXXa+lWBt8RoIRD89Fi8OdABODa/kEnyeyjS5aZO5/GNvI5sEINADqP/h8M29UHTHUb53sUu5Ihqdw==", "dev": true, "dependencies": { "reusify": "^1.0.4" @@ -3055,30 +3247,6 @@ "url": "https://github.com/sponsors/sindresorhus" } }, - "node_modules/figures/node_modules/escape-string-regexp": { - "version": "5.0.0", - "resolved": "https://registry.npmjs.org/escape-string-regexp/-/escape-string-regexp-5.0.0.tgz", - "integrity": "sha512-/veY75JbMK4j1yjvuUxuVsiS/hr/4iHs9FTT6cgTexxdE0Ly/glccBAkloH/DofkjRbZU3bnoj38mOmhkZ0lHw==", - "dev": true, - "engines": { - "node": ">=12" - }, - "funding": { - "url": "https://github.com/sponsors/sindresorhus" - } - }, - "node_modules/figures/node_modules/is-unicode-supported": { - "version": "1.3.0", - "resolved": "https://registry.npmjs.org/is-unicode-supported/-/is-unicode-supported-1.3.0.tgz", - "integrity": "sha512-43r2mRvz+8JRIKnWJ+3j8JtjRKZ6GmjzfaE/qiBJnikNnYv/6bagRJ1kUhNk8R5EX/GkobD+r+sfxCPJsiKBLQ==", - "dev": true, - "engines": { - "node": ">=12" - }, - "funding": { - "url": "https://github.com/sponsors/sindresorhus" - } - }, "node_modules/file-entry-cache": { "version": "6.0.1", "resolved": "https://registry.npmjs.org/file-entry-cache/-/file-entry-cache-6.0.1.tgz", @@ -3156,19 +3324,16 @@ } }, "node_modules/find-up": { - "version": "5.0.0", - "resolved": "https://registry.npmjs.org/find-up/-/find-up-5.0.0.tgz", - "integrity": "sha512-78/PXT1wlLLDgTzDs7sjq9hzz0vXD+zn+7wypEe4fXQxCmdmqfGsEPQxmiCSQI3ajFV91bVSsvNtrJRiW6nGng==", + "version": "4.1.0", + "resolved": "https://registry.npmjs.org/find-up/-/find-up-4.1.0.tgz", + "integrity": "sha512-PpOwAdQ/YlXQ2vj8a3h8IipDuYRi3wceVQQGYWxNINccq40Anw7BlsEXCMbt1Zt+OLA6Fq9suIpIWD0OsnISlw==", "dev": true, "dependencies": { - "locate-path": "^6.0.0", + "locate-path": "^5.0.0", "path-exists": "^4.0.0" }, "engines": { - "node": ">=10" - }, - "funding": { - "url": "https://github.com/sponsors/sindresorhus" + "node": ">=8" } }, "node_modules/flat": { @@ -3199,6 +3364,15 @@ "integrity": "sha512-5nqDSxl8nn5BSNxyR3n4I6eDmbolI6WT+QqR547RwxQapgjQBmtktdP+HTBb/a/zLsbzERTONyUB5pefh5TtjQ==", "dev": true }, + "node_modules/for-each": { + "version": "0.3.3", + "resolved": "https://registry.npmjs.org/for-each/-/for-each-0.3.3.tgz", + "integrity": "sha512-jqYfLp7mo9vIyQf8ykW2v7A+2N4QjeCeI5+Dz9XraiO1ign81wjiH7Fb9vSOWvQfNtmSa4H2RoQTrrXivdUZmw==", + "dev": true, + "dependencies": { + "is-callable": "^1.1.3" + } + }, "node_modules/foreground-child": { "version": "2.0.0", "resolved": "https://registry.npmjs.org/foreground-child/-/foreground-child-2.0.0.tgz", @@ -3212,24 +3386,10 @@ "node": ">=8.0.0" } }, - "node_modules/form-data": { - "version": "4.0.0", - "resolved": "https://registry.npmjs.org/form-data/-/form-data-4.0.0.tgz", - "integrity": "sha512-ETEklSGi5t0QMZuiXoA/Q6vcnxcLQP5vdugSpuAyi6SVGi2clPPp+xgEhuMaHC+zGgn31Kd235W35f7Hykkaww==", - "dev": true, - "dependencies": { - "asynckit": "^0.4.0", - "combined-stream": "^1.0.8", - "mime-types": "^2.1.12" - }, - "engines": { - "node": ">= 6" - } - }, "node_modules/form-data-encoder": { - "version": "2.1.3", - "resolved": "https://registry.npmjs.org/form-data-encoder/-/form-data-encoder-2.1.3.tgz", - "integrity": "sha512-KqU0nnPMgIJcCOFTNJFEA8epcseEaoox4XZffTgy8jlI6pL/5EFyR54NRG7CnCJN0biY7q52DO3MH6/sJ/TKlQ==", + "version": "2.1.4", + "resolved": "https://registry.npmjs.org/form-data-encoder/-/form-data-encoder-2.1.4.tgz", + "integrity": "sha512-yDYSgNMraqvnxiEXO4hi88+YZxaHC6QKzb5N84iRCTDeRO7ZALpir/lVmf/uXUhnwUr2O4HU8s/n6x+yNjQkHw==", "dev": true, "engines": { "node": ">= 14.17" @@ -3475,14 +3635,14 @@ } }, "node_modules/gh-pages": { - "version": "4.0.0", - "resolved": "https://registry.npmjs.org/gh-pages/-/gh-pages-4.0.0.tgz", - "integrity": "sha512-p8S0T3aGJc68MtwOcZusul5qPSNZCalap3NWbhRUZYu1YOdp+EjZ+4kPmRM8h3NNRdqw00yuevRjlkuSzCn7iQ==", + "version": "5.0.0", + "resolved": "https://registry.npmjs.org/gh-pages/-/gh-pages-5.0.0.tgz", + "integrity": "sha512-Nqp1SjkPIB94Xw/3yYNTUL+G2dxlhjvv1zeN/4kMC1jfViTEqhtVz/Ba1zSXHuvXCN9ADNS1dN4r5/J/nZWEQQ==", "dev": true, "dependencies": { - "async": "^2.6.1", + "async": "^3.2.4", "commander": "^2.18.0", - "email-addresses": "^3.0.1", + "email-addresses": "^5.0.0", "filenamify": "^4.3.0", "find-cache-dir": "^3.3.1", "fs-extra": "^8.1.0", @@ -3496,34 +3656,6 @@ "node": ">=10" } }, - "node_modules/gh-pages/node_modules/array-union": { - "version": "1.0.2", - "resolved": "https://registry.npmjs.org/array-union/-/array-union-1.0.2.tgz", - "integrity": "sha512-Dxr6QJj/RdU/hCaBjOfxW+q6lyuVE6JFWIrAUpuOOhoJJoQ99cUn3igRaHVB5P9WrgFVN0FfArM3x0cueOU8ng==", - "dev": true, - "dependencies": { - "array-uniq": "^1.0.1" - }, - "engines": { - "node": ">=0.10.0" - } - }, - "node_modules/gh-pages/node_modules/globby": { - "version": "6.1.0", - "resolved": "https://registry.npmjs.org/globby/-/globby-6.1.0.tgz", - "integrity": "sha512-KVbFv2TQtbzCoxAnfD6JcHZTYCzyliEaaeM/gH8qQdkKr5s0OP9scEgvdcngyk7AVdY6YVW/TJHd+lQ/Df3Daw==", - "dev": true, - "dependencies": { - "array-union": "^1.0.1", - "glob": "^7.0.3", - "object-assign": "^4.0.1", - "pify": "^2.0.0", - "pinkie-promise": "^2.0.0" - }, - "engines": { - "node": ">=0.10.0" - } - }, "node_modules/git-up": { "version": "7.0.0", "resolved": "https://registry.npmjs.org/git-up/-/git-up-7.0.0.tgz", @@ -3544,15 +3676,15 @@ } }, "node_modules/glob": { - "version": "7.2.0", - "resolved": "https://registry.npmjs.org/glob/-/glob-7.2.0.tgz", - "integrity": "sha512-lmLf6gtyrPq8tTjSmrO94wBeQbFR3HbLHbuyD69wuyQkImp2hWqMGB47OX65FBkPffO641IP9jWa1z4ivqG26Q==", + "version": "7.2.3", + "resolved": "https://registry.npmjs.org/glob/-/glob-7.2.3.tgz", + "integrity": "sha512-nFR0zLpU2YCaRxwoCJvL6UvCH2JFyFVIvwTLsIf21AuHlMskA1hhTdk+LlYJtOlYt9v6dvszD2BGRqBL+iQK9Q==", "dev": true, "dependencies": { "fs.realpath": "^1.0.0", "inflight": "^1.0.4", "inherits": "2", - "minimatch": "^3.0.4", + "minimatch": "^3.1.1", "once": "^1.3.0", "path-is-absolute": "^1.0.0" }, @@ -3576,9 +3708,9 @@ } }, "node_modules/global-dirs": { - "version": "3.0.0", - "resolved": "https://registry.npmjs.org/global-dirs/-/global-dirs-3.0.0.tgz", - "integrity": "sha512-v8ho2DS5RiCjftj1nD9NmnfaOzTdud7RRnVd9kFNOjqZbISlx5DQ+OrTkywgd0dIt7oFCvKetZSHoHcP3sDdiA==", + "version": "3.0.1", + "resolved": "https://registry.npmjs.org/global-dirs/-/global-dirs-3.0.1.tgz", + "integrity": "sha512-NBcGGFbBA9s1VzD41QXDG+3++t9Mn5t1FpLdhESY6oKY4gYTFpX4wO3sqGUa0Srjtbfj3szX0RnemmrVRUdULA==", "dev": true, "dependencies": { "ini": "2.0.0" @@ -3591,9 +3723,9 @@ } }, "node_modules/globals": { - "version": "13.17.0", - "resolved": "https://registry.npmjs.org/globals/-/globals-13.17.0.tgz", - "integrity": "sha512-1C+6nQRb1GwGMKm2dH/E7enFAMxGTmGI7/dEdhy/DNelv85w9B72t3uc5frtMNXIbzrarJJ/lTCjcaZwbLJmyw==", + "version": "13.19.0", + "resolved": "https://registry.npmjs.org/globals/-/globals-13.19.0.tgz", + "integrity": "sha512-dkQ957uSRWHw7CFXLUtUHQI3g3aWApYhfNR2O6jn/907riyTYKVBmxYVROkBcY614FSSeSJh7Xm7SrUWCxvJMQ==", "dev": true, "dependencies": { "type-fest": "^0.20.2" @@ -3605,30 +3737,65 @@ "url": "https://github.com/sponsors/sindresorhus" } }, + "node_modules/globals/node_modules/type-fest": { + "version": "0.20.2", + "resolved": "https://registry.npmjs.org/type-fest/-/type-fest-0.20.2.tgz", + "integrity": "sha512-Ne+eE4r0/iWnpAxD852z3A+N0Bt5RN//NjJwRd2VFHEmrywxf5vsZlh4R6lixl6B+wz/8d+maTSAkN1FIkI3LQ==", + "dev": true, + "engines": { + "node": ">=10" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/globalthis": { + "version": "1.0.3", + "resolved": "https://registry.npmjs.org/globalthis/-/globalthis-1.0.3.tgz", + "integrity": "sha512-sFdI5LyBiNTHjRd7cGPWapiHWMOXKyuBNX/cWJ3NfzrZQVa8GI/8cofCl74AOVqq9W5kNmguTIzJ/1s2gyI9wA==", + "dev": true, + "dependencies": { + "define-properties": "^1.1.3" + }, + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, "node_modules/globby": { - "version": "11.1.0", - "resolved": "https://registry.npmjs.org/globby/-/globby-11.1.0.tgz", - "integrity": "sha512-jhIXaOzy1sb8IyocaruWSn1TjmnBVs8Ayhcy83rmxNJ8q2uWKCAj3CnJY+KpGSXCueAPc0i05kVvVKtP1t9S3g==", + "version": "6.1.0", + "resolved": "https://registry.npmjs.org/globby/-/globby-6.1.0.tgz", + "integrity": "sha512-KVbFv2TQtbzCoxAnfD6JcHZTYCzyliEaaeM/gH8qQdkKr5s0OP9scEgvdcngyk7AVdY6YVW/TJHd+lQ/Df3Daw==", "dev": true, "dependencies": { - "array-union": "^2.1.0", - "dir-glob": "^3.0.1", - "fast-glob": "^3.2.9", - "ignore": "^5.2.0", - "merge2": "^1.4.1", - "slash": "^3.0.0" + "array-union": "^1.0.1", + "glob": "^7.0.3", + "object-assign": "^4.0.1", + "pify": "^2.0.0", + "pinkie-promise": "^2.0.0" }, "engines": { - "node": ">=10" + "node": ">=0.10.0" + } + }, + "node_modules/gopd": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/gopd/-/gopd-1.0.1.tgz", + "integrity": "sha512-d65bNlIadxvpb/A2abVdlqKqV563juRnZ1Wtk6s1sIR8uNsXR70xqIzVqxVf1eTqDunwT2MkczEeaezCKTZhwA==", + "dev": true, + "dependencies": { + "get-intrinsic": "^1.1.3" }, "funding": { - "url": "https://github.com/sponsors/sindresorhus" + "url": "https://github.com/sponsors/ljharb" } }, "node_modules/got": { - "version": "12.5.1", - "resolved": "https://registry.npmjs.org/got/-/got-12.5.1.tgz", - "integrity": "sha512-sD16AK8cCyUoPtKr/NMvLTFFa+T3i3S+zoiuvhq0HP2YiqBZA9AtlBjAdsQBsLBK7slPuvmfE0OxhGi7N5dD4w==", + "version": "12.5.3", + "resolved": "https://registry.npmjs.org/got/-/got-12.5.3.tgz", + "integrity": "sha512-8wKnb9MGU8IPGRIo+/ukTy9XLJBwDiCpIf5TVzQ9Cpol50eMTpBq2GAuDsuDIz7hTYmZgMgC1e9ydr6kSDWs3w==", "dev": true, "dependencies": { "@sindresorhus/is": "^5.2.0", @@ -3704,6 +3871,18 @@ "url": "https://github.com/sponsors/ljharb" } }, + "node_modules/has-proto": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/has-proto/-/has-proto-1.0.1.tgz", + "integrity": "sha512-7qE+iP+O+bgF9clE5+UoBFzE65mlBiVj3tKCrlNQ0Ogwm0BjpT/gK4SlLYDMybDh5I3TCTKnPPa0oMG7JDYrhg==", + "dev": true, + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, "node_modules/has-symbols": { "version": "1.0.3", "resolved": "https://registry.npmjs.org/has-symbols/-/has-symbols-1.0.3.tgz", @@ -3759,6 +3938,18 @@ "url": "https://github.com/sponsors/sindresorhus" } }, + "node_modules/hasha/node_modules/is-stream": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/is-stream/-/is-stream-2.0.1.tgz", + "integrity": "sha512-hFoiJiTl63nn+kstHGBtewWSKnQLpyb155KHheA1l39uvtO9nWIop1p3udqPcUd/xbF1VLMO4n7OI6p7RbngDg==", + "dev": true, + "engines": { + "node": ">=8" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, "node_modules/hasha/node_modules/type-fest": { "version": "0.8.1", "resolved": "https://registry.npmjs.org/type-fest/-/type-fest-0.8.1.tgz", @@ -3820,9 +4011,9 @@ } }, "node_modules/http2-wrapper": { - "version": "2.1.11", - "resolved": "https://registry.npmjs.org/http2-wrapper/-/http2-wrapper-2.1.11.tgz", - "integrity": "sha512-aNAk5JzLturWEUiuhAN73Jcbq96R7rTitAoXV54FYMatvihnpD2+6PUgU4ce3D/m5VDbw+F5CsyKSF176ptitQ==", + "version": "2.2.0", + "resolved": "https://registry.npmjs.org/http2-wrapper/-/http2-wrapper-2.2.0.tgz", + "integrity": "sha512-kZB0wxMo0sh1PehyjJUWRFEd99KC5TLjZ2cULC4f9iqJBAmKQQXEICjxl5iPJRwP40dpeHFqqhm7tYCvODpqpQ==", "dev": true, "dependencies": { "quick-lru": "^5.1.1", @@ -3887,9 +4078,9 @@ ] }, "node_modules/ignore": { - "version": "5.2.0", - "resolved": "https://registry.npmjs.org/ignore/-/ignore-5.2.0.tgz", - "integrity": "sha512-CmxgYGiEPCLhfLnpPp1MoRmifwEIOgjcHXxOBjv7mY96c+eWScsOP9c112ZyLdWHi0FxHjI+4uVhKYp/gcdRmQ==", + "version": "5.2.4", + "resolved": "https://registry.npmjs.org/ignore/-/ignore-5.2.4.tgz", + "integrity": "sha512-MAb38BcSbH0eHNBxn7ql2NH/kX33OkB3lZ1BNdh7ENeRChHTYsTvWrMubiIAMNS2llXEEgZ1MUOBtXChP3kaFQ==", "dev": true, "engines": { "node": ">= 4" @@ -3964,13 +4155,13 @@ } }, "node_modules/inquirer": { - "version": "9.1.2", - "resolved": "https://registry.npmjs.org/inquirer/-/inquirer-9.1.2.tgz", - "integrity": "sha512-Hj2Ml1WpxKJU2npP2Rj0OURGkHV+GtNW2CwFdHDiXlqUBAUrWTcZHxCkFywX/XHzOS7wrG/kExgJFbUkVgyHzg==", + "version": "9.1.4", + "resolved": "https://registry.npmjs.org/inquirer/-/inquirer-9.1.4.tgz", + "integrity": "sha512-9hiJxE5gkK/cM2d1mTEnuurGTAoHebbkX0BYl3h7iEg7FYfuNIom+nDfBCSWtvSnoSrWCeBxqqBZu26xdlJlXA==", "dev": true, "dependencies": { - "ansi-escapes": "^5.0.0", - "chalk": "^5.0.1", + "ansi-escapes": "^6.0.0", + "chalk": "^5.1.2", "cli-cursor": "^4.0.0", "cli-width": "^4.0.0", "external-editor": "^3.0.3", @@ -3979,7 +4170,7 @@ "mute-stream": "0.0.8", "ora": "^6.1.2", "run-async": "^2.4.0", - "rxjs": "^7.5.6", + "rxjs": "^7.5.7", "string-width": "^5.1.2", "strip-ansi": "^7.0.1", "through": "^2.3.6", @@ -3989,63 +4180,24 @@ "node": ">=12.0.0" } }, - "node_modules/inquirer/node_modules/ansi-regex": { - "version": "6.0.1", - "resolved": "https://registry.npmjs.org/ansi-regex/-/ansi-regex-6.0.1.tgz", - "integrity": "sha512-n5M855fKb2SsfMIiFFoVrABHJC8QtHwVx+mHWP3QcEqBHYienj5dHSgjbxtC0WEZXYt4wcD6zrQElDPhFuZgfA==", + "node_modules/internal-slot": { + "version": "1.0.4", + "resolved": "https://registry.npmjs.org/internal-slot/-/internal-slot-1.0.4.tgz", + "integrity": "sha512-tA8URYccNzMo94s5MQZgH8NB/XTa6HsOo0MLfXTKKEnHVVdegzaQoFZ7Jp44bdvLvY2waT5dc+j5ICEswhi7UQ==", "dev": true, - "engines": { - "node": ">=12" + "dependencies": { + "get-intrinsic": "^1.1.3", + "has": "^1.0.3", + "side-channel": "^1.0.4" }, - "funding": { - "url": "https://github.com/chalk/ansi-regex?sponsor=1" + "engines": { + "node": ">= 0.4" } }, - "node_modules/inquirer/node_modules/chalk": { - "version": "5.1.2", - "resolved": "https://registry.npmjs.org/chalk/-/chalk-5.1.2.tgz", - "integrity": "sha512-E5CkT4jWURs1Vy5qGJye+XwCkNj7Od3Af7CP6SujMetSMkLs8Do2RWJK5yx1wamHV/op8Rz+9rltjaTQWDnEFQ==", - "dev": true, - "engines": { - "node": "^12.17.0 || ^14.13 || >=16.0.0" - }, - "funding": { - "url": "https://github.com/chalk/chalk?sponsor=1" - } - }, - "node_modules/inquirer/node_modules/strip-ansi": { - "version": "7.0.1", - "resolved": "https://registry.npmjs.org/strip-ansi/-/strip-ansi-7.0.1.tgz", - "integrity": "sha512-cXNxvT8dFNRVfhVME3JAe98mkXDYN2O1l7jmcwMnOslDeESg1rF/OZMtK0nRAhiari1unG5cD4jG3rapUAkLbw==", - "dev": true, - "dependencies": { - "ansi-regex": "^6.0.1" - }, - "engines": { - "node": ">=12" - }, - "funding": { - "url": "https://github.com/chalk/strip-ansi?sponsor=1" - } - }, - "node_modules/internal-slot": { - "version": "1.0.3", - "resolved": "https://registry.npmjs.org/internal-slot/-/internal-slot-1.0.3.tgz", - "integrity": "sha512-O0DB1JC/sPyZl7cIo78n5dR7eUSwwpYPiXRhTzNxZVAMUuB8vlnRFyLxdrVToks6XPLVnFfbzaVd5WLjhgg+vA==", - "dev": true, - "dependencies": { - "get-intrinsic": "^1.1.0", - "has": "^1.0.3", - "side-channel": "^1.0.4" - }, - "engines": { - "node": ">= 0.4" - } - }, - "node_modules/interpret": { - "version": "1.4.0", - "resolved": "https://registry.npmjs.org/interpret/-/interpret-1.4.0.tgz", - "integrity": "sha512-agE4QfB2Lkp9uICn7BAqoscw4SZP9kTE2hxiFI3jBPmXJfdqiahTbUuKGsMoN2GtqL9AxhYioAcVvgsb1HvRbA==", + "node_modules/interpret": { + "version": "1.4.0", + "resolved": "https://registry.npmjs.org/interpret/-/interpret-1.4.0.tgz", + "integrity": "sha512-agE4QfB2Lkp9uICn7BAqoscw4SZP9kTE2hxiFI3jBPmXJfdqiahTbUuKGsMoN2GtqL9AxhYioAcVvgsb1HvRbA==", "dev": true, "engines": { "node": ">= 0.10" @@ -4073,6 +4225,20 @@ "url": "https://github.com/sponsors/ljharb" } }, + "node_modules/is-array-buffer": { + "version": "3.0.1", + "resolved": "https://registry.npmjs.org/is-array-buffer/-/is-array-buffer-3.0.1.tgz", + "integrity": "sha512-ASfLknmY8Xa2XtB4wmbz13Wu202baeA18cJBCeCy0wXUHZF0IPyVEXqKEcd+t2fNSLLL1vC6k7lxZEojNbISXQ==", + "dev": true, + "dependencies": { + "call-bind": "^1.0.2", + "get-intrinsic": "^1.1.3", + "is-typed-array": "^1.1.10" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, "node_modules/is-arrayish": { "version": "0.2.1", "resolved": "https://registry.npmjs.org/is-arrayish/-/is-arrayish-0.2.1.tgz", @@ -4383,12 +4549,12 @@ } }, "node_modules/is-stream": { - "version": "2.0.1", - "resolved": "https://registry.npmjs.org/is-stream/-/is-stream-2.0.1.tgz", - "integrity": "sha512-hFoiJiTl63nn+kstHGBtewWSKnQLpyb155KHheA1l39uvtO9nWIop1p3udqPcUd/xbF1VLMO4n7OI6p7RbngDg==", + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/is-stream/-/is-stream-3.0.0.tgz", + "integrity": "sha512-LnQR4bZ9IADDRSkvpqMGvt/tEJWclzklNgSw48V5EAaAeDd6qGvN8ei6k5p0tvxSR171VmGyHuTiAOfxAbr8kA==", "dev": true, "engines": { - "node": ">=8" + "node": "^12.20.0 || ^14.13.1 || >=16.0.0" }, "funding": { "url": "https://github.com/sponsors/sindresorhus" @@ -4424,6 +4590,25 @@ "url": "https://github.com/sponsors/ljharb" } }, + "node_modules/is-typed-array": { + "version": "1.1.10", + "resolved": "https://registry.npmjs.org/is-typed-array/-/is-typed-array-1.1.10.tgz", + "integrity": "sha512-PJqgEHiWZvMpaFZ3uTc8kHPM4+4ADTlDniuQL7cU/UDA0Ql7F70yGfHph3cLNe+c9toaigv+DFzTJKhc2CtO6A==", + "dev": true, + "dependencies": { + "available-typed-arrays": "^1.0.5", + "call-bind": "^1.0.2", + "for-each": "^0.3.3", + "gopd": "^1.0.1", + "has-tostringtag": "^1.0.0" + }, + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, "node_modules/is-typedarray": { "version": "1.0.0", "resolved": "https://registry.npmjs.org/is-typedarray/-/is-typedarray-1.0.0.tgz", @@ -4431,12 +4616,12 @@ "dev": true }, "node_modules/is-unicode-supported": { - "version": "0.1.0", - "resolved": "https://registry.npmjs.org/is-unicode-supported/-/is-unicode-supported-0.1.0.tgz", - "integrity": "sha512-knxG2q4UC3u8stRGyAVJCOdxFmv5DZiRcdlIaAQXAbSfJya+OhopNotLQrstBhququ4ZpuKbDc/8S6mgXgPFPw==", + "version": "1.3.0", + "resolved": "https://registry.npmjs.org/is-unicode-supported/-/is-unicode-supported-1.3.0.tgz", + "integrity": "sha512-43r2mRvz+8JRIKnWJ+3j8JtjRKZ6GmjzfaE/qiBJnikNnYv/6bagRJ1kUhNk8R5EX/GkobD+r+sfxCPJsiKBLQ==", "dev": true, "engines": { - "node": ">=10" + "node": ">=12" }, "funding": { "url": "https://github.com/sponsors/sindresorhus" @@ -4476,9 +4661,9 @@ } }, "node_modules/is-yarn-global": { - "version": "0.4.0", - "resolved": "https://registry.npmjs.org/is-yarn-global/-/is-yarn-global-0.4.0.tgz", - "integrity": "sha512-HneQBCrXGBy15QnaDfcn6OLoU8AQPAa0Qn0IeJR/QCo4E8dNZaGGwxpCwWyEBQC5QvFonP8d6t60iGpAHVAfNA==", + "version": "0.4.1", + "resolved": "https://registry.npmjs.org/is-yarn-global/-/is-yarn-global-0.4.1.tgz", + "integrity": "sha512-/kppl+R+LO5VmhYSEWARUFjodS25D68gvj8W7z0I7OWhUla5xWu8KL6CtB2V0R6yqhnRgbcaREMr4EEM6htLPQ==", "dev": true, "engines": { "node": ">=12" @@ -4532,15 +4717,6 @@ "node": ">=8" } }, - "node_modules/istanbul-lib-instrument/node_modules/semver": { - "version": "6.3.0", - "resolved": "https://registry.npmjs.org/semver/-/semver-6.3.0.tgz", - "integrity": "sha512-b39TBaTSfV6yBrapU89p5fKekE2m/NwnDocOVruQFS1/veMgdzuPcnOM34M6CwxW8jH/lxEa5rBoDeUwu5HHTw==", - "dev": true, - "bin": { - "semver": "bin/semver.js" - } - }, "node_modules/istanbul-lib-processinfo": { "version": "2.0.3", "resolved": "https://registry.npmjs.org/istanbul-lib-processinfo/-/istanbul-lib-processinfo-2.0.3.tgz", @@ -4622,10 +4798,14 @@ } }, "node_modules/js-sdsl": { - "version": "4.1.5", - "resolved": "https://registry.npmjs.org/js-sdsl/-/js-sdsl-4.1.5.tgz", - "integrity": "sha512-08bOAKweV2NUC1wqTtf3qZlnpOX/R2DU9ikpjOHs0H+ibQv3zpncVQg6um4uYtRtrwIX8M4Nh3ytK4HGlYAq7Q==", - "dev": true + "version": "4.2.0", + "resolved": "https://registry.npmjs.org/js-sdsl/-/js-sdsl-4.2.0.tgz", + "integrity": "sha512-dyBIzQBDkCqCu+0upx25Y2jGdbTGxE9fshMsCdK0ViOongpV+n5tXRcZY9v7CaVQ79AGS9KA1KHtojxiM7aXSQ==", + "dev": true, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/js-sdsl" + } }, "node_modules/js-tokens": { "version": "4.0.0", @@ -4682,9 +4862,9 @@ "dev": true }, "node_modules/json5": { - "version": "2.2.1", - "resolved": "https://registry.npmjs.org/json5/-/json5-2.2.1.tgz", - "integrity": "sha512-1hqLFMSrGHRHxav9q9gNjJ5EXznIxGVO09xQRrwplcS8qs28pZ8s8hupZAmqDwZUmVZ2Qb2jnyPOWcDH8m8dlA==", + "version": "2.2.3", + "resolved": "https://registry.npmjs.org/json5/-/json5-2.2.3.tgz", + "integrity": "sha512-XmOWe7eyHYH14cLdVPoyg+GOH3rYX++KpzrylJwSW98t3Nk+U8XOl8FWKOgwtzdb8lXGf6zYwDUzeHMWfxasyg==", "dev": true, "bin": { "json5": "lib/cli.js" @@ -4715,9 +4895,9 @@ "dev": true }, "node_modules/keyv": { - "version": "4.5.0", - "resolved": "https://registry.npmjs.org/keyv/-/keyv-4.5.0.tgz", - "integrity": "sha512-2YvuMsA+jnFGtBareKqgANOEKe1mk3HKiXu2fRmAfyxG0MJAywNhi5ttWA3PMjl4NmpyjZNbFifR2vNjW1znfA==", + "version": "4.5.2", + "resolved": "https://registry.npmjs.org/keyv/-/keyv-4.5.2.tgz", + "integrity": "sha512-5MHbFaKn8cNSmVW7BYnijeAVlE4cYA/SVkifVgrh7yotnfhKmjuXpDKjrABLnT0SfHWV21P8ow07OGfRrNDg8g==", "dev": true, "dependencies": { "json-buffer": "3.0.1" @@ -4758,18 +4938,15 @@ "dev": true }, "node_modules/locate-path": { - "version": "6.0.0", - "resolved": "https://registry.npmjs.org/locate-path/-/locate-path-6.0.0.tgz", - "integrity": "sha512-iPZK6eYjbxRu3uB4/WZ3EsEIMJFMqAoopl3R+zuq0UjcAm/MO6KCweDgPfP3elTztoKP3KtnVHxTn2NHBSDVUw==", + "version": "5.0.0", + "resolved": "https://registry.npmjs.org/locate-path/-/locate-path-5.0.0.tgz", + "integrity": "sha512-t7hw9pI+WvuwNJXwk5zVHpyhIqzg2qTlklJOf0mVxGSbe3Fp2VieZcduNYjaLDoy6p9uGpQEGWG87WpMKlNq8g==", "dev": true, "dependencies": { - "p-locate": "^5.0.0" + "p-locate": "^4.1.0" }, "engines": { - "node": ">=10" - }, - "funding": { - "url": "https://github.com/sponsors/sindresorhus" + "node": ">=8" } }, "node_modules/lodash": { @@ -4797,16 +4974,16 @@ "dev": true }, "node_modules/log-symbols": { - "version": "4.1.0", - "resolved": "https://registry.npmjs.org/log-symbols/-/log-symbols-4.1.0.tgz", - "integrity": "sha512-8XPvpAA8uyhfteu8pIvQxpJZ7SYYdpUivZpGy6sFsBuKRY/7rQGavedeB8aK+Zkyq6upMFVL/9AW6vOYzfRyLg==", + "version": "5.1.0", + "resolved": "https://registry.npmjs.org/log-symbols/-/log-symbols-5.1.0.tgz", + "integrity": "sha512-l0x2DvrW294C9uDCoQe1VSU4gf529FkSZ6leBl4TiqZH/e+0R7hSfHQBNut2mNygDgHwvYHfFLn6Oxb3VWj2rA==", "dev": true, "dependencies": { - "chalk": "^4.1.0", - "is-unicode-supported": "^0.1.0" + "chalk": "^5.0.0", + "is-unicode-supported": "^1.1.0" }, "engines": { - "node": ">=10" + "node": ">=12" }, "funding": { "url": "https://github.com/sponsors/sindresorhus" @@ -4866,15 +5043,6 @@ "url": "https://github.com/sponsors/sindresorhus" } }, - "node_modules/make-dir/node_modules/semver": { - "version": "6.3.0", - "resolved": "https://registry.npmjs.org/semver/-/semver-6.3.0.tgz", - "integrity": "sha512-b39TBaTSfV6yBrapU89p5fKekE2m/NwnDocOVruQFS1/veMgdzuPcnOM34M6CwxW8jH/lxEa5rBoDeUwu5HHTw==", - "dev": true, - "bin": { - "semver": "bin/semver.js" - } - }, "node_modules/make-error": { "version": "1.3.6", "resolved": "https://registry.npmjs.org/make-error/-/make-error-1.3.6.tgz", @@ -4882,9 +5050,9 @@ "dev": true }, "node_modules/marked": { - "version": "4.1.1", - "resolved": "https://registry.npmjs.org/marked/-/marked-4.1.1.tgz", - "integrity": "sha512-0cNMnTcUJPxbA6uWmCmjWz4NJRe/0Xfk2NhXCUHjew9qJzFN20krFnsUe7QynwqOwa5m1fZ4UDg0ycKFVC0ccw==", + "version": "4.2.12", + "resolved": "https://registry.npmjs.org/marked/-/marked-4.2.12.tgz", + "integrity": "sha512-yr8hSKa3Fv4D3jdZmtMMPghgVt6TWbk86WQaWhDloQjRSQhMMYCAro7jP7VDJrjjdV8pxVxMssXS8B8Y5DZ5aw==", "dev": true, "bin": { "marked": "bin/marked.js" @@ -4988,9 +5156,9 @@ } }, "node_modules/mocha": { - "version": "10.1.0", - "resolved": "https://registry.npmjs.org/mocha/-/mocha-10.1.0.tgz", - "integrity": "sha512-vUF7IYxEoN7XhQpFLxQAEMtE4W91acW4B6En9l97MwE9stL1A9gusXfoHZCLVHDUJ/7V5+lbCM6yMqzo5vNymg==", + "version": "10.2.0", + "resolved": "https://registry.npmjs.org/mocha/-/mocha-10.2.0.tgz", + "integrity": "sha512-IDY7fl/BecMwFHzoqF2sg/SHHANeBoMMXFlS9r0OXKDssYE1M5O43wUY/9BVPeIvfH2zmEbBfseqN9gBQZzXkg==", "dev": true, "dependencies": { "ansi-colors": "4.1.1", @@ -5027,13 +5195,56 @@ "url": "https://opencollective.com/mochajs" } }, - "node_modules/mocha/node_modules/brace-expansion": { - "version": "2.0.1", - "resolved": "https://registry.npmjs.org/brace-expansion/-/brace-expansion-2.0.1.tgz", - "integrity": "sha512-XnAIvQ8eM+kC6aULx6wuQiwVsnzsi9d3WxzV3FpWTGA19F621kwdbsAcFKXgKUHZWsy+mY6iL1sHTxWEFCytDA==", + "node_modules/mocha/node_modules/ansi-regex": { + "version": "5.0.1", + "resolved": "https://registry.npmjs.org/ansi-regex/-/ansi-regex-5.0.1.tgz", + "integrity": "sha512-quJQXlTSUGL2LH9SUXo8VwsY4soanhgo6LNSm84E1LBcE8s3O0wpdiRzyR9z/ZZJMlMWv37qOOb9pdJlMUEKFQ==", + "dev": true, + "engines": { + "node": ">=8" + } + }, + "node_modules/mocha/node_modules/ansi-styles": { + "version": "4.3.0", + "resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-4.3.0.tgz", + "integrity": "sha512-zbB9rCJAT1rbjiVDb2hqKFHNYLxgtk8NURxZ3IZwD3F6NtxbXZQCnnSi1Lkx+IDohdPlFp222wVALIheZJQSEg==", "dev": true, "dependencies": { - "balanced-match": "^1.0.0" + "color-convert": "^2.0.1" + }, + "engines": { + "node": ">=8" + }, + "funding": { + "url": "https://github.com/chalk/ansi-styles?sponsor=1" + } + }, + "node_modules/mocha/node_modules/chalk": { + "version": "4.1.2", + "resolved": "https://registry.npmjs.org/chalk/-/chalk-4.1.2.tgz", + "integrity": "sha512-oKnbhFyRIXpUuez8iBMmyEa4nbj4IOQyuhc/wy9kY7/WVPcwIO9VA668Pu8RkO7+0G76SLROeyw9CpQ061i4mA==", + "dev": true, + "dependencies": { + "ansi-styles": "^4.1.0", + "supports-color": "^7.1.0" + }, + "engines": { + "node": ">=10" + }, + "funding": { + "url": "https://github.com/chalk/chalk?sponsor=1" + } + }, + "node_modules/mocha/node_modules/chalk/node_modules/supports-color": { + "version": "7.2.0", + "resolved": "https://registry.npmjs.org/supports-color/-/supports-color-7.2.0.tgz", + "integrity": "sha512-qpCAvRl9stuOHveKsn7HncJRvv501qIacKzQlO/+Lwxc9+0q2wLyv4Dfvt80/DPn2pqOBsJdDiogXGR9+OvwRw==", + "dev": true, + "dependencies": { + "has-flag": "^4.0.0" + }, + "engines": { + "node": ">=8" } }, "node_modules/mocha/node_modules/cliui": { @@ -5053,74 +5264,237 @@ "integrity": "sha512-MSjYzcWNOA0ewAHpz0MxpYFvwg6yjy1NG3xteoqz644VCo/RPgnr1/GGt+ic3iJTzQ8Eu3TdM14SawnVUmGE6A==", "dev": true }, - "node_modules/mocha/node_modules/minimatch": { - "version": "5.0.1", - "resolved": "https://registry.npmjs.org/minimatch/-/minimatch-5.0.1.tgz", - "integrity": "sha512-nLDxIFRyhDblz3qMuq+SoRZED4+miJ/G+tdDrjkkkRnjAsBexeGpgjLEQ0blJy7rHhR2b93rhQY4SvyWu9v03g==", + "node_modules/mocha/node_modules/escape-string-regexp": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/escape-string-regexp/-/escape-string-regexp-4.0.0.tgz", + "integrity": "sha512-TtpcNJ3XAzx3Gq8sWRzJaVajRs0uVxA2YAkdb1jm2YkPz4G6egUFAyA3n5vtEIZefPk5Wa4UXbKuS5fKkJWdgA==", "dev": true, - "dependencies": { - "brace-expansion": "^2.0.1" - }, "engines": { "node": ">=10" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" } }, - "node_modules/mocha/node_modules/ms": { - "version": "2.1.3", - "resolved": "https://registry.npmjs.org/ms/-/ms-2.1.3.tgz", - "integrity": "sha512-6FlzubTLZG3J2a/NVCAleEhjzq5oxgHyaCU9yYXvcLsvoVaHJq/s5xXI6/XXP6tz7R9xAOtHnSO/tXtF3WRTlA==", - "dev": true - }, - "node_modules/mocha/node_modules/string-width": { - "version": "4.2.3", - "resolved": "https://registry.npmjs.org/string-width/-/string-width-4.2.3.tgz", - "integrity": "sha512-wKyQRQpjJ0sIp62ErSZdGsjMJWsap5oRNihHhu6G7JVO/9jIB6UyevL+tXuOqrng8j/cxKTWyWUwvSTriiZz/g==", + "node_modules/mocha/node_modules/find-up": { + "version": "5.0.0", + "resolved": "https://registry.npmjs.org/find-up/-/find-up-5.0.0.tgz", + "integrity": "sha512-78/PXT1wlLLDgTzDs7sjq9hzz0vXD+zn+7wypEe4fXQxCmdmqfGsEPQxmiCSQI3ajFV91bVSsvNtrJRiW6nGng==", "dev": true, "dependencies": { - "emoji-regex": "^8.0.0", - "is-fullwidth-code-point": "^3.0.0", - "strip-ansi": "^6.0.1" + "locate-path": "^6.0.0", + "path-exists": "^4.0.0" }, "engines": { - "node": ">=8" + "node": ">=10" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" } }, - "node_modules/mocha/node_modules/supports-color": { - "version": "8.1.1", - "resolved": "https://registry.npmjs.org/supports-color/-/supports-color-8.1.1.tgz", - "integrity": "sha512-MpUEN2OodtUzxvKQl72cUF7RQ5EiHsGvSsVG0ia9c5RbWGL2CI4C7EpPS8UTBIplnlzZiNuV56w+FuNxy3ty2Q==", + "node_modules/mocha/node_modules/glob": { + "version": "7.2.0", + "resolved": "https://registry.npmjs.org/glob/-/glob-7.2.0.tgz", + "integrity": "sha512-lmLf6gtyrPq8tTjSmrO94wBeQbFR3HbLHbuyD69wuyQkImp2hWqMGB47OX65FBkPffO641IP9jWa1z4ivqG26Q==", "dev": true, "dependencies": { - "has-flag": "^4.0.0" + "fs.realpath": "^1.0.0", + "inflight": "^1.0.4", + "inherits": "2", + "minimatch": "^3.0.4", + "once": "^1.3.0", + "path-is-absolute": "^1.0.0" }, "engines": { - "node": ">=10" + "node": "*" }, "funding": { - "url": "https://github.com/chalk/supports-color?sponsor=1" + "url": "https://github.com/sponsors/isaacs" } }, - "node_modules/mocha/node_modules/wrap-ansi": { - "version": "7.0.0", - "resolved": "https://registry.npmjs.org/wrap-ansi/-/wrap-ansi-7.0.0.tgz", - "integrity": "sha512-YVGIj2kamLSTxw6NsZjoBxfSwsn0ycdesmc4p+Q21c5zPuZ1pl+NfxVdxPtdHvmNVOQ6XSYG4AUtyt/Fi7D16Q==", + "node_modules/mocha/node_modules/glob/node_modules/minimatch": { + "version": "3.1.2", + "resolved": "https://registry.npmjs.org/minimatch/-/minimatch-3.1.2.tgz", + "integrity": "sha512-J7p63hRiAjw1NDEww1W7i37+ByIrOWO5XQQAzZ3VOcL0PNybwpfmV/N05zFAzwQ9USyEcX6t3UO+K5aqBQOIHw==", "dev": true, "dependencies": { - "ansi-styles": "^4.0.0", - "string-width": "^4.1.0", - "strip-ansi": "^6.0.0" + "brace-expansion": "^1.1.7" }, + "engines": { + "node": "*" + } + }, + "node_modules/mocha/node_modules/is-unicode-supported": { + "version": "0.1.0", + "resolved": "https://registry.npmjs.org/is-unicode-supported/-/is-unicode-supported-0.1.0.tgz", + "integrity": "sha512-knxG2q4UC3u8stRGyAVJCOdxFmv5DZiRcdlIaAQXAbSfJya+OhopNotLQrstBhququ4ZpuKbDc/8S6mgXgPFPw==", + "dev": true, "engines": { "node": ">=10" }, "funding": { - "url": "https://github.com/chalk/wrap-ansi?sponsor=1" + "url": "https://github.com/sponsors/sindresorhus" } }, - "node_modules/mocha/node_modules/yargs": { - "version": "16.2.0", - "resolved": "https://registry.npmjs.org/yargs/-/yargs-16.2.0.tgz", - "integrity": "sha512-D1mvvtDG0L5ft/jGWkLpG1+m0eQxOfaBvTNELraWj22wSVUMWxZUvYgJYcKh6jGGIkJFhH4IZPQhR4TKpc8mBw==", + "node_modules/mocha/node_modules/locate-path": { + "version": "6.0.0", + "resolved": "https://registry.npmjs.org/locate-path/-/locate-path-6.0.0.tgz", + "integrity": "sha512-iPZK6eYjbxRu3uB4/WZ3EsEIMJFMqAoopl3R+zuq0UjcAm/MO6KCweDgPfP3elTztoKP3KtnVHxTn2NHBSDVUw==", + "dev": true, + "dependencies": { + "p-locate": "^5.0.0" + }, + "engines": { + "node": ">=10" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/mocha/node_modules/log-symbols": { + "version": "4.1.0", + "resolved": "https://registry.npmjs.org/log-symbols/-/log-symbols-4.1.0.tgz", + "integrity": "sha512-8XPvpAA8uyhfteu8pIvQxpJZ7SYYdpUivZpGy6sFsBuKRY/7rQGavedeB8aK+Zkyq6upMFVL/9AW6vOYzfRyLg==", + "dev": true, + "dependencies": { + "chalk": "^4.1.0", + "is-unicode-supported": "^0.1.0" + }, + "engines": { + "node": ">=10" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/mocha/node_modules/minimatch": { + "version": "5.0.1", + "resolved": "https://registry.npmjs.org/minimatch/-/minimatch-5.0.1.tgz", + "integrity": "sha512-nLDxIFRyhDblz3qMuq+SoRZED4+miJ/G+tdDrjkkkRnjAsBexeGpgjLEQ0blJy7rHhR2b93rhQY4SvyWu9v03g==", + "dev": true, + "dependencies": { + "brace-expansion": "^2.0.1" + }, + "engines": { + "node": ">=10" + } + }, + "node_modules/mocha/node_modules/minimatch/node_modules/brace-expansion": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/brace-expansion/-/brace-expansion-2.0.1.tgz", + "integrity": "sha512-XnAIvQ8eM+kC6aULx6wuQiwVsnzsi9d3WxzV3FpWTGA19F621kwdbsAcFKXgKUHZWsy+mY6iL1sHTxWEFCytDA==", + "dev": true, + "dependencies": { + "balanced-match": "^1.0.0" + } + }, + "node_modules/mocha/node_modules/ms": { + "version": "2.1.3", + "resolved": "https://registry.npmjs.org/ms/-/ms-2.1.3.tgz", + "integrity": "sha512-6FlzubTLZG3J2a/NVCAleEhjzq5oxgHyaCU9yYXvcLsvoVaHJq/s5xXI6/XXP6tz7R9xAOtHnSO/tXtF3WRTlA==", + "dev": true + }, + "node_modules/mocha/node_modules/p-limit": { + "version": "3.1.0", + "resolved": "https://registry.npmjs.org/p-limit/-/p-limit-3.1.0.tgz", + "integrity": "sha512-TYOanM3wGwNGsZN2cVTYPArw454xnXj5qmWF1bEoAc4+cU/ol7GVh7odevjp1FNHduHc3KZMcFduxU5Xc6uJRQ==", + "dev": true, + "dependencies": { + "yocto-queue": "^0.1.0" + }, + "engines": { + "node": ">=10" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/mocha/node_modules/p-locate": { + "version": "5.0.0", + "resolved": "https://registry.npmjs.org/p-locate/-/p-locate-5.0.0.tgz", + "integrity": "sha512-LaNjtRWUBY++zB5nE/NwcaoMylSPk+S+ZHNB1TzdbMJMny6dynpAGt7X/tl/QYq3TIeE6nxHppbo2LGymrG5Pw==", + "dev": true, + "dependencies": { + "p-limit": "^3.0.2" + }, + "engines": { + "node": ">=10" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/mocha/node_modules/string-width": { + "version": "4.2.3", + "resolved": "https://registry.npmjs.org/string-width/-/string-width-4.2.3.tgz", + "integrity": "sha512-wKyQRQpjJ0sIp62ErSZdGsjMJWsap5oRNihHhu6G7JVO/9jIB6UyevL+tXuOqrng8j/cxKTWyWUwvSTriiZz/g==", + "dev": true, + "dependencies": { + "emoji-regex": "^8.0.0", + "is-fullwidth-code-point": "^3.0.0", + "strip-ansi": "^6.0.1" + }, + "engines": { + "node": ">=8" + } + }, + "node_modules/mocha/node_modules/strip-ansi": { + "version": "6.0.1", + "resolved": "https://registry.npmjs.org/strip-ansi/-/strip-ansi-6.0.1.tgz", + "integrity": "sha512-Y38VPSHcqkFrCpFnQ9vuSXmquuv5oXOKpGeT6aGrr3o3Gc9AlVa6JBfUSOCnbxGGZF+/0ooI7KrPuUSztUdU5A==", + "dev": true, + "dependencies": { + "ansi-regex": "^5.0.1" + }, + "engines": { + "node": ">=8" + } + }, + "node_modules/mocha/node_modules/supports-color": { + "version": "8.1.1", + "resolved": "https://registry.npmjs.org/supports-color/-/supports-color-8.1.1.tgz", + "integrity": "sha512-MpUEN2OodtUzxvKQl72cUF7RQ5EiHsGvSsVG0ia9c5RbWGL2CI4C7EpPS8UTBIplnlzZiNuV56w+FuNxy3ty2Q==", + "dev": true, + "dependencies": { + "has-flag": "^4.0.0" + }, + "engines": { + "node": ">=10" + }, + "funding": { + "url": "https://github.com/chalk/supports-color?sponsor=1" + } + }, + "node_modules/mocha/node_modules/wrap-ansi": { + "version": "7.0.0", + "resolved": "https://registry.npmjs.org/wrap-ansi/-/wrap-ansi-7.0.0.tgz", + "integrity": "sha512-YVGIj2kamLSTxw6NsZjoBxfSwsn0ycdesmc4p+Q21c5zPuZ1pl+NfxVdxPtdHvmNVOQ6XSYG4AUtyt/Fi7D16Q==", + "dev": true, + "dependencies": { + "ansi-styles": "^4.0.0", + "string-width": "^4.1.0", + "strip-ansi": "^6.0.0" + }, + "engines": { + "node": ">=10" + }, + "funding": { + "url": "https://github.com/chalk/wrap-ansi?sponsor=1" + } + }, + "node_modules/mocha/node_modules/y18n": { + "version": "5.0.8", + "resolved": "https://registry.npmjs.org/y18n/-/y18n-5.0.8.tgz", + "integrity": "sha512-0pfFzegeDWJHJIAmTLRP2DwHjdF5s7jo9tuztdQxAhINCdvS+3nGINqPd00AphqJR/0LhANUS6/+7SCb98YOfA==", + "dev": true, + "engines": { + "node": ">=10" + } + }, + "node_modules/mocha/node_modules/yargs": { + "version": "16.2.0", + "resolved": "https://registry.npmjs.org/yargs/-/yargs-16.2.0.tgz", + "integrity": "sha512-D1mvvtDG0L5ft/jGWkLpG1+m0eQxOfaBvTNELraWj22wSVUMWxZUvYgJYcKh6jGGIkJFhH4IZPQhR4TKpc8mBw==", "dev": true, "dependencies": { "cliui": "^7.0.2", @@ -5135,6 +5509,15 @@ "node": ">=10" } }, + "node_modules/mocha/node_modules/yargs-parser": { + "version": "20.2.4", + "resolved": "https://registry.npmjs.org/yargs-parser/-/yargs-parser-20.2.4.tgz", + "integrity": "sha512-WOkpgNhPTlE73h4VFAFsOnomJVaovO8VqLDzy5saChRBFQFBoMYirowyW+Q9HB4HFF4Z7VZTiG3iSzJJA29yRA==", + "dev": true, + "engines": { + "node": ">=10" + } + }, "node_modules/ms": { "version": "2.1.2", "resolved": "https://registry.npmjs.org/ms/-/ms-2.1.2.tgz", @@ -5165,6 +5548,12 @@ "integrity": "sha512-OWND8ei3VtNC9h7V60qff3SVobHr996CTwgxubgyQYEpg290h9J0buyECNNJexkFm5sOajh5G116RYA1c8ZMSw==", "dev": true }, + "node_modules/natural-compare-lite": { + "version": "1.4.0", + "resolved": "https://registry.npmjs.org/natural-compare-lite/-/natural-compare-lite-1.4.0.tgz", + "integrity": "sha512-Tj+HTDSJJKaZnfiuw+iaF9skdPpTo2GtEly5JHnWV/hfv2Qj/9RKsGISQtLh2ox3l5EAGw487hnBee0sIJ6v2g==", + "dev": true + }, "node_modules/netmask": { "version": "2.0.2", "resolved": "https://registry.npmjs.org/netmask/-/netmask-2.0.2.tgz", @@ -5202,13 +5591,13 @@ } }, "node_modules/nise": { - "version": "5.1.1", - "resolved": "https://registry.npmjs.org/nise/-/nise-5.1.1.tgz", - "integrity": "sha512-yr5kW2THW1AkxVmCnKEh4nbYkJdB3I7LUkiUgOvEkOp414mc2UMaHMA7pjq1nYowhdoJZGwEKGaQVbxfpWj10A==", + "version": "5.1.4", + "resolved": "https://registry.npmjs.org/nise/-/nise-5.1.4.tgz", + "integrity": "sha512-8+Ib8rRJ4L0o3kfmyVCL7gzrohyDe0cMFTBa2d364yIrEGMEoetznKJx899YxjybU6bL9SQkYPSBBs1gyYs8Xg==", "dev": true, "dependencies": { - "@sinonjs/commons": "^1.8.3", - "@sinonjs/fake-timers": ">=5", + "@sinonjs/commons": "^2.0.0", + "@sinonjs/fake-timers": "^10.0.2", "@sinonjs/text-encoding": "^0.7.1", "just-extend": "^4.0.2", "path-to-regexp": "^1.7.0" @@ -5234,9 +5623,9 @@ } }, "node_modules/node-fetch": { - "version": "3.2.10", - "resolved": "https://registry.npmjs.org/node-fetch/-/node-fetch-3.2.10.tgz", - "integrity": "sha512-MhuzNwdURnZ1Cp4XTazr69K0BTizsBroX7Zx3UgDSVcZYKF/6p0CBe4EUb/hLqmzVhl0UpYfgRljQ4yxE+iCxA==", + "version": "3.3.0", + "resolved": "https://registry.npmjs.org/node-fetch/-/node-fetch-3.3.0.tgz", + "integrity": "sha512-BKwRP/O0UvoMKp7GNdwPlObhYGB5DQqwhEDQlNKuoqwVYSxkSZCSbHjnFFmUEtwSKRPU4kNK8PbDYYitwaE3QA==", "dev": true, "dependencies": { "data-uri-to-buffer": "^4.0.0", @@ -5264,9 +5653,9 @@ } }, "node_modules/node-releases": { - "version": "2.0.6", - "resolved": "https://registry.npmjs.org/node-releases/-/node-releases-2.0.6.tgz", - "integrity": "sha512-PiVXnNuFm5+iYkLBNeq5211hvO38y63T0i2KKh2KnUs3RpzJ+JtODFjkD8yjLwnDkTYF1eKXheUwdssR+NRZdg==", + "version": "2.0.8", + "resolved": "https://registry.npmjs.org/node-releases/-/node-releases-2.0.8.tgz", + "integrity": "sha512-dFSmB8fFHEH/s81Xi+Y/15DQY6VHW81nXRj86EMSL3lmuTmK1e+aT4wrFCkTbm+gSwkw4KpX+rT/pMM2c1mF+A==", "dev": true }, "node_modules/normalize-path": { @@ -5279,12 +5668,12 @@ } }, "node_modules/normalize-url": { - "version": "7.2.0", - "resolved": "https://registry.npmjs.org/normalize-url/-/normalize-url-7.2.0.tgz", - "integrity": "sha512-uhXOdZry0L6M2UIo9BTt7FdpBDiAGN/7oItedQwPKh8jh31ZlvC8U9Xl/EJ3aijDHaywXTW3QbZ6LuCocur1YA==", + "version": "8.0.0", + "resolved": "https://registry.npmjs.org/normalize-url/-/normalize-url-8.0.0.tgz", + "integrity": "sha512-uVFpKhj5MheNBJRTiMZ9pE/7hD1QTeEvugSJW/OmLzAp78PB5O6adfMNTvmfKhXBkvCzC+rqifWcVYpGFwTjnw==", "dev": true, "engines": { - "node": ">=12.20" + "node": ">=14.16" }, "funding": { "url": "https://github.com/sponsors/sindresorhus" @@ -5358,75 +5747,6 @@ "node": ">=8.9" } }, - "node_modules/nyc/node_modules/cliui": { - "version": "6.0.0", - "resolved": "https://registry.npmjs.org/cliui/-/cliui-6.0.0.tgz", - "integrity": "sha512-t6wbgtoCXvAzst7QgXxJYqPt0usEfbgQdftEPbLL/cvv6HPE5VgvqCuAIDR0NgU52ds6rFwqrgakNLrHEjCbrQ==", - "dev": true, - "dependencies": { - "string-width": "^4.2.0", - "strip-ansi": "^6.0.0", - "wrap-ansi": "^6.2.0" - } - }, - "node_modules/nyc/node_modules/emoji-regex": { - "version": "8.0.0", - "resolved": "https://registry.npmjs.org/emoji-regex/-/emoji-regex-8.0.0.tgz", - "integrity": "sha512-MSjYzcWNOA0ewAHpz0MxpYFvwg6yjy1NG3xteoqz644VCo/RPgnr1/GGt+ic3iJTzQ8Eu3TdM14SawnVUmGE6A==", - "dev": true - }, - "node_modules/nyc/node_modules/find-up": { - "version": "4.1.0", - "resolved": "https://registry.npmjs.org/find-up/-/find-up-4.1.0.tgz", - "integrity": "sha512-PpOwAdQ/YlXQ2vj8a3h8IipDuYRi3wceVQQGYWxNINccq40Anw7BlsEXCMbt1Zt+OLA6Fq9suIpIWD0OsnISlw==", - "dev": true, - "dependencies": { - "locate-path": "^5.0.0", - "path-exists": "^4.0.0" - }, - "engines": { - "node": ">=8" - } - }, - "node_modules/nyc/node_modules/locate-path": { - "version": "5.0.0", - "resolved": "https://registry.npmjs.org/locate-path/-/locate-path-5.0.0.tgz", - "integrity": "sha512-t7hw9pI+WvuwNJXwk5zVHpyhIqzg2qTlklJOf0mVxGSbe3Fp2VieZcduNYjaLDoy6p9uGpQEGWG87WpMKlNq8g==", - "dev": true, - "dependencies": { - "p-locate": "^4.1.0" - }, - "engines": { - "node": ">=8" - } - }, - "node_modules/nyc/node_modules/p-limit": { - "version": "2.3.0", - "resolved": "https://registry.npmjs.org/p-limit/-/p-limit-2.3.0.tgz", - "integrity": "sha512-//88mFWSJx8lxCzwdAABTJL2MyWB12+eIY7MDL2SqLmAkeKU9qxRvWuSyTjm3FUmpBEMuFfckAIqEaVGUDxb6w==", - "dev": true, - "dependencies": { - "p-try": "^2.0.0" - }, - "engines": { - "node": ">=6" - }, - "funding": { - "url": "https://github.com/sponsors/sindresorhus" - } - }, - "node_modules/nyc/node_modules/p-locate": { - "version": "4.1.0", - "resolved": "https://registry.npmjs.org/p-locate/-/p-locate-4.1.0.tgz", - "integrity": "sha512-R79ZZ/0wAxKGu3oYMlz8jy/kbhsNrS7SKZ7PxEHBgJ5+F2mtFW2fK2cOtBh1cHYkQsbzFV7I+EoRKe6Yt0oK7A==", - "dev": true, - "dependencies": { - "p-limit": "^2.2.0" - }, - "engines": { - "node": ">=8" - } - }, "node_modules/nyc/node_modules/resolve-from": { "version": "5.0.0", "resolved": "https://registry.npmjs.org/resolve-from/-/resolve-from-5.0.0.tgz", @@ -5436,75 +5756,6 @@ "node": ">=8" } }, - "node_modules/nyc/node_modules/string-width": { - "version": "4.2.3", - "resolved": "https://registry.npmjs.org/string-width/-/string-width-4.2.3.tgz", - "integrity": "sha512-wKyQRQpjJ0sIp62ErSZdGsjMJWsap5oRNihHhu6G7JVO/9jIB6UyevL+tXuOqrng8j/cxKTWyWUwvSTriiZz/g==", - "dev": true, - "dependencies": { - "emoji-regex": "^8.0.0", - "is-fullwidth-code-point": "^3.0.0", - "strip-ansi": "^6.0.1" - }, - "engines": { - "node": ">=8" - } - }, - "node_modules/nyc/node_modules/wrap-ansi": { - "version": "6.2.0", - "resolved": "https://registry.npmjs.org/wrap-ansi/-/wrap-ansi-6.2.0.tgz", - "integrity": "sha512-r6lPcBGxZXlIcymEu7InxDMhdW0KDxpLgoFLcguasxCaJ/SOIZwINatK9KY/tf+ZrlywOKU0UDj3ATXUBfxJXA==", - "dev": true, - "dependencies": { - "ansi-styles": "^4.0.0", - "string-width": "^4.1.0", - "strip-ansi": "^6.0.0" - }, - "engines": { - "node": ">=8" - } - }, - "node_modules/nyc/node_modules/y18n": { - "version": "4.0.3", - "resolved": "https://registry.npmjs.org/y18n/-/y18n-4.0.3.tgz", - "integrity": "sha512-JKhqTOwSrqNA1NY5lSztJ1GrBiUodLMmIZuLiDaMRJ+itFd+ABVE8XBjOvIWL+rSqNDC74LCSFmlb/U4UZ4hJQ==", - "dev": true - }, - "node_modules/nyc/node_modules/yargs": { - "version": "15.4.1", - "resolved": "https://registry.npmjs.org/yargs/-/yargs-15.4.1.tgz", - "integrity": "sha512-aePbxDmcYW++PaqBsJ+HYUFwCdv4LVvdnhBy78E57PIor8/OVvhMrADFFEDh8DHDFRv/O9i3lPhsENjO7QX0+A==", - "dev": true, - "dependencies": { - "cliui": "^6.0.0", - "decamelize": "^1.2.0", - "find-up": "^4.1.0", - "get-caller-file": "^2.0.1", - "require-directory": "^2.1.1", - "require-main-filename": "^2.0.0", - "set-blocking": "^2.0.0", - "string-width": "^4.2.0", - "which-module": "^2.0.0", - "y18n": "^4.0.0", - "yargs-parser": "^18.1.2" - }, - "engines": { - "node": ">=8" - } - }, - "node_modules/nyc/node_modules/yargs-parser": { - "version": "18.1.3", - "resolved": "https://registry.npmjs.org/yargs-parser/-/yargs-parser-18.1.3.tgz", - "integrity": "sha512-o50j0JeToy/4K6OZcaQmW6lyXXKhq7csREXcDwk2omFPJEwUNOVtJKvmDr9EI1fAJZUyZcRF7kxGBWmRXudrCQ==", - "dev": true, - "dependencies": { - "camelcase": "^5.0.0", - "decamelize": "^1.2.0" - }, - "engines": { - "node": ">=6" - } - }, "node_modules/object-assign": { "version": "4.1.1", "resolved": "https://registry.npmjs.org/object-assign/-/object-assign-4.1.1.tgz", @@ -5515,9 +5766,9 @@ } }, "node_modules/object-inspect": { - "version": "1.12.2", - "resolved": "https://registry.npmjs.org/object-inspect/-/object-inspect-1.12.2.tgz", - "integrity": "sha512-z+cPxW0QGUp0mcqcsgQyLVRDoXFQbXOwBaqyF7VIgI4TWNQsDHrBpUQslRmIfAoYWdYzs6UlKJtB2XJpTaNSpQ==", + "version": "1.12.3", + "resolved": "https://registry.npmjs.org/object-inspect/-/object-inspect-1.12.3.tgz", + "integrity": "sha512-geUvdk7c+eizMNUDkRpW1wJwgfOiOeHbxBR/hLXK1aT6zmVSO0jsQcs7fj6MGw89jC/cjGfLcNOrtMYtGqm81g==", "dev": true, "funding": { "url": "https://github.com/sponsors/ljharb" @@ -5621,81 +5872,14 @@ "is-interactive": "^2.0.0", "is-unicode-supported": "^1.1.0", "log-symbols": "^5.1.0", - "strip-ansi": "^7.0.1", - "wcwidth": "^1.0.1" - }, - "engines": { - "node": "^12.20.0 || ^14.13.1 || >=16.0.0" - }, - "funding": { - "url": "https://github.com/sponsors/sindresorhus" - } - }, - "node_modules/ora/node_modules/ansi-regex": { - "version": "6.0.1", - "resolved": "https://registry.npmjs.org/ansi-regex/-/ansi-regex-6.0.1.tgz", - "integrity": "sha512-n5M855fKb2SsfMIiFFoVrABHJC8QtHwVx+mHWP3QcEqBHYienj5dHSgjbxtC0WEZXYt4wcD6zrQElDPhFuZgfA==", - "dev": true, - "engines": { - "node": ">=12" - }, - "funding": { - "url": "https://github.com/chalk/ansi-regex?sponsor=1" - } - }, - "node_modules/ora/node_modules/chalk": { - "version": "5.1.2", - "resolved": "https://registry.npmjs.org/chalk/-/chalk-5.1.2.tgz", - "integrity": "sha512-E5CkT4jWURs1Vy5qGJye+XwCkNj7Od3Af7CP6SujMetSMkLs8Do2RWJK5yx1wamHV/op8Rz+9rltjaTQWDnEFQ==", - "dev": true, - "engines": { - "node": "^12.17.0 || ^14.13 || >=16.0.0" - }, - "funding": { - "url": "https://github.com/chalk/chalk?sponsor=1" - } - }, - "node_modules/ora/node_modules/is-unicode-supported": { - "version": "1.3.0", - "resolved": "https://registry.npmjs.org/is-unicode-supported/-/is-unicode-supported-1.3.0.tgz", - "integrity": "sha512-43r2mRvz+8JRIKnWJ+3j8JtjRKZ6GmjzfaE/qiBJnikNnYv/6bagRJ1kUhNk8R5EX/GkobD+r+sfxCPJsiKBLQ==", - "dev": true, - "engines": { - "node": ">=12" - }, - "funding": { - "url": "https://github.com/sponsors/sindresorhus" - } - }, - "node_modules/ora/node_modules/log-symbols": { - "version": "5.1.0", - "resolved": "https://registry.npmjs.org/log-symbols/-/log-symbols-5.1.0.tgz", - "integrity": "sha512-l0x2DvrW294C9uDCoQe1VSU4gf529FkSZ6leBl4TiqZH/e+0R7hSfHQBNut2mNygDgHwvYHfFLn6Oxb3VWj2rA==", - "dev": true, - "dependencies": { - "chalk": "^5.0.0", - "is-unicode-supported": "^1.1.0" - }, - "engines": { - "node": ">=12" - }, - "funding": { - "url": "https://github.com/sponsors/sindresorhus" - } - }, - "node_modules/ora/node_modules/strip-ansi": { - "version": "7.0.1", - "resolved": "https://registry.npmjs.org/strip-ansi/-/strip-ansi-7.0.1.tgz", - "integrity": "sha512-cXNxvT8dFNRVfhVME3JAe98mkXDYN2O1l7jmcwMnOslDeESg1rF/OZMtK0nRAhiari1unG5cD4jG3rapUAkLbw==", - "dev": true, - "dependencies": { - "ansi-regex": "^6.0.1" + "strip-ansi": "^7.0.1", + "wcwidth": "^1.0.1" }, "engines": { - "node": ">=12" + "node": "^12.20.0 || ^14.13.1 || >=16.0.0" }, "funding": { - "url": "https://github.com/chalk/strip-ansi?sponsor=1" + "url": "https://github.com/sponsors/sindresorhus" } }, "node_modules/os-name": { @@ -5733,33 +5917,30 @@ } }, "node_modules/p-limit": { - "version": "3.1.0", - "resolved": "https://registry.npmjs.org/p-limit/-/p-limit-3.1.0.tgz", - "integrity": "sha512-TYOanM3wGwNGsZN2cVTYPArw454xnXj5qmWF1bEoAc4+cU/ol7GVh7odevjp1FNHduHc3KZMcFduxU5Xc6uJRQ==", + "version": "2.3.0", + "resolved": "https://registry.npmjs.org/p-limit/-/p-limit-2.3.0.tgz", + "integrity": "sha512-//88mFWSJx8lxCzwdAABTJL2MyWB12+eIY7MDL2SqLmAkeKU9qxRvWuSyTjm3FUmpBEMuFfckAIqEaVGUDxb6w==", "dev": true, "dependencies": { - "yocto-queue": "^0.1.0" + "p-try": "^2.0.0" }, "engines": { - "node": ">=10" + "node": ">=6" }, "funding": { "url": "https://github.com/sponsors/sindresorhus" } }, "node_modules/p-locate": { - "version": "5.0.0", - "resolved": "https://registry.npmjs.org/p-locate/-/p-locate-5.0.0.tgz", - "integrity": "sha512-LaNjtRWUBY++zB5nE/NwcaoMylSPk+S+ZHNB1TzdbMJMny6dynpAGt7X/tl/QYq3TIeE6nxHppbo2LGymrG5Pw==", + "version": "4.1.0", + "resolved": "https://registry.npmjs.org/p-locate/-/p-locate-4.1.0.tgz", + "integrity": "sha512-R79ZZ/0wAxKGu3oYMlz8jy/kbhsNrS7SKZ7PxEHBgJ5+F2mtFW2fK2cOtBh1cHYkQsbzFV7I+EoRKe6Yt0oK7A==", "dev": true, "dependencies": { - "p-limit": "^3.0.2" + "p-limit": "^2.2.0" }, "engines": { - "node": ">=10" - }, - "funding": { - "url": "https://github.com/sponsors/sindresorhus" + "node": ">=8" } }, "node_modules/p-map": { @@ -5850,6 +6031,39 @@ "url": "https://github.com/sponsors/sindresorhus" } }, + "node_modules/package-json/node_modules/lru-cache": { + "version": "6.0.0", + "resolved": "https://registry.npmjs.org/lru-cache/-/lru-cache-6.0.0.tgz", + "integrity": "sha512-Jo6dJ04CmSjuznwJSS3pUeWmd/H0ffTlkXXgwZi+eq1UCmqQwCh+eLsYOYCwY991i2Fah4h1BEMCx4qThGbsiA==", + "dev": true, + "dependencies": { + "yallist": "^4.0.0" + }, + "engines": { + "node": ">=10" + } + }, + "node_modules/package-json/node_modules/semver": { + "version": "7.3.8", + "resolved": "https://registry.npmjs.org/semver/-/semver-7.3.8.tgz", + "integrity": "sha512-NB1ctGL5rlHrPJtFDVIVzTyQylMLu9N9VICA6HSFJo8MCGVTMW6gfpicwKmmK/dAjTOrqu5l63JJOpDSrAis3A==", + "dev": true, + "dependencies": { + "lru-cache": "^6.0.0" + }, + "bin": { + "semver": "bin/semver.js" + }, + "engines": { + "node": ">=10" + } + }, + "node_modules/package-json/node_modules/yallist": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/yallist/-/yallist-4.0.0.tgz", + "integrity": "sha512-3wdGidZyq5PB084XLES5TpOSRA3wjXAlIWMhum2kRcv/41Sn2emQ0dycQW4uZXLejwKvg6EsvbdlVL+FYEct7A==", + "dev": true + }, "node_modules/parent-module": { "version": "1.0.1", "resolved": "https://registry.npmjs.org/parent-module/-/parent-module-1.0.1.tgz", @@ -6015,58 +6229,6 @@ "node": ">=8" } }, - "node_modules/pkg-dir/node_modules/find-up": { - "version": "4.1.0", - "resolved": "https://registry.npmjs.org/find-up/-/find-up-4.1.0.tgz", - "integrity": "sha512-PpOwAdQ/YlXQ2vj8a3h8IipDuYRi3wceVQQGYWxNINccq40Anw7BlsEXCMbt1Zt+OLA6Fq9suIpIWD0OsnISlw==", - "dev": true, - "dependencies": { - "locate-path": "^5.0.0", - "path-exists": "^4.0.0" - }, - "engines": { - "node": ">=8" - } - }, - "node_modules/pkg-dir/node_modules/locate-path": { - "version": "5.0.0", - "resolved": "https://registry.npmjs.org/locate-path/-/locate-path-5.0.0.tgz", - "integrity": "sha512-t7hw9pI+WvuwNJXwk5zVHpyhIqzg2qTlklJOf0mVxGSbe3Fp2VieZcduNYjaLDoy6p9uGpQEGWG87WpMKlNq8g==", - "dev": true, - "dependencies": { - "p-locate": "^4.1.0" - }, - "engines": { - "node": ">=8" - } - }, - "node_modules/pkg-dir/node_modules/p-limit": { - "version": "2.3.0", - "resolved": "https://registry.npmjs.org/p-limit/-/p-limit-2.3.0.tgz", - "integrity": "sha512-//88mFWSJx8lxCzwdAABTJL2MyWB12+eIY7MDL2SqLmAkeKU9qxRvWuSyTjm3FUmpBEMuFfckAIqEaVGUDxb6w==", - "dev": true, - "dependencies": { - "p-try": "^2.0.0" - }, - "engines": { - "node": ">=6" - }, - "funding": { - "url": "https://github.com/sponsors/sindresorhus" - } - }, - "node_modules/pkg-dir/node_modules/p-locate": { - "version": "4.1.0", - "resolved": "https://registry.npmjs.org/p-locate/-/p-locate-4.1.0.tgz", - "integrity": "sha512-R79ZZ/0wAxKGu3oYMlz8jy/kbhsNrS7SKZ7PxEHBgJ5+F2mtFW2fK2cOtBh1cHYkQsbzFV7I+EoRKe6Yt0oK7A==", - "dev": true, - "dependencies": { - "p-limit": "^2.2.0" - }, - "engines": { - "node": ">=8" - } - }, "node_modules/prelude-ls": { "version": "1.2.1", "resolved": "https://registry.npmjs.org/prelude-ls/-/prelude-ls-1.2.1.tgz", @@ -6089,16 +6251,16 @@ } }, "node_modules/promise.allsettled": { - "version": "1.0.5", - "resolved": "https://registry.npmjs.org/promise.allsettled/-/promise.allsettled-1.0.5.tgz", - "integrity": "sha512-tVDqeZPoBC0SlzJHzWGZ2NKAguVq2oiYj7gbggbiTvH2itHohijTp7njOUA0aQ/nl+0lr/r6egmhoYu63UZ/pQ==", + "version": "1.0.6", + "resolved": "https://registry.npmjs.org/promise.allsettled/-/promise.allsettled-1.0.6.tgz", + "integrity": "sha512-22wJUOD3zswWFqgwjNHa1965LvqTX87WPu/lreY2KSd7SVcERfuZ4GfUaOnJNnvtoIv2yXT/W00YIGMetXtFXg==", "dev": true, "dependencies": { - "array.prototype.map": "^1.0.4", + "array.prototype.map": "^1.0.5", "call-bind": "^1.0.2", - "define-properties": "^1.1.3", - "es-abstract": "^1.19.1", - "get-intrinsic": "^1.1.1", + "define-properties": "^1.1.4", + "es-abstract": "^1.20.4", + "get-intrinsic": "^1.1.3", "iterate-value": "^1.0.2" }, "engines": { @@ -6146,9 +6308,9 @@ "dev": true }, "node_modules/punycode": { - "version": "2.1.1", - "resolved": "https://registry.npmjs.org/punycode/-/punycode-2.1.1.tgz", - "integrity": "sha512-XRsRjdf+j5ml+y/6GKHPZbrF/8p2Yga0JPtdqTIY2Xe5ohJPD9saDJJLPvp9+NSBprVvevdXZybnj2cv8OEd0A==", + "version": "2.2.0", + "resolved": "https://registry.npmjs.org/punycode/-/punycode-2.2.0.tgz", + "integrity": "sha512-LN6QV1IJ9ZhxWTNdktaPClrNfp8xdSAYS0Zk2ddX7XsXZAxckMHPCBcHRo0cTcEIgYPRiGEkmji3Idkh2yFtYw==", "dev": true, "engines": { "node": ">=6" @@ -6350,33 +6512,32 @@ } }, "node_modules/release-it": { - "version": "15.5.0", - "resolved": "https://registry.npmjs.org/release-it/-/release-it-15.5.0.tgz", - "integrity": "sha512-/pQo/PwEXAWRBgVGLE+3IQ3hUoeiDZMGAo/Egin1envCyLyjzrU7+0P2w4iZ1Xv5OxhC2AcaPaN5eY1ql47cBA==", + "version": "15.6.0", + "resolved": "https://registry.npmjs.org/release-it/-/release-it-15.6.0.tgz", + "integrity": "sha512-NXewgzO8QV1LOFjn2K7/dgE1Y1cG+2JiLOU/x9X/Lq9UdFn2hTH1r9SSrufCxG+y/Rp+oN8liYTsNptKrj92kg==", "dev": true, "dependencies": { "@iarna/toml": "2.2.5", - "@octokit/rest": "19.0.4", + "@octokit/rest": "19.0.5", "async-retry": "1.3.3", - "chalk": "5.0.1", - "cosmiconfig": "7.0.1", + "chalk": "5.1.2", + "cosmiconfig": "8.0.0", "execa": "6.1.0", - "form-data": "4.0.0", "git-url-parse": "13.1.0", "globby": "13.1.2", - "got": "12.5.1", - "inquirer": "9.1.2", + "got": "12.5.3", + "inquirer": "9.1.4", "is-ci": "3.0.1", "lodash": "4.17.21", "mime-types": "2.1.35", "new-github-release-url": "2.0.0", - "node-fetch": "3.2.10", + "node-fetch": "3.3.0", "open": "8.4.0", "ora": "6.1.2", "os-name": "5.0.1", - "promise.allsettled": "1.0.5", + "promise.allsettled": "1.0.6", "proxy-agent": "5.0.0", - "semver": "7.3.7", + "semver": "7.3.8", "shelljs": "0.8.5", "update-notifier": "6.0.2", "url-join": "5.0.0", @@ -6390,18 +6551,6 @@ "node": ">=14.9" } }, - "node_modules/release-it/node_modules/chalk": { - "version": "5.0.1", - "resolved": "https://registry.npmjs.org/chalk/-/chalk-5.0.1.tgz", - "integrity": "sha512-Fo07WOYGqMfCWHOzSXOt2CxDbC6skS/jO9ynEcmpANMoPrD+W1r1K6Vx7iNm+AQmETU1Xr2t+n8nzkV9t6xh3w==", - "dev": true, - "engines": { - "node": "^12.17.0 || ^14.13 || >=16.0.0" - }, - "funding": { - "url": "https://github.com/chalk/chalk?sponsor=1" - } - }, "node_modules/release-it/node_modules/globby": { "version": "13.1.2", "resolved": "https://registry.npmjs.org/globby/-/globby-13.1.2.tgz", @@ -6434,9 +6583,9 @@ } }, "node_modules/release-it/node_modules/semver": { - "version": "7.3.7", - "resolved": "https://registry.npmjs.org/semver/-/semver-7.3.7.tgz", - "integrity": "sha512-QlYTucUYOews+WeEujDoEGziz4K6c47V/Bd+LjSSYcA94p+DmINdf7ncaUinThfvZyu13lN9OY1XDxt8C0Tw0g==", + "version": "7.3.8", + "resolved": "https://registry.npmjs.org/semver/-/semver-7.3.8.tgz", + "integrity": "sha512-NB1ctGL5rlHrPJtFDVIVzTyQylMLu9N9VICA6HSFJo8MCGVTMW6gfpicwKmmK/dAjTOrqu5l63JJOpDSrAis3A==", "dev": true, "dependencies": { "lru-cache": "^6.0.0" @@ -6466,15 +6615,6 @@ "integrity": "sha512-3wdGidZyq5PB084XLES5TpOSRA3wjXAlIWMhum2kRcv/41Sn2emQ0dycQW4uZXLejwKvg6EsvbdlVL+FYEct7A==", "dev": true }, - "node_modules/release-it/node_modules/yargs-parser": { - "version": "21.1.1", - "resolved": "https://registry.npmjs.org/yargs-parser/-/yargs-parser-21.1.1.tgz", - "integrity": "sha512-tVpsJW7DdjecAiFpbIB1e3qxIQsE6NoPc5/eTdrbbIC4h0LVsWhnoa3g+m2HclBIujHzsxZ4VJVA+GUuc2/LBw==", - "dev": true, - "engines": { - "node": ">=12" - } - }, "node_modules/release-zalgo": { "version": "1.0.0", "resolved": "https://registry.npmjs.org/release-zalgo/-/release-zalgo-1.0.0.tgz", @@ -6656,9 +6796,9 @@ } }, "node_modules/rxjs": { - "version": "7.5.7", - "resolved": "https://registry.npmjs.org/rxjs/-/rxjs-7.5.7.tgz", - "integrity": "sha512-z9MzKh/UcOqB3i20H6rtrlaE/CgjLOvheWK/9ILrbhROGTweAi1BaFsTT9FbwZi5Trr1qNRs+MXkhmR06awzQA==", + "version": "7.8.0", + "resolved": "https://registry.npmjs.org/rxjs/-/rxjs-7.8.0.tgz", + "integrity": "sha512-F2+gxDshqmIub1KdvZkaEfGDwLNpPvk9Fs6LD/MyQxNgMds/WH9OdDDXOmxUZpME+iSK3rQCctkL0DYyytUqMg==", "dev": true, "dependencies": { "tslib": "^2.1.0" @@ -6705,18 +6845,12 @@ "dev": true }, "node_modules/semver": { - "version": "7.3.8", - "resolved": "https://registry.npmjs.org/semver/-/semver-7.3.8.tgz", - "integrity": "sha512-NB1ctGL5rlHrPJtFDVIVzTyQylMLu9N9VICA6HSFJo8MCGVTMW6gfpicwKmmK/dAjTOrqu5l63JJOpDSrAis3A==", + "version": "6.3.0", + "resolved": "https://registry.npmjs.org/semver/-/semver-6.3.0.tgz", + "integrity": "sha512-b39TBaTSfV6yBrapU89p5fKekE2m/NwnDocOVruQFS1/veMgdzuPcnOM34M6CwxW8jH/lxEa5rBoDeUwu5HHTw==", "dev": true, - "dependencies": { - "lru-cache": "^6.0.0" - }, "bin": { "semver": "bin/semver.js" - }, - "engines": { - "node": ">=10" } }, "node_modules/semver-diff": { @@ -6734,7 +6868,7 @@ "url": "https://github.com/sponsors/sindresorhus" } }, - "node_modules/semver/node_modules/lru-cache": { + "node_modules/semver-diff/node_modules/lru-cache": { "version": "6.0.0", "resolved": "https://registry.npmjs.org/lru-cache/-/lru-cache-6.0.0.tgz", "integrity": "sha512-Jo6dJ04CmSjuznwJSS3pUeWmd/H0ffTlkXXgwZi+eq1UCmqQwCh+eLsYOYCwY991i2Fah4h1BEMCx4qThGbsiA==", @@ -6746,7 +6880,22 @@ "node": ">=10" } }, - "node_modules/semver/node_modules/yallist": { + "node_modules/semver-diff/node_modules/semver": { + "version": "7.3.8", + "resolved": "https://registry.npmjs.org/semver/-/semver-7.3.8.tgz", + "integrity": "sha512-NB1ctGL5rlHrPJtFDVIVzTyQylMLu9N9VICA6HSFJo8MCGVTMW6gfpicwKmmK/dAjTOrqu5l63JJOpDSrAis3A==", + "dev": true, + "dependencies": { + "lru-cache": "^6.0.0" + }, + "bin": { + "semver": "bin/semver.js" + }, + "engines": { + "node": ">=10" + } + }, + "node_modules/semver-diff/node_modules/yallist": { "version": "4.0.0", "resolved": "https://registry.npmjs.org/yallist/-/yallist-4.0.0.tgz", "integrity": "sha512-3wdGidZyq5PB084XLES5TpOSRA3wjXAlIWMhum2kRcv/41Sn2emQ0dycQW4uZXLejwKvg6EsvbdlVL+FYEct7A==", @@ -6812,14 +6961,14 @@ } }, "node_modules/shiki": { - "version": "0.11.1", - "resolved": "https://registry.npmjs.org/shiki/-/shiki-0.11.1.tgz", - "integrity": "sha512-EugY9VASFuDqOexOgXR18ZV+TbFrQHeCpEYaXamO+SZlsnT/2LxuLBX25GGtIrwaEVFXUAbUQ601SWE2rMwWHA==", + "version": "0.12.1", + "resolved": "https://registry.npmjs.org/shiki/-/shiki-0.12.1.tgz", + "integrity": "sha512-aieaV1m349rZINEBkjxh2QbBvFFQOlgqYTNtCal82hHj4dDZ76oMlQIX+C7ryerBTDiga3e5NfH6smjdJ02BbQ==", "dev": true, "dependencies": { - "jsonc-parser": "^3.0.0", - "vscode-oniguruma": "^1.6.1", - "vscode-textmate": "^6.0.0" + "jsonc-parser": "^3.2.0", + "vscode-oniguruma": "^1.7.0", + "vscode-textmate": "^8.0.0" } }, "node_modules/side-channel": { @@ -6843,16 +6992,16 @@ "dev": true }, "node_modules/sinon": { - "version": "14.0.1", - "resolved": "https://registry.npmjs.org/sinon/-/sinon-14.0.1.tgz", - "integrity": "sha512-JhJ0jCiyBWVAHDS+YSjgEbDn7Wgz9iIjA1/RK+eseJN0vAAWIWiXBdrnb92ELPyjsfreCYntD1ORtLSfIrlvSQ==", + "version": "15.0.1", + "resolved": "https://registry.npmjs.org/sinon/-/sinon-15.0.1.tgz", + "integrity": "sha512-PZXKc08f/wcA/BMRGBze2Wmw50CWPiAH3E21EOi4B49vJ616vW4DQh4fQrqsYox2aNR/N3kCqLuB0PwwOucQrg==", "dev": true, "dependencies": { - "@sinonjs/commons": "^1.8.3", - "@sinonjs/fake-timers": "^9.1.2", - "@sinonjs/samsam": "^6.1.1", + "@sinonjs/commons": "^2.0.0", + "@sinonjs/fake-timers": "10.0.2", + "@sinonjs/samsam": "^7.0.1", "diff": "^5.0.0", - "nise": "^5.1.1", + "nise": "^5.1.2", "supports-color": "^7.2.0" }, "funding": { @@ -6964,6 +7113,18 @@ "node": ">= 0.8" } }, + "node_modules/stop-iteration-iterator": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/stop-iteration-iterator/-/stop-iteration-iterator-1.0.0.tgz", + "integrity": "sha512-iCGQj+0l0HOdZ2AEeBADlsRC+vsnDsZsbdSiH1yNSjcfKM7fdpCMfqAL/dwF5BLiw/XhRft/Wax6zQbhq2BcjQ==", + "dev": true, + "dependencies": { + "internal-slot": "^1.0.4" + }, + "engines": { + "node": ">= 0.4" + } + }, "node_modules/string_decoder": { "version": "1.3.0", "resolved": "https://registry.npmjs.org/string_decoder/-/string_decoder-1.3.0.tgz", @@ -6990,71 +7151,47 @@ "url": "https://github.com/sponsors/sindresorhus" } }, - "node_modules/string-width/node_modules/ansi-regex": { - "version": "6.0.1", - "resolved": "https://registry.npmjs.org/ansi-regex/-/ansi-regex-6.0.1.tgz", - "integrity": "sha512-n5M855fKb2SsfMIiFFoVrABHJC8QtHwVx+mHWP3QcEqBHYienj5dHSgjbxtC0WEZXYt4wcD6zrQElDPhFuZgfA==", - "dev": true, - "engines": { - "node": ">=12" - }, - "funding": { - "url": "https://github.com/chalk/ansi-regex?sponsor=1" - } - }, - "node_modules/string-width/node_modules/strip-ansi": { - "version": "7.0.1", - "resolved": "https://registry.npmjs.org/strip-ansi/-/strip-ansi-7.0.1.tgz", - "integrity": "sha512-cXNxvT8dFNRVfhVME3JAe98mkXDYN2O1l7jmcwMnOslDeESg1rF/OZMtK0nRAhiari1unG5cD4jG3rapUAkLbw==", - "dev": true, - "dependencies": { - "ansi-regex": "^6.0.1" - }, - "engines": { - "node": ">=12" - }, - "funding": { - "url": "https://github.com/chalk/strip-ansi?sponsor=1" - } - }, "node_modules/string.prototype.trimend": { - "version": "1.0.5", - "resolved": "https://registry.npmjs.org/string.prototype.trimend/-/string.prototype.trimend-1.0.5.tgz", - "integrity": "sha512-I7RGvmjV4pJ7O3kdf+LXFpVfdNOxtCW/2C8f6jNiW4+PQchwxkCDzlk1/7p+Wl4bqFIZeF47qAHXLuHHWKAxog==", + "version": "1.0.6", + "resolved": "https://registry.npmjs.org/string.prototype.trimend/-/string.prototype.trimend-1.0.6.tgz", + "integrity": "sha512-JySq+4mrPf9EsDBEDYMOb/lM7XQLulwg5R/m1r0PXEFqrV0qHvl58sdTilSXtKOflCsK2E8jxf+GKC0T07RWwQ==", "dev": true, "dependencies": { "call-bind": "^1.0.2", "define-properties": "^1.1.4", - "es-abstract": "^1.19.5" + "es-abstract": "^1.20.4" }, "funding": { "url": "https://github.com/sponsors/ljharb" } }, "node_modules/string.prototype.trimstart": { - "version": "1.0.5", - "resolved": "https://registry.npmjs.org/string.prototype.trimstart/-/string.prototype.trimstart-1.0.5.tgz", - "integrity": "sha512-THx16TJCGlsN0o6dl2o6ncWUsdgnLRSA23rRE5pyGBw/mLr3Ej/R2LaqCtgP8VNMGZsvMWnf9ooZPyY2bHvUFg==", + "version": "1.0.6", + "resolved": "https://registry.npmjs.org/string.prototype.trimstart/-/string.prototype.trimstart-1.0.6.tgz", + "integrity": "sha512-omqjMDaY92pbn5HOX7f9IccLA+U1tA9GvtU4JrodiXFfYB7jPzzHpRzpglLAjtUV6bB557zwClJezTqnAiYnQA==", "dev": true, "dependencies": { "call-bind": "^1.0.2", "define-properties": "^1.1.4", - "es-abstract": "^1.19.5" + "es-abstract": "^1.20.4" }, "funding": { "url": "https://github.com/sponsors/ljharb" } }, "node_modules/strip-ansi": { - "version": "6.0.1", - "resolved": "https://registry.npmjs.org/strip-ansi/-/strip-ansi-6.0.1.tgz", - "integrity": "sha512-Y38VPSHcqkFrCpFnQ9vuSXmquuv5oXOKpGeT6aGrr3o3Gc9AlVa6JBfUSOCnbxGGZF+/0ooI7KrPuUSztUdU5A==", + "version": "7.0.1", + "resolved": "https://registry.npmjs.org/strip-ansi/-/strip-ansi-7.0.1.tgz", + "integrity": "sha512-cXNxvT8dFNRVfhVME3JAe98mkXDYN2O1l7jmcwMnOslDeESg1rF/OZMtK0nRAhiari1unG5cD4jG3rapUAkLbw==", "dev": true, "dependencies": { - "ansi-regex": "^5.0.1" + "ansi-regex": "^6.0.1" }, "engines": { - "node": ">=8" + "node": ">=12" + }, + "funding": { + "url": "https://github.com/chalk/strip-ansi?sponsor=1" } }, "node_modules/strip-bom": { @@ -7283,9 +7420,9 @@ } }, "node_modules/tslib": { - "version": "2.4.0", - "resolved": "https://registry.npmjs.org/tslib/-/tslib-2.4.0.tgz", - "integrity": "sha512-d6xOpEDfsi2CZVlPQzGeux8XMwLT9hssAsaPYExaQMuYskwb+x1x7J371tWlbBdWHroy99KnVB6qIkUbs5X3UQ==", + "version": "2.4.1", + "resolved": "https://registry.npmjs.org/tslib/-/tslib-2.4.1.tgz", + "integrity": "sha512-tGyy4dAjRIEwI7BzsB0lynWgOpfqjUdq91XXAlIWD2OwKBH7oCl/GZG/HT4BOHrTlPMOASlMQ7veyTqpmRcrNA==", "dev": true }, "node_modules/tsutils": { @@ -7331,17 +7468,31 @@ } }, "node_modules/type-fest": { - "version": "0.20.2", - "resolved": "https://registry.npmjs.org/type-fest/-/type-fest-0.20.2.tgz", - "integrity": "sha512-Ne+eE4r0/iWnpAxD852z3A+N0Bt5RN//NjJwRd2VFHEmrywxf5vsZlh4R6lixl6B+wz/8d+maTSAkN1FIkI3LQ==", + "version": "3.5.2", + "resolved": "https://registry.npmjs.org/type-fest/-/type-fest-3.5.2.tgz", + "integrity": "sha512-Ph7S4EhXzWy0sbljEuZo0tTNoLl+K2tPauGrQpcwUWrOVneLePTuhVzcuzVJJ6RU5DsNwQZka+8YtkXXU4z9cA==", "dev": true, "engines": { - "node": ">=10" + "node": ">=14.16" }, "funding": { "url": "https://github.com/sponsors/sindresorhus" } }, + "node_modules/typed-array-length": { + "version": "1.0.4", + "resolved": "https://registry.npmjs.org/typed-array-length/-/typed-array-length-1.0.4.tgz", + "integrity": "sha512-KjZypGq+I/H7HI5HlOoGHkWUUGq+Q0TPhQurLbyrVrvnKTBgzLhIJ7j6J/XTQOi0d1RjyZ0wdas8bKs2p0x3Ng==", + "dev": true, + "dependencies": { + "call-bind": "^1.0.2", + "for-each": "^0.3.3", + "is-typed-array": "^1.1.9" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, "node_modules/typedarray-to-buffer": { "version": "3.1.5", "resolved": "https://registry.npmjs.org/typedarray-to-buffer/-/typedarray-to-buffer-3.1.5.tgz", @@ -7352,15 +7503,15 @@ } }, "node_modules/typedoc": { - "version": "0.23.18", - "resolved": "https://registry.npmjs.org/typedoc/-/typedoc-0.23.18.tgz", - "integrity": "sha512-0Tq/uFkUuWyRYyjOShTkhsOm6u5E8wf0i6L76/k5znEaxvWKHGeT2ywZThGrDrryV/skO/REM824D1gm8ccQuA==", + "version": "0.23.24", + "resolved": "https://registry.npmjs.org/typedoc/-/typedoc-0.23.24.tgz", + "integrity": "sha512-bfmy8lNQh+WrPYcJbtjQ6JEEsVl/ce1ZIXyXhyW+a1vFrjO39t6J8sL/d6FfAGrJTc7McCXgk9AanYBSNvLdIA==", "dev": true, "dependencies": { "lunr": "^2.3.9", - "marked": "^4.0.19", - "minimatch": "^5.1.0", - "shiki": "^0.11.1" + "marked": "^4.2.5", + "minimatch": "^5.1.2", + "shiki": "^0.12.1" }, "bin": { "typedoc": "bin/typedoc" @@ -7369,7 +7520,7 @@ "node": ">= 14.14" }, "peerDependencies": { - "typescript": "4.6.x || 4.7.x || 4.8.x" + "typescript": "4.6.x || 4.7.x || 4.8.x || 4.9.x" } }, "node_modules/typedoc/node_modules/brace-expansion": { @@ -7382,9 +7533,9 @@ } }, "node_modules/typedoc/node_modules/minimatch": { - "version": "5.1.0", - "resolved": "https://registry.npmjs.org/minimatch/-/minimatch-5.1.0.tgz", - "integrity": "sha512-9TPBGGak4nHfGZsPBohm9AWg6NoT7QTCehS3BIJABslyZbzxfV78QM2Y6+i741OPZIafFAaiiEMh5OyIrJPgtg==", + "version": "5.1.6", + "resolved": "https://registry.npmjs.org/minimatch/-/minimatch-5.1.6.tgz", + "integrity": "sha512-lKwV/1brpG6mBUFHtb7NUmtABCb2WZZmm2wNiOA5hAb8VdCS4B3dtMWyvcoViccwAW/COERjXLt0zP1zXUN26g==", "dev": true, "dependencies": { "brace-expansion": "^2.0.1" @@ -7394,9 +7545,9 @@ } }, "node_modules/typescript": { - "version": "4.8.4", - "resolved": "https://registry.npmjs.org/typescript/-/typescript-4.8.4.tgz", - "integrity": "sha512-QCh+85mCy+h0IGff8r5XWzOVSbBO+KfeYrMQh7NJ58QujwcE22u+NUSmUxqF+un70P9GXKxa2HCNiTTMJknyjQ==", + "version": "4.9.4", + "resolved": "https://registry.npmjs.org/typescript/-/typescript-4.9.4.tgz", + "integrity": "sha512-Uz+dTXYzxXXbsFpM86Wh3dKCxrQqUcVMxwU54orwlJjOpO3ao8L7j5lH+dWfTwgCwIuM9GQ2kvVotzYJMXTBZg==", "dev": true, "bin": { "tsc": "bin/tsc", @@ -7514,18 +7665,39 @@ "url": "https://github.com/yeoman/update-notifier?sponsor=1" } }, - "node_modules/update-notifier/node_modules/chalk": { - "version": "5.1.2", - "resolved": "https://registry.npmjs.org/chalk/-/chalk-5.1.2.tgz", - "integrity": "sha512-E5CkT4jWURs1Vy5qGJye+XwCkNj7Od3Af7CP6SujMetSMkLs8Do2RWJK5yx1wamHV/op8Rz+9rltjaTQWDnEFQ==", + "node_modules/update-notifier/node_modules/lru-cache": { + "version": "6.0.0", + "resolved": "https://registry.npmjs.org/lru-cache/-/lru-cache-6.0.0.tgz", + "integrity": "sha512-Jo6dJ04CmSjuznwJSS3pUeWmd/H0ffTlkXXgwZi+eq1UCmqQwCh+eLsYOYCwY991i2Fah4h1BEMCx4qThGbsiA==", "dev": true, + "dependencies": { + "yallist": "^4.0.0" + }, "engines": { - "node": "^12.17.0 || ^14.13 || >=16.0.0" + "node": ">=10" + } + }, + "node_modules/update-notifier/node_modules/semver": { + "version": "7.3.8", + "resolved": "https://registry.npmjs.org/semver/-/semver-7.3.8.tgz", + "integrity": "sha512-NB1ctGL5rlHrPJtFDVIVzTyQylMLu9N9VICA6HSFJo8MCGVTMW6gfpicwKmmK/dAjTOrqu5l63JJOpDSrAis3A==", + "dev": true, + "dependencies": { + "lru-cache": "^6.0.0" }, - "funding": { - "url": "https://github.com/chalk/chalk?sponsor=1" + "bin": { + "semver": "bin/semver.js" + }, + "engines": { + "node": ">=10" } }, + "node_modules/update-notifier/node_modules/yallist": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/yallist/-/yallist-4.0.0.tgz", + "integrity": "sha512-3wdGidZyq5PB084XLES5TpOSRA3wjXAlIWMhum2kRcv/41Sn2emQ0dycQW4uZXLejwKvg6EsvbdlVL+FYEct7A==", + "dev": true + }, "node_modules/uri-js": { "version": "4.4.1", "resolved": "https://registry.npmjs.org/uri-js/-/uri-js-4.4.1.tgz", @@ -7566,9 +7738,9 @@ "dev": true }, "node_modules/vm2": { - "version": "3.9.11", - "resolved": "https://registry.npmjs.org/vm2/-/vm2-3.9.11.tgz", - "integrity": "sha512-PFG8iJRSjvvBdisowQ7iVF580DXb1uCIiGaXgm7tynMR1uTBlv7UJlB1zdv5KJ+Tmq1f0Upnj3fayoEOPpCBKg==", + "version": "3.9.13", + "resolved": "https://registry.npmjs.org/vm2/-/vm2-3.9.13.tgz", + "integrity": "sha512-0rvxpB8P8Shm4wX2EKOiMp7H2zq+HUE/UwodY0pCZXs9IffIKZq6vUti5OgkVCTakKo9e/fgO4X1fkwfjWxE3Q==", "dev": true, "dependencies": { "acorn": "^8.7.0", @@ -7582,15 +7754,15 @@ } }, "node_modules/vscode-oniguruma": { - "version": "1.6.2", - "resolved": "https://registry.npmjs.org/vscode-oniguruma/-/vscode-oniguruma-1.6.2.tgz", - "integrity": "sha512-KH8+KKov5eS/9WhofZR8M8dMHWN2gTxjMsG4jd04YhpbPR91fUj7rYQ2/XjeHCJWbg7X++ApRIU9NUwM2vTvLA==", + "version": "1.7.0", + "resolved": "https://registry.npmjs.org/vscode-oniguruma/-/vscode-oniguruma-1.7.0.tgz", + "integrity": "sha512-L9WMGRfrjOhgHSdOYgCt/yRMsXzLDJSL7BPrOZt73gU0iWO4mpqzqQzOz5srxqTvMBaR0XZTSrVWo4j55Rc6cA==", "dev": true }, "node_modules/vscode-textmate": { - "version": "6.0.0", - "resolved": "https://registry.npmjs.org/vscode-textmate/-/vscode-textmate-6.0.0.tgz", - "integrity": "sha512-gu73tuZfJgu+mvCSy4UZwd2JXykjK9zAZsfmDeut5dx/1a7FeTk0XwJsSuqQn+cuMCGVbIBfl+s53X4T19DnzQ==", + "version": "8.0.0", + "resolved": "https://registry.npmjs.org/vscode-textmate/-/vscode-textmate-8.0.0.tgz", + "integrity": "sha512-AFbieoL7a5LMqcnOF04ji+rpXadgOXnZsxQr//r83kLPr7biP7am3g9zbaZIaBGwBRWeSvoMD4mgPdX3e4NWBg==", "dev": true }, "node_modules/wcwidth": { @@ -7664,6 +7836,26 @@ "integrity": "sha512-B+enWhmw6cjfVC7kS8Pj9pCrKSc5txArRyaYGe088shv/FGWH+0Rjx/xPgtsWfsUtS27FkP697E4DDhgrgoc0Q==", "dev": true }, + "node_modules/which-typed-array": { + "version": "1.1.9", + "resolved": "https://registry.npmjs.org/which-typed-array/-/which-typed-array-1.1.9.tgz", + "integrity": "sha512-w9c4xkx6mPidwp7180ckYWfMmvxpjlZuIudNtDf4N/tTAUB8VJbX25qZoAsrtGuYNnGw3pa0AXgbGKRB8/EceA==", + "dev": true, + "dependencies": { + "available-typed-arrays": "^1.0.5", + "call-bind": "^1.0.2", + "for-each": "^0.3.3", + "gopd": "^1.0.1", + "has-tostringtag": "^1.0.0", + "is-typed-array": "^1.1.10" + }, + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, "node_modules/widest-line": { "version": "4.0.1", "resolved": "https://registry.npmjs.org/widest-line/-/widest-line-4.0.1.tgz", @@ -7686,9 +7878,9 @@ "dev": true }, "node_modules/windows-release": { - "version": "5.0.1", - "resolved": "https://registry.npmjs.org/windows-release/-/windows-release-5.0.1.tgz", - "integrity": "sha512-y1xFdFvdMiDXI3xiOhMbJwt1Y7dUxidha0CWPs1NgjZIjZANTcX7+7bMqNjuezhzb8s5JGEiBAbQjQQYYy7ulw==", + "version": "5.1.0", + "resolved": "https://registry.npmjs.org/windows-release/-/windows-release-5.1.0.tgz", + "integrity": "sha512-CddHecz5dt0ngTjGPP1uYr9Tjl4qq5rEKNk8UGb8XCdngNXI+GRYvqelD055FdiUgqODZz3R/5oZWYldPtXQpA==", "dev": true, "dependencies": { "execa": "^5.1.1" @@ -7732,6 +7924,18 @@ "node": ">=10.17.0" } }, + "node_modules/windows-release/node_modules/is-stream": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/is-stream/-/is-stream-2.0.1.tgz", + "integrity": "sha512-hFoiJiTl63nn+kstHGBtewWSKnQLpyb155KHheA1l39uvtO9nWIop1p3udqPcUd/xbF1VLMO4n7OI6p7RbngDg==", + "dev": true, + "engines": { + "node": ">=8" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, "node_modules/windows-release/node_modules/mimic-fn": { "version": "2.1.0", "resolved": "https://registry.npmjs.org/mimic-fn/-/mimic-fn-2.1.0.tgz", @@ -7809,45 +8013,6 @@ "url": "https://github.com/chalk/wrap-ansi?sponsor=1" } }, - "node_modules/wrap-ansi/node_modules/ansi-regex": { - "version": "6.0.1", - "resolved": "https://registry.npmjs.org/ansi-regex/-/ansi-regex-6.0.1.tgz", - "integrity": "sha512-n5M855fKb2SsfMIiFFoVrABHJC8QtHwVx+mHWP3QcEqBHYienj5dHSgjbxtC0WEZXYt4wcD6zrQElDPhFuZgfA==", - "dev": true, - "engines": { - "node": ">=12" - }, - "funding": { - "url": "https://github.com/chalk/ansi-regex?sponsor=1" - } - }, - "node_modules/wrap-ansi/node_modules/ansi-styles": { - "version": "6.2.1", - "resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-6.2.1.tgz", - "integrity": "sha512-bN798gFfQX+viw3R7yrGWRqnrN2oRkEkUjjl4JNn4E8GxxbjtG3FbrEIIY3l8/hrwUwIeCZvi4QuOTP4MErVug==", - "dev": true, - "engines": { - "node": ">=12" - }, - "funding": { - "url": "https://github.com/chalk/ansi-styles?sponsor=1" - } - }, - "node_modules/wrap-ansi/node_modules/strip-ansi": { - "version": "7.0.1", - "resolved": "https://registry.npmjs.org/strip-ansi/-/strip-ansi-7.0.1.tgz", - "integrity": "sha512-cXNxvT8dFNRVfhVME3JAe98mkXDYN2O1l7jmcwMnOslDeESg1rF/OZMtK0nRAhiari1unG5cD4jG3rapUAkLbw==", - "dev": true, - "dependencies": { - "ansi-regex": "^6.0.1" - }, - "engines": { - "node": ">=12" - }, - "funding": { - "url": "https://github.com/chalk/strip-ansi?sponsor=1" - } - }, "node_modules/wrappy": { "version": "1.0.2", "resolved": "https://registry.npmjs.org/wrappy/-/wrappy-1.0.2.tgz", @@ -7888,13 +8053,10 @@ } }, "node_modules/y18n": { - "version": "5.0.8", - "resolved": "https://registry.npmjs.org/y18n/-/y18n-5.0.8.tgz", - "integrity": "sha512-0pfFzegeDWJHJIAmTLRP2DwHjdF5s7jo9tuztdQxAhINCdvS+3nGINqPd00AphqJR/0LhANUS6/+7SCb98YOfA==", - "dev": true, - "engines": { - "node": ">=10" - } + "version": "4.0.3", + "resolved": "https://registry.npmjs.org/y18n/-/y18n-4.0.3.tgz", + "integrity": "sha512-JKhqTOwSrqNA1NY5lSztJ1GrBiUodLMmIZuLiDaMRJ+itFd+ABVE8XBjOvIWL+rSqNDC74LCSFmlb/U4UZ4hJQ==", + "dev": true }, "node_modules/yallist": { "version": "3.1.1", @@ -7902,40 +8064,35 @@ "integrity": "sha512-a4UGQaWPH59mOXUYnAG2ewncQS4i4F43Tv3JoAM+s2VDAmS9NsK8GpDMLrCHPksFT7h3K6TOoUNn2pb7RoXx4g==", "dev": true }, - "node_modules/yaml": { - "version": "1.10.2", - "resolved": "https://registry.npmjs.org/yaml/-/yaml-1.10.2.tgz", - "integrity": "sha512-r3vXyErRCYJ7wg28yvBY5VSoAF8ZvlcW9/BwUzEtUsjvX/DKs24dIkuwjtuprwJJHsbyUbLApepYTR1BN4uHrg==", - "dev": true, - "engines": { - "node": ">= 6" - } - }, "node_modules/yargs": { - "version": "17.6.0", - "resolved": "https://registry.npmjs.org/yargs/-/yargs-17.6.0.tgz", - "integrity": "sha512-8H/wTDqlSwoSnScvV2N/JHfLWOKuh5MVla9hqLjK3nsfyy6Y4kDSYSvkU5YCUEPOSnRXfIyx3Sq+B/IWudTo4g==", + "version": "15.4.1", + "resolved": "https://registry.npmjs.org/yargs/-/yargs-15.4.1.tgz", + "integrity": "sha512-aePbxDmcYW++PaqBsJ+HYUFwCdv4LVvdnhBy78E57PIor8/OVvhMrADFFEDh8DHDFRv/O9i3lPhsENjO7QX0+A==", "dev": true, "dependencies": { - "cliui": "^8.0.1", - "escalade": "^3.1.1", - "get-caller-file": "^2.0.5", + "cliui": "^6.0.0", + "decamelize": "^1.2.0", + "find-up": "^4.1.0", + "get-caller-file": "^2.0.1", "require-directory": "^2.1.1", - "string-width": "^4.2.3", - "y18n": "^5.0.5", - "yargs-parser": "^21.0.0" + "require-main-filename": "^2.0.0", + "set-blocking": "^2.0.0", + "string-width": "^4.2.0", + "which-module": "^2.0.0", + "y18n": "^4.0.0", + "yargs-parser": "^18.1.2" }, "engines": { - "node": ">=12" + "node": ">=8" } }, "node_modules/yargs-parser": { - "version": "20.2.4", - "resolved": "https://registry.npmjs.org/yargs-parser/-/yargs-parser-20.2.4.tgz", - "integrity": "sha512-WOkpgNhPTlE73h4VFAFsOnomJVaovO8VqLDzy5saChRBFQFBoMYirowyW+Q9HB4HFF4Z7VZTiG3iSzJJA29yRA==", + "version": "21.1.1", + "resolved": "https://registry.npmjs.org/yargs-parser/-/yargs-parser-21.1.1.tgz", + "integrity": "sha512-tVpsJW7DdjecAiFpbIB1e3qxIQsE6NoPc5/eTdrbbIC4h0LVsWhnoa3g+m2HclBIujHzsxZ4VJVA+GUuc2/LBw==", "dev": true, "engines": { - "node": ">=10" + "node": ">=12" } }, "node_modules/yargs-unparser": { @@ -7977,6 +8134,15 @@ "url": "https://github.com/sponsors/sindresorhus" } }, + "node_modules/yargs/node_modules/ansi-regex": { + "version": "5.0.1", + "resolved": "https://registry.npmjs.org/ansi-regex/-/ansi-regex-5.0.1.tgz", + "integrity": "sha512-quJQXlTSUGL2LH9SUXo8VwsY4soanhgo6LNSm84E1LBcE8s3O0wpdiRzyR9z/ZZJMlMWv37qOOb9pdJlMUEKFQ==", + "dev": true, + "engines": { + "node": ">=8" + } + }, "node_modules/yargs/node_modules/emoji-regex": { "version": "8.0.0", "resolved": "https://registry.npmjs.org/emoji-regex/-/emoji-regex-8.0.0.tgz", @@ -7997,13 +8163,29 @@ "node": ">=8" } }, + "node_modules/yargs/node_modules/strip-ansi": { + "version": "6.0.1", + "resolved": "https://registry.npmjs.org/strip-ansi/-/strip-ansi-6.0.1.tgz", + "integrity": "sha512-Y38VPSHcqkFrCpFnQ9vuSXmquuv5oXOKpGeT6aGrr3o3Gc9AlVa6JBfUSOCnbxGGZF+/0ooI7KrPuUSztUdU5A==", + "dev": true, + "dependencies": { + "ansi-regex": "^5.0.1" + }, + "engines": { + "node": ">=8" + } + }, "node_modules/yargs/node_modules/yargs-parser": { - "version": "21.1.1", - "resolved": "https://registry.npmjs.org/yargs-parser/-/yargs-parser-21.1.1.tgz", - "integrity": "sha512-tVpsJW7DdjecAiFpbIB1e3qxIQsE6NoPc5/eTdrbbIC4h0LVsWhnoa3g+m2HclBIujHzsxZ4VJVA+GUuc2/LBw==", + "version": "18.1.3", + "resolved": "https://registry.npmjs.org/yargs-parser/-/yargs-parser-18.1.3.tgz", + "integrity": "sha512-o50j0JeToy/4K6OZcaQmW6lyXXKhq7csREXcDwk2omFPJEwUNOVtJKvmDr9EI1fAJZUyZcRF7kxGBWmRXudrCQ==", "dev": true, + "dependencies": { + "camelcase": "^5.0.0", + "decamelize": "^1.2.0" + }, "engines": { - "node": ">=12" + "node": ">=6" } }, "node_modules/yn": { @@ -8034,13 +8216,13 @@ "devDependencies": { "@istanbuljs/nyc-config-typescript": "^1.0.2", "@redis/test-utils": "*", - "@types/node": "^18.11.6", + "@types/node": "^18.11.18", "nyc": "^15.1.0", - "release-it": "^15.5.0", + "release-it": "^15.6.0", "source-map-support": "^0.5.21", "ts-node": "^10.9.1", - "typedoc": "^0.23.18", - "typescript": "^4.8.4" + "typedoc": "^0.23.24", + "typescript": "^4.9.4" }, "peerDependencies": { "@redis/client": "^1.0.0" @@ -8048,29 +8230,29 @@ }, "packages/client": { "name": "@redis/client", - "version": "1.4.2", + "version": "1.5.0", "license": "MIT", "dependencies": { - "cluster-key-slot": "1.1.1", + "cluster-key-slot": "1.1.2", "generic-pool": "3.9.0", "yallist": "4.0.0" }, "devDependencies": { "@istanbuljs/nyc-config-typescript": "^1.0.2", "@redis/test-utils": "*", - "@types/node": "^18.11.6", + "@types/node": "^18.11.18", "@types/sinon": "^10.0.13", "@types/yallist": "^4.0.1", - "@typescript-eslint/eslint-plugin": "^5.41.0", - "@typescript-eslint/parser": "^5.41.0", - "eslint": "^8.26.0", + "@typescript-eslint/eslint-plugin": "^5.49.0", + "@typescript-eslint/parser": "^5.49.0", + "eslint": "^8.32.0", "nyc": "^15.1.0", - "release-it": "^15.5.0", - "sinon": "^14.0.1", + "release-it": "^15.6.0", + "sinon": "^15.0.1", "source-map-support": "^0.5.21", "ts-node": "^10.9.1", - "typedoc": "^0.23.18", - "typescript": "^4.8.4" + "typedoc": "^0.23.24", + "typescript": "^4.9.4" }, "engines": { "node": ">=14" @@ -8078,7 +8260,8 @@ }, "packages/client/node_modules/yallist": { "version": "4.0.0", - "license": "ISC" + "resolved": "https://registry.npmjs.org/yallist/-/yallist-4.0.0.tgz", + "integrity": "sha512-3wdGidZyq5PB084XLES5TpOSRA3wjXAlIWMhum2kRcv/41Sn2emQ0dycQW4uZXLejwKvg6EsvbdlVL+FYEct7A==" }, "packages/graph": { "name": "@redis/graph", @@ -8087,13 +8270,13 @@ "devDependencies": { "@istanbuljs/nyc-config-typescript": "^1.0.2", "@redis/test-utils": "*", - "@types/node": "^18.11.6", + "@types/node": "^18.11.18", "nyc": "^15.1.0", - "release-it": "^15.5.0", + "release-it": "^15.6.0", "source-map-support": "^0.5.21", "ts-node": "^10.9.1", - "typedoc": "^0.23.18", - "typescript": "^4.8.4" + "typedoc": "^0.23.24", + "typescript": "^4.9.4" }, "peerDependencies": { "@redis/client": "^1.0.0" @@ -8106,13 +8289,13 @@ "devDependencies": { "@istanbuljs/nyc-config-typescript": "^1.0.2", "@redis/test-utils": "*", - "@types/node": "^18.11.6", + "@types/node": "^18.11.18", "nyc": "^15.1.0", - "release-it": "^15.5.0", + "release-it": "^15.6.0", "source-map-support": "^0.5.21", "ts-node": "^10.9.1", - "typedoc": "^0.23.18", - "typescript": "^4.8.4" + "typedoc": "^0.23.24", + "typescript": "^4.9.4" }, "peerDependencies": { "@redis/client": "^1.0.0" @@ -8125,13 +8308,13 @@ "devDependencies": { "@istanbuljs/nyc-config-typescript": "^1.0.2", "@redis/test-utils": "*", - "@types/node": "^18.11.6", + "@types/node": "^18.11.18", "nyc": "^15.1.0", - "release-it": "^15.5.0", + "release-it": "^15.6.0", "source-map-support": "^0.5.21", "ts-node": "^10.9.1", - "typedoc": "^0.23.18", - "typescript": "^4.8.4" + "typedoc": "^0.23.24", + "typescript": "^4.9.4" }, "peerDependencies": { "@redis/client": "^1.0.0" @@ -8141,20 +8324,134 @@ "name": "@redis/test-utils", "devDependencies": { "@istanbuljs/nyc-config-typescript": "^1.0.2", - "@types/mocha": "^10.0.0", - "@types/node": "^18.11.6", - "@types/yargs": "^17.0.13", - "mocha": "^10.1.0", + "@types/mocha": "^10.0.1", + "@types/node": "^18.11.18", + "@types/yargs": "^17.0.20", + "mocha": "^10.2.0", "nyc": "^15.1.0", "source-map-support": "^0.5.21", "ts-node": "^10.9.1", - "typescript": "^4.8.4", - "yargs": "^17.6.0" + "typescript": "^4.9.4", + "yargs": "^17.6.2" }, "peerDependencies": { "@redis/client": "^1.0.0" } }, + "packages/test-utils/node_modules/ansi-regex": { + "version": "5.0.1", + "resolved": "https://registry.npmjs.org/ansi-regex/-/ansi-regex-5.0.1.tgz", + "integrity": "sha512-quJQXlTSUGL2LH9SUXo8VwsY4soanhgo6LNSm84E1LBcE8s3O0wpdiRzyR9z/ZZJMlMWv37qOOb9pdJlMUEKFQ==", + "dev": true, + "engines": { + "node": ">=8" + } + }, + "packages/test-utils/node_modules/ansi-styles": { + "version": "4.3.0", + "resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-4.3.0.tgz", + "integrity": "sha512-zbB9rCJAT1rbjiVDb2hqKFHNYLxgtk8NURxZ3IZwD3F6NtxbXZQCnnSi1Lkx+IDohdPlFp222wVALIheZJQSEg==", + "dev": true, + "dependencies": { + "color-convert": "^2.0.1" + }, + "engines": { + "node": ">=8" + }, + "funding": { + "url": "https://github.com/chalk/ansi-styles?sponsor=1" + } + }, + "packages/test-utils/node_modules/cliui": { + "version": "8.0.1", + "resolved": "https://registry.npmjs.org/cliui/-/cliui-8.0.1.tgz", + "integrity": "sha512-BSeNnyus75C4//NQ9gQt1/csTXyo/8Sb+afLAkzAptFuMsod9HFokGNudZpi/oQV73hnVK+sR+5PVRMd+Dr7YQ==", + "dev": true, + "dependencies": { + "string-width": "^4.2.0", + "strip-ansi": "^6.0.1", + "wrap-ansi": "^7.0.0" + }, + "engines": { + "node": ">=12" + } + }, + "packages/test-utils/node_modules/emoji-regex": { + "version": "8.0.0", + "resolved": "https://registry.npmjs.org/emoji-regex/-/emoji-regex-8.0.0.tgz", + "integrity": "sha512-MSjYzcWNOA0ewAHpz0MxpYFvwg6yjy1NG3xteoqz644VCo/RPgnr1/GGt+ic3iJTzQ8Eu3TdM14SawnVUmGE6A==", + "dev": true + }, + "packages/test-utils/node_modules/string-width": { + "version": "4.2.3", + "resolved": "https://registry.npmjs.org/string-width/-/string-width-4.2.3.tgz", + "integrity": "sha512-wKyQRQpjJ0sIp62ErSZdGsjMJWsap5oRNihHhu6G7JVO/9jIB6UyevL+tXuOqrng8j/cxKTWyWUwvSTriiZz/g==", + "dev": true, + "dependencies": { + "emoji-regex": "^8.0.0", + "is-fullwidth-code-point": "^3.0.0", + "strip-ansi": "^6.0.1" + }, + "engines": { + "node": ">=8" + } + }, + "packages/test-utils/node_modules/strip-ansi": { + "version": "6.0.1", + "resolved": "https://registry.npmjs.org/strip-ansi/-/strip-ansi-6.0.1.tgz", + "integrity": "sha512-Y38VPSHcqkFrCpFnQ9vuSXmquuv5oXOKpGeT6aGrr3o3Gc9AlVa6JBfUSOCnbxGGZF+/0ooI7KrPuUSztUdU5A==", + "dev": true, + "dependencies": { + "ansi-regex": "^5.0.1" + }, + "engines": { + "node": ">=8" + } + }, + "packages/test-utils/node_modules/wrap-ansi": { + "version": "7.0.0", + "resolved": "https://registry.npmjs.org/wrap-ansi/-/wrap-ansi-7.0.0.tgz", + "integrity": "sha512-YVGIj2kamLSTxw6NsZjoBxfSwsn0ycdesmc4p+Q21c5zPuZ1pl+NfxVdxPtdHvmNVOQ6XSYG4AUtyt/Fi7D16Q==", + "dev": true, + "dependencies": { + "ansi-styles": "^4.0.0", + "string-width": "^4.1.0", + "strip-ansi": "^6.0.0" + }, + "engines": { + "node": ">=10" + }, + "funding": { + "url": "https://github.com/chalk/wrap-ansi?sponsor=1" + } + }, + "packages/test-utils/node_modules/y18n": { + "version": "5.0.8", + "resolved": "https://registry.npmjs.org/y18n/-/y18n-5.0.8.tgz", + "integrity": "sha512-0pfFzegeDWJHJIAmTLRP2DwHjdF5s7jo9tuztdQxAhINCdvS+3nGINqPd00AphqJR/0LhANUS6/+7SCb98YOfA==", + "dev": true, + "engines": { + "node": ">=10" + } + }, + "packages/test-utils/node_modules/yargs": { + "version": "17.6.2", + "resolved": "https://registry.npmjs.org/yargs/-/yargs-17.6.2.tgz", + "integrity": "sha512-1/9UrdHjDZc0eOU0HxOHoS78C69UD3JRMvzlJ7S79S2nTaWRA/whGCTV8o9e/N/1Va9YIV7Q4sOxD8VV4pCWOw==", + "dev": true, + "dependencies": { + "cliui": "^8.0.1", + "escalade": "^3.1.1", + "get-caller-file": "^2.0.5", + "require-directory": "^2.1.1", + "string-width": "^4.2.3", + "y18n": "^5.0.5", + "yargs-parser": "^21.1.1" + }, + "engines": { + "node": ">=12" + } + }, "packages/time-series": { "name": "@redis/time-series", "version": "1.0.4", @@ -8162,13 +8459,13 @@ "devDependencies": { "@istanbuljs/nyc-config-typescript": "^1.0.2", "@redis/test-utils": "*", - "@types/node": "^18.11.6", + "@types/node": "^18.11.18", "nyc": "^15.1.0", - "release-it": "^15.5.0", + "release-it": "^15.6.0", "source-map-support": "^0.5.21", "ts-node": "^10.9.1", - "typedoc": "^0.23.18", - "typescript": "^4.8.4" + "typedoc": "^0.23.24", + "typescript": "^4.9.4" }, "peerDependencies": { "@redis/client": "^1.0.0" @@ -8196,49 +8493,41 @@ } }, "@babel/compat-data": { - "version": "7.19.4", - "resolved": "https://registry.npmjs.org/@babel/compat-data/-/compat-data-7.19.4.tgz", - "integrity": "sha512-CHIGpJcUQ5lU9KrPHTjBMhVwQG6CQjxfg36fGXl3qk/Gik1WwWachaXFuo0uCWJT/mStOKtcbFJCaVLihC1CMw==", + "version": "7.20.10", + "resolved": "https://registry.npmjs.org/@babel/compat-data/-/compat-data-7.20.10.tgz", + "integrity": "sha512-sEnuDPpOJR/fcafHMjpcpGN5M2jbUGUHwmuWKM/YdPzeEDJg8bgmbcWQFUfE32MQjti1koACvoPVsDe8Uq+idg==", "dev": true }, "@babel/core": { - "version": "7.19.6", - "resolved": "https://registry.npmjs.org/@babel/core/-/core-7.19.6.tgz", - "integrity": "sha512-D2Ue4KHpc6Ys2+AxpIx1BZ8+UegLLLE2p3KJEuJRKmokHOtl49jQ5ny1773KsGLZs8MQvBidAF6yWUJxRqtKtg==", + "version": "7.20.12", + "resolved": "https://registry.npmjs.org/@babel/core/-/core-7.20.12.tgz", + "integrity": "sha512-XsMfHovsUYHFMdrIHkZphTN/2Hzzi78R08NuHfDBehym2VsPDL6Zn/JAD/JQdnRvbSsbQc4mVaU1m6JgtTEElg==", "dev": true, "requires": { "@ampproject/remapping": "^2.1.0", "@babel/code-frame": "^7.18.6", - "@babel/generator": "^7.19.6", - "@babel/helper-compilation-targets": "^7.19.3", - "@babel/helper-module-transforms": "^7.19.6", - "@babel/helpers": "^7.19.4", - "@babel/parser": "^7.19.6", - "@babel/template": "^7.18.10", - "@babel/traverse": "^7.19.6", - "@babel/types": "^7.19.4", + "@babel/generator": "^7.20.7", + "@babel/helper-compilation-targets": "^7.20.7", + "@babel/helper-module-transforms": "^7.20.11", + "@babel/helpers": "^7.20.7", + "@babel/parser": "^7.20.7", + "@babel/template": "^7.20.7", + "@babel/traverse": "^7.20.12", + "@babel/types": "^7.20.7", "convert-source-map": "^1.7.0", "debug": "^4.1.0", "gensync": "^1.0.0-beta.2", - "json5": "^2.2.1", + "json5": "^2.2.2", "semver": "^6.3.0" - }, - "dependencies": { - "semver": { - "version": "6.3.0", - "resolved": "https://registry.npmjs.org/semver/-/semver-6.3.0.tgz", - "integrity": "sha512-b39TBaTSfV6yBrapU89p5fKekE2m/NwnDocOVruQFS1/veMgdzuPcnOM34M6CwxW8jH/lxEa5rBoDeUwu5HHTw==", - "dev": true - } } }, "@babel/generator": { - "version": "7.19.6", - "resolved": "https://registry.npmjs.org/@babel/generator/-/generator-7.19.6.tgz", - "integrity": "sha512-oHGRUQeoX1QrKeJIKVe0hwjGqNnVYsM5Nep5zo0uE0m42sLH+Fsd2pStJ5sRM1bNyTUUoz0pe2lTeMJrb/taTA==", + "version": "7.20.7", + "resolved": "https://registry.npmjs.org/@babel/generator/-/generator-7.20.7.tgz", + "integrity": "sha512-7wqMOJq8doJMZmP4ApXTzLxSr7+oO2jroJURrVEp6XShrQUObV8Tq/D0NCcoYg2uHqUrjzO0zwBjoYzelxK+sw==", "dev": true, "requires": { - "@babel/types": "^7.19.4", + "@babel/types": "^7.20.7", "@jridgewell/gen-mapping": "^0.3.2", "jsesc": "^2.5.1" }, @@ -8257,23 +8546,16 @@ } }, "@babel/helper-compilation-targets": { - "version": "7.19.3", - "resolved": "https://registry.npmjs.org/@babel/helper-compilation-targets/-/helper-compilation-targets-7.19.3.tgz", - "integrity": "sha512-65ESqLGyGmLvgR0mst5AdW1FkNlj9rQsCKduzEoEPhBCDFGXvz2jW6bXFG6i0/MrV2s7hhXjjb2yAzcPuQlLwg==", + "version": "7.20.7", + "resolved": "https://registry.npmjs.org/@babel/helper-compilation-targets/-/helper-compilation-targets-7.20.7.tgz", + "integrity": "sha512-4tGORmfQcrc+bvrjb5y3dG9Mx1IOZjsHqQVUz7XCNHO+iTmqxWnVg3KRygjGmpRLJGdQSKuvFinbIb0CnZwHAQ==", "dev": true, "requires": { - "@babel/compat-data": "^7.19.3", + "@babel/compat-data": "^7.20.5", "@babel/helper-validator-option": "^7.18.6", - "browserslist": "^4.21.3", - "semver": "^6.3.0" - }, - "dependencies": { - "semver": { - "version": "6.3.0", - "resolved": "https://registry.npmjs.org/semver/-/semver-6.3.0.tgz", - "integrity": "sha512-b39TBaTSfV6yBrapU89p5fKekE2m/NwnDocOVruQFS1/veMgdzuPcnOM34M6CwxW8jH/lxEa5rBoDeUwu5HHTw==", - "dev": true - } + "browserslist": "^4.21.3", + "lru-cache": "^5.1.1", + "semver": "^6.3.0" } }, "@babel/helper-environment-visitor": { @@ -8311,28 +8593,28 @@ } }, "@babel/helper-module-transforms": { - "version": "7.19.6", - "resolved": "https://registry.npmjs.org/@babel/helper-module-transforms/-/helper-module-transforms-7.19.6.tgz", - "integrity": "sha512-fCmcfQo/KYr/VXXDIyd3CBGZ6AFhPFy1TfSEJ+PilGVlQT6jcbqtHAM4C1EciRqMza7/TpOUZliuSH+U6HAhJw==", + "version": "7.20.11", + "resolved": "https://registry.npmjs.org/@babel/helper-module-transforms/-/helper-module-transforms-7.20.11.tgz", + "integrity": "sha512-uRy78kN4psmji1s2QtbtcCSaj/LILFDp0f/ymhpQH5QY3nljUZCaNWz9X1dEj/8MBdBEFECs7yRhKn8i7NjZgg==", "dev": true, "requires": { "@babel/helper-environment-visitor": "^7.18.9", "@babel/helper-module-imports": "^7.18.6", - "@babel/helper-simple-access": "^7.19.4", + "@babel/helper-simple-access": "^7.20.2", "@babel/helper-split-export-declaration": "^7.18.6", "@babel/helper-validator-identifier": "^7.19.1", - "@babel/template": "^7.18.10", - "@babel/traverse": "^7.19.6", - "@babel/types": "^7.19.4" + "@babel/template": "^7.20.7", + "@babel/traverse": "^7.20.10", + "@babel/types": "^7.20.7" } }, "@babel/helper-simple-access": { - "version": "7.19.4", - "resolved": "https://registry.npmjs.org/@babel/helper-simple-access/-/helper-simple-access-7.19.4.tgz", - "integrity": "sha512-f9Xq6WqBFqaDfbCzn2w85hwklswz5qsKlh7f08w4Y9yhJHpnNC0QemtSkK5YyOY8kPGvyiwdzZksGUhnGdaUIg==", + "version": "7.20.2", + "resolved": "https://registry.npmjs.org/@babel/helper-simple-access/-/helper-simple-access-7.20.2.tgz", + "integrity": "sha512-+0woI/WPq59IrqDYbVGfshjT5Dmk/nnbdpcF8SnMhhXObpTq2KNBdLFRFrkVdbDOyUmHBCxzm5FHV1rACIkIbA==", "dev": true, "requires": { - "@babel/types": "^7.19.4" + "@babel/types": "^7.20.2" } }, "@babel/helper-split-export-declaration": { @@ -8363,14 +8645,14 @@ "dev": true }, "@babel/helpers": { - "version": "7.19.4", - "resolved": "https://registry.npmjs.org/@babel/helpers/-/helpers-7.19.4.tgz", - "integrity": "sha512-G+z3aOx2nfDHwX/kyVii5fJq+bgscg89/dJNWpYeKeBv3v9xX8EIabmx1k6u9LS04H7nROFVRVK+e3k0VHp+sw==", + "version": "7.20.7", + "resolved": "https://registry.npmjs.org/@babel/helpers/-/helpers-7.20.7.tgz", + "integrity": "sha512-PBPjs5BppzsGaxHQCDKnZ6Gd9s6xl8bBCluz3vEInLGRJmnZan4F6BYCeqtyXqkk4W5IlPmjK4JlOuZkpJ3xZA==", "dev": true, "requires": { - "@babel/template": "^7.18.10", - "@babel/traverse": "^7.19.4", - "@babel/types": "^7.19.4" + "@babel/template": "^7.20.7", + "@babel/traverse": "^7.20.7", + "@babel/types": "^7.20.7" } }, "@babel/highlight": { @@ -8443,36 +8725,36 @@ } }, "@babel/parser": { - "version": "7.19.6", - "resolved": "https://registry.npmjs.org/@babel/parser/-/parser-7.19.6.tgz", - "integrity": "sha512-h1IUp81s2JYJ3mRkdxJgs4UvmSsRvDrx5ICSJbPvtWYv5i1nTBGcBpnog+89rAFMwvvru6E5NUHdBe01UeSzYA==", + "version": "7.20.7", + "resolved": "https://registry.npmjs.org/@babel/parser/-/parser-7.20.7.tgz", + "integrity": "sha512-T3Z9oHybU+0vZlY9CiDSJQTD5ZapcW18ZctFMi0MOAl/4BjFF4ul7NVSARLdbGO5vDqy9eQiGTV0LtKfvCYvcg==", "dev": true }, "@babel/template": { - "version": "7.18.10", - "resolved": "https://registry.npmjs.org/@babel/template/-/template-7.18.10.tgz", - "integrity": "sha512-TI+rCtooWHr3QJ27kJxfjutghu44DLnasDMwpDqCXVTal9RLp3RSYNh4NdBrRP2cQAoG9A8juOQl6P6oZG4JxA==", + "version": "7.20.7", + "resolved": "https://registry.npmjs.org/@babel/template/-/template-7.20.7.tgz", + "integrity": "sha512-8SegXApWe6VoNw0r9JHpSteLKTpTiLZ4rMlGIm9JQ18KiCtyQiAMEazujAHrUS5flrcqYZa75ukev3P6QmUwUw==", "dev": true, "requires": { "@babel/code-frame": "^7.18.6", - "@babel/parser": "^7.18.10", - "@babel/types": "^7.18.10" + "@babel/parser": "^7.20.7", + "@babel/types": "^7.20.7" } }, "@babel/traverse": { - "version": "7.19.6", - "resolved": "https://registry.npmjs.org/@babel/traverse/-/traverse-7.19.6.tgz", - "integrity": "sha512-6l5HrUCzFM04mfbG09AagtYyR2P0B71B1wN7PfSPiksDPz2k5H9CBC1tcZpz2M8OxbKTPccByoOJ22rUKbpmQQ==", + "version": "7.20.12", + "resolved": "https://registry.npmjs.org/@babel/traverse/-/traverse-7.20.12.tgz", + "integrity": "sha512-MsIbFN0u+raeja38qboyF8TIT7K0BFzz/Yd/77ta4MsUsmP2RAnidIlwq7d5HFQrH/OZJecGV6B71C4zAgpoSQ==", "dev": true, "requires": { "@babel/code-frame": "^7.18.6", - "@babel/generator": "^7.19.6", + "@babel/generator": "^7.20.7", "@babel/helper-environment-visitor": "^7.18.9", "@babel/helper-function-name": "^7.19.0", "@babel/helper-hoist-variables": "^7.18.6", "@babel/helper-split-export-declaration": "^7.18.6", - "@babel/parser": "^7.19.6", - "@babel/types": "^7.19.4", + "@babel/parser": "^7.20.7", + "@babel/types": "^7.20.7", "debug": "^4.1.0", "globals": "^11.1.0" }, @@ -8486,9 +8768,9 @@ } }, "@babel/types": { - "version": "7.19.4", - "resolved": "https://registry.npmjs.org/@babel/types/-/types-7.19.4.tgz", - "integrity": "sha512-M5LK7nAeS6+9j7hAq+b3fQs+pNfUtTGq+yFFfHnauFA8zQtLRfmuipmsKDKKLuyG+wC8ABW43A153YNawNTEtw==", + "version": "7.20.7", + "resolved": "https://registry.npmjs.org/@babel/types/-/types-7.20.7.tgz", + "integrity": "sha512-69OnhBxSSgK0OzTJai4kyPDiKTIe3j+ctaHdIGVbRahTLAT7L3R9oeXHC2aVSuGYt3cVnoAMDmOCgJ2yaiLMvg==", "dev": true, "requires": { "@babel/helper-string-parser": "^7.19.4", @@ -8518,15 +8800,15 @@ } }, "@eslint/eslintrc": { - "version": "1.3.3", - "resolved": "https://registry.npmjs.org/@eslint/eslintrc/-/eslintrc-1.3.3.tgz", - "integrity": "sha512-uj3pT6Mg+3t39fvLrj8iuCIJ38zKO9FpGtJ4BBJebJhEwjoT+KLVNCcHT5QC9NGRIEi7fZ0ZR8YRb884auB4Lg==", + "version": "1.4.1", + "resolved": "https://registry.npmjs.org/@eslint/eslintrc/-/eslintrc-1.4.1.tgz", + "integrity": "sha512-XXrH9Uarn0stsyldqDYq8r++mROmWRI1xKMXa640Bb//SY1+ECYX6VzT6Lcx5frD0V30XieqJ0oX9I2Xj5aoMA==", "dev": true, "requires": { "ajv": "^6.12.4", "debug": "^4.3.2", "espree": "^9.4.0", - "globals": "^13.15.0", + "globals": "^13.19.0", "ignore": "^5.2.0", "import-fresh": "^3.2.1", "js-yaml": "^4.1.0", @@ -8535,14 +8817,14 @@ } }, "@humanwhocodes/config-array": { - "version": "0.11.6", - "resolved": "https://registry.npmjs.org/@humanwhocodes/config-array/-/config-array-0.11.6.tgz", - "integrity": "sha512-jJr+hPTJYKyDILJfhNSHsjiwXYf26Flsz8DvNndOsHs5pwSnpGUEy8yzF0JYhCEvTDdV2vuOK5tt8BVhwO5/hg==", + "version": "0.11.8", + "resolved": "https://registry.npmjs.org/@humanwhocodes/config-array/-/config-array-0.11.8.tgz", + "integrity": "sha512-UybHIJzJnR5Qc/MsD9Kr+RpO2h+/P1GhOwdiLPXK5TWk5sgTdu88bTD9UP+CKbPPh5Rni1u0GjAdYQLemG8g+g==", "dev": true, "requires": { "@humanwhocodes/object-schema": "^1.2.1", "debug": "^4.1.1", - "minimatch": "^3.0.4" + "minimatch": "^3.0.5" } }, "@humanwhocodes/module-importer": { @@ -8585,16 +8867,6 @@ "sprintf-js": "~1.0.2" } }, - "find-up": { - "version": "4.1.0", - "resolved": "https://registry.npmjs.org/find-up/-/find-up-4.1.0.tgz", - "integrity": "sha512-PpOwAdQ/YlXQ2vj8a3h8IipDuYRi3wceVQQGYWxNINccq40Anw7BlsEXCMbt1Zt+OLA6Fq9suIpIWD0OsnISlw==", - "dev": true, - "requires": { - "locate-path": "^5.0.0", - "path-exists": "^4.0.0" - } - }, "js-yaml": { "version": "3.14.1", "resolved": "https://registry.npmjs.org/js-yaml/-/js-yaml-3.14.1.tgz", @@ -8605,33 +8877,6 @@ "esprima": "^4.0.0" } }, - "locate-path": { - "version": "5.0.0", - "resolved": "https://registry.npmjs.org/locate-path/-/locate-path-5.0.0.tgz", - "integrity": "sha512-t7hw9pI+WvuwNJXwk5zVHpyhIqzg2qTlklJOf0mVxGSbe3Fp2VieZcduNYjaLDoy6p9uGpQEGWG87WpMKlNq8g==", - "dev": true, - "requires": { - "p-locate": "^4.1.0" - } - }, - "p-limit": { - "version": "2.3.0", - "resolved": "https://registry.npmjs.org/p-limit/-/p-limit-2.3.0.tgz", - "integrity": "sha512-//88mFWSJx8lxCzwdAABTJL2MyWB12+eIY7MDL2SqLmAkeKU9qxRvWuSyTjm3FUmpBEMuFfckAIqEaVGUDxb6w==", - "dev": true, - "requires": { - "p-try": "^2.0.0" - } - }, - "p-locate": { - "version": "4.1.0", - "resolved": "https://registry.npmjs.org/p-locate/-/p-locate-4.1.0.tgz", - "integrity": "sha512-R79ZZ/0wAxKGu3oYMlz8jy/kbhsNrS7SKZ7PxEHBgJ5+F2mtFW2fK2cOtBh1cHYkQsbzFV7I+EoRKe6Yt0oK7A==", - "dev": true, - "requires": { - "p-limit": "^2.2.0" - } - }, "resolve-from": { "version": "5.0.0", "resolved": "https://registry.npmjs.org/resolve-from/-/resolve-from-5.0.0.tgz", @@ -8772,29 +9017,12 @@ "dev": true }, "@octokit/plugin-paginate-rest": { - "version": "4.3.1", - "resolved": "https://registry.npmjs.org/@octokit/plugin-paginate-rest/-/plugin-paginate-rest-4.3.1.tgz", - "integrity": "sha512-h8KKxESmSFTcXX409CAxlaOYscEDvN2KGQRsLCGT1NSqRW+D6EXLVQ8vuHhFznS9MuH9QYw1GfsUN30bg8hjVA==", + "version": "5.0.1", + "resolved": "https://registry.npmjs.org/@octokit/plugin-paginate-rest/-/plugin-paginate-rest-5.0.1.tgz", + "integrity": "sha512-7A+rEkS70pH36Z6JivSlR7Zqepz3KVucEFVDnSrgHXzG7WLAzYwcHZbKdfTXHwuTHbkT1vKvz7dHl1+HNf6Qyw==", "dev": true, "requires": { - "@octokit/types": "^7.5.0" - }, - "dependencies": { - "@octokit/openapi-types": { - "version": "13.13.1", - "resolved": "https://registry.npmjs.org/@octokit/openapi-types/-/openapi-types-13.13.1.tgz", - "integrity": "sha512-4EuKSk3N95UBWFau3Bz9b3pheQ8jQYbKmBL5+GSuY8YDPDwu03J4BjI+66yNi8aaX/3h1qDpb0mbBkLdr+cfGQ==", - "dev": true - }, - "@octokit/types": { - "version": "7.5.1", - "resolved": "https://registry.npmjs.org/@octokit/types/-/types-7.5.1.tgz", - "integrity": "sha512-Zk4OUMLCSpXNI8KZZn47lVLJSsgMyCimsWWQI5hyjZg7hdYm0kjotaIkbG0Pp8SfU2CofMBzonboTqvzn3FrJA==", - "dev": true, - "requires": { - "@octokit/openapi-types": "^13.11.0" - } - } + "@octokit/types": "^8.0.0" } }, "@octokit/plugin-request-log": { @@ -8829,9 +9057,9 @@ }, "dependencies": { "node-fetch": { - "version": "2.6.7", - "resolved": "https://registry.npmjs.org/node-fetch/-/node-fetch-2.6.7.tgz", - "integrity": "sha512-ZjMPFEfVx5j+y2yF35Kzx5sF7kDzxuDj6ziH4FFbOp87zKDZNx8yExJIb05OGF4Nlt9IHFIMBkRl41VdvcNdbQ==", + "version": "2.6.8", + "resolved": "https://registry.npmjs.org/node-fetch/-/node-fetch-2.6.8.tgz", + "integrity": "sha512-RZ6dBYuj8dRSfxpUSu+NsdF1dpPpluJxwOp+6IoDp/sH2QNDSvurYsAa+F1WxY2RjA1iP93xhcsUoYbF2XBqVg==", "dev": true, "requires": { "whatwg-url": "^5.0.0" @@ -8851,30 +9079,30 @@ } }, "@octokit/rest": { - "version": "19.0.4", - "resolved": "https://registry.npmjs.org/@octokit/rest/-/rest-19.0.4.tgz", - "integrity": "sha512-LwG668+6lE8zlSYOfwPj4FxWdv/qFXYBpv79TWIQEpBLKA9D/IMcWsF/U9RGpA3YqMVDiTxpgVpEW3zTFfPFTA==", + "version": "19.0.5", + "resolved": "https://registry.npmjs.org/@octokit/rest/-/rest-19.0.5.tgz", + "integrity": "sha512-+4qdrUFq2lk7Va+Qff3ofREQWGBeoTKNqlJO+FGjFP35ZahP+nBenhZiGdu8USSgmq4Ky3IJ/i4u0xbLqHaeow==", "dev": true, "requires": { - "@octokit/core": "^4.0.0", - "@octokit/plugin-paginate-rest": "^4.0.0", + "@octokit/core": "^4.1.0", + "@octokit/plugin-paginate-rest": "^5.0.0", "@octokit/plugin-request-log": "^1.0.4", - "@octokit/plugin-rest-endpoint-methods": "^6.0.0" + "@octokit/plugin-rest-endpoint-methods": "^6.7.0" } }, "@octokit/types": { - "version": "8.0.0", - "resolved": "https://registry.npmjs.org/@octokit/types/-/types-8.0.0.tgz", - "integrity": "sha512-65/TPpOJP1i3K4lBJMnWqPUJ6zuOtzhtagDvydAWbEXpbFYA0oMKKyLb95NFZZP0lSh/4b6K+DQlzvYQJQQePg==", + "version": "8.1.1", + "resolved": "https://registry.npmjs.org/@octokit/types/-/types-8.1.1.tgz", + "integrity": "sha512-7tjk+6DyhYAmei8FOEwPfGKc0VE1x56CKPJ+eE44zhDbOyMT+9yan8apfQFxo8oEFsy+0O7PiBtH8w0Yo0Y9Kw==", "dev": true, "requires": { "@octokit/openapi-types": "^14.0.0" } }, "@pnpm/network.ca-file": { - "version": "1.0.1", - "resolved": "https://registry.npmjs.org/@pnpm/network.ca-file/-/network.ca-file-1.0.1.tgz", - "integrity": "sha512-gkINruT2KUhZLTaiHxwCOh1O4NVnFT0wLjWFBHmTz9vpKag/C/noIMJXBxFe4F0mYpUVX2puLwAieLYFg2NvoA==", + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/@pnpm/network.ca-file/-/network.ca-file-1.0.2.tgz", + "integrity": "sha512-YcPQ8a0jwYU9bTdJDpXjMi7Brhkr1mXsXrUJvjqM2mQDgkRiz8jFaQGOdaLxgjtUfQgZhKy/O3cG/YwmgKaxLA==", "dev": true, "requires": { "graceful-fs": "4.2.10" @@ -8895,13 +9123,13 @@ "requires": { "@istanbuljs/nyc-config-typescript": "^1.0.2", "@redis/test-utils": "*", - "@types/node": "^18.11.6", + "@types/node": "^18.11.18", "nyc": "^15.1.0", - "release-it": "^15.5.0", + "release-it": "^15.6.0", "source-map-support": "^0.5.21", "ts-node": "^10.9.1", - "typedoc": "^0.23.18", - "typescript": "^4.8.4" + "typedoc": "^0.23.24", + "typescript": "^4.9.4" } }, "@redis/client": { @@ -8909,26 +9137,28 @@ "requires": { "@istanbuljs/nyc-config-typescript": "^1.0.2", "@redis/test-utils": "*", - "@types/node": "^18.11.6", + "@types/node": "^18.11.18", "@types/sinon": "^10.0.13", "@types/yallist": "^4.0.1", - "@typescript-eslint/eslint-plugin": "^5.41.0", - "@typescript-eslint/parser": "^5.41.0", - "cluster-key-slot": "1.1.1", - "eslint": "^8.26.0", + "@typescript-eslint/eslint-plugin": "^5.49.0", + "@typescript-eslint/parser": "^5.49.0", + "cluster-key-slot": "1.1.2", + "eslint": "^8.32.0", "generic-pool": "3.9.0", "nyc": "^15.1.0", - "release-it": "^15.5.0", - "sinon": "^14.0.1", + "release-it": "^15.6.0", + "sinon": "^15.0.1", "source-map-support": "^0.5.21", "ts-node": "^10.9.1", - "typedoc": "^0.23.18", - "typescript": "^4.8.4", + "typedoc": "^0.23.24", + "typescript": "^4.9.4", "yallist": "4.0.0" }, "dependencies": { "yallist": { - "version": "4.0.0" + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/yallist/-/yallist-4.0.0.tgz", + "integrity": "sha512-3wdGidZyq5PB084XLES5TpOSRA3wjXAlIWMhum2kRcv/41Sn2emQ0dycQW4uZXLejwKvg6EsvbdlVL+FYEct7A==" } } }, @@ -8937,13 +9167,13 @@ "requires": { "@istanbuljs/nyc-config-typescript": "^1.0.2", "@redis/test-utils": "*", - "@types/node": "^18.11.6", + "@types/node": "^18.11.18", "nyc": "^15.1.0", - "release-it": "^15.5.0", + "release-it": "^15.6.0", "source-map-support": "^0.5.21", "ts-node": "^10.9.1", - "typedoc": "^0.23.18", - "typescript": "^4.8.4" + "typedoc": "^0.23.24", + "typescript": "^4.9.4" } }, "@redis/json": { @@ -8951,13 +9181,13 @@ "requires": { "@istanbuljs/nyc-config-typescript": "^1.0.2", "@redis/test-utils": "*", - "@types/node": "^18.11.6", + "@types/node": "^18.11.18", "nyc": "^15.1.0", - "release-it": "^15.5.0", + "release-it": "^15.6.0", "source-map-support": "^0.5.21", "ts-node": "^10.9.1", - "typedoc": "^0.23.18", - "typescript": "^4.8.4" + "typedoc": "^0.23.24", + "typescript": "^4.9.4" } }, "@redis/search": { @@ -8965,28 +9195,114 @@ "requires": { "@istanbuljs/nyc-config-typescript": "^1.0.2", "@redis/test-utils": "*", - "@types/node": "^18.11.6", + "@types/node": "^18.11.18", "nyc": "^15.1.0", - "release-it": "^15.5.0", + "release-it": "^15.6.0", "source-map-support": "^0.5.21", "ts-node": "^10.9.1", - "typedoc": "^0.23.18", - "typescript": "^4.8.4" + "typedoc": "^0.23.24", + "typescript": "^4.9.4" } }, "@redis/test-utils": { "version": "file:packages/test-utils", "requires": { "@istanbuljs/nyc-config-typescript": "^1.0.2", - "@types/mocha": "^10.0.0", - "@types/node": "^18.11.6", - "@types/yargs": "^17.0.13", - "mocha": "^10.1.0", + "@types/mocha": "^10.0.1", + "@types/node": "^18.11.18", + "@types/yargs": "^17.0.20", + "mocha": "^10.2.0", "nyc": "^15.1.0", "source-map-support": "^0.5.21", "ts-node": "^10.9.1", - "typescript": "^4.8.4", - "yargs": "^17.6.0" + "typescript": "^4.9.4", + "yargs": "^17.6.2" + }, + "dependencies": { + "ansi-regex": { + "version": "5.0.1", + "resolved": "https://registry.npmjs.org/ansi-regex/-/ansi-regex-5.0.1.tgz", + "integrity": "sha512-quJQXlTSUGL2LH9SUXo8VwsY4soanhgo6LNSm84E1LBcE8s3O0wpdiRzyR9z/ZZJMlMWv37qOOb9pdJlMUEKFQ==", + "dev": true + }, + "ansi-styles": { + "version": "4.3.0", + "resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-4.3.0.tgz", + "integrity": "sha512-zbB9rCJAT1rbjiVDb2hqKFHNYLxgtk8NURxZ3IZwD3F6NtxbXZQCnnSi1Lkx+IDohdPlFp222wVALIheZJQSEg==", + "dev": true, + "requires": { + "color-convert": "^2.0.1" + } + }, + "cliui": { + "version": "8.0.1", + "resolved": "https://registry.npmjs.org/cliui/-/cliui-8.0.1.tgz", + "integrity": "sha512-BSeNnyus75C4//NQ9gQt1/csTXyo/8Sb+afLAkzAptFuMsod9HFokGNudZpi/oQV73hnVK+sR+5PVRMd+Dr7YQ==", + "dev": true, + "requires": { + "string-width": "^4.2.0", + "strip-ansi": "^6.0.1", + "wrap-ansi": "^7.0.0" + } + }, + "emoji-regex": { + "version": "8.0.0", + "resolved": "https://registry.npmjs.org/emoji-regex/-/emoji-regex-8.0.0.tgz", + "integrity": "sha512-MSjYzcWNOA0ewAHpz0MxpYFvwg6yjy1NG3xteoqz644VCo/RPgnr1/GGt+ic3iJTzQ8Eu3TdM14SawnVUmGE6A==", + "dev": true + }, + "string-width": { + "version": "4.2.3", + "resolved": "https://registry.npmjs.org/string-width/-/string-width-4.2.3.tgz", + "integrity": "sha512-wKyQRQpjJ0sIp62ErSZdGsjMJWsap5oRNihHhu6G7JVO/9jIB6UyevL+tXuOqrng8j/cxKTWyWUwvSTriiZz/g==", + "dev": true, + "requires": { + "emoji-regex": "^8.0.0", + "is-fullwidth-code-point": "^3.0.0", + "strip-ansi": "^6.0.1" + } + }, + "strip-ansi": { + "version": "6.0.1", + "resolved": "https://registry.npmjs.org/strip-ansi/-/strip-ansi-6.0.1.tgz", + "integrity": "sha512-Y38VPSHcqkFrCpFnQ9vuSXmquuv5oXOKpGeT6aGrr3o3Gc9AlVa6JBfUSOCnbxGGZF+/0ooI7KrPuUSztUdU5A==", + "dev": true, + "requires": { + "ansi-regex": "^5.0.1" + } + }, + "wrap-ansi": { + "version": "7.0.0", + "resolved": "https://registry.npmjs.org/wrap-ansi/-/wrap-ansi-7.0.0.tgz", + "integrity": "sha512-YVGIj2kamLSTxw6NsZjoBxfSwsn0ycdesmc4p+Q21c5zPuZ1pl+NfxVdxPtdHvmNVOQ6XSYG4AUtyt/Fi7D16Q==", + "dev": true, + "requires": { + "ansi-styles": "^4.0.0", + "string-width": "^4.1.0", + "strip-ansi": "^6.0.0" + } + }, + "y18n": { + "version": "5.0.8", + "resolved": "https://registry.npmjs.org/y18n/-/y18n-5.0.8.tgz", + "integrity": "sha512-0pfFzegeDWJHJIAmTLRP2DwHjdF5s7jo9tuztdQxAhINCdvS+3nGINqPd00AphqJR/0LhANUS6/+7SCb98YOfA==", + "dev": true + }, + "yargs": { + "version": "17.6.2", + "resolved": "https://registry.npmjs.org/yargs/-/yargs-17.6.2.tgz", + "integrity": "sha512-1/9UrdHjDZc0eOU0HxOHoS78C69UD3JRMvzlJ7S79S2nTaWRA/whGCTV8o9e/N/1Va9YIV7Q4sOxD8VV4pCWOw==", + "dev": true, + "requires": { + "cliui": "^8.0.1", + "escalade": "^3.1.1", + "get-caller-file": "^2.0.5", + "require-directory": "^2.1.1", + "string-width": "^4.2.3", + "y18n": "^5.0.5", + "yargs-parser": "^21.1.1" + } + } } }, "@redis/time-series": { @@ -8994,13 +9310,13 @@ "requires": { "@istanbuljs/nyc-config-typescript": "^1.0.2", "@redis/test-utils": "*", - "@types/node": "^18.11.6", + "@types/node": "^18.11.18", "nyc": "^15.1.0", - "release-it": "^15.5.0", + "release-it": "^15.6.0", "source-map-support": "^0.5.21", "ts-node": "^10.9.1", - "typedoc": "^0.23.18", - "typescript": "^4.8.4" + "typedoc": "^0.23.24", + "typescript": "^4.9.4" } }, "@sindresorhus/is": { @@ -9010,30 +9326,30 @@ "dev": true }, "@sinonjs/commons": { - "version": "1.8.3", - "resolved": "https://registry.npmjs.org/@sinonjs/commons/-/commons-1.8.3.tgz", - "integrity": "sha512-xkNcLAn/wZaX14RPlwizcKicDk9G3F8m2nU3L7Ukm5zBgTwiT0wsoFAHx9Jq56fJA1z/7uKGtCRu16sOUCLIHQ==", + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/@sinonjs/commons/-/commons-2.0.0.tgz", + "integrity": "sha512-uLa0j859mMrg2slwQYdO/AkrOfmH+X6LTVmNTS9CqexuE2IvVORIkSpJLqePAbEnKJ77aMmCwr1NUZ57120Xcg==", "dev": true, "requires": { "type-detect": "4.0.8" } }, "@sinonjs/fake-timers": { - "version": "9.1.2", - "resolved": "https://registry.npmjs.org/@sinonjs/fake-timers/-/fake-timers-9.1.2.tgz", - "integrity": "sha512-BPS4ynJW/o92PUR4wgriz2Ud5gpST5vz6GQfMixEDK0Z8ZCUv2M7SkBLykH56T++Xs+8ln9zTGbOvNGIe02/jw==", + "version": "10.0.2", + "resolved": "https://registry.npmjs.org/@sinonjs/fake-timers/-/fake-timers-10.0.2.tgz", + "integrity": "sha512-SwUDyjWnah1AaNl7kxsa7cfLhlTYoiyhDAIgyh+El30YvXs/o7OLXpYH88Zdhyx9JExKrmHDJ+10bwIcY80Jmw==", "dev": true, "requires": { - "@sinonjs/commons": "^1.7.0" + "@sinonjs/commons": "^2.0.0" } }, "@sinonjs/samsam": { - "version": "6.1.1", - "resolved": "https://registry.npmjs.org/@sinonjs/samsam/-/samsam-6.1.1.tgz", - "integrity": "sha512-cZ7rKJTLiE7u7Wi/v9Hc2fs3Ucc3jrWeMgPHbbTCeVAB2S0wOBbYlkJVeNSL04i7fdhT8wIbDq1zhC/PXTD2SA==", + "version": "7.0.1", + "resolved": "https://registry.npmjs.org/@sinonjs/samsam/-/samsam-7.0.1.tgz", + "integrity": "sha512-zsAk2Jkiq89mhZovB2LLOdTCxJF4hqqTToGP0ASWlhp4I1hqOjcfmZGafXntCN7MDC6yySH0mFHrYtHceOeLmw==", "dev": true, "requires": { - "@sinonjs/commons": "^1.6.0", + "@sinonjs/commons": "^2.0.0", "lodash.get": "^4.4.2", "type-detect": "^4.0.8" } @@ -9096,27 +9412,21 @@ "dev": true }, "@types/mocha": { - "version": "10.0.0", - "resolved": "https://registry.npmjs.org/@types/mocha/-/mocha-10.0.0.tgz", - "integrity": "sha512-rADY+HtTOA52l9VZWtgQfn4p+UDVM2eDVkMZT1I6syp0YKxW2F9v+0pbRZLsvskhQv/vMb6ZfCay81GHbz5SHg==", + "version": "10.0.1", + "resolved": "https://registry.npmjs.org/@types/mocha/-/mocha-10.0.1.tgz", + "integrity": "sha512-/fvYntiO1GeICvqbQ3doGDIP97vWmvFt83GKguJ6prmQM2iXZfFcq6YE8KteFyRtX2/h5Hf91BYvPodJKFYv5Q==", "dev": true }, "@types/node": { - "version": "18.11.6", - "resolved": "https://registry.npmjs.org/@types/node/-/node-18.11.6.tgz", - "integrity": "sha512-j3CEDa2vd96K0AXF8Wur7UucACvnjkk8hYyQAHhUNciabZLDl9nfAEVUSwmh245OOZV15bRA3Y590Gi5jUcDJg==", - "dev": true - }, - "@types/parse-json": { - "version": "4.0.0", - "resolved": "https://registry.npmjs.org/@types/parse-json/-/parse-json-4.0.0.tgz", - "integrity": "sha512-//oorEZjL6sbPcKUaCdIGlIUeH26mgzimjBB77G6XRgnDl/L5wOnpyBGRe/Mmf5CVW3PwEBE1NjiMZ/ssFh4wA==", + "version": "18.11.18", + "resolved": "https://registry.npmjs.org/@types/node/-/node-18.11.18.tgz", + "integrity": "sha512-DHQpWGjyQKSHj3ebjFI/wRKcqQcdR+MoFBygntYOZytCqNfkd2ZC4ARDJ2DQqhjH5p85Nnd3jhUJIXrszFX/JA==", "dev": true }, "@types/semver": { - "version": "7.3.12", - "resolved": "https://registry.npmjs.org/@types/semver/-/semver-7.3.12.tgz", - "integrity": "sha512-WwA1MW0++RfXmCr12xeYOOC5baSC9mSb0ZqCquFzKhcoF4TvHu5MKOuXsncgZcpVFhB1pXd5hZmM0ryAoCp12A==", + "version": "7.3.13", + "resolved": "https://registry.npmjs.org/@types/semver/-/semver-7.3.13.tgz", + "integrity": "sha512-21cFJr9z3g5dW8B0CVI9g2O9beqaThGQ6ZFBqHfwhzLDKUxaqTIy3vnfah/UPkfOiF2pLq+tGz+W8RyCskuslw==", "dev": true }, "@types/sinon": { @@ -9141,9 +9451,9 @@ "dev": true }, "@types/yargs": { - "version": "17.0.13", - "resolved": "https://registry.npmjs.org/@types/yargs/-/yargs-17.0.13.tgz", - "integrity": "sha512-9sWaruZk2JGxIQU+IhI1fhPYRcQ0UuTNuKuCW9bR5fp7qi2Llf7WDzNa17Cy7TKnh3cdxDOiyTu6gaLS0eDatg==", + "version": "17.0.20", + "resolved": "https://registry.npmjs.org/@types/yargs/-/yargs-17.0.20.tgz", + "integrity": "sha512-eknWrTHofQuPk2iuqDm1waA7V6xPlbgBoaaXEgYkClhLOnB0TtbW+srJaOToAgawPxPlHQzwypFA2bhZaUGP5A==", "dev": true, "requires": { "@types/yargs-parser": "*" @@ -9156,99 +9466,198 @@ "dev": true }, "@typescript-eslint/eslint-plugin": { - "version": "5.41.0", - "resolved": "https://registry.npmjs.org/@typescript-eslint/eslint-plugin/-/eslint-plugin-5.41.0.tgz", - "integrity": "sha512-DXUS22Y57/LAFSg3x7Vi6RNAuLpTXwxB9S2nIA7msBb/Zt8p7XqMwdpdc1IU7CkOQUPgAqR5fWvxuKCbneKGmA==", + "version": "5.49.0", + "resolved": "https://registry.npmjs.org/@typescript-eslint/eslint-plugin/-/eslint-plugin-5.49.0.tgz", + "integrity": "sha512-IhxabIpcf++TBaBa1h7jtOWyon80SXPRLDq0dVz5SLFC/eW6tofkw/O7Ar3lkx5z5U6wzbKDrl2larprp5kk5Q==", "dev": true, "requires": { - "@typescript-eslint/scope-manager": "5.41.0", - "@typescript-eslint/type-utils": "5.41.0", - "@typescript-eslint/utils": "5.41.0", + "@typescript-eslint/scope-manager": "5.49.0", + "@typescript-eslint/type-utils": "5.49.0", + "@typescript-eslint/utils": "5.49.0", "debug": "^4.3.4", "ignore": "^5.2.0", + "natural-compare-lite": "^1.4.0", "regexpp": "^3.2.0", "semver": "^7.3.7", "tsutils": "^3.21.0" + }, + "dependencies": { + "lru-cache": { + "version": "6.0.0", + "resolved": "https://registry.npmjs.org/lru-cache/-/lru-cache-6.0.0.tgz", + "integrity": "sha512-Jo6dJ04CmSjuznwJSS3pUeWmd/H0ffTlkXXgwZi+eq1UCmqQwCh+eLsYOYCwY991i2Fah4h1BEMCx4qThGbsiA==", + "dev": true, + "requires": { + "yallist": "^4.0.0" + } + }, + "semver": { + "version": "7.3.8", + "resolved": "https://registry.npmjs.org/semver/-/semver-7.3.8.tgz", + "integrity": "sha512-NB1ctGL5rlHrPJtFDVIVzTyQylMLu9N9VICA6HSFJo8MCGVTMW6gfpicwKmmK/dAjTOrqu5l63JJOpDSrAis3A==", + "dev": true, + "requires": { + "lru-cache": "^6.0.0" + } + }, + "yallist": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/yallist/-/yallist-4.0.0.tgz", + "integrity": "sha512-3wdGidZyq5PB084XLES5TpOSRA3wjXAlIWMhum2kRcv/41Sn2emQ0dycQW4uZXLejwKvg6EsvbdlVL+FYEct7A==", + "dev": true + } } }, "@typescript-eslint/parser": { - "version": "5.41.0", - "resolved": "https://registry.npmjs.org/@typescript-eslint/parser/-/parser-5.41.0.tgz", - "integrity": "sha512-HQVfix4+RL5YRWZboMD1pUfFN8MpRH4laziWkkAzyO1fvNOY/uinZcvo3QiFJVS/siNHupV8E5+xSwQZrl6PZA==", + "version": "5.49.0", + "resolved": "https://registry.npmjs.org/@typescript-eslint/parser/-/parser-5.49.0.tgz", + "integrity": "sha512-veDlZN9mUhGqU31Qiv2qEp+XrJj5fgZpJ8PW30sHU+j/8/e5ruAhLaVDAeznS7A7i4ucb/s8IozpDtt9NqCkZg==", "dev": true, "requires": { - "@typescript-eslint/scope-manager": "5.41.0", - "@typescript-eslint/types": "5.41.0", - "@typescript-eslint/typescript-estree": "5.41.0", + "@typescript-eslint/scope-manager": "5.49.0", + "@typescript-eslint/types": "5.49.0", + "@typescript-eslint/typescript-estree": "5.49.0", "debug": "^4.3.4" } }, "@typescript-eslint/scope-manager": { - "version": "5.41.0", - "resolved": "https://registry.npmjs.org/@typescript-eslint/scope-manager/-/scope-manager-5.41.0.tgz", - "integrity": "sha512-xOxPJCnuktUkY2xoEZBKXO5DBCugFzjrVndKdUnyQr3+9aDWZReKq9MhaoVnbL+maVwWJu/N0SEtrtEUNb62QQ==", + "version": "5.49.0", + "resolved": "https://registry.npmjs.org/@typescript-eslint/scope-manager/-/scope-manager-5.49.0.tgz", + "integrity": "sha512-clpROBOiMIzpbWNxCe1xDK14uPZh35u4QaZO1GddilEzoCLAEz4szb51rBpdgurs5k2YzPtJeTEN3qVbG+LRUQ==", "dev": true, "requires": { - "@typescript-eslint/types": "5.41.0", - "@typescript-eslint/visitor-keys": "5.41.0" + "@typescript-eslint/types": "5.49.0", + "@typescript-eslint/visitor-keys": "5.49.0" } }, "@typescript-eslint/type-utils": { - "version": "5.41.0", - "resolved": "https://registry.npmjs.org/@typescript-eslint/type-utils/-/type-utils-5.41.0.tgz", - "integrity": "sha512-L30HNvIG6A1Q0R58e4hu4h+fZqaO909UcnnPbwKiN6Rc3BUEx6ez2wgN7aC0cBfcAjZfwkzE+E2PQQ9nEuoqfA==", + "version": "5.49.0", + "resolved": "https://registry.npmjs.org/@typescript-eslint/type-utils/-/type-utils-5.49.0.tgz", + "integrity": "sha512-eUgLTYq0tR0FGU5g1YHm4rt5H/+V2IPVkP0cBmbhRyEmyGe4XvJ2YJ6sYTmONfjmdMqyMLad7SB8GvblbeESZA==", "dev": true, "requires": { - "@typescript-eslint/typescript-estree": "5.41.0", - "@typescript-eslint/utils": "5.41.0", + "@typescript-eslint/typescript-estree": "5.49.0", + "@typescript-eslint/utils": "5.49.0", "debug": "^4.3.4", "tsutils": "^3.21.0" } }, "@typescript-eslint/types": { - "version": "5.41.0", - "resolved": "https://registry.npmjs.org/@typescript-eslint/types/-/types-5.41.0.tgz", - "integrity": "sha512-5BejraMXMC+2UjefDvrH0Fo/eLwZRV6859SXRg+FgbhA0R0l6lDqDGAQYhKbXhPN2ofk2kY5sgGyLNL907UXpA==", + "version": "5.49.0", + "resolved": "https://registry.npmjs.org/@typescript-eslint/types/-/types-5.49.0.tgz", + "integrity": "sha512-7If46kusG+sSnEpu0yOz2xFv5nRz158nzEXnJFCGVEHWnuzolXKwrH5Bsf9zsNlOQkyZuk0BZKKoJQI+1JPBBg==", "dev": true }, "@typescript-eslint/typescript-estree": { - "version": "5.41.0", - "resolved": "https://registry.npmjs.org/@typescript-eslint/typescript-estree/-/typescript-estree-5.41.0.tgz", - "integrity": "sha512-SlzFYRwFSvswzDSQ/zPkIWcHv8O5y42YUskko9c4ki+fV6HATsTODUPbRbcGDFYP86gaJL5xohUEytvyNNcXWg==", + "version": "5.49.0", + "resolved": "https://registry.npmjs.org/@typescript-eslint/typescript-estree/-/typescript-estree-5.49.0.tgz", + "integrity": "sha512-PBdx+V7deZT/3GjNYPVQv1Nc0U46dAHbIuOG8AZ3on3vuEKiPDwFE/lG1snN2eUB9IhF7EyF7K1hmTcLztNIsA==", "dev": true, "requires": { - "@typescript-eslint/types": "5.41.0", - "@typescript-eslint/visitor-keys": "5.41.0", + "@typescript-eslint/types": "5.49.0", + "@typescript-eslint/visitor-keys": "5.49.0", "debug": "^4.3.4", "globby": "^11.1.0", "is-glob": "^4.0.3", "semver": "^7.3.7", "tsutils": "^3.21.0" + }, + "dependencies": { + "array-union": { + "version": "2.1.0", + "resolved": "https://registry.npmjs.org/array-union/-/array-union-2.1.0.tgz", + "integrity": "sha512-HGyxoOTYUyCM6stUe6EJgnd4EoewAI7zMdfqO+kGjnlZmBDz/cR5pf8r/cR4Wq60sL/p0IkcjUEEPwS3GFrIyw==", + "dev": true + }, + "globby": { + "version": "11.1.0", + "resolved": "https://registry.npmjs.org/globby/-/globby-11.1.0.tgz", + "integrity": "sha512-jhIXaOzy1sb8IyocaruWSn1TjmnBVs8Ayhcy83rmxNJ8q2uWKCAj3CnJY+KpGSXCueAPc0i05kVvVKtP1t9S3g==", + "dev": true, + "requires": { + "array-union": "^2.1.0", + "dir-glob": "^3.0.1", + "fast-glob": "^3.2.9", + "ignore": "^5.2.0", + "merge2": "^1.4.1", + "slash": "^3.0.0" + } + }, + "lru-cache": { + "version": "6.0.0", + "resolved": "https://registry.npmjs.org/lru-cache/-/lru-cache-6.0.0.tgz", + "integrity": "sha512-Jo6dJ04CmSjuznwJSS3pUeWmd/H0ffTlkXXgwZi+eq1UCmqQwCh+eLsYOYCwY991i2Fah4h1BEMCx4qThGbsiA==", + "dev": true, + "requires": { + "yallist": "^4.0.0" + } + }, + "semver": { + "version": "7.3.8", + "resolved": "https://registry.npmjs.org/semver/-/semver-7.3.8.tgz", + "integrity": "sha512-NB1ctGL5rlHrPJtFDVIVzTyQylMLu9N9VICA6HSFJo8MCGVTMW6gfpicwKmmK/dAjTOrqu5l63JJOpDSrAis3A==", + "dev": true, + "requires": { + "lru-cache": "^6.0.0" + } + }, + "yallist": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/yallist/-/yallist-4.0.0.tgz", + "integrity": "sha512-3wdGidZyq5PB084XLES5TpOSRA3wjXAlIWMhum2kRcv/41Sn2emQ0dycQW4uZXLejwKvg6EsvbdlVL+FYEct7A==", + "dev": true + } } }, "@typescript-eslint/utils": { - "version": "5.41.0", - "resolved": "https://registry.npmjs.org/@typescript-eslint/utils/-/utils-5.41.0.tgz", - "integrity": "sha512-QlvfwaN9jaMga9EBazQ+5DDx/4sAdqDkcs05AsQHMaopluVCUyu1bTRUVKzXbgjDlrRAQrYVoi/sXJ9fmG+KLQ==", + "version": "5.49.0", + "resolved": "https://registry.npmjs.org/@typescript-eslint/utils/-/utils-5.49.0.tgz", + "integrity": "sha512-cPJue/4Si25FViIb74sHCLtM4nTSBXtLx1d3/QT6mirQ/c65bV8arBEebBJJizfq8W2YyMoPI/WWPFWitmNqnQ==", "dev": true, "requires": { "@types/json-schema": "^7.0.9", "@types/semver": "^7.3.12", - "@typescript-eslint/scope-manager": "5.41.0", - "@typescript-eslint/types": "5.41.0", - "@typescript-eslint/typescript-estree": "5.41.0", + "@typescript-eslint/scope-manager": "5.49.0", + "@typescript-eslint/types": "5.49.0", + "@typescript-eslint/typescript-estree": "5.49.0", "eslint-scope": "^5.1.1", "eslint-utils": "^3.0.0", "semver": "^7.3.7" + }, + "dependencies": { + "lru-cache": { + "version": "6.0.0", + "resolved": "https://registry.npmjs.org/lru-cache/-/lru-cache-6.0.0.tgz", + "integrity": "sha512-Jo6dJ04CmSjuznwJSS3pUeWmd/H0ffTlkXXgwZi+eq1UCmqQwCh+eLsYOYCwY991i2Fah4h1BEMCx4qThGbsiA==", + "dev": true, + "requires": { + "yallist": "^4.0.0" + } + }, + "semver": { + "version": "7.3.8", + "resolved": "https://registry.npmjs.org/semver/-/semver-7.3.8.tgz", + "integrity": "sha512-NB1ctGL5rlHrPJtFDVIVzTyQylMLu9N9VICA6HSFJo8MCGVTMW6gfpicwKmmK/dAjTOrqu5l63JJOpDSrAis3A==", + "dev": true, + "requires": { + "lru-cache": "^6.0.0" + } + }, + "yallist": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/yallist/-/yallist-4.0.0.tgz", + "integrity": "sha512-3wdGidZyq5PB084XLES5TpOSRA3wjXAlIWMhum2kRcv/41Sn2emQ0dycQW4uZXLejwKvg6EsvbdlVL+FYEct7A==", + "dev": true + } } }, "@typescript-eslint/visitor-keys": { - "version": "5.41.0", - "resolved": "https://registry.npmjs.org/@typescript-eslint/visitor-keys/-/visitor-keys-5.41.0.tgz", - "integrity": "sha512-vilqeHj267v8uzzakbm13HkPMl7cbYpKVjgFWZPIOHIJHZtinvypUhJ5xBXfWYg4eFKqztbMMpOgFpT9Gfx4fw==", + "version": "5.49.0", + "resolved": "https://registry.npmjs.org/@typescript-eslint/visitor-keys/-/visitor-keys-5.49.0.tgz", + "integrity": "sha512-v9jBMjpNWyn8B6k/Mjt6VbUS4J1GvUlR4x3Y+ibnP1z7y7V4n0WRz+50DY6+Myj0UaXVSuUlHohO+eZ8IJEnkg==", "dev": true, "requires": { - "@typescript-eslint/types": "5.41.0", + "@typescript-eslint/types": "5.49.0", "eslint-visitor-keys": "^3.3.0" } }, @@ -9311,6 +9720,12 @@ "string-width": "^4.1.0" }, "dependencies": { + "ansi-regex": { + "version": "5.0.1", + "resolved": "https://registry.npmjs.org/ansi-regex/-/ansi-regex-5.0.1.tgz", + "integrity": "sha512-quJQXlTSUGL2LH9SUXo8VwsY4soanhgo6LNSm84E1LBcE8s3O0wpdiRzyR9z/ZZJMlMWv37qOOb9pdJlMUEKFQ==", + "dev": true + }, "emoji-regex": { "version": "8.0.0", "resolved": "https://registry.npmjs.org/emoji-regex/-/emoji-regex-8.0.0.tgz", @@ -9327,6 +9742,15 @@ "is-fullwidth-code-point": "^3.0.0", "strip-ansi": "^6.0.1" } + }, + "strip-ansi": { + "version": "6.0.1", + "resolved": "https://registry.npmjs.org/strip-ansi/-/strip-ansi-6.0.1.tgz", + "integrity": "sha512-Y38VPSHcqkFrCpFnQ9vuSXmquuv5oXOKpGeT6aGrr3o3Gc9AlVa6JBfUSOCnbxGGZF+/0ooI7KrPuUSztUdU5A==", + "dev": true, + "requires": { + "ansi-regex": "^5.0.1" + } } } }, @@ -9337,41 +9761,30 @@ "dev": true }, "ansi-escapes": { - "version": "5.0.0", - "resolved": "https://registry.npmjs.org/ansi-escapes/-/ansi-escapes-5.0.0.tgz", - "integrity": "sha512-5GFMVX8HqE/TB+FuBJGuO5XG0WrsA6ptUqoODaT/n9mmUaZFkqnBueB4leqGBCmrUHnCnC4PCZTCd0E7QQ83bA==", + "version": "6.0.0", + "resolved": "https://registry.npmjs.org/ansi-escapes/-/ansi-escapes-6.0.0.tgz", + "integrity": "sha512-IG23inYII3dWlU2EyiAiGj6Bwal5GzsgPMwjYGvc1HPE2dgbj4ZB5ToWBKSquKw74nB3TIuOwaI6/jSULzfgrw==", "dev": true, "requires": { - "type-fest": "^1.0.2" - }, - "dependencies": { - "type-fest": { - "version": "1.4.0", - "resolved": "https://registry.npmjs.org/type-fest/-/type-fest-1.4.0.tgz", - "integrity": "sha512-yGSza74xk0UG8k+pLh5oeoYirvIiWo5t0/o3zHHAO2tRDiZcxWP7fywNlXhqb6/r6sWvwi+RsyQMWhVLe4BVuA==", - "dev": true - } + "type-fest": "^3.0.0" } }, "ansi-regex": { - "version": "5.0.1", - "resolved": "https://registry.npmjs.org/ansi-regex/-/ansi-regex-5.0.1.tgz", - "integrity": "sha512-quJQXlTSUGL2LH9SUXo8VwsY4soanhgo6LNSm84E1LBcE8s3O0wpdiRzyR9z/ZZJMlMWv37qOOb9pdJlMUEKFQ==", + "version": "6.0.1", + "resolved": "https://registry.npmjs.org/ansi-regex/-/ansi-regex-6.0.1.tgz", + "integrity": "sha512-n5M855fKb2SsfMIiFFoVrABHJC8QtHwVx+mHWP3QcEqBHYienj5dHSgjbxtC0WEZXYt4wcD6zrQElDPhFuZgfA==", "dev": true }, "ansi-styles": { - "version": "4.3.0", - "resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-4.3.0.tgz", - "integrity": "sha512-zbB9rCJAT1rbjiVDb2hqKFHNYLxgtk8NURxZ3IZwD3F6NtxbXZQCnnSi1Lkx+IDohdPlFp222wVALIheZJQSEg==", - "dev": true, - "requires": { - "color-convert": "^2.0.1" - } + "version": "6.2.1", + "resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-6.2.1.tgz", + "integrity": "sha512-bN798gFfQX+viw3R7yrGWRqnrN2oRkEkUjjl4JNn4E8GxxbjtG3FbrEIIY3l8/hrwUwIeCZvi4QuOTP4MErVug==", + "dev": true }, "anymatch": { - "version": "3.1.2", - "resolved": "https://registry.npmjs.org/anymatch/-/anymatch-3.1.2.tgz", - "integrity": "sha512-P43ePfOAIupkguHUycrc4qJ9kz8ZiuOUijaETwX7THt0Y/GNK7v0aa8rY816xWjZ7rJdA5XdMcpVFTKMq+RvWg==", + "version": "3.1.3", + "resolved": "https://registry.npmjs.org/anymatch/-/anymatch-3.1.3.tgz", + "integrity": "sha512-KMReFUr0B4t+D+OBkjR3KYqvocp2XaSzO55UcB6mgQMd3KbcE+mWTyvVV7D/zsdEbNnV6acZUutkiHQXvTr1Rw==", "dev": true, "requires": { "normalize-path": "^3.0.0", @@ -9406,10 +9819,13 @@ "dev": true }, "array-union": { - "version": "2.1.0", - "resolved": "https://registry.npmjs.org/array-union/-/array-union-2.1.0.tgz", - "integrity": "sha512-HGyxoOTYUyCM6stUe6EJgnd4EoewAI7zMdfqO+kGjnlZmBDz/cR5pf8r/cR4Wq60sL/p0IkcjUEEPwS3GFrIyw==", - "dev": true + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/array-union/-/array-union-1.0.2.tgz", + "integrity": "sha512-Dxr6QJj/RdU/hCaBjOfxW+q6lyuVE6JFWIrAUpuOOhoJJoQ99cUn3igRaHVB5P9WrgFVN0FfArM3x0cueOU8ng==", + "dev": true, + "requires": { + "array-uniq": "^1.0.1" + } }, "array-uniq": { "version": "1.0.3", @@ -9418,14 +9834,14 @@ "dev": true }, "array.prototype.map": { - "version": "1.0.4", - "resolved": "https://registry.npmjs.org/array.prototype.map/-/array.prototype.map-1.0.4.tgz", - "integrity": "sha512-Qds9QnX7A0qISY7JT5WuJO0NJPE9CMlC6JzHQfhpqAAQQzufVRoeH7EzUY5GcPTx72voG8LV/5eo+b8Qi8hmhA==", + "version": "1.0.5", + "resolved": "https://registry.npmjs.org/array.prototype.map/-/array.prototype.map-1.0.5.tgz", + "integrity": "sha512-gfaKntvwqYIuC7mLLyv2wzZIJqrRhn5PZ9EfFejSx6a78sV7iDsGpG9P+3oUPtm1Rerqm6nrKS4FYuTIvWfo3g==", "dev": true, "requires": { "call-bind": "^1.0.2", - "define-properties": "^1.1.3", - "es-abstract": "^1.19.0", + "define-properties": "^1.1.4", + "es-abstract": "^1.20.4", "es-array-method-boxes-properly": "^1.0.0", "is-string": "^1.0.7" } @@ -9440,13 +9856,10 @@ } }, "async": { - "version": "2.6.4", - "resolved": "https://registry.npmjs.org/async/-/async-2.6.4.tgz", - "integrity": "sha512-mzo5dfJYwAn29PeiJ0zvwTo04zj8HDJj0Mn8TD7sno7q12prdbnasKJHhkm2c1LgrhlJ0teaea8860oxi51mGA==", - "dev": true, - "requires": { - "lodash": "^4.17.14" - } + "version": "3.2.4", + "resolved": "https://registry.npmjs.org/async/-/async-3.2.4.tgz", + "integrity": "sha512-iAB+JbDEGXhyIUavoDl9WP/Jj106Kz9DEn1DPgYw5ruDn0e3Wgi3sKFm55sASdGBNOQB8F59d9qQ7deqrHA8wQ==", + "dev": true }, "async-retry": { "version": "1.3.3", @@ -9457,10 +9870,10 @@ "retry": "0.13.1" } }, - "asynckit": { - "version": "0.4.0", - "resolved": "https://registry.npmjs.org/asynckit/-/asynckit-0.4.0.tgz", - "integrity": "sha512-Oei9OH4tRh0YqU3GxhX79dM/mwVgvbZJaSNaRk+bshkj0S5cfHcgYakreBjrHwatXKbz+IoIdYLxrKim2MjW0Q==", + "available-typed-arrays": { + "version": "1.0.5", + "resolved": "https://registry.npmjs.org/available-typed-arrays/-/available-typed-arrays-1.0.5.tgz", + "integrity": "sha512-DMD0KiN46eipeziST1LPP/STfDU0sufISXmjSgvVsoU2tqxctQeASejWcfNtxYKqETM1UxQ8sp2OrSBWpHY6sw==", "dev": true }, "balanced-match": { @@ -9499,9 +9912,9 @@ } }, "boxen": { - "version": "7.0.0", - "resolved": "https://registry.npmjs.org/boxen/-/boxen-7.0.0.tgz", - "integrity": "sha512-j//dBVuyacJbvW+tvZ9HuH03fZ46QcaKvvhZickZqtB271DxJ7SNRSNxrV/dZX0085m7hISRZWbzWlJvx/rHSg==", + "version": "7.0.1", + "resolved": "https://registry.npmjs.org/boxen/-/boxen-7.0.1.tgz", + "integrity": "sha512-8k2eH6SRAK00NDl1iX5q17RJ8rfl53TajdYxE3ssMLehbg487dEVgsad4pIsZb/QqBgYWIl6JOauMTLGX2Kpkw==", "dev": true, "requires": { "ansi-align": "^3.0.1", @@ -9515,15 +9928,9 @@ }, "dependencies": { "camelcase": { - "version": "7.0.0", - "resolved": "https://registry.npmjs.org/camelcase/-/camelcase-7.0.0.tgz", - "integrity": "sha512-JToIvOmz6nhGsUhAYScbo2d6Py5wojjNfoxoc2mEVLUdJ70gJK2gnd+ABY1Tc3sVMyK7QDPtN0T/XdlCQWITyQ==", - "dev": true - }, - "chalk": { - "version": "5.1.2", - "resolved": "https://registry.npmjs.org/chalk/-/chalk-5.1.2.tgz", - "integrity": "sha512-E5CkT4jWURs1Vy5qGJye+XwCkNj7Od3Af7CP6SujMetSMkLs8Do2RWJK5yx1wamHV/op8Rz+9rltjaTQWDnEFQ==", + "version": "7.0.1", + "resolved": "https://registry.npmjs.org/camelcase/-/camelcase-7.0.1.tgz", + "integrity": "sha512-xlx1yCK2Oc1APsPXDL2LdlNP6+uu8OCDdhOBSVT279M/S+y75O30C2VuD8T2ogdePBBl7PfPF4504tnLgX3zfw==", "dev": true }, "type-fest": { @@ -9600,17 +10007,17 @@ "dev": true }, "cacheable-request": { - "version": "10.2.2", - "resolved": "https://registry.npmjs.org/cacheable-request/-/cacheable-request-10.2.2.tgz", - "integrity": "sha512-KxjQZM3UIo7/J6W4sLpwFvu1GB3Whv8NtZ8ZrUL284eiQjiXeeqWTdhixNrp/NLZ/JNuFBo6BD4ZaO8ZJ5BN8Q==", + "version": "10.2.5", + "resolved": "https://registry.npmjs.org/cacheable-request/-/cacheable-request-10.2.5.tgz", + "integrity": "sha512-5RwYYCfzjNPsyJxb/QpaM0bfzx+kw5/YpDhZPm9oMIDntHFQ9YXeyV47ZvzlTE0XrrrbyO2UITJH4GF9eRLdXQ==", "dev": true, "requires": { "@types/http-cache-semantics": "^4.0.1", "get-stream": "^6.0.1", "http-cache-semantics": "^4.1.0", - "keyv": "^4.5.0", + "keyv": "^4.5.2", "mimic-response": "^4.0.0", - "normalize-url": "^7.2.0", + "normalize-url": "^8.0.0", "responselike": "^3.0.0" } }, @@ -9649,20 +10056,16 @@ "dev": true }, "caniuse-lite": { - "version": "1.0.30001425", - "resolved": "https://registry.npmjs.org/caniuse-lite/-/caniuse-lite-1.0.30001425.tgz", - "integrity": "sha512-/pzFv0OmNG6W0ym80P3NtapU0QEiDS3VuYAZMGoLLqiC7f6FJFe1MjpQDREGApeenD9wloeytmVDj+JLXPC6qw==", + "version": "1.0.30001445", + "resolved": "https://registry.npmjs.org/caniuse-lite/-/caniuse-lite-1.0.30001445.tgz", + "integrity": "sha512-8sdQIdMztYmzfTMO6KfLny878Ln9c2M0fc7EH60IjlP4Dc4PiCy7K2Vl3ITmWgOyPgVQKa5x+UP/KqFsxj4mBg==", "dev": true }, "chalk": { - "version": "4.1.2", - "resolved": "https://registry.npmjs.org/chalk/-/chalk-4.1.2.tgz", - "integrity": "sha512-oKnbhFyRIXpUuez8iBMmyEa4nbj4IOQyuhc/wy9kY7/WVPcwIO9VA668Pu8RkO7+0G76SLROeyw9CpQ061i4mA==", - "dev": true, - "requires": { - "ansi-styles": "^4.1.0", - "supports-color": "^7.1.0" - } + "version": "5.1.2", + "resolved": "https://registry.npmjs.org/chalk/-/chalk-5.1.2.tgz", + "integrity": "sha512-E5CkT4jWURs1Vy5qGJye+XwCkNj7Od3Af7CP6SujMetSMkLs8Do2RWJK5yx1wamHV/op8Rz+9rltjaTQWDnEFQ==", + "dev": true }, "chardet": { "version": "0.7.0", @@ -9698,9 +10101,9 @@ } }, "ci-info": { - "version": "3.5.0", - "resolved": "https://registry.npmjs.org/ci-info/-/ci-info-3.5.0.tgz", - "integrity": "sha512-yH4RezKOGlOhxkmhbeNuC4eYZKAUsEaGtBuBzDDP1eFUKiccDWzBABxBfOx31IDwDIXMTxWuwAxUGModvkbuVw==", + "version": "3.7.1", + "resolved": "https://registry.npmjs.org/ci-info/-/ci-info-3.7.1.tgz", + "integrity": "sha512-4jYS4MOAaCIStSRwiuxc4B8MYhIe676yO1sYGzARnjXkWpmzZMMYxY6zu8WYWDhSuth5zhrQ1rhNSibyyvv4/w==", "dev": true }, "clean-stack": { @@ -9737,16 +10140,31 @@ "dev": true }, "cliui": { - "version": "8.0.1", - "resolved": "https://registry.npmjs.org/cliui/-/cliui-8.0.1.tgz", - "integrity": "sha512-BSeNnyus75C4//NQ9gQt1/csTXyo/8Sb+afLAkzAptFuMsod9HFokGNudZpi/oQV73hnVK+sR+5PVRMd+Dr7YQ==", + "version": "6.0.0", + "resolved": "https://registry.npmjs.org/cliui/-/cliui-6.0.0.tgz", + "integrity": "sha512-t6wbgtoCXvAzst7QgXxJYqPt0usEfbgQdftEPbLL/cvv6HPE5VgvqCuAIDR0NgU52ds6rFwqrgakNLrHEjCbrQ==", "dev": true, "requires": { "string-width": "^4.2.0", - "strip-ansi": "^6.0.1", - "wrap-ansi": "^7.0.0" + "strip-ansi": "^6.0.0", + "wrap-ansi": "^6.2.0" }, "dependencies": { + "ansi-regex": { + "version": "5.0.1", + "resolved": "https://registry.npmjs.org/ansi-regex/-/ansi-regex-5.0.1.tgz", + "integrity": "sha512-quJQXlTSUGL2LH9SUXo8VwsY4soanhgo6LNSm84E1LBcE8s3O0wpdiRzyR9z/ZZJMlMWv37qOOb9pdJlMUEKFQ==", + "dev": true + }, + "ansi-styles": { + "version": "4.3.0", + "resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-4.3.0.tgz", + "integrity": "sha512-zbB9rCJAT1rbjiVDb2hqKFHNYLxgtk8NURxZ3IZwD3F6NtxbXZQCnnSi1Lkx+IDohdPlFp222wVALIheZJQSEg==", + "dev": true, + "requires": { + "color-convert": "^2.0.1" + } + }, "emoji-regex": { "version": "8.0.0", "resolved": "https://registry.npmjs.org/emoji-regex/-/emoji-regex-8.0.0.tgz", @@ -9764,10 +10182,19 @@ "strip-ansi": "^6.0.1" } }, + "strip-ansi": { + "version": "6.0.1", + "resolved": "https://registry.npmjs.org/strip-ansi/-/strip-ansi-6.0.1.tgz", + "integrity": "sha512-Y38VPSHcqkFrCpFnQ9vuSXmquuv5oXOKpGeT6aGrr3o3Gc9AlVa6JBfUSOCnbxGGZF+/0ooI7KrPuUSztUdU5A==", + "dev": true, + "requires": { + "ansi-regex": "^5.0.1" + } + }, "wrap-ansi": { - "version": "7.0.0", - "resolved": "https://registry.npmjs.org/wrap-ansi/-/wrap-ansi-7.0.0.tgz", - "integrity": "sha512-YVGIj2kamLSTxw6NsZjoBxfSwsn0ycdesmc4p+Q21c5zPuZ1pl+NfxVdxPtdHvmNVOQ6XSYG4AUtyt/Fi7D16Q==", + "version": "6.2.0", + "resolved": "https://registry.npmjs.org/wrap-ansi/-/wrap-ansi-6.2.0.tgz", + "integrity": "sha512-r6lPcBGxZXlIcymEu7InxDMhdW0KDxpLgoFLcguasxCaJ/SOIZwINatK9KY/tf+ZrlywOKU0UDj3ATXUBfxJXA==", "dev": true, "requires": { "ansi-styles": "^4.0.0", @@ -9784,9 +10211,9 @@ "dev": true }, "cluster-key-slot": { - "version": "1.1.1", - "resolved": "https://registry.npmjs.org/cluster-key-slot/-/cluster-key-slot-1.1.1.tgz", - "integrity": "sha512-rwHwUfXL40Chm1r08yrhU3qpUvdVlgkKNeyeGPOxnW8/SyVDvgRaed/Uz54AqWNaTCAThlj6QAs3TZcKI0xDEw==" + "version": "1.1.2", + "resolved": "https://registry.npmjs.org/cluster-key-slot/-/cluster-key-slot-1.1.2.tgz", + "integrity": "sha512-RMr0FhtfXemyinomL4hrWcYJxmX6deFdCxpJzhDttxgO1+bcCnkk+9drydLVDmAMG7NE6aN/fl4F7ucU/90gAA==" }, "color-convert": { "version": "2.0.1", @@ -9803,15 +10230,6 @@ "integrity": "sha512-dOy+3AuW3a2wNbZHIuMZpTcgjGuLU/uBL/ubcZF9OXbDo8ff4O8yVp5Bf0efS8uEoYo5q4Fx7dY9OgQGXgAsQA==", "dev": true }, - "combined-stream": { - "version": "1.0.8", - "resolved": "https://registry.npmjs.org/combined-stream/-/combined-stream-1.0.8.tgz", - "integrity": "sha512-FQN4MRfuJeHf7cBbBMJFXhKSDq+2kAArBlmRBvcvFE5BB1HZKXtSFASDhdlz9zOYwxh8lDdnvmMOe/+5cdoEdg==", - "dev": true, - "requires": { - "delayed-stream": "~1.0.0" - } - }, "commander": { "version": "2.20.3", "resolved": "https://registry.npmjs.org/commander/-/commander-2.20.3.tgz", @@ -9874,16 +10292,15 @@ "dev": true }, "cosmiconfig": { - "version": "7.0.1", - "resolved": "https://registry.npmjs.org/cosmiconfig/-/cosmiconfig-7.0.1.tgz", - "integrity": "sha512-a1YWNUV2HwGimB7dU2s1wUMurNKjpx60HxBB6xUM8Re+2s1g1IIfJvFR0/iCF+XHdE0GMTKTuLR32UQff4TEyQ==", + "version": "8.0.0", + "resolved": "https://registry.npmjs.org/cosmiconfig/-/cosmiconfig-8.0.0.tgz", + "integrity": "sha512-da1EafcpH6b/TD8vDRaWV7xFINlHlF6zKsGwS1TsuVJTZRkquaS5HTMq7uq6h31619QjbsYl21gVDOm32KM1vQ==", "dev": true, "requires": { - "@types/parse-json": "^4.0.0", "import-fresh": "^3.2.1", + "js-yaml": "^4.1.0", "parse-json": "^5.0.0", - "path-type": "^4.0.0", - "yaml": "^1.10.0" + "path-type": "^4.0.0" } }, "create-require": { @@ -9921,9 +10338,9 @@ } }, "data-uri-to-buffer": { - "version": "4.0.0", - "resolved": "https://registry.npmjs.org/data-uri-to-buffer/-/data-uri-to-buffer-4.0.0.tgz", - "integrity": "sha512-Vr3mLBA8qWmcuschSLAOogKgQ/Jwxulv3RNE4FXnYWRGujzrRWQI4m12fQqRkwX06C0KanhLr4hK+GydchZsaA==", + "version": "4.0.1", + "resolved": "https://registry.npmjs.org/data-uri-to-buffer/-/data-uri-to-buffer-4.0.1.tgz", + "integrity": "sha512-0R9ikRb668HB7QDxT1vkpuUBtqc53YyAwMwGeUFKRojY/NWKvdZ+9UYtRfGmhqNbRkTSVpMbmyhXipFFv2cb/A==", "dev": true }, "debug": { @@ -10022,12 +10439,6 @@ "vm2": "^3.9.8" } }, - "delayed-stream": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/delayed-stream/-/delayed-stream-1.0.0.tgz", - "integrity": "sha512-ZySD7Nf91aLB0RxL4KGrKHBXl7Eds1DAmEdcoVawXnLD7SDhpNgtuII2aAkg7a7QS41jxPSZ17p4VdGnMHk3MQ==", - "dev": true - }, "depd": { "version": "2.0.0", "resolved": "https://registry.npmjs.org/depd/-/depd-2.0.0.tgz", @@ -10086,9 +10497,9 @@ "dev": true }, "email-addresses": { - "version": "3.1.0", - "resolved": "https://registry.npmjs.org/email-addresses/-/email-addresses-3.1.0.tgz", - "integrity": "sha512-k0/r7GrWVL32kZlGwfPNgB2Y/mMXVTq/decgLczm/j34whdaspNrZO8CnXPf1laaHxI6ptUlsnAxN+UAPw+fzg==", + "version": "5.0.0", + "resolved": "https://registry.npmjs.org/email-addresses/-/email-addresses-5.0.0.tgz", + "integrity": "sha512-4OIPYlA6JXqtVn8zpHpGiI7vE6EQOAg16aGnDMIAlZVinnoZ8208tW1hAbjWydgN/4PLTT9q+O1K6AH/vALJGw==", "dev": true }, "emoji-regex": { @@ -10107,35 +10518,44 @@ } }, "es-abstract": { - "version": "1.20.4", - "resolved": "https://registry.npmjs.org/es-abstract/-/es-abstract-1.20.4.tgz", - "integrity": "sha512-0UtvRN79eMe2L+UNEF1BwRe364sj/DXhQ/k5FmivgoSdpM90b8Jc0mDzKMGo7QS0BVbOP/bTwBKNnDc9rNzaPA==", + "version": "1.21.1", + "resolved": "https://registry.npmjs.org/es-abstract/-/es-abstract-1.21.1.tgz", + "integrity": "sha512-QudMsPOz86xYz/1dG1OuGBKOELjCh99IIWHLzy5znUB6j8xG2yMA7bfTV86VSqKF+Y/H08vQPR+9jyXpuC6hfg==", "dev": true, "requires": { + "available-typed-arrays": "^1.0.5", "call-bind": "^1.0.2", + "es-set-tostringtag": "^2.0.1", "es-to-primitive": "^1.2.1", "function-bind": "^1.1.1", "function.prototype.name": "^1.1.5", "get-intrinsic": "^1.1.3", "get-symbol-description": "^1.0.0", + "globalthis": "^1.0.3", + "gopd": "^1.0.1", "has": "^1.0.3", "has-property-descriptors": "^1.0.0", + "has-proto": "^1.0.1", "has-symbols": "^1.0.3", - "internal-slot": "^1.0.3", + "internal-slot": "^1.0.4", + "is-array-buffer": "^3.0.1", "is-callable": "^1.2.7", "is-negative-zero": "^2.0.2", "is-regex": "^1.1.4", "is-shared-array-buffer": "^1.0.2", "is-string": "^1.0.7", + "is-typed-array": "^1.1.10", "is-weakref": "^1.0.2", "object-inspect": "^1.12.2", "object-keys": "^1.1.1", "object.assign": "^4.1.4", "regexp.prototype.flags": "^1.4.3", "safe-regex-test": "^1.0.0", - "string.prototype.trimend": "^1.0.5", - "string.prototype.trimstart": "^1.0.5", - "unbox-primitive": "^1.0.2" + "string.prototype.trimend": "^1.0.6", + "string.prototype.trimstart": "^1.0.6", + "typed-array-length": "^1.0.4", + "unbox-primitive": "^1.0.2", + "which-typed-array": "^1.1.9" } }, "es-array-method-boxes-properly": { @@ -10145,19 +10565,31 @@ "dev": true }, "es-get-iterator": { - "version": "1.1.2", - "resolved": "https://registry.npmjs.org/es-get-iterator/-/es-get-iterator-1.1.2.tgz", - "integrity": "sha512-+DTO8GYwbMCwbywjimwZMHp8AuYXOS2JZFWoi2AlPOS3ebnII9w/NLpNZtA7A0YLaVDw+O7KFCeoIV7OPvM7hQ==", + "version": "1.1.3", + "resolved": "https://registry.npmjs.org/es-get-iterator/-/es-get-iterator-1.1.3.tgz", + "integrity": "sha512-sPZmqHBe6JIiTfN5q2pEi//TwxmAFHwj/XEuYjTuse78i8KxaqMTTzxPoFKuzRpDpTJ+0NAbpfenkmH2rePtuw==", "dev": true, "requires": { "call-bind": "^1.0.2", - "get-intrinsic": "^1.1.0", - "has-symbols": "^1.0.1", - "is-arguments": "^1.1.0", + "get-intrinsic": "^1.1.3", + "has-symbols": "^1.0.3", + "is-arguments": "^1.1.1", "is-map": "^2.0.2", "is-set": "^2.0.2", - "is-string": "^1.0.5", - "isarray": "^2.0.5" + "is-string": "^1.0.7", + "isarray": "^2.0.5", + "stop-iteration-iterator": "^1.0.0" + } + }, + "es-set-tostringtag": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/es-set-tostringtag/-/es-set-tostringtag-2.0.1.tgz", + "integrity": "sha512-g3OMbtlwY3QewlqAiMLI47KywjWZoEytKr8pf6iTC8uJq5bIAH52Z9pnQ8pVL6whrCto53JZDuUIsifGeLorTg==", + "dev": true, + "requires": { + "get-intrinsic": "^1.1.3", + "has": "^1.0.3", + "has-tostringtag": "^1.0.0" } }, "es-to-primitive": { @@ -10190,9 +10622,9 @@ "dev": true }, "escape-string-regexp": { - "version": "4.0.0", - "resolved": "https://registry.npmjs.org/escape-string-regexp/-/escape-string-regexp-4.0.0.tgz", - "integrity": "sha512-TtpcNJ3XAzx3Gq8sWRzJaVajRs0uVxA2YAkdb1jm2YkPz4G6egUFAyA3n5vtEIZefPk5Wa4UXbKuS5fKkJWdgA==", + "version": "5.0.0", + "resolved": "https://registry.npmjs.org/escape-string-regexp/-/escape-string-regexp-5.0.0.tgz", + "integrity": "sha512-/veY75JbMK4j1yjvuUxuVsiS/hr/4iHs9FTT6cgTexxdE0Ly/glccBAkloH/DofkjRbZU3bnoj38mOmhkZ0lHw==", "dev": true }, "escodegen": { @@ -10250,13 +10682,13 @@ } }, "eslint": { - "version": "8.26.0", - "resolved": "https://registry.npmjs.org/eslint/-/eslint-8.26.0.tgz", - "integrity": "sha512-kzJkpaw1Bfwheq4VXUezFriD1GxszX6dUekM7Z3aC2o4hju+tsR/XyTC3RcoSD7jmy9VkPU3+N6YjVU2e96Oyg==", + "version": "8.32.0", + "resolved": "https://registry.npmjs.org/eslint/-/eslint-8.32.0.tgz", + "integrity": "sha512-nETVXpnthqKPFyuY2FNjz/bEd6nbosRgKbkgS/y1C7LJop96gYHWpiguLecMHQ2XCPxn77DS0P+68WzG6vkZSQ==", "dev": true, "requires": { - "@eslint/eslintrc": "^1.3.3", - "@humanwhocodes/config-array": "^0.11.6", + "@eslint/eslintrc": "^1.4.1", + "@humanwhocodes/config-array": "^0.11.8", "@humanwhocodes/module-importer": "^1.0.1", "@nodelib/fs.walk": "^1.2.8", "ajv": "^6.10.0", @@ -10275,7 +10707,7 @@ "file-entry-cache": "^6.0.1", "find-up": "^5.0.0", "glob-parent": "^6.0.2", - "globals": "^13.15.0", + "globals": "^13.19.0", "grapheme-splitter": "^1.0.4", "ignore": "^5.2.0", "import-fresh": "^3.0.0", @@ -10296,6 +10728,37 @@ "text-table": "^0.2.0" }, "dependencies": { + "ansi-regex": { + "version": "5.0.1", + "resolved": "https://registry.npmjs.org/ansi-regex/-/ansi-regex-5.0.1.tgz", + "integrity": "sha512-quJQXlTSUGL2LH9SUXo8VwsY4soanhgo6LNSm84E1LBcE8s3O0wpdiRzyR9z/ZZJMlMWv37qOOb9pdJlMUEKFQ==", + "dev": true + }, + "ansi-styles": { + "version": "4.3.0", + "resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-4.3.0.tgz", + "integrity": "sha512-zbB9rCJAT1rbjiVDb2hqKFHNYLxgtk8NURxZ3IZwD3F6NtxbXZQCnnSi1Lkx+IDohdPlFp222wVALIheZJQSEg==", + "dev": true, + "requires": { + "color-convert": "^2.0.1" + } + }, + "chalk": { + "version": "4.1.2", + "resolved": "https://registry.npmjs.org/chalk/-/chalk-4.1.2.tgz", + "integrity": "sha512-oKnbhFyRIXpUuez8iBMmyEa4nbj4IOQyuhc/wy9kY7/WVPcwIO9VA668Pu8RkO7+0G76SLROeyw9CpQ061i4mA==", + "dev": true, + "requires": { + "ansi-styles": "^4.1.0", + "supports-color": "^7.1.0" + } + }, + "escape-string-regexp": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/escape-string-regexp/-/escape-string-regexp-4.0.0.tgz", + "integrity": "sha512-TtpcNJ3XAzx3Gq8sWRzJaVajRs0uVxA2YAkdb1jm2YkPz4G6egUFAyA3n5vtEIZefPk5Wa4UXbKuS5fKkJWdgA==", + "dev": true + }, "eslint-scope": { "version": "7.1.1", "resolved": "https://registry.npmjs.org/eslint-scope/-/eslint-scope-7.1.1.tgz", @@ -10311,6 +10774,52 @@ "resolved": "https://registry.npmjs.org/estraverse/-/estraverse-5.3.0.tgz", "integrity": "sha512-MMdARuVEQziNTeJD8DgMqmhwR11BRQ/cBP+pLtYdSTnf3MIO8fFeiINEbX36ZdNlfU/7A9f3gUw49B3oQsvwBA==", "dev": true + }, + "find-up": { + "version": "5.0.0", + "resolved": "https://registry.npmjs.org/find-up/-/find-up-5.0.0.tgz", + "integrity": "sha512-78/PXT1wlLLDgTzDs7sjq9hzz0vXD+zn+7wypEe4fXQxCmdmqfGsEPQxmiCSQI3ajFV91bVSsvNtrJRiW6nGng==", + "dev": true, + "requires": { + "locate-path": "^6.0.0", + "path-exists": "^4.0.0" + } + }, + "locate-path": { + "version": "6.0.0", + "resolved": "https://registry.npmjs.org/locate-path/-/locate-path-6.0.0.tgz", + "integrity": "sha512-iPZK6eYjbxRu3uB4/WZ3EsEIMJFMqAoopl3R+zuq0UjcAm/MO6KCweDgPfP3elTztoKP3KtnVHxTn2NHBSDVUw==", + "dev": true, + "requires": { + "p-locate": "^5.0.0" + } + }, + "p-limit": { + "version": "3.1.0", + "resolved": "https://registry.npmjs.org/p-limit/-/p-limit-3.1.0.tgz", + "integrity": "sha512-TYOanM3wGwNGsZN2cVTYPArw454xnXj5qmWF1bEoAc4+cU/ol7GVh7odevjp1FNHduHc3KZMcFduxU5Xc6uJRQ==", + "dev": true, + "requires": { + "yocto-queue": "^0.1.0" + } + }, + "p-locate": { + "version": "5.0.0", + "resolved": "https://registry.npmjs.org/p-locate/-/p-locate-5.0.0.tgz", + "integrity": "sha512-LaNjtRWUBY++zB5nE/NwcaoMylSPk+S+ZHNB1TzdbMJMny6dynpAGt7X/tl/QYq3TIeE6nxHppbo2LGymrG5Pw==", + "dev": true, + "requires": { + "p-limit": "^3.0.2" + } + }, + "strip-ansi": { + "version": "6.0.1", + "resolved": "https://registry.npmjs.org/strip-ansi/-/strip-ansi-6.0.1.tgz", + "integrity": "sha512-Y38VPSHcqkFrCpFnQ9vuSXmquuv5oXOKpGeT6aGrr3o3Gc9AlVa6JBfUSOCnbxGGZF+/0ooI7KrPuUSztUdU5A==", + "dev": true, + "requires": { + "ansi-regex": "^5.0.1" + } } } }, @@ -10348,9 +10857,9 @@ "dev": true }, "espree": { - "version": "9.4.0", - "resolved": "https://registry.npmjs.org/espree/-/espree-9.4.0.tgz", - "integrity": "sha512-DQmnRpLj7f6TgN/NYb0MTzJXL+vJF9h3pHy4JhCIs3zwcgez8xmGg3sXHcEO97BrmO2OSvCwMdfdlyl+E9KjOw==", + "version": "9.4.1", + "resolved": "https://registry.npmjs.org/espree/-/espree-9.4.1.tgz", + "integrity": "sha512-XwctdmTO6SIvCzd9810yyNzIrOrqNYV9Koizx4C/mRhf9uq0o4yHoCEU/670pOxOL/MSraektvSAji79kX90Vg==", "dev": true, "requires": { "acorn": "^8.8.0", @@ -10425,14 +10934,6 @@ "onetime": "^6.0.0", "signal-exit": "^3.0.7", "strip-final-newline": "^3.0.0" - }, - "dependencies": { - "is-stream": { - "version": "3.0.0", - "resolved": "https://registry.npmjs.org/is-stream/-/is-stream-3.0.0.tgz", - "integrity": "sha512-LnQR4bZ9IADDRSkvpqMGvt/tEJWclzklNgSw48V5EAaAeDd6qGvN8ei6k5p0tvxSR171VmGyHuTiAOfxAbr8kA==", - "dev": true - } } }, "external-editor": { @@ -10489,9 +10990,9 @@ "dev": true }, "fastq": { - "version": "1.13.0", - "resolved": "https://registry.npmjs.org/fastq/-/fastq-1.13.0.tgz", - "integrity": "sha512-YpkpUnK8od0o1hmeSc7UUs/eB/vIPWJYjKck2QKIzAf71Vm1AAQ3EbuZB3g2JIy+pg+ERD0vqI79KyZiB2e2Nw==", + "version": "1.15.0", + "resolved": "https://registry.npmjs.org/fastq/-/fastq-1.15.0.tgz", + "integrity": "sha512-wBrocU2LCXXa+lWBt8RoIRD89Fi8OdABODa/kEnyeyjS5aZO5/GNvI5sEINADqP/h8M29UHTHUb53sUu5Ihqdw==", "dev": true, "requires": { "reusify": "^1.0.4" @@ -10515,20 +11016,6 @@ "requires": { "escape-string-regexp": "^5.0.0", "is-unicode-supported": "^1.2.0" - }, - "dependencies": { - "escape-string-regexp": { - "version": "5.0.0", - "resolved": "https://registry.npmjs.org/escape-string-regexp/-/escape-string-regexp-5.0.0.tgz", - "integrity": "sha512-/veY75JbMK4j1yjvuUxuVsiS/hr/4iHs9FTT6cgTexxdE0Ly/glccBAkloH/DofkjRbZU3bnoj38mOmhkZ0lHw==", - "dev": true - }, - "is-unicode-supported": { - "version": "1.3.0", - "resolved": "https://registry.npmjs.org/is-unicode-supported/-/is-unicode-supported-1.3.0.tgz", - "integrity": "sha512-43r2mRvz+8JRIKnWJ+3j8JtjRKZ6GmjzfaE/qiBJnikNnYv/6bagRJ1kUhNk8R5EX/GkobD+r+sfxCPJsiKBLQ==", - "dev": true - } } }, "file-entry-cache": { @@ -10584,12 +11071,12 @@ } }, "find-up": { - "version": "5.0.0", - "resolved": "https://registry.npmjs.org/find-up/-/find-up-5.0.0.tgz", - "integrity": "sha512-78/PXT1wlLLDgTzDs7sjq9hzz0vXD+zn+7wypEe4fXQxCmdmqfGsEPQxmiCSQI3ajFV91bVSsvNtrJRiW6nGng==", + "version": "4.1.0", + "resolved": "https://registry.npmjs.org/find-up/-/find-up-4.1.0.tgz", + "integrity": "sha512-PpOwAdQ/YlXQ2vj8a3h8IipDuYRi3wceVQQGYWxNINccq40Anw7BlsEXCMbt1Zt+OLA6Fq9suIpIWD0OsnISlw==", "dev": true, "requires": { - "locate-path": "^6.0.0", + "locate-path": "^5.0.0", "path-exists": "^4.0.0" } }, @@ -10615,6 +11102,15 @@ "integrity": "sha512-5nqDSxl8nn5BSNxyR3n4I6eDmbolI6WT+QqR547RwxQapgjQBmtktdP+HTBb/a/zLsbzERTONyUB5pefh5TtjQ==", "dev": true }, + "for-each": { + "version": "0.3.3", + "resolved": "https://registry.npmjs.org/for-each/-/for-each-0.3.3.tgz", + "integrity": "sha512-jqYfLp7mo9vIyQf8ykW2v7A+2N4QjeCeI5+Dz9XraiO1ign81wjiH7Fb9vSOWvQfNtmSa4H2RoQTrrXivdUZmw==", + "dev": true, + "requires": { + "is-callable": "^1.1.3" + } + }, "foreground-child": { "version": "2.0.0", "resolved": "https://registry.npmjs.org/foreground-child/-/foreground-child-2.0.0.tgz", @@ -10625,21 +11121,10 @@ "signal-exit": "^3.0.2" } }, - "form-data": { - "version": "4.0.0", - "resolved": "https://registry.npmjs.org/form-data/-/form-data-4.0.0.tgz", - "integrity": "sha512-ETEklSGi5t0QMZuiXoA/Q6vcnxcLQP5vdugSpuAyi6SVGi2clPPp+xgEhuMaHC+zGgn31Kd235W35f7Hykkaww==", - "dev": true, - "requires": { - "asynckit": "^0.4.0", - "combined-stream": "^1.0.8", - "mime-types": "^2.1.12" - } - }, "form-data-encoder": { - "version": "2.1.3", - "resolved": "https://registry.npmjs.org/form-data-encoder/-/form-data-encoder-2.1.3.tgz", - "integrity": "sha512-KqU0nnPMgIJcCOFTNJFEA8epcseEaoox4XZffTgy8jlI6pL/5EFyR54NRG7CnCJN0biY7q52DO3MH6/sJ/TKlQ==", + "version": "2.1.4", + "resolved": "https://registry.npmjs.org/form-data-encoder/-/form-data-encoder-2.1.4.tgz", + "integrity": "sha512-yDYSgNMraqvnxiEXO4hi88+YZxaHC6QKzb5N84iRCTDeRO7ZALpir/lVmf/uXUhnwUr2O4HU8s/n6x+yNjQkHw==", "dev": true }, "formdata-polyfill": { @@ -10814,42 +11299,18 @@ } }, "gh-pages": { - "version": "4.0.0", - "resolved": "https://registry.npmjs.org/gh-pages/-/gh-pages-4.0.0.tgz", - "integrity": "sha512-p8S0T3aGJc68MtwOcZusul5qPSNZCalap3NWbhRUZYu1YOdp+EjZ+4kPmRM8h3NNRdqw00yuevRjlkuSzCn7iQ==", + "version": "5.0.0", + "resolved": "https://registry.npmjs.org/gh-pages/-/gh-pages-5.0.0.tgz", + "integrity": "sha512-Nqp1SjkPIB94Xw/3yYNTUL+G2dxlhjvv1zeN/4kMC1jfViTEqhtVz/Ba1zSXHuvXCN9ADNS1dN4r5/J/nZWEQQ==", "dev": true, "requires": { - "async": "^2.6.1", + "async": "^3.2.4", "commander": "^2.18.0", - "email-addresses": "^3.0.1", + "email-addresses": "^5.0.0", "filenamify": "^4.3.0", "find-cache-dir": "^3.3.1", "fs-extra": "^8.1.0", "globby": "^6.1.0" - }, - "dependencies": { - "array-union": { - "version": "1.0.2", - "resolved": "https://registry.npmjs.org/array-union/-/array-union-1.0.2.tgz", - "integrity": "sha512-Dxr6QJj/RdU/hCaBjOfxW+q6lyuVE6JFWIrAUpuOOhoJJoQ99cUn3igRaHVB5P9WrgFVN0FfArM3x0cueOU8ng==", - "dev": true, - "requires": { - "array-uniq": "^1.0.1" - } - }, - "globby": { - "version": "6.1.0", - "resolved": "https://registry.npmjs.org/globby/-/globby-6.1.0.tgz", - "integrity": "sha512-KVbFv2TQtbzCoxAnfD6JcHZTYCzyliEaaeM/gH8qQdkKr5s0OP9scEgvdcngyk7AVdY6YVW/TJHd+lQ/Df3Daw==", - "dev": true, - "requires": { - "array-union": "^1.0.1", - "glob": "^7.0.3", - "object-assign": "^4.0.1", - "pify": "^2.0.0", - "pinkie-promise": "^2.0.0" - } - } } }, "git-up": { @@ -10872,15 +11333,15 @@ } }, "glob": { - "version": "7.2.0", - "resolved": "https://registry.npmjs.org/glob/-/glob-7.2.0.tgz", - "integrity": "sha512-lmLf6gtyrPq8tTjSmrO94wBeQbFR3HbLHbuyD69wuyQkImp2hWqMGB47OX65FBkPffO641IP9jWa1z4ivqG26Q==", + "version": "7.2.3", + "resolved": "https://registry.npmjs.org/glob/-/glob-7.2.3.tgz", + "integrity": "sha512-nFR0zLpU2YCaRxwoCJvL6UvCH2JFyFVIvwTLsIf21AuHlMskA1hhTdk+LlYJtOlYt9v6dvszD2BGRqBL+iQK9Q==", "dev": true, "requires": { "fs.realpath": "^1.0.0", "inflight": "^1.0.4", "inherits": "2", - "minimatch": "^3.0.4", + "minimatch": "^3.1.1", "once": "^1.3.0", "path-is-absolute": "^1.0.0" } @@ -10895,41 +11356,66 @@ } }, "global-dirs": { - "version": "3.0.0", - "resolved": "https://registry.npmjs.org/global-dirs/-/global-dirs-3.0.0.tgz", - "integrity": "sha512-v8ho2DS5RiCjftj1nD9NmnfaOzTdud7RRnVd9kFNOjqZbISlx5DQ+OrTkywgd0dIt7oFCvKetZSHoHcP3sDdiA==", + "version": "3.0.1", + "resolved": "https://registry.npmjs.org/global-dirs/-/global-dirs-3.0.1.tgz", + "integrity": "sha512-NBcGGFbBA9s1VzD41QXDG+3++t9Mn5t1FpLdhESY6oKY4gYTFpX4wO3sqGUa0Srjtbfj3szX0RnemmrVRUdULA==", "dev": true, "requires": { "ini": "2.0.0" } }, "globals": { - "version": "13.17.0", - "resolved": "https://registry.npmjs.org/globals/-/globals-13.17.0.tgz", - "integrity": "sha512-1C+6nQRb1GwGMKm2dH/E7enFAMxGTmGI7/dEdhy/DNelv85w9B72t3uc5frtMNXIbzrarJJ/lTCjcaZwbLJmyw==", + "version": "13.19.0", + "resolved": "https://registry.npmjs.org/globals/-/globals-13.19.0.tgz", + "integrity": "sha512-dkQ957uSRWHw7CFXLUtUHQI3g3aWApYhfNR2O6jn/907riyTYKVBmxYVROkBcY614FSSeSJh7Xm7SrUWCxvJMQ==", "dev": true, "requires": { "type-fest": "^0.20.2" + }, + "dependencies": { + "type-fest": { + "version": "0.20.2", + "resolved": "https://registry.npmjs.org/type-fest/-/type-fest-0.20.2.tgz", + "integrity": "sha512-Ne+eE4r0/iWnpAxD852z3A+N0Bt5RN//NjJwRd2VFHEmrywxf5vsZlh4R6lixl6B+wz/8d+maTSAkN1FIkI3LQ==", + "dev": true + } + } + }, + "globalthis": { + "version": "1.0.3", + "resolved": "https://registry.npmjs.org/globalthis/-/globalthis-1.0.3.tgz", + "integrity": "sha512-sFdI5LyBiNTHjRd7cGPWapiHWMOXKyuBNX/cWJ3NfzrZQVa8GI/8cofCl74AOVqq9W5kNmguTIzJ/1s2gyI9wA==", + "dev": true, + "requires": { + "define-properties": "^1.1.3" } }, "globby": { - "version": "11.1.0", - "resolved": "https://registry.npmjs.org/globby/-/globby-11.1.0.tgz", - "integrity": "sha512-jhIXaOzy1sb8IyocaruWSn1TjmnBVs8Ayhcy83rmxNJ8q2uWKCAj3CnJY+KpGSXCueAPc0i05kVvVKtP1t9S3g==", + "version": "6.1.0", + "resolved": "https://registry.npmjs.org/globby/-/globby-6.1.0.tgz", + "integrity": "sha512-KVbFv2TQtbzCoxAnfD6JcHZTYCzyliEaaeM/gH8qQdkKr5s0OP9scEgvdcngyk7AVdY6YVW/TJHd+lQ/Df3Daw==", "dev": true, "requires": { - "array-union": "^2.1.0", - "dir-glob": "^3.0.1", - "fast-glob": "^3.2.9", - "ignore": "^5.2.0", - "merge2": "^1.4.1", - "slash": "^3.0.0" + "array-union": "^1.0.1", + "glob": "^7.0.3", + "object-assign": "^4.0.1", + "pify": "^2.0.0", + "pinkie-promise": "^2.0.0" + } + }, + "gopd": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/gopd/-/gopd-1.0.1.tgz", + "integrity": "sha512-d65bNlIadxvpb/A2abVdlqKqV563juRnZ1Wtk6s1sIR8uNsXR70xqIzVqxVf1eTqDunwT2MkczEeaezCKTZhwA==", + "dev": true, + "requires": { + "get-intrinsic": "^1.1.3" } }, "got": { - "version": "12.5.1", - "resolved": "https://registry.npmjs.org/got/-/got-12.5.1.tgz", - "integrity": "sha512-sD16AK8cCyUoPtKr/NMvLTFFa+T3i3S+zoiuvhq0HP2YiqBZA9AtlBjAdsQBsLBK7slPuvmfE0OxhGi7N5dD4w==", + "version": "12.5.3", + "resolved": "https://registry.npmjs.org/got/-/got-12.5.3.tgz", + "integrity": "sha512-8wKnb9MGU8IPGRIo+/ukTy9XLJBwDiCpIf5TVzQ9Cpol50eMTpBq2GAuDsuDIz7hTYmZgMgC1e9ydr6kSDWs3w==", "dev": true, "requires": { "@sindresorhus/is": "^5.2.0", @@ -10987,6 +11473,12 @@ "get-intrinsic": "^1.1.1" } }, + "has-proto": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/has-proto/-/has-proto-1.0.1.tgz", + "integrity": "sha512-7qE+iP+O+bgF9clE5+UoBFzE65mlBiVj3tKCrlNQ0Ogwm0BjpT/gK4SlLYDMybDh5I3TCTKnPPa0oMG7JDYrhg==", + "dev": true + }, "has-symbols": { "version": "1.0.3", "resolved": "https://registry.npmjs.org/has-symbols/-/has-symbols-1.0.3.tgz", @@ -11018,6 +11510,12 @@ "type-fest": "^0.8.0" }, "dependencies": { + "is-stream": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/is-stream/-/is-stream-2.0.1.tgz", + "integrity": "sha512-hFoiJiTl63nn+kstHGBtewWSKnQLpyb155KHheA1l39uvtO9nWIop1p3udqPcUd/xbF1VLMO4n7OI6p7RbngDg==", + "dev": true + }, "type-fest": { "version": "0.8.1", "resolved": "https://registry.npmjs.org/type-fest/-/type-fest-0.8.1.tgz", @@ -11069,9 +11567,9 @@ } }, "http2-wrapper": { - "version": "2.1.11", - "resolved": "https://registry.npmjs.org/http2-wrapper/-/http2-wrapper-2.1.11.tgz", - "integrity": "sha512-aNAk5JzLturWEUiuhAN73Jcbq96R7rTitAoXV54FYMatvihnpD2+6PUgU4ce3D/m5VDbw+F5CsyKSF176ptitQ==", + "version": "2.2.0", + "resolved": "https://registry.npmjs.org/http2-wrapper/-/http2-wrapper-2.2.0.tgz", + "integrity": "sha512-kZB0wxMo0sh1PehyjJUWRFEd99KC5TLjZ2cULC4f9iqJBAmKQQXEICjxl5iPJRwP40dpeHFqqhm7tYCvODpqpQ==", "dev": true, "requires": { "quick-lru": "^5.1.1", @@ -11110,9 +11608,9 @@ "dev": true }, "ignore": { - "version": "5.2.0", - "resolved": "https://registry.npmjs.org/ignore/-/ignore-5.2.0.tgz", - "integrity": "sha512-CmxgYGiEPCLhfLnpPp1MoRmifwEIOgjcHXxOBjv7mY96c+eWScsOP9c112ZyLdWHi0FxHjI+4uVhKYp/gcdRmQ==", + "version": "5.2.4", + "resolved": "https://registry.npmjs.org/ignore/-/ignore-5.2.4.tgz", + "integrity": "sha512-MAb38BcSbH0eHNBxn7ql2NH/kX33OkB3lZ1BNdh7ENeRChHTYsTvWrMubiIAMNS2llXEEgZ1MUOBtXChP3kaFQ==", "dev": true }, "import-fresh": { @@ -11166,13 +11664,13 @@ "dev": true }, "inquirer": { - "version": "9.1.2", - "resolved": "https://registry.npmjs.org/inquirer/-/inquirer-9.1.2.tgz", - "integrity": "sha512-Hj2Ml1WpxKJU2npP2Rj0OURGkHV+GtNW2CwFdHDiXlqUBAUrWTcZHxCkFywX/XHzOS7wrG/kExgJFbUkVgyHzg==", + "version": "9.1.4", + "resolved": "https://registry.npmjs.org/inquirer/-/inquirer-9.1.4.tgz", + "integrity": "sha512-9hiJxE5gkK/cM2d1mTEnuurGTAoHebbkX0BYl3h7iEg7FYfuNIom+nDfBCSWtvSnoSrWCeBxqqBZu26xdlJlXA==", "dev": true, "requires": { - "ansi-escapes": "^5.0.0", - "chalk": "^5.0.1", + "ansi-escapes": "^6.0.0", + "chalk": "^5.1.2", "cli-cursor": "^4.0.0", "cli-width": "^4.0.0", "external-editor": "^3.0.3", @@ -11181,43 +11679,20 @@ "mute-stream": "0.0.8", "ora": "^6.1.2", "run-async": "^2.4.0", - "rxjs": "^7.5.6", + "rxjs": "^7.5.7", "string-width": "^5.1.2", "strip-ansi": "^7.0.1", - "through": "^2.3.6", - "wrap-ansi": "^8.0.1" - }, - "dependencies": { - "ansi-regex": { - "version": "6.0.1", - "resolved": "https://registry.npmjs.org/ansi-regex/-/ansi-regex-6.0.1.tgz", - "integrity": "sha512-n5M855fKb2SsfMIiFFoVrABHJC8QtHwVx+mHWP3QcEqBHYienj5dHSgjbxtC0WEZXYt4wcD6zrQElDPhFuZgfA==", - "dev": true - }, - "chalk": { - "version": "5.1.2", - "resolved": "https://registry.npmjs.org/chalk/-/chalk-5.1.2.tgz", - "integrity": "sha512-E5CkT4jWURs1Vy5qGJye+XwCkNj7Od3Af7CP6SujMetSMkLs8Do2RWJK5yx1wamHV/op8Rz+9rltjaTQWDnEFQ==", - "dev": true - }, - "strip-ansi": { - "version": "7.0.1", - "resolved": "https://registry.npmjs.org/strip-ansi/-/strip-ansi-7.0.1.tgz", - "integrity": "sha512-cXNxvT8dFNRVfhVME3JAe98mkXDYN2O1l7jmcwMnOslDeESg1rF/OZMtK0nRAhiari1unG5cD4jG3rapUAkLbw==", - "dev": true, - "requires": { - "ansi-regex": "^6.0.1" - } - } + "through": "^2.3.6", + "wrap-ansi": "^8.0.1" } }, "internal-slot": { - "version": "1.0.3", - "resolved": "https://registry.npmjs.org/internal-slot/-/internal-slot-1.0.3.tgz", - "integrity": "sha512-O0DB1JC/sPyZl7cIo78n5dR7eUSwwpYPiXRhTzNxZVAMUuB8vlnRFyLxdrVToks6XPLVnFfbzaVd5WLjhgg+vA==", + "version": "1.0.4", + "resolved": "https://registry.npmjs.org/internal-slot/-/internal-slot-1.0.4.tgz", + "integrity": "sha512-tA8URYccNzMo94s5MQZgH8NB/XTa6HsOo0MLfXTKKEnHVVdegzaQoFZ7Jp44bdvLvY2waT5dc+j5ICEswhi7UQ==", "dev": true, "requires": { - "get-intrinsic": "^1.1.0", + "get-intrinsic": "^1.1.3", "has": "^1.0.3", "side-channel": "^1.0.4" } @@ -11244,6 +11719,17 @@ "has-tostringtag": "^1.0.0" } }, + "is-array-buffer": { + "version": "3.0.1", + "resolved": "https://registry.npmjs.org/is-array-buffer/-/is-array-buffer-3.0.1.tgz", + "integrity": "sha512-ASfLknmY8Xa2XtB4wmbz13Wu202baeA18cJBCeCy0wXUHZF0IPyVEXqKEcd+t2fNSLLL1vC6k7lxZEojNbISXQ==", + "dev": true, + "requires": { + "call-bind": "^1.0.2", + "get-intrinsic": "^1.1.3", + "is-typed-array": "^1.1.10" + } + }, "is-arrayish": { "version": "0.2.1", "resolved": "https://registry.npmjs.org/is-arrayish/-/is-arrayish-0.2.1.tgz", @@ -11446,9 +11932,9 @@ } }, "is-stream": { - "version": "2.0.1", - "resolved": "https://registry.npmjs.org/is-stream/-/is-stream-2.0.1.tgz", - "integrity": "sha512-hFoiJiTl63nn+kstHGBtewWSKnQLpyb155KHheA1l39uvtO9nWIop1p3udqPcUd/xbF1VLMO4n7OI6p7RbngDg==", + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/is-stream/-/is-stream-3.0.0.tgz", + "integrity": "sha512-LnQR4bZ9IADDRSkvpqMGvt/tEJWclzklNgSw48V5EAaAeDd6qGvN8ei6k5p0tvxSR171VmGyHuTiAOfxAbr8kA==", "dev": true }, "is-string": { @@ -11469,6 +11955,19 @@ "has-symbols": "^1.0.2" } }, + "is-typed-array": { + "version": "1.1.10", + "resolved": "https://registry.npmjs.org/is-typed-array/-/is-typed-array-1.1.10.tgz", + "integrity": "sha512-PJqgEHiWZvMpaFZ3uTc8kHPM4+4ADTlDniuQL7cU/UDA0Ql7F70yGfHph3cLNe+c9toaigv+DFzTJKhc2CtO6A==", + "dev": true, + "requires": { + "available-typed-arrays": "^1.0.5", + "call-bind": "^1.0.2", + "for-each": "^0.3.3", + "gopd": "^1.0.1", + "has-tostringtag": "^1.0.0" + } + }, "is-typedarray": { "version": "1.0.0", "resolved": "https://registry.npmjs.org/is-typedarray/-/is-typedarray-1.0.0.tgz", @@ -11476,9 +11975,9 @@ "dev": true }, "is-unicode-supported": { - "version": "0.1.0", - "resolved": "https://registry.npmjs.org/is-unicode-supported/-/is-unicode-supported-0.1.0.tgz", - "integrity": "sha512-knxG2q4UC3u8stRGyAVJCOdxFmv5DZiRcdlIaAQXAbSfJya+OhopNotLQrstBhququ4ZpuKbDc/8S6mgXgPFPw==", + "version": "1.3.0", + "resolved": "https://registry.npmjs.org/is-unicode-supported/-/is-unicode-supported-1.3.0.tgz", + "integrity": "sha512-43r2mRvz+8JRIKnWJ+3j8JtjRKZ6GmjzfaE/qiBJnikNnYv/6bagRJ1kUhNk8R5EX/GkobD+r+sfxCPJsiKBLQ==", "dev": true }, "is-weakref": { @@ -11506,9 +12005,9 @@ } }, "is-yarn-global": { - "version": "0.4.0", - "resolved": "https://registry.npmjs.org/is-yarn-global/-/is-yarn-global-0.4.0.tgz", - "integrity": "sha512-HneQBCrXGBy15QnaDfcn6OLoU8AQPAa0Qn0IeJR/QCo4E8dNZaGGwxpCwWyEBQC5QvFonP8d6t60iGpAHVAfNA==", + "version": "0.4.1", + "resolved": "https://registry.npmjs.org/is-yarn-global/-/is-yarn-global-0.4.1.tgz", + "integrity": "sha512-/kppl+R+LO5VmhYSEWARUFjodS25D68gvj8W7z0I7OWhUla5xWu8KL6CtB2V0R6yqhnRgbcaREMr4EEM6htLPQ==", "dev": true }, "isarray": { @@ -11548,14 +12047,6 @@ "@istanbuljs/schema": "^0.1.2", "istanbul-lib-coverage": "^3.0.0", "semver": "^6.3.0" - }, - "dependencies": { - "semver": { - "version": "6.3.0", - "resolved": "https://registry.npmjs.org/semver/-/semver-6.3.0.tgz", - "integrity": "sha512-b39TBaTSfV6yBrapU89p5fKekE2m/NwnDocOVruQFS1/veMgdzuPcnOM34M6CwxW8jH/lxEa5rBoDeUwu5HHTw==", - "dev": true - } } }, "istanbul-lib-processinfo": { @@ -11621,9 +12112,9 @@ } }, "js-sdsl": { - "version": "4.1.5", - "resolved": "https://registry.npmjs.org/js-sdsl/-/js-sdsl-4.1.5.tgz", - "integrity": "sha512-08bOAKweV2NUC1wqTtf3qZlnpOX/R2DU9ikpjOHs0H+ibQv3zpncVQg6um4uYtRtrwIX8M4Nh3ytK4HGlYAq7Q==", + "version": "4.2.0", + "resolved": "https://registry.npmjs.org/js-sdsl/-/js-sdsl-4.2.0.tgz", + "integrity": "sha512-dyBIzQBDkCqCu+0upx25Y2jGdbTGxE9fshMsCdK0ViOongpV+n5tXRcZY9v7CaVQ79AGS9KA1KHtojxiM7aXSQ==", "dev": true }, "js-tokens": { @@ -11672,9 +12163,9 @@ "dev": true }, "json5": { - "version": "2.2.1", - "resolved": "https://registry.npmjs.org/json5/-/json5-2.2.1.tgz", - "integrity": "sha512-1hqLFMSrGHRHxav9q9gNjJ5EXznIxGVO09xQRrwplcS8qs28pZ8s8hupZAmqDwZUmVZ2Qb2jnyPOWcDH8m8dlA==", + "version": "2.2.3", + "resolved": "https://registry.npmjs.org/json5/-/json5-2.2.3.tgz", + "integrity": "sha512-XmOWe7eyHYH14cLdVPoyg+GOH3rYX++KpzrylJwSW98t3Nk+U8XOl8FWKOgwtzdb8lXGf6zYwDUzeHMWfxasyg==", "dev": true }, "jsonc-parser": { @@ -11699,9 +12190,9 @@ "dev": true }, "keyv": { - "version": "4.5.0", - "resolved": "https://registry.npmjs.org/keyv/-/keyv-4.5.0.tgz", - "integrity": "sha512-2YvuMsA+jnFGtBareKqgANOEKe1mk3HKiXu2fRmAfyxG0MJAywNhi5ttWA3PMjl4NmpyjZNbFifR2vNjW1znfA==", + "version": "4.5.2", + "resolved": "https://registry.npmjs.org/keyv/-/keyv-4.5.2.tgz", + "integrity": "sha512-5MHbFaKn8cNSmVW7BYnijeAVlE4cYA/SVkifVgrh7yotnfhKmjuXpDKjrABLnT0SfHWV21P8ow07OGfRrNDg8g==", "dev": true, "requires": { "json-buffer": "3.0.1" @@ -11733,12 +12224,12 @@ "dev": true }, "locate-path": { - "version": "6.0.0", - "resolved": "https://registry.npmjs.org/locate-path/-/locate-path-6.0.0.tgz", - "integrity": "sha512-iPZK6eYjbxRu3uB4/WZ3EsEIMJFMqAoopl3R+zuq0UjcAm/MO6KCweDgPfP3elTztoKP3KtnVHxTn2NHBSDVUw==", + "version": "5.0.0", + "resolved": "https://registry.npmjs.org/locate-path/-/locate-path-5.0.0.tgz", + "integrity": "sha512-t7hw9pI+WvuwNJXwk5zVHpyhIqzg2qTlklJOf0mVxGSbe3Fp2VieZcduNYjaLDoy6p9uGpQEGWG87WpMKlNq8g==", "dev": true, "requires": { - "p-locate": "^5.0.0" + "p-locate": "^4.1.0" } }, "lodash": { @@ -11766,13 +12257,13 @@ "dev": true }, "log-symbols": { - "version": "4.1.0", - "resolved": "https://registry.npmjs.org/log-symbols/-/log-symbols-4.1.0.tgz", - "integrity": "sha512-8XPvpAA8uyhfteu8pIvQxpJZ7SYYdpUivZpGy6sFsBuKRY/7rQGavedeB8aK+Zkyq6upMFVL/9AW6vOYzfRyLg==", + "version": "5.1.0", + "resolved": "https://registry.npmjs.org/log-symbols/-/log-symbols-5.1.0.tgz", + "integrity": "sha512-l0x2DvrW294C9uDCoQe1VSU4gf529FkSZ6leBl4TiqZH/e+0R7hSfHQBNut2mNygDgHwvYHfFLn6Oxb3VWj2rA==", "dev": true, "requires": { - "chalk": "^4.1.0", - "is-unicode-supported": "^0.1.0" + "chalk": "^5.0.0", + "is-unicode-supported": "^1.1.0" } }, "lowercase-keys": { @@ -11809,14 +12300,6 @@ "dev": true, "requires": { "semver": "^6.0.0" - }, - "dependencies": { - "semver": { - "version": "6.3.0", - "resolved": "https://registry.npmjs.org/semver/-/semver-6.3.0.tgz", - "integrity": "sha512-b39TBaTSfV6yBrapU89p5fKekE2m/NwnDocOVruQFS1/veMgdzuPcnOM34M6CwxW8jH/lxEa5rBoDeUwu5HHTw==", - "dev": true - } } }, "make-error": { @@ -11826,9 +12309,9 @@ "dev": true }, "marked": { - "version": "4.1.1", - "resolved": "https://registry.npmjs.org/marked/-/marked-4.1.1.tgz", - "integrity": "sha512-0cNMnTcUJPxbA6uWmCmjWz4NJRe/0Xfk2NhXCUHjew9qJzFN20krFnsUe7QynwqOwa5m1fZ4UDg0ycKFVC0ccw==", + "version": "4.2.12", + "resolved": "https://registry.npmjs.org/marked/-/marked-4.2.12.tgz", + "integrity": "sha512-yr8hSKa3Fv4D3jdZmtMMPghgVt6TWbk86WQaWhDloQjRSQhMMYCAro7jP7VDJrjjdV8pxVxMssXS8B8Y5DZ5aw==", "dev": true }, "merge-stream": { @@ -11896,9 +12379,9 @@ "dev": true }, "mocha": { - "version": "10.1.0", - "resolved": "https://registry.npmjs.org/mocha/-/mocha-10.1.0.tgz", - "integrity": "sha512-vUF7IYxEoN7XhQpFLxQAEMtE4W91acW4B6En9l97MwE9stL1A9gusXfoHZCLVHDUJ/7V5+lbCM6yMqzo5vNymg==", + "version": "10.2.0", + "resolved": "https://registry.npmjs.org/mocha/-/mocha-10.2.0.tgz", + "integrity": "sha512-IDY7fl/BecMwFHzoqF2sg/SHHANeBoMMXFlS9r0OXKDssYE1M5O43wUY/9BVPeIvfH2zmEbBfseqN9gBQZzXkg==", "dev": true, "requires": { "ansi-colors": "4.1.1", @@ -11924,13 +12407,40 @@ "yargs-unparser": "2.0.0" }, "dependencies": { - "brace-expansion": { - "version": "2.0.1", - "resolved": "https://registry.npmjs.org/brace-expansion/-/brace-expansion-2.0.1.tgz", - "integrity": "sha512-XnAIvQ8eM+kC6aULx6wuQiwVsnzsi9d3WxzV3FpWTGA19F621kwdbsAcFKXgKUHZWsy+mY6iL1sHTxWEFCytDA==", + "ansi-regex": { + "version": "5.0.1", + "resolved": "https://registry.npmjs.org/ansi-regex/-/ansi-regex-5.0.1.tgz", + "integrity": "sha512-quJQXlTSUGL2LH9SUXo8VwsY4soanhgo6LNSm84E1LBcE8s3O0wpdiRzyR9z/ZZJMlMWv37qOOb9pdJlMUEKFQ==", + "dev": true + }, + "ansi-styles": { + "version": "4.3.0", + "resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-4.3.0.tgz", + "integrity": "sha512-zbB9rCJAT1rbjiVDb2hqKFHNYLxgtk8NURxZ3IZwD3F6NtxbXZQCnnSi1Lkx+IDohdPlFp222wVALIheZJQSEg==", "dev": true, "requires": { - "balanced-match": "^1.0.0" + "color-convert": "^2.0.1" + } + }, + "chalk": { + "version": "4.1.2", + "resolved": "https://registry.npmjs.org/chalk/-/chalk-4.1.2.tgz", + "integrity": "sha512-oKnbhFyRIXpUuez8iBMmyEa4nbj4IOQyuhc/wy9kY7/WVPcwIO9VA668Pu8RkO7+0G76SLROeyw9CpQ061i4mA==", + "dev": true, + "requires": { + "ansi-styles": "^4.1.0", + "supports-color": "^7.1.0" + }, + "dependencies": { + "supports-color": { + "version": "7.2.0", + "resolved": "https://registry.npmjs.org/supports-color/-/supports-color-7.2.0.tgz", + "integrity": "sha512-qpCAvRl9stuOHveKsn7HncJRvv501qIacKzQlO/+Lwxc9+0q2wLyv4Dfvt80/DPn2pqOBsJdDiogXGR9+OvwRw==", + "dev": true, + "requires": { + "has-flag": "^4.0.0" + } + } } }, "cliui": { @@ -11950,6 +12460,72 @@ "integrity": "sha512-MSjYzcWNOA0ewAHpz0MxpYFvwg6yjy1NG3xteoqz644VCo/RPgnr1/GGt+ic3iJTzQ8Eu3TdM14SawnVUmGE6A==", "dev": true }, + "escape-string-regexp": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/escape-string-regexp/-/escape-string-regexp-4.0.0.tgz", + "integrity": "sha512-TtpcNJ3XAzx3Gq8sWRzJaVajRs0uVxA2YAkdb1jm2YkPz4G6egUFAyA3n5vtEIZefPk5Wa4UXbKuS5fKkJWdgA==", + "dev": true + }, + "find-up": { + "version": "5.0.0", + "resolved": "https://registry.npmjs.org/find-up/-/find-up-5.0.0.tgz", + "integrity": "sha512-78/PXT1wlLLDgTzDs7sjq9hzz0vXD+zn+7wypEe4fXQxCmdmqfGsEPQxmiCSQI3ajFV91bVSsvNtrJRiW6nGng==", + "dev": true, + "requires": { + "locate-path": "^6.0.0", + "path-exists": "^4.0.0" + } + }, + "glob": { + "version": "7.2.0", + "resolved": "https://registry.npmjs.org/glob/-/glob-7.2.0.tgz", + "integrity": "sha512-lmLf6gtyrPq8tTjSmrO94wBeQbFR3HbLHbuyD69wuyQkImp2hWqMGB47OX65FBkPffO641IP9jWa1z4ivqG26Q==", + "dev": true, + "requires": { + "fs.realpath": "^1.0.0", + "inflight": "^1.0.4", + "inherits": "2", + "minimatch": "^3.0.4", + "once": "^1.3.0", + "path-is-absolute": "^1.0.0" + }, + "dependencies": { + "minimatch": { + "version": "3.1.2", + "resolved": "https://registry.npmjs.org/minimatch/-/minimatch-3.1.2.tgz", + "integrity": "sha512-J7p63hRiAjw1NDEww1W7i37+ByIrOWO5XQQAzZ3VOcL0PNybwpfmV/N05zFAzwQ9USyEcX6t3UO+K5aqBQOIHw==", + "dev": true, + "requires": { + "brace-expansion": "^1.1.7" + } + } + } + }, + "is-unicode-supported": { + "version": "0.1.0", + "resolved": "https://registry.npmjs.org/is-unicode-supported/-/is-unicode-supported-0.1.0.tgz", + "integrity": "sha512-knxG2q4UC3u8stRGyAVJCOdxFmv5DZiRcdlIaAQXAbSfJya+OhopNotLQrstBhququ4ZpuKbDc/8S6mgXgPFPw==", + "dev": true + }, + "locate-path": { + "version": "6.0.0", + "resolved": "https://registry.npmjs.org/locate-path/-/locate-path-6.0.0.tgz", + "integrity": "sha512-iPZK6eYjbxRu3uB4/WZ3EsEIMJFMqAoopl3R+zuq0UjcAm/MO6KCweDgPfP3elTztoKP3KtnVHxTn2NHBSDVUw==", + "dev": true, + "requires": { + "p-locate": "^5.0.0" + } + }, + "log-symbols": { + "version": "4.1.0", + "resolved": "https://registry.npmjs.org/log-symbols/-/log-symbols-4.1.0.tgz", + "integrity": "sha512-8XPvpAA8uyhfteu8pIvQxpJZ7SYYdpUivZpGy6sFsBuKRY/7rQGavedeB8aK+Zkyq6upMFVL/9AW6vOYzfRyLg==", + "dev": true, + "requires": { + "chalk": "^4.1.0", + "is-unicode-supported": "^0.1.0" + } + }, "minimatch": { "version": "5.0.1", "resolved": "https://registry.npmjs.org/minimatch/-/minimatch-5.0.1.tgz", @@ -11957,6 +12533,17 @@ "dev": true, "requires": { "brace-expansion": "^2.0.1" + }, + "dependencies": { + "brace-expansion": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/brace-expansion/-/brace-expansion-2.0.1.tgz", + "integrity": "sha512-XnAIvQ8eM+kC6aULx6wuQiwVsnzsi9d3WxzV3FpWTGA19F621kwdbsAcFKXgKUHZWsy+mY6iL1sHTxWEFCytDA==", + "dev": true, + "requires": { + "balanced-match": "^1.0.0" + } + } } }, "ms": { @@ -11965,6 +12552,24 @@ "integrity": "sha512-6FlzubTLZG3J2a/NVCAleEhjzq5oxgHyaCU9yYXvcLsvoVaHJq/s5xXI6/XXP6tz7R9xAOtHnSO/tXtF3WRTlA==", "dev": true }, + "p-limit": { + "version": "3.1.0", + "resolved": "https://registry.npmjs.org/p-limit/-/p-limit-3.1.0.tgz", + "integrity": "sha512-TYOanM3wGwNGsZN2cVTYPArw454xnXj5qmWF1bEoAc4+cU/ol7GVh7odevjp1FNHduHc3KZMcFduxU5Xc6uJRQ==", + "dev": true, + "requires": { + "yocto-queue": "^0.1.0" + } + }, + "p-locate": { + "version": "5.0.0", + "resolved": "https://registry.npmjs.org/p-locate/-/p-locate-5.0.0.tgz", + "integrity": "sha512-LaNjtRWUBY++zB5nE/NwcaoMylSPk+S+ZHNB1TzdbMJMny6dynpAGt7X/tl/QYq3TIeE6nxHppbo2LGymrG5Pw==", + "dev": true, + "requires": { + "p-limit": "^3.0.2" + } + }, "string-width": { "version": "4.2.3", "resolved": "https://registry.npmjs.org/string-width/-/string-width-4.2.3.tgz", @@ -11976,6 +12581,15 @@ "strip-ansi": "^6.0.1" } }, + "strip-ansi": { + "version": "6.0.1", + "resolved": "https://registry.npmjs.org/strip-ansi/-/strip-ansi-6.0.1.tgz", + "integrity": "sha512-Y38VPSHcqkFrCpFnQ9vuSXmquuv5oXOKpGeT6aGrr3o3Gc9AlVa6JBfUSOCnbxGGZF+/0ooI7KrPuUSztUdU5A==", + "dev": true, + "requires": { + "ansi-regex": "^5.0.1" + } + }, "supports-color": { "version": "8.1.1", "resolved": "https://registry.npmjs.org/supports-color/-/supports-color-8.1.1.tgz", @@ -11996,6 +12610,12 @@ "strip-ansi": "^6.0.0" } }, + "y18n": { + "version": "5.0.8", + "resolved": "https://registry.npmjs.org/y18n/-/y18n-5.0.8.tgz", + "integrity": "sha512-0pfFzegeDWJHJIAmTLRP2DwHjdF5s7jo9tuztdQxAhINCdvS+3nGINqPd00AphqJR/0LhANUS6/+7SCb98YOfA==", + "dev": true + }, "yargs": { "version": "16.2.0", "resolved": "https://registry.npmjs.org/yargs/-/yargs-16.2.0.tgz", @@ -12010,6 +12630,12 @@ "y18n": "^5.0.5", "yargs-parser": "^20.2.2" } + }, + "yargs-parser": { + "version": "20.2.4", + "resolved": "https://registry.npmjs.org/yargs-parser/-/yargs-parser-20.2.4.tgz", + "integrity": "sha512-WOkpgNhPTlE73h4VFAFsOnomJVaovO8VqLDzy5saChRBFQFBoMYirowyW+Q9HB4HFF4Z7VZTiG3iSzJJA29yRA==", + "dev": true } } }, @@ -12037,6 +12663,12 @@ "integrity": "sha512-OWND8ei3VtNC9h7V60qff3SVobHr996CTwgxubgyQYEpg290h9J0buyECNNJexkFm5sOajh5G116RYA1c8ZMSw==", "dev": true }, + "natural-compare-lite": { + "version": "1.4.0", + "resolved": "https://registry.npmjs.org/natural-compare-lite/-/natural-compare-lite-1.4.0.tgz", + "integrity": "sha512-Tj+HTDSJJKaZnfiuw+iaF9skdPpTo2GtEly5JHnWV/hfv2Qj/9RKsGISQtLh2ox3l5EAGw487hnBee0sIJ6v2g==", + "dev": true + }, "netmask": { "version": "2.0.2", "resolved": "https://registry.npmjs.org/netmask/-/netmask-2.0.2.tgz", @@ -12061,13 +12693,13 @@ } }, "nise": { - "version": "5.1.1", - "resolved": "https://registry.npmjs.org/nise/-/nise-5.1.1.tgz", - "integrity": "sha512-yr5kW2THW1AkxVmCnKEh4nbYkJdB3I7LUkiUgOvEkOp414mc2UMaHMA7pjq1nYowhdoJZGwEKGaQVbxfpWj10A==", + "version": "5.1.4", + "resolved": "https://registry.npmjs.org/nise/-/nise-5.1.4.tgz", + "integrity": "sha512-8+Ib8rRJ4L0o3kfmyVCL7gzrohyDe0cMFTBa2d364yIrEGMEoetznKJx899YxjybU6bL9SQkYPSBBs1gyYs8Xg==", "dev": true, "requires": { - "@sinonjs/commons": "^1.8.3", - "@sinonjs/fake-timers": ">=5", + "@sinonjs/commons": "^2.0.0", + "@sinonjs/fake-timers": "^10.0.2", "@sinonjs/text-encoding": "^0.7.1", "just-extend": "^4.0.2", "path-to-regexp": "^1.7.0" @@ -12080,9 +12712,9 @@ "dev": true }, "node-fetch": { - "version": "3.2.10", - "resolved": "https://registry.npmjs.org/node-fetch/-/node-fetch-3.2.10.tgz", - "integrity": "sha512-MhuzNwdURnZ1Cp4XTazr69K0BTizsBroX7Zx3UgDSVcZYKF/6p0CBe4EUb/hLqmzVhl0UpYfgRljQ4yxE+iCxA==", + "version": "3.3.0", + "resolved": "https://registry.npmjs.org/node-fetch/-/node-fetch-3.3.0.tgz", + "integrity": "sha512-BKwRP/O0UvoMKp7GNdwPlObhYGB5DQqwhEDQlNKuoqwVYSxkSZCSbHjnFFmUEtwSKRPU4kNK8PbDYYitwaE3QA==", "dev": true, "requires": { "data-uri-to-buffer": "^4.0.0", @@ -12100,9 +12732,9 @@ } }, "node-releases": { - "version": "2.0.6", - "resolved": "https://registry.npmjs.org/node-releases/-/node-releases-2.0.6.tgz", - "integrity": "sha512-PiVXnNuFm5+iYkLBNeq5211hvO38y63T0i2KKh2KnUs3RpzJ+JtODFjkD8yjLwnDkTYF1eKXheUwdssR+NRZdg==", + "version": "2.0.8", + "resolved": "https://registry.npmjs.org/node-releases/-/node-releases-2.0.8.tgz", + "integrity": "sha512-dFSmB8fFHEH/s81Xi+Y/15DQY6VHW81nXRj86EMSL3lmuTmK1e+aT4wrFCkTbm+gSwkw4KpX+rT/pMM2c1mF+A==", "dev": true }, "normalize-path": { @@ -12112,9 +12744,9 @@ "dev": true }, "normalize-url": { - "version": "7.2.0", - "resolved": "https://registry.npmjs.org/normalize-url/-/normalize-url-7.2.0.tgz", - "integrity": "sha512-uhXOdZry0L6M2UIo9BTt7FdpBDiAGN/7oItedQwPKh8jh31ZlvC8U9Xl/EJ3aijDHaywXTW3QbZ6LuCocur1YA==", + "version": "8.0.0", + "resolved": "https://registry.npmjs.org/normalize-url/-/normalize-url-8.0.0.tgz", + "integrity": "sha512-uVFpKhj5MheNBJRTiMZ9pE/7hD1QTeEvugSJW/OmLzAp78PB5O6adfMNTvmfKhXBkvCzC+rqifWcVYpGFwTjnw==", "dev": true }, "npm-run-path": { @@ -12169,122 +12801,11 @@ "yargs": "^15.0.2" }, "dependencies": { - "cliui": { - "version": "6.0.0", - "resolved": "https://registry.npmjs.org/cliui/-/cliui-6.0.0.tgz", - "integrity": "sha512-t6wbgtoCXvAzst7QgXxJYqPt0usEfbgQdftEPbLL/cvv6HPE5VgvqCuAIDR0NgU52ds6rFwqrgakNLrHEjCbrQ==", - "dev": true, - "requires": { - "string-width": "^4.2.0", - "strip-ansi": "^6.0.0", - "wrap-ansi": "^6.2.0" - } - }, - "emoji-regex": { - "version": "8.0.0", - "resolved": "https://registry.npmjs.org/emoji-regex/-/emoji-regex-8.0.0.tgz", - "integrity": "sha512-MSjYzcWNOA0ewAHpz0MxpYFvwg6yjy1NG3xteoqz644VCo/RPgnr1/GGt+ic3iJTzQ8Eu3TdM14SawnVUmGE6A==", - "dev": true - }, - "find-up": { - "version": "4.1.0", - "resolved": "https://registry.npmjs.org/find-up/-/find-up-4.1.0.tgz", - "integrity": "sha512-PpOwAdQ/YlXQ2vj8a3h8IipDuYRi3wceVQQGYWxNINccq40Anw7BlsEXCMbt1Zt+OLA6Fq9suIpIWD0OsnISlw==", - "dev": true, - "requires": { - "locate-path": "^5.0.0", - "path-exists": "^4.0.0" - } - }, - "locate-path": { - "version": "5.0.0", - "resolved": "https://registry.npmjs.org/locate-path/-/locate-path-5.0.0.tgz", - "integrity": "sha512-t7hw9pI+WvuwNJXwk5zVHpyhIqzg2qTlklJOf0mVxGSbe3Fp2VieZcduNYjaLDoy6p9uGpQEGWG87WpMKlNq8g==", - "dev": true, - "requires": { - "p-locate": "^4.1.0" - } - }, - "p-limit": { - "version": "2.3.0", - "resolved": "https://registry.npmjs.org/p-limit/-/p-limit-2.3.0.tgz", - "integrity": "sha512-//88mFWSJx8lxCzwdAABTJL2MyWB12+eIY7MDL2SqLmAkeKU9qxRvWuSyTjm3FUmpBEMuFfckAIqEaVGUDxb6w==", - "dev": true, - "requires": { - "p-try": "^2.0.0" - } - }, - "p-locate": { - "version": "4.1.0", - "resolved": "https://registry.npmjs.org/p-locate/-/p-locate-4.1.0.tgz", - "integrity": "sha512-R79ZZ/0wAxKGu3oYMlz8jy/kbhsNrS7SKZ7PxEHBgJ5+F2mtFW2fK2cOtBh1cHYkQsbzFV7I+EoRKe6Yt0oK7A==", - "dev": true, - "requires": { - "p-limit": "^2.2.0" - } - }, "resolve-from": { "version": "5.0.0", "resolved": "https://registry.npmjs.org/resolve-from/-/resolve-from-5.0.0.tgz", "integrity": "sha512-qYg9KP24dD5qka9J47d0aVky0N+b4fTU89LN9iDnjB5waksiC49rvMB0PrUJQGoTmH50XPiqOvAjDfaijGxYZw==", "dev": true - }, - "string-width": { - "version": "4.2.3", - "resolved": "https://registry.npmjs.org/string-width/-/string-width-4.2.3.tgz", - "integrity": "sha512-wKyQRQpjJ0sIp62ErSZdGsjMJWsap5oRNihHhu6G7JVO/9jIB6UyevL+tXuOqrng8j/cxKTWyWUwvSTriiZz/g==", - "dev": true, - "requires": { - "emoji-regex": "^8.0.0", - "is-fullwidth-code-point": "^3.0.0", - "strip-ansi": "^6.0.1" - } - }, - "wrap-ansi": { - "version": "6.2.0", - "resolved": "https://registry.npmjs.org/wrap-ansi/-/wrap-ansi-6.2.0.tgz", - "integrity": "sha512-r6lPcBGxZXlIcymEu7InxDMhdW0KDxpLgoFLcguasxCaJ/SOIZwINatK9KY/tf+ZrlywOKU0UDj3ATXUBfxJXA==", - "dev": true, - "requires": { - "ansi-styles": "^4.0.0", - "string-width": "^4.1.0", - "strip-ansi": "^6.0.0" - } - }, - "y18n": { - "version": "4.0.3", - "resolved": "https://registry.npmjs.org/y18n/-/y18n-4.0.3.tgz", - "integrity": "sha512-JKhqTOwSrqNA1NY5lSztJ1GrBiUodLMmIZuLiDaMRJ+itFd+ABVE8XBjOvIWL+rSqNDC74LCSFmlb/U4UZ4hJQ==", - "dev": true - }, - "yargs": { - "version": "15.4.1", - "resolved": "https://registry.npmjs.org/yargs/-/yargs-15.4.1.tgz", - "integrity": "sha512-aePbxDmcYW++PaqBsJ+HYUFwCdv4LVvdnhBy78E57PIor8/OVvhMrADFFEDh8DHDFRv/O9i3lPhsENjO7QX0+A==", - "dev": true, - "requires": { - "cliui": "^6.0.0", - "decamelize": "^1.2.0", - "find-up": "^4.1.0", - "get-caller-file": "^2.0.1", - "require-directory": "^2.1.1", - "require-main-filename": "^2.0.0", - "set-blocking": "^2.0.0", - "string-width": "^4.2.0", - "which-module": "^2.0.0", - "y18n": "^4.0.0", - "yargs-parser": "^18.1.2" - } - }, - "yargs-parser": { - "version": "18.1.3", - "resolved": "https://registry.npmjs.org/yargs-parser/-/yargs-parser-18.1.3.tgz", - "integrity": "sha512-o50j0JeToy/4K6OZcaQmW6lyXXKhq7csREXcDwk2omFPJEwUNOVtJKvmDr9EI1fAJZUyZcRF7kxGBWmRXudrCQ==", - "dev": true, - "requires": { - "camelcase": "^5.0.0", - "decamelize": "^1.2.0" - } } } }, @@ -12295,9 +12816,9 @@ "dev": true }, "object-inspect": { - "version": "1.12.2", - "resolved": "https://registry.npmjs.org/object-inspect/-/object-inspect-1.12.2.tgz", - "integrity": "sha512-z+cPxW0QGUp0mcqcsgQyLVRDoXFQbXOwBaqyF7VIgI4TWNQsDHrBpUQslRmIfAoYWdYzs6UlKJtB2XJpTaNSpQ==", + "version": "1.12.3", + "resolved": "https://registry.npmjs.org/object-inspect/-/object-inspect-1.12.3.tgz", + "integrity": "sha512-geUvdk7c+eizMNUDkRpW1wJwgfOiOeHbxBR/hLXK1aT6zmVSO0jsQcs7fj6MGw89jC/cjGfLcNOrtMYtGqm81g==", "dev": true }, "object-keys": { @@ -12367,54 +12888,15 @@ "integrity": "sha512-EJQ3NiP5Xo94wJXIzAyOtSb0QEIAUu7m8t6UZ9krbz0vAJqr92JpcK/lEXg91q6B9pEGqrykkd2EQplnifDSBw==", "dev": true, "requires": { - "bl": "^5.0.0", - "chalk": "^5.0.0", - "cli-cursor": "^4.0.0", - "cli-spinners": "^2.6.1", - "is-interactive": "^2.0.0", - "is-unicode-supported": "^1.1.0", - "log-symbols": "^5.1.0", - "strip-ansi": "^7.0.1", - "wcwidth": "^1.0.1" - }, - "dependencies": { - "ansi-regex": { - "version": "6.0.1", - "resolved": "https://registry.npmjs.org/ansi-regex/-/ansi-regex-6.0.1.tgz", - "integrity": "sha512-n5M855fKb2SsfMIiFFoVrABHJC8QtHwVx+mHWP3QcEqBHYienj5dHSgjbxtC0WEZXYt4wcD6zrQElDPhFuZgfA==", - "dev": true - }, - "chalk": { - "version": "5.1.2", - "resolved": "https://registry.npmjs.org/chalk/-/chalk-5.1.2.tgz", - "integrity": "sha512-E5CkT4jWURs1Vy5qGJye+XwCkNj7Od3Af7CP6SujMetSMkLs8Do2RWJK5yx1wamHV/op8Rz+9rltjaTQWDnEFQ==", - "dev": true - }, - "is-unicode-supported": { - "version": "1.3.0", - "resolved": "https://registry.npmjs.org/is-unicode-supported/-/is-unicode-supported-1.3.0.tgz", - "integrity": "sha512-43r2mRvz+8JRIKnWJ+3j8JtjRKZ6GmjzfaE/qiBJnikNnYv/6bagRJ1kUhNk8R5EX/GkobD+r+sfxCPJsiKBLQ==", - "dev": true - }, - "log-symbols": { - "version": "5.1.0", - "resolved": "https://registry.npmjs.org/log-symbols/-/log-symbols-5.1.0.tgz", - "integrity": "sha512-l0x2DvrW294C9uDCoQe1VSU4gf529FkSZ6leBl4TiqZH/e+0R7hSfHQBNut2mNygDgHwvYHfFLn6Oxb3VWj2rA==", - "dev": true, - "requires": { - "chalk": "^5.0.0", - "is-unicode-supported": "^1.1.0" - } - }, - "strip-ansi": { - "version": "7.0.1", - "resolved": "https://registry.npmjs.org/strip-ansi/-/strip-ansi-7.0.1.tgz", - "integrity": "sha512-cXNxvT8dFNRVfhVME3JAe98mkXDYN2O1l7jmcwMnOslDeESg1rF/OZMtK0nRAhiari1unG5cD4jG3rapUAkLbw==", - "dev": true, - "requires": { - "ansi-regex": "^6.0.1" - } - } + "bl": "^5.0.0", + "chalk": "^5.0.0", + "cli-cursor": "^4.0.0", + "cli-spinners": "^2.6.1", + "is-interactive": "^2.0.0", + "is-unicode-supported": "^1.1.0", + "log-symbols": "^5.1.0", + "strip-ansi": "^7.0.1", + "wcwidth": "^1.0.1" } }, "os-name": { @@ -12440,21 +12922,21 @@ "dev": true }, "p-limit": { - "version": "3.1.0", - "resolved": "https://registry.npmjs.org/p-limit/-/p-limit-3.1.0.tgz", - "integrity": "sha512-TYOanM3wGwNGsZN2cVTYPArw454xnXj5qmWF1bEoAc4+cU/ol7GVh7odevjp1FNHduHc3KZMcFduxU5Xc6uJRQ==", + "version": "2.3.0", + "resolved": "https://registry.npmjs.org/p-limit/-/p-limit-2.3.0.tgz", + "integrity": "sha512-//88mFWSJx8lxCzwdAABTJL2MyWB12+eIY7MDL2SqLmAkeKU9qxRvWuSyTjm3FUmpBEMuFfckAIqEaVGUDxb6w==", "dev": true, "requires": { - "yocto-queue": "^0.1.0" + "p-try": "^2.0.0" } }, "p-locate": { - "version": "5.0.0", - "resolved": "https://registry.npmjs.org/p-locate/-/p-locate-5.0.0.tgz", - "integrity": "sha512-LaNjtRWUBY++zB5nE/NwcaoMylSPk+S+ZHNB1TzdbMJMny6dynpAGt7X/tl/QYq3TIeE6nxHppbo2LGymrG5Pw==", + "version": "4.1.0", + "resolved": "https://registry.npmjs.org/p-locate/-/p-locate-4.1.0.tgz", + "integrity": "sha512-R79ZZ/0wAxKGu3oYMlz8jy/kbhsNrS7SKZ7PxEHBgJ5+F2mtFW2fK2cOtBh1cHYkQsbzFV7I+EoRKe6Yt0oK7A==", "dev": true, "requires": { - "p-limit": "^3.0.2" + "p-limit": "^2.2.0" } }, "p-map": { @@ -12522,6 +13004,32 @@ "registry-auth-token": "^5.0.1", "registry-url": "^6.0.0", "semver": "^7.3.7" + }, + "dependencies": { + "lru-cache": { + "version": "6.0.0", + "resolved": "https://registry.npmjs.org/lru-cache/-/lru-cache-6.0.0.tgz", + "integrity": "sha512-Jo6dJ04CmSjuznwJSS3pUeWmd/H0ffTlkXXgwZi+eq1UCmqQwCh+eLsYOYCwY991i2Fah4h1BEMCx4qThGbsiA==", + "dev": true, + "requires": { + "yallist": "^4.0.0" + } + }, + "semver": { + "version": "7.3.8", + "resolved": "https://registry.npmjs.org/semver/-/semver-7.3.8.tgz", + "integrity": "sha512-NB1ctGL5rlHrPJtFDVIVzTyQylMLu9N9VICA6HSFJo8MCGVTMW6gfpicwKmmK/dAjTOrqu5l63JJOpDSrAis3A==", + "dev": true, + "requires": { + "lru-cache": "^6.0.0" + } + }, + "yallist": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/yallist/-/yallist-4.0.0.tgz", + "integrity": "sha512-3wdGidZyq5PB084XLES5TpOSRA3wjXAlIWMhum2kRcv/41Sn2emQ0dycQW4uZXLejwKvg6EsvbdlVL+FYEct7A==", + "dev": true + } } }, "parent-module": { @@ -12650,45 +13158,6 @@ "dev": true, "requires": { "find-up": "^4.0.0" - }, - "dependencies": { - "find-up": { - "version": "4.1.0", - "resolved": "https://registry.npmjs.org/find-up/-/find-up-4.1.0.tgz", - "integrity": "sha512-PpOwAdQ/YlXQ2vj8a3h8IipDuYRi3wceVQQGYWxNINccq40Anw7BlsEXCMbt1Zt+OLA6Fq9suIpIWD0OsnISlw==", - "dev": true, - "requires": { - "locate-path": "^5.0.0", - "path-exists": "^4.0.0" - } - }, - "locate-path": { - "version": "5.0.0", - "resolved": "https://registry.npmjs.org/locate-path/-/locate-path-5.0.0.tgz", - "integrity": "sha512-t7hw9pI+WvuwNJXwk5zVHpyhIqzg2qTlklJOf0mVxGSbe3Fp2VieZcduNYjaLDoy6p9uGpQEGWG87WpMKlNq8g==", - "dev": true, - "requires": { - "p-locate": "^4.1.0" - } - }, - "p-limit": { - "version": "2.3.0", - "resolved": "https://registry.npmjs.org/p-limit/-/p-limit-2.3.0.tgz", - "integrity": "sha512-//88mFWSJx8lxCzwdAABTJL2MyWB12+eIY7MDL2SqLmAkeKU9qxRvWuSyTjm3FUmpBEMuFfckAIqEaVGUDxb6w==", - "dev": true, - "requires": { - "p-try": "^2.0.0" - } - }, - "p-locate": { - "version": "4.1.0", - "resolved": "https://registry.npmjs.org/p-locate/-/p-locate-4.1.0.tgz", - "integrity": "sha512-R79ZZ/0wAxKGu3oYMlz8jy/kbhsNrS7SKZ7PxEHBgJ5+F2mtFW2fK2cOtBh1cHYkQsbzFV7I+EoRKe6Yt0oK7A==", - "dev": true, - "requires": { - "p-limit": "^2.2.0" - } - } } }, "prelude-ls": { @@ -12707,16 +13176,16 @@ } }, "promise.allsettled": { - "version": "1.0.5", - "resolved": "https://registry.npmjs.org/promise.allsettled/-/promise.allsettled-1.0.5.tgz", - "integrity": "sha512-tVDqeZPoBC0SlzJHzWGZ2NKAguVq2oiYj7gbggbiTvH2itHohijTp7njOUA0aQ/nl+0lr/r6egmhoYu63UZ/pQ==", + "version": "1.0.6", + "resolved": "https://registry.npmjs.org/promise.allsettled/-/promise.allsettled-1.0.6.tgz", + "integrity": "sha512-22wJUOD3zswWFqgwjNHa1965LvqTX87WPu/lreY2KSd7SVcERfuZ4GfUaOnJNnvtoIv2yXT/W00YIGMetXtFXg==", "dev": true, "requires": { - "array.prototype.map": "^1.0.4", + "array.prototype.map": "^1.0.5", "call-bind": "^1.0.2", - "define-properties": "^1.1.3", - "es-abstract": "^1.19.1", - "get-intrinsic": "^1.1.1", + "define-properties": "^1.1.4", + "es-abstract": "^1.20.4", + "get-intrinsic": "^1.1.3", "iterate-value": "^1.0.2" } }, @@ -12755,9 +13224,9 @@ "dev": true }, "punycode": { - "version": "2.1.1", - "resolved": "https://registry.npmjs.org/punycode/-/punycode-2.1.1.tgz", - "integrity": "sha512-XRsRjdf+j5ml+y/6GKHPZbrF/8p2Yga0JPtdqTIY2Xe5ohJPD9saDJJLPvp9+NSBprVvevdXZybnj2cv8OEd0A==", + "version": "2.2.0", + "resolved": "https://registry.npmjs.org/punycode/-/punycode-2.2.0.tgz", + "integrity": "sha512-LN6QV1IJ9ZhxWTNdktaPClrNfp8xdSAYS0Zk2ddX7XsXZAxckMHPCBcHRo0cTcEIgYPRiGEkmji3Idkh2yFtYw==", "dev": true }, "pupa": { @@ -12893,33 +13362,32 @@ } }, "release-it": { - "version": "15.5.0", - "resolved": "https://registry.npmjs.org/release-it/-/release-it-15.5.0.tgz", - "integrity": "sha512-/pQo/PwEXAWRBgVGLE+3IQ3hUoeiDZMGAo/Egin1envCyLyjzrU7+0P2w4iZ1Xv5OxhC2AcaPaN5eY1ql47cBA==", + "version": "15.6.0", + "resolved": "https://registry.npmjs.org/release-it/-/release-it-15.6.0.tgz", + "integrity": "sha512-NXewgzO8QV1LOFjn2K7/dgE1Y1cG+2JiLOU/x9X/Lq9UdFn2hTH1r9SSrufCxG+y/Rp+oN8liYTsNptKrj92kg==", "dev": true, "requires": { "@iarna/toml": "2.2.5", - "@octokit/rest": "19.0.4", + "@octokit/rest": "19.0.5", "async-retry": "1.3.3", - "chalk": "5.0.1", - "cosmiconfig": "7.0.1", + "chalk": "5.1.2", + "cosmiconfig": "8.0.0", "execa": "6.1.0", - "form-data": "4.0.0", "git-url-parse": "13.1.0", "globby": "13.1.2", - "got": "12.5.1", - "inquirer": "9.1.2", + "got": "12.5.3", + "inquirer": "9.1.4", "is-ci": "3.0.1", "lodash": "4.17.21", "mime-types": "2.1.35", "new-github-release-url": "2.0.0", - "node-fetch": "3.2.10", + "node-fetch": "3.3.0", "open": "8.4.0", "ora": "6.1.2", "os-name": "5.0.1", - "promise.allsettled": "1.0.5", + "promise.allsettled": "1.0.6", "proxy-agent": "5.0.0", - "semver": "7.3.7", + "semver": "7.3.8", "shelljs": "0.8.5", "update-notifier": "6.0.2", "url-join": "5.0.0", @@ -12927,12 +13395,6 @@ "yargs-parser": "21.1.1" }, "dependencies": { - "chalk": { - "version": "5.0.1", - "resolved": "https://registry.npmjs.org/chalk/-/chalk-5.0.1.tgz", - "integrity": "sha512-Fo07WOYGqMfCWHOzSXOt2CxDbC6skS/jO9ynEcmpANMoPrD+W1r1K6Vx7iNm+AQmETU1Xr2t+n8nzkV9t6xh3w==", - "dev": true - }, "globby": { "version": "13.1.2", "resolved": "https://registry.npmjs.org/globby/-/globby-13.1.2.tgz", @@ -12956,9 +13418,9 @@ } }, "semver": { - "version": "7.3.7", - "resolved": "https://registry.npmjs.org/semver/-/semver-7.3.7.tgz", - "integrity": "sha512-QlYTucUYOews+WeEujDoEGziz4K6c47V/Bd+LjSSYcA94p+DmINdf7ncaUinThfvZyu13lN9OY1XDxt8C0Tw0g==", + "version": "7.3.8", + "resolved": "https://registry.npmjs.org/semver/-/semver-7.3.8.tgz", + "integrity": "sha512-NB1ctGL5rlHrPJtFDVIVzTyQylMLu9N9VICA6HSFJo8MCGVTMW6gfpicwKmmK/dAjTOrqu5l63JJOpDSrAis3A==", "dev": true, "requires": { "lru-cache": "^6.0.0" @@ -12975,12 +13437,6 @@ "resolved": "https://registry.npmjs.org/yallist/-/yallist-4.0.0.tgz", "integrity": "sha512-3wdGidZyq5PB084XLES5TpOSRA3wjXAlIWMhum2kRcv/41Sn2emQ0dycQW4uZXLejwKvg6EsvbdlVL+FYEct7A==", "dev": true - }, - "yargs-parser": { - "version": "21.1.1", - "resolved": "https://registry.npmjs.org/yargs-parser/-/yargs-parser-21.1.1.tgz", - "integrity": "sha512-tVpsJW7DdjecAiFpbIB1e3qxIQsE6NoPc5/eTdrbbIC4h0LVsWhnoa3g+m2HclBIujHzsxZ4VJVA+GUuc2/LBw==", - "dev": true } } }, @@ -13101,9 +13557,9 @@ } }, "rxjs": { - "version": "7.5.7", - "resolved": "https://registry.npmjs.org/rxjs/-/rxjs-7.5.7.tgz", - "integrity": "sha512-z9MzKh/UcOqB3i20H6rtrlaE/CgjLOvheWK/9ILrbhROGTweAi1BaFsTT9FbwZi5Trr1qNRs+MXkhmR06awzQA==", + "version": "7.8.0", + "resolved": "https://registry.npmjs.org/rxjs/-/rxjs-7.8.0.tgz", + "integrity": "sha512-F2+gxDshqmIub1KdvZkaEfGDwLNpPvk9Fs6LD/MyQxNgMds/WH9OdDDXOmxUZpME+iSK3rQCctkL0DYyytUqMg==", "dev": true, "requires": { "tslib": "^2.1.0" @@ -13133,12 +13589,18 @@ "dev": true }, "semver": { - "version": "7.3.8", - "resolved": "https://registry.npmjs.org/semver/-/semver-7.3.8.tgz", - "integrity": "sha512-NB1ctGL5rlHrPJtFDVIVzTyQylMLu9N9VICA6HSFJo8MCGVTMW6gfpicwKmmK/dAjTOrqu5l63JJOpDSrAis3A==", + "version": "6.3.0", + "resolved": "https://registry.npmjs.org/semver/-/semver-6.3.0.tgz", + "integrity": "sha512-b39TBaTSfV6yBrapU89p5fKekE2m/NwnDocOVruQFS1/veMgdzuPcnOM34M6CwxW8jH/lxEa5rBoDeUwu5HHTw==", + "dev": true + }, + "semver-diff": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/semver-diff/-/semver-diff-4.0.0.tgz", + "integrity": "sha512-0Ju4+6A8iOnpL/Thra7dZsSlOHYAHIeMxfhWQRI1/VLcT3WDBZKKtQt/QkBOsiIN9ZpuvHE6cGZ0x4glCMmfiA==", "dev": true, "requires": { - "lru-cache": "^6.0.0" + "semver": "^7.3.5" }, "dependencies": { "lru-cache": { @@ -13150,6 +13612,15 @@ "yallist": "^4.0.0" } }, + "semver": { + "version": "7.3.8", + "resolved": "https://registry.npmjs.org/semver/-/semver-7.3.8.tgz", + "integrity": "sha512-NB1ctGL5rlHrPJtFDVIVzTyQylMLu9N9VICA6HSFJo8MCGVTMW6gfpicwKmmK/dAjTOrqu5l63JJOpDSrAis3A==", + "dev": true, + "requires": { + "lru-cache": "^6.0.0" + } + }, "yallist": { "version": "4.0.0", "resolved": "https://registry.npmjs.org/yallist/-/yallist-4.0.0.tgz", @@ -13158,15 +13629,6 @@ } } }, - "semver-diff": { - "version": "4.0.0", - "resolved": "https://registry.npmjs.org/semver-diff/-/semver-diff-4.0.0.tgz", - "integrity": "sha512-0Ju4+6A8iOnpL/Thra7dZsSlOHYAHIeMxfhWQRI1/VLcT3WDBZKKtQt/QkBOsiIN9ZpuvHE6cGZ0x4glCMmfiA==", - "dev": true, - "requires": { - "semver": "^7.3.5" - } - }, "serialize-javascript": { "version": "6.0.0", "resolved": "https://registry.npmjs.org/serialize-javascript/-/serialize-javascript-6.0.0.tgz", @@ -13215,14 +13677,14 @@ } }, "shiki": { - "version": "0.11.1", - "resolved": "https://registry.npmjs.org/shiki/-/shiki-0.11.1.tgz", - "integrity": "sha512-EugY9VASFuDqOexOgXR18ZV+TbFrQHeCpEYaXamO+SZlsnT/2LxuLBX25GGtIrwaEVFXUAbUQ601SWE2rMwWHA==", + "version": "0.12.1", + "resolved": "https://registry.npmjs.org/shiki/-/shiki-0.12.1.tgz", + "integrity": "sha512-aieaV1m349rZINEBkjxh2QbBvFFQOlgqYTNtCal82hHj4dDZ76oMlQIX+C7ryerBTDiga3e5NfH6smjdJ02BbQ==", "dev": true, "requires": { - "jsonc-parser": "^3.0.0", - "vscode-oniguruma": "^1.6.1", - "vscode-textmate": "^6.0.0" + "jsonc-parser": "^3.2.0", + "vscode-oniguruma": "^1.7.0", + "vscode-textmate": "^8.0.0" } }, "side-channel": { @@ -13243,16 +13705,16 @@ "dev": true }, "sinon": { - "version": "14.0.1", - "resolved": "https://registry.npmjs.org/sinon/-/sinon-14.0.1.tgz", - "integrity": "sha512-JhJ0jCiyBWVAHDS+YSjgEbDn7Wgz9iIjA1/RK+eseJN0vAAWIWiXBdrnb92ELPyjsfreCYntD1ORtLSfIrlvSQ==", + "version": "15.0.1", + "resolved": "https://registry.npmjs.org/sinon/-/sinon-15.0.1.tgz", + "integrity": "sha512-PZXKc08f/wcA/BMRGBze2Wmw50CWPiAH3E21EOi4B49vJ616vW4DQh4fQrqsYox2aNR/N3kCqLuB0PwwOucQrg==", "dev": true, "requires": { - "@sinonjs/commons": "^1.8.3", - "@sinonjs/fake-timers": "^9.1.2", - "@sinonjs/samsam": "^6.1.1", + "@sinonjs/commons": "^2.0.0", + "@sinonjs/fake-timers": "10.0.2", + "@sinonjs/samsam": "^7.0.1", "diff": "^5.0.0", - "nise": "^5.1.1", + "nise": "^5.1.2", "supports-color": "^7.2.0" } }, @@ -13339,6 +13801,15 @@ "integrity": "sha512-RwNA9Z/7PrK06rYLIzFMlaF+l73iwpzsqRIFgbMLbTcLD6cOao82TaWefPXQvB2fOC4AjuYSEndS7N/mTCbkdQ==", "dev": true }, + "stop-iteration-iterator": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/stop-iteration-iterator/-/stop-iteration-iterator-1.0.0.tgz", + "integrity": "sha512-iCGQj+0l0HOdZ2AEeBADlsRC+vsnDsZsbdSiH1yNSjcfKM7fdpCMfqAL/dwF5BLiw/XhRft/Wax6zQbhq2BcjQ==", + "dev": true, + "requires": { + "internal-slot": "^1.0.4" + } + }, "string_decoder": { "version": "1.3.0", "resolved": "https://registry.npmjs.org/string_decoder/-/string_decoder-1.3.0.tgz", @@ -13357,54 +13828,37 @@ "eastasianwidth": "^0.2.0", "emoji-regex": "^9.2.2", "strip-ansi": "^7.0.1" - }, - "dependencies": { - "ansi-regex": { - "version": "6.0.1", - "resolved": "https://registry.npmjs.org/ansi-regex/-/ansi-regex-6.0.1.tgz", - "integrity": "sha512-n5M855fKb2SsfMIiFFoVrABHJC8QtHwVx+mHWP3QcEqBHYienj5dHSgjbxtC0WEZXYt4wcD6zrQElDPhFuZgfA==", - "dev": true - }, - "strip-ansi": { - "version": "7.0.1", - "resolved": "https://registry.npmjs.org/strip-ansi/-/strip-ansi-7.0.1.tgz", - "integrity": "sha512-cXNxvT8dFNRVfhVME3JAe98mkXDYN2O1l7jmcwMnOslDeESg1rF/OZMtK0nRAhiari1unG5cD4jG3rapUAkLbw==", - "dev": true, - "requires": { - "ansi-regex": "^6.0.1" - } - } } }, "string.prototype.trimend": { - "version": "1.0.5", - "resolved": "https://registry.npmjs.org/string.prototype.trimend/-/string.prototype.trimend-1.0.5.tgz", - "integrity": "sha512-I7RGvmjV4pJ7O3kdf+LXFpVfdNOxtCW/2C8f6jNiW4+PQchwxkCDzlk1/7p+Wl4bqFIZeF47qAHXLuHHWKAxog==", + "version": "1.0.6", + "resolved": "https://registry.npmjs.org/string.prototype.trimend/-/string.prototype.trimend-1.0.6.tgz", + "integrity": "sha512-JySq+4mrPf9EsDBEDYMOb/lM7XQLulwg5R/m1r0PXEFqrV0qHvl58sdTilSXtKOflCsK2E8jxf+GKC0T07RWwQ==", "dev": true, "requires": { "call-bind": "^1.0.2", "define-properties": "^1.1.4", - "es-abstract": "^1.19.5" + "es-abstract": "^1.20.4" } }, "string.prototype.trimstart": { - "version": "1.0.5", - "resolved": "https://registry.npmjs.org/string.prototype.trimstart/-/string.prototype.trimstart-1.0.5.tgz", - "integrity": "sha512-THx16TJCGlsN0o6dl2o6ncWUsdgnLRSA23rRE5pyGBw/mLr3Ej/R2LaqCtgP8VNMGZsvMWnf9ooZPyY2bHvUFg==", + "version": "1.0.6", + "resolved": "https://registry.npmjs.org/string.prototype.trimstart/-/string.prototype.trimstart-1.0.6.tgz", + "integrity": "sha512-omqjMDaY92pbn5HOX7f9IccLA+U1tA9GvtU4JrodiXFfYB7jPzzHpRzpglLAjtUV6bB557zwClJezTqnAiYnQA==", "dev": true, "requires": { "call-bind": "^1.0.2", "define-properties": "^1.1.4", - "es-abstract": "^1.19.5" + "es-abstract": "^1.20.4" } }, "strip-ansi": { - "version": "6.0.1", - "resolved": "https://registry.npmjs.org/strip-ansi/-/strip-ansi-6.0.1.tgz", - "integrity": "sha512-Y38VPSHcqkFrCpFnQ9vuSXmquuv5oXOKpGeT6aGrr3o3Gc9AlVa6JBfUSOCnbxGGZF+/0ooI7KrPuUSztUdU5A==", + "version": "7.0.1", + "resolved": "https://registry.npmjs.org/strip-ansi/-/strip-ansi-7.0.1.tgz", + "integrity": "sha512-cXNxvT8dFNRVfhVME3JAe98mkXDYN2O1l7jmcwMnOslDeESg1rF/OZMtK0nRAhiari1unG5cD4jG3rapUAkLbw==", "dev": true, "requires": { - "ansi-regex": "^5.0.1" + "ansi-regex": "^6.0.1" } }, "strip-bom": { @@ -13563,9 +14017,9 @@ } }, "tslib": { - "version": "2.4.0", - "resolved": "https://registry.npmjs.org/tslib/-/tslib-2.4.0.tgz", - "integrity": "sha512-d6xOpEDfsi2CZVlPQzGeux8XMwLT9hssAsaPYExaQMuYskwb+x1x7J371tWlbBdWHroy99KnVB6qIkUbs5X3UQ==", + "version": "2.4.1", + "resolved": "https://registry.npmjs.org/tslib/-/tslib-2.4.1.tgz", + "integrity": "sha512-tGyy4dAjRIEwI7BzsB0lynWgOpfqjUdq91XXAlIWD2OwKBH7oCl/GZG/HT4BOHrTlPMOASlMQ7veyTqpmRcrNA==", "dev": true }, "tsutils": { @@ -13601,11 +14055,22 @@ "dev": true }, "type-fest": { - "version": "0.20.2", - "resolved": "https://registry.npmjs.org/type-fest/-/type-fest-0.20.2.tgz", - "integrity": "sha512-Ne+eE4r0/iWnpAxD852z3A+N0Bt5RN//NjJwRd2VFHEmrywxf5vsZlh4R6lixl6B+wz/8d+maTSAkN1FIkI3LQ==", + "version": "3.5.2", + "resolved": "https://registry.npmjs.org/type-fest/-/type-fest-3.5.2.tgz", + "integrity": "sha512-Ph7S4EhXzWy0sbljEuZo0tTNoLl+K2tPauGrQpcwUWrOVneLePTuhVzcuzVJJ6RU5DsNwQZka+8YtkXXU4z9cA==", "dev": true }, + "typed-array-length": { + "version": "1.0.4", + "resolved": "https://registry.npmjs.org/typed-array-length/-/typed-array-length-1.0.4.tgz", + "integrity": "sha512-KjZypGq+I/H7HI5HlOoGHkWUUGq+Q0TPhQurLbyrVrvnKTBgzLhIJ7j6J/XTQOi0d1RjyZ0wdas8bKs2p0x3Ng==", + "dev": true, + "requires": { + "call-bind": "^1.0.2", + "for-each": "^0.3.3", + "is-typed-array": "^1.1.9" + } + }, "typedarray-to-buffer": { "version": "3.1.5", "resolved": "https://registry.npmjs.org/typedarray-to-buffer/-/typedarray-to-buffer-3.1.5.tgz", @@ -13616,15 +14081,15 @@ } }, "typedoc": { - "version": "0.23.18", - "resolved": "https://registry.npmjs.org/typedoc/-/typedoc-0.23.18.tgz", - "integrity": "sha512-0Tq/uFkUuWyRYyjOShTkhsOm6u5E8wf0i6L76/k5znEaxvWKHGeT2ywZThGrDrryV/skO/REM824D1gm8ccQuA==", + "version": "0.23.24", + "resolved": "https://registry.npmjs.org/typedoc/-/typedoc-0.23.24.tgz", + "integrity": "sha512-bfmy8lNQh+WrPYcJbtjQ6JEEsVl/ce1ZIXyXhyW+a1vFrjO39t6J8sL/d6FfAGrJTc7McCXgk9AanYBSNvLdIA==", "dev": true, "requires": { "lunr": "^2.3.9", - "marked": "^4.0.19", - "minimatch": "^5.1.0", - "shiki": "^0.11.1" + "marked": "^4.2.5", + "minimatch": "^5.1.2", + "shiki": "^0.12.1" }, "dependencies": { "brace-expansion": { @@ -13637,9 +14102,9 @@ } }, "minimatch": { - "version": "5.1.0", - "resolved": "https://registry.npmjs.org/minimatch/-/minimatch-5.1.0.tgz", - "integrity": "sha512-9TPBGGak4nHfGZsPBohm9AWg6NoT7QTCehS3BIJABslyZbzxfV78QM2Y6+i741OPZIafFAaiiEMh5OyIrJPgtg==", + "version": "5.1.6", + "resolved": "https://registry.npmjs.org/minimatch/-/minimatch-5.1.6.tgz", + "integrity": "sha512-lKwV/1brpG6mBUFHtb7NUmtABCb2WZZmm2wNiOA5hAb8VdCS4B3dtMWyvcoViccwAW/COERjXLt0zP1zXUN26g==", "dev": true, "requires": { "brace-expansion": "^2.0.1" @@ -13648,9 +14113,9 @@ } }, "typescript": { - "version": "4.8.4", - "resolved": "https://registry.npmjs.org/typescript/-/typescript-4.8.4.tgz", - "integrity": "sha512-QCh+85mCy+h0IGff8r5XWzOVSbBO+KfeYrMQh7NJ58QujwcE22u+NUSmUxqF+un70P9GXKxa2HCNiTTMJknyjQ==", + "version": "4.9.4", + "resolved": "https://registry.npmjs.org/typescript/-/typescript-4.9.4.tgz", + "integrity": "sha512-Uz+dTXYzxXXbsFpM86Wh3dKCxrQqUcVMxwU54orwlJjOpO3ao8L7j5lH+dWfTwgCwIuM9GQ2kvVotzYJMXTBZg==", "dev": true }, "unbox-primitive": { @@ -13724,10 +14189,28 @@ "xdg-basedir": "^5.1.0" }, "dependencies": { - "chalk": { - "version": "5.1.2", - "resolved": "https://registry.npmjs.org/chalk/-/chalk-5.1.2.tgz", - "integrity": "sha512-E5CkT4jWURs1Vy5qGJye+XwCkNj7Od3Af7CP6SujMetSMkLs8Do2RWJK5yx1wamHV/op8Rz+9rltjaTQWDnEFQ==", + "lru-cache": { + "version": "6.0.0", + "resolved": "https://registry.npmjs.org/lru-cache/-/lru-cache-6.0.0.tgz", + "integrity": "sha512-Jo6dJ04CmSjuznwJSS3pUeWmd/H0ffTlkXXgwZi+eq1UCmqQwCh+eLsYOYCwY991i2Fah4h1BEMCx4qThGbsiA==", + "dev": true, + "requires": { + "yallist": "^4.0.0" + } + }, + "semver": { + "version": "7.3.8", + "resolved": "https://registry.npmjs.org/semver/-/semver-7.3.8.tgz", + "integrity": "sha512-NB1ctGL5rlHrPJtFDVIVzTyQylMLu9N9VICA6HSFJo8MCGVTMW6gfpicwKmmK/dAjTOrqu5l63JJOpDSrAis3A==", + "dev": true, + "requires": { + "lru-cache": "^6.0.0" + } + }, + "yallist": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/yallist/-/yallist-4.0.0.tgz", + "integrity": "sha512-3wdGidZyq5PB084XLES5TpOSRA3wjXAlIWMhum2kRcv/41Sn2emQ0dycQW4uZXLejwKvg6EsvbdlVL+FYEct7A==", "dev": true } } @@ -13766,9 +14249,9 @@ "dev": true }, "vm2": { - "version": "3.9.11", - "resolved": "https://registry.npmjs.org/vm2/-/vm2-3.9.11.tgz", - "integrity": "sha512-PFG8iJRSjvvBdisowQ7iVF580DXb1uCIiGaXgm7tynMR1uTBlv7UJlB1zdv5KJ+Tmq1f0Upnj3fayoEOPpCBKg==", + "version": "3.9.13", + "resolved": "https://registry.npmjs.org/vm2/-/vm2-3.9.13.tgz", + "integrity": "sha512-0rvxpB8P8Shm4wX2EKOiMp7H2zq+HUE/UwodY0pCZXs9IffIKZq6vUti5OgkVCTakKo9e/fgO4X1fkwfjWxE3Q==", "dev": true, "requires": { "acorn": "^8.7.0", @@ -13776,15 +14259,15 @@ } }, "vscode-oniguruma": { - "version": "1.6.2", - "resolved": "https://registry.npmjs.org/vscode-oniguruma/-/vscode-oniguruma-1.6.2.tgz", - "integrity": "sha512-KH8+KKov5eS/9WhofZR8M8dMHWN2gTxjMsG4jd04YhpbPR91fUj7rYQ2/XjeHCJWbg7X++ApRIU9NUwM2vTvLA==", + "version": "1.7.0", + "resolved": "https://registry.npmjs.org/vscode-oniguruma/-/vscode-oniguruma-1.7.0.tgz", + "integrity": "sha512-L9WMGRfrjOhgHSdOYgCt/yRMsXzLDJSL7BPrOZt73gU0iWO4mpqzqQzOz5srxqTvMBaR0XZTSrVWo4j55Rc6cA==", "dev": true }, "vscode-textmate": { - "version": "6.0.0", - "resolved": "https://registry.npmjs.org/vscode-textmate/-/vscode-textmate-6.0.0.tgz", - "integrity": "sha512-gu73tuZfJgu+mvCSy4UZwd2JXykjK9zAZsfmDeut5dx/1a7FeTk0XwJsSuqQn+cuMCGVbIBfl+s53X4T19DnzQ==", + "version": "8.0.0", + "resolved": "https://registry.npmjs.org/vscode-textmate/-/vscode-textmate-8.0.0.tgz", + "integrity": "sha512-AFbieoL7a5LMqcnOF04ji+rpXadgOXnZsxQr//r83kLPr7biP7am3g9zbaZIaBGwBRWeSvoMD4mgPdX3e4NWBg==", "dev": true }, "wcwidth": { @@ -13846,6 +14329,20 @@ "integrity": "sha512-B+enWhmw6cjfVC7kS8Pj9pCrKSc5txArRyaYGe088shv/FGWH+0Rjx/xPgtsWfsUtS27FkP697E4DDhgrgoc0Q==", "dev": true }, + "which-typed-array": { + "version": "1.1.9", + "resolved": "https://registry.npmjs.org/which-typed-array/-/which-typed-array-1.1.9.tgz", + "integrity": "sha512-w9c4xkx6mPidwp7180ckYWfMmvxpjlZuIudNtDf4N/tTAUB8VJbX25qZoAsrtGuYNnGw3pa0AXgbGKRB8/EceA==", + "dev": true, + "requires": { + "available-typed-arrays": "^1.0.5", + "call-bind": "^1.0.2", + "for-each": "^0.3.3", + "gopd": "^1.0.1", + "has-tostringtag": "^1.0.0", + "is-typed-array": "^1.1.10" + } + }, "widest-line": { "version": "4.0.1", "resolved": "https://registry.npmjs.org/widest-line/-/widest-line-4.0.1.tgz", @@ -13862,9 +14359,9 @@ "dev": true }, "windows-release": { - "version": "5.0.1", - "resolved": "https://registry.npmjs.org/windows-release/-/windows-release-5.0.1.tgz", - "integrity": "sha512-y1xFdFvdMiDXI3xiOhMbJwt1Y7dUxidha0CWPs1NgjZIjZANTcX7+7bMqNjuezhzb8s5JGEiBAbQjQQYYy7ulw==", + "version": "5.1.0", + "resolved": "https://registry.npmjs.org/windows-release/-/windows-release-5.1.0.tgz", + "integrity": "sha512-CddHecz5dt0ngTjGPP1uYr9Tjl4qq5rEKNk8UGb8XCdngNXI+GRYvqelD055FdiUgqODZz3R/5oZWYldPtXQpA==", "dev": true, "requires": { "execa": "^5.1.1" @@ -13893,6 +14390,12 @@ "integrity": "sha512-B4FFZ6q/T2jhhksgkbEW3HBvWIfDW85snkQgawt07S7J5QXTk6BkNV+0yAeZrM5QpMAdYlocGoljn0sJ/WQkFw==", "dev": true }, + "is-stream": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/is-stream/-/is-stream-2.0.1.tgz", + "integrity": "sha512-hFoiJiTl63nn+kstHGBtewWSKnQLpyb155KHheA1l39uvtO9nWIop1p3udqPcUd/xbF1VLMO4n7OI6p7RbngDg==", + "dev": true + }, "mimic-fn": { "version": "2.1.0", "resolved": "https://registry.npmjs.org/mimic-fn/-/mimic-fn-2.1.0.tgz", @@ -13946,29 +14449,6 @@ "ansi-styles": "^6.1.0", "string-width": "^5.0.1", "strip-ansi": "^7.0.1" - }, - "dependencies": { - "ansi-regex": { - "version": "6.0.1", - "resolved": "https://registry.npmjs.org/ansi-regex/-/ansi-regex-6.0.1.tgz", - "integrity": "sha512-n5M855fKb2SsfMIiFFoVrABHJC8QtHwVx+mHWP3QcEqBHYienj5dHSgjbxtC0WEZXYt4wcD6zrQElDPhFuZgfA==", - "dev": true - }, - "ansi-styles": { - "version": "6.2.1", - "resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-6.2.1.tgz", - "integrity": "sha512-bN798gFfQX+viw3R7yrGWRqnrN2oRkEkUjjl4JNn4E8GxxbjtG3FbrEIIY3l8/hrwUwIeCZvi4QuOTP4MErVug==", - "dev": true - }, - "strip-ansi": { - "version": "7.0.1", - "resolved": "https://registry.npmjs.org/strip-ansi/-/strip-ansi-7.0.1.tgz", - "integrity": "sha512-cXNxvT8dFNRVfhVME3JAe98mkXDYN2O1l7jmcwMnOslDeESg1rF/OZMtK0nRAhiari1unG5cD4jG3rapUAkLbw==", - "dev": true, - "requires": { - "ansi-regex": "^6.0.1" - } - } } }, "wrappy": { @@ -14002,9 +14482,9 @@ "dev": true }, "y18n": { - "version": "5.0.8", - "resolved": "https://registry.npmjs.org/y18n/-/y18n-5.0.8.tgz", - "integrity": "sha512-0pfFzegeDWJHJIAmTLRP2DwHjdF5s7jo9tuztdQxAhINCdvS+3nGINqPd00AphqJR/0LhANUS6/+7SCb98YOfA==", + "version": "4.0.3", + "resolved": "https://registry.npmjs.org/y18n/-/y18n-4.0.3.tgz", + "integrity": "sha512-JKhqTOwSrqNA1NY5lSztJ1GrBiUodLMmIZuLiDaMRJ+itFd+ABVE8XBjOvIWL+rSqNDC74LCSFmlb/U4UZ4hJQ==", "dev": true }, "yallist": { @@ -14013,27 +14493,31 @@ "integrity": "sha512-a4UGQaWPH59mOXUYnAG2ewncQS4i4F43Tv3JoAM+s2VDAmS9NsK8GpDMLrCHPksFT7h3K6TOoUNn2pb7RoXx4g==", "dev": true }, - "yaml": { - "version": "1.10.2", - "resolved": "https://registry.npmjs.org/yaml/-/yaml-1.10.2.tgz", - "integrity": "sha512-r3vXyErRCYJ7wg28yvBY5VSoAF8ZvlcW9/BwUzEtUsjvX/DKs24dIkuwjtuprwJJHsbyUbLApepYTR1BN4uHrg==", - "dev": true - }, "yargs": { - "version": "17.6.0", - "resolved": "https://registry.npmjs.org/yargs/-/yargs-17.6.0.tgz", - "integrity": "sha512-8H/wTDqlSwoSnScvV2N/JHfLWOKuh5MVla9hqLjK3nsfyy6Y4kDSYSvkU5YCUEPOSnRXfIyx3Sq+B/IWudTo4g==", + "version": "15.4.1", + "resolved": "https://registry.npmjs.org/yargs/-/yargs-15.4.1.tgz", + "integrity": "sha512-aePbxDmcYW++PaqBsJ+HYUFwCdv4LVvdnhBy78E57PIor8/OVvhMrADFFEDh8DHDFRv/O9i3lPhsENjO7QX0+A==", "dev": true, "requires": { - "cliui": "^8.0.1", - "escalade": "^3.1.1", - "get-caller-file": "^2.0.5", + "cliui": "^6.0.0", + "decamelize": "^1.2.0", + "find-up": "^4.1.0", + "get-caller-file": "^2.0.1", "require-directory": "^2.1.1", - "string-width": "^4.2.3", - "y18n": "^5.0.5", - "yargs-parser": "^21.0.0" + "require-main-filename": "^2.0.0", + "set-blocking": "^2.0.0", + "string-width": "^4.2.0", + "which-module": "^2.0.0", + "y18n": "^4.0.0", + "yargs-parser": "^18.1.2" }, "dependencies": { + "ansi-regex": { + "version": "5.0.1", + "resolved": "https://registry.npmjs.org/ansi-regex/-/ansi-regex-5.0.1.tgz", + "integrity": "sha512-quJQXlTSUGL2LH9SUXo8VwsY4soanhgo6LNSm84E1LBcE8s3O0wpdiRzyR9z/ZZJMlMWv37qOOb9pdJlMUEKFQ==", + "dev": true + }, "emoji-regex": { "version": "8.0.0", "resolved": "https://registry.npmjs.org/emoji-regex/-/emoji-regex-8.0.0.tgz", @@ -14051,18 +14535,31 @@ "strip-ansi": "^6.0.1" } }, + "strip-ansi": { + "version": "6.0.1", + "resolved": "https://registry.npmjs.org/strip-ansi/-/strip-ansi-6.0.1.tgz", + "integrity": "sha512-Y38VPSHcqkFrCpFnQ9vuSXmquuv5oXOKpGeT6aGrr3o3Gc9AlVa6JBfUSOCnbxGGZF+/0ooI7KrPuUSztUdU5A==", + "dev": true, + "requires": { + "ansi-regex": "^5.0.1" + } + }, "yargs-parser": { - "version": "21.1.1", - "resolved": "https://registry.npmjs.org/yargs-parser/-/yargs-parser-21.1.1.tgz", - "integrity": "sha512-tVpsJW7DdjecAiFpbIB1e3qxIQsE6NoPc5/eTdrbbIC4h0LVsWhnoa3g+m2HclBIujHzsxZ4VJVA+GUuc2/LBw==", - "dev": true + "version": "18.1.3", + "resolved": "https://registry.npmjs.org/yargs-parser/-/yargs-parser-18.1.3.tgz", + "integrity": "sha512-o50j0JeToy/4K6OZcaQmW6lyXXKhq7csREXcDwk2omFPJEwUNOVtJKvmDr9EI1fAJZUyZcRF7kxGBWmRXudrCQ==", + "dev": true, + "requires": { + "camelcase": "^5.0.0", + "decamelize": "^1.2.0" + } } } }, "yargs-parser": { - "version": "20.2.4", - "resolved": "https://registry.npmjs.org/yargs-parser/-/yargs-parser-20.2.4.tgz", - "integrity": "sha512-WOkpgNhPTlE73h4VFAFsOnomJVaovO8VqLDzy5saChRBFQFBoMYirowyW+Q9HB4HFF4Z7VZTiG3iSzJJA29yRA==", + "version": "21.1.1", + "resolved": "https://registry.npmjs.org/yargs-parser/-/yargs-parser-21.1.1.tgz", + "integrity": "sha512-tVpsJW7DdjecAiFpbIB1e3qxIQsE6NoPc5/eTdrbbIC4h0LVsWhnoa3g+m2HclBIujHzsxZ4VJVA+GUuc2/LBw==", "dev": true }, "yargs-unparser": { diff --git a/package.json b/package.json index a5a5cbe99fc..8f87eb64ef7 100644 --- a/package.json +++ b/package.json @@ -32,9 +32,9 @@ }, "devDependencies": { "@tsconfig/node14": "^1.0.3", - "gh-pages": "^4.0.0", - "release-it": "^15.3.0", - "typescript": "^4.7.4" + "gh-pages": "^5.0.0", + "release-it": "^15.6.0", + "typescript": "^4.9.4" }, "repository": { "type": "git", diff --git a/packages/bloom/package.json b/packages/bloom/package.json index ce79d3ccf7a..15a1ecd76b2 100644 --- a/packages/bloom/package.json +++ b/packages/bloom/package.json @@ -18,12 +18,12 @@ "devDependencies": { "@istanbuljs/nyc-config-typescript": "^1.0.2", "@redis/test-utils": "*", - "@types/node": "^18.11.6", + "@types/node": "^18.11.18", "nyc": "^15.1.0", - "release-it": "^15.5.0", + "release-it": "^15.6.0", "source-map-support": "^0.5.21", "ts-node": "^10.9.1", - "typedoc": "^0.23.18", - "typescript": "^4.8.4" + "typedoc": "^0.23.24", + "typescript": "^4.9.4" } } diff --git a/packages/client/package.json b/packages/client/package.json index 42a512b644c..113bf570f1f 100644 --- a/packages/client/package.json +++ b/packages/client/package.json @@ -14,26 +14,26 @@ "documentation": "typedoc" }, "dependencies": { - "cluster-key-slot": "1.1.1", + "cluster-key-slot": "1.1.2", "generic-pool": "3.9.0", "yallist": "4.0.0" }, "devDependencies": { "@istanbuljs/nyc-config-typescript": "^1.0.2", "@redis/test-utils": "*", - "@types/node": "^18.11.6", + "@types/node": "^18.11.18", "@types/sinon": "^10.0.13", "@types/yallist": "^4.0.1", - "@typescript-eslint/eslint-plugin": "^5.41.0", - "@typescript-eslint/parser": "^5.41.0", - "eslint": "^8.26.0", + "@typescript-eslint/eslint-plugin": "^5.49.0", + "@typescript-eslint/parser": "^5.49.0", + "eslint": "^8.32.0", "nyc": "^15.1.0", - "release-it": "^15.5.0", - "sinon": "^14.0.1", + "release-it": "^15.6.0", + "sinon": "^15.0.1", "source-map-support": "^0.5.21", "ts-node": "^10.9.1", - "typedoc": "^0.23.18", - "typescript": "^4.8.4" + "typedoc": "^0.23.24", + "typescript": "^4.9.4" }, "engines": { "node": ">=14" diff --git a/packages/graph/package.json b/packages/graph/package.json index 36697ec3c84..418d48f0223 100644 --- a/packages/graph/package.json +++ b/packages/graph/package.json @@ -18,12 +18,12 @@ "devDependencies": { "@istanbuljs/nyc-config-typescript": "^1.0.2", "@redis/test-utils": "*", - "@types/node": "^18.11.6", + "@types/node": "^18.11.18", "nyc": "^15.1.0", - "release-it": "^15.5.0", + "release-it": "^15.6.0", "source-map-support": "^0.5.21", "ts-node": "^10.9.1", - "typedoc": "^0.23.18", - "typescript": "^4.8.4" + "typedoc": "^0.23.24", + "typescript": "^4.9.4" } } diff --git a/packages/json/package.json b/packages/json/package.json index 11dd7cae780..b7a6fac35d7 100644 --- a/packages/json/package.json +++ b/packages/json/package.json @@ -18,12 +18,12 @@ "devDependencies": { "@istanbuljs/nyc-config-typescript": "^1.0.2", "@redis/test-utils": "*", - "@types/node": "^18.11.6", + "@types/node": "^18.11.18", "nyc": "^15.1.0", - "release-it": "^15.5.0", + "release-it": "^15.6.0", "source-map-support": "^0.5.21", "ts-node": "^10.9.1", - "typedoc": "^0.23.18", - "typescript": "^4.8.4" + "typedoc": "^0.23.24", + "typescript": "^4.9.4" } } diff --git a/packages/search/package.json b/packages/search/package.json index 64591968715..1658f530e50 100644 --- a/packages/search/package.json +++ b/packages/search/package.json @@ -18,12 +18,12 @@ "devDependencies": { "@istanbuljs/nyc-config-typescript": "^1.0.2", "@redis/test-utils": "*", - "@types/node": "^18.11.6", + "@types/node": "^18.11.18", "nyc": "^15.1.0", - "release-it": "^15.5.0", + "release-it": "^15.6.0", "source-map-support": "^0.5.21", "ts-node": "^10.9.1", - "typedoc": "^0.23.18", - "typescript": "^4.8.4" + "typedoc": "^0.23.24", + "typescript": "^4.9.4" } } diff --git a/packages/test-utils/package.json b/packages/test-utils/package.json index 4549aa71a81..71fe1b33457 100644 --- a/packages/test-utils/package.json +++ b/packages/test-utils/package.json @@ -11,14 +11,14 @@ }, "devDependencies": { "@istanbuljs/nyc-config-typescript": "^1.0.2", - "@types/mocha": "^10.0.0", - "@types/node": "^18.11.6", - "@types/yargs": "^17.0.13", - "mocha": "^10.1.0", + "@types/mocha": "^10.0.1", + "@types/node": "^18.11.18", + "@types/yargs": "^17.0.20", + "mocha": "^10.2.0", "nyc": "^15.1.0", "source-map-support": "^0.5.21", "ts-node": "^10.9.1", - "typescript": "^4.8.4", - "yargs": "^17.6.0" + "typescript": "^4.9.4", + "yargs": "^17.6.2" } } diff --git a/packages/time-series/package.json b/packages/time-series/package.json index 8f0530fb486..26db747a2a4 100644 --- a/packages/time-series/package.json +++ b/packages/time-series/package.json @@ -18,12 +18,12 @@ "devDependencies": { "@istanbuljs/nyc-config-typescript": "^1.0.2", "@redis/test-utils": "*", - "@types/node": "^18.11.6", + "@types/node": "^18.11.18", "nyc": "^15.1.0", - "release-it": "^15.5.0", + "release-it": "^15.6.0", "source-map-support": "^0.5.21", "ts-node": "^10.9.1", - "typedoc": "^0.23.18", - "typescript": "^4.8.4" + "typedoc": "^0.23.24", + "typescript": "^4.9.4" } } From b06f92ae009b4c54cd362420e2345fbdf6891a68 Mon Sep 17 00:00:00 2001 From: Leibale Date: Wed, 25 Jan 2023 13:15:45 -0500 Subject: [PATCH 1315/1748] Release bloom@1.2.0 --- packages/bloom/package.json | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/packages/bloom/package.json b/packages/bloom/package.json index 15a1ecd76b2..d6d5e4778eb 100644 --- a/packages/bloom/package.json +++ b/packages/bloom/package.json @@ -1,6 +1,6 @@ { "name": "@redis/bloom", - "version": "1.1.0", + "version": "1.2.0", "license": "MIT", "main": "./dist/index.js", "types": "./dist/index.d.ts", From f3ed246ff8e877830d197304717aa174d1932d65 Mon Sep 17 00:00:00 2001 From: Leibale Date: Wed, 25 Jan 2023 13:19:21 -0500 Subject: [PATCH 1316/1748] Release search@1.1.1 --- packages/search/package.json | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/packages/search/package.json b/packages/search/package.json index 1658f530e50..896b7746cca 100644 --- a/packages/search/package.json +++ b/packages/search/package.json @@ -1,6 +1,6 @@ { "name": "@redis/search", - "version": "1.1.0", + "version": "1.1.1", "license": "MIT", "main": "./dist/index.js", "types": "./dist/index.d.ts", From e9de74c96f45e83ff8eab5d55d84177959b79c1b Mon Sep 17 00:00:00 2001 From: Leibale Date: Wed, 25 Jan 2023 13:23:28 -0500 Subject: [PATCH 1317/1748] revert client version to 1.4.2 --- packages/client/package.json | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/packages/client/package.json b/packages/client/package.json index 113bf570f1f..f9e800fce00 100644 --- a/packages/client/package.json +++ b/packages/client/package.json @@ -1,6 +1,6 @@ { "name": "@redis/client", - "version": "1.5.0", + "version": "1.4.2", "license": "MIT", "main": "./dist/index.js", "types": "./dist/index.d.ts", From 4904fe4a5d71cc032b0abee107e028d5500e6e82 Mon Sep 17 00:00:00 2001 From: Leibale Date: Wed, 25 Jan 2023 13:23:58 -0500 Subject: [PATCH 1318/1748] Release client@1.5.0 --- packages/client/package.json | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/packages/client/package.json b/packages/client/package.json index f9e800fce00..113bf570f1f 100644 --- a/packages/client/package.json +++ b/packages/client/package.json @@ -1,6 +1,6 @@ { "name": "@redis/client", - "version": "1.4.2", + "version": "1.5.0", "license": "MIT", "main": "./dist/index.js", "types": "./dist/index.d.ts", From d3c5019e3c7da692d2043aee0779694fc3b6c5d3 Mon Sep 17 00:00:00 2001 From: Leibale Date: Wed, 25 Jan 2023 13:30:15 -0500 Subject: [PATCH 1319/1748] upgrade subpackages --- package-lock.json | 6305 +-------------------------------------------- package.json | 6 +- 2 files changed, 106 insertions(+), 6205 deletions(-) diff --git a/package-lock.json b/package-lock.json index 4e7db229b8d..175818b347b 100644 --- a/package-lock.json +++ b/package-lock.json @@ -1,7 +1,7 @@ { "name": "redis", "version": "4.5.1", - "lockfileVersion": 2, + "lockfileVersion": 3, "requires": true, "packages": { "": { @@ -12,11 +12,11 @@ "./packages/*" ], "dependencies": { - "@redis/bloom": "1.1.0", - "@redis/client": "1.4.2", + "@redis/bloom": "1.2.0", + "@redis/client": "1.5.0", "@redis/graph": "1.1.0", "@redis/json": "1.0.4", - "@redis/search": "1.1.0", + "@redis/search": "1.1.1", "@redis/time-series": "1.0.4" }, "devDependencies": { @@ -254,13 +254,13 @@ } }, "node_modules/@babel/helpers": { - "version": "7.20.7", - "resolved": "https://registry.npmjs.org/@babel/helpers/-/helpers-7.20.7.tgz", - "integrity": "sha512-PBPjs5BppzsGaxHQCDKnZ6Gd9s6xl8bBCluz3vEInLGRJmnZan4F6BYCeqtyXqkk4W5IlPmjK4JlOuZkpJ3xZA==", + "version": "7.20.13", + "resolved": "https://registry.npmjs.org/@babel/helpers/-/helpers-7.20.13.tgz", + "integrity": "sha512-nzJ0DWCL3gB5RCXbUO3KIMMsBY2Eqbx8mBpKGE/02PgyRQFcPQLbkQ1vyy596mZLaP+dAfD+R4ckASzNVmW3jg==", "dev": true, "dependencies": { "@babel/template": "^7.20.7", - "@babel/traverse": "^7.20.7", + "@babel/traverse": "^7.20.13", "@babel/types": "^7.20.7" }, "engines": { @@ -353,9 +353,9 @@ } }, "node_modules/@babel/parser": { - "version": "7.20.7", - "resolved": "https://registry.npmjs.org/@babel/parser/-/parser-7.20.7.tgz", - "integrity": "sha512-T3Z9oHybU+0vZlY9CiDSJQTD5ZapcW18ZctFMi0MOAl/4BjFF4ul7NVSARLdbGO5vDqy9eQiGTV0LtKfvCYvcg==", + "version": "7.20.13", + "resolved": "https://registry.npmjs.org/@babel/parser/-/parser-7.20.13.tgz", + "integrity": "sha512-gFDLKMfpiXCsjt4za2JA9oTMn70CeseCehb11kRZgvd7+F67Hih3OHOK24cRrWECJ/ljfPGac6ygXAs/C8kIvw==", "dev": true, "bin": { "parser": "bin/babel-parser.js" @@ -379,9 +379,9 @@ } }, "node_modules/@babel/traverse": { - "version": "7.20.12", - "resolved": "https://registry.npmjs.org/@babel/traverse/-/traverse-7.20.12.tgz", - "integrity": "sha512-MsIbFN0u+raeja38qboyF8TIT7K0BFzz/Yd/77ta4MsUsmP2RAnidIlwq7d5HFQrH/OZJecGV6B71C4zAgpoSQ==", + "version": "7.20.13", + "resolved": "https://registry.npmjs.org/@babel/traverse/-/traverse-7.20.13.tgz", + "integrity": "sha512-kMJXfF0T6DIS9E8cgdLCSAL+cuCK+YEZHWiLK0SXpTo8YRj5lpJu3CDNKiIBCne4m9hhTIqUg6SYTAI39tAiVQ==", "dev": true, "dependencies": { "@babel/code-frame": "^7.18.6", @@ -390,7 +390,7 @@ "@babel/helper-function-name": "^7.19.0", "@babel/helper-hoist-variables": "^7.18.6", "@babel/helper-split-export-declaration": "^7.18.6", - "@babel/parser": "^7.20.7", + "@babel/parser": "^7.20.13", "@babel/types": "^7.20.7", "debug": "^4.1.0", "globals": "^11.1.0" @@ -660,28 +660,28 @@ } }, "node_modules/@octokit/auth-token": { - "version": "3.0.2", - "resolved": "https://registry.npmjs.org/@octokit/auth-token/-/auth-token-3.0.2.tgz", - "integrity": "sha512-pq7CwIMV1kmzkFTimdwjAINCXKTajZErLB4wMLYapR2nuB/Jpr66+05wOTZMSCBXP6n4DdDWT2W19Bm17vU69Q==", + "version": "3.0.3", + "resolved": "https://registry.npmjs.org/@octokit/auth-token/-/auth-token-3.0.3.tgz", + "integrity": "sha512-/aFM2M4HVDBT/jjDBa84sJniv1t9Gm/rLkalaz9htOm+L+8JMj1k9w0CkUdcxNyNxZPlTxKPVko+m1VlM58ZVA==", "dev": true, "dependencies": { - "@octokit/types": "^8.0.0" + "@octokit/types": "^9.0.0" }, "engines": { "node": ">= 14" } }, "node_modules/@octokit/core": { - "version": "4.1.0", - "resolved": "https://registry.npmjs.org/@octokit/core/-/core-4.1.0.tgz", - "integrity": "sha512-Czz/59VefU+kKDy+ZfDwtOIYIkFjExOKf+HA92aiTZJ6EfWpFzYQWw0l54ji8bVmyhc+mGaLUbSUmXazG7z5OQ==", + "version": "4.2.0", + "resolved": "https://registry.npmjs.org/@octokit/core/-/core-4.2.0.tgz", + "integrity": "sha512-AgvDRUg3COpR82P7PBdGZF/NNqGmtMq2NiPqeSsDIeCfYFOZ9gddqWNQHnFdEUf+YwOj4aZYmJnlPp7OXmDIDg==", "dev": true, "dependencies": { "@octokit/auth-token": "^3.0.0", "@octokit/graphql": "^5.0.0", "@octokit/request": "^6.0.0", "@octokit/request-error": "^3.0.0", - "@octokit/types": "^8.0.0", + "@octokit/types": "^9.0.0", "before-after-hook": "^2.2.0", "universal-user-agent": "^6.0.0" }, @@ -690,12 +690,12 @@ } }, "node_modules/@octokit/endpoint": { - "version": "7.0.3", - "resolved": "https://registry.npmjs.org/@octokit/endpoint/-/endpoint-7.0.3.tgz", - "integrity": "sha512-57gRlb28bwTsdNXq+O3JTQ7ERmBTuik9+LelgcLIVfYwf235VHbN9QNo4kXExtp/h8T423cR5iJThKtFYxC7Lw==", + "version": "7.0.5", + "resolved": "https://registry.npmjs.org/@octokit/endpoint/-/endpoint-7.0.5.tgz", + "integrity": "sha512-LG4o4HMY1Xoaec87IqQ41TQ+glvIeTKqfjkCEmt5AIwDZJwQeVZFIEYXrYY6yLwK+pAScb9Gj4q+Nz2qSw1roA==", "dev": true, "dependencies": { - "@octokit/types": "^8.0.0", + "@octokit/types": "^9.0.0", "is-plain-object": "^5.0.0", "universal-user-agent": "^6.0.0" }, @@ -704,13 +704,13 @@ } }, "node_modules/@octokit/graphql": { - "version": "5.0.4", - "resolved": "https://registry.npmjs.org/@octokit/graphql/-/graphql-5.0.4.tgz", - "integrity": "sha512-amO1M5QUQgYQo09aStR/XO7KAl13xpigcy/kI8/N1PnZYSS69fgte+xA4+c2DISKqUZfsh0wwjc2FaCt99L41A==", + "version": "5.0.5", + "resolved": "https://registry.npmjs.org/@octokit/graphql/-/graphql-5.0.5.tgz", + "integrity": "sha512-Qwfvh3xdqKtIznjX9lz2D458r7dJPP8l6r4GQkIdWQouZwHQK0mVT88uwiU2bdTU2OtT1uOlKpRciUWldpG0yQ==", "dev": true, "dependencies": { "@octokit/request": "^6.0.0", - "@octokit/types": "^8.0.0", + "@octokit/types": "^9.0.0", "universal-user-agent": "^6.0.0" }, "engines": { @@ -718,9 +718,9 @@ } }, "node_modules/@octokit/openapi-types": { - "version": "14.0.0", - "resolved": "https://registry.npmjs.org/@octokit/openapi-types/-/openapi-types-14.0.0.tgz", - "integrity": "sha512-HNWisMYlR8VCnNurDU6os2ikx0s0VyEjDYHNS/h4cgb8DeOxQ0n72HyinUtdDVxJhFy3FWLGl0DJhfEWk3P5Iw==", + "version": "16.0.0", + "resolved": "https://registry.npmjs.org/@octokit/openapi-types/-/openapi-types-16.0.0.tgz", + "integrity": "sha512-JbFWOqTJVLHZSUUoF4FzAZKYtqdxWu9Z5m2QQnOyEa04fOFljvyh7D3GYKbfuaSWisqehImiVIMG4eyJeP5VEA==", "dev": true }, "node_modules/@octokit/plugin-paginate-rest": { @@ -738,6 +738,21 @@ "@octokit/core": ">=4" } }, + "node_modules/@octokit/plugin-paginate-rest/node_modules/@octokit/openapi-types": { + "version": "14.0.0", + "resolved": "https://registry.npmjs.org/@octokit/openapi-types/-/openapi-types-14.0.0.tgz", + "integrity": "sha512-HNWisMYlR8VCnNurDU6os2ikx0s0VyEjDYHNS/h4cgb8DeOxQ0n72HyinUtdDVxJhFy3FWLGl0DJhfEWk3P5Iw==", + "dev": true + }, + "node_modules/@octokit/plugin-paginate-rest/node_modules/@octokit/types": { + "version": "8.2.1", + "resolved": "https://registry.npmjs.org/@octokit/types/-/types-8.2.1.tgz", + "integrity": "sha512-8oWMUji8be66q2B9PmEIUyQm00VPDPun07umUWSaCwxmeaquFBro4Hcc3ruVoDo3zkQyZBlRvhIMEYS3pBhanw==", + "dev": true, + "dependencies": { + "@octokit/openapi-types": "^14.0.0" + } + }, "node_modules/@octokit/plugin-request-log": { "version": "1.0.4", "resolved": "https://registry.npmjs.org/@octokit/plugin-request-log/-/plugin-request-log-1.0.4.tgz", @@ -748,12 +763,12 @@ } }, "node_modules/@octokit/plugin-rest-endpoint-methods": { - "version": "6.7.0", - "resolved": "https://registry.npmjs.org/@octokit/plugin-rest-endpoint-methods/-/plugin-rest-endpoint-methods-6.7.0.tgz", - "integrity": "sha512-orxQ0fAHA7IpYhG2flD2AygztPlGYNAdlzYz8yrD8NDgelPfOYoRPROfEyIe035PlxvbYrgkfUZIhSBKju/Cvw==", + "version": "6.8.1", + "resolved": "https://registry.npmjs.org/@octokit/plugin-rest-endpoint-methods/-/plugin-rest-endpoint-methods-6.8.1.tgz", + "integrity": "sha512-QrlaTm8Lyc/TbU7BL/8bO49vp+RZ6W3McxxmmQTgYxf2sWkO8ZKuj4dLhPNJD6VCUW1hetCmeIM0m6FTVpDiEg==", "dev": true, "dependencies": { - "@octokit/types": "^8.0.0", + "@octokit/types": "^8.1.1", "deprecation": "^2.3.1" }, "engines": { @@ -763,15 +778,30 @@ "@octokit/core": ">=3" } }, + "node_modules/@octokit/plugin-rest-endpoint-methods/node_modules/@octokit/openapi-types": { + "version": "14.0.0", + "resolved": "https://registry.npmjs.org/@octokit/openapi-types/-/openapi-types-14.0.0.tgz", + "integrity": "sha512-HNWisMYlR8VCnNurDU6os2ikx0s0VyEjDYHNS/h4cgb8DeOxQ0n72HyinUtdDVxJhFy3FWLGl0DJhfEWk3P5Iw==", + "dev": true + }, + "node_modules/@octokit/plugin-rest-endpoint-methods/node_modules/@octokit/types": { + "version": "8.2.1", + "resolved": "https://registry.npmjs.org/@octokit/types/-/types-8.2.1.tgz", + "integrity": "sha512-8oWMUji8be66q2B9PmEIUyQm00VPDPun07umUWSaCwxmeaquFBro4Hcc3ruVoDo3zkQyZBlRvhIMEYS3pBhanw==", + "dev": true, + "dependencies": { + "@octokit/openapi-types": "^14.0.0" + } + }, "node_modules/@octokit/request": { - "version": "6.2.2", - "resolved": "https://registry.npmjs.org/@octokit/request/-/request-6.2.2.tgz", - "integrity": "sha512-6VDqgj0HMc2FUX2awIs+sM6OwLgwHvAi4KCK3mT2H2IKRt6oH9d0fej5LluF5mck1lRR/rFWN0YIDSYXYSylbw==", + "version": "6.2.3", + "resolved": "https://registry.npmjs.org/@octokit/request/-/request-6.2.3.tgz", + "integrity": "sha512-TNAodj5yNzrrZ/VxP+H5HiYaZep0H3GU0O7PaF+fhDrt8FPrnkei9Aal/txsN/1P7V3CPiThG0tIvpPDYUsyAA==", "dev": true, "dependencies": { "@octokit/endpoint": "^7.0.0", "@octokit/request-error": "^3.0.0", - "@octokit/types": "^8.0.0", + "@octokit/types": "^9.0.0", "is-plain-object": "^5.0.0", "node-fetch": "^2.6.7", "universal-user-agent": "^6.0.0" @@ -781,12 +811,12 @@ } }, "node_modules/@octokit/request-error": { - "version": "3.0.2", - "resolved": "https://registry.npmjs.org/@octokit/request-error/-/request-error-3.0.2.tgz", - "integrity": "sha512-WMNOFYrSaX8zXWoJg9u/pKgWPo94JXilMLb2VManNOby9EZxrQaBe/QSC4a1TzpAlpxofg2X/jMnCyZgL6y7eg==", + "version": "3.0.3", + "resolved": "https://registry.npmjs.org/@octokit/request-error/-/request-error-3.0.3.tgz", + "integrity": "sha512-crqw3V5Iy2uOU5Np+8M/YexTlT8zxCfI+qu+LxUB7SZpje4Qmx3mub5DfEKSO8Ylyk0aogi6TYdf6kxzh2BguQ==", "dev": true, "dependencies": { - "@octokit/types": "^8.0.0", + "@octokit/types": "^9.0.0", "deprecation": "^2.0.0", "once": "^1.4.0" }, @@ -830,12 +860,12 @@ } }, "node_modules/@octokit/types": { - "version": "8.1.1", - "resolved": "https://registry.npmjs.org/@octokit/types/-/types-8.1.1.tgz", - "integrity": "sha512-7tjk+6DyhYAmei8FOEwPfGKc0VE1x56CKPJ+eE44zhDbOyMT+9yan8apfQFxo8oEFsy+0O7PiBtH8w0Yo0Y9Kw==", + "version": "9.0.0", + "resolved": "https://registry.npmjs.org/@octokit/types/-/types-9.0.0.tgz", + "integrity": "sha512-LUewfj94xCMH2rbD5YJ+6AQ4AVjFYTgpp6rboWM5T7N3IsIF65SBEOVcYMGAEzO/kKNiNaW4LoWtoThOhH06gw==", "dev": true, "dependencies": { - "@octokit/openapi-types": "^14.0.0" + "@octokit/openapi-types": "^16.0.0" } }, "node_modules/@pnpm/network.ca-file": { @@ -1365,9 +1395,9 @@ } }, "node_modules/acorn": { - "version": "8.8.1", - "resolved": "https://registry.npmjs.org/acorn/-/acorn-8.8.1.tgz", - "integrity": "sha512-7zFpHzhnqYKrkYdUjF1HI1bzd0VygEGX8lFk4k5zVMqHEoES+P+7TKI+EvLO9WVMJ8eekdO0aDEK044xTXwPPA==", + "version": "8.8.2", + "resolved": "https://registry.npmjs.org/acorn/-/acorn-8.8.2.tgz", + "integrity": "sha512-xjIYgE8HBrkpd/sJqOGNspf8uHG+NOHGOw6a/Urj8taM2EXfdNAH2oFcPeIFfsv3+kz/mJrS5VuMqbNLjCa2vw==", "dev": true, "bin": { "acorn": "bin/acorn" @@ -1922,9 +1952,9 @@ } }, "node_modules/caniuse-lite": { - "version": "1.0.30001445", - "resolved": "https://registry.npmjs.org/caniuse-lite/-/caniuse-lite-1.0.30001445.tgz", - "integrity": "sha512-8sdQIdMztYmzfTMO6KfLny878Ln9c2M0fc7EH60IjlP4Dc4PiCy7K2Vl3ITmWgOyPgVQKa5x+UP/KqFsxj4mBg==", + "version": "1.0.30001448", + "resolved": "https://registry.npmjs.org/caniuse-lite/-/caniuse-lite-1.0.30001448.tgz", + "integrity": "sha512-tq2YI+MJnooG96XpbTRYkBxLxklZPOdLmNIOdIhvf7SNJan6u5vCKum8iT7ZfCt70m1GPkuC7P3TtX6UuhupuA==", "dev": true, "funding": [ { @@ -3558,9 +3588,9 @@ } }, "node_modules/get-intrinsic": { - "version": "1.1.3", - "resolved": "https://registry.npmjs.org/get-intrinsic/-/get-intrinsic-1.1.3.tgz", - "integrity": "sha512-QJVz1Tj7MS099PevUG5jvnt9tSkXN8K14dxQlikJuPt4uD9hHAHjLyLBiLR5zELelBdD9QNRAXZzsJx0WaDL9A==", + "version": "1.2.0", + "resolved": "https://registry.npmjs.org/get-intrinsic/-/get-intrinsic-1.2.0.tgz", + "integrity": "sha512-L049y6nFOuom5wGyRc3/gdTLO94dySVKRACj1RmJZBQXlbTMhtNIgkWkUHq+jYmZvKf14EW1EoJnnjbmoHij0Q==", "dev": true, "dependencies": { "function-bind": "^1.1.1", @@ -4798,9 +4828,9 @@ } }, "node_modules/js-sdsl": { - "version": "4.2.0", - "resolved": "https://registry.npmjs.org/js-sdsl/-/js-sdsl-4.2.0.tgz", - "integrity": "sha512-dyBIzQBDkCqCu+0upx25Y2jGdbTGxE9fshMsCdK0ViOongpV+n5tXRcZY9v7CaVQ79AGS9KA1KHtojxiM7aXSQ==", + "version": "4.3.0", + "resolved": "https://registry.npmjs.org/js-sdsl/-/js-sdsl-4.3.0.tgz", + "integrity": "sha512-mifzlm2+5nZ+lEcLJMoBK0/IH/bDg8XnJfd/Wq6IP+xoCjLZsTOnV2QpxlVbX9bMnkl5PdEjNtBJ9Cj1NjifhQ==", "dev": true, "funding": { "type": "opencollective", @@ -6308,9 +6338,9 @@ "dev": true }, "node_modules/punycode": { - "version": "2.2.0", - "resolved": "https://registry.npmjs.org/punycode/-/punycode-2.2.0.tgz", - "integrity": "sha512-LN6QV1IJ9ZhxWTNdktaPClrNfp8xdSAYS0Zk2ddX7XsXZAxckMHPCBcHRo0cTcEIgYPRiGEkmji3Idkh2yFtYw==", + "version": "2.3.0", + "resolved": "https://registry.npmjs.org/punycode/-/punycode-2.3.0.tgz", + "integrity": "sha512-rRV+zQD8tVFys26lAGR9WUuS4iUAngJScM+ZRSKtvl5tKeZ2t5bvdNFdNHBW9FWR4guGHlgmsZ1G7BSm2wTbuA==", "dev": true, "engines": { "node": ">=6" @@ -7468,9 +7498,9 @@ } }, "node_modules/type-fest": { - "version": "3.5.2", - "resolved": "https://registry.npmjs.org/type-fest/-/type-fest-3.5.2.tgz", - "integrity": "sha512-Ph7S4EhXzWy0sbljEuZo0tTNoLl+K2tPauGrQpcwUWrOVneLePTuhVzcuzVJJ6RU5DsNwQZka+8YtkXXU4z9cA==", + "version": "3.5.3", + "resolved": "https://registry.npmjs.org/type-fest/-/type-fest-3.5.3.tgz", + "integrity": "sha512-V2+og4j/rWReWvaFrse3s9g2xvUv/K9Azm/xo6CjIuq7oeGqsoimC7+9/A3tfvNcbQf8RPSVj/HV81fB4DJrjA==", "dev": true, "engines": { "node": ">=14.16" @@ -7997,9 +8027,9 @@ "dev": true }, "node_modules/wrap-ansi": { - "version": "8.0.1", - "resolved": "https://registry.npmjs.org/wrap-ansi/-/wrap-ansi-8.0.1.tgz", - "integrity": "sha512-QFF+ufAqhoYHvoHdajT/Po7KoXVBPXS2bgjIam5isfWJPfIOnQZ50JtUiVvCv/sjgacf3yRrt2ZKUZ/V4itN4g==", + "version": "8.1.0", + "resolved": "https://registry.npmjs.org/wrap-ansi/-/wrap-ansi-8.1.0.tgz", + "integrity": "sha512-si7QWI6zUMq56bESFvagtmzMdGOtoxfR+Sez11Mobfc7tm+VkUckk9bW2UeffTGVUbOksxmSw0AA2gs8g71NCQ==", "dev": true, "dependencies": { "ansi-styles": "^6.1.0", @@ -8211,7 +8241,7 @@ }, "packages/bloom": { "name": "@redis/bloom", - "version": "1.1.0", + "version": "1.2.0", "license": "MIT", "devDependencies": { "@istanbuljs/nyc-config-typescript": "^1.0.2", @@ -8303,7 +8333,7 @@ }, "packages/search": { "name": "@redis/search", - "version": "1.1.0", + "version": "1.1.1", "license": "MIT", "devDependencies": { "@istanbuljs/nyc-config-typescript": "^1.0.2", @@ -8471,6134 +8501,5 @@ "@redis/client": "^1.0.0" } } - }, - "dependencies": { - "@ampproject/remapping": { - "version": "2.2.0", - "resolved": "https://registry.npmjs.org/@ampproject/remapping/-/remapping-2.2.0.tgz", - "integrity": "sha512-qRmjj8nj9qmLTQXXmaR1cck3UXSRMPrbsLJAasZpF+t3riI71BXed5ebIOYwQntykeZuhjsdweEc9BxH5Jc26w==", - "dev": true, - "requires": { - "@jridgewell/gen-mapping": "^0.1.0", - "@jridgewell/trace-mapping": "^0.3.9" - } - }, - "@babel/code-frame": { - "version": "7.18.6", - "resolved": "https://registry.npmjs.org/@babel/code-frame/-/code-frame-7.18.6.tgz", - "integrity": "sha512-TDCmlK5eOvH+eH7cdAFlNXeVJqWIQ7gW9tY1GJIpUtFb6CmjVyq2VM3u71bOyR8CRihcCgMUYoDNyLXao3+70Q==", - "dev": true, - "requires": { - "@babel/highlight": "^7.18.6" - } - }, - "@babel/compat-data": { - "version": "7.20.10", - "resolved": "https://registry.npmjs.org/@babel/compat-data/-/compat-data-7.20.10.tgz", - "integrity": "sha512-sEnuDPpOJR/fcafHMjpcpGN5M2jbUGUHwmuWKM/YdPzeEDJg8bgmbcWQFUfE32MQjti1koACvoPVsDe8Uq+idg==", - "dev": true - }, - "@babel/core": { - "version": "7.20.12", - "resolved": "https://registry.npmjs.org/@babel/core/-/core-7.20.12.tgz", - "integrity": "sha512-XsMfHovsUYHFMdrIHkZphTN/2Hzzi78R08NuHfDBehym2VsPDL6Zn/JAD/JQdnRvbSsbQc4mVaU1m6JgtTEElg==", - "dev": true, - "requires": { - "@ampproject/remapping": "^2.1.0", - "@babel/code-frame": "^7.18.6", - "@babel/generator": "^7.20.7", - "@babel/helper-compilation-targets": "^7.20.7", - "@babel/helper-module-transforms": "^7.20.11", - "@babel/helpers": "^7.20.7", - "@babel/parser": "^7.20.7", - "@babel/template": "^7.20.7", - "@babel/traverse": "^7.20.12", - "@babel/types": "^7.20.7", - "convert-source-map": "^1.7.0", - "debug": "^4.1.0", - "gensync": "^1.0.0-beta.2", - "json5": "^2.2.2", - "semver": "^6.3.0" - } - }, - "@babel/generator": { - "version": "7.20.7", - "resolved": "https://registry.npmjs.org/@babel/generator/-/generator-7.20.7.tgz", - "integrity": "sha512-7wqMOJq8doJMZmP4ApXTzLxSr7+oO2jroJURrVEp6XShrQUObV8Tq/D0NCcoYg2uHqUrjzO0zwBjoYzelxK+sw==", - "dev": true, - "requires": { - "@babel/types": "^7.20.7", - "@jridgewell/gen-mapping": "^0.3.2", - "jsesc": "^2.5.1" - }, - "dependencies": { - "@jridgewell/gen-mapping": { - "version": "0.3.2", - "resolved": "https://registry.npmjs.org/@jridgewell/gen-mapping/-/gen-mapping-0.3.2.tgz", - "integrity": "sha512-mh65xKQAzI6iBcFzwv28KVWSmCkdRBWoOh+bYQGW3+6OZvbbN3TqMGo5hqYxQniRcH9F2VZIoJCm4pa3BPDK/A==", - "dev": true, - "requires": { - "@jridgewell/set-array": "^1.0.1", - "@jridgewell/sourcemap-codec": "^1.4.10", - "@jridgewell/trace-mapping": "^0.3.9" - } - } - } - }, - "@babel/helper-compilation-targets": { - "version": "7.20.7", - "resolved": "https://registry.npmjs.org/@babel/helper-compilation-targets/-/helper-compilation-targets-7.20.7.tgz", - "integrity": "sha512-4tGORmfQcrc+bvrjb5y3dG9Mx1IOZjsHqQVUz7XCNHO+iTmqxWnVg3KRygjGmpRLJGdQSKuvFinbIb0CnZwHAQ==", - "dev": true, - "requires": { - "@babel/compat-data": "^7.20.5", - "@babel/helper-validator-option": "^7.18.6", - "browserslist": "^4.21.3", - "lru-cache": "^5.1.1", - "semver": "^6.3.0" - } - }, - "@babel/helper-environment-visitor": { - "version": "7.18.9", - "resolved": "https://registry.npmjs.org/@babel/helper-environment-visitor/-/helper-environment-visitor-7.18.9.tgz", - "integrity": "sha512-3r/aACDJ3fhQ/EVgFy0hpj8oHyHpQc+LPtJoY9SzTThAsStm4Ptegq92vqKoE3vD706ZVFWITnMnxucw+S9Ipg==", - "dev": true - }, - "@babel/helper-function-name": { - "version": "7.19.0", - "resolved": "https://registry.npmjs.org/@babel/helper-function-name/-/helper-function-name-7.19.0.tgz", - "integrity": "sha512-WAwHBINyrpqywkUH0nTnNgI5ina5TFn85HKS0pbPDfxFfhyR/aNQEn4hGi1P1JyT//I0t4OgXUlofzWILRvS5w==", - "dev": true, - "requires": { - "@babel/template": "^7.18.10", - "@babel/types": "^7.19.0" - } - }, - "@babel/helper-hoist-variables": { - "version": "7.18.6", - "resolved": "https://registry.npmjs.org/@babel/helper-hoist-variables/-/helper-hoist-variables-7.18.6.tgz", - "integrity": "sha512-UlJQPkFqFULIcyW5sbzgbkxn2FKRgwWiRexcuaR8RNJRy8+LLveqPjwZV/bwrLZCN0eUHD/x8D0heK1ozuoo6Q==", - "dev": true, - "requires": { - "@babel/types": "^7.18.6" - } - }, - "@babel/helper-module-imports": { - "version": "7.18.6", - "resolved": "https://registry.npmjs.org/@babel/helper-module-imports/-/helper-module-imports-7.18.6.tgz", - "integrity": "sha512-0NFvs3VkuSYbFi1x2Vd6tKrywq+z/cLeYC/RJNFrIX/30Bf5aiGYbtvGXolEktzJH8o5E5KJ3tT+nkxuuZFVlA==", - "dev": true, - "requires": { - "@babel/types": "^7.18.6" - } - }, - "@babel/helper-module-transforms": { - "version": "7.20.11", - "resolved": "https://registry.npmjs.org/@babel/helper-module-transforms/-/helper-module-transforms-7.20.11.tgz", - "integrity": "sha512-uRy78kN4psmji1s2QtbtcCSaj/LILFDp0f/ymhpQH5QY3nljUZCaNWz9X1dEj/8MBdBEFECs7yRhKn8i7NjZgg==", - "dev": true, - "requires": { - "@babel/helper-environment-visitor": "^7.18.9", - "@babel/helper-module-imports": "^7.18.6", - "@babel/helper-simple-access": "^7.20.2", - "@babel/helper-split-export-declaration": "^7.18.6", - "@babel/helper-validator-identifier": "^7.19.1", - "@babel/template": "^7.20.7", - "@babel/traverse": "^7.20.10", - "@babel/types": "^7.20.7" - } - }, - "@babel/helper-simple-access": { - "version": "7.20.2", - "resolved": "https://registry.npmjs.org/@babel/helper-simple-access/-/helper-simple-access-7.20.2.tgz", - "integrity": "sha512-+0woI/WPq59IrqDYbVGfshjT5Dmk/nnbdpcF8SnMhhXObpTq2KNBdLFRFrkVdbDOyUmHBCxzm5FHV1rACIkIbA==", - "dev": true, - "requires": { - "@babel/types": "^7.20.2" - } - }, - "@babel/helper-split-export-declaration": { - "version": "7.18.6", - "resolved": "https://registry.npmjs.org/@babel/helper-split-export-declaration/-/helper-split-export-declaration-7.18.6.tgz", - "integrity": "sha512-bde1etTx6ZyTmobl9LLMMQsaizFVZrquTEHOqKeQESMKo4PlObf+8+JA25ZsIpZhT/WEd39+vOdLXAFG/nELpA==", - "dev": true, - "requires": { - "@babel/types": "^7.18.6" - } - }, - "@babel/helper-string-parser": { - "version": "7.19.4", - "resolved": "https://registry.npmjs.org/@babel/helper-string-parser/-/helper-string-parser-7.19.4.tgz", - "integrity": "sha512-nHtDoQcuqFmwYNYPz3Rah5ph2p8PFeFCsZk9A/48dPc/rGocJ5J3hAAZ7pb76VWX3fZKu+uEr/FhH5jLx7umrw==", - "dev": true - }, - "@babel/helper-validator-identifier": { - "version": "7.19.1", - "resolved": "https://registry.npmjs.org/@babel/helper-validator-identifier/-/helper-validator-identifier-7.19.1.tgz", - "integrity": "sha512-awrNfaMtnHUr653GgGEs++LlAvW6w+DcPrOliSMXWCKo597CwL5Acf/wWdNkf/tfEQE3mjkeD1YOVZOUV/od1w==", - "dev": true - }, - "@babel/helper-validator-option": { - "version": "7.18.6", - "resolved": "https://registry.npmjs.org/@babel/helper-validator-option/-/helper-validator-option-7.18.6.tgz", - "integrity": "sha512-XO7gESt5ouv/LRJdrVjkShckw6STTaB7l9BrpBaAHDeF5YZT+01PCwmR0SJHnkW6i8OwW/EVWRShfi4j2x+KQw==", - "dev": true - }, - "@babel/helpers": { - "version": "7.20.7", - "resolved": "https://registry.npmjs.org/@babel/helpers/-/helpers-7.20.7.tgz", - "integrity": "sha512-PBPjs5BppzsGaxHQCDKnZ6Gd9s6xl8bBCluz3vEInLGRJmnZan4F6BYCeqtyXqkk4W5IlPmjK4JlOuZkpJ3xZA==", - "dev": true, - "requires": { - "@babel/template": "^7.20.7", - "@babel/traverse": "^7.20.7", - "@babel/types": "^7.20.7" - } - }, - "@babel/highlight": { - "version": "7.18.6", - "resolved": "https://registry.npmjs.org/@babel/highlight/-/highlight-7.18.6.tgz", - "integrity": "sha512-u7stbOuYjaPezCuLj29hNW1v64M2Md2qupEKP1fHc7WdOA3DgLh37suiSrZYY7haUB7iBeQZ9P1uiRF359do3g==", - "dev": true, - "requires": { - "@babel/helper-validator-identifier": "^7.18.6", - "chalk": "^2.0.0", - "js-tokens": "^4.0.0" - }, - "dependencies": { - "ansi-styles": { - "version": "3.2.1", - "resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-3.2.1.tgz", - "integrity": "sha512-VT0ZI6kZRdTh8YyJw3SMbYm/u+NqfsAxEpWO0Pf9sq8/e94WxxOpPKx9FR1FlyCtOVDNOQ+8ntlqFxiRc+r5qA==", - "dev": true, - "requires": { - "color-convert": "^1.9.0" - } - }, - "chalk": { - "version": "2.4.2", - "resolved": "https://registry.npmjs.org/chalk/-/chalk-2.4.2.tgz", - "integrity": "sha512-Mti+f9lpJNcwF4tWV8/OrTTtF1gZi+f8FqlyAdouralcFWFQWF2+NgCHShjkCb+IFBLq9buZwE1xckQU4peSuQ==", - "dev": true, - "requires": { - "ansi-styles": "^3.2.1", - "escape-string-regexp": "^1.0.5", - "supports-color": "^5.3.0" - } - }, - "color-convert": { - "version": "1.9.3", - "resolved": "https://registry.npmjs.org/color-convert/-/color-convert-1.9.3.tgz", - "integrity": "sha512-QfAUtd+vFdAtFQcC8CCyYt1fYWxSqAiK2cSD6zDB8N3cpsEBAvRxp9zOGg6G/SHHJYAT88/az/IuDGALsNVbGg==", - "dev": true, - "requires": { - "color-name": "1.1.3" - } - }, - "color-name": { - "version": "1.1.3", - "resolved": "https://registry.npmjs.org/color-name/-/color-name-1.1.3.tgz", - "integrity": "sha512-72fSenhMw2HZMTVHeCA9KCmpEIbzWiQsjN+BHcBbS9vr1mtt+vJjPdksIBNUmKAW8TFUDPJK5SUU3QhE9NEXDw==", - "dev": true - }, - "escape-string-regexp": { - "version": "1.0.5", - "resolved": "https://registry.npmjs.org/escape-string-regexp/-/escape-string-regexp-1.0.5.tgz", - "integrity": "sha512-vbRorB5FUQWvla16U8R/qgaFIya2qGzwDrNmCZuYKrbdSUMG6I1ZCGQRefkRVhuOkIGVne7BQ35DSfo1qvJqFg==", - "dev": true - }, - "has-flag": { - "version": "3.0.0", - "resolved": "https://registry.npmjs.org/has-flag/-/has-flag-3.0.0.tgz", - "integrity": "sha512-sKJf1+ceQBr4SMkvQnBDNDtf4TXpVhVGateu0t918bl30FnbE2m4vNLX+VWe/dpjlb+HugGYzW7uQXH98HPEYw==", - "dev": true - }, - "supports-color": { - "version": "5.5.0", - "resolved": "https://registry.npmjs.org/supports-color/-/supports-color-5.5.0.tgz", - "integrity": "sha512-QjVjwdXIt408MIiAqCX4oUKsgU2EqAGzs2Ppkm4aQYbjm+ZEWEcW4SfFNTr4uMNZma0ey4f5lgLrkB0aX0QMow==", - "dev": true, - "requires": { - "has-flag": "^3.0.0" - } - } - } - }, - "@babel/parser": { - "version": "7.20.7", - "resolved": "https://registry.npmjs.org/@babel/parser/-/parser-7.20.7.tgz", - "integrity": "sha512-T3Z9oHybU+0vZlY9CiDSJQTD5ZapcW18ZctFMi0MOAl/4BjFF4ul7NVSARLdbGO5vDqy9eQiGTV0LtKfvCYvcg==", - "dev": true - }, - "@babel/template": { - "version": "7.20.7", - "resolved": "https://registry.npmjs.org/@babel/template/-/template-7.20.7.tgz", - "integrity": "sha512-8SegXApWe6VoNw0r9JHpSteLKTpTiLZ4rMlGIm9JQ18KiCtyQiAMEazujAHrUS5flrcqYZa75ukev3P6QmUwUw==", - "dev": true, - "requires": { - "@babel/code-frame": "^7.18.6", - "@babel/parser": "^7.20.7", - "@babel/types": "^7.20.7" - } - }, - "@babel/traverse": { - "version": "7.20.12", - "resolved": "https://registry.npmjs.org/@babel/traverse/-/traverse-7.20.12.tgz", - "integrity": "sha512-MsIbFN0u+raeja38qboyF8TIT7K0BFzz/Yd/77ta4MsUsmP2RAnidIlwq7d5HFQrH/OZJecGV6B71C4zAgpoSQ==", - "dev": true, - "requires": { - "@babel/code-frame": "^7.18.6", - "@babel/generator": "^7.20.7", - "@babel/helper-environment-visitor": "^7.18.9", - "@babel/helper-function-name": "^7.19.0", - "@babel/helper-hoist-variables": "^7.18.6", - "@babel/helper-split-export-declaration": "^7.18.6", - "@babel/parser": "^7.20.7", - "@babel/types": "^7.20.7", - "debug": "^4.1.0", - "globals": "^11.1.0" - }, - "dependencies": { - "globals": { - "version": "11.12.0", - "resolved": "https://registry.npmjs.org/globals/-/globals-11.12.0.tgz", - "integrity": "sha512-WOBp/EEGUiIsJSp7wcv/y6MO+lV9UoncWqxuFfm8eBwzWNgyfBd6Gz+IeKQ9jCmyhoH99g15M3T+QaVHFjizVA==", - "dev": true - } - } - }, - "@babel/types": { - "version": "7.20.7", - "resolved": "https://registry.npmjs.org/@babel/types/-/types-7.20.7.tgz", - "integrity": "sha512-69OnhBxSSgK0OzTJai4kyPDiKTIe3j+ctaHdIGVbRahTLAT7L3R9oeXHC2aVSuGYt3cVnoAMDmOCgJ2yaiLMvg==", - "dev": true, - "requires": { - "@babel/helper-string-parser": "^7.19.4", - "@babel/helper-validator-identifier": "^7.19.1", - "to-fast-properties": "^2.0.0" - } - }, - "@cspotcode/source-map-support": { - "version": "0.8.1", - "resolved": "https://registry.npmjs.org/@cspotcode/source-map-support/-/source-map-support-0.8.1.tgz", - "integrity": "sha512-IchNf6dN4tHoMFIn/7OE8LWZ19Y6q/67Bmf6vnGREv8RSbBVb9LPJxEcnwrcwX6ixSvaiGoomAUvu4YSxXrVgw==", - "dev": true, - "requires": { - "@jridgewell/trace-mapping": "0.3.9" - }, - "dependencies": { - "@jridgewell/trace-mapping": { - "version": "0.3.9", - "resolved": "https://registry.npmjs.org/@jridgewell/trace-mapping/-/trace-mapping-0.3.9.tgz", - "integrity": "sha512-3Belt6tdc8bPgAtbcmdtNJlirVoTmEb5e2gC94PnkwEW9jI6CAHUeoG85tjWP5WquqfavoMtMwiG4P926ZKKuQ==", - "dev": true, - "requires": { - "@jridgewell/resolve-uri": "^3.0.3", - "@jridgewell/sourcemap-codec": "^1.4.10" - } - } - } - }, - "@eslint/eslintrc": { - "version": "1.4.1", - "resolved": "https://registry.npmjs.org/@eslint/eslintrc/-/eslintrc-1.4.1.tgz", - "integrity": "sha512-XXrH9Uarn0stsyldqDYq8r++mROmWRI1xKMXa640Bb//SY1+ECYX6VzT6Lcx5frD0V30XieqJ0oX9I2Xj5aoMA==", - "dev": true, - "requires": { - "ajv": "^6.12.4", - "debug": "^4.3.2", - "espree": "^9.4.0", - "globals": "^13.19.0", - "ignore": "^5.2.0", - "import-fresh": "^3.2.1", - "js-yaml": "^4.1.0", - "minimatch": "^3.1.2", - "strip-json-comments": "^3.1.1" - } - }, - "@humanwhocodes/config-array": { - "version": "0.11.8", - "resolved": "https://registry.npmjs.org/@humanwhocodes/config-array/-/config-array-0.11.8.tgz", - "integrity": "sha512-UybHIJzJnR5Qc/MsD9Kr+RpO2h+/P1GhOwdiLPXK5TWk5sgTdu88bTD9UP+CKbPPh5Rni1u0GjAdYQLemG8g+g==", - "dev": true, - "requires": { - "@humanwhocodes/object-schema": "^1.2.1", - "debug": "^4.1.1", - "minimatch": "^3.0.5" - } - }, - "@humanwhocodes/module-importer": { - "version": "1.0.1", - "resolved": "https://registry.npmjs.org/@humanwhocodes/module-importer/-/module-importer-1.0.1.tgz", - "integrity": "sha512-bxveV4V8v5Yb4ncFTT3rPSgZBOpCkjfK0y4oVVVJwIuDVBRMDXrPyXRL988i5ap9m9bnyEEjWfm5WkBmtffLfA==", - "dev": true - }, - "@humanwhocodes/object-schema": { - "version": "1.2.1", - "resolved": "https://registry.npmjs.org/@humanwhocodes/object-schema/-/object-schema-1.2.1.tgz", - "integrity": "sha512-ZnQMnLV4e7hDlUvw8H+U8ASL02SS2Gn6+9Ac3wGGLIe7+je2AeAOxPY+izIPJDfFDb7eDjev0Us8MO1iFRN8hA==", - "dev": true - }, - "@iarna/toml": { - "version": "2.2.5", - "resolved": "https://registry.npmjs.org/@iarna/toml/-/toml-2.2.5.tgz", - "integrity": "sha512-trnsAYxU3xnS1gPHPyU961coFyLkh4gAD/0zQ5mymY4yOZ+CYvsPqUbOFSw0aDM4y0tV7tiFxL/1XfXPNC6IPg==", - "dev": true - }, - "@istanbuljs/load-nyc-config": { - "version": "1.1.0", - "resolved": "https://registry.npmjs.org/@istanbuljs/load-nyc-config/-/load-nyc-config-1.1.0.tgz", - "integrity": "sha512-VjeHSlIzpv/NyD3N0YuHfXOPDIixcA1q2ZV98wsMqcYlPmv2n3Yb2lYP9XMElnaFVXg5A7YLTeLu6V84uQDjmQ==", - "dev": true, - "requires": { - "camelcase": "^5.3.1", - "find-up": "^4.1.0", - "get-package-type": "^0.1.0", - "js-yaml": "^3.13.1", - "resolve-from": "^5.0.0" - }, - "dependencies": { - "argparse": { - "version": "1.0.10", - "resolved": "https://registry.npmjs.org/argparse/-/argparse-1.0.10.tgz", - "integrity": "sha512-o5Roy6tNG4SL/FOkCAN6RzjiakZS25RLYFrcMttJqbdd8BWrnA+fGz57iN5Pb06pvBGvl5gQ0B48dJlslXvoTg==", - "dev": true, - "requires": { - "sprintf-js": "~1.0.2" - } - }, - "js-yaml": { - "version": "3.14.1", - "resolved": "https://registry.npmjs.org/js-yaml/-/js-yaml-3.14.1.tgz", - "integrity": "sha512-okMH7OXXJ7YrN9Ok3/SXrnu4iX9yOk+25nqX4imS2npuvTYDmo/QEZoqwZkYaIDk3jVvBOTOIEgEhaLOynBS9g==", - "dev": true, - "requires": { - "argparse": "^1.0.7", - "esprima": "^4.0.0" - } - }, - "resolve-from": { - "version": "5.0.0", - "resolved": "https://registry.npmjs.org/resolve-from/-/resolve-from-5.0.0.tgz", - "integrity": "sha512-qYg9KP24dD5qka9J47d0aVky0N+b4fTU89LN9iDnjB5waksiC49rvMB0PrUJQGoTmH50XPiqOvAjDfaijGxYZw==", - "dev": true - } - } - }, - "@istanbuljs/nyc-config-typescript": { - "version": "1.0.2", - "resolved": "https://registry.npmjs.org/@istanbuljs/nyc-config-typescript/-/nyc-config-typescript-1.0.2.tgz", - "integrity": "sha512-iKGIyMoyJuFnJRSVTZ78POIRvNnwZaWIf8vG4ZS3rQq58MMDrqEX2nnzx0R28V2X8JvmKYiqY9FP2hlJsm8A0w==", - "dev": true, - "requires": { - "@istanbuljs/schema": "^0.1.2" - } - }, - "@istanbuljs/schema": { - "version": "0.1.3", - "resolved": "https://registry.npmjs.org/@istanbuljs/schema/-/schema-0.1.3.tgz", - "integrity": "sha512-ZXRY4jNvVgSVQ8DL3LTcakaAtXwTVUxE81hslsyD2AtoXW/wVob10HkOJ1X/pAlcI7D+2YoZKg5do8G/w6RYgA==", - "dev": true - }, - "@jridgewell/gen-mapping": { - "version": "0.1.1", - "resolved": "https://registry.npmjs.org/@jridgewell/gen-mapping/-/gen-mapping-0.1.1.tgz", - "integrity": "sha512-sQXCasFk+U8lWYEe66WxRDOE9PjVz4vSM51fTu3Hw+ClTpUSQb718772vH3pyS5pShp6lvQM7SxgIDXXXmOX7w==", - "dev": true, - "requires": { - "@jridgewell/set-array": "^1.0.0", - "@jridgewell/sourcemap-codec": "^1.4.10" - } - }, - "@jridgewell/resolve-uri": { - "version": "3.1.0", - "resolved": "https://registry.npmjs.org/@jridgewell/resolve-uri/-/resolve-uri-3.1.0.tgz", - "integrity": "sha512-F2msla3tad+Mfht5cJq7LSXcdudKTWCVYUgw6pLFOOHSTtZlj6SWNYAp+AhuqLmWdBO2X5hPrLcu8cVP8fy28w==", - "dev": true - }, - "@jridgewell/set-array": { - "version": "1.1.2", - "resolved": "https://registry.npmjs.org/@jridgewell/set-array/-/set-array-1.1.2.tgz", - "integrity": "sha512-xnkseuNADM0gt2bs+BvhO0p78Mk762YnZdsuzFV018NoG1Sj1SCQvpSqa7XUaTam5vAGasABV9qXASMKnFMwMw==", - "dev": true - }, - "@jridgewell/sourcemap-codec": { - "version": "1.4.14", - "resolved": "https://registry.npmjs.org/@jridgewell/sourcemap-codec/-/sourcemap-codec-1.4.14.tgz", - "integrity": "sha512-XPSJHWmi394fuUuzDnGz1wiKqWfo1yXecHQMRf2l6hztTO+nPru658AyDngaBe7isIxEkRsPR3FZh+s7iVa4Uw==", - "dev": true - }, - "@jridgewell/trace-mapping": { - "version": "0.3.17", - "resolved": "https://registry.npmjs.org/@jridgewell/trace-mapping/-/trace-mapping-0.3.17.tgz", - "integrity": "sha512-MCNzAp77qzKca9+W/+I0+sEpaUnZoeasnghNeVc41VZCEKaCH73Vq3BZZ/SzWIgrqE4H4ceI+p+b6C0mHf9T4g==", - "dev": true, - "requires": { - "@jridgewell/resolve-uri": "3.1.0", - "@jridgewell/sourcemap-codec": "1.4.14" - } - }, - "@nodelib/fs.scandir": { - "version": "2.1.5", - "resolved": "https://registry.npmjs.org/@nodelib/fs.scandir/-/fs.scandir-2.1.5.tgz", - "integrity": "sha512-vq24Bq3ym5HEQm2NKCr3yXDwjc7vTsEThRDnkp2DK9p1uqLR+DHurm/NOTo0KG7HYHU7eppKZj3MyqYuMBf62g==", - "dev": true, - "requires": { - "@nodelib/fs.stat": "2.0.5", - "run-parallel": "^1.1.9" - } - }, - "@nodelib/fs.stat": { - "version": "2.0.5", - "resolved": "https://registry.npmjs.org/@nodelib/fs.stat/-/fs.stat-2.0.5.tgz", - "integrity": "sha512-RkhPPp2zrqDAQA/2jNhnztcPAlv64XdhIp7a7454A5ovI7Bukxgt7MX7udwAu3zg1DcpPU0rz3VV1SeaqvY4+A==", - "dev": true - }, - "@nodelib/fs.walk": { - "version": "1.2.8", - "resolved": "https://registry.npmjs.org/@nodelib/fs.walk/-/fs.walk-1.2.8.tgz", - "integrity": "sha512-oGB+UxlgWcgQkgwo8GcEGwemoTFt3FIO9ababBmaGwXIoBKZ+GTy0pP185beGg7Llih/NSHSV2XAs1lnznocSg==", - "dev": true, - "requires": { - "@nodelib/fs.scandir": "2.1.5", - "fastq": "^1.6.0" - } - }, - "@octokit/auth-token": { - "version": "3.0.2", - "resolved": "https://registry.npmjs.org/@octokit/auth-token/-/auth-token-3.0.2.tgz", - "integrity": "sha512-pq7CwIMV1kmzkFTimdwjAINCXKTajZErLB4wMLYapR2nuB/Jpr66+05wOTZMSCBXP6n4DdDWT2W19Bm17vU69Q==", - "dev": true, - "requires": { - "@octokit/types": "^8.0.0" - } - }, - "@octokit/core": { - "version": "4.1.0", - "resolved": "https://registry.npmjs.org/@octokit/core/-/core-4.1.0.tgz", - "integrity": "sha512-Czz/59VefU+kKDy+ZfDwtOIYIkFjExOKf+HA92aiTZJ6EfWpFzYQWw0l54ji8bVmyhc+mGaLUbSUmXazG7z5OQ==", - "dev": true, - "requires": { - "@octokit/auth-token": "^3.0.0", - "@octokit/graphql": "^5.0.0", - "@octokit/request": "^6.0.0", - "@octokit/request-error": "^3.0.0", - "@octokit/types": "^8.0.0", - "before-after-hook": "^2.2.0", - "universal-user-agent": "^6.0.0" - } - }, - "@octokit/endpoint": { - "version": "7.0.3", - "resolved": "https://registry.npmjs.org/@octokit/endpoint/-/endpoint-7.0.3.tgz", - "integrity": "sha512-57gRlb28bwTsdNXq+O3JTQ7ERmBTuik9+LelgcLIVfYwf235VHbN9QNo4kXExtp/h8T423cR5iJThKtFYxC7Lw==", - "dev": true, - "requires": { - "@octokit/types": "^8.0.0", - "is-plain-object": "^5.0.0", - "universal-user-agent": "^6.0.0" - } - }, - "@octokit/graphql": { - "version": "5.0.4", - "resolved": "https://registry.npmjs.org/@octokit/graphql/-/graphql-5.0.4.tgz", - "integrity": "sha512-amO1M5QUQgYQo09aStR/XO7KAl13xpigcy/kI8/N1PnZYSS69fgte+xA4+c2DISKqUZfsh0wwjc2FaCt99L41A==", - "dev": true, - "requires": { - "@octokit/request": "^6.0.0", - "@octokit/types": "^8.0.0", - "universal-user-agent": "^6.0.0" - } - }, - "@octokit/openapi-types": { - "version": "14.0.0", - "resolved": "https://registry.npmjs.org/@octokit/openapi-types/-/openapi-types-14.0.0.tgz", - "integrity": "sha512-HNWisMYlR8VCnNurDU6os2ikx0s0VyEjDYHNS/h4cgb8DeOxQ0n72HyinUtdDVxJhFy3FWLGl0DJhfEWk3P5Iw==", - "dev": true - }, - "@octokit/plugin-paginate-rest": { - "version": "5.0.1", - "resolved": "https://registry.npmjs.org/@octokit/plugin-paginate-rest/-/plugin-paginate-rest-5.0.1.tgz", - "integrity": "sha512-7A+rEkS70pH36Z6JivSlR7Zqepz3KVucEFVDnSrgHXzG7WLAzYwcHZbKdfTXHwuTHbkT1vKvz7dHl1+HNf6Qyw==", - "dev": true, - "requires": { - "@octokit/types": "^8.0.0" - } - }, - "@octokit/plugin-request-log": { - "version": "1.0.4", - "resolved": "https://registry.npmjs.org/@octokit/plugin-request-log/-/plugin-request-log-1.0.4.tgz", - "integrity": "sha512-mLUsMkgP7K/cnFEw07kWqXGF5LKrOkD+lhCrKvPHXWDywAwuDUeDwWBpc69XK3pNX0uKiVt8g5z96PJ6z9xCFA==", - "dev": true, - "requires": {} - }, - "@octokit/plugin-rest-endpoint-methods": { - "version": "6.7.0", - "resolved": "https://registry.npmjs.org/@octokit/plugin-rest-endpoint-methods/-/plugin-rest-endpoint-methods-6.7.0.tgz", - "integrity": "sha512-orxQ0fAHA7IpYhG2flD2AygztPlGYNAdlzYz8yrD8NDgelPfOYoRPROfEyIe035PlxvbYrgkfUZIhSBKju/Cvw==", - "dev": true, - "requires": { - "@octokit/types": "^8.0.0", - "deprecation": "^2.3.1" - } - }, - "@octokit/request": { - "version": "6.2.2", - "resolved": "https://registry.npmjs.org/@octokit/request/-/request-6.2.2.tgz", - "integrity": "sha512-6VDqgj0HMc2FUX2awIs+sM6OwLgwHvAi4KCK3mT2H2IKRt6oH9d0fej5LluF5mck1lRR/rFWN0YIDSYXYSylbw==", - "dev": true, - "requires": { - "@octokit/endpoint": "^7.0.0", - "@octokit/request-error": "^3.0.0", - "@octokit/types": "^8.0.0", - "is-plain-object": "^5.0.0", - "node-fetch": "^2.6.7", - "universal-user-agent": "^6.0.0" - }, - "dependencies": { - "node-fetch": { - "version": "2.6.8", - "resolved": "https://registry.npmjs.org/node-fetch/-/node-fetch-2.6.8.tgz", - "integrity": "sha512-RZ6dBYuj8dRSfxpUSu+NsdF1dpPpluJxwOp+6IoDp/sH2QNDSvurYsAa+F1WxY2RjA1iP93xhcsUoYbF2XBqVg==", - "dev": true, - "requires": { - "whatwg-url": "^5.0.0" - } - } - } - }, - "@octokit/request-error": { - "version": "3.0.2", - "resolved": "https://registry.npmjs.org/@octokit/request-error/-/request-error-3.0.2.tgz", - "integrity": "sha512-WMNOFYrSaX8zXWoJg9u/pKgWPo94JXilMLb2VManNOby9EZxrQaBe/QSC4a1TzpAlpxofg2X/jMnCyZgL6y7eg==", - "dev": true, - "requires": { - "@octokit/types": "^8.0.0", - "deprecation": "^2.0.0", - "once": "^1.4.0" - } - }, - "@octokit/rest": { - "version": "19.0.5", - "resolved": "https://registry.npmjs.org/@octokit/rest/-/rest-19.0.5.tgz", - "integrity": "sha512-+4qdrUFq2lk7Va+Qff3ofREQWGBeoTKNqlJO+FGjFP35ZahP+nBenhZiGdu8USSgmq4Ky3IJ/i4u0xbLqHaeow==", - "dev": true, - "requires": { - "@octokit/core": "^4.1.0", - "@octokit/plugin-paginate-rest": "^5.0.0", - "@octokit/plugin-request-log": "^1.0.4", - "@octokit/plugin-rest-endpoint-methods": "^6.7.0" - } - }, - "@octokit/types": { - "version": "8.1.1", - "resolved": "https://registry.npmjs.org/@octokit/types/-/types-8.1.1.tgz", - "integrity": "sha512-7tjk+6DyhYAmei8FOEwPfGKc0VE1x56CKPJ+eE44zhDbOyMT+9yan8apfQFxo8oEFsy+0O7PiBtH8w0Yo0Y9Kw==", - "dev": true, - "requires": { - "@octokit/openapi-types": "^14.0.0" - } - }, - "@pnpm/network.ca-file": { - "version": "1.0.2", - "resolved": "https://registry.npmjs.org/@pnpm/network.ca-file/-/network.ca-file-1.0.2.tgz", - "integrity": "sha512-YcPQ8a0jwYU9bTdJDpXjMi7Brhkr1mXsXrUJvjqM2mQDgkRiz8jFaQGOdaLxgjtUfQgZhKy/O3cG/YwmgKaxLA==", - "dev": true, - "requires": { - "graceful-fs": "4.2.10" - } - }, - "@pnpm/npm-conf": { - "version": "1.0.5", - "resolved": "https://registry.npmjs.org/@pnpm/npm-conf/-/npm-conf-1.0.5.tgz", - "integrity": "sha512-hD8ml183638O3R6/Txrh0L8VzGOrFXgRtRDG4qQC4tONdZ5Z1M+tlUUDUvrjYdmK6G+JTBTeaCLMna11cXzi8A==", - "dev": true, - "requires": { - "@pnpm/network.ca-file": "^1.0.1", - "config-chain": "^1.1.11" - } - }, - "@redis/bloom": { - "version": "file:packages/bloom", - "requires": { - "@istanbuljs/nyc-config-typescript": "^1.0.2", - "@redis/test-utils": "*", - "@types/node": "^18.11.18", - "nyc": "^15.1.0", - "release-it": "^15.6.0", - "source-map-support": "^0.5.21", - "ts-node": "^10.9.1", - "typedoc": "^0.23.24", - "typescript": "^4.9.4" - } - }, - "@redis/client": { - "version": "file:packages/client", - "requires": { - "@istanbuljs/nyc-config-typescript": "^1.0.2", - "@redis/test-utils": "*", - "@types/node": "^18.11.18", - "@types/sinon": "^10.0.13", - "@types/yallist": "^4.0.1", - "@typescript-eslint/eslint-plugin": "^5.49.0", - "@typescript-eslint/parser": "^5.49.0", - "cluster-key-slot": "1.1.2", - "eslint": "^8.32.0", - "generic-pool": "3.9.0", - "nyc": "^15.1.0", - "release-it": "^15.6.0", - "sinon": "^15.0.1", - "source-map-support": "^0.5.21", - "ts-node": "^10.9.1", - "typedoc": "^0.23.24", - "typescript": "^4.9.4", - "yallist": "4.0.0" - }, - "dependencies": { - "yallist": { - "version": "4.0.0", - "resolved": "https://registry.npmjs.org/yallist/-/yallist-4.0.0.tgz", - "integrity": "sha512-3wdGidZyq5PB084XLES5TpOSRA3wjXAlIWMhum2kRcv/41Sn2emQ0dycQW4uZXLejwKvg6EsvbdlVL+FYEct7A==" - } - } - }, - "@redis/graph": { - "version": "file:packages/graph", - "requires": { - "@istanbuljs/nyc-config-typescript": "^1.0.2", - "@redis/test-utils": "*", - "@types/node": "^18.11.18", - "nyc": "^15.1.0", - "release-it": "^15.6.0", - "source-map-support": "^0.5.21", - "ts-node": "^10.9.1", - "typedoc": "^0.23.24", - "typescript": "^4.9.4" - } - }, - "@redis/json": { - "version": "file:packages/json", - "requires": { - "@istanbuljs/nyc-config-typescript": "^1.0.2", - "@redis/test-utils": "*", - "@types/node": "^18.11.18", - "nyc": "^15.1.0", - "release-it": "^15.6.0", - "source-map-support": "^0.5.21", - "ts-node": "^10.9.1", - "typedoc": "^0.23.24", - "typescript": "^4.9.4" - } - }, - "@redis/search": { - "version": "file:packages/search", - "requires": { - "@istanbuljs/nyc-config-typescript": "^1.0.2", - "@redis/test-utils": "*", - "@types/node": "^18.11.18", - "nyc": "^15.1.0", - "release-it": "^15.6.0", - "source-map-support": "^0.5.21", - "ts-node": "^10.9.1", - "typedoc": "^0.23.24", - "typescript": "^4.9.4" - } - }, - "@redis/test-utils": { - "version": "file:packages/test-utils", - "requires": { - "@istanbuljs/nyc-config-typescript": "^1.0.2", - "@types/mocha": "^10.0.1", - "@types/node": "^18.11.18", - "@types/yargs": "^17.0.20", - "mocha": "^10.2.0", - "nyc": "^15.1.0", - "source-map-support": "^0.5.21", - "ts-node": "^10.9.1", - "typescript": "^4.9.4", - "yargs": "^17.6.2" - }, - "dependencies": { - "ansi-regex": { - "version": "5.0.1", - "resolved": "https://registry.npmjs.org/ansi-regex/-/ansi-regex-5.0.1.tgz", - "integrity": "sha512-quJQXlTSUGL2LH9SUXo8VwsY4soanhgo6LNSm84E1LBcE8s3O0wpdiRzyR9z/ZZJMlMWv37qOOb9pdJlMUEKFQ==", - "dev": true - }, - "ansi-styles": { - "version": "4.3.0", - "resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-4.3.0.tgz", - "integrity": "sha512-zbB9rCJAT1rbjiVDb2hqKFHNYLxgtk8NURxZ3IZwD3F6NtxbXZQCnnSi1Lkx+IDohdPlFp222wVALIheZJQSEg==", - "dev": true, - "requires": { - "color-convert": "^2.0.1" - } - }, - "cliui": { - "version": "8.0.1", - "resolved": "https://registry.npmjs.org/cliui/-/cliui-8.0.1.tgz", - "integrity": "sha512-BSeNnyus75C4//NQ9gQt1/csTXyo/8Sb+afLAkzAptFuMsod9HFokGNudZpi/oQV73hnVK+sR+5PVRMd+Dr7YQ==", - "dev": true, - "requires": { - "string-width": "^4.2.0", - "strip-ansi": "^6.0.1", - "wrap-ansi": "^7.0.0" - } - }, - "emoji-regex": { - "version": "8.0.0", - "resolved": "https://registry.npmjs.org/emoji-regex/-/emoji-regex-8.0.0.tgz", - "integrity": "sha512-MSjYzcWNOA0ewAHpz0MxpYFvwg6yjy1NG3xteoqz644VCo/RPgnr1/GGt+ic3iJTzQ8Eu3TdM14SawnVUmGE6A==", - "dev": true - }, - "string-width": { - "version": "4.2.3", - "resolved": "https://registry.npmjs.org/string-width/-/string-width-4.2.3.tgz", - "integrity": "sha512-wKyQRQpjJ0sIp62ErSZdGsjMJWsap5oRNihHhu6G7JVO/9jIB6UyevL+tXuOqrng8j/cxKTWyWUwvSTriiZz/g==", - "dev": true, - "requires": { - "emoji-regex": "^8.0.0", - "is-fullwidth-code-point": "^3.0.0", - "strip-ansi": "^6.0.1" - } - }, - "strip-ansi": { - "version": "6.0.1", - "resolved": "https://registry.npmjs.org/strip-ansi/-/strip-ansi-6.0.1.tgz", - "integrity": "sha512-Y38VPSHcqkFrCpFnQ9vuSXmquuv5oXOKpGeT6aGrr3o3Gc9AlVa6JBfUSOCnbxGGZF+/0ooI7KrPuUSztUdU5A==", - "dev": true, - "requires": { - "ansi-regex": "^5.0.1" - } - }, - "wrap-ansi": { - "version": "7.0.0", - "resolved": "https://registry.npmjs.org/wrap-ansi/-/wrap-ansi-7.0.0.tgz", - "integrity": "sha512-YVGIj2kamLSTxw6NsZjoBxfSwsn0ycdesmc4p+Q21c5zPuZ1pl+NfxVdxPtdHvmNVOQ6XSYG4AUtyt/Fi7D16Q==", - "dev": true, - "requires": { - "ansi-styles": "^4.0.0", - "string-width": "^4.1.0", - "strip-ansi": "^6.0.0" - } - }, - "y18n": { - "version": "5.0.8", - "resolved": "https://registry.npmjs.org/y18n/-/y18n-5.0.8.tgz", - "integrity": "sha512-0pfFzegeDWJHJIAmTLRP2DwHjdF5s7jo9tuztdQxAhINCdvS+3nGINqPd00AphqJR/0LhANUS6/+7SCb98YOfA==", - "dev": true - }, - "yargs": { - "version": "17.6.2", - "resolved": "https://registry.npmjs.org/yargs/-/yargs-17.6.2.tgz", - "integrity": "sha512-1/9UrdHjDZc0eOU0HxOHoS78C69UD3JRMvzlJ7S79S2nTaWRA/whGCTV8o9e/N/1Va9YIV7Q4sOxD8VV4pCWOw==", - "dev": true, - "requires": { - "cliui": "^8.0.1", - "escalade": "^3.1.1", - "get-caller-file": "^2.0.5", - "require-directory": "^2.1.1", - "string-width": "^4.2.3", - "y18n": "^5.0.5", - "yargs-parser": "^21.1.1" - } - } - } - }, - "@redis/time-series": { - "version": "file:packages/time-series", - "requires": { - "@istanbuljs/nyc-config-typescript": "^1.0.2", - "@redis/test-utils": "*", - "@types/node": "^18.11.18", - "nyc": "^15.1.0", - "release-it": "^15.6.0", - "source-map-support": "^0.5.21", - "ts-node": "^10.9.1", - "typedoc": "^0.23.24", - "typescript": "^4.9.4" - } - }, - "@sindresorhus/is": { - "version": "5.3.0", - "resolved": "https://registry.npmjs.org/@sindresorhus/is/-/is-5.3.0.tgz", - "integrity": "sha512-CX6t4SYQ37lzxicAqsBtxA3OseeoVrh9cSJ5PFYam0GksYlupRfy1A+Q4aYD3zvcfECLc0zO2u+ZnR2UYKvCrw==", - "dev": true - }, - "@sinonjs/commons": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/@sinonjs/commons/-/commons-2.0.0.tgz", - "integrity": "sha512-uLa0j859mMrg2slwQYdO/AkrOfmH+X6LTVmNTS9CqexuE2IvVORIkSpJLqePAbEnKJ77aMmCwr1NUZ57120Xcg==", - "dev": true, - "requires": { - "type-detect": "4.0.8" - } - }, - "@sinonjs/fake-timers": { - "version": "10.0.2", - "resolved": "https://registry.npmjs.org/@sinonjs/fake-timers/-/fake-timers-10.0.2.tgz", - "integrity": "sha512-SwUDyjWnah1AaNl7kxsa7cfLhlTYoiyhDAIgyh+El30YvXs/o7OLXpYH88Zdhyx9JExKrmHDJ+10bwIcY80Jmw==", - "dev": true, - "requires": { - "@sinonjs/commons": "^2.0.0" - } - }, - "@sinonjs/samsam": { - "version": "7.0.1", - "resolved": "https://registry.npmjs.org/@sinonjs/samsam/-/samsam-7.0.1.tgz", - "integrity": "sha512-zsAk2Jkiq89mhZovB2LLOdTCxJF4hqqTToGP0ASWlhp4I1hqOjcfmZGafXntCN7MDC6yySH0mFHrYtHceOeLmw==", - "dev": true, - "requires": { - "@sinonjs/commons": "^2.0.0", - "lodash.get": "^4.4.2", - "type-detect": "^4.0.8" - } - }, - "@sinonjs/text-encoding": { - "version": "0.7.2", - "resolved": "https://registry.npmjs.org/@sinonjs/text-encoding/-/text-encoding-0.7.2.tgz", - "integrity": "sha512-sXXKG+uL9IrKqViTtao2Ws6dy0znu9sOaP1di/jKGW1M6VssO8vlpXCQcpZ+jisQ1tTFAC5Jo/EOzFbggBagFQ==", - "dev": true - }, - "@szmarczak/http-timer": { - "version": "5.0.1", - "resolved": "https://registry.npmjs.org/@szmarczak/http-timer/-/http-timer-5.0.1.tgz", - "integrity": "sha512-+PmQX0PiAYPMeVYe237LJAYvOMYW1j2rH5YROyS3b4CTVJum34HfRvKvAzozHAQG0TnHNdUfY9nCeUyRAs//cw==", - "dev": true, - "requires": { - "defer-to-connect": "^2.0.1" - } - }, - "@tootallnate/once": { - "version": "1.1.2", - "resolved": "https://registry.npmjs.org/@tootallnate/once/-/once-1.1.2.tgz", - "integrity": "sha512-RbzJvlNzmRq5c3O09UipeuXno4tA1FE6ikOjxZK0tuxVv3412l64l5t1W5pj4+rJq9vpkm/kwiR07aZXnsKPxw==", - "dev": true - }, - "@tsconfig/node10": { - "version": "1.0.9", - "resolved": "https://registry.npmjs.org/@tsconfig/node10/-/node10-1.0.9.tgz", - "integrity": "sha512-jNsYVVxU8v5g43Erja32laIDHXeoNvFEpX33OK4d6hljo3jDhCBDhx5dhCCTMWUojscpAagGiRkBKxpdl9fxqA==", - "dev": true - }, - "@tsconfig/node12": { - "version": "1.0.11", - "resolved": "https://registry.npmjs.org/@tsconfig/node12/-/node12-1.0.11.tgz", - "integrity": "sha512-cqefuRsh12pWyGsIoBKJA9luFu3mRxCA+ORZvA4ktLSzIuCUtWVxGIuXigEwO5/ywWFMZ2QEGKWvkZG1zDMTag==", - "dev": true - }, - "@tsconfig/node14": { - "version": "1.0.3", - "resolved": "https://registry.npmjs.org/@tsconfig/node14/-/node14-1.0.3.tgz", - "integrity": "sha512-ysT8mhdixWK6Hw3i1V2AeRqZ5WfXg1G43mqoYlM2nc6388Fq5jcXyr5mRsqViLx/GJYdoL0bfXD8nmF+Zn/Iow==", - "dev": true - }, - "@tsconfig/node16": { - "version": "1.0.3", - "resolved": "https://registry.npmjs.org/@tsconfig/node16/-/node16-1.0.3.tgz", - "integrity": "sha512-yOlFc+7UtL/89t2ZhjPvvB/DeAr3r+Dq58IgzsFkOAvVC6NMJXmCGjbptdXdR9qsX7pKcTL+s87FtYREi2dEEQ==", - "dev": true - }, - "@types/http-cache-semantics": { - "version": "4.0.1", - "resolved": "https://registry.npmjs.org/@types/http-cache-semantics/-/http-cache-semantics-4.0.1.tgz", - "integrity": "sha512-SZs7ekbP8CN0txVG2xVRH6EgKmEm31BOxA07vkFaETzZz1xh+cbt8BcI0slpymvwhx5dlFnQG2rTlPVQn+iRPQ==", - "dev": true - }, - "@types/json-schema": { - "version": "7.0.11", - "resolved": "https://registry.npmjs.org/@types/json-schema/-/json-schema-7.0.11.tgz", - "integrity": "sha512-wOuvG1SN4Us4rez+tylwwwCV1psiNVOkJeM3AUWUNWg/jDQY2+HE/444y5gc+jBmRqASOm2Oeh5c1axHobwRKQ==", - "dev": true - }, - "@types/mocha": { - "version": "10.0.1", - "resolved": "https://registry.npmjs.org/@types/mocha/-/mocha-10.0.1.tgz", - "integrity": "sha512-/fvYntiO1GeICvqbQ3doGDIP97vWmvFt83GKguJ6prmQM2iXZfFcq6YE8KteFyRtX2/h5Hf91BYvPodJKFYv5Q==", - "dev": true - }, - "@types/node": { - "version": "18.11.18", - "resolved": "https://registry.npmjs.org/@types/node/-/node-18.11.18.tgz", - "integrity": "sha512-DHQpWGjyQKSHj3ebjFI/wRKcqQcdR+MoFBygntYOZytCqNfkd2ZC4ARDJ2DQqhjH5p85Nnd3jhUJIXrszFX/JA==", - "dev": true - }, - "@types/semver": { - "version": "7.3.13", - "resolved": "https://registry.npmjs.org/@types/semver/-/semver-7.3.13.tgz", - "integrity": "sha512-21cFJr9z3g5dW8B0CVI9g2O9beqaThGQ6ZFBqHfwhzLDKUxaqTIy3vnfah/UPkfOiF2pLq+tGz+W8RyCskuslw==", - "dev": true - }, - "@types/sinon": { - "version": "10.0.13", - "resolved": "https://registry.npmjs.org/@types/sinon/-/sinon-10.0.13.tgz", - "integrity": "sha512-UVjDqJblVNQYvVNUsj0PuYYw0ELRmgt1Nt5Vk0pT5f16ROGfcKJY8o1HVuMOJOpD727RrGB9EGvoaTQE5tgxZQ==", - "dev": true, - "requires": { - "@types/sinonjs__fake-timers": "*" - } - }, - "@types/sinonjs__fake-timers": { - "version": "8.1.2", - "resolved": "https://registry.npmjs.org/@types/sinonjs__fake-timers/-/sinonjs__fake-timers-8.1.2.tgz", - "integrity": "sha512-9GcLXF0/v3t80caGs5p2rRfkB+a8VBGLJZVih6CNFkx8IZ994wiKKLSRs9nuFwk1HevWs/1mnUmkApGrSGsShA==", - "dev": true - }, - "@types/yallist": { - "version": "4.0.1", - "resolved": "https://registry.npmjs.org/@types/yallist/-/yallist-4.0.1.tgz", - "integrity": "sha512-G3FNJfaYtN8URU6wd6+uwFI62KO79j7n3XTYcwcFncP8gkfoi0b821GoVVt0oqKVnCqKYOMNKIGpakPoFhzAGA==", - "dev": true - }, - "@types/yargs": { - "version": "17.0.20", - "resolved": "https://registry.npmjs.org/@types/yargs/-/yargs-17.0.20.tgz", - "integrity": "sha512-eknWrTHofQuPk2iuqDm1waA7V6xPlbgBoaaXEgYkClhLOnB0TtbW+srJaOToAgawPxPlHQzwypFA2bhZaUGP5A==", - "dev": true, - "requires": { - "@types/yargs-parser": "*" - } - }, - "@types/yargs-parser": { - "version": "21.0.0", - "resolved": "https://registry.npmjs.org/@types/yargs-parser/-/yargs-parser-21.0.0.tgz", - "integrity": "sha512-iO9ZQHkZxHn4mSakYV0vFHAVDyEOIJQrV2uZ06HxEPcx+mt8swXoZHIbaaJ2crJYFfErySgktuTZ3BeLz+XmFA==", - "dev": true - }, - "@typescript-eslint/eslint-plugin": { - "version": "5.49.0", - "resolved": "https://registry.npmjs.org/@typescript-eslint/eslint-plugin/-/eslint-plugin-5.49.0.tgz", - "integrity": "sha512-IhxabIpcf++TBaBa1h7jtOWyon80SXPRLDq0dVz5SLFC/eW6tofkw/O7Ar3lkx5z5U6wzbKDrl2larprp5kk5Q==", - "dev": true, - "requires": { - "@typescript-eslint/scope-manager": "5.49.0", - "@typescript-eslint/type-utils": "5.49.0", - "@typescript-eslint/utils": "5.49.0", - "debug": "^4.3.4", - "ignore": "^5.2.0", - "natural-compare-lite": "^1.4.0", - "regexpp": "^3.2.0", - "semver": "^7.3.7", - "tsutils": "^3.21.0" - }, - "dependencies": { - "lru-cache": { - "version": "6.0.0", - "resolved": "https://registry.npmjs.org/lru-cache/-/lru-cache-6.0.0.tgz", - "integrity": "sha512-Jo6dJ04CmSjuznwJSS3pUeWmd/H0ffTlkXXgwZi+eq1UCmqQwCh+eLsYOYCwY991i2Fah4h1BEMCx4qThGbsiA==", - "dev": true, - "requires": { - "yallist": "^4.0.0" - } - }, - "semver": { - "version": "7.3.8", - "resolved": "https://registry.npmjs.org/semver/-/semver-7.3.8.tgz", - "integrity": "sha512-NB1ctGL5rlHrPJtFDVIVzTyQylMLu9N9VICA6HSFJo8MCGVTMW6gfpicwKmmK/dAjTOrqu5l63JJOpDSrAis3A==", - "dev": true, - "requires": { - "lru-cache": "^6.0.0" - } - }, - "yallist": { - "version": "4.0.0", - "resolved": "https://registry.npmjs.org/yallist/-/yallist-4.0.0.tgz", - "integrity": "sha512-3wdGidZyq5PB084XLES5TpOSRA3wjXAlIWMhum2kRcv/41Sn2emQ0dycQW4uZXLejwKvg6EsvbdlVL+FYEct7A==", - "dev": true - } - } - }, - "@typescript-eslint/parser": { - "version": "5.49.0", - "resolved": "https://registry.npmjs.org/@typescript-eslint/parser/-/parser-5.49.0.tgz", - "integrity": "sha512-veDlZN9mUhGqU31Qiv2qEp+XrJj5fgZpJ8PW30sHU+j/8/e5ruAhLaVDAeznS7A7i4ucb/s8IozpDtt9NqCkZg==", - "dev": true, - "requires": { - "@typescript-eslint/scope-manager": "5.49.0", - "@typescript-eslint/types": "5.49.0", - "@typescript-eslint/typescript-estree": "5.49.0", - "debug": "^4.3.4" - } - }, - "@typescript-eslint/scope-manager": { - "version": "5.49.0", - "resolved": "https://registry.npmjs.org/@typescript-eslint/scope-manager/-/scope-manager-5.49.0.tgz", - "integrity": "sha512-clpROBOiMIzpbWNxCe1xDK14uPZh35u4QaZO1GddilEzoCLAEz4szb51rBpdgurs5k2YzPtJeTEN3qVbG+LRUQ==", - "dev": true, - "requires": { - "@typescript-eslint/types": "5.49.0", - "@typescript-eslint/visitor-keys": "5.49.0" - } - }, - "@typescript-eslint/type-utils": { - "version": "5.49.0", - "resolved": "https://registry.npmjs.org/@typescript-eslint/type-utils/-/type-utils-5.49.0.tgz", - "integrity": "sha512-eUgLTYq0tR0FGU5g1YHm4rt5H/+V2IPVkP0cBmbhRyEmyGe4XvJ2YJ6sYTmONfjmdMqyMLad7SB8GvblbeESZA==", - "dev": true, - "requires": { - "@typescript-eslint/typescript-estree": "5.49.0", - "@typescript-eslint/utils": "5.49.0", - "debug": "^4.3.4", - "tsutils": "^3.21.0" - } - }, - "@typescript-eslint/types": { - "version": "5.49.0", - "resolved": "https://registry.npmjs.org/@typescript-eslint/types/-/types-5.49.0.tgz", - "integrity": "sha512-7If46kusG+sSnEpu0yOz2xFv5nRz158nzEXnJFCGVEHWnuzolXKwrH5Bsf9zsNlOQkyZuk0BZKKoJQI+1JPBBg==", - "dev": true - }, - "@typescript-eslint/typescript-estree": { - "version": "5.49.0", - "resolved": "https://registry.npmjs.org/@typescript-eslint/typescript-estree/-/typescript-estree-5.49.0.tgz", - "integrity": "sha512-PBdx+V7deZT/3GjNYPVQv1Nc0U46dAHbIuOG8AZ3on3vuEKiPDwFE/lG1snN2eUB9IhF7EyF7K1hmTcLztNIsA==", - "dev": true, - "requires": { - "@typescript-eslint/types": "5.49.0", - "@typescript-eslint/visitor-keys": "5.49.0", - "debug": "^4.3.4", - "globby": "^11.1.0", - "is-glob": "^4.0.3", - "semver": "^7.3.7", - "tsutils": "^3.21.0" - }, - "dependencies": { - "array-union": { - "version": "2.1.0", - "resolved": "https://registry.npmjs.org/array-union/-/array-union-2.1.0.tgz", - "integrity": "sha512-HGyxoOTYUyCM6stUe6EJgnd4EoewAI7zMdfqO+kGjnlZmBDz/cR5pf8r/cR4Wq60sL/p0IkcjUEEPwS3GFrIyw==", - "dev": true - }, - "globby": { - "version": "11.1.0", - "resolved": "https://registry.npmjs.org/globby/-/globby-11.1.0.tgz", - "integrity": "sha512-jhIXaOzy1sb8IyocaruWSn1TjmnBVs8Ayhcy83rmxNJ8q2uWKCAj3CnJY+KpGSXCueAPc0i05kVvVKtP1t9S3g==", - "dev": true, - "requires": { - "array-union": "^2.1.0", - "dir-glob": "^3.0.1", - "fast-glob": "^3.2.9", - "ignore": "^5.2.0", - "merge2": "^1.4.1", - "slash": "^3.0.0" - } - }, - "lru-cache": { - "version": "6.0.0", - "resolved": "https://registry.npmjs.org/lru-cache/-/lru-cache-6.0.0.tgz", - "integrity": "sha512-Jo6dJ04CmSjuznwJSS3pUeWmd/H0ffTlkXXgwZi+eq1UCmqQwCh+eLsYOYCwY991i2Fah4h1BEMCx4qThGbsiA==", - "dev": true, - "requires": { - "yallist": "^4.0.0" - } - }, - "semver": { - "version": "7.3.8", - "resolved": "https://registry.npmjs.org/semver/-/semver-7.3.8.tgz", - "integrity": "sha512-NB1ctGL5rlHrPJtFDVIVzTyQylMLu9N9VICA6HSFJo8MCGVTMW6gfpicwKmmK/dAjTOrqu5l63JJOpDSrAis3A==", - "dev": true, - "requires": { - "lru-cache": "^6.0.0" - } - }, - "yallist": { - "version": "4.0.0", - "resolved": "https://registry.npmjs.org/yallist/-/yallist-4.0.0.tgz", - "integrity": "sha512-3wdGidZyq5PB084XLES5TpOSRA3wjXAlIWMhum2kRcv/41Sn2emQ0dycQW4uZXLejwKvg6EsvbdlVL+FYEct7A==", - "dev": true - } - } - }, - "@typescript-eslint/utils": { - "version": "5.49.0", - "resolved": "https://registry.npmjs.org/@typescript-eslint/utils/-/utils-5.49.0.tgz", - "integrity": "sha512-cPJue/4Si25FViIb74sHCLtM4nTSBXtLx1d3/QT6mirQ/c65bV8arBEebBJJizfq8W2YyMoPI/WWPFWitmNqnQ==", - "dev": true, - "requires": { - "@types/json-schema": "^7.0.9", - "@types/semver": "^7.3.12", - "@typescript-eslint/scope-manager": "5.49.0", - "@typescript-eslint/types": "5.49.0", - "@typescript-eslint/typescript-estree": "5.49.0", - "eslint-scope": "^5.1.1", - "eslint-utils": "^3.0.0", - "semver": "^7.3.7" - }, - "dependencies": { - "lru-cache": { - "version": "6.0.0", - "resolved": "https://registry.npmjs.org/lru-cache/-/lru-cache-6.0.0.tgz", - "integrity": "sha512-Jo6dJ04CmSjuznwJSS3pUeWmd/H0ffTlkXXgwZi+eq1UCmqQwCh+eLsYOYCwY991i2Fah4h1BEMCx4qThGbsiA==", - "dev": true, - "requires": { - "yallist": "^4.0.0" - } - }, - "semver": { - "version": "7.3.8", - "resolved": "https://registry.npmjs.org/semver/-/semver-7.3.8.tgz", - "integrity": "sha512-NB1ctGL5rlHrPJtFDVIVzTyQylMLu9N9VICA6HSFJo8MCGVTMW6gfpicwKmmK/dAjTOrqu5l63JJOpDSrAis3A==", - "dev": true, - "requires": { - "lru-cache": "^6.0.0" - } - }, - "yallist": { - "version": "4.0.0", - "resolved": "https://registry.npmjs.org/yallist/-/yallist-4.0.0.tgz", - "integrity": "sha512-3wdGidZyq5PB084XLES5TpOSRA3wjXAlIWMhum2kRcv/41Sn2emQ0dycQW4uZXLejwKvg6EsvbdlVL+FYEct7A==", - "dev": true - } - } - }, - "@typescript-eslint/visitor-keys": { - "version": "5.49.0", - "resolved": "https://registry.npmjs.org/@typescript-eslint/visitor-keys/-/visitor-keys-5.49.0.tgz", - "integrity": "sha512-v9jBMjpNWyn8B6k/Mjt6VbUS4J1GvUlR4x3Y+ibnP1z7y7V4n0WRz+50DY6+Myj0UaXVSuUlHohO+eZ8IJEnkg==", - "dev": true, - "requires": { - "@typescript-eslint/types": "5.49.0", - "eslint-visitor-keys": "^3.3.0" - } - }, - "acorn": { - "version": "8.8.1", - "resolved": "https://registry.npmjs.org/acorn/-/acorn-8.8.1.tgz", - "integrity": "sha512-7zFpHzhnqYKrkYdUjF1HI1bzd0VygEGX8lFk4k5zVMqHEoES+P+7TKI+EvLO9WVMJ8eekdO0aDEK044xTXwPPA==", - "dev": true - }, - "acorn-jsx": { - "version": "5.3.2", - "resolved": "https://registry.npmjs.org/acorn-jsx/-/acorn-jsx-5.3.2.tgz", - "integrity": "sha512-rq9s+JNhf0IChjtDXxllJ7g41oZk5SlXtp0LHwyA5cejwn7vKmKp4pPri6YEePv2PU65sAsegbXtIinmDFDXgQ==", - "dev": true, - "requires": {} - }, - "acorn-walk": { - "version": "8.2.0", - "resolved": "https://registry.npmjs.org/acorn-walk/-/acorn-walk-8.2.0.tgz", - "integrity": "sha512-k+iyHEuPgSw6SbuDpGQM+06HQUa04DZ3o+F6CSzXMvvI5KMvnaEqXe+YVe555R9nn6GPt404fos4wcgpw12SDA==", - "dev": true - }, - "agent-base": { - "version": "6.0.2", - "resolved": "https://registry.npmjs.org/agent-base/-/agent-base-6.0.2.tgz", - "integrity": "sha512-RZNwNclF7+MS/8bDg70amg32dyeZGZxiDuQmZxKLAlQjr3jGyLx+4Kkk58UO7D2QdgFIQCovuSuZESne6RG6XQ==", - "dev": true, - "requires": { - "debug": "4" - } - }, - "aggregate-error": { - "version": "3.1.0", - "resolved": "https://registry.npmjs.org/aggregate-error/-/aggregate-error-3.1.0.tgz", - "integrity": "sha512-4I7Td01quW/RpocfNayFdFVk1qSuoh0E7JrbRJ16nH01HhKFQ88INq9Sd+nd72zqRySlr9BmDA8xlEJ6vJMrYA==", - "dev": true, - "requires": { - "clean-stack": "^2.0.0", - "indent-string": "^4.0.0" - } - }, - "ajv": { - "version": "6.12.6", - "resolved": "https://registry.npmjs.org/ajv/-/ajv-6.12.6.tgz", - "integrity": "sha512-j3fVLgvTo527anyYyJOGTYJbG+vnnQYvE0m5mmkc1TK+nxAppkCLMIL0aZ4dblVCNoGShhm+kzE4ZUykBoMg4g==", - "dev": true, - "requires": { - "fast-deep-equal": "^3.1.1", - "fast-json-stable-stringify": "^2.0.0", - "json-schema-traverse": "^0.4.1", - "uri-js": "^4.2.2" - } - }, - "ansi-align": { - "version": "3.0.1", - "resolved": "https://registry.npmjs.org/ansi-align/-/ansi-align-3.0.1.tgz", - "integrity": "sha512-IOfwwBF5iczOjp/WeY4YxyjqAFMQoZufdQWDd19SEExbVLNXqvpzSJ/M7Za4/sCPmQ0+GRquoA7bGcINcxew6w==", - "dev": true, - "requires": { - "string-width": "^4.1.0" - }, - "dependencies": { - "ansi-regex": { - "version": "5.0.1", - "resolved": "https://registry.npmjs.org/ansi-regex/-/ansi-regex-5.0.1.tgz", - "integrity": "sha512-quJQXlTSUGL2LH9SUXo8VwsY4soanhgo6LNSm84E1LBcE8s3O0wpdiRzyR9z/ZZJMlMWv37qOOb9pdJlMUEKFQ==", - "dev": true - }, - "emoji-regex": { - "version": "8.0.0", - "resolved": "https://registry.npmjs.org/emoji-regex/-/emoji-regex-8.0.0.tgz", - "integrity": "sha512-MSjYzcWNOA0ewAHpz0MxpYFvwg6yjy1NG3xteoqz644VCo/RPgnr1/GGt+ic3iJTzQ8Eu3TdM14SawnVUmGE6A==", - "dev": true - }, - "string-width": { - "version": "4.2.3", - "resolved": "https://registry.npmjs.org/string-width/-/string-width-4.2.3.tgz", - "integrity": "sha512-wKyQRQpjJ0sIp62ErSZdGsjMJWsap5oRNihHhu6G7JVO/9jIB6UyevL+tXuOqrng8j/cxKTWyWUwvSTriiZz/g==", - "dev": true, - "requires": { - "emoji-regex": "^8.0.0", - "is-fullwidth-code-point": "^3.0.0", - "strip-ansi": "^6.0.1" - } - }, - "strip-ansi": { - "version": "6.0.1", - "resolved": "https://registry.npmjs.org/strip-ansi/-/strip-ansi-6.0.1.tgz", - "integrity": "sha512-Y38VPSHcqkFrCpFnQ9vuSXmquuv5oXOKpGeT6aGrr3o3Gc9AlVa6JBfUSOCnbxGGZF+/0ooI7KrPuUSztUdU5A==", - "dev": true, - "requires": { - "ansi-regex": "^5.0.1" - } - } - } - }, - "ansi-colors": { - "version": "4.1.1", - "resolved": "https://registry.npmjs.org/ansi-colors/-/ansi-colors-4.1.1.tgz", - "integrity": "sha512-JoX0apGbHaUJBNl6yF+p6JAFYZ666/hhCGKN5t9QFjbJQKUU/g8MNbFDbvfrgKXvI1QpZplPOnwIo99lX/AAmA==", - "dev": true - }, - "ansi-escapes": { - "version": "6.0.0", - "resolved": "https://registry.npmjs.org/ansi-escapes/-/ansi-escapes-6.0.0.tgz", - "integrity": "sha512-IG23inYII3dWlU2EyiAiGj6Bwal5GzsgPMwjYGvc1HPE2dgbj4ZB5ToWBKSquKw74nB3TIuOwaI6/jSULzfgrw==", - "dev": true, - "requires": { - "type-fest": "^3.0.0" - } - }, - "ansi-regex": { - "version": "6.0.1", - "resolved": "https://registry.npmjs.org/ansi-regex/-/ansi-regex-6.0.1.tgz", - "integrity": "sha512-n5M855fKb2SsfMIiFFoVrABHJC8QtHwVx+mHWP3QcEqBHYienj5dHSgjbxtC0WEZXYt4wcD6zrQElDPhFuZgfA==", - "dev": true - }, - "ansi-styles": { - "version": "6.2.1", - "resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-6.2.1.tgz", - "integrity": "sha512-bN798gFfQX+viw3R7yrGWRqnrN2oRkEkUjjl4JNn4E8GxxbjtG3FbrEIIY3l8/hrwUwIeCZvi4QuOTP4MErVug==", - "dev": true - }, - "anymatch": { - "version": "3.1.3", - "resolved": "https://registry.npmjs.org/anymatch/-/anymatch-3.1.3.tgz", - "integrity": "sha512-KMReFUr0B4t+D+OBkjR3KYqvocp2XaSzO55UcB6mgQMd3KbcE+mWTyvVV7D/zsdEbNnV6acZUutkiHQXvTr1Rw==", - "dev": true, - "requires": { - "normalize-path": "^3.0.0", - "picomatch": "^2.0.4" - } - }, - "append-transform": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/append-transform/-/append-transform-2.0.0.tgz", - "integrity": "sha512-7yeyCEurROLQJFv5Xj4lEGTy0borxepjFv1g22oAdqFu//SrAlDl1O1Nxx15SH1RoliUml6p8dwJW9jvZughhg==", - "dev": true, - "requires": { - "default-require-extensions": "^3.0.0" - } - }, - "archy": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/archy/-/archy-1.0.0.tgz", - "integrity": "sha512-Xg+9RwCg/0p32teKdGMPTPnVXKD0w3DfHnFTficozsAgsvq2XenPJq/MYpzzQ/v8zrOyJn6Ds39VA4JIDwFfqw==", - "dev": true - }, - "arg": { - "version": "4.1.3", - "resolved": "https://registry.npmjs.org/arg/-/arg-4.1.3.tgz", - "integrity": "sha512-58S9QDqG0Xx27YwPSt9fJxivjYl432YCwfDMfZ+71RAqUrZef7LrKQZ3LHLOwCS4FLNBplP533Zx895SeOCHvA==", - "dev": true - }, - "argparse": { - "version": "2.0.1", - "resolved": "https://registry.npmjs.org/argparse/-/argparse-2.0.1.tgz", - "integrity": "sha512-8+9WqebbFzpX9OR+Wa6O29asIogeRMzcGtAINdpMHHyAg10f05aSFVBbcEqGf/PXw1EjAZ+q2/bEBg3DvurK3Q==", - "dev": true - }, - "array-union": { - "version": "1.0.2", - "resolved": "https://registry.npmjs.org/array-union/-/array-union-1.0.2.tgz", - "integrity": "sha512-Dxr6QJj/RdU/hCaBjOfxW+q6lyuVE6JFWIrAUpuOOhoJJoQ99cUn3igRaHVB5P9WrgFVN0FfArM3x0cueOU8ng==", - "dev": true, - "requires": { - "array-uniq": "^1.0.1" - } - }, - "array-uniq": { - "version": "1.0.3", - "resolved": "https://registry.npmjs.org/array-uniq/-/array-uniq-1.0.3.tgz", - "integrity": "sha512-MNha4BWQ6JbwhFhj03YK552f7cb3AzoE8SzeljgChvL1dl3IcvggXVz1DilzySZkCja+CXuZbdW7yATchWn8/Q==", - "dev": true - }, - "array.prototype.map": { - "version": "1.0.5", - "resolved": "https://registry.npmjs.org/array.prototype.map/-/array.prototype.map-1.0.5.tgz", - "integrity": "sha512-gfaKntvwqYIuC7mLLyv2wzZIJqrRhn5PZ9EfFejSx6a78sV7iDsGpG9P+3oUPtm1Rerqm6nrKS4FYuTIvWfo3g==", - "dev": true, - "requires": { - "call-bind": "^1.0.2", - "define-properties": "^1.1.4", - "es-abstract": "^1.20.4", - "es-array-method-boxes-properly": "^1.0.0", - "is-string": "^1.0.7" - } - }, - "ast-types": { - "version": "0.13.4", - "resolved": "https://registry.npmjs.org/ast-types/-/ast-types-0.13.4.tgz", - "integrity": "sha512-x1FCFnFifvYDDzTaLII71vG5uvDwgtmDTEVWAxrgeiR8VjMONcCXJx7E+USjDtHlwFmt9MysbqgF9b9Vjr6w+w==", - "dev": true, - "requires": { - "tslib": "^2.0.1" - } - }, - "async": { - "version": "3.2.4", - "resolved": "https://registry.npmjs.org/async/-/async-3.2.4.tgz", - "integrity": "sha512-iAB+JbDEGXhyIUavoDl9WP/Jj106Kz9DEn1DPgYw5ruDn0e3Wgi3sKFm55sASdGBNOQB8F59d9qQ7deqrHA8wQ==", - "dev": true - }, - "async-retry": { - "version": "1.3.3", - "resolved": "https://registry.npmjs.org/async-retry/-/async-retry-1.3.3.tgz", - "integrity": "sha512-wfr/jstw9xNi/0teMHrRW7dsz3Lt5ARhYNZ2ewpadnhaIp5mbALhOAP+EAdsC7t4Z6wqsDVv9+W6gm1Dk9mEyw==", - "dev": true, - "requires": { - "retry": "0.13.1" - } - }, - "available-typed-arrays": { - "version": "1.0.5", - "resolved": "https://registry.npmjs.org/available-typed-arrays/-/available-typed-arrays-1.0.5.tgz", - "integrity": "sha512-DMD0KiN46eipeziST1LPP/STfDU0sufISXmjSgvVsoU2tqxctQeASejWcfNtxYKqETM1UxQ8sp2OrSBWpHY6sw==", - "dev": true - }, - "balanced-match": { - "version": "1.0.2", - "resolved": "https://registry.npmjs.org/balanced-match/-/balanced-match-1.0.2.tgz", - "integrity": "sha512-3oSeUO0TMV67hN1AmbXsK4yaqU7tjiHlbxRDZOpH0KW9+CeX4bRAaX0Anxt0tx2MrpRpWwQaPwIlISEJhYU5Pw==", - "dev": true - }, - "base64-js": { - "version": "1.5.1", - "resolved": "https://registry.npmjs.org/base64-js/-/base64-js-1.5.1.tgz", - "integrity": "sha512-AKpaYlHn8t4SVbOHCy+b5+KKgvR4vrsD8vbvrbiQJps7fKDTkjkDry6ji0rUJjC0kzbNePLwzxq8iypo41qeWA==", - "dev": true - }, - "before-after-hook": { - "version": "2.2.3", - "resolved": "https://registry.npmjs.org/before-after-hook/-/before-after-hook-2.2.3.tgz", - "integrity": "sha512-NzUnlZexiaH/46WDhANlyR2bXRopNg4F/zuSA3OpZnllCUgRaOF2znDioDWrmbNVsuZk6l9pMquQB38cfBZwkQ==", - "dev": true - }, - "binary-extensions": { - "version": "2.2.0", - "resolved": "https://registry.npmjs.org/binary-extensions/-/binary-extensions-2.2.0.tgz", - "integrity": "sha512-jDctJ/IVQbZoJykoeHbhXpOlNBqGNcwXJKJog42E5HDPUwQTSdjCHdihjj0DlnheQ7blbT6dHOafNAiS8ooQKA==", - "dev": true - }, - "bl": { - "version": "5.1.0", - "resolved": "https://registry.npmjs.org/bl/-/bl-5.1.0.tgz", - "integrity": "sha512-tv1ZJHLfTDnXE6tMHv73YgSJaWR2AFuPwMntBe7XL/GBFHnT0CLnsHMogfk5+GzCDC5ZWarSCYaIGATZt9dNsQ==", - "dev": true, - "requires": { - "buffer": "^6.0.3", - "inherits": "^2.0.4", - "readable-stream": "^3.4.0" - } - }, - "boxen": { - "version": "7.0.1", - "resolved": "https://registry.npmjs.org/boxen/-/boxen-7.0.1.tgz", - "integrity": "sha512-8k2eH6SRAK00NDl1iX5q17RJ8rfl53TajdYxE3ssMLehbg487dEVgsad4pIsZb/QqBgYWIl6JOauMTLGX2Kpkw==", - "dev": true, - "requires": { - "ansi-align": "^3.0.1", - "camelcase": "^7.0.0", - "chalk": "^5.0.1", - "cli-boxes": "^3.0.0", - "string-width": "^5.1.2", - "type-fest": "^2.13.0", - "widest-line": "^4.0.1", - "wrap-ansi": "^8.0.1" - }, - "dependencies": { - "camelcase": { - "version": "7.0.1", - "resolved": "https://registry.npmjs.org/camelcase/-/camelcase-7.0.1.tgz", - "integrity": "sha512-xlx1yCK2Oc1APsPXDL2LdlNP6+uu8OCDdhOBSVT279M/S+y75O30C2VuD8T2ogdePBBl7PfPF4504tnLgX3zfw==", - "dev": true - }, - "type-fest": { - "version": "2.19.0", - "resolved": "https://registry.npmjs.org/type-fest/-/type-fest-2.19.0.tgz", - "integrity": "sha512-RAH822pAdBgcNMAfWnCBU3CFZcfZ/i1eZjwFU/dsLKumyuuP3niueg2UAukXYF0E2AAoc82ZSSf9J0WQBinzHA==", - "dev": true - } - } - }, - "brace-expansion": { - "version": "1.1.11", - "resolved": "https://registry.npmjs.org/brace-expansion/-/brace-expansion-1.1.11.tgz", - "integrity": "sha512-iCuPHDFgrHX7H2vEI/5xpz07zSHB00TpugqhmYtVmMO6518mCuRMoOYFldEBl0g187ufozdaHgWKcYFb61qGiA==", - "dev": true, - "requires": { - "balanced-match": "^1.0.0", - "concat-map": "0.0.1" - } - }, - "braces": { - "version": "3.0.2", - "resolved": "https://registry.npmjs.org/braces/-/braces-3.0.2.tgz", - "integrity": "sha512-b8um+L1RzM3WDSzvhm6gIz1yfTbBt6YTlcEKAvsmqCZZFw46z626lVj9j1yEPW33H5H+lBQpZMP1k8l+78Ha0A==", - "dev": true, - "requires": { - "fill-range": "^7.0.1" - } - }, - "browser-stdout": { - "version": "1.3.1", - "resolved": "https://registry.npmjs.org/browser-stdout/-/browser-stdout-1.3.1.tgz", - "integrity": "sha512-qhAVI1+Av2X7qelOfAIYwXONood6XlZE/fXaBSmW/T5SzLAmCgzi+eiWE7fUvbHaeNBQH13UftjpXxsfLkMpgw==", - "dev": true - }, - "browserslist": { - "version": "4.21.4", - "resolved": "https://registry.npmjs.org/browserslist/-/browserslist-4.21.4.tgz", - "integrity": "sha512-CBHJJdDmgjl3daYjN5Cp5kbTf1mUhZoS+beLklHIvkOWscs83YAhLlF3Wsh/lciQYAcbBJgTOD44VtG31ZM4Hw==", - "dev": true, - "requires": { - "caniuse-lite": "^1.0.30001400", - "electron-to-chromium": "^1.4.251", - "node-releases": "^2.0.6", - "update-browserslist-db": "^1.0.9" - } - }, - "buffer": { - "version": "6.0.3", - "resolved": "https://registry.npmjs.org/buffer/-/buffer-6.0.3.tgz", - "integrity": "sha512-FTiCpNxtwiZZHEZbcbTIcZjERVICn9yq/pDFkTl95/AxzD1naBctN7YO68riM/gLSDY7sdrMby8hofADYuuqOA==", - "dev": true, - "requires": { - "base64-js": "^1.3.1", - "ieee754": "^1.2.1" - } - }, - "buffer-from": { - "version": "1.1.2", - "resolved": "https://registry.npmjs.org/buffer-from/-/buffer-from-1.1.2.tgz", - "integrity": "sha512-E+XQCRwSbaaiChtv6k6Dwgc+bx+Bs6vuKJHHl5kox/BaKbhiXzqQOwK4cO22yElGp2OCmjwVhT3HmxgyPGnJfQ==", - "dev": true - }, - "bytes": { - "version": "3.1.2", - "resolved": "https://registry.npmjs.org/bytes/-/bytes-3.1.2.tgz", - "integrity": "sha512-/Nf7TyzTx6S3yRJObOAV7956r8cr2+Oj8AC5dt8wSP3BQAoeX58NoHyCU8P8zGkNXStjTSi6fzO6F0pBdcYbEg==", - "dev": true - }, - "cacheable-lookup": { - "version": "7.0.0", - "resolved": "https://registry.npmjs.org/cacheable-lookup/-/cacheable-lookup-7.0.0.tgz", - "integrity": "sha512-+qJyx4xiKra8mZrcwhjMRMUhD5NR1R8esPkzIYxX96JiecFoxAXFuz/GpR3+ev4PE1WamHip78wV0vcmPQtp8w==", - "dev": true - }, - "cacheable-request": { - "version": "10.2.5", - "resolved": "https://registry.npmjs.org/cacheable-request/-/cacheable-request-10.2.5.tgz", - "integrity": "sha512-5RwYYCfzjNPsyJxb/QpaM0bfzx+kw5/YpDhZPm9oMIDntHFQ9YXeyV47ZvzlTE0XrrrbyO2UITJH4GF9eRLdXQ==", - "dev": true, - "requires": { - "@types/http-cache-semantics": "^4.0.1", - "get-stream": "^6.0.1", - "http-cache-semantics": "^4.1.0", - "keyv": "^4.5.2", - "mimic-response": "^4.0.0", - "normalize-url": "^8.0.0", - "responselike": "^3.0.0" - } - }, - "caching-transform": { - "version": "4.0.0", - "resolved": "https://registry.npmjs.org/caching-transform/-/caching-transform-4.0.0.tgz", - "integrity": "sha512-kpqOvwXnjjN44D89K5ccQC+RUrsy7jB/XLlRrx0D7/2HNcTPqzsb6XgYoErwko6QsV184CA2YgS1fxDiiDZMWA==", - "dev": true, - "requires": { - "hasha": "^5.0.0", - "make-dir": "^3.0.0", - "package-hash": "^4.0.0", - "write-file-atomic": "^3.0.0" - } - }, - "call-bind": { - "version": "1.0.2", - "resolved": "https://registry.npmjs.org/call-bind/-/call-bind-1.0.2.tgz", - "integrity": "sha512-7O+FbCihrB5WGbFYesctwmTKae6rOiIzmz1icreWJ+0aA7LJfuqhEso2T9ncpcFtzMQtzXf2QGGueWJGTYsqrA==", - "dev": true, - "requires": { - "function-bind": "^1.1.1", - "get-intrinsic": "^1.0.2" - } - }, - "callsites": { - "version": "3.1.0", - "resolved": "https://registry.npmjs.org/callsites/-/callsites-3.1.0.tgz", - "integrity": "sha512-P8BjAsXvZS+VIDUI11hHCQEv74YT67YUi5JJFNWIqL235sBmjX4+qx9Muvls5ivyNENctx46xQLQ3aTuE7ssaQ==", - "dev": true - }, - "camelcase": { - "version": "5.3.1", - "resolved": "https://registry.npmjs.org/camelcase/-/camelcase-5.3.1.tgz", - "integrity": "sha512-L28STB170nwWS63UjtlEOE3dldQApaJXZkOI1uMFfzf3rRuPegHaHesyee+YxQ+W6SvRDQV6UrdOdRiR153wJg==", - "dev": true - }, - "caniuse-lite": { - "version": "1.0.30001445", - "resolved": "https://registry.npmjs.org/caniuse-lite/-/caniuse-lite-1.0.30001445.tgz", - "integrity": "sha512-8sdQIdMztYmzfTMO6KfLny878Ln9c2M0fc7EH60IjlP4Dc4PiCy7K2Vl3ITmWgOyPgVQKa5x+UP/KqFsxj4mBg==", - "dev": true - }, - "chalk": { - "version": "5.1.2", - "resolved": "https://registry.npmjs.org/chalk/-/chalk-5.1.2.tgz", - "integrity": "sha512-E5CkT4jWURs1Vy5qGJye+XwCkNj7Od3Af7CP6SujMetSMkLs8Do2RWJK5yx1wamHV/op8Rz+9rltjaTQWDnEFQ==", - "dev": true - }, - "chardet": { - "version": "0.7.0", - "resolved": "https://registry.npmjs.org/chardet/-/chardet-0.7.0.tgz", - "integrity": "sha512-mT8iDcrh03qDGRRmoA2hmBJnxpllMR+0/0qlzjqZES6NdiWDcZkCNAk4rPFZ9Q85r27unkiNNg8ZOiwZXBHwcA==", - "dev": true - }, - "chokidar": { - "version": "3.5.3", - "resolved": "https://registry.npmjs.org/chokidar/-/chokidar-3.5.3.tgz", - "integrity": "sha512-Dr3sfKRP6oTcjf2JmUmFJfeVMvXBdegxB0iVQ5eb2V10uFJUCAS8OByZdVAyVb8xXNz3GjjTgj9kLWsZTqE6kw==", - "dev": true, - "requires": { - "anymatch": "~3.1.2", - "braces": "~3.0.2", - "fsevents": "~2.3.2", - "glob-parent": "~5.1.2", - "is-binary-path": "~2.1.0", - "is-glob": "~4.0.1", - "normalize-path": "~3.0.0", - "readdirp": "~3.6.0" - }, - "dependencies": { - "glob-parent": { - "version": "5.1.2", - "resolved": "https://registry.npmjs.org/glob-parent/-/glob-parent-5.1.2.tgz", - "integrity": "sha512-AOIgSQCepiJYwP3ARnGx+5VnTu2HBYdzbGP45eLw1vr3zB3vZLeyed1sC9hnbcOc9/SrMyM5RPQrkGz4aS9Zow==", - "dev": true, - "requires": { - "is-glob": "^4.0.1" - } - } - } - }, - "ci-info": { - "version": "3.7.1", - "resolved": "https://registry.npmjs.org/ci-info/-/ci-info-3.7.1.tgz", - "integrity": "sha512-4jYS4MOAaCIStSRwiuxc4B8MYhIe676yO1sYGzARnjXkWpmzZMMYxY6zu8WYWDhSuth5zhrQ1rhNSibyyvv4/w==", - "dev": true - }, - "clean-stack": { - "version": "2.2.0", - "resolved": "https://registry.npmjs.org/clean-stack/-/clean-stack-2.2.0.tgz", - "integrity": "sha512-4diC9HaTE+KRAMWhDhrGOECgWZxoevMc5TlkObMqNSsVU62PYzXZ/SMTjzyGAFF1YusgxGcSWTEXBhp0CPwQ1A==", - "dev": true - }, - "cli-boxes": { - "version": "3.0.0", - "resolved": "https://registry.npmjs.org/cli-boxes/-/cli-boxes-3.0.0.tgz", - "integrity": "sha512-/lzGpEWL/8PfI0BmBOPRwp0c/wFNX1RdUML3jK/RcSBA9T8mZDdQpqYBKtCFTOfQbwPqWEOpjqW+Fnayc0969g==", - "dev": true - }, - "cli-cursor": { - "version": "4.0.0", - "resolved": "https://registry.npmjs.org/cli-cursor/-/cli-cursor-4.0.0.tgz", - "integrity": "sha512-VGtlMu3x/4DOtIUwEkRezxUZ2lBacNJCHash0N0WeZDBS+7Ux1dm3XWAgWYxLJFMMdOeXMHXorshEFhbMSGelg==", - "dev": true, - "requires": { - "restore-cursor": "^4.0.0" - } - }, - "cli-spinners": { - "version": "2.7.0", - "resolved": "https://registry.npmjs.org/cli-spinners/-/cli-spinners-2.7.0.tgz", - "integrity": "sha512-qu3pN8Y3qHNgE2AFweciB1IfMnmZ/fsNTEE+NOFjmGB2F/7rLhnhzppvpCnN4FovtP26k8lHyy9ptEbNwWFLzw==", - "dev": true - }, - "cli-width": { - "version": "4.0.0", - "resolved": "https://registry.npmjs.org/cli-width/-/cli-width-4.0.0.tgz", - "integrity": "sha512-ZksGS2xpa/bYkNzN3BAw1wEjsLV/ZKOf/CCrJ/QOBsxx6fOARIkwTutxp1XIOIohi6HKmOFjMoK/XaqDVUpEEw==", - "dev": true - }, - "cliui": { - "version": "6.0.0", - "resolved": "https://registry.npmjs.org/cliui/-/cliui-6.0.0.tgz", - "integrity": "sha512-t6wbgtoCXvAzst7QgXxJYqPt0usEfbgQdftEPbLL/cvv6HPE5VgvqCuAIDR0NgU52ds6rFwqrgakNLrHEjCbrQ==", - "dev": true, - "requires": { - "string-width": "^4.2.0", - "strip-ansi": "^6.0.0", - "wrap-ansi": "^6.2.0" - }, - "dependencies": { - "ansi-regex": { - "version": "5.0.1", - "resolved": "https://registry.npmjs.org/ansi-regex/-/ansi-regex-5.0.1.tgz", - "integrity": "sha512-quJQXlTSUGL2LH9SUXo8VwsY4soanhgo6LNSm84E1LBcE8s3O0wpdiRzyR9z/ZZJMlMWv37qOOb9pdJlMUEKFQ==", - "dev": true - }, - "ansi-styles": { - "version": "4.3.0", - "resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-4.3.0.tgz", - "integrity": "sha512-zbB9rCJAT1rbjiVDb2hqKFHNYLxgtk8NURxZ3IZwD3F6NtxbXZQCnnSi1Lkx+IDohdPlFp222wVALIheZJQSEg==", - "dev": true, - "requires": { - "color-convert": "^2.0.1" - } - }, - "emoji-regex": { - "version": "8.0.0", - "resolved": "https://registry.npmjs.org/emoji-regex/-/emoji-regex-8.0.0.tgz", - "integrity": "sha512-MSjYzcWNOA0ewAHpz0MxpYFvwg6yjy1NG3xteoqz644VCo/RPgnr1/GGt+ic3iJTzQ8Eu3TdM14SawnVUmGE6A==", - "dev": true - }, - "string-width": { - "version": "4.2.3", - "resolved": "https://registry.npmjs.org/string-width/-/string-width-4.2.3.tgz", - "integrity": "sha512-wKyQRQpjJ0sIp62ErSZdGsjMJWsap5oRNihHhu6G7JVO/9jIB6UyevL+tXuOqrng8j/cxKTWyWUwvSTriiZz/g==", - "dev": true, - "requires": { - "emoji-regex": "^8.0.0", - "is-fullwidth-code-point": "^3.0.0", - "strip-ansi": "^6.0.1" - } - }, - "strip-ansi": { - "version": "6.0.1", - "resolved": "https://registry.npmjs.org/strip-ansi/-/strip-ansi-6.0.1.tgz", - "integrity": "sha512-Y38VPSHcqkFrCpFnQ9vuSXmquuv5oXOKpGeT6aGrr3o3Gc9AlVa6JBfUSOCnbxGGZF+/0ooI7KrPuUSztUdU5A==", - "dev": true, - "requires": { - "ansi-regex": "^5.0.1" - } - }, - "wrap-ansi": { - "version": "6.2.0", - "resolved": "https://registry.npmjs.org/wrap-ansi/-/wrap-ansi-6.2.0.tgz", - "integrity": "sha512-r6lPcBGxZXlIcymEu7InxDMhdW0KDxpLgoFLcguasxCaJ/SOIZwINatK9KY/tf+ZrlywOKU0UDj3ATXUBfxJXA==", - "dev": true, - "requires": { - "ansi-styles": "^4.0.0", - "string-width": "^4.1.0", - "strip-ansi": "^6.0.0" - } - } - } - }, - "clone": { - "version": "1.0.4", - "resolved": "https://registry.npmjs.org/clone/-/clone-1.0.4.tgz", - "integrity": "sha512-JQHZ2QMW6l3aH/j6xCqQThY/9OH4D/9ls34cgkUBiEeocRTU04tHfKPBsUK1PqZCUQM7GiA0IIXJSuXHI64Kbg==", - "dev": true - }, - "cluster-key-slot": { - "version": "1.1.2", - "resolved": "https://registry.npmjs.org/cluster-key-slot/-/cluster-key-slot-1.1.2.tgz", - "integrity": "sha512-RMr0FhtfXemyinomL4hrWcYJxmX6deFdCxpJzhDttxgO1+bcCnkk+9drydLVDmAMG7NE6aN/fl4F7ucU/90gAA==" - }, - "color-convert": { - "version": "2.0.1", - "resolved": "https://registry.npmjs.org/color-convert/-/color-convert-2.0.1.tgz", - "integrity": "sha512-RRECPsj7iu/xb5oKYcsFHSppFNnsj/52OVTRKb4zP5onXwVF3zVmmToNcOfGC+CRDpfK/U584fMg38ZHCaElKQ==", - "dev": true, - "requires": { - "color-name": "~1.1.4" - } - }, - "color-name": { - "version": "1.1.4", - "resolved": "https://registry.npmjs.org/color-name/-/color-name-1.1.4.tgz", - "integrity": "sha512-dOy+3AuW3a2wNbZHIuMZpTcgjGuLU/uBL/ubcZF9OXbDo8ff4O8yVp5Bf0efS8uEoYo5q4Fx7dY9OgQGXgAsQA==", - "dev": true - }, - "commander": { - "version": "2.20.3", - "resolved": "https://registry.npmjs.org/commander/-/commander-2.20.3.tgz", - "integrity": "sha512-GpVkmM8vF2vQUkj2LvZmD35JxeJOLCwJ9cUkugyk2nuhbv3+mJvpLYYt+0+USMxE+oj+ey/lJEnhZw75x/OMcQ==", - "dev": true - }, - "commondir": { - "version": "1.0.1", - "resolved": "https://registry.npmjs.org/commondir/-/commondir-1.0.1.tgz", - "integrity": "sha512-W9pAhw0ja1Edb5GVdIF1mjZw/ASI0AlShXM83UUGe2DVr5TdAPEA1OA8m/g8zWp9x6On7gqufY+FatDbC3MDQg==", - "dev": true - }, - "concat-map": { - "version": "0.0.1", - "resolved": "https://registry.npmjs.org/concat-map/-/concat-map-0.0.1.tgz", - "integrity": "sha512-/Srv4dswyQNBfohGpz9o6Yb3Gz3SrUDqBH5rTuhGR7ahtlbYKnVxw2bCFMRljaA7EXHaXZ8wsHdodFvbkhKmqg==", - "dev": true - }, - "config-chain": { - "version": "1.1.13", - "resolved": "https://registry.npmjs.org/config-chain/-/config-chain-1.1.13.tgz", - "integrity": "sha512-qj+f8APARXHrM0hraqXYb2/bOVSV4PvJQlNZ/DVj0QrmNM2q2euizkeuVckQ57J+W0mRH6Hvi+k50M4Jul2VRQ==", - "dev": true, - "requires": { - "ini": "^1.3.4", - "proto-list": "~1.2.1" - }, - "dependencies": { - "ini": { - "version": "1.3.8", - "resolved": "https://registry.npmjs.org/ini/-/ini-1.3.8.tgz", - "integrity": "sha512-JV/yugV2uzW5iMRSiZAyDtQd+nxtUnjeLt0acNdw98kKLrvuRVyB80tsREOE7yvGVgalhZ6RNXCmEHkUKBKxew==", - "dev": true - } - } - }, - "configstore": { - "version": "6.0.0", - "resolved": "https://registry.npmjs.org/configstore/-/configstore-6.0.0.tgz", - "integrity": "sha512-cD31W1v3GqUlQvbBCGcXmd2Nj9SvLDOP1oQ0YFuLETufzSPaKp11rYBsSOm7rCsW3OnIRAFM3OxRhceaXNYHkA==", - "dev": true, - "requires": { - "dot-prop": "^6.0.1", - "graceful-fs": "^4.2.6", - "unique-string": "^3.0.0", - "write-file-atomic": "^3.0.3", - "xdg-basedir": "^5.0.1" - } - }, - "convert-source-map": { - "version": "1.9.0", - "resolved": "https://registry.npmjs.org/convert-source-map/-/convert-source-map-1.9.0.tgz", - "integrity": "sha512-ASFBup0Mz1uyiIjANan1jzLQami9z1PoYSZCiiYW2FczPbenXc45FZdBZLzOT+r6+iciuEModtmCti+hjaAk0A==", - "dev": true - }, - "core-util-is": { - "version": "1.0.3", - "resolved": "https://registry.npmjs.org/core-util-is/-/core-util-is-1.0.3.tgz", - "integrity": "sha512-ZQBvi1DcpJ4GDqanjucZ2Hj3wEO5pZDS89BWbkcrvdxksJorwUDDZamX9ldFkp9aw2lmBDLgkObEA4DWNJ9FYQ==", - "dev": true - }, - "cosmiconfig": { - "version": "8.0.0", - "resolved": "https://registry.npmjs.org/cosmiconfig/-/cosmiconfig-8.0.0.tgz", - "integrity": "sha512-da1EafcpH6b/TD8vDRaWV7xFINlHlF6zKsGwS1TsuVJTZRkquaS5HTMq7uq6h31619QjbsYl21gVDOm32KM1vQ==", - "dev": true, - "requires": { - "import-fresh": "^3.2.1", - "js-yaml": "^4.1.0", - "parse-json": "^5.0.0", - "path-type": "^4.0.0" - } - }, - "create-require": { - "version": "1.1.1", - "resolved": "https://registry.npmjs.org/create-require/-/create-require-1.1.1.tgz", - "integrity": "sha512-dcKFX3jn0MpIaXjisoRvexIJVEKzaq7z2rZKxf+MSr9TkdmHmsU4m2lcLojrj/FHl8mk5VxMmYA+ftRkP/3oKQ==", - "dev": true - }, - "cross-spawn": { - "version": "7.0.3", - "resolved": "https://registry.npmjs.org/cross-spawn/-/cross-spawn-7.0.3.tgz", - "integrity": "sha512-iRDPJKUPVEND7dHPO8rkbOnPpyDygcDFtWjpeWNCgy8WP2rXcxXL8TskReQl6OrB2G7+UJrags1q15Fudc7G6w==", - "dev": true, - "requires": { - "path-key": "^3.1.0", - "shebang-command": "^2.0.0", - "which": "^2.0.1" - } - }, - "crypto-random-string": { - "version": "4.0.0", - "resolved": "https://registry.npmjs.org/crypto-random-string/-/crypto-random-string-4.0.0.tgz", - "integrity": "sha512-x8dy3RnvYdlUcPOjkEHqozhiwzKNSq7GcPuXFbnyMOCHxX8V3OgIg/pYuabl2sbUPfIJaeAQB7PMOK8DFIdoRA==", - "dev": true, - "requires": { - "type-fest": "^1.0.1" - }, - "dependencies": { - "type-fest": { - "version": "1.4.0", - "resolved": "https://registry.npmjs.org/type-fest/-/type-fest-1.4.0.tgz", - "integrity": "sha512-yGSza74xk0UG8k+pLh5oeoYirvIiWo5t0/o3zHHAO2tRDiZcxWP7fywNlXhqb6/r6sWvwi+RsyQMWhVLe4BVuA==", - "dev": true - } - } - }, - "data-uri-to-buffer": { - "version": "4.0.1", - "resolved": "https://registry.npmjs.org/data-uri-to-buffer/-/data-uri-to-buffer-4.0.1.tgz", - "integrity": "sha512-0R9ikRb668HB7QDxT1vkpuUBtqc53YyAwMwGeUFKRojY/NWKvdZ+9UYtRfGmhqNbRkTSVpMbmyhXipFFv2cb/A==", - "dev": true - }, - "debug": { - "version": "4.3.4", - "resolved": "https://registry.npmjs.org/debug/-/debug-4.3.4.tgz", - "integrity": "sha512-PRWFHuSU3eDtQJPvnNY7Jcket1j0t5OuOsFzPPzsekD52Zl8qUfFIPEiswXqIvHWGVHOgX+7G/vCNNhehwxfkQ==", - "dev": true, - "requires": { - "ms": "2.1.2" - } - }, - "decamelize": { - "version": "1.2.0", - "resolved": "https://registry.npmjs.org/decamelize/-/decamelize-1.2.0.tgz", - "integrity": "sha512-z2S+W9X73hAUUki+N+9Za2lBlun89zigOyGrsax+KUQ6wKW4ZoWpEYBkGhQjwAjjDCkWxhY0VKEhk8wzY7F5cA==", - "dev": true - }, - "decompress-response": { - "version": "6.0.0", - "resolved": "https://registry.npmjs.org/decompress-response/-/decompress-response-6.0.0.tgz", - "integrity": "sha512-aW35yZM6Bb/4oJlZncMH2LCoZtJXTRxES17vE3hoRiowU2kWHaJKFkSBDnDR+cm9J+9QhXmREyIfv0pji9ejCQ==", - "dev": true, - "requires": { - "mimic-response": "^3.1.0" - }, - "dependencies": { - "mimic-response": { - "version": "3.1.0", - "resolved": "https://registry.npmjs.org/mimic-response/-/mimic-response-3.1.0.tgz", - "integrity": "sha512-z0yWI+4FDrrweS8Zmt4Ej5HdJmky15+L2e6Wgn3+iK5fWzb6T3fhNFq2+MeTRb064c6Wr4N/wv0DzQTjNzHNGQ==", - "dev": true - } - } - }, - "deep-extend": { - "version": "0.6.0", - "resolved": "https://registry.npmjs.org/deep-extend/-/deep-extend-0.6.0.tgz", - "integrity": "sha512-LOHxIOaPYdHlJRtCQfDIVZtfw/ufM8+rVj649RIHzcm/vGwQRXFt6OPqIFWsm2XEMrNIEtWR64sY1LEKD2vAOA==", - "dev": true - }, - "deep-is": { - "version": "0.1.4", - "resolved": "https://registry.npmjs.org/deep-is/-/deep-is-0.1.4.tgz", - "integrity": "sha512-oIPzksmTg4/MriiaYGO+okXDT7ztn/w3Eptv/+gSIdMdKsJo0u4CfYNFJPy+4SKMuCqGw2wxnA+URMg3t8a/bQ==", - "dev": true - }, - "default-require-extensions": { - "version": "3.0.1", - "resolved": "https://registry.npmjs.org/default-require-extensions/-/default-require-extensions-3.0.1.tgz", - "integrity": "sha512-eXTJmRbm2TIt9MgWTsOH1wEuhew6XGZcMeGKCtLedIg/NCsg1iBePXkceTdK4Fii7pzmN9tGsZhKzZ4h7O/fxw==", - "dev": true, - "requires": { - "strip-bom": "^4.0.0" - } - }, - "defaults": { - "version": "1.0.4", - "resolved": "https://registry.npmjs.org/defaults/-/defaults-1.0.4.tgz", - "integrity": "sha512-eFuaLoy/Rxalv2kr+lqMlUnrDWV+3j4pljOIJgLIhI058IQfWJ7vXhyEIHu+HtC738klGALYxOKDO0bQP3tg8A==", - "dev": true, - "requires": { - "clone": "^1.0.2" - } - }, - "defer-to-connect": { - "version": "2.0.1", - "resolved": "https://registry.npmjs.org/defer-to-connect/-/defer-to-connect-2.0.1.tgz", - "integrity": "sha512-4tvttepXG1VaYGrRibk5EwJd1t4udunSOVMdLSAL6mId1ix438oPwPZMALY41FCijukO1L0twNcGsdzS7dHgDg==", - "dev": true - }, - "define-lazy-prop": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/define-lazy-prop/-/define-lazy-prop-2.0.0.tgz", - "integrity": "sha512-Ds09qNh8yw3khSjiJjiUInaGX9xlqZDY7JVryGxdxV7NPeuqQfplOpQ66yJFZut3jLa5zOwkXw1g9EI2uKh4Og==", - "dev": true - }, - "define-properties": { - "version": "1.1.4", - "resolved": "https://registry.npmjs.org/define-properties/-/define-properties-1.1.4.tgz", - "integrity": "sha512-uckOqKcfaVvtBdsVkdPv3XjveQJsNQqmhXgRi8uhvWWuPYZCNlzT8qAyblUgNoXdHdjMTzAqeGjAoli8f+bzPA==", - "dev": true, - "requires": { - "has-property-descriptors": "^1.0.0", - "object-keys": "^1.1.1" - } - }, - "degenerator": { - "version": "3.0.2", - "resolved": "https://registry.npmjs.org/degenerator/-/degenerator-3.0.2.tgz", - "integrity": "sha512-c0mef3SNQo56t6urUU6tdQAs+ThoD0o9B9MJ8HEt7NQcGEILCRFqQb7ZbP9JAv+QF1Ky5plydhMR/IrqWDm+TQ==", - "dev": true, - "requires": { - "ast-types": "^0.13.2", - "escodegen": "^1.8.1", - "esprima": "^4.0.0", - "vm2": "^3.9.8" - } - }, - "depd": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/depd/-/depd-2.0.0.tgz", - "integrity": "sha512-g7nH6P6dyDioJogAAGprGpCtVImJhpPk/roCzdb3fIh61/s/nPsfR6onyMwkCAR/OlC3yBC0lESvUoQEAssIrw==", - "dev": true - }, - "deprecation": { - "version": "2.3.1", - "resolved": "https://registry.npmjs.org/deprecation/-/deprecation-2.3.1.tgz", - "integrity": "sha512-xmHIy4F3scKVwMsQ4WnVaS8bHOx0DmVwRywosKhaILI0ywMDWPtBSku2HNxRvF7jtwDRsoEwYQSfbxj8b7RlJQ==", - "dev": true - }, - "diff": { - "version": "5.0.0", - "resolved": "https://registry.npmjs.org/diff/-/diff-5.0.0.tgz", - "integrity": "sha512-/VTCrvm5Z0JGty/BWHljh+BAiw3IK+2j87NGMu8Nwc/f48WoDAC395uomO9ZD117ZOBaHmkX1oyLvkVM/aIT3w==", - "dev": true - }, - "dir-glob": { - "version": "3.0.1", - "resolved": "https://registry.npmjs.org/dir-glob/-/dir-glob-3.0.1.tgz", - "integrity": "sha512-WkrWp9GR4KXfKGYzOLmTuGVi1UWFfws377n9cc55/tb6DuqyF6pcQ5AbiHEshaDpY9v6oaSr2XCDidGmMwdzIA==", - "dev": true, - "requires": { - "path-type": "^4.0.0" - } - }, - "doctrine": { - "version": "3.0.0", - "resolved": "https://registry.npmjs.org/doctrine/-/doctrine-3.0.0.tgz", - "integrity": "sha512-yS+Q5i3hBf7GBkd4KG8a7eBNNWNGLTaEwwYWUijIYM7zrlYDM0BFXHjjPWlWZ1Rg7UaddZeIDmi9jF3HmqiQ2w==", - "dev": true, - "requires": { - "esutils": "^2.0.2" - } - }, - "dot-prop": { - "version": "6.0.1", - "resolved": "https://registry.npmjs.org/dot-prop/-/dot-prop-6.0.1.tgz", - "integrity": "sha512-tE7ztYzXHIeyvc7N+hR3oi7FIbf/NIjVP9hmAt3yMXzrQ072/fpjGLx2GxNxGxUl5V73MEqYzioOMoVhGMJ5cA==", - "dev": true, - "requires": { - "is-obj": "^2.0.0" - } - }, - "eastasianwidth": { - "version": "0.2.0", - "resolved": "https://registry.npmjs.org/eastasianwidth/-/eastasianwidth-0.2.0.tgz", - "integrity": "sha512-I88TYZWc9XiYHRQ4/3c5rjjfgkjhLyW2luGIheGERbNQ6OY7yTybanSpDXZa8y7VUP9YmDcYa+eyq4ca7iLqWA==", - "dev": true - }, - "electron-to-chromium": { - "version": "1.4.284", - "resolved": "https://registry.npmjs.org/electron-to-chromium/-/electron-to-chromium-1.4.284.tgz", - "integrity": "sha512-M8WEXFuKXMYMVr45fo8mq0wUrrJHheiKZf6BArTKk9ZBYCKJEOU5H8cdWgDT+qCVZf7Na4lVUaZsA+h6uA9+PA==", - "dev": true - }, - "email-addresses": { - "version": "5.0.0", - "resolved": "https://registry.npmjs.org/email-addresses/-/email-addresses-5.0.0.tgz", - "integrity": "sha512-4OIPYlA6JXqtVn8zpHpGiI7vE6EQOAg16aGnDMIAlZVinnoZ8208tW1hAbjWydgN/4PLTT9q+O1K6AH/vALJGw==", - "dev": true - }, - "emoji-regex": { - "version": "9.2.2", - "resolved": "https://registry.npmjs.org/emoji-regex/-/emoji-regex-9.2.2.tgz", - "integrity": "sha512-L18DaJsXSUk2+42pv8mLs5jJT2hqFkFE4j21wOmgbUqsZ2hL72NsUU785g9RXgo3s0ZNgVl42TiHp3ZtOv/Vyg==", - "dev": true - }, - "error-ex": { - "version": "1.3.2", - "resolved": "https://registry.npmjs.org/error-ex/-/error-ex-1.3.2.tgz", - "integrity": "sha512-7dFHNmqeFSEt2ZBsCriorKnn3Z2pj+fd9kmI6QoWw4//DL+icEBfc0U7qJCisqrTsKTjw4fNFy2pW9OqStD84g==", - "dev": true, - "requires": { - "is-arrayish": "^0.2.1" - } - }, - "es-abstract": { - "version": "1.21.1", - "resolved": "https://registry.npmjs.org/es-abstract/-/es-abstract-1.21.1.tgz", - "integrity": "sha512-QudMsPOz86xYz/1dG1OuGBKOELjCh99IIWHLzy5znUB6j8xG2yMA7bfTV86VSqKF+Y/H08vQPR+9jyXpuC6hfg==", - "dev": true, - "requires": { - "available-typed-arrays": "^1.0.5", - "call-bind": "^1.0.2", - "es-set-tostringtag": "^2.0.1", - "es-to-primitive": "^1.2.1", - "function-bind": "^1.1.1", - "function.prototype.name": "^1.1.5", - "get-intrinsic": "^1.1.3", - "get-symbol-description": "^1.0.0", - "globalthis": "^1.0.3", - "gopd": "^1.0.1", - "has": "^1.0.3", - "has-property-descriptors": "^1.0.0", - "has-proto": "^1.0.1", - "has-symbols": "^1.0.3", - "internal-slot": "^1.0.4", - "is-array-buffer": "^3.0.1", - "is-callable": "^1.2.7", - "is-negative-zero": "^2.0.2", - "is-regex": "^1.1.4", - "is-shared-array-buffer": "^1.0.2", - "is-string": "^1.0.7", - "is-typed-array": "^1.1.10", - "is-weakref": "^1.0.2", - "object-inspect": "^1.12.2", - "object-keys": "^1.1.1", - "object.assign": "^4.1.4", - "regexp.prototype.flags": "^1.4.3", - "safe-regex-test": "^1.0.0", - "string.prototype.trimend": "^1.0.6", - "string.prototype.trimstart": "^1.0.6", - "typed-array-length": "^1.0.4", - "unbox-primitive": "^1.0.2", - "which-typed-array": "^1.1.9" - } - }, - "es-array-method-boxes-properly": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/es-array-method-boxes-properly/-/es-array-method-boxes-properly-1.0.0.tgz", - "integrity": "sha512-wd6JXUmyHmt8T5a2xreUwKcGPq6f1f+WwIJkijUqiGcJz1qqnZgP6XIK+QyIWU5lT7imeNxUll48bziG+TSYcA==", - "dev": true - }, - "es-get-iterator": { - "version": "1.1.3", - "resolved": "https://registry.npmjs.org/es-get-iterator/-/es-get-iterator-1.1.3.tgz", - "integrity": "sha512-sPZmqHBe6JIiTfN5q2pEi//TwxmAFHwj/XEuYjTuse78i8KxaqMTTzxPoFKuzRpDpTJ+0NAbpfenkmH2rePtuw==", - "dev": true, - "requires": { - "call-bind": "^1.0.2", - "get-intrinsic": "^1.1.3", - "has-symbols": "^1.0.3", - "is-arguments": "^1.1.1", - "is-map": "^2.0.2", - "is-set": "^2.0.2", - "is-string": "^1.0.7", - "isarray": "^2.0.5", - "stop-iteration-iterator": "^1.0.0" - } - }, - "es-set-tostringtag": { - "version": "2.0.1", - "resolved": "https://registry.npmjs.org/es-set-tostringtag/-/es-set-tostringtag-2.0.1.tgz", - "integrity": "sha512-g3OMbtlwY3QewlqAiMLI47KywjWZoEytKr8pf6iTC8uJq5bIAH52Z9pnQ8pVL6whrCto53JZDuUIsifGeLorTg==", - "dev": true, - "requires": { - "get-intrinsic": "^1.1.3", - "has": "^1.0.3", - "has-tostringtag": "^1.0.0" - } - }, - "es-to-primitive": { - "version": "1.2.1", - "resolved": "https://registry.npmjs.org/es-to-primitive/-/es-to-primitive-1.2.1.tgz", - "integrity": "sha512-QCOllgZJtaUo9miYBcLChTUaHNjJF3PYs1VidD7AwiEj1kYxKeQTctLAezAOH5ZKRH0g2IgPn6KwB4IT8iRpvA==", - "dev": true, - "requires": { - "is-callable": "^1.1.4", - "is-date-object": "^1.0.1", - "is-symbol": "^1.0.2" - } - }, - "es6-error": { - "version": "4.1.1", - "resolved": "https://registry.npmjs.org/es6-error/-/es6-error-4.1.1.tgz", - "integrity": "sha512-Um/+FxMr9CISWh0bi5Zv0iOD+4cFh5qLeks1qhAopKVAJw3drgKbKySikp7wGhDL0HPeaja0P5ULZrxLkniUVg==", - "dev": true - }, - "escalade": { - "version": "3.1.1", - "resolved": "https://registry.npmjs.org/escalade/-/escalade-3.1.1.tgz", - "integrity": "sha512-k0er2gUkLf8O0zKJiAhmkTnJlTvINGv7ygDNPbeIsX/TJjGJZHuh9B2UxbsaEkmlEo9MfhrSzmhIlhRlI2GXnw==", - "dev": true - }, - "escape-goat": { - "version": "4.0.0", - "resolved": "https://registry.npmjs.org/escape-goat/-/escape-goat-4.0.0.tgz", - "integrity": "sha512-2Sd4ShcWxbx6OY1IHyla/CVNwvg7XwZVoXZHcSu9w9SReNP1EzzD5T8NWKIR38fIqEns9kDWKUQTXXAmlDrdPg==", - "dev": true - }, - "escape-string-regexp": { - "version": "5.0.0", - "resolved": "https://registry.npmjs.org/escape-string-regexp/-/escape-string-regexp-5.0.0.tgz", - "integrity": "sha512-/veY75JbMK4j1yjvuUxuVsiS/hr/4iHs9FTT6cgTexxdE0Ly/glccBAkloH/DofkjRbZU3bnoj38mOmhkZ0lHw==", - "dev": true - }, - "escodegen": { - "version": "1.14.3", - "resolved": "https://registry.npmjs.org/escodegen/-/escodegen-1.14.3.tgz", - "integrity": "sha512-qFcX0XJkdg+PB3xjZZG/wKSuT1PnQWx57+TVSjIMmILd2yC/6ByYElPwJnslDsuWuSAp4AwJGumarAAmJch5Kw==", - "dev": true, - "requires": { - "esprima": "^4.0.1", - "estraverse": "^4.2.0", - "esutils": "^2.0.2", - "optionator": "^0.8.1", - "source-map": "~0.6.1" - }, - "dependencies": { - "levn": { - "version": "0.3.0", - "resolved": "https://registry.npmjs.org/levn/-/levn-0.3.0.tgz", - "integrity": "sha512-0OO4y2iOHix2W6ujICbKIaEQXvFQHue65vUG3pb5EUomzPI90z9hsA1VsO/dbIIpC53J8gxM9Q4Oho0jrCM/yA==", - "dev": true, - "requires": { - "prelude-ls": "~1.1.2", - "type-check": "~0.3.2" - } - }, - "optionator": { - "version": "0.8.3", - "resolved": "https://registry.npmjs.org/optionator/-/optionator-0.8.3.tgz", - "integrity": "sha512-+IW9pACdk3XWmmTXG8m3upGUJst5XRGzxMRjXzAuJ1XnIFNvfhjjIuYkDvysnPQ7qzqVzLt78BCruntqRhWQbA==", - "dev": true, - "requires": { - "deep-is": "~0.1.3", - "fast-levenshtein": "~2.0.6", - "levn": "~0.3.0", - "prelude-ls": "~1.1.2", - "type-check": "~0.3.2", - "word-wrap": "~1.2.3" - } - }, - "prelude-ls": { - "version": "1.1.2", - "resolved": "https://registry.npmjs.org/prelude-ls/-/prelude-ls-1.1.2.tgz", - "integrity": "sha512-ESF23V4SKG6lVSGZgYNpbsiaAkdab6ZgOxe52p7+Kid3W3u3bxR4Vfd/o21dmN7jSt0IwgZ4v5MUd26FEtXE9w==", - "dev": true - }, - "type-check": { - "version": "0.3.2", - "resolved": "https://registry.npmjs.org/type-check/-/type-check-0.3.2.tgz", - "integrity": "sha512-ZCmOJdvOWDBYJlzAoFkC+Q0+bUyEOS1ltgp1MGU03fqHG+dbi9tBFU2Rd9QKiDZFAYrhPh2JUf7rZRIuHRKtOg==", - "dev": true, - "requires": { - "prelude-ls": "~1.1.2" - } - } - } - }, - "eslint": { - "version": "8.32.0", - "resolved": "https://registry.npmjs.org/eslint/-/eslint-8.32.0.tgz", - "integrity": "sha512-nETVXpnthqKPFyuY2FNjz/bEd6nbosRgKbkgS/y1C7LJop96gYHWpiguLecMHQ2XCPxn77DS0P+68WzG6vkZSQ==", - "dev": true, - "requires": { - "@eslint/eslintrc": "^1.4.1", - "@humanwhocodes/config-array": "^0.11.8", - "@humanwhocodes/module-importer": "^1.0.1", - "@nodelib/fs.walk": "^1.2.8", - "ajv": "^6.10.0", - "chalk": "^4.0.0", - "cross-spawn": "^7.0.2", - "debug": "^4.3.2", - "doctrine": "^3.0.0", - "escape-string-regexp": "^4.0.0", - "eslint-scope": "^7.1.1", - "eslint-utils": "^3.0.0", - "eslint-visitor-keys": "^3.3.0", - "espree": "^9.4.0", - "esquery": "^1.4.0", - "esutils": "^2.0.2", - "fast-deep-equal": "^3.1.3", - "file-entry-cache": "^6.0.1", - "find-up": "^5.0.0", - "glob-parent": "^6.0.2", - "globals": "^13.19.0", - "grapheme-splitter": "^1.0.4", - "ignore": "^5.2.0", - "import-fresh": "^3.0.0", - "imurmurhash": "^0.1.4", - "is-glob": "^4.0.0", - "is-path-inside": "^3.0.3", - "js-sdsl": "^4.1.4", - "js-yaml": "^4.1.0", - "json-stable-stringify-without-jsonify": "^1.0.1", - "levn": "^0.4.1", - "lodash.merge": "^4.6.2", - "minimatch": "^3.1.2", - "natural-compare": "^1.4.0", - "optionator": "^0.9.1", - "regexpp": "^3.2.0", - "strip-ansi": "^6.0.1", - "strip-json-comments": "^3.1.0", - "text-table": "^0.2.0" - }, - "dependencies": { - "ansi-regex": { - "version": "5.0.1", - "resolved": "https://registry.npmjs.org/ansi-regex/-/ansi-regex-5.0.1.tgz", - "integrity": "sha512-quJQXlTSUGL2LH9SUXo8VwsY4soanhgo6LNSm84E1LBcE8s3O0wpdiRzyR9z/ZZJMlMWv37qOOb9pdJlMUEKFQ==", - "dev": true - }, - "ansi-styles": { - "version": "4.3.0", - "resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-4.3.0.tgz", - "integrity": "sha512-zbB9rCJAT1rbjiVDb2hqKFHNYLxgtk8NURxZ3IZwD3F6NtxbXZQCnnSi1Lkx+IDohdPlFp222wVALIheZJQSEg==", - "dev": true, - "requires": { - "color-convert": "^2.0.1" - } - }, - "chalk": { - "version": "4.1.2", - "resolved": "https://registry.npmjs.org/chalk/-/chalk-4.1.2.tgz", - "integrity": "sha512-oKnbhFyRIXpUuez8iBMmyEa4nbj4IOQyuhc/wy9kY7/WVPcwIO9VA668Pu8RkO7+0G76SLROeyw9CpQ061i4mA==", - "dev": true, - "requires": { - "ansi-styles": "^4.1.0", - "supports-color": "^7.1.0" - } - }, - "escape-string-regexp": { - "version": "4.0.0", - "resolved": "https://registry.npmjs.org/escape-string-regexp/-/escape-string-regexp-4.0.0.tgz", - "integrity": "sha512-TtpcNJ3XAzx3Gq8sWRzJaVajRs0uVxA2YAkdb1jm2YkPz4G6egUFAyA3n5vtEIZefPk5Wa4UXbKuS5fKkJWdgA==", - "dev": true - }, - "eslint-scope": { - "version": "7.1.1", - "resolved": "https://registry.npmjs.org/eslint-scope/-/eslint-scope-7.1.1.tgz", - "integrity": "sha512-QKQM/UXpIiHcLqJ5AOyIW7XZmzjkzQXYE54n1++wb0u9V/abW3l9uQnxX8Z5Xd18xyKIMTUAyQ0k1e8pz6LUrw==", - "dev": true, - "requires": { - "esrecurse": "^4.3.0", - "estraverse": "^5.2.0" - } - }, - "estraverse": { - "version": "5.3.0", - "resolved": "https://registry.npmjs.org/estraverse/-/estraverse-5.3.0.tgz", - "integrity": "sha512-MMdARuVEQziNTeJD8DgMqmhwR11BRQ/cBP+pLtYdSTnf3MIO8fFeiINEbX36ZdNlfU/7A9f3gUw49B3oQsvwBA==", - "dev": true - }, - "find-up": { - "version": "5.0.0", - "resolved": "https://registry.npmjs.org/find-up/-/find-up-5.0.0.tgz", - "integrity": "sha512-78/PXT1wlLLDgTzDs7sjq9hzz0vXD+zn+7wypEe4fXQxCmdmqfGsEPQxmiCSQI3ajFV91bVSsvNtrJRiW6nGng==", - "dev": true, - "requires": { - "locate-path": "^6.0.0", - "path-exists": "^4.0.0" - } - }, - "locate-path": { - "version": "6.0.0", - "resolved": "https://registry.npmjs.org/locate-path/-/locate-path-6.0.0.tgz", - "integrity": "sha512-iPZK6eYjbxRu3uB4/WZ3EsEIMJFMqAoopl3R+zuq0UjcAm/MO6KCweDgPfP3elTztoKP3KtnVHxTn2NHBSDVUw==", - "dev": true, - "requires": { - "p-locate": "^5.0.0" - } - }, - "p-limit": { - "version": "3.1.0", - "resolved": "https://registry.npmjs.org/p-limit/-/p-limit-3.1.0.tgz", - "integrity": "sha512-TYOanM3wGwNGsZN2cVTYPArw454xnXj5qmWF1bEoAc4+cU/ol7GVh7odevjp1FNHduHc3KZMcFduxU5Xc6uJRQ==", - "dev": true, - "requires": { - "yocto-queue": "^0.1.0" - } - }, - "p-locate": { - "version": "5.0.0", - "resolved": "https://registry.npmjs.org/p-locate/-/p-locate-5.0.0.tgz", - "integrity": "sha512-LaNjtRWUBY++zB5nE/NwcaoMylSPk+S+ZHNB1TzdbMJMny6dynpAGt7X/tl/QYq3TIeE6nxHppbo2LGymrG5Pw==", - "dev": true, - "requires": { - "p-limit": "^3.0.2" - } - }, - "strip-ansi": { - "version": "6.0.1", - "resolved": "https://registry.npmjs.org/strip-ansi/-/strip-ansi-6.0.1.tgz", - "integrity": "sha512-Y38VPSHcqkFrCpFnQ9vuSXmquuv5oXOKpGeT6aGrr3o3Gc9AlVa6JBfUSOCnbxGGZF+/0ooI7KrPuUSztUdU5A==", - "dev": true, - "requires": { - "ansi-regex": "^5.0.1" - } - } - } - }, - "eslint-scope": { - "version": "5.1.1", - "resolved": "https://registry.npmjs.org/eslint-scope/-/eslint-scope-5.1.1.tgz", - "integrity": "sha512-2NxwbF/hZ0KpepYN0cNbo+FN6XoK7GaHlQhgx/hIZl6Va0bF45RQOOwhLIy8lQDbuCiadSLCBnH2CFYquit5bw==", - "dev": true, - "requires": { - "esrecurse": "^4.3.0", - "estraverse": "^4.1.1" - } - }, - "eslint-utils": { - "version": "3.0.0", - "resolved": "https://registry.npmjs.org/eslint-utils/-/eslint-utils-3.0.0.tgz", - "integrity": "sha512-uuQC43IGctw68pJA1RgbQS8/NP7rch6Cwd4j3ZBtgo4/8Flj4eGE7ZYSZRN3iq5pVUv6GPdW5Z1RFleo84uLDA==", - "dev": true, - "requires": { - "eslint-visitor-keys": "^2.0.0" - }, - "dependencies": { - "eslint-visitor-keys": { - "version": "2.1.0", - "resolved": "https://registry.npmjs.org/eslint-visitor-keys/-/eslint-visitor-keys-2.1.0.tgz", - "integrity": "sha512-0rSmRBzXgDzIsD6mGdJgevzgezI534Cer5L/vyMX0kHzT/jiB43jRhd9YUlMGYLQy2zprNmoT8qasCGtY+QaKw==", - "dev": true - } - } - }, - "eslint-visitor-keys": { - "version": "3.3.0", - "resolved": "https://registry.npmjs.org/eslint-visitor-keys/-/eslint-visitor-keys-3.3.0.tgz", - "integrity": "sha512-mQ+suqKJVyeuwGYHAdjMFqjCyfl8+Ldnxuyp3ldiMBFKkvytrXUZWaiPCEav8qDHKty44bD+qV1IP4T+w+xXRA==", - "dev": true - }, - "espree": { - "version": "9.4.1", - "resolved": "https://registry.npmjs.org/espree/-/espree-9.4.1.tgz", - "integrity": "sha512-XwctdmTO6SIvCzd9810yyNzIrOrqNYV9Koizx4C/mRhf9uq0o4yHoCEU/670pOxOL/MSraektvSAji79kX90Vg==", - "dev": true, - "requires": { - "acorn": "^8.8.0", - "acorn-jsx": "^5.3.2", - "eslint-visitor-keys": "^3.3.0" - } - }, - "esprima": { - "version": "4.0.1", - "resolved": "https://registry.npmjs.org/esprima/-/esprima-4.0.1.tgz", - "integrity": "sha512-eGuFFw7Upda+g4p+QHvnW0RyTX/SVeJBDM/gCtMARO0cLuT2HcEKnTPvhjV6aGeqrCB/sbNop0Kszm0jsaWU4A==", - "dev": true - }, - "esquery": { - "version": "1.4.0", - "resolved": "https://registry.npmjs.org/esquery/-/esquery-1.4.0.tgz", - "integrity": "sha512-cCDispWt5vHHtwMY2YrAQ4ibFkAL8RbH5YGBnZBc90MolvvfkkQcJro/aZiAQUlQ3qgrYS6D6v8Gc5G5CQsc9w==", - "dev": true, - "requires": { - "estraverse": "^5.1.0" - }, - "dependencies": { - "estraverse": { - "version": "5.3.0", - "resolved": "https://registry.npmjs.org/estraverse/-/estraverse-5.3.0.tgz", - "integrity": "sha512-MMdARuVEQziNTeJD8DgMqmhwR11BRQ/cBP+pLtYdSTnf3MIO8fFeiINEbX36ZdNlfU/7A9f3gUw49B3oQsvwBA==", - "dev": true - } - } - }, - "esrecurse": { - "version": "4.3.0", - "resolved": "https://registry.npmjs.org/esrecurse/-/esrecurse-4.3.0.tgz", - "integrity": "sha512-KmfKL3b6G+RXvP8N1vr3Tq1kL/oCFgn2NYXEtqP8/L3pKapUA4G8cFVaoF3SU323CD4XypR/ffioHmkti6/Tag==", - "dev": true, - "requires": { - "estraverse": "^5.2.0" - }, - "dependencies": { - "estraverse": { - "version": "5.3.0", - "resolved": "https://registry.npmjs.org/estraverse/-/estraverse-5.3.0.tgz", - "integrity": "sha512-MMdARuVEQziNTeJD8DgMqmhwR11BRQ/cBP+pLtYdSTnf3MIO8fFeiINEbX36ZdNlfU/7A9f3gUw49B3oQsvwBA==", - "dev": true - } - } - }, - "estraverse": { - "version": "4.3.0", - "resolved": "https://registry.npmjs.org/estraverse/-/estraverse-4.3.0.tgz", - "integrity": "sha512-39nnKffWz8xN1BU/2c79n9nB9HDzo0niYUqx6xyqUnyoAnQyyWpOTdZEeiCch8BBu515t4wp9ZmgVfVhn9EBpw==", - "dev": true - }, - "esutils": { - "version": "2.0.3", - "resolved": "https://registry.npmjs.org/esutils/-/esutils-2.0.3.tgz", - "integrity": "sha512-kVscqXk4OCp68SZ0dkgEKVi6/8ij300KBWTJq32P/dYeWTSwK41WyTxalN1eRmA5Z9UU/LX9D7FWSmV9SAYx6g==", - "dev": true - }, - "execa": { - "version": "6.1.0", - "resolved": "https://registry.npmjs.org/execa/-/execa-6.1.0.tgz", - "integrity": "sha512-QVWlX2e50heYJcCPG0iWtf8r0xjEYfz/OYLGDYH+IyjWezzPNxz63qNFOu0l4YftGWuizFVZHHs8PrLU5p2IDA==", - "dev": true, - "requires": { - "cross-spawn": "^7.0.3", - "get-stream": "^6.0.1", - "human-signals": "^3.0.1", - "is-stream": "^3.0.0", - "merge-stream": "^2.0.0", - "npm-run-path": "^5.1.0", - "onetime": "^6.0.0", - "signal-exit": "^3.0.7", - "strip-final-newline": "^3.0.0" - } - }, - "external-editor": { - "version": "3.1.0", - "resolved": "https://registry.npmjs.org/external-editor/-/external-editor-3.1.0.tgz", - "integrity": "sha512-hMQ4CX1p1izmuLYyZqLMO/qGNw10wSv9QDCPfzXfyFrOaCSSoRfqE1Kf1s5an66J5JZC62NewG+mK49jOCtQew==", - "dev": true, - "requires": { - "chardet": "^0.7.0", - "iconv-lite": "^0.4.24", - "tmp": "^0.0.33" - } - }, - "fast-deep-equal": { - "version": "3.1.3", - "resolved": "https://registry.npmjs.org/fast-deep-equal/-/fast-deep-equal-3.1.3.tgz", - "integrity": "sha512-f3qQ9oQy9j2AhBe/H9VC91wLmKBCCU/gDOnKNAYG5hswO7BLKj09Hc5HYNz9cGI++xlpDCIgDaitVs03ATR84Q==", - "dev": true - }, - "fast-glob": { - "version": "3.2.12", - "resolved": "https://registry.npmjs.org/fast-glob/-/fast-glob-3.2.12.tgz", - "integrity": "sha512-DVj4CQIYYow0BlaelwK1pHl5n5cRSJfM60UA0zK891sVInoPri2Ekj7+e1CT3/3qxXenpI+nBBmQAcJPJgaj4w==", - "dev": true, - "requires": { - "@nodelib/fs.stat": "^2.0.2", - "@nodelib/fs.walk": "^1.2.3", - "glob-parent": "^5.1.2", - "merge2": "^1.3.0", - "micromatch": "^4.0.4" - }, - "dependencies": { - "glob-parent": { - "version": "5.1.2", - "resolved": "https://registry.npmjs.org/glob-parent/-/glob-parent-5.1.2.tgz", - "integrity": "sha512-AOIgSQCepiJYwP3ARnGx+5VnTu2HBYdzbGP45eLw1vr3zB3vZLeyed1sC9hnbcOc9/SrMyM5RPQrkGz4aS9Zow==", - "dev": true, - "requires": { - "is-glob": "^4.0.1" - } - } - } - }, - "fast-json-stable-stringify": { - "version": "2.1.0", - "resolved": "https://registry.npmjs.org/fast-json-stable-stringify/-/fast-json-stable-stringify-2.1.0.tgz", - "integrity": "sha512-lhd/wF+Lk98HZoTCtlVraHtfh5XYijIjalXck7saUtuanSDyLMxnHhSXEDJqHxD7msR8D0uCmqlkwjCV8xvwHw==", - "dev": true - }, - "fast-levenshtein": { - "version": "2.0.6", - "resolved": "https://registry.npmjs.org/fast-levenshtein/-/fast-levenshtein-2.0.6.tgz", - "integrity": "sha512-DCXu6Ifhqcks7TZKY3Hxp3y6qphY5SJZmrWMDrKcERSOXWQdMhU9Ig/PYrzyw/ul9jOIyh0N4M0tbC5hodg8dw==", - "dev": true - }, - "fastq": { - "version": "1.15.0", - "resolved": "https://registry.npmjs.org/fastq/-/fastq-1.15.0.tgz", - "integrity": "sha512-wBrocU2LCXXa+lWBt8RoIRD89Fi8OdABODa/kEnyeyjS5aZO5/GNvI5sEINADqP/h8M29UHTHUb53sUu5Ihqdw==", - "dev": true, - "requires": { - "reusify": "^1.0.4" - } - }, - "fetch-blob": { - "version": "3.2.0", - "resolved": "https://registry.npmjs.org/fetch-blob/-/fetch-blob-3.2.0.tgz", - "integrity": "sha512-7yAQpD2UMJzLi1Dqv7qFYnPbaPx7ZfFK6PiIxQ4PfkGPyNyl2Ugx+a/umUonmKqjhM4DnfbMvdX6otXq83soQQ==", - "dev": true, - "requires": { - "node-domexception": "^1.0.0", - "web-streams-polyfill": "^3.0.3" - } - }, - "figures": { - "version": "5.0.0", - "resolved": "https://registry.npmjs.org/figures/-/figures-5.0.0.tgz", - "integrity": "sha512-ej8ksPF4x6e5wvK9yevct0UCXh8TTFlWGVLlgjZuoBH1HwjIfKE/IdL5mq89sFA7zELi1VhKpmtDnrs7zWyeyg==", - "dev": true, - "requires": { - "escape-string-regexp": "^5.0.0", - "is-unicode-supported": "^1.2.0" - } - }, - "file-entry-cache": { - "version": "6.0.1", - "resolved": "https://registry.npmjs.org/file-entry-cache/-/file-entry-cache-6.0.1.tgz", - "integrity": "sha512-7Gps/XWymbLk2QLYK4NzpMOrYjMhdIxXuIvy2QBsLE6ljuodKvdkWs/cpyJJ3CVIVpH0Oi1Hvg1ovbMzLdFBBg==", - "dev": true, - "requires": { - "flat-cache": "^3.0.4" - } - }, - "file-uri-to-path": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/file-uri-to-path/-/file-uri-to-path-2.0.0.tgz", - "integrity": "sha512-hjPFI8oE/2iQPVe4gbrJ73Pp+Xfub2+WI2LlXDbsaJBwT5wuMh35WNWVYYTpnz895shtwfyutMFLFywpQAFdLg==", - "dev": true - }, - "filename-reserved-regex": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/filename-reserved-regex/-/filename-reserved-regex-2.0.0.tgz", - "integrity": "sha512-lc1bnsSr4L4Bdif8Xb/qrtokGbq5zlsms/CYH8PP+WtCkGNF65DPiQY8vG3SakEdRn8Dlnm+gW/qWKKjS5sZzQ==", - "dev": true - }, - "filenamify": { - "version": "4.3.0", - "resolved": "https://registry.npmjs.org/filenamify/-/filenamify-4.3.0.tgz", - "integrity": "sha512-hcFKyUG57yWGAzu1CMt/dPzYZuv+jAJUT85bL8mrXvNe6hWj6yEHEc4EdcgiA6Z3oi1/9wXJdZPXF2dZNgwgOg==", - "dev": true, - "requires": { - "filename-reserved-regex": "^2.0.0", - "strip-outer": "^1.0.1", - "trim-repeated": "^1.0.0" - } - }, - "fill-range": { - "version": "7.0.1", - "resolved": "https://registry.npmjs.org/fill-range/-/fill-range-7.0.1.tgz", - "integrity": "sha512-qOo9F+dMUmC2Lcb4BbVvnKJxTPjCm+RRpe4gDuGrzkL7mEVl/djYSu2OdQ2Pa302N4oqkSg9ir6jaLWJ2USVpQ==", - "dev": true, - "requires": { - "to-regex-range": "^5.0.1" - } - }, - "find-cache-dir": { - "version": "3.3.2", - "resolved": "https://registry.npmjs.org/find-cache-dir/-/find-cache-dir-3.3.2.tgz", - "integrity": "sha512-wXZV5emFEjrridIgED11OoUKLxiYjAcqot/NJdAkOhlJ+vGzwhOAfcG5OX1jP+S0PcjEn8bdMJv+g2jwQ3Onig==", - "dev": true, - "requires": { - "commondir": "^1.0.1", - "make-dir": "^3.0.2", - "pkg-dir": "^4.1.0" - } - }, - "find-up": { - "version": "4.1.0", - "resolved": "https://registry.npmjs.org/find-up/-/find-up-4.1.0.tgz", - "integrity": "sha512-PpOwAdQ/YlXQ2vj8a3h8IipDuYRi3wceVQQGYWxNINccq40Anw7BlsEXCMbt1Zt+OLA6Fq9suIpIWD0OsnISlw==", - "dev": true, - "requires": { - "locate-path": "^5.0.0", - "path-exists": "^4.0.0" - } - }, - "flat": { - "version": "5.0.2", - "resolved": "https://registry.npmjs.org/flat/-/flat-5.0.2.tgz", - "integrity": "sha512-b6suED+5/3rTpUBdG1gupIl8MPFCAMA0QXwmljLhvCUKcUvdE4gWky9zpuGCcXHOsz4J9wPGNWq6OKpmIzz3hQ==", - "dev": true - }, - "flat-cache": { - "version": "3.0.4", - "resolved": "https://registry.npmjs.org/flat-cache/-/flat-cache-3.0.4.tgz", - "integrity": "sha512-dm9s5Pw7Jc0GvMYbshN6zchCA9RgQlzzEZX3vylR9IqFfS8XciblUXOKfW6SiuJ0e13eDYZoZV5wdrev7P3Nwg==", - "dev": true, - "requires": { - "flatted": "^3.1.0", - "rimraf": "^3.0.2" - } - }, - "flatted": { - "version": "3.2.7", - "resolved": "https://registry.npmjs.org/flatted/-/flatted-3.2.7.tgz", - "integrity": "sha512-5nqDSxl8nn5BSNxyR3n4I6eDmbolI6WT+QqR547RwxQapgjQBmtktdP+HTBb/a/zLsbzERTONyUB5pefh5TtjQ==", - "dev": true - }, - "for-each": { - "version": "0.3.3", - "resolved": "https://registry.npmjs.org/for-each/-/for-each-0.3.3.tgz", - "integrity": "sha512-jqYfLp7mo9vIyQf8ykW2v7A+2N4QjeCeI5+Dz9XraiO1ign81wjiH7Fb9vSOWvQfNtmSa4H2RoQTrrXivdUZmw==", - "dev": true, - "requires": { - "is-callable": "^1.1.3" - } - }, - "foreground-child": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/foreground-child/-/foreground-child-2.0.0.tgz", - "integrity": "sha512-dCIq9FpEcyQyXKCkyzmlPTFNgrCzPudOe+mhvJU5zAtlBnGVy2yKxtfsxK2tQBThwq225jcvBjpw1Gr40uzZCA==", - "dev": true, - "requires": { - "cross-spawn": "^7.0.0", - "signal-exit": "^3.0.2" - } - }, - "form-data-encoder": { - "version": "2.1.4", - "resolved": "https://registry.npmjs.org/form-data-encoder/-/form-data-encoder-2.1.4.tgz", - "integrity": "sha512-yDYSgNMraqvnxiEXO4hi88+YZxaHC6QKzb5N84iRCTDeRO7ZALpir/lVmf/uXUhnwUr2O4HU8s/n6x+yNjQkHw==", - "dev": true - }, - "formdata-polyfill": { - "version": "4.0.10", - "resolved": "https://registry.npmjs.org/formdata-polyfill/-/formdata-polyfill-4.0.10.tgz", - "integrity": "sha512-buewHzMvYL29jdeQTVILecSaZKnt/RJWjoZCF5OW60Z67/GmSLBkOFM7qh1PI3zFNtJbaZL5eQu1vLfazOwj4g==", - "dev": true, - "requires": { - "fetch-blob": "^3.1.2" - } - }, - "fromentries": { - "version": "1.3.2", - "resolved": "https://registry.npmjs.org/fromentries/-/fromentries-1.3.2.tgz", - "integrity": "sha512-cHEpEQHUg0f8XdtZCc2ZAhrHzKzT0MrFUTcvx+hfxYu7rGMDc5SKoXFh+n4YigxsHXRzc6OrCshdR1bWH6HHyg==", - "dev": true - }, - "fs-extra": { - "version": "8.1.0", - "resolved": "https://registry.npmjs.org/fs-extra/-/fs-extra-8.1.0.tgz", - "integrity": "sha512-yhlQgA6mnOJUKOsRUFsgJdQCvkKhcz8tlZG5HBQfReYZy46OwLcY+Zia0mtdHsOo9y/hP+CxMN0TU9QxoOtG4g==", - "dev": true, - "requires": { - "graceful-fs": "^4.2.0", - "jsonfile": "^4.0.0", - "universalify": "^0.1.0" - } - }, - "fs.realpath": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/fs.realpath/-/fs.realpath-1.0.0.tgz", - "integrity": "sha512-OO0pH2lK6a0hZnAdau5ItzHPI6pUlvI7jMVnxUQRtw4owF2wk8lOSabtGDCTP4Ggrg2MbGnWO9X8K1t4+fGMDw==", - "dev": true - }, - "fsevents": { - "version": "2.3.2", - "resolved": "https://registry.npmjs.org/fsevents/-/fsevents-2.3.2.tgz", - "integrity": "sha512-xiqMQR4xAeHTuB9uWm+fFRcIOgKBMiOBP+eXiyT7jsgVCq1bkVygt00oASowB7EdtpOHaaPgKt812P9ab+DDKA==", - "dev": true, - "optional": true - }, - "ftp": { - "version": "0.3.10", - "resolved": "https://registry.npmjs.org/ftp/-/ftp-0.3.10.tgz", - "integrity": "sha512-faFVML1aBx2UoDStmLwv2Wptt4vw5x03xxX172nhA5Y5HBshW5JweqQ2W4xL4dezQTG8inJsuYcpPHHU3X5OTQ==", - "dev": true, - "requires": { - "readable-stream": "1.1.x", - "xregexp": "2.0.0" - }, - "dependencies": { - "isarray": { - "version": "0.0.1", - "resolved": "https://registry.npmjs.org/isarray/-/isarray-0.0.1.tgz", - "integrity": "sha512-D2S+3GLxWH+uhrNEcoh/fnmYeP8E8/zHl644d/jdA0g2uyXvy3sb0qxotE+ne0LtccHknQzWwZEzhak7oJ0COQ==", - "dev": true - }, - "readable-stream": { - "version": "1.1.14", - "resolved": "https://registry.npmjs.org/readable-stream/-/readable-stream-1.1.14.tgz", - "integrity": "sha512-+MeVjFf4L44XUkhM1eYbD8fyEsxcV81pqMSR5gblfcLCHfZvbrqy4/qYHE+/R5HoBUT11WV5O08Cr1n3YXkWVQ==", - "dev": true, - "requires": { - "core-util-is": "~1.0.0", - "inherits": "~2.0.1", - "isarray": "0.0.1", - "string_decoder": "~0.10.x" - } - }, - "string_decoder": { - "version": "0.10.31", - "resolved": "https://registry.npmjs.org/string_decoder/-/string_decoder-0.10.31.tgz", - "integrity": "sha512-ev2QzSzWPYmy9GuqfIVildA4OdcGLeFZQrq5ys6RtiuF+RQQiZWr8TZNyAcuVXyQRYfEO+MsoB/1BuQVhOJuoQ==", - "dev": true - } - } - }, - "function-bind": { - "version": "1.1.1", - "resolved": "https://registry.npmjs.org/function-bind/-/function-bind-1.1.1.tgz", - "integrity": "sha512-yIovAzMX49sF8Yl58fSCWJ5svSLuaibPxXQJFLmBObTuCr0Mf1KiPopGM9NiFjiYBCbfaa2Fh6breQ6ANVTI0A==", - "dev": true - }, - "function.prototype.name": { - "version": "1.1.5", - "resolved": "https://registry.npmjs.org/function.prototype.name/-/function.prototype.name-1.1.5.tgz", - "integrity": "sha512-uN7m/BzVKQnCUF/iW8jYea67v++2u7m5UgENbHRtdDVclOUP+FMPlCNdmk0h/ysGyo2tavMJEDqJAkJdRa1vMA==", - "dev": true, - "requires": { - "call-bind": "^1.0.2", - "define-properties": "^1.1.3", - "es-abstract": "^1.19.0", - "functions-have-names": "^1.2.2" - } - }, - "functions-have-names": { - "version": "1.2.3", - "resolved": "https://registry.npmjs.org/functions-have-names/-/functions-have-names-1.2.3.tgz", - "integrity": "sha512-xckBUXyTIqT97tq2x2AMb+g163b5JFysYk0x4qxNFwbfQkmNZoiRHb6sPzI9/QV33WeuvVYBUIiD4NzNIyqaRQ==", - "dev": true - }, - "generic-pool": { - "version": "3.9.0", - "resolved": "https://registry.npmjs.org/generic-pool/-/generic-pool-3.9.0.tgz", - "integrity": "sha512-hymDOu5B53XvN4QT9dBmZxPX4CWhBPPLguTZ9MMFeFa/Kg0xWVfylOVNlJji/E7yTZWFd/q9GO5TxDLq156D7g==" - }, - "gensync": { - "version": "1.0.0-beta.2", - "resolved": "https://registry.npmjs.org/gensync/-/gensync-1.0.0-beta.2.tgz", - "integrity": "sha512-3hN7NaskYvMDLQY55gnW3NQ+mesEAepTqlg+VEbj7zzqEMBVNhzcGYYeqFo/TlYz6eQiFcp1HcsCZO+nGgS8zg==", - "dev": true - }, - "get-caller-file": { - "version": "2.0.5", - "resolved": "https://registry.npmjs.org/get-caller-file/-/get-caller-file-2.0.5.tgz", - "integrity": "sha512-DyFP3BM/3YHTQOCUL/w0OZHR0lpKeGrxotcHWcqNEdnltqFwXVfhEBQ94eIo34AfQpo0rGki4cyIiftY06h2Fg==", - "dev": true - }, - "get-intrinsic": { - "version": "1.1.3", - "resolved": "https://registry.npmjs.org/get-intrinsic/-/get-intrinsic-1.1.3.tgz", - "integrity": "sha512-QJVz1Tj7MS099PevUG5jvnt9tSkXN8K14dxQlikJuPt4uD9hHAHjLyLBiLR5zELelBdD9QNRAXZzsJx0WaDL9A==", - "dev": true, - "requires": { - "function-bind": "^1.1.1", - "has": "^1.0.3", - "has-symbols": "^1.0.3" - } - }, - "get-package-type": { - "version": "0.1.0", - "resolved": "https://registry.npmjs.org/get-package-type/-/get-package-type-0.1.0.tgz", - "integrity": "sha512-pjzuKtY64GYfWizNAJ0fr9VqttZkNiK2iS430LtIHzjBEr6bX8Am2zm4sW4Ro5wjWW5cAlRL1qAMTcXbjNAO2Q==", - "dev": true - }, - "get-stream": { - "version": "6.0.1", - "resolved": "https://registry.npmjs.org/get-stream/-/get-stream-6.0.1.tgz", - "integrity": "sha512-ts6Wi+2j3jQjqi70w5AlN8DFnkSwC+MqmxEzdEALB2qXZYV3X/b1CTfgPLGJNMeAWxdPfU8FO1ms3NUfaHCPYg==", - "dev": true - }, - "get-symbol-description": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/get-symbol-description/-/get-symbol-description-1.0.0.tgz", - "integrity": "sha512-2EmdH1YvIQiZpltCNgkuiUnyukzxM/R6NDJX31Ke3BG1Nq5b0S2PhX59UKi9vZpPDQVdqn+1IcaAwnzTT5vCjw==", - "dev": true, - "requires": { - "call-bind": "^1.0.2", - "get-intrinsic": "^1.1.1" - } - }, - "get-uri": { - "version": "3.0.2", - "resolved": "https://registry.npmjs.org/get-uri/-/get-uri-3.0.2.tgz", - "integrity": "sha512-+5s0SJbGoyiJTZZ2JTpFPLMPSch72KEqGOTvQsBqg0RBWvwhWUSYZFAtz3TPW0GXJuLBJPts1E241iHg+VRfhg==", - "dev": true, - "requires": { - "@tootallnate/once": "1", - "data-uri-to-buffer": "3", - "debug": "4", - "file-uri-to-path": "2", - "fs-extra": "^8.1.0", - "ftp": "^0.3.10" - }, - "dependencies": { - "data-uri-to-buffer": { - "version": "3.0.1", - "resolved": "https://registry.npmjs.org/data-uri-to-buffer/-/data-uri-to-buffer-3.0.1.tgz", - "integrity": "sha512-WboRycPNsVw3B3TL559F7kuBUM4d8CgMEvk6xEJlOp7OBPjt6G7z8WMWlD2rOFZLk6OYfFIUGsCOWzcQH9K2og==", - "dev": true - } - } - }, - "gh-pages": { - "version": "5.0.0", - "resolved": "https://registry.npmjs.org/gh-pages/-/gh-pages-5.0.0.tgz", - "integrity": "sha512-Nqp1SjkPIB94Xw/3yYNTUL+G2dxlhjvv1zeN/4kMC1jfViTEqhtVz/Ba1zSXHuvXCN9ADNS1dN4r5/J/nZWEQQ==", - "dev": true, - "requires": { - "async": "^3.2.4", - "commander": "^2.18.0", - "email-addresses": "^5.0.0", - "filenamify": "^4.3.0", - "find-cache-dir": "^3.3.1", - "fs-extra": "^8.1.0", - "globby": "^6.1.0" - } - }, - "git-up": { - "version": "7.0.0", - "resolved": "https://registry.npmjs.org/git-up/-/git-up-7.0.0.tgz", - "integrity": "sha512-ONdIrbBCFusq1Oy0sC71F5azx8bVkvtZtMJAsv+a6lz5YAmbNnLD6HAB4gptHZVLPR8S2/kVN6Gab7lryq5+lQ==", - "dev": true, - "requires": { - "is-ssh": "^1.4.0", - "parse-url": "^8.1.0" - } - }, - "git-url-parse": { - "version": "13.1.0", - "resolved": "https://registry.npmjs.org/git-url-parse/-/git-url-parse-13.1.0.tgz", - "integrity": "sha512-5FvPJP/70WkIprlUZ33bm4UAaFdjcLkJLpWft1BeZKqwR0uhhNGoKwlUaPtVb4LxCSQ++erHapRak9kWGj+FCA==", - "dev": true, - "requires": { - "git-up": "^7.0.0" - } - }, - "glob": { - "version": "7.2.3", - "resolved": "https://registry.npmjs.org/glob/-/glob-7.2.3.tgz", - "integrity": "sha512-nFR0zLpU2YCaRxwoCJvL6UvCH2JFyFVIvwTLsIf21AuHlMskA1hhTdk+LlYJtOlYt9v6dvszD2BGRqBL+iQK9Q==", - "dev": true, - "requires": { - "fs.realpath": "^1.0.0", - "inflight": "^1.0.4", - "inherits": "2", - "minimatch": "^3.1.1", - "once": "^1.3.0", - "path-is-absolute": "^1.0.0" - } - }, - "glob-parent": { - "version": "6.0.2", - "resolved": "https://registry.npmjs.org/glob-parent/-/glob-parent-6.0.2.tgz", - "integrity": "sha512-XxwI8EOhVQgWp6iDL+3b0r86f4d6AX6zSU55HfB4ydCEuXLXc5FcYeOu+nnGftS4TEju/11rt4KJPTMgbfmv4A==", - "dev": true, - "requires": { - "is-glob": "^4.0.3" - } - }, - "global-dirs": { - "version": "3.0.1", - "resolved": "https://registry.npmjs.org/global-dirs/-/global-dirs-3.0.1.tgz", - "integrity": "sha512-NBcGGFbBA9s1VzD41QXDG+3++t9Mn5t1FpLdhESY6oKY4gYTFpX4wO3sqGUa0Srjtbfj3szX0RnemmrVRUdULA==", - "dev": true, - "requires": { - "ini": "2.0.0" - } - }, - "globals": { - "version": "13.19.0", - "resolved": "https://registry.npmjs.org/globals/-/globals-13.19.0.tgz", - "integrity": "sha512-dkQ957uSRWHw7CFXLUtUHQI3g3aWApYhfNR2O6jn/907riyTYKVBmxYVROkBcY614FSSeSJh7Xm7SrUWCxvJMQ==", - "dev": true, - "requires": { - "type-fest": "^0.20.2" - }, - "dependencies": { - "type-fest": { - "version": "0.20.2", - "resolved": "https://registry.npmjs.org/type-fest/-/type-fest-0.20.2.tgz", - "integrity": "sha512-Ne+eE4r0/iWnpAxD852z3A+N0Bt5RN//NjJwRd2VFHEmrywxf5vsZlh4R6lixl6B+wz/8d+maTSAkN1FIkI3LQ==", - "dev": true - } - } - }, - "globalthis": { - "version": "1.0.3", - "resolved": "https://registry.npmjs.org/globalthis/-/globalthis-1.0.3.tgz", - "integrity": "sha512-sFdI5LyBiNTHjRd7cGPWapiHWMOXKyuBNX/cWJ3NfzrZQVa8GI/8cofCl74AOVqq9W5kNmguTIzJ/1s2gyI9wA==", - "dev": true, - "requires": { - "define-properties": "^1.1.3" - } - }, - "globby": { - "version": "6.1.0", - "resolved": "https://registry.npmjs.org/globby/-/globby-6.1.0.tgz", - "integrity": "sha512-KVbFv2TQtbzCoxAnfD6JcHZTYCzyliEaaeM/gH8qQdkKr5s0OP9scEgvdcngyk7AVdY6YVW/TJHd+lQ/Df3Daw==", - "dev": true, - "requires": { - "array-union": "^1.0.1", - "glob": "^7.0.3", - "object-assign": "^4.0.1", - "pify": "^2.0.0", - "pinkie-promise": "^2.0.0" - } - }, - "gopd": { - "version": "1.0.1", - "resolved": "https://registry.npmjs.org/gopd/-/gopd-1.0.1.tgz", - "integrity": "sha512-d65bNlIadxvpb/A2abVdlqKqV563juRnZ1Wtk6s1sIR8uNsXR70xqIzVqxVf1eTqDunwT2MkczEeaezCKTZhwA==", - "dev": true, - "requires": { - "get-intrinsic": "^1.1.3" - } - }, - "got": { - "version": "12.5.3", - "resolved": "https://registry.npmjs.org/got/-/got-12.5.3.tgz", - "integrity": "sha512-8wKnb9MGU8IPGRIo+/ukTy9XLJBwDiCpIf5TVzQ9Cpol50eMTpBq2GAuDsuDIz7hTYmZgMgC1e9ydr6kSDWs3w==", - "dev": true, - "requires": { - "@sindresorhus/is": "^5.2.0", - "@szmarczak/http-timer": "^5.0.1", - "cacheable-lookup": "^7.0.0", - "cacheable-request": "^10.2.1", - "decompress-response": "^6.0.0", - "form-data-encoder": "^2.1.2", - "get-stream": "^6.0.1", - "http2-wrapper": "^2.1.10", - "lowercase-keys": "^3.0.0", - "p-cancelable": "^3.0.0", - "responselike": "^3.0.0" - } - }, - "graceful-fs": { - "version": "4.2.10", - "resolved": "https://registry.npmjs.org/graceful-fs/-/graceful-fs-4.2.10.tgz", - "integrity": "sha512-9ByhssR2fPVsNZj478qUUbKfmL0+t5BDVyjShtyZZLiK7ZDAArFFfopyOTj0M05wE2tJPisA4iTnnXl2YoPvOA==", - "dev": true - }, - "grapheme-splitter": { - "version": "1.0.4", - "resolved": "https://registry.npmjs.org/grapheme-splitter/-/grapheme-splitter-1.0.4.tgz", - "integrity": "sha512-bzh50DW9kTPM00T8y4o8vQg89Di9oLJVLW/KaOGIXJWP/iqCN6WKYkbNOF04vFLJhwcpYUh9ydh/+5vpOqV4YQ==", - "dev": true - }, - "has": { - "version": "1.0.3", - "resolved": "https://registry.npmjs.org/has/-/has-1.0.3.tgz", - "integrity": "sha512-f2dvO0VU6Oej7RkWJGrehjbzMAjFp5/VKPp5tTpWIV4JHHZK1/BxbFRtf/siA2SWTe09caDmVtYYzWEIbBS4zw==", - "dev": true, - "requires": { - "function-bind": "^1.1.1" - } - }, - "has-bigints": { - "version": "1.0.2", - "resolved": "https://registry.npmjs.org/has-bigints/-/has-bigints-1.0.2.tgz", - "integrity": "sha512-tSvCKtBr9lkF0Ex0aQiP9N+OpV4zi2r/Nee5VkRDbaqv35RLYMzbwQfFSZZH0kR+Rd6302UJZ2p/bJCEoR3VoQ==", - "dev": true - }, - "has-flag": { - "version": "4.0.0", - "resolved": "https://registry.npmjs.org/has-flag/-/has-flag-4.0.0.tgz", - "integrity": "sha512-EykJT/Q1KjTWctppgIAgfSO0tKVuZUjhgMr17kqTumMl6Afv3EISleU7qZUzoXDFTAHTDC4NOoG/ZxU3EvlMPQ==", - "dev": true - }, - "has-property-descriptors": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/has-property-descriptors/-/has-property-descriptors-1.0.0.tgz", - "integrity": "sha512-62DVLZGoiEBDHQyqG4w9xCuZ7eJEwNmJRWw2VY84Oedb7WFcA27fiEVe8oUQx9hAUJ4ekurquucTGwsyO1XGdQ==", - "dev": true, - "requires": { - "get-intrinsic": "^1.1.1" - } - }, - "has-proto": { - "version": "1.0.1", - "resolved": "https://registry.npmjs.org/has-proto/-/has-proto-1.0.1.tgz", - "integrity": "sha512-7qE+iP+O+bgF9clE5+UoBFzE65mlBiVj3tKCrlNQ0Ogwm0BjpT/gK4SlLYDMybDh5I3TCTKnPPa0oMG7JDYrhg==", - "dev": true - }, - "has-symbols": { - "version": "1.0.3", - "resolved": "https://registry.npmjs.org/has-symbols/-/has-symbols-1.0.3.tgz", - "integrity": "sha512-l3LCuF6MgDNwTDKkdYGEihYjt5pRPbEg46rtlmnSPlUbgmB8LOIrKJbYYFBSbnPaJexMKtiPO8hmeRjRz2Td+A==", - "dev": true - }, - "has-tostringtag": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/has-tostringtag/-/has-tostringtag-1.0.0.tgz", - "integrity": "sha512-kFjcSNhnlGV1kyoGk7OXKSawH5JOb/LzUc5w9B02hOTO0dfFRjbHQKvg1d6cf3HbeUmtU9VbbV3qzZ2Teh97WQ==", - "dev": true, - "requires": { - "has-symbols": "^1.0.2" - } - }, - "has-yarn": { - "version": "3.0.0", - "resolved": "https://registry.npmjs.org/has-yarn/-/has-yarn-3.0.0.tgz", - "integrity": "sha512-IrsVwUHhEULx3R8f/aA8AHuEzAorplsab/v8HBzEiIukwq5i/EC+xmOW+HfP1OaDP+2JkgT1yILHN2O3UFIbcA==", - "dev": true - }, - "hasha": { - "version": "5.2.2", - "resolved": "https://registry.npmjs.org/hasha/-/hasha-5.2.2.tgz", - "integrity": "sha512-Hrp5vIK/xr5SkeN2onO32H0MgNZ0f17HRNH39WfL0SYUNOTZ5Lz1TJ8Pajo/87dYGEFlLMm7mIc/k/s6Bvz9HQ==", - "dev": true, - "requires": { - "is-stream": "^2.0.0", - "type-fest": "^0.8.0" - }, - "dependencies": { - "is-stream": { - "version": "2.0.1", - "resolved": "https://registry.npmjs.org/is-stream/-/is-stream-2.0.1.tgz", - "integrity": "sha512-hFoiJiTl63nn+kstHGBtewWSKnQLpyb155KHheA1l39uvtO9nWIop1p3udqPcUd/xbF1VLMO4n7OI6p7RbngDg==", - "dev": true - }, - "type-fest": { - "version": "0.8.1", - "resolved": "https://registry.npmjs.org/type-fest/-/type-fest-0.8.1.tgz", - "integrity": "sha512-4dbzIzqvjtgiM5rw1k5rEHtBANKmdudhGyBEajN01fEyhaAIhsoKNy6y7+IN93IfpFtwY9iqi7kD+xwKhQsNJA==", - "dev": true - } - } - }, - "he": { - "version": "1.2.0", - "resolved": "https://registry.npmjs.org/he/-/he-1.2.0.tgz", - "integrity": "sha512-F/1DnUGPopORZi0ni+CvrCgHQ5FyEAHRLSApuYWMmrbSwoN2Mn/7k+Gl38gJnR7yyDZk6WLXwiGod1JOWNDKGw==", - "dev": true - }, - "html-escaper": { - "version": "2.0.2", - "resolved": "https://registry.npmjs.org/html-escaper/-/html-escaper-2.0.2.tgz", - "integrity": "sha512-H2iMtd0I4Mt5eYiapRdIDjp+XzelXQ0tFE4JS7YFwFevXXMmOp9myNrUvCg0D6ws8iqkRPBfKHgbwig1SmlLfg==", - "dev": true - }, - "http-cache-semantics": { - "version": "4.1.0", - "resolved": "https://registry.npmjs.org/http-cache-semantics/-/http-cache-semantics-4.1.0.tgz", - "integrity": "sha512-carPklcUh7ROWRK7Cv27RPtdhYhUsela/ue5/jKzjegVvXDqM2ILE9Q2BGn9JZJh1g87cp56su/FgQSzcWS8cQ==", - "dev": true - }, - "http-errors": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/http-errors/-/http-errors-2.0.0.tgz", - "integrity": "sha512-FtwrG/euBzaEjYeRqOgly7G0qviiXoJWnvEH2Z1plBdXgbyjv34pHTSb9zoeHMyDy33+DWy5Wt9Wo+TURtOYSQ==", - "dev": true, - "requires": { - "depd": "2.0.0", - "inherits": "2.0.4", - "setprototypeof": "1.2.0", - "statuses": "2.0.1", - "toidentifier": "1.0.1" - } - }, - "http-proxy-agent": { - "version": "4.0.1", - "resolved": "https://registry.npmjs.org/http-proxy-agent/-/http-proxy-agent-4.0.1.tgz", - "integrity": "sha512-k0zdNgqWTGA6aeIRVpvfVob4fL52dTfaehylg0Y4UvSySvOq/Y+BOyPrgpUrA7HylqvU8vIZGsRuXmspskV0Tg==", - "dev": true, - "requires": { - "@tootallnate/once": "1", - "agent-base": "6", - "debug": "4" - } - }, - "http2-wrapper": { - "version": "2.2.0", - "resolved": "https://registry.npmjs.org/http2-wrapper/-/http2-wrapper-2.2.0.tgz", - "integrity": "sha512-kZB0wxMo0sh1PehyjJUWRFEd99KC5TLjZ2cULC4f9iqJBAmKQQXEICjxl5iPJRwP40dpeHFqqhm7tYCvODpqpQ==", - "dev": true, - "requires": { - "quick-lru": "^5.1.1", - "resolve-alpn": "^1.2.0" - } - }, - "https-proxy-agent": { - "version": "5.0.1", - "resolved": "https://registry.npmjs.org/https-proxy-agent/-/https-proxy-agent-5.0.1.tgz", - "integrity": "sha512-dFcAjpTQFgoLMzC2VwU+C/CbS7uRL0lWmxDITmqm7C+7F0Odmj6s9l6alZc6AELXhrnggM2CeWSXHGOdX2YtwA==", - "dev": true, - "requires": { - "agent-base": "6", - "debug": "4" - } - }, - "human-signals": { - "version": "3.0.1", - "resolved": "https://registry.npmjs.org/human-signals/-/human-signals-3.0.1.tgz", - "integrity": "sha512-rQLskxnM/5OCldHo+wNXbpVgDn5A17CUoKX+7Sokwaknlq7CdSnphy0W39GU8dw59XiCXmFXDg4fRuckQRKewQ==", - "dev": true - }, - "iconv-lite": { - "version": "0.4.24", - "resolved": "https://registry.npmjs.org/iconv-lite/-/iconv-lite-0.4.24.tgz", - "integrity": "sha512-v3MXnZAcvnywkTUEZomIActle7RXXeedOR31wwl7VlyoXO4Qi9arvSenNQWne1TcRwhCL1HwLI21bEqdpj8/rA==", - "dev": true, - "requires": { - "safer-buffer": ">= 2.1.2 < 3" - } - }, - "ieee754": { - "version": "1.2.1", - "resolved": "https://registry.npmjs.org/ieee754/-/ieee754-1.2.1.tgz", - "integrity": "sha512-dcyqhDvX1C46lXZcVqCpK+FtMRQVdIMN6/Df5js2zouUsqG7I6sFxitIC+7KYK29KdXOLHdu9zL4sFnoVQnqaA==", - "dev": true - }, - "ignore": { - "version": "5.2.4", - "resolved": "https://registry.npmjs.org/ignore/-/ignore-5.2.4.tgz", - "integrity": "sha512-MAb38BcSbH0eHNBxn7ql2NH/kX33OkB3lZ1BNdh7ENeRChHTYsTvWrMubiIAMNS2llXEEgZ1MUOBtXChP3kaFQ==", - "dev": true - }, - "import-fresh": { - "version": "3.3.0", - "resolved": "https://registry.npmjs.org/import-fresh/-/import-fresh-3.3.0.tgz", - "integrity": "sha512-veYYhQa+D1QBKznvhUHxb8faxlrwUnxseDAbAp457E0wLNio2bOSKnjYDhMj+YiAq61xrMGhQk9iXVk5FzgQMw==", - "dev": true, - "requires": { - "parent-module": "^1.0.0", - "resolve-from": "^4.0.0" - } - }, - "import-lazy": { - "version": "4.0.0", - "resolved": "https://registry.npmjs.org/import-lazy/-/import-lazy-4.0.0.tgz", - "integrity": "sha512-rKtvo6a868b5Hu3heneU+L4yEQ4jYKLtjpnPeUdK7h0yzXGmyBTypknlkCvHFBqfX9YlorEiMM6Dnq/5atfHkw==", - "dev": true - }, - "imurmurhash": { - "version": "0.1.4", - "resolved": "https://registry.npmjs.org/imurmurhash/-/imurmurhash-0.1.4.tgz", - "integrity": "sha512-JmXMZ6wuvDmLiHEml9ykzqO6lwFbof0GG4IkcGaENdCRDDmMVnny7s5HsIgHCbaq0w2MyPhDqkhTUgS2LU2PHA==", - "dev": true - }, - "indent-string": { - "version": "4.0.0", - "resolved": "https://registry.npmjs.org/indent-string/-/indent-string-4.0.0.tgz", - "integrity": "sha512-EdDDZu4A2OyIK7Lr/2zG+w5jmbuk1DVBnEwREQvBzspBJkCEbRa8GxU1lghYcaGJCnRWibjDXlq779X1/y5xwg==", - "dev": true - }, - "inflight": { - "version": "1.0.6", - "resolved": "https://registry.npmjs.org/inflight/-/inflight-1.0.6.tgz", - "integrity": "sha512-k92I/b08q4wvFscXCLvqfsHCrjrF7yiXsQuIVvVE7N82W3+aqpzuUdBbfhWcy/FZR3/4IgflMgKLOsvPDrGCJA==", - "dev": true, - "requires": { - "once": "^1.3.0", - "wrappy": "1" - } - }, - "inherits": { - "version": "2.0.4", - "resolved": "https://registry.npmjs.org/inherits/-/inherits-2.0.4.tgz", - "integrity": "sha512-k/vGaX4/Yla3WzyMCvTQOXYeIHvqOKtnqBduzTHpzpQZzAskKMhZ2K+EnBiSM9zGSoIFeMpXKxa4dYeZIQqewQ==", - "dev": true - }, - "ini": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/ini/-/ini-2.0.0.tgz", - "integrity": "sha512-7PnF4oN3CvZF23ADhA5wRaYEQpJ8qygSkbtTXWBeXWXmEVRXK+1ITciHWwHhsjv1TmW0MgacIv6hEi5pX5NQdA==", - "dev": true - }, - "inquirer": { - "version": "9.1.4", - "resolved": "https://registry.npmjs.org/inquirer/-/inquirer-9.1.4.tgz", - "integrity": "sha512-9hiJxE5gkK/cM2d1mTEnuurGTAoHebbkX0BYl3h7iEg7FYfuNIom+nDfBCSWtvSnoSrWCeBxqqBZu26xdlJlXA==", - "dev": true, - "requires": { - "ansi-escapes": "^6.0.0", - "chalk": "^5.1.2", - "cli-cursor": "^4.0.0", - "cli-width": "^4.0.0", - "external-editor": "^3.0.3", - "figures": "^5.0.0", - "lodash": "^4.17.21", - "mute-stream": "0.0.8", - "ora": "^6.1.2", - "run-async": "^2.4.0", - "rxjs": "^7.5.7", - "string-width": "^5.1.2", - "strip-ansi": "^7.0.1", - "through": "^2.3.6", - "wrap-ansi": "^8.0.1" - } - }, - "internal-slot": { - "version": "1.0.4", - "resolved": "https://registry.npmjs.org/internal-slot/-/internal-slot-1.0.4.tgz", - "integrity": "sha512-tA8URYccNzMo94s5MQZgH8NB/XTa6HsOo0MLfXTKKEnHVVdegzaQoFZ7Jp44bdvLvY2waT5dc+j5ICEswhi7UQ==", - "dev": true, - "requires": { - "get-intrinsic": "^1.1.3", - "has": "^1.0.3", - "side-channel": "^1.0.4" - } - }, - "interpret": { - "version": "1.4.0", - "resolved": "https://registry.npmjs.org/interpret/-/interpret-1.4.0.tgz", - "integrity": "sha512-agE4QfB2Lkp9uICn7BAqoscw4SZP9kTE2hxiFI3jBPmXJfdqiahTbUuKGsMoN2GtqL9AxhYioAcVvgsb1HvRbA==", - "dev": true - }, - "ip": { - "version": "1.1.8", - "resolved": "https://registry.npmjs.org/ip/-/ip-1.1.8.tgz", - "integrity": "sha512-PuExPYUiu6qMBQb4l06ecm6T6ujzhmh+MeJcW9wa89PoAz5pvd4zPgN5WJV104mb6S2T1AwNIAaB70JNrLQWhg==", - "dev": true - }, - "is-arguments": { - "version": "1.1.1", - "resolved": "https://registry.npmjs.org/is-arguments/-/is-arguments-1.1.1.tgz", - "integrity": "sha512-8Q7EARjzEnKpt/PCD7e1cgUS0a6X8u5tdSiMqXhojOdoV9TsMsiO+9VLC5vAmO8N7/GmXn7yjR8qnA6bVAEzfA==", - "dev": true, - "requires": { - "call-bind": "^1.0.2", - "has-tostringtag": "^1.0.0" - } - }, - "is-array-buffer": { - "version": "3.0.1", - "resolved": "https://registry.npmjs.org/is-array-buffer/-/is-array-buffer-3.0.1.tgz", - "integrity": "sha512-ASfLknmY8Xa2XtB4wmbz13Wu202baeA18cJBCeCy0wXUHZF0IPyVEXqKEcd+t2fNSLLL1vC6k7lxZEojNbISXQ==", - "dev": true, - "requires": { - "call-bind": "^1.0.2", - "get-intrinsic": "^1.1.3", - "is-typed-array": "^1.1.10" - } - }, - "is-arrayish": { - "version": "0.2.1", - "resolved": "https://registry.npmjs.org/is-arrayish/-/is-arrayish-0.2.1.tgz", - "integrity": "sha512-zz06S8t0ozoDXMG+ube26zeCTNXcKIPJZJi8hBrF4idCLms4CG9QtK7qBl1boi5ODzFpjswb5JPmHCbMpjaYzg==", - "dev": true - }, - "is-bigint": { - "version": "1.0.4", - "resolved": "https://registry.npmjs.org/is-bigint/-/is-bigint-1.0.4.tgz", - "integrity": "sha512-zB9CruMamjym81i2JZ3UMn54PKGsQzsJeo6xvN3HJJ4CAsQNB6iRutp2To77OfCNuoxspsIhzaPoO1zyCEhFOg==", - "dev": true, - "requires": { - "has-bigints": "^1.0.1" - } - }, - "is-binary-path": { - "version": "2.1.0", - "resolved": "https://registry.npmjs.org/is-binary-path/-/is-binary-path-2.1.0.tgz", - "integrity": "sha512-ZMERYes6pDydyuGidse7OsHxtbI7WVeUEozgR/g7rd0xUimYNlvZRE/K2MgZTjWy725IfelLeVcEM97mmtRGXw==", - "dev": true, - "requires": { - "binary-extensions": "^2.0.0" - } - }, - "is-boolean-object": { - "version": "1.1.2", - "resolved": "https://registry.npmjs.org/is-boolean-object/-/is-boolean-object-1.1.2.tgz", - "integrity": "sha512-gDYaKHJmnj4aWxyj6YHyXVpdQawtVLHU5cb+eztPGczf6cjuTdwve5ZIEfgXqH4e57An1D1AKf8CZ3kYrQRqYA==", - "dev": true, - "requires": { - "call-bind": "^1.0.2", - "has-tostringtag": "^1.0.0" - } - }, - "is-callable": { - "version": "1.2.7", - "resolved": "https://registry.npmjs.org/is-callable/-/is-callable-1.2.7.tgz", - "integrity": "sha512-1BC0BVFhS/p0qtw6enp8e+8OD0UrK0oFLztSjNzhcKA3WDuJxxAPXzPuPtKkjEY9UUoEWlX/8fgKeu2S8i9JTA==", - "dev": true - }, - "is-ci": { - "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.2.0" - } - }, - "is-core-module": { - "version": "2.11.0", - "resolved": "https://registry.npmjs.org/is-core-module/-/is-core-module-2.11.0.tgz", - "integrity": "sha512-RRjxlvLDkD1YJwDbroBHMb+cukurkDWNyHx7D3oNB5x9rb5ogcksMC5wHCadcXoo67gVr/+3GFySh3134zi6rw==", - "dev": true, - "requires": { - "has": "^1.0.3" - } - }, - "is-date-object": { - "version": "1.0.5", - "resolved": "https://registry.npmjs.org/is-date-object/-/is-date-object-1.0.5.tgz", - "integrity": "sha512-9YQaSxsAiSwcvS33MBk3wTCVnWK+HhF8VZR2jRxehM16QcVOdHqPn4VPHmRK4lSr38n9JriurInLcP90xsYNfQ==", - "dev": true, - "requires": { - "has-tostringtag": "^1.0.0" - } - }, - "is-docker": { - "version": "2.2.1", - "resolved": "https://registry.npmjs.org/is-docker/-/is-docker-2.2.1.tgz", - "integrity": "sha512-F+i2BKsFrH66iaUFc0woD8sLy8getkwTwtOBjvs56Cx4CgJDeKQeqfz8wAYiSb8JOprWhHH5p77PbmYCvvUuXQ==", - "dev": true - }, - "is-extglob": { - "version": "2.1.1", - "resolved": "https://registry.npmjs.org/is-extglob/-/is-extglob-2.1.1.tgz", - "integrity": "sha512-SbKbANkN603Vi4jEZv49LeVJMn4yGwsbzZworEoyEiutsN3nJYdbO36zfhGJ6QEDpOZIFkDtnq5JRxmvl3jsoQ==", - "dev": true - }, - "is-fullwidth-code-point": { - "version": "3.0.0", - "resolved": "https://registry.npmjs.org/is-fullwidth-code-point/-/is-fullwidth-code-point-3.0.0.tgz", - "integrity": "sha512-zymm5+u+sCsSWyD9qNaejV3DFvhCKclKdizYaJUuHA83RLjb7nSuGnddCHGv0hk+KY7BMAlsWeK4Ueg6EV6XQg==", - "dev": true - }, - "is-glob": { - "version": "4.0.3", - "resolved": "https://registry.npmjs.org/is-glob/-/is-glob-4.0.3.tgz", - "integrity": "sha512-xelSayHH36ZgE7ZWhli7pW34hNbNl8Ojv5KVmkJD4hBdD3th8Tfk9vYasLM+mXWOZhFkgZfxhLSnrwRr4elSSg==", - "dev": true, - "requires": { - "is-extglob": "^2.1.1" - } - }, - "is-installed-globally": { - "version": "0.4.0", - "resolved": "https://registry.npmjs.org/is-installed-globally/-/is-installed-globally-0.4.0.tgz", - "integrity": "sha512-iwGqO3J21aaSkC7jWnHP/difazwS7SFeIqxv6wEtLU8Y5KlzFTjyqcSIT0d8s4+dDhKytsk9PJZ2BkS5eZwQRQ==", - "dev": true, - "requires": { - "global-dirs": "^3.0.0", - "is-path-inside": "^3.0.2" - } - }, - "is-interactive": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/is-interactive/-/is-interactive-2.0.0.tgz", - "integrity": "sha512-qP1vozQRI+BMOPcjFzrjXuQvdak2pHNUMZoeG2eRbiSqyvbEf/wQtEOTOX1guk6E3t36RkaqiSt8A/6YElNxLQ==", - "dev": true - }, - "is-map": { - "version": "2.0.2", - "resolved": "https://registry.npmjs.org/is-map/-/is-map-2.0.2.tgz", - "integrity": "sha512-cOZFQQozTha1f4MxLFzlgKYPTyj26picdZTx82hbc/Xf4K/tZOOXSCkMvU4pKioRXGDLJRn0GM7Upe7kR721yg==", - "dev": true - }, - "is-negative-zero": { - "version": "2.0.2", - "resolved": "https://registry.npmjs.org/is-negative-zero/-/is-negative-zero-2.0.2.tgz", - "integrity": "sha512-dqJvarLawXsFbNDeJW7zAz8ItJ9cd28YufuuFzh0G8pNHjJMnY08Dv7sYX2uF5UpQOwieAeOExEYAWWfu7ZZUA==", - "dev": true - }, - "is-npm": { - "version": "6.0.0", - "resolved": "https://registry.npmjs.org/is-npm/-/is-npm-6.0.0.tgz", - "integrity": "sha512-JEjxbSmtPSt1c8XTkVrlujcXdKV1/tvuQ7GwKcAlyiVLeYFQ2VHat8xfrDJsIkhCdF/tZ7CiIR3sy141c6+gPQ==", - "dev": true - }, - "is-number": { - "version": "7.0.0", - "resolved": "https://registry.npmjs.org/is-number/-/is-number-7.0.0.tgz", - "integrity": "sha512-41Cifkg6e8TylSpdtTpeLVMqvSBEVzTttHvERD741+pnZ8ANv0004MRL43QKPDlK9cGvNp6NZWZUBlbGXYxxng==", - "dev": true - }, - "is-number-object": { - "version": "1.0.7", - "resolved": "https://registry.npmjs.org/is-number-object/-/is-number-object-1.0.7.tgz", - "integrity": "sha512-k1U0IRzLMo7ZlYIfzRu23Oh6MiIFasgpb9X76eqfFZAqwH44UI4KTBvBYIZ1dSL9ZzChTB9ShHfLkR4pdW5krQ==", - "dev": true, - "requires": { - "has-tostringtag": "^1.0.0" - } - }, - "is-obj": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/is-obj/-/is-obj-2.0.0.tgz", - "integrity": "sha512-drqDG3cbczxxEJRoOXcOjtdp1J/lyp1mNn0xaznRs8+muBhgQcrnbspox5X5fOw0HnMnbfDzvnEMEtqDEJEo8w==", - "dev": true - }, - "is-path-inside": { - "version": "3.0.3", - "resolved": "https://registry.npmjs.org/is-path-inside/-/is-path-inside-3.0.3.tgz", - "integrity": "sha512-Fd4gABb+ycGAmKou8eMftCupSir5lRxqf4aD/vd0cD2qc4HL07OjCeuHMr8Ro4CoMaeCKDB0/ECBOVWjTwUvPQ==", - "dev": true - }, - "is-plain-obj": { - "version": "2.1.0", - "resolved": "https://registry.npmjs.org/is-plain-obj/-/is-plain-obj-2.1.0.tgz", - "integrity": "sha512-YWnfyRwxL/+SsrWYfOpUtz5b3YD+nyfkHvjbcanzk8zgyO4ASD67uVMRt8k5bM4lLMDnXfriRhOpemw+NfT1eA==", - "dev": true - }, - "is-plain-object": { - "version": "5.0.0", - "resolved": "https://registry.npmjs.org/is-plain-object/-/is-plain-object-5.0.0.tgz", - "integrity": "sha512-VRSzKkbMm5jMDoKLbltAkFQ5Qr7VDiTFGXxYFXXowVj387GeGNOCsOH6Msy00SGZ3Fp84b1Naa1psqgcCIEP5Q==", - "dev": true - }, - "is-regex": { - "version": "1.1.4", - "resolved": "https://registry.npmjs.org/is-regex/-/is-regex-1.1.4.tgz", - "integrity": "sha512-kvRdxDsxZjhzUX07ZnLydzS1TU/TJlTUHHY4YLL87e37oUA49DfkLqgy+VjFocowy29cKvcSiu+kIv728jTTVg==", - "dev": true, - "requires": { - "call-bind": "^1.0.2", - "has-tostringtag": "^1.0.0" - } - }, - "is-set": { - "version": "2.0.2", - "resolved": "https://registry.npmjs.org/is-set/-/is-set-2.0.2.tgz", - "integrity": "sha512-+2cnTEZeY5z/iXGbLhPrOAaK/Mau5k5eXq9j14CpRTftq0pAJu2MwVRSZhyZWBzx3o6X795Lz6Bpb6R0GKf37g==", - "dev": true - }, - "is-shared-array-buffer": { - "version": "1.0.2", - "resolved": "https://registry.npmjs.org/is-shared-array-buffer/-/is-shared-array-buffer-1.0.2.tgz", - "integrity": "sha512-sqN2UDu1/0y6uvXyStCOzyhAjCSlHceFoMKJW8W9EU9cvic/QdsZ0kEU93HEy3IUEFZIiH/3w+AH/UQbPHNdhA==", - "dev": true, - "requires": { - "call-bind": "^1.0.2" - } - }, - "is-ssh": { - "version": "1.4.0", - "resolved": "https://registry.npmjs.org/is-ssh/-/is-ssh-1.4.0.tgz", - "integrity": "sha512-x7+VxdxOdlV3CYpjvRLBv5Lo9OJerlYanjwFrPR9fuGPjCiNiCzFgAWpiLAohSbsnH4ZAys3SBh+hq5rJosxUQ==", - "dev": true, - "requires": { - "protocols": "^2.0.1" - } - }, - "is-stream": { - "version": "3.0.0", - "resolved": "https://registry.npmjs.org/is-stream/-/is-stream-3.0.0.tgz", - "integrity": "sha512-LnQR4bZ9IADDRSkvpqMGvt/tEJWclzklNgSw48V5EAaAeDd6qGvN8ei6k5p0tvxSR171VmGyHuTiAOfxAbr8kA==", - "dev": true - }, - "is-string": { - "version": "1.0.7", - "resolved": "https://registry.npmjs.org/is-string/-/is-string-1.0.7.tgz", - "integrity": "sha512-tE2UXzivje6ofPW7l23cjDOMa09gb7xlAqG6jG5ej6uPV32TlWP3NKPigtaGeHNu9fohccRYvIiZMfOOnOYUtg==", - "dev": true, - "requires": { - "has-tostringtag": "^1.0.0" - } - }, - "is-symbol": { - "version": "1.0.4", - "resolved": "https://registry.npmjs.org/is-symbol/-/is-symbol-1.0.4.tgz", - "integrity": "sha512-C/CPBqKWnvdcxqIARxyOh4v1UUEOCHpgDa0WYgpKDFMszcrPcffg5uhwSgPCLD2WWxmq6isisz87tzT01tuGhg==", - "dev": true, - "requires": { - "has-symbols": "^1.0.2" - } - }, - "is-typed-array": { - "version": "1.1.10", - "resolved": "https://registry.npmjs.org/is-typed-array/-/is-typed-array-1.1.10.tgz", - "integrity": "sha512-PJqgEHiWZvMpaFZ3uTc8kHPM4+4ADTlDniuQL7cU/UDA0Ql7F70yGfHph3cLNe+c9toaigv+DFzTJKhc2CtO6A==", - "dev": true, - "requires": { - "available-typed-arrays": "^1.0.5", - "call-bind": "^1.0.2", - "for-each": "^0.3.3", - "gopd": "^1.0.1", - "has-tostringtag": "^1.0.0" - } - }, - "is-typedarray": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/is-typedarray/-/is-typedarray-1.0.0.tgz", - "integrity": "sha512-cyA56iCMHAh5CdzjJIa4aohJyeO1YbwLi3Jc35MmRU6poroFjIGZzUzupGiRPOjgHg9TLu43xbpwXk523fMxKA==", - "dev": true - }, - "is-unicode-supported": { - "version": "1.3.0", - "resolved": "https://registry.npmjs.org/is-unicode-supported/-/is-unicode-supported-1.3.0.tgz", - "integrity": "sha512-43r2mRvz+8JRIKnWJ+3j8JtjRKZ6GmjzfaE/qiBJnikNnYv/6bagRJ1kUhNk8R5EX/GkobD+r+sfxCPJsiKBLQ==", - "dev": true - }, - "is-weakref": { - "version": "1.0.2", - "resolved": "https://registry.npmjs.org/is-weakref/-/is-weakref-1.0.2.tgz", - "integrity": "sha512-qctsuLZmIQ0+vSSMfoVvyFe2+GSEvnmZ2ezTup1SBse9+twCCeial6EEi3Nc2KFcf6+qz2FBPnjXsk8xhKSaPQ==", - "dev": true, - "requires": { - "call-bind": "^1.0.2" - } - }, - "is-windows": { - "version": "1.0.2", - "resolved": "https://registry.npmjs.org/is-windows/-/is-windows-1.0.2.tgz", - "integrity": "sha512-eXK1UInq2bPmjyX6e3VHIzMLobc4J94i4AWn+Hpq3OU5KkrRC96OAcR3PRJ/pGu6m8TRnBHP9dkXQVsT/COVIA==", - "dev": true - }, - "is-wsl": { - "version": "2.2.0", - "resolved": "https://registry.npmjs.org/is-wsl/-/is-wsl-2.2.0.tgz", - "integrity": "sha512-fKzAra0rGJUUBwGBgNkHZuToZcn+TtXHpeCgmkMJMMYx1sQDYaCSyjJBSCa2nH1DGm7s3n1oBnohoVTBaN7Lww==", - "dev": true, - "requires": { - "is-docker": "^2.0.0" - } - }, - "is-yarn-global": { - "version": "0.4.1", - "resolved": "https://registry.npmjs.org/is-yarn-global/-/is-yarn-global-0.4.1.tgz", - "integrity": "sha512-/kppl+R+LO5VmhYSEWARUFjodS25D68gvj8W7z0I7OWhUla5xWu8KL6CtB2V0R6yqhnRgbcaREMr4EEM6htLPQ==", - "dev": true - }, - "isarray": { - "version": "2.0.5", - "resolved": "https://registry.npmjs.org/isarray/-/isarray-2.0.5.tgz", - "integrity": "sha512-xHjhDr3cNBK0BzdUJSPXZntQUx/mwMS5Rw4A7lPJ90XGAO6ISP/ePDNuo0vhqOZU+UD5JoodwCAAoZQd3FeAKw==", - "dev": true - }, - "isexe": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/isexe/-/isexe-2.0.0.tgz", - "integrity": "sha512-RHxMLp9lnKHGHRng9QFhRCMbYAcVpn69smSGcq3f36xjgVVWThj4qqLbTLlq7Ssj8B+fIQ1EuCEGI2lKsyQeIw==", - "dev": true - }, - "istanbul-lib-coverage": { - "version": "3.2.0", - "resolved": "https://registry.npmjs.org/istanbul-lib-coverage/-/istanbul-lib-coverage-3.2.0.tgz", - "integrity": "sha512-eOeJ5BHCmHYvQK7xt9GkdHuzuCGS1Y6g9Gvnx3Ym33fz/HpLRYxiS0wHNr+m/MBC8B647Xt608vCDEvhl9c6Mw==", - "dev": true - }, - "istanbul-lib-hook": { - "version": "3.0.0", - "resolved": "https://registry.npmjs.org/istanbul-lib-hook/-/istanbul-lib-hook-3.0.0.tgz", - "integrity": "sha512-Pt/uge1Q9s+5VAZ+pCo16TYMWPBIl+oaNIjgLQxcX0itS6ueeaA+pEfThZpH8WxhFgCiEb8sAJY6MdUKgiIWaQ==", - "dev": true, - "requires": { - "append-transform": "^2.0.0" - } - }, - "istanbul-lib-instrument": { - "version": "4.0.3", - "resolved": "https://registry.npmjs.org/istanbul-lib-instrument/-/istanbul-lib-instrument-4.0.3.tgz", - "integrity": "sha512-BXgQl9kf4WTCPCCpmFGoJkz/+uhvm7h7PFKUYxh7qarQd3ER33vHG//qaE8eN25l07YqZPpHXU9I09l/RD5aGQ==", - "dev": true, - "requires": { - "@babel/core": "^7.7.5", - "@istanbuljs/schema": "^0.1.2", - "istanbul-lib-coverage": "^3.0.0", - "semver": "^6.3.0" - } - }, - "istanbul-lib-processinfo": { - "version": "2.0.3", - "resolved": "https://registry.npmjs.org/istanbul-lib-processinfo/-/istanbul-lib-processinfo-2.0.3.tgz", - "integrity": "sha512-NkwHbo3E00oybX6NGJi6ar0B29vxyvNwoC7eJ4G4Yq28UfY758Hgn/heV8VRFhevPED4LXfFz0DQ8z/0kw9zMg==", - "dev": true, - "requires": { - "archy": "^1.0.0", - "cross-spawn": "^7.0.3", - "istanbul-lib-coverage": "^3.2.0", - "p-map": "^3.0.0", - "rimraf": "^3.0.0", - "uuid": "^8.3.2" - } - }, - "istanbul-lib-report": { - "version": "3.0.0", - "resolved": "https://registry.npmjs.org/istanbul-lib-report/-/istanbul-lib-report-3.0.0.tgz", - "integrity": "sha512-wcdi+uAKzfiGT2abPpKZ0hSU1rGQjUQnLvtY5MpQ7QCTahD3VODhcu4wcfY1YtkGaDD5yuydOLINXsfbus9ROw==", - "dev": true, - "requires": { - "istanbul-lib-coverage": "^3.0.0", - "make-dir": "^3.0.0", - "supports-color": "^7.1.0" - } - }, - "istanbul-lib-source-maps": { - "version": "4.0.1", - "resolved": "https://registry.npmjs.org/istanbul-lib-source-maps/-/istanbul-lib-source-maps-4.0.1.tgz", - "integrity": "sha512-n3s8EwkdFIJCG3BPKBYvskgXGoy88ARzvegkitk60NxRdwltLOTaH7CUiMRXvwYorl0Q712iEjcWB+fK/MrWVw==", - "dev": true, - "requires": { - "debug": "^4.1.1", - "istanbul-lib-coverage": "^3.0.0", - "source-map": "^0.6.1" - } - }, - "istanbul-reports": { - "version": "3.1.5", - "resolved": "https://registry.npmjs.org/istanbul-reports/-/istanbul-reports-3.1.5.tgz", - "integrity": "sha512-nUsEMa9pBt/NOHqbcbeJEgqIlY/K7rVWUX6Lql2orY5e9roQOthbR3vtY4zzf2orPELg80fnxxk9zUyPlgwD1w==", - "dev": true, - "requires": { - "html-escaper": "^2.0.0", - "istanbul-lib-report": "^3.0.0" - } - }, - "iterate-iterator": { - "version": "1.0.2", - "resolved": "https://registry.npmjs.org/iterate-iterator/-/iterate-iterator-1.0.2.tgz", - "integrity": "sha512-t91HubM4ZDQ70M9wqp+pcNpu8OyJ9UAtXntT/Bcsvp5tZMnz9vRa+IunKXeI8AnfZMTv0jNuVEmGeLSMjVvfPw==", - "dev": true - }, - "iterate-value": { - "version": "1.0.2", - "resolved": "https://registry.npmjs.org/iterate-value/-/iterate-value-1.0.2.tgz", - "integrity": "sha512-A6fMAio4D2ot2r/TYzr4yUWrmwNdsN5xL7+HUiyACE4DXm+q8HtPcnFTp+NnW3k4N05tZ7FVYFFb2CR13NxyHQ==", - "dev": true, - "requires": { - "es-get-iterator": "^1.0.2", - "iterate-iterator": "^1.0.1" - } - }, - "js-sdsl": { - "version": "4.2.0", - "resolved": "https://registry.npmjs.org/js-sdsl/-/js-sdsl-4.2.0.tgz", - "integrity": "sha512-dyBIzQBDkCqCu+0upx25Y2jGdbTGxE9fshMsCdK0ViOongpV+n5tXRcZY9v7CaVQ79AGS9KA1KHtojxiM7aXSQ==", - "dev": true - }, - "js-tokens": { - "version": "4.0.0", - "resolved": "https://registry.npmjs.org/js-tokens/-/js-tokens-4.0.0.tgz", - "integrity": "sha512-RdJUflcE3cUzKiMqQgsCu06FPu9UdIJO0beYbPhHN4k6apgJtifcoCtT9bcxOpYBtpD2kCM6Sbzg4CausW/PKQ==", - "dev": true - }, - "js-yaml": { - "version": "4.1.0", - "resolved": "https://registry.npmjs.org/js-yaml/-/js-yaml-4.1.0.tgz", - "integrity": "sha512-wpxZs9NoxZaJESJGIZTyDEaYpl0FKSA+FB9aJiyemKhMwkxQg63h4T1KJgUGHpTqPDNRcmmYLugrRjJlBtWvRA==", - "dev": true, - "requires": { - "argparse": "^2.0.1" - } - }, - "jsesc": { - "version": "2.5.2", - "resolved": "https://registry.npmjs.org/jsesc/-/jsesc-2.5.2.tgz", - "integrity": "sha512-OYu7XEzjkCQ3C5Ps3QIZsQfNpqoJyZZA99wd9aWd05NCtC5pWOkShK2mkL6HXQR6/Cy2lbNdPlZBpuQHXE63gA==", - "dev": true - }, - "json-buffer": { - "version": "3.0.1", - "resolved": "https://registry.npmjs.org/json-buffer/-/json-buffer-3.0.1.tgz", - "integrity": "sha512-4bV5BfR2mqfQTJm+V5tPPdf+ZpuhiIvTuAB5g8kcrXOZpTT/QwwVRWBywX1ozr6lEuPdbHxwaJlm9G6mI2sfSQ==", - "dev": true - }, - "json-parse-even-better-errors": { - "version": "2.3.1", - "resolved": "https://registry.npmjs.org/json-parse-even-better-errors/-/json-parse-even-better-errors-2.3.1.tgz", - "integrity": "sha512-xyFwyhro/JEof6Ghe2iz2NcXoj2sloNsWr/XsERDK/oiPCfaNhl5ONfp+jQdAZRQQ0IJWNzH9zIZF7li91kh2w==", - "dev": true - }, - "json-schema-traverse": { - "version": "0.4.1", - "resolved": "https://registry.npmjs.org/json-schema-traverse/-/json-schema-traverse-0.4.1.tgz", - "integrity": "sha512-xbbCH5dCYU5T8LcEhhuh7HJ88HXuW3qsI3Y0zOZFKfZEHcpWiHU/Jxzk629Brsab/mMiHQti9wMP+845RPe3Vg==", - "dev": true - }, - "json-stable-stringify-without-jsonify": { - "version": "1.0.1", - "resolved": "https://registry.npmjs.org/json-stable-stringify-without-jsonify/-/json-stable-stringify-without-jsonify-1.0.1.tgz", - "integrity": "sha512-Bdboy+l7tA3OGW6FjyFHWkP5LuByj1Tk33Ljyq0axyzdk9//JSi2u3fP1QSmd1KNwq6VOKYGlAu87CisVir6Pw==", - "dev": true - }, - "json5": { - "version": "2.2.3", - "resolved": "https://registry.npmjs.org/json5/-/json5-2.2.3.tgz", - "integrity": "sha512-XmOWe7eyHYH14cLdVPoyg+GOH3rYX++KpzrylJwSW98t3Nk+U8XOl8FWKOgwtzdb8lXGf6zYwDUzeHMWfxasyg==", - "dev": true - }, - "jsonc-parser": { - "version": "3.2.0", - "resolved": "https://registry.npmjs.org/jsonc-parser/-/jsonc-parser-3.2.0.tgz", - "integrity": "sha512-gfFQZrcTc8CnKXp6Y4/CBT3fTc0OVuDofpre4aEeEpSBPV5X5v4+Vmx+8snU7RLPrNHPKSgLxGo9YuQzz20o+w==", - "dev": true - }, - "jsonfile": { - "version": "4.0.0", - "resolved": "https://registry.npmjs.org/jsonfile/-/jsonfile-4.0.0.tgz", - "integrity": "sha512-m6F1R3z8jjlf2imQHS2Qez5sjKWQzbuuhuJ/FKYFRZvPE3PuHcSMVZzfsLhGVOkfd20obL5SWEBew5ShlquNxg==", - "dev": true, - "requires": { - "graceful-fs": "^4.1.6" - } - }, - "just-extend": { - "version": "4.2.1", - "resolved": "https://registry.npmjs.org/just-extend/-/just-extend-4.2.1.tgz", - "integrity": "sha512-g3UB796vUFIY90VIv/WX3L2c8CS2MdWUww3CNrYmqza1Fg0DURc2K/O4YrnklBdQarSJ/y8JnJYDGc+1iumQjg==", - "dev": true - }, - "keyv": { - "version": "4.5.2", - "resolved": "https://registry.npmjs.org/keyv/-/keyv-4.5.2.tgz", - "integrity": "sha512-5MHbFaKn8cNSmVW7BYnijeAVlE4cYA/SVkifVgrh7yotnfhKmjuXpDKjrABLnT0SfHWV21P8ow07OGfRrNDg8g==", - "dev": true, - "requires": { - "json-buffer": "3.0.1" - } - }, - "latest-version": { - "version": "7.0.0", - "resolved": "https://registry.npmjs.org/latest-version/-/latest-version-7.0.0.tgz", - "integrity": "sha512-KvNT4XqAMzdcL6ka6Tl3i2lYeFDgXNCuIX+xNx6ZMVR1dFq+idXd9FLKNMOIx0t9mJ9/HudyX4oZWXZQ0UJHeg==", - "dev": true, - "requires": { - "package-json": "^8.1.0" - } - }, - "levn": { - "version": "0.4.1", - "resolved": "https://registry.npmjs.org/levn/-/levn-0.4.1.tgz", - "integrity": "sha512-+bT2uH4E5LGE7h/n3evcS/sQlJXCpIp6ym8OWJ5eV6+67Dsql/LaaT7qJBAt2rzfoa/5QBGBhxDix1dMt2kQKQ==", - "dev": true, - "requires": { - "prelude-ls": "^1.2.1", - "type-check": "~0.4.0" - } - }, - "lines-and-columns": { - "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": { - "version": "5.0.0", - "resolved": "https://registry.npmjs.org/locate-path/-/locate-path-5.0.0.tgz", - "integrity": "sha512-t7hw9pI+WvuwNJXwk5zVHpyhIqzg2qTlklJOf0mVxGSbe3Fp2VieZcduNYjaLDoy6p9uGpQEGWG87WpMKlNq8g==", - "dev": true, - "requires": { - "p-locate": "^4.1.0" - } - }, - "lodash": { - "version": "4.17.21", - "resolved": "https://registry.npmjs.org/lodash/-/lodash-4.17.21.tgz", - "integrity": "sha512-v2kDEe57lecTulaDIuNTPy3Ry4gLGJ6Z1O3vE1krgXZNrsQ+LFTGHVxVjcXPs17LhbZVGedAJv8XZ1tvj5FvSg==", - "dev": true - }, - "lodash.flattendeep": { - "version": "4.4.0", - "resolved": "https://registry.npmjs.org/lodash.flattendeep/-/lodash.flattendeep-4.4.0.tgz", - "integrity": "sha512-uHaJFihxmJcEX3kT4I23ABqKKalJ/zDrDg0lsFtc1h+3uw49SIJ5beyhx5ExVRti3AvKoOJngIj7xz3oylPdWQ==", - "dev": true - }, - "lodash.get": { - "version": "4.4.2", - "resolved": "https://registry.npmjs.org/lodash.get/-/lodash.get-4.4.2.tgz", - "integrity": "sha512-z+Uw/vLuy6gQe8cfaFWD7p0wVv8fJl3mbzXh33RS+0oW2wvUqiRXiQ69gLWSLpgB5/6sU+r6BlQR0MBILadqTQ==", - "dev": true - }, - "lodash.merge": { - "version": "4.6.2", - "resolved": "https://registry.npmjs.org/lodash.merge/-/lodash.merge-4.6.2.tgz", - "integrity": "sha512-0KpjqXRVvrYyCsX1swR/XTK0va6VQkQM6MNo7PqW77ByjAhoARA8EfrP1N4+KlKj8YS0ZUCtRT/YUuhyYDujIQ==", - "dev": true - }, - "log-symbols": { - "version": "5.1.0", - "resolved": "https://registry.npmjs.org/log-symbols/-/log-symbols-5.1.0.tgz", - "integrity": "sha512-l0x2DvrW294C9uDCoQe1VSU4gf529FkSZ6leBl4TiqZH/e+0R7hSfHQBNut2mNygDgHwvYHfFLn6Oxb3VWj2rA==", - "dev": true, - "requires": { - "chalk": "^5.0.0", - "is-unicode-supported": "^1.1.0" - } - }, - "lowercase-keys": { - "version": "3.0.0", - "resolved": "https://registry.npmjs.org/lowercase-keys/-/lowercase-keys-3.0.0.tgz", - "integrity": "sha512-ozCC6gdQ+glXOQsveKD0YsDy8DSQFjDTz4zyzEHNV5+JP5D62LmfDZ6o1cycFx9ouG940M5dE8C8CTewdj2YWQ==", - "dev": true - }, - "lru-cache": { - "version": "5.1.1", - "resolved": "https://registry.npmjs.org/lru-cache/-/lru-cache-5.1.1.tgz", - "integrity": "sha512-KpNARQA3Iwv+jTA0utUVVbrh+Jlrr1Fv0e56GGzAFOXN7dk/FviaDW8LHmK52DlcH4WP2n6gI8vN1aesBFgo9w==", - "dev": true, - "requires": { - "yallist": "^3.0.2" - } - }, - "lunr": { - "version": "2.3.9", - "resolved": "https://registry.npmjs.org/lunr/-/lunr-2.3.9.tgz", - "integrity": "sha512-zTU3DaZaF3Rt9rhN3uBMGQD3dD2/vFQqnvZCDv4dl5iOzq2IZQqTxu90r4E5J+nP70J3ilqVCrbho2eWaeW8Ow==", - "dev": true - }, - "macos-release": { - "version": "3.1.0", - "resolved": "https://registry.npmjs.org/macos-release/-/macos-release-3.1.0.tgz", - "integrity": "sha512-/M/R0gCDgM+Cv1IuBG1XGdfTFnMEG6PZeT+KGWHO/OG+imqmaD9CH5vHBTycEM3+Kc4uG2Il+tFAuUWLqQOeUA==", - "dev": true - }, - "make-dir": { - "version": "3.1.0", - "resolved": "https://registry.npmjs.org/make-dir/-/make-dir-3.1.0.tgz", - "integrity": "sha512-g3FeP20LNwhALb/6Cz6Dd4F2ngze0jz7tbzrD2wAV+o9FeNHe4rL+yK2md0J/fiSf1sa1ADhXqi5+oVwOM/eGw==", - "dev": true, - "requires": { - "semver": "^6.0.0" - } - }, - "make-error": { - "version": "1.3.6", - "resolved": "https://registry.npmjs.org/make-error/-/make-error-1.3.6.tgz", - "integrity": "sha512-s8UhlNe7vPKomQhC1qFelMokr/Sc3AgNbso3n74mVPA5LTZwkB9NlXf4XPamLxJE8h0gh73rM94xvwRT2CVInw==", - "dev": true - }, - "marked": { - "version": "4.2.12", - "resolved": "https://registry.npmjs.org/marked/-/marked-4.2.12.tgz", - "integrity": "sha512-yr8hSKa3Fv4D3jdZmtMMPghgVt6TWbk86WQaWhDloQjRSQhMMYCAro7jP7VDJrjjdV8pxVxMssXS8B8Y5DZ5aw==", - "dev": true - }, - "merge-stream": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/merge-stream/-/merge-stream-2.0.0.tgz", - "integrity": "sha512-abv/qOcuPfk3URPfDzmZU1LKmuw8kT+0nIHvKrKgFrwifol/doWcdA4ZqsWQ8ENrFKkd67Mfpo/LovbIUsbt3w==", - "dev": true - }, - "merge2": { - "version": "1.4.1", - "resolved": "https://registry.npmjs.org/merge2/-/merge2-1.4.1.tgz", - "integrity": "sha512-8q7VEgMJW4J8tcfVPy8g09NcQwZdbwFEqhe/WZkoIzjn/3TGDwtOCYtXGxA3O8tPzpczCCDgv+P2P5y00ZJOOg==", - "dev": true - }, - "micromatch": { - "version": "4.0.5", - "resolved": "https://registry.npmjs.org/micromatch/-/micromatch-4.0.5.tgz", - "integrity": "sha512-DMy+ERcEW2q8Z2Po+WNXuw3c5YaUSFjAO5GsJqfEl7UjvtIuFKO6ZrKvcItdy98dwFI2N1tg3zNIdKaQT+aNdA==", - "dev": true, - "requires": { - "braces": "^3.0.2", - "picomatch": "^2.3.1" - } - }, - "mime-db": { - "version": "1.52.0", - "resolved": "https://registry.npmjs.org/mime-db/-/mime-db-1.52.0.tgz", - "integrity": "sha512-sPU4uV7dYlvtWJxwwxHD0PuihVNiE7TyAbQ5SWxDCB9mUYvOgroQOwYQQOKPJ8CIbE+1ETVlOoK1UC2nU3gYvg==", - "dev": true - }, - "mime-types": { - "version": "2.1.35", - "resolved": "https://registry.npmjs.org/mime-types/-/mime-types-2.1.35.tgz", - "integrity": "sha512-ZDY+bPm5zTTF+YpCrAU9nK0UgICYPT0QtT1NZWFv4s++TNkcgVaT0g6+4R2uI4MjQjzysHB1zxuWL50hzaeXiw==", - "dev": true, - "requires": { - "mime-db": "1.52.0" - } - }, - "mimic-fn": { - "version": "4.0.0", - "resolved": "https://registry.npmjs.org/mimic-fn/-/mimic-fn-4.0.0.tgz", - "integrity": "sha512-vqiC06CuhBTUdZH+RYl8sFrL096vA45Ok5ISO6sE/Mr1jRbGH4Csnhi8f3wKVl7x8mO4Au7Ir9D3Oyv1VYMFJw==", - "dev": true - }, - "mimic-response": { - "version": "4.0.0", - "resolved": "https://registry.npmjs.org/mimic-response/-/mimic-response-4.0.0.tgz", - "integrity": "sha512-e5ISH9xMYU0DzrT+jl8q2ze9D6eWBto+I8CNpe+VI+K2J/F/k3PdkdTdz4wvGVH4NTpo+NRYTVIuMQEMMcsLqg==", - "dev": true - }, - "minimatch": { - "version": "3.1.2", - "resolved": "https://registry.npmjs.org/minimatch/-/minimatch-3.1.2.tgz", - "integrity": "sha512-J7p63hRiAjw1NDEww1W7i37+ByIrOWO5XQQAzZ3VOcL0PNybwpfmV/N05zFAzwQ9USyEcX6t3UO+K5aqBQOIHw==", - "dev": true, - "requires": { - "brace-expansion": "^1.1.7" - } - }, - "minimist": { - "version": "1.2.7", - "resolved": "https://registry.npmjs.org/minimist/-/minimist-1.2.7.tgz", - "integrity": "sha512-bzfL1YUZsP41gmu/qjrEk0Q6i2ix/cVeAhbCbqH9u3zYutS1cLg00qhrD0M2MVdCcx4Sc0UpP2eBWo9rotpq6g==", - "dev": true - }, - "mocha": { - "version": "10.2.0", - "resolved": "https://registry.npmjs.org/mocha/-/mocha-10.2.0.tgz", - "integrity": "sha512-IDY7fl/BecMwFHzoqF2sg/SHHANeBoMMXFlS9r0OXKDssYE1M5O43wUY/9BVPeIvfH2zmEbBfseqN9gBQZzXkg==", - "dev": true, - "requires": { - "ansi-colors": "4.1.1", - "browser-stdout": "1.3.1", - "chokidar": "3.5.3", - "debug": "4.3.4", - "diff": "5.0.0", - "escape-string-regexp": "4.0.0", - "find-up": "5.0.0", - "glob": "7.2.0", - "he": "1.2.0", - "js-yaml": "4.1.0", - "log-symbols": "4.1.0", - "minimatch": "5.0.1", - "ms": "2.1.3", - "nanoid": "3.3.3", - "serialize-javascript": "6.0.0", - "strip-json-comments": "3.1.1", - "supports-color": "8.1.1", - "workerpool": "6.2.1", - "yargs": "16.2.0", - "yargs-parser": "20.2.4", - "yargs-unparser": "2.0.0" - }, - "dependencies": { - "ansi-regex": { - "version": "5.0.1", - "resolved": "https://registry.npmjs.org/ansi-regex/-/ansi-regex-5.0.1.tgz", - "integrity": "sha512-quJQXlTSUGL2LH9SUXo8VwsY4soanhgo6LNSm84E1LBcE8s3O0wpdiRzyR9z/ZZJMlMWv37qOOb9pdJlMUEKFQ==", - "dev": true - }, - "ansi-styles": { - "version": "4.3.0", - "resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-4.3.0.tgz", - "integrity": "sha512-zbB9rCJAT1rbjiVDb2hqKFHNYLxgtk8NURxZ3IZwD3F6NtxbXZQCnnSi1Lkx+IDohdPlFp222wVALIheZJQSEg==", - "dev": true, - "requires": { - "color-convert": "^2.0.1" - } - }, - "chalk": { - "version": "4.1.2", - "resolved": "https://registry.npmjs.org/chalk/-/chalk-4.1.2.tgz", - "integrity": "sha512-oKnbhFyRIXpUuez8iBMmyEa4nbj4IOQyuhc/wy9kY7/WVPcwIO9VA668Pu8RkO7+0G76SLROeyw9CpQ061i4mA==", - "dev": true, - "requires": { - "ansi-styles": "^4.1.0", - "supports-color": "^7.1.0" - }, - "dependencies": { - "supports-color": { - "version": "7.2.0", - "resolved": "https://registry.npmjs.org/supports-color/-/supports-color-7.2.0.tgz", - "integrity": "sha512-qpCAvRl9stuOHveKsn7HncJRvv501qIacKzQlO/+Lwxc9+0q2wLyv4Dfvt80/DPn2pqOBsJdDiogXGR9+OvwRw==", - "dev": true, - "requires": { - "has-flag": "^4.0.0" - } - } - } - }, - "cliui": { - "version": "7.0.4", - "resolved": "https://registry.npmjs.org/cliui/-/cliui-7.0.4.tgz", - "integrity": "sha512-OcRE68cOsVMXp1Yvonl/fzkQOyjLSu/8bhPDfQt0e0/Eb283TKP20Fs2MqoPsr9SwA595rRCA+QMzYc9nBP+JQ==", - "dev": true, - "requires": { - "string-width": "^4.2.0", - "strip-ansi": "^6.0.0", - "wrap-ansi": "^7.0.0" - } - }, - "emoji-regex": { - "version": "8.0.0", - "resolved": "https://registry.npmjs.org/emoji-regex/-/emoji-regex-8.0.0.tgz", - "integrity": "sha512-MSjYzcWNOA0ewAHpz0MxpYFvwg6yjy1NG3xteoqz644VCo/RPgnr1/GGt+ic3iJTzQ8Eu3TdM14SawnVUmGE6A==", - "dev": true - }, - "escape-string-regexp": { - "version": "4.0.0", - "resolved": "https://registry.npmjs.org/escape-string-regexp/-/escape-string-regexp-4.0.0.tgz", - "integrity": "sha512-TtpcNJ3XAzx3Gq8sWRzJaVajRs0uVxA2YAkdb1jm2YkPz4G6egUFAyA3n5vtEIZefPk5Wa4UXbKuS5fKkJWdgA==", - "dev": true - }, - "find-up": { - "version": "5.0.0", - "resolved": "https://registry.npmjs.org/find-up/-/find-up-5.0.0.tgz", - "integrity": "sha512-78/PXT1wlLLDgTzDs7sjq9hzz0vXD+zn+7wypEe4fXQxCmdmqfGsEPQxmiCSQI3ajFV91bVSsvNtrJRiW6nGng==", - "dev": true, - "requires": { - "locate-path": "^6.0.0", - "path-exists": "^4.0.0" - } - }, - "glob": { - "version": "7.2.0", - "resolved": "https://registry.npmjs.org/glob/-/glob-7.2.0.tgz", - "integrity": "sha512-lmLf6gtyrPq8tTjSmrO94wBeQbFR3HbLHbuyD69wuyQkImp2hWqMGB47OX65FBkPffO641IP9jWa1z4ivqG26Q==", - "dev": true, - "requires": { - "fs.realpath": "^1.0.0", - "inflight": "^1.0.4", - "inherits": "2", - "minimatch": "^3.0.4", - "once": "^1.3.0", - "path-is-absolute": "^1.0.0" - }, - "dependencies": { - "minimatch": { - "version": "3.1.2", - "resolved": "https://registry.npmjs.org/minimatch/-/minimatch-3.1.2.tgz", - "integrity": "sha512-J7p63hRiAjw1NDEww1W7i37+ByIrOWO5XQQAzZ3VOcL0PNybwpfmV/N05zFAzwQ9USyEcX6t3UO+K5aqBQOIHw==", - "dev": true, - "requires": { - "brace-expansion": "^1.1.7" - } - } - } - }, - "is-unicode-supported": { - "version": "0.1.0", - "resolved": "https://registry.npmjs.org/is-unicode-supported/-/is-unicode-supported-0.1.0.tgz", - "integrity": "sha512-knxG2q4UC3u8stRGyAVJCOdxFmv5DZiRcdlIaAQXAbSfJya+OhopNotLQrstBhququ4ZpuKbDc/8S6mgXgPFPw==", - "dev": true - }, - "locate-path": { - "version": "6.0.0", - "resolved": "https://registry.npmjs.org/locate-path/-/locate-path-6.0.0.tgz", - "integrity": "sha512-iPZK6eYjbxRu3uB4/WZ3EsEIMJFMqAoopl3R+zuq0UjcAm/MO6KCweDgPfP3elTztoKP3KtnVHxTn2NHBSDVUw==", - "dev": true, - "requires": { - "p-locate": "^5.0.0" - } - }, - "log-symbols": { - "version": "4.1.0", - "resolved": "https://registry.npmjs.org/log-symbols/-/log-symbols-4.1.0.tgz", - "integrity": "sha512-8XPvpAA8uyhfteu8pIvQxpJZ7SYYdpUivZpGy6sFsBuKRY/7rQGavedeB8aK+Zkyq6upMFVL/9AW6vOYzfRyLg==", - "dev": true, - "requires": { - "chalk": "^4.1.0", - "is-unicode-supported": "^0.1.0" - } - }, - "minimatch": { - "version": "5.0.1", - "resolved": "https://registry.npmjs.org/minimatch/-/minimatch-5.0.1.tgz", - "integrity": "sha512-nLDxIFRyhDblz3qMuq+SoRZED4+miJ/G+tdDrjkkkRnjAsBexeGpgjLEQ0blJy7rHhR2b93rhQY4SvyWu9v03g==", - "dev": true, - "requires": { - "brace-expansion": "^2.0.1" - }, - "dependencies": { - "brace-expansion": { - "version": "2.0.1", - "resolved": "https://registry.npmjs.org/brace-expansion/-/brace-expansion-2.0.1.tgz", - "integrity": "sha512-XnAIvQ8eM+kC6aULx6wuQiwVsnzsi9d3WxzV3FpWTGA19F621kwdbsAcFKXgKUHZWsy+mY6iL1sHTxWEFCytDA==", - "dev": true, - "requires": { - "balanced-match": "^1.0.0" - } - } - } - }, - "ms": { - "version": "2.1.3", - "resolved": "https://registry.npmjs.org/ms/-/ms-2.1.3.tgz", - "integrity": "sha512-6FlzubTLZG3J2a/NVCAleEhjzq5oxgHyaCU9yYXvcLsvoVaHJq/s5xXI6/XXP6tz7R9xAOtHnSO/tXtF3WRTlA==", - "dev": true - }, - "p-limit": { - "version": "3.1.0", - "resolved": "https://registry.npmjs.org/p-limit/-/p-limit-3.1.0.tgz", - "integrity": "sha512-TYOanM3wGwNGsZN2cVTYPArw454xnXj5qmWF1bEoAc4+cU/ol7GVh7odevjp1FNHduHc3KZMcFduxU5Xc6uJRQ==", - "dev": true, - "requires": { - "yocto-queue": "^0.1.0" - } - }, - "p-locate": { - "version": "5.0.0", - "resolved": "https://registry.npmjs.org/p-locate/-/p-locate-5.0.0.tgz", - "integrity": "sha512-LaNjtRWUBY++zB5nE/NwcaoMylSPk+S+ZHNB1TzdbMJMny6dynpAGt7X/tl/QYq3TIeE6nxHppbo2LGymrG5Pw==", - "dev": true, - "requires": { - "p-limit": "^3.0.2" - } - }, - "string-width": { - "version": "4.2.3", - "resolved": "https://registry.npmjs.org/string-width/-/string-width-4.2.3.tgz", - "integrity": "sha512-wKyQRQpjJ0sIp62ErSZdGsjMJWsap5oRNihHhu6G7JVO/9jIB6UyevL+tXuOqrng8j/cxKTWyWUwvSTriiZz/g==", - "dev": true, - "requires": { - "emoji-regex": "^8.0.0", - "is-fullwidth-code-point": "^3.0.0", - "strip-ansi": "^6.0.1" - } - }, - "strip-ansi": { - "version": "6.0.1", - "resolved": "https://registry.npmjs.org/strip-ansi/-/strip-ansi-6.0.1.tgz", - "integrity": "sha512-Y38VPSHcqkFrCpFnQ9vuSXmquuv5oXOKpGeT6aGrr3o3Gc9AlVa6JBfUSOCnbxGGZF+/0ooI7KrPuUSztUdU5A==", - "dev": true, - "requires": { - "ansi-regex": "^5.0.1" - } - }, - "supports-color": { - "version": "8.1.1", - "resolved": "https://registry.npmjs.org/supports-color/-/supports-color-8.1.1.tgz", - "integrity": "sha512-MpUEN2OodtUzxvKQl72cUF7RQ5EiHsGvSsVG0ia9c5RbWGL2CI4C7EpPS8UTBIplnlzZiNuV56w+FuNxy3ty2Q==", - "dev": true, - "requires": { - "has-flag": "^4.0.0" - } - }, - "wrap-ansi": { - "version": "7.0.0", - "resolved": "https://registry.npmjs.org/wrap-ansi/-/wrap-ansi-7.0.0.tgz", - "integrity": "sha512-YVGIj2kamLSTxw6NsZjoBxfSwsn0ycdesmc4p+Q21c5zPuZ1pl+NfxVdxPtdHvmNVOQ6XSYG4AUtyt/Fi7D16Q==", - "dev": true, - "requires": { - "ansi-styles": "^4.0.0", - "string-width": "^4.1.0", - "strip-ansi": "^6.0.0" - } - }, - "y18n": { - "version": "5.0.8", - "resolved": "https://registry.npmjs.org/y18n/-/y18n-5.0.8.tgz", - "integrity": "sha512-0pfFzegeDWJHJIAmTLRP2DwHjdF5s7jo9tuztdQxAhINCdvS+3nGINqPd00AphqJR/0LhANUS6/+7SCb98YOfA==", - "dev": true - }, - "yargs": { - "version": "16.2.0", - "resolved": "https://registry.npmjs.org/yargs/-/yargs-16.2.0.tgz", - "integrity": "sha512-D1mvvtDG0L5ft/jGWkLpG1+m0eQxOfaBvTNELraWj22wSVUMWxZUvYgJYcKh6jGGIkJFhH4IZPQhR4TKpc8mBw==", - "dev": true, - "requires": { - "cliui": "^7.0.2", - "escalade": "^3.1.1", - "get-caller-file": "^2.0.5", - "require-directory": "^2.1.1", - "string-width": "^4.2.0", - "y18n": "^5.0.5", - "yargs-parser": "^20.2.2" - } - }, - "yargs-parser": { - "version": "20.2.4", - "resolved": "https://registry.npmjs.org/yargs-parser/-/yargs-parser-20.2.4.tgz", - "integrity": "sha512-WOkpgNhPTlE73h4VFAFsOnomJVaovO8VqLDzy5saChRBFQFBoMYirowyW+Q9HB4HFF4Z7VZTiG3iSzJJA29yRA==", - "dev": true - } - } - }, - "ms": { - "version": "2.1.2", - "resolved": "https://registry.npmjs.org/ms/-/ms-2.1.2.tgz", - "integrity": "sha512-sGkPx+VjMtmA6MX27oA4FBFELFCZZ4S4XqeGOXCv68tT+jb3vk/RyaKWP0PTKyWtmLSM0b+adUTEvbs1PEaH2w==", - "dev": true - }, - "mute-stream": { - "version": "0.0.8", - "resolved": "https://registry.npmjs.org/mute-stream/-/mute-stream-0.0.8.tgz", - "integrity": "sha512-nnbWWOkoWyUsTjKrhgD0dcz22mdkSnpYqbEjIm2nhwhuxlSkpywJmBo8h0ZqJdkp73mb90SssHkN4rsRaBAfAA==", - "dev": true - }, - "nanoid": { - "version": "3.3.3", - "resolved": "https://registry.npmjs.org/nanoid/-/nanoid-3.3.3.tgz", - "integrity": "sha512-p1sjXuopFs0xg+fPASzQ28agW1oHD7xDsd9Xkf3T15H3c/cifrFHVwrh74PdoklAPi+i7MdRsE47vm2r6JoB+w==", - "dev": true - }, - "natural-compare": { - "version": "1.4.0", - "resolved": "https://registry.npmjs.org/natural-compare/-/natural-compare-1.4.0.tgz", - "integrity": "sha512-OWND8ei3VtNC9h7V60qff3SVobHr996CTwgxubgyQYEpg290h9J0buyECNNJexkFm5sOajh5G116RYA1c8ZMSw==", - "dev": true - }, - "natural-compare-lite": { - "version": "1.4.0", - "resolved": "https://registry.npmjs.org/natural-compare-lite/-/natural-compare-lite-1.4.0.tgz", - "integrity": "sha512-Tj+HTDSJJKaZnfiuw+iaF9skdPpTo2GtEly5JHnWV/hfv2Qj/9RKsGISQtLh2ox3l5EAGw487hnBee0sIJ6v2g==", - "dev": true - }, - "netmask": { - "version": "2.0.2", - "resolved": "https://registry.npmjs.org/netmask/-/netmask-2.0.2.tgz", - "integrity": "sha512-dBpDMdxv9Irdq66304OLfEmQ9tbNRFnFTuZiLo+bD+r332bBmMJ8GBLXklIXXgxd3+v9+KUnZaUR5PJMa75Gsg==", - "dev": true - }, - "new-github-release-url": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/new-github-release-url/-/new-github-release-url-2.0.0.tgz", - "integrity": "sha512-NHDDGYudnvRutt/VhKFlX26IotXe1w0cmkDm6JGquh5bz/bDTw0LufSmH/GxTjEdpHEO+bVKFTwdrcGa/9XlKQ==", - "dev": true, - "requires": { - "type-fest": "^2.5.1" - }, - "dependencies": { - "type-fest": { - "version": "2.19.0", - "resolved": "https://registry.npmjs.org/type-fest/-/type-fest-2.19.0.tgz", - "integrity": "sha512-RAH822pAdBgcNMAfWnCBU3CFZcfZ/i1eZjwFU/dsLKumyuuP3niueg2UAukXYF0E2AAoc82ZSSf9J0WQBinzHA==", - "dev": true - } - } - }, - "nise": { - "version": "5.1.4", - "resolved": "https://registry.npmjs.org/nise/-/nise-5.1.4.tgz", - "integrity": "sha512-8+Ib8rRJ4L0o3kfmyVCL7gzrohyDe0cMFTBa2d364yIrEGMEoetznKJx899YxjybU6bL9SQkYPSBBs1gyYs8Xg==", - "dev": true, - "requires": { - "@sinonjs/commons": "^2.0.0", - "@sinonjs/fake-timers": "^10.0.2", - "@sinonjs/text-encoding": "^0.7.1", - "just-extend": "^4.0.2", - "path-to-regexp": "^1.7.0" - } - }, - "node-domexception": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/node-domexception/-/node-domexception-1.0.0.tgz", - "integrity": "sha512-/jKZoMpw0F8GRwl4/eLROPA3cfcXtLApP0QzLmUT/HuPCZWyB7IY9ZrMeKw2O/nFIqPQB3PVM9aYm0F312AXDQ==", - "dev": true - }, - "node-fetch": { - "version": "3.3.0", - "resolved": "https://registry.npmjs.org/node-fetch/-/node-fetch-3.3.0.tgz", - "integrity": "sha512-BKwRP/O0UvoMKp7GNdwPlObhYGB5DQqwhEDQlNKuoqwVYSxkSZCSbHjnFFmUEtwSKRPU4kNK8PbDYYitwaE3QA==", - "dev": true, - "requires": { - "data-uri-to-buffer": "^4.0.0", - "fetch-blob": "^3.1.4", - "formdata-polyfill": "^4.0.10" - } - }, - "node-preload": { - "version": "0.2.1", - "resolved": "https://registry.npmjs.org/node-preload/-/node-preload-0.2.1.tgz", - "integrity": "sha512-RM5oyBy45cLEoHqCeh+MNuFAxO0vTFBLskvQbOKnEE7YTTSN4tbN8QWDIPQ6L+WvKsB/qLEGpYe2ZZ9d4W9OIQ==", - "dev": true, - "requires": { - "process-on-spawn": "^1.0.0" - } - }, - "node-releases": { - "version": "2.0.8", - "resolved": "https://registry.npmjs.org/node-releases/-/node-releases-2.0.8.tgz", - "integrity": "sha512-dFSmB8fFHEH/s81Xi+Y/15DQY6VHW81nXRj86EMSL3lmuTmK1e+aT4wrFCkTbm+gSwkw4KpX+rT/pMM2c1mF+A==", - "dev": true - }, - "normalize-path": { - "version": "3.0.0", - "resolved": "https://registry.npmjs.org/normalize-path/-/normalize-path-3.0.0.tgz", - "integrity": "sha512-6eZs5Ls3WtCisHWp9S2GUy8dqkpGi4BVSz3GaqiE6ezub0512ESztXUwUB6C6IKbQkY2Pnb/mD4WYojCRwcwLA==", - "dev": true - }, - "normalize-url": { - "version": "8.0.0", - "resolved": "https://registry.npmjs.org/normalize-url/-/normalize-url-8.0.0.tgz", - "integrity": "sha512-uVFpKhj5MheNBJRTiMZ9pE/7hD1QTeEvugSJW/OmLzAp78PB5O6adfMNTvmfKhXBkvCzC+rqifWcVYpGFwTjnw==", - "dev": true - }, - "npm-run-path": { - "version": "5.1.0", - "resolved": "https://registry.npmjs.org/npm-run-path/-/npm-run-path-5.1.0.tgz", - "integrity": "sha512-sJOdmRGrY2sjNTRMbSvluQqg+8X7ZK61yvzBEIDhz4f8z1TZFYABsqjjCBd/0PUNE9M6QDgHJXQkGUEm7Q+l9Q==", - "dev": true, - "requires": { - "path-key": "^4.0.0" - }, - "dependencies": { - "path-key": { - "version": "4.0.0", - "resolved": "https://registry.npmjs.org/path-key/-/path-key-4.0.0.tgz", - "integrity": "sha512-haREypq7xkM7ErfgIyA0z+Bj4AGKlMSdlQE2jvJo6huWD1EdkKYV+G/T4nq0YEF2vgTT8kqMFKo1uHn950r4SQ==", - "dev": true - } - } - }, - "nyc": { - "version": "15.1.0", - "resolved": "https://registry.npmjs.org/nyc/-/nyc-15.1.0.tgz", - "integrity": "sha512-jMW04n9SxKdKi1ZMGhvUTHBN0EICCRkHemEoE5jm6mTYcqcdas0ATzgUgejlQUHMvpnOZqGB5Xxsv9KxJW1j8A==", - "dev": true, - "requires": { - "@istanbuljs/load-nyc-config": "^1.0.0", - "@istanbuljs/schema": "^0.1.2", - "caching-transform": "^4.0.0", - "convert-source-map": "^1.7.0", - "decamelize": "^1.2.0", - "find-cache-dir": "^3.2.0", - "find-up": "^4.1.0", - "foreground-child": "^2.0.0", - "get-package-type": "^0.1.0", - "glob": "^7.1.6", - "istanbul-lib-coverage": "^3.0.0", - "istanbul-lib-hook": "^3.0.0", - "istanbul-lib-instrument": "^4.0.0", - "istanbul-lib-processinfo": "^2.0.2", - "istanbul-lib-report": "^3.0.0", - "istanbul-lib-source-maps": "^4.0.0", - "istanbul-reports": "^3.0.2", - "make-dir": "^3.0.0", - "node-preload": "^0.2.1", - "p-map": "^3.0.0", - "process-on-spawn": "^1.0.0", - "resolve-from": "^5.0.0", - "rimraf": "^3.0.0", - "signal-exit": "^3.0.2", - "spawn-wrap": "^2.0.0", - "test-exclude": "^6.0.0", - "yargs": "^15.0.2" - }, - "dependencies": { - "resolve-from": { - "version": "5.0.0", - "resolved": "https://registry.npmjs.org/resolve-from/-/resolve-from-5.0.0.tgz", - "integrity": "sha512-qYg9KP24dD5qka9J47d0aVky0N+b4fTU89LN9iDnjB5waksiC49rvMB0PrUJQGoTmH50XPiqOvAjDfaijGxYZw==", - "dev": true - } - } - }, - "object-assign": { - "version": "4.1.1", - "resolved": "https://registry.npmjs.org/object-assign/-/object-assign-4.1.1.tgz", - "integrity": "sha512-rJgTQnkUnH1sFw8yT6VSU3zD3sWmu6sZhIseY8VX+GRu3P6F7Fu+JNDoXfklElbLJSnc3FUQHVe4cU5hj+BcUg==", - "dev": true - }, - "object-inspect": { - "version": "1.12.3", - "resolved": "https://registry.npmjs.org/object-inspect/-/object-inspect-1.12.3.tgz", - "integrity": "sha512-geUvdk7c+eizMNUDkRpW1wJwgfOiOeHbxBR/hLXK1aT6zmVSO0jsQcs7fj6MGw89jC/cjGfLcNOrtMYtGqm81g==", - "dev": true - }, - "object-keys": { - "version": "1.1.1", - "resolved": "https://registry.npmjs.org/object-keys/-/object-keys-1.1.1.tgz", - "integrity": "sha512-NuAESUOUMrlIXOfHKzD6bpPu3tYt3xvjNdRIQ+FeT0lNb4K8WR70CaDxhuNguS2XG+GjkyMwOzsN5ZktImfhLA==", - "dev": true - }, - "object.assign": { - "version": "4.1.4", - "resolved": "https://registry.npmjs.org/object.assign/-/object.assign-4.1.4.tgz", - "integrity": "sha512-1mxKf0e58bvyjSCtKYY4sRe9itRk3PJpquJOjeIkz885CczcI4IvJJDLPS72oowuSh+pBxUFROpX+TU++hxhZQ==", - "dev": true, - "requires": { - "call-bind": "^1.0.2", - "define-properties": "^1.1.4", - "has-symbols": "^1.0.3", - "object-keys": "^1.1.1" - } - }, - "once": { - "version": "1.4.0", - "resolved": "https://registry.npmjs.org/once/-/once-1.4.0.tgz", - "integrity": "sha512-lNaJgI+2Q5URQBkccEKHTQOPaXdUxnZZElQTZY0MFUAuaEqe1E+Nyvgdz/aIyNi6Z9MzO5dv1H8n58/GELp3+w==", - "dev": true, - "requires": { - "wrappy": "1" - } - }, - "onetime": { - "version": "6.0.0", - "resolved": "https://registry.npmjs.org/onetime/-/onetime-6.0.0.tgz", - "integrity": "sha512-1FlR+gjXK7X+AsAHso35MnyN5KqGwJRi/31ft6x0M194ht7S+rWAvd7PHss9xSKMzE0asv1pyIHaJYq+BbacAQ==", - "dev": true, - "requires": { - "mimic-fn": "^4.0.0" - } - }, - "open": { - "version": "8.4.0", - "resolved": "https://registry.npmjs.org/open/-/open-8.4.0.tgz", - "integrity": "sha512-XgFPPM+B28FtCCgSb9I+s9szOC1vZRSwgWsRUA5ylIxRTgKozqjOCrVOqGsYABPYK5qnfqClxZTFBa8PKt2v6Q==", - "dev": true, - "requires": { - "define-lazy-prop": "^2.0.0", - "is-docker": "^2.1.1", - "is-wsl": "^2.2.0" - } - }, - "optionator": { - "version": "0.9.1", - "resolved": "https://registry.npmjs.org/optionator/-/optionator-0.9.1.tgz", - "integrity": "sha512-74RlY5FCnhq4jRxVUPKDaRwrVNXMqsGsiW6AJw4XK8hmtm10wC0ypZBLw5IIp85NZMr91+qd1RvvENwg7jjRFw==", - "dev": true, - "requires": { - "deep-is": "^0.1.3", - "fast-levenshtein": "^2.0.6", - "levn": "^0.4.1", - "prelude-ls": "^1.2.1", - "type-check": "^0.4.0", - "word-wrap": "^1.2.3" - } - }, - "ora": { - "version": "6.1.2", - "resolved": "https://registry.npmjs.org/ora/-/ora-6.1.2.tgz", - "integrity": "sha512-EJQ3NiP5Xo94wJXIzAyOtSb0QEIAUu7m8t6UZ9krbz0vAJqr92JpcK/lEXg91q6B9pEGqrykkd2EQplnifDSBw==", - "dev": true, - "requires": { - "bl": "^5.0.0", - "chalk": "^5.0.0", - "cli-cursor": "^4.0.0", - "cli-spinners": "^2.6.1", - "is-interactive": "^2.0.0", - "is-unicode-supported": "^1.1.0", - "log-symbols": "^5.1.0", - "strip-ansi": "^7.0.1", - "wcwidth": "^1.0.1" - } - }, - "os-name": { - "version": "5.0.1", - "resolved": "https://registry.npmjs.org/os-name/-/os-name-5.0.1.tgz", - "integrity": "sha512-0EQpaHUHq7olp2/YFUr+0vZi9tMpDTblHGz+Ch5RntKxiRXOAY0JOz1UlxhSjMSksHvkm13eD6elJj3M8Ht/kw==", - "dev": true, - "requires": { - "macos-release": "^3.0.1", - "windows-release": "^5.0.1" - } - }, - "os-tmpdir": { - "version": "1.0.2", - "resolved": "https://registry.npmjs.org/os-tmpdir/-/os-tmpdir-1.0.2.tgz", - "integrity": "sha512-D2FR03Vir7FIu45XBY20mTb+/ZSWB00sjU9jdQXt83gDrI4Ztz5Fs7/yy74g2N5SVQY4xY1qDr4rNddwYRVX0g==", - "dev": true - }, - "p-cancelable": { - "version": "3.0.0", - "resolved": "https://registry.npmjs.org/p-cancelable/-/p-cancelable-3.0.0.tgz", - "integrity": "sha512-mlVgR3PGuzlo0MmTdk4cXqXWlwQDLnONTAg6sm62XkMJEiRxN3GL3SffkYvqwonbkJBcrI7Uvv5Zh9yjvn2iUw==", - "dev": true - }, - "p-limit": { - "version": "2.3.0", - "resolved": "https://registry.npmjs.org/p-limit/-/p-limit-2.3.0.tgz", - "integrity": "sha512-//88mFWSJx8lxCzwdAABTJL2MyWB12+eIY7MDL2SqLmAkeKU9qxRvWuSyTjm3FUmpBEMuFfckAIqEaVGUDxb6w==", - "dev": true, - "requires": { - "p-try": "^2.0.0" - } - }, - "p-locate": { - "version": "4.1.0", - "resolved": "https://registry.npmjs.org/p-locate/-/p-locate-4.1.0.tgz", - "integrity": "sha512-R79ZZ/0wAxKGu3oYMlz8jy/kbhsNrS7SKZ7PxEHBgJ5+F2mtFW2fK2cOtBh1cHYkQsbzFV7I+EoRKe6Yt0oK7A==", - "dev": true, - "requires": { - "p-limit": "^2.2.0" - } - }, - "p-map": { - "version": "3.0.0", - "resolved": "https://registry.npmjs.org/p-map/-/p-map-3.0.0.tgz", - "integrity": "sha512-d3qXVTF/s+W+CdJ5A29wywV2n8CQQYahlgz2bFiA+4eVNJbHJodPZ+/gXwPGh0bOqA+j8S+6+ckmvLGPk1QpxQ==", - "dev": true, - "requires": { - "aggregate-error": "^3.0.0" - } - }, - "p-try": { - "version": "2.2.0", - "resolved": "https://registry.npmjs.org/p-try/-/p-try-2.2.0.tgz", - "integrity": "sha512-R4nPAVTAU0B9D35/Gk3uJf/7XYbQcyohSKdvAxIRSNghFl4e71hVoGnBNQz9cWaXxO2I10KTC+3jMdvvoKw6dQ==", - "dev": true - }, - "pac-proxy-agent": { - "version": "5.0.0", - "resolved": "https://registry.npmjs.org/pac-proxy-agent/-/pac-proxy-agent-5.0.0.tgz", - "integrity": "sha512-CcFG3ZtnxO8McDigozwE3AqAw15zDvGH+OjXO4kzf7IkEKkQ4gxQ+3sdF50WmhQ4P/bVusXcqNE2S3XrNURwzQ==", - "dev": true, - "requires": { - "@tootallnate/once": "1", - "agent-base": "6", - "debug": "4", - "get-uri": "3", - "http-proxy-agent": "^4.0.1", - "https-proxy-agent": "5", - "pac-resolver": "^5.0.0", - "raw-body": "^2.2.0", - "socks-proxy-agent": "5" - } - }, - "pac-resolver": { - "version": "5.0.1", - "resolved": "https://registry.npmjs.org/pac-resolver/-/pac-resolver-5.0.1.tgz", - "integrity": "sha512-cy7u00ko2KVgBAjuhevqpPeHIkCIqPe1v24cydhWjmeuzaBfmUWFCZJ1iAh5TuVzVZoUzXIW7K8sMYOZ84uZ9Q==", - "dev": true, - "requires": { - "degenerator": "^3.0.2", - "ip": "^1.1.5", - "netmask": "^2.0.2" - } - }, - "package-hash": { - "version": "4.0.0", - "resolved": "https://registry.npmjs.org/package-hash/-/package-hash-4.0.0.tgz", - "integrity": "sha512-whdkPIooSu/bASggZ96BWVvZTRMOFxnyUG5PnTSGKoJE2gd5mbVNmR2Nj20QFzxYYgAXpoqC+AiXzl+UMRh7zQ==", - "dev": true, - "requires": { - "graceful-fs": "^4.1.15", - "hasha": "^5.0.0", - "lodash.flattendeep": "^4.4.0", - "release-zalgo": "^1.0.0" - } - }, - "package-json": { - "version": "8.1.0", - "resolved": "https://registry.npmjs.org/package-json/-/package-json-8.1.0.tgz", - "integrity": "sha512-hySwcV8RAWeAfPsXb9/HGSPn8lwDnv6fabH+obUZKX169QknRkRhPxd1yMubpKDskLFATkl3jHpNtVtDPFA0Wg==", - "dev": true, - "requires": { - "got": "^12.1.0", - "registry-auth-token": "^5.0.1", - "registry-url": "^6.0.0", - "semver": "^7.3.7" - }, - "dependencies": { - "lru-cache": { - "version": "6.0.0", - "resolved": "https://registry.npmjs.org/lru-cache/-/lru-cache-6.0.0.tgz", - "integrity": "sha512-Jo6dJ04CmSjuznwJSS3pUeWmd/H0ffTlkXXgwZi+eq1UCmqQwCh+eLsYOYCwY991i2Fah4h1BEMCx4qThGbsiA==", - "dev": true, - "requires": { - "yallist": "^4.0.0" - } - }, - "semver": { - "version": "7.3.8", - "resolved": "https://registry.npmjs.org/semver/-/semver-7.3.8.tgz", - "integrity": "sha512-NB1ctGL5rlHrPJtFDVIVzTyQylMLu9N9VICA6HSFJo8MCGVTMW6gfpicwKmmK/dAjTOrqu5l63JJOpDSrAis3A==", - "dev": true, - "requires": { - "lru-cache": "^6.0.0" - } - }, - "yallist": { - "version": "4.0.0", - "resolved": "https://registry.npmjs.org/yallist/-/yallist-4.0.0.tgz", - "integrity": "sha512-3wdGidZyq5PB084XLES5TpOSRA3wjXAlIWMhum2kRcv/41Sn2emQ0dycQW4uZXLejwKvg6EsvbdlVL+FYEct7A==", - "dev": true - } - } - }, - "parent-module": { - "version": "1.0.1", - "resolved": "https://registry.npmjs.org/parent-module/-/parent-module-1.0.1.tgz", - "integrity": "sha512-GQ2EWRpQV8/o+Aw8YqtfZZPfNRWZYkbidE9k5rpl/hC3vtHHBfGm2Ifi6qWV+coDGkrUKZAxE3Lot5kcsRlh+g==", - "dev": true, - "requires": { - "callsites": "^3.0.0" - } - }, - "parse-json": { - "version": "5.2.0", - "resolved": "https://registry.npmjs.org/parse-json/-/parse-json-5.2.0.tgz", - "integrity": "sha512-ayCKvm/phCGxOkYRSCM82iDwct8/EonSEgCSxWxD7ve6jHggsFl4fZVQBPRNgQoKiuV/odhFrGzQXZwbifC8Rg==", - "dev": true, - "requires": { - "@babel/code-frame": "^7.0.0", - "error-ex": "^1.3.1", - "json-parse-even-better-errors": "^2.3.0", - "lines-and-columns": "^1.1.6" - } - }, - "parse-path": { - "version": "7.0.0", - "resolved": "https://registry.npmjs.org/parse-path/-/parse-path-7.0.0.tgz", - "integrity": "sha512-Euf9GG8WT9CdqwuWJGdf3RkUcTBArppHABkO7Lm8IzRQp0e2r/kkFnmhu4TSK30Wcu5rVAZLmfPKSBBi9tWFog==", - "dev": true, - "requires": { - "protocols": "^2.0.0" - } - }, - "parse-url": { - "version": "8.1.0", - "resolved": "https://registry.npmjs.org/parse-url/-/parse-url-8.1.0.tgz", - "integrity": "sha512-xDvOoLU5XRrcOZvnI6b8zA6n9O9ejNk/GExuz1yBuWUGn9KA97GI6HTs6u02wKara1CeVmZhH+0TZFdWScR89w==", - "dev": true, - "requires": { - "parse-path": "^7.0.0" - } - }, - "path-exists": { - "version": "4.0.0", - "resolved": "https://registry.npmjs.org/path-exists/-/path-exists-4.0.0.tgz", - "integrity": "sha512-ak9Qy5Q7jYb2Wwcey5Fpvg2KoAc/ZIhLSLOSBmRmygPsGwkVVt0fZa0qrtMz+m6tJTAHfZQ8FnmB4MG4LWy7/w==", - "dev": true - }, - "path-is-absolute": { - "version": "1.0.1", - "resolved": "https://registry.npmjs.org/path-is-absolute/-/path-is-absolute-1.0.1.tgz", - "integrity": "sha512-AVbw3UJ2e9bq64vSaS9Am0fje1Pa8pbGqTTsmXfaIiMpnr5DlDhfJOuLj9Sf95ZPVDAUerDfEk88MPmPe7UCQg==", - "dev": true - }, - "path-key": { - "version": "3.1.1", - "resolved": "https://registry.npmjs.org/path-key/-/path-key-3.1.1.tgz", - "integrity": "sha512-ojmeN0qd+y0jszEtoY48r0Peq5dwMEkIlCOu6Q5f41lfkswXuKtYrhgoTpLnyIcHm24Uhqx+5Tqm2InSwLhE6Q==", - "dev": true - }, - "path-parse": { - "version": "1.0.7", - "resolved": "https://registry.npmjs.org/path-parse/-/path-parse-1.0.7.tgz", - "integrity": "sha512-LDJzPVEEEPR+y48z93A0Ed0yXb8pAByGWo/k5YYdYgpY2/2EsOsksJrq7lOHxryrVOn1ejG6oAp8ahvOIQD8sw==", - "dev": true - }, - "path-to-regexp": { - "version": "1.8.0", - "resolved": "https://registry.npmjs.org/path-to-regexp/-/path-to-regexp-1.8.0.tgz", - "integrity": "sha512-n43JRhlUKUAlibEJhPeir1ncUID16QnEjNpwzNdO3Lm4ywrBpBZ5oLD0I6br9evr1Y9JTqwRtAh7JLoOzAQdVA==", - "dev": true, - "requires": { - "isarray": "0.0.1" - }, - "dependencies": { - "isarray": { - "version": "0.0.1", - "resolved": "https://registry.npmjs.org/isarray/-/isarray-0.0.1.tgz", - "integrity": "sha512-D2S+3GLxWH+uhrNEcoh/fnmYeP8E8/zHl644d/jdA0g2uyXvy3sb0qxotE+ne0LtccHknQzWwZEzhak7oJ0COQ==", - "dev": true - } - } - }, - "path-type": { - "version": "4.0.0", - "resolved": "https://registry.npmjs.org/path-type/-/path-type-4.0.0.tgz", - "integrity": "sha512-gDKb8aZMDeD/tZWs9P6+q0J9Mwkdl6xMV8TjnGP3qJVJ06bdMgkbBlLU8IdfOsIsFz2BW1rNVT3XuNEl8zPAvw==", - "dev": true - }, - "picocolors": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/picocolors/-/picocolors-1.0.0.tgz", - "integrity": "sha512-1fygroTLlHu66zi26VoTDv8yRgm0Fccecssto+MhsZ0D/DGW2sm8E8AjW7NU5VVTRt5GxbeZ5qBuJr+HyLYkjQ==", - "dev": true - }, - "picomatch": { - "version": "2.3.1", - "resolved": "https://registry.npmjs.org/picomatch/-/picomatch-2.3.1.tgz", - "integrity": "sha512-JU3teHTNjmE2VCGFzuY8EXzCDVwEqB2a8fsIvwaStHhAWJEeVd1o1QD80CU6+ZdEXXSLbSsuLwJjkCBWqRQUVA==", - "dev": true - }, - "pify": { - "version": "2.3.0", - "resolved": "https://registry.npmjs.org/pify/-/pify-2.3.0.tgz", - "integrity": "sha512-udgsAY+fTnvv7kI7aaxbqwWNb0AHiB0qBO89PZKPkoTmGOgdbrHDKD+0B2X4uTfJ/FT1R09r9gTsjUjNJotuog==", - "dev": true - }, - "pinkie": { - "version": "2.0.4", - "resolved": "https://registry.npmjs.org/pinkie/-/pinkie-2.0.4.tgz", - "integrity": "sha512-MnUuEycAemtSaeFSjXKW/aroV7akBbY+Sv+RkyqFjgAe73F+MR0TBWKBRDkmfWq/HiFmdavfZ1G7h4SPZXaCSg==", - "dev": true - }, - "pinkie-promise": { - "version": "2.0.1", - "resolved": "https://registry.npmjs.org/pinkie-promise/-/pinkie-promise-2.0.1.tgz", - "integrity": "sha512-0Gni6D4UcLTbv9c57DfxDGdr41XfgUjqWZu492f0cIGr16zDU06BWP/RAEvOuo7CQ0CNjHaLlM59YJJFm3NWlw==", - "dev": true, - "requires": { - "pinkie": "^2.0.0" - } - }, - "pkg-dir": { - "version": "4.2.0", - "resolved": "https://registry.npmjs.org/pkg-dir/-/pkg-dir-4.2.0.tgz", - "integrity": "sha512-HRDzbaKjC+AOWVXxAU/x54COGeIv9eb+6CkDSQoNTt4XyWoIJvuPsXizxu/Fr23EiekbtZwmh1IcIG/l/a10GQ==", - "dev": true, - "requires": { - "find-up": "^4.0.0" - } - }, - "prelude-ls": { - "version": "1.2.1", - "resolved": "https://registry.npmjs.org/prelude-ls/-/prelude-ls-1.2.1.tgz", - "integrity": "sha512-vkcDPrRZo1QZLbn5RLGPpg/WmIQ65qoWWhcGKf/b5eplkkarX0m9z8ppCat4mlOqUsWpyNuYgO3VRyrYHSzX5g==", - "dev": true - }, - "process-on-spawn": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/process-on-spawn/-/process-on-spawn-1.0.0.tgz", - "integrity": "sha512-1WsPDsUSMmZH5LeMLegqkPDrsGgsWwk1Exipy2hvB0o/F0ASzbpIctSCcZIK1ykJvtTJULEH+20WOFjMvGnCTg==", - "dev": true, - "requires": { - "fromentries": "^1.2.0" - } - }, - "promise.allsettled": { - "version": "1.0.6", - "resolved": "https://registry.npmjs.org/promise.allsettled/-/promise.allsettled-1.0.6.tgz", - "integrity": "sha512-22wJUOD3zswWFqgwjNHa1965LvqTX87WPu/lreY2KSd7SVcERfuZ4GfUaOnJNnvtoIv2yXT/W00YIGMetXtFXg==", - "dev": true, - "requires": { - "array.prototype.map": "^1.0.5", - "call-bind": "^1.0.2", - "define-properties": "^1.1.4", - "es-abstract": "^1.20.4", - "get-intrinsic": "^1.1.3", - "iterate-value": "^1.0.2" - } - }, - "proto-list": { - "version": "1.2.4", - "resolved": "https://registry.npmjs.org/proto-list/-/proto-list-1.2.4.tgz", - "integrity": "sha512-vtK/94akxsTMhe0/cbfpR+syPuszcuwhqVjJq26CuNDgFGj682oRBXOP5MJpv2r7JtE8MsiepGIqvvOTBwn2vA==", - "dev": true - }, - "protocols": { - "version": "2.0.1", - "resolved": "https://registry.npmjs.org/protocols/-/protocols-2.0.1.tgz", - "integrity": "sha512-/XJ368cyBJ7fzLMwLKv1e4vLxOju2MNAIokcr7meSaNcVbWz/CPcW22cP04mwxOErdA5mwjA8Q6w/cdAQxVn7Q==", - "dev": true - }, - "proxy-agent": { - "version": "5.0.0", - "resolved": "https://registry.npmjs.org/proxy-agent/-/proxy-agent-5.0.0.tgz", - "integrity": "sha512-gkH7BkvLVkSfX9Dk27W6TyNOWWZWRilRfk1XxGNWOYJ2TuedAv1yFpCaU9QSBmBe716XOTNpYNOzhysyw8xn7g==", - "dev": true, - "requires": { - "agent-base": "^6.0.0", - "debug": "4", - "http-proxy-agent": "^4.0.0", - "https-proxy-agent": "^5.0.0", - "lru-cache": "^5.1.1", - "pac-proxy-agent": "^5.0.0", - "proxy-from-env": "^1.0.0", - "socks-proxy-agent": "^5.0.0" - } - }, - "proxy-from-env": { - "version": "1.1.0", - "resolved": "https://registry.npmjs.org/proxy-from-env/-/proxy-from-env-1.1.0.tgz", - "integrity": "sha512-D+zkORCbA9f1tdWRK0RaCR3GPv50cMxcrz4X8k5LTSUD1Dkw47mKJEZQNunItRTkWwgtaUSo1RVFRIG9ZXiFYg==", - "dev": true - }, - "punycode": { - "version": "2.2.0", - "resolved": "https://registry.npmjs.org/punycode/-/punycode-2.2.0.tgz", - "integrity": "sha512-LN6QV1IJ9ZhxWTNdktaPClrNfp8xdSAYS0Zk2ddX7XsXZAxckMHPCBcHRo0cTcEIgYPRiGEkmji3Idkh2yFtYw==", - "dev": true - }, - "pupa": { - "version": "3.1.0", - "resolved": "https://registry.npmjs.org/pupa/-/pupa-3.1.0.tgz", - "integrity": "sha512-FLpr4flz5xZTSJxSeaheeMKN/EDzMdK7b8PTOC6a5PYFKTucWbdqjgqaEyH0shFiSJrVB1+Qqi4Tk19ccU6Aug==", - "dev": true, - "requires": { - "escape-goat": "^4.0.0" - } - }, - "queue-microtask": { - "version": "1.2.3", - "resolved": "https://registry.npmjs.org/queue-microtask/-/queue-microtask-1.2.3.tgz", - "integrity": "sha512-NuaNSa6flKT5JaSYQzJok04JzTL1CA6aGhv5rfLW3PgqA+M2ChpZQnAC8h8i4ZFkBS8X5RqkDBHA7r4hej3K9A==", - "dev": true - }, - "quick-lru": { - "version": "5.1.1", - "resolved": "https://registry.npmjs.org/quick-lru/-/quick-lru-5.1.1.tgz", - "integrity": "sha512-WuyALRjWPDGtt/wzJiadO5AXY+8hZ80hVpe6MyivgraREW751X3SbhRvG3eLKOYN+8VEvqLcf3wdnt44Z4S4SA==", - "dev": true - }, - "randombytes": { - "version": "2.1.0", - "resolved": "https://registry.npmjs.org/randombytes/-/randombytes-2.1.0.tgz", - "integrity": "sha512-vYl3iOX+4CKUWuxGi9Ukhie6fsqXqS9FE2Zaic4tNFD2N2QQaXOMFbuKK4QmDHC0JO6B1Zp41J0LpT0oR68amQ==", - "dev": true, - "requires": { - "safe-buffer": "^5.1.0" - } - }, - "raw-body": { - "version": "2.5.1", - "resolved": "https://registry.npmjs.org/raw-body/-/raw-body-2.5.1.tgz", - "integrity": "sha512-qqJBtEyVgS0ZmPGdCFPWJ3FreoqvG4MVQln/kCgF7Olq95IbOp0/BWyMwbdtn4VTvkM8Y7khCQ2Xgk/tcrCXig==", - "dev": true, - "requires": { - "bytes": "3.1.2", - "http-errors": "2.0.0", - "iconv-lite": "0.4.24", - "unpipe": "1.0.0" - } - }, - "rc": { - "version": "1.2.8", - "resolved": "https://registry.npmjs.org/rc/-/rc-1.2.8.tgz", - "integrity": "sha512-y3bGgqKj3QBdxLbLkomlohkvsA8gdAiUQlSBJnBhfn+BPxg4bc62d8TcBW15wavDfgexCgccckhcZvywyQYPOw==", - "dev": true, - "requires": { - "deep-extend": "^0.6.0", - "ini": "~1.3.0", - "minimist": "^1.2.0", - "strip-json-comments": "~2.0.1" - }, - "dependencies": { - "ini": { - "version": "1.3.8", - "resolved": "https://registry.npmjs.org/ini/-/ini-1.3.8.tgz", - "integrity": "sha512-JV/yugV2uzW5iMRSiZAyDtQd+nxtUnjeLt0acNdw98kKLrvuRVyB80tsREOE7yvGVgalhZ6RNXCmEHkUKBKxew==", - "dev": true - }, - "strip-json-comments": { - "version": "2.0.1", - "resolved": "https://registry.npmjs.org/strip-json-comments/-/strip-json-comments-2.0.1.tgz", - "integrity": "sha512-4gB8na07fecVVkOI6Rs4e7T6NOTki5EmL7TUduTs6bu3EdnSycntVJ4re8kgZA+wx9IueI2Y11bfbgwtzuE0KQ==", - "dev": true - } - } - }, - "readable-stream": { - "version": "3.6.0", - "resolved": "https://registry.npmjs.org/readable-stream/-/readable-stream-3.6.0.tgz", - "integrity": "sha512-BViHy7LKeTz4oNnkcLJ+lVSL6vpiFeX6/d3oSH8zCW7UxP2onchk+vTGB143xuFjHS3deTgkKoXXymXqymiIdA==", - "dev": true, - "requires": { - "inherits": "^2.0.3", - "string_decoder": "^1.1.1", - "util-deprecate": "^1.0.1" - } - }, - "readdirp": { - "version": "3.6.0", - "resolved": "https://registry.npmjs.org/readdirp/-/readdirp-3.6.0.tgz", - "integrity": "sha512-hOS089on8RduqdbhvQ5Z37A0ESjsqz6qnRcffsMU3495FuTdqSm+7bhJ29JvIOsBDEEnan5DPu9t3To9VRlMzA==", - "dev": true, - "requires": { - "picomatch": "^2.2.1" - } - }, - "rechoir": { - "version": "0.6.2", - "resolved": "https://registry.npmjs.org/rechoir/-/rechoir-0.6.2.tgz", - "integrity": "sha512-HFM8rkZ+i3zrV+4LQjwQ0W+ez98pApMGM3HUrN04j3CqzPOzl9nmP15Y8YXNm8QHGv/eacOVEjqhmWpkRV0NAw==", - "dev": true, - "requires": { - "resolve": "^1.1.6" - } - }, - "regexp.prototype.flags": { - "version": "1.4.3", - "resolved": "https://registry.npmjs.org/regexp.prototype.flags/-/regexp.prototype.flags-1.4.3.tgz", - "integrity": "sha512-fjggEOO3slI6Wvgjwflkc4NFRCTZAu5CnNfBd5qOMYhWdn67nJBBu34/TkD++eeFmd8C9r9jfXJ27+nSiRkSUA==", - "dev": true, - "requires": { - "call-bind": "^1.0.2", - "define-properties": "^1.1.3", - "functions-have-names": "^1.2.2" - } - }, - "regexpp": { - "version": "3.2.0", - "resolved": "https://registry.npmjs.org/regexpp/-/regexpp-3.2.0.tgz", - "integrity": "sha512-pq2bWo9mVD43nbts2wGv17XLiNLya+GklZ8kaDLV2Z08gDCsGpnKn9BFMepvWuHCbyVvY7J5o5+BVvoQbmlJLg==", - "dev": true - }, - "registry-auth-token": { - "version": "5.0.1", - "resolved": "https://registry.npmjs.org/registry-auth-token/-/registry-auth-token-5.0.1.tgz", - "integrity": "sha512-UfxVOj8seK1yaIOiieV4FIP01vfBDLsY0H9sQzi9EbbUdJiuuBjJgLa1DpImXMNPnVkBD4eVxTEXcrZA6kfpJA==", - "dev": true, - "requires": { - "@pnpm/npm-conf": "^1.0.4" - } - }, - "registry-url": { - "version": "6.0.1", - "resolved": "https://registry.npmjs.org/registry-url/-/registry-url-6.0.1.tgz", - "integrity": "sha512-+crtS5QjFRqFCoQmvGduwYWEBng99ZvmFvF+cUJkGYF1L1BfU8C6Zp9T7f5vPAwyLkUExpvK+ANVZmGU49qi4Q==", - "dev": true, - "requires": { - "rc": "1.2.8" - } - }, - "release-it": { - "version": "15.6.0", - "resolved": "https://registry.npmjs.org/release-it/-/release-it-15.6.0.tgz", - "integrity": "sha512-NXewgzO8QV1LOFjn2K7/dgE1Y1cG+2JiLOU/x9X/Lq9UdFn2hTH1r9SSrufCxG+y/Rp+oN8liYTsNptKrj92kg==", - "dev": true, - "requires": { - "@iarna/toml": "2.2.5", - "@octokit/rest": "19.0.5", - "async-retry": "1.3.3", - "chalk": "5.1.2", - "cosmiconfig": "8.0.0", - "execa": "6.1.0", - "git-url-parse": "13.1.0", - "globby": "13.1.2", - "got": "12.5.3", - "inquirer": "9.1.4", - "is-ci": "3.0.1", - "lodash": "4.17.21", - "mime-types": "2.1.35", - "new-github-release-url": "2.0.0", - "node-fetch": "3.3.0", - "open": "8.4.0", - "ora": "6.1.2", - "os-name": "5.0.1", - "promise.allsettled": "1.0.6", - "proxy-agent": "5.0.0", - "semver": "7.3.8", - "shelljs": "0.8.5", - "update-notifier": "6.0.2", - "url-join": "5.0.0", - "wildcard-match": "5.1.2", - "yargs-parser": "21.1.1" - }, - "dependencies": { - "globby": { - "version": "13.1.2", - "resolved": "https://registry.npmjs.org/globby/-/globby-13.1.2.tgz", - "integrity": "sha512-LKSDZXToac40u8Q1PQtZihbNdTYSNMuWe+K5l+oa6KgDzSvVrHXlJy40hUP522RjAIoNLJYBJi7ow+rbFpIhHQ==", - "dev": true, - "requires": { - "dir-glob": "^3.0.1", - "fast-glob": "^3.2.11", - "ignore": "^5.2.0", - "merge2": "^1.4.1", - "slash": "^4.0.0" - } - }, - "lru-cache": { - "version": "6.0.0", - "resolved": "https://registry.npmjs.org/lru-cache/-/lru-cache-6.0.0.tgz", - "integrity": "sha512-Jo6dJ04CmSjuznwJSS3pUeWmd/H0ffTlkXXgwZi+eq1UCmqQwCh+eLsYOYCwY991i2Fah4h1BEMCx4qThGbsiA==", - "dev": true, - "requires": { - "yallist": "^4.0.0" - } - }, - "semver": { - "version": "7.3.8", - "resolved": "https://registry.npmjs.org/semver/-/semver-7.3.8.tgz", - "integrity": "sha512-NB1ctGL5rlHrPJtFDVIVzTyQylMLu9N9VICA6HSFJo8MCGVTMW6gfpicwKmmK/dAjTOrqu5l63JJOpDSrAis3A==", - "dev": true, - "requires": { - "lru-cache": "^6.0.0" - } - }, - "slash": { - "version": "4.0.0", - "resolved": "https://registry.npmjs.org/slash/-/slash-4.0.0.tgz", - "integrity": "sha512-3dOsAHXXUkQTpOYcoAxLIorMTp4gIQr5IW3iVb7A7lFIp0VHhnynm9izx6TssdrIcVIESAlVjtnO2K8bg+Coew==", - "dev": true - }, - "yallist": { - "version": "4.0.0", - "resolved": "https://registry.npmjs.org/yallist/-/yallist-4.0.0.tgz", - "integrity": "sha512-3wdGidZyq5PB084XLES5TpOSRA3wjXAlIWMhum2kRcv/41Sn2emQ0dycQW4uZXLejwKvg6EsvbdlVL+FYEct7A==", - "dev": true - } - } - }, - "release-zalgo": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/release-zalgo/-/release-zalgo-1.0.0.tgz", - "integrity": "sha512-gUAyHVHPPC5wdqX/LG4LWtRYtgjxyX78oanFNTMMyFEfOqdC54s3eE82imuWKbOeqYht2CrNf64Qb8vgmmtZGA==", - "dev": true, - "requires": { - "es6-error": "^4.0.1" - } - }, - "require-directory": { - "version": "2.1.1", - "resolved": "https://registry.npmjs.org/require-directory/-/require-directory-2.1.1.tgz", - "integrity": "sha512-fGxEI7+wsG9xrvdjsrlmL22OMTTiHRwAMroiEeMgq8gzoLC/PQr7RsRDSTLUg/bZAZtF+TVIkHc6/4RIKrui+Q==", - "dev": true - }, - "require-main-filename": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/require-main-filename/-/require-main-filename-2.0.0.tgz", - "integrity": "sha512-NKN5kMDylKuldxYLSUfrbo5Tuzh4hd+2E8NPPX02mZtn1VuREQToYe/ZdlJy+J3uCpfaiGF05e7B8W0iXbQHmg==", - "dev": true - }, - "resolve": { - "version": "1.22.1", - "resolved": "https://registry.npmjs.org/resolve/-/resolve-1.22.1.tgz", - "integrity": "sha512-nBpuuYuY5jFsli/JIs1oldw6fOQCBioohqWZg/2hiaOybXOft4lonv85uDOKXdf8rhyK159cxU5cDcK/NKk8zw==", - "dev": true, - "requires": { - "is-core-module": "^2.9.0", - "path-parse": "^1.0.7", - "supports-preserve-symlinks-flag": "^1.0.0" - } - }, - "resolve-alpn": { - "version": "1.2.1", - "resolved": "https://registry.npmjs.org/resolve-alpn/-/resolve-alpn-1.2.1.tgz", - "integrity": "sha512-0a1F4l73/ZFZOakJnQ3FvkJ2+gSTQWz/r2KE5OdDY0TxPm5h4GkqkWWfM47T7HsbnOtcJVEF4epCVy6u7Q3K+g==", - "dev": true - }, - "resolve-from": { - "version": "4.0.0", - "resolved": "https://registry.npmjs.org/resolve-from/-/resolve-from-4.0.0.tgz", - "integrity": "sha512-pb/MYmXstAkysRFx8piNI1tGFNQIFA3vkE3Gq4EuA1dF6gHp/+vgZqsCGJapvy8N3Q+4o7FwvquPJcnZ7RYy4g==", - "dev": true - }, - "responselike": { - "version": "3.0.0", - "resolved": "https://registry.npmjs.org/responselike/-/responselike-3.0.0.tgz", - "integrity": "sha512-40yHxbNcl2+rzXvZuVkrYohathsSJlMTXKryG5y8uciHv1+xDLHQpgjG64JUO9nrEq2jGLH6IZ8BcZyw3wrweg==", - "dev": true, - "requires": { - "lowercase-keys": "^3.0.0" - } - }, - "restore-cursor": { - "version": "4.0.0", - "resolved": "https://registry.npmjs.org/restore-cursor/-/restore-cursor-4.0.0.tgz", - "integrity": "sha512-I9fPXU9geO9bHOt9pHHOhOkYerIMsmVaWB0rA2AI9ERh/+x/i7MV5HKBNrg+ljO5eoPVgCcnFuRjJ9uH6I/3eg==", - "dev": true, - "requires": { - "onetime": "^5.1.0", - "signal-exit": "^3.0.2" - }, - "dependencies": { - "mimic-fn": { - "version": "2.1.0", - "resolved": "https://registry.npmjs.org/mimic-fn/-/mimic-fn-2.1.0.tgz", - "integrity": "sha512-OqbOk5oEQeAZ8WXWydlu9HJjz9WVdEIvamMCcXmuqUYjTknH/sqsWvhQ3vgwKFRR1HpjvNBKQ37nbJgYzGqGcg==", - "dev": true - }, - "onetime": { - "version": "5.1.2", - "resolved": "https://registry.npmjs.org/onetime/-/onetime-5.1.2.tgz", - "integrity": "sha512-kbpaSSGJTWdAY5KPVeMOKXSrPtr8C8C7wodJbcsd51jRnmD+GZu8Y0VoU6Dm5Z4vWr0Ig/1NKuWRKf7j5aaYSg==", - "dev": true, - "requires": { - "mimic-fn": "^2.1.0" - } - } - } - }, - "retry": { - "version": "0.13.1", - "resolved": "https://registry.npmjs.org/retry/-/retry-0.13.1.tgz", - "integrity": "sha512-XQBQ3I8W1Cge0Seh+6gjj03LbmRFWuoszgK9ooCpwYIrhhoO80pfq4cUkU5DkknwfOfFteRwlZ56PYOGYyFWdg==", - "dev": true - }, - "reusify": { - "version": "1.0.4", - "resolved": "https://registry.npmjs.org/reusify/-/reusify-1.0.4.tgz", - "integrity": "sha512-U9nH88a3fc/ekCF1l0/UP1IosiuIjyTh7hBvXVMHYgVcfGvt897Xguj2UOLDeI5BG2m7/uwyaLVT6fbtCwTyzw==", - "dev": true - }, - "rimraf": { - "version": "3.0.2", - "resolved": "https://registry.npmjs.org/rimraf/-/rimraf-3.0.2.tgz", - "integrity": "sha512-JZkJMZkAGFFPP2YqXZXPbMlMBgsxzE8ILs4lMIX/2o0L9UBw9O/Y3o6wFw/i9YLapcUJWwqbi3kdxIPdC62TIA==", - "dev": true, - "requires": { - "glob": "^7.1.3" - } - }, - "run-async": { - "version": "2.4.1", - "resolved": "https://registry.npmjs.org/run-async/-/run-async-2.4.1.tgz", - "integrity": "sha512-tvVnVv01b8c1RrA6Ep7JkStj85Guv/YrMcwqYQnwjsAS2cTmmPGBBjAjpCW7RrSodNSoE2/qg9O4bceNvUuDgQ==", - "dev": true - }, - "run-parallel": { - "version": "1.2.0", - "resolved": "https://registry.npmjs.org/run-parallel/-/run-parallel-1.2.0.tgz", - "integrity": "sha512-5l4VyZR86LZ/lDxZTR6jqL8AFE2S0IFLMP26AbjsLVADxHdhB/c0GUsH+y39UfCi3dzz8OlQuPmnaJOMoDHQBA==", - "dev": true, - "requires": { - "queue-microtask": "^1.2.2" - } - }, - "rxjs": { - "version": "7.8.0", - "resolved": "https://registry.npmjs.org/rxjs/-/rxjs-7.8.0.tgz", - "integrity": "sha512-F2+gxDshqmIub1KdvZkaEfGDwLNpPvk9Fs6LD/MyQxNgMds/WH9OdDDXOmxUZpME+iSK3rQCctkL0DYyytUqMg==", - "dev": true, - "requires": { - "tslib": "^2.1.0" - } - }, - "safe-buffer": { - "version": "5.2.1", - "resolved": "https://registry.npmjs.org/safe-buffer/-/safe-buffer-5.2.1.tgz", - "integrity": "sha512-rp3So07KcdmmKbGvgaNxQSJr7bGVSVk5S9Eq1F+ppbRo70+YeaDxkw5Dd8NPN+GD6bjnYm2VuPuCXmpuYvmCXQ==", - "dev": true - }, - "safe-regex-test": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/safe-regex-test/-/safe-regex-test-1.0.0.tgz", - "integrity": "sha512-JBUUzyOgEwXQY1NuPtvcj/qcBDbDmEvWufhlnXZIm75DEHp+afM1r1ujJpJsV/gSM4t59tpDyPi1sd6ZaPFfsA==", - "dev": true, - "requires": { - "call-bind": "^1.0.2", - "get-intrinsic": "^1.1.3", - "is-regex": "^1.1.4" - } - }, - "safer-buffer": { - "version": "2.1.2", - "resolved": "https://registry.npmjs.org/safer-buffer/-/safer-buffer-2.1.2.tgz", - "integrity": "sha512-YZo3K82SD7Riyi0E1EQPojLz7kpepnSQI9IyPbHHg1XXXevb5dJI7tpyN2ADxGcQbHG7vcyRHk0cbwqcQriUtg==", - "dev": true - }, - "semver": { - "version": "6.3.0", - "resolved": "https://registry.npmjs.org/semver/-/semver-6.3.0.tgz", - "integrity": "sha512-b39TBaTSfV6yBrapU89p5fKekE2m/NwnDocOVruQFS1/veMgdzuPcnOM34M6CwxW8jH/lxEa5rBoDeUwu5HHTw==", - "dev": true - }, - "semver-diff": { - "version": "4.0.0", - "resolved": "https://registry.npmjs.org/semver-diff/-/semver-diff-4.0.0.tgz", - "integrity": "sha512-0Ju4+6A8iOnpL/Thra7dZsSlOHYAHIeMxfhWQRI1/VLcT3WDBZKKtQt/QkBOsiIN9ZpuvHE6cGZ0x4glCMmfiA==", - "dev": true, - "requires": { - "semver": "^7.3.5" - }, - "dependencies": { - "lru-cache": { - "version": "6.0.0", - "resolved": "https://registry.npmjs.org/lru-cache/-/lru-cache-6.0.0.tgz", - "integrity": "sha512-Jo6dJ04CmSjuznwJSS3pUeWmd/H0ffTlkXXgwZi+eq1UCmqQwCh+eLsYOYCwY991i2Fah4h1BEMCx4qThGbsiA==", - "dev": true, - "requires": { - "yallist": "^4.0.0" - } - }, - "semver": { - "version": "7.3.8", - "resolved": "https://registry.npmjs.org/semver/-/semver-7.3.8.tgz", - "integrity": "sha512-NB1ctGL5rlHrPJtFDVIVzTyQylMLu9N9VICA6HSFJo8MCGVTMW6gfpicwKmmK/dAjTOrqu5l63JJOpDSrAis3A==", - "dev": true, - "requires": { - "lru-cache": "^6.0.0" - } - }, - "yallist": { - "version": "4.0.0", - "resolved": "https://registry.npmjs.org/yallist/-/yallist-4.0.0.tgz", - "integrity": "sha512-3wdGidZyq5PB084XLES5TpOSRA3wjXAlIWMhum2kRcv/41Sn2emQ0dycQW4uZXLejwKvg6EsvbdlVL+FYEct7A==", - "dev": true - } - } - }, - "serialize-javascript": { - "version": "6.0.0", - "resolved": "https://registry.npmjs.org/serialize-javascript/-/serialize-javascript-6.0.0.tgz", - "integrity": "sha512-Qr3TosvguFt8ePWqsvRfrKyQXIiW+nGbYpy8XK24NQHE83caxWt+mIymTT19DGFbNWNLfEwsrkSmN64lVWB9ag==", - "dev": true, - "requires": { - "randombytes": "^2.1.0" - } - }, - "set-blocking": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/set-blocking/-/set-blocking-2.0.0.tgz", - "integrity": "sha512-KiKBS8AnWGEyLzofFfmvKwpdPzqiy16LvQfK3yv/fVH7Bj13/wl3JSR1J+rfgRE9q7xUJK4qvgS8raSOeLUehw==", - "dev": true - }, - "setprototypeof": { - "version": "1.2.0", - "resolved": "https://registry.npmjs.org/setprototypeof/-/setprototypeof-1.2.0.tgz", - "integrity": "sha512-E5LDX7Wrp85Kil5bhZv46j8jOeboKq5JMmYM3gVGdGH8xFpPWXUMsNrlODCrkoxMEeNi/XZIwuRvY4XNwYMJpw==", - "dev": true - }, - "shebang-command": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/shebang-command/-/shebang-command-2.0.0.tgz", - "integrity": "sha512-kHxr2zZpYtdmrN1qDjrrX/Z1rR1kG8Dx+gkpK1G4eXmvXswmcE1hTWBWYUzlraYw1/yZp6YuDY77YtvbN0dmDA==", - "dev": true, - "requires": { - "shebang-regex": "^3.0.0" - } - }, - "shebang-regex": { - "version": "3.0.0", - "resolved": "https://registry.npmjs.org/shebang-regex/-/shebang-regex-3.0.0.tgz", - "integrity": "sha512-7++dFhtcx3353uBaq8DDR4NuxBetBzC7ZQOhmTQInHEd6bSrXdiEyzCvG07Z44UYdLShWUyXt5M/yhz8ekcb1A==", - "dev": true - }, - "shelljs": { - "version": "0.8.5", - "resolved": "https://registry.npmjs.org/shelljs/-/shelljs-0.8.5.tgz", - "integrity": "sha512-TiwcRcrkhHvbrZbnRcFYMLl30Dfov3HKqzp5tO5b4pt6G/SezKcYhmDg15zXVBswHmctSAQKznqNW2LO5tTDow==", - "dev": true, - "requires": { - "glob": "^7.0.0", - "interpret": "^1.0.0", - "rechoir": "^0.6.2" - } - }, - "shiki": { - "version": "0.12.1", - "resolved": "https://registry.npmjs.org/shiki/-/shiki-0.12.1.tgz", - "integrity": "sha512-aieaV1m349rZINEBkjxh2QbBvFFQOlgqYTNtCal82hHj4dDZ76oMlQIX+C7ryerBTDiga3e5NfH6smjdJ02BbQ==", - "dev": true, - "requires": { - "jsonc-parser": "^3.2.0", - "vscode-oniguruma": "^1.7.0", - "vscode-textmate": "^8.0.0" - } - }, - "side-channel": { - "version": "1.0.4", - "resolved": "https://registry.npmjs.org/side-channel/-/side-channel-1.0.4.tgz", - "integrity": "sha512-q5XPytqFEIKHkGdiMIrY10mvLRvnQh42/+GoBlFW3b2LXLE2xxJpZFdm94we0BaoV3RwJyGqg5wS7epxTv0Zvw==", - "dev": true, - "requires": { - "call-bind": "^1.0.0", - "get-intrinsic": "^1.0.2", - "object-inspect": "^1.9.0" - } - }, - "signal-exit": { - "version": "3.0.7", - "resolved": "https://registry.npmjs.org/signal-exit/-/signal-exit-3.0.7.tgz", - "integrity": "sha512-wnD2ZE+l+SPC/uoS0vXeE9L1+0wuaMqKlfz9AMUo38JsyLSBWSFcHR1Rri62LZc12vLr1gb3jl7iwQhgwpAbGQ==", - "dev": true - }, - "sinon": { - "version": "15.0.1", - "resolved": "https://registry.npmjs.org/sinon/-/sinon-15.0.1.tgz", - "integrity": "sha512-PZXKc08f/wcA/BMRGBze2Wmw50CWPiAH3E21EOi4B49vJ616vW4DQh4fQrqsYox2aNR/N3kCqLuB0PwwOucQrg==", - "dev": true, - "requires": { - "@sinonjs/commons": "^2.0.0", - "@sinonjs/fake-timers": "10.0.2", - "@sinonjs/samsam": "^7.0.1", - "diff": "^5.0.0", - "nise": "^5.1.2", - "supports-color": "^7.2.0" - } - }, - "slash": { - "version": "3.0.0", - "resolved": "https://registry.npmjs.org/slash/-/slash-3.0.0.tgz", - "integrity": "sha512-g9Q1haeby36OSStwb4ntCGGGaKsaVSjQ68fBxoQcutl5fS1vuY18H3wSt3jFyFtrkx+Kz0V1G85A4MyAdDMi2Q==", - "dev": true - }, - "smart-buffer": { - "version": "4.2.0", - "resolved": "https://registry.npmjs.org/smart-buffer/-/smart-buffer-4.2.0.tgz", - "integrity": "sha512-94hK0Hh8rPqQl2xXc3HsaBoOXKV20MToPkcXvwbISWLEs+64sBq5kFgn2kJDHb1Pry9yrP0dxrCI9RRci7RXKg==", - "dev": true - }, - "socks": { - "version": "2.7.1", - "resolved": "https://registry.npmjs.org/socks/-/socks-2.7.1.tgz", - "integrity": "sha512-7maUZy1N7uo6+WVEX6psASxtNlKaNVMlGQKkG/63nEDdLOWNbiUMoLK7X4uYoLhQstau72mLgfEWcXcwsaHbYQ==", - "dev": true, - "requires": { - "ip": "^2.0.0", - "smart-buffer": "^4.2.0" - }, - "dependencies": { - "ip": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/ip/-/ip-2.0.0.tgz", - "integrity": "sha512-WKa+XuLG1A1R0UWhl2+1XQSi+fZWMsYKffMZTTYsiZaUD8k2yDAj5atimTUD2TZkyCkNEeYE5NhFZmupOGtjYQ==", - "dev": true - } - } - }, - "socks-proxy-agent": { - "version": "5.0.1", - "resolved": "https://registry.npmjs.org/socks-proxy-agent/-/socks-proxy-agent-5.0.1.tgz", - "integrity": "sha512-vZdmnjb9a2Tz6WEQVIurybSwElwPxMZaIc7PzqbJTrezcKNznv6giT7J7tZDZ1BojVaa1jvO/UiUdhDVB0ACoQ==", - "dev": true, - "requires": { - "agent-base": "^6.0.2", - "debug": "4", - "socks": "^2.3.3" - } - }, - "source-map": { - "version": "0.6.1", - "resolved": "https://registry.npmjs.org/source-map/-/source-map-0.6.1.tgz", - "integrity": "sha512-UjgapumWlbMhkBgzT7Ykc5YXUT46F0iKu8SGXq0bcwP5dz/h0Plj6enJqjz1Zbq2l5WaqYnrVbwWOWMyF3F47g==", - "dev": true - }, - "source-map-support": { - "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", - "source-map": "^0.6.0" - } - }, - "spawn-wrap": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/spawn-wrap/-/spawn-wrap-2.0.0.tgz", - "integrity": "sha512-EeajNjfN9zMnULLwhZZQU3GWBoFNkbngTUPfaawT4RkMiviTxcX0qfhVbGey39mfctfDHkWtuecgQ8NJcyQWHg==", - "dev": true, - "requires": { - "foreground-child": "^2.0.0", - "is-windows": "^1.0.2", - "make-dir": "^3.0.0", - "rimraf": "^3.0.0", - "signal-exit": "^3.0.2", - "which": "^2.0.1" - } - }, - "sprintf-js": { - "version": "1.0.3", - "resolved": "https://registry.npmjs.org/sprintf-js/-/sprintf-js-1.0.3.tgz", - "integrity": "sha512-D9cPgkvLlV3t3IzL0D0YLvGA9Ahk4PcvVwUbN0dSGr1aP0Nrt4AEnTUbuGvquEC0mA64Gqt1fzirlRs5ibXx8g==", - "dev": true - }, - "statuses": { - "version": "2.0.1", - "resolved": "https://registry.npmjs.org/statuses/-/statuses-2.0.1.tgz", - "integrity": "sha512-RwNA9Z/7PrK06rYLIzFMlaF+l73iwpzsqRIFgbMLbTcLD6cOao82TaWefPXQvB2fOC4AjuYSEndS7N/mTCbkdQ==", - "dev": true - }, - "stop-iteration-iterator": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/stop-iteration-iterator/-/stop-iteration-iterator-1.0.0.tgz", - "integrity": "sha512-iCGQj+0l0HOdZ2AEeBADlsRC+vsnDsZsbdSiH1yNSjcfKM7fdpCMfqAL/dwF5BLiw/XhRft/Wax6zQbhq2BcjQ==", - "dev": true, - "requires": { - "internal-slot": "^1.0.4" - } - }, - "string_decoder": { - "version": "1.3.0", - "resolved": "https://registry.npmjs.org/string_decoder/-/string_decoder-1.3.0.tgz", - "integrity": "sha512-hkRX8U1WjJFd8LsDJ2yQ/wWWxaopEsABU1XfkM8A+j0+85JAGppt16cr1Whg6KIbb4okU6Mql6BOj+uup/wKeA==", - "dev": true, - "requires": { - "safe-buffer": "~5.2.0" - } - }, - "string-width": { - "version": "5.1.2", - "resolved": "https://registry.npmjs.org/string-width/-/string-width-5.1.2.tgz", - "integrity": "sha512-HnLOCR3vjcY8beoNLtcjZ5/nxn2afmME6lhrDrebokqMap+XbeW8n9TXpPDOqdGK5qcI3oT0GKTW6wC7EMiVqA==", - "dev": true, - "requires": { - "eastasianwidth": "^0.2.0", - "emoji-regex": "^9.2.2", - "strip-ansi": "^7.0.1" - } - }, - "string.prototype.trimend": { - "version": "1.0.6", - "resolved": "https://registry.npmjs.org/string.prototype.trimend/-/string.prototype.trimend-1.0.6.tgz", - "integrity": "sha512-JySq+4mrPf9EsDBEDYMOb/lM7XQLulwg5R/m1r0PXEFqrV0qHvl58sdTilSXtKOflCsK2E8jxf+GKC0T07RWwQ==", - "dev": true, - "requires": { - "call-bind": "^1.0.2", - "define-properties": "^1.1.4", - "es-abstract": "^1.20.4" - } - }, - "string.prototype.trimstart": { - "version": "1.0.6", - "resolved": "https://registry.npmjs.org/string.prototype.trimstart/-/string.prototype.trimstart-1.0.6.tgz", - "integrity": "sha512-omqjMDaY92pbn5HOX7f9IccLA+U1tA9GvtU4JrodiXFfYB7jPzzHpRzpglLAjtUV6bB557zwClJezTqnAiYnQA==", - "dev": true, - "requires": { - "call-bind": "^1.0.2", - "define-properties": "^1.1.4", - "es-abstract": "^1.20.4" - } - }, - "strip-ansi": { - "version": "7.0.1", - "resolved": "https://registry.npmjs.org/strip-ansi/-/strip-ansi-7.0.1.tgz", - "integrity": "sha512-cXNxvT8dFNRVfhVME3JAe98mkXDYN2O1l7jmcwMnOslDeESg1rF/OZMtK0nRAhiari1unG5cD4jG3rapUAkLbw==", - "dev": true, - "requires": { - "ansi-regex": "^6.0.1" - } - }, - "strip-bom": { - "version": "4.0.0", - "resolved": "https://registry.npmjs.org/strip-bom/-/strip-bom-4.0.0.tgz", - "integrity": "sha512-3xurFv5tEgii33Zi8Jtp55wEIILR9eh34FAW00PZf+JnSsTmV/ioewSgQl97JHvgjoRGwPShsWm+IdrxB35d0w==", - "dev": true - }, - "strip-final-newline": { - "version": "3.0.0", - "resolved": "https://registry.npmjs.org/strip-final-newline/-/strip-final-newline-3.0.0.tgz", - "integrity": "sha512-dOESqjYr96iWYylGObzd39EuNTa5VJxyvVAEm5Jnh7KGo75V43Hk1odPQkNDyXNmUR6k+gEiDVXnjB8HJ3crXw==", - "dev": true - }, - "strip-json-comments": { - "version": "3.1.1", - "resolved": "https://registry.npmjs.org/strip-json-comments/-/strip-json-comments-3.1.1.tgz", - "integrity": "sha512-6fPc+R4ihwqP6N/aIv2f1gMH8lOVtWQHoqC4yK6oSDVVocumAsfCqjkXnqiYMhmMwS/mEHLp7Vehlt3ql6lEig==", - "dev": true - }, - "strip-outer": { - "version": "1.0.1", - "resolved": "https://registry.npmjs.org/strip-outer/-/strip-outer-1.0.1.tgz", - "integrity": "sha512-k55yxKHwaXnpYGsOzg4Vl8+tDrWylxDEpknGjhTiZB8dFRU5rTo9CAzeycivxV3s+zlTKwrs6WxMxR95n26kwg==", - "dev": true, - "requires": { - "escape-string-regexp": "^1.0.2" - }, - "dependencies": { - "escape-string-regexp": { - "version": "1.0.5", - "resolved": "https://registry.npmjs.org/escape-string-regexp/-/escape-string-regexp-1.0.5.tgz", - "integrity": "sha512-vbRorB5FUQWvla16U8R/qgaFIya2qGzwDrNmCZuYKrbdSUMG6I1ZCGQRefkRVhuOkIGVne7BQ35DSfo1qvJqFg==", - "dev": true - } - } - }, - "supports-color": { - "version": "7.2.0", - "resolved": "https://registry.npmjs.org/supports-color/-/supports-color-7.2.0.tgz", - "integrity": "sha512-qpCAvRl9stuOHveKsn7HncJRvv501qIacKzQlO/+Lwxc9+0q2wLyv4Dfvt80/DPn2pqOBsJdDiogXGR9+OvwRw==", - "dev": true, - "requires": { - "has-flag": "^4.0.0" - } - }, - "supports-preserve-symlinks-flag": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/supports-preserve-symlinks-flag/-/supports-preserve-symlinks-flag-1.0.0.tgz", - "integrity": "sha512-ot0WnXS9fgdkgIcePe6RHNk1WA8+muPa6cSjeR3V8K27q9BB1rTE3R1p7Hv0z1ZyAc8s6Vvv8DIyWf681MAt0w==", - "dev": true - }, - "test-exclude": { - "version": "6.0.0", - "resolved": "https://registry.npmjs.org/test-exclude/-/test-exclude-6.0.0.tgz", - "integrity": "sha512-cAGWPIyOHU6zlmg88jwm7VRyXnMN7iV68OGAbYDk/Mh/xC/pzVPlQtY6ngoIH/5/tciuhGfvESU8GrHrcxD56w==", - "dev": true, - "requires": { - "@istanbuljs/schema": "^0.1.2", - "glob": "^7.1.4", - "minimatch": "^3.0.4" - } - }, - "text-table": { - "version": "0.2.0", - "resolved": "https://registry.npmjs.org/text-table/-/text-table-0.2.0.tgz", - "integrity": "sha512-N+8UisAXDGk8PFXP4HAzVR9nbfmVJ3zYLAWiTIoqC5v5isinhr+r5uaO8+7r3BMfuNIufIsA7RdpVgacC2cSpw==", - "dev": true - }, - "through": { - "version": "2.3.8", - "resolved": "https://registry.npmjs.org/through/-/through-2.3.8.tgz", - "integrity": "sha512-w89qg7PI8wAdvX60bMDP+bFoD5Dvhm9oLheFp5O4a2QF0cSBGsBX4qZmadPMvVqlLJBBci+WqGGOAPvcDeNSVg==", - "dev": true - }, - "tmp": { - "version": "0.0.33", - "resolved": "https://registry.npmjs.org/tmp/-/tmp-0.0.33.tgz", - "integrity": "sha512-jRCJlojKnZ3addtTOjdIqoRuPEKBvNXcGYqzO6zWZX8KfKEpnGY5jfggJQ3EjKuu8D4bJRr0y+cYJFmYbImXGw==", - "dev": true, - "requires": { - "os-tmpdir": "~1.0.2" - } - }, - "to-fast-properties": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/to-fast-properties/-/to-fast-properties-2.0.0.tgz", - "integrity": "sha512-/OaKK0xYrs3DmxRYqL/yDc+FxFUVYhDlXMhRmv3z915w2HF1tnN1omB354j8VUGO/hbRzyD6Y3sA7v7GS/ceog==", - "dev": true - }, - "to-regex-range": { - "version": "5.0.1", - "resolved": "https://registry.npmjs.org/to-regex-range/-/to-regex-range-5.0.1.tgz", - "integrity": "sha512-65P7iz6X5yEr1cwcgvQxbbIw7Uk3gOy5dIdtZ4rDveLqhrdJP+Li/Hx6tyK0NEb+2GCyneCMJiGqrADCSNk8sQ==", - "dev": true, - "requires": { - "is-number": "^7.0.0" - } - }, - "toidentifier": { - "version": "1.0.1", - "resolved": "https://registry.npmjs.org/toidentifier/-/toidentifier-1.0.1.tgz", - "integrity": "sha512-o5sSPKEkg/DIQNmH43V0/uerLrpzVedkUh8tGNvaeXpfpuwjKenlSox/2O/BTlZUtEe+JG7s5YhEz608PlAHRA==", - "dev": true - }, - "tr46": { - "version": "0.0.3", - "resolved": "https://registry.npmjs.org/tr46/-/tr46-0.0.3.tgz", - "integrity": "sha512-N3WMsuqV66lT30CrXNbEjx4GEwlow3v6rr4mCcv6prnfwhS01rkgyFdjPNBYd9br7LpXV1+Emh01fHnq2Gdgrw==", - "dev": true - }, - "trim-repeated": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/trim-repeated/-/trim-repeated-1.0.0.tgz", - "integrity": "sha512-pkonvlKk8/ZuR0D5tLW8ljt5I8kmxp2XKymhepUeOdCEfKpZaktSArkLHZt76OB1ZvO9bssUsDty4SWhLvZpLg==", - "dev": true, - "requires": { - "escape-string-regexp": "^1.0.2" - }, - "dependencies": { - "escape-string-regexp": { - "version": "1.0.5", - "resolved": "https://registry.npmjs.org/escape-string-regexp/-/escape-string-regexp-1.0.5.tgz", - "integrity": "sha512-vbRorB5FUQWvla16U8R/qgaFIya2qGzwDrNmCZuYKrbdSUMG6I1ZCGQRefkRVhuOkIGVne7BQ35DSfo1qvJqFg==", - "dev": true - } - } - }, - "ts-node": { - "version": "10.9.1", - "resolved": "https://registry.npmjs.org/ts-node/-/ts-node-10.9.1.tgz", - "integrity": "sha512-NtVysVPkxxrwFGUUxGYhfux8k78pQB3JqYBXlLRZgdGUqTO5wU/UyHop5p70iEbGhB7q5KmiZiU0Y3KlJrScEw==", - "dev": true, - "requires": { - "@cspotcode/source-map-support": "^0.8.0", - "@tsconfig/node10": "^1.0.7", - "@tsconfig/node12": "^1.0.7", - "@tsconfig/node14": "^1.0.0", - "@tsconfig/node16": "^1.0.2", - "acorn": "^8.4.1", - "acorn-walk": "^8.1.1", - "arg": "^4.1.0", - "create-require": "^1.1.0", - "diff": "^4.0.1", - "make-error": "^1.1.1", - "v8-compile-cache-lib": "^3.0.1", - "yn": "3.1.1" - }, - "dependencies": { - "diff": { - "version": "4.0.2", - "resolved": "https://registry.npmjs.org/diff/-/diff-4.0.2.tgz", - "integrity": "sha512-58lmxKSA4BNyLz+HHMUzlOEpg09FV+ev6ZMe3vJihgdxzgcwZ8VoEEPmALCZG9LmqfVoNMMKpttIYTVG6uDY7A==", - "dev": true - } - } - }, - "tslib": { - "version": "2.4.1", - "resolved": "https://registry.npmjs.org/tslib/-/tslib-2.4.1.tgz", - "integrity": "sha512-tGyy4dAjRIEwI7BzsB0lynWgOpfqjUdq91XXAlIWD2OwKBH7oCl/GZG/HT4BOHrTlPMOASlMQ7veyTqpmRcrNA==", - "dev": true - }, - "tsutils": { - "version": "3.21.0", - "resolved": "https://registry.npmjs.org/tsutils/-/tsutils-3.21.0.tgz", - "integrity": "sha512-mHKK3iUXL+3UF6xL5k0PEhKRUBKPBCv/+RkEOpjRWxxx27KKRBmmA60A9pgOUvMi8GKhRMPEmjBRPzs2W7O1OA==", - "dev": true, - "requires": { - "tslib": "^1.8.1" - }, - "dependencies": { - "tslib": { - "version": "1.14.1", - "resolved": "https://registry.npmjs.org/tslib/-/tslib-1.14.1.tgz", - "integrity": "sha512-Xni35NKzjgMrwevysHTCArtLDpPvye8zV/0E4EyYn43P7/7qvQwPh9BGkHewbMulVntbigmcT7rdX3BNo9wRJg==", - "dev": true - } - } - }, - "type-check": { - "version": "0.4.0", - "resolved": "https://registry.npmjs.org/type-check/-/type-check-0.4.0.tgz", - "integrity": "sha512-XleUoc9uwGXqjWwXaUTZAmzMcFZ5858QA2vvx1Ur5xIcixXIP+8LnFDgRplU30us6teqdlskFfu+ae4K79Ooew==", - "dev": true, - "requires": { - "prelude-ls": "^1.2.1" - } - }, - "type-detect": { - "version": "4.0.8", - "resolved": "https://registry.npmjs.org/type-detect/-/type-detect-4.0.8.tgz", - "integrity": "sha512-0fr/mIH1dlO+x7TlcMy+bIDqKPsw/70tVyeHW787goQjhmqaZe10uwLujubK9q9Lg6Fiho1KUKDYz0Z7k7g5/g==", - "dev": true - }, - "type-fest": { - "version": "3.5.2", - "resolved": "https://registry.npmjs.org/type-fest/-/type-fest-3.5.2.tgz", - "integrity": "sha512-Ph7S4EhXzWy0sbljEuZo0tTNoLl+K2tPauGrQpcwUWrOVneLePTuhVzcuzVJJ6RU5DsNwQZka+8YtkXXU4z9cA==", - "dev": true - }, - "typed-array-length": { - "version": "1.0.4", - "resolved": "https://registry.npmjs.org/typed-array-length/-/typed-array-length-1.0.4.tgz", - "integrity": "sha512-KjZypGq+I/H7HI5HlOoGHkWUUGq+Q0TPhQurLbyrVrvnKTBgzLhIJ7j6J/XTQOi0d1RjyZ0wdas8bKs2p0x3Ng==", - "dev": true, - "requires": { - "call-bind": "^1.0.2", - "for-each": "^0.3.3", - "is-typed-array": "^1.1.9" - } - }, - "typedarray-to-buffer": { - "version": "3.1.5", - "resolved": "https://registry.npmjs.org/typedarray-to-buffer/-/typedarray-to-buffer-3.1.5.tgz", - "integrity": "sha512-zdu8XMNEDepKKR+XYOXAVPtWui0ly0NtohUscw+UmaHiAWT8hrV1rr//H6V+0DvJ3OQ19S979M0laLfX8rm82Q==", - "dev": true, - "requires": { - "is-typedarray": "^1.0.0" - } - }, - "typedoc": { - "version": "0.23.24", - "resolved": "https://registry.npmjs.org/typedoc/-/typedoc-0.23.24.tgz", - "integrity": "sha512-bfmy8lNQh+WrPYcJbtjQ6JEEsVl/ce1ZIXyXhyW+a1vFrjO39t6J8sL/d6FfAGrJTc7McCXgk9AanYBSNvLdIA==", - "dev": true, - "requires": { - "lunr": "^2.3.9", - "marked": "^4.2.5", - "minimatch": "^5.1.2", - "shiki": "^0.12.1" - }, - "dependencies": { - "brace-expansion": { - "version": "2.0.1", - "resolved": "https://registry.npmjs.org/brace-expansion/-/brace-expansion-2.0.1.tgz", - "integrity": "sha512-XnAIvQ8eM+kC6aULx6wuQiwVsnzsi9d3WxzV3FpWTGA19F621kwdbsAcFKXgKUHZWsy+mY6iL1sHTxWEFCytDA==", - "dev": true, - "requires": { - "balanced-match": "^1.0.0" - } - }, - "minimatch": { - "version": "5.1.6", - "resolved": "https://registry.npmjs.org/minimatch/-/minimatch-5.1.6.tgz", - "integrity": "sha512-lKwV/1brpG6mBUFHtb7NUmtABCb2WZZmm2wNiOA5hAb8VdCS4B3dtMWyvcoViccwAW/COERjXLt0zP1zXUN26g==", - "dev": true, - "requires": { - "brace-expansion": "^2.0.1" - } - } - } - }, - "typescript": { - "version": "4.9.4", - "resolved": "https://registry.npmjs.org/typescript/-/typescript-4.9.4.tgz", - "integrity": "sha512-Uz+dTXYzxXXbsFpM86Wh3dKCxrQqUcVMxwU54orwlJjOpO3ao8L7j5lH+dWfTwgCwIuM9GQ2kvVotzYJMXTBZg==", - "dev": true - }, - "unbox-primitive": { - "version": "1.0.2", - "resolved": "https://registry.npmjs.org/unbox-primitive/-/unbox-primitive-1.0.2.tgz", - "integrity": "sha512-61pPlCD9h51VoreyJ0BReideM3MDKMKnh6+V9L08331ipq6Q8OFXZYiqP6n/tbHx4s5I9uRhcye6BrbkizkBDw==", - "dev": true, - "requires": { - "call-bind": "^1.0.2", - "has-bigints": "^1.0.2", - "has-symbols": "^1.0.3", - "which-boxed-primitive": "^1.0.2" - } - }, - "unique-string": { - "version": "3.0.0", - "resolved": "https://registry.npmjs.org/unique-string/-/unique-string-3.0.0.tgz", - "integrity": "sha512-VGXBUVwxKMBUznyffQweQABPRRW1vHZAbadFZud4pLFAqRGvv/96vafgjWFqzourzr8YonlQiPgH0YCJfawoGQ==", - "dev": true, - "requires": { - "crypto-random-string": "^4.0.0" - } - }, - "universal-user-agent": { - "version": "6.0.0", - "resolved": "https://registry.npmjs.org/universal-user-agent/-/universal-user-agent-6.0.0.tgz", - "integrity": "sha512-isyNax3wXoKaulPDZWHQqbmIx1k2tb9fb3GGDBRxCscfYV2Ch7WxPArBsFEG8s/safwXTT7H4QGhaIkTp9447w==", - "dev": true - }, - "universalify": { - "version": "0.1.2", - "resolved": "https://registry.npmjs.org/universalify/-/universalify-0.1.2.tgz", - "integrity": "sha512-rBJeI5CXAlmy1pV+617WB9J63U6XcazHHF2f2dbJix4XzpUF0RS3Zbj0FGIOCAva5P/d/GBOYaACQ1w+0azUkg==", - "dev": true - }, - "unpipe": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/unpipe/-/unpipe-1.0.0.tgz", - "integrity": "sha512-pjy2bYhSsufwWlKwPc+l3cN7+wuJlK6uz0YdJEOlQDbl6jo/YlPi4mb8agUkVC8BF7V8NuzeyPNqRksA3hztKQ==", - "dev": true - }, - "update-browserslist-db": { - "version": "1.0.10", - "resolved": "https://registry.npmjs.org/update-browserslist-db/-/update-browserslist-db-1.0.10.tgz", - "integrity": "sha512-OztqDenkfFkbSG+tRxBeAnCVPckDBcvibKd35yDONx6OU8N7sqgwc7rCbkJ/WcYtVRZ4ba68d6byhC21GFh7sQ==", - "dev": true, - "requires": { - "escalade": "^3.1.1", - "picocolors": "^1.0.0" - } - }, - "update-notifier": { - "version": "6.0.2", - "resolved": "https://registry.npmjs.org/update-notifier/-/update-notifier-6.0.2.tgz", - "integrity": "sha512-EDxhTEVPZZRLWYcJ4ZXjGFN0oP7qYvbXWzEgRm/Yql4dHX5wDbvh89YHP6PK1lzZJYrMtXUuZZz8XGK+U6U1og==", - "dev": true, - "requires": { - "boxen": "^7.0.0", - "chalk": "^5.0.1", - "configstore": "^6.0.0", - "has-yarn": "^3.0.0", - "import-lazy": "^4.0.0", - "is-ci": "^3.0.1", - "is-installed-globally": "^0.4.0", - "is-npm": "^6.0.0", - "is-yarn-global": "^0.4.0", - "latest-version": "^7.0.0", - "pupa": "^3.1.0", - "semver": "^7.3.7", - "semver-diff": "^4.0.0", - "xdg-basedir": "^5.1.0" - }, - "dependencies": { - "lru-cache": { - "version": "6.0.0", - "resolved": "https://registry.npmjs.org/lru-cache/-/lru-cache-6.0.0.tgz", - "integrity": "sha512-Jo6dJ04CmSjuznwJSS3pUeWmd/H0ffTlkXXgwZi+eq1UCmqQwCh+eLsYOYCwY991i2Fah4h1BEMCx4qThGbsiA==", - "dev": true, - "requires": { - "yallist": "^4.0.0" - } - }, - "semver": { - "version": "7.3.8", - "resolved": "https://registry.npmjs.org/semver/-/semver-7.3.8.tgz", - "integrity": "sha512-NB1ctGL5rlHrPJtFDVIVzTyQylMLu9N9VICA6HSFJo8MCGVTMW6gfpicwKmmK/dAjTOrqu5l63JJOpDSrAis3A==", - "dev": true, - "requires": { - "lru-cache": "^6.0.0" - } - }, - "yallist": { - "version": "4.0.0", - "resolved": "https://registry.npmjs.org/yallist/-/yallist-4.0.0.tgz", - "integrity": "sha512-3wdGidZyq5PB084XLES5TpOSRA3wjXAlIWMhum2kRcv/41Sn2emQ0dycQW4uZXLejwKvg6EsvbdlVL+FYEct7A==", - "dev": true - } - } - }, - "uri-js": { - "version": "4.4.1", - "resolved": "https://registry.npmjs.org/uri-js/-/uri-js-4.4.1.tgz", - "integrity": "sha512-7rKUyy33Q1yc98pQ1DAmLtwX109F7TIfWlW1Ydo8Wl1ii1SeHieeh0HHfPeL2fMXK6z0s8ecKs9frCuLJvndBg==", - "dev": true, - "requires": { - "punycode": "^2.1.0" - } - }, - "url-join": { - "version": "5.0.0", - "resolved": "https://registry.npmjs.org/url-join/-/url-join-5.0.0.tgz", - "integrity": "sha512-n2huDr9h9yzd6exQVnH/jU5mr+Pfx08LRXXZhkLLetAMESRj+anQsTAh940iMrIetKAmry9coFuZQ2jY8/p3WA==", - "dev": true - }, - "util-deprecate": { - "version": "1.0.2", - "resolved": "https://registry.npmjs.org/util-deprecate/-/util-deprecate-1.0.2.tgz", - "integrity": "sha512-EPD5q1uXyFxJpCrLnCc1nHnq3gOa6DZBocAIiI2TaSCA7VCJ1UJDMagCzIkXNsUYfD1daK//LTEQ8xiIbrHtcw==", - "dev": true - }, - "uuid": { - "version": "8.3.2", - "resolved": "https://registry.npmjs.org/uuid/-/uuid-8.3.2.tgz", - "integrity": "sha512-+NYs2QeMWy+GWFOEm9xnn6HCDp0l7QBD7ml8zLUmJ+93Q5NF0NocErnwkTkXVFNiX3/fpC6afS8Dhb/gz7R7eg==", - "dev": true - }, - "v8-compile-cache-lib": { - "version": "3.0.1", - "resolved": "https://registry.npmjs.org/v8-compile-cache-lib/-/v8-compile-cache-lib-3.0.1.tgz", - "integrity": "sha512-wa7YjyUGfNZngI/vtK0UHAN+lgDCxBPCylVXGp0zu59Fz5aiGtNXaq3DhIov063MorB+VfufLh3JlF2KdTK3xg==", - "dev": true - }, - "vm2": { - "version": "3.9.13", - "resolved": "https://registry.npmjs.org/vm2/-/vm2-3.9.13.tgz", - "integrity": "sha512-0rvxpB8P8Shm4wX2EKOiMp7H2zq+HUE/UwodY0pCZXs9IffIKZq6vUti5OgkVCTakKo9e/fgO4X1fkwfjWxE3Q==", - "dev": true, - "requires": { - "acorn": "^8.7.0", - "acorn-walk": "^8.2.0" - } - }, - "vscode-oniguruma": { - "version": "1.7.0", - "resolved": "https://registry.npmjs.org/vscode-oniguruma/-/vscode-oniguruma-1.7.0.tgz", - "integrity": "sha512-L9WMGRfrjOhgHSdOYgCt/yRMsXzLDJSL7BPrOZt73gU0iWO4mpqzqQzOz5srxqTvMBaR0XZTSrVWo4j55Rc6cA==", - "dev": true - }, - "vscode-textmate": { - "version": "8.0.0", - "resolved": "https://registry.npmjs.org/vscode-textmate/-/vscode-textmate-8.0.0.tgz", - "integrity": "sha512-AFbieoL7a5LMqcnOF04ji+rpXadgOXnZsxQr//r83kLPr7biP7am3g9zbaZIaBGwBRWeSvoMD4mgPdX3e4NWBg==", - "dev": true - }, - "wcwidth": { - "version": "1.0.1", - "resolved": "https://registry.npmjs.org/wcwidth/-/wcwidth-1.0.1.tgz", - "integrity": "sha512-XHPEwS0q6TaxcvG85+8EYkbiCux2XtWG2mkc47Ng2A77BQu9+DqIOJldST4HgPkuea7dvKSj5VgX3P1d4rW8Tg==", - "dev": true, - "requires": { - "defaults": "^1.0.3" - } - }, - "web-streams-polyfill": { - "version": "3.2.1", - "resolved": "https://registry.npmjs.org/web-streams-polyfill/-/web-streams-polyfill-3.2.1.tgz", - "integrity": "sha512-e0MO3wdXWKrLbL0DgGnUV7WHVuw9OUvL4hjgnPkIeEvESk74gAITi5G606JtZPp39cd8HA9VQzCIvA49LpPN5Q==", - "dev": true - }, - "webidl-conversions": { - "version": "3.0.1", - "resolved": "https://registry.npmjs.org/webidl-conversions/-/webidl-conversions-3.0.1.tgz", - "integrity": "sha512-2JAn3z8AR6rjK8Sm8orRC0h/bcl/DqL7tRPdGZ4I1CjdF+EaMLmYxBHyXuKL849eucPFhvBoxMsflfOb8kxaeQ==", - "dev": true - }, - "whatwg-url": { - "version": "5.0.0", - "resolved": "https://registry.npmjs.org/whatwg-url/-/whatwg-url-5.0.0.tgz", - "integrity": "sha512-saE57nupxk6v3HY35+jzBwYa0rKSy0XR8JSxZPwgLr7ys0IBzhGviA1/TUGJLmSVqs8pb9AnvICXEuOHLprYTw==", - "dev": true, - "requires": { - "tr46": "~0.0.3", - "webidl-conversions": "^3.0.0" - } - }, - "which": { - "version": "2.0.2", - "resolved": "https://registry.npmjs.org/which/-/which-2.0.2.tgz", - "integrity": "sha512-BLI3Tl1TW3Pvl70l3yq3Y64i+awpwXqsGBYWkkqMtnbXgrMD+yj7rhW0kuEDxzJaYXGjEW5ogapKNMEKNMjibA==", - "dev": true, - "requires": { - "isexe": "^2.0.0" - } - }, - "which-boxed-primitive": { - "version": "1.0.2", - "resolved": "https://registry.npmjs.org/which-boxed-primitive/-/which-boxed-primitive-1.0.2.tgz", - "integrity": "sha512-bwZdv0AKLpplFY2KZRX6TvyuN7ojjr7lwkg6ml0roIy9YeuSr7JS372qlNW18UQYzgYK9ziGcerWqZOmEn9VNg==", - "dev": true, - "requires": { - "is-bigint": "^1.0.1", - "is-boolean-object": "^1.1.0", - "is-number-object": "^1.0.4", - "is-string": "^1.0.5", - "is-symbol": "^1.0.3" - } - }, - "which-module": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/which-module/-/which-module-2.0.0.tgz", - "integrity": "sha512-B+enWhmw6cjfVC7kS8Pj9pCrKSc5txArRyaYGe088shv/FGWH+0Rjx/xPgtsWfsUtS27FkP697E4DDhgrgoc0Q==", - "dev": true - }, - "which-typed-array": { - "version": "1.1.9", - "resolved": "https://registry.npmjs.org/which-typed-array/-/which-typed-array-1.1.9.tgz", - "integrity": "sha512-w9c4xkx6mPidwp7180ckYWfMmvxpjlZuIudNtDf4N/tTAUB8VJbX25qZoAsrtGuYNnGw3pa0AXgbGKRB8/EceA==", - "dev": true, - "requires": { - "available-typed-arrays": "^1.0.5", - "call-bind": "^1.0.2", - "for-each": "^0.3.3", - "gopd": "^1.0.1", - "has-tostringtag": "^1.0.0", - "is-typed-array": "^1.1.10" - } - }, - "widest-line": { - "version": "4.0.1", - "resolved": "https://registry.npmjs.org/widest-line/-/widest-line-4.0.1.tgz", - "integrity": "sha512-o0cyEG0e8GPzT4iGHphIOh0cJOV8fivsXxddQasHPHfoZf1ZexrfeA21w2NaEN1RHE+fXlfISmOE8R9N3u3Qig==", - "dev": true, - "requires": { - "string-width": "^5.0.1" - } - }, - "wildcard-match": { - "version": "5.1.2", - "resolved": "https://registry.npmjs.org/wildcard-match/-/wildcard-match-5.1.2.tgz", - "integrity": "sha512-qNXwI591Z88c8bWxp+yjV60Ch4F8Riawe3iGxbzquhy8Xs9m+0+SLFBGb/0yCTIDElawtaImC37fYZ+dr32KqQ==", - "dev": true - }, - "windows-release": { - "version": "5.1.0", - "resolved": "https://registry.npmjs.org/windows-release/-/windows-release-5.1.0.tgz", - "integrity": "sha512-CddHecz5dt0ngTjGPP1uYr9Tjl4qq5rEKNk8UGb8XCdngNXI+GRYvqelD055FdiUgqODZz3R/5oZWYldPtXQpA==", - "dev": true, - "requires": { - "execa": "^5.1.1" - }, - "dependencies": { - "execa": { - "version": "5.1.1", - "resolved": "https://registry.npmjs.org/execa/-/execa-5.1.1.tgz", - "integrity": "sha512-8uSpZZocAZRBAPIEINJj3Lo9HyGitllczc27Eh5YYojjMFMn8yHMDMaUHE2Jqfq05D/wucwI4JGURyXt1vchyg==", - "dev": true, - "requires": { - "cross-spawn": "^7.0.3", - "get-stream": "^6.0.0", - "human-signals": "^2.1.0", - "is-stream": "^2.0.0", - "merge-stream": "^2.0.0", - "npm-run-path": "^4.0.1", - "onetime": "^5.1.2", - "signal-exit": "^3.0.3", - "strip-final-newline": "^2.0.0" - } - }, - "human-signals": { - "version": "2.1.0", - "resolved": "https://registry.npmjs.org/human-signals/-/human-signals-2.1.0.tgz", - "integrity": "sha512-B4FFZ6q/T2jhhksgkbEW3HBvWIfDW85snkQgawt07S7J5QXTk6BkNV+0yAeZrM5QpMAdYlocGoljn0sJ/WQkFw==", - "dev": true - }, - "is-stream": { - "version": "2.0.1", - "resolved": "https://registry.npmjs.org/is-stream/-/is-stream-2.0.1.tgz", - "integrity": "sha512-hFoiJiTl63nn+kstHGBtewWSKnQLpyb155KHheA1l39uvtO9nWIop1p3udqPcUd/xbF1VLMO4n7OI6p7RbngDg==", - "dev": true - }, - "mimic-fn": { - "version": "2.1.0", - "resolved": "https://registry.npmjs.org/mimic-fn/-/mimic-fn-2.1.0.tgz", - "integrity": "sha512-OqbOk5oEQeAZ8WXWydlu9HJjz9WVdEIvamMCcXmuqUYjTknH/sqsWvhQ3vgwKFRR1HpjvNBKQ37nbJgYzGqGcg==", - "dev": true - }, - "npm-run-path": { - "version": "4.0.1", - "resolved": "https://registry.npmjs.org/npm-run-path/-/npm-run-path-4.0.1.tgz", - "integrity": "sha512-S48WzZW777zhNIrn7gxOlISNAqi9ZC/uQFnRdbeIHhZhCA6UqpkOT8T1G7BvfdgP4Er8gF4sUbaS0i7QvIfCWw==", - "dev": true, - "requires": { - "path-key": "^3.0.0" - } - }, - "onetime": { - "version": "5.1.2", - "resolved": "https://registry.npmjs.org/onetime/-/onetime-5.1.2.tgz", - "integrity": "sha512-kbpaSSGJTWdAY5KPVeMOKXSrPtr8C8C7wodJbcsd51jRnmD+GZu8Y0VoU6Dm5Z4vWr0Ig/1NKuWRKf7j5aaYSg==", - "dev": true, - "requires": { - "mimic-fn": "^2.1.0" - } - }, - "strip-final-newline": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/strip-final-newline/-/strip-final-newline-2.0.0.tgz", - "integrity": "sha512-BrpvfNAE3dcvq7ll3xVumzjKjZQ5tI1sEUIKr3Uoks0XUl45St3FlatVqef9prk4jRDzhW6WZg+3bk93y6pLjA==", - "dev": true - } - } - }, - "word-wrap": { - "version": "1.2.3", - "resolved": "https://registry.npmjs.org/word-wrap/-/word-wrap-1.2.3.tgz", - "integrity": "sha512-Hz/mrNwitNRh/HUAtM/VT/5VH+ygD6DV7mYKZAtHOrbs8U7lvPS6xf7EJKMF0uW1KJCl0H701g3ZGus+muE5vQ==", - "dev": true - }, - "workerpool": { - "version": "6.2.1", - "resolved": "https://registry.npmjs.org/workerpool/-/workerpool-6.2.1.tgz", - "integrity": "sha512-ILEIE97kDZvF9Wb9f6h5aXK4swSlKGUcOEGiIYb2OOu/IrDU9iwj0fD//SsA6E5ibwJxpEvhullJY4Sl4GcpAw==", - "dev": true - }, - "wrap-ansi": { - "version": "8.0.1", - "resolved": "https://registry.npmjs.org/wrap-ansi/-/wrap-ansi-8.0.1.tgz", - "integrity": "sha512-QFF+ufAqhoYHvoHdajT/Po7KoXVBPXS2bgjIam5isfWJPfIOnQZ50JtUiVvCv/sjgacf3yRrt2ZKUZ/V4itN4g==", - "dev": true, - "requires": { - "ansi-styles": "^6.1.0", - "string-width": "^5.0.1", - "strip-ansi": "^7.0.1" - } - }, - "wrappy": { - "version": "1.0.2", - "resolved": "https://registry.npmjs.org/wrappy/-/wrappy-1.0.2.tgz", - "integrity": "sha512-l4Sp/DRseor9wL6EvV2+TuQn63dMkPjZ/sp9XkghTEbV9KlPS1xUsZ3u7/IQO4wxtcFB4bgpQPRcR3QCvezPcQ==", - "dev": true - }, - "write-file-atomic": { - "version": "3.0.3", - "resolved": "https://registry.npmjs.org/write-file-atomic/-/write-file-atomic-3.0.3.tgz", - "integrity": "sha512-AvHcyZ5JnSfq3ioSyjrBkH9yW4m7Ayk8/9My/DD9onKeu/94fwrMocemO2QAJFAlnnDN+ZDS+ZjAR5ua1/PV/Q==", - "dev": true, - "requires": { - "imurmurhash": "^0.1.4", - "is-typedarray": "^1.0.0", - "signal-exit": "^3.0.2", - "typedarray-to-buffer": "^3.1.5" - } - }, - "xdg-basedir": { - "version": "5.1.0", - "resolved": "https://registry.npmjs.org/xdg-basedir/-/xdg-basedir-5.1.0.tgz", - "integrity": "sha512-GCPAHLvrIH13+c0SuacwvRYj2SxJXQ4kaVTT5xgL3kPrz56XxkF21IGhjSE1+W0aw7gpBWRGXLCPnPby6lSpmQ==", - "dev": true - }, - "xregexp": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/xregexp/-/xregexp-2.0.0.tgz", - "integrity": "sha512-xl/50/Cf32VsGq/1R8jJE5ajH1yMCQkpmoS10QbFZWl2Oor4H0Me64Pu2yxvsRWK3m6soJbmGfzSR7BYmDcWAA==", - "dev": true - }, - "y18n": { - "version": "4.0.3", - "resolved": "https://registry.npmjs.org/y18n/-/y18n-4.0.3.tgz", - "integrity": "sha512-JKhqTOwSrqNA1NY5lSztJ1GrBiUodLMmIZuLiDaMRJ+itFd+ABVE8XBjOvIWL+rSqNDC74LCSFmlb/U4UZ4hJQ==", - "dev": true - }, - "yallist": { - "version": "3.1.1", - "resolved": "https://registry.npmjs.org/yallist/-/yallist-3.1.1.tgz", - "integrity": "sha512-a4UGQaWPH59mOXUYnAG2ewncQS4i4F43Tv3JoAM+s2VDAmS9NsK8GpDMLrCHPksFT7h3K6TOoUNn2pb7RoXx4g==", - "dev": true - }, - "yargs": { - "version": "15.4.1", - "resolved": "https://registry.npmjs.org/yargs/-/yargs-15.4.1.tgz", - "integrity": "sha512-aePbxDmcYW++PaqBsJ+HYUFwCdv4LVvdnhBy78E57PIor8/OVvhMrADFFEDh8DHDFRv/O9i3lPhsENjO7QX0+A==", - "dev": true, - "requires": { - "cliui": "^6.0.0", - "decamelize": "^1.2.0", - "find-up": "^4.1.0", - "get-caller-file": "^2.0.1", - "require-directory": "^2.1.1", - "require-main-filename": "^2.0.0", - "set-blocking": "^2.0.0", - "string-width": "^4.2.0", - "which-module": "^2.0.0", - "y18n": "^4.0.0", - "yargs-parser": "^18.1.2" - }, - "dependencies": { - "ansi-regex": { - "version": "5.0.1", - "resolved": "https://registry.npmjs.org/ansi-regex/-/ansi-regex-5.0.1.tgz", - "integrity": "sha512-quJQXlTSUGL2LH9SUXo8VwsY4soanhgo6LNSm84E1LBcE8s3O0wpdiRzyR9z/ZZJMlMWv37qOOb9pdJlMUEKFQ==", - "dev": true - }, - "emoji-regex": { - "version": "8.0.0", - "resolved": "https://registry.npmjs.org/emoji-regex/-/emoji-regex-8.0.0.tgz", - "integrity": "sha512-MSjYzcWNOA0ewAHpz0MxpYFvwg6yjy1NG3xteoqz644VCo/RPgnr1/GGt+ic3iJTzQ8Eu3TdM14SawnVUmGE6A==", - "dev": true - }, - "string-width": { - "version": "4.2.3", - "resolved": "https://registry.npmjs.org/string-width/-/string-width-4.2.3.tgz", - "integrity": "sha512-wKyQRQpjJ0sIp62ErSZdGsjMJWsap5oRNihHhu6G7JVO/9jIB6UyevL+tXuOqrng8j/cxKTWyWUwvSTriiZz/g==", - "dev": true, - "requires": { - "emoji-regex": "^8.0.0", - "is-fullwidth-code-point": "^3.0.0", - "strip-ansi": "^6.0.1" - } - }, - "strip-ansi": { - "version": "6.0.1", - "resolved": "https://registry.npmjs.org/strip-ansi/-/strip-ansi-6.0.1.tgz", - "integrity": "sha512-Y38VPSHcqkFrCpFnQ9vuSXmquuv5oXOKpGeT6aGrr3o3Gc9AlVa6JBfUSOCnbxGGZF+/0ooI7KrPuUSztUdU5A==", - "dev": true, - "requires": { - "ansi-regex": "^5.0.1" - } - }, - "yargs-parser": { - "version": "18.1.3", - "resolved": "https://registry.npmjs.org/yargs-parser/-/yargs-parser-18.1.3.tgz", - "integrity": "sha512-o50j0JeToy/4K6OZcaQmW6lyXXKhq7csREXcDwk2omFPJEwUNOVtJKvmDr9EI1fAJZUyZcRF7kxGBWmRXudrCQ==", - "dev": true, - "requires": { - "camelcase": "^5.0.0", - "decamelize": "^1.2.0" - } - } - } - }, - "yargs-parser": { - "version": "21.1.1", - "resolved": "https://registry.npmjs.org/yargs-parser/-/yargs-parser-21.1.1.tgz", - "integrity": "sha512-tVpsJW7DdjecAiFpbIB1e3qxIQsE6NoPc5/eTdrbbIC4h0LVsWhnoa3g+m2HclBIujHzsxZ4VJVA+GUuc2/LBw==", - "dev": true - }, - "yargs-unparser": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/yargs-unparser/-/yargs-unparser-2.0.0.tgz", - "integrity": "sha512-7pRTIA9Qc1caZ0bZ6RYRGbHJthJWuakf+WmHK0rVeLkNrrGhfoabBNdue6kdINI6r4if7ocq9aD/n7xwKOdzOA==", - "dev": true, - "requires": { - "camelcase": "^6.0.0", - "decamelize": "^4.0.0", - "flat": "^5.0.2", - "is-plain-obj": "^2.1.0" - }, - "dependencies": { - "camelcase": { - "version": "6.3.0", - "resolved": "https://registry.npmjs.org/camelcase/-/camelcase-6.3.0.tgz", - "integrity": "sha512-Gmy6FhYlCY7uOElZUSbxo2UCDH8owEk996gkbrpsgGtrJLM3J7jGxl9Ic7Qwwj4ivOE5AWZWRMecDdF7hqGjFA==", - "dev": true - }, - "decamelize": { - "version": "4.0.0", - "resolved": "https://registry.npmjs.org/decamelize/-/decamelize-4.0.0.tgz", - "integrity": "sha512-9iE1PgSik9HeIIw2JO94IidnE3eBoQrFJ3w7sFuzSX4DpmZ3v5sZpUiV5Swcf6mQEF+Y0ru8Neo+p+nyh2J+hQ==", - "dev": true - } - } - }, - "yn": { - "version": "3.1.1", - "resolved": "https://registry.npmjs.org/yn/-/yn-3.1.1.tgz", - "integrity": "sha512-Ux4ygGWsu2c7isFWe8Yu1YluJmqVhxqK2cLXNQA5AcC3QfbGNpM7fu0Y8b/z16pXLnFxZYvWhd3fhBY9DLmC6Q==", - "dev": true - }, - "yocto-queue": { - "version": "0.1.0", - "resolved": "https://registry.npmjs.org/yocto-queue/-/yocto-queue-0.1.0.tgz", - "integrity": "sha512-rVksvsnNCdJ/ohGc6xgPwyN8eheCxsiLM8mxuE/t/mOVqJewPuO1miLpTHQiRgTKCLexL4MeAFVagts7HmNZ2Q==", - "dev": true - } } } diff --git a/package.json b/package.json index 8f87eb64ef7..205c301f4b2 100644 --- a/package.json +++ b/package.json @@ -23,11 +23,11 @@ "gh-pages": "gh-pages -d ./documentation -e ./documentation -u 'documentation-bot '" }, "dependencies": { - "@redis/bloom": "1.1.0", - "@redis/client": "1.4.2", + "@redis/bloom": "1.2.0", + "@redis/client": "1.5.0", "@redis/graph": "1.1.0", "@redis/json": "1.0.4", - "@redis/search": "1.1.0", + "@redis/search": "1.1.1", "@redis/time-series": "1.0.4" }, "devDependencies": { From 9decde68abe388eaaafd77b04154db99361bdc1a Mon Sep 17 00:00:00 2001 From: Leibale Date: Wed, 25 Jan 2023 13:31:01 -0500 Subject: [PATCH 1320/1748] Release redis@4.6.0 --- package-lock.json | 4 ++-- package.json | 2 +- 2 files changed, 3 insertions(+), 3 deletions(-) diff --git a/package-lock.json b/package-lock.json index 175818b347b..71eb863749c 100644 --- a/package-lock.json +++ b/package-lock.json @@ -1,12 +1,12 @@ { "name": "redis", - "version": "4.5.1", + "version": "4.6.0", "lockfileVersion": 3, "requires": true, "packages": { "": { "name": "redis", - "version": "4.5.1", + "version": "4.6.0", "license": "MIT", "workspaces": [ "./packages/*" diff --git a/package.json b/package.json index 205c301f4b2..fe6fab61666 100644 --- a/package.json +++ b/package.json @@ -1,7 +1,7 @@ { "name": "redis", "description": "A modern, high performance Redis client", - "version": "4.5.1", + "version": "4.6.0", "license": "MIT", "main": "./dist/index.js", "types": "./dist/index.d.ts", From c6e1124dffd58c2450af2ca633df84e8bd65d221 Mon Sep 17 00:00:00 2001 From: Leibale Eidelman Date: Wed, 25 Jan 2023 13:41:53 -0500 Subject: [PATCH 1321/1748] Update pub-sub.md --- docs/pub-sub.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/docs/pub-sub.md b/docs/pub-sub.md index 1428dca77ff..0c269c2a0f1 100644 --- a/docs/pub-sub.md +++ b/docs/pub-sub.md @@ -26,7 +26,7 @@ The event listener signature is as follows: buffers: Set; strings: Set; } -)`. +) ``` ## Subscribing From 3bd28a9924d9675b0df76063d2fc59ca1bd70120 Mon Sep 17 00:00:00 2001 From: Leibale Eidelman Date: Wed, 25 Jan 2023 20:37:00 -0500 Subject: [PATCH 1322/1748] fix #2391 - remove node: prefix from imports --- packages/client/lib/client/socket.spec.ts | 4 ++-- packages/client/lib/cluster/cluster-slots.ts | 4 ++-- 2 files changed, 4 insertions(+), 4 deletions(-) diff --git a/packages/client/lib/client/socket.spec.ts b/packages/client/lib/client/socket.spec.ts index 86929b227a1..eb555351ac4 100644 --- a/packages/client/lib/client/socket.spec.ts +++ b/packages/client/lib/client/socket.spec.ts @@ -1,6 +1,6 @@ -import { strict as assert } from 'node:assert'; +import { strict as assert } from 'assert'; import { spy } from 'sinon'; -import { once } from 'node:events'; +import { once } from 'events'; import RedisSocket, { RedisSocketOptions } from './socket'; describe('Socket', () => { diff --git a/packages/client/lib/cluster/cluster-slots.ts b/packages/client/lib/cluster/cluster-slots.ts index 2804f499f59..b540c2fa85f 100644 --- a/packages/client/lib/cluster/cluster-slots.ts +++ b/packages/client/lib/cluster/cluster-slots.ts @@ -3,9 +3,9 @@ import { RedisClusterClientOptions, RedisClusterOptions } from '.'; import { RedisCommandArgument, RedisFunctions, RedisModules, RedisScripts } from '../commands'; import { RootNodesUnavailableError } from '../errors'; import { ClusterSlotsNode } from '../commands/CLUSTER_SLOTS'; -import { types } from 'node:util'; +import { types } from 'util'; import { ChannelListeners, PubSubType, PubSubTypeListeners } from '../client/pub-sub'; -import { EventEmitter } from 'node:stream'; +import { EventEmitter } from 'stream'; // We need to use 'require', because it's not possible with Typescript to import // function that are exported as 'module.exports = function`, without esModuleInterop From c6587e219a1ded10a753742b4d8bf5f42cebd3a5 Mon Sep 17 00:00:00 2001 From: Leibale Date: Wed, 25 Jan 2023 20:38:56 -0500 Subject: [PATCH 1323/1748] Release client@1.5.1 --- packages/client/package.json | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/packages/client/package.json b/packages/client/package.json index 113bf570f1f..f282ee9386e 100644 --- a/packages/client/package.json +++ b/packages/client/package.json @@ -1,6 +1,6 @@ { "name": "@redis/client", - "version": "1.5.0", + "version": "1.5.1", "license": "MIT", "main": "./dist/index.js", "types": "./dist/index.d.ts", From 92043563f21ad0c1599903a3b165031a54d045b3 Mon Sep 17 00:00:00 2001 From: Leibale Date: Wed, 25 Jan 2023 20:46:05 -0500 Subject: [PATCH 1324/1748] Release client@1.5.2 --- packages/client/package.json | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/packages/client/package.json b/packages/client/package.json index f282ee9386e..22865dcd0d4 100644 --- a/packages/client/package.json +++ b/packages/client/package.json @@ -1,6 +1,6 @@ { "name": "@redis/client", - "version": "1.5.1", + "version": "1.5.2", "license": "MIT", "main": "./dist/index.js", "types": "./dist/index.d.ts", From b0ae4aa53ea80164754a4c2f0e56ab01c7046be4 Mon Sep 17 00:00:00 2001 From: Leibale Date: Wed, 25 Jan 2023 20:48:42 -0500 Subject: [PATCH 1325/1748] upgrade @redis/client --- package-lock.json | 4 ++-- package.json | 2 +- 2 files changed, 3 insertions(+), 3 deletions(-) diff --git a/package-lock.json b/package-lock.json index 71eb863749c..f0ce5498f35 100644 --- a/package-lock.json +++ b/package-lock.json @@ -13,7 +13,7 @@ ], "dependencies": { "@redis/bloom": "1.2.0", - "@redis/client": "1.5.0", + "@redis/client": "1.5.2", "@redis/graph": "1.1.0", "@redis/json": "1.0.4", "@redis/search": "1.1.1", @@ -8260,7 +8260,7 @@ }, "packages/client": { "name": "@redis/client", - "version": "1.5.0", + "version": "1.5.2", "license": "MIT", "dependencies": { "cluster-key-slot": "1.1.2", diff --git a/package.json b/package.json index fe6fab61666..9ec5ac53c2a 100644 --- a/package.json +++ b/package.json @@ -24,7 +24,7 @@ }, "dependencies": { "@redis/bloom": "1.2.0", - "@redis/client": "1.5.0", + "@redis/client": "1.5.2", "@redis/graph": "1.1.0", "@redis/json": "1.0.4", "@redis/search": "1.1.1", From 00e365250ecb45990118739e624a64c3b1183446 Mon Sep 17 00:00:00 2001 From: Leibale Date: Wed, 25 Jan 2023 20:49:08 -0500 Subject: [PATCH 1326/1748] Release redis@4.6.1 --- package-lock.json | 4 ++-- package.json | 2 +- 2 files changed, 3 insertions(+), 3 deletions(-) diff --git a/package-lock.json b/package-lock.json index f0ce5498f35..7c491078aa5 100644 --- a/package-lock.json +++ b/package-lock.json @@ -1,12 +1,12 @@ { "name": "redis", - "version": "4.6.0", + "version": "4.6.1", "lockfileVersion": 3, "requires": true, "packages": { "": { "name": "redis", - "version": "4.6.0", + "version": "4.6.1", "license": "MIT", "workspaces": [ "./packages/*" diff --git a/package.json b/package.json index 9ec5ac53c2a..450f6eb686a 100644 --- a/package.json +++ b/package.json @@ -1,7 +1,7 @@ { "name": "redis", "description": "A modern, high performance Redis client", - "version": "4.6.0", + "version": "4.6.1", "license": "MIT", "main": "./dist/index.js", "types": "./dist/index.d.ts", From 2dba7f200b93f13e37224083da43567b192cf22f Mon Sep 17 00:00:00 2001 From: Leibale Eidelman Date: Fri, 27 Jan 2023 09:45:22 -0500 Subject: [PATCH 1327/1748] fix #2392 - handle errors in `legacyMode` (#2394) --- packages/client/lib/client/index.spec.ts | 190 +++-------------------- packages/client/lib/client/index.ts | 19 ++- 2 files changed, 36 insertions(+), 173 deletions(-) diff --git a/packages/client/lib/client/index.spec.ts b/packages/client/lib/client/index.spec.ts index aadf823e572..7af7f35d4d8 100644 --- a/packages/client/lib/client/index.spec.ts +++ b/packages/client/lib/client/index.spec.ts @@ -2,20 +2,14 @@ import { strict as assert } from 'assert'; import testUtils, { GLOBAL, waitTillBeenCalled } from '../test-utils'; import RedisClient, { RedisClientType } from '.'; import { RedisClientMultiCommandType } from './multi-command'; -import { RedisCommandRawReply, RedisModules, RedisFunctions, RedisScripts } from '../commands'; -import { AbortError, ClientClosedError, ClientOfflineError, ConnectionTimeoutError, DisconnectsClientError, ErrorReply, SocketClosedUnexpectedlyError, WatchError } from '../errors'; +import { RedisCommandArguments, RedisCommandRawReply, RedisModules, RedisFunctions, RedisScripts } from '../commands'; +import { AbortError, ClientClosedError, ClientOfflineError, ConnectionTimeoutError, DisconnectsClientError, SocketClosedUnexpectedlyError, WatchError } from '../errors'; import { defineScript } from '../lua-script'; import { spy } from 'sinon'; import { once } from 'events'; import { ClientKillFilters } from '../commands/CLIENT_KILL'; -import { ClusterSlotStates } from '../commands/CLUSTER_SETSLOT'; import { promisify } from 'util'; -// We need to use 'require', because it's not possible with Typescript to import -// function that are exported as 'module.exports = function`, without esModuleInterop -// set to true. -const calculateSlot = require('cluster-key-slot'); - export const SQUARE_SCRIPT = defineScript({ SCRIPT: 'return ARGV[1] * ARGV[1];', NUMBER_OF_KEYS: 0, @@ -171,6 +165,28 @@ describe('Client', () => { } }); + testUtils.testWithClient('client.sendCommand should reply with error', async client => { + await assert.rejects( + promisify(client.sendCommand).call(client, '1', '2') + ); + }, { + ...GLOBAL.SERVERS.OPEN, + clientOptions: { + legacyMode: true + } + }); + + testUtils.testWithClient('client.hGetAll should reply with error', async client => { + await assert.rejects( + promisify(client.hGetAll).call(client) + ); + }, { + ...GLOBAL.SERVERS.OPEN, + clientOptions: { + legacyMode: true + } + }); + testUtils.testWithClient('client.v4.sendCommand should return a promise', async client => { assert.equal( await client.v4.sendCommand(['PING']), @@ -347,19 +363,6 @@ describe('Client', () => { legacyMode: true } }); - - testUtils.testWithClient('pingInterval', async client => { - assert.deepEqual( - await once(client, 'ping-interval'), - ['PONG'] - ); - }, { - ...GLOBAL.SERVERS.OPEN, - clientOptions: { - legacyMode: true, - pingInterval: 1 - } - }); }); describe('events', () => { @@ -823,34 +826,7 @@ describe('Client', () => { } }, GLOBAL.SERVERS.OPEN); - testUtils.testWithClient('should be able to PING in PubSub mode', async client => { - await client.connect(); - - try { - await client.subscribe('channel', () => { - // noop - }); - - const [string, buffer, customString, customBuffer] = await Promise.all([ - client.ping(), - client.ping(client.commandOptions({ returnBuffers: true })), - client.ping('custom'), - client.ping(client.commandOptions({ returnBuffers: true }), 'custom') - ]); - - assert.equal(string, 'pong'); - assert.deepEqual(buffer, Buffer.from('pong')); - assert.equal(customString, 'custom'); - assert.deepEqual(customBuffer, Buffer.from('custom')); - } finally { - await client.disconnect(); - } - }, { - ...GLOBAL.SERVERS.OPEN, - disableClientSetup: true - }); - - testUtils.testWithClient('should be able to QUIT in PubSub mode', async client => { + testUtils.testWithClient('should be able to quit in PubSub mode', async client => { await client.subscribe('channel', () => { // noop }); @@ -859,122 +835,6 @@ describe('Client', () => { assert.equal(client.isOpen, false); }, GLOBAL.SERVERS.OPEN); - - testUtils.testWithClient('should reject GET in PubSub mode', async client => { - await client.connect(); - - try { - await client.subscribe('channel', () => { - // noop - }); - - await assert.rejects(client.get('key'), ErrorReply); - } finally { - await client.disconnect(); - } - }, { - ...GLOBAL.SERVERS.OPEN, - disableClientSetup: true - }); - - describe('shareded PubSub', () => { - testUtils.isVersionGreaterThanHook([7]); - - testUtils.testWithClient('should be able to receive messages', async publisher => { - const subscriber = publisher.duplicate(); - - await subscriber.connect(); - - try { - const listener = spy(); - await subscriber.sSubscribe('channel', listener); - - await Promise.all([ - waitTillBeenCalled(listener), - publisher.sPublish('channel', 'message') - ]); - - assert.ok(listener.calledOnceWithExactly('message', 'channel')); - - await subscriber.sUnsubscribe(); - - // should be able to send commands - await assert.doesNotReject(subscriber.ping()); - } finally { - await subscriber.disconnect(); - } - }, { - ...GLOBAL.SERVERS.OPEN - }); - - testUtils.testWithClient('should emit sharded-channel-moved event', async publisher => { - await publisher.clusterAddSlotsRange({ start: 0, end: 16383 }); - - const subscriber = publisher.duplicate(); - - await subscriber.connect(); - - try { - await subscriber.sSubscribe('channel', () => {}); - - await Promise.all([ - publisher.clusterSetSlot( - calculateSlot('channel'), - ClusterSlotStates.NODE, - await publisher.clusterMyId() - ), - once(subscriber, 'sharded-channel-moved') - ]); - - assert.equal( - await subscriber.ping(), - 'PONG' - ); - } finally { - await subscriber.disconnect(); - } - }, { - serverArguments: ['--cluster-enabled', 'yes'] - }); - }); - - testUtils.testWithClient('should handle errors in SUBSCRIBE', async publisher => { - const subscriber = publisher.duplicate(); - - await subscriber.connect(); - - try { - const listener1 = spy(); - await subscriber.subscribe('1', listener1); - - await publisher.aclSetUser('default', 'resetchannels'); - - - const listener2 = spy(); - await assert.rejects(subscriber.subscribe('2', listener2)); - - await Promise.all([ - waitTillBeenCalled(listener1), - publisher.aclSetUser('default', 'allchannels'), - publisher.publish('1', 'message'), - ]); - assert.ok(listener1.calledOnceWithExactly('message', '1')); - - await subscriber.subscribe('2', listener2); - - await Promise.all([ - waitTillBeenCalled(listener2), - publisher.publish('2', 'message'), - ]); - assert.ok(listener2.calledOnceWithExactly('message', '2')); - } finally { - await subscriber.disconnect(); - } - }, { - // this test change ACL rules, running in isolated server - serverArguments: [], - minimumDockerVersion: [6 ,2] // ACL PubSub rules were added in Redis 6.2 - }); }); testUtils.testWithClient('ConnectionTimeoutError', async client => { diff --git a/packages/client/lib/client/index.ts b/packages/client/lib/client/index.ts index ae5e2fe5e84..6af572edc6a 100644 --- a/packages/client/lib/client/index.ts +++ b/packages/client/lib/client/index.ts @@ -15,6 +15,7 @@ import { ClientClosedError, ClientOfflineError, DisconnectsClientError } from '. import { URL } from 'url'; import { TcpSocketConnectOpts } from 'net'; import { PubSubType, PubSubListener, PubSubTypeListeners, ChannelListeners } from './pub-sub'; +import { callbackify } from 'util'; export interface RedisClientOptions< M extends RedisModules = RedisModules, @@ -343,7 +344,9 @@ export default class RedisClient< (this as any).sendCommand = (...args: Array): void => { const result = this.#legacySendCommand(...args); if (result) { - result.promise.then(reply => result.callback(null, reply)); + result.promise + .then(reply => result.callback(null, reply)) + .catch(err => result.callback(err)); } }; @@ -380,18 +383,18 @@ export default class RedisClient< promise.catch(err => this.emit('error', err)); } - #defineLegacyCommand(this: any, name: string, command?: RedisCommand): void { - this.#v4[name] = this[name].bind(this); - this[name] = command && command.TRANSFORM_LEGACY_REPLY && command.transformReply ? + #defineLegacyCommand(name: string, command?: RedisCommand): void { + this.#v4[name] = (this as any)[name].bind(this); + (this as any)[name] = command && command.TRANSFORM_LEGACY_REPLY && command.transformReply ? (...args: Array) => { const result = this.#legacySendCommand(name, ...args); if (result) { - result.promise.then((reply: any) => { - result.callback(null, command.transformReply!(reply)); - }); + result.promise + .then(reply => result.callback(null, command.transformReply!(reply))) + .catch(err => result.callback(err)); } } : - (...args: Array) => this.sendCommand(name, ...args); + (...args: Array) => (this as any).sendCommand(name, ...args); } #pingTimer?: NodeJS.Timer; From be335efc209315200cd0c93758780ee8da52682b Mon Sep 17 00:00:00 2001 From: Leibale Date: Fri, 27 Jan 2023 09:47:16 -0500 Subject: [PATCH 1328/1748] Release client@1.5.3 --- packages/client/package.json | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/packages/client/package.json b/packages/client/package.json index 22865dcd0d4..b50cdb59eb6 100644 --- a/packages/client/package.json +++ b/packages/client/package.json @@ -1,6 +1,6 @@ { "name": "@redis/client", - "version": "1.5.2", + "version": "1.5.3", "license": "MIT", "main": "./dist/index.js", "types": "./dist/index.d.ts", From c00b20f02ae072aab07863bac2a9484d82b51b46 Mon Sep 17 00:00:00 2001 From: Leibale Date: Fri, 27 Jan 2023 09:50:34 -0500 Subject: [PATCH 1329/1748] upgrade subpackages --- package-lock.json | 4 ++-- package.json | 2 +- 2 files changed, 3 insertions(+), 3 deletions(-) diff --git a/package-lock.json b/package-lock.json index 7c491078aa5..46962a3c869 100644 --- a/package-lock.json +++ b/package-lock.json @@ -13,7 +13,7 @@ ], "dependencies": { "@redis/bloom": "1.2.0", - "@redis/client": "1.5.2", + "@redis/client": "1.5.3", "@redis/graph": "1.1.0", "@redis/json": "1.0.4", "@redis/search": "1.1.1", @@ -8260,7 +8260,7 @@ }, "packages/client": { "name": "@redis/client", - "version": "1.5.2", + "version": "1.5.3", "license": "MIT", "dependencies": { "cluster-key-slot": "1.1.2", diff --git a/package.json b/package.json index 450f6eb686a..b294bef03db 100644 --- a/package.json +++ b/package.json @@ -24,7 +24,7 @@ }, "dependencies": { "@redis/bloom": "1.2.0", - "@redis/client": "1.5.2", + "@redis/client": "1.5.3", "@redis/graph": "1.1.0", "@redis/json": "1.0.4", "@redis/search": "1.1.1", From 58e572bdc723b35f37396c847963edc9f2e9fb6c Mon Sep 17 00:00:00 2001 From: Leibale Date: Fri, 27 Jan 2023 09:51:20 -0500 Subject: [PATCH 1330/1748] Release redis@4.6.2 --- package-lock.json | 4 ++-- package.json | 2 +- 2 files changed, 3 insertions(+), 3 deletions(-) diff --git a/package-lock.json b/package-lock.json index 46962a3c869..aec044d5b79 100644 --- a/package-lock.json +++ b/package-lock.json @@ -1,12 +1,12 @@ { "name": "redis", - "version": "4.6.1", + "version": "4.6.2", "lockfileVersion": 3, "requires": true, "packages": { "": { "name": "redis", - "version": "4.6.1", + "version": "4.6.2", "license": "MIT", "workspaces": [ "./packages/*" diff --git a/package.json b/package.json index b294bef03db..ab8eb9c93b3 100644 --- a/package.json +++ b/package.json @@ -1,7 +1,7 @@ { "name": "redis", "description": "A modern, high performance Redis client", - "version": "4.6.1", + "version": "4.6.2", "license": "MIT", "main": "./dist/index.js", "types": "./dist/index.d.ts", From b3cd78503dbfe7f81919bde1ad6fd75068006ba3 Mon Sep 17 00:00:00 2001 From: Leibale Eidelman Date: Sat, 28 Jan 2023 18:20:48 -0500 Subject: [PATCH 1331/1748] fix reconnecting event (#2396) * fix #2395 - fix reconnecting event * Update socket.ts --- packages/client/lib/client/socket.ts | 6 ++---- 1 file changed, 2 insertions(+), 4 deletions(-) diff --git a/packages/client/lib/client/socket.ts b/packages/client/lib/client/socket.ts index 4c64f899559..ac3b7f5bf3b 100644 --- a/packages/client/lib/client/socket.ts +++ b/packages/client/lib/client/socket.ts @@ -162,17 +162,15 @@ export default class RedisSocket extends EventEmitter { this.#isReady = true; this.emit('ready'); } catch (err) { - const retryIn = this.#shouldReconnect(retries, err as Error); + const retryIn = this.#shouldReconnect(retries++, err as Error); if (typeof retryIn !== 'number') { throw retryIn; } this.emit('error', err); await promiseTimeout(retryIn); + this.emit('reconnecting'); } - - retries++; - this.emit('reconnecting'); } while (this.#isOpen && !this.#isReady); } From f1e951debecca530d2a55893abeb8d60fa5ba1e8 Mon Sep 17 00:00:00 2001 From: Leibale Date: Sat, 28 Jan 2023 18:24:14 -0500 Subject: [PATCH 1332/1748] Release client@1.5.4 --- packages/client/package.json | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/packages/client/package.json b/packages/client/package.json index b50cdb59eb6..918a8e97f14 100644 --- a/packages/client/package.json +++ b/packages/client/package.json @@ -1,6 +1,6 @@ { "name": "@redis/client", - "version": "1.5.3", + "version": "1.5.4", "license": "MIT", "main": "./dist/index.js", "types": "./dist/index.d.ts", From a8915536bf85b144ba969b421b037be1c01057a8 Mon Sep 17 00:00:00 2001 From: Leibale Date: Sat, 28 Jan 2023 18:26:55 -0500 Subject: [PATCH 1333/1748] upgrade subpackages --- package-lock.json | 2 +- package.json | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/package-lock.json b/package-lock.json index aec044d5b79..6699dcd74c9 100644 --- a/package-lock.json +++ b/package-lock.json @@ -8260,7 +8260,7 @@ }, "packages/client": { "name": "@redis/client", - "version": "1.5.3", + "version": "1.5.4", "license": "MIT", "dependencies": { "cluster-key-slot": "1.1.2", diff --git a/package.json b/package.json index ab8eb9c93b3..381de00c6f6 100644 --- a/package.json +++ b/package.json @@ -24,7 +24,7 @@ }, "dependencies": { "@redis/bloom": "1.2.0", - "@redis/client": "1.5.3", + "@redis/client": "1.5.4", "@redis/graph": "1.1.0", "@redis/json": "1.0.4", "@redis/search": "1.1.1", From 7a0334f39679aa60d6a4b6f1d8421e58f0847ca2 Mon Sep 17 00:00:00 2001 From: Leibale Date: Sat, 28 Jan 2023 18:27:34 -0500 Subject: [PATCH 1334/1748] Release redis@4.6.3 --- package-lock.json | 4 ++-- package.json | 2 +- 2 files changed, 3 insertions(+), 3 deletions(-) diff --git a/package-lock.json b/package-lock.json index 6699dcd74c9..51947c86d38 100644 --- a/package-lock.json +++ b/package-lock.json @@ -1,12 +1,12 @@ { "name": "redis", - "version": "4.6.2", + "version": "4.6.3", "lockfileVersion": 3, "requires": true, "packages": { "": { "name": "redis", - "version": "4.6.2", + "version": "4.6.3", "license": "MIT", "workspaces": [ "./packages/*" diff --git a/package.json b/package.json index 381de00c6f6..fddfb80e2a3 100644 --- a/package.json +++ b/package.json @@ -1,7 +1,7 @@ { "name": "redis", "description": "A modern, high performance Redis client", - "version": "4.6.2", + "version": "4.6.3", "license": "MIT", "main": "./dist/index.js", "types": "./dist/index.d.ts", From bb9a024d91ecda71595370d717420964c6d7bda0 Mon Sep 17 00:00:00 2001 From: Soumitra Shewale Date: Mon, 30 Jan 2023 22:53:17 +0530 Subject: [PATCH 1335/1748] fix: Fix small typo in Redis Pub/Sub docs (#2400) --- docs/pub-sub.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/docs/pub-sub.md b/docs/pub-sub.md index 0c269c2a0f1..4b0e138d8ac 100644 --- a/docs/pub-sub.md +++ b/docs/pub-sub.md @@ -8,7 +8,7 @@ Pub/Sub requires a dedicated stand-alone client. You can easily get one by `.dup ```typescript const subscriber = client.duplicate(); -subscribe.on('error', err => console.error(err)); +subscriber.on('error', err => console.error(err)); await subscriber.connect(); ``` From b3c260a5dbac8506279dfbf13bfeed050dc67c2e Mon Sep 17 00:00:00 2001 From: Leibale Date: Mon, 30 Jan 2023 14:02:15 -0500 Subject: [PATCH 1336/1748] fix graph tests --- packages/graph/lib/commands/EXPLAIN.spec.ts | 5 ++++- packages/graph/lib/commands/RO_QUERY.spec.ts | 5 ++++- packages/graph/lib/graph.spec.ts | 16 ++++++++-------- 3 files changed, 16 insertions(+), 10 deletions(-) diff --git a/packages/graph/lib/commands/EXPLAIN.spec.ts b/packages/graph/lib/commands/EXPLAIN.spec.ts index 2919a211631..86d89b212cb 100644 --- a/packages/graph/lib/commands/EXPLAIN.spec.ts +++ b/packages/graph/lib/commands/EXPLAIN.spec.ts @@ -11,7 +11,10 @@ describe('EXPLAIN', () => { }); testUtils.testWithClient('client.graph.explain', async client => { - const reply = await client.graph.explain('key', 'RETURN 0'); + const [, reply] = await Promise.all([ + client.graph.query('key', 'RETURN 0'), // make sure to create a graph first + client.graph.explain('key', 'RETURN 0') + ]); assert.ok(Array.isArray(reply)); assert.ok(!reply.find(x => typeof x !== 'string')); }, GLOBAL.SERVERS.OPEN); diff --git a/packages/graph/lib/commands/RO_QUERY.spec.ts b/packages/graph/lib/commands/RO_QUERY.spec.ts index 0fbaeb69537..1d76b1bd652 100644 --- a/packages/graph/lib/commands/RO_QUERY.spec.ts +++ b/packages/graph/lib/commands/RO_QUERY.spec.ts @@ -11,7 +11,10 @@ describe('RO_QUERY', () => { }); testUtils.testWithClient('client.graph.roQuery', async client => { - const { data } = await client.graph.roQuery('key', 'RETURN 0'); + const [, { data }] = await Promise.all([ + client.graph.query('key', 'RETURN 0'), // make sure to create a graph first + client.graph.roQuery('key', 'RETURN 0') + ]); assert.deepEqual(data, [[0]]); }, GLOBAL.SERVERS.OPEN); }); \ No newline at end of file diff --git a/packages/graph/lib/graph.spec.ts b/packages/graph/lib/graph.spec.ts index 51912356d3a..ff45759fe83 100644 --- a/packages/graph/lib/graph.spec.ts +++ b/packages/graph/lib/graph.spec.ts @@ -5,7 +5,7 @@ import Graph from './graph'; describe('Graph', () => { testUtils.testWithClient('null', async client => { const graph = new Graph(client as any, 'graph'), - { data } = await graph.roQuery('RETURN null AS key'); + { data } = await graph.query('RETURN null AS key'); assert.deepEqual( data, @@ -15,7 +15,7 @@ describe('Graph', () => { testUtils.testWithClient('string', async client => { const graph = new Graph(client as any, 'graph'), - { data } = await graph.roQuery('RETURN "string" AS key'); + { data } = await graph.query('RETURN "string" AS key'); assert.deepEqual( data, @@ -25,7 +25,7 @@ describe('Graph', () => { testUtils.testWithClient('integer', async client => { const graph = new Graph(client as any, 'graph'), - { data } = await graph.roQuery('RETURN 0 AS key'); + { data } = await graph.query('RETURN 0 AS key'); assert.deepEqual( data, @@ -35,7 +35,7 @@ describe('Graph', () => { testUtils.testWithClient('boolean', async client => { const graph = new Graph(client as any, 'graph'), - { data } = await graph.roQuery('RETURN false AS key'); + { data } = await graph.query('RETURN false AS key'); assert.deepEqual( data, @@ -45,7 +45,7 @@ describe('Graph', () => { testUtils.testWithClient('double', async client => { const graph = new Graph(client as any, 'graph'), - { data } = await graph.roQuery('RETURN 0.1 AS key'); + { data } = await graph.query('RETURN 0.1 AS key'); assert.deepEqual( data, @@ -55,7 +55,7 @@ describe('Graph', () => { testUtils.testWithClient('array', async client => { const graph = new Graph(client as any, 'graph'), - { data } = await graph.roQuery('RETURN [null] AS key'); + { data } = await graph.query('RETURN [null] AS key'); assert.deepEqual( data, @@ -125,7 +125,7 @@ describe('Graph', () => { testUtils.testWithClient('map', async client => { const graph = new Graph(client as any, 'graph'), - { data } = await graph.roQuery('RETURN { key: "value" } AS map'); + { data } = await graph.query('RETURN { key: "value" } AS map'); assert.deepEqual(data, [{ map: { @@ -136,7 +136,7 @@ describe('Graph', () => { testUtils.testWithClient('point', async client => { const graph = new Graph(client as any, 'graph'), - { data } = await graph.roQuery('RETURN point({ latitude: 1, longitude: 2 }) AS point'); + { data } = await graph.query('RETURN point({ latitude: 1, longitude: 2 }) AS point'); assert.deepEqual(data, [{ point: { From 7cb467ad236b28ed4b6ad2ee283241685583f2fa Mon Sep 17 00:00:00 2001 From: Leibale Eidelman Date: Mon, 30 Jan 2023 14:24:11 -0500 Subject: [PATCH 1337/1748] fix #2398 - fix `v4` interface in `legacyMode` (#2402) --- packages/client/lib/client/index.spec.ts | 14 +++++++++++++- packages/client/lib/client/index.ts | 2 +- packages/client/lib/client/multi-command.ts | 2 +- 3 files changed, 15 insertions(+), 3 deletions(-) diff --git a/packages/client/lib/client/index.spec.ts b/packages/client/lib/client/index.spec.ts index 7af7f35d4d8..25c966c2719 100644 --- a/packages/client/lib/client/index.spec.ts +++ b/packages/client/lib/client/index.spec.ts @@ -2,7 +2,7 @@ import { strict as assert } from 'assert'; import testUtils, { GLOBAL, waitTillBeenCalled } from '../test-utils'; import RedisClient, { RedisClientType } from '.'; import { RedisClientMultiCommandType } from './multi-command'; -import { RedisCommandArguments, RedisCommandRawReply, RedisModules, RedisFunctions, RedisScripts } from '../commands'; +import { RedisCommandRawReply, RedisModules, RedisFunctions, RedisScripts } from '../commands'; import { AbortError, ClientClosedError, ClientOfflineError, ConnectionTimeoutError, DisconnectsClientError, SocketClosedUnexpectedlyError, WatchError } from '../errors'; import { defineScript } from '../lua-script'; import { spy } from 'sinon'; @@ -199,6 +199,18 @@ describe('Client', () => { } }); + testUtils.testWithClient('client.v4.{command} should return a promise', async client => { + assert.equal( + await client.v4.ping(), + 'PONG' + ); + }, { + ...GLOBAL.SERVERS.OPEN, + clientOptions: { + legacyMode: true + } + }); + testUtils.testWithClient('client.{command} should accept vardict arguments', async client => { assert.equal( await promisify(client.set).call(client, 'a', 'b'), diff --git a/packages/client/lib/client/index.ts b/packages/client/lib/client/index.ts index 6af572edc6a..b4bf49fc7bc 100644 --- a/packages/client/lib/client/index.ts +++ b/packages/client/lib/client/index.ts @@ -352,7 +352,7 @@ export default class RedisClient< for (const [ name, command ] of Object.entries(COMMANDS as RedisCommands)) { this.#defineLegacyCommand(name, command); - (this as any)[name.toLowerCase()] = (this as any)[name]; + (this as any)[name.toLowerCase()] ??= (this as any)[name]; } // hard coded commands diff --git a/packages/client/lib/client/multi-command.ts b/packages/client/lib/client/multi-command.ts index 4a3b668b758..749ab4c4e0f 100644 --- a/packages/client/lib/client/multi-command.ts +++ b/packages/client/lib/client/multi-command.ts @@ -119,7 +119,7 @@ export default class RedisClientMultiCommand { for (const [ name, command ] of Object.entries(COMMANDS as RedisCommands)) { this.#defineLegacyCommand(name, command); - (this as any)[name.toLowerCase()] = (this as any)[name]; + (this as any)[name.toLowerCase()] ??= (this as any)[name]; } } From e4229a40b65025981e38938edb2f2310b918fbdc Mon Sep 17 00:00:00 2001 From: Leibale Date: Mon, 30 Jan 2023 14:27:42 -0500 Subject: [PATCH 1338/1748] Release client@1.5.5 --- packages/client/package.json | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/packages/client/package.json b/packages/client/package.json index 918a8e97f14..d42c0d4b912 100644 --- a/packages/client/package.json +++ b/packages/client/package.json @@ -1,6 +1,6 @@ { "name": "@redis/client", - "version": "1.5.4", + "version": "1.5.5", "license": "MIT", "main": "./dist/index.js", "types": "./dist/index.d.ts", From 92092456111e11601c69851ae772b0968b03a03c Mon Sep 17 00:00:00 2001 From: Leibale Date: Mon, 30 Jan 2023 14:29:38 -0500 Subject: [PATCH 1339/1748] upgrade subpackages --- package-lock.json | 4 ++-- package.json | 2 +- 2 files changed, 3 insertions(+), 3 deletions(-) diff --git a/package-lock.json b/package-lock.json index 51947c86d38..bf03ace732e 100644 --- a/package-lock.json +++ b/package-lock.json @@ -13,7 +13,7 @@ ], "dependencies": { "@redis/bloom": "1.2.0", - "@redis/client": "1.5.3", + "@redis/client": "1.5.5", "@redis/graph": "1.1.0", "@redis/json": "1.0.4", "@redis/search": "1.1.1", @@ -8260,7 +8260,7 @@ }, "packages/client": { "name": "@redis/client", - "version": "1.5.4", + "version": "1.5.5", "license": "MIT", "dependencies": { "cluster-key-slot": "1.1.2", diff --git a/package.json b/package.json index fddfb80e2a3..59dc60942c0 100644 --- a/package.json +++ b/package.json @@ -24,7 +24,7 @@ }, "dependencies": { "@redis/bloom": "1.2.0", - "@redis/client": "1.5.4", + "@redis/client": "1.5.5", "@redis/graph": "1.1.0", "@redis/json": "1.0.4", "@redis/search": "1.1.1", From 9ffae592f1023cc1e675eb7ec25a21f4f03de66e Mon Sep 17 00:00:00 2001 From: Leibale Date: Mon, 30 Jan 2023 14:30:00 -0500 Subject: [PATCH 1340/1748] Release redis@4.6.4 --- package-lock.json | 4 ++-- package.json | 2 +- 2 files changed, 3 insertions(+), 3 deletions(-) diff --git a/package-lock.json b/package-lock.json index bf03ace732e..07ae856cfcc 100644 --- a/package-lock.json +++ b/package-lock.json @@ -1,12 +1,12 @@ { "name": "redis", - "version": "4.6.3", + "version": "4.6.4", "lockfileVersion": 3, "requires": true, "packages": { "": { "name": "redis", - "version": "4.6.3", + "version": "4.6.4", "license": "MIT", "workspaces": [ "./packages/*" diff --git a/package.json b/package.json index 59dc60942c0..630aeae156d 100644 --- a/package.json +++ b/package.json @@ -1,7 +1,7 @@ { "name": "redis", "description": "A modern, high performance Redis client", - "version": "4.6.3", + "version": "4.6.4", "license": "MIT", "main": "./dist/index.js", "types": "./dist/index.d.ts", From 1be84228b566a69243d1872d48c20669221dcaff Mon Sep 17 00:00:00 2001 From: Igor Malinovskiy Date: Tue, 21 Feb 2023 16:45:48 +0100 Subject: [PATCH 1341/1748] Remove redundant UNF sub-option in JSON example (#2418) UNF option is not required with JSON and makes the example harder to read and understand. --- examples/search-json.js | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/examples/search-json.js b/examples/search-json.js index 75de0221a1e..6481889ecfd 100644 --- a/examples/search-json.js +++ b/examples/search-json.js @@ -15,7 +15,7 @@ try { await client.ft.create('idx:users', { '$.name': { type: SchemaFieldTypes.TEXT, - SORTABLE: 'UNF' + SORTABLE: true }, '$.age': { type: SchemaFieldTypes.NUMERIC, From 0f28dad2a299df28db0b83785cbfa72c1130ea34 Mon Sep 17 00:00:00 2001 From: Mik13 Date: Fri, 24 Feb 2023 23:33:16 +0100 Subject: [PATCH 1342/1748] Execute empty MULTI (#2423) * Fix multi.exec with empty queue and previous watch When calling exec on a multi instance which you did not use, no command is sent currently. This is a problem for watched keys, because no EXEC means no unwatch, which might cause hard-to-debug problems. Proposed Fix: Sending UNWATCH * execute empty multi command (instead of skipping) * Update index.ts * Update index.ts * Update multi-command.ts * Update multi-command.ts * Update multi-command.ts * Update multi-command.ts * short circuit empty pipelines * Update index.ts --------- Co-authored-by: Leibale --- packages/client/lib/client/index.spec.ts | 25 +++++++++++++------- packages/client/lib/client/index.ts | 21 +++++++++++----- packages/client/lib/client/multi-command.ts | 7 +++--- packages/client/lib/cluster/multi-command.ts | 5 +--- packages/client/lib/multi-command.spec.ts | 21 ++++++++-------- packages/client/lib/multi-command.ts | 12 ---------- 6 files changed, 46 insertions(+), 45 deletions(-) diff --git a/packages/client/lib/client/index.spec.ts b/packages/client/lib/client/index.spec.ts index 25c966c2719..7396a2e9c37 100644 --- a/packages/client/lib/client/index.spec.ts +++ b/packages/client/lib/client/index.spec.ts @@ -518,14 +518,23 @@ describe('Client', () => { ); }, GLOBAL.SERVERS.OPEN); - testUtils.testWithClient('execAsPipeline', async client => { - assert.deepEqual( - await client.multi() - .ping() - .exec(true), - ['PONG'] - ); - }, GLOBAL.SERVERS.OPEN); + describe('execAsPipeline', () => { + testUtils.testWithClient('exec(true)', async client => { + assert.deepEqual( + await client.multi() + .ping() + .exec(true), + ['PONG'] + ); + }, GLOBAL.SERVERS.OPEN); + + testUtils.testWithClient('empty execAsPipeline', async client => { + assert.deepEqual( + await client.multi().execAsPipeline(), + [] + ); + }, GLOBAL.SERVERS.OPEN); + }); testUtils.testWithClient('should remember selected db', async client => { await client.multi() diff --git a/packages/client/lib/client/index.ts b/packages/client/lib/client/index.ts index b4bf49fc7bc..5dd386647ef 100644 --- a/packages/client/lib/client/index.ts +++ b/packages/client/lib/client/index.ts @@ -460,7 +460,7 @@ export default class RedisClient< ); } else if (!this.#socket.isReady && this.#options?.disableOfflineQueue) { return Promise.reject(new ClientOfflineError()); - } + } const promise = this.#queue.addCommand(args, options); this.#tick(); @@ -725,11 +725,14 @@ export default class RedisClient< return Promise.reject(new ClientClosedError()); } - const promise = Promise.all( - commands.map(({ args }) => { - return this.#queue.addCommand(args, { chainId }); - }) - ); + const promise = chainId ? + // if `chainId` has a value, it's a `MULTI` (and not "pipeline") - need to add the `MULTI` and `EXEC` commands + Promise.all([ + this.#queue.addCommand(['MULTI'], { chainId }), + this.#addMultiCommands(commands, chainId), + this.#queue.addCommand(['EXEC'], { chainId }) + ]) : + this.#addMultiCommands(commands); this.#tick(); @@ -742,6 +745,12 @@ export default class RedisClient< return results; } + #addMultiCommands(commands: Array, chainId?: symbol) { + return Promise.all( + commands.map(({ args }) => this.#queue.addCommand(args, { chainId })) + ); + } + async* scanIterator(options?: ScanCommandOptions): AsyncIterable { let cursor = 0; do { diff --git a/packages/client/lib/client/multi-command.ts b/packages/client/lib/client/multi-command.ts index 749ab4c4e0f..e347667bf2c 100644 --- a/packages/client/lib/client/multi-command.ts +++ b/packages/client/lib/client/multi-command.ts @@ -170,12 +170,9 @@ export default class RedisClientMultiCommand { return this.execAsPipeline(); } - const commands = this.#multi.exec(); - if (!commands) return []; - return this.#multi.handleExecReplies( await this.#executor( - commands, + this.#multi.queue, this.#selectedDB, RedisMultiCommand.generateChainId() ) @@ -185,6 +182,8 @@ export default class RedisClientMultiCommand { EXEC = this.exec; async execAsPipeline(): Promise> { + if (this.#multi.queue.length === 0) return []; + return this.#multi.transformReplies( await this.#executor( this.#multi.queue, diff --git a/packages/client/lib/cluster/multi-command.ts b/packages/client/lib/cluster/multi-command.ts index 52ab6eb0e23..ef3c7590ec7 100644 --- a/packages/client/lib/cluster/multi-command.ts +++ b/packages/client/lib/cluster/multi-command.ts @@ -120,11 +120,8 @@ export default class RedisClusterMultiCommand { return this.execAsPipeline(); } - const commands = this.#multi.exec(); - if (!commands) return []; - return this.#multi.handleExecReplies( - await this.#executor(commands, this.#firstKey, RedisMultiCommand.generateChainId()) + await this.#executor(this.#multi.queue, this.#firstKey, RedisMultiCommand.generateChainId()) ); } diff --git a/packages/client/lib/multi-command.spec.ts b/packages/client/lib/multi-command.spec.ts index 7e9667cd518..b0f79c6e157 100644 --- a/packages/client/lib/multi-command.spec.ts +++ b/packages/client/lib/multi-command.spec.ts @@ -46,24 +46,23 @@ describe('Multi Command', () => { }); describe('exec', () => { - it('undefined', () => { - assert.equal( - new RedisMultiCommand().exec(), - undefined + it('without commands', () => { + assert.deepEqual( + new RedisMultiCommand().queue, + [] ); }); - it('Array', () => { + it('with commands', () => { const multi = new RedisMultiCommand(); multi.addCommand(['PING']); assert.deepEqual( - multi.exec(), - [ - { args: ['MULTI'] }, - { args: ['PING'], transformReply: undefined }, - { args: ['EXEC'] } - ] + multi.queue, + [{ + args: ['PING'], + transformReply: undefined + }] ); }); }); diff --git a/packages/client/lib/multi-command.ts b/packages/client/lib/multi-command.ts index 8865cc8e002..08f23ffa454 100644 --- a/packages/client/lib/multi-command.ts +++ b/packages/client/lib/multi-command.ts @@ -69,18 +69,6 @@ export default class RedisMultiCommand { return transformedArguments; } - exec(): undefined | Array { - if (!this.queue.length) { - return; - } - - return [ - { args: ['MULTI'] }, - ...this.queue, - { args: ['EXEC'] } - ]; - } - handleExecReplies(rawReplies: Array): Array { const execReply = rawReplies[rawReplies.length - 1] as (null | Array); if (execReply === null) { From 26e057ebf9f76f665b20d75e58ec1fa85ad16be7 Mon Sep 17 00:00:00 2001 From: Leibale Eidelman Date: Fri, 24 Feb 2023 17:33:33 -0500 Subject: [PATCH 1343/1748] fix FT.SEARCH RETURN [] (#2421) * ref #2419 - fix FT.SEARCH RETURN [] * fix transformReply * fix PROFILE SEARCH as well * fix PROFILE SEARCH preserve * move preserve login to `pushSearchOptions` * attach preserve only if true * fix RETURN: [] test --- packages/search/lib/commands/PROFILE_SEARCH.ts | 6 +++--- packages/search/lib/commands/SEARCH.spec.ts | 8 ++++++-- packages/search/lib/commands/SEARCH.ts | 5 ++--- packages/search/lib/commands/index.ts | 4 ++++ 4 files changed, 15 insertions(+), 8 deletions(-) diff --git a/packages/search/lib/commands/PROFILE_SEARCH.ts b/packages/search/lib/commands/PROFILE_SEARCH.ts index 8040ccb5e05..94fba8a6a54 100644 --- a/packages/search/lib/commands/PROFILE_SEARCH.ts +++ b/packages/search/lib/commands/PROFILE_SEARCH.ts @@ -9,7 +9,7 @@ export function transformArguments( query: string, options?: ProfileOptions & SearchOptions ): RedisCommandArguments { - const args = ['FT.PROFILE', index, 'SEARCH']; + let args: RedisCommandArguments = ['FT.PROFILE', index, 'SEARCH']; if (options?.LIMITED) { args.push('LIMITED'); @@ -21,9 +21,9 @@ export function transformArguments( type ProfileSearchRawReply = ProfileRawReply; -export function transformReply(reply: ProfileSearchRawReply): ProfileReply { +export function transformReply(reply: ProfileSearchRawReply, withoutDocuments: boolean): ProfileReply { return { - results: transformSearchReply(reply[0]), + results: transformSearchReply(reply[0], withoutDocuments), profile: transformProfile(reply[1]) }; } diff --git a/packages/search/lib/commands/SEARCH.spec.ts b/packages/search/lib/commands/SEARCH.spec.ts index a5d8ae9e6c4..47c82cf892b 100644 --- a/packages/search/lib/commands/SEARCH.spec.ts +++ b/packages/search/lib/commands/SEARCH.spec.ts @@ -267,7 +267,8 @@ describe('SEARCH', () => { client.ft.create('index', { field: SchemaFieldTypes.NUMERIC }), - client.hSet('1', 'field', '1') + client.hSet('1', 'field', '1'), + client.hSet('2', 'field', '2') ]); assert.deepEqual( @@ -275,10 +276,13 @@ describe('SEARCH', () => { RETURN: [] }), { - total: 1, + total: 2, documents: [{ id: '1', value: Object.create(null) + }, { + id: '2', + value: Object.create(null) }] } ); diff --git a/packages/search/lib/commands/SEARCH.ts b/packages/search/lib/commands/SEARCH.ts index bed06e22c36..fbccb25058b 100644 --- a/packages/search/lib/commands/SEARCH.ts +++ b/packages/search/lib/commands/SEARCH.ts @@ -70,13 +70,13 @@ export function transformArguments( export type SearchRawReply = Array; -export function transformReply(reply: SearchRawReply): SearchReply { +export function transformReply(reply: SearchRawReply, withoutDocuments: boolean): SearchReply { const documents = []; let i = 1; while (i < reply.length) { documents.push({ id: reply[i++], - value: documentValue(reply[i++]) + value: withoutDocuments ? Object.create(null) : documentValue(reply[i++]) }); } @@ -88,7 +88,6 @@ export function transformReply(reply: SearchRawReply): SearchReply { function documentValue(tuples: any) { const message = Object.create(null); - if (tuples === undefined) return message; let i = 0; while (i < tuples.length) { diff --git a/packages/search/lib/commands/index.ts b/packages/search/lib/commands/index.ts index 74a49cb1bba..d56db7bdbbe 100644 --- a/packages/search/lib/commands/index.ts +++ b/packages/search/lib/commands/index.ts @@ -506,6 +506,10 @@ export function pushSearchOptions( args.push('DIALECT', options.DIALECT.toString()); } + if (options?.RETURN?.length === 0) { + args.preserve = true; + } + return args; } From 63e5228ce6341f79a8c17087b91f32d85d4c2ecf Mon Sep 17 00:00:00 2001 From: Leibale Eidelman Date: Fri, 24 Feb 2023 17:34:00 -0500 Subject: [PATCH 1344/1748] fix #2411 - export RedisFlushModes (#2425) --- packages/client/index.ts | 6 ++++-- 1 file changed, 4 insertions(+), 2 deletions(-) diff --git a/packages/client/index.ts b/packages/client/index.ts index 7e2d25f6d1b..8b21c5d5a32 100644 --- a/packages/client/index.ts +++ b/packages/client/index.ts @@ -15,8 +15,10 @@ export const createCluster = RedisCluster.create; export { defineScript } from './lib/lua-script'; +export * from './lib/errors'; + export { GeoReplyWith } from './lib/commands/generic-transformers'; -export * from './lib/errors'; +export { SetOptions } from './lib/commands/SET'; -export { SetOptions } from "./lib/commands/SET"; +export { RedisFlushModes } from './lib/commands/FLUSHALL'; From e95b258dca702621b8bcce59ae71e26734bd56bc Mon Sep 17 00:00:00 2001 From: Leibale Eidelman Date: Fri, 24 Feb 2023 17:34:19 -0500 Subject: [PATCH 1345/1748] fix #2419 - fix RESP2 array decoder in edge cases (#2424) --- packages/client/lib/client/RESP2/decoder.ts | 15 +++++++++------ 1 file changed, 9 insertions(+), 6 deletions(-) diff --git a/packages/client/lib/client/RESP2/decoder.ts b/packages/client/lib/client/RESP2/decoder.ts index e9d2317deab..525f118bf30 100644 --- a/packages/client/lib/client/RESP2/decoder.ts +++ b/packages/client/lib/client/RESP2/decoder.ts @@ -203,9 +203,9 @@ export default class RESP2Decoder { this.arrayItemType = undefined; if (length === -1) { - return this.returnArrayReply(null, arraysToKeep); + return this.returnArrayReply(null, arraysToKeep, chunk); } else if (length === 0) { - return this.returnArrayReply([], arraysToKeep); + return this.returnArrayReply([], arraysToKeep, chunk); } this.arraysInProcess.push({ @@ -235,20 +235,23 @@ export default class RESP2Decoder { } } - private returnArrayReply(reply: ArrayReply, arraysToKeep: number): ArrayReply | undefined { + private returnArrayReply(reply: ArrayReply, arraysToKeep: number, chunk?: Buffer): ArrayReply | undefined { if (this.arraysInProcess.length <= arraysToKeep) return reply; - return this.pushArrayItem(reply, arraysToKeep); + return this.pushArrayItem(reply, arraysToKeep, chunk); } - private pushArrayItem(item: Reply, arraysToKeep: number): ArrayReply | undefined { + private pushArrayItem(item: Reply, arraysToKeep: number, chunk?: Buffer): ArrayReply | undefined { const to = this.arraysInProcess[this.arraysInProcess.length - 1]!; to.array[to.pushCounter] = item; if (++to.pushCounter === to.array.length) { return this.returnArrayReply( this.arraysInProcess.pop()!.array, - arraysToKeep + arraysToKeep, + chunk ); + } else if (chunk && chunk.length > this.cursor) { + return this.parseArray(chunk, arraysToKeep); } } } From 4937efca6b8e7802088a39b00186fdf2f70ff7dd Mon Sep 17 00:00:00 2001 From: Leibale Eidelman Date: Fri, 24 Feb 2023 18:05:43 -0500 Subject: [PATCH 1346/1748] upgrade dependencies (#2426) --- package-lock.json | 414 +++++++++++++++--------------- package.json | 2 +- packages/bloom/package.json | 6 +- packages/client/package.json | 12 +- packages/graph/package.json | 6 +- packages/json/package.json | 6 +- packages/search/package.json | 6 +- packages/test-utils/package.json | 8 +- packages/time-series/package.json | 6 +- 9 files changed, 239 insertions(+), 227 deletions(-) diff --git a/package-lock.json b/package-lock.json index 07ae856cfcc..1bb78ea74ae 100644 --- a/package-lock.json +++ b/package-lock.json @@ -23,7 +23,7 @@ "@tsconfig/node14": "^1.0.3", "gh-pages": "^5.0.0", "release-it": "^15.6.0", - "typescript": "^4.9.4" + "typescript": "^4.9.5" } }, "node_modules/@ampproject/remapping": { @@ -52,30 +52,30 @@ } }, "node_modules/@babel/compat-data": { - "version": "7.20.10", - "resolved": "https://registry.npmjs.org/@babel/compat-data/-/compat-data-7.20.10.tgz", - "integrity": "sha512-sEnuDPpOJR/fcafHMjpcpGN5M2jbUGUHwmuWKM/YdPzeEDJg8bgmbcWQFUfE32MQjti1koACvoPVsDe8Uq+idg==", + "version": "7.21.0", + "resolved": "https://registry.npmjs.org/@babel/compat-data/-/compat-data-7.21.0.tgz", + "integrity": "sha512-gMuZsmsgxk/ENC3O/fRw5QY8A9/uxQbbCEypnLIiYYc/qVJtEV7ouxC3EllIIwNzMqAQee5tanFabWsUOutS7g==", "dev": true, "engines": { "node": ">=6.9.0" } }, "node_modules/@babel/core": { - "version": "7.20.12", - "resolved": "https://registry.npmjs.org/@babel/core/-/core-7.20.12.tgz", - "integrity": "sha512-XsMfHovsUYHFMdrIHkZphTN/2Hzzi78R08NuHfDBehym2VsPDL6Zn/JAD/JQdnRvbSsbQc4mVaU1m6JgtTEElg==", + "version": "7.21.0", + "resolved": "https://registry.npmjs.org/@babel/core/-/core-7.21.0.tgz", + "integrity": "sha512-PuxUbxcW6ZYe656yL3EAhpy7qXKq0DmYsrJLpbB8XrsCP9Nm+XCg9XFMb5vIDliPD7+U/+M+QJlH17XOcB7eXA==", "dev": true, "dependencies": { - "@ampproject/remapping": "^2.1.0", + "@ampproject/remapping": "^2.2.0", "@babel/code-frame": "^7.18.6", - "@babel/generator": "^7.20.7", + "@babel/generator": "^7.21.0", "@babel/helper-compilation-targets": "^7.20.7", - "@babel/helper-module-transforms": "^7.20.11", - "@babel/helpers": "^7.20.7", - "@babel/parser": "^7.20.7", + "@babel/helper-module-transforms": "^7.21.0", + "@babel/helpers": "^7.21.0", + "@babel/parser": "^7.21.0", "@babel/template": "^7.20.7", - "@babel/traverse": "^7.20.12", - "@babel/types": "^7.20.7", + "@babel/traverse": "^7.21.0", + "@babel/types": "^7.21.0", "convert-source-map": "^1.7.0", "debug": "^4.1.0", "gensync": "^1.0.0-beta.2", @@ -91,13 +91,14 @@ } }, "node_modules/@babel/generator": { - "version": "7.20.7", - "resolved": "https://registry.npmjs.org/@babel/generator/-/generator-7.20.7.tgz", - "integrity": "sha512-7wqMOJq8doJMZmP4ApXTzLxSr7+oO2jroJURrVEp6XShrQUObV8Tq/D0NCcoYg2uHqUrjzO0zwBjoYzelxK+sw==", + "version": "7.21.1", + "resolved": "https://registry.npmjs.org/@babel/generator/-/generator-7.21.1.tgz", + "integrity": "sha512-1lT45bAYlQhFn/BHivJs43AiW2rg3/UbLyShGfF3C0KmHvO5fSghWd5kBJy30kpRRucGzXStvnnCFniCR2kXAA==", "dev": true, "dependencies": { - "@babel/types": "^7.20.7", + "@babel/types": "^7.21.0", "@jridgewell/gen-mapping": "^0.3.2", + "@jridgewell/trace-mapping": "^0.3.17", "jsesc": "^2.5.1" }, "engines": { @@ -147,13 +148,13 @@ } }, "node_modules/@babel/helper-function-name": { - "version": "7.19.0", - "resolved": "https://registry.npmjs.org/@babel/helper-function-name/-/helper-function-name-7.19.0.tgz", - "integrity": "sha512-WAwHBINyrpqywkUH0nTnNgI5ina5TFn85HKS0pbPDfxFfhyR/aNQEn4hGi1P1JyT//I0t4OgXUlofzWILRvS5w==", + "version": "7.21.0", + "resolved": "https://registry.npmjs.org/@babel/helper-function-name/-/helper-function-name-7.21.0.tgz", + "integrity": "sha512-HfK1aMRanKHpxemaY2gqBmL04iAPOPRj7DxtNbiDOrJK+gdwkiNRVpCpUJYbUT+aZyemKN8brqTOxzCaG6ExRg==", "dev": true, "dependencies": { - "@babel/template": "^7.18.10", - "@babel/types": "^7.19.0" + "@babel/template": "^7.20.7", + "@babel/types": "^7.21.0" }, "engines": { "node": ">=6.9.0" @@ -184,9 +185,9 @@ } }, "node_modules/@babel/helper-module-transforms": { - "version": "7.20.11", - "resolved": "https://registry.npmjs.org/@babel/helper-module-transforms/-/helper-module-transforms-7.20.11.tgz", - "integrity": "sha512-uRy78kN4psmji1s2QtbtcCSaj/LILFDp0f/ymhpQH5QY3nljUZCaNWz9X1dEj/8MBdBEFECs7yRhKn8i7NjZgg==", + "version": "7.21.2", + "resolved": "https://registry.npmjs.org/@babel/helper-module-transforms/-/helper-module-transforms-7.21.2.tgz", + "integrity": "sha512-79yj2AR4U/Oqq/WOV7Lx6hUjau1Zfo4cI+JLAVYeMV5XIlbOhmjEk5ulbTc9fMpmlojzZHkUUxAiK+UKn+hNQQ==", "dev": true, "dependencies": { "@babel/helper-environment-visitor": "^7.18.9", @@ -195,8 +196,8 @@ "@babel/helper-split-export-declaration": "^7.18.6", "@babel/helper-validator-identifier": "^7.19.1", "@babel/template": "^7.20.7", - "@babel/traverse": "^7.20.10", - "@babel/types": "^7.20.7" + "@babel/traverse": "^7.21.2", + "@babel/types": "^7.21.2" }, "engines": { "node": ">=6.9.0" @@ -245,23 +246,23 @@ } }, "node_modules/@babel/helper-validator-option": { - "version": "7.18.6", - "resolved": "https://registry.npmjs.org/@babel/helper-validator-option/-/helper-validator-option-7.18.6.tgz", - "integrity": "sha512-XO7gESt5ouv/LRJdrVjkShckw6STTaB7l9BrpBaAHDeF5YZT+01PCwmR0SJHnkW6i8OwW/EVWRShfi4j2x+KQw==", + "version": "7.21.0", + "resolved": "https://registry.npmjs.org/@babel/helper-validator-option/-/helper-validator-option-7.21.0.tgz", + "integrity": "sha512-rmL/B8/f0mKS2baE9ZpyTcTavvEuWhTTW8amjzXNvYG4AwBsqTLikfXsEofsJEfKHf+HQVQbFOHy6o+4cnC/fQ==", "dev": true, "engines": { "node": ">=6.9.0" } }, "node_modules/@babel/helpers": { - "version": "7.20.13", - "resolved": "https://registry.npmjs.org/@babel/helpers/-/helpers-7.20.13.tgz", - "integrity": "sha512-nzJ0DWCL3gB5RCXbUO3KIMMsBY2Eqbx8mBpKGE/02PgyRQFcPQLbkQ1vyy596mZLaP+dAfD+R4ckASzNVmW3jg==", + "version": "7.21.0", + "resolved": "https://registry.npmjs.org/@babel/helpers/-/helpers-7.21.0.tgz", + "integrity": "sha512-XXve0CBtOW0pd7MRzzmoyuSj0e3SEzj8pgyFxnTT1NJZL38BD1MK7yYrm8yefRPIDvNNe14xR4FdbHwpInD4rA==", "dev": true, "dependencies": { "@babel/template": "^7.20.7", - "@babel/traverse": "^7.20.13", - "@babel/types": "^7.20.7" + "@babel/traverse": "^7.21.0", + "@babel/types": "^7.21.0" }, "engines": { "node": ">=6.9.0" @@ -353,9 +354,9 @@ } }, "node_modules/@babel/parser": { - "version": "7.20.13", - "resolved": "https://registry.npmjs.org/@babel/parser/-/parser-7.20.13.tgz", - "integrity": "sha512-gFDLKMfpiXCsjt4za2JA9oTMn70CeseCehb11kRZgvd7+F67Hih3OHOK24cRrWECJ/ljfPGac6ygXAs/C8kIvw==", + "version": "7.21.2", + "resolved": "https://registry.npmjs.org/@babel/parser/-/parser-7.21.2.tgz", + "integrity": "sha512-URpaIJQwEkEC2T9Kn+Ai6Xe/02iNaVCuT/PtoRz3GPVJVDpPd7mLo+VddTbhCRU9TXqW5mSrQfXZyi8kDKOVpQ==", "dev": true, "bin": { "parser": "bin/babel-parser.js" @@ -379,19 +380,19 @@ } }, "node_modules/@babel/traverse": { - "version": "7.20.13", - "resolved": "https://registry.npmjs.org/@babel/traverse/-/traverse-7.20.13.tgz", - "integrity": "sha512-kMJXfF0T6DIS9E8cgdLCSAL+cuCK+YEZHWiLK0SXpTo8YRj5lpJu3CDNKiIBCne4m9hhTIqUg6SYTAI39tAiVQ==", + "version": "7.21.2", + "resolved": "https://registry.npmjs.org/@babel/traverse/-/traverse-7.21.2.tgz", + "integrity": "sha512-ts5FFU/dSUPS13tv8XiEObDu9K+iagEKME9kAbaP7r0Y9KtZJZ+NGndDvWoRAYNpeWafbpFeki3q9QoMD6gxyw==", "dev": true, "dependencies": { "@babel/code-frame": "^7.18.6", - "@babel/generator": "^7.20.7", + "@babel/generator": "^7.21.1", "@babel/helper-environment-visitor": "^7.18.9", - "@babel/helper-function-name": "^7.19.0", + "@babel/helper-function-name": "^7.21.0", "@babel/helper-hoist-variables": "^7.18.6", "@babel/helper-split-export-declaration": "^7.18.6", - "@babel/parser": "^7.20.13", - "@babel/types": "^7.20.7", + "@babel/parser": "^7.21.2", + "@babel/types": "^7.21.2", "debug": "^4.1.0", "globals": "^11.1.0" }, @@ -409,9 +410,9 @@ } }, "node_modules/@babel/types": { - "version": "7.20.7", - "resolved": "https://registry.npmjs.org/@babel/types/-/types-7.20.7.tgz", - "integrity": "sha512-69OnhBxSSgK0OzTJai4kyPDiKTIe3j+ctaHdIGVbRahTLAT7L3R9oeXHC2aVSuGYt3cVnoAMDmOCgJ2yaiLMvg==", + "version": "7.21.2", + "resolved": "https://registry.npmjs.org/@babel/types/-/types-7.21.2.tgz", + "integrity": "sha512-3wRZSs7jiFaB8AjxiiD+VqN5DTG2iRvJGQ+qYFrs/654lg6kGTQWIOFjlBo5RaXuAZjBmP3+OQH4dmhqiiyYxw==", "dev": true, "dependencies": { "@babel/helper-string-parser": "^7.19.4", @@ -825,9 +826,9 @@ } }, "node_modules/@octokit/request/node_modules/node-fetch": { - "version": "2.6.8", - "resolved": "https://registry.npmjs.org/node-fetch/-/node-fetch-2.6.8.tgz", - "integrity": "sha512-RZ6dBYuj8dRSfxpUSu+NsdF1dpPpluJxwOp+6IoDp/sH2QNDSvurYsAa+F1WxY2RjA1iP93xhcsUoYbF2XBqVg==", + "version": "2.6.9", + "resolved": "https://registry.npmjs.org/node-fetch/-/node-fetch-2.6.9.tgz", + "integrity": "sha512-DJm/CJkZkRjKKj4Zi4BsKVZh3ValV5IR5s7LVZnW+6YMh0W1BfNA8XSs6DLMGYlId5F3KnA70uu2qepcR08Qqg==", "dev": true, "dependencies": { "whatwg-url": "^5.0.0" @@ -1032,9 +1033,9 @@ "dev": true }, "node_modules/@types/node": { - "version": "18.11.18", - "resolved": "https://registry.npmjs.org/@types/node/-/node-18.11.18.tgz", - "integrity": "sha512-DHQpWGjyQKSHj3ebjFI/wRKcqQcdR+MoFBygntYOZytCqNfkd2ZC4ARDJ2DQqhjH5p85Nnd3jhUJIXrszFX/JA==", + "version": "18.14.1", + "resolved": "https://registry.npmjs.org/@types/node/-/node-18.14.1.tgz", + "integrity": "sha512-QH+37Qds3E0eDlReeboBxfHbX9omAcBCXEzswCu6jySP642jiM3cYSIkU/REqwhCUqXdonHFuBfJDiAJxMNhaQ==", "dev": true }, "node_modules/@types/semver": { @@ -1065,9 +1066,9 @@ "dev": true }, "node_modules/@types/yargs": { - "version": "17.0.20", - "resolved": "https://registry.npmjs.org/@types/yargs/-/yargs-17.0.20.tgz", - "integrity": "sha512-eknWrTHofQuPk2iuqDm1waA7V6xPlbgBoaaXEgYkClhLOnB0TtbW+srJaOToAgawPxPlHQzwypFA2bhZaUGP5A==", + "version": "17.0.22", + "resolved": "https://registry.npmjs.org/@types/yargs/-/yargs-17.0.22.tgz", + "integrity": "sha512-pet5WJ9U8yPVRhkwuEIp5ktAeAqRZOq4UdAyWLWzxbtpyXnzbtLdKiXAjJzi/KLmPGS9wk86lUFWZFN6sISo4g==", "dev": true, "dependencies": { "@types/yargs-parser": "*" @@ -1080,15 +1081,16 @@ "dev": true }, "node_modules/@typescript-eslint/eslint-plugin": { - "version": "5.49.0", - "resolved": "https://registry.npmjs.org/@typescript-eslint/eslint-plugin/-/eslint-plugin-5.49.0.tgz", - "integrity": "sha512-IhxabIpcf++TBaBa1h7jtOWyon80SXPRLDq0dVz5SLFC/eW6tofkw/O7Ar3lkx5z5U6wzbKDrl2larprp5kk5Q==", + "version": "5.53.0", + "resolved": "https://registry.npmjs.org/@typescript-eslint/eslint-plugin/-/eslint-plugin-5.53.0.tgz", + "integrity": "sha512-alFpFWNucPLdUOySmXCJpzr6HKC3bu7XooShWM+3w/EL6J2HIoB2PFxpLnq4JauWVk6DiVeNKzQlFEaE+X9sGw==", "dev": true, "dependencies": { - "@typescript-eslint/scope-manager": "5.49.0", - "@typescript-eslint/type-utils": "5.49.0", - "@typescript-eslint/utils": "5.49.0", + "@typescript-eslint/scope-manager": "5.53.0", + "@typescript-eslint/type-utils": "5.53.0", + "@typescript-eslint/utils": "5.53.0", "debug": "^4.3.4", + "grapheme-splitter": "^1.0.4", "ignore": "^5.2.0", "natural-compare-lite": "^1.4.0", "regexpp": "^3.2.0", @@ -1146,14 +1148,14 @@ "dev": true }, "node_modules/@typescript-eslint/parser": { - "version": "5.49.0", - "resolved": "https://registry.npmjs.org/@typescript-eslint/parser/-/parser-5.49.0.tgz", - "integrity": "sha512-veDlZN9mUhGqU31Qiv2qEp+XrJj5fgZpJ8PW30sHU+j/8/e5ruAhLaVDAeznS7A7i4ucb/s8IozpDtt9NqCkZg==", + "version": "5.53.0", + "resolved": "https://registry.npmjs.org/@typescript-eslint/parser/-/parser-5.53.0.tgz", + "integrity": "sha512-MKBw9i0DLYlmdOb3Oq/526+al20AJZpANdT6Ct9ffxcV8nKCHz63t/S0IhlTFNsBIHJv+GY5SFJ0XfqVeydQrQ==", "dev": true, "dependencies": { - "@typescript-eslint/scope-manager": "5.49.0", - "@typescript-eslint/types": "5.49.0", - "@typescript-eslint/typescript-estree": "5.49.0", + "@typescript-eslint/scope-manager": "5.53.0", + "@typescript-eslint/types": "5.53.0", + "@typescript-eslint/typescript-estree": "5.53.0", "debug": "^4.3.4" }, "engines": { @@ -1173,13 +1175,13 @@ } }, "node_modules/@typescript-eslint/scope-manager": { - "version": "5.49.0", - "resolved": "https://registry.npmjs.org/@typescript-eslint/scope-manager/-/scope-manager-5.49.0.tgz", - "integrity": "sha512-clpROBOiMIzpbWNxCe1xDK14uPZh35u4QaZO1GddilEzoCLAEz4szb51rBpdgurs5k2YzPtJeTEN3qVbG+LRUQ==", + "version": "5.53.0", + "resolved": "https://registry.npmjs.org/@typescript-eslint/scope-manager/-/scope-manager-5.53.0.tgz", + "integrity": "sha512-Opy3dqNsp/9kBBeCPhkCNR7fmdSQqA+47r21hr9a14Bx0xnkElEQmhoHga+VoaoQ6uDHjDKmQPIYcUcKJifS7w==", "dev": true, "dependencies": { - "@typescript-eslint/types": "5.49.0", - "@typescript-eslint/visitor-keys": "5.49.0" + "@typescript-eslint/types": "5.53.0", + "@typescript-eslint/visitor-keys": "5.53.0" }, "engines": { "node": "^12.22.0 || ^14.17.0 || >=16.0.0" @@ -1190,13 +1192,13 @@ } }, "node_modules/@typescript-eslint/type-utils": { - "version": "5.49.0", - "resolved": "https://registry.npmjs.org/@typescript-eslint/type-utils/-/type-utils-5.49.0.tgz", - "integrity": "sha512-eUgLTYq0tR0FGU5g1YHm4rt5H/+V2IPVkP0cBmbhRyEmyGe4XvJ2YJ6sYTmONfjmdMqyMLad7SB8GvblbeESZA==", + "version": "5.53.0", + "resolved": "https://registry.npmjs.org/@typescript-eslint/type-utils/-/type-utils-5.53.0.tgz", + "integrity": "sha512-HO2hh0fmtqNLzTAme/KnND5uFNwbsdYhCZghK2SoxGp3Ifn2emv+hi0PBUjzzSh0dstUIFqOj3bp0AwQlK4OWw==", "dev": true, "dependencies": { - "@typescript-eslint/typescript-estree": "5.49.0", - "@typescript-eslint/utils": "5.49.0", + "@typescript-eslint/typescript-estree": "5.53.0", + "@typescript-eslint/utils": "5.53.0", "debug": "^4.3.4", "tsutils": "^3.21.0" }, @@ -1217,9 +1219,9 @@ } }, "node_modules/@typescript-eslint/types": { - "version": "5.49.0", - "resolved": "https://registry.npmjs.org/@typescript-eslint/types/-/types-5.49.0.tgz", - "integrity": "sha512-7If46kusG+sSnEpu0yOz2xFv5nRz158nzEXnJFCGVEHWnuzolXKwrH5Bsf9zsNlOQkyZuk0BZKKoJQI+1JPBBg==", + "version": "5.53.0", + "resolved": "https://registry.npmjs.org/@typescript-eslint/types/-/types-5.53.0.tgz", + "integrity": "sha512-5kcDL9ZUIP756K6+QOAfPkigJmCPHcLN7Zjdz76lQWWDdzfOhZDTj1irs6gPBKiXx5/6O3L0+AvupAut3z7D2A==", "dev": true, "engines": { "node": "^12.22.0 || ^14.17.0 || >=16.0.0" @@ -1230,13 +1232,13 @@ } }, "node_modules/@typescript-eslint/typescript-estree": { - "version": "5.49.0", - "resolved": "https://registry.npmjs.org/@typescript-eslint/typescript-estree/-/typescript-estree-5.49.0.tgz", - "integrity": "sha512-PBdx+V7deZT/3GjNYPVQv1Nc0U46dAHbIuOG8AZ3on3vuEKiPDwFE/lG1snN2eUB9IhF7EyF7K1hmTcLztNIsA==", + "version": "5.53.0", + "resolved": "https://registry.npmjs.org/@typescript-eslint/typescript-estree/-/typescript-estree-5.53.0.tgz", + "integrity": "sha512-eKmipH7QyScpHSkhbptBBYh9v8FxtngLquq292YTEQ1pxVs39yFBlLC1xeIZcPPz1RWGqb7YgERJRGkjw8ZV7w==", "dev": true, "dependencies": { - "@typescript-eslint/types": "5.49.0", - "@typescript-eslint/visitor-keys": "5.49.0", + "@typescript-eslint/types": "5.53.0", + "@typescript-eslint/visitor-keys": "5.53.0", "debug": "^4.3.4", "globby": "^11.1.0", "is-glob": "^4.0.3", @@ -1319,16 +1321,16 @@ "dev": true }, "node_modules/@typescript-eslint/utils": { - "version": "5.49.0", - "resolved": "https://registry.npmjs.org/@typescript-eslint/utils/-/utils-5.49.0.tgz", - "integrity": "sha512-cPJue/4Si25FViIb74sHCLtM4nTSBXtLx1d3/QT6mirQ/c65bV8arBEebBJJizfq8W2YyMoPI/WWPFWitmNqnQ==", + "version": "5.53.0", + "resolved": "https://registry.npmjs.org/@typescript-eslint/utils/-/utils-5.53.0.tgz", + "integrity": "sha512-VUOOtPv27UNWLxFwQK/8+7kvxVC+hPHNsJjzlJyotlaHjLSIgOCKj9I0DBUjwOOA64qjBwx5afAPjksqOxMO0g==", "dev": true, "dependencies": { "@types/json-schema": "^7.0.9", "@types/semver": "^7.3.12", - "@typescript-eslint/scope-manager": "5.49.0", - "@typescript-eslint/types": "5.49.0", - "@typescript-eslint/typescript-estree": "5.49.0", + "@typescript-eslint/scope-manager": "5.53.0", + "@typescript-eslint/types": "5.53.0", + "@typescript-eslint/typescript-estree": "5.53.0", "eslint-scope": "^5.1.1", "eslint-utils": "^3.0.0", "semver": "^7.3.7" @@ -1378,12 +1380,12 @@ "dev": true }, "node_modules/@typescript-eslint/visitor-keys": { - "version": "5.49.0", - "resolved": "https://registry.npmjs.org/@typescript-eslint/visitor-keys/-/visitor-keys-5.49.0.tgz", - "integrity": "sha512-v9jBMjpNWyn8B6k/Mjt6VbUS4J1GvUlR4x3Y+ibnP1z7y7V4n0WRz+50DY6+Myj0UaXVSuUlHohO+eZ8IJEnkg==", + "version": "5.53.0", + "resolved": "https://registry.npmjs.org/@typescript-eslint/visitor-keys/-/visitor-keys-5.53.0.tgz", + "integrity": "sha512-JqNLnX3leaHFZEN0gCh81sIvgrp/2GOACZNgO4+Tkf64u51kTpAyWFOY8XHx8XuXr3N2C9zgPPHtcpMg6z1g0w==", "dev": true, "dependencies": { - "@typescript-eslint/types": "5.49.0", + "@typescript-eslint/types": "5.53.0", "eslint-visitor-keys": "^3.3.0" }, "engines": { @@ -1551,6 +1553,12 @@ "url": "https://github.com/chalk/ansi-regex?sponsor=1" } }, + "node_modules/ansi-sequence-parser": { + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/ansi-sequence-parser/-/ansi-sequence-parser-1.1.0.tgz", + "integrity": "sha512-lEm8mt52to2fT8GhciPCGeCXACSz2UwIN4X2e2LJSnZ5uAbn2/dsYdOmUXq0AtWS5cpAupysIneExOgH0Vd2TQ==", + "dev": true + }, "node_modules/ansi-styles": { "version": "6.2.1", "resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-6.2.1.tgz", @@ -1812,9 +1820,9 @@ "dev": true }, "node_modules/browserslist": { - "version": "4.21.4", - "resolved": "https://registry.npmjs.org/browserslist/-/browserslist-4.21.4.tgz", - "integrity": "sha512-CBHJJdDmgjl3daYjN5Cp5kbTf1mUhZoS+beLklHIvkOWscs83YAhLlF3Wsh/lciQYAcbBJgTOD44VtG31ZM4Hw==", + "version": "4.21.5", + "resolved": "https://registry.npmjs.org/browserslist/-/browserslist-4.21.5.tgz", + "integrity": "sha512-tUkiguQGW7S3IhB7N+c2MV/HZPSCPAAiYBZXLsBhFB/PCy6ZKKsZrmBayHV9fdGV/ARIfJ14NkxKzRDjvp7L6w==", "dev": true, "funding": [ { @@ -1827,10 +1835,10 @@ } ], "dependencies": { - "caniuse-lite": "^1.0.30001400", - "electron-to-chromium": "^1.4.251", - "node-releases": "^2.0.6", - "update-browserslist-db": "^1.0.9" + "caniuse-lite": "^1.0.30001449", + "electron-to-chromium": "^1.4.284", + "node-releases": "^2.0.8", + "update-browserslist-db": "^1.0.10" }, "bin": { "browserslist": "cli.js" @@ -1888,14 +1896,14 @@ } }, "node_modules/cacheable-request": { - "version": "10.2.5", - "resolved": "https://registry.npmjs.org/cacheable-request/-/cacheable-request-10.2.5.tgz", - "integrity": "sha512-5RwYYCfzjNPsyJxb/QpaM0bfzx+kw5/YpDhZPm9oMIDntHFQ9YXeyV47ZvzlTE0XrrrbyO2UITJH4GF9eRLdXQ==", + "version": "10.2.8", + "resolved": "https://registry.npmjs.org/cacheable-request/-/cacheable-request-10.2.8.tgz", + "integrity": "sha512-IDVO5MJ4LItE6HKFQTqT2ocAQsisOoCTUDu1ddCmnhyiwFQjXNPp4081Xj23N4tO+AFEFNzGuNEf/c8Gwwt15A==", "dev": true, "dependencies": { "@types/http-cache-semantics": "^4.0.1", "get-stream": "^6.0.1", - "http-cache-semantics": "^4.1.0", + "http-cache-semantics": "^4.1.1", "keyv": "^4.5.2", "mimic-response": "^4.0.0", "normalize-url": "^8.0.0", @@ -1952,9 +1960,9 @@ } }, "node_modules/caniuse-lite": { - "version": "1.0.30001448", - "resolved": "https://registry.npmjs.org/caniuse-lite/-/caniuse-lite-1.0.30001448.tgz", - "integrity": "sha512-tq2YI+MJnooG96XpbTRYkBxLxklZPOdLmNIOdIhvf7SNJan6u5vCKum8iT7ZfCt70m1GPkuC7P3TtX6UuhupuA==", + "version": "1.0.30001457", + "resolved": "https://registry.npmjs.org/caniuse-lite/-/caniuse-lite-1.0.30001457.tgz", + "integrity": "sha512-SDIV6bgE1aVbK6XyxdURbUE89zY7+k1BBBaOwYwkNCglXlel/E7mELiHC64HQ+W0xSKlqWhV9Wh7iHxUjMs4fA==", "dev": true, "funding": [ { @@ -2025,9 +2033,9 @@ } }, "node_modules/ci-info": { - "version": "3.7.1", - "resolved": "https://registry.npmjs.org/ci-info/-/ci-info-3.7.1.tgz", - "integrity": "sha512-4jYS4MOAaCIStSRwiuxc4B8MYhIe676yO1sYGzARnjXkWpmzZMMYxY6zu8WYWDhSuth5zhrQ1rhNSibyyvv4/w==", + "version": "3.8.0", + "resolved": "https://registry.npmjs.org/ci-info/-/ci-info-3.8.0.tgz", + "integrity": "sha512-eXTggHWSooYhq49F2opQhuHWgzucfF2YgODK4e1566GQs5BIfP30B0oenwBJHfWxAs2fyPB1s7Mg949zLf61Yw==", "dev": true, "funding": [ { @@ -2462,9 +2470,9 @@ } }, "node_modules/define-properties": { - "version": "1.1.4", - "resolved": "https://registry.npmjs.org/define-properties/-/define-properties-1.1.4.tgz", - "integrity": "sha512-uckOqKcfaVvtBdsVkdPv3XjveQJsNQqmhXgRi8uhvWWuPYZCNlzT8qAyblUgNoXdHdjMTzAqeGjAoli8f+bzPA==", + "version": "1.2.0", + "resolved": "https://registry.npmjs.org/define-properties/-/define-properties-1.2.0.tgz", + "integrity": "sha512-xvqAVKGfT1+UAvPwKTVw/njhdQ8ZhXK4lI0bCIuCMrp2up9nPnaDftrLtmpTazqd1o+UY4zgzU+avtMbDP+ldA==", "dev": true, "dependencies": { "has-property-descriptors": "^1.0.0", @@ -2562,9 +2570,9 @@ "dev": true }, "node_modules/electron-to-chromium": { - "version": "1.4.284", - "resolved": "https://registry.npmjs.org/electron-to-chromium/-/electron-to-chromium-1.4.284.tgz", - "integrity": "sha512-M8WEXFuKXMYMVr45fo8mq0wUrrJHheiKZf6BArTKk9ZBYCKJEOU5H8cdWgDT+qCVZf7Na4lVUaZsA+h6uA9+PA==", + "version": "1.4.311", + "resolved": "https://registry.npmjs.org/electron-to-chromium/-/electron-to-chromium-1.4.311.tgz", + "integrity": "sha512-RoDlZufvrtr2Nx3Yx5MB8jX3aHIxm8nRWPJm3yVvyHmyKaRvn90RjzB6hNnt0AkhS3IInJdyRfQb4mWhPvUjVw==", "dev": true }, "node_modules/email-addresses": { @@ -2805,9 +2813,9 @@ } }, "node_modules/eslint": { - "version": "8.32.0", - "resolved": "https://registry.npmjs.org/eslint/-/eslint-8.32.0.tgz", - "integrity": "sha512-nETVXpnthqKPFyuY2FNjz/bEd6nbosRgKbkgS/y1C7LJop96gYHWpiguLecMHQ2XCPxn77DS0P+68WzG6vkZSQ==", + "version": "8.34.0", + "resolved": "https://registry.npmjs.org/eslint/-/eslint-8.34.0.tgz", + "integrity": "sha512-1Z8iFsucw+7kSqXNZVslXS8Ioa4u2KM7GPwuKtkTFAqZ/cHMcEaR+1+Br0wLlot49cNxIiZk5wp8EAbPcYZxTg==", "dev": true, "dependencies": { "@eslint/eslintrc": "^1.4.1", @@ -3087,9 +3095,9 @@ } }, "node_modules/esquery": { - "version": "1.4.0", - "resolved": "https://registry.npmjs.org/esquery/-/esquery-1.4.0.tgz", - "integrity": "sha512-cCDispWt5vHHtwMY2YrAQ4ibFkAL8RbH5YGBnZBc90MolvvfkkQcJro/aZiAQUlQ3qgrYS6D6v8Gc5G5CQsc9w==", + "version": "1.4.2", + "resolved": "https://registry.npmjs.org/esquery/-/esquery-1.4.2.tgz", + "integrity": "sha512-JVSoLdTlTDkmjFmab7H/9SL9qGSyjElT3myyKp7krqjVFQCDLmj1QFaCLRFBszBKI0XVZaiiXvuPIX3ZwHe1Ng==", "dev": true, "dependencies": { "estraverse": "^5.1.0" @@ -3753,9 +3761,9 @@ } }, "node_modules/globals": { - "version": "13.19.0", - "resolved": "https://registry.npmjs.org/globals/-/globals-13.19.0.tgz", - "integrity": "sha512-dkQ957uSRWHw7CFXLUtUHQI3g3aWApYhfNR2O6jn/907riyTYKVBmxYVROkBcY614FSSeSJh7Xm7SrUWCxvJMQ==", + "version": "13.20.0", + "resolved": "https://registry.npmjs.org/globals/-/globals-13.20.0.tgz", + "integrity": "sha512-Qg5QtVkCy/kv3FUSlu4ukeZDVf9ee0iXLAUYX13gbR17bnejFTzr4iS9bY7kwCf1NztRNm1t91fjOiyx4CSwPQ==", "dev": true, "dependencies": { "type-fest": "^0.20.2" @@ -4005,9 +4013,9 @@ "dev": true }, "node_modules/http-cache-semantics": { - "version": "4.1.0", - "resolved": "https://registry.npmjs.org/http-cache-semantics/-/http-cache-semantics-4.1.0.tgz", - "integrity": "sha512-carPklcUh7ROWRK7Cv27RPtdhYhUsela/ue5/jKzjegVvXDqM2ILE9Q2BGn9JZJh1g87cp56su/FgQSzcWS8cQ==", + "version": "4.1.1", + "resolved": "https://registry.npmjs.org/http-cache-semantics/-/http-cache-semantics-4.1.1.tgz", + "integrity": "sha512-er295DKPVsV82j5kw1Gjt+ADA/XYHsajl82cGNQG2eyoPkvgUhX+nDIyelzhIWbbsXP39EHcI6l5tYs2FYqYXQ==", "dev": true }, "node_modules/http-errors": { @@ -4211,12 +4219,12 @@ } }, "node_modules/internal-slot": { - "version": "1.0.4", - "resolved": "https://registry.npmjs.org/internal-slot/-/internal-slot-1.0.4.tgz", - "integrity": "sha512-tA8URYccNzMo94s5MQZgH8NB/XTa6HsOo0MLfXTKKEnHVVdegzaQoFZ7Jp44bdvLvY2waT5dc+j5ICEswhi7UQ==", + "version": "1.0.5", + "resolved": "https://registry.npmjs.org/internal-slot/-/internal-slot-1.0.5.tgz", + "integrity": "sha512-Y+R5hJrzs52QCG2laLn4udYVnxsfny9CpOhNhUvk/SSSVyF6T27FzRbF0sroPidSu3X8oEAkOn2K804mjpt6UQ==", "dev": true, "dependencies": { - "get-intrinsic": "^1.1.3", + "get-intrinsic": "^1.2.0", "has": "^1.0.3", "side-channel": "^1.0.4" }, @@ -5177,9 +5185,9 @@ } }, "node_modules/minimist": { - "version": "1.2.7", - "resolved": "https://registry.npmjs.org/minimist/-/minimist-1.2.7.tgz", - "integrity": "sha512-bzfL1YUZsP41gmu/qjrEk0Q6i2ix/cVeAhbCbqH9u3zYutS1cLg00qhrD0M2MVdCcx4Sc0UpP2eBWo9rotpq6g==", + "version": "1.2.8", + "resolved": "https://registry.npmjs.org/minimist/-/minimist-1.2.8.tgz", + "integrity": "sha512-2yyAR8qBkN3YuheJanUpWC5U3bb5osDywNB8RzDVlDwDHbocAJveqqj1u8+SVD7jkWT4yvsHCpWqqWqAxb0zCA==", "dev": true, "funding": { "url": "https://github.com/sponsors/ljharb" @@ -5683,9 +5691,9 @@ } }, "node_modules/node-releases": { - "version": "2.0.8", - "resolved": "https://registry.npmjs.org/node-releases/-/node-releases-2.0.8.tgz", - "integrity": "sha512-dFSmB8fFHEH/s81Xi+Y/15DQY6VHW81nXRj86EMSL3lmuTmK1e+aT4wrFCkTbm+gSwkw4KpX+rT/pMM2c1mF+A==", + "version": "2.0.10", + "resolved": "https://registry.npmjs.org/node-releases/-/node-releases-2.0.10.tgz", + "integrity": "sha512-5GFldHPXVG/YZmFzJvKK2zDSzPKhEp0+ZR5SVaoSag9fsL5YgHbUHDfnG5494ISANDcK4KwPXAx2xqVEydmd7w==", "dev": true }, "node_modules/normalize-path": { @@ -6403,9 +6411,9 @@ } }, "node_modules/raw-body": { - "version": "2.5.1", - "resolved": "https://registry.npmjs.org/raw-body/-/raw-body-2.5.1.tgz", - "integrity": "sha512-qqJBtEyVgS0ZmPGdCFPWJ3FreoqvG4MVQln/kCgF7Olq95IbOp0/BWyMwbdtn4VTvkM8Y7khCQ2Xgk/tcrCXig==", + "version": "2.5.2", + "resolved": "https://registry.npmjs.org/raw-body/-/raw-body-2.5.2.tgz", + "integrity": "sha512-8zGqypfENjCIqGhgXToC8aB2r7YrBX+AQAfIPs/Mlk+BtPTztOvTS01NRW/3Eh60J+a48lt8qsCzirQ6loCVfA==", "dev": true, "dependencies": { "bytes": "3.1.2", @@ -6448,9 +6456,9 @@ } }, "node_modules/readable-stream": { - "version": "3.6.0", - "resolved": "https://registry.npmjs.org/readable-stream/-/readable-stream-3.6.0.tgz", - "integrity": "sha512-BViHy7LKeTz4oNnkcLJ+lVSL6vpiFeX6/d3oSH8zCW7UxP2onchk+vTGB143xuFjHS3deTgkKoXXymXqymiIdA==", + "version": "3.6.1", + "resolved": "https://registry.npmjs.org/readable-stream/-/readable-stream-3.6.1.tgz", + "integrity": "sha512-+rQmrWMYGA90yenhTYsLWAsLsqVC8osOw6PKE1HDYiO0gdPeKe/xDHNzIAIn4C91YQ6oenEhfYqqc1883qHbjQ==", "dev": true, "dependencies": { "inherits": "^2.0.3", @@ -6991,11 +6999,12 @@ } }, "node_modules/shiki": { - "version": "0.12.1", - "resolved": "https://registry.npmjs.org/shiki/-/shiki-0.12.1.tgz", - "integrity": "sha512-aieaV1m349rZINEBkjxh2QbBvFFQOlgqYTNtCal82hHj4dDZ76oMlQIX+C7ryerBTDiga3e5NfH6smjdJ02BbQ==", + "version": "0.14.1", + "resolved": "https://registry.npmjs.org/shiki/-/shiki-0.14.1.tgz", + "integrity": "sha512-+Jz4nBkCBe0mEDqo1eKRcCdjRtrCjozmcbTUjbPTX7OOJfEbTZzlUWlZtGe3Gb5oV1/jnojhG//YZc3rs9zSEw==", "dev": true, "dependencies": { + "ansi-sequence-parser": "^1.1.0", "jsonc-parser": "^3.2.0", "vscode-oniguruma": "^1.7.0", "vscode-textmate": "^8.0.0" @@ -7450,9 +7459,9 @@ } }, "node_modules/tslib": { - "version": "2.4.1", - "resolved": "https://registry.npmjs.org/tslib/-/tslib-2.4.1.tgz", - "integrity": "sha512-tGyy4dAjRIEwI7BzsB0lynWgOpfqjUdq91XXAlIWD2OwKBH7oCl/GZG/HT4BOHrTlPMOASlMQ7veyTqpmRcrNA==", + "version": "2.5.0", + "resolved": "https://registry.npmjs.org/tslib/-/tslib-2.5.0.tgz", + "integrity": "sha512-336iVw3rtn2BUK7ORdIAHTyxHGRIHVReokCR3XjbckJMK7ms8FysBfhLR8IXnAgy7T0PTPNBWKiH514FOW/WSg==", "dev": true }, "node_modules/tsutils": { @@ -7498,9 +7507,9 @@ } }, "node_modules/type-fest": { - "version": "3.5.3", - "resolved": "https://registry.npmjs.org/type-fest/-/type-fest-3.5.3.tgz", - "integrity": "sha512-V2+og4j/rWReWvaFrse3s9g2xvUv/K9Azm/xo6CjIuq7oeGqsoimC7+9/A3tfvNcbQf8RPSVj/HV81fB4DJrjA==", + "version": "3.6.0", + "resolved": "https://registry.npmjs.org/type-fest/-/type-fest-3.6.0.tgz", + "integrity": "sha512-RqTRtKTzvPpNdDUp1dVkKQRunlPITk4mXeqFlAZoJsS+fLRn8AdPK0TcQDumGayhU7fjlBfiBjsq3pe3rIfXZQ==", "dev": true, "engines": { "node": ">=14.16" @@ -7533,15 +7542,15 @@ } }, "node_modules/typedoc": { - "version": "0.23.24", - "resolved": "https://registry.npmjs.org/typedoc/-/typedoc-0.23.24.tgz", - "integrity": "sha512-bfmy8lNQh+WrPYcJbtjQ6JEEsVl/ce1ZIXyXhyW+a1vFrjO39t6J8sL/d6FfAGrJTc7McCXgk9AanYBSNvLdIA==", + "version": "0.23.25", + "resolved": "https://registry.npmjs.org/typedoc/-/typedoc-0.23.25.tgz", + "integrity": "sha512-O1he153qVyoCgJYSvIyY3bPP1wAJTegZfa6tL3APinSZhJOf8CSd8F/21M6ex8pUY/fuY6n0jAsT4fIuMGA6sA==", "dev": true, "dependencies": { "lunr": "^2.3.9", - "marked": "^4.2.5", - "minimatch": "^5.1.2", - "shiki": "^0.12.1" + "marked": "^4.2.12", + "minimatch": "^6.1.6", + "shiki": "^0.14.1" }, "bin": { "typedoc": "bin/typedoc" @@ -7563,21 +7572,24 @@ } }, "node_modules/typedoc/node_modules/minimatch": { - "version": "5.1.6", - "resolved": "https://registry.npmjs.org/minimatch/-/minimatch-5.1.6.tgz", - "integrity": "sha512-lKwV/1brpG6mBUFHtb7NUmtABCb2WZZmm2wNiOA5hAb8VdCS4B3dtMWyvcoViccwAW/COERjXLt0zP1zXUN26g==", + "version": "6.2.0", + "resolved": "https://registry.npmjs.org/minimatch/-/minimatch-6.2.0.tgz", + "integrity": "sha512-sauLxniAmvnhhRjFwPNnJKaPFYyddAgbYdeUpHULtCT/GhzdCx/MDNy+Y40lBxTQUrMzDE8e0S43Z5uqfO0REg==", "dev": true, "dependencies": { "brace-expansion": "^2.0.1" }, "engines": { "node": ">=10" + }, + "funding": { + "url": "https://github.com/sponsors/isaacs" } }, "node_modules/typescript": { - "version": "4.9.4", - "resolved": "https://registry.npmjs.org/typescript/-/typescript-4.9.4.tgz", - "integrity": "sha512-Uz+dTXYzxXXbsFpM86Wh3dKCxrQqUcVMxwU54orwlJjOpO3ao8L7j5lH+dWfTwgCwIuM9GQ2kvVotzYJMXTBZg==", + "version": "4.9.5", + "resolved": "https://registry.npmjs.org/typescript/-/typescript-4.9.5.tgz", + "integrity": "sha512-1FXk9E2Hm+QzZQ7z+McJiHL4NW1F2EzMu9Nq9i3zAaGqibafqYwCVU6WyWAuyQRRzOlxou8xZSyXLEN8oKj24g==", "dev": true, "bin": { "tsc": "bin/tsc", @@ -7768,9 +7780,9 @@ "dev": true }, "node_modules/vm2": { - "version": "3.9.13", - "resolved": "https://registry.npmjs.org/vm2/-/vm2-3.9.13.tgz", - "integrity": "sha512-0rvxpB8P8Shm4wX2EKOiMp7H2zq+HUE/UwodY0pCZXs9IffIKZq6vUti5OgkVCTakKo9e/fgO4X1fkwfjWxE3Q==", + "version": "3.9.14", + "resolved": "https://registry.npmjs.org/vm2/-/vm2-3.9.14.tgz", + "integrity": "sha512-HgvPHYHeQy8+QhzlFryvSteA4uQLBCOub02mgqdR+0bN/akRZ48TGB1v0aCv7ksyc0HXx16AZtMHKS38alc6TA==", "dev": true, "dependencies": { "acorn": "^8.7.0", @@ -8246,13 +8258,13 @@ "devDependencies": { "@istanbuljs/nyc-config-typescript": "^1.0.2", "@redis/test-utils": "*", - "@types/node": "^18.11.18", + "@types/node": "^18.14.1", "nyc": "^15.1.0", "release-it": "^15.6.0", "source-map-support": "^0.5.21", "ts-node": "^10.9.1", - "typedoc": "^0.23.24", - "typescript": "^4.9.4" + "typedoc": "^0.23.25", + "typescript": "^4.9.5" }, "peerDependencies": { "@redis/client": "^1.0.0" @@ -8270,19 +8282,19 @@ "devDependencies": { "@istanbuljs/nyc-config-typescript": "^1.0.2", "@redis/test-utils": "*", - "@types/node": "^18.11.18", + "@types/node": "^18.14.1", "@types/sinon": "^10.0.13", "@types/yallist": "^4.0.1", - "@typescript-eslint/eslint-plugin": "^5.49.0", - "@typescript-eslint/parser": "^5.49.0", - "eslint": "^8.32.0", + "@typescript-eslint/eslint-plugin": "^5.53.0", + "@typescript-eslint/parser": "^5.53.0", + "eslint": "^8.34.0", "nyc": "^15.1.0", "release-it": "^15.6.0", "sinon": "^15.0.1", "source-map-support": "^0.5.21", "ts-node": "^10.9.1", - "typedoc": "^0.23.24", - "typescript": "^4.9.4" + "typedoc": "^0.23.25", + "typescript": "^4.9.5" }, "engines": { "node": ">=14" @@ -8300,13 +8312,13 @@ "devDependencies": { "@istanbuljs/nyc-config-typescript": "^1.0.2", "@redis/test-utils": "*", - "@types/node": "^18.11.18", + "@types/node": "^18.14.1", "nyc": "^15.1.0", "release-it": "^15.6.0", "source-map-support": "^0.5.21", "ts-node": "^10.9.1", - "typedoc": "^0.23.24", - "typescript": "^4.9.4" + "typedoc": "^0.23.25", + "typescript": "^4.9.5" }, "peerDependencies": { "@redis/client": "^1.0.0" @@ -8319,13 +8331,13 @@ "devDependencies": { "@istanbuljs/nyc-config-typescript": "^1.0.2", "@redis/test-utils": "*", - "@types/node": "^18.11.18", + "@types/node": "^18.14.1", "nyc": "^15.1.0", "release-it": "^15.6.0", "source-map-support": "^0.5.21", "ts-node": "^10.9.1", - "typedoc": "^0.23.24", - "typescript": "^4.9.4" + "typedoc": "^0.23.25", + "typescript": "^4.9.5" }, "peerDependencies": { "@redis/client": "^1.0.0" @@ -8338,13 +8350,13 @@ "devDependencies": { "@istanbuljs/nyc-config-typescript": "^1.0.2", "@redis/test-utils": "*", - "@types/node": "^18.11.18", + "@types/node": "^18.14.1", "nyc": "^15.1.0", "release-it": "^15.6.0", "source-map-support": "^0.5.21", "ts-node": "^10.9.1", - "typedoc": "^0.23.24", - "typescript": "^4.9.4" + "typedoc": "^0.23.25", + "typescript": "^4.9.5" }, "peerDependencies": { "@redis/client": "^1.0.0" @@ -8355,14 +8367,14 @@ "devDependencies": { "@istanbuljs/nyc-config-typescript": "^1.0.2", "@types/mocha": "^10.0.1", - "@types/node": "^18.11.18", - "@types/yargs": "^17.0.20", + "@types/node": "^18.14.1", + "@types/yargs": "^17.0.22", "mocha": "^10.2.0", "nyc": "^15.1.0", "source-map-support": "^0.5.21", "ts-node": "^10.9.1", - "typescript": "^4.9.4", - "yargs": "^17.6.2" + "typescript": "^4.9.5", + "yargs": "^17.7.1" }, "peerDependencies": { "@redis/client": "^1.0.0" @@ -8465,9 +8477,9 @@ } }, "packages/test-utils/node_modules/yargs": { - "version": "17.6.2", - "resolved": "https://registry.npmjs.org/yargs/-/yargs-17.6.2.tgz", - "integrity": "sha512-1/9UrdHjDZc0eOU0HxOHoS78C69UD3JRMvzlJ7S79S2nTaWRA/whGCTV8o9e/N/1Va9YIV7Q4sOxD8VV4pCWOw==", + "version": "17.7.1", + "resolved": "https://registry.npmjs.org/yargs/-/yargs-17.7.1.tgz", + "integrity": "sha512-cwiTb08Xuv5fqF4AovYacTFNxk62th7LKJ6BL9IGUpTJrWoU7/7WdQGTP2SjKf1dUNBGzDd28p/Yfs/GI6JrLw==", "dev": true, "dependencies": { "cliui": "^8.0.1", @@ -8489,13 +8501,13 @@ "devDependencies": { "@istanbuljs/nyc-config-typescript": "^1.0.2", "@redis/test-utils": "*", - "@types/node": "^18.11.18", + "@types/node": "^18.14.1", "nyc": "^15.1.0", "release-it": "^15.6.0", "source-map-support": "^0.5.21", "ts-node": "^10.9.1", - "typedoc": "^0.23.24", - "typescript": "^4.9.4" + "typedoc": "^0.23.25", + "typescript": "^4.9.5" }, "peerDependencies": { "@redis/client": "^1.0.0" diff --git a/package.json b/package.json index 630aeae156d..f200e361f35 100644 --- a/package.json +++ b/package.json @@ -34,7 +34,7 @@ "@tsconfig/node14": "^1.0.3", "gh-pages": "^5.0.0", "release-it": "^15.6.0", - "typescript": "^4.9.4" + "typescript": "^4.9.5" }, "repository": { "type": "git", diff --git a/packages/bloom/package.json b/packages/bloom/package.json index d6d5e4778eb..d4eef193d84 100644 --- a/packages/bloom/package.json +++ b/packages/bloom/package.json @@ -18,12 +18,12 @@ "devDependencies": { "@istanbuljs/nyc-config-typescript": "^1.0.2", "@redis/test-utils": "*", - "@types/node": "^18.11.18", + "@types/node": "^18.14.1", "nyc": "^15.1.0", "release-it": "^15.6.0", "source-map-support": "^0.5.21", "ts-node": "^10.9.1", - "typedoc": "^0.23.24", - "typescript": "^4.9.4" + "typedoc": "^0.23.25", + "typescript": "^4.9.5" } } diff --git a/packages/client/package.json b/packages/client/package.json index d42c0d4b912..59a6a17ffb8 100644 --- a/packages/client/package.json +++ b/packages/client/package.json @@ -21,19 +21,19 @@ "devDependencies": { "@istanbuljs/nyc-config-typescript": "^1.0.2", "@redis/test-utils": "*", - "@types/node": "^18.11.18", + "@types/node": "^18.14.1", "@types/sinon": "^10.0.13", "@types/yallist": "^4.0.1", - "@typescript-eslint/eslint-plugin": "^5.49.0", - "@typescript-eslint/parser": "^5.49.0", - "eslint": "^8.32.0", + "@typescript-eslint/eslint-plugin": "^5.53.0", + "@typescript-eslint/parser": "^5.53.0", + "eslint": "^8.34.0", "nyc": "^15.1.0", "release-it": "^15.6.0", "sinon": "^15.0.1", "source-map-support": "^0.5.21", "ts-node": "^10.9.1", - "typedoc": "^0.23.24", - "typescript": "^4.9.4" + "typedoc": "^0.23.25", + "typescript": "^4.9.5" }, "engines": { "node": ">=14" diff --git a/packages/graph/package.json b/packages/graph/package.json index 418d48f0223..40ce8b7c560 100644 --- a/packages/graph/package.json +++ b/packages/graph/package.json @@ -18,12 +18,12 @@ "devDependencies": { "@istanbuljs/nyc-config-typescript": "^1.0.2", "@redis/test-utils": "*", - "@types/node": "^18.11.18", + "@types/node": "^18.14.1", "nyc": "^15.1.0", "release-it": "^15.6.0", "source-map-support": "^0.5.21", "ts-node": "^10.9.1", - "typedoc": "^0.23.24", - "typescript": "^4.9.4" + "typedoc": "^0.23.25", + "typescript": "^4.9.5" } } diff --git a/packages/json/package.json b/packages/json/package.json index b7a6fac35d7..6f4c468bfd4 100644 --- a/packages/json/package.json +++ b/packages/json/package.json @@ -18,12 +18,12 @@ "devDependencies": { "@istanbuljs/nyc-config-typescript": "^1.0.2", "@redis/test-utils": "*", - "@types/node": "^18.11.18", + "@types/node": "^18.14.1", "nyc": "^15.1.0", "release-it": "^15.6.0", "source-map-support": "^0.5.21", "ts-node": "^10.9.1", - "typedoc": "^0.23.24", - "typescript": "^4.9.4" + "typedoc": "^0.23.25", + "typescript": "^4.9.5" } } diff --git a/packages/search/package.json b/packages/search/package.json index 896b7746cca..15a0aa6fa7d 100644 --- a/packages/search/package.json +++ b/packages/search/package.json @@ -18,12 +18,12 @@ "devDependencies": { "@istanbuljs/nyc-config-typescript": "^1.0.2", "@redis/test-utils": "*", - "@types/node": "^18.11.18", + "@types/node": "^18.14.1", "nyc": "^15.1.0", "release-it": "^15.6.0", "source-map-support": "^0.5.21", "ts-node": "^10.9.1", - "typedoc": "^0.23.24", - "typescript": "^4.9.4" + "typedoc": "^0.23.25", + "typescript": "^4.9.5" } } diff --git a/packages/test-utils/package.json b/packages/test-utils/package.json index 71fe1b33457..c82ca367301 100644 --- a/packages/test-utils/package.json +++ b/packages/test-utils/package.json @@ -12,13 +12,13 @@ "devDependencies": { "@istanbuljs/nyc-config-typescript": "^1.0.2", "@types/mocha": "^10.0.1", - "@types/node": "^18.11.18", - "@types/yargs": "^17.0.20", + "@types/node": "^18.14.1", + "@types/yargs": "^17.0.22", "mocha": "^10.2.0", "nyc": "^15.1.0", "source-map-support": "^0.5.21", "ts-node": "^10.9.1", - "typescript": "^4.9.4", - "yargs": "^17.6.2" + "typescript": "^4.9.5", + "yargs": "^17.7.1" } } diff --git a/packages/time-series/package.json b/packages/time-series/package.json index 26db747a2a4..1d6a184e5fc 100644 --- a/packages/time-series/package.json +++ b/packages/time-series/package.json @@ -18,12 +18,12 @@ "devDependencies": { "@istanbuljs/nyc-config-typescript": "^1.0.2", "@redis/test-utils": "*", - "@types/node": "^18.11.18", + "@types/node": "^18.14.1", "nyc": "^15.1.0", "release-it": "^15.6.0", "source-map-support": "^0.5.21", "ts-node": "^10.9.1", - "typedoc": "^0.23.24", - "typescript": "^4.9.4" + "typedoc": "^0.23.25", + "typescript": "^4.9.5" } } From f0794ece012265e6d3c1d6f9b1fe4c23aacb5536 Mon Sep 17 00:00:00 2001 From: Leibale Date: Fri, 24 Feb 2023 18:07:31 -0500 Subject: [PATCH 1347/1748] Release search@1.1.2 --- packages/search/package.json | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/packages/search/package.json b/packages/search/package.json index 15a0aa6fa7d..684bfe1ff00 100644 --- a/packages/search/package.json +++ b/packages/search/package.json @@ -1,6 +1,6 @@ { "name": "@redis/search", - "version": "1.1.1", + "version": "1.1.2", "license": "MIT", "main": "./dist/index.js", "types": "./dist/index.d.ts", From f756d9aaa92ca7f6d8aa09fa40c017b73751c859 Mon Sep 17 00:00:00 2001 From: Leibale Date: Fri, 24 Feb 2023 18:10:07 -0500 Subject: [PATCH 1348/1748] Release client@1.5.6 --- packages/client/package.json | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/packages/client/package.json b/packages/client/package.json index 59a6a17ffb8..c342fce26fa 100644 --- a/packages/client/package.json +++ b/packages/client/package.json @@ -1,6 +1,6 @@ { "name": "@redis/client", - "version": "1.5.5", + "version": "1.5.6", "license": "MIT", "main": "./dist/index.js", "types": "./dist/index.d.ts", From 4745fae37f072226ee3a2defe6b80808f8e9594c Mon Sep 17 00:00:00 2001 From: Leibale Date: Fri, 24 Feb 2023 18:16:56 -0500 Subject: [PATCH 1349/1748] ugprade subpackages --- package-lock.json | 8 ++++---- package.json | 4 ++-- 2 files changed, 6 insertions(+), 6 deletions(-) diff --git a/package-lock.json b/package-lock.json index 1bb78ea74ae..071079d9e62 100644 --- a/package-lock.json +++ b/package-lock.json @@ -13,10 +13,10 @@ ], "dependencies": { "@redis/bloom": "1.2.0", - "@redis/client": "1.5.5", + "@redis/client": "1.5.6", "@redis/graph": "1.1.0", "@redis/json": "1.0.4", - "@redis/search": "1.1.1", + "@redis/search": "1.1.2", "@redis/time-series": "1.0.4" }, "devDependencies": { @@ -8272,7 +8272,7 @@ }, "packages/client": { "name": "@redis/client", - "version": "1.5.5", + "version": "1.5.6", "license": "MIT", "dependencies": { "cluster-key-slot": "1.1.2", @@ -8345,7 +8345,7 @@ }, "packages/search": { "name": "@redis/search", - "version": "1.1.1", + "version": "1.1.2", "license": "MIT", "devDependencies": { "@istanbuljs/nyc-config-typescript": "^1.0.2", diff --git a/package.json b/package.json index f200e361f35..5a15a1aeb2a 100644 --- a/package.json +++ b/package.json @@ -24,10 +24,10 @@ }, "dependencies": { "@redis/bloom": "1.2.0", - "@redis/client": "1.5.5", + "@redis/client": "1.5.6", "@redis/graph": "1.1.0", "@redis/json": "1.0.4", - "@redis/search": "1.1.1", + "@redis/search": "1.1.2", "@redis/time-series": "1.0.4" }, "devDependencies": { From ca1d04f2ba5d99aa86f211db2e2b2fb6c2bfb89f Mon Sep 17 00:00:00 2001 From: Leibale Date: Fri, 24 Feb 2023 18:22:13 -0500 Subject: [PATCH 1350/1748] Release redis@4.6.5 --- package-lock.json | 4 ++-- package.json | 2 +- 2 files changed, 3 insertions(+), 3 deletions(-) diff --git a/package-lock.json b/package-lock.json index 071079d9e62..3ff77cd4bcd 100644 --- a/package-lock.json +++ b/package-lock.json @@ -1,12 +1,12 @@ { "name": "redis", - "version": "4.6.4", + "version": "4.6.5", "lockfileVersion": 3, "requires": true, "packages": { "": { "name": "redis", - "version": "4.6.4", + "version": "4.6.5", "license": "MIT", "workspaces": [ "./packages/*" diff --git a/package.json b/package.json index 5a15a1aeb2a..def009747ca 100644 --- a/package.json +++ b/package.json @@ -1,7 +1,7 @@ { "name": "redis", "description": "A modern, high performance Redis client", - "version": "4.6.4", + "version": "4.6.5", "license": "MIT", "main": "./dist/index.js", "types": "./dist/index.d.ts", From 1d8302c9db2a49c4875bd83d3c87fa05265d5400 Mon Sep 17 00:00:00 2001 From: Carl Hopf Date: Tue, 14 Mar 2023 16:37:13 +0000 Subject: [PATCH 1351/1748] `FT.CREATE` - set `FIRST_KEY_INDEX` to `1` (#2440) --- packages/search/lib/commands/CREATE.ts | 2 ++ 1 file changed, 2 insertions(+) diff --git a/packages/search/lib/commands/CREATE.ts b/packages/search/lib/commands/CREATE.ts index 21662c28d7d..37e8c6fb110 100644 --- a/packages/search/lib/commands/CREATE.ts +++ b/packages/search/lib/commands/CREATE.ts @@ -1,6 +1,8 @@ import { pushOptionalVerdictArgument } from '@redis/client/dist/lib/commands/generic-transformers'; import { RedisSearchLanguages, PropertyName, RediSearchSchema, pushSchema } from '.'; +export const FIRST_KEY_INDEX = 1; + interface CreateOptions { ON?: 'HASH' | 'JSON'; PREFIX?: string | Array; From d65a641b2db96c6d63a2a51c43e823aba8256e28 Mon Sep 17 00:00:00 2001 From: Leibale Eidelman Date: Fri, 17 Mar 2023 09:52:30 -0400 Subject: [PATCH 1352/1748] revert 1d8302c9db2a49c4875bd83d3c87fa05265d5400 --- packages/search/lib/commands/CREATE.ts | 2 -- 1 file changed, 2 deletions(-) diff --git a/packages/search/lib/commands/CREATE.ts b/packages/search/lib/commands/CREATE.ts index 37e8c6fb110..21662c28d7d 100644 --- a/packages/search/lib/commands/CREATE.ts +++ b/packages/search/lib/commands/CREATE.ts @@ -1,8 +1,6 @@ import { pushOptionalVerdictArgument } from '@redis/client/dist/lib/commands/generic-transformers'; import { RedisSearchLanguages, PropertyName, RediSearchSchema, pushSchema } from '.'; -export const FIRST_KEY_INDEX = 1; - interface CreateOptions { ON?: 'HASH' | 'JSON'; PREFIX?: string | Array; From 9f2e27d07d4edf2d89d5cf79c432e60478f21fcf Mon Sep 17 00:00:00 2001 From: sorilove Date: Tue, 25 Apr 2023 01:41:30 +0900 Subject: [PATCH 1353/1748] Fixed a bug in the updateIsActive function (#2476) Missing PubSubType.SHARDED and duplicate comparison of PubSubType.CHANNELS when comparing listeners size in updateIsActive function --- packages/client/lib/client/pub-sub.ts | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/packages/client/lib/client/pub-sub.ts b/packages/client/lib/client/pub-sub.ts index 8efc9d8a2e2..a8a909e0252 100644 --- a/packages/client/lib/client/pub-sub.ts +++ b/packages/client/lib/client/pub-sub.ts @@ -307,7 +307,7 @@ export class PubSub { this.#isActive = ( this.#listeners[PubSubType.CHANNELS].size !== 0 || this.#listeners[PubSubType.PATTERNS].size !== 0 || - this.#listeners[PubSubType.CHANNELS].size !== 0 || + this.#listeners[PubSubType.SHARDED].size !== 0 || this.#subscribing !== 0 ); } From c88dea6151e2b96fe901cf549bc8c43e6ccb969f Mon Sep 17 00:00:00 2001 From: Gady Date: Mon, 24 Apr 2023 19:41:44 +0300 Subject: [PATCH 1354/1748] Augment subpackages npm info (#2478) * augment subpackages npm info * some more keywords --------- Co-authored-by: Leibale Eidelman --- packages/bloom/package.json | 14 +++++++++++++- packages/client/package.json | 5 ++++- packages/graph/package.json | 14 +++++++++++++- packages/json/package.json | 14 +++++++++++++- packages/search/package.json | 14 +++++++++++++- packages/time-series/package.json | 14 +++++++++++++- 6 files changed, 69 insertions(+), 6 deletions(-) diff --git a/packages/bloom/package.json b/packages/bloom/package.json index d4eef193d84..d2e2c80806d 100644 --- a/packages/bloom/package.json +++ b/packages/bloom/package.json @@ -25,5 +25,17 @@ "ts-node": "^10.9.1", "typedoc": "^0.23.25", "typescript": "^4.9.5" - } + }, + "repository": { + "type": "git", + "url": "git://github.com/redis/node-redis.git" + }, + "bugs": { + "url": "https://github.com/redis/node-redis/issues" + }, + "homepage": "https://github.com/redis/node-redis/tree/master/packages/bloom", + "keywords": [ + "redis", + "RedisBloom" + ] } diff --git a/packages/client/package.json b/packages/client/package.json index c342fce26fa..5fde2a35ad7 100644 --- a/packages/client/package.json +++ b/packages/client/package.json @@ -45,5 +45,8 @@ "bugs": { "url": "https://github.com/redis/node-redis/issues" }, - "homepage": "https://github.com/redis/node-redis/tree/master/packages/client" + "homepage": "https://github.com/redis/node-redis/tree/master/packages/client", + "keywords": [ + "redis" + ] } diff --git a/packages/graph/package.json b/packages/graph/package.json index 40ce8b7c560..bbd5dd778d9 100644 --- a/packages/graph/package.json +++ b/packages/graph/package.json @@ -25,5 +25,17 @@ "ts-node": "^10.9.1", "typedoc": "^0.23.25", "typescript": "^4.9.5" - } + }, + "repository": { + "type": "git", + "url": "git://github.com/redis/node-redis.git" + }, + "bugs": { + "url": "https://github.com/redis/node-redis/issues" + }, + "homepage": "https://github.com/redis/node-redis/tree/master/packages/graph", + "keywords": [ + "redis", + "RedisGraph" + ] } diff --git a/packages/json/package.json b/packages/json/package.json index 6f4c468bfd4..b2f0ec9dbe2 100644 --- a/packages/json/package.json +++ b/packages/json/package.json @@ -25,5 +25,17 @@ "ts-node": "^10.9.1", "typedoc": "^0.23.25", "typescript": "^4.9.5" - } + }, + "repository": { + "type": "git", + "url": "git://github.com/redis/node-redis.git" + }, + "bugs": { + "url": "https://github.com/redis/node-redis/issues" + }, + "homepage": "https://github.com/redis/node-redis/tree/master/packages/json", + "keywords": [ + "redis", + "RedisJSON" + ] } diff --git a/packages/search/package.json b/packages/search/package.json index 684bfe1ff00..61154daa014 100644 --- a/packages/search/package.json +++ b/packages/search/package.json @@ -25,5 +25,17 @@ "ts-node": "^10.9.1", "typedoc": "^0.23.25", "typescript": "^4.9.5" - } + }, + "repository": { + "type": "git", + "url": "git://github.com/redis/node-redis.git" + }, + "bugs": { + "url": "https://github.com/redis/node-redis/issues" + }, + "homepage": "https://github.com/redis/node-redis/tree/master/packages/search", + "keywords": [ + "redis", + "RediSearch" + ] } diff --git a/packages/time-series/package.json b/packages/time-series/package.json index 1d6a184e5fc..fffad02f62b 100644 --- a/packages/time-series/package.json +++ b/packages/time-series/package.json @@ -25,5 +25,17 @@ "ts-node": "^10.9.1", "typedoc": "^0.23.25", "typescript": "^4.9.5" - } + }, + "repository": { + "type": "git", + "url": "git://github.com/redis/node-redis.git" + }, + "bugs": { + "url": "https://github.com/redis/node-redis/issues" + }, + "homepage": "https://github.com/redis/node-redis/tree/master/packages/time-series", + "keywords": [ + "redis", + "RedisTimeSeries" + ] } From e1658ba6efa138f9caadfcd60563dbb7372a6f56 Mon Sep 17 00:00:00 2001 From: Carl Hopf Date: Wed, 26 Apr 2023 18:56:04 +0200 Subject: [PATCH 1355/1748] fix cluster extractFirstKey skip commandOptions() passed to args (#2439) * cluster extractFirstKey skip commandOptions() passed to args * cluster with commandOptions unit test * improve performance * fix type * fix type --------- Co-authored-by: Leibale Eidelman --- packages/client/lib/cluster/index.spec.ts | 16 ++++++++++++++++ packages/client/lib/cluster/index.ts | 4 ++-- packages/client/lib/commander.ts | 2 ++ 3 files changed, 20 insertions(+), 2 deletions(-) diff --git a/packages/client/lib/cluster/index.spec.ts b/packages/client/lib/cluster/index.spec.ts index 6b9d35f75fb..8200375056a 100644 --- a/packages/client/lib/cluster/index.spec.ts +++ b/packages/client/lib/cluster/index.spec.ts @@ -2,6 +2,7 @@ import { strict as assert } from 'assert'; import testUtils, { GLOBAL, waitTillBeenCalled } from '../test-utils'; import RedisCluster from '.'; import { ClusterSlotStates } from '../commands/CLUSTER_SETSLOT'; +import { commandOptions } from '../command-options'; import { SQUARE_SCRIPT } from '../client/index.spec'; import { RootNodesUnavailableError } from '../errors'; import { spy } from 'sinon'; @@ -178,6 +179,21 @@ describe('Cluster', () => { await assert.rejects(cluster.mGet(['a', 'b'])); }, GLOBAL.CLUSTERS.OPEN); + testUtils.testWithCluster('should send commands with commandOptions to correct cluster slot (without redirections)', async cluster => { + // 'a' and 'b' hash to different cluster slots (see previous unit test) + // -> maxCommandRedirections 0: rejects on MOVED/ASK reply + await cluster.set(commandOptions({ isolated: true }), 'a', '1'), + await cluster.set(commandOptions({ isolated: true }), 'b', '2'), + + assert.equal(await cluster.get('a'), '1'); + assert.equal(await cluster.get('b'), '2'); + }, { + ...GLOBAL.CLUSTERS.OPEN, + clusterConfiguration: { + maxCommandRedirections: 0 + } + }); + describe('minimizeConnections', () => { testUtils.testWithCluster('false', async cluster => { for (const master of cluster.masters) { diff --git a/packages/client/lib/cluster/index.ts b/packages/client/lib/cluster/index.ts index 818930c8c82..7e486a376b6 100644 --- a/packages/client/lib/cluster/index.ts +++ b/packages/client/lib/cluster/index.ts @@ -152,11 +152,11 @@ export default class RedisCluster< command: C, args: Array ): Promise> { - const { args: redisArgs, options } = transformCommandArguments(command, args); + const { jsArgs, args: redisArgs, options } = transformCommandArguments(command, args); return transformCommandReply( command, await this.sendCommand( - RedisCluster.extractFirstKey(command, args, redisArgs), + RedisCluster.extractFirstKey(command, jsArgs, redisArgs), command.IS_READ_ONLY, redisArgs, options diff --git a/packages/client/lib/commander.ts b/packages/client/lib/commander.ts index 1407a80344c..c04f41e1eb0 100644 --- a/packages/client/lib/commander.ts +++ b/packages/client/lib/commander.ts @@ -108,6 +108,7 @@ export function transformCommandArguments( command: RedisCommand, args: Array ): { + jsArgs: Array; args: RedisCommandArguments; options: CommandOptions | undefined; } { @@ -118,6 +119,7 @@ export function transformCommandArguments( } return { + jsArgs: args, args: command.transformArguments(...args), options }; From ba31c8a50e73bd19825f3eb779270343c0fff205 Mon Sep 17 00:00:00 2001 From: Leibale Eidelman Date: Wed, 26 Apr 2023 12:56:42 -0400 Subject: [PATCH 1356/1748] fix #2443 - fix multiple sockets on error in socket initiator (#2480) * fix #2443 - fix multiple sockets on error in socket initiator * handle `error` events in test --- packages/client/lib/client/index.spec.ts | 3 +++ packages/client/lib/client/socket.ts | 2 +- 2 files changed, 4 insertions(+), 1 deletion(-) diff --git a/packages/client/lib/client/index.spec.ts b/packages/client/lib/client/index.spec.ts index 7396a2e9c37..b608c7c092c 100644 --- a/packages/client/lib/client/index.spec.ts +++ b/packages/client/lib/client/index.spec.ts @@ -647,6 +647,9 @@ describe('Client', () => { }); testUtils.testWithClient('should propagated errors from "isolated" clients', client => { + client.on('error', () => { + // ignore errors + }); return client.executeIsolated(isolated => killClient(isolated, client)); }, GLOBAL.SERVERS.OPEN); diff --git a/packages/client/lib/client/socket.ts b/packages/client/lib/client/socket.ts index ac3b7f5bf3b..89533350f59 100644 --- a/packages/client/lib/client/socket.ts +++ b/packages/client/lib/client/socket.ts @@ -199,7 +199,7 @@ export default class RedisSocket extends EventEmitter { .off('error', reject) .once('error', (err: Error) => this.#onSocketError(err)) .once('close', hadError => { - if (!hadError && this.#isOpen && this.#socket === socket) { + if (!hadError && this.#isReady && this.#socket === socket) { this.#onSocketError(new SocketClosedUnexpectedlyError()); } }) From 986a510237a44780a358e59d1512a4a3beabd5b2 Mon Sep 17 00:00:00 2001 From: Leibale Eidelman Date: Wed, 26 Apr 2023 14:29:39 -0400 Subject: [PATCH 1357/1748] fix #2481 - fix "falsy" arguments in TS.ALTER (#2483) --- packages/time-series/lib/commands/index.ts | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/packages/time-series/lib/commands/index.ts b/packages/time-series/lib/commands/index.ts index 19cd075ba49..356b0416648 100644 --- a/packages/time-series/lib/commands/index.ts +++ b/packages/time-series/lib/commands/index.ts @@ -128,7 +128,7 @@ export function transformTimestampArgument(timestamp: Timestamp): string { } export function pushRetentionArgument(args: RedisCommandArguments, retention?: number): RedisCommandArguments { - if (retention) { + if (retention !== undefined) { args.push( 'RETENTION', retention.toString() @@ -144,7 +144,7 @@ export enum TimeSeriesEncoding { } export function pushEncodingArgument(args: RedisCommandArguments, encoding?: TimeSeriesEncoding): RedisCommandArguments { - if (encoding) { + if (encoding !== undefined) { args.push( 'ENCODING', encoding @@ -155,7 +155,7 @@ export function pushEncodingArgument(args: RedisCommandArguments, encoding?: Tim } export function pushChunkSizeArgument(args: RedisCommandArguments, chunkSize?: number): RedisCommandArguments { - if (chunkSize) { + if (chunkSize !== undefined) { args.push( 'CHUNK_SIZE', chunkSize.toString() @@ -166,7 +166,7 @@ export function pushChunkSizeArgument(args: RedisCommandArguments, chunkSize?: n } export function pushDuplicatePolicy(args: RedisCommandArguments, duplicatePolicy?: TimeSeriesDuplicatePolicies): RedisCommandArguments { - if (duplicatePolicy) { + if (duplicatePolicy !== undefined) { args.push( 'DUPLICATE_POLICY', duplicatePolicy From dc920d3b67b45384aa7ac4c4a248b3af5fcebae6 Mon Sep 17 00:00:00 2001 From: Leibale Eidelman Date: Wed, 26 Apr 2023 14:44:09 -0400 Subject: [PATCH 1358/1748] fix `isolationPool` after reconnect (#2409) * fix #2406 - fix isolationPool after reconnect * revert breaking change * fix --- packages/client/lib/client/index.spec.ts | 42 ++++++++++++++++++++---- packages/client/lib/client/index.ts | 37 +++++++++++++-------- 2 files changed, 59 insertions(+), 20 deletions(-) diff --git a/packages/client/lib/client/index.spec.ts b/packages/client/lib/client/index.spec.ts index b608c7c092c..a0824e440ba 100644 --- a/packages/client/lib/client/index.spec.ts +++ b/packages/client/lib/client/index.spec.ts @@ -607,11 +607,41 @@ describe('Client', () => { } }); - testUtils.testWithClient('executeIsolated', async client => { - const id = await client.clientId(), - isolatedId = await client.executeIsolated(isolatedClient => isolatedClient.clientId()); - assert.ok(id !== isolatedId); - }, GLOBAL.SERVERS.OPEN); + describe('isolationPool', () => { + testUtils.testWithClient('executeIsolated', async client => { + const id = await client.clientId(), + isolatedId = await client.executeIsolated(isolatedClient => isolatedClient.clientId()); + assert.ok(id !== isolatedId); + }, GLOBAL.SERVERS.OPEN); + + testUtils.testWithClient('should be able to use pool even before connect', async client => { + await client.executeIsolated(() => Promise.resolve()); + // make sure to destroy isolation pool + await client.connect(); + await client.disconnect(); + }, { + ...GLOBAL.SERVERS.OPEN, + disableClientSetup: true + }); + + testUtils.testWithClient('should work after reconnect (#2406)', async client => { + await client.disconnect(); + await client.connect(); + await client.executeIsolated(() => Promise.resolve()); + }, GLOBAL.SERVERS.OPEN); + + testUtils.testWithClient('should throw ClientClosedError after disconnect', async client => { + await client.connect(); + await client.disconnect(); + await assert.rejects( + client.executeIsolated(() => Promise.resolve()), + ClientClosedError + ); + }, { + ...GLOBAL.SERVERS.OPEN, + disableClientSetup: true + }); + }); async function killClient< M extends RedisModules, @@ -731,7 +761,7 @@ describe('Client', () => { members.map(member => [member.value, member.score]).sort(sort) ); }, GLOBAL.SERVERS.OPEN); - + describe('PubSub', () => { testUtils.testWithClient('should be able to publish and subscribe to messages', async publisher => { function assertStringListener(message: string, channel: string) { diff --git a/packages/client/lib/client/index.ts b/packages/client/lib/client/index.ts index 5dd386647ef..5b9badf3f37 100644 --- a/packages/client/lib/client/index.ts +++ b/packages/client/lib/client/index.ts @@ -15,7 +15,6 @@ import { ClientClosedError, ClientOfflineError, DisconnectsClientError } from '. import { URL } from 'url'; import { TcpSocketConnectOpts } from 'net'; import { PubSubType, PubSubListener, PubSubTypeListeners, ChannelListeners } from './pub-sub'; -import { callbackify } from 'util'; export interface RedisClientOptions< M extends RedisModules = RedisModules, @@ -190,7 +189,7 @@ export default class RedisClient< readonly #options?: RedisClientOptions; readonly #socket: RedisSocket; readonly #queue: RedisCommandsQueue; - readonly #isolationPool: Pool>; + #isolationPool?: Pool>; readonly #v4: Record = {}; #selectedDB = 0; @@ -223,16 +222,9 @@ export default class RedisClient< this.#options = this.#initiateOptions(options); this.#queue = this.#initiateQueue(); this.#socket = this.#initiateSocket(); - this.#isolationPool = createPool({ - create: async () => { - const duplicate = this.duplicate({ - isolationPoolOptions: undefined - }).on('error', err => this.emit('error', err)); - await duplicate.connect(); - return duplicate; - }, - destroy: client => client.disconnect() - }, options?.isolationPoolOptions); + // should be initiated in connect, not here + // TODO: consider breaking in v5 + this.#isolationPool = this.#initiateIsolationPool(); this.#legacyMode(); } @@ -337,6 +329,19 @@ export default class RedisClient< .on('end', () => this.emit('end')); } + #initiateIsolationPool() { + return createPool({ + create: async () => { + const duplicate = this.duplicate({ + isolationPoolOptions: undefined + }).on('error', err => this.emit('error', err)); + await duplicate.connect(); + return duplicate; + }, + destroy: client => client.disconnect() + }, this.#options?.isolationPoolOptions); + } + #legacyMode(): void { if (!this.#options?.legacyMode) return; @@ -422,6 +427,8 @@ export default class RedisClient< } connect(): Promise { + // see comment in constructor + this.#isolationPool ??= this.#initiateIsolationPool(); return this.#socket.connect(); } @@ -704,6 +711,7 @@ export default class RedisClient< } executeIsolated(fn: (client: RedisClientType) => T | Promise): Promise { + if (!this.#isolationPool) return Promise.reject(new ClientClosedError()); return this.#isolationPool.use(fn); } @@ -802,8 +810,9 @@ export default class RedisClient< } async #destroyIsolationPool(): Promise { - await this.#isolationPool.drain(); - await this.#isolationPool.clear(); + await this.#isolationPool!.drain(); + await this.#isolationPool!.clear(); + this.#isolationPool = undefined; } ref(): void { From e8e09a7b5b042ff19ebfe32fcffcc7b4c1b8cd59 Mon Sep 17 00:00:00 2001 From: Leibale Date: Thu, 27 Apr 2023 18:46:40 -0400 Subject: [PATCH 1359/1748] Release client@1.5.7 --- packages/client/package.json | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/packages/client/package.json b/packages/client/package.json index 5fde2a35ad7..5f5793251dc 100644 --- a/packages/client/package.json +++ b/packages/client/package.json @@ -1,6 +1,6 @@ { "name": "@redis/client", - "version": "1.5.6", + "version": "1.5.7", "license": "MIT", "main": "./dist/index.js", "types": "./dist/index.d.ts", From f0bd7e0c2abb6a159bfdf68e83a528e05fbc0821 Mon Sep 17 00:00:00 2001 From: Leibale Date: Thu, 27 Apr 2023 18:47:43 -0400 Subject: [PATCH 1360/1748] upgrade deps --- package-lock.json | 4 ++-- package.json | 2 +- 2 files changed, 3 insertions(+), 3 deletions(-) diff --git a/package-lock.json b/package-lock.json index 3ff77cd4bcd..4ef64ce10d0 100644 --- a/package-lock.json +++ b/package-lock.json @@ -13,7 +13,7 @@ ], "dependencies": { "@redis/bloom": "1.2.0", - "@redis/client": "1.5.6", + "@redis/client": "1.5.7", "@redis/graph": "1.1.0", "@redis/json": "1.0.4", "@redis/search": "1.1.2", @@ -8272,7 +8272,7 @@ }, "packages/client": { "name": "@redis/client", - "version": "1.5.6", + "version": "1.5.7", "license": "MIT", "dependencies": { "cluster-key-slot": "1.1.2", diff --git a/package.json b/package.json index def009747ca..765e1b0918f 100644 --- a/package.json +++ b/package.json @@ -24,7 +24,7 @@ }, "dependencies": { "@redis/bloom": "1.2.0", - "@redis/client": "1.5.6", + "@redis/client": "1.5.7", "@redis/graph": "1.1.0", "@redis/json": "1.0.4", "@redis/search": "1.1.2", From 3273c8540d8354119e2e07d81e1913bb1bd9cbe4 Mon Sep 17 00:00:00 2001 From: Leibale Date: Thu, 27 Apr 2023 18:48:02 -0400 Subject: [PATCH 1361/1748] Release redis@4.6.6 --- package-lock.json | 4 ++-- package.json | 2 +- 2 files changed, 3 insertions(+), 3 deletions(-) diff --git a/package-lock.json b/package-lock.json index 4ef64ce10d0..49d7aea5d14 100644 --- a/package-lock.json +++ b/package-lock.json @@ -1,12 +1,12 @@ { "name": "redis", - "version": "4.6.5", + "version": "4.6.6", "lockfileVersion": 3, "requires": true, "packages": { "": { "name": "redis", - "version": "4.6.5", + "version": "4.6.6", "license": "MIT", "workspaces": [ "./packages/*" diff --git a/package.json b/package.json index 765e1b0918f..423c8506648 100644 --- a/package.json +++ b/package.json @@ -1,7 +1,7 @@ { "name": "redis", "description": "A modern, high performance Redis client", - "version": "4.6.5", + "version": "4.6.6", "license": "MIT", "main": "./dist/index.js", "types": "./dist/index.d.ts", From 260a41750c379b5929913096f264e9a433f8c0cc Mon Sep 17 00:00:00 2001 From: Chayim Date: Tue, 16 May 2023 14:03:42 +0300 Subject: [PATCH 1362/1748] Auto release via drafting, step one (#2453) * first step of auto-release with the release drafter * PR Comments * making leibale happy --- .github/release-drafter/bloom-config.yml | 50 +++++++++++++++++++ .github/release-drafter/graph-config.yml | 49 ++++++++++++++++++ .../json-config.yml} | 13 +++-- .github/release-drafter/search-config.yml | 50 +++++++++++++++++++ .../release-drafter/time-series-config.yml | 49 ++++++++++++++++++ .github/workflows/release-drafter-bloom.yml | 23 +++++++++ .github/workflows/release-drafter-graph.yml | 23 +++++++++ ...e-drafter.yml => release-drafter-json.yml} | 6 ++- .github/workflows/release-drafter-search.yml | 23 +++++++++ .../workflows/release-drafter-time-series.yml | 23 +++++++++ 10 files changed, 305 insertions(+), 4 deletions(-) create mode 100644 .github/release-drafter/bloom-config.yml create mode 100644 .github/release-drafter/graph-config.yml rename .github/{release-drafter-config.yml => release-drafter/json-config.yml} (77%) create mode 100644 .github/release-drafter/search-config.yml create mode 100644 .github/release-drafter/time-series-config.yml create mode 100644 .github/workflows/release-drafter-bloom.yml create mode 100644 .github/workflows/release-drafter-graph.yml rename .github/workflows/{release-drafter.yml => release-drafter-json.yml} (79%) create mode 100644 .github/workflows/release-drafter-search.yml create mode 100644 .github/workflows/release-drafter-time-series.yml diff --git a/.github/release-drafter/bloom-config.yml b/.github/release-drafter/bloom-config.yml new file mode 100644 index 00000000000..7734330b95c --- /dev/null +++ b/.github/release-drafter/bloom-config.yml @@ -0,0 +1,50 @@ +name-template: 'bloom@$NEXT_PATCH_VERSION' +tag-template: 'bloom@$NEXT_PATCH_VERSION' +autolabeler: + - label: 'chore' + files: + - '*.md' + - '.github/*' + - label: 'bug' + branch: + - '/bug-.+' + - label: 'chore' + branch: + - '/chore-.+' + - label: 'feature' + branch: + - '/feature-.+' +categories: + - title: 'Breaking Changes' + labels: + - 'breakingchange' + - title: '🚀 New Features' + labels: + - 'feature' + - 'enhancement' + - title: '🐛 Bug Fixes' + labels: + - 'fix' + - 'bugfix' + - 'bug' + - title: '🧰 Maintenance' + label: + - 'chore' + - 'maintenance' + - 'documentation' + - 'docs' + +change-template: '- $TITLE (#$NUMBER)' +include-paths: + - 'packages/bloom' +exclude-labels: + - 'skip-changelog' +template: | + ## Changes + + $CHANGES + + ## Contributors + We'd like to thank all the contributors who worked on this release! + + $CONTRIBUTORS diff --git a/.github/release-drafter/graph-config.yml b/.github/release-drafter/graph-config.yml new file mode 100644 index 00000000000..88d76b78b55 --- /dev/null +++ b/.github/release-drafter/graph-config.yml @@ -0,0 +1,49 @@ +name-template: 'graph@$NEXT_PATCH_VERSION' +tag-template: 'graph@$NEXT_PATCH_VERSION' +autolabeler: + - label: 'chore' + files: + - '*.md' + - '.github/*' + - label: 'bug' + branch: + - '/bug-.+' + - label: 'chore' + branch: + - '/chore-.+' + - label: 'feature' + branch: + - '/feature-.+' +categories: + - title: 'Breaking Changes' + labels: + - 'breakingchange' + - title: '🚀 New Features' + labels: + - 'feature' + - 'enhancement' + - title: '🐛 Bug Fixes' + labels: + - 'fix' + - 'bugfix' + - 'bug' + - title: '🧰 Maintenance' + label: + - 'chore' + - 'maintenance' + - 'documentation' + - 'docs' +change-template: '- $TITLE (#$NUMBER)' +include-paths: + - 'packages/graph' +exclude-labels: + - 'skip-changelog' +template: | + ## Changes + + $CHANGES + + ## Contributors + We'd like to thank all the contributors who worked on this release! + + $CONTRIBUTORS diff --git a/.github/release-drafter-config.yml b/.github/release-drafter/json-config.yml similarity index 77% rename from .github/release-drafter-config.yml rename to .github/release-drafter/json-config.yml index 9a98b1c08a3..ea259fc0d2d 100644 --- a/.github/release-drafter-config.yml +++ b/.github/release-drafter/json-config.yml @@ -1,5 +1,5 @@ -name-template: 'Version $NEXT_PATCH_VERSION' -tag-template: 'v$NEXT_PATCH_VERSION' +name-template: 'json@$NEXT_PATCH_VERSION' +tag-template: 'json@$NEXT_PATCH_VERSION' autolabeler: - label: 'chore' files: @@ -28,8 +28,15 @@ categories: - 'bugfix' - 'bug' - title: '🧰 Maintenance' - label: 'chore' + label: + - 'chore' + - 'maintenance' + - 'documentation' + - 'docs' + change-template: '- $TITLE (#$NUMBER)' +include-paths: + - 'packages/json' exclude-labels: - 'skip-changelog' template: | diff --git a/.github/release-drafter/search-config.yml b/.github/release-drafter/search-config.yml new file mode 100644 index 00000000000..a78070aa59c --- /dev/null +++ b/.github/release-drafter/search-config.yml @@ -0,0 +1,50 @@ +name-template: 'search@$NEXT_PATCH_VERSION' +tag-template: 'search@$NEXT_PATCH_VERSION' +autolabeler: + - label: 'chore' + files: + - '*.md' + - '.github/*' + - label: 'bug' + branch: + - '/bug-.+' + - label: 'chore' + branch: + - '/chore-.+' + - label: 'feature' + branch: + - '/feature-.+' +categories: + - title: 'Breaking Changes' + labels: + - 'breakingchange' + - title: '🚀 New Features' + labels: + - 'feature' + - 'enhancement' + - title: '🐛 Bug Fixes' + labels: + - 'fix' + - 'bugfix' + - 'bug' + - title: '🧰 Maintenance' + label: + - 'chore' + - 'maintenance' + - 'documentation' + - 'docs' + +change-template: '- $TITLE (#$NUMBER)' +include-paths: + - 'packages/search' +exclude-labels: + - 'skip-changelog' +template: | + ## Changes + + $CHANGES + + ## Contributors + We'd like to thank all the contributors who worked on this release! + + $CONTRIBUTORS diff --git a/.github/release-drafter/time-series-config.yml b/.github/release-drafter/time-series-config.yml new file mode 100644 index 00000000000..29aee0cbc95 --- /dev/null +++ b/.github/release-drafter/time-series-config.yml @@ -0,0 +1,49 @@ +name-template: 'time-series@$NEXT_PATCH_VERSION' +tag-template: 'time-series@$NEXT_PATCH_VERSION' +autolabeler: + - label: 'chore' + files: + - '*.md' + - '.github/*' + - label: 'bug' + branch: + - '/bug-.+' + - label: 'chore' + branch: + - '/chore-.+' + - label: 'feature' + branch: + - '/feature-.+' +categories: + - title: 'Breaking Changes' + labels: + - 'breakingchange' + - title: '🚀 New Features' + labels: + - 'feature' + - 'enhancement' + - title: '🐛 Bug Fixes' + labels: + - 'fix' + - 'bugfix' + - 'bug' + - title: '🧰 Maintenance' + label: + - 'chore' + - 'maintenance' + - 'documentation' + - 'docs' +change-template: '- $TITLE (#$NUMBER)' +include-paths: + - 'packages/time-series' +exclude-labels: + - 'skip-changelog' +template: | + ## Changes + + $CHANGES + + ## Contributors + We'd like to thank all the contributors who worked on this release! + + $CONTRIBUTORS diff --git a/.github/workflows/release-drafter-bloom.yml b/.github/workflows/release-drafter-bloom.yml new file mode 100644 index 00000000000..3b1197e1c97 --- /dev/null +++ b/.github/workflows/release-drafter-bloom.yml @@ -0,0 +1,23 @@ +name: Release Drafter + +on: + push: + # branches to consider in the event; optional, defaults to all + branches: + - master + +jobs: + + update_release_draft: + permissions: + contents: write + pull-requests: write + runs-on: ubuntu-latest + steps: + # Drafts your next Release notes as Pull Requests are merged into "master" + - uses: release-drafter/release-drafter@v5 + with: + # (Optional) specify config name to use, relative to .github/. Default: release-drafter.yml + config-name: release-drafter/bloom-config.yml + env: + GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }} diff --git a/.github/workflows/release-drafter-graph.yml b/.github/workflows/release-drafter-graph.yml new file mode 100644 index 00000000000..8046922178d --- /dev/null +++ b/.github/workflows/release-drafter-graph.yml @@ -0,0 +1,23 @@ +name: Release Drafter + +on: + push: + # branches to consider in the event; optional, defaults to all + branches: + - master + +jobs: + + update_release_draft: + permissions: + contents: write + pull-requests: write + runs-on: ubuntu-latest + steps: + # Drafts your next Release notes as Pull Requests are merged into "master" + - uses: release-drafter/release-drafter@v5 + with: + # (Optional) specify config name to use, relative to .github/. Default: release-drafter.yml + config-name: release-drafter/graph-config.yml + env: + GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }} diff --git a/.github/workflows/release-drafter.yml b/.github/workflows/release-drafter-json.yml similarity index 79% rename from .github/workflows/release-drafter.yml rename to .github/workflows/release-drafter-json.yml index ec2d88bf6e7..b66616d3809 100644 --- a/.github/workflows/release-drafter.yml +++ b/.github/workflows/release-drafter-json.yml @@ -7,13 +7,17 @@ on: - master jobs: + update_release_draft: + permissions: + contents: write + pull-requests: write runs-on: ubuntu-latest steps: # Drafts your next Release notes as Pull Requests are merged into "master" - uses: release-drafter/release-drafter@v5 with: # (Optional) specify config name to use, relative to .github/. Default: release-drafter.yml - config-name: release-drafter-config.yml + config-name: release-drafter/json-config.yml env: GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }} diff --git a/.github/workflows/release-drafter-search.yml b/.github/workflows/release-drafter-search.yml new file mode 100644 index 00000000000..821b748076c --- /dev/null +++ b/.github/workflows/release-drafter-search.yml @@ -0,0 +1,23 @@ +name: Release Drafter + +on: + push: + # branches to consider in the event; optional, defaults to all + branches: + - master + +jobs: + + update_release_draft: + permissions: + contents: write + pull-requests: write + runs-on: ubuntu-latest + steps: + # Drafts your next Release notes as Pull Requests are merged into "master" + - uses: release-drafter/release-drafter@v5 + with: + # (Optional) specify config name to use, relative to .github/. Default: release-drafter.yml + config-name: release-drafter/search-config.yml + env: + GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }} diff --git a/.github/workflows/release-drafter-time-series.yml b/.github/workflows/release-drafter-time-series.yml new file mode 100644 index 00000000000..1a9555e6468 --- /dev/null +++ b/.github/workflows/release-drafter-time-series.yml @@ -0,0 +1,23 @@ +name: Release Drafter + +on: + push: + # branches to consider in the event; optional, defaults to all + branches: + - master + +jobs: + + update_release_draft: + permissions: + contents: write + pull-requests: write + runs-on: ubuntu-latest + steps: + # Drafts your next Release notes as Pull Requests are merged into "master" + - uses: release-drafter/release-drafter@v5 + with: + # (Optional) specify config name to use, relative to .github/. Default: release-drafter.yml + config-name: release-drafter/time-series-config.yml + env: + GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }} From 759e83742b9c354f8df4f8029a2fa403c7365007 Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Tue, 16 May 2023 07:04:36 -0400 Subject: [PATCH 1363/1748] Bump vm2 from 3.9.14 to 3.9.18 (#2509) Bumps [vm2](https://github.com/patriksimek/vm2) from 3.9.14 to 3.9.18. - [Release notes](https://github.com/patriksimek/vm2/releases) - [Changelog](https://github.com/patriksimek/vm2/blob/master/CHANGELOG.md) - [Commits](https://github.com/patriksimek/vm2/compare/3.9.14...3.9.18) --- updated-dependencies: - dependency-name: vm2 dependency-type: indirect ... Signed-off-by: dependabot[bot] Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com> --- package-lock.json | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/package-lock.json b/package-lock.json index 49d7aea5d14..ff1033680a7 100644 --- a/package-lock.json +++ b/package-lock.json @@ -7780,9 +7780,9 @@ "dev": true }, "node_modules/vm2": { - "version": "3.9.14", - "resolved": "https://registry.npmjs.org/vm2/-/vm2-3.9.14.tgz", - "integrity": "sha512-HgvPHYHeQy8+QhzlFryvSteA4uQLBCOub02mgqdR+0bN/akRZ48TGB1v0aCv7ksyc0HXx16AZtMHKS38alc6TA==", + "version": "3.9.18", + "resolved": "https://registry.npmjs.org/vm2/-/vm2-3.9.18.tgz", + "integrity": "sha512-iM7PchOElv6Uv6Q+0Hq7dcgDtWWT6SizYqVcvol+1WQc+E9HlgTCnPozbQNSP3yDV9oXHQOEQu530w2q/BCVZg==", "dev": true, "dependencies": { "acorn": "^8.7.0", From 67d9c3c46a6112fbb8b878c8708966433ad0ed45 Mon Sep 17 00:00:00 2001 From: Chayim Date: Tue, 16 May 2023 14:15:53 +0300 Subject: [PATCH 1364/1748] syntax (#2510) --- .github/workflows/release-drafter-bloom.yml | 7 ++++--- .github/workflows/release-drafter-graph.yml | 7 ++++--- .github/workflows/release-drafter-json.yml | 7 ++++--- .github/workflows/release-drafter-search.yml | 7 ++++--- .github/workflows/release-drafter-time-series.yml | 7 ++++--- 5 files changed, 20 insertions(+), 15 deletions(-) diff --git a/.github/workflows/release-drafter-bloom.yml b/.github/workflows/release-drafter-bloom.yml index 3b1197e1c97..4ad525c058f 100644 --- a/.github/workflows/release-drafter-bloom.yml +++ b/.github/workflows/release-drafter-bloom.yml @@ -9,9 +9,10 @@ on: jobs: update_release_draft: - permissions: - contents: write - pull-requests: write + + permissions: + contents: write + pull-requests: write runs-on: ubuntu-latest steps: # Drafts your next Release notes as Pull Requests are merged into "master" diff --git a/.github/workflows/release-drafter-graph.yml b/.github/workflows/release-drafter-graph.yml index 8046922178d..4d664e5f19e 100644 --- a/.github/workflows/release-drafter-graph.yml +++ b/.github/workflows/release-drafter-graph.yml @@ -9,9 +9,10 @@ on: jobs: update_release_draft: - permissions: - contents: write - pull-requests: write + + permissions: + contents: write + pull-requests: write runs-on: ubuntu-latest steps: # Drafts your next Release notes as Pull Requests are merged into "master" diff --git a/.github/workflows/release-drafter-json.yml b/.github/workflows/release-drafter-json.yml index b66616d3809..a8b3ba4d135 100644 --- a/.github/workflows/release-drafter-json.yml +++ b/.github/workflows/release-drafter-json.yml @@ -9,9 +9,10 @@ on: jobs: update_release_draft: - permissions: - contents: write - pull-requests: write + + permissions: + contents: write + pull-requests: write runs-on: ubuntu-latest steps: # Drafts your next Release notes as Pull Requests are merged into "master" diff --git a/.github/workflows/release-drafter-search.yml b/.github/workflows/release-drafter-search.yml index 821b748076c..c331430353f 100644 --- a/.github/workflows/release-drafter-search.yml +++ b/.github/workflows/release-drafter-search.yml @@ -9,9 +9,10 @@ on: jobs: update_release_draft: - permissions: - contents: write - pull-requests: write + + permissions: + contents: write + pull-requests: write runs-on: ubuntu-latest steps: # Drafts your next Release notes as Pull Requests are merged into "master" diff --git a/.github/workflows/release-drafter-time-series.yml b/.github/workflows/release-drafter-time-series.yml index 1a9555e6468..71e44a70fd3 100644 --- a/.github/workflows/release-drafter-time-series.yml +++ b/.github/workflows/release-drafter-time-series.yml @@ -9,9 +9,10 @@ on: jobs: update_release_draft: - permissions: - contents: write - pull-requests: write + + permissions: + contents: write + pull-requests: write runs-on: ubuntu-latest steps: # Drafts your next Release notes as Pull Requests are merged into "master" From 8d37c115e6f6d709bc57a845de6e158ec71eb93c Mon Sep 17 00:00:00 2001 From: Nanak <36790357+Nanak360@users.noreply.github.com> Date: Sun, 21 May 2023 17:31:17 +0530 Subject: [PATCH 1365/1748] Add support for `TIMEOUT` in `FT.AGGREGATE` and `FT.SEARCH` (#2488) * #2486: add timeout as optional param in FT.Search * return timeout from aggregate * add test case for TIMEOUT in aggregate * add TIMEOUT option in search file * add test cases for TIMEOUT option in search file * uodate search/aggregates to add timeout when it is not undefuned * update search to add timeout when it is not undefuned * update test case for AGGREGATE --- packages/search/lib/commands/AGGREGATE.spec.ts | 7 +++++++ packages/search/lib/commands/AGGREGATE.ts | 5 +++++ packages/search/lib/commands/SEARCH.spec.ts | 9 +++++++++ packages/search/lib/commands/SEARCH.ts | 1 + packages/search/lib/commands/index.ts | 4 ++++ 5 files changed, 26 insertions(+) diff --git a/packages/search/lib/commands/AGGREGATE.spec.ts b/packages/search/lib/commands/AGGREGATE.spec.ts index a2330076438..d1e4565339a 100644 --- a/packages/search/lib/commands/AGGREGATE.spec.ts +++ b/packages/search/lib/commands/AGGREGATE.spec.ts @@ -454,6 +454,13 @@ describe('AGGREGATE', () => { ['FT.AGGREGATE', 'index', '*', 'DIALECT', '1'] ); }); + + it('with TIMEOUT', () => { + assert.deepEqual( + transformArguments('index', '*', { TIMEOUT: 10 }), + ['FT.AGGREGATE', 'index', '*', 'TIMEOUT', '10'] + ); + }); }); testUtils.testWithClient('client.ft.aggregate', async client => { diff --git a/packages/search/lib/commands/AGGREGATE.ts b/packages/search/lib/commands/AGGREGATE.ts index c32d20b0b1c..950d959243a 100644 --- a/packages/search/lib/commands/AGGREGATE.ts +++ b/packages/search/lib/commands/AGGREGATE.ts @@ -124,6 +124,7 @@ export interface AggregateOptions { STEPS?: Array; PARAMS?: Params; DIALECT?: number; + TIMEOUT?: number; } export const FIRST_KEY_INDEX = 1; @@ -213,6 +214,10 @@ export function pushAggregatehOptions( args.push('DIALECT', options.DIALECT.toString()); } + if (options?.TIMEOUT !== undefined) { + args.push('TIMEOUT', options.TIMEOUT.toString()); + } + return args; } diff --git a/packages/search/lib/commands/SEARCH.spec.ts b/packages/search/lib/commands/SEARCH.spec.ts index 47c82cf892b..931458b3a25 100644 --- a/packages/search/lib/commands/SEARCH.spec.ts +++ b/packages/search/lib/commands/SEARCH.spec.ts @@ -233,6 +233,15 @@ describe('SEARCH', () => { ['FT.SEARCH', 'index', 'query', 'DIALECT', '1'] ); }); + + it('with TIMEOUT', () => { + assert.deepEqual( + transformArguments('index', 'query', { + TIMEOUT: 5 + }), + ['FT.SEARCH', 'index', 'query', 'TIMEOUT', '5'] + ); + }); }); describe('client.ft.search', () => { diff --git a/packages/search/lib/commands/SEARCH.ts b/packages/search/lib/commands/SEARCH.ts index fbccb25058b..ef6c0d5c2d1 100644 --- a/packages/search/lib/commands/SEARCH.ts +++ b/packages/search/lib/commands/SEARCH.ts @@ -55,6 +55,7 @@ export interface SearchOptions { }; PARAMS?: Params; DIALECT?: number; + TIMEOUT?: number; } export function transformArguments( diff --git a/packages/search/lib/commands/index.ts b/packages/search/lib/commands/index.ts index d56db7bdbbe..0470aff213f 100644 --- a/packages/search/lib/commands/index.ts +++ b/packages/search/lib/commands/index.ts @@ -510,6 +510,10 @@ export function pushSearchOptions( args.preserve = true; } + if (options?.TIMEOUT !== undefined) { + args.push('TIMEOUT', options.TIMEOUT.toString()); + } + return args; } From d4f194352f52f545e5e5747b9b7a14fa05db2688 Mon Sep 17 00:00:00 2001 From: Codrin-Mihai Chira <122961778+codrin-ch@users.noreply.github.com> Date: Sun, 21 May 2023 15:06:46 +0300 Subject: [PATCH 1366/1748] `XINFO CONSUMERS` - add support for the `inactive` field (#2490) * Support XINFO CONSUMERS: Added the inactive field * Update XINFO_CONSUMERS.ts --------- Co-authored-by: Leibale Eidelman --- packages/client/lib/commands/XINFO_CONSUMERS.spec.ts | 10 ++++++---- packages/client/lib/commands/XINFO_CONSUMERS.ts | 4 +++- 2 files changed, 9 insertions(+), 5 deletions(-) diff --git a/packages/client/lib/commands/XINFO_CONSUMERS.spec.ts b/packages/client/lib/commands/XINFO_CONSUMERS.spec.ts index 87c82b34f29..a2c58999773 100644 --- a/packages/client/lib/commands/XINFO_CONSUMERS.spec.ts +++ b/packages/client/lib/commands/XINFO_CONSUMERS.spec.ts @@ -13,17 +13,19 @@ describe('XINFO CONSUMERS', () => { it('transformReply', () => { assert.deepEqual( transformReply([ - ['name', 'Alice', 'pending', 1, 'idle', 9104628], - ['name', 'Bob', 'pending', 1, 'idle', 83841983] + ['name', 'Alice', 'pending', 1, 'idle', 9104628, 'inactive', 9281221], + ['name', 'Bob', 'pending', 1, 'idle', 83841983, 'inactive', 7213871] ]), [{ name: 'Alice', pending: 1, - idle: 9104628 + idle: 9104628, + inactive: 9281221, }, { name: 'Bob', pending: 1, - idle: 83841983 + idle: 83841983, + inactive: 7213871, }] ); }); diff --git a/packages/client/lib/commands/XINFO_CONSUMERS.ts b/packages/client/lib/commands/XINFO_CONSUMERS.ts index 05e3a26b172..9b3893cc93c 100644 --- a/packages/client/lib/commands/XINFO_CONSUMERS.ts +++ b/packages/client/lib/commands/XINFO_CONSUMERS.ts @@ -15,12 +15,14 @@ type XInfoConsumersReply = Array<{ name: RedisCommandArgument; pending: number; idle: number; + inactive: number; }>; export function transformReply(rawReply: Array): XInfoConsumersReply { return rawReply.map(consumer => ({ name: consumer[1], pending: consumer[3], - idle: consumer[5] + idle: consumer[5], + inactive: consumer[7] })); } From 85091cde5a6d4f9dffb44f706e3bbd90300a0c5a Mon Sep 17 00:00:00 2001 From: fast-facts <107660271+fast-facts@users.noreply.github.com> Date: Sun, 21 May 2023 08:08:27 -0400 Subject: [PATCH 1367/1748] Add `count` option to `FT.CURSOR READ` (#2492) * feat: Add count option to FT.CURSOR READ * Update CURSOR_READ.spec.ts --------- Co-authored-by: Leibale Eidelman --- .../search/lib/commands/CURSOR_READ.spec.ts | 21 +++++++++++++------ packages/search/lib/commands/CURSOR_READ.ts | 15 +++++++++++-- 2 files changed, 28 insertions(+), 8 deletions(-) diff --git a/packages/search/lib/commands/CURSOR_READ.spec.ts b/packages/search/lib/commands/CURSOR_READ.spec.ts index 5b4f4122d49..bb68e2b6396 100644 --- a/packages/search/lib/commands/CURSOR_READ.spec.ts +++ b/packages/search/lib/commands/CURSOR_READ.spec.ts @@ -4,15 +4,24 @@ import testUtils, { GLOBAL } from '../test-utils'; import { transformArguments } from './CURSOR_READ'; describe('CURSOR READ', () => { - it('transformArguments', () => { - assert.deepEqual( - transformArguments('index', 0), - ['FT.CURSOR', 'READ', 'index', '0'] - ); + describe('transformArguments', () => { + it('without options', () => { + assert.deepEqual( + transformArguments('index', 0), + ['FT.CURSOR', 'READ', 'index', '0'] + ); + }); + + it('with COUNT', () => { + assert.deepEqual( + transformArguments('index', 0, { COUNT: 1 }), + ['FT.CURSOR', 'READ', 'index', '0', 'COUNT', '1'] + ); + }); }); testUtils.testWithClient('client.ft.cursorRead', async client => { - const [ ,, { cursor } ] = await Promise.all([ + const [, , { cursor }] = await Promise.all([ client.ft.create('idx', { field: { type: SchemaFieldTypes.TEXT diff --git a/packages/search/lib/commands/CURSOR_READ.ts b/packages/search/lib/commands/CURSOR_READ.ts index 1e828cc3e46..35cf1bc4f06 100644 --- a/packages/search/lib/commands/CURSOR_READ.ts +++ b/packages/search/lib/commands/CURSOR_READ.ts @@ -4,16 +4,27 @@ export const FIRST_KEY_INDEX = 1; export const IS_READ_ONLY = true; +interface CursorReadOptions { + COUNT?: number; +} + export function transformArguments( index: RedisCommandArgument, - cursor: number + cursor: number, + options?: CursorReadOptions ): RedisCommandArguments { - return [ + const args = [ 'FT.CURSOR', 'READ', index, cursor.toString() ]; + + if (options?.COUNT) { + args.push('COUNT', options.COUNT.toString()); + } + + return args; } export { transformReply } from './AGGREGATE_WITHCURSOR'; From 499ea855947ff99d059c64da03ce40cdffd0f0c8 Mon Sep 17 00:00:00 2001 From: Leibale Eidelman Date: Mon, 29 May 2023 07:18:59 -0400 Subject: [PATCH 1368/1748] Update search-hashes.js (#2519) --- examples/search-hashes.js | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/examples/search-hashes.js b/examples/search-hashes.js index 85e6106a99a..2f8b5fbf7b6 100644 --- a/examples/search-hashes.js +++ b/examples/search-hashes.js @@ -13,7 +13,7 @@ try { await client.ft.create('idx:animals', { name: { type: SchemaFieldTypes.TEXT, - sortable: true + SORTABLE: true }, species: SchemaFieldTypes.TAG, age: SchemaFieldTypes.NUMERIC From e696653bf9ae40380866b482cfaec64d6ef80f94 Mon Sep 17 00:00:00 2001 From: Kumar Arnav <36244620+Karnav123@users.noreply.github.com> Date: Mon, 29 May 2023 18:30:58 +0530 Subject: [PATCH 1369/1748] Add support for `LATENCY LATEST` (#2514) * Add support for LATENCY LATEST. * Fix the review comments. * Fix the review comments. * Update LATENCY_LATEST.ts * Update dockers.ts * Update LATENCY_GRAPH.spec.ts * enable debug mode in tests --------- Co-authored-by: Leibale Eidelman --- packages/client/lib/client/commands.ts | 3 +++ .../client/lib/commands/LATENCY_GRAPH.spec.ts | 6 +---- .../lib/commands/LATENCY_LATEST.spec.ts | 27 +++++++++++++++++++ .../client/lib/commands/LATENCY_LATEST.ts | 12 +++++++++ packages/client/lib/test-utils.ts | 22 +++++++++------ 5 files changed, 57 insertions(+), 13 deletions(-) create mode 100644 packages/client/lib/commands/LATENCY_LATEST.spec.ts create mode 100644 packages/client/lib/commands/LATENCY_LATEST.ts diff --git a/packages/client/lib/client/commands.ts b/packages/client/lib/client/commands.ts index 2605962432a..1e2e5274c0a 100644 --- a/packages/client/lib/client/commands.ts +++ b/packages/client/lib/client/commands.ts @@ -84,6 +84,7 @@ import * as KEYS from '../commands/KEYS'; import * as LASTSAVE from '../commands/LASTSAVE'; import * as LATENCY_DOCTOR from '../commands/LATENCY_DOCTOR'; import * as LATENCY_GRAPH from '../commands/LATENCY_GRAPH'; +import * as LATENCY_LATEST from '../commands/LATENCY_LATEST'; import * as LOLWUT from '../commands/LOLWUT'; import * as MEMORY_DOCTOR from '../commands/MEMORY_DOCTOR'; import * as MEMORY_MALLOC_STATS from '../commands/MEMORY_MALLOC-STATS'; @@ -290,6 +291,8 @@ export default { latencyDoctor: LATENCY_DOCTOR, LATENCY_GRAPH, latencyGraph: LATENCY_GRAPH, + LATENCY_LATEST, + latencyLatest: LATENCY_LATEST, LOLWUT, lolwut: LOLWUT, MEMORY_DOCTOR, diff --git a/packages/client/lib/commands/LATENCY_GRAPH.spec.ts b/packages/client/lib/commands/LATENCY_GRAPH.spec.ts index df4d5d466ab..21755a253b3 100644 --- a/packages/client/lib/commands/LATENCY_GRAPH.spec.ts +++ b/packages/client/lib/commands/LATENCY_GRAPH.spec.ts @@ -24,9 +24,5 @@ describe('LATENCY GRAPH', () => { typeof await client.latencyGraph('command'), 'string' ); - }, { - serverArguments: testUtils.isVersionGreaterThan([7]) ? - ['--enable-debug-command', 'yes'] : - GLOBAL.SERVERS.OPEN.serverArguments - }); + }, GLOBAL.SERVERS.OPEN); }); diff --git a/packages/client/lib/commands/LATENCY_LATEST.spec.ts b/packages/client/lib/commands/LATENCY_LATEST.spec.ts new file mode 100644 index 00000000000..4087f212139 --- /dev/null +++ b/packages/client/lib/commands/LATENCY_LATEST.spec.ts @@ -0,0 +1,27 @@ +import {strict as assert} from 'assert'; +import testUtils, {GLOBAL} from '../test-utils'; +import { transformArguments } from './LATENCY_LATEST'; + +describe('LATENCY LATEST', () => { + it('transformArguments', () => { + assert.deepEqual( + transformArguments(), + ['LATENCY', 'LATEST'] + ); + }); + + testUtils.testWithClient('client.latencyLatest', async client => { + await Promise.all([ + client.configSet('latency-monitor-threshold', '100'), + client.sendCommand(['DEBUG', 'SLEEP', '1']) + ]); + const latency = await client.latencyLatest(); + assert.ok(Array.isArray(latency)); + for (const [name, timestamp, latestLatency, allTimeLatency] of latency) { + assert.equal(typeof name, 'string'); + assert.equal(typeof timestamp, 'number'); + assert.equal(typeof latestLatency, 'number'); + assert.equal(typeof allTimeLatency, 'number'); + } + }, GLOBAL.SERVERS.OPEN); +}); diff --git a/packages/client/lib/commands/LATENCY_LATEST.ts b/packages/client/lib/commands/LATENCY_LATEST.ts new file mode 100644 index 00000000000..3e4dd6236c6 --- /dev/null +++ b/packages/client/lib/commands/LATENCY_LATEST.ts @@ -0,0 +1,12 @@ +import { RedisCommandArguments } from '.'; + +export function transformArguments(): RedisCommandArguments { + return ['LATENCY', 'LATEST']; +} + +export declare function transformReply(): Array<[ + name: string, + timestamp: number, + latestLatency: number, + allTimeLatency: number +]>; diff --git a/packages/client/lib/test-utils.ts b/packages/client/lib/test-utils.ts index 9032f1dd344..65d526f6019 100644 --- a/packages/client/lib/test-utils.ts +++ b/packages/client/lib/test-utils.ts @@ -2,18 +2,24 @@ import TestUtils from '@redis/test-utils'; import { SinonSpy } from 'sinon'; import { promiseTimeout } from './utils'; -export default new TestUtils({ - dockerImageName: 'redis', - dockerImageVersionArgument: 'redis-version' +const utils = new TestUtils({ + dockerImageName: 'redis', + dockerImageVersionArgument: 'redis-version' }); +export default utils; + +const DEBUG_MODE_ARGS = utils.isVersionGreaterThan([7]) ? + ['--enable-debug-command', 'yes'] : + []; + export const GLOBAL = { SERVERS: { OPEN: { - serverArguments: [] + serverArguments: [...DEBUG_MODE_ARGS] }, PASSWORD: { - serverArguments: ['--requirepass', 'password'], + serverArguments: ['--requirepass', 'password', ...DEBUG_MODE_ARGS], clientOptions: { password: 'password' } @@ -21,10 +27,10 @@ export const GLOBAL = { }, CLUSTERS: { OPEN: { - serverArguments: [] + serverArguments: [...DEBUG_MODE_ARGS] }, PASSWORD: { - serverArguments: ['--requirepass', 'password'], + serverArguments: ['--requirepass', 'password', ...DEBUG_MODE_ARGS], clusterConfiguration: { defaults: { password: 'password' @@ -32,7 +38,7 @@ export const GLOBAL = { } }, WITH_REPLICAS: { - serverArguments: [], + serverArguments: [...DEBUG_MODE_ARGS], numberOfMasters: 2, numberOfReplicas: 1, clusterConfiguration: { From 100a5232ac9af3032c1fa039dd87617ef562c482 Mon Sep 17 00:00:00 2001 From: Leibale Eidelman Date: Mon, 29 May 2023 09:01:22 -0400 Subject: [PATCH 1370/1748] use `setImmediate` instead of `queueMicrotask` (to improve performance) (#2516) --- docs/FAQ.md | 2 +- packages/client/lib/client/socket.ts | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/docs/FAQ.md b/docs/FAQ.md index 4b7710df3fc..5774213da5b 100644 --- a/docs/FAQ.md +++ b/docs/FAQ.md @@ -10,7 +10,7 @@ If don't want to queue commands in memory until a new socket is established, set ## How are commands batched? -Commands are pipelined using [`queueMicrotask`](https://nodejs.org/api/globals.html#globals_queuemicrotask_callback). +Commands are pipelined using [`setImmediate`](https://nodejs.org/api/timers.html#setimmediatecallback-args). If `socket.write()` returns `false`—meaning that ["all or part of the data was queued in user memory"](https://nodejs.org/api/net.html#net_socket_write_data_encoding_callback:~:text=all%20or%20part%20of%20the%20data%20was%20queued%20in%20user%20memory)—the commands will stack in memory until the [`drain`](https://nodejs.org/api/net.html#net_event_drain) event is fired. diff --git a/packages/client/lib/client/socket.ts b/packages/client/lib/client/socket.ts index 89533350f59..b079a51fea9 100644 --- a/packages/client/lib/client/socket.ts +++ b/packages/client/lib/client/socket.ts @@ -291,7 +291,7 @@ export default class RedisSocket extends EventEmitter { this.#socket.cork(); this.#isCorked = true; - queueMicrotask(() => { + setImmediate(() => { this.#socket?.uncork(); this.#isCorked = false; }); From 0298c1ac4122a356e4cad8970fd4d9a2dafb6acb Mon Sep 17 00:00:00 2001 From: Leibale Eidelman Date: Mon, 29 May 2023 09:01:34 -0400 Subject: [PATCH 1371/1748] upgrade deps (#2517) * upgrade deps * fix graph tests --- benchmark/package-lock.json | 289 +------- benchmark/package.json | 4 +- package-lock.json | 1123 +++++++++++++++++++---------- packages/bloom/package.json | 8 +- packages/client/package.json | 18 +- packages/graph/lib/graph.spec.ts | 6 +- packages/graph/package.json | 8 +- packages/json/package.json | 8 +- packages/search/package.json | 8 +- packages/test-utils/package.json | 8 +- packages/time-series/package.json | 8 +- 11 files changed, 813 insertions(+), 675 deletions(-) diff --git a/benchmark/package-lock.json b/benchmark/package-lock.json index 828b42803eb..441c0b07b67 100644 --- a/benchmark/package-lock.json +++ b/benchmark/package-lock.json @@ -1,6 +1,6 @@ { "name": "@redis/client-benchmark", - "lockfileVersion": 2, + "lockfileVersion": 3, "requires": true, "packages": { "": { @@ -8,9 +8,9 @@ "dependencies": { "@redis/client": "../packages/client", "hdr-histogram-js": "3.0.0", - "ioredis": "5.3.0", + "ioredis": "5.3.2", "redis-v3": "npm:redis@3.1.2", - "yargs": "17.6.2" + "yargs": "17.7.2" } }, "node_modules/@assemblyscript/loader": { @@ -19,12 +19,12 @@ "integrity": "sha512-ulkCYfFbYj01ie1MDOyxv2F6SpRN1TOj7fQxbP07D6HmeR+gr2JLSmINKjga2emB+b1L2KGrFKBTc+e00p54nw==" }, "node_modules/@ioredis/commands": { - "version": "1.1.1", - "resolved": "https://registry.npmjs.org/@ioredis/commands/-/commands-1.1.1.tgz", - "integrity": "sha512-fsR4P/ROllzf/7lXYyElUJCheWdTJVJvOTps8v9IWKFATxR61ANOlnoPqhH099xYLrJGpc2ZQ28B3rMeUt5VQg==" + "version": "1.2.0", + "resolved": "https://registry.npmjs.org/@ioredis/commands/-/commands-1.2.0.tgz", + "integrity": "sha512-Sx1pU8EM64o2BrqNpEO1CNLtKQwyhuXuqyfH7oGKCk+1a33d2r5saW8zNwm3j6BTExtjrv2BxTgzzkMwts6vGg==" }, "node_modules/@redis/client": { - "version": "1.5.0", + "version": "1.5.7", "resolved": "file:../packages/client", "license": "MIT", "dependencies": { @@ -181,9 +181,9 @@ } }, "node_modules/ioredis": { - "version": "5.3.0", - "resolved": "https://registry.npmjs.org/ioredis/-/ioredis-5.3.0.tgz", - "integrity": "sha512-Id9jKHhsILuIZpHc61QkagfVdUj2Rag5GzG1TGEvRNeM7dtTOjICgjC+tvqYxi//PuX2wjQ+Xjva2ONBuf92Pw==", + "version": "5.3.2", + "resolved": "https://registry.npmjs.org/ioredis/-/ioredis-5.3.2.tgz", + "integrity": "sha512-1DKMMzlIHM02eBBVOFQ1+AolGjs6+xEcM4PDL7NqOS6szq7H9jSaEkIUH6/a5Hl241LzW6JLSiAbNvTQjUupUA==", "dependencies": { "@ioredis/commands": "^1.1.1", "cluster-key-slot": "^1.1.0", @@ -214,12 +214,12 @@ "node_modules/lodash.defaults": { "version": "4.2.0", "resolved": "https://registry.npmjs.org/lodash.defaults/-/lodash.defaults-4.2.0.tgz", - "integrity": "sha1-0JF4cW/+pN3p5ft7N/bwgCJ0WAw=" + "integrity": "sha512-qjxPLHd3r5DnsdGacqOMU6pb/avJzdh9tFX2ymgoZE27BmjXrNy/y4LoaiTeAb+O3gL8AfpJGtqfX/ae2leYYQ==" }, "node_modules/lodash.isarguments": { "version": "3.1.0", "resolved": "https://registry.npmjs.org/lodash.isarguments/-/lodash.isarguments-3.1.0.tgz", - "integrity": "sha1-L1c9hcaiQon/AGY7SRwdM4/zRYo=" + "integrity": "sha512-chi4NHZlZqZD18a0imDHnZPrDeBbTtVN7GXMwuGdRH9qotxAjYs3aVLKc7zNOG9eddR5Ksd8rvFEBc9SsggPpg==" }, "node_modules/ms": { "version": "2.1.2", @@ -239,7 +239,7 @@ "node_modules/redis-errors": { "version": "1.2.0", "resolved": "https://registry.npmjs.org/redis-errors/-/redis-errors-1.2.0.tgz", - "integrity": "sha1-62LSrbFeTq9GEMBK/hUpOEJQq60=", + "integrity": "sha512-1qny3OExCf0UvUV/5wpYKf2YwPcOqXzkwKKSmKHiE6ZMQs5heeE/c8eXK+PNllPvmjgAbfnsbpkGZWy8cBpn9w==", "engines": { "node": ">=4" } @@ -247,7 +247,7 @@ "node_modules/redis-parser": { "version": "3.0.0", "resolved": "https://registry.npmjs.org/redis-parser/-/redis-parser-3.0.0.tgz", - "integrity": "sha1-tm2CjNyv5rS4pCin3vTGvKwxyLQ=", + "integrity": "sha512-DJnGAeenTdpMEH6uAJRK/uiyEIH9WVsUmoLwzudwGJUwZPp80PDBWPHXSAGNPwNvIXAbe7MSUB1zQFugFml66A==", "dependencies": { "redis-errors": "^1.0.0" }, @@ -285,7 +285,7 @@ "node_modules/require-directory": { "version": "2.1.1", "resolved": "https://registry.npmjs.org/require-directory/-/require-directory-2.1.1.tgz", - "integrity": "sha1-jGStX9MNqxyXbiNE/+f3kqam30I=", + "integrity": "sha512-fGxEI7+wsG9xrvdjsrlmL22OMTTiHRwAMroiEeMgq8gzoLC/PQr7RsRDSTLUg/bZAZtF+TVIkHc6/4RIKrui+Q==", "engines": { "node": ">=0.10.0" } @@ -349,9 +349,9 @@ "integrity": "sha512-3wdGidZyq5PB084XLES5TpOSRA3wjXAlIWMhum2kRcv/41Sn2emQ0dycQW4uZXLejwKvg6EsvbdlVL+FYEct7A==" }, "node_modules/yargs": { - "version": "17.6.2", - "resolved": "https://registry.npmjs.org/yargs/-/yargs-17.6.2.tgz", - "integrity": "sha512-1/9UrdHjDZc0eOU0HxOHoS78C69UD3JRMvzlJ7S79S2nTaWRA/whGCTV8o9e/N/1Va9YIV7Q4sOxD8VV4pCWOw==", + "version": "17.7.2", + "resolved": "https://registry.npmjs.org/yargs/-/yargs-17.7.2.tgz", + "integrity": "sha512-7dSzzRQ++CKnNI/krKnYRV7JKKPUXMEh61soaHKg9mrWEhzFWhFnxPxGl+69cD1Ou63C13NUPCnmIcrvqCuM6w==", "dependencies": { "cliui": "^8.0.1", "escalade": "^3.1.1", @@ -373,258 +373,5 @@ "node": ">=12" } } - }, - "dependencies": { - "@assemblyscript/loader": { - "version": "0.19.23", - "resolved": "https://registry.npmjs.org/@assemblyscript/loader/-/loader-0.19.23.tgz", - "integrity": "sha512-ulkCYfFbYj01ie1MDOyxv2F6SpRN1TOj7fQxbP07D6HmeR+gr2JLSmINKjga2emB+b1L2KGrFKBTc+e00p54nw==" - }, - "@ioredis/commands": { - "version": "1.1.1", - "resolved": "https://registry.npmjs.org/@ioredis/commands/-/commands-1.1.1.tgz", - "integrity": "sha512-fsR4P/ROllzf/7lXYyElUJCheWdTJVJvOTps8v9IWKFATxR61ANOlnoPqhH099xYLrJGpc2ZQ28B3rMeUt5VQg==" - }, - "@redis/client": { - "version": "1.5.0", - "requires": { - "cluster-key-slot": "1.1.2", - "generic-pool": "3.9.0", - "yallist": "4.0.0" - } - }, - "ansi-regex": { - "version": "5.0.1", - "resolved": "https://registry.npmjs.org/ansi-regex/-/ansi-regex-5.0.1.tgz", - "integrity": "sha512-quJQXlTSUGL2LH9SUXo8VwsY4soanhgo6LNSm84E1LBcE8s3O0wpdiRzyR9z/ZZJMlMWv37qOOb9pdJlMUEKFQ==" - }, - "ansi-styles": { - "version": "4.3.0", - "resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-4.3.0.tgz", - "integrity": "sha512-zbB9rCJAT1rbjiVDb2hqKFHNYLxgtk8NURxZ3IZwD3F6NtxbXZQCnnSi1Lkx+IDohdPlFp222wVALIheZJQSEg==", - "requires": { - "color-convert": "^2.0.1" - } - }, - "base64-js": { - "version": "1.5.1", - "resolved": "https://registry.npmjs.org/base64-js/-/base64-js-1.5.1.tgz", - "integrity": "sha512-AKpaYlHn8t4SVbOHCy+b5+KKgvR4vrsD8vbvrbiQJps7fKDTkjkDry6ji0rUJjC0kzbNePLwzxq8iypo41qeWA==" - }, - "cliui": { - "version": "8.0.1", - "resolved": "https://registry.npmjs.org/cliui/-/cliui-8.0.1.tgz", - "integrity": "sha512-BSeNnyus75C4//NQ9gQt1/csTXyo/8Sb+afLAkzAptFuMsod9HFokGNudZpi/oQV73hnVK+sR+5PVRMd+Dr7YQ==", - "requires": { - "string-width": "^4.2.0", - "strip-ansi": "^6.0.1", - "wrap-ansi": "^7.0.0" - } - }, - "cluster-key-slot": { - "version": "1.1.2", - "resolved": "https://registry.npmjs.org/cluster-key-slot/-/cluster-key-slot-1.1.2.tgz", - "integrity": "sha512-RMr0FhtfXemyinomL4hrWcYJxmX6deFdCxpJzhDttxgO1+bcCnkk+9drydLVDmAMG7NE6aN/fl4F7ucU/90gAA==" - }, - "color-convert": { - "version": "2.0.1", - "resolved": "https://registry.npmjs.org/color-convert/-/color-convert-2.0.1.tgz", - "integrity": "sha512-RRECPsj7iu/xb5oKYcsFHSppFNnsj/52OVTRKb4zP5onXwVF3zVmmToNcOfGC+CRDpfK/U584fMg38ZHCaElKQ==", - "requires": { - "color-name": "~1.1.4" - } - }, - "color-name": { - "version": "1.1.4", - "resolved": "https://registry.npmjs.org/color-name/-/color-name-1.1.4.tgz", - "integrity": "sha512-dOy+3AuW3a2wNbZHIuMZpTcgjGuLU/uBL/ubcZF9OXbDo8ff4O8yVp5Bf0efS8uEoYo5q4Fx7dY9OgQGXgAsQA==" - }, - "debug": { - "version": "4.3.4", - "resolved": "https://registry.npmjs.org/debug/-/debug-4.3.4.tgz", - "integrity": "sha512-PRWFHuSU3eDtQJPvnNY7Jcket1j0t5OuOsFzPPzsekD52Zl8qUfFIPEiswXqIvHWGVHOgX+7G/vCNNhehwxfkQ==", - "requires": { - "ms": "2.1.2" - } - }, - "denque": { - "version": "2.1.0", - "resolved": "https://registry.npmjs.org/denque/-/denque-2.1.0.tgz", - "integrity": "sha512-HVQE3AAb/pxF8fQAoiqpvg9i3evqug3hoiwakOyZAwJm+6vZehbkYXZ0l4JxS+I3QxM97v5aaRNhj8v5oBhekw==" - }, - "emoji-regex": { - "version": "8.0.0", - "resolved": "https://registry.npmjs.org/emoji-regex/-/emoji-regex-8.0.0.tgz", - "integrity": "sha512-MSjYzcWNOA0ewAHpz0MxpYFvwg6yjy1NG3xteoqz644VCo/RPgnr1/GGt+ic3iJTzQ8Eu3TdM14SawnVUmGE6A==" - }, - "escalade": { - "version": "3.1.1", - "resolved": "https://registry.npmjs.org/escalade/-/escalade-3.1.1.tgz", - "integrity": "sha512-k0er2gUkLf8O0zKJiAhmkTnJlTvINGv7ygDNPbeIsX/TJjGJZHuh9B2UxbsaEkmlEo9MfhrSzmhIlhRlI2GXnw==" - }, - "generic-pool": { - "version": "3.9.0", - "resolved": "https://registry.npmjs.org/generic-pool/-/generic-pool-3.9.0.tgz", - "integrity": "sha512-hymDOu5B53XvN4QT9dBmZxPX4CWhBPPLguTZ9MMFeFa/Kg0xWVfylOVNlJji/E7yTZWFd/q9GO5TxDLq156D7g==" - }, - "get-caller-file": { - "version": "2.0.5", - "resolved": "https://registry.npmjs.org/get-caller-file/-/get-caller-file-2.0.5.tgz", - "integrity": "sha512-DyFP3BM/3YHTQOCUL/w0OZHR0lpKeGrxotcHWcqNEdnltqFwXVfhEBQ94eIo34AfQpo0rGki4cyIiftY06h2Fg==" - }, - "hdr-histogram-js": { - "version": "3.0.0", - "resolved": "https://registry.npmjs.org/hdr-histogram-js/-/hdr-histogram-js-3.0.0.tgz", - "integrity": "sha512-/EpvQI2/Z98mNFYEnlqJ8Ogful8OpArLG/6Tf2bPnkutBVLIeMVNHjk1ZDfshF2BUweipzbk+dB1hgSB7SIakw==", - "requires": { - "@assemblyscript/loader": "^0.19.21", - "base64-js": "^1.2.0", - "pako": "^1.0.3" - } - }, - "ioredis": { - "version": "5.3.0", - "resolved": "https://registry.npmjs.org/ioredis/-/ioredis-5.3.0.tgz", - "integrity": "sha512-Id9jKHhsILuIZpHc61QkagfVdUj2Rag5GzG1TGEvRNeM7dtTOjICgjC+tvqYxi//PuX2wjQ+Xjva2ONBuf92Pw==", - "requires": { - "@ioredis/commands": "^1.1.1", - "cluster-key-slot": "^1.1.0", - "debug": "^4.3.4", - "denque": "^2.1.0", - "lodash.defaults": "^4.2.0", - "lodash.isarguments": "^3.1.0", - "redis-errors": "^1.2.0", - "redis-parser": "^3.0.0", - "standard-as-callback": "^2.1.0" - } - }, - "is-fullwidth-code-point": { - "version": "3.0.0", - "resolved": "https://registry.npmjs.org/is-fullwidth-code-point/-/is-fullwidth-code-point-3.0.0.tgz", - "integrity": "sha512-zymm5+u+sCsSWyD9qNaejV3DFvhCKclKdizYaJUuHA83RLjb7nSuGnddCHGv0hk+KY7BMAlsWeK4Ueg6EV6XQg==" - }, - "lodash.defaults": { - "version": "4.2.0", - "resolved": "https://registry.npmjs.org/lodash.defaults/-/lodash.defaults-4.2.0.tgz", - "integrity": "sha1-0JF4cW/+pN3p5ft7N/bwgCJ0WAw=" - }, - "lodash.isarguments": { - "version": "3.1.0", - "resolved": "https://registry.npmjs.org/lodash.isarguments/-/lodash.isarguments-3.1.0.tgz", - "integrity": "sha1-L1c9hcaiQon/AGY7SRwdM4/zRYo=" - }, - "ms": { - "version": "2.1.2", - "resolved": "https://registry.npmjs.org/ms/-/ms-2.1.2.tgz", - "integrity": "sha512-sGkPx+VjMtmA6MX27oA4FBFELFCZZ4S4XqeGOXCv68tT+jb3vk/RyaKWP0PTKyWtmLSM0b+adUTEvbs1PEaH2w==" - }, - "pako": { - "version": "1.0.11", - "resolved": "https://registry.npmjs.org/pako/-/pako-1.0.11.tgz", - "integrity": "sha512-4hLB8Py4zZce5s4yd9XzopqwVv/yGNhV1Bl8NTmCq1763HeK2+EwVTv+leGeL13Dnh2wfbqowVPXCIO0z4taYw==" - }, - "redis-commands": { - "version": "1.7.0", - "resolved": "https://registry.npmjs.org/redis-commands/-/redis-commands-1.7.0.tgz", - "integrity": "sha512-nJWqw3bTFy21hX/CPKHth6sfhZbdiHP6bTawSgQBlKOVRG7EZkfHbbHwQJnrE4vsQf0CMNE+3gJ4Fmm16vdVlQ==" - }, - "redis-errors": { - "version": "1.2.0", - "resolved": "https://registry.npmjs.org/redis-errors/-/redis-errors-1.2.0.tgz", - "integrity": "sha1-62LSrbFeTq9GEMBK/hUpOEJQq60=" - }, - "redis-parser": { - "version": "3.0.0", - "resolved": "https://registry.npmjs.org/redis-parser/-/redis-parser-3.0.0.tgz", - "integrity": "sha1-tm2CjNyv5rS4pCin3vTGvKwxyLQ=", - "requires": { - "redis-errors": "^1.0.0" - } - }, - "redis-v3": { - "version": "npm:redis@3.1.2", - "resolved": "https://registry.npmjs.org/redis/-/redis-3.1.2.tgz", - "integrity": "sha512-grn5KoZLr/qrRQVwoSkmzdbw6pwF+/rwODtrOr6vuBRiR/f3rjSTGupbF90Zpqm2oenix8Do6RV7pYEkGwlKkw==", - "requires": { - "denque": "^1.5.0", - "redis-commands": "^1.7.0", - "redis-errors": "^1.2.0", - "redis-parser": "^3.0.0" - }, - "dependencies": { - "denque": { - "version": "1.5.1", - "resolved": "https://registry.npmjs.org/denque/-/denque-1.5.1.tgz", - "integrity": "sha512-XwE+iZ4D6ZUB7mfYRMb5wByE8L74HCn30FBN7sWnXksWc1LO1bPDl67pBR9o/kC4z/xSNAwkMYcGgqDV3BE3Hw==" - } - } - }, - "require-directory": { - "version": "2.1.1", - "resolved": "https://registry.npmjs.org/require-directory/-/require-directory-2.1.1.tgz", - "integrity": "sha1-jGStX9MNqxyXbiNE/+f3kqam30I=" - }, - "standard-as-callback": { - "version": "2.1.0", - "resolved": "https://registry.npmjs.org/standard-as-callback/-/standard-as-callback-2.1.0.tgz", - "integrity": "sha512-qoRRSyROncaz1z0mvYqIE4lCd9p2R90i6GxW3uZv5ucSu8tU7B5HXUP1gG8pVZsYNVaXjk8ClXHPttLyxAL48A==" - }, - "string-width": { - "version": "4.2.3", - "resolved": "https://registry.npmjs.org/string-width/-/string-width-4.2.3.tgz", - "integrity": "sha512-wKyQRQpjJ0sIp62ErSZdGsjMJWsap5oRNihHhu6G7JVO/9jIB6UyevL+tXuOqrng8j/cxKTWyWUwvSTriiZz/g==", - "requires": { - "emoji-regex": "^8.0.0", - "is-fullwidth-code-point": "^3.0.0", - "strip-ansi": "^6.0.1" - } - }, - "strip-ansi": { - "version": "6.0.1", - "resolved": "https://registry.npmjs.org/strip-ansi/-/strip-ansi-6.0.1.tgz", - "integrity": "sha512-Y38VPSHcqkFrCpFnQ9vuSXmquuv5oXOKpGeT6aGrr3o3Gc9AlVa6JBfUSOCnbxGGZF+/0ooI7KrPuUSztUdU5A==", - "requires": { - "ansi-regex": "^5.0.1" - } - }, - "wrap-ansi": { - "version": "7.0.0", - "resolved": "https://registry.npmjs.org/wrap-ansi/-/wrap-ansi-7.0.0.tgz", - "integrity": "sha512-YVGIj2kamLSTxw6NsZjoBxfSwsn0ycdesmc4p+Q21c5zPuZ1pl+NfxVdxPtdHvmNVOQ6XSYG4AUtyt/Fi7D16Q==", - "requires": { - "ansi-styles": "^4.0.0", - "string-width": "^4.1.0", - "strip-ansi": "^6.0.0" - } - }, - "y18n": { - "version": "5.0.8", - "resolved": "https://registry.npmjs.org/y18n/-/y18n-5.0.8.tgz", - "integrity": "sha512-0pfFzegeDWJHJIAmTLRP2DwHjdF5s7jo9tuztdQxAhINCdvS+3nGINqPd00AphqJR/0LhANUS6/+7SCb98YOfA==" - }, - "yallist": { - "version": "4.0.0", - "resolved": "https://registry.npmjs.org/yallist/-/yallist-4.0.0.tgz", - "integrity": "sha512-3wdGidZyq5PB084XLES5TpOSRA3wjXAlIWMhum2kRcv/41Sn2emQ0dycQW4uZXLejwKvg6EsvbdlVL+FYEct7A==" - }, - "yargs": { - "version": "17.6.2", - "resolved": "https://registry.npmjs.org/yargs/-/yargs-17.6.2.tgz", - "integrity": "sha512-1/9UrdHjDZc0eOU0HxOHoS78C69UD3JRMvzlJ7S79S2nTaWRA/whGCTV8o9e/N/1Va9YIV7Q4sOxD8VV4pCWOw==", - "requires": { - "cliui": "^8.0.1", - "escalade": "^3.1.1", - "get-caller-file": "^2.0.5", - "require-directory": "^2.1.1", - "string-width": "^4.2.3", - "y18n": "^5.0.5", - "yargs-parser": "^21.1.1" - } - }, - "yargs-parser": { - "version": "21.1.1", - "resolved": "https://registry.npmjs.org/yargs-parser/-/yargs-parser-21.1.1.tgz", - "integrity": "sha512-tVpsJW7DdjecAiFpbIB1e3qxIQsE6NoPc5/eTdrbbIC4h0LVsWhnoa3g+m2HclBIujHzsxZ4VJVA+GUuc2/LBw==" - } } } diff --git a/benchmark/package.json b/benchmark/package.json index 79b0facda83..f46f0e00b22 100644 --- a/benchmark/package.json +++ b/benchmark/package.json @@ -9,8 +9,8 @@ "dependencies": { "@redis/client": "../packages/client", "hdr-histogram-js": "3.0.0", - "ioredis": "5.3.0", + "ioredis": "5.3.2", "redis-v3": "npm:redis@3.1.2", - "yargs": "17.6.2" + "yargs": "17.7.2" } } diff --git a/package-lock.json b/package-lock.json index ff1033680a7..3a67efff081 100644 --- a/package-lock.json +++ b/package-lock.json @@ -445,15 +445,39 @@ "@jridgewell/sourcemap-codec": "^1.4.10" } }, + "node_modules/@eslint-community/eslint-utils": { + "version": "4.4.0", + "resolved": "https://registry.npmjs.org/@eslint-community/eslint-utils/-/eslint-utils-4.4.0.tgz", + "integrity": "sha512-1/sA4dwrzBAyeUoQ6oxahHKmrZvsnLCg4RfxW3ZFGGmQkSNQPFNLV9CUEFQP1x9EYXHTo5p6xdhZM1Ne9p/AfA==", + "dev": true, + "dependencies": { + "eslint-visitor-keys": "^3.3.0" + }, + "engines": { + "node": "^12.22.0 || ^14.17.0 || >=16.0.0" + }, + "peerDependencies": { + "eslint": "^6.0.0 || ^7.0.0 || >=8.0.0" + } + }, + "node_modules/@eslint-community/regexpp": { + "version": "4.5.1", + "resolved": "https://registry.npmjs.org/@eslint-community/regexpp/-/regexpp-4.5.1.tgz", + "integrity": "sha512-Z5ba73P98O1KUYCCJTUeVpja9RcGoMdncZ6T49FCUl2lN38JtCJ+3WgIDBv0AuY4WChU5PmtJmOCTlN6FZTFKQ==", + "dev": true, + "engines": { + "node": "^12.0.0 || ^14.0.0 || >=16.0.0" + } + }, "node_modules/@eslint/eslintrc": { - "version": "1.4.1", - "resolved": "https://registry.npmjs.org/@eslint/eslintrc/-/eslintrc-1.4.1.tgz", - "integrity": "sha512-XXrH9Uarn0stsyldqDYq8r++mROmWRI1xKMXa640Bb//SY1+ECYX6VzT6Lcx5frD0V30XieqJ0oX9I2Xj5aoMA==", + "version": "2.0.3", + "resolved": "https://registry.npmjs.org/@eslint/eslintrc/-/eslintrc-2.0.3.tgz", + "integrity": "sha512-+5gy6OQfk+xx3q0d6jGZZC3f3KzAkXc/IanVxd1is/VIIziRqqt3ongQz0FiTUXqTk0c7aDB3OaFuKnuSoJicQ==", "dev": true, "dependencies": { "ajv": "^6.12.4", "debug": "^4.3.2", - "espree": "^9.4.0", + "espree": "^9.5.2", "globals": "^13.19.0", "ignore": "^5.2.0", "import-fresh": "^3.2.1", @@ -468,6 +492,15 @@ "url": "https://opencollective.com/eslint" } }, + "node_modules/@eslint/js": { + "version": "8.41.0", + "resolved": "https://registry.npmjs.org/@eslint/js/-/js-8.41.0.tgz", + "integrity": "sha512-LxcyMGxwmTh2lY9FwHPGWOHmYFCZvbrFCBZL4FzSSsxsRPuhrYUg/49/0KDfW8tnIEaEHtfmn6+NPN+1DqaNmA==", + "dev": true, + "engines": { + "node": "^12.22.0 || ^14.17.0 || >=16.0.0" + } + }, "node_modules/@humanwhocodes/config-array": { "version": "0.11.8", "resolved": "https://registry.npmjs.org/@humanwhocodes/config-array/-/config-array-0.11.8.tgz", @@ -673,9 +706,9 @@ } }, "node_modules/@octokit/core": { - "version": "4.2.0", - "resolved": "https://registry.npmjs.org/@octokit/core/-/core-4.2.0.tgz", - "integrity": "sha512-AgvDRUg3COpR82P7PBdGZF/NNqGmtMq2NiPqeSsDIeCfYFOZ9gddqWNQHnFdEUf+YwOj4aZYmJnlPp7OXmDIDg==", + "version": "4.2.1", + "resolved": "https://registry.npmjs.org/@octokit/core/-/core-4.2.1.tgz", + "integrity": "sha512-tEDxFx8E38zF3gT7sSMDrT1tGumDgsw5yPG6BBh/X+5ClIQfMH/Yqocxz1PnHx6CHyF6pxmovUTOfZAUvQ0Lvw==", "dev": true, "dependencies": { "@octokit/auth-token": "^3.0.0", @@ -705,9 +738,9 @@ } }, "node_modules/@octokit/graphql": { - "version": "5.0.5", - "resolved": "https://registry.npmjs.org/@octokit/graphql/-/graphql-5.0.5.tgz", - "integrity": "sha512-Qwfvh3xdqKtIznjX9lz2D458r7dJPP8l6r4GQkIdWQouZwHQK0mVT88uwiU2bdTU2OtT1uOlKpRciUWldpG0yQ==", + "version": "5.0.6", + "resolved": "https://registry.npmjs.org/@octokit/graphql/-/graphql-5.0.6.tgz", + "integrity": "sha512-Fxyxdy/JH0MnIB5h+UQ3yCoh1FG4kWXfFKkpWqjZHw/p+Kc8Y44Hu/kCgNBT6nU1shNumEchmW/sUO1JuQnPcw==", "dev": true, "dependencies": { "@octokit/request": "^6.0.0", @@ -719,18 +752,19 @@ } }, "node_modules/@octokit/openapi-types": { - "version": "16.0.0", - "resolved": "https://registry.npmjs.org/@octokit/openapi-types/-/openapi-types-16.0.0.tgz", - "integrity": "sha512-JbFWOqTJVLHZSUUoF4FzAZKYtqdxWu9Z5m2QQnOyEa04fOFljvyh7D3GYKbfuaSWisqehImiVIMG4eyJeP5VEA==", + "version": "17.2.0", + "resolved": "https://registry.npmjs.org/@octokit/openapi-types/-/openapi-types-17.2.0.tgz", + "integrity": "sha512-MazrFNx4plbLsGl+LFesMo96eIXkFgEtaKbnNpdh4aQ0VM10aoylFsTYP1AEjkeoRNZiiPe3T6Gl2Hr8dJWdlQ==", "dev": true }, "node_modules/@octokit/plugin-paginate-rest": { - "version": "5.0.1", - "resolved": "https://registry.npmjs.org/@octokit/plugin-paginate-rest/-/plugin-paginate-rest-5.0.1.tgz", - "integrity": "sha512-7A+rEkS70pH36Z6JivSlR7Zqepz3KVucEFVDnSrgHXzG7WLAzYwcHZbKdfTXHwuTHbkT1vKvz7dHl1+HNf6Qyw==", + "version": "6.1.2", + "resolved": "https://registry.npmjs.org/@octokit/plugin-paginate-rest/-/plugin-paginate-rest-6.1.2.tgz", + "integrity": "sha512-qhrmtQeHU/IivxucOV1bbI/xZyC/iOBhclokv7Sut5vnejAIAEXVcGQeRpQlU39E0WwK9lNvJHphHri/DB6lbQ==", "dev": true, "dependencies": { - "@octokit/types": "^8.0.0" + "@octokit/tsconfig": "^1.0.2", + "@octokit/types": "^9.2.3" }, "engines": { "node": ">= 14" @@ -739,21 +773,6 @@ "@octokit/core": ">=4" } }, - "node_modules/@octokit/plugin-paginate-rest/node_modules/@octokit/openapi-types": { - "version": "14.0.0", - "resolved": "https://registry.npmjs.org/@octokit/openapi-types/-/openapi-types-14.0.0.tgz", - "integrity": "sha512-HNWisMYlR8VCnNurDU6os2ikx0s0VyEjDYHNS/h4cgb8DeOxQ0n72HyinUtdDVxJhFy3FWLGl0DJhfEWk3P5Iw==", - "dev": true - }, - "node_modules/@octokit/plugin-paginate-rest/node_modules/@octokit/types": { - "version": "8.2.1", - "resolved": "https://registry.npmjs.org/@octokit/types/-/types-8.2.1.tgz", - "integrity": "sha512-8oWMUji8be66q2B9PmEIUyQm00VPDPun07umUWSaCwxmeaquFBro4Hcc3ruVoDo3zkQyZBlRvhIMEYS3pBhanw==", - "dev": true, - "dependencies": { - "@octokit/openapi-types": "^14.0.0" - } - }, "node_modules/@octokit/plugin-request-log": { "version": "1.0.4", "resolved": "https://registry.npmjs.org/@octokit/plugin-request-log/-/plugin-request-log-1.0.4.tgz", @@ -764,12 +783,12 @@ } }, "node_modules/@octokit/plugin-rest-endpoint-methods": { - "version": "6.8.1", - "resolved": "https://registry.npmjs.org/@octokit/plugin-rest-endpoint-methods/-/plugin-rest-endpoint-methods-6.8.1.tgz", - "integrity": "sha512-QrlaTm8Lyc/TbU7BL/8bO49vp+RZ6W3McxxmmQTgYxf2sWkO8ZKuj4dLhPNJD6VCUW1hetCmeIM0m6FTVpDiEg==", + "version": "7.1.2", + "resolved": "https://registry.npmjs.org/@octokit/plugin-rest-endpoint-methods/-/plugin-rest-endpoint-methods-7.1.2.tgz", + "integrity": "sha512-R0oJ7j6f/AdqPLtB9qRXLO+wjI9pctUn8Ka8UGfGaFCcCv3Otx14CshQ89K4E88pmyYZS8p0rNTiprML/81jig==", "dev": true, "dependencies": { - "@octokit/types": "^8.1.1", + "@octokit/types": "^9.2.3", "deprecation": "^2.3.1" }, "engines": { @@ -779,25 +798,10 @@ "@octokit/core": ">=3" } }, - "node_modules/@octokit/plugin-rest-endpoint-methods/node_modules/@octokit/openapi-types": { - "version": "14.0.0", - "resolved": "https://registry.npmjs.org/@octokit/openapi-types/-/openapi-types-14.0.0.tgz", - "integrity": "sha512-HNWisMYlR8VCnNurDU6os2ikx0s0VyEjDYHNS/h4cgb8DeOxQ0n72HyinUtdDVxJhFy3FWLGl0DJhfEWk3P5Iw==", - "dev": true - }, - "node_modules/@octokit/plugin-rest-endpoint-methods/node_modules/@octokit/types": { - "version": "8.2.1", - "resolved": "https://registry.npmjs.org/@octokit/types/-/types-8.2.1.tgz", - "integrity": "sha512-8oWMUji8be66q2B9PmEIUyQm00VPDPun07umUWSaCwxmeaquFBro4Hcc3ruVoDo3zkQyZBlRvhIMEYS3pBhanw==", - "dev": true, - "dependencies": { - "@octokit/openapi-types": "^14.0.0" - } - }, "node_modules/@octokit/request": { - "version": "6.2.3", - "resolved": "https://registry.npmjs.org/@octokit/request/-/request-6.2.3.tgz", - "integrity": "sha512-TNAodj5yNzrrZ/VxP+H5HiYaZep0H3GU0O7PaF+fhDrt8FPrnkei9Aal/txsN/1P7V3CPiThG0tIvpPDYUsyAA==", + "version": "6.2.5", + "resolved": "https://registry.npmjs.org/@octokit/request/-/request-6.2.5.tgz", + "integrity": "sha512-z83E8UIlPNaJUsXpjD8E0V5o/5f+vJJNbNcBwVZsX3/vC650U41cOkTLjq4PKk9BYonQGOnx7N17gvLyNjgGcQ==", "dev": true, "dependencies": { "@octokit/endpoint": "^7.0.0", @@ -826,9 +830,9 @@ } }, "node_modules/@octokit/request/node_modules/node-fetch": { - "version": "2.6.9", - "resolved": "https://registry.npmjs.org/node-fetch/-/node-fetch-2.6.9.tgz", - "integrity": "sha512-DJm/CJkZkRjKKj4Zi4BsKVZh3ValV5IR5s7LVZnW+6YMh0W1BfNA8XSs6DLMGYlId5F3KnA70uu2qepcR08Qqg==", + "version": "2.6.11", + "resolved": "https://registry.npmjs.org/node-fetch/-/node-fetch-2.6.11.tgz", + "integrity": "sha512-4I6pdBY1EthSqDmJkiNk3JIT8cswwR9nfeW/cPdUagJYEQG7R95WRH74wpz7ma8Gh/9dI9FP+OU+0E4FvtA55w==", "dev": true, "dependencies": { "whatwg-url": "^5.0.0" @@ -846,27 +850,33 @@ } }, "node_modules/@octokit/rest": { - "version": "19.0.5", - "resolved": "https://registry.npmjs.org/@octokit/rest/-/rest-19.0.5.tgz", - "integrity": "sha512-+4qdrUFq2lk7Va+Qff3ofREQWGBeoTKNqlJO+FGjFP35ZahP+nBenhZiGdu8USSgmq4Ky3IJ/i4u0xbLqHaeow==", + "version": "19.0.7", + "resolved": "https://registry.npmjs.org/@octokit/rest/-/rest-19.0.7.tgz", + "integrity": "sha512-HRtSfjrWmWVNp2uAkEpQnuGMJsu/+dBr47dRc5QVgsCbnIc1+GFEaoKBWkYG+zjrsHpSqcAElMio+n10c0b5JA==", "dev": true, "dependencies": { "@octokit/core": "^4.1.0", - "@octokit/plugin-paginate-rest": "^5.0.0", + "@octokit/plugin-paginate-rest": "^6.0.0", "@octokit/plugin-request-log": "^1.0.4", - "@octokit/plugin-rest-endpoint-methods": "^6.7.0" + "@octokit/plugin-rest-endpoint-methods": "^7.0.0" }, "engines": { "node": ">= 14" } }, + "node_modules/@octokit/tsconfig": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/@octokit/tsconfig/-/tsconfig-1.0.2.tgz", + "integrity": "sha512-I0vDR0rdtP8p2lGMzvsJzbhdOWy405HcGovrspJ8RRibHnyRgggUSNO5AIox5LmqiwmatHKYsvj6VGFHkqS7lA==", + "dev": true + }, "node_modules/@octokit/types": { - "version": "9.0.0", - "resolved": "https://registry.npmjs.org/@octokit/types/-/types-9.0.0.tgz", - "integrity": "sha512-LUewfj94xCMH2rbD5YJ+6AQ4AVjFYTgpp6rboWM5T7N3IsIF65SBEOVcYMGAEzO/kKNiNaW4LoWtoThOhH06gw==", + "version": "9.2.3", + "resolved": "https://registry.npmjs.org/@octokit/types/-/types-9.2.3.tgz", + "integrity": "sha512-MMeLdHyFIALioycq+LFcA71v0S2xpQUX2cw6pPbHQjaibcHYwLnmK/kMZaWuGfGfjBJZ3wRUq+dOaWsvrPJVvA==", "dev": true, "dependencies": { - "@octokit/openapi-types": "^16.0.0" + "@octokit/openapi-types": "^17.2.0" } }, "node_modules/@pnpm/network.ca-file": { @@ -944,18 +954,27 @@ } }, "node_modules/@sinonjs/fake-timers": { - "version": "10.0.2", - "resolved": "https://registry.npmjs.org/@sinonjs/fake-timers/-/fake-timers-10.0.2.tgz", - "integrity": "sha512-SwUDyjWnah1AaNl7kxsa7cfLhlTYoiyhDAIgyh+El30YvXs/o7OLXpYH88Zdhyx9JExKrmHDJ+10bwIcY80Jmw==", + "version": "10.2.0", + "resolved": "https://registry.npmjs.org/@sinonjs/fake-timers/-/fake-timers-10.2.0.tgz", + "integrity": "sha512-OPwQlEdg40HAj5KNF8WW6q2KG4Z+cBCZb3m4ninfTZKaBmbIJodviQsDBoYMPHkOyJJMHnOJo5j2+LKDOhOACg==", "dev": true, "dependencies": { - "@sinonjs/commons": "^2.0.0" + "@sinonjs/commons": "^3.0.0" + } + }, + "node_modules/@sinonjs/fake-timers/node_modules/@sinonjs/commons": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/@sinonjs/commons/-/commons-3.0.0.tgz", + "integrity": "sha512-jXBtWAF4vmdNmZgD5FoKsVLv3rPgDnLgPbU84LIJ3otV44vJlDRokVng5v8NFJdCf/da9legHcKaRuZs4L7faA==", + "dev": true, + "dependencies": { + "type-detect": "4.0.8" } }, "node_modules/@sinonjs/samsam": { - "version": "7.0.1", - "resolved": "https://registry.npmjs.org/@sinonjs/samsam/-/samsam-7.0.1.tgz", - "integrity": "sha512-zsAk2Jkiq89mhZovB2LLOdTCxJF4hqqTToGP0ASWlhp4I1hqOjcfmZGafXntCN7MDC6yySH0mFHrYtHceOeLmw==", + "version": "8.0.0", + "resolved": "https://registry.npmjs.org/@sinonjs/samsam/-/samsam-8.0.0.tgz", + "integrity": "sha512-Bp8KUVlLp8ibJZrnvq2foVhP0IVX2CIprMJPK0vqGqgrDa0OHVKeZyBykqskkrdxV6yKBPmGasO8LVjAKR3Gew==", "dev": true, "dependencies": { "@sinonjs/commons": "^2.0.0", @@ -1033,21 +1052,21 @@ "dev": true }, "node_modules/@types/node": { - "version": "18.14.1", - "resolved": "https://registry.npmjs.org/@types/node/-/node-18.14.1.tgz", - "integrity": "sha512-QH+37Qds3E0eDlReeboBxfHbX9omAcBCXEzswCu6jySP642jiM3cYSIkU/REqwhCUqXdonHFuBfJDiAJxMNhaQ==", + "version": "20.2.3", + "resolved": "https://registry.npmjs.org/@types/node/-/node-20.2.3.tgz", + "integrity": "sha512-pg9d0yC4rVNWQzX8U7xb4olIOFuuVL9za3bzMT2pu2SU0SNEi66i2qrvhE2qt0HvkhuCaWJu7pLNOt/Pj8BIrw==", "dev": true }, "node_modules/@types/semver": { - "version": "7.3.13", - "resolved": "https://registry.npmjs.org/@types/semver/-/semver-7.3.13.tgz", - "integrity": "sha512-21cFJr9z3g5dW8B0CVI9g2O9beqaThGQ6ZFBqHfwhzLDKUxaqTIy3vnfah/UPkfOiF2pLq+tGz+W8RyCskuslw==", + "version": "7.5.0", + "resolved": "https://registry.npmjs.org/@types/semver/-/semver-7.5.0.tgz", + "integrity": "sha512-G8hZ6XJiHnuhQKR7ZmysCeJWE08o8T0AXtk5darsCaTVsYZhhgUrq53jizaR2FvsoeCwJhlmwTjkXBY5Pn/ZHw==", "dev": true }, "node_modules/@types/sinon": { - "version": "10.0.13", - "resolved": "https://registry.npmjs.org/@types/sinon/-/sinon-10.0.13.tgz", - "integrity": "sha512-UVjDqJblVNQYvVNUsj0PuYYw0ELRmgt1Nt5Vk0pT5f16ROGfcKJY8o1HVuMOJOpD727RrGB9EGvoaTQE5tgxZQ==", + "version": "10.0.15", + "resolved": "https://registry.npmjs.org/@types/sinon/-/sinon-10.0.15.tgz", + "integrity": "sha512-3lrFNQG0Kr2LDzvjyjB6AMJk4ge+8iYhQfdnSwIwlG88FUOV43kPcQqDZkDa/h3WSZy6i8Fr0BSjfQtB1B3xuQ==", "dev": true, "dependencies": { "@types/sinonjs__fake-timers": "*" @@ -1066,9 +1085,9 @@ "dev": true }, "node_modules/@types/yargs": { - "version": "17.0.22", - "resolved": "https://registry.npmjs.org/@types/yargs/-/yargs-17.0.22.tgz", - "integrity": "sha512-pet5WJ9U8yPVRhkwuEIp5ktAeAqRZOq4UdAyWLWzxbtpyXnzbtLdKiXAjJzi/KLmPGS9wk86lUFWZFN6sISo4g==", + "version": "17.0.24", + "resolved": "https://registry.npmjs.org/@types/yargs/-/yargs-17.0.24.tgz", + "integrity": "sha512-6i0aC7jV6QzQB8ne1joVZ0eSFIstHsCrobmOtghM11yGlH0j43FKL2UhWdELkyps0zuf7qVTUVCCR+tgSlyLLw==", "dev": true, "dependencies": { "@types/yargs-parser": "*" @@ -1081,19 +1100,19 @@ "dev": true }, "node_modules/@typescript-eslint/eslint-plugin": { - "version": "5.53.0", - "resolved": "https://registry.npmjs.org/@typescript-eslint/eslint-plugin/-/eslint-plugin-5.53.0.tgz", - "integrity": "sha512-alFpFWNucPLdUOySmXCJpzr6HKC3bu7XooShWM+3w/EL6J2HIoB2PFxpLnq4JauWVk6DiVeNKzQlFEaE+X9sGw==", + "version": "5.59.7", + "resolved": "https://registry.npmjs.org/@typescript-eslint/eslint-plugin/-/eslint-plugin-5.59.7.tgz", + "integrity": "sha512-BL+jYxUFIbuYwy+4fF86k5vdT9lT0CNJ6HtwrIvGh0PhH8s0yy5rjaKH2fDCrz5ITHy07WCzVGNvAmjJh4IJFA==", "dev": true, "dependencies": { - "@typescript-eslint/scope-manager": "5.53.0", - "@typescript-eslint/type-utils": "5.53.0", - "@typescript-eslint/utils": "5.53.0", + "@eslint-community/regexpp": "^4.4.0", + "@typescript-eslint/scope-manager": "5.59.7", + "@typescript-eslint/type-utils": "5.59.7", + "@typescript-eslint/utils": "5.59.7", "debug": "^4.3.4", "grapheme-splitter": "^1.0.4", "ignore": "^5.2.0", "natural-compare-lite": "^1.4.0", - "regexpp": "^3.2.0", "semver": "^7.3.7", "tsutils": "^3.21.0" }, @@ -1148,14 +1167,14 @@ "dev": true }, "node_modules/@typescript-eslint/parser": { - "version": "5.53.0", - "resolved": "https://registry.npmjs.org/@typescript-eslint/parser/-/parser-5.53.0.tgz", - "integrity": "sha512-MKBw9i0DLYlmdOb3Oq/526+al20AJZpANdT6Ct9ffxcV8nKCHz63t/S0IhlTFNsBIHJv+GY5SFJ0XfqVeydQrQ==", + "version": "5.59.7", + "resolved": "https://registry.npmjs.org/@typescript-eslint/parser/-/parser-5.59.7.tgz", + "integrity": "sha512-VhpsIEuq/8i5SF+mPg9jSdIwgMBBp0z9XqjiEay+81PYLJuroN+ET1hM5IhkiYMJd9MkTz8iJLt7aaGAgzWUbQ==", "dev": true, "dependencies": { - "@typescript-eslint/scope-manager": "5.53.0", - "@typescript-eslint/types": "5.53.0", - "@typescript-eslint/typescript-estree": "5.53.0", + "@typescript-eslint/scope-manager": "5.59.7", + "@typescript-eslint/types": "5.59.7", + "@typescript-eslint/typescript-estree": "5.59.7", "debug": "^4.3.4" }, "engines": { @@ -1175,13 +1194,13 @@ } }, "node_modules/@typescript-eslint/scope-manager": { - "version": "5.53.0", - "resolved": "https://registry.npmjs.org/@typescript-eslint/scope-manager/-/scope-manager-5.53.0.tgz", - "integrity": "sha512-Opy3dqNsp/9kBBeCPhkCNR7fmdSQqA+47r21hr9a14Bx0xnkElEQmhoHga+VoaoQ6uDHjDKmQPIYcUcKJifS7w==", + "version": "5.59.7", + "resolved": "https://registry.npmjs.org/@typescript-eslint/scope-manager/-/scope-manager-5.59.7.tgz", + "integrity": "sha512-FL6hkYWK9zBGdxT2wWEd2W8ocXMu3K94i3gvMrjXpx+koFYdYV7KprKfirpgY34vTGzEPPuKoERpP8kD5h7vZQ==", "dev": true, "dependencies": { - "@typescript-eslint/types": "5.53.0", - "@typescript-eslint/visitor-keys": "5.53.0" + "@typescript-eslint/types": "5.59.7", + "@typescript-eslint/visitor-keys": "5.59.7" }, "engines": { "node": "^12.22.0 || ^14.17.0 || >=16.0.0" @@ -1192,13 +1211,13 @@ } }, "node_modules/@typescript-eslint/type-utils": { - "version": "5.53.0", - "resolved": "https://registry.npmjs.org/@typescript-eslint/type-utils/-/type-utils-5.53.0.tgz", - "integrity": "sha512-HO2hh0fmtqNLzTAme/KnND5uFNwbsdYhCZghK2SoxGp3Ifn2emv+hi0PBUjzzSh0dstUIFqOj3bp0AwQlK4OWw==", + "version": "5.59.7", + "resolved": "https://registry.npmjs.org/@typescript-eslint/type-utils/-/type-utils-5.59.7.tgz", + "integrity": "sha512-ozuz/GILuYG7osdY5O5yg0QxXUAEoI4Go3Do5xeu+ERH9PorHBPSdvD3Tjp2NN2bNLh1NJQSsQu2TPu/Ly+HaQ==", "dev": true, "dependencies": { - "@typescript-eslint/typescript-estree": "5.53.0", - "@typescript-eslint/utils": "5.53.0", + "@typescript-eslint/typescript-estree": "5.59.7", + "@typescript-eslint/utils": "5.59.7", "debug": "^4.3.4", "tsutils": "^3.21.0" }, @@ -1219,9 +1238,9 @@ } }, "node_modules/@typescript-eslint/types": { - "version": "5.53.0", - "resolved": "https://registry.npmjs.org/@typescript-eslint/types/-/types-5.53.0.tgz", - "integrity": "sha512-5kcDL9ZUIP756K6+QOAfPkigJmCPHcLN7Zjdz76lQWWDdzfOhZDTj1irs6gPBKiXx5/6O3L0+AvupAut3z7D2A==", + "version": "5.59.7", + "resolved": "https://registry.npmjs.org/@typescript-eslint/types/-/types-5.59.7.tgz", + "integrity": "sha512-UnVS2MRRg6p7xOSATscWkKjlf/NDKuqo5TdbWck6rIRZbmKpVNTLALzNvcjIfHBE7736kZOFc/4Z3VcZwuOM/A==", "dev": true, "engines": { "node": "^12.22.0 || ^14.17.0 || >=16.0.0" @@ -1232,13 +1251,13 @@ } }, "node_modules/@typescript-eslint/typescript-estree": { - "version": "5.53.0", - "resolved": "https://registry.npmjs.org/@typescript-eslint/typescript-estree/-/typescript-estree-5.53.0.tgz", - "integrity": "sha512-eKmipH7QyScpHSkhbptBBYh9v8FxtngLquq292YTEQ1pxVs39yFBlLC1xeIZcPPz1RWGqb7YgERJRGkjw8ZV7w==", + "version": "5.59.7", + "resolved": "https://registry.npmjs.org/@typescript-eslint/typescript-estree/-/typescript-estree-5.59.7.tgz", + "integrity": "sha512-4A1NtZ1I3wMN2UGDkU9HMBL+TIQfbrh4uS0WDMMpf3xMRursDbqEf1ahh6vAAe3mObt8k3ZATnezwG4pdtWuUQ==", "dev": true, "dependencies": { - "@typescript-eslint/types": "5.53.0", - "@typescript-eslint/visitor-keys": "5.53.0", + "@typescript-eslint/types": "5.59.7", + "@typescript-eslint/visitor-keys": "5.59.7", "debug": "^4.3.4", "globby": "^11.1.0", "is-glob": "^4.0.3", @@ -1300,9 +1319,9 @@ } }, "node_modules/@typescript-eslint/typescript-estree/node_modules/semver": { - "version": "7.3.8", - "resolved": "https://registry.npmjs.org/semver/-/semver-7.3.8.tgz", - "integrity": "sha512-NB1ctGL5rlHrPJtFDVIVzTyQylMLu9N9VICA6HSFJo8MCGVTMW6gfpicwKmmK/dAjTOrqu5l63JJOpDSrAis3A==", + "version": "7.5.1", + "resolved": "https://registry.npmjs.org/semver/-/semver-7.5.1.tgz", + "integrity": "sha512-Wvss5ivl8TMRZXXESstBA4uR5iXgEN/VC5/sOcuXdVLzcdkz4HWetIoRfG5gb5X+ij/G9rw9YoGn3QoQ8OCSpw==", "dev": true, "dependencies": { "lru-cache": "^6.0.0" @@ -1321,18 +1340,18 @@ "dev": true }, "node_modules/@typescript-eslint/utils": { - "version": "5.53.0", - "resolved": "https://registry.npmjs.org/@typescript-eslint/utils/-/utils-5.53.0.tgz", - "integrity": "sha512-VUOOtPv27UNWLxFwQK/8+7kvxVC+hPHNsJjzlJyotlaHjLSIgOCKj9I0DBUjwOOA64qjBwx5afAPjksqOxMO0g==", + "version": "5.59.7", + "resolved": "https://registry.npmjs.org/@typescript-eslint/utils/-/utils-5.59.7.tgz", + "integrity": "sha512-yCX9WpdQKaLufz5luG4aJbOpdXf/fjwGMcLFXZVPUz3QqLirG5QcwwnIHNf8cjLjxK4qtzTO8udUtMQSAToQnQ==", "dev": true, "dependencies": { + "@eslint-community/eslint-utils": "^4.2.0", "@types/json-schema": "^7.0.9", "@types/semver": "^7.3.12", - "@typescript-eslint/scope-manager": "5.53.0", - "@typescript-eslint/types": "5.53.0", - "@typescript-eslint/typescript-estree": "5.53.0", + "@typescript-eslint/scope-manager": "5.59.7", + "@typescript-eslint/types": "5.59.7", + "@typescript-eslint/typescript-estree": "5.59.7", "eslint-scope": "^5.1.1", - "eslint-utils": "^3.0.0", "semver": "^7.3.7" }, "engines": { @@ -1359,9 +1378,9 @@ } }, "node_modules/@typescript-eslint/utils/node_modules/semver": { - "version": "7.3.8", - "resolved": "https://registry.npmjs.org/semver/-/semver-7.3.8.tgz", - "integrity": "sha512-NB1ctGL5rlHrPJtFDVIVzTyQylMLu9N9VICA6HSFJo8MCGVTMW6gfpicwKmmK/dAjTOrqu5l63JJOpDSrAis3A==", + "version": "7.5.1", + "resolved": "https://registry.npmjs.org/semver/-/semver-7.5.1.tgz", + "integrity": "sha512-Wvss5ivl8TMRZXXESstBA4uR5iXgEN/VC5/sOcuXdVLzcdkz4HWetIoRfG5gb5X+ij/G9rw9YoGn3QoQ8OCSpw==", "dev": true, "dependencies": { "lru-cache": "^6.0.0" @@ -1380,12 +1399,12 @@ "dev": true }, "node_modules/@typescript-eslint/visitor-keys": { - "version": "5.53.0", - "resolved": "https://registry.npmjs.org/@typescript-eslint/visitor-keys/-/visitor-keys-5.53.0.tgz", - "integrity": "sha512-JqNLnX3leaHFZEN0gCh81sIvgrp/2GOACZNgO4+Tkf64u51kTpAyWFOY8XHx8XuXr3N2C9zgPPHtcpMg6z1g0w==", + "version": "5.59.7", + "resolved": "https://registry.npmjs.org/@typescript-eslint/visitor-keys/-/visitor-keys-5.59.7.tgz", + "integrity": "sha512-tyN+X2jvMslUszIiYbF0ZleP+RqQsFVpGrKI6e0Eet1w8WmhsAtmzaqm8oM8WJQ1ysLwhnsK/4hYHJjOgJVfQQ==", "dev": true, "dependencies": { - "@typescript-eslint/types": "5.53.0", + "@typescript-eslint/types": "5.59.7", "eslint-visitor-keys": "^3.3.0" }, "engines": { @@ -1527,9 +1546,9 @@ } }, "node_modules/ansi-escapes": { - "version": "6.0.0", - "resolved": "https://registry.npmjs.org/ansi-escapes/-/ansi-escapes-6.0.0.tgz", - "integrity": "sha512-IG23inYII3dWlU2EyiAiGj6Bwal5GzsgPMwjYGvc1HPE2dgbj4ZB5ToWBKSquKw74nB3TIuOwaI6/jSULzfgrw==", + "version": "6.2.0", + "resolved": "https://registry.npmjs.org/ansi-escapes/-/ansi-escapes-6.2.0.tgz", + "integrity": "sha512-kzRaCqXnpzWs+3z5ABPQiVke+iq0KXkHo8xiWV4RPTi5Yli0l97BEQuhXV1s7+aSU/fu1kUuxgS4MsQ0fRuygw==", "dev": true, "dependencies": { "type-fest": "^3.0.0" @@ -1541,6 +1560,18 @@ "url": "https://github.com/sponsors/sindresorhus" } }, + "node_modules/ansi-escapes/node_modules/type-fest": { + "version": "3.11.0", + "resolved": "https://registry.npmjs.org/type-fest/-/type-fest-3.11.0.tgz", + "integrity": "sha512-JaPw5U9ixP0XcpUbQoVSbxSDcK/K4nww20C3kjm9yE6cDRRhptU28AH60VWf9ltXmCrIfIbtt9J+2OUk2Uqiaw==", + "dev": true, + "engines": { + "node": ">=14.16" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, "node_modules/ansi-regex": { "version": "6.0.1", "resolved": "https://registry.npmjs.org/ansi-regex/-/ansi-regex-6.0.1.tgz", @@ -1725,6 +1756,15 @@ "integrity": "sha512-NzUnlZexiaH/46WDhANlyR2bXRopNg4F/zuSA3OpZnllCUgRaOF2znDioDWrmbNVsuZk6l9pMquQB38cfBZwkQ==", "dev": true }, + "node_modules/big-integer": { + "version": "1.6.51", + "resolved": "https://registry.npmjs.org/big-integer/-/big-integer-1.6.51.tgz", + "integrity": "sha512-GPEid2Y9QU1Exl1rpO9B2IPJGHPSupF5GnVIP0blYvNOMer2bTvSWs1jGOUg04hTmu67nmLsQ9TBo1puaotBHg==", + "dev": true, + "engines": { + "node": ">=0.6" + } + }, "node_modules/binary-extensions": { "version": "2.2.0", "resolved": "https://registry.npmjs.org/binary-extensions/-/binary-extensions-2.2.0.tgz", @@ -1791,6 +1831,18 @@ "url": "https://github.com/sponsors/sindresorhus" } }, + "node_modules/bplist-parser": { + "version": "0.2.0", + "resolved": "https://registry.npmjs.org/bplist-parser/-/bplist-parser-0.2.0.tgz", + "integrity": "sha512-z0M+byMThzQmD9NILRniCUXYsYpjwnlO8N5uCFaCqIOpqRsJCrQL9NK3JsD67CN5a08nF5oIL2bD6loTdHOuKw==", + "dev": true, + "dependencies": { + "big-integer": "^1.6.44" + }, + "engines": { + "node": ">= 5.10.0" + } + }, "node_modules/brace-expansion": { "version": "1.1.11", "resolved": "https://registry.npmjs.org/brace-expansion/-/brace-expansion-1.1.11.tgz", @@ -1877,6 +1929,21 @@ "integrity": "sha512-E+XQCRwSbaaiChtv6k6Dwgc+bx+Bs6vuKJHHl5kox/BaKbhiXzqQOwK4cO22yElGp2OCmjwVhT3HmxgyPGnJfQ==", "dev": true }, + "node_modules/bundle-name": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/bundle-name/-/bundle-name-3.0.0.tgz", + "integrity": "sha512-PKA4BeSvBpQKQ8iPOGCSiell+N8P+Tf1DlwqmYhpe2gAhKPHn8EYOxVT+ShuGmhg8lN8XiSlS80yiExKXrURlw==", + "dev": true, + "dependencies": { + "run-applescript": "^5.0.0" + }, + "engines": { + "node": ">=12" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, "node_modules/bytes": { "version": "3.1.2", "resolved": "https://registry.npmjs.org/bytes/-/bytes-3.1.2.tgz", @@ -1976,9 +2043,9 @@ ] }, "node_modules/chalk": { - "version": "5.1.2", - "resolved": "https://registry.npmjs.org/chalk/-/chalk-5.1.2.tgz", - "integrity": "sha512-E5CkT4jWURs1Vy5qGJye+XwCkNj7Od3Af7CP6SujMetSMkLs8Do2RWJK5yx1wamHV/op8Rz+9rltjaTQWDnEFQ==", + "version": "5.2.0", + "resolved": "https://registry.npmjs.org/chalk/-/chalk-5.2.0.tgz", + "integrity": "sha512-ree3Gqw/nazQAPuJJEy+avdl7QfZMcUvmHIKgEZkGL+xOBzRvup5Hxo6LHuMceSxOabuJLJm5Yp/92R9eMmMvA==", "dev": true, "engines": { "node": "^12.17.0 || ^14.13 || >=16.0.0" @@ -2084,9 +2151,9 @@ } }, "node_modules/cli-spinners": { - "version": "2.7.0", - "resolved": "https://registry.npmjs.org/cli-spinners/-/cli-spinners-2.7.0.tgz", - "integrity": "sha512-qu3pN8Y3qHNgE2AFweciB1IfMnmZ/fsNTEE+NOFjmGB2F/7rLhnhzppvpCnN4FovtP26k8lHyy9ptEbNwWFLzw==", + "version": "2.9.0", + "resolved": "https://registry.npmjs.org/cli-spinners/-/cli-spinners-2.9.0.tgz", + "integrity": "sha512-4/aL9X3Wh0yiMQlE+eeRhWP6vclO3QRtw1JHKIT0FFUs5FjpFmESqtMvYZ0+lbzBw900b95mS0hohy+qn2VK/g==", "dev": true, "engines": { "node": ">=6" @@ -2286,9 +2353,9 @@ "dev": true }, "node_modules/cosmiconfig": { - "version": "8.0.0", - "resolved": "https://registry.npmjs.org/cosmiconfig/-/cosmiconfig-8.0.0.tgz", - "integrity": "sha512-da1EafcpH6b/TD8vDRaWV7xFINlHlF6zKsGwS1TsuVJTZRkquaS5HTMq7uq6h31619QjbsYl21gVDOm32KM1vQ==", + "version": "8.1.3", + "resolved": "https://registry.npmjs.org/cosmiconfig/-/cosmiconfig-8.1.3.tgz", + "integrity": "sha512-/UkO2JKI18b5jVMJUp0lvKFMpa/Gye+ZgZjKD+DGEN9y7NRcf/nK1A0sp67ONmKtnDCNMS44E6jrk0Yc3bDuUw==", "dev": true, "dependencies": { "import-fresh": "^3.2.1", @@ -2298,6 +2365,9 @@ }, "engines": { "node": ">=14" + }, + "funding": { + "url": "https://github.com/sponsors/d-fischer" } }, "node_modules/create-require": { @@ -2424,6 +2494,40 @@ "integrity": "sha512-oIPzksmTg4/MriiaYGO+okXDT7ztn/w3Eptv/+gSIdMdKsJo0u4CfYNFJPy+4SKMuCqGw2wxnA+URMg3t8a/bQ==", "dev": true }, + "node_modules/default-browser": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/default-browser/-/default-browser-4.0.0.tgz", + "integrity": "sha512-wX5pXO1+BrhMkSbROFsyxUm0i/cJEScyNhA4PPxc41ICuv05ZZB/MX28s8aZx6xjmatvebIapF6hLEKEcpneUA==", + "dev": true, + "dependencies": { + "bundle-name": "^3.0.0", + "default-browser-id": "^3.0.0", + "execa": "^7.1.1", + "titleize": "^3.0.0" + }, + "engines": { + "node": ">=14.16" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/default-browser-id": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/default-browser-id/-/default-browser-id-3.0.0.tgz", + "integrity": "sha512-OZ1y3y0SqSICtE8DE4S8YOE9UZOJ8wO16fKWVP5J1Qz42kV9jcnMVFrEE/noXb/ss3Q4pZIH79kxofzyNNtUNA==", + "dev": true, + "dependencies": { + "bplist-parser": "^0.2.0", + "untildify": "^4.0.0" + }, + "engines": { + "node": ">=12" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, "node_modules/default-require-extensions": { "version": "3.0.1", "resolved": "https://registry.npmjs.org/default-require-extensions/-/default-require-extensions-3.0.1.tgz", @@ -2461,12 +2565,15 @@ } }, "node_modules/define-lazy-prop": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/define-lazy-prop/-/define-lazy-prop-2.0.0.tgz", - "integrity": "sha512-Ds09qNh8yw3khSjiJjiUInaGX9xlqZDY7JVryGxdxV7NPeuqQfplOpQ66yJFZut3jLa5zOwkXw1g9EI2uKh4Og==", + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/define-lazy-prop/-/define-lazy-prop-3.0.0.tgz", + "integrity": "sha512-N+MeXYoqr3pOgn8xfyRPREN7gHakLYjhsHhWGT3fWAiL4IkAt0iDw14QiiEm2bE30c5XX5q0FtAA3CK5f9/BUg==", "dev": true, "engines": { - "node": ">=8" + "node": ">=12" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" } }, "node_modules/define-properties": { @@ -2813,12 +2920,15 @@ } }, "node_modules/eslint": { - "version": "8.34.0", - "resolved": "https://registry.npmjs.org/eslint/-/eslint-8.34.0.tgz", - "integrity": "sha512-1Z8iFsucw+7kSqXNZVslXS8Ioa4u2KM7GPwuKtkTFAqZ/cHMcEaR+1+Br0wLlot49cNxIiZk5wp8EAbPcYZxTg==", + "version": "8.41.0", + "resolved": "https://registry.npmjs.org/eslint/-/eslint-8.41.0.tgz", + "integrity": "sha512-WQDQpzGBOP5IrXPo4Hc0814r4/v2rrIsB0rhT7jtunIalgg6gYXWhRMOejVO8yH21T/FGaxjmFjBMNqcIlmH1Q==", "dev": true, "dependencies": { - "@eslint/eslintrc": "^1.4.1", + "@eslint-community/eslint-utils": "^4.2.0", + "@eslint-community/regexpp": "^4.4.0", + "@eslint/eslintrc": "^2.0.3", + "@eslint/js": "8.41.0", "@humanwhocodes/config-array": "^0.11.8", "@humanwhocodes/module-importer": "^1.0.1", "@nodelib/fs.walk": "^1.2.8", @@ -2828,24 +2938,22 @@ "debug": "^4.3.2", "doctrine": "^3.0.0", "escape-string-regexp": "^4.0.0", - "eslint-scope": "^7.1.1", - "eslint-utils": "^3.0.0", - "eslint-visitor-keys": "^3.3.0", - "espree": "^9.4.0", - "esquery": "^1.4.0", + "eslint-scope": "^7.2.0", + "eslint-visitor-keys": "^3.4.1", + "espree": "^9.5.2", + "esquery": "^1.4.2", "esutils": "^2.0.2", "fast-deep-equal": "^3.1.3", "file-entry-cache": "^6.0.1", "find-up": "^5.0.0", "glob-parent": "^6.0.2", "globals": "^13.19.0", - "grapheme-splitter": "^1.0.4", + "graphemer": "^1.4.0", "ignore": "^5.2.0", "import-fresh": "^3.0.0", "imurmurhash": "^0.1.4", "is-glob": "^4.0.0", "is-path-inside": "^3.0.3", - "js-sdsl": "^4.1.4", "js-yaml": "^4.1.0", "json-stable-stringify-without-jsonify": "^1.0.1", "levn": "^0.4.1", @@ -2853,7 +2961,6 @@ "minimatch": "^3.1.2", "natural-compare": "^1.4.0", "optionator": "^0.9.1", - "regexpp": "^3.2.0", "strip-ansi": "^6.0.1", "strip-json-comments": "^3.1.0", "text-table": "^0.2.0" @@ -2881,40 +2988,16 @@ "node": ">=8.0.0" } }, - "node_modules/eslint-utils": { - "version": "3.0.0", - "resolved": "https://registry.npmjs.org/eslint-utils/-/eslint-utils-3.0.0.tgz", - "integrity": "sha512-uuQC43IGctw68pJA1RgbQS8/NP7rch6Cwd4j3ZBtgo4/8Flj4eGE7ZYSZRN3iq5pVUv6GPdW5Z1RFleo84uLDA==", - "dev": true, - "dependencies": { - "eslint-visitor-keys": "^2.0.0" - }, - "engines": { - "node": "^10.0.0 || ^12.0.0 || >= 14.0.0" - }, - "funding": { - "url": "https://github.com/sponsors/mysticatea" - }, - "peerDependencies": { - "eslint": ">=5" - } - }, - "node_modules/eslint-utils/node_modules/eslint-visitor-keys": { - "version": "2.1.0", - "resolved": "https://registry.npmjs.org/eslint-visitor-keys/-/eslint-visitor-keys-2.1.0.tgz", - "integrity": "sha512-0rSmRBzXgDzIsD6mGdJgevzgezI534Cer5L/vyMX0kHzT/jiB43jRhd9YUlMGYLQy2zprNmoT8qasCGtY+QaKw==", - "dev": true, - "engines": { - "node": ">=10" - } - }, "node_modules/eslint-visitor-keys": { - "version": "3.3.0", - "resolved": "https://registry.npmjs.org/eslint-visitor-keys/-/eslint-visitor-keys-3.3.0.tgz", - "integrity": "sha512-mQ+suqKJVyeuwGYHAdjMFqjCyfl8+Ldnxuyp3ldiMBFKkvytrXUZWaiPCEav8qDHKty44bD+qV1IP4T+w+xXRA==", + "version": "3.4.1", + "resolved": "https://registry.npmjs.org/eslint-visitor-keys/-/eslint-visitor-keys-3.4.1.tgz", + "integrity": "sha512-pZnmmLwYzf+kWaM/Qgrvpen51upAktaaiI01nsJD/Yr3lMOdNtq0cxkrrg16w64VtisN6okbs7Q8AfGqj4c9fA==", "dev": true, "engines": { "node": "^12.22.0 || ^14.17.0 || >=16.0.0" + }, + "funding": { + "url": "https://opencollective.com/eslint" } }, "node_modules/eslint/node_modules/ansi-regex": { @@ -2970,9 +3053,9 @@ } }, "node_modules/eslint/node_modules/eslint-scope": { - "version": "7.1.1", - "resolved": "https://registry.npmjs.org/eslint-scope/-/eslint-scope-7.1.1.tgz", - "integrity": "sha512-QKQM/UXpIiHcLqJ5AOyIW7XZmzjkzQXYE54n1++wb0u9V/abW3l9uQnxX8Z5Xd18xyKIMTUAyQ0k1e8pz6LUrw==", + "version": "7.2.0", + "resolved": "https://registry.npmjs.org/eslint-scope/-/eslint-scope-7.2.0.tgz", + "integrity": "sha512-DYj5deGlHBfMt15J7rdtyKNq/Nqlv5KfU4iodrQ019XESsRnwXH9KAE0y3cwtUHDo2ob7CypAnCqefh6vioWRw==", "dev": true, "dependencies": { "esrecurse": "^4.3.0", @@ -2980,6 +3063,9 @@ }, "engines": { "node": "^12.22.0 || ^14.17.0 || >=16.0.0" + }, + "funding": { + "url": "https://opencollective.com/eslint" } }, "node_modules/eslint/node_modules/estraverse": { @@ -3065,14 +3151,14 @@ } }, "node_modules/espree": { - "version": "9.4.1", - "resolved": "https://registry.npmjs.org/espree/-/espree-9.4.1.tgz", - "integrity": "sha512-XwctdmTO6SIvCzd9810yyNzIrOrqNYV9Koizx4C/mRhf9uq0o4yHoCEU/670pOxOL/MSraektvSAji79kX90Vg==", + "version": "9.5.2", + "resolved": "https://registry.npmjs.org/espree/-/espree-9.5.2.tgz", + "integrity": "sha512-7OASN1Wma5fum5SrNhFMAMJxOUAbhyfQ8dQ//PJaJbNw0URTPWqIghHWt1MmAANKhHZIYOHruW4Kw4ruUWOdGw==", "dev": true, "dependencies": { "acorn": "^8.8.0", "acorn-jsx": "^5.3.2", - "eslint-visitor-keys": "^3.3.0" + "eslint-visitor-keys": "^3.4.1" }, "engines": { "node": "^12.22.0 || ^14.17.0 || >=16.0.0" @@ -3155,14 +3241,14 @@ } }, "node_modules/execa": { - "version": "6.1.0", - "resolved": "https://registry.npmjs.org/execa/-/execa-6.1.0.tgz", - "integrity": "sha512-QVWlX2e50heYJcCPG0iWtf8r0xjEYfz/OYLGDYH+IyjWezzPNxz63qNFOu0l4YftGWuizFVZHHs8PrLU5p2IDA==", + "version": "7.1.1", + "resolved": "https://registry.npmjs.org/execa/-/execa-7.1.1.tgz", + "integrity": "sha512-wH0eMf/UXckdUYnO21+HDztteVv05rq2GXksxT4fCGeHkBhw1DROXh40wcjMcRqDOWE7iPJ4n3M7e2+YFP+76Q==", "dev": true, "dependencies": { "cross-spawn": "^7.0.3", "get-stream": "^6.0.1", - "human-signals": "^3.0.1", + "human-signals": "^4.3.0", "is-stream": "^3.0.0", "merge-stream": "^2.0.0", "npm-run-path": "^5.1.0", @@ -3171,7 +3257,7 @@ "strip-final-newline": "^3.0.0" }, "engines": { - "node": "^12.20.0 || ^14.13.1 || >=16.0.0" + "node": "^14.18.0 || ^16.14.0 || >=18.0.0" }, "funding": { "url": "https://github.com/sindresorhus/execa?sponsor=1" @@ -3775,18 +3861,6 @@ "url": "https://github.com/sponsors/sindresorhus" } }, - "node_modules/globals/node_modules/type-fest": { - "version": "0.20.2", - "resolved": "https://registry.npmjs.org/type-fest/-/type-fest-0.20.2.tgz", - "integrity": "sha512-Ne+eE4r0/iWnpAxD852z3A+N0Bt5RN//NjJwRd2VFHEmrywxf5vsZlh4R6lixl6B+wz/8d+maTSAkN1FIkI3LQ==", - "dev": true, - "engines": { - "node": ">=10" - }, - "funding": { - "url": "https://github.com/sponsors/sindresorhus" - } - }, "node_modules/globalthis": { "version": "1.0.3", "resolved": "https://registry.npmjs.org/globalthis/-/globalthis-1.0.3.tgz", @@ -3831,15 +3905,15 @@ } }, "node_modules/got": { - "version": "12.5.3", - "resolved": "https://registry.npmjs.org/got/-/got-12.5.3.tgz", - "integrity": "sha512-8wKnb9MGU8IPGRIo+/ukTy9XLJBwDiCpIf5TVzQ9Cpol50eMTpBq2GAuDsuDIz7hTYmZgMgC1e9ydr6kSDWs3w==", + "version": "12.6.0", + "resolved": "https://registry.npmjs.org/got/-/got-12.6.0.tgz", + "integrity": "sha512-WTcaQ963xV97MN3x0/CbAriXFZcXCfgxVp91I+Ze6pawQOa7SgzwSx2zIJJsX+kTajMnVs0xcFD1TxZKFqhdnQ==", "dev": true, "dependencies": { "@sindresorhus/is": "^5.2.0", "@szmarczak/http-timer": "^5.0.1", "cacheable-lookup": "^7.0.0", - "cacheable-request": "^10.2.1", + "cacheable-request": "^10.2.8", "decompress-response": "^6.0.0", "form-data-encoder": "^2.1.2", "get-stream": "^6.0.1", @@ -3867,6 +3941,12 @@ "integrity": "sha512-bzh50DW9kTPM00T8y4o8vQg89Di9oLJVLW/KaOGIXJWP/iqCN6WKYkbNOF04vFLJhwcpYUh9ydh/+5vpOqV4YQ==", "dev": true }, + "node_modules/graphemer": { + "version": "1.4.0", + "resolved": "https://registry.npmjs.org/graphemer/-/graphemer-1.4.0.tgz", + "integrity": "sha512-EtKwoO6kxCL9WO5xipiHTZlSzBm7WLT627TqC/uVRd0HKmq8NXyebnNYxDoBi7wt8eTWrUrKXCOVaFq9x1kgag==", + "dev": true + }, "node_modules/has": { "version": "1.0.3", "resolved": "https://registry.npmjs.org/has/-/has-1.0.3.tgz", @@ -4075,12 +4155,12 @@ } }, "node_modules/human-signals": { - "version": "3.0.1", - "resolved": "https://registry.npmjs.org/human-signals/-/human-signals-3.0.1.tgz", - "integrity": "sha512-rQLskxnM/5OCldHo+wNXbpVgDn5A17CUoKX+7Sokwaknlq7CdSnphy0W39GU8dw59XiCXmFXDg4fRuckQRKewQ==", + "version": "4.3.1", + "resolved": "https://registry.npmjs.org/human-signals/-/human-signals-4.3.1.tgz", + "integrity": "sha512-nZXjEF2nbo7lIw3mgYjItAfgQXog3OjJogSbKa2CQIIvSGWcKgeJnQlNXip6NglNzYH45nSRiEVimMvYL8DDqQ==", "dev": true, "engines": { - "node": ">=12.20.0" + "node": ">=14.18.0" } }, "node_modules/iconv-lite": { @@ -4193,29 +4273,29 @@ } }, "node_modules/inquirer": { - "version": "9.1.4", - "resolved": "https://registry.npmjs.org/inquirer/-/inquirer-9.1.4.tgz", - "integrity": "sha512-9hiJxE5gkK/cM2d1mTEnuurGTAoHebbkX0BYl3h7iEg7FYfuNIom+nDfBCSWtvSnoSrWCeBxqqBZu26xdlJlXA==", + "version": "9.2.0", + "resolved": "https://registry.npmjs.org/inquirer/-/inquirer-9.2.0.tgz", + "integrity": "sha512-WWERbVqjsTXjXub1ZW0ZHDit1dyHqy0T9XIkky9TnmKAPrjU9Jkd59nZPK0dUuM3s73GZAZu2Jo4iFU3XSPVLA==", "dev": true, "dependencies": { "ansi-escapes": "^6.0.0", - "chalk": "^5.1.2", + "chalk": "^5.2.0", "cli-cursor": "^4.0.0", "cli-width": "^4.0.0", "external-editor": "^3.0.3", "figures": "^5.0.0", "lodash": "^4.17.21", - "mute-stream": "0.0.8", + "mute-stream": "1.0.0", "ora": "^6.1.2", "run-async": "^2.4.0", - "rxjs": "^7.5.7", + "rxjs": "^7.8.0", "string-width": "^5.1.2", "strip-ansi": "^7.0.1", "through": "^2.3.6", - "wrap-ansi": "^8.0.1" + "wrap-ansi": "^8.1.0" }, "engines": { - "node": ">=12.0.0" + "node": ">=14.18.0" } }, "node_modules/internal-slot": { @@ -4375,15 +4455,15 @@ } }, "node_modules/is-docker": { - "version": "2.2.1", - "resolved": "https://registry.npmjs.org/is-docker/-/is-docker-2.2.1.tgz", - "integrity": "sha512-F+i2BKsFrH66iaUFc0woD8sLy8getkwTwtOBjvs56Cx4CgJDeKQeqfz8wAYiSb8JOprWhHH5p77PbmYCvvUuXQ==", + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/is-docker/-/is-docker-3.0.0.tgz", + "integrity": "sha512-eljcgEDlEns/7AXFosB5K/2nCM4P7FQPkGc/DWLy5rmFEWvZayGrik1d9/QIY5nJ4f9YsVvBkA6kJpHn9rISdQ==", "dev": true, "bin": { "is-docker": "cli.js" }, "engines": { - "node": ">=8" + "node": "^12.20.0 || ^14.13.1 || >=16.0.0" }, "funding": { "url": "https://github.com/sponsors/sindresorhus" @@ -4419,6 +4499,24 @@ "node": ">=0.10.0" } }, + "node_modules/is-inside-container": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/is-inside-container/-/is-inside-container-1.0.0.tgz", + "integrity": "sha512-KIYLCCJghfHZxqjYBE7rEy0OBuTd5xCHS7tHVgvCLkx7StIoaxwNW3hCALgEUjFfeRk+MG/Qxmp/vtETEF3tRA==", + "dev": true, + "dependencies": { + "is-docker": "^3.0.0" + }, + "bin": { + "is-inside-container": "cli.js" + }, + "engines": { + "node": ">=14.16" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, "node_modules/is-installed-globally": { "version": "0.4.0", "resolved": "https://registry.npmjs.org/is-installed-globally/-/is-installed-globally-0.4.0.tgz", @@ -4698,6 +4796,21 @@ "node": ">=8" } }, + "node_modules/is-wsl/node_modules/is-docker": { + "version": "2.2.1", + "resolved": "https://registry.npmjs.org/is-docker/-/is-docker-2.2.1.tgz", + "integrity": "sha512-F+i2BKsFrH66iaUFc0woD8sLy8getkwTwtOBjvs56Cx4CgJDeKQeqfz8wAYiSb8JOprWhHH5p77PbmYCvvUuXQ==", + "dev": true, + "bin": { + "is-docker": "cli.js" + }, + "engines": { + "node": ">=8" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, "node_modules/is-yarn-global": { "version": "0.4.1", "resolved": "https://registry.npmjs.org/is-yarn-global/-/is-yarn-global-0.4.1.tgz", @@ -4719,6 +4832,22 @@ "integrity": "sha512-RHxMLp9lnKHGHRng9QFhRCMbYAcVpn69smSGcq3f36xjgVVWThj4qqLbTLlq7Ssj8B+fIQ1EuCEGI2lKsyQeIw==", "dev": true }, + "node_modules/issue-parser": { + "version": "6.0.0", + "resolved": "https://registry.npmjs.org/issue-parser/-/issue-parser-6.0.0.tgz", + "integrity": "sha512-zKa/Dxq2lGsBIXQ7CUZWTHfvxPC2ej0KfO7fIPqLlHB9J2hJ7rGhZ5rilhuufylr4RXYPzJUeFjKxz305OsNlA==", + "dev": true, + "dependencies": { + "lodash.capitalize": "^4.2.1", + "lodash.escaperegexp": "^4.1.2", + "lodash.isplainobject": "^4.0.6", + "lodash.isstring": "^4.0.1", + "lodash.uniqby": "^4.7.0" + }, + "engines": { + "node": ">=10.13" + } + }, "node_modules/istanbul-lib-coverage": { "version": "3.2.0", "resolved": "https://registry.npmjs.org/istanbul-lib-coverage/-/istanbul-lib-coverage-3.2.0.tgz", @@ -4835,16 +4964,6 @@ "url": "https://github.com/sponsors/ljharb" } }, - "node_modules/js-sdsl": { - "version": "4.3.0", - "resolved": "https://registry.npmjs.org/js-sdsl/-/js-sdsl-4.3.0.tgz", - "integrity": "sha512-mifzlm2+5nZ+lEcLJMoBK0/IH/bDg8XnJfd/Wq6IP+xoCjLZsTOnV2QpxlVbX9bMnkl5PdEjNtBJ9Cj1NjifhQ==", - "dev": true, - "funding": { - "type": "opencollective", - "url": "https://opencollective.com/js-sdsl" - } - }, "node_modules/js-tokens": { "version": "4.0.0", "resolved": "https://registry.npmjs.org/js-tokens/-/js-tokens-4.0.0.tgz", @@ -4993,6 +5112,18 @@ "integrity": "sha512-v2kDEe57lecTulaDIuNTPy3Ry4gLGJ6Z1O3vE1krgXZNrsQ+LFTGHVxVjcXPs17LhbZVGedAJv8XZ1tvj5FvSg==", "dev": true }, + "node_modules/lodash.capitalize": { + "version": "4.2.1", + "resolved": "https://registry.npmjs.org/lodash.capitalize/-/lodash.capitalize-4.2.1.tgz", + "integrity": "sha512-kZzYOKspf8XVX5AvmQF94gQW0lejFVgb80G85bU4ZWzoJ6C03PQg3coYAUpSTpQWelrZELd3XWgHzw4Ck5kaIw==", + "dev": true + }, + "node_modules/lodash.escaperegexp": { + "version": "4.1.2", + "resolved": "https://registry.npmjs.org/lodash.escaperegexp/-/lodash.escaperegexp-4.1.2.tgz", + "integrity": "sha512-TM9YBvyC84ZxE3rgfefxUWiQKLilstD6k7PTGt6wfbtXF8ixIJLOL3VYyV/z+ZiPLsVxAsKAFVwWlWeb2Y8Yyw==", + "dev": true + }, "node_modules/lodash.flattendeep": { "version": "4.4.0", "resolved": "https://registry.npmjs.org/lodash.flattendeep/-/lodash.flattendeep-4.4.0.tgz", @@ -5005,12 +5136,30 @@ "integrity": "sha512-z+Uw/vLuy6gQe8cfaFWD7p0wVv8fJl3mbzXh33RS+0oW2wvUqiRXiQ69gLWSLpgB5/6sU+r6BlQR0MBILadqTQ==", "dev": true }, + "node_modules/lodash.isplainobject": { + "version": "4.0.6", + "resolved": "https://registry.npmjs.org/lodash.isplainobject/-/lodash.isplainobject-4.0.6.tgz", + "integrity": "sha512-oSXzaWypCMHkPC3NvBEaPHf0KsA5mvPrOPgQWDsbg8n7orZ290M0BmC/jgRZ4vcJ6DTAhjrsSYgdsW/F+MFOBA==", + "dev": true + }, + "node_modules/lodash.isstring": { + "version": "4.0.1", + "resolved": "https://registry.npmjs.org/lodash.isstring/-/lodash.isstring-4.0.1.tgz", + "integrity": "sha512-0wJxfxH1wgO3GrbuP+dTTk7op+6L41QCXbGINEmD+ny/G/eCqGzxyCsh7159S+mgDDcoarnBw6PC1PS5+wUGgw==", + "dev": true + }, "node_modules/lodash.merge": { "version": "4.6.2", "resolved": "https://registry.npmjs.org/lodash.merge/-/lodash.merge-4.6.2.tgz", "integrity": "sha512-0KpjqXRVvrYyCsX1swR/XTK0va6VQkQM6MNo7PqW77ByjAhoARA8EfrP1N4+KlKj8YS0ZUCtRT/YUuhyYDujIQ==", "dev": true }, + "node_modules/lodash.uniqby": { + "version": "4.7.0", + "resolved": "https://registry.npmjs.org/lodash.uniqby/-/lodash.uniqby-4.7.0.tgz", + "integrity": "sha512-e/zcLx6CSbmaEgFHCA7BnoQKyCtKMxnuWrJygbwPs/AIn+IMKl66L8/s+wBUn5LRw2pZx3bUHibiV1b6aTWIww==", + "dev": true + }, "node_modules/log-symbols": { "version": "5.1.0", "resolved": "https://registry.npmjs.org/log-symbols/-/log-symbols-5.1.0.tgz", @@ -5088,9 +5237,9 @@ "dev": true }, "node_modules/marked": { - "version": "4.2.12", - "resolved": "https://registry.npmjs.org/marked/-/marked-4.2.12.tgz", - "integrity": "sha512-yr8hSKa3Fv4D3jdZmtMMPghgVt6TWbk86WQaWhDloQjRSQhMMYCAro7jP7VDJrjjdV8pxVxMssXS8B8Y5DZ5aw==", + "version": "4.3.0", + "resolved": "https://registry.npmjs.org/marked/-/marked-4.3.0.tgz", + "integrity": "sha512-PRsaiG84bK+AMvxziE/lCFss8juXjNaWzVbN5tXAm4XjeaS9NAHhop+PjQxz2A9h8Q4M/xGmzP8vqNwy6JeK0A==", "dev": true, "bin": { "marked": "bin/marked.js" @@ -5563,10 +5712,13 @@ "dev": true }, "node_modules/mute-stream": { - "version": "0.0.8", - "resolved": "https://registry.npmjs.org/mute-stream/-/mute-stream-0.0.8.tgz", - "integrity": "sha512-nnbWWOkoWyUsTjKrhgD0dcz22mdkSnpYqbEjIm2nhwhuxlSkpywJmBo8h0ZqJdkp73mb90SssHkN4rsRaBAfAA==", - "dev": true + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/mute-stream/-/mute-stream-1.0.0.tgz", + "integrity": "sha512-avsJQhyd+680gKXyG/sQc0nXaC6rBkPOfyHYcFb9+hdkqQkR9bdnkJ0AMZhke0oesPqIO+mFFJ+IdBc7mst4IA==", + "dev": true, + "engines": { + "node": "^14.17.0 || ^16.13.0 || >=18.0.0" + } }, "node_modules/nanoid": { "version": "3.3.3", @@ -5661,9 +5813,9 @@ } }, "node_modules/node-fetch": { - "version": "3.3.0", - "resolved": "https://registry.npmjs.org/node-fetch/-/node-fetch-3.3.0.tgz", - "integrity": "sha512-BKwRP/O0UvoMKp7GNdwPlObhYGB5DQqwhEDQlNKuoqwVYSxkSZCSbHjnFFmUEtwSKRPU4kNK8PbDYYitwaE3QA==", + "version": "3.3.1", + "resolved": "https://registry.npmjs.org/node-fetch/-/node-fetch-3.3.1.tgz", + "integrity": "sha512-cRVc/kyto/7E5shrWca1Wsea4y6tL9iYJE5FBCius3JQfb/4P4I295PfhgbJQBLTx6lATE4z+wK0rPM4VS2uow==", "dev": true, "dependencies": { "data-uri-to-buffer": "^4.0.0", @@ -5864,17 +6016,18 @@ } }, "node_modules/open": { - "version": "8.4.0", - "resolved": "https://registry.npmjs.org/open/-/open-8.4.0.tgz", - "integrity": "sha512-XgFPPM+B28FtCCgSb9I+s9szOC1vZRSwgWsRUA5ylIxRTgKozqjOCrVOqGsYABPYK5qnfqClxZTFBa8PKt2v6Q==", + "version": "9.1.0", + "resolved": "https://registry.npmjs.org/open/-/open-9.1.0.tgz", + "integrity": "sha512-OS+QTnw1/4vrf+9hh1jc1jnYjzSG4ttTBB8UxOwAnInG3Uo4ssetzC1ihqaIHjLJnA5GGlRl6QlZXOTQhRBUvg==", "dev": true, "dependencies": { - "define-lazy-prop": "^2.0.0", - "is-docker": "^2.1.1", + "default-browser": "^4.0.0", + "define-lazy-prop": "^3.0.0", + "is-inside-container": "^1.0.0", "is-wsl": "^2.2.0" }, "engines": { - "node": ">=12" + "node": ">=14.16" }, "funding": { "url": "https://github.com/sponsors/sindresorhus" @@ -5898,18 +6051,18 @@ } }, "node_modules/ora": { - "version": "6.1.2", - "resolved": "https://registry.npmjs.org/ora/-/ora-6.1.2.tgz", - "integrity": "sha512-EJQ3NiP5Xo94wJXIzAyOtSb0QEIAUu7m8t6UZ9krbz0vAJqr92JpcK/lEXg91q6B9pEGqrykkd2EQplnifDSBw==", + "version": "6.3.0", + "resolved": "https://registry.npmjs.org/ora/-/ora-6.3.0.tgz", + "integrity": "sha512-1/D8uRFY0ay2kgBpmAwmSA404w4OoPVhHMqRqtjvrcK/dnzcEZxMJ+V4DUbyICu8IIVRclHcOf5wlD1tMY4GUQ==", "dev": true, "dependencies": { - "bl": "^5.0.0", "chalk": "^5.0.0", "cli-cursor": "^4.0.0", "cli-spinners": "^2.6.1", "is-interactive": "^2.0.0", "is-unicode-supported": "^1.1.0", "log-symbols": "^5.1.0", + "stdin-discarder": "^0.1.0", "strip-ansi": "^7.0.1", "wcwidth": "^1.0.1" }, @@ -5921,12 +6074,12 @@ } }, "node_modules/os-name": { - "version": "5.0.1", - "resolved": "https://registry.npmjs.org/os-name/-/os-name-5.0.1.tgz", - "integrity": "sha512-0EQpaHUHq7olp2/YFUr+0vZi9tMpDTblHGz+Ch5RntKxiRXOAY0JOz1UlxhSjMSksHvkm13eD6elJj3M8Ht/kw==", + "version": "5.1.0", + "resolved": "https://registry.npmjs.org/os-name/-/os-name-5.1.0.tgz", + "integrity": "sha512-YEIoAnM6zFmzw3PQ201gCVCIWbXNyKObGlVvpAVvraAeOHnlYVKFssbA/riRX5R40WA6kKrZ7Dr7dWzO3nKSeQ==", "dev": true, "dependencies": { - "macos-release": "^3.0.1", + "macos-release": "^3.1.0", "windows-release": "^5.0.1" }, "engines": { @@ -6456,9 +6609,9 @@ } }, "node_modules/readable-stream": { - "version": "3.6.1", - "resolved": "https://registry.npmjs.org/readable-stream/-/readable-stream-3.6.1.tgz", - "integrity": "sha512-+rQmrWMYGA90yenhTYsLWAsLsqVC8osOw6PKE1HDYiO0gdPeKe/xDHNzIAIn4C91YQ6oenEhfYqqc1883qHbjQ==", + "version": "3.6.2", + "resolved": "https://registry.npmjs.org/readable-stream/-/readable-stream-3.6.2.tgz", + "integrity": "sha512-9u/sniCrY3D5WdsERHzHE4G2YCXqoG5FTHUiCC4SIbr6XcLZBY05ya9EKjYek9O5xOAwjGq+1JdGBAS7Q9ScoA==", "dev": true, "dependencies": { "inherits": "^2.0.3", @@ -6510,18 +6663,6 @@ "url": "https://github.com/sponsors/ljharb" } }, - "node_modules/regexpp": { - "version": "3.2.0", - "resolved": "https://registry.npmjs.org/regexpp/-/regexpp-3.2.0.tgz", - "integrity": "sha512-pq2bWo9mVD43nbts2wGv17XLiNLya+GklZ8kaDLV2Z08gDCsGpnKn9BFMepvWuHCbyVvY7J5o5+BVvoQbmlJLg==", - "dev": true, - "engines": { - "node": ">=8" - }, - "funding": { - "url": "https://github.com/sponsors/mysticatea" - } - }, "node_modules/registry-auth-token": { "version": "5.0.1", "resolved": "https://registry.npmjs.org/registry-auth-token/-/registry-auth-token-5.0.1.tgz", @@ -6550,32 +6691,33 @@ } }, "node_modules/release-it": { - "version": "15.6.0", - "resolved": "https://registry.npmjs.org/release-it/-/release-it-15.6.0.tgz", - "integrity": "sha512-NXewgzO8QV1LOFjn2K7/dgE1Y1cG+2JiLOU/x9X/Lq9UdFn2hTH1r9SSrufCxG+y/Rp+oN8liYTsNptKrj92kg==", + "version": "15.10.3", + "resolved": "https://registry.npmjs.org/release-it/-/release-it-15.10.3.tgz", + "integrity": "sha512-OSdHOg76gwkpLbSLBK09GZQj5XWXwBP+S6v//rSoQKkjqklaCLK04Gl5NkTwNrQOHHiihs4ToesDNh2+w55k3w==", "dev": true, "dependencies": { "@iarna/toml": "2.2.5", - "@octokit/rest": "19.0.5", + "@octokit/rest": "19.0.7", "async-retry": "1.3.3", - "chalk": "5.1.2", - "cosmiconfig": "8.0.0", - "execa": "6.1.0", + "chalk": "5.2.0", + "cosmiconfig": "8.1.3", + "execa": "7.1.1", "git-url-parse": "13.1.0", - "globby": "13.1.2", - "got": "12.5.3", - "inquirer": "9.1.4", + "globby": "13.1.4", + "got": "12.6.0", + "inquirer": "9.2.0", "is-ci": "3.0.1", + "issue-parser": "6.0.0", "lodash": "4.17.21", "mime-types": "2.1.35", "new-github-release-url": "2.0.0", - "node-fetch": "3.3.0", - "open": "8.4.0", - "ora": "6.1.2", - "os-name": "5.0.1", + "node-fetch": "3.3.1", + "open": "9.1.0", + "ora": "6.3.0", + "os-name": "5.1.0", "promise.allsettled": "1.0.6", "proxy-agent": "5.0.0", - "semver": "7.3.8", + "semver": "7.5.0", "shelljs": "0.8.5", "update-notifier": "6.0.2", "url-join": "5.0.0", @@ -6590,9 +6732,9 @@ } }, "node_modules/release-it/node_modules/globby": { - "version": "13.1.2", - "resolved": "https://registry.npmjs.org/globby/-/globby-13.1.2.tgz", - "integrity": "sha512-LKSDZXToac40u8Q1PQtZihbNdTYSNMuWe+K5l+oa6KgDzSvVrHXlJy40hUP522RjAIoNLJYBJi7ow+rbFpIhHQ==", + "version": "13.1.4", + "resolved": "https://registry.npmjs.org/globby/-/globby-13.1.4.tgz", + "integrity": "sha512-iui/IiiW+QrJ1X1hKH5qwlMQyv34wJAYwH1vrf8b9kBA4sNiif3gKsMHa+BrdnOpEudWjpotfa7LrTzB1ERS/g==", "dev": true, "dependencies": { "dir-glob": "^3.0.1", @@ -6621,9 +6763,9 @@ } }, "node_modules/release-it/node_modules/semver": { - "version": "7.3.8", - "resolved": "https://registry.npmjs.org/semver/-/semver-7.3.8.tgz", - "integrity": "sha512-NB1ctGL5rlHrPJtFDVIVzTyQylMLu9N9VICA6HSFJo8MCGVTMW6gfpicwKmmK/dAjTOrqu5l63JJOpDSrAis3A==", + "version": "7.5.0", + "resolved": "https://registry.npmjs.org/semver/-/semver-7.5.0.tgz", + "integrity": "sha512-+XC0AD/R7Q2mPSRuy2Id0+CGTZ98+8f+KvwirxOKIEyid+XSx6HbC63p+O4IndTHuX5Z+JxQ0TghCkO5Cg/2HA==", "dev": true, "dependencies": { "lru-cache": "^6.0.0" @@ -6801,6 +6943,110 @@ "url": "https://github.com/sponsors/isaacs" } }, + "node_modules/run-applescript": { + "version": "5.0.0", + "resolved": "https://registry.npmjs.org/run-applescript/-/run-applescript-5.0.0.tgz", + "integrity": "sha512-XcT5rBksx1QdIhlFOCtgZkB99ZEouFZ1E2Kc2LHqNW13U3/74YGdkQRmThTwxy4QIyookibDKYZOPqX//6BlAg==", + "dev": true, + "dependencies": { + "execa": "^5.0.0" + }, + "engines": { + "node": ">=12" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/run-applescript/node_modules/execa": { + "version": "5.1.1", + "resolved": "https://registry.npmjs.org/execa/-/execa-5.1.1.tgz", + "integrity": "sha512-8uSpZZocAZRBAPIEINJj3Lo9HyGitllczc27Eh5YYojjMFMn8yHMDMaUHE2Jqfq05D/wucwI4JGURyXt1vchyg==", + "dev": true, + "dependencies": { + "cross-spawn": "^7.0.3", + "get-stream": "^6.0.0", + "human-signals": "^2.1.0", + "is-stream": "^2.0.0", + "merge-stream": "^2.0.0", + "npm-run-path": "^4.0.1", + "onetime": "^5.1.2", + "signal-exit": "^3.0.3", + "strip-final-newline": "^2.0.0" + }, + "engines": { + "node": ">=10" + }, + "funding": { + "url": "https://github.com/sindresorhus/execa?sponsor=1" + } + }, + "node_modules/run-applescript/node_modules/human-signals": { + "version": "2.1.0", + "resolved": "https://registry.npmjs.org/human-signals/-/human-signals-2.1.0.tgz", + "integrity": "sha512-B4FFZ6q/T2jhhksgkbEW3HBvWIfDW85snkQgawt07S7J5QXTk6BkNV+0yAeZrM5QpMAdYlocGoljn0sJ/WQkFw==", + "dev": true, + "engines": { + "node": ">=10.17.0" + } + }, + "node_modules/run-applescript/node_modules/is-stream": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/is-stream/-/is-stream-2.0.1.tgz", + "integrity": "sha512-hFoiJiTl63nn+kstHGBtewWSKnQLpyb155KHheA1l39uvtO9nWIop1p3udqPcUd/xbF1VLMO4n7OI6p7RbngDg==", + "dev": true, + "engines": { + "node": ">=8" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/run-applescript/node_modules/mimic-fn": { + "version": "2.1.0", + "resolved": "https://registry.npmjs.org/mimic-fn/-/mimic-fn-2.1.0.tgz", + "integrity": "sha512-OqbOk5oEQeAZ8WXWydlu9HJjz9WVdEIvamMCcXmuqUYjTknH/sqsWvhQ3vgwKFRR1HpjvNBKQ37nbJgYzGqGcg==", + "dev": true, + "engines": { + "node": ">=6" + } + }, + "node_modules/run-applescript/node_modules/npm-run-path": { + "version": "4.0.1", + "resolved": "https://registry.npmjs.org/npm-run-path/-/npm-run-path-4.0.1.tgz", + "integrity": "sha512-S48WzZW777zhNIrn7gxOlISNAqi9ZC/uQFnRdbeIHhZhCA6UqpkOT8T1G7BvfdgP4Er8gF4sUbaS0i7QvIfCWw==", + "dev": true, + "dependencies": { + "path-key": "^3.0.0" + }, + "engines": { + "node": ">=8" + } + }, + "node_modules/run-applescript/node_modules/onetime": { + "version": "5.1.2", + "resolved": "https://registry.npmjs.org/onetime/-/onetime-5.1.2.tgz", + "integrity": "sha512-kbpaSSGJTWdAY5KPVeMOKXSrPtr8C8C7wodJbcsd51jRnmD+GZu8Y0VoU6Dm5Z4vWr0Ig/1NKuWRKf7j5aaYSg==", + "dev": true, + "dependencies": { + "mimic-fn": "^2.1.0" + }, + "engines": { + "node": ">=6" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/run-applescript/node_modules/strip-final-newline": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/strip-final-newline/-/strip-final-newline-2.0.0.tgz", + "integrity": "sha512-BrpvfNAE3dcvq7ll3xVumzjKjZQ5tI1sEUIKr3Uoks0XUl45St3FlatVqef9prk4jRDzhW6WZg+3bk93y6pLjA==", + "dev": true, + "engines": { + "node": ">=6" + } + }, "node_modules/run-async": { "version": "2.4.1", "resolved": "https://registry.npmjs.org/run-async/-/run-async-2.4.1.tgz", @@ -6834,9 +7080,9 @@ } }, "node_modules/rxjs": { - "version": "7.8.0", - "resolved": "https://registry.npmjs.org/rxjs/-/rxjs-7.8.0.tgz", - "integrity": "sha512-F2+gxDshqmIub1KdvZkaEfGDwLNpPvk9Fs6LD/MyQxNgMds/WH9OdDDXOmxUZpME+iSK3rQCctkL0DYyytUqMg==", + "version": "7.8.1", + "resolved": "https://registry.npmjs.org/rxjs/-/rxjs-7.8.1.tgz", + "integrity": "sha512-AA3TVj+0A2iuIoQkWEK/tqFjBq2j+6PO6Y0zJcvzLAFhEFIO3HL0vls9hWLncZbAAbK0mar7oZ4V079I/qPMxg==", "dev": true, "dependencies": { "tslib": "^2.1.0" @@ -7031,16 +7277,16 @@ "dev": true }, "node_modules/sinon": { - "version": "15.0.1", - "resolved": "https://registry.npmjs.org/sinon/-/sinon-15.0.1.tgz", - "integrity": "sha512-PZXKc08f/wcA/BMRGBze2Wmw50CWPiAH3E21EOi4B49vJ616vW4DQh4fQrqsYox2aNR/N3kCqLuB0PwwOucQrg==", + "version": "15.1.0", + "resolved": "https://registry.npmjs.org/sinon/-/sinon-15.1.0.tgz", + "integrity": "sha512-cS5FgpDdE9/zx7no8bxROHymSlPLZzq0ChbbLk1DrxBfc+eTeBK3y8nIL+nu/0QeYydhhbLIr7ecHJpywjQaoQ==", "dev": true, "dependencies": { - "@sinonjs/commons": "^2.0.0", - "@sinonjs/fake-timers": "10.0.2", - "@sinonjs/samsam": "^7.0.1", - "diff": "^5.0.0", - "nise": "^5.1.2", + "@sinonjs/commons": "^3.0.0", + "@sinonjs/fake-timers": "^10.2.0", + "@sinonjs/samsam": "^8.0.0", + "diff": "^5.1.0", + "nise": "^5.1.4", "supports-color": "^7.2.0" }, "funding": { @@ -7048,6 +7294,24 @@ "url": "https://opencollective.com/sinon" } }, + "node_modules/sinon/node_modules/@sinonjs/commons": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/@sinonjs/commons/-/commons-3.0.0.tgz", + "integrity": "sha512-jXBtWAF4vmdNmZgD5FoKsVLv3rPgDnLgPbU84LIJ3otV44vJlDRokVng5v8NFJdCf/da9legHcKaRuZs4L7faA==", + "dev": true, + "dependencies": { + "type-detect": "4.0.8" + } + }, + "node_modules/sinon/node_modules/diff": { + "version": "5.1.0", + "resolved": "https://registry.npmjs.org/diff/-/diff-5.1.0.tgz", + "integrity": "sha512-D+mk+qE8VC/PAUrlAU34N+VfXev0ghe5ywmpqrawphmVZc1bEfn56uo9qpyGp1p4xpzOHkSW4ztBd6L7Xx4ACw==", + "dev": true, + "engines": { + "node": ">=0.3.1" + } + }, "node_modules/slash": { "version": "3.0.0", "resolved": "https://registry.npmjs.org/slash/-/slash-3.0.0.tgz", @@ -7152,6 +7416,21 @@ "node": ">= 0.8" } }, + "node_modules/stdin-discarder": { + "version": "0.1.0", + "resolved": "https://registry.npmjs.org/stdin-discarder/-/stdin-discarder-0.1.0.tgz", + "integrity": "sha512-xhV7w8S+bUwlPTb4bAOUQhv8/cSS5offJuX8GQGq32ONF0ZtDWKfkdomM3HMRA+LhX6um/FZ0COqlwsjD53LeQ==", + "dev": true, + "dependencies": { + "bl": "^5.0.0" + }, + "engines": { + "node": "^12.20.0 || ^14.13.1 || >=16.0.0" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, "node_modules/stop-iteration-iterator": { "version": "1.0.0", "resolved": "https://registry.npmjs.org/stop-iteration-iterator/-/stop-iteration-iterator-1.0.0.tgz", @@ -7337,6 +7616,18 @@ "integrity": "sha512-w89qg7PI8wAdvX60bMDP+bFoD5Dvhm9oLheFp5O4a2QF0cSBGsBX4qZmadPMvVqlLJBBci+WqGGOAPvcDeNSVg==", "dev": true }, + "node_modules/titleize": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/titleize/-/titleize-3.0.0.tgz", + "integrity": "sha512-KxVu8EYHDPBdUYdKZdKtU2aj2XfEx9AfjXxE/Aj0vT06w2icA09Vus1rh6eSu1y01akYg6BjIK/hxyLJINoMLQ==", + "dev": true, + "engines": { + "node": ">=12" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, "node_modules/tmp": { "version": "0.0.33", "resolved": "https://registry.npmjs.org/tmp/-/tmp-0.0.33.tgz", @@ -7507,12 +7798,12 @@ } }, "node_modules/type-fest": { - "version": "3.6.0", - "resolved": "https://registry.npmjs.org/type-fest/-/type-fest-3.6.0.tgz", - "integrity": "sha512-RqTRtKTzvPpNdDUp1dVkKQRunlPITk4mXeqFlAZoJsS+fLRn8AdPK0TcQDumGayhU7fjlBfiBjsq3pe3rIfXZQ==", + "version": "0.20.2", + "resolved": "https://registry.npmjs.org/type-fest/-/type-fest-0.20.2.tgz", + "integrity": "sha512-Ne+eE4r0/iWnpAxD852z3A+N0Bt5RN//NjJwRd2VFHEmrywxf5vsZlh4R6lixl6B+wz/8d+maTSAkN1FIkI3LQ==", "dev": true, "engines": { - "node": ">=14.16" + "node": ">=10" }, "funding": { "url": "https://github.com/sponsors/sindresorhus" @@ -7542,14 +7833,14 @@ } }, "node_modules/typedoc": { - "version": "0.23.25", - "resolved": "https://registry.npmjs.org/typedoc/-/typedoc-0.23.25.tgz", - "integrity": "sha512-O1he153qVyoCgJYSvIyY3bPP1wAJTegZfa6tL3APinSZhJOf8CSd8F/21M6ex8pUY/fuY6n0jAsT4fIuMGA6sA==", + "version": "0.24.7", + "resolved": "https://registry.npmjs.org/typedoc/-/typedoc-0.24.7.tgz", + "integrity": "sha512-zzfKDFIZADA+XRIp2rMzLe9xZ6pt12yQOhCr7cD7/PBTjhPmMyMvGrkZ2lPNJitg3Hj1SeiYFNzCsSDrlpxpKw==", "dev": true, "dependencies": { "lunr": "^2.3.9", - "marked": "^4.2.12", - "minimatch": "^6.1.6", + "marked": "^4.3.0", + "minimatch": "^9.0.0", "shiki": "^0.14.1" }, "bin": { @@ -7559,7 +7850,7 @@ "node": ">= 14.14" }, "peerDependencies": { - "typescript": "4.6.x || 4.7.x || 4.8.x || 4.9.x" + "typescript": "4.6.x || 4.7.x || 4.8.x || 4.9.x || 5.0.x" } }, "node_modules/typedoc/node_modules/brace-expansion": { @@ -7572,15 +7863,15 @@ } }, "node_modules/typedoc/node_modules/minimatch": { - "version": "6.2.0", - "resolved": "https://registry.npmjs.org/minimatch/-/minimatch-6.2.0.tgz", - "integrity": "sha512-sauLxniAmvnhhRjFwPNnJKaPFYyddAgbYdeUpHULtCT/GhzdCx/MDNy+Y40lBxTQUrMzDE8e0S43Z5uqfO0REg==", + "version": "9.0.1", + "resolved": "https://registry.npmjs.org/minimatch/-/minimatch-9.0.1.tgz", + "integrity": "sha512-0jWhJpD/MdhPXwPuiRkCbfYfSKp2qnn2eOc279qI7f+osl/l+prKSrvhg157zSYvx/1nmgn2NqdT6k2Z7zSH9w==", "dev": true, "dependencies": { "brace-expansion": "^2.0.1" }, "engines": { - "node": ">=10" + "node": ">=16 || 14 >=14.17" }, "funding": { "url": "https://github.com/sponsors/isaacs" @@ -7653,6 +7944,15 @@ "node": ">= 0.8" } }, + "node_modules/untildify": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/untildify/-/untildify-4.0.0.tgz", + "integrity": "sha512-KK8xQ1mkzZeg9inewmFVDNkg3l5LUhoq9kN6iWYB/CC9YMG8HA+c1Q8HwDe6dEX7kErrEVNVBO3fWsVq5iDgtw==", + "dev": true, + "engines": { + "node": ">=8" + } + }, "node_modules/update-browserslist-db": { "version": "1.0.10", "resolved": "https://registry.npmjs.org/update-browserslist-db/-/update-browserslist-db-1.0.10.tgz", @@ -8258,18 +8558,31 @@ "devDependencies": { "@istanbuljs/nyc-config-typescript": "^1.0.2", "@redis/test-utils": "*", - "@types/node": "^18.14.1", + "@types/node": "^20.2.3", "nyc": "^15.1.0", - "release-it": "^15.6.0", + "release-it": "^15.10.3", "source-map-support": "^0.5.21", "ts-node": "^10.9.1", - "typedoc": "^0.23.25", - "typescript": "^4.9.5" + "typedoc": "^0.24.7", + "typescript": "^5.0.4" }, "peerDependencies": { "@redis/client": "^1.0.0" } }, + "packages/bloom/node_modules/typescript": { + "version": "5.0.4", + "resolved": "https://registry.npmjs.org/typescript/-/typescript-5.0.4.tgz", + "integrity": "sha512-cW9T5W9xY37cc+jfEnaUvX91foxtHkza3Nw3wkoF4sSlKn0MONdkdEndig/qPBWXNkmplh3NzayQzCiHM4/hqw==", + "dev": true, + "bin": { + "tsc": "bin/tsc", + "tsserver": "bin/tsserver" + }, + "engines": { + "node": ">=12.20" + } + }, "packages/client": { "name": "@redis/client", "version": "1.5.7", @@ -8282,24 +8595,37 @@ "devDependencies": { "@istanbuljs/nyc-config-typescript": "^1.0.2", "@redis/test-utils": "*", - "@types/node": "^18.14.1", - "@types/sinon": "^10.0.13", + "@types/node": "^20.2.3", + "@types/sinon": "^10.0.15", "@types/yallist": "^4.0.1", - "@typescript-eslint/eslint-plugin": "^5.53.0", - "@typescript-eslint/parser": "^5.53.0", - "eslint": "^8.34.0", + "@typescript-eslint/eslint-plugin": "^5.59.7", + "@typescript-eslint/parser": "^5.59.7", + "eslint": "^8.41.0", "nyc": "^15.1.0", - "release-it": "^15.6.0", - "sinon": "^15.0.1", + "release-it": "^15.10.3", + "sinon": "^15.1.0", "source-map-support": "^0.5.21", "ts-node": "^10.9.1", - "typedoc": "^0.23.25", - "typescript": "^4.9.5" + "typedoc": "^0.24.7", + "typescript": "^5.0.4" }, "engines": { "node": ">=14" } }, + "packages/client/node_modules/typescript": { + "version": "5.0.4", + "resolved": "https://registry.npmjs.org/typescript/-/typescript-5.0.4.tgz", + "integrity": "sha512-cW9T5W9xY37cc+jfEnaUvX91foxtHkza3Nw3wkoF4sSlKn0MONdkdEndig/qPBWXNkmplh3NzayQzCiHM4/hqw==", + "dev": true, + "bin": { + "tsc": "bin/tsc", + "tsserver": "bin/tsserver" + }, + "engines": { + "node": ">=12.20" + } + }, "packages/client/node_modules/yallist": { "version": "4.0.0", "resolved": "https://registry.npmjs.org/yallist/-/yallist-4.0.0.tgz", @@ -8312,18 +8638,31 @@ "devDependencies": { "@istanbuljs/nyc-config-typescript": "^1.0.2", "@redis/test-utils": "*", - "@types/node": "^18.14.1", + "@types/node": "^20.2.3", "nyc": "^15.1.0", - "release-it": "^15.6.0", + "release-it": "^15.10.3", "source-map-support": "^0.5.21", "ts-node": "^10.9.1", - "typedoc": "^0.23.25", - "typescript": "^4.9.5" + "typedoc": "^0.24.7", + "typescript": "^5.0.4" }, "peerDependencies": { "@redis/client": "^1.0.0" } }, + "packages/graph/node_modules/typescript": { + "version": "5.0.4", + "resolved": "https://registry.npmjs.org/typescript/-/typescript-5.0.4.tgz", + "integrity": "sha512-cW9T5W9xY37cc+jfEnaUvX91foxtHkza3Nw3wkoF4sSlKn0MONdkdEndig/qPBWXNkmplh3NzayQzCiHM4/hqw==", + "dev": true, + "bin": { + "tsc": "bin/tsc", + "tsserver": "bin/tsserver" + }, + "engines": { + "node": ">=12.20" + } + }, "packages/json": { "name": "@redis/json", "version": "1.0.4", @@ -8331,18 +8670,31 @@ "devDependencies": { "@istanbuljs/nyc-config-typescript": "^1.0.2", "@redis/test-utils": "*", - "@types/node": "^18.14.1", + "@types/node": "^20.2.3", "nyc": "^15.1.0", - "release-it": "^15.6.0", + "release-it": "^15.10.3", "source-map-support": "^0.5.21", "ts-node": "^10.9.1", - "typedoc": "^0.23.25", - "typescript": "^4.9.5" + "typedoc": "^0.24.7", + "typescript": "^5.0.4" }, "peerDependencies": { "@redis/client": "^1.0.0" } }, + "packages/json/node_modules/typescript": { + "version": "5.0.4", + "resolved": "https://registry.npmjs.org/typescript/-/typescript-5.0.4.tgz", + "integrity": "sha512-cW9T5W9xY37cc+jfEnaUvX91foxtHkza3Nw3wkoF4sSlKn0MONdkdEndig/qPBWXNkmplh3NzayQzCiHM4/hqw==", + "dev": true, + "bin": { + "tsc": "bin/tsc", + "tsserver": "bin/tsserver" + }, + "engines": { + "node": ">=12.20" + } + }, "packages/search": { "name": "@redis/search", "version": "1.1.2", @@ -8350,31 +8702,44 @@ "devDependencies": { "@istanbuljs/nyc-config-typescript": "^1.0.2", "@redis/test-utils": "*", - "@types/node": "^18.14.1", + "@types/node": "^20.2.3", "nyc": "^15.1.0", - "release-it": "^15.6.0", + "release-it": "^15.10.3", "source-map-support": "^0.5.21", "ts-node": "^10.9.1", - "typedoc": "^0.23.25", - "typescript": "^4.9.5" + "typedoc": "^0.24.7", + "typescript": "^5.0.4" }, "peerDependencies": { "@redis/client": "^1.0.0" } }, + "packages/search/node_modules/typescript": { + "version": "5.0.4", + "resolved": "https://registry.npmjs.org/typescript/-/typescript-5.0.4.tgz", + "integrity": "sha512-cW9T5W9xY37cc+jfEnaUvX91foxtHkza3Nw3wkoF4sSlKn0MONdkdEndig/qPBWXNkmplh3NzayQzCiHM4/hqw==", + "dev": true, + "bin": { + "tsc": "bin/tsc", + "tsserver": "bin/tsserver" + }, + "engines": { + "node": ">=12.20" + } + }, "packages/test-utils": { "name": "@redis/test-utils", "devDependencies": { "@istanbuljs/nyc-config-typescript": "^1.0.2", "@types/mocha": "^10.0.1", - "@types/node": "^18.14.1", - "@types/yargs": "^17.0.22", + "@types/node": "^20.2.3", + "@types/yargs": "^17.0.24", "mocha": "^10.2.0", "nyc": "^15.1.0", "source-map-support": "^0.5.21", "ts-node": "^10.9.1", - "typescript": "^4.9.5", - "yargs": "^17.7.1" + "typescript": "^5.0.4", + "yargs": "^17.7.2" }, "peerDependencies": { "@redis/client": "^1.0.0" @@ -8450,6 +8815,19 @@ "node": ">=8" } }, + "packages/test-utils/node_modules/typescript": { + "version": "5.0.4", + "resolved": "https://registry.npmjs.org/typescript/-/typescript-5.0.4.tgz", + "integrity": "sha512-cW9T5W9xY37cc+jfEnaUvX91foxtHkza3Nw3wkoF4sSlKn0MONdkdEndig/qPBWXNkmplh3NzayQzCiHM4/hqw==", + "dev": true, + "bin": { + "tsc": "bin/tsc", + "tsserver": "bin/tsserver" + }, + "engines": { + "node": ">=12.20" + } + }, "packages/test-utils/node_modules/wrap-ansi": { "version": "7.0.0", "resolved": "https://registry.npmjs.org/wrap-ansi/-/wrap-ansi-7.0.0.tgz", @@ -8477,9 +8855,9 @@ } }, "packages/test-utils/node_modules/yargs": { - "version": "17.7.1", - "resolved": "https://registry.npmjs.org/yargs/-/yargs-17.7.1.tgz", - "integrity": "sha512-cwiTb08Xuv5fqF4AovYacTFNxk62th7LKJ6BL9IGUpTJrWoU7/7WdQGTP2SjKf1dUNBGzDd28p/Yfs/GI6JrLw==", + "version": "17.7.2", + "resolved": "https://registry.npmjs.org/yargs/-/yargs-17.7.2.tgz", + "integrity": "sha512-7dSzzRQ++CKnNI/krKnYRV7JKKPUXMEh61soaHKg9mrWEhzFWhFnxPxGl+69cD1Ou63C13NUPCnmIcrvqCuM6w==", "dev": true, "dependencies": { "cliui": "^8.0.1", @@ -8501,17 +8879,30 @@ "devDependencies": { "@istanbuljs/nyc-config-typescript": "^1.0.2", "@redis/test-utils": "*", - "@types/node": "^18.14.1", + "@types/node": "^20.2.3", "nyc": "^15.1.0", - "release-it": "^15.6.0", + "release-it": "^15.10.3", "source-map-support": "^0.5.21", "ts-node": "^10.9.1", - "typedoc": "^0.23.25", - "typescript": "^4.9.5" + "typedoc": "^0.24.7", + "typescript": "^5.0.4" }, "peerDependencies": { "@redis/client": "^1.0.0" } + }, + "packages/time-series/node_modules/typescript": { + "version": "5.0.4", + "resolved": "https://registry.npmjs.org/typescript/-/typescript-5.0.4.tgz", + "integrity": "sha512-cW9T5W9xY37cc+jfEnaUvX91foxtHkza3Nw3wkoF4sSlKn0MONdkdEndig/qPBWXNkmplh3NzayQzCiHM4/hqw==", + "dev": true, + "bin": { + "tsc": "bin/tsc", + "tsserver": "bin/tsserver" + }, + "engines": { + "node": ">=12.20" + } } } } diff --git a/packages/bloom/package.json b/packages/bloom/package.json index d2e2c80806d..e0ad7a450b6 100644 --- a/packages/bloom/package.json +++ b/packages/bloom/package.json @@ -18,13 +18,13 @@ "devDependencies": { "@istanbuljs/nyc-config-typescript": "^1.0.2", "@redis/test-utils": "*", - "@types/node": "^18.14.1", + "@types/node": "^20.2.3", "nyc": "^15.1.0", - "release-it": "^15.6.0", + "release-it": "^15.10.3", "source-map-support": "^0.5.21", "ts-node": "^10.9.1", - "typedoc": "^0.23.25", - "typescript": "^4.9.5" + "typedoc": "^0.24.7", + "typescript": "^5.0.4" }, "repository": { "type": "git", diff --git a/packages/client/package.json b/packages/client/package.json index 5f5793251dc..c26a4859265 100644 --- a/packages/client/package.json +++ b/packages/client/package.json @@ -21,19 +21,19 @@ "devDependencies": { "@istanbuljs/nyc-config-typescript": "^1.0.2", "@redis/test-utils": "*", - "@types/node": "^18.14.1", - "@types/sinon": "^10.0.13", + "@types/node": "^20.2.3", + "@types/sinon": "^10.0.15", "@types/yallist": "^4.0.1", - "@typescript-eslint/eslint-plugin": "^5.53.0", - "@typescript-eslint/parser": "^5.53.0", - "eslint": "^8.34.0", + "@typescript-eslint/eslint-plugin": "^5.59.7", + "@typescript-eslint/parser": "^5.59.7", + "eslint": "^8.41.0", "nyc": "^15.1.0", - "release-it": "^15.6.0", - "sinon": "^15.0.1", + "release-it": "^15.10.3", + "sinon": "^15.1.0", "source-map-support": "^0.5.21", "ts-node": "^10.9.1", - "typedoc": "^0.23.25", - "typescript": "^4.9.5" + "typedoc": "^0.24.7", + "typescript": "^5.0.4" }, "engines": { "node": ">=14" diff --git a/packages/graph/lib/graph.spec.ts b/packages/graph/lib/graph.spec.ts index ff45759fe83..495c6d17a8a 100644 --- a/packages/graph/lib/graph.spec.ts +++ b/packages/graph/lib/graph.spec.ts @@ -68,7 +68,7 @@ describe('Graph', () => { // check with and without metadata cache for (let i = 0; i < 2; i++) { - const { data } = await graph.query('CREATE ()-[edge :edge]->() RETURN edge'); + const { data } = await graph.query('CREATE ()-[edge :edge]->() RETURN edge'); assert.ok(Array.isArray(data)); assert.equal(data.length, 1); assert.equal(typeof data[0].edge.id, 'number'); @@ -85,7 +85,7 @@ describe('Graph', () => { // check with and without metadata cache for (let i = 0; i < 2; i++) { - const { data } = await graph.query('CREATE (node :node { p: 0 }) RETURN node'); + const { data } = await graph.query('CREATE (node :node { p: 0 }) RETURN node'); assert.ok(Array.isArray(data)); assert.equal(data.length, 1); assert.equal(typeof data[0].node.id, 'number'); @@ -98,7 +98,7 @@ describe('Graph', () => { const graph = new Graph(client as any, 'graph'), [, { data }] = await Promise.all([ await graph.query('CREATE ()-[:edge]->()'), - await graph.roQuery('MATCH path = ()-[:edge]->() RETURN path') + await graph.roQuery('MATCH path = ()-[:edge]->() RETURN path') ]); assert.ok(Array.isArray(data)); diff --git a/packages/graph/package.json b/packages/graph/package.json index bbd5dd778d9..659f5cdb325 100644 --- a/packages/graph/package.json +++ b/packages/graph/package.json @@ -18,13 +18,13 @@ "devDependencies": { "@istanbuljs/nyc-config-typescript": "^1.0.2", "@redis/test-utils": "*", - "@types/node": "^18.14.1", + "@types/node": "^20.2.3", "nyc": "^15.1.0", - "release-it": "^15.6.0", + "release-it": "^15.10.3", "source-map-support": "^0.5.21", "ts-node": "^10.9.1", - "typedoc": "^0.23.25", - "typescript": "^4.9.5" + "typedoc": "^0.24.7", + "typescript": "^5.0.4" }, "repository": { "type": "git", diff --git a/packages/json/package.json b/packages/json/package.json index b2f0ec9dbe2..ae074be4f5c 100644 --- a/packages/json/package.json +++ b/packages/json/package.json @@ -18,13 +18,13 @@ "devDependencies": { "@istanbuljs/nyc-config-typescript": "^1.0.2", "@redis/test-utils": "*", - "@types/node": "^18.14.1", + "@types/node": "^20.2.3", "nyc": "^15.1.0", - "release-it": "^15.6.0", + "release-it": "^15.10.3", "source-map-support": "^0.5.21", "ts-node": "^10.9.1", - "typedoc": "^0.23.25", - "typescript": "^4.9.5" + "typedoc": "^0.24.7", + "typescript": "^5.0.4" }, "repository": { "type": "git", diff --git a/packages/search/package.json b/packages/search/package.json index 61154daa014..b3b5ce37436 100644 --- a/packages/search/package.json +++ b/packages/search/package.json @@ -18,13 +18,13 @@ "devDependencies": { "@istanbuljs/nyc-config-typescript": "^1.0.2", "@redis/test-utils": "*", - "@types/node": "^18.14.1", + "@types/node": "^20.2.3", "nyc": "^15.1.0", - "release-it": "^15.6.0", + "release-it": "^15.10.3", "source-map-support": "^0.5.21", "ts-node": "^10.9.1", - "typedoc": "^0.23.25", - "typescript": "^4.9.5" + "typedoc": "^0.24.7", + "typescript": "^5.0.4" }, "repository": { "type": "git", diff --git a/packages/test-utils/package.json b/packages/test-utils/package.json index c82ca367301..d920052f3ea 100644 --- a/packages/test-utils/package.json +++ b/packages/test-utils/package.json @@ -12,13 +12,13 @@ "devDependencies": { "@istanbuljs/nyc-config-typescript": "^1.0.2", "@types/mocha": "^10.0.1", - "@types/node": "^18.14.1", - "@types/yargs": "^17.0.22", + "@types/node": "^20.2.3", + "@types/yargs": "^17.0.24", "mocha": "^10.2.0", "nyc": "^15.1.0", "source-map-support": "^0.5.21", "ts-node": "^10.9.1", - "typescript": "^4.9.5", - "yargs": "^17.7.1" + "typescript": "^5.0.4", + "yargs": "^17.7.2" } } diff --git a/packages/time-series/package.json b/packages/time-series/package.json index fffad02f62b..de2d2adc6e5 100644 --- a/packages/time-series/package.json +++ b/packages/time-series/package.json @@ -18,13 +18,13 @@ "devDependencies": { "@istanbuljs/nyc-config-typescript": "^1.0.2", "@redis/test-utils": "*", - "@types/node": "^18.14.1", + "@types/node": "^20.2.3", "nyc": "^15.1.0", - "release-it": "^15.6.0", + "release-it": "^15.10.3", "source-map-support": "^0.5.21", "ts-node": "^10.9.1", - "typedoc": "^0.23.25", - "typescript": "^4.9.5" + "typedoc": "^0.24.7", + "typescript": "^5.0.4" }, "repository": { "type": "git", From 454617bf830f851c7db2660427b699b47092e342 Mon Sep 17 00:00:00 2001 From: James White Date: Mon, 29 May 2023 09:04:30 -0400 Subject: [PATCH 1372/1748] Add a note about multiple Pub/Sub channel listeners (#2433) * Add multiple listener language to pub/sub docks Clarify that multiple subscriptions create multiple listeners. * Update pub-sub.md --------- Co-authored-by: Leibale Eidelman --- docs/pub-sub.md | 2 ++ 1 file changed, 2 insertions(+) diff --git a/docs/pub-sub.md b/docs/pub-sub.md index 4b0e138d8ac..b319925569d 100644 --- a/docs/pub-sub.md +++ b/docs/pub-sub.md @@ -39,6 +39,8 @@ await client.pSubscribe('channe*', listener); await client.sSubscribe('channel', listener); ``` +> ⚠️ Subscribing to the same channel more than once will create multiple listeners which will each be called when a message is recieved. + ## Publishing ```javascript From 99c7cd7c5528160b5fc15fcce896cdbfb2e7417f Mon Sep 17 00:00:00 2001 From: Leibale Date: Wed, 31 May 2023 15:44:12 +0300 Subject: [PATCH 1373/1748] Release search@1.1.3 --- packages/search/package.json | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/packages/search/package.json b/packages/search/package.json index b3b5ce37436..ec59e5e57b1 100644 --- a/packages/search/package.json +++ b/packages/search/package.json @@ -1,6 +1,6 @@ { "name": "@redis/search", - "version": "1.1.2", + "version": "1.1.3", "license": "MIT", "main": "./dist/index.js", "types": "./dist/index.d.ts", From 2baf5df23cb345609ec937813452540c39f298e4 Mon Sep 17 00:00:00 2001 From: Leibale Date: Wed, 31 May 2023 15:46:32 +0300 Subject: [PATCH 1374/1748] Release client@1.5.8 --- packages/client/package.json | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/packages/client/package.json b/packages/client/package.json index c26a4859265..81213710e41 100644 --- a/packages/client/package.json +++ b/packages/client/package.json @@ -1,6 +1,6 @@ { "name": "@redis/client", - "version": "1.5.7", + "version": "1.5.8", "license": "MIT", "main": "./dist/index.js", "types": "./dist/index.d.ts", From 9250fd740c9f0484cc13461f00d1bff02f04ecc9 Mon Sep 17 00:00:00 2001 From: Leibale Date: Wed, 31 May 2023 15:47:25 +0300 Subject: [PATCH 1375/1748] upgrade subpackages --- package-lock.json | 8 ++++---- package.json | 4 ++-- 2 files changed, 6 insertions(+), 6 deletions(-) diff --git a/package-lock.json b/package-lock.json index 3a67efff081..20ee56046f4 100644 --- a/package-lock.json +++ b/package-lock.json @@ -13,10 +13,10 @@ ], "dependencies": { "@redis/bloom": "1.2.0", - "@redis/client": "1.5.7", + "@redis/client": "1.5.8", "@redis/graph": "1.1.0", "@redis/json": "1.0.4", - "@redis/search": "1.1.2", + "@redis/search": "1.1.3", "@redis/time-series": "1.0.4" }, "devDependencies": { @@ -8585,7 +8585,7 @@ }, "packages/client": { "name": "@redis/client", - "version": "1.5.7", + "version": "1.5.8", "license": "MIT", "dependencies": { "cluster-key-slot": "1.1.2", @@ -8697,7 +8697,7 @@ }, "packages/search": { "name": "@redis/search", - "version": "1.1.2", + "version": "1.1.3", "license": "MIT", "devDependencies": { "@istanbuljs/nyc-config-typescript": "^1.0.2", diff --git a/package.json b/package.json index 423c8506648..9f85c0de269 100644 --- a/package.json +++ b/package.json @@ -24,10 +24,10 @@ }, "dependencies": { "@redis/bloom": "1.2.0", - "@redis/client": "1.5.7", + "@redis/client": "1.5.8", "@redis/graph": "1.1.0", "@redis/json": "1.0.4", - "@redis/search": "1.1.2", + "@redis/search": "1.1.3", "@redis/time-series": "1.0.4" }, "devDependencies": { From 79b0df8229b0c0e43b7da2a81eb382558946aa59 Mon Sep 17 00:00:00 2001 From: Leibale Date: Wed, 31 May 2023 15:47:56 +0300 Subject: [PATCH 1376/1748] Release redis@4.6.7 --- package-lock.json | 4 ++-- package.json | 2 +- 2 files changed, 3 insertions(+), 3 deletions(-) diff --git a/package-lock.json b/package-lock.json index 20ee56046f4..9bd47fdbc24 100644 --- a/package-lock.json +++ b/package-lock.json @@ -1,12 +1,12 @@ { "name": "redis", - "version": "4.6.6", + "version": "4.6.7", "lockfileVersion": 3, "requires": true, "packages": { "": { "name": "redis", - "version": "4.6.6", + "version": "4.6.7", "license": "MIT", "workspaces": [ "./packages/*" diff --git a/package.json b/package.json index 9f85c0de269..d7a07771d27 100644 --- a/package.json +++ b/package.json @@ -1,7 +1,7 @@ { "name": "redis", "description": "A modern, high performance Redis client", - "version": "4.6.6", + "version": "4.6.7", "license": "MIT", "main": "./dist/index.js", "types": "./dist/index.d.ts", From a7d5bc7ca4da6cde66140ae4fea96a8ea6e7ce64 Mon Sep 17 00:00:00 2001 From: Leibale Eidelman Date: Tue, 6 Jun 2023 10:24:59 -0400 Subject: [PATCH 1377/1748] fix #2526 - fix json README links (#2527) --- packages/json/README.md | 24 ++++++++++++------------ 1 file changed, 12 insertions(+), 12 deletions(-) diff --git a/packages/json/README.md b/packages/json/README.md index adc125eb980..e7f70174116 100644 --- a/packages/json/README.md +++ b/packages/json/README.md @@ -1,6 +1,6 @@ # @redis/json -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. +This package provides support for the [RedisJSON](https://redis.io/docs/stack/json/) 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. @@ -10,7 +10,7 @@ For a complete example, see [`managing-json.js`](https://github.com/redis/node-r ### 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. +The [`JSON.SET`](https://redis.io/commands/json.set/) 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`": @@ -37,11 +37,11 @@ await client.json.set('noderedis:jsondata', '$', { }); ``` -For more information about RedisJSON's path syntax, [check out the documentation](https://oss.redis.com/redisjson/path/). +For more information about RedisJSON's path syntax, [check out the documentation](https://redis.io/docs/stack/json/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: +With RedisJSON, we can retrieve all or part(s) of a JSON document using the [`JSON.GET`](https://redis.io/commands/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', { @@ -62,19 +62,19 @@ const results = await client.json.get('noderedis:jsondata', { 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: +Using the [`JSON.NUMINCRBY`](https://redis.io/commands/json.numincrby/) 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: +And we can add a new object to the pets array with the [`JSON.ARRAPPEND`](https://redis.io/commands/json.arrappend/) command: ```javascript - await client.json.arrAppend('noderedis:jsondata', '.pets', { - name: 'Robin', - species: 'bird', - age: 1, - isMammal: false - }); +await client.json.arrAppend('noderedis:jsondata', '.pets', { + name: 'Robin', + species: 'bird', + age: 1, + isMammal: false +}); ``` From b58833b6ea2fd4a595f5fa2ec4d0b0c0cb16d344 Mon Sep 17 00:00:00 2001 From: alphabetkrish Date: Fri, 21 Jul 2023 01:39:35 +0530 Subject: [PATCH 1378/1748] fix #2569 - fix `@redis/search` README --- packages/search/README.md | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/packages/search/README.md b/packages/search/README.md index e7d99cf75e1..60186ba7f92 100644 --- a/packages/search/README.md +++ b/packages/search/README.md @@ -12,7 +12,7 @@ For complete examples, see [`search-hashes.js`](https://github.com/redis/node-re #### 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`: +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://redis.io/commands/ft.create) 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', { @@ -28,11 +28,11 @@ await client.ft.create('idx:animals', { }); ``` -See the [`FT.CREATE` documentation](https://oss.redis.com/redisearch/Commands/#ftcreate) for information about the different field types and additional options. +See the [`FT.CREATE` documentation](https://redis.io/commands/ft.create/#description) 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. +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://redis.io/commands/ft.search) and the [query syntax reference](https://redis.io/docs/interact/search-and-query/query) for more information. Let's write a query to find all the animals where the `species` field has the value `dog`: @@ -112,7 +112,7 @@ Note that we're using JSON Path to specify where the fields to index are in our 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: +We'll use the [RediSearch query language](https://redis.io/docs/interact/search-and-query/query) and [`FT.SEARCH`](https://redis.io/commands/ft.search) command. Here's a query to find users under the age of 30: ```javascript await client.ft.search('idx:users', '@age:[0 30]'); From 259e9b2e1f184d5e83413a73a88bda85de814ac0 Mon Sep 17 00:00:00 2001 From: Chayim Date: Thu, 10 Aug 2023 11:50:57 +0300 Subject: [PATCH 1379/1748] Updating all client licenses to clearly be MIT (#2596) --- LICENSE | 35 ++++++++++++++++------------------- 1 file changed, 16 insertions(+), 19 deletions(-) diff --git a/LICENSE b/LICENSE index db86cc4de7f..8509ccd678e 100644 --- a/LICENSE +++ b/LICENSE @@ -1,24 +1,21 @@ MIT License -Copyright (c) 2016-present Node Redis contributors. +Copyright (c) 2022-2023, Redis, inc. -Permission is hereby granted, free of charge, to any person -obtaining a copy of this software and associated documentation -files (the "Software"), to deal in the Software without -restriction, including without limitation the rights to use, -copy, modify, merge, publish, distribute, sublicense, and/or sell -copies of the Software, and to permit persons to whom the -Software is furnished to do so, subject to the following -conditions: +Permission is hereby granted, free of charge, to any person obtaining a copy +of this software and associated documentation files (the "Software"), to deal +in the Software without restriction, including without limitation the rights +to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +copies of the Software, and to permit persons to whom the Software is +furnished to do so, subject to the following conditions: -The above copyright notice and this permission notice shall be -included in all copies or substantial portions of the Software. +The above copyright notice and this permission notice shall be included in all +copies or substantial portions of the Software. -THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, -EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES -OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND -NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT -HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, -WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING -FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR -OTHER DEALINGS IN THE SOFTWARE. +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE +SOFTWARE. From bc8abd5a38110d764b7703bf502b9100b9eaa70f Mon Sep 17 00:00:00 2001 From: Joren Vandeweyer Date: Wed, 23 Aug 2023 19:20:20 +0200 Subject: [PATCH 1380/1748] fixes #2524 delaying graceful exit while pingInterval is set (#2525) * fixes #2524 * `clearTimeout` in `disconnect` too --------- Co-authored-by: Leibale Eidelman --- packages/client/lib/client/index.ts | 2 ++ 1 file changed, 2 insertions(+) diff --git a/packages/client/lib/client/index.ts b/packages/client/lib/client/index.ts index 5b9badf3f37..90cdbd09111 100644 --- a/packages/client/lib/client/index.ts +++ b/packages/client/lib/client/index.ts @@ -683,6 +683,7 @@ export default class RedisClient< QUIT(): Promise { return this.#socket.quit(async () => { + if (this.#pingTimer) clearTimeout(this.#pingTimer); const quitPromise = this.#queue.addCommand(['QUIT']); this.#tick(); const [reply] = await Promise.all([ @@ -804,6 +805,7 @@ export default class RedisClient< } async disconnect(): Promise { + if (this.#pingTimer) clearTimeout(this.#pingTimer); this.#queue.flushAll(new DisconnectsClientError()); this.#socket.disconnect(); await this.#destroyIsolationPool(); From fe74e322dd75a910d8f99f951fda7d8b6f5f612c Mon Sep 17 00:00:00 2001 From: Leibale Date: Wed, 23 Aug 2023 14:53:26 -0400 Subject: [PATCH 1381/1748] upgrade deps --- package-lock.json | 3591 ++++++++++++++------------- package.json | 8 +- packages/bloom/package.json | 8 +- packages/client/lib/client/index.ts | 2 +- packages/client/package.json | 18 +- packages/graph/package.json | 8 +- packages/json/package.json | 8 +- packages/search/package.json | 8 +- packages/test-utils/package.json | 4 +- packages/time-series/package.json | 8 +- 10 files changed, 1844 insertions(+), 1819 deletions(-) diff --git a/package-lock.json b/package-lock.json index 9bd47fdbc24..66ba3d75ed0 100644 --- a/package-lock.json +++ b/package-lock.json @@ -20,19 +20,28 @@ "@redis/time-series": "1.0.4" }, "devDependencies": { - "@tsconfig/node14": "^1.0.3", - "gh-pages": "^5.0.0", - "release-it": "^15.6.0", - "typescript": "^4.9.5" + "@tsconfig/node14": "^14.1.0", + "gh-pages": "^6.0.0", + "release-it": "^16.1.5", + "typescript": "^5.1.6" + } + }, + "node_modules/@aashutoshrathi/word-wrap": { + "version": "1.2.6", + "resolved": "https://registry.npmjs.org/@aashutoshrathi/word-wrap/-/word-wrap-1.2.6.tgz", + "integrity": "sha512-1Yjs2SvM8TflER/OD3cOjhWWOZb58A2t7wpE2S9XfBYTiIl+XFhQG2bjy4Pu1I+EAlCNUzRDYDdFwFYUKvXcIA==", + "dev": true, + "engines": { + "node": ">=0.10.0" } }, "node_modules/@ampproject/remapping": { - "version": "2.2.0", - "resolved": "https://registry.npmjs.org/@ampproject/remapping/-/remapping-2.2.0.tgz", - "integrity": "sha512-qRmjj8nj9qmLTQXXmaR1cck3UXSRMPrbsLJAasZpF+t3riI71BXed5ebIOYwQntykeZuhjsdweEc9BxH5Jc26w==", + "version": "2.2.1", + "resolved": "https://registry.npmjs.org/@ampproject/remapping/-/remapping-2.2.1.tgz", + "integrity": "sha512-lFMjJTrFL3j7L9yBxwYfCq2k6qqwHyzuUl/XBnif78PWTJYyL/dfowQHWE3sp6U6ZzqWiiIZnpTMO96zhkjwtg==", "dev": true, "dependencies": { - "@jridgewell/gen-mapping": "^0.1.0", + "@jridgewell/gen-mapping": "^0.3.0", "@jridgewell/trace-mapping": "^0.3.9" }, "engines": { @@ -40,47 +49,98 @@ } }, "node_modules/@babel/code-frame": { - "version": "7.18.6", - "resolved": "https://registry.npmjs.org/@babel/code-frame/-/code-frame-7.18.6.tgz", - "integrity": "sha512-TDCmlK5eOvH+eH7cdAFlNXeVJqWIQ7gW9tY1GJIpUtFb6CmjVyq2VM3u71bOyR8CRihcCgMUYoDNyLXao3+70Q==", + "version": "7.22.10", + "resolved": "https://registry.npmjs.org/@babel/code-frame/-/code-frame-7.22.10.tgz", + "integrity": "sha512-/KKIMG4UEL35WmI9OlvMhurwtytjvXoFcGNrOvyG9zIzA8YmPjVtIZUf7b05+TPO7G7/GEmLHDaoCgACHl9hhA==", "dev": true, "dependencies": { - "@babel/highlight": "^7.18.6" + "@babel/highlight": "^7.22.10", + "chalk": "^2.4.2" }, "engines": { "node": ">=6.9.0" } }, + "node_modules/@babel/code-frame/node_modules/ansi-styles": { + "version": "3.2.1", + "resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-3.2.1.tgz", + "integrity": "sha512-VT0ZI6kZRdTh8YyJw3SMbYm/u+NqfsAxEpWO0Pf9sq8/e94WxxOpPKx9FR1FlyCtOVDNOQ+8ntlqFxiRc+r5qA==", + "dev": true, + "dependencies": { + "color-convert": "^1.9.0" + }, + "engines": { + "node": ">=4" + } + }, + "node_modules/@babel/code-frame/node_modules/chalk": { + "version": "2.4.2", + "resolved": "https://registry.npmjs.org/chalk/-/chalk-2.4.2.tgz", + "integrity": "sha512-Mti+f9lpJNcwF4tWV8/OrTTtF1gZi+f8FqlyAdouralcFWFQWF2+NgCHShjkCb+IFBLq9buZwE1xckQU4peSuQ==", + "dev": true, + "dependencies": { + "ansi-styles": "^3.2.1", + "escape-string-regexp": "^1.0.5", + "supports-color": "^5.3.0" + }, + "engines": { + "node": ">=4" + } + }, + "node_modules/@babel/code-frame/node_modules/color-convert": { + "version": "1.9.3", + "resolved": "https://registry.npmjs.org/color-convert/-/color-convert-1.9.3.tgz", + "integrity": "sha512-QfAUtd+vFdAtFQcC8CCyYt1fYWxSqAiK2cSD6zDB8N3cpsEBAvRxp9zOGg6G/SHHJYAT88/az/IuDGALsNVbGg==", + "dev": true, + "dependencies": { + "color-name": "1.1.3" + } + }, + "node_modules/@babel/code-frame/node_modules/color-name": { + "version": "1.1.3", + "resolved": "https://registry.npmjs.org/color-name/-/color-name-1.1.3.tgz", + "integrity": "sha512-72fSenhMw2HZMTVHeCA9KCmpEIbzWiQsjN+BHcBbS9vr1mtt+vJjPdksIBNUmKAW8TFUDPJK5SUU3QhE9NEXDw==", + "dev": true + }, + "node_modules/@babel/code-frame/node_modules/escape-string-regexp": { + "version": "1.0.5", + "resolved": "https://registry.npmjs.org/escape-string-regexp/-/escape-string-regexp-1.0.5.tgz", + "integrity": "sha512-vbRorB5FUQWvla16U8R/qgaFIya2qGzwDrNmCZuYKrbdSUMG6I1ZCGQRefkRVhuOkIGVne7BQ35DSfo1qvJqFg==", + "dev": true, + "engines": { + "node": ">=0.8.0" + } + }, "node_modules/@babel/compat-data": { - "version": "7.21.0", - "resolved": "https://registry.npmjs.org/@babel/compat-data/-/compat-data-7.21.0.tgz", - "integrity": "sha512-gMuZsmsgxk/ENC3O/fRw5QY8A9/uxQbbCEypnLIiYYc/qVJtEV7ouxC3EllIIwNzMqAQee5tanFabWsUOutS7g==", + "version": "7.22.9", + "resolved": "https://registry.npmjs.org/@babel/compat-data/-/compat-data-7.22.9.tgz", + "integrity": "sha512-5UamI7xkUcJ3i9qVDS+KFDEK8/7oJ55/sJMB1Ge7IEapr7KfdfV/HErR+koZwOfd+SgtFKOKRhRakdg++DcJpQ==", "dev": true, "engines": { "node": ">=6.9.0" } }, "node_modules/@babel/core": { - "version": "7.21.0", - "resolved": "https://registry.npmjs.org/@babel/core/-/core-7.21.0.tgz", - "integrity": "sha512-PuxUbxcW6ZYe656yL3EAhpy7qXKq0DmYsrJLpbB8XrsCP9Nm+XCg9XFMb5vIDliPD7+U/+M+QJlH17XOcB7eXA==", + "version": "7.22.10", + "resolved": "https://registry.npmjs.org/@babel/core/-/core-7.22.10.tgz", + "integrity": "sha512-fTmqbbUBAwCcre6zPzNngvsI0aNrPZe77AeqvDxWM9Nm+04RrJ3CAmGHA9f7lJQY6ZMhRztNemy4uslDxTX4Qw==", "dev": true, "dependencies": { "@ampproject/remapping": "^2.2.0", - "@babel/code-frame": "^7.18.6", - "@babel/generator": "^7.21.0", - "@babel/helper-compilation-targets": "^7.20.7", - "@babel/helper-module-transforms": "^7.21.0", - "@babel/helpers": "^7.21.0", - "@babel/parser": "^7.21.0", - "@babel/template": "^7.20.7", - "@babel/traverse": "^7.21.0", - "@babel/types": "^7.21.0", + "@babel/code-frame": "^7.22.10", + "@babel/generator": "^7.22.10", + "@babel/helper-compilation-targets": "^7.22.10", + "@babel/helper-module-transforms": "^7.22.9", + "@babel/helpers": "^7.22.10", + "@babel/parser": "^7.22.10", + "@babel/template": "^7.22.5", + "@babel/traverse": "^7.22.10", + "@babel/types": "^7.22.10", "convert-source-map": "^1.7.0", "debug": "^4.1.0", "gensync": "^1.0.0-beta.2", "json5": "^2.2.2", - "semver": "^6.3.0" + "semver": "^6.3.1" }, "engines": { "node": ">=6.9.0" @@ -91,12 +151,12 @@ } }, "node_modules/@babel/generator": { - "version": "7.21.1", - "resolved": "https://registry.npmjs.org/@babel/generator/-/generator-7.21.1.tgz", - "integrity": "sha512-1lT45bAYlQhFn/BHivJs43AiW2rg3/UbLyShGfF3C0KmHvO5fSghWd5kBJy30kpRRucGzXStvnnCFniCR2kXAA==", + "version": "7.22.10", + "resolved": "https://registry.npmjs.org/@babel/generator/-/generator-7.22.10.tgz", + "integrity": "sha512-79KIf7YiWjjdZ81JnLujDRApWtl7BxTqWD88+FFdQEIOG8LJ0etDOM7CXuIgGJa55sGOwZVwuEsaLEm0PJ5/+A==", "dev": true, "dependencies": { - "@babel/types": "^7.21.0", + "@babel/types": "^7.22.10", "@jridgewell/gen-mapping": "^0.3.2", "@jridgewell/trace-mapping": "^0.3.17", "jsesc": "^2.5.1" @@ -105,177 +165,175 @@ "node": ">=6.9.0" } }, - "node_modules/@babel/generator/node_modules/@jridgewell/gen-mapping": { - "version": "0.3.2", - "resolved": "https://registry.npmjs.org/@jridgewell/gen-mapping/-/gen-mapping-0.3.2.tgz", - "integrity": "sha512-mh65xKQAzI6iBcFzwv28KVWSmCkdRBWoOh+bYQGW3+6OZvbbN3TqMGo5hqYxQniRcH9F2VZIoJCm4pa3BPDK/A==", + "node_modules/@babel/helper-compilation-targets": { + "version": "7.22.10", + "resolved": "https://registry.npmjs.org/@babel/helper-compilation-targets/-/helper-compilation-targets-7.22.10.tgz", + "integrity": "sha512-JMSwHD4J7SLod0idLq5PKgI+6g/hLD/iuWBq08ZX49xE14VpVEojJ5rHWptpirV2j020MvypRLAXAO50igCJ5Q==", "dev": true, "dependencies": { - "@jridgewell/set-array": "^1.0.1", - "@jridgewell/sourcemap-codec": "^1.4.10", - "@jridgewell/trace-mapping": "^0.3.9" + "@babel/compat-data": "^7.22.9", + "@babel/helper-validator-option": "^7.22.5", + "browserslist": "^4.21.9", + "lru-cache": "^5.1.1", + "semver": "^6.3.1" }, "engines": { - "node": ">=6.0.0" + "node": ">=6.9.0" } }, - "node_modules/@babel/helper-compilation-targets": { - "version": "7.20.7", - "resolved": "https://registry.npmjs.org/@babel/helper-compilation-targets/-/helper-compilation-targets-7.20.7.tgz", - "integrity": "sha512-4tGORmfQcrc+bvrjb5y3dG9Mx1IOZjsHqQVUz7XCNHO+iTmqxWnVg3KRygjGmpRLJGdQSKuvFinbIb0CnZwHAQ==", + "node_modules/@babel/helper-compilation-targets/node_modules/lru-cache": { + "version": "5.1.1", + "resolved": "https://registry.npmjs.org/lru-cache/-/lru-cache-5.1.1.tgz", + "integrity": "sha512-KpNARQA3Iwv+jTA0utUVVbrh+Jlrr1Fv0e56GGzAFOXN7dk/FviaDW8LHmK52DlcH4WP2n6gI8vN1aesBFgo9w==", "dev": true, "dependencies": { - "@babel/compat-data": "^7.20.5", - "@babel/helper-validator-option": "^7.18.6", - "browserslist": "^4.21.3", - "lru-cache": "^5.1.1", - "semver": "^6.3.0" - }, - "engines": { - "node": ">=6.9.0" - }, - "peerDependencies": { - "@babel/core": "^7.0.0" + "yallist": "^3.0.2" } }, + "node_modules/@babel/helper-compilation-targets/node_modules/yallist": { + "version": "3.1.1", + "resolved": "https://registry.npmjs.org/yallist/-/yallist-3.1.1.tgz", + "integrity": "sha512-a4UGQaWPH59mOXUYnAG2ewncQS4i4F43Tv3JoAM+s2VDAmS9NsK8GpDMLrCHPksFT7h3K6TOoUNn2pb7RoXx4g==", + "dev": true + }, "node_modules/@babel/helper-environment-visitor": { - "version": "7.18.9", - "resolved": "https://registry.npmjs.org/@babel/helper-environment-visitor/-/helper-environment-visitor-7.18.9.tgz", - "integrity": "sha512-3r/aACDJ3fhQ/EVgFy0hpj8oHyHpQc+LPtJoY9SzTThAsStm4Ptegq92vqKoE3vD706ZVFWITnMnxucw+S9Ipg==", + "version": "7.22.5", + "resolved": "https://registry.npmjs.org/@babel/helper-environment-visitor/-/helper-environment-visitor-7.22.5.tgz", + "integrity": "sha512-XGmhECfVA/5sAt+H+xpSg0mfrHq6FzNr9Oxh7PSEBBRUb/mL7Kz3NICXb194rCqAEdxkhPT1a88teizAFyvk8Q==", "dev": true, "engines": { "node": ">=6.9.0" } }, "node_modules/@babel/helper-function-name": { - "version": "7.21.0", - "resolved": "https://registry.npmjs.org/@babel/helper-function-name/-/helper-function-name-7.21.0.tgz", - "integrity": "sha512-HfK1aMRanKHpxemaY2gqBmL04iAPOPRj7DxtNbiDOrJK+gdwkiNRVpCpUJYbUT+aZyemKN8brqTOxzCaG6ExRg==", + "version": "7.22.5", + "resolved": "https://registry.npmjs.org/@babel/helper-function-name/-/helper-function-name-7.22.5.tgz", + "integrity": "sha512-wtHSq6jMRE3uF2otvfuD3DIvVhOsSNshQl0Qrd7qC9oQJzHvOL4qQXlQn2916+CXGywIjpGuIkoyZRRxHPiNQQ==", "dev": true, "dependencies": { - "@babel/template": "^7.20.7", - "@babel/types": "^7.21.0" + "@babel/template": "^7.22.5", + "@babel/types": "^7.22.5" }, "engines": { "node": ">=6.9.0" } }, "node_modules/@babel/helper-hoist-variables": { - "version": "7.18.6", - "resolved": "https://registry.npmjs.org/@babel/helper-hoist-variables/-/helper-hoist-variables-7.18.6.tgz", - "integrity": "sha512-UlJQPkFqFULIcyW5sbzgbkxn2FKRgwWiRexcuaR8RNJRy8+LLveqPjwZV/bwrLZCN0eUHD/x8D0heK1ozuoo6Q==", + "version": "7.22.5", + "resolved": "https://registry.npmjs.org/@babel/helper-hoist-variables/-/helper-hoist-variables-7.22.5.tgz", + "integrity": "sha512-wGjk9QZVzvknA6yKIUURb8zY3grXCcOZt+/7Wcy8O2uctxhplmUPkOdlgoNhmdVee2c92JXbf1xpMtVNbfoxRw==", "dev": true, "dependencies": { - "@babel/types": "^7.18.6" + "@babel/types": "^7.22.5" }, "engines": { "node": ">=6.9.0" } }, "node_modules/@babel/helper-module-imports": { - "version": "7.18.6", - "resolved": "https://registry.npmjs.org/@babel/helper-module-imports/-/helper-module-imports-7.18.6.tgz", - "integrity": "sha512-0NFvs3VkuSYbFi1x2Vd6tKrywq+z/cLeYC/RJNFrIX/30Bf5aiGYbtvGXolEktzJH8o5E5KJ3tT+nkxuuZFVlA==", + "version": "7.22.5", + "resolved": "https://registry.npmjs.org/@babel/helper-module-imports/-/helper-module-imports-7.22.5.tgz", + "integrity": "sha512-8Dl6+HD/cKifutF5qGd/8ZJi84QeAKh+CEe1sBzz8UayBBGg1dAIJrdHOcOM5b2MpzWL2yuotJTtGjETq0qjXg==", "dev": true, "dependencies": { - "@babel/types": "^7.18.6" + "@babel/types": "^7.22.5" }, "engines": { "node": ">=6.9.0" } }, "node_modules/@babel/helper-module-transforms": { - "version": "7.21.2", - "resolved": "https://registry.npmjs.org/@babel/helper-module-transforms/-/helper-module-transforms-7.21.2.tgz", - "integrity": "sha512-79yj2AR4U/Oqq/WOV7Lx6hUjau1Zfo4cI+JLAVYeMV5XIlbOhmjEk5ulbTc9fMpmlojzZHkUUxAiK+UKn+hNQQ==", + "version": "7.22.9", + "resolved": "https://registry.npmjs.org/@babel/helper-module-transforms/-/helper-module-transforms-7.22.9.tgz", + "integrity": "sha512-t+WA2Xn5K+rTeGtC8jCsdAH52bjggG5TKRuRrAGNM/mjIbO4GxvlLMFOEz9wXY5I2XQ60PMFsAG2WIcG82dQMQ==", "dev": true, "dependencies": { - "@babel/helper-environment-visitor": "^7.18.9", - "@babel/helper-module-imports": "^7.18.6", - "@babel/helper-simple-access": "^7.20.2", - "@babel/helper-split-export-declaration": "^7.18.6", - "@babel/helper-validator-identifier": "^7.19.1", - "@babel/template": "^7.20.7", - "@babel/traverse": "^7.21.2", - "@babel/types": "^7.21.2" + "@babel/helper-environment-visitor": "^7.22.5", + "@babel/helper-module-imports": "^7.22.5", + "@babel/helper-simple-access": "^7.22.5", + "@babel/helper-split-export-declaration": "^7.22.6", + "@babel/helper-validator-identifier": "^7.22.5" }, "engines": { "node": ">=6.9.0" + }, + "peerDependencies": { + "@babel/core": "^7.0.0" } }, "node_modules/@babel/helper-simple-access": { - "version": "7.20.2", - "resolved": "https://registry.npmjs.org/@babel/helper-simple-access/-/helper-simple-access-7.20.2.tgz", - "integrity": "sha512-+0woI/WPq59IrqDYbVGfshjT5Dmk/nnbdpcF8SnMhhXObpTq2KNBdLFRFrkVdbDOyUmHBCxzm5FHV1rACIkIbA==", + "version": "7.22.5", + "resolved": "https://registry.npmjs.org/@babel/helper-simple-access/-/helper-simple-access-7.22.5.tgz", + "integrity": "sha512-n0H99E/K+Bika3++WNL17POvo4rKWZ7lZEp1Q+fStVbUi8nxPQEBOlTmCOxW/0JsS56SKKQ+ojAe2pHKJHN35w==", "dev": true, "dependencies": { - "@babel/types": "^7.20.2" + "@babel/types": "^7.22.5" }, "engines": { "node": ">=6.9.0" } }, "node_modules/@babel/helper-split-export-declaration": { - "version": "7.18.6", - "resolved": "https://registry.npmjs.org/@babel/helper-split-export-declaration/-/helper-split-export-declaration-7.18.6.tgz", - "integrity": "sha512-bde1etTx6ZyTmobl9LLMMQsaizFVZrquTEHOqKeQESMKo4PlObf+8+JA25ZsIpZhT/WEd39+vOdLXAFG/nELpA==", + "version": "7.22.6", + "resolved": "https://registry.npmjs.org/@babel/helper-split-export-declaration/-/helper-split-export-declaration-7.22.6.tgz", + "integrity": "sha512-AsUnxuLhRYsisFiaJwvp1QF+I3KjD5FOxut14q/GzovUe6orHLesW2C7d754kRm53h5gqrz6sFl6sxc4BVtE/g==", "dev": true, "dependencies": { - "@babel/types": "^7.18.6" + "@babel/types": "^7.22.5" }, "engines": { "node": ">=6.9.0" } }, "node_modules/@babel/helper-string-parser": { - "version": "7.19.4", - "resolved": "https://registry.npmjs.org/@babel/helper-string-parser/-/helper-string-parser-7.19.4.tgz", - "integrity": "sha512-nHtDoQcuqFmwYNYPz3Rah5ph2p8PFeFCsZk9A/48dPc/rGocJ5J3hAAZ7pb76VWX3fZKu+uEr/FhH5jLx7umrw==", + "version": "7.22.5", + "resolved": "https://registry.npmjs.org/@babel/helper-string-parser/-/helper-string-parser-7.22.5.tgz", + "integrity": "sha512-mM4COjgZox8U+JcXQwPijIZLElkgEpO5rsERVDJTc2qfCDfERyob6k5WegS14SX18IIjv+XD+GrqNumY5JRCDw==", "dev": true, "engines": { "node": ">=6.9.0" } }, "node_modules/@babel/helper-validator-identifier": { - "version": "7.19.1", - "resolved": "https://registry.npmjs.org/@babel/helper-validator-identifier/-/helper-validator-identifier-7.19.1.tgz", - "integrity": "sha512-awrNfaMtnHUr653GgGEs++LlAvW6w+DcPrOliSMXWCKo597CwL5Acf/wWdNkf/tfEQE3mjkeD1YOVZOUV/od1w==", + "version": "7.22.5", + "resolved": "https://registry.npmjs.org/@babel/helper-validator-identifier/-/helper-validator-identifier-7.22.5.tgz", + "integrity": "sha512-aJXu+6lErq8ltp+JhkJUfk1MTGyuA4v7f3pA+BJ5HLfNC6nAQ0Cpi9uOquUj8Hehg0aUiHzWQbOVJGao6ztBAQ==", "dev": true, "engines": { "node": ">=6.9.0" } }, "node_modules/@babel/helper-validator-option": { - "version": "7.21.0", - "resolved": "https://registry.npmjs.org/@babel/helper-validator-option/-/helper-validator-option-7.21.0.tgz", - "integrity": "sha512-rmL/B8/f0mKS2baE9ZpyTcTavvEuWhTTW8amjzXNvYG4AwBsqTLikfXsEofsJEfKHf+HQVQbFOHy6o+4cnC/fQ==", + "version": "7.22.5", + "resolved": "https://registry.npmjs.org/@babel/helper-validator-option/-/helper-validator-option-7.22.5.tgz", + "integrity": "sha512-R3oB6xlIVKUnxNUxbmgq7pKjxpru24zlimpE8WK47fACIlM0II/Hm1RS8IaOI7NgCr6LNS+jl5l75m20npAziw==", "dev": true, "engines": { "node": ">=6.9.0" } }, "node_modules/@babel/helpers": { - "version": "7.21.0", - "resolved": "https://registry.npmjs.org/@babel/helpers/-/helpers-7.21.0.tgz", - "integrity": "sha512-XXve0CBtOW0pd7MRzzmoyuSj0e3SEzj8pgyFxnTT1NJZL38BD1MK7yYrm8yefRPIDvNNe14xR4FdbHwpInD4rA==", + "version": "7.22.10", + "resolved": "https://registry.npmjs.org/@babel/helpers/-/helpers-7.22.10.tgz", + "integrity": "sha512-a41J4NW8HyZa1I1vAndrraTlPZ/eZoga2ZgS7fEr0tZJGVU4xqdE80CEm0CcNjha5EZ8fTBYLKHF0kqDUuAwQw==", "dev": true, "dependencies": { - "@babel/template": "^7.20.7", - "@babel/traverse": "^7.21.0", - "@babel/types": "^7.21.0" + "@babel/template": "^7.22.5", + "@babel/traverse": "^7.22.10", + "@babel/types": "^7.22.10" }, "engines": { "node": ">=6.9.0" } }, "node_modules/@babel/highlight": { - "version": "7.18.6", - "resolved": "https://registry.npmjs.org/@babel/highlight/-/highlight-7.18.6.tgz", - "integrity": "sha512-u7stbOuYjaPezCuLj29hNW1v64M2Md2qupEKP1fHc7WdOA3DgLh37suiSrZYY7haUB7iBeQZ9P1uiRF359do3g==", + "version": "7.22.10", + "resolved": "https://registry.npmjs.org/@babel/highlight/-/highlight-7.22.10.tgz", + "integrity": "sha512-78aUtVcT7MUscr0K5mIEnkwxPE0MaxkR5RxRwuHaQ+JuU5AmTPhY+do2mdzVTnIJJpyBglql2pehuBIWHug+WQ==", "dev": true, "dependencies": { - "@babel/helper-validator-identifier": "^7.18.6", - "chalk": "^2.0.0", + "@babel/helper-validator-identifier": "^7.22.5", + "chalk": "^2.4.2", "js-tokens": "^4.0.0" }, "engines": { @@ -332,31 +390,10 @@ "node": ">=0.8.0" } }, - "node_modules/@babel/highlight/node_modules/has-flag": { - "version": "3.0.0", - "resolved": "https://registry.npmjs.org/has-flag/-/has-flag-3.0.0.tgz", - "integrity": "sha512-sKJf1+ceQBr4SMkvQnBDNDtf4TXpVhVGateu0t918bl30FnbE2m4vNLX+VWe/dpjlb+HugGYzW7uQXH98HPEYw==", - "dev": true, - "engines": { - "node": ">=4" - } - }, - "node_modules/@babel/highlight/node_modules/supports-color": { - "version": "5.5.0", - "resolved": "https://registry.npmjs.org/supports-color/-/supports-color-5.5.0.tgz", - "integrity": "sha512-QjVjwdXIt408MIiAqCX4oUKsgU2EqAGzs2Ppkm4aQYbjm+ZEWEcW4SfFNTr4uMNZma0ey4f5lgLrkB0aX0QMow==", - "dev": true, - "dependencies": { - "has-flag": "^3.0.0" - }, - "engines": { - "node": ">=4" - } - }, "node_modules/@babel/parser": { - "version": "7.21.2", - "resolved": "https://registry.npmjs.org/@babel/parser/-/parser-7.21.2.tgz", - "integrity": "sha512-URpaIJQwEkEC2T9Kn+Ai6Xe/02iNaVCuT/PtoRz3GPVJVDpPd7mLo+VddTbhCRU9TXqW5mSrQfXZyi8kDKOVpQ==", + "version": "7.22.10", + "resolved": "https://registry.npmjs.org/@babel/parser/-/parser-7.22.10.tgz", + "integrity": "sha512-lNbdGsQb9ekfsnjFGhEiF4hfFqGgfOP3H3d27re3n+CGhNuTSUEQdfWk556sTLNTloczcdM5TYF2LhzmDQKyvQ==", "dev": true, "bin": { "parser": "bin/babel-parser.js" @@ -366,33 +403,33 @@ } }, "node_modules/@babel/template": { - "version": "7.20.7", - "resolved": "https://registry.npmjs.org/@babel/template/-/template-7.20.7.tgz", - "integrity": "sha512-8SegXApWe6VoNw0r9JHpSteLKTpTiLZ4rMlGIm9JQ18KiCtyQiAMEazujAHrUS5flrcqYZa75ukev3P6QmUwUw==", + "version": "7.22.5", + "resolved": "https://registry.npmjs.org/@babel/template/-/template-7.22.5.tgz", + "integrity": "sha512-X7yV7eiwAxdj9k94NEylvbVHLiVG1nvzCV2EAowhxLTwODV1jl9UzZ48leOC0sH7OnuHrIkllaBgneUykIcZaw==", "dev": true, "dependencies": { - "@babel/code-frame": "^7.18.6", - "@babel/parser": "^7.20.7", - "@babel/types": "^7.20.7" + "@babel/code-frame": "^7.22.5", + "@babel/parser": "^7.22.5", + "@babel/types": "^7.22.5" }, "engines": { "node": ">=6.9.0" } }, "node_modules/@babel/traverse": { - "version": "7.21.2", - "resolved": "https://registry.npmjs.org/@babel/traverse/-/traverse-7.21.2.tgz", - "integrity": "sha512-ts5FFU/dSUPS13tv8XiEObDu9K+iagEKME9kAbaP7r0Y9KtZJZ+NGndDvWoRAYNpeWafbpFeki3q9QoMD6gxyw==", - "dev": true, - "dependencies": { - "@babel/code-frame": "^7.18.6", - "@babel/generator": "^7.21.1", - "@babel/helper-environment-visitor": "^7.18.9", - "@babel/helper-function-name": "^7.21.0", - "@babel/helper-hoist-variables": "^7.18.6", - "@babel/helper-split-export-declaration": "^7.18.6", - "@babel/parser": "^7.21.2", - "@babel/types": "^7.21.2", + "version": "7.22.10", + "resolved": "https://registry.npmjs.org/@babel/traverse/-/traverse-7.22.10.tgz", + "integrity": "sha512-Q/urqV4pRByiNNpb/f5OSv28ZlGJiFiiTh+GAHktbIrkPhPbl90+uW6SmpoLyZqutrg9AEaEf3Q/ZBRHBXgxig==", + "dev": true, + "dependencies": { + "@babel/code-frame": "^7.22.10", + "@babel/generator": "^7.22.10", + "@babel/helper-environment-visitor": "^7.22.5", + "@babel/helper-function-name": "^7.22.5", + "@babel/helper-hoist-variables": "^7.22.5", + "@babel/helper-split-export-declaration": "^7.22.6", + "@babel/parser": "^7.22.10", + "@babel/types": "^7.22.10", "debug": "^4.1.0", "globals": "^11.1.0" }, @@ -410,13 +447,13 @@ } }, "node_modules/@babel/types": { - "version": "7.21.2", - "resolved": "https://registry.npmjs.org/@babel/types/-/types-7.21.2.tgz", - "integrity": "sha512-3wRZSs7jiFaB8AjxiiD+VqN5DTG2iRvJGQ+qYFrs/654lg6kGTQWIOFjlBo5RaXuAZjBmP3+OQH4dmhqiiyYxw==", + "version": "7.22.10", + "resolved": "https://registry.npmjs.org/@babel/types/-/types-7.22.10.tgz", + "integrity": "sha512-obaoigiLrlDZ7TUQln/8m4mSqIW2QFeOrCQc9r+xsaHGNoplVNYlRVpsfE8Vj35GEm2ZH4ZhrNYogs/3fj85kg==", "dev": true, "dependencies": { - "@babel/helper-string-parser": "^7.19.4", - "@babel/helper-validator-identifier": "^7.19.1", + "@babel/helper-string-parser": "^7.22.5", + "@babel/helper-validator-identifier": "^7.22.5", "to-fast-properties": "^2.0.0" }, "engines": { @@ -461,23 +498,23 @@ } }, "node_modules/@eslint-community/regexpp": { - "version": "4.5.1", - "resolved": "https://registry.npmjs.org/@eslint-community/regexpp/-/regexpp-4.5.1.tgz", - "integrity": "sha512-Z5ba73P98O1KUYCCJTUeVpja9RcGoMdncZ6T49FCUl2lN38JtCJ+3WgIDBv0AuY4WChU5PmtJmOCTlN6FZTFKQ==", + "version": "4.7.0", + "resolved": "https://registry.npmjs.org/@eslint-community/regexpp/-/regexpp-4.7.0.tgz", + "integrity": "sha512-+HencqxU7CFJnQb7IKtuNBqS6Yx3Tz4kOL8BJXo+JyeiBm5MEX6pO8onXDkjrkCRlfYXS1Axro15ZjVFe9YgsA==", "dev": true, "engines": { "node": "^12.0.0 || ^14.0.0 || >=16.0.0" } }, "node_modules/@eslint/eslintrc": { - "version": "2.0.3", - "resolved": "https://registry.npmjs.org/@eslint/eslintrc/-/eslintrc-2.0.3.tgz", - "integrity": "sha512-+5gy6OQfk+xx3q0d6jGZZC3f3KzAkXc/IanVxd1is/VIIziRqqt3ongQz0FiTUXqTk0c7aDB3OaFuKnuSoJicQ==", + "version": "2.1.2", + "resolved": "https://registry.npmjs.org/@eslint/eslintrc/-/eslintrc-2.1.2.tgz", + "integrity": "sha512-+wvgpDsrB1YqAMdEUCcnTlpfVBH7Vqn6A/NT3D8WVXFIaKMlErPIZT3oCIAVCOtarRpMtelZLqJeU3t7WY6X6g==", "dev": true, "dependencies": { "ajv": "^6.12.4", "debug": "^4.3.2", - "espree": "^9.5.2", + "espree": "^9.6.0", "globals": "^13.19.0", "ignore": "^5.2.0", "import-fresh": "^3.2.1", @@ -492,19 +529,31 @@ "url": "https://opencollective.com/eslint" } }, + "node_modules/@eslint/eslintrc/node_modules/strip-json-comments": { + "version": "3.1.1", + "resolved": "https://registry.npmjs.org/strip-json-comments/-/strip-json-comments-3.1.1.tgz", + "integrity": "sha512-6fPc+R4ihwqP6N/aIv2f1gMH8lOVtWQHoqC4yK6oSDVVocumAsfCqjkXnqiYMhmMwS/mEHLp7Vehlt3ql6lEig==", + "dev": true, + "engines": { + "node": ">=8" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, "node_modules/@eslint/js": { - "version": "8.41.0", - "resolved": "https://registry.npmjs.org/@eslint/js/-/js-8.41.0.tgz", - "integrity": "sha512-LxcyMGxwmTh2lY9FwHPGWOHmYFCZvbrFCBZL4FzSSsxsRPuhrYUg/49/0KDfW8tnIEaEHtfmn6+NPN+1DqaNmA==", + "version": "8.47.0", + "resolved": "https://registry.npmjs.org/@eslint/js/-/js-8.47.0.tgz", + "integrity": "sha512-P6omY1zv5MItm93kLM8s2vr1HICJH8v0dvddDhysbIuZ+vcjOHg5Zbkf1mTkcmi2JA9oBG2anOkRnW8WJTS8Og==", "dev": true, "engines": { "node": "^12.22.0 || ^14.17.0 || >=16.0.0" } }, "node_modules/@humanwhocodes/config-array": { - "version": "0.11.8", - "resolved": "https://registry.npmjs.org/@humanwhocodes/config-array/-/config-array-0.11.8.tgz", - "integrity": "sha512-UybHIJzJnR5Qc/MsD9Kr+RpO2h+/P1GhOwdiLPXK5TWk5sgTdu88bTD9UP+CKbPPh5Rni1u0GjAdYQLemG8g+g==", + "version": "0.11.10", + "resolved": "https://registry.npmjs.org/@humanwhocodes/config-array/-/config-array-0.11.10.tgz", + "integrity": "sha512-KVVjQmNUepDVGXNuoRRdmmEjruj0KfiGSbS8LVc12LMsWDQzRXJ0qdhN8L8uUigKpfEHRhlaQFY0ib1tnUbNeQ==", "dev": true, "dependencies": { "@humanwhocodes/object-schema": "^1.2.1", @@ -565,6 +614,15 @@ "sprintf-js": "~1.0.2" } }, + "node_modules/@istanbuljs/load-nyc-config/node_modules/camelcase": { + "version": "5.3.1", + "resolved": "https://registry.npmjs.org/camelcase/-/camelcase-5.3.1.tgz", + "integrity": "sha512-L28STB170nwWS63UjtlEOE3dldQApaJXZkOI1uMFfzf3rRuPegHaHesyee+YxQ+W6SvRDQV6UrdOdRiR153wJg==", + "dev": true, + "engines": { + "node": ">=6" + } + }, "node_modules/@istanbuljs/load-nyc-config/node_modules/js-yaml": { "version": "3.14.1", "resolved": "https://registry.npmjs.org/js-yaml/-/js-yaml-3.14.1.tgz", @@ -612,22 +670,23 @@ } }, "node_modules/@jridgewell/gen-mapping": { - "version": "0.1.1", - "resolved": "https://registry.npmjs.org/@jridgewell/gen-mapping/-/gen-mapping-0.1.1.tgz", - "integrity": "sha512-sQXCasFk+U8lWYEe66WxRDOE9PjVz4vSM51fTu3Hw+ClTpUSQb718772vH3pyS5pShp6lvQM7SxgIDXXXmOX7w==", + "version": "0.3.3", + "resolved": "https://registry.npmjs.org/@jridgewell/gen-mapping/-/gen-mapping-0.3.3.tgz", + "integrity": "sha512-HLhSWOLRi875zjjMG/r+Nv0oCW8umGb0BgEhyX3dDX3egwZtB8PqLnjz3yedt8R5StBrzcg4aBpnh8UA9D1BoQ==", "dev": true, "dependencies": { - "@jridgewell/set-array": "^1.0.0", - "@jridgewell/sourcemap-codec": "^1.4.10" + "@jridgewell/set-array": "^1.0.1", + "@jridgewell/sourcemap-codec": "^1.4.10", + "@jridgewell/trace-mapping": "^0.3.9" }, "engines": { "node": ">=6.0.0" } }, "node_modules/@jridgewell/resolve-uri": { - "version": "3.1.0", - "resolved": "https://registry.npmjs.org/@jridgewell/resolve-uri/-/resolve-uri-3.1.0.tgz", - "integrity": "sha512-F2msla3tad+Mfht5cJq7LSXcdudKTWCVYUgw6pLFOOHSTtZlj6SWNYAp+AhuqLmWdBO2X5hPrLcu8cVP8fy28w==", + "version": "3.1.1", + "resolved": "https://registry.npmjs.org/@jridgewell/resolve-uri/-/resolve-uri-3.1.1.tgz", + "integrity": "sha512-dSYZh7HhCDtCKm4QakX0xFpsRDqjjtZf/kjI/v3T3Nwt5r8/qz/M19F9ySyOqU94SXBmeG9ttTul+YnR4LOxFA==", "dev": true, "engines": { "node": ">=6.0.0" @@ -643,19 +702,28 @@ } }, "node_modules/@jridgewell/sourcemap-codec": { - "version": "1.4.14", - "resolved": "https://registry.npmjs.org/@jridgewell/sourcemap-codec/-/sourcemap-codec-1.4.14.tgz", - "integrity": "sha512-XPSJHWmi394fuUuzDnGz1wiKqWfo1yXecHQMRf2l6hztTO+nPru658AyDngaBe7isIxEkRsPR3FZh+s7iVa4Uw==", + "version": "1.4.15", + "resolved": "https://registry.npmjs.org/@jridgewell/sourcemap-codec/-/sourcemap-codec-1.4.15.tgz", + "integrity": "sha512-eF2rxCRulEKXHTRiDrDy6erMYWqNw4LPdQ8UQA4huuxaQsVeRPFl2oM8oDGxMFhJUWZf9McpLtJasDDZb/Bpeg==", "dev": true }, "node_modules/@jridgewell/trace-mapping": { - "version": "0.3.17", - "resolved": "https://registry.npmjs.org/@jridgewell/trace-mapping/-/trace-mapping-0.3.17.tgz", - "integrity": "sha512-MCNzAp77qzKca9+W/+I0+sEpaUnZoeasnghNeVc41VZCEKaCH73Vq3BZZ/SzWIgrqE4H4ceI+p+b6C0mHf9T4g==", + "version": "0.3.19", + "resolved": "https://registry.npmjs.org/@jridgewell/trace-mapping/-/trace-mapping-0.3.19.tgz", + "integrity": "sha512-kf37QtfW+Hwx/buWGMPcR60iF9ziHa6r/CZJIHbmcm4+0qrXiVdxegAH0F6yddEVQ7zdkjcGCgCzUu+BcbhQxw==", "dev": true, "dependencies": { - "@jridgewell/resolve-uri": "3.1.0", - "@jridgewell/sourcemap-codec": "1.4.14" + "@jridgewell/resolve-uri": "^3.1.0", + "@jridgewell/sourcemap-codec": "^1.4.14" + } + }, + "node_modules/@ljharb/through": { + "version": "2.3.9", + "resolved": "https://registry.npmjs.org/@ljharb/through/-/through-2.3.9.tgz", + "integrity": "sha512-yN599ZBuMPPK4tdoToLlvgJB4CLK8fGl7ntfy0Wn7U6ttNvHYurd81bfUiK/6sMkiIwm65R6ck4L6+Y3DfVbNQ==", + "dev": true, + "engines": { + "node": ">= 0.4" } }, "node_modules/@nodelib/fs.scandir": { @@ -694,21 +762,18 @@ } }, "node_modules/@octokit/auth-token": { - "version": "3.0.3", - "resolved": "https://registry.npmjs.org/@octokit/auth-token/-/auth-token-3.0.3.tgz", - "integrity": "sha512-/aFM2M4HVDBT/jjDBa84sJniv1t9Gm/rLkalaz9htOm+L+8JMj1k9w0CkUdcxNyNxZPlTxKPVko+m1VlM58ZVA==", + "version": "3.0.4", + "resolved": "https://registry.npmjs.org/@octokit/auth-token/-/auth-token-3.0.4.tgz", + "integrity": "sha512-TWFX7cZF2LXoCvdmJWY7XVPi74aSY0+FfBZNSXEXFkMpjcqsQwDSYVv5FhRFaI0V1ECnwbz4j59T/G+rXNWaIQ==", "dev": true, - "dependencies": { - "@octokit/types": "^9.0.0" - }, "engines": { "node": ">= 14" } }, "node_modules/@octokit/core": { - "version": "4.2.1", - "resolved": "https://registry.npmjs.org/@octokit/core/-/core-4.2.1.tgz", - "integrity": "sha512-tEDxFx8E38zF3gT7sSMDrT1tGumDgsw5yPG6BBh/X+5ClIQfMH/Yqocxz1PnHx6CHyF6pxmovUTOfZAUvQ0Lvw==", + "version": "4.2.4", + "resolved": "https://registry.npmjs.org/@octokit/core/-/core-4.2.4.tgz", + "integrity": "sha512-rYKilwgzQ7/imScn3M9/pFfUf4I1AZEH3KhyJmtPdE2zfaXAn2mFfUy4FbKewzc2We5y/LlKLj36fWJLKC2SIQ==", "dev": true, "dependencies": { "@octokit/auth-token": "^3.0.0", @@ -724,9 +789,9 @@ } }, "node_modules/@octokit/endpoint": { - "version": "7.0.5", - "resolved": "https://registry.npmjs.org/@octokit/endpoint/-/endpoint-7.0.5.tgz", - "integrity": "sha512-LG4o4HMY1Xoaec87IqQ41TQ+glvIeTKqfjkCEmt5AIwDZJwQeVZFIEYXrYY6yLwK+pAScb9Gj4q+Nz2qSw1roA==", + "version": "7.0.6", + "resolved": "https://registry.npmjs.org/@octokit/endpoint/-/endpoint-7.0.6.tgz", + "integrity": "sha512-5L4fseVRUsDFGR00tMWD/Trdeeihn999rTMGRMC1G/Ldi1uWlWJzI98H4Iak5DB/RVvQuyMYKqSK/R6mbSOQyg==", "dev": true, "dependencies": { "@octokit/types": "^9.0.0", @@ -752,9 +817,9 @@ } }, "node_modules/@octokit/openapi-types": { - "version": "17.2.0", - "resolved": "https://registry.npmjs.org/@octokit/openapi-types/-/openapi-types-17.2.0.tgz", - "integrity": "sha512-MazrFNx4plbLsGl+LFesMo96eIXkFgEtaKbnNpdh4aQ0VM10aoylFsTYP1AEjkeoRNZiiPe3T6Gl2Hr8dJWdlQ==", + "version": "18.0.0", + "resolved": "https://registry.npmjs.org/@octokit/openapi-types/-/openapi-types-18.0.0.tgz", + "integrity": "sha512-V8GImKs3TeQRxRtXFpG2wl19V7444NIOTDF24AWuIbmNaNYOQMWRbjcGDXV5B+0n887fgDcuMNOmlul+k+oJtw==", "dev": true }, "node_modules/@octokit/plugin-paginate-rest": { @@ -783,13 +848,12 @@ } }, "node_modules/@octokit/plugin-rest-endpoint-methods": { - "version": "7.1.2", - "resolved": "https://registry.npmjs.org/@octokit/plugin-rest-endpoint-methods/-/plugin-rest-endpoint-methods-7.1.2.tgz", - "integrity": "sha512-R0oJ7j6f/AdqPLtB9qRXLO+wjI9pctUn8Ka8UGfGaFCcCv3Otx14CshQ89K4E88pmyYZS8p0rNTiprML/81jig==", + "version": "7.2.3", + "resolved": "https://registry.npmjs.org/@octokit/plugin-rest-endpoint-methods/-/plugin-rest-endpoint-methods-7.2.3.tgz", + "integrity": "sha512-I5Gml6kTAkzVlN7KCtjOM+Ruwe/rQppp0QU372K1GP7kNOYEKe8Xn5BW4sE62JAHdwpq95OQK/qGNyKQMUzVgA==", "dev": true, "dependencies": { - "@octokit/types": "^9.2.3", - "deprecation": "^2.3.1" + "@octokit/types": "^10.0.0" }, "engines": { "node": ">= 14" @@ -798,10 +862,19 @@ "@octokit/core": ">=3" } }, + "node_modules/@octokit/plugin-rest-endpoint-methods/node_modules/@octokit/types": { + "version": "10.0.0", + "resolved": "https://registry.npmjs.org/@octokit/types/-/types-10.0.0.tgz", + "integrity": "sha512-Vm8IddVmhCgU1fxC1eyinpwqzXPEYu0NrYzD3YZjlGjyftdLBTeqNblRC0jmJmgxbJIsQlyogVeGnrNaaMVzIg==", + "dev": true, + "dependencies": { + "@octokit/openapi-types": "^18.0.0" + } + }, "node_modules/@octokit/request": { - "version": "6.2.5", - "resolved": "https://registry.npmjs.org/@octokit/request/-/request-6.2.5.tgz", - "integrity": "sha512-z83E8UIlPNaJUsXpjD8E0V5o/5f+vJJNbNcBwVZsX3/vC650U41cOkTLjq4PKk9BYonQGOnx7N17gvLyNjgGcQ==", + "version": "6.2.8", + "resolved": "https://registry.npmjs.org/@octokit/request/-/request-6.2.8.tgz", + "integrity": "sha512-ow4+pkVQ+6XVVsekSYBzJC0VTVvh/FCTUUgTsboGq+DTeWdyIFV8WSCdo0RIxk6wSkBTHqIK1mYuY7nOBXOchw==", "dev": true, "dependencies": { "@octokit/endpoint": "^7.0.0", @@ -830,9 +903,9 @@ } }, "node_modules/@octokit/request/node_modules/node-fetch": { - "version": "2.6.11", - "resolved": "https://registry.npmjs.org/node-fetch/-/node-fetch-2.6.11.tgz", - "integrity": "sha512-4I6pdBY1EthSqDmJkiNk3JIT8cswwR9nfeW/cPdUagJYEQG7R95WRH74wpz7ma8Gh/9dI9FP+OU+0E4FvtA55w==", + "version": "2.7.0", + "resolved": "https://registry.npmjs.org/node-fetch/-/node-fetch-2.7.0.tgz", + "integrity": "sha512-c4FRfUm/dbcWZ7U+1Wq0AwCyFL+3nt2bEw05wfxSz+DWpWsitgmSgYmy2dQdWyKC1694ELPqMs/YzUSNozLt8A==", "dev": true, "dependencies": { "whatwg-url": "^5.0.0" @@ -850,15 +923,15 @@ } }, "node_modules/@octokit/rest": { - "version": "19.0.7", - "resolved": "https://registry.npmjs.org/@octokit/rest/-/rest-19.0.7.tgz", - "integrity": "sha512-HRtSfjrWmWVNp2uAkEpQnuGMJsu/+dBr47dRc5QVgsCbnIc1+GFEaoKBWkYG+zjrsHpSqcAElMio+n10c0b5JA==", + "version": "19.0.13", + "resolved": "https://registry.npmjs.org/@octokit/rest/-/rest-19.0.13.tgz", + "integrity": "sha512-/EzVox5V9gYGdbAI+ovYj3nXQT1TtTHRT+0eZPcuC05UFSWO3mdO9UY1C0i2eLF9Un1ONJkAk+IEtYGAC+TahA==", "dev": true, "dependencies": { - "@octokit/core": "^4.1.0", - "@octokit/plugin-paginate-rest": "^6.0.0", + "@octokit/core": "^4.2.1", + "@octokit/plugin-paginate-rest": "^6.1.2", "@octokit/plugin-request-log": "^1.0.4", - "@octokit/plugin-rest-endpoint-methods": "^7.0.0" + "@octokit/plugin-rest-endpoint-methods": "^7.1.2" }, "engines": { "node": ">= 14" @@ -871,12 +944,21 @@ "dev": true }, "node_modules/@octokit/types": { - "version": "9.2.3", - "resolved": "https://registry.npmjs.org/@octokit/types/-/types-9.2.3.tgz", - "integrity": "sha512-MMeLdHyFIALioycq+LFcA71v0S2xpQUX2cw6pPbHQjaibcHYwLnmK/kMZaWuGfGfjBJZ3wRUq+dOaWsvrPJVvA==", + "version": "9.3.2", + "resolved": "https://registry.npmjs.org/@octokit/types/-/types-9.3.2.tgz", + "integrity": "sha512-D4iHGTdAnEEVsB8fl95m1hiz7D5YiRdQ9b/OEb3BYRVwbLsGHcRVPz+u+BgRLNk0Q0/4iZCBqDN96j2XNxfXrA==", "dev": true, "dependencies": { - "@octokit/openapi-types": "^17.2.0" + "@octokit/openapi-types": "^18.0.0" + } + }, + "node_modules/@pnpm/config.env-replace": { + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/@pnpm/config.env-replace/-/config.env-replace-1.1.0.tgz", + "integrity": "sha512-htyl8TWnKL7K/ESFa1oW2UB5lVDxuF5DpM7tBi6Hu2LNL3mWkIzNLG6N4zoCUP1lCKNxWy/3iu8mS8MvToGd6w==", + "dev": true, + "engines": { + "node": ">=12.22.0" } }, "node_modules/@pnpm/network.ca-file": { @@ -891,12 +973,19 @@ "node": ">=12.22.0" } }, + "node_modules/@pnpm/network.ca-file/node_modules/graceful-fs": { + "version": "4.2.10", + "resolved": "https://registry.npmjs.org/graceful-fs/-/graceful-fs-4.2.10.tgz", + "integrity": "sha512-9ByhssR2fPVsNZj478qUUbKfmL0+t5BDVyjShtyZZLiK7ZDAArFFfopyOTj0M05wE2tJPisA4iTnnXl2YoPvOA==", + "dev": true + }, "node_modules/@pnpm/npm-conf": { - "version": "1.0.5", - "resolved": "https://registry.npmjs.org/@pnpm/npm-conf/-/npm-conf-1.0.5.tgz", - "integrity": "sha512-hD8ml183638O3R6/Txrh0L8VzGOrFXgRtRDG4qQC4tONdZ5Z1M+tlUUDUvrjYdmK6G+JTBTeaCLMna11cXzi8A==", + "version": "2.2.2", + "resolved": "https://registry.npmjs.org/@pnpm/npm-conf/-/npm-conf-2.2.2.tgz", + "integrity": "sha512-UA91GwWPhFExt3IizW6bOeY/pQ0BkuNwKjk9iQW9KqxluGCrg4VenZ0/L+2Y0+ZOtme72EVvg6v0zo3AMQRCeA==", "dev": true, "dependencies": { + "@pnpm/config.env-replace": "^1.1.0", "@pnpm/network.ca-file": "^1.0.1", "config-chain": "^1.1.11" }, @@ -933,9 +1022,9 @@ "link": true }, "node_modules/@sindresorhus/is": { - "version": "5.3.0", - "resolved": "https://registry.npmjs.org/@sindresorhus/is/-/is-5.3.0.tgz", - "integrity": "sha512-CX6t4SYQ37lzxicAqsBtxA3OseeoVrh9cSJ5PFYam0GksYlupRfy1A+Q4aYD3zvcfECLc0zO2u+ZnR2UYKvCrw==", + "version": "5.6.0", + "resolved": "https://registry.npmjs.org/@sindresorhus/is/-/is-5.6.0.tgz", + "integrity": "sha512-TV7t8GKYaJWsn00tFDqBw8+Uqmr8A0fRU1tvTQhyZzGv0sJCGRQL3JGMI3ucuKo3XIZdUP+Lx7/gh2t3lewy7g==", "dev": true, "engines": { "node": ">=14.16" @@ -945,32 +1034,23 @@ } }, "node_modules/@sinonjs/commons": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/@sinonjs/commons/-/commons-2.0.0.tgz", - "integrity": "sha512-uLa0j859mMrg2slwQYdO/AkrOfmH+X6LTVmNTS9CqexuE2IvVORIkSpJLqePAbEnKJ77aMmCwr1NUZ57120Xcg==", + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/@sinonjs/commons/-/commons-3.0.0.tgz", + "integrity": "sha512-jXBtWAF4vmdNmZgD5FoKsVLv3rPgDnLgPbU84LIJ3otV44vJlDRokVng5v8NFJdCf/da9legHcKaRuZs4L7faA==", "dev": true, "dependencies": { "type-detect": "4.0.8" } }, "node_modules/@sinonjs/fake-timers": { - "version": "10.2.0", - "resolved": "https://registry.npmjs.org/@sinonjs/fake-timers/-/fake-timers-10.2.0.tgz", - "integrity": "sha512-OPwQlEdg40HAj5KNF8WW6q2KG4Z+cBCZb3m4ninfTZKaBmbIJodviQsDBoYMPHkOyJJMHnOJo5j2+LKDOhOACg==", + "version": "10.3.0", + "resolved": "https://registry.npmjs.org/@sinonjs/fake-timers/-/fake-timers-10.3.0.tgz", + "integrity": "sha512-V4BG07kuYSUkTCSBHG8G8TNhM+F19jXFWnQtzj+we8DrkpSBCee9Z3Ms8yiGer/dlmhe35/Xdgyo3/0rQKg7YA==", "dev": true, "dependencies": { "@sinonjs/commons": "^3.0.0" } }, - "node_modules/@sinonjs/fake-timers/node_modules/@sinonjs/commons": { - "version": "3.0.0", - "resolved": "https://registry.npmjs.org/@sinonjs/commons/-/commons-3.0.0.tgz", - "integrity": "sha512-jXBtWAF4vmdNmZgD5FoKsVLv3rPgDnLgPbU84LIJ3otV44vJlDRokVng5v8NFJdCf/da9legHcKaRuZs4L7faA==", - "dev": true, - "dependencies": { - "type-detect": "4.0.8" - } - }, "node_modules/@sinonjs/samsam": { "version": "8.0.0", "resolved": "https://registry.npmjs.org/@sinonjs/samsam/-/samsam-8.0.0.tgz", @@ -982,6 +1062,15 @@ "type-detect": "^4.0.8" } }, + "node_modules/@sinonjs/samsam/node_modules/@sinonjs/commons": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/@sinonjs/commons/-/commons-2.0.0.tgz", + "integrity": "sha512-uLa0j859mMrg2slwQYdO/AkrOfmH+X6LTVmNTS9CqexuE2IvVORIkSpJLqePAbEnKJ77aMmCwr1NUZ57120Xcg==", + "dev": true, + "dependencies": { + "type-detect": "4.0.8" + } + }, "node_modules/@sinonjs/text-encoding": { "version": "0.7.2", "resolved": "https://registry.npmjs.org/@sinonjs/text-encoding/-/text-encoding-0.7.2.tgz", @@ -1000,14 +1089,11 @@ "node": ">=14.16" } }, - "node_modules/@tootallnate/once": { - "version": "1.1.2", - "resolved": "https://registry.npmjs.org/@tootallnate/once/-/once-1.1.2.tgz", - "integrity": "sha512-RbzJvlNzmRq5c3O09UipeuXno4tA1FE6ikOjxZK0tuxVv3412l64l5t1W5pj4+rJq9vpkm/kwiR07aZXnsKPxw==", - "dev": true, - "engines": { - "node": ">= 6" - } + "node_modules/@tootallnate/quickjs-emscripten": { + "version": "0.23.0", + "resolved": "https://registry.npmjs.org/@tootallnate/quickjs-emscripten/-/quickjs-emscripten-0.23.0.tgz", + "integrity": "sha512-C5Mc6rdnsaJDjO3UpGW/CQTHtCKaYlScZTly4JIu97Jxo/odCiH0ITnDXSJPTOrEKk/ycSZ0AOgTmkDtkOsvIA==", + "dev": true }, "node_modules/@tsconfig/node10": { "version": "1.0.9", @@ -1022,15 +1108,15 @@ "dev": true }, "node_modules/@tsconfig/node14": { - "version": "1.0.3", - "resolved": "https://registry.npmjs.org/@tsconfig/node14/-/node14-1.0.3.tgz", - "integrity": "sha512-ysT8mhdixWK6Hw3i1V2AeRqZ5WfXg1G43mqoYlM2nc6388Fq5jcXyr5mRsqViLx/GJYdoL0bfXD8nmF+Zn/Iow==", + "version": "14.1.0", + "resolved": "https://registry.npmjs.org/@tsconfig/node14/-/node14-14.1.0.tgz", + "integrity": "sha512-VmsCG04YR58ciHBeJKBDNMWWfYbyP8FekWVuTlpstaUPlat1D0x/tXzkWP7yCMU0eSz9V4OZU0LBWTFJ3xZf6w==", "dev": true }, "node_modules/@tsconfig/node16": { - "version": "1.0.3", - "resolved": "https://registry.npmjs.org/@tsconfig/node16/-/node16-1.0.3.tgz", - "integrity": "sha512-yOlFc+7UtL/89t2ZhjPvvB/DeAr3r+Dq58IgzsFkOAvVC6NMJXmCGjbptdXdR9qsX7pKcTL+s87FtYREi2dEEQ==", + "version": "1.0.4", + "resolved": "https://registry.npmjs.org/@tsconfig/node16/-/node16-1.0.4.tgz", + "integrity": "sha512-vxhUy4J8lyeyinH7Azl1pdd43GJhZH/tP2weN8TntQblOY+A0XbT8DJk1/oCPuOOyg/Ja757rG0CgHcWC8OfMA==", "dev": true }, "node_modules/@types/http-cache-semantics": { @@ -1040,9 +1126,9 @@ "dev": true }, "node_modules/@types/json-schema": { - "version": "7.0.11", - "resolved": "https://registry.npmjs.org/@types/json-schema/-/json-schema-7.0.11.tgz", - "integrity": "sha512-wOuvG1SN4Us4rez+tylwwwCV1psiNVOkJeM3AUWUNWg/jDQY2+HE/444y5gc+jBmRqASOm2Oeh5c1axHobwRKQ==", + "version": "7.0.12", + "resolved": "https://registry.npmjs.org/@types/json-schema/-/json-schema-7.0.12.tgz", + "integrity": "sha512-Hr5Jfhc9eYOQNPYO5WLDq/n4jqijdHNlDXjuAQkkt+mWdQR+XJToOHrsD4cPaMXpn6KO7y2+wM8AZEs8VpBLVA==", "dev": true }, "node_modules/@types/mocha": { @@ -1052,9 +1138,9 @@ "dev": true }, "node_modules/@types/node": { - "version": "20.2.3", - "resolved": "https://registry.npmjs.org/@types/node/-/node-20.2.3.tgz", - "integrity": "sha512-pg9d0yC4rVNWQzX8U7xb4olIOFuuVL9za3bzMT2pu2SU0SNEi66i2qrvhE2qt0HvkhuCaWJu7pLNOt/Pj8BIrw==", + "version": "20.5.3", + "resolved": "https://registry.npmjs.org/@types/node/-/node-20.5.3.tgz", + "integrity": "sha512-ITI7rbWczR8a/S6qjAW7DMqxqFMjjTo61qZVWJ1ubPvbIQsL5D/TvwjYEalM8Kthpe3hTzOGrF2TGbAu2uyqeA==", "dev": true }, "node_modules/@types/semver": { @@ -1064,9 +1150,9 @@ "dev": true }, "node_modules/@types/sinon": { - "version": "10.0.15", - "resolved": "https://registry.npmjs.org/@types/sinon/-/sinon-10.0.15.tgz", - "integrity": "sha512-3lrFNQG0Kr2LDzvjyjB6AMJk4ge+8iYhQfdnSwIwlG88FUOV43kPcQqDZkDa/h3WSZy6i8Fr0BSjfQtB1B3xuQ==", + "version": "10.0.16", + "resolved": "https://registry.npmjs.org/@types/sinon/-/sinon-10.0.16.tgz", + "integrity": "sha512-j2Du5SYpXZjJVJtXBokASpPRj+e2z+VUhCPHmM6WMfe3dpHu6iVKJMU6AiBcMp/XTAYnEj6Wc1trJUWwZ0QaAQ==", "dev": true, "dependencies": { "@types/sinonjs__fake-timers": "*" @@ -1100,32 +1186,33 @@ "dev": true }, "node_modules/@typescript-eslint/eslint-plugin": { - "version": "5.59.7", - "resolved": "https://registry.npmjs.org/@typescript-eslint/eslint-plugin/-/eslint-plugin-5.59.7.tgz", - "integrity": "sha512-BL+jYxUFIbuYwy+4fF86k5vdT9lT0CNJ6HtwrIvGh0PhH8s0yy5rjaKH2fDCrz5ITHy07WCzVGNvAmjJh4IJFA==", + "version": "6.4.1", + "resolved": "https://registry.npmjs.org/@typescript-eslint/eslint-plugin/-/eslint-plugin-6.4.1.tgz", + "integrity": "sha512-3F5PtBzUW0dYlq77Lcqo13fv+58KDwUib3BddilE8ajPJT+faGgxmI9Sw+I8ZS22BYwoir9ZhNXcLi+S+I2bkw==", "dev": true, "dependencies": { - "@eslint-community/regexpp": "^4.4.0", - "@typescript-eslint/scope-manager": "5.59.7", - "@typescript-eslint/type-utils": "5.59.7", - "@typescript-eslint/utils": "5.59.7", + "@eslint-community/regexpp": "^4.5.1", + "@typescript-eslint/scope-manager": "6.4.1", + "@typescript-eslint/type-utils": "6.4.1", + "@typescript-eslint/utils": "6.4.1", + "@typescript-eslint/visitor-keys": "6.4.1", "debug": "^4.3.4", - "grapheme-splitter": "^1.0.4", - "ignore": "^5.2.0", - "natural-compare-lite": "^1.4.0", - "semver": "^7.3.7", - "tsutils": "^3.21.0" + "graphemer": "^1.4.0", + "ignore": "^5.2.4", + "natural-compare": "^1.4.0", + "semver": "^7.5.4", + "ts-api-utils": "^1.0.1" }, "engines": { - "node": "^12.22.0 || ^14.17.0 || >=16.0.0" + "node": "^16.0.0 || >=18.0.0" }, "funding": { "type": "opencollective", "url": "https://opencollective.com/typescript-eslint" }, "peerDependencies": { - "@typescript-eslint/parser": "^5.0.0", - "eslint": "^6.0.0 || ^7.0.0 || ^8.0.0" + "@typescript-eslint/parser": "^6.0.0 || ^6.0.0-alpha", + "eslint": "^7.0.0 || ^8.0.0" }, "peerDependenciesMeta": { "typescript": { @@ -1146,9 +1233,9 @@ } }, "node_modules/@typescript-eslint/eslint-plugin/node_modules/semver": { - "version": "7.3.8", - "resolved": "https://registry.npmjs.org/semver/-/semver-7.3.8.tgz", - "integrity": "sha512-NB1ctGL5rlHrPJtFDVIVzTyQylMLu9N9VICA6HSFJo8MCGVTMW6gfpicwKmmK/dAjTOrqu5l63JJOpDSrAis3A==", + "version": "7.5.4", + "resolved": "https://registry.npmjs.org/semver/-/semver-7.5.4.tgz", + "integrity": "sha512-1bCSESV6Pv+i21Hvpxp3Dx+pSD8lIPt8uVjRrxAUt/nbswYc+tK6Y2btiULjd4+fnq15PX+nqQDC7Oft7WkwcA==", "dev": true, "dependencies": { "lru-cache": "^6.0.0" @@ -1160,32 +1247,27 @@ "node": ">=10" } }, - "node_modules/@typescript-eslint/eslint-plugin/node_modules/yallist": { - "version": "4.0.0", - "resolved": "https://registry.npmjs.org/yallist/-/yallist-4.0.0.tgz", - "integrity": "sha512-3wdGidZyq5PB084XLES5TpOSRA3wjXAlIWMhum2kRcv/41Sn2emQ0dycQW4uZXLejwKvg6EsvbdlVL+FYEct7A==", - "dev": true - }, "node_modules/@typescript-eslint/parser": { - "version": "5.59.7", - "resolved": "https://registry.npmjs.org/@typescript-eslint/parser/-/parser-5.59.7.tgz", - "integrity": "sha512-VhpsIEuq/8i5SF+mPg9jSdIwgMBBp0z9XqjiEay+81PYLJuroN+ET1hM5IhkiYMJd9MkTz8iJLt7aaGAgzWUbQ==", + "version": "6.4.1", + "resolved": "https://registry.npmjs.org/@typescript-eslint/parser/-/parser-6.4.1.tgz", + "integrity": "sha512-610G6KHymg9V7EqOaNBMtD1GgpAmGROsmfHJPXNLCU9bfIuLrkdOygltK784F6Crboyd5tBFayPB7Sf0McrQwg==", "dev": true, "dependencies": { - "@typescript-eslint/scope-manager": "5.59.7", - "@typescript-eslint/types": "5.59.7", - "@typescript-eslint/typescript-estree": "5.59.7", + "@typescript-eslint/scope-manager": "6.4.1", + "@typescript-eslint/types": "6.4.1", + "@typescript-eslint/typescript-estree": "6.4.1", + "@typescript-eslint/visitor-keys": "6.4.1", "debug": "^4.3.4" }, "engines": { - "node": "^12.22.0 || ^14.17.0 || >=16.0.0" + "node": "^16.0.0 || >=18.0.0" }, "funding": { "type": "opencollective", "url": "https://opencollective.com/typescript-eslint" }, "peerDependencies": { - "eslint": "^6.0.0 || ^7.0.0 || ^8.0.0" + "eslint": "^7.0.0 || ^8.0.0" }, "peerDependenciesMeta": { "typescript": { @@ -1194,16 +1276,16 @@ } }, "node_modules/@typescript-eslint/scope-manager": { - "version": "5.59.7", - "resolved": "https://registry.npmjs.org/@typescript-eslint/scope-manager/-/scope-manager-5.59.7.tgz", - "integrity": "sha512-FL6hkYWK9zBGdxT2wWEd2W8ocXMu3K94i3gvMrjXpx+koFYdYV7KprKfirpgY34vTGzEPPuKoERpP8kD5h7vZQ==", + "version": "6.4.1", + "resolved": "https://registry.npmjs.org/@typescript-eslint/scope-manager/-/scope-manager-6.4.1.tgz", + "integrity": "sha512-p/OavqOQfm4/Hdrr7kvacOSFjwQ2rrDVJRPxt/o0TOWdFnjJptnjnZ+sYDR7fi4OimvIuKp+2LCkc+rt9fIW+A==", "dev": true, "dependencies": { - "@typescript-eslint/types": "5.59.7", - "@typescript-eslint/visitor-keys": "5.59.7" + "@typescript-eslint/types": "6.4.1", + "@typescript-eslint/visitor-keys": "6.4.1" }, "engines": { - "node": "^12.22.0 || ^14.17.0 || >=16.0.0" + "node": "^16.0.0 || >=18.0.0" }, "funding": { "type": "opencollective", @@ -1211,25 +1293,25 @@ } }, "node_modules/@typescript-eslint/type-utils": { - "version": "5.59.7", - "resolved": "https://registry.npmjs.org/@typescript-eslint/type-utils/-/type-utils-5.59.7.tgz", - "integrity": "sha512-ozuz/GILuYG7osdY5O5yg0QxXUAEoI4Go3Do5xeu+ERH9PorHBPSdvD3Tjp2NN2bNLh1NJQSsQu2TPu/Ly+HaQ==", + "version": "6.4.1", + "resolved": "https://registry.npmjs.org/@typescript-eslint/type-utils/-/type-utils-6.4.1.tgz", + "integrity": "sha512-7ON8M8NXh73SGZ5XvIqWHjgX2f+vvaOarNliGhjrJnv1vdjG0LVIz+ToYfPirOoBi56jxAKLfsLm40+RvxVVXA==", "dev": true, "dependencies": { - "@typescript-eslint/typescript-estree": "5.59.7", - "@typescript-eslint/utils": "5.59.7", + "@typescript-eslint/typescript-estree": "6.4.1", + "@typescript-eslint/utils": "6.4.1", "debug": "^4.3.4", - "tsutils": "^3.21.0" + "ts-api-utils": "^1.0.1" }, "engines": { - "node": "^12.22.0 || ^14.17.0 || >=16.0.0" + "node": "^16.0.0 || >=18.0.0" }, "funding": { "type": "opencollective", "url": "https://opencollective.com/typescript-eslint" }, "peerDependencies": { - "eslint": "*" + "eslint": "^7.0.0 || ^8.0.0" }, "peerDependenciesMeta": { "typescript": { @@ -1238,12 +1320,12 @@ } }, "node_modules/@typescript-eslint/types": { - "version": "5.59.7", - "resolved": "https://registry.npmjs.org/@typescript-eslint/types/-/types-5.59.7.tgz", - "integrity": "sha512-UnVS2MRRg6p7xOSATscWkKjlf/NDKuqo5TdbWck6rIRZbmKpVNTLALzNvcjIfHBE7736kZOFc/4Z3VcZwuOM/A==", + "version": "6.4.1", + "resolved": "https://registry.npmjs.org/@typescript-eslint/types/-/types-6.4.1.tgz", + "integrity": "sha512-zAAopbNuYu++ijY1GV2ylCsQsi3B8QvfPHVqhGdDcbx/NK5lkqMnCGU53amAjccSpk+LfeONxwzUhDzArSfZJg==", "dev": true, "engines": { - "node": "^12.22.0 || ^14.17.0 || >=16.0.0" + "node": "^16.0.0 || >=18.0.0" }, "funding": { "type": "opencollective", @@ -1251,21 +1333,21 @@ } }, "node_modules/@typescript-eslint/typescript-estree": { - "version": "5.59.7", - "resolved": "https://registry.npmjs.org/@typescript-eslint/typescript-estree/-/typescript-estree-5.59.7.tgz", - "integrity": "sha512-4A1NtZ1I3wMN2UGDkU9HMBL+TIQfbrh4uS0WDMMpf3xMRursDbqEf1ahh6vAAe3mObt8k3ZATnezwG4pdtWuUQ==", + "version": "6.4.1", + "resolved": "https://registry.npmjs.org/@typescript-eslint/typescript-estree/-/typescript-estree-6.4.1.tgz", + "integrity": "sha512-xF6Y7SatVE/OyV93h1xGgfOkHr2iXuo8ip0gbfzaKeGGuKiAnzS+HtVhSPx8Www243bwlW8IF7X0/B62SzFftg==", "dev": true, "dependencies": { - "@typescript-eslint/types": "5.59.7", - "@typescript-eslint/visitor-keys": "5.59.7", + "@typescript-eslint/types": "6.4.1", + "@typescript-eslint/visitor-keys": "6.4.1", "debug": "^4.3.4", "globby": "^11.1.0", "is-glob": "^4.0.3", - "semver": "^7.3.7", - "tsutils": "^3.21.0" + "semver": "^7.5.4", + "ts-api-utils": "^1.0.1" }, "engines": { - "node": "^12.22.0 || ^14.17.0 || >=16.0.0" + "node": "^16.0.0 || >=18.0.0" }, "funding": { "type": "opencollective", @@ -1319,9 +1401,9 @@ } }, "node_modules/@typescript-eslint/typescript-estree/node_modules/semver": { - "version": "7.5.1", - "resolved": "https://registry.npmjs.org/semver/-/semver-7.5.1.tgz", - "integrity": "sha512-Wvss5ivl8TMRZXXESstBA4uR5iXgEN/VC5/sOcuXdVLzcdkz4HWetIoRfG5gb5X+ij/G9rw9YoGn3QoQ8OCSpw==", + "version": "7.5.4", + "resolved": "https://registry.npmjs.org/semver/-/semver-7.5.4.tgz", + "integrity": "sha512-1bCSESV6Pv+i21Hvpxp3Dx+pSD8lIPt8uVjRrxAUt/nbswYc+tK6Y2btiULjd4+fnq15PX+nqQDC7Oft7WkwcA==", "dev": true, "dependencies": { "lru-cache": "^6.0.0" @@ -1333,36 +1415,38 @@ "node": ">=10" } }, - "node_modules/@typescript-eslint/typescript-estree/node_modules/yallist": { - "version": "4.0.0", - "resolved": "https://registry.npmjs.org/yallist/-/yallist-4.0.0.tgz", - "integrity": "sha512-3wdGidZyq5PB084XLES5TpOSRA3wjXAlIWMhum2kRcv/41Sn2emQ0dycQW4uZXLejwKvg6EsvbdlVL+FYEct7A==", - "dev": true + "node_modules/@typescript-eslint/typescript-estree/node_modules/slash": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/slash/-/slash-3.0.0.tgz", + "integrity": "sha512-g9Q1haeby36OSStwb4ntCGGGaKsaVSjQ68fBxoQcutl5fS1vuY18H3wSt3jFyFtrkx+Kz0V1G85A4MyAdDMi2Q==", + "dev": true, + "engines": { + "node": ">=8" + } }, "node_modules/@typescript-eslint/utils": { - "version": "5.59.7", - "resolved": "https://registry.npmjs.org/@typescript-eslint/utils/-/utils-5.59.7.tgz", - "integrity": "sha512-yCX9WpdQKaLufz5luG4aJbOpdXf/fjwGMcLFXZVPUz3QqLirG5QcwwnIHNf8cjLjxK4qtzTO8udUtMQSAToQnQ==", + "version": "6.4.1", + "resolved": "https://registry.npmjs.org/@typescript-eslint/utils/-/utils-6.4.1.tgz", + "integrity": "sha512-F/6r2RieNeorU0zhqZNv89s9bDZSovv3bZQpUNOmmQK1L80/cV4KEu95YUJWi75u5PhboFoKUJBnZ4FQcoqhDw==", "dev": true, "dependencies": { - "@eslint-community/eslint-utils": "^4.2.0", - "@types/json-schema": "^7.0.9", - "@types/semver": "^7.3.12", - "@typescript-eslint/scope-manager": "5.59.7", - "@typescript-eslint/types": "5.59.7", - "@typescript-eslint/typescript-estree": "5.59.7", - "eslint-scope": "^5.1.1", - "semver": "^7.3.7" + "@eslint-community/eslint-utils": "^4.4.0", + "@types/json-schema": "^7.0.12", + "@types/semver": "^7.5.0", + "@typescript-eslint/scope-manager": "6.4.1", + "@typescript-eslint/types": "6.4.1", + "@typescript-eslint/typescript-estree": "6.4.1", + "semver": "^7.5.4" }, "engines": { - "node": "^12.22.0 || ^14.17.0 || >=16.0.0" + "node": "^16.0.0 || >=18.0.0" }, "funding": { "type": "opencollective", "url": "https://opencollective.com/typescript-eslint" }, "peerDependencies": { - "eslint": "^6.0.0 || ^7.0.0 || ^8.0.0" + "eslint": "^7.0.0 || ^8.0.0" } }, "node_modules/@typescript-eslint/utils/node_modules/lru-cache": { @@ -1378,9 +1462,9 @@ } }, "node_modules/@typescript-eslint/utils/node_modules/semver": { - "version": "7.5.1", - "resolved": "https://registry.npmjs.org/semver/-/semver-7.5.1.tgz", - "integrity": "sha512-Wvss5ivl8TMRZXXESstBA4uR5iXgEN/VC5/sOcuXdVLzcdkz4HWetIoRfG5gb5X+ij/G9rw9YoGn3QoQ8OCSpw==", + "version": "7.5.4", + "resolved": "https://registry.npmjs.org/semver/-/semver-7.5.4.tgz", + "integrity": "sha512-1bCSESV6Pv+i21Hvpxp3Dx+pSD8lIPt8uVjRrxAUt/nbswYc+tK6Y2btiULjd4+fnq15PX+nqQDC7Oft7WkwcA==", "dev": true, "dependencies": { "lru-cache": "^6.0.0" @@ -1392,23 +1476,17 @@ "node": ">=10" } }, - "node_modules/@typescript-eslint/utils/node_modules/yallist": { - "version": "4.0.0", - "resolved": "https://registry.npmjs.org/yallist/-/yallist-4.0.0.tgz", - "integrity": "sha512-3wdGidZyq5PB084XLES5TpOSRA3wjXAlIWMhum2kRcv/41Sn2emQ0dycQW4uZXLejwKvg6EsvbdlVL+FYEct7A==", - "dev": true - }, "node_modules/@typescript-eslint/visitor-keys": { - "version": "5.59.7", - "resolved": "https://registry.npmjs.org/@typescript-eslint/visitor-keys/-/visitor-keys-5.59.7.tgz", - "integrity": "sha512-tyN+X2jvMslUszIiYbF0ZleP+RqQsFVpGrKI6e0Eet1w8WmhsAtmzaqm8oM8WJQ1ysLwhnsK/4hYHJjOgJVfQQ==", + "version": "6.4.1", + "resolved": "https://registry.npmjs.org/@typescript-eslint/visitor-keys/-/visitor-keys-6.4.1.tgz", + "integrity": "sha512-y/TyRJsbZPkJIZQXrHfdnxVnxyKegnpEvnRGNam7s3TRR2ykGefEWOhaef00/UUN3IZxizS7BTO3svd3lCOJRQ==", "dev": true, "dependencies": { - "@typescript-eslint/types": "5.59.7", - "eslint-visitor-keys": "^3.3.0" + "@typescript-eslint/types": "6.4.1", + "eslint-visitor-keys": "^3.4.1" }, "engines": { - "node": "^12.22.0 || ^14.17.0 || >=16.0.0" + "node": "^16.0.0 || >=18.0.0" }, "funding": { "type": "opencollective", @@ -1416,9 +1494,9 @@ } }, "node_modules/acorn": { - "version": "8.8.2", - "resolved": "https://registry.npmjs.org/acorn/-/acorn-8.8.2.tgz", - "integrity": "sha512-xjIYgE8HBrkpd/sJqOGNspf8uHG+NOHGOw6a/Urj8taM2EXfdNAH2oFcPeIFfsv3+kz/mJrS5VuMqbNLjCa2vw==", + "version": "8.10.0", + "resolved": "https://registry.npmjs.org/acorn/-/acorn-8.10.0.tgz", + "integrity": "sha512-F0SAmZ8iUtS//m8DmCTA0jlh6TDKkHQyK6xc6V4KDTyZKA9dnvX9/3sRTVQrWm79glUAZbnmmNcdYwUIHWVybw==", "dev": true, "bin": { "acorn": "bin/acorn" @@ -1446,15 +1524,15 @@ } }, "node_modules/agent-base": { - "version": "6.0.2", - "resolved": "https://registry.npmjs.org/agent-base/-/agent-base-6.0.2.tgz", - "integrity": "sha512-RZNwNclF7+MS/8bDg70amg32dyeZGZxiDuQmZxKLAlQjr3jGyLx+4Kkk58UO7D2QdgFIQCovuSuZESne6RG6XQ==", + "version": "7.1.0", + "resolved": "https://registry.npmjs.org/agent-base/-/agent-base-7.1.0.tgz", + "integrity": "sha512-o/zjMZRhJxny7OyEF+Op8X+efiELC7k7yOjMzgfzVqOzXqkBkWI79YoTdOtsuWd5BWhAGAuOY/Xa6xpiaWXiNg==", "dev": true, "dependencies": { - "debug": "4" + "debug": "^4.3.4" }, "engines": { - "node": ">= 6.0.0" + "node": ">= 14" } }, "node_modules/aggregate-error": { @@ -1495,108 +1573,55 @@ "string-width": "^4.1.0" } }, - "node_modules/ansi-align/node_modules/ansi-regex": { - "version": "5.0.1", - "resolved": "https://registry.npmjs.org/ansi-regex/-/ansi-regex-5.0.1.tgz", - "integrity": "sha512-quJQXlTSUGL2LH9SUXo8VwsY4soanhgo6LNSm84E1LBcE8s3O0wpdiRzyR9z/ZZJMlMWv37qOOb9pdJlMUEKFQ==", + "node_modules/ansi-colors": { + "version": "4.1.1", + "resolved": "https://registry.npmjs.org/ansi-colors/-/ansi-colors-4.1.1.tgz", + "integrity": "sha512-JoX0apGbHaUJBNl6yF+p6JAFYZ666/hhCGKN5t9QFjbJQKUU/g8MNbFDbvfrgKXvI1QpZplPOnwIo99lX/AAmA==", "dev": true, "engines": { - "node": ">=8" + "node": ">=6" } }, - "node_modules/ansi-align/node_modules/emoji-regex": { - "version": "8.0.0", - "resolved": "https://registry.npmjs.org/emoji-regex/-/emoji-regex-8.0.0.tgz", - "integrity": "sha512-MSjYzcWNOA0ewAHpz0MxpYFvwg6yjy1NG3xteoqz644VCo/RPgnr1/GGt+ic3iJTzQ8Eu3TdM14SawnVUmGE6A==", - "dev": true - }, - "node_modules/ansi-align/node_modules/string-width": { - "version": "4.2.3", - "resolved": "https://registry.npmjs.org/string-width/-/string-width-4.2.3.tgz", - "integrity": "sha512-wKyQRQpjJ0sIp62ErSZdGsjMJWsap5oRNihHhu6G7JVO/9jIB6UyevL+tXuOqrng8j/cxKTWyWUwvSTriiZz/g==", + "node_modules/ansi-escapes": { + "version": "4.3.2", + "resolved": "https://registry.npmjs.org/ansi-escapes/-/ansi-escapes-4.3.2.tgz", + "integrity": "sha512-gKXj5ALrKWQLsYG9jlTRmR/xKluxHV+Z9QEwNIgCfM1/uwPMCuzVVnh5mwTd+OuBZcwSIMbqssNWRm1lE51QaQ==", "dev": true, "dependencies": { - "emoji-regex": "^8.0.0", - "is-fullwidth-code-point": "^3.0.0", - "strip-ansi": "^6.0.1" + "type-fest": "^0.21.3" }, "engines": { "node": ">=8" - } - }, - "node_modules/ansi-align/node_modules/strip-ansi": { - "version": "6.0.1", - "resolved": "https://registry.npmjs.org/strip-ansi/-/strip-ansi-6.0.1.tgz", - "integrity": "sha512-Y38VPSHcqkFrCpFnQ9vuSXmquuv5oXOKpGeT6aGrr3o3Gc9AlVa6JBfUSOCnbxGGZF+/0ooI7KrPuUSztUdU5A==", - "dev": true, - "dependencies": { - "ansi-regex": "^5.0.1" - }, - "engines": { - "node": ">=8" - } - }, - "node_modules/ansi-colors": { - "version": "4.1.1", - "resolved": "https://registry.npmjs.org/ansi-colors/-/ansi-colors-4.1.1.tgz", - "integrity": "sha512-JoX0apGbHaUJBNl6yF+p6JAFYZ666/hhCGKN5t9QFjbJQKUU/g8MNbFDbvfrgKXvI1QpZplPOnwIo99lX/AAmA==", - "dev": true, - "engines": { - "node": ">=6" - } - }, - "node_modules/ansi-escapes": { - "version": "6.2.0", - "resolved": "https://registry.npmjs.org/ansi-escapes/-/ansi-escapes-6.2.0.tgz", - "integrity": "sha512-kzRaCqXnpzWs+3z5ABPQiVke+iq0KXkHo8xiWV4RPTi5Yli0l97BEQuhXV1s7+aSU/fu1kUuxgS4MsQ0fRuygw==", - "dev": true, - "dependencies": { - "type-fest": "^3.0.0" - }, - "engines": { - "node": ">=14.16" - }, - "funding": { - "url": "https://github.com/sponsors/sindresorhus" - } - }, - "node_modules/ansi-escapes/node_modules/type-fest": { - "version": "3.11.0", - "resolved": "https://registry.npmjs.org/type-fest/-/type-fest-3.11.0.tgz", - "integrity": "sha512-JaPw5U9ixP0XcpUbQoVSbxSDcK/K4nww20C3kjm9yE6cDRRhptU28AH60VWf9ltXmCrIfIbtt9J+2OUk2Uqiaw==", - "dev": true, - "engines": { - "node": ">=14.16" - }, - "funding": { - "url": "https://github.com/sponsors/sindresorhus" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" } }, "node_modules/ansi-regex": { - "version": "6.0.1", - "resolved": "https://registry.npmjs.org/ansi-regex/-/ansi-regex-6.0.1.tgz", - "integrity": "sha512-n5M855fKb2SsfMIiFFoVrABHJC8QtHwVx+mHWP3QcEqBHYienj5dHSgjbxtC0WEZXYt4wcD6zrQElDPhFuZgfA==", + "version": "5.0.1", + "resolved": "https://registry.npmjs.org/ansi-regex/-/ansi-regex-5.0.1.tgz", + "integrity": "sha512-quJQXlTSUGL2LH9SUXo8VwsY4soanhgo6LNSm84E1LBcE8s3O0wpdiRzyR9z/ZZJMlMWv37qOOb9pdJlMUEKFQ==", "dev": true, "engines": { - "node": ">=12" - }, - "funding": { - "url": "https://github.com/chalk/ansi-regex?sponsor=1" + "node": ">=8" } }, "node_modules/ansi-sequence-parser": { - "version": "1.1.0", - "resolved": "https://registry.npmjs.org/ansi-sequence-parser/-/ansi-sequence-parser-1.1.0.tgz", - "integrity": "sha512-lEm8mt52to2fT8GhciPCGeCXACSz2UwIN4X2e2LJSnZ5uAbn2/dsYdOmUXq0AtWS5cpAupysIneExOgH0Vd2TQ==", + "version": "1.1.1", + "resolved": "https://registry.npmjs.org/ansi-sequence-parser/-/ansi-sequence-parser-1.1.1.tgz", + "integrity": "sha512-vJXt3yiaUL4UU546s3rPXlsry/RnM730G1+HkpKE012AN0sx1eOrxSu95oKDIonskeLTijMgqWZ3uDEe3NFvyg==", "dev": true }, "node_modules/ansi-styles": { - "version": "6.2.1", - "resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-6.2.1.tgz", - "integrity": "sha512-bN798gFfQX+viw3R7yrGWRqnrN2oRkEkUjjl4JNn4E8GxxbjtG3FbrEIIY3l8/hrwUwIeCZvi4QuOTP4MErVug==", + "version": "4.3.0", + "resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-4.3.0.tgz", + "integrity": "sha512-zbB9rCJAT1rbjiVDb2hqKFHNYLxgtk8NURxZ3IZwD3F6NtxbXZQCnnSi1Lkx+IDohdPlFp222wVALIheZJQSEg==", "dev": true, + "dependencies": { + "color-convert": "^2.0.1" + }, "engines": { - "node": ">=12" + "node": ">=8" }, "funding": { "url": "https://github.com/chalk/ansi-styles?sponsor=1" @@ -1645,6 +1670,19 @@ "integrity": "sha512-8+9WqebbFzpX9OR+Wa6O29asIogeRMzcGtAINdpMHHyAg10f05aSFVBbcEqGf/PXw1EjAZ+q2/bEBg3DvurK3Q==", "dev": true }, + "node_modules/array-buffer-byte-length": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/array-buffer-byte-length/-/array-buffer-byte-length-1.0.0.tgz", + "integrity": "sha512-LPuwb2P+NrQw3XhxGc36+XSvuBPopovXYTR9Ew++Du9Yb/bx5AzBfrIsBoj0EZUifjQU+sHL21sseZ3jerWO/A==", + "dev": true, + "dependencies": { + "call-bind": "^1.0.2", + "is-array-buffer": "^3.0.1" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, "node_modules/array-union": { "version": "1.0.2", "resolved": "https://registry.npmjs.org/array-union/-/array-union-1.0.2.tgz", @@ -1685,6 +1723,26 @@ "url": "https://github.com/sponsors/ljharb" } }, + "node_modules/arraybuffer.prototype.slice": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/arraybuffer.prototype.slice/-/arraybuffer.prototype.slice-1.0.1.tgz", + "integrity": "sha512-09x0ZWFEjj4WD8PDbykUwo3t9arLn8NIzmmYEJFpYekOAQjpkGSyrQhNoRTcwwcFRu+ycWF78QZ63oWTqSjBcw==", + "dev": true, + "dependencies": { + "array-buffer-byte-length": "^1.0.0", + "call-bind": "^1.0.2", + "define-properties": "^1.2.0", + "get-intrinsic": "^1.2.1", + "is-array-buffer": "^3.0.2", + "is-shared-array-buffer": "^1.0.2" + }, + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, "node_modules/ast-types": { "version": "0.13.4", "resolved": "https://registry.npmjs.org/ast-types/-/ast-types-0.13.4.tgz", @@ -1750,6 +1808,15 @@ } ] }, + "node_modules/basic-ftp": { + "version": "5.0.3", + "resolved": "https://registry.npmjs.org/basic-ftp/-/basic-ftp-5.0.3.tgz", + "integrity": "sha512-QHX8HLlncOLpy54mh+k/sWIFd0ThmRqwe9ZjELybGZK+tZ8rUb9VO0saKJUROTbE+KhzDUT7xziGpGrW8Kmd+g==", + "dev": true, + "engines": { + "node": ">=10.0.0" + } + }, "node_modules/before-after-hook": { "version": "2.2.3", "resolved": "https://registry.npmjs.org/before-after-hook/-/before-after-hook-2.2.3.tgz", @@ -1786,19 +1853,19 @@ } }, "node_modules/boxen": { - "version": "7.0.1", - "resolved": "https://registry.npmjs.org/boxen/-/boxen-7.0.1.tgz", - "integrity": "sha512-8k2eH6SRAK00NDl1iX5q17RJ8rfl53TajdYxE3ssMLehbg487dEVgsad4pIsZb/QqBgYWIl6JOauMTLGX2Kpkw==", + "version": "7.1.1", + "resolved": "https://registry.npmjs.org/boxen/-/boxen-7.1.1.tgz", + "integrity": "sha512-2hCgjEmP8YLWQ130n2FerGv7rYpfBmnmp9Uy2Le1vge6X3gZIfSmEzP5QTDElFxcvVcXlEn8Aq6MU/PZygIOog==", "dev": true, "dependencies": { "ansi-align": "^3.0.1", - "camelcase": "^7.0.0", - "chalk": "^5.0.1", + "camelcase": "^7.0.1", + "chalk": "^5.2.0", "cli-boxes": "^3.0.0", "string-width": "^5.1.2", "type-fest": "^2.13.0", "widest-line": "^4.0.1", - "wrap-ansi": "^8.0.1" + "wrap-ansi": "^8.1.0" }, "engines": { "node": ">=14.16" @@ -1807,18 +1874,68 @@ "url": "https://github.com/sponsors/sindresorhus" } }, - "node_modules/boxen/node_modules/camelcase": { - "version": "7.0.1", - "resolved": "https://registry.npmjs.org/camelcase/-/camelcase-7.0.1.tgz", - "integrity": "sha512-xlx1yCK2Oc1APsPXDL2LdlNP6+uu8OCDdhOBSVT279M/S+y75O30C2VuD8T2ogdePBBl7PfPF4504tnLgX3zfw==", + "node_modules/boxen/node_modules/ansi-regex": { + "version": "6.0.1", + "resolved": "https://registry.npmjs.org/ansi-regex/-/ansi-regex-6.0.1.tgz", + "integrity": "sha512-n5M855fKb2SsfMIiFFoVrABHJC8QtHwVx+mHWP3QcEqBHYienj5dHSgjbxtC0WEZXYt4wcD6zrQElDPhFuZgfA==", "dev": true, "engines": { - "node": ">=14.16" + "node": ">=12" + }, + "funding": { + "url": "https://github.com/chalk/ansi-regex?sponsor=1" + } + }, + "node_modules/boxen/node_modules/ansi-styles": { + "version": "6.2.1", + "resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-6.2.1.tgz", + "integrity": "sha512-bN798gFfQX+viw3R7yrGWRqnrN2oRkEkUjjl4JNn4E8GxxbjtG3FbrEIIY3l8/hrwUwIeCZvi4QuOTP4MErVug==", + "dev": true, + "engines": { + "node": ">=12" + }, + "funding": { + "url": "https://github.com/chalk/ansi-styles?sponsor=1" + } + }, + "node_modules/boxen/node_modules/emoji-regex": { + "version": "9.2.2", + "resolved": "https://registry.npmjs.org/emoji-regex/-/emoji-regex-9.2.2.tgz", + "integrity": "sha512-L18DaJsXSUk2+42pv8mLs5jJT2hqFkFE4j21wOmgbUqsZ2hL72NsUU785g9RXgo3s0ZNgVl42TiHp3ZtOv/Vyg==", + "dev": true + }, + "node_modules/boxen/node_modules/string-width": { + "version": "5.1.2", + "resolved": "https://registry.npmjs.org/string-width/-/string-width-5.1.2.tgz", + "integrity": "sha512-HnLOCR3vjcY8beoNLtcjZ5/nxn2afmME6lhrDrebokqMap+XbeW8n9TXpPDOqdGK5qcI3oT0GKTW6wC7EMiVqA==", + "dev": true, + "dependencies": { + "eastasianwidth": "^0.2.0", + "emoji-regex": "^9.2.2", + "strip-ansi": "^7.0.1" + }, + "engines": { + "node": ">=12" }, "funding": { "url": "https://github.com/sponsors/sindresorhus" } }, + "node_modules/boxen/node_modules/strip-ansi": { + "version": "7.1.0", + "resolved": "https://registry.npmjs.org/strip-ansi/-/strip-ansi-7.1.0.tgz", + "integrity": "sha512-iq6eVVI64nQQTRYq2KtEg2d2uU7LElhTJwsH4YzIHZshxlgZms/wIc4VoDQTlG/IvVIrBKG06CrZnp0qv7hkcQ==", + "dev": true, + "dependencies": { + "ansi-regex": "^6.0.1" + }, + "engines": { + "node": ">=12" + }, + "funding": { + "url": "https://github.com/chalk/strip-ansi?sponsor=1" + } + }, "node_modules/boxen/node_modules/type-fest": { "version": "2.19.0", "resolved": "https://registry.npmjs.org/type-fest/-/type-fest-2.19.0.tgz", @@ -1831,6 +1948,23 @@ "url": "https://github.com/sponsors/sindresorhus" } }, + "node_modules/boxen/node_modules/wrap-ansi": { + "version": "8.1.0", + "resolved": "https://registry.npmjs.org/wrap-ansi/-/wrap-ansi-8.1.0.tgz", + "integrity": "sha512-si7QWI6zUMq56bESFvagtmzMdGOtoxfR+Sez11Mobfc7tm+VkUckk9bW2UeffTGVUbOksxmSw0AA2gs8g71NCQ==", + "dev": true, + "dependencies": { + "ansi-styles": "^6.1.0", + "string-width": "^5.0.1", + "strip-ansi": "^7.0.1" + }, + "engines": { + "node": ">=12" + }, + "funding": { + "url": "https://github.com/chalk/wrap-ansi?sponsor=1" + } + }, "node_modules/bplist-parser": { "version": "0.2.0", "resolved": "https://registry.npmjs.org/bplist-parser/-/bplist-parser-0.2.0.tgz", @@ -1872,9 +2006,9 @@ "dev": true }, "node_modules/browserslist": { - "version": "4.21.5", - "resolved": "https://registry.npmjs.org/browserslist/-/browserslist-4.21.5.tgz", - "integrity": "sha512-tUkiguQGW7S3IhB7N+c2MV/HZPSCPAAiYBZXLsBhFB/PCy6ZKKsZrmBayHV9fdGV/ARIfJ14NkxKzRDjvp7L6w==", + "version": "4.21.10", + "resolved": "https://registry.npmjs.org/browserslist/-/browserslist-4.21.10.tgz", + "integrity": "sha512-bipEBdZfVH5/pwrvqc+Ub0kUPVfGUhlKxbvfD+z1BDnPEO/X98ruXGA1WP5ASpAFKan7Qr6j736IacbZQuAlKQ==", "dev": true, "funding": [ { @@ -1884,13 +2018,17 @@ { "type": "tidelift", "url": "https://tidelift.com/funding/github/npm/browserslist" + }, + { + "type": "github", + "url": "https://github.com/sponsors/ai" } ], "dependencies": { - "caniuse-lite": "^1.0.30001449", - "electron-to-chromium": "^1.4.284", - "node-releases": "^2.0.8", - "update-browserslist-db": "^1.0.10" + "caniuse-lite": "^1.0.30001517", + "electron-to-chromium": "^1.4.477", + "node-releases": "^2.0.13", + "update-browserslist-db": "^1.0.11" }, "bin": { "browserslist": "cli.js" @@ -1944,15 +2082,6 @@ "url": "https://github.com/sponsors/sindresorhus" } }, - "node_modules/bytes": { - "version": "3.1.2", - "resolved": "https://registry.npmjs.org/bytes/-/bytes-3.1.2.tgz", - "integrity": "sha512-/Nf7TyzTx6S3yRJObOAV7956r8cr2+Oj8AC5dt8wSP3BQAoeX58NoHyCU8P8zGkNXStjTSi6fzO6F0pBdcYbEg==", - "dev": true, - "engines": { - "node": ">= 0.8" - } - }, "node_modules/cacheable-lookup": { "version": "7.0.0", "resolved": "https://registry.npmjs.org/cacheable-lookup/-/cacheable-lookup-7.0.0.tgz", @@ -1963,15 +2092,15 @@ } }, "node_modules/cacheable-request": { - "version": "10.2.8", - "resolved": "https://registry.npmjs.org/cacheable-request/-/cacheable-request-10.2.8.tgz", - "integrity": "sha512-IDVO5MJ4LItE6HKFQTqT2ocAQsisOoCTUDu1ddCmnhyiwFQjXNPp4081Xj23N4tO+AFEFNzGuNEf/c8Gwwt15A==", + "version": "10.2.13", + "resolved": "https://registry.npmjs.org/cacheable-request/-/cacheable-request-10.2.13.tgz", + "integrity": "sha512-3SD4rrMu1msNGEtNSt8Od6enwdo//U9s4ykmXfA2TD58kcLkCobtCDiby7kNyj7a/Q7lz/mAesAFI54rTdnvBA==", "dev": true, "dependencies": { "@types/http-cache-semantics": "^4.0.1", "get-stream": "^6.0.1", "http-cache-semantics": "^4.1.1", - "keyv": "^4.5.2", + "keyv": "^4.5.3", "mimic-response": "^4.0.0", "normalize-url": "^8.0.0", "responselike": "^3.0.0" @@ -2018,18 +2147,21 @@ } }, "node_modules/camelcase": { - "version": "5.3.1", - "resolved": "https://registry.npmjs.org/camelcase/-/camelcase-5.3.1.tgz", - "integrity": "sha512-L28STB170nwWS63UjtlEOE3dldQApaJXZkOI1uMFfzf3rRuPegHaHesyee+YxQ+W6SvRDQV6UrdOdRiR153wJg==", + "version": "7.0.1", + "resolved": "https://registry.npmjs.org/camelcase/-/camelcase-7.0.1.tgz", + "integrity": "sha512-xlx1yCK2Oc1APsPXDL2LdlNP6+uu8OCDdhOBSVT279M/S+y75O30C2VuD8T2ogdePBBl7PfPF4504tnLgX3zfw==", "dev": true, "engines": { - "node": ">=6" + "node": ">=14.16" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" } }, "node_modules/caniuse-lite": { - "version": "1.0.30001457", - "resolved": "https://registry.npmjs.org/caniuse-lite/-/caniuse-lite-1.0.30001457.tgz", - "integrity": "sha512-SDIV6bgE1aVbK6XyxdURbUE89zY7+k1BBBaOwYwkNCglXlel/E7mELiHC64HQ+W0xSKlqWhV9Wh7iHxUjMs4fA==", + "version": "1.0.30001522", + "resolved": "https://registry.npmjs.org/caniuse-lite/-/caniuse-lite-1.0.30001522.tgz", + "integrity": "sha512-TKiyTVZxJGhsTszLuzb+6vUZSjVOAhClszBr2Ta2k9IwtNBT/4dzmL6aywt0HCgEZlmwJzXJd8yNiob6HgwTRg==", "dev": true, "funding": [ { @@ -2039,13 +2171,17 @@ { "type": "tidelift", "url": "https://tidelift.com/funding/github/npm/caniuse-lite" + }, + { + "type": "github", + "url": "https://github.com/sponsors/ai" } ] }, "node_modules/chalk": { - "version": "5.2.0", - "resolved": "https://registry.npmjs.org/chalk/-/chalk-5.2.0.tgz", - "integrity": "sha512-ree3Gqw/nazQAPuJJEy+avdl7QfZMcUvmHIKgEZkGL+xOBzRvup5Hxo6LHuMceSxOabuJLJm5Yp/92R9eMmMvA==", + "version": "5.3.0", + "resolved": "https://registry.npmjs.org/chalk/-/chalk-5.3.0.tgz", + "integrity": "sha512-dLitG79d+GV1Nb/VYcCDFivJeK1hiukt9QjRNVOsUtTy1rR1YJsmpGGTZ3qJos+uw7WmWF4wUwBd9jxjocFC2w==", "dev": true, "engines": { "node": "^12.17.0 || ^14.13 || >=16.0.0" @@ -2087,18 +2223,6 @@ "fsevents": "~2.3.2" } }, - "node_modules/chokidar/node_modules/glob-parent": { - "version": "5.1.2", - "resolved": "https://registry.npmjs.org/glob-parent/-/glob-parent-5.1.2.tgz", - "integrity": "sha512-AOIgSQCepiJYwP3ARnGx+5VnTu2HBYdzbGP45eLw1vr3zB3vZLeyed1sC9hnbcOc9/SrMyM5RPQrkGz4aS9Zow==", - "dev": true, - "dependencies": { - "is-glob": "^4.0.1" - }, - "engines": { - "node": ">= 6" - } - }, "node_modules/ci-info": { "version": "3.8.0", "resolved": "https://registry.npmjs.org/ci-info/-/ci-info-3.8.0.tgz", @@ -2136,18 +2260,15 @@ } }, "node_modules/cli-cursor": { - "version": "4.0.0", - "resolved": "https://registry.npmjs.org/cli-cursor/-/cli-cursor-4.0.0.tgz", - "integrity": "sha512-VGtlMu3x/4DOtIUwEkRezxUZ2lBacNJCHash0N0WeZDBS+7Ux1dm3XWAgWYxLJFMMdOeXMHXorshEFhbMSGelg==", + "version": "3.1.0", + "resolved": "https://registry.npmjs.org/cli-cursor/-/cli-cursor-3.1.0.tgz", + "integrity": "sha512-I/zHAwsKf9FqGoXM4WWRACob9+SNukZTd94DWF57E4toouRulbCxcUh6RKUEOQlYTHJnzkPMySvPNaaSLNfLZw==", "dev": true, "dependencies": { - "restore-cursor": "^4.0.0" + "restore-cursor": "^3.1.0" }, "engines": { - "node": "^12.20.0 || ^14.13.1 || >=16.0.0" - }, - "funding": { - "url": "https://github.com/sponsors/sindresorhus" + "node": ">=8" } }, "node_modules/cli-spinners": { @@ -2163,99 +2284,49 @@ } }, "node_modules/cli-width": { - "version": "4.0.0", - "resolved": "https://registry.npmjs.org/cli-width/-/cli-width-4.0.0.tgz", - "integrity": "sha512-ZksGS2xpa/bYkNzN3BAw1wEjsLV/ZKOf/CCrJ/QOBsxx6fOARIkwTutxp1XIOIohi6HKmOFjMoK/XaqDVUpEEw==", + "version": "4.1.0", + "resolved": "https://registry.npmjs.org/cli-width/-/cli-width-4.1.0.tgz", + "integrity": "sha512-ouuZd4/dm2Sw5Gmqy6bGyNNNe1qt9RpmxveLSO7KcgsTnU7RXfsw+/bukWGo1abgBiMAic068rclZsO4IWmmxQ==", "dev": true, "engines": { "node": ">= 12" } }, "node_modules/cliui": { - "version": "6.0.0", - "resolved": "https://registry.npmjs.org/cliui/-/cliui-6.0.0.tgz", - "integrity": "sha512-t6wbgtoCXvAzst7QgXxJYqPt0usEfbgQdftEPbLL/cvv6HPE5VgvqCuAIDR0NgU52ds6rFwqrgakNLrHEjCbrQ==", + "version": "8.0.1", + "resolved": "https://registry.npmjs.org/cliui/-/cliui-8.0.1.tgz", + "integrity": "sha512-BSeNnyus75C4//NQ9gQt1/csTXyo/8Sb+afLAkzAptFuMsod9HFokGNudZpi/oQV73hnVK+sR+5PVRMd+Dr7YQ==", "dev": true, "dependencies": { "string-width": "^4.2.0", - "strip-ansi": "^6.0.0", - "wrap-ansi": "^6.2.0" - } - }, - "node_modules/cliui/node_modules/ansi-regex": { - "version": "5.0.1", - "resolved": "https://registry.npmjs.org/ansi-regex/-/ansi-regex-5.0.1.tgz", - "integrity": "sha512-quJQXlTSUGL2LH9SUXo8VwsY4soanhgo6LNSm84E1LBcE8s3O0wpdiRzyR9z/ZZJMlMWv37qOOb9pdJlMUEKFQ==", - "dev": true, + "strip-ansi": "^6.0.1", + "wrap-ansi": "^7.0.0" + }, "engines": { - "node": ">=8" + "node": ">=12" } }, - "node_modules/cliui/node_modules/ansi-styles": { - "version": "4.3.0", - "resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-4.3.0.tgz", - "integrity": "sha512-zbB9rCJAT1rbjiVDb2hqKFHNYLxgtk8NURxZ3IZwD3F6NtxbXZQCnnSi1Lkx+IDohdPlFp222wVALIheZJQSEg==", + "node_modules/cliui/node_modules/wrap-ansi": { + "version": "7.0.0", + "resolved": "https://registry.npmjs.org/wrap-ansi/-/wrap-ansi-7.0.0.tgz", + "integrity": "sha512-YVGIj2kamLSTxw6NsZjoBxfSwsn0ycdesmc4p+Q21c5zPuZ1pl+NfxVdxPtdHvmNVOQ6XSYG4AUtyt/Fi7D16Q==", "dev": true, "dependencies": { - "color-convert": "^2.0.1" + "ansi-styles": "^4.0.0", + "string-width": "^4.1.0", + "strip-ansi": "^6.0.0" }, "engines": { - "node": ">=8" + "node": ">=10" }, "funding": { - "url": "https://github.com/chalk/ansi-styles?sponsor=1" + "url": "https://github.com/chalk/wrap-ansi?sponsor=1" } }, - "node_modules/cliui/node_modules/emoji-regex": { - "version": "8.0.0", - "resolved": "https://registry.npmjs.org/emoji-regex/-/emoji-regex-8.0.0.tgz", - "integrity": "sha512-MSjYzcWNOA0ewAHpz0MxpYFvwg6yjy1NG3xteoqz644VCo/RPgnr1/GGt+ic3iJTzQ8Eu3TdM14SawnVUmGE6A==", - "dev": true - }, - "node_modules/cliui/node_modules/string-width": { - "version": "4.2.3", - "resolved": "https://registry.npmjs.org/string-width/-/string-width-4.2.3.tgz", - "integrity": "sha512-wKyQRQpjJ0sIp62ErSZdGsjMJWsap5oRNihHhu6G7JVO/9jIB6UyevL+tXuOqrng8j/cxKTWyWUwvSTriiZz/g==", - "dev": true, - "dependencies": { - "emoji-regex": "^8.0.0", - "is-fullwidth-code-point": "^3.0.0", - "strip-ansi": "^6.0.1" - }, - "engines": { - "node": ">=8" - } - }, - "node_modules/cliui/node_modules/strip-ansi": { - "version": "6.0.1", - "resolved": "https://registry.npmjs.org/strip-ansi/-/strip-ansi-6.0.1.tgz", - "integrity": "sha512-Y38VPSHcqkFrCpFnQ9vuSXmquuv5oXOKpGeT6aGrr3o3Gc9AlVa6JBfUSOCnbxGGZF+/0ooI7KrPuUSztUdU5A==", - "dev": true, - "dependencies": { - "ansi-regex": "^5.0.1" - }, - "engines": { - "node": ">=8" - } - }, - "node_modules/cliui/node_modules/wrap-ansi": { - "version": "6.2.0", - "resolved": "https://registry.npmjs.org/wrap-ansi/-/wrap-ansi-6.2.0.tgz", - "integrity": "sha512-r6lPcBGxZXlIcymEu7InxDMhdW0KDxpLgoFLcguasxCaJ/SOIZwINatK9KY/tf+ZrlywOKU0UDj3ATXUBfxJXA==", - "dev": true, - "dependencies": { - "ansi-styles": "^4.0.0", - "string-width": "^4.1.0", - "strip-ansi": "^6.0.0" - }, - "engines": { - "node": ">=8" - } - }, - "node_modules/clone": { - "version": "1.0.4", - "resolved": "https://registry.npmjs.org/clone/-/clone-1.0.4.tgz", - "integrity": "sha512-JQHZ2QMW6l3aH/j6xCqQThY/9OH4D/9ls34cgkUBiEeocRTU04tHfKPBsUK1PqZCUQM7GiA0IIXJSuXHI64Kbg==", + "node_modules/clone": { + "version": "1.0.4", + "resolved": "https://registry.npmjs.org/clone/-/clone-1.0.4.tgz", + "integrity": "sha512-JQHZ2QMW6l3aH/j6xCqQThY/9OH4D/9ls34cgkUBiEeocRTU04tHfKPBsUK1PqZCUQM7GiA0IIXJSuXHI64Kbg==", "dev": true, "engines": { "node": ">=0.8" @@ -2288,10 +2359,13 @@ "dev": true }, "node_modules/commander": { - "version": "2.20.3", - "resolved": "https://registry.npmjs.org/commander/-/commander-2.20.3.tgz", - "integrity": "sha512-GpVkmM8vF2vQUkj2LvZmD35JxeJOLCwJ9cUkugyk2nuhbv3+mJvpLYYt+0+USMxE+oj+ey/lJEnhZw75x/OMcQ==", - "dev": true + "version": "11.0.0", + "resolved": "https://registry.npmjs.org/commander/-/commander-11.0.0.tgz", + "integrity": "sha512-9HMlXtt/BNoYr8ooyjjNRdIilOTkVJXB+GhxMTtOKwk0R4j4lS4NpjuqmRxroBfnfTSHQIHQB7wryHhXarNjmQ==", + "dev": true, + "engines": { + "node": ">=16" + } }, "node_modules/commondir": { "version": "1.0.1", @@ -2346,16 +2420,10 @@ "integrity": "sha512-ASFBup0Mz1uyiIjANan1jzLQami9z1PoYSZCiiYW2FczPbenXc45FZdBZLzOT+r6+iciuEModtmCti+hjaAk0A==", "dev": true }, - "node_modules/core-util-is": { - "version": "1.0.3", - "resolved": "https://registry.npmjs.org/core-util-is/-/core-util-is-1.0.3.tgz", - "integrity": "sha512-ZQBvi1DcpJ4GDqanjucZ2Hj3wEO5pZDS89BWbkcrvdxksJorwUDDZamX9ldFkp9aw2lmBDLgkObEA4DWNJ9FYQ==", - "dev": true - }, "node_modules/cosmiconfig": { - "version": "8.1.3", - "resolved": "https://registry.npmjs.org/cosmiconfig/-/cosmiconfig-8.1.3.tgz", - "integrity": "sha512-/UkO2JKI18b5jVMJUp0lvKFMpa/Gye+ZgZjKD+DGEN9y7NRcf/nK1A0sp67ONmKtnDCNMS44E6jrk0Yc3bDuUw==", + "version": "8.2.0", + "resolved": "https://registry.npmjs.org/cosmiconfig/-/cosmiconfig-8.2.0.tgz", + "integrity": "sha512-3rTMnFJA1tCOPwRxtgF4wd7Ab2qvDbL8jX+3smjIbS4HlZBagTlpERbdN7iAbWlrfxE3M8c27kTwTawQ7st+OQ==", "dev": true, "dependencies": { "import-fresh": "^3.2.1", @@ -2593,27 +2661,17 @@ } }, "node_modules/degenerator": { - "version": "3.0.2", - "resolved": "https://registry.npmjs.org/degenerator/-/degenerator-3.0.2.tgz", - "integrity": "sha512-c0mef3SNQo56t6urUU6tdQAs+ThoD0o9B9MJ8HEt7NQcGEILCRFqQb7ZbP9JAv+QF1Ky5plydhMR/IrqWDm+TQ==", + "version": "5.0.1", + "resolved": "https://registry.npmjs.org/degenerator/-/degenerator-5.0.1.tgz", + "integrity": "sha512-TllpMR/t0M5sqCXfj85i4XaAzxmS5tVA16dqvdkMwGmzI+dXLXnw3J+3Vdv7VKw+ThlTMboK6i9rnZ6Nntj5CQ==", "dev": true, "dependencies": { - "ast-types": "^0.13.2", - "escodegen": "^1.8.1", - "esprima": "^4.0.0", - "vm2": "^3.9.8" + "ast-types": "^0.13.4", + "escodegen": "^2.1.0", + "esprima": "^4.0.1" }, "engines": { - "node": ">= 6" - } - }, - "node_modules/depd": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/depd/-/depd-2.0.0.tgz", - "integrity": "sha512-g7nH6P6dyDioJogAAGprGpCtVImJhpPk/roCzdb3fIh61/s/nPsfR6onyMwkCAR/OlC3yBC0lESvUoQEAssIrw==", - "dev": true, - "engines": { - "node": ">= 0.8" + "node": ">= 14" } }, "node_modules/deprecation": { @@ -2677,9 +2735,9 @@ "dev": true }, "node_modules/electron-to-chromium": { - "version": "1.4.311", - "resolved": "https://registry.npmjs.org/electron-to-chromium/-/electron-to-chromium-1.4.311.tgz", - "integrity": "sha512-RoDlZufvrtr2Nx3Yx5MB8jX3aHIxm8nRWPJm3yVvyHmyKaRvn90RjzB6hNnt0AkhS3IInJdyRfQb4mWhPvUjVw==", + "version": "1.4.499", + "resolved": "https://registry.npmjs.org/electron-to-chromium/-/electron-to-chromium-1.4.499.tgz", + "integrity": "sha512-0NmjlYBLKVHva4GABWAaHuPJolnDuL0AhV3h1hES6rcLCWEIbRL6/8TghfsVwkx6TEroQVdliX7+aLysUpKvjw==", "dev": true }, "node_modules/email-addresses": { @@ -2689,9 +2747,9 @@ "dev": true }, "node_modules/emoji-regex": { - "version": "9.2.2", - "resolved": "https://registry.npmjs.org/emoji-regex/-/emoji-regex-9.2.2.tgz", - "integrity": "sha512-L18DaJsXSUk2+42pv8mLs5jJT2hqFkFE4j21wOmgbUqsZ2hL72NsUU785g9RXgo3s0ZNgVl42TiHp3ZtOv/Vyg==", + "version": "8.0.0", + "resolved": "https://registry.npmjs.org/emoji-regex/-/emoji-regex-8.0.0.tgz", + "integrity": "sha512-MSjYzcWNOA0ewAHpz0MxpYFvwg6yjy1NG3xteoqz644VCo/RPgnr1/GGt+ic3iJTzQ8Eu3TdM14SawnVUmGE6A==", "dev": true }, "node_modules/error-ex": { @@ -2704,18 +2762,19 @@ } }, "node_modules/es-abstract": { - "version": "1.21.1", - "resolved": "https://registry.npmjs.org/es-abstract/-/es-abstract-1.21.1.tgz", - "integrity": "sha512-QudMsPOz86xYz/1dG1OuGBKOELjCh99IIWHLzy5znUB6j8xG2yMA7bfTV86VSqKF+Y/H08vQPR+9jyXpuC6hfg==", + "version": "1.22.1", + "resolved": "https://registry.npmjs.org/es-abstract/-/es-abstract-1.22.1.tgz", + "integrity": "sha512-ioRRcXMO6OFyRpyzV3kE1IIBd4WG5/kltnzdxSCqoP8CMGs/Li+M1uF5o7lOkZVFjDs+NLesthnF66Pg/0q0Lw==", "dev": true, "dependencies": { + "array-buffer-byte-length": "^1.0.0", + "arraybuffer.prototype.slice": "^1.0.1", "available-typed-arrays": "^1.0.5", "call-bind": "^1.0.2", "es-set-tostringtag": "^2.0.1", "es-to-primitive": "^1.2.1", - "function-bind": "^1.1.1", "function.prototype.name": "^1.1.5", - "get-intrinsic": "^1.1.3", + "get-intrinsic": "^1.2.1", "get-symbol-description": "^1.0.0", "globalthis": "^1.0.3", "gopd": "^1.0.1", @@ -2723,8 +2782,8 @@ "has-property-descriptors": "^1.0.0", "has-proto": "^1.0.1", "has-symbols": "^1.0.3", - "internal-slot": "^1.0.4", - "is-array-buffer": "^3.0.1", + "internal-slot": "^1.0.5", + "is-array-buffer": "^3.0.2", "is-callable": "^1.2.7", "is-negative-zero": "^2.0.2", "is-regex": "^1.1.4", @@ -2732,16 +2791,21 @@ "is-string": "^1.0.7", "is-typed-array": "^1.1.10", "is-weakref": "^1.0.2", - "object-inspect": "^1.12.2", + "object-inspect": "^1.12.3", "object-keys": "^1.1.1", "object.assign": "^4.1.4", - "regexp.prototype.flags": "^1.4.3", + "regexp.prototype.flags": "^1.5.0", + "safe-array-concat": "^1.0.0", "safe-regex-test": "^1.0.0", + "string.prototype.trim": "^1.2.7", "string.prototype.trimend": "^1.0.6", "string.prototype.trimstart": "^1.0.6", + "typed-array-buffer": "^1.0.0", + "typed-array-byte-length": "^1.0.0", + "typed-array-byte-offset": "^1.0.0", "typed-array-length": "^1.0.4", "unbox-primitive": "^1.0.2", - "which-typed-array": "^1.1.9" + "which-typed-array": "^1.1.10" }, "engines": { "node": ">= 0.4" @@ -2847,100 +2911,48 @@ } }, "node_modules/escodegen": { - "version": "1.14.3", - "resolved": "https://registry.npmjs.org/escodegen/-/escodegen-1.14.3.tgz", - "integrity": "sha512-qFcX0XJkdg+PB3xjZZG/wKSuT1PnQWx57+TVSjIMmILd2yC/6ByYElPwJnslDsuWuSAp4AwJGumarAAmJch5Kw==", + "version": "2.1.0", + "resolved": "https://registry.npmjs.org/escodegen/-/escodegen-2.1.0.tgz", + "integrity": "sha512-2NlIDTwUWJN0mRPQOdtQBzbUHvdGY2P1VXSyU83Q3xKxM7WHX2Ql8dKq782Q9TgQUNOLEzEYu9bzLNj1q88I5w==", "dev": true, "dependencies": { "esprima": "^4.0.1", - "estraverse": "^4.2.0", - "esutils": "^2.0.2", - "optionator": "^0.8.1" + "estraverse": "^5.2.0", + "esutils": "^2.0.2" }, "bin": { "escodegen": "bin/escodegen.js", "esgenerate": "bin/esgenerate.js" }, "engines": { - "node": ">=4.0" + "node": ">=6.0" }, "optionalDependencies": { "source-map": "~0.6.1" } }, - "node_modules/escodegen/node_modules/levn": { - "version": "0.3.0", - "resolved": "https://registry.npmjs.org/levn/-/levn-0.3.0.tgz", - "integrity": "sha512-0OO4y2iOHix2W6ujICbKIaEQXvFQHue65vUG3pb5EUomzPI90z9hsA1VsO/dbIIpC53J8gxM9Q4Oho0jrCM/yA==", - "dev": true, - "dependencies": { - "prelude-ls": "~1.1.2", - "type-check": "~0.3.2" - }, - "engines": { - "node": ">= 0.8.0" - } - }, - "node_modules/escodegen/node_modules/optionator": { - "version": "0.8.3", - "resolved": "https://registry.npmjs.org/optionator/-/optionator-0.8.3.tgz", - "integrity": "sha512-+IW9pACdk3XWmmTXG8m3upGUJst5XRGzxMRjXzAuJ1XnIFNvfhjjIuYkDvysnPQ7qzqVzLt78BCruntqRhWQbA==", - "dev": true, - "dependencies": { - "deep-is": "~0.1.3", - "fast-levenshtein": "~2.0.6", - "levn": "~0.3.0", - "prelude-ls": "~1.1.2", - "type-check": "~0.3.2", - "word-wrap": "~1.2.3" - }, - "engines": { - "node": ">= 0.8.0" - } - }, - "node_modules/escodegen/node_modules/prelude-ls": { - "version": "1.1.2", - "resolved": "https://registry.npmjs.org/prelude-ls/-/prelude-ls-1.1.2.tgz", - "integrity": "sha512-ESF23V4SKG6lVSGZgYNpbsiaAkdab6ZgOxe52p7+Kid3W3u3bxR4Vfd/o21dmN7jSt0IwgZ4v5MUd26FEtXE9w==", - "dev": true, - "engines": { - "node": ">= 0.8.0" - } - }, - "node_modules/escodegen/node_modules/type-check": { - "version": "0.3.2", - "resolved": "https://registry.npmjs.org/type-check/-/type-check-0.3.2.tgz", - "integrity": "sha512-ZCmOJdvOWDBYJlzAoFkC+Q0+bUyEOS1ltgp1MGU03fqHG+dbi9tBFU2Rd9QKiDZFAYrhPh2JUf7rZRIuHRKtOg==", - "dev": true, - "dependencies": { - "prelude-ls": "~1.1.2" - }, - "engines": { - "node": ">= 0.8.0" - } - }, "node_modules/eslint": { - "version": "8.41.0", - "resolved": "https://registry.npmjs.org/eslint/-/eslint-8.41.0.tgz", - "integrity": "sha512-WQDQpzGBOP5IrXPo4Hc0814r4/v2rrIsB0rhT7jtunIalgg6gYXWhRMOejVO8yH21T/FGaxjmFjBMNqcIlmH1Q==", + "version": "8.47.0", + "resolved": "https://registry.npmjs.org/eslint/-/eslint-8.47.0.tgz", + "integrity": "sha512-spUQWrdPt+pRVP1TTJLmfRNJJHHZryFmptzcafwSvHsceV81djHOdnEeDmkdotZyLNjDhrOasNK8nikkoG1O8Q==", "dev": true, "dependencies": { "@eslint-community/eslint-utils": "^4.2.0", - "@eslint-community/regexpp": "^4.4.0", - "@eslint/eslintrc": "^2.0.3", - "@eslint/js": "8.41.0", - "@humanwhocodes/config-array": "^0.11.8", + "@eslint-community/regexpp": "^4.6.1", + "@eslint/eslintrc": "^2.1.2", + "@eslint/js": "^8.47.0", + "@humanwhocodes/config-array": "^0.11.10", "@humanwhocodes/module-importer": "^1.0.1", "@nodelib/fs.walk": "^1.2.8", - "ajv": "^6.10.0", + "ajv": "^6.12.4", "chalk": "^4.0.0", "cross-spawn": "^7.0.2", "debug": "^4.3.2", "doctrine": "^3.0.0", "escape-string-regexp": "^4.0.0", - "eslint-scope": "^7.2.0", - "eslint-visitor-keys": "^3.4.1", - "espree": "^9.5.2", + "eslint-scope": "^7.2.2", + "eslint-visitor-keys": "^3.4.3", + "espree": "^9.6.1", "esquery": "^1.4.2", "esutils": "^2.0.2", "fast-deep-equal": "^3.1.3", @@ -2950,7 +2962,6 @@ "globals": "^13.19.0", "graphemer": "^1.4.0", "ignore": "^5.2.0", - "import-fresh": "^3.0.0", "imurmurhash": "^0.1.4", "is-glob": "^4.0.0", "is-path-inside": "^3.0.3", @@ -2960,9 +2971,8 @@ "lodash.merge": "^4.6.2", "minimatch": "^3.1.2", "natural-compare": "^1.4.0", - "optionator": "^0.9.1", + "optionator": "^0.9.3", "strip-ansi": "^6.0.1", - "strip-json-comments": "^3.1.0", "text-table": "^0.2.0" }, "bin": { @@ -2976,23 +2986,14 @@ } }, "node_modules/eslint-scope": { - "version": "5.1.1", - "resolved": "https://registry.npmjs.org/eslint-scope/-/eslint-scope-5.1.1.tgz", - "integrity": "sha512-2NxwbF/hZ0KpepYN0cNbo+FN6XoK7GaHlQhgx/hIZl6Va0bF45RQOOwhLIy8lQDbuCiadSLCBnH2CFYquit5bw==", + "version": "7.2.2", + "resolved": "https://registry.npmjs.org/eslint-scope/-/eslint-scope-7.2.2.tgz", + "integrity": "sha512-dOt21O7lTMhDM+X9mB4GX+DZrZtCUJPL/wlcTqxyrx5IvO0IYtILdtrQGQp+8n5S0gwSVmOf9NQrjMOgfQZlIg==", "dev": true, "dependencies": { "esrecurse": "^4.3.0", - "estraverse": "^4.1.1" + "estraverse": "^5.2.0" }, - "engines": { - "node": ">=8.0.0" - } - }, - "node_modules/eslint-visitor-keys": { - "version": "3.4.1", - "resolved": "https://registry.npmjs.org/eslint-visitor-keys/-/eslint-visitor-keys-3.4.1.tgz", - "integrity": "sha512-pZnmmLwYzf+kWaM/Qgrvpen51upAktaaiI01nsJD/Yr3lMOdNtq0cxkrrg16w64VtisN6okbs7Q8AfGqj4c9fA==", - "dev": true, "engines": { "node": "^12.22.0 || ^14.17.0 || >=16.0.0" }, @@ -3000,28 +3001,16 @@ "url": "https://opencollective.com/eslint" } }, - "node_modules/eslint/node_modules/ansi-regex": { - "version": "5.0.1", - "resolved": "https://registry.npmjs.org/ansi-regex/-/ansi-regex-5.0.1.tgz", - "integrity": "sha512-quJQXlTSUGL2LH9SUXo8VwsY4soanhgo6LNSm84E1LBcE8s3O0wpdiRzyR9z/ZZJMlMWv37qOOb9pdJlMUEKFQ==", - "dev": true, - "engines": { - "node": ">=8" - } - }, - "node_modules/eslint/node_modules/ansi-styles": { - "version": "4.3.0", - "resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-4.3.0.tgz", - "integrity": "sha512-zbB9rCJAT1rbjiVDb2hqKFHNYLxgtk8NURxZ3IZwD3F6NtxbXZQCnnSi1Lkx+IDohdPlFp222wVALIheZJQSEg==", + "node_modules/eslint-visitor-keys": { + "version": "3.4.3", + "resolved": "https://registry.npmjs.org/eslint-visitor-keys/-/eslint-visitor-keys-3.4.3.tgz", + "integrity": "sha512-wpc+LXeiyiisxPlEkUzU6svyS1frIO3Mgxj1fdy7Pm8Ygzguax2N3Fa/D/ag1WqbOprdI+uY6wMUl8/a2G+iag==", "dev": true, - "dependencies": { - "color-convert": "^2.0.1" - }, "engines": { - "node": ">=8" + "node": "^12.22.0 || ^14.17.0 || >=16.0.0" }, "funding": { - "url": "https://github.com/chalk/ansi-styles?sponsor=1" + "url": "https://opencollective.com/eslint" } }, "node_modules/eslint/node_modules/chalk": { @@ -3052,31 +3041,6 @@ "url": "https://github.com/sponsors/sindresorhus" } }, - "node_modules/eslint/node_modules/eslint-scope": { - "version": "7.2.0", - "resolved": "https://registry.npmjs.org/eslint-scope/-/eslint-scope-7.2.0.tgz", - "integrity": "sha512-DYj5deGlHBfMt15J7rdtyKNq/Nqlv5KfU4iodrQ019XESsRnwXH9KAE0y3cwtUHDo2ob7CypAnCqefh6vioWRw==", - "dev": true, - "dependencies": { - "esrecurse": "^4.3.0", - "estraverse": "^5.2.0" - }, - "engines": { - "node": "^12.22.0 || ^14.17.0 || >=16.0.0" - }, - "funding": { - "url": "https://opencollective.com/eslint" - } - }, - "node_modules/eslint/node_modules/estraverse": { - "version": "5.3.0", - "resolved": "https://registry.npmjs.org/estraverse/-/estraverse-5.3.0.tgz", - "integrity": "sha512-MMdARuVEQziNTeJD8DgMqmhwR11BRQ/cBP+pLtYdSTnf3MIO8fFeiINEbX36ZdNlfU/7A9f3gUw49B3oQsvwBA==", - "dev": true, - "engines": { - "node": ">=4.0" - } - }, "node_modules/eslint/node_modules/find-up": { "version": "5.0.0", "resolved": "https://registry.npmjs.org/find-up/-/find-up-5.0.0.tgz", @@ -3093,6 +3057,27 @@ "url": "https://github.com/sponsors/sindresorhus" } }, + "node_modules/eslint/node_modules/glob-parent": { + "version": "6.0.2", + "resolved": "https://registry.npmjs.org/glob-parent/-/glob-parent-6.0.2.tgz", + "integrity": "sha512-XxwI8EOhVQgWp6iDL+3b0r86f4d6AX6zSU55HfB4ydCEuXLXc5FcYeOu+nnGftS4TEju/11rt4KJPTMgbfmv4A==", + "dev": true, + "dependencies": { + "is-glob": "^4.0.3" + }, + "engines": { + "node": ">=10.13.0" + } + }, + "node_modules/eslint/node_modules/has-flag": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/has-flag/-/has-flag-4.0.0.tgz", + "integrity": "sha512-EykJT/Q1KjTWctppgIAgfSO0tKVuZUjhgMr17kqTumMl6Afv3EISleU7qZUzoXDFTAHTDC4NOoG/ZxU3EvlMPQ==", + "dev": true, + "engines": { + "node": ">=8" + } + }, "node_modules/eslint/node_modules/locate-path": { "version": "6.0.0", "resolved": "https://registry.npmjs.org/locate-path/-/locate-path-6.0.0.tgz", @@ -3138,25 +3123,25 @@ "url": "https://github.com/sponsors/sindresorhus" } }, - "node_modules/eslint/node_modules/strip-ansi": { - "version": "6.0.1", - "resolved": "https://registry.npmjs.org/strip-ansi/-/strip-ansi-6.0.1.tgz", - "integrity": "sha512-Y38VPSHcqkFrCpFnQ9vuSXmquuv5oXOKpGeT6aGrr3o3Gc9AlVa6JBfUSOCnbxGGZF+/0ooI7KrPuUSztUdU5A==", + "node_modules/eslint/node_modules/supports-color": { + "version": "7.2.0", + "resolved": "https://registry.npmjs.org/supports-color/-/supports-color-7.2.0.tgz", + "integrity": "sha512-qpCAvRl9stuOHveKsn7HncJRvv501qIacKzQlO/+Lwxc9+0q2wLyv4Dfvt80/DPn2pqOBsJdDiogXGR9+OvwRw==", "dev": true, "dependencies": { - "ansi-regex": "^5.0.1" + "has-flag": "^4.0.0" }, "engines": { "node": ">=8" } }, "node_modules/espree": { - "version": "9.5.2", - "resolved": "https://registry.npmjs.org/espree/-/espree-9.5.2.tgz", - "integrity": "sha512-7OASN1Wma5fum5SrNhFMAMJxOUAbhyfQ8dQ//PJaJbNw0URTPWqIghHWt1MmAANKhHZIYOHruW4Kw4ruUWOdGw==", + "version": "9.6.1", + "resolved": "https://registry.npmjs.org/espree/-/espree-9.6.1.tgz", + "integrity": "sha512-oruZaFkjorTpF32kDSI5/75ViwGeZginGGy2NoOSg3Q9bnwlnmDm4HLnkl0RE3n+njDXR037aY1+x58Z/zFdwQ==", "dev": true, "dependencies": { - "acorn": "^8.8.0", + "acorn": "^8.9.0", "acorn-jsx": "^5.3.2", "eslint-visitor-keys": "^3.4.1" }, @@ -3181,9 +3166,9 @@ } }, "node_modules/esquery": { - "version": "1.4.2", - "resolved": "https://registry.npmjs.org/esquery/-/esquery-1.4.2.tgz", - "integrity": "sha512-JVSoLdTlTDkmjFmab7H/9SL9qGSyjElT3myyKp7krqjVFQCDLmj1QFaCLRFBszBKI0XVZaiiXvuPIX3ZwHe1Ng==", + "version": "1.5.0", + "resolved": "https://registry.npmjs.org/esquery/-/esquery-1.5.0.tgz", + "integrity": "sha512-YQLXUplAwJgCydQ78IMJywZCceoqk1oH01OERdSAJc/7U2AylwjhSCLDEtqwg811idIS/9fIU5GjG73IgjKMVg==", "dev": true, "dependencies": { "estraverse": "^5.1.0" @@ -3192,15 +3177,6 @@ "node": ">=0.10" } }, - "node_modules/esquery/node_modules/estraverse": { - "version": "5.3.0", - "resolved": "https://registry.npmjs.org/estraverse/-/estraverse-5.3.0.tgz", - "integrity": "sha512-MMdARuVEQziNTeJD8DgMqmhwR11BRQ/cBP+pLtYdSTnf3MIO8fFeiINEbX36ZdNlfU/7A9f3gUw49B3oQsvwBA==", - "dev": true, - "engines": { - "node": ">=4.0" - } - }, "node_modules/esrecurse": { "version": "4.3.0", "resolved": "https://registry.npmjs.org/esrecurse/-/esrecurse-4.3.0.tgz", @@ -3213,7 +3189,7 @@ "node": ">=4.0" } }, - "node_modules/esrecurse/node_modules/estraverse": { + "node_modules/estraverse": { "version": "5.3.0", "resolved": "https://registry.npmjs.org/estraverse/-/estraverse-5.3.0.tgz", "integrity": "sha512-MMdARuVEQziNTeJD8DgMqmhwR11BRQ/cBP+pLtYdSTnf3MIO8fFeiINEbX36ZdNlfU/7A9f3gUw49B3oQsvwBA==", @@ -3222,15 +3198,6 @@ "node": ">=4.0" } }, - "node_modules/estraverse": { - "version": "4.3.0", - "resolved": "https://registry.npmjs.org/estraverse/-/estraverse-4.3.0.tgz", - "integrity": "sha512-39nnKffWz8xN1BU/2c79n9nB9HDzo0niYUqx6xyqUnyoAnQyyWpOTdZEeiCch8BBu515t4wp9ZmgVfVhn9EBpw==", - "dev": true, - "engines": { - "node": ">=4.0" - } - }, "node_modules/esutils": { "version": "2.0.3", "resolved": "https://registry.npmjs.org/esutils/-/esutils-2.0.3.tgz", @@ -3241,9 +3208,9 @@ } }, "node_modules/execa": { - "version": "7.1.1", - "resolved": "https://registry.npmjs.org/execa/-/execa-7.1.1.tgz", - "integrity": "sha512-wH0eMf/UXckdUYnO21+HDztteVv05rq2GXksxT4fCGeHkBhw1DROXh40wcjMcRqDOWE7iPJ4n3M7e2+YFP+76Q==", + "version": "7.2.0", + "resolved": "https://registry.npmjs.org/execa/-/execa-7.2.0.tgz", + "integrity": "sha512-UduyVP7TLB5IcAQl+OzLyLcS/l32W/GLg+AhHJ+ow40FOk2U3SAllPwR44v4vmdFwIWqpdwxxpQbF1n5ta9seA==", "dev": true, "dependencies": { "cross-spawn": "^7.0.3", @@ -3284,9 +3251,9 @@ "dev": true }, "node_modules/fast-glob": { - "version": "3.2.12", - "resolved": "https://registry.npmjs.org/fast-glob/-/fast-glob-3.2.12.tgz", - "integrity": "sha512-DVj4CQIYYow0BlaelwK1pHl5n5cRSJfM60UA0zK891sVInoPri2Ekj7+e1CT3/3qxXenpI+nBBmQAcJPJgaj4w==", + "version": "3.3.1", + "resolved": "https://registry.npmjs.org/fast-glob/-/fast-glob-3.3.1.tgz", + "integrity": "sha512-kNFPyjhh5cKjrUltxs+wFx+ZkbRaxxmZ+X0ZU31SOsxCEtP9VPgtq2teZw1DebupL5GmDaNQ6yKMMVcM41iqDg==", "dev": true, "dependencies": { "@nodelib/fs.stat": "^2.0.2", @@ -3299,18 +3266,6 @@ "node": ">=8.6.0" } }, - "node_modules/fast-glob/node_modules/glob-parent": { - "version": "5.1.2", - "resolved": "https://registry.npmjs.org/glob-parent/-/glob-parent-5.1.2.tgz", - "integrity": "sha512-AOIgSQCepiJYwP3ARnGx+5VnTu2HBYdzbGP45eLw1vr3zB3vZLeyed1sC9hnbcOc9/SrMyM5RPQrkGz4aS9Zow==", - "dev": true, - "dependencies": { - "is-glob": "^4.0.1" - }, - "engines": { - "node": ">= 6" - } - }, "node_modules/fast-json-stable-stringify": { "version": "2.1.0", "resolved": "https://registry.npmjs.org/fast-json-stable-stringify/-/fast-json-stable-stringify-2.1.0.tgz", @@ -3383,15 +3338,6 @@ "node": "^10.12.0 || >=12.0.0" } }, - "node_modules/file-uri-to-path": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/file-uri-to-path/-/file-uri-to-path-2.0.0.tgz", - "integrity": "sha512-hjPFI8oE/2iQPVe4gbrJ73Pp+Xfub2+WI2LlXDbsaJBwT5wuMh35WNWVYYTpnz895shtwfyutMFLFywpQAFdLg==", - "dev": true, - "engines": { - "node": ">= 6" - } - }, "node_modules/filename-reserved-regex": { "version": "2.0.0", "resolved": "https://registry.npmjs.org/filename-reserved-regex/-/filename-reserved-regex-2.0.0.tgz", @@ -3552,17 +3498,17 @@ ] }, "node_modules/fs-extra": { - "version": "8.1.0", - "resolved": "https://registry.npmjs.org/fs-extra/-/fs-extra-8.1.0.tgz", - "integrity": "sha512-yhlQgA6mnOJUKOsRUFsgJdQCvkKhcz8tlZG5HBQfReYZy46OwLcY+Zia0mtdHsOo9y/hP+CxMN0TU9QxoOtG4g==", + "version": "11.1.1", + "resolved": "https://registry.npmjs.org/fs-extra/-/fs-extra-11.1.1.tgz", + "integrity": "sha512-MGIE4HOvQCeUCzmlHs0vXpih4ysz4wg9qiSAu6cd42lVwPbTM1TjV7RusoyQqMmk/95gdQZX72u+YW+c3eEpFQ==", "dev": true, "dependencies": { "graceful-fs": "^4.2.0", - "jsonfile": "^4.0.0", - "universalify": "^0.1.0" + "jsonfile": "^6.0.1", + "universalify": "^2.0.0" }, "engines": { - "node": ">=6 <7 || >=8" + "node": ">=14.14" } }, "node_modules/fs.realpath": { @@ -3572,9 +3518,9 @@ "dev": true }, "node_modules/fsevents": { - "version": "2.3.2", - "resolved": "https://registry.npmjs.org/fsevents/-/fsevents-2.3.2.tgz", - "integrity": "sha512-xiqMQR4xAeHTuB9uWm+fFRcIOgKBMiOBP+eXiyT7jsgVCq1bkVygt00oASowB7EdtpOHaaPgKt812P9ab+DDKA==", + "version": "2.3.3", + "resolved": "https://registry.npmjs.org/fsevents/-/fsevents-2.3.3.tgz", + "integrity": "sha512-5xoDfX+fL7faATnagmWPpbFtwh/R77WmMMqqHGS65C3vvB0YHrgF+B1YmZ3441tMj5n63k0212XNoJwzlhffQw==", "dev": true, "hasInstallScript": true, "optional": true, @@ -3585,43 +3531,6 @@ "node": "^8.16.0 || ^10.6.0 || >=11.0.0" } }, - "node_modules/ftp": { - "version": "0.3.10", - "resolved": "https://registry.npmjs.org/ftp/-/ftp-0.3.10.tgz", - "integrity": "sha512-faFVML1aBx2UoDStmLwv2Wptt4vw5x03xxX172nhA5Y5HBshW5JweqQ2W4xL4dezQTG8inJsuYcpPHHU3X5OTQ==", - "dev": true, - "dependencies": { - "readable-stream": "1.1.x", - "xregexp": "2.0.0" - }, - "engines": { - "node": ">=0.8.0" - } - }, - "node_modules/ftp/node_modules/isarray": { - "version": "0.0.1", - "resolved": "https://registry.npmjs.org/isarray/-/isarray-0.0.1.tgz", - "integrity": "sha512-D2S+3GLxWH+uhrNEcoh/fnmYeP8E8/zHl644d/jdA0g2uyXvy3sb0qxotE+ne0LtccHknQzWwZEzhak7oJ0COQ==", - "dev": true - }, - "node_modules/ftp/node_modules/readable-stream": { - "version": "1.1.14", - "resolved": "https://registry.npmjs.org/readable-stream/-/readable-stream-1.1.14.tgz", - "integrity": "sha512-+MeVjFf4L44XUkhM1eYbD8fyEsxcV81pqMSR5gblfcLCHfZvbrqy4/qYHE+/R5HoBUT11WV5O08Cr1n3YXkWVQ==", - "dev": true, - "dependencies": { - "core-util-is": "~1.0.0", - "inherits": "~2.0.1", - "isarray": "0.0.1", - "string_decoder": "~0.10.x" - } - }, - "node_modules/ftp/node_modules/string_decoder": { - "version": "0.10.31", - "resolved": "https://registry.npmjs.org/string_decoder/-/string_decoder-0.10.31.tgz", - "integrity": "sha512-ev2QzSzWPYmy9GuqfIVildA4OdcGLeFZQrq5ys6RtiuF+RQQiZWr8TZNyAcuVXyQRYfEO+MsoB/1BuQVhOJuoQ==", - "dev": true - }, "node_modules/function-bind": { "version": "1.1.1", "resolved": "https://registry.npmjs.org/function-bind/-/function-bind-1.1.1.tgz", @@ -3682,13 +3591,14 @@ } }, "node_modules/get-intrinsic": { - "version": "1.2.0", - "resolved": "https://registry.npmjs.org/get-intrinsic/-/get-intrinsic-1.2.0.tgz", - "integrity": "sha512-L049y6nFOuom5wGyRc3/gdTLO94dySVKRACj1RmJZBQXlbTMhtNIgkWkUHq+jYmZvKf14EW1EoJnnjbmoHij0Q==", + "version": "1.2.1", + "resolved": "https://registry.npmjs.org/get-intrinsic/-/get-intrinsic-1.2.1.tgz", + "integrity": "sha512-2DcsyfABl+gVHEfCOaTrWgyt+tb6MSEGmKq+kI5HwLbIYgjgmMcV8KQ41uaKz1xxUcn9tJtgFbQUEVcEbd0FYw==", "dev": true, "dependencies": { "function-bind": "^1.1.1", "has": "^1.0.3", + "has-proto": "^1.0.1", "has-symbols": "^1.0.3" }, "funding": { @@ -3733,46 +3643,76 @@ } }, "node_modules/get-uri": { - "version": "3.0.2", - "resolved": "https://registry.npmjs.org/get-uri/-/get-uri-3.0.2.tgz", - "integrity": "sha512-+5s0SJbGoyiJTZZ2JTpFPLMPSch72KEqGOTvQsBqg0RBWvwhWUSYZFAtz3TPW0GXJuLBJPts1E241iHg+VRfhg==", + "version": "6.0.1", + "resolved": "https://registry.npmjs.org/get-uri/-/get-uri-6.0.1.tgz", + "integrity": "sha512-7ZqONUVqaabogsYNWlYj0t3YZaL6dhuEueZXGF+/YVmf6dHmaFg8/6psJKqhx9QykIDKzpGcy2cn4oV4YC7V/Q==", "dev": true, "dependencies": { - "@tootallnate/once": "1", - "data-uri-to-buffer": "3", - "debug": "4", - "file-uri-to-path": "2", - "fs-extra": "^8.1.0", - "ftp": "^0.3.10" + "basic-ftp": "^5.0.2", + "data-uri-to-buffer": "^5.0.1", + "debug": "^4.3.4", + "fs-extra": "^8.1.0" }, "engines": { - "node": ">= 6" + "node": ">= 14" } }, "node_modules/get-uri/node_modules/data-uri-to-buffer": { - "version": "3.0.1", - "resolved": "https://registry.npmjs.org/data-uri-to-buffer/-/data-uri-to-buffer-3.0.1.tgz", - "integrity": "sha512-WboRycPNsVw3B3TL559F7kuBUM4d8CgMEvk6xEJlOp7OBPjt6G7z8WMWlD2rOFZLk6OYfFIUGsCOWzcQH9K2og==", + "version": "5.0.1", + "resolved": "https://registry.npmjs.org/data-uri-to-buffer/-/data-uri-to-buffer-5.0.1.tgz", + "integrity": "sha512-a9l6T1qqDogvvnw0nKlfZzqsyikEBZBClF39V3TFoKhDtGBqHu2HkuomJc02j5zft8zrUaXEuoicLeW54RkzPg==", "dev": true, "engines": { - "node": ">= 6" + "node": ">= 14" } }, - "node_modules/gh-pages": { - "version": "5.0.0", - "resolved": "https://registry.npmjs.org/gh-pages/-/gh-pages-5.0.0.tgz", - "integrity": "sha512-Nqp1SjkPIB94Xw/3yYNTUL+G2dxlhjvv1zeN/4kMC1jfViTEqhtVz/Ba1zSXHuvXCN9ADNS1dN4r5/J/nZWEQQ==", + "node_modules/get-uri/node_modules/fs-extra": { + "version": "8.1.0", + "resolved": "https://registry.npmjs.org/fs-extra/-/fs-extra-8.1.0.tgz", + "integrity": "sha512-yhlQgA6mnOJUKOsRUFsgJdQCvkKhcz8tlZG5HBQfReYZy46OwLcY+Zia0mtdHsOo9y/hP+CxMN0TU9QxoOtG4g==", "dev": true, "dependencies": { - "async": "^3.2.4", - "commander": "^2.18.0", - "email-addresses": "^5.0.0", - "filenamify": "^4.3.0", - "find-cache-dir": "^3.3.1", - "fs-extra": "^8.1.0", - "globby": "^6.1.0" + "graceful-fs": "^4.2.0", + "jsonfile": "^4.0.0", + "universalify": "^0.1.0" }, - "bin": { + "engines": { + "node": ">=6 <7 || >=8" + } + }, + "node_modules/get-uri/node_modules/jsonfile": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/jsonfile/-/jsonfile-4.0.0.tgz", + "integrity": "sha512-m6F1R3z8jjlf2imQHS2Qez5sjKWQzbuuhuJ/FKYFRZvPE3PuHcSMVZzfsLhGVOkfd20obL5SWEBew5ShlquNxg==", + "dev": true, + "optionalDependencies": { + "graceful-fs": "^4.1.6" + } + }, + "node_modules/get-uri/node_modules/universalify": { + "version": "0.1.2", + "resolved": "https://registry.npmjs.org/universalify/-/universalify-0.1.2.tgz", + "integrity": "sha512-rBJeI5CXAlmy1pV+617WB9J63U6XcazHHF2f2dbJix4XzpUF0RS3Zbj0FGIOCAva5P/d/GBOYaACQ1w+0azUkg==", + "dev": true, + "engines": { + "node": ">= 4.0.0" + } + }, + "node_modules/gh-pages": { + "version": "6.0.0", + "resolved": "https://registry.npmjs.org/gh-pages/-/gh-pages-6.0.0.tgz", + "integrity": "sha512-FXZWJRsvP/fK2HJGY+Di6FRNHvqFF6gOIELaopDjXXgjeOYSNURcuYwEO/6bwuq6koP5Lnkvnr5GViXzuOB89g==", + "dev": true, + "dependencies": { + "async": "^3.2.4", + "commander": "^11.0.0", + "email-addresses": "^5.0.0", + "filenamify": "^4.3.0", + "find-cache-dir": "^3.3.1", + "fs-extra": "^11.1.1", + "globby": "^6.1.0" + }, + "bin": { "gh-pages": "bin/gh-pages.js", "gh-pages-clean": "bin/gh-pages-clean.js" }, @@ -3820,15 +3760,15 @@ } }, "node_modules/glob-parent": { - "version": "6.0.2", - "resolved": "https://registry.npmjs.org/glob-parent/-/glob-parent-6.0.2.tgz", - "integrity": "sha512-XxwI8EOhVQgWp6iDL+3b0r86f4d6AX6zSU55HfB4ydCEuXLXc5FcYeOu+nnGftS4TEju/11rt4KJPTMgbfmv4A==", + "version": "5.1.2", + "resolved": "https://registry.npmjs.org/glob-parent/-/glob-parent-5.1.2.tgz", + "integrity": "sha512-AOIgSQCepiJYwP3ARnGx+5VnTu2HBYdzbGP45eLw1vr3zB3vZLeyed1sC9hnbcOc9/SrMyM5RPQrkGz4aS9Zow==", "dev": true, "dependencies": { - "is-glob": "^4.0.3" + "is-glob": "^4.0.1" }, "engines": { - "node": ">=10.13.0" + "node": ">= 6" } }, "node_modules/global-dirs": { @@ -3847,9 +3787,9 @@ } }, "node_modules/globals": { - "version": "13.20.0", - "resolved": "https://registry.npmjs.org/globals/-/globals-13.20.0.tgz", - "integrity": "sha512-Qg5QtVkCy/kv3FUSlu4ukeZDVf9ee0iXLAUYX13gbR17bnejFTzr4iS9bY7kwCf1NztRNm1t91fjOiyx4CSwPQ==", + "version": "13.21.0", + "resolved": "https://registry.npmjs.org/globals/-/globals-13.21.0.tgz", + "integrity": "sha512-ybyme3s4yy/t/3s35bewwXKOf7cvzfreG2lH0lZl0JB7I4GxRP2ghxOK/Nb9EkRXdbBXZLfq/p/0W2JUONB/Gg==", "dev": true, "dependencies": { "type-fest": "^0.20.2" @@ -3861,6 +3801,18 @@ "url": "https://github.com/sponsors/sindresorhus" } }, + "node_modules/globals/node_modules/type-fest": { + "version": "0.20.2", + "resolved": "https://registry.npmjs.org/type-fest/-/type-fest-0.20.2.tgz", + "integrity": "sha512-Ne+eE4r0/iWnpAxD852z3A+N0Bt5RN//NjJwRd2VFHEmrywxf5vsZlh4R6lixl6B+wz/8d+maTSAkN1FIkI3LQ==", + "dev": true, + "engines": { + "node": ">=10" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, "node_modules/globalthis": { "version": "1.0.3", "resolved": "https://registry.npmjs.org/globalthis/-/globalthis-1.0.3.tgz", @@ -3905,9 +3857,9 @@ } }, "node_modules/got": { - "version": "12.6.0", - "resolved": "https://registry.npmjs.org/got/-/got-12.6.0.tgz", - "integrity": "sha512-WTcaQ963xV97MN3x0/CbAriXFZcXCfgxVp91I+Ze6pawQOa7SgzwSx2zIJJsX+kTajMnVs0xcFD1TxZKFqhdnQ==", + "version": "13.0.0", + "resolved": "https://registry.npmjs.org/got/-/got-13.0.0.tgz", + "integrity": "sha512-XfBk1CxOOScDcMr9O1yKkNaQyy865NbYs+F7dr4H0LZMVgCj2Le59k6PqbNHoL5ToeaEQUYh6c6yMfVcc6SJxA==", "dev": true, "dependencies": { "@sindresorhus/is": "^5.2.0", @@ -3923,22 +3875,16 @@ "responselike": "^3.0.0" }, "engines": { - "node": ">=14.16" + "node": ">=16" }, "funding": { "url": "https://github.com/sindresorhus/got?sponsor=1" } }, "node_modules/graceful-fs": { - "version": "4.2.10", - "resolved": "https://registry.npmjs.org/graceful-fs/-/graceful-fs-4.2.10.tgz", - "integrity": "sha512-9ByhssR2fPVsNZj478qUUbKfmL0+t5BDVyjShtyZZLiK7ZDAArFFfopyOTj0M05wE2tJPisA4iTnnXl2YoPvOA==", - "dev": true - }, - "node_modules/grapheme-splitter": { - "version": "1.0.4", - "resolved": "https://registry.npmjs.org/grapheme-splitter/-/grapheme-splitter-1.0.4.tgz", - "integrity": "sha512-bzh50DW9kTPM00T8y4o8vQg89Di9oLJVLW/KaOGIXJWP/iqCN6WKYkbNOF04vFLJhwcpYUh9ydh/+5vpOqV4YQ==", + "version": "4.2.11", + "resolved": "https://registry.npmjs.org/graceful-fs/-/graceful-fs-4.2.11.tgz", + "integrity": "sha512-RbJ5/jmFcNNCcDV5o9eTnBLJ/HszWV0P73bc+Ff4nS/rJj+YaS6IGyiOL0VoBYX+l1Wrl3k63h/KrH+nhJ0XvQ==", "dev": true }, "node_modules/graphemer": { @@ -3969,12 +3915,12 @@ } }, "node_modules/has-flag": { - "version": "4.0.0", - "resolved": "https://registry.npmjs.org/has-flag/-/has-flag-4.0.0.tgz", - "integrity": "sha512-EykJT/Q1KjTWctppgIAgfSO0tKVuZUjhgMr17kqTumMl6Afv3EISleU7qZUzoXDFTAHTDC4NOoG/ZxU3EvlMPQ==", + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/has-flag/-/has-flag-3.0.0.tgz", + "integrity": "sha512-sKJf1+ceQBr4SMkvQnBDNDtf4TXpVhVGateu0t918bl30FnbE2m4vNLX+VWe/dpjlb+HugGYzW7uQXH98HPEYw==", "dev": true, "engines": { - "node": ">=8" + "node": ">=4" } }, "node_modules/has-property-descriptors": { @@ -4098,34 +4044,17 @@ "integrity": "sha512-er295DKPVsV82j5kw1Gjt+ADA/XYHsajl82cGNQG2eyoPkvgUhX+nDIyelzhIWbbsXP39EHcI6l5tYs2FYqYXQ==", "dev": true }, - "node_modules/http-errors": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/http-errors/-/http-errors-2.0.0.tgz", - "integrity": "sha512-FtwrG/euBzaEjYeRqOgly7G0qviiXoJWnvEH2Z1plBdXgbyjv34pHTSb9zoeHMyDy33+DWy5Wt9Wo+TURtOYSQ==", - "dev": true, - "dependencies": { - "depd": "2.0.0", - "inherits": "2.0.4", - "setprototypeof": "1.2.0", - "statuses": "2.0.1", - "toidentifier": "1.0.1" - }, - "engines": { - "node": ">= 0.8" - } - }, "node_modules/http-proxy-agent": { - "version": "4.0.1", - "resolved": "https://registry.npmjs.org/http-proxy-agent/-/http-proxy-agent-4.0.1.tgz", - "integrity": "sha512-k0zdNgqWTGA6aeIRVpvfVob4fL52dTfaehylg0Y4UvSySvOq/Y+BOyPrgpUrA7HylqvU8vIZGsRuXmspskV0Tg==", + "version": "7.0.0", + "resolved": "https://registry.npmjs.org/http-proxy-agent/-/http-proxy-agent-7.0.0.tgz", + "integrity": "sha512-+ZT+iBxVUQ1asugqnD6oWoRiS25AkjNfG085dKJGtGxkdwLQrMKU5wJr2bOOFAXzKcTuqq+7fZlTMgG3SRfIYQ==", "dev": true, "dependencies": { - "@tootallnate/once": "1", - "agent-base": "6", - "debug": "4" + "agent-base": "^7.1.0", + "debug": "^4.3.4" }, "engines": { - "node": ">= 6" + "node": ">= 14" } }, "node_modules/http2-wrapper": { @@ -4142,16 +4071,16 @@ } }, "node_modules/https-proxy-agent": { - "version": "5.0.1", - "resolved": "https://registry.npmjs.org/https-proxy-agent/-/https-proxy-agent-5.0.1.tgz", - "integrity": "sha512-dFcAjpTQFgoLMzC2VwU+C/CbS7uRL0lWmxDITmqm7C+7F0Odmj6s9l6alZc6AELXhrnggM2CeWSXHGOdX2YtwA==", + "version": "7.0.1", + "resolved": "https://registry.npmjs.org/https-proxy-agent/-/https-proxy-agent-7.0.1.tgz", + "integrity": "sha512-Eun8zV0kcYS1g19r78osiQLEFIRspRUDd9tIfBCTBPBeMieF/EsJNL8VI3xOIdYRDEkjQnqOYPsZ2DsWsVsFwQ==", "dev": true, "dependencies": { - "agent-base": "6", + "agent-base": "^7.0.2", "debug": "4" }, "engines": { - "node": ">= 6" + "node": ">= 14" } }, "node_modules/human-signals": { @@ -4273,31 +4202,179 @@ } }, "node_modules/inquirer": { - "version": "9.2.0", - "resolved": "https://registry.npmjs.org/inquirer/-/inquirer-9.2.0.tgz", - "integrity": "sha512-WWERbVqjsTXjXub1ZW0ZHDit1dyHqy0T9XIkky9TnmKAPrjU9Jkd59nZPK0dUuM3s73GZAZu2Jo4iFU3XSPVLA==", + "version": "9.2.10", + "resolved": "https://registry.npmjs.org/inquirer/-/inquirer-9.2.10.tgz", + "integrity": "sha512-tVVNFIXU8qNHoULiazz612GFl+yqNfjMTbLuViNJE/d860Qxrd3NMrse8dm40VUQLOQeULvaQF8lpAhvysjeyA==", "dev": true, "dependencies": { - "ansi-escapes": "^6.0.0", - "chalk": "^5.2.0", - "cli-cursor": "^4.0.0", - "cli-width": "^4.0.0", - "external-editor": "^3.0.3", + "@ljharb/through": "^2.3.9", + "ansi-escapes": "^4.3.2", + "chalk": "^5.3.0", + "cli-cursor": "^3.1.0", + "cli-width": "^4.1.0", + "external-editor": "^3.1.0", "figures": "^5.0.0", "lodash": "^4.17.21", "mute-stream": "1.0.0", - "ora": "^6.1.2", - "run-async": "^2.4.0", - "rxjs": "^7.8.0", - "string-width": "^5.1.2", - "strip-ansi": "^7.0.1", - "through": "^2.3.6", - "wrap-ansi": "^8.1.0" + "ora": "^5.4.1", + "run-async": "^3.0.0", + "rxjs": "^7.8.1", + "string-width": "^4.2.3", + "strip-ansi": "^6.0.1", + "wrap-ansi": "^6.2.0" }, "engines": { "node": ">=14.18.0" } }, + "node_modules/inquirer/node_modules/bl": { + "version": "4.1.0", + "resolved": "https://registry.npmjs.org/bl/-/bl-4.1.0.tgz", + "integrity": "sha512-1W07cM9gS6DcLperZfFSj+bWLtaPGSOHWhPiGzXmvVJbRLdG82sH/Kn8EtW1VqWVA54AKf2h5k5BbnIbwF3h6w==", + "dev": true, + "dependencies": { + "buffer": "^5.5.0", + "inherits": "^2.0.4", + "readable-stream": "^3.4.0" + } + }, + "node_modules/inquirer/node_modules/buffer": { + "version": "5.7.1", + "resolved": "https://registry.npmjs.org/buffer/-/buffer-5.7.1.tgz", + "integrity": "sha512-EHcyIPBQ4BSGlvjB16k5KgAJ27CIsHY/2JBmCRReo48y9rQ3MaUzWX3KVlBa4U7MyX02HdVj0K7C3WaB3ju7FQ==", + "dev": true, + "funding": [ + { + "type": "github", + "url": "https://github.com/sponsors/feross" + }, + { + "type": "patreon", + "url": "https://www.patreon.com/feross" + }, + { + "type": "consulting", + "url": "https://feross.org/support" + } + ], + "dependencies": { + "base64-js": "^1.3.1", + "ieee754": "^1.1.13" + } + }, + "node_modules/inquirer/node_modules/has-flag": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/has-flag/-/has-flag-4.0.0.tgz", + "integrity": "sha512-EykJT/Q1KjTWctppgIAgfSO0tKVuZUjhgMr17kqTumMl6Afv3EISleU7qZUzoXDFTAHTDC4NOoG/ZxU3EvlMPQ==", + "dev": true, + "engines": { + "node": ">=8" + } + }, + "node_modules/inquirer/node_modules/is-interactive": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/is-interactive/-/is-interactive-1.0.0.tgz", + "integrity": "sha512-2HvIEKRoqS62guEC+qBjpvRubdX910WCMuJTZ+I9yvqKU2/12eSL549HMwtabb4oupdj2sMP50k+XJfB/8JE6w==", + "dev": true, + "engines": { + "node": ">=8" + } + }, + "node_modules/inquirer/node_modules/is-unicode-supported": { + "version": "0.1.0", + "resolved": "https://registry.npmjs.org/is-unicode-supported/-/is-unicode-supported-0.1.0.tgz", + "integrity": "sha512-knxG2q4UC3u8stRGyAVJCOdxFmv5DZiRcdlIaAQXAbSfJya+OhopNotLQrstBhququ4ZpuKbDc/8S6mgXgPFPw==", + "dev": true, + "engines": { + "node": ">=10" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/inquirer/node_modules/log-symbols": { + "version": "4.1.0", + "resolved": "https://registry.npmjs.org/log-symbols/-/log-symbols-4.1.0.tgz", + "integrity": "sha512-8XPvpAA8uyhfteu8pIvQxpJZ7SYYdpUivZpGy6sFsBuKRY/7rQGavedeB8aK+Zkyq6upMFVL/9AW6vOYzfRyLg==", + "dev": true, + "dependencies": { + "chalk": "^4.1.0", + "is-unicode-supported": "^0.1.0" + }, + "engines": { + "node": ">=10" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/inquirer/node_modules/log-symbols/node_modules/chalk": { + "version": "4.1.2", + "resolved": "https://registry.npmjs.org/chalk/-/chalk-4.1.2.tgz", + "integrity": "sha512-oKnbhFyRIXpUuez8iBMmyEa4nbj4IOQyuhc/wy9kY7/WVPcwIO9VA668Pu8RkO7+0G76SLROeyw9CpQ061i4mA==", + "dev": true, + "dependencies": { + "ansi-styles": "^4.1.0", + "supports-color": "^7.1.0" + }, + "engines": { + "node": ">=10" + }, + "funding": { + "url": "https://github.com/chalk/chalk?sponsor=1" + } + }, + "node_modules/inquirer/node_modules/ora": { + "version": "5.4.1", + "resolved": "https://registry.npmjs.org/ora/-/ora-5.4.1.tgz", + "integrity": "sha512-5b6Y85tPxZZ7QytO+BQzysW31HJku27cRIlkbAXaNx+BdcVi+LlRFmVXzeF6a7JCwJpyw5c4b+YSVImQIrBpuQ==", + "dev": true, + "dependencies": { + "bl": "^4.1.0", + "chalk": "^4.1.0", + "cli-cursor": "^3.1.0", + "cli-spinners": "^2.5.0", + "is-interactive": "^1.0.0", + "is-unicode-supported": "^0.1.0", + "log-symbols": "^4.1.0", + "strip-ansi": "^6.0.0", + "wcwidth": "^1.0.1" + }, + "engines": { + "node": ">=10" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/inquirer/node_modules/ora/node_modules/chalk": { + "version": "4.1.2", + "resolved": "https://registry.npmjs.org/chalk/-/chalk-4.1.2.tgz", + "integrity": "sha512-oKnbhFyRIXpUuez8iBMmyEa4nbj4IOQyuhc/wy9kY7/WVPcwIO9VA668Pu8RkO7+0G76SLROeyw9CpQ061i4mA==", + "dev": true, + "dependencies": { + "ansi-styles": "^4.1.0", + "supports-color": "^7.1.0" + }, + "engines": { + "node": ">=10" + }, + "funding": { + "url": "https://github.com/chalk/chalk?sponsor=1" + } + }, + "node_modules/inquirer/node_modules/supports-color": { + "version": "7.2.0", + "resolved": "https://registry.npmjs.org/supports-color/-/supports-color-7.2.0.tgz", + "integrity": "sha512-qpCAvRl9stuOHveKsn7HncJRvv501qIacKzQlO/+Lwxc9+0q2wLyv4Dfvt80/DPn2pqOBsJdDiogXGR9+OvwRw==", + "dev": true, + "dependencies": { + "has-flag": "^4.0.0" + }, + "engines": { + "node": ">=8" + } + }, "node_modules/internal-slot": { "version": "1.0.5", "resolved": "https://registry.npmjs.org/internal-slot/-/internal-slot-1.0.5.tgz", @@ -4344,13 +4421,13 @@ } }, "node_modules/is-array-buffer": { - "version": "3.0.1", - "resolved": "https://registry.npmjs.org/is-array-buffer/-/is-array-buffer-3.0.1.tgz", - "integrity": "sha512-ASfLknmY8Xa2XtB4wmbz13Wu202baeA18cJBCeCy0wXUHZF0IPyVEXqKEcd+t2fNSLLL1vC6k7lxZEojNbISXQ==", + "version": "3.0.2", + "resolved": "https://registry.npmjs.org/is-array-buffer/-/is-array-buffer-3.0.2.tgz", + "integrity": "sha512-y+FyyR/w8vfIRq4eQcM1EYgSTnmHXPqaF+IgzgraytCFq5Xh8lllDVmAZolPJiZttZLeFSINPYMaEJ7/vWUa1w==", "dev": true, "dependencies": { "call-bind": "^1.0.2", - "get-intrinsic": "^1.1.3", + "get-intrinsic": "^1.2.0", "is-typed-array": "^1.1.10" }, "funding": { @@ -4428,9 +4505,9 @@ } }, "node_modules/is-core-module": { - "version": "2.11.0", - "resolved": "https://registry.npmjs.org/is-core-module/-/is-core-module-2.11.0.tgz", - "integrity": "sha512-RRjxlvLDkD1YJwDbroBHMb+cukurkDWNyHx7D3oNB5x9rb5ogcksMC5wHCadcXoo67gVr/+3GFySh3134zi6rw==", + "version": "2.13.0", + "resolved": "https://registry.npmjs.org/is-core-module/-/is-core-module-2.13.0.tgz", + "integrity": "sha512-Z7dk6Qo8pOCp3l4tsX2C5ZVas4V+UxwQodwZhLopL91TX8UyyHEXafPcyoeeWuLrwzHcr3igO78wNLwHJHsMCQ==", "dev": true, "dependencies": { "has": "^1.0.3" @@ -4727,16 +4804,12 @@ } }, "node_modules/is-typed-array": { - "version": "1.1.10", - "resolved": "https://registry.npmjs.org/is-typed-array/-/is-typed-array-1.1.10.tgz", - "integrity": "sha512-PJqgEHiWZvMpaFZ3uTc8kHPM4+4ADTlDniuQL7cU/UDA0Ql7F70yGfHph3cLNe+c9toaigv+DFzTJKhc2CtO6A==", + "version": "1.1.12", + "resolved": "https://registry.npmjs.org/is-typed-array/-/is-typed-array-1.1.12.tgz", + "integrity": "sha512-Z14TF2JNG8Lss5/HMqt0//T9JeHXttXy5pH/DBU4vi98ozO2btxzq9MwYDZYnKwU8nRsz/+GVFVRDq3DkVuSPg==", "dev": true, "dependencies": { - "available-typed-arrays": "^1.0.5", - "call-bind": "^1.0.2", - "for-each": "^0.3.3", - "gopd": "^1.0.1", - "has-tostringtag": "^1.0.0" + "which-typed-array": "^1.1.11" }, "engines": { "node": ">= 0.4" @@ -4902,50 +4975,113 @@ } }, "node_modules/istanbul-lib-report": { - "version": "3.0.0", - "resolved": "https://registry.npmjs.org/istanbul-lib-report/-/istanbul-lib-report-3.0.0.tgz", - "integrity": "sha512-wcdi+uAKzfiGT2abPpKZ0hSU1rGQjUQnLvtY5MpQ7QCTahD3VODhcu4wcfY1YtkGaDD5yuydOLINXsfbus9ROw==", + "version": "3.0.1", + "resolved": "https://registry.npmjs.org/istanbul-lib-report/-/istanbul-lib-report-3.0.1.tgz", + "integrity": "sha512-GCfE1mtsHGOELCU8e/Z7YWzpmybrx/+dSTfLrvY8qRmaY6zXTKWn6WQIjaAFw069icm6GVMNkgu0NzI4iPZUNw==", "dev": true, "dependencies": { "istanbul-lib-coverage": "^3.0.0", - "make-dir": "^3.0.0", + "make-dir": "^4.0.0", "supports-color": "^7.1.0" }, + "engines": { + "node": ">=10" + } + }, + "node_modules/istanbul-lib-report/node_modules/has-flag": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/has-flag/-/has-flag-4.0.0.tgz", + "integrity": "sha512-EykJT/Q1KjTWctppgIAgfSO0tKVuZUjhgMr17kqTumMl6Afv3EISleU7qZUzoXDFTAHTDC4NOoG/ZxU3EvlMPQ==", + "dev": true, "engines": { "node": ">=8" } }, - "node_modules/istanbul-lib-source-maps": { - "version": "4.0.1", - "resolved": "https://registry.npmjs.org/istanbul-lib-source-maps/-/istanbul-lib-source-maps-4.0.1.tgz", - "integrity": "sha512-n3s8EwkdFIJCG3BPKBYvskgXGoy88ARzvegkitk60NxRdwltLOTaH7CUiMRXvwYorl0Q712iEjcWB+fK/MrWVw==", + "node_modules/istanbul-lib-report/node_modules/lru-cache": { + "version": "6.0.0", + "resolved": "https://registry.npmjs.org/lru-cache/-/lru-cache-6.0.0.tgz", + "integrity": "sha512-Jo6dJ04CmSjuznwJSS3pUeWmd/H0ffTlkXXgwZi+eq1UCmqQwCh+eLsYOYCwY991i2Fah4h1BEMCx4qThGbsiA==", "dev": true, "dependencies": { - "debug": "^4.1.1", - "istanbul-lib-coverage": "^3.0.0", - "source-map": "^0.6.1" + "yallist": "^4.0.0" }, "engines": { "node": ">=10" } }, - "node_modules/istanbul-reports": { - "version": "3.1.5", - "resolved": "https://registry.npmjs.org/istanbul-reports/-/istanbul-reports-3.1.5.tgz", - "integrity": "sha512-nUsEMa9pBt/NOHqbcbeJEgqIlY/K7rVWUX6Lql2orY5e9roQOthbR3vtY4zzf2orPELg80fnxxk9zUyPlgwD1w==", + "node_modules/istanbul-lib-report/node_modules/make-dir": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/make-dir/-/make-dir-4.0.0.tgz", + "integrity": "sha512-hXdUTZYIVOt1Ex//jAQi+wTZZpUpwBj/0QsOzqegb3rGMMeJiSEu5xLHnYfBrRV4RH2+OCSOO95Is/7x1WJ4bw==", "dev": true, "dependencies": { - "html-escaper": "^2.0.0", - "istanbul-lib-report": "^3.0.0" + "semver": "^7.5.3" }, "engines": { - "node": ">=8" + "node": ">=10" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" } }, - "node_modules/iterate-iterator": { - "version": "1.0.2", - "resolved": "https://registry.npmjs.org/iterate-iterator/-/iterate-iterator-1.0.2.tgz", - "integrity": "sha512-t91HubM4ZDQ70M9wqp+pcNpu8OyJ9UAtXntT/Bcsvp5tZMnz9vRa+IunKXeI8AnfZMTv0jNuVEmGeLSMjVvfPw==", + "node_modules/istanbul-lib-report/node_modules/semver": { + "version": "7.5.4", + "resolved": "https://registry.npmjs.org/semver/-/semver-7.5.4.tgz", + "integrity": "sha512-1bCSESV6Pv+i21Hvpxp3Dx+pSD8lIPt8uVjRrxAUt/nbswYc+tK6Y2btiULjd4+fnq15PX+nqQDC7Oft7WkwcA==", + "dev": true, + "dependencies": { + "lru-cache": "^6.0.0" + }, + "bin": { + "semver": "bin/semver.js" + }, + "engines": { + "node": ">=10" + } + }, + "node_modules/istanbul-lib-report/node_modules/supports-color": { + "version": "7.2.0", + "resolved": "https://registry.npmjs.org/supports-color/-/supports-color-7.2.0.tgz", + "integrity": "sha512-qpCAvRl9stuOHveKsn7HncJRvv501qIacKzQlO/+Lwxc9+0q2wLyv4Dfvt80/DPn2pqOBsJdDiogXGR9+OvwRw==", + "dev": true, + "dependencies": { + "has-flag": "^4.0.0" + }, + "engines": { + "node": ">=8" + } + }, + "node_modules/istanbul-lib-source-maps": { + "version": "4.0.1", + "resolved": "https://registry.npmjs.org/istanbul-lib-source-maps/-/istanbul-lib-source-maps-4.0.1.tgz", + "integrity": "sha512-n3s8EwkdFIJCG3BPKBYvskgXGoy88ARzvegkitk60NxRdwltLOTaH7CUiMRXvwYorl0Q712iEjcWB+fK/MrWVw==", + "dev": true, + "dependencies": { + "debug": "^4.1.1", + "istanbul-lib-coverage": "^3.0.0", + "source-map": "^0.6.1" + }, + "engines": { + "node": ">=10" + } + }, + "node_modules/istanbul-reports": { + "version": "3.1.6", + "resolved": "https://registry.npmjs.org/istanbul-reports/-/istanbul-reports-3.1.6.tgz", + "integrity": "sha512-TLgnMkKg3iTDsQ9PbPTdpfAK2DzjF9mqUG7RMgcQl8oFjad8ob4laGxv5XV5U9MAfx8D6tSJiUyuAwzLicaxlg==", + "dev": true, + "dependencies": { + "html-escaper": "^2.0.0", + "istanbul-lib-report": "^3.0.0" + }, + "engines": { + "node": ">=8" + } + }, + "node_modules/iterate-iterator": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/iterate-iterator/-/iterate-iterator-1.0.2.tgz", + "integrity": "sha512-t91HubM4ZDQ70M9wqp+pcNpu8OyJ9UAtXntT/Bcsvp5tZMnz9vRa+IunKXeI8AnfZMTv0jNuVEmGeLSMjVvfPw==", "dev": true, "funding": { "url": "https://github.com/sponsors/ljharb" @@ -5037,10 +5173,13 @@ "dev": true }, "node_modules/jsonfile": { - "version": "4.0.0", - "resolved": "https://registry.npmjs.org/jsonfile/-/jsonfile-4.0.0.tgz", - "integrity": "sha512-m6F1R3z8jjlf2imQHS2Qez5sjKWQzbuuhuJ/FKYFRZvPE3PuHcSMVZzfsLhGVOkfd20obL5SWEBew5ShlquNxg==", + "version": "6.1.0", + "resolved": "https://registry.npmjs.org/jsonfile/-/jsonfile-6.1.0.tgz", + "integrity": "sha512-5dgndWOriYSm5cnYaJNhalLNDKOqFwyDB/rr1E9ZsGciGvKPs8R2xYGCacuf3z6K1YKDz182fd+fY3cn3pMqXQ==", "dev": true, + "dependencies": { + "universalify": "^2.0.0" + }, "optionalDependencies": { "graceful-fs": "^4.1.6" } @@ -5052,9 +5191,9 @@ "dev": true }, "node_modules/keyv": { - "version": "4.5.2", - "resolved": "https://registry.npmjs.org/keyv/-/keyv-4.5.2.tgz", - "integrity": "sha512-5MHbFaKn8cNSmVW7BYnijeAVlE4cYA/SVkifVgrh7yotnfhKmjuXpDKjrABLnT0SfHWV21P8ow07OGfRrNDg8g==", + "version": "4.5.3", + "resolved": "https://registry.npmjs.org/keyv/-/keyv-4.5.3.tgz", + "integrity": "sha512-QCiSav9WaX1PgETJ+SpNnx2PRRapJ/oRSXM4VO5OGYGSjrxbKPVFVhB3l2OCbLCk329N8qyAtsJjSjvVBWzEug==", "dev": true, "dependencies": { "json-buffer": "3.0.1" @@ -5189,12 +5328,12 @@ } }, "node_modules/lru-cache": { - "version": "5.1.1", - "resolved": "https://registry.npmjs.org/lru-cache/-/lru-cache-5.1.1.tgz", - "integrity": "sha512-KpNARQA3Iwv+jTA0utUVVbrh+Jlrr1Fv0e56GGzAFOXN7dk/FviaDW8LHmK52DlcH4WP2n6gI8vN1aesBFgo9w==", + "version": "7.18.3", + "resolved": "https://registry.npmjs.org/lru-cache/-/lru-cache-7.18.3.tgz", + "integrity": "sha512-jumlc0BIUrS3qJGgIkWZsyfAM7NCWiBcCDhnd+3NNM5KbBmLTgHVfWBcg6W+rLUsIpzpERPsvwUP7CckAQSOoA==", "dev": true, - "dependencies": { - "yallist": "^3.0.2" + "engines": { + "node": ">=12" } }, "node_modules/lunr": { @@ -5204,9 +5343,9 @@ "dev": true }, "node_modules/macos-release": { - "version": "3.1.0", - "resolved": "https://registry.npmjs.org/macos-release/-/macos-release-3.1.0.tgz", - "integrity": "sha512-/M/R0gCDgM+Cv1IuBG1XGdfTFnMEG6PZeT+KGWHO/OG+imqmaD9CH5vHBTycEM3+Kc4uG2Il+tFAuUWLqQOeUA==", + "version": "3.2.0", + "resolved": "https://registry.npmjs.org/macos-release/-/macos-release-3.2.0.tgz", + "integrity": "sha512-fSErXALFNsnowREYZ49XCdOHF8wOPWuFOGQrAhP7x5J/BqQv+B02cNsTykGpDgRVx43EKg++6ANmTaGTtW+hUA==", "dev": true, "engines": { "node": "^12.20.0 || ^14.13.1 || >=16.0.0" @@ -5382,30 +5521,6 @@ "url": "https://opencollective.com/mochajs" } }, - "node_modules/mocha/node_modules/ansi-regex": { - "version": "5.0.1", - "resolved": "https://registry.npmjs.org/ansi-regex/-/ansi-regex-5.0.1.tgz", - "integrity": "sha512-quJQXlTSUGL2LH9SUXo8VwsY4soanhgo6LNSm84E1LBcE8s3O0wpdiRzyR9z/ZZJMlMWv37qOOb9pdJlMUEKFQ==", - "dev": true, - "engines": { - "node": ">=8" - } - }, - "node_modules/mocha/node_modules/ansi-styles": { - "version": "4.3.0", - "resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-4.3.0.tgz", - "integrity": "sha512-zbB9rCJAT1rbjiVDb2hqKFHNYLxgtk8NURxZ3IZwD3F6NtxbXZQCnnSi1Lkx+IDohdPlFp222wVALIheZJQSEg==", - "dev": true, - "dependencies": { - "color-convert": "^2.0.1" - }, - "engines": { - "node": ">=8" - }, - "funding": { - "url": "https://github.com/chalk/ansi-styles?sponsor=1" - } - }, "node_modules/mocha/node_modules/chalk": { "version": "4.1.2", "resolved": "https://registry.npmjs.org/chalk/-/chalk-4.1.2.tgz", @@ -5445,12 +5560,6 @@ "wrap-ansi": "^7.0.0" } }, - "node_modules/mocha/node_modules/emoji-regex": { - "version": "8.0.0", - "resolved": "https://registry.npmjs.org/emoji-regex/-/emoji-regex-8.0.0.tgz", - "integrity": "sha512-MSjYzcWNOA0ewAHpz0MxpYFvwg6yjy1NG3xteoqz644VCo/RPgnr1/GGt+ic3iJTzQ8Eu3TdM14SawnVUmGE6A==", - "dev": true - }, "node_modules/mocha/node_modules/escape-string-regexp": { "version": "4.0.0", "resolved": "https://registry.npmjs.org/escape-string-regexp/-/escape-string-regexp-4.0.0.tgz", @@ -5511,6 +5620,15 @@ "node": "*" } }, + "node_modules/mocha/node_modules/has-flag": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/has-flag/-/has-flag-4.0.0.tgz", + "integrity": "sha512-EykJT/Q1KjTWctppgIAgfSO0tKVuZUjhgMr17kqTumMl6Afv3EISleU7qZUzoXDFTAHTDC4NOoG/ZxU3EvlMPQ==", + "dev": true, + "engines": { + "node": ">=8" + } + }, "node_modules/mocha/node_modules/is-unicode-supported": { "version": "0.1.0", "resolved": "https://registry.npmjs.org/is-unicode-supported/-/is-unicode-supported-0.1.0.tgz", @@ -5611,30 +5729,16 @@ "url": "https://github.com/sponsors/sindresorhus" } }, - "node_modules/mocha/node_modules/string-width": { - "version": "4.2.3", - "resolved": "https://registry.npmjs.org/string-width/-/string-width-4.2.3.tgz", - "integrity": "sha512-wKyQRQpjJ0sIp62ErSZdGsjMJWsap5oRNihHhu6G7JVO/9jIB6UyevL+tXuOqrng8j/cxKTWyWUwvSTriiZz/g==", + "node_modules/mocha/node_modules/strip-json-comments": { + "version": "3.1.1", + "resolved": "https://registry.npmjs.org/strip-json-comments/-/strip-json-comments-3.1.1.tgz", + "integrity": "sha512-6fPc+R4ihwqP6N/aIv2f1gMH8lOVtWQHoqC4yK6oSDVVocumAsfCqjkXnqiYMhmMwS/mEHLp7Vehlt3ql6lEig==", "dev": true, - "dependencies": { - "emoji-regex": "^8.0.0", - "is-fullwidth-code-point": "^3.0.0", - "strip-ansi": "^6.0.1" - }, "engines": { "node": ">=8" - } - }, - "node_modules/mocha/node_modules/strip-ansi": { - "version": "6.0.1", - "resolved": "https://registry.npmjs.org/strip-ansi/-/strip-ansi-6.0.1.tgz", - "integrity": "sha512-Y38VPSHcqkFrCpFnQ9vuSXmquuv5oXOKpGeT6aGrr3o3Gc9AlVa6JBfUSOCnbxGGZF+/0ooI7KrPuUSztUdU5A==", - "dev": true, - "dependencies": { - "ansi-regex": "^5.0.1" }, - "engines": { - "node": ">=8" + "funding": { + "url": "https://github.com/sponsors/sindresorhus" } }, "node_modules/mocha/node_modules/supports-color": { @@ -5669,15 +5773,6 @@ "url": "https://github.com/chalk/wrap-ansi?sponsor=1" } }, - "node_modules/mocha/node_modules/y18n": { - "version": "5.0.8", - "resolved": "https://registry.npmjs.org/y18n/-/y18n-5.0.8.tgz", - "integrity": "sha512-0pfFzegeDWJHJIAmTLRP2DwHjdF5s7jo9tuztdQxAhINCdvS+3nGINqPd00AphqJR/0LhANUS6/+7SCb98YOfA==", - "dev": true, - "engines": { - "node": ">=10" - } - }, "node_modules/mocha/node_modules/yargs": { "version": "16.2.0", "resolved": "https://registry.npmjs.org/yargs/-/yargs-16.2.0.tgz", @@ -5738,12 +5833,6 @@ "integrity": "sha512-OWND8ei3VtNC9h7V60qff3SVobHr996CTwgxubgyQYEpg290h9J0buyECNNJexkFm5sOajh5G116RYA1c8ZMSw==", "dev": true }, - "node_modules/natural-compare-lite": { - "version": "1.4.0", - "resolved": "https://registry.npmjs.org/natural-compare-lite/-/natural-compare-lite-1.4.0.tgz", - "integrity": "sha512-Tj+HTDSJJKaZnfiuw+iaF9skdPpTo2GtEly5JHnWV/hfv2Qj/9RKsGISQtLh2ox3l5EAGw487hnBee0sIJ6v2g==", - "dev": true - }, "node_modules/netmask": { "version": "2.0.2", "resolved": "https://registry.npmjs.org/netmask/-/netmask-2.0.2.tgz", @@ -5793,6 +5882,15 @@ "path-to-regexp": "^1.7.0" } }, + "node_modules/nise/node_modules/@sinonjs/commons": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/@sinonjs/commons/-/commons-2.0.0.tgz", + "integrity": "sha512-uLa0j859mMrg2slwQYdO/AkrOfmH+X6LTVmNTS9CqexuE2IvVORIkSpJLqePAbEnKJ77aMmCwr1NUZ57120Xcg==", + "dev": true, + "dependencies": { + "type-detect": "4.0.8" + } + }, "node_modules/node-domexception": { "version": "1.0.0", "resolved": "https://registry.npmjs.org/node-domexception/-/node-domexception-1.0.0.tgz", @@ -5813,9 +5911,9 @@ } }, "node_modules/node-fetch": { - "version": "3.3.1", - "resolved": "https://registry.npmjs.org/node-fetch/-/node-fetch-3.3.1.tgz", - "integrity": "sha512-cRVc/kyto/7E5shrWca1Wsea4y6tL9iYJE5FBCius3JQfb/4P4I295PfhgbJQBLTx6lATE4z+wK0rPM4VS2uow==", + "version": "3.3.2", + "resolved": "https://registry.npmjs.org/node-fetch/-/node-fetch-3.3.2.tgz", + "integrity": "sha512-dRB78srN/l6gqWulah9SrxeYnxeddIG30+GOqK/9OlLVyLg3HPnr6SqOWTWOXKRwC2eGYCkZ59NNuSgvSrpgOA==", "dev": true, "dependencies": { "data-uri-to-buffer": "^4.0.0", @@ -5843,9 +5941,9 @@ } }, "node_modules/node-releases": { - "version": "2.0.10", - "resolved": "https://registry.npmjs.org/node-releases/-/node-releases-2.0.10.tgz", - "integrity": "sha512-5GFldHPXVG/YZmFzJvKK2zDSzPKhEp0+ZR5SVaoSag9fsL5YgHbUHDfnG5494ISANDcK4KwPXAx2xqVEydmd7w==", + "version": "2.0.13", + "resolved": "https://registry.npmjs.org/node-releases/-/node-releases-2.0.13.tgz", + "integrity": "sha512-uYr7J37ae/ORWdZeQ1xxMJe3NtdmqMC/JZK+geofDrkLUApKRHPd18/TxtBOJ4A0/+uUIliorNrfYV6s1b02eQ==", "dev": true }, "node_modules/normalize-path": { @@ -5937,6 +6035,26 @@ "node": ">=8.9" } }, + "node_modules/nyc/node_modules/camelcase": { + "version": "5.3.1", + "resolved": "https://registry.npmjs.org/camelcase/-/camelcase-5.3.1.tgz", + "integrity": "sha512-L28STB170nwWS63UjtlEOE3dldQApaJXZkOI1uMFfzf3rRuPegHaHesyee+YxQ+W6SvRDQV6UrdOdRiR153wJg==", + "dev": true, + "engines": { + "node": ">=6" + } + }, + "node_modules/nyc/node_modules/cliui": { + "version": "6.0.0", + "resolved": "https://registry.npmjs.org/cliui/-/cliui-6.0.0.tgz", + "integrity": "sha512-t6wbgtoCXvAzst7QgXxJYqPt0usEfbgQdftEPbLL/cvv6HPE5VgvqCuAIDR0NgU52ds6rFwqrgakNLrHEjCbrQ==", + "dev": true, + "dependencies": { + "string-width": "^4.2.0", + "strip-ansi": "^6.0.0", + "wrap-ansi": "^6.2.0" + } + }, "node_modules/nyc/node_modules/resolve-from": { "version": "5.0.0", "resolved": "https://registry.npmjs.org/resolve-from/-/resolve-from-5.0.0.tgz", @@ -5946,6 +6064,47 @@ "node": ">=8" } }, + "node_modules/nyc/node_modules/y18n": { + "version": "4.0.3", + "resolved": "https://registry.npmjs.org/y18n/-/y18n-4.0.3.tgz", + "integrity": "sha512-JKhqTOwSrqNA1NY5lSztJ1GrBiUodLMmIZuLiDaMRJ+itFd+ABVE8XBjOvIWL+rSqNDC74LCSFmlb/U4UZ4hJQ==", + "dev": true + }, + "node_modules/nyc/node_modules/yargs": { + "version": "15.4.1", + "resolved": "https://registry.npmjs.org/yargs/-/yargs-15.4.1.tgz", + "integrity": "sha512-aePbxDmcYW++PaqBsJ+HYUFwCdv4LVvdnhBy78E57PIor8/OVvhMrADFFEDh8DHDFRv/O9i3lPhsENjO7QX0+A==", + "dev": true, + "dependencies": { + "cliui": "^6.0.0", + "decamelize": "^1.2.0", + "find-up": "^4.1.0", + "get-caller-file": "^2.0.1", + "require-directory": "^2.1.1", + "require-main-filename": "^2.0.0", + "set-blocking": "^2.0.0", + "string-width": "^4.2.0", + "which-module": "^2.0.0", + "y18n": "^4.0.0", + "yargs-parser": "^18.1.2" + }, + "engines": { + "node": ">=8" + } + }, + "node_modules/nyc/node_modules/yargs-parser": { + "version": "18.1.3", + "resolved": "https://registry.npmjs.org/yargs-parser/-/yargs-parser-18.1.3.tgz", + "integrity": "sha512-o50j0JeToy/4K6OZcaQmW6lyXXKhq7csREXcDwk2omFPJEwUNOVtJKvmDr9EI1fAJZUyZcRF7kxGBWmRXudrCQ==", + "dev": true, + "dependencies": { + "camelcase": "^5.0.0", + "decamelize": "^1.2.0" + }, + "engines": { + "node": ">=6" + } + }, "node_modules/object-assign": { "version": "4.1.1", "resolved": "https://registry.npmjs.org/object-assign/-/object-assign-4.1.1.tgz", @@ -6034,53 +6193,64 @@ } }, "node_modules/optionator": { - "version": "0.9.1", - "resolved": "https://registry.npmjs.org/optionator/-/optionator-0.9.1.tgz", - "integrity": "sha512-74RlY5FCnhq4jRxVUPKDaRwrVNXMqsGsiW6AJw4XK8hmtm10wC0ypZBLw5IIp85NZMr91+qd1RvvENwg7jjRFw==", + "version": "0.9.3", + "resolved": "https://registry.npmjs.org/optionator/-/optionator-0.9.3.tgz", + "integrity": "sha512-JjCoypp+jKn1ttEFExxhetCKeJt9zhAgAve5FXHixTvFDW/5aEktX9bufBKLRRMdU7bNtpLfcGu94B3cdEJgjg==", "dev": true, "dependencies": { + "@aashutoshrathi/word-wrap": "^1.2.3", "deep-is": "^0.1.3", "fast-levenshtein": "^2.0.6", "levn": "^0.4.1", "prelude-ls": "^1.2.1", - "type-check": "^0.4.0", - "word-wrap": "^1.2.3" + "type-check": "^0.4.0" }, "engines": { "node": ">= 0.8.0" } }, "node_modules/ora": { - "version": "6.3.0", - "resolved": "https://registry.npmjs.org/ora/-/ora-6.3.0.tgz", - "integrity": "sha512-1/D8uRFY0ay2kgBpmAwmSA404w4OoPVhHMqRqtjvrcK/dnzcEZxMJ+V4DUbyICu8IIVRclHcOf5wlD1tMY4GUQ==", + "version": "7.0.1", + "resolved": "https://registry.npmjs.org/ora/-/ora-7.0.1.tgz", + "integrity": "sha512-0TUxTiFJWv+JnjWm4o9yvuskpEJLXTcng8MJuKd+SzAzp2o+OP3HWqNhB4OdJRt1Vsd9/mR0oyaEYlOnL7XIRw==", "dev": true, "dependencies": { - "chalk": "^5.0.0", + "chalk": "^5.3.0", "cli-cursor": "^4.0.0", - "cli-spinners": "^2.6.1", + "cli-spinners": "^2.9.0", "is-interactive": "^2.0.0", - "is-unicode-supported": "^1.1.0", + "is-unicode-supported": "^1.3.0", "log-symbols": "^5.1.0", "stdin-discarder": "^0.1.0", - "strip-ansi": "^7.0.1", - "wcwidth": "^1.0.1" + "string-width": "^6.1.0", + "strip-ansi": "^7.1.0" }, "engines": { - "node": "^12.20.0 || ^14.13.1 || >=16.0.0" + "node": ">=16" }, "funding": { "url": "https://github.com/sponsors/sindresorhus" } }, - "node_modules/os-name": { - "version": "5.1.0", - "resolved": "https://registry.npmjs.org/os-name/-/os-name-5.1.0.tgz", - "integrity": "sha512-YEIoAnM6zFmzw3PQ201gCVCIWbXNyKObGlVvpAVvraAeOHnlYVKFssbA/riRX5R40WA6kKrZ7Dr7dWzO3nKSeQ==", + "node_modules/ora/node_modules/ansi-regex": { + "version": "6.0.1", + "resolved": "https://registry.npmjs.org/ansi-regex/-/ansi-regex-6.0.1.tgz", + "integrity": "sha512-n5M855fKb2SsfMIiFFoVrABHJC8QtHwVx+mHWP3QcEqBHYienj5dHSgjbxtC0WEZXYt4wcD6zrQElDPhFuZgfA==", + "dev": true, + "engines": { + "node": ">=12" + }, + "funding": { + "url": "https://github.com/chalk/ansi-regex?sponsor=1" + } + }, + "node_modules/ora/node_modules/cli-cursor": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/cli-cursor/-/cli-cursor-4.0.0.tgz", + "integrity": "sha512-VGtlMu3x/4DOtIUwEkRezxUZ2lBacNJCHash0N0WeZDBS+7Ux1dm3XWAgWYxLJFMMdOeXMHXorshEFhbMSGelg==", "dev": true, "dependencies": { - "macos-release": "^3.1.0", - "windows-release": "^5.0.1" + "restore-cursor": "^4.0.0" }, "engines": { "node": "^12.20.0 || ^14.13.1 || >=16.0.0" @@ -6089,31 +6259,28 @@ "url": "https://github.com/sponsors/sindresorhus" } }, - "node_modules/os-tmpdir": { - "version": "1.0.2", - "resolved": "https://registry.npmjs.org/os-tmpdir/-/os-tmpdir-1.0.2.tgz", - "integrity": "sha512-D2FR03Vir7FIu45XBY20mTb+/ZSWB00sjU9jdQXt83gDrI4Ztz5Fs7/yy74g2N5SVQY4xY1qDr4rNddwYRVX0g==", - "dev": true, - "engines": { - "node": ">=0.10.0" - } + "node_modules/ora/node_modules/emoji-regex": { + "version": "10.2.1", + "resolved": "https://registry.npmjs.org/emoji-regex/-/emoji-regex-10.2.1.tgz", + "integrity": "sha512-97g6QgOk8zlDRdgq1WxwgTMgEWGVAQvB5Fdpgc1MkNy56la5SKP9GsMXKDOdqwn90/41a8yPwIGk1Y6WVbeMQA==", + "dev": true }, - "node_modules/p-cancelable": { - "version": "3.0.0", - "resolved": "https://registry.npmjs.org/p-cancelable/-/p-cancelable-3.0.0.tgz", - "integrity": "sha512-mlVgR3PGuzlo0MmTdk4cXqXWlwQDLnONTAg6sm62XkMJEiRxN3GL3SffkYvqwonbkJBcrI7Uvv5Zh9yjvn2iUw==", + "node_modules/ora/node_modules/mimic-fn": { + "version": "2.1.0", + "resolved": "https://registry.npmjs.org/mimic-fn/-/mimic-fn-2.1.0.tgz", + "integrity": "sha512-OqbOk5oEQeAZ8WXWydlu9HJjz9WVdEIvamMCcXmuqUYjTknH/sqsWvhQ3vgwKFRR1HpjvNBKQ37nbJgYzGqGcg==", "dev": true, "engines": { - "node": ">=12.20" + "node": ">=6" } }, - "node_modules/p-limit": { - "version": "2.3.0", - "resolved": "https://registry.npmjs.org/p-limit/-/p-limit-2.3.0.tgz", - "integrity": "sha512-//88mFWSJx8lxCzwdAABTJL2MyWB12+eIY7MDL2SqLmAkeKU9qxRvWuSyTjm3FUmpBEMuFfckAIqEaVGUDxb6w==", + "node_modules/ora/node_modules/onetime": { + "version": "5.1.2", + "resolved": "https://registry.npmjs.org/onetime/-/onetime-5.1.2.tgz", + "integrity": "sha512-kbpaSSGJTWdAY5KPVeMOKXSrPtr8C8C7wodJbcsd51jRnmD+GZu8Y0VoU6Dm5Z4vWr0Ig/1NKuWRKf7j5aaYSg==", "dev": true, "dependencies": { - "p-try": "^2.0.0" + "mimic-fn": "^2.1.0" }, "engines": { "node": ">=6" @@ -6122,9 +6289,106 @@ "url": "https://github.com/sponsors/sindresorhus" } }, - "node_modules/p-locate": { - "version": "4.1.0", - "resolved": "https://registry.npmjs.org/p-locate/-/p-locate-4.1.0.tgz", + "node_modules/ora/node_modules/restore-cursor": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/restore-cursor/-/restore-cursor-4.0.0.tgz", + "integrity": "sha512-I9fPXU9geO9bHOt9pHHOhOkYerIMsmVaWB0rA2AI9ERh/+x/i7MV5HKBNrg+ljO5eoPVgCcnFuRjJ9uH6I/3eg==", + "dev": true, + "dependencies": { + "onetime": "^5.1.0", + "signal-exit": "^3.0.2" + }, + "engines": { + "node": "^12.20.0 || ^14.13.1 || >=16.0.0" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/ora/node_modules/string-width": { + "version": "6.1.0", + "resolved": "https://registry.npmjs.org/string-width/-/string-width-6.1.0.tgz", + "integrity": "sha512-k01swCJAgQmuADB0YIc+7TuatfNvTBVOoaUWJjTB9R4VJzR5vNWzf5t42ESVZFPS8xTySF7CAdV4t/aaIm3UnQ==", + "dev": true, + "dependencies": { + "eastasianwidth": "^0.2.0", + "emoji-regex": "^10.2.1", + "strip-ansi": "^7.0.1" + }, + "engines": { + "node": ">=16" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/ora/node_modules/strip-ansi": { + "version": "7.1.0", + "resolved": "https://registry.npmjs.org/strip-ansi/-/strip-ansi-7.1.0.tgz", + "integrity": "sha512-iq6eVVI64nQQTRYq2KtEg2d2uU7LElhTJwsH4YzIHZshxlgZms/wIc4VoDQTlG/IvVIrBKG06CrZnp0qv7hkcQ==", + "dev": true, + "dependencies": { + "ansi-regex": "^6.0.1" + }, + "engines": { + "node": ">=12" + }, + "funding": { + "url": "https://github.com/chalk/strip-ansi?sponsor=1" + } + }, + "node_modules/os-name": { + "version": "5.1.0", + "resolved": "https://registry.npmjs.org/os-name/-/os-name-5.1.0.tgz", + "integrity": "sha512-YEIoAnM6zFmzw3PQ201gCVCIWbXNyKObGlVvpAVvraAeOHnlYVKFssbA/riRX5R40WA6kKrZ7Dr7dWzO3nKSeQ==", + "dev": true, + "dependencies": { + "macos-release": "^3.1.0", + "windows-release": "^5.0.1" + }, + "engines": { + "node": "^12.20.0 || ^14.13.1 || >=16.0.0" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/os-tmpdir": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/os-tmpdir/-/os-tmpdir-1.0.2.tgz", + "integrity": "sha512-D2FR03Vir7FIu45XBY20mTb+/ZSWB00sjU9jdQXt83gDrI4Ztz5Fs7/yy74g2N5SVQY4xY1qDr4rNddwYRVX0g==", + "dev": true, + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/p-cancelable": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/p-cancelable/-/p-cancelable-3.0.0.tgz", + "integrity": "sha512-mlVgR3PGuzlo0MmTdk4cXqXWlwQDLnONTAg6sm62XkMJEiRxN3GL3SffkYvqwonbkJBcrI7Uvv5Zh9yjvn2iUw==", + "dev": true, + "engines": { + "node": ">=12.20" + } + }, + "node_modules/p-limit": { + "version": "2.3.0", + "resolved": "https://registry.npmjs.org/p-limit/-/p-limit-2.3.0.tgz", + "integrity": "sha512-//88mFWSJx8lxCzwdAABTJL2MyWB12+eIY7MDL2SqLmAkeKU9qxRvWuSyTjm3FUmpBEMuFfckAIqEaVGUDxb6w==", + "dev": true, + "dependencies": { + "p-try": "^2.0.0" + }, + "engines": { + "node": ">=6" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/p-locate": { + "version": "4.1.0", + "resolved": "https://registry.npmjs.org/p-locate/-/p-locate-4.1.0.tgz", "integrity": "sha512-R79ZZ/0wAxKGu3oYMlz8jy/kbhsNrS7SKZ7PxEHBgJ5+F2mtFW2fK2cOtBh1cHYkQsbzFV7I+EoRKe6Yt0oK7A==", "dev": true, "dependencies": { @@ -6156,37 +6420,36 @@ } }, "node_modules/pac-proxy-agent": { - "version": "5.0.0", - "resolved": "https://registry.npmjs.org/pac-proxy-agent/-/pac-proxy-agent-5.0.0.tgz", - "integrity": "sha512-CcFG3ZtnxO8McDigozwE3AqAw15zDvGH+OjXO4kzf7IkEKkQ4gxQ+3sdF50WmhQ4P/bVusXcqNE2S3XrNURwzQ==", + "version": "7.0.0", + "resolved": "https://registry.npmjs.org/pac-proxy-agent/-/pac-proxy-agent-7.0.0.tgz", + "integrity": "sha512-t4tRAMx0uphnZrio0S0Jw9zg3oDbz1zVhQ/Vy18FjLfP1XOLNUEjaVxYCYRI6NS+BsMBXKIzV6cTLOkO9AtywA==", "dev": true, "dependencies": { - "@tootallnate/once": "1", - "agent-base": "6", - "debug": "4", - "get-uri": "3", - "http-proxy-agent": "^4.0.1", - "https-proxy-agent": "5", - "pac-resolver": "^5.0.0", - "raw-body": "^2.2.0", - "socks-proxy-agent": "5" + "@tootallnate/quickjs-emscripten": "^0.23.0", + "agent-base": "^7.0.2", + "debug": "^4.3.4", + "get-uri": "^6.0.1", + "http-proxy-agent": "^7.0.0", + "https-proxy-agent": "^7.0.0", + "pac-resolver": "^7.0.0", + "socks-proxy-agent": "^8.0.1" }, "engines": { - "node": ">= 8" + "node": ">= 14" } }, "node_modules/pac-resolver": { - "version": "5.0.1", - "resolved": "https://registry.npmjs.org/pac-resolver/-/pac-resolver-5.0.1.tgz", - "integrity": "sha512-cy7u00ko2KVgBAjuhevqpPeHIkCIqPe1v24cydhWjmeuzaBfmUWFCZJ1iAh5TuVzVZoUzXIW7K8sMYOZ84uZ9Q==", + "version": "7.0.0", + "resolved": "https://registry.npmjs.org/pac-resolver/-/pac-resolver-7.0.0.tgz", + "integrity": "sha512-Fd9lT9vJbHYRACT8OhCbZBbxr6KRSawSovFpy8nDGshaK99S/EBhVIHp9+crhxrsZOuvLpgL1n23iyPg6Rl2hg==", "dev": true, "dependencies": { - "degenerator": "^3.0.2", - "ip": "^1.1.5", + "degenerator": "^5.0.0", + "ip": "^1.1.8", "netmask": "^2.0.2" }, "engines": { - "node": ">= 8" + "node": ">= 14" } }, "node_modules/package-hash": { @@ -6205,9 +6468,9 @@ } }, "node_modules/package-json": { - "version": "8.1.0", - "resolved": "https://registry.npmjs.org/package-json/-/package-json-8.1.0.tgz", - "integrity": "sha512-hySwcV8RAWeAfPsXb9/HGSPn8lwDnv6fabH+obUZKX169QknRkRhPxd1yMubpKDskLFATkl3jHpNtVtDPFA0Wg==", + "version": "8.1.1", + "resolved": "https://registry.npmjs.org/package-json/-/package-json-8.1.1.tgz", + "integrity": "sha512-cbH9IAIJHNj9uXi196JVsRlt7cHKak6u/e6AkL/bkRelZ7rlL3X1YKxsZwa36xipOEKAsdtmaG6aAJoM1fx2zA==", "dev": true, "dependencies": { "got": "^12.1.0", @@ -6222,6 +6485,31 @@ "url": "https://github.com/sponsors/sindresorhus" } }, + "node_modules/package-json/node_modules/got": { + "version": "12.6.1", + "resolved": "https://registry.npmjs.org/got/-/got-12.6.1.tgz", + "integrity": "sha512-mThBblvlAF1d4O5oqyvN+ZxLAYwIJK7bpMxgYqPD9okW0C3qm5FFn7k811QrcuEBwaogR3ngOFoCfs6mRv7teQ==", + "dev": true, + "dependencies": { + "@sindresorhus/is": "^5.2.0", + "@szmarczak/http-timer": "^5.0.1", + "cacheable-lookup": "^7.0.0", + "cacheable-request": "^10.2.8", + "decompress-response": "^6.0.0", + "form-data-encoder": "^2.1.2", + "get-stream": "^6.0.1", + "http2-wrapper": "^2.1.10", + "lowercase-keys": "^3.0.0", + "p-cancelable": "^3.0.0", + "responselike": "^3.0.0" + }, + "engines": { + "node": ">=14.16" + }, + "funding": { + "url": "https://github.com/sindresorhus/got?sponsor=1" + } + }, "node_modules/package-json/node_modules/lru-cache": { "version": "6.0.0", "resolved": "https://registry.npmjs.org/lru-cache/-/lru-cache-6.0.0.tgz", @@ -6235,9 +6523,9 @@ } }, "node_modules/package-json/node_modules/semver": { - "version": "7.3.8", - "resolved": "https://registry.npmjs.org/semver/-/semver-7.3.8.tgz", - "integrity": "sha512-NB1ctGL5rlHrPJtFDVIVzTyQylMLu9N9VICA6HSFJo8MCGVTMW6gfpicwKmmK/dAjTOrqu5l63JJOpDSrAis3A==", + "version": "7.5.4", + "resolved": "https://registry.npmjs.org/semver/-/semver-7.5.4.tgz", + "integrity": "sha512-1bCSESV6Pv+i21Hvpxp3Dx+pSD8lIPt8uVjRrxAUt/nbswYc+tK6Y2btiULjd4+fnq15PX+nqQDC7Oft7WkwcA==", "dev": true, "dependencies": { "lru-cache": "^6.0.0" @@ -6249,12 +6537,6 @@ "node": ">=10" } }, - "node_modules/package-json/node_modules/yallist": { - "version": "4.0.0", - "resolved": "https://registry.npmjs.org/yallist/-/yallist-4.0.0.tgz", - "integrity": "sha512-3wdGidZyq5PB084XLES5TpOSRA3wjXAlIWMhum2kRcv/41Sn2emQ0dycQW4uZXLejwKvg6EsvbdlVL+FYEct7A==", - "dev": true - }, "node_modules/parent-module": { "version": "1.0.1", "resolved": "https://registry.npmjs.org/parent-module/-/parent-module-1.0.1.tgz", @@ -6474,22 +6756,22 @@ "dev": true }, "node_modules/proxy-agent": { - "version": "5.0.0", - "resolved": "https://registry.npmjs.org/proxy-agent/-/proxy-agent-5.0.0.tgz", - "integrity": "sha512-gkH7BkvLVkSfX9Dk27W6TyNOWWZWRilRfk1XxGNWOYJ2TuedAv1yFpCaU9QSBmBe716XOTNpYNOzhysyw8xn7g==", + "version": "6.3.0", + "resolved": "https://registry.npmjs.org/proxy-agent/-/proxy-agent-6.3.0.tgz", + "integrity": "sha512-0LdR757eTj/JfuU7TL2YCuAZnxWXu3tkJbg4Oq3geW/qFNT/32T0sp2HnZ9O0lMR4q3vwAt0+xCA8SR0WAD0og==", "dev": true, "dependencies": { - "agent-base": "^6.0.0", - "debug": "4", - "http-proxy-agent": "^4.0.0", - "https-proxy-agent": "^5.0.0", - "lru-cache": "^5.1.1", - "pac-proxy-agent": "^5.0.0", - "proxy-from-env": "^1.0.0", - "socks-proxy-agent": "^5.0.0" + "agent-base": "^7.0.2", + "debug": "^4.3.4", + "http-proxy-agent": "^7.0.0", + "https-proxy-agent": "^7.0.0", + "lru-cache": "^7.14.1", + "pac-proxy-agent": "^7.0.0", + "proxy-from-env": "^1.1.0", + "socks-proxy-agent": "^8.0.1" }, "engines": { - "node": ">= 8" + "node": ">= 14" } }, "node_modules/proxy-from-env": { @@ -6563,21 +6845,6 @@ "safe-buffer": "^5.1.0" } }, - "node_modules/raw-body": { - "version": "2.5.2", - "resolved": "https://registry.npmjs.org/raw-body/-/raw-body-2.5.2.tgz", - "integrity": "sha512-8zGqypfENjCIqGhgXToC8aB2r7YrBX+AQAfIPs/Mlk+BtPTztOvTS01NRW/3Eh60J+a48lt8qsCzirQ6loCVfA==", - "dev": true, - "dependencies": { - "bytes": "3.1.2", - "http-errors": "2.0.0", - "iconv-lite": "0.4.24", - "unpipe": "1.0.0" - }, - "engines": { - "node": ">= 0.8" - } - }, "node_modules/rc": { "version": "1.2.8", "resolved": "https://registry.npmjs.org/rc/-/rc-1.2.8.tgz", @@ -6599,15 +6866,6 @@ "integrity": "sha512-JV/yugV2uzW5iMRSiZAyDtQd+nxtUnjeLt0acNdw98kKLrvuRVyB80tsREOE7yvGVgalhZ6RNXCmEHkUKBKxew==", "dev": true }, - "node_modules/rc/node_modules/strip-json-comments": { - "version": "2.0.1", - "resolved": "https://registry.npmjs.org/strip-json-comments/-/strip-json-comments-2.0.1.tgz", - "integrity": "sha512-4gB8na07fecVVkOI6Rs4e7T6NOTki5EmL7TUduTs6bu3EdnSycntVJ4re8kgZA+wx9IueI2Y11bfbgwtzuE0KQ==", - "dev": true, - "engines": { - "node": ">=0.10.0" - } - }, "node_modules/readable-stream": { "version": "3.6.2", "resolved": "https://registry.npmjs.org/readable-stream/-/readable-stream-3.6.2.tgz", @@ -6647,14 +6905,14 @@ } }, "node_modules/regexp.prototype.flags": { - "version": "1.4.3", - "resolved": "https://registry.npmjs.org/regexp.prototype.flags/-/regexp.prototype.flags-1.4.3.tgz", - "integrity": "sha512-fjggEOO3slI6Wvgjwflkc4NFRCTZAu5CnNfBd5qOMYhWdn67nJBBu34/TkD++eeFmd8C9r9jfXJ27+nSiRkSUA==", + "version": "1.5.0", + "resolved": "https://registry.npmjs.org/regexp.prototype.flags/-/regexp.prototype.flags-1.5.0.tgz", + "integrity": "sha512-0SutC3pNudRKgquxGoRGIz946MZVHqbNfPjBdxeOhBrdgDKlRoXmYLQN9xRbrR09ZXWeGAdPuif7egofn6v5LA==", "dev": true, "dependencies": { "call-bind": "^1.0.2", - "define-properties": "^1.1.3", - "functions-have-names": "^1.2.2" + "define-properties": "^1.2.0", + "functions-have-names": "^1.2.3" }, "engines": { "node": ">= 0.4" @@ -6664,12 +6922,12 @@ } }, "node_modules/registry-auth-token": { - "version": "5.0.1", - "resolved": "https://registry.npmjs.org/registry-auth-token/-/registry-auth-token-5.0.1.tgz", - "integrity": "sha512-UfxVOj8seK1yaIOiieV4FIP01vfBDLsY0H9sQzi9EbbUdJiuuBjJgLa1DpImXMNPnVkBD4eVxTEXcrZA6kfpJA==", + "version": "5.0.2", + "resolved": "https://registry.npmjs.org/registry-auth-token/-/registry-auth-token-5.0.2.tgz", + "integrity": "sha512-o/3ikDxtXaA59BmZuZrJZDJv8NMDGSj+6j6XaeBmHw8eY1i1qd9+6H+LjVvQXx3HN6aRCGa1cUdJ9RaJZUugnQ==", "dev": true, "dependencies": { - "@pnpm/npm-conf": "^1.0.4" + "@pnpm/npm-conf": "^2.1.0" }, "engines": { "node": ">=14" @@ -6691,33 +6949,33 @@ } }, "node_modules/release-it": { - "version": "15.10.3", - "resolved": "https://registry.npmjs.org/release-it/-/release-it-15.10.3.tgz", - "integrity": "sha512-OSdHOg76gwkpLbSLBK09GZQj5XWXwBP+S6v//rSoQKkjqklaCLK04Gl5NkTwNrQOHHiihs4ToesDNh2+w55k3w==", + "version": "16.1.5", + "resolved": "https://registry.npmjs.org/release-it/-/release-it-16.1.5.tgz", + "integrity": "sha512-w/zCljPZBSYcCwR9fjDB1zaYwie1CAQganUrwNqjtXacXhrrsS5E6dDUNLcxm2ypu8GWAgZNMJfuBJqIO2E7fA==", "dev": true, "dependencies": { "@iarna/toml": "2.2.5", - "@octokit/rest": "19.0.7", + "@octokit/rest": "19.0.13", "async-retry": "1.3.3", - "chalk": "5.2.0", - "cosmiconfig": "8.1.3", - "execa": "7.1.1", + "chalk": "5.3.0", + "cosmiconfig": "8.2.0", + "execa": "7.2.0", "git-url-parse": "13.1.0", - "globby": "13.1.4", - "got": "12.6.0", - "inquirer": "9.2.0", + "globby": "13.2.2", + "got": "13.0.0", + "inquirer": "9.2.10", "is-ci": "3.0.1", "issue-parser": "6.0.0", "lodash": "4.17.21", "mime-types": "2.1.35", "new-github-release-url": "2.0.0", - "node-fetch": "3.3.1", + "node-fetch": "3.3.2", "open": "9.1.0", - "ora": "6.3.0", + "ora": "7.0.1", "os-name": "5.1.0", "promise.allsettled": "1.0.6", - "proxy-agent": "5.0.0", - "semver": "7.5.0", + "proxy-agent": "6.3.0", + "semver": "7.5.4", "shelljs": "0.8.5", "update-notifier": "6.0.2", "url-join": "5.0.0", @@ -6728,18 +6986,18 @@ "release-it": "bin/release-it.js" }, "engines": { - "node": ">=14.9" + "node": ">=16" } }, "node_modules/release-it/node_modules/globby": { - "version": "13.1.4", - "resolved": "https://registry.npmjs.org/globby/-/globby-13.1.4.tgz", - "integrity": "sha512-iui/IiiW+QrJ1X1hKH5qwlMQyv34wJAYwH1vrf8b9kBA4sNiif3gKsMHa+BrdnOpEudWjpotfa7LrTzB1ERS/g==", + "version": "13.2.2", + "resolved": "https://registry.npmjs.org/globby/-/globby-13.2.2.tgz", + "integrity": "sha512-Y1zNGV+pzQdh7H39l9zgB4PJqjRNqydvdYCDG4HFXM4XuvSaQQlEc91IU1yALL8gUTDomgBAfz3XJdmUS+oo0w==", "dev": true, "dependencies": { "dir-glob": "^3.0.1", - "fast-glob": "^3.2.11", - "ignore": "^5.2.0", + "fast-glob": "^3.3.0", + "ignore": "^5.2.4", "merge2": "^1.4.1", "slash": "^4.0.0" }, @@ -6763,9 +7021,9 @@ } }, "node_modules/release-it/node_modules/semver": { - "version": "7.5.0", - "resolved": "https://registry.npmjs.org/semver/-/semver-7.5.0.tgz", - "integrity": "sha512-+XC0AD/R7Q2mPSRuy2Id0+CGTZ98+8f+KvwirxOKIEyid+XSx6HbC63p+O4IndTHuX5Z+JxQ0TghCkO5Cg/2HA==", + "version": "7.5.4", + "resolved": "https://registry.npmjs.org/semver/-/semver-7.5.4.tgz", + "integrity": "sha512-1bCSESV6Pv+i21Hvpxp3Dx+pSD8lIPt8uVjRrxAUt/nbswYc+tK6Y2btiULjd4+fnq15PX+nqQDC7Oft7WkwcA==", "dev": true, "dependencies": { "lru-cache": "^6.0.0" @@ -6777,24 +7035,6 @@ "node": ">=10" } }, - "node_modules/release-it/node_modules/slash": { - "version": "4.0.0", - "resolved": "https://registry.npmjs.org/slash/-/slash-4.0.0.tgz", - "integrity": "sha512-3dOsAHXXUkQTpOYcoAxLIorMTp4gIQr5IW3iVb7A7lFIp0VHhnynm9izx6TssdrIcVIESAlVjtnO2K8bg+Coew==", - "dev": true, - "engines": { - "node": ">=12" - }, - "funding": { - "url": "https://github.com/sponsors/sindresorhus" - } - }, - "node_modules/release-it/node_modules/yallist": { - "version": "4.0.0", - "resolved": "https://registry.npmjs.org/yallist/-/yallist-4.0.0.tgz", - "integrity": "sha512-3wdGidZyq5PB084XLES5TpOSRA3wjXAlIWMhum2kRcv/41Sn2emQ0dycQW4uZXLejwKvg6EsvbdlVL+FYEct7A==", - "dev": true - }, "node_modules/release-zalgo": { "version": "1.0.0", "resolved": "https://registry.npmjs.org/release-zalgo/-/release-zalgo-1.0.0.tgz", @@ -6823,12 +7063,12 @@ "dev": true }, "node_modules/resolve": { - "version": "1.22.1", - "resolved": "https://registry.npmjs.org/resolve/-/resolve-1.22.1.tgz", - "integrity": "sha512-nBpuuYuY5jFsli/JIs1oldw6fOQCBioohqWZg/2hiaOybXOft4lonv85uDOKXdf8rhyK159cxU5cDcK/NKk8zw==", + "version": "1.22.4", + "resolved": "https://registry.npmjs.org/resolve/-/resolve-1.22.4.tgz", + "integrity": "sha512-PXNdCiPqDqeUou+w1C2eTQbNfxKSuMxqTCuvlmmMsk1NWHL5fRrhY6Pl0qEYYc6+QqGClco1Qj8XnjPego4wfg==", "dev": true, "dependencies": { - "is-core-module": "^2.9.0", + "is-core-module": "^2.13.0", "path-parse": "^1.0.7", "supports-preserve-symlinks-flag": "^1.0.0" }, @@ -6870,19 +7110,16 @@ } }, "node_modules/restore-cursor": { - "version": "4.0.0", - "resolved": "https://registry.npmjs.org/restore-cursor/-/restore-cursor-4.0.0.tgz", - "integrity": "sha512-I9fPXU9geO9bHOt9pHHOhOkYerIMsmVaWB0rA2AI9ERh/+x/i7MV5HKBNrg+ljO5eoPVgCcnFuRjJ9uH6I/3eg==", + "version": "3.1.0", + "resolved": "https://registry.npmjs.org/restore-cursor/-/restore-cursor-3.1.0.tgz", + "integrity": "sha512-l+sSefzHpj5qimhFSE5a8nufZYAM3sBSVMAPtYkmC+4EH2anSGaEMXSD0izRQbu9nfyQ9y5JrVmp7E8oZrUjvA==", "dev": true, "dependencies": { "onetime": "^5.1.0", "signal-exit": "^3.0.2" }, "engines": { - "node": "^12.20.0 || ^14.13.1 || >=16.0.0" - }, - "funding": { - "url": "https://github.com/sponsors/sindresorhus" + "node": ">=8" } }, "node_modules/restore-cursor/node_modules/mimic-fn": { @@ -7048,9 +7285,9 @@ } }, "node_modules/run-async": { - "version": "2.4.1", - "resolved": "https://registry.npmjs.org/run-async/-/run-async-2.4.1.tgz", - "integrity": "sha512-tvVnVv01b8c1RrA6Ep7JkStj85Guv/YrMcwqYQnwjsAS2cTmmPGBBjAjpCW7RrSodNSoE2/qg9O4bceNvUuDgQ==", + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/run-async/-/run-async-3.0.0.tgz", + "integrity": "sha512-540WwVDOMxA6dN6We19EcT9sc3hkXPw5mzRNGM3FkdN/vtE9NFvj5lFAPNwUDmJjXidm3v7TC1cTE7t17Ulm1Q==", "dev": true, "engines": { "node": ">=0.12.0" @@ -7088,6 +7325,24 @@ "tslib": "^2.1.0" } }, + "node_modules/safe-array-concat": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/safe-array-concat/-/safe-array-concat-1.0.0.tgz", + "integrity": "sha512-9dVEFruWIsnie89yym+xWTAYASdpw3CJV7Li/6zBewGf9z2i1j31rP6jnY0pHEO4QZh6N0K11bFjWmdR8UGdPQ==", + "dev": true, + "dependencies": { + "call-bind": "^1.0.2", + "get-intrinsic": "^1.2.0", + "has-symbols": "^1.0.3", + "isarray": "^2.0.5" + }, + "engines": { + "node": ">=0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, "node_modules/safe-buffer": { "version": "5.2.1", "resolved": "https://registry.npmjs.org/safe-buffer/-/safe-buffer-5.2.1.tgz", @@ -7129,9 +7384,9 @@ "dev": true }, "node_modules/semver": { - "version": "6.3.0", - "resolved": "https://registry.npmjs.org/semver/-/semver-6.3.0.tgz", - "integrity": "sha512-b39TBaTSfV6yBrapU89p5fKekE2m/NwnDocOVruQFS1/veMgdzuPcnOM34M6CwxW8jH/lxEa5rBoDeUwu5HHTw==", + "version": "6.3.1", + "resolved": "https://registry.npmjs.org/semver/-/semver-6.3.1.tgz", + "integrity": "sha512-BR7VvDCVHO+q2xBEWskxS6DJE1qRnb7DxzUrogb71CWoSficBxYsiAGd+Kl0mmq/MprG9yArRkyrQxTO6XjMzA==", "dev": true, "bin": { "semver": "bin/semver.js" @@ -7165,9 +7420,9 @@ } }, "node_modules/semver-diff/node_modules/semver": { - "version": "7.3.8", - "resolved": "https://registry.npmjs.org/semver/-/semver-7.3.8.tgz", - "integrity": "sha512-NB1ctGL5rlHrPJtFDVIVzTyQylMLu9N9VICA6HSFJo8MCGVTMW6gfpicwKmmK/dAjTOrqu5l63JJOpDSrAis3A==", + "version": "7.5.4", + "resolved": "https://registry.npmjs.org/semver/-/semver-7.5.4.tgz", + "integrity": "sha512-1bCSESV6Pv+i21Hvpxp3Dx+pSD8lIPt8uVjRrxAUt/nbswYc+tK6Y2btiULjd4+fnq15PX+nqQDC7Oft7WkwcA==", "dev": true, "dependencies": { "lru-cache": "^6.0.0" @@ -7179,12 +7434,6 @@ "node": ">=10" } }, - "node_modules/semver-diff/node_modules/yallist": { - "version": "4.0.0", - "resolved": "https://registry.npmjs.org/yallist/-/yallist-4.0.0.tgz", - "integrity": "sha512-3wdGidZyq5PB084XLES5TpOSRA3wjXAlIWMhum2kRcv/41Sn2emQ0dycQW4uZXLejwKvg6EsvbdlVL+FYEct7A==", - "dev": true - }, "node_modules/serialize-javascript": { "version": "6.0.0", "resolved": "https://registry.npmjs.org/serialize-javascript/-/serialize-javascript-6.0.0.tgz", @@ -7200,12 +7449,6 @@ "integrity": "sha512-KiKBS8AnWGEyLzofFfmvKwpdPzqiy16LvQfK3yv/fVH7Bj13/wl3JSR1J+rfgRE9q7xUJK4qvgS8raSOeLUehw==", "dev": true }, - "node_modules/setprototypeof": { - "version": "1.2.0", - "resolved": "https://registry.npmjs.org/setprototypeof/-/setprototypeof-1.2.0.tgz", - "integrity": "sha512-E5LDX7Wrp85Kil5bhZv46j8jOeboKq5JMmYM3gVGdGH8xFpPWXUMsNrlODCrkoxMEeNi/XZIwuRvY4XNwYMJpw==", - "dev": true - }, "node_modules/shebang-command": { "version": "2.0.0", "resolved": "https://registry.npmjs.org/shebang-command/-/shebang-command-2.0.0.tgz", @@ -7245,9 +7488,9 @@ } }, "node_modules/shiki": { - "version": "0.14.1", - "resolved": "https://registry.npmjs.org/shiki/-/shiki-0.14.1.tgz", - "integrity": "sha512-+Jz4nBkCBe0mEDqo1eKRcCdjRtrCjozmcbTUjbPTX7OOJfEbTZzlUWlZtGe3Gb5oV1/jnojhG//YZc3rs9zSEw==", + "version": "0.14.3", + "resolved": "https://registry.npmjs.org/shiki/-/shiki-0.14.3.tgz", + "integrity": "sha512-U3S/a+b0KS+UkTyMjoNojvTgrBHjgp7L6ovhFVZsXmBGnVdQ4K4U9oK0z63w538S91ATngv1vXigHCSWOwnr+g==", "dev": true, "dependencies": { "ansi-sequence-parser": "^1.1.0", @@ -7277,13 +7520,13 @@ "dev": true }, "node_modules/sinon": { - "version": "15.1.0", - "resolved": "https://registry.npmjs.org/sinon/-/sinon-15.1.0.tgz", - "integrity": "sha512-cS5FgpDdE9/zx7no8bxROHymSlPLZzq0ChbbLk1DrxBfc+eTeBK3y8nIL+nu/0QeYydhhbLIr7ecHJpywjQaoQ==", + "version": "15.2.0", + "resolved": "https://registry.npmjs.org/sinon/-/sinon-15.2.0.tgz", + "integrity": "sha512-nPS85arNqwBXaIsFCkolHjGIkFo+Oxu9vbgmBJizLAhqe6P2o3Qmj3KCUoRkfhHtvgDhZdWD3risLHAUJ8npjw==", "dev": true, "dependencies": { "@sinonjs/commons": "^3.0.0", - "@sinonjs/fake-timers": "^10.2.0", + "@sinonjs/fake-timers": "^10.3.0", "@sinonjs/samsam": "^8.0.0", "diff": "^5.1.0", "nise": "^5.1.4", @@ -7294,15 +7537,6 @@ "url": "https://opencollective.com/sinon" } }, - "node_modules/sinon/node_modules/@sinonjs/commons": { - "version": "3.0.0", - "resolved": "https://registry.npmjs.org/@sinonjs/commons/-/commons-3.0.0.tgz", - "integrity": "sha512-jXBtWAF4vmdNmZgD5FoKsVLv3rPgDnLgPbU84LIJ3otV44vJlDRokVng5v8NFJdCf/da9legHcKaRuZs4L7faA==", - "dev": true, - "dependencies": { - "type-detect": "4.0.8" - } - }, "node_modules/sinon/node_modules/diff": { "version": "5.1.0", "resolved": "https://registry.npmjs.org/diff/-/diff-5.1.0.tgz", @@ -7312,15 +7546,39 @@ "node": ">=0.3.1" } }, - "node_modules/slash": { - "version": "3.0.0", - "resolved": "https://registry.npmjs.org/slash/-/slash-3.0.0.tgz", - "integrity": "sha512-g9Q1haeby36OSStwb4ntCGGGaKsaVSjQ68fBxoQcutl5fS1vuY18H3wSt3jFyFtrkx+Kz0V1G85A4MyAdDMi2Q==", + "node_modules/sinon/node_modules/has-flag": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/has-flag/-/has-flag-4.0.0.tgz", + "integrity": "sha512-EykJT/Q1KjTWctppgIAgfSO0tKVuZUjhgMr17kqTumMl6Afv3EISleU7qZUzoXDFTAHTDC4NOoG/ZxU3EvlMPQ==", + "dev": true, + "engines": { + "node": ">=8" + } + }, + "node_modules/sinon/node_modules/supports-color": { + "version": "7.2.0", + "resolved": "https://registry.npmjs.org/supports-color/-/supports-color-7.2.0.tgz", + "integrity": "sha512-qpCAvRl9stuOHveKsn7HncJRvv501qIacKzQlO/+Lwxc9+0q2wLyv4Dfvt80/DPn2pqOBsJdDiogXGR9+OvwRw==", "dev": true, + "dependencies": { + "has-flag": "^4.0.0" + }, "engines": { "node": ">=8" } }, + "node_modules/slash": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/slash/-/slash-4.0.0.tgz", + "integrity": "sha512-3dOsAHXXUkQTpOYcoAxLIorMTp4gIQr5IW3iVb7A7lFIp0VHhnynm9izx6TssdrIcVIESAlVjtnO2K8bg+Coew==", + "dev": true, + "engines": { + "node": ">=12" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, "node_modules/smart-buffer": { "version": "4.2.0", "resolved": "https://registry.npmjs.org/smart-buffer/-/smart-buffer-4.2.0.tgz", @@ -7346,17 +7604,17 @@ } }, "node_modules/socks-proxy-agent": { - "version": "5.0.1", - "resolved": "https://registry.npmjs.org/socks-proxy-agent/-/socks-proxy-agent-5.0.1.tgz", - "integrity": "sha512-vZdmnjb9a2Tz6WEQVIurybSwElwPxMZaIc7PzqbJTrezcKNznv6giT7J7tZDZ1BojVaa1jvO/UiUdhDVB0ACoQ==", + "version": "8.0.1", + "resolved": "https://registry.npmjs.org/socks-proxy-agent/-/socks-proxy-agent-8.0.1.tgz", + "integrity": "sha512-59EjPbbgg8U3x62hhKOFVAmySQUcfRQ4C7Q/D5sEHnZTQRrQlNKINks44DMR1gwXp0p4LaVIeccX2KHTTcHVqQ==", "dev": true, "dependencies": { - "agent-base": "^6.0.2", - "debug": "4", - "socks": "^2.3.3" + "agent-base": "^7.0.1", + "debug": "^4.3.4", + "socks": "^2.7.1" }, "engines": { - "node": ">= 6" + "node": ">= 14" } }, "node_modules/socks/node_modules/ip": { @@ -7407,15 +7665,6 @@ "integrity": "sha512-D9cPgkvLlV3t3IzL0D0YLvGA9Ahk4PcvVwUbN0dSGr1aP0Nrt4AEnTUbuGvquEC0mA64Gqt1fzirlRs5ibXx8g==", "dev": true }, - "node_modules/statuses": { - "version": "2.0.1", - "resolved": "https://registry.npmjs.org/statuses/-/statuses-2.0.1.tgz", - "integrity": "sha512-RwNA9Z/7PrK06rYLIzFMlaF+l73iwpzsqRIFgbMLbTcLD6cOao82TaWefPXQvB2fOC4AjuYSEndS7N/mTCbkdQ==", - "dev": true, - "engines": { - "node": ">= 0.8" - } - }, "node_modules/stdin-discarder": { "version": "0.1.0", "resolved": "https://registry.npmjs.org/stdin-discarder/-/stdin-discarder-0.1.0.tgz", @@ -7453,20 +7702,34 @@ } }, "node_modules/string-width": { - "version": "5.1.2", - "resolved": "https://registry.npmjs.org/string-width/-/string-width-5.1.2.tgz", - "integrity": "sha512-HnLOCR3vjcY8beoNLtcjZ5/nxn2afmME6lhrDrebokqMap+XbeW8n9TXpPDOqdGK5qcI3oT0GKTW6wC7EMiVqA==", + "version": "4.2.3", + "resolved": "https://registry.npmjs.org/string-width/-/string-width-4.2.3.tgz", + "integrity": "sha512-wKyQRQpjJ0sIp62ErSZdGsjMJWsap5oRNihHhu6G7JVO/9jIB6UyevL+tXuOqrng8j/cxKTWyWUwvSTriiZz/g==", "dev": true, "dependencies": { - "eastasianwidth": "^0.2.0", - "emoji-regex": "^9.2.2", - "strip-ansi": "^7.0.1" + "emoji-regex": "^8.0.0", + "is-fullwidth-code-point": "^3.0.0", + "strip-ansi": "^6.0.1" }, "engines": { - "node": ">=12" + "node": ">=8" + } + }, + "node_modules/string.prototype.trim": { + "version": "1.2.7", + "resolved": "https://registry.npmjs.org/string.prototype.trim/-/string.prototype.trim-1.2.7.tgz", + "integrity": "sha512-p6TmeT1T3411M8Cgg9wBTMRtY2q9+PNy9EV1i2lIXUN/btt763oIfxwN3RR8VU6wHX8j/1CFy0L+YuThm6bgOg==", + "dev": true, + "dependencies": { + "call-bind": "^1.0.2", + "define-properties": "^1.1.4", + "es-abstract": "^1.20.4" + }, + "engines": { + "node": ">= 0.4" }, "funding": { - "url": "https://github.com/sponsors/sindresorhus" + "url": "https://github.com/sponsors/ljharb" } }, "node_modules/string.prototype.trimend": { @@ -7498,18 +7761,15 @@ } }, "node_modules/strip-ansi": { - "version": "7.0.1", - "resolved": "https://registry.npmjs.org/strip-ansi/-/strip-ansi-7.0.1.tgz", - "integrity": "sha512-cXNxvT8dFNRVfhVME3JAe98mkXDYN2O1l7jmcwMnOslDeESg1rF/OZMtK0nRAhiari1unG5cD4jG3rapUAkLbw==", + "version": "6.0.1", + "resolved": "https://registry.npmjs.org/strip-ansi/-/strip-ansi-6.0.1.tgz", + "integrity": "sha512-Y38VPSHcqkFrCpFnQ9vuSXmquuv5oXOKpGeT6aGrr3o3Gc9AlVa6JBfUSOCnbxGGZF+/0ooI7KrPuUSztUdU5A==", "dev": true, "dependencies": { - "ansi-regex": "^6.0.1" + "ansi-regex": "^5.0.1" }, "engines": { - "node": ">=12" - }, - "funding": { - "url": "https://github.com/chalk/strip-ansi?sponsor=1" + "node": ">=8" } }, "node_modules/strip-bom": { @@ -7534,15 +7794,12 @@ } }, "node_modules/strip-json-comments": { - "version": "3.1.1", - "resolved": "https://registry.npmjs.org/strip-json-comments/-/strip-json-comments-3.1.1.tgz", - "integrity": "sha512-6fPc+R4ihwqP6N/aIv2f1gMH8lOVtWQHoqC4yK6oSDVVocumAsfCqjkXnqiYMhmMwS/mEHLp7Vehlt3ql6lEig==", + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/strip-json-comments/-/strip-json-comments-2.0.1.tgz", + "integrity": "sha512-4gB8na07fecVVkOI6Rs4e7T6NOTki5EmL7TUduTs6bu3EdnSycntVJ4re8kgZA+wx9IueI2Y11bfbgwtzuE0KQ==", "dev": true, "engines": { - "node": ">=8" - }, - "funding": { - "url": "https://github.com/sponsors/sindresorhus" + "node": ">=0.10.0" } }, "node_modules/strip-outer": { @@ -7567,15 +7824,15 @@ } }, "node_modules/supports-color": { - "version": "7.2.0", - "resolved": "https://registry.npmjs.org/supports-color/-/supports-color-7.2.0.tgz", - "integrity": "sha512-qpCAvRl9stuOHveKsn7HncJRvv501qIacKzQlO/+Lwxc9+0q2wLyv4Dfvt80/DPn2pqOBsJdDiogXGR9+OvwRw==", + "version": "5.5.0", + "resolved": "https://registry.npmjs.org/supports-color/-/supports-color-5.5.0.tgz", + "integrity": "sha512-QjVjwdXIt408MIiAqCX4oUKsgU2EqAGzs2Ppkm4aQYbjm+ZEWEcW4SfFNTr4uMNZma0ey4f5lgLrkB0aX0QMow==", "dev": true, "dependencies": { - "has-flag": "^4.0.0" + "has-flag": "^3.0.0" }, "engines": { - "node": ">=8" + "node": ">=4" } }, "node_modules/supports-preserve-symlinks-flag": { @@ -7610,12 +7867,6 @@ "integrity": "sha512-N+8UisAXDGk8PFXP4HAzVR9nbfmVJ3zYLAWiTIoqC5v5isinhr+r5uaO8+7r3BMfuNIufIsA7RdpVgacC2cSpw==", "dev": true }, - "node_modules/through": { - "version": "2.3.8", - "resolved": "https://registry.npmjs.org/through/-/through-2.3.8.tgz", - "integrity": "sha512-w89qg7PI8wAdvX60bMDP+bFoD5Dvhm9oLheFp5O4a2QF0cSBGsBX4qZmadPMvVqlLJBBci+WqGGOAPvcDeNSVg==", - "dev": true - }, "node_modules/titleize": { "version": "3.0.0", "resolved": "https://registry.npmjs.org/titleize/-/titleize-3.0.0.tgz", @@ -7661,15 +7912,6 @@ "node": ">=8.0" } }, - "node_modules/toidentifier": { - "version": "1.0.1", - "resolved": "https://registry.npmjs.org/toidentifier/-/toidentifier-1.0.1.tgz", - "integrity": "sha512-o5sSPKEkg/DIQNmH43V0/uerLrpzVedkUh8tGNvaeXpfpuwjKenlSox/2O/BTlZUtEe+JG7s5YhEz608PlAHRA==", - "dev": true, - "engines": { - "node": ">=0.6" - } - }, "node_modules/tr46": { "version": "0.0.3", "resolved": "https://registry.npmjs.org/tr46/-/tr46-0.0.3.tgz", @@ -7697,6 +7939,18 @@ "node": ">=0.8.0" } }, + "node_modules/ts-api-utils": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/ts-api-utils/-/ts-api-utils-1.0.2.tgz", + "integrity": "sha512-Cbu4nIqnEdd+THNEsBdkolnOXhg0I8XteoHaEKgvsxpsbWda4IsUut2c187HxywQCvveojow0Dgw/amxtSKVkQ==", + "dev": true, + "engines": { + "node": ">=16.13.0" + }, + "peerDependencies": { + "typescript": ">=4.2.0" + } + }, "node_modules/ts-node": { "version": "10.9.1", "resolved": "https://registry.npmjs.org/ts-node/-/ts-node-10.9.1.tgz", @@ -7740,6 +7994,12 @@ } } }, + "node_modules/ts-node/node_modules/@tsconfig/node14": { + "version": "1.0.3", + "resolved": "https://registry.npmjs.org/@tsconfig/node14/-/node14-1.0.3.tgz", + "integrity": "sha512-ysT8mhdixWK6Hw3i1V2AeRqZ5WfXg1G43mqoYlM2nc6388Fq5jcXyr5mRsqViLx/GJYdoL0bfXD8nmF+Zn/Iow==", + "dev": true + }, "node_modules/ts-node/node_modules/diff": { "version": "4.0.2", "resolved": "https://registry.npmjs.org/diff/-/diff-4.0.2.tgz", @@ -7750,30 +8010,9 @@ } }, "node_modules/tslib": { - "version": "2.5.0", - "resolved": "https://registry.npmjs.org/tslib/-/tslib-2.5.0.tgz", - "integrity": "sha512-336iVw3rtn2BUK7ORdIAHTyxHGRIHVReokCR3XjbckJMK7ms8FysBfhLR8IXnAgy7T0PTPNBWKiH514FOW/WSg==", - "dev": true - }, - "node_modules/tsutils": { - "version": "3.21.0", - "resolved": "https://registry.npmjs.org/tsutils/-/tsutils-3.21.0.tgz", - "integrity": "sha512-mHKK3iUXL+3UF6xL5k0PEhKRUBKPBCv/+RkEOpjRWxxx27KKRBmmA60A9pgOUvMi8GKhRMPEmjBRPzs2W7O1OA==", - "dev": true, - "dependencies": { - "tslib": "^1.8.1" - }, - "engines": { - "node": ">= 6" - }, - "peerDependencies": { - "typescript": ">=2.8.0 || >= 3.2.0-dev || >= 3.3.0-dev || >= 3.4.0-dev || >= 3.5.0-dev || >= 3.6.0-dev || >= 3.6.0-beta || >= 3.7.0-dev || >= 3.7.0-beta" - } - }, - "node_modules/tsutils/node_modules/tslib": { - "version": "1.14.1", - "resolved": "https://registry.npmjs.org/tslib/-/tslib-1.14.1.tgz", - "integrity": "sha512-Xni35NKzjgMrwevysHTCArtLDpPvye8zV/0E4EyYn43P7/7qvQwPh9BGkHewbMulVntbigmcT7rdX3BNo9wRJg==", + "version": "2.6.2", + "resolved": "https://registry.npmjs.org/tslib/-/tslib-2.6.2.tgz", + "integrity": "sha512-AEYxH93jGFPn/a2iVAwW87VuUIkR1FVUKB77NwMF7nBTDkDrrT/Hpt/IrCJ0QXhW27jTBDcf5ZY7w6RiqTMw2Q==", "dev": true }, "node_modules/type-check": { @@ -7798,9 +8037,9 @@ } }, "node_modules/type-fest": { - "version": "0.20.2", - "resolved": "https://registry.npmjs.org/type-fest/-/type-fest-0.20.2.tgz", - "integrity": "sha512-Ne+eE4r0/iWnpAxD852z3A+N0Bt5RN//NjJwRd2VFHEmrywxf5vsZlh4R6lixl6B+wz/8d+maTSAkN1FIkI3LQ==", + "version": "0.21.3", + "resolved": "https://registry.npmjs.org/type-fest/-/type-fest-0.21.3.tgz", + "integrity": "sha512-t0rzBq87m3fVcduHDUFhKmyyX+9eo6WQjZvf51Ea/M0Q7+T374Jp1aUiyUl0GKxp8M/OETVHSDvmkyPgvX+X2w==", "dev": true, "engines": { "node": ">=10" @@ -7809,6 +8048,57 @@ "url": "https://github.com/sponsors/sindresorhus" } }, + "node_modules/typed-array-buffer": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/typed-array-buffer/-/typed-array-buffer-1.0.0.tgz", + "integrity": "sha512-Y8KTSIglk9OZEr8zywiIHG/kmQ7KWyjseXs1CbSo8vC42w7hg2HgYTxSWwP0+is7bWDc1H+Fo026CpHFwm8tkw==", + "dev": true, + "dependencies": { + "call-bind": "^1.0.2", + "get-intrinsic": "^1.2.1", + "is-typed-array": "^1.1.10" + }, + "engines": { + "node": ">= 0.4" + } + }, + "node_modules/typed-array-byte-length": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/typed-array-byte-length/-/typed-array-byte-length-1.0.0.tgz", + "integrity": "sha512-Or/+kvLxNpeQ9DtSydonMxCx+9ZXOswtwJn17SNLvhptaXYDJvkFFP5zbfU/uLmvnBJlI4yrnXRxpdWH/M5tNA==", + "dev": true, + "dependencies": { + "call-bind": "^1.0.2", + "for-each": "^0.3.3", + "has-proto": "^1.0.1", + "is-typed-array": "^1.1.10" + }, + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/typed-array-byte-offset": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/typed-array-byte-offset/-/typed-array-byte-offset-1.0.0.tgz", + "integrity": "sha512-RD97prjEt9EL8YgAgpOkf3O4IF9lhJFr9g0htQkm0rchFp/Vx7LW5Q8fSXXub7BXAODyUQohRMyOc3faCPd0hg==", + "dev": true, + "dependencies": { + "available-typed-arrays": "^1.0.5", + "call-bind": "^1.0.2", + "for-each": "^0.3.3", + "has-proto": "^1.0.1", + "is-typed-array": "^1.1.10" + }, + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, "node_modules/typed-array-length": { "version": "1.0.4", "resolved": "https://registry.npmjs.org/typed-array-length/-/typed-array-length-1.0.4.tgz", @@ -7833,9 +8123,9 @@ } }, "node_modules/typedoc": { - "version": "0.24.7", - "resolved": "https://registry.npmjs.org/typedoc/-/typedoc-0.24.7.tgz", - "integrity": "sha512-zzfKDFIZADA+XRIp2rMzLe9xZ6pt12yQOhCr7cD7/PBTjhPmMyMvGrkZ2lPNJitg3Hj1SeiYFNzCsSDrlpxpKw==", + "version": "0.24.8", + "resolved": "https://registry.npmjs.org/typedoc/-/typedoc-0.24.8.tgz", + "integrity": "sha512-ahJ6Cpcvxwaxfu4KtjA8qZNqS43wYt6JL27wYiIgl1vd38WW/KWX11YuAeZhuz9v+ttrutSsgK+XO1CjL1kA3w==", "dev": true, "dependencies": { "lunr": "^2.3.9", @@ -7850,7 +8140,7 @@ "node": ">= 14.14" }, "peerDependencies": { - "typescript": "4.6.x || 4.7.x || 4.8.x || 4.9.x || 5.0.x" + "typescript": "4.6.x || 4.7.x || 4.8.x || 4.9.x || 5.0.x || 5.1.x" } }, "node_modules/typedoc/node_modules/brace-expansion": { @@ -7863,9 +8153,9 @@ } }, "node_modules/typedoc/node_modules/minimatch": { - "version": "9.0.1", - "resolved": "https://registry.npmjs.org/minimatch/-/minimatch-9.0.1.tgz", - "integrity": "sha512-0jWhJpD/MdhPXwPuiRkCbfYfSKp2qnn2eOc279qI7f+osl/l+prKSrvhg157zSYvx/1nmgn2NqdT6k2Z7zSH9w==", + "version": "9.0.3", + "resolved": "https://registry.npmjs.org/minimatch/-/minimatch-9.0.3.tgz", + "integrity": "sha512-RHiac9mvaRw0x3AYRgDC1CxAP7HTcNrrECeA8YYJeWnpo+2Q5CegtZjaotWTWxDG3UeGA1coE05iH1mPjT/2mg==", "dev": true, "dependencies": { "brace-expansion": "^2.0.1" @@ -7878,16 +8168,16 @@ } }, "node_modules/typescript": { - "version": "4.9.5", - "resolved": "https://registry.npmjs.org/typescript/-/typescript-4.9.5.tgz", - "integrity": "sha512-1FXk9E2Hm+QzZQ7z+McJiHL4NW1F2EzMu9Nq9i3zAaGqibafqYwCVU6WyWAuyQRRzOlxou8xZSyXLEN8oKj24g==", + "version": "5.1.6", + "resolved": "https://registry.npmjs.org/typescript/-/typescript-5.1.6.tgz", + "integrity": "sha512-zaWCozRZ6DLEWAWFrVDz1H6FVXzUSfTy5FUMWsQlU8Ym5JP9eO4xkTIROFCQvhQf61z6O/G6ugw3SgAnvvm+HA==", "dev": true, "bin": { "tsc": "bin/tsc", "tsserver": "bin/tsserver" }, "engines": { - "node": ">=4.2.0" + "node": ">=14.17" } }, "node_modules/unbox-primitive": { @@ -7927,21 +8217,12 @@ "dev": true }, "node_modules/universalify": { - "version": "0.1.2", - "resolved": "https://registry.npmjs.org/universalify/-/universalify-0.1.2.tgz", - "integrity": "sha512-rBJeI5CXAlmy1pV+617WB9J63U6XcazHHF2f2dbJix4XzpUF0RS3Zbj0FGIOCAva5P/d/GBOYaACQ1w+0azUkg==", - "dev": true, - "engines": { - "node": ">= 4.0.0" - } - }, - "node_modules/unpipe": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/unpipe/-/unpipe-1.0.0.tgz", - "integrity": "sha512-pjy2bYhSsufwWlKwPc+l3cN7+wuJlK6uz0YdJEOlQDbl6jo/YlPi4mb8agUkVC8BF7V8NuzeyPNqRksA3hztKQ==", + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/universalify/-/universalify-2.0.0.tgz", + "integrity": "sha512-hAZsKq7Yy11Zu1DE0OzWjw7nnLZmJZYTDZZyEFHZdUhV8FkH5MCfoU1XMaxXovpyW5nq5scPqq0ZDP9Zyl04oQ==", "dev": true, "engines": { - "node": ">= 0.8" + "node": ">= 10.0.0" } }, "node_modules/untildify": { @@ -7954,9 +8235,9 @@ } }, "node_modules/update-browserslist-db": { - "version": "1.0.10", - "resolved": "https://registry.npmjs.org/update-browserslist-db/-/update-browserslist-db-1.0.10.tgz", - "integrity": "sha512-OztqDenkfFkbSG+tRxBeAnCVPckDBcvibKd35yDONx6OU8N7sqgwc7rCbkJ/WcYtVRZ4ba68d6byhC21GFh7sQ==", + "version": "1.0.11", + "resolved": "https://registry.npmjs.org/update-browserslist-db/-/update-browserslist-db-1.0.11.tgz", + "integrity": "sha512-dCwEFf0/oT85M1fHBg4F0jtLwJrutGoHSQXCh7u4o2t1drG+c0a9Flnqww6XUKSfQMPpJBRjU8d4RXB09qtvaA==", "dev": true, "funding": [ { @@ -7966,6 +8247,10 @@ { "type": "tidelift", "url": "https://tidelift.com/funding/github/npm/browserslist" + }, + { + "type": "github", + "url": "https://github.com/sponsors/ai" } ], "dependencies": { @@ -7973,7 +8258,7 @@ "picocolors": "^1.0.0" }, "bin": { - "browserslist-lint": "cli.js" + "update-browserslist-db": "cli.js" }, "peerDependencies": { "browserslist": ">= 4.21.0" @@ -8020,9 +8305,9 @@ } }, "node_modules/update-notifier/node_modules/semver": { - "version": "7.3.8", - "resolved": "https://registry.npmjs.org/semver/-/semver-7.3.8.tgz", - "integrity": "sha512-NB1ctGL5rlHrPJtFDVIVzTyQylMLu9N9VICA6HSFJo8MCGVTMW6gfpicwKmmK/dAjTOrqu5l63JJOpDSrAis3A==", + "version": "7.5.4", + "resolved": "https://registry.npmjs.org/semver/-/semver-7.5.4.tgz", + "integrity": "sha512-1bCSESV6Pv+i21Hvpxp3Dx+pSD8lIPt8uVjRrxAUt/nbswYc+tK6Y2btiULjd4+fnq15PX+nqQDC7Oft7WkwcA==", "dev": true, "dependencies": { "lru-cache": "^6.0.0" @@ -8034,12 +8319,6 @@ "node": ">=10" } }, - "node_modules/update-notifier/node_modules/yallist": { - "version": "4.0.0", - "resolved": "https://registry.npmjs.org/yallist/-/yallist-4.0.0.tgz", - "integrity": "sha512-3wdGidZyq5PB084XLES5TpOSRA3wjXAlIWMhum2kRcv/41Sn2emQ0dycQW4uZXLejwKvg6EsvbdlVL+FYEct7A==", - "dev": true - }, "node_modules/uri-js": { "version": "4.4.1", "resolved": "https://registry.npmjs.org/uri-js/-/uri-js-4.4.1.tgz", @@ -8079,22 +8358,6 @@ "integrity": "sha512-wa7YjyUGfNZngI/vtK0UHAN+lgDCxBPCylVXGp0zu59Fz5aiGtNXaq3DhIov063MorB+VfufLh3JlF2KdTK3xg==", "dev": true }, - "node_modules/vm2": { - "version": "3.9.18", - "resolved": "https://registry.npmjs.org/vm2/-/vm2-3.9.18.tgz", - "integrity": "sha512-iM7PchOElv6Uv6Q+0Hq7dcgDtWWT6SizYqVcvol+1WQc+E9HlgTCnPozbQNSP3yDV9oXHQOEQu530w2q/BCVZg==", - "dev": true, - "dependencies": { - "acorn": "^8.7.0", - "acorn-walk": "^8.2.0" - }, - "bin": { - "vm2": "bin/vm2" - }, - "engines": { - "node": ">=6.0" - } - }, "node_modules/vscode-oniguruma": { "version": "1.7.0", "resolved": "https://registry.npmjs.org/vscode-oniguruma/-/vscode-oniguruma-1.7.0.tgz", @@ -8173,23 +8436,22 @@ } }, "node_modules/which-module": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/which-module/-/which-module-2.0.0.tgz", - "integrity": "sha512-B+enWhmw6cjfVC7kS8Pj9pCrKSc5txArRyaYGe088shv/FGWH+0Rjx/xPgtsWfsUtS27FkP697E4DDhgrgoc0Q==", + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/which-module/-/which-module-2.0.1.tgz", + "integrity": "sha512-iBdZ57RDvnOR9AGBhML2vFZf7h8vmBjhoaZqODJBFWHVtKkDmKuHai3cx5PgVMrX5YDNp27AofYbAwctSS+vhQ==", "dev": true }, "node_modules/which-typed-array": { - "version": "1.1.9", - "resolved": "https://registry.npmjs.org/which-typed-array/-/which-typed-array-1.1.9.tgz", - "integrity": "sha512-w9c4xkx6mPidwp7180ckYWfMmvxpjlZuIudNtDf4N/tTAUB8VJbX25qZoAsrtGuYNnGw3pa0AXgbGKRB8/EceA==", + "version": "1.1.11", + "resolved": "https://registry.npmjs.org/which-typed-array/-/which-typed-array-1.1.11.tgz", + "integrity": "sha512-qe9UWWpkeG5yzZ0tNYxDmd7vo58HDBc39mZ0xWWpolAGADdFOzkfamWLDxkOWcvHQKVmdTyQdLD4NOfjLWTKew==", "dev": true, "dependencies": { "available-typed-arrays": "^1.0.5", "call-bind": "^1.0.2", "for-each": "^0.3.3", "gopd": "^1.0.1", - "has-tostringtag": "^1.0.0", - "is-typed-array": "^1.1.10" + "has-tostringtag": "^1.0.0" }, "engines": { "node": ">= 0.4" @@ -8213,6 +8475,56 @@ "url": "https://github.com/sponsors/sindresorhus" } }, + "node_modules/widest-line/node_modules/ansi-regex": { + "version": "6.0.1", + "resolved": "https://registry.npmjs.org/ansi-regex/-/ansi-regex-6.0.1.tgz", + "integrity": "sha512-n5M855fKb2SsfMIiFFoVrABHJC8QtHwVx+mHWP3QcEqBHYienj5dHSgjbxtC0WEZXYt4wcD6zrQElDPhFuZgfA==", + "dev": true, + "engines": { + "node": ">=12" + }, + "funding": { + "url": "https://github.com/chalk/ansi-regex?sponsor=1" + } + }, + "node_modules/widest-line/node_modules/emoji-regex": { + "version": "9.2.2", + "resolved": "https://registry.npmjs.org/emoji-regex/-/emoji-regex-9.2.2.tgz", + "integrity": "sha512-L18DaJsXSUk2+42pv8mLs5jJT2hqFkFE4j21wOmgbUqsZ2hL72NsUU785g9RXgo3s0ZNgVl42TiHp3ZtOv/Vyg==", + "dev": true + }, + "node_modules/widest-line/node_modules/string-width": { + "version": "5.1.2", + "resolved": "https://registry.npmjs.org/string-width/-/string-width-5.1.2.tgz", + "integrity": "sha512-HnLOCR3vjcY8beoNLtcjZ5/nxn2afmME6lhrDrebokqMap+XbeW8n9TXpPDOqdGK5qcI3oT0GKTW6wC7EMiVqA==", + "dev": true, + "dependencies": { + "eastasianwidth": "^0.2.0", + "emoji-regex": "^9.2.2", + "strip-ansi": "^7.0.1" + }, + "engines": { + "node": ">=12" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/widest-line/node_modules/strip-ansi": { + "version": "7.1.0", + "resolved": "https://registry.npmjs.org/strip-ansi/-/strip-ansi-7.1.0.tgz", + "integrity": "sha512-iq6eVVI64nQQTRYq2KtEg2d2uU7LElhTJwsH4YzIHZshxlgZms/wIc4VoDQTlG/IvVIrBKG06CrZnp0qv7hkcQ==", + "dev": true, + "dependencies": { + "ansi-regex": "^6.0.1" + }, + "engines": { + "node": ">=12" + }, + "funding": { + "url": "https://github.com/chalk/strip-ansi?sponsor=1" + } + }, "node_modules/wildcard-match": { "version": "5.1.2", "resolved": "https://registry.npmjs.org/wildcard-match/-/wildcard-match-5.1.2.tgz", @@ -8220,9 +8532,9 @@ "dev": true }, "node_modules/windows-release": { - "version": "5.1.0", - "resolved": "https://registry.npmjs.org/windows-release/-/windows-release-5.1.0.tgz", - "integrity": "sha512-CddHecz5dt0ngTjGPP1uYr9Tjl4qq5rEKNk8UGb8XCdngNXI+GRYvqelD055FdiUgqODZz3R/5oZWYldPtXQpA==", + "version": "5.1.1", + "resolved": "https://registry.npmjs.org/windows-release/-/windows-release-5.1.1.tgz", + "integrity": "sha512-NMD00arvqcq2nwqc5Q6KtrSRHK+fVD31erE5FEMahAw5PmVCgD7MUXodq3pdZSUkqA9Cda2iWx6s1XYwiJWRmw==", "dev": true, "dependencies": { "execa": "^5.1.1" @@ -8323,15 +8635,6 @@ "node": ">=6" } }, - "node_modules/word-wrap": { - "version": "1.2.3", - "resolved": "https://registry.npmjs.org/word-wrap/-/word-wrap-1.2.3.tgz", - "integrity": "sha512-Hz/mrNwitNRh/HUAtM/VT/5VH+ygD6DV7mYKZAtHOrbs8U7lvPS6xf7EJKMF0uW1KJCl0H701g3ZGus+muE5vQ==", - "dev": true, - "engines": { - "node": ">=0.10.0" - } - }, "node_modules/workerpool": { "version": "6.2.1", "resolved": "https://registry.npmjs.org/workerpool/-/workerpool-6.2.1.tgz", @@ -8339,20 +8642,17 @@ "dev": true }, "node_modules/wrap-ansi": { - "version": "8.1.0", - "resolved": "https://registry.npmjs.org/wrap-ansi/-/wrap-ansi-8.1.0.tgz", - "integrity": "sha512-si7QWI6zUMq56bESFvagtmzMdGOtoxfR+Sez11Mobfc7tm+VkUckk9bW2UeffTGVUbOksxmSw0AA2gs8g71NCQ==", + "version": "6.2.0", + "resolved": "https://registry.npmjs.org/wrap-ansi/-/wrap-ansi-6.2.0.tgz", + "integrity": "sha512-r6lPcBGxZXlIcymEu7InxDMhdW0KDxpLgoFLcguasxCaJ/SOIZwINatK9KY/tf+ZrlywOKU0UDj3ATXUBfxJXA==", "dev": true, "dependencies": { - "ansi-styles": "^6.1.0", - "string-width": "^5.0.1", - "strip-ansi": "^7.0.1" + "ansi-styles": "^4.0.0", + "string-width": "^4.1.0", + "strip-ansi": "^6.0.0" }, "engines": { - "node": ">=12" - }, - "funding": { - "url": "https://github.com/chalk/wrap-ansi?sponsor=1" + "node": ">=8" } }, "node_modules/wrappy": { @@ -8385,47 +8685,36 @@ "url": "https://github.com/sponsors/sindresorhus" } }, - "node_modules/xregexp": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/xregexp/-/xregexp-2.0.0.tgz", - "integrity": "sha512-xl/50/Cf32VsGq/1R8jJE5ajH1yMCQkpmoS10QbFZWl2Oor4H0Me64Pu2yxvsRWK3m6soJbmGfzSR7BYmDcWAA==", + "node_modules/y18n": { + "version": "5.0.8", + "resolved": "https://registry.npmjs.org/y18n/-/y18n-5.0.8.tgz", + "integrity": "sha512-0pfFzegeDWJHJIAmTLRP2DwHjdF5s7jo9tuztdQxAhINCdvS+3nGINqPd00AphqJR/0LhANUS6/+7SCb98YOfA==", "dev": true, "engines": { - "node": "*" + "node": ">=10" } }, - "node_modules/y18n": { - "version": "4.0.3", - "resolved": "https://registry.npmjs.org/y18n/-/y18n-4.0.3.tgz", - "integrity": "sha512-JKhqTOwSrqNA1NY5lSztJ1GrBiUodLMmIZuLiDaMRJ+itFd+ABVE8XBjOvIWL+rSqNDC74LCSFmlb/U4UZ4hJQ==", - "dev": true - }, "node_modules/yallist": { - "version": "3.1.1", - "resolved": "https://registry.npmjs.org/yallist/-/yallist-3.1.1.tgz", - "integrity": "sha512-a4UGQaWPH59mOXUYnAG2ewncQS4i4F43Tv3JoAM+s2VDAmS9NsK8GpDMLrCHPksFT7h3K6TOoUNn2pb7RoXx4g==", - "dev": true + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/yallist/-/yallist-4.0.0.tgz", + "integrity": "sha512-3wdGidZyq5PB084XLES5TpOSRA3wjXAlIWMhum2kRcv/41Sn2emQ0dycQW4uZXLejwKvg6EsvbdlVL+FYEct7A==" }, "node_modules/yargs": { - "version": "15.4.1", - "resolved": "https://registry.npmjs.org/yargs/-/yargs-15.4.1.tgz", - "integrity": "sha512-aePbxDmcYW++PaqBsJ+HYUFwCdv4LVvdnhBy78E57PIor8/OVvhMrADFFEDh8DHDFRv/O9i3lPhsENjO7QX0+A==", + "version": "17.7.2", + "resolved": "https://registry.npmjs.org/yargs/-/yargs-17.7.2.tgz", + "integrity": "sha512-7dSzzRQ++CKnNI/krKnYRV7JKKPUXMEh61soaHKg9mrWEhzFWhFnxPxGl+69cD1Ou63C13NUPCnmIcrvqCuM6w==", "dev": true, "dependencies": { - "cliui": "^6.0.0", - "decamelize": "^1.2.0", - "find-up": "^4.1.0", - "get-caller-file": "^2.0.1", + "cliui": "^8.0.1", + "escalade": "^3.1.1", + "get-caller-file": "^2.0.5", "require-directory": "^2.1.1", - "require-main-filename": "^2.0.0", - "set-blocking": "^2.0.0", - "string-width": "^4.2.0", - "which-module": "^2.0.0", - "y18n": "^4.0.0", - "yargs-parser": "^18.1.2" + "string-width": "^4.2.3", + "y18n": "^5.0.5", + "yargs-parser": "^21.1.1" }, "engines": { - "node": ">=8" + "node": ">=12" } }, "node_modules/yargs-parser": { @@ -8476,60 +8765,6 @@ "url": "https://github.com/sponsors/sindresorhus" } }, - "node_modules/yargs/node_modules/ansi-regex": { - "version": "5.0.1", - "resolved": "https://registry.npmjs.org/ansi-regex/-/ansi-regex-5.0.1.tgz", - "integrity": "sha512-quJQXlTSUGL2LH9SUXo8VwsY4soanhgo6LNSm84E1LBcE8s3O0wpdiRzyR9z/ZZJMlMWv37qOOb9pdJlMUEKFQ==", - "dev": true, - "engines": { - "node": ">=8" - } - }, - "node_modules/yargs/node_modules/emoji-regex": { - "version": "8.0.0", - "resolved": "https://registry.npmjs.org/emoji-regex/-/emoji-regex-8.0.0.tgz", - "integrity": "sha512-MSjYzcWNOA0ewAHpz0MxpYFvwg6yjy1NG3xteoqz644VCo/RPgnr1/GGt+ic3iJTzQ8Eu3TdM14SawnVUmGE6A==", - "dev": true - }, - "node_modules/yargs/node_modules/string-width": { - "version": "4.2.3", - "resolved": "https://registry.npmjs.org/string-width/-/string-width-4.2.3.tgz", - "integrity": "sha512-wKyQRQpjJ0sIp62ErSZdGsjMJWsap5oRNihHhu6G7JVO/9jIB6UyevL+tXuOqrng8j/cxKTWyWUwvSTriiZz/g==", - "dev": true, - "dependencies": { - "emoji-regex": "^8.0.0", - "is-fullwidth-code-point": "^3.0.0", - "strip-ansi": "^6.0.1" - }, - "engines": { - "node": ">=8" - } - }, - "node_modules/yargs/node_modules/strip-ansi": { - "version": "6.0.1", - "resolved": "https://registry.npmjs.org/strip-ansi/-/strip-ansi-6.0.1.tgz", - "integrity": "sha512-Y38VPSHcqkFrCpFnQ9vuSXmquuv5oXOKpGeT6aGrr3o3Gc9AlVa6JBfUSOCnbxGGZF+/0ooI7KrPuUSztUdU5A==", - "dev": true, - "dependencies": { - "ansi-regex": "^5.0.1" - }, - "engines": { - "node": ">=8" - } - }, - "node_modules/yargs/node_modules/yargs-parser": { - "version": "18.1.3", - "resolved": "https://registry.npmjs.org/yargs-parser/-/yargs-parser-18.1.3.tgz", - "integrity": "sha512-o50j0JeToy/4K6OZcaQmW6lyXXKhq7csREXcDwk2omFPJEwUNOVtJKvmDr9EI1fAJZUyZcRF7kxGBWmRXudrCQ==", - "dev": true, - "dependencies": { - "camelcase": "^5.0.0", - "decamelize": "^1.2.0" - }, - "engines": { - "node": ">=6" - } - }, "node_modules/yn": { "version": "3.1.1", "resolved": "https://registry.npmjs.org/yn/-/yn-3.1.1.tgz", @@ -8558,31 +8793,18 @@ "devDependencies": { "@istanbuljs/nyc-config-typescript": "^1.0.2", "@redis/test-utils": "*", - "@types/node": "^20.2.3", + "@types/node": "^20.5.3", "nyc": "^15.1.0", - "release-it": "^15.10.3", + "release-it": "^16.1.5", "source-map-support": "^0.5.21", "ts-node": "^10.9.1", - "typedoc": "^0.24.7", - "typescript": "^5.0.4" + "typedoc": "^0.24.8", + "typescript": "^5.1.6" }, "peerDependencies": { "@redis/client": "^1.0.0" } }, - "packages/bloom/node_modules/typescript": { - "version": "5.0.4", - "resolved": "https://registry.npmjs.org/typescript/-/typescript-5.0.4.tgz", - "integrity": "sha512-cW9T5W9xY37cc+jfEnaUvX91foxtHkza3Nw3wkoF4sSlKn0MONdkdEndig/qPBWXNkmplh3NzayQzCiHM4/hqw==", - "dev": true, - "bin": { - "tsc": "bin/tsc", - "tsserver": "bin/tsserver" - }, - "engines": { - "node": ">=12.20" - } - }, "packages/client": { "name": "@redis/client", "version": "1.5.8", @@ -8595,42 +8817,24 @@ "devDependencies": { "@istanbuljs/nyc-config-typescript": "^1.0.2", "@redis/test-utils": "*", - "@types/node": "^20.2.3", - "@types/sinon": "^10.0.15", + "@types/node": "^20.5.3", + "@types/sinon": "^10.0.16", "@types/yallist": "^4.0.1", - "@typescript-eslint/eslint-plugin": "^5.59.7", - "@typescript-eslint/parser": "^5.59.7", - "eslint": "^8.41.0", + "@typescript-eslint/eslint-plugin": "^6.4.1", + "@typescript-eslint/parser": "^6.4.1", + "eslint": "^8.47.0", "nyc": "^15.1.0", - "release-it": "^15.10.3", - "sinon": "^15.1.0", + "release-it": "^16.1.5", + "sinon": "^15.2.0", "source-map-support": "^0.5.21", "ts-node": "^10.9.1", - "typedoc": "^0.24.7", - "typescript": "^5.0.4" + "typedoc": "^0.24.8", + "typescript": "^5.1.6" }, "engines": { "node": ">=14" } }, - "packages/client/node_modules/typescript": { - "version": "5.0.4", - "resolved": "https://registry.npmjs.org/typescript/-/typescript-5.0.4.tgz", - "integrity": "sha512-cW9T5W9xY37cc+jfEnaUvX91foxtHkza3Nw3wkoF4sSlKn0MONdkdEndig/qPBWXNkmplh3NzayQzCiHM4/hqw==", - "dev": true, - "bin": { - "tsc": "bin/tsc", - "tsserver": "bin/tsserver" - }, - "engines": { - "node": ">=12.20" - } - }, - "packages/client/node_modules/yallist": { - "version": "4.0.0", - "resolved": "https://registry.npmjs.org/yallist/-/yallist-4.0.0.tgz", - "integrity": "sha512-3wdGidZyq5PB084XLES5TpOSRA3wjXAlIWMhum2kRcv/41Sn2emQ0dycQW4uZXLejwKvg6EsvbdlVL+FYEct7A==" - }, "packages/graph": { "name": "@redis/graph", "version": "1.1.0", @@ -8638,31 +8842,18 @@ "devDependencies": { "@istanbuljs/nyc-config-typescript": "^1.0.2", "@redis/test-utils": "*", - "@types/node": "^20.2.3", + "@types/node": "^20.5.3", "nyc": "^15.1.0", - "release-it": "^15.10.3", + "release-it": "^16.1.5", "source-map-support": "^0.5.21", "ts-node": "^10.9.1", - "typedoc": "^0.24.7", - "typescript": "^5.0.4" + "typedoc": "^0.24.8", + "typescript": "^5.1.6" }, "peerDependencies": { "@redis/client": "^1.0.0" } }, - "packages/graph/node_modules/typescript": { - "version": "5.0.4", - "resolved": "https://registry.npmjs.org/typescript/-/typescript-5.0.4.tgz", - "integrity": "sha512-cW9T5W9xY37cc+jfEnaUvX91foxtHkza3Nw3wkoF4sSlKn0MONdkdEndig/qPBWXNkmplh3NzayQzCiHM4/hqw==", - "dev": true, - "bin": { - "tsc": "bin/tsc", - "tsserver": "bin/tsserver" - }, - "engines": { - "node": ">=12.20" - } - }, "packages/json": { "name": "@redis/json", "version": "1.0.4", @@ -8670,31 +8861,18 @@ "devDependencies": { "@istanbuljs/nyc-config-typescript": "^1.0.2", "@redis/test-utils": "*", - "@types/node": "^20.2.3", + "@types/node": "^20.5.3", "nyc": "^15.1.0", - "release-it": "^15.10.3", + "release-it": "^16.1.5", "source-map-support": "^0.5.21", "ts-node": "^10.9.1", - "typedoc": "^0.24.7", - "typescript": "^5.0.4" + "typedoc": "^0.24.8", + "typescript": "^5.1.6" }, "peerDependencies": { "@redis/client": "^1.0.0" } }, - "packages/json/node_modules/typescript": { - "version": "5.0.4", - "resolved": "https://registry.npmjs.org/typescript/-/typescript-5.0.4.tgz", - "integrity": "sha512-cW9T5W9xY37cc+jfEnaUvX91foxtHkza3Nw3wkoF4sSlKn0MONdkdEndig/qPBWXNkmplh3NzayQzCiHM4/hqw==", - "dev": true, - "bin": { - "tsc": "bin/tsc", - "tsserver": "bin/tsserver" - }, - "engines": { - "node": ">=12.20" - } - }, "packages/search": { "name": "@redis/search", "version": "1.1.3", @@ -8702,176 +8880,36 @@ "devDependencies": { "@istanbuljs/nyc-config-typescript": "^1.0.2", "@redis/test-utils": "*", - "@types/node": "^20.2.3", + "@types/node": "^20.5.3", "nyc": "^15.1.0", - "release-it": "^15.10.3", + "release-it": "^16.1.5", "source-map-support": "^0.5.21", "ts-node": "^10.9.1", - "typedoc": "^0.24.7", - "typescript": "^5.0.4" + "typedoc": "^0.24.8", + "typescript": "^5.1.6" }, "peerDependencies": { "@redis/client": "^1.0.0" } }, - "packages/search/node_modules/typescript": { - "version": "5.0.4", - "resolved": "https://registry.npmjs.org/typescript/-/typescript-5.0.4.tgz", - "integrity": "sha512-cW9T5W9xY37cc+jfEnaUvX91foxtHkza3Nw3wkoF4sSlKn0MONdkdEndig/qPBWXNkmplh3NzayQzCiHM4/hqw==", - "dev": true, - "bin": { - "tsc": "bin/tsc", - "tsserver": "bin/tsserver" - }, - "engines": { - "node": ">=12.20" - } - }, "packages/test-utils": { "name": "@redis/test-utils", "devDependencies": { "@istanbuljs/nyc-config-typescript": "^1.0.2", "@types/mocha": "^10.0.1", - "@types/node": "^20.2.3", + "@types/node": "^20.5.3", "@types/yargs": "^17.0.24", "mocha": "^10.2.0", "nyc": "^15.1.0", "source-map-support": "^0.5.21", "ts-node": "^10.9.1", - "typescript": "^5.0.4", + "typescript": "^5.1.6", "yargs": "^17.7.2" }, "peerDependencies": { "@redis/client": "^1.0.0" } }, - "packages/test-utils/node_modules/ansi-regex": { - "version": "5.0.1", - "resolved": "https://registry.npmjs.org/ansi-regex/-/ansi-regex-5.0.1.tgz", - "integrity": "sha512-quJQXlTSUGL2LH9SUXo8VwsY4soanhgo6LNSm84E1LBcE8s3O0wpdiRzyR9z/ZZJMlMWv37qOOb9pdJlMUEKFQ==", - "dev": true, - "engines": { - "node": ">=8" - } - }, - "packages/test-utils/node_modules/ansi-styles": { - "version": "4.3.0", - "resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-4.3.0.tgz", - "integrity": "sha512-zbB9rCJAT1rbjiVDb2hqKFHNYLxgtk8NURxZ3IZwD3F6NtxbXZQCnnSi1Lkx+IDohdPlFp222wVALIheZJQSEg==", - "dev": true, - "dependencies": { - "color-convert": "^2.0.1" - }, - "engines": { - "node": ">=8" - }, - "funding": { - "url": "https://github.com/chalk/ansi-styles?sponsor=1" - } - }, - "packages/test-utils/node_modules/cliui": { - "version": "8.0.1", - "resolved": "https://registry.npmjs.org/cliui/-/cliui-8.0.1.tgz", - "integrity": "sha512-BSeNnyus75C4//NQ9gQt1/csTXyo/8Sb+afLAkzAptFuMsod9HFokGNudZpi/oQV73hnVK+sR+5PVRMd+Dr7YQ==", - "dev": true, - "dependencies": { - "string-width": "^4.2.0", - "strip-ansi": "^6.0.1", - "wrap-ansi": "^7.0.0" - }, - "engines": { - "node": ">=12" - } - }, - "packages/test-utils/node_modules/emoji-regex": { - "version": "8.0.0", - "resolved": "https://registry.npmjs.org/emoji-regex/-/emoji-regex-8.0.0.tgz", - "integrity": "sha512-MSjYzcWNOA0ewAHpz0MxpYFvwg6yjy1NG3xteoqz644VCo/RPgnr1/GGt+ic3iJTzQ8Eu3TdM14SawnVUmGE6A==", - "dev": true - }, - "packages/test-utils/node_modules/string-width": { - "version": "4.2.3", - "resolved": "https://registry.npmjs.org/string-width/-/string-width-4.2.3.tgz", - "integrity": "sha512-wKyQRQpjJ0sIp62ErSZdGsjMJWsap5oRNihHhu6G7JVO/9jIB6UyevL+tXuOqrng8j/cxKTWyWUwvSTriiZz/g==", - "dev": true, - "dependencies": { - "emoji-regex": "^8.0.0", - "is-fullwidth-code-point": "^3.0.0", - "strip-ansi": "^6.0.1" - }, - "engines": { - "node": ">=8" - } - }, - "packages/test-utils/node_modules/strip-ansi": { - "version": "6.0.1", - "resolved": "https://registry.npmjs.org/strip-ansi/-/strip-ansi-6.0.1.tgz", - "integrity": "sha512-Y38VPSHcqkFrCpFnQ9vuSXmquuv5oXOKpGeT6aGrr3o3Gc9AlVa6JBfUSOCnbxGGZF+/0ooI7KrPuUSztUdU5A==", - "dev": true, - "dependencies": { - "ansi-regex": "^5.0.1" - }, - "engines": { - "node": ">=8" - } - }, - "packages/test-utils/node_modules/typescript": { - "version": "5.0.4", - "resolved": "https://registry.npmjs.org/typescript/-/typescript-5.0.4.tgz", - "integrity": "sha512-cW9T5W9xY37cc+jfEnaUvX91foxtHkza3Nw3wkoF4sSlKn0MONdkdEndig/qPBWXNkmplh3NzayQzCiHM4/hqw==", - "dev": true, - "bin": { - "tsc": "bin/tsc", - "tsserver": "bin/tsserver" - }, - "engines": { - "node": ">=12.20" - } - }, - "packages/test-utils/node_modules/wrap-ansi": { - "version": "7.0.0", - "resolved": "https://registry.npmjs.org/wrap-ansi/-/wrap-ansi-7.0.0.tgz", - "integrity": "sha512-YVGIj2kamLSTxw6NsZjoBxfSwsn0ycdesmc4p+Q21c5zPuZ1pl+NfxVdxPtdHvmNVOQ6XSYG4AUtyt/Fi7D16Q==", - "dev": true, - "dependencies": { - "ansi-styles": "^4.0.0", - "string-width": "^4.1.0", - "strip-ansi": "^6.0.0" - }, - "engines": { - "node": ">=10" - }, - "funding": { - "url": "https://github.com/chalk/wrap-ansi?sponsor=1" - } - }, - "packages/test-utils/node_modules/y18n": { - "version": "5.0.8", - "resolved": "https://registry.npmjs.org/y18n/-/y18n-5.0.8.tgz", - "integrity": "sha512-0pfFzegeDWJHJIAmTLRP2DwHjdF5s7jo9tuztdQxAhINCdvS+3nGINqPd00AphqJR/0LhANUS6/+7SCb98YOfA==", - "dev": true, - "engines": { - "node": ">=10" - } - }, - "packages/test-utils/node_modules/yargs": { - "version": "17.7.2", - "resolved": "https://registry.npmjs.org/yargs/-/yargs-17.7.2.tgz", - "integrity": "sha512-7dSzzRQ++CKnNI/krKnYRV7JKKPUXMEh61soaHKg9mrWEhzFWhFnxPxGl+69cD1Ou63C13NUPCnmIcrvqCuM6w==", - "dev": true, - "dependencies": { - "cliui": "^8.0.1", - "escalade": "^3.1.1", - "get-caller-file": "^2.0.5", - "require-directory": "^2.1.1", - "string-width": "^4.2.3", - "y18n": "^5.0.5", - "yargs-parser": "^21.1.1" - }, - "engines": { - "node": ">=12" - } - }, "packages/time-series": { "name": "@redis/time-series", "version": "1.0.4", @@ -8879,30 +8917,17 @@ "devDependencies": { "@istanbuljs/nyc-config-typescript": "^1.0.2", "@redis/test-utils": "*", - "@types/node": "^20.2.3", + "@types/node": "^20.5.3", "nyc": "^15.1.0", - "release-it": "^15.10.3", + "release-it": "^16.1.5", "source-map-support": "^0.5.21", "ts-node": "^10.9.1", - "typedoc": "^0.24.7", - "typescript": "^5.0.4" + "typedoc": "^0.24.8", + "typescript": "^5.1.6" }, "peerDependencies": { "@redis/client": "^1.0.0" } - }, - "packages/time-series/node_modules/typescript": { - "version": "5.0.4", - "resolved": "https://registry.npmjs.org/typescript/-/typescript-5.0.4.tgz", - "integrity": "sha512-cW9T5W9xY37cc+jfEnaUvX91foxtHkza3Nw3wkoF4sSlKn0MONdkdEndig/qPBWXNkmplh3NzayQzCiHM4/hqw==", - "dev": true, - "bin": { - "tsc": "bin/tsc", - "tsserver": "bin/tsserver" - }, - "engines": { - "node": ">=12.20" - } } } } diff --git a/package.json b/package.json index d7a07771d27..18223a8b926 100644 --- a/package.json +++ b/package.json @@ -31,10 +31,10 @@ "@redis/time-series": "1.0.4" }, "devDependencies": { - "@tsconfig/node14": "^1.0.3", - "gh-pages": "^5.0.0", - "release-it": "^15.6.0", - "typescript": "^4.9.5" + "@tsconfig/node14": "^14.1.0", + "gh-pages": "^6.0.0", + "release-it": "^16.1.5", + "typescript": "^5.1.6" }, "repository": { "type": "git", diff --git a/packages/bloom/package.json b/packages/bloom/package.json index e0ad7a450b6..5f76df0b104 100644 --- a/packages/bloom/package.json +++ b/packages/bloom/package.json @@ -18,13 +18,13 @@ "devDependencies": { "@istanbuljs/nyc-config-typescript": "^1.0.2", "@redis/test-utils": "*", - "@types/node": "^20.2.3", + "@types/node": "^20.5.3", "nyc": "^15.1.0", - "release-it": "^15.10.3", + "release-it": "^16.1.5", "source-map-support": "^0.5.21", "ts-node": "^10.9.1", - "typedoc": "^0.24.7", - "typescript": "^5.0.4" + "typedoc": "^0.24.8", + "typescript": "^5.1.6" }, "repository": { "type": "git", diff --git a/packages/client/lib/client/index.ts b/packages/client/lib/client/index.ts index 90cdbd09111..c06759f2eca 100644 --- a/packages/client/lib/client/index.ts +++ b/packages/client/lib/client/index.ts @@ -402,7 +402,7 @@ export default class RedisClient< (...args: Array) => (this as any).sendCommand(name, ...args); } - #pingTimer?: NodeJS.Timer; + #pingTimer?: NodeJS.Timeout; #setPingTimer(): void { if (!this.#options?.pingInterval || !this.#socket.isReady) return; diff --git a/packages/client/package.json b/packages/client/package.json index 81213710e41..c6a657997a8 100644 --- a/packages/client/package.json +++ b/packages/client/package.json @@ -21,19 +21,19 @@ "devDependencies": { "@istanbuljs/nyc-config-typescript": "^1.0.2", "@redis/test-utils": "*", - "@types/node": "^20.2.3", - "@types/sinon": "^10.0.15", + "@types/node": "^20.5.3", + "@types/sinon": "^10.0.16", "@types/yallist": "^4.0.1", - "@typescript-eslint/eslint-plugin": "^5.59.7", - "@typescript-eslint/parser": "^5.59.7", - "eslint": "^8.41.0", + "@typescript-eslint/eslint-plugin": "^6.4.1", + "@typescript-eslint/parser": "^6.4.1", + "eslint": "^8.47.0", "nyc": "^15.1.0", - "release-it": "^15.10.3", - "sinon": "^15.1.0", + "release-it": "^16.1.5", + "sinon": "^15.2.0", "source-map-support": "^0.5.21", "ts-node": "^10.9.1", - "typedoc": "^0.24.7", - "typescript": "^5.0.4" + "typedoc": "^0.24.8", + "typescript": "^5.1.6" }, "engines": { "node": ">=14" diff --git a/packages/graph/package.json b/packages/graph/package.json index 659f5cdb325..e79abf911ff 100644 --- a/packages/graph/package.json +++ b/packages/graph/package.json @@ -18,13 +18,13 @@ "devDependencies": { "@istanbuljs/nyc-config-typescript": "^1.0.2", "@redis/test-utils": "*", - "@types/node": "^20.2.3", + "@types/node": "^20.5.3", "nyc": "^15.1.0", - "release-it": "^15.10.3", + "release-it": "^16.1.5", "source-map-support": "^0.5.21", "ts-node": "^10.9.1", - "typedoc": "^0.24.7", - "typescript": "^5.0.4" + "typedoc": "^0.24.8", + "typescript": "^5.1.6" }, "repository": { "type": "git", diff --git a/packages/json/package.json b/packages/json/package.json index ae074be4f5c..68df3de12dd 100644 --- a/packages/json/package.json +++ b/packages/json/package.json @@ -18,13 +18,13 @@ "devDependencies": { "@istanbuljs/nyc-config-typescript": "^1.0.2", "@redis/test-utils": "*", - "@types/node": "^20.2.3", + "@types/node": "^20.5.3", "nyc": "^15.1.0", - "release-it": "^15.10.3", + "release-it": "^16.1.5", "source-map-support": "^0.5.21", "ts-node": "^10.9.1", - "typedoc": "^0.24.7", - "typescript": "^5.0.4" + "typedoc": "^0.24.8", + "typescript": "^5.1.6" }, "repository": { "type": "git", diff --git a/packages/search/package.json b/packages/search/package.json index ec59e5e57b1..0290490da13 100644 --- a/packages/search/package.json +++ b/packages/search/package.json @@ -18,13 +18,13 @@ "devDependencies": { "@istanbuljs/nyc-config-typescript": "^1.0.2", "@redis/test-utils": "*", - "@types/node": "^20.2.3", + "@types/node": "^20.5.3", "nyc": "^15.1.0", - "release-it": "^15.10.3", + "release-it": "^16.1.5", "source-map-support": "^0.5.21", "ts-node": "^10.9.1", - "typedoc": "^0.24.7", - "typescript": "^5.0.4" + "typedoc": "^0.24.8", + "typescript": "^5.1.6" }, "repository": { "type": "git", diff --git a/packages/test-utils/package.json b/packages/test-utils/package.json index d920052f3ea..7fb92b5fe41 100644 --- a/packages/test-utils/package.json +++ b/packages/test-utils/package.json @@ -12,13 +12,13 @@ "devDependencies": { "@istanbuljs/nyc-config-typescript": "^1.0.2", "@types/mocha": "^10.0.1", - "@types/node": "^20.2.3", + "@types/node": "^20.5.3", "@types/yargs": "^17.0.24", "mocha": "^10.2.0", "nyc": "^15.1.0", "source-map-support": "^0.5.21", "ts-node": "^10.9.1", - "typescript": "^5.0.4", + "typescript": "^5.1.6", "yargs": "^17.7.2" } } diff --git a/packages/time-series/package.json b/packages/time-series/package.json index de2d2adc6e5..1fee91c0c44 100644 --- a/packages/time-series/package.json +++ b/packages/time-series/package.json @@ -18,13 +18,13 @@ "devDependencies": { "@istanbuljs/nyc-config-typescript": "^1.0.2", "@redis/test-utils": "*", - "@types/node": "^20.2.3", + "@types/node": "^20.5.3", "nyc": "^15.1.0", - "release-it": "^15.10.3", + "release-it": "^16.1.5", "source-map-support": "^0.5.21", "ts-node": "^10.9.1", - "typedoc": "^0.24.7", - "typescript": "^5.0.4" + "typedoc": "^0.24.8", + "typescript": "^5.1.6" }, "repository": { "type": "git", From 504a05baade24348312f57a81d85056029ca66cf Mon Sep 17 00:00:00 2001 From: Leibale Date: Wed, 23 Aug 2023 15:16:51 -0400 Subject: [PATCH 1382/1748] Release client@1.5.9 --- packages/client/package.json | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/packages/client/package.json b/packages/client/package.json index c6a657997a8..7fb545de389 100644 --- a/packages/client/package.json +++ b/packages/client/package.json @@ -1,6 +1,6 @@ { "name": "@redis/client", - "version": "1.5.8", + "version": "1.5.9", "license": "MIT", "main": "./dist/index.js", "types": "./dist/index.d.ts", From afd8c235fb9b79f51bb6f803786aea660c074ca4 Mon Sep 17 00:00:00 2001 From: Leibale Date: Wed, 23 Aug 2023 15:30:19 -0400 Subject: [PATCH 1383/1748] Release time-series@1.0.5 --- packages/time-series/package.json | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/packages/time-series/package.json b/packages/time-series/package.json index 1fee91c0c44..39aabc45cc4 100644 --- a/packages/time-series/package.json +++ b/packages/time-series/package.json @@ -1,6 +1,6 @@ { "name": "@redis/time-series", - "version": "1.0.4", + "version": "1.0.5", "license": "MIT", "main": "./dist/index.js", "types": "./dist/index.d.ts", From b3fe14e224ada6b9da5542fa34ad67979973ef02 Mon Sep 17 00:00:00 2001 From: Leibale Date: Wed, 23 Aug 2023 15:32:22 -0400 Subject: [PATCH 1384/1748] ugprade subpackages --- package-lock.json | 8 ++++---- package.json | 4 ++-- 2 files changed, 6 insertions(+), 6 deletions(-) diff --git a/package-lock.json b/package-lock.json index 66ba3d75ed0..21aa12904a1 100644 --- a/package-lock.json +++ b/package-lock.json @@ -13,11 +13,11 @@ ], "dependencies": { "@redis/bloom": "1.2.0", - "@redis/client": "1.5.8", + "@redis/client": "1.5.9", "@redis/graph": "1.1.0", "@redis/json": "1.0.4", "@redis/search": "1.1.3", - "@redis/time-series": "1.0.4" + "@redis/time-series": "1.0.5" }, "devDependencies": { "@tsconfig/node14": "^14.1.0", @@ -8807,7 +8807,7 @@ }, "packages/client": { "name": "@redis/client", - "version": "1.5.8", + "version": "1.5.9", "license": "MIT", "dependencies": { "cluster-key-slot": "1.1.2", @@ -8912,7 +8912,7 @@ }, "packages/time-series": { "name": "@redis/time-series", - "version": "1.0.4", + "version": "1.0.5", "license": "MIT", "devDependencies": { "@istanbuljs/nyc-config-typescript": "^1.0.2", diff --git a/package.json b/package.json index 18223a8b926..29b27df567f 100644 --- a/package.json +++ b/package.json @@ -24,11 +24,11 @@ }, "dependencies": { "@redis/bloom": "1.2.0", - "@redis/client": "1.5.8", + "@redis/client": "1.5.9", "@redis/graph": "1.1.0", "@redis/json": "1.0.4", "@redis/search": "1.1.3", - "@redis/time-series": "1.0.4" + "@redis/time-series": "1.0.5" }, "devDependencies": { "@tsconfig/node14": "^14.1.0", From 294cbf8367295ac81cbe51ce2932493ab80493f1 Mon Sep 17 00:00:00 2001 From: Leibale Date: Wed, 23 Aug 2023 15:32:52 -0400 Subject: [PATCH 1385/1748] Release redis@4.6.8 --- package-lock.json | 4 ++-- package.json | 2 +- 2 files changed, 3 insertions(+), 3 deletions(-) diff --git a/package-lock.json b/package-lock.json index 21aa12904a1..599c0a91c77 100644 --- a/package-lock.json +++ b/package-lock.json @@ -1,12 +1,12 @@ { "name": "redis", - "version": "4.6.7", + "version": "4.6.8", "lockfileVersion": 3, "requires": true, "packages": { "": { "name": "redis", - "version": "4.6.7", + "version": "4.6.8", "license": "MIT", "workspaces": [ "./packages/*" diff --git a/package.json b/package.json index 29b27df567f..87cf2c5c5fe 100644 --- a/package.json +++ b/package.json @@ -1,7 +1,7 @@ { "name": "redis", "description": "A modern, high performance Redis client", - "version": "4.6.7", + "version": "4.6.8", "license": "MIT", "main": "./dist/index.js", "types": "./dist/index.d.ts", From 4976231f24ccb1ac7e814ce0129c4de242778f86 Mon Sep 17 00:00:00 2001 From: Leibale Date: Mon, 18 Sep 2023 17:23:01 -0400 Subject: [PATCH 1386/1748] upgrade deps, remove node 14 & 16 from tests matrix --- .github/workflows/tests.yml | 2 +- package-lock.json | 599 ++++++++++++++++-------------- package.json | 4 +- packages/bloom/package.json | 6 +- packages/client/package.json | 16 +- packages/graph/package.json | 6 +- packages/json/package.json | 6 +- packages/search/package.json | 6 +- packages/test-utils/package.json | 4 +- packages/time-series/package.json | 6 +- tsconfig.base.json | 2 +- 11 files changed, 344 insertions(+), 313 deletions(-) diff --git a/.github/workflows/tests.yml b/.github/workflows/tests.yml index 365e9f31d3c..4ba70e66040 100644 --- a/.github/workflows/tests.yml +++ b/.github/workflows/tests.yml @@ -16,7 +16,7 @@ jobs: strategy: fail-fast: false matrix: - node-version: ['14', '16', '18', '19'] + node-version: ['18', '19'] redis-version: ['5', '6.0', '6.2', '7.0'] steps: - uses: actions/checkout@v2.3.4 diff --git a/package-lock.json b/package-lock.json index 599c0a91c77..c08b693191e 100644 --- a/package-lock.json +++ b/package-lock.json @@ -20,10 +20,10 @@ "@redis/time-series": "1.0.5" }, "devDependencies": { - "@tsconfig/node14": "^14.1.0", + "@tsconfig/node18": "^18.2.2", "gh-pages": "^6.0.0", "release-it": "^16.1.5", - "typescript": "^5.1.6" + "typescript": "^5.2.2" } }, "node_modules/@aashutoshrathi/word-wrap": { @@ -49,12 +49,12 @@ } }, "node_modules/@babel/code-frame": { - "version": "7.22.10", - "resolved": "https://registry.npmjs.org/@babel/code-frame/-/code-frame-7.22.10.tgz", - "integrity": "sha512-/KKIMG4UEL35WmI9OlvMhurwtytjvXoFcGNrOvyG9zIzA8YmPjVtIZUf7b05+TPO7G7/GEmLHDaoCgACHl9hhA==", + "version": "7.22.13", + "resolved": "https://registry.npmjs.org/@babel/code-frame/-/code-frame-7.22.13.tgz", + "integrity": "sha512-XktuhWlJ5g+3TJXc5upd9Ks1HutSArik6jf2eAjYFyIOf4ej3RN+184cZbzDvbPnuTJIUhPKKJE3cIsYTiAT3w==", "dev": true, "dependencies": { - "@babel/highlight": "^7.22.10", + "@babel/highlight": "^7.22.13", "chalk": "^2.4.2" }, "engines": { @@ -112,34 +112,34 @@ } }, "node_modules/@babel/compat-data": { - "version": "7.22.9", - "resolved": "https://registry.npmjs.org/@babel/compat-data/-/compat-data-7.22.9.tgz", - "integrity": "sha512-5UamI7xkUcJ3i9qVDS+KFDEK8/7oJ55/sJMB1Ge7IEapr7KfdfV/HErR+koZwOfd+SgtFKOKRhRakdg++DcJpQ==", + "version": "7.22.20", + "resolved": "https://registry.npmjs.org/@babel/compat-data/-/compat-data-7.22.20.tgz", + "integrity": "sha512-BQYjKbpXjoXwFW5jGqiizJQQT/aC7pFm9Ok1OWssonuguICi264lbgMzRp2ZMmRSlfkX6DsWDDcsrctK8Rwfiw==", "dev": true, "engines": { "node": ">=6.9.0" } }, "node_modules/@babel/core": { - "version": "7.22.10", - "resolved": "https://registry.npmjs.org/@babel/core/-/core-7.22.10.tgz", - "integrity": "sha512-fTmqbbUBAwCcre6zPzNngvsI0aNrPZe77AeqvDxWM9Nm+04RrJ3CAmGHA9f7lJQY6ZMhRztNemy4uslDxTX4Qw==", + "version": "7.22.20", + "resolved": "https://registry.npmjs.org/@babel/core/-/core-7.22.20.tgz", + "integrity": "sha512-Y6jd1ahLubuYweD/zJH+vvOY141v4f9igNQAQ+MBgq9JlHS2iTsZKn1aMsb3vGccZsXI16VzTBw52Xx0DWmtnA==", "dev": true, "dependencies": { "@ampproject/remapping": "^2.2.0", - "@babel/code-frame": "^7.22.10", - "@babel/generator": "^7.22.10", - "@babel/helper-compilation-targets": "^7.22.10", - "@babel/helper-module-transforms": "^7.22.9", - "@babel/helpers": "^7.22.10", - "@babel/parser": "^7.22.10", - "@babel/template": "^7.22.5", - "@babel/traverse": "^7.22.10", - "@babel/types": "^7.22.10", + "@babel/code-frame": "^7.22.13", + "@babel/generator": "^7.22.15", + "@babel/helper-compilation-targets": "^7.22.15", + "@babel/helper-module-transforms": "^7.22.20", + "@babel/helpers": "^7.22.15", + "@babel/parser": "^7.22.16", + "@babel/template": "^7.22.15", + "@babel/traverse": "^7.22.20", + "@babel/types": "^7.22.19", "convert-source-map": "^1.7.0", "debug": "^4.1.0", "gensync": "^1.0.0-beta.2", - "json5": "^2.2.2", + "json5": "^2.2.3", "semver": "^6.3.1" }, "engines": { @@ -151,12 +151,12 @@ } }, "node_modules/@babel/generator": { - "version": "7.22.10", - "resolved": "https://registry.npmjs.org/@babel/generator/-/generator-7.22.10.tgz", - "integrity": "sha512-79KIf7YiWjjdZ81JnLujDRApWtl7BxTqWD88+FFdQEIOG8LJ0etDOM7CXuIgGJa55sGOwZVwuEsaLEm0PJ5/+A==", + "version": "7.22.15", + "resolved": "https://registry.npmjs.org/@babel/generator/-/generator-7.22.15.tgz", + "integrity": "sha512-Zu9oWARBqeVOW0dZOjXc3JObrzuqothQ3y/n1kUtrjCoCPLkXUwMvOo/F/TCfoHMbWIFlWwpZtkZVb9ga4U2pA==", "dev": true, "dependencies": { - "@babel/types": "^7.22.10", + "@babel/types": "^7.22.15", "@jridgewell/gen-mapping": "^0.3.2", "@jridgewell/trace-mapping": "^0.3.17", "jsesc": "^2.5.1" @@ -166,13 +166,13 @@ } }, "node_modules/@babel/helper-compilation-targets": { - "version": "7.22.10", - "resolved": "https://registry.npmjs.org/@babel/helper-compilation-targets/-/helper-compilation-targets-7.22.10.tgz", - "integrity": "sha512-JMSwHD4J7SLod0idLq5PKgI+6g/hLD/iuWBq08ZX49xE14VpVEojJ5rHWptpirV2j020MvypRLAXAO50igCJ5Q==", + "version": "7.22.15", + "resolved": "https://registry.npmjs.org/@babel/helper-compilation-targets/-/helper-compilation-targets-7.22.15.tgz", + "integrity": "sha512-y6EEzULok0Qvz8yyLkCvVX+02ic+By2UdOhylwUOvOn9dvYc9mKICJuuU1n1XBI02YWsNsnrY1kc6DVbjcXbtw==", "dev": true, "dependencies": { "@babel/compat-data": "^7.22.9", - "@babel/helper-validator-option": "^7.22.5", + "@babel/helper-validator-option": "^7.22.15", "browserslist": "^4.21.9", "lru-cache": "^5.1.1", "semver": "^6.3.1" @@ -197,9 +197,9 @@ "dev": true }, "node_modules/@babel/helper-environment-visitor": { - "version": "7.22.5", - "resolved": "https://registry.npmjs.org/@babel/helper-environment-visitor/-/helper-environment-visitor-7.22.5.tgz", - "integrity": "sha512-XGmhECfVA/5sAt+H+xpSg0mfrHq6FzNr9Oxh7PSEBBRUb/mL7Kz3NICXb194rCqAEdxkhPT1a88teizAFyvk8Q==", + "version": "7.22.20", + "resolved": "https://registry.npmjs.org/@babel/helper-environment-visitor/-/helper-environment-visitor-7.22.20.tgz", + "integrity": "sha512-zfedSIzFhat/gFhWfHtgWvlec0nqB9YEIVrpuwjruLlXfUSnA8cJB0miHKwqDnQ7d32aKo2xt88/xZptwxbfhA==", "dev": true, "engines": { "node": ">=6.9.0" @@ -231,28 +231,28 @@ } }, "node_modules/@babel/helper-module-imports": { - "version": "7.22.5", - "resolved": "https://registry.npmjs.org/@babel/helper-module-imports/-/helper-module-imports-7.22.5.tgz", - "integrity": "sha512-8Dl6+HD/cKifutF5qGd/8ZJi84QeAKh+CEe1sBzz8UayBBGg1dAIJrdHOcOM5b2MpzWL2yuotJTtGjETq0qjXg==", + "version": "7.22.15", + "resolved": "https://registry.npmjs.org/@babel/helper-module-imports/-/helper-module-imports-7.22.15.tgz", + "integrity": "sha512-0pYVBnDKZO2fnSPCrgM/6WMc7eS20Fbok+0r88fp+YtWVLZrp4CkafFGIp+W0VKw4a22sgebPT99y+FDNMdP4w==", "dev": true, "dependencies": { - "@babel/types": "^7.22.5" + "@babel/types": "^7.22.15" }, "engines": { "node": ">=6.9.0" } }, "node_modules/@babel/helper-module-transforms": { - "version": "7.22.9", - "resolved": "https://registry.npmjs.org/@babel/helper-module-transforms/-/helper-module-transforms-7.22.9.tgz", - "integrity": "sha512-t+WA2Xn5K+rTeGtC8jCsdAH52bjggG5TKRuRrAGNM/mjIbO4GxvlLMFOEz9wXY5I2XQ60PMFsAG2WIcG82dQMQ==", + "version": "7.22.20", + "resolved": "https://registry.npmjs.org/@babel/helper-module-transforms/-/helper-module-transforms-7.22.20.tgz", + "integrity": "sha512-dLT7JVWIUUxKOs1UnJUBR3S70YK+pKX6AbJgB2vMIvEkZkrfJDbYDJesnPshtKV4LhDOR3Oc5YULeDizRek+5A==", "dev": true, "dependencies": { - "@babel/helper-environment-visitor": "^7.22.5", - "@babel/helper-module-imports": "^7.22.5", + "@babel/helper-environment-visitor": "^7.22.20", + "@babel/helper-module-imports": "^7.22.15", "@babel/helper-simple-access": "^7.22.5", "@babel/helper-split-export-declaration": "^7.22.6", - "@babel/helper-validator-identifier": "^7.22.5" + "@babel/helper-validator-identifier": "^7.22.20" }, "engines": { "node": ">=6.9.0" @@ -295,44 +295,44 @@ } }, "node_modules/@babel/helper-validator-identifier": { - "version": "7.22.5", - "resolved": "https://registry.npmjs.org/@babel/helper-validator-identifier/-/helper-validator-identifier-7.22.5.tgz", - "integrity": "sha512-aJXu+6lErq8ltp+JhkJUfk1MTGyuA4v7f3pA+BJ5HLfNC6nAQ0Cpi9uOquUj8Hehg0aUiHzWQbOVJGao6ztBAQ==", + "version": "7.22.20", + "resolved": "https://registry.npmjs.org/@babel/helper-validator-identifier/-/helper-validator-identifier-7.22.20.tgz", + "integrity": "sha512-Y4OZ+ytlatR8AI+8KZfKuL5urKp7qey08ha31L8b3BwewJAoJamTzyvxPR/5D+KkdJCGPq/+8TukHBlY10FX9A==", "dev": true, "engines": { "node": ">=6.9.0" } }, "node_modules/@babel/helper-validator-option": { - "version": "7.22.5", - "resolved": "https://registry.npmjs.org/@babel/helper-validator-option/-/helper-validator-option-7.22.5.tgz", - "integrity": "sha512-R3oB6xlIVKUnxNUxbmgq7pKjxpru24zlimpE8WK47fACIlM0II/Hm1RS8IaOI7NgCr6LNS+jl5l75m20npAziw==", + "version": "7.22.15", + "resolved": "https://registry.npmjs.org/@babel/helper-validator-option/-/helper-validator-option-7.22.15.tgz", + "integrity": "sha512-bMn7RmyFjY/mdECUbgn9eoSY4vqvacUnS9i9vGAGttgFWesO6B4CYWA7XlpbWgBt71iv/hfbPlynohStqnu5hA==", "dev": true, "engines": { "node": ">=6.9.0" } }, "node_modules/@babel/helpers": { - "version": "7.22.10", - "resolved": "https://registry.npmjs.org/@babel/helpers/-/helpers-7.22.10.tgz", - "integrity": "sha512-a41J4NW8HyZa1I1vAndrraTlPZ/eZoga2ZgS7fEr0tZJGVU4xqdE80CEm0CcNjha5EZ8fTBYLKHF0kqDUuAwQw==", + "version": "7.22.15", + "resolved": "https://registry.npmjs.org/@babel/helpers/-/helpers-7.22.15.tgz", + "integrity": "sha512-7pAjK0aSdxOwR+CcYAqgWOGy5dcfvzsTIfFTb2odQqW47MDfv14UaJDY6eng8ylM2EaeKXdxaSWESbkmaQHTmw==", "dev": true, "dependencies": { - "@babel/template": "^7.22.5", - "@babel/traverse": "^7.22.10", - "@babel/types": "^7.22.10" + "@babel/template": "^7.22.15", + "@babel/traverse": "^7.22.15", + "@babel/types": "^7.22.15" }, "engines": { "node": ">=6.9.0" } }, "node_modules/@babel/highlight": { - "version": "7.22.10", - "resolved": "https://registry.npmjs.org/@babel/highlight/-/highlight-7.22.10.tgz", - "integrity": "sha512-78aUtVcT7MUscr0K5mIEnkwxPE0MaxkR5RxRwuHaQ+JuU5AmTPhY+do2mdzVTnIJJpyBglql2pehuBIWHug+WQ==", + "version": "7.22.20", + "resolved": "https://registry.npmjs.org/@babel/highlight/-/highlight-7.22.20.tgz", + "integrity": "sha512-dkdMCN3py0+ksCgYmGG8jKeGA/8Tk+gJwSYYlFGxG5lmhfKNoAy004YpLxpS1W2J8m/EK2Ew+yOs9pVRwO89mg==", "dev": true, "dependencies": { - "@babel/helper-validator-identifier": "^7.22.5", + "@babel/helper-validator-identifier": "^7.22.20", "chalk": "^2.4.2", "js-tokens": "^4.0.0" }, @@ -391,9 +391,9 @@ } }, "node_modules/@babel/parser": { - "version": "7.22.10", - "resolved": "https://registry.npmjs.org/@babel/parser/-/parser-7.22.10.tgz", - "integrity": "sha512-lNbdGsQb9ekfsnjFGhEiF4hfFqGgfOP3H3d27re3n+CGhNuTSUEQdfWk556sTLNTloczcdM5TYF2LhzmDQKyvQ==", + "version": "7.22.16", + "resolved": "https://registry.npmjs.org/@babel/parser/-/parser-7.22.16.tgz", + "integrity": "sha512-+gPfKv8UWeKKeJTUxe59+OobVcrYHETCsORl61EmSkmgymguYk/X5bp7GuUIXaFsc6y++v8ZxPsLSSuujqDphA==", "dev": true, "bin": { "parser": "bin/babel-parser.js" @@ -403,33 +403,33 @@ } }, "node_modules/@babel/template": { - "version": "7.22.5", - "resolved": "https://registry.npmjs.org/@babel/template/-/template-7.22.5.tgz", - "integrity": "sha512-X7yV7eiwAxdj9k94NEylvbVHLiVG1nvzCV2EAowhxLTwODV1jl9UzZ48leOC0sH7OnuHrIkllaBgneUykIcZaw==", + "version": "7.22.15", + "resolved": "https://registry.npmjs.org/@babel/template/-/template-7.22.15.tgz", + "integrity": "sha512-QPErUVm4uyJa60rkI73qneDacvdvzxshT3kksGqlGWYdOTIUOwJ7RDUL8sGqslY1uXWSL6xMFKEXDS3ox2uF0w==", "dev": true, "dependencies": { - "@babel/code-frame": "^7.22.5", - "@babel/parser": "^7.22.5", - "@babel/types": "^7.22.5" + "@babel/code-frame": "^7.22.13", + "@babel/parser": "^7.22.15", + "@babel/types": "^7.22.15" }, "engines": { "node": ">=6.9.0" } }, "node_modules/@babel/traverse": { - "version": "7.22.10", - "resolved": "https://registry.npmjs.org/@babel/traverse/-/traverse-7.22.10.tgz", - "integrity": "sha512-Q/urqV4pRByiNNpb/f5OSv28ZlGJiFiiTh+GAHktbIrkPhPbl90+uW6SmpoLyZqutrg9AEaEf3Q/ZBRHBXgxig==", + "version": "7.22.20", + "resolved": "https://registry.npmjs.org/@babel/traverse/-/traverse-7.22.20.tgz", + "integrity": "sha512-eU260mPZbU7mZ0N+X10pxXhQFMGTeLb9eFS0mxehS8HZp9o1uSnFeWQuG1UPrlxgA7QoUzFhOnilHDp0AXCyHw==", "dev": true, "dependencies": { - "@babel/code-frame": "^7.22.10", - "@babel/generator": "^7.22.10", - "@babel/helper-environment-visitor": "^7.22.5", + "@babel/code-frame": "^7.22.13", + "@babel/generator": "^7.22.15", + "@babel/helper-environment-visitor": "^7.22.20", "@babel/helper-function-name": "^7.22.5", "@babel/helper-hoist-variables": "^7.22.5", "@babel/helper-split-export-declaration": "^7.22.6", - "@babel/parser": "^7.22.10", - "@babel/types": "^7.22.10", + "@babel/parser": "^7.22.16", + "@babel/types": "^7.22.19", "debug": "^4.1.0", "globals": "^11.1.0" }, @@ -447,13 +447,13 @@ } }, "node_modules/@babel/types": { - "version": "7.22.10", - "resolved": "https://registry.npmjs.org/@babel/types/-/types-7.22.10.tgz", - "integrity": "sha512-obaoigiLrlDZ7TUQln/8m4mSqIW2QFeOrCQc9r+xsaHGNoplVNYlRVpsfE8Vj35GEm2ZH4ZhrNYogs/3fj85kg==", + "version": "7.22.19", + "resolved": "https://registry.npmjs.org/@babel/types/-/types-7.22.19.tgz", + "integrity": "sha512-P7LAw/LbojPzkgp5oznjE6tQEIWbp4PkkfrZDINTro9zgBRtI324/EYsiSI7lhPbpIQ+DCeR2NNmMWANGGfZsg==", "dev": true, "dependencies": { "@babel/helper-string-parser": "^7.22.5", - "@babel/helper-validator-identifier": "^7.22.5", + "@babel/helper-validator-identifier": "^7.22.19", "to-fast-properties": "^2.0.0" }, "engines": { @@ -498,9 +498,9 @@ } }, "node_modules/@eslint-community/regexpp": { - "version": "4.7.0", - "resolved": "https://registry.npmjs.org/@eslint-community/regexpp/-/regexpp-4.7.0.tgz", - "integrity": "sha512-+HencqxU7CFJnQb7IKtuNBqS6Yx3Tz4kOL8BJXo+JyeiBm5MEX6pO8onXDkjrkCRlfYXS1Axro15ZjVFe9YgsA==", + "version": "4.8.1", + "resolved": "https://registry.npmjs.org/@eslint-community/regexpp/-/regexpp-4.8.1.tgz", + "integrity": "sha512-PWiOzLIUAjN/w5K17PoF4n6sKBw0gqLHPhywmYHP4t1VFQQVYeb1yWsJwnMVEMl3tUHME7X/SJPZLmtG7XBDxQ==", "dev": true, "engines": { "node": "^12.0.0 || ^14.0.0 || >=16.0.0" @@ -542,18 +542,18 @@ } }, "node_modules/@eslint/js": { - "version": "8.47.0", - "resolved": "https://registry.npmjs.org/@eslint/js/-/js-8.47.0.tgz", - "integrity": "sha512-P6omY1zv5MItm93kLM8s2vr1HICJH8v0dvddDhysbIuZ+vcjOHg5Zbkf1mTkcmi2JA9oBG2anOkRnW8WJTS8Og==", + "version": "8.49.0", + "resolved": "https://registry.npmjs.org/@eslint/js/-/js-8.49.0.tgz", + "integrity": "sha512-1S8uAY/MTJqVx0SC4epBq+N2yhuwtNwLbJYNZyhL2pO1ZVKn5HFXav5T41Ryzy9K9V7ZId2JB2oy/W4aCd9/2w==", "dev": true, "engines": { "node": "^12.22.0 || ^14.17.0 || >=16.0.0" } }, "node_modules/@humanwhocodes/config-array": { - "version": "0.11.10", - "resolved": "https://registry.npmjs.org/@humanwhocodes/config-array/-/config-array-0.11.10.tgz", - "integrity": "sha512-KVVjQmNUepDVGXNuoRRdmmEjruj0KfiGSbS8LVc12LMsWDQzRXJ0qdhN8L8uUigKpfEHRhlaQFY0ib1tnUbNeQ==", + "version": "0.11.11", + "resolved": "https://registry.npmjs.org/@humanwhocodes/config-array/-/config-array-0.11.11.tgz", + "integrity": "sha512-N2brEuAadi0CcdeMXUkhbZB84eskAc8MEX1By6qEchoVywSgXPIjou4rYsl0V3Hj0ZnuGycGCjdNgockbzeWNA==", "dev": true, "dependencies": { "@humanwhocodes/object-schema": "^1.2.1", @@ -1108,9 +1108,9 @@ "dev": true }, "node_modules/@tsconfig/node14": { - "version": "14.1.0", - "resolved": "https://registry.npmjs.org/@tsconfig/node14/-/node14-14.1.0.tgz", - "integrity": "sha512-VmsCG04YR58ciHBeJKBDNMWWfYbyP8FekWVuTlpstaUPlat1D0x/tXzkWP7yCMU0eSz9V4OZU0LBWTFJ3xZf6w==", + "version": "1.0.3", + "resolved": "https://registry.npmjs.org/@tsconfig/node14/-/node14-1.0.3.tgz", + "integrity": "sha512-ysT8mhdixWK6Hw3i1V2AeRqZ5WfXg1G43mqoYlM2nc6388Fq5jcXyr5mRsqViLx/GJYdoL0bfXD8nmF+Zn/Iow==", "dev": true }, "node_modules/@tsconfig/node16": { @@ -1119,16 +1119,22 @@ "integrity": "sha512-vxhUy4J8lyeyinH7Azl1pdd43GJhZH/tP2weN8TntQblOY+A0XbT8DJk1/oCPuOOyg/Ja757rG0CgHcWC8OfMA==", "dev": true }, + "node_modules/@tsconfig/node18": { + "version": "18.2.2", + "resolved": "https://registry.npmjs.org/@tsconfig/node18/-/node18-18.2.2.tgz", + "integrity": "sha512-d6McJeGsuoRlwWZmVIeE8CUA27lu6jLjvv1JzqmpsytOYYbVi1tHZEnwCNVOXnj4pyLvneZlFlpXUK+X9wBWyw==", + "dev": true + }, "node_modules/@types/http-cache-semantics": { - "version": "4.0.1", - "resolved": "https://registry.npmjs.org/@types/http-cache-semantics/-/http-cache-semantics-4.0.1.tgz", - "integrity": "sha512-SZs7ekbP8CN0txVG2xVRH6EgKmEm31BOxA07vkFaETzZz1xh+cbt8BcI0slpymvwhx5dlFnQG2rTlPVQn+iRPQ==", + "version": "4.0.2", + "resolved": "https://registry.npmjs.org/@types/http-cache-semantics/-/http-cache-semantics-4.0.2.tgz", + "integrity": "sha512-FD+nQWA2zJjh4L9+pFXqWOi0Hs1ryBCfI+985NjluQ1p8EYtoLvjLOKidXBtZ4/IcxDX4o8/E8qDS3540tNliw==", "dev": true }, "node_modules/@types/json-schema": { - "version": "7.0.12", - "resolved": "https://registry.npmjs.org/@types/json-schema/-/json-schema-7.0.12.tgz", - "integrity": "sha512-Hr5Jfhc9eYOQNPYO5WLDq/n4jqijdHNlDXjuAQkkt+mWdQR+XJToOHrsD4cPaMXpn6KO7y2+wM8AZEs8VpBLVA==", + "version": "7.0.13", + "resolved": "https://registry.npmjs.org/@types/json-schema/-/json-schema-7.0.13.tgz", + "integrity": "sha512-RbSSoHliUbnXj3ny0CNFOoxrIDV6SUGyStHsvDqosw6CkdPV8TtWGlfecuK4ToyMEAql6pzNxgCFKanovUzlgQ==", "dev": true }, "node_modules/@types/mocha": { @@ -1138,15 +1144,15 @@ "dev": true }, "node_modules/@types/node": { - "version": "20.5.3", - "resolved": "https://registry.npmjs.org/@types/node/-/node-20.5.3.tgz", - "integrity": "sha512-ITI7rbWczR8a/S6qjAW7DMqxqFMjjTo61qZVWJ1ubPvbIQsL5D/TvwjYEalM8Kthpe3hTzOGrF2TGbAu2uyqeA==", + "version": "20.6.2", + "resolved": "https://registry.npmjs.org/@types/node/-/node-20.6.2.tgz", + "integrity": "sha512-Y+/1vGBHV/cYk6OI1Na/LHzwnlNCAfU3ZNGrc1LdRe/LAIbdDPTTv/HU3M7yXN448aTVDq3eKRm2cg7iKLb8gw==", "dev": true }, "node_modules/@types/semver": { - "version": "7.5.0", - "resolved": "https://registry.npmjs.org/@types/semver/-/semver-7.5.0.tgz", - "integrity": "sha512-G8hZ6XJiHnuhQKR7ZmysCeJWE08o8T0AXtk5darsCaTVsYZhhgUrq53jizaR2FvsoeCwJhlmwTjkXBY5Pn/ZHw==", + "version": "7.5.2", + "resolved": "https://registry.npmjs.org/@types/semver/-/semver-7.5.2.tgz", + "integrity": "sha512-7aqorHYgdNO4DM36stTiGO3DvKoex9TQRwsJU6vMaFGyqpBA1MNZkz+PG3gaNUPpTAOYhT1WR7M1JyA3fbS9Cw==", "dev": true }, "node_modules/@types/sinon": { @@ -1186,16 +1192,16 @@ "dev": true }, "node_modules/@typescript-eslint/eslint-plugin": { - "version": "6.4.1", - "resolved": "https://registry.npmjs.org/@typescript-eslint/eslint-plugin/-/eslint-plugin-6.4.1.tgz", - "integrity": "sha512-3F5PtBzUW0dYlq77Lcqo13fv+58KDwUib3BddilE8ajPJT+faGgxmI9Sw+I8ZS22BYwoir9ZhNXcLi+S+I2bkw==", + "version": "6.7.2", + "resolved": "https://registry.npmjs.org/@typescript-eslint/eslint-plugin/-/eslint-plugin-6.7.2.tgz", + "integrity": "sha512-ooaHxlmSgZTM6CHYAFRlifqh1OAr3PAQEwi7lhYhaegbnXrnh7CDcHmc3+ihhbQC7H0i4JF0psI5ehzkF6Yl6Q==", "dev": true, "dependencies": { "@eslint-community/regexpp": "^4.5.1", - "@typescript-eslint/scope-manager": "6.4.1", - "@typescript-eslint/type-utils": "6.4.1", - "@typescript-eslint/utils": "6.4.1", - "@typescript-eslint/visitor-keys": "6.4.1", + "@typescript-eslint/scope-manager": "6.7.2", + "@typescript-eslint/type-utils": "6.7.2", + "@typescript-eslint/utils": "6.7.2", + "@typescript-eslint/visitor-keys": "6.7.2", "debug": "^4.3.4", "graphemer": "^1.4.0", "ignore": "^5.2.4", @@ -1248,15 +1254,15 @@ } }, "node_modules/@typescript-eslint/parser": { - "version": "6.4.1", - "resolved": "https://registry.npmjs.org/@typescript-eslint/parser/-/parser-6.4.1.tgz", - "integrity": "sha512-610G6KHymg9V7EqOaNBMtD1GgpAmGROsmfHJPXNLCU9bfIuLrkdOygltK784F6Crboyd5tBFayPB7Sf0McrQwg==", + "version": "6.7.2", + "resolved": "https://registry.npmjs.org/@typescript-eslint/parser/-/parser-6.7.2.tgz", + "integrity": "sha512-KA3E4ox0ws+SPyxQf9iSI25R6b4Ne78ORhNHeVKrPQnoYsb9UhieoiRoJgrzgEeKGOXhcY1i8YtOeCHHTDa6Fw==", "dev": true, "dependencies": { - "@typescript-eslint/scope-manager": "6.4.1", - "@typescript-eslint/types": "6.4.1", - "@typescript-eslint/typescript-estree": "6.4.1", - "@typescript-eslint/visitor-keys": "6.4.1", + "@typescript-eslint/scope-manager": "6.7.2", + "@typescript-eslint/types": "6.7.2", + "@typescript-eslint/typescript-estree": "6.7.2", + "@typescript-eslint/visitor-keys": "6.7.2", "debug": "^4.3.4" }, "engines": { @@ -1276,13 +1282,13 @@ } }, "node_modules/@typescript-eslint/scope-manager": { - "version": "6.4.1", - "resolved": "https://registry.npmjs.org/@typescript-eslint/scope-manager/-/scope-manager-6.4.1.tgz", - "integrity": "sha512-p/OavqOQfm4/Hdrr7kvacOSFjwQ2rrDVJRPxt/o0TOWdFnjJptnjnZ+sYDR7fi4OimvIuKp+2LCkc+rt9fIW+A==", + "version": "6.7.2", + "resolved": "https://registry.npmjs.org/@typescript-eslint/scope-manager/-/scope-manager-6.7.2.tgz", + "integrity": "sha512-bgi6plgyZjEqapr7u2mhxGR6E8WCzKNUFWNh6fkpVe9+yzRZeYtDTbsIBzKbcxI+r1qVWt6VIoMSNZ4r2A+6Yw==", "dev": true, "dependencies": { - "@typescript-eslint/types": "6.4.1", - "@typescript-eslint/visitor-keys": "6.4.1" + "@typescript-eslint/types": "6.7.2", + "@typescript-eslint/visitor-keys": "6.7.2" }, "engines": { "node": "^16.0.0 || >=18.0.0" @@ -1293,13 +1299,13 @@ } }, "node_modules/@typescript-eslint/type-utils": { - "version": "6.4.1", - "resolved": "https://registry.npmjs.org/@typescript-eslint/type-utils/-/type-utils-6.4.1.tgz", - "integrity": "sha512-7ON8M8NXh73SGZ5XvIqWHjgX2f+vvaOarNliGhjrJnv1vdjG0LVIz+ToYfPirOoBi56jxAKLfsLm40+RvxVVXA==", + "version": "6.7.2", + "resolved": "https://registry.npmjs.org/@typescript-eslint/type-utils/-/type-utils-6.7.2.tgz", + "integrity": "sha512-36F4fOYIROYRl0qj95dYKx6kybddLtsbmPIYNK0OBeXv2j9L5nZ17j9jmfy+bIDHKQgn2EZX+cofsqi8NPATBQ==", "dev": true, "dependencies": { - "@typescript-eslint/typescript-estree": "6.4.1", - "@typescript-eslint/utils": "6.4.1", + "@typescript-eslint/typescript-estree": "6.7.2", + "@typescript-eslint/utils": "6.7.2", "debug": "^4.3.4", "ts-api-utils": "^1.0.1" }, @@ -1320,9 +1326,9 @@ } }, "node_modules/@typescript-eslint/types": { - "version": "6.4.1", - "resolved": "https://registry.npmjs.org/@typescript-eslint/types/-/types-6.4.1.tgz", - "integrity": "sha512-zAAopbNuYu++ijY1GV2ylCsQsi3B8QvfPHVqhGdDcbx/NK5lkqMnCGU53amAjccSpk+LfeONxwzUhDzArSfZJg==", + "version": "6.7.2", + "resolved": "https://registry.npmjs.org/@typescript-eslint/types/-/types-6.7.2.tgz", + "integrity": "sha512-flJYwMYgnUNDAN9/GAI3l8+wTmvTYdv64fcH8aoJK76Y+1FCZ08RtI5zDerM/FYT5DMkAc+19E4aLmd5KqdFyg==", "dev": true, "engines": { "node": "^16.0.0 || >=18.0.0" @@ -1333,13 +1339,13 @@ } }, "node_modules/@typescript-eslint/typescript-estree": { - "version": "6.4.1", - "resolved": "https://registry.npmjs.org/@typescript-eslint/typescript-estree/-/typescript-estree-6.4.1.tgz", - "integrity": "sha512-xF6Y7SatVE/OyV93h1xGgfOkHr2iXuo8ip0gbfzaKeGGuKiAnzS+HtVhSPx8Www243bwlW8IF7X0/B62SzFftg==", + "version": "6.7.2", + "resolved": "https://registry.npmjs.org/@typescript-eslint/typescript-estree/-/typescript-estree-6.7.2.tgz", + "integrity": "sha512-kiJKVMLkoSciGyFU0TOY0fRxnp9qq1AzVOHNeN1+B9erKFCJ4Z8WdjAkKQPP+b1pWStGFqezMLltxO+308dJTQ==", "dev": true, "dependencies": { - "@typescript-eslint/types": "6.4.1", - "@typescript-eslint/visitor-keys": "6.4.1", + "@typescript-eslint/types": "6.7.2", + "@typescript-eslint/visitor-keys": "6.7.2", "debug": "^4.3.4", "globby": "^11.1.0", "is-glob": "^4.0.3", @@ -1425,17 +1431,17 @@ } }, "node_modules/@typescript-eslint/utils": { - "version": "6.4.1", - "resolved": "https://registry.npmjs.org/@typescript-eslint/utils/-/utils-6.4.1.tgz", - "integrity": "sha512-F/6r2RieNeorU0zhqZNv89s9bDZSovv3bZQpUNOmmQK1L80/cV4KEu95YUJWi75u5PhboFoKUJBnZ4FQcoqhDw==", + "version": "6.7.2", + "resolved": "https://registry.npmjs.org/@typescript-eslint/utils/-/utils-6.7.2.tgz", + "integrity": "sha512-ZCcBJug/TS6fXRTsoTkgnsvyWSiXwMNiPzBUani7hDidBdj1779qwM1FIAmpH4lvlOZNF3EScsxxuGifjpLSWQ==", "dev": true, "dependencies": { "@eslint-community/eslint-utils": "^4.4.0", "@types/json-schema": "^7.0.12", "@types/semver": "^7.5.0", - "@typescript-eslint/scope-manager": "6.4.1", - "@typescript-eslint/types": "6.4.1", - "@typescript-eslint/typescript-estree": "6.4.1", + "@typescript-eslint/scope-manager": "6.7.2", + "@typescript-eslint/types": "6.7.2", + "@typescript-eslint/typescript-estree": "6.7.2", "semver": "^7.5.4" }, "engines": { @@ -1477,12 +1483,12 @@ } }, "node_modules/@typescript-eslint/visitor-keys": { - "version": "6.4.1", - "resolved": "https://registry.npmjs.org/@typescript-eslint/visitor-keys/-/visitor-keys-6.4.1.tgz", - "integrity": "sha512-y/TyRJsbZPkJIZQXrHfdnxVnxyKegnpEvnRGNam7s3TRR2ykGefEWOhaef00/UUN3IZxizS7BTO3svd3lCOJRQ==", + "version": "6.7.2", + "resolved": "https://registry.npmjs.org/@typescript-eslint/visitor-keys/-/visitor-keys-6.7.2.tgz", + "integrity": "sha512-uVw9VIMFBUTz8rIeaUT3fFe8xIUx8r4ywAdlQv1ifH+6acn/XF8Y6rwJ7XNmkNMDrTW+7+vxFFPIF40nJCVsMQ==", "dev": true, "dependencies": { - "@typescript-eslint/types": "6.4.1", + "@typescript-eslint/types": "6.7.2", "eslint-visitor-keys": "^3.4.1" }, "engines": { @@ -1705,14 +1711,14 @@ } }, "node_modules/array.prototype.map": { - "version": "1.0.5", - "resolved": "https://registry.npmjs.org/array.prototype.map/-/array.prototype.map-1.0.5.tgz", - "integrity": "sha512-gfaKntvwqYIuC7mLLyv2wzZIJqrRhn5PZ9EfFejSx6a78sV7iDsGpG9P+3oUPtm1Rerqm6nrKS4FYuTIvWfo3g==", + "version": "1.0.6", + "resolved": "https://registry.npmjs.org/array.prototype.map/-/array.prototype.map-1.0.6.tgz", + "integrity": "sha512-nK1psgF2cXqP3wSyCSq0Hc7zwNq3sfljQqaG27r/7a7ooNUnn5nGq6yYWyks9jMO5EoFQ0ax80hSg6oXSRNXaw==", "dev": true, "dependencies": { "call-bind": "^1.0.2", - "define-properties": "^1.1.4", - "es-abstract": "^1.20.4", + "define-properties": "^1.2.0", + "es-abstract": "^1.22.1", "es-array-method-boxes-properly": "^1.0.0", "is-string": "^1.0.7" }, @@ -1724,14 +1730,15 @@ } }, "node_modules/arraybuffer.prototype.slice": { - "version": "1.0.1", - "resolved": "https://registry.npmjs.org/arraybuffer.prototype.slice/-/arraybuffer.prototype.slice-1.0.1.tgz", - "integrity": "sha512-09x0ZWFEjj4WD8PDbykUwo3t9arLn8NIzmmYEJFpYekOAQjpkGSyrQhNoRTcwwcFRu+ycWF78QZ63oWTqSjBcw==", + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/arraybuffer.prototype.slice/-/arraybuffer.prototype.slice-1.0.2.tgz", + "integrity": "sha512-yMBKppFur/fbHu9/6USUe03bZ4knMYiwFBcyiaXB8Go0qNehwX6inYPzK9U0NeQvGxKthcmHcaR8P5MStSRBAw==", "dev": true, "dependencies": { "array-buffer-byte-length": "^1.0.0", "call-bind": "^1.0.2", "define-properties": "^1.2.0", + "es-abstract": "^1.22.1", "get-intrinsic": "^1.2.1", "is-array-buffer": "^3.0.2", "is-shared-array-buffer": "^1.0.2" @@ -2159,9 +2166,9 @@ } }, "node_modules/caniuse-lite": { - "version": "1.0.30001522", - "resolved": "https://registry.npmjs.org/caniuse-lite/-/caniuse-lite-1.0.30001522.tgz", - "integrity": "sha512-TKiyTVZxJGhsTszLuzb+6vUZSjVOAhClszBr2Ta2k9IwtNBT/4dzmL6aywt0HCgEZlmwJzXJd8yNiob6HgwTRg==", + "version": "1.0.30001535", + "resolved": "https://registry.npmjs.org/caniuse-lite/-/caniuse-lite-1.0.30001535.tgz", + "integrity": "sha512-48jLyUkiWFfhm/afF7cQPqPjaUmSraEhK4j+FCTJpgnGGEZHqyLe3hmWH7lIooZdSzXL0ReMvHz0vKDoTBsrwg==", "dev": true, "funding": [ { @@ -2272,9 +2279,9 @@ } }, "node_modules/cli-spinners": { - "version": "2.9.0", - "resolved": "https://registry.npmjs.org/cli-spinners/-/cli-spinners-2.9.0.tgz", - "integrity": "sha512-4/aL9X3Wh0yiMQlE+eeRhWP6vclO3QRtw1JHKIT0FFUs5FjpFmESqtMvYZ0+lbzBw900b95mS0hohy+qn2VK/g==", + "version": "2.9.1", + "resolved": "https://registry.npmjs.org/cli-spinners/-/cli-spinners-2.9.1.tgz", + "integrity": "sha512-jHgecW0pxkonBJdrKsqxgRX9AcG+u/5k0Q7WPDfi8AogLAdwxEkyYYNWwZ5GvVFoFx2uiY1eNcSK00fh+1+FyQ==", "dev": true, "engines": { "node": ">=6" @@ -2632,6 +2639,20 @@ "node": ">=10" } }, + "node_modules/define-data-property": { + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/define-data-property/-/define-data-property-1.1.0.tgz", + "integrity": "sha512-UzGwzcjyv3OtAvolTj1GoyNYzfFR+iqbGjcnBEENZVCpM4/Ng1yhGNvS3lR/xDS74Tb2wGG9WzNSNIOS9UVb2g==", + "dev": true, + "dependencies": { + "get-intrinsic": "^1.2.1", + "gopd": "^1.0.1", + "has-property-descriptors": "^1.0.0" + }, + "engines": { + "node": ">= 0.4" + } + }, "node_modules/define-lazy-prop": { "version": "3.0.0", "resolved": "https://registry.npmjs.org/define-lazy-prop/-/define-lazy-prop-3.0.0.tgz", @@ -2645,11 +2666,12 @@ } }, "node_modules/define-properties": { - "version": "1.2.0", - "resolved": "https://registry.npmjs.org/define-properties/-/define-properties-1.2.0.tgz", - "integrity": "sha512-xvqAVKGfT1+UAvPwKTVw/njhdQ8ZhXK4lI0bCIuCMrp2up9nPnaDftrLtmpTazqd1o+UY4zgzU+avtMbDP+ldA==", + "version": "1.2.1", + "resolved": "https://registry.npmjs.org/define-properties/-/define-properties-1.2.1.tgz", + "integrity": "sha512-8QmQKqEASLd5nx0U1B1okLElbUuuttJ/AnYmRXbbbGDWh6uS208EjD4Xqq/I9wK7u0v6O08XhTWnt5XtEbR6Dg==", "dev": true, "dependencies": { + "define-data-property": "^1.0.1", "has-property-descriptors": "^1.0.0", "object-keys": "^1.1.1" }, @@ -2735,9 +2757,9 @@ "dev": true }, "node_modules/electron-to-chromium": { - "version": "1.4.499", - "resolved": "https://registry.npmjs.org/electron-to-chromium/-/electron-to-chromium-1.4.499.tgz", - "integrity": "sha512-0NmjlYBLKVHva4GABWAaHuPJolnDuL0AhV3h1hES6rcLCWEIbRL6/8TghfsVwkx6TEroQVdliX7+aLysUpKvjw==", + "version": "1.4.523", + "resolved": "https://registry.npmjs.org/electron-to-chromium/-/electron-to-chromium-1.4.523.tgz", + "integrity": "sha512-9AreocSUWnzNtvLcbpng6N+GkXnCcBR80IQkxRC9Dfdyg4gaWNUPBujAHUpKkiUkoSoR9UlhA4zD/IgBklmhzg==", "dev": true }, "node_modules/email-addresses": { @@ -2762,18 +2784,18 @@ } }, "node_modules/es-abstract": { - "version": "1.22.1", - "resolved": "https://registry.npmjs.org/es-abstract/-/es-abstract-1.22.1.tgz", - "integrity": "sha512-ioRRcXMO6OFyRpyzV3kE1IIBd4WG5/kltnzdxSCqoP8CMGs/Li+M1uF5o7lOkZVFjDs+NLesthnF66Pg/0q0Lw==", + "version": "1.22.2", + "resolved": "https://registry.npmjs.org/es-abstract/-/es-abstract-1.22.2.tgz", + "integrity": "sha512-YoxfFcDmhjOgWPWsV13+2RNjq1F6UQnfs+8TftwNqtzlmFzEXvlUwdrNrYeaizfjQzRMxkZ6ElWMOJIFKdVqwA==", "dev": true, "dependencies": { "array-buffer-byte-length": "^1.0.0", - "arraybuffer.prototype.slice": "^1.0.1", + "arraybuffer.prototype.slice": "^1.0.2", "available-typed-arrays": "^1.0.5", "call-bind": "^1.0.2", "es-set-tostringtag": "^2.0.1", "es-to-primitive": "^1.2.1", - "function.prototype.name": "^1.1.5", + "function.prototype.name": "^1.1.6", "get-intrinsic": "^1.2.1", "get-symbol-description": "^1.0.0", "globalthis": "^1.0.3", @@ -2789,23 +2811,23 @@ "is-regex": "^1.1.4", "is-shared-array-buffer": "^1.0.2", "is-string": "^1.0.7", - "is-typed-array": "^1.1.10", + "is-typed-array": "^1.1.12", "is-weakref": "^1.0.2", "object-inspect": "^1.12.3", "object-keys": "^1.1.1", "object.assign": "^4.1.4", - "regexp.prototype.flags": "^1.5.0", - "safe-array-concat": "^1.0.0", + "regexp.prototype.flags": "^1.5.1", + "safe-array-concat": "^1.0.1", "safe-regex-test": "^1.0.0", - "string.prototype.trim": "^1.2.7", - "string.prototype.trimend": "^1.0.6", - "string.prototype.trimstart": "^1.0.6", + "string.prototype.trim": "^1.2.8", + "string.prototype.trimend": "^1.0.7", + "string.prototype.trimstart": "^1.0.7", "typed-array-buffer": "^1.0.0", "typed-array-byte-length": "^1.0.0", "typed-array-byte-offset": "^1.0.0", "typed-array-length": "^1.0.4", "unbox-primitive": "^1.0.2", - "which-typed-array": "^1.1.10" + "which-typed-array": "^1.1.11" }, "engines": { "node": ">= 0.4" @@ -2932,16 +2954,16 @@ } }, "node_modules/eslint": { - "version": "8.47.0", - "resolved": "https://registry.npmjs.org/eslint/-/eslint-8.47.0.tgz", - "integrity": "sha512-spUQWrdPt+pRVP1TTJLmfRNJJHHZryFmptzcafwSvHsceV81djHOdnEeDmkdotZyLNjDhrOasNK8nikkoG1O8Q==", + "version": "8.49.0", + "resolved": "https://registry.npmjs.org/eslint/-/eslint-8.49.0.tgz", + "integrity": "sha512-jw03ENfm6VJI0jA9U+8H5zfl5b+FvuU3YYvZRdZHOlU2ggJkxrlkJH4HcDrZpj6YwD8kuYqvQM8LyesoazrSOQ==", "dev": true, "dependencies": { "@eslint-community/eslint-utils": "^4.2.0", "@eslint-community/regexpp": "^4.6.1", "@eslint/eslintrc": "^2.1.2", - "@eslint/js": "^8.47.0", - "@humanwhocodes/config-array": "^0.11.10", + "@eslint/js": "8.49.0", + "@humanwhocodes/config-array": "^0.11.11", "@humanwhocodes/module-importer": "^1.0.1", "@nodelib/fs.walk": "^1.2.8", "ajv": "^6.12.4", @@ -3416,22 +3438,23 @@ } }, "node_modules/flat-cache": { - "version": "3.0.4", - "resolved": "https://registry.npmjs.org/flat-cache/-/flat-cache-3.0.4.tgz", - "integrity": "sha512-dm9s5Pw7Jc0GvMYbshN6zchCA9RgQlzzEZX3vylR9IqFfS8XciblUXOKfW6SiuJ0e13eDYZoZV5wdrev7P3Nwg==", + "version": "3.1.0", + "resolved": "https://registry.npmjs.org/flat-cache/-/flat-cache-3.1.0.tgz", + "integrity": "sha512-OHx4Qwrrt0E4jEIcI5/Xb+f+QmJYNj2rrK8wiIdQOIrB9WrrJL8cjZvXdXuBTkkEwEqLycb5BeZDV1o2i9bTew==", "dev": true, "dependencies": { - "flatted": "^3.1.0", + "flatted": "^3.2.7", + "keyv": "^4.5.3", "rimraf": "^3.0.2" }, "engines": { - "node": "^10.12.0 || >=12.0.0" + "node": ">=12.0.0" } }, "node_modules/flatted": { - "version": "3.2.7", - "resolved": "https://registry.npmjs.org/flatted/-/flatted-3.2.7.tgz", - "integrity": "sha512-5nqDSxl8nn5BSNxyR3n4I6eDmbolI6WT+QqR547RwxQapgjQBmtktdP+HTBb/a/zLsbzERTONyUB5pefh5TtjQ==", + "version": "3.2.9", + "resolved": "https://registry.npmjs.org/flatted/-/flatted-3.2.9.tgz", + "integrity": "sha512-36yxDn5H7OFZQla0/jFJmbIKTdZAQHngCedGxiMmpNfEZM0sdEeT+WczLQrjK6D7o2aiyLYDnkw0R3JK0Qv1RQ==", "dev": true }, "node_modules/for-each": { @@ -3538,15 +3561,15 @@ "dev": true }, "node_modules/function.prototype.name": { - "version": "1.1.5", - "resolved": "https://registry.npmjs.org/function.prototype.name/-/function.prototype.name-1.1.5.tgz", - "integrity": "sha512-uN7m/BzVKQnCUF/iW8jYea67v++2u7m5UgENbHRtdDVclOUP+FMPlCNdmk0h/ysGyo2tavMJEDqJAkJdRa1vMA==", + "version": "1.1.6", + "resolved": "https://registry.npmjs.org/function.prototype.name/-/function.prototype.name-1.1.6.tgz", + "integrity": "sha512-Z5kx79swU5P27WEayXM1tBi5Ze/lbIyiNgU3qyXUOf9b2rgXYyF9Dy9Cx+IQv/Lc8WCG6L82zwUPpSS9hGehIg==", "dev": true, "dependencies": { "call-bind": "^1.0.2", - "define-properties": "^1.1.3", - "es-abstract": "^1.19.0", - "functions-have-names": "^1.2.2" + "define-properties": "^1.2.0", + "es-abstract": "^1.22.1", + "functions-have-names": "^1.2.3" }, "engines": { "node": ">= 0.4" @@ -4071,9 +4094,9 @@ } }, "node_modules/https-proxy-agent": { - "version": "7.0.1", - "resolved": "https://registry.npmjs.org/https-proxy-agent/-/https-proxy-agent-7.0.1.tgz", - "integrity": "sha512-Eun8zV0kcYS1g19r78osiQLEFIRspRUDd9tIfBCTBPBeMieF/EsJNL8VI3xOIdYRDEkjQnqOYPsZ2DsWsVsFwQ==", + "version": "7.0.2", + "resolved": "https://registry.npmjs.org/https-proxy-agent/-/https-proxy-agent-7.0.2.tgz", + "integrity": "sha512-NmLNjm6ucYwtcUmL7JQC1ZQ57LmHP4lT15FQ8D61nak1rO6DH+fz5qNK2Ap5UN4ZapYICE3/0KodcLYSPsPbaA==", "dev": true, "dependencies": { "agent-base": "^7.0.2", @@ -6420,9 +6443,9 @@ } }, "node_modules/pac-proxy-agent": { - "version": "7.0.0", - "resolved": "https://registry.npmjs.org/pac-proxy-agent/-/pac-proxy-agent-7.0.0.tgz", - "integrity": "sha512-t4tRAMx0uphnZrio0S0Jw9zg3oDbz1zVhQ/Vy18FjLfP1XOLNUEjaVxYCYRI6NS+BsMBXKIzV6cTLOkO9AtywA==", + "version": "7.0.1", + "resolved": "https://registry.npmjs.org/pac-proxy-agent/-/pac-proxy-agent-7.0.1.tgz", + "integrity": "sha512-ASV8yU4LLKBAjqIPMbrgtaKIvxQri/yh2OpI+S6hVa9JRkUI3Y3NPFbfngDtY7oFtSMD3w31Xns89mDa3Feo5A==", "dev": true, "dependencies": { "@tootallnate/quickjs-emscripten": "^0.23.0", @@ -6430,9 +6453,9 @@ "debug": "^4.3.4", "get-uri": "^6.0.1", "http-proxy-agent": "^7.0.0", - "https-proxy-agent": "^7.0.0", + "https-proxy-agent": "^7.0.2", "pac-resolver": "^7.0.0", - "socks-proxy-agent": "^8.0.1" + "socks-proxy-agent": "^8.0.2" }, "engines": { "node": ">= 14" @@ -6905,14 +6928,14 @@ } }, "node_modules/regexp.prototype.flags": { - "version": "1.5.0", - "resolved": "https://registry.npmjs.org/regexp.prototype.flags/-/regexp.prototype.flags-1.5.0.tgz", - "integrity": "sha512-0SutC3pNudRKgquxGoRGIz946MZVHqbNfPjBdxeOhBrdgDKlRoXmYLQN9xRbrR09ZXWeGAdPuif7egofn6v5LA==", + "version": "1.5.1", + "resolved": "https://registry.npmjs.org/regexp.prototype.flags/-/regexp.prototype.flags-1.5.1.tgz", + "integrity": "sha512-sy6TXMN+hnP/wMy+ISxg3krXx7BAtWVO4UouuCN/ziM9UEne0euamVNafDfvC83bRNr95y0V5iijeDQFUNpvrg==", "dev": true, "dependencies": { "call-bind": "^1.0.2", "define-properties": "^1.2.0", - "functions-have-names": "^1.2.3" + "set-function-name": "^2.0.0" }, "engines": { "node": ">= 0.4" @@ -7063,9 +7086,9 @@ "dev": true }, "node_modules/resolve": { - "version": "1.22.4", - "resolved": "https://registry.npmjs.org/resolve/-/resolve-1.22.4.tgz", - "integrity": "sha512-PXNdCiPqDqeUou+w1C2eTQbNfxKSuMxqTCuvlmmMsk1NWHL5fRrhY6Pl0qEYYc6+QqGClco1Qj8XnjPego4wfg==", + "version": "1.22.6", + "resolved": "https://registry.npmjs.org/resolve/-/resolve-1.22.6.tgz", + "integrity": "sha512-njhxM7mV12JfufShqGy3Rz8j11RPdLy4xi15UurGJeoHLfJpVXKdh3ueuOqbYUcDZnffr6X739JBo5LzyahEsw==", "dev": true, "dependencies": { "is-core-module": "^2.13.0", @@ -7326,13 +7349,13 @@ } }, "node_modules/safe-array-concat": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/safe-array-concat/-/safe-array-concat-1.0.0.tgz", - "integrity": "sha512-9dVEFruWIsnie89yym+xWTAYASdpw3CJV7Li/6zBewGf9z2i1j31rP6jnY0pHEO4QZh6N0K11bFjWmdR8UGdPQ==", + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/safe-array-concat/-/safe-array-concat-1.0.1.tgz", + "integrity": "sha512-6XbUAseYE2KtOuGueyeobCySj9L4+66Tn6KQMOPQJrAJEowYKW/YR/MGJZl7FdydUdaFu4LYyDZjxf4/Nmo23Q==", "dev": true, "dependencies": { "call-bind": "^1.0.2", - "get-intrinsic": "^1.2.0", + "get-intrinsic": "^1.2.1", "has-symbols": "^1.0.3", "isarray": "^2.0.5" }, @@ -7449,6 +7472,20 @@ "integrity": "sha512-KiKBS8AnWGEyLzofFfmvKwpdPzqiy16LvQfK3yv/fVH7Bj13/wl3JSR1J+rfgRE9q7xUJK4qvgS8raSOeLUehw==", "dev": true }, + "node_modules/set-function-name": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/set-function-name/-/set-function-name-2.0.1.tgz", + "integrity": "sha512-tMNCiqYVkXIZgc2Hnoy2IvC/f8ezc5koaRFkCjrpWzGpCd3qbZXPzVy9MAZzK1ch/X0jvSkojys3oqJN0qCmdA==", + "dev": true, + "dependencies": { + "define-data-property": "^1.0.1", + "functions-have-names": "^1.2.3", + "has-property-descriptors": "^1.0.0" + }, + "engines": { + "node": ">= 0.4" + } + }, "node_modules/shebang-command": { "version": "2.0.0", "resolved": "https://registry.npmjs.org/shebang-command/-/shebang-command-2.0.0.tgz", @@ -7488,9 +7525,9 @@ } }, "node_modules/shiki": { - "version": "0.14.3", - "resolved": "https://registry.npmjs.org/shiki/-/shiki-0.14.3.tgz", - "integrity": "sha512-U3S/a+b0KS+UkTyMjoNojvTgrBHjgp7L6ovhFVZsXmBGnVdQ4K4U9oK0z63w538S91ATngv1vXigHCSWOwnr+g==", + "version": "0.14.4", + "resolved": "https://registry.npmjs.org/shiki/-/shiki-0.14.4.tgz", + "integrity": "sha512-IXCRip2IQzKwxArNNq1S+On4KPML3Yyn8Zzs/xRgcgOWIr8ntIK3IKzjFPfjy/7kt9ZMjc+FItfqHRBg8b6tNQ==", "dev": true, "dependencies": { "ansi-sequence-parser": "^1.1.0", @@ -7520,9 +7557,9 @@ "dev": true }, "node_modules/sinon": { - "version": "15.2.0", - "resolved": "https://registry.npmjs.org/sinon/-/sinon-15.2.0.tgz", - "integrity": "sha512-nPS85arNqwBXaIsFCkolHjGIkFo+Oxu9vbgmBJizLAhqe6P2o3Qmj3KCUoRkfhHtvgDhZdWD3risLHAUJ8npjw==", + "version": "16.0.0", + "resolved": "https://registry.npmjs.org/sinon/-/sinon-16.0.0.tgz", + "integrity": "sha512-B8AaZZm9CT5pqe4l4uWJztfD/mOTa7dL8Qo0W4+s+t74xECOgSZDDQCBjNgIK3+n4kyxQrSTv2V5ul8K25qkiQ==", "dev": true, "dependencies": { "@sinonjs/commons": "^3.0.0", @@ -7604,12 +7641,12 @@ } }, "node_modules/socks-proxy-agent": { - "version": "8.0.1", - "resolved": "https://registry.npmjs.org/socks-proxy-agent/-/socks-proxy-agent-8.0.1.tgz", - "integrity": "sha512-59EjPbbgg8U3x62hhKOFVAmySQUcfRQ4C7Q/D5sEHnZTQRrQlNKINks44DMR1gwXp0p4LaVIeccX2KHTTcHVqQ==", + "version": "8.0.2", + "resolved": "https://registry.npmjs.org/socks-proxy-agent/-/socks-proxy-agent-8.0.2.tgz", + "integrity": "sha512-8zuqoLv1aP/66PHF5TqwJ7Czm3Yv32urJQHrVyhD7mmA6d61Zv8cIXQYPTWwmg6qlupnPvs/QKDmfa4P/qct2g==", "dev": true, "dependencies": { - "agent-base": "^7.0.1", + "agent-base": "^7.0.2", "debug": "^4.3.4", "socks": "^2.7.1" }, @@ -7716,14 +7753,14 @@ } }, "node_modules/string.prototype.trim": { - "version": "1.2.7", - "resolved": "https://registry.npmjs.org/string.prototype.trim/-/string.prototype.trim-1.2.7.tgz", - "integrity": "sha512-p6TmeT1T3411M8Cgg9wBTMRtY2q9+PNy9EV1i2lIXUN/btt763oIfxwN3RR8VU6wHX8j/1CFy0L+YuThm6bgOg==", + "version": "1.2.8", + "resolved": "https://registry.npmjs.org/string.prototype.trim/-/string.prototype.trim-1.2.8.tgz", + "integrity": "sha512-lfjY4HcixfQXOfaqCvcBuOIapyaroTXhbkfJN3gcB1OtyupngWK4sEET9Knd0cXd28kTUqu/kHoV4HKSJdnjiQ==", "dev": true, "dependencies": { "call-bind": "^1.0.2", - "define-properties": "^1.1.4", - "es-abstract": "^1.20.4" + "define-properties": "^1.2.0", + "es-abstract": "^1.22.1" }, "engines": { "node": ">= 0.4" @@ -7733,28 +7770,28 @@ } }, "node_modules/string.prototype.trimend": { - "version": "1.0.6", - "resolved": "https://registry.npmjs.org/string.prototype.trimend/-/string.prototype.trimend-1.0.6.tgz", - "integrity": "sha512-JySq+4mrPf9EsDBEDYMOb/lM7XQLulwg5R/m1r0PXEFqrV0qHvl58sdTilSXtKOflCsK2E8jxf+GKC0T07RWwQ==", + "version": "1.0.7", + "resolved": "https://registry.npmjs.org/string.prototype.trimend/-/string.prototype.trimend-1.0.7.tgz", + "integrity": "sha512-Ni79DqeB72ZFq1uH/L6zJ+DKZTkOtPIHovb3YZHQViE+HDouuU4mBrLOLDn5Dde3RF8qw5qVETEjhu9locMLvA==", "dev": true, "dependencies": { "call-bind": "^1.0.2", - "define-properties": "^1.1.4", - "es-abstract": "^1.20.4" + "define-properties": "^1.2.0", + "es-abstract": "^1.22.1" }, "funding": { "url": "https://github.com/sponsors/ljharb" } }, "node_modules/string.prototype.trimstart": { - "version": "1.0.6", - "resolved": "https://registry.npmjs.org/string.prototype.trimstart/-/string.prototype.trimstart-1.0.6.tgz", - "integrity": "sha512-omqjMDaY92pbn5HOX7f9IccLA+U1tA9GvtU4JrodiXFfYB7jPzzHpRzpglLAjtUV6bB557zwClJezTqnAiYnQA==", + "version": "1.0.7", + "resolved": "https://registry.npmjs.org/string.prototype.trimstart/-/string.prototype.trimstart-1.0.7.tgz", + "integrity": "sha512-NGhtDFu3jCEm7B4Fy0DpLewdJQOZcQ0rGbwQ/+stjnrp2i+rlKeCvos9hOIeCmqwratM47OBxY7uFZzjxHXmrg==", "dev": true, "dependencies": { "call-bind": "^1.0.2", - "define-properties": "^1.1.4", - "es-abstract": "^1.20.4" + "define-properties": "^1.2.0", + "es-abstract": "^1.22.1" }, "funding": { "url": "https://github.com/sponsors/ljharb" @@ -7940,9 +7977,9 @@ } }, "node_modules/ts-api-utils": { - "version": "1.0.2", - "resolved": "https://registry.npmjs.org/ts-api-utils/-/ts-api-utils-1.0.2.tgz", - "integrity": "sha512-Cbu4nIqnEdd+THNEsBdkolnOXhg0I8XteoHaEKgvsxpsbWda4IsUut2c187HxywQCvveojow0Dgw/amxtSKVkQ==", + "version": "1.0.3", + "resolved": "https://registry.npmjs.org/ts-api-utils/-/ts-api-utils-1.0.3.tgz", + "integrity": "sha512-wNMeqtMz5NtwpT/UZGY5alT+VoKdSsOOP/kqHFcUW1P/VRhH2wJ48+DN2WwUliNbQ976ETwDL0Ifd2VVvgonvg==", "dev": true, "engines": { "node": ">=16.13.0" @@ -7994,12 +8031,6 @@ } } }, - "node_modules/ts-node/node_modules/@tsconfig/node14": { - "version": "1.0.3", - "resolved": "https://registry.npmjs.org/@tsconfig/node14/-/node14-1.0.3.tgz", - "integrity": "sha512-ysT8mhdixWK6Hw3i1V2AeRqZ5WfXg1G43mqoYlM2nc6388Fq5jcXyr5mRsqViLx/GJYdoL0bfXD8nmF+Zn/Iow==", - "dev": true - }, "node_modules/ts-node/node_modules/diff": { "version": "4.0.2", "resolved": "https://registry.npmjs.org/diff/-/diff-4.0.2.tgz", @@ -8123,24 +8154,24 @@ } }, "node_modules/typedoc": { - "version": "0.24.8", - "resolved": "https://registry.npmjs.org/typedoc/-/typedoc-0.24.8.tgz", - "integrity": "sha512-ahJ6Cpcvxwaxfu4KtjA8qZNqS43wYt6JL27wYiIgl1vd38WW/KWX11YuAeZhuz9v+ttrutSsgK+XO1CjL1kA3w==", + "version": "0.25.1", + "resolved": "https://registry.npmjs.org/typedoc/-/typedoc-0.25.1.tgz", + "integrity": "sha512-c2ye3YUtGIadxN2O6YwPEXgrZcvhlZ6HlhWZ8jQRNzwLPn2ylhdGqdR8HbyDRyALP8J6lmSANILCkkIdNPFxqA==", "dev": true, "dependencies": { "lunr": "^2.3.9", "marked": "^4.3.0", - "minimatch": "^9.0.0", + "minimatch": "^9.0.3", "shiki": "^0.14.1" }, "bin": { "typedoc": "bin/typedoc" }, "engines": { - "node": ">= 14.14" + "node": ">= 16" }, "peerDependencies": { - "typescript": "4.6.x || 4.7.x || 4.8.x || 4.9.x || 5.0.x || 5.1.x" + "typescript": "4.6.x || 4.7.x || 4.8.x || 4.9.x || 5.0.x || 5.1.x || 5.2.x" } }, "node_modules/typedoc/node_modules/brace-expansion": { @@ -8168,9 +8199,9 @@ } }, "node_modules/typescript": { - "version": "5.1.6", - "resolved": "https://registry.npmjs.org/typescript/-/typescript-5.1.6.tgz", - "integrity": "sha512-zaWCozRZ6DLEWAWFrVDz1H6FVXzUSfTy5FUMWsQlU8Ym5JP9eO4xkTIROFCQvhQf61z6O/G6ugw3SgAnvvm+HA==", + "version": "5.2.2", + "resolved": "https://registry.npmjs.org/typescript/-/typescript-5.2.2.tgz", + "integrity": "sha512-mI4WrpHsbCIcwT9cF4FZvr80QUeKvsUsUvKDoR+X/7XHQH98xYD8YHZg7ANtz2GtZt/CBq2QJ0thkGJMHfqc1w==", "dev": true, "bin": { "tsc": "bin/tsc", @@ -8793,13 +8824,13 @@ "devDependencies": { "@istanbuljs/nyc-config-typescript": "^1.0.2", "@redis/test-utils": "*", - "@types/node": "^20.5.3", + "@types/node": "^20.6.2", "nyc": "^15.1.0", "release-it": "^16.1.5", "source-map-support": "^0.5.21", "ts-node": "^10.9.1", - "typedoc": "^0.24.8", - "typescript": "^5.1.6" + "typedoc": "^0.25.1", + "typescript": "^5.2.2" }, "peerDependencies": { "@redis/client": "^1.0.0" @@ -8817,22 +8848,22 @@ "devDependencies": { "@istanbuljs/nyc-config-typescript": "^1.0.2", "@redis/test-utils": "*", - "@types/node": "^20.5.3", + "@types/node": "^20.6.2", "@types/sinon": "^10.0.16", "@types/yallist": "^4.0.1", - "@typescript-eslint/eslint-plugin": "^6.4.1", - "@typescript-eslint/parser": "^6.4.1", - "eslint": "^8.47.0", + "@typescript-eslint/eslint-plugin": "^6.7.2", + "@typescript-eslint/parser": "^6.7.2", + "eslint": "^8.49.0", "nyc": "^15.1.0", "release-it": "^16.1.5", - "sinon": "^15.2.0", + "sinon": "^16.0.0", "source-map-support": "^0.5.21", "ts-node": "^10.9.1", - "typedoc": "^0.24.8", - "typescript": "^5.1.6" + "typedoc": "^0.25.1", + "typescript": "^5.2.2" }, "engines": { - "node": ">=14" + "node": ">=18" } }, "packages/graph": { @@ -8842,13 +8873,13 @@ "devDependencies": { "@istanbuljs/nyc-config-typescript": "^1.0.2", "@redis/test-utils": "*", - "@types/node": "^20.5.3", + "@types/node": "^20.6.2", "nyc": "^15.1.0", "release-it": "^16.1.5", "source-map-support": "^0.5.21", "ts-node": "^10.9.1", - "typedoc": "^0.24.8", - "typescript": "^5.1.6" + "typedoc": "^0.25.1", + "typescript": "^5.2.2" }, "peerDependencies": { "@redis/client": "^1.0.0" @@ -8861,13 +8892,13 @@ "devDependencies": { "@istanbuljs/nyc-config-typescript": "^1.0.2", "@redis/test-utils": "*", - "@types/node": "^20.5.3", + "@types/node": "^20.6.2", "nyc": "^15.1.0", "release-it": "^16.1.5", "source-map-support": "^0.5.21", "ts-node": "^10.9.1", - "typedoc": "^0.24.8", - "typescript": "^5.1.6" + "typedoc": "^0.25.1", + "typescript": "^5.2.2" }, "peerDependencies": { "@redis/client": "^1.0.0" @@ -8880,13 +8911,13 @@ "devDependencies": { "@istanbuljs/nyc-config-typescript": "^1.0.2", "@redis/test-utils": "*", - "@types/node": "^20.5.3", + "@types/node": "^20.6.2", "nyc": "^15.1.0", "release-it": "^16.1.5", "source-map-support": "^0.5.21", "ts-node": "^10.9.1", - "typedoc": "^0.24.8", - "typescript": "^5.1.6" + "typedoc": "^0.25.1", + "typescript": "^5.2.2" }, "peerDependencies": { "@redis/client": "^1.0.0" @@ -8897,13 +8928,13 @@ "devDependencies": { "@istanbuljs/nyc-config-typescript": "^1.0.2", "@types/mocha": "^10.0.1", - "@types/node": "^20.5.3", + "@types/node": "^20.6.2", "@types/yargs": "^17.0.24", "mocha": "^10.2.0", "nyc": "^15.1.0", "source-map-support": "^0.5.21", "ts-node": "^10.9.1", - "typescript": "^5.1.6", + "typescript": "^5.2.2", "yargs": "^17.7.2" }, "peerDependencies": { @@ -8917,13 +8948,13 @@ "devDependencies": { "@istanbuljs/nyc-config-typescript": "^1.0.2", "@redis/test-utils": "*", - "@types/node": "^20.5.3", + "@types/node": "^20.6.2", "nyc": "^15.1.0", "release-it": "^16.1.5", "source-map-support": "^0.5.21", "ts-node": "^10.9.1", - "typedoc": "^0.24.8", - "typescript": "^5.1.6" + "typedoc": "^0.25.1", + "typescript": "^5.2.2" }, "peerDependencies": { "@redis/client": "^1.0.0" diff --git a/package.json b/package.json index 87cf2c5c5fe..aba3b5afa6c 100644 --- a/package.json +++ b/package.json @@ -31,10 +31,10 @@ "@redis/time-series": "1.0.5" }, "devDependencies": { - "@tsconfig/node14": "^14.1.0", + "@tsconfig/node18": "^18.2.2", "gh-pages": "^6.0.0", "release-it": "^16.1.5", - "typescript": "^5.1.6" + "typescript": "^5.2.2" }, "repository": { "type": "git", diff --git a/packages/bloom/package.json b/packages/bloom/package.json index 5f76df0b104..8a9d9f7a87a 100644 --- a/packages/bloom/package.json +++ b/packages/bloom/package.json @@ -18,13 +18,13 @@ "devDependencies": { "@istanbuljs/nyc-config-typescript": "^1.0.2", "@redis/test-utils": "*", - "@types/node": "^20.5.3", + "@types/node": "^20.6.2", "nyc": "^15.1.0", "release-it": "^16.1.5", "source-map-support": "^0.5.21", "ts-node": "^10.9.1", - "typedoc": "^0.24.8", - "typescript": "^5.1.6" + "typedoc": "^0.25.1", + "typescript": "^5.2.2" }, "repository": { "type": "git", diff --git a/packages/client/package.json b/packages/client/package.json index 7fb545de389..e6fee66e6d4 100644 --- a/packages/client/package.json +++ b/packages/client/package.json @@ -21,22 +21,22 @@ "devDependencies": { "@istanbuljs/nyc-config-typescript": "^1.0.2", "@redis/test-utils": "*", - "@types/node": "^20.5.3", + "@types/node": "^20.6.2", "@types/sinon": "^10.0.16", "@types/yallist": "^4.0.1", - "@typescript-eslint/eslint-plugin": "^6.4.1", - "@typescript-eslint/parser": "^6.4.1", - "eslint": "^8.47.0", + "@typescript-eslint/eslint-plugin": "^6.7.2", + "@typescript-eslint/parser": "^6.7.2", + "eslint": "^8.49.0", "nyc": "^15.1.0", "release-it": "^16.1.5", - "sinon": "^15.2.0", + "sinon": "^16.0.0", "source-map-support": "^0.5.21", "ts-node": "^10.9.1", - "typedoc": "^0.24.8", - "typescript": "^5.1.6" + "typedoc": "^0.25.1", + "typescript": "^5.2.2" }, "engines": { - "node": ">=14" + "node": ">=18" }, "repository": { "type": "git", diff --git a/packages/graph/package.json b/packages/graph/package.json index e79abf911ff..9d48d963670 100644 --- a/packages/graph/package.json +++ b/packages/graph/package.json @@ -18,13 +18,13 @@ "devDependencies": { "@istanbuljs/nyc-config-typescript": "^1.0.2", "@redis/test-utils": "*", - "@types/node": "^20.5.3", + "@types/node": "^20.6.2", "nyc": "^15.1.0", "release-it": "^16.1.5", "source-map-support": "^0.5.21", "ts-node": "^10.9.1", - "typedoc": "^0.24.8", - "typescript": "^5.1.6" + "typedoc": "^0.25.1", + "typescript": "^5.2.2" }, "repository": { "type": "git", diff --git a/packages/json/package.json b/packages/json/package.json index 68df3de12dd..ed39079efcf 100644 --- a/packages/json/package.json +++ b/packages/json/package.json @@ -18,13 +18,13 @@ "devDependencies": { "@istanbuljs/nyc-config-typescript": "^1.0.2", "@redis/test-utils": "*", - "@types/node": "^20.5.3", + "@types/node": "^20.6.2", "nyc": "^15.1.0", "release-it": "^16.1.5", "source-map-support": "^0.5.21", "ts-node": "^10.9.1", - "typedoc": "^0.24.8", - "typescript": "^5.1.6" + "typedoc": "^0.25.1", + "typescript": "^5.2.2" }, "repository": { "type": "git", diff --git a/packages/search/package.json b/packages/search/package.json index 0290490da13..a18ddf1174f 100644 --- a/packages/search/package.json +++ b/packages/search/package.json @@ -18,13 +18,13 @@ "devDependencies": { "@istanbuljs/nyc-config-typescript": "^1.0.2", "@redis/test-utils": "*", - "@types/node": "^20.5.3", + "@types/node": "^20.6.2", "nyc": "^15.1.0", "release-it": "^16.1.5", "source-map-support": "^0.5.21", "ts-node": "^10.9.1", - "typedoc": "^0.24.8", - "typescript": "^5.1.6" + "typedoc": "^0.25.1", + "typescript": "^5.2.2" }, "repository": { "type": "git", diff --git a/packages/test-utils/package.json b/packages/test-utils/package.json index 7fb92b5fe41..2f4e366536e 100644 --- a/packages/test-utils/package.json +++ b/packages/test-utils/package.json @@ -12,13 +12,13 @@ "devDependencies": { "@istanbuljs/nyc-config-typescript": "^1.0.2", "@types/mocha": "^10.0.1", - "@types/node": "^20.5.3", + "@types/node": "^20.6.2", "@types/yargs": "^17.0.24", "mocha": "^10.2.0", "nyc": "^15.1.0", "source-map-support": "^0.5.21", "ts-node": "^10.9.1", - "typescript": "^5.1.6", + "typescript": "^5.2.2", "yargs": "^17.7.2" } } diff --git a/packages/time-series/package.json b/packages/time-series/package.json index 39aabc45cc4..ee3c7526192 100644 --- a/packages/time-series/package.json +++ b/packages/time-series/package.json @@ -18,13 +18,13 @@ "devDependencies": { "@istanbuljs/nyc-config-typescript": "^1.0.2", "@redis/test-utils": "*", - "@types/node": "^20.5.3", + "@types/node": "^20.6.2", "nyc": "^15.1.0", "release-it": "^16.1.5", "source-map-support": "^0.5.21", "ts-node": "^10.9.1", - "typedoc": "^0.24.8", - "typescript": "^5.1.6" + "typedoc": "^0.25.1", + "typescript": "^5.2.2" }, "repository": { "type": "git", diff --git a/tsconfig.base.json b/tsconfig.base.json index 68325e51dcc..e7d0afa262e 100644 --- a/tsconfig.base.json +++ b/tsconfig.base.json @@ -1,5 +1,5 @@ { - "extends": "@tsconfig/node14/tsconfig.json", + "extends": "@tsconfig/node18/tsconfig.json", "compilerOptions": { "declaration": true, "allowJs": true, From 26b9e6dc78413b862eb6f38c5ae5c5b886dc0ee0 Mon Sep 17 00:00:00 2001 From: Leibale Date: Mon, 18 Sep 2023 17:31:28 -0400 Subject: [PATCH 1387/1748] remove node 19 --- .github/workflows/tests.yml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.github/workflows/tests.yml b/.github/workflows/tests.yml index 4ba70e66040..898c7acd3bc 100644 --- a/.github/workflows/tests.yml +++ b/.github/workflows/tests.yml @@ -16,7 +16,7 @@ jobs: strategy: fail-fast: false matrix: - node-version: ['18', '19'] + node-version: ['18', '20'] redis-version: ['5', '6.0', '6.2', '7.0'] steps: - uses: actions/checkout@v2.3.4 From 5a108265dacf8e03734cad79b4e4df64eda28959 Mon Sep 17 00:00:00 2001 From: Rowan Trollope Date: Mon, 18 Sep 2023 14:39:23 -0700 Subject: [PATCH 1388/1748] Add support for `JSON.MSET` (#2605) * Added MSET command MSET command added. Requires all keys to have the same JSON Path, which might fit most use cases, but is a limitation. Optionally we could make the path an array as well to support all use cases. * change JSON.MSET signature, add to json command object, fix tests * its `item.value`, not `item.json`.. * Update MSET.ts Removed unused RedisCommandArguments --------- Co-authored-by: Leibale Eidelman --- packages/json/lib/commands/MSET.spec.ts | 35 +++++++++++++++++++++++++ packages/json/lib/commands/MSET.ts | 28 ++++++++++++++++++++ packages/json/lib/commands/index.ts | 3 +++ 3 files changed, 66 insertions(+) create mode 100644 packages/json/lib/commands/MSET.spec.ts create mode 100644 packages/json/lib/commands/MSET.ts diff --git a/packages/json/lib/commands/MSET.spec.ts b/packages/json/lib/commands/MSET.spec.ts new file mode 100644 index 00000000000..53d4d822505 --- /dev/null +++ b/packages/json/lib/commands/MSET.spec.ts @@ -0,0 +1,35 @@ +import { strict as assert } from 'assert'; +import testUtils, { GLOBAL } from '../test-utils'; +import { transformArguments } from './MSET'; + +describe('MSET', () => { + it('transformArguments', () => { + assert.deepEqual( + transformArguments([{ + key: '1', + path: '$', + value: 1 + }, { + key: '2', + path: '$', + value: '2' + }]), + ['JSON.MSET', '1', '$', '1', '2', '$', '"2"'] + ); + }); + + testUtils.testWithClient('client.json.mSet', async client => { + assert.deepEqual( + await client.json.mSet([{ + key: '1', + path: '$', + value: 1 + }, { + key: '2', + path: '$', + value: '2' + }]), + 'OK' + ); + }, GLOBAL.SERVERS.OPEN); +}); diff --git a/packages/json/lib/commands/MSET.ts b/packages/json/lib/commands/MSET.ts new file mode 100644 index 00000000000..67228f264d2 --- /dev/null +++ b/packages/json/lib/commands/MSET.ts @@ -0,0 +1,28 @@ +import { RedisJSON, transformRedisJsonArgument } from '.'; +import { RedisCommandArgument } from '@redis/client/dist/lib/commands'; + +export const FIRST_KEY_INDEX = 1; + +interface JsonMSetItem { + key: RedisCommandArgument; + path: RedisCommandArgument; + value: RedisJSON; +} + +export function transformArguments(items: Array): Array { + + const args = new Array(1 + items.length * 3); + args[0] = 'JSON.MSET'; + + let argsIndex = 1; + for (let i = 0; i < items.length; i++) { + const item = items[i]; + args[argsIndex++] = item.key; + args[argsIndex++] = item.path; + args[argsIndex++] = transformRedisJsonArgument(item.value); + } + + return args; +} + +export declare function transformReply(): 'OK'; diff --git a/packages/json/lib/commands/index.ts b/packages/json/lib/commands/index.ts index efcf156b84d..a42ece5f277 100644 --- a/packages/json/lib/commands/index.ts +++ b/packages/json/lib/commands/index.ts @@ -9,6 +9,7 @@ import * as DEL from './DEL'; import * as FORGET from './FORGET'; import * as GET from './GET'; import * as MGET from './MGET'; +import * as MSET from './MSET'; import * as NUMINCRBY from './NUMINCRBY'; import * as NUMMULTBY from './NUMMULTBY'; import * as OBJKEYS from './OBJKEYS'; @@ -42,6 +43,8 @@ export default { get: GET, MGET, mGet: MGET, + MSET, + mSet: MSET, NUMINCRBY, numIncrBy: NUMINCRBY, NUMMULTBY, From fb255eb5d07eb7f7d93d014852afcf401bc2a9cd Mon Sep 17 00:00:00 2001 From: Francisco Presencia Date: Tue, 19 Sep 2023 06:40:08 +0900 Subject: [PATCH 1389/1748] Have client.connect() return a Promise (#2602) * Connect returns the instance of the client * Added a test * No auto setup * Added a bit of docs * fix the return type, test, and the docs * fix return type * Update packages/client/lib/client/index.spec.ts Co-authored-by: Francisco Presencia --------- Co-authored-by: Leibale Eidelman --- README.md | 8 +++----- packages/client/lib/client/index.spec.ts | 13 +++++++++++++ packages/client/lib/client/index.ts | 5 +++-- 3 files changed, 19 insertions(+), 7 deletions(-) diff --git a/README.md b/README.md index 4fca8d405d4..95816f51e95 100644 --- a/README.md +++ b/README.md @@ -51,11 +51,9 @@ Looking for a high-level library to handle object mapping? See [redis-om-node](h ```typescript import { createClient } from 'redis'; -const client = createClient(); - -client.on('error', err => console.log('Redis Client Error', err)); - -await client.connect(); +const client = await createClient() + .on('error', err => console.log('Redis Client Error', err)) + .connect(); await client.set('key', 'value'); const value = await client.get('key'); diff --git a/packages/client/lib/client/index.spec.ts b/packages/client/lib/client/index.spec.ts index a0824e440ba..4c2899a9b73 100644 --- a/packages/client/lib/client/index.spec.ts +++ b/packages/client/lib/client/index.spec.ts @@ -107,6 +107,19 @@ describe('Client', () => { }); }); + describe('connect', () => { + testUtils.testWithClient('connect should return the client instance', async client => { + try { + assert.equal(await client.connect(), client); + } finally { + if (client.isOpen) await client.disconnect(); + } + }, { + ...GLOBAL.SERVERS.PASSWORD, + disableClientSetup: true + }); + }); + describe('authentication', () => { testUtils.testWithClient('Client should be authenticated', async client => { assert.equal( diff --git a/packages/client/lib/client/index.ts b/packages/client/lib/client/index.ts index c06759f2eca..800894e06bb 100644 --- a/packages/client/lib/client/index.ts +++ b/packages/client/lib/client/index.ts @@ -426,10 +426,11 @@ export default class RedisClient< }); } - connect(): Promise { + async connect() { // see comment in constructor this.#isolationPool ??= this.#initiateIsolationPool(); - return this.#socket.connect(); + await this.#socket.connect(); + return this as unknown as RedisClientType; } async commandsExecutor( From 4e610c2f8a7df2f231660c65bf6bf39183552e04 Mon Sep 17 00:00:00 2001 From: Codrin-Mihai Chira <122961778+codrin-ch@users.noreply.github.com> Date: Tue, 19 Sep 2023 00:49:37 +0300 Subject: [PATCH 1390/1748] Add support for `CLIENT NO-TOUCH` (#2497) --- packages/client/lib/client/commands.ts | 3 ++ .../lib/commands/CLIENT_NO-TOUCH.spec.ts | 30 +++++++++++++++++++ .../client/lib/commands/CLIENT_NO-TOUCH.ts | 11 +++++++ 3 files changed, 44 insertions(+) create mode 100644 packages/client/lib/commands/CLIENT_NO-TOUCH.spec.ts create mode 100644 packages/client/lib/commands/CLIENT_NO-TOUCH.ts diff --git a/packages/client/lib/client/commands.ts b/packages/client/lib/client/commands.ts index 1e2e5274c0a..8cab10e2187 100644 --- a/packages/client/lib/client/commands.ts +++ b/packages/client/lib/client/commands.ts @@ -23,6 +23,7 @@ import * as CLIENT_ID from '../commands/CLIENT_ID'; import * as CLIENT_KILL from '../commands/CLIENT_KILL'; import * as CLIENT_LIST from '../commands/CLIENT_LIST'; import * as CLIENT_NO_EVICT from '../commands/CLIENT_NO-EVICT'; +import * as CLIENT_NO_TOUCH from '../commands/CLIENT_NO-TOUCH'; import * as CLIENT_PAUSE from '../commands/CLIENT_PAUSE'; import * as CLIENT_SETNAME from '../commands/CLIENT_SETNAME'; import * as CLIENT_TRACKING from '../commands/CLIENT_TRACKING'; @@ -167,6 +168,8 @@ export default { clientKill: CLIENT_KILL, 'CLIENT_NO-EVICT': CLIENT_NO_EVICT, clientNoEvict: CLIENT_NO_EVICT, + 'CLIENT_NO-TOUCH': CLIENT_NO_TOUCH, + clientNoTouch: CLIENT_NO_TOUCH, CLIENT_LIST, clientList: CLIENT_LIST, CLIENT_PAUSE, diff --git a/packages/client/lib/commands/CLIENT_NO-TOUCH.spec.ts b/packages/client/lib/commands/CLIENT_NO-TOUCH.spec.ts new file mode 100644 index 00000000000..80ee0ada1fd --- /dev/null +++ b/packages/client/lib/commands/CLIENT_NO-TOUCH.spec.ts @@ -0,0 +1,30 @@ +import { strict as assert } from 'assert'; +import testUtils, { GLOBAL } from '../test-utils'; +import { transformArguments } from './CLIENT_NO-TOUCH'; + +describe('CLIENT NO-TOUCH', () => { + testUtils.isVersionGreaterThanHook([7, 2]); + + describe('transformArguments', () => { + it('true', () => { + assert.deepEqual( + transformArguments(true), + ['CLIENT', 'NO-TOUCH', 'ON'] + ); + }); + + it('false', () => { + assert.deepEqual( + transformArguments(false), + ['CLIENT', 'NO-TOUCH', 'OFF'] + ); + }); + }); + + testUtils.testWithClient('client.clientNoTouch', async client => { + assert.equal( + await client.clientNoTouch(true), + 'OK' + ); + }, GLOBAL.SERVERS.OPEN); +}); diff --git a/packages/client/lib/commands/CLIENT_NO-TOUCH.ts b/packages/client/lib/commands/CLIENT_NO-TOUCH.ts new file mode 100644 index 00000000000..d11f693dbab --- /dev/null +++ b/packages/client/lib/commands/CLIENT_NO-TOUCH.ts @@ -0,0 +1,11 @@ +import { RedisCommandArguments } from '.'; + +export function transformArguments(value: boolean): RedisCommandArguments { + return [ + 'CLIENT', + 'NO-TOUCH', + value ? 'ON' : 'OFF' + ]; +} + +export declare function transformReply(): 'OK' | Buffer; From 8c8740a4ba8f77dcaf0534cc83a207039cc33c18 Mon Sep 17 00:00:00 2001 From: Leibale Eidelman Date: Mon, 18 Sep 2023 17:51:40 -0400 Subject: [PATCH 1391/1748] Add 7.2 to tests matrix --- .github/workflows/tests.yml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.github/workflows/tests.yml b/.github/workflows/tests.yml index 898c7acd3bc..7c61c7a2209 100644 --- a/.github/workflows/tests.yml +++ b/.github/workflows/tests.yml @@ -17,7 +17,7 @@ jobs: fail-fast: false matrix: node-version: ['18', '20'] - redis-version: ['5', '6.0', '6.2', '7.0'] + redis-version: ['5', '6.0', '6.2', '7.0', '7.2'] steps: - uses: actions/checkout@v2.3.4 with: From cf21c1a1f8b56eea9760dab403e8b55afeedbba0 Mon Sep 17 00:00:00 2001 From: shacharPash <93581407+shacharPash@users.noreply.github.com> Date: Tue, 19 Sep 2023 00:54:25 +0300 Subject: [PATCH 1392/1748] Add support for `JSON.MERGE` (#2511) * Support JSON.MERGE Command * test only 2.6+ ReJson version * test on edge * review * Update test-utils.ts --------- Co-authored-by: Leibale Eidelman --- packages/json/lib/commands/MERGE.spec.ts | 21 +++++++++++++++++++++ packages/json/lib/commands/MERGE.ts | 9 +++++++++ packages/json/lib/commands/index.ts | 3 +++ 3 files changed, 33 insertions(+) create mode 100644 packages/json/lib/commands/MERGE.spec.ts create mode 100644 packages/json/lib/commands/MERGE.ts diff --git a/packages/json/lib/commands/MERGE.spec.ts b/packages/json/lib/commands/MERGE.spec.ts new file mode 100644 index 00000000000..ee5e6fff86d --- /dev/null +++ b/packages/json/lib/commands/MERGE.spec.ts @@ -0,0 +1,21 @@ +import { strict as assert } from 'assert'; +import testUtils, { GLOBAL } from '../test-utils'; +import { transformArguments } from './MERGE'; + +describe('MERGE', () => { + testUtils.isVersionGreaterThanHook([2, 6]); + + it('transformArguments', () => { + assert.deepEqual( + transformArguments('key', '$', 1), + ['JSON.MERGE', 'key', '$', '1'] + ); + }); + + testUtils.testWithClient('client.json.merge', async client => { + assert.equal( + await client.json.merge('key', '$', 'json'), + 'OK' + ); + }, GLOBAL.SERVERS.OPEN); +}); diff --git a/packages/json/lib/commands/MERGE.ts b/packages/json/lib/commands/MERGE.ts new file mode 100644 index 00000000000..81cce7f006b --- /dev/null +++ b/packages/json/lib/commands/MERGE.ts @@ -0,0 +1,9 @@ +import { RedisJSON, transformRedisJsonArgument } from '.'; + +export const FIRST_KEY_INDEX = 1; + +export function transformArguments(key: string, path: string, json: RedisJSON): Array { + return ['JSON.MERGE', key, path, transformRedisJsonArgument(json)]; +} + +export declare function transformReply(): 'OK'; diff --git a/packages/json/lib/commands/index.ts b/packages/json/lib/commands/index.ts index a42ece5f277..9d0a82ec271 100644 --- a/packages/json/lib/commands/index.ts +++ b/packages/json/lib/commands/index.ts @@ -8,6 +8,7 @@ import * as DEBUG_MEMORY from './DEBUG_MEMORY'; import * as DEL from './DEL'; import * as FORGET from './FORGET'; import * as GET from './GET'; +import * as MERGE from './MERGE'; import * as MGET from './MGET'; import * as MSET from './MSET'; import * as NUMINCRBY from './NUMINCRBY'; @@ -41,6 +42,8 @@ export default { forget: FORGET, GET, get: GET, + MERGE, + merge: MERGE, MGET, mGet: MGET, MSET, From 1f978937852b2030455d163be850503b2e6a1bdd Mon Sep 17 00:00:00 2001 From: Leibale Date: Mon, 18 Sep 2023 18:25:13 -0400 Subject: [PATCH 1393/1748] fix `ACL GETUSER` test --- .../client/lib/commands/ACL_GETUSER.spec.ts | 34 +++++++------------ 1 file changed, 12 insertions(+), 22 deletions(-) diff --git a/packages/client/lib/commands/ACL_GETUSER.spec.ts b/packages/client/lib/commands/ACL_GETUSER.spec.ts index f91f2ff9e5c..4cd693db9ce 100644 --- a/packages/client/lib/commands/ACL_GETUSER.spec.ts +++ b/packages/client/lib/commands/ACL_GETUSER.spec.ts @@ -13,32 +13,22 @@ describe('ACL GETUSER', () => { }); testUtils.testWithClient('client.aclGetUser', async client => { - const expectedReply: any = { - passwords: [], - commands: '+@all', - }; + const reply = await client.aclGetUser('default'); + + assert.ok(Array.isArray(reply.passwords)); + assert.equal(typeof reply.commands, 'string'); + assert.ok(Array.isArray(reply.flags)); if (testUtils.isVersionGreaterThan([7])) { - expectedReply.flags = ['on', 'nopass']; - expectedReply.keys = '~*'; - expectedReply.channels = '&*'; - expectedReply.selectors = []; + assert.equal(typeof reply.keys, 'string'); + assert.equal(typeof reply.channels, 'string'); + assert.ok(Array.isArray(reply.selectors)); } else { - expectedReply.keys = ['*']; - expectedReply.selectors = undefined; + assert.ok(Array.isArray(reply.keys)); - if (testUtils.isVersionGreaterThan([6, 2])) { - expectedReply.flags = ['on', 'allkeys', 'allchannels', 'allcommands', 'nopass']; - expectedReply.channels = ['*']; - } else { - expectedReply.flags = ['on', 'allkeys', 'allcommands', 'nopass']; - expectedReply.channels = undefined; - } + if (testUtils.isVersionGreaterThan([6, 2])) { + assert.ok(Array.isArray(reply.channels)); + } } - - assert.deepEqual( - await client.aclGetUser('default'), - expectedReply - ); }, GLOBAL.SERVERS.OPEN); }); From a217cc1a8847b1adeb338006ac2bac84cbd61537 Mon Sep 17 00:00:00 2001 From: Brett Burch Date: Mon, 18 Sep 2023 16:33:17 -0600 Subject: [PATCH 1394/1748] Add support for `FT.SEARCH NOCONTENT` (#2610) * Add support for NOCONTENT in FT.SEARCH * Move support for NOCONTENT search option from client.search to client.searchNoContent * Add test for SEARCH_NOCONTENT#transformReply * Fix typo * Enable test * Update test field type --------- Co-authored-by: Leibale --- packages/search/lib/commands/SEARCH.ts | 1 - .../lib/commands/SEARCH_NOCONTENT.spec.ts | 45 +++++++++++++++++++ .../search/lib/commands/SEARCH_NOCONTENT.ts | 30 +++++++++++++ packages/search/lib/commands/index.ts | 3 ++ 4 files changed, 78 insertions(+), 1 deletion(-) create mode 100644 packages/search/lib/commands/SEARCH_NOCONTENT.spec.ts create mode 100644 packages/search/lib/commands/SEARCH_NOCONTENT.ts diff --git a/packages/search/lib/commands/SEARCH.ts b/packages/search/lib/commands/SEARCH.ts index ef6c0d5c2d1..ff7ab7e201d 100644 --- a/packages/search/lib/commands/SEARCH.ts +++ b/packages/search/lib/commands/SEARCH.ts @@ -6,7 +6,6 @@ export const FIRST_KEY_INDEX = 1; export const IS_READ_ONLY = true; export interface SearchOptions { - // NOCONTENT?: true; TODO VERBATIM?: true; NOSTOPWORDS?: true; // WITHSCORES?: true; diff --git a/packages/search/lib/commands/SEARCH_NOCONTENT.spec.ts b/packages/search/lib/commands/SEARCH_NOCONTENT.spec.ts new file mode 100644 index 00000000000..da5a6feaba7 --- /dev/null +++ b/packages/search/lib/commands/SEARCH_NOCONTENT.spec.ts @@ -0,0 +1,45 @@ +import { strict as assert } from 'assert'; +import { SchemaFieldTypes } from '.'; +import testUtils, { GLOBAL } from '../test-utils'; +import { transformArguments, transformReply } from './SEARCH_NOCONTENT'; + +describe('SEARCH_NOCONTENT', () => { + describe('transformArguments', () => { + it('without options', () => { + assert.deepEqual( + transformArguments('index', 'query'), + ['FT.SEARCH', 'index', 'query', 'NOCONTENT'] + ); + }); + }); + + describe('transformReply', () => { + it('returns total and keys', () => { + assert.deepEqual(transformReply([3, '1', '2', '3']), { + total: 3, + documents: ['1', '2', '3'] + }) + }); + }); + + describe('client.ft.searchNoContent', () => { + testUtils.testWithClient('returns total and keys', async client => { + await Promise.all([ + client.ft.create('index', { + field: SchemaFieldTypes.TEXT + }), + client.hSet('1', 'field', 'field1'), + client.hSet('2', 'field', 'field2'), + client.hSet('3', 'field', 'field3') + ]); + + assert.deepEqual( + await client.ft.searchNoContent('index', '*'), + { + total: 3, + documents: ['1','2','3'] + } + ); + }, GLOBAL.SERVERS.OPEN); + }); +}); diff --git a/packages/search/lib/commands/SEARCH_NOCONTENT.ts b/packages/search/lib/commands/SEARCH_NOCONTENT.ts new file mode 100644 index 00000000000..ab50ae2b9ff --- /dev/null +++ b/packages/search/lib/commands/SEARCH_NOCONTENT.ts @@ -0,0 +1,30 @@ +import { RedisCommandArguments } from "@redis/client/dist/lib/commands"; +import { pushSearchOptions } from "."; +import { SearchOptions, SearchRawReply } from "./SEARCH"; + +export const FIRST_KEY_INDEX = 1; + +export const IS_READ_ONLY = true; + +export function transformArguments( + index: string, + query: string, + options?: SearchOptions +): RedisCommandArguments { + return pushSearchOptions( + ['FT.SEARCH', index, query, 'NOCONTENT'], + options + ); +} + +export interface SearchNoContentReply { + total: number; + documents: Array; +}; + +export function transformReply(reply: SearchRawReply): SearchNoContentReply { + return { + total: reply[0], + documents: reply.slice(1) + }; +} diff --git a/packages/search/lib/commands/index.ts b/packages/search/lib/commands/index.ts index 0470aff213f..75a2b4e380a 100644 --- a/packages/search/lib/commands/index.ts +++ b/packages/search/lib/commands/index.ts @@ -20,6 +20,7 @@ import * as INFO from './INFO'; import * as PROFILESEARCH from './PROFILE_SEARCH'; import * as PROFILEAGGREGATE from './PROFILE_AGGREGATE'; import * as SEARCH from './SEARCH'; +import * as SEARCH_NOCONTENT from './SEARCH_NOCONTENT'; import * as SPELLCHECK from './SPELLCHECK'; import * as SUGADD from './SUGADD'; import * as SUGDEL from './SUGDEL'; @@ -80,6 +81,8 @@ export default { profileAggregate: PROFILEAGGREGATE, SEARCH, search: SEARCH, + SEARCH_NOCONTENT, + searchNoContent: SEARCH_NOCONTENT, SPELLCHECK, spellCheck: SPELLCHECK, SUGADD, From d8ae8cf0fff88e366616e2fb474873abc8e38ed1 Mon Sep 17 00:00:00 2001 From: Leibale Date: Mon, 18 Sep 2023 19:03:18 -0400 Subject: [PATCH 1395/1748] Upgrade actions/checkout & actions/setup-node --- .github/workflows/codeql.yml | 2 +- .github/workflows/documentation.yml | 4 ++-- .github/workflows/tests.yml | 4 ++-- 3 files changed, 5 insertions(+), 5 deletions(-) diff --git a/.github/workflows/codeql.yml b/.github/workflows/codeql.yml index 3004337c278..47c82baea2c 100644 --- a/.github/workflows/codeql.yml +++ b/.github/workflows/codeql.yml @@ -38,7 +38,7 @@ jobs: steps: - name: Checkout repository - uses: actions/checkout@v3 + uses: actions/checkout@v4 # Initializes the CodeQL tools for scanning. - name: Initialize CodeQL diff --git a/.github/workflows/documentation.yml b/.github/workflows/documentation.yml index 7b3d42bf9f4..e095faf1918 100644 --- a/.github/workflows/documentation.yml +++ b/.github/workflows/documentation.yml @@ -10,11 +10,11 @@ jobs: documentation: runs-on: ubuntu-latest steps: - - uses: actions/checkout@v2.3.4 + - uses: actions/checkout@v4 with: fetch-depth: 1 - name: Use Node.js - uses: actions/setup-node@v2.3.0 + uses: actions/setup-node@v3 - name: Install Packages run: npm ci - name: Build tests tools diff --git a/.github/workflows/tests.yml b/.github/workflows/tests.yml index 7c61c7a2209..99181e08e5e 100644 --- a/.github/workflows/tests.yml +++ b/.github/workflows/tests.yml @@ -19,11 +19,11 @@ jobs: node-version: ['18', '20'] redis-version: ['5', '6.0', '6.2', '7.0', '7.2'] steps: - - uses: actions/checkout@v2.3.4 + - uses: actions/checkout@v4 with: fetch-depth: 1 - name: Use Node.js ${{ matrix.node-version }} - uses: actions/setup-node@v2.3.0 + uses: actions/setup-node@v3 with: node-version: ${{ matrix.node-version }} - name: Update npm From 8ecfd3ebda1efafb1ced06fca29ca1bc57d1163f Mon Sep 17 00:00:00 2001 From: Evis Cheng Date: Tue, 19 Sep 2023 07:31:03 +0800 Subject: [PATCH 1396/1748] Add support for `CLUSTER MYSHARDID` (#2528) * Add support for CLUSTER.MYSHARDID command * Update CLUSTER_MYSHARDID.ts * Update CLUSTER_MYSHARDID.spec.ts * add test and clean code --------- Co-authored-by: Leibale Eidelman --- packages/client/lib/client/commands.ts | 3 +++ .../lib/commands/CLUSTER_MYSHARDID.spec.ts | 22 +++++++++++++++++++ .../client/lib/commands/CLUSTER_MYSHARDID.ts | 7 ++++++ 3 files changed, 32 insertions(+) create mode 100644 packages/client/lib/commands/CLUSTER_MYSHARDID.spec.ts create mode 100644 packages/client/lib/commands/CLUSTER_MYSHARDID.ts diff --git a/packages/client/lib/client/commands.ts b/packages/client/lib/client/commands.ts index 8cab10e2187..278039166e8 100644 --- a/packages/client/lib/client/commands.ts +++ b/packages/client/lib/client/commands.ts @@ -46,6 +46,7 @@ import * as CLUSTER_KEYSLOT from '../commands/CLUSTER_KEYSLOT'; import * as CLUSTER_LINKS from '../commands/CLUSTER_LINKS'; import * as CLUSTER_MEET from '../commands/CLUSTER_MEET'; import * as CLUSTER_MYID from '../commands/CLUSTER_MYID'; +import * as CLUSTER_MYSHARDID from '../commands/CLUSTER_MYSHARDID'; import * as CLUSTER_NODES from '../commands/CLUSTER_NODES'; import * as CLUSTER_REPLICAS from '../commands/CLUSTER_REPLICAS'; import * as CLUSTER_REPLICATE from '../commands/CLUSTER_REPLICATE'; @@ -216,6 +217,8 @@ export default { clusterMeet: CLUSTER_MEET, CLUSTER_MYID, clusterMyId: CLUSTER_MYID, + CLUSTER_MYSHARDID, + clusterMyShardId: CLUSTER_MYSHARDID, CLUSTER_NODES, clusterNodes: CLUSTER_NODES, CLUSTER_REPLICAS, diff --git a/packages/client/lib/commands/CLUSTER_MYSHARDID.spec.ts b/packages/client/lib/commands/CLUSTER_MYSHARDID.spec.ts new file mode 100644 index 00000000000..180289870ca --- /dev/null +++ b/packages/client/lib/commands/CLUSTER_MYSHARDID.spec.ts @@ -0,0 +1,22 @@ +import { strict as assert } from 'assert'; +import testUtils, { GLOBAL } from '../test-utils'; +import { transformArguments } from './CLUSTER_MYSHARDID'; + +describe('CLUSTER MYSHARDID', () => { + testUtils.isVersionGreaterThanHook([7, 2]); + + it('transformArguments', () => { + assert.deepEqual( + transformArguments(), + ['CLUSTER', 'MYSHARDID'] + ); + }); + + testUtils.testWithCluster('clusterNode.clusterMyShardId', async cluster => { + const client = await cluster.nodeClient(cluster.masters[0]); + assert.equal( + typeof await client.clusterMyShardId(), + 'string' + ); + }, GLOBAL.CLUSTERS.OPEN); +}); diff --git a/packages/client/lib/commands/CLUSTER_MYSHARDID.ts b/packages/client/lib/commands/CLUSTER_MYSHARDID.ts new file mode 100644 index 00000000000..1c4f8b82f56 --- /dev/null +++ b/packages/client/lib/commands/CLUSTER_MYSHARDID.ts @@ -0,0 +1,7 @@ +export const IS_READ_ONLY = true; + +export function transformArguments() { + return ['CLUSTER', 'MYSHARDID']; +} + +export declare function transformReply(): string | Buffer; From 01ca54e907902e7f9cb1a96736b980f0b07e2162 Mon Sep 17 00:00:00 2001 From: Moshe L <121929078+Moshe-RS@users.noreply.github.com> Date: Tue, 19 Sep 2023 02:45:33 +0300 Subject: [PATCH 1397/1748] fix #1970 - add support for `RESTORE` (#2535) * - Added RESTORE functionality * add FIRST_KEY_INDEX, fix tests, clean example, add example to examples table * use returnBuffers in test --------- Co-authored-by: Leibale Eidelman --- examples/README.md | 3 +- examples/dump-and-restore.js | 22 ++++++ packages/client/lib/cluster/commands.ts | 3 + packages/client/lib/commands/RESTORE.spec.ts | 74 ++++++++++++++++++++ packages/client/lib/commands/RESTORE.ts | 39 +++++++++++ 5 files changed, 140 insertions(+), 1 deletion(-) create mode 100644 examples/dump-and-restore.js create mode 100644 packages/client/lib/commands/RESTORE.spec.ts create mode 100644 packages/client/lib/commands/RESTORE.ts diff --git a/examples/README.md b/examples/README.md index 19e9df31f90..4e7655a3519 100644 --- a/examples/README.md +++ b/examples/README.md @@ -3,7 +3,7 @@ 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. | | `bloom-filter.js` | Space efficient set membership checks with a [Bloom Filter](https://en.wikipedia.org/wiki/Bloom_filter) using [RedisBloom](https://redisbloom.io). | | `check-connection-status.js` | Check the client's connection status. | @@ -12,6 +12,7 @@ This folder contains example scripts showing how to use Node Redis in different | `connect-to-cluster.js` | Connect to a Redis cluster. | | `count-min-sketch.js` | Estimate the frequency of a given event using the [RedisBloom](https://redisbloom.io) Count-Min Sketch. | | `cuckoo-filter.js` | Space efficient set membership checks with a [Cuckoo Filter](https://en.wikipedia.org/wiki/Cuckoo_filter) using [RedisBloom](https://redisbloom.io). | +| `dump-and-restore.js` | Demonstrates the use of the [`DUMP`](https://redis.io/commands/dump/) and [`RESTORE`](https://redis.io/commands/restore/) commands | | `get-server-time.js` | Get the time from the Redis server. | | `hyperloglog.js` | Showing use of Hyperloglog commands [PFADD, PFCOUNT and PFMERGE](https://redis.io/commands/?group=hyperloglog). | | `lua-multi-incr.js` | Define a custom lua script that allows you to perform INCRBY on multiple keys. | diff --git a/examples/dump-and-restore.js b/examples/dump-and-restore.js new file mode 100644 index 00000000000..081e44f9f9a --- /dev/null +++ b/examples/dump-and-restore.js @@ -0,0 +1,22 @@ +// This example demonstrates the use of the DUMP and RESTORE commands + +import { commandOptions, createClient } from 'redis'; + +const client = createClient(); +await client.connect(); + +// DUMP a specific key into a local variable +const dump = await client.dump( + commandOptions({ returnBuffers: true }), + 'source' +); + +// RESTORE into a new key +await client.restore('destination', 0, dump); + +// RESTORE and REPLACE an existing key +await client.restore('destination', 0, dump, { + REPLACE: true +}); + +await client.quit(); diff --git a/packages/client/lib/cluster/commands.ts b/packages/client/lib/cluster/commands.ts index 58fa651be1b..84a37862772 100644 --- a/packages/client/lib/cluster/commands.ts +++ b/packages/client/lib/cluster/commands.ts @@ -110,6 +110,7 @@ import * as PTTL from '../commands/PTTL'; import * as PUBLISH from '../commands/PUBLISH'; import * as RENAME from '../commands/RENAME'; import * as RENAMENX from '../commands/RENAMENX'; +import * as RESTORE from '../commands/RESTORE'; import * as RPOP_COUNT from '../commands/RPOP_COUNT'; import * as RPOP from '../commands/RPOP'; import * as RPOPLPUSH from '../commands/RPOPLPUSH'; @@ -434,6 +435,8 @@ export default { rename: RENAME, RENAMENX, renameNX: RENAMENX, + RESTORE, + restore: RESTORE, RPOP_COUNT, rPopCount: RPOP_COUNT, RPOP, diff --git a/packages/client/lib/commands/RESTORE.spec.ts b/packages/client/lib/commands/RESTORE.spec.ts new file mode 100644 index 00000000000..89d42f3d4de --- /dev/null +++ b/packages/client/lib/commands/RESTORE.spec.ts @@ -0,0 +1,74 @@ +import { strict as assert } from 'assert'; +import testUtils, { GLOBAL } from '../test-utils'; +import { transformArguments } from './RESTORE'; + +describe('RESTORE', () => { + describe('transformArguments', () => { + it('simple', () => { + assert.deepEqual( + transformArguments('key', 0, 'value'), + ['RESTORE', 'key', '0', 'value'] + ); + }); + + it('with REPLACE', () => { + assert.deepEqual( + transformArguments('key', 0, 'value', { + REPLACE: true + }), + ['RESTORE', 'key', '0', 'value', 'REPLACE'] + ); + }); + + it('with ABSTTL', () => { + assert.deepEqual( + transformArguments('key', 0, 'value', { + ABSTTL: true + }), + ['RESTORE', 'key', '0', 'value', 'ABSTTL'] + ); + }); + + it('with IDLETIME', () => { + assert.deepEqual( + transformArguments('key', 0, 'value', { + IDLETIME: 1 + }), + ['RESTORE', 'key', '0', 'value', 'IDLETIME', '1'] + ); + }); + + it('with FREQ', () => { + assert.deepEqual( + transformArguments('key', 0, 'value', { + FREQ: 1 + }), + ['RESTORE', 'key', '0', 'value', 'FREQ', '1'] + ); + }); + + it('with REPLACE, ABSTTL, IDLETIME and FREQ', () => { + assert.deepEqual( + transformArguments('key', 0, 'value', { + REPLACE: true, + ABSTTL: true, + IDLETIME: 1, + FREQ: 2 + }), + ['RESTORE', 'key', '0', 'value', 'REPLACE', 'ABSTTL', 'IDLETIME', '1', 'FREQ', '2'] + ); + }); + }); + + testUtils.testWithClient('client.restore', async client => { + const [, dump] = await Promise.all([ + client.set('source', 'value'), + client.dump(client.commandOptions({ returnBuffers: true }), 'source') + ]); + + assert.equal( + await client.restore('destination', 0, dump), + 'OK' + ); + }, GLOBAL.SERVERS.OPEN); +}); diff --git a/packages/client/lib/commands/RESTORE.ts b/packages/client/lib/commands/RESTORE.ts new file mode 100644 index 00000000000..d9ac11c424b --- /dev/null +++ b/packages/client/lib/commands/RESTORE.ts @@ -0,0 +1,39 @@ +import { RedisCommandArgument, RedisCommandArguments } from '.'; + +export const FIRST_KEY_INDEX = 1; + +interface RestoreOptions { + REPLACE?: true; + ABSTTL?: true; + IDLETIME?: number; + FREQ?: number; +} + +export function transformArguments( + key: RedisCommandArgument, + ttl: number, + serializedValue: RedisCommandArgument, + options?: RestoreOptions +): RedisCommandArguments { + const args = ['RESTORE', key, ttl.toString(), serializedValue]; + + if (options?.REPLACE) { + args.push('REPLACE'); + } + + if (options?.ABSTTL) { + args.push('ABSTTL'); + } + + if (options?.IDLETIME) { + args.push('IDLETIME', options.IDLETIME.toString()); + } + + if (options?.FREQ) { + args.push('FREQ', options.FREQ.toString()); + } + + return args; +} + +export declare function transformReply(): 'OK'; From 6848f3d207f848330d5da5d491a499365e0a6312 Mon Sep 17 00:00:00 2001 From: Asher Date: Wed, 20 Sep 2023 04:59:02 +0900 Subject: [PATCH 1398/1748] Add support for `PUBSUB SHARDNUMSUB` (#2541) * Add support for command 'PUBSUB SHARDNUMSUB' * Use import from PUBSUB_SHARDNUMSUB * Add test case for non-empty reply * clean tests * run tests in redis >= 7, fix integration test --------- Co-authored-by: Leibale Eidelman --- packages/client/lib/client/commands.ts | 3 ++ .../lib/commands/PUBSUB_SHARDNUMSUB.spec.ts | 48 +++++++++++++++++++ .../client/lib/commands/PUBSUB_SHARDNUMSUB.ts | 24 ++++++++++ 3 files changed, 75 insertions(+) create mode 100644 packages/client/lib/commands/PUBSUB_SHARDNUMSUB.spec.ts create mode 100644 packages/client/lib/commands/PUBSUB_SHARDNUMSUB.ts diff --git a/packages/client/lib/client/commands.ts b/packages/client/lib/client/commands.ts index 278039166e8..206d5b781af 100644 --- a/packages/client/lib/client/commands.ts +++ b/packages/client/lib/client/commands.ts @@ -102,6 +102,7 @@ import * as PUBSUB_CHANNELS from '../commands/PUBSUB_CHANNELS'; import * as PUBSUB_NUMPAT from '../commands/PUBSUB_NUMPAT'; import * as PUBSUB_NUMSUB from '../commands/PUBSUB_NUMSUB'; import * as PUBSUB_SHARDCHANNELS from '../commands/PUBSUB_SHARDCHANNELS'; +import * as PUBSUB_SHARDNUMSUB from '../commands/PUBSUB_SHARDNUMSUB'; import * as RANDOMKEY from '../commands/RANDOMKEY'; import * as READONLY from '../commands/READONLY'; import * as READWRITE from '../commands/READWRITE'; @@ -329,6 +330,8 @@ export default { pubSubNumSub: PUBSUB_NUMSUB, PUBSUB_SHARDCHANNELS, pubSubShardChannels: PUBSUB_SHARDCHANNELS, + PUBSUB_SHARDNUMSUB, + pubSubShardNumSub: PUBSUB_SHARDNUMSUB, RANDOMKEY, randomKey: RANDOMKEY, READONLY, diff --git a/packages/client/lib/commands/PUBSUB_SHARDNUMSUB.spec.ts b/packages/client/lib/commands/PUBSUB_SHARDNUMSUB.spec.ts new file mode 100644 index 00000000000..fea1373b55d --- /dev/null +++ b/packages/client/lib/commands/PUBSUB_SHARDNUMSUB.spec.ts @@ -0,0 +1,48 @@ +import { strict as assert } from 'assert'; +import testUtils, { GLOBAL } from '../test-utils'; +import { transformArguments } from './PUBSUB_SHARDNUMSUB'; + +describe('PUBSUB SHARDNUMSUB', () => { + testUtils.isVersionGreaterThanHook([7]); + + describe('transformArguments', () => { + it('simple', () => { + assert.deepEqual( + transformArguments(), + ['PUBSUB', 'SHARDNUMSUB'] + ); + }); + + it('string', () => { + assert.deepEqual( + transformArguments('channel'), + ['PUBSUB', 'SHARDNUMSUB', 'channel'] + ); + }); + + it('array', () => { + assert.deepEqual( + transformArguments(['1', '2']), + ['PUBSUB', 'SHARDNUMSUB', '1', '2'] + ); + }); + }); + + testUtils.testWithClient('client.pubSubShardNumSub', async client => { + assert.deepEqual( + await client.pubSubShardNumSub(['foo', 'bar']), + Object.create(null, { + foo: { + value: 0, + configurable: true, + enumerable: true + }, + bar: { + value: 0, + configurable: true, + enumerable: true + } + }) + ); + }, GLOBAL.SERVERS.OPEN); +}); diff --git a/packages/client/lib/commands/PUBSUB_SHARDNUMSUB.ts b/packages/client/lib/commands/PUBSUB_SHARDNUMSUB.ts new file mode 100644 index 00000000000..4d7f4d8a71e --- /dev/null +++ b/packages/client/lib/commands/PUBSUB_SHARDNUMSUB.ts @@ -0,0 +1,24 @@ +import { pushVerdictArguments } from './generic-transformers'; +import { RedisCommandArgument, RedisCommandArguments } from '.'; + +export const IS_READ_ONLY = true; + +export function transformArguments( + channels?: Array | RedisCommandArgument +): RedisCommandArguments { + const args = ['PUBSUB', 'SHARDNUMSUB']; + + if (channels) return pushVerdictArguments(args, channels); + + return args; +} + +export function transformReply(rawReply: Array): Record { + const transformedReply = Object.create(null); + + for (let i = 0; i < rawReply.length; i += 2) { + transformedReply[rawReply[i]] = rawReply[i + 1]; + } + + return transformedReply; +} From 4ec97be4f05af7288e66645beec6bf18aa86e3e8 Mon Sep 17 00:00:00 2001 From: avinashkrishna613 <36639277+avinashkrishna613@users.noreply.github.com> Date: Wed, 20 Sep 2023 01:51:54 +0530 Subject: [PATCH 1399/1748] fix #1956 - add support for `LATENCY HISTORY` (#2555) * added support for LATENCY_HISTORY command * clean code * Update LATENCY_HISTORY.ts --------- Co-authored-by: Leibale Eidelman --- packages/client/lib/client/commands.ts | 3 +++ .../lib/commands/LATENCY_HISTORY.spec.ts | 26 ++++++++++++++++++ .../client/lib/commands/LATENCY_HISTORY.ts | 27 +++++++++++++++++++ 3 files changed, 56 insertions(+) create mode 100644 packages/client/lib/commands/LATENCY_HISTORY.spec.ts create mode 100644 packages/client/lib/commands/LATENCY_HISTORY.ts diff --git a/packages/client/lib/client/commands.ts b/packages/client/lib/client/commands.ts index 206d5b781af..76ae5d73735 100644 --- a/packages/client/lib/client/commands.ts +++ b/packages/client/lib/client/commands.ts @@ -86,6 +86,7 @@ import * as KEYS from '../commands/KEYS'; import * as LASTSAVE from '../commands/LASTSAVE'; import * as LATENCY_DOCTOR from '../commands/LATENCY_DOCTOR'; import * as LATENCY_GRAPH from '../commands/LATENCY_GRAPH'; +import * as LATENCY_HISTORY from '../commands/LATENCY_HISTORY'; import * as LATENCY_LATEST from '../commands/LATENCY_LATEST'; import * as LOLWUT from '../commands/LOLWUT'; import * as MEMORY_DOCTOR from '../commands/MEMORY_DOCTOR'; @@ -298,6 +299,8 @@ export default { latencyDoctor: LATENCY_DOCTOR, LATENCY_GRAPH, latencyGraph: LATENCY_GRAPH, + LATENCY_HISTORY, + latencyHistory: LATENCY_HISTORY, LATENCY_LATEST, latencyLatest: LATENCY_LATEST, LOLWUT, diff --git a/packages/client/lib/commands/LATENCY_HISTORY.spec.ts b/packages/client/lib/commands/LATENCY_HISTORY.spec.ts new file mode 100644 index 00000000000..e79e969b261 --- /dev/null +++ b/packages/client/lib/commands/LATENCY_HISTORY.spec.ts @@ -0,0 +1,26 @@ +import {strict as assert} from 'assert'; +import testUtils, {GLOBAL} from '../test-utils'; +import { transformArguments } from './LATENCY_HISTORY'; + +describe('LATENCY HISTORY', () => { + it('transformArguments', () => { + assert.deepEqual( + transformArguments('command'), + ['LATENCY', 'HISTORY', 'command'] + ); + }); + + testUtils.testWithClient('client.latencyHistory', async client => { + await Promise.all([ + client.configSet('latency-monitor-threshold', '100'), + client.sendCommand(['DEBUG', 'SLEEP', '1']) + ]); + + const latencyHisRes = await client.latencyHistory('command'); + assert.ok(Array.isArray(latencyHisRes)); + for (const [timestamp, latency] of latencyHisRes) { + assert.equal(typeof timestamp, 'number'); + assert.equal(typeof latency, 'number'); + } + }, GLOBAL.SERVERS.OPEN); +}); diff --git a/packages/client/lib/commands/LATENCY_HISTORY.ts b/packages/client/lib/commands/LATENCY_HISTORY.ts new file mode 100644 index 00000000000..c0b1964553e --- /dev/null +++ b/packages/client/lib/commands/LATENCY_HISTORY.ts @@ -0,0 +1,27 @@ +export type EventType = ( + 'active-defrag-cycle' | + 'aof-fsync-always' | + 'aof-stat' | + 'aof-rewrite-diff-write' | + 'aof-rename' | + 'aof-write' | + 'aof-write-active-child' | + 'aof-write-alone' | + 'aof-write-pending-fsync' | + 'command' | + 'expire-cycle' | + 'eviction-cycle' | + 'eviction-del' | + 'fast-command' | + 'fork' | + 'rdb-unlink-temp-file' +); + +export function transformArguments(event: EventType) { + return ['LATENCY', 'HISTORY', event]; +} + +export declare function transformReply(): Array<[ + timestamp: number, + latency: number, +]>; From e00041e0eb80840585cf7806a4d360a083c76cc7 Mon Sep 17 00:00:00 2001 From: Charley DAVID Date: Tue, 19 Sep 2023 23:37:16 +0200 Subject: [PATCH 1400/1748] Fix: XAUTOCLAIM after a TRIM with pending messages returns nil (#2565) * fix(client): XCLAIM & XAUTOCLAIM after a TRIM might return nils * fix(client): Fix race condition in specs * revert test utils changes * make tests faster --------- Co-authored-by: Leibale Eidelman --- .../client/lib/commands/XAUTOCLAIM.spec.ts | 80 ++++++++++++++++--- packages/client/lib/commands/XAUTOCLAIM.ts | 6 +- packages/client/lib/commands/XCLAIM.spec.ts | 32 +++++++- packages/client/lib/commands/XCLAIM.ts | 2 +- .../lib/commands/generic-transformers.spec.ts | 33 ++++++++ .../lib/commands/generic-transformers.ts | 28 ++++--- 6 files changed, 154 insertions(+), 27 deletions(-) diff --git a/packages/client/lib/commands/XAUTOCLAIM.spec.ts b/packages/client/lib/commands/XAUTOCLAIM.spec.ts index 4447a06d773..bae914bda05 100644 --- a/packages/client/lib/commands/XAUTOCLAIM.spec.ts +++ b/packages/client/lib/commands/XAUTOCLAIM.spec.ts @@ -23,20 +23,76 @@ describe('XAUTOCLAIM', () => { }); }); - testUtils.testWithClient('client.xAutoClaim', async client => { - await Promise.all([ - client.xGroupCreate('key', 'group', '$', { - MKSTREAM: true - }), + testUtils.testWithClient('client.xAutoClaim without messages', async client => { + const [,, reply] = await Promise.all([ + client.xGroupCreate('key', 'group', '$', { MKSTREAM: true }), client.xGroupCreateConsumer('key', 'group', 'consumer'), + client.xAutoClaim('key', 'group', 'consumer', 1, '0-0') ]); - assert.deepEqual( - await client.xAutoClaim('key', 'group', 'consumer', 1, '0-0'), - { - nextId: '0-0', - messages: [] - } - ); + assert.deepEqual(reply, { + nextId: '0-0', + messages: [] + }); + }, GLOBAL.SERVERS.OPEN); + + testUtils.testWithClient('client.xAutoClaim with messages', async client => { + const [,, id,, reply] = await Promise.all([ + client.xGroupCreate('key', 'group', '$', { MKSTREAM: true }), + client.xGroupCreateConsumer('key', 'group', 'consumer'), + client.xAdd('key', '*', { foo: 'bar' }), + client.xReadGroup('group', 'consumer', { key: 'key', id: '>' }), + client.xAutoClaim('key', 'group', 'consumer', 0, '0-0') + ]); + + assert.deepEqual(reply, { + nextId: '0-0', + messages: [{ + id, + message: Object.create(null, { + foo: { + value: 'bar', + configurable: true, + enumerable: true + } + }) + }] + }); + }, GLOBAL.SERVERS.OPEN); + + testUtils.testWithClient('client.xAutoClaim with trimmed messages', async client => { + const [,,,,, id,, reply] = await Promise.all([ + client.xGroupCreate('key', 'group', '$', { MKSTREAM: true }), + client.xGroupCreateConsumer('key', 'group', 'consumer'), + client.xAdd('key', '*', { foo: 'bar' }), + client.xReadGroup('group', 'consumer', { key: 'key', id: '>' }), + client.xTrim('key', 'MAXLEN', 0), + client.xAdd('key', '*', { bar: 'baz' }), + client.xReadGroup('group', 'consumer', { key: 'key', id: '>' }), + client.xAutoClaim('key', 'group', 'consumer', 0, '0-0') + ]); + + assert.deepEqual(reply, { + nextId: '0-0', + messages: testUtils.isVersionGreaterThan([7, 0]) ? [{ + id, + message: Object.create(null, { + bar: { + value: 'baz', + configurable: true, + enumerable: true + } + }) + }] : [null, { + id, + message: Object.create(null, { + bar: { + value: 'baz', + configurable: true, + enumerable: true + } + }) + }] + }); }, GLOBAL.SERVERS.OPEN); }); diff --git a/packages/client/lib/commands/XAUTOCLAIM.ts b/packages/client/lib/commands/XAUTOCLAIM.ts index 4bf46057bac..831563981a6 100644 --- a/packages/client/lib/commands/XAUTOCLAIM.ts +++ b/packages/client/lib/commands/XAUTOCLAIM.ts @@ -1,5 +1,5 @@ import { RedisCommandArgument, RedisCommandArguments } from '.'; -import { StreamMessagesReply, transformStreamMessagesReply } from './generic-transformers'; +import { StreamMessagesNullReply, transformStreamMessagesNullReply } from './generic-transformers'; export const FIRST_KEY_INDEX = 1; @@ -28,12 +28,12 @@ type XAutoClaimRawReply = [RedisCommandArgument, Array]; interface XAutoClaimReply { nextId: RedisCommandArgument; - messages: StreamMessagesReply; + messages: StreamMessagesNullReply; } export function transformReply(reply: XAutoClaimRawReply): XAutoClaimReply { return { nextId: reply[0], - messages: transformStreamMessagesReply(reply[1]) + messages: transformStreamMessagesNullReply(reply[1]) }; } diff --git a/packages/client/lib/commands/XCLAIM.spec.ts b/packages/client/lib/commands/XCLAIM.spec.ts index 141a62ab77a..6626e84c731 100644 --- a/packages/client/lib/commands/XCLAIM.spec.ts +++ b/packages/client/lib/commands/XCLAIM.spec.ts @@ -83,8 +83,38 @@ describe('XCLAIM', () => { }); assert.deepEqual( - await client.xClaim('key', 'group', 'consumer', 1, '0-0'), + await client.xClaim('key', 'group', 'consumer', 0, '0-0'), [] ); }, GLOBAL.SERVERS.OPEN); + + testUtils.testWithClient('client.xClaim with a message', async client => { + await client.xGroupCreate('key', 'group', '$', { MKSTREAM: true }); + const id = await client.xAdd('key', '*', { foo: 'bar' }); + await client.xReadGroup('group', 'consumer', { key: 'key', id: '>' }); + + assert.deepEqual( + await client.xClaim('key', 'group', 'consumer', 0, id), + [{ + id, + message: Object.create(null, { 'foo': { + value: 'bar', + configurable: true, + enumerable: true + } }) + }] + ); + }, GLOBAL.SERVERS.OPEN); + + testUtils.testWithClient('client.xClaim with a trimmed message', async client => { + await client.xGroupCreate('key', 'group', '$', { MKSTREAM: true }); + const id = await client.xAdd('key', '*', { foo: 'bar' }); + await client.xReadGroup('group', 'consumer', { key: 'key', id: '>' }); + await client.xTrim('key', 'MAXLEN', 0); + + assert.deepEqual( + await client.xClaim('key', 'group', 'consumer', 0, id), + testUtils.isVersionGreaterThan([7, 0]) ? []: [null] + ); + }, GLOBAL.SERVERS.OPEN); }); diff --git a/packages/client/lib/commands/XCLAIM.ts b/packages/client/lib/commands/XCLAIM.ts index bc38f9b9e95..e7b458e2376 100644 --- a/packages/client/lib/commands/XCLAIM.ts +++ b/packages/client/lib/commands/XCLAIM.ts @@ -45,4 +45,4 @@ export function transformArguments( return args; } -export { transformStreamMessagesReply as transformReply } from './generic-transformers'; +export { transformStreamMessagesNullReply as transformReply } from './generic-transformers'; diff --git a/packages/client/lib/commands/generic-transformers.spec.ts b/packages/client/lib/commands/generic-transformers.spec.ts index 301cab0a75c..60caf26eaad 100644 --- a/packages/client/lib/commands/generic-transformers.spec.ts +++ b/packages/client/lib/commands/generic-transformers.spec.ts @@ -9,6 +9,7 @@ import { transformStringNumberInfinityArgument, transformTuplesReply, transformStreamMessagesReply, + transformStreamMessagesNullReply, transformStreamsMessagesReply, transformSortedSetWithScoresReply, pushGeoCountArgument, @@ -219,6 +220,38 @@ describe('Generic Transformers', () => { ); }); + it('transformStreamMessagesNullReply', () => { + assert.deepEqual( + transformStreamMessagesNullReply([null, ['0-0', ['0key', '0value']]]), + [null, { + id: '0-0', + message: Object.create(null, { + '0key': { + value: '0value', + configurable: true, + enumerable: true + } + }) + }] + ); + }); + + it('transformStreamMessagesNullReply', () => { + assert.deepEqual( + transformStreamMessagesNullReply([null, ['0-1', ['11key', '11value']]]), + [null, { + id: '0-1', + message: Object.create(null, { + '11key': { + value: '11value', + configurable: true, + enumerable: true + } + }) + }] + ); + }); + describe('transformStreamsMessagesReply', () => { it('null', () => { assert.equal( diff --git a/packages/client/lib/commands/generic-transformers.ts b/packages/client/lib/commands/generic-transformers.ts index 5048de9399a..4cf610a036e 100644 --- a/packages/client/lib/commands/generic-transformers.ts +++ b/packages/client/lib/commands/generic-transformers.ts @@ -92,19 +92,27 @@ export interface StreamMessageReply { message: Record; } -export type StreamMessagesReply = Array; +export function transformStreamMessageReply([id, message]: Array): StreamMessageReply { + return { + id, + message: transformTuplesReply(message) + }; +} -export function transformStreamMessagesReply(reply: Array): StreamMessagesReply { - const messages = []; +export function transformStreamMessageNullReply(reply: Array): StreamMessageReply | null { + if (reply === null) return null; + return transformStreamMessageReply(reply); +} - for (const [id, message] of reply) { - messages.push({ - id, - message: transformTuplesReply(message) - }); - } - return messages; +export type StreamMessagesReply = Array; +export function transformStreamMessagesReply(reply: Array): StreamMessagesReply { + return reply.map(transformStreamMessageReply); +} + +export type StreamMessagesNullReply = Array; +export function transformStreamMessagesNullReply(reply: Array): StreamMessagesNullReply { + return reply.map(transformStreamMessageNullReply); } export type StreamsMessagesReply = Array<{ From fef006cc1d54345694351d8bb24629912c377931 Mon Sep 17 00:00:00 2001 From: Leibale Date: Tue, 19 Sep 2023 17:53:20 -0400 Subject: [PATCH 1401/1748] Release client@1.5.10 --- packages/client/package.json | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/packages/client/package.json b/packages/client/package.json index e6fee66e6d4..c5cf2072f36 100644 --- a/packages/client/package.json +++ b/packages/client/package.json @@ -1,6 +1,6 @@ { "name": "@redis/client", - "version": "1.5.9", + "version": "1.5.10", "license": "MIT", "main": "./dist/index.js", "types": "./dist/index.d.ts", From 6cc635cb93bc8590fa1c876d220584cbf0b8bc28 Mon Sep 17 00:00:00 2001 From: Leibale Date: Tue, 19 Sep 2023 17:54:58 -0400 Subject: [PATCH 1402/1748] Release search@1.1.4 --- packages/search/package.json | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/packages/search/package.json b/packages/search/package.json index a18ddf1174f..9cbb8eacab3 100644 --- a/packages/search/package.json +++ b/packages/search/package.json @@ -1,6 +1,6 @@ { "name": "@redis/search", - "version": "1.1.3", + "version": "1.1.4", "license": "MIT", "main": "./dist/index.js", "types": "./dist/index.d.ts", From d258296f33bf2829846b800ba9f105749e519dbe Mon Sep 17 00:00:00 2001 From: Leibale Date: Tue, 19 Sep 2023 17:56:29 -0400 Subject: [PATCH 1403/1748] Release json@1.0.5 --- packages/json/package.json | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/packages/json/package.json b/packages/json/package.json index ed39079efcf..5bb19debe12 100644 --- a/packages/json/package.json +++ b/packages/json/package.json @@ -1,6 +1,6 @@ { "name": "@redis/json", - "version": "1.0.4", + "version": "1.0.5", "license": "MIT", "main": "./dist/index.js", "types": "./dist/index.d.ts", From 077e099938975c8b14cf49760e60afebea544d4f Mon Sep 17 00:00:00 2001 From: Leibale Date: Tue, 19 Sep 2023 17:58:22 -0400 Subject: [PATCH 1404/1748] upgrade subpackages --- package-lock.json | 12 ++++++------ package.json | 6 +++--- 2 files changed, 9 insertions(+), 9 deletions(-) diff --git a/package-lock.json b/package-lock.json index c08b693191e..c72ad38ff7c 100644 --- a/package-lock.json +++ b/package-lock.json @@ -13,10 +13,10 @@ ], "dependencies": { "@redis/bloom": "1.2.0", - "@redis/client": "1.5.9", + "@redis/client": "1.5.10", "@redis/graph": "1.1.0", - "@redis/json": "1.0.4", - "@redis/search": "1.1.3", + "@redis/json": "1.0.5", + "@redis/search": "1.1.4", "@redis/time-series": "1.0.5" }, "devDependencies": { @@ -8838,7 +8838,7 @@ }, "packages/client": { "name": "@redis/client", - "version": "1.5.9", + "version": "1.5.10", "license": "MIT", "dependencies": { "cluster-key-slot": "1.1.2", @@ -8887,7 +8887,7 @@ }, "packages/json": { "name": "@redis/json", - "version": "1.0.4", + "version": "1.0.5", "license": "MIT", "devDependencies": { "@istanbuljs/nyc-config-typescript": "^1.0.2", @@ -8906,7 +8906,7 @@ }, "packages/search": { "name": "@redis/search", - "version": "1.1.3", + "version": "1.1.4", "license": "MIT", "devDependencies": { "@istanbuljs/nyc-config-typescript": "^1.0.2", diff --git a/package.json b/package.json index aba3b5afa6c..f1c34cf06d5 100644 --- a/package.json +++ b/package.json @@ -24,10 +24,10 @@ }, "dependencies": { "@redis/bloom": "1.2.0", - "@redis/client": "1.5.9", + "@redis/client": "1.5.10", "@redis/graph": "1.1.0", - "@redis/json": "1.0.4", - "@redis/search": "1.1.3", + "@redis/json": "1.0.5", + "@redis/search": "1.1.4", "@redis/time-series": "1.0.5" }, "devDependencies": { From 7706a5e86d710cd49929bb73753576af87c2f924 Mon Sep 17 00:00:00 2001 From: Leibale Date: Tue, 19 Sep 2023 17:58:47 -0400 Subject: [PATCH 1405/1748] Release redis@4.6.9 --- package-lock.json | 4 ++-- package.json | 2 +- 2 files changed, 3 insertions(+), 3 deletions(-) diff --git a/package-lock.json b/package-lock.json index c72ad38ff7c..1a419ea651b 100644 --- a/package-lock.json +++ b/package-lock.json @@ -1,12 +1,12 @@ { "name": "redis", - "version": "4.6.8", + "version": "4.6.9", "lockfileVersion": 3, "requires": true, "packages": { "": { "name": "redis", - "version": "4.6.8", + "version": "4.6.9", "license": "MIT", "workspaces": [ "./packages/*" diff --git a/package.json b/package.json index f1c34cf06d5..c3986f7371c 100644 --- a/package.json +++ b/package.json @@ -1,7 +1,7 @@ { "name": "redis", "description": "A modern, high performance Redis client", - "version": "4.6.8", + "version": "4.6.9", "license": "MIT", "main": "./dist/index.js", "types": "./dist/index.d.ts", From 38e9a40d1aff15ddd489629f1ac6524b639fd054 Mon Sep 17 00:00:00 2001 From: Leibale Date: Fri, 22 Sep 2023 07:00:26 -0400 Subject: [PATCH 1406/1748] fix #2619 #2618 #2617 #2616 - support for node >= 14 --- package-lock.json | 20 ++++++++++---------- package.json | 2 +- packages/client/package.json | 2 +- tsconfig.base.json | 2 +- 4 files changed, 13 insertions(+), 13 deletions(-) diff --git a/package-lock.json b/package-lock.json index 1a419ea651b..6c63749a34c 100644 --- a/package-lock.json +++ b/package-lock.json @@ -20,7 +20,7 @@ "@redis/time-series": "1.0.5" }, "devDependencies": { - "@tsconfig/node18": "^18.2.2", + "@tsconfig/node14": "^14.1.0", "gh-pages": "^6.0.0", "release-it": "^16.1.5", "typescript": "^5.2.2" @@ -1108,9 +1108,9 @@ "dev": true }, "node_modules/@tsconfig/node14": { - "version": "1.0.3", - "resolved": "https://registry.npmjs.org/@tsconfig/node14/-/node14-1.0.3.tgz", - "integrity": "sha512-ysT8mhdixWK6Hw3i1V2AeRqZ5WfXg1G43mqoYlM2nc6388Fq5jcXyr5mRsqViLx/GJYdoL0bfXD8nmF+Zn/Iow==", + "version": "14.1.0", + "resolved": "https://registry.npmjs.org/@tsconfig/node14/-/node14-14.1.0.tgz", + "integrity": "sha512-VmsCG04YR58ciHBeJKBDNMWWfYbyP8FekWVuTlpstaUPlat1D0x/tXzkWP7yCMU0eSz9V4OZU0LBWTFJ3xZf6w==", "dev": true }, "node_modules/@tsconfig/node16": { @@ -1119,12 +1119,6 @@ "integrity": "sha512-vxhUy4J8lyeyinH7Azl1pdd43GJhZH/tP2weN8TntQblOY+A0XbT8DJk1/oCPuOOyg/Ja757rG0CgHcWC8OfMA==", "dev": true }, - "node_modules/@tsconfig/node18": { - "version": "18.2.2", - "resolved": "https://registry.npmjs.org/@tsconfig/node18/-/node18-18.2.2.tgz", - "integrity": "sha512-d6McJeGsuoRlwWZmVIeE8CUA27lu6jLjvv1JzqmpsytOYYbVi1tHZEnwCNVOXnj4pyLvneZlFlpXUK+X9wBWyw==", - "dev": true - }, "node_modules/@types/http-cache-semantics": { "version": "4.0.2", "resolved": "https://registry.npmjs.org/@types/http-cache-semantics/-/http-cache-semantics-4.0.2.tgz", @@ -8031,6 +8025,12 @@ } } }, + "node_modules/ts-node/node_modules/@tsconfig/node14": { + "version": "1.0.3", + "resolved": "https://registry.npmjs.org/@tsconfig/node14/-/node14-1.0.3.tgz", + "integrity": "sha512-ysT8mhdixWK6Hw3i1V2AeRqZ5WfXg1G43mqoYlM2nc6388Fq5jcXyr5mRsqViLx/GJYdoL0bfXD8nmF+Zn/Iow==", + "dev": true + }, "node_modules/ts-node/node_modules/diff": { "version": "4.0.2", "resolved": "https://registry.npmjs.org/diff/-/diff-4.0.2.tgz", diff --git a/package.json b/package.json index c3986f7371c..5d320a3113d 100644 --- a/package.json +++ b/package.json @@ -31,7 +31,7 @@ "@redis/time-series": "1.0.5" }, "devDependencies": { - "@tsconfig/node18": "^18.2.2", + "@tsconfig/node14": "^14.1.0", "gh-pages": "^6.0.0", "release-it": "^16.1.5", "typescript": "^5.2.2" diff --git a/packages/client/package.json b/packages/client/package.json index c5cf2072f36..abdf49168ff 100644 --- a/packages/client/package.json +++ b/packages/client/package.json @@ -36,7 +36,7 @@ "typescript": "^5.2.2" }, "engines": { - "node": ">=18" + "node": ">=14" }, "repository": { "type": "git", diff --git a/tsconfig.base.json b/tsconfig.base.json index e7d0afa262e..68325e51dcc 100644 --- a/tsconfig.base.json +++ b/tsconfig.base.json @@ -1,5 +1,5 @@ { - "extends": "@tsconfig/node18/tsconfig.json", + "extends": "@tsconfig/node14/tsconfig.json", "compilerOptions": { "declaration": true, "allowJs": true, From d1fdf1e7e2f30b3776add5da533f562ff76e4010 Mon Sep 17 00:00:00 2001 From: Leibale Date: Fri, 22 Sep 2023 07:01:48 -0400 Subject: [PATCH 1407/1748] Release client@1.5.11 --- packages/client/package.json | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/packages/client/package.json b/packages/client/package.json index abdf49168ff..cbb36ab7727 100644 --- a/packages/client/package.json +++ b/packages/client/package.json @@ -1,6 +1,6 @@ { "name": "@redis/client", - "version": "1.5.10", + "version": "1.5.11", "license": "MIT", "main": "./dist/index.js", "types": "./dist/index.d.ts", From f7d400e896de7a38d6d75692590471cb6cbbf0c3 Mon Sep 17 00:00:00 2001 From: Leibale Date: Fri, 22 Sep 2023 07:02:29 -0400 Subject: [PATCH 1408/1748] Release json@1.0.6 --- packages/json/package.json | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/packages/json/package.json b/packages/json/package.json index 5bb19debe12..3a168b7d4b5 100644 --- a/packages/json/package.json +++ b/packages/json/package.json @@ -1,6 +1,6 @@ { "name": "@redis/json", - "version": "1.0.5", + "version": "1.0.6", "license": "MIT", "main": "./dist/index.js", "types": "./dist/index.d.ts", From 3862fd2e9f82bb062a6b07888b84d353b11d50b3 Mon Sep 17 00:00:00 2001 From: Leibale Date: Fri, 22 Sep 2023 07:03:00 -0400 Subject: [PATCH 1409/1748] Release search@1.1.5 --- packages/search/package.json | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/packages/search/package.json b/packages/search/package.json index 9cbb8eacab3..48f4e5cdec6 100644 --- a/packages/search/package.json +++ b/packages/search/package.json @@ -1,6 +1,6 @@ { "name": "@redis/search", - "version": "1.1.4", + "version": "1.1.5", "license": "MIT", "main": "./dist/index.js", "types": "./dist/index.d.ts", From 14909e7ccb91ac5bdcc694ac6341e12cfcbdae4a Mon Sep 17 00:00:00 2001 From: Leibale Date: Fri, 22 Sep 2023 07:03:34 -0400 Subject: [PATCH 1410/1748] ugprade subpackages --- package-lock.json | 14 +++++++------- package.json | 6 +++--- 2 files changed, 10 insertions(+), 10 deletions(-) diff --git a/package-lock.json b/package-lock.json index 6c63749a34c..24d114e2e97 100644 --- a/package-lock.json +++ b/package-lock.json @@ -13,10 +13,10 @@ ], "dependencies": { "@redis/bloom": "1.2.0", - "@redis/client": "1.5.10", + "@redis/client": "1.5.11", "@redis/graph": "1.1.0", - "@redis/json": "1.0.5", - "@redis/search": "1.1.4", + "@redis/json": "1.0.6", + "@redis/search": "1.1.5", "@redis/time-series": "1.0.5" }, "devDependencies": { @@ -8838,7 +8838,7 @@ }, "packages/client": { "name": "@redis/client", - "version": "1.5.10", + "version": "1.5.11", "license": "MIT", "dependencies": { "cluster-key-slot": "1.1.2", @@ -8863,7 +8863,7 @@ "typescript": "^5.2.2" }, "engines": { - "node": ">=18" + "node": ">=14" } }, "packages/graph": { @@ -8887,7 +8887,7 @@ }, "packages/json": { "name": "@redis/json", - "version": "1.0.5", + "version": "1.0.6", "license": "MIT", "devDependencies": { "@istanbuljs/nyc-config-typescript": "^1.0.2", @@ -8906,7 +8906,7 @@ }, "packages/search": { "name": "@redis/search", - "version": "1.1.4", + "version": "1.1.5", "license": "MIT", "devDependencies": { "@istanbuljs/nyc-config-typescript": "^1.0.2", diff --git a/package.json b/package.json index 5d320a3113d..ef8558a46e8 100644 --- a/package.json +++ b/package.json @@ -24,10 +24,10 @@ }, "dependencies": { "@redis/bloom": "1.2.0", - "@redis/client": "1.5.10", + "@redis/client": "1.5.11", "@redis/graph": "1.1.0", - "@redis/json": "1.0.5", - "@redis/search": "1.1.4", + "@redis/json": "1.0.6", + "@redis/search": "1.1.5", "@redis/time-series": "1.0.5" }, "devDependencies": { From c64ce74383018266015e1fff6394261dad243dd1 Mon Sep 17 00:00:00 2001 From: Leibale Date: Fri, 22 Sep 2023 07:03:52 -0400 Subject: [PATCH 1411/1748] Release redis@4.6.10 --- package-lock.json | 4 ++-- package.json | 2 +- 2 files changed, 3 insertions(+), 3 deletions(-) diff --git a/package-lock.json b/package-lock.json index 24d114e2e97..344044b1340 100644 --- a/package-lock.json +++ b/package-lock.json @@ -1,12 +1,12 @@ { "name": "redis", - "version": "4.6.9", + "version": "4.6.10", "lockfileVersion": 3, "requires": true, "packages": { "": { "name": "redis", - "version": "4.6.9", + "version": "4.6.10", "license": "MIT", "workspaces": [ "./packages/*" diff --git a/package.json b/package.json index ef8558a46e8..2982a32a5e6 100644 --- a/package.json +++ b/package.json @@ -1,7 +1,7 @@ { "name": "redis", "description": "A modern, high performance Redis client", - "version": "4.6.9", + "version": "4.6.10", "license": "MIT", "main": "./dist/index.js", "types": "./dist/index.d.ts", From a8b81bdd01329252466eb1dd608b2a92b960c3ae Mon Sep 17 00:00:00 2001 From: Shaya Potter Date: Tue, 7 Nov 2023 14:43:20 +0200 Subject: [PATCH 1412/1748] Send client user-agent during connection, via CLIENT SETINFO (#2645) * Add SETINFO support to client connection, with the ability to disable sending the user agent if the end user desires. * Also enables modifying the user-agent with a tag to enable distinguishing different usages. --- packages/client/lib/client/index.spec.ts | 40 +++++++++++++++++++++ packages/client/lib/client/index.ts | 39 +++++++++++++++++++- packages/client/lib/commands/CLIENT_INFO.ts | 7 +++- packages/client/lib/test-utils.ts | 3 +- packages/client/tsconfig.json | 3 +- tsconfig.base.json | 3 +- 6 files changed, 90 insertions(+), 5 deletions(-) diff --git a/packages/client/lib/client/index.spec.ts b/packages/client/lib/client/index.spec.ts index 4c2899a9b73..3278d27775b 100644 --- a/packages/client/lib/client/index.spec.ts +++ b/packages/client/lib/client/index.spec.ts @@ -10,6 +10,8 @@ import { once } from 'events'; import { ClientKillFilters } from '../commands/CLIENT_KILL'; import { promisify } from 'util'; +import {version} from '../../package.json'; + export const SQUARE_SCRIPT = defineScript({ SCRIPT: 'return ARGV[1] * ARGV[1];', NUMBER_OF_KEYS: 0, @@ -118,6 +120,44 @@ describe('Client', () => { ...GLOBAL.SERVERS.PASSWORD, disableClientSetup: true }); + + testUtils.testWithClient('should set default lib name and version', async client => { + const clientInfo = await client.clientInfo(); + + assert.equal(clientInfo.libName, 'node-redis'); + assert.equal(clientInfo.libVer, version); + }, { + ...GLOBAL.SERVERS.PASSWORD, + minimumDockerVersion: [7, 2] + }); + + testUtils.testWithClient('disable sending lib name and version', async client => { + const clientInfo = await client.clientInfo(); + + assert.equal(clientInfo.libName, ''); + assert.equal(clientInfo.libVer, ''); + }, { + ...GLOBAL.SERVERS.PASSWORD, + clientOptions: { + ...GLOBAL.SERVERS.PASSWORD.clientOptions, + disableClientInfo: true + }, + minimumDockerVersion: [7, 2] + }); + + testUtils.testWithClient('send client name tag', async client => { + const clientInfo = await client.clientInfo(); + + assert.equal(clientInfo.libName, 'node-redis(test)'); + assert.equal(clientInfo.libVer, version); + }, { + ...GLOBAL.SERVERS.PASSWORD, + clientOptions: { + ...GLOBAL.SERVERS.PASSWORD.clientOptions, + clientInfoTag: "test" + }, + minimumDockerVersion: [7, 2] + }); }); describe('authentication', () => { diff --git a/packages/client/lib/client/index.ts b/packages/client/lib/client/index.ts index 800894e06bb..4c3964c7aa0 100644 --- a/packages/client/lib/client/index.ts +++ b/packages/client/lib/client/index.ts @@ -11,11 +11,13 @@ import { ScanCommandOptions } from '../commands/SCAN'; import { HScanTuple } from '../commands/HSCAN'; import { attachCommands, attachExtensions, fCallArguments, transformCommandArguments, transformCommandReply, transformLegacyCommandArguments } from '../commander'; import { Pool, Options as PoolOptions, createPool } from 'generic-pool'; -import { ClientClosedError, ClientOfflineError, DisconnectsClientError } from '../errors'; +import { ClientClosedError, ClientOfflineError, DisconnectsClientError, ErrorReply } from '../errors'; import { URL } from 'url'; import { TcpSocketConnectOpts } from 'net'; import { PubSubType, PubSubListener, PubSubTypeListeners, ChannelListeners } from './pub-sub'; +import {version} from '../../package.json'; + export interface RedisClientOptions< M extends RedisModules = RedisModules, F extends RedisFunctions = RedisFunctions, @@ -66,6 +68,14 @@ export interface RedisClientOptions< * Useful with Redis deployments that do not use TCP Keep-Alive. */ pingInterval?: number; + /** + * If set to true, disables sending client identifier (user-agent like message) to the redis server + */ + disableClientInfo?: boolean; + /** + * Tag to append to library name that is sent to the Redis server + */ + clientInfoTag?: string; } type WithCommands = { @@ -274,6 +284,33 @@ export default class RedisClient< ); } + if (!this.#options?.disableClientInfo) { + promises.push( + this.#queue.addCommand( + [ 'CLIENT', 'SETINFO', 'LIB-VER', version], + { asap: true } + ).catch(err => { + if (!(err instanceof ErrorReply)) { + throw err; + } + }) + ); + + promises.push( + this.#queue.addCommand( + [ + 'CLIENT', 'SETINFO', 'LIB-NAME', + this.#options?.clientInfoTag ? `node-redis(${this.#options.clientInfoTag})` : 'node-redis' + ], + { asap: true } + ).catch(err => { + if (!(err instanceof ErrorReply)) { + throw err; + } + }) + ); + } + if (this.#options?.name) { promises.push( this.#queue.addCommand( diff --git a/packages/client/lib/commands/CLIENT_INFO.ts b/packages/client/lib/commands/CLIENT_INFO.ts index 7f6b6e1884e..fd823542f86 100644 --- a/packages/client/lib/commands/CLIENT_INFO.ts +++ b/packages/client/lib/commands/CLIENT_INFO.ts @@ -31,6 +31,9 @@ export interface ClientInfoReply { user?: string; // 6.0 redir?: number; // 6.2 resp?: number; // 7.0 + // 7.2 + libName?: string; + libVer?: string; } const CLIENT_INFO_REGEX = /([^\s=]+)=([^\s]*)/g; @@ -62,7 +65,9 @@ export function transformReply(rawReply: string): ClientInfoReply { totMem: Number(map['tot-mem']), events: map.events, cmd: map.cmd, - user: map.user + user: map.user, + libName: map['lib-name'], + libVer: map['lib-ver'], }; if (map.laddr !== undefined) { diff --git a/packages/client/lib/test-utils.ts b/packages/client/lib/test-utils.ts index 65d526f6019..a9db70c860f 100644 --- a/packages/client/lib/test-utils.ts +++ b/packages/client/lib/test-utils.ts @@ -4,7 +4,8 @@ import { promiseTimeout } from './utils'; const utils = new TestUtils({ dockerImageName: 'redis', - dockerImageVersionArgument: 'redis-version' + dockerImageVersionArgument: 'redis-version', + defaultDockerVersion: '7.2' }); export default utils; diff --git a/packages/client/tsconfig.json b/packages/client/tsconfig.json index 3271cf400a2..c71595c5702 100644 --- a/packages/client/tsconfig.json +++ b/packages/client/tsconfig.json @@ -5,7 +5,8 @@ }, "include": [ "./index.ts", - "./lib/**/*.ts" + "./lib/**/*.ts", + "./package.json" ], "exclude": [ "./lib/test-utils.ts", diff --git a/tsconfig.base.json b/tsconfig.base.json index 68325e51dcc..1157be947b9 100644 --- a/tsconfig.base.json +++ b/tsconfig.base.json @@ -4,7 +4,8 @@ "declaration": true, "allowJs": true, "useDefineForClassFields": true, - "esModuleInterop": false + "esModuleInterop": false, + "resolveJsonModule": true }, "ts-node": { "files": true From 68d835d7a228cc1cf6b0a18b960fbdbb2cce2e4a Mon Sep 17 00:00:00 2001 From: Leibale Eidelman Date: Mon, 20 Nov 2023 13:16:36 -0500 Subject: [PATCH 1413/1748] fix #2632 - handle socket close in "socket initiator" phase (#2653) --- packages/client/lib/client/socket.ts | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/packages/client/lib/client/socket.ts b/packages/client/lib/client/socket.ts index b079a51fea9..b701f6ea979 100644 --- a/packages/client/lib/client/socket.ts +++ b/packages/client/lib/client/socket.ts @@ -199,7 +199,7 @@ export default class RedisSocket extends EventEmitter { .off('error', reject) .once('error', (err: Error) => this.#onSocketError(err)) .once('close', hadError => { - if (!hadError && this.#isReady && this.#socket === socket) { + if (!hadError && this.#isOpen && this.#socket === socket) { this.#onSocketError(new SocketClosedUnexpectedlyError()); } }) @@ -229,10 +229,11 @@ export default class RedisSocket extends EventEmitter { } #onSocketError(err: Error): void { + const wasReady = this.#isReady; this.#isReady = false; this.emit('error', err); - if (!this.#isOpen || typeof this.#shouldReconnect(0, err) !== 'number') return; + if (!wasReady || !this.#isOpen || typeof this.#shouldReconnect(0, err) !== 'number') return; this.emit('reconnecting'); this.#connect().catch(() => { From e91509a3e1b91400beb7e442f90ef2b7d3a9c5a3 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Mustafa=20Ate=C5=9F=20Uzun?= Date: Mon, 20 Nov 2023 21:40:06 +0300 Subject: [PATCH 1414/1748] fix `GRAPH.EXPLAIN` - fix transform typo in `transformReply` --- packages/graph/lib/commands/EXPLAIN.ts | 2 +- packages/graph/lib/commands/PROFILE.ts | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/packages/graph/lib/commands/EXPLAIN.ts b/packages/graph/lib/commands/EXPLAIN.ts index 419ff62b112..ebea9ca900d 100644 --- a/packages/graph/lib/commands/EXPLAIN.ts +++ b/packages/graph/lib/commands/EXPLAIN.ts @@ -6,4 +6,4 @@ export function transformArguments(key: string, query: string): Array { return ['GRAPH.EXPLAIN', key, query]; } -export declare function transfromReply(): Array; +export declare function transformReply(): Array; diff --git a/packages/graph/lib/commands/PROFILE.ts b/packages/graph/lib/commands/PROFILE.ts index 473c526e679..c964452f497 100644 --- a/packages/graph/lib/commands/PROFILE.ts +++ b/packages/graph/lib/commands/PROFILE.ts @@ -6,4 +6,4 @@ export function transformArguments(key: string, query: string): Array { return ['GRAPH.PROFILE', key, query]; } -export declare function transfromReply(): Array; +export declare function transformReply(): Array; From 623b56b5ca43a0ebe749da044e7a36ad3042d53b Mon Sep 17 00:00:00 2001 From: Clubsandwich Date: Tue, 21 Nov 2023 03:40:38 +0900 Subject: [PATCH 1415/1748] fix `cluster.sUnsubscribe` - make `listener` optional --- packages/client/lib/cluster/index.ts | 18 +++++++++--------- 1 file changed, 9 insertions(+), 9 deletions(-) diff --git a/packages/client/lib/cluster/index.ts b/packages/client/lib/cluster/index.ts index 7e486a376b6..49ac293d6cf 100644 --- a/packages/client/lib/cluster/index.ts +++ b/packages/client/lib/cluster/index.ts @@ -20,12 +20,12 @@ export interface RedisClusterOptions< S extends RedisScripts = Record > extends RedisExtensions { /** - * Should contain details for some of the cluster nodes that the client will use to discover + * Should contain details for some of the cluster nodes that the client will use to discover * the "cluster topology". We recommend including details for at least 3 nodes here. */ rootNodes: Array; /** - * Default values used for every client in the cluster. Use this to specify global values, + * Default values used for every client in the cluster. Use this to specify global values, * for example: ACL credentials, timeouts, TLS configuration etc. */ defaults?: Partial; @@ -45,7 +45,7 @@ export interface RedisClusterOptions< /** * Mapping between the addresses in the cluster (see `CLUSTER SHARDS`) and the addresses the client should connect to * Useful when the cluster is running on another network - * + * */ nodeAddressMap?: NodeAddressMap; } @@ -98,7 +98,7 @@ export default class RedisCluster< readonly #options: RedisClusterOptions; readonly #slots: RedisClusterSlots; - + get slots() { return this.#slots.slots; } @@ -310,7 +310,7 @@ export default class RedisCluster< listener?: PubSubListener, bufferMode?: T ) { - return this.#slots.executeUnsubscribeCommand(client => + return this.#slots.executeUnsubscribeCommand(client => client.UNSUBSCRIBE(channels, listener, bufferMode) ); } @@ -333,7 +333,7 @@ export default class RedisCluster< listener?: PubSubListener, bufferMode?: T ) { - return this.#slots.executeUnsubscribeCommand(client => + return this.#slots.executeUnsubscribeCommand(client => client.PUNSUBSCRIBE(patterns, listener, bufferMode) ); } @@ -344,7 +344,7 @@ export default class RedisCluster< channels: string | Array, listener: PubSubListener, bufferMode?: T - ) { + ) { const maxCommandRedirections = this.#options.maxCommandRedirections ?? 16, firstChannel = Array.isArray(channels) ? channels[0] : channels; let client = await this.#slots.getShardedPubSubClient(firstChannel); @@ -371,7 +371,7 @@ export default class RedisCluster< SUNSUBSCRIBE( channels: string | Array, - listener: PubSubListener, + listener?: PubSubListener, bufferMode?: T ) { return this.#slots.executeShardedUnsubscribeCommand( @@ -391,7 +391,7 @@ export default class RedisCluster< } nodeClient(node: ShardNode) { - return this.#slots.nodeClient(node); + return this.#slots.nodeClient(node); } getRandomNode() { From 0ee3278085420b2406fb34f804d2fea25e870d39 Mon Sep 17 00:00:00 2001 From: Savvas Papageorgiadis <50584606+papsavas@users.noreply.github.com> Date: Mon, 20 Nov 2023 20:41:44 +0200 Subject: [PATCH 1416/1748] chore(search): export languages (#2651) --- packages/search/lib/index.ts | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/packages/search/lib/index.ts b/packages/search/lib/index.ts index 296136021ef..0f84c11466f 100644 --- a/packages/search/lib/index.ts +++ b/packages/search/lib/index.ts @@ -1,5 +1,5 @@ export { default } from './commands'; -export { RediSearchSchema, SchemaFieldTypes, SchemaTextFieldPhonetics, SearchReply, VectorAlgorithms } from './commands'; -export { AggregateSteps, AggregateGroupByReducers } from './commands/AGGREGATE'; +export { RediSearchSchema, RedisSearchLanguages, SchemaFieldTypes, SchemaTextFieldPhonetics, SearchReply, VectorAlgorithms } from './commands'; +export { AggregateGroupByReducers, AggregateSteps } from './commands/AGGREGATE'; export { SearchOptions } from './commands/SEARCH'; From 9ed9cb5be9454706765b17386a2683f9221c680e Mon Sep 17 00:00:00 2001 From: Guy Korland Date: Mon, 20 Nov 2023 20:42:35 +0200 Subject: [PATCH 1417/1748] export missing graph types --- packages/graph/lib/graph.ts | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/packages/graph/lib/graph.ts b/packages/graph/lib/graph.ts index 5baff1dae29..a95338bd8f3 100644 --- a/packages/graph/lib/graph.ts +++ b/packages/graph/lib/graph.ts @@ -126,11 +126,11 @@ type GraphValue = null | string | number | boolean | Array | { longitude: string; }; -type GraphReply = Omit & { +export type GraphReply = Omit & { data?: Array; }; -type GraphClientType = RedisClientType<{ +export type GraphClientType = RedisClientType<{ graph: { query: typeof import('./commands/QUERY'), roQuery: typeof import('./commands/RO_QUERY') From debd2770028bf17552b4d00b0f42b6f9e3c26a71 Mon Sep 17 00:00:00 2001 From: Leibale Date: Mon, 20 Nov 2023 13:48:13 -0500 Subject: [PATCH 1418/1748] Release client@1.5.12 --- packages/client/package.json | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/packages/client/package.json b/packages/client/package.json index cbb36ab7727..aa1ce57b119 100644 --- a/packages/client/package.json +++ b/packages/client/package.json @@ -1,6 +1,6 @@ { "name": "@redis/client", - "version": "1.5.11", + "version": "1.5.12", "license": "MIT", "main": "./dist/index.js", "types": "./dist/index.d.ts", From 0419b600b8698b5aee96283e5665d46015dfc9c0 Mon Sep 17 00:00:00 2001 From: Leibale Date: Mon, 20 Nov 2023 13:48:43 -0500 Subject: [PATCH 1419/1748] Release graph@1.1.1 --- packages/graph/package.json | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/packages/graph/package.json b/packages/graph/package.json index 9d48d963670..95cce6b8a86 100644 --- a/packages/graph/package.json +++ b/packages/graph/package.json @@ -1,6 +1,6 @@ { "name": "@redis/graph", - "version": "1.1.0", + "version": "1.1.1", "license": "MIT", "main": "./dist/index.js", "types": "./dist/index.d.ts", From a227cb0cefaf231ad449a21d7e1e12b1518055b6 Mon Sep 17 00:00:00 2001 From: Leibale Date: Mon, 20 Nov 2023 13:49:13 -0500 Subject: [PATCH 1420/1748] Release search@1.1.6 --- packages/search/package.json | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/packages/search/package.json b/packages/search/package.json index 48f4e5cdec6..9fb1d0e002d 100644 --- a/packages/search/package.json +++ b/packages/search/package.json @@ -1,6 +1,6 @@ { "name": "@redis/search", - "version": "1.1.5", + "version": "1.1.6", "license": "MIT", "main": "./dist/index.js", "types": "./dist/index.d.ts", From 5e4165eb6a15eee8a698a8c68ef1f94868ee6927 Mon Sep 17 00:00:00 2001 From: Leibale Date: Mon, 20 Nov 2023 13:49:56 -0500 Subject: [PATCH 1421/1748] upgrade subpackages --- package-lock.json | 12 ++++++------ package.json | 6 +++--- 2 files changed, 9 insertions(+), 9 deletions(-) diff --git a/package-lock.json b/package-lock.json index 344044b1340..9b5fd2a0170 100644 --- a/package-lock.json +++ b/package-lock.json @@ -13,10 +13,10 @@ ], "dependencies": { "@redis/bloom": "1.2.0", - "@redis/client": "1.5.11", - "@redis/graph": "1.1.0", + "@redis/client": "1.5.12", + "@redis/graph": "1.1.1", "@redis/json": "1.0.6", - "@redis/search": "1.1.5", + "@redis/search": "1.1.6", "@redis/time-series": "1.0.5" }, "devDependencies": { @@ -8838,7 +8838,7 @@ }, "packages/client": { "name": "@redis/client", - "version": "1.5.11", + "version": "1.5.12", "license": "MIT", "dependencies": { "cluster-key-slot": "1.1.2", @@ -8868,7 +8868,7 @@ }, "packages/graph": { "name": "@redis/graph", - "version": "1.1.0", + "version": "1.1.1", "license": "MIT", "devDependencies": { "@istanbuljs/nyc-config-typescript": "^1.0.2", @@ -8906,7 +8906,7 @@ }, "packages/search": { "name": "@redis/search", - "version": "1.1.5", + "version": "1.1.6", "license": "MIT", "devDependencies": { "@istanbuljs/nyc-config-typescript": "^1.0.2", diff --git a/package.json b/package.json index 2982a32a5e6..b58cb8f2782 100644 --- a/package.json +++ b/package.json @@ -24,10 +24,10 @@ }, "dependencies": { "@redis/bloom": "1.2.0", - "@redis/client": "1.5.11", - "@redis/graph": "1.1.0", + "@redis/client": "1.5.12", + "@redis/graph": "1.1.1", "@redis/json": "1.0.6", - "@redis/search": "1.1.5", + "@redis/search": "1.1.6", "@redis/time-series": "1.0.5" }, "devDependencies": { From d6d2064c72b99d34fc88318f3979177e3c89acd4 Mon Sep 17 00:00:00 2001 From: Leibale Date: Mon, 20 Nov 2023 13:50:15 -0500 Subject: [PATCH 1422/1748] Release redis@4.6.11 --- package-lock.json | 4 ++-- package.json | 2 +- 2 files changed, 3 insertions(+), 3 deletions(-) diff --git a/package-lock.json b/package-lock.json index 9b5fd2a0170..1557c398776 100644 --- a/package-lock.json +++ b/package-lock.json @@ -1,12 +1,12 @@ { "name": "redis", - "version": "4.6.10", + "version": "4.6.11", "lockfileVersion": 3, "requires": true, "packages": { "": { "name": "redis", - "version": "4.6.10", + "version": "4.6.11", "license": "MIT", "workspaces": [ "./packages/*" diff --git a/package.json b/package.json index b58cb8f2782..2924233d9cb 100644 --- a/package.json +++ b/package.json @@ -1,7 +1,7 @@ { "name": "redis", "description": "A modern, high performance Redis client", - "version": "4.6.10", + "version": "4.6.11", "license": "MIT", "main": "./dist/index.js", "types": "./dist/index.d.ts", From f4680f0849a0516453bcab1a23785cbb2888f287 Mon Sep 17 00:00:00 2001 From: Leibale Eidelman Date: Mon, 18 Dec 2023 15:15:21 -0500 Subject: [PATCH 1423/1748] fix #2665 - handle errors in multi/pipeline replies (#2666) * fix #2665 - handle errors in multi/pipeline replies * fix MultiErrorReply replies type * run tests on all versions, remove console.log, fix bug * add errors iterator helper * test `.errors()` as well --- packages/client/lib/client/index.spec.ts | 19 ++++++++++++++++++- packages/client/lib/errors.ts | 19 +++++++++++++++++++ packages/client/lib/multi-command.ts | 22 +++++++++++++++------- 3 files changed, 52 insertions(+), 8 deletions(-) diff --git a/packages/client/lib/client/index.spec.ts b/packages/client/lib/client/index.spec.ts index 3278d27775b..4442d3adb83 100644 --- a/packages/client/lib/client/index.spec.ts +++ b/packages/client/lib/client/index.spec.ts @@ -3,7 +3,7 @@ import testUtils, { GLOBAL, waitTillBeenCalled } from '../test-utils'; import RedisClient, { RedisClientType } from '.'; import { RedisClientMultiCommandType } from './multi-command'; import { RedisCommandRawReply, RedisModules, RedisFunctions, RedisScripts } from '../commands'; -import { AbortError, ClientClosedError, ClientOfflineError, ConnectionTimeoutError, DisconnectsClientError, SocketClosedUnexpectedlyError, WatchError } from '../errors'; +import { AbortError, ClientClosedError, ClientOfflineError, ConnectionTimeoutError, DisconnectsClientError, ErrorReply, MultiErrorReply, SocketClosedUnexpectedlyError, WatchError } from '../errors'; import { defineScript } from '../lua-script'; import { spy } from 'sinon'; import { once } from 'events'; @@ -602,6 +602,23 @@ describe('Client', () => { ...GLOBAL.SERVERS.OPEN, minimumDockerVersion: [6, 2] // CLIENT INFO }); + + testUtils.testWithClient('should handle error replies (#2665)', async client => { + await assert.rejects( + client.multi() + .set('key', 'value') + .hGetAll('key') + .exec(), + err => { + assert.ok(err instanceof MultiErrorReply); + assert.equal(err.replies.length, 2); + assert.deepEqual(err.errorIndexes, [1]); + assert.ok(err.replies[1] instanceof ErrorReply); + assert.deepEqual([...err.errors()], [err.replies[1]]); + return true; + } + ); + }, GLOBAL.SERVERS.OPEN); }); testUtils.testWithClient('scripts', async client => { diff --git a/packages/client/lib/errors.ts b/packages/client/lib/errors.ts index 30709703153..aa97d9cf26d 100644 --- a/packages/client/lib/errors.ts +++ b/packages/client/lib/errors.ts @@ -1,3 +1,5 @@ +import { RedisCommandRawReply } from './commands'; + export class AbortError extends Error { constructor() { super('The command was aborted'); @@ -63,3 +65,20 @@ export class ErrorReply extends Error { this.stack = undefined; } } + +export class MultiErrorReply extends ErrorReply { + replies; + errorIndexes; + + constructor(replies: Array, errorIndexes: Array) { + super(`${errorIndexes.length} commands failed, see .replies and .errorIndexes for more information`); + this.replies = replies; + this.errorIndexes = errorIndexes; + } + + *errors() { + for (const index of this.errorIndexes) { + yield this.replies[index]; + } + } +} diff --git a/packages/client/lib/multi-command.ts b/packages/client/lib/multi-command.ts index 08f23ffa454..642c2ea36c0 100644 --- a/packages/client/lib/multi-command.ts +++ b/packages/client/lib/multi-command.ts @@ -1,6 +1,6 @@ import { fCallArguments } from './commander'; import { RedisCommand, RedisCommandArguments, RedisCommandRawReply, RedisFunction, RedisScript } from './commands'; -import { WatchError } from './errors'; +import { ErrorReply, MultiErrorReply, WatchError } from './errors'; export interface RedisMultiQueuedCommand { args: RedisCommandArguments; @@ -69,7 +69,7 @@ export default class RedisMultiCommand { return transformedArguments; } - handleExecReplies(rawReplies: Array): Array { + handleExecReplies(rawReplies: Array): Array { const execReply = rawReplies[rawReplies.length - 1] as (null | Array); if (execReply === null) { throw new WatchError(); @@ -78,10 +78,18 @@ export default class RedisMultiCommand { return this.transformReplies(execReply); } - transformReplies(rawReplies: Array): Array { - return rawReplies.map((reply, i) => { - const { transformReply, args } = this.queue[i]; - return transformReply ? transformReply(reply, args.preserve) : reply; - }); + transformReplies(rawReplies: Array): Array { + const errorIndexes: Array = [], + replies = rawReplies.map((reply, i) => { + if (reply instanceof ErrorReply) { + errorIndexes.push(i); + return reply; + } + const { transformReply, args } = this.queue[i]; + return transformReply ? transformReply(reply, args.preserve) : reply; + }); + + if (errorIndexes.length) throw new MultiErrorReply(replies, errorIndexes); + return replies; } } From bb6f14cf7e5f68d1658531db5eec4d0ca635570b Mon Sep 17 00:00:00 2001 From: Leibale Date: Mon, 18 Dec 2023 15:18:29 -0500 Subject: [PATCH 1424/1748] Release client@1.5.13 --- packages/client/package.json | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/packages/client/package.json b/packages/client/package.json index aa1ce57b119..15ab1c5f1e6 100644 --- a/packages/client/package.json +++ b/packages/client/package.json @@ -1,6 +1,6 @@ { "name": "@redis/client", - "version": "1.5.12", + "version": "1.5.13", "license": "MIT", "main": "./dist/index.js", "types": "./dist/index.d.ts", From 55d07d276790edfd7d46b6297226c93da4d4d23b Mon Sep 17 00:00:00 2001 From: Leibale Date: Mon, 18 Dec 2023 15:20:16 -0500 Subject: [PATCH 1425/1748] upgrade @redis/client --- package-lock.json | 4 ++-- package.json | 2 +- 2 files changed, 3 insertions(+), 3 deletions(-) diff --git a/package-lock.json b/package-lock.json index 1557c398776..f71f608e9d6 100644 --- a/package-lock.json +++ b/package-lock.json @@ -13,7 +13,7 @@ ], "dependencies": { "@redis/bloom": "1.2.0", - "@redis/client": "1.5.12", + "@redis/client": "1.5.13", "@redis/graph": "1.1.1", "@redis/json": "1.0.6", "@redis/search": "1.1.6", @@ -8838,7 +8838,7 @@ }, "packages/client": { "name": "@redis/client", - "version": "1.5.12", + "version": "1.5.13", "license": "MIT", "dependencies": { "cluster-key-slot": "1.1.2", diff --git a/package.json b/package.json index 2924233d9cb..5845f6faffa 100644 --- a/package.json +++ b/package.json @@ -24,7 +24,7 @@ }, "dependencies": { "@redis/bloom": "1.2.0", - "@redis/client": "1.5.12", + "@redis/client": "1.5.13", "@redis/graph": "1.1.1", "@redis/json": "1.0.6", "@redis/search": "1.1.6", From ccd96101773c7332c01a2eb45cfbd21868a7490b Mon Sep 17 00:00:00 2001 From: Leibale Date: Mon, 18 Dec 2023 15:20:52 -0500 Subject: [PATCH 1426/1748] Release redis@4.6.12 --- package-lock.json | 4 ++-- package.json | 2 +- 2 files changed, 3 insertions(+), 3 deletions(-) diff --git a/package-lock.json b/package-lock.json index f71f608e9d6..683c1e7c284 100644 --- a/package-lock.json +++ b/package-lock.json @@ -1,12 +1,12 @@ { "name": "redis", - "version": "4.6.11", + "version": "4.6.12", "lockfileVersion": 3, "requires": true, "packages": { "": { "name": "redis", - "version": "4.6.11", + "version": "4.6.12", "license": "MIT", "workspaces": [ "./packages/*" diff --git a/package.json b/package.json index 5845f6faffa..08413982c93 100644 --- a/package.json +++ b/package.json @@ -1,7 +1,7 @@ { "name": "redis", "description": "A modern, high performance Redis client", - "version": "4.6.11", + "version": "4.6.12", "license": "MIT", "main": "./dist/index.js", "types": "./dist/index.d.ts", From 5a96058c2f77c1278a0438ca5923f0772cf74790 Mon Sep 17 00:00:00 2001 From: Chayim Date: Wed, 20 Dec 2023 12:32:49 +0200 Subject: [PATCH 1427/1748] Linking to Redis learning resources (#2628) --- README.md | 13 +++++++++++++ 1 file changed, 13 insertions(+) diff --git a/README.md b/README.md index 95816f51e95..a590372b1b1 100644 --- a/README.md +++ b/README.md @@ -11,6 +11,19 @@ node-redis is a modern, high performance [Redis](https://redis.io) client for Node.js. +## How do I Redis? + +[Learn for free at Redis University](https://university.redis.com/) + +[Build faster with the Redis Launchpad](https://launchpad.redis.com/) + +[Try the Redis Cloud](https://redis.com/try-free/) + +[Dive in developer tutorials](https://developer.redis.com/) + +[Join the Redis community](https://redis.com/community/) + +[Work at Redis](https://redis.com/company/careers/jobs/) ## Packages From 295647cf9d7ddc93b9b516a45098765084a0c2d8 Mon Sep 17 00:00:00 2001 From: Brent Layne Date: Mon, 29 Jan 2024 03:25:26 -0500 Subject: [PATCH 1428/1748] fix(clustered pubsub): check that `client.isOpen` before calling `client.disconnect()` when unsubscribing (#2687) * Confirm the client isOpen before disconnecting * Write tests * fix tests * fix tests --------- Co-authored-by: Leibale Eidelman --- packages/client/lib/cluster/cluster-slots.ts | 4 +-- packages/client/lib/cluster/index.spec.ts | 27 ++++++++++++++++++++ 2 files changed, 29 insertions(+), 2 deletions(-) diff --git a/packages/client/lib/cluster/cluster-slots.ts b/packages/client/lib/cluster/cluster-slots.ts index b540c2fa85f..b1cc49b4c82 100644 --- a/packages/client/lib/cluster/cluster-slots.ts +++ b/packages/client/lib/cluster/cluster-slots.ts @@ -562,7 +562,7 @@ export default class RedisClusterSlots< const client = await this.getPubSubClient(); await unsubscribe(client); - if (!client.isPubSubActive) { + if (!client.isPubSubActive && client.isOpen) { await client.disconnect(); this.pubSubNode = undefined; } @@ -613,7 +613,7 @@ export default class RedisClusterSlots< const client = await master.pubSubClient; await unsubscribe(client); - if (!client.isPubSubActive) { + if (!client.isPubSubActive && client.isOpen) { await client.disconnect(); master.pubSubClient = undefined; } diff --git a/packages/client/lib/cluster/index.spec.ts b/packages/client/lib/cluster/index.spec.ts index 8200375056a..569d716272a 100644 --- a/packages/client/lib/cluster/index.spec.ts +++ b/packages/client/lib/cluster/index.spec.ts @@ -235,6 +235,18 @@ describe('Cluster', () => { assert.equal(cluster.pubSubNode, undefined); }, GLOBAL.CLUSTERS.OPEN); + + testUtils.testWithCluster('concurrent UNSUBSCRIBE does not throw an error (#2685)', async cluster => { + const listener = spy(); + await Promise.all([ + cluster.subscribe('1', listener), + cluster.subscribe('2', listener) + ]); + await Promise.all([ + cluster.unsubscribe('1', listener), + cluster.unsubscribe('2', listener) + ]); + }, GLOBAL.CLUSTERS.OPEN); testUtils.testWithCluster('psubscribe & punsubscribe', async cluster => { const listener = spy(); @@ -323,6 +335,21 @@ describe('Cluster', () => { minimumDockerVersion: [7] }); + testUtils.testWithCluster('concurrent SUNSUBCRIBE does not throw an error (#2685)', async cluster => { + const listener = spy(); + await Promise.all([ + await cluster.sSubscribe('1', listener), + await cluster.sSubscribe('2', listener) + ]); + await Promise.all([ + cluster.sUnsubscribe('1', listener), + cluster.sUnsubscribe('2', listener) + ]); + }, { + ...GLOBAL.CLUSTERS.OPEN, + minimumDockerVersion: [7] + }); + testUtils.testWithCluster('should handle sharded-channel-moved events', async cluster => { const SLOT = 10328, migrating = cluster.slots[SLOT].master, From 0f29f4238f9eebea9fc0f5267c0f424870b7c526 Mon Sep 17 00:00:00 2001 From: Leibale Eidelman Date: Mon, 5 Feb 2024 09:14:24 -0500 Subject: [PATCH 1429/1748] Change json docker version to 2.6.9 --- packages/json/lib/test-utils.ts | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/packages/json/lib/test-utils.ts b/packages/json/lib/test-utils.ts index f4c4e4eb201..55426890e00 100644 --- a/packages/json/lib/test-utils.ts +++ b/packages/json/lib/test-utils.ts @@ -3,7 +3,8 @@ import RedisJSON from '.'; export default new TestUtils({ dockerImageName: 'redislabs/rejson', - dockerImageVersionArgument: 'rejson-version' + dockerImageVersionArgument: 'rejson-version', + defaultDockerVersion: '2.6.9' }); export const GLOBAL = { From 03ab4fbd6c3ceb7500cf95a3465685b0fd8c7152 Mon Sep 17 00:00:00 2001 From: Leibale Date: Mon, 5 Feb 2024 09:24:20 -0500 Subject: [PATCH 1430/1748] Release client@1.5.14 --- packages/client/package.json | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/packages/client/package.json b/packages/client/package.json index 15ab1c5f1e6..839ce6d8b66 100644 --- a/packages/client/package.json +++ b/packages/client/package.json @@ -1,6 +1,6 @@ { "name": "@redis/client", - "version": "1.5.13", + "version": "1.5.14", "license": "MIT", "main": "./dist/index.js", "types": "./dist/index.d.ts", From b6a31dd71f591cd0ff198e94e85b9c041d0e1ab0 Mon Sep 17 00:00:00 2001 From: Leibale Date: Mon, 5 Feb 2024 09:25:14 -0500 Subject: [PATCH 1431/1748] upgrade `@redis/client` --- package-lock.json | 4 ++-- package.json | 2 +- 2 files changed, 3 insertions(+), 3 deletions(-) diff --git a/package-lock.json b/package-lock.json index 683c1e7c284..40301b362c9 100644 --- a/package-lock.json +++ b/package-lock.json @@ -13,7 +13,7 @@ ], "dependencies": { "@redis/bloom": "1.2.0", - "@redis/client": "1.5.13", + "@redis/client": "1.5.14", "@redis/graph": "1.1.1", "@redis/json": "1.0.6", "@redis/search": "1.1.6", @@ -8838,7 +8838,7 @@ }, "packages/client": { "name": "@redis/client", - "version": "1.5.13", + "version": "1.5.14", "license": "MIT", "dependencies": { "cluster-key-slot": "1.1.2", diff --git a/package.json b/package.json index 08413982c93..1f3037dbfee 100644 --- a/package.json +++ b/package.json @@ -24,7 +24,7 @@ }, "dependencies": { "@redis/bloom": "1.2.0", - "@redis/client": "1.5.13", + "@redis/client": "1.5.14", "@redis/graph": "1.1.1", "@redis/json": "1.0.6", "@redis/search": "1.1.6", From dbf8f59a47573e6a1c75b78e566af8c493015d5d Mon Sep 17 00:00:00 2001 From: Leibale Date: Mon, 5 Feb 2024 09:26:12 -0500 Subject: [PATCH 1432/1748] Release redis@4.6.13 --- package-lock.json | 4 ++-- package.json | 2 +- 2 files changed, 3 insertions(+), 3 deletions(-) diff --git a/package-lock.json b/package-lock.json index 40301b362c9..5cae28a971f 100644 --- a/package-lock.json +++ b/package-lock.json @@ -1,12 +1,12 @@ { "name": "redis", - "version": "4.6.12", + "version": "4.6.13", "lockfileVersion": 3, "requires": true, "packages": { "": { "name": "redis", - "version": "4.6.12", + "version": "4.6.13", "license": "MIT", "workspaces": [ "./packages/*" diff --git a/package.json b/package.json index 1f3037dbfee..07e1f13484d 100644 --- a/package.json +++ b/package.json @@ -1,7 +1,7 @@ { "name": "redis", "description": "A modern, high performance Redis client", - "version": "4.6.12", + "version": "4.6.13", "license": "MIT", "main": "./dist/index.js", "types": "./dist/index.d.ts", From bfa803fa55e336301eceaa25ded9f19cb9678b6b Mon Sep 17 00:00:00 2001 From: jjsimps <98552694+jjsimps@users.noreply.github.com> Date: Thu, 16 May 2024 12:17:32 -0500 Subject: [PATCH 1433/1748] Fix race condition when slots are re-calculated (#2731) --- packages/client/lib/cluster/cluster-slots.ts | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/packages/client/lib/cluster/cluster-slots.ts b/packages/client/lib/cluster/cluster-slots.ts index b1cc49b4c82..489ded4854f 100644 --- a/packages/client/lib/cluster/cluster-slots.ts +++ b/packages/client/lib/cluster/cluster-slots.ts @@ -158,13 +158,13 @@ export default class RedisClusterSlots< } async #discover(rootNode?: RedisClusterClientOptions) { - this.#resetSlots(); const addressesInUse = new Set(); try { const shards = await this.#getShards(rootNode), promises: Array> = [], eagerConnect = this.#options.minimizeConnections !== true; + this.#resetSlots(); for (const { from, to, master, replicas } of shards) { const shard: Shard = { master: this.#initiateSlotNode(master, false, eagerConnect, addressesInUse, promises) From d7f7f79e538759366cd733fa0cb978684e5c1305 Mon Sep 17 00:00:00 2001 From: Leibale Date: Thu, 16 May 2024 13:53:52 -0400 Subject: [PATCH 1434/1748] Release client@1.5.15 --- packages/client/package.json | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/packages/client/package.json b/packages/client/package.json index 839ce6d8b66..4e6fce3ed9d 100644 --- a/packages/client/package.json +++ b/packages/client/package.json @@ -1,6 +1,6 @@ { "name": "@redis/client", - "version": "1.5.14", + "version": "1.5.15", "license": "MIT", "main": "./dist/index.js", "types": "./dist/index.d.ts", From 1a3ac1e0970e0e53a2e8d4f8884dde30fe028300 Mon Sep 17 00:00:00 2001 From: Leibale Date: Thu, 16 May 2024 13:56:30 -0400 Subject: [PATCH 1435/1748] upgrade @redis/client --- package-lock.json | 4 ++-- package.json | 2 +- 2 files changed, 3 insertions(+), 3 deletions(-) diff --git a/package-lock.json b/package-lock.json index 5cae28a971f..e45d5dcbf1b 100644 --- a/package-lock.json +++ b/package-lock.json @@ -13,7 +13,7 @@ ], "dependencies": { "@redis/bloom": "1.2.0", - "@redis/client": "1.5.14", + "@redis/client": "1.5.15", "@redis/graph": "1.1.1", "@redis/json": "1.0.6", "@redis/search": "1.1.6", @@ -8838,7 +8838,7 @@ }, "packages/client": { "name": "@redis/client", - "version": "1.5.14", + "version": "1.5.15", "license": "MIT", "dependencies": { "cluster-key-slot": "1.1.2", diff --git a/package.json b/package.json index 07e1f13484d..cde019ed0d3 100644 --- a/package.json +++ b/package.json @@ -24,7 +24,7 @@ }, "dependencies": { "@redis/bloom": "1.2.0", - "@redis/client": "1.5.14", + "@redis/client": "1.5.15", "@redis/graph": "1.1.1", "@redis/json": "1.0.6", "@redis/search": "1.1.6", From d5518e0b5f8a0b2705ff5174f518a9522dd0f9dd Mon Sep 17 00:00:00 2001 From: Leibale Date: Thu, 16 May 2024 14:01:01 -0400 Subject: [PATCH 1436/1748] Release client@1.5.16 --- packages/client/package.json | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/packages/client/package.json b/packages/client/package.json index 4e6fce3ed9d..26f0c0e649c 100644 --- a/packages/client/package.json +++ b/packages/client/package.json @@ -1,6 +1,6 @@ { "name": "@redis/client", - "version": "1.5.15", + "version": "1.5.16", "license": "MIT", "main": "./dist/index.js", "types": "./dist/index.d.ts", From fcf41192d159b54a619f39f2379133bbe25d4c36 Mon Sep 17 00:00:00 2001 From: Leibale Date: Thu, 16 May 2024 14:03:30 -0400 Subject: [PATCH 1437/1748] upgrade @redis/client --- package-lock.json | 4 ++-- package.json | 2 +- 2 files changed, 3 insertions(+), 3 deletions(-) diff --git a/package-lock.json b/package-lock.json index e45d5dcbf1b..a0398526f58 100644 --- a/package-lock.json +++ b/package-lock.json @@ -13,7 +13,7 @@ ], "dependencies": { "@redis/bloom": "1.2.0", - "@redis/client": "1.5.15", + "@redis/client": "1.5.16", "@redis/graph": "1.1.1", "@redis/json": "1.0.6", "@redis/search": "1.1.6", @@ -8838,7 +8838,7 @@ }, "packages/client": { "name": "@redis/client", - "version": "1.5.15", + "version": "1.5.16", "license": "MIT", "dependencies": { "cluster-key-slot": "1.1.2", diff --git a/package.json b/package.json index cde019ed0d3..79ff2afd330 100644 --- a/package.json +++ b/package.json @@ -24,7 +24,7 @@ }, "dependencies": { "@redis/bloom": "1.2.0", - "@redis/client": "1.5.15", + "@redis/client": "1.5.16", "@redis/graph": "1.1.1", "@redis/json": "1.0.6", "@redis/search": "1.1.6", From d5355d43275fedf1b2afc4db8a926f72b05f79c5 Mon Sep 17 00:00:00 2001 From: Leibale Date: Thu, 16 May 2024 14:05:01 -0400 Subject: [PATCH 1438/1748] Release redis@4.6.14 --- package-lock.json | 4 ++-- package.json | 2 +- 2 files changed, 3 insertions(+), 3 deletions(-) diff --git a/package-lock.json b/package-lock.json index a0398526f58..9f977d2c99d 100644 --- a/package-lock.json +++ b/package-lock.json @@ -1,12 +1,12 @@ { "name": "redis", - "version": "4.6.13", + "version": "4.6.14", "lockfileVersion": 3, "requires": true, "packages": { "": { "name": "redis", - "version": "4.6.13", + "version": "4.6.14", "license": "MIT", "workspaces": [ "./packages/*" diff --git a/package.json b/package.json index 79ff2afd330..f42208c55a8 100644 --- a/package.json +++ b/package.json @@ -1,7 +1,7 @@ { "name": "redis", "description": "A modern, high performance Redis client", - "version": "4.6.13", + "version": "4.6.14", "license": "MIT", "main": "./dist/index.js", "types": "./dist/index.d.ts", From 4ac97eeaacceb7f0710e10bb020f8793b37cf820 Mon Sep 17 00:00:00 2001 From: Max Gruenfelder Date: Tue, 2 Jul 2024 18:00:27 +0200 Subject: [PATCH 1439/1748] fix createCluster - copy `options.defaults.socket` before modifying it (#2783) * shallow copy of this.#options.defaults.socket * shallow copy of this.#options.defaults.socket * nit --------- Co-authored-by: Max Gruenfelder Co-authored-by: Leibale Eidelman --- packages/client/lib/cluster/cluster-slots.ts | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/packages/client/lib/cluster/cluster-slots.ts b/packages/client/lib/cluster/cluster-slots.ts index 489ded4854f..45c96a80b50 100644 --- a/packages/client/lib/cluster/cluster-slots.ts +++ b/packages/client/lib/cluster/cluster-slots.ts @@ -269,10 +269,10 @@ export default class RedisClusterSlots< if (this.#options.defaults) { let socket; if (this.#options.defaults.socket) { - socket = options?.socket ? { + socket = { ...this.#options.defaults.socket, - ...options.socket - } : this.#options.defaults.socket; + ...options?.socket + }; } else { socket = options?.socket; } From 60c1c466babd1249e50df5a7fd9289b89348dc6e Mon Sep 17 00:00:00 2001 From: Leibale Date: Tue, 2 Jul 2024 13:37:23 -0400 Subject: [PATCH 1440/1748] Release client@1.5.17 --- packages/client/package.json | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/packages/client/package.json b/packages/client/package.json index 26f0c0e649c..026e4261786 100644 --- a/packages/client/package.json +++ b/packages/client/package.json @@ -1,6 +1,6 @@ { "name": "@redis/client", - "version": "1.5.16", + "version": "1.5.17", "license": "MIT", "main": "./dist/index.js", "types": "./dist/index.d.ts", From b4c1e59fc9db83902889e5a88e3917c7102b32e4 Mon Sep 17 00:00:00 2001 From: Leibale Date: Tue, 2 Jul 2024 13:38:04 -0400 Subject: [PATCH 1441/1748] Upgrade @redis/client --- package-lock.json | 4 ++-- package.json | 2 +- 2 files changed, 3 insertions(+), 3 deletions(-) diff --git a/package-lock.json b/package-lock.json index 9f977d2c99d..dc869d8ddbf 100644 --- a/package-lock.json +++ b/package-lock.json @@ -13,7 +13,7 @@ ], "dependencies": { "@redis/bloom": "1.2.0", - "@redis/client": "1.5.16", + "@redis/client": "1.5.17", "@redis/graph": "1.1.1", "@redis/json": "1.0.6", "@redis/search": "1.1.6", @@ -8838,7 +8838,7 @@ }, "packages/client": { "name": "@redis/client", - "version": "1.5.16", + "version": "1.5.17", "license": "MIT", "dependencies": { "cluster-key-slot": "1.1.2", diff --git a/package.json b/package.json index f42208c55a8..37b307fbac8 100644 --- a/package.json +++ b/package.json @@ -24,7 +24,7 @@ }, "dependencies": { "@redis/bloom": "1.2.0", - "@redis/client": "1.5.16", + "@redis/client": "1.5.17", "@redis/graph": "1.1.1", "@redis/json": "1.0.6", "@redis/search": "1.1.6", From 72345fe1a2227bd726a242f31211ed2596705258 Mon Sep 17 00:00:00 2001 From: Leibale Date: Tue, 2 Jul 2024 13:38:40 -0400 Subject: [PATCH 1442/1748] Release redis@4.6.15 --- package-lock.json | 4 ++-- package.json | 2 +- 2 files changed, 3 insertions(+), 3 deletions(-) diff --git a/package-lock.json b/package-lock.json index dc869d8ddbf..11ae2a61088 100644 --- a/package-lock.json +++ b/package-lock.json @@ -1,12 +1,12 @@ { "name": "redis", - "version": "4.6.14", + "version": "4.6.15", "lockfileVersion": 3, "requires": true, "packages": { "": { "name": "redis", - "version": "4.6.14", + "version": "4.6.15", "license": "MIT", "workspaces": [ "./packages/*" diff --git a/package.json b/package.json index 37b307fbac8..586ef8b8dad 100644 --- a/package.json +++ b/package.json @@ -1,7 +1,7 @@ { "name": "redis", "description": "A modern, high performance Redis client", - "version": "4.6.14", + "version": "4.6.15", "license": "MIT", "main": "./dist/index.js", "types": "./dist/index.d.ts", From b4df2b24a82ab9aae30d81572417810c3145e977 Mon Sep 17 00:00:00 2001 From: Shaya Potter Date: Wed, 10 Jul 2024 19:44:30 +0300 Subject: [PATCH 1443/1748] add support for all hash field expiration commands (#2787) --- packages/client/lib/cluster/commands.ts | 27 ++++++++++ packages/client/lib/commands/HEXPIRE.spec.ts | 40 +++++++++++++++ packages/client/lib/commands/HEXPIRE.ts | 44 +++++++++++++++++ .../client/lib/commands/HEXPIREAT.spec.ts | 49 +++++++++++++++++++ packages/client/lib/commands/HEXPIREAT.ts | 28 +++++++++++ .../client/lib/commands/HEXPIRETIME.spec.ts | 32 ++++++++++++ packages/client/lib/commands/HEXPIRETIME.ts | 21 ++++++++ packages/client/lib/commands/HPERSIST.spec.ts | 33 +++++++++++++ packages/client/lib/commands/HPERSIST.ts | 10 ++++ packages/client/lib/commands/HPEXPIRE.spec.ts | 40 +++++++++++++++ packages/client/lib/commands/HPEXPIRE.ts | 24 +++++++++ .../client/lib/commands/HPEXPIREAT.spec.ts | 48 ++++++++++++++++++ packages/client/lib/commands/HPEXPIREAT.ts | 25 ++++++++++ .../client/lib/commands/HPEXPIRETIME.spec.ts | 33 +++++++++++++ packages/client/lib/commands/HPEXPIRETIME.ts | 11 +++++ packages/client/lib/commands/HPTTL.spec.ts | 33 +++++++++++++ packages/client/lib/commands/HPTTL.ts | 11 +++++ packages/client/lib/commands/HTTL.spec.ts | 34 +++++++++++++ packages/client/lib/commands/HTTL.ts | 11 +++++ packages/client/lib/test-utils.ts | 2 +- 20 files changed, 555 insertions(+), 1 deletion(-) create mode 100644 packages/client/lib/commands/HEXPIRE.spec.ts create mode 100644 packages/client/lib/commands/HEXPIRE.ts create mode 100644 packages/client/lib/commands/HEXPIREAT.spec.ts create mode 100644 packages/client/lib/commands/HEXPIREAT.ts create mode 100644 packages/client/lib/commands/HEXPIRETIME.spec.ts create mode 100644 packages/client/lib/commands/HEXPIRETIME.ts create mode 100644 packages/client/lib/commands/HPERSIST.spec.ts create mode 100644 packages/client/lib/commands/HPERSIST.ts create mode 100644 packages/client/lib/commands/HPEXPIRE.spec.ts create mode 100644 packages/client/lib/commands/HPEXPIRE.ts create mode 100644 packages/client/lib/commands/HPEXPIREAT.spec.ts create mode 100644 packages/client/lib/commands/HPEXPIREAT.ts create mode 100644 packages/client/lib/commands/HPEXPIRETIME.spec.ts create mode 100644 packages/client/lib/commands/HPEXPIRETIME.ts create mode 100644 packages/client/lib/commands/HPTTL.spec.ts create mode 100644 packages/client/lib/commands/HPTTL.ts create mode 100644 packages/client/lib/commands/HTTL.spec.ts create mode 100644 packages/client/lib/commands/HTTL.ts diff --git a/packages/client/lib/cluster/commands.ts b/packages/client/lib/cluster/commands.ts index 84a37862772..b6913b40686 100644 --- a/packages/client/lib/cluster/commands.ts +++ b/packages/client/lib/cluster/commands.ts @@ -53,6 +53,9 @@ import * as GETRANGE from '../commands/GETRANGE'; import * as GETSET from '../commands/GETSET'; import * as HDEL from '../commands/HDEL'; import * as HEXISTS from '../commands/HEXISTS'; +import * as HEXPIRE from '../commands/HEXPIRE'; +import * as HEXPIREAT from '../commands/HEXPIREAT'; +import * as HEXPIRETIME from '../commands/HEXPIRETIME'; import * as HGET from '../commands/HGET'; import * as HGETALL from '../commands/HGETALL'; import * as HINCRBY from '../commands/HINCRBY'; @@ -60,6 +63,11 @@ import * as HINCRBYFLOAT from '../commands/HINCRBYFLOAT'; import * as HKEYS from '../commands/HKEYS'; import * as HLEN from '../commands/HLEN'; import * as HMGET from '../commands/HMGET'; +import * as HPERSIST from '../commands/HPERSIST'; +import * as HPEXPIRE from '../commands/HPEXPIRE'; +import * as HPEXPIREAT from '../commands/HPEXPIREAT'; +import * as HPEXPIRETIME from '../commands/HPEXPIRETIME'; +import * as HPTTL from '../commands/HPTTL'; import * as HRANDFIELD_COUNT_WITHVALUES from '../commands/HRANDFIELD_COUNT_WITHVALUES'; import * as HRANDFIELD_COUNT from '../commands/HRANDFIELD_COUNT'; import * as HRANDFIELD from '../commands/HRANDFIELD'; @@ -67,6 +75,7 @@ import * as HSCAN from '../commands/HSCAN'; import * as HSET from '../commands/HSET'; import * as HSETNX from '../commands/HSETNX'; import * as HSTRLEN from '../commands/HSTRLEN'; +import * as HTTL from '../commands/HTTL'; import * as HVALS from '../commands/HVALS'; import * as INCR from '../commands/INCR'; import * as INCRBY from '../commands/INCRBY'; @@ -321,6 +330,12 @@ export default { hDel: HDEL, HEXISTS, hExists: HEXISTS, + HEXPIRE, + hExpire: HEXPIRE, + HEXPIREAT, + hExpireAt: HEXPIREAT, + HEXPIRETIME, + hExpireTime: HEXPIRETIME, HGET, hGet: HGET, HGETALL, @@ -335,6 +350,16 @@ export default { hLen: HLEN, HMGET, hmGet: HMGET, + HPERSIST, + hPersist: HPERSIST, + HPEXPIRE, + hpExpire: HPEXPIRE, + HPEXPIREAT, + hpExpireAt: HPEXPIREAT, + HPEXPIRETIME, + hpExpireTime: HPEXPIRETIME, + HPTTL, + hpTTL: HPTTL, HRANDFIELD_COUNT_WITHVALUES, hRandFieldCountWithValues: HRANDFIELD_COUNT_WITHVALUES, HRANDFIELD_COUNT, @@ -349,6 +374,8 @@ export default { hSetNX: HSETNX, HSTRLEN, hStrLen: HSTRLEN, + HTTL, + hTTL: HTTL, HVALS, hVals: HVALS, INCR, diff --git a/packages/client/lib/commands/HEXPIRE.spec.ts b/packages/client/lib/commands/HEXPIRE.spec.ts new file mode 100644 index 00000000000..40f841b3af8 --- /dev/null +++ b/packages/client/lib/commands/HEXPIRE.spec.ts @@ -0,0 +1,40 @@ +import { strict as assert } from 'node:assert'; +import testUtils, { GLOBAL } from '../test-utils'; +import { transformArguments } from './HEXPIRE'; +import { HASH_EXPIRATION_TIME } from './HEXPIRETIME'; + +describe('HEXPIRE', () => { + testUtils.isVersionGreaterThanHook([7, 4]); + + describe('transformArguments', () => { + it('string', () => { + assert.deepEqual( + transformArguments('key', 'field', 1), + ['HEXPIRE', 'key', '1', 'FIELDS', '1', 'field'] + ); + }); + + it('array', () => { + assert.deepEqual( + transformArguments('key', ['field1', 'field2'], 1), + ['HEXPIRE', 'key', '1', 'FIELDS', '2', 'field1', 'field2'] + ); + }); + + it('with set option', () => { + assert.deepEqual( + transformArguments('key', ['field1'], 1, 'NX'), + ['HEXPIRE', 'key', '1', 'NX', 'FIELDS', '1', 'field1'] + ); + }); + }); + + testUtils.testWithClient('hexpire', async client => { + assert.deepEqual( + await client.hExpire('key', ['field1'], 0), + [ HASH_EXPIRATION_TIME.FieldNotExists ] + ); + }, { + ...GLOBAL.SERVERS.OPEN + }); +}); diff --git a/packages/client/lib/commands/HEXPIRE.ts b/packages/client/lib/commands/HEXPIRE.ts new file mode 100644 index 00000000000..032790c40bb --- /dev/null +++ b/packages/client/lib/commands/HEXPIRE.ts @@ -0,0 +1,44 @@ +import { RedisCommandArgument } from '.'; +import { pushVerdictArgument } from './generic-transformers'; + +/** + * @readonly + * @enum {number} + */ +export const HASH_EXPIRATION = { + /** @property {number} */ + /** The field does not exist */ + FieldNotExists: -2, + /** @property {number} */ + /** Specified NX | XX | GT | LT condition not met */ + ConditionNotMet: 0, + /** @property {number} */ + /** Expiration time was set or updated */ + Updated: 1, + /** @property {number} */ + /** Field deleted because the specified expiration time is in the past */ + Deleted: 2 +} as const; + +export type HashExpiration = typeof HASH_EXPIRATION[keyof typeof HASH_EXPIRATION]; + +export const FIRST_KEY_INDEX = 1; + +export function transformArguments( + key: RedisCommandArgument, + fields: RedisCommandArgument| Array, + seconds: number, + mode?: 'NX' | 'XX' | 'GT' | 'LT', +) { + const args = ['HEXPIRE', key, seconds.toString()]; + + if (mode) { + args.push(mode); + } + + args.push('FIELDS'); + + return pushVerdictArgument(args, fields); +} + +export declare function transformReply(): Array; \ No newline at end of file diff --git a/packages/client/lib/commands/HEXPIREAT.spec.ts b/packages/client/lib/commands/HEXPIREAT.spec.ts new file mode 100644 index 00000000000..2ab969eb02e --- /dev/null +++ b/packages/client/lib/commands/HEXPIREAT.spec.ts @@ -0,0 +1,49 @@ +import { strict as assert } from 'node:assert'; +import testUtils, { GLOBAL } from '../test-utils'; +import { transformArguments } from './HEXPIREAT'; +import { HASH_EXPIRATION_TIME } from './HEXPIRETIME'; + +describe('HEXPIREAT', () => { + testUtils.isVersionGreaterThanHook([7, 4]); + + describe('transformArguments', () => { + it('string + number', () => { + assert.deepEqual( + transformArguments('key', 'field', 1), + ['HEXPIREAT', 'key', '1', 'FIELDS', '1', 'field'] + ); + }); + + it('array + number', () => { + assert.deepEqual( + transformArguments('key', ['field1', 'field2'], 1), + ['HEXPIREAT', 'key', '1', 'FIELDS', '2', 'field1', 'field2'] + ); + }); + + it('date', () => { + const d = new Date(); + + assert.deepEqual( + transformArguments('key', ['field1'], d), + ['HEXPIREAT', 'key', Math.floor(d.getTime() / 1000).toString(), 'FIELDS', '1', 'field1'] + ); + }); + + it('with set option', () => { + assert.deepEqual( + transformArguments('key', 'field1', 1, 'GT'), + ['HEXPIREAT', 'key', '1', 'GT', 'FIELDS', '1', 'field1'] + ); + }); + }); + + testUtils.testWithClient('expireAt', async client => { + assert.deepEqual( + await client.hExpireAt('key', 'field1', 1), + [ HASH_EXPIRATION_TIME.FieldNotExists ] + ); + }, { + ...GLOBAL.SERVERS.OPEN, + }); +}); diff --git a/packages/client/lib/commands/HEXPIREAT.ts b/packages/client/lib/commands/HEXPIREAT.ts new file mode 100644 index 00000000000..58c52d3a1f6 --- /dev/null +++ b/packages/client/lib/commands/HEXPIREAT.ts @@ -0,0 +1,28 @@ +import { RedisCommandArgument } from '.'; +import { pushVerdictArgument, transformEXAT } from './generic-transformers'; +import { HashExpiration } from './HEXPIRE'; + +export const FIRST_KEY_INDEX = 1; + +export function transformArguments( + key: RedisCommandArgument, + fields: RedisCommandArgument | Array, + timestamp: number | Date, + mode?: 'NX' | 'XX' | 'GT' | 'LT' +) { + const args = [ + 'HEXPIREAT', + key, + transformEXAT(timestamp) + ]; + + if (mode) { + args.push(mode); + } + + args.push('FIELDS') + + return pushVerdictArgument(args, fields); +} + +export declare function transformReply(): Array; \ No newline at end of file diff --git a/packages/client/lib/commands/HEXPIRETIME.spec.ts b/packages/client/lib/commands/HEXPIRETIME.spec.ts new file mode 100644 index 00000000000..4e5ab82a7e9 --- /dev/null +++ b/packages/client/lib/commands/HEXPIRETIME.spec.ts @@ -0,0 +1,32 @@ +import { strict as assert } from 'node:assert'; +import testUtils, { GLOBAL } from '../test-utils'; +import { HASH_EXPIRATION_TIME, transformArguments } from './HEXPIRETIME'; + +describe('HEXPIRETIME', () => { + testUtils.isVersionGreaterThanHook([7, 4]); + + describe('transformArguments', () => { + it('string', () => { + assert.deepEqual( + transformArguments('key', 'field'), + ['HEXPIRETIME', 'key', 'FIELDS', '1', 'field'] + ); + }); + + it('array', () => { + assert.deepEqual( + transformArguments('key', ['field1', 'field2']), + ['HEXPIRETIME', 'key', 'FIELDS', '2', 'field1', 'field2'] + ); + }); + }) + + testUtils.testWithClient('hExpireTime', async client => { + assert.deepEqual( + await client.hExpireTime('key', 'field1'), + [ HASH_EXPIRATION_TIME.FieldNotExists ] + ); + }, { + ...GLOBAL.SERVERS.OPEN, + }); +}); diff --git a/packages/client/lib/commands/HEXPIRETIME.ts b/packages/client/lib/commands/HEXPIRETIME.ts new file mode 100644 index 00000000000..55ae577f9d0 --- /dev/null +++ b/packages/client/lib/commands/HEXPIRETIME.ts @@ -0,0 +1,21 @@ +import { RedisCommandArgument } from '.'; +import { pushVerdictArgument } from './generic-transformers'; + +export const HASH_EXPIRATION_TIME = { + /** @property {number} */ + /** The field does not exist */ + FieldNotExists: -2, + /** @property {number} */ + /** The field exists but has no associated expire */ + NoExpiration: -1, +} as const; + +export const FIRST_KEY_INDEX = 1 + +export const IS_READ_ONLY = true; + +export function transformArguments(key: RedisCommandArgument, fields: RedisCommandArgument | Array) { + return pushVerdictArgument(['HEXPIRETIME', key, 'FIELDS'], fields); +} + +export declare function transformReply(): Array; \ No newline at end of file diff --git a/packages/client/lib/commands/HPERSIST.spec.ts b/packages/client/lib/commands/HPERSIST.spec.ts new file mode 100644 index 00000000000..594dc30cf74 --- /dev/null +++ b/packages/client/lib/commands/HPERSIST.spec.ts @@ -0,0 +1,33 @@ +import { strict as assert } from 'node:assert'; +import testUtils, { GLOBAL } from '../test-utils'; +import { transformArguments } from './HPERSIST'; +import { HASH_EXPIRATION_TIME } from './HEXPIRETIME'; + +describe('HPERSIST', () => { + testUtils.isVersionGreaterThanHook([7, 4]); + + describe('transformArguments', () => { + it('string', () => { + assert.deepEqual( + transformArguments('key', 'field'), + ['HPERSIST', 'key', 'FIELDS', '1', 'field'] + ); + }); + + it('array', () => { + assert.deepEqual( + transformArguments('key', ['field1', 'field2']), + ['HPERSIST', 'key', 'FIELDS', '2', 'field1', 'field2'] + ); + }); + }) + + testUtils.testWithClient('hPersist', async client => { + assert.deepEqual( + await client.hPersist('key', 'field1'), + [ HASH_EXPIRATION_TIME.FieldNotExists ] + ); + }, { + ...GLOBAL.SERVERS.OPEN, + }); +}); diff --git a/packages/client/lib/commands/HPERSIST.ts b/packages/client/lib/commands/HPERSIST.ts new file mode 100644 index 00000000000..862a7548ac1 --- /dev/null +++ b/packages/client/lib/commands/HPERSIST.ts @@ -0,0 +1,10 @@ +import { RedisCommandArgument } from '.'; +import { pushVerdictArgument } from './generic-transformers'; + +export const FIRST_KEY_INDEX = 1; + +export function transformArguments(key: RedisCommandArgument, fields: RedisCommandArgument | Array) { + return pushVerdictArgument(['HPERSIST', key, 'FIELDS'], fields); +} + +export declare function transformReply(): Array | null; \ No newline at end of file diff --git a/packages/client/lib/commands/HPEXPIRE.spec.ts b/packages/client/lib/commands/HPEXPIRE.spec.ts new file mode 100644 index 00000000000..fd11ea359cf --- /dev/null +++ b/packages/client/lib/commands/HPEXPIRE.spec.ts @@ -0,0 +1,40 @@ +import { strict as assert } from 'node:assert'; +import testUtils, { GLOBAL } from '../test-utils'; +import { transformArguments } from './HPEXPIRE'; +import { HASH_EXPIRATION_TIME } from './HEXPIRETIME'; + +describe('HEXPIRE', () => { + testUtils.isVersionGreaterThanHook([7, 4]); + + describe('transformArguments', () => { + it('string', () => { + assert.deepEqual( + transformArguments('key', 'field', 1), + ['HPEXPIRE', 'key', '1', 'FIELDS', '1', 'field'] + ); + }); + + it('array', () => { + assert.deepEqual( + transformArguments('key', ['field1', 'field2'], 1), + ['HPEXPIRE', 'key', '1', 'FIELDS', '2', 'field1', 'field2'] + ); + }); + + it('with set option', () => { + assert.deepEqual( + transformArguments('key', ['field1'], 1, 'NX'), + ['HPEXPIRE', 'key', '1', 'NX', 'FIELDS', '1', 'field1'] + ); + }); + }); + + testUtils.testWithClient('hexpire', async client => { + assert.deepEqual( + await client.hpExpire('key', ['field1'], 0), + [ HASH_EXPIRATION_TIME.FieldNotExists ] + ); + }, { + ...GLOBAL.SERVERS.OPEN + }); +}); diff --git a/packages/client/lib/commands/HPEXPIRE.ts b/packages/client/lib/commands/HPEXPIRE.ts new file mode 100644 index 00000000000..afbb056ed4e --- /dev/null +++ b/packages/client/lib/commands/HPEXPIRE.ts @@ -0,0 +1,24 @@ +import { RedisCommandArgument } from '.'; +import { pushVerdictArgument } from './generic-transformers'; +import { HashExpiration } from "./HEXPIRE"; + +export const FIRST_KEY_INDEX = 1; + +export function transformArguments( + key: RedisCommandArgument, + fields: RedisCommandArgument | Array, + ms: number, + mode?: 'NX' | 'XX' | 'GT' | 'LT', +) { + const args = ['HPEXPIRE', key, ms.toString()]; + + if (mode) { + args.push(mode); + } + + args.push('FIELDS') + + return pushVerdictArgument(args, fields); +} + +export declare function transformReply(): Array | null; \ No newline at end of file diff --git a/packages/client/lib/commands/HPEXPIREAT.spec.ts b/packages/client/lib/commands/HPEXPIREAT.spec.ts new file mode 100644 index 00000000000..abe49f1d8d7 --- /dev/null +++ b/packages/client/lib/commands/HPEXPIREAT.spec.ts @@ -0,0 +1,48 @@ +import { strict as assert } from 'node:assert'; +import testUtils, { GLOBAL } from '../test-utils'; +import { transformArguments } from './HPEXPIREAT'; +import { HASH_EXPIRATION_TIME } from './HEXPIRETIME'; + +describe('HPEXPIREAT', () => { + testUtils.isVersionGreaterThanHook([7, 4]); + + describe('transformArguments', () => { + it('string + number', () => { + assert.deepEqual( + transformArguments('key', 'field', 1), + ['HPEXPIREAT', 'key', '1', 'FIELDS', '1', 'field'] + ); + }); + + it('array + number', () => { + assert.deepEqual( + transformArguments('key', ['field1', 'field2'], 1), + ['HPEXPIREAT', 'key', '1', 'FIELDS', '2', 'field1', 'field2'] + ); + }); + + it('date', () => { + const d = new Date(); + assert.deepEqual( + transformArguments('key', ['field1'], d), + ['HPEXPIREAT', 'key', d.getTime().toString(), 'FIELDS', '1', 'field1'] + ); + }); + + it('with set option', () => { + assert.deepEqual( + transformArguments('key', ['field1'], 1, 'XX'), + ['HPEXPIREAT', 'key', '1', 'XX', 'FIELDS', '1', 'field1'] + ); + }); + }); + + testUtils.testWithClient('hpExpireAt', async client => { + assert.deepEqual( + await client.hpExpireAt('key', ['field1'], 1), + [ HASH_EXPIRATION_TIME.FieldNotExists ] + ); + }, { + ...GLOBAL.SERVERS.OPEN, + }); +}); diff --git a/packages/client/lib/commands/HPEXPIREAT.ts b/packages/client/lib/commands/HPEXPIREAT.ts new file mode 100644 index 00000000000..b6e01d8ee5c --- /dev/null +++ b/packages/client/lib/commands/HPEXPIREAT.ts @@ -0,0 +1,25 @@ +import { RedisCommandArgument } from '.'; +import { pushVerdictArgument, transformEXAT, transformPXAT } from './generic-transformers'; +import { HashExpiration } from './HEXPIRE'; + +export const FIRST_KEY_INDEX = 1; +export const IS_READ_ONLY = true; + +export function transformArguments( + key: RedisCommandArgument, + fields: RedisCommandArgument | Array, + timestamp: number | Date, + mode?: 'NX' | 'XX' | 'GT' | 'LT' +) { + const args = ['HPEXPIREAT', key, transformPXAT(timestamp)]; + + if (mode) { + args.push(mode); + } + + args.push('FIELDS') + + return pushVerdictArgument(args, fields); +} + +export declare function transformReply(): Array | null; \ No newline at end of file diff --git a/packages/client/lib/commands/HPEXPIRETIME.spec.ts b/packages/client/lib/commands/HPEXPIRETIME.spec.ts new file mode 100644 index 00000000000..afaa46a7793 --- /dev/null +++ b/packages/client/lib/commands/HPEXPIRETIME.spec.ts @@ -0,0 +1,33 @@ +import { strict as assert } from 'node:assert'; +import testUtils, { GLOBAL } from '../test-utils'; +import { transformArguments } from './HPEXPIRETIME'; +import { HASH_EXPIRATION_TIME } from './HEXPIRETIME'; + +describe('HPEXPIRETIME', () => { + testUtils.isVersionGreaterThanHook([7, 4]); + + describe('transformArguments', () => { + it('string', () => { + assert.deepEqual( + transformArguments('key', 'field'), + ['HPEXPIRETIME', 'key', 'FIELDS', '1', 'field'] + ); + }); + + it('array', () => { + assert.deepEqual( + transformArguments('key', ['field1', 'field2']), + ['HPEXPIRETIME', 'key', 'FIELDS', '2', 'field1', 'field2'] + ); + }); + }); + + testUtils.testWithClient('hpExpireTime', async client => { + assert.deepEqual( + await client.hpExpireTime('key', 'field1'), + [ HASH_EXPIRATION_TIME.FieldNotExists ] + ); + }, { + ...GLOBAL.SERVERS.OPEN + }); +}); diff --git a/packages/client/lib/commands/HPEXPIRETIME.ts b/packages/client/lib/commands/HPEXPIRETIME.ts new file mode 100644 index 00000000000..22a794ccefa --- /dev/null +++ b/packages/client/lib/commands/HPEXPIRETIME.ts @@ -0,0 +1,11 @@ +import { RedisCommandArgument } from '.'; +import { pushVerdictArgument } from './generic-transformers'; + +export const FIRST_KEY_INDEX = 1; +export const IS_READ_ONLY = true; + +export function transformArguments(key: RedisCommandArgument, fields: RedisCommandArgument | Array) { + return pushVerdictArgument(['HPEXPIRETIME', key, 'FIELDS'], fields); +} + +export declare function transformReply(): Array | null; \ No newline at end of file diff --git a/packages/client/lib/commands/HPTTL.spec.ts b/packages/client/lib/commands/HPTTL.spec.ts new file mode 100644 index 00000000000..0bf10c860e0 --- /dev/null +++ b/packages/client/lib/commands/HPTTL.spec.ts @@ -0,0 +1,33 @@ +import { strict as assert } from 'node:assert'; +import testUtils, { GLOBAL } from '../test-utils'; +import { transformArguments } from './HPTTL'; +import { HASH_EXPIRATION_TIME } from './HEXPIRETIME'; + +describe('HPTTL', () => { + testUtils.isVersionGreaterThanHook([7, 4]); + + describe('transformArguments', () => { + it('string', () => { + assert.deepEqual( + transformArguments('key', 'field'), + ['HPTTL', 'key', 'FIELDS', '1', 'field'] + ); + }); + + it('array', () => { + assert.deepEqual( + transformArguments('key', ['field1', 'field2']), + ['HPTTL', 'key', 'FIELDS', '2', 'field1', 'field2'] + ); + }); + }); + + testUtils.testWithClient('hpTTL', async client => { + assert.deepEqual( + await client.hpTTL('key', 'field1'), + [ HASH_EXPIRATION_TIME.FieldNotExists ] + ); + }, { + ...GLOBAL.SERVERS.OPEN + }); +}); diff --git a/packages/client/lib/commands/HPTTL.ts b/packages/client/lib/commands/HPTTL.ts new file mode 100644 index 00000000000..988b805c0c9 --- /dev/null +++ b/packages/client/lib/commands/HPTTL.ts @@ -0,0 +1,11 @@ +import { RedisCommandArgument } from '.'; +import { pushVerdictArgument } from './generic-transformers'; + +export const FIRST_KEY_INDEX = 1; +export const IS_READ_ONLY = true; + +export function transformArguments(key: RedisCommandArgument, fields: RedisCommandArgument | Array) { + return pushVerdictArgument(['HPTTL', key, 'FIELDS'], fields); +} + +export declare function transformReply(): Array | null; diff --git a/packages/client/lib/commands/HTTL.spec.ts b/packages/client/lib/commands/HTTL.spec.ts new file mode 100644 index 00000000000..a36c681299e --- /dev/null +++ b/packages/client/lib/commands/HTTL.spec.ts @@ -0,0 +1,34 @@ +import { strict as assert } from 'node:assert'; +import testUtils, { GLOBAL } from '../test-utils'; +import { transformArguments } from './HTTL'; +import { HASH_EXPIRATION_TIME } from './HEXPIRETIME'; + +describe('HTTL', () => { + testUtils.isVersionGreaterThanHook([7, 4]); + + describe('transformArguments', () => { + it('string', () => { + assert.deepEqual( + transformArguments('key', 'field'), + ['HTTL', 'key', 'FIELDS', '1', 'field'] + ); + }); + + it('array', () => { + assert.deepEqual( + transformArguments('key', ['field1', 'field2']), + ['HTTL', 'key', 'FIELDS', '2', 'field1', 'field2'] + ); + }); + + }); + + testUtils.testWithClient('hTTL', async client => { + assert.deepEqual( + await client.hTTL('key', 'field1'), + [ HASH_EXPIRATION_TIME.FieldNotExists ] + ); + }, { + ...GLOBAL.SERVERS.OPEN + }); +}); diff --git a/packages/client/lib/commands/HTTL.ts b/packages/client/lib/commands/HTTL.ts new file mode 100644 index 00000000000..d3eedd0db0e --- /dev/null +++ b/packages/client/lib/commands/HTTL.ts @@ -0,0 +1,11 @@ +import { RedisCommandArgument } from '.'; +import { pushVerdictArgument } from './generic-transformers'; + +export const FIRST_KEY_INDEX = 1; +export const IS_READ_ONLY = true; + +export function transformArguments(key: RedisCommandArgument, fields: RedisCommandArgument | Array) { + return pushVerdictArgument(['HTTL', key, 'FIELDS'], fields); +} + +export declare function transformReply(): Array | null; diff --git a/packages/client/lib/test-utils.ts b/packages/client/lib/test-utils.ts index a9db70c860f..fbbac3e0b71 100644 --- a/packages/client/lib/test-utils.ts +++ b/packages/client/lib/test-utils.ts @@ -5,7 +5,7 @@ import { promiseTimeout } from './utils'; const utils = new TestUtils({ dockerImageName: 'redis', dockerImageVersionArgument: 'redis-version', - defaultDockerVersion: '7.2' + defaultDockerVersion: '7.4-rc2' }); export default utils; From 7d43a97bc7de62d8eefcb4b552d75d1e10d7e7c1 Mon Sep 17 00:00:00 2001 From: Shaya Potter Date: Wed, 10 Jul 2024 19:45:06 +0300 Subject: [PATCH 1444/1748] add CLIENT KILL maxAge support (v4) (#2761) --- packages/client/lib/commands/CLIENT_KILL.spec.ts | 10 ++++++++++ packages/client/lib/commands/CLIENT_KILL.ts | 13 +++++++++++-- 2 files changed, 21 insertions(+), 2 deletions(-) diff --git a/packages/client/lib/commands/CLIENT_KILL.spec.ts b/packages/client/lib/commands/CLIENT_KILL.spec.ts index 2fe894f3610..733aaca858b 100644 --- a/packages/client/lib/commands/CLIENT_KILL.spec.ts +++ b/packages/client/lib/commands/CLIENT_KILL.spec.ts @@ -65,6 +65,16 @@ describe('CLIENT KILL', () => { ); }); + it('MAXAGE', () => { + assert.deepEqual( + transformArguments({ + filter: ClientKillFilters.MAXAGE, + maxAge: 10 + }), + ['CLIENT', 'KILL', 'MAXAGE', '10'] + ); + }); + describe('SKIP_ME', () => { it('undefined', () => { assert.deepEqual( diff --git a/packages/client/lib/commands/CLIENT_KILL.ts b/packages/client/lib/commands/CLIENT_KILL.ts index adb2a5a6569..f0b31e5cac0 100644 --- a/packages/client/lib/commands/CLIENT_KILL.ts +++ b/packages/client/lib/commands/CLIENT_KILL.ts @@ -6,7 +6,8 @@ export enum ClientKillFilters { ID = 'ID', TYPE = 'TYPE', USER = 'USER', - SKIP_ME = 'SKIPME' + SKIP_ME = 'SKIPME', + MAXAGE = 'MAXAGE' } interface KillFilter { @@ -37,7 +38,11 @@ type KillSkipMe = ClientKillFilters.SKIP_ME | (KillFilter { + maxAge: number; +} + +type KillFilters = KillAddress | KillLocalAddress | KillId | KillType | KillUser | KillSkipMe | KillMaxage; export function transformArguments(filters: KillFilters | Array): RedisCommandArguments { const args = ['CLIENT', 'KILL']; @@ -89,6 +94,10 @@ function pushFilter(args: RedisCommandArguments, filter: KillFilters): void { case ClientKillFilters.SKIP_ME: args.push(filter.skipMe ? 'yes' : 'no'); break; + + case ClientKillFilters.MAXAGE: + args.push(filter.maxAge.toString()); + break; } } From a1bee1caaf39f98dc3ff99821d111597bfdbe707 Mon Sep 17 00:00:00 2001 From: Shaya Potter Date: Wed, 10 Jul 2024 19:45:17 +0300 Subject: [PATCH 1445/1748] add geoshape support (#2788) copied from what leibele did for v5 --- packages/search/lib/commands/CREATE.spec.ts | 38 ++++++++++++++++++++- packages/search/lib/commands/index.ts | 24 +++++++++++-- 2 files changed, 59 insertions(+), 3 deletions(-) diff --git a/packages/search/lib/commands/CREATE.spec.ts b/packages/search/lib/commands/CREATE.spec.ts index 1a0a4f244bd..31eaf02dbba 100644 --- a/packages/search/lib/commands/CREATE.spec.ts +++ b/packages/search/lib/commands/CREATE.spec.ts @@ -1,7 +1,7 @@ import { strict as assert } from 'assert'; import testUtils, { GLOBAL } from '../test-utils'; import { transformArguments } from './CREATE'; -import { SchemaFieldTypes, SchemaTextFieldPhonetics, RedisSearchLanguages, VectorAlgorithms } from '.'; +import { SchemaFieldTypes, SchemaTextFieldPhonetics, RedisSearchLanguages, VectorAlgorithms, SCHEMA_GEO_SHAPE_COORD_SYSTEM } from '.'; describe('CREATE', () => { describe('transformArguments', () => { @@ -196,6 +196,42 @@ describe('CREATE', () => { }); }); + describe('GEOSHAPE', () => { + describe('without options', () => { + it('SCHEMA_FIELD_TYPE.GEOSHAPE', () => { + assert.deepEqual( + transformArguments('index', { + field: SchemaFieldTypes.GEOSHAPE + }), + ['FT.CREATE', 'index', 'SCHEMA', 'field', 'GEOSHAPE'] + ); + }); + + it('{ type: SCHEMA_FIELD_TYPE.GEOSHAPE }', () => { + assert.deepEqual( + transformArguments('index', { + field: { + type: SchemaFieldTypes.GEOSHAPE + } + }), + ['FT.CREATE', 'index', 'SCHEMA', 'field', 'GEOSHAPE'] + ); + }); + }); + + it('with COORD_SYSTEM', () => { + assert.deepEqual( + transformArguments('index', { + field: { + type: SchemaFieldTypes.GEOSHAPE, + COORD_SYSTEM: SCHEMA_GEO_SHAPE_COORD_SYSTEM.SPHERICAL + } + }), + ['FT.CREATE', 'index', 'SCHEMA', 'field', 'GEOSHAPE', 'COORD_SYSTEM', 'SPHERICAL'] + ); + }); + }); + describe('with generic options', () => { it('with AS', () => { assert.deepEqual( diff --git a/packages/search/lib/commands/index.ts b/packages/search/lib/commands/index.ts index 75a2b4e380a..053e2fab38f 100644 --- a/packages/search/lib/commands/index.ts +++ b/packages/search/lib/commands/index.ts @@ -185,7 +185,8 @@ export enum SchemaFieldTypes { NUMERIC = 'NUMERIC', GEO = 'GEO', TAG = 'TAG', - VECTOR = 'VECTOR' + VECTOR = 'VECTOR', + GEOSHAPE = 'GEOSHAPE' } type CreateSchemaField< @@ -257,6 +258,17 @@ type CreateSchemaHNSWVectorField = CreateSchemaVectorField; +export const SCHEMA_GEO_SHAPE_COORD_SYSTEM = { + SPHERICAL: 'SPHERICAL', + FLAT: 'FLAT' +} as const; + +export type SchemaGeoShapeFieldCoordSystem = typeof SCHEMA_GEO_SHAPE_COORD_SYSTEM[keyof typeof SCHEMA_GEO_SHAPE_COORD_SYSTEM]; + +type CreateSchemaGeoShapeField = CreateSchemaCommonField; + export interface RediSearchSchema { [field: string]: CreateSchemaTextField | @@ -264,7 +276,8 @@ export interface RediSearchSchema { CreateSchemaGeoField | CreateSchemaTagField | CreateSchemaFlatVectorField | - CreateSchemaHNSWVectorField; + CreateSchemaHNSWVectorField | + CreateSchemaGeoShapeField } export function pushSchema(args: RedisCommandArguments, schema: RediSearchSchema) { @@ -361,6 +374,13 @@ export function pushSchema(args: RedisCommandArguments, schema: RediSearchSchema }); continue; // vector fields do not contain SORTABLE and NOINDEX options + + case SchemaFieldTypes.GEOSHAPE: + if (fieldOptions.COORD_SYSTEM !== undefined) { + args.push('COORD_SYSTEM', fieldOptions.COORD_SYSTEM); + } + + continue; // geo shape fields do not contain SORTABLE and NOINDEX options } if (fieldOptions.SORTABLE) { From 5576a0db492cda2cd88e09881bc330aa956dd0f5 Mon Sep 17 00:00:00 2001 From: Shaya Potter Date: Wed, 10 Jul 2024 19:45:33 +0300 Subject: [PATCH 1446/1748] CAE-193: add "IGNORE" options to time series commands (for v4 branch) (#2752) --- packages/time-series/lib/commands/ADD.spec.ts | 16 +++++++++++++--- packages/time-series/lib/commands/ADD.ts | 9 +++++++++ packages/time-series/lib/commands/ALTER.spec.ts | 16 +++++++++++++--- packages/time-series/lib/commands/ALTER.ts | 6 +++++- packages/time-series/lib/commands/CREATE.spec.ts | 16 +++++++++++++--- packages/time-series/lib/commands/CREATE.ts | 7 ++++++- packages/time-series/lib/commands/index.ts | 6 ++++++ 7 files changed, 65 insertions(+), 11 deletions(-) diff --git a/packages/time-series/lib/commands/ADD.spec.ts b/packages/time-series/lib/commands/ADD.spec.ts index 94ad30627f8..07e67c1adec 100644 --- a/packages/time-series/lib/commands/ADD.spec.ts +++ b/packages/time-series/lib/commands/ADD.spec.ts @@ -57,16 +57,26 @@ describe('ADD', () => { ); }); - it('with RETENTION, ENCODING, CHUNK_SIZE, ON_DUPLICATE, LABELS', () => { + it('with IGNORE', () => { + assert.deepEqual( + transformArguments('key', '*', 1, { + IGNORE: { MAX_TIME_DIFF: 1, MAX_VAL_DIFF: 1} + }), + ['TS.ADD', 'key', '*', '1', 'IGNORE', '1', '1'] + ) + }); + + it('with RETENTION, ENCODING, CHUNK_SIZE, ON_DUPLICATE, LABELS, IGNORE', () => { assert.deepEqual( transformArguments('key', '*', 1, { RETENTION: 1, ENCODING: TimeSeriesEncoding.UNCOMPRESSED, CHUNK_SIZE: 1, ON_DUPLICATE: TimeSeriesDuplicatePolicies.BLOCK, - LABELS: { label: 'value' } + LABELS: { label: 'value' }, + IGNORE: { MAX_TIME_DIFF: 1, MAX_VAL_DIFF: 1} }), - ['TS.ADD', 'key', '*', '1', 'RETENTION', '1', 'ENCODING', 'UNCOMPRESSED', 'CHUNK_SIZE', '1', 'ON_DUPLICATE', 'BLOCK', 'LABELS', 'label', 'value'] + ['TS.ADD', 'key', '*', '1', 'RETENTION', '1', 'ENCODING', 'UNCOMPRESSED', 'CHUNK_SIZE', '1', 'ON_DUPLICATE', 'BLOCK', 'LABELS', 'label', 'value', 'IGNORE', '1', '1'] ); }); }); diff --git a/packages/time-series/lib/commands/ADD.ts b/packages/time-series/lib/commands/ADD.ts index 1988a964513..3ed185b9b75 100644 --- a/packages/time-series/lib/commands/ADD.ts +++ b/packages/time-series/lib/commands/ADD.ts @@ -8,14 +8,21 @@ import { Labels, pushLabelsArgument, Timestamp, + pushIgnoreArgument, } from '.'; +export interface TsIgnoreOptions { + MAX_TIME_DIFF: number; + MAX_VAL_DIFF: number; +} + interface AddOptions { RETENTION?: number; ENCODING?: TimeSeriesEncoding; CHUNK_SIZE?: number; ON_DUPLICATE?: TimeSeriesDuplicatePolicies; LABELS?: Labels; + IGNORE?: TsIgnoreOptions; } export const FIRST_KEY_INDEX = 1; @@ -40,6 +47,8 @@ export function transformArguments(key: string, timestamp: Timestamp, value: num pushLabelsArgument(args, options?.LABELS); + pushIgnoreArgument(args, options?.IGNORE); + return args; } diff --git a/packages/time-series/lib/commands/ALTER.spec.ts b/packages/time-series/lib/commands/ALTER.spec.ts index cd066533aa4..7add3eeec3a 100644 --- a/packages/time-series/lib/commands/ALTER.spec.ts +++ b/packages/time-series/lib/commands/ALTER.spec.ts @@ -48,15 +48,25 @@ describe('ALTER', () => { ); }); - it('with RETENTION, CHUNK_SIZE, DUPLICATE_POLICY, LABELS', () => { + it('with IGNORE with MAX_TIME_DIFF', () => { + assert.deepEqual( + transformArguments('key', { + IGNORE: { MAX_TIME_DIFF: 1, MAX_VAL_DIFF: 1} + }), + ['TS.ALTER', 'key', 'IGNORE', '1', '1'] + ) + }); + + it('with RETENTION, CHUNK_SIZE, DUPLICATE_POLICY, LABELS, IGNORE', () => { assert.deepEqual( transformArguments('key', { RETENTION: 1, CHUNK_SIZE: 1, DUPLICATE_POLICY: TimeSeriesDuplicatePolicies.BLOCK, - LABELS: { label: 'value' } + LABELS: { label: 'value' }, + IGNORE: { MAX_TIME_DIFF: 1, MAX_VAL_DIFF: 1} }), - ['TS.ALTER', 'key', 'RETENTION', '1', 'CHUNK_SIZE', '1', 'DUPLICATE_POLICY', 'BLOCK', 'LABELS', 'label', 'value'] + ['TS.ALTER', 'key', 'RETENTION', '1', 'CHUNK_SIZE', '1', 'DUPLICATE_POLICY', 'BLOCK', 'LABELS', 'label', 'value', 'IGNORE', '1', '1'] ); }); }); diff --git a/packages/time-series/lib/commands/ALTER.ts b/packages/time-series/lib/commands/ALTER.ts index 7b9e1e774c6..576153a0cca 100644 --- a/packages/time-series/lib/commands/ALTER.ts +++ b/packages/time-series/lib/commands/ALTER.ts @@ -1,4 +1,5 @@ -import { pushRetentionArgument, Labels, pushLabelsArgument, TimeSeriesDuplicatePolicies, pushChunkSizeArgument, pushDuplicatePolicy } from '.'; +import { pushRetentionArgument, Labels, pushLabelsArgument, TimeSeriesDuplicatePolicies, pushChunkSizeArgument, pushDuplicatePolicy, pushIgnoreArgument } from '.'; +import { TsIgnoreOptions } from './ADD'; export const FIRST_KEY_INDEX = 1; @@ -7,6 +8,7 @@ interface AlterOptions { CHUNK_SIZE?: number; DUPLICATE_POLICY?: TimeSeriesDuplicatePolicies; LABELS?: Labels; + IGNORE?: TsIgnoreOptions; } export function transformArguments(key: string, options?: AlterOptions): Array { @@ -20,6 +22,8 @@ export function transformArguments(key: string, options?: AlterOptions): Array { ['TS.CREATE', 'key', 'LABELS', 'label', 'value'] ); }); + + it('with IGNORE with MAX_TIME_DIFF', () => { + assert.deepEqual( + transformArguments('key', { + IGNORE: { MAX_TIME_DIFF: 1, MAX_VAL_DIFF: 1} + }), + ['TS.CREATE', 'key', 'IGNORE', '1', '1'] + ) + }); - it('with RETENTION, ENCODING, CHUNK_SIZE, DUPLICATE_POLICY, LABELS', () => { + it('with RETENTION, ENCODING, CHUNK_SIZE, DUPLICATE_POLICY, LABELS, IGNORE', () => { assert.deepEqual( transformArguments('key', { RETENTION: 1, ENCODING: TimeSeriesEncoding.UNCOMPRESSED, CHUNK_SIZE: 1, DUPLICATE_POLICY: TimeSeriesDuplicatePolicies.BLOCK, - LABELS: { label: 'value' } + LABELS: { label: 'value' }, + IGNORE: { MAX_TIME_DIFF: 1, MAX_VAL_DIFF: 1} }), - ['TS.CREATE', 'key', 'RETENTION', '1', 'ENCODING', 'UNCOMPRESSED', 'CHUNK_SIZE', '1', 'DUPLICATE_POLICY', 'BLOCK', 'LABELS', 'label', 'value'] + ['TS.CREATE', 'key', 'RETENTION', '1', 'ENCODING', 'UNCOMPRESSED', 'CHUNK_SIZE', '1', 'DUPLICATE_POLICY', 'BLOCK', 'LABELS', 'label', 'value', 'IGNORE', '1', '1'] ); }); }); diff --git a/packages/time-series/lib/commands/CREATE.ts b/packages/time-series/lib/commands/CREATE.ts index a360950feff..a84d4b5f9fb 100644 --- a/packages/time-series/lib/commands/CREATE.ts +++ b/packages/time-series/lib/commands/CREATE.ts @@ -6,8 +6,10 @@ import { TimeSeriesDuplicatePolicies, Labels, pushLabelsArgument, - pushDuplicatePolicy + pushDuplicatePolicy, + pushIgnoreArgument } from '.'; +import { TsIgnoreOptions } from './ADD'; export const FIRST_KEY_INDEX = 1; @@ -17,6 +19,7 @@ interface CreateOptions { CHUNK_SIZE?: number; DUPLICATE_POLICY?: TimeSeriesDuplicatePolicies; LABELS?: Labels; + IGNORE?: TsIgnoreOptions; } export function transformArguments(key: string, options?: CreateOptions): Array { @@ -32,6 +35,8 @@ export function transformArguments(key: string, options?: CreateOptions): Array< pushLabelsArgument(args, options?.LABELS); + pushIgnoreArgument(args, options?.IGNORE); + return args; } diff --git a/packages/time-series/lib/commands/index.ts b/packages/time-series/lib/commands/index.ts index 356b0416648..ca382498060 100644 --- a/packages/time-series/lib/commands/index.ts +++ b/packages/time-series/lib/commands/index.ts @@ -127,6 +127,12 @@ export function transformTimestampArgument(timestamp: Timestamp): string { ).toString(); } +export function pushIgnoreArgument(args: RedisCommandArguments, ignore?: ADD.TsIgnoreOptions) { + if (ignore !== undefined) { + args.push('IGNORE', ignore.MAX_TIME_DIFF.toString(), ignore.MAX_VAL_DIFF.toString()); + } +} + export function pushRetentionArgument(args: RedisCommandArguments, retention?: number): RedisCommandArguments { if (retention !== undefined) { args.push( From 64fca37944a2415ae735e2e046e22967b2b6d125 Mon Sep 17 00:00:00 2001 From: Gabriel Erzse Date: Sun, 14 Jul 2024 14:20:30 +0300 Subject: [PATCH 1447/1748] Support the NOVALUES option of HSCAN (#2711) * Support the NOVALUES option of HSCAN Issue #2705 The NOVALUES option instructs HSCAN to only return keys, without their values. This is materialized as a new command, `hScanNoValues`, given that the return type is different from the usual return type of `hScan`. Also a new iterator is provided, `hScanNoValuesIterator`, for the same reason. * skip hscan novalues test if redis < 7.4 * Also don't test hscan no values iterator < 7.4 --------- Co-authored-by: Shaya Potter --- packages/client/lib/client/index.spec.ts | 25 ++++++ packages/client/lib/client/index.ts | 13 ++- packages/client/lib/cluster/commands.ts | 3 + packages/client/lib/commands/HSCAN.spec.ts | 13 +++ packages/client/lib/commands/HSCAN.ts | 2 +- .../lib/commands/HSCAN_NOVALUES.spec.ts | 79 +++++++++++++++++++ .../client/lib/commands/HSCAN_NOVALUES.ts | 27 +++++++ 7 files changed, 160 insertions(+), 2 deletions(-) create mode 100644 packages/client/lib/commands/HSCAN_NOVALUES.spec.ts create mode 100644 packages/client/lib/commands/HSCAN_NOVALUES.ts diff --git a/packages/client/lib/client/index.spec.ts b/packages/client/lib/client/index.spec.ts index 4442d3adb83..7f93efaa1c3 100644 --- a/packages/client/lib/client/index.spec.ts +++ b/packages/client/lib/client/index.spec.ts @@ -788,6 +788,31 @@ describe('Client', () => { assert.deepEqual(hash, results); }, GLOBAL.SERVERS.OPEN); + testUtils.testWithClient('hScanNoValuesIterator', async client => { + const hash: Record = {}; + const expectedKeys: Array = []; + for (let i = 0; i < 100; i++) { + hash[i.toString()] = i.toString(); + expectedKeys.push(i.toString()); + } + + await client.hSet('key', hash); + + const keys: Array = []; + for await (const key of client.hScanNoValuesIterator('key')) { + keys.push(key); + } + + function sort(a: string, b: string) { + return Number(a) - Number(b); + } + + assert.deepEqual(keys.sort(sort), expectedKeys); + }, { + ...GLOBAL.SERVERS.OPEN, + minimumDockerVersion: [7, 4] + }); + testUtils.testWithClient('sScanIterator', async client => { const members = new Set(); for (let i = 0; i < 100; i++) { diff --git a/packages/client/lib/client/index.ts b/packages/client/lib/client/index.ts index 4c3964c7aa0..d7f33e97b16 100644 --- a/packages/client/lib/client/index.ts +++ b/packages/client/lib/client/index.ts @@ -1,5 +1,5 @@ import COMMANDS from './commands'; -import { RedisCommand, RedisCommandArguments, RedisCommandRawReply, RedisCommandReply, RedisFunctions, RedisModules, RedisExtensions, RedisScript, RedisScripts, RedisCommandSignature, ConvertArgumentType, RedisFunction, ExcludeMappedString, RedisCommands } from '../commands'; +import { RedisCommand, RedisCommandArgument, RedisCommandArguments, RedisCommandRawReply, RedisCommandReply, RedisFunctions, RedisModules, RedisExtensions, RedisScript, RedisScripts, RedisCommandSignature, ConvertArgumentType, RedisFunction, ExcludeMappedString, RedisCommands } from '../commands'; import RedisSocket, { RedisSocketOptions, RedisTlsSocketOptions } from './socket'; import RedisCommandsQueue, { QueueCommandOptions } from './commands-queue'; import RedisClientMultiCommand, { RedisClientMultiCommandType } from './multi-command'; @@ -820,6 +820,17 @@ export default class RedisClient< } while (cursor !== 0); } + async* hScanNoValuesIterator(key: string, options?: ScanOptions): AsyncIterable> { + let cursor = 0; + do { + const reply = await (this as any).hScanNoValues(key, cursor, options); + cursor = reply.cursor; + for (const k of reply.keys) { + yield k; + } + } while (cursor !== 0); + } + async* sScanIterator(key: string, options?: ScanOptions): AsyncIterable { let cursor = 0; do { diff --git a/packages/client/lib/cluster/commands.ts b/packages/client/lib/cluster/commands.ts index b6913b40686..9027c5c0b5e 100644 --- a/packages/client/lib/cluster/commands.ts +++ b/packages/client/lib/cluster/commands.ts @@ -72,6 +72,7 @@ import * as HRANDFIELD_COUNT_WITHVALUES from '../commands/HRANDFIELD_COUNT_WITHV import * as HRANDFIELD_COUNT from '../commands/HRANDFIELD_COUNT'; import * as HRANDFIELD from '../commands/HRANDFIELD'; import * as HSCAN from '../commands/HSCAN'; +import * as HSCAN_NOVALUES from '../commands/HSCAN_NOVALUES'; import * as HSET from '../commands/HSET'; import * as HSETNX from '../commands/HSETNX'; import * as HSTRLEN from '../commands/HSTRLEN'; @@ -368,6 +369,8 @@ export default { hRandField: HRANDFIELD, HSCAN, hScan: HSCAN, + HSCAN_NOVALUES, + hScanNoValues: HSCAN_NOVALUES, HSET, hSet: HSET, HSETNX, diff --git a/packages/client/lib/commands/HSCAN.spec.ts b/packages/client/lib/commands/HSCAN.spec.ts index b426763b99b..6757888a875 100644 --- a/packages/client/lib/commands/HSCAN.spec.ts +++ b/packages/client/lib/commands/HSCAN.spec.ts @@ -73,5 +73,18 @@ describe('HSCAN', () => { tuples: [] } ); + + await Promise.all([ + client.hSet('key', 'a', '1'), + client.hSet('key', 'b', '2') + ]); + + assert.deepEqual( + await client.hScan('key', 0), + { + cursor: 0, + tuples: [{field: 'a', value: '1'}, {field: 'b', value: '2'}] + } + ); }, GLOBAL.SERVERS.OPEN); }); diff --git a/packages/client/lib/commands/HSCAN.ts b/packages/client/lib/commands/HSCAN.ts index ba18fb986bc..5167693b604 100644 --- a/packages/client/lib/commands/HSCAN.ts +++ b/packages/client/lib/commands/HSCAN.ts @@ -16,7 +16,7 @@ export function transformArguments( ], cursor, options); } -type HScanRawReply = [RedisCommandArgument, Array]; +export type HScanRawReply = [RedisCommandArgument, Array]; export interface HScanTuple { field: RedisCommandArgument; diff --git a/packages/client/lib/commands/HSCAN_NOVALUES.spec.ts b/packages/client/lib/commands/HSCAN_NOVALUES.spec.ts new file mode 100644 index 00000000000..7e05b841e43 --- /dev/null +++ b/packages/client/lib/commands/HSCAN_NOVALUES.spec.ts @@ -0,0 +1,79 @@ +import { strict as assert } from 'assert'; +import testUtils, { GLOBAL } from '../test-utils'; +import { transformArguments, transformReply } from './HSCAN_NOVALUES'; + +describe('HSCAN_NOVALUES', () => { + testUtils.isVersionGreaterThanHook([7, 4]); + + describe('transformArguments', () => { + it('cusror only', () => { + assert.deepEqual( + transformArguments('key', 0), + ['HSCAN', 'key', '0', 'NOVALUES'] + ); + }); + + it('with MATCH', () => { + assert.deepEqual( + transformArguments('key', 0, { + MATCH: 'pattern' + }), + ['HSCAN', 'key', '0', 'MATCH', 'pattern', 'NOVALUES'] + ); + }); + + it('with COUNT', () => { + assert.deepEqual( + transformArguments('key', 0, { + COUNT: 1 + }), + ['HSCAN', 'key', '0', 'COUNT', '1', 'NOVALUES'] + ); + }); + }); + + describe('transformReply', () => { + it('without keys', () => { + assert.deepEqual( + transformReply(['0', []]), + { + cursor: 0, + keys: [] + } + ); + }); + + it('with keys', () => { + assert.deepEqual( + transformReply(['0', ['key1', 'key2']]), + { + cursor: 0, + keys: ['key1', 'key2'] + } + ); + }); + }); + + testUtils.testWithClient('client.hScanNoValues', async client => { + assert.deepEqual( + await client.hScanNoValues('key', 0), + { + cursor: 0, + keys: [] + } + ); + + await Promise.all([ + client.hSet('key', 'a', '1'), + client.hSet('key', 'b', '2') + ]); + + assert.deepEqual( + await client.hScanNoValues('key', 0), + { + cursor: 0, + keys: ['a', 'b'] + } + ); + }, GLOBAL.SERVERS.OPEN); +}); diff --git a/packages/client/lib/commands/HSCAN_NOVALUES.ts b/packages/client/lib/commands/HSCAN_NOVALUES.ts new file mode 100644 index 00000000000..bde5d846430 --- /dev/null +++ b/packages/client/lib/commands/HSCAN_NOVALUES.ts @@ -0,0 +1,27 @@ +import { RedisCommandArgument, RedisCommandArguments } from '.'; +import { ScanOptions } from './generic-transformers'; +import { HScanRawReply, transformArguments as transformHScanArguments } from './HSCAN'; + +export { FIRST_KEY_INDEX, IS_READ_ONLY } from './HSCAN'; + +export function transformArguments( + key: RedisCommandArgument, + cursor: number, + options?: ScanOptions +): RedisCommandArguments { + const args = transformHScanArguments(key, cursor, options); + args.push('NOVALUES'); + return args; +} + +interface HScanNoValuesReply { + cursor: number; + keys: Array; +} + +export function transformReply([cursor, rawData]: HScanRawReply): HScanNoValuesReply { + return { + cursor: Number(cursor), + keys: [...rawData] + }; +} From 0e7e2e6e2964ab7b0d0455b23b9b6adc489e5e53 Mon Sep 17 00:00:00 2001 From: Shaya Potter Date: Wed, 17 Jul 2024 11:36:45 +0300 Subject: [PATCH 1448/1748] add missing fields support to schema (v4) (#2789) --- packages/search/lib/commands/CREATE.spec.ts | 30 ++++++++++++++++++ packages/search/lib/commands/index.ts | 35 +++++++++++++++++++-- 2 files changed, 62 insertions(+), 3 deletions(-) diff --git a/packages/search/lib/commands/CREATE.spec.ts b/packages/search/lib/commands/CREATE.spec.ts index 31eaf02dbba..094ba4529f0 100644 --- a/packages/search/lib/commands/CREATE.spec.ts +++ b/packages/search/lib/commands/CREATE.spec.ts @@ -441,6 +441,36 @@ describe('CREATE', () => { ); }); }); + + describe('Missing Values', () => { + it('with INDEXEMPTY', () => { + assert.deepEqual( + transformArguments('index', { + field: { + type: SchemaFieldTypes.TEXT, + MISSING_VALUES: { + INDEXEMPTY: true + } + } + }), + ['FT.CREATE', 'index', 'SCHEMA', 'field', 'TEXT', 'INDEXEMPTY'] + ); + }); + + it('with INDEXMISSING', () => { + assert.deepEqual( + transformArguments('index', { + field: { + type: SchemaFieldTypes.TEXT, + MISSING_VALUES: { + INDEXMISSING: true + } + } + }), + ['FT.CREATE', 'index', 'SCHEMA', 'field', 'TEXT', 'INDEXMISSING'] + ); + }); + }); }); testUtils.testWithClient('client.ft.create', async client => { diff --git a/packages/search/lib/commands/index.ts b/packages/search/lib/commands/index.ts index 053e2fab38f..29dbd2480ce 100644 --- a/packages/search/lib/commands/index.ts +++ b/packages/search/lib/commands/index.ts @@ -189,12 +189,32 @@ export enum SchemaFieldTypes { GEOSHAPE = 'GEOSHAPE' } +export interface MissingValues { + INDEXEMPTY?: boolean; + INDEXMISSING?: boolean; +} + +function pushMissingValues(args: RedisCommandArguments, missingValues?: MissingValues) { + if (!missingValues) { + return; + } + + if (missingValues.INDEXEMPTY) { + args.push("INDEXEMPTY"); + } + + if (missingValues.INDEXMISSING) { + args.push("INDEXMISSING"); + } +} + type CreateSchemaField< T extends SchemaFieldTypes, E = Record > = T | ({ type: T; AS?: string; + MISSING_VALUES?: MissingValues; } & E); type CreateSchemaCommonField< @@ -313,11 +333,14 @@ export function pushSchema(args: RedisCommandArguments, schema: RediSearchSchema args.push('WITHSUFFIXTRIE'); } + pushMissingValues(args, fieldOptions.MISSING_VALUES); + break; - // case SchemaFieldTypes.NUMERIC: - // case SchemaFieldTypes.GEO: - // break; + case SchemaFieldTypes.NUMERIC: + case SchemaFieldTypes.GEO: + pushMissingValues(args, fieldOptions.MISSING_VALUES); + break; case SchemaFieldTypes.TAG: if (fieldOptions.SEPARATOR) { @@ -332,6 +355,8 @@ export function pushSchema(args: RedisCommandArguments, schema: RediSearchSchema args.push('WITHSUFFIXTRIE'); } + pushMissingValues(args, fieldOptions.MISSING_VALUES); + break; case SchemaFieldTypes.VECTOR: @@ -373,6 +398,8 @@ export function pushSchema(args: RedisCommandArguments, schema: RediSearchSchema } }); + pushMissingValues(args, fieldOptions.MISSING_VALUES); + continue; // vector fields do not contain SORTABLE and NOINDEX options case SchemaFieldTypes.GEOSHAPE: @@ -380,6 +407,8 @@ export function pushSchema(args: RedisCommandArguments, schema: RediSearchSchema args.push('COORD_SYSTEM', fieldOptions.COORD_SYSTEM); } + pushMissingValues(args, fieldOptions.MISSING_VALUES); + continue; // geo shape fields do not contain SORTABLE and NOINDEX options } From d09aaa311d45978ae0b97e4c189bc180ed85c9b0 Mon Sep 17 00:00:00 2001 From: Shaya Potter Date: Tue, 23 Jul 2024 19:59:27 +0300 Subject: [PATCH 1449/1748] small internal tweak for maxage per discussion w/ leibele (#2803) --- packages/client/lib/commands/CLIENT_KILL.ts | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/packages/client/lib/commands/CLIENT_KILL.ts b/packages/client/lib/commands/CLIENT_KILL.ts index f0b31e5cac0..b1a53df64d8 100644 --- a/packages/client/lib/commands/CLIENT_KILL.ts +++ b/packages/client/lib/commands/CLIENT_KILL.ts @@ -38,11 +38,11 @@ type KillSkipMe = ClientKillFilters.SKIP_ME | (KillFilter { +interface KillMaxAge extends KillFilter { maxAge: number; } -type KillFilters = KillAddress | KillLocalAddress | KillId | KillType | KillUser | KillSkipMe | KillMaxage; +type KillFilters = KillAddress | KillLocalAddress | KillId | KillType | KillUser | KillSkipMe | KillMaxAge; export function transformArguments(filters: KillFilters | Array): RedisCommandArguments { const args = ['CLIENT', 'KILL']; From b884e132fa54908b022f565263d1402b43cec83a Mon Sep 17 00:00:00 2001 From: Shaya Potter Date: Tue, 23 Jul 2024 21:07:32 +0300 Subject: [PATCH 1450/1748] don't expand/copy rawValues, return it directly. (#2800) --- packages/client/lib/commands/HSCAN_NOVALUES.ts | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/packages/client/lib/commands/HSCAN_NOVALUES.ts b/packages/client/lib/commands/HSCAN_NOVALUES.ts index bde5d846430..37a929754c6 100644 --- a/packages/client/lib/commands/HSCAN_NOVALUES.ts +++ b/packages/client/lib/commands/HSCAN_NOVALUES.ts @@ -22,6 +22,6 @@ interface HScanNoValuesReply { export function transformReply([cursor, rawData]: HScanRawReply): HScanNoValuesReply { return { cursor: Number(cursor), - keys: [...rawData] + keys: rawData }; } From 6f79b49f731a1aaf57f42e16ea72774f062e57a1 Mon Sep 17 00:00:00 2001 From: Shaya Potter Date: Wed, 24 Jul 2024 20:15:01 +0300 Subject: [PATCH 1451/1748] updated per discussion w/ leibele (#2804) --- packages/client/lib/commands/HEXPIRE.ts | 8 ++++---- packages/client/lib/commands/HEXPIRETIME.ts | 4 ++-- 2 files changed, 6 insertions(+), 6 deletions(-) diff --git a/packages/client/lib/commands/HEXPIRE.ts b/packages/client/lib/commands/HEXPIRE.ts index 032790c40bb..938f9039939 100644 --- a/packages/client/lib/commands/HEXPIRE.ts +++ b/packages/client/lib/commands/HEXPIRE.ts @@ -8,16 +8,16 @@ import { pushVerdictArgument } from './generic-transformers'; export const HASH_EXPIRATION = { /** @property {number} */ /** The field does not exist */ - FieldNotExists: -2, + FIELD_NOT_EXISTS: -2, /** @property {number} */ /** Specified NX | XX | GT | LT condition not met */ - ConditionNotMet: 0, + CONDITION_NOT_MET: 0, /** @property {number} */ /** Expiration time was set or updated */ - Updated: 1, + UPDATED: 1, /** @property {number} */ /** Field deleted because the specified expiration time is in the past */ - Deleted: 2 + DELETED: 2 } as const; export type HashExpiration = typeof HASH_EXPIRATION[keyof typeof HASH_EXPIRATION]; diff --git a/packages/client/lib/commands/HEXPIRETIME.ts b/packages/client/lib/commands/HEXPIRETIME.ts index 55ae577f9d0..01764b1032d 100644 --- a/packages/client/lib/commands/HEXPIRETIME.ts +++ b/packages/client/lib/commands/HEXPIRETIME.ts @@ -4,10 +4,10 @@ import { pushVerdictArgument } from './generic-transformers'; export const HASH_EXPIRATION_TIME = { /** @property {number} */ /** The field does not exist */ - FieldNotExists: -2, + FIELD_NOT_EXISTS: -2, /** @property {number} */ /** The field exists but has no associated expire */ - NoExpiration: -1, + NO_EXPIRATION: -1, } as const; export const FIRST_KEY_INDEX = 1 From 54b3e178f9943ae24362cb99c8d91ac865cae8b4 Mon Sep 17 00:00:00 2001 From: Shaya Potter Date: Mon, 29 Jul 2024 16:22:38 +0300 Subject: [PATCH 1452/1748] add addscores to aggregate search command (#2799) * add addscores to aggregate search command * change `true` to `boolean` --------- Co-authored-by: Leibale Eidelman --- packages/search/lib/commands/AGGREGATE.spec.ts | 7 +++++++ packages/search/lib/commands/AGGREGATE.ts | 9 +++++++-- 2 files changed, 14 insertions(+), 2 deletions(-) diff --git a/packages/search/lib/commands/AGGREGATE.spec.ts b/packages/search/lib/commands/AGGREGATE.spec.ts index d1e4565339a..5b34d7dc16f 100644 --- a/packages/search/lib/commands/AGGREGATE.spec.ts +++ b/packages/search/lib/commands/AGGREGATE.spec.ts @@ -19,6 +19,13 @@ describe('AGGREGATE', () => { ); }); + it('with ADDSCORES', () => { + assert.deepEqual( + transformArguments('index', '*', { ADDSCORES: true }), + ['FT.AGGREGATE', 'index', '*', 'ADDSCORES'] + ); + }); + describe('with LOAD', () => { describe('single', () => { describe('without alias', () => { diff --git a/packages/search/lib/commands/AGGREGATE.ts b/packages/search/lib/commands/AGGREGATE.ts index 950d959243a..0cab9b25d48 100644 --- a/packages/search/lib/commands/AGGREGATE.ts +++ b/packages/search/lib/commands/AGGREGATE.ts @@ -119,7 +119,8 @@ type LoadField = PropertyName | { } export interface AggregateOptions { - VERBATIM?: true; + VERBATIM?: boolean; + ADDSCORES?: boolean; LOAD?: LoadField | Array; STEPS?: Array; PARAMS?: Params; @@ -150,6 +151,10 @@ export function pushAggregatehOptions( args.push('VERBATIM'); } + if (options?.ADDSCORES) { + args.push('ADDSCORES'); + } + if (options?.LOAD) { args.push('LOAD'); pushArgumentsWithLength(args, () => { @@ -308,4 +313,4 @@ export function transformReply(rawReply: AggregateRawReply): AggregateReply { total: rawReply[0], results }; -} \ No newline at end of file +} From dc3be85db7a4d64f8d15b80ccb72e6c1282e71c2 Mon Sep 17 00:00:00 2001 From: Shaya Potter Date: Mon, 29 Jul 2024 16:24:02 +0300 Subject: [PATCH 1453/1748] add 7.4-rc2 to github action test suite (#2793) * add 7.4-rc2 to github action test suite * Update tests.yml --------- Co-authored-by: Leibale Eidelman --- .github/workflows/tests.yml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.github/workflows/tests.yml b/.github/workflows/tests.yml index 99181e08e5e..852691b3c82 100644 --- a/.github/workflows/tests.yml +++ b/.github/workflows/tests.yml @@ -17,7 +17,7 @@ jobs: fail-fast: false matrix: node-version: ['18', '20'] - redis-version: ['5', '6.0', '6.2', '7.0', '7.2'] + redis-version: ['5', '6.0', '6.2', '7.0', '7.2', '7.4'] steps: - uses: actions/checkout@v4 with: From 179b9e009422235982a1e706c8ac79fd9e3496c0 Mon Sep 17 00:00:00 2001 From: Marco Reni Date: Mon, 29 Jul 2024 15:25:03 +0200 Subject: [PATCH 1454/1748] fix: json.mget should be readonly (#2807) (#2808) * fix: json.mget should be readonly (#2807) * lint --- packages/json/lib/commands/MGET.ts | 2 ++ 1 file changed, 2 insertions(+) diff --git a/packages/json/lib/commands/MGET.ts b/packages/json/lib/commands/MGET.ts index 582b73bf85a..34ca8da289f 100644 --- a/packages/json/lib/commands/MGET.ts +++ b/packages/json/lib/commands/MGET.ts @@ -2,6 +2,8 @@ import { RedisJSON, transformRedisJsonNullReply } from '.'; export const FIRST_KEY_INDEX = 1; +export const IS_READ_ONLY = true; + export function transformArguments(keys: Array, path: string): Array { return [ 'JSON.MGET', From 840ec57fb965ad66113608cd8e80154a4db3b2e6 Mon Sep 17 00:00:00 2001 From: Shaya Potter Date: Mon, 29 Jul 2024 16:27:53 +0300 Subject: [PATCH 1455/1748] small refactor per discussion with leibele (#2801) * small refactor per discussion with leibele * move true type to boolean type * fix geoshape to support NOINDEX & SORTABLE, clean code * fix for last commit --------- Co-authored-by: Leibale Eidelman --- packages/search/lib/commands/CREATE.spec.ts | 66 ++++++++-------- packages/search/lib/commands/index.ts | 83 ++++++++++----------- 2 files changed, 76 insertions(+), 73 deletions(-) diff --git a/packages/search/lib/commands/CREATE.spec.ts b/packages/search/lib/commands/CREATE.spec.ts index 094ba4529f0..50c5c011c89 100644 --- a/packages/search/lib/commands/CREATE.spec.ts +++ b/packages/search/lib/commands/CREATE.spec.ts @@ -70,6 +70,18 @@ describe('CREATE', () => { ['FT.CREATE', 'index', 'SCHEMA', 'field', 'TEXT', 'WITHSUFFIXTRIE'] ); }); + + it('with INDEXEMPTY', () => { + assert.deepEqual( + transformArguments('index', { + field: { + type: SchemaFieldTypes.TEXT, + INDEXEMPTY: true + } + }), + ['FT.CREATE', 'index', 'SCHEMA', 'field', 'TEXT', 'INDEXEMPTY'] + ); + }); }); it('NUMERIC', () => { @@ -148,6 +160,18 @@ describe('CREATE', () => { ['FT.CREATE', 'index', 'SCHEMA', 'field', 'TAG', 'WITHSUFFIXTRIE'] ); }); + + it('with INDEXEMPTY', () => { + assert.deepEqual( + transformArguments('index', { + field: { + type: SchemaFieldTypes.TAG, + INDEXEMPTY: true + } + }), + ['FT.CREATE', 'index', 'SCHEMA', 'field', 'TAG', 'INDEXEMPTY'] + ); + }); }); describe('VECTOR', () => { @@ -282,6 +306,18 @@ describe('CREATE', () => { ['FT.CREATE', 'index', 'SCHEMA', 'field', 'TEXT', 'NOINDEX'] ); }); + + it('with INDEXMISSING', () => { + assert.deepEqual( + transformArguments('index', { + field: { + type: SchemaFieldTypes.TEXT, + INDEXMISSING: true + } + }), + ['FT.CREATE', 'index', 'SCHEMA', 'field', 'TEXT', 'INDEXMISSING'] + ); + }); }); }); @@ -441,36 +477,6 @@ describe('CREATE', () => { ); }); }); - - describe('Missing Values', () => { - it('with INDEXEMPTY', () => { - assert.deepEqual( - transformArguments('index', { - field: { - type: SchemaFieldTypes.TEXT, - MISSING_VALUES: { - INDEXEMPTY: true - } - } - }), - ['FT.CREATE', 'index', 'SCHEMA', 'field', 'TEXT', 'INDEXEMPTY'] - ); - }); - - it('with INDEXMISSING', () => { - assert.deepEqual( - transformArguments('index', { - field: { - type: SchemaFieldTypes.TEXT, - MISSING_VALUES: { - INDEXMISSING: true - } - } - }), - ['FT.CREATE', 'index', 'SCHEMA', 'field', 'TEXT', 'INDEXMISSING'] - ); - }); - }); }); testUtils.testWithClient('client.ft.create', async client => { diff --git a/packages/search/lib/commands/index.ts b/packages/search/lib/commands/index.ts index 29dbd2480ce..f907e1999e6 100644 --- a/packages/search/lib/commands/index.ts +++ b/packages/search/lib/commands/index.ts @@ -188,25 +188,6 @@ export enum SchemaFieldTypes { VECTOR = 'VECTOR', GEOSHAPE = 'GEOSHAPE' } - -export interface MissingValues { - INDEXEMPTY?: boolean; - INDEXMISSING?: boolean; -} - -function pushMissingValues(args: RedisCommandArguments, missingValues?: MissingValues) { - if (!missingValues) { - return; - } - - if (missingValues.INDEXEMPTY) { - args.push("INDEXEMPTY"); - } - - if (missingValues.INDEXMISSING) { - args.push("INDEXMISSING"); - } -} type CreateSchemaField< T extends SchemaFieldTypes, @@ -214,20 +195,36 @@ type CreateSchemaField< > = T | ({ type: T; AS?: string; - MISSING_VALUES?: MissingValues; + INDEXMISSING?: boolean; } & E); +type CommonFieldArguments = { + SORTABLE?: boolean | 'UNF'; + NOINDEX?: boolean; +}; + type CreateSchemaCommonField< T extends SchemaFieldTypes, E = Record > = CreateSchemaField< T, - ({ - SORTABLE?: true | 'UNF'; - NOINDEX?: true; - } & E) + (CommonFieldArguments & E) >; +function pushCommonFieldArguments(args: RedisCommandArguments, fieldOptions: CommonFieldArguments) { + if (fieldOptions.SORTABLE) { + args.push('SORTABLE'); + + if (fieldOptions.SORTABLE === 'UNF') { + args.push('UNF'); + } + } + + if (fieldOptions.NOINDEX) { + args.push('NOINDEX'); + } +} + export enum SchemaTextFieldPhonetics { DM_EN = 'dm:en', DM_FR = 'dm:fr', @@ -240,6 +237,7 @@ type CreateSchemaTextField = CreateSchemaCommonField; type CreateSchemaNumericField = CreateSchemaCommonField; @@ -250,6 +248,7 @@ type CreateSchemaTagField = CreateSchemaCommonField; export enum VectorAlgorithms { @@ -297,7 +296,7 @@ export interface RediSearchSchema { CreateSchemaTagField | CreateSchemaFlatVectorField | CreateSchemaHNSWVectorField | - CreateSchemaGeoShapeField + CreateSchemaGeoShapeField; } export function pushSchema(args: RedisCommandArguments, schema: RediSearchSchema) { @@ -333,13 +332,17 @@ export function pushSchema(args: RedisCommandArguments, schema: RediSearchSchema args.push('WITHSUFFIXTRIE'); } - pushMissingValues(args, fieldOptions.MISSING_VALUES); + pushCommonFieldArguments(args, fieldOptions); + + if (fieldOptions.INDEXEMPTY) { + args.push('INDEXEMPTY'); + } break; case SchemaFieldTypes.NUMERIC: case SchemaFieldTypes.GEO: - pushMissingValues(args, fieldOptions.MISSING_VALUES); + pushCommonFieldArguments(args, fieldOptions); break; case SchemaFieldTypes.TAG: @@ -355,7 +358,11 @@ export function pushSchema(args: RedisCommandArguments, schema: RediSearchSchema args.push('WITHSUFFIXTRIE'); } - pushMissingValues(args, fieldOptions.MISSING_VALUES); + pushCommonFieldArguments(args, fieldOptions); + + if (fieldOptions.INDEXEMPTY) { + args.push('INDEXEMPTY'); + } break; @@ -398,30 +405,20 @@ export function pushSchema(args: RedisCommandArguments, schema: RediSearchSchema } }); - pushMissingValues(args, fieldOptions.MISSING_VALUES); - - continue; // vector fields do not contain SORTABLE and NOINDEX options + break; case SchemaFieldTypes.GEOSHAPE: if (fieldOptions.COORD_SYSTEM !== undefined) { args.push('COORD_SYSTEM', fieldOptions.COORD_SYSTEM); } - pushMissingValues(args, fieldOptions.MISSING_VALUES); - - continue; // geo shape fields do not contain SORTABLE and NOINDEX options - } - - if (fieldOptions.SORTABLE) { - args.push('SORTABLE'); + pushCommonFieldArguments(args, fieldOptions); - if (fieldOptions.SORTABLE === 'UNF') { - args.push('UNF'); - } + break; } - if (fieldOptions.NOINDEX) { - args.push('NOINDEX'); + if (fieldOptions.INDEXMISSING) { + args.push('INDEXMISSING'); } } } From 3973fa24e1c658b3c5395cf439b02d215b979719 Mon Sep 17 00:00:00 2001 From: Leibale Date: Mon, 29 Jul 2024 09:39:21 -0400 Subject: [PATCH 1456/1748] Release json@1.0.7 --- packages/json/package.json | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/packages/json/package.json b/packages/json/package.json index 3a168b7d4b5..ad60cc13c26 100644 --- a/packages/json/package.json +++ b/packages/json/package.json @@ -1,6 +1,6 @@ { "name": "@redis/json", - "version": "1.0.6", + "version": "1.0.7", "license": "MIT", "main": "./dist/index.js", "types": "./dist/index.d.ts", From 49ef273df2727fa3007a460d828958276f408d52 Mon Sep 17 00:00:00 2001 From: Leibale Eidelman Date: Mon, 29 Jul 2024 09:56:39 -0400 Subject: [PATCH 1457/1748] Update tests.yml --- .github/workflows/tests.yml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.github/workflows/tests.yml b/.github/workflows/tests.yml index 852691b3c82..84d70d6b4c0 100644 --- a/.github/workflows/tests.yml +++ b/.github/workflows/tests.yml @@ -17,7 +17,7 @@ jobs: fail-fast: false matrix: node-version: ['18', '20'] - redis-version: ['5', '6.0', '6.2', '7.0', '7.2', '7.4'] + redis-version: ['5', '6.0', '6.2', '7.0', '7.2', '7.4-rc2'] steps: - uses: actions/checkout@v4 with: From 18ccc28c2fb5629fc47aa6450f1efa964e363ddd Mon Sep 17 00:00:00 2001 From: Leibale Date: Mon, 29 Jul 2024 10:11:04 -0400 Subject: [PATCH 1458/1748] fix hash expiration tests --- packages/client/lib/commands/HEXPIRE.spec.ts | 2 +- packages/client/lib/commands/HEXPIREAT.spec.ts | 2 +- packages/client/lib/commands/HEXPIRETIME.spec.ts | 2 +- packages/client/lib/commands/HPERSIST.spec.ts | 2 +- packages/client/lib/commands/HPEXPIRE.spec.ts | 2 +- packages/client/lib/commands/HPEXPIREAT.spec.ts | 2 +- packages/client/lib/commands/HPEXPIRETIME.spec.ts | 2 +- packages/client/lib/commands/HPTTL.spec.ts | 2 +- packages/client/lib/commands/HTTL.spec.ts | 2 +- 9 files changed, 9 insertions(+), 9 deletions(-) diff --git a/packages/client/lib/commands/HEXPIRE.spec.ts b/packages/client/lib/commands/HEXPIRE.spec.ts index 40f841b3af8..3714f617f58 100644 --- a/packages/client/lib/commands/HEXPIRE.spec.ts +++ b/packages/client/lib/commands/HEXPIRE.spec.ts @@ -32,7 +32,7 @@ describe('HEXPIRE', () => { testUtils.testWithClient('hexpire', async client => { assert.deepEqual( await client.hExpire('key', ['field1'], 0), - [ HASH_EXPIRATION_TIME.FieldNotExists ] + [HASH_EXPIRATION_TIME.FIELD_NOT_EXISTS] ); }, { ...GLOBAL.SERVERS.OPEN diff --git a/packages/client/lib/commands/HEXPIREAT.spec.ts b/packages/client/lib/commands/HEXPIREAT.spec.ts index 2ab969eb02e..1c65fb61773 100644 --- a/packages/client/lib/commands/HEXPIREAT.spec.ts +++ b/packages/client/lib/commands/HEXPIREAT.spec.ts @@ -41,7 +41,7 @@ describe('HEXPIREAT', () => { testUtils.testWithClient('expireAt', async client => { assert.deepEqual( await client.hExpireAt('key', 'field1', 1), - [ HASH_EXPIRATION_TIME.FieldNotExists ] + [HASH_EXPIRATION_TIME.FIELD_NOT_EXISTS] ); }, { ...GLOBAL.SERVERS.OPEN, diff --git a/packages/client/lib/commands/HEXPIRETIME.spec.ts b/packages/client/lib/commands/HEXPIRETIME.spec.ts index 4e5ab82a7e9..9c3eb024bed 100644 --- a/packages/client/lib/commands/HEXPIRETIME.spec.ts +++ b/packages/client/lib/commands/HEXPIRETIME.spec.ts @@ -24,7 +24,7 @@ describe('HEXPIRETIME', () => { testUtils.testWithClient('hExpireTime', async client => { assert.deepEqual( await client.hExpireTime('key', 'field1'), - [ HASH_EXPIRATION_TIME.FieldNotExists ] + [HASH_EXPIRATION_TIME.FIELD_NOT_EXISTS] ); }, { ...GLOBAL.SERVERS.OPEN, diff --git a/packages/client/lib/commands/HPERSIST.spec.ts b/packages/client/lib/commands/HPERSIST.spec.ts index 594dc30cf74..8cf3f1fe221 100644 --- a/packages/client/lib/commands/HPERSIST.spec.ts +++ b/packages/client/lib/commands/HPERSIST.spec.ts @@ -25,7 +25,7 @@ describe('HPERSIST', () => { testUtils.testWithClient('hPersist', async client => { assert.deepEqual( await client.hPersist('key', 'field1'), - [ HASH_EXPIRATION_TIME.FieldNotExists ] + [HASH_EXPIRATION_TIME.FIELD_NOT_EXISTS] ); }, { ...GLOBAL.SERVERS.OPEN, diff --git a/packages/client/lib/commands/HPEXPIRE.spec.ts b/packages/client/lib/commands/HPEXPIRE.spec.ts index fd11ea359cf..852d9f5bd21 100644 --- a/packages/client/lib/commands/HPEXPIRE.spec.ts +++ b/packages/client/lib/commands/HPEXPIRE.spec.ts @@ -32,7 +32,7 @@ describe('HEXPIRE', () => { testUtils.testWithClient('hexpire', async client => { assert.deepEqual( await client.hpExpire('key', ['field1'], 0), - [ HASH_EXPIRATION_TIME.FieldNotExists ] + [HASH_EXPIRATION_TIME.FIELD_NOT_EXISTS] ); }, { ...GLOBAL.SERVERS.OPEN diff --git a/packages/client/lib/commands/HPEXPIREAT.spec.ts b/packages/client/lib/commands/HPEXPIREAT.spec.ts index abe49f1d8d7..9747cca1a2d 100644 --- a/packages/client/lib/commands/HPEXPIREAT.spec.ts +++ b/packages/client/lib/commands/HPEXPIREAT.spec.ts @@ -40,7 +40,7 @@ describe('HPEXPIREAT', () => { testUtils.testWithClient('hpExpireAt', async client => { assert.deepEqual( await client.hpExpireAt('key', ['field1'], 1), - [ HASH_EXPIRATION_TIME.FieldNotExists ] + [HASH_EXPIRATION_TIME.FIELD_NOT_EXISTS] ); }, { ...GLOBAL.SERVERS.OPEN, diff --git a/packages/client/lib/commands/HPEXPIRETIME.spec.ts b/packages/client/lib/commands/HPEXPIRETIME.spec.ts index afaa46a7793..ff03b73c71d 100644 --- a/packages/client/lib/commands/HPEXPIRETIME.spec.ts +++ b/packages/client/lib/commands/HPEXPIRETIME.spec.ts @@ -25,7 +25,7 @@ describe('HPEXPIRETIME', () => { testUtils.testWithClient('hpExpireTime', async client => { assert.deepEqual( await client.hpExpireTime('key', 'field1'), - [ HASH_EXPIRATION_TIME.FieldNotExists ] + [HASH_EXPIRATION_TIME.FIELD_NOT_EXISTS] ); }, { ...GLOBAL.SERVERS.OPEN diff --git a/packages/client/lib/commands/HPTTL.spec.ts b/packages/client/lib/commands/HPTTL.spec.ts index 0bf10c860e0..ddca26ea85b 100644 --- a/packages/client/lib/commands/HPTTL.spec.ts +++ b/packages/client/lib/commands/HPTTL.spec.ts @@ -25,7 +25,7 @@ describe('HPTTL', () => { testUtils.testWithClient('hpTTL', async client => { assert.deepEqual( await client.hpTTL('key', 'field1'), - [ HASH_EXPIRATION_TIME.FieldNotExists ] + [HASH_EXPIRATION_TIME.FIELD_NOT_EXISTS] ); }, { ...GLOBAL.SERVERS.OPEN diff --git a/packages/client/lib/commands/HTTL.spec.ts b/packages/client/lib/commands/HTTL.spec.ts index a36c681299e..21b8b329a5d 100644 --- a/packages/client/lib/commands/HTTL.spec.ts +++ b/packages/client/lib/commands/HTTL.spec.ts @@ -26,7 +26,7 @@ describe('HTTL', () => { testUtils.testWithClient('hTTL', async client => { assert.deepEqual( await client.hTTL('key', 'field1'), - [ HASH_EXPIRATION_TIME.FieldNotExists ] + [HASH_EXPIRATION_TIME.FIELD_NOT_EXISTS] ); }, { ...GLOBAL.SERVERS.OPEN From fff7b006c393e2b4e78e4f953398dfd24ed78ee0 Mon Sep 17 00:00:00 2001 From: Leibale Date: Mon, 29 Jul 2024 10:25:26 -0400 Subject: [PATCH 1459/1748] Release search@1.2.0 --- packages/search/package.json | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/packages/search/package.json b/packages/search/package.json index 9fb1d0e002d..aaf9bc50f11 100644 --- a/packages/search/package.json +++ b/packages/search/package.json @@ -1,6 +1,6 @@ { "name": "@redis/search", - "version": "1.1.6", + "version": "1.2.0", "license": "MIT", "main": "./dist/index.js", "types": "./dist/index.d.ts", From 268ceda6259f8fafe04f456311fa395df116b50d Mon Sep 17 00:00:00 2001 From: Leibale Date: Mon, 29 Jul 2024 10:27:51 -0400 Subject: [PATCH 1460/1748] Release time-series@1.1.0 --- packages/time-series/package.json | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/packages/time-series/package.json b/packages/time-series/package.json index ee3c7526192..65ee1e99c23 100644 --- a/packages/time-series/package.json +++ b/packages/time-series/package.json @@ -1,6 +1,6 @@ { "name": "@redis/time-series", - "version": "1.0.5", + "version": "1.1.0", "license": "MIT", "main": "./dist/index.js", "types": "./dist/index.d.ts", From b493afac60b7294446aaf80853d17a0200d1ab5c Mon Sep 17 00:00:00 2001 From: Leibale Date: Mon, 29 Jul 2024 10:29:17 -0400 Subject: [PATCH 1461/1748] Release client@1.6.0 --- packages/client/package.json | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/packages/client/package.json b/packages/client/package.json index 026e4261786..e344edd52c3 100644 --- a/packages/client/package.json +++ b/packages/client/package.json @@ -1,6 +1,6 @@ { "name": "@redis/client", - "version": "1.5.17", + "version": "1.6.0", "license": "MIT", "main": "./dist/index.js", "types": "./dist/index.d.ts", From 878fb69591fb1877823bec6876eb22025a3815b2 Mon Sep 17 00:00:00 2001 From: Leibale Date: Mon, 29 Jul 2024 10:32:42 -0400 Subject: [PATCH 1462/1748] upgrade deps --- package-lock.json | 16 ++++++++-------- package.json | 8 ++++---- 2 files changed, 12 insertions(+), 12 deletions(-) diff --git a/package-lock.json b/package-lock.json index 11ae2a61088..9fc773717d7 100644 --- a/package-lock.json +++ b/package-lock.json @@ -13,11 +13,11 @@ ], "dependencies": { "@redis/bloom": "1.2.0", - "@redis/client": "1.5.17", + "@redis/client": "1.6.0", "@redis/graph": "1.1.1", - "@redis/json": "1.0.6", - "@redis/search": "1.1.6", - "@redis/time-series": "1.0.5" + "@redis/json": "1.0.7", + "@redis/search": "1.2.0", + "@redis/time-series": "1.1.0" }, "devDependencies": { "@tsconfig/node14": "^14.1.0", @@ -8838,7 +8838,7 @@ }, "packages/client": { "name": "@redis/client", - "version": "1.5.17", + "version": "1.6.0", "license": "MIT", "dependencies": { "cluster-key-slot": "1.1.2", @@ -8887,7 +8887,7 @@ }, "packages/json": { "name": "@redis/json", - "version": "1.0.6", + "version": "1.0.7", "license": "MIT", "devDependencies": { "@istanbuljs/nyc-config-typescript": "^1.0.2", @@ -8906,7 +8906,7 @@ }, "packages/search": { "name": "@redis/search", - "version": "1.1.6", + "version": "1.2.0", "license": "MIT", "devDependencies": { "@istanbuljs/nyc-config-typescript": "^1.0.2", @@ -8943,7 +8943,7 @@ }, "packages/time-series": { "name": "@redis/time-series", - "version": "1.0.5", + "version": "1.1.0", "license": "MIT", "devDependencies": { "@istanbuljs/nyc-config-typescript": "^1.0.2", diff --git a/package.json b/package.json index 586ef8b8dad..9aaec4d2ae9 100644 --- a/package.json +++ b/package.json @@ -24,11 +24,11 @@ }, "dependencies": { "@redis/bloom": "1.2.0", - "@redis/client": "1.5.17", + "@redis/client": "1.6.0", "@redis/graph": "1.1.1", - "@redis/json": "1.0.6", - "@redis/search": "1.1.6", - "@redis/time-series": "1.0.5" + "@redis/json": "1.0.7", + "@redis/search": "1.2.0", + "@redis/time-series": "1.1.0" }, "devDependencies": { "@tsconfig/node14": "^14.1.0", From 2fc79bdfb375602e2aaba15962f57c88d8afe46b Mon Sep 17 00:00:00 2001 From: Leibale Date: Mon, 29 Jul 2024 10:34:21 -0400 Subject: [PATCH 1463/1748] Release redis@4.7.0 --- package-lock.json | 4 ++-- package.json | 2 +- 2 files changed, 3 insertions(+), 3 deletions(-) diff --git a/package-lock.json b/package-lock.json index 9fc773717d7..18a7003947e 100644 --- a/package-lock.json +++ b/package-lock.json @@ -1,12 +1,12 @@ { "name": "redis", - "version": "4.6.15", + "version": "4.7.0", "lockfileVersion": 3, "requires": true, "packages": { "": { "name": "redis", - "version": "4.6.15", + "version": "4.7.0", "license": "MIT", "workspaces": [ "./packages/*" diff --git a/package.json b/package.json index 9aaec4d2ae9..e8ceef7173d 100644 --- a/package.json +++ b/package.json @@ -1,7 +1,7 @@ { "name": "redis", "description": "A modern, high performance Redis client", - "version": "4.6.15", + "version": "4.7.0", "license": "MIT", "main": "./dist/index.js", "types": "./dist/index.d.ts", From b2d35c528654046eaed5d5d32099de32384400ae Mon Sep 17 00:00:00 2001 From: Shaya Potter Date: Tue, 15 Oct 2024 17:46:52 +0300 Subject: [PATCH 1464/1748] V5 bringing RESP3, Sentinel and TypeMapping to node-redis RESP3 Support - Some commands responses in RESP3 aren't stable yet and therefore return an "untyped" ReplyUnion. Sentinel TypeMapping Correctly types Multi commands Note: some API changes to be further documented in v4-to-v5.md --- .github/release-drafter-base.yml | 50 + .github/workflows/documentation.yml | 2 - .github/workflows/tests.yml | 11 +- .gitignore | 1 + .npmignore | 12 - README.md | 346 +- benchmark/lib/index.js | 10 +- benchmark/lib/ping/ioredis-auto-pipeline.js | 20 + benchmark/lib/ping/local-resp2.js | 21 + .../lib/ping/local-resp3-buffer-proxy.js | 23 + benchmark/lib/ping/local-resp3-buffer.js | 24 + .../lib/ping/local-resp3-module-with-flags.js | 27 + benchmark/lib/ping/local-resp3-module.js | 27 + benchmark/lib/ping/local-resp3.js | 21 + benchmark/lib/ping/v3.js | 4 +- benchmark/lib/ping/v4.js | 2 +- benchmark/lib/runner.js | 6 +- benchmark/lib/set-get-delete-string/index.js | 2 +- benchmark/lib/set-get-delete-string/v3.js | 4 +- benchmark/package-lock.json | 87 +- benchmark/package.json | 9 +- docs/RESP.md | 46 + docs/client-configuration.md | 64 +- docs/clustering.md | 30 +- docs/command-options.md | 68 + docs/isolated-execution.md | 67 - docs/pool.md | 74 + docs/programmability.md | 76 + docs/pub-sub.md | 14 +- docs/scan-iterators.md | 30 + docs/sentinel.md | 100 + docs/todo.md | 6 + docs/transactions.md | 53 + docs/v4-to-v5.md | 240 + docs/v5.md | 38 + examples/README.md | 2 +- examples/blocking-list-pop.js | 2 +- examples/bloom-filter.js | 2 +- examples/check-connection-status.js | 2 +- examples/command-with-modifiers.js | 38 +- examples/connect-as-acl-user.js | 2 +- examples/count-min-sketch.js | 2 +- examples/cuckoo-filter.js | 2 +- examples/dump-and-restore.js | 22 +- examples/get-server-time.js | 2 +- examples/hyperloglog.js | 2 +- examples/lua-multi-incr.js | 2 +- examples/managing-json.js | 2 +- examples/package.json | 2 +- examples/search-hashes.js | 2 +- examples/search-json.js | 2 +- examples/search-knn.js | 2 +- examples/set-scan.js | 2 +- examples/sorted-set.js | 2 +- examples/stream-producer.js | 2 +- examples/time-series.js | 2 +- examples/topk.js | 2 +- .../transaction-with-arbitrary-commands.js | 2 +- index.ts | 77 - package-lock.json | 4613 +++++++---------- package.json | 55 +- packages/bloom/.npmignore | 6 - packages/bloom/.release-it.json | 1 + packages/bloom/README.md | 11 +- packages/bloom/lib/commands/bloom/ADD.spec.ts | 30 +- packages/bloom/lib/commands/bloom/ADD.ts | 14 +- .../bloom/lib/commands/bloom/CARD.spec.ts | 30 +- packages/bloom/lib/commands/bloom/CARD.ts | 15 +- .../bloom/lib/commands/bloom/EXISTS.spec.ts | 30 +- packages/bloom/lib/commands/bloom/EXISTS.ts | 16 +- .../bloom/lib/commands/bloom/INFO.spec.ts | 40 +- packages/bloom/lib/commands/bloom/INFO.ts | 62 +- .../bloom/lib/commands/bloom/INSERT.spec.ts | 116 +- packages/bloom/lib/commands/bloom/INSERT.ts | 58 +- .../lib/commands/bloom/LOADCHUNK.spec.ts | 53 +- .../bloom/lib/commands/bloom/LOADCHUNK.ts | 21 +- .../bloom/lib/commands/bloom/MADD.spec.ts | 30 +- packages/bloom/lib/commands/bloom/MADD.ts | 17 +- .../bloom/lib/commands/bloom/MEXISTS.spec.ts | 30 +- packages/bloom/lib/commands/bloom/MEXISTS.ts | 19 +- .../bloom/lib/commands/bloom/RESERVE.spec.ts | 82 +- packages/bloom/lib/commands/bloom/RESERVE.ts | 25 +- .../bloom/lib/commands/bloom/SCANDUMP.spec.ts | 36 +- packages/bloom/lib/commands/bloom/SCANDUMP.ts | 31 +- packages/bloom/lib/commands/bloom/index.ts | 93 +- .../commands/count-min-sketch/INCRBY.spec.ts | 71 +- .../lib/commands/count-min-sketch/INCRBY.ts | 37 +- .../commands/count-min-sketch/INFO.spec.ts | 43 +- .../lib/commands/count-min-sketch/INFO.ts | 52 +- .../count-min-sketch/INITBYDIM.spec.ts | 30 +- .../commands/count-min-sketch/INITBYDIM.ts | 13 +- .../count-min-sketch/INITBYPROB.spec.ts | 30 +- .../commands/count-min-sketch/INITBYPROB.ts | 13 +- .../commands/count-min-sketch/MERGE.spec.ts | 56 +- .../lib/commands/count-min-sketch/MERGE.ts | 52 +- .../commands/count-min-sketch/QUERY.spec.ts | 33 +- .../lib/commands/count-min-sketch/QUERY.ts | 26 +- .../lib/commands/count-min-sketch/index.ts | 39 +- .../bloom/lib/commands/cuckoo/ADD.spec.ts | 30 +- packages/bloom/lib/commands/cuckoo/ADD.ts | 14 +- .../bloom/lib/commands/cuckoo/ADDNX.spec.ts | 32 +- packages/bloom/lib/commands/cuckoo/ADDNX.ts | 14 +- .../bloom/lib/commands/cuckoo/COUNT.spec.ts | 30 +- packages/bloom/lib/commands/cuckoo/COUNT.ts | 13 +- .../bloom/lib/commands/cuckoo/DEL.spec.ts | 32 +- packages/bloom/lib/commands/cuckoo/DEL.ts | 14 +- .../bloom/lib/commands/cuckoo/EXISTS.spec.ts | 30 +- packages/bloom/lib/commands/cuckoo/EXISTS.ts | 16 +- .../bloom/lib/commands/cuckoo/INFO.spec.ts | 46 +- packages/bloom/lib/commands/cuckoo/INFO.ts | 71 +- .../bloom/lib/commands/cuckoo/INSERT.spec.ts | 36 +- packages/bloom/lib/commands/cuckoo/INSERT.ts | 44 +- .../lib/commands/cuckoo/INSERTNX.spec.ts | 36 +- .../bloom/lib/commands/cuckoo/INSERTNX.ts | 25 +- .../lib/commands/cuckoo/LOADCHUNK.spec.ts | 57 +- .../bloom/lib/commands/cuckoo/LOADCHUNK.ts | 19 +- .../bloom/lib/commands/cuckoo/RESERVE.spec.ts | 80 +- packages/bloom/lib/commands/cuckoo/RESERVE.ts | 39 +- .../lib/commands/cuckoo/SCANDUMP.spec.ts | 39 +- .../bloom/lib/commands/cuckoo/SCANDUMP.ts | 29 +- .../bloom/lib/commands/cuckoo/index.spec.ts | 48 - packages/bloom/lib/commands/cuckoo/index.ts | 95 +- packages/bloom/lib/commands/index.ts | 13 +- .../bloom/lib/commands/t-digest/ADD.spec.ts | 30 +- packages/bloom/lib/commands/t-digest/ADD.ts | 23 +- .../lib/commands/t-digest/BYRANK.spec.ts | 30 +- .../bloom/lib/commands/t-digest/BYRANK.ts | 33 +- .../lib/commands/t-digest/BYREVRANK.spec.ts | 30 +- .../bloom/lib/commands/t-digest/BYREVRANK.ts | 28 +- .../bloom/lib/commands/t-digest/CDF.spec.ts | 30 +- packages/bloom/lib/commands/t-digest/CDF.ts | 24 +- .../lib/commands/t-digest/CREATE.spec.ts | 46 +- .../bloom/lib/commands/t-digest/CREATE.ts | 30 +- .../bloom/lib/commands/t-digest/INFO.spec.ts | 43 +- packages/bloom/lib/commands/t-digest/INFO.ts | 75 +- .../bloom/lib/commands/t-digest/MAX.spec.ts | 30 +- packages/bloom/lib/commands/t-digest/MAX.ts | 25 +- .../bloom/lib/commands/t-digest/MERGE.spec.ts | 80 +- packages/bloom/lib/commands/t-digest/MERGE.ts | 42 +- .../bloom/lib/commands/t-digest/MIN.spec.ts | 30 +- packages/bloom/lib/commands/t-digest/MIN.ts | 25 +- .../lib/commands/t-digest/QUANTILE.spec.ts | 36 +- .../bloom/lib/commands/t-digest/QUANTILE.ts | 28 +- .../bloom/lib/commands/t-digest/RANK.spec.ts | 30 +- packages/bloom/lib/commands/t-digest/RANK.ts | 31 +- .../bloom/lib/commands/t-digest/RESET.spec.ts | 30 +- packages/bloom/lib/commands/t-digest/RESET.ts | 15 +- .../lib/commands/t-digest/REVRANK.spec.ts | 30 +- .../bloom/lib/commands/t-digest/REVRANK.ts | 28 +- .../commands/t-digest/TRIMMED_MEAN.spec.ts | 32 +- .../lib/commands/t-digest/TRIMMED_MEAN.ts | 30 +- .../bloom/lib/commands/t-digest/index.spec.ts | 55 - packages/bloom/lib/commands/t-digest/index.ts | 123 +- packages/bloom/lib/commands/top-k/ADD.spec.ts | 33 +- packages/bloom/lib/commands/top-k/ADD.ts | 22 +- .../bloom/lib/commands/top-k/COUNT.spec.ts | 32 +- packages/bloom/lib/commands/top-k/COUNT.ts | 26 +- .../bloom/lib/commands/top-k/INCRBY.spec.ts | 70 +- packages/bloom/lib/commands/top-k/INCRBY.ts | 41 +- .../bloom/lib/commands/top-k/INFO.spec.ts | 37 +- packages/bloom/lib/commands/top-k/INFO.ts | 52 +- .../bloom/lib/commands/top-k/LIST.spec.ts | 32 +- packages/bloom/lib/commands/top-k/LIST.ts | 15 +- .../lib/commands/top-k/LIST_WITHCOUNT.spec.ts | 47 +- .../lib/commands/top-k/LIST_WITHCOUNT.ts | 38 +- .../bloom/lib/commands/top-k/QUERY.spec.ts | 32 +- packages/bloom/lib/commands/top-k/QUERY.ts | 26 +- .../bloom/lib/commands/top-k/RESERVE.spec.ts | 52 +- packages/bloom/lib/commands/top-k/RESERVE.ts | 37 +- packages/bloom/lib/commands/top-k/index.ts | 51 +- packages/bloom/lib/test-utils.ts | 20 +- packages/bloom/package.json | 28 +- packages/client/.eslintrc.json | 15 - packages/client/.npmignore | 10 - packages/client/.release-it.json | 1 + packages/client/index.ts | 33 +- packages/client/lib/RESP/decoder.spec.ts | 426 ++ packages/client/lib/RESP/decoder.ts | 1178 +++++ packages/client/lib/RESP/encoder.spec.ts | 33 + packages/client/lib/RESP/encoder.ts | 28 + packages/client/lib/RESP/types.ts | 398 ++ packages/client/lib/RESP/verbatim-string.ts | 8 + .../lib/client/RESP2/composers/buffer.spec.ts | 14 - .../lib/client/RESP2/composers/buffer.ts | 18 - .../lib/client/RESP2/composers/interface.ts | 7 - .../lib/client/RESP2/composers/string.spec.ts | 14 - .../lib/client/RESP2/composers/string.ts | 22 - .../client/lib/client/RESP2/decoder.spec.ts | 195 - packages/client/lib/client/RESP2/decoder.ts | 257 - .../client/lib/client/RESP2/encoder.spec.ts | 33 - packages/client/lib/client/RESP2/encoder.ts | 28 - packages/client/lib/client/commands-queue.ts | 615 ++- packages/client/lib/client/commands.ts | 374 -- packages/client/lib/client/index.spec.ts | 1684 +++--- packages/client/lib/client/index.ts | 1782 ++++--- .../client/lib/client/legacy-mode.spec.ts | 111 + packages/client/lib/client/legacy-mode.ts | 177 + .../client/lib/client/linked-list.spec.ts | 138 + packages/client/lib/client/linked-list.ts | 195 + packages/client/lib/client/multi-command.ts | 363 +- packages/client/lib/client/pool.spec.ts | 11 + packages/client/lib/client/pool.ts | 489 ++ packages/client/lib/client/pub-sub.spec.ts | 268 +- packages/client/lib/client/pub-sub.ts | 711 +-- packages/client/lib/client/socket.spec.ts | 162 +- packages/client/lib/client/socket.ts | 537 +- packages/client/lib/cluster/cluster-slots.ts | 1021 ++-- packages/client/lib/cluster/commands.ts | 670 --- packages/client/lib/cluster/index.spec.ts | 661 ++- packages/client/lib/cluster/index.ts | 1053 ++-- packages/client/lib/cluster/multi-command.ts | 355 +- packages/client/lib/command-options.ts | 14 - packages/client/lib/commander.ts | 237 +- packages/client/lib/commands/ACL_CAT.spec.ts | 42 +- packages/client/lib/commands/ACL_CAT.ts | 17 +- .../client/lib/commands/ACL_DELUSER.spec.ts | 44 +- packages/client/lib/commands/ACL_DELUSER.ts | 19 +- .../client/lib/commands/ACL_DRYRUN.spec.ts | 30 +- packages/client/lib/commands/ACL_DRYRUN.ts | 26 +- .../client/lib/commands/ACL_GENPASS.spec.ts | 41 +- packages/client/lib/commands/ACL_GENPASS.ts | 14 +- .../client/lib/commands/ACL_GETUSER.spec.ts | 50 +- packages/client/lib/commands/ACL_GETUSER.ts | 79 +- packages/client/lib/commands/ACL_LIST.spec.ts | 28 +- packages/client/lib/commands/ACL_LIST.ts | 13 +- packages/client/lib/commands/ACL_LOAD.spec.ts | 20 +- packages/client/lib/commands/ACL_LOAD.ts | 13 +- packages/client/lib/commands/ACL_LOG.spec.ts | 89 +- packages/client/lib/commands/ACL_LOG.ts | 90 +- .../client/lib/commands/ACL_LOG_RESET.spec.ts | 27 +- packages/client/lib/commands/ACL_LOG_RESET.ts | 14 +- packages/client/lib/commands/ACL_SAVE.spec.ts | 20 +- packages/client/lib/commands/ACL_SAVE.ts | 13 +- .../client/lib/commands/ACL_SETUSER.spec.ts | 32 +- packages/client/lib/commands/ACL_SETUSER.ts | 20 +- .../client/lib/commands/ACL_USERS.spec.ts | 18 +- packages/client/lib/commands/ACL_USERS.ts | 13 +- .../client/lib/commands/ACL_WHOAMI.spec.ts | 18 +- packages/client/lib/commands/ACL_WHOAMI.ts | 13 +- packages/client/lib/commands/APPEND.spec.ts | 27 +- packages/client/lib/commands/APPEND.ts | 18 +- packages/client/lib/commands/ASKING.spec.ts | 16 +- packages/client/lib/commands/ASKING.ts | 13 +- packages/client/lib/commands/AUTH.spec.ts | 40 +- packages/client/lib/commands/AUTH.ts | 25 +- .../client/lib/commands/BGREWRITEAOF.spec.ts | 24 +- packages/client/lib/commands/BGREWRITEAOF.ts | 13 +- packages/client/lib/commands/BGSAVE.spec.ts | 45 +- packages/client/lib/commands/BGSAVE.ts | 21 +- packages/client/lib/commands/BITCOUNT.spec.ts | 75 +- packages/client/lib/commands/BITCOUNT.ts | 44 +- packages/client/lib/commands/BITFIELD.spec.ts | 91 +- packages/client/lib/commands/BITFIELD.ts | 115 +- .../client/lib/commands/BITFIELD_RO.spec.ts | 47 +- packages/client/lib/commands/BITFIELD_RO.ts | 31 +- packages/client/lib/commands/BITOP.spec.ts | 52 +- packages/client/lib/commands/BITOP.ts | 27 +- packages/client/lib/commands/BITPOS.spec.ts | 76 +- packages/client/lib/commands/BITPOS.ts | 31 +- packages/client/lib/commands/BLMOVE.spec.ts | 66 +- packages/client/lib/commands/BLMOVE.ts | 37 +- packages/client/lib/commands/BLMPOP.spec.ts | 67 +- packages/client/lib/commands/BLMPOP.ts | 29 +- packages/client/lib/commands/BLPOP.spec.ts | 113 +- packages/client/lib/commands/BLPOP.ts | 40 +- packages/client/lib/commands/BRPOP.spec.ts | 113 +- packages/client/lib/commands/BRPOP.ts | 26 +- .../client/lib/commands/BRPOPLPUSH.spec.ts | 77 +- packages/client/lib/commands/BRPOPLPUSH.ts | 21 +- packages/client/lib/commands/BZMPOP.spec.ts | 73 +- packages/client/lib/commands/BZMPOP.ts | 29 +- packages/client/lib/commands/BZPOPMAX.spec.ts | 100 +- packages/client/lib/commands/BZPOPMAX.ts | 64 +- packages/client/lib/commands/BZPOPMIN.spec.ts | 100 +- packages/client/lib/commands/BZPOPMIN.ts | 27 +- .../lib/commands/CLIENT_CACHING.spec.ts | 30 +- .../client/lib/commands/CLIENT_CACHING.ts | 19 +- .../lib/commands/CLIENT_GETNAME.spec.ts | 24 +- .../client/lib/commands/CLIENT_GETNAME.ts | 18 +- .../lib/commands/CLIENT_GETREDIR.spec.ts | 16 +- .../client/lib/commands/CLIENT_GETREDIR.ts | 15 +- .../client/lib/commands/CLIENT_ID.spec.ts | 28 +- packages/client/lib/commands/CLIENT_ID.ts | 13 +- .../client/lib/commands/CLIENT_INFO.spec.ts | 84 +- packages/client/lib/commands/CLIENT_INFO.ts | 154 +- .../client/lib/commands/CLIENT_KILL.spec.ts | 214 +- packages/client/lib/commands/CLIENT_KILL.ts | 163 +- .../client/lib/commands/CLIENT_LIST.spec.ts | 129 +- packages/client/lib/commands/CLIENT_LIST.ts | 57 +- .../lib/commands/CLIENT_NO-EVICT.spec.ts | 44 +- .../client/lib/commands/CLIENT_NO-EVICT.ts | 19 +- .../lib/commands/CLIENT_NO-TOUCH.spec.ts | 42 +- .../client/lib/commands/CLIENT_NO-TOUCH.ts | 18 +- .../client/lib/commands/CLIENT_PAUSE.spec.ts | 42 +- packages/client/lib/commands/CLIENT_PAUSE.ts | 24 +- .../lib/commands/CLIENT_SETNAME.spec.ts | 25 +- .../client/lib/commands/CLIENT_SETNAME.ts | 13 +- .../lib/commands/CLIENT_TRACKING.spec.ts | 172 +- .../client/lib/commands/CLIENT_TRACKING.ts | 98 +- .../lib/commands/CLIENT_TRACKINGINFO.spec.ts | 38 +- .../lib/commands/CLIENT_TRACKINGINFO.ts | 47 +- .../lib/commands/CLIENT_UNPAUSE.spec.ts | 30 +- .../client/lib/commands/CLIENT_UNPAUSE.ts | 13 +- .../lib/commands/CLUSTER_ADDSLOTS.spec.ts | 30 +- .../client/lib/commands/CLUSTER_ADDSLOTS.ts | 21 +- .../commands/CLUSTER_ADDSLOTSRANGE.spec.ts | 51 +- .../lib/commands/CLUSTER_ADDSLOTSRANGE.ts | 19 +- .../lib/commands/CLUSTER_BUMPEPOCH.spec.ts | 30 +- .../client/lib/commands/CLUSTER_BUMPEPOCH.ts | 13 +- .../CLUSTER_COUNT-FAILURE-REPORTS.spec.ts | 33 +- .../commands/CLUSTER_COUNT-FAILURE-REPORTS.ts | 13 +- .../commands/CLUSTER_COUNTKEYSINSLOT.spec.ts | 30 +- .../lib/commands/CLUSTER_COUNTKEYSINSLOT.ts | 13 +- .../lib/commands/CLUSTER_DELSLOTS.spec.ts | 30 +- .../client/lib/commands/CLUSTER_DELSLOTS.ts | 21 +- .../commands/CLUSTER_DELSLOTSRANGE.spec.ts | 48 +- .../lib/commands/CLUSTER_DELSLOTSRANGE.ts | 19 +- .../lib/commands/CLUSTER_FAILOVER.spec.ts | 34 +- .../client/lib/commands/CLUSTER_FAILOVER.ts | 29 +- .../lib/commands/CLUSTER_FLUSHSLOTS.spec.ts | 16 +- .../client/lib/commands/CLUSTER_FLUSHSLOTS.ts | 13 +- .../lib/commands/CLUSTER_FORGET.spec.ts | 16 +- .../client/lib/commands/CLUSTER_FORGET.ts | 13 +- .../commands/CLUSTER_GETKEYSINSLOT.spec.ts | 36 +- .../lib/commands/CLUSTER_GETKEYSINSLOT.ts | 13 +- .../client/lib/commands/CLUSTER_INFO.spec.ts | 65 +- packages/client/lib/commands/CLUSTER_INFO.ts | 55 +- .../lib/commands/CLUSTER_KEYSLOT.spec.ts | 30 +- .../client/lib/commands/CLUSTER_KEYSLOT.ts | 13 +- .../client/lib/commands/CLUSTER_LINKS.spec.ts | 44 +- packages/client/lib/commands/CLUSTER_LINKS.ts | 66 +- .../client/lib/commands/CLUSTER_MEET.spec.ts | 16 +- packages/client/lib/commands/CLUSTER_MEET.ts | 13 +- .../client/lib/commands/CLUSTER_MYID.spec.ts | 32 +- packages/client/lib/commands/CLUSTER_MYID.ts | 13 +- .../lib/commands/CLUSTER_MYSHARDID.spec.ts | 30 +- .../client/lib/commands/CLUSTER_MYSHARDID.ts | 12 +- .../client/lib/commands/CLUSTER_NODES.spec.ts | 157 +- packages/client/lib/commands/CLUSTER_NODES.ts | 113 +- .../lib/commands/CLUSTER_REPLICAS.spec.ts | 26 +- .../client/lib/commands/CLUSTER_REPLICAS.ts | 13 +- .../lib/commands/CLUSTER_REPLICATE.spec.ts | 16 +- .../client/lib/commands/CLUSTER_REPLICATE.ts | 13 +- .../client/lib/commands/CLUSTER_RESET.spec.ts | 32 +- packages/client/lib/commands/CLUSTER_RESET.ts | 21 +- .../lib/commands/CLUSTER_SAVECONFIG.spec.ts | 30 +- .../client/lib/commands/CLUSTER_SAVECONFIG.ts | 14 +- .../commands/CLUSTER_SET-CONFIG-EPOCH.spec.ts | 16 +- .../lib/commands/CLUSTER_SET-CONFIG-EPOCH.ts | 13 +- .../lib/commands/CLUSTER_SETSLOT.spec.ts | 30 +- .../client/lib/commands/CLUSTER_SETSLOT.ts | 35 +- .../client/lib/commands/CLUSTER_SLOTS.spec.ts | 98 +- packages/client/lib/commands/CLUSTER_SLOTS.ts | 73 +- packages/client/lib/commands/COMMAND.spec.ts | 30 +- packages/client/lib/commands/COMMAND.ts | 17 +- .../client/lib/commands/COMMAND_COUNT.spec.ts | 28 +- packages/client/lib/commands/COMMAND_COUNT.ts | 15 +- .../lib/commands/COMMAND_GETKEYS.spec.ts | 28 +- .../client/lib/commands/COMMAND_GETKEYS.ts | 15 +- .../commands/COMMAND_GETKEYSANDFLAGS.spec.ts | 42 +- .../lib/commands/COMMAND_GETKEYSANDFLAGS.ts | 37 +- .../client/lib/commands/COMMAND_INFO.spec.ts | 90 +- packages/client/lib/commands/COMMAND_INFO.ts | 18 +- .../client/lib/commands/COMMAND_LIST.spec.ts | 98 +- packages/client/lib/commands/COMMAND_LIST.ts | 50 +- .../client/lib/commands/CONFIG_GET.spec.ts | 34 +- packages/client/lib/commands/CONFIG_GET.ts | 17 +- .../lib/commands/CONFIG_RESETSTAT.spec.ts | 16 +- .../client/lib/commands/CONFIG_RESETSTAT.ts | 13 +- .../lib/commands/CONFIG_REWRITE.spec.ts | 16 +- .../client/lib/commands/CONFIG_REWRITE.ts | 13 +- .../client/lib/commands/CONFIG_SET.spec.ts | 46 +- packages/client/lib/commands/CONFIG_SET.ts | 35 +- packages/client/lib/commands/COPY.spec.ts | 103 +- packages/client/lib/commands/COPY.ts | 33 +- packages/client/lib/commands/DBSIZE.spec.ts | 28 +- packages/client/lib/commands/DBSIZE.ts | 13 +- packages/client/lib/commands/DECR.spec.ts | 31 +- packages/client/lib/commands/DECR.ts | 14 +- packages/client/lib/commands/DECRBY.spec.ts | 31 +- packages/client/lib/commands/DECRBY.ts | 17 +- packages/client/lib/commands/DEL.spec.ts | 45 +- packages/client/lib/commands/DEL.ts | 21 +- packages/client/lib/commands/DISCARD.spec.ts | 16 +- packages/client/lib/commands/DISCARD.ts | 11 +- packages/client/lib/commands/DUMP.spec.ts | 17 +- packages/client/lib/commands/DUMP.ts | 15 +- packages/client/lib/commands/ECHO.spec.ts | 28 +- packages/client/lib/commands/ECHO.ts | 15 +- packages/client/lib/commands/EVAL.spec.ts | 44 +- packages/client/lib/commands/EVAL.ts | 34 +- packages/client/lib/commands/EVALSHA.spec.ts | 22 +- packages/client/lib/commands/EVALSHA.ts | 14 +- .../client/lib/commands/EVALSHA_RO.spec.ts | 24 +- packages/client/lib/commands/EVALSHA_RO.ts | 16 +- packages/client/lib/commands/EVAL_RO.spec.ts | 46 +- packages/client/lib/commands/EVAL_RO.ts | 16 +- packages/client/lib/commands/EXISTS.spec.ts | 45 +- packages/client/lib/commands/EXISTS.ts | 25 +- packages/client/lib/commands/EXPIRE.spec.ts | 45 +- packages/client/lib/commands/EXPIRE.ts | 21 +- packages/client/lib/commands/EXPIREAT.spec.ts | 61 +- packages/client/lib/commands/EXPIREAT.ts | 27 +- .../client/lib/commands/EXPIRETIME.spec.ts | 33 +- packages/client/lib/commands/EXPIRETIME.ts | 15 +- packages/client/lib/commands/FAILOVER.spec.ts | 126 +- packages/client/lib/commands/FAILOVER.ts | 37 +- packages/client/lib/commands/FCALL.spec.ts | 45 +- packages/client/lib/commands/FCALL.ts | 14 +- packages/client/lib/commands/FCALL_RO.spec.ts | 45 +- packages/client/lib/commands/FCALL_RO.ts | 16 +- packages/client/lib/commands/FLUSHALL.spec.ts | 54 +- packages/client/lib/commands/FLUSHALL.ts | 27 +- packages/client/lib/commands/FLUSHDB.spec.ts | 56 +- packages/client/lib/commands/FLUSHDB.ts | 18 +- .../lib/commands/FUNCTION_DELETE.spec.ts | 34 +- .../client/lib/commands/FUNCTION_DELETE.ts | 13 +- .../client/lib/commands/FUNCTION_DUMP.spec.ts | 30 +- packages/client/lib/commands/FUNCTION_DUMP.ts | 13 +- .../lib/commands/FUNCTION_FLUSH.spec.ts | 44 +- .../client/lib/commands/FUNCTION_FLUSH.ts | 18 +- .../client/lib/commands/FUNCTION_KILL.spec.ts | 18 +- packages/client/lib/commands/FUNCTION_KILL.ts | 13 +- .../client/lib/commands/FUNCTION_LIST.spec.ts | 68 +- packages/client/lib/commands/FUNCTION_LIST.ts | 57 +- .../commands/FUNCTION_LIST_WITHCODE.spec.ts | 72 +- .../lib/commands/FUNCTION_LIST_WITHCODE.ts | 60 +- .../client/lib/commands/FUNCTION_LOAD.spec.ts | 101 +- packages/client/lib/commands/FUNCTION_LOAD.ts | 24 +- .../lib/commands/FUNCTION_RESTORE.spec.ts | 61 +- .../client/lib/commands/FUNCTION_RESTORE.ts | 24 +- .../lib/commands/FUNCTION_STATS.spec.ts | 38 +- .../client/lib/commands/FUNCTION_STATS.ts | 112 +- packages/client/lib/commands/GEOADD.spec.ts | 173 +- packages/client/lib/commands/GEOADD.ts | 84 +- packages/client/lib/commands/GEODIST.spec.ts | 93 +- packages/client/lib/commands/GEODIST.ts | 31 +- packages/client/lib/commands/GEOHASH.spec.ts | 52 +- packages/client/lib/commands/GEOHASH.ts | 29 +- packages/client/lib/commands/GEOPOS.spec.ts | 106 +- packages/client/lib/commands/GEOPOS.ts | 45 +- .../client/lib/commands/GEORADIUS.spec.ts | 53 +- packages/client/lib/commands/GEORADIUS.ts | 47 +- .../lib/commands/GEORADIUSBYMEMBER.spec.ts | 38 +- .../client/lib/commands/GEORADIUSBYMEMBER.ts | 45 +- .../commands/GEORADIUSBYMEMBERSTORE.spec.ts | 53 - .../lib/commands/GEORADIUSBYMEMBERSTORE.ts | 25 - .../lib/commands/GEORADIUSBYMEMBER_RO.spec.ts | 38 +- .../lib/commands/GEORADIUSBYMEMBER_RO.ts | 34 +- .../GEORADIUSBYMEMBER_RO_WITH.spec.ts | 61 +- .../lib/commands/GEORADIUSBYMEMBER_RO_WITH.ts | 39 +- .../commands/GEORADIUSBYMEMBER_STORE.spec.ts | 39 + .../lib/commands/GEORADIUSBYMEMBER_STORE.ts | 31 + .../commands/GEORADIUSBYMEMBER_WITH.spec.ts | 61 +- .../lib/commands/GEORADIUSBYMEMBER_WITH.ts | 54 +- .../lib/commands/GEORADIUSSTORE.spec.ts | 53 - .../client/lib/commands/GEORADIUSSTORE.ts | 25 - .../client/lib/commands/GEORADIUS_RO.spec.ts | 53 +- packages/client/lib/commands/GEORADIUS_RO.ts | 34 +- .../lib/commands/GEORADIUS_RO_WITH.spec.ts | 74 +- .../client/lib/commands/GEORADIUS_RO_WITH.ts | 39 +- .../lib/commands/GEORADIUS_STORE.spec.ts | 48 + .../client/lib/commands/GEORADIUS_STORE.ts | 31 + .../lib/commands/GEORADIUS_WITH.spec.ts | 74 +- .../client/lib/commands/GEORADIUS_WITH.ts | 57 +- .../client/lib/commands/GEOSEARCH.spec.ts | 100 +- packages/client/lib/commands/GEOSEARCH.ts | 97 +- .../lib/commands/GEOSEARCHSTORE.spec.ts | 111 +- .../client/lib/commands/GEOSEARCHSTORE.ts | 43 +- .../lib/commands/GEOSEARCH_WITH.spec.ts | 75 +- .../client/lib/commands/GEOSEARCH_WITH.ts | 76 +- packages/client/lib/commands/GET.spec.ts | 45 +- packages/client/lib/commands/GET.ts | 17 +- packages/client/lib/commands/GETBIT.spec.ts | 38 +- packages/client/lib/commands/GETBIT.ts | 20 +- packages/client/lib/commands/GETDEL.spec.ts | 40 +- packages/client/lib/commands/GETDEL.ts | 15 +- packages/client/lib/commands/GETEX.spec.ts | 186 +- packages/client/lib/commands/GETEX.ts | 91 +- packages/client/lib/commands/GETRANGE.spec.ts | 38 +- packages/client/lib/commands/GETRANGE.ts | 21 +- packages/client/lib/commands/GETSET.spec.ts | 38 +- packages/client/lib/commands/GETSET.ts | 18 +- packages/client/lib/commands/HDEL.spec.ts | 45 +- packages/client/lib/commands/HDEL.ts | 21 +- packages/client/lib/commands/HELLO.spec.ts | 125 +- packages/client/lib/commands/HELLO.ts | 116 +- packages/client/lib/commands/HEXISTS.spec.ts | 31 +- packages/client/lib/commands/HEXISTS.ts | 18 +- packages/client/lib/commands/HEXPIRE.spec.ts | 8 +- packages/client/lib/commands/HEXPIRE.ts | 45 +- .../client/lib/commands/HEXPIREAT.spec.ts | 10 +- packages/client/lib/commands/HEXPIREAT.ts | 52 +- .../client/lib/commands/HEXPIRETIME.spec.ts | 6 +- packages/client/lib/commands/HEXPIRETIME.ts | 23 +- packages/client/lib/commands/HGET.spec.ts | 31 +- packages/client/lib/commands/HGET.ts | 20 +- packages/client/lib/commands/HGETALL.spec.ts | 63 +- packages/client/lib/commands/HGETALL.ts | 24 +- packages/client/lib/commands/HINCRBY.spec.ts | 31 +- packages/client/lib/commands/HINCRBY.ts | 27 +- .../client/lib/commands/HINCRBYFLOAT.spec.ts | 31 +- packages/client/lib/commands/HINCRBYFLOAT.ts | 27 +- packages/client/lib/commands/HKEYS.spec.ts | 31 +- packages/client/lib/commands/HKEYS.ts | 15 +- packages/client/lib/commands/HLEN.spec.ts | 31 +- packages/client/lib/commands/HLEN.ts | 15 +- packages/client/lib/commands/HMGET.spec.ts | 45 +- packages/client/lib/commands/HMGET.ts | 29 +- packages/client/lib/commands/HPERSIST.spec.ts | 6 +- packages/client/lib/commands/HPERSIST.ts | 19 +- packages/client/lib/commands/HPEXPIRE.spec.ts | 8 +- packages/client/lib/commands/HPEXPIRE.ts | 44 +- .../client/lib/commands/HPEXPIREAT.spec.ts | 10 +- packages/client/lib/commands/HPEXPIREAT.ts | 39 +- .../client/lib/commands/HPEXPIRETIME.spec.ts | 6 +- packages/client/lib/commands/HPEXPIRETIME.ts | 20 +- packages/client/lib/commands/HPTTL.spec.ts | 6 +- packages/client/lib/commands/HPTTL.ts | 20 +- .../client/lib/commands/HRANDFIELD.spec.ts | 33 +- packages/client/lib/commands/HRANDFIELD.ts | 17 +- .../lib/commands/HRANDFIELD_COUNT.spec.ts | 33 +- .../client/lib/commands/HRANDFIELD_COUNT.ts | 24 +- .../HRANDFIELD_COUNT_WITHVALUES.spec.ts | 21 - .../commands/HRANDFIELD_COUNT_WITHVALUES.ts | 49 +- packages/client/lib/commands/HSCAN.spec.ts | 148 +- packages/client/lib/commands/HSCAN.ts | 64 +- .../lib/commands/HSCAN_NOVALUES.spec.ts | 135 +- .../client/lib/commands/HSCAN_NOVALUES.ts | 39 +- packages/client/lib/commands/HSET.spec.ts | 118 +- packages/client/lib/commands/HSET.ts | 98 +- packages/client/lib/commands/HSETNX.spec.ts | 31 +- packages/client/lib/commands/HSETNX.ts | 23 +- packages/client/lib/commands/HSTRLEN.spec.ts | 31 +- packages/client/lib/commands/HSTRLEN.ts | 18 +- packages/client/lib/commands/HTTL.spec.ts | 6 +- packages/client/lib/commands/HTTL.ts | 20 +- packages/client/lib/commands/HVALS.spec.ts | 31 +- packages/client/lib/commands/HVALS.ts | 15 +- packages/client/lib/commands/INCR.spec.ts | 31 +- packages/client/lib/commands/INCR.ts | 14 +- packages/client/lib/commands/INCRBY.spec.ts | 33 +- packages/client/lib/commands/INCRBY.ts | 17 +- .../client/lib/commands/INCRBYFLOAT.spec.ts | 31 +- packages/client/lib/commands/INCRBYFLOAT.ts | 17 +- packages/client/lib/commands/INFO.spec.ts | 38 +- packages/client/lib/commands/INFO.ts | 17 +- packages/client/lib/commands/KEYS.spec.ts | 14 +- packages/client/lib/commands/KEYS.ts | 13 +- packages/client/lib/commands/LASTSAVE.spec.ts | 25 +- packages/client/lib/commands/LASTSAVE.ts | 15 +- .../lib/commands/LATENCY_DOCTOR.spec.ts | 30 +- .../client/lib/commands/LATENCY_DOCTOR.ts | 13 +- .../client/lib/commands/LATENCY_GRAPH.spec.ts | 42 +- packages/client/lib/commands/LATENCY_GRAPH.ts | 50 +- .../lib/commands/LATENCY_HISTORY.spec.ts | 44 +- .../client/lib/commands/LATENCY_HISTORY.ts | 52 +- .../lib/commands/LATENCY_LATEST.spec.ts | 46 +- .../client/lib/commands/LATENCY_LATEST.ts | 22 +- packages/client/lib/commands/LCS.spec.ts | 40 +- packages/client/lib/commands/LCS.ts | 31 +- packages/client/lib/commands/LCS_IDX.spec.ts | 63 +- packages/client/lib/commands/LCS_IDX.ts | 82 +- .../lib/commands/LCS_IDX_WITHMATCHLEN.spec.ts | 64 +- .../lib/commands/LCS_IDX_WITHMATCHLEN.ts | 75 +- packages/client/lib/commands/LCS_LEN.spec.ts | 40 +- packages/client/lib/commands/LCS_LEN.ts | 25 +- packages/client/lib/commands/LINDEX.spec.ts | 50 +- packages/client/lib/commands/LINDEX.ts | 20 +- packages/client/lib/commands/LINSERT.spec.ts | 38 +- packages/client/lib/commands/LINSERT.ts | 33 +- packages/client/lib/commands/LLEN.spec.ts | 38 +- packages/client/lib/commands/LLEN.ts | 17 +- packages/client/lib/commands/LMOVE.spec.ts | 40 +- packages/client/lib/commands/LMOVE.ts | 31 +- packages/client/lib/commands/LMPOP.spec.ts | 66 +- packages/client/lib/commands/LMPOP.ts | 51 +- packages/client/lib/commands/LOLWUT.spec.ts | 54 +- packages/client/lib/commands/LOLWUT.ts | 25 +- packages/client/lib/commands/LPOP.spec.ts | 38 +- packages/client/lib/commands/LPOP.ts | 14 +- .../client/lib/commands/LPOP_COUNT.spec.ts | 40 +- packages/client/lib/commands/LPOP_COUNT.ts | 18 +- packages/client/lib/commands/LPOS.spec.ts | 92 +- packages/client/lib/commands/LPOS.ts | 35 +- .../client/lib/commands/LPOS_COUNT.spec.ts | 92 +- packages/client/lib/commands/LPOS_COUNT.ts | 31 +- packages/client/lib/commands/LPUSH.spec.ts | 52 +- packages/client/lib/commands/LPUSH.ts | 20 +- packages/client/lib/commands/LPUSHX.spec.ts | 52 +- packages/client/lib/commands/LPUSHX.ts | 21 +- packages/client/lib/commands/LRANGE.spec.ts | 39 +- packages/client/lib/commands/LRANGE.ts | 29 +- packages/client/lib/commands/LREM.spec.ts | 39 +- packages/client/lib/commands/LREM.ts | 29 +- packages/client/lib/commands/LSET.spec.ts | 41 +- packages/client/lib/commands/LSET.ts | 29 +- packages/client/lib/commands/LTRIM.spec.ts | 38 +- packages/client/lib/commands/LTRIM.ts | 26 +- .../client/lib/commands/MEMORY_DOCTOR.spec.ts | 28 +- packages/client/lib/commands/MEMORY_DOCTOR.ts | 13 +- .../lib/commands/MEMORY_MALLOC-STATS.spec.ts | 28 +- .../lib/commands/MEMORY_MALLOC-STATS.ts | 12 +- .../client/lib/commands/MEMORY_PURGE.spec.ts | 28 +- packages/client/lib/commands/MEMORY_PURGE.ts | 12 +- .../client/lib/commands/MEMORY_STATS.spec.ts | 146 +- packages/client/lib/commands/MEMORY_STATS.ts | 151 +- .../client/lib/commands/MEMORY_USAGE.spec.ts | 46 +- packages/client/lib/commands/MEMORY_USAGE.ts | 23 +- packages/client/lib/commands/MGET.spec.ts | 38 +- packages/client/lib/commands/MGET.ts | 19 +- packages/client/lib/commands/MIGRATE.spec.ts | 134 +- packages/client/lib/commands/MIGRATE.ts | 86 +- .../client/lib/commands/MODULE_LIST.spec.ts | 16 +- packages/client/lib/commands/MODULE_LIST.ts | 29 +- .../client/lib/commands/MODULE_LOAD.spec.ts | 30 +- packages/client/lib/commands/MODULE_LOAD.ts | 19 +- .../client/lib/commands/MODULE_UNLOAD.spec.ts | 16 +- packages/client/lib/commands/MODULE_UNLOAD.ts | 13 +- packages/client/lib/commands/MOVE.spec.ts | 28 +- packages/client/lib/commands/MOVE.ts | 12 +- packages/client/lib/commands/MSET.spec.ts | 64 +- packages/client/lib/commands/MSET.ts | 35 +- packages/client/lib/commands/MSETNX.spec.ts | 64 +- packages/client/lib/commands/MSETNX.ts | 29 +- .../lib/commands/OBJECT_ENCODING.spec.ts | 31 +- .../client/lib/commands/OBJECT_ENCODING.ts | 17 +- .../client/lib/commands/OBJECT_FREQ.spec.ts | 31 +- packages/client/lib/commands/OBJECT_FREQ.ts | 17 +- .../lib/commands/OBJECT_IDLETIME.spec.ts | 31 +- .../client/lib/commands/OBJECT_IDLETIME.ts | 17 +- .../lib/commands/OBJECT_REFCOUNT.spec.ts | 31 +- .../client/lib/commands/OBJECT_REFCOUNT.ts | 17 +- packages/client/lib/commands/PERSIST.spec.ts | 31 +- packages/client/lib/commands/PERSIST.ts | 14 +- packages/client/lib/commands/PEXPIRE.spec.ts | 45 +- packages/client/lib/commands/PEXPIRE.ts | 25 +- .../client/lib/commands/PEXPIREAT.spec.ts | 59 +- packages/client/lib/commands/PEXPIREAT.ts | 29 +- .../client/lib/commands/PEXPIRETIME.spec.ts | 33 +- packages/client/lib/commands/PEXPIRETIME.ts | 15 +- packages/client/lib/commands/PFADD.spec.ts | 45 +- packages/client/lib/commands/PFADD.ts | 23 +- packages/client/lib/commands/PFCOUNT.spec.ts | 45 +- packages/client/lib/commands/PFCOUNT.ts | 21 +- packages/client/lib/commands/PFMERGE.spec.ts | 45 +- packages/client/lib/commands/PFMERGE.ts | 22 +- packages/client/lib/commands/PING.spec.ts | 54 +- packages/client/lib/commands/PING.ts | 17 +- packages/client/lib/commands/PSETEX.spec.ts | 39 +- packages/client/lib/commands/PSETEX.ts | 30 +- packages/client/lib/commands/PTTL.spec.ts | 31 +- packages/client/lib/commands/PTTL.ts | 17 +- packages/client/lib/commands/PUBLISH.spec.ts | 28 +- packages/client/lib/commands/PUBLISH.ts | 19 +- .../lib/commands/PUBSUB_CHANNELS.spec.ts | 42 +- .../client/lib/commands/PUBSUB_CHANNELS.ts | 16 +- .../client/lib/commands/PUBSUB_NUMPAT.spec.ts | 28 +- packages/client/lib/commands/PUBSUB_NUMPAT.ts | 13 +- .../client/lib/commands/PUBSUB_NUMSUB.spec.ts | 54 +- packages/client/lib/commands/PUBSUB_NUMSUB.ts | 33 +- .../lib/commands/PUBSUB_SHARDCHANNELS.spec.ts | 46 +- .../lib/commands/PUBSUB_SHARDCHANNELS.ts | 23 +- .../lib/commands/PUBSUB_SHARDNUMSUB.spec.ts | 76 +- .../client/lib/commands/PUBSUB_SHARDNUMSUB.ts | 32 +- .../client/lib/commands/RANDOMKEY.spec.ts | 31 +- packages/client/lib/commands/RANDOMKEY.ts | 15 +- packages/client/lib/commands/READONLY.spec.ts | 16 +- packages/client/lib/commands/READONLY.ts | 13 +- .../client/lib/commands/READWRITE.spec.ts | 16 +- packages/client/lib/commands/READWRITE.ts | 13 +- packages/client/lib/commands/RENAME.spec.ts | 35 +- packages/client/lib/commands/RENAME.ts | 18 +- packages/client/lib/commands/RENAMENX.spec.ts | 33 +- packages/client/lib/commands/RENAMENX.ts | 18 +- .../client/lib/commands/REPLICAOF.spec.ts | 16 +- packages/client/lib/commands/REPLICAOF.ts | 13 +- .../lib/commands/RESTORE-ASKING.spec.ts | 16 +- .../client/lib/commands/RESTORE-ASKING.ts | 13 +- packages/client/lib/commands/RESTORE.spec.ts | 132 +- packages/client/lib/commands/RESTORE.ts | 39 +- packages/client/lib/commands/ROLE.spec.ts | 120 +- packages/client/lib/commands/ROLE.ts | 125 +- packages/client/lib/commands/RPOP.spec.ts | 38 +- packages/client/lib/commands/RPOP.ts | 14 +- .../client/lib/commands/RPOPLPUSH.spec.ts | 38 +- packages/client/lib/commands/RPOPLPUSH.ts | 20 +- .../client/lib/commands/RPOP_COUNT.spec.ts | 40 +- packages/client/lib/commands/RPOP_COUNT.ts | 17 +- packages/client/lib/commands/RPUSH.spec.ts | 52 +- packages/client/lib/commands/RPUSH.ts | 24 +- packages/client/lib/commands/RPUSHX.spec.ts | 52 +- packages/client/lib/commands/RPUSHX.ts | 24 +- packages/client/lib/commands/SADD.spec.ts | 45 +- packages/client/lib/commands/SADD.ts | 21 +- packages/client/lib/commands/SAVE.spec.ts | 16 +- packages/client/lib/commands/SAVE.ts | 13 +- packages/client/lib/commands/SCAN.spec.ts | 126 +- packages/client/lib/commands/SCAN.ts | 60 +- packages/client/lib/commands/SCARD.spec.ts | 31 +- packages/client/lib/commands/SCARD.ts | 13 +- .../client/lib/commands/SCRIPT_DEBUG.spec.ts | 28 +- packages/client/lib/commands/SCRIPT_DEBUG.ts | 13 +- .../client/lib/commands/SCRIPT_EXISTS.spec.ts | 42 +- packages/client/lib/commands/SCRIPT_EXISTS.ts | 17 +- .../client/lib/commands/SCRIPT_FLUSH.spec.ts | 42 +- packages/client/lib/commands/SCRIPT_FLUSH.ts | 15 +- .../client/lib/commands/SCRIPT_KILL.spec.ts | 16 +- packages/client/lib/commands/SCRIPT_KILL.ts | 13 +- .../client/lib/commands/SCRIPT_LOAD.spec.ts | 34 +- packages/client/lib/commands/SCRIPT_LOAD.ts | 13 +- packages/client/lib/commands/SDIFF.spec.ts | 45 +- packages/client/lib/commands/SDIFF.ts | 25 +- .../client/lib/commands/SDIFFSTORE.spec.ts | 45 +- packages/client/lib/commands/SDIFFSTORE.ts | 21 +- packages/client/lib/commands/SET.spec.ts | 257 +- packages/client/lib/commands/SET.ts | 114 +- packages/client/lib/commands/SETBIT.spec.ts | 38 +- packages/client/lib/commands/SETBIT.ts | 19 +- packages/client/lib/commands/SETEX.spec.ts | 38 +- packages/client/lib/commands/SETEX.ts | 28 +- packages/client/lib/commands/SETNX .spec.ts | 38 +- packages/client/lib/commands/SETNX.ts | 17 +- packages/client/lib/commands/SETRANGE.spec.ts | 38 +- packages/client/lib/commands/SETRANGE.ts | 27 +- packages/client/lib/commands/SHUTDOWN.spec.ts | 64 +- packages/client/lib/commands/SHUTDOWN.ts | 38 +- packages/client/lib/commands/SINTER.spec.ts | 45 +- packages/client/lib/commands/SINTER.ts | 25 +- .../client/lib/commands/SINTERCARD.spec.ts | 56 +- packages/client/lib/commands/SINTERCARD.ts | 35 +- .../client/lib/commands/SINTERSTORE.spec.ts | 45 +- packages/client/lib/commands/SINTERSTORE.ts | 25 +- .../client/lib/commands/SISMEMBER.spec.ts | 31 +- packages/client/lib/commands/SISMEMBER.ts | 18 +- packages/client/lib/commands/SMEMBERS.spec.ts | 31 +- packages/client/lib/commands/SMEMBERS.ts | 18 +- .../client/lib/commands/SMISMEMBER.spec.ts | 33 +- packages/client/lib/commands/SMISMEMBER.ts | 18 +- packages/client/lib/commands/SMOVE.spec.ts | 31 +- packages/client/lib/commands/SMOVE.ts | 23 +- packages/client/lib/commands/SORT.spec.ts | 169 +- packages/client/lib/commands/SORT.ts | 64 +- packages/client/lib/commands/SORT_RO.spec.ts | 171 +- packages/client/lib/commands/SORT_RO.ts | 24 +- .../client/lib/commands/SORT_STORE.spec.ts | 169 +- packages/client/lib/commands/SORT_STORE.ts | 26 +- packages/client/lib/commands/SPOP.spec.ts | 40 +- packages/client/lib/commands/SPOP.ts | 28 +- .../client/lib/commands/SPOP_COUNT.spec.ts | 22 + packages/client/lib/commands/SPOP_COUNT.ts | 10 + packages/client/lib/commands/SPUBLISH.spec.ts | 35 +- packages/client/lib/commands/SPUBLISH.ts | 20 +- .../client/lib/commands/SRANDMEMBER.spec.ts | 31 +- packages/client/lib/commands/SRANDMEMBER.ts | 15 +- .../lib/commands/SRANDMEMBER_COUNT.spec.ts | 31 +- .../client/lib/commands/SRANDMEMBER_COUNT.ts | 27 +- packages/client/lib/commands/SREM.spec.ts | 45 +- packages/client/lib/commands/SREM.ts | 22 +- packages/client/lib/commands/SSCAN.spec.ts | 109 +- packages/client/lib/commands/SSCAN.ts | 45 +- packages/client/lib/commands/STRLEN.spec.ts | 38 +- packages/client/lib/commands/STRLEN.ts | 17 +- packages/client/lib/commands/SUNION.spec.ts | 45 +- packages/client/lib/commands/SUNION.ts | 25 +- .../client/lib/commands/SUNIONSTORE.spec.ts | 45 +- packages/client/lib/commands/SUNIONSTORE.ts | 25 +- packages/client/lib/commands/SWAPDB.spec.ts | 28 +- packages/client/lib/commands/SWAPDB.ts | 12 +- packages/client/lib/commands/TIME.spec.ts | 27 +- packages/client/lib/commands/TIME.ts | 26 +- packages/client/lib/commands/TOUCH.spec.ts | 45 +- packages/client/lib/commands/TOUCH.ts | 21 +- packages/client/lib/commands/TTL.spec.ts | 31 +- packages/client/lib/commands/TTL.ts | 17 +- packages/client/lib/commands/TYPE.spec.ts | 31 +- packages/client/lib/commands/TYPE.ts | 17 +- packages/client/lib/commands/UNLINK.spec.ts | 45 +- packages/client/lib/commands/UNLINK.ts | 21 +- packages/client/lib/commands/UNWATCH.spec.ts | 19 - packages/client/lib/commands/UNWATCH.ts | 5 - packages/client/lib/commands/WAIT.spec.ts | 28 +- packages/client/lib/commands/WAIT.ts | 13 +- packages/client/lib/commands/WATCH.spec.ts | 20 - packages/client/lib/commands/WATCH.ts | 10 - packages/client/lib/commands/XACK.spec.ts | 45 +- packages/client/lib/commands/XACK.ts | 27 +- packages/client/lib/commands/XADD.spec.ts | 189 +- packages/client/lib/commands/XADD.ts | 83 +- .../lib/commands/XADD_NOMKSTREAM.spec.ts | 95 + .../client/lib/commands/XADD_NOMKSTREAM.ts | 16 + .../client/lib/commands/XAUTOCLAIM.spec.ts | 146 +- packages/client/lib/commands/XAUTOCLAIM.ts | 60 +- .../lib/commands/XAUTOCLAIM_JUSTID.spec.ts | 54 +- .../client/lib/commands/XAUTOCLAIM_JUSTID.ts | 42 +- packages/client/lib/commands/XCLAIM.spec.ts | 217 +- packages/client/lib/commands/XCLAIM.ts | 72 +- .../client/lib/commands/XCLAIM_JUSTID.spec.ts | 35 +- packages/client/lib/commands/XCLAIM_JUSTID.ts | 24 +- packages/client/lib/commands/XDEL.spec.ts | 45 +- packages/client/lib/commands/XDEL.ts | 22 +- .../client/lib/commands/XGROUP_CREATE.spec.ts | 62 +- packages/client/lib/commands/XGROUP_CREATE.ts | 36 +- .../commands/XGROUP_CREATECONSUMER.spec.ts | 39 +- .../lib/commands/XGROUP_CREATECONSUMER.ts | 23 +- .../lib/commands/XGROUP_DELCONSUMER.spec.ts | 37 +- .../client/lib/commands/XGROUP_DELCONSUMER.ts | 23 +- .../lib/commands/XGROUP_DESTROY.spec.ts | 37 +- .../client/lib/commands/XGROUP_DESTROY.ts | 21 +- .../client/lib/commands/XGROUP_SETID.spec.ts | 37 +- packages/client/lib/commands/XGROUP_SETID.ts | 33 +- .../lib/commands/XINFO_CONSUMERS.spec.ts | 69 +- .../client/lib/commands/XINFO_CONSUMERS.ts | 56 +- .../client/lib/commands/XINFO_GROUPS.spec.ts | 74 +- packages/client/lib/commands/XINFO_GROUPS.ts | 55 +- .../client/lib/commands/XINFO_STREAM.spec.ts | 99 +- packages/client/lib/commands/XINFO_STREAM.ts | 124 +- packages/client/lib/commands/XLEN.spec.ts | 31 +- packages/client/lib/commands/XLEN.ts | 17 +- packages/client/lib/commands/XPENDING.spec.ts | 102 +- packages/client/lib/commands/XPENDING.ts | 70 +- .../lib/commands/XPENDING_RANGE.spec.ts | 99 +- .../client/lib/commands/XPENDING_RANGE.ts | 83 +- packages/client/lib/commands/XRANGE.spec.ts | 61 +- packages/client/lib/commands/XRANGE.ts | 47 +- packages/client/lib/commands/XREAD.spec.ts | 209 +- packages/client/lib/commands/XREAD.ts | 72 +- .../client/lib/commands/XREADGROUP.spec.ts | 270 +- packages/client/lib/commands/XREADGROUP.ts | 74 +- .../client/lib/commands/XREVRANGE.spec.ts | 61 +- packages/client/lib/commands/XREVRANGE.ts | 33 +- packages/client/lib/commands/XSETID.spec.ts | 46 + packages/client/lib/commands/XSETID.ts | 33 +- packages/client/lib/commands/XTRIM.spec.ts | 83 +- packages/client/lib/commands/XTRIM.ts | 30 +- packages/client/lib/commands/ZADD.spec.ts | 244 +- packages/client/lib/commands/ZADD.ts | 127 +- .../client/lib/commands/ZADD_INCR.spec.ts | 93 + packages/client/lib/commands/ZADD_INCR.ts | 39 + packages/client/lib/commands/ZCARD.spec.ts | 31 +- packages/client/lib/commands/ZCARD.ts | 17 +- packages/client/lib/commands/ZCOUNT.spec.ts | 31 +- packages/client/lib/commands/ZCOUNT.ts | 35 +- packages/client/lib/commands/ZDIFF.spec.ts | 47 +- packages/client/lib/commands/ZDIFF.ts | 25 +- .../client/lib/commands/ZDIFFSTORE.spec.ts | 47 +- packages/client/lib/commands/ZDIFFSTORE.ts | 25 +- .../lib/commands/ZDIFF_WITHSCORES.spec.ts | 47 +- .../client/lib/commands/ZDIFF_WITHSCORES.ts | 25 +- packages/client/lib/commands/ZINCRBY.spec.ts | 31 +- packages/client/lib/commands/ZINCRBY.ts | 30 +- packages/client/lib/commands/ZINTER.spec.ts | 101 +- packages/client/lib/commands/ZINTER.ts | 54 +- .../client/lib/commands/ZINTERCARD.spec.ts | 56 +- packages/client/lib/commands/ZINTERCARD.ts | 36 +- .../client/lib/commands/ZINTERSTORE.spec.ts | 99 +- packages/client/lib/commands/ZINTERSTORE.ts | 45 +- .../lib/commands/ZINTER_WITHSCORES.spec.ts | 101 +- .../client/lib/commands/ZINTER_WITHSCORES.ts | 25 +- .../client/lib/commands/ZLEXCOUNT.spec.ts | 31 +- packages/client/lib/commands/ZLEXCOUNT.ts | 33 +- packages/client/lib/commands/ZMPOP.spec.ts | 71 +- packages/client/lib/commands/ZMPOP.ts | 85 +- packages/client/lib/commands/ZMSCORE.spec.ts | 47 +- packages/client/lib/commands/ZMSCORE.ts | 32 +- packages/client/lib/commands/ZPOPMAX.spec.ts | 64 +- packages/client/lib/commands/ZPOPMAX.ts | 34 +- .../client/lib/commands/ZPOPMAX_COUNT.spec.ts | 41 +- packages/client/lib/commands/ZPOPMAX_COUNT.ts | 25 +- packages/client/lib/commands/ZPOPMIN.spec.ts | 64 +- packages/client/lib/commands/ZPOPMIN.ts | 21 +- .../client/lib/commands/ZPOPMIN_COUNT.spec.ts | 41 +- packages/client/lib/commands/ZPOPMIN_COUNT.ts | 25 +- .../client/lib/commands/ZRANDMEMBER.spec.ts | 33 +- packages/client/lib/commands/ZRANDMEMBER.ts | 17 +- .../lib/commands/ZRANDMEMBER_COUNT.spec.ts | 33 +- .../client/lib/commands/ZRANDMEMBER_COUNT.ts | 27 +- .../ZRANDMEMBER_COUNT_WITHSCORES.spec.ts | 33 +- .../commands/ZRANDMEMBER_COUNT_WITHSCORES.ts | 25 +- packages/client/lib/commands/ZRANGE.spec.ts | 129 +- packages/client/lib/commands/ZRANGE.ts | 71 +- .../client/lib/commands/ZRANGEBYLEX.spec.ts | 55 +- packages/client/lib/commands/ZRANGEBYLEX.ts | 45 +- .../client/lib/commands/ZRANGEBYSCORE.spec.ts | 55 +- packages/client/lib/commands/ZRANGEBYSCORE.ts | 43 +- .../commands/ZRANGEBYSCORE_WITHSCORES.spec.ts | 55 +- .../lib/commands/ZRANGEBYSCORE_WITHSCORES.ts | 30 +- .../client/lib/commands/ZRANGESTORE.spec.ts | 145 +- packages/client/lib/commands/ZRANGESTORE.ts | 80 +- .../lib/commands/ZRANGE_WITHSCORES.spec.ts | 120 +- .../client/lib/commands/ZRANGE_WITHSCORES.ts | 24 +- packages/client/lib/commands/ZRANK.spec.ts | 31 +- packages/client/lib/commands/ZRANK.ts | 20 +- .../lib/commands/ZRANK_WITHSCORE.spec.ts | 46 + .../client/lib/commands/ZRANK_WITHSCORE.ts | 30 + packages/client/lib/commands/ZREM.spec.ts | 45 +- packages/client/lib/commands/ZREM.ts | 25 +- .../lib/commands/ZREMRANGEBYLEX.spec.ts | 31 +- .../client/lib/commands/ZREMRANGEBYLEX.ts | 33 +- .../lib/commands/ZREMRANGEBYRANK.spec.ts | 31 +- .../client/lib/commands/ZREMRANGEBYRANK.ts | 20 +- .../lib/commands/ZREMRANGEBYSCORE.spec.ts | 31 +- .../client/lib/commands/ZREMRANGEBYSCORE.ts | 33 +- packages/client/lib/commands/ZREVRANK.spec.ts | 31 +- packages/client/lib/commands/ZREVRANK.ts | 20 +- packages/client/lib/commands/ZSCAN.spec.ts | 109 +- packages/client/lib/commands/ZSCAN.ts | 55 +- packages/client/lib/commands/ZSCORE.spec.ts | 31 +- packages/client/lib/commands/ZSCORE.ts | 20 +- packages/client/lib/commands/ZUNION.spec.ts | 93 +- packages/client/lib/commands/ZUNION.ts | 36 +- .../client/lib/commands/ZUNIONSTORE.spec.ts | 99 +- packages/client/lib/commands/ZUNIONSTORE.ts | 36 +- .../lib/commands/ZUNION_WITHSCORES.spec.ts | 93 +- .../client/lib/commands/ZUNION_WITHSCORES.ts | 25 +- .../lib/commands/generic-transformers.spec.ts | 1403 +++-- .../lib/commands/generic-transformers.ts | 1076 ++-- packages/client/lib/commands/index.ts | 1121 +++- packages/client/lib/errors.ts | 100 +- packages/client/lib/lua-script.ts | 24 +- packages/client/lib/multi-command.spec.ts | 139 +- packages/client/lib/multi-command.ts | 125 +- .../lib/sentinel/commands/SENTINEL_MASTER.ts | 12 + .../lib/sentinel/commands/SENTINEL_MONITOR.ts | 8 + .../sentinel/commands/SENTINEL_REPLICAS.ts | 23 + .../sentinel/commands/SENTINEL_SENTINELS.ts | 23 + .../lib/sentinel/commands/SENTINEL_SET.ts | 19 + .../client/lib/sentinel/commands/index.ts | 19 + packages/client/lib/sentinel/index.spec.ts | 1276 +++++ packages/client/lib/sentinel/index.ts | 1487 ++++++ packages/client/lib/sentinel/module.ts | 7 + .../client/lib/sentinel/multi-commands.ts | 219 + packages/client/lib/sentinel/pub-sub-proxy.ts | 209 + packages/client/lib/sentinel/test-util.ts | 605 +++ packages/client/lib/sentinel/types.ts | 175 + packages/client/lib/sentinel/utils.ts | 114 + packages/client/lib/sentinel/wait-queue.ts | 24 + packages/client/lib/test-utils.ts | 86 +- packages/client/lib/utils.ts | 3 - packages/client/package.json | 32 +- packages/client/tsconfig.json | 9 +- packages/graph/.release-it.json | 1 + .../graph/lib/commands/CONFIG_GET.spec.ts | 36 +- packages/graph/lib/commands/CONFIG_GET.ts | 23 +- .../graph/lib/commands/CONFIG_SET.spec.ts | 30 +- packages/graph/lib/commands/CONFIG_SET.ts | 21 +- packages/graph/lib/commands/DELETE.spec.ts | 32 +- packages/graph/lib/commands/DELETE.ts | 13 +- packages/graph/lib/commands/EXPLAIN.spec.ts | 36 +- packages/graph/lib/commands/EXPLAIN.ts | 15 +- packages/graph/lib/commands/LIST.spec.ts | 30 +- packages/graph/lib/commands/LIST.ts | 13 +- packages/graph/lib/commands/PROFILE.spec.ts | 30 +- packages/graph/lib/commands/PROFILE.ts | 15 +- packages/graph/lib/commands/QUERY.spec.ts | 68 +- packages/graph/lib/commands/QUERY.ts | 137 +- packages/graph/lib/commands/RO_QUERY.spec.ts | 32 +- packages/graph/lib/commands/RO_QUERY.ts | 32 +- packages/graph/lib/commands/SLOWLOG.spec.ts | 30 +- packages/graph/lib/commands/SLOWLOG.ts | 45 +- packages/graph/lib/commands/index.spec.ts | 62 - packages/graph/lib/commands/index.ts | 141 +- packages/graph/lib/graph.spec.ts | 286 +- packages/graph/lib/graph.ts | 568 +- packages/graph/lib/test-utils.ts | 21 +- packages/graph/package.json | 28 +- packages/json/.npmignore | 6 - packages/json/.release-it.json | 1 + packages/json/README.md | 24 +- packages/json/lib/commands/ARRAPPEND.spec.ts | 46 +- packages/json/lib/commands/ARRAPPEND.ts | 30 +- packages/json/lib/commands/ARRINDEX.spec.ts | 69 +- packages/json/lib/commands/ARRINDEX.ts | 36 +- packages/json/lib/commands/ARRINSERT.spec.ts | 46 +- packages/json/lib/commands/ARRINSERT.ts | 32 +- packages/json/lib/commands/ARRLEN.spec.ts | 48 +- packages/json/lib/commands/ARRLEN.ts | 21 +- packages/json/lib/commands/ARRPOP.spec.ts | 113 +- packages/json/lib/commands/ARRPOP.ts | 43 +- packages/json/lib/commands/ARRTRIM.spec.ts | 32 +- packages/json/lib/commands/ARRTRIM.ts | 13 +- packages/json/lib/commands/CLEAR.spec.ts | 32 + packages/json/lib/commands/CLEAR.ts | 20 + .../json/lib/commands/DEBUG_MEMORY.spec.ts | 46 +- packages/json/lib/commands/DEBUG_MEMORY.ts | 21 +- packages/json/lib/commands/DEL.spec.ts | 47 +- packages/json/lib/commands/DEL.ts | 21 +- packages/json/lib/commands/FORGET.spec.ts | 46 +- packages/json/lib/commands/FORGET.ts | 21 +- packages/json/lib/commands/GET.spec.ts | 105 +- packages/json/lib/commands/GET.ts | 50 +- packages/json/lib/commands/MERGE.spec.ts | 32 +- packages/json/lib/commands/MERGE.ts | 21 +- packages/json/lib/commands/MGET.spec.ts | 30 +- packages/json/lib/commands/MGET.ts | 28 +- packages/json/lib/commands/MSET.spec.ts | 62 +- packages/json/lib/commands/MSET.ts | 34 +- packages/json/lib/commands/NUMINCRBY.spec.ts | 32 +- packages/json/lib/commands/NUMINCRBY.ts | 18 +- packages/json/lib/commands/NUMMULTBY.spec.ts | 32 +- packages/json/lib/commands/NUMMULTBY.ts | 14 +- packages/json/lib/commands/OBJKEYS.spec.ts | 46 +- packages/json/lib/commands/OBJKEYS.ts | 21 +- packages/json/lib/commands/OBJLEN.spec.ts | 46 +- packages/json/lib/commands/OBJLEN.ts | 21 +- packages/json/lib/commands/RESP.spec.ts | 2 +- packages/json/lib/commands/SET.spec.ts | 56 +- packages/json/lib/commands/SET.ts | 45 +- packages/json/lib/commands/STRAPPEND.spec.ts | 48 +- packages/json/lib/commands/STRAPPEND.ts | 27 +- packages/json/lib/commands/STRLEN.spec.ts | 48 +- packages/json/lib/commands/STRLEN.ts | 21 +- packages/json/lib/commands/TOGGLE.spec.ts | 21 + packages/json/lib/commands/TOGGLE.ts | 10 + packages/json/lib/commands/TYPE.spec.ts | 46 +- packages/json/lib/commands/TYPE.ts | 26 +- packages/json/lib/commands/index.ts | 174 +- packages/json/lib/test-utils.ts | 22 +- packages/json/package.json | 28 +- .../redis/.release-it.json | 0 packages/redis/README.md | 203 + packages/redis/index.ts | 87 + packages/redis/package.json | 34 + packages/redis/tsconfig.json | 9 + packages/search/.npmignore | 6 - packages/search/.release-it.json | 1 + packages/search/README.md | 10 +- .../search/lib/commands/AGGREGATE.spec.ts | 955 ++-- packages/search/lib/commands/AGGREGATE.ts | 513 +- .../lib/commands/AGGREGATE_WITHCURSOR.spec.ts | 68 +- .../lib/commands/AGGREGATE_WITHCURSOR.ts | 73 +- packages/search/lib/commands/ALIASADD.spec.ts | 31 +- packages/search/lib/commands/ALIASADD.ts | 13 +- packages/search/lib/commands/ALIASDEL.spec.ts | 32 +- packages/search/lib/commands/ALIASDEL.ts | 13 +- .../search/lib/commands/ALIASUPDATE.spec.ts | 31 +- packages/search/lib/commands/ALIASUPDATE.ts | 13 +- packages/search/lib/commands/ALTER.spec.ts | 60 +- packages/search/lib/commands/ALTER.ts | 15 +- .../search/lib/commands/CONFIG_GET.spec.ts | 42 +- packages/search/lib/commands/CONFIG_GET.ts | 26 +- .../search/lib/commands/CONFIG_SET.spec.ts | 25 +- packages/search/lib/commands/CONFIG_SET.ts | 17 +- packages/search/lib/commands/CREATE.spec.ts | 877 ++-- packages/search/lib/commands/CREATE.ts | 349 +- .../search/lib/commands/CURSOR_DEL.spec.ts | 55 +- packages/search/lib/commands/CURSOR_DEL.ts | 22 +- .../search/lib/commands/CURSOR_READ.spec.ts | 75 +- packages/search/lib/commands/CURSOR_READ.ts | 38 +- packages/search/lib/commands/DICTADD.spec.ts | 44 +- packages/search/lib/commands/DICTADD.ts | 17 +- packages/search/lib/commands/DICTDEL.spec.ts | 44 +- packages/search/lib/commands/DICTDEL.ts | 17 +- packages/search/lib/commands/DICTDUMP.spec.ts | 32 +- packages/search/lib/commands/DICTDUMP.ts | 16 +- .../search/lib/commands/DROPINDEX.spec.ts | 52 +- packages/search/lib/commands/DROPINDEX.ts | 22 +- packages/search/lib/commands/EXPLAIN.spec.ts | 67 +- packages/search/lib/commands/EXPLAIN.ts | 34 +- .../search/lib/commands/EXPLAINCLI.spec.ts | 16 +- packages/search/lib/commands/EXPLAINCLI.ts | 13 +- packages/search/lib/commands/INFO.spec.ts | 91 +- packages/search/lib/commands/INFO.ts | 316 +- .../lib/commands/PROFILE_AGGREGATE.spec.ts | 17 +- .../search/lib/commands/PROFILE_AGGREGATE.ts | 57 +- .../lib/commands/PROFILE_SEARCH.spec.ts | 14 +- .../search/lib/commands/PROFILE_SEARCH.ts | 157 +- packages/search/lib/commands/SEARCH.spec.ts | 561 +- packages/search/lib/commands/SEARCH.ts | 301 +- .../lib/commands/SEARCH_NOCONTENT.spec.ts | 65 +- .../search/lib/commands/SEARCH_NOCONTENT.ts | 51 +- .../search/lib/commands/SPELLCHECK.spec.ts | 139 +- packages/search/lib/commands/SPELLCHECK.ts | 83 +- packages/search/lib/commands/SUGADD.spec.ts | 56 +- packages/search/lib/commands/SUGADD.ts | 23 +- packages/search/lib/commands/SUGDEL.spec.ts | 30 +- packages/search/lib/commands/SUGDEL.ts | 13 +- packages/search/lib/commands/SUGGET.spec.ts | 74 +- packages/search/lib/commands/SUGGET.ts | 25 +- .../lib/commands/SUGGET_WITHPAYLOADS.spec.ts | 56 +- .../lib/commands/SUGGET_WITHPAYLOADS.ts | 50 +- .../lib/commands/SUGGET_WITHSCORES.spec.ts | 54 +- .../search/lib/commands/SUGGET_WITHSCORES.ts | 67 +- .../SUGGET_WITHSCORES_WITHPAYLOADS.spec.ts | 58 +- .../SUGGET_WITHSCORES_WITHPAYLOADS.ts | 74 +- packages/search/lib/commands/SUGLEN.spec.ts | 30 +- packages/search/lib/commands/SUGLEN.ts | 13 +- packages/search/lib/commands/SYNDUMP.spec.ts | 38 +- packages/search/lib/commands/SYNDUMP.ts | 25 +- .../search/lib/commands/SYNUPDATE.spec.ts | 66 +- packages/search/lib/commands/SYNUPDATE.ts | 33 +- packages/search/lib/commands/TAGVALS.spec.ts | 38 +- packages/search/lib/commands/TAGVALS.ts | 16 +- packages/search/lib/commands/_LIST.spec.ts | 28 +- packages/search/lib/commands/_LIST.ts | 16 +- packages/search/lib/commands/index.spec.ts | 6 +- packages/search/lib/commands/index.ts | 789 +-- packages/search/lib/index.ts | 8 +- packages/search/lib/test-utils.ts | 6 +- packages/search/package.json | 28 +- packages/test-utils/lib/dockers.ts | 374 +- packages/test-utils/lib/index.ts | 471 +- packages/test-utils/package.json | 19 +- packages/test-utils/tsconfig.json | 5 +- packages/time-series/.npmignore | 6 - packages/time-series/.release-it.json | 1 + packages/time-series/README.md | 180 +- packages/time-series/lib/commands/ADD.spec.ts | 159 +- packages/time-series/lib/commands/ADD.ts | 67 +- .../time-series/lib/commands/ALTER.spec.ts | 143 +- packages/time-series/lib/commands/ALTER.ts | 26 +- .../time-series/lib/commands/CREATE.spec.ts | 161 +- packages/time-series/lib/commands/CREATE.ts | 46 +- .../lib/commands/CREATERULE.spec.ts | 51 +- .../time-series/lib/commands/CREATERULE.ts | 53 +- .../time-series/lib/commands/DECRBY.spec.ts | 151 +- packages/time-series/lib/commands/DECRBY.ts | 17 +- packages/time-series/lib/commands/DEL.spec.ts | 32 +- packages/time-series/lib/commands/DEL.ts | 23 +- .../lib/commands/DELETERULE.spec.ts | 40 +- .../time-series/lib/commands/DELETERULE.ts | 19 +- packages/time-series/lib/commands/GET.spec.ts | 74 +- packages/time-series/lib/commands/GET.ts | 45 +- .../time-series/lib/commands/INCRBY.spec.ts | 169 +- packages/time-series/lib/commands/INCRBY.ts | 52 +- .../time-series/lib/commands/INFO.spec.ts | 17 +- packages/time-series/lib/commands/INFO.ts | 194 +- .../lib/commands/INFO_DEBUG.spec.ts | 19 +- .../time-series/lib/commands/INFO_DEBUG.ts | 122 +- .../time-series/lib/commands/MADD.spec.ts | 70 +- packages/time-series/lib/commands/MADD.ts | 32 +- .../time-series/lib/commands/MGET.spec.ts | 71 +- packages/time-series/lib/commands/MGET.ts | 76 +- .../lib/commands/MGET_SELECTED_LABELS.spec.ts | 46 + .../lib/commands/MGET_SELECTED_LABELS.ts | 16 + .../lib/commands/MGET_WITHLABELS.spec.ts | 72 +- .../lib/commands/MGET_WITHLABELS.ts | 86 +- .../time-series/lib/commands/MRANGE.spec.ts | 102 +- packages/time-series/lib/commands/MRANGE.ts | 65 +- .../lib/commands/MRANGE_GROUPBY.spec.ts | 66 + .../lib/commands/MRANGE_GROUPBY.ts | 108 + .../commands/MRANGE_SELECTED_LABELS.spec.ts | 72 + .../lib/commands/MRANGE_SELECTED_LABELS.ts | 70 + .../MRANGE_SELECTED_LABELS_GROUPBY.spec.ts | 80 + .../MRANGE_SELECTED_LABELS_GROUPBY.ts | 63 + .../lib/commands/MRANGE_WITHLABELS.spec.ts | 106 +- .../lib/commands/MRANGE_WITHLABELS.ts | 85 +- .../MRANGE_WITHLABELS_GROUPBY.spec.ts | 77 + .../lib/commands/MRANGE_WITHLABELS_GROUPBY.ts | 79 + .../lib/commands/MREVRANGE.spec.ts | 102 +- .../time-series/lib/commands/MREVRANGE.ts | 28 +- .../lib/commands/MREVRANGE_GROUPBY.spec.ts | 67 + .../lib/commands/MREVRANGE_GROUPBY.ts | 9 + .../MREVRANGE_SELECTED_LABELS.spec.ts | 73 + .../lib/commands/MREVRANGE_SELECTED_LABELS.ts | 9 + .../MREVRANGE_SELECTED_LABELS_GROUPBY.spec.ts | 80 + .../MREVRANGE_SELECTED_LABELS_GROUPBY.ts | 9 + .../lib/commands/MREVRANGE_WITHLABELS.spec.ts | 106 +- .../lib/commands/MREVRANGE_WITHLABELS.ts | 28 +- .../MREVRANGE_WITHLABELS_GROUPBY.spec.ts | 77 + .../commands/MREVRANGE_WITHLABELS_GROUPBY.ts | 9 + .../lib/commands/QUERYINDEX.spec.ts | 56 +- .../time-series/lib/commands/QUERYINDEX.ts | 23 +- .../time-series/lib/commands/RANGE.spec.ts | 68 +- packages/time-series/lib/commands/RANGE.ts | 135 +- .../time-series/lib/commands/REVRANGE.spec.ts | 142 +- packages/time-series/lib/commands/REVRANGE.ts | 33 +- .../time-series/lib/commands/index.spec.ts | 862 ++- packages/time-series/lib/commands/index.ts | 764 ++- packages/time-series/lib/index.ts | 12 +- packages/time-series/lib/test-utils.ts | 21 +- packages/time-series/package.json | 28 +- tsconfig.base.json | 23 +- tsconfig.json | 25 +- 1174 files changed, 45668 insertions(+), 36011 deletions(-) create mode 100644 .github/release-drafter-base.yml delete mode 100644 .npmignore create mode 100644 benchmark/lib/ping/ioredis-auto-pipeline.js create mode 100644 benchmark/lib/ping/local-resp2.js create mode 100644 benchmark/lib/ping/local-resp3-buffer-proxy.js create mode 100644 benchmark/lib/ping/local-resp3-buffer.js create mode 100644 benchmark/lib/ping/local-resp3-module-with-flags.js create mode 100644 benchmark/lib/ping/local-resp3-module.js create mode 100644 benchmark/lib/ping/local-resp3.js create mode 100644 docs/RESP.md create mode 100644 docs/command-options.md delete mode 100644 docs/isolated-execution.md create mode 100644 docs/pool.md create mode 100644 docs/programmability.md create mode 100644 docs/scan-iterators.md create mode 100644 docs/sentinel.md create mode 100644 docs/todo.md create mode 100644 docs/transactions.md create mode 100644 docs/v4-to-v5.md create mode 100644 docs/v5.md delete mode 100644 index.ts delete mode 100644 packages/bloom/.npmignore delete mode 100644 packages/bloom/lib/commands/cuckoo/index.spec.ts delete mode 100644 packages/bloom/lib/commands/t-digest/index.spec.ts delete mode 100644 packages/client/.eslintrc.json delete mode 100644 packages/client/.npmignore create mode 100644 packages/client/lib/RESP/decoder.spec.ts create mode 100644 packages/client/lib/RESP/decoder.ts create mode 100644 packages/client/lib/RESP/encoder.spec.ts create mode 100644 packages/client/lib/RESP/encoder.ts create mode 100644 packages/client/lib/RESP/types.ts create mode 100644 packages/client/lib/RESP/verbatim-string.ts delete mode 100644 packages/client/lib/client/RESP2/composers/buffer.spec.ts delete mode 100644 packages/client/lib/client/RESP2/composers/buffer.ts delete mode 100644 packages/client/lib/client/RESP2/composers/interface.ts delete mode 100644 packages/client/lib/client/RESP2/composers/string.spec.ts delete mode 100644 packages/client/lib/client/RESP2/composers/string.ts delete mode 100644 packages/client/lib/client/RESP2/decoder.spec.ts delete mode 100644 packages/client/lib/client/RESP2/decoder.ts delete mode 100644 packages/client/lib/client/RESP2/encoder.spec.ts delete mode 100644 packages/client/lib/client/RESP2/encoder.ts delete mode 100644 packages/client/lib/client/commands.ts create mode 100644 packages/client/lib/client/legacy-mode.spec.ts create mode 100644 packages/client/lib/client/legacy-mode.ts create mode 100644 packages/client/lib/client/linked-list.spec.ts create mode 100644 packages/client/lib/client/linked-list.ts create mode 100644 packages/client/lib/client/pool.spec.ts create mode 100644 packages/client/lib/client/pool.ts delete mode 100644 packages/client/lib/cluster/commands.ts delete mode 100644 packages/client/lib/command-options.ts delete mode 100644 packages/client/lib/commands/GEORADIUSBYMEMBERSTORE.spec.ts delete mode 100644 packages/client/lib/commands/GEORADIUSBYMEMBERSTORE.ts create mode 100644 packages/client/lib/commands/GEORADIUSBYMEMBER_STORE.spec.ts create mode 100644 packages/client/lib/commands/GEORADIUSBYMEMBER_STORE.ts delete mode 100644 packages/client/lib/commands/GEORADIUSSTORE.spec.ts delete mode 100644 packages/client/lib/commands/GEORADIUSSTORE.ts create mode 100644 packages/client/lib/commands/GEORADIUS_STORE.spec.ts create mode 100644 packages/client/lib/commands/GEORADIUS_STORE.ts create mode 100644 packages/client/lib/commands/SPOP_COUNT.spec.ts create mode 100644 packages/client/lib/commands/SPOP_COUNT.ts delete mode 100644 packages/client/lib/commands/UNWATCH.spec.ts delete mode 100644 packages/client/lib/commands/UNWATCH.ts delete mode 100644 packages/client/lib/commands/WATCH.spec.ts delete mode 100644 packages/client/lib/commands/WATCH.ts create mode 100644 packages/client/lib/commands/XADD_NOMKSTREAM.spec.ts create mode 100644 packages/client/lib/commands/XADD_NOMKSTREAM.ts create mode 100644 packages/client/lib/commands/ZADD_INCR.spec.ts create mode 100644 packages/client/lib/commands/ZADD_INCR.ts create mode 100644 packages/client/lib/commands/ZRANK_WITHSCORE.spec.ts create mode 100644 packages/client/lib/commands/ZRANK_WITHSCORE.ts create mode 100644 packages/client/lib/sentinel/commands/SENTINEL_MASTER.ts create mode 100644 packages/client/lib/sentinel/commands/SENTINEL_MONITOR.ts create mode 100644 packages/client/lib/sentinel/commands/SENTINEL_REPLICAS.ts create mode 100644 packages/client/lib/sentinel/commands/SENTINEL_SENTINELS.ts create mode 100644 packages/client/lib/sentinel/commands/SENTINEL_SET.ts create mode 100644 packages/client/lib/sentinel/commands/index.ts create mode 100644 packages/client/lib/sentinel/index.spec.ts create mode 100644 packages/client/lib/sentinel/index.ts create mode 100644 packages/client/lib/sentinel/module.ts create mode 100644 packages/client/lib/sentinel/multi-commands.ts create mode 100644 packages/client/lib/sentinel/pub-sub-proxy.ts create mode 100644 packages/client/lib/sentinel/test-util.ts create mode 100644 packages/client/lib/sentinel/types.ts create mode 100644 packages/client/lib/sentinel/utils.ts create mode 100644 packages/client/lib/sentinel/wait-queue.ts delete mode 100644 packages/client/lib/utils.ts delete mode 100644 packages/graph/lib/commands/index.spec.ts delete mode 100644 packages/json/.npmignore create mode 100644 packages/json/lib/commands/CLEAR.spec.ts create mode 100644 packages/json/lib/commands/CLEAR.ts create mode 100644 packages/json/lib/commands/TOGGLE.spec.ts create mode 100644 packages/json/lib/commands/TOGGLE.ts rename .release-it.json => packages/redis/.release-it.json (100%) create mode 100644 packages/redis/README.md create mode 100644 packages/redis/index.ts create mode 100644 packages/redis/package.json create mode 100644 packages/redis/tsconfig.json delete mode 100644 packages/search/.npmignore delete mode 100644 packages/time-series/.npmignore create mode 100644 packages/time-series/lib/commands/MGET_SELECTED_LABELS.spec.ts create mode 100644 packages/time-series/lib/commands/MGET_SELECTED_LABELS.ts create mode 100644 packages/time-series/lib/commands/MRANGE_GROUPBY.spec.ts create mode 100644 packages/time-series/lib/commands/MRANGE_GROUPBY.ts create mode 100644 packages/time-series/lib/commands/MRANGE_SELECTED_LABELS.spec.ts create mode 100644 packages/time-series/lib/commands/MRANGE_SELECTED_LABELS.ts create mode 100644 packages/time-series/lib/commands/MRANGE_SELECTED_LABELS_GROUPBY.spec.ts create mode 100644 packages/time-series/lib/commands/MRANGE_SELECTED_LABELS_GROUPBY.ts create mode 100644 packages/time-series/lib/commands/MRANGE_WITHLABELS_GROUPBY.spec.ts create mode 100644 packages/time-series/lib/commands/MRANGE_WITHLABELS_GROUPBY.ts create mode 100644 packages/time-series/lib/commands/MREVRANGE_GROUPBY.spec.ts create mode 100644 packages/time-series/lib/commands/MREVRANGE_GROUPBY.ts create mode 100644 packages/time-series/lib/commands/MREVRANGE_SELECTED_LABELS.spec.ts create mode 100644 packages/time-series/lib/commands/MREVRANGE_SELECTED_LABELS.ts create mode 100644 packages/time-series/lib/commands/MREVRANGE_SELECTED_LABELS_GROUPBY.spec.ts create mode 100644 packages/time-series/lib/commands/MREVRANGE_SELECTED_LABELS_GROUPBY.ts create mode 100644 packages/time-series/lib/commands/MREVRANGE_WITHLABELS_GROUPBY.spec.ts create mode 100644 packages/time-series/lib/commands/MREVRANGE_WITHLABELS_GROUPBY.ts diff --git a/.github/release-drafter-base.yml b/.github/release-drafter-base.yml new file mode 100644 index 00000000000..ea259fc0d2d --- /dev/null +++ b/.github/release-drafter-base.yml @@ -0,0 +1,50 @@ +name-template: 'json@$NEXT_PATCH_VERSION' +tag-template: 'json@$NEXT_PATCH_VERSION' +autolabeler: + - label: 'chore' + files: + - '*.md' + - '.github/*' + - label: 'bug' + branch: + - '/bug-.+' + - label: 'chore' + branch: + - '/chore-.+' + - label: 'feature' + branch: + - '/feature-.+' +categories: + - title: 'Breaking Changes' + labels: + - 'breakingchange' + - title: '🚀 New Features' + labels: + - 'feature' + - 'enhancement' + - title: '🐛 Bug Fixes' + labels: + - 'fix' + - 'bugfix' + - 'bug' + - title: '🧰 Maintenance' + label: + - 'chore' + - 'maintenance' + - 'documentation' + - 'docs' + +change-template: '- $TITLE (#$NUMBER)' +include-paths: + - 'packages/json' +exclude-labels: + - 'skip-changelog' +template: | + ## Changes + + $CHANGES + + ## Contributors + We'd like to thank all the contributors who worked on this release! + + $CONTRIBUTORS diff --git a/.github/workflows/documentation.yml b/.github/workflows/documentation.yml index e095faf1918..a8c22752423 100644 --- a/.github/workflows/documentation.yml +++ b/.github/workflows/documentation.yml @@ -17,8 +17,6 @@ jobs: uses: actions/setup-node@v3 - name: Install Packages run: npm ci - - name: Build tests tools - run: npm run build:tests-tools - name: Generate Documentation run: npm run documentation - name: Upload diff --git a/.github/workflows/tests.yml b/.github/workflows/tests.yml index 84d70d6b4c0..68ea09c6e4f 100644 --- a/.github/workflows/tests.yml +++ b/.github/workflows/tests.yml @@ -5,11 +5,12 @@ on: branches: - master - v4.0 + - v5 pull_request: branches: - master - v4.0 - + - v5 jobs: tests: runs-on: ubuntu-latest @@ -17,7 +18,7 @@ jobs: fail-fast: false matrix: node-version: ['18', '20'] - redis-version: ['5', '6.0', '6.2', '7.0', '7.2', '7.4-rc2'] + redis-version: ['6.2.6-v17', '7.2.0-v13', '7.4.0-v1'] steps: - uses: actions/checkout@v4 with: @@ -31,10 +32,10 @@ jobs: if: ${{ matrix.node-version <= 14 }} - name: Install Packages run: npm ci - - name: Build tests tools - run: npm run build:tests-tools + - name: Build + run: npm run build - name: Run Tests - run: npm run test -- -- --forbid-only --redis-version=${{ matrix.redis-version }} + run: npm run test -ws --if-present -- --forbid-only --redis-version=${{ matrix.redis-version }} - name: Upload to Codecov run: | curl https://keybase.io/codecovsecurity/pgp_keys.asc | gpg --no-default-keyring --keyring trustedkeys.gpg --import diff --git a/.gitignore b/.gitignore index dfd47ff6716..ecdef37dffd 100644 --- a/.gitignore +++ b/.gitignore @@ -7,3 +7,4 @@ node_modules/ .DS_Store dump.rdb documentation/ +tsconfig.tsbuildinfo diff --git a/.npmignore b/.npmignore deleted file mode 100644 index a36c5e83cf2..00000000000 --- a/.npmignore +++ /dev/null @@ -1,12 +0,0 @@ -.github/ -.vscode/ -docs/ -examples/ -packages/ -.deepsource.toml -.release-it.json -CONTRIBUTING.md -SECURITY.md -index.ts -tsconfig.base.json -tsconfig.json diff --git a/README.md b/README.md index a590372b1b1..990204eb3df 100644 --- a/README.md +++ b/README.md @@ -25,25 +25,11 @@ node-redis is a modern, high performance [Redis](https://redis.io) client for No [Work at Redis](https://redis.com/company/careers/jobs/) -## Packages - -| Name | Description | -|----------------------------------------------|--------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------| -| [redis](./) | [![Downloads](https://img.shields.io/npm/dm/redis.svg)](https://www.npmjs.com/package/redis) [![Version](https://img.shields.io/npm/v/redis.svg)](https://www.npmjs.com/package/redis) | -| [@redis/client](./packages/client) | [![Downloads](https://img.shields.io/npm/dm/@redis/client.svg)](https://www.npmjs.com/package/@redis/client) [![Version](https://img.shields.io/npm/v/@redis/client.svg)](https://www.npmjs.com/package/@redis/client) [![Docs](https://img.shields.io/badge/-documentation-dc382c)](https://redis.js.org/documentation/client/) | -| [@redis/bloom](./packages/bloom) | [![Downloads](https://img.shields.io/npm/dm/@redis/bloom.svg)](https://www.npmjs.com/package/@redis/bloom) [![Version](https://img.shields.io/npm/v/@redis/bloom.svg)](https://www.npmjs.com/package/@redis/bloom) [![Docs](https://img.shields.io/badge/-documentation-dc382c)](https://redis.js.org/documentation/bloom/) [Redis Bloom](https://oss.redis.com/redisbloom/) commands | -| [@redis/graph](./packages/graph) | [![Downloads](https://img.shields.io/npm/dm/@redis/graph.svg)](https://www.npmjs.com/package/@redis/graph) [![Version](https://img.shields.io/npm/v/@redis/graph.svg)](https://www.npmjs.com/package/@redis/graph) [![Docs](https://img.shields.io/badge/-documentation-dc382c)](https://redis.js.org/documentation/graph/) [Redis Graph](https://oss.redis.com/redisgraph/) commands | -| [@redis/json](./packages/json) | [![Downloads](https://img.shields.io/npm/dm/@redis/json.svg)](https://www.npmjs.com/package/@redis/json) [![Version](https://img.shields.io/npm/v/@redis/json.svg)](https://www.npmjs.com/package/@redis/json) [![Docs](https://img.shields.io/badge/-documentation-dc382c)](https://redis.js.org/documentation/json/) [Redis JSON](https://oss.redis.com/redisjson/) commands | -| [@redis/search](./packages/search) | [![Downloads](https://img.shields.io/npm/dm/@redis/search.svg)](https://www.npmjs.com/package/@redis/search) [![Version](https://img.shields.io/npm/v/@redis/search.svg)](https://www.npmjs.com/package/@redis/search) [![Docs](https://img.shields.io/badge/-documentation-dc382c)](https://redis.js.org/documentation/search/) [RediSearch](https://oss.redis.com/redisearch/) commands | -| [@redis/time-series](./packages/time-series) | [![Downloads](https://img.shields.io/npm/dm/@redis/time-series.svg)](https://www.npmjs.com/package/@redis/time-series) [![Version](https://img.shields.io/npm/v/@redis/time-series.svg)](https://www.npmjs.com/package/@redis/time-series) [![Docs](https://img.shields.io/badge/-documentation-dc382c)](https://redis.js.org/documentation/time-series/) [Redis Time-Series](https://oss.redis.com/redistimeseries/) commands | - -> :warning: In version 4.1.0 we moved our subpackages from `@node-redis` to `@redis`. If you're just using `npm install redis`, you don't need to do anything—it'll upgrade automatically. If you're using the subpackages directly, you'll need to point to the new scope (e.g. `@redis/client` instead of `@node-redis/client`). - ## Installation -Start a redis via docker: +Start a redis-server via docker (or any other method you prefer): -``` bash +```bash docker run -p 6379:6379 -it redis/redis-stack-server:latest ``` @@ -53,323 +39,21 @@ To install node-redis, simply: npm install redis ``` -> :warning: The new interface is clean and cool, but if you have an existing codebase, you'll want to read the [migration guide](./docs/v3-to-v4.md). - -Looking for a high-level library to handle object mapping? See [redis-om-node](https://github.com/redis/redis-om-node)! - -## Usage - -### Basic Example - -```typescript -import { createClient } from 'redis'; - -const client = await createClient() - .on('error', err => console.log('Redis Client Error', err)) - .connect(); - -await client.set('key', 'value'); -const value = await client.get('key'); -await client.disconnect(); -``` - -The above code connects to localhost on port 6379. To connect to a different host or port, use a connection string in the format `redis[s]://[[username][:password]@][host][:port][/db-number]`: - -```typescript -createClient({ - url: 'redis://alice:foobared@awesome.redis.server:6380' -}); -``` - -You can also use discrete parameters, UNIX sockets, and even TLS to connect. Details can be found in the [client configuration guide](./docs/client-configuration.md). - -To check if the the client is connected and ready to send commands, use `client.isReady` which returns a boolean. `client.isOpen` is also available. This returns `true` when the client's underlying socket is open, and `false` when it isn't (for example when the client is still connecting or reconnecting after a network error). - -### Redis Commands - -There is built-in support for all of the [out-of-the-box Redis commands](https://redis.io/commands). They are exposed using the raw Redis command names (`HSET`, `HGETALL`, etc.) and a friendlier camel-cased version (`hSet`, `hGetAll`, etc.): - -```typescript -// raw Redis commands -await client.HSET('key', 'field', 'value'); -await client.HGETALL('key'); - -// friendly JavaScript commands -await client.hSet('key', 'field', 'value'); -await client.hGetAll('key'); -``` - -Modifiers to commands are specified using a JavaScript object: - -```typescript -await client.set('key', 'value', { - EX: 10, - NX: true -}); -``` - -Replies will be transformed into useful data structures: - -```typescript -await client.hGetAll('key'); // { field1: 'value1', field2: 'value2' } -await client.hVals('key'); // ['value1', 'value2'] -``` - -`Buffer`s are supported as well: - -```typescript -await client.hSet('key', 'field', Buffer.from('value')); // 'OK' -await client.hGetAll( - commandOptions({ returnBuffers: true }), - 'key' -); // { field: } -``` - -### Unsupported Redis Commands - -If you want to run commands and/or use arguments that Node Redis doesn't know about (yet!) use `.sendCommand()`: - -```typescript -await client.sendCommand(['SET', 'key', 'value', 'NX']); // 'OK' - -await client.sendCommand(['HGETALL', 'key']); // ['key1', 'field1', 'key2', 'field2'] -``` - -### Transactions (Multi/Exec) - -Start a [transaction](https://redis.io/topics/transactions) by calling `.multi()`, then chaining your commands. When you're done, call `.exec()` and you'll get an array back with your results: - -```typescript -await client.set('another-key', 'another-value'); - -const [setKeyReply, otherKeyValue] = await client - .multi() - .set('key', 'value') - .get('another-key') - .exec(); // ['OK', 'another-value'] -``` - -You can also [watch](https://redis.io/topics/transactions#optimistic-locking-using-check-and-set) keys by calling `.watch()`. Your transaction will abort if any of the watched keys change. - -To dig deeper into transactions, check out the [Isolated Execution Guide](./docs/isolated-execution.md). - -### Blocking Commands - -Any command can be run on a new connection by specifying the `isolated` option. The newly created connection is closed when the command's `Promise` is fulfilled. - -This pattern works especially well for blocking commands—such as `BLPOP` and `BLMOVE`: - -```typescript -import { commandOptions } from 'redis'; - -const blPopPromise = client.blPop( - commandOptions({ isolated: true }), - 'key', - 0 -); - -await client.lPush('key', ['1', '2']); - -await blPopPromise; // '2' -``` - -To learn more about isolated execution, check out the [guide](./docs/isolated-execution.md). - -### Pub/Sub - -See the [Pub/Sub overview](./docs/pub-sub.md). - -### Scan Iterator - -[`SCAN`](https://redis.io/commands/scan) results can be looped over using [async iterators](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Symbol/asyncIterator): - -```typescript -for await (const key of client.scanIterator()) { - // use the key! - await client.get(key); -} -``` - -This works with `HSCAN`, `SSCAN`, and `ZSCAN` too: - -```typescript -for await (const { field, value } of client.hScanIterator('hash')) {} -for await (const member of client.sScanIterator('set')) {} -for await (const { score, value } of client.zScanIterator('sorted-set')) {} -``` - -You can override the default options by providing a configuration object: - -```typescript -client.scanIterator({ - TYPE: 'string', // `SCAN` only - MATCH: 'patter*', - COUNT: 100 -}); -``` - -### [Programmability](https://redis.io/docs/manual/programmability/) - -Redis provides a programming interface allowing code execution on the redis server. - -#### [Functions](https://redis.io/docs/manual/programmability/functions-intro/) - -The following example retrieves a key in redis, returning the value of the key, incremented by an integer. For example, if your key _foo_ has the value _17_ and we run `add('foo', 25)`, it returns the answer to Life, the Universe and Everything. - -```lua -#!lua name=library - -redis.register_function { - function_name = 'add', - callback = function(keys, args) return redis.call('GET', keys[1]) + args[1] end, - flags = { 'no-writes' } -} -``` - -Here is the same example, but in a format that can be pasted into the `redis-cli`. - -``` -FUNCTION LOAD "#!lua name=library\nredis.register_function{function_name=\"add\", callback=function(keys, args) return redis.call('GET', keys[1])+args[1] end, flags={\"no-writes\"}}" -``` - -Load the prior redis function on the _redis server_ before running the example below. - -```typescript -import { createClient } from 'redis'; - -const client = createClient({ - functions: { - library: { - add: { - NUMBER_OF_KEYS: 1, - transformArguments(key: string, toAdd: number): Array { - return [key, toAdd.toString()]; - }, - transformReply(reply: number): number { - return reply; - } - } - } - } -}); - -await client.connect(); - -await client.set('key', '1'); -await client.library.add('key', 2); // 3 -``` - -#### [Lua Scripts](https://redis.io/docs/manual/programmability/eval-intro/) - -The following is an end-to-end example of the prior concept. - -```typescript -import { createClient, defineScript } from 'redis'; - -const client = createClient({ - scripts: { - add: defineScript({ - NUMBER_OF_KEYS: 1, - SCRIPT: - 'return redis.call("GET", KEYS[1]) + ARGV[1];', - transformArguments(key: string, toAdd: number): Array { - return [key, toAdd.toString()]; - }, - transformReply(reply: number): number { - return reply; - } - }) - } -}); - -await client.connect(); - -await client.set('key', '1'); -await client.add('key', 2); // 3 -``` - -### Disconnecting - -There are two functions that disconnect a client from the Redis server. In most scenarios you should use `.quit()` to ensure that pending commands are sent to Redis before closing a connection. - -#### `.QUIT()`/`.quit()` - -Gracefully close a client's connection to Redis, by sending the [`QUIT`](https://redis.io/commands/quit) command to the server. Before quitting, the client executes any remaining commands in its queue, and will receive replies from Redis for each of them. - -```typescript -const [ping, get, quit] = await Promise.all([ - client.ping(), - client.get('key'), - client.quit() -]); // ['PONG', null, 'OK'] - -try { - await client.get('key'); -} catch (err) { - // ClosedClient Error -} -``` +> "redis" is the "whole in one" package that includes all the other packages. If you only need a subset of the commands, you can install the individual packages. See the list below. -#### `.disconnect()` - -Forcibly close a client's connection to Redis immediately. Calling `disconnect` will not send further pending commands to the Redis server, or wait for or parse outstanding responses. - -```typescript -await client.disconnect(); -``` - -### Auto-Pipelining - -Node Redis will automatically pipeline requests that are made during the same "tick". - -```typescript -client.set('Tm9kZSBSZWRpcw==', 'users:1'); -client.sAdd('users:1:tokens', 'Tm9kZSBSZWRpcw=='); -``` - -Of course, if you don't do something with your Promises you're certain to get [unhandled Promise exceptions](https://nodejs.org/api/process.html#process_event_unhandledrejection). To take advantage of auto-pipelining and handle your Promises, use `Promise.all()`. - -```typescript -await Promise.all([ - client.set('Tm9kZSBSZWRpcw==', 'users:1'), - client.sAdd('users:1:tokens', 'Tm9kZSBSZWRpcw==') -]); -``` - -### Clustering - -Check out the [Clustering Guide](./docs/clustering.md) when using Node Redis to connect to a Redis Cluster. - -### Events - -The Node Redis client class is an Nodejs EventEmitter and it emits an event each time the network status changes: - -| Name | When | Listener arguments | -|-------------------------|------------------------------------------------------------------------------------|------------------------------------------------------------| -| `connect` | Initiating a connection to the server | *No arguments* | -| `ready` | Client is ready to use | *No arguments* | -| `end` | Connection has been closed (via `.quit()` or `.disconnect()`) | *No arguments* | -| `error` | An error has occurred—usually a network issue such as "Socket closed unexpectedly" | `(error: Error)` | -| `reconnecting` | Client is trying to reconnect to the server | *No arguments* | -| `sharded-channel-moved` | See [here](./docs/pub-sub.md#sharded-channel-moved-event) | See [here](./docs/pub-sub.md#sharded-channel-moved-event) | - -> :warning: You **MUST** listen to `error` events. If a client doesn't have at least one `error` listener registered and an `error` occurs, that error will be thrown and the Node.js process will exit. See the [`EventEmitter` docs](https://nodejs.org/api/events.html#events_error_events) for more details. - -> The client will not emit [any other events](./docs/v3-to-v4.md#all-the-removed-events) beyond those listed above. - -## Supported Redis versions - -Node Redis is supported with the following versions of Redis: - -| Version | Supported | -|---------|--------------------| -| 7.0.z | :heavy_check_mark: | -| 6.2.z | :heavy_check_mark: | -| 6.0.z | :heavy_check_mark: | -| 5.0.z | :heavy_check_mark: | -| < 5.0 | :x: | +## Packages -> Node Redis should work with older versions of Redis, but it is not fully tested and we cannot offer support. +| Name | Description | +|------------------------------------------------|---------------------------------------------------------------------------------------------| +| [`redis`](./packages/redis) | The client with all the ["redis-stack"](https://github.com/redis-stack/redis-stack) modules | +| [`@redis/client`](./packages/client) | The base clients (i.e `RedisClient`, `RedisCluster`, etc.) | +| [`@redis/bloom`](./packages/bloom) | [Redis Bloom](https://redis.io/docs/data-types/probabilistic/) commands | +| [`@redis/graph`](./packages/graph) | [Redis Graph](https://redis.io/docs/data-types/probabilistic/) commands | +| [`@redis/json`](./packages/json) | [Redis JSON](https://redis.io/docs/data-types/json/) commands | +| [`@redis/search`](./packages/search) | [RediSearch](https://redis.io/docs/interact/search-and-query/) commands | +| [`@redis/time-series`](./packages/time-series) | [Redis Time-Series](https://redis.io/docs/data-types/timeseries/) commands | + +> Looking for a high-level library to handle object mapping? See [redis-om-node](https://github.com/redis/redis-om-node)! ## Contributing diff --git a/benchmark/lib/index.js b/benchmark/lib/index.js index 15c8a12f401..5576999bfbc 100644 --- a/benchmark/lib/index.js +++ b/benchmark/lib/index.js @@ -1,10 +1,10 @@ import yargs from 'yargs'; import { hideBin } from 'yargs/helpers'; -import { promises as fs } from 'fs'; -import { fork } from 'child_process'; -import { URL, fileURLToPath } from 'url'; -import { once } from 'events'; -import { extname } from 'path'; +import { promises as fs } from 'node:fs'; +import { fork } from 'node:child_process'; +import { URL, fileURLToPath } from 'node:url'; +import { once } from 'node:events'; +import { extname } from 'node:path'; async function getPathChoices() { const dirents = await fs.readdir(new URL('.', import.meta.url), { diff --git a/benchmark/lib/ping/ioredis-auto-pipeline.js b/benchmark/lib/ping/ioredis-auto-pipeline.js new file mode 100644 index 00000000000..ee400fe6ca9 --- /dev/null +++ b/benchmark/lib/ping/ioredis-auto-pipeline.js @@ -0,0 +1,20 @@ +import Redis from 'ioredis'; + +export default async (host) => { + const client = new Redis({ + host, + lazyConnect: true, + enableAutoPipelining: true + }); + + await client.connect(); + + return { + benchmark() { + return client.ping(); + }, + teardown() { + return client.disconnect(); + } + } +}; diff --git a/benchmark/lib/ping/local-resp2.js b/benchmark/lib/ping/local-resp2.js new file mode 100644 index 00000000000..873698a131f --- /dev/null +++ b/benchmark/lib/ping/local-resp2.js @@ -0,0 +1,21 @@ +import { createClient } from 'redis-local'; + +export default async (host) => { + const client = createClient({ + socket: { + host + }, + RESP: 2 + }); + + await client.connect(); + + return { + benchmark() { + return client.ping(); + }, + teardown() { + return client.disconnect(); + } + }; +}; diff --git a/benchmark/lib/ping/local-resp3-buffer-proxy.js b/benchmark/lib/ping/local-resp3-buffer-proxy.js new file mode 100644 index 00000000000..2ded38b21ca --- /dev/null +++ b/benchmark/lib/ping/local-resp3-buffer-proxy.js @@ -0,0 +1,23 @@ +import { createClient, RESP_TYPES } from 'redis-local'; + +export default async (host) => { + const client = createClient({ + socket: { + host + }, + RESP: 3 + }).withTypeMapping({ + [RESP_TYPES.SIMPLE_STRING]: Buffer + }); + + await client.connect(); + + return { + benchmark() { + return client.ping(); + }, + teardown() { + return client.disconnect(); + } + }; +}; diff --git a/benchmark/lib/ping/local-resp3-buffer.js b/benchmark/lib/ping/local-resp3-buffer.js new file mode 100644 index 00000000000..624a524ce06 --- /dev/null +++ b/benchmark/lib/ping/local-resp3-buffer.js @@ -0,0 +1,24 @@ +import { createClient, RESP_TYPES } from 'redis-local'; + +export default async (host) => { + const client = createClient({ + socket: { + host + }, + commandOptions: { + [RESP_TYPES.SIMPLE_STRING]: Buffer + }, + RESP: 3 + }); + + await client.connect(); + + return { + benchmark() { + return client.ping(); + }, + teardown() { + return client.disconnect(); + } + }; +}; diff --git a/benchmark/lib/ping/local-resp3-module-with-flags.js b/benchmark/lib/ping/local-resp3-module-with-flags.js new file mode 100644 index 00000000000..e58856dcb9e --- /dev/null +++ b/benchmark/lib/ping/local-resp3-module-with-flags.js @@ -0,0 +1,27 @@ +import { createClient } from 'redis-local'; +import PING from 'redis-local/dist/lib/commands/PING.js'; + +export default async (host) => { + const client = createClient({ + socket: { + host + }, + RESP: 3, + modules: { + module: { + ping: PING.default + } + } + }); + + await client.connect(); + + return { + benchmark() { + return client.withTypeMapping({}).module.ping(); + }, + teardown() { + return client.disconnect(); + } + }; +}; diff --git a/benchmark/lib/ping/local-resp3-module.js b/benchmark/lib/ping/local-resp3-module.js new file mode 100644 index 00000000000..66f6e3ec291 --- /dev/null +++ b/benchmark/lib/ping/local-resp3-module.js @@ -0,0 +1,27 @@ +import { createClient } from 'redis-local'; +import PING from 'redis-local/dist/lib/commands/PING.js'; + +export default async (host) => { + const client = createClient({ + socket: { + host + }, + RESP: 3, + modules: { + module: { + ping: PING.default + } + } + }); + + await client.connect(); + + return { + benchmark() { + return client.module.ping(); + }, + teardown() { + return client.disconnect(); + } + }; +}; diff --git a/benchmark/lib/ping/local-resp3.js b/benchmark/lib/ping/local-resp3.js new file mode 100644 index 00000000000..a4ee4f24a2a --- /dev/null +++ b/benchmark/lib/ping/local-resp3.js @@ -0,0 +1,21 @@ +import { createClient } from 'redis-local'; + +export default async (host) => { + const client = createClient({ + socket: { + host + }, + RESP: 3 + }); + + await client.connect(); + + return { + benchmark() { + return client.ping(); + }, + teardown() { + return client.disconnect(); + } + }; +}; diff --git a/benchmark/lib/ping/v3.js b/benchmark/lib/ping/v3.js index 26f269a42cf..e7e62d3e15a 100644 --- a/benchmark/lib/ping/v3.js +++ b/benchmark/lib/ping/v3.js @@ -1,6 +1,6 @@ import { createClient } from 'redis-v3'; -import { once } from 'events'; -import { promisify } from 'util'; +import { once } from 'node:events'; +import { promisify } from 'node:util'; export default async (host) => { const client = createClient({ host }), diff --git a/benchmark/lib/ping/v4.js b/benchmark/lib/ping/v4.js index 69aa3c06929..c570aa1477f 100644 --- a/benchmark/lib/ping/v4.js +++ b/benchmark/lib/ping/v4.js @@ -1,4 +1,4 @@ -import { createClient } from '@redis/client'; +import { createClient } from 'redis-v4'; export default async (host) => { const client = createClient({ diff --git a/benchmark/lib/runner.js b/benchmark/lib/runner.js index a96ff55cab0..7d81d3bb8c7 100644 --- a/benchmark/lib/runner.js +++ b/benchmark/lib/runner.js @@ -1,7 +1,7 @@ import yargs from 'yargs'; import { hideBin } from 'yargs/helpers'; -import { basename } from 'path'; -import { promises as fs } from 'fs'; +import { basename } from 'node:path'; +import { promises as fs } from 'node:fs'; import * as hdr from 'hdr-histogram-js'; hdr.initWebAssemblySync(); @@ -71,7 +71,7 @@ const benchmarkStart = process.hrtime.bigint(), histogram = await run(times), benchmarkNanoseconds = process.hrtime.bigint() - benchmarkStart, json = { - timestamp, + // timestamp, operationsPerSecond: times / Number(benchmarkNanoseconds) * 1_000_000_000, p0: histogram.getValueAtPercentile(0), p50: histogram.getValueAtPercentile(50), diff --git a/benchmark/lib/set-get-delete-string/index.js b/benchmark/lib/set-get-delete-string/index.js index 719edfc7fdf..506b222a6cb 100644 --- a/benchmark/lib/set-get-delete-string/index.js +++ b/benchmark/lib/set-get-delete-string/index.js @@ -1,6 +1,6 @@ import yargs from 'yargs'; import { hideBin } from 'yargs/helpers'; -import { randomBytes } from 'crypto'; +import { randomBytes } from 'node:crypto'; const { size } = yargs(hideBin(process.argv)) .option('size', { diff --git a/benchmark/lib/set-get-delete-string/v3.js b/benchmark/lib/set-get-delete-string/v3.js index 27ff6702a51..1e2122a0e49 100644 --- a/benchmark/lib/set-get-delete-string/v3.js +++ b/benchmark/lib/set-get-delete-string/v3.js @@ -1,6 +1,6 @@ import { createClient } from 'redis-v3'; -import { once } from 'events'; -import { promisify } from 'util'; +import { once } from 'node:events'; +import { promisify } from 'node:util'; export default async (host, { randomString }) => { const client = createClient({ host }), diff --git a/benchmark/package-lock.json b/benchmark/package-lock.json index 441c0b07b67..30114847134 100644 --- a/benchmark/package-lock.json +++ b/benchmark/package-lock.json @@ -6,11 +6,12 @@ "": { "name": "@redis/client-benchmark", "dependencies": { - "@redis/client": "../packages/client", "hdr-histogram-js": "3.0.0", - "ioredis": "5.3.2", - "redis-v3": "npm:redis@3.1.2", - "yargs": "17.7.2" + "ioredis": "5", + "redis-local": "file:../packages/client", + "redis-v3": "npm:redis@3", + "redis-v4": "npm:redis@4", + "yargs": "17.7.1" } }, "node_modules/@assemblyscript/loader": { @@ -23,10 +24,18 @@ "resolved": "https://registry.npmjs.org/@ioredis/commands/-/commands-1.2.0.tgz", "integrity": "sha512-Sx1pU8EM64o2BrqNpEO1CNLtKQwyhuXuqyfH7oGKCk+1a33d2r5saW8zNwm3j6BTExtjrv2BxTgzzkMwts6vGg==" }, + "node_modules/@redis/bloom": { + "version": "1.2.0", + "resolved": "https://registry.npmjs.org/@redis/bloom/-/bloom-1.2.0.tgz", + "integrity": "sha512-HG2DFjYKbpNmVXsa0keLHp/3leGJz1mjh09f2RLGGLQZzSHpkmZWuwJbAvo3QcRY8p80m5+ZdXZdYOSBLlp7Cg==", + "peerDependencies": { + "@redis/client": "^1.0.0" + } + }, "node_modules/@redis/client": { "version": "1.5.7", - "resolved": "file:../packages/client", - "license": "MIT", + "resolved": "https://registry.npmjs.org/@redis/client/-/client-1.5.7.tgz", + "integrity": "sha512-gaOBOuJPjK5fGtxSseaKgSvjiZXQCdLlGg9WYQst+/GRUjmXaiB5kVkeQMRtPc7Q2t93XZcJfBMSwzs/XS9UZw==", "dependencies": { "cluster-key-slot": "1.1.2", "generic-pool": "3.9.0", @@ -36,6 +45,38 @@ "node": ">=14" } }, + "node_modules/@redis/graph": { + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/@redis/graph/-/graph-1.1.0.tgz", + "integrity": "sha512-16yZWngxyXPd+MJxeSr0dqh2AIOi8j9yXKcKCwVaKDbH3HTuETpDVPcLujhFYVPtYrngSco31BUcSa9TH31Gqg==", + "peerDependencies": { + "@redis/client": "^1.0.0" + } + }, + "node_modules/@redis/json": { + "version": "1.0.4", + "resolved": "https://registry.npmjs.org/@redis/json/-/json-1.0.4.tgz", + "integrity": "sha512-LUZE2Gdrhg0Rx7AN+cZkb1e6HjoSKaeeW8rYnt89Tly13GBI5eP4CwDVr+MY8BAYfCg4/N15OUrtLoona9uSgw==", + "peerDependencies": { + "@redis/client": "^1.0.0" + } + }, + "node_modules/@redis/search": { + "version": "1.1.2", + "resolved": "https://registry.npmjs.org/@redis/search/-/search-1.1.2.tgz", + "integrity": "sha512-/cMfstG/fOh/SsE+4/BQGeuH/JJloeWuH+qJzM8dbxuWvdWibWAOAHHCZTMPhV3xIlH4/cUEIA8OV5QnYpaVoA==", + "peerDependencies": { + "@redis/client": "^1.0.0" + } + }, + "node_modules/@redis/time-series": { + "version": "1.0.4", + "resolved": "https://registry.npmjs.org/@redis/time-series/-/time-series-1.0.4.tgz", + "integrity": "sha512-ThUIgo2U/g7cCuZavucQTQzA9g9JbDDY2f64u3AbAoz/8vE2lt2U37LamDUVChhaDA3IRT9R6VvJwqnUfTJzng==", + "peerDependencies": { + "@redis/client": "^1.0.0" + } + }, "node_modules/ansi-regex": { "version": "5.0.1", "resolved": "https://registry.npmjs.org/ansi-regex/-/ansi-regex-5.0.1.tgz", @@ -244,6 +285,20 @@ "node": ">=4" } }, + "node_modules/redis-local": { + "name": "@redis/client", + "version": "1.5.6", + "resolved": "file:../packages/client", + "license": "MIT", + "dependencies": { + "cluster-key-slot": "1.1.2", + "generic-pool": "3.9.0", + "yallist": "4.0.0" + }, + "engines": { + "node": ">=14" + } + }, "node_modules/redis-parser": { "version": "3.0.0", "resolved": "https://registry.npmjs.org/redis-parser/-/redis-parser-3.0.0.tgz", @@ -282,6 +337,20 @@ "node": ">=0.10" } }, + "node_modules/redis-v4": { + "name": "redis", + "version": "4.6.6", + "resolved": "https://registry.npmjs.org/redis/-/redis-4.6.6.tgz", + "integrity": "sha512-aLs2fuBFV/VJ28oLBqYykfnhGGkFxvx0HdCEBYdJ99FFbSEMZ7c1nVKwR6ZRv+7bb7JnC0mmCzaqu8frgOYhpA==", + "dependencies": { + "@redis/bloom": "1.2.0", + "@redis/client": "1.5.7", + "@redis/graph": "1.1.0", + "@redis/json": "1.0.4", + "@redis/search": "1.1.2", + "@redis/time-series": "1.0.4" + } + }, "node_modules/require-directory": { "version": "2.1.1", "resolved": "https://registry.npmjs.org/require-directory/-/require-directory-2.1.1.tgz", @@ -349,9 +418,9 @@ "integrity": "sha512-3wdGidZyq5PB084XLES5TpOSRA3wjXAlIWMhum2kRcv/41Sn2emQ0dycQW4uZXLejwKvg6EsvbdlVL+FYEct7A==" }, "node_modules/yargs": { - "version": "17.7.2", - "resolved": "https://registry.npmjs.org/yargs/-/yargs-17.7.2.tgz", - "integrity": "sha512-7dSzzRQ++CKnNI/krKnYRV7JKKPUXMEh61soaHKg9mrWEhzFWhFnxPxGl+69cD1Ou63C13NUPCnmIcrvqCuM6w==", + "version": "17.7.1", + "resolved": "https://registry.npmjs.org/yargs/-/yargs-17.7.1.tgz", + "integrity": "sha512-cwiTb08Xuv5fqF4AovYacTFNxk62th7LKJ6BL9IGUpTJrWoU7/7WdQGTP2SjKf1dUNBGzDd28p/Yfs/GI6JrLw==", "dependencies": { "cliui": "^8.0.1", "escalade": "^3.1.1", diff --git a/benchmark/package.json b/benchmark/package.json index f46f0e00b22..73acf9d0f1c 100644 --- a/benchmark/package.json +++ b/benchmark/package.json @@ -7,10 +7,11 @@ "start": "node ." }, "dependencies": { - "@redis/client": "../packages/client", "hdr-histogram-js": "3.0.0", - "ioredis": "5.3.2", - "redis-v3": "npm:redis@3.1.2", - "yargs": "17.7.2" + "ioredis": "5", + "redis-local": "file:../packages/client", + "redis-v3": "npm:redis@3", + "redis-v4": "npm:redis@4", + "yargs": "17.7.1" } } diff --git a/docs/RESP.md b/docs/RESP.md new file mode 100644 index 00000000000..5d634831e17 --- /dev/null +++ b/docs/RESP.md @@ -0,0 +1,46 @@ +# Mapping RESP types + +RESP, which stands for **R**edis **SE**rialization **P**rotocol, is the protocol used by Redis to communicate with clients. This document shows how RESP types can be mapped to JavaScript types. You can learn more about RESP itself in the [offical documentation](https://redis.io/docs/reference/protocol-spec/). + +By default, each type is mapped to the first option in the lists below. To change this, configure a [`typeMapping`](.). + +## RESP2 + +- Integer (`:`) => `number` +- Simple String (`+`) => `string | Buffer` +- Blob String (`$`) => `string | Buffer` +- Simple Error (`-`) => `ErrorReply` +- Array (`*`) => `Array` + +> NOTE: the first type is the default type + +## RESP3 + +- Null (`_`) => `null` +- Boolean (`#`) => `boolean` +- Number (`:`) => `number | string` +- Big Number (`(`) => `BigInt | string` +- Double (`,`) => `number | string` +- Simple String (`+`) => `string | Buffer` +- Blob String (`$`) => `string | Buffer` +- Verbatim String (`=`) => `string | Buffer | VerbatimString` (TODO: `VerbatimString` typedoc link) +- Simple Error (`-`) => `ErrorReply` +- Blob Error (`!`) => `ErrorReply` +- Array (`*`) => `Array` +- Set (`~`) => `Array | Set` +- Map (`%`) => `object | Map | Array` +- Push (`>`) => `Array` => PubSub push/`'push'` event + +> NOTE: the first type is the default type + +### Map keys and Set members + +When decoding a Map to `Map | object` or a Set to `Set`, keys and members of type "Simple String" or "Blob String" will be decoded as `string`s which enables lookups by value, ignoring type mapping. If you want them as `Buffer`s, decode them as `Array`s instead. + +### Not Implemented + +These parts of RESP3 are not yet implemented in Redis itself (at the time of writing), so are not yet implemented in the Node-Redis client either: + +- [Attribute type](https://github.com/redis/redis-specifications/blob/master/protocol/RESP3.md#attribute-type) +- [Streamed strings](https://github.com/redis/redis-specifications/blob/master/protocol/RESP3.md#streamed-strings) +- [Streamed aggregated data types](https://github.com/redis/redis-specifications/blob/master/protocol/RESP3.md#streamed-aggregated-data-types) diff --git a/docs/client-configuration.md b/docs/client-configuration.md index 1854f07158a..deb68437e16 100644 --- a/docs/client-configuration.md +++ b/docs/client-configuration.md @@ -1,31 +1,32 @@ # `createClient` configuration -| Property | Default | Description | -|--------------------------|------------------------------------------|-----------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------| -| url | | `redis[s]://[[username][:password]@][host][:port][/db-number]` (see [`redis`](https://www.iana.org/assignments/uri-schemes/prov/redis) and [`rediss`](https://www.iana.org/assignments/uri-schemes/prov/rediss) IANA registration for more details) | -| socket | | Socket connection properties. Unlisted [`net.connect`](https://nodejs.org/api/net.html#socketconnectoptions-connectlistener) properties (and [`tls.connect`](https://nodejs.org/api/tls.html#tlsconnectoptions-callback)) are also supported | -| socket.port | `6379` | Redis server port | -| socket.host | `'localhost'` | Redis server hostname | -| socket.family | `0` | IP Stack version (one of `4 \| 6 \| 0`) | -| socket.path | | Path to the UNIX Socket | -| socket.connectTimeout | `5000` | Connection Timeout (in milliseconds) | -| socket.noDelay | `true` | Toggle [`Nagle's algorithm`](https://nodejs.org/api/net.html#net_socket_setnodelay_nodelay) | -| socket.keepAlive | `5000` | Toggle [`keep-alive`](https://nodejs.org/api/net.html#net_socket_setkeepalive_enable_initialdelay) functionality | -| socket.tls | | See explanation and examples [below](#TLS) | -| socket.reconnectStrategy | `retries => Math.min(retries * 50, 500)` | A function containing the [Reconnect Strategy](#reconnect-strategy) logic | -| username | | ACL username ([see ACL guide](https://redis.io/topics/acl)) | -| password | | ACL password or the old "--requirepass" password | -| name | | Client name ([see `CLIENT SETNAME`](https://redis.io/commands/client-setname)) | -| database | | Redis database number (see [`SELECT`](https://redis.io/commands/select) command) | -| modules | | Included [Redis Modules](../README.md#packages) | -| scripts | | Script definitions (see [Lua Scripts](../README.md#lua-scripts)) | -| functions | | Function definitions (see [Functions](../README.md#functions)) | -| commandsQueueMaxLength | | Maximum length of the client's internal command queue | -| disableOfflineQueue | `false` | Disables offline queuing, see [FAQ](./FAQ.md#what-happens-when-the-network-goes-down) | -| 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)) | -| isolationPoolOptions | | See the [Isolated Execution Guide](./isolated-execution.md) | -| pingInterval | | Send `PING` command at interval (in ms). Useful with ["Azure Cache for Redis"](https://learn.microsoft.com/en-us/azure/azure-cache-for-redis/cache-best-practices-connection#idle-timeout) | +| Property | Default | Description | +|------------------------------|------------------------------------------|-----------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------| +| url | | `redis[s]://[[username][:password]@][host][:port][/db-number]` (see [`redis`](https://www.iana.org/assignments/uri-schemes/prov/redis) and [`rediss`](https://www.iana.org/assignments/uri-schemes/prov/rediss) IANA registration for more details) | +| socket | | Socket connection properties. Unlisted [`net.connect`](https://nodejs.org/api/net.html#socketconnectoptions-connectlistener) properties (and [`tls.connect`](https://nodejs.org/api/tls.html#tlsconnectoptions-callback)) are also supported | +| socket.port | `6379` | Redis server port | +| socket.host | `'localhost'` | Redis server hostname | +| socket.family | `0` | IP Stack version (one of `4 \| 6 \| 0`) | +| socket.path | | Path to the UNIX Socket | +| socket.connectTimeout | `5000` | Connection timeout (in milliseconds) | +| socket.noDelay | `true` | Toggle [`Nagle's algorithm`](https://nodejs.org/api/net.html#net_socket_setnodelay_nodelay) | +| socket.keepAlive | `true` | Toggle [`keep-alive`](https://nodejs.org/api/net.html#socketsetkeepaliveenable-initialdelay) functionality | +| socket.keepAliveInitialDelay | `5000` | If set to a positive number, it sets the initial delay before the first keepalive probe is sent on an idle socket | +| socket.tls | | See explanation and examples [below](#TLS) | +| socket.reconnectStrategy | Exponential backoff with a maximum of 2000 ms; plus 0-200 ms random jitter. | A function containing the [Reconnect Strategy](#reconnect-strategy) logic | +| username | | ACL username ([see ACL guide](https://redis.io/topics/acl)) | +| password | | ACL password or the old "--requirepass" password | +| name | | Client name ([see `CLIENT SETNAME`](https://redis.io/commands/client-setname)) | +| database | | Redis database number (see [`SELECT`](https://redis.io/commands/select) command) | +| modules | | Included [Redis Modules](../README.md#packages) | +| scripts | | Script definitions (see [Lua Scripts](../README.md#lua-scripts)) | +| functions | | Function definitions (see [Functions](../README.md#functions)) | +| commandsQueueMaxLength | | Maximum length of the client's internal command queue | +| disableOfflineQueue | `false` | Disables offline queuing, see [FAQ](./FAQ.md#what-happens-when-the-network-goes-down) | +| 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)) | +| isolationPoolOptions | | See the [Isolated Execution Guide](./isolated-execution.md) | +| pingInterval | | Send `PING` command at interval (in ms). Useful with ["Azure Cache for Redis"](https://learn.microsoft.com/en-us/azure/azure-cache-for-redis/cache-best-practices-connection#idle-timeout) | ## Reconnect Strategy @@ -34,12 +35,19 @@ When the socket closes unexpectedly (without calling `.quit()`/`.disconnect()`), 2. `number` -> wait for `X` milliseconds before reconnecting. 3. `(retries: number, cause: Error) => false | number | Error` -> `number` is the same as configuring a `number` directly, `Error` is the same as `false`, but with a custom error. -By default the strategy is `Math.min(retries * 50, 500)`, but it can be overwritten like so: +By default the strategy uses exponential backoff, but it can be overwritten like so: ```javascript createClient({ socket: { - reconnectStrategy: retries => Math.min(retries * 50, 1000) + reconnectStrategy: retries => { + // Generate a random jitter between 0 – 200 ms: + const jitter = Math.floor(Math.random() * 200); + // Delay is an exponential back off, (times^2) * 50 ms, with a maximum value of 2000 ms: + const delay = Math.min(Math.pow(2, retries) * 50, 2000); + + return delay + jitter; + } } }); ``` diff --git a/docs/clustering.md b/docs/clustering.md index 28ea0e2964c..f335c259c24 100644 --- a/docs/clustering.md +++ b/docs/clustering.md @@ -4,26 +4,22 @@ Connecting to a cluster is a bit different. Create the client by specifying some (or all) of the nodes in your cluster and then use it like a regular client instance: -```typescript +```javascript import { createCluster } from 'redis'; -const cluster = createCluster({ - rootNodes: [ - { +const cluster = await createCluster({ + rootNodes: [{ url: 'redis://10.0.0.1:30001' - }, - { + }, { url: 'redis://10.0.0.2:30002' - } - ] -}); - -cluster.on('error', (err) => console.log('Redis Cluster Error', err)); - -await cluster.connect(); + }] + }) + .on('error', err => console.log('Redis Cluster Error', err)) + .connect(); await cluster.set('key', 'value'); const value = await cluster.get('key'); +await cluster.close(); ``` ## `createCluster` configuration @@ -32,7 +28,7 @@ const value = await cluster.get('key'); | Property | Default | Description | |------------------------|---------|---------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------| -| rootNodes | | An array of root nodes that are part of the cluster, which will be used to get the cluster topology. Each element in the array is a client configuration object. There is no need to specify every node in the cluster, 3 should be enough to reliably connect and obtain the cluster configuration from the server | +| rootNodes | | An array of root nodes that are part of the cluster, which will be used to get the cluster topology. Each element in the array is a client configuration object. There is no need to specify every node in the cluster: 3 should be enough to reliably connect and obtain the cluster configuration from the server | | defaults | | The default configuration values for every client in the cluster. Use this for example when specifying an ACL user to connect with | | useReplicas | `false` | When `true`, distribute load by executing readonly commands (such as `GET`, `GEOSEARCH`, etc.) across all cluster nodes. When `false`, only use master nodes | | minimizeConnections | `false` | When `true`, `.connect()` will only discover the cluster topology, without actually connecting to all the nodes. Useful for short-term or Pub/Sub-only connections. | @@ -41,9 +37,11 @@ const value = await cluster.get('key'); | modules | | Included [Redis Modules](../README.md#packages) | | scripts | | Script definitions (see [Lua Scripts](../README.md#lua-scripts)) | | functions | | Function definitions (see [Functions](../README.md#functions)) | + ## Auth with password and username Specifying the password in the URL or a root node will only affect the connection to that specific node. In case you want to set the password for all the connections being created from a cluster instance, use the `defaults` option. + ```javascript createCluster({ rootNodes: [{ @@ -107,7 +105,7 @@ createCluster({ ### Commands that operate on Redis Keys -Commands such as `GET`, `SET`, etc. are routed by the first key, for instance `MGET 1 2 3` will be routed by the key `1`. +Commands such as `GET`, `SET`, etc. are routed by the first key specified. For example `MGET 1 2 3` will be routed by the key `1`. ### [Server Commands](https://redis.io/commands#server) @@ -115,4 +113,4 @@ Admin commands such as `MEMORY STATS`, `FLUSHALL`, etc. are not attached to the ### "Forwarded Commands" -Certain commands (e.g. `PUBLISH`) are forwarded to other cluster nodes by the Redis server. This client sends these commands to a random node in order to spread the load across the cluster. +Certain commands (e.g. `PUBLISH`) are forwarded to other cluster nodes by the Redis server. The client sends these commands to a random node in order to spread the load across the cluster. diff --git a/docs/command-options.md b/docs/command-options.md new file mode 100644 index 00000000000..b246445ad74 --- /dev/null +++ b/docs/command-options.md @@ -0,0 +1,68 @@ +# Command Options + +> :warning: The command options API in v5 has breaking changes from the previous version. For more details, refer to the [v4-to-v5 guide](./v4-to-v5.md#command-options). + +Command Options are used to create "proxy clients" that change the behavior of executed commands. See the sections below for details. + +## Type Mapping + +Some [RESP types](./RESP.md) can be mapped to more than one JavaScript type. For example, "Blob String" can be mapped to `string` or `Buffer`. You can override the default type mapping using the `withTypeMapping` function: + +```javascript +await client.get('key'); // `string | null` + +const proxyClient = client.withTypeMapping({ + [TYPES.BLOB_STRING]: Buffer +}); + +await proxyClient.get('key'); // `Buffer | null` +``` + +See [RESP](./RESP.md) for a full list of types. + +## Abort Signal + +The client [batches commands](./FAQ.md#how-are-commands-batched) before sending them to Redis. Commands that haven't been written to the socket yet can be aborted using the [`AbortSignal`](https://developer.mozilla.org/en-US/docs/Web/API/AbortSignal) API: + +```javascript +const controller = new AbortController(), + client = client.withAbortSignal(controller.signal); + +try { + const promise = client.get('key'); + controller.abort(); + await promise; +} catch (err) { + // AbortError +} +``` + +## ASAP + +Commands that are executed in the "asap" mode are added to the beginning of the "to sent" queue. + +```javascript +const asapClient = client.asap(); +await asapClient.ping(); +``` + +## `withCommandOptions` + +You can set all of the above command options in a single call with the `withCommandOptions` function: + +```javascript +client.withCommandOptions({ + typeMapping: ..., + abortSignal: ..., + asap: ... +}); +``` + +If any of the above options are omitted, the default value will be used. For example, the following client would **not** be in ASAP mode: + +```javascript +client.asap().withCommandOptions({ + typeMapping: ..., + abortSignal: ... +}); +``` diff --git a/docs/isolated-execution.md b/docs/isolated-execution.md deleted file mode 100644 index 7870a4680e7..00000000000 --- a/docs/isolated-execution.md +++ /dev/null @@ -1,67 +0,0 @@ -# Isolated Execution - -Sometimes you want to run your commands on an exclusive connection. There are a few reasons to do this: - -- You're using [transactions]() and need to `WATCH` a key or keys for changes. -- You want to run a blocking command that will take over the connection, such as `BLPOP` or `BLMOVE`. -- You're using the `MONITOR` command which also takes over a connection. - -Below are several examples of how to use isolated execution. - -> NOTE: Behind the scenes we're using [`generic-pool`](https://www.npmjs.com/package/generic-pool) to provide a pool of connections that can be isolated. Go there to learn more. - -## The Simple Scenario - -This just isolates execution on a single connection. Do what you want with that connection: - -```typescript -await client.executeIsolated(async isolatedClient => { - await isolatedClient.set('key', 'value'); - await isolatedClient.get('key'); -}); -``` - -## Transactions - -Things get a little more complex with transactions. Here we are `.watch()`ing some keys. If the keys change during the transaction, a `WatchError` is thrown when `.exec()` is called: - -```typescript -try { - await client.executeIsolated(async isolatedClient => { - await isolatedClient.watch('key'); - - const multi = isolatedClient.multi() - .ping() - .get('key'); - - if (Math.random() > 0.5) { - await isolatedClient.watch('another-key'); - multi.set('another-key', await isolatedClient.get('another-key') / 2); - } - - return multi.exec(); - }); -} catch (err) { - if (err instanceof WatchError) { - // the transaction aborted - } -} - -``` - -## Blocking Commands - -For blocking commands, you can execute a tidy little one-liner: - -```typescript -await client.executeIsolated(isolatedClient => isolatedClient.blPop('key')); -``` - -Or, you can just run the command directly, and provide the `isolated` option: - -```typescript -await client.blPop( - commandOptions({ isolated: true }), - 'key' -); -``` diff --git a/docs/pool.md b/docs/pool.md new file mode 100644 index 00000000000..7121e601d73 --- /dev/null +++ b/docs/pool.md @@ -0,0 +1,74 @@ +# `RedisClientPool` + +Sometimes you want to run your commands on an exclusive connection. There are a few reasons to do this: + +- You want to run a blocking command that will take over the connection, such as `BLPOP` or `BLMOVE`. +- You're using [transactions](https://redis.io/docs/interact/transactions/) and need to `WATCH` a key or keys for changes. +- Some more... + +For those use cases you'll need to create a connection pool. + +## Creating a pool + +You can create a pool using the `createClientPool` function: + +```javascript +import { createClientPool } from 'redis'; + +const pool = await createClientPool() + .on('error', err => console.error('Redis Client Pool Error', err)); +``` + +the function accepts two arguments, the client configuration (see [here](./client-configuration.md) for more details), and the pool configuration: + +| Property | Default | Description | +|----------------|---------|--------------------------------------------------------------------------------------------------------------------------------| +| minimum | 1 | The minimum clients the pool should hold to. The pool won't close clients if the pool size is less than the minimum. | +| maximum | 100 | The maximum clients the pool will have at once. The pool won't create any more resources and queue requests in memory. | +| acquireTimeout | 3000 | The maximum time (in ms) a task can wait in the queue. The pool will reject the task with `TimeoutError` in case of a timeout. | +| cleanupDelay | 3000 | The time to wait before cleaning up unused clients. | + +You can also create a pool from a client (reusing it's configuration): +```javascript +const pool = await client.createPool() + .on('error', err => console.error('Redis Client Pool Error', err)); +``` + +## The Simple Scenario + +All the client APIs are exposed on the pool instance directly, and will execute the commands using one of the available clients. + +```javascript +await pool.sendCommand(['PING']); // 'PONG' +await client.ping(); // 'PONG' +await client.withTypeMapping({ + [RESP_TYPES.SIMPLE_STRING]: Buffer +}).ping(); // Buffer +``` + +## Transactions + +Things get a little more complex with transactions. Here we are `.watch()`ing some keys. If the keys change during the transaction, a `WatchError` is thrown when `.exec()` is called: + +```javascript +try { + await pool.execute(async client => { + await client.watch('key'); + + const multi = client.multi() + .ping() + .get('key'); + + if (Math.random() > 0.5) { + await client.watch('another-key'); + multi.set('another-key', await client.get('another-key') / 2); + } + + return multi.exec(); + }); +} catch (err) { + if (err instanceof WatchError) { + // the transaction aborted + } +} +``` diff --git a/docs/programmability.md b/docs/programmability.md new file mode 100644 index 00000000000..f6ae42033c6 --- /dev/null +++ b/docs/programmability.md @@ -0,0 +1,76 @@ +# [Programmability](https://redis.io/docs/manual/programmability/) + +Redis provides a programming interface allowing code execution on the redis server. + +## [Functions](https://redis.io/docs/manual/programmability/functions-intro/) + +The following example retrieves a key in redis, returning the value of the key, incremented by an integer. For example, if your key _foo_ has the value _17_ and we run `add('foo', 25)`, it returns the answer to Life, the Universe and Everything. + +```lua +#!lua name=library + +redis.register_function { + function_name = 'add', + callback = function(keys, args) return redis.call('GET', keys[1]) + args[1] end, + flags = { 'no-writes' } +} +``` + +Here is the same example, but in a format that can be pasted into the `redis-cli`. + +``` +FUNCTION LOAD "#!lua name=library\nredis.register_function{function_name='add', callback=function(keys, args) return redis.call('GET', keys[1])+args[1] end, flags={'no-writes'}}" +``` + +Load the prior redis function on the _redis server_ before running the example below. + +```typescript +import { createClient } from 'redis'; + +const client = createClient({ + functions: { + library: { + add: { + NUMBER_OF_KEYS: 1, + FIRST_KEY_INDEX: 1, + transformArguments(key: string, toAdd: number): Array { + return [key, toAdd.toString()]; + }, + transformReply: undefined as unknown as () => NumberReply + } + } + } +}); + +await client.connect(); + +await client.set('key', '1'); +await client.library.add('key', 2); // 3 +``` + +## [Lua Scripts](https://redis.io/docs/manual/programmability/eval-intro/) + +The following is an end-to-end example of the prior concept. + +```typescript +import { createClient, defineScript, NumberReply } from 'redis'; + +const client = createClient({ + scripts: { + add: defineScript({ + SCRIPT: 'return redis.call("GET", KEYS[1]) + ARGV[1];', + NUMBER_OF_KEYS: 1, + FIRST_KEY_INDEX: 1, + transformArguments(key: string, toAdd: number): Array { + return [key, toAdd.toString()]; + }, + transformReply: undefined as unknown as () => NumberReply + }) + } +}); + +await client.connect(); + +await client.set('key', '1'); +await client.add('key', 2); // 3 +``` diff --git a/docs/pub-sub.md b/docs/pub-sub.md index b319925569d..7bbb0733c18 100644 --- a/docs/pub-sub.md +++ b/docs/pub-sub.md @@ -1,18 +1,20 @@ # Pub/Sub -The Pub/Sub API is implemented by `RedisClient` and `RedisCluster`. +The Pub/Sub API is implemented by `RedisClient`, `RedisCluster`, and `RedisSentinel`. ## Pub/Sub with `RedisClient` -Pub/Sub requires a dedicated stand-alone client. You can easily get one by `.duplicate()`ing an existing `RedisClient`: +### RESP2 -```typescript +Using RESP2, Pub/Sub "takes over" the connection (a client with subscriptions will not execute commands), therefore it requires a dedicated connection. You can easily get one by `.duplicate()`ing an existing `RedisClient`: + +```javascript const subscriber = client.duplicate(); subscriber.on('error', err => console.error(err)); await subscriber.connect(); ``` -When working with a `RedisCluster`, this is handled automatically for you. +> When working with either `RedisCluster` or `RedisSentinel`, this is handled automatically for you. ### `sharded-channel-moved` event @@ -29,6 +31,8 @@ The event listener signature is as follows: ) ``` +> When working with `RedisCluster`, this is handled automatically for you. + ## Subscribing ```javascript @@ -39,7 +43,7 @@ await client.pSubscribe('channe*', listener); await client.sSubscribe('channel', listener); ``` -> ⚠️ Subscribing to the same channel more than once will create multiple listeners which will each be called when a message is recieved. +> ⚠️ Subscribing to the same channel more than once will create multiple listeners, each of which will be called when a message is received. ## Publishing diff --git a/docs/scan-iterators.md b/docs/scan-iterators.md new file mode 100644 index 00000000000..47c4d6c0567 --- /dev/null +++ b/docs/scan-iterators.md @@ -0,0 +1,30 @@ +# Scan Iterators + +> :warning: The scan iterators API in v5 has breaking changes from the previous version. For more details, refer to the [v4-to-v5 guide](./v4-to-v5.md#scan-iterators). + +[`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): + +```javascript +for await (const keys of client.scanIterator()) { + const values = await client.mGet(keys); +} +``` + +This works with `HSCAN`, `SSCAN`, and `ZSCAN` too: + +```javascript +for await (const entries of client.hScanIterator('hash')) {} +for await (const members of client.sScanIterator('set')) {} +for await (const membersWithScores of client.zScanIterator('sorted-set')) {} +``` + +You can override the default options by providing a configuration object: + +```javascript +client.scanIterator({ + cursor: '0', // optional, defaults to '0' + TYPE: 'string', // `SCAN` only + MATCH: 'patter*', + COUNT: 100 +}); +``` diff --git a/docs/sentinel.md b/docs/sentinel.md new file mode 100644 index 00000000000..80e79c3f88c --- /dev/null +++ b/docs/sentinel.md @@ -0,0 +1,100 @@ +# Redis Sentinel + +The [Redis Sentinel](https://redis.io/docs/management/sentinel/) object of node-redis provides a high level object that provides access to a high availability redis installation managed by Redis Sentinel to provide enumeration of master and replica nodes belonging to an installation as well as reconfigure itself on demand for failover and topology changes. + +## Basic Example + +```javascript +import { createSentinel } from 'redis'; + +const sentinel = await createSentinel({ + name: 'sentinel-db', + sentinelRootNodes: [{ + host: 'example', + port: 1234 + }] + }) + .on('error', err => console.error('Redis Sentinel Error', err)); + .connect(); + +await sentinel.set('key', 'value'); +const value = await sentinel.get('key'); +await sentinel.close(); +``` + +In the above example, we configure the sentinel object to fetch the configuration for the database Redis Sentinel is monitoring as "sentinel-db" with one of the sentinels being located at `example:1234`, then using it like a regular Redis client. + +## `createSentinel` configuration + +| Property | Default | Description | +|-----------------------|---------|-----------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------| +| name | | The sentinel identifier for a particular database cluster | +| sentinelRootNodes | | An array of root nodes that are part of the sentinel cluster, which will be used to get the topology. Each element in the array is a client configuration object. There is no need to specify every node in the cluster: 3 should be enough to reliably connect and obtain the sentinel configuration from the server | +| maxCommandRediscovers | `16` | The maximum number of times a command will retry due to topology changes. | +| nodeClientOptions | | The configuration values for every node in the cluster. Use this for example when specifying an ACL user to connect with | +| sentinelClientOptions | | The configuration values for every sentinel in the cluster. Use this for example when specifying an ACL user to connect with | +| masterPoolSize | `1` | The number of clients connected to the master node | +| replicaPoolSize | `0` | The number of clients connected to each replica node. When greater than 0, the client will distribute the load by executing read-only commands (such as `GET`, `GEOSEARCH`, etc.) across all the cluster nodes. | +| reserveClient | `false` | When `true`, one client will be reserved for the sentinel object. When `false`, the sentinel object will wait for the first available client from the pool. | +## PubSub + +It supports PubSub via the normal mechanisms, including migrating the listeners if the node they are connected to goes down. + +```javascript +await sentinel.subscribe('channel', message => { + // ... +}); +await sentinel.unsubscribe('channel'); +``` + +see [the PubSub guide](./pub-sub.md) for more details. + +## Sentinel as a pool + +The sentinel object provides the ability to manage a pool of clients for the master node: + +```javascript +createSentinel({ + // ... + masterPoolSize: 10 +}); +``` + +In addition, it also provides the ability have a pool of clients connected to the replica nodes, and to direct all read-only commands to them: + +```javascript +createSentinel({ + // ... + replicaPoolSize: 10 +}); +``` + +## Master client lease + +Sometimes multiple commands needs to run on an exclusive client (for example, using `WATCH/MULTI/EXEC`). + +There are 2 ways to get a client lease: + +`.use()` +```javascript +const result = await sentinel.use(async client => { + await client.watch('key'); + return client.multi() + .get('key') + .exec(); +}); +``` + +`.getMasterClientLease()` +```javascript +const clientLease = await sentinel.getMasterClientLease(); + +try { + await clientLease.watch('key'); + const resp = await clientLease.multi() + .get('key') + .exec(); +} finally { + clientLease.release(); +} +``` diff --git a/docs/todo.md b/docs/todo.md new file mode 100644 index 00000000000..49163444986 --- /dev/null +++ b/docs/todo.md @@ -0,0 +1,6 @@ +- "Isolation Pool" -> pool +- Cluster request response policies (either implement, or block "server" commands in cluster) + +Docs: +- [Command Options](./command-options.md) +- [RESP](./RESP.md) diff --git a/docs/transactions.md b/docs/transactions.md new file mode 100644 index 00000000000..6331fef4be5 --- /dev/null +++ b/docs/transactions.md @@ -0,0 +1,53 @@ +# [Transactions](https://redis.io/docs/interact/transactions/) ([`MULTI`](https://redis.io/commands/multi/)/[`EXEC`](https://redis.io/commands/exec/)) + +Start a [transaction](https://redis.io/docs/interact/transactions/) by calling `.multi()`, then chaining your commands. When you're done, call `.exec()` and you'll get an array back with your results: + +```javascript +const [setReply, getReply] = await client.multi() + .set('key', 'value') + .get('another-key') + .exec(); +``` + +## `exec<'typed'>()`/`execTyped()` + +A transaction invoked with `.exec<'typed'>`/`execTyped()` will return types appropriate to the commands in the transaction: + +```javascript +const multi = client.multi().ping(); +await multi.exec(); // Array +await multi.exec<'typed'>(); // [string] +await multi.execTyped(); // [string] +``` + +> :warning: this only works when all the commands are invoked in a single "call chain" + +## [`WATCH`](https://redis.io/commands/watch/) + +You can also [watch](https://redis.io/docs/interact/transactions/#optimistic-locking-using-check-and-set) keys by calling `.watch()`. Your transaction will abort if any of the watched keys change or if the client reconnected between the `watch` and `exec` calls. + +The `WATCH` state is stored on the connection (by the server). In case you need to run multiple `WATCH` & `MULTI` in parallel you'll need to use a [pool](./pool.md). + +## `execAsPipeline` + +`execAsPipeline` will execute the commands without "wrapping" it with `MULTI` & `EXEC` (and lose the transactional semantics). + +```javascript +await client.multi() + .get('a') + .get('b') + .execAsPipeline(); +``` + +the diffrence between the above pipeline and `Promise.all`: + +```javascript +await Promise.all([ + client.get('a'), + client.get('b') +]); +``` + +is that if the socket disconnects during the pipeline, any unwritten commands will be discarded. i.e. if the socket disconnects after `GET a` is written to the socket, but before `GET b` is: +- using `Promise.all` - the client will try to execute `GET b` when the socket reconnects +- using `execAsPipeline` - `GET b` promise will be rejected as well diff --git a/docs/v4-to-v5.md b/docs/v4-to-v5.md new file mode 100644 index 00000000000..95c2230ce23 --- /dev/null +++ b/docs/v4-to-v5.md @@ -0,0 +1,240 @@ +# v4 to v5 migration guide + +## Client Configuration + +### Keep Alive + +To better align with Node.js build-in [`net`](https://nodejs.org/api/net.html) and [`tls`](https://nodejs.org/api/tls.html) modules, the `keepAlive` option has been split into 2 options: `keepAlive` (`boolean`) and `keepAliveInitialDelay` (`number`). The defaults remain `true` and `5000`. + +### Legacy Mode + +In the previous version, you could access "legacy" mode by creating a client and passing in `{ legacyMode: true }`. Now, you can create one off of an existing client by calling the `.legacy()` function. This allows easier access to both APIs and enables better TypeScript support. + +```javascript +// use `client` for the current API +const client = createClient(); +await client.set('key', 'value'); + +// use `legacyClient` for the "legacy" API +const legacyClient = client.legacy(); +legacyClient.set('key', 'value', (err, reply) => { + // ... +}); +``` + +## Command Options + +In v4, command options are passed as a first optional argument: + +```javascript +await client.get('key'); // `string | null` +await client.get(client.commandOptions({ returnBuffers: true }), 'key'); // `Buffer | null` +``` + +This has a couple of flaws: +1. The argument types are checked in runtime, which is a performance hit. +2. Code suggestions are less readable/usable, due to "function overloading". +3. Overall, "user code" is not as readable as it could be. + +### The new API for v5 + +With the new API, instead of passing the options directly to the commands we use a "proxy client" to store them: + +```javascript +await client.get('key'); // `string | null` + +const proxyClient = client.withCommandOptions({ + typeMapping: { + [TYPES.BLOB_STRING]: Buffer + } +}); + +await proxyClient.get('key'); // `Buffer | null` +``` + +for more information, see the [Command Options guide](./command-options.md). + +## Quit VS Disconnect + +The `QUIT` command has been deprecated in Redis 7.2 and should now also be considered deprecated in Node-Redis. Instead of sending a `QUIT` command to the server, the client can simply close the network connection. + +`client.QUIT/quit()` is replaced by `client.close()`. and, to avoid confusion, `client.disconnect()` has been renamed to `client.destroy()`. + +## Scan Iterators + +Iterator commands like `SCAN`, `HSCAN`, `SSCAN`, and `ZSCAN` return collections of elements (depending on the data type). However, v4 iterators loop over these collections and yield individual items: + +```javascript +for await (const key of client.scanIterator()) { + console.log(key, await client.get(key)); +} +``` + +This mismatch can be awkward and makes "multi-key" commands like `MGET`, `UNLINK`, etc. pointless. So, in v5 the iterators now yield a collection instead of an element: + +```javascript +for await (const keys of client.scanIterator()) { + // we can now meaningfully utilize "multi-key" commands + console.log(keys, await client.mGet(keys)); +} +``` + +for more information, see the [Scan Iterators guide](./scan-iterators.md). + +## Isolation Pool + +In v4, `RedisClient` had the ability to create a pool of connections using an "Isolation Pool" on top of the "main" connection. However, there was no way to use the pool without a "main" connection: +```javascript +const client = await createClient() + .on('error', err => console.error(err)) + .connect(); + +await client.ping( + client.commandOptions({ isolated: true }) +); +``` + +In v5 we've extracted this pool logic into its own class—`RedisClientPool`: + +```javascript +const pool = await createClientPool() + .on('error', err => console.error(err)) + .connect(); + +await pool.ping(); +``` + +See the [pool guide](./pool.md) for more information. + +## Cluster `MULTI` + +In v4, `cluster.multi()` did not support executing commands on replicas, even if they were readonly. + +```javascript +// this might execute on a replica, depending on configuration +await cluster.sendCommand('key', true, ['GET', 'key']); + +// this always executes on a master +await cluster.multi() + .addCommand('key', ['GET', 'key']) + .exec(); +``` + +To support executing commands on replicas, `cluster.multi().addCommand` now requires `isReadonly` as the second argument, which matches the signature of `cluster.sendCommand`: + +```javascript +await cluster.multi() + .addCommand('key', true, ['GET', 'key']) + .exec(); +``` + +## `MULTI.execAsPipeline()` + +```javascript +await client.multi() + .set('a', 'a') + .set('b', 'b') + .execAsPipeline(); +``` + +In older versions, if the socket disconnects during the pipeline execution, i.e. after writing `SET a a` and before `SET b b`, the returned promise is rejected, but `SET b b` will still be executed on the server. + +In v5, any unwritten commands (in the same pipeline) will be discarded. + +## Commands + +### Redis + +- `ACL GETUSER`: `selectors` +- `COPY`: `destinationDb` -> `DB`, `replace` -> `REPLACE`, `boolean` -> `number` [^boolean-to-number] +- `CLIENT KILL`: `enum ClientKillFilters` -> `const CLIENT_KILL_FILTERS` [^enum-to-constants] +- `CLUSTER FAILOVER`: `enum FailoverModes` -> `const FAILOVER_MODES` [^enum-to-constants] +- `CLIENT TRACKINGINFO`: `flags` in RESP2 - `Set` -> `Array` (to match RESP3 default type mapping) +- `CLUSTER INFO`: +- `CLUSTER SETSLOT`: `ClusterSlotStates` -> `CLUSTER_SLOT_STATES` [^enum-to-constants] +- `CLUSTER RESET`: the second argument is `{ mode: string; }` instead of `string` [^future-proofing] +- `CLUSTER FAILOVER`: `enum FailoverModes` -> `const FAILOVER_MODES` [^enum-to-constants], the second argument is `{ mode: string; }` instead of `string` [^future-proofing] +- `CLUSTER LINKS`: `createTime` -> `create-time`, `sendBufferAllocated` -> `send-buffer-allocated`, `sendBufferUsed` -> `send-buffer-used` [^map-keys] +- `CLUSTER NODES`, `CLUSTER REPLICAS`, `CLUSTER INFO`: returning the raw `VerbatimStringReply` +- `EXPIRE`: `boolean` -> `number` [^boolean-to-number] +- `EXPIREAT`: `boolean` -> `number` [^boolean-to-number] +- `HSCAN`: `tuples` has been renamed to `entries` +- `HEXISTS`: `boolean` -> `number` [^boolean-to-number] +- `HRANDFIELD_COUNT_WITHVALUES`: `Record` -> `Array<{ field: BlobString; value: BlobString; }>` (it can return duplicates). +- `HSETNX`: `boolean` -> `number` [^boolean-to-number] +- `INFO`: +- `LCS IDX`: `length` has been changed to `len`, `matches` has been changed from `Array<{ key1: RangeReply; key2: RangeReply; }>` to `Array<[key1: RangeReply, key2: RangeReply]>` + + +- `ZINTER`: instead of `client.ZINTER('key', { WEIGHTS: [1] })` use `client.ZINTER({ key: 'key', weight: 1 }])` +- `ZINTER_WITHSCORES`: instead of `client.ZINTER_WITHSCORES('key', { WEIGHTS: [1] })` use `client.ZINTER_WITHSCORES({ key: 'key', weight: 1 }])` +- `ZUNION`: instead of `client.ZUNION('key', { WEIGHTS: [1] })` use `client.ZUNION({ key: 'key', weight: 1 }])` +- `ZUNION_WITHSCORES`: instead of `client.ZUNION_WITHSCORES('key', { WEIGHTS: [1] })` use `client.ZUNION_WITHSCORES({ key: 'key', weight: 1 }])` +- `ZMPOP`: `{ elements: Array<{ member: string; score: number; }>; }` -> `{ members: Array<{ value: string; score: number; }>; }` to match other sorted set commands (e.g. `ZRANGE`, `ZSCAN`) + +- `MOVE`: `boolean` -> `number` [^boolean-to-number] +- `PEXPIRE`: `boolean` -> `number` [^boolean-to-number] +- `PEXPIREAT`: `boolean` -> `number` [^boolean-to-number] +- `PFADD`: `boolean` -> `number` [^boolean-to-number] + +- `RENAMENX`: `boolean` -> `number` [^boolean-to-number] +- `SETNX`: `boolean` -> `number` [^boolean-to-number] +- `SCAN`, `HSCAN`, `SSCAN`, and `ZSCAN`: `reply.cursor` will not be converted to number to avoid issues when the number is bigger than `Number.MAX_SAFE_INTEGER`. See [here](https://github.com/redis/node-redis/issues/2561). +- `SCRIPT EXISTS`: `Array` -> `Array` [^boolean-to-number] +- `SISMEMBER`: `boolean` -> `number` [^boolean-to-number] +- `SMISMEMBER`: `Array` -> `Array` [^boolean-to-number] +- `SMOVE`: `boolean` -> `number` [^boolean-to-number] + +- `GEOSEARCH_WITH`/`GEORADIUS_WITH`: `GeoReplyWith` -> `GEO_REPLY_WITH` [^enum-to-constants] +- `GEORADIUSSTORE` -> `GEORADIUS_STORE` +- `GEORADIUSBYMEMBERSTORE` -> `GEORADIUSBYMEMBER_STORE` +- `XACK`: `boolean` -> `number` [^boolean-to-number] +- `XADD`: the `INCR` option has been removed, use `XADD_INCR` instead +- `LASTSAVE`: `Date` -> `number` (unix timestamp) +- `HELLO`: `protover` moved from the options object to it's own argument, `auth` -> `AUTH`, `clientName` -> `SETNAME` +- `MODULE LIST`: `version` -> `ver` [^map-keys] +- `MEMORY STATS`: [^map-keys] +- `FUNCTION RESTORE`: the second argument is `{ mode: string; }` instead of `string` [^future-proofing] +- `FUNCTION STATS`: `runningScript` -> `running_script`, `durationMs` -> `duration_ms`, `librariesCount` -> `libraries_count`, `functionsCount` -> `functions_count` [^map-keys] + +- `TIME`: `Date` -> `[unixTimestamp: string, microseconds: string]` + +- `XGROUP_CREATECONSUMER`: [^boolean-to-number] +- `XGROUP_DESTROY`: [^boolean-to-number] +- `XINFO GROUPS`: `lastDeliveredId` -> `last-delivered-id` [^map-keys] +- `XINFO STREAM`: `radixTreeKeys` -> `radix-tree-keys`, `radixTreeNodes` -> `radix-tree-nodes`, `lastGeneratedId` -> `last-generated-id`, `maxDeletedEntryId` -> `max-deleted-entry-id`, `entriesAdded` -> `entries-added`, `recordedFirstEntryId` -> `recorded-first-entry-id`, `firstEntry` -> `first-entry`, `lastEntry` -> `last-entry` +- `XAUTOCLAIM`, `XCLAIM`, `XRANGE`, `XREVRANGE`: `Array<{ name: string; messages: Array<{ id: string; message: Record }>; }>` -> `Record }>>` + +- `COMMAND LIST`: `enum FilterBy` -> `const COMMAND_LIST_FILTER_BY` [^enum-to-constants], the filter argument has been moved from a "top level argument" into ` { FILTERBY: { type: ; value: } }` + +### Bloom + +- `TOPK.QUERY`: `Array` -> `Array` + +### Graph + +- `GRAPH.SLOWLOG`: `timestamp` has been changed from `Date` to `number` + +### JSON + +- `JSON.ARRINDEX`: `start` and `end` arguments moved to `{ range: { start: number; end: number; }; }` [^future-proofing] +- `JSON.ARRPOP`: `path` and `index` arguments moved to `{ path: string; index: number; }` [^future-proofing] +- `JSON.ARRLEN`, `JSON.CLEAR`, `JSON.DEBUG MEMORY`, `JSON.DEL`, `JSON.FORGET`, `JSON.OBJKEYS`, `JSON.OBJLEN`, `JSON.STRAPPEND`, `JSON.STRLEN`, `JSON.TYPE`: `path` argument moved to `{ path: string; }` [^future-proofing] + +### Search + +- `FT.SUGDEL`: [^boolean-to-number] +- `FT.CURSOR READ`: `cursor` type changed from `number` to `string` (in and out) to avoid issues when the number is bigger than `Number.MAX_SAFE_INTEGER`. See [here](https://github.com/redis/node-redis/issues/2561). + +### Time Series + +- `TS.ADD`: `boolean` -> `number` [^boolean-to-number] +- `TS.[M][REV]RANGE`: `enum TimeSeriesBucketTimestamp` -> `const TIME_SERIES_BUCKET_TIMESTAMP` [^enum-to-constants], `enum TimeSeriesReducers` -> `const TIME_SERIES_REDUCERS` [^enum-to-constants], the `ALIGN` argument has been moved into `AGGREGRATION` +- `TS.SYNUPDATE`: `Array>` -> `Record>` +- `TS.M[REV]RANGE[_WITHLABELS]`, `TS.MGET[_WITHLABELS]`: TODO + +[^enum-to-constants]: TODO + +[^map-keys]: To avoid unnecessary transformations and confusion, map keys will not be transformed to "js friendly" names (i.e. `number-of-keys` will not be renamed to `numberOfKeys`). See [here](https://github.com/redis/node-redis/discussions/2506). + +[^future-proofing]: TODO diff --git a/docs/v5.md b/docs/v5.md new file mode 100644 index 00000000000..4a1bd817b9b --- /dev/null +++ b/docs/v5.md @@ -0,0 +1,38 @@ +# RESP3 Support + +TODO + +```javascript +const client = createClient({ + RESP: 3 +}); +``` + +```javascript +// by default +await client.hGetAll('key'); // Record + +await client.withTypeMapping({ + [TYPES.MAP]: Map +}).hGetAll('key'); // Map + +await client.withTypeMapping({ + [TYPES.MAP]: Map, + [TYPES.BLOB_STRING]: Buffer +}).hGetAll('key'); // Map +``` + +# Sentinel Support + +[TODO](./sentinel.md) + +# `multi.exec<'typed'>` / `multi.execTyped` + +We have introduced the ability to perform a "typed" `MULTI`/`EXEC` transaction. Rather than returning `Array`, a transaction invoked with `.exec<'typed'>` will return types appropriate to the commands in the transaction where possible: + +```javascript +const multi = client.multi().ping(); +await multi.exec(); // Array +await multi.exec<'typed'>(); // [string] +await multi.execTyped(); // [string] +``` diff --git a/examples/README.md b/examples/README.md index 4e7655a3519..1080f424da1 100644 --- a/examples/README.md +++ b/examples/README.md @@ -91,5 +91,5 @@ await client.connect(); // Add your example code here... -await client.quit(); +client.destroy(); ``` diff --git a/examples/blocking-list-pop.js b/examples/blocking-list-pop.js index 099c73a2a96..ec9bec4d639 100644 --- a/examples/blocking-list-pop.js +++ b/examples/blocking-list-pop.js @@ -27,4 +27,4 @@ console.log('blpopPromise resolved'); // {"key":"keyName","element":"value"} console.log(`listItem is '${JSON.stringify(listItem)}'`); -await client.quit(); +client.destroy(); diff --git a/examples/bloom-filter.js b/examples/bloom-filter.js index cf5f1940b3e..a133b0274f2 100644 --- a/examples/bloom-filter.js +++ b/examples/bloom-filter.js @@ -77,4 +77,4 @@ const info = await client.bf.info('mybloom'); // } console.log(info); -await client.quit(); +client.destroy(); diff --git a/examples/check-connection-status.js b/examples/check-connection-status.js index 0ccf8ff5e21..ae3c863fb14 100644 --- a/examples/check-connection-status.js +++ b/examples/check-connection-status.js @@ -25,4 +25,4 @@ console.log('Afer connectPromise has resolved...'); // isReady will return True here, client is ready to use. console.log(`client.isOpen: ${client.isOpen}, client.isReady: ${client.isReady}`); -await client.quit(); +client.destroy(); diff --git a/examples/command-with-modifiers.js b/examples/command-with-modifiers.js index 974f78dc5d8..31106b17e45 100644 --- a/examples/command-with-modifiers.js +++ b/examples/command-with-modifiers.js @@ -1,25 +1,31 @@ // Define a custom script that shows example of SET command // with several modifiers. -import { createClient } from 'redis'; +import { createClient } from '../packages/client'; const client = createClient(); await client.connect(); await client.del('mykey'); -let result = await client.set('mykey', 'myvalue', { - EX: 60, - GET: true -}); - -console.log(result); //null - -result = await client.set('mykey', 'newvalue', { - EX: 60, - GET: true -}); - -console.log(result); //myvalue - -await client.quit(); +console.log( + await client.set('mykey', 'myvalue', { + expiration: { + type: 'EX', + value: 60 + }, + GET: true + }) +); // null + +console.log( + await client.set('mykey', 'newvalue', { + expiration: { + type: 'EX', + value: 60 + }, + GET: true + }) +); // 'myvalue' + +await client.close(); diff --git a/examples/connect-as-acl-user.js b/examples/connect-as-acl-user.js index df46aa1e288..bc3069b5bbc 100644 --- a/examples/connect-as-acl-user.js +++ b/examples/connect-as-acl-user.js @@ -23,4 +23,4 @@ try { console.log(`GET command failed: ${e.message}`); } -await client.quit(); +client.destroy(); diff --git a/examples/count-min-sketch.js b/examples/count-min-sketch.js index f88a148986f..ffbe13a7c27 100644 --- a/examples/count-min-sketch.js +++ b/examples/count-min-sketch.js @@ -77,4 +77,4 @@ console.log('Count-Min Sketch info:'); // } console.log(info); -await client.quit(); +client.destroy(); diff --git a/examples/cuckoo-filter.js b/examples/cuckoo-filter.js index 87976f3fefb..6ab58fbfa5c 100644 --- a/examples/cuckoo-filter.js +++ b/examples/cuckoo-filter.js @@ -76,4 +76,4 @@ const info = await client.cf.info('mycuckoo'); // } console.log(info); -await client.quit(); +client.destroy(); diff --git a/examples/dump-and-restore.js b/examples/dump-and-restore.js index 081e44f9f9a..c2ee7f1e199 100644 --- a/examples/dump-and-restore.js +++ b/examples/dump-and-restore.js @@ -1,22 +1,26 @@ // This example demonstrates the use of the DUMP and RESTORE commands -import { commandOptions, createClient } from 'redis'; +import { createClient, RESP_TYPES } from 'redis'; -const client = createClient(); -await client.connect(); +const client = await createClient({ + commandOptions: { + typeMapping: { + [RESP_TYPES.BLOB_STRING]: Buffer + } + } +}).on('error', err => { + console.log('Redis Client Error', err); +}).connect(); // DUMP a specific key into a local variable -const dump = await client.dump( - commandOptions({ returnBuffers: true }), - 'source' -); +const dump = await client.dump('source'); // RESTORE into a new key await client.restore('destination', 0, dump); // RESTORE and REPLACE an existing key await client.restore('destination', 0, dump, { - REPLACE: true + REPLACE: true }); -await client.quit(); +await client.close(); diff --git a/examples/get-server-time.js b/examples/get-server-time.js index 967859f0136..0e32c1296af 100644 --- a/examples/get-server-time.js +++ b/examples/get-server-time.js @@ -9,4 +9,4 @@ const serverTime = await client.time(); // 2022-02-25T12:57:40.000Z { microseconds: 351346 } console.log(serverTime); -await client.quit(); +client.destroy(); diff --git a/examples/hyperloglog.js b/examples/hyperloglog.js index 4ac9b575f96..027112a08bf 100644 --- a/examples/hyperloglog.js +++ b/examples/hyperloglog.js @@ -48,4 +48,4 @@ try { console.error(e); } -await client.quit(); +client.destroy(); diff --git a/examples/lua-multi-incr.js b/examples/lua-multi-incr.js index 8eb1092c295..5cf39142006 100644 --- a/examples/lua-multi-incr.js +++ b/examples/lua-multi-incr.js @@ -24,4 +24,4 @@ await client.connect(); await client.set('mykey', '5'); console.log(await client.mincr('mykey', 'myotherkey', 10)); // [ 15, 10 ] -await client.quit(); +client.destroy(); diff --git a/examples/managing-json.js b/examples/managing-json.js index 81949d5c222..a28a0ee5106 100644 --- a/examples/managing-json.js +++ b/examples/managing-json.js @@ -73,4 +73,4 @@ const numPets = await client.json.arrLen('noderedis:jsondata', '$.pets'); // We now have 4 pets. console.log(`We now have ${numPets} pets.`); -await client.quit(); +client.destroy(); diff --git a/examples/package.json b/examples/package.json index 65ba1442f7e..91120774d94 100644 --- a/examples/package.json +++ b/examples/package.json @@ -6,7 +6,7 @@ "private": true, "type": "module", "dependencies": { - "redis": "../" + "redis": "../packages/client" } } diff --git a/examples/search-hashes.js b/examples/search-hashes.js index 2f8b5fbf7b6..f3aca6b8aed 100644 --- a/examples/search-hashes.js +++ b/examples/search-hashes.js @@ -85,4 +85,4 @@ for (const doc of results.documents) { console.log(`${doc.id}: ${doc.value.name}, ${doc.value.age} years old.`); } -await client.quit(); +client.destroy(); diff --git a/examples/search-json.js b/examples/search-json.js index 6481889ecfd..bff5b2cb362 100644 --- a/examples/search-json.js +++ b/examples/search-json.js @@ -145,4 +145,4 @@ console.log( // ] // } -await client.quit(); +client.destroy(); diff --git a/examples/search-knn.js b/examples/search-knn.js index ea20f52e3fe..49bd00d86df 100644 --- a/examples/search-knn.js +++ b/examples/search-knn.js @@ -88,4 +88,4 @@ console.log(JSON.stringify(results, null, 2)); // } // ] // } -await client.quit(); +client.destroy(); diff --git a/examples/set-scan.js b/examples/set-scan.js index 73f6c443444..0e379224d9d 100644 --- a/examples/set-scan.js +++ b/examples/set-scan.js @@ -12,4 +12,4 @@ for await (const member of client.sScanIterator(setName)) { console.log(member); } -await client.quit(); +client.destroy(); diff --git a/examples/sorted-set.js b/examples/sorted-set.js index eb1f82867c1..3fcc24b8442 100644 --- a/examples/sorted-set.js +++ b/examples/sorted-set.js @@ -28,4 +28,4 @@ for await (const memberWithScore of client.zScanIterator('mysortedset')) { console.log(memberWithScore); } -await client.quit(); +client.destroy(); diff --git a/examples/stream-producer.js b/examples/stream-producer.js index f81931e5197..113265dbd40 100644 --- a/examples/stream-producer.js +++ b/examples/stream-producer.js @@ -47,4 +47,4 @@ console.log(`Length of mystream: ${await client.xLen('mystream')}.`); // Should be approximately 1000: console.log(`Length of mytrimmedstream: ${await client.xLen('mytrimmedstream')}.`); -await client.quit(); +client.destroy(); diff --git a/examples/time-series.js b/examples/time-series.js index 2f2ac598032..1d61ff94408 100644 --- a/examples/time-series.js +++ b/examples/time-series.js @@ -119,4 +119,4 @@ try { console.error(e); } -await client.quit(); +client.destroy(); diff --git a/examples/topk.js b/examples/topk.js index 35cdc4a8500..d09144c230c 100644 --- a/examples/topk.js +++ b/examples/topk.js @@ -110,4 +110,4 @@ const [ simonCount, lanceCount ] = await client.topK.count('mytopk', [ console.log(`Count estimate for simon: ${simonCount}.`); console.log(`Count estimate for lance: ${lanceCount}.`); -await client.quit(); +client.destroy(); diff --git a/examples/transaction-with-arbitrary-commands.js b/examples/transaction-with-arbitrary-commands.js index 274a362d57e..d68533205a1 100644 --- a/examples/transaction-with-arbitrary-commands.js +++ b/examples/transaction-with-arbitrary-commands.js @@ -37,4 +37,4 @@ console.log(responses); // Clean up fixtures. await client.del(['hash1', 'hash2', 'hash3']); -await client.quit(); +client.destroy(); diff --git a/index.ts b/index.ts deleted file mode 100644 index 5b5a6e81294..00000000000 --- a/index.ts +++ /dev/null @@ -1,77 +0,0 @@ -import { - RedisModules, - RedisFunctions, - RedisScripts, - createClient as _createClient, - RedisClientOptions, - RedisClientType as _RedisClientType, - createCluster as _createCluster, - RedisClusterOptions, - RedisClusterType as _RedisClusterType -} from '@redis/client'; -import RedisBloomModules from '@redis/bloom'; -import RedisGraph from '@redis/graph'; -import RedisJSON from '@redis/json'; -import RediSearch from '@redis/search'; -import RedisTimeSeries from '@redis/time-series'; - -export * from '@redis/client'; -export * from '@redis/bloom'; -export * from '@redis/graph'; -export * from '@redis/json'; -export * from '@redis/search'; -export * from '@redis/time-series'; - -const modules = { - ...RedisBloomModules, - graph: RedisGraph, - json: RedisJSON, - ft: RediSearch, - ts: RedisTimeSeries -}; - -export type RedisDefaultModules = typeof modules; - -export type RedisClientType< - M extends RedisModules = RedisDefaultModules, - F extends RedisFunctions = Record, - S extends RedisScripts = Record -> = _RedisClientType; - -export function createClient< - M extends RedisModules, - F extends RedisFunctions, - S extends RedisScripts ->( - options?: RedisClientOptions -): _RedisClientType { - return _createClient({ - ...options, - modules: { - ...modules, - ...(options?.modules as M) - } - }); -} - -export type RedisClusterType< - M extends RedisModules = RedisDefaultModules, - F extends RedisFunctions = Record, - S extends RedisScripts = Record -> = _RedisClusterType; - -export function createCluster< - M extends RedisModules, - F extends RedisFunctions, - S extends RedisScripts ->( - options: RedisClusterOptions -): RedisClusterType { - return _createCluster({ - ...options, - modules: { - ...modules, - ...(options?.modules as M) - } - }); -} diff --git a/package-lock.json b/package-lock.json index 18a7003947e..aefd0678434 100644 --- a/package-lock.json +++ b/package-lock.json @@ -1,38 +1,24 @@ { - "name": "redis", - "version": "4.7.0", + "name": "redis-monorepo", "lockfileVersion": 3, "requires": true, "packages": { "": { - "name": "redis", - "version": "4.7.0", - "license": "MIT", + "name": "redis-monorepo", "workspaces": [ "./packages/*" ], - "dependencies": { - "@redis/bloom": "1.2.0", - "@redis/client": "1.6.0", - "@redis/graph": "1.1.1", - "@redis/json": "1.0.7", - "@redis/search": "1.2.0", - "@redis/time-series": "1.1.0" - }, "devDependencies": { - "@tsconfig/node14": "^14.1.0", - "gh-pages": "^6.0.0", - "release-it": "^16.1.5", - "typescript": "^5.2.2" - } - }, - "node_modules/@aashutoshrathi/word-wrap": { - "version": "1.2.6", - "resolved": "https://registry.npmjs.org/@aashutoshrathi/word-wrap/-/word-wrap-1.2.6.tgz", - "integrity": "sha512-1Yjs2SvM8TflER/OD3cOjhWWOZb58A2t7wpE2S9XfBYTiIl+XFhQG2bjy4Pu1I+EAlCNUzRDYDdFwFYUKvXcIA==", - "dev": true, - "engines": { - "node": ">=0.10.0" + "@istanbuljs/nyc-config-typescript": "^1.0.2", + "@types/mocha": "^10.0.6", + "@types/node": "^20.11.16", + "gh-pages": "^6.1.1", + "mocha": "^10.2.0", + "nyc": "^15.1.0", + "release-it": "^17.0.3", + "tsx": "^4.7.0", + "typedoc": "^0.25.7", + "typescript": "^5.3.3" } }, "node_modules/@ampproject/remapping": { @@ -49,12 +35,12 @@ } }, "node_modules/@babel/code-frame": { - "version": "7.22.13", - "resolved": "https://registry.npmjs.org/@babel/code-frame/-/code-frame-7.22.13.tgz", - "integrity": "sha512-XktuhWlJ5g+3TJXc5upd9Ks1HutSArik6jf2eAjYFyIOf4ej3RN+184cZbzDvbPnuTJIUhPKKJE3cIsYTiAT3w==", + "version": "7.23.5", + "resolved": "https://registry.npmjs.org/@babel/code-frame/-/code-frame-7.23.5.tgz", + "integrity": "sha512-CgH3s1a96LipHCmSUmYFPwY7MNx8C3avkq7i4Wl3cfa662ldtUe4VM1TPXX70pfmrlWTb6jLqTYrZyT2ZTJBgA==", "dev": true, "dependencies": { - "@babel/highlight": "^7.22.13", + "@babel/highlight": "^7.23.4", "chalk": "^2.4.2" }, "engines": { @@ -111,32 +97,53 @@ "node": ">=0.8.0" } }, + "node_modules/@babel/code-frame/node_modules/has-flag": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/has-flag/-/has-flag-3.0.0.tgz", + "integrity": "sha512-sKJf1+ceQBr4SMkvQnBDNDtf4TXpVhVGateu0t918bl30FnbE2m4vNLX+VWe/dpjlb+HugGYzW7uQXH98HPEYw==", + "dev": true, + "engines": { + "node": ">=4" + } + }, + "node_modules/@babel/code-frame/node_modules/supports-color": { + "version": "5.5.0", + "resolved": "https://registry.npmjs.org/supports-color/-/supports-color-5.5.0.tgz", + "integrity": "sha512-QjVjwdXIt408MIiAqCX4oUKsgU2EqAGzs2Ppkm4aQYbjm+ZEWEcW4SfFNTr4uMNZma0ey4f5lgLrkB0aX0QMow==", + "dev": true, + "dependencies": { + "has-flag": "^3.0.0" + }, + "engines": { + "node": ">=4" + } + }, "node_modules/@babel/compat-data": { - "version": "7.22.20", - "resolved": "https://registry.npmjs.org/@babel/compat-data/-/compat-data-7.22.20.tgz", - "integrity": "sha512-BQYjKbpXjoXwFW5jGqiizJQQT/aC7pFm9Ok1OWssonuguICi264lbgMzRp2ZMmRSlfkX6DsWDDcsrctK8Rwfiw==", + "version": "7.23.5", + "resolved": "https://registry.npmjs.org/@babel/compat-data/-/compat-data-7.23.5.tgz", + "integrity": "sha512-uU27kfDRlhfKl+w1U6vp16IuvSLtjAxdArVXPa9BvLkrr7CYIsxH5adpHObeAGY/41+syctUWOZ140a2Rvkgjw==", "dev": true, "engines": { "node": ">=6.9.0" } }, "node_modules/@babel/core": { - "version": "7.22.20", - "resolved": "https://registry.npmjs.org/@babel/core/-/core-7.22.20.tgz", - "integrity": "sha512-Y6jd1ahLubuYweD/zJH+vvOY141v4f9igNQAQ+MBgq9JlHS2iTsZKn1aMsb3vGccZsXI16VzTBw52Xx0DWmtnA==", + "version": "7.23.9", + "resolved": "https://registry.npmjs.org/@babel/core/-/core-7.23.9.tgz", + "integrity": "sha512-5q0175NOjddqpvvzU+kDiSOAk4PfdO6FvwCWoQ6RO7rTzEe8vlo+4HVfcnAREhD4npMs0e9uZypjTwzZPCf/cw==", "dev": true, "dependencies": { "@ampproject/remapping": "^2.2.0", - "@babel/code-frame": "^7.22.13", - "@babel/generator": "^7.22.15", - "@babel/helper-compilation-targets": "^7.22.15", - "@babel/helper-module-transforms": "^7.22.20", - "@babel/helpers": "^7.22.15", - "@babel/parser": "^7.22.16", - "@babel/template": "^7.22.15", - "@babel/traverse": "^7.22.20", - "@babel/types": "^7.22.19", - "convert-source-map": "^1.7.0", + "@babel/code-frame": "^7.23.5", + "@babel/generator": "^7.23.6", + "@babel/helper-compilation-targets": "^7.23.6", + "@babel/helper-module-transforms": "^7.23.3", + "@babel/helpers": "^7.23.9", + "@babel/parser": "^7.23.9", + "@babel/template": "^7.23.9", + "@babel/traverse": "^7.23.9", + "@babel/types": "^7.23.9", + "convert-source-map": "^2.0.0", "debug": "^4.1.0", "gensync": "^1.0.0-beta.2", "json5": "^2.2.3", @@ -150,13 +157,19 @@ "url": "https://opencollective.com/babel" } }, + "node_modules/@babel/core/node_modules/convert-source-map": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/convert-source-map/-/convert-source-map-2.0.0.tgz", + "integrity": "sha512-Kvp459HrV2FEJ1CAsi1Ku+MY3kasH19TFykTz2xWmMeq6bk2NU3XXvfJ+Q61m0xktWwt+1HSYf3JZsTms3aRJg==", + "dev": true + }, "node_modules/@babel/generator": { - "version": "7.22.15", - "resolved": "https://registry.npmjs.org/@babel/generator/-/generator-7.22.15.tgz", - "integrity": "sha512-Zu9oWARBqeVOW0dZOjXc3JObrzuqothQ3y/n1kUtrjCoCPLkXUwMvOo/F/TCfoHMbWIFlWwpZtkZVb9ga4U2pA==", + "version": "7.23.6", + "resolved": "https://registry.npmjs.org/@babel/generator/-/generator-7.23.6.tgz", + "integrity": "sha512-qrSfCYxYQB5owCmGLbl8XRpX1ytXlpueOb0N0UmQwA073KZxejgQTzAmJezxvpwQD9uGtK2shHdi55QT+MbjIw==", "dev": true, "dependencies": { - "@babel/types": "^7.22.15", + "@babel/types": "^7.23.6", "@jridgewell/gen-mapping": "^0.3.2", "@jridgewell/trace-mapping": "^0.3.17", "jsesc": "^2.5.1" @@ -166,14 +179,14 @@ } }, "node_modules/@babel/helper-compilation-targets": { - "version": "7.22.15", - "resolved": "https://registry.npmjs.org/@babel/helper-compilation-targets/-/helper-compilation-targets-7.22.15.tgz", - "integrity": "sha512-y6EEzULok0Qvz8yyLkCvVX+02ic+By2UdOhylwUOvOn9dvYc9mKICJuuU1n1XBI02YWsNsnrY1kc6DVbjcXbtw==", + "version": "7.23.6", + "resolved": "https://registry.npmjs.org/@babel/helper-compilation-targets/-/helper-compilation-targets-7.23.6.tgz", + "integrity": "sha512-9JB548GZoQVmzrFgp8o7KxdgkTGm6xs9DW0o/Pim72UDjzr5ObUQ6ZzYPqA+g9OTS2bBQoctLJrky0RDCAWRgQ==", "dev": true, "dependencies": { - "@babel/compat-data": "^7.22.9", - "@babel/helper-validator-option": "^7.22.15", - "browserslist": "^4.21.9", + "@babel/compat-data": "^7.23.5", + "@babel/helper-validator-option": "^7.23.5", + "browserslist": "^4.22.2", "lru-cache": "^5.1.1", "semver": "^6.3.1" }, @@ -181,21 +194,6 @@ "node": ">=6.9.0" } }, - "node_modules/@babel/helper-compilation-targets/node_modules/lru-cache": { - "version": "5.1.1", - "resolved": "https://registry.npmjs.org/lru-cache/-/lru-cache-5.1.1.tgz", - "integrity": "sha512-KpNARQA3Iwv+jTA0utUVVbrh+Jlrr1Fv0e56GGzAFOXN7dk/FviaDW8LHmK52DlcH4WP2n6gI8vN1aesBFgo9w==", - "dev": true, - "dependencies": { - "yallist": "^3.0.2" - } - }, - "node_modules/@babel/helper-compilation-targets/node_modules/yallist": { - "version": "3.1.1", - "resolved": "https://registry.npmjs.org/yallist/-/yallist-3.1.1.tgz", - "integrity": "sha512-a4UGQaWPH59mOXUYnAG2ewncQS4i4F43Tv3JoAM+s2VDAmS9NsK8GpDMLrCHPksFT7h3K6TOoUNn2pb7RoXx4g==", - "dev": true - }, "node_modules/@babel/helper-environment-visitor": { "version": "7.22.20", "resolved": "https://registry.npmjs.org/@babel/helper-environment-visitor/-/helper-environment-visitor-7.22.20.tgz", @@ -206,13 +204,13 @@ } }, "node_modules/@babel/helper-function-name": { - "version": "7.22.5", - "resolved": "https://registry.npmjs.org/@babel/helper-function-name/-/helper-function-name-7.22.5.tgz", - "integrity": "sha512-wtHSq6jMRE3uF2otvfuD3DIvVhOsSNshQl0Qrd7qC9oQJzHvOL4qQXlQn2916+CXGywIjpGuIkoyZRRxHPiNQQ==", + "version": "7.23.0", + "resolved": "https://registry.npmjs.org/@babel/helper-function-name/-/helper-function-name-7.23.0.tgz", + "integrity": "sha512-OErEqsrxjZTJciZ4Oo+eoZqeW9UIiOcuYKRJA4ZAgV9myA+pOXhhmpfNCKjEH/auVfEYVFJ6y1Tc4r0eIApqiw==", "dev": true, "dependencies": { - "@babel/template": "^7.22.5", - "@babel/types": "^7.22.5" + "@babel/template": "^7.22.15", + "@babel/types": "^7.23.0" }, "engines": { "node": ">=6.9.0" @@ -243,9 +241,9 @@ } }, "node_modules/@babel/helper-module-transforms": { - "version": "7.22.20", - "resolved": "https://registry.npmjs.org/@babel/helper-module-transforms/-/helper-module-transforms-7.22.20.tgz", - "integrity": "sha512-dLT7JVWIUUxKOs1UnJUBR3S70YK+pKX6AbJgB2vMIvEkZkrfJDbYDJesnPshtKV4LhDOR3Oc5YULeDizRek+5A==", + "version": "7.23.3", + "resolved": "https://registry.npmjs.org/@babel/helper-module-transforms/-/helper-module-transforms-7.23.3.tgz", + "integrity": "sha512-7bBs4ED9OmswdfDzpz4MpWgSrV7FXlc3zIagvLFjS5H+Mk7Snr21vQ6QwrsoCGMfNC4e4LQPdoULEt4ykz0SRQ==", "dev": true, "dependencies": { "@babel/helper-environment-visitor": "^7.22.20", @@ -286,9 +284,9 @@ } }, "node_modules/@babel/helper-string-parser": { - "version": "7.22.5", - "resolved": "https://registry.npmjs.org/@babel/helper-string-parser/-/helper-string-parser-7.22.5.tgz", - "integrity": "sha512-mM4COjgZox8U+JcXQwPijIZLElkgEpO5rsERVDJTc2qfCDfERyob6k5WegS14SX18IIjv+XD+GrqNumY5JRCDw==", + "version": "7.23.4", + "resolved": "https://registry.npmjs.org/@babel/helper-string-parser/-/helper-string-parser-7.23.4.tgz", + "integrity": "sha512-803gmbQdqwdf4olxrX4AJyFBV/RTr3rSmOj0rKwesmzlfhYNDEs+/iOcznzpNWlJlIlTJC2QfPFcHB6DlzdVLQ==", "dev": true, "engines": { "node": ">=6.9.0" @@ -304,32 +302,32 @@ } }, "node_modules/@babel/helper-validator-option": { - "version": "7.22.15", - "resolved": "https://registry.npmjs.org/@babel/helper-validator-option/-/helper-validator-option-7.22.15.tgz", - "integrity": "sha512-bMn7RmyFjY/mdECUbgn9eoSY4vqvacUnS9i9vGAGttgFWesO6B4CYWA7XlpbWgBt71iv/hfbPlynohStqnu5hA==", + "version": "7.23.5", + "resolved": "https://registry.npmjs.org/@babel/helper-validator-option/-/helper-validator-option-7.23.5.tgz", + "integrity": "sha512-85ttAOMLsr53VgXkTbkx8oA6YTfT4q7/HzXSLEYmjcSTJPMPQtvq1BD79Byep5xMUYbGRzEpDsjUf3dyp54IKw==", "dev": true, "engines": { "node": ">=6.9.0" } }, "node_modules/@babel/helpers": { - "version": "7.22.15", - "resolved": "https://registry.npmjs.org/@babel/helpers/-/helpers-7.22.15.tgz", - "integrity": "sha512-7pAjK0aSdxOwR+CcYAqgWOGy5dcfvzsTIfFTb2odQqW47MDfv14UaJDY6eng8ylM2EaeKXdxaSWESbkmaQHTmw==", + "version": "7.23.9", + "resolved": "https://registry.npmjs.org/@babel/helpers/-/helpers-7.23.9.tgz", + "integrity": "sha512-87ICKgU5t5SzOT7sBMfCOZQ2rHjRU+Pcb9BoILMYz600W6DkVRLFBPwQ18gwUVvggqXivaUakpnxWQGbpywbBQ==", "dev": true, "dependencies": { - "@babel/template": "^7.22.15", - "@babel/traverse": "^7.22.15", - "@babel/types": "^7.22.15" + "@babel/template": "^7.23.9", + "@babel/traverse": "^7.23.9", + "@babel/types": "^7.23.9" }, "engines": { "node": ">=6.9.0" } }, "node_modules/@babel/highlight": { - "version": "7.22.20", - "resolved": "https://registry.npmjs.org/@babel/highlight/-/highlight-7.22.20.tgz", - "integrity": "sha512-dkdMCN3py0+ksCgYmGG8jKeGA/8Tk+gJwSYYlFGxG5lmhfKNoAy004YpLxpS1W2J8m/EK2Ew+yOs9pVRwO89mg==", + "version": "7.23.4", + "resolved": "https://registry.npmjs.org/@babel/highlight/-/highlight-7.23.4.tgz", + "integrity": "sha512-acGdbYSfp2WheJoJm/EBBBLh/ID8KDc64ISZ9DYtBmC8/Q204PZJLHyzeB5qMzJ5trcOkybd78M4x2KWsUq++A==", "dev": true, "dependencies": { "@babel/helper-validator-identifier": "^7.22.20", @@ -390,10 +388,31 @@ "node": ">=0.8.0" } }, + "node_modules/@babel/highlight/node_modules/has-flag": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/has-flag/-/has-flag-3.0.0.tgz", + "integrity": "sha512-sKJf1+ceQBr4SMkvQnBDNDtf4TXpVhVGateu0t918bl30FnbE2m4vNLX+VWe/dpjlb+HugGYzW7uQXH98HPEYw==", + "dev": true, + "engines": { + "node": ">=4" + } + }, + "node_modules/@babel/highlight/node_modules/supports-color": { + "version": "5.5.0", + "resolved": "https://registry.npmjs.org/supports-color/-/supports-color-5.5.0.tgz", + "integrity": "sha512-QjVjwdXIt408MIiAqCX4oUKsgU2EqAGzs2Ppkm4aQYbjm+ZEWEcW4SfFNTr4uMNZma0ey4f5lgLrkB0aX0QMow==", + "dev": true, + "dependencies": { + "has-flag": "^3.0.0" + }, + "engines": { + "node": ">=4" + } + }, "node_modules/@babel/parser": { - "version": "7.22.16", - "resolved": "https://registry.npmjs.org/@babel/parser/-/parser-7.22.16.tgz", - "integrity": "sha512-+gPfKv8UWeKKeJTUxe59+OobVcrYHETCsORl61EmSkmgymguYk/X5bp7GuUIXaFsc6y++v8ZxPsLSSuujqDphA==", + "version": "7.23.9", + "resolved": "https://registry.npmjs.org/@babel/parser/-/parser-7.23.9.tgz", + "integrity": "sha512-9tcKgqKbs3xGJ+NtKF2ndOBBLVwPjl1SHxPQkd36r3Dlirw3xWUeGaTbqr7uGZcTaxkVNwc+03SVP7aCdWrTlA==", "dev": true, "bin": { "parser": "bin/babel-parser.js" @@ -403,260 +422,530 @@ } }, "node_modules/@babel/template": { - "version": "7.22.15", - "resolved": "https://registry.npmjs.org/@babel/template/-/template-7.22.15.tgz", - "integrity": "sha512-QPErUVm4uyJa60rkI73qneDacvdvzxshT3kksGqlGWYdOTIUOwJ7RDUL8sGqslY1uXWSL6xMFKEXDS3ox2uF0w==", + "version": "7.23.9", + "resolved": "https://registry.npmjs.org/@babel/template/-/template-7.23.9.tgz", + "integrity": "sha512-+xrD2BWLpvHKNmX2QbpdpsBaWnRxahMwJjO+KZk2JOElj5nSmKezyS1B4u+QbHMTX69t4ukm6hh9lsYQ7GHCKA==", "dev": true, "dependencies": { - "@babel/code-frame": "^7.22.13", - "@babel/parser": "^7.22.15", - "@babel/types": "^7.22.15" + "@babel/code-frame": "^7.23.5", + "@babel/parser": "^7.23.9", + "@babel/types": "^7.23.9" }, "engines": { "node": ">=6.9.0" } }, "node_modules/@babel/traverse": { - "version": "7.22.20", - "resolved": "https://registry.npmjs.org/@babel/traverse/-/traverse-7.22.20.tgz", - "integrity": "sha512-eU260mPZbU7mZ0N+X10pxXhQFMGTeLb9eFS0mxehS8HZp9o1uSnFeWQuG1UPrlxgA7QoUzFhOnilHDp0AXCyHw==", + "version": "7.23.9", + "resolved": "https://registry.npmjs.org/@babel/traverse/-/traverse-7.23.9.tgz", + "integrity": "sha512-I/4UJ9vs90OkBtY6iiiTORVMyIhJ4kAVmsKo9KFc8UOxMeUfi2hvtIBsET5u9GizXE6/GFSuKCTNfgCswuEjRg==", "dev": true, "dependencies": { - "@babel/code-frame": "^7.22.13", - "@babel/generator": "^7.22.15", + "@babel/code-frame": "^7.23.5", + "@babel/generator": "^7.23.6", "@babel/helper-environment-visitor": "^7.22.20", - "@babel/helper-function-name": "^7.22.5", + "@babel/helper-function-name": "^7.23.0", "@babel/helper-hoist-variables": "^7.22.5", "@babel/helper-split-export-declaration": "^7.22.6", - "@babel/parser": "^7.22.16", - "@babel/types": "^7.22.19", - "debug": "^4.1.0", + "@babel/parser": "^7.23.9", + "@babel/types": "^7.23.9", + "debug": "^4.3.1", "globals": "^11.1.0" }, "engines": { "node": ">=6.9.0" } }, - "node_modules/@babel/traverse/node_modules/globals": { - "version": "11.12.0", - "resolved": "https://registry.npmjs.org/globals/-/globals-11.12.0.tgz", - "integrity": "sha512-WOBp/EEGUiIsJSp7wcv/y6MO+lV9UoncWqxuFfm8eBwzWNgyfBd6Gz+IeKQ9jCmyhoH99g15M3T+QaVHFjizVA==", - "dev": true, - "engines": { - "node": ">=4" - } - }, "node_modules/@babel/types": { - "version": "7.22.19", - "resolved": "https://registry.npmjs.org/@babel/types/-/types-7.22.19.tgz", - "integrity": "sha512-P7LAw/LbojPzkgp5oznjE6tQEIWbp4PkkfrZDINTro9zgBRtI324/EYsiSI7lhPbpIQ+DCeR2NNmMWANGGfZsg==", + "version": "7.23.9", + "resolved": "https://registry.npmjs.org/@babel/types/-/types-7.23.9.tgz", + "integrity": "sha512-dQjSq/7HaSjRM43FFGnv5keM2HsxpmyV1PfaSVm0nzzjwwTmjOe6J4bC8e3+pTEIgHaHj+1ZlLThRJ2auc/w1Q==", "dev": true, "dependencies": { - "@babel/helper-string-parser": "^7.22.5", - "@babel/helper-validator-identifier": "^7.22.19", + "@babel/helper-string-parser": "^7.23.4", + "@babel/helper-validator-identifier": "^7.22.20", "to-fast-properties": "^2.0.0" }, "engines": { "node": ">=6.9.0" } }, - "node_modules/@cspotcode/source-map-support": { - "version": "0.8.1", - "resolved": "https://registry.npmjs.org/@cspotcode/source-map-support/-/source-map-support-0.8.1.tgz", - "integrity": "sha512-IchNf6dN4tHoMFIn/7OE8LWZ19Y6q/67Bmf6vnGREv8RSbBVb9LPJxEcnwrcwX6ixSvaiGoomAUvu4YSxXrVgw==", + "node_modules/@esbuild/aix-ppc64": { + "version": "0.19.12", + "resolved": "https://registry.npmjs.org/@esbuild/aix-ppc64/-/aix-ppc64-0.19.12.tgz", + "integrity": "sha512-bmoCYyWdEL3wDQIVbcyzRyeKLgk2WtWLTWz1ZIAZF/EGbNOwSA6ew3PftJ1PqMiOOGu0OyFMzG53L0zqIpPeNA==", + "cpu": [ + "ppc64" + ], "dev": true, - "dependencies": { - "@jridgewell/trace-mapping": "0.3.9" - }, + "optional": true, + "os": [ + "aix" + ], "engines": { "node": ">=12" } }, - "node_modules/@cspotcode/source-map-support/node_modules/@jridgewell/trace-mapping": { - "version": "0.3.9", - "resolved": "https://registry.npmjs.org/@jridgewell/trace-mapping/-/trace-mapping-0.3.9.tgz", - "integrity": "sha512-3Belt6tdc8bPgAtbcmdtNJlirVoTmEb5e2gC94PnkwEW9jI6CAHUeoG85tjWP5WquqfavoMtMwiG4P926ZKKuQ==", + "node_modules/@esbuild/android-arm": { + "version": "0.19.12", + "resolved": "https://registry.npmjs.org/@esbuild/android-arm/-/android-arm-0.19.12.tgz", + "integrity": "sha512-qg/Lj1mu3CdQlDEEiWrlC4eaPZ1KztwGJ9B6J+/6G+/4ewxJg7gqj8eVYWvao1bXrqGiW2rsBZFSX3q2lcW05w==", + "cpu": [ + "arm" + ], "dev": true, - "dependencies": { - "@jridgewell/resolve-uri": "^3.0.3", - "@jridgewell/sourcemap-codec": "^1.4.10" + "optional": true, + "os": [ + "android" + ], + "engines": { + "node": ">=12" } }, - "node_modules/@eslint-community/eslint-utils": { - "version": "4.4.0", - "resolved": "https://registry.npmjs.org/@eslint-community/eslint-utils/-/eslint-utils-4.4.0.tgz", - "integrity": "sha512-1/sA4dwrzBAyeUoQ6oxahHKmrZvsnLCg4RfxW3ZFGGmQkSNQPFNLV9CUEFQP1x9EYXHTo5p6xdhZM1Ne9p/AfA==", + "node_modules/@esbuild/android-arm64": { + "version": "0.19.12", + "resolved": "https://registry.npmjs.org/@esbuild/android-arm64/-/android-arm64-0.19.12.tgz", + "integrity": "sha512-P0UVNGIienjZv3f5zq0DP3Nt2IE/3plFzuaS96vihvD0Hd6H/q4WXUGpCxD/E8YrSXfNyRPbpTq+T8ZQioSuPA==", + "cpu": [ + "arm64" + ], "dev": true, - "dependencies": { - "eslint-visitor-keys": "^3.3.0" - }, + "optional": true, + "os": [ + "android" + ], "engines": { - "node": "^12.22.0 || ^14.17.0 || >=16.0.0" - }, - "peerDependencies": { - "eslint": "^6.0.0 || ^7.0.0 || >=8.0.0" + "node": ">=12" } }, - "node_modules/@eslint-community/regexpp": { - "version": "4.8.1", - "resolved": "https://registry.npmjs.org/@eslint-community/regexpp/-/regexpp-4.8.1.tgz", - "integrity": "sha512-PWiOzLIUAjN/w5K17PoF4n6sKBw0gqLHPhywmYHP4t1VFQQVYeb1yWsJwnMVEMl3tUHME7X/SJPZLmtG7XBDxQ==", + "node_modules/@esbuild/android-x64": { + "version": "0.19.12", + "resolved": "https://registry.npmjs.org/@esbuild/android-x64/-/android-x64-0.19.12.tgz", + "integrity": "sha512-3k7ZoUW6Q6YqhdhIaq/WZ7HwBpnFBlW905Fa4s4qWJyiNOgT1dOqDiVAQFwBH7gBRZr17gLrlFCRzF6jFh7Kew==", + "cpu": [ + "x64" + ], "dev": true, + "optional": true, + "os": [ + "android" + ], "engines": { - "node": "^12.0.0 || ^14.0.0 || >=16.0.0" + "node": ">=12" } }, - "node_modules/@eslint/eslintrc": { - "version": "2.1.2", - "resolved": "https://registry.npmjs.org/@eslint/eslintrc/-/eslintrc-2.1.2.tgz", - "integrity": "sha512-+wvgpDsrB1YqAMdEUCcnTlpfVBH7Vqn6A/NT3D8WVXFIaKMlErPIZT3oCIAVCOtarRpMtelZLqJeU3t7WY6X6g==", + "node_modules/@esbuild/darwin-arm64": { + "version": "0.19.12", + "resolved": "https://registry.npmjs.org/@esbuild/darwin-arm64/-/darwin-arm64-0.19.12.tgz", + "integrity": "sha512-B6IeSgZgtEzGC42jsI+YYu9Z3HKRxp8ZT3cqhvliEHovq8HSX2YX8lNocDn79gCKJXOSaEot9MVYky7AKjCs8g==", + "cpu": [ + "arm64" + ], "dev": true, - "dependencies": { - "ajv": "^6.12.4", - "debug": "^4.3.2", - "espree": "^9.6.0", - "globals": "^13.19.0", - "ignore": "^5.2.0", - "import-fresh": "^3.2.1", - "js-yaml": "^4.1.0", - "minimatch": "^3.1.2", - "strip-json-comments": "^3.1.1" - }, + "optional": true, + "os": [ + "darwin" + ], "engines": { - "node": "^12.22.0 || ^14.17.0 || >=16.0.0" - }, - "funding": { - "url": "https://opencollective.com/eslint" + "node": ">=12" } }, - "node_modules/@eslint/eslintrc/node_modules/strip-json-comments": { - "version": "3.1.1", - "resolved": "https://registry.npmjs.org/strip-json-comments/-/strip-json-comments-3.1.1.tgz", - "integrity": "sha512-6fPc+R4ihwqP6N/aIv2f1gMH8lOVtWQHoqC4yK6oSDVVocumAsfCqjkXnqiYMhmMwS/mEHLp7Vehlt3ql6lEig==", + "node_modules/@esbuild/darwin-x64": { + "version": "0.19.12", + "resolved": "https://registry.npmjs.org/@esbuild/darwin-x64/-/darwin-x64-0.19.12.tgz", + "integrity": "sha512-hKoVkKzFiToTgn+41qGhsUJXFlIjxI/jSYeZf3ugemDYZldIXIxhvwN6erJGlX4t5h417iFuheZ7l+YVn05N3A==", + "cpu": [ + "x64" + ], "dev": true, + "optional": true, + "os": [ + "darwin" + ], "engines": { - "node": ">=8" - }, - "funding": { - "url": "https://github.com/sponsors/sindresorhus" + "node": ">=12" } }, - "node_modules/@eslint/js": { - "version": "8.49.0", - "resolved": "https://registry.npmjs.org/@eslint/js/-/js-8.49.0.tgz", - "integrity": "sha512-1S8uAY/MTJqVx0SC4epBq+N2yhuwtNwLbJYNZyhL2pO1ZVKn5HFXav5T41Ryzy9K9V7ZId2JB2oy/W4aCd9/2w==", + "node_modules/@esbuild/freebsd-arm64": { + "version": "0.19.12", + "resolved": "https://registry.npmjs.org/@esbuild/freebsd-arm64/-/freebsd-arm64-0.19.12.tgz", + "integrity": "sha512-4aRvFIXmwAcDBw9AueDQ2YnGmz5L6obe5kmPT8Vd+/+x/JMVKCgdcRwH6APrbpNXsPz+K653Qg8HB/oXvXVukA==", + "cpu": [ + "arm64" + ], "dev": true, + "optional": true, + "os": [ + "freebsd" + ], "engines": { - "node": "^12.22.0 || ^14.17.0 || >=16.0.0" + "node": ">=12" } }, - "node_modules/@humanwhocodes/config-array": { - "version": "0.11.11", - "resolved": "https://registry.npmjs.org/@humanwhocodes/config-array/-/config-array-0.11.11.tgz", - "integrity": "sha512-N2brEuAadi0CcdeMXUkhbZB84eskAc8MEX1By6qEchoVywSgXPIjou4rYsl0V3Hj0ZnuGycGCjdNgockbzeWNA==", + "node_modules/@esbuild/freebsd-x64": { + "version": "0.19.12", + "resolved": "https://registry.npmjs.org/@esbuild/freebsd-x64/-/freebsd-x64-0.19.12.tgz", + "integrity": "sha512-EYoXZ4d8xtBoVN7CEwWY2IN4ho76xjYXqSXMNccFSx2lgqOG/1TBPW0yPx1bJZk94qu3tX0fycJeeQsKovA8gg==", + "cpu": [ + "x64" + ], "dev": true, - "dependencies": { - "@humanwhocodes/object-schema": "^1.2.1", - "debug": "^4.1.1", - "minimatch": "^3.0.5" - }, + "optional": true, + "os": [ + "freebsd" + ], "engines": { - "node": ">=10.10.0" + "node": ">=12" } }, - "node_modules/@humanwhocodes/module-importer": { - "version": "1.0.1", - "resolved": "https://registry.npmjs.org/@humanwhocodes/module-importer/-/module-importer-1.0.1.tgz", - "integrity": "sha512-bxveV4V8v5Yb4ncFTT3rPSgZBOpCkjfK0y4oVVVJwIuDVBRMDXrPyXRL988i5ap9m9bnyEEjWfm5WkBmtffLfA==", + "node_modules/@esbuild/linux-arm": { + "version": "0.19.12", + "resolved": "https://registry.npmjs.org/@esbuild/linux-arm/-/linux-arm-0.19.12.tgz", + "integrity": "sha512-J5jPms//KhSNv+LO1S1TX1UWp1ucM6N6XuL6ITdKWElCu8wXP72l9MM0zDTzzeikVyqFE6U8YAV9/tFyj0ti+w==", + "cpu": [ + "arm" + ], "dev": true, + "optional": true, + "os": [ + "linux" + ], "engines": { - "node": ">=12.22" - }, - "funding": { - "type": "github", - "url": "https://github.com/sponsors/nzakas" + "node": ">=12" } }, - "node_modules/@humanwhocodes/object-schema": { - "version": "1.2.1", - "resolved": "https://registry.npmjs.org/@humanwhocodes/object-schema/-/object-schema-1.2.1.tgz", - "integrity": "sha512-ZnQMnLV4e7hDlUvw8H+U8ASL02SS2Gn6+9Ac3wGGLIe7+je2AeAOxPY+izIPJDfFDb7eDjev0Us8MO1iFRN8hA==", - "dev": true - }, - "node_modules/@iarna/toml": { - "version": "2.2.5", - "resolved": "https://registry.npmjs.org/@iarna/toml/-/toml-2.2.5.tgz", - "integrity": "sha512-trnsAYxU3xnS1gPHPyU961coFyLkh4gAD/0zQ5mymY4yOZ+CYvsPqUbOFSw0aDM4y0tV7tiFxL/1XfXPNC6IPg==", - "dev": true - }, - "node_modules/@istanbuljs/load-nyc-config": { - "version": "1.1.0", - "resolved": "https://registry.npmjs.org/@istanbuljs/load-nyc-config/-/load-nyc-config-1.1.0.tgz", - "integrity": "sha512-VjeHSlIzpv/NyD3N0YuHfXOPDIixcA1q2ZV98wsMqcYlPmv2n3Yb2lYP9XMElnaFVXg5A7YLTeLu6V84uQDjmQ==", + "node_modules/@esbuild/linux-arm64": { + "version": "0.19.12", + "resolved": "https://registry.npmjs.org/@esbuild/linux-arm64/-/linux-arm64-0.19.12.tgz", + "integrity": "sha512-EoTjyYyLuVPfdPLsGVVVC8a0p1BFFvtpQDB/YLEhaXyf/5bczaGeN15QkR+O4S5LeJ92Tqotve7i1jn35qwvdA==", + "cpu": [ + "arm64" + ], "dev": true, - "dependencies": { - "camelcase": "^5.3.1", - "find-up": "^4.1.0", - "get-package-type": "^0.1.0", - "js-yaml": "^3.13.1", - "resolve-from": "^5.0.0" - }, + "optional": true, + "os": [ + "linux" + ], "engines": { - "node": ">=8" + "node": ">=12" } }, - "node_modules/@istanbuljs/load-nyc-config/node_modules/argparse": { - "version": "1.0.10", - "resolved": "https://registry.npmjs.org/argparse/-/argparse-1.0.10.tgz", - "integrity": "sha512-o5Roy6tNG4SL/FOkCAN6RzjiakZS25RLYFrcMttJqbdd8BWrnA+fGz57iN5Pb06pvBGvl5gQ0B48dJlslXvoTg==", + "node_modules/@esbuild/linux-ia32": { + "version": "0.19.12", + "resolved": "https://registry.npmjs.org/@esbuild/linux-ia32/-/linux-ia32-0.19.12.tgz", + "integrity": "sha512-Thsa42rrP1+UIGaWz47uydHSBOgTUnwBwNq59khgIwktK6x60Hivfbux9iNR0eHCHzOLjLMLfUMLCypBkZXMHA==", + "cpu": [ + "ia32" + ], "dev": true, - "dependencies": { - "sprintf-js": "~1.0.2" + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": ">=12" } }, - "node_modules/@istanbuljs/load-nyc-config/node_modules/camelcase": { - "version": "5.3.1", - "resolved": "https://registry.npmjs.org/camelcase/-/camelcase-5.3.1.tgz", - "integrity": "sha512-L28STB170nwWS63UjtlEOE3dldQApaJXZkOI1uMFfzf3rRuPegHaHesyee+YxQ+W6SvRDQV6UrdOdRiR153wJg==", + "node_modules/@esbuild/linux-loong64": { + "version": "0.19.12", + "resolved": "https://registry.npmjs.org/@esbuild/linux-loong64/-/linux-loong64-0.19.12.tgz", + "integrity": "sha512-LiXdXA0s3IqRRjm6rV6XaWATScKAXjI4R4LoDlvO7+yQqFdlr1Bax62sRwkVvRIrwXxvtYEHHI4dm50jAXkuAA==", + "cpu": [ + "loong64" + ], "dev": true, + "optional": true, + "os": [ + "linux" + ], "engines": { - "node": ">=6" + "node": ">=12" } }, - "node_modules/@istanbuljs/load-nyc-config/node_modules/js-yaml": { - "version": "3.14.1", - "resolved": "https://registry.npmjs.org/js-yaml/-/js-yaml-3.14.1.tgz", - "integrity": "sha512-okMH7OXXJ7YrN9Ok3/SXrnu4iX9yOk+25nqX4imS2npuvTYDmo/QEZoqwZkYaIDk3jVvBOTOIEgEhaLOynBS9g==", + "node_modules/@esbuild/linux-mips64el": { + "version": "0.19.12", + "resolved": "https://registry.npmjs.org/@esbuild/linux-mips64el/-/linux-mips64el-0.19.12.tgz", + "integrity": "sha512-fEnAuj5VGTanfJ07ff0gOA6IPsvrVHLVb6Lyd1g2/ed67oU1eFzL0r9WL7ZzscD+/N6i3dWumGE1Un4f7Amf+w==", + "cpu": [ + "mips64el" + ], "dev": true, - "dependencies": { - "argparse": "^1.0.7", - "esprima": "^4.0.0" - }, - "bin": { - "js-yaml": "bin/js-yaml.js" + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": ">=12" } }, - "node_modules/@istanbuljs/load-nyc-config/node_modules/resolve-from": { - "version": "5.0.0", - "resolved": "https://registry.npmjs.org/resolve-from/-/resolve-from-5.0.0.tgz", - "integrity": "sha512-qYg9KP24dD5qka9J47d0aVky0N+b4fTU89LN9iDnjB5waksiC49rvMB0PrUJQGoTmH50XPiqOvAjDfaijGxYZw==", + "node_modules/@esbuild/linux-ppc64": { + "version": "0.19.12", + "resolved": "https://registry.npmjs.org/@esbuild/linux-ppc64/-/linux-ppc64-0.19.12.tgz", + "integrity": "sha512-nYJA2/QPimDQOh1rKWedNOe3Gfc8PabU7HT3iXWtNUbRzXS9+vgB0Fjaqr//XNbd82mCxHzik2qotuI89cfixg==", + "cpu": [ + "ppc64" + ], "dev": true, + "optional": true, + "os": [ + "linux" + ], "engines": { - "node": ">=8" + "node": ">=12" } }, - "node_modules/@istanbuljs/nyc-config-typescript": { - "version": "1.0.2", - "resolved": "https://registry.npmjs.org/@istanbuljs/nyc-config-typescript/-/nyc-config-typescript-1.0.2.tgz", - "integrity": "sha512-iKGIyMoyJuFnJRSVTZ78POIRvNnwZaWIf8vG4ZS3rQq58MMDrqEX2nnzx0R28V2X8JvmKYiqY9FP2hlJsm8A0w==", + "node_modules/@esbuild/linux-riscv64": { + "version": "0.19.12", + "resolved": "https://registry.npmjs.org/@esbuild/linux-riscv64/-/linux-riscv64-0.19.12.tgz", + "integrity": "sha512-2MueBrlPQCw5dVJJpQdUYgeqIzDQgw3QtiAHUC4RBz9FXPrskyyU3VI1hw7C0BSKB9OduwSJ79FTCqtGMWqJHg==", + "cpu": [ + "riscv64" + ], "dev": true, - "dependencies": { - "@istanbuljs/schema": "^0.1.2" - }, + "optional": true, + "os": [ + "linux" + ], "engines": { - "node": ">=8" - }, - "peerDependencies": { + "node": ">=12" + } + }, + "node_modules/@esbuild/linux-s390x": { + "version": "0.19.12", + "resolved": "https://registry.npmjs.org/@esbuild/linux-s390x/-/linux-s390x-0.19.12.tgz", + "integrity": "sha512-+Pil1Nv3Umes4m3AZKqA2anfhJiVmNCYkPchwFJNEJN5QxmTs1uzyy4TvmDrCRNT2ApwSari7ZIgrPeUx4UZDg==", + "cpu": [ + "s390x" + ], + "dev": true, + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": ">=12" + } + }, + "node_modules/@esbuild/linux-x64": { + "version": "0.19.12", + "resolved": "https://registry.npmjs.org/@esbuild/linux-x64/-/linux-x64-0.19.12.tgz", + "integrity": "sha512-B71g1QpxfwBvNrfyJdVDexenDIt1CiDN1TIXLbhOw0KhJzE78KIFGX6OJ9MrtC0oOqMWf+0xop4qEU8JrJTwCg==", + "cpu": [ + "x64" + ], + "dev": true, + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": ">=12" + } + }, + "node_modules/@esbuild/netbsd-x64": { + "version": "0.19.12", + "resolved": "https://registry.npmjs.org/@esbuild/netbsd-x64/-/netbsd-x64-0.19.12.tgz", + "integrity": "sha512-3ltjQ7n1owJgFbuC61Oj++XhtzmymoCihNFgT84UAmJnxJfm4sYCiSLTXZtE00VWYpPMYc+ZQmB6xbSdVh0JWA==", + "cpu": [ + "x64" + ], + "dev": true, + "optional": true, + "os": [ + "netbsd" + ], + "engines": { + "node": ">=12" + } + }, + "node_modules/@esbuild/openbsd-x64": { + "version": "0.19.12", + "resolved": "https://registry.npmjs.org/@esbuild/openbsd-x64/-/openbsd-x64-0.19.12.tgz", + "integrity": "sha512-RbrfTB9SWsr0kWmb9srfF+L933uMDdu9BIzdA7os2t0TXhCRjrQyCeOt6wVxr79CKD4c+p+YhCj31HBkYcXebw==", + "cpu": [ + "x64" + ], + "dev": true, + "optional": true, + "os": [ + "openbsd" + ], + "engines": { + "node": ">=12" + } + }, + "node_modules/@esbuild/sunos-x64": { + "version": "0.19.12", + "resolved": "https://registry.npmjs.org/@esbuild/sunos-x64/-/sunos-x64-0.19.12.tgz", + "integrity": "sha512-HKjJwRrW8uWtCQnQOz9qcU3mUZhTUQvi56Q8DPTLLB+DawoiQdjsYq+j+D3s9I8VFtDr+F9CjgXKKC4ss89IeA==", + "cpu": [ + "x64" + ], + "dev": true, + "optional": true, + "os": [ + "sunos" + ], + "engines": { + "node": ">=12" + } + }, + "node_modules/@esbuild/win32-arm64": { + "version": "0.19.12", + "resolved": "https://registry.npmjs.org/@esbuild/win32-arm64/-/win32-arm64-0.19.12.tgz", + "integrity": "sha512-URgtR1dJnmGvX864pn1B2YUYNzjmXkuJOIqG2HdU62MVS4EHpU2946OZoTMnRUHklGtJdJZ33QfzdjGACXhn1A==", + "cpu": [ + "arm64" + ], + "dev": true, + "optional": true, + "os": [ + "win32" + ], + "engines": { + "node": ">=12" + } + }, + "node_modules/@esbuild/win32-ia32": { + "version": "0.19.12", + "resolved": "https://registry.npmjs.org/@esbuild/win32-ia32/-/win32-ia32-0.19.12.tgz", + "integrity": "sha512-+ZOE6pUkMOJfmxmBZElNOx72NKpIa/HFOMGzu8fqzQJ5kgf6aTGrcJaFsNiVMH4JKpMipyK+7k0n2UXN7a8YKQ==", + "cpu": [ + "ia32" + ], + "dev": true, + "optional": true, + "os": [ + "win32" + ], + "engines": { + "node": ">=12" + } + }, + "node_modules/@esbuild/win32-x64": { + "version": "0.19.12", + "resolved": "https://registry.npmjs.org/@esbuild/win32-x64/-/win32-x64-0.19.12.tgz", + "integrity": "sha512-T1QyPSDCyMXaO3pzBkF96E8xMkiRYbUEZADd29SyPGabqxMViNoii+NcK7eWJAEoU6RZyEm5lVSIjTmcdoB9HA==", + "cpu": [ + "x64" + ], + "dev": true, + "optional": true, + "os": [ + "win32" + ], + "engines": { + "node": ">=12" + } + }, + "node_modules/@iarna/toml": { + "version": "2.2.5", + "resolved": "https://registry.npmjs.org/@iarna/toml/-/toml-2.2.5.tgz", + "integrity": "sha512-trnsAYxU3xnS1gPHPyU961coFyLkh4gAD/0zQ5mymY4yOZ+CYvsPqUbOFSw0aDM4y0tV7tiFxL/1XfXPNC6IPg==", + "dev": true + }, + "node_modules/@istanbuljs/load-nyc-config": { + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/@istanbuljs/load-nyc-config/-/load-nyc-config-1.1.0.tgz", + "integrity": "sha512-VjeHSlIzpv/NyD3N0YuHfXOPDIixcA1q2ZV98wsMqcYlPmv2n3Yb2lYP9XMElnaFVXg5A7YLTeLu6V84uQDjmQ==", + "dev": true, + "dependencies": { + "camelcase": "^5.3.1", + "find-up": "^4.1.0", + "get-package-type": "^0.1.0", + "js-yaml": "^3.13.1", + "resolve-from": "^5.0.0" + }, + "engines": { + "node": ">=8" + } + }, + "node_modules/@istanbuljs/load-nyc-config/node_modules/argparse": { + "version": "1.0.10", + "resolved": "https://registry.npmjs.org/argparse/-/argparse-1.0.10.tgz", + "integrity": "sha512-o5Roy6tNG4SL/FOkCAN6RzjiakZS25RLYFrcMttJqbdd8BWrnA+fGz57iN5Pb06pvBGvl5gQ0B48dJlslXvoTg==", + "dev": true, + "dependencies": { + "sprintf-js": "~1.0.2" + } + }, + "node_modules/@istanbuljs/load-nyc-config/node_modules/find-up": { + "version": "4.1.0", + "resolved": "https://registry.npmjs.org/find-up/-/find-up-4.1.0.tgz", + "integrity": "sha512-PpOwAdQ/YlXQ2vj8a3h8IipDuYRi3wceVQQGYWxNINccq40Anw7BlsEXCMbt1Zt+OLA6Fq9suIpIWD0OsnISlw==", + "dev": true, + "dependencies": { + "locate-path": "^5.0.0", + "path-exists": "^4.0.0" + }, + "engines": { + "node": ">=8" + } + }, + "node_modules/@istanbuljs/load-nyc-config/node_modules/js-yaml": { + "version": "3.14.1", + "resolved": "https://registry.npmjs.org/js-yaml/-/js-yaml-3.14.1.tgz", + "integrity": "sha512-okMH7OXXJ7YrN9Ok3/SXrnu4iX9yOk+25nqX4imS2npuvTYDmo/QEZoqwZkYaIDk3jVvBOTOIEgEhaLOynBS9g==", + "dev": true, + "dependencies": { + "argparse": "^1.0.7", + "esprima": "^4.0.0" + }, + "bin": { + "js-yaml": "bin/js-yaml.js" + } + }, + "node_modules/@istanbuljs/load-nyc-config/node_modules/locate-path": { + "version": "5.0.0", + "resolved": "https://registry.npmjs.org/locate-path/-/locate-path-5.0.0.tgz", + "integrity": "sha512-t7hw9pI+WvuwNJXwk5zVHpyhIqzg2qTlklJOf0mVxGSbe3Fp2VieZcduNYjaLDoy6p9uGpQEGWG87WpMKlNq8g==", + "dev": true, + "dependencies": { + "p-locate": "^4.1.0" + }, + "engines": { + "node": ">=8" + } + }, + "node_modules/@istanbuljs/load-nyc-config/node_modules/p-limit": { + "version": "2.3.0", + "resolved": "https://registry.npmjs.org/p-limit/-/p-limit-2.3.0.tgz", + "integrity": "sha512-//88mFWSJx8lxCzwdAABTJL2MyWB12+eIY7MDL2SqLmAkeKU9qxRvWuSyTjm3FUmpBEMuFfckAIqEaVGUDxb6w==", + "dev": true, + "dependencies": { + "p-try": "^2.0.0" + }, + "engines": { + "node": ">=6" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/@istanbuljs/load-nyc-config/node_modules/p-locate": { + "version": "4.1.0", + "resolved": "https://registry.npmjs.org/p-locate/-/p-locate-4.1.0.tgz", + "integrity": "sha512-R79ZZ/0wAxKGu3oYMlz8jy/kbhsNrS7SKZ7PxEHBgJ5+F2mtFW2fK2cOtBh1cHYkQsbzFV7I+EoRKe6Yt0oK7A==", + "dev": true, + "dependencies": { + "p-limit": "^2.2.0" + }, + "engines": { + "node": ">=8" + } + }, + "node_modules/@istanbuljs/nyc-config-typescript": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/@istanbuljs/nyc-config-typescript/-/nyc-config-typescript-1.0.2.tgz", + "integrity": "sha512-iKGIyMoyJuFnJRSVTZ78POIRvNnwZaWIf8vG4ZS3rQq58MMDrqEX2nnzx0R28V2X8JvmKYiqY9FP2hlJsm8A0w==", + "dev": true, + "dependencies": { + "@istanbuljs/schema": "^0.1.2" + }, + "engines": { + "node": ">=8" + }, + "peerDependencies": { "nyc": ">=15" } }, @@ -708,9 +997,9 @@ "dev": true }, "node_modules/@jridgewell/trace-mapping": { - "version": "0.3.19", - "resolved": "https://registry.npmjs.org/@jridgewell/trace-mapping/-/trace-mapping-0.3.19.tgz", - "integrity": "sha512-kf37QtfW+Hwx/buWGMPcR60iF9ziHa6r/CZJIHbmcm4+0qrXiVdxegAH0F6yddEVQ7zdkjcGCgCzUu+BcbhQxw==", + "version": "0.3.22", + "resolved": "https://registry.npmjs.org/@jridgewell/trace-mapping/-/trace-mapping-0.3.22.tgz", + "integrity": "sha512-Wf963MzWtA2sjrNt+g18IAln9lKnlRp+K2eH4jjIoF1wYeq3aMREpG09xhlhdzS0EjwU7qmUJYangWa+151vZw==", "dev": true, "dependencies": { "@jridgewell/resolve-uri": "^3.1.0", @@ -718,10 +1007,13 @@ } }, "node_modules/@ljharb/through": { - "version": "2.3.9", - "resolved": "https://registry.npmjs.org/@ljharb/through/-/through-2.3.9.tgz", - "integrity": "sha512-yN599ZBuMPPK4tdoToLlvgJB4CLK8fGl7ntfy0Wn7U6ttNvHYurd81bfUiK/6sMkiIwm65R6ck4L6+Y3DfVbNQ==", + "version": "2.3.12", + "resolved": "https://registry.npmjs.org/@ljharb/through/-/through-2.3.12.tgz", + "integrity": "sha512-ajo/heTlG3QgC8EGP6APIejksVAYt4ayz4tqoP3MolFELzcH1x1fzwEYRJTPO0IELutZ5HQ0c26/GqAYy79u3g==", "dev": true, + "dependencies": { + "call-bind": "^1.0.5" + }, "engines": { "node": ">= 0.4" } @@ -762,194 +1054,158 @@ } }, "node_modules/@octokit/auth-token": { - "version": "3.0.4", - "resolved": "https://registry.npmjs.org/@octokit/auth-token/-/auth-token-3.0.4.tgz", - "integrity": "sha512-TWFX7cZF2LXoCvdmJWY7XVPi74aSY0+FfBZNSXEXFkMpjcqsQwDSYVv5FhRFaI0V1ECnwbz4j59T/G+rXNWaIQ==", + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/@octokit/auth-token/-/auth-token-4.0.0.tgz", + "integrity": "sha512-tY/msAuJo6ARbK6SPIxZrPBms3xPbfwBrulZe0Wtr/DIY9lje2HeV1uoebShn6mx7SjCHif6EjMvoREj+gZ+SA==", "dev": true, "engines": { - "node": ">= 14" + "node": ">= 18" } }, "node_modules/@octokit/core": { - "version": "4.2.4", - "resolved": "https://registry.npmjs.org/@octokit/core/-/core-4.2.4.tgz", - "integrity": "sha512-rYKilwgzQ7/imScn3M9/pFfUf4I1AZEH3KhyJmtPdE2zfaXAn2mFfUy4FbKewzc2We5y/LlKLj36fWJLKC2SIQ==", + "version": "5.1.0", + "resolved": "https://registry.npmjs.org/@octokit/core/-/core-5.1.0.tgz", + "integrity": "sha512-BDa2VAMLSh3otEiaMJ/3Y36GU4qf6GI+VivQ/P41NC6GHcdxpKlqV0ikSZ5gdQsmS3ojXeRx5vasgNTinF0Q4g==", "dev": true, "dependencies": { - "@octokit/auth-token": "^3.0.0", - "@octokit/graphql": "^5.0.0", - "@octokit/request": "^6.0.0", - "@octokit/request-error": "^3.0.0", - "@octokit/types": "^9.0.0", + "@octokit/auth-token": "^4.0.0", + "@octokit/graphql": "^7.0.0", + "@octokit/request": "^8.0.2", + "@octokit/request-error": "^5.0.0", + "@octokit/types": "^12.0.0", "before-after-hook": "^2.2.0", "universal-user-agent": "^6.0.0" }, "engines": { - "node": ">= 14" + "node": ">= 18" } }, "node_modules/@octokit/endpoint": { - "version": "7.0.6", - "resolved": "https://registry.npmjs.org/@octokit/endpoint/-/endpoint-7.0.6.tgz", - "integrity": "sha512-5L4fseVRUsDFGR00tMWD/Trdeeihn999rTMGRMC1G/Ldi1uWlWJzI98H4Iak5DB/RVvQuyMYKqSK/R6mbSOQyg==", + "version": "9.0.4", + "resolved": "https://registry.npmjs.org/@octokit/endpoint/-/endpoint-9.0.4.tgz", + "integrity": "sha512-DWPLtr1Kz3tv8L0UvXTDP1fNwM0S+z6EJpRcvH66orY6Eld4XBMCSYsaWp4xIm61jTWxK68BrR7ibO+vSDnZqw==", "dev": true, "dependencies": { - "@octokit/types": "^9.0.0", - "is-plain-object": "^5.0.0", + "@octokit/types": "^12.0.0", "universal-user-agent": "^6.0.0" }, "engines": { - "node": ">= 14" + "node": ">= 18" } }, "node_modules/@octokit/graphql": { - "version": "5.0.6", - "resolved": "https://registry.npmjs.org/@octokit/graphql/-/graphql-5.0.6.tgz", - "integrity": "sha512-Fxyxdy/JH0MnIB5h+UQ3yCoh1FG4kWXfFKkpWqjZHw/p+Kc8Y44Hu/kCgNBT6nU1shNumEchmW/sUO1JuQnPcw==", + "version": "7.0.2", + "resolved": "https://registry.npmjs.org/@octokit/graphql/-/graphql-7.0.2.tgz", + "integrity": "sha512-OJ2iGMtj5Tg3s6RaXH22cJcxXRi7Y3EBqbHTBRq+PQAqfaS8f/236fUrWhfSn8P4jovyzqucxme7/vWSSZBX2Q==", "dev": true, "dependencies": { - "@octokit/request": "^6.0.0", - "@octokit/types": "^9.0.0", + "@octokit/request": "^8.0.1", + "@octokit/types": "^12.0.0", "universal-user-agent": "^6.0.0" }, "engines": { - "node": ">= 14" + "node": ">= 18" } }, "node_modules/@octokit/openapi-types": { - "version": "18.0.0", - "resolved": "https://registry.npmjs.org/@octokit/openapi-types/-/openapi-types-18.0.0.tgz", - "integrity": "sha512-V8GImKs3TeQRxRtXFpG2wl19V7444NIOTDF24AWuIbmNaNYOQMWRbjcGDXV5B+0n887fgDcuMNOmlul+k+oJtw==", + "version": "19.1.0", + "resolved": "https://registry.npmjs.org/@octokit/openapi-types/-/openapi-types-19.1.0.tgz", + "integrity": "sha512-6G+ywGClliGQwRsjvqVYpklIfa7oRPA0vyhPQG/1Feh+B+wU0vGH1JiJ5T25d3g1JZYBHzR2qefLi9x8Gt+cpw==", "dev": true }, "node_modules/@octokit/plugin-paginate-rest": { - "version": "6.1.2", - "resolved": "https://registry.npmjs.org/@octokit/plugin-paginate-rest/-/plugin-paginate-rest-6.1.2.tgz", - "integrity": "sha512-qhrmtQeHU/IivxucOV1bbI/xZyC/iOBhclokv7Sut5vnejAIAEXVcGQeRpQlU39E0WwK9lNvJHphHri/DB6lbQ==", + "version": "9.1.5", + "resolved": "https://registry.npmjs.org/@octokit/plugin-paginate-rest/-/plugin-paginate-rest-9.1.5.tgz", + "integrity": "sha512-WKTQXxK+bu49qzwv4qKbMMRXej1DU2gq017euWyKVudA6MldaSSQuxtz+vGbhxV4CjxpUxjZu6rM2wfc1FiWVg==", "dev": true, "dependencies": { - "@octokit/tsconfig": "^1.0.2", - "@octokit/types": "^9.2.3" + "@octokit/types": "^12.4.0" }, "engines": { - "node": ">= 14" + "node": ">= 18" }, "peerDependencies": { - "@octokit/core": ">=4" + "@octokit/core": ">=5" } }, "node_modules/@octokit/plugin-request-log": { - "version": "1.0.4", - "resolved": "https://registry.npmjs.org/@octokit/plugin-request-log/-/plugin-request-log-1.0.4.tgz", - "integrity": "sha512-mLUsMkgP7K/cnFEw07kWqXGF5LKrOkD+lhCrKvPHXWDywAwuDUeDwWBpc69XK3pNX0uKiVt8g5z96PJ6z9xCFA==", + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/@octokit/plugin-request-log/-/plugin-request-log-4.0.0.tgz", + "integrity": "sha512-2uJI1COtYCq8Z4yNSnM231TgH50bRkheQ9+aH8TnZanB6QilOnx8RMD2qsnamSOXtDj0ilxvevf5fGsBhBBzKA==", "dev": true, + "engines": { + "node": ">= 18" + }, "peerDependencies": { - "@octokit/core": ">=3" + "@octokit/core": ">=5" } }, "node_modules/@octokit/plugin-rest-endpoint-methods": { - "version": "7.2.3", - "resolved": "https://registry.npmjs.org/@octokit/plugin-rest-endpoint-methods/-/plugin-rest-endpoint-methods-7.2.3.tgz", - "integrity": "sha512-I5Gml6kTAkzVlN7KCtjOM+Ruwe/rQppp0QU372K1GP7kNOYEKe8Xn5BW4sE62JAHdwpq95OQK/qGNyKQMUzVgA==", + "version": "10.2.0", + "resolved": "https://registry.npmjs.org/@octokit/plugin-rest-endpoint-methods/-/plugin-rest-endpoint-methods-10.2.0.tgz", + "integrity": "sha512-ePbgBMYtGoRNXDyKGvr9cyHjQ163PbwD0y1MkDJCpkO2YH4OeXX40c4wYHKikHGZcpGPbcRLuy0unPUuafco8Q==", "dev": true, "dependencies": { - "@octokit/types": "^10.0.0" + "@octokit/types": "^12.3.0" }, "engines": { - "node": ">= 14" + "node": ">= 18" }, "peerDependencies": { - "@octokit/core": ">=3" - } - }, - "node_modules/@octokit/plugin-rest-endpoint-methods/node_modules/@octokit/types": { - "version": "10.0.0", - "resolved": "https://registry.npmjs.org/@octokit/types/-/types-10.0.0.tgz", - "integrity": "sha512-Vm8IddVmhCgU1fxC1eyinpwqzXPEYu0NrYzD3YZjlGjyftdLBTeqNblRC0jmJmgxbJIsQlyogVeGnrNaaMVzIg==", - "dev": true, - "dependencies": { - "@octokit/openapi-types": "^18.0.0" + "@octokit/core": ">=5" } }, "node_modules/@octokit/request": { - "version": "6.2.8", - "resolved": "https://registry.npmjs.org/@octokit/request/-/request-6.2.8.tgz", - "integrity": "sha512-ow4+pkVQ+6XVVsekSYBzJC0VTVvh/FCTUUgTsboGq+DTeWdyIFV8WSCdo0RIxk6wSkBTHqIK1mYuY7nOBXOchw==", + "version": "8.1.6", + "resolved": "https://registry.npmjs.org/@octokit/request/-/request-8.1.6.tgz", + "integrity": "sha512-YhPaGml3ncZC1NfXpP3WZ7iliL1ap6tLkAp6MvbK2fTTPytzVUyUesBBogcdMm86uRYO5rHaM1xIWxigWZ17MQ==", "dev": true, "dependencies": { - "@octokit/endpoint": "^7.0.0", - "@octokit/request-error": "^3.0.0", - "@octokit/types": "^9.0.0", - "is-plain-object": "^5.0.0", - "node-fetch": "^2.6.7", + "@octokit/endpoint": "^9.0.0", + "@octokit/request-error": "^5.0.0", + "@octokit/types": "^12.0.0", "universal-user-agent": "^6.0.0" }, "engines": { - "node": ">= 14" + "node": ">= 18" } }, "node_modules/@octokit/request-error": { - "version": "3.0.3", - "resolved": "https://registry.npmjs.org/@octokit/request-error/-/request-error-3.0.3.tgz", - "integrity": "sha512-crqw3V5Iy2uOU5Np+8M/YexTlT8zxCfI+qu+LxUB7SZpje4Qmx3mub5DfEKSO8Ylyk0aogi6TYdf6kxzh2BguQ==", + "version": "5.0.1", + "resolved": "https://registry.npmjs.org/@octokit/request-error/-/request-error-5.0.1.tgz", + "integrity": "sha512-X7pnyTMV7MgtGmiXBwmO6M5kIPrntOXdyKZLigNfQWSEQzVxR4a4vo49vJjTWX70mPndj8KhfT4Dx+2Ng3vnBQ==", "dev": true, "dependencies": { - "@octokit/types": "^9.0.0", + "@octokit/types": "^12.0.0", "deprecation": "^2.0.0", "once": "^1.4.0" }, "engines": { - "node": ">= 14" - } - }, - "node_modules/@octokit/request/node_modules/node-fetch": { - "version": "2.7.0", - "resolved": "https://registry.npmjs.org/node-fetch/-/node-fetch-2.7.0.tgz", - "integrity": "sha512-c4FRfUm/dbcWZ7U+1Wq0AwCyFL+3nt2bEw05wfxSz+DWpWsitgmSgYmy2dQdWyKC1694ELPqMs/YzUSNozLt8A==", - "dev": true, - "dependencies": { - "whatwg-url": "^5.0.0" - }, - "engines": { - "node": "4.x || >=6.0.0" - }, - "peerDependencies": { - "encoding": "^0.1.0" - }, - "peerDependenciesMeta": { - "encoding": { - "optional": true - } + "node": ">= 18" } }, "node_modules/@octokit/rest": { - "version": "19.0.13", - "resolved": "https://registry.npmjs.org/@octokit/rest/-/rest-19.0.13.tgz", - "integrity": "sha512-/EzVox5V9gYGdbAI+ovYj3nXQT1TtTHRT+0eZPcuC05UFSWO3mdO9UY1C0i2eLF9Un1ONJkAk+IEtYGAC+TahA==", + "version": "20.0.2", + "resolved": "https://registry.npmjs.org/@octokit/rest/-/rest-20.0.2.tgz", + "integrity": "sha512-Ux8NDgEraQ/DMAU1PlAohyfBBXDwhnX2j33Z1nJNziqAfHi70PuxkFYIcIt8aIAxtRE7KVuKp8lSR8pA0J5iOQ==", "dev": true, "dependencies": { - "@octokit/core": "^4.2.1", - "@octokit/plugin-paginate-rest": "^6.1.2", - "@octokit/plugin-request-log": "^1.0.4", - "@octokit/plugin-rest-endpoint-methods": "^7.1.2" + "@octokit/core": "^5.0.0", + "@octokit/plugin-paginate-rest": "^9.0.0", + "@octokit/plugin-request-log": "^4.0.0", + "@octokit/plugin-rest-endpoint-methods": "^10.0.0" }, "engines": { - "node": ">= 14" + "node": ">= 18" } }, - "node_modules/@octokit/tsconfig": { - "version": "1.0.2", - "resolved": "https://registry.npmjs.org/@octokit/tsconfig/-/tsconfig-1.0.2.tgz", - "integrity": "sha512-I0vDR0rdtP8p2lGMzvsJzbhdOWy405HcGovrspJ8RRibHnyRgggUSNO5AIox5LmqiwmatHKYsvj6VGFHkqS7lA==", - "dev": true - }, "node_modules/@octokit/types": { - "version": "9.3.2", - "resolved": "https://registry.npmjs.org/@octokit/types/-/types-9.3.2.tgz", - "integrity": "sha512-D4iHGTdAnEEVsB8fl95m1hiz7D5YiRdQ9b/OEb3BYRVwbLsGHcRVPz+u+BgRLNk0Q0/4iZCBqDN96j2XNxfXrA==", + "version": "12.4.0", + "resolved": "https://registry.npmjs.org/@octokit/types/-/types-12.4.0.tgz", + "integrity": "sha512-FLWs/AvZllw/AGVs+nJ+ELCDZZJk+kY0zMen118xhL2zD0s1etIUHm1odgjP7epxYU1ln7SZxEUWYop5bhsdgQ==", "dev": true, "dependencies": { - "@octokit/openapi-types": "^18.0.0" + "@octokit/openapi-types": "^19.1.0" } }, "node_modules/@pnpm/config.env-replace": { @@ -1011,517 +1267,152 @@ }, "node_modules/@redis/search": { "resolved": "packages/search", - "link": true - }, - "node_modules/@redis/test-utils": { - "resolved": "packages/test-utils", - "link": true - }, - "node_modules/@redis/time-series": { - "resolved": "packages/time-series", - "link": true - }, - "node_modules/@sindresorhus/is": { - "version": "5.6.0", - "resolved": "https://registry.npmjs.org/@sindresorhus/is/-/is-5.6.0.tgz", - "integrity": "sha512-TV7t8GKYaJWsn00tFDqBw8+Uqmr8A0fRU1tvTQhyZzGv0sJCGRQL3JGMI3ucuKo3XIZdUP+Lx7/gh2t3lewy7g==", - "dev": true, - "engines": { - "node": ">=14.16" - }, - "funding": { - "url": "https://github.com/sindresorhus/is?sponsor=1" - } - }, - "node_modules/@sinonjs/commons": { - "version": "3.0.0", - "resolved": "https://registry.npmjs.org/@sinonjs/commons/-/commons-3.0.0.tgz", - "integrity": "sha512-jXBtWAF4vmdNmZgD5FoKsVLv3rPgDnLgPbU84LIJ3otV44vJlDRokVng5v8NFJdCf/da9legHcKaRuZs4L7faA==", - "dev": true, - "dependencies": { - "type-detect": "4.0.8" - } - }, - "node_modules/@sinonjs/fake-timers": { - "version": "10.3.0", - "resolved": "https://registry.npmjs.org/@sinonjs/fake-timers/-/fake-timers-10.3.0.tgz", - "integrity": "sha512-V4BG07kuYSUkTCSBHG8G8TNhM+F19jXFWnQtzj+we8DrkpSBCee9Z3Ms8yiGer/dlmhe35/Xdgyo3/0rQKg7YA==", - "dev": true, - "dependencies": { - "@sinonjs/commons": "^3.0.0" - } - }, - "node_modules/@sinonjs/samsam": { - "version": "8.0.0", - "resolved": "https://registry.npmjs.org/@sinonjs/samsam/-/samsam-8.0.0.tgz", - "integrity": "sha512-Bp8KUVlLp8ibJZrnvq2foVhP0IVX2CIprMJPK0vqGqgrDa0OHVKeZyBykqskkrdxV6yKBPmGasO8LVjAKR3Gew==", - "dev": true, - "dependencies": { - "@sinonjs/commons": "^2.0.0", - "lodash.get": "^4.4.2", - "type-detect": "^4.0.8" - } - }, - "node_modules/@sinonjs/samsam/node_modules/@sinonjs/commons": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/@sinonjs/commons/-/commons-2.0.0.tgz", - "integrity": "sha512-uLa0j859mMrg2slwQYdO/AkrOfmH+X6LTVmNTS9CqexuE2IvVORIkSpJLqePAbEnKJ77aMmCwr1NUZ57120Xcg==", - "dev": true, - "dependencies": { - "type-detect": "4.0.8" - } - }, - "node_modules/@sinonjs/text-encoding": { - "version": "0.7.2", - "resolved": "https://registry.npmjs.org/@sinonjs/text-encoding/-/text-encoding-0.7.2.tgz", - "integrity": "sha512-sXXKG+uL9IrKqViTtao2Ws6dy0znu9sOaP1di/jKGW1M6VssO8vlpXCQcpZ+jisQ1tTFAC5Jo/EOzFbggBagFQ==", - "dev": true - }, - "node_modules/@szmarczak/http-timer": { - "version": "5.0.1", - "resolved": "https://registry.npmjs.org/@szmarczak/http-timer/-/http-timer-5.0.1.tgz", - "integrity": "sha512-+PmQX0PiAYPMeVYe237LJAYvOMYW1j2rH5YROyS3b4CTVJum34HfRvKvAzozHAQG0TnHNdUfY9nCeUyRAs//cw==", - "dev": true, - "dependencies": { - "defer-to-connect": "^2.0.1" - }, - "engines": { - "node": ">=14.16" - } - }, - "node_modules/@tootallnate/quickjs-emscripten": { - "version": "0.23.0", - "resolved": "https://registry.npmjs.org/@tootallnate/quickjs-emscripten/-/quickjs-emscripten-0.23.0.tgz", - "integrity": "sha512-C5Mc6rdnsaJDjO3UpGW/CQTHtCKaYlScZTly4JIu97Jxo/odCiH0ITnDXSJPTOrEKk/ycSZ0AOgTmkDtkOsvIA==", - "dev": true - }, - "node_modules/@tsconfig/node10": { - "version": "1.0.9", - "resolved": "https://registry.npmjs.org/@tsconfig/node10/-/node10-1.0.9.tgz", - "integrity": "sha512-jNsYVVxU8v5g43Erja32laIDHXeoNvFEpX33OK4d6hljo3jDhCBDhx5dhCCTMWUojscpAagGiRkBKxpdl9fxqA==", - "dev": true - }, - "node_modules/@tsconfig/node12": { - "version": "1.0.11", - "resolved": "https://registry.npmjs.org/@tsconfig/node12/-/node12-1.0.11.tgz", - "integrity": "sha512-cqefuRsh12pWyGsIoBKJA9luFu3mRxCA+ORZvA4ktLSzIuCUtWVxGIuXigEwO5/ywWFMZ2QEGKWvkZG1zDMTag==", - "dev": true - }, - "node_modules/@tsconfig/node14": { - "version": "14.1.0", - "resolved": "https://registry.npmjs.org/@tsconfig/node14/-/node14-14.1.0.tgz", - "integrity": "sha512-VmsCG04YR58ciHBeJKBDNMWWfYbyP8FekWVuTlpstaUPlat1D0x/tXzkWP7yCMU0eSz9V4OZU0LBWTFJ3xZf6w==", - "dev": true - }, - "node_modules/@tsconfig/node16": { - "version": "1.0.4", - "resolved": "https://registry.npmjs.org/@tsconfig/node16/-/node16-1.0.4.tgz", - "integrity": "sha512-vxhUy4J8lyeyinH7Azl1pdd43GJhZH/tP2weN8TntQblOY+A0XbT8DJk1/oCPuOOyg/Ja757rG0CgHcWC8OfMA==", - "dev": true - }, - "node_modules/@types/http-cache-semantics": { - "version": "4.0.2", - "resolved": "https://registry.npmjs.org/@types/http-cache-semantics/-/http-cache-semantics-4.0.2.tgz", - "integrity": "sha512-FD+nQWA2zJjh4L9+pFXqWOi0Hs1ryBCfI+985NjluQ1p8EYtoLvjLOKidXBtZ4/IcxDX4o8/E8qDS3540tNliw==", - "dev": true - }, - "node_modules/@types/json-schema": { - "version": "7.0.13", - "resolved": "https://registry.npmjs.org/@types/json-schema/-/json-schema-7.0.13.tgz", - "integrity": "sha512-RbSSoHliUbnXj3ny0CNFOoxrIDV6SUGyStHsvDqosw6CkdPV8TtWGlfecuK4ToyMEAql6pzNxgCFKanovUzlgQ==", - "dev": true - }, - "node_modules/@types/mocha": { - "version": "10.0.1", - "resolved": "https://registry.npmjs.org/@types/mocha/-/mocha-10.0.1.tgz", - "integrity": "sha512-/fvYntiO1GeICvqbQ3doGDIP97vWmvFt83GKguJ6prmQM2iXZfFcq6YE8KteFyRtX2/h5Hf91BYvPodJKFYv5Q==", - "dev": true - }, - "node_modules/@types/node": { - "version": "20.6.2", - "resolved": "https://registry.npmjs.org/@types/node/-/node-20.6.2.tgz", - "integrity": "sha512-Y+/1vGBHV/cYk6OI1Na/LHzwnlNCAfU3ZNGrc1LdRe/LAIbdDPTTv/HU3M7yXN448aTVDq3eKRm2cg7iKLb8gw==", - "dev": true - }, - "node_modules/@types/semver": { - "version": "7.5.2", - "resolved": "https://registry.npmjs.org/@types/semver/-/semver-7.5.2.tgz", - "integrity": "sha512-7aqorHYgdNO4DM36stTiGO3DvKoex9TQRwsJU6vMaFGyqpBA1MNZkz+PG3gaNUPpTAOYhT1WR7M1JyA3fbS9Cw==", - "dev": true - }, - "node_modules/@types/sinon": { - "version": "10.0.16", - "resolved": "https://registry.npmjs.org/@types/sinon/-/sinon-10.0.16.tgz", - "integrity": "sha512-j2Du5SYpXZjJVJtXBokASpPRj+e2z+VUhCPHmM6WMfe3dpHu6iVKJMU6AiBcMp/XTAYnEj6Wc1trJUWwZ0QaAQ==", - "dev": true, - "dependencies": { - "@types/sinonjs__fake-timers": "*" - } - }, - "node_modules/@types/sinonjs__fake-timers": { - "version": "8.1.2", - "resolved": "https://registry.npmjs.org/@types/sinonjs__fake-timers/-/sinonjs__fake-timers-8.1.2.tgz", - "integrity": "sha512-9GcLXF0/v3t80caGs5p2rRfkB+a8VBGLJZVih6CNFkx8IZ994wiKKLSRs9nuFwk1HevWs/1mnUmkApGrSGsShA==", - "dev": true - }, - "node_modules/@types/yallist": { - "version": "4.0.1", - "resolved": "https://registry.npmjs.org/@types/yallist/-/yallist-4.0.1.tgz", - "integrity": "sha512-G3FNJfaYtN8URU6wd6+uwFI62KO79j7n3XTYcwcFncP8gkfoi0b821GoVVt0oqKVnCqKYOMNKIGpakPoFhzAGA==", - "dev": true - }, - "node_modules/@types/yargs": { - "version": "17.0.24", - "resolved": "https://registry.npmjs.org/@types/yargs/-/yargs-17.0.24.tgz", - "integrity": "sha512-6i0aC7jV6QzQB8ne1joVZ0eSFIstHsCrobmOtghM11yGlH0j43FKL2UhWdELkyps0zuf7qVTUVCCR+tgSlyLLw==", - "dev": true, - "dependencies": { - "@types/yargs-parser": "*" - } - }, - "node_modules/@types/yargs-parser": { - "version": "21.0.0", - "resolved": "https://registry.npmjs.org/@types/yargs-parser/-/yargs-parser-21.0.0.tgz", - "integrity": "sha512-iO9ZQHkZxHn4mSakYV0vFHAVDyEOIJQrV2uZ06HxEPcx+mt8swXoZHIbaaJ2crJYFfErySgktuTZ3BeLz+XmFA==", - "dev": true - }, - "node_modules/@typescript-eslint/eslint-plugin": { - "version": "6.7.2", - "resolved": "https://registry.npmjs.org/@typescript-eslint/eslint-plugin/-/eslint-plugin-6.7.2.tgz", - "integrity": "sha512-ooaHxlmSgZTM6CHYAFRlifqh1OAr3PAQEwi7lhYhaegbnXrnh7CDcHmc3+ihhbQC7H0i4JF0psI5ehzkF6Yl6Q==", - "dev": true, - "dependencies": { - "@eslint-community/regexpp": "^4.5.1", - "@typescript-eslint/scope-manager": "6.7.2", - "@typescript-eslint/type-utils": "6.7.2", - "@typescript-eslint/utils": "6.7.2", - "@typescript-eslint/visitor-keys": "6.7.2", - "debug": "^4.3.4", - "graphemer": "^1.4.0", - "ignore": "^5.2.4", - "natural-compare": "^1.4.0", - "semver": "^7.5.4", - "ts-api-utils": "^1.0.1" - }, - "engines": { - "node": "^16.0.0 || >=18.0.0" - }, - "funding": { - "type": "opencollective", - "url": "https://opencollective.com/typescript-eslint" - }, - "peerDependencies": { - "@typescript-eslint/parser": "^6.0.0 || ^6.0.0-alpha", - "eslint": "^7.0.0 || ^8.0.0" - }, - "peerDependenciesMeta": { - "typescript": { - "optional": true - } - } - }, - "node_modules/@typescript-eslint/eslint-plugin/node_modules/lru-cache": { - "version": "6.0.0", - "resolved": "https://registry.npmjs.org/lru-cache/-/lru-cache-6.0.0.tgz", - "integrity": "sha512-Jo6dJ04CmSjuznwJSS3pUeWmd/H0ffTlkXXgwZi+eq1UCmqQwCh+eLsYOYCwY991i2Fah4h1BEMCx4qThGbsiA==", - "dev": true, - "dependencies": { - "yallist": "^4.0.0" - }, - "engines": { - "node": ">=10" - } - }, - "node_modules/@typescript-eslint/eslint-plugin/node_modules/semver": { - "version": "7.5.4", - "resolved": "https://registry.npmjs.org/semver/-/semver-7.5.4.tgz", - "integrity": "sha512-1bCSESV6Pv+i21Hvpxp3Dx+pSD8lIPt8uVjRrxAUt/nbswYc+tK6Y2btiULjd4+fnq15PX+nqQDC7Oft7WkwcA==", - "dev": true, - "dependencies": { - "lru-cache": "^6.0.0" - }, - "bin": { - "semver": "bin/semver.js" - }, - "engines": { - "node": ">=10" - } - }, - "node_modules/@typescript-eslint/parser": { - "version": "6.7.2", - "resolved": "https://registry.npmjs.org/@typescript-eslint/parser/-/parser-6.7.2.tgz", - "integrity": "sha512-KA3E4ox0ws+SPyxQf9iSI25R6b4Ne78ORhNHeVKrPQnoYsb9UhieoiRoJgrzgEeKGOXhcY1i8YtOeCHHTDa6Fw==", - "dev": true, - "dependencies": { - "@typescript-eslint/scope-manager": "6.7.2", - "@typescript-eslint/types": "6.7.2", - "@typescript-eslint/typescript-estree": "6.7.2", - "@typescript-eslint/visitor-keys": "6.7.2", - "debug": "^4.3.4" - }, - "engines": { - "node": "^16.0.0 || >=18.0.0" - }, - "funding": { - "type": "opencollective", - "url": "https://opencollective.com/typescript-eslint" - }, - "peerDependencies": { - "eslint": "^7.0.0 || ^8.0.0" - }, - "peerDependenciesMeta": { - "typescript": { - "optional": true - } - } - }, - "node_modules/@typescript-eslint/scope-manager": { - "version": "6.7.2", - "resolved": "https://registry.npmjs.org/@typescript-eslint/scope-manager/-/scope-manager-6.7.2.tgz", - "integrity": "sha512-bgi6plgyZjEqapr7u2mhxGR6E8WCzKNUFWNh6fkpVe9+yzRZeYtDTbsIBzKbcxI+r1qVWt6VIoMSNZ4r2A+6Yw==", - "dev": true, - "dependencies": { - "@typescript-eslint/types": "6.7.2", - "@typescript-eslint/visitor-keys": "6.7.2" - }, - "engines": { - "node": "^16.0.0 || >=18.0.0" - }, - "funding": { - "type": "opencollective", - "url": "https://opencollective.com/typescript-eslint" - } + "link": true }, - "node_modules/@typescript-eslint/type-utils": { - "version": "6.7.2", - "resolved": "https://registry.npmjs.org/@typescript-eslint/type-utils/-/type-utils-6.7.2.tgz", - "integrity": "sha512-36F4fOYIROYRl0qj95dYKx6kybddLtsbmPIYNK0OBeXv2j9L5nZ17j9jmfy+bIDHKQgn2EZX+cofsqi8NPATBQ==", - "dev": true, - "dependencies": { - "@typescript-eslint/typescript-estree": "6.7.2", - "@typescript-eslint/utils": "6.7.2", - "debug": "^4.3.4", - "ts-api-utils": "^1.0.1" - }, - "engines": { - "node": "^16.0.0 || >=18.0.0" - }, - "funding": { - "type": "opencollective", - "url": "https://opencollective.com/typescript-eslint" - }, - "peerDependencies": { - "eslint": "^7.0.0 || ^8.0.0" - }, - "peerDependenciesMeta": { - "typescript": { - "optional": true - } - } + "node_modules/@redis/test-utils": { + "resolved": "packages/test-utils", + "link": true }, - "node_modules/@typescript-eslint/types": { - "version": "6.7.2", - "resolved": "https://registry.npmjs.org/@typescript-eslint/types/-/types-6.7.2.tgz", - "integrity": "sha512-flJYwMYgnUNDAN9/GAI3l8+wTmvTYdv64fcH8aoJK76Y+1FCZ08RtI5zDerM/FYT5DMkAc+19E4aLmd5KqdFyg==", - "dev": true, - "engines": { - "node": "^16.0.0 || >=18.0.0" - }, - "funding": { - "type": "opencollective", - "url": "https://opencollective.com/typescript-eslint" - } + "node_modules/@redis/time-series": { + "resolved": "packages/time-series", + "link": true }, - "node_modules/@typescript-eslint/typescript-estree": { - "version": "6.7.2", - "resolved": "https://registry.npmjs.org/@typescript-eslint/typescript-estree/-/typescript-estree-6.7.2.tgz", - "integrity": "sha512-kiJKVMLkoSciGyFU0TOY0fRxnp9qq1AzVOHNeN1+B9erKFCJ4Z8WdjAkKQPP+b1pWStGFqezMLltxO+308dJTQ==", + "node_modules/@sindresorhus/is": { + "version": "5.6.0", + "resolved": "https://registry.npmjs.org/@sindresorhus/is/-/is-5.6.0.tgz", + "integrity": "sha512-TV7t8GKYaJWsn00tFDqBw8+Uqmr8A0fRU1tvTQhyZzGv0sJCGRQL3JGMI3ucuKo3XIZdUP+Lx7/gh2t3lewy7g==", "dev": true, - "dependencies": { - "@typescript-eslint/types": "6.7.2", - "@typescript-eslint/visitor-keys": "6.7.2", - "debug": "^4.3.4", - "globby": "^11.1.0", - "is-glob": "^4.0.3", - "semver": "^7.5.4", - "ts-api-utils": "^1.0.1" - }, "engines": { - "node": "^16.0.0 || >=18.0.0" + "node": ">=14.16" }, "funding": { - "type": "opencollective", - "url": "https://opencollective.com/typescript-eslint" - }, - "peerDependenciesMeta": { - "typescript": { - "optional": true - } - } - }, - "node_modules/@typescript-eslint/typescript-estree/node_modules/array-union": { - "version": "2.1.0", - "resolved": "https://registry.npmjs.org/array-union/-/array-union-2.1.0.tgz", - "integrity": "sha512-HGyxoOTYUyCM6stUe6EJgnd4EoewAI7zMdfqO+kGjnlZmBDz/cR5pf8r/cR4Wq60sL/p0IkcjUEEPwS3GFrIyw==", - "dev": true, - "engines": { - "node": ">=8" + "url": "https://github.com/sindresorhus/is?sponsor=1" } }, - "node_modules/@typescript-eslint/typescript-estree/node_modules/globby": { - "version": "11.1.0", - "resolved": "https://registry.npmjs.org/globby/-/globby-11.1.0.tgz", - "integrity": "sha512-jhIXaOzy1sb8IyocaruWSn1TjmnBVs8Ayhcy83rmxNJ8q2uWKCAj3CnJY+KpGSXCueAPc0i05kVvVKtP1t9S3g==", + "node_modules/@sindresorhus/merge-streams": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/@sindresorhus/merge-streams/-/merge-streams-1.0.0.tgz", + "integrity": "sha512-rUV5WyJrJLoloD4NDN1V1+LDMDWOa4OTsT4yYJwQNpTU6FWxkxHpL7eu4w+DmiH8x/EAM1otkPE1+LaspIbplw==", "dev": true, - "dependencies": { - "array-union": "^2.1.0", - "dir-glob": "^3.0.1", - "fast-glob": "^3.2.9", - "ignore": "^5.2.0", - "merge2": "^1.4.1", - "slash": "^3.0.0" - }, "engines": { - "node": ">=10" + "node": ">=18" }, "funding": { "url": "https://github.com/sponsors/sindresorhus" } }, - "node_modules/@typescript-eslint/typescript-estree/node_modules/lru-cache": { - "version": "6.0.0", - "resolved": "https://registry.npmjs.org/lru-cache/-/lru-cache-6.0.0.tgz", - "integrity": "sha512-Jo6dJ04CmSjuznwJSS3pUeWmd/H0ffTlkXXgwZi+eq1UCmqQwCh+eLsYOYCwY991i2Fah4h1BEMCx4qThGbsiA==", + "node_modules/@sinonjs/commons": { + "version": "3.0.1", + "resolved": "https://registry.npmjs.org/@sinonjs/commons/-/commons-3.0.1.tgz", + "integrity": "sha512-K3mCHKQ9sVh8o1C9cxkwxaOmXoAMlDxC1mYyHrjqOWEcBjYr76t96zL2zlj5dUGZ3HSw240X1qgH3Mjf1yJWpQ==", "dev": true, "dependencies": { - "yallist": "^4.0.0" - }, - "engines": { - "node": ">=10" + "type-detect": "4.0.8" } }, - "node_modules/@typescript-eslint/typescript-estree/node_modules/semver": { - "version": "7.5.4", - "resolved": "https://registry.npmjs.org/semver/-/semver-7.5.4.tgz", - "integrity": "sha512-1bCSESV6Pv+i21Hvpxp3Dx+pSD8lIPt8uVjRrxAUt/nbswYc+tK6Y2btiULjd4+fnq15PX+nqQDC7Oft7WkwcA==", + "node_modules/@sinonjs/fake-timers": { + "version": "11.2.2", + "resolved": "https://registry.npmjs.org/@sinonjs/fake-timers/-/fake-timers-11.2.2.tgz", + "integrity": "sha512-G2piCSxQ7oWOxwGSAyFHfPIsyeJGXYtc6mFbnFA+kRXkiEnTl8c/8jul2S329iFBnDI9HGoeWWAZvuvOkZccgw==", "dev": true, "dependencies": { - "lru-cache": "^6.0.0" - }, - "bin": { - "semver": "bin/semver.js" - }, - "engines": { - "node": ">=10" + "@sinonjs/commons": "^3.0.0" } }, - "node_modules/@typescript-eslint/typescript-estree/node_modules/slash": { - "version": "3.0.0", - "resolved": "https://registry.npmjs.org/slash/-/slash-3.0.0.tgz", - "integrity": "sha512-g9Q1haeby36OSStwb4ntCGGGaKsaVSjQ68fBxoQcutl5fS1vuY18H3wSt3jFyFtrkx+Kz0V1G85A4MyAdDMi2Q==", + "node_modules/@sinonjs/samsam": { + "version": "8.0.0", + "resolved": "https://registry.npmjs.org/@sinonjs/samsam/-/samsam-8.0.0.tgz", + "integrity": "sha512-Bp8KUVlLp8ibJZrnvq2foVhP0IVX2CIprMJPK0vqGqgrDa0OHVKeZyBykqskkrdxV6yKBPmGasO8LVjAKR3Gew==", "dev": true, - "engines": { - "node": ">=8" + "dependencies": { + "@sinonjs/commons": "^2.0.0", + "lodash.get": "^4.4.2", + "type-detect": "^4.0.8" } }, - "node_modules/@typescript-eslint/utils": { - "version": "6.7.2", - "resolved": "https://registry.npmjs.org/@typescript-eslint/utils/-/utils-6.7.2.tgz", - "integrity": "sha512-ZCcBJug/TS6fXRTsoTkgnsvyWSiXwMNiPzBUani7hDidBdj1779qwM1FIAmpH4lvlOZNF3EScsxxuGifjpLSWQ==", + "node_modules/@sinonjs/samsam/node_modules/@sinonjs/commons": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/@sinonjs/commons/-/commons-2.0.0.tgz", + "integrity": "sha512-uLa0j859mMrg2slwQYdO/AkrOfmH+X6LTVmNTS9CqexuE2IvVORIkSpJLqePAbEnKJ77aMmCwr1NUZ57120Xcg==", "dev": true, "dependencies": { - "@eslint-community/eslint-utils": "^4.4.0", - "@types/json-schema": "^7.0.12", - "@types/semver": "^7.5.0", - "@typescript-eslint/scope-manager": "6.7.2", - "@typescript-eslint/types": "6.7.2", - "@typescript-eslint/typescript-estree": "6.7.2", - "semver": "^7.5.4" - }, - "engines": { - "node": "^16.0.0 || >=18.0.0" - }, - "funding": { - "type": "opencollective", - "url": "https://opencollective.com/typescript-eslint" - }, - "peerDependencies": { - "eslint": "^7.0.0 || ^8.0.0" + "type-detect": "4.0.8" } }, - "node_modules/@typescript-eslint/utils/node_modules/lru-cache": { - "version": "6.0.0", - "resolved": "https://registry.npmjs.org/lru-cache/-/lru-cache-6.0.0.tgz", - "integrity": "sha512-Jo6dJ04CmSjuznwJSS3pUeWmd/H0ffTlkXXgwZi+eq1UCmqQwCh+eLsYOYCwY991i2Fah4h1BEMCx4qThGbsiA==", + "node_modules/@sinonjs/text-encoding": { + "version": "0.7.2", + "resolved": "https://registry.npmjs.org/@sinonjs/text-encoding/-/text-encoding-0.7.2.tgz", + "integrity": "sha512-sXXKG+uL9IrKqViTtao2Ws6dy0znu9sOaP1di/jKGW1M6VssO8vlpXCQcpZ+jisQ1tTFAC5Jo/EOzFbggBagFQ==", + "dev": true + }, + "node_modules/@szmarczak/http-timer": { + "version": "5.0.1", + "resolved": "https://registry.npmjs.org/@szmarczak/http-timer/-/http-timer-5.0.1.tgz", + "integrity": "sha512-+PmQX0PiAYPMeVYe237LJAYvOMYW1j2rH5YROyS3b4CTVJum34HfRvKvAzozHAQG0TnHNdUfY9nCeUyRAs//cw==", "dev": true, "dependencies": { - "yallist": "^4.0.0" + "defer-to-connect": "^2.0.1" }, "engines": { - "node": ">=10" + "node": ">=14.16" } }, - "node_modules/@typescript-eslint/utils/node_modules/semver": { - "version": "7.5.4", - "resolved": "https://registry.npmjs.org/semver/-/semver-7.5.4.tgz", - "integrity": "sha512-1bCSESV6Pv+i21Hvpxp3Dx+pSD8lIPt8uVjRrxAUt/nbswYc+tK6Y2btiULjd4+fnq15PX+nqQDC7Oft7WkwcA==", + "node_modules/@tootallnate/quickjs-emscripten": { + "version": "0.23.0", + "resolved": "https://registry.npmjs.org/@tootallnate/quickjs-emscripten/-/quickjs-emscripten-0.23.0.tgz", + "integrity": "sha512-C5Mc6rdnsaJDjO3UpGW/CQTHtCKaYlScZTly4JIu97Jxo/odCiH0ITnDXSJPTOrEKk/ycSZ0AOgTmkDtkOsvIA==", + "dev": true + }, + "node_modules/@types/http-cache-semantics": { + "version": "4.0.4", + "resolved": "https://registry.npmjs.org/@types/http-cache-semantics/-/http-cache-semantics-4.0.4.tgz", + "integrity": "sha512-1m0bIFVc7eJWyve9S0RnuRgcQqF/Xd5QsUZAZeQFr1Q3/p9JWoQQEqmVy+DPTNpGXwhgIetAoYF8JSc33q29QA==", + "dev": true + }, + "node_modules/@types/mocha": { + "version": "10.0.6", + "resolved": "https://registry.npmjs.org/@types/mocha/-/mocha-10.0.6.tgz", + "integrity": "sha512-dJvrYWxP/UcXm36Qn36fxhUKu8A/xMRXVT2cliFF1Z7UA9liG5Psj3ezNSZw+5puH2czDXRLcXQxf8JbJt0ejg==", + "dev": true + }, + "node_modules/@types/node": { + "version": "20.11.16", + "resolved": "https://registry.npmjs.org/@types/node/-/node-20.11.16.tgz", + "integrity": "sha512-gKb0enTmRCzXSSUJDq6/sPcqrfCv2mkkG6Jt/clpn5eiCbKTY+SgZUxo+p8ZKMof5dCp9vHQUAB7wOUTod22wQ==", "dev": true, "dependencies": { - "lru-cache": "^6.0.0" - }, - "bin": { - "semver": "bin/semver.js" - }, - "engines": { - "node": ">=10" + "undici-types": "~5.26.4" } }, - "node_modules/@typescript-eslint/visitor-keys": { - "version": "6.7.2", - "resolved": "https://registry.npmjs.org/@typescript-eslint/visitor-keys/-/visitor-keys-6.7.2.tgz", - "integrity": "sha512-uVw9VIMFBUTz8rIeaUT3fFe8xIUx8r4ywAdlQv1ifH+6acn/XF8Y6rwJ7XNmkNMDrTW+7+vxFFPIF40nJCVsMQ==", + "node_modules/@types/sinon": { + "version": "17.0.3", + "resolved": "https://registry.npmjs.org/@types/sinon/-/sinon-17.0.3.tgz", + "integrity": "sha512-j3uovdn8ewky9kRBG19bOwaZbexJu/XjtkHyjvUgt4xfPFz18dcORIMqnYh66Fx3Powhcr85NT5+er3+oViapw==", "dev": true, "dependencies": { - "@typescript-eslint/types": "6.7.2", - "eslint-visitor-keys": "^3.4.1" - }, - "engines": { - "node": "^16.0.0 || >=18.0.0" - }, - "funding": { - "type": "opencollective", - "url": "https://opencollective.com/typescript-eslint" + "@types/sinonjs__fake-timers": "*" } }, - "node_modules/acorn": { - "version": "8.10.0", - "resolved": "https://registry.npmjs.org/acorn/-/acorn-8.10.0.tgz", - "integrity": "sha512-F0SAmZ8iUtS//m8DmCTA0jlh6TDKkHQyK6xc6V4KDTyZKA9dnvX9/3sRTVQrWm79glUAZbnmmNcdYwUIHWVybw==", - "dev": true, - "bin": { - "acorn": "bin/acorn" - }, - "engines": { - "node": ">=0.4.0" - } + "node_modules/@types/sinonjs__fake-timers": { + "version": "8.1.5", + "resolved": "https://registry.npmjs.org/@types/sinonjs__fake-timers/-/sinonjs__fake-timers-8.1.5.tgz", + "integrity": "sha512-mQkU2jY8jJEF7YHjHvsQO8+3ughTL1mcnn96igfhONmR+fUPSKIkefQYpSe8bsly2Ep7oQbn/6VG5/9/0qcArQ==", + "dev": true }, - "node_modules/acorn-jsx": { - "version": "5.3.2", - "resolved": "https://registry.npmjs.org/acorn-jsx/-/acorn-jsx-5.3.2.tgz", - "integrity": "sha512-rq9s+JNhf0IChjtDXxllJ7g41oZk5SlXtp0LHwyA5cejwn7vKmKp4pPri6YEePv2PU65sAsegbXtIinmDFDXgQ==", + "node_modules/@types/yargs": { + "version": "17.0.32", + "resolved": "https://registry.npmjs.org/@types/yargs/-/yargs-17.0.32.tgz", + "integrity": "sha512-xQ67Yc/laOG5uMfX/093MRlGGCIBzZMarVa+gfNKJxWAIgykYpVGkBdbqEzGDDfCrVUj6Hiff4mTZ5BA6TmAog==", "dev": true, - "peerDependencies": { - "acorn": "^6.0.0 || ^7.0.0 || ^8.0.0" + "dependencies": { + "@types/yargs-parser": "*" } }, - "node_modules/acorn-walk": { - "version": "8.2.0", - "resolved": "https://registry.npmjs.org/acorn-walk/-/acorn-walk-8.2.0.tgz", - "integrity": "sha512-k+iyHEuPgSw6SbuDpGQM+06HQUa04DZ3o+F6CSzXMvvI5KMvnaEqXe+YVe555R9nn6GPt404fos4wcgpw12SDA==", - "dev": true, - "engines": { - "node": ">=0.4.0" - } + "node_modules/@types/yargs-parser": { + "version": "21.0.3", + "resolved": "https://registry.npmjs.org/@types/yargs-parser/-/yargs-parser-21.0.3.tgz", + "integrity": "sha512-I4q9QU9MQv4oEOz4tAHJtNz1cwuLxn2F3xcc2iV5WdqLPpUnj30aUuxt1mAxYTG+oe8CZMV/+6rU4S4gRDzqtQ==", + "dev": true }, "node_modules/agent-base": { "version": "7.1.0", @@ -1548,22 +1439,6 @@ "node": ">=8" } }, - "node_modules/ajv": { - "version": "6.12.6", - "resolved": "https://registry.npmjs.org/ajv/-/ajv-6.12.6.tgz", - "integrity": "sha512-j3fVLgvTo527anyYyJOGTYJbG+vnnQYvE0m5mmkc1TK+nxAppkCLMIL0aZ4dblVCNoGShhm+kzE4ZUykBoMg4g==", - "dev": true, - "dependencies": { - "fast-deep-equal": "^3.1.1", - "fast-json-stable-stringify": "^2.0.0", - "json-schema-traverse": "^0.4.1", - "uri-js": "^4.2.2" - }, - "funding": { - "type": "github", - "url": "https://github.com/sponsors/epoberezkin" - } - }, "node_modules/ansi-align": { "version": "3.0.1", "resolved": "https://registry.npmjs.org/ansi-align/-/ansi-align-3.0.1.tgz", @@ -1597,6 +1472,18 @@ "url": "https://github.com/sponsors/sindresorhus" } }, + "node_modules/ansi-escapes/node_modules/type-fest": { + "version": "0.21.3", + "resolved": "https://registry.npmjs.org/type-fest/-/type-fest-0.21.3.tgz", + "integrity": "sha512-t0rzBq87m3fVcduHDUFhKmyyX+9eo6WQjZvf51Ea/M0Q7+T374Jp1aUiyUl0GKxp8M/OETVHSDvmkyPgvX+X2w==", + "dev": true, + "engines": { + "node": ">=10" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, "node_modules/ansi-regex": { "version": "5.0.1", "resolved": "https://registry.npmjs.org/ansi-regex/-/ansi-regex-5.0.1.tgz", @@ -1658,12 +1545,6 @@ "integrity": "sha512-Xg+9RwCg/0p32teKdGMPTPnVXKD0w3DfHnFTficozsAgsvq2XenPJq/MYpzzQ/v8zrOyJn6Ds39VA4JIDwFfqw==", "dev": true }, - "node_modules/arg": { - "version": "4.1.3", - "resolved": "https://registry.npmjs.org/arg/-/arg-4.1.3.tgz", - "integrity": "sha512-58S9QDqG0Xx27YwPSt9fJxivjYl432YCwfDMfZ+71RAqUrZef7LrKQZ3LHLOwCS4FLNBplP533Zx895SeOCHvA==", - "dev": true - }, "node_modules/argparse": { "version": "2.0.1", "resolved": "https://registry.npmjs.org/argparse/-/argparse-2.0.1.tgz", @@ -1671,13 +1552,16 @@ "dev": true }, "node_modules/array-buffer-byte-length": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/array-buffer-byte-length/-/array-buffer-byte-length-1.0.0.tgz", - "integrity": "sha512-LPuwb2P+NrQw3XhxGc36+XSvuBPopovXYTR9Ew++Du9Yb/bx5AzBfrIsBoj0EZUifjQU+sHL21sseZ3jerWO/A==", + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/array-buffer-byte-length/-/array-buffer-byte-length-1.0.1.tgz", + "integrity": "sha512-ahC5W1xgou+KTXix4sAO8Ki12Q+jf4i0+tmk3sC+zgcynshkHxzpXdImBehiUYKKKDwvfFiJl1tZt6ewscS1Mg==", "dev": true, "dependencies": { - "call-bind": "^1.0.2", - "is-array-buffer": "^3.0.1" + "call-bind": "^1.0.5", + "is-array-buffer": "^3.0.4" + }, + "engines": { + "node": ">= 0.4" }, "funding": { "url": "https://github.com/sponsors/ljharb" @@ -1724,17 +1608,18 @@ } }, "node_modules/arraybuffer.prototype.slice": { - "version": "1.0.2", - "resolved": "https://registry.npmjs.org/arraybuffer.prototype.slice/-/arraybuffer.prototype.slice-1.0.2.tgz", - "integrity": "sha512-yMBKppFur/fbHu9/6USUe03bZ4knMYiwFBcyiaXB8Go0qNehwX6inYPzK9U0NeQvGxKthcmHcaR8P5MStSRBAw==", + "version": "1.0.3", + "resolved": "https://registry.npmjs.org/arraybuffer.prototype.slice/-/arraybuffer.prototype.slice-1.0.3.tgz", + "integrity": "sha512-bMxMKAjg13EBSVscxTaYA4mRc5t1UAXa2kXiGTNfZ079HIWXEkKmkgFrh/nJqamaLSrXO5H4WFFkPEaLJWbs3A==", "dev": true, "dependencies": { - "array-buffer-byte-length": "^1.0.0", - "call-bind": "^1.0.2", - "define-properties": "^1.2.0", - "es-abstract": "^1.22.1", - "get-intrinsic": "^1.2.1", - "is-array-buffer": "^3.0.2", + "array-buffer-byte-length": "^1.0.1", + "call-bind": "^1.0.5", + "define-properties": "^1.2.1", + "es-abstract": "^1.22.3", + "es-errors": "^1.2.1", + "get-intrinsic": "^1.2.3", + "is-array-buffer": "^3.0.4", "is-shared-array-buffer": "^1.0.2" }, "engines": { @@ -1757,9 +1642,9 @@ } }, "node_modules/async": { - "version": "3.2.4", - "resolved": "https://registry.npmjs.org/async/-/async-3.2.4.tgz", - "integrity": "sha512-iAB+JbDEGXhyIUavoDl9WP/Jj106Kz9DEn1DPgYw5ruDn0e3Wgi3sKFm55sASdGBNOQB8F59d9qQ7deqrHA8wQ==", + "version": "3.2.5", + "resolved": "https://registry.npmjs.org/async/-/async-3.2.5.tgz", + "integrity": "sha512-baNZyqaaLhyLVKm/DlvdW051MSgO6b8eVfIezl9E5PqWxFgzLm/wQntEW4zOytVburDEr0JlALEpdOFwvErLsg==", "dev": true }, "node_modules/async-retry": { @@ -1772,9 +1657,9 @@ } }, "node_modules/available-typed-arrays": { - "version": "1.0.5", - "resolved": "https://registry.npmjs.org/available-typed-arrays/-/available-typed-arrays-1.0.5.tgz", - "integrity": "sha512-DMD0KiN46eipeziST1LPP/STfDU0sufISXmjSgvVsoU2tqxctQeASejWcfNtxYKqETM1UxQ8sp2OrSBWpHY6sw==", + "version": "1.0.6", + "resolved": "https://registry.npmjs.org/available-typed-arrays/-/available-typed-arrays-1.0.6.tgz", + "integrity": "sha512-j1QzY8iPNPG4o4xmO3ptzpRxTciqD3MgEHtifP/YnJpIo58Xu+ne4BejlbkuaLfXn/nz6HFiw29bLpj2PNMdGg==", "dev": true, "engines": { "node": ">= 0.4" @@ -1810,9 +1695,9 @@ ] }, "node_modules/basic-ftp": { - "version": "5.0.3", - "resolved": "https://registry.npmjs.org/basic-ftp/-/basic-ftp-5.0.3.tgz", - "integrity": "sha512-QHX8HLlncOLpy54mh+k/sWIFd0ThmRqwe9ZjELybGZK+tZ8rUb9VO0saKJUROTbE+KhzDUT7xziGpGrW8Kmd+g==", + "version": "5.0.4", + "resolved": "https://registry.npmjs.org/basic-ftp/-/basic-ftp-5.0.4.tgz", + "integrity": "sha512-8PzkB0arJFV4jJWSGOYR+OEic6aeKMu/osRhBULN6RY0ykby6LKhbmuQ5ublvaas5BOwboah5D87nrHyuh8PPA==", "dev": true, "engines": { "node": ">=10.0.0" @@ -1824,15 +1709,6 @@ "integrity": "sha512-NzUnlZexiaH/46WDhANlyR2bXRopNg4F/zuSA3OpZnllCUgRaOF2znDioDWrmbNVsuZk6l9pMquQB38cfBZwkQ==", "dev": true }, - "node_modules/big-integer": { - "version": "1.6.51", - "resolved": "https://registry.npmjs.org/big-integer/-/big-integer-1.6.51.tgz", - "integrity": "sha512-GPEid2Y9QU1Exl1rpO9B2IPJGHPSupF5GnVIP0blYvNOMer2bTvSWs1jGOUg04hTmu67nmLsQ9TBo1puaotBHg==", - "dev": true, - "engines": { - "node": ">=0.6" - } - }, "node_modules/binary-extensions": { "version": "2.2.0", "resolved": "https://registry.npmjs.org/binary-extensions/-/binary-extensions-2.2.0.tgz", @@ -1843,12 +1719,12 @@ } }, "node_modules/bl": { - "version": "5.1.0", - "resolved": "https://registry.npmjs.org/bl/-/bl-5.1.0.tgz", - "integrity": "sha512-tv1ZJHLfTDnXE6tMHv73YgSJaWR2AFuPwMntBe7XL/GBFHnT0CLnsHMogfk5+GzCDC5ZWarSCYaIGATZt9dNsQ==", + "version": "4.1.0", + "resolved": "https://registry.npmjs.org/bl/-/bl-4.1.0.tgz", + "integrity": "sha512-1W07cM9gS6DcLperZfFSj+bWLtaPGSOHWhPiGzXmvVJbRLdG82sH/Kn8EtW1VqWVA54AKf2h5k5BbnIbwF3h6w==", "dev": true, "dependencies": { - "buffer": "^6.0.3", + "buffer": "^5.5.0", "inherits": "^2.0.4", "readable-stream": "^3.4.0" } @@ -1899,6 +1775,30 @@ "url": "https://github.com/chalk/ansi-styles?sponsor=1" } }, + "node_modules/boxen/node_modules/camelcase": { + "version": "7.0.1", + "resolved": "https://registry.npmjs.org/camelcase/-/camelcase-7.0.1.tgz", + "integrity": "sha512-xlx1yCK2Oc1APsPXDL2LdlNP6+uu8OCDdhOBSVT279M/S+y75O30C2VuD8T2ogdePBBl7PfPF4504tnLgX3zfw==", + "dev": true, + "engines": { + "node": ">=14.16" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/boxen/node_modules/chalk": { + "version": "5.3.0", + "resolved": "https://registry.npmjs.org/chalk/-/chalk-5.3.0.tgz", + "integrity": "sha512-dLitG79d+GV1Nb/VYcCDFivJeK1hiukt9QjRNVOsUtTy1rR1YJsmpGGTZ3qJos+uw7WmWF4wUwBd9jxjocFC2w==", + "dev": true, + "engines": { + "node": "^12.17.0 || ^14.13 || >=16.0.0" + }, + "funding": { + "url": "https://github.com/chalk/chalk?sponsor=1" + } + }, "node_modules/boxen/node_modules/emoji-regex": { "version": "9.2.2", "resolved": "https://registry.npmjs.org/emoji-regex/-/emoji-regex-9.2.2.tgz", @@ -1966,18 +1866,6 @@ "url": "https://github.com/chalk/wrap-ansi?sponsor=1" } }, - "node_modules/bplist-parser": { - "version": "0.2.0", - "resolved": "https://registry.npmjs.org/bplist-parser/-/bplist-parser-0.2.0.tgz", - "integrity": "sha512-z0M+byMThzQmD9NILRniCUXYsYpjwnlO8N5uCFaCqIOpqRsJCrQL9NK3JsD67CN5a08nF5oIL2bD6loTdHOuKw==", - "dev": true, - "dependencies": { - "big-integer": "^1.6.44" - }, - "engines": { - "node": ">= 5.10.0" - } - }, "node_modules/brace-expansion": { "version": "1.1.11", "resolved": "https://registry.npmjs.org/brace-expansion/-/brace-expansion-1.1.11.tgz", @@ -2007,9 +1895,9 @@ "dev": true }, "node_modules/browserslist": { - "version": "4.21.10", - "resolved": "https://registry.npmjs.org/browserslist/-/browserslist-4.21.10.tgz", - "integrity": "sha512-bipEBdZfVH5/pwrvqc+Ub0kUPVfGUhlKxbvfD+z1BDnPEO/X98ruXGA1WP5ASpAFKan7Qr6j736IacbZQuAlKQ==", + "version": "4.22.3", + "resolved": "https://registry.npmjs.org/browserslist/-/browserslist-4.22.3.tgz", + "integrity": "sha512-UAp55yfwNv0klWNapjs/ktHoguxuQNGnOzxYmfnXIS+8AsRDZkSDxg7R1AX3GKzn078SBI5dzwzj/Yx0Or0e3A==", "dev": true, "funding": [ { @@ -2026,10 +1914,10 @@ } ], "dependencies": { - "caniuse-lite": "^1.0.30001517", - "electron-to-chromium": "^1.4.477", - "node-releases": "^2.0.13", - "update-browserslist-db": "^1.0.11" + "caniuse-lite": "^1.0.30001580", + "electron-to-chromium": "^1.4.648", + "node-releases": "^2.0.14", + "update-browserslist-db": "^1.0.13" }, "bin": { "browserslist": "cli.js" @@ -2039,9 +1927,9 @@ } }, "node_modules/buffer": { - "version": "6.0.3", - "resolved": "https://registry.npmjs.org/buffer/-/buffer-6.0.3.tgz", - "integrity": "sha512-FTiCpNxtwiZZHEZbcbTIcZjERVICn9yq/pDFkTl95/AxzD1naBctN7YO68riM/gLSDY7sdrMby8hofADYuuqOA==", + "version": "5.7.1", + "resolved": "https://registry.npmjs.org/buffer/-/buffer-5.7.1.tgz", + "integrity": "sha512-EHcyIPBQ4BSGlvjB16k5KgAJ27CIsHY/2JBmCRReo48y9rQ3MaUzWX3KVlBa4U7MyX02HdVj0K7C3WaB3ju7FQ==", "dev": true, "funding": [ { @@ -2059,25 +1947,19 @@ ], "dependencies": { "base64-js": "^1.3.1", - "ieee754": "^1.2.1" + "ieee754": "^1.1.13" } }, - "node_modules/buffer-from": { - "version": "1.1.2", - "resolved": "https://registry.npmjs.org/buffer-from/-/buffer-from-1.1.2.tgz", - "integrity": "sha512-E+XQCRwSbaaiChtv6k6Dwgc+bx+Bs6vuKJHHl5kox/BaKbhiXzqQOwK4cO22yElGp2OCmjwVhT3HmxgyPGnJfQ==", - "dev": true - }, "node_modules/bundle-name": { - "version": "3.0.0", - "resolved": "https://registry.npmjs.org/bundle-name/-/bundle-name-3.0.0.tgz", - "integrity": "sha512-PKA4BeSvBpQKQ8iPOGCSiell+N8P+Tf1DlwqmYhpe2gAhKPHn8EYOxVT+ShuGmhg8lN8XiSlS80yiExKXrURlw==", + "version": "4.1.0", + "resolved": "https://registry.npmjs.org/bundle-name/-/bundle-name-4.1.0.tgz", + "integrity": "sha512-tjwM5exMg6BGRI+kNmTntNsvdZS1X8BFYS6tnJ2hdH0kVxM6/eVZ2xy+FqStSWvYmtfFMDLIxurorHwDKfDz5Q==", "dev": true, "dependencies": { - "run-applescript": "^5.0.0" + "run-applescript": "^7.0.0" }, "engines": { - "node": ">=12" + "node": ">=18" }, "funding": { "url": "https://github.com/sponsors/sindresorhus" @@ -2093,12 +1975,12 @@ } }, "node_modules/cacheable-request": { - "version": "10.2.13", - "resolved": "https://registry.npmjs.org/cacheable-request/-/cacheable-request-10.2.13.tgz", - "integrity": "sha512-3SD4rrMu1msNGEtNSt8Od6enwdo//U9s4ykmXfA2TD58kcLkCobtCDiby7kNyj7a/Q7lz/mAesAFI54rTdnvBA==", + "version": "10.2.14", + "resolved": "https://registry.npmjs.org/cacheable-request/-/cacheable-request-10.2.14.tgz", + "integrity": "sha512-zkDT5WAF4hSSoUgyfg5tFIxz8XQK+25W/TLVojJTMKBaxevLBBtLxgqguAuVQB8PVW79FVjHcU+GJ9tVbDZ9mQ==", "dev": true, "dependencies": { - "@types/http-cache-semantics": "^4.0.1", + "@types/http-cache-semantics": "^4.0.2", "get-stream": "^6.0.1", "http-cache-semantics": "^4.1.1", "keyv": "^4.5.3", @@ -2110,6 +1992,18 @@ "node": ">=14.16" } }, + "node_modules/cacheable-request/node_modules/get-stream": { + "version": "6.0.1", + "resolved": "https://registry.npmjs.org/get-stream/-/get-stream-6.0.1.tgz", + "integrity": "sha512-ts6Wi+2j3jQjqi70w5AlN8DFnkSwC+MqmxEzdEALB2qXZYV3X/b1CTfgPLGJNMeAWxdPfU8FO1ms3NUfaHCPYg==", + "dev": true, + "engines": { + "node": ">=10" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, "node_modules/caching-transform": { "version": "4.0.0", "resolved": "https://registry.npmjs.org/caching-transform/-/caching-transform-4.0.0.tgz", @@ -2126,13 +2020,14 @@ } }, "node_modules/call-bind": { - "version": "1.0.2", - "resolved": "https://registry.npmjs.org/call-bind/-/call-bind-1.0.2.tgz", - "integrity": "sha512-7O+FbCihrB5WGbFYesctwmTKae6rOiIzmz1icreWJ+0aA7LJfuqhEso2T9ncpcFtzMQtzXf2QGGueWJGTYsqrA==", + "version": "1.0.5", + "resolved": "https://registry.npmjs.org/call-bind/-/call-bind-1.0.5.tgz", + "integrity": "sha512-C3nQxfFZxFRVoJoGKKI8y3MOEo129NQ+FgQ08iye+Mk4zNZZGdjfs06bVTr+DBSlA66Q2VEcMki/cUCP4SercQ==", "dev": true, "dependencies": { - "function-bind": "^1.1.1", - "get-intrinsic": "^1.0.2" + "function-bind": "^1.1.2", + "get-intrinsic": "^1.2.1", + "set-function-length": "^1.1.1" }, "funding": { "url": "https://github.com/sponsors/ljharb" @@ -2148,21 +2043,18 @@ } }, "node_modules/camelcase": { - "version": "7.0.1", - "resolved": "https://registry.npmjs.org/camelcase/-/camelcase-7.0.1.tgz", - "integrity": "sha512-xlx1yCK2Oc1APsPXDL2LdlNP6+uu8OCDdhOBSVT279M/S+y75O30C2VuD8T2ogdePBBl7PfPF4504tnLgX3zfw==", + "version": "5.3.1", + "resolved": "https://registry.npmjs.org/camelcase/-/camelcase-5.3.1.tgz", + "integrity": "sha512-L28STB170nwWS63UjtlEOE3dldQApaJXZkOI1uMFfzf3rRuPegHaHesyee+YxQ+W6SvRDQV6UrdOdRiR153wJg==", "dev": true, "engines": { - "node": ">=14.16" - }, - "funding": { - "url": "https://github.com/sponsors/sindresorhus" + "node": ">=6" } }, "node_modules/caniuse-lite": { - "version": "1.0.30001535", - "resolved": "https://registry.npmjs.org/caniuse-lite/-/caniuse-lite-1.0.30001535.tgz", - "integrity": "sha512-48jLyUkiWFfhm/afF7cQPqPjaUmSraEhK4j+FCTJpgnGGEZHqyLe3hmWH7lIooZdSzXL0ReMvHz0vKDoTBsrwg==", + "version": "1.0.30001584", + "resolved": "https://registry.npmjs.org/caniuse-lite/-/caniuse-lite-1.0.30001584.tgz", + "integrity": "sha512-LOz7CCQ9M1G7OjJOF9/mzmqmj3jE/7VOmrfw6Mgs0E8cjOsbRXQJHsPBfmBOXDskXKrHLyyW3n7kpDW/4BsfpQ==", "dev": true, "funding": [ { @@ -2180,17 +2072,33 @@ ] }, "node_modules/chalk": { - "version": "5.3.0", - "resolved": "https://registry.npmjs.org/chalk/-/chalk-5.3.0.tgz", - "integrity": "sha512-dLitG79d+GV1Nb/VYcCDFivJeK1hiukt9QjRNVOsUtTy1rR1YJsmpGGTZ3qJos+uw7WmWF4wUwBd9jxjocFC2w==", + "version": "4.1.2", + "resolved": "https://registry.npmjs.org/chalk/-/chalk-4.1.2.tgz", + "integrity": "sha512-oKnbhFyRIXpUuez8iBMmyEa4nbj4IOQyuhc/wy9kY7/WVPcwIO9VA668Pu8RkO7+0G76SLROeyw9CpQ061i4mA==", "dev": true, + "dependencies": { + "ansi-styles": "^4.1.0", + "supports-color": "^7.1.0" + }, "engines": { - "node": "^12.17.0 || ^14.13 || >=16.0.0" + "node": ">=10" }, "funding": { "url": "https://github.com/chalk/chalk?sponsor=1" } }, + "node_modules/chalk/node_modules/supports-color": { + "version": "7.2.0", + "resolved": "https://registry.npmjs.org/supports-color/-/supports-color-7.2.0.tgz", + "integrity": "sha512-qpCAvRl9stuOHveKsn7HncJRvv501qIacKzQlO/+Lwxc9+0q2wLyv4Dfvt80/DPn2pqOBsJdDiogXGR9+OvwRw==", + "dev": true, + "dependencies": { + "has-flag": "^4.0.0" + }, + "engines": { + "node": ">=8" + } + }, "node_modules/chardet": { "version": "0.7.0", "resolved": "https://registry.npmjs.org/chardet/-/chardet-0.7.0.tgz", @@ -2225,9 +2133,9 @@ } }, "node_modules/ci-info": { - "version": "3.8.0", - "resolved": "https://registry.npmjs.org/ci-info/-/ci-info-3.8.0.tgz", - "integrity": "sha512-eXTggHWSooYhq49F2opQhuHWgzucfF2YgODK4e1566GQs5BIfP30B0oenwBJHfWxAs2fyPB1s7Mg949zLf61Yw==", + "version": "3.9.0", + "resolved": "https://registry.npmjs.org/ci-info/-/ci-info-3.9.0.tgz", + "integrity": "sha512-NIxF55hv4nSqQswkAeiOi1r83xy8JldOFDTWiug55KBu9Jnblncd2U6ViHmYgHf01TPZS77NJBhBMKdWj9HQMQ==", "dev": true, "funding": [ { @@ -2273,9 +2181,9 @@ } }, "node_modules/cli-spinners": { - "version": "2.9.1", - "resolved": "https://registry.npmjs.org/cli-spinners/-/cli-spinners-2.9.1.tgz", - "integrity": "sha512-jHgecW0pxkonBJdrKsqxgRX9AcG+u/5k0Q7WPDfi8AogLAdwxEkyYYNWwZ5GvVFoFx2uiY1eNcSK00fh+1+FyQ==", + "version": "2.9.2", + "resolved": "https://registry.npmjs.org/cli-spinners/-/cli-spinners-2.9.2.tgz", + "integrity": "sha512-ywqV+5MmyL4E7ybXgKys4DugZbX0FC6LnwrhjuykIjnK9k8OQacQ7axGKnjDXWNhns0xot3bZI5h55H8yo9cJg==", "dev": true, "engines": { "node": ">=6" @@ -2294,17 +2202,14 @@ } }, "node_modules/cliui": { - "version": "8.0.1", - "resolved": "https://registry.npmjs.org/cliui/-/cliui-8.0.1.tgz", - "integrity": "sha512-BSeNnyus75C4//NQ9gQt1/csTXyo/8Sb+afLAkzAptFuMsod9HFokGNudZpi/oQV73hnVK+sR+5PVRMd+Dr7YQ==", + "version": "7.0.4", + "resolved": "https://registry.npmjs.org/cliui/-/cliui-7.0.4.tgz", + "integrity": "sha512-OcRE68cOsVMXp1Yvonl/fzkQOyjLSu/8bhPDfQt0e0/Eb283TKP20Fs2MqoPsr9SwA595rRCA+QMzYc9nBP+JQ==", "dev": true, "dependencies": { "string-width": "^4.2.0", - "strip-ansi": "^6.0.1", + "strip-ansi": "^6.0.0", "wrap-ansi": "^7.0.0" - }, - "engines": { - "node": ">=12" } }, "node_modules/cliui/node_modules/wrap-ansi": { @@ -2360,9 +2265,9 @@ "dev": true }, "node_modules/commander": { - "version": "11.0.0", - "resolved": "https://registry.npmjs.org/commander/-/commander-11.0.0.tgz", - "integrity": "sha512-9HMlXtt/BNoYr8ooyjjNRdIilOTkVJXB+GhxMTtOKwk0R4j4lS4NpjuqmRxroBfnfTSHQIHQB7wryHhXarNjmQ==", + "version": "11.1.0", + "resolved": "https://registry.npmjs.org/commander/-/commander-11.1.0.tgz", + "integrity": "sha512-yPVavfyCcRhmorC7rWlkHn15b4wDVgVmBA7kV4QVBsF7kv/9TKJAbAXVTxvTnwP8HHKjRCJDClKbciiYS7p0DQ==", "dev": true, "engines": { "node": ">=16" @@ -2422,29 +2327,31 @@ "dev": true }, "node_modules/cosmiconfig": { - "version": "8.2.0", - "resolved": "https://registry.npmjs.org/cosmiconfig/-/cosmiconfig-8.2.0.tgz", - "integrity": "sha512-3rTMnFJA1tCOPwRxtgF4wd7Ab2qvDbL8jX+3smjIbS4HlZBagTlpERbdN7iAbWlrfxE3M8c27kTwTawQ7st+OQ==", + "version": "9.0.0", + "resolved": "https://registry.npmjs.org/cosmiconfig/-/cosmiconfig-9.0.0.tgz", + "integrity": "sha512-itvL5h8RETACmOTFc4UfIyB2RfEHi71Ax6E/PivVxq9NseKbOWpeyHEOIbmAw1rs8Ak0VursQNww7lf7YtUwzg==", "dev": true, "dependencies": { - "import-fresh": "^3.2.1", + "env-paths": "^2.2.1", + "import-fresh": "^3.3.0", "js-yaml": "^4.1.0", - "parse-json": "^5.0.0", - "path-type": "^4.0.0" + "parse-json": "^5.2.0" }, "engines": { "node": ">=14" }, "funding": { "url": "https://github.com/sponsors/d-fischer" + }, + "peerDependencies": { + "typescript": ">=4.9.5" + }, + "peerDependenciesMeta": { + "typescript": { + "optional": true + } } }, - "node_modules/create-require": { - "version": "1.1.1", - "resolved": "https://registry.npmjs.org/create-require/-/create-require-1.1.1.tgz", - "integrity": "sha512-dcKFX3jn0MpIaXjisoRvexIJVEKzaq7z2rZKxf+MSr9TkdmHmsU4m2lcLojrj/FHl8mk5VxMmYA+ftRkP/3oKQ==", - "dev": true - }, "node_modules/cross-spawn": { "version": "7.0.3", "resolved": "https://registry.npmjs.org/cross-spawn/-/cross-spawn-7.0.3.tgz", @@ -2512,6 +2419,12 @@ } } }, + "node_modules/debug/node_modules/ms": { + "version": "2.1.2", + "resolved": "https://registry.npmjs.org/ms/-/ms-2.1.2.tgz", + "integrity": "sha512-sGkPx+VjMtmA6MX27oA4FBFELFCZZ4S4XqeGOXCv68tT+jb3vk/RyaKWP0PTKyWtmLSM0b+adUTEvbs1PEaH2w==", + "dev": true + }, "node_modules/decamelize": { "version": "1.2.0", "resolved": "https://registry.npmjs.org/decamelize/-/decamelize-1.2.0.tgz", @@ -2557,41 +2470,29 @@ "node": ">=4.0.0" } }, - "node_modules/deep-is": { - "version": "0.1.4", - "resolved": "https://registry.npmjs.org/deep-is/-/deep-is-0.1.4.tgz", - "integrity": "sha512-oIPzksmTg4/MriiaYGO+okXDT7ztn/w3Eptv/+gSIdMdKsJo0u4CfYNFJPy+4SKMuCqGw2wxnA+URMg3t8a/bQ==", - "dev": true - }, "node_modules/default-browser": { - "version": "4.0.0", - "resolved": "https://registry.npmjs.org/default-browser/-/default-browser-4.0.0.tgz", - "integrity": "sha512-wX5pXO1+BrhMkSbROFsyxUm0i/cJEScyNhA4PPxc41ICuv05ZZB/MX28s8aZx6xjmatvebIapF6hLEKEcpneUA==", + "version": "5.2.1", + "resolved": "https://registry.npmjs.org/default-browser/-/default-browser-5.2.1.tgz", + "integrity": "sha512-WY/3TUME0x3KPYdRRxEJJvXRHV4PyPoUsxtZa78lwItwRQRHhd2U9xOscaT/YTf8uCXIAjeJOFBVEh/7FtD8Xg==", "dev": true, "dependencies": { - "bundle-name": "^3.0.0", - "default-browser-id": "^3.0.0", - "execa": "^7.1.1", - "titleize": "^3.0.0" + "bundle-name": "^4.1.0", + "default-browser-id": "^5.0.0" }, "engines": { - "node": ">=14.16" + "node": ">=18" }, "funding": { "url": "https://github.com/sponsors/sindresorhus" } }, "node_modules/default-browser-id": { - "version": "3.0.0", - "resolved": "https://registry.npmjs.org/default-browser-id/-/default-browser-id-3.0.0.tgz", - "integrity": "sha512-OZ1y3y0SqSICtE8DE4S8YOE9UZOJ8wO16fKWVP5J1Qz42kV9jcnMVFrEE/noXb/ss3Q4pZIH79kxofzyNNtUNA==", + "version": "5.0.0", + "resolved": "https://registry.npmjs.org/default-browser-id/-/default-browser-id-5.0.0.tgz", + "integrity": "sha512-A6p/pu/6fyBcA1TRz/GqWYPViplrftcW2gZC9q79ngNCKAeR/X3gcEdXQHl4KNXV+3wgIJ1CPkJQ3IHM6lcsyA==", "dev": true, - "dependencies": { - "bplist-parser": "^0.2.0", - "untildify": "^4.0.0" - }, "engines": { - "node": ">=12" + "node": ">=18" }, "funding": { "url": "https://github.com/sponsors/sindresorhus" @@ -2634,9 +2535,9 @@ } }, "node_modules/define-data-property": { - "version": "1.1.0", - "resolved": "https://registry.npmjs.org/define-data-property/-/define-data-property-1.1.0.tgz", - "integrity": "sha512-UzGwzcjyv3OtAvolTj1GoyNYzfFR+iqbGjcnBEENZVCpM4/Ng1yhGNvS3lR/xDS74Tb2wGG9WzNSNIOS9UVb2g==", + "version": "1.1.1", + "resolved": "https://registry.npmjs.org/define-data-property/-/define-data-property-1.1.1.tgz", + "integrity": "sha512-E7uGkTzkk1d0ByLeSc6ZsFS79Axg+m1P/VsgYsxHgiuc3tFSj+MjMIwe90FC4lOAZzNBdY7kkO2P2wKdsQ1vgQ==", "dev": true, "dependencies": { "get-intrinsic": "^1.2.1", @@ -2705,30 +2606,6 @@ "node": ">=0.3.1" } }, - "node_modules/dir-glob": { - "version": "3.0.1", - "resolved": "https://registry.npmjs.org/dir-glob/-/dir-glob-3.0.1.tgz", - "integrity": "sha512-WkrWp9GR4KXfKGYzOLmTuGVi1UWFfws377n9cc55/tb6DuqyF6pcQ5AbiHEshaDpY9v6oaSr2XCDidGmMwdzIA==", - "dev": true, - "dependencies": { - "path-type": "^4.0.0" - }, - "engines": { - "node": ">=8" - } - }, - "node_modules/doctrine": { - "version": "3.0.0", - "resolved": "https://registry.npmjs.org/doctrine/-/doctrine-3.0.0.tgz", - "integrity": "sha512-yS+Q5i3hBf7GBkd4KG8a7eBNNWNGLTaEwwYWUijIYM7zrlYDM0BFXHjjPWlWZ1Rg7UaddZeIDmi9jF3HmqiQ2w==", - "dev": true, - "dependencies": { - "esutils": "^2.0.2" - }, - "engines": { - "node": ">=6.0.0" - } - }, "node_modules/dot-prop": { "version": "6.0.1", "resolved": "https://registry.npmjs.org/dot-prop/-/dot-prop-6.0.1.tgz", @@ -2751,9 +2628,9 @@ "dev": true }, "node_modules/electron-to-chromium": { - "version": "1.4.523", - "resolved": "https://registry.npmjs.org/electron-to-chromium/-/electron-to-chromium-1.4.523.tgz", - "integrity": "sha512-9AreocSUWnzNtvLcbpng6N+GkXnCcBR80IQkxRC9Dfdyg4gaWNUPBujAHUpKkiUkoSoR9UlhA4zD/IgBklmhzg==", + "version": "1.4.656", + "resolved": "https://registry.npmjs.org/electron-to-chromium/-/electron-to-chromium-1.4.656.tgz", + "integrity": "sha512-9AQB5eFTHyR3Gvt2t/NwR0le2jBSUNwCnMbUCejFWHD+so4tH40/dRLgoE+jxlPeWS43XJewyvCv+I8LPMl49Q==", "dev": true }, "node_modules/email-addresses": { @@ -2768,6 +2645,15 @@ "integrity": "sha512-MSjYzcWNOA0ewAHpz0MxpYFvwg6yjy1NG3xteoqz644VCo/RPgnr1/GGt+ic3iJTzQ8Eu3TdM14SawnVUmGE6A==", "dev": true }, + "node_modules/env-paths": { + "version": "2.2.1", + "resolved": "https://registry.npmjs.org/env-paths/-/env-paths-2.2.1.tgz", + "integrity": "sha512-+h1lkLKhZMTYjog1VEpJNG7NZJWcuc2DDk/qsqSTRRCOXiLjeQ1d1/udrUGhqMxUgAlwKNZ0cf2uqan5GLuS2A==", + "dev": true, + "engines": { + "node": ">=6" + } + }, "node_modules/error-ex": { "version": "1.3.2", "resolved": "https://registry.npmjs.org/error-ex/-/error-ex-1.3.2.tgz", @@ -2778,26 +2664,26 @@ } }, "node_modules/es-abstract": { - "version": "1.22.2", - "resolved": "https://registry.npmjs.org/es-abstract/-/es-abstract-1.22.2.tgz", - "integrity": "sha512-YoxfFcDmhjOgWPWsV13+2RNjq1F6UQnfs+8TftwNqtzlmFzEXvlUwdrNrYeaizfjQzRMxkZ6ElWMOJIFKdVqwA==", + "version": "1.22.3", + "resolved": "https://registry.npmjs.org/es-abstract/-/es-abstract-1.22.3.tgz", + "integrity": "sha512-eiiY8HQeYfYH2Con2berK+To6GrK2RxbPawDkGq4UiCQQfZHb6wX9qQqkbpPqaxQFcl8d9QzZqo0tGE0VcrdwA==", "dev": true, "dependencies": { "array-buffer-byte-length": "^1.0.0", "arraybuffer.prototype.slice": "^1.0.2", "available-typed-arrays": "^1.0.5", - "call-bind": "^1.0.2", + "call-bind": "^1.0.5", "es-set-tostringtag": "^2.0.1", "es-to-primitive": "^1.2.1", "function.prototype.name": "^1.1.6", - "get-intrinsic": "^1.2.1", + "get-intrinsic": "^1.2.2", "get-symbol-description": "^1.0.0", "globalthis": "^1.0.3", "gopd": "^1.0.1", - "has": "^1.0.3", "has-property-descriptors": "^1.0.0", "has-proto": "^1.0.1", "has-symbols": "^1.0.3", + "hasown": "^2.0.0", "internal-slot": "^1.0.5", "is-array-buffer": "^3.0.2", "is-callable": "^1.2.7", @@ -2807,7 +2693,7 @@ "is-string": "^1.0.7", "is-typed-array": "^1.1.12", "is-weakref": "^1.0.2", - "object-inspect": "^1.12.3", + "object-inspect": "^1.13.1", "object-keys": "^1.1.1", "object.assign": "^4.1.4", "regexp.prototype.flags": "^1.5.1", @@ -2821,7 +2707,7 @@ "typed-array-byte-offset": "^1.0.0", "typed-array-length": "^1.0.4", "unbox-primitive": "^1.0.2", - "which-typed-array": "^1.1.11" + "which-typed-array": "^1.1.13" }, "engines": { "node": ">= 0.4" @@ -2836,6 +2722,15 @@ "integrity": "sha512-wd6JXUmyHmt8T5a2xreUwKcGPq6f1f+WwIJkijUqiGcJz1qqnZgP6XIK+QyIWU5lT7imeNxUll48bziG+TSYcA==", "dev": true }, + "node_modules/es-errors": { + "version": "1.3.0", + "resolved": "https://registry.npmjs.org/es-errors/-/es-errors-1.3.0.tgz", + "integrity": "sha512-Zf5H2Kxt2xjTvbJvP2ZWLEICxA6j+hAmMzIlypy4xcBg1vKVnx89Wy0GbS+kf5cwCVFFzdCFh2XSCFNULS6csw==", + "dev": true, + "engines": { + "node": ">= 0.4" + } + }, "node_modules/es-get-iterator": { "version": "1.1.3", "resolved": "https://registry.npmjs.org/es-get-iterator/-/es-get-iterator-1.1.3.tgz", @@ -2845,293 +2740,118 @@ "call-bind": "^1.0.2", "get-intrinsic": "^1.1.3", "has-symbols": "^1.0.3", - "is-arguments": "^1.1.1", - "is-map": "^2.0.2", - "is-set": "^2.0.2", - "is-string": "^1.0.7", - "isarray": "^2.0.5", - "stop-iteration-iterator": "^1.0.0" - }, - "funding": { - "url": "https://github.com/sponsors/ljharb" - } - }, - "node_modules/es-set-tostringtag": { - "version": "2.0.1", - "resolved": "https://registry.npmjs.org/es-set-tostringtag/-/es-set-tostringtag-2.0.1.tgz", - "integrity": "sha512-g3OMbtlwY3QewlqAiMLI47KywjWZoEytKr8pf6iTC8uJq5bIAH52Z9pnQ8pVL6whrCto53JZDuUIsifGeLorTg==", - "dev": true, - "dependencies": { - "get-intrinsic": "^1.1.3", - "has": "^1.0.3", - "has-tostringtag": "^1.0.0" - }, - "engines": { - "node": ">= 0.4" - } - }, - "node_modules/es-to-primitive": { - "version": "1.2.1", - "resolved": "https://registry.npmjs.org/es-to-primitive/-/es-to-primitive-1.2.1.tgz", - "integrity": "sha512-QCOllgZJtaUo9miYBcLChTUaHNjJF3PYs1VidD7AwiEj1kYxKeQTctLAezAOH5ZKRH0g2IgPn6KwB4IT8iRpvA==", - "dev": true, - "dependencies": { - "is-callable": "^1.1.4", - "is-date-object": "^1.0.1", - "is-symbol": "^1.0.2" - }, - "engines": { - "node": ">= 0.4" - }, - "funding": { - "url": "https://github.com/sponsors/ljharb" - } - }, - "node_modules/es6-error": { - "version": "4.1.1", - "resolved": "https://registry.npmjs.org/es6-error/-/es6-error-4.1.1.tgz", - "integrity": "sha512-Um/+FxMr9CISWh0bi5Zv0iOD+4cFh5qLeks1qhAopKVAJw3drgKbKySikp7wGhDL0HPeaja0P5ULZrxLkniUVg==", - "dev": true - }, - "node_modules/escalade": { - "version": "3.1.1", - "resolved": "https://registry.npmjs.org/escalade/-/escalade-3.1.1.tgz", - "integrity": "sha512-k0er2gUkLf8O0zKJiAhmkTnJlTvINGv7ygDNPbeIsX/TJjGJZHuh9B2UxbsaEkmlEo9MfhrSzmhIlhRlI2GXnw==", - "dev": true, - "engines": { - "node": ">=6" - } - }, - "node_modules/escape-goat": { - "version": "4.0.0", - "resolved": "https://registry.npmjs.org/escape-goat/-/escape-goat-4.0.0.tgz", - "integrity": "sha512-2Sd4ShcWxbx6OY1IHyla/CVNwvg7XwZVoXZHcSu9w9SReNP1EzzD5T8NWKIR38fIqEns9kDWKUQTXXAmlDrdPg==", - "dev": true, - "engines": { - "node": ">=12" - }, - "funding": { - "url": "https://github.com/sponsors/sindresorhus" - } - }, - "node_modules/escape-string-regexp": { - "version": "5.0.0", - "resolved": "https://registry.npmjs.org/escape-string-regexp/-/escape-string-regexp-5.0.0.tgz", - "integrity": "sha512-/veY75JbMK4j1yjvuUxuVsiS/hr/4iHs9FTT6cgTexxdE0Ly/glccBAkloH/DofkjRbZU3bnoj38mOmhkZ0lHw==", - "dev": true, - "engines": { - "node": ">=12" - }, - "funding": { - "url": "https://github.com/sponsors/sindresorhus" - } - }, - "node_modules/escodegen": { - "version": "2.1.0", - "resolved": "https://registry.npmjs.org/escodegen/-/escodegen-2.1.0.tgz", - "integrity": "sha512-2NlIDTwUWJN0mRPQOdtQBzbUHvdGY2P1VXSyU83Q3xKxM7WHX2Ql8dKq782Q9TgQUNOLEzEYu9bzLNj1q88I5w==", - "dev": true, - "dependencies": { - "esprima": "^4.0.1", - "estraverse": "^5.2.0", - "esutils": "^2.0.2" - }, - "bin": { - "escodegen": "bin/escodegen.js", - "esgenerate": "bin/esgenerate.js" - }, - "engines": { - "node": ">=6.0" - }, - "optionalDependencies": { - "source-map": "~0.6.1" - } - }, - "node_modules/eslint": { - "version": "8.49.0", - "resolved": "https://registry.npmjs.org/eslint/-/eslint-8.49.0.tgz", - "integrity": "sha512-jw03ENfm6VJI0jA9U+8H5zfl5b+FvuU3YYvZRdZHOlU2ggJkxrlkJH4HcDrZpj6YwD8kuYqvQM8LyesoazrSOQ==", - "dev": true, - "dependencies": { - "@eslint-community/eslint-utils": "^4.2.0", - "@eslint-community/regexpp": "^4.6.1", - "@eslint/eslintrc": "^2.1.2", - "@eslint/js": "8.49.0", - "@humanwhocodes/config-array": "^0.11.11", - "@humanwhocodes/module-importer": "^1.0.1", - "@nodelib/fs.walk": "^1.2.8", - "ajv": "^6.12.4", - "chalk": "^4.0.0", - "cross-spawn": "^7.0.2", - "debug": "^4.3.2", - "doctrine": "^3.0.0", - "escape-string-regexp": "^4.0.0", - "eslint-scope": "^7.2.2", - "eslint-visitor-keys": "^3.4.3", - "espree": "^9.6.1", - "esquery": "^1.4.2", - "esutils": "^2.0.2", - "fast-deep-equal": "^3.1.3", - "file-entry-cache": "^6.0.1", - "find-up": "^5.0.0", - "glob-parent": "^6.0.2", - "globals": "^13.19.0", - "graphemer": "^1.4.0", - "ignore": "^5.2.0", - "imurmurhash": "^0.1.4", - "is-glob": "^4.0.0", - "is-path-inside": "^3.0.3", - "js-yaml": "^4.1.0", - "json-stable-stringify-without-jsonify": "^1.0.1", - "levn": "^0.4.1", - "lodash.merge": "^4.6.2", - "minimatch": "^3.1.2", - "natural-compare": "^1.4.0", - "optionator": "^0.9.3", - "strip-ansi": "^6.0.1", - "text-table": "^0.2.0" - }, - "bin": { - "eslint": "bin/eslint.js" - }, - "engines": { - "node": "^12.22.0 || ^14.17.0 || >=16.0.0" - }, - "funding": { - "url": "https://opencollective.com/eslint" - } - }, - "node_modules/eslint-scope": { - "version": "7.2.2", - "resolved": "https://registry.npmjs.org/eslint-scope/-/eslint-scope-7.2.2.tgz", - "integrity": "sha512-dOt21O7lTMhDM+X9mB4GX+DZrZtCUJPL/wlcTqxyrx5IvO0IYtILdtrQGQp+8n5S0gwSVmOf9NQrjMOgfQZlIg==", - "dev": true, - "dependencies": { - "esrecurse": "^4.3.0", - "estraverse": "^5.2.0" - }, - "engines": { - "node": "^12.22.0 || ^14.17.0 || >=16.0.0" - }, - "funding": { - "url": "https://opencollective.com/eslint" - } - }, - "node_modules/eslint-visitor-keys": { - "version": "3.4.3", - "resolved": "https://registry.npmjs.org/eslint-visitor-keys/-/eslint-visitor-keys-3.4.3.tgz", - "integrity": "sha512-wpc+LXeiyiisxPlEkUzU6svyS1frIO3Mgxj1fdy7Pm8Ygzguax2N3Fa/D/ag1WqbOprdI+uY6wMUl8/a2G+iag==", - "dev": true, - "engines": { - "node": "^12.22.0 || ^14.17.0 || >=16.0.0" - }, - "funding": { - "url": "https://opencollective.com/eslint" - } - }, - "node_modules/eslint/node_modules/chalk": { - "version": "4.1.2", - "resolved": "https://registry.npmjs.org/chalk/-/chalk-4.1.2.tgz", - "integrity": "sha512-oKnbhFyRIXpUuez8iBMmyEa4nbj4IOQyuhc/wy9kY7/WVPcwIO9VA668Pu8RkO7+0G76SLROeyw9CpQ061i4mA==", - "dev": true, - "dependencies": { - "ansi-styles": "^4.1.0", - "supports-color": "^7.1.0" - }, - "engines": { - "node": ">=10" - }, - "funding": { - "url": "https://github.com/chalk/chalk?sponsor=1" - } - }, - "node_modules/eslint/node_modules/escape-string-regexp": { - "version": "4.0.0", - "resolved": "https://registry.npmjs.org/escape-string-regexp/-/escape-string-regexp-4.0.0.tgz", - "integrity": "sha512-TtpcNJ3XAzx3Gq8sWRzJaVajRs0uVxA2YAkdb1jm2YkPz4G6egUFAyA3n5vtEIZefPk5Wa4UXbKuS5fKkJWdgA==", - "dev": true, - "engines": { - "node": ">=10" + "is-arguments": "^1.1.1", + "is-map": "^2.0.2", + "is-set": "^2.0.2", + "is-string": "^1.0.7", + "isarray": "^2.0.5", + "stop-iteration-iterator": "^1.0.0" }, "funding": { - "url": "https://github.com/sponsors/sindresorhus" + "url": "https://github.com/sponsors/ljharb" } }, - "node_modules/eslint/node_modules/find-up": { - "version": "5.0.0", - "resolved": "https://registry.npmjs.org/find-up/-/find-up-5.0.0.tgz", - "integrity": "sha512-78/PXT1wlLLDgTzDs7sjq9hzz0vXD+zn+7wypEe4fXQxCmdmqfGsEPQxmiCSQI3ajFV91bVSsvNtrJRiW6nGng==", + "node_modules/es-set-tostringtag": { + "version": "2.0.2", + "resolved": "https://registry.npmjs.org/es-set-tostringtag/-/es-set-tostringtag-2.0.2.tgz", + "integrity": "sha512-BuDyupZt65P9D2D2vA/zqcI3G5xRsklm5N3xCwuiy+/vKy8i0ifdsQP1sLgO4tZDSCaQUSnmC48khknGMV3D2Q==", "dev": true, "dependencies": { - "locate-path": "^6.0.0", - "path-exists": "^4.0.0" + "get-intrinsic": "^1.2.2", + "has-tostringtag": "^1.0.0", + "hasown": "^2.0.0" }, "engines": { - "node": ">=10" - }, - "funding": { - "url": "https://github.com/sponsors/sindresorhus" + "node": ">= 0.4" } }, - "node_modules/eslint/node_modules/glob-parent": { - "version": "6.0.2", - "resolved": "https://registry.npmjs.org/glob-parent/-/glob-parent-6.0.2.tgz", - "integrity": "sha512-XxwI8EOhVQgWp6iDL+3b0r86f4d6AX6zSU55HfB4ydCEuXLXc5FcYeOu+nnGftS4TEju/11rt4KJPTMgbfmv4A==", + "node_modules/es-to-primitive": { + "version": "1.2.1", + "resolved": "https://registry.npmjs.org/es-to-primitive/-/es-to-primitive-1.2.1.tgz", + "integrity": "sha512-QCOllgZJtaUo9miYBcLChTUaHNjJF3PYs1VidD7AwiEj1kYxKeQTctLAezAOH5ZKRH0g2IgPn6KwB4IT8iRpvA==", "dev": true, "dependencies": { - "is-glob": "^4.0.3" + "is-callable": "^1.1.4", + "is-date-object": "^1.0.1", + "is-symbol": "^1.0.2" }, "engines": { - "node": ">=10.13.0" + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" } }, - "node_modules/eslint/node_modules/has-flag": { - "version": "4.0.0", - "resolved": "https://registry.npmjs.org/has-flag/-/has-flag-4.0.0.tgz", - "integrity": "sha512-EykJT/Q1KjTWctppgIAgfSO0tKVuZUjhgMr17kqTumMl6Afv3EISleU7qZUzoXDFTAHTDC4NOoG/ZxU3EvlMPQ==", + "node_modules/es6-error": { + "version": "4.1.1", + "resolved": "https://registry.npmjs.org/es6-error/-/es6-error-4.1.1.tgz", + "integrity": "sha512-Um/+FxMr9CISWh0bi5Zv0iOD+4cFh5qLeks1qhAopKVAJw3drgKbKySikp7wGhDL0HPeaja0P5ULZrxLkniUVg==", + "dev": true + }, + "node_modules/esbuild": { + "version": "0.19.12", + "resolved": "https://registry.npmjs.org/esbuild/-/esbuild-0.19.12.tgz", + "integrity": "sha512-aARqgq8roFBj054KvQr5f1sFu0D65G+miZRCuJyJ0G13Zwx7vRar5Zhn2tkQNzIXcBrNVsv/8stehpj+GAjgbg==", "dev": true, + "hasInstallScript": true, + "bin": { + "esbuild": "bin/esbuild" + }, "engines": { - "node": ">=8" + "node": ">=12" + }, + "optionalDependencies": { + "@esbuild/aix-ppc64": "0.19.12", + "@esbuild/android-arm": "0.19.12", + "@esbuild/android-arm64": "0.19.12", + "@esbuild/android-x64": "0.19.12", + "@esbuild/darwin-arm64": "0.19.12", + "@esbuild/darwin-x64": "0.19.12", + "@esbuild/freebsd-arm64": "0.19.12", + "@esbuild/freebsd-x64": "0.19.12", + "@esbuild/linux-arm": "0.19.12", + "@esbuild/linux-arm64": "0.19.12", + "@esbuild/linux-ia32": "0.19.12", + "@esbuild/linux-loong64": "0.19.12", + "@esbuild/linux-mips64el": "0.19.12", + "@esbuild/linux-ppc64": "0.19.12", + "@esbuild/linux-riscv64": "0.19.12", + "@esbuild/linux-s390x": "0.19.12", + "@esbuild/linux-x64": "0.19.12", + "@esbuild/netbsd-x64": "0.19.12", + "@esbuild/openbsd-x64": "0.19.12", + "@esbuild/sunos-x64": "0.19.12", + "@esbuild/win32-arm64": "0.19.12", + "@esbuild/win32-ia32": "0.19.12", + "@esbuild/win32-x64": "0.19.12" } }, - "node_modules/eslint/node_modules/locate-path": { - "version": "6.0.0", - "resolved": "https://registry.npmjs.org/locate-path/-/locate-path-6.0.0.tgz", - "integrity": "sha512-iPZK6eYjbxRu3uB4/WZ3EsEIMJFMqAoopl3R+zuq0UjcAm/MO6KCweDgPfP3elTztoKP3KtnVHxTn2NHBSDVUw==", + "node_modules/escalade": { + "version": "3.1.1", + "resolved": "https://registry.npmjs.org/escalade/-/escalade-3.1.1.tgz", + "integrity": "sha512-k0er2gUkLf8O0zKJiAhmkTnJlTvINGv7ygDNPbeIsX/TJjGJZHuh9B2UxbsaEkmlEo9MfhrSzmhIlhRlI2GXnw==", "dev": true, - "dependencies": { - "p-locate": "^5.0.0" - }, "engines": { - "node": ">=10" - }, - "funding": { - "url": "https://github.com/sponsors/sindresorhus" + "node": ">=6" } }, - "node_modules/eslint/node_modules/p-limit": { - "version": "3.1.0", - "resolved": "https://registry.npmjs.org/p-limit/-/p-limit-3.1.0.tgz", - "integrity": "sha512-TYOanM3wGwNGsZN2cVTYPArw454xnXj5qmWF1bEoAc4+cU/ol7GVh7odevjp1FNHduHc3KZMcFduxU5Xc6uJRQ==", + "node_modules/escape-goat": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/escape-goat/-/escape-goat-4.0.0.tgz", + "integrity": "sha512-2Sd4ShcWxbx6OY1IHyla/CVNwvg7XwZVoXZHcSu9w9SReNP1EzzD5T8NWKIR38fIqEns9kDWKUQTXXAmlDrdPg==", "dev": true, - "dependencies": { - "yocto-queue": "^0.1.0" - }, "engines": { - "node": ">=10" + "node": ">=12" }, "funding": { "url": "https://github.com/sponsors/sindresorhus" } }, - "node_modules/eslint/node_modules/p-locate": { - "version": "5.0.0", - "resolved": "https://registry.npmjs.org/p-locate/-/p-locate-5.0.0.tgz", - "integrity": "sha512-LaNjtRWUBY++zB5nE/NwcaoMylSPk+S+ZHNB1TzdbMJMny6dynpAGt7X/tl/QYq3TIeE6nxHppbo2LGymrG5Pw==", + "node_modules/escape-string-regexp": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/escape-string-regexp/-/escape-string-regexp-4.0.0.tgz", + "integrity": "sha512-TtpcNJ3XAzx3Gq8sWRzJaVajRs0uVxA2YAkdb1jm2YkPz4G6egUFAyA3n5vtEIZefPk5Wa4UXbKuS5fKkJWdgA==", "dev": true, - "dependencies": { - "p-limit": "^3.0.2" - }, "engines": { "node": ">=10" }, @@ -3139,33 +2859,25 @@ "url": "https://github.com/sponsors/sindresorhus" } }, - "node_modules/eslint/node_modules/supports-color": { - "version": "7.2.0", - "resolved": "https://registry.npmjs.org/supports-color/-/supports-color-7.2.0.tgz", - "integrity": "sha512-qpCAvRl9stuOHveKsn7HncJRvv501qIacKzQlO/+Lwxc9+0q2wLyv4Dfvt80/DPn2pqOBsJdDiogXGR9+OvwRw==", + "node_modules/escodegen": { + "version": "2.1.0", + "resolved": "https://registry.npmjs.org/escodegen/-/escodegen-2.1.0.tgz", + "integrity": "sha512-2NlIDTwUWJN0mRPQOdtQBzbUHvdGY2P1VXSyU83Q3xKxM7WHX2Ql8dKq782Q9TgQUNOLEzEYu9bzLNj1q88I5w==", "dev": true, "dependencies": { - "has-flag": "^4.0.0" + "esprima": "^4.0.1", + "estraverse": "^5.2.0", + "esutils": "^2.0.2" }, - "engines": { - "node": ">=8" - } - }, - "node_modules/espree": { - "version": "9.6.1", - "resolved": "https://registry.npmjs.org/espree/-/espree-9.6.1.tgz", - "integrity": "sha512-oruZaFkjorTpF32kDSI5/75ViwGeZginGGy2NoOSg3Q9bnwlnmDm4HLnkl0RE3n+njDXR037aY1+x58Z/zFdwQ==", - "dev": true, - "dependencies": { - "acorn": "^8.9.0", - "acorn-jsx": "^5.3.2", - "eslint-visitor-keys": "^3.4.1" + "bin": { + "escodegen": "bin/escodegen.js", + "esgenerate": "bin/esgenerate.js" }, "engines": { - "node": "^12.22.0 || ^14.17.0 || >=16.0.0" + "node": ">=6.0" }, - "funding": { - "url": "https://opencollective.com/eslint" + "optionalDependencies": { + "source-map": "~0.6.1" } }, "node_modules/esprima": { @@ -3181,30 +2893,6 @@ "node": ">=4" } }, - "node_modules/esquery": { - "version": "1.5.0", - "resolved": "https://registry.npmjs.org/esquery/-/esquery-1.5.0.tgz", - "integrity": "sha512-YQLXUplAwJgCydQ78IMJywZCceoqk1oH01OERdSAJc/7U2AylwjhSCLDEtqwg811idIS/9fIU5GjG73IgjKMVg==", - "dev": true, - "dependencies": { - "estraverse": "^5.1.0" - }, - "engines": { - "node": ">=0.10" - } - }, - "node_modules/esrecurse": { - "version": "4.3.0", - "resolved": "https://registry.npmjs.org/esrecurse/-/esrecurse-4.3.0.tgz", - "integrity": "sha512-KmfKL3b6G+RXvP8N1vr3Tq1kL/oCFgn2NYXEtqP8/L3pKapUA4G8cFVaoF3SU323CD4XypR/ffioHmkti6/Tag==", - "dev": true, - "dependencies": { - "estraverse": "^5.2.0" - }, - "engines": { - "node": ">=4.0" - } - }, "node_modules/estraverse": { "version": "5.3.0", "resolved": "https://registry.npmjs.org/estraverse/-/estraverse-5.3.0.tgz", @@ -3224,28 +2912,52 @@ } }, "node_modules/execa": { - "version": "7.2.0", - "resolved": "https://registry.npmjs.org/execa/-/execa-7.2.0.tgz", - "integrity": "sha512-UduyVP7TLB5IcAQl+OzLyLcS/l32W/GLg+AhHJ+ow40FOk2U3SAllPwR44v4vmdFwIWqpdwxxpQbF1n5ta9seA==", + "version": "8.0.1", + "resolved": "https://registry.npmjs.org/execa/-/execa-8.0.1.tgz", + "integrity": "sha512-VyhnebXciFV2DESc+p6B+y0LjSm0krU4OgJN44qFAhBY0TJ+1V61tYD2+wHusZ6F9n5K+vl8k0sTy7PEfV4qpg==", "dev": true, "dependencies": { "cross-spawn": "^7.0.3", - "get-stream": "^6.0.1", - "human-signals": "^4.3.0", + "get-stream": "^8.0.1", + "human-signals": "^5.0.0", "is-stream": "^3.0.0", "merge-stream": "^2.0.0", "npm-run-path": "^5.1.0", "onetime": "^6.0.0", - "signal-exit": "^3.0.7", + "signal-exit": "^4.1.0", "strip-final-newline": "^3.0.0" }, "engines": { - "node": "^14.18.0 || ^16.14.0 || >=18.0.0" + "node": ">=16.17" }, "funding": { "url": "https://github.com/sindresorhus/execa?sponsor=1" } }, + "node_modules/execa/node_modules/is-stream": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/is-stream/-/is-stream-3.0.0.tgz", + "integrity": "sha512-LnQR4bZ9IADDRSkvpqMGvt/tEJWclzklNgSw48V5EAaAeDd6qGvN8ei6k5p0tvxSR171VmGyHuTiAOfxAbr8kA==", + "dev": true, + "engines": { + "node": "^12.20.0 || ^14.13.1 || >=16.0.0" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/execa/node_modules/signal-exit": { + "version": "4.1.0", + "resolved": "https://registry.npmjs.org/signal-exit/-/signal-exit-4.1.0.tgz", + "integrity": "sha512-bzyZ1e88w9O1iNJbKnOlvYTrWPDl46O1bG0D3XInv+9tkPrxrN8jUUTiFlDkkmKWgn1M6CfIA13SuGqOa9Korw==", + "dev": true, + "engines": { + "node": ">=14" + }, + "funding": { + "url": "https://github.com/sponsors/isaacs" + } + }, "node_modules/external-editor": { "version": "3.1.0", "resolved": "https://registry.npmjs.org/external-editor/-/external-editor-3.1.0.tgz", @@ -3260,16 +2972,10 @@ "node": ">=4" } }, - "node_modules/fast-deep-equal": { - "version": "3.1.3", - "resolved": "https://registry.npmjs.org/fast-deep-equal/-/fast-deep-equal-3.1.3.tgz", - "integrity": "sha512-f3qQ9oQy9j2AhBe/H9VC91wLmKBCCU/gDOnKNAYG5hswO7BLKj09Hc5HYNz9cGI++xlpDCIgDaitVs03ATR84Q==", - "dev": true - }, "node_modules/fast-glob": { - "version": "3.3.1", - "resolved": "https://registry.npmjs.org/fast-glob/-/fast-glob-3.3.1.tgz", - "integrity": "sha512-kNFPyjhh5cKjrUltxs+wFx+ZkbRaxxmZ+X0ZU31SOsxCEtP9VPgtq2teZw1DebupL5GmDaNQ6yKMMVcM41iqDg==", + "version": "3.3.2", + "resolved": "https://registry.npmjs.org/fast-glob/-/fast-glob-3.3.2.tgz", + "integrity": "sha512-oX2ruAFQwf/Orj8m737Y5adxDQO0LAB7/S5MnxCdTNDd4p6BsyIVsv9JQsATbTSq8KHRpLwIHbVlUNatxd+1Ow==", "dev": true, "dependencies": { "@nodelib/fs.stat": "^2.0.2", @@ -3282,22 +2988,10 @@ "node": ">=8.6.0" } }, - "node_modules/fast-json-stable-stringify": { - "version": "2.1.0", - "resolved": "https://registry.npmjs.org/fast-json-stable-stringify/-/fast-json-stable-stringify-2.1.0.tgz", - "integrity": "sha512-lhd/wF+Lk98HZoTCtlVraHtfh5XYijIjalXck7saUtuanSDyLMxnHhSXEDJqHxD7msR8D0uCmqlkwjCV8xvwHw==", - "dev": true - }, - "node_modules/fast-levenshtein": { - "version": "2.0.6", - "resolved": "https://registry.npmjs.org/fast-levenshtein/-/fast-levenshtein-2.0.6.tgz", - "integrity": "sha512-DCXu6Ifhqcks7TZKY3Hxp3y6qphY5SJZmrWMDrKcERSOXWQdMhU9Ig/PYrzyw/ul9jOIyh0N4M0tbC5hodg8dw==", - "dev": true - }, "node_modules/fastq": { - "version": "1.15.0", - "resolved": "https://registry.npmjs.org/fastq/-/fastq-1.15.0.tgz", - "integrity": "sha512-wBrocU2LCXXa+lWBt8RoIRD89Fi8OdABODa/kEnyeyjS5aZO5/GNvI5sEINADqP/h8M29UHTHUb53sUu5Ihqdw==", + "version": "1.17.1", + "resolved": "https://registry.npmjs.org/fastq/-/fastq-1.17.1.tgz", + "integrity": "sha512-sRVD3lWVIXWg6By68ZN7vho9a1pQcN/WBFaAAsDDFzlJjvoGx0P8z7V1t72grFJfJhu3YPZBuu25f7Kaw2jN1w==", "dev": true, "dependencies": { "reusify": "^1.0.4" @@ -3342,16 +3036,28 @@ "url": "https://github.com/sponsors/sindresorhus" } }, - "node_modules/file-entry-cache": { - "version": "6.0.1", - "resolved": "https://registry.npmjs.org/file-entry-cache/-/file-entry-cache-6.0.1.tgz", - "integrity": "sha512-7Gps/XWymbLk2QLYK4NzpMOrYjMhdIxXuIvy2QBsLE6ljuodKvdkWs/cpyJJ3CVIVpH0Oi1Hvg1ovbMzLdFBBg==", + "node_modules/figures/node_modules/escape-string-regexp": { + "version": "5.0.0", + "resolved": "https://registry.npmjs.org/escape-string-regexp/-/escape-string-regexp-5.0.0.tgz", + "integrity": "sha512-/veY75JbMK4j1yjvuUxuVsiS/hr/4iHs9FTT6cgTexxdE0Ly/glccBAkloH/DofkjRbZU3bnoj38mOmhkZ0lHw==", "dev": true, - "dependencies": { - "flat-cache": "^3.0.4" + "engines": { + "node": ">=12" }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/figures/node_modules/is-unicode-supported": { + "version": "1.3.0", + "resolved": "https://registry.npmjs.org/is-unicode-supported/-/is-unicode-supported-1.3.0.tgz", + "integrity": "sha512-43r2mRvz+8JRIKnWJ+3j8JtjRKZ6GmjzfaE/qiBJnikNnYv/6bagRJ1kUhNk8R5EX/GkobD+r+sfxCPJsiKBLQ==", + "dev": true, "engines": { - "node": "^10.12.0 || >=12.0.0" + "node": ">=12" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" } }, "node_modules/filename-reserved-regex": { @@ -3410,16 +3116,19 @@ } }, "node_modules/find-up": { - "version": "4.1.0", - "resolved": "https://registry.npmjs.org/find-up/-/find-up-4.1.0.tgz", - "integrity": "sha512-PpOwAdQ/YlXQ2vj8a3h8IipDuYRi3wceVQQGYWxNINccq40Anw7BlsEXCMbt1Zt+OLA6Fq9suIpIWD0OsnISlw==", + "version": "5.0.0", + "resolved": "https://registry.npmjs.org/find-up/-/find-up-5.0.0.tgz", + "integrity": "sha512-78/PXT1wlLLDgTzDs7sjq9hzz0vXD+zn+7wypEe4fXQxCmdmqfGsEPQxmiCSQI3ajFV91bVSsvNtrJRiW6nGng==", "dev": true, "dependencies": { - "locate-path": "^5.0.0", + "locate-path": "^6.0.0", "path-exists": "^4.0.0" }, "engines": { - "node": ">=8" + "node": ">=10" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" } }, "node_modules/flat": { @@ -3431,26 +3140,6 @@ "flat": "cli.js" } }, - "node_modules/flat-cache": { - "version": "3.1.0", - "resolved": "https://registry.npmjs.org/flat-cache/-/flat-cache-3.1.0.tgz", - "integrity": "sha512-OHx4Qwrrt0E4jEIcI5/Xb+f+QmJYNj2rrK8wiIdQOIrB9WrrJL8cjZvXdXuBTkkEwEqLycb5BeZDV1o2i9bTew==", - "dev": true, - "dependencies": { - "flatted": "^3.2.7", - "keyv": "^4.5.3", - "rimraf": "^3.0.2" - }, - "engines": { - "node": ">=12.0.0" - } - }, - "node_modules/flatted": { - "version": "3.2.9", - "resolved": "https://registry.npmjs.org/flatted/-/flatted-3.2.9.tgz", - "integrity": "sha512-36yxDn5H7OFZQla0/jFJmbIKTdZAQHngCedGxiMmpNfEZM0sdEeT+WczLQrjK6D7o2aiyLYDnkw0R3JK0Qv1RQ==", - "dev": true - }, "node_modules/for-each": { "version": "0.3.3", "resolved": "https://registry.npmjs.org/for-each/-/for-each-0.3.3.tgz", @@ -3515,9 +3204,9 @@ ] }, "node_modules/fs-extra": { - "version": "11.1.1", - "resolved": "https://registry.npmjs.org/fs-extra/-/fs-extra-11.1.1.tgz", - "integrity": "sha512-MGIE4HOvQCeUCzmlHs0vXpih4ysz4wg9qiSAu6cd42lVwPbTM1TjV7RusoyQqMmk/95gdQZX72u+YW+c3eEpFQ==", + "version": "11.2.0", + "resolved": "https://registry.npmjs.org/fs-extra/-/fs-extra-11.2.0.tgz", + "integrity": "sha512-PmDi3uwK5nFuXh7XDTlVnS17xJS7vW36is2+w3xcv8SVxiB4NyATf4ctkVY5bkSjX0Y4nbvZCq1/EjtEyr9ktw==", "dev": true, "dependencies": { "graceful-fs": "^4.2.0", @@ -3549,10 +3238,13 @@ } }, "node_modules/function-bind": { - "version": "1.1.1", - "resolved": "https://registry.npmjs.org/function-bind/-/function-bind-1.1.1.tgz", - "integrity": "sha512-yIovAzMX49sF8Yl58fSCWJ5svSLuaibPxXQJFLmBObTuCr0Mf1KiPopGM9NiFjiYBCbfaa2Fh6breQ6ANVTI0A==", - "dev": true + "version": "1.1.2", + "resolved": "https://registry.npmjs.org/function-bind/-/function-bind-1.1.2.tgz", + "integrity": "sha512-7XHNxH7qX9xG5mIwxkhumTox/MIRNcOgDrxWsMt2pAr23WHp6MrRlN7FBSFpCpr+oVO0F744iUgR82nJMfG2SA==", + "dev": true, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } }, "node_modules/function.prototype.name": { "version": "1.1.6", @@ -3581,14 +3273,6 @@ "url": "https://github.com/sponsors/ljharb" } }, - "node_modules/generic-pool": { - "version": "3.9.0", - "resolved": "https://registry.npmjs.org/generic-pool/-/generic-pool-3.9.0.tgz", - "integrity": "sha512-hymDOu5B53XvN4QT9dBmZxPX4CWhBPPLguTZ9MMFeFa/Kg0xWVfylOVNlJji/E7yTZWFd/q9GO5TxDLq156D7g==", - "engines": { - "node": ">= 4" - } - }, "node_modules/gensync": { "version": "1.0.0-beta.2", "resolved": "https://registry.npmjs.org/gensync/-/gensync-1.0.0-beta.2.tgz", @@ -3607,16 +3291,32 @@ "node": "6.* || 8.* || >= 10.*" } }, + "node_modules/get-east-asian-width": { + "version": "1.2.0", + "resolved": "https://registry.npmjs.org/get-east-asian-width/-/get-east-asian-width-1.2.0.tgz", + "integrity": "sha512-2nk+7SIVb14QrgXFHcm84tD4bKQz0RxPuMT8Ag5KPOq7J5fEmAg0UbXdTOSHqNuHSU28k55qnceesxXRZGzKWA==", + "dev": true, + "engines": { + "node": ">=18" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, "node_modules/get-intrinsic": { - "version": "1.2.1", - "resolved": "https://registry.npmjs.org/get-intrinsic/-/get-intrinsic-1.2.1.tgz", - "integrity": "sha512-2DcsyfABl+gVHEfCOaTrWgyt+tb6MSEGmKq+kI5HwLbIYgjgmMcV8KQ41uaKz1xxUcn9tJtgFbQUEVcEbd0FYw==", + "version": "1.2.3", + "resolved": "https://registry.npmjs.org/get-intrinsic/-/get-intrinsic-1.2.3.tgz", + "integrity": "sha512-JIcZczvcMVE7AUOP+X72bh8HqHBRxFdz5PDHYtNG/lE3yk9b3KZBJlwFcTyPYjg3L4RLLmZJzvjxhaZVapxFrQ==", "dev": true, "dependencies": { - "function-bind": "^1.1.1", - "has": "^1.0.3", + "es-errors": "^1.0.0", + "function-bind": "^1.1.2", "has-proto": "^1.0.1", - "has-symbols": "^1.0.3" + "has-symbols": "^1.0.3", + "hasown": "^2.0.0" + }, + "engines": { + "node": ">= 0.4" }, "funding": { "url": "https://github.com/sponsors/ljharb" @@ -3632,12 +3332,12 @@ } }, "node_modules/get-stream": { - "version": "6.0.1", - "resolved": "https://registry.npmjs.org/get-stream/-/get-stream-6.0.1.tgz", - "integrity": "sha512-ts6Wi+2j3jQjqi70w5AlN8DFnkSwC+MqmxEzdEALB2qXZYV3X/b1CTfgPLGJNMeAWxdPfU8FO1ms3NUfaHCPYg==", + "version": "8.0.1", + "resolved": "https://registry.npmjs.org/get-stream/-/get-stream-8.0.1.tgz", + "integrity": "sha512-VaUJspBffn/LMCJVoMvSAdmscJyS1auj5Zulnn5UoYcY531UWmdwhRWkcGKnGU93m5HSXP9LP2usOryrBtQowA==", "dev": true, "engines": { - "node": ">=10" + "node": ">=16" }, "funding": { "url": "https://github.com/sponsors/sindresorhus" @@ -3659,14 +3359,26 @@ "url": "https://github.com/sponsors/ljharb" } }, + "node_modules/get-tsconfig": { + "version": "4.7.2", + "resolved": "https://registry.npmjs.org/get-tsconfig/-/get-tsconfig-4.7.2.tgz", + "integrity": "sha512-wuMsz4leaj5hbGgg4IvDU0bqJagpftG5l5cXIAvo8uZrqn0NJqwtfupTN00VnkQJPcIRrxYrm1Ue24btpCha2A==", + "dev": true, + "dependencies": { + "resolve-pkg-maps": "^1.0.0" + }, + "funding": { + "url": "https://github.com/privatenumber/get-tsconfig?sponsor=1" + } + }, "node_modules/get-uri": { - "version": "6.0.1", - "resolved": "https://registry.npmjs.org/get-uri/-/get-uri-6.0.1.tgz", - "integrity": "sha512-7ZqONUVqaabogsYNWlYj0t3YZaL6dhuEueZXGF+/YVmf6dHmaFg8/6psJKqhx9QykIDKzpGcy2cn4oV4YC7V/Q==", + "version": "6.0.2", + "resolved": "https://registry.npmjs.org/get-uri/-/get-uri-6.0.2.tgz", + "integrity": "sha512-5KLucCJobh8vBY1K07EFV4+cPZH3mrV9YeAruUseCQKHB58SGjjT2l9/eA9LD082IiuMjSlFJEcdJ27TXvbZNw==", "dev": true, "dependencies": { "basic-ftp": "^5.0.2", - "data-uri-to-buffer": "^5.0.1", + "data-uri-to-buffer": "^6.0.0", "debug": "^4.3.4", "fs-extra": "^8.1.0" }, @@ -3675,9 +3387,9 @@ } }, "node_modules/get-uri/node_modules/data-uri-to-buffer": { - "version": "5.0.1", - "resolved": "https://registry.npmjs.org/data-uri-to-buffer/-/data-uri-to-buffer-5.0.1.tgz", - "integrity": "sha512-a9l6T1qqDogvvnw0nKlfZzqsyikEBZBClF39V3TFoKhDtGBqHu2HkuomJc02j5zft8zrUaXEuoicLeW54RkzPg==", + "version": "6.0.1", + "resolved": "https://registry.npmjs.org/data-uri-to-buffer/-/data-uri-to-buffer-6.0.1.tgz", + "integrity": "sha512-MZd3VlchQkp8rdend6vrx7MmVDJzSNTBvghvKjirLkD+WTChA3KUf0jkE68Q4UyctNqI11zZO9/x2Yx+ub5Cvg==", "dev": true, "engines": { "node": ">= 14" @@ -3716,9 +3428,9 @@ } }, "node_modules/gh-pages": { - "version": "6.0.0", - "resolved": "https://registry.npmjs.org/gh-pages/-/gh-pages-6.0.0.tgz", - "integrity": "sha512-FXZWJRsvP/fK2HJGY+Di6FRNHvqFF6gOIELaopDjXXgjeOYSNURcuYwEO/6bwuq6koP5Lnkvnr5GViXzuOB89g==", + "version": "6.1.1", + "resolved": "https://registry.npmjs.org/gh-pages/-/gh-pages-6.1.1.tgz", + "integrity": "sha512-upnohfjBwN5hBP9w2dPE7HO5JJTHzSGMV1JrLrHvNuqmjoYHg6TBrCcnEoorjG/e0ejbuvnwyKMdTyM40PEByw==", "dev": true, "dependencies": { "async": "^3.2.4", @@ -3748,9 +3460,9 @@ } }, "node_modules/git-url-parse": { - "version": "13.1.0", - "resolved": "https://registry.npmjs.org/git-url-parse/-/git-url-parse-13.1.0.tgz", - "integrity": "sha512-5FvPJP/70WkIprlUZ33bm4UAaFdjcLkJLpWft1BeZKqwR0uhhNGoKwlUaPtVb4LxCSQ++erHapRak9kWGj+FCA==", + "version": "14.0.0", + "resolved": "https://registry.npmjs.org/git-url-parse/-/git-url-parse-14.0.0.tgz", + "integrity": "sha512-NnLweV+2A4nCvn4U/m2AoYu0pPKlsmhK9cknG7IMwsjFY1S2jxM+mAhsDxyxfCIGfGaD+dozsyX4b6vkYc83yQ==", "dev": true, "dependencies": { "git-up": "^7.0.0" @@ -3788,46 +3500,28 @@ "node": ">= 6" } }, - "node_modules/global-dirs": { - "version": "3.0.1", - "resolved": "https://registry.npmjs.org/global-dirs/-/global-dirs-3.0.1.tgz", - "integrity": "sha512-NBcGGFbBA9s1VzD41QXDG+3++t9Mn5t1FpLdhESY6oKY4gYTFpX4wO3sqGUa0Srjtbfj3szX0RnemmrVRUdULA==", - "dev": true, - "dependencies": { - "ini": "2.0.0" - }, - "engines": { - "node": ">=10" - }, - "funding": { - "url": "https://github.com/sponsors/sindresorhus" - } - }, - "node_modules/globals": { - "version": "13.21.0", - "resolved": "https://registry.npmjs.org/globals/-/globals-13.21.0.tgz", - "integrity": "sha512-ybyme3s4yy/t/3s35bewwXKOf7cvzfreG2lH0lZl0JB7I4GxRP2ghxOK/Nb9EkRXdbBXZLfq/p/0W2JUONB/Gg==", + "node_modules/global-dirs": { + "version": "3.0.1", + "resolved": "https://registry.npmjs.org/global-dirs/-/global-dirs-3.0.1.tgz", + "integrity": "sha512-NBcGGFbBA9s1VzD41QXDG+3++t9Mn5t1FpLdhESY6oKY4gYTFpX4wO3sqGUa0Srjtbfj3szX0RnemmrVRUdULA==", "dev": true, "dependencies": { - "type-fest": "^0.20.2" + "ini": "2.0.0" }, "engines": { - "node": ">=8" + "node": ">=10" }, "funding": { "url": "https://github.com/sponsors/sindresorhus" } }, - "node_modules/globals/node_modules/type-fest": { - "version": "0.20.2", - "resolved": "https://registry.npmjs.org/type-fest/-/type-fest-0.20.2.tgz", - "integrity": "sha512-Ne+eE4r0/iWnpAxD852z3A+N0Bt5RN//NjJwRd2VFHEmrywxf5vsZlh4R6lixl6B+wz/8d+maTSAkN1FIkI3LQ==", + "node_modules/globals": { + "version": "11.12.0", + "resolved": "https://registry.npmjs.org/globals/-/globals-11.12.0.tgz", + "integrity": "sha512-WOBp/EEGUiIsJSp7wcv/y6MO+lV9UoncWqxuFfm8eBwzWNgyfBd6Gz+IeKQ9jCmyhoH99g15M3T+QaVHFjizVA==", "dev": true, "engines": { - "node": ">=10" - }, - "funding": { - "url": "https://github.com/sponsors/sindresorhus" + "node": ">=4" } }, "node_modules/globalthis": { @@ -3898,30 +3592,24 @@ "url": "https://github.com/sindresorhus/got?sponsor=1" } }, + "node_modules/got/node_modules/get-stream": { + "version": "6.0.1", + "resolved": "https://registry.npmjs.org/get-stream/-/get-stream-6.0.1.tgz", + "integrity": "sha512-ts6Wi+2j3jQjqi70w5AlN8DFnkSwC+MqmxEzdEALB2qXZYV3X/b1CTfgPLGJNMeAWxdPfU8FO1ms3NUfaHCPYg==", + "dev": true, + "engines": { + "node": ">=10" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, "node_modules/graceful-fs": { "version": "4.2.11", "resolved": "https://registry.npmjs.org/graceful-fs/-/graceful-fs-4.2.11.tgz", "integrity": "sha512-RbJ5/jmFcNNCcDV5o9eTnBLJ/HszWV0P73bc+Ff4nS/rJj+YaS6IGyiOL0VoBYX+l1Wrl3k63h/KrH+nhJ0XvQ==", "dev": true }, - "node_modules/graphemer": { - "version": "1.4.0", - "resolved": "https://registry.npmjs.org/graphemer/-/graphemer-1.4.0.tgz", - "integrity": "sha512-EtKwoO6kxCL9WO5xipiHTZlSzBm7WLT627TqC/uVRd0HKmq8NXyebnNYxDoBi7wt8eTWrUrKXCOVaFq9x1kgag==", - "dev": true - }, - "node_modules/has": { - "version": "1.0.3", - "resolved": "https://registry.npmjs.org/has/-/has-1.0.3.tgz", - "integrity": "sha512-f2dvO0VU6Oej7RkWJGrehjbzMAjFp5/VKPp5tTpWIV4JHHZK1/BxbFRtf/siA2SWTe09caDmVtYYzWEIbBS4zw==", - "dev": true, - "dependencies": { - "function-bind": "^1.1.1" - }, - "engines": { - "node": ">= 0.4.0" - } - }, "node_modules/has-bigints": { "version": "1.0.2", "resolved": "https://registry.npmjs.org/has-bigints/-/has-bigints-1.0.2.tgz", @@ -3932,21 +3620,21 @@ } }, "node_modules/has-flag": { - "version": "3.0.0", - "resolved": "https://registry.npmjs.org/has-flag/-/has-flag-3.0.0.tgz", - "integrity": "sha512-sKJf1+ceQBr4SMkvQnBDNDtf4TXpVhVGateu0t918bl30FnbE2m4vNLX+VWe/dpjlb+HugGYzW7uQXH98HPEYw==", + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/has-flag/-/has-flag-4.0.0.tgz", + "integrity": "sha512-EykJT/Q1KjTWctppgIAgfSO0tKVuZUjhgMr17kqTumMl6Afv3EISleU7qZUzoXDFTAHTDC4NOoG/ZxU3EvlMPQ==", "dev": true, "engines": { - "node": ">=4" + "node": ">=8" } }, "node_modules/has-property-descriptors": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/has-property-descriptors/-/has-property-descriptors-1.0.0.tgz", - "integrity": "sha512-62DVLZGoiEBDHQyqG4w9xCuZ7eJEwNmJRWw2VY84Oedb7WFcA27fiEVe8oUQx9hAUJ4ekurquucTGwsyO1XGdQ==", + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/has-property-descriptors/-/has-property-descriptors-1.0.1.tgz", + "integrity": "sha512-VsX8eaIewvas0xnvinAe9bw4WfIeODpGYikiWYLH+dma0Jw6KHYqWiWfhQlgOVK8D6PvjubK5Uc4P0iIhIcNVg==", "dev": true, "dependencies": { - "get-intrinsic": "^1.1.1" + "get-intrinsic": "^1.2.2" }, "funding": { "url": "https://github.com/sponsors/ljharb" @@ -3977,12 +3665,12 @@ } }, "node_modules/has-tostringtag": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/has-tostringtag/-/has-tostringtag-1.0.0.tgz", - "integrity": "sha512-kFjcSNhnlGV1kyoGk7OXKSawH5JOb/LzUc5w9B02hOTO0dfFRjbHQKvg1d6cf3HbeUmtU9VbbV3qzZ2Teh97WQ==", + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/has-tostringtag/-/has-tostringtag-1.0.2.tgz", + "integrity": "sha512-NqADB8VjPFLM2V0VvHUewwwsw0ZWBaIdgo+ieHtK3hasLz4qeCRjYcqfB6AQrBggRKppKF8L52/VqdVsO47Dlw==", "dev": true, "dependencies": { - "has-symbols": "^1.0.2" + "has-symbols": "^1.0.3" }, "engines": { "node": ">= 0.4" @@ -3991,18 +3679,6 @@ "url": "https://github.com/sponsors/ljharb" } }, - "node_modules/has-yarn": { - "version": "3.0.0", - "resolved": "https://registry.npmjs.org/has-yarn/-/has-yarn-3.0.0.tgz", - "integrity": "sha512-IrsVwUHhEULx3R8f/aA8AHuEzAorplsab/v8HBzEiIukwq5i/EC+xmOW+HfP1OaDP+2JkgT1yILHN2O3UFIbcA==", - "dev": true, - "engines": { - "node": "^12.20.0 || ^14.13.1 || >=16.0.0" - }, - "funding": { - "url": "https://github.com/sponsors/sindresorhus" - } - }, "node_modules/hasha": { "version": "5.2.2", "resolved": "https://registry.npmjs.org/hasha/-/hasha-5.2.2.tgz", @@ -4019,25 +3695,16 @@ "url": "https://github.com/sponsors/sindresorhus" } }, - "node_modules/hasha/node_modules/is-stream": { - "version": "2.0.1", - "resolved": "https://registry.npmjs.org/is-stream/-/is-stream-2.0.1.tgz", - "integrity": "sha512-hFoiJiTl63nn+kstHGBtewWSKnQLpyb155KHheA1l39uvtO9nWIop1p3udqPcUd/xbF1VLMO4n7OI6p7RbngDg==", + "node_modules/hasown": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/hasown/-/hasown-2.0.0.tgz", + "integrity": "sha512-vUptKVTpIJhcczKBbgnS+RtcuYMB8+oNzPK2/Hp3hanz8JmpATdmmgLgSaadVREkDm+e2giHwY3ZRkyjSIDDFA==", "dev": true, - "engines": { - "node": ">=8" + "dependencies": { + "function-bind": "^1.1.2" }, - "funding": { - "url": "https://github.com/sponsors/sindresorhus" - } - }, - "node_modules/hasha/node_modules/type-fest": { - "version": "0.8.1", - "resolved": "https://registry.npmjs.org/type-fest/-/type-fest-0.8.1.tgz", - "integrity": "sha512-4dbzIzqvjtgiM5rw1k5rEHtBANKmdudhGyBEajN01fEyhaAIhsoKNy6y7+IN93IfpFtwY9iqi7kD+xwKhQsNJA==", - "dev": true, "engines": { - "node": ">=8" + "node": ">= 0.4" } }, "node_modules/he": { @@ -4075,9 +3742,9 @@ } }, "node_modules/http2-wrapper": { - "version": "2.2.0", - "resolved": "https://registry.npmjs.org/http2-wrapper/-/http2-wrapper-2.2.0.tgz", - "integrity": "sha512-kZB0wxMo0sh1PehyjJUWRFEd99KC5TLjZ2cULC4f9iqJBAmKQQXEICjxl5iPJRwP40dpeHFqqhm7tYCvODpqpQ==", + "version": "2.2.1", + "resolved": "https://registry.npmjs.org/http2-wrapper/-/http2-wrapper-2.2.1.tgz", + "integrity": "sha512-V5nVw1PAOgfI3Lmeaj2Exmeg7fenjhRUgz1lPSezy1CuhPYbgQtbQj4jZfEAEMlaL+vupsvhjqCyjzob0yxsmQ==", "dev": true, "dependencies": { "quick-lru": "^5.1.1", @@ -4101,12 +3768,12 @@ } }, "node_modules/human-signals": { - "version": "4.3.1", - "resolved": "https://registry.npmjs.org/human-signals/-/human-signals-4.3.1.tgz", - "integrity": "sha512-nZXjEF2nbo7lIw3mgYjItAfgQXog3OjJogSbKa2CQIIvSGWcKgeJnQlNXip6NglNzYH45nSRiEVimMvYL8DDqQ==", + "version": "5.0.0", + "resolved": "https://registry.npmjs.org/human-signals/-/human-signals-5.0.0.tgz", + "integrity": "sha512-AXcZb6vzzrFAUE61HnN4mpLqd/cSIwNQjtNWR0euPm6y0iqx3G4gOXaIDdtdDwZmhwe82LA6+zinmW4UBWVePQ==", "dev": true, "engines": { - "node": ">=14.18.0" + "node": ">=16.17.0" } }, "node_modules/iconv-lite": { @@ -4142,9 +3809,9 @@ ] }, "node_modules/ignore": { - "version": "5.2.4", - "resolved": "https://registry.npmjs.org/ignore/-/ignore-5.2.4.tgz", - "integrity": "sha512-MAb38BcSbH0eHNBxn7ql2NH/kX33OkB3lZ1BNdh7ENeRChHTYsTvWrMubiIAMNS2llXEEgZ1MUOBtXChP3kaFQ==", + "version": "5.3.1", + "resolved": "https://registry.npmjs.org/ignore/-/ignore-5.3.1.tgz", + "integrity": "sha512-5Fytz/IraMjqpwfd34ke28PTVMjZjJG2MPn5t7OE4eUCUNf8BAa7b5WUS9/Qvr6mwOQS7Mk6vdsMno5he+T8Xw==", "dev": true, "engines": { "node": ">= 4" @@ -4166,6 +3833,15 @@ "url": "https://github.com/sponsors/sindresorhus" } }, + "node_modules/import-fresh/node_modules/resolve-from": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/resolve-from/-/resolve-from-4.0.0.tgz", + "integrity": "sha512-pb/MYmXstAkysRFx8piNI1tGFNQIFA3vkE3Gq4EuA1dF6gHp/+vgZqsCGJapvy8N3Q+4o7FwvquPJcnZ7RYy4g==", + "dev": true, + "engines": { + "node": ">=4" + } + }, "node_modules/import-lazy": { "version": "4.0.0", "resolved": "https://registry.npmjs.org/import-lazy/-/import-lazy-4.0.0.tgz", @@ -4219,12 +3895,12 @@ } }, "node_modules/inquirer": { - "version": "9.2.10", - "resolved": "https://registry.npmjs.org/inquirer/-/inquirer-9.2.10.tgz", - "integrity": "sha512-tVVNFIXU8qNHoULiazz612GFl+yqNfjMTbLuViNJE/d860Qxrd3NMrse8dm40VUQLOQeULvaQF8lpAhvysjeyA==", + "version": "9.2.12", + "resolved": "https://registry.npmjs.org/inquirer/-/inquirer-9.2.12.tgz", + "integrity": "sha512-mg3Fh9g2zfuVWJn6lhST0O7x4n03k7G8Tx5nvikJkbq8/CK47WDVm+UznF0G6s5Zi0KcyUisr6DU8T67N5U+1Q==", "dev": true, "dependencies": { - "@ljharb/through": "^2.3.9", + "@ljharb/through": "^2.3.11", "ansi-escapes": "^4.3.2", "chalk": "^5.3.0", "cli-cursor": "^3.1.0", @@ -4244,48 +3920,16 @@ "node": ">=14.18.0" } }, - "node_modules/inquirer/node_modules/bl": { - "version": "4.1.0", - "resolved": "https://registry.npmjs.org/bl/-/bl-4.1.0.tgz", - "integrity": "sha512-1W07cM9gS6DcLperZfFSj+bWLtaPGSOHWhPiGzXmvVJbRLdG82sH/Kn8EtW1VqWVA54AKf2h5k5BbnIbwF3h6w==", - "dev": true, - "dependencies": { - "buffer": "^5.5.0", - "inherits": "^2.0.4", - "readable-stream": "^3.4.0" - } - }, - "node_modules/inquirer/node_modules/buffer": { - "version": "5.7.1", - "resolved": "https://registry.npmjs.org/buffer/-/buffer-5.7.1.tgz", - "integrity": "sha512-EHcyIPBQ4BSGlvjB16k5KgAJ27CIsHY/2JBmCRReo48y9rQ3MaUzWX3KVlBa4U7MyX02HdVj0K7C3WaB3ju7FQ==", - "dev": true, - "funding": [ - { - "type": "github", - "url": "https://github.com/sponsors/feross" - }, - { - "type": "patreon", - "url": "https://www.patreon.com/feross" - }, - { - "type": "consulting", - "url": "https://feross.org/support" - } - ], - "dependencies": { - "base64-js": "^1.3.1", - "ieee754": "^1.1.13" - } - }, - "node_modules/inquirer/node_modules/has-flag": { - "version": "4.0.0", - "resolved": "https://registry.npmjs.org/has-flag/-/has-flag-4.0.0.tgz", - "integrity": "sha512-EykJT/Q1KjTWctppgIAgfSO0tKVuZUjhgMr17kqTumMl6Afv3EISleU7qZUzoXDFTAHTDC4NOoG/ZxU3EvlMPQ==", + "node_modules/inquirer/node_modules/chalk": { + "version": "5.3.0", + "resolved": "https://registry.npmjs.org/chalk/-/chalk-5.3.0.tgz", + "integrity": "sha512-dLitG79d+GV1Nb/VYcCDFivJeK1hiukt9QjRNVOsUtTy1rR1YJsmpGGTZ3qJos+uw7WmWF4wUwBd9jxjocFC2w==", "dev": true, "engines": { - "node": ">=8" + "node": "^12.17.0 || ^14.13 || >=16.0.0" + }, + "funding": { + "url": "https://github.com/chalk/chalk?sponsor=1" } }, "node_modules/inquirer/node_modules/is-interactive": { @@ -4297,50 +3941,6 @@ "node": ">=8" } }, - "node_modules/inquirer/node_modules/is-unicode-supported": { - "version": "0.1.0", - "resolved": "https://registry.npmjs.org/is-unicode-supported/-/is-unicode-supported-0.1.0.tgz", - "integrity": "sha512-knxG2q4UC3u8stRGyAVJCOdxFmv5DZiRcdlIaAQXAbSfJya+OhopNotLQrstBhququ4ZpuKbDc/8S6mgXgPFPw==", - "dev": true, - "engines": { - "node": ">=10" - }, - "funding": { - "url": "https://github.com/sponsors/sindresorhus" - } - }, - "node_modules/inquirer/node_modules/log-symbols": { - "version": "4.1.0", - "resolved": "https://registry.npmjs.org/log-symbols/-/log-symbols-4.1.0.tgz", - "integrity": "sha512-8XPvpAA8uyhfteu8pIvQxpJZ7SYYdpUivZpGy6sFsBuKRY/7rQGavedeB8aK+Zkyq6upMFVL/9AW6vOYzfRyLg==", - "dev": true, - "dependencies": { - "chalk": "^4.1.0", - "is-unicode-supported": "^0.1.0" - }, - "engines": { - "node": ">=10" - }, - "funding": { - "url": "https://github.com/sponsors/sindresorhus" - } - }, - "node_modules/inquirer/node_modules/log-symbols/node_modules/chalk": { - "version": "4.1.2", - "resolved": "https://registry.npmjs.org/chalk/-/chalk-4.1.2.tgz", - "integrity": "sha512-oKnbhFyRIXpUuez8iBMmyEa4nbj4IOQyuhc/wy9kY7/WVPcwIO9VA668Pu8RkO7+0G76SLROeyw9CpQ061i4mA==", - "dev": true, - "dependencies": { - "ansi-styles": "^4.1.0", - "supports-color": "^7.1.0" - }, - "engines": { - "node": ">=10" - }, - "funding": { - "url": "https://github.com/chalk/chalk?sponsor=1" - } - }, "node_modules/inquirer/node_modules/ora": { "version": "5.4.1", "resolved": "https://registry.npmjs.org/ora/-/ora-5.4.1.tgz", @@ -4393,13 +3993,13 @@ } }, "node_modules/internal-slot": { - "version": "1.0.5", - "resolved": "https://registry.npmjs.org/internal-slot/-/internal-slot-1.0.5.tgz", - "integrity": "sha512-Y+R5hJrzs52QCG2laLn4udYVnxsfny9CpOhNhUvk/SSSVyF6T27FzRbF0sroPidSu3X8oEAkOn2K804mjpt6UQ==", + "version": "1.0.6", + "resolved": "https://registry.npmjs.org/internal-slot/-/internal-slot-1.0.6.tgz", + "integrity": "sha512-Xj6dv+PsbtwyPpEflsejS+oIZxmMlV44zAhG479uYu89MsjcYOhCFnNyKrkJrihbsiasQyY0afoCl/9BLR65bg==", "dev": true, "dependencies": { - "get-intrinsic": "^1.2.0", - "has": "^1.0.3", + "get-intrinsic": "^1.2.2", + "hasown": "^2.0.0", "side-channel": "^1.0.4" }, "engines": { @@ -4438,14 +4038,16 @@ } }, "node_modules/is-array-buffer": { - "version": "3.0.2", - "resolved": "https://registry.npmjs.org/is-array-buffer/-/is-array-buffer-3.0.2.tgz", - "integrity": "sha512-y+FyyR/w8vfIRq4eQcM1EYgSTnmHXPqaF+IgzgraytCFq5Xh8lllDVmAZolPJiZttZLeFSINPYMaEJ7/vWUa1w==", + "version": "3.0.4", + "resolved": "https://registry.npmjs.org/is-array-buffer/-/is-array-buffer-3.0.4.tgz", + "integrity": "sha512-wcjaerHw0ydZwfhiKbXJWLDY8A7yV7KhjQOpb83hGgGfId/aQa4TOvwyzn2PuswW2gPCYEL/nEAiSVpdOj1lXw==", "dev": true, "dependencies": { "call-bind": "^1.0.2", - "get-intrinsic": "^1.2.0", - "is-typed-array": "^1.1.10" + "get-intrinsic": "^1.2.1" + }, + "engines": { + "node": ">= 0.4" }, "funding": { "url": "https://github.com/sponsors/ljharb" @@ -4522,12 +4124,12 @@ } }, "node_modules/is-core-module": { - "version": "2.13.0", - "resolved": "https://registry.npmjs.org/is-core-module/-/is-core-module-2.13.0.tgz", - "integrity": "sha512-Z7dk6Qo8pOCp3l4tsX2C5ZVas4V+UxwQodwZhLopL91TX8UyyHEXafPcyoeeWuLrwzHcr3igO78wNLwHJHsMCQ==", + "version": "2.13.1", + "resolved": "https://registry.npmjs.org/is-core-module/-/is-core-module-2.13.1.tgz", + "integrity": "sha512-hHrIjvZsftOsvKSn2TRYl63zvxsgE0K+0mYMoH6gD4omR5IWB2KynivBQczo3+wF1cCkjzvptnI9Q0sPU66ilw==", "dev": true, "dependencies": { - "has": "^1.0.3" + "hasown": "^2.0.0" }, "funding": { "url": "https://github.com/sponsors/ljharb" @@ -4593,6 +4195,21 @@ "node": ">=0.10.0" } }, + "node_modules/is-in-ci": { + "version": "0.1.0", + "resolved": "https://registry.npmjs.org/is-in-ci/-/is-in-ci-0.1.0.tgz", + "integrity": "sha512-d9PXLEY0v1iJ64xLiQMJ51J128EYHAaOR4yZqQi8aHGfw6KgifM3/Viw1oZZ1GCVmb3gBuyhLyHj0HgR2DhSXQ==", + "dev": true, + "bin": { + "is-in-ci": "cli.js" + }, + "engines": { + "node": ">=18" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, "node_modules/is-inside-container": { "version": "1.0.0", "resolved": "https://registry.npmjs.org/is-inside-container/-/is-inside-container-1.0.0.tgz", @@ -4723,15 +4340,6 @@ "node": ">=8" } }, - "node_modules/is-plain-object": { - "version": "5.0.0", - "resolved": "https://registry.npmjs.org/is-plain-object/-/is-plain-object-5.0.0.tgz", - "integrity": "sha512-VRSzKkbMm5jMDoKLbltAkFQ5Qr7VDiTFGXxYFXXowVj387GeGNOCsOH6Msy00SGZ3Fp84b1Naa1psqgcCIEP5Q==", - "dev": true, - "engines": { - "node": ">=0.10.0" - } - }, "node_modules/is-regex": { "version": "1.1.4", "resolved": "https://registry.npmjs.org/is-regex/-/is-regex-1.1.4.tgz", @@ -4779,12 +4387,12 @@ } }, "node_modules/is-stream": { - "version": "3.0.0", - "resolved": "https://registry.npmjs.org/is-stream/-/is-stream-3.0.0.tgz", - "integrity": "sha512-LnQR4bZ9IADDRSkvpqMGvt/tEJWclzklNgSw48V5EAaAeDd6qGvN8ei6k5p0tvxSR171VmGyHuTiAOfxAbr8kA==", + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/is-stream/-/is-stream-2.0.1.tgz", + "integrity": "sha512-hFoiJiTl63nn+kstHGBtewWSKnQLpyb155KHheA1l39uvtO9nWIop1p3udqPcUd/xbF1VLMO4n7OI6p7RbngDg==", "dev": true, "engines": { - "node": "^12.20.0 || ^14.13.1 || >=16.0.0" + "node": ">=8" }, "funding": { "url": "https://github.com/sponsors/sindresorhus" @@ -4821,12 +4429,12 @@ } }, "node_modules/is-typed-array": { - "version": "1.1.12", - "resolved": "https://registry.npmjs.org/is-typed-array/-/is-typed-array-1.1.12.tgz", - "integrity": "sha512-Z14TF2JNG8Lss5/HMqt0//T9JeHXttXy5pH/DBU4vi98ozO2btxzq9MwYDZYnKwU8nRsz/+GVFVRDq3DkVuSPg==", + "version": "1.1.13", + "resolved": "https://registry.npmjs.org/is-typed-array/-/is-typed-array-1.1.13.tgz", + "integrity": "sha512-uZ25/bUAlUY5fR4OKT4rZQEBrzQWYV9ZJYGGsUmEJ6thodVJ1HX64ePQ6Z0qPWP+m+Uq6e9UugrE38jeYsDSMw==", "dev": true, "dependencies": { - "which-typed-array": "^1.1.11" + "which-typed-array": "^1.1.14" }, "engines": { "node": ">= 0.4" @@ -4842,12 +4450,12 @@ "dev": true }, "node_modules/is-unicode-supported": { - "version": "1.3.0", - "resolved": "https://registry.npmjs.org/is-unicode-supported/-/is-unicode-supported-1.3.0.tgz", - "integrity": "sha512-43r2mRvz+8JRIKnWJ+3j8JtjRKZ6GmjzfaE/qiBJnikNnYv/6bagRJ1kUhNk8R5EX/GkobD+r+sfxCPJsiKBLQ==", + "version": "0.1.0", + "resolved": "https://registry.npmjs.org/is-unicode-supported/-/is-unicode-supported-0.1.0.tgz", + "integrity": "sha512-knxG2q4UC3u8stRGyAVJCOdxFmv5DZiRcdlIaAQXAbSfJya+OhopNotLQrstBhququ4ZpuKbDc/8S6mgXgPFPw==", "dev": true, "engines": { - "node": ">=12" + "node": ">=10" }, "funding": { "url": "https://github.com/sponsors/sindresorhus" @@ -4875,41 +4483,20 @@ } }, "node_modules/is-wsl": { - "version": "2.2.0", - "resolved": "https://registry.npmjs.org/is-wsl/-/is-wsl-2.2.0.tgz", - "integrity": "sha512-fKzAra0rGJUUBwGBgNkHZuToZcn+TtXHpeCgmkMJMMYx1sQDYaCSyjJBSCa2nH1DGm7s3n1oBnohoVTBaN7Lww==", + "version": "3.1.0", + "resolved": "https://registry.npmjs.org/is-wsl/-/is-wsl-3.1.0.tgz", + "integrity": "sha512-UcVfVfaK4Sc4m7X3dUSoHoozQGBEFeDC+zVo06t98xe8CzHSZZBekNXH+tu0NalHolcJ/QAGqS46Hef7QXBIMw==", "dev": true, "dependencies": { - "is-docker": "^2.0.0" - }, - "engines": { - "node": ">=8" - } - }, - "node_modules/is-wsl/node_modules/is-docker": { - "version": "2.2.1", - "resolved": "https://registry.npmjs.org/is-docker/-/is-docker-2.2.1.tgz", - "integrity": "sha512-F+i2BKsFrH66iaUFc0woD8sLy8getkwTwtOBjvs56Cx4CgJDeKQeqfz8wAYiSb8JOprWhHH5p77PbmYCvvUuXQ==", - "dev": true, - "bin": { - "is-docker": "cli.js" + "is-inside-container": "^1.0.0" }, "engines": { - "node": ">=8" + "node": ">=16" }, "funding": { "url": "https://github.com/sponsors/sindresorhus" } }, - "node_modules/is-yarn-global": { - "version": "0.4.1", - "resolved": "https://registry.npmjs.org/is-yarn-global/-/is-yarn-global-0.4.1.tgz", - "integrity": "sha512-/kppl+R+LO5VmhYSEWARUFjodS25D68gvj8W7z0I7OWhUla5xWu8KL6CtB2V0R6yqhnRgbcaREMr4EEM6htLPQ==", - "dev": true, - "engines": { - "node": ">=12" - } - }, "node_modules/isarray": { "version": "2.0.5", "resolved": "https://registry.npmjs.org/isarray/-/isarray-2.0.5.tgz", @@ -4939,9 +4526,9 @@ } }, "node_modules/istanbul-lib-coverage": { - "version": "3.2.0", - "resolved": "https://registry.npmjs.org/istanbul-lib-coverage/-/istanbul-lib-coverage-3.2.0.tgz", - "integrity": "sha512-eOeJ5BHCmHYvQK7xt9GkdHuzuCGS1Y6g9Gvnx3Ym33fz/HpLRYxiS0wHNr+m/MBC8B647Xt608vCDEvhl9c6Mw==", + "version": "3.2.2", + "resolved": "https://registry.npmjs.org/istanbul-lib-coverage/-/istanbul-lib-coverage-3.2.2.tgz", + "integrity": "sha512-O8dpsF+r0WV/8MNRKfnmrtCWhuKjxrq2w+jpzBL5UZKTi2LeVWnWOmWRxFlesJONmc+wLAGvKQZEOanko0LFTg==", "dev": true, "engines": { "node": ">=8" @@ -5005,15 +4592,6 @@ "node": ">=10" } }, - "node_modules/istanbul-lib-report/node_modules/has-flag": { - "version": "4.0.0", - "resolved": "https://registry.npmjs.org/has-flag/-/has-flag-4.0.0.tgz", - "integrity": "sha512-EykJT/Q1KjTWctppgIAgfSO0tKVuZUjhgMr17kqTumMl6Afv3EISleU7qZUzoXDFTAHTDC4NOoG/ZxU3EvlMPQ==", - "dev": true, - "engines": { - "node": ">=8" - } - }, "node_modules/istanbul-lib-report/node_modules/lru-cache": { "version": "6.0.0", "resolved": "https://registry.npmjs.org/lru-cache/-/lru-cache-6.0.0.tgz", @@ -5068,6 +4646,12 @@ "node": ">=8" } }, + "node_modules/istanbul-lib-report/node_modules/yallist": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/yallist/-/yallist-4.0.0.tgz", + "integrity": "sha512-3wdGidZyq5PB084XLES5TpOSRA3wjXAlIWMhum2kRcv/41Sn2emQ0dycQW4uZXLejwKvg6EsvbdlVL+FYEct7A==", + "dev": true + }, "node_modules/istanbul-lib-source-maps": { "version": "4.0.1", "resolved": "https://registry.npmjs.org/istanbul-lib-source-maps/-/istanbul-lib-source-maps-4.0.1.tgz", @@ -5159,18 +4743,6 @@ "integrity": "sha512-xyFwyhro/JEof6Ghe2iz2NcXoj2sloNsWr/XsERDK/oiPCfaNhl5ONfp+jQdAZRQQ0IJWNzH9zIZF7li91kh2w==", "dev": true }, - "node_modules/json-schema-traverse": { - "version": "0.4.1", - "resolved": "https://registry.npmjs.org/json-schema-traverse/-/json-schema-traverse-0.4.1.tgz", - "integrity": "sha512-xbbCH5dCYU5T8LcEhhuh7HJ88HXuW3qsI3Y0zOZFKfZEHcpWiHU/Jxzk629Brsab/mMiHQti9wMP+845RPe3Vg==", - "dev": true - }, - "node_modules/json-stable-stringify-without-jsonify": { - "version": "1.0.1", - "resolved": "https://registry.npmjs.org/json-stable-stringify-without-jsonify/-/json-stable-stringify-without-jsonify-1.0.1.tgz", - "integrity": "sha512-Bdboy+l7tA3OGW6FjyFHWkP5LuByj1Tk33Ljyq0axyzdk9//JSi2u3fP1QSmd1KNwq6VOKYGlAu87CisVir6Pw==", - "dev": true - }, "node_modules/json5": { "version": "2.2.3", "resolved": "https://registry.npmjs.org/json5/-/json5-2.2.3.tgz", @@ -5184,9 +4756,9 @@ } }, "node_modules/jsonc-parser": { - "version": "3.2.0", - "resolved": "https://registry.npmjs.org/jsonc-parser/-/jsonc-parser-3.2.0.tgz", - "integrity": "sha512-gfFQZrcTc8CnKXp6Y4/CBT3fTc0OVuDofpre4aEeEpSBPV5X5v4+Vmx+8snU7RLPrNHPKSgLxGo9YuQzz20o+w==", + "version": "3.2.1", + "resolved": "https://registry.npmjs.org/jsonc-parser/-/jsonc-parser-3.2.1.tgz", + "integrity": "sha512-AilxAyFOAcK5wA1+LeaySVBrHsGQvUFCDWXKpZjzaL0PqW+xfBOttn8GNtWKFWqneyMZj41MWF9Kl6iPWLwgOA==", "dev": true }, "node_modules/jsonfile": { @@ -5202,15 +4774,15 @@ } }, "node_modules/just-extend": { - "version": "4.2.1", - "resolved": "https://registry.npmjs.org/just-extend/-/just-extend-4.2.1.tgz", - "integrity": "sha512-g3UB796vUFIY90VIv/WX3L2c8CS2MdWUww3CNrYmqza1Fg0DURc2K/O4YrnklBdQarSJ/y8JnJYDGc+1iumQjg==", + "version": "6.2.0", + "resolved": "https://registry.npmjs.org/just-extend/-/just-extend-6.2.0.tgz", + "integrity": "sha512-cYofQu2Xpom82S6qD778jBDpwvvy39s1l/hrYij2u9AMdQcGRpaBu6kY4mVhuno5kJVi1DAz4aiphA2WI1/OAw==", "dev": true }, "node_modules/keyv": { - "version": "4.5.3", - "resolved": "https://registry.npmjs.org/keyv/-/keyv-4.5.3.tgz", - "integrity": "sha512-QCiSav9WaX1PgETJ+SpNnx2PRRapJ/oRSXM4VO5OGYGSjrxbKPVFVhB3l2OCbLCk329N8qyAtsJjSjvVBWzEug==", + "version": "4.5.4", + "resolved": "https://registry.npmjs.org/keyv/-/keyv-4.5.4.tgz", + "integrity": "sha512-oxVHkHR/EJf2CNXnWxRLW6mg7JyCCUcG0DtEGmL2ctUo1PNTin1PUil+r/+4r5MpVgC/fn1kjsx7mjSujKqIpw==", "dev": true, "dependencies": { "json-buffer": "3.0.1" @@ -5231,19 +4803,6 @@ "url": "https://github.com/sponsors/sindresorhus" } }, - "node_modules/levn": { - "version": "0.4.1", - "resolved": "https://registry.npmjs.org/levn/-/levn-0.4.1.tgz", - "integrity": "sha512-+bT2uH4E5LGE7h/n3evcS/sQlJXCpIp6ym8OWJ5eV6+67Dsql/LaaT7qJBAt2rzfoa/5QBGBhxDix1dMt2kQKQ==", - "dev": true, - "dependencies": { - "prelude-ls": "^1.2.1", - "type-check": "~0.4.0" - }, - "engines": { - "node": ">= 0.8.0" - } - }, "node_modules/lines-and-columns": { "version": "1.2.4", "resolved": "https://registry.npmjs.org/lines-and-columns/-/lines-and-columns-1.2.4.tgz", @@ -5251,15 +4810,18 @@ "dev": true }, "node_modules/locate-path": { - "version": "5.0.0", - "resolved": "https://registry.npmjs.org/locate-path/-/locate-path-5.0.0.tgz", - "integrity": "sha512-t7hw9pI+WvuwNJXwk5zVHpyhIqzg2qTlklJOf0mVxGSbe3Fp2VieZcduNYjaLDoy6p9uGpQEGWG87WpMKlNq8g==", + "version": "6.0.0", + "resolved": "https://registry.npmjs.org/locate-path/-/locate-path-6.0.0.tgz", + "integrity": "sha512-iPZK6eYjbxRu3uB4/WZ3EsEIMJFMqAoopl3R+zuq0UjcAm/MO6KCweDgPfP3elTztoKP3KtnVHxTn2NHBSDVUw==", "dev": true, "dependencies": { - "p-locate": "^4.1.0" + "p-locate": "^5.0.0" }, "engines": { - "node": ">=8" + "node": ">=10" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" } }, "node_modules/lodash": { @@ -5304,12 +4866,6 @@ "integrity": "sha512-0wJxfxH1wgO3GrbuP+dTTk7op+6L41QCXbGINEmD+ny/G/eCqGzxyCsh7159S+mgDDcoarnBw6PC1PS5+wUGgw==", "dev": true }, - "node_modules/lodash.merge": { - "version": "4.6.2", - "resolved": "https://registry.npmjs.org/lodash.merge/-/lodash.merge-4.6.2.tgz", - "integrity": "sha512-0KpjqXRVvrYyCsX1swR/XTK0va6VQkQM6MNo7PqW77ByjAhoARA8EfrP1N4+KlKj8YS0ZUCtRT/YUuhyYDujIQ==", - "dev": true - }, "node_modules/lodash.uniqby": { "version": "4.7.0", "resolved": "https://registry.npmjs.org/lodash.uniqby/-/lodash.uniqby-4.7.0.tgz", @@ -5317,16 +4873,16 @@ "dev": true }, "node_modules/log-symbols": { - "version": "5.1.0", - "resolved": "https://registry.npmjs.org/log-symbols/-/log-symbols-5.1.0.tgz", - "integrity": "sha512-l0x2DvrW294C9uDCoQe1VSU4gf529FkSZ6leBl4TiqZH/e+0R7hSfHQBNut2mNygDgHwvYHfFLn6Oxb3VWj2rA==", + "version": "4.1.0", + "resolved": "https://registry.npmjs.org/log-symbols/-/log-symbols-4.1.0.tgz", + "integrity": "sha512-8XPvpAA8uyhfteu8pIvQxpJZ7SYYdpUivZpGy6sFsBuKRY/7rQGavedeB8aK+Zkyq6upMFVL/9AW6vOYzfRyLg==", "dev": true, "dependencies": { - "chalk": "^5.0.0", - "is-unicode-supported": "^1.1.0" + "chalk": "^4.1.0", + "is-unicode-supported": "^0.1.0" }, "engines": { - "node": ">=12" + "node": ">=10" }, "funding": { "url": "https://github.com/sponsors/sindresorhus" @@ -5345,12 +4901,12 @@ } }, "node_modules/lru-cache": { - "version": "7.18.3", - "resolved": "https://registry.npmjs.org/lru-cache/-/lru-cache-7.18.3.tgz", - "integrity": "sha512-jumlc0BIUrS3qJGgIkWZsyfAM7NCWiBcCDhnd+3NNM5KbBmLTgHVfWBcg6W+rLUsIpzpERPsvwUP7CckAQSOoA==", + "version": "5.1.1", + "resolved": "https://registry.npmjs.org/lru-cache/-/lru-cache-5.1.1.tgz", + "integrity": "sha512-KpNARQA3Iwv+jTA0utUVVbrh+Jlrr1Fv0e56GGzAFOXN7dk/FviaDW8LHmK52DlcH4WP2n6gI8vN1aesBFgo9w==", "dev": true, - "engines": { - "node": ">=12" + "dependencies": { + "yallist": "^3.0.2" } }, "node_modules/lunr": { @@ -5386,12 +4942,6 @@ "url": "https://github.com/sponsors/sindresorhus" } }, - "node_modules/make-error": { - "version": "1.3.6", - "resolved": "https://registry.npmjs.org/make-error/-/make-error-1.3.6.tgz", - "integrity": "sha512-s8UhlNe7vPKomQhC1qFelMokr/Sc3AgNbso3n74mVPA5LTZwkB9NlXf4XPamLxJE8h0gh73rM94xvwRT2CVInw==", - "dev": true - }, "node_modules/marked": { "version": "4.3.0", "resolved": "https://registry.npmjs.org/marked/-/marked-4.3.0.tgz", @@ -5538,73 +5088,6 @@ "url": "https://opencollective.com/mochajs" } }, - "node_modules/mocha/node_modules/chalk": { - "version": "4.1.2", - "resolved": "https://registry.npmjs.org/chalk/-/chalk-4.1.2.tgz", - "integrity": "sha512-oKnbhFyRIXpUuez8iBMmyEa4nbj4IOQyuhc/wy9kY7/WVPcwIO9VA668Pu8RkO7+0G76SLROeyw9CpQ061i4mA==", - "dev": true, - "dependencies": { - "ansi-styles": "^4.1.0", - "supports-color": "^7.1.0" - }, - "engines": { - "node": ">=10" - }, - "funding": { - "url": "https://github.com/chalk/chalk?sponsor=1" - } - }, - "node_modules/mocha/node_modules/chalk/node_modules/supports-color": { - "version": "7.2.0", - "resolved": "https://registry.npmjs.org/supports-color/-/supports-color-7.2.0.tgz", - "integrity": "sha512-qpCAvRl9stuOHveKsn7HncJRvv501qIacKzQlO/+Lwxc9+0q2wLyv4Dfvt80/DPn2pqOBsJdDiogXGR9+OvwRw==", - "dev": true, - "dependencies": { - "has-flag": "^4.0.0" - }, - "engines": { - "node": ">=8" - } - }, - "node_modules/mocha/node_modules/cliui": { - "version": "7.0.4", - "resolved": "https://registry.npmjs.org/cliui/-/cliui-7.0.4.tgz", - "integrity": "sha512-OcRE68cOsVMXp1Yvonl/fzkQOyjLSu/8bhPDfQt0e0/Eb283TKP20Fs2MqoPsr9SwA595rRCA+QMzYc9nBP+JQ==", - "dev": true, - "dependencies": { - "string-width": "^4.2.0", - "strip-ansi": "^6.0.0", - "wrap-ansi": "^7.0.0" - } - }, - "node_modules/mocha/node_modules/escape-string-regexp": { - "version": "4.0.0", - "resolved": "https://registry.npmjs.org/escape-string-regexp/-/escape-string-regexp-4.0.0.tgz", - "integrity": "sha512-TtpcNJ3XAzx3Gq8sWRzJaVajRs0uVxA2YAkdb1jm2YkPz4G6egUFAyA3n5vtEIZefPk5Wa4UXbKuS5fKkJWdgA==", - "dev": true, - "engines": { - "node": ">=10" - }, - "funding": { - "url": "https://github.com/sponsors/sindresorhus" - } - }, - "node_modules/mocha/node_modules/find-up": { - "version": "5.0.0", - "resolved": "https://registry.npmjs.org/find-up/-/find-up-5.0.0.tgz", - "integrity": "sha512-78/PXT1wlLLDgTzDs7sjq9hzz0vXD+zn+7wypEe4fXQxCmdmqfGsEPQxmiCSQI3ajFV91bVSsvNtrJRiW6nGng==", - "dev": true, - "dependencies": { - "locate-path": "^6.0.0", - "path-exists": "^4.0.0" - }, - "engines": { - "node": ">=10" - }, - "funding": { - "url": "https://github.com/sponsors/sindresorhus" - } - }, "node_modules/mocha/node_modules/glob": { "version": "7.2.0", "resolved": "https://registry.npmjs.org/glob/-/glob-7.2.0.tgz", @@ -5637,58 +5120,6 @@ "node": "*" } }, - "node_modules/mocha/node_modules/has-flag": { - "version": "4.0.0", - "resolved": "https://registry.npmjs.org/has-flag/-/has-flag-4.0.0.tgz", - "integrity": "sha512-EykJT/Q1KjTWctppgIAgfSO0tKVuZUjhgMr17kqTumMl6Afv3EISleU7qZUzoXDFTAHTDC4NOoG/ZxU3EvlMPQ==", - "dev": true, - "engines": { - "node": ">=8" - } - }, - "node_modules/mocha/node_modules/is-unicode-supported": { - "version": "0.1.0", - "resolved": "https://registry.npmjs.org/is-unicode-supported/-/is-unicode-supported-0.1.0.tgz", - "integrity": "sha512-knxG2q4UC3u8stRGyAVJCOdxFmv5DZiRcdlIaAQXAbSfJya+OhopNotLQrstBhququ4ZpuKbDc/8S6mgXgPFPw==", - "dev": true, - "engines": { - "node": ">=10" - }, - "funding": { - "url": "https://github.com/sponsors/sindresorhus" - } - }, - "node_modules/mocha/node_modules/locate-path": { - "version": "6.0.0", - "resolved": "https://registry.npmjs.org/locate-path/-/locate-path-6.0.0.tgz", - "integrity": "sha512-iPZK6eYjbxRu3uB4/WZ3EsEIMJFMqAoopl3R+zuq0UjcAm/MO6KCweDgPfP3elTztoKP3KtnVHxTn2NHBSDVUw==", - "dev": true, - "dependencies": { - "p-locate": "^5.0.0" - }, - "engines": { - "node": ">=10" - }, - "funding": { - "url": "https://github.com/sponsors/sindresorhus" - } - }, - "node_modules/mocha/node_modules/log-symbols": { - "version": "4.1.0", - "resolved": "https://registry.npmjs.org/log-symbols/-/log-symbols-4.1.0.tgz", - "integrity": "sha512-8XPvpAA8uyhfteu8pIvQxpJZ7SYYdpUivZpGy6sFsBuKRY/7rQGavedeB8aK+Zkyq6upMFVL/9AW6vOYzfRyLg==", - "dev": true, - "dependencies": { - "chalk": "^4.1.0", - "is-unicode-supported": "^0.1.0" - }, - "engines": { - "node": ">=10" - }, - "funding": { - "url": "https://github.com/sponsors/sindresorhus" - } - }, "node_modules/mocha/node_modules/minimatch": { "version": "5.0.1", "resolved": "https://registry.npmjs.org/minimatch/-/minimatch-5.0.1.tgz", @@ -5705,122 +5136,15 @@ "version": "2.0.1", "resolved": "https://registry.npmjs.org/brace-expansion/-/brace-expansion-2.0.1.tgz", "integrity": "sha512-XnAIvQ8eM+kC6aULx6wuQiwVsnzsi9d3WxzV3FpWTGA19F621kwdbsAcFKXgKUHZWsy+mY6iL1sHTxWEFCytDA==", - "dev": true, - "dependencies": { - "balanced-match": "^1.0.0" - } - }, - "node_modules/mocha/node_modules/ms": { - "version": "2.1.3", - "resolved": "https://registry.npmjs.org/ms/-/ms-2.1.3.tgz", - "integrity": "sha512-6FlzubTLZG3J2a/NVCAleEhjzq5oxgHyaCU9yYXvcLsvoVaHJq/s5xXI6/XXP6tz7R9xAOtHnSO/tXtF3WRTlA==", - "dev": true - }, - "node_modules/mocha/node_modules/p-limit": { - "version": "3.1.0", - "resolved": "https://registry.npmjs.org/p-limit/-/p-limit-3.1.0.tgz", - "integrity": "sha512-TYOanM3wGwNGsZN2cVTYPArw454xnXj5qmWF1bEoAc4+cU/ol7GVh7odevjp1FNHduHc3KZMcFduxU5Xc6uJRQ==", - "dev": true, - "dependencies": { - "yocto-queue": "^0.1.0" - }, - "engines": { - "node": ">=10" - }, - "funding": { - "url": "https://github.com/sponsors/sindresorhus" - } - }, - "node_modules/mocha/node_modules/p-locate": { - "version": "5.0.0", - "resolved": "https://registry.npmjs.org/p-locate/-/p-locate-5.0.0.tgz", - "integrity": "sha512-LaNjtRWUBY++zB5nE/NwcaoMylSPk+S+ZHNB1TzdbMJMny6dynpAGt7X/tl/QYq3TIeE6nxHppbo2LGymrG5Pw==", - "dev": true, - "dependencies": { - "p-limit": "^3.0.2" - }, - "engines": { - "node": ">=10" - }, - "funding": { - "url": "https://github.com/sponsors/sindresorhus" - } - }, - "node_modules/mocha/node_modules/strip-json-comments": { - "version": "3.1.1", - "resolved": "https://registry.npmjs.org/strip-json-comments/-/strip-json-comments-3.1.1.tgz", - "integrity": "sha512-6fPc+R4ihwqP6N/aIv2f1gMH8lOVtWQHoqC4yK6oSDVVocumAsfCqjkXnqiYMhmMwS/mEHLp7Vehlt3ql6lEig==", - "dev": true, - "engines": { - "node": ">=8" - }, - "funding": { - "url": "https://github.com/sponsors/sindresorhus" - } - }, - "node_modules/mocha/node_modules/supports-color": { - "version": "8.1.1", - "resolved": "https://registry.npmjs.org/supports-color/-/supports-color-8.1.1.tgz", - "integrity": "sha512-MpUEN2OodtUzxvKQl72cUF7RQ5EiHsGvSsVG0ia9c5RbWGL2CI4C7EpPS8UTBIplnlzZiNuV56w+FuNxy3ty2Q==", - "dev": true, - "dependencies": { - "has-flag": "^4.0.0" - }, - "engines": { - "node": ">=10" - }, - "funding": { - "url": "https://github.com/chalk/supports-color?sponsor=1" - } - }, - "node_modules/mocha/node_modules/wrap-ansi": { - "version": "7.0.0", - "resolved": "https://registry.npmjs.org/wrap-ansi/-/wrap-ansi-7.0.0.tgz", - "integrity": "sha512-YVGIj2kamLSTxw6NsZjoBxfSwsn0ycdesmc4p+Q21c5zPuZ1pl+NfxVdxPtdHvmNVOQ6XSYG4AUtyt/Fi7D16Q==", - "dev": true, - "dependencies": { - "ansi-styles": "^4.0.0", - "string-width": "^4.1.0", - "strip-ansi": "^6.0.0" - }, - "engines": { - "node": ">=10" - }, - "funding": { - "url": "https://github.com/chalk/wrap-ansi?sponsor=1" - } - }, - "node_modules/mocha/node_modules/yargs": { - "version": "16.2.0", - "resolved": "https://registry.npmjs.org/yargs/-/yargs-16.2.0.tgz", - "integrity": "sha512-D1mvvtDG0L5ft/jGWkLpG1+m0eQxOfaBvTNELraWj22wSVUMWxZUvYgJYcKh6jGGIkJFhH4IZPQhR4TKpc8mBw==", - "dev": true, - "dependencies": { - "cliui": "^7.0.2", - "escalade": "^3.1.1", - "get-caller-file": "^2.0.5", - "require-directory": "^2.1.1", - "string-width": "^4.2.0", - "y18n": "^5.0.5", - "yargs-parser": "^20.2.2" - }, - "engines": { - "node": ">=10" - } - }, - "node_modules/mocha/node_modules/yargs-parser": { - "version": "20.2.4", - "resolved": "https://registry.npmjs.org/yargs-parser/-/yargs-parser-20.2.4.tgz", - "integrity": "sha512-WOkpgNhPTlE73h4VFAFsOnomJVaovO8VqLDzy5saChRBFQFBoMYirowyW+Q9HB4HFF4Z7VZTiG3iSzJJA29yRA==", - "dev": true, - "engines": { - "node": ">=10" + "dev": true, + "dependencies": { + "balanced-match": "^1.0.0" } }, "node_modules/ms": { - "version": "2.1.2", - "resolved": "https://registry.npmjs.org/ms/-/ms-2.1.2.tgz", - "integrity": "sha512-sGkPx+VjMtmA6MX27oA4FBFELFCZZ4S4XqeGOXCv68tT+jb3vk/RyaKWP0PTKyWtmLSM0b+adUTEvbs1PEaH2w==", + "version": "2.1.3", + "resolved": "https://registry.npmjs.org/ms/-/ms-2.1.3.tgz", + "integrity": "sha512-6FlzubTLZG3J2a/NVCAleEhjzq5oxgHyaCU9yYXvcLsvoVaHJq/s5xXI6/XXP6tz7R9xAOtHnSO/tXtF3WRTlA==", "dev": true }, "node_modules/mute-stream": { @@ -5844,12 +5168,6 @@ "node": "^10 || ^12 || ^13.7 || ^14 || >=15.0.1" } }, - "node_modules/natural-compare": { - "version": "1.4.0", - "resolved": "https://registry.npmjs.org/natural-compare/-/natural-compare-1.4.0.tgz", - "integrity": "sha512-OWND8ei3VtNC9h7V60qff3SVobHr996CTwgxubgyQYEpg290h9J0buyECNNJexkFm5sOajh5G116RYA1c8ZMSw==", - "dev": true - }, "node_modules/netmask": { "version": "2.0.2", "resolved": "https://registry.npmjs.org/netmask/-/netmask-2.0.2.tgz", @@ -5887,25 +5205,16 @@ } }, "node_modules/nise": { - "version": "5.1.4", - "resolved": "https://registry.npmjs.org/nise/-/nise-5.1.4.tgz", - "integrity": "sha512-8+Ib8rRJ4L0o3kfmyVCL7gzrohyDe0cMFTBa2d364yIrEGMEoetznKJx899YxjybU6bL9SQkYPSBBs1gyYs8Xg==", - "dev": true, - "dependencies": { - "@sinonjs/commons": "^2.0.0", - "@sinonjs/fake-timers": "^10.0.2", - "@sinonjs/text-encoding": "^0.7.1", - "just-extend": "^4.0.2", - "path-to-regexp": "^1.7.0" - } - }, - "node_modules/nise/node_modules/@sinonjs/commons": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/@sinonjs/commons/-/commons-2.0.0.tgz", - "integrity": "sha512-uLa0j859mMrg2slwQYdO/AkrOfmH+X6LTVmNTS9CqexuE2IvVORIkSpJLqePAbEnKJ77aMmCwr1NUZ57120Xcg==", + "version": "5.1.9", + "resolved": "https://registry.npmjs.org/nise/-/nise-5.1.9.tgz", + "integrity": "sha512-qOnoujW4SV6e40dYxJOb3uvuoPHtmLzIk4TFo+j0jPJoC+5Z9xja5qH5JZobEPsa8+YYphMrOSwnrshEhG2qww==", "dev": true, "dependencies": { - "type-detect": "4.0.8" + "@sinonjs/commons": "^3.0.0", + "@sinonjs/fake-timers": "^11.2.2", + "@sinonjs/text-encoding": "^0.7.2", + "just-extend": "^6.2.0", + "path-to-regexp": "^6.2.1" } }, "node_modules/node-domexception": { @@ -5958,9 +5267,9 @@ } }, "node_modules/node-releases": { - "version": "2.0.13", - "resolved": "https://registry.npmjs.org/node-releases/-/node-releases-2.0.13.tgz", - "integrity": "sha512-uYr7J37ae/ORWdZeQ1xxMJe3NtdmqMC/JZK+geofDrkLUApKRHPd18/TxtBOJ4A0/+uUIliorNrfYV6s1b02eQ==", + "version": "2.0.14", + "resolved": "https://registry.npmjs.org/node-releases/-/node-releases-2.0.14.tgz", + "integrity": "sha512-y10wOWt8yZpqXmOgRo77WaHEmhYQYGNA6y421PKsKYWEK8aW+cqAphborZDhqfyKrbZEN92CN1X2KbafY2s7Yw==", "dev": true }, "node_modules/normalize-path": { @@ -5985,9 +5294,9 @@ } }, "node_modules/npm-run-path": { - "version": "5.1.0", - "resolved": "https://registry.npmjs.org/npm-run-path/-/npm-run-path-5.1.0.tgz", - "integrity": "sha512-sJOdmRGrY2sjNTRMbSvluQqg+8X7ZK61yvzBEIDhz4f8z1TZFYABsqjjCBd/0PUNE9M6QDgHJXQkGUEm7Q+l9Q==", + "version": "5.2.0", + "resolved": "https://registry.npmjs.org/npm-run-path/-/npm-run-path-5.2.0.tgz", + "integrity": "sha512-W4/tgAXFqFA0iL7fk0+uQ3g7wkL8xJmx3XdK0VGb4cHW//eZTtKGvFBBoRKVTpY7n6ze4NL9ly7rgXcHufqXKg==", "dev": true, "dependencies": { "path-key": "^4.0.0" @@ -6052,15 +5361,6 @@ "node": ">=8.9" } }, - "node_modules/nyc/node_modules/camelcase": { - "version": "5.3.1", - "resolved": "https://registry.npmjs.org/camelcase/-/camelcase-5.3.1.tgz", - "integrity": "sha512-L28STB170nwWS63UjtlEOE3dldQApaJXZkOI1uMFfzf3rRuPegHaHesyee+YxQ+W6SvRDQV6UrdOdRiR153wJg==", - "dev": true, - "engines": { - "node": ">=6" - } - }, "node_modules/nyc/node_modules/cliui": { "version": "6.0.0", "resolved": "https://registry.npmjs.org/cliui/-/cliui-6.0.0.tgz", @@ -6072,11 +5372,54 @@ "wrap-ansi": "^6.2.0" } }, - "node_modules/nyc/node_modules/resolve-from": { + "node_modules/nyc/node_modules/find-up": { + "version": "4.1.0", + "resolved": "https://registry.npmjs.org/find-up/-/find-up-4.1.0.tgz", + "integrity": "sha512-PpOwAdQ/YlXQ2vj8a3h8IipDuYRi3wceVQQGYWxNINccq40Anw7BlsEXCMbt1Zt+OLA6Fq9suIpIWD0OsnISlw==", + "dev": true, + "dependencies": { + "locate-path": "^5.0.0", + "path-exists": "^4.0.0" + }, + "engines": { + "node": ">=8" + } + }, + "node_modules/nyc/node_modules/locate-path": { "version": "5.0.0", - "resolved": "https://registry.npmjs.org/resolve-from/-/resolve-from-5.0.0.tgz", - "integrity": "sha512-qYg9KP24dD5qka9J47d0aVky0N+b4fTU89LN9iDnjB5waksiC49rvMB0PrUJQGoTmH50XPiqOvAjDfaijGxYZw==", + "resolved": "https://registry.npmjs.org/locate-path/-/locate-path-5.0.0.tgz", + "integrity": "sha512-t7hw9pI+WvuwNJXwk5zVHpyhIqzg2qTlklJOf0mVxGSbe3Fp2VieZcduNYjaLDoy6p9uGpQEGWG87WpMKlNq8g==", + "dev": true, + "dependencies": { + "p-locate": "^4.1.0" + }, + "engines": { + "node": ">=8" + } + }, + "node_modules/nyc/node_modules/p-limit": { + "version": "2.3.0", + "resolved": "https://registry.npmjs.org/p-limit/-/p-limit-2.3.0.tgz", + "integrity": "sha512-//88mFWSJx8lxCzwdAABTJL2MyWB12+eIY7MDL2SqLmAkeKU9qxRvWuSyTjm3FUmpBEMuFfckAIqEaVGUDxb6w==", + "dev": true, + "dependencies": { + "p-try": "^2.0.0" + }, + "engines": { + "node": ">=6" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/nyc/node_modules/p-locate": { + "version": "4.1.0", + "resolved": "https://registry.npmjs.org/p-locate/-/p-locate-4.1.0.tgz", + "integrity": "sha512-R79ZZ/0wAxKGu3oYMlz8jy/kbhsNrS7SKZ7PxEHBgJ5+F2mtFW2fK2cOtBh1cHYkQsbzFV7I+EoRKe6Yt0oK7A==", "dev": true, + "dependencies": { + "p-limit": "^2.2.0" + }, "engines": { "node": ">=8" } @@ -6132,9 +5475,9 @@ } }, "node_modules/object-inspect": { - "version": "1.12.3", - "resolved": "https://registry.npmjs.org/object-inspect/-/object-inspect-1.12.3.tgz", - "integrity": "sha512-geUvdk7c+eizMNUDkRpW1wJwgfOiOeHbxBR/hLXK1aT6zmVSO0jsQcs7fj6MGw89jC/cjGfLcNOrtMYtGqm81g==", + "version": "1.13.1", + "resolved": "https://registry.npmjs.org/object-inspect/-/object-inspect-1.13.1.tgz", + "integrity": "sha512-5qoj1RUiKOMsCCNLV1CBiPYE10sziTsnmNxkAI/rZhiD63CF7IqdFGC/XzjWjpSgLf0LxXX3bDFIh0E18f6UhQ==", "dev": true, "funding": { "url": "https://github.com/sponsors/ljharb" @@ -6150,13 +5493,13 @@ } }, "node_modules/object.assign": { - "version": "4.1.4", - "resolved": "https://registry.npmjs.org/object.assign/-/object.assign-4.1.4.tgz", - "integrity": "sha512-1mxKf0e58bvyjSCtKYY4sRe9itRk3PJpquJOjeIkz885CczcI4IvJJDLPS72oowuSh+pBxUFROpX+TU++hxhZQ==", + "version": "4.1.5", + "resolved": "https://registry.npmjs.org/object.assign/-/object.assign-4.1.5.tgz", + "integrity": "sha512-byy+U7gp+FVwmyzKPYhW2h5l3crpmGsxl7X2s8y43IgxvG4g3QZ6CffDtsNQy1WsmZpQbO+ybo0AlW7TY6DcBQ==", "dev": true, "dependencies": { - "call-bind": "^1.0.2", - "define-properties": "^1.1.4", + "call-bind": "^1.0.5", + "define-properties": "^1.2.1", "has-symbols": "^1.0.3", "object-keys": "^1.1.1" }, @@ -6192,58 +5535,41 @@ } }, "node_modules/open": { - "version": "9.1.0", - "resolved": "https://registry.npmjs.org/open/-/open-9.1.0.tgz", - "integrity": "sha512-OS+QTnw1/4vrf+9hh1jc1jnYjzSG4ttTBB8UxOwAnInG3Uo4ssetzC1ihqaIHjLJnA5GGlRl6QlZXOTQhRBUvg==", + "version": "10.0.3", + "resolved": "https://registry.npmjs.org/open/-/open-10.0.3.tgz", + "integrity": "sha512-dtbI5oW7987hwC9qjJTyABldTaa19SuyJse1QboWv3b0qCcrrLNVDqBx1XgELAjh9QTVQaP/C5b1nhQebd1H2A==", "dev": true, "dependencies": { - "default-browser": "^4.0.0", + "default-browser": "^5.2.1", "define-lazy-prop": "^3.0.0", "is-inside-container": "^1.0.0", - "is-wsl": "^2.2.0" + "is-wsl": "^3.1.0" }, "engines": { - "node": ">=14.16" + "node": ">=18" }, "funding": { "url": "https://github.com/sponsors/sindresorhus" } }, - "node_modules/optionator": { - "version": "0.9.3", - "resolved": "https://registry.npmjs.org/optionator/-/optionator-0.9.3.tgz", - "integrity": "sha512-JjCoypp+jKn1ttEFExxhetCKeJt9zhAgAve5FXHixTvFDW/5aEktX9bufBKLRRMdU7bNtpLfcGu94B3cdEJgjg==", - "dev": true, - "dependencies": { - "@aashutoshrathi/word-wrap": "^1.2.3", - "deep-is": "^0.1.3", - "fast-levenshtein": "^2.0.6", - "levn": "^0.4.1", - "prelude-ls": "^1.2.1", - "type-check": "^0.4.0" - }, - "engines": { - "node": ">= 0.8.0" - } - }, "node_modules/ora": { - "version": "7.0.1", - "resolved": "https://registry.npmjs.org/ora/-/ora-7.0.1.tgz", - "integrity": "sha512-0TUxTiFJWv+JnjWm4o9yvuskpEJLXTcng8MJuKd+SzAzp2o+OP3HWqNhB4OdJRt1Vsd9/mR0oyaEYlOnL7XIRw==", + "version": "8.0.1", + "resolved": "https://registry.npmjs.org/ora/-/ora-8.0.1.tgz", + "integrity": "sha512-ANIvzobt1rls2BDny5fWZ3ZVKyD6nscLvfFRpQgfWsythlcsVUC9kL0zq6j2Z5z9wwp1kd7wpsD/T9qNPVLCaQ==", "dev": true, "dependencies": { "chalk": "^5.3.0", "cli-cursor": "^4.0.0", - "cli-spinners": "^2.9.0", + "cli-spinners": "^2.9.2", "is-interactive": "^2.0.0", - "is-unicode-supported": "^1.3.0", - "log-symbols": "^5.1.0", - "stdin-discarder": "^0.1.0", - "string-width": "^6.1.0", + "is-unicode-supported": "^2.0.0", + "log-symbols": "^6.0.0", + "stdin-discarder": "^0.2.1", + "string-width": "^7.0.0", "strip-ansi": "^7.1.0" }, "engines": { - "node": ">=16" + "node": ">=18" }, "funding": { "url": "https://github.com/sponsors/sindresorhus" @@ -6261,6 +5587,18 @@ "url": "https://github.com/chalk/ansi-regex?sponsor=1" } }, + "node_modules/ora/node_modules/chalk": { + "version": "5.3.0", + "resolved": "https://registry.npmjs.org/chalk/-/chalk-5.3.0.tgz", + "integrity": "sha512-dLitG79d+GV1Nb/VYcCDFivJeK1hiukt9QjRNVOsUtTy1rR1YJsmpGGTZ3qJos+uw7WmWF4wUwBd9jxjocFC2w==", + "dev": true, + "engines": { + "node": "^12.17.0 || ^14.13 || >=16.0.0" + }, + "funding": { + "url": "https://github.com/chalk/chalk?sponsor=1" + } + }, "node_modules/ora/node_modules/cli-cursor": { "version": "4.0.0", "resolved": "https://registry.npmjs.org/cli-cursor/-/cli-cursor-4.0.0.tgz", @@ -6277,11 +5615,51 @@ } }, "node_modules/ora/node_modules/emoji-regex": { - "version": "10.2.1", - "resolved": "https://registry.npmjs.org/emoji-regex/-/emoji-regex-10.2.1.tgz", - "integrity": "sha512-97g6QgOk8zlDRdgq1WxwgTMgEWGVAQvB5Fdpgc1MkNy56la5SKP9GsMXKDOdqwn90/41a8yPwIGk1Y6WVbeMQA==", + "version": "10.3.0", + "resolved": "https://registry.npmjs.org/emoji-regex/-/emoji-regex-10.3.0.tgz", + "integrity": "sha512-QpLs9D9v9kArv4lfDEgg1X/gN5XLnf/A6l9cs8SPZLRZR3ZkY9+kwIQTxm+fsSej5UMYGE8fdoaZVIBlqG0XTw==", "dev": true }, + "node_modules/ora/node_modules/is-unicode-supported": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/is-unicode-supported/-/is-unicode-supported-2.0.0.tgz", + "integrity": "sha512-FRdAyx5lusK1iHG0TWpVtk9+1i+GjrzRffhDg4ovQ7mcidMQ6mj+MhKPmvh7Xwyv5gIS06ns49CA7Sqg7lC22Q==", + "dev": true, + "engines": { + "node": ">=18" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/ora/node_modules/log-symbols": { + "version": "6.0.0", + "resolved": "https://registry.npmjs.org/log-symbols/-/log-symbols-6.0.0.tgz", + "integrity": "sha512-i24m8rpwhmPIS4zscNzK6MSEhk0DUWa/8iYQWxhffV8jkI4Phvs3F+quL5xvS0gdQR0FyTCMMH33Y78dDTzzIw==", + "dev": true, + "dependencies": { + "chalk": "^5.3.0", + "is-unicode-supported": "^1.3.0" + }, + "engines": { + "node": ">=18" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/ora/node_modules/log-symbols/node_modules/is-unicode-supported": { + "version": "1.3.0", + "resolved": "https://registry.npmjs.org/is-unicode-supported/-/is-unicode-supported-1.3.0.tgz", + "integrity": "sha512-43r2mRvz+8JRIKnWJ+3j8JtjRKZ6GmjzfaE/qiBJnikNnYv/6bagRJ1kUhNk8R5EX/GkobD+r+sfxCPJsiKBLQ==", + "dev": true, + "engines": { + "node": ">=12" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, "node_modules/ora/node_modules/mimic-fn": { "version": "2.1.0", "resolved": "https://registry.npmjs.org/mimic-fn/-/mimic-fn-2.1.0.tgz", @@ -6323,17 +5701,17 @@ } }, "node_modules/ora/node_modules/string-width": { - "version": "6.1.0", - "resolved": "https://registry.npmjs.org/string-width/-/string-width-6.1.0.tgz", - "integrity": "sha512-k01swCJAgQmuADB0YIc+7TuatfNvTBVOoaUWJjTB9R4VJzR5vNWzf5t42ESVZFPS8xTySF7CAdV4t/aaIm3UnQ==", + "version": "7.1.0", + "resolved": "https://registry.npmjs.org/string-width/-/string-width-7.1.0.tgz", + "integrity": "sha512-SEIJCWiX7Kg4c129n48aDRwLbFb2LJmXXFrWBG4NGaRtMQ3myKPKbwrD1BKqQn74oCoNMBVrfDEr5M9YxCsrkw==", "dev": true, "dependencies": { - "eastasianwidth": "^0.2.0", - "emoji-regex": "^10.2.1", - "strip-ansi": "^7.0.1" + "emoji-regex": "^10.3.0", + "get-east-asian-width": "^1.0.0", + "strip-ansi": "^7.1.0" }, "engines": { - "node": ">=16" + "node": ">=18" }, "funding": { "url": "https://github.com/sponsors/sindresorhus" @@ -6389,30 +5767,33 @@ } }, "node_modules/p-limit": { - "version": "2.3.0", - "resolved": "https://registry.npmjs.org/p-limit/-/p-limit-2.3.0.tgz", - "integrity": "sha512-//88mFWSJx8lxCzwdAABTJL2MyWB12+eIY7MDL2SqLmAkeKU9qxRvWuSyTjm3FUmpBEMuFfckAIqEaVGUDxb6w==", + "version": "3.1.0", + "resolved": "https://registry.npmjs.org/p-limit/-/p-limit-3.1.0.tgz", + "integrity": "sha512-TYOanM3wGwNGsZN2cVTYPArw454xnXj5qmWF1bEoAc4+cU/ol7GVh7odevjp1FNHduHc3KZMcFduxU5Xc6uJRQ==", "dev": true, "dependencies": { - "p-try": "^2.0.0" + "yocto-queue": "^0.1.0" }, "engines": { - "node": ">=6" + "node": ">=10" }, "funding": { "url": "https://github.com/sponsors/sindresorhus" } }, "node_modules/p-locate": { - "version": "4.1.0", - "resolved": "https://registry.npmjs.org/p-locate/-/p-locate-4.1.0.tgz", - "integrity": "sha512-R79ZZ/0wAxKGu3oYMlz8jy/kbhsNrS7SKZ7PxEHBgJ5+F2mtFW2fK2cOtBh1cHYkQsbzFV7I+EoRKe6Yt0oK7A==", + "version": "5.0.0", + "resolved": "https://registry.npmjs.org/p-locate/-/p-locate-5.0.0.tgz", + "integrity": "sha512-LaNjtRWUBY++zB5nE/NwcaoMylSPk+S+ZHNB1TzdbMJMny6dynpAGt7X/tl/QYq3TIeE6nxHppbo2LGymrG5Pw==", "dev": true, "dependencies": { - "p-limit": "^2.2.0" + "p-limit": "^3.0.2" }, "engines": { - "node": ">=8" + "node": ">=10" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" } }, "node_modules/p-map": { @@ -6502,6 +5883,18 @@ "url": "https://github.com/sponsors/sindresorhus" } }, + "node_modules/package-json/node_modules/get-stream": { + "version": "6.0.1", + "resolved": "https://registry.npmjs.org/get-stream/-/get-stream-6.0.1.tgz", + "integrity": "sha512-ts6Wi+2j3jQjqi70w5AlN8DFnkSwC+MqmxEzdEALB2qXZYV3X/b1CTfgPLGJNMeAWxdPfU8FO1ms3NUfaHCPYg==", + "dev": true, + "engines": { + "node": ">=10" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, "node_modules/package-json/node_modules/got": { "version": "12.6.1", "resolved": "https://registry.npmjs.org/got/-/got-12.6.1.tgz", @@ -6554,6 +5947,12 @@ "node": ">=10" } }, + "node_modules/package-json/node_modules/yallist": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/yallist/-/yallist-4.0.0.tgz", + "integrity": "sha512-3wdGidZyq5PB084XLES5TpOSRA3wjXAlIWMhum2kRcv/41Sn2emQ0dycQW4uZXLejwKvg6EsvbdlVL+FYEct7A==", + "dev": true + }, "node_modules/parent-module": { "version": "1.0.1", "resolved": "https://registry.npmjs.org/parent-module/-/parent-module-1.0.1.tgz", @@ -6636,27 +6035,21 @@ "dev": true }, "node_modules/path-to-regexp": { - "version": "1.8.0", - "resolved": "https://registry.npmjs.org/path-to-regexp/-/path-to-regexp-1.8.0.tgz", - "integrity": "sha512-n43JRhlUKUAlibEJhPeir1ncUID16QnEjNpwzNdO3Lm4ywrBpBZ5oLD0I6br9evr1Y9JTqwRtAh7JLoOzAQdVA==", - "dev": true, - "dependencies": { - "isarray": "0.0.1" - } - }, - "node_modules/path-to-regexp/node_modules/isarray": { - "version": "0.0.1", - "resolved": "https://registry.npmjs.org/isarray/-/isarray-0.0.1.tgz", - "integrity": "sha512-D2S+3GLxWH+uhrNEcoh/fnmYeP8E8/zHl644d/jdA0g2uyXvy3sb0qxotE+ne0LtccHknQzWwZEzhak7oJ0COQ==", + "version": "6.2.1", + "resolved": "https://registry.npmjs.org/path-to-regexp/-/path-to-regexp-6.2.1.tgz", + "integrity": "sha512-JLyh7xT1kizaEvcaXOQwOc2/Yhw6KZOvPf1S8401UyLk86CU79LN3vl7ztXGm/pZ+YjoyAJ4rxmHwbkBXJX+yw==", "dev": true }, "node_modules/path-type": { - "version": "4.0.0", - "resolved": "https://registry.npmjs.org/path-type/-/path-type-4.0.0.tgz", - "integrity": "sha512-gDKb8aZMDeD/tZWs9P6+q0J9Mwkdl6xMV8TjnGP3qJVJ06bdMgkbBlLU8IdfOsIsFz2BW1rNVT3XuNEl8zPAvw==", + "version": "5.0.0", + "resolved": "https://registry.npmjs.org/path-type/-/path-type-5.0.0.tgz", + "integrity": "sha512-5HviZNaZcfqP95rwpv+1HDgUamezbqdSYTyzjTvwtJSnIH+3vnbmWsItli8OFEndS984VT55M3jduxZbX351gg==", "dev": true, "engines": { - "node": ">=8" + "node": ">=12" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" } }, "node_modules/picocolors": { @@ -6719,13 +6112,56 @@ "node": ">=8" } }, - "node_modules/prelude-ls": { - "version": "1.2.1", - "resolved": "https://registry.npmjs.org/prelude-ls/-/prelude-ls-1.2.1.tgz", - "integrity": "sha512-vkcDPrRZo1QZLbn5RLGPpg/WmIQ65qoWWhcGKf/b5eplkkarX0m9z8ppCat4mlOqUsWpyNuYgO3VRyrYHSzX5g==", + "node_modules/pkg-dir/node_modules/find-up": { + "version": "4.1.0", + "resolved": "https://registry.npmjs.org/find-up/-/find-up-4.1.0.tgz", + "integrity": "sha512-PpOwAdQ/YlXQ2vj8a3h8IipDuYRi3wceVQQGYWxNINccq40Anw7BlsEXCMbt1Zt+OLA6Fq9suIpIWD0OsnISlw==", + "dev": true, + "dependencies": { + "locate-path": "^5.0.0", + "path-exists": "^4.0.0" + }, + "engines": { + "node": ">=8" + } + }, + "node_modules/pkg-dir/node_modules/locate-path": { + "version": "5.0.0", + "resolved": "https://registry.npmjs.org/locate-path/-/locate-path-5.0.0.tgz", + "integrity": "sha512-t7hw9pI+WvuwNJXwk5zVHpyhIqzg2qTlklJOf0mVxGSbe3Fp2VieZcduNYjaLDoy6p9uGpQEGWG87WpMKlNq8g==", + "dev": true, + "dependencies": { + "p-locate": "^4.1.0" + }, + "engines": { + "node": ">=8" + } + }, + "node_modules/pkg-dir/node_modules/p-limit": { + "version": "2.3.0", + "resolved": "https://registry.npmjs.org/p-limit/-/p-limit-2.3.0.tgz", + "integrity": "sha512-//88mFWSJx8lxCzwdAABTJL2MyWB12+eIY7MDL2SqLmAkeKU9qxRvWuSyTjm3FUmpBEMuFfckAIqEaVGUDxb6w==", + "dev": true, + "dependencies": { + "p-try": "^2.0.0" + }, + "engines": { + "node": ">=6" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/pkg-dir/node_modules/p-locate": { + "version": "4.1.0", + "resolved": "https://registry.npmjs.org/p-locate/-/p-locate-4.1.0.tgz", + "integrity": "sha512-R79ZZ/0wAxKGu3oYMlz8jy/kbhsNrS7SKZ7PxEHBgJ5+F2mtFW2fK2cOtBh1cHYkQsbzFV7I+EoRKe6Yt0oK7A==", "dev": true, + "dependencies": { + "p-limit": "^2.2.0" + }, "engines": { - "node": ">= 0.8.0" + "node": ">=8" } }, "node_modules/process-on-spawn": { @@ -6741,16 +6177,16 @@ } }, "node_modules/promise.allsettled": { - "version": "1.0.6", - "resolved": "https://registry.npmjs.org/promise.allsettled/-/promise.allsettled-1.0.6.tgz", - "integrity": "sha512-22wJUOD3zswWFqgwjNHa1965LvqTX87WPu/lreY2KSd7SVcERfuZ4GfUaOnJNnvtoIv2yXT/W00YIGMetXtFXg==", + "version": "1.0.7", + "resolved": "https://registry.npmjs.org/promise.allsettled/-/promise.allsettled-1.0.7.tgz", + "integrity": "sha512-hezvKvQQmsFkOdrZfYxUxkyxl8mgFQeT259Ajj9PXdbg9VzBCWrItOev72JyWxkCD5VSSqAeHmlN3tWx4DlmsA==", "dev": true, "dependencies": { "array.prototype.map": "^1.0.5", "call-bind": "^1.0.2", - "define-properties": "^1.1.4", - "es-abstract": "^1.20.4", - "get-intrinsic": "^1.1.3", + "define-properties": "^1.2.0", + "es-abstract": "^1.22.1", + "get-intrinsic": "^1.2.1", "iterate-value": "^1.0.2" }, "engines": { @@ -6773,38 +6209,38 @@ "dev": true }, "node_modules/proxy-agent": { - "version": "6.3.0", - "resolved": "https://registry.npmjs.org/proxy-agent/-/proxy-agent-6.3.0.tgz", - "integrity": "sha512-0LdR757eTj/JfuU7TL2YCuAZnxWXu3tkJbg4Oq3geW/qFNT/32T0sp2HnZ9O0lMR4q3vwAt0+xCA8SR0WAD0og==", + "version": "6.3.1", + "resolved": "https://registry.npmjs.org/proxy-agent/-/proxy-agent-6.3.1.tgz", + "integrity": "sha512-Rb5RVBy1iyqOtNl15Cw/llpeLH8bsb37gM1FUfKQ+Wck6xHlbAhWGUFiTRHtkjqGTA5pSHz6+0hrPW/oECihPQ==", "dev": true, "dependencies": { "agent-base": "^7.0.2", "debug": "^4.3.4", "http-proxy-agent": "^7.0.0", - "https-proxy-agent": "^7.0.0", + "https-proxy-agent": "^7.0.2", "lru-cache": "^7.14.1", - "pac-proxy-agent": "^7.0.0", + "pac-proxy-agent": "^7.0.1", "proxy-from-env": "^1.1.0", - "socks-proxy-agent": "^8.0.1" + "socks-proxy-agent": "^8.0.2" }, "engines": { "node": ">= 14" } }, + "node_modules/proxy-agent/node_modules/lru-cache": { + "version": "7.18.3", + "resolved": "https://registry.npmjs.org/lru-cache/-/lru-cache-7.18.3.tgz", + "integrity": "sha512-jumlc0BIUrS3qJGgIkWZsyfAM7NCWiBcCDhnd+3NNM5KbBmLTgHVfWBcg6W+rLUsIpzpERPsvwUP7CckAQSOoA==", + "dev": true, + "engines": { + "node": ">=12" + } + }, "node_modules/proxy-from-env": { "version": "1.1.0", "resolved": "https://registry.npmjs.org/proxy-from-env/-/proxy-from-env-1.1.0.tgz", "integrity": "sha512-D+zkORCbA9f1tdWRK0RaCR3GPv50cMxcrz4X8k5LTSUD1Dkw47mKJEZQNunItRTkWwgtaUSo1RVFRIG9ZXiFYg==", - "dev": true - }, - "node_modules/punycode": { - "version": "2.3.0", - "resolved": "https://registry.npmjs.org/punycode/-/punycode-2.3.0.tgz", - "integrity": "sha512-rRV+zQD8tVFys26lAGR9WUuS4iUAngJScM+ZRSKtvl5tKeZ2t5bvdNFdNHBW9FWR4guGHlgmsZ1G7BSm2wTbuA==", - "dev": true, - "engines": { - "node": ">=6" - } + "dev": true }, "node_modules/pupa": { "version": "3.1.0", @@ -6883,6 +6319,15 @@ "integrity": "sha512-JV/yugV2uzW5iMRSiZAyDtQd+nxtUnjeLt0acNdw98kKLrvuRVyB80tsREOE7yvGVgalhZ6RNXCmEHkUKBKxew==", "dev": true }, + "node_modules/rc/node_modules/strip-json-comments": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/strip-json-comments/-/strip-json-comments-2.0.1.tgz", + "integrity": "sha512-4gB8na07fecVVkOI6Rs4e7T6NOTki5EmL7TUduTs6bu3EdnSycntVJ4re8kgZA+wx9IueI2Y11bfbgwtzuE0KQ==", + "dev": true, + "engines": { + "node": ">=0.10.0" + } + }, "node_modules/readable-stream": { "version": "3.6.2", "resolved": "https://registry.npmjs.org/readable-stream/-/readable-stream-3.6.2.tgz", @@ -6921,6 +6366,10 @@ "node": ">= 0.10" } }, + "node_modules/redis": { + "resolved": "packages/redis", + "link": true + }, "node_modules/regexp.prototype.flags": { "version": "1.5.1", "resolved": "https://registry.npmjs.org/regexp.prototype.flags/-/regexp.prototype.flags-1.5.1.tgz", @@ -6966,35 +6415,45 @@ } }, "node_modules/release-it": { - "version": "16.1.5", - "resolved": "https://registry.npmjs.org/release-it/-/release-it-16.1.5.tgz", - "integrity": "sha512-w/zCljPZBSYcCwR9fjDB1zaYwie1CAQganUrwNqjtXacXhrrsS5E6dDUNLcxm2ypu8GWAgZNMJfuBJqIO2E7fA==", + "version": "17.0.3", + "resolved": "https://registry.npmjs.org/release-it/-/release-it-17.0.3.tgz", + "integrity": "sha512-QjTCmvQm91pwLEbvavEs9jofHNe8thsb9Uimin+8DNSwFRdUd73p0Owy2PP/Dzh/EegRkKq/o+4Pn1xp8pC1og==", "dev": true, + "funding": [ + { + "type": "github", + "url": "https://github.com/sponsors/webpro" + }, + { + "type": "opencollective", + "url": "https://opencollective.com/webpro" + } + ], "dependencies": { "@iarna/toml": "2.2.5", - "@octokit/rest": "19.0.13", + "@octokit/rest": "20.0.2", "async-retry": "1.3.3", "chalk": "5.3.0", - "cosmiconfig": "8.2.0", - "execa": "7.2.0", - "git-url-parse": "13.1.0", - "globby": "13.2.2", + "cosmiconfig": "9.0.0", + "execa": "8.0.1", + "git-url-parse": "14.0.0", + "globby": "14.0.0", "got": "13.0.0", - "inquirer": "9.2.10", + "inquirer": "9.2.12", "is-ci": "3.0.1", "issue-parser": "6.0.0", "lodash": "4.17.21", "mime-types": "2.1.35", "new-github-release-url": "2.0.0", "node-fetch": "3.3.2", - "open": "9.1.0", - "ora": "7.0.1", + "open": "10.0.3", + "ora": "8.0.1", "os-name": "5.1.0", - "promise.allsettled": "1.0.6", - "proxy-agent": "6.3.0", + "promise.allsettled": "1.0.7", + "proxy-agent": "6.3.1", "semver": "7.5.4", "shelljs": "0.8.5", - "update-notifier": "6.0.2", + "update-notifier": "7.0.0", "url-join": "5.0.0", "wildcard-match": "5.1.2", "yargs-parser": "21.1.1" @@ -7003,23 +6462,36 @@ "release-it": "bin/release-it.js" }, "engines": { - "node": ">=16" + "node": ">=18" + } + }, + "node_modules/release-it/node_modules/chalk": { + "version": "5.3.0", + "resolved": "https://registry.npmjs.org/chalk/-/chalk-5.3.0.tgz", + "integrity": "sha512-dLitG79d+GV1Nb/VYcCDFivJeK1hiukt9QjRNVOsUtTy1rR1YJsmpGGTZ3qJos+uw7WmWF4wUwBd9jxjocFC2w==", + "dev": true, + "engines": { + "node": "^12.17.0 || ^14.13 || >=16.0.0" + }, + "funding": { + "url": "https://github.com/chalk/chalk?sponsor=1" } }, "node_modules/release-it/node_modules/globby": { - "version": "13.2.2", - "resolved": "https://registry.npmjs.org/globby/-/globby-13.2.2.tgz", - "integrity": "sha512-Y1zNGV+pzQdh7H39l9zgB4PJqjRNqydvdYCDG4HFXM4XuvSaQQlEc91IU1yALL8gUTDomgBAfz3XJdmUS+oo0w==", + "version": "14.0.0", + "resolved": "https://registry.npmjs.org/globby/-/globby-14.0.0.tgz", + "integrity": "sha512-/1WM/LNHRAOH9lZta77uGbq0dAEQM+XjNesWwhlERDVenqothRbnzTrL3/LrIoEPPjeUHC3vrS6TwoyxeHs7MQ==", "dev": true, "dependencies": { - "dir-glob": "^3.0.1", - "fast-glob": "^3.3.0", + "@sindresorhus/merge-streams": "^1.0.0", + "fast-glob": "^3.3.2", "ignore": "^5.2.4", - "merge2": "^1.4.1", - "slash": "^4.0.0" + "path-type": "^5.0.0", + "slash": "^5.1.0", + "unicorn-magic": "^0.1.0" }, "engines": { - "node": "^12.20.0 || ^14.13.1 || >=16.0.0" + "node": ">=18" }, "funding": { "url": "https://github.com/sponsors/sindresorhus" @@ -7052,6 +6524,21 @@ "node": ">=10" } }, + "node_modules/release-it/node_modules/yallist": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/yallist/-/yallist-4.0.0.tgz", + "integrity": "sha512-3wdGidZyq5PB084XLES5TpOSRA3wjXAlIWMhum2kRcv/41Sn2emQ0dycQW4uZXLejwKvg6EsvbdlVL+FYEct7A==", + "dev": true + }, + "node_modules/release-it/node_modules/yargs-parser": { + "version": "21.1.1", + "resolved": "https://registry.npmjs.org/yargs-parser/-/yargs-parser-21.1.1.tgz", + "integrity": "sha512-tVpsJW7DdjecAiFpbIB1e3qxIQsE6NoPc5/eTdrbbIC4h0LVsWhnoa3g+m2HclBIujHzsxZ4VJVA+GUuc2/LBw==", + "dev": true, + "engines": { + "node": ">=12" + } + }, "node_modules/release-zalgo": { "version": "1.0.0", "resolved": "https://registry.npmjs.org/release-zalgo/-/release-zalgo-1.0.0.tgz", @@ -7080,9 +6567,9 @@ "dev": true }, "node_modules/resolve": { - "version": "1.22.6", - "resolved": "https://registry.npmjs.org/resolve/-/resolve-1.22.6.tgz", - "integrity": "sha512-njhxM7mV12JfufShqGy3Rz8j11RPdLy4xi15UurGJeoHLfJpVXKdh3ueuOqbYUcDZnffr6X739JBo5LzyahEsw==", + "version": "1.22.8", + "resolved": "https://registry.npmjs.org/resolve/-/resolve-1.22.8.tgz", + "integrity": "sha512-oKWePCxqpd6FlLvGV1VU0x7bkPmmCNolxzjMf4NczoDnQcIWrAF+cPtZn5i6n+RfD2d9i0tzpKnG6Yk168yIyw==", "dev": true, "dependencies": { "is-core-module": "^2.13.0", @@ -7103,12 +6590,21 @@ "dev": true }, "node_modules/resolve-from": { - "version": "4.0.0", - "resolved": "https://registry.npmjs.org/resolve-from/-/resolve-from-4.0.0.tgz", - "integrity": "sha512-pb/MYmXstAkysRFx8piNI1tGFNQIFA3vkE3Gq4EuA1dF6gHp/+vgZqsCGJapvy8N3Q+4o7FwvquPJcnZ7RYy4g==", + "version": "5.0.0", + "resolved": "https://registry.npmjs.org/resolve-from/-/resolve-from-5.0.0.tgz", + "integrity": "sha512-qYg9KP24dD5qka9J47d0aVky0N+b4fTU89LN9iDnjB5waksiC49rvMB0PrUJQGoTmH50XPiqOvAjDfaijGxYZw==", "dev": true, "engines": { - "node": ">=4" + "node": ">=8" + } + }, + "node_modules/resolve-pkg-maps": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/resolve-pkg-maps/-/resolve-pkg-maps-1.0.0.tgz", + "integrity": "sha512-seS2Tj26TBVOC2NIc2rOe2y2ZO7efxITtLZcGSOnHHNOQ7CkiUBfw0Iw2ck6xkIhPwLhKNLS8BO+hEpngQlqzw==", + "dev": true, + "funding": { + "url": "https://github.com/privatenumber/resolve-pkg-maps?sponsor=1" } }, "node_modules/responselike": { @@ -7198,109 +6694,17 @@ } }, "node_modules/run-applescript": { - "version": "5.0.0", - "resolved": "https://registry.npmjs.org/run-applescript/-/run-applescript-5.0.0.tgz", - "integrity": "sha512-XcT5rBksx1QdIhlFOCtgZkB99ZEouFZ1E2Kc2LHqNW13U3/74YGdkQRmThTwxy4QIyookibDKYZOPqX//6BlAg==", - "dev": true, - "dependencies": { - "execa": "^5.0.0" - }, - "engines": { - "node": ">=12" - }, - "funding": { - "url": "https://github.com/sponsors/sindresorhus" - } - }, - "node_modules/run-applescript/node_modules/execa": { - "version": "5.1.1", - "resolved": "https://registry.npmjs.org/execa/-/execa-5.1.1.tgz", - "integrity": "sha512-8uSpZZocAZRBAPIEINJj3Lo9HyGitllczc27Eh5YYojjMFMn8yHMDMaUHE2Jqfq05D/wucwI4JGURyXt1vchyg==", - "dev": true, - "dependencies": { - "cross-spawn": "^7.0.3", - "get-stream": "^6.0.0", - "human-signals": "^2.1.0", - "is-stream": "^2.0.0", - "merge-stream": "^2.0.0", - "npm-run-path": "^4.0.1", - "onetime": "^5.1.2", - "signal-exit": "^3.0.3", - "strip-final-newline": "^2.0.0" - }, - "engines": { - "node": ">=10" - }, - "funding": { - "url": "https://github.com/sindresorhus/execa?sponsor=1" - } - }, - "node_modules/run-applescript/node_modules/human-signals": { - "version": "2.1.0", - "resolved": "https://registry.npmjs.org/human-signals/-/human-signals-2.1.0.tgz", - "integrity": "sha512-B4FFZ6q/T2jhhksgkbEW3HBvWIfDW85snkQgawt07S7J5QXTk6BkNV+0yAeZrM5QpMAdYlocGoljn0sJ/WQkFw==", - "dev": true, - "engines": { - "node": ">=10.17.0" - } - }, - "node_modules/run-applescript/node_modules/is-stream": { - "version": "2.0.1", - "resolved": "https://registry.npmjs.org/is-stream/-/is-stream-2.0.1.tgz", - "integrity": "sha512-hFoiJiTl63nn+kstHGBtewWSKnQLpyb155KHheA1l39uvtO9nWIop1p3udqPcUd/xbF1VLMO4n7OI6p7RbngDg==", - "dev": true, - "engines": { - "node": ">=8" - }, - "funding": { - "url": "https://github.com/sponsors/sindresorhus" - } - }, - "node_modules/run-applescript/node_modules/mimic-fn": { - "version": "2.1.0", - "resolved": "https://registry.npmjs.org/mimic-fn/-/mimic-fn-2.1.0.tgz", - "integrity": "sha512-OqbOk5oEQeAZ8WXWydlu9HJjz9WVdEIvamMCcXmuqUYjTknH/sqsWvhQ3vgwKFRR1HpjvNBKQ37nbJgYzGqGcg==", - "dev": true, - "engines": { - "node": ">=6" - } - }, - "node_modules/run-applescript/node_modules/npm-run-path": { - "version": "4.0.1", - "resolved": "https://registry.npmjs.org/npm-run-path/-/npm-run-path-4.0.1.tgz", - "integrity": "sha512-S48WzZW777zhNIrn7gxOlISNAqi9ZC/uQFnRdbeIHhZhCA6UqpkOT8T1G7BvfdgP4Er8gF4sUbaS0i7QvIfCWw==", - "dev": true, - "dependencies": { - "path-key": "^3.0.0" - }, - "engines": { - "node": ">=8" - } - }, - "node_modules/run-applescript/node_modules/onetime": { - "version": "5.1.2", - "resolved": "https://registry.npmjs.org/onetime/-/onetime-5.1.2.tgz", - "integrity": "sha512-kbpaSSGJTWdAY5KPVeMOKXSrPtr8C8C7wodJbcsd51jRnmD+GZu8Y0VoU6Dm5Z4vWr0Ig/1NKuWRKf7j5aaYSg==", + "version": "7.0.0", + "resolved": "https://registry.npmjs.org/run-applescript/-/run-applescript-7.0.0.tgz", + "integrity": "sha512-9by4Ij99JUr/MCFBUkDKLWK3G9HVXmabKz9U5MlIAIuvuzkiOicRYs8XJLxX+xahD+mLiiCYDqF9dKAgtzKP1A==", "dev": true, - "dependencies": { - "mimic-fn": "^2.1.0" - }, "engines": { - "node": ">=6" + "node": ">=18" }, "funding": { "url": "https://github.com/sponsors/sindresorhus" } }, - "node_modules/run-applescript/node_modules/strip-final-newline": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/strip-final-newline/-/strip-final-newline-2.0.0.tgz", - "integrity": "sha512-BrpvfNAE3dcvq7ll3xVumzjKjZQ5tI1sEUIKr3Uoks0XUl45St3FlatVqef9prk4jRDzhW6WZg+3bk93y6pLjA==", - "dev": true, - "engines": { - "node": ">=6" - } - }, "node_modules/run-async": { "version": "3.0.0", "resolved": "https://registry.npmjs.org/run-async/-/run-async-3.0.0.tgz", @@ -7343,13 +6747,13 @@ } }, "node_modules/safe-array-concat": { - "version": "1.0.1", - "resolved": "https://registry.npmjs.org/safe-array-concat/-/safe-array-concat-1.0.1.tgz", - "integrity": "sha512-6XbUAseYE2KtOuGueyeobCySj9L4+66Tn6KQMOPQJrAJEowYKW/YR/MGJZl7FdydUdaFu4LYyDZjxf4/Nmo23Q==", + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/safe-array-concat/-/safe-array-concat-1.1.0.tgz", + "integrity": "sha512-ZdQ0Jeb9Ofti4hbt5lX3T2JcAamT9hfzYU1MNB+z/jaEbB6wfFfPIR/zEORmZqobkCCJhSjodobH6WHNmJ97dg==", "dev": true, "dependencies": { - "call-bind": "^1.0.2", - "get-intrinsic": "^1.2.1", + "call-bind": "^1.0.5", + "get-intrinsic": "^1.2.2", "has-symbols": "^1.0.3", "isarray": "^2.0.5" }, @@ -7381,15 +6785,18 @@ ] }, "node_modules/safe-regex-test": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/safe-regex-test/-/safe-regex-test-1.0.0.tgz", - "integrity": "sha512-JBUUzyOgEwXQY1NuPtvcj/qcBDbDmEvWufhlnXZIm75DEHp+afM1r1ujJpJsV/gSM4t59tpDyPi1sd6ZaPFfsA==", + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/safe-regex-test/-/safe-regex-test-1.0.2.tgz", + "integrity": "sha512-83S9w6eFq12BBIJYvjMux6/dkirb8+4zJRA9cxNBVb7Wq5fJBW+Xze48WqR8pxua7bDuAaaAxtVVd4Idjp1dBQ==", "dev": true, "dependencies": { - "call-bind": "^1.0.2", - "get-intrinsic": "^1.1.3", + "call-bind": "^1.0.5", + "get-intrinsic": "^1.2.2", "is-regex": "^1.1.4" }, + "engines": { + "node": ">= 0.4" + }, "funding": { "url": "https://github.com/sponsors/ljharb" } @@ -7451,6 +6858,12 @@ "node": ">=10" } }, + "node_modules/semver-diff/node_modules/yallist": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/yallist/-/yallist-4.0.0.tgz", + "integrity": "sha512-3wdGidZyq5PB084XLES5TpOSRA3wjXAlIWMhum2kRcv/41Sn2emQ0dycQW4uZXLejwKvg6EsvbdlVL+FYEct7A==", + "dev": true + }, "node_modules/serialize-javascript": { "version": "6.0.0", "resolved": "https://registry.npmjs.org/serialize-javascript/-/serialize-javascript-6.0.0.tgz", @@ -7466,6 +6879,22 @@ "integrity": "sha512-KiKBS8AnWGEyLzofFfmvKwpdPzqiy16LvQfK3yv/fVH7Bj13/wl3JSR1J+rfgRE9q7xUJK4qvgS8raSOeLUehw==", "dev": true }, + "node_modules/set-function-length": { + "version": "1.2.0", + "resolved": "https://registry.npmjs.org/set-function-length/-/set-function-length-1.2.0.tgz", + "integrity": "sha512-4DBHDoyHlM1IRPGYcoxexgh67y4ueR53FKV1yyxwFMY7aCqcN/38M1+SwZ/qJQ8iLv7+ck385ot4CcisOAPT9w==", + "dev": true, + "dependencies": { + "define-data-property": "^1.1.1", + "function-bind": "^1.1.2", + "get-intrinsic": "^1.2.2", + "gopd": "^1.0.1", + "has-property-descriptors": "^1.0.1" + }, + "engines": { + "node": ">= 0.4" + } + }, "node_modules/set-function-name": { "version": "2.0.1", "resolved": "https://registry.npmjs.org/set-function-name/-/set-function-name-2.0.1.tgz", @@ -7519,9 +6948,9 @@ } }, "node_modules/shiki": { - "version": "0.14.4", - "resolved": "https://registry.npmjs.org/shiki/-/shiki-0.14.4.tgz", - "integrity": "sha512-IXCRip2IQzKwxArNNq1S+On4KPML3Yyn8Zzs/xRgcgOWIr8ntIK3IKzjFPfjy/7kt9ZMjc+FItfqHRBg8b6tNQ==", + "version": "0.14.7", + "resolved": "https://registry.npmjs.org/shiki/-/shiki-0.14.7.tgz", + "integrity": "sha512-dNPAPrxSc87ua2sKJ3H5dQ/6ZaY8RNnaAqK+t0eG7p0Soi2ydiqbGOTaZCqaYvA/uZYfS1LJnemt3Q+mSfcPCg==", "dev": true, "dependencies": { "ansi-sequence-parser": "^1.1.0", @@ -7551,16 +6980,16 @@ "dev": true }, "node_modules/sinon": { - "version": "16.0.0", - "resolved": "https://registry.npmjs.org/sinon/-/sinon-16.0.0.tgz", - "integrity": "sha512-B8AaZZm9CT5pqe4l4uWJztfD/mOTa7dL8Qo0W4+s+t74xECOgSZDDQCBjNgIK3+n4kyxQrSTv2V5ul8K25qkiQ==", + "version": "17.0.1", + "resolved": "https://registry.npmjs.org/sinon/-/sinon-17.0.1.tgz", + "integrity": "sha512-wmwE19Lie0MLT+ZYNpDymasPHUKTaZHUH/pKEubRXIzySv9Atnlw+BUMGCzWgV7b7wO+Hw6f1TEOr0IUnmU8/g==", "dev": true, "dependencies": { "@sinonjs/commons": "^3.0.0", - "@sinonjs/fake-timers": "^10.3.0", + "@sinonjs/fake-timers": "^11.2.2", "@sinonjs/samsam": "^8.0.0", "diff": "^5.1.0", - "nise": "^5.1.4", + "nise": "^5.1.5", "supports-color": "^7.2.0" }, "funding": { @@ -7577,15 +7006,6 @@ "node": ">=0.3.1" } }, - "node_modules/sinon/node_modules/has-flag": { - "version": "4.0.0", - "resolved": "https://registry.npmjs.org/has-flag/-/has-flag-4.0.0.tgz", - "integrity": "sha512-EykJT/Q1KjTWctppgIAgfSO0tKVuZUjhgMr17kqTumMl6Afv3EISleU7qZUzoXDFTAHTDC4NOoG/ZxU3EvlMPQ==", - "dev": true, - "engines": { - "node": ">=8" - } - }, "node_modules/sinon/node_modules/supports-color": { "version": "7.2.0", "resolved": "https://registry.npmjs.org/supports-color/-/supports-color-7.2.0.tgz", @@ -7599,12 +7019,12 @@ } }, "node_modules/slash": { - "version": "4.0.0", - "resolved": "https://registry.npmjs.org/slash/-/slash-4.0.0.tgz", - "integrity": "sha512-3dOsAHXXUkQTpOYcoAxLIorMTp4gIQr5IW3iVb7A7lFIp0VHhnynm9izx6TssdrIcVIESAlVjtnO2K8bg+Coew==", + "version": "5.1.0", + "resolved": "https://registry.npmjs.org/slash/-/slash-5.1.0.tgz", + "integrity": "sha512-ZA6oR3T/pEyuqwMgAKT0/hAv8oAXckzbkmR0UkUosQ+Mc4RxGoJkRmwHgHufaenlyAgE1Mxgpdcrf75y6XcnDg==", "dev": true, "engines": { - "node": ">=12" + "node": ">=14.16" }, "funding": { "url": "https://github.com/sponsors/sindresorhus" @@ -7663,16 +7083,6 @@ "node": ">=0.10.0" } }, - "node_modules/source-map-support": { - "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", - "source-map": "^0.6.0" - } - }, "node_modules/spawn-wrap": { "version": "2.0.0", "resolved": "https://registry.npmjs.org/spawn-wrap/-/spawn-wrap-2.0.0.tgz", @@ -7697,15 +7107,12 @@ "dev": true }, "node_modules/stdin-discarder": { - "version": "0.1.0", - "resolved": "https://registry.npmjs.org/stdin-discarder/-/stdin-discarder-0.1.0.tgz", - "integrity": "sha512-xhV7w8S+bUwlPTb4bAOUQhv8/cSS5offJuX8GQGq32ONF0ZtDWKfkdomM3HMRA+LhX6um/FZ0COqlwsjD53LeQ==", + "version": "0.2.2", + "resolved": "https://registry.npmjs.org/stdin-discarder/-/stdin-discarder-0.2.2.tgz", + "integrity": "sha512-UhDfHmA92YAlNnCfhmq0VeNL5bDbiZGg7sZ2IvPsXubGkiNa9EC+tUTsjBRsYUAz87btI6/1wf4XoVvQ3uRnmQ==", "dev": true, - "dependencies": { - "bl": "^5.0.0" - }, "engines": { - "node": "^12.20.0 || ^14.13.1 || >=16.0.0" + "node": ">=18" }, "funding": { "url": "https://github.com/sponsors/sindresorhus" @@ -7825,12 +7232,15 @@ } }, "node_modules/strip-json-comments": { - "version": "2.0.1", - "resolved": "https://registry.npmjs.org/strip-json-comments/-/strip-json-comments-2.0.1.tgz", - "integrity": "sha512-4gB8na07fecVVkOI6Rs4e7T6NOTki5EmL7TUduTs6bu3EdnSycntVJ4re8kgZA+wx9IueI2Y11bfbgwtzuE0KQ==", + "version": "3.1.1", + "resolved": "https://registry.npmjs.org/strip-json-comments/-/strip-json-comments-3.1.1.tgz", + "integrity": "sha512-6fPc+R4ihwqP6N/aIv2f1gMH8lOVtWQHoqC4yK6oSDVVocumAsfCqjkXnqiYMhmMwS/mEHLp7Vehlt3ql6lEig==", "dev": true, "engines": { - "node": ">=0.10.0" + "node": ">=8" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" } }, "node_modules/strip-outer": { @@ -7855,15 +7265,18 @@ } }, "node_modules/supports-color": { - "version": "5.5.0", - "resolved": "https://registry.npmjs.org/supports-color/-/supports-color-5.5.0.tgz", - "integrity": "sha512-QjVjwdXIt408MIiAqCX4oUKsgU2EqAGzs2Ppkm4aQYbjm+ZEWEcW4SfFNTr4uMNZma0ey4f5lgLrkB0aX0QMow==", + "version": "8.1.1", + "resolved": "https://registry.npmjs.org/supports-color/-/supports-color-8.1.1.tgz", + "integrity": "sha512-MpUEN2OodtUzxvKQl72cUF7RQ5EiHsGvSsVG0ia9c5RbWGL2CI4C7EpPS8UTBIplnlzZiNuV56w+FuNxy3ty2Q==", "dev": true, "dependencies": { - "has-flag": "^3.0.0" + "has-flag": "^4.0.0" }, "engines": { - "node": ">=4" + "node": ">=10" + }, + "funding": { + "url": "https://github.com/chalk/supports-color?sponsor=1" } }, "node_modules/supports-preserve-symlinks-flag": { @@ -7892,24 +7305,6 @@ "node": ">=8" } }, - "node_modules/text-table": { - "version": "0.2.0", - "resolved": "https://registry.npmjs.org/text-table/-/text-table-0.2.0.tgz", - "integrity": "sha512-N+8UisAXDGk8PFXP4HAzVR9nbfmVJ3zYLAWiTIoqC5v5isinhr+r5uaO8+7r3BMfuNIufIsA7RdpVgacC2cSpw==", - "dev": true - }, - "node_modules/titleize": { - "version": "3.0.0", - "resolved": "https://registry.npmjs.org/titleize/-/titleize-3.0.0.tgz", - "integrity": "sha512-KxVu8EYHDPBdUYdKZdKtU2aj2XfEx9AfjXxE/Aj0vT06w2icA09Vus1rh6eSu1y01akYg6BjIK/hxyLJINoMLQ==", - "dev": true, - "engines": { - "node": ">=12" - }, - "funding": { - "url": "https://github.com/sponsors/sindresorhus" - } - }, "node_modules/tmp": { "version": "0.0.33", "resolved": "https://registry.npmjs.org/tmp/-/tmp-0.0.33.tgz", @@ -7939,105 +7334,29 @@ "dependencies": { "is-number": "^7.0.0" }, - "engines": { - "node": ">=8.0" - } - }, - "node_modules/tr46": { - "version": "0.0.3", - "resolved": "https://registry.npmjs.org/tr46/-/tr46-0.0.3.tgz", - "integrity": "sha512-N3WMsuqV66lT30CrXNbEjx4GEwlow3v6rr4mCcv6prnfwhS01rkgyFdjPNBYd9br7LpXV1+Emh01fHnq2Gdgrw==", - "dev": true - }, - "node_modules/trim-repeated": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/trim-repeated/-/trim-repeated-1.0.0.tgz", - "integrity": "sha512-pkonvlKk8/ZuR0D5tLW8ljt5I8kmxp2XKymhepUeOdCEfKpZaktSArkLHZt76OB1ZvO9bssUsDty4SWhLvZpLg==", - "dev": true, - "dependencies": { - "escape-string-regexp": "^1.0.2" - }, - "engines": { - "node": ">=0.10.0" - } - }, - "node_modules/trim-repeated/node_modules/escape-string-regexp": { - "version": "1.0.5", - "resolved": "https://registry.npmjs.org/escape-string-regexp/-/escape-string-regexp-1.0.5.tgz", - "integrity": "sha512-vbRorB5FUQWvla16U8R/qgaFIya2qGzwDrNmCZuYKrbdSUMG6I1ZCGQRefkRVhuOkIGVne7BQ35DSfo1qvJqFg==", - "dev": true, - "engines": { - "node": ">=0.8.0" - } - }, - "node_modules/ts-api-utils": { - "version": "1.0.3", - "resolved": "https://registry.npmjs.org/ts-api-utils/-/ts-api-utils-1.0.3.tgz", - "integrity": "sha512-wNMeqtMz5NtwpT/UZGY5alT+VoKdSsOOP/kqHFcUW1P/VRhH2wJ48+DN2WwUliNbQ976ETwDL0Ifd2VVvgonvg==", - "dev": true, - "engines": { - "node": ">=16.13.0" - }, - "peerDependencies": { - "typescript": ">=4.2.0" + "engines": { + "node": ">=8.0" } }, - "node_modules/ts-node": { - "version": "10.9.1", - "resolved": "https://registry.npmjs.org/ts-node/-/ts-node-10.9.1.tgz", - "integrity": "sha512-NtVysVPkxxrwFGUUxGYhfux8k78pQB3JqYBXlLRZgdGUqTO5wU/UyHop5p70iEbGhB7q5KmiZiU0Y3KlJrScEw==", + "node_modules/trim-repeated": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/trim-repeated/-/trim-repeated-1.0.0.tgz", + "integrity": "sha512-pkonvlKk8/ZuR0D5tLW8ljt5I8kmxp2XKymhepUeOdCEfKpZaktSArkLHZt76OB1ZvO9bssUsDty4SWhLvZpLg==", "dev": true, "dependencies": { - "@cspotcode/source-map-support": "^0.8.0", - "@tsconfig/node10": "^1.0.7", - "@tsconfig/node12": "^1.0.7", - "@tsconfig/node14": "^1.0.0", - "@tsconfig/node16": "^1.0.2", - "acorn": "^8.4.1", - "acorn-walk": "^8.1.1", - "arg": "^4.1.0", - "create-require": "^1.1.0", - "diff": "^4.0.1", - "make-error": "^1.1.1", - "v8-compile-cache-lib": "^3.0.1", - "yn": "3.1.1" - }, - "bin": { - "ts-node": "dist/bin.js", - "ts-node-cwd": "dist/bin-cwd.js", - "ts-node-esm": "dist/bin-esm.js", - "ts-node-script": "dist/bin-script.js", - "ts-node-transpile-only": "dist/bin-transpile.js", - "ts-script": "dist/bin-script-deprecated.js" - }, - "peerDependencies": { - "@swc/core": ">=1.2.50", - "@swc/wasm": ">=1.2.50", - "@types/node": "*", - "typescript": ">=2.7" + "escape-string-regexp": "^1.0.2" }, - "peerDependenciesMeta": { - "@swc/core": { - "optional": true - }, - "@swc/wasm": { - "optional": true - } + "engines": { + "node": ">=0.10.0" } }, - "node_modules/ts-node/node_modules/@tsconfig/node14": { - "version": "1.0.3", - "resolved": "https://registry.npmjs.org/@tsconfig/node14/-/node14-1.0.3.tgz", - "integrity": "sha512-ysT8mhdixWK6Hw3i1V2AeRqZ5WfXg1G43mqoYlM2nc6388Fq5jcXyr5mRsqViLx/GJYdoL0bfXD8nmF+Zn/Iow==", - "dev": true - }, - "node_modules/ts-node/node_modules/diff": { - "version": "4.0.2", - "resolved": "https://registry.npmjs.org/diff/-/diff-4.0.2.tgz", - "integrity": "sha512-58lmxKSA4BNyLz+HHMUzlOEpg09FV+ev6ZMe3vJihgdxzgcwZ8VoEEPmALCZG9LmqfVoNMMKpttIYTVG6uDY7A==", + "node_modules/trim-repeated/node_modules/escape-string-regexp": { + "version": "1.0.5", + "resolved": "https://registry.npmjs.org/escape-string-regexp/-/escape-string-regexp-1.0.5.tgz", + "integrity": "sha512-vbRorB5FUQWvla16U8R/qgaFIya2qGzwDrNmCZuYKrbdSUMG6I1ZCGQRefkRVhuOkIGVne7BQ35DSfo1qvJqFg==", "dev": true, "engines": { - "node": ">=0.3.1" + "node": ">=0.8.0" } }, "node_modules/tslib": { @@ -8046,16 +7365,23 @@ "integrity": "sha512-AEYxH93jGFPn/a2iVAwW87VuUIkR1FVUKB77NwMF7nBTDkDrrT/Hpt/IrCJ0QXhW27jTBDcf5ZY7w6RiqTMw2Q==", "dev": true }, - "node_modules/type-check": { - "version": "0.4.0", - "resolved": "https://registry.npmjs.org/type-check/-/type-check-0.4.0.tgz", - "integrity": "sha512-XleUoc9uwGXqjWwXaUTZAmzMcFZ5858QA2vvx1Ur5xIcixXIP+8LnFDgRplU30us6teqdlskFfu+ae4K79Ooew==", + "node_modules/tsx": { + "version": "4.7.0", + "resolved": "https://registry.npmjs.org/tsx/-/tsx-4.7.0.tgz", + "integrity": "sha512-I+t79RYPlEYlHn9a+KzwrvEwhJg35h/1zHsLC2JXvhC2mdynMv6Zxzvhv5EMV6VF5qJlLlkSnMVvdZV3PSIGcg==", "dev": true, "dependencies": { - "prelude-ls": "^1.2.1" + "esbuild": "~0.19.10", + "get-tsconfig": "^4.7.2" + }, + "bin": { + "tsx": "dist/cli.mjs" }, "engines": { - "node": ">= 0.8.0" + "node": ">=18.0.0" + }, + "optionalDependencies": { + "fsevents": "~2.3.3" } }, "node_modules/type-detect": { @@ -8068,15 +7394,12 @@ } }, "node_modules/type-fest": { - "version": "0.21.3", - "resolved": "https://registry.npmjs.org/type-fest/-/type-fest-0.21.3.tgz", - "integrity": "sha512-t0rzBq87m3fVcduHDUFhKmyyX+9eo6WQjZvf51Ea/M0Q7+T374Jp1aUiyUl0GKxp8M/OETVHSDvmkyPgvX+X2w==", + "version": "0.8.1", + "resolved": "https://registry.npmjs.org/type-fest/-/type-fest-0.8.1.tgz", + "integrity": "sha512-4dbzIzqvjtgiM5rw1k5rEHtBANKmdudhGyBEajN01fEyhaAIhsoKNy6y7+IN93IfpFtwY9iqi7kD+xwKhQsNJA==", "dev": true, "engines": { - "node": ">=10" - }, - "funding": { - "url": "https://github.com/sponsors/sindresorhus" + "node": ">=8" } }, "node_modules/typed-array-buffer": { @@ -8154,15 +7477,15 @@ } }, "node_modules/typedoc": { - "version": "0.25.1", - "resolved": "https://registry.npmjs.org/typedoc/-/typedoc-0.25.1.tgz", - "integrity": "sha512-c2ye3YUtGIadxN2O6YwPEXgrZcvhlZ6HlhWZ8jQRNzwLPn2ylhdGqdR8HbyDRyALP8J6lmSANILCkkIdNPFxqA==", + "version": "0.25.7", + "resolved": "https://registry.npmjs.org/typedoc/-/typedoc-0.25.7.tgz", + "integrity": "sha512-m6A6JjQRg39p2ZVRIN3NKXgrN8vzlHhOS+r9ymUYtcUP/TIQPvWSq7YgE5ZjASfv5Vd5BW5xrir6Gm2XNNcOow==", "dev": true, "dependencies": { "lunr": "^2.3.9", "marked": "^4.3.0", "minimatch": "^9.0.3", - "shiki": "^0.14.1" + "shiki": "^0.14.7" }, "bin": { "typedoc": "bin/typedoc" @@ -8171,7 +7494,7 @@ "node": ">= 16" }, "peerDependencies": { - "typescript": "4.6.x || 4.7.x || 4.8.x || 4.9.x || 5.0.x || 5.1.x || 5.2.x" + "typescript": "4.6.x || 4.7.x || 4.8.x || 4.9.x || 5.0.x || 5.1.x || 5.2.x || 5.3.x" } }, "node_modules/typedoc/node_modules/brace-expansion": { @@ -8199,9 +7522,9 @@ } }, "node_modules/typescript": { - "version": "5.2.2", - "resolved": "https://registry.npmjs.org/typescript/-/typescript-5.2.2.tgz", - "integrity": "sha512-mI4WrpHsbCIcwT9cF4FZvr80QUeKvsUsUvKDoR+X/7XHQH98xYD8YHZg7ANtz2GtZt/CBq2QJ0thkGJMHfqc1w==", + "version": "5.3.3", + "resolved": "https://registry.npmjs.org/typescript/-/typescript-5.3.3.tgz", + "integrity": "sha512-pXWcraxM0uxAS+tN0AG/BF2TyqmHO014Z070UsJ+pFvYuRSq8KH8DmWpnbXe0pEPDHXZV3FcAbJkijJ5oNEnWw==", "dev": true, "bin": { "tsc": "bin/tsc", @@ -8226,6 +7549,24 @@ "url": "https://github.com/sponsors/ljharb" } }, + "node_modules/undici-types": { + "version": "5.26.5", + "resolved": "https://registry.npmjs.org/undici-types/-/undici-types-5.26.5.tgz", + "integrity": "sha512-JlCMO+ehdEIKqlFxk6IfVoAUVmgz7cU7zD/h9XZ0qzeosSHmUJVOzSQvvYSYWXkFXC+IfLKSIffhv0sVZup6pA==", + "dev": true + }, + "node_modules/unicorn-magic": { + "version": "0.1.0", + "resolved": "https://registry.npmjs.org/unicorn-magic/-/unicorn-magic-0.1.0.tgz", + "integrity": "sha512-lRfVq8fE8gz6QMBuDM6a+LO3IAzTi05H6gCVaUpir2E1Rwpo4ZUog45KpNXKC/Mn3Yb9UDuHumeFTo9iV/D9FQ==", + "dev": true, + "engines": { + "node": ">=18" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, "node_modules/unique-string": { "version": "3.0.0", "resolved": "https://registry.npmjs.org/unique-string/-/unique-string-3.0.0.tgz", @@ -8242,33 +7583,24 @@ } }, "node_modules/universal-user-agent": { - "version": "6.0.0", - "resolved": "https://registry.npmjs.org/universal-user-agent/-/universal-user-agent-6.0.0.tgz", - "integrity": "sha512-isyNax3wXoKaulPDZWHQqbmIx1k2tb9fb3GGDBRxCscfYV2Ch7WxPArBsFEG8s/safwXTT7H4QGhaIkTp9447w==", + "version": "6.0.1", + "resolved": "https://registry.npmjs.org/universal-user-agent/-/universal-user-agent-6.0.1.tgz", + "integrity": "sha512-yCzhz6FN2wU1NiiQRogkTQszlQSlpWaw8SvVegAc+bDxbzHgh1vX8uIe8OYyMH6DwH+sdTJsgMl36+mSMdRJIQ==", "dev": true }, "node_modules/universalify": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/universalify/-/universalify-2.0.0.tgz", - "integrity": "sha512-hAZsKq7Yy11Zu1DE0OzWjw7nnLZmJZYTDZZyEFHZdUhV8FkH5MCfoU1XMaxXovpyW5nq5scPqq0ZDP9Zyl04oQ==", + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/universalify/-/universalify-2.0.1.tgz", + "integrity": "sha512-gptHNQghINnc/vTGIk0SOFGFNXw7JVrlRUtConJRlvaw6DuX0wO5Jeko9sWrMBhh+PsYAZ7oXAiOnf/UKogyiw==", "dev": true, "engines": { "node": ">= 10.0.0" } }, - "node_modules/untildify": { - "version": "4.0.0", - "resolved": "https://registry.npmjs.org/untildify/-/untildify-4.0.0.tgz", - "integrity": "sha512-KK8xQ1mkzZeg9inewmFVDNkg3l5LUhoq9kN6iWYB/CC9YMG8HA+c1Q8HwDe6dEX7kErrEVNVBO3fWsVq5iDgtw==", - "dev": true, - "engines": { - "node": ">=8" - } - }, "node_modules/update-browserslist-db": { - "version": "1.0.11", - "resolved": "https://registry.npmjs.org/update-browserslist-db/-/update-browserslist-db-1.0.11.tgz", - "integrity": "sha512-dCwEFf0/oT85M1fHBg4F0jtLwJrutGoHSQXCh7u4o2t1drG+c0a9Flnqww6XUKSfQMPpJBRjU8d4RXB09qtvaA==", + "version": "1.0.13", + "resolved": "https://registry.npmjs.org/update-browserslist-db/-/update-browserslist-db-1.0.13.tgz", + "integrity": "sha512-xebP81SNcPuNpPP3uzeW1NYXxI3rxyJzF3pD6sH4jE7o/IX+WtSpwnVU+qIsDPyk0d3hmFQ7mjqc6AtV604hbg==", "dev": true, "funding": [ { @@ -8296,33 +7628,43 @@ } }, "node_modules/update-notifier": { - "version": "6.0.2", - "resolved": "https://registry.npmjs.org/update-notifier/-/update-notifier-6.0.2.tgz", - "integrity": "sha512-EDxhTEVPZZRLWYcJ4ZXjGFN0oP7qYvbXWzEgRm/Yql4dHX5wDbvh89YHP6PK1lzZJYrMtXUuZZz8XGK+U6U1og==", + "version": "7.0.0", + "resolved": "https://registry.npmjs.org/update-notifier/-/update-notifier-7.0.0.tgz", + "integrity": "sha512-Hv25Bh+eAbOLlsjJreVPOs4vd51rrtCrmhyOJtbpAojro34jS4KQaEp4/EvlHJX7jSO42VvEFpkastVyXyIsdQ==", "dev": true, "dependencies": { - "boxen": "^7.0.0", - "chalk": "^5.0.1", + "boxen": "^7.1.1", + "chalk": "^5.3.0", "configstore": "^6.0.0", - "has-yarn": "^3.0.0", "import-lazy": "^4.0.0", - "is-ci": "^3.0.1", + "is-in-ci": "^0.1.0", "is-installed-globally": "^0.4.0", "is-npm": "^6.0.0", - "is-yarn-global": "^0.4.0", "latest-version": "^7.0.0", "pupa": "^3.1.0", - "semver": "^7.3.7", + "semver": "^7.5.4", "semver-diff": "^4.0.0", "xdg-basedir": "^5.1.0" }, "engines": { - "node": ">=14.16" + "node": ">=18" }, "funding": { "url": "https://github.com/yeoman/update-notifier?sponsor=1" } }, + "node_modules/update-notifier/node_modules/chalk": { + "version": "5.3.0", + "resolved": "https://registry.npmjs.org/chalk/-/chalk-5.3.0.tgz", + "integrity": "sha512-dLitG79d+GV1Nb/VYcCDFivJeK1hiukt9QjRNVOsUtTy1rR1YJsmpGGTZ3qJos+uw7WmWF4wUwBd9jxjocFC2w==", + "dev": true, + "engines": { + "node": "^12.17.0 || ^14.13 || >=16.0.0" + }, + "funding": { + "url": "https://github.com/chalk/chalk?sponsor=1" + } + }, "node_modules/update-notifier/node_modules/lru-cache": { "version": "6.0.0", "resolved": "https://registry.npmjs.org/lru-cache/-/lru-cache-6.0.0.tgz", @@ -8350,14 +7692,11 @@ "node": ">=10" } }, - "node_modules/uri-js": { - "version": "4.4.1", - "resolved": "https://registry.npmjs.org/uri-js/-/uri-js-4.4.1.tgz", - "integrity": "sha512-7rKUyy33Q1yc98pQ1DAmLtwX109F7TIfWlW1Ydo8Wl1ii1SeHieeh0HHfPeL2fMXK6z0s8ecKs9frCuLJvndBg==", - "dev": true, - "dependencies": { - "punycode": "^2.1.0" - } + "node_modules/update-notifier/node_modules/yallist": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/yallist/-/yallist-4.0.0.tgz", + "integrity": "sha512-3wdGidZyq5PB084XLES5TpOSRA3wjXAlIWMhum2kRcv/41Sn2emQ0dycQW4uZXLejwKvg6EsvbdlVL+FYEct7A==", + "dev": true }, "node_modules/url-join": { "version": "5.0.0", @@ -8383,12 +7722,6 @@ "uuid": "dist/bin/uuid" } }, - "node_modules/v8-compile-cache-lib": { - "version": "3.0.1", - "resolved": "https://registry.npmjs.org/v8-compile-cache-lib/-/v8-compile-cache-lib-3.0.1.tgz", - "integrity": "sha512-wa7YjyUGfNZngI/vtK0UHAN+lgDCxBPCylVXGp0zu59Fz5aiGtNXaq3DhIov063MorB+VfufLh3JlF2KdTK3xg==", - "dev": true - }, "node_modules/vscode-oniguruma": { "version": "1.7.0", "resolved": "https://registry.npmjs.org/vscode-oniguruma/-/vscode-oniguruma-1.7.0.tgz", @@ -8411,30 +7744,14 @@ } }, "node_modules/web-streams-polyfill": { - "version": "3.2.1", - "resolved": "https://registry.npmjs.org/web-streams-polyfill/-/web-streams-polyfill-3.2.1.tgz", - "integrity": "sha512-e0MO3wdXWKrLbL0DgGnUV7WHVuw9OUvL4hjgnPkIeEvESk74gAITi5G606JtZPp39cd8HA9VQzCIvA49LpPN5Q==", + "version": "3.3.2", + "resolved": "https://registry.npmjs.org/web-streams-polyfill/-/web-streams-polyfill-3.3.2.tgz", + "integrity": "sha512-3pRGuxRF5gpuZc0W+EpwQRmCD7gRqcDOMt688KmdlDAgAyaB1XlN0zq2njfDNm44XVdIouE7pZ6GzbdyH47uIQ==", "dev": true, "engines": { "node": ">= 8" } }, - "node_modules/webidl-conversions": { - "version": "3.0.1", - "resolved": "https://registry.npmjs.org/webidl-conversions/-/webidl-conversions-3.0.1.tgz", - "integrity": "sha512-2JAn3z8AR6rjK8Sm8orRC0h/bcl/DqL7tRPdGZ4I1CjdF+EaMLmYxBHyXuKL849eucPFhvBoxMsflfOb8kxaeQ==", - "dev": true - }, - "node_modules/whatwg-url": { - "version": "5.0.0", - "resolved": "https://registry.npmjs.org/whatwg-url/-/whatwg-url-5.0.0.tgz", - "integrity": "sha512-saE57nupxk6v3HY35+jzBwYa0rKSy0XR8JSxZPwgLr7ys0IBzhGviA1/TUGJLmSVqs8pb9AnvICXEuOHLprYTw==", - "dev": true, - "dependencies": { - "tr46": "~0.0.3", - "webidl-conversions": "^3.0.0" - } - }, "node_modules/which": { "version": "2.0.2", "resolved": "https://registry.npmjs.org/which/-/which-2.0.2.tgz", @@ -8473,16 +7790,16 @@ "dev": true }, "node_modules/which-typed-array": { - "version": "1.1.11", - "resolved": "https://registry.npmjs.org/which-typed-array/-/which-typed-array-1.1.11.tgz", - "integrity": "sha512-qe9UWWpkeG5yzZ0tNYxDmd7vo58HDBc39mZ0xWWpolAGADdFOzkfamWLDxkOWcvHQKVmdTyQdLD4NOfjLWTKew==", + "version": "1.1.14", + "resolved": "https://registry.npmjs.org/which-typed-array/-/which-typed-array-1.1.14.tgz", + "integrity": "sha512-VnXFiIW8yNn9kIHN88xvZ4yOWchftKDsRJ8fEPacX/wl1lOvBrhsJ/OeJCXq7B0AaijRuqgzSKalJoPk+D8MPg==", "dev": true, "dependencies": { - "available-typed-arrays": "^1.0.5", - "call-bind": "^1.0.2", + "available-typed-arrays": "^1.0.6", + "call-bind": "^1.0.5", "for-each": "^0.3.3", "gopd": "^1.0.1", - "has-tostringtag": "^1.0.0" + "has-tostringtag": "^1.0.1" }, "engines": { "node": ">= 0.4" @@ -8600,6 +7917,18 @@ "url": "https://github.com/sindresorhus/execa?sponsor=1" } }, + "node_modules/windows-release/node_modules/get-stream": { + "version": "6.0.1", + "resolved": "https://registry.npmjs.org/get-stream/-/get-stream-6.0.1.tgz", + "integrity": "sha512-ts6Wi+2j3jQjqi70w5AlN8DFnkSwC+MqmxEzdEALB2qXZYV3X/b1CTfgPLGJNMeAWxdPfU8FO1ms3NUfaHCPYg==", + "dev": true, + "engines": { + "node": ">=10" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, "node_modules/windows-release/node_modules/human-signals": { "version": "2.1.0", "resolved": "https://registry.npmjs.org/human-signals/-/human-signals-2.1.0.tgz", @@ -8609,18 +7938,6 @@ "node": ">=10.17.0" } }, - "node_modules/windows-release/node_modules/is-stream": { - "version": "2.0.1", - "resolved": "https://registry.npmjs.org/is-stream/-/is-stream-2.0.1.tgz", - "integrity": "sha512-hFoiJiTl63nn+kstHGBtewWSKnQLpyb155KHheA1l39uvtO9nWIop1p3udqPcUd/xbF1VLMO4n7OI6p7RbngDg==", - "dev": true, - "engines": { - "node": ">=8" - }, - "funding": { - "url": "https://github.com/sponsors/sindresorhus" - } - }, "node_modules/windows-release/node_modules/mimic-fn": { "version": "2.1.0", "resolved": "https://registry.npmjs.org/mimic-fn/-/mimic-fn-2.1.0.tgz", @@ -8726,35 +8043,36 @@ } }, "node_modules/yallist": { - "version": "4.0.0", - "resolved": "https://registry.npmjs.org/yallist/-/yallist-4.0.0.tgz", - "integrity": "sha512-3wdGidZyq5PB084XLES5TpOSRA3wjXAlIWMhum2kRcv/41Sn2emQ0dycQW4uZXLejwKvg6EsvbdlVL+FYEct7A==" + "version": "3.1.1", + "resolved": "https://registry.npmjs.org/yallist/-/yallist-3.1.1.tgz", + "integrity": "sha512-a4UGQaWPH59mOXUYnAG2ewncQS4i4F43Tv3JoAM+s2VDAmS9NsK8GpDMLrCHPksFT7h3K6TOoUNn2pb7RoXx4g==", + "dev": true }, "node_modules/yargs": { - "version": "17.7.2", - "resolved": "https://registry.npmjs.org/yargs/-/yargs-17.7.2.tgz", - "integrity": "sha512-7dSzzRQ++CKnNI/krKnYRV7JKKPUXMEh61soaHKg9mrWEhzFWhFnxPxGl+69cD1Ou63C13NUPCnmIcrvqCuM6w==", + "version": "16.2.0", + "resolved": "https://registry.npmjs.org/yargs/-/yargs-16.2.0.tgz", + "integrity": "sha512-D1mvvtDG0L5ft/jGWkLpG1+m0eQxOfaBvTNELraWj22wSVUMWxZUvYgJYcKh6jGGIkJFhH4IZPQhR4TKpc8mBw==", "dev": true, "dependencies": { - "cliui": "^8.0.1", + "cliui": "^7.0.2", "escalade": "^3.1.1", "get-caller-file": "^2.0.5", "require-directory": "^2.1.1", - "string-width": "^4.2.3", + "string-width": "^4.2.0", "y18n": "^5.0.5", - "yargs-parser": "^21.1.1" + "yargs-parser": "^20.2.2" }, "engines": { - "node": ">=12" + "node": ">=10" } }, "node_modules/yargs-parser": { - "version": "21.1.1", - "resolved": "https://registry.npmjs.org/yargs-parser/-/yargs-parser-21.1.1.tgz", - "integrity": "sha512-tVpsJW7DdjecAiFpbIB1e3qxIQsE6NoPc5/eTdrbbIC4h0LVsWhnoa3g+m2HclBIujHzsxZ4VJVA+GUuc2/LBw==", + "version": "20.2.4", + "resolved": "https://registry.npmjs.org/yargs-parser/-/yargs-parser-20.2.4.tgz", + "integrity": "sha512-WOkpgNhPTlE73h4VFAFsOnomJVaovO8VqLDzy5saChRBFQFBoMYirowyW+Q9HB4HFF4Z7VZTiG3iSzJJA29yRA==", "dev": true, "engines": { - "node": ">=12" + "node": ">=10" } }, "node_modules/yargs-unparser": { @@ -8796,15 +8114,6 @@ "url": "https://github.com/sponsors/sindresorhus" } }, - "node_modules/yn": { - "version": "3.1.1", - "resolved": "https://registry.npmjs.org/yn/-/yn-3.1.1.tgz", - "integrity": "sha512-Ux4ygGWsu2c7isFWe8Yu1YluJmqVhxqK2cLXNQA5AcC3QfbGNpM7fu0Y8b/z16pXLnFxZYvWhd3fhBY9DLmC6Q==", - "dev": true, - "engines": { - "node": ">=6" - } - }, "node_modules/yocto-queue": { "version": "0.1.0", "resolved": "https://registry.npmjs.org/yocto-queue/-/yocto-queue-0.1.0.tgz", @@ -8819,145 +8128,171 @@ }, "packages/bloom": { "name": "@redis/bloom", - "version": "1.2.0", + "version": "2.0.0-next.3", "license": "MIT", "devDependencies": { - "@istanbuljs/nyc-config-typescript": "^1.0.2", - "@redis/test-utils": "*", - "@types/node": "^20.6.2", - "nyc": "^15.1.0", - "release-it": "^16.1.5", - "source-map-support": "^0.5.21", - "ts-node": "^10.9.1", - "typedoc": "^0.25.1", - "typescript": "^5.2.2" + "@redis/test-utils": "*" + }, + "engines": { + "node": ">= 18" }, "peerDependencies": { - "@redis/client": "^1.0.0" + "@redis/client": "^2.0.0-next.4" } }, "packages/client": { "name": "@redis/client", - "version": "1.6.0", + "version": "2.0.0-next.4", "license": "MIT", "dependencies": { - "cluster-key-slot": "1.1.2", - "generic-pool": "3.9.0", - "yallist": "4.0.0" + "cluster-key-slot": "1.1.2" }, "devDependencies": { - "@istanbuljs/nyc-config-typescript": "^1.0.2", "@redis/test-utils": "*", - "@types/node": "^20.6.2", - "@types/sinon": "^10.0.16", - "@types/yallist": "^4.0.1", - "@typescript-eslint/eslint-plugin": "^6.7.2", - "@typescript-eslint/parser": "^6.7.2", - "eslint": "^8.49.0", - "nyc": "^15.1.0", - "release-it": "^16.1.5", - "sinon": "^16.0.0", - "source-map-support": "^0.5.21", - "ts-node": "^10.9.1", - "typedoc": "^0.25.1", - "typescript": "^5.2.2" + "@types/sinon": "^17.0.3", + "sinon": "^17.0.1" }, "engines": { - "node": ">=14" + "node": ">= 18" } }, "packages/graph": { "name": "@redis/graph", - "version": "1.1.1", + "version": "2.0.0-next.2", "license": "MIT", "devDependencies": { - "@istanbuljs/nyc-config-typescript": "^1.0.2", - "@redis/test-utils": "*", - "@types/node": "^20.6.2", - "nyc": "^15.1.0", - "release-it": "^16.1.5", - "source-map-support": "^0.5.21", - "ts-node": "^10.9.1", - "typedoc": "^0.25.1", - "typescript": "^5.2.2" + "@redis/test-utils": "*" + }, + "engines": { + "node": ">= 18" }, "peerDependencies": { - "@redis/client": "^1.0.0" + "@redis/client": "^2.0.0-next.4" } }, "packages/json": { "name": "@redis/json", - "version": "1.0.7", + "version": "2.0.0-next.2", "license": "MIT", "devDependencies": { - "@istanbuljs/nyc-config-typescript": "^1.0.2", - "@redis/test-utils": "*", - "@types/node": "^20.6.2", - "nyc": "^15.1.0", - "release-it": "^16.1.5", - "source-map-support": "^0.5.21", - "ts-node": "^10.9.1", - "typedoc": "^0.25.1", - "typescript": "^5.2.2" + "@redis/test-utils": "*" + }, + "engines": { + "node": ">= 18" }, "peerDependencies": { - "@redis/client": "^1.0.0" + "@redis/client": "^2.0.0-next.4" + } + }, + "packages/redis": { + "version": "5.0.0-next.4", + "license": "MIT", + "dependencies": { + "@redis/bloom": "2.0.0-next.3", + "@redis/client": "2.0.0-next.4", + "@redis/graph": "2.0.0-next.2", + "@redis/json": "2.0.0-next.2", + "@redis/search": "2.0.0-next.2", + "@redis/time-series": "2.0.0-next.2" + }, + "engines": { + "node": ">= 18" } }, "packages/search": { "name": "@redis/search", - "version": "1.2.0", + "version": "2.0.0-next.2", "license": "MIT", "devDependencies": { - "@istanbuljs/nyc-config-typescript": "^1.0.2", - "@redis/test-utils": "*", - "@types/node": "^20.6.2", - "nyc": "^15.1.0", - "release-it": "^16.1.5", - "source-map-support": "^0.5.21", - "ts-node": "^10.9.1", - "typedoc": "^0.25.1", - "typescript": "^5.2.2" + "@redis/test-utils": "*" + }, + "engines": { + "node": ">= 18" }, "peerDependencies": { - "@redis/client": "^1.0.0" + "@redis/client": "^2.0.0-next.4" } }, "packages/test-utils": { "name": "@redis/test-utils", "devDependencies": { - "@istanbuljs/nyc-config-typescript": "^1.0.2", - "@types/mocha": "^10.0.1", - "@types/node": "^20.6.2", - "@types/yargs": "^17.0.24", - "mocha": "^10.2.0", - "nyc": "^15.1.0", - "source-map-support": "^0.5.21", - "ts-node": "^10.9.1", - "typescript": "^5.2.2", + "@types/yargs": "^17.0.32", "yargs": "^17.7.2" }, "peerDependencies": { - "@redis/client": "^1.0.0" + "@redis/client": "*" + } + }, + "packages/test-utils/node_modules/cliui": { + "version": "8.0.1", + "resolved": "https://registry.npmjs.org/cliui/-/cliui-8.0.1.tgz", + "integrity": "sha512-BSeNnyus75C4//NQ9gQt1/csTXyo/8Sb+afLAkzAptFuMsod9HFokGNudZpi/oQV73hnVK+sR+5PVRMd+Dr7YQ==", + "dev": true, + "dependencies": { + "string-width": "^4.2.0", + "strip-ansi": "^6.0.1", + "wrap-ansi": "^7.0.0" + }, + "engines": { + "node": ">=12" + } + }, + "packages/test-utils/node_modules/wrap-ansi": { + "version": "7.0.0", + "resolved": "https://registry.npmjs.org/wrap-ansi/-/wrap-ansi-7.0.0.tgz", + "integrity": "sha512-YVGIj2kamLSTxw6NsZjoBxfSwsn0ycdesmc4p+Q21c5zPuZ1pl+NfxVdxPtdHvmNVOQ6XSYG4AUtyt/Fi7D16Q==", + "dev": true, + "dependencies": { + "ansi-styles": "^4.0.0", + "string-width": "^4.1.0", + "strip-ansi": "^6.0.0" + }, + "engines": { + "node": ">=10" + }, + "funding": { + "url": "https://github.com/chalk/wrap-ansi?sponsor=1" + } + }, + "packages/test-utils/node_modules/yargs": { + "version": "17.7.2", + "resolved": "https://registry.npmjs.org/yargs/-/yargs-17.7.2.tgz", + "integrity": "sha512-7dSzzRQ++CKnNI/krKnYRV7JKKPUXMEh61soaHKg9mrWEhzFWhFnxPxGl+69cD1Ou63C13NUPCnmIcrvqCuM6w==", + "dev": true, + "dependencies": { + "cliui": "^8.0.1", + "escalade": "^3.1.1", + "get-caller-file": "^2.0.5", + "require-directory": "^2.1.1", + "string-width": "^4.2.3", + "y18n": "^5.0.5", + "yargs-parser": "^21.1.1" + }, + "engines": { + "node": ">=12" + } + }, + "packages/test-utils/node_modules/yargs-parser": { + "version": "21.1.1", + "resolved": "https://registry.npmjs.org/yargs-parser/-/yargs-parser-21.1.1.tgz", + "integrity": "sha512-tVpsJW7DdjecAiFpbIB1e3qxIQsE6NoPc5/eTdrbbIC4h0LVsWhnoa3g+m2HclBIujHzsxZ4VJVA+GUuc2/LBw==", + "dev": true, + "engines": { + "node": ">=12" } }, "packages/time-series": { "name": "@redis/time-series", - "version": "1.1.0", + "version": "2.0.0-next.2", "license": "MIT", "devDependencies": { - "@istanbuljs/nyc-config-typescript": "^1.0.2", - "@redis/test-utils": "*", - "@types/node": "^20.6.2", - "nyc": "^15.1.0", - "release-it": "^16.1.5", - "source-map-support": "^0.5.21", - "ts-node": "^10.9.1", - "typedoc": "^0.25.1", - "typescript": "^5.2.2" + "@redis/test-utils": "*" + }, + "engines": { + "node": ">= 18" }, "peerDependencies": { - "@redis/client": "^1.0.0" + "@redis/client": "^2.0.0-next.4" } } } diff --git a/package.json b/package.json index e8ceef7173d..c626d4a48e8 100644 --- a/package.json +++ b/package.json @@ -1,50 +1,25 @@ { - "name": "redis", - "description": "A modern, high performance Redis client", - "version": "4.7.0", - "license": "MIT", - "main": "./dist/index.js", - "types": "./dist/index.d.ts", - "files": [ - "dist/" - ], + "name": "redis-monorepo", + "private": true, "workspaces": [ "./packages/*" ], "scripts": { "test": "npm run test -ws --if-present", - "build:client": "npm run build -w ./packages/client", - "build:test-utils": "npm run build -w ./packages/test-utils", - "build:tests-tools": "npm run build:client && npm run build:test-utils", - "build:modules": "find ./packages -mindepth 1 -maxdepth 1 -type d ! -name 'client' ! -name 'test-utils' -exec npm run build -w {} \\;", - "build": "tsc", - "build-all": "npm run build:client && npm run build:test-utils && npm run build:modules && npm run build", - "documentation": "npm run documentation -ws --if-present", + "build": "tsc --build", + "documentation": "typedoc", "gh-pages": "gh-pages -d ./documentation -e ./documentation -u 'documentation-bot '" }, - "dependencies": { - "@redis/bloom": "1.2.0", - "@redis/client": "1.6.0", - "@redis/graph": "1.1.1", - "@redis/json": "1.0.7", - "@redis/search": "1.2.0", - "@redis/time-series": "1.1.0" - }, "devDependencies": { - "@tsconfig/node14": "^14.1.0", - "gh-pages": "^6.0.0", - "release-it": "^16.1.5", - "typescript": "^5.2.2" - }, - "repository": { - "type": "git", - "url": "git://github.com/redis/node-redis.git" - }, - "bugs": { - "url": "https://github.com/redis/node-redis/issues" - }, - "homepage": "https://github.com/redis/node-redis", - "keywords": [ - "redis" - ] + "@istanbuljs/nyc-config-typescript": "^1.0.2", + "@types/mocha": "^10.0.6", + "@types/node": "^20.11.16", + "gh-pages": "^6.1.1", + "mocha": "^10.2.0", + "nyc": "^15.1.0", + "release-it": "^17.0.3", + "tsx": "^4.7.0", + "typedoc": "^0.25.7", + "typescript": "^5.3.3" + } } diff --git a/packages/bloom/.npmignore b/packages/bloom/.npmignore deleted file mode 100644 index bbef2b404fb..00000000000 --- a/packages/bloom/.npmignore +++ /dev/null @@ -1,6 +0,0 @@ -.nyc_output/ -coverage/ -lib/ -.nycrc.json -.release-it.json -tsconfig.json diff --git a/packages/bloom/.release-it.json b/packages/bloom/.release-it.json index 5d11263645f..3a27a088058 100644 --- a/packages/bloom/.release-it.json +++ b/packages/bloom/.release-it.json @@ -5,6 +5,7 @@ "tagAnnotation": "Release ${tagName}" }, "npm": { + "versionArgs": ["--workspaces-update=false"], "publishArgs": ["--access", "public"] } } diff --git a/packages/bloom/README.md b/packages/bloom/README.md index 8eb1445d188..e527ff5552c 100644 --- a/packages/bloom/README.md +++ b/packages/bloom/README.md @@ -1,14 +1,17 @@ # @redis/bloom -This package provides support for the [RedisBloom](https://redisbloom.io) module, which adds additional probabilistic data structures to Redis. It extends the [Node Redis client](https://github.com/redis/node-redis) to include functions for each of the RediBloom commands. +This package provides support for the [RedisBloom](https://redis.io/docs/data-types/probabilistic/) module, which adds additional probabilistic data structures to Redis. -To use these extra commands, your Redis server must have the RedisBloom module installed. +Should be used with [`redis`/`@redis/client`](https://github.com/redis/node-redis). + +:warning: To use these extra commands, your Redis server must have the RedisBloom module installed. RedisBloom provides the following probabilistic data structures: * Bloom Filter: for checking set membership with a high degree of certainty. * Cuckoo Filter: for checking set membership with a high degree of certainty. -* Count-Min Sketch: Determine the frequency of events in a stream. +* T-Digest: for estimating the quantiles of a stream of data. * Top-K: Maintain a list of k most frequently seen items. +* Count-Min Sketch: Determine the frequency of events in a stream. -For complete examples, see `bloom-filter.js`, `cuckoo-filter.js`, `count-min-sketch.js` and `topk.js` in the Node Redis examples folder. +For some examples, see [`bloom-filter.js`](https://github.com/redis/node-redis/tree/master/examples/bloom-filter.js), [`cuckoo-filter.js`](https://github.com/redis/node-redis/tree/master/examples/cuckoo-filter.js), [`count-min-sketch.js`](https://github.com/redis/node-redis/tree/master/examples/count-min-sketch.js) and [`topk.js`](https://github.com/redis/node-redis/tree/master/examples/topk.js) in the [examples folder](https://github.com/redis/node-redis/tree/master/examples). diff --git a/packages/bloom/lib/commands/bloom/ADD.spec.ts b/packages/bloom/lib/commands/bloom/ADD.spec.ts index e7ec3409136..11267e2afdb 100644 --- a/packages/bloom/lib/commands/bloom/ADD.spec.ts +++ b/packages/bloom/lib/commands/bloom/ADD.spec.ts @@ -1,19 +1,19 @@ -import { strict as assert } from 'assert'; +import { strict as assert } from 'node:assert'; import testUtils, { GLOBAL } from '../../test-utils'; -import { transformArguments } from './ADD'; +import ADD from './ADD'; -describe('BF ADD', () => { - it('transformArguments', () => { - assert.deepEqual( - transformArguments('key', 'item'), - ['BF.ADD', 'key', 'item'] - ); - }); +describe('BF.ADD', () => { + it('transformArguments', () => { + assert.deepEqual( + ADD.transformArguments('key', 'item'), + ['BF.ADD', 'key', 'item'] + ); + }); - testUtils.testWithClient('client.bf.add', async client => { - assert.equal( - await client.bf.add('key', 'item'), - true - ); - }, GLOBAL.SERVERS.OPEN); + testUtils.testWithClient('client.bf.add', async client => { + assert.equal( + await client.bf.add('key', 'item'), + true + ); + }, GLOBAL.SERVERS.OPEN); }); diff --git a/packages/bloom/lib/commands/bloom/ADD.ts b/packages/bloom/lib/commands/bloom/ADD.ts index d8938f4c2b0..a9655754897 100644 --- a/packages/bloom/lib/commands/bloom/ADD.ts +++ b/packages/bloom/lib/commands/bloom/ADD.ts @@ -1,7 +1,11 @@ -export const FIRST_KEY_INDEX = 1; +import { RedisArgument, Command } from '@redis/client/dist/lib/RESP/types'; +import { transformBooleanReply } from '@redis/client/dist/lib/commands/generic-transformers'; -export function transformArguments(key: string, item: string): Array { +export default { + FIRST_KEY_INDEX: 1, + IS_READ_ONLY: false, + transformArguments(key: RedisArgument, item: RedisArgument) { return ['BF.ADD', key, item]; -} - -export { transformBooleanReply as transformReply } from '@redis/client/dist/lib/commands/generic-transformers'; + }, + transformReply: transformBooleanReply +} as const satisfies Command; diff --git a/packages/bloom/lib/commands/bloom/CARD.spec.ts b/packages/bloom/lib/commands/bloom/CARD.spec.ts index 4d5620ea196..b150f812574 100644 --- a/packages/bloom/lib/commands/bloom/CARD.spec.ts +++ b/packages/bloom/lib/commands/bloom/CARD.spec.ts @@ -1,19 +1,19 @@ -import { strict as assert } from 'assert'; +import { strict as assert } from 'node:assert'; import testUtils, { GLOBAL } from '../../test-utils'; -import { transformArguments } from './CARD'; +import CARD from './CARD'; -describe('BF CARD', () => { - it('transformArguments', () => { - assert.deepEqual( - transformArguments('bloom'), - ['BF.CARD', 'bloom'] - ); - }); +describe('BF.CARD', () => { + it('transformArguments', () => { + assert.deepEqual( + CARD.transformArguments('bloom'), + ['BF.CARD', 'bloom'] + ); + }); - testUtils.testWithClient('client.bf.card', async client => { - assert.equal( - await client.bf.card('key'), - 0 - ); - }, GLOBAL.SERVERS.OPEN); + testUtils.testWithClient('client.bf.card', async client => { + assert.equal( + await client.bf.card('key'), + 0 + ); + }, GLOBAL.SERVERS.OPEN); }); diff --git a/packages/bloom/lib/commands/bloom/CARD.ts b/packages/bloom/lib/commands/bloom/CARD.ts index 530284c3f60..ddaa76cc1f8 100644 --- a/packages/bloom/lib/commands/bloom/CARD.ts +++ b/packages/bloom/lib/commands/bloom/CARD.ts @@ -1,9 +1,10 @@ -export const FIRST_KEY_INDEX = 1; +import { RedisArgument, NumberReply, Command } from '@redis/client/dist/lib/RESP/types'; -export const IS_READ_ONLY = true; - -export function transformArguments(key: string): Array { +export default { + FIRST_KEY_INDEX: 1, + IS_READ_ONLY: true, + transformArguments(key: RedisArgument) { return ['BF.CARD', key]; -} - -export declare function transformReply(): number; + }, + transformReply: undefined as unknown as () => NumberReply +} as const satisfies Command; diff --git a/packages/bloom/lib/commands/bloom/EXISTS.spec.ts b/packages/bloom/lib/commands/bloom/EXISTS.spec.ts index 1088e739e61..7db891b92bf 100644 --- a/packages/bloom/lib/commands/bloom/EXISTS.spec.ts +++ b/packages/bloom/lib/commands/bloom/EXISTS.spec.ts @@ -1,19 +1,19 @@ -import { strict as assert } from 'assert'; +import { strict as assert } from 'node:assert'; import testUtils, { GLOBAL } from '../../test-utils'; -import { transformArguments } from './EXISTS'; +import EXISTS from './EXISTS'; -describe('BF EXISTS', () => { - it('transformArguments', () => { - assert.deepEqual( - transformArguments('key', 'item'), - ['BF.EXISTS', 'key', 'item'] - ); - }); +describe('BF.EXISTS', () => { + it('transformArguments', () => { + assert.deepEqual( + EXISTS.transformArguments('key', 'item'), + ['BF.EXISTS', 'key', 'item'] + ); + }); - testUtils.testWithClient('client.bf.exists', async client => { - assert.equal( - await client.bf.exists('key', 'item'), - false - ); - }, GLOBAL.SERVERS.OPEN); + testUtils.testWithClient('client.bf.exists', async client => { + assert.equal( + await client.bf.exists('key', 'item'), + false + ); + }, GLOBAL.SERVERS.OPEN); }); diff --git a/packages/bloom/lib/commands/bloom/EXISTS.ts b/packages/bloom/lib/commands/bloom/EXISTS.ts index d044207e244..9d28d671d61 100644 --- a/packages/bloom/lib/commands/bloom/EXISTS.ts +++ b/packages/bloom/lib/commands/bloom/EXISTS.ts @@ -1,9 +1,11 @@ -export const FIRST_KEY_INDEX = 1; +import { RedisArgument, Command } from '@redis/client/dist/lib/RESP/types'; +import { transformBooleanReply } from '@redis/client/dist/lib/commands/generic-transformers'; -export const IS_READ_ONLY = true; - -export function transformArguments(key: string, item: string): Array { +export default { + FIRST_KEY_INDEX: 1, + IS_READ_ONLY: true, + transformArguments(key: RedisArgument, item: RedisArgument) { return ['BF.EXISTS', key, item]; -} - -export { transformBooleanReply as transformReply } from '@redis/client/dist/lib/commands/generic-transformers'; + }, + transformReply: transformBooleanReply +} as const satisfies Command; diff --git a/packages/bloom/lib/commands/bloom/INFO.spec.ts b/packages/bloom/lib/commands/bloom/INFO.spec.ts index 7a5e5724c22..4a17dab8d33 100644 --- a/packages/bloom/lib/commands/bloom/INFO.spec.ts +++ b/packages/bloom/lib/commands/bloom/INFO.spec.ts @@ -1,24 +1,26 @@ -import { strict as assert } from 'assert'; +import { strict as assert } from 'node:assert'; import testUtils, { GLOBAL } from '../../test-utils'; -import { transformArguments } from './INFO'; +import INFO from './INFO'; -describe('BF INFO', () => { - it('transformArguments', () => { - assert.deepEqual( - transformArguments('bloom'), - ['BF.INFO', 'bloom'] - ); - }); +describe('BF.INFO', () => { + it('transformArguments', () => { + assert.deepEqual( + INFO.transformArguments('bloom'), + ['BF.INFO', 'bloom'] + ); + }); - testUtils.testWithClient('client.bf.info', async client => { - await client.bf.reserve('key', 0.01, 100); + testUtils.testWithClient('client.bf.info', async client => { + const [, reply] = await Promise.all([ + client.bf.reserve('key', 0.01, 100), + client.bf.info('key') + ]); - const info = await client.bf.info('key'); - assert.equal(typeof info, 'object'); - assert.equal(info.capacity, 100); - assert.equal(typeof info.size, 'number'); - assert.equal(typeof info.numberOfFilters, 'number'); - assert.equal(typeof info.numberOfInsertedItems, 'number'); - assert.equal(typeof info.expansionRate, 'number'); - }, GLOBAL.SERVERS.OPEN); + assert.equal(typeof reply, 'object'); + assert.equal(reply['Capacity'], 100); + assert.equal(typeof reply['Size'], 'number'); + assert.equal(typeof reply['Number of filters'], 'number'); + assert.equal(typeof reply['Number of items inserted'], 'number'); + assert.equal(typeof reply['Expansion rate'], 'number'); + }, GLOBAL.SERVERS.OPEN); }); diff --git a/packages/bloom/lib/commands/bloom/INFO.ts b/packages/bloom/lib/commands/bloom/INFO.ts index 52e97646404..208c999b970 100644 --- a/packages/bloom/lib/commands/bloom/INFO.ts +++ b/packages/bloom/lib/commands/bloom/INFO.ts @@ -1,38 +1,32 @@ -export const FIRST_KEY_INDEX = 1; +import { RedisArgument, Command, UnwrapReply, NullReply, NumberReply, TuplesToMapReply, Resp2Reply, SimpleStringReply, TypeMapping } from '@redis/client/dist/lib/RESP/types'; +import { transformInfoV2Reply } from '.'; -export const IS_READ_ONLY = true; +export type BfInfoReplyMap = TuplesToMapReply<[ + [SimpleStringReply<'Capacity'>, NumberReply], + [SimpleStringReply<'Size'>, NumberReply], + [SimpleStringReply<'Number of filters'>, NumberReply], + [SimpleStringReply<'Number of items inserted'>, NumberReply], + [SimpleStringReply<'Expansion rate'>, NullReply | NumberReply] +]>; -export function transformArguments(key: string): Array { - return ['BF.INFO', key]; -} - -export type InfoRawReply = [ - _: string, - capacity: number, - _: string, - size: number, - _: string, - numberOfFilters: number, - _: string, - numberOfInsertedItems: number, - _: string, - expansionRate: number, -]; - -export interface InfoReply { - capacity: number; - size: number; - numberOfFilters: number; - numberOfInsertedItems: number; - expansionRate: number; +export interface BfInfoReply { + capacity: NumberReply; + size: NumberReply; + numberOfFilters: NumberReply; + numberOfInsertedItems: NumberReply; + expansionRate: NullReply | NumberReply; } -export function transformReply(reply: InfoRawReply): InfoReply { - return { - capacity: reply[1], - size: reply[3], - numberOfFilters: reply[5], - numberOfInsertedItems: reply[7], - expansionRate: reply[9] - }; -} +export default { + FIRST_KEY_INDEX: 1, + IS_READ_ONLY: true, + transformArguments(key: RedisArgument) { + return ['BF.INFO', key]; + }, + transformReply: { + 2: (reply: UnwrapReply>, _, typeMapping?: TypeMapping): BfInfoReplyMap => { + return transformInfoV2Reply(reply, typeMapping); + }, + 3: undefined as unknown as () => BfInfoReplyMap + } +} as const satisfies Command; diff --git a/packages/bloom/lib/commands/bloom/INSERT.spec.ts b/packages/bloom/lib/commands/bloom/INSERT.spec.ts index aff9e6e282b..ccd81e070f1 100644 --- a/packages/bloom/lib/commands/bloom/INSERT.spec.ts +++ b/packages/bloom/lib/commands/bloom/INSERT.spec.ts @@ -1,69 +1,69 @@ -import { strict as assert } from 'assert'; +import { strict as assert } from 'node:assert'; import testUtils, { GLOBAL } from '../../test-utils'; -import { transformArguments } from './INSERT'; +import INSERT from './INSERT'; -describe('BF INSERT', () => { - describe('transformArguments', () => { - it('simple', () => { - assert.deepEqual( - transformArguments('key', 'item'), - ['BF.INSERT', 'key', 'ITEMS', 'item'] - ); - }); +describe('BF.INSERT', () => { + describe('transformArguments', () => { + it('simple', () => { + assert.deepEqual( + INSERT.transformArguments('key', 'item'), + ['BF.INSERT', 'key', 'ITEMS', 'item'] + ); + }); - it('with CAPACITY', () => { - assert.deepEqual( - transformArguments('key', 'item', { CAPACITY: 100 }), - ['BF.INSERT', 'key', 'CAPACITY', '100', 'ITEMS', 'item'] - ); - }); + it('with CAPACITY', () => { + assert.deepEqual( + INSERT.transformArguments('key', 'item', { CAPACITY: 100 }), + ['BF.INSERT', 'key', 'CAPACITY', '100', 'ITEMS', 'item'] + ); + }); - it('with ERROR', () => { - assert.deepEqual( - transformArguments('key', 'item', { ERROR: 0.01 }), - ['BF.INSERT', 'key', 'ERROR', '0.01', 'ITEMS', 'item'] - ); - }); + it('with ERROR', () => { + assert.deepEqual( + INSERT.transformArguments('key', 'item', { ERROR: 0.01 }), + ['BF.INSERT', 'key', 'ERROR', '0.01', 'ITEMS', 'item'] + ); + }); - it('with EXPANSION', () => { - assert.deepEqual( - transformArguments('key', 'item', { EXPANSION: 1 }), - ['BF.INSERT', 'key', 'EXPANSION', '1', 'ITEMS', 'item'] - ); - }); + it('with EXPANSION', () => { + assert.deepEqual( + INSERT.transformArguments('key', 'item', { EXPANSION: 1 }), + ['BF.INSERT', 'key', 'EXPANSION', '1', 'ITEMS', 'item'] + ); + }); - it('with NOCREATE', () => { - assert.deepEqual( - transformArguments('key', 'item', { NOCREATE: true }), - ['BF.INSERT', 'key', 'NOCREATE', 'ITEMS', 'item'] - ); - }); + it('with NOCREATE', () => { + assert.deepEqual( + INSERT.transformArguments('key', 'item', { NOCREATE: true }), + ['BF.INSERT', 'key', 'NOCREATE', 'ITEMS', 'item'] + ); + }); - it('with NONSCALING', () => { - assert.deepEqual( - transformArguments('key', 'item', { NONSCALING: true }), - ['BF.INSERT', 'key', 'NONSCALING', 'ITEMS', 'item'] - ); - }); + it('with NONSCALING', () => { + assert.deepEqual( + INSERT.transformArguments('key', 'item', { NONSCALING: true }), + ['BF.INSERT', 'key', 'NONSCALING', 'ITEMS', 'item'] + ); + }); - it('with CAPACITY, ERROR, EXPANSION, NOCREATE and NONSCALING', () => { - assert.deepEqual( - transformArguments('key', 'item', { - CAPACITY: 100, - ERROR: 0.01, - EXPANSION: 1, - NOCREATE: true, - NONSCALING: true - }), - ['BF.INSERT', 'key', 'CAPACITY', '100', 'ERROR', '0.01', 'EXPANSION', '1', 'NOCREATE', 'NONSCALING', 'ITEMS', 'item'] - ); - }); + it('with CAPACITY, ERROR, EXPANSION, NOCREATE and NONSCALING', () => { + assert.deepEqual( + INSERT.transformArguments('key', 'item', { + CAPACITY: 100, + ERROR: 0.01, + EXPANSION: 1, + NOCREATE: true, + NONSCALING: true + }), + ['BF.INSERT', 'key', 'CAPACITY', '100', 'ERROR', '0.01', 'EXPANSION', '1', 'NOCREATE', 'NONSCALING', 'ITEMS', 'item'] + ); }); + }); - testUtils.testWithClient('client.bf.insert', async client => { - assert.deepEqual( - await client.bf.insert('key', 'item'), - [true] - ); - }, GLOBAL.SERVERS.OPEN); + testUtils.testWithClient('client.bf.insert', async client => { + assert.deepEqual( + await client.bf.insert('key', 'item'), + [true] + ); + }, GLOBAL.SERVERS.OPEN); }); diff --git a/packages/bloom/lib/commands/bloom/INSERT.ts b/packages/bloom/lib/commands/bloom/INSERT.ts index f6deb7a8612..dfeaf5f20de 100644 --- a/packages/bloom/lib/commands/bloom/INSERT.ts +++ b/packages/bloom/lib/commands/bloom/INSERT.ts @@ -1,45 +1,47 @@ -import { pushVerdictArguments } from '@redis/client/dist/lib/commands/generic-transformers'; -import { RedisCommandArgument, RedisCommandArguments } from '@redis/client/dist/lib/commands'; - -export const FIRST_KEY_INDEX = 1; - -interface InsertOptions { - CAPACITY?: number; - ERROR?: number; - EXPANSION?: number; - NOCREATE?: true; - NONSCALING?: true; +import { RedisArgument, Command } from '@redis/client/dist/lib/RESP/types'; +import { RedisVariadicArgument, pushVariadicArguments } from '@redis/client/dist/lib/commands/generic-transformers'; +import { transformBooleanArrayReply } from '@redis/client/dist/lib/commands/generic-transformers'; + +export interface BfInsertOptions { + CAPACITY?: number; + ERROR?: number; + EXPANSION?: number; + NOCREATE?: boolean; + NONSCALING?: boolean; } -export function transformArguments( - key: string, - items: RedisCommandArgument | Array, - options?: InsertOptions -): RedisCommandArguments { +export default { + FIRST_KEY_INDEX: 1, + IS_READ_ONLY: false, + transformArguments( + key: RedisArgument, + items: RedisVariadicArgument, + options?: BfInsertOptions + ) { const args = ['BF.INSERT', key]; - if (options?.CAPACITY) { - args.push('CAPACITY', options.CAPACITY.toString()); + if (options?.CAPACITY !== undefined) { + args.push('CAPACITY', options.CAPACITY.toString()); } - if (options?.ERROR) { - args.push('ERROR', options.ERROR.toString()); + if (options?.ERROR !== undefined) { + args.push('ERROR', options.ERROR.toString()); } - if (options?.EXPANSION) { - args.push('EXPANSION', options.EXPANSION.toString()); + if (options?.EXPANSION !== undefined) { + args.push('EXPANSION', options.EXPANSION.toString()); } if (options?.NOCREATE) { - args.push('NOCREATE'); + args.push('NOCREATE'); } if (options?.NONSCALING) { - args.push('NONSCALING'); + args.push('NONSCALING'); } args.push('ITEMS'); - return pushVerdictArguments(args, items); -} - -export { transformBooleanArrayReply as transformReply } from '@redis/client/dist/lib/commands/generic-transformers'; + return pushVariadicArguments(args, items); + }, + transformReply: transformBooleanArrayReply +} as const satisfies Command; diff --git a/packages/bloom/lib/commands/bloom/LOADCHUNK.spec.ts b/packages/bloom/lib/commands/bloom/LOADCHUNK.spec.ts index 19634cb4a78..f958863c0dc 100644 --- a/packages/bloom/lib/commands/bloom/LOADCHUNK.spec.ts +++ b/packages/bloom/lib/commands/bloom/LOADCHUNK.spec.ts @@ -1,28 +1,35 @@ -import { strict as assert } from 'assert'; +import { strict as assert } from 'node:assert'; import testUtils, { GLOBAL } from '../../test-utils'; -import { transformArguments } from './LOADCHUNK'; +import LOADCHUNK from './LOADCHUNK'; +import { RESP_TYPES } from '@redis/client'; -describe('BF LOADCHUNK', () => { - it('transformArguments', () => { - assert.deepEqual( - transformArguments('key', 0, ''), - ['BF.LOADCHUNK', 'key', '0', ''] - ); - }); +describe('BF.LOADCHUNK', () => { + it('transformArguments', () => { + assert.deepEqual( + LOADCHUNK.transformArguments('key', 0, ''), + ['BF.LOADCHUNK', 'key', '0', ''] + ); + }); - testUtils.testWithClient('client.bf.loadChunk', async client => { - const [, { iterator, chunk }] = await Promise.all([ - client.bf.reserve('source', 0.01, 100), - client.bf.scanDump( - client.commandOptions({ returnBuffers: true }), - 'source', - 0 - ) - ]); + testUtils.testWithClient('client.bf.loadChunk', async client => { + const [, { iterator, chunk }] = await Promise.all([ + client.bf.reserve('source', 0.01, 100), + client.bf.scanDump('source', 0) + ]); - assert.equal( - await client.bf.loadChunk('destination', iterator, chunk), - 'OK' - ); - }, GLOBAL.SERVERS.OPEN); + assert.equal( + await client.bf.loadChunk('destination', iterator, chunk), + 'OK' + ); + }, { + ...GLOBAL.SERVERS.OPEN, + clientOptions: { + ...GLOBAL.SERVERS.OPEN.clientOptions, + commandOptions: { + typeMapping: { + [RESP_TYPES.BLOB_STRING]: Buffer + } + } + } + }); }); diff --git a/packages/bloom/lib/commands/bloom/LOADCHUNK.ts b/packages/bloom/lib/commands/bloom/LOADCHUNK.ts index 491f572a49e..feade2fac4c 100644 --- a/packages/bloom/lib/commands/bloom/LOADCHUNK.ts +++ b/packages/bloom/lib/commands/bloom/LOADCHUNK.ts @@ -1,13 +1,10 @@ -import { RedisCommandArgument, RedisCommandArguments } from '@redis/client/dist/lib/commands'; +import { RedisArgument, SimpleStringReply, Command } from '@redis/client/dist/lib/RESP/types'; -export const FIRST_KEY_INDEX = 1; - -export function transformArguments( - key: string, - iteretor: number, - chunk: RedisCommandArgument -): RedisCommandArguments { - return ['BF.LOADCHUNK', key, iteretor.toString(), chunk]; -} - -export declare function transformReply(): 'OK'; +export default { + FIRST_KEY_INDEX: 1, + IS_READ_ONLY: false, + transformArguments(key: RedisArgument, iterator: number, chunk: RedisArgument) { + return ['BF.LOADCHUNK', key, iterator.toString(), chunk]; + }, + transformReply: undefined as unknown as () => SimpleStringReply<'OK'> +} as const satisfies Command; diff --git a/packages/bloom/lib/commands/bloom/MADD.spec.ts b/packages/bloom/lib/commands/bloom/MADD.spec.ts index 784f99926ff..5241a09485a 100644 --- a/packages/bloom/lib/commands/bloom/MADD.spec.ts +++ b/packages/bloom/lib/commands/bloom/MADD.spec.ts @@ -1,19 +1,19 @@ -import { strict as assert } from 'assert'; +import { strict as assert } from 'node:assert'; import testUtils, { GLOBAL } from '../../test-utils'; -import { transformArguments } from './MADD'; +import MADD from './MADD'; -describe('BF MADD', () => { - it('transformArguments', () => { - assert.deepEqual( - transformArguments('key', ['1', '2']), - ['BF.MADD', 'key', '1', '2'] - ); - }); +describe('BF.MADD', () => { + it('transformArguments', () => { + assert.deepEqual( + MADD.transformArguments('key', ['1', '2']), + ['BF.MADD', 'key', '1', '2'] + ); + }); - testUtils.testWithClient('client.ts.mAdd', async client => { - assert.deepEqual( - await client.bf.mAdd('key', ['1', '2']), - [true, true] - ); - }, GLOBAL.SERVERS.OPEN); + testUtils.testWithClient('client.ts.mAdd', async client => { + assert.deepEqual( + await client.bf.mAdd('key', ['1', '2']), + [true, true] + ); + }, GLOBAL.SERVERS.OPEN); }); diff --git a/packages/bloom/lib/commands/bloom/MADD.ts b/packages/bloom/lib/commands/bloom/MADD.ts index 056c4a1c1c2..afb122476fd 100644 --- a/packages/bloom/lib/commands/bloom/MADD.ts +++ b/packages/bloom/lib/commands/bloom/MADD.ts @@ -1,7 +1,12 @@ -export const FIRST_KEY_INDEX = 1; +import { RedisArgument, Command } from '@redis/client/dist/lib/RESP/types'; +import { RedisVariadicArgument, pushVariadicArguments } from '@redis/client/dist/lib/commands/generic-transformers'; +import { transformBooleanArrayReply } from '@redis/client/dist/lib/commands/generic-transformers'; -export function transformArguments(key: string, items: Array): Array { - return ['BF.MADD', key, ...items]; -} - -export { transformBooleanArrayReply as transformReply } from '@redis/client/dist/lib/commands/generic-transformers'; +export default { + FIRST_KEY_INDEX: 1, + IS_READ_ONLY: false, + transformArguments(key: RedisArgument, items: RedisVariadicArgument) { + return pushVariadicArguments(['BF.MADD', key], items); + }, + transformReply: transformBooleanArrayReply +} as const satisfies Command; diff --git a/packages/bloom/lib/commands/bloom/MEXISTS.spec.ts b/packages/bloom/lib/commands/bloom/MEXISTS.spec.ts index 027e51d2c43..0f313ba636f 100644 --- a/packages/bloom/lib/commands/bloom/MEXISTS.spec.ts +++ b/packages/bloom/lib/commands/bloom/MEXISTS.spec.ts @@ -1,19 +1,19 @@ -import { strict as assert } from 'assert'; +import { strict as assert } from 'node:assert'; import testUtils, { GLOBAL } from '../../test-utils'; -import { transformArguments } from './MEXISTS'; +import MEXISTS from './MEXISTS'; -describe('BF MEXISTS', () => { - it('transformArguments', () => { - assert.deepEqual( - transformArguments('key', ['1', '2']), - ['BF.MEXISTS', 'key', '1', '2'] - ); - }); +describe('BF.MEXISTS', () => { + it('transformArguments', () => { + assert.deepEqual( + MEXISTS.transformArguments('key', ['1', '2']), + ['BF.MEXISTS', 'key', '1', '2'] + ); + }); - testUtils.testWithClient('client.bf.mExists', async client => { - assert.deepEqual( - await client.bf.mExists('key', ['1', '2']), - [false, false] - ); - }, GLOBAL.SERVERS.OPEN); + testUtils.testWithClient('client.bf.mExists', async client => { + assert.deepEqual( + await client.bf.mExists('key', ['1', '2']), + [false, false] + ); + }, GLOBAL.SERVERS.OPEN); }); diff --git a/packages/bloom/lib/commands/bloom/MEXISTS.ts b/packages/bloom/lib/commands/bloom/MEXISTS.ts index fb79410155d..a23b713b50c 100644 --- a/packages/bloom/lib/commands/bloom/MEXISTS.ts +++ b/packages/bloom/lib/commands/bloom/MEXISTS.ts @@ -1,9 +1,12 @@ -export const FIRST_KEY_INDEX = 1; +import { RedisArgument, Command } from '@redis/client/dist/lib/RESP/types'; +import { RedisVariadicArgument, pushVariadicArguments } from '@redis/client/dist/lib/commands/generic-transformers'; +import { transformBooleanArrayReply } from '@redis/client/dist/lib/commands/generic-transformers'; -export const IS_READ_ONLY = true; - -export function transformArguments(key: string, items: Array): Array { - return ['BF.MEXISTS', key, ...items]; -} - -export { transformBooleanArrayReply as transformReply } from '@redis/client/dist/lib/commands/generic-transformers'; +export default { + FIRST_KEY_INDEX: 1, + IS_READ_ONLY: true, + transformArguments(key: RedisArgument, items: RedisVariadicArgument) { + return pushVariadicArguments(['BF.MEXISTS', key], items); + }, + transformReply: transformBooleanArrayReply +} as const satisfies Command; diff --git a/packages/bloom/lib/commands/bloom/RESERVE.spec.ts b/packages/bloom/lib/commands/bloom/RESERVE.spec.ts index bc872f9c3f7..caf40d4a48f 100644 --- a/packages/bloom/lib/commands/bloom/RESERVE.spec.ts +++ b/packages/bloom/lib/commands/bloom/RESERVE.spec.ts @@ -1,49 +1,49 @@ -import { strict as assert } from 'assert'; +import { strict as assert } from 'node:assert'; import testUtils, { GLOBAL } from '../../test-utils'; -import { transformArguments } from './RESERVE'; +import RESERVE from './RESERVE'; -describe('BF RESERVE', () => { - describe('transformArguments', () => { - it('simple', () => { - assert.deepEqual( - transformArguments('key', 0.01, 100), - ['BF.RESERVE', 'key', '0.01', '100'] - ); - }); +describe('BF.RESERVE', () => { + describe('transformArguments', () => { + it('simple', () => { + assert.deepEqual( + RESERVE.transformArguments('key', 0.01, 100), + ['BF.RESERVE', 'key', '0.01', '100'] + ); + }); - it('with EXPANSION', () => { - assert.deepEqual( - transformArguments('key', 0.01, 100, { - EXPANSION: 1 - }), - ['BF.RESERVE', 'key', '0.01', '100', 'EXPANSION', '1'] - ); - }); + it('with EXPANSION', () => { + assert.deepEqual( + RESERVE.transformArguments('key', 0.01, 100, { + EXPANSION: 1 + }), + ['BF.RESERVE', 'key', '0.01', '100', 'EXPANSION', '1'] + ); + }); - it('with NONSCALING', () => { - assert.deepEqual( - transformArguments('key', 0.01, 100, { - NONSCALING: true - }), - ['BF.RESERVE', 'key', '0.01', '100', 'NONSCALING'] - ); - }); + it('with NONSCALING', () => { + assert.deepEqual( + RESERVE.transformArguments('key', 0.01, 100, { + NONSCALING: true + }), + ['BF.RESERVE', 'key', '0.01', '100', 'NONSCALING'] + ); + }); - it('with EXPANSION and NONSCALING', () => { - assert.deepEqual( - transformArguments('key', 0.01, 100, { - EXPANSION: 1, - NONSCALING: true - }), - ['BF.RESERVE', 'key', '0.01', '100', 'EXPANSION', '1', 'NONSCALING'] - ); - }); + it('with EXPANSION and NONSCALING', () => { + assert.deepEqual( + RESERVE.transformArguments('key', 0.01, 100, { + EXPANSION: 1, + NONSCALING: true + }), + ['BF.RESERVE', 'key', '0.01', '100', 'EXPANSION', '1', 'NONSCALING'] + ); }); + }); - testUtils.testWithClient('client.bf.reserve', async client => { - assert.equal( - await client.bf.reserve('bloom', 0.01, 100), - 'OK' - ); - }, GLOBAL.SERVERS.OPEN); + testUtils.testWithClient('client.bf.reserve', async client => { + assert.equal( + await client.bf.reserve('bloom', 0.01, 100), + 'OK' + ); + }, GLOBAL.SERVERS.OPEN); }); diff --git a/packages/bloom/lib/commands/bloom/RESERVE.ts b/packages/bloom/lib/commands/bloom/RESERVE.ts index 18d7002f158..6bccb1d1d13 100644 --- a/packages/bloom/lib/commands/bloom/RESERVE.ts +++ b/packages/bloom/lib/commands/bloom/RESERVE.ts @@ -1,16 +1,19 @@ -export const FIRST_KEY_INDEX = 1; +import { RedisArgument, SimpleStringReply, Command } from '@redis/client/dist/lib/RESP/types'; -interface ReserveOptions { - EXPANSION?: number; - NONSCALING?: true; +export interface BfReserveOptions { + EXPANSION?: number; + NONSCALING?: boolean; } -export function transformArguments( - key: string, +export default { + FIRST_KEY_INDEX: 1, + IS_READ_ONLY: true, + transformArguments( + key: RedisArgument, errorRate: number, capacity: number, - options?: ReserveOptions -): Array { + options?: BfReserveOptions + ) { const args = ['BF.RESERVE', key, errorRate.toString(), capacity.toString()]; if (options?.EXPANSION) { @@ -22,6 +25,6 @@ export function transformArguments( } return args; -} - -export declare function transformReply(): 'OK'; + }, + transformReply: undefined as unknown as () => SimpleStringReply<'OK'> +} as const satisfies Command; diff --git a/packages/bloom/lib/commands/bloom/SCANDUMP.spec.ts b/packages/bloom/lib/commands/bloom/SCANDUMP.spec.ts index 50119590482..a7de98eabe7 100644 --- a/packages/bloom/lib/commands/bloom/SCANDUMP.spec.ts +++ b/packages/bloom/lib/commands/bloom/SCANDUMP.spec.ts @@ -1,22 +1,22 @@ -import { strict as assert } from 'assert'; +import { strict as assert } from 'node:assert'; import testUtils, { GLOBAL } from '../../test-utils'; -import { transformArguments } from './SCANDUMP'; +import SCANDUMP from './SCANDUMP'; -describe('BF SCANDUMP', () => { - it('transformArguments', () => { - assert.deepEqual( - transformArguments('key', 0), - ['BF.SCANDUMP', 'key', '0'] - ); - }); +describe('BF.SCANDUMP', () => { + it('transformArguments', () => { + assert.deepEqual( + SCANDUMP.transformArguments('key', 0), + ['BF.SCANDUMP', 'key', '0'] + ); + }); - testUtils.testWithClient('client.bf.scanDump', async client => { - const [, dump] = await Promise.all([ - client.bf.reserve('key', 0.01, 100), - client.bf.scanDump('key', 0) - ]); - assert.equal(typeof dump, 'object'); - assert.equal(typeof dump.iterator, 'number'); - assert.equal(typeof dump.chunk, 'string'); - }, GLOBAL.SERVERS.OPEN); + testUtils.testWithClient('client.bf.scanDump', async client => { + const [, dump] = await Promise.all([ + client.bf.reserve('key', 0.01, 100), + client.bf.scanDump('key', 0) + ]); + assert.equal(typeof dump, 'object'); + assert.equal(typeof dump.iterator, 'number'); + assert.equal(typeof dump.chunk, 'string'); + }, GLOBAL.SERVERS.OPEN); }); diff --git a/packages/bloom/lib/commands/bloom/SCANDUMP.ts b/packages/bloom/lib/commands/bloom/SCANDUMP.ts index 04b3edc2a1f..588957b1743 100644 --- a/packages/bloom/lib/commands/bloom/SCANDUMP.ts +++ b/packages/bloom/lib/commands/bloom/SCANDUMP.ts @@ -1,24 +1,15 @@ -export const FIRST_KEY_INDEX = 1; +import { RedisArgument, TuplesReply, NumberReply, BlobStringReply, UnwrapReply, Command } from '@redis/client/dist/lib/RESP/types'; -export const IS_READ_ONLY = true; - -export function transformArguments(key: string, iterator: number): Array { +export default { + FIRST_KEY_INDEX: 1, + IS_READ_ONLY: true, + transformArguments(key: RedisArgument, iterator: number) { return ['BF.SCANDUMP', key, iterator.toString()]; -} - -type ScanDumpRawReply = [ - iterator: number, - chunk: string -]; - -interface ScanDumpReply { - iterator: number; - chunk: string; -} - -export function transformReply([iterator, chunk]: ScanDumpRawReply): ScanDumpReply { + }, + transformReply(reply: UnwrapReply>) { return { - iterator, - chunk + iterator: reply[0], + chunk: reply[1] }; -} + } +} as const satisfies Command; diff --git a/packages/bloom/lib/commands/bloom/index.ts b/packages/bloom/lib/commands/bloom/index.ts index f18b8f71095..a93f79c9c56 100644 --- a/packages/bloom/lib/commands/bloom/index.ts +++ b/packages/bloom/lib/commands/bloom/index.ts @@ -1,33 +1,64 @@ -import * as ADD from './ADD'; -import * as CARD from './CARD'; -import * as EXISTS from './EXISTS'; -import * as INFO from './INFO'; -import * as INSERT from './INSERT'; -import * as LOADCHUNK from './LOADCHUNK'; -import * as MADD from './MADD'; -import * as MEXISTS from './MEXISTS'; -import * as RESERVE from './RESERVE'; -import * as SCANDUMP from './SCANDUMP'; +import type { RedisCommands, TypeMapping } from '@redis/client/dist/lib/RESP/types'; + +import ADD from './ADD'; +import CARD from './CARD'; +import EXISTS from './EXISTS'; +import INFO from './INFO'; +import INSERT from './INSERT'; +import LOADCHUNK from './LOADCHUNK'; +import MADD from './MADD'; +import MEXISTS from './MEXISTS'; +import RESERVE from './RESERVE'; +import SCANDUMP from './SCANDUMP'; +import { RESP_TYPES } from '@redis/client'; export default { - ADD, - add: ADD, - CARD, - card: CARD, - EXISTS, - exists: EXISTS, - INFO, - info: INFO, - INSERT, - insert: INSERT, - LOADCHUNK, - loadChunk: LOADCHUNK, - MADD, - mAdd: MADD, - MEXISTS, - mExists: MEXISTS, - RESERVE, - reserve: RESERVE, - SCANDUMP, - scanDump: SCANDUMP -}; + ADD, + add: ADD, + CARD, + card: CARD, + EXISTS, + exists: EXISTS, + INFO, + info: INFO, + INSERT, + insert: INSERT, + LOADCHUNK, + loadChunk: LOADCHUNK, + MADD, + mAdd: MADD, + MEXISTS, + mExists: MEXISTS, + RESERVE, + reserve: RESERVE, + SCANDUMP, + scanDump: SCANDUMP +} as const satisfies RedisCommands; + +export function transformInfoV2Reply(reply: Array, typeMapping?: TypeMapping): T { + const mapType = typeMapping ? typeMapping[RESP_TYPES.MAP] : undefined; + + switch (mapType) { + case Array: { + return reply as unknown as T; + } + case Map: { + const ret = new Map(); + + for (let i = 0; i < reply.length; i += 2) { + ret.set(reply[i].toString(), reply[i + 1]); + } + + return ret as unknown as T; + } + default: { + const ret = Object.create(null); + + for (let i = 0; i < reply.length; i += 2) { + ret[reply[i].toString()] = reply[i + 1]; + } + + return ret as unknown as T; + } + } +} \ No newline at end of file diff --git a/packages/bloom/lib/commands/count-min-sketch/INCRBY.spec.ts b/packages/bloom/lib/commands/count-min-sketch/INCRBY.spec.ts index 95bb28e88b5..1d2921cab75 100644 --- a/packages/bloom/lib/commands/count-min-sketch/INCRBY.spec.ts +++ b/packages/bloom/lib/commands/count-min-sketch/INCRBY.spec.ts @@ -1,41 +1,42 @@ -import { strict as assert } from 'assert'; +import { strict as assert } from 'node:assert'; import testUtils, { GLOBAL } from '../../test-utils'; -import { transformArguments } from './INCRBY'; +import INCRBY from './INCRBY'; -describe('CMS INCRBY', () => { - describe('transformArguments', () => { - it('single item', () => { - assert.deepEqual( - transformArguments('key', { - item: 'item', - incrementBy: 1 - }), - ['CMS.INCRBY', 'key', 'item', '1'] - ); - }); +describe('CMS.INCRBY', () => { + describe('transformArguments', () => { + it('single item', () => { + assert.deepEqual( + INCRBY.transformArguments('key', { + item: 'item', + incrementBy: 1 + }), + ['CMS.INCRBY', 'key', 'item', '1'] + ); + }); - it('multiple items', () => { - assert.deepEqual( - transformArguments('key', [{ - item: 'a', - incrementBy: 1 - }, { - item: 'b', - incrementBy: 2 - }]), - ['CMS.INCRBY', 'key', 'a', '1', 'b', '2'] - ); - }); + it('multiple items', () => { + assert.deepEqual( + INCRBY.transformArguments('key', [{ + item: 'a', + incrementBy: 1 + }, { + item: 'b', + incrementBy: 2 + }]), + ['CMS.INCRBY', 'key', 'a', '1', 'b', '2'] + ); }); + }); + + testUtils.testWithClient('client.cms.incrBy', async client => { + const [, reply] = await Promise.all([ + client.cms.initByDim('key', 1000, 5), + client.cms.incrBy('key', { + item: 'item', + incrementBy: 1 + }) + ]); - testUtils.testWithClient('client.cms.incrBy', async client => { - await client.cms.initByDim('key', 1000, 5); - assert.deepEqual( - await client.cms.incrBy('key', { - item: 'item', - incrementBy: 1 - }), - [1] - ); - }, GLOBAL.SERVERS.OPEN); + assert.deepEqual(reply, [1]); + }, GLOBAL.SERVERS.OPEN); }); diff --git a/packages/bloom/lib/commands/count-min-sketch/INCRBY.ts b/packages/bloom/lib/commands/count-min-sketch/INCRBY.ts index e27fb397cdf..1dfbabbaa49 100644 --- a/packages/bloom/lib/commands/count-min-sketch/INCRBY.ts +++ b/packages/bloom/lib/commands/count-min-sketch/INCRBY.ts @@ -1,29 +1,32 @@ -export const FIRST_KEY_INDEX = 1; +import { RedisArgument, ArrayReply, NumberReply, Command } from '@redis/client/dist/lib/RESP/types'; -interface IncrByItem { - item: string; - incrementBy: number; +export interface BfIncrByItem { + item: RedisArgument; + incrementBy: number; } -export function transformArguments( - key: string, - items: IncrByItem | Array -): Array { +export default { + FIRST_KEY_INDEX: 1, + IS_READ_ONLY: false, + transformArguments( + key: RedisArgument, + items: BfIncrByItem | Array + ) { const args = ['CMS.INCRBY', key]; if (Array.isArray(items)) { - for (const item of items) { - pushIncrByItem(args, item); - } + for (const item of items) { + pushIncrByItem(args, item); + } } else { - pushIncrByItem(args, items); + pushIncrByItem(args, items); } return args; -} + }, + transformReply: undefined as unknown as () => ArrayReply +} as const satisfies Command; -function pushIncrByItem(args: Array, { item, incrementBy }: IncrByItem): void { - args.push(item, incrementBy.toString()); +function pushIncrByItem(args: Array, { item, incrementBy }: BfIncrByItem): void { + args.push(item, incrementBy.toString()); } - -export declare function transformReply(): Array; diff --git a/packages/bloom/lib/commands/count-min-sketch/INFO.spec.ts b/packages/bloom/lib/commands/count-min-sketch/INFO.spec.ts index 0db8a48447e..e650d78d2ed 100644 --- a/packages/bloom/lib/commands/count-min-sketch/INFO.spec.ts +++ b/packages/bloom/lib/commands/count-min-sketch/INFO.spec.ts @@ -1,25 +1,28 @@ -import { strict as assert } from 'assert'; +import { strict as assert } from 'node:assert'; import testUtils, { GLOBAL } from '../../test-utils'; -import { transformArguments } from './INFO'; +import INFO from './INFO'; -describe('CMS INFO', () => { - it('transformArguments', () => { - assert.deepEqual( - transformArguments('key'), - ['CMS.INFO', 'key'] - ); - }); +describe('CMS.INFO', () => { + it('transformArguments', () => { + assert.deepEqual( + INFO.transformArguments('key'), + ['CMS.INFO', 'key'] + ); + }); - testUtils.testWithClient('client.cms.info', async client => { - await client.cms.initByDim('key', 1000, 5); + testUtils.testWithClient('client.cms.info', async client => { + const width = 1000, + depth = 5, + [, reply] = await Promise.all([ + client.cms.initByDim('key', width, depth), + client.cms.info('key') + ]); - assert.deepEqual( - await client.cms.info('key'), - { - width: 1000, - depth: 5, - count: 0 - } - ); - }, GLOBAL.SERVERS.OPEN); + const expected = Object.create(null); + expected['width'] = width; + expected['depth'] = depth; + expected['count'] = 0; + + assert.deepEqual(reply, expected); + }, GLOBAL.SERVERS.OPEN); }); diff --git a/packages/bloom/lib/commands/count-min-sketch/INFO.ts b/packages/bloom/lib/commands/count-min-sketch/INFO.ts index 6dbfffcb0e0..e4aae5bf47b 100644 --- a/packages/bloom/lib/commands/count-min-sketch/INFO.ts +++ b/packages/bloom/lib/commands/count-min-sketch/INFO.ts @@ -1,30 +1,28 @@ -export const FIRST_KEY_INDEX = 1; +import { RedisArgument, TuplesToMapReply, NumberReply, UnwrapReply, Resp2Reply, Command, SimpleStringReply, TypeMapping } from '@redis/client/dist/lib/RESP/types'; +import { transformInfoV2Reply } from '../bloom'; -export const IS_READ_ONLY = true; +export type CmsInfoReplyMap = TuplesToMapReply<[ + [SimpleStringReply<'width'>, NumberReply], + [SimpleStringReply<'depth'>, NumberReply], + [SimpleStringReply<'count'>, NumberReply] +]>; -export function transformArguments(key: string): Array { - return ['CMS.INFO', key]; -} - -export type InfoRawReply = [ - _: string, - width: number, - _: string, - depth: number, - _: string, - count: number -]; - -export interface InfoReply { - width: number; - depth: number; - count: number; -} - -export function transformReply(reply: InfoRawReply): InfoReply { - return { - width: reply[1], - depth: reply[3], - count: reply[5] - }; +export interface CmsInfoReply { + width: NumberReply; + depth: NumberReply; + count: NumberReply; } + +export default { + FIRST_KEY_INDEX: 1, + IS_READ_ONLY: true, + transformArguments(key: RedisArgument) { + return ['CMS.INFO', key]; + }, + transformReply: { + 2: (reply: UnwrapReply>, _, typeMapping?: TypeMapping): CmsInfoReply => { + return transformInfoV2Reply(reply, typeMapping); + }, + 3: undefined as unknown as () => CmsInfoReply + } +} as const satisfies Command; diff --git a/packages/bloom/lib/commands/count-min-sketch/INITBYDIM.spec.ts b/packages/bloom/lib/commands/count-min-sketch/INITBYDIM.spec.ts index 2a9014b765a..a3d27c17df3 100644 --- a/packages/bloom/lib/commands/count-min-sketch/INITBYDIM.spec.ts +++ b/packages/bloom/lib/commands/count-min-sketch/INITBYDIM.spec.ts @@ -1,19 +1,19 @@ -import { strict as assert } from 'assert'; +import { strict as assert } from 'node:assert'; import testUtils, { GLOBAL } from '../../test-utils'; -import { transformArguments } from './INITBYDIM'; +import INITBYDIM from './INITBYDIM'; -describe('CMS INITBYDIM', () => { - it('transformArguments', () => { - assert.deepEqual( - transformArguments('key', 1000, 5), - ['CMS.INITBYDIM', 'key', '1000', '5'] - ); - }); +describe('CMS.INITBYDIM', () => { + it('transformArguments', () => { + assert.deepEqual( + INITBYDIM.transformArguments('key', 1000, 5), + ['CMS.INITBYDIM', 'key', '1000', '5'] + ); + }); - testUtils.testWithClient('client.cms.initByDim', async client => { - assert.equal( - await client.cms.initByDim('key', 1000, 5), - 'OK' - ); - }, GLOBAL.SERVERS.OPEN); + testUtils.testWithClient('client.cms.initByDim', async client => { + assert.equal( + await client.cms.initByDim('key', 1000, 5), + 'OK' + ); + }, GLOBAL.SERVERS.OPEN); }); diff --git a/packages/bloom/lib/commands/count-min-sketch/INITBYDIM.ts b/packages/bloom/lib/commands/count-min-sketch/INITBYDIM.ts index 4ec6cedd9ea..60790d421e4 100644 --- a/packages/bloom/lib/commands/count-min-sketch/INITBYDIM.ts +++ b/packages/bloom/lib/commands/count-min-sketch/INITBYDIM.ts @@ -1,7 +1,10 @@ -export const FIRST_KEY_INDEX = 1; +import { RedisArgument, SimpleStringReply, Command } from '@redis/client/dist/lib/RESP/types'; -export function transformArguments(key: string, width: number, depth: number): Array { +export default { + FIRST_KEY_INDEX: 1, + IS_READ_ONLY: false, + transformArguments(key: RedisArgument, width: number, depth: number) { return ['CMS.INITBYDIM', key, width.toString(), depth.toString()]; -} - -export declare function transformReply(): 'OK'; + }, + transformReply: undefined as unknown as () => SimpleStringReply<'OK'> +} as const satisfies Command; diff --git a/packages/bloom/lib/commands/count-min-sketch/INITBYPROB.spec.ts b/packages/bloom/lib/commands/count-min-sketch/INITBYPROB.spec.ts index 004d3df14ef..8df62020e89 100644 --- a/packages/bloom/lib/commands/count-min-sketch/INITBYPROB.spec.ts +++ b/packages/bloom/lib/commands/count-min-sketch/INITBYPROB.spec.ts @@ -1,19 +1,19 @@ -import { strict as assert } from 'assert'; +import { strict as assert } from 'node:assert'; import testUtils, { GLOBAL } from '../../test-utils'; -import { transformArguments } from './INITBYPROB'; +import INITBYPROB from './INITBYPROB'; -describe('CMS INITBYPROB', () => { - it('transformArguments', () => { - assert.deepEqual( - transformArguments('key', 0.001, 0.01), - ['CMS.INITBYPROB', 'key', '0.001', '0.01'] - ); - }); +describe('CMS.INITBYPROB', () => { + it('transformArguments', () => { + assert.deepEqual( + INITBYPROB.transformArguments('key', 0.001, 0.01), + ['CMS.INITBYPROB', 'key', '0.001', '0.01'] + ); + }); - testUtils.testWithClient('client.cms.initByProb', async client => { - assert.equal( - await client.cms.initByProb('key', 0.001, 0.01), - 'OK' - ); - }, GLOBAL.SERVERS.OPEN); + testUtils.testWithClient('client.cms.initByProb', async client => { + assert.equal( + await client.cms.initByProb('key', 0.001, 0.01), + 'OK' + ); + }, GLOBAL.SERVERS.OPEN); }); diff --git a/packages/bloom/lib/commands/count-min-sketch/INITBYPROB.ts b/packages/bloom/lib/commands/count-min-sketch/INITBYPROB.ts index 7f0256515fb..7b21755f17d 100644 --- a/packages/bloom/lib/commands/count-min-sketch/INITBYPROB.ts +++ b/packages/bloom/lib/commands/count-min-sketch/INITBYPROB.ts @@ -1,7 +1,10 @@ -export const FIRST_KEY_INDEX = 1; +import { RedisArgument, SimpleStringReply, Command } from '@redis/client/dist/lib/RESP/types'; -export function transformArguments(key: string, error: number, probability: number): Array { +export default { + FIRST_KEY_INDEX: 1, + IS_READ_ONLY: false, + transformArguments(key: RedisArgument, error: number, probability: number) { return ['CMS.INITBYPROB', key, error.toString(), probability.toString()]; -} - -export declare function transformReply(): 'OK'; + }, + transformReply: undefined as unknown as () => SimpleStringReply<'OK'> +} as const satisfies Command; diff --git a/packages/bloom/lib/commands/count-min-sketch/MERGE.spec.ts b/packages/bloom/lib/commands/count-min-sketch/MERGE.spec.ts index cf234e5734f..eef4bd403ae 100644 --- a/packages/bloom/lib/commands/count-min-sketch/MERGE.spec.ts +++ b/packages/bloom/lib/commands/count-min-sketch/MERGE.spec.ts @@ -1,36 +1,34 @@ -import { strict as assert } from 'assert'; +import { strict as assert } from 'node:assert'; import testUtils, { GLOBAL } from '../../test-utils'; -import { transformArguments } from './MERGE'; +import MERGE from './MERGE'; -describe('CMS MERGE', () => { - describe('transformArguments', () => { - it('without WEIGHTS', () => { - assert.deepEqual( - transformArguments('dest', ['src']), - ['CMS.MERGE', 'dest', '1', 'src'] - ); - }); +describe('CMS.MERGE', () => { + describe('transformArguments', () => { + it('without WEIGHTS', () => { + assert.deepEqual( + MERGE.transformArguments('destination', ['source']), + ['CMS.MERGE', 'destination', '1', 'source'] + ); + }); - it('with WEIGHTS', () => { - assert.deepEqual( - transformArguments('dest', [{ - name: 'src', - weight: 1 - }]), - ['CMS.MERGE', 'dest', '1', 'src', 'WEIGHTS', '1'] - ); - }); + it('with WEIGHTS', () => { + assert.deepEqual( + MERGE.transformArguments('destination', [{ + name: 'source', + weight: 1 + }]), + ['CMS.MERGE', 'destination', '1', 'source', 'WEIGHTS', '1'] + ); }); + }); - testUtils.testWithClient('client.cms.merge', async client => { - await Promise.all([ - client.cms.initByDim('src', 1000, 5), - client.cms.initByDim('dest', 1000, 5), - ]); + testUtils.testWithClient('client.cms.merge', async client => { + const [, , reply] = await Promise.all([ + client.cms.initByDim('source', 1000, 5), + client.cms.initByDim('destination', 1000, 5), + client.cms.merge('destination', ['source']) + ]); - assert.equal( - await client.cms.merge('dest', ['src']), - 'OK' - ); - }, GLOBAL.SERVERS.OPEN); + assert.equal(reply, 'OK'); + }, GLOBAL.SERVERS.OPEN); }); diff --git a/packages/bloom/lib/commands/count-min-sketch/MERGE.ts b/packages/bloom/lib/commands/count-min-sketch/MERGE.ts index 6cca4e797cd..2e63065d1cc 100644 --- a/packages/bloom/lib/commands/count-min-sketch/MERGE.ts +++ b/packages/bloom/lib/commands/count-min-sketch/MERGE.ts @@ -1,37 +1,37 @@ -export const FIRST_KEY_INDEX = 1; +import { RedisArgument, SimpleStringReply, Command } from '@redis/client/dist/lib/RESP/types'; -interface Sketch { - name: string; - weight: number; +interface BfMergeSketch { + name: RedisArgument; + weight: number; } -type Sketches = Array | Array; +export type BfMergeSketches = Array | Array; -export function transformArguments(dest: string, src: Sketches): Array { - const args = [ - 'CMS.MERGE', - dest, - src.length.toString() - ]; +export default { + FIRST_KEY_INDEX: 1, + IS_READ_ONLY: false, + transformArguments( + destination: RedisArgument, + source: BfMergeSketches + ) { + let args = ['CMS.MERGE', destination, source.length.toString()]; - if (isStringSketches(src)) { - args.push(...src); + if (isPlainSketches(source)) { + args = args.concat(source); } else { - for (const sketch of src) { - args.push(sketch.name); - } - - args.push('WEIGHTS'); - for (const sketch of src) { - args.push(sketch.weight.toString()); - } + const { length } = args; + args[length + source.length] = 'WEIGHTS'; + for (let i = 0; i < source.length; i++) { + args[length + i] = source[i].name; + args[length + source.length + i + 1] = source[i].weight.toString(); + } } return args; -} + }, + transformReply: undefined as unknown as () => SimpleStringReply<'OK'> +} as const satisfies Command; -function isStringSketches(src: Sketches): src is Array { - return typeof src[0] === 'string'; +function isPlainSketches(src: BfMergeSketches): src is Array { + return typeof src[0] === 'string' || src[0] instanceof Buffer; } - -export declare function transformReply(): 'OK'; diff --git a/packages/bloom/lib/commands/count-min-sketch/QUERY.spec.ts b/packages/bloom/lib/commands/count-min-sketch/QUERY.spec.ts index d391ab838be..cc9c913b563 100644 --- a/packages/bloom/lib/commands/count-min-sketch/QUERY.spec.ts +++ b/packages/bloom/lib/commands/count-min-sketch/QUERY.spec.ts @@ -1,22 +1,21 @@ -import { strict as assert } from 'assert'; +import { strict as assert } from 'node:assert'; import testUtils, { GLOBAL } from '../../test-utils'; -import { transformArguments } from './QUERY'; +import QUERY from './QUERY'; -describe('CMS QUERY', () => { - it('transformArguments', () => { - assert.deepEqual( - transformArguments('key', 'item'), - ['CMS.QUERY', 'key', 'item'] - ); - }); +describe('CMS.QUERY', () => { + it('transformArguments', () => { + assert.deepEqual( + QUERY.transformArguments('key', 'item'), + ['CMS.QUERY', 'key', 'item'] + ); + }); - testUtils.testWithClient('client.cms.query', async client => { - await client.cms.initByDim('key', 1000, 5); + testUtils.testWithClient('client.cms.query', async client => { + const [, reply] = await Promise.all([ + client.cms.initByDim('key', 1000, 5), + client.cms.query('key', 'item') + ]); - assert.deepEqual( - await client.cms.query('key', 'item'), - [0] - ); - - }, GLOBAL.SERVERS.OPEN); + assert.deepEqual(reply, [0]); + }, GLOBAL.SERVERS.OPEN); }); diff --git a/packages/bloom/lib/commands/count-min-sketch/QUERY.ts b/packages/bloom/lib/commands/count-min-sketch/QUERY.ts index 13a71c9b1de..5d2905300b1 100644 --- a/packages/bloom/lib/commands/count-min-sketch/QUERY.ts +++ b/packages/bloom/lib/commands/count-min-sketch/QUERY.ts @@ -1,15 +1,11 @@ -import { RedisCommandArguments } from '@redis/client/dist/lib/commands'; -import { pushVerdictArguments } from '@redis/client/dist/lib/commands/generic-transformers'; - -export const FIRST_KEY_INDEX = 1; - -export const IS_READ_ONLY = true; - -export function transformArguments( - key: string, - items: string | Array -): RedisCommandArguments { - return pushVerdictArguments(['CMS.QUERY', key], items); -} - -export declare function transformReply(): Array; +import { ArrayReply, NumberReply, Command, RedisArgument } from '@redis/client/dist/lib/RESP/types'; +import { RedisVariadicArgument, pushVariadicArguments } from '@redis/client/dist/lib/commands/generic-transformers'; + +export default { + FIRST_KEY_INDEX: 1, + IS_READ_ONLY: true, + transformArguments(key: RedisArgument, items: RedisVariadicArgument) { + return pushVariadicArguments(['CMS.QUERY', key], items); + }, + transformReply: undefined as unknown as () => ArrayReply +} as const satisfies Command; diff --git a/packages/bloom/lib/commands/count-min-sketch/index.ts b/packages/bloom/lib/commands/count-min-sketch/index.ts index 1d61734a8d0..4f0f395ca3d 100644 --- a/packages/bloom/lib/commands/count-min-sketch/index.ts +++ b/packages/bloom/lib/commands/count-min-sketch/index.ts @@ -1,21 +1,22 @@ -import * as INCRBY from './INCRBY'; -import * as INFO from './INFO'; -import * as INITBYDIM from './INITBYDIM'; -import * as INITBYPROB from './INITBYPROB'; -import * as MERGE from './MERGE'; -import * as QUERY from './QUERY'; +import type { RedisCommands } from '@redis/client/dist/lib/RESP/types'; +import INCRBY from './INCRBY'; +import INFO from './INFO'; +import INITBYDIM from './INITBYDIM'; +import INITBYPROB from './INITBYPROB'; +import MERGE from './MERGE'; +import QUERY from './QUERY'; export default { - INCRBY, - incrBy: INCRBY, - INFO, - info: INFO, - INITBYDIM, - initByDim: INITBYDIM, - INITBYPROB, - initByProb: INITBYPROB, - MERGE, - merge: MERGE, - QUERY, - query: QUERY -}; + INCRBY, + incrBy: INCRBY, + INFO, + info: INFO, + INITBYDIM, + initByDim: INITBYDIM, + INITBYPROB, + initByProb: INITBYPROB, + MERGE, + merge: MERGE, + QUERY, + query: QUERY +} as const satisfies RedisCommands; diff --git a/packages/bloom/lib/commands/cuckoo/ADD.spec.ts b/packages/bloom/lib/commands/cuckoo/ADD.spec.ts index f2c029fad3d..fa610cc6666 100644 --- a/packages/bloom/lib/commands/cuckoo/ADD.spec.ts +++ b/packages/bloom/lib/commands/cuckoo/ADD.spec.ts @@ -1,19 +1,19 @@ -import { strict as assert } from 'assert'; +import { strict as assert } from 'node:assert'; import testUtils, { GLOBAL } from '../../test-utils'; -import { transformArguments, transformReply } from './ADD'; +import ADD from './ADD'; -describe('CF ADD', () => { - it('transformArguments', () => { - assert.deepEqual( - transformArguments('key', 'item'), - ['CF.ADD', 'key', 'item'] - ); - }); +describe('CF.ADD', () => { + it('transformArguments', () => { + assert.deepEqual( + ADD.transformArguments('key', 'item'), + ['CF.ADD', 'key', 'item'] + ); + }); - testUtils.testWithClient('client.cf.add', async client => { - assert.equal( - await client.cf.add('key', 'item'), - true - ); - }, GLOBAL.SERVERS.OPEN); + testUtils.testWithClient('client.cf.add', async client => { + assert.equal( + await client.cf.add('key', 'item'), + true + ); + }, GLOBAL.SERVERS.OPEN); }); diff --git a/packages/bloom/lib/commands/cuckoo/ADD.ts b/packages/bloom/lib/commands/cuckoo/ADD.ts index 8d16c0f2ed0..52e98a801d4 100644 --- a/packages/bloom/lib/commands/cuckoo/ADD.ts +++ b/packages/bloom/lib/commands/cuckoo/ADD.ts @@ -1,7 +1,11 @@ -export const FIRST_KEY_INDEX = 1; +import { RedisArgument, Command } from '@redis/client/dist/lib/RESP/types'; +import { transformBooleanReply } from '@redis/client/dist/lib/commands/generic-transformers'; -export function transformArguments(key: string, item: string): Array { +export default { + FIRST_KEY_INDEX: 1, + IS_READ_ONLY: false, + transformArguments(key: RedisArgument, item: RedisArgument) { return ['CF.ADD', key, item]; -} - -export { transformBooleanReply as transformReply } from '@redis/client/dist/lib/commands/generic-transformers'; + }, + transformReply: transformBooleanReply +} as const satisfies Command; diff --git a/packages/bloom/lib/commands/cuckoo/ADDNX.spec.ts b/packages/bloom/lib/commands/cuckoo/ADDNX.spec.ts index ddd9f922b13..f50ad87dc15 100644 --- a/packages/bloom/lib/commands/cuckoo/ADDNX.spec.ts +++ b/packages/bloom/lib/commands/cuckoo/ADDNX.spec.ts @@ -1,21 +1,19 @@ -import { strict as assert } from 'assert'; +import { strict as assert } from 'node:assert'; import testUtils, { GLOBAL } from '../../test-utils'; -import { transformArguments } from './ADDNX'; +import ADDNX from './ADDNX'; -describe('CF ADDNX', () => { - describe('transformArguments', () => { - it('basic add', () => { - assert.deepEqual( - transformArguments('key', 'item'), - ['CF.ADDNX', 'key', 'item'] - ); - }); - }); +describe('CF.ADDNX', () => { + it('transformArguments', () => { + assert.deepEqual( + ADDNX.transformArguments('key', 'item'), + ['CF.ADDNX', 'key', 'item'] + ); + }); - testUtils.testWithClient('client.cf.add', async client => { - assert.equal( - await client.cf.addNX('key', 'item'), - true - ); - }, GLOBAL.SERVERS.OPEN); + testUtils.testWithClient('client.cf.add', async client => { + assert.equal( + await client.cf.addNX('key', 'item'), + true + ); + }, GLOBAL.SERVERS.OPEN); }); diff --git a/packages/bloom/lib/commands/cuckoo/ADDNX.ts b/packages/bloom/lib/commands/cuckoo/ADDNX.ts index 789003a3a57..c739077ee46 100644 --- a/packages/bloom/lib/commands/cuckoo/ADDNX.ts +++ b/packages/bloom/lib/commands/cuckoo/ADDNX.ts @@ -1,7 +1,11 @@ -export const FIRST_KEY_INDEX = 1; +import { RedisArgument, Command } from '@redis/client/dist/lib/RESP/types'; +import { transformBooleanReply } from '@redis/client/dist/lib/commands/generic-transformers'; -export function transformArguments(key: string, item: string): Array { +export default { + FIRST_KEY_INDEX: 1, + IS_READ_ONLY: false, + transformArguments(key: RedisArgument, item: RedisArgument) { return ['CF.ADDNX', key, item]; -} - -export { transformBooleanReply as transformReply } from '@redis/client/dist/lib/commands/generic-transformers'; + }, + transformReply: transformBooleanReply +} as const satisfies Command; diff --git a/packages/bloom/lib/commands/cuckoo/COUNT.spec.ts b/packages/bloom/lib/commands/cuckoo/COUNT.spec.ts index 29f5b415935..ff8d40f064e 100644 --- a/packages/bloom/lib/commands/cuckoo/COUNT.spec.ts +++ b/packages/bloom/lib/commands/cuckoo/COUNT.spec.ts @@ -1,19 +1,19 @@ -import { strict as assert } from 'assert'; +import { strict as assert } from 'node:assert'; import testUtils, { GLOBAL } from '../../test-utils'; -import { transformArguments } from './COUNT'; +import COUNT from './COUNT'; -describe('CF COUNT', () => { - it('transformArguments', () => { - assert.deepEqual( - transformArguments('key', 'item'), - ['CF.COUNT', 'key', 'item'] - ); - }); +describe('CF.COUNT', () => { + it('transformArguments', () => { + assert.deepEqual( + COUNT.transformArguments('key', 'item'), + ['CF.COUNT', 'key', 'item'] + ); + }); - testUtils.testWithClient('client.cf.count', async client => { - assert.equal( - await client.cf.count('key', 'item'), - 0 - ); - }, GLOBAL.SERVERS.OPEN); + testUtils.testWithClient('client.cf.count', async client => { + assert.equal( + await client.cf.count('key', 'item'), + 0 + ); + }, GLOBAL.SERVERS.OPEN); }); diff --git a/packages/bloom/lib/commands/cuckoo/COUNT.ts b/packages/bloom/lib/commands/cuckoo/COUNT.ts index c9f3e28b38a..2284edff174 100644 --- a/packages/bloom/lib/commands/cuckoo/COUNT.ts +++ b/packages/bloom/lib/commands/cuckoo/COUNT.ts @@ -1,7 +1,10 @@ -export const FIRST_KEY_INDEX = 1; +import { RedisArgument, NumberReply, Command } from '@redis/client/dist/lib/RESP/types'; -export function transformArguments(key: string, item: string): Array { +export default { + FIRST_KEY_INDEX: 1, + IS_READ_ONLY: true, + transformArguments(key: RedisArgument, item: RedisArgument) { return ['CF.COUNT', key, item]; -} - -export declare function transformReply(): number; + }, + transformReply: undefined as unknown as () => NumberReply +} as const satisfies Command; diff --git a/packages/bloom/lib/commands/cuckoo/DEL.spec.ts b/packages/bloom/lib/commands/cuckoo/DEL.spec.ts index 03da65881c1..e02b5636e12 100644 --- a/packages/bloom/lib/commands/cuckoo/DEL.spec.ts +++ b/packages/bloom/lib/commands/cuckoo/DEL.spec.ts @@ -1,21 +1,21 @@ -import { strict as assert } from 'assert'; +import { strict as assert } from 'node:assert'; import testUtils, { GLOBAL } from '../../test-utils'; -import { transformArguments } from './DEL'; +import DEL from './DEL'; -describe('CF DEL', () => { - it('transformArguments', () => { - assert.deepEqual( - transformArguments('key', 'item'), - ['CF.DEL', 'key', 'item'] - ); - }); +describe('CF.DEL', () => { + it('transformArguments', () => { + assert.deepEqual( + DEL.transformArguments('key', 'item'), + ['CF.DEL', 'key', 'item'] + ); + }); - testUtils.testWithClient('client.cf.del', async client => { - await client.cf.reserve('key', 4); + testUtils.testWithClient('client.cf.del', async client => { + const [, reply] = await Promise.all([ + client.cf.reserve('key', 4), + client.cf.del('key', 'item') + ]); - assert.equal( - await client.cf.del('key', 'item'), - false - ); - }, GLOBAL.SERVERS.OPEN); + assert.equal(reply, false); + }, GLOBAL.SERVERS.OPEN); }); diff --git a/packages/bloom/lib/commands/cuckoo/DEL.ts b/packages/bloom/lib/commands/cuckoo/DEL.ts index 1c395a515a8..0af8ebc851b 100644 --- a/packages/bloom/lib/commands/cuckoo/DEL.ts +++ b/packages/bloom/lib/commands/cuckoo/DEL.ts @@ -1,7 +1,11 @@ -export const FIRST_KEY_INDEX = 1; +import { RedisArgument, Command } from '@redis/client/dist/lib/RESP/types'; +import { transformBooleanReply } from '@redis/client/dist/lib/commands/generic-transformers'; -export function transformArguments(key: string, item: string): Array { +export default { + FIRST_KEY_INDEX: 1, + IS_READ_ONLY: false, + transformArguments(key: RedisArgument, item: RedisArgument) { return ['CF.DEL', key, item]; -} - -export { transformBooleanReply as transformReply } from '@redis/client/dist/lib/commands/generic-transformers'; + }, + transformReply: transformBooleanReply +} as const satisfies Command; diff --git a/packages/bloom/lib/commands/cuckoo/EXISTS.spec.ts b/packages/bloom/lib/commands/cuckoo/EXISTS.spec.ts index e281bde6d8a..899c11e8394 100644 --- a/packages/bloom/lib/commands/cuckoo/EXISTS.spec.ts +++ b/packages/bloom/lib/commands/cuckoo/EXISTS.spec.ts @@ -1,19 +1,19 @@ -import { strict as assert } from 'assert'; +import { strict as assert } from 'node:assert'; import testUtils, { GLOBAL } from '../../test-utils'; -import { transformArguments } from './EXISTS'; +import EXISTS from './EXISTS'; -describe('CF EXISTS', () => { - it('transformArguments', () => { - assert.deepEqual( - transformArguments('key', 'item'), - ['CF.EXISTS', 'key', 'item'] - ); - }); +describe('CF.EXISTS', () => { + it('transformArguments', () => { + assert.deepEqual( + EXISTS.transformArguments('key', 'item'), + ['CF.EXISTS', 'key', 'item'] + ); + }); - testUtils.testWithClient('client.cf.exists', async client => { - assert.equal( - await client.cf.exists('key', 'item'), - false - ); - }, GLOBAL.SERVERS.OPEN); + testUtils.testWithClient('client.cf.exists', async client => { + assert.equal( + await client.cf.exists('key', 'item'), + false + ); + }, GLOBAL.SERVERS.OPEN); }); diff --git a/packages/bloom/lib/commands/cuckoo/EXISTS.ts b/packages/bloom/lib/commands/cuckoo/EXISTS.ts index b50a1e25a87..8fd74ca47ca 100644 --- a/packages/bloom/lib/commands/cuckoo/EXISTS.ts +++ b/packages/bloom/lib/commands/cuckoo/EXISTS.ts @@ -1,9 +1,11 @@ -export const FIRST_KEY_INDEX = 1; +import { RedisArgument, Command } from '@redis/client/dist/lib/RESP/types'; +import { transformBooleanReply } from '@redis/client/dist/lib/commands/generic-transformers'; -export const IS_READ_ONLY = true; - -export function transformArguments(key: string, item: string): Array { +export default { + FIRST_KEY_INDEX: 1, + IS_READ_ONLY: false, + transformArguments(key: RedisArgument, item: RedisArgument) { return ['CF.EXISTS', key, item]; -} - -export { transformBooleanReply as transformReply } from '@redis/client/dist/lib/commands/generic-transformers'; + }, + transformReply: transformBooleanReply +} as const satisfies Command; diff --git a/packages/bloom/lib/commands/cuckoo/INFO.spec.ts b/packages/bloom/lib/commands/cuckoo/INFO.spec.ts index c2ac5de6fe0..222177c4650 100644 --- a/packages/bloom/lib/commands/cuckoo/INFO.spec.ts +++ b/packages/bloom/lib/commands/cuckoo/INFO.spec.ts @@ -1,27 +1,29 @@ -import { strict as assert } from 'assert'; +import { strict as assert } from 'node:assert'; import testUtils, { GLOBAL } from '../../test-utils'; -import { transformArguments } from './INFO'; +import INFO from './INFO'; -describe('CF INFO', () => { - it('transformArguments', () => { - assert.deepEqual( - transformArguments('cuckoo'), - ['CF.INFO', 'cuckoo'] - ); - }); +describe('CF.INFO', () => { + it('transformArguments', () => { + assert.deepEqual( + INFO.transformArguments('cuckoo'), + ['CF.INFO', 'cuckoo'] + ); + }); - testUtils.testWithClient('client.cf.info', async client => { - await client.cf.reserve('key', 4); + testUtils.testWithClient('client.cf.info', async client => { + const [, reply] = await Promise.all([ + client.cf.reserve('key', 4), + client.cf.info('key') + ]); - const info = await client.cf.info('key'); - assert.equal(typeof info, 'object'); - assert.equal(typeof info.size, 'number'); - assert.equal(typeof info.numberOfBuckets, 'number'); - assert.equal(typeof info.numberOfFilters, 'number'); - assert.equal(typeof info.numberOfInsertedItems, 'number'); - assert.equal(typeof info.numberOfDeletedItems, 'number'); - assert.equal(typeof info.bucketSize, 'number'); - assert.equal(typeof info.expansionRate, 'number'); - assert.equal(typeof info.maxIteration, 'number'); - }, GLOBAL.SERVERS.OPEN); + assert.equal(typeof reply, 'object'); + assert.equal(typeof reply['Size'], 'number'); + assert.equal(typeof reply['Number of buckets'], 'number'); + assert.equal(typeof reply['Number of filters'], 'number'); + assert.equal(typeof reply['Number of items inserted'], 'number'); + assert.equal(typeof reply['Number of items deleted'], 'number'); + assert.equal(typeof reply['Bucket size'], 'number'); + assert.equal(typeof reply['Expansion rate'], 'number'); + assert.equal(typeof reply['Max iterations'], 'number'); + }, GLOBAL.SERVERS.OPEN); }); diff --git a/packages/bloom/lib/commands/cuckoo/INFO.ts b/packages/bloom/lib/commands/cuckoo/INFO.ts index 04d6954e37a..70a7d80c6f2 100644 --- a/packages/bloom/lib/commands/cuckoo/INFO.ts +++ b/packages/bloom/lib/commands/cuckoo/INFO.ts @@ -1,50 +1,27 @@ -export const FIRST_KEY_INDEX = 1; +import { RedisArgument, Command, NumberReply, TuplesToMapReply, UnwrapReply, Resp2Reply, SimpleStringReply, TypeMapping } from '@redis/client/dist/lib/RESP/types'; +import { transformInfoV2Reply } from '../bloom'; -export const IS_READ_ONLY = true; +export type CfInfoReplyMap = TuplesToMapReply<[ + [SimpleStringReply<'Size'>, NumberReply], + [SimpleStringReply<'Number of buckets'>, NumberReply], + [SimpleStringReply<'Number of filters'>, NumberReply], + [SimpleStringReply<'Number of items inserted'>, NumberReply], + [SimpleStringReply<'Number of items deleted'>, NumberReply], + [SimpleStringReply<'Bucket size'>, NumberReply], + [SimpleStringReply<'Expansion rate'>, NumberReply], + [SimpleStringReply<'Max iterations'>, NumberReply] +]>; -export function transformArguments(key: string): Array { +export default { + FIRST_KEY_INDEX: 1, + IS_READ_ONLY: true, + transformArguments(key: RedisArgument) { return ['CF.INFO', key]; -} - -export type InfoRawReply = [ - _: string, - size: number, - _: string, - numberOfBuckets: number, - _: string, - numberOfFilters: number, - _: string, - numberOfInsertedItems: number, - _: string, - numberOfDeletedItems: number, - _: string, - bucketSize: number, - _: string, - expansionRate: number, - _: string, - maxIteration: number -]; - -export interface InfoReply { - size: number; - numberOfBuckets: number; - numberOfFilters: number; - numberOfInsertedItems: number; - numberOfDeletedItems: number; - bucketSize: number; - expansionRate: number; - maxIteration: number; -} - -export function transformReply(reply: InfoRawReply): InfoReply { - return { - size: reply[1], - numberOfBuckets: reply[3], - numberOfFilters: reply[5], - numberOfInsertedItems: reply[7], - numberOfDeletedItems: reply[9], - bucketSize: reply[11], - expansionRate: reply[13], - maxIteration: reply[15] - }; -} + }, + transformReply: { + 2: (reply: UnwrapReply>, _, typeMapping?: TypeMapping): CfInfoReplyMap => { + return transformInfoV2Reply(reply, typeMapping); + }, + 3: undefined as unknown as () => CfInfoReplyMap + } +} as const satisfies Command; diff --git a/packages/bloom/lib/commands/cuckoo/INSERT.spec.ts b/packages/bloom/lib/commands/cuckoo/INSERT.spec.ts index 9b56b86a6b7..096cf547098 100644 --- a/packages/bloom/lib/commands/cuckoo/INSERT.spec.ts +++ b/packages/bloom/lib/commands/cuckoo/INSERT.spec.ts @@ -1,22 +1,22 @@ -import { strict as assert } from 'assert'; +import { strict as assert } from 'node:assert'; import testUtils, { GLOBAL } from '../../test-utils'; -import { transformArguments } from './INSERT'; +import INSERT from './INSERT'; -describe('CF INSERT', () => { - it('transformArguments', () => { - assert.deepEqual( - transformArguments('key', 'item', { - CAPACITY: 100, - NOCREATE: true - }), - ['CF.INSERT', 'key', 'CAPACITY', '100', 'NOCREATE', 'ITEMS', 'item'] - ); - }); +describe('CF.INSERT', () => { + it('transformArguments', () => { + assert.deepEqual( + INSERT.transformArguments('key', 'item', { + CAPACITY: 100, + NOCREATE: true + }), + ['CF.INSERT', 'key', 'CAPACITY', '100', 'NOCREATE', 'ITEMS', 'item'] + ); + }); - testUtils.testWithClient('client.cf.insert', async client => { - assert.deepEqual( - await client.cf.insert('key', 'item'), - [true] - ); - }, GLOBAL.SERVERS.OPEN); + testUtils.testWithClient('client.cf.insert', async client => { + assert.deepEqual( + await client.cf.insert('key', 'item'), + [true] + ); + }, GLOBAL.SERVERS.OPEN); }); diff --git a/packages/bloom/lib/commands/cuckoo/INSERT.ts b/packages/bloom/lib/commands/cuckoo/INSERT.ts index bcfd4f13a88..d6df64eea1a 100644 --- a/packages/bloom/lib/commands/cuckoo/INSERT.ts +++ b/packages/bloom/lib/commands/cuckoo/INSERT.ts @@ -1,18 +1,34 @@ -import { RedisCommandArguments } from '@redis/client/dist/lib/commands'; -import { InsertOptions, pushInsertOptions } from "."; +import { Command, RedisArgument } from '@redis/client/dist/lib/RESP/types'; +import { RedisVariadicArgument, pushVariadicArguments, transformBooleanArrayReply } from '@redis/client/dist/lib/commands/generic-transformers'; -export const FIRST_KEY_INDEX = 1; +export interface CfInsertOptions { + CAPACITY?: number; + NOCREATE?: boolean; +} + +export function transofrmCfInsertArguments( + command: RedisArgument, + key: RedisArgument, + items: RedisVariadicArgument, + options?: CfInsertOptions +) { + const args = [command, key]; + + if (options?.CAPACITY !== undefined) { + args.push('CAPACITY', options.CAPACITY.toString()); + } + + if (options?.NOCREATE) { + args.push('NOCREATE'); + } -export function transformArguments( - key: string, - items: string | Array, - options?: InsertOptions -): RedisCommandArguments { - return pushInsertOptions( - ['CF.INSERT', key], - items, - options - ); + args.push('ITEMS'); + return pushVariadicArguments(args, items); } -export { transformBooleanArrayReply as transformReply } from '@redis/client/dist/lib/commands/generic-transformers'; +export default { + FIRST_KEY_INDEX: 1, + IS_READ_ONLY: false, + transformArguments: transofrmCfInsertArguments.bind(undefined, 'CF.INSERT'), + transformReply: transformBooleanArrayReply +} as const satisfies Command; diff --git a/packages/bloom/lib/commands/cuckoo/INSERTNX.spec.ts b/packages/bloom/lib/commands/cuckoo/INSERTNX.spec.ts index 7b1d974e5a6..0f874278220 100644 --- a/packages/bloom/lib/commands/cuckoo/INSERTNX.spec.ts +++ b/packages/bloom/lib/commands/cuckoo/INSERTNX.spec.ts @@ -1,22 +1,22 @@ -import { strict as assert } from 'assert'; +import { strict as assert } from 'node:assert'; import testUtils, { GLOBAL } from '../../test-utils'; -import { transformArguments } from './INSERTNX'; +import INSERTNX from './INSERTNX'; -describe('CF INSERTNX', () => { - it('transformArguments', () => { - assert.deepEqual( - transformArguments('key', 'item', { - CAPACITY: 100, - NOCREATE: true - }), - ['CF.INSERTNX', 'key', 'CAPACITY', '100', 'NOCREATE', 'ITEMS', 'item'] - ); - }); +describe('CF.INSERTNX', () => { + it('transformArguments', () => { + assert.deepEqual( + INSERTNX.transformArguments('key', 'item', { + CAPACITY: 100, + NOCREATE: true + }), + ['CF.INSERTNX', 'key', 'CAPACITY', '100', 'NOCREATE', 'ITEMS', 'item'] + ); + }); - testUtils.testWithClient('client.cf.insertnx', async client => { - assert.deepEqual( - await client.cf.insertNX('key', 'item'), - [true] - ); - }, GLOBAL.SERVERS.OPEN); + testUtils.testWithClient('client.cf.insertnx', async client => { + assert.deepEqual( + await client.cf.insertNX('key', 'item'), + [true] + ); + }, GLOBAL.SERVERS.OPEN); }); diff --git a/packages/bloom/lib/commands/cuckoo/INSERTNX.ts b/packages/bloom/lib/commands/cuckoo/INSERTNX.ts index 17009e35a42..5cd56e794f9 100644 --- a/packages/bloom/lib/commands/cuckoo/INSERTNX.ts +++ b/packages/bloom/lib/commands/cuckoo/INSERTNX.ts @@ -1,18 +1,9 @@ -import { RedisCommandArguments } from '@redis/client/dist/lib/commands'; -import { InsertOptions, pushInsertOptions } from "."; +import { Command } from '@redis/client/dist/lib/RESP/types'; +import INSERT, { transofrmCfInsertArguments } from './INSERT'; -export const FIRST_KEY_INDEX = 1; - -export function transformArguments( - key: string, - items: string | Array, - options?: InsertOptions -): RedisCommandArguments { - return pushInsertOptions( - ['CF.INSERTNX', key], - items, - options - ); -} - -export { transformBooleanArrayReply as transformReply } from '@redis/client/dist/lib/commands/generic-transformers'; +export default { + FIRST_KEY_INDEX: INSERT.FIRST_KEY_INDEX, + IS_READ_ONLY: INSERT.IS_READ_ONLY, + transformArguments: transofrmCfInsertArguments.bind(undefined, 'CF.INSERTNX'), + transformReply: INSERT.transformReply +} as const satisfies Command; diff --git a/packages/bloom/lib/commands/cuckoo/LOADCHUNK.spec.ts b/packages/bloom/lib/commands/cuckoo/LOADCHUNK.spec.ts index ca3d6f2f8f7..5b880e0dd9d 100644 --- a/packages/bloom/lib/commands/cuckoo/LOADCHUNK.spec.ts +++ b/packages/bloom/lib/commands/cuckoo/LOADCHUNK.spec.ts @@ -1,31 +1,36 @@ -import { strict as assert } from 'assert'; +import { strict as assert } from 'node:assert'; import testUtils, { GLOBAL } from '../../test-utils'; -import { transformArguments } from './LOADCHUNK'; +import LOADCHUNK from './LOADCHUNK'; +import { RESP_TYPES } from '@redis/client'; -describe('CF LOADCHUNK', () => { - it('transformArguments', () => { - assert.deepEqual( - transformArguments('item', 0, ''), - ['CF.LOADCHUNK', 'item', '0', ''] - ); - }); +describe('CF.LOADCHUNK', () => { + it('transformArguments', () => { + assert.deepEqual( + LOADCHUNK.transformArguments('item', 0, ''), + ['CF.LOADCHUNK', 'item', '0', ''] + ); + }); - testUtils.testWithClient('client.cf.loadChunk', async client => { - const [,, { iterator, chunk }] = await Promise.all([ - client.cf.reserve('source', 4), - client.cf.add('source', 'item'), - client.cf.scanDump( - client.commandOptions({ returnBuffers: true }), - 'source', - 0 - ) - ]); + testUtils.testWithClient('client.cf.loadChunk', async client => { + const [, , { iterator, chunk }] = await Promise.all([ + client.cf.reserve('source', 4), + client.cf.add('source', 'item'), + client.cf.scanDump('source', 0) + ]); - assert.ok(Buffer.isBuffer(chunk)); - - assert.equal( - await client.cf.loadChunk('destination', iterator, chunk), - 'OK' - ); - }, GLOBAL.SERVERS.OPEN); + assert.equal( + await client.cf.loadChunk('destination', iterator, chunk!), + 'OK' + ); + }, { + ...GLOBAL.SERVERS.OPEN, + clientOptions: { + ...GLOBAL.SERVERS.OPEN.clientOptions, + commandOptions: { + typeMapping: { + [RESP_TYPES.BLOB_STRING]: Buffer + } + } + } + }); }); diff --git a/packages/bloom/lib/commands/cuckoo/LOADCHUNK.ts b/packages/bloom/lib/commands/cuckoo/LOADCHUNK.ts index 6d960c014e2..08cb749b595 100644 --- a/packages/bloom/lib/commands/cuckoo/LOADCHUNK.ts +++ b/packages/bloom/lib/commands/cuckoo/LOADCHUNK.ts @@ -1,13 +1,10 @@ -import { RedisCommandArgument, RedisCommandArguments } from '@redis/client/dist/lib/commands'; +import { SimpleStringReply, Command, RedisArgument } from '@redis/client/dist/lib/RESP/types'; -export const FIRST_KEY_INDEX = 1; - -export function transformArguments( - key: string, - iterator: number, - chunk: RedisCommandArgument -): RedisCommandArguments { +export default { + FIRST_KEY_INDEX: 1, + IS_READ_ONLY: false, + transformArguments(key: RedisArgument, iterator: number, chunk: RedisArgument) { return ['CF.LOADCHUNK', key, iterator.toString(), chunk]; -} - -export declare function transformReply(): 'OK'; + }, + transformReply: undefined as unknown as () => SimpleStringReply<'OK'> +} as const satisfies Command; diff --git a/packages/bloom/lib/commands/cuckoo/RESERVE.spec.ts b/packages/bloom/lib/commands/cuckoo/RESERVE.spec.ts index 3145a222c5e..b8f2556bc4f 100644 --- a/packages/bloom/lib/commands/cuckoo/RESERVE.spec.ts +++ b/packages/bloom/lib/commands/cuckoo/RESERVE.spec.ts @@ -1,48 +1,48 @@ -import { strict as assert } from 'assert'; +import { strict as assert } from 'node:assert'; import testUtils, { GLOBAL } from '../../test-utils'; -import { transformArguments } from './RESERVE'; +import RESERVE from './RESERVE'; -describe('CF RESERVE', () => { - describe('transformArguments', () => { - it('simple', () => { - assert.deepEqual( - transformArguments('key', 4), - ['CF.RESERVE', 'key', '4'] - ); - }); +describe('CF.RESERVE', () => { + describe('transformArguments', () => { + it('simple', () => { + assert.deepEqual( + RESERVE.transformArguments('key', 4), + ['CF.RESERVE', 'key', '4'] + ); + }); - it('with EXPANSION', () => { - assert.deepEqual( - transformArguments('key', 4, { - EXPANSION: 1 - }), - ['CF.RESERVE', 'key', '4', 'EXPANSION', '1'] - ); - }); + it('with EXPANSION', () => { + assert.deepEqual( + RESERVE.transformArguments('key', 4, { + EXPANSION: 1 + }), + ['CF.RESERVE', 'key', '4', 'EXPANSION', '1'] + ); + }); - it('with BUCKETSIZE', () => { - assert.deepEqual( - transformArguments('key', 4, { - BUCKETSIZE: 2 - }), - ['CF.RESERVE', 'key', '4', 'BUCKETSIZE', '2'] - ); - }); + it('with BUCKETSIZE', () => { + assert.deepEqual( + RESERVE.transformArguments('key', 4, { + BUCKETSIZE: 2 + }), + ['CF.RESERVE', 'key', '4', 'BUCKETSIZE', '2'] + ); + }); - it('with MAXITERATIONS', () => { - assert.deepEqual( - transformArguments('key', 4, { - MAXITERATIONS: 1 - }), - ['CF.RESERVE', 'key', '4', 'MAXITERATIONS', '1'] - ); - }); + it('with MAXITERATIONS', () => { + assert.deepEqual( + RESERVE.transformArguments('key', 4, { + MAXITERATIONS: 1 + }), + ['CF.RESERVE', 'key', '4', 'MAXITERATIONS', '1'] + ); }); + }); - testUtils.testWithClient('client.cf.reserve', async client => { - assert.equal( - await client.cf.reserve('key', 4), - 'OK' - ); - }, GLOBAL.SERVERS.OPEN); + testUtils.testWithClient('client.cf.reserve', async client => { + assert.equal( + await client.cf.reserve('key', 4), + 'OK' + ); + }, GLOBAL.SERVERS.OPEN); }); diff --git a/packages/bloom/lib/commands/cuckoo/RESERVE.ts b/packages/bloom/lib/commands/cuckoo/RESERVE.ts index 114c1fdf441..bc80daa0087 100644 --- a/packages/bloom/lib/commands/cuckoo/RESERVE.ts +++ b/packages/bloom/lib/commands/cuckoo/RESERVE.ts @@ -1,31 +1,34 @@ -export const FIRST_KEY_INDEX = 1; +import { RedisArgument, SimpleStringReply, Command } from '@redis/client/dist/lib/RESP/types'; -interface ReserveOptions { - BUCKETSIZE?: number; - MAXITERATIONS?: number; - EXPANSION?: number; +export interface CfReserveOptions { + BUCKETSIZE?: number; + MAXITERATIONS?: number; + EXPANSION?: number; } -export function transformArguments( - key: string, +export default { + FIRST_KEY_INDEX: 1, + IS_READ_ONLY: false, + transformArguments( + key: RedisArgument, capacity: number, - options?: ReserveOptions -): Array { + options?: CfReserveOptions + ) { const args = ['CF.RESERVE', key, capacity.toString()]; - if (options?.BUCKETSIZE) { - args.push('BUCKETSIZE', options.BUCKETSIZE.toString()); + if (options?.BUCKETSIZE !== undefined) { + args.push('BUCKETSIZE', options.BUCKETSIZE.toString()); } - if (options?.MAXITERATIONS) { - args.push('MAXITERATIONS', options.MAXITERATIONS.toString()); + if (options?.MAXITERATIONS !== undefined) { + args.push('MAXITERATIONS', options.MAXITERATIONS.toString()); } - if (options?.EXPANSION) { - args.push('EXPANSION', options.EXPANSION.toString()); + if (options?.EXPANSION !== undefined) { + args.push('EXPANSION', options.EXPANSION.toString()); } return args; -} - -export declare function transformReply(): 'OK'; + }, + transformReply: undefined as unknown as () => SimpleStringReply<'OK'> +} as const satisfies Command; diff --git a/packages/bloom/lib/commands/cuckoo/SCANDUMP.spec.ts b/packages/bloom/lib/commands/cuckoo/SCANDUMP.spec.ts index ec269c62aa5..e1bac59d323 100644 --- a/packages/bloom/lib/commands/cuckoo/SCANDUMP.spec.ts +++ b/packages/bloom/lib/commands/cuckoo/SCANDUMP.spec.ts @@ -1,23 +1,24 @@ -import { strict as assert } from 'assert'; +import { strict as assert } from 'node:assert'; import testUtils, { GLOBAL } from '../../test-utils'; -import { transformArguments } from './SCANDUMP'; +import SCANDUMP from './SCANDUMP'; -describe('CF SCANDUMP', () => { - it('transformArguments', () => { - assert.deepEqual( - transformArguments('key', 0), - ['CF.SCANDUMP', 'key', '0'] - ); - }); +describe('CF.SCANDUMP', () => { + it('transformArguments', () => { + assert.deepEqual( + SCANDUMP.transformArguments('key', 0), + ['CF.SCANDUMP', 'key', '0'] + ); + }); + + testUtils.testWithClient('client.cf.scanDump', async client => { + const [, reply] = await Promise.all([ + client.cf.reserve('key', 4), + client.cf.scanDump('key', 0) + ]); - testUtils.testWithClient('client.cf.scanDump', async client => { - await client.cf.reserve('key', 4); - assert.deepEqual( - await client.cf.scanDump('key', 0), - { - iterator: 0, - chunk: null - } - ); - }, GLOBAL.SERVERS.OPEN); + assert.deepEqual(reply, { + iterator: 0, + chunk: null + }); + }, GLOBAL.SERVERS.OPEN); }); diff --git a/packages/bloom/lib/commands/cuckoo/SCANDUMP.ts b/packages/bloom/lib/commands/cuckoo/SCANDUMP.ts index 91476b49a7a..dc076689288 100644 --- a/packages/bloom/lib/commands/cuckoo/SCANDUMP.ts +++ b/packages/bloom/lib/commands/cuckoo/SCANDUMP.ts @@ -1,22 +1,15 @@ -export const FIRST_KEY_INDEX = 1; +import { RedisArgument, TuplesReply, NumberReply, BlobStringReply, NullReply, UnwrapReply, Command } from '@redis/client/dist/lib/RESP/types'; -export function transformArguments(key: string, iterator: number): Array { +export default { + FIRST_KEY_INDEX: 1, + IS_READ_ONLY: true, + transformArguments(key: RedisArgument, iterator: number) { return ['CF.SCANDUMP', key, iterator.toString()]; -} - -type ScanDumpRawReply = [ - iterator: number, - chunk: string | null -]; - -interface ScanDumpReply { - iterator: number; - chunk: string | null; -} - -export function transformReply([iterator, chunk]: ScanDumpRawReply): ScanDumpReply { + }, + transformReply(reply: UnwrapReply>) { return { - iterator, - chunk + iterator: reply[0], + chunk: reply[1] }; -} + } +} as const satisfies Command; diff --git a/packages/bloom/lib/commands/cuckoo/index.spec.ts b/packages/bloom/lib/commands/cuckoo/index.spec.ts deleted file mode 100644 index 94f3a0ae281..00000000000 --- a/packages/bloom/lib/commands/cuckoo/index.spec.ts +++ /dev/null @@ -1,48 +0,0 @@ -import { strict as assert } from 'assert'; -import { pushInsertOptions } from '.'; - -describe('pushInsertOptions', () => { - describe('single item', () => { - it('single item', () => { - assert.deepEqual( - pushInsertOptions([], 'item'), - ['ITEMS', 'item'] - ); - }); - - it('multiple items', () => { - assert.deepEqual( - pushInsertOptions([], ['1', '2']), - ['ITEMS', '1', '2'] - ); - }); - }); - - it('with CAPACITY', () => { - assert.deepEqual( - pushInsertOptions([], 'item', { - CAPACITY: 100 - }), - ['CAPACITY', '100', 'ITEMS', 'item'] - ); - }); - - it('with NOCREATE', () => { - assert.deepEqual( - pushInsertOptions([], 'item', { - NOCREATE: true - }), - ['NOCREATE', 'ITEMS', 'item'] - ); - }); - - it('with CAPACITY and NOCREATE', () => { - assert.deepEqual( - pushInsertOptions([], 'item', { - CAPACITY: 100, - NOCREATE: true - }), - ['CAPACITY', '100', 'NOCREATE', 'ITEMS', 'item'] - ); - }); -}); diff --git a/packages/bloom/lib/commands/cuckoo/index.ts b/packages/bloom/lib/commands/cuckoo/index.ts index 96b4453bc39..62c63fe8d19 100644 --- a/packages/bloom/lib/commands/cuckoo/index.ts +++ b/packages/bloom/lib/commands/cuckoo/index.ts @@ -1,62 +1,37 @@ - -import * as ADD from './ADD'; -import * as ADDNX from './ADDNX'; -import * as COUNT from './COUNT'; -import * as DEL from './DEL'; -import * as EXISTS from './EXISTS'; -import * as INFO from './INFO'; -import * as INSERT from './INSERT'; -import * as INSERTNX from './INSERTNX'; -import * as LOADCHUNK from './LOADCHUNK'; -import * as RESERVE from './RESERVE'; -import * as SCANDUMP from './SCANDUMP'; -import { pushVerdictArguments } from '@redis/client/dist/lib/commands/generic-transformers'; -import { RedisCommandArguments } from '@redis/client/dist/lib/commands'; +import type { RedisCommands } from '@redis/client/dist/lib/RESP/types'; +import ADD from './ADD'; +import ADDNX from './ADDNX'; +import COUNT from './COUNT'; +import DEL from './DEL'; +import EXISTS from './EXISTS'; +import INFO from './INFO'; +import INSERT from './INSERT'; +import INSERTNX from './INSERTNX'; +import LOADCHUNK from './LOADCHUNK'; +import RESERVE from './RESERVE'; +import SCANDUMP from './SCANDUMP'; export default { - ADD, - add: ADD, - ADDNX, - addNX: ADDNX, - COUNT, - count: COUNT, - DEL, - del: DEL, - EXISTS, - exists: EXISTS, - INFO, - info: INFO, - INSERT, - insert: INSERT, - INSERTNX, - insertNX: INSERTNX, - LOADCHUNK, - loadChunk: LOADCHUNK, - RESERVE, - reserve: RESERVE, - SCANDUMP, - scanDump: SCANDUMP -}; - -export interface InsertOptions { - CAPACITY?: number; - NOCREATE?: true; -} - -export function pushInsertOptions( - args: RedisCommandArguments, - items: string | Array, - options?: InsertOptions -): RedisCommandArguments { - if (options?.CAPACITY) { - args.push('CAPACITY'); - args.push(options.CAPACITY.toString()); - } - - if (options?.NOCREATE) { - args.push('NOCREATE'); - } - - args.push('ITEMS'); - return pushVerdictArguments(args, items); -} + ADD, + add: ADD, + ADDNX, + addNX: ADDNX, + COUNT, + count: COUNT, + DEL, + del: DEL, + EXISTS, + exists: EXISTS, + INFO, + info: INFO, + INSERT, + insert: INSERT, + INSERTNX, + insertNX: INSERTNX, + LOADCHUNK, + loadChunk: LOADCHUNK, + RESERVE, + reserve: RESERVE, + SCANDUMP, + scanDump: SCANDUMP +} as const satisfies RedisCommands; diff --git a/packages/bloom/lib/commands/index.ts b/packages/bloom/lib/commands/index.ts index cea55b2a7c0..6f91089460a 100644 --- a/packages/bloom/lib/commands/index.ts +++ b/packages/bloom/lib/commands/index.ts @@ -1,3 +1,4 @@ +import { RedisModules } from '@redis/client'; import bf from './bloom'; import cms from './count-min-sketch'; import cf from './cuckoo'; @@ -5,9 +6,9 @@ import tDigest from './t-digest'; import topK from './top-k'; export default { - bf, - cms, - cf, - tDigest, - topK -}; + bf, + cms, + cf, + tDigest, + topK +} as const satisfies RedisModules; diff --git a/packages/bloom/lib/commands/t-digest/ADD.spec.ts b/packages/bloom/lib/commands/t-digest/ADD.spec.ts index 3e1dbff7f27..31d4957c6ad 100644 --- a/packages/bloom/lib/commands/t-digest/ADD.spec.ts +++ b/packages/bloom/lib/commands/t-digest/ADD.spec.ts @@ -1,21 +1,21 @@ -import { strict as assert } from 'assert'; +import { strict as assert } from 'node:assert'; import testUtils, { GLOBAL } from '../../test-utils'; -import { transformArguments } from './ADD'; +import ADD from './ADD'; describe('TDIGEST.ADD', () => { - it('transformArguments', () => { - assert.deepEqual( - transformArguments('key', [1, 2]), - ['TDIGEST.ADD', 'key', '1', '2'] - ); - }); + it('transformArguments', () => { + assert.deepEqual( + ADD.transformArguments('key', [1, 2]), + ['TDIGEST.ADD', 'key', '1', '2'] + ); + }); - testUtils.testWithClient('client.tDigest.add', async client => { - const [ , reply ] = await Promise.all([ - client.tDigest.create('key'), - client.tDigest.add('key', [1]) - ]); + testUtils.testWithClient('client.tDigest.add', async client => { + const [, reply] = await Promise.all([ + client.tDigest.create('key'), + client.tDigest.add('key', [1]) + ]); - assert.equal(reply, 'OK'); - }, GLOBAL.SERVERS.OPEN); + assert.equal(reply, 'OK'); + }, GLOBAL.SERVERS.OPEN); }); diff --git a/packages/bloom/lib/commands/t-digest/ADD.ts b/packages/bloom/lib/commands/t-digest/ADD.ts index 941e8531003..e7c6d7c4429 100644 --- a/packages/bloom/lib/commands/t-digest/ADD.ts +++ b/packages/bloom/lib/commands/t-digest/ADD.ts @@ -1,17 +1,16 @@ -import { RedisCommandArgument, RedisCommandArguments } from '@redis/client/dist/lib/commands'; +import { SimpleStringReply, Command, RedisArgument } from '@redis/client/dist/lib/RESP/types'; -export const FIRST_KEY_INDEX = 1; - -export function transformArguments( - key: RedisCommandArgument, - values: Array -): RedisCommandArguments { +export default { + FIRST_KEY_INDEX: 1, + IS_READ_ONLY: false, + transformArguments(key: RedisArgument, values: Array) { const args = ['TDIGEST.ADD', key]; - for (const item of values) { - args.push(item.toString()); + + for (const value of values) { + args.push(value.toString()); } return args; -} - -export declare function transformReply(): 'OK'; + }, + transformReply: undefined as unknown as () => SimpleStringReply<'OK'> +} as const satisfies Command; diff --git a/packages/bloom/lib/commands/t-digest/BYRANK.spec.ts b/packages/bloom/lib/commands/t-digest/BYRANK.spec.ts index 083f09d22af..a6443d77432 100644 --- a/packages/bloom/lib/commands/t-digest/BYRANK.spec.ts +++ b/packages/bloom/lib/commands/t-digest/BYRANK.spec.ts @@ -1,21 +1,21 @@ -import { strict as assert } from 'assert'; +import { strict as assert } from 'node:assert'; import testUtils, { GLOBAL } from '../../test-utils'; -import { transformArguments } from './BYRANK'; +import BYRANK from './BYRANK'; describe('TDIGEST.BYRANK', () => { - it('transformArguments', () => { - assert.deepEqual( - transformArguments('key', [1, 2]), - ['TDIGEST.BYRANK', 'key', '1', '2'] - ); - }); + it('transformArguments', () => { + assert.deepEqual( + BYRANK.transformArguments('key', [1, 2]), + ['TDIGEST.BYRANK', 'key', '1', '2'] + ); + }); - testUtils.testWithClient('client.tDigest.byRank', async client => { - const [ , reply ] = await Promise.all([ - client.tDigest.create('key'), - client.tDigest.byRank('key', [1]) - ]); + testUtils.testWithClient('client.tDigest.byRank', async client => { + const [, reply] = await Promise.all([ + client.tDigest.create('key'), + client.tDigest.byRank('key', [1]) + ]); - assert.deepEqual(reply, [NaN]); - }, GLOBAL.SERVERS.OPEN); + assert.deepEqual(reply, [NaN]); + }, GLOBAL.SERVERS.OPEN); }); diff --git a/packages/bloom/lib/commands/t-digest/BYRANK.ts b/packages/bloom/lib/commands/t-digest/BYRANK.ts index 5684385b4d3..8b48acd1b1b 100644 --- a/packages/bloom/lib/commands/t-digest/BYRANK.ts +++ b/packages/bloom/lib/commands/t-digest/BYRANK.ts @@ -1,19 +1,24 @@ -import { RedisCommandArgument, RedisCommandArguments } from '@redis/client/dist/lib/commands'; +import { RedisArgument, Command } from '@redis/client/dist/lib/RESP/types'; +import { transformDoubleArrayReply } from '@redis/client/dist/lib/commands/generic-transformers'; -export const FIRST_KEY_INDEX = 1; +export function transformByRankArguments( + command: RedisArgument, + key: RedisArgument, + ranks: Array +) { + const args = [command, key]; -export const IS_READ_ONLY = true; + for (const rank of ranks) { + args.push(rank.toString()); + } -export function transformArguments( - key: RedisCommandArgument, - ranks: Array -): RedisCommandArguments { - const args = ['TDIGEST.BYRANK', key]; - for (const rank of ranks) { - args.push(rank.toString()); - } - - return args; + return args; } -export { transformDoublesReply as transformReply } from '.'; +export default { + FIRST_KEY_INDEX: 1, + IS_READ_ONLY: true, + transformArguments: transformByRankArguments.bind(undefined, 'TDIGEST.BYRANK'), + transformReply: transformDoubleArrayReply +} as const satisfies Command; + diff --git a/packages/bloom/lib/commands/t-digest/BYREVRANK.spec.ts b/packages/bloom/lib/commands/t-digest/BYREVRANK.spec.ts index c094f36e71d..f5bb4e62816 100644 --- a/packages/bloom/lib/commands/t-digest/BYREVRANK.spec.ts +++ b/packages/bloom/lib/commands/t-digest/BYREVRANK.spec.ts @@ -1,21 +1,21 @@ -import { strict as assert } from 'assert'; +import { strict as assert } from 'node:assert'; import testUtils, { GLOBAL } from '../../test-utils'; -import { transformArguments } from './BYREVRANK'; +import BYREVRANK from './BYREVRANK'; describe('TDIGEST.BYREVRANK', () => { - it('transformArguments', () => { - assert.deepEqual( - transformArguments('key', [1, 2]), - ['TDIGEST.BYREVRANK', 'key', '1', '2'] - ); - }); + it('transformArguments', () => { + assert.deepEqual( + BYREVRANK.transformArguments('key', [1, 2]), + ['TDIGEST.BYREVRANK', 'key', '1', '2'] + ); + }); - testUtils.testWithClient('client.tDigest.byRevRank', async client => { - const [ , reply ] = await Promise.all([ - client.tDigest.create('key'), - client.tDigest.byRevRank('key', [1]) - ]); + testUtils.testWithClient('client.tDigest.byRevRank', async client => { + const [, reply] = await Promise.all([ + client.tDigest.create('key'), + client.tDigest.byRevRank('key', [1]) + ]); - assert.deepEqual(reply, [NaN]); - }, GLOBAL.SERVERS.OPEN); + assert.deepEqual(reply, [NaN]); + }, GLOBAL.SERVERS.OPEN); }); diff --git a/packages/bloom/lib/commands/t-digest/BYREVRANK.ts b/packages/bloom/lib/commands/t-digest/BYREVRANK.ts index 3dcf3a973c4..9f62b42d812 100644 --- a/packages/bloom/lib/commands/t-digest/BYREVRANK.ts +++ b/packages/bloom/lib/commands/t-digest/BYREVRANK.ts @@ -1,19 +1,9 @@ -import { RedisCommandArgument, RedisCommandArguments } from '@redis/client/dist/lib/commands'; - -export const FIRST_KEY_INDEX = 1; - -export const IS_READ_ONLY = true; - -export function transformArguments( - key: RedisCommandArgument, - ranks: Array -): RedisCommandArguments { - const args = ['TDIGEST.BYREVRANK', key]; - for (const rank of ranks) { - args.push(rank.toString()); - } - - return args; -} - -export { transformDoublesReply as transformReply } from '.'; +import { Command } from '@redis/client/dist/lib/RESP/types'; +import BYRANK, { transformByRankArguments } from './BYRANK'; + +export default { + FIRST_KEY_INDEX: BYRANK.FIRST_KEY_INDEX, + IS_READ_ONLY: BYRANK.IS_READ_ONLY, + transformArguments: transformByRankArguments.bind(undefined, 'TDIGEST.BYREVRANK'), + transformReply: BYRANK.transformReply +} as const satisfies Command; diff --git a/packages/bloom/lib/commands/t-digest/CDF.spec.ts b/packages/bloom/lib/commands/t-digest/CDF.spec.ts index 36d3564f62c..09208deba11 100644 --- a/packages/bloom/lib/commands/t-digest/CDF.spec.ts +++ b/packages/bloom/lib/commands/t-digest/CDF.spec.ts @@ -1,21 +1,21 @@ -import { strict as assert } from 'assert'; +import { strict as assert } from 'node:assert'; import testUtils, { GLOBAL } from '../../test-utils'; -import { transformArguments } from './CDF'; +import CDF from './CDF'; describe('TDIGEST.CDF', () => { - it('transformArguments', () => { - assert.deepEqual( - transformArguments('key', [1, 2]), - ['TDIGEST.CDF', 'key', '1', '2'] - ); - }); + it('transformArguments', () => { + assert.deepEqual( + CDF.transformArguments('key', [1, 2]), + ['TDIGEST.CDF', 'key', '1', '2'] + ); + }); - testUtils.testWithClient('client.tDigest.cdf', async client => { - const [ , reply ] = await Promise.all([ - client.tDigest.create('key'), - client.tDigest.cdf('key', [1]) - ]); + testUtils.testWithClient('client.tDigest.cdf', async client => { + const [, reply] = await Promise.all([ + client.tDigest.create('key'), + client.tDigest.cdf('key', [1]) + ]); - assert.deepEqual(reply, [NaN]); - }, GLOBAL.SERVERS.OPEN); + assert.deepEqual(reply, [NaN]); + }, GLOBAL.SERVERS.OPEN); }); diff --git a/packages/bloom/lib/commands/t-digest/CDF.ts b/packages/bloom/lib/commands/t-digest/CDF.ts index fe7ece59d76..0fbdedb3a47 100644 --- a/packages/bloom/lib/commands/t-digest/CDF.ts +++ b/packages/bloom/lib/commands/t-digest/CDF.ts @@ -1,19 +1,17 @@ -import { RedisCommandArgument, RedisCommandArguments } from '@redis/client/dist/lib/commands'; +import { RedisArgument, Command } from '@redis/client/dist/lib/RESP/types'; +import { transformDoubleArrayReply } from '@redis/client/dist/lib/commands/generic-transformers'; -export const FIRST_KEY_INDEX = 1; - -export const IS_READ_ONLY = true; - -export function transformArguments( - key: RedisCommandArgument, - values: Array -): RedisCommandArguments { +export default { + FIRST_KEY_INDEX: 1, + IS_READ_ONLY: true, + transformArguments(key: RedisArgument, values: Array) { const args = ['TDIGEST.CDF', key]; + for (const item of values) { - args.push(item.toString()); + args.push(item.toString()); } return args; -} - -export { transformDoublesReply as transformReply } from '.'; + }, + transformReply: transformDoubleArrayReply +} as const satisfies Command; diff --git a/packages/bloom/lib/commands/t-digest/CREATE.spec.ts b/packages/bloom/lib/commands/t-digest/CREATE.spec.ts index 4d329cc81ae..781b2a7e432 100644 --- a/packages/bloom/lib/commands/t-digest/CREATE.spec.ts +++ b/packages/bloom/lib/commands/t-digest/CREATE.spec.ts @@ -1,30 +1,30 @@ -import { strict as assert } from 'assert'; +import { strict as assert } from 'node:assert'; import testUtils, { GLOBAL } from '../../test-utils'; -import { transformArguments } from './CREATE'; +import CREATE from './CREATE'; describe('TDIGEST.CREATE', () => { - describe('transformArguments', () => { - it('without options', () => { - assert.deepEqual( - transformArguments('key'), - ['TDIGEST.CREATE', 'key'] - ); - }); + describe('transformArguments', () => { + it('without options', () => { + assert.deepEqual( + CREATE.transformArguments('key'), + ['TDIGEST.CREATE', 'key'] + ); + }); - it('with COMPRESSION', () => { - assert.deepEqual( - transformArguments('key', { - COMPRESSION: 100 - }), - ['TDIGEST.CREATE', 'key', 'COMPRESSION', '100'] - ); - }); + it('with COMPRESSION', () => { + assert.deepEqual( + CREATE.transformArguments('key', { + COMPRESSION: 100 + }), + ['TDIGEST.CREATE', 'key', 'COMPRESSION', '100'] + ); }); + }); - testUtils.testWithClient('client.tDigest.create', async client => { - assert.equal( - await client.tDigest.create('key'), - 'OK' - ); - }, GLOBAL.SERVERS.OPEN); + testUtils.testWithClient('client.tDigest.create', async client => { + assert.equal( + await client.tDigest.create('key'), + 'OK' + ); + }, GLOBAL.SERVERS.OPEN); }); diff --git a/packages/bloom/lib/commands/t-digest/CREATE.ts b/packages/bloom/lib/commands/t-digest/CREATE.ts index 1935d2973dc..8b832487daa 100644 --- a/packages/bloom/lib/commands/t-digest/CREATE.ts +++ b/packages/bloom/lib/commands/t-digest/CREATE.ts @@ -1,16 +1,20 @@ -import { RedisCommandArgument, RedisCommandArguments } from '@redis/client/dist/lib/commands'; -import { CompressionOption, pushCompressionArgument } from '.'; +import { RedisArgument, SimpleStringReply, Command } from '@redis/client/dist/lib/RESP/types'; -export const FIRST_KEY_INDEX = 1; - -export function transformArguments( - key: RedisCommandArgument, - options?: CompressionOption -): RedisCommandArguments { - return pushCompressionArgument( - ['TDIGEST.CREATE', key], - options - ); +export interface TDigestCreateOptions { + COMPRESSION?: number; } -export declare function transformReply(): 'OK'; +export default { + FIRST_KEY_INDEX: 1, + IS_READ_ONLY: false, + transformArguments(key: RedisArgument, options?: TDigestCreateOptions) { + const args = ['TDIGEST.CREATE', key]; + + if (options?.COMPRESSION !== undefined) { + args.push('COMPRESSION', options.COMPRESSION.toString()); + } + + return args; + }, + transformReply: undefined as unknown as () => SimpleStringReply<'OK'> +} as const satisfies Command; diff --git a/packages/bloom/lib/commands/t-digest/INFO.spec.ts b/packages/bloom/lib/commands/t-digest/INFO.spec.ts index 992fda6ea05..247f4ab0b61 100644 --- a/packages/bloom/lib/commands/t-digest/INFO.spec.ts +++ b/packages/bloom/lib/commands/t-digest/INFO.spec.ts @@ -1,25 +1,30 @@ -import { strict as assert } from 'assert'; +import { strict as assert } from 'node:assert'; import testUtils, { GLOBAL } from '../../test-utils'; -import { transformArguments } from './INFO'; +import INFO from './INFO'; describe('TDIGEST.INFO', () => { - it('transformArguments', () => { - assert.deepEqual( - transformArguments('key'), - ['TDIGEST.INFO', 'key'] - ); - }); + it('transformArguments', () => { + assert.deepEqual( + INFO.transformArguments('key'), + ['TDIGEST.INFO', 'key'] + ); + }); - testUtils.testWithClient('client.tDigest.info', async client => { - await client.tDigest.create('key'); + testUtils.testWithClient('client.tDigest.info', async client => { + const [, reply] = await Promise.all([ + client.tDigest.create('key'), + client.tDigest.info('key') + ]); - const info = await client.tDigest.info('key'); - assert(typeof info.capacity, 'number'); - assert(typeof info.mergedNodes, 'number'); - assert(typeof info.unmergedNodes, 'number'); - assert(typeof info.mergedWeight, 'number'); - assert(typeof info.unmergedWeight, 'number'); - assert(typeof info.totalCompression, 'number'); - assert(typeof info.totalCompression, 'number'); - }, GLOBAL.SERVERS.OPEN); + assert(typeof reply, 'object'); + assert(typeof reply['Compression'], 'number'); + assert(typeof reply['Capacity'], 'number'); + assert(typeof reply['Merged nodes'], 'number'); + assert(typeof reply['Unmerged nodes'], 'number'); + assert(typeof reply['Merged weight'], 'number'); + assert(typeof reply['Unmerged weight'], 'number'); + assert(typeof reply['Observations'], 'number'); + assert(typeof reply['Total compressions'], 'number'); + assert(typeof reply['Memory usage'], 'number'); + }, GLOBAL.SERVERS.OPEN); }); diff --git a/packages/bloom/lib/commands/t-digest/INFO.ts b/packages/bloom/lib/commands/t-digest/INFO.ts index 44d2083524f..c7c2357d2b4 100644 --- a/packages/bloom/lib/commands/t-digest/INFO.ts +++ b/packages/bloom/lib/commands/t-digest/INFO.ts @@ -1,51 +1,28 @@ -import { RedisCommandArgument, RedisCommandArguments } from '@redis/client/dist/lib/commands'; +import { RedisArgument, Command, NumberReply, TuplesToMapReply, UnwrapReply, Resp2Reply, SimpleStringReply, TypeMapping } from '@redis/client/dist/lib/RESP/types'; +import { transformInfoV2Reply } from '../bloom'; -export const FIRST_KEY_INDEX = 1; +export type TdInfoReplyMap = TuplesToMapReply<[ + [SimpleStringReply<'Compression'>, NumberReply], + [SimpleStringReply<'Capacity'>, NumberReply], + [SimpleStringReply<'Merged nodes'>, NumberReply], + [SimpleStringReply<'Unmerged nodes'>, NumberReply], + [SimpleStringReply<'Merged weight'>, NumberReply], + [SimpleStringReply<'Unmerged weight'>, NumberReply], + [SimpleStringReply<'Observations'>, NumberReply], + [SimpleStringReply<'Total compressions'>, NumberReply], + [SimpleStringReply<'Memory usage'>, NumberReply] +]>; -export const IS_READ_ONLY = true; - -export function transformArguments(key: RedisCommandArgument): RedisCommandArguments { - return [ - 'TDIGEST.INFO', - key - ]; -} - -type InfoRawReply = [ - 'Compression', - number, - 'Capacity', - number, - 'Merged nodes', - number, - 'Unmerged nodes', - number, - 'Merged weight', - string, - 'Unmerged weight', - string, - 'Total compressions', - number -]; - -interface InfoReply { - comperssion: number; - capacity: number; - mergedNodes: number; - unmergedNodes: number; - mergedWeight: number; - unmergedWeight: number; - totalCompression: number; -} - -export function transformReply(reply: InfoRawReply): InfoReply { - return { - comperssion: reply[1], - capacity: reply[3], - mergedNodes: reply[5], - unmergedNodes: reply[7], - mergedWeight: Number(reply[9]), - unmergedWeight: Number(reply[11]), - totalCompression: reply[13] - }; -} \ No newline at end of file +export default { + FIRST_KEY_INDEX: 1, + IS_READ_ONLY: true, + transformArguments(key: RedisArgument) { + return ['TDIGEST.INFO', key]; + }, + transformReply: { + 2: (reply: UnwrapReply>, _, typeMapping?: TypeMapping): TdInfoReplyMap => { + return transformInfoV2Reply(reply, typeMapping); + }, + 3: undefined as unknown as () => TdInfoReplyMap + } +} as const satisfies Command; diff --git a/packages/bloom/lib/commands/t-digest/MAX.spec.ts b/packages/bloom/lib/commands/t-digest/MAX.spec.ts index bf850cbfd83..caa92b0a6a0 100644 --- a/packages/bloom/lib/commands/t-digest/MAX.spec.ts +++ b/packages/bloom/lib/commands/t-digest/MAX.spec.ts @@ -1,21 +1,21 @@ -import { strict as assert } from 'assert'; +import { strict as assert } from 'node:assert'; import testUtils, { GLOBAL } from '../../test-utils'; -import { transformArguments, transformReply } from './MAX'; +import MAX from './MAX'; describe('TDIGEST.MAX', () => { - it('transformArguments', () => { - assert.deepEqual( - transformArguments('key'), - ['TDIGEST.MAX', 'key'] - ); - }); + it('transformArguments', () => { + assert.deepEqual( + MAX.transformArguments('key'), + ['TDIGEST.MAX', 'key'] + ); + }); - testUtils.testWithClient('client.tDigest.max', async client => { - const [ , reply ] = await Promise.all([ - client.tDigest.create('key'), - client.tDigest.max('key') - ]); + testUtils.testWithClient('client.tDigest.max', async client => { + const [, reply] = await Promise.all([ + client.tDigest.create('key'), + client.tDigest.max('key') + ]); - assert.deepEqual(reply, NaN); - }, GLOBAL.SERVERS.OPEN); + assert.deepEqual(reply, NaN); + }, GLOBAL.SERVERS.OPEN); }); diff --git a/packages/bloom/lib/commands/t-digest/MAX.ts b/packages/bloom/lib/commands/t-digest/MAX.ts index 90c42ec6067..ef778f832a6 100644 --- a/packages/bloom/lib/commands/t-digest/MAX.ts +++ b/packages/bloom/lib/commands/t-digest/MAX.ts @@ -1,14 +1,11 @@ -import { RedisCommandArgument, RedisCommandArguments } from '@redis/client/dist/lib/commands'; - -export const FIRST_KEY_INDEX = 1; - -export const IS_READ_ONLY = true; - -export function transformArguments(key: RedisCommandArgument): RedisCommandArguments { - return [ - 'TDIGEST.MAX', - key - ]; -} - -export { transformDoubleReply as transformReply } from '.'; +import { RedisArgument, Command } from '@redis/client/dist/lib/RESP/types'; +import { transformDoubleReply } from '@redis/client/dist/lib/commands/generic-transformers'; + +export default { + FIRST_KEY_INDEX: 1, + IS_READ_ONLY: true, + transformArguments(key: RedisArgument) { + return ['TDIGEST.MAX', key]; + }, + transformReply: transformDoubleReply +} as const satisfies Command; diff --git a/packages/bloom/lib/commands/t-digest/MERGE.spec.ts b/packages/bloom/lib/commands/t-digest/MERGE.spec.ts index 1205cdd9216..1ee792e3a40 100644 --- a/packages/bloom/lib/commands/t-digest/MERGE.spec.ts +++ b/packages/bloom/lib/commands/t-digest/MERGE.spec.ts @@ -1,50 +1,50 @@ -import { strict as assert } from 'assert'; +import { strict as assert } from 'node:assert'; import testUtils, { GLOBAL } from '../../test-utils'; -import { transformArguments, transformReply } from './MERGE'; +import MERGE from './MERGE'; describe('TDIGEST.MERGE', () => { - describe('transformArguments', () => { - describe('srcKeys', () => { - it('string', () => { - assert.deepEqual( - transformArguments('dest', 'src'), - ['TDIGEST.MERGE', 'dest', '1', 'src'] - ); - }); + describe('transformArguments', () => { + describe('source', () => { + it('string', () => { + assert.deepEqual( + MERGE.transformArguments('destination', 'source'), + ['TDIGEST.MERGE', 'destination', '1', 'source'] + ); + }); - it('Array', () => { - assert.deepEqual( - transformArguments('dest', ['1', '2']), - ['TDIGEST.MERGE', 'dest', '2', '1', '2'] - ); - }); - }); + it('Array', () => { + assert.deepEqual( + MERGE.transformArguments('destination', ['1', '2']), + ['TDIGEST.MERGE', 'destination', '2', '1', '2'] + ); + }); + }); - it('with COMPRESSION', () => { - assert.deepEqual( - transformArguments('dest', 'src', { - COMPRESSION: 100 - }), - ['TDIGEST.MERGE', 'dest', '1', 'src', 'COMPRESSION', '100'] - ); - }); + it('with COMPRESSION', () => { + assert.deepEqual( + MERGE.transformArguments('destination', 'source', { + COMPRESSION: 100 + }), + ['TDIGEST.MERGE', 'destination', '1', 'source', 'COMPRESSION', '100'] + ); + }); - it('with OVERRIDE', () => { - assert.deepEqual( - transformArguments('dest', 'src', { - OVERRIDE: true - }), - ['TDIGEST.MERGE', 'dest', '1', 'src', 'OVERRIDE'] - ); - }); + it('with OVERRIDE', () => { + assert.deepEqual( + MERGE.transformArguments('destination', 'source', { + OVERRIDE: true + }), + ['TDIGEST.MERGE', 'destination', '1', 'source', 'OVERRIDE'] + ); }); + }); - testUtils.testWithClient('client.tDigest.merge', async client => { - const [ , reply ] = await Promise.all([ - client.tDigest.create('src'), - client.tDigest.merge('dest', 'src') - ]); + testUtils.testWithClient('client.tDigest.merge', async client => { + const [, reply] = await Promise.all([ + client.tDigest.create('source'), + client.tDigest.merge('destination', 'source') + ]); - assert.equal(reply, 'OK'); - }, GLOBAL.SERVERS.OPEN); + assert.equal(reply, 'OK'); + }, GLOBAL.SERVERS.OPEN); }); diff --git a/packages/bloom/lib/commands/t-digest/MERGE.ts b/packages/bloom/lib/commands/t-digest/MERGE.ts index 5119d0b9e18..e9cd99aabf9 100644 --- a/packages/bloom/lib/commands/t-digest/MERGE.ts +++ b/packages/bloom/lib/commands/t-digest/MERGE.ts @@ -1,30 +1,30 @@ -import { RedisCommandArgument, RedisCommandArguments } from '@redis/client/dist/lib/commands'; -import { pushVerdictArgument } from '@redis/client/dist/lib/commands/generic-transformers'; -import { CompressionOption, pushCompressionArgument } from '.'; +import { RedisArgument, SimpleStringReply, Command } from '@redis/client/dist/lib/RESP/types'; +import { RedisVariadicArgument, pushVariadicArgument } from '@redis/client/dist/lib/commands/generic-transformers'; -export const FIRST_KEY_INDEX = 1; - -interface MergeOptions extends CompressionOption { - OVERRIDE?: boolean; +export interface TDigestMergeOptions { + COMPRESSION?: number; + OVERRIDE?: boolean; } -export function transformArguments( - destKey: RedisCommandArgument, - srcKeys: RedisCommandArgument | Array, - options?: MergeOptions -): RedisCommandArguments { - const args = pushVerdictArgument( - ['TDIGEST.MERGE', destKey], - srcKeys - ); +export default { + FIRST_KEY_INDEX: undefined, + IS_READ_ONLY: false, + transformArguments( + destination: RedisArgument, + source: RedisVariadicArgument, + options?: TDigestMergeOptions + ) { + const args = pushVariadicArgument(['TDIGEST.MERGE', destination], source); - pushCompressionArgument(args, options); + if (options?.COMPRESSION !== undefined) { + args.push('COMPRESSION', options.COMPRESSION.toString()); + } if (options?.OVERRIDE) { - args.push('OVERRIDE'); + args.push('OVERRIDE'); } return args; -} - -export declare function transformReply(): 'OK'; + }, + transformReply: undefined as unknown as () => SimpleStringReply<'OK'> +} as const satisfies Command; diff --git a/packages/bloom/lib/commands/t-digest/MIN.spec.ts b/packages/bloom/lib/commands/t-digest/MIN.spec.ts index d48deaca7fb..0d1637cc9b7 100644 --- a/packages/bloom/lib/commands/t-digest/MIN.spec.ts +++ b/packages/bloom/lib/commands/t-digest/MIN.spec.ts @@ -1,21 +1,21 @@ -import { strict as assert } from 'assert'; +import { strict as assert } from 'node:assert'; import testUtils, { GLOBAL } from '../../test-utils'; -import { transformArguments, transformReply } from './MIN'; +import MIN from './MIN'; describe('TDIGEST.MIN', () => { - it('transformArguments', () => { - assert.deepEqual( - transformArguments('key'), - ['TDIGEST.MIN', 'key'] - ); - }); + it('transformArguments', () => { + assert.deepEqual( + MIN.transformArguments('key'), + ['TDIGEST.MIN', 'key'] + ); + }); - testUtils.testWithClient('client.tDigest.min', async client => { - const [ , reply ] = await Promise.all([ - client.tDigest.create('key'), - client.tDigest.min('key') - ]); + testUtils.testWithClient('client.tDigest.min', async client => { + const [, reply] = await Promise.all([ + client.tDigest.create('key'), + client.tDigest.min('key') + ]); - assert.equal(reply, NaN); - }, GLOBAL.SERVERS.OPEN); + assert.equal(reply, NaN); + }, GLOBAL.SERVERS.OPEN); }); diff --git a/packages/bloom/lib/commands/t-digest/MIN.ts b/packages/bloom/lib/commands/t-digest/MIN.ts index d8be8722b60..914998a1ec4 100644 --- a/packages/bloom/lib/commands/t-digest/MIN.ts +++ b/packages/bloom/lib/commands/t-digest/MIN.ts @@ -1,14 +1,11 @@ -import { RedisCommandArgument, RedisCommandArguments } from '@redis/client/dist/lib/commands'; - -export const FIRST_KEY_INDEX = 1; - -export const IS_READ_ONLY = true; - -export function transformArguments(key: RedisCommandArgument): RedisCommandArguments { - return [ - 'TDIGEST.MIN', - key - ]; -} - -export { transformDoubleReply as transformReply } from '.'; +import { RedisArgument, Command } from '@redis/client/dist/lib/RESP/types'; +import { transformDoubleReply } from '@redis/client/dist/lib/commands/generic-transformers'; + +export default { + FIRST_KEY_INDEX: 1, + IS_READ_ONLY: true, + transformArguments(key: RedisArgument) { + return ['TDIGEST.MIN', key]; + }, + transformReply: transformDoubleReply +} as const satisfies Command; diff --git a/packages/bloom/lib/commands/t-digest/QUANTILE.spec.ts b/packages/bloom/lib/commands/t-digest/QUANTILE.spec.ts index 7790debf0de..c427f8c4501 100644 --- a/packages/bloom/lib/commands/t-digest/QUANTILE.spec.ts +++ b/packages/bloom/lib/commands/t-digest/QUANTILE.spec.ts @@ -1,24 +1,24 @@ -import { strict as assert } from 'assert'; +import { strict as assert } from 'node:assert'; import testUtils, { GLOBAL } from '../../test-utils'; -import { transformArguments } from './QUANTILE'; +import QUANTILE from './QUANTILE'; describe('TDIGEST.QUANTILE', () => { - it('transformArguments', () => { - assert.deepEqual( - transformArguments('key', [1, 2]), - ['TDIGEST.QUANTILE', 'key', '1', '2'] - ); - }); + it('transformArguments', () => { + assert.deepEqual( + QUANTILE.transformArguments('key', [1, 2]), + ['TDIGEST.QUANTILE', 'key', '1', '2'] + ); + }); - testUtils.testWithClient('client.tDigest.quantile', async client => { - const [, reply] = await Promise.all([ - client.tDigest.create('key'), - client.tDigest.quantile('key', [1]) - ]); + testUtils.testWithClient('client.tDigest.quantile', async client => { + const [, reply] = await Promise.all([ + client.tDigest.create('key'), + client.tDigest.quantile('key', [1]) + ]); - assert.deepEqual( - reply, - [NaN] - ); - }, GLOBAL.SERVERS.OPEN); + assert.deepEqual( + reply, + [NaN] + ); + }, GLOBAL.SERVERS.OPEN); }); diff --git a/packages/bloom/lib/commands/t-digest/QUANTILE.ts b/packages/bloom/lib/commands/t-digest/QUANTILE.ts index 2289ffc6f55..f7057a37d1c 100644 --- a/packages/bloom/lib/commands/t-digest/QUANTILE.ts +++ b/packages/bloom/lib/commands/t-digest/QUANTILE.ts @@ -1,23 +1,17 @@ -import { RedisCommandArgument, RedisCommandArguments } from '@redis/client/dist/lib/commands'; +import { RedisArgument, Command } from '@redis/client/dist/lib/RESP/types'; +import { transformDoubleArrayReply } from '@redis/client/dist/lib/commands/generic-transformers'; -export const FIRST_KEY_INDEX = 1; - -export const IS_READ_ONLY = true; - -export function transformArguments( - key: RedisCommandArgument, - quantiles: Array -): RedisCommandArguments { - const args = [ - 'TDIGEST.QUANTILE', - key - ]; +export default { + FIRST_KEY_INDEX: 1, + IS_READ_ONLY: true, + transformArguments(key: RedisArgument, quantiles: Array) { + const args = ['TDIGEST.QUANTILE', key]; for (const quantile of quantiles) { - args.push(quantile.toString()); + args.push(quantile.toString()); } return args; -} - -export { transformDoublesReply as transformReply } from '.'; + }, + transformReply: transformDoubleArrayReply +} as const satisfies Command; diff --git a/packages/bloom/lib/commands/t-digest/RANK.spec.ts b/packages/bloom/lib/commands/t-digest/RANK.spec.ts index 258bedf3491..dcdae48cb06 100644 --- a/packages/bloom/lib/commands/t-digest/RANK.spec.ts +++ b/packages/bloom/lib/commands/t-digest/RANK.spec.ts @@ -1,21 +1,21 @@ -import { strict as assert } from 'assert'; +import { strict as assert } from 'node:assert'; import testUtils, { GLOBAL } from '../../test-utils'; -import { transformArguments } from './RANK'; +import RANK from './RANK'; describe('TDIGEST.RANK', () => { - it('transformArguments', () => { - assert.deepEqual( - transformArguments('key', [1, 2]), - ['TDIGEST.RANK', 'key', '1', '2'] - ); - }); + it('transformArguments', () => { + assert.deepEqual( + RANK.transformArguments('key', [1, 2]), + ['TDIGEST.RANK', 'key', '1', '2'] + ); + }); - testUtils.testWithClient('client.tDigest.rank', async client => { - const [ , reply ] = await Promise.all([ - client.tDigest.create('key'), - client.tDigest.rank('key', [1]) - ]); + testUtils.testWithClient('client.tDigest.rank', async client => { + const [, reply] = await Promise.all([ + client.tDigest.create('key'), + client.tDigest.rank('key', [1]) + ]); - assert.deepEqual(reply, [-2]); - }, GLOBAL.SERVERS.OPEN); + assert.deepEqual(reply, [-2]); + }, GLOBAL.SERVERS.OPEN); }); diff --git a/packages/bloom/lib/commands/t-digest/RANK.ts b/packages/bloom/lib/commands/t-digest/RANK.ts index 1a6c84bbd4d..8c18ad12778 100644 --- a/packages/bloom/lib/commands/t-digest/RANK.ts +++ b/packages/bloom/lib/commands/t-digest/RANK.ts @@ -1,19 +1,22 @@ -import { RedisCommandArgument, RedisCommandArguments } from '@redis/client/dist/lib/commands'; +import { RedisArgument, ArrayReply, NumberReply, Command } from '@redis/client/dist/lib/RESP/types'; -export const FIRST_KEY_INDEX = 1; +export function transformRankArguments( + command: RedisArgument, + key: RedisArgument, + values: Array +) { + const args = [command, key]; -export const IS_READ_ONLY = true; + for (const value of values) { + args.push(value.toString()); + } -export function transformArguments( - key: RedisCommandArgument, - values: Array -): RedisCommandArguments { - const args = ['TDIGEST.RANK', key]; - for (const item of values) { - args.push(item.toString()); - } - - return args; + return args; } -export declare function transformReply(): Array; +export default { + FIRST_KEY_INDEX: 1, + IS_READ_ONLY: true, + transformArguments: transformRankArguments.bind(undefined, 'TDIGEST.RANK'), + transformReply: undefined as unknown as () => ArrayReply +} as const satisfies Command; diff --git a/packages/bloom/lib/commands/t-digest/RESET.spec.ts b/packages/bloom/lib/commands/t-digest/RESET.spec.ts index 036fbebc8cc..072257113b9 100644 --- a/packages/bloom/lib/commands/t-digest/RESET.spec.ts +++ b/packages/bloom/lib/commands/t-digest/RESET.spec.ts @@ -1,21 +1,21 @@ -import { strict as assert } from 'assert'; +import { strict as assert } from 'node:assert'; import testUtils, { GLOBAL } from '../../test-utils'; -import { transformArguments } from './RESET'; +import RESET from './RESET'; describe('TDIGEST.RESET', () => { - it('transformArguments', () => { - assert.deepEqual( - transformArguments('key'), - ['TDIGEST.RESET', 'key'] - ); - }); + it('transformArguments', () => { + assert.deepEqual( + RESET.transformArguments('key'), + ['TDIGEST.RESET', 'key'] + ); + }); - testUtils.testWithClient('client.tDigest.reset', async client => { - const [, reply] = await Promise.all([ - client.tDigest.create('key'), - client.tDigest.reset('key') - ]); + testUtils.testWithClient('client.tDigest.reset', async client => { + const [, reply] = await Promise.all([ + client.tDigest.create('key'), + client.tDigest.reset('key') + ]); - assert.equal(reply, 'OK'); - }, GLOBAL.SERVERS.OPEN); + assert.equal(reply, 'OK'); + }, GLOBAL.SERVERS.OPEN); }); diff --git a/packages/bloom/lib/commands/t-digest/RESET.ts b/packages/bloom/lib/commands/t-digest/RESET.ts index 6c700e6b932..372a1efd488 100644 --- a/packages/bloom/lib/commands/t-digest/RESET.ts +++ b/packages/bloom/lib/commands/t-digest/RESET.ts @@ -1,9 +1,10 @@ -import { RedisCommandArgument, RedisCommandArguments } from '@redis/client/dist/lib/commands'; +import { RedisArgument, SimpleStringReply, Command } from '@redis/client/dist/lib/RESP/types'; -export const FIRST_KEY_INDEX = 1; - -export function transformArguments(key: RedisCommandArgument): RedisCommandArguments { +export default { + FIRST_KEY_INDEX: 1, + IS_READ_ONLY: false, + transformArguments(key: RedisArgument) { return ['TDIGEST.RESET', key]; -} - -export declare function transformReply(): 'OK'; + }, + transformReply: undefined as unknown as () => SimpleStringReply<'OK'> +} as const satisfies Command; diff --git a/packages/bloom/lib/commands/t-digest/REVRANK.spec.ts b/packages/bloom/lib/commands/t-digest/REVRANK.spec.ts index 21d16661dfe..baa1b94afa8 100644 --- a/packages/bloom/lib/commands/t-digest/REVRANK.spec.ts +++ b/packages/bloom/lib/commands/t-digest/REVRANK.spec.ts @@ -1,21 +1,21 @@ -import { strict as assert } from 'assert'; +import { strict as assert } from 'node:assert'; import testUtils, { GLOBAL } from '../../test-utils'; -import { transformArguments } from './REVRANK'; +import REVRANK from './REVRANK'; describe('TDIGEST.REVRANK', () => { - it('transformArguments', () => { - assert.deepEqual( - transformArguments('key', [1, 2]), - ['TDIGEST.REVRANK', 'key', '1', '2'] - ); - }); + it('transformArguments', () => { + assert.deepEqual( + REVRANK.transformArguments('key', [1, 2]), + ['TDIGEST.REVRANK', 'key', '1', '2'] + ); + }); - testUtils.testWithClient('client.tDigest.revRank', async client => { - const [ , reply ] = await Promise.all([ - client.tDigest.create('key'), - client.tDigest.revRank('key', [1]) - ]); + testUtils.testWithClient('client.tDigest.revRank', async client => { + const [, reply] = await Promise.all([ + client.tDigest.create('key'), + client.tDigest.revRank('key', [1]) + ]); - assert.deepEqual(reply, [-2]); - }, GLOBAL.SERVERS.OPEN); + assert.deepEqual(reply, [-2]); + }, GLOBAL.SERVERS.OPEN); }); diff --git a/packages/bloom/lib/commands/t-digest/REVRANK.ts b/packages/bloom/lib/commands/t-digest/REVRANK.ts index a2465052774..456b2be5a38 100644 --- a/packages/bloom/lib/commands/t-digest/REVRANK.ts +++ b/packages/bloom/lib/commands/t-digest/REVRANK.ts @@ -1,19 +1,9 @@ -import { RedisCommandArgument, RedisCommandArguments } from '@redis/client/dist/lib/commands'; - -export const FIRST_KEY_INDEX = 1; - -export const IS_READ_ONLY = true; - -export function transformArguments( - key: RedisCommandArgument, - values: Array -): RedisCommandArguments { - const args = ['TDIGEST.REVRANK', key]; - for (const item of values) { - args.push(item.toString()); - } - - return args; -} - -export declare function transformReply(): Array; +import { Command } from '@redis/client/dist/lib/RESP/types'; +import RANK, { transformRankArguments } from './RANK'; + +export default { + FIRST_KEY_INDEX: RANK.FIRST_KEY_INDEX, + IS_READ_ONLY: RANK.IS_READ_ONLY, + transformArguments: transformRankArguments.bind(undefined, 'TDIGEST.REVRANK'), + transformReply: RANK.transformReply +} as const satisfies Command; diff --git a/packages/bloom/lib/commands/t-digest/TRIMMED_MEAN.spec.ts b/packages/bloom/lib/commands/t-digest/TRIMMED_MEAN.spec.ts index dd07f325c8d..c43c0f47553 100644 --- a/packages/bloom/lib/commands/t-digest/TRIMMED_MEAN.spec.ts +++ b/packages/bloom/lib/commands/t-digest/TRIMMED_MEAN.spec.ts @@ -1,21 +1,21 @@ -import { strict as assert } from 'assert'; +import { strict as assert } from 'node:assert'; import testUtils, { GLOBAL } from '../../test-utils'; -import { transformArguments, transformReply } from './TRIMMED_MEAN'; +import TRIMMED_MEAN from './TRIMMED_MEAN'; -describe('TDIGEST.RESET', () => { - it('transformArguments', () => { - assert.deepEqual( - transformArguments('key', 0, 1), - ['TDIGEST.TRIMMED_MEAN', 'key', '0', '1'] - ); - }); +describe('TDIGEST.TRIMMED_MEAN', () => { + it('transformArguments', () => { + assert.deepEqual( + TRIMMED_MEAN.transformArguments('key', 0, 1), + ['TDIGEST.TRIMMED_MEAN', 'key', '0', '1'] + ); + }); - testUtils.testWithClient('client.tDigest.trimmedMean', async client => { - const [, reply] = await Promise.all([ - client.tDigest.create('key'), - client.tDigest.trimmedMean('key', 0, 1) - ]); + testUtils.testWithClient('client.tDigest.trimmedMean', async client => { + const [, reply] = await Promise.all([ + client.tDigest.create('key'), + client.tDigest.trimmedMean('key', 0, 1) + ]); - assert.equal(reply, NaN); - }, GLOBAL.SERVERS.OPEN); + assert.equal(reply, NaN); + }, GLOBAL.SERVERS.OPEN); }); diff --git a/packages/bloom/lib/commands/t-digest/TRIMMED_MEAN.ts b/packages/bloom/lib/commands/t-digest/TRIMMED_MEAN.ts index 6de80ba7c7c..f91dd7d8093 100644 --- a/packages/bloom/lib/commands/t-digest/TRIMMED_MEAN.ts +++ b/packages/bloom/lib/commands/t-digest/TRIMMED_MEAN.ts @@ -1,20 +1,20 @@ -import { RedisCommandArgument, RedisCommandArguments } from '@redis/client/dist/lib/commands'; +import { RedisArgument, Command } from '@redis/client/dist/lib/RESP/types'; +import { transformDoubleReply } from '@redis/client/dist/lib/commands/generic-transformers'; -export const FIRST_KEY_INDEX = 1; - -export const IS_READ_ONLY = true; - -export function transformArguments( - key: RedisCommandArgument, +export default { + FIRST_KEY_INDEX: 1, + IS_READ_ONLY: true, + transformArguments( + key: RedisArgument, lowCutPercentile: number, highCutPercentile: number -): RedisCommandArguments { + ) { return [ - 'TDIGEST.TRIMMED_MEAN', - key, - lowCutPercentile.toString(), - highCutPercentile.toString() + 'TDIGEST.TRIMMED_MEAN', + key, + lowCutPercentile.toString(), + highCutPercentile.toString() ]; -} - -export { transformDoubleReply as transformReply } from '.'; + }, + transformReply: transformDoubleReply +} as const satisfies Command; diff --git a/packages/bloom/lib/commands/t-digest/index.spec.ts b/packages/bloom/lib/commands/t-digest/index.spec.ts deleted file mode 100644 index 5bef6df04b2..00000000000 --- a/packages/bloom/lib/commands/t-digest/index.spec.ts +++ /dev/null @@ -1,55 +0,0 @@ -import { strict as assert } from 'assert'; -import { pushCompressionArgument, transformDoubleReply, transformDoublesReply } from '.'; - -describe('pushCompressionArgument', () => { - it('undefined', () => { - assert.deepEqual( - pushCompressionArgument([]), - [] - ); - }); - - it('100', () => { - assert.deepEqual( - pushCompressionArgument([], { COMPRESSION: 100 }), - ['COMPRESSION', '100'] - ); - }); -}); - -describe('transformDoubleReply', () => { - it('inf', () => { - assert.equal( - transformDoubleReply('inf'), - Infinity - ); - }); - - it('-inf', () => { - assert.equal( - transformDoubleReply('-inf'), - -Infinity - ); - }); - - it('nan', () => { - assert.equal( - transformDoubleReply('nan'), - NaN - ); - }); - - it('0', () => { - assert.equal( - transformDoubleReply('0'), - 0 - ); - }); -}); - -it('transformDoublesReply', () => { - assert.deepEqual( - transformDoublesReply(['inf', '-inf', 'nan', '0']), - [Infinity, -Infinity, NaN, 0] - ); -}); diff --git a/packages/bloom/lib/commands/t-digest/index.ts b/packages/bloom/lib/commands/t-digest/index.ts index da3b37464d2..d180911dbf9 100644 --- a/packages/bloom/lib/commands/t-digest/index.ts +++ b/packages/bloom/lib/commands/t-digest/index.ts @@ -1,81 +1,46 @@ -import { RedisCommandArguments } from '@redis/client/dist/lib/commands'; -import * as ADD from './ADD'; -import * as BYRANK from './BYRANK'; -import * as BYREVRANK from './BYREVRANK'; -import * as CDF from './CDF'; -import * as CREATE from './CREATE'; -import * as INFO from './INFO'; -import * as MAX from './MAX'; -import * as MERGE from './MERGE'; -import * as MIN from './MIN'; -import * as QUANTILE from './QUANTILE'; -import * as RANK from './RANK'; -import * as RESET from './RESET'; -import * as REVRANK from './REVRANK'; -import * as TRIMMED_MEAN from './TRIMMED_MEAN'; +import type { RedisCommands } from '@redis/client/dist/lib/RESP/types'; +import ADD from './ADD'; +import BYRANK from './BYRANK'; +import BYREVRANK from './BYREVRANK'; +import CDF from './CDF'; +import CREATE from './CREATE'; +import INFO from './INFO'; +import MAX from './MAX'; +import MERGE from './MERGE'; +import MIN from './MIN'; +import QUANTILE from './QUANTILE'; +import RANK from './RANK'; +import RESET from './RESET'; +import REVRANK from './REVRANK'; +import TRIMMED_MEAN from './TRIMMED_MEAN'; export default { - ADD, - add: ADD, - BYRANK, - byRank: BYRANK, - BYREVRANK, - byRevRank: BYREVRANK, - CDF, - cdf: CDF, - CREATE, - create: CREATE, - INFO, - info: INFO, - MAX, - max: MAX, - MERGE, - merge: MERGE, - MIN, - min: MIN, - QUANTILE, - quantile: QUANTILE, - RANK, - rank: RANK, - RESET, - reset: RESET, - REVRANK, - revRank: REVRANK, - TRIMMED_MEAN, - trimmedMean: TRIMMED_MEAN -}; - -export interface CompressionOption { - COMPRESSION?: number; -} - -export function pushCompressionArgument( - args: RedisCommandArguments, - options?: CompressionOption -): RedisCommandArguments { - if (options?.COMPRESSION) { - args.push('COMPRESSION', options.COMPRESSION.toString()); - } - - return args; -} - -export function transformDoubleReply(reply: string): number { - switch (reply) { - case 'inf': - return Infinity; - - case '-inf': - return -Infinity; - - case 'nan': - return NaN; - - default: - return parseFloat(reply); - } -} - -export function transformDoublesReply(reply: Array): Array { - return reply.map(transformDoubleReply); -} + ADD, + add: ADD, + BYRANK, + byRank: BYRANK, + BYREVRANK, + byRevRank: BYREVRANK, + CDF, + cdf: CDF, + CREATE, + create: CREATE, + INFO, + info: INFO, + MAX, + max: MAX, + MERGE, + merge: MERGE, + MIN, + min: MIN, + QUANTILE, + quantile: QUANTILE, + RANK, + rank: RANK, + RESET, + reset: RESET, + REVRANK, + revRank: REVRANK, + TRIMMED_MEAN, + trimmedMean: TRIMMED_MEAN +} as const satisfies RedisCommands; diff --git a/packages/bloom/lib/commands/top-k/ADD.spec.ts b/packages/bloom/lib/commands/top-k/ADD.spec.ts index 149007f81d0..8f6f9300b36 100644 --- a/packages/bloom/lib/commands/top-k/ADD.spec.ts +++ b/packages/bloom/lib/commands/top-k/ADD.spec.ts @@ -1,22 +1,21 @@ -import { strict as assert } from 'assert'; +import { strict as assert } from 'node:assert'; import testUtils, { GLOBAL } from '../../test-utils'; -import { transformArguments } from './ADD'; +import ADD from './ADD'; -describe('TOPK ADD', () => { - it('transformArguments', () => { - assert.deepEqual( - transformArguments('key', 'item'), - ['TOPK.ADD', 'key', 'item'] - ); - }); +describe('TOPK.ADD', () => { + it('transformArguments', () => { + assert.deepEqual( + ADD.transformArguments('key', 'item'), + ['TOPK.ADD', 'key', 'item'] + ); + }); - testUtils.testWithClient('client.topK.add', async client => { - await client.topK.reserve('topK', 3); + testUtils.testWithClient('client.topK.add', async client => { + const [, reply] = await Promise.all([ + client.topK.reserve('topK', 3), + client.topK.add('topK', 'item') + ]); - assert.deepEqual( - await client.topK.add('topK', 'item'), - [null] - ); - - }, GLOBAL.SERVERS.OPEN); + assert.deepEqual(reply, [null]); + }, GLOBAL.SERVERS.OPEN); }); diff --git a/packages/bloom/lib/commands/top-k/ADD.ts b/packages/bloom/lib/commands/top-k/ADD.ts index beee3a2206c..99982cc8e64 100644 --- a/packages/bloom/lib/commands/top-k/ADD.ts +++ b/packages/bloom/lib/commands/top-k/ADD.ts @@ -1,13 +1,11 @@ -import { RedisCommandArguments } from '@redis/client/dist/lib/commands'; -import { pushVerdictArguments } from '@redis/client/dist/lib/commands/generic-transformers'; +import { RedisArgument, ArrayReply, BlobStringReply, Command } from '@redis/client/dist/lib/RESP/types'; +import { RedisVariadicArgument, pushVariadicArguments } from '@redis/client/dist/lib/commands/generic-transformers'; -export const FIRST_KEY_INDEX = 1; - -export function transformArguments( - key: string, - items: string | Array -): RedisCommandArguments { - return pushVerdictArguments(['TOPK.ADD', key], items); -} - -export declare function transformReply(): Array; +export default { + FIRST_KEY_INDEX: 1, + IS_READ_ONLY: false, + transformArguments(key: RedisArgument, items: RedisVariadicArgument) { + return pushVariadicArguments(['TOPK.ADD', key], items); + }, + transformReply: undefined as unknown as () => ArrayReply +} as const satisfies Command; diff --git a/packages/bloom/lib/commands/top-k/COUNT.spec.ts b/packages/bloom/lib/commands/top-k/COUNT.spec.ts index 318fc74c679..dce03f0e78c 100644 --- a/packages/bloom/lib/commands/top-k/COUNT.spec.ts +++ b/packages/bloom/lib/commands/top-k/COUNT.spec.ts @@ -1,21 +1,21 @@ -import { strict as assert } from 'assert'; +import { strict as assert } from 'node:assert'; import testUtils, { GLOBAL } from '../../test-utils'; -import { transformArguments } from './COUNT'; +import COUNT from './COUNT'; -describe('TOPK COUNT', () => { - it('transformArguments', () => { - assert.deepEqual( - transformArguments('key', 'item'), - ['TOPK.COUNT', 'key', 'item'] - ); - }); +describe('TOPK.COUNT', () => { + it('transformArguments', () => { + assert.deepEqual( + COUNT.transformArguments('key', 'item'), + ['TOPK.COUNT', 'key', 'item'] + ); + }); - testUtils.testWithClient('client.topK.count', async client => { - await client.topK.reserve('key', 3); + testUtils.testWithClient('client.topK.count', async client => { + const [, reply] = await Promise.all([ + client.topK.reserve('key', 3), + client.topK.count('key', 'item') + ]); - assert.deepEqual( - await client.topK.count('key', 'item'), - [0] - ); - }, GLOBAL.SERVERS.OPEN); + assert.deepEqual(reply, [0]); + }, GLOBAL.SERVERS.OPEN); }); diff --git a/packages/bloom/lib/commands/top-k/COUNT.ts b/packages/bloom/lib/commands/top-k/COUNT.ts index fc8cf557dca..7e3ccc6dc4c 100644 --- a/packages/bloom/lib/commands/top-k/COUNT.ts +++ b/packages/bloom/lib/commands/top-k/COUNT.ts @@ -1,15 +1,11 @@ -import { RedisCommandArguments } from '@redis/client/dist/lib/commands'; -import { pushVerdictArguments } from '@redis/client/dist/lib/commands/generic-transformers'; - -export const FIRST_KEY_INDEX = 1; - -export const IS_READ_ONLY = true; - -export function transformArguments( - key: string, - items: string | Array -): RedisCommandArguments { - return pushVerdictArguments(['TOPK.COUNT', key], items); -} - -export declare function transformReply(): Array; +import { RedisArgument, ArrayReply, NumberReply, Command } from '@redis/client/dist/lib/RESP/types'; +import { RedisVariadicArgument, pushVariadicArguments } from '@redis/client/dist/lib/commands/generic-transformers'; + +export default { + FIRST_KEY_INDEX: 1, + IS_READ_ONLY: true, + transformArguments(key: RedisArgument, items: RedisVariadicArgument) { + return pushVariadicArguments(['TOPK.COUNT', key], items); + }, + transformReply: undefined as unknown as () => ArrayReply +} as const satisfies Command; diff --git a/packages/bloom/lib/commands/top-k/INCRBY.spec.ts b/packages/bloom/lib/commands/top-k/INCRBY.spec.ts index b23ca6e0ed1..aa7032a9a02 100644 --- a/packages/bloom/lib/commands/top-k/INCRBY.spec.ts +++ b/packages/bloom/lib/commands/top-k/INCRBY.spec.ts @@ -1,42 +1,42 @@ -import { strict as assert } from 'assert'; +import { strict as assert } from 'node:assert'; import testUtils, { GLOBAL } from '../../test-utils'; -import { transformArguments } from './INCRBY'; +import INCRBY from './INCRBY'; -describe('TOPK INCRBY', () => { - describe('transformArguments', () => { - it('single item', () => { - assert.deepEqual( - transformArguments('key', { - item: 'item', - incrementBy: 1 - }), - ['TOPK.INCRBY', 'key', 'item', '1'] - ); - }); +describe('TOPK.INCRBY', () => { + describe('transformArguments', () => { + it('single item', () => { + assert.deepEqual( + INCRBY.transformArguments('key', { + item: 'item', + incrementBy: 1 + }), + ['TOPK.INCRBY', 'key', 'item', '1'] + ); + }); - it('multiple items', () => { - assert.deepEqual( - transformArguments('key', [{ - item: 'a', - incrementBy: 1 - }, { - item: 'b', - incrementBy: 2 - }]), - ['TOPK.INCRBY', 'key', 'a', '1', 'b', '2'] - ); - }); + it('multiple items', () => { + assert.deepEqual( + INCRBY.transformArguments('key', [{ + item: 'a', + incrementBy: 1 + }, { + item: 'b', + incrementBy: 2 + }]), + ['TOPK.INCRBY', 'key', 'a', '1', 'b', '2'] + ); }); + }); - testUtils.testWithClient('client.topK.incrby', async client => { - await client.topK.reserve('key', 5); + testUtils.testWithClient('client.topK.incrby', async client => { + const [, reply] = await Promise.all([ + client.topK.reserve('key', 5), + client.topK.incrBy('key', { + item: 'item', + incrementBy: 1 + }) + ]); - assert.deepEqual( - await client.topK.incrBy('key', { - item: 'item', - incrementBy: 1 - }), - [null] - ); - }, GLOBAL.SERVERS.OPEN); + assert.deepEqual(reply, [null]); + }, GLOBAL.SERVERS.OPEN); }); diff --git a/packages/bloom/lib/commands/top-k/INCRBY.ts b/packages/bloom/lib/commands/top-k/INCRBY.ts index 2533cb05594..16decf44dca 100644 --- a/packages/bloom/lib/commands/top-k/INCRBY.ts +++ b/packages/bloom/lib/commands/top-k/INCRBY.ts @@ -1,29 +1,32 @@ -export const FIRST_KEY_INDEX = 1; +import { RedisArgument, ArrayReply, SimpleStringReply, NullReply, Command } from '@redis/client/dist/lib/RESP/types'; -interface IncrByItem { - item: string; - incrementBy: number; +export interface TopKIncrByItem { + item: string; + incrementBy: number; } -export function transformArguments( - key: string, - items: IncrByItem | Array -): Array { +function pushIncrByItem(args: Array, { item, incrementBy }: TopKIncrByItem) { + args.push(item, incrementBy.toString()); +} + +export default { + FIRST_KEY_INDEX: 1, + IS_READ_ONLY: false, + transformArguments( + key: RedisArgument, + items: TopKIncrByItem | Array + ) { const args = ['TOPK.INCRBY', key]; if (Array.isArray(items)) { - for (const item of items) { - pushIncrByItem(args, item); - } + for (const item of items) { + pushIncrByItem(args, item); + } } else { - pushIncrByItem(args, items); + pushIncrByItem(args, items); } return args; -} - -function pushIncrByItem(args: Array, { item, incrementBy }: IncrByItem): void { - args.push(item, incrementBy.toString()); -} - -export declare function transformReply(): Array; + }, + transformReply: undefined as unknown as () => ArrayReply +} as const satisfies Command; diff --git a/packages/bloom/lib/commands/top-k/INFO.spec.ts b/packages/bloom/lib/commands/top-k/INFO.spec.ts index 2741a58a8ba..8e17829a2a6 100644 --- a/packages/bloom/lib/commands/top-k/INFO.spec.ts +++ b/packages/bloom/lib/commands/top-k/INFO.spec.ts @@ -1,23 +1,26 @@ -import { strict as assert } from 'assert'; +import { strict as assert } from 'node:assert'; import testUtils, { GLOBAL } from '../../test-utils'; -import { transformArguments } from './INFO'; +import INFO from './INFO'; describe('TOPK INFO', () => { - it('transformArguments', () => { - assert.deepEqual( - transformArguments('key'), - ['TOPK.INFO', 'key'] - ); - }); + it('transformArguments', () => { + assert.deepEqual( + INFO.transformArguments('key'), + ['TOPK.INFO', 'key'] + ); + }); - testUtils.testWithClient('client.topK.info', async client => { - await client.topK.reserve('key', 3); + testUtils.testWithClient('client.topK.info', async client => { + const k = 3, + [, reply] = await Promise.all([ + client.topK.reserve('key', 3), + client.topK.info('key') + ]); - const info = await client.topK.info('key'); - assert.equal(typeof info, 'object'); - assert.equal(info.k, 3); - assert.equal(typeof info.width, 'number'); - assert.equal(typeof info.depth, 'number'); - assert.equal(typeof info.decay, 'number'); - }, GLOBAL.SERVERS.OPEN); + assert.equal(typeof reply, 'object'); + assert.equal(reply.k, k); + assert.equal(typeof reply.width, 'number'); + assert.equal(typeof reply.depth, 'number'); + assert.equal(typeof reply.decay, 'number'); + }, GLOBAL.SERVERS.OPEN); }); diff --git a/packages/bloom/lib/commands/top-k/INFO.ts b/packages/bloom/lib/commands/top-k/INFO.ts index 8c9e8d432b3..e6f55ac2c1b 100644 --- a/packages/bloom/lib/commands/top-k/INFO.ts +++ b/packages/bloom/lib/commands/top-k/INFO.ts @@ -1,34 +1,26 @@ -export const FIRST_KEY_INDEX = 1; +import { transformDoubleReply } from '@redis/client/dist/lib/commands/generic-transformers'; +import { RedisArgument, TuplesToMapReply, NumberReply, DoubleReply, UnwrapReply, Resp2Reply, Command, SimpleStringReply, TypeMapping } from '@redis/client/dist/lib/RESP/types'; +import { transformInfoV2Reply } from '../bloom'; -export const IS_READ_ONLY = true; +export type TopKInfoReplyMap = TuplesToMapReply<[ + [SimpleStringReply<'k'>, NumberReply], + [SimpleStringReply<'width'>, NumberReply], + [SimpleStringReply<'depth'>, NumberReply], + [SimpleStringReply<'decay'>, DoubleReply] +]>; -export function transformArguments(key: string): Array { +export default { + FIRST_KEY_INDEX: 1, + IS_READ_ONLY: true, + transformArguments(key: RedisArgument) { return ['TOPK.INFO', key]; -} + }, + transformReply: { + 2: (reply: UnwrapReply>, preserve?: any, typeMapping?: TypeMapping): TopKInfoReplyMap => { + reply[7] = transformDoubleReply[2](reply[7], preserve, typeMapping) as any; -export type InfoRawReply = [ - _: string, - k: number, - _: string, - width: number, - _: string, - depth: number, - _: string, - decay: string -]; - -export interface InfoReply { - k: number, - width: number; - depth: number; - decay: number; -} - -export function transformReply(reply: InfoRawReply): InfoReply { - return { - k: reply[1], - width: reply[3], - depth: reply[5], - decay: Number(reply[7]) - }; -} + return transformInfoV2Reply(reply, typeMapping); + }, + 3: undefined as unknown as () => TopKInfoReplyMap + } +} as const satisfies Command diff --git a/packages/bloom/lib/commands/top-k/LIST.spec.ts b/packages/bloom/lib/commands/top-k/LIST.spec.ts index 709ac7ffc39..7ab96182bbe 100644 --- a/packages/bloom/lib/commands/top-k/LIST.spec.ts +++ b/packages/bloom/lib/commands/top-k/LIST.spec.ts @@ -1,21 +1,21 @@ -import { strict as assert } from 'assert'; +import { strict as assert } from 'node:assert'; import testUtils, { GLOBAL } from '../../test-utils'; -import { transformArguments } from './LIST'; +import LIST from './LIST'; -describe('TOPK LIST', () => { - it('transformArguments', () => { - assert.deepEqual( - transformArguments('key'), - ['TOPK.LIST', 'key'] - ); - }); +describe('TOPK.LIST', () => { + it('transformArguments', () => { + assert.deepEqual( + LIST.transformArguments('key'), + ['TOPK.LIST', 'key'] + ); + }); - testUtils.testWithClient('client.topK.list', async client => { - await client.topK.reserve('key', 3); + testUtils.testWithClient('client.topK.list', async client => { + const [, reply] = await Promise.all([ + client.topK.reserve('key', 3), + client.topK.list('key') + ]); - assert.deepEqual( - await client.topK.list('key'), - [] - ); - }, GLOBAL.SERVERS.OPEN); + assert.deepEqual(reply, []); + }, GLOBAL.SERVERS.OPEN); }); diff --git a/packages/bloom/lib/commands/top-k/LIST.ts b/packages/bloom/lib/commands/top-k/LIST.ts index 8837b86f830..26345b72462 100644 --- a/packages/bloom/lib/commands/top-k/LIST.ts +++ b/packages/bloom/lib/commands/top-k/LIST.ts @@ -1,9 +1,10 @@ -export const FIRST_KEY_INDEX = 1; +import { RedisArgument, ArrayReply, BlobStringReply, Command } from '@redis/client/dist/lib/RESP/types'; -export const IS_READ_ONLY = true; - -export function transformArguments(key: string): Array { +export default { + FIRST_KEY_INDEX: 1, + IS_READ_ONLY: true, + transformArguments(key: RedisArgument) { return ['TOPK.LIST', key]; -} - -export declare function transformReply(): Array; + }, + transformReply: undefined as unknown as () => ArrayReply +} as const satisfies Command; diff --git a/packages/bloom/lib/commands/top-k/LIST_WITHCOUNT.spec.ts b/packages/bloom/lib/commands/top-k/LIST_WITHCOUNT.spec.ts index 1e55239c243..862d17eb3e3 100644 --- a/packages/bloom/lib/commands/top-k/LIST_WITHCOUNT.spec.ts +++ b/packages/bloom/lib/commands/top-k/LIST_WITHCOUNT.spec.ts @@ -1,30 +1,27 @@ -import { strict as assert } from 'assert'; +import { strict as assert } from 'node:assert'; import testUtils, { GLOBAL } from '../../test-utils'; -import { transformArguments } from './LIST_WITHCOUNT'; +import LIST_WITHCOUNT from './LIST_WITHCOUNT'; -describe('TOPK LIST WITHCOUNT', () => { - testUtils.isVersionGreaterThanHook([2, 2, 9]); - - it('transformArguments', () => { - assert.deepEqual( - transformArguments('key'), - ['TOPK.LIST', 'key', 'WITHCOUNT'] - ); - }); +describe('TOPK.LIST WITHCOUNT', () => { + testUtils.isVersionGreaterThanHook([2, 2, 9]); - testUtils.testWithClient('client.topK.listWithCount', async client => { - const [,, list] = await Promise.all([ - client.topK.reserve('key', 3), - client.topK.add('key', 'item'), - client.topK.listWithCount('key') - ]); + it('transformArguments', () => { + assert.deepEqual( + LIST_WITHCOUNT.transformArguments('key'), + ['TOPK.LIST', 'key', 'WITHCOUNT'] + ); + }); - assert.deepEqual( - list, - [{ - item: 'item', - count: 1 - }] - ); - }, GLOBAL.SERVERS.OPEN); + testUtils.testWithClient('client.topK.listWithCount', async client => { + const [, , list] = await Promise.all([ + client.topK.reserve('key', 3), + client.topK.add('key', 'item'), + client.topK.listWithCount('key') + ]); + + assert.deepEqual(list, [{ + item: 'item', + count: 1 + }]); + }, GLOBAL.SERVERS.OPEN); }); diff --git a/packages/bloom/lib/commands/top-k/LIST_WITHCOUNT.ts b/packages/bloom/lib/commands/top-k/LIST_WITHCOUNT.ts index 47b7d3848ed..d26936fd3c7 100644 --- a/packages/bloom/lib/commands/top-k/LIST_WITHCOUNT.ts +++ b/packages/bloom/lib/commands/top-k/LIST_WITHCOUNT.ts @@ -1,26 +1,24 @@ -export const FIRST_KEY_INDEX = 1; +import { RedisArgument, ArrayReply, BlobStringReply, NumberReply, UnwrapReply, Command } from '@redis/client/dist/lib/RESP/types'; -export const IS_READ_ONLY = true; - -export function transformArguments(key: string): Array { +export default { + FIRST_KEY_INDEX: 1, + IS_READ_ONLY: true, + transformArguments(key: RedisArgument) { return ['TOPK.LIST', key, 'WITHCOUNT']; -} - -type ListWithCountRawReply = Array; - -type ListWithCountReply = Array<{ - item: string, - count: number -}>; - -export function transformReply(rawReply: ListWithCountRawReply): ListWithCountReply { - const reply: ListWithCountReply = []; + }, + transformReply(rawReply: UnwrapReply>) { + const reply: Array<{ + item: BlobStringReply; + count: NumberReply; + }> = []; + for (let i = 0; i < rawReply.length; i++) { - reply.push({ - item: rawReply[i] as string, - count: rawReply[++i] as number - }); + reply.push({ + item: rawReply[i] as BlobStringReply, + count: rawReply[++i] as NumberReply + }); } return reply; -} \ No newline at end of file + } +} as const satisfies Command; diff --git a/packages/bloom/lib/commands/top-k/QUERY.spec.ts b/packages/bloom/lib/commands/top-k/QUERY.spec.ts index ada9e7e2e39..d5ecfebb6c6 100644 --- a/packages/bloom/lib/commands/top-k/QUERY.spec.ts +++ b/packages/bloom/lib/commands/top-k/QUERY.spec.ts @@ -1,21 +1,21 @@ -import { strict as assert } from 'assert'; +import { strict as assert } from 'node:assert'; import testUtils, { GLOBAL } from '../../test-utils'; -import { transformArguments } from './QUERY'; +import QUERY from './QUERY'; -describe('TOPK QUERY', () => { - it('transformArguments', () => { - assert.deepEqual( - transformArguments('key', 'item'), - ['TOPK.QUERY', 'key', 'item'] - ); - }); +describe('TOPK.QUERY', () => { + it('transformArguments', () => { + assert.deepEqual( + QUERY.transformArguments('key', 'item'), + ['TOPK.QUERY', 'key', 'item'] + ); + }); - testUtils.testWithClient('client.cms.query', async client => { - await client.topK.reserve('key', 3); + testUtils.testWithClient('client.topK.query', async client => { + const [, reply] = await Promise.all([ + client.topK.reserve('key', 3), + client.topK.query('key', 'item') + ]); - assert.deepEqual( - await client.topK.query('key', 'item'), - [0] - ); - }, GLOBAL.SERVERS.OPEN); + assert.deepEqual(reply, [false]); + }, GLOBAL.SERVERS.OPEN); }); diff --git a/packages/bloom/lib/commands/top-k/QUERY.ts b/packages/bloom/lib/commands/top-k/QUERY.ts index 94943a26fd7..5529d4ab838 100644 --- a/packages/bloom/lib/commands/top-k/QUERY.ts +++ b/packages/bloom/lib/commands/top-k/QUERY.ts @@ -1,15 +1,11 @@ -import { RedisCommandArguments } from '@redis/client/dist/lib/commands'; -import { pushVerdictArguments } from '@redis/client/dist/lib/commands/generic-transformers'; - -export const FIRST_KEY_INDEX = 1; - -export const IS_READ_ONLY = true; - -export function transformArguments( - key: string, - items: string | Array -): RedisCommandArguments { - return pushVerdictArguments(['TOPK.QUERY', key], items); -} - -export declare function transformReply(): Array; +import { RedisArgument, Command } from '@redis/client/dist/lib/RESP/types'; +import { RedisVariadicArgument, pushVariadicArguments, transformBooleanArrayReply } from '@redis/client/dist/lib/commands/generic-transformers'; + +export default { + FIRST_KEY_INDEX: undefined, + IS_READ_ONLY: false, + transformArguments(key: RedisArgument, items: RedisVariadicArgument) { + return pushVariadicArguments(['TOPK.QUERY', key], items); + }, + transformReply: transformBooleanArrayReply +} as const satisfies Command; diff --git a/packages/bloom/lib/commands/top-k/RESERVE.spec.ts b/packages/bloom/lib/commands/top-k/RESERVE.spec.ts index 54600c0e4f5..39d8fb7efc6 100644 --- a/packages/bloom/lib/commands/top-k/RESERVE.spec.ts +++ b/packages/bloom/lib/commands/top-k/RESERVE.spec.ts @@ -1,32 +1,32 @@ -import { strict as assert } from 'assert'; +import { strict as assert } from 'node:assert'; import testUtils, { GLOBAL } from '../../test-utils'; -import { transformArguments } from './RESERVE'; +import RESERVE from './RESERVE'; -describe('TOPK RESERVE', () => { - describe('transformArguments', () => { - it('simple', () => { - assert.deepEqual( - transformArguments('topK', 3), - ['TOPK.RESERVE', 'topK', '3'] - ); - }); +describe('TOPK.RESERVE', () => { + describe('transformArguments', () => { + it('simple', () => { + assert.deepEqual( + RESERVE.transformArguments('topK', 3), + ['TOPK.RESERVE', 'topK', '3'] + ); + }); - it('with options', () => { - assert.deepEqual( - transformArguments('topK', 3, { - width: 8, - depth: 7, - decay: 0.9 - }), - ['TOPK.RESERVE', 'topK', '3', '8', '7', '0.9'] - ); - }); + it('with options', () => { + assert.deepEqual( + RESERVE.transformArguments('topK', 3, { + width: 8, + depth: 7, + decay: 0.9 + }), + ['TOPK.RESERVE', 'topK', '3', '8', '7', '0.9'] + ); }); + }); - testUtils.testWithClient('client.topK.reserve', async client => { - assert.equal( - await client.topK.reserve('topK', 3), - 'OK' - ); - }, GLOBAL.SERVERS.OPEN); + testUtils.testWithClient('client.topK.reserve', async client => { + assert.equal( + await client.topK.reserve('topK', 3), + 'OK' + ); + }, GLOBAL.SERVERS.OPEN); }); diff --git a/packages/bloom/lib/commands/top-k/RESERVE.ts b/packages/bloom/lib/commands/top-k/RESERVE.ts index 350d4cd8339..12671728fea 100644 --- a/packages/bloom/lib/commands/top-k/RESERVE.ts +++ b/packages/bloom/lib/commands/top-k/RESERVE.ts @@ -1,29 +1,26 @@ -export const FIRST_KEY_INDEX = 1; +import { SimpleStringReply, Command, RedisArgument } from '@redis/client/dist/lib/RESP/types'; -export const IS_READ_ONLY = true; - -interface ReserveOptions { - width: number; - depth: number; - decay: number; +export interface TopKReserveOptions { + width: number; + depth: number; + decay: number; } -export function transformArguments( - key: string, - topK: number, - options?: ReserveOptions -): Array { +export default { + FIRST_KEY_INDEX: 1, + IS_READ_ONLY: false, + transformArguments(key: RedisArgument, topK: number, options?: TopKReserveOptions) { const args = ['TOPK.RESERVE', key, topK.toString()]; if (options) { - args.push( - options.width.toString(), - options.depth.toString(), - options.decay.toString() - ); + args.push( + options.width.toString(), + options.depth.toString(), + options.decay.toString() + ); } return args; -} - -export declare function transformReply(): 'OK'; + }, + transformReply: undefined as unknown as () => SimpleStringReply<'OK'> +} as const satisfies Command; diff --git a/packages/bloom/lib/commands/top-k/index.ts b/packages/bloom/lib/commands/top-k/index.ts index 750c91dfa88..fb5de543cab 100644 --- a/packages/bloom/lib/commands/top-k/index.ts +++ b/packages/bloom/lib/commands/top-k/index.ts @@ -1,27 +1,28 @@ -import * as ADD from './ADD'; -import * as COUNT from './COUNT'; -import * as INCRBY from './INCRBY'; -import * as INFO from './INFO'; -import * as LIST_WITHCOUNT from './LIST_WITHCOUNT'; -import * as LIST from './LIST'; -import * as QUERY from './QUERY'; -import * as RESERVE from './RESERVE'; +import type { RedisCommands } from '@redis/client/dist/lib/RESP/types'; +import ADD from './ADD'; +import COUNT from './COUNT'; +import INCRBY from './INCRBY'; +import INFO from './INFO'; +import LIST_WITHCOUNT from './LIST_WITHCOUNT'; +import LIST from './LIST'; +import QUERY from './QUERY'; +import RESERVE from './RESERVE'; export default { - ADD, - add: ADD, - COUNT, - count: COUNT, - INCRBY, - incrBy: INCRBY, - INFO, - info: INFO, - LIST_WITHCOUNT, - listWithCount: LIST_WITHCOUNT, - LIST, - list: LIST, - QUERY, - query: QUERY, - RESERVE, - reserve: RESERVE -}; + ADD, + add: ADD, + COUNT, + count: COUNT, + INCRBY, + incrBy: INCRBY, + INFO, + info: INFO, + LIST_WITHCOUNT, + listWithCount: LIST_WITHCOUNT, + LIST, + list: LIST, + QUERY, + query: QUERY, + RESERVE, + reserve: RESERVE +} as const satisfies RedisCommands; diff --git a/packages/bloom/lib/test-utils.ts b/packages/bloom/lib/test-utils.ts index a2e059b3b99..1291054e802 100644 --- a/packages/bloom/lib/test-utils.ts +++ b/packages/bloom/lib/test-utils.ts @@ -2,18 +2,18 @@ import TestUtils from '@redis/test-utils'; import RedisBloomModules from '.'; export default new TestUtils({ - dockerImageName: 'redislabs/rebloom', - dockerImageVersionArgument: 'redisbloom-version', - defaultDockerVersion: 'edge' + dockerImageName: 'redis/redis-stack', + dockerImageVersionArgument: 'redisbloom-version', + defaultDockerVersion: '7.4.0-v1' }); export const GLOBAL = { - SERVERS: { - OPEN: { - serverArguments: ['--loadmodule /usr/lib/redis/modules/redisbloom.so'], - clientOptions: { - modules: RedisBloomModules - } - } + SERVERS: { + OPEN: { + serverArguments: [], + clientOptions: { + modules: RedisBloomModules + } } + } }; diff --git a/packages/bloom/package.json b/packages/bloom/package.json index 8a9d9f7a87a..850ad802a5d 100644 --- a/packages/bloom/package.json +++ b/packages/bloom/package.json @@ -1,30 +1,24 @@ { "name": "@redis/bloom", - "version": "1.2.0", + "version": "2.0.0-next.3", "license": "MIT", - "main": "./dist/index.js", - "types": "./dist/index.d.ts", + "main": "./dist/lib/index.js", + "types": "./dist/lib/index.d.ts", "files": [ - "dist/" + "dist/", + "!dist/tsconfig.tsbuildinfo" ], "scripts": { - "test": "nyc -r text-summary -r lcov mocha -r source-map-support/register -r ts-node/register './lib/**/*.spec.ts'", - "build": "tsc", - "documentation": "typedoc" + "test": "nyc -r text-summary -r lcov mocha -r tsx './lib/**/*.spec.ts'" }, "peerDependencies": { - "@redis/client": "^1.0.0" + "@redis/client": "^2.0.0-next.4" }, "devDependencies": { - "@istanbuljs/nyc-config-typescript": "^1.0.2", - "@redis/test-utils": "*", - "@types/node": "^20.6.2", - "nyc": "^15.1.0", - "release-it": "^16.1.5", - "source-map-support": "^0.5.21", - "ts-node": "^10.9.1", - "typedoc": "^0.25.1", - "typescript": "^5.2.2" + "@redis/test-utils": "*" + }, + "engines": { + "node": ">= 18" }, "repository": { "type": "git", diff --git a/packages/client/.eslintrc.json b/packages/client/.eslintrc.json deleted file mode 100644 index 4536bc31338..00000000000 --- a/packages/client/.eslintrc.json +++ /dev/null @@ -1,15 +0,0 @@ -{ - "root": true, - "parser": "@typescript-eslint/parser", - "plugins": [ - "@typescript-eslint" - ], - "extends": [ - "eslint:recommended", - "plugin:@typescript-eslint/eslint-recommended", - "plugin:@typescript-eslint/recommended" - ], - "rules": { - "semi": [2, "always"] - } - } diff --git a/packages/client/.npmignore b/packages/client/.npmignore deleted file mode 100644 index d064e1d0db6..00000000000 --- a/packages/client/.npmignore +++ /dev/null @@ -1,10 +0,0 @@ -.nyc_output/ -coverage/ -documentation/ -lib/ -.eslintrc.json -.nycrc.json -.release-it.json -dump.rdb -index.ts -tsconfig.json diff --git a/packages/client/.release-it.json b/packages/client/.release-it.json index 035124348ca..3ae247ad371 100644 --- a/packages/client/.release-it.json +++ b/packages/client/.release-it.json @@ -5,6 +5,7 @@ "tagAnnotation": "Release ${tagName}" }, "npm": { + "versionArgs": ["--workspaces-update=false"], "publishArgs": ["--access", "public"] } } diff --git a/packages/client/index.ts b/packages/client/index.ts index 8b21c5d5a32..56cdf703ca3 100644 --- a/packages/client/index.ts +++ b/packages/client/index.ts @@ -1,24 +1,27 @@ -import RedisClient from './lib/client'; -import RedisCluster from './lib/cluster'; - -export { RedisClientType, RedisClientOptions } from './lib/client'; - -export { RedisModules, RedisFunctions, RedisScripts } from './lib/commands'; +export { RedisModules, RedisFunctions, RedisScripts, RespVersions, TypeMapping/*, CommandPolicies*/, RedisArgument } from './lib/RESP/types'; +export { RESP_TYPES } from './lib/RESP/decoder'; +export { VerbatimString } from './lib/RESP/verbatim-string'; +export { defineScript } from './lib/lua-script'; +// export * from './lib/errors'; +import RedisClient, { RedisClientOptions, RedisClientType } from './lib/client'; +export { RedisClientOptions, RedisClientType }; export const createClient = RedisClient.create; -export const commandOptions = RedisClient.commandOptions; - -export { RedisClusterType, RedisClusterOptions } from './lib/cluster'; +import { RedisClientPool, RedisPoolOptions, RedisClientPoolType } from './lib/client/pool'; +export { RedisClientPoolType, RedisPoolOptions }; +export const createClientPool = RedisClientPool.create; +import RedisCluster, { RedisClusterOptions, RedisClusterType } from './lib/cluster'; +export { RedisClusterType, RedisClusterOptions }; export const createCluster = RedisCluster.create; -export { defineScript } from './lib/lua-script'; - -export * from './lib/errors'; +import RedisSentinel from './lib/sentinel'; +export { RedisSentinelOptions, RedisSentinelType } from './lib/sentinel/types'; +export const createSentinel = RedisSentinel.create; -export { GeoReplyWith } from './lib/commands/generic-transformers'; +// export { GeoReplyWith } from './lib/commands/generic-transformers'; -export { SetOptions } from './lib/commands/SET'; +// export { SetOptions } from './lib/commands/SET'; -export { RedisFlushModes } from './lib/commands/FLUSHALL'; +// export { RedisFlushModes } from './lib/commands/FLUSHALL'; diff --git a/packages/client/lib/RESP/decoder.spec.ts b/packages/client/lib/RESP/decoder.spec.ts new file mode 100644 index 00000000000..c034815c9cd --- /dev/null +++ b/packages/client/lib/RESP/decoder.spec.ts @@ -0,0 +1,426 @@ +import { strict as assert } from 'node:assert'; +import { SinonSpy, spy } from 'sinon'; +import { Decoder, RESP_TYPES } from './decoder'; +import { BlobError, SimpleError } from '../errors'; +import { TypeMapping } from './types'; +import { VerbatimString } from './verbatim-string'; + +interface Test { + toWrite: Buffer; + typeMapping?: TypeMapping; + replies?: Array; + errorReplies?: Array; + pushReplies?: Array; +} + +function test(name: string, config: Test) { + describe(name, () => { + it('single chunk', () => { + const setup = setupTest(config); + setup.decoder.write(config.toWrite); + assertSpiesCalls(config, setup); + }); + + it('byte by byte', () => { + const setup = setupTest(config); + for (let i = 0; i < config.toWrite.length; i++) { + setup.decoder.write(config.toWrite.subarray(i, i + 1)); + } + assertSpiesCalls(config, setup); + }); + }) +} + +function setupTest(config: Test) { + const onReplySpy = spy(), + onErrorReplySpy = spy(), + onPushSpy = spy(); + + return { + decoder: new Decoder({ + getTypeMapping: () => config.typeMapping ?? {}, + onReply: onReplySpy, + onErrorReply: onErrorReplySpy, + onPush: onPushSpy + }), + onReplySpy, + onErrorReplySpy, + onPushSpy + }; +} + +function assertSpiesCalls(config: Test, spies: ReturnType) { + assertSpyCalls(spies.onReplySpy, config.replies); + assertSpyCalls(spies.onErrorReplySpy, config.errorReplies); + assertSpyCalls(spies.onPushSpy, config.pushReplies); +} + +function assertSpyCalls(spy: SinonSpy, replies?: Array) { + if (!replies) { + assert.equal(spy.callCount, 0); + return; + } + + assert.equal(spy.callCount, replies.length); + for (const [i, reply] of replies.entries()) { + assert.deepEqual( + spy.getCall(i).args, + [reply] + ); + } +} + +describe('RESP Decoder', () => { + test('Null', { + toWrite: Buffer.from('_\r\n'), + replies: [null] + }); + + describe('Boolean', () => { + test('true', { + toWrite: Buffer.from('#t\r\n'), + replies: [true] + }); + + test('false', { + toWrite: Buffer.from('#f\r\n'), + replies: [false] + }); + }); + + describe('Number', () => { + test('0', { + toWrite: Buffer.from(':0\r\n'), + replies: [0] + }); + + test('1', { + toWrite: Buffer.from(':+1\r\n'), + replies: [1] + }); + + test('+1', { + toWrite: Buffer.from(':+1\r\n'), + replies: [1] + }); + + test('-1', { + toWrite: Buffer.from(':-1\r\n'), + replies: [-1] + }); + + test('1 as string', { + typeMapping: { + [RESP_TYPES.NUMBER]: String + }, + toWrite: Buffer.from(':1\r\n'), + replies: ['1'] + }); + }); + + describe('BigNumber', () => { + test('0', { + toWrite: Buffer.from('(0\r\n'), + replies: [0n] + }); + + test('1', { + toWrite: Buffer.from('(1\r\n'), + replies: [1n] + }); + + test('+1', { + toWrite: Buffer.from('(+1\r\n'), + replies: [1n] + }); + + test('-1', { + toWrite: Buffer.from('(-1\r\n'), + replies: [-1n] + }); + + test('1 as string', { + typeMapping: { + [RESP_TYPES.BIG_NUMBER]: String + }, + toWrite: Buffer.from('(1\r\n'), + replies: ['1'] + }); + }); + + describe('Double', () => { + test('0', { + toWrite: Buffer.from(',0\r\n'), + replies: [0] + }); + + test('1', { + toWrite: Buffer.from(',1\r\n'), + replies: [1] + }); + + test('+1', { + toWrite: Buffer.from(',+1\r\n'), + replies: [1] + }); + + test('-1', { + toWrite: Buffer.from(',-1\r\n'), + replies: [-1] + }); + + test('1.1', { + toWrite: Buffer.from(',1.1\r\n'), + replies: [1.1] + }); + + test('nan', { + toWrite: Buffer.from(',nan\r\n'), + replies: [NaN] + }); + + test('inf', { + toWrite: Buffer.from(',inf\r\n'), + replies: [Infinity] + }); + + test('+inf', { + toWrite: Buffer.from(',+inf\r\n'), + replies: [Infinity] + }); + + test('-inf', { + toWrite: Buffer.from(',-inf\r\n'), + replies: [-Infinity] + }); + + test('1e1', { + toWrite: Buffer.from(',1e1\r\n'), + replies: [1e1] + }); + + test('-1.1E+1', { + toWrite: Buffer.from(',-1.1E+1\r\n'), + replies: [-1.1E+1] + }); + + test('1 as string', { + typeMapping: { + [RESP_TYPES.DOUBLE]: String + }, + toWrite: Buffer.from(',1\r\n'), + replies: ['1'] + }); + }); + + describe('SimpleString', () => { + test("'OK'", { + toWrite: Buffer.from('+OK\r\n'), + replies: ['OK'] + }); + + test("'OK' as Buffer", { + typeMapping: { + [RESP_TYPES.SIMPLE_STRING]: Buffer + }, + toWrite: Buffer.from('+OK\r\n'), + replies: [Buffer.from('OK')] + }); + }); + + describe('BlobString', () => { + test("''", { + toWrite: Buffer.from('$0\r\n\r\n'), + replies: [''] + }); + + test("'1234567890'", { + toWrite: Buffer.from('$10\r\n1234567890\r\n'), + replies: ['1234567890'] + }); + + test('null (RESP2 backwards compatibility)', { + toWrite: Buffer.from('$-1\r\n'), + replies: [null] + }); + + test("'OK' as Buffer", { + typeMapping: { + [RESP_TYPES.BLOB_STRING]: Buffer + }, + toWrite: Buffer.from('$2\r\nOK\r\n'), + replies: [Buffer.from('OK')] + }); + }); + + describe('VerbatimString', () => { + test("''", { + toWrite: Buffer.from('=4\r\ntxt:\r\n'), + replies: [''] + }); + + test("'123456'", { + toWrite: Buffer.from('=10\r\ntxt:123456\r\n'), + replies: ['123456'] + }); + + test("'OK' as VerbatimString", { + typeMapping: { + [RESP_TYPES.VERBATIM_STRING]: VerbatimString + }, + toWrite: Buffer.from('=6\r\ntxt:OK\r\n'), + replies: [new VerbatimString('txt', 'OK')] + }); + + test("'OK' as Buffer", { + typeMapping: { + [RESP_TYPES.VERBATIM_STRING]: Buffer + }, + toWrite: Buffer.from('=6\r\ntxt:OK\r\n'), + replies: [Buffer.from('OK')] + }); + }); + + test('SimpleError', { + toWrite: Buffer.from('-ERROR\r\n'), + errorReplies: [new SimpleError('ERROR')] + }); + + test('BlobError', { + toWrite: Buffer.from('!5\r\nERROR\r\n'), + errorReplies: [new BlobError('ERROR')] + }); + + describe('Array', () => { + test('[]', { + toWrite: Buffer.from('*0\r\n'), + replies: [[]] + }); + + test('[0..9]', { + toWrite: Buffer.from(`*10\r\n:0\r\n:1\r\n:2\r\n:3\r\n:4\r\n:5\r\n:6\r\n:7\r\n:8\r\n:9\r\n`), + replies: [[0, 1, 2, 3, 4, 5, 6, 7, 8, 9]] + }); + + test('with all types', { + toWrite: Buffer.from([ + '*13\r\n', + '_\r\n', + '#f\r\n', + ':0\r\n', + '(0\r\n', + ',0\r\n', + '+\r\n', + '$0\r\n\r\n', + '=4\r\ntxt:\r\n', + '-\r\n', + '!0\r\n\r\n', + '*0\r\n', + '~0\r\n', + '%0\r\n' + ].join('')), + replies: [[ + null, + false, + 0, + 0n, + 0, + '', + '', + '', + new SimpleError(''), + new BlobError(''), + [], + [], + Object.create(null) + ]] + }); + + test('null (RESP2 backwards compatibility)', { + toWrite: Buffer.from('*-1\r\n'), + replies: [null] + }); + }); + + describe('Set', () => { + test('empty', { + toWrite: Buffer.from('~0\r\n'), + replies: [[]] + }); + + test('of 0..9', { + toWrite: Buffer.from(`~10\r\n:0\r\n:1\r\n:2\r\n:3\r\n:4\r\n:5\r\n:6\r\n:7\r\n:8\r\n:9\r\n`), + replies: [[0, 1, 2, 3, 4, 5, 6, 7, 8, 9]] + }); + + test('0..9 as Set', { + typeMapping: { + [RESP_TYPES.SET]: Set + }, + toWrite: Buffer.from(`~10\r\n:0\r\n:1\r\n:2\r\n:3\r\n:4\r\n:5\r\n:6\r\n:7\r\n:8\r\n:9\r\n`), + replies: [new Set([0, 1, 2, 3, 4, 5, 6, 7, 8, 9])] + }); + }); + + describe('Map', () => { + test('{}', { + toWrite: Buffer.from('%0\r\n'), + replies: [Object.create(null)] + }); + + test("{ '0'..'9': }", { + toWrite: Buffer.from(`%10\r\n+0\r\n+0\r\n+1\r\n+1\r\n+2\r\n+2\r\n+3\r\n+3\r\n+4\r\n+4\r\n+5\r\n+5\r\n+6\r\n+6\r\n+7\r\n+7\r\n+8\r\n+8\r\n+9\r\n+9\r\n`), + replies: [Object.create(null, { + 0: { value: '0', enumerable: true }, + 1: { value: '1', enumerable: true }, + 2: { value: '2', enumerable: true }, + 3: { value: '3', enumerable: true }, + 4: { value: '4', enumerable: true }, + 5: { value: '5', enumerable: true }, + 6: { value: '6', enumerable: true }, + 7: { value: '7', enumerable: true }, + 8: { value: '8', enumerable: true }, + 9: { value: '9', enumerable: true } + })] + }); + + test("{ '0'..'9': } as Map", { + typeMapping: { + [RESP_TYPES.MAP]: Map + }, + toWrite: Buffer.from(`%10\r\n+0\r\n+0\r\n+1\r\n+1\r\n+2\r\n+2\r\n+3\r\n+3\r\n+4\r\n+4\r\n+5\r\n+5\r\n+6\r\n+6\r\n+7\r\n+7\r\n+8\r\n+8\r\n+9\r\n+9\r\n`), + replies: [new Map([ + ['0', '0'], + ['1', '1'], + ['2', '2'], + ['3', '3'], + ['4', '4'], + ['5', '5'], + ['6', '6'], + ['7', '7'], + ['8', '8'], + ['9', '9'] + ])] + }); + + test("{ '0'..'9': } as Array", { + typeMapping: { + [RESP_TYPES.MAP]: Array + }, + toWrite: Buffer.from(`%10\r\n+0\r\n+0\r\n+1\r\n+1\r\n+2\r\n+2\r\n+3\r\n+3\r\n+4\r\n+4\r\n+5\r\n+5\r\n+6\r\n+6\r\n+7\r\n+7\r\n+8\r\n+8\r\n+9\r\n+9\r\n`), + replies: [['0', '0', '1', '1', '2', '2', '3', '3', '4', '4', '5', '5', '6', '6', '7', '7', '8', '8', '9', '9']] + }); + }); + + describe('Push', () => { + test('[]', { + toWrite: Buffer.from('>0\r\n'), + pushReplies: [[]] + }); + + test('[0..9]', { + toWrite: Buffer.from(`>10\r\n:0\r\n:1\r\n:2\r\n:3\r\n:4\r\n:5\r\n:6\r\n:7\r\n:8\r\n:9\r\n`), + pushReplies: [[0, 1, 2, 3, 4, 5, 6, 7, 8, 9]] + }); + }); +}); diff --git a/packages/client/lib/RESP/decoder.ts b/packages/client/lib/RESP/decoder.ts new file mode 100644 index 00000000000..2485ea23b37 --- /dev/null +++ b/packages/client/lib/RESP/decoder.ts @@ -0,0 +1,1178 @@ +// @ts-nocheck +import { VerbatimString } from './verbatim-string'; +import { SimpleError, BlobError, ErrorReply } from '../errors'; +import { TypeMapping } from './types'; + +// https://github.com/redis/redis-specifications/blob/master/protocol/RESP3.md +export const RESP_TYPES = { + NULL: 95, // _ + BOOLEAN: 35, // # + NUMBER: 58, // : + BIG_NUMBER: 40, // ( + DOUBLE: 44, // , + SIMPLE_STRING: 43, // + + BLOB_STRING: 36, // $ + VERBATIM_STRING: 61, // = + SIMPLE_ERROR: 45, // - + BLOB_ERROR: 33, // ! + ARRAY: 42, // * + SET: 126, // ~ + MAP: 37, // % + PUSH: 62 // > +} as const; + +const ASCII = { + '\r': 13, + 't': 116, + '+': 43, + '-': 45, + '0': 48, + '.': 46, + 'i': 105, + 'n': 110, + 'E': 69, + 'e': 101 +} as const; + +export const PUSH_TYPE_MAPPING = { + [RESP_TYPES.BLOB_STRING]: Buffer +}; + +// this was written with performance in mind, so it's not very readable... sorry :( + +interface DecoderOptions { + onReply(reply: any): unknown; + onErrorReply(err: ErrorReply): unknown; + onPush(push: Array): unknown; + getTypeMapping(): TypeMapping; +} + +export class Decoder { + onReply; + onErrorReply; + onPush; + getTypeMapping; + #cursor = 0; + #next; + + constructor(config: DecoderOptions) { + this.onReply = config.onReply; + this.onErrorReply = config.onErrorReply; + this.onPush = config.onPush; + this.getTypeMapping = config.getTypeMapping; + } + + reset() { + this.#cursor = 0; + this.#next = undefined; + } + + write(chunk) { + if (this.#cursor >= chunk.length) { + this.#cursor -= chunk.length; + return; + } + + if (this.#next) { + if (this.#next(chunk) || this.#cursor >= chunk.length) { + this.#cursor -= chunk.length; + return; + } + } + + do { + const type = chunk[this.#cursor]; + if (++this.#cursor === chunk.length) { + this.#next = this.#continueDecodeTypeValue.bind(this, type); + break; + } + + if (this.#decodeTypeValue(type, chunk)) { + break; + } + } while (this.#cursor < chunk.length); + this.#cursor -= chunk.length; + } + + #continueDecodeTypeValue(type, chunk) { + this.#next = undefined; + return this.#decodeTypeValue(type, chunk); + } + + #decodeTypeValue(type, chunk) { + switch (type) { + case RESP_TYPES.NULL: + this.onReply(this.#decodeNull()); + return false; + + case RESP_TYPES.BOOLEAN: + return this.#handleDecodedValue( + this.onReply, + this.#decodeBoolean(chunk) + ); + + case RESP_TYPES.NUMBER: + return this.#handleDecodedValue( + this.onReply, + this.#decodeNumber( + this.getTypeMapping()[RESP_TYPES.NUMBER], + chunk + ) + ); + + case RESP_TYPES.BIG_NUMBER: + return this.#handleDecodedValue( + this.onReply, + this.#decodeBigNumber( + this.getTypeMapping()[RESP_TYPES.BIG_NUMBER], + chunk + ) + ); + + case RESP_TYPES.DOUBLE: + return this.#handleDecodedValue( + this.onReply, + this.#decodeDouble( + this.getTypeMapping()[RESP_TYPES.DOUBLE], + chunk + ) + ); + + case RESP_TYPES.SIMPLE_STRING: + return this.#handleDecodedValue( + this.onReply, + this.#decodeSimpleString( + this.getTypeMapping()[RESP_TYPES.SIMPLE_STRING], + chunk + ) + ); + + case RESP_TYPES.BLOB_STRING: + return this.#handleDecodedValue( + this.onReply, + this.#decodeBlobString( + this.getTypeMapping()[RESP_TYPES.BLOB_STRING], + chunk + ) + ); + + case RESP_TYPES.VERBATIM_STRING: + return this.#handleDecodedValue( + this.onReply, + this.#decodeVerbatimString( + this.getTypeMapping()[RESP_TYPES.VERBATIM_STRING], + chunk + ) + ); + + case RESP_TYPES.SIMPLE_ERROR: + return this.#handleDecodedValue( + this.onErrorReply, + this.#decodeSimpleError(chunk) + ); + + case RESP_TYPES.BLOB_ERROR: + return this.#handleDecodedValue( + this.onErrorReply, + this.#decodeBlobError(chunk) + ); + + case RESP_TYPES.ARRAY: + return this.#handleDecodedValue( + this.onReply, + this.#decodeArray(this.getTypeMapping(), chunk) + ); + + case RESP_TYPES.SET: + return this.#handleDecodedValue( + this.onReply, + this.#decodeSet(this.getTypeMapping(), chunk) + ); + + case RESP_TYPES.MAP: + return this.#handleDecodedValue( + this.onReply, + this.#decodeMap(this.getTypeMapping(), chunk) + ); + + case RESP_TYPES.PUSH: + return this.#handleDecodedValue( + this.onPush, + this.#decodeArray(PUSH_TYPE_MAPPING, chunk) + ); + + default: + throw new Error(`Unknown RESP type ${type} "${String.fromCharCode(type)}"`); + } + } + + #handleDecodedValue(cb, value) { + if (typeof value === 'function') { + this.#next = this.#continueDecodeValue.bind(this, cb, value); + return true; + } + + cb(value); + return false; + } + + #continueDecodeValue(cb, next, chunk) { + this.#next = undefined; + return this.#handleDecodedValue(cb, next(chunk)); + } + + #decodeNull() { + this.#cursor += 2; // skip \r\n + return null; + } + + #decodeBoolean(chunk) { + const boolean = chunk[this.#cursor] === ASCII.t; + this.#cursor += 3; // skip {t | f}\r\n + return boolean; + } + + #decodeNumber(type, chunk) { + if (type === String) { + return this.#decodeSimpleString(String, chunk); + } + + switch (chunk[this.#cursor]) { + case ASCII['+']: + return this.#maybeDecodeNumberValue(false, chunk); + + case ASCII['-']: + return this.#maybeDecodeNumberValue(true, chunk); + + default: + return this.#decodeNumberValue( + false, + this.#decodeUnsingedNumber.bind(this, 0), + chunk + ); + } + } + + #maybeDecodeNumberValue(isNegative, chunk) { + const cb = this.#decodeUnsingedNumber.bind(this, 0); + return ++this.#cursor === chunk.length ? + this.#decodeNumberValue.bind(this, isNegative, cb) : + this.#decodeNumberValue(isNegative, cb, chunk); + } + + #decodeNumberValue(isNegative, numberCb, chunk) { + const number = numberCb(chunk); + return typeof number === 'function' ? + this.#decodeNumberValue.bind(this, isNegative, number) : + isNegative ? -number : number; + } + + #decodeUnsingedNumber(number, chunk) { + let cursor = this.#cursor; + do { + const byte = chunk[cursor]; + if (byte === ASCII['\r']) { + this.#cursor = cursor + 2; // skip \r\n + return number; + } + number = number * 10 + byte - ASCII['0']; + } while (++cursor < chunk.length); + + this.#cursor = cursor; + return this.#decodeUnsingedNumber.bind(this, number); + } + + #decodeBigNumber(type, chunk) { + if (type === String) { + return this.#decodeSimpleString(String, chunk); + } + + switch (chunk[this.#cursor]) { + case ASCII['+']: + return this.#maybeDecodeBigNumberValue(false, chunk); + + case ASCII['-']: + return this.#maybeDecodeBigNumberValue(true, chunk); + + default: + return this.#decodeBigNumberValue( + false, + this.#decodeUnsingedBigNumber.bind(this, 0n), + chunk + ); + } + } + + #maybeDecodeBigNumberValue(isNegative, chunk) { + const cb = this.#decodeUnsingedBigNumber.bind(this, 0n); + return ++this.#cursor === chunk.length ? + this.#decodeBigNumberValue.bind(this, isNegative, cb) : + this.#decodeBigNumberValue(isNegative, cb, chunk); + } + + #decodeBigNumberValue(isNegative, bigNumberCb, chunk) { + const bigNumber = bigNumberCb(chunk); + return typeof bigNumber === 'function' ? + this.#decodeBigNumberValue.bind(this, isNegative, bigNumber) : + isNegative ? -bigNumber : bigNumber; + } + + #decodeUnsingedBigNumber(bigNumber, chunk) { + let cursor = this.#cursor; + do { + const byte = chunk[cursor]; + if (byte === ASCII['\r']) { + this.#cursor = cursor + 2; // skip \r\n + return bigNumber; + } + bigNumber = bigNumber * 10n + BigInt(byte - ASCII['0']); + } while (++cursor < chunk.length); + + this.#cursor = cursor; + return this.#decodeUnsingedBigNumber.bind(this, bigNumber); + } + + #decodeDouble(type, chunk) { + if (type === String) { + return this.#decodeSimpleString(String, chunk); + } + + switch (chunk[this.#cursor]) { + case ASCII.n: + this.#cursor += 5; // skip nan\r\n + return NaN; + + case ASCII['+']: + return this.#maybeDecodeDoubleInteger(false, chunk); + + case ASCII['-']: + return this.#maybeDecodeDoubleInteger(true, chunk); + + default: + return this.#decodeDoubleInteger(false, 0, chunk); + } + } + + #maybeDecodeDoubleInteger(isNegative, chunk) { + return ++this.#cursor === chunk.length ? + this.#decodeDoubleInteger.bind(this, isNegative, 0) : + this.#decodeDoubleInteger(isNegative, 0, chunk); + } + + #decodeDoubleInteger(isNegative, integer, chunk) { + if (chunk[this.#cursor] === ASCII.i) { + this.#cursor += 5; // skip inf\r\n + return isNegative ? -Infinity : Infinity; + } + + return this.#continueDecodeDoubleInteger(isNegative, integer, chunk); + } + + #continueDecodeDoubleInteger(isNegative, integer, chunk) { + let cursor = this.#cursor; + do { + const byte = chunk[cursor]; + switch (byte) { + case ASCII['.']: + this.#cursor = cursor + 1; // skip . + return this.#cursor < chunk.length ? + this.#decodeDoubleDecimal(isNegative, 0, integer, chunk) : + this.#decodeDoubleDecimal.bind(this, isNegative, 0, integer); + + case ASCII.E: + case ASCII.e: + this.#cursor = cursor + 1; // skip E/e + const i = isNegative ? -integer : integer; + return this.#cursor < chunk.length ? + this.#decodeDoubleExponent(i, chunk) : + this.#decodeDoubleExponent.bind(this, i); + + case ASCII['\r']: + this.#cursor = cursor + 2; // skip \r\n + return isNegative ? -integer : integer; + + default: + integer = integer * 10 + byte - ASCII['0']; + } + } while (++cursor < chunk.length); + + this.#cursor = cursor; + return this.#continueDecodeDoubleInteger.bind(this, isNegative, integer); + } + + // Precalculated multipliers for decimal points to improve performance + // "... about 15 to 17 decimal places ..." + // https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Number#:~:text=about%2015%20to%2017%20decimal%20places + static #DOUBLE_DECIMAL_MULTIPLIERS = [ + 1e-1, 1e-2, 1e-3, 1e-4, 1e-5, 1e-6, + 1e-7, 1e-8, 1e-9, 1e-10, 1e-11, 1e-12, + 1e-13, 1e-14, 1e-15, 1e-16, 1e-17 + ]; + + #decodeDoubleDecimal(isNegative, decimalIndex, double, chunk) { + let cursor = this.#cursor; + do { + const byte = chunk[cursor]; + switch (byte) { + case ASCII.E: + case ASCII.e: + this.#cursor = cursor + 1; // skip E/e + const d = isNegative ? -double : double; + return this.#cursor === chunk.length ? + this.#decodeDoubleExponent.bind(this, d) : + this.#decodeDoubleExponent(d, chunk); + + case ASCII['\r']: + this.#cursor = cursor + 2; // skip \r\n + return isNegative ? -double : double; + } + + if (decimalIndex < Decoder.#DOUBLE_DECIMAL_MULTIPLIERS.length) { + double += (byte - ASCII['0']) * Decoder.#DOUBLE_DECIMAL_MULTIPLIERS[decimalIndex++]; + } + } while (++cursor < chunk.length); + + this.#cursor = cursor; + return this.#decodeDoubleDecimal.bind(this, isNegative, decimalIndex, double); + } + + #decodeDoubleExponent(double, chunk) { + switch (chunk[this.#cursor]) { + case ASCII['+']: + return ++this.#cursor === chunk.length ? + this.#continueDecodeDoubleExponent.bind(this, false, double, 0) : + this.#continueDecodeDoubleExponent(false, double, 0, chunk); + + case ASCII['-']: + return ++this.#cursor === chunk.length ? + this.#continueDecodeDoubleExponent.bind(this, true, double, 0) : + this.#continueDecodeDoubleExponent(true, double, 0, chunk); + } + + return this.#continueDecodeDoubleExponent(false, double, 0, chunk); + } + + #continueDecodeDoubleExponent(isNegative, double, exponent, chunk) { + let cursor = this.#cursor; + do { + const byte = chunk[cursor]; + if (byte === ASCII['\r']) { + this.#cursor = cursor + 2; // skip \r\n + return double * 10 ** (isNegative ? -exponent : exponent); + } + + exponent = exponent * 10 + byte - ASCII['0']; + } while (++cursor < chunk.length); + + this.#cursor = cursor; + return this.#continueDecodeDoubleExponent.bind(this, isNegative, double, exponent); + } + + #findCRLF(chunk, cursor) { + while (chunk[cursor] !== ASCII['\r']) { + if (++cursor === chunk.length) { + this.#cursor = chunk.length; + return -1; + } + } + + this.#cursor = cursor + 2; // skip \r\n + return cursor; + } + + #decodeSimpleString(type, chunk) { + const start = this.#cursor, + crlfIndex = this.#findCRLF(chunk, start); + if (crlfIndex === -1) { + return this.#continueDecodeSimpleString.bind( + this, + [chunk.subarray(start)], + type + ); + } + + const slice = chunk.subarray(start, crlfIndex); + return type === Buffer ? + slice : + slice.toString(); + } + + #continueDecodeSimpleString(chunks, type, chunk) { + const start = this.#cursor, + crlfIndex = this.#findCRLF(chunk, start); + if (crlfIndex === -1) { + chunks.push(chunk.subarray(start)); + return this.#continueDecodeSimpleString.bind(this, chunks, type); + } + + chunks.push(chunk.subarray(start, crlfIndex)); + return type === Buffer ? + Buffer.concat(chunks) : + chunks.join(''); + } + + #decodeBlobString(type, chunk) { + // RESP 2 bulk string null + // https://github.com/redis/redis-specifications/blob/master/protocol/RESP2.md#resp-bulk-strings + if (chunk[this.#cursor] === ASCII['-']) { + this.#cursor += 4; // skip -1\r\n + return null; + } + + const length = this.#decodeUnsingedNumber(0, chunk); + if (typeof length === 'function') { + return this.#continueDecodeBlobStringLength.bind(this, length, type); + } else if (this.#cursor >= chunk.length) { + return this.#decodeBlobStringWithLength.bind(this, length, type); + } + + return this.#decodeBlobStringWithLength(length, type, chunk); + } + + #continueDecodeBlobStringLength(lengthCb, type, chunk) { + const length = lengthCb(chunk); + if (typeof length === 'function') { + return this.#continueDecodeBlobStringLength.bind(this, length, type); + } else if (this.#cursor >= chunk.length) { + return this.#decodeBlobStringWithLength.bind(this, length, type); + } + + return this.#decodeBlobStringWithLength(length, type, chunk); + } + + #decodeStringWithLength(length, skip, type, chunk) { + const end = this.#cursor + length; + if (end >= chunk.length) { + const slice = chunk.subarray(this.#cursor); + this.#cursor = chunk.length; + return this.#continueDecodeStringWithLength.bind( + this, + length - slice.length, + [slice], + skip, + type + ); + } + + const slice = chunk.subarray(this.#cursor, end); + this.#cursor = end + skip; + return type === Buffer ? + slice : + slice.toString(); + } + + #continueDecodeStringWithLength(length, chunks, skip, type, chunk) { + const end = this.#cursor + length; + if (end >= chunk.length) { + const slice = chunk.subarray(this.#cursor); + chunks.push(slice); + this.#cursor = chunk.length; + return this.#continueDecodeStringWithLength.bind( + this, + length - slice.length, + chunks, + skip, + type + ); + } + + chunks.push(chunk.subarray(this.#cursor, end)); + this.#cursor = end + skip; + return type === Buffer ? + Buffer.concat(chunks) : + chunks.join(''); + } + + #decodeBlobStringWithLength(length, type, chunk) { + return this.#decodeStringWithLength(length, 2, type, chunk); + } + + #decodeVerbatimString(type, chunk) { + return this.#continueDecodeVerbatimStringLength( + this.#decodeUnsingedNumber.bind(this, 0), + type, + chunk + ); + } + + #continueDecodeVerbatimStringLength(lengthCb, type, chunk) { + const length = lengthCb(chunk); + return typeof length === 'function' ? + this.#continueDecodeVerbatimStringLength.bind(this, length, type) : + this.#decodeVerbatimStringWithLength(length, type, chunk); + } + + #decodeVerbatimStringWithLength(length, type, chunk) { + const stringLength = length - 4; // skip : + if (type === VerbatimString) { + return this.#decodeVerbatimStringFormat(stringLength, chunk); + } + + this.#cursor += 4; // skip : + return this.#cursor >= chunk.length ? + this.#decodeBlobStringWithLength.bind(this, stringLength, type) : + this.#decodeBlobStringWithLength(stringLength, type, chunk); + } + + #decodeVerbatimStringFormat(stringLength, chunk) { + const formatCb = this.#decodeStringWithLength.bind(this, 3, 1, String); + return this.#cursor >= chunk.length ? + this.#continueDecodeVerbatimStringFormat.bind(this, stringLength, formatCb) : + this.#continueDecodeVerbatimStringFormat(stringLength, formatCb, chunk); + } + + #continueDecodeVerbatimStringFormat(stringLength, formatCb, chunk) { + const format = formatCb(chunk); + return typeof format === 'function' ? + this.#continueDecodeVerbatimStringFormat.bind(this, stringLength, format) : + this.#decodeVerbatimStringWithFormat(stringLength, format, chunk); + } + + #decodeVerbatimStringWithFormat(stringLength, format, chunk) { + return this.#continueDecodeVerbatimStringWithFormat( + format, + this.#decodeBlobStringWithLength.bind(this, stringLength, String), + chunk + ); + } + + #continueDecodeVerbatimStringWithFormat(format, stringCb, chunk) { + const string = stringCb(chunk); + return typeof string === 'function' ? + this.#continueDecodeVerbatimStringWithFormat.bind(this, format, string) : + new VerbatimString(format, string); + } + + #decodeSimpleError(chunk) { + const string = this.#decodeSimpleString(String, chunk); + return typeof string === 'function' ? + this.#continueDecodeSimpleError.bind(this, string) : + new SimpleError(string); + } + + #continueDecodeSimpleError(stringCb, chunk) { + const string = stringCb(chunk); + return typeof string === 'function' ? + this.#continueDecodeSimpleError.bind(this, string) : + new SimpleError(string); + } + + #decodeBlobError(chunk) { + const string = this.#decodeBlobString(String, chunk); + return typeof string === 'function' ? + this.#continueDecodeBlobError.bind(this, string) : + new BlobError(string); + } + + #continueDecodeBlobError(stringCb, chunk) { + const string = stringCb(chunk); + return typeof string === 'function' ? + this.#continueDecodeBlobError.bind(this, string) : + new BlobError(string); + } + + #decodeNestedType(typeMapping, chunk) { + const type = chunk[this.#cursor]; + return ++this.#cursor === chunk.length ? + this.#decodeNestedTypeValue.bind(this, type, typeMapping) : + this.#decodeNestedTypeValue(type, typeMapping, chunk); + } + + #decodeNestedTypeValue(type, typeMapping, chunk) { + switch (type) { + case RESP_TYPES.NULL: + return this.#decodeNull(); + + case RESP_TYPES.BOOLEAN: + return this.#decodeBoolean(chunk); + + case RESP_TYPES.NUMBER: + return this.#decodeNumber(typeMapping[RESP_TYPES.NUMBER], chunk); + + case RESP_TYPES.BIG_NUMBER: + return this.#decodeBigNumber(typeMapping[RESP_TYPES.BIG_NUMBER], chunk); + + case RESP_TYPES.DOUBLE: + return this.#decodeDouble(typeMapping[RESP_TYPES.DOUBLE], chunk); + + case RESP_TYPES.SIMPLE_STRING: + return this.#decodeSimpleString(typeMapping[RESP_TYPES.SIMPLE_STRING], chunk); + + case RESP_TYPES.BLOB_STRING: + return this.#decodeBlobString(typeMapping[RESP_TYPES.BLOB_STRING], chunk); + + case RESP_TYPES.VERBATIM_STRING: + return this.#decodeVerbatimString(typeMapping[RESP_TYPES.VERBATIM_STRING], chunk); + + case RESP_TYPES.SIMPLE_ERROR: + return this.#decodeSimpleError(chunk); + + case RESP_TYPES.BLOB_ERROR: + return this.#decodeBlobError(chunk); + + case RESP_TYPES.ARRAY: + return this.#decodeArray(typeMapping, chunk); + + case RESP_TYPES.SET: + return this.#decodeSet(typeMapping, chunk); + + case RESP_TYPES.MAP: + return this.#decodeMap(typeMapping, chunk); + + default: + throw new Error(`Unknown RESP type ${type} "${String.fromCharCode(type)}"`); + } + } + + #decodeArray(typeMapping, chunk) { + // RESP 2 null + // https://github.com/redis/redis-specifications/blob/master/protocol/RESP2.md#resp-arrays + if (chunk[this.#cursor] === ASCII['-']) { + this.#cursor += 4; // skip -1\r\n + return null; + } + + return this.#decodeArrayWithLength( + this.#decodeUnsingedNumber(0, chunk), + typeMapping, + chunk + ); + } + + #decodeArrayWithLength(length, typeMapping, chunk) { + return typeof length === 'function' ? + this.#continueDecodeArrayLength.bind(this, length, typeMapping) : + this.#decodeArrayItems( + new Array(length), + 0, + typeMapping, + chunk + ); + } + + #continueDecodeArrayLength(lengthCb, typeMapping, chunk) { + return this.#decodeArrayWithLength( + lengthCb(chunk), + typeMapping, + chunk + ); + } + + #decodeArrayItems(array, filled, typeMapping, chunk) { + for (let i = filled; i < array.length; i++) { + if (this.#cursor >= chunk.length) { + return this.#decodeArrayItems.bind( + this, + array, + i, + typeMapping + ); + } + + const item = this.#decodeNestedType(typeMapping, chunk); + if (typeof item === 'function') { + return this.#continueDecodeArrayItems.bind( + this, + array, + i, + item, + typeMapping + ); + } + + array[i] = item; + } + + return array; + } + + #continueDecodeArrayItems(array, filled, itemCb, typeMapping, chunk) { + const item = itemCb(chunk); + if (typeof item === 'function') { + return this.#continueDecodeArrayItems.bind( + this, + array, + filled, + item, + typeMapping + ); + } + + array[filled++] = item; + + return this.#decodeArrayItems(array, filled, typeMapping, chunk); + } + + #decodeSet(typeMapping, chunk) { + const length = this.#decodeUnsingedNumber(0, chunk); + if (typeof length === 'function') { + return this.#continueDecodeSetLength.bind(this, length, typeMapping); + } + + return this.#decodeSetItems( + length, + typeMapping, + chunk + ); + } + + #continueDecodeSetLength(lengthCb, typeMapping, chunk) { + const length = lengthCb(chunk); + return typeof length === 'function' ? + this.#continueDecodeSetLength.bind(this, length, typeMapping) : + this.#decodeSetItems(length, typeMapping, chunk); + } + + #decodeSetItems(length, typeMapping, chunk) { + return typeMapping[RESP_TYPES.SET] === Set ? + this.#decodeSetAsSet( + new Set(), + length, + typeMapping, + chunk + ) : + this.#decodeArrayItems( + new Array(length), + 0, + typeMapping, + chunk + ); + } + + #decodeSetAsSet(set, remaining, typeMapping, chunk) { + // using `remaining` instead of `length` & `set.size` to make it work even if the set contains duplicates + while (remaining > 0) { + if (this.#cursor >= chunk.length) { + return this.#decodeSetAsSet.bind( + this, + set, + remaining, + typeMapping + ); + } + + const item = this.#decodeNestedType(typeMapping, chunk); + if (typeof item === 'function') { + return this.#continueDecodeSetAsSet.bind( + this, + set, + remaining, + item, + typeMapping + ); + } + + set.add(item); + --remaining; + } + + return set; + } + + #continueDecodeSetAsSet(set, remaining, itemCb, typeMapping, chunk) { + const item = itemCb(chunk); + if (typeof item === 'function') { + return this.#continueDecodeSetAsSet.bind( + this, + set, + remaining, + item, + typeMapping + ); + } + + set.add(item); + + return this.#decodeSetAsSet(set, remaining - 1, typeMapping, chunk); + } + + #decodeMap(typeMapping, chunk) { + const length = this.#decodeUnsingedNumber(0, chunk); + if (typeof length === 'function') { + return this.#continueDecodeMapLength.bind(this, length, typeMapping); + } + + return this.#decodeMapItems( + length, + typeMapping, + chunk + ); + } + + #continueDecodeMapLength(lengthCb, typeMapping, chunk) { + const length = lengthCb(chunk); + return typeof length === 'function' ? + this.#continueDecodeMapLength.bind(this, length, typeMapping) : + this.#decodeMapItems(length, typeMapping, chunk); + } + + #decodeMapItems(length, typeMapping, chunk) { + switch (typeMapping[RESP_TYPES.MAP]) { + case Map: + return this.#decodeMapAsMap( + new Map(), + length, + typeMapping, + chunk + ); + + case Array: + return this.#decodeArrayItems( + new Array(length * 2), + 0, + typeMapping, + chunk + ); + + default: + return this.#decodeMapAsObject( + Object.create(null), + length, + typeMapping, + chunk + ); + } + } + + #decodeMapAsMap(map, remaining, typeMapping, chunk) { + // using `remaining` instead of `length` & `map.size` to make it work even if the map contains duplicate keys + while (remaining > 0) { + if (this.#cursor >= chunk.length) { + return this.#decodeMapAsMap.bind( + this, + map, + remaining, + typeMapping + ); + } + + const key = this.#decodeMapKey(typeMapping, chunk); + if (typeof key === 'function') { + return this.#continueDecodeMapKey.bind( + this, + map, + remaining, + key, + typeMapping + ); + } + + if (this.#cursor >= chunk.length) { + return this.#continueDecodeMapValue.bind( + this, + map, + remaining, + key, + this.#decodeNestedType.bind(this, typeMapping), + typeMapping + ); + } + + const value = this.#decodeNestedType(typeMapping, chunk); + if (typeof value === 'function') { + return this.#continueDecodeMapValue.bind( + this, + map, + remaining, + key, + value, + typeMapping + ); + } + + map.set(key, value); + --remaining; + } + + return map; + } + + #decodeMapKey(typeMapping, chunk) { + const type = chunk[this.#cursor]; + return ++this.#cursor === chunk.length ? + this.#decodeMapKeyValue.bind(this, type, typeMapping) : + this.#decodeMapKeyValue(type, typeMapping, chunk); + } + + #decodeMapKeyValue(type, typeMapping, chunk) { + switch (type) { + // decode simple string map key as string (and not as buffer) + case RESP_TYPES.SIMPLE_STRING: + return this.#decodeSimpleString(String, chunk); + + // decode blob string map key as string (and not as buffer) + case RESP_TYPES.BLOB_STRING: + return this.#decodeBlobString(String, chunk); + + default: + return this.#decodeNestedTypeValue(type, typeMapping, chunk); + } + } + + #continueDecodeMapKey(map, remaining, keyCb, typeMapping, chunk) { + const key = keyCb(chunk); + if (typeof key === 'function') { + return this.#continueDecodeMapKey.bind( + this, + map, + remaining, + key, + typeMapping + ); + } + + if (this.#cursor >= chunk.length) { + return this.#continueDecodeMapValue.bind( + this, + map, + remaining, + key, + this.#decodeNestedType.bind(this, typeMapping), + typeMapping + ); + } + + const value = this.#decodeNestedType(typeMapping, chunk); + if (typeof value === 'function') { + return this.#continueDecodeMapValue.bind( + this, + map, + remaining, + key, + value, + typeMapping + ); + } + + map.set(key, value); + return this.#decodeMapAsMap(map, remaining - 1, typeMapping, chunk); + } + + #continueDecodeMapValue(map, remaining, key, valueCb, typeMapping, chunk) { + const value = valueCb(chunk); + if (typeof value === 'function') { + return this.#continueDecodeMapValue.bind( + this, + map, + remaining, + key, + value, + typeMapping + ); + } + + map.set(key, value); + + return this.#decodeMapAsMap(map, remaining - 1, typeMapping, chunk); + } + + #decodeMapAsObject(object, remaining, typeMapping, chunk) { + while (remaining > 0) { + if (this.#cursor >= chunk.length) { + return this.#decodeMapAsObject.bind( + this, + object, + remaining, + typeMapping + ); + } + + const key = this.#decodeMapKey(typeMapping, chunk); + if (typeof key === 'function') { + return this.#continueDecodeMapAsObjectKey.bind( + this, + object, + remaining, + key, + typeMapping + ); + } + + if (this.#cursor >= chunk.length) { + return this.#continueDecodeMapAsObjectValue.bind( + this, + object, + remaining, + key, + this.#decodeNestedType.bind(this, typeMapping), + typeMapping + ); + } + + const value = this.#decodeNestedType(typeMapping, chunk); + if (typeof value === 'function') { + return this.#continueDecodeMapAsObjectValue.bind( + this, + object, + remaining, + key, + value, + typeMapping + ); + } + + object[key] = value; + --remaining; + } + + return object; + } + + #continueDecodeMapAsObjectKey(object, remaining, keyCb, typeMapping, chunk) { + const key = keyCb(chunk); + if (typeof key === 'function') { + return this.#continueDecodeMapAsObjectKey.bind( + this, + object, + remaining, + key, + typeMapping + ); + } + + if (this.#cursor >= chunk.length) { + return this.#continueDecodeMapAsObjectValue.bind( + this, + object, + remaining, + key, + this.#decodeNestedType.bind(this, typeMapping), + typeMapping + ); + } + + const value = this.#decodeNestedType(typeMapping, chunk); + if (typeof value === 'function') { + return this.#continueDecodeMapAsObjectValue.bind( + this, + object, + remaining, + key, + value, + typeMapping + ); + } + + object[key] = value; + + return this.#decodeMapAsObject(object, remaining - 1, typeMapping, chunk); + } + + #continueDecodeMapAsObjectValue(object, remaining, key, valueCb, typeMapping, chunk) { + const value = valueCb(chunk); + if (typeof value === 'function') { + return this.#continueDecodeMapAsObjectValue.bind( + this, + object, + remaining, + key, + value, + typeMapping + ); + } + + object[key] = value; + + return this.#decodeMapAsObject(object, remaining - 1, typeMapping, chunk); + } +} diff --git a/packages/client/lib/RESP/encoder.spec.ts b/packages/client/lib/RESP/encoder.spec.ts new file mode 100644 index 00000000000..2cbdc7d0b24 --- /dev/null +++ b/packages/client/lib/RESP/encoder.spec.ts @@ -0,0 +1,33 @@ +import { strict as assert } from 'node:assert'; +import { describe } from 'mocha'; +import encodeCommand from './encoder'; + +describe('RESP Encoder', () => { + it('1 byte', () => { + assert.deepEqual( + encodeCommand(['a', 'z']), + ['*2\r\n$1\r\na\r\n$1\r\nz\r\n'] + ); + }); + + it('2 bytes', () => { + assert.deepEqual( + encodeCommand(['א', 'ת']), + ['*2\r\n$2\r\nא\r\n$2\r\nת\r\n'] + ); + }); + + it('4 bytes', () => { + assert.deepEqual( + [...encodeCommand(['🐣', '🐤'])], + ['*2\r\n$4\r\n🐣\r\n$4\r\n🐤\r\n'] + ); + }); + + it('buffer', () => { + assert.deepEqual( + encodeCommand([Buffer.from('string')]), + ['*1\r\n$6\r\n', Buffer.from('string'), '\r\n'] + ); + }); +}); diff --git a/packages/client/lib/RESP/encoder.ts b/packages/client/lib/RESP/encoder.ts new file mode 100644 index 00000000000..af857711dc3 --- /dev/null +++ b/packages/client/lib/RESP/encoder.ts @@ -0,0 +1,28 @@ +import { RedisArgument } from './types'; + +const CRLF = '\r\n'; + +export default function encodeCommand(args: Array): Array { + const toWrite: Array = []; + + let strings = '*' + args.length + CRLF; + + for (let i = 0; i < args.length; i++) { + const arg = args[i]; + if (typeof arg === 'string') { + strings += '$' + Buffer.byteLength(arg) + CRLF + arg + CRLF; + } else if (arg instanceof Buffer) { + toWrite.push( + strings + '$' + arg.length.toString() + CRLF, + arg + ); + strings = CRLF; + } else { + throw new TypeError(`"arguments[${i}]" must be of type "string | Buffer", got ${typeof arg} instead.`); + } + } + + toWrite.push(strings); + + return toWrite; +} diff --git a/packages/client/lib/RESP/types.ts b/packages/client/lib/RESP/types.ts new file mode 100644 index 00000000000..46fcd7ac8c1 --- /dev/null +++ b/packages/client/lib/RESP/types.ts @@ -0,0 +1,398 @@ +import { BlobError, SimpleError } from '../errors'; +import { RedisScriptConfig, SHA1 } from '../lua-script'; +import { RESP_TYPES } from './decoder'; +import { VerbatimString } from './verbatim-string'; + +export type RESP_TYPES = typeof RESP_TYPES; + +export type RespTypes = RESP_TYPES[keyof RESP_TYPES]; + +// using interface(s) to allow circular references +// type X = BlobStringReply | ArrayReply; + +export interface RespType< + RESP_TYPE extends RespTypes, + DEFAULT, + TYPES = never, + TYPE_MAPPING = DEFAULT | TYPES +> { + RESP_TYPE: RESP_TYPE; + DEFAULT: DEFAULT; + TYPES: TYPES; + TYPE_MAPPING: MappedType; +} + +export interface NullReply extends RespType< + RESP_TYPES['NULL'], + null +> {} + +export interface BooleanReply< + T extends boolean = boolean +> extends RespType< + RESP_TYPES['BOOLEAN'], + T +> {} + +export interface NumberReply< + T extends number = number +> extends RespType< + RESP_TYPES['NUMBER'], + T, + `${T}`, + number | string +> {} + +export interface BigNumberReply< + T extends bigint = bigint +> extends RespType< + RESP_TYPES['BIG_NUMBER'], + T, + number | `${T}`, + bigint | number | string +> {} + +export interface DoubleReply< + T extends number = number +> extends RespType< + RESP_TYPES['DOUBLE'], + T, + `${T}`, + number | string +> {} + +export interface SimpleStringReply< + T extends string = string +> extends RespType< + RESP_TYPES['SIMPLE_STRING'], + T, + Buffer, + string | Buffer +> {} + +export interface BlobStringReply< + T extends string = string +> extends RespType< + RESP_TYPES['BLOB_STRING'], + T, + Buffer, + string | Buffer +> { + toString(): string +} + +export interface VerbatimStringReply< + T extends string = string +> extends RespType< + RESP_TYPES['VERBATIM_STRING'], + T, + Buffer | VerbatimString, + string | Buffer | VerbatimString +> {} + +export interface SimpleErrorReply extends RespType< + RESP_TYPES['SIMPLE_ERROR'], + SimpleError, + Buffer +> {} + +export interface BlobErrorReply extends RespType< + RESP_TYPES['BLOB_ERROR'], + BlobError, + Buffer +> {} + +export interface ArrayReply extends RespType< + RESP_TYPES['ARRAY'], + Array, + never, + Array +> {} + +export interface TuplesReply]> extends RespType< + RESP_TYPES['ARRAY'], + T, + never, + Array +> {} + +export interface SetReply extends RespType< + RESP_TYPES['SET'], + Array, + Set, + Array | Set +> {} + +export interface MapReply extends RespType< + RESP_TYPES['MAP'], + { [key: string]: V }, + Map | Array, + Map | Array +> {} + +type MapKeyValue = [key: BlobStringReply | SimpleStringReply, value: unknown]; + +type MapTuples = Array; + +type ExtractMapKey = ( + T extends BlobStringReply ? S : + T extends SimpleStringReply ? S : + never +); + +export interface TuplesToMapReply extends RespType< + RESP_TYPES['MAP'], + { + [P in T[number] as ExtractMapKey]: P[1]; + }, + Map, T[number][1]> | FlattenTuples +> {} + +type FlattenTuples = ( + T extends [] ? [] : + T extends [MapKeyValue] ? T[0] : + T extends [MapKeyValue, ...infer R] ? [ + ...T[0], + ...FlattenTuples + ] : + never +); + +export type ReplyUnion = ( + NullReply | + BooleanReply | + NumberReply | + BigNumberReply | + DoubleReply | + SimpleStringReply | + BlobStringReply | + VerbatimStringReply | + SimpleErrorReply | + BlobErrorReply | + ArrayReply | + SetReply | + MapReply +); + +export type MappedType = ((...args: any) => T) | (new (...args: any) => T); + +type InferTypeMapping = T extends RespType ? FLAG_TYPES : never; + +export type TypeMapping = { + [P in RespTypes]?: MappedType>>>; +}; + +type MapKey< + T, + TYPE_MAPPING extends TypeMapping +> = ReplyWithTypeMapping; + +export type UnwrapReply> = REPLY['DEFAULT' | 'TYPES']; + +export type ReplyWithTypeMapping< + REPLY, + TYPE_MAPPING extends TypeMapping +> = ( + // if REPLY is a type, extract the coresponding type from TYPE_MAPPING or use the default type + REPLY extends RespType ? + TYPE_MAPPING[RESP_TYPE] extends MappedType ? + ReplyWithTypeMapping, TYPE_MAPPING> : + ReplyWithTypeMapping + : ( + // if REPLY is a known generic type, convert its generic arguments + // TODO: tuples? + REPLY extends Array ? Array> : + REPLY extends Set ? Set> : + REPLY extends Map ? Map, ReplyWithTypeMapping> : + // `Date | Buffer | Error` are supersets of `Record`, so they need to be checked first + REPLY extends Date | Buffer | Error ? REPLY : + REPLY extends Record ? { + [P in keyof REPLY]: ReplyWithTypeMapping; + } : + // otherwise, just return the REPLY as is + REPLY + ) +); + +export type TransformReply = (this: void, reply: any, preserve?: any, typeMapping?: TypeMapping) => any; // TODO; + +export type RedisArgument = string | Buffer; + +export type CommandArguments = Array & { preserve?: unknown }; + +// export const REQUEST_POLICIES = { +// /** +// * TODO +// */ +// ALL_NODES: 'all_nodes', +// /** +// * TODO +// */ +// ALL_SHARDS: 'all_shards', +// /** +// * TODO +// */ +// SPECIAL: 'special' +// } as const; + +// export type REQUEST_POLICIES = typeof REQUEST_POLICIES; + +// export type RequestPolicies = REQUEST_POLICIES[keyof REQUEST_POLICIES]; + +// export const RESPONSE_POLICIES = { +// /** +// * TODO +// */ +// ONE_SUCCEEDED: 'one_succeeded', +// /** +// * TODO +// */ +// ALL_SUCCEEDED: 'all_succeeded', +// /** +// * TODO +// */ +// LOGICAL_AND: 'agg_logical_and', +// /** +// * TODO +// */ +// SPECIAL: 'special' +// } as const; + +// export type RESPONSE_POLICIES = typeof RESPONSE_POLICIES; + +// export type ResponsePolicies = RESPONSE_POLICIES[keyof RESPONSE_POLICIES]; + +// export type CommandPolicies = { +// request?: RequestPolicies | null; +// response?: ResponsePolicies | null; +// }; + +export type Command = { + FIRST_KEY_INDEX?: number | ((this: void, ...args: Array) => RedisArgument | undefined); + IS_READ_ONLY?: boolean; + /** + * @internal + * TODO: remove once `POLICIES` is implemented + */ + IS_FORWARD_COMMAND?: boolean; + // POLICIES?: CommandPolicies; + transformArguments(this: void, ...args: Array): CommandArguments; + TRANSFORM_LEGACY_REPLY?: boolean; + transformReply: TransformReply | Record; + unstableResp3?: boolean; +}; + +export type RedisCommands = Record; + +export type RedisModules = Record; + +export interface RedisFunction extends Command { + NUMBER_OF_KEYS?: number; +} + +export type RedisFunctions = Record>; + +export type RedisScript = RedisScriptConfig & SHA1; + +export type RedisScripts = Record; + +// TODO: move to Commander? +export interface CommanderConfig< + M extends RedisModules, + F extends RedisFunctions, + S extends RedisScripts, + RESP extends RespVersions +> { + modules?: M; + functions?: F; + scripts?: S; + /** + * TODO + */ + RESP?: RESP; + /** + * TODO + */ + unstableResp3?: boolean; +} + +type Resp2Array = ( + T extends [] ? [] : + T extends [infer ITEM] ? [Resp2Reply] : + T extends [infer ITEM, ...infer REST] ? [ + Resp2Reply, + ...Resp2Array + ] : + T extends Array ? Array> : + never +); + +export type Resp2Reply = ( + RESP3REPLY extends RespType ? + // TODO: RESP3 only scalar types + RESP_TYPE extends RESP_TYPES['DOUBLE'] ? BlobStringReply : + RESP_TYPE extends RESP_TYPES['ARRAY'] | RESP_TYPES['SET'] ? RespType< + RESP_TYPE, + Resp2Array + > : + RESP_TYPE extends RESP_TYPES['MAP'] ? RespType< + RESP_TYPES['ARRAY'], + Resp2Array>> + > : + RESP3REPLY : + RESP3REPLY +); + +export type RespVersions = 2 | 3; + +export type CommandReply< + COMMAND extends Command, + RESP extends RespVersions +> = ( + // if transformReply is a function, use its return type + COMMAND['transformReply'] extends (...args: any) => infer T ? T : + // if transformReply[RESP] is a function, use its return type + COMMAND['transformReply'] extends Record infer T> ? T : + // otherwise use the generic reply type + ReplyUnion +); + +export type CommandSignature< + COMMAND extends Command, + RESP extends RespVersions, + TYPE_MAPPING extends TypeMapping +> = (...args: Parameters) => Promise, TYPE_MAPPING>>; + +// export type CommandWithPoliciesSignature< +// COMMAND extends Command, +// RESP extends RespVersions, +// TYPE_MAPPING extends TypeMapping, +// POLICIES extends CommandPolicies +// > = (...args: Parameters) => Promise< +// ReplyWithPolicy< +// ReplyWithTypeMapping, TYPE_MAPPING>, +// MergePolicies +// > +// >; + +// export type MergePolicies< +// COMMAND extends Command, +// POLICIES extends CommandPolicies +// > = Omit & POLICIES; + +// type ReplyWithPolicy< +// REPLY, +// POLICIES extends CommandPolicies, +// > = ( +// POLICIES['request'] extends REQUEST_POLICIES['SPECIAL'] ? never : +// POLICIES['request'] extends null | undefined ? REPLY : +// unknown extends POLICIES['request'] ? REPLY : +// POLICIES['response'] extends RESPONSE_POLICIES['SPECIAL'] ? never : +// POLICIES['response'] extends RESPONSE_POLICIES['ALL_SUCCEEDED' | 'ONE_SUCCEEDED' | 'LOGICAL_AND'] ? REPLY : +// // otherwise, return array of replies +// Array +// ); diff --git a/packages/client/lib/RESP/verbatim-string.ts b/packages/client/lib/RESP/verbatim-string.ts new file mode 100644 index 00000000000..92ff4fe3fb1 --- /dev/null +++ b/packages/client/lib/RESP/verbatim-string.ts @@ -0,0 +1,8 @@ +export class VerbatimString extends String { + constructor( + public format: string, + value: string + ) { + super(value); + } +} diff --git a/packages/client/lib/client/RESP2/composers/buffer.spec.ts b/packages/client/lib/client/RESP2/composers/buffer.spec.ts deleted file mode 100644 index f57c369fecb..00000000000 --- a/packages/client/lib/client/RESP2/composers/buffer.spec.ts +++ /dev/null @@ -1,14 +0,0 @@ -import { strict as assert } from 'assert'; -import BufferComposer from './buffer'; - -describe('Buffer Composer', () => { - const composer = new BufferComposer(); - - it('should compose two buffers', () => { - composer.write(Buffer.from([0])); - assert.deepEqual( - composer.end(Buffer.from([1])), - Buffer.from([0, 1]) - ); - }); -}); diff --git a/packages/client/lib/client/RESP2/composers/buffer.ts b/packages/client/lib/client/RESP2/composers/buffer.ts deleted file mode 100644 index 4affb4283e0..00000000000 --- a/packages/client/lib/client/RESP2/composers/buffer.ts +++ /dev/null @@ -1,18 +0,0 @@ -import { Composer } from './interface'; - -export default class BufferComposer implements Composer { - private chunks: Array = []; - - write(buffer: Buffer): void { - this.chunks.push(buffer); - } - - end(buffer: Buffer): Buffer { - this.write(buffer); - return Buffer.concat(this.chunks.splice(0)); - } - - reset() { - this.chunks = []; - } -} diff --git a/packages/client/lib/client/RESP2/composers/interface.ts b/packages/client/lib/client/RESP2/composers/interface.ts deleted file mode 100644 index 0fc8f031414..00000000000 --- a/packages/client/lib/client/RESP2/composers/interface.ts +++ /dev/null @@ -1,7 +0,0 @@ -export interface Composer { - write(buffer: Buffer): void; - - end(buffer: Buffer): T; - - reset(): void; -} diff --git a/packages/client/lib/client/RESP2/composers/string.spec.ts b/packages/client/lib/client/RESP2/composers/string.spec.ts deleted file mode 100644 index 9dd26aae021..00000000000 --- a/packages/client/lib/client/RESP2/composers/string.spec.ts +++ /dev/null @@ -1,14 +0,0 @@ -import { strict as assert } from 'assert'; -import StringComposer from './string'; - -describe('String Composer', () => { - const composer = new StringComposer(); - - it('should compose two strings', () => { - composer.write(Buffer.from([0])); - assert.deepEqual( - composer.end(Buffer.from([1])), - Buffer.from([0, 1]).toString() - ); - }); -}); diff --git a/packages/client/lib/client/RESP2/composers/string.ts b/packages/client/lib/client/RESP2/composers/string.ts deleted file mode 100644 index 0cd8f00e95c..00000000000 --- a/packages/client/lib/client/RESP2/composers/string.ts +++ /dev/null @@ -1,22 +0,0 @@ -import { StringDecoder } from 'string_decoder'; -import { Composer } from './interface'; - -export default class StringComposer implements Composer { - private decoder = new StringDecoder(); - - private string = ''; - - write(buffer: Buffer): void { - this.string += this.decoder.write(buffer); - } - - end(buffer: Buffer): string { - const string = this.string + this.decoder.end(buffer); - this.string = ''; - return string; - } - - reset() { - this.string = ''; - } -} diff --git a/packages/client/lib/client/RESP2/decoder.spec.ts b/packages/client/lib/client/RESP2/decoder.spec.ts deleted file mode 100644 index dcce9f60115..00000000000 --- a/packages/client/lib/client/RESP2/decoder.spec.ts +++ /dev/null @@ -1,195 +0,0 @@ -import { strict as assert } from 'assert'; -import { SinonSpy, spy } from 'sinon'; -import RESP2Decoder from './decoder'; -import { ErrorReply } from '../../errors'; - -interface DecoderAndSpies { - decoder: RESP2Decoder; - returnStringsAsBuffersSpy: SinonSpy; - onReplySpy: SinonSpy; -} - -function createDecoderAndSpies(returnStringsAsBuffers: boolean): DecoderAndSpies { - const returnStringsAsBuffersSpy = spy(() => returnStringsAsBuffers), - onReplySpy = spy(); - - return { - decoder: new RESP2Decoder({ - returnStringsAsBuffers: returnStringsAsBuffersSpy, - onReply: onReplySpy - }), - returnStringsAsBuffersSpy, - onReplySpy - }; -} - -function writeChunks(stream: RESP2Decoder, buffer: Buffer) { - let i = 0; - while (i < buffer.length) { - stream.write(buffer.slice(i, ++i)); - } -} - -type Replies = Array>; - -interface TestsOptions { - toWrite: Buffer; - returnStringsAsBuffers: boolean; - replies: Replies; -} - -function generateTests({ - toWrite, - returnStringsAsBuffers, - replies -}: TestsOptions): void { - it('single chunk', () => { - const { decoder, returnStringsAsBuffersSpy, onReplySpy } = - createDecoderAndSpies(returnStringsAsBuffers); - decoder.write(toWrite); - assert.equal(returnStringsAsBuffersSpy.callCount, replies.length); - testReplies(onReplySpy, replies); - }); - - it('multiple chunks', () => { - const { decoder, returnStringsAsBuffersSpy, onReplySpy } = - createDecoderAndSpies(returnStringsAsBuffers); - writeChunks(decoder, toWrite); - assert.equal(returnStringsAsBuffersSpy.callCount, replies.length); - testReplies(onReplySpy, replies); - }); -} - -function testReplies(spy: SinonSpy, replies: Replies): void { - if (!replies) { - assert.equal(spy.callCount, 0); - return; - } - - assert.equal(spy.callCount, replies.length); - for (const [i, reply] of replies.entries()) { - assert.deepEqual( - spy.getCall(i).args, - reply - ); - } -} - -describe('RESP2Parser', () => { - describe('Simple String', () => { - describe('as strings', () => { - generateTests({ - toWrite: Buffer.from('+OK\r\n'), - returnStringsAsBuffers: false, - replies: [['OK']] - }); - }); - - describe('as buffers', () => { - generateTests({ - toWrite: Buffer.from('+OK\r\n'), - returnStringsAsBuffers: true, - replies: [[Buffer.from('OK')]] - }); - }); - }); - - describe('Error', () => { - generateTests({ - toWrite: Buffer.from('-ERR\r\n'), - returnStringsAsBuffers: false, - replies: [[new ErrorReply('ERR')]] - }); - }); - - describe('Integer', () => { - describe('-1', () => { - generateTests({ - toWrite: Buffer.from(':-1\r\n'), - returnStringsAsBuffers: false, - replies: [[-1]] - }); - }); - - describe('0', () => { - generateTests({ - toWrite: Buffer.from(':0\r\n'), - returnStringsAsBuffers: false, - replies: [[0]] - }); - }); - }); - - describe('Bulk String', () => { - describe('null', () => { - generateTests({ - toWrite: Buffer.from('$-1\r\n'), - returnStringsAsBuffers: false, - replies: [[null]] - }); - }); - - describe('as strings', () => { - generateTests({ - toWrite: Buffer.from('$2\r\naa\r\n'), - returnStringsAsBuffers: false, - replies: [['aa']] - }); - }); - - describe('as buffers', () => { - generateTests({ - toWrite: Buffer.from('$2\r\naa\r\n'), - returnStringsAsBuffers: true, - replies: [[Buffer.from('aa')]] - }); - }); - }); - - describe('Array', () => { - describe('null', () => { - generateTests({ - toWrite: Buffer.from('*-1\r\n'), - returnStringsAsBuffers: false, - replies: [[null]] - }); - }); - - const arrayBuffer = Buffer.from( - '*5\r\n' + - '+OK\r\n' + - '-ERR\r\n' + - ':0\r\n' + - '$1\r\na\r\n' + - '*0\r\n' - ); - - describe('as strings', () => { - generateTests({ - toWrite: arrayBuffer, - returnStringsAsBuffers: false, - replies: [[[ - 'OK', - new ErrorReply('ERR'), - 0, - 'a', - [] - ]]] - }); - }); - - describe('as buffers', () => { - generateTests({ - toWrite: arrayBuffer, - returnStringsAsBuffers: true, - replies: [[[ - Buffer.from('OK'), - new ErrorReply('ERR'), - 0, - Buffer.from('a'), - [] - ]]] - }); - }); - }); -}); diff --git a/packages/client/lib/client/RESP2/decoder.ts b/packages/client/lib/client/RESP2/decoder.ts deleted file mode 100644 index 525f118bf30..00000000000 --- a/packages/client/lib/client/RESP2/decoder.ts +++ /dev/null @@ -1,257 +0,0 @@ -import { ErrorReply } from '../../errors'; -import { Composer } from './composers/interface'; -import BufferComposer from './composers/buffer'; -import StringComposer from './composers/string'; - -// RESP2 specification -// https://redis.io/topics/protocol - -enum Types { - SIMPLE_STRING = 43, // + - ERROR = 45, // - - INTEGER = 58, // : - BULK_STRING = 36, // $ - ARRAY = 42 // * -} - -enum ASCII { - CR = 13, // \r - ZERO = 48, - MINUS = 45 -} - -export type Reply = string | Buffer | ErrorReply | number | null | Array; - -type ArrayReply = Array | null; - -export type ReturnStringsAsBuffers = () => boolean; - -interface RESP2Options { - returnStringsAsBuffers: ReturnStringsAsBuffers; - onReply(reply: Reply): unknown; -} - -interface ArrayInProcess { - array: Array; - pushCounter: number; -} - -// Using TypeScript `private` and not the build-in `#` to avoid __classPrivateFieldGet and __classPrivateFieldSet - -export default class RESP2Decoder { - constructor(private options: RESP2Options) {} - - private cursor = 0; - - private type?: Types; - - private bufferComposer = new BufferComposer(); - - private stringComposer = new StringComposer(); - - private currentStringComposer: BufferComposer | StringComposer = this.stringComposer; - - reset() { - this.cursor = 0; - this.type = undefined; - this.bufferComposer.reset(); - this.stringComposer.reset(); - this.currentStringComposer = this.stringComposer; - } - - write(chunk: Buffer): void { - while (this.cursor < chunk.length) { - if (!this.type) { - this.currentStringComposer = this.options.returnStringsAsBuffers() ? - this.bufferComposer : - this.stringComposer; - - this.type = chunk[this.cursor]; - if (++this.cursor >= chunk.length) break; - } - - const reply = this.parseType(chunk, this.type); - if (reply === undefined) break; - - this.type = undefined; - this.options.onReply(reply); - } - - this.cursor -= chunk.length; - } - - private parseType(chunk: Buffer, type: Types, arraysToKeep?: number): Reply | undefined { - switch (type) { - case Types.SIMPLE_STRING: - return this.parseSimpleString(chunk); - - case Types.ERROR: - return this.parseError(chunk); - - case Types.INTEGER: - return this.parseInteger(chunk); - - case Types.BULK_STRING: - return this.parseBulkString(chunk); - - case Types.ARRAY: - return this.parseArray(chunk, arraysToKeep); - } - } - - private compose< - C extends Composer, - T = C extends Composer ? TT : never - >( - chunk: Buffer, - composer: C - ): T | undefined { - for (let i = this.cursor; i < chunk.length; i++) { - if (chunk[i] === ASCII.CR) { - const reply = composer.end( - chunk.subarray(this.cursor, i) - ); - this.cursor = i + 2; - return reply; - } - } - - const toWrite = chunk.subarray(this.cursor); - composer.write(toWrite); - this.cursor = chunk.length; - } - - private parseSimpleString(chunk: Buffer): string | Buffer | undefined { - return this.compose(chunk, this.currentStringComposer); - } - - private parseError(chunk: Buffer): ErrorReply | undefined { - const message = this.compose(chunk, this.stringComposer); - if (message !== undefined) { - return new ErrorReply(message); - } - } - - private integer = 0; - - private isNegativeInteger?: boolean; - - private parseInteger(chunk: Buffer): number | undefined { - if (this.isNegativeInteger === undefined) { - this.isNegativeInteger = chunk[this.cursor] === ASCII.MINUS; - if (this.isNegativeInteger && ++this.cursor === chunk.length) return; - } - - do { - const byte = chunk[this.cursor]; - if (byte === ASCII.CR) { - const integer = this.isNegativeInteger ? -this.integer : this.integer; - this.integer = 0; - this.isNegativeInteger = undefined; - this.cursor += 2; - return integer; - } - - this.integer = this.integer * 10 + byte - ASCII.ZERO; - } while (++this.cursor < chunk.length); - } - - private bulkStringRemainingLength?: number; - - private parseBulkString(chunk: Buffer): string | Buffer | null | undefined { - if (this.bulkStringRemainingLength === undefined) { - const length = this.parseInteger(chunk); - if (length === undefined) return; - if (length === -1) return null; - - this.bulkStringRemainingLength = length; - - if (this.cursor >= chunk.length) return; - } - - const end = this.cursor + this.bulkStringRemainingLength; - if (chunk.length >= end) { - const reply = this.currentStringComposer.end( - chunk.subarray(this.cursor, end) - ); - this.bulkStringRemainingLength = undefined; - this.cursor = end + 2; - return reply; - } - - const toWrite = chunk.subarray(this.cursor); - this.currentStringComposer.write(toWrite); - this.bulkStringRemainingLength -= toWrite.length; - this.cursor = chunk.length; - } - - private arraysInProcess: Array = []; - - private initializeArray = false; - - private arrayItemType?: Types; - - private parseArray(chunk: Buffer, arraysToKeep = 0): ArrayReply | undefined { - if (this.initializeArray || this.arraysInProcess.length === arraysToKeep) { - const length = this.parseInteger(chunk); - if (length === undefined) { - this.initializeArray = true; - return undefined; - } - - this.initializeArray = false; - this.arrayItemType = undefined; - - if (length === -1) { - return this.returnArrayReply(null, arraysToKeep, chunk); - } else if (length === 0) { - return this.returnArrayReply([], arraysToKeep, chunk); - } - - this.arraysInProcess.push({ - array: new Array(length), - pushCounter: 0 - }); - } - - while (this.cursor < chunk.length) { - if (!this.arrayItemType) { - this.arrayItemType = chunk[this.cursor]; - - if (++this.cursor >= chunk.length) break; - } - - const item = this.parseType( - chunk, - this.arrayItemType, - arraysToKeep + 1 - ); - if (item === undefined) break; - - this.arrayItemType = undefined; - - const reply = this.pushArrayItem(item, arraysToKeep); - if (reply !== undefined) return reply; - } - } - - private returnArrayReply(reply: ArrayReply, arraysToKeep: number, chunk?: Buffer): ArrayReply | undefined { - if (this.arraysInProcess.length <= arraysToKeep) return reply; - - return this.pushArrayItem(reply, arraysToKeep, chunk); - } - - private pushArrayItem(item: Reply, arraysToKeep: number, chunk?: Buffer): ArrayReply | undefined { - const to = this.arraysInProcess[this.arraysInProcess.length - 1]!; - to.array[to.pushCounter] = item; - if (++to.pushCounter === to.array.length) { - return this.returnArrayReply( - this.arraysInProcess.pop()!.array, - arraysToKeep, - chunk - ); - } else if (chunk && chunk.length > this.cursor) { - return this.parseArray(chunk, arraysToKeep); - } - } -} diff --git a/packages/client/lib/client/RESP2/encoder.spec.ts b/packages/client/lib/client/RESP2/encoder.spec.ts deleted file mode 100644 index 486259472a4..00000000000 --- a/packages/client/lib/client/RESP2/encoder.spec.ts +++ /dev/null @@ -1,33 +0,0 @@ -import { strict as assert } from 'assert'; -import { describe } from 'mocha'; -import encodeCommand from './encoder'; - -describe('RESP2 Encoder', () => { - it('1 byte', () => { - assert.deepEqual( - encodeCommand(['a', 'z']), - ['*2\r\n$1\r\na\r\n$1\r\nz\r\n'] - ); - }); - - it('2 bytes', () => { - assert.deepEqual( - encodeCommand(['א', 'ת']), - ['*2\r\n$2\r\nא\r\n$2\r\nת\r\n'] - ); - }); - - it('4 bytes', () => { - assert.deepEqual( - [...encodeCommand(['🐣', '🐤'])], - ['*2\r\n$4\r\n🐣\r\n$4\r\n🐤\r\n'] - ); - }); - - it('buffer', () => { - assert.deepEqual( - encodeCommand([Buffer.from('string')]), - ['*1\r\n$6\r\n', Buffer.from('string'), '\r\n'] - ); - }); -}); diff --git a/packages/client/lib/client/RESP2/encoder.ts b/packages/client/lib/client/RESP2/encoder.ts deleted file mode 100644 index 217fbc714bb..00000000000 --- a/packages/client/lib/client/RESP2/encoder.ts +++ /dev/null @@ -1,28 +0,0 @@ -import { RedisCommandArgument, RedisCommandArguments } from '../../commands'; - -const CRLF = '\r\n'; - -export default function encodeCommand(args: RedisCommandArguments): Array { - const toWrite: Array = []; - - let strings = '*' + args.length + CRLF; - - for (let i = 0; i < args.length; i++) { - const arg = args[i]; - if (typeof arg === 'string') { - strings += '$' + Buffer.byteLength(arg) + CRLF + arg + CRLF; - } else if (arg instanceof Buffer) { - toWrite.push( - strings + '$' + arg.length.toString() + CRLF, - arg - ); - strings = CRLF; - } else { - throw new TypeError('Invalid argument type'); - } - } - - toWrite.push(strings); - - return toWrite; -} diff --git a/packages/client/lib/client/commands-queue.ts b/packages/client/lib/client/commands-queue.ts index 7fffed86580..a4029779fc8 100644 --- a/packages/client/lib/client/commands-queue.ts +++ b/packages/client/lib/client/commands-queue.ts @@ -1,263 +1,426 @@ -import * as LinkedList from 'yallist'; -import { AbortError, ErrorReply } from '../errors'; -import { RedisCommandArguments, RedisCommandRawReply } from '../commands'; -import RESP2Decoder from './RESP2/decoder'; -import encodeCommand from './RESP2/encoder'; +import { SinglyLinkedList, DoublyLinkedNode, DoublyLinkedList } from './linked-list'; +import encodeCommand from '../RESP/encoder'; +import { Decoder, PUSH_TYPE_MAPPING, RESP_TYPES } from '../RESP/decoder'; +import { CommandArguments, TypeMapping, ReplyUnion, RespVersions } from '../RESP/types'; import { ChannelListeners, PubSub, PubSubCommand, PubSubListener, PubSubType, PubSubTypeListeners } from './pub-sub'; - -export interface QueueCommandOptions { - asap?: boolean; - chainId?: symbol; - signal?: AbortSignal; - returnBuffers?: boolean; +import { AbortError, ErrorReply } from '../errors'; +import { MonitorCallback } from '.'; + +export interface CommandOptions { + chainId?: symbol; + asap?: boolean; + abortSignal?: AbortSignal; + /** + * Maps between RESP and JavaScript types + */ + typeMapping?: T; } -export interface CommandWaitingToBeSent extends CommandWaitingForReply { - args: RedisCommandArguments; - chainId?: symbol; - abort?: { - signal: AbortSignal; - listener(): void; - }; +export interface CommandToWrite extends CommandWaitingForReply { + args: CommandArguments; + chainId: symbol | undefined; + abort: { + signal: AbortSignal; + listener: () => unknown; + } | undefined; } interface CommandWaitingForReply { - resolve(reply?: unknown): void; - reject(err: unknown): void; - channelsCounter?: number; - returnBuffers?: boolean; + resolve(reply?: unknown): void; + reject(err: unknown): void; + channelsCounter: number | undefined; + typeMapping: TypeMapping | undefined; } -const PONG = Buffer.from('pong'); - export type OnShardedChannelMoved = (channel: string, listeners: ChannelListeners) => void; -export default class RedisCommandsQueue { - static #flushQueue(queue: LinkedList, err: Error): void { - while (queue.length) { - queue.shift()!.reject(err); - } - } - - readonly #maxLength: number | null | undefined; - readonly #waitingToBeSent = new LinkedList(); - readonly #waitingForReply = new LinkedList(); - readonly #onShardedChannelMoved: OnShardedChannelMoved; +const PONG = Buffer.from('pong'), + RESET = Buffer.from('RESET'); - readonly #pubSub = new PubSub(); +const RESP2_PUSH_TYPE_MAPPING = { + ...PUSH_TYPE_MAPPING, + [RESP_TYPES.SIMPLE_STRING]: Buffer +}; - get isPubSubActive() { - return this.#pubSub.isActive; +export default class RedisCommandsQueue { + readonly #respVersion; + readonly #maxLength; + readonly #toWrite = new DoublyLinkedList(); + readonly #waitingForReply = new SinglyLinkedList(); + readonly #onShardedChannelMoved; + #chainInExecution: symbol | undefined; + readonly decoder; + readonly #pubSub = new PubSub(); + + get isPubSubActive() { + return this.#pubSub.isActive; + } + + constructor( + respVersion: RespVersions, + maxLength: number | null | undefined, + onShardedChannelMoved: OnShardedChannelMoved + ) { + this.#respVersion = respVersion; + this.#maxLength = maxLength; + this.#onShardedChannelMoved = onShardedChannelMoved; + this.decoder = this.#initiateDecoder(); + } + + #onReply(reply: ReplyUnion) { + this.#waitingForReply.shift()!.resolve(reply); + } + + #onErrorReply(err: ErrorReply) { + this.#waitingForReply.shift()!.reject(err); + } + + #onPush(push: Array) { + // TODO: type + if (this.#pubSub.handleMessageReply(push)) return true; + + const isShardedUnsubscribe = PubSub.isShardedUnsubscribe(push); + if (isShardedUnsubscribe && !this.#waitingForReply.length) { + const channel = push[1].toString(); + this.#onShardedChannelMoved( + channel, + this.#pubSub.removeShardedListeners(channel) + ); + return true; + } else if (isShardedUnsubscribe || PubSub.isStatusReply(push)) { + const head = this.#waitingForReply.head!.value; + if ( + (Number.isNaN(head.channelsCounter!) && push[2] === 0) || + --head.channelsCounter! === 0 + ) { + this.#waitingForReply.shift()!.resolve(); + } + return true; } + } - #chainInExecution: symbol | undefined; + #getTypeMapping() { + return this.#waitingForReply.head!.value.typeMapping ?? {}; + } + + #initiateDecoder() { + return new Decoder({ + onReply: reply => this.#onReply(reply), + onErrorReply: err => this.#onErrorReply(err), + onPush: push => { + if (!this.#onPush(push)) { - #decoder = new RESP2Decoder({ - returnStringsAsBuffers: () => { - return !!this.#waitingForReply.head?.value.returnBuffers || - this.#pubSub.isActive; - }, - onReply: reply => { - if (this.#pubSub.isActive && Array.isArray(reply)) { - if (this.#pubSub.handleMessageReply(reply as Array)) return; - - const isShardedUnsubscribe = PubSub.isShardedUnsubscribe(reply as Array); - if (isShardedUnsubscribe && !this.#waitingForReply.length) { - const channel = (reply[1] as Buffer).toString(); - this.#onShardedChannelMoved( - channel, - this.#pubSub.removeShardedListeners(channel) - ); - return; - } else if (isShardedUnsubscribe || PubSub.isStatusReply(reply as Array)) { - const head = this.#waitingForReply.head!.value; - if ( - (Number.isNaN(head.channelsCounter!) && reply[2] === 0) || - --head.channelsCounter! === 0 - ) { - this.#waitingForReply.shift()!.resolve(); - } - return; - } - if (PONG.equals(reply[0] as Buffer)) { - const { resolve, returnBuffers } = this.#waitingForReply.shift()!, - buffer = ((reply[1] as Buffer).length === 0 ? reply[0] : reply[1]) as Buffer; - resolve(returnBuffers ? buffer : buffer.toString()); - return; - } - } - - const { resolve, reject } = this.#waitingForReply.shift()!; - if (reply instanceof ErrorReply) { - reject(reply); - } else { - resolve(reply); - } } + }, + getTypeMapping: () => this.#getTypeMapping() }); - - constructor( - maxLength: number | null | undefined, - onShardedChannelMoved: OnShardedChannelMoved - ) { - this.#maxLength = maxLength; - this.#onShardedChannelMoved = onShardedChannelMoved; + } + + addCommand( + args: CommandArguments, + options?: CommandOptions + ): Promise { + if (this.#maxLength && this.#toWrite.length + this.#waitingForReply.length >= this.#maxLength) { + return Promise.reject(new Error('The queue is full')); + } else if (options?.abortSignal?.aborted) { + return Promise.reject(new AbortError()); } - addCommand(args: RedisCommandArguments, options?: QueueCommandOptions): Promise { - if (this.#maxLength && this.#waitingToBeSent.length + this.#waitingForReply.length >= this.#maxLength) { - return Promise.reject(new Error('The queue is full')); - } else if (options?.signal?.aborted) { - return Promise.reject(new AbortError()); + return new Promise((resolve, reject) => { + let node: DoublyLinkedNode; + const value: CommandToWrite = { + args, + chainId: options?.chainId, + abort: undefined, + resolve, + reject, + channelsCounter: undefined, + typeMapping: options?.typeMapping + }; + + const signal = options?.abortSignal; + if (signal) { + value.abort = { + signal, + listener: () => { + this.#toWrite.remove(node); + value.reject(new AbortError()); + } + }; + signal.addEventListener('abort', value.abort.listener, { once: true }); + } + + node = this.#toWrite.add(value, options?.asap); + }); + } + + #addPubSubCommand(command: PubSubCommand, asap = false, chainId?: symbol) { + return new Promise((resolve, reject) => { + this.#toWrite.add({ + args: command.args, + chainId, + abort: undefined, + resolve() { + command.resolve(); + resolve(); + }, + reject(err) { + command.reject?.(); + reject(err); + }, + channelsCounter: command.channelsCounter, + typeMapping: PUSH_TYPE_MAPPING + }, asap); + }); + } + + #setupPubSubHandler() { + // RESP3 uses `onPush` to handle PubSub, so no need to modify `onReply` + if (this.#respVersion !== 2) return; + + this.decoder.onReply = (reply => { + if (Array.isArray(reply)) { + if (this.#onPush(reply)) return; + + if (PONG.equals(reply[0] as Buffer)) { + const { resolve, typeMapping } = this.#waitingForReply.shift()!, + buffer = ((reply[1] as Buffer).length === 0 ? reply[0] : reply[1]) as Buffer; + resolve(typeMapping?.[RESP_TYPES.SIMPLE_STRING] === Buffer ? buffer : buffer.toString()); + return; } - - return new Promise((resolve, reject) => { - const node = new LinkedList.Node({ - args, - chainId: options?.chainId, - returnBuffers: options?.returnBuffers, - resolve, - reject - }); - - if (options?.signal) { - const listener = () => { - this.#waitingToBeSent.removeNode(node); - node.value.reject(new AbortError()); - }; - node.value.abort = { - signal: options.signal, - listener - }; - // AbortSignal type is incorrent - (options.signal as any).addEventListener('abort', listener, { - once: true - }); - } - - if (options?.asap) { - this.#waitingToBeSent.unshiftNode(node); - } else { - this.#waitingToBeSent.pushNode(node); - } - }); - } - - subscribe( - type: PubSubType, - channels: string | Array, - listener: PubSubListener, - returnBuffers?: T - ) { - return this.#pushPubSubCommand( - this.#pubSub.subscribe(type, channels, listener, returnBuffers) - ); - } - - unsubscribe( - type: PubSubType, - channels?: string | Array, - listener?: PubSubListener, - returnBuffers?: T - ) { - return this.#pushPubSubCommand( - this.#pubSub.unsubscribe(type, channels, listener, returnBuffers) - ); - } - - resubscribe(): Promise | undefined { - const commands = this.#pubSub.resubscribe(); - if (!commands.length) return; - - return Promise.all( - commands.map(command => this.#pushPubSubCommand(command)) - ); + } + + return this.#onReply(reply); + }) as Decoder['onReply']; + this.decoder.getTypeMapping = () => RESP2_PUSH_TYPE_MAPPING; + } + + subscribe( + type: PubSubType, + channels: string | Array, + listener: PubSubListener, + returnBuffers?: T + ) { + const command = this.#pubSub.subscribe(type, channels, listener, returnBuffers); + if (!command) return; + + this.#setupPubSubHandler(); + return this.#addPubSubCommand(command); + } + + #resetDecoderCallbacks() { + this.decoder.onReply = (reply => this.#onReply(reply)) as Decoder['onReply']; + this.decoder.getTypeMapping = () => this.#getTypeMapping(); + } + + unsubscribe( + type: PubSubType, + channels?: string | Array, + listener?: PubSubListener, + returnBuffers?: T + ) { + const command = this.#pubSub.unsubscribe(type, channels, listener, returnBuffers); + if (!command) return; + + if (command && this.#respVersion === 2) { + // RESP2 modifies `onReply` to handle PubSub (see #setupPubSubHandler) + const { resolve } = command; + command.resolve = () => { + if (!this.#pubSub.isActive) { + this.#resetDecoderCallbacks(); + } + + resolve(); + }; } - extendPubSubChannelListeners( - type: PubSubType, - channel: string, - listeners: ChannelListeners - ) { - return this.#pushPubSubCommand( - this.#pubSub.extendChannelListeners(type, channel, listeners) - ); + return this.#addPubSubCommand(command); + } + + resubscribe(chainId?: symbol) { + const commands = this.#pubSub.resubscribe(); + if (!commands.length) return; + + this.#setupPubSubHandler(); + return Promise.all( + commands.map(command => this.#addPubSubCommand(command, true, chainId)) + ); + } + + extendPubSubChannelListeners( + type: PubSubType, + channel: string, + listeners: ChannelListeners + ) { + const command = this.#pubSub.extendChannelListeners(type, channel, listeners); + if (!command) return; + + this.#setupPubSubHandler(); + return this.#addPubSubCommand(command); + } + + extendPubSubListeners(type: PubSubType, listeners: PubSubTypeListeners) { + const command = this.#pubSub.extendTypeListeners(type, listeners); + if (!command) return; + + this.#setupPubSubHandler(); + return this.#addPubSubCommand(command); + } + + getPubSubListeners(type: PubSubType) { + return this.#pubSub.listeners[type]; + } + + monitor(callback: MonitorCallback, options?: CommandOptions) { + return new Promise((resolve, reject) => { + const typeMapping = options?.typeMapping ?? {}; + this.#toWrite.add({ + args: ['MONITOR'], + chainId: options?.chainId, + abort: undefined, + // using `resolve` instead of using `.then`/`await` to make sure it'll be called before processing the next reply + resolve: () => { + // after running `MONITOR` only `MONITOR` and `RESET` replies are expected + // any other command should cause an error + + // if `RESET` already overrides `onReply`, set monitor as it's fallback + if (this.#resetFallbackOnReply) { + this.#resetFallbackOnReply = callback; + } else { + this.decoder.onReply = callback; + } + + this.decoder.getTypeMapping = () => typeMapping; + resolve(); + }, + reject, + channelsCounter: undefined, + typeMapping + }, options?.asap); + }); + } + + resetDecoder() { + this.#resetDecoderCallbacks(); + this.decoder.reset(); + } + + #resetFallbackOnReply?: Decoder['onReply']; + + async reset(chainId: symbol, typeMapping?: T) { + return new Promise((resolve, reject) => { + // overriding onReply to handle `RESET` while in `MONITOR` or PubSub mode + this.#resetFallbackOnReply = this.decoder.onReply; + this.decoder.onReply = (reply => { + if ( + (typeof reply === 'string' && reply === 'RESET') || + (reply instanceof Buffer && RESET.equals(reply)) + ) { + this.#resetDecoderCallbacks(); + this.#resetFallbackOnReply = undefined; + this.#pubSub.reset(); + + this.#waitingForReply.shift()!.resolve(reply); + return; + } + + this.#resetFallbackOnReply!(reply); + }) as Decoder['onReply']; + + this.#toWrite.push({ + args: ['RESET'], + chainId, + abort: undefined, + resolve, + reject, + channelsCounter: undefined, + typeMapping + }); + }); + } + + isWaitingToWrite() { + return this.#toWrite.length > 0; + } + + *commandsToWrite() { + let toSend = this.#toWrite.shift(); + while (toSend) { + let encoded: CommandArguments; + try { + encoded = encodeCommand(toSend.args); + } catch (err) { + toSend.reject(err); + toSend = this.#toWrite.shift(); + continue; + } + + // TODO reuse `toSend` or create new object? + (toSend as any).args = undefined; + if (toSend.abort) { + RedisCommandsQueue.#removeAbortListener(toSend); + toSend.abort = undefined; + } + this.#chainInExecution = toSend.chainId; + toSend.chainId = undefined; + this.#waitingForReply.push(toSend); + + yield encoded; + toSend = this.#toWrite.shift(); } + } - extendPubSubListeners(type: PubSubType, listeners: PubSubTypeListeners) { - return this.#pushPubSubCommand( - this.#pubSub.extendTypeListeners(type, listeners) - ); + #flushWaitingForReply(err: Error): void { + for (const node of this.#waitingForReply) { + node.reject(err); } + this.#waitingForReply.reset(); + } - getPubSubListeners(type: PubSubType) { - return this.#pubSub.getTypeListeners(type); - } + static #removeAbortListener(command: CommandToWrite) { + command.abort!.signal.removeEventListener('abort', command.abort!.listener); + } - #pushPubSubCommand(command: PubSubCommand) { - if (command === undefined) return; - - return new Promise((resolve, reject) => { - this.#waitingToBeSent.push({ - args: command.args, - channelsCounter: command.channelsCounter, - returnBuffers: true, - resolve: () => { - command.resolve(); - resolve(); - }, - reject: err => { - command.reject?.(); - reject(err); - } - }); - }); + static #flushToWrite(toBeSent: CommandToWrite, err: Error) { + if (toBeSent.abort) { + RedisCommandsQueue.#removeAbortListener(toBeSent); } + + toBeSent.reject(err); + } - getCommandToSend(): RedisCommandArguments | undefined { - const toSend = this.#waitingToBeSent.shift(); - if (!toSend) return; + flushWaitingForReply(err: Error): void { + this.resetDecoder(); + this.#pubSub.reset(); - let encoded: RedisCommandArguments; - try { - encoded = encodeCommand(toSend.args); - } catch (err) { - toSend.reject(err); - return; - } + this.#flushWaitingForReply(err); - this.#waitingForReply.push({ - resolve: toSend.resolve, - reject: toSend.reject, - channelsCounter: toSend.channelsCounter, - returnBuffers: toSend.returnBuffers - }); - this.#chainInExecution = toSend.chainId; - return encoded; - } + if (!this.#chainInExecution) return; - onReplyChunk(chunk: Buffer): void { - this.#decoder.write(chunk); + while (this.#toWrite.head?.value.chainId === this.#chainInExecution) { + RedisCommandsQueue.#flushToWrite( + this.#toWrite.shift()!, + err + ); } - flushWaitingForReply(err: Error): void { - this.#decoder.reset(); - this.#pubSub.reset(); - RedisCommandsQueue.#flushQueue(this.#waitingForReply, err); - - if (!this.#chainInExecution) return; - - while (this.#waitingToBeSent.head?.value.chainId === this.#chainInExecution) { - this.#waitingToBeSent.shift(); - } - - this.#chainInExecution = undefined; - } + this.#chainInExecution = undefined; + } - flushAll(err: Error): void { - this.#decoder.reset(); - this.#pubSub.reset(); - RedisCommandsQueue.#flushQueue(this.#waitingForReply, err); - RedisCommandsQueue.#flushQueue(this.#waitingToBeSent, err); + flushAll(err: Error): void { + this.resetDecoder(); + this.#pubSub.reset(); + this.#flushWaitingForReply(err); + for (const node of this.#toWrite) { + RedisCommandsQueue.#flushToWrite(node, err); } + this.#toWrite.reset(); + } + + isEmpty() { + return ( + this.#toWrite.length === 0 && + this.#waitingForReply.length === 0 + ); + } } diff --git a/packages/client/lib/client/commands.ts b/packages/client/lib/client/commands.ts deleted file mode 100644 index 76ae5d73735..00000000000 --- a/packages/client/lib/client/commands.ts +++ /dev/null @@ -1,374 +0,0 @@ -import CLUSTER_COMMANDS from '../cluster/commands'; -import * as ACL_CAT from '../commands/ACL_CAT'; -import * as ACL_DELUSER from '../commands/ACL_DELUSER'; -import * as ACL_DRYRUN from '../commands/ACL_DRYRUN'; -import * as ACL_GENPASS from '../commands/ACL_GENPASS'; -import * as ACL_GETUSER from '../commands/ACL_GETUSER'; -import * as ACL_LIST from '../commands/ACL_LIST'; -import * as ACL_LOAD from '../commands/ACL_LOAD'; -import * as ACL_LOG_RESET from '../commands/ACL_LOG_RESET'; -import * as ACL_LOG from '../commands/ACL_LOG'; -import * as ACL_SAVE from '../commands/ACL_SAVE'; -import * as ACL_SETUSER from '../commands/ACL_SETUSER'; -import * as ACL_USERS from '../commands/ACL_USERS'; -import * as ACL_WHOAMI from '../commands/ACL_WHOAMI'; -import * as ASKING from '../commands/ASKING'; -import * as AUTH from '../commands/AUTH'; -import * as BGREWRITEAOF from '../commands/BGREWRITEAOF'; -import * as BGSAVE from '../commands/BGSAVE'; -import * as CLIENT_CACHING from '../commands/CLIENT_CACHING'; -import * as CLIENT_GETNAME from '../commands/CLIENT_GETNAME'; -import * as CLIENT_GETREDIR from '../commands/CLIENT_GETREDIR'; -import * as CLIENT_ID from '../commands/CLIENT_ID'; -import * as CLIENT_KILL from '../commands/CLIENT_KILL'; -import * as CLIENT_LIST from '../commands/CLIENT_LIST'; -import * as CLIENT_NO_EVICT from '../commands/CLIENT_NO-EVICT'; -import * as CLIENT_NO_TOUCH from '../commands/CLIENT_NO-TOUCH'; -import * as CLIENT_PAUSE from '../commands/CLIENT_PAUSE'; -import * as CLIENT_SETNAME from '../commands/CLIENT_SETNAME'; -import * as CLIENT_TRACKING from '../commands/CLIENT_TRACKING'; -import * as CLIENT_TRACKINGINFO from '../commands/CLIENT_TRACKINGINFO'; -import * as CLIENT_UNPAUSE from '../commands/CLIENT_UNPAUSE'; -import * as CLIENT_INFO from '../commands/CLIENT_INFO'; -import * as CLUSTER_ADDSLOTS from '../commands/CLUSTER_ADDSLOTS'; -import * as CLUSTER_ADDSLOTSRANGE from '../commands/CLUSTER_ADDSLOTSRANGE'; -import * as CLUSTER_BUMPEPOCH from '../commands/CLUSTER_BUMPEPOCH'; -import * as CLUSTER_COUNT_FAILURE_REPORTS from '../commands/CLUSTER_COUNT-FAILURE-REPORTS'; -import * as CLUSTER_COUNTKEYSINSLOT from '../commands/CLUSTER_COUNTKEYSINSLOT'; -import * as CLUSTER_DELSLOTS from '../commands/CLUSTER_DELSLOTS'; -import * as CLUSTER_DELSLOTSRANGE from '../commands/CLUSTER_DELSLOTSRANGE'; -import * as CLUSTER_FAILOVER from '../commands/CLUSTER_FAILOVER'; -import * as CLUSTER_FLUSHSLOTS from '../commands/CLUSTER_FLUSHSLOTS'; -import * as CLUSTER_FORGET from '../commands/CLUSTER_FORGET'; -import * as CLUSTER_GETKEYSINSLOT from '../commands/CLUSTER_GETKEYSINSLOT'; -import * as CLUSTER_INFO from '../commands/CLUSTER_INFO'; -import * as CLUSTER_KEYSLOT from '../commands/CLUSTER_KEYSLOT'; -import * as CLUSTER_LINKS from '../commands/CLUSTER_LINKS'; -import * as CLUSTER_MEET from '../commands/CLUSTER_MEET'; -import * as CLUSTER_MYID from '../commands/CLUSTER_MYID'; -import * as CLUSTER_MYSHARDID from '../commands/CLUSTER_MYSHARDID'; -import * as CLUSTER_NODES from '../commands/CLUSTER_NODES'; -import * as CLUSTER_REPLICAS from '../commands/CLUSTER_REPLICAS'; -import * as CLUSTER_REPLICATE from '../commands/CLUSTER_REPLICATE'; -import * as CLUSTER_RESET from '../commands/CLUSTER_RESET'; -import * as CLUSTER_SAVECONFIG from '../commands/CLUSTER_SAVECONFIG'; -import * as CLUSTER_SET_CONFIG_EPOCH from '../commands/CLUSTER_SET-CONFIG-EPOCH'; -import * as CLUSTER_SETSLOT from '../commands/CLUSTER_SETSLOT'; -import * as CLUSTER_SLOTS from '../commands/CLUSTER_SLOTS'; -import * as COMMAND_COUNT from '../commands/COMMAND_COUNT'; -import * as COMMAND_GETKEYS from '../commands/COMMAND_GETKEYS'; -import * as COMMAND_GETKEYSANDFLAGS from '../commands/COMMAND_GETKEYSANDFLAGS'; -import * as COMMAND_INFO from '../commands/COMMAND_INFO'; -import * as COMMAND_LIST from '../commands/COMMAND_LIST'; -import * as COMMAND from '../commands/COMMAND'; -import * as CONFIG_GET from '../commands/CONFIG_GET'; -import * as CONFIG_RESETASTAT from '../commands/CONFIG_RESETSTAT'; -import * as CONFIG_REWRITE from '../commands/CONFIG_REWRITE'; -import * as CONFIG_SET from '../commands/CONFIG_SET'; -import * as DBSIZE from '../commands/DBSIZE'; -import * as DISCARD from '../commands/DISCARD'; -import * as ECHO from '../commands/ECHO'; -import * as FAILOVER from '../commands/FAILOVER'; -import * as FLUSHALL from '../commands/FLUSHALL'; -import * as FLUSHDB from '../commands/FLUSHDB'; -import * as FUNCTION_DELETE from '../commands/FUNCTION_DELETE'; -import * as FUNCTION_DUMP from '../commands/FUNCTION_DUMP'; -import * as FUNCTION_FLUSH from '../commands/FUNCTION_FLUSH'; -import * as FUNCTION_KILL from '../commands/FUNCTION_KILL'; -import * as FUNCTION_LIST_WITHCODE from '../commands/FUNCTION_LIST_WITHCODE'; -import * as FUNCTION_LIST from '../commands/FUNCTION_LIST'; -import * as FUNCTION_LOAD from '../commands/FUNCTION_LOAD'; -import * as FUNCTION_RESTORE from '../commands/FUNCTION_RESTORE'; -import * as FUNCTION_STATS from '../commands/FUNCTION_STATS'; -import * as HELLO from '../commands/HELLO'; -import * as INFO from '../commands/INFO'; -import * as KEYS from '../commands/KEYS'; -import * as LASTSAVE from '../commands/LASTSAVE'; -import * as LATENCY_DOCTOR from '../commands/LATENCY_DOCTOR'; -import * as LATENCY_GRAPH from '../commands/LATENCY_GRAPH'; -import * as LATENCY_HISTORY from '../commands/LATENCY_HISTORY'; -import * as LATENCY_LATEST from '../commands/LATENCY_LATEST'; -import * as LOLWUT from '../commands/LOLWUT'; -import * as MEMORY_DOCTOR from '../commands/MEMORY_DOCTOR'; -import * as MEMORY_MALLOC_STATS from '../commands/MEMORY_MALLOC-STATS'; -import * as MEMORY_PURGE from '../commands/MEMORY_PURGE'; -import * as MEMORY_STATS from '../commands/MEMORY_STATS'; -import * as MEMORY_USAGE from '../commands/MEMORY_USAGE'; -import * as MODULE_LIST from '../commands/MODULE_LIST'; -import * as MODULE_LOAD from '../commands/MODULE_LOAD'; -import * as MODULE_UNLOAD from '../commands/MODULE_UNLOAD'; -import * as MOVE from '../commands/MOVE'; -import * as PING from '../commands/PING'; -import * as PUBSUB_CHANNELS from '../commands/PUBSUB_CHANNELS'; -import * as PUBSUB_NUMPAT from '../commands/PUBSUB_NUMPAT'; -import * as PUBSUB_NUMSUB from '../commands/PUBSUB_NUMSUB'; -import * as PUBSUB_SHARDCHANNELS from '../commands/PUBSUB_SHARDCHANNELS'; -import * as PUBSUB_SHARDNUMSUB from '../commands/PUBSUB_SHARDNUMSUB'; -import * as RANDOMKEY from '../commands/RANDOMKEY'; -import * as READONLY from '../commands/READONLY'; -import * as READWRITE from '../commands/READWRITE'; -import * as REPLICAOF from '../commands/REPLICAOF'; -import * as RESTORE_ASKING from '../commands/RESTORE-ASKING'; -import * as ROLE from '../commands/ROLE'; -import * as SAVE from '../commands/SAVE'; -import * as SCAN from '../commands/SCAN'; -import * as SCRIPT_DEBUG from '../commands/SCRIPT_DEBUG'; -import * as SCRIPT_EXISTS from '../commands/SCRIPT_EXISTS'; -import * as SCRIPT_FLUSH from '../commands/SCRIPT_FLUSH'; -import * as SCRIPT_KILL from '../commands/SCRIPT_KILL'; -import * as SCRIPT_LOAD from '../commands/SCRIPT_LOAD'; -import * as SHUTDOWN from '../commands/SHUTDOWN'; -import * as SWAPDB from '../commands/SWAPDB'; -import * as TIME from '../commands/TIME'; -import * as UNWATCH from '../commands/UNWATCH'; -import * as WAIT from '../commands/WAIT'; - -export default { - ...CLUSTER_COMMANDS, - ACL_CAT, - aclCat: ACL_CAT, - ACL_DELUSER, - aclDelUser: ACL_DELUSER, - ACL_DRYRUN, - aclDryRun: ACL_DRYRUN, - ACL_GENPASS, - aclGenPass: ACL_GENPASS, - ACL_GETUSER, - aclGetUser: ACL_GETUSER, - ACL_LIST, - aclList: ACL_LIST, - ACL_LOAD, - aclLoad: ACL_LOAD, - ACL_LOG_RESET, - aclLogReset: ACL_LOG_RESET, - ACL_LOG, - aclLog: ACL_LOG, - ACL_SAVE, - aclSave: ACL_SAVE, - ACL_SETUSER, - aclSetUser: ACL_SETUSER, - ACL_USERS, - aclUsers: ACL_USERS, - ACL_WHOAMI, - aclWhoAmI: ACL_WHOAMI, - ASKING, - asking: ASKING, - AUTH, - auth: AUTH, - BGREWRITEAOF, - bgRewriteAof: BGREWRITEAOF, - BGSAVE, - bgSave: BGSAVE, - CLIENT_CACHING, - clientCaching: CLIENT_CACHING, - CLIENT_GETNAME, - clientGetName: CLIENT_GETNAME, - CLIENT_GETREDIR, - clientGetRedir: CLIENT_GETREDIR, - CLIENT_ID, - clientId: CLIENT_ID, - CLIENT_KILL, - clientKill: CLIENT_KILL, - 'CLIENT_NO-EVICT': CLIENT_NO_EVICT, - clientNoEvict: CLIENT_NO_EVICT, - 'CLIENT_NO-TOUCH': CLIENT_NO_TOUCH, - clientNoTouch: CLIENT_NO_TOUCH, - CLIENT_LIST, - clientList: CLIENT_LIST, - CLIENT_PAUSE, - clientPause: CLIENT_PAUSE, - CLIENT_SETNAME, - clientSetName: CLIENT_SETNAME, - CLIENT_TRACKING, - clientTracking: CLIENT_TRACKING, - CLIENT_TRACKINGINFO, - clientTrackingInfo: CLIENT_TRACKINGINFO, - CLIENT_UNPAUSE, - clientUnpause: CLIENT_UNPAUSE, - CLIENT_INFO, - clientInfo: CLIENT_INFO, - CLUSTER_ADDSLOTS, - clusterAddSlots: CLUSTER_ADDSLOTS, - CLUSTER_ADDSLOTSRANGE, - clusterAddSlotsRange: CLUSTER_ADDSLOTSRANGE, - CLUSTER_BUMPEPOCH, - clusterBumpEpoch: CLUSTER_BUMPEPOCH, - CLUSTER_COUNT_FAILURE_REPORTS, - clusterCountFailureReports: CLUSTER_COUNT_FAILURE_REPORTS, - CLUSTER_COUNTKEYSINSLOT, - clusterCountKeysInSlot: CLUSTER_COUNTKEYSINSLOT, - CLUSTER_DELSLOTS, - clusterDelSlots: CLUSTER_DELSLOTS, - CLUSTER_DELSLOTSRANGE, - clusterDelSlotsRange: CLUSTER_DELSLOTSRANGE, - CLUSTER_FAILOVER, - clusterFailover: CLUSTER_FAILOVER, - CLUSTER_FLUSHSLOTS, - clusterFlushSlots: CLUSTER_FLUSHSLOTS, - CLUSTER_FORGET, - clusterForget: CLUSTER_FORGET, - CLUSTER_GETKEYSINSLOT, - clusterGetKeysInSlot: CLUSTER_GETKEYSINSLOT, - CLUSTER_INFO, - clusterInfo: CLUSTER_INFO, - CLUSTER_KEYSLOT, - clusterKeySlot: CLUSTER_KEYSLOT, - CLUSTER_LINKS, - clusterLinks: CLUSTER_LINKS, - CLUSTER_MEET, - clusterMeet: CLUSTER_MEET, - CLUSTER_MYID, - clusterMyId: CLUSTER_MYID, - CLUSTER_MYSHARDID, - clusterMyShardId: CLUSTER_MYSHARDID, - CLUSTER_NODES, - clusterNodes: CLUSTER_NODES, - CLUSTER_REPLICAS, - clusterReplicas: CLUSTER_REPLICAS, - CLUSTER_REPLICATE, - clusterReplicate: CLUSTER_REPLICATE, - CLUSTER_RESET, - clusterReset: CLUSTER_RESET, - CLUSTER_SAVECONFIG, - clusterSaveConfig: CLUSTER_SAVECONFIG, - CLUSTER_SET_CONFIG_EPOCH, - clusterSetConfigEpoch: CLUSTER_SET_CONFIG_EPOCH, - CLUSTER_SETSLOT, - clusterSetSlot: CLUSTER_SETSLOT, - CLUSTER_SLOTS, - clusterSlots: CLUSTER_SLOTS, - COMMAND_COUNT, - commandCount: COMMAND_COUNT, - COMMAND_GETKEYS, - commandGetKeys: COMMAND_GETKEYS, - COMMAND_GETKEYSANDFLAGS, - commandGetKeysAndFlags: COMMAND_GETKEYSANDFLAGS, - COMMAND_INFO, - commandInfo: COMMAND_INFO, - COMMAND_LIST, - commandList: COMMAND_LIST, - COMMAND, - command: COMMAND, - CONFIG_GET, - configGet: CONFIG_GET, - CONFIG_RESETASTAT, - configResetStat: CONFIG_RESETASTAT, - CONFIG_REWRITE, - configRewrite: CONFIG_REWRITE, - CONFIG_SET, - configSet: CONFIG_SET, - DBSIZE, - dbSize: DBSIZE, - DISCARD, - discard: DISCARD, - ECHO, - echo: ECHO, - FAILOVER, - failover: FAILOVER, - FLUSHALL, - flushAll: FLUSHALL, - FLUSHDB, - flushDb: FLUSHDB, - FUNCTION_DELETE, - functionDelete: FUNCTION_DELETE, - FUNCTION_DUMP, - functionDump: FUNCTION_DUMP, - FUNCTION_FLUSH, - functionFlush: FUNCTION_FLUSH, - FUNCTION_KILL, - functionKill: FUNCTION_KILL, - FUNCTION_LIST_WITHCODE, - functionListWithCode: FUNCTION_LIST_WITHCODE, - FUNCTION_LIST, - functionList: FUNCTION_LIST, - FUNCTION_LOAD, - functionLoad: FUNCTION_LOAD, - FUNCTION_RESTORE, - functionRestore: FUNCTION_RESTORE, - FUNCTION_STATS, - functionStats: FUNCTION_STATS, - HELLO, - hello: HELLO, - INFO, - info: INFO, - KEYS, - keys: KEYS, - LASTSAVE, - lastSave: LASTSAVE, - LATENCY_DOCTOR, - latencyDoctor: LATENCY_DOCTOR, - LATENCY_GRAPH, - latencyGraph: LATENCY_GRAPH, - LATENCY_HISTORY, - latencyHistory: LATENCY_HISTORY, - LATENCY_LATEST, - latencyLatest: LATENCY_LATEST, - LOLWUT, - lolwut: LOLWUT, - MEMORY_DOCTOR, - memoryDoctor: MEMORY_DOCTOR, - 'MEMORY_MALLOC-STATS': MEMORY_MALLOC_STATS, - memoryMallocStats: MEMORY_MALLOC_STATS, - MEMORY_PURGE, - memoryPurge: MEMORY_PURGE, - MEMORY_STATS, - memoryStats: MEMORY_STATS, - MEMORY_USAGE, - memoryUsage: MEMORY_USAGE, - MODULE_LIST, - moduleList: MODULE_LIST, - MODULE_LOAD, - moduleLoad: MODULE_LOAD, - MODULE_UNLOAD, - moduleUnload: MODULE_UNLOAD, - MOVE, - move: MOVE, - PING, - ping: PING, - PUBSUB_CHANNELS, - pubSubChannels: PUBSUB_CHANNELS, - PUBSUB_NUMPAT, - pubSubNumPat: PUBSUB_NUMPAT, - PUBSUB_NUMSUB, - pubSubNumSub: PUBSUB_NUMSUB, - PUBSUB_SHARDCHANNELS, - pubSubShardChannels: PUBSUB_SHARDCHANNELS, - PUBSUB_SHARDNUMSUB, - pubSubShardNumSub: PUBSUB_SHARDNUMSUB, - RANDOMKEY, - randomKey: RANDOMKEY, - READONLY, - readonly: READONLY, - READWRITE, - readwrite: READWRITE, - REPLICAOF, - replicaOf: REPLICAOF, - 'RESTORE-ASKING': RESTORE_ASKING, - restoreAsking: RESTORE_ASKING, - ROLE, - role: ROLE, - SAVE, - save: SAVE, - SCAN, - scan: SCAN, - SCRIPT_DEBUG, - scriptDebug: SCRIPT_DEBUG, - SCRIPT_EXISTS, - scriptExists: SCRIPT_EXISTS, - SCRIPT_FLUSH, - scriptFlush: SCRIPT_FLUSH, - SCRIPT_KILL, - scriptKill: SCRIPT_KILL, - SCRIPT_LOAD, - scriptLoad: SCRIPT_LOAD, - SHUTDOWN, - shutdown: SHUTDOWN, - SWAPDB, - swapDb: SWAPDB, - TIME, - time: TIME, - UNWATCH, - unwatch: UNWATCH, - WAIT, - wait: WAIT -}; diff --git a/packages/client/lib/client/index.spec.ts b/packages/client/lib/client/index.spec.ts index 7f93efaa1c3..50ed3d39da1 100644 --- a/packages/client/lib/client/index.spec.ts +++ b/packages/client/lib/client/index.spec.ts @@ -1,1076 +1,800 @@ -import { strict as assert } from 'assert'; +import { strict as assert } from 'node:assert'; import testUtils, { GLOBAL, waitTillBeenCalled } from '../test-utils'; import RedisClient, { RedisClientType } from '.'; -import { RedisClientMultiCommandType } from './multi-command'; -import { RedisCommandRawReply, RedisModules, RedisFunctions, RedisScripts } from '../commands'; import { AbortError, ClientClosedError, ClientOfflineError, ConnectionTimeoutError, DisconnectsClientError, ErrorReply, MultiErrorReply, SocketClosedUnexpectedlyError, WatchError } from '../errors'; import { defineScript } from '../lua-script'; import { spy } from 'sinon'; -import { once } from 'events'; -import { ClientKillFilters } from '../commands/CLIENT_KILL'; -import { promisify } from 'util'; - -import {version} from '../../package.json'; +import { once } from 'node:events'; +import { MATH_FUNCTION, loadMathFunction } from '../commands/FUNCTION_LOAD.spec'; +import { RESP_TYPES } from '../RESP/decoder'; +import { BlobStringReply, NumberReply } from '../RESP/types'; +import { SortedSetMember } from '../commands/generic-transformers'; export const SQUARE_SCRIPT = defineScript({ - SCRIPT: 'return ARGV[1] * ARGV[1];', - NUMBER_OF_KEYS: 0, - transformArguments(number: number): Array { - return [number.toString()]; - } + SCRIPT: + `local number = redis.call('GET', KEYS[1]) + return number * number`, + NUMBER_OF_KEYS: 1, + FIRST_KEY_INDEX: 0, + transformArguments(key: string) { + return [key]; + }, + transformReply: undefined as unknown as () => NumberReply }); -export const MATH_FUNCTION = { - name: 'math', - engine: 'LUA', - code: `#!LUA name=math - redis.register_function{ - function_name = "square", - callback = function(keys, args) return args[1] * args[1] end, - flags = { "no-writes" } - }`, - library: { - square: { - NAME: 'square', - IS_READ_ONLY: true, - NUMBER_OF_KEYS: 0, - transformArguments(number: number): Array { - return [number.toString()]; - } - } - } -}; - -export async function loadMathFunction( - client: RedisClientType -): Promise { - await client.functionLoad( - MATH_FUNCTION.code, - { REPLACE: true } - ); -} - describe('Client', () => { - describe('parseURL', () => { - it('redis://user:secret@localhost:6379/0', () => { - assert.deepEqual( - RedisClient.parseURL('redis://user:secret@localhost:6379/0'), - { - socket: { - host: 'localhost', - port: 6379 - }, - username: 'user', - password: 'secret', - database: 0 - } - ); - }); - - it('rediss://user:secret@localhost:6379/0', () => { - assert.deepEqual( - RedisClient.parseURL('rediss://user:secret@localhost:6379/0'), - { - socket: { - host: 'localhost', - port: 6379, - tls: true - }, - username: 'user', - password: 'secret', - database: 0 - } - ); - }); - - it('Invalid protocol', () => { - assert.throws( - () => RedisClient.parseURL('redi://user:secret@localhost:6379/0'), - TypeError - ); - }); - - it('Invalid pathname', () => { - assert.throws( - () => RedisClient.parseURL('redis://user:secret@localhost:6379/NaN'), - TypeError - ); - }); - - it('redis://localhost', () => { - assert.deepEqual( - RedisClient.parseURL('redis://localhost'), - { - socket: { - host: 'localhost', - } - } - ); - }); + describe('parseURL', () => { + it('redis://user:secret@localhost:6379/0', () => { + assert.deepEqual( + RedisClient.parseURL('redis://user:secret@localhost:6379/0'), + { + socket: { + host: 'localhost', + port: 6379 + }, + username: 'user', + password: 'secret', + database: 0 + } + ); }); - describe('connect', () => { - testUtils.testWithClient('connect should return the client instance', async client => { - try { - assert.equal(await client.connect(), client); - } finally { - if (client.isOpen) await client.disconnect(); - } - }, { - ...GLOBAL.SERVERS.PASSWORD, - disableClientSetup: true - }); - - testUtils.testWithClient('should set default lib name and version', async client => { - const clientInfo = await client.clientInfo(); - - assert.equal(clientInfo.libName, 'node-redis'); - assert.equal(clientInfo.libVer, version); - }, { - ...GLOBAL.SERVERS.PASSWORD, - minimumDockerVersion: [7, 2] - }); - - testUtils.testWithClient('disable sending lib name and version', async client => { - const clientInfo = await client.clientInfo(); - - assert.equal(clientInfo.libName, ''); - assert.equal(clientInfo.libVer, ''); - }, { - ...GLOBAL.SERVERS.PASSWORD, - clientOptions: { - ...GLOBAL.SERVERS.PASSWORD.clientOptions, - disableClientInfo: true - }, - minimumDockerVersion: [7, 2] - }); + it('rediss://user:secret@localhost:6379/0', () => { + assert.deepEqual( + RedisClient.parseURL('rediss://user:secret@localhost:6379/0'), + { + socket: { + host: 'localhost', + port: 6379, + tls: true + }, + username: 'user', + password: 'secret', + database: 0 + } + ); + }); - testUtils.testWithClient('send client name tag', async client => { - const clientInfo = await client.clientInfo(); - - assert.equal(clientInfo.libName, 'node-redis(test)'); - assert.equal(clientInfo.libVer, version); - }, { - ...GLOBAL.SERVERS.PASSWORD, - clientOptions: { - ...GLOBAL.SERVERS.PASSWORD.clientOptions, - clientInfoTag: "test" - }, - minimumDockerVersion: [7, 2] - }); + it('Invalid protocol', () => { + assert.throws( + () => RedisClient.parseURL('redi://user:secret@localhost:6379/0'), + TypeError + ); }); - describe('authentication', () => { - testUtils.testWithClient('Client should be authenticated', async client => { - assert.equal( - await client.ping(), - 'PONG' - ); - }, GLOBAL.SERVERS.PASSWORD); - - testUtils.testWithClient('should execute AUTH before SELECT', async client => { - assert.equal( - (await client.clientInfo()).db, - 2 - ); - }, { - ...GLOBAL.SERVERS.PASSWORD, - clientOptions: { - ...GLOBAL.SERVERS.PASSWORD.clientOptions, - database: 2 - }, - minimumDockerVersion: [6, 2] - }); + it('Invalid pathname', () => { + assert.throws( + () => RedisClient.parseURL('redis://user:secret@localhost:6379/NaN'), + TypeError + ); }); - testUtils.testWithClient('should set connection name', async client => { - assert.equal( - await client.clientGetName(), - 'name' - ); - }, { - ...GLOBAL.SERVERS.OPEN, - clientOptions: { - name: 'name' + it('redis://localhost', () => { + assert.deepEqual( + RedisClient.parseURL('redis://localhost'), + { + socket: { + host: 'localhost', + } } + ); }); + }); + + describe('authentication', () => { + testUtils.testWithClient('Client should be authenticated', async client => { + assert.equal( + await client.ping(), + 'PONG' + ); + }, GLOBAL.SERVERS.PASSWORD); + + testUtils.testWithClient('should execute AUTH before SELECT', async client => { + assert.equal( + (await client.clientInfo()).db, + 2 + ); + }, { + ...GLOBAL.SERVERS.PASSWORD, + clientOptions: { + ...GLOBAL.SERVERS.PASSWORD.clientOptions, + database: 2 + }, + minimumDockerVersion: [6, 2] + }); + }); - describe('legacyMode', () => { - testUtils.testWithClient('client.sendCommand should call the callback', async client => { - assert.equal( - await promisify(client.sendCommand).call(client, 'PING'), - 'PONG' - ); - }, { - ...GLOBAL.SERVERS.OPEN, - clientOptions: { - legacyMode: true - } - }); - - testUtils.testWithClient('client.sendCommand should work without callback', async client => { - client.sendCommand(['PING']); - await client.v4.ping(); // make sure the first command was replied - }, { - ...GLOBAL.SERVERS.OPEN, - clientOptions: { - legacyMode: true - } - }); - - testUtils.testWithClient('client.sendCommand should reply with error', async client => { - await assert.rejects( - promisify(client.sendCommand).call(client, '1', '2') - ); - }, { - ...GLOBAL.SERVERS.OPEN, - clientOptions: { - legacyMode: true - } - }); - - testUtils.testWithClient('client.hGetAll should reply with error', async client => { - await assert.rejects( - promisify(client.hGetAll).call(client) - ); - }, { - ...GLOBAL.SERVERS.OPEN, - clientOptions: { - legacyMode: true - } - }); - - testUtils.testWithClient('client.v4.sendCommand should return a promise', async client => { - assert.equal( - await client.v4.sendCommand(['PING']), - 'PONG' - ); - }, { - ...GLOBAL.SERVERS.OPEN, - clientOptions: { - legacyMode: true - } - }); - - testUtils.testWithClient('client.v4.{command} should return a promise', async client => { - assert.equal( - await client.v4.ping(), - 'PONG' - ); - }, { - ...GLOBAL.SERVERS.OPEN, - clientOptions: { - legacyMode: true - } - }); - - testUtils.testWithClient('client.{command} should accept vardict arguments', async client => { - assert.equal( - await promisify(client.set).call(client, 'a', 'b'), - 'OK' - ); - }, { - ...GLOBAL.SERVERS.OPEN, - clientOptions: { - legacyMode: true - } - }); - - testUtils.testWithClient('client.{command} should accept arguments array', async client => { - assert.equal( - await promisify(client.set).call(client, ['a', 'b']), - 'OK' - ); - }, { - ...GLOBAL.SERVERS.OPEN, - clientOptions: { - legacyMode: true - } - }); - - testUtils.testWithClient('client.{command} should accept mix of arrays and arguments', async client => { - assert.equal( - await promisify(client.set).call(client, ['a'], 'b', ['EX', 1]), - 'OK' - ); - }, { - ...GLOBAL.SERVERS.OPEN, - clientOptions: { - legacyMode: true - } - }); - - testUtils.testWithClient('client.hGetAll should return object', async client => { - await client.v4.hSet('key', 'field', 'value'); - - assert.deepEqual( - await promisify(client.hGetAll).call(client, 'key'), - Object.create(null, { - field: { - value: 'value', - configurable: true, - enumerable: true - } - }) - ); - }, { - ...GLOBAL.SERVERS.OPEN, - clientOptions: { - legacyMode: true - } - }); + testUtils.testWithClient('should set connection name', async client => { + assert.equal( + await client.clientGetName(), + 'name' + ); + }, { + ...GLOBAL.SERVERS.OPEN, + clientOptions: { + name: 'name' + } + }); + + // TODO: fix & uncomment + // testUtils.testWithClient('connect, ready and end events', async client => { + // await Promise.all([ + // once(client, 'connect'), + // once(client, 'ready'), + // client.connect() + // ]); + + // await Promise.all([ + // once(client, 'end'), + // client.close() + // ]); + // }, { + // ...GLOBAL.SERVERS.OPEN, + // disableClientSetup: true + // }); + + describe('sendCommand', () => { + testUtils.testWithClient('PING', async client => { + assert.equal(await client.sendCommand(['PING']), 'PONG'); + }, GLOBAL.SERVERS.OPEN); - function multiExecAsync< - M extends RedisModules, - F extends RedisFunctions, - S extends RedisScripts - >(multi: RedisClientMultiCommandType): Promise> { - return new Promise((resolve, reject) => { - (multi as any).exec((err: Error | undefined, replies: Array) => { - if (err) return reject(err); - - resolve(replies); - }); - }); + describe('AbortController', () => { + before(function () { + if (!global.AbortController) { + this.skip(); } + }); - testUtils.testWithClient('client.multi.ping.exec should call the callback', async client => { - assert.deepEqual( - await multiExecAsync( - client.multi().ping() - ), - ['PONG'] - ); - }, { - ...GLOBAL.SERVERS.OPEN, - clientOptions: { - legacyMode: true - } + testUtils.testWithClient('success', async client => { + await client.sendCommand(['PING'], { + abortSignal: new AbortController().signal }); + }, GLOBAL.SERVERS.OPEN); - testUtils.testWithClient('client.multi.ping.exec should call the callback', async client => { - client.multi() - .ping() - .exec(); - await client.v4.ping(); // make sure the first command was replied - }, { - ...GLOBAL.SERVERS.OPEN, - clientOptions: { - legacyMode: true - } - }); + testUtils.testWithClient('AbortError', client => { + const controller = new AbortController(); + controller.abort(); - testUtils.testWithClient('client.multi.ping.v4.ping.v4.exec should return a promise', async client => { - assert.deepEqual( - await client.multi() - .ping() - .v4.ping() - .v4.exec(), - ['PONG', 'PONG'] - ); - }, { - ...GLOBAL.SERVERS.OPEN, - clientOptions: { - legacyMode: true - } - }); + return assert.rejects( + client.sendCommand(['PING'], { + abortSignal: controller.signal + }), + AbortError + ); + }, GLOBAL.SERVERS.OPEN); + }); - testUtils.testWithClient('client.{script} should return a promise', async client => { - assert.equal( - await client.square(2), - 4 - ); - }, { - ...GLOBAL.SERVERS.OPEN, - clientOptions: { - legacyMode: true, - scripts: { - square: SQUARE_SCRIPT - } - } - }); + testUtils.testWithClient('undefined and null should not break the client', async client => { + await assert.rejects( + client.sendCommand([null as any, undefined as any]), + TypeError + ); - testUtils.testWithClient('client.multi.{command}.exec should flatten array arguments', async client => { - assert.deepEqual( - await client.multi() - .sAdd('a', ['b', 'c']) - .v4.exec(), - [2] - ); - }, { - ...GLOBAL.SERVERS.OPEN, - clientOptions: { - legacyMode: true - } - }); - - testUtils.testWithClient('client.multi.hGetAll should return object', async client => { - assert.deepEqual( - await multiExecAsync( - client.multi() - .hSet('key', 'field', 'value') - .hGetAll('key') - ), - [ - 1, - Object.create(null, { - field: { - value: 'value', - configurable: true, - enumerable: true - } - }) - ] - ); - }, { - ...GLOBAL.SERVERS.OPEN, - clientOptions: { - legacyMode: true - } - }); - }); + assert.equal( + await client.ping(), + 'PONG' + ); + }, GLOBAL.SERVERS.OPEN); + }); + + describe('multi', () => { + testUtils.testWithClient('simple', async client => { + assert.deepEqual( + await client.multi() + .ping() + .set('key', 'value') + .get('key') + .exec(), + ['PONG', 'OK', 'value'] + ); + }, GLOBAL.SERVERS.OPEN); - describe('events', () => { - testUtils.testWithClient('connect, ready, end', async client => { - await Promise.all([ - once(client, 'connect'), - once(client, 'ready'), - client.connect() - ]); - - await Promise.all([ - once(client, 'end'), - client.disconnect() - ]); - }, { - ...GLOBAL.SERVERS.OPEN, - disableClientSetup: true - }); - }); + testUtils.testWithClient('should reject the whole chain on error', client => { + return assert.rejects( + client.multi() + .ping() + .addCommand(['INVALID COMMAND']) + .ping() + .exec() + ); + }, GLOBAL.SERVERS.OPEN); - describe('sendCommand', () => { - testUtils.testWithClient('PING', async client => { - assert.equal(await client.sendCommand(['PING']), 'PONG'); - }, GLOBAL.SERVERS.OPEN); - - testUtils.testWithClient('returnBuffers', async client => { - assert.deepEqual( - await client.sendCommand(['PING'], { - returnBuffers: true - }), - Buffer.from('PONG') - ); - }, GLOBAL.SERVERS.OPEN); - - describe('AbortController', () => { - before(function () { - if (!global.AbortController) { - this.skip(); - } - }); - - testUtils.testWithClient('success', async client => { - await client.sendCommand(['PING'], { - signal: new AbortController().signal - }); - }, GLOBAL.SERVERS.OPEN); - - testUtils.testWithClient('AbortError', client => { - const controller = new AbortController(); - controller.abort(); - - return assert.rejects( - client.sendCommand(['PING'], { - signal: controller.signal - }), - AbortError - ); - }, GLOBAL.SERVERS.OPEN); - }); + testUtils.testWithClient('should reject the whole chain upon client disconnect', async client => { + await client.close(); + + return assert.rejects( + client.multi() + .ping() + .set('key', 'value') + .get('key') + .exec(), + ClientClosedError + ); + }, GLOBAL.SERVERS.OPEN); - testUtils.testWithClient('undefined and null should not break the client', async client => { - await assert.rejects( - client.sendCommand([null as any, undefined as any]), - TypeError - ); - - assert.equal( - await client.ping(), - 'PONG' - ); - }, GLOBAL.SERVERS.OPEN); + testUtils.testWithClient('with script', async client => { + assert.deepEqual( + await client.multi() + .set('key', '2') + .square('key') + .exec(), + ['OK', 4] + ); + }, { + ...GLOBAL.SERVERS.OPEN, + clientOptions: { + scripts: { + square: SQUARE_SCRIPT + } + } }); - describe('multi', () => { - testUtils.testWithClient('simple', async client => { - assert.deepEqual( - await client.multi() - .ping() - .set('key', 'value') - .get('key') - .exec(), - ['PONG', 'OK', 'value'] - ); - }, GLOBAL.SERVERS.OPEN); - - testUtils.testWithClient('should reject the whole chain on error', client => { - return assert.rejects( - client.multi() - .ping() - .addCommand(['INVALID COMMAND']) - .ping() - .exec() - ); - }, GLOBAL.SERVERS.OPEN); - - testUtils.testWithClient('should reject the whole chain upon client disconnect', async client => { - await client.disconnect(); - - return assert.rejects( - client.multi() - .ping() - .set('key', 'value') - .get('key') - .exec(), - ClientClosedError - ); - }, GLOBAL.SERVERS.OPEN); - - testUtils.testWithClient('with script', async client => { - assert.deepEqual( - await client.multi() - .square(2) - .exec(), - [4] - ); - }, { - ...GLOBAL.SERVERS.OPEN, - clientOptions: { - scripts: { - square: SQUARE_SCRIPT - } - } - }); + testUtils.testWithClient('WatchError', async client => { + await client.watch('key'); - testUtils.testWithClient('WatchError', async client => { - await client.watch('key'); - - await client.set( - RedisClient.commandOptions({ - isolated: true - }), - 'key', - '1' - ); - - await assert.rejects( - client.multi() - .decr('key') - .exec(), - WatchError - ); - }, GLOBAL.SERVERS.OPEN); - - describe('execAsPipeline', () => { - testUtils.testWithClient('exec(true)', async client => { - assert.deepEqual( - await client.multi() - .ping() - .exec(true), - ['PONG'] - ); - }, GLOBAL.SERVERS.OPEN); - - testUtils.testWithClient('empty execAsPipeline', async client => { - assert.deepEqual( - await client.multi().execAsPipeline(), - [] - ); - }, GLOBAL.SERVERS.OPEN); - }); + const duplicate = await client.duplicate().connect(); + try { + await client.set( + 'key', + '1' + ); + } finally { + duplicate.destroy(); + } + + await assert.rejects( + client.multi() + .decr('key') + .exec(), + WatchError + ); + }, GLOBAL.SERVERS.OPEN); - testUtils.testWithClient('should remember selected db', async client => { - await client.multi() - .select(1) - .exec(); - await killClient(client); - assert.equal( - (await client.clientInfo()).db, - 1 - ); - }, { - ...GLOBAL.SERVERS.OPEN, - minimumDockerVersion: [6, 2] // CLIENT INFO - }); + describe('execAsPipeline', () => { + testUtils.testWithClient('exec(true)', async client => { + assert.deepEqual( + await client.multi() + .ping() + .exec(true), + ['PONG'] + ); + }, GLOBAL.SERVERS.OPEN); - testUtils.testWithClient('should handle error replies (#2665)', async client => { - await assert.rejects( - client.multi() - .set('key', 'value') - .hGetAll('key') - .exec(), - err => { - assert.ok(err instanceof MultiErrorReply); - assert.equal(err.replies.length, 2); - assert.deepEqual(err.errorIndexes, [1]); - assert.ok(err.replies[1] instanceof ErrorReply); - assert.deepEqual([...err.errors()], [err.replies[1]]); - return true; - } - ); - }, GLOBAL.SERVERS.OPEN); + testUtils.testWithClient('empty execAsPipeline', async client => { + assert.deepEqual( + await client.multi().execAsPipeline(), + [] + ); + }, GLOBAL.SERVERS.OPEN); }); - testUtils.testWithClient('scripts', async client => { - assert.equal( - await client.square(2), - 4 - ); + testUtils.testWithClient('should remember selected db', async client => { + await client.multi() + .select(1) + .exec(); + await killClient(client); + assert.equal( + (await client.clientInfo()).db, + 1 + ); }, { - ...GLOBAL.SERVERS.OPEN, - clientOptions: { - scripts: { - square: SQUARE_SCRIPT - } - } + ...GLOBAL.SERVERS.OPEN, + minimumDockerVersion: [6, 2] // CLIENT INFO }); - const module = { - echo: { - transformArguments(message: string): Array { - return ['ECHO', message]; - }, - transformReply(reply: string): string { - return reply; - } + testUtils.testWithClient('should handle error replies (#2665)', async client => { + await assert.rejects( + client.multi() + .set('key', 'value') + .hGetAll('key') + .exec(), + err => { + assert.ok(err instanceof MultiErrorReply); + assert.equal(err.replies.length, 2); + assert.deepEqual(err.errorIndexes, [1]); + assert.ok(err.replies[1] instanceof ErrorReply); + assert.deepEqual([...err.errors()], [err.replies[1]]); + return true; } - }; + ); + }, GLOBAL.SERVERS.OPEN); + }); + + testUtils.testWithClient('scripts', async client => { + const [, reply] = await Promise.all([ + client.set('key', '2'), + client.square('key') + ]); + + assert.equal(reply, 4); + }, { + ...GLOBAL.SERVERS.OPEN, + clientOptions: { + scripts: { + square: SQUARE_SCRIPT + } + } + }); + + const module = { + echo: { + transformArguments(message: string) { + return ['ECHO', message]; + }, + transformReply: undefined as unknown as () => BlobStringReply + } + }; - testUtils.testWithClient('modules', async client => { - assert.equal( - await client.module.echo('message'), - 'message' - ); - }, { - ...GLOBAL.SERVERS.OPEN, - clientOptions: { - modules: { - module - } - } - }); + testUtils.testWithClient('modules', async client => { + assert.equal( + await client.module.echo('message'), + 'message' + ); + }, { + ...GLOBAL.SERVERS.OPEN, + clientOptions: { + modules: { + module + } + } + }); + + testUtils.testWithClient('functions', async client => { + const [,, reply] = await Promise.all([ + loadMathFunction(client), + client.set('key', '2'), + client.math.square('key') + ]); + + assert.equal(reply, 4); + }, { + ...GLOBAL.SERVERS.OPEN, + minimumDockerVersion: [7, 0], + clientOptions: { + functions: { + math: MATH_FUNCTION.library + } + } + }); - testUtils.testWithClient('functions', async client => { - await loadMathFunction(client); + testUtils.testWithClient('duplicate should reuse command options', async client => { + const duplicate = client.duplicate(); - assert.equal( - await client.math.square(2), - 4 - ); - }, { - ...GLOBAL.SERVERS.OPEN, - minimumDockerVersion: [7, 0], - clientOptions: { - functions: { - math: MATH_FUNCTION.library - } + await duplicate.connect(); + + try { + assert.deepEqual( + await duplicate.ping(), + Buffer.from('PONG') + ); + } finally { + duplicate.close(); + } + }, { + ...GLOBAL.SERVERS.OPEN, + clientOptions: { + commandOptions: { + typeMapping: { + [RESP_TYPES.SIMPLE_STRING]: Buffer } - }); + } + }, + disableClientSetup: true, + }); + + async function killClient( + client: RedisClientType, + errorClient: RedisClientType = client + ): Promise { + const onceErrorPromise = once(errorClient, 'error'); + await client.sendCommand(['QUIT']); + await Promise.all([ + onceErrorPromise, + assert.rejects(client.ping()) + ]); + } + + testUtils.testWithClient('should reconnect when socket disconnects', async client => { + await killClient(client); + await assert.doesNotReject(client.ping()); + }, GLOBAL.SERVERS.OPEN); + + testUtils.testWithClient('should remember selected db', async client => { + await client.select(1); + await killClient(client); + assert.equal( + (await client.clientInfo()).db, + 1 + ); + }, { + ...GLOBAL.SERVERS.OPEN, + minimumDockerVersion: [6, 2] // CLIENT INFO + }); + + testUtils.testWithClient('scanIterator', async client => { + const entries: Array = [], + keys = new Set(); + for (let i = 0; i < 100; i++) { + const key = i.toString(); + keys.add(key); + entries.push(key, ''); + } - describe('isolationPool', () => { - testUtils.testWithClient('executeIsolated', async client => { - const id = await client.clientId(), - isolatedId = await client.executeIsolated(isolatedClient => isolatedClient.clientId()); - assert.ok(id !== isolatedId); - }, GLOBAL.SERVERS.OPEN); - - testUtils.testWithClient('should be able to use pool even before connect', async client => { - await client.executeIsolated(() => Promise.resolve()); - // make sure to destroy isolation pool - await client.connect(); - await client.disconnect(); - }, { - ...GLOBAL.SERVERS.OPEN, - disableClientSetup: true - }); + await client.mSet(entries); - testUtils.testWithClient('should work after reconnect (#2406)', async client => { - await client.disconnect(); - await client.connect(); - await client.executeIsolated(() => Promise.resolve()); - }, GLOBAL.SERVERS.OPEN); - - testUtils.testWithClient('should throw ClientClosedError after disconnect', async client => { - await client.connect(); - await client.disconnect(); - await assert.rejects( - client.executeIsolated(() => Promise.resolve()), - ClientClosedError - ); - }, { - ...GLOBAL.SERVERS.OPEN, - disableClientSetup: true - }); - }); + const results = new Set(); + for await (const keys of client.scanIterator()) { + for (const key of keys) { + results.add(key); + } + } - async function killClient< - M extends RedisModules, - F extends RedisFunctions, - S extends RedisScripts - >( - client: RedisClientType, - errorClient: RedisClientType = client - ): Promise { - const onceErrorPromise = once(errorClient, 'error'); - await client.sendCommand(['QUIT']); - await Promise.all([ - onceErrorPromise, - assert.rejects(client.ping(), SocketClosedUnexpectedlyError) - ]); + assert.deepEqual(keys, results); + }, GLOBAL.SERVERS.OPEN); + + testUtils.testWithClient('hScanIterator', async client => { + const hash: Record = {}; + for (let i = 0; i < 100; i++) { + hash[i.toString()] = i.toString(); } - testUtils.testWithClient('should reconnect when socket disconnects', async client => { - await killClient(client); - await assert.doesNotReject(client.ping()); - }, GLOBAL.SERVERS.OPEN); + await client.hSet('key', hash); - testUtils.testWithClient('should remember selected db', async client => { - await client.select(1); - await killClient(client); - assert.equal( - (await client.clientInfo()).db, - 1 - ); - }, { - ...GLOBAL.SERVERS.OPEN, - minimumDockerVersion: [6, 2] // CLIENT INFO - }); + const results: Record = {}; + for await (const entries of client.hScanIterator('key')) { + for (const { field, value } of entries) { + results[field] = value; + } + } - testUtils.testWithClient('should propagated errors from "isolated" clients', client => { - client.on('error', () => { - // ignore errors - }); - return client.executeIsolated(isolated => killClient(isolated, client)); - }, GLOBAL.SERVERS.OPEN); + assert.deepEqual(hash, results); + }, GLOBAL.SERVERS.OPEN); - testUtils.testWithClient('scanIterator', async client => { - const promises = [], - keys = new Set(); - for (let i = 0; i < 100; i++) { - const key = i.toString(); - keys.add(key); - promises.push(client.set(key, '')); - } + testUtils.testWithClient('hScanNoValuesIterator', async client => { + const hash: Record = {}; + const expectedFields: Array = []; + for (let i = 0; i < 100; i++) { + hash[i.toString()] = i.toString(); + expectedFields.push(i.toString()); + } - await Promise.all(promises); + await client.hSet('key', hash); - const results = new Set(); - for await (const key of client.scanIterator()) { - results.add(key); - } + const actualFields: Array = []; + for await (const fields of client.hScanNoValuesIterator('key')) { + for (const field of fields) { + actualFields.push(field); + } + } - assert.deepEqual(keys, results); - }, GLOBAL.SERVERS.OPEN); + function sort(a: string, b: string) { + return Number(a) - Number(b); + } - testUtils.testWithClient('hScanIterator', async client => { - const hash: Record = {}; - for (let i = 0; i < 100; i++) { - hash[i.toString()] = i.toString(); - } + assert.deepEqual(actualFields.sort(sort), expectedFields); + }, { + ...GLOBAL.SERVERS.OPEN, + minimumDockerVersion: [7, 4] + }); - await client.hSet('key', hash); + testUtils.testWithClient('sScanIterator', async client => { + const members = new Set(); + for (let i = 0; i < 100; i++) { + members.add(i.toString()); + } - const results: Record = {}; - for await (const { field, value } of client.hScanIterator('key')) { - results[field] = value; - } + await client.sAdd('key', Array.from(members)); - assert.deepEqual(hash, results); - }, GLOBAL.SERVERS.OPEN); + const results = new Set(); + for await (const members of client.sScanIterator('key')) { + for (const member of members) { + results.add(member); + } + } - testUtils.testWithClient('hScanNoValuesIterator', async client => { - const hash: Record = {}; - const expectedKeys: Array = []; - for (let i = 0; i < 100; i++) { - hash[i.toString()] = i.toString(); - expectedKeys.push(i.toString()); - } + assert.deepEqual(members, results); + }, GLOBAL.SERVERS.OPEN); + + testUtils.testWithClient('zScanIterator', async client => { + const members: Array = [], + map = new Map(); + for (let i = 0; i < 100; i++) { + const member = { + value: i.toString(), + score: 1 + }; + map.set(member.value, member.score); + members.push(member); + } - await client.hSet('key', hash); + await client.zAdd('key', members); - const keys: Array = []; - for await (const key of client.hScanNoValuesIterator('key')) { - keys.push(key); - } + const results = new Map(); + for await (const members of client.zScanIterator('key')) { + for (const { value, score } of members) { + results.set(value, score); + } + } - function sort(a: string, b: string) { - return Number(a) - Number(b); - } + assert.deepEqual(map, results); + }, GLOBAL.SERVERS.OPEN); - assert.deepEqual(keys.sort(sort), expectedKeys); - }, { - ...GLOBAL.SERVERS.OPEN, - minimumDockerVersion: [7, 4] - }); + describe('PubSub', () => { + testUtils.testWithClient('should be able to publish and subscribe to messages', async publisher => { + function assertStringListener(message: string, channel: string) { + assert.equal(typeof message, 'string'); + assert.equal(typeof channel, 'string'); + } - testUtils.testWithClient('sScanIterator', async client => { - const members = new Set(); - for (let i = 0; i < 100; i++) { - members.add(i.toString()); - } + function assertBufferListener(message: Buffer, channel: Buffer) { + assert.ok(message instanceof Buffer); + assert.ok(channel instanceof Buffer); + } - await client.sAdd('key', Array.from(members)); + const subscriber = await publisher.duplicate().connect(); - const results = new Set(); - for await (const key of client.sScanIterator('key')) { - results.add(key); - } + try { + const channelListener1 = spy(assertBufferListener), + channelListener2 = spy(assertStringListener), + patternListener = spy(assertStringListener); - assert.deepEqual(members, results); + await Promise.all([ + subscriber.subscribe('channel', channelListener1, true), + subscriber.subscribe('channel', channelListener2), + subscriber.pSubscribe('channel*', patternListener) + ]); + await Promise.all([ + waitTillBeenCalled(channelListener1), + waitTillBeenCalled(channelListener2), + waitTillBeenCalled(patternListener), + publisher.publish(Buffer.from('channel'), Buffer.from('message')) + ]); + assert.ok(channelListener1.calledOnceWithExactly(Buffer.from('message'), Buffer.from('channel'))); + assert.ok(channelListener2.calledOnceWithExactly('message', 'channel')); + assert.ok(patternListener.calledOnceWithExactly('message', 'channel')); + + await subscriber.unsubscribe('channel', channelListener1, true); + await Promise.all([ + waitTillBeenCalled(channelListener2), + waitTillBeenCalled(patternListener), + publisher.publish('channel', 'message') + ]); + assert.ok(channelListener1.calledOnce); + assert.ok(channelListener2.calledTwice); + assert.ok(channelListener2.secondCall.calledWithExactly('message', 'channel')); + assert.ok(patternListener.calledTwice); + assert.ok(patternListener.secondCall.calledWithExactly('message', 'channel')); + await subscriber.unsubscribe('channel'); + await Promise.all([ + waitTillBeenCalled(patternListener), + publisher.publish('channel', 'message') + ]); + assert.ok(channelListener1.calledOnce); + assert.ok(channelListener2.calledTwice); + assert.ok(patternListener.calledThrice); + assert.ok(patternListener.thirdCall.calledWithExactly('message', 'channel')); + + await subscriber.pUnsubscribe(); + await publisher.publish('channel', 'message'); + assert.ok(channelListener1.calledOnce); + assert.ok(channelListener2.calledTwice); + assert.ok(patternListener.calledThrice); + + // should be able to send commands when unsubsribed from all channels (see #1652) + await assert.doesNotReject(subscriber.ping()); + } finally { + subscriber.destroy(); + } }, GLOBAL.SERVERS.OPEN); - testUtils.testWithClient('zScanIterator', async client => { - const members = []; - for (let i = 0; i < 100; i++) { - members.push({ - score: 1, - value: i.toString() - }); - } + testUtils.testWithClient('should resubscribe', async publisher => { + const subscriber = await publisher.duplicate().connect(); - await client.zAdd('key', members); + try { + const channelListener = spy(); + await subscriber.subscribe('channel', channelListener); - const map = new Map(); - for await (const member of client.zScanIterator('key')) { - map.set(member.value, member.score); - } + const patternListener = spy(); + await subscriber.pSubscribe('channe*', patternListener); - type MemberTuple = [string, number]; + await Promise.all([ + once(subscriber, 'error'), + publisher.clientKill({ + filter: 'SKIPME', + skipMe: true + }) + ]); - function sort(a: MemberTuple, b: MemberTuple) { - return Number(b[0]) - Number(a[0]); - } + await once(subscriber, 'ready'); - assert.deepEqual( - [...map.entries()].sort(sort), - members.map(member => [member.value, member.score]).sort(sort) - ); + await Promise.all([ + waitTillBeenCalled(channelListener), + waitTillBeenCalled(patternListener), + publisher.publish('channel', 'message') + ]); + } finally { + subscriber.destroy(); + } }, GLOBAL.SERVERS.OPEN); - - describe('PubSub', () => { - testUtils.testWithClient('should be able to publish and subscribe to messages', async publisher => { - function assertStringListener(message: string, channel: string) { - assert.equal(typeof message, 'string'); - assert.equal(typeof channel, 'string'); - } - - function assertBufferListener(message: Buffer, channel: Buffer) { - assert.ok(Buffer.isBuffer(message)); - assert.ok(Buffer.isBuffer(channel)); - } - - const subscriber = publisher.duplicate(); - - await subscriber.connect(); - - try { - const channelListener1 = spy(assertBufferListener), - channelListener2 = spy(assertStringListener), - patternListener = spy(assertStringListener); - - await Promise.all([ - subscriber.subscribe('channel', channelListener1, true), - subscriber.subscribe('channel', channelListener2), - subscriber.pSubscribe('channel*', patternListener) - ]); - await Promise.all([ - waitTillBeenCalled(channelListener1), - waitTillBeenCalled(channelListener2), - waitTillBeenCalled(patternListener), - publisher.publish(Buffer.from('channel'), Buffer.from('message')) - ]); - - assert.ok(channelListener1.calledOnceWithExactly(Buffer.from('message'), Buffer.from('channel'))); - assert.ok(channelListener2.calledOnceWithExactly('message', 'channel')); - assert.ok(patternListener.calledOnceWithExactly('message', 'channel')); - - await subscriber.unsubscribe('channel', channelListener1, true); - await Promise.all([ - waitTillBeenCalled(channelListener2), - waitTillBeenCalled(patternListener), - publisher.publish('channel', 'message') - ]); - assert.ok(channelListener1.calledOnce); - assert.ok(channelListener2.calledTwice); - assert.ok(channelListener2.secondCall.calledWithExactly('message', 'channel')); - assert.ok(patternListener.calledTwice); - assert.ok(patternListener.secondCall.calledWithExactly('message', 'channel')); - await subscriber.unsubscribe('channel'); - await Promise.all([ - waitTillBeenCalled(patternListener), - publisher.publish('channel', 'message') - ]); - assert.ok(channelListener1.calledOnce); - assert.ok(channelListener2.calledTwice); - assert.ok(patternListener.calledThrice); - assert.ok(patternListener.thirdCall.calledWithExactly('message', 'channel')); - await subscriber.pUnsubscribe(); - await publisher.publish('channel', 'message'); - assert.ok(channelListener1.calledOnce); - assert.ok(channelListener2.calledTwice); - assert.ok(patternListener.calledThrice); - // should be able to send commands when unsubsribed from all channels (see #1652) - await assert.doesNotReject(subscriber.ping()); - } finally { - await subscriber.disconnect(); - } - }, GLOBAL.SERVERS.OPEN); - - testUtils.testWithClient('should resubscribe', async publisher => { - const subscriber = publisher.duplicate(); - - await subscriber.connect(); - - try { - const channelListener = spy(); - await subscriber.subscribe('channel', channelListener); - - const patternListener = spy(); - await subscriber.pSubscribe('channe*', patternListener); - - await Promise.all([ - once(subscriber, 'error'), - publisher.clientKill({ - filter: ClientKillFilters.SKIP_ME, - skipMe: true - }) - ]); - - await once(subscriber, 'ready'); - - await Promise.all([ - waitTillBeenCalled(channelListener), - waitTillBeenCalled(patternListener), - publisher.publish('channel', 'message') - ]); - } finally { - await subscriber.disconnect(); - } - }, GLOBAL.SERVERS.OPEN); - - testUtils.testWithClient('should not fail when message arrives right after subscribe', async publisher => { - const subscriber = publisher.duplicate(); - - await subscriber.connect(); - - try { - await assert.doesNotReject(Promise.all([ - subscriber.subscribe('channel', () => { - // noop - }), - publisher.publish('channel', 'message') - ])); - } finally { - await subscriber.disconnect(); - } - }, GLOBAL.SERVERS.OPEN); - - testUtils.testWithClient('should be able to quit in PubSub mode', async client => { - await client.subscribe('channel', () => { - // noop - }); - - await assert.doesNotReject(client.quit()); - - assert.equal(client.isOpen, false); - }, GLOBAL.SERVERS.OPEN); - }); - testUtils.testWithClient('ConnectionTimeoutError', async client => { - const promise = assert.rejects(client.connect(), ConnectionTimeoutError), - start = process.hrtime.bigint(); + testUtils.testWithClient('should not fail when message arrives right after subscribe', async publisher => { + const subscriber = await publisher.duplicate().connect(); + + try { + await assert.doesNotReject(Promise.all([ + subscriber.subscribe('channel', () => { + // noop + }), + publisher.publish('channel', 'message') + ])); + } finally { + subscriber.destroy(); + } + }, GLOBAL.SERVERS.OPEN); - while (process.hrtime.bigint() - start < 1_000_000) { - // block the event loop for 1ms, to make sure the connection will timeout - } + testUtils.testWithClient('should be able to quit in PubSub mode', async client => { + await client.subscribe('channel', () => { + // noop + }); - await promise; - }, { - ...GLOBAL.SERVERS.OPEN, - clientOptions: { - socket: { - connectTimeout: 1 - } - }, - disableClientSetup: true - }); + await assert.doesNotReject(client.quit()); - testUtils.testWithClient('client.quit', async client => { - await client.connect(); + assert.equal(client.isOpen, false); + }, GLOBAL.SERVERS.OPEN); + }); - const pingPromise = client.ping(), - quitPromise = client.quit(); - assert.equal(client.isOpen, false); + testUtils.testWithClient('ConnectionTimeoutError', async client => { + const promise = assert.rejects(client.connect(), ConnectionTimeoutError), + start = process.hrtime.bigint(); - const [ping, quit] = await Promise.all([ - pingPromise, - quitPromise, - assert.rejects(client.ping(), ClientClosedError) - ]); + while (process.hrtime.bigint() - start < 1_000_000) { + // block the event loop for 1ms, to make sure the connection will timeout + } - assert.equal(ping, 'PONG'); - assert.equal(quit, 'OK'); - }, { - ...GLOBAL.SERVERS.OPEN, - disableClientSetup: true - }); + await promise; + }, { + ...GLOBAL.SERVERS.OPEN, + clientOptions: { + socket: { + connectTimeout: 1 + } + }, + disableClientSetup: true + }); + + testUtils.testWithClient('client.quit', async client => { + await client.connect(); + + const pingPromise = client.ping(), + quitPromise = client.quit(); + assert.equal(client.isOpen, false); + + const [ping, quit] = await Promise.all([ + pingPromise, + quitPromise, + assert.rejects(client.ping(), ClientClosedError) + ]); + + assert.equal(ping, 'PONG'); + assert.equal(quit, 'OK'); + }, { + ...GLOBAL.SERVERS.OPEN, + disableClientSetup: true + }); + + testUtils.testWithClient('client.disconnect', async client => { + const pingPromise = client.ping(), + disconnectPromise = client.disconnect(); + assert.equal(client.isOpen, false); + await Promise.all([ + assert.rejects(pingPromise, DisconnectsClientError), + assert.doesNotReject(disconnectPromise), + assert.rejects(client.ping(), ClientClosedError) + ]); + }, GLOBAL.SERVERS.OPEN); + + testUtils.testWithClient('should be able to connect after disconnect (see #1801)', async client => { + await client.disconnect(); + await client.connect(); + }, GLOBAL.SERVERS.OPEN); + + testUtils.testWithClient('should be able to use ref and unref', client => { + client.unref(); + client.ref(); + }, GLOBAL.SERVERS.OPEN); + + testUtils.testWithClient('pingInterval', async client => { + assert.deepEqual( + await once(client, 'ping-interval'), + ['PONG'] + ); + }, { + ...GLOBAL.SERVERS.OPEN, + clientOptions: { + pingInterval: 1 + } + }); - testUtils.testWithClient('client.disconnect', async client => { - const pingPromise = client.ping(), - disconnectPromise = client.disconnect(); - assert.equal(client.isOpen, false); + testUtils.testWithClient('should reject commands in connect phase when `disableOfflineQueue`', async client => { + const connectPromise = client.connect(); + await assert.rejects( + client.ping(), + ClientOfflineError + ); + await connectPromise; + await client.disconnect(); + }, { + ...GLOBAL.SERVERS.OPEN, + clientOptions: { + disableOfflineQueue: true + }, + disableClientSetup: true + }); + + describe('MONITOR', () => { + testUtils.testWithClient('should be able to monitor commands', async client => { + const duplicate = await client.duplicate().connect(), + listener = spy(message => assert.equal(typeof message, 'string')); + await duplicate.monitor(listener); + + try { await Promise.all([ - assert.rejects(pingPromise, DisconnectsClientError), - assert.doesNotReject(disconnectPromise), - assert.rejects(client.ping(), ClientClosedError) + waitTillBeenCalled(listener), + client.ping() ]); + } finally { + duplicate.destroy(); + } }, GLOBAL.SERVERS.OPEN); - testUtils.testWithClient('should be able to connect after disconnect (see #1801)', async client => { - await client.disconnect(); - await client.connect(); + testUtils.testWithClient('should keep monitoring after reconnection', async client => { + const duplicate = await client.duplicate().connect(), + listener = spy(message => assert.equal(typeof message, 'string')); + await duplicate.monitor(listener); + + try { + await Promise.all([ + once(duplicate, 'error'), + client.clientKill({ + filter: 'SKIPME', + skipMe: true + }) + ]); + + await once(duplicate, 'ready'); + + await Promise.all([ + waitTillBeenCalled(listener), + client.ping() + ]); + } finally { + duplicate.destroy(); + } }, GLOBAL.SERVERS.OPEN); - testUtils.testWithClient('should be able to use ref and unref', client => { - client.unref(); - client.ref(); + testUtils.testWithClient('should be able to go back to "normal mode"', async client => { + await Promise.all([ + client.monitor(() => {}), + client.reset() + ]); + await assert.doesNotReject(client.ping()); }, GLOBAL.SERVERS.OPEN); - testUtils.testWithClient('pingInterval', async client => { - assert.deepEqual( - await once(client, 'ping-interval'), - ['PONG'] - ); - }, { - ...GLOBAL.SERVERS.OPEN, - clientOptions: { - pingInterval: 1 - } - }); + testUtils.testWithClient('should respect type mapping', async client => { + const duplicate = await client.duplicate().connect(), + listener = spy(message => assert.ok(message instanceof Buffer)); + await duplicate.withTypeMapping({ + [RESP_TYPES.SIMPLE_STRING]: Buffer + }).monitor(listener); - testUtils.testWithClient('should reject commands in connect phase when `disableOfflineQueue`', async client => { - const connectPromise = client.connect(); - await assert.rejects( - client.ping(), - ClientOfflineError - ); - await connectPromise; - await client.disconnect(); - }, { - ...GLOBAL.SERVERS.OPEN, - clientOptions: { - disableOfflineQueue: true - }, - disableClientSetup: true - }); + try { + await Promise.all([ + waitTillBeenCalled(listener), + client.ping() + ]); + } finally { + duplicate.destroy(); + } + }, GLOBAL.SERVERS.OPEN); + }); }); diff --git a/packages/client/lib/client/index.ts b/packages/client/lib/client/index.ts index d7f33e97b16..64a3b578815 100644 --- a/packages/client/lib/client/index.ts +++ b/packages/client/lib/client/index.ts @@ -1,883 +1,1095 @@ -import COMMANDS from './commands'; -import { RedisCommand, RedisCommandArgument, RedisCommandArguments, RedisCommandRawReply, RedisCommandReply, RedisFunctions, RedisModules, RedisExtensions, RedisScript, RedisScripts, RedisCommandSignature, ConvertArgumentType, RedisFunction, ExcludeMappedString, RedisCommands } from '../commands'; -import RedisSocket, { RedisSocketOptions, RedisTlsSocketOptions } from './socket'; -import RedisCommandsQueue, { QueueCommandOptions } from './commands-queue'; +import COMMANDS from '../commands'; +import RedisSocket, { RedisSocketOptions } from './socket'; +import RedisCommandsQueue, { CommandOptions } from './commands-queue'; +import { EventEmitter } from 'node:events'; +import { attachConfig, functionArgumentsPrefix, getTransformReply, scriptArgumentsPrefix } from '../commander'; +import { ClientClosedError, ClientOfflineError, DisconnectsClientError, WatchError } from '../errors'; +import { URL } from 'node:url'; +import { TcpSocketConnectOpts } from 'node:net'; +import { PUBSUB_TYPE, PubSubType, PubSubListener, PubSubTypeListeners, ChannelListeners } from './pub-sub'; +import { Command, CommandSignature, TypeMapping, CommanderConfig, RedisFunction, RedisFunctions, RedisModules, RedisScript, RedisScripts, ReplyUnion, RespVersions, RedisArgument, ReplyWithTypeMapping, SimpleStringReply } from '../RESP/types'; import RedisClientMultiCommand, { RedisClientMultiCommandType } from './multi-command'; import { RedisMultiQueuedCommand } from '../multi-command'; -import { EventEmitter } from 'events'; -import { CommandOptions, commandOptions, isCommandOptions } from '../command-options'; -import { ScanOptions, ZMember } from '../commands/generic-transformers'; -import { ScanCommandOptions } from '../commands/SCAN'; -import { HScanTuple } from '../commands/HSCAN'; -import { attachCommands, attachExtensions, fCallArguments, transformCommandArguments, transformCommandReply, transformLegacyCommandArguments } from '../commander'; -import { Pool, Options as PoolOptions, createPool } from 'generic-pool'; -import { ClientClosedError, ClientOfflineError, DisconnectsClientError, ErrorReply } from '../errors'; -import { URL } from 'url'; -import { TcpSocketConnectOpts } from 'net'; -import { PubSubType, PubSubListener, PubSubTypeListeners, ChannelListeners } from './pub-sub'; - -import {version} from '../../package.json'; +import HELLO, { HelloOptions } from '../commands/HELLO'; +import { ScanOptions, ScanCommonOptions } from '../commands/SCAN'; +import { RedisLegacyClient, RedisLegacyClientType } from './legacy-mode'; +import { RedisPoolOptions, RedisClientPool } from './pool'; +import { RedisVariadicArgument, pushVariadicArguments } from '../commands/generic-transformers'; export interface RedisClientOptions< - M extends RedisModules = RedisModules, - F extends RedisFunctions = RedisFunctions, - S extends RedisScripts = RedisScripts -> extends RedisExtensions { - /** - * `redis[s]://[[username][:password]@][host][:port][/db-number]` - * See [`redis`](https://www.iana.org/assignments/uri-schemes/prov/redis) and [`rediss`](https://www.iana.org/assignments/uri-schemes/prov/rediss) IANA registration for more details - */ - url?: string; - /** - * Socket connection properties - */ - socket?: RedisSocketOptions; - /** - * ACL username ([see ACL guide](https://redis.io/topics/acl)) - */ - username?: string; - /** - * ACL password or the old "--requirepass" password - */ - password?: string; - /** - * Client name ([see `CLIENT SETNAME`](https://redis.io/commands/client-setname)) - */ - name?: string; - /** - * Redis database number (see [`SELECT`](https://redis.io/commands/select) command) - */ - database?: number; - /** - * Maximum length of the client's internal command queue - */ - commandsQueueMaxLength?: number; - /** - * When `true`, commands are rejected when the client is reconnecting. - * When `false`, commands are queued for execution after reconnection. - */ - disableOfflineQueue?: boolean; - /** - * Connect in [`READONLY`](https://redis.io/commands/readonly) mode - */ - readonly?: boolean; - legacyMode?: boolean; - isolationPoolOptions?: PoolOptions; - /** - * Send `PING` command at interval (in ms). - * Useful with Redis deployments that do not use TCP Keep-Alive. - */ - pingInterval?: number; - /** - * If set to true, disables sending client identifier (user-agent like message) to the redis server - */ - disableClientInfo?: boolean; - /** - * Tag to append to library name that is sent to the Redis server - */ - clientInfoTag?: string; + M extends RedisModules = RedisModules, + F extends RedisFunctions = RedisFunctions, + S extends RedisScripts = RedisScripts, + RESP extends RespVersions = RespVersions, + TYPE_MAPPING extends TypeMapping = TypeMapping, + SocketOptions extends RedisSocketOptions = RedisSocketOptions +> extends CommanderConfig { + /** + * `redis[s]://[[username][:password]@][host][:port][/db-number]` + * See [`redis`](https://www.iana.org/assignments/uri-schemes/prov/redis) and [`rediss`](https://www.iana.org/assignments/uri-schemes/prov/rediss) IANA registration for more details + */ + url?: string; + /** + * Socket connection properties + */ + socket?: SocketOptions; + /** + * ACL username ([see ACL guide](https://redis.io/topics/acl)) + */ + username?: string; + /** + * ACL password or the old "--requirepass" password + */ + password?: string; + /** + * Client name ([see `CLIENT SETNAME`](https://redis.io/commands/client-setname)) + */ + name?: string; + /** + * Redis database number (see [`SELECT`](https://redis.io/commands/select) command) + */ + database?: number; + /** + * Maximum length of the client's internal command queue + */ + commandsQueueMaxLength?: number; + /** + * When `true`, commands are rejected when the client is reconnecting. + * When `false`, commands are queued for execution after reconnection. + */ + disableOfflineQueue?: boolean; + /** + * Connect in [`READONLY`](https://redis.io/commands/readonly) mode + */ + readonly?: boolean; + /** + * Send `PING` command at interval (in ms). + * Useful with Redis deployments that do not honor TCP Keep-Alive. + */ + pingInterval?: number; + /** + * TODO + */ + commandOptions?: CommandOptions; } -type WithCommands = { - [P in keyof typeof COMMANDS]: RedisCommandSignature<(typeof COMMANDS)[P]>; +type WithCommands< + RESP extends RespVersions, + TYPE_MAPPING extends TypeMapping +> = { + [P in keyof typeof COMMANDS]: CommandSignature<(typeof COMMANDS)[P], RESP, TYPE_MAPPING>; }; -export type WithModules = { - [P in keyof M as ExcludeMappedString

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

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

]: RedisCommandSignature; +type WithScripts< + S extends RedisScripts, + RESP extends RespVersions, + TYPE_MAPPING extends TypeMapping +> = { + [P in keyof S]: CommandSignature; }; +export type RedisClientExtensions< + M extends RedisModules = {}, + F extends RedisFunctions = {}, + S extends RedisScripts = {}, + RESP extends RespVersions = 2, + TYPE_MAPPING extends TypeMapping = {} +> = ( + WithCommands & + WithModules & + WithFunctions & + WithScripts +); + export type RedisClientType< - M extends RedisModules = Record, - F extends RedisFunctions = Record, - S extends RedisScripts = Record -> = RedisClient & WithCommands & WithModules & WithFunctions & WithScripts; - -export type InstantiableRedisClient< - M extends RedisModules, - F extends RedisFunctions, - S extends RedisScripts -> = new (options?: RedisClientOptions) => RedisClientType; - -export interface ClientCommandOptions extends QueueCommandOptions { - isolated?: boolean; + M extends RedisModules = {}, + F extends RedisFunctions = {}, + S extends RedisScripts = {}, + RESP extends RespVersions = 2, + TYPE_MAPPING extends TypeMapping = {} +> = ( + RedisClient & + RedisClientExtensions +); + +type ProxyClient = RedisClient; + +type NamespaceProxyClient = { _self: ProxyClient }; + +interface ScanIteratorOptions { + cursor?: RedisArgument; } -type ClientLegacyCallback = (err: Error | null, reply?: RedisCommandRawReply) => void; +export type MonitorCallback = (reply: ReplyWithTypeMapping) => unknown; export default class RedisClient< - M extends RedisModules, - F extends RedisFunctions, - S extends RedisScripts + M extends RedisModules, + F extends RedisFunctions, + S extends RedisScripts, + RESP extends RespVersions, + TYPE_MAPPING extends TypeMapping > extends EventEmitter { - static commandOptions(options: T): CommandOptions { - return commandOptions(options); - } - - commandOptions = RedisClient.commandOptions; - - static extend< - M extends RedisModules, - F extends RedisFunctions, - S extends RedisScripts - >(extensions?: RedisExtensions): InstantiableRedisClient { - const Client = attachExtensions({ - BaseClass: RedisClient, - modulesExecutor: RedisClient.prototype.commandsExecutor, - modules: extensions?.modules, - functionsExecutor: RedisClient.prototype.functionsExecuter, - functions: extensions?.functions, - scriptsExecutor: RedisClient.prototype.scriptsExecuter, - scripts: extensions?.scripts - }); - - if (Client !== RedisClient) { - Client.prototype.Multi = RedisClientMultiCommand.extend(extensions); - } - - return Client; - } - - static create< - M extends RedisModules, - F extends RedisFunctions, - S extends RedisScripts - >(options?: RedisClientOptions): RedisClientType { - return new (RedisClient.extend(options))(options); - } + static #createCommand(command: Command, resp: RespVersions) { + const transformReply = getTransformReply(command, resp); + return async function (this: ProxyClient, ...args: Array) { + const redisArgs = command.transformArguments(...args); + const typeMapping = this._commandOptions?.typeMapping; - static parseURL(url: string): RedisClientOptions { - // https://www.iana.org/assignments/uri-schemes/prov/redis - const { hostname, port, protocol, username, password, pathname } = new URL(url), - parsed: RedisClientOptions = { - socket: { - host: hostname - } - }; - - if (protocol === 'rediss:') { - (parsed.socket as RedisTlsSocketOptions).tls = true; - } else if (protocol !== 'redis:') { - throw new TypeError('Invalid protocol'); - } + const reply = await this.sendCommand(redisArgs, this._commandOptions); - if (port) { - (parsed.socket as TcpSocketConnectOpts).port = Number(port); - } + return transformReply ? + transformReply(reply, redisArgs.preserve, typeMapping) : + reply; + }; + } - if (username) { - parsed.username = decodeURIComponent(username); - } + static #createModuleCommand(command: Command, resp: RespVersions) { + const transformReply = getTransformReply(command, resp); + return async function (this: NamespaceProxyClient, ...args: Array) { + const redisArgs = command.transformArguments(...args); + const typeMapping = this._self._commandOptions?.typeMapping - if (password) { - parsed.password = decodeURIComponent(password); - } + const reply = await this._self.sendCommand(redisArgs, this._self._commandOptions); - if (pathname.length > 1) { - const database = Number(pathname.substring(1)); - if (isNaN(database)) { - throw new TypeError('Invalid pathname'); - } + return transformReply ? + transformReply(reply, redisArgs.preserve, typeMapping) : + reply; + }; + } + + static #createFunctionCommand(name: string, fn: RedisFunction, resp: RespVersions) { + const prefix = functionArgumentsPrefix(name, fn), + transformReply = getTransformReply(fn, resp); + return async function (this: NamespaceProxyClient, ...args: Array) { + const fnArgs = fn.transformArguments(...args); + const typeMapping = this._self._commandOptions?.typeMapping; + + const reply = await this._self.sendCommand( + prefix.concat(fnArgs), + this._self._commandOptions + ); - parsed.database = database; + return transformReply ? + transformReply(reply, fnArgs.preserve, typeMapping) : + reply; + }; + } + + static #createScriptCommand(script: RedisScript, resp: RespVersions) { + const prefix = scriptArgumentsPrefix(script), + transformReply = getTransformReply(script, resp); + return async function (this: ProxyClient, ...args: Array) { + const scriptArgs = script.transformArguments(...args); + const redisArgs = prefix.concat(scriptArgs); + const typeMapping = this._commandOptions?.typeMapping; + + const reply = await this.executeScript(script, redisArgs, this._commandOptions); + + return transformReply ? + transformReply(reply, scriptArgs.preserve, typeMapping) : + reply; + }; + } + + static factory< + M extends RedisModules = {}, + F extends RedisFunctions = {}, + S extends RedisScripts = {}, + RESP extends RespVersions = 2 + >(config?: CommanderConfig) { + const Client = attachConfig({ + BaseClass: RedisClient, + commands: COMMANDS, + createCommand: RedisClient.#createCommand, + createModuleCommand: RedisClient.#createModuleCommand, + createFunctionCommand: RedisClient.#createFunctionCommand, + createScriptCommand: RedisClient.#createScriptCommand, + config + }); + + Client.prototype.Multi = RedisClientMultiCommand.extend(config); + + return ( + options?: Omit, keyof Exclude> + ) => { + // returning a "proxy" to prevent the namespaces._self to leak between "proxies" + return Object.create(new Client(options)) as RedisClientType; + }; + } + + static create< + M extends RedisModules = {}, + F extends RedisFunctions = {}, + S extends RedisScripts = {}, + RESP extends RespVersions = 2, + TYPE_MAPPING extends TypeMapping = {} + >(this: void, options?: RedisClientOptions) { + return RedisClient.factory(options)(options); + } + + static parseURL(url: string): RedisClientOptions { + // https://www.iana.org/assignments/uri-schemes/prov/redis + const { hostname, port, protocol, username, password, pathname } = new URL(url), + parsed: RedisClientOptions = { + socket: { + host: hostname } + }; - return parsed; + if (protocol === 'rediss:') { + parsed!.socket!.tls = true; + } else if (protocol !== 'redis:') { + throw new TypeError('Invalid protocol'); } - readonly #options?: RedisClientOptions; - readonly #socket: RedisSocket; - readonly #queue: RedisCommandsQueue; - #isolationPool?: Pool>; - readonly #v4: Record = {}; - #selectedDB = 0; - - get options(): RedisClientOptions | undefined { - return this.#options; + if (port) { + (parsed.socket as TcpSocketConnectOpts).port = Number(port); } - get isOpen(): boolean { - return this.#socket.isOpen; + if (username) { + parsed.username = decodeURIComponent(username); } - get isReady(): boolean { - return this.#socket.isReady; + if (password) { + parsed.password = decodeURIComponent(password); } - get isPubSubActive() { - return this.#queue.isPubSubActive; - } + if (pathname.length > 1) { + const database = Number(pathname.substring(1)); + if (isNaN(database)) { + throw new TypeError('Invalid pathname'); + } - get v4(): Record { - if (!this.#options?.legacyMode) { - throw new Error('the client is not in "legacy mode"'); - } - - return this.#v4; + parsed.database = database; } - constructor(options?: RedisClientOptions) { - super(); - this.#options = this.#initiateOptions(options); - this.#queue = this.#initiateQueue(); - this.#socket = this.#initiateSocket(); - // should be initiated in connect, not here - // TODO: consider breaking in v5 - this.#isolationPool = this.#initiateIsolationPool(); - this.#legacyMode(); + return parsed; + } + + readonly #options?: RedisClientOptions; + readonly #socket: RedisSocket; + readonly #queue: RedisCommandsQueue; + #selectedDB = 0; + #monitorCallback?: MonitorCallback; + private _self = this; + private _commandOptions?: CommandOptions; + #dirtyWatch?: string; + #epoch: number; + #watchEpoch?: number; + + get options(): RedisClientOptions | undefined { + return this._self.#options; + } + + get isOpen(): boolean { + return this._self.#socket.isOpen; + } + + get isReady(): boolean { + return this._self.#socket.isReady; + } + + get isPubSubActive() { + return this._self.#queue.isPubSubActive; + } + + get isWatching() { + return this._self.#watchEpoch !== undefined; + } + + setDirtyWatch(msg: string) { + this._self.#dirtyWatch = msg; + } + + constructor(options?: RedisClientOptions) { + super(); + this.#options = this.#initiateOptions(options); + this.#queue = this.#initiateQueue(); + this.#socket = this.#initiateSocket(); + this.#epoch = 0; + } + + #initiateOptions(options?: RedisClientOptions): RedisClientOptions | undefined { + if (options?.url) { + const parsed = RedisClient.parseURL(options.url); + if (options.socket) { + parsed.socket = Object.assign(options.socket, parsed.socket); + } + + Object.assign(options, parsed); } - #initiateOptions(options?: RedisClientOptions): RedisClientOptions | undefined { - if (options?.url) { - const parsed = RedisClient.parseURL(options.url); - if (options.socket) { - parsed.socket = Object.assign(options.socket, parsed.socket); - } - - Object.assign(options, parsed); - } - - if (options?.database) { - this.#selectedDB = options.database; - } - - return options; + if (options?.database) { + this._self.#selectedDB = options.database; } - #initiateQueue(): RedisCommandsQueue { - return new RedisCommandsQueue( - this.#options?.commandsQueueMaxLength, - (channel, listeners) => this.emit('sharded-channel-moved', channel, listeners) - ); + if (options?.commandOptions) { + this._commandOptions = options.commandOptions; } - #initiateSocket(): RedisSocket { - const socketInitiator = async (): Promise => { - const promises = []; - - if (this.#selectedDB !== 0) { - promises.push( - this.#queue.addCommand( - ['SELECT', this.#selectedDB.toString()], - { asap: true } - ) - ); - } + return options; + } - if (this.#options?.readonly) { - promises.push( - this.#queue.addCommand( - COMMANDS.READONLY.transformArguments(), - { asap: true } - ) - ); - } + #initiateQueue(): RedisCommandsQueue { + return new RedisCommandsQueue( + this.#options?.RESP ?? 2, + this.#options?.commandsQueueMaxLength, + (channel, listeners) => this.emit('sharded-channel-moved', channel, listeners) + ); + } - if (!this.#options?.disableClientInfo) { - promises.push( - this.#queue.addCommand( - [ 'CLIENT', 'SETINFO', 'LIB-VER', version], - { asap: true } - ).catch(err => { - if (!(err instanceof ErrorReply)) { - throw err; - } - }) - ); - - promises.push( - this.#queue.addCommand( - [ - 'CLIENT', 'SETINFO', 'LIB-NAME', - this.#options?.clientInfoTag ? `node-redis(${this.#options.clientInfoTag})` : 'node-redis' - ], - { asap: true } - ).catch(err => { - if (!(err instanceof ErrorReply)) { - throw err; - } - }) - ); - } - - if (this.#options?.name) { - promises.push( - this.#queue.addCommand( - COMMANDS.CLIENT_SETNAME.transformArguments(this.#options.name), - { asap: true } - ) - ); - } + #handshake(selectedDB: number) { + const commands = []; - if (this.#options?.username || this.#options?.password) { - promises.push( - this.#queue.addCommand( - COMMANDS.AUTH.transformArguments({ - username: this.#options.username, - password: this.#options.password ?? '' - }), - { asap: true } - ) - ); - } - - const resubscribePromise = this.#queue.resubscribe(); - if (resubscribePromise) { - promises.push(resubscribePromise); - } - - if (promises.length) { - this.#tick(true); - await Promise.all(promises); - } - }; - - return new RedisSocket(socketInitiator, this.#options?.socket) - .on('data', chunk => this.#queue.onReplyChunk(chunk)) - .on('error', err => { - this.emit('error', err); - if (this.#socket.isOpen && !this.#options?.disableOfflineQueue) { - this.#queue.flushWaitingForReply(err); - } else { - this.#queue.flushAll(err); - } - }) - .on('connect', () => { - this.emit('connect'); - }) - .on('ready', () => { - this.emit('ready'); - this.#setPingTimer(); - this.#tick(); - }) - .on('reconnecting', () => this.emit('reconnecting')) - .on('drain', () => this.#tick()) - .on('end', () => this.emit('end')); - } - - #initiateIsolationPool() { - return createPool({ - create: async () => { - const duplicate = this.duplicate({ - isolationPoolOptions: undefined - }).on('error', err => this.emit('error', err)); - await duplicate.connect(); - return duplicate; - }, - destroy: client => client.disconnect() - }, this.#options?.isolationPoolOptions); - } - - #legacyMode(): void { - if (!this.#options?.legacyMode) return; - - (this as any).#v4.sendCommand = this.#sendCommand.bind(this); - (this as any).sendCommand = (...args: Array): void => { - const result = this.#legacySendCommand(...args); - if (result) { - result.promise - .then(reply => result.callback(null, reply)) - .catch(err => result.callback(err)); - } - }; - - for (const [ name, command ] of Object.entries(COMMANDS as RedisCommands)) { - this.#defineLegacyCommand(name, command); - (this as any)[name.toLowerCase()] ??= (this as any)[name]; - } - - // hard coded commands - this.#defineLegacyCommand('SELECT'); - this.#defineLegacyCommand('select'); - this.#defineLegacyCommand('SUBSCRIBE'); - this.#defineLegacyCommand('subscribe'); - this.#defineLegacyCommand('PSUBSCRIBE'); - this.#defineLegacyCommand('pSubscribe'); - this.#defineLegacyCommand('UNSUBSCRIBE'); - this.#defineLegacyCommand('unsubscribe'); - this.#defineLegacyCommand('PUNSUBSCRIBE'); - this.#defineLegacyCommand('pUnsubscribe'); - this.#defineLegacyCommand('QUIT'); - this.#defineLegacyCommand('quit'); - } - - #legacySendCommand(...args: Array) { - const callback = typeof args[args.length - 1] === 'function' ? - args.pop() as ClientLegacyCallback : - undefined; + if (this.#options?.RESP) { + const hello: HelloOptions = {}; - const promise = this.#sendCommand(transformLegacyCommandArguments(args)); - if (callback) return { - promise, - callback + if (this.#options.password) { + hello.AUTH = { + username: this.#options.username ?? 'default', + password: this.#options.password }; - promise.catch(err => this.emit('error', err)); - } - - #defineLegacyCommand(name: string, command?: RedisCommand): void { - this.#v4[name] = (this as any)[name].bind(this); - (this as any)[name] = command && command.TRANSFORM_LEGACY_REPLY && command.transformReply ? - (...args: Array) => { - const result = this.#legacySendCommand(name, ...args); - if (result) { - result.promise - .then(reply => result.callback(null, command.transformReply!(reply))) - .catch(err => result.callback(err)); - } - } : - (...args: Array) => (this as any).sendCommand(name, ...args); - } - - #pingTimer?: NodeJS.Timeout; - - #setPingTimer(): void { - if (!this.#options?.pingInterval || !this.#socket.isReady) return; - clearTimeout(this.#pingTimer); - - this.#pingTimer = setTimeout(() => { - if (!this.#socket.isReady) return; - - // using #sendCommand to support legacy mode - this.#sendCommand(['PING']) - .then(reply => this.emit('ping-interval', reply)) - .catch(err => this.emit('error', err)) - .finally(() => this.#setPingTimer()); - }, this.#options.pingInterval); - } - - duplicate(overrides?: Partial>): RedisClientType { - return new (Object.getPrototypeOf(this).constructor)({ - ...this.#options, - ...overrides - }); - } - - async connect() { - // see comment in constructor - this.#isolationPool ??= this.#initiateIsolationPool(); - await this.#socket.connect(); - return this as unknown as RedisClientType; - } + } + + if (this.#options.name) { + hello.SETNAME = this.#options.name; + } + + commands.push( + HELLO.transformArguments(this.#options.RESP, hello) + ); + } else { + if (this.#options?.username || this.#options?.password) { + commands.push( + COMMANDS.AUTH.transformArguments({ + username: this.#options.username, + password: this.#options.password ?? '' + }) + ); + } - async commandsExecutor( - command: C, - args: Array - ): Promise> { - const { args: redisArgs, options } = transformCommandArguments(command, args); - return transformCommandReply( - command, - await this.#sendCommand(redisArgs, options), - redisArgs.preserve + if (this.#options?.name) { + commands.push( + COMMANDS.CLIENT_SETNAME.transformArguments(this.#options.name) ); + } } - sendCommand( - args: RedisCommandArguments, - options?: ClientCommandOptions - ): Promise { - return this.#sendCommand(args, options); + if (selectedDB !== 0) { + commands.push(['SELECT', this.#selectedDB.toString()]); } - // using `#sendCommand` cause `sendCommand` is overwritten in legacy mode - #sendCommand( - args: RedisCommandArguments, - options?: ClientCommandOptions - ): Promise { - if (!this.#socket.isOpen) { - return Promise.reject(new ClientClosedError()); - } else if (options?.isolated) { - return this.executeIsolated(isolatedClient => - isolatedClient.sendCommand(args, { - ...options, - isolated: false - }) - ); - } else if (!this.#socket.isReady && this.#options?.disableOfflineQueue) { - return Promise.reject(new ClientOfflineError()); - } - - const promise = this.#queue.addCommand(args, options); - this.#tick(); - return promise; + if (this.#options?.readonly) { + commands.push( + COMMANDS.READONLY.transformArguments() + ); } - async functionsExecuter( - fn: F, - args: Array, - name: string - ): Promise> { - const { args: redisArgs, options } = transformCommandArguments(fn, args); - return transformCommandReply( - fn, - await this.executeFunction(name, fn, redisArgs, options), - redisArgs.preserve + return commands; + } + + #initiateSocket(): RedisSocket { + const socketInitiator = () => { + const promises = [], + chainId = Symbol('Socket Initiator'); + + const resubscribePromise = this.#queue.resubscribe(chainId); + if (resubscribePromise) { + promises.push(resubscribePromise); + } + + if (this.#monitorCallback) { + promises.push( + this.#queue.monitor( + this.#monitorCallback, + { + typeMapping: this._commandOptions?.typeMapping, + chainId, + asap: true + } + ) ); - } - - executeFunction( - name: string, - fn: RedisFunction, - args: RedisCommandArguments, - options?: ClientCommandOptions - ): Promise { - return this.#sendCommand( - fCallArguments(name, fn, args), - options + } + + const commands = this.#handshake(this.#selectedDB); + for (let i = commands.length - 1; i >= 0; --i) { + promises.push( + this.#queue.addCommand(commands[i], { + chainId, + asap: true + }) ); - } + } - async scriptsExecuter( - script: S, - args: Array - ): Promise> { - const { args: redisArgs, options } = transformCommandArguments(script, args); - return transformCommandReply( - script, - await this.executeScript(script, redisArgs, options), - redisArgs.preserve - ); - } - - async executeScript( - script: RedisScript, - args: RedisCommandArguments, - options?: ClientCommandOptions - ): Promise { - const redisArgs: RedisCommandArguments = ['EVALSHA', script.SHA1]; - - if (script.NUMBER_OF_KEYS !== undefined) { - redisArgs.push(script.NUMBER_OF_KEYS.toString()); - } - - redisArgs.push(...args); + if (promises.length) { + this.#write(); + return Promise.all(promises); + } + }; + return new RedisSocket(socketInitiator, this.#options?.socket) + .on('data', chunk => { try { - return await this.#sendCommand(redisArgs, options); - } catch (err: any) { - if (!err?.message?.startsWith?.('NOSCRIPT')) { - throw err; - } - - redisArgs[0] = 'EVAL'; - redisArgs[1] = script.SCRIPT; - return this.#sendCommand(redisArgs, options); + this.#queue.decoder.write(chunk); + } catch (err) { + this.#queue.resetDecoder(); + this.emit('error', err); } - } - - async SELECT(db: number): Promise; - async SELECT(options: CommandOptions, db: number): Promise; - async SELECT(options?: any, db?: any): Promise { - if (!isCommandOptions(options)) { - db = options; - options = null; + }) + .on('error', err => { + this.emit('error', err); + if (this.#socket.isOpen && !this.#options?.disableOfflineQueue) { + this.#queue.flushWaitingForReply(err); + } else { + this.#queue.flushAll(err); } - - await this.#sendCommand(['SELECT', db.toString()], options); - this.#selectedDB = db; - } - - select = this.SELECT; - - #pubSubCommand(promise: Promise | undefined) { - if (promise === undefined) return Promise.resolve(); - - this.#tick(); - return promise; - } - - SUBSCRIBE( - channels: string | Array, - listener: PubSubListener, - bufferMode?: T - ): Promise { - return this.#pubSubCommand( - this.#queue.subscribe( - PubSubType.CHANNELS, - channels, - listener, - bufferMode - ) - ); - } - - subscribe = this.SUBSCRIBE; - - - UNSUBSCRIBE( - channels?: string | Array, - listener?: PubSubListener, - bufferMode?: T - ): Promise { - return this.#pubSubCommand( - this.#queue.unsubscribe( - PubSubType.CHANNELS, - channels, - listener, - bufferMode - ) - ); - } - - unsubscribe = this.UNSUBSCRIBE; - - PSUBSCRIBE( - patterns: string | Array, - listener: PubSubListener, - bufferMode?: T - ): Promise { - return this.#pubSubCommand( - this.#queue.subscribe( - PubSubType.PATTERNS, - patterns, - listener, - bufferMode - ) - ); - } - - pSubscribe = this.PSUBSCRIBE; - - PUNSUBSCRIBE( - patterns?: string | Array, - listener?: PubSubListener, - bufferMode?: T - ): Promise { - return this.#pubSubCommand( - this.#queue.unsubscribe( - PubSubType.PATTERNS, - patterns, - listener, - bufferMode - ) - ); - } - - pUnsubscribe = this.PUNSUBSCRIBE; - - SSUBSCRIBE( - channels: string | Array, - listener: PubSubListener, - bufferMode?: T - ): Promise { - return this.#pubSubCommand( - this.#queue.subscribe( - PubSubType.SHARDED, - channels, - listener, - bufferMode - ) - ); + }) + .on('connect', () => this.emit('connect')) + .on('ready', () => { + this.#epoch++; + this.emit('ready'); + this.#setPingTimer(); + this.#maybeScheduleWrite(); + }) + .on('reconnecting', () => this.emit('reconnecting')) + .on('drain', () => this.#maybeScheduleWrite()) + .on('end', () => this.emit('end')); + } + + #pingTimer?: NodeJS.Timeout; + + #setPingTimer(): void { + if (!this.#options?.pingInterval || !this.#socket.isReady) return; + clearTimeout(this.#pingTimer); + + this.#pingTimer = setTimeout(() => { + if (!this.#socket.isReady) return; + + this.sendCommand(['PING']) + .then(reply => this.emit('ping-interval', reply)) + .catch(err => this.emit('error', err)) + .finally(() => this.#setPingTimer()); + }, this.#options.pingInterval); + } + + withCommandOptions< + OPTIONS extends CommandOptions, + TYPE_MAPPING extends TypeMapping + >(options: OPTIONS) { + const proxy = Object.create(this._self); + proxy._commandOptions = options; + return proxy as RedisClientType< + M, + F, + S, + RESP, + TYPE_MAPPING extends TypeMapping ? TYPE_MAPPING : {} + >; + } + + private _commandOptionsProxy< + K extends keyof CommandOptions, + V extends CommandOptions[K] + >( + key: K, + value: V + ) { + const proxy = Object.create(this._self); + proxy._commandOptions = Object.create(this._commandOptions ?? null); + proxy._commandOptions[key] = value; + return proxy as RedisClientType< + M, + F, + S, + RESP, + K extends 'typeMapping' ? V extends TypeMapping ? V : {} : TYPE_MAPPING + >; + } + + /** + * Override the `typeMapping` command option + */ + withTypeMapping(typeMapping: TYPE_MAPPING) { + return this._commandOptionsProxy('typeMapping', typeMapping); + } + + /** + * Override the `abortSignal` command option + */ + withAbortSignal(abortSignal: AbortSignal) { + return this._commandOptionsProxy('abortSignal', abortSignal); + } + + /** + * Override the `asap` command option to `true` + */ + asap() { + return this._commandOptionsProxy('asap', true); + } + + /** + * Create the "legacy" (v3/callback) interface + */ + legacy(): RedisLegacyClientType { + return new RedisLegacyClient( + this as unknown as RedisClientType + ) as RedisLegacyClientType; + } + + /** + * Create {@link RedisClientPool `RedisClientPool`} using this client as a prototype + */ + createPool(options?: Partial) { + return RedisClientPool.create( + this._self.#options, + options + ); + } + + duplicate< + _M extends RedisModules = M, + _F extends RedisFunctions = F, + _S extends RedisScripts = S, + _RESP extends RespVersions = RESP, + _TYPE_MAPPING extends TypeMapping = TYPE_MAPPING + >(overrides?: Partial>) { + return new (Object.getPrototypeOf(this).constructor)({ + ...this._self.#options, + commandOptions: this._commandOptions, + ...overrides + }) as RedisClientType<_M, _F, _S, _RESP, _TYPE_MAPPING>; + } + + async connect() { + await this._self.#socket.connect(); + return this as unknown as RedisClientType; + } + + sendCommand( + args: Array, + options?: CommandOptions + ): Promise { + if (!this._self.#socket.isOpen) { + return Promise.reject(new ClientClosedError()); + } else if (!this._self.#socket.isReady && this._self.#options?.disableOfflineQueue) { + return Promise.reject(new ClientOfflineError()); } - sSubscribe = this.SSUBSCRIBE; - - SUNSUBSCRIBE( - channels?: string | Array, - listener?: PubSubListener, - bufferMode?: T - ): Promise { - return this.#pubSubCommand( - this.#queue.unsubscribe( - PubSubType.SHARDED, - channels, - listener, - bufferMode - ) - ); + const promise = this._self.#queue.addCommand(args, options); + this._self.#scheduleWrite(); + return promise; + } + + async executeScript( + script: RedisScript, + args: Array, + options?: CommandOptions + ) { + try { + return await this.sendCommand(args, options); + } catch (err) { + if (!(err as Error)?.message?.startsWith?.('NOSCRIPT')) throw err; + + args[0] = 'EVAL'; + args[1] = script.SCRIPT; + return await this.sendCommand(args, options); } - - sUnsubscribe = this.SUNSUBSCRIBE; - - getPubSubListeners(type: PubSubType) { - return this.#queue.getPubSubListeners(type); + } + + async SELECT(db: number): Promise { + await this.sendCommand(['SELECT', db.toString()]); + this._self.#selectedDB = db; + } + + select = this.SELECT; + + #pubSubCommand(promise: Promise | undefined) { + if (promise === undefined) return Promise.resolve(); + + this.#scheduleWrite(); + return promise; + } + + SUBSCRIBE( + channels: string | Array, + listener: PubSubListener, + bufferMode?: T + ): Promise { + return this._self.#pubSubCommand( + this._self.#queue.subscribe( + PUBSUB_TYPE.CHANNELS, + channels, + listener, + bufferMode + ) + ); + } + + subscribe = this.SUBSCRIBE; + + UNSUBSCRIBE( + channels?: string | Array, + listener?: PubSubListener, + bufferMode?: T + ): Promise { + return this._self.#pubSubCommand( + this._self.#queue.unsubscribe( + PUBSUB_TYPE.CHANNELS, + channels, + listener, + bufferMode + ) + ); + } + + unsubscribe = this.UNSUBSCRIBE; + + PSUBSCRIBE( + patterns: string | Array, + listener: PubSubListener, + bufferMode?: T + ): Promise { + return this._self.#pubSubCommand( + this._self.#queue.subscribe( + PUBSUB_TYPE.PATTERNS, + patterns, + listener, + bufferMode + ) + ); + } + + pSubscribe = this.PSUBSCRIBE; + + PUNSUBSCRIBE( + patterns?: string | Array, + listener?: PubSubListener, + bufferMode?: T + ): Promise { + return this._self.#pubSubCommand( + this._self.#queue.unsubscribe( + PUBSUB_TYPE.PATTERNS, + patterns, + listener, + bufferMode + ) + ); + } + + pUnsubscribe = this.PUNSUBSCRIBE; + + SSUBSCRIBE( + channels: string | Array, + listener: PubSubListener, + bufferMode?: T + ): Promise { + return this._self.#pubSubCommand( + this._self.#queue.subscribe( + PUBSUB_TYPE.SHARDED, + channels, + listener, + bufferMode + ) + ); + } + + sSubscribe = this.SSUBSCRIBE; + + SUNSUBSCRIBE( + channels?: string | Array, + listener?: PubSubListener, + bufferMode?: T + ): Promise { + return this._self.#pubSubCommand( + this._self.#queue.unsubscribe( + PUBSUB_TYPE.SHARDED, + channels, + listener, + bufferMode + ) + ); + } + + sUnsubscribe = this.SUNSUBSCRIBE; + + async WATCH(key: RedisVariadicArgument) { + const reply = await this._self.sendCommand( + pushVariadicArguments(['WATCH'], key) + ); + this._self.#watchEpoch ??= this._self.#epoch; + return reply as unknown as ReplyWithTypeMapping, TYPE_MAPPING>; + } + + watch = this.WATCH; + + async UNWATCH() { + const reply = await this._self.sendCommand(['UNWATCH']); + this._self.#watchEpoch = undefined; + return reply as unknown as ReplyWithTypeMapping, TYPE_MAPPING>; + } + + unwatch = this.UNWATCH; + + getPubSubListeners(type: PubSubType) { + return this._self.#queue.getPubSubListeners(type); + } + + extendPubSubChannelListeners( + type: PubSubType, + channel: string, + listeners: ChannelListeners + ) { + return this._self.#pubSubCommand( + this._self.#queue.extendPubSubChannelListeners(type, channel, listeners) + ); + } + + extendPubSubListeners(type: PubSubType, listeners: PubSubTypeListeners) { + return this._self.#pubSubCommand( + this._self.#queue.extendPubSubListeners(type, listeners) + ); + } + + #write() { + this.#socket.write(this.#queue.commandsToWrite()); + } + + #scheduledWrite?: NodeJS.Immediate; + + #scheduleWrite() { + if (!this.#socket.isReady || this.#scheduledWrite) return; + + this.#scheduledWrite = setImmediate(() => { + this.#write(); + this.#scheduledWrite = undefined; + }); + } + + #maybeScheduleWrite() { + if (!this.#queue.isWaitingToWrite()) return; + + this.#scheduleWrite(); + } + + /** + * @internal + */ + async _executePipeline( + commands: Array, + selectedDB?: number + ) { + if (!this._self.#socket.isOpen) { + return Promise.reject(new ClientClosedError()); } - extendPubSubChannelListeners( - type: PubSubType, - channel: string, - listeners: ChannelListeners - ) { - return this.#pubSubCommand( - this.#queue.extendPubSubChannelListeners(type, channel, listeners) - ); + const chainId = Symbol('Pipeline Chain'), + promise = Promise.all( + commands.map(({ args }) => this._self.#queue.addCommand(args, { + chainId, + typeMapping: this._commandOptions?.typeMapping + })) + ); + this._self.#scheduleWrite(); + const result = await promise; + + if (selectedDB !== undefined) { + this._self.#selectedDB = selectedDB; } - extendPubSubListeners(type: PubSubType, listeners: PubSubTypeListeners) { - return this.#pubSubCommand( - this.#queue.extendPubSubListeners(type, listeners) - ); + return result; + } + + /** + * @internal + */ + async _executeMulti( + commands: Array, + selectedDB?: number + ) { + const dirtyWatch = this._self.#dirtyWatch; + this._self.#dirtyWatch = undefined; + const watchEpoch = this._self.#watchEpoch; + this._self.#watchEpoch = undefined; + + if (!this._self.#socket.isOpen) { + throw new ClientClosedError(); } - QUIT(): Promise { - return this.#socket.quit(async () => { - if (this.#pingTimer) clearTimeout(this.#pingTimer); - const quitPromise = this.#queue.addCommand(['QUIT']); - this.#tick(); - const [reply] = await Promise.all([ - quitPromise, - this.#destroyIsolationPool() - ]); - return reply; - }); + if (dirtyWatch) { + throw new WatchError(dirtyWatch); } - quit = this.QUIT; - - #tick(force = false): void { - if (this.#socket.writableNeedDrain || (!force && !this.#socket.isReady)) { - return; - } - - this.#socket.cork(); - - while (!this.#socket.writableNeedDrain) { - const args = this.#queue.getCommandToSend(); - if (args === undefined) break; - - this.#socket.writeCommand(args); - } - } - - executeIsolated(fn: (client: RedisClientType) => T | Promise): Promise { - if (!this.#isolationPool) return Promise.reject(new ClientClosedError()); - return this.#isolationPool.use(fn); + if (watchEpoch && watchEpoch !== this._self.#epoch) { + throw new WatchError('Client reconnected after WATCH'); } - MULTI(): RedisClientMultiCommandType { - return new (this as any).Multi( - this.multiExecutor.bind(this), - this.#options?.legacyMode - ); + const typeMapping = this._commandOptions?.typeMapping; + const chainId = Symbol('MULTI Chain'); + const promises = [ + this._self.#queue.addCommand(['MULTI'], { chainId }), + ]; + + for (const { args } of commands) { + promises.push( + this._self.#queue.addCommand(args, { + chainId, + typeMapping + }) + ); } - multi = this.MULTI; - - async multiExecutor( - commands: Array, - selectedDB?: number, - chainId?: symbol - ): Promise> { - if (!this.#socket.isOpen) { - return Promise.reject(new ClientClosedError()); - } - - const promise = chainId ? - // if `chainId` has a value, it's a `MULTI` (and not "pipeline") - need to add the `MULTI` and `EXEC` commands - Promise.all([ - this.#queue.addCommand(['MULTI'], { chainId }), - this.#addMultiCommands(commands, chainId), - this.#queue.addCommand(['EXEC'], { chainId }) - ]) : - this.#addMultiCommands(commands); - - this.#tick(); + promises.push( + this._self.#queue.addCommand(['EXEC'], { chainId }) + ); - const results = await promise; + this._self.#scheduleWrite(); - if (selectedDB !== undefined) { - this.#selectedDB = selectedDB; - } + const results = await Promise.all(promises), + execResult = results[results.length - 1]; - return results; + if (execResult === null) { + throw new WatchError(); } - #addMultiCommands(commands: Array, chainId?: symbol) { - return Promise.all( - commands.map(({ args }) => this.#queue.addCommand(args, { chainId })) - ); + if (selectedDB !== undefined) { + this._self.#selectedDB = selectedDB; } - async* scanIterator(options?: ScanCommandOptions): AsyncIterable { - let cursor = 0; - do { - const reply = await (this as any).scan(cursor, options); - cursor = reply.cursor; - for (const key of reply.keys) { - yield key; - } - } while (cursor !== 0); + return execResult as Array; + } + + MULTI() { + type Multi = new (...args: ConstructorParameters) => RedisClientMultiCommandType<[], M, F, S, RESP, TYPE_MAPPING>; + return new ((this as any).Multi as Multi)( + this._executeMulti.bind(this), + this._executePipeline.bind(this), + this._commandOptions?.typeMapping + ); + } + + multi = this.MULTI; + + async* scanIterator( + this: RedisClientType, + options?: ScanOptions & ScanIteratorOptions + ) { + let cursor = options?.cursor ?? '0'; + do { + const reply = await this.scan(cursor, options); + cursor = reply.cursor; + yield reply.keys; + } while (cursor !== '0'); + } + + async* hScanIterator( + this: RedisClientType, + key: RedisArgument, + options?: ScanCommonOptions & ScanIteratorOptions + ) { + let cursor = options?.cursor ?? '0'; + do { + const reply = await this.hScan(key, cursor, options); + cursor = reply.cursor; + yield reply.entries; + } while (cursor !== '0'); + } + + async* hScanValuesIterator( + this: RedisClientType, + key: RedisArgument, + options?: ScanCommonOptions & ScanIteratorOptions + ) { + let cursor = options?.cursor ?? '0'; + do { + const reply = await this.hScanNoValues(key, cursor, options); + cursor = reply.cursor; + yield reply.fields; + } while (cursor !== '0'); + } + + async* hScanNoValuesIterator( + this: RedisClientType, + key: RedisArgument, + options?: ScanCommonOptions & ScanIteratorOptions + ) { + let cursor = options?.cursor ?? '0'; + do { + const reply = await this.hScanNoValues(key, cursor, options); + cursor = reply.cursor; + yield reply.fields; + } while (cursor !== '0'); + } + + async* sScanIterator( + this: RedisClientType, + key: RedisArgument, + options?: ScanCommonOptions & ScanIteratorOptions + ) { + let cursor = options?.cursor ?? '0'; + do { + const reply = await this.sScan(key, cursor, options); + cursor = reply.cursor; + yield reply.members; + } while (cursor !== '0'); + } + + async* zScanIterator( + this: RedisClientType, + key: RedisArgument, + options?: ScanCommonOptions & ScanIteratorOptions + ) { + let cursor = options?.cursor ?? '0'; + do { + const reply = await this.zScan(key, cursor, options); + cursor = reply.cursor; + yield reply.members; + } while (cursor !== '0'); + } + + async MONITOR(callback: MonitorCallback) { + const promise = this._self.#queue.monitor(callback, { + typeMapping: this._commandOptions?.typeMapping + }); + this._self.#scheduleWrite(); + await promise; + this._self.#monitorCallback = callback; + } + + monitor = this.MONITOR; + + /** + * Reset the client to its default state (i.e. stop PubSub, stop monitoring, select default DB, etc.) + */ + async reset() { + const chainId = Symbol('Reset Chain'), + promises = [this._self.#queue.reset(chainId)], + selectedDB = this._self.#options?.database ?? 0; + for (const command of this._self.#handshake(selectedDB)) { + promises.push( + this._self.#queue.addCommand(command, { + chainId + }) + ); } - - async* hScanIterator(key: string, options?: ScanOptions): AsyncIterable> { - let cursor = 0; - do { - const reply = await (this as any).hScan(key, cursor, options); - cursor = reply.cursor; - for (const tuple of reply.tuples) { - yield tuple; - } - } while (cursor !== 0); + this._self.#scheduleWrite(); + await Promise.all(promises); + this._self.#selectedDB = selectedDB; + this._self.#monitorCallback = undefined; + this._self.#dirtyWatch = undefined; + this._self.#watchEpoch = undefined; + } + + /** + * If the client has state, reset it. + * An internal function to be used by wrapper class such as `RedisClientPool`. + * @internal + */ + resetIfDirty() { + let shouldReset = false; + if (this._self.#selectedDB !== (this._self.#options?.database ?? 0)) { + console.warn('Returning a client with a different selected DB'); + shouldReset = true; } - async* hScanNoValuesIterator(key: string, options?: ScanOptions): AsyncIterable> { - let cursor = 0; - do { - const reply = await (this as any).hScanNoValues(key, cursor, options); - cursor = reply.cursor; - for (const k of reply.keys) { - yield k; - } - } while (cursor !== 0); + if (this._self.#monitorCallback) { + console.warn('Returning a client with active MONITOR'); + shouldReset = true; } - async* sScanIterator(key: string, options?: ScanOptions): AsyncIterable { - let cursor = 0; - do { - const reply = await (this as any).sScan(key, cursor, options); - cursor = reply.cursor; - for (const member of reply.members) { - yield member; - } - } while (cursor !== 0); + if (this._self.#queue.isPubSubActive) { + console.warn('Returning a client with active PubSub'); + shouldReset = true; } - async* zScanIterator(key: string, options?: ScanOptions): AsyncIterable> { - let cursor = 0; - do { - const reply = await (this as any).zScan(key, cursor, options); - cursor = reply.cursor; - for (const member of reply.members) { - yield member; - } - } while (cursor !== 0); - } - - async disconnect(): Promise { - if (this.#pingTimer) clearTimeout(this.#pingTimer); - this.#queue.flushAll(new DisconnectsClientError()); - this.#socket.disconnect(); - await this.#destroyIsolationPool(); - } - - async #destroyIsolationPool(): Promise { - await this.#isolationPool!.drain(); - await this.#isolationPool!.clear(); - this.#isolationPool = undefined; - } - - ref(): void { - this.#socket.ref(); + if (this._self.#dirtyWatch || this._self.#watchEpoch) { + console.warn('Returning a client with active WATCH'); + shouldReset = true; } - unref(): void { - this.#socket.unref(); + if (shouldReset) { + return this.reset(); } + } + + /** + * @deprecated use .close instead + */ + QUIT(): Promise { + return this._self.#socket.quit(async () => { + clearTimeout(this._self.#pingTimer); + const quitPromise = this._self.#queue.addCommand(['QUIT']); + this._self.#scheduleWrite(); + return quitPromise; + }); + } + + quit = this.QUIT; + + /** + * @deprecated use .destroy instead + */ + disconnect() { + return Promise.resolve(this.destroy()); + } + + /** + * Close the client. Wait for pending commands. + */ + close() { + return new Promise(resolve => { + clearTimeout(this._self.#pingTimer); + this._self.#socket.close(); + + if (this._self.#queue.isEmpty()) { + this._self.#socket.destroySocket(); + return resolve(); + } + + const maybeClose = () => { + if (!this._self.#queue.isEmpty()) return; + + this._self.#socket.off('data', maybeClose); + this._self.#socket.destroySocket(); + resolve(); + }; + this._self.#socket.on('data', maybeClose); + }); + } + + /** + * Destroy the client. Rejects all commands immediately. + */ + destroy() { + clearTimeout(this._self.#pingTimer); + this._self.#queue.flushAll(new DisconnectsClientError()); + this._self.#socket.destroy(); + } + + ref() { + this._self.#socket.ref(); + } + + unref() { + this._self.#socket.unref(); + } } - -attachCommands({ - BaseClass: RedisClient, - commands: COMMANDS, - executor: RedisClient.prototype.commandsExecutor -}); -(RedisClient.prototype as any).Multi = RedisClientMultiCommand; diff --git a/packages/client/lib/client/legacy-mode.spec.ts b/packages/client/lib/client/legacy-mode.spec.ts new file mode 100644 index 00000000000..306ea7f3353 --- /dev/null +++ b/packages/client/lib/client/legacy-mode.spec.ts @@ -0,0 +1,111 @@ +import { strict as assert } from 'node:assert'; +import testUtils, { GLOBAL } from '../test-utils'; +import { promisify } from 'node:util'; +import { RedisLegacyClientType } from './legacy-mode'; +import { ErrorReply } from '../errors'; +import { RedisClientType } from '.'; +import { once } from 'node:events'; + +function testWithLegacyClient(title: string, fn: (legacy: RedisLegacyClientType, client: RedisClientType) => Promise) { + testUtils.testWithClient(title, client => fn(client.legacy(), client), GLOBAL.SERVERS.OPEN); +} + +describe('Legacy Mode', () => { + describe('client.sendCommand', () => { + testWithLegacyClient('resolve', async client => { + assert.equal( + await promisify(client.sendCommand).call(client, 'PING'), + 'PONG' + ); + }); + + testWithLegacyClient('reject', async client => { + await assert.rejects( + promisify(client.sendCommand).call(client, 'ERROR'), + ErrorReply + ); + }); + + testWithLegacyClient('reject without a callback', async (legacy, client) => { + legacy.sendCommand('ERROR'); + const [err] = await once(client, 'error'); + assert.ok(err instanceof ErrorReply); + }); + }); + + describe('hGetAll (TRANSFORM_LEGACY_REPLY)', () => { + testWithLegacyClient('resolve', async client => { + await promisify(client.hSet).call(client, 'key', 'field', 'value'); + assert.deepEqual( + await promisify(client.hGetAll).call(client, 'key'), + Object.create(null, { + field: { + value: 'value', + configurable: true, + enumerable: true + } + }) + ); + }); + + testWithLegacyClient('reject', async client => { + await assert.rejects( + promisify(client.hGetAll).call(client), + ErrorReply + ); + }); + }); + + describe('client.set', () => { + testWithLegacyClient('vardict', async client => { + assert.equal( + await promisify(client.set).call(client, 'a', 'b'), + 'OK' + ); + }); + + testWithLegacyClient('array', async client => { + assert.equal( + await promisify(client.set).call(client, ['a', 'b']), + 'OK' + ); + }); + + testWithLegacyClient('vardict & arrays', async client => { + assert.equal( + await promisify(client.set).call(client, ['a'], 'b', ['EX', 1]), + 'OK' + ); + }); + + testWithLegacyClient('reject without a callback', async (legacy, client) => { + legacy.set('ERROR'); + const [err] = await once(client, 'error'); + assert.ok(err instanceof ErrorReply); + }); + }); + + describe('client.multi', () => { + testWithLegacyClient('resolve', async client => { + const multi = client.multi().ping().sendCommand('PING'); + assert.deepEqual( + await promisify(multi.exec).call(multi), + ['PONG', 'PONG'] + ); + }); + + testWithLegacyClient('reject', async client => { + const multi = client.multi().sendCommand('ERROR'); + await assert.rejects( + promisify(multi.exec).call(multi), + ErrorReply + ); + }); + + testWithLegacyClient('reject without a callback', async (legacy, client) => { + legacy.multi().sendCommand('ERROR').exec(); + const [err] = await once(client, 'error'); + assert.ok(err instanceof ErrorReply); + }); + }); +}); diff --git a/packages/client/lib/client/legacy-mode.ts b/packages/client/lib/client/legacy-mode.ts new file mode 100644 index 00000000000..03e7cf4efe1 --- /dev/null +++ b/packages/client/lib/client/legacy-mode.ts @@ -0,0 +1,177 @@ +import { RedisModules, RedisFunctions, RedisScripts, RespVersions, Command, CommandArguments, ReplyUnion } from '../RESP/types'; +import { RedisClientType } from '.'; +import { getTransformReply } from '../commander'; +import { ErrorReply } from '../errors'; +import COMMANDS from '../commands'; +import RedisMultiCommand from '../multi-command'; + +type LegacyArgument = string | Buffer | number | Date; + +type LegacyArguments = Array; + +type LegacyCallback = (err: ErrorReply | null, reply?: ReplyUnion) => unknown + +type LegacyCommandArguments = LegacyArguments | [ + ...args: LegacyArguments, + callback: LegacyCallback +]; + +type WithCommands = { + [P in keyof typeof COMMANDS]: (...args: LegacyCommandArguments) => void; +}; + +export type RedisLegacyClientType = RedisLegacyClient & WithCommands; + +export class RedisLegacyClient { + static #transformArguments(redisArgs: CommandArguments, args: LegacyCommandArguments) { + let callback: LegacyCallback | undefined; + if (typeof args[args.length - 1] === 'function') { + callback = args.pop() as LegacyCallback; + } + + RedisLegacyClient.pushArguments(redisArgs, args as LegacyArguments); + + return callback; + } + + static pushArguments(redisArgs: CommandArguments, args: LegacyArguments) { + for (let i = 0; i < args.length; ++i) { + const arg = args[i]; + if (Array.isArray(arg)) { + RedisLegacyClient.pushArguments(redisArgs, arg); + } else { + redisArgs.push( + typeof arg === 'number' || arg instanceof Date ? + arg.toString() : + arg + ); + } + } + } + + static getTransformReply(command: Command, resp: RespVersions) { + return command.TRANSFORM_LEGACY_REPLY ? + getTransformReply(command, resp) : + undefined; + } + + static #createCommand(name: string, command: Command, resp: RespVersions) { + const transformReply = RedisLegacyClient.getTransformReply(command, resp); + return function (this: RedisLegacyClient, ...args: LegacyCommandArguments) { + const redisArgs = [name], + callback = RedisLegacyClient.#transformArguments(redisArgs, args), + promise = this.#client.sendCommand(redisArgs); + + if (!callback) { + promise.catch(err => this.#client.emit('error', err)); + return; + } + + promise + .then(reply => callback(null, transformReply ? transformReply(reply) : reply)) + .catch(err => callback(err)); + }; + } + + #client: RedisClientType; + #Multi: ReturnType; + + constructor( + client: RedisClientType + ) { + this.#client = client; + + const RESP = client.options?.RESP ?? 2; + for (const [name, command] of Object.entries(COMMANDS)) { + // TODO: as any? + (this as any)[name] = RedisLegacyClient.#createCommand( + name, + command, + RESP + ); + } + + this.#Multi = LegacyMultiCommand.factory(RESP); + } + + sendCommand(...args: LegacyCommandArguments) { + const redisArgs: CommandArguments = [], + callback = RedisLegacyClient.#transformArguments(redisArgs, args), + promise = this.#client.sendCommand(redisArgs); + + if (!callback) { + promise.catch(err => this.#client.emit('error', err)); + return; + } + + promise + .then(reply => callback(null, reply)) + .catch(err => callback(err)); + } + + multi() { + return this.#Multi(this.#client); + } +} + +type MultiWithCommands = { + [P in keyof typeof COMMANDS]: (...args: LegacyCommandArguments) => RedisLegacyMultiType; +}; + +export type RedisLegacyMultiType = LegacyMultiCommand & MultiWithCommands; + +class LegacyMultiCommand { + static #createCommand(name: string, command: Command, resp: RespVersions) { + const transformReply = RedisLegacyClient.getTransformReply(command, resp); + return function (this: LegacyMultiCommand, ...args: LegacyArguments) { + const redisArgs = [name]; + RedisLegacyClient.pushArguments(redisArgs, args); + this.#multi.addCommand(redisArgs, transformReply); + return this; + }; + } + + static factory(resp: RespVersions) { + const Multi = class extends LegacyMultiCommand {}; + + for (const [name, command] of Object.entries(COMMANDS)) { + // TODO: as any? + (Multi as any).prototype[name] = LegacyMultiCommand.#createCommand( + name, + command, + resp + ); + } + + return (client: RedisClientType) => { + return new Multi(client) as unknown as RedisLegacyMultiType; + }; + } + + readonly #multi = new RedisMultiCommand(); + readonly #client: RedisClientType; + + constructor(client: RedisClientType) { + this.#client = client; + } + + sendCommand(...args: LegacyArguments) { + const redisArgs: CommandArguments = []; + RedisLegacyClient.pushArguments(redisArgs, args); + this.#multi.addCommand(redisArgs); + return this; + } + + exec(cb?: (err: ErrorReply | null, replies?: Array) => unknown) { + const promise = this.#client._executeMulti(this.#multi.queue); + + if (!cb) { + promise.catch(err => this.#client.emit('error', err)); + return; + } + + promise + .then(results => cb(null, this.#multi.transformReplies(results))) + .catch(err => cb?.(err)); + } +} diff --git a/packages/client/lib/client/linked-list.spec.ts b/packages/client/lib/client/linked-list.spec.ts new file mode 100644 index 00000000000..9547fb81c7c --- /dev/null +++ b/packages/client/lib/client/linked-list.spec.ts @@ -0,0 +1,138 @@ +import { SinglyLinkedList, DoublyLinkedList } from './linked-list'; +import { equal, deepEqual } from 'assert/strict'; + +describe('DoublyLinkedList', () => { + const list = new DoublyLinkedList(); + + it('should start empty', () => { + equal(list.length, 0); + equal(list.head, undefined); + equal(list.tail, undefined); + deepEqual(Array.from(list), []); + }); + + it('shift empty', () => { + equal(list.shift(), undefined); + equal(list.length, 0); + deepEqual(Array.from(list), []); + }); + + it('push 1', () => { + list.push(1); + equal(list.length, 1); + deepEqual(Array.from(list), [1]); + }); + + it('push 2', () => { + list.push(2); + equal(list.length, 2); + deepEqual(Array.from(list), [1, 2]); + }); + + it('unshift 0', () => { + list.unshift(0); + equal(list.length, 3); + deepEqual(Array.from(list), [0, 1, 2]); + }); + + it('remove middle node', () => { + list.remove(list.head!.next!); + equal(list.length, 2); + deepEqual(Array.from(list), [0, 2]); + }); + + it('remove head', () => { + list.remove(list.head!); + equal(list.length, 1); + deepEqual(Array.from(list), [2]); + }); + + it('remove tail', () => { + list.remove(list.tail!); + equal(list.length, 0); + deepEqual(Array.from(list), []); + }); + + it('unshift empty queue', () => { + list.unshift(0); + equal(list.length, 1); + deepEqual(Array.from(list), [0]); + }); + + it('push 1', () => { + list.push(1); + equal(list.length, 2); + deepEqual(Array.from(list), [0, 1]); + }); + + it('shift', () => { + equal(list.shift(), 0); + equal(list.length, 1); + deepEqual(Array.from(list), [1]); + }); + + it('shift last element', () => { + equal(list.shift(), 1); + equal(list.length, 0); + deepEqual(Array.from(list), []); + }); +}); + +describe('SinglyLinkedList', () => { + const list = new SinglyLinkedList(); + + it('should start empty', () => { + equal(list.length, 0); + equal(list.head, undefined); + equal(list.tail, undefined); + deepEqual(Array.from(list), []); + }); + + it('shift empty', () => { + equal(list.shift(), undefined); + equal(list.length, 0); + deepEqual(Array.from(list), []); + }); + + it('push 1', () => { + list.push(1); + equal(list.length, 1); + deepEqual(Array.from(list), [1]); + }); + + it('push 2', () => { + list.push(2); + equal(list.length, 2); + deepEqual(Array.from(list), [1, 2]); + }); + + it('push 3', () => { + list.push(3); + equal(list.length, 3); + deepEqual(Array.from(list), [1, 2, 3]); + }); + + it('shift 1', () => { + equal(list.shift(), 1); + equal(list.length, 2); + deepEqual(Array.from(list), [2, 3]); + }); + + it('shift 2', () => { + equal(list.shift(), 2); + equal(list.length, 1); + deepEqual(Array.from(list), [3]); + }); + + it('shift 3', () => { + equal(list.shift(), 3); + equal(list.length, 0); + deepEqual(Array.from(list), []); + }); + + it('should be empty', () => { + equal(list.length, 0); + equal(list.head, undefined); + equal(list.tail, undefined); + }); +}); diff --git a/packages/client/lib/client/linked-list.ts b/packages/client/lib/client/linked-list.ts new file mode 100644 index 00000000000..ac1d021be91 --- /dev/null +++ b/packages/client/lib/client/linked-list.ts @@ -0,0 +1,195 @@ +export interface DoublyLinkedNode { + value: T; + previous: DoublyLinkedNode | undefined; + next: DoublyLinkedNode | undefined; +} + +export class DoublyLinkedList { + #length = 0; + + get length() { + return this.#length; + } + + #head?: DoublyLinkedNode; + + get head() { + return this.#head; + } + + #tail?: DoublyLinkedNode; + + get tail() { + return this.#tail; + } + + push(value: T) { + ++this.#length; + + if (this.#tail === undefined) { + return this.#tail = this.#head = { + previous: this.#head, + next: undefined, + value + }; + } + + return this.#tail = this.#tail.next = { + previous: this.#tail, + next: undefined, + value + }; + } + + unshift(value: T) { + ++this.#length; + + if (this.#head === undefined) { + return this.#head = this.#tail = { + previous: undefined, + next: undefined, + value + }; + } + + return this.#head = this.#head.previous = { + previous: undefined, + next: this.#head, + value + }; + } + + add(value: T, prepend = false) { + return prepend ? + this.unshift(value) : + this.push(value); + } + + shift() { + if (this.#head === undefined) return undefined; + + --this.#length; + const node = this.#head; + if (node.next) { + node.next.previous = node.previous; + this.#head = node.next; + node.next = undefined; + } else { + this.#head = this.#tail = undefined; + } + return node.value; + } + + remove(node: DoublyLinkedNode) { + --this.#length; + + if (this.#tail === node) { + this.#tail = node.previous; + } + + if (this.#head === node) { + this.#head = node.next; + } else { + node.previous!.next = node.next; + node.previous = undefined; + } + + node.next = undefined; + } + + reset() { + this.#length = 0; + this.#head = this.#tail = undefined; + } + + *[Symbol.iterator]() { + let node = this.#head; + while (node !== undefined) { + yield node.value; + node = node.next; + } + } +} + +export interface SinglyLinkedNode { + value: T; + next: SinglyLinkedNode | undefined; +} + +export class SinglyLinkedList { + #length = 0; + + get length() { + return this.#length; + } + + #head?: SinglyLinkedNode; + + get head() { + return this.#head; + } + + #tail?: SinglyLinkedNode; + + get tail() { + return this.#tail; + } + + push(value: T) { + ++this.#length; + + const node = { + value, + next: undefined + }; + + if (this.#head === undefined) { + return this.#head = this.#tail = node; + } + + return this.#tail!.next = this.#tail = node; + } + + remove(node: SinglyLinkedNode, parent: SinglyLinkedNode | undefined) { + --this.#length; + + if (this.#head === node) { + if (this.#tail === node) { + this.#head = this.#tail = undefined; + } else { + this.#head = node.next; + } + } else if (this.#tail === node) { + this.#tail = parent; + parent!.next = undefined; + } else { + parent!.next = node.next; + } + } + + shift() { + if (this.#head === undefined) return undefined; + + const node = this.#head; + if (--this.#length === 0) { + this.#head = this.#tail = undefined; + } else { + this.#head = node.next; + } + + return node.value; + } + + reset() { + this.#length = 0; + this.#head = this.#tail = undefined; + } + + *[Symbol.iterator]() { + let node = this.#head; + while (node !== undefined) { + yield node.value; + node = node.next; + } + } +} diff --git a/packages/client/lib/client/multi-command.ts b/packages/client/lib/client/multi-command.ts index e347667bf2c..b6579fcf9bf 100644 --- a/packages/client/lib/client/multi-command.ts +++ b/packages/client/lib/client/multi-command.ts @@ -1,200 +1,205 @@ -import COMMANDS from './commands'; -import { RedisCommand, RedisCommandArguments, RedisCommandRawReply, RedisFunctions, RedisModules, RedisExtensions, RedisScript, RedisScripts, ExcludeMappedString, RedisFunction, RedisCommands } from '../commands'; -import RedisMultiCommand, { RedisMultiQueuedCommand } from '../multi-command'; -import { attachCommands, attachExtensions, transformLegacyCommandArguments } from '../commander'; +import COMMANDS from '../commands'; +import RedisMultiCommand, { MULTI_REPLY, MultiReply, MultiReplyType, RedisMultiQueuedCommand } from '../multi-command'; +import { ReplyWithTypeMapping, CommandReply, Command, CommandArguments, CommanderConfig, RedisFunctions, RedisModules, RedisScripts, RespVersions, TransformReply, RedisScript, RedisFunction, TypeMapping } from '../RESP/types'; +import { attachConfig, functionArgumentsPrefix, getTransformReply } from '../commander'; type CommandSignature< - C extends RedisCommand, - M extends RedisModules, - F extends RedisFunctions, - S extends RedisScripts -> = (...args: Parameters) => RedisClientMultiCommandType; + REPLIES extends Array, + C extends Command, + M extends RedisModules, + F extends RedisFunctions, + S extends RedisScripts, + RESP extends RespVersions, + TYPE_MAPPING extends TypeMapping +> = (...args: Parameters) => RedisClientMultiCommandType< + [...REPLIES, ReplyWithTypeMapping, TYPE_MAPPING>], + M, + F, + S, + RESP, + TYPE_MAPPING +>; type WithCommands< - M extends RedisModules, - F extends RedisFunctions, - S extends RedisScripts + REPLIES extends Array, + M extends RedisModules, + F extends RedisFunctions, + S extends RedisScripts, + RESP extends RespVersions, + TYPE_MAPPING extends TypeMapping > = { - [P in keyof typeof COMMANDS]: CommandSignature<(typeof COMMANDS)[P], M, F, S>; + [P in keyof typeof COMMANDS]: CommandSignature; }; type WithModules< - M extends RedisModules, - F extends RedisFunctions, - S extends RedisScripts + REPLIES extends Array, + M extends RedisModules, + F extends RedisFunctions, + S extends RedisScripts, + RESP extends RespVersions, + TYPE_MAPPING extends TypeMapping > = { - [P in keyof M as ExcludeMappedString

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

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

]: CommandSignature; + [P in keyof S]: CommandSignature; }; export type RedisClientMultiCommandType< - M extends RedisModules, - F extends RedisFunctions, - S extends RedisScripts -> = RedisClientMultiCommand & WithCommands & WithModules & WithFunctions & WithScripts; - -type InstantiableRedisMultiCommand< - M extends RedisModules, - F extends RedisFunctions, - S extends RedisScripts -> = new (...args: ConstructorParameters) => RedisClientMultiCommandType; - -export type RedisClientMultiExecutor = ( - queue: Array, - selectedDB?: number, - chainId?: symbol -) => Promise>; - -export default class RedisClientMultiCommand { - static extend< - M extends RedisModules, - F extends RedisFunctions, - S extends RedisScripts - >(extensions?: RedisExtensions): InstantiableRedisMultiCommand { - return attachExtensions({ - BaseClass: RedisClientMultiCommand, - modulesExecutor: RedisClientMultiCommand.prototype.commandsExecutor, - modules: extensions?.modules, - functionsExecutor: RedisClientMultiCommand.prototype.functionsExecutor, - functions: extensions?.functions, - scriptsExecutor: RedisClientMultiCommand.prototype.scriptsExecutor, - scripts: extensions?.scripts - }); - } - - readonly #multi = new RedisMultiCommand(); - readonly #executor: RedisClientMultiExecutor; - readonly v4: Record = {}; - #selectedDB?: number; - - constructor(executor: RedisClientMultiExecutor, legacyMode = false) { - this.#executor = executor; - if (legacyMode) { - this.#legacyMode(); - } - } - - #legacyMode(): void { - this.v4.addCommand = this.addCommand.bind(this); - (this as any).addCommand = (...args: Array): this => { - this.#multi.addCommand(transformLegacyCommandArguments(args)); - return this; - }; - this.v4.exec = this.exec.bind(this); - (this as any).exec = (callback?: (err: Error | null, replies?: Array) => unknown): void => { - this.v4.exec() - .then((reply: Array) => { - if (!callback) return; - - callback(null, reply); - }) - .catch((err: Error) => { - if (!callback) { - // this.emit('error', err); - return; - } - - callback(err); - }); - }; - - for (const [ name, command ] of Object.entries(COMMANDS as RedisCommands)) { - this.#defineLegacyCommand(name, command); - (this as any)[name.toLowerCase()] ??= (this as any)[name]; - } - } - - #defineLegacyCommand(this: any, name: string, command?: RedisCommand): void { - this.v4[name] = this[name].bind(this.v4); - this[name] = command && command.TRANSFORM_LEGACY_REPLY && command.transformReply ? - (...args: Array) => { - this.#multi.addCommand( - [name, ...transformLegacyCommandArguments(args)], - command.transformReply - ); - return this; - } : - (...args: Array) => this.addCommand(name, ...args); - } - - commandsExecutor(command: RedisCommand, args: Array): this { - return this.addCommand( - command.transformArguments(...args), - command.transformReply - ); - } - - SELECT(db: number, transformReply?: RedisCommand['transformReply']): this { - this.#selectedDB = db; - return this.addCommand(['SELECT', db.toString()], transformReply); - } - - select = this.SELECT; - - addCommand(args: RedisCommandArguments, transformReply?: RedisCommand['transformReply']): this { - this.#multi.addCommand(args, transformReply); - return this; - } - - functionsExecutor(fn: RedisFunction, args: Array, name: string): this { - this.#multi.addFunction(name, fn, args); - return this; - } - - scriptsExecutor(script: RedisScript, args: Array): this { - this.#multi.addScript(script, args); - return this; - } - - async exec(execAsPipeline = false): Promise> { - if (execAsPipeline) { - return this.execAsPipeline(); - } - - return this.#multi.handleExecReplies( - await this.#executor( - this.#multi.queue, - this.#selectedDB, - RedisMultiCommand.generateChainId() - ) - ); - } - - EXEC = this.exec; - - async execAsPipeline(): Promise> { - if (this.#multi.queue.length === 0) return []; - - return this.#multi.transformReplies( - await this.#executor( - this.#multi.queue, - this.#selectedDB - ) - ); - } + REPLIES extends Array, + M extends RedisModules, + F extends RedisFunctions, + S extends RedisScripts, + RESP extends RespVersions, + TYPE_MAPPING extends TypeMapping +> = ( + RedisClientMultiCommand & + WithCommands & + WithModules & + WithFunctions & + WithScripts +); + +type ExecuteMulti = (commands: Array, selectedDB?: number) => Promise>; + +export default class RedisClientMultiCommand { + static #createCommand(command: Command, resp: RespVersions) { + const transformReply = getTransformReply(command, resp); + return function (this: RedisClientMultiCommand, ...args: Array) { + return this.addCommand( + command.transformArguments(...args), + transformReply + ); + }; + } + + static #createModuleCommand(command: Command, resp: RespVersions) { + const transformReply = getTransformReply(command, resp); + return function (this: { _self: RedisClientMultiCommand }, ...args: Array) { + return this._self.addCommand( + command.transformArguments(...args), + transformReply + ); + }; + } + + static #createFunctionCommand(name: string, fn: RedisFunction, resp: RespVersions) { + const prefix = functionArgumentsPrefix(name, fn), + transformReply = getTransformReply(fn, resp); + return function (this: { _self: RedisClientMultiCommand }, ...args: Array) { + const fnArgs = fn.transformArguments(...args), + redisArgs: CommandArguments = prefix.concat(fnArgs); + redisArgs.preserve = fnArgs.preserve; + return this._self.addCommand( + redisArgs, + transformReply + ); + }; + } + + static #createScriptCommand(script: RedisScript, resp: RespVersions) { + const transformReply = getTransformReply(script, resp); + return function (this: RedisClientMultiCommand, ...args: Array) { + this.#multi.addScript( + script, + script.transformArguments(...args), + transformReply + ); + return this; + }; + } + + static extend< + M extends RedisModules = Record, + F extends RedisFunctions = Record, + S extends RedisScripts = Record, + RESP extends RespVersions = 2 + >(config?: CommanderConfig) { + return attachConfig({ + BaseClass: RedisClientMultiCommand, + commands: COMMANDS, + createCommand: RedisClientMultiCommand.#createCommand, + createModuleCommand: RedisClientMultiCommand.#createModuleCommand, + createFunctionCommand: RedisClientMultiCommand.#createFunctionCommand, + createScriptCommand: RedisClientMultiCommand.#createScriptCommand, + config + }); + } + + readonly #multi = new RedisMultiCommand(); + readonly #executeMulti: ExecuteMulti; + readonly #executePipeline: ExecuteMulti; + readonly #typeMapping?: TypeMapping; + + #selectedDB?: number; + + constructor(executeMulti: ExecuteMulti, executePipeline: ExecuteMulti, typeMapping?: TypeMapping) { + this.#executeMulti = executeMulti; + this.#executePipeline = executePipeline; + this.#typeMapping = typeMapping; + } + + SELECT(db: number, transformReply?: TransformReply): this { + this.#selectedDB = db; + this.#multi.addCommand(['SELECT', db.toString()], transformReply); + return this; + } + + select = this.SELECT; + + addCommand(args: CommandArguments, transformReply?: TransformReply) { + this.#multi.addCommand(args, transformReply); + return this; + } + + async exec(execAsPipeline = false): Promise> { + if (execAsPipeline) return this.execAsPipeline(); + + return this.#multi.transformReplies( + await this.#executeMulti(this.#multi.queue, this.#selectedDB), + this.#typeMapping + ) as MultiReplyType; + } + + EXEC = this.exec; + + execTyped(execAsPipeline = false) { + return this.exec(execAsPipeline); + } + + async execAsPipeline(): Promise> { + if (this.#multi.queue.length === 0) return [] as MultiReplyType; + + return this.#multi.transformReplies( + await this.#executePipeline(this.#multi.queue, this.#selectedDB), + this.#typeMapping + ) as MultiReplyType; + } + + execAsPipelineTyped() { + return this.execAsPipeline(); + } } - -attachCommands({ - BaseClass: RedisClientMultiCommand, - commands: COMMANDS, - executor: RedisClientMultiCommand.prototype.commandsExecutor -}); diff --git a/packages/client/lib/client/pool.spec.ts b/packages/client/lib/client/pool.spec.ts new file mode 100644 index 00000000000..8fc7a258df9 --- /dev/null +++ b/packages/client/lib/client/pool.spec.ts @@ -0,0 +1,11 @@ +import { strict as assert } from 'node:assert'; +import testUtils, { GLOBAL } from '../test-utils'; + +describe('RedisClientPool', () => { + testUtils.testWithClientPool('sendCommand', async pool => { + assert.equal( + await pool.sendCommand(['PING']), + 'PONG' + ); + }, GLOBAL.SERVERS.OPEN); +}); diff --git a/packages/client/lib/client/pool.ts b/packages/client/lib/client/pool.ts new file mode 100644 index 00000000000..4bd99ece8b6 --- /dev/null +++ b/packages/client/lib/client/pool.ts @@ -0,0 +1,489 @@ +import COMMANDS from '../commands'; +import { Command, RedisArgument, RedisFunction, RedisFunctions, RedisModules, RedisScript, RedisScripts, RespVersions, TypeMapping } from '../RESP/types'; +import RedisClient, { RedisClientType, RedisClientOptions, RedisClientExtensions } from '.'; +import { EventEmitter } from 'node:events'; +import { DoublyLinkedNode, DoublyLinkedList, SinglyLinkedList } from './linked-list'; +import { TimeoutError } from '../errors'; +import { attachConfig, functionArgumentsPrefix, getTransformReply, scriptArgumentsPrefix } from '../commander'; +import { CommandOptions } from './commands-queue'; +import RedisClientMultiCommand, { RedisClientMultiCommandType } from './multi-command'; + +export interface RedisPoolOptions { + /** + * The minimum number of clients to keep in the pool (>= 1). + */ + minimum: number; + /** + * The maximum number of clients to keep in the pool (>= {@link RedisPoolOptions.minimum} >= 1). + */ + maximum: number; + /** + * The maximum time a task can wait for a client to become available (>= 0). + */ + acquireTimeout: number; + /** + * TODO + */ + cleanupDelay: number; + /** + * TODO + */ + unstableResp3Modules?: boolean; +} + +export type PoolTask< + M extends RedisModules, + F extends RedisFunctions, + S extends RedisScripts, + RESP extends RespVersions, + TYPE_MAPPING extends TypeMapping, + T = unknown +> = (client: RedisClientType) => T; + +export type RedisClientPoolType< + M extends RedisModules = {}, + F extends RedisFunctions = {}, + S extends RedisScripts = {}, + RESP extends RespVersions = 2, + TYPE_MAPPING extends TypeMapping = {} +> = ( + RedisClientPool & + RedisClientExtensions +); + +type ProxyPool = RedisClientPoolType; + +type NamespaceProxyPool = { _self: ProxyPool }; + +export class RedisClientPool< + M extends RedisModules = {}, + F extends RedisFunctions = {}, + S extends RedisScripts = {}, + RESP extends RespVersions = 2, + TYPE_MAPPING extends TypeMapping = {} +> extends EventEmitter { + static #createCommand(command: Command, resp: RespVersions) { + const transformReply = getTransformReply(command, resp); + return async function (this: ProxyPool, ...args: Array) { + const redisArgs = command.transformArguments(...args); + const typeMapping = this._commandOptions?.typeMapping; + + const reply = await this.sendCommand(redisArgs, this._commandOptions); + + return transformReply ? + transformReply(reply, redisArgs.preserve, typeMapping) : + reply; + }; + } + + static #createModuleCommand(command: Command, resp: RespVersions) { + const transformReply = getTransformReply(command, resp); + return async function (this: NamespaceProxyPool, ...args: Array) { + const redisArgs = command.transformArguments(...args); + const typeMapping = this._self._commandOptions?.typeMapping; + + const reply = await this._self.sendCommand(redisArgs, this._self._commandOptions); + + return transformReply ? + transformReply(reply, redisArgs.preserve, typeMapping) : + reply; + }; + } + + static #createFunctionCommand(name: string, fn: RedisFunction, resp: RespVersions) { + const prefix = functionArgumentsPrefix(name, fn), + transformReply = getTransformReply(fn, resp); + return async function (this: NamespaceProxyPool, ...args: Array) { + const fnArgs = fn.transformArguments(...args); + const typeMapping = this._self._commandOptions?.typeMapping; + + const reply = await this._self.sendCommand( + prefix.concat(fnArgs), + this._self._commandOptions + ); + + return transformReply ? + transformReply(reply, fnArgs.preserve, typeMapping) : + reply; + }; + } + + static #createScriptCommand(script: RedisScript, resp: RespVersions) { + const prefix = scriptArgumentsPrefix(script), + transformReply = getTransformReply(script, resp); + return async function (this: ProxyPool, ...args: Array) { + const scriptArgs = script.transformArguments(...args); + const redisArgs = prefix.concat(scriptArgs); + const typeMapping = this._commandOptions?.typeMapping; + + const reply = await this.executeScript(script, redisArgs, this._commandOptions); + + return transformReply ? + transformReply(reply, scriptArgs.preserve, typeMapping) : + reply; + }; + } + + static create< + M extends RedisModules, + F extends RedisFunctions, + S extends RedisScripts, + RESP extends RespVersions, + TYPE_MAPPING extends TypeMapping = {} + >( + clientOptions?: RedisClientOptions, + options?: Partial + ) { + const Pool = attachConfig({ + BaseClass: RedisClientPool, + commands: COMMANDS, + createCommand: RedisClientPool.#createCommand, + createModuleCommand: RedisClientPool.#createModuleCommand, + createFunctionCommand: RedisClientPool.#createFunctionCommand, + createScriptCommand: RedisClientPool.#createScriptCommand, + config: clientOptions + }); + + Pool.prototype.Multi = RedisClientMultiCommand.extend(clientOptions); + + // returning a "proxy" to prevent the namespaces._self to leak between "proxies" + return Object.create( + new Pool( + RedisClient.factory(clientOptions).bind(undefined, clientOptions), + options + ) + ) as RedisClientPoolType; + } + + // TODO: defaults + static #DEFAULTS = { + minimum: 1, + maximum: 100, + acquireTimeout: 3000, + cleanupDelay: 3000 + } satisfies RedisPoolOptions; + + readonly #clientFactory: () => RedisClientType; + readonly #options: RedisPoolOptions; + + readonly #idleClients = new SinglyLinkedList>(); + + /** + * The number of idle clients. + */ + get idleClients() { + return this._self.#idleClients.length; + } + + readonly #clientsInUse = new DoublyLinkedList>(); + + /** + * The number of clients in use. + */ + get clientsInUse() { + return this._self.#clientsInUse.length; + } + + /** + * The total number of clients in the pool (including connecting, idle, and in use). + */ + get totalClients() { + return this._self.#idleClients.length + this._self.#clientsInUse.length; + } + + readonly #tasksQueue = new SinglyLinkedList<{ + timeout: NodeJS.Timeout | undefined; + resolve: (value: unknown) => unknown; + reject: (reason?: unknown) => unknown; + fn: PoolTask; + }>(); + + /** + * The number of tasks waiting for a client to become available. + */ + get tasksQueueLength() { + return this._self.#tasksQueue.length; + } + + #isOpen = false; + + /** + * Whether the pool is open (either connecting or connected). + */ + get isOpen() { + return this._self.#isOpen; + } + + #isClosing = false; + + /** + * Whether the pool is closing (*not* closed). + */ + get isClosing() { + return this._self.#isClosing; + } + + /** + * You are probably looking for {@link RedisClient.createPool `RedisClient.createPool`}, + * {@link RedisClientPool.fromClient `RedisClientPool.fromClient`}, + * or {@link RedisClientPool.fromOptions `RedisClientPool.fromOptions`}... + */ + constructor( + clientFactory: () => RedisClientType, + options?: Partial + ) { + super(); + + this.#clientFactory = clientFactory; + this.#options = { + ...RedisClientPool.#DEFAULTS, + ...options + }; + } + + private _self = this; + private _commandOptions?: CommandOptions; + + withCommandOptions< + OPTIONS extends CommandOptions, + TYPE_MAPPING extends TypeMapping + >(options: OPTIONS) { + const proxy = Object.create(this._self); + proxy._commandOptions = options; + return proxy as RedisClientPoolType< + M, + F, + S, + RESP, + TYPE_MAPPING extends TypeMapping ? TYPE_MAPPING : {} + >; + } + + #commandOptionsProxy< + K extends keyof CommandOptions, + V extends CommandOptions[K] + >( + key: K, + value: V + ) { + const proxy = Object.create(this._self); + proxy._commandOptions = Object.create(this._commandOptions ?? null); + proxy._commandOptions[key] = value; + return proxy as RedisClientPoolType< + M, + F, + S, + RESP, + K extends 'typeMapping' ? V extends TypeMapping ? V : {} : TYPE_MAPPING + >; + } + + /** + * Override the `typeMapping` command option + */ + withTypeMapping(typeMapping: TYPE_MAPPING) { + return this._self.#commandOptionsProxy('typeMapping', typeMapping); + } + + /** + * Override the `abortSignal` command option + */ + withAbortSignal(abortSignal: AbortSignal) { + return this._self.#commandOptionsProxy('abortSignal', abortSignal); + } + + /** + * Override the `asap` command option to `true` + * TODO: remove? + */ + asap() { + return this._self.#commandOptionsProxy('asap', true); + } + + async connect() { + if (this._self.#isOpen) return; // TODO: throw error? + + this._self.#isOpen = true; + + const promises = []; + while (promises.length < this._self.#options.minimum) { + promises.push(this._self.#create()); + } + + try { + await Promise.all(promises); + return this as unknown as RedisClientPoolType; + } catch (err) { + this.destroy(); + throw err; + } + } + + async #create() { + const node = this._self.#clientsInUse.push( + this._self.#clientFactory() + .on('error', (err: Error) => this.emit('error', err)) + ); + + try { + await node.value.connect(); + } catch (err) { + this._self.#clientsInUse.remove(node); + throw err; + } + + this._self.#returnClient(node); + } + + execute(fn: PoolTask) { + return new Promise>((resolve, reject) => { + const client = this._self.#idleClients.shift(), + { tail } = this._self.#tasksQueue; + if (!client) { + let timeout; + if (this._self.#options.acquireTimeout > 0) { + timeout = setTimeout( + () => { + this._self.#tasksQueue.remove(task, tail); + reject(new TimeoutError('Timeout waiting for a client')); // TODO: message + }, + this._self.#options.acquireTimeout + ); + } + + const task = this._self.#tasksQueue.push({ + timeout, + // @ts-ignore + resolve, + reject, + fn + }); + + if (this.totalClients < this._self.#options.maximum) { + this._self.#create(); + } + + return; + } + + const node = this._self.#clientsInUse.push(client); + // @ts-ignore + this._self.#executeTask(node, resolve, reject, fn); + }); + } + + #executeTask( + node: DoublyLinkedNode>, + resolve: (value: T | PromiseLike) => void, + reject: (reason?: unknown) => void, + fn: PoolTask + ) { + const result = fn(node.value); + if (result instanceof Promise) { + result.then(resolve, reject); + result.finally(() => this.#returnClient(node)) + } else { + resolve(result); + this.#returnClient(node); + } + } + + #returnClient(node: DoublyLinkedNode>) { + const task = this.#tasksQueue.shift(); + if (task) { + clearTimeout(task.timeout); + this.#executeTask(node, task.resolve, task.reject, task.fn); + return; + } + + this.#clientsInUse.remove(node); + this.#idleClients.push(node.value); + + this.#scheduleCleanup(); + } + + cleanupTimeout?: NodeJS.Timeout; + + #scheduleCleanup() { + if (this.totalClients <= this.#options.minimum) return; + + clearTimeout(this.cleanupTimeout); + this.cleanupTimeout = setTimeout(() => this.#cleanup(), this.#options.cleanupDelay); + } + + #cleanup() { + const toDestroy = Math.min(this.#idleClients.length, this.totalClients - this.#options.minimum); + for (let i = 0; i < toDestroy; i++) { + // TODO: shift vs pop + this.#idleClients.shift()!.destroy(); + } + } + + sendCommand( + args: Array, + options?: CommandOptions + ) { + return this.execute(client => client.sendCommand(args, options)); + } + + executeScript( + script: RedisScript, + args: Array, + options?: CommandOptions + ) { + return this.execute(client => client.executeScript(script, args, options)); + } + + MULTI() { + type Multi = new (...args: ConstructorParameters) => RedisClientMultiCommandType<[], M, F, S, RESP, TYPE_MAPPING>; + return new ((this as any).Multi as Multi)( + (commands, selectedDB) => this.execute(client => client._executeMulti(commands, selectedDB)), + commands => this.execute(client => client._executePipeline(commands)), + this._commandOptions?.typeMapping + ); + } + + multi = this.MULTI; + + async close() { + if (this._self.#isClosing) return; // TODO: throw err? + if (!this._self.#isOpen) return; // TODO: throw err? + + this._self.#isClosing = true; + + try { + const promises = []; + + for (const client of this._self.#idleClients) { + promises.push(client.close()); + } + + for (const client of this._self.#clientsInUse) { + promises.push(client.close()); + } + + await Promise.all(promises); + + this._self.#idleClients.reset(); + this._self.#clientsInUse.reset(); + } catch (err) { + + } finally { + this._self.#isClosing = false; + } + } + + destroy() { + for (const client of this._self.#idleClients) { + client.destroy(); + } + this._self.#idleClients.reset(); + + for (const client of this._self.#clientsInUse) { + client.destroy(); + } + this._self.#clientsInUse.reset(); + + this._self.#isOpen = false; + } +} diff --git a/packages/client/lib/client/pub-sub.spec.ts b/packages/client/lib/client/pub-sub.spec.ts index 8b9f16732cb..74bd85c1831 100644 --- a/packages/client/lib/client/pub-sub.spec.ts +++ b/packages/client/lib/client/pub-sub.spec.ts @@ -1,151 +1,151 @@ -import { strict as assert } from 'assert'; -import { PubSub, PubSubType } from './pub-sub'; +import { strict as assert } from 'node:assert'; +import { PubSub, PUBSUB_TYPE } from './pub-sub'; describe('PubSub', () => { - const TYPE = PubSubType.CHANNELS, - CHANNEL = 'channel', - LISTENER = () => {}; - - describe('subscribe to new channel', () => { - function createAndSubscribe() { - const pubSub = new PubSub(), - command = pubSub.subscribe(TYPE, CHANNEL, LISTENER); - - assert.equal(pubSub.isActive, true); - assert.ok(command); - assert.equal(command.channelsCounter, 1); - - return { - pubSub, - command - }; - } - - it('resolve', () => { - const { pubSub, command } = createAndSubscribe(); - - command.resolve(); - - assert.equal(pubSub.isActive, true); - }); - - it('reject', () => { - const { pubSub, command } = createAndSubscribe(); - - assert.ok(command.reject); - command.reject(); - - assert.equal(pubSub.isActive, false); - }); + const TYPE = PUBSUB_TYPE.CHANNELS, + CHANNEL = 'channel', + LISTENER = () => {}; + + describe('subscribe to new channel', () => { + function createAndSubscribe() { + const pubSub = new PubSub(), + command = pubSub.subscribe(TYPE, CHANNEL, LISTENER); + + assert.equal(pubSub.isActive, true); + assert.ok(command); + assert.equal(command.channelsCounter, 1); + + return { + pubSub, + command + }; + } + + it('resolve', () => { + const { pubSub, command } = createAndSubscribe(); + + command.resolve(); + + assert.equal(pubSub.isActive, true); }); - it('subscribe to already subscribed channel', () => { - const pubSub = new PubSub(), - firstSubscribe = pubSub.subscribe(TYPE, CHANNEL, LISTENER); - assert.ok(firstSubscribe); + it('reject', () => { + const { pubSub, command } = createAndSubscribe(); - const secondSubscribe = pubSub.subscribe(TYPE, CHANNEL, LISTENER); - assert.ok(secondSubscribe); + assert.ok(command.reject); + command.reject(); - firstSubscribe.resolve(); + assert.equal(pubSub.isActive, false); + }); + }); + + it('subscribe to already subscribed channel', () => { + const pubSub = new PubSub(), + firstSubscribe = pubSub.subscribe(TYPE, CHANNEL, LISTENER); + assert.ok(firstSubscribe); + + const secondSubscribe = pubSub.subscribe(TYPE, CHANNEL, LISTENER); + assert.ok(secondSubscribe); + + firstSubscribe.resolve(); + + assert.equal( + pubSub.subscribe(TYPE, CHANNEL, LISTENER), + undefined + ); + }); + + it('unsubscribe all', () => { + const pubSub = new PubSub(); + + const subscribe = pubSub.subscribe(TYPE, CHANNEL, LISTENER); + assert.ok(subscribe); + subscribe.resolve(); + assert.equal(pubSub.isActive, true); + + const unsubscribe = pubSub.unsubscribe(TYPE); + assert.equal(pubSub.isActive, true); + assert.ok(unsubscribe); + unsubscribe.resolve(); + assert.equal(pubSub.isActive, false); + }); + + describe('unsubscribe from channel', () => { + it('when not subscribed', () => { + const pubSub = new PubSub(), + unsubscribe = pubSub.unsubscribe(TYPE, CHANNEL); + assert.ok(unsubscribe); + unsubscribe.resolve(); + assert.equal(pubSub.isActive, false); + }); - assert.equal( - pubSub.subscribe(TYPE, CHANNEL, LISTENER), - undefined - ); + it('when already subscribed', () => { + const pubSub = new PubSub(), + subscribe = pubSub.subscribe(TYPE, CHANNEL, LISTENER); + assert.ok(subscribe); + subscribe.resolve(); + assert.equal(pubSub.isActive, true); + + const unsubscribe = pubSub.unsubscribe(TYPE, CHANNEL); + assert.equal(pubSub.isActive, true); + assert.ok(unsubscribe); + unsubscribe.resolve(); + assert.equal(pubSub.isActive, false); + }); + }); + + describe('unsubscribe from listener', () => { + it('when it\'s the only listener', () => { + const pubSub = new PubSub(), + subscribe = pubSub.subscribe(TYPE, CHANNEL, LISTENER); + assert.ok(subscribe); + subscribe.resolve(); + assert.equal(pubSub.isActive, true); + + const unsubscribe = pubSub.unsubscribe(TYPE, CHANNEL, LISTENER); + assert.ok(unsubscribe); + unsubscribe.resolve(); + assert.equal(pubSub.isActive, false); }); - it('unsubscribe all', () => { - const pubSub = new PubSub(); - - const subscribe = pubSub.subscribe(TYPE, CHANNEL, LISTENER); + it('when there are more listeners', () => { + const pubSub = new PubSub(), + subscribe = pubSub.subscribe(TYPE, CHANNEL, LISTENER); + assert.ok(subscribe); + subscribe.resolve(); + assert.equal(pubSub.isActive, true); + + assert.equal( + pubSub.subscribe(TYPE, CHANNEL, () => { }), + undefined + ); + + assert.equal( + pubSub.unsubscribe(TYPE, CHANNEL, LISTENER), + undefined + ); + }); + + describe('non-existing listener', () => { + it('on subscribed channel', () => { + const pubSub = new PubSub(), + subscribe = pubSub.subscribe(TYPE, CHANNEL, LISTENER); assert.ok(subscribe); subscribe.resolve(); assert.equal(pubSub.isActive, true); - const unsubscribe = pubSub.unsubscribe(TYPE); + assert.equal( + pubSub.unsubscribe(TYPE, CHANNEL, () => { }), + undefined + ); assert.equal(pubSub.isActive, true); - assert.ok(unsubscribe); - unsubscribe.resolve(); - assert.equal(pubSub.isActive, false); - }); - - describe('unsubscribe from channel', () => { - it('when not subscribed', () => { - const pubSub = new PubSub(), - unsubscribe = pubSub.unsubscribe(TYPE, CHANNEL); - assert.ok(unsubscribe); - unsubscribe.resolve(); - assert.equal(pubSub.isActive, false); - }); - - it('when already subscribed', () => { - const pubSub = new PubSub(), - subscribe = pubSub.subscribe(TYPE, CHANNEL, LISTENER); - assert.ok(subscribe); - subscribe.resolve(); - assert.equal(pubSub.isActive, true); - - const unsubscribe = pubSub.unsubscribe(TYPE, CHANNEL); - assert.equal(pubSub.isActive, true); - assert.ok(unsubscribe); - unsubscribe.resolve(); - assert.equal(pubSub.isActive, false); - }); - }); + }); - describe('unsubscribe from listener', () => { - it('when it\'s the only listener', () => { - const pubSub = new PubSub(), - subscribe = pubSub.subscribe(TYPE, CHANNEL, LISTENER); - assert.ok(subscribe); - subscribe.resolve(); - assert.equal(pubSub.isActive, true); - - const unsubscribe = pubSub.unsubscribe(TYPE, CHANNEL, LISTENER); - assert.ok(unsubscribe); - unsubscribe.resolve(); - assert.equal(pubSub.isActive, false); - }); - - it('when there are more listeners', () => { - const pubSub = new PubSub(), - subscribe = pubSub.subscribe(TYPE, CHANNEL, LISTENER); - assert.ok(subscribe); - subscribe.resolve(); - assert.equal(pubSub.isActive, true); - - assert.equal( - pubSub.subscribe(TYPE, CHANNEL, () => {}), - undefined - ); - - assert.equal( - pubSub.unsubscribe(TYPE, CHANNEL, LISTENER), - undefined - ); - }); - - describe('non-existing listener', () => { - it('on subscribed channel', () => { - const pubSub = new PubSub(), - subscribe = pubSub.subscribe(TYPE, CHANNEL, LISTENER); - assert.ok(subscribe); - subscribe.resolve(); - assert.equal(pubSub.isActive, true); - - assert.equal( - pubSub.unsubscribe(TYPE, CHANNEL, () => {}), - undefined - ); - assert.equal(pubSub.isActive, true); - }); - - it('on unsubscribed channel', () => { - const pubSub = new PubSub(); - assert.ok(pubSub.unsubscribe(TYPE, CHANNEL, () => {})); - assert.equal(pubSub.isActive, false); - }); - }); + it('on unsubscribed channel', () => { + const pubSub = new PubSub(); + assert.ok(pubSub.unsubscribe(TYPE, CHANNEL, () => { })); + assert.equal(pubSub.isActive, false); + }); }); + }); }); diff --git a/packages/client/lib/client/pub-sub.ts b/packages/client/lib/client/pub-sub.ts index a8a909e0252..1387aea8417 100644 --- a/packages/client/lib/client/pub-sub.ts +++ b/packages/client/lib/client/pub-sub.ts @@ -1,408 +1,409 @@ -import { RedisCommandArgument } from "../commands"; +import { RedisArgument } from '../RESP/types'; +import { CommandToWrite } from './commands-queue'; -export enum PubSubType { - CHANNELS = 'CHANNELS', - PATTERNS = 'PATTERNS', - SHARDED = 'SHARDED' -} +export const PUBSUB_TYPE = { + CHANNELS: 'CHANNELS', + PATTERNS: 'PATTERNS', + SHARDED: 'SHARDED' +} as const; + +export type PUBSUB_TYPE = typeof PUBSUB_TYPE; + +export type PubSubType = PUBSUB_TYPE[keyof PUBSUB_TYPE]; const COMMANDS = { - [PubSubType.CHANNELS]: { - subscribe: Buffer.from('subscribe'), - unsubscribe: Buffer.from('unsubscribe'), - message: Buffer.from('message') - }, - [PubSubType.PATTERNS]: { - subscribe: Buffer.from('psubscribe'), - unsubscribe: Buffer.from('punsubscribe'), - message: Buffer.from('pmessage') - }, - [PubSubType.SHARDED]: { - subscribe: Buffer.from('ssubscribe'), - unsubscribe: Buffer.from('sunsubscribe'), - message: Buffer.from('smessage') - } + [PUBSUB_TYPE.CHANNELS]: { + subscribe: Buffer.from('subscribe'), + unsubscribe: Buffer.from('unsubscribe'), + message: Buffer.from('message') + }, + [PUBSUB_TYPE.PATTERNS]: { + subscribe: Buffer.from('psubscribe'), + unsubscribe: Buffer.from('punsubscribe'), + message: Buffer.from('pmessage') + }, + [PUBSUB_TYPE.SHARDED]: { + subscribe: Buffer.from('ssubscribe'), + unsubscribe: Buffer.from('sunsubscribe'), + message: Buffer.from('smessage') + } }; export type PubSubListener< - RETURN_BUFFERS extends boolean = false + RETURN_BUFFERS extends boolean = false > = (message: T, channel: T) => unknown; export interface ChannelListeners { - unsubscribing: boolean; - buffers: Set>; - strings: Set>; + unsubscribing: boolean; + buffers: Set>; + strings: Set>; } export type PubSubTypeListeners = Map; -type Listeners = Record; +export type PubSubListeners = Record; -export type PubSubCommand = ReturnType< - typeof PubSub.prototype.subscribe | - typeof PubSub.prototype.unsubscribe | - typeof PubSub.prototype.extendTypeListeners ->; +export type PubSubCommand = ( + Required> & { + reject: undefined | (() => unknown); + } +); export class PubSub { - static isStatusReply(reply: Array): boolean { - return ( - COMMANDS[PubSubType.CHANNELS].subscribe.equals(reply[0]) || - COMMANDS[PubSubType.CHANNELS].unsubscribe.equals(reply[0]) || - COMMANDS[PubSubType.PATTERNS].subscribe.equals(reply[0]) || - COMMANDS[PubSubType.PATTERNS].unsubscribe.equals(reply[0]) || - COMMANDS[PubSubType.SHARDED].subscribe.equals(reply[0]) - ); - } - - static isShardedUnsubscribe(reply: Array): boolean { - return COMMANDS[PubSubType.SHARDED].unsubscribe.equals(reply[0]); + static isStatusReply(reply: Array): boolean { + return ( + COMMANDS[PUBSUB_TYPE.CHANNELS].subscribe.equals(reply[0]) || + COMMANDS[PUBSUB_TYPE.CHANNELS].unsubscribe.equals(reply[0]) || + COMMANDS[PUBSUB_TYPE.PATTERNS].subscribe.equals(reply[0]) || + COMMANDS[PUBSUB_TYPE.PATTERNS].unsubscribe.equals(reply[0]) || + COMMANDS[PUBSUB_TYPE.SHARDED].subscribe.equals(reply[0]) + ); + } + + static isShardedUnsubscribe(reply: Array): boolean { + return COMMANDS[PUBSUB_TYPE.SHARDED].unsubscribe.equals(reply[0]); + } + + static #channelsArray(channels: string | Array) { + return (Array.isArray(channels) ? channels : [channels]); + } + + static #listenersSet( + listeners: ChannelListeners, + returnBuffers?: T + ) { + return (returnBuffers ? listeners.buffers : listeners.strings); + } + + #subscribing = 0; + + #isActive = false; + + get isActive() { + return this.#isActive; + } + + readonly listeners: PubSubListeners = { + [PUBSUB_TYPE.CHANNELS]: new Map(), + [PUBSUB_TYPE.PATTERNS]: new Map(), + [PUBSUB_TYPE.SHARDED]: new Map() + }; + + subscribe( + type: PubSubType, + channels: string | Array, + listener: PubSubListener, + returnBuffers?: T + ) { + const args: Array = [COMMANDS[type].subscribe], + channelsArray = PubSub.#channelsArray(channels); + for (const channel of channelsArray) { + let channelListeners = this.listeners[type].get(channel); + if (!channelListeners || channelListeners.unsubscribing) { + args.push(channel); + } } - - static #channelsArray(channels: string | Array) { - return (Array.isArray(channels) ? channels : [channels]); - } - - static #listenersSet( - listeners: ChannelListeners, - returnBuffers?: T - ) { - return (returnBuffers ? listeners.buffers : listeners.strings); - } - - #subscribing = 0; - - #isActive = false; - get isActive() { - return this.#isActive; + if (args.length === 1) { + // all channels are already subscribed, add listeners without issuing a command + for (const channel of channelsArray) { + PubSub.#listenersSet( + this.listeners[type].get(channel)!, + returnBuffers + ).add(listener); + } + return; } - #listeners: Listeners = { - [PubSubType.CHANNELS]: new Map(), - [PubSubType.PATTERNS]: new Map(), - [PubSubType.SHARDED]: new Map() - }; - - subscribe( - type: PubSubType, - channels: string | Array, - listener: PubSubListener, - returnBuffers?: T - ) { - const args: Array = [COMMANDS[type].subscribe], - channelsArray = PubSub.#channelsArray(channels); + this.#isActive = true; + this.#subscribing++; + return { + args, + channelsCounter: args.length - 1, + resolve: () => { + this.#subscribing--; for (const channel of channelsArray) { - let channelListeners = this.#listeners[type].get(channel); - if (!channelListeners || channelListeners.unsubscribing) { - args.push(channel); - } + let listeners = this.listeners[type].get(channel); + if (!listeners) { + listeners = { + unsubscribing: false, + buffers: new Set(), + strings: new Set() + }; + this.listeners[type].set(channel, listeners); + } + + PubSub.#listenersSet(listeners, returnBuffers).add(listener); } - - if (args.length === 1) { - // all channels are already subscribed, add listeners without issuing a command - for (const channel of channelsArray) { - PubSub.#listenersSet( - this.#listeners[type].get(channel)!, - returnBuffers - ).add(listener); - } - return; - } - - this.#isActive = true; - this.#subscribing++; - return { - args, - channelsCounter: args.length - 1, - resolve: () => { - this.#subscribing--; - for (const channel of channelsArray) { - let listeners = this.#listeners[type].get(channel); - if (!listeners) { - listeners = { - unsubscribing: false, - buffers: new Set(), - strings: new Set() - }; - this.#listeners[type].set(channel, listeners); - } - - PubSub.#listenersSet(listeners, returnBuffers).add(listener); - } - }, - reject: () => { - this.#subscribing--; - this.#updateIsActive(); - } - }; + }, + reject: () => { + this.#subscribing--; + this.#updateIsActive(); + } + } satisfies PubSubCommand; + } + + extendChannelListeners( + type: PubSubType, + channel: string, + listeners: ChannelListeners + ) { + if (!this.#extendChannelListeners(type, channel, listeners)) return; + + this.#isActive = true; + this.#subscribing++; + return { + args: [ + COMMANDS[type].subscribe, + channel + ], + channelsCounter: 1, + resolve: () => this.#subscribing--, + reject: () => { + this.#subscribing--; + this.#updateIsActive(); + } + } satisfies PubSubCommand; + } + + #extendChannelListeners( + type: PubSubType, + channel: string, + listeners: ChannelListeners + ) { + const existingListeners = this.listeners[type].get(channel); + if (!existingListeners) { + this.listeners[type].set(channel, listeners); + return true; } - extendChannelListeners( - type: PubSubType, - channel: string, - listeners: ChannelListeners - ) { - if (!this.#extendChannelListeners(type, channel, listeners)) return; - - this.#isActive = true; - this.#subscribing++; - return { - args: [ - COMMANDS[type].subscribe, - channel - ], - channelsCounter: 1, - resolve: () => this.#subscribing--, - reject: () => { - this.#subscribing--; - this.#updateIsActive(); - } - }; + for (const listener of listeners.buffers) { + existingListeners.buffers.add(listener); } - #extendChannelListeners( - type: PubSubType, - channel: string, - listeners: ChannelListeners - ) { - const existingListeners = this.#listeners[type].get(channel); - if (!existingListeners) { - this.#listeners[type].set(channel, listeners); - return true; - } - - for (const listener of listeners.buffers) { - existingListeners.buffers.add(listener); - } - - for (const listener of listeners.strings) { - existingListeners.strings.add(listener); - } - - return false; + for (const listener of listeners.strings) { + existingListeners.strings.add(listener); } - extendTypeListeners(type: PubSubType, listeners: PubSubTypeListeners) { - const args: Array = [COMMANDS[type].subscribe]; - for (const [channel, channelListeners] of listeners) { - if (this.#extendChannelListeners(type, channel, channelListeners)) { - args.push(channel); - } - } + return false; + } - if (args.length === 1) return; - - this.#isActive = true; - this.#subscribing++; - return { - args, - channelsCounter: args.length - 1, - resolve: () => this.#subscribing--, - reject: () => { - this.#subscribing--; - this.#updateIsActive(); - } - }; + extendTypeListeners(type: PubSubType, listeners: PubSubTypeListeners) { + const args: Array = [COMMANDS[type].subscribe]; + for (const [channel, channelListeners] of listeners) { + if (this.#extendChannelListeners(type, channel, channelListeners)) { + args.push(channel); + } } - unsubscribe( - type: PubSubType, - channels?: string | Array, - listener?: PubSubListener, - returnBuffers?: T - ) { - const listeners = this.#listeners[type]; - if (!channels) { - return this.#unsubscribeCommand( - [COMMANDS[type].unsubscribe], - // cannot use `this.#subscribed` because there might be some `SUBSCRIBE` commands in the queue - // cannot use `this.#subscribed + this.#subscribing` because some `SUBSCRIBE` commands might fail - NaN, - () => listeners.clear() - ); - } + if (args.length === 1) return; - const channelsArray = PubSub.#channelsArray(channels); - if (!listener) { - return this.#unsubscribeCommand( - [COMMANDS[type].unsubscribe, ...channelsArray], - channelsArray.length, - () => { - for (const channel of channelsArray) { - listeners.delete(channel); - } - } - ); - } + this.#isActive = true; + this.#subscribing++; + return { + args, + channelsCounter: args.length - 1, + resolve: () => this.#subscribing--, + reject: () => { + this.#subscribing--; + this.#updateIsActive(); + } + } satisfies PubSubCommand; + } + + unsubscribe( + type: PubSubType, + channels?: string | Array, + listener?: PubSubListener, + returnBuffers?: T + ) { + const listeners = this.listeners[type]; + if (!channels) { + return this.#unsubscribeCommand( + [COMMANDS[type].unsubscribe], + // cannot use `this.#subscribed` because there might be some `SUBSCRIBE` commands in the queue + // cannot use `this.#subscribed + this.#subscribing` because some `SUBSCRIBE` commands might fail + NaN, + () => listeners.clear() + ); + } - const args: Array = [COMMANDS[type].unsubscribe]; - for (const channel of channelsArray) { - const sets = listeners.get(channel); - if (sets) { - let current, - other; - if (returnBuffers) { - current = sets.buffers; - other = sets.strings; - } else { - current = sets.strings; - other = sets.buffers; - } - - const currentSize = current.has(listener) ? current.size - 1 : current.size; - if (currentSize !== 0 || other.size !== 0) continue; - sets.unsubscribing = true; - } - - args.push(channel); + const channelsArray = PubSub.#channelsArray(channels); + if (!listener) { + return this.#unsubscribeCommand( + [COMMANDS[type].unsubscribe, ...channelsArray], + channelsArray.length, + () => { + for (const channel of channelsArray) { + listeners.delete(channel); + } } + ); + } - if (args.length === 1) { - // all channels has other listeners, - // delete the listeners without issuing a command - for (const channel of channelsArray) { - PubSub.#listenersSet( - listeners.get(channel)!, - returnBuffers - ).delete(listener); - } - return; + const args: Array = [COMMANDS[type].unsubscribe]; + for (const channel of channelsArray) { + const sets = listeners.get(channel); + if (sets) { + let current, + other; + if (returnBuffers) { + current = sets.buffers; + other = sets.strings; + } else { + current = sets.strings; + other = sets.buffers; } - return this.#unsubscribeCommand( - args, - args.length - 1, - () => { - for (const channel of channelsArray) { - const sets = listeners.get(channel); - if (!sets) continue; - - (returnBuffers ? sets.buffers : sets.strings).delete(listener); - if (sets.buffers.size === 0 && sets.strings.size === 0) { - listeners.delete(channel); - } - } - } - ); - } + const currentSize = current.has(listener) ? current.size - 1 : current.size; + if (currentSize !== 0 || other.size !== 0) continue; + sets.unsubscribing = true; + } - #unsubscribeCommand( - args: Array, - channelsCounter: number, - removeListeners: () => void - ) { - return { - args, - channelsCounter, - resolve: () => { - removeListeners(); - this.#updateIsActive(); - }, - reject: undefined // use the same structure as `subscribe` - }; + args.push(channel); } - #updateIsActive() { - this.#isActive = ( - this.#listeners[PubSubType.CHANNELS].size !== 0 || - this.#listeners[PubSubType.PATTERNS].size !== 0 || - this.#listeners[PubSubType.SHARDED].size !== 0 || - this.#subscribing !== 0 - ); + if (args.length === 1) { + // all channels has other listeners, + // delete the listeners without issuing a command + for (const channel of channelsArray) { + PubSub.#listenersSet( + listeners.get(channel)!, + returnBuffers + ).delete(listener); + } + return; } - reset() { - this.#isActive = false; - this.#subscribing = 0; - } + return this.#unsubscribeCommand( + args, + args.length - 1, + () => { + for (const channel of channelsArray) { + const sets = listeners.get(channel); + if (!sets) continue; - resubscribe(): Array { - const commands = []; - for (const [type, listeners] of Object.entries(this.#listeners)) { - if (!listeners.size) continue; - - this.#isActive = true; - this.#subscribing++; - const callback = () => this.#subscribing--; - commands.push({ - args: [ - COMMANDS[type as PubSubType].subscribe, - ...listeners.keys() - ], - channelsCounter: listeners.size, - resolve: callback, - reject: callback - }); + (returnBuffers ? sets.buffers : sets.strings).delete(listener); + if (sets.buffers.size === 0 && sets.strings.size === 0) { + listeners.delete(channel); + } } - - return commands; + } + ); + } + + #unsubscribeCommand( + args: Array, + channelsCounter: number, + removeListeners: () => void + ) { + return { + args, + channelsCounter, + resolve: () => { + removeListeners(); + this.#updateIsActive(); + }, + reject: undefined + } satisfies PubSubCommand; + } + + #updateIsActive() { + this.#isActive = ( + this.listeners[PUBSUB_TYPE.CHANNELS].size !== 0 || + this.listeners[PUBSUB_TYPE.PATTERNS].size !== 0 || + this.listeners[PUBSUB_TYPE.SHARDED].size !== 0 || + this.#subscribing !== 0 + ); + } + + reset() { + this.#isActive = false; + this.#subscribing = 0; + } + + resubscribe() { + const commands = []; + for (const [type, listeners] of Object.entries(this.listeners)) { + if (!listeners.size) continue; + + this.#isActive = true; + this.#subscribing++; + const callback = () => this.#subscribing--; + commands.push({ + args: [ + COMMANDS[type as PubSubType].subscribe, + ...listeners.keys() + ], + channelsCounter: listeners.size, + resolve: callback, + reject: callback + } satisfies PubSubCommand); } - handleMessageReply(reply: Array): boolean { - if (COMMANDS[PubSubType.CHANNELS].message.equals(reply[0])) { - this.#emitPubSubMessage( - PubSubType.CHANNELS, - reply[2], - reply[1] - ); - return true; - } else if (COMMANDS[PubSubType.PATTERNS].message.equals(reply[0])) { - this.#emitPubSubMessage( - PubSubType.PATTERNS, - reply[3], - reply[2], - reply[1] - ); - return true; - } else if (COMMANDS[PubSubType.SHARDED].message.equals(reply[0])) { - this.#emitPubSubMessage( - PubSubType.SHARDED, - reply[2], - reply[1] - ); - return true; - } - - return false; + return commands; + } + + handleMessageReply(reply: Array): boolean { + if (COMMANDS[PUBSUB_TYPE.CHANNELS].message.equals(reply[0])) { + this.#emitPubSubMessage( + PUBSUB_TYPE.CHANNELS, + reply[2], + reply[1] + ); + return true; + } else if (COMMANDS[PUBSUB_TYPE.PATTERNS].message.equals(reply[0])) { + this.#emitPubSubMessage( + PUBSUB_TYPE.PATTERNS, + reply[3], + reply[2], + reply[1] + ); + return true; + } else if (COMMANDS[PUBSUB_TYPE.SHARDED].message.equals(reply[0])) { + this.#emitPubSubMessage( + PUBSUB_TYPE.SHARDED, + reply[2], + reply[1] + ); + return true; } - removeShardedListeners(channel: string): ChannelListeners { - const listeners = this.#listeners[PubSubType.SHARDED].get(channel)!; - this.#listeners[PubSubType.SHARDED].delete(channel); - this.#updateIsActive(); - return listeners; + return false; + } + + removeShardedListeners(channel: string): ChannelListeners { + const listeners = this.listeners[PUBSUB_TYPE.SHARDED].get(channel)!; + this.listeners[PUBSUB_TYPE.SHARDED].delete(channel); + this.#updateIsActive(); + return listeners; + } + + #emitPubSubMessage( + type: PubSubType, + message: Buffer, + channel: Buffer, + pattern?: Buffer + ): void { + const keyString = (pattern ?? channel).toString(), + listeners = this.listeners[type].get(keyString); + + if (!listeners) return; + + for (const listener of listeners.buffers) { + listener(message, channel); } - - #emitPubSubMessage( - type: PubSubType, - message: Buffer, - channel: Buffer, - pattern?: Buffer - ): void { - const keyString = (pattern ?? channel).toString(), - listeners = this.#listeners[type].get(keyString); - - if (!listeners) return; - - for (const listener of listeners.buffers) { - listener(message, channel); - } - - if (!listeners.strings.size) return; - const channelString = pattern ? channel.toString() : keyString, - messageString = channelString === '__redis__:invalidate' ? - // https://github.com/redis/redis/pull/7469 - // https://github.com/redis/redis/issues/7463 - (message === null ? null : (message as any as Array).map(x => x.toString())) as any : - message.toString(); - for (const listener of listeners.strings) { - listener(messageString, channelString); - } - } + if (!listeners.strings.size) return; - getTypeListeners(type: PubSubType): PubSubTypeListeners { - return this.#listeners[type]; + const channelString = pattern ? channel.toString() : keyString, + messageString = channelString === '__redis__:invalidate' ? + // https://github.com/redis/redis/pull/7469 + // https://github.com/redis/redis/issues/7463 + (message === null ? null : (message as any as Array).map(x => x.toString())) as any : + message.toString(); + for (const listener of listeners.strings) { + listener(messageString, channelString); } + } } diff --git a/packages/client/lib/client/socket.spec.ts b/packages/client/lib/client/socket.spec.ts index eb555351ac4..20b238a3a38 100644 --- a/packages/client/lib/client/socket.spec.ts +++ b/packages/client/lib/client/socket.spec.ts @@ -1,87 +1,87 @@ -import { strict as assert } from 'assert'; +import { strict as assert } from 'node:assert'; import { spy } from 'sinon'; -import { once } from 'events'; +import { once } from 'node:events'; import RedisSocket, { RedisSocketOptions } from './socket'; describe('Socket', () => { - function createSocket(options: RedisSocketOptions): RedisSocket { - const socket = new RedisSocket( - () => Promise.resolve(), - options - ); - - socket.on('error', () => { - // ignore errors - }); - - return socket; - } - - describe('reconnectStrategy', () => { - it('false', async () => { - const socket = createSocket({ - host: 'error', - connectTimeout: 1, - reconnectStrategy: false - }); - - await assert.rejects(socket.connect()); - - assert.equal(socket.isOpen, false); - }); - - it('0', async () => { - const socket = createSocket({ - host: 'error', - connectTimeout: 1, - reconnectStrategy: 0 - }); - - socket.connect(); - await once(socket, 'error'); - assert.equal(socket.isOpen, true); - assert.equal(socket.isReady, false); - socket.disconnect(); - assert.equal(socket.isOpen, false); - }); - - it('custom strategy', async () => { - const numberOfRetries = 3; - - const reconnectStrategy = spy((retries: number) => { - assert.equal(retries + 1, reconnectStrategy.callCount); - - if (retries === numberOfRetries) return new Error(`${numberOfRetries}`); - - return 0; - }); - - const socket = createSocket({ - host: 'error', - connectTimeout: 1, - reconnectStrategy - }); - - await assert.rejects(socket.connect(), { - message: `${numberOfRetries}` - }); - - assert.equal(socket.isOpen, false); - }); - - it('should handle errors', async () => { - const socket = createSocket({ - host: 'error', - connectTimeout: 1, - reconnectStrategy(retries: number) { - if (retries === 1) return new Error('done'); - throw new Error(); - } - }); - - await assert.rejects(socket.connect()); - - assert.equal(socket.isOpen, false); - }); + function createSocket(options: RedisSocketOptions): RedisSocket { + const socket = new RedisSocket( + () => Promise.resolve(), + options + ); + + socket.on('error', () => { + // ignore errors }); + + return socket; + } + + describe('reconnectStrategy', () => { + it('false', async () => { + const socket = createSocket({ + host: 'error', + connectTimeout: 1, + reconnectStrategy: false + }); + + await assert.rejects(socket.connect()); + + assert.equal(socket.isOpen, false); + }); + + it('0', async () => { + const socket = createSocket({ + host: 'error', + connectTimeout: 1, + reconnectStrategy: 0 + }); + + socket.connect(); + await once(socket, 'error'); + assert.equal(socket.isOpen, true); + assert.equal(socket.isReady, false); + socket.destroy(); + assert.equal(socket.isOpen, false); + }); + + it('custom strategy', async () => { + const numberOfRetries = 3; + + const reconnectStrategy = spy((retries: number) => { + assert.equal(retries + 1, reconnectStrategy.callCount); + + if (retries === numberOfRetries) return new Error(`${numberOfRetries}`); + + return 0; + }); + + const socket = createSocket({ + host: 'error', + connectTimeout: 1, + reconnectStrategy + }); + + await assert.rejects(socket.connect(), { + message: `${numberOfRetries}` + }); + + assert.equal(socket.isOpen, false); + }); + + it('should handle errors', async () => { + const socket = createSocket({ + host: 'error', + connectTimeout: 1, + reconnectStrategy(retries: number) { + if (retries === 1) return new Error('done'); + throw new Error(); + } + }); + + await assert.rejects(socket.connect()); + + assert.equal(socket.isOpen, false); + }); + }); }); diff --git a/packages/client/lib/client/socket.ts b/packages/client/lib/client/socket.ts index b701f6ea979..3c2666e1067 100644 --- a/packages/client/lib/client/socket.ts +++ b/packages/client/lib/client/socket.ts @@ -1,310 +1,345 @@ -import { EventEmitter } from 'events'; -import * as net from 'net'; -import * as tls from 'tls'; -import { RedisCommandArguments } from '../commands'; +import { EventEmitter, once } from 'node:events'; +import net from 'node:net'; +import tls from 'node:tls'; import { ConnectionTimeoutError, ClientClosedError, SocketClosedUnexpectedlyError, ReconnectStrategyError } from '../errors'; -import { promiseTimeout } from '../utils'; - -export interface RedisSocketCommonOptions { - /** - * Connection Timeout (in milliseconds) - */ - connectTimeout?: number; - /** - * Toggle [`Nagle's algorithm`](https://nodejs.org/api/net.html#net_socket_setnodelay_nodelay) - */ - noDelay?: boolean; - /** - * Toggle [`keep-alive`](https://nodejs.org/api/net.html#net_socket_setkeepalive_enable_initialdelay) - */ - keepAlive?: number | false; - /** - * When the socket closes unexpectedly (without calling `.quit()`/`.disconnect()`), the client uses `reconnectStrategy` to decide what to do. The following values are supported: - * 1. `false` -> do not reconnect, close the client and flush the command queue. - * 2. `number` -> wait for `X` milliseconds before reconnecting. - * 3. `(retries: number, cause: Error) => false | number | Error` -> `number` is the same as configuring a `number` directly, `Error` is the same as `false`, but with a custom error. - * Defaults to `retries => Math.min(retries * 50, 500)` - */ - reconnectStrategy?: false | number | ((retries: number, cause: Error) => false | Error | number); -} +import { setTimeout } from 'node:timers/promises'; +import { RedisArgument } from '../RESP/types'; -type RedisNetSocketOptions = Partial & { - tls?: false; +type NetOptions = { + tls?: false; }; -export interface RedisTlsSocketOptions extends tls.ConnectionOptions { - tls: true; +type ReconnectStrategyFunction = (retries: number, cause: Error) => false | Error | number; + +type RedisSocketOptionsCommon = { + /** + * Connection timeout (in milliseconds) + */ + connectTimeout?: number; + /** + * When the socket closes unexpectedly (without calling `.close()`/`.destroy()`), the client uses `reconnectStrategy` to decide what to do. The following values are supported: + * 1. `false` -> do not reconnect, close the client and flush the command queue. + * 2. `number` -> wait for `X` milliseconds before reconnecting. + * 3. `(retries: number, cause: Error) => false | number | Error` -> `number` is the same as configuring a `number` directly, `Error` is the same as `false`, but with a custom error. + */ + reconnectStrategy?: false | number | ReconnectStrategyFunction; } -export type RedisSocketOptions = RedisSocketCommonOptions & (RedisNetSocketOptions | RedisTlsSocketOptions); +type RedisTcpOptions = RedisSocketOptionsCommon & NetOptions & Omit< + net.TcpNetConnectOpts, + 'timeout' | 'onread' | 'readable' | 'writable' | 'port' +> & { + port?: number; +}; -interface CreateSocketReturn { - connectEvent: string; - socket: T; +type RedisTlsOptions = RedisSocketOptionsCommon & tls.ConnectionOptions & { + tls: true; + host: string; } -export type RedisSocketInitiator = () => Promise; - -export default class RedisSocket extends EventEmitter { - static #initiateOptions(options?: RedisSocketOptions): RedisSocketOptions { - options ??= {}; - if (!(options as net.IpcSocketConnectOpts).path) { - (options as net.TcpSocketConnectOpts).port ??= 6379; - (options as net.TcpSocketConnectOpts).host ??= 'localhost'; - } - - options.connectTimeout ??= 5000; - options.keepAlive ??= 5000; - options.noDelay ??= true; - - return options; - } +type RedisIpcOptions = RedisSocketOptionsCommon & Omit< + net.IpcNetConnectOpts, + 'timeout' | 'onread' | 'readable' | 'writable' +> & { + tls: false; +} - static #isTlsSocket(options: RedisSocketOptions): options is RedisTlsSocketOptions { - return (options as RedisTlsSocketOptions).tls === true; - } +export type RedisTcpSocketOptions = RedisTcpOptions | RedisTlsOptions; - readonly #initiator: RedisSocketInitiator; +export type RedisSocketOptions = RedisTcpSocketOptions | RedisIpcOptions; - readonly #options: RedisSocketOptions; +export type RedisSocketInitiator = () => void | Promise; - #socket?: net.Socket | tls.TLSSocket; +export default class RedisSocket extends EventEmitter { + readonly #initiator; + readonly #connectTimeout; + readonly #reconnectStrategy; + readonly #socketFactory; - #isOpen = false; + #socket?: net.Socket | tls.TLSSocket; - get isOpen(): boolean { - return this.#isOpen; - } + #isOpen = false; - #isReady = false; + get isOpen() { + return this.#isOpen; + } - get isReady(): boolean { - return this.#isReady; - } + #isReady = false; - // `writable.writableNeedDrain` was added in v15.2.0 and therefore can't be used - // https://nodejs.org/api/stream.html#stream_writable_writableneeddrain - #writableNeedDrain = false; + get isReady() { + return this.#isReady; + } - get writableNeedDrain(): boolean { - return this.#writableNeedDrain; - } + #isSocketUnrefed = false; - #isSocketUnrefed = false; + constructor(initiator: RedisSocketInitiator, options?: RedisSocketOptions) { + super(); - constructor(initiator: RedisSocketInitiator, options?: RedisSocketOptions) { - super(); + this.#initiator = initiator; + this.#connectTimeout = options?.connectTimeout ?? 5000; + this.#reconnectStrategy = this.#createReconnectStrategy(options); + this.#socketFactory = this.#createSocketFactory(options); + } - this.#initiator = initiator; - this.#options = RedisSocket.#initiateOptions(options); + #createReconnectStrategy(options?: RedisSocketOptions): ReconnectStrategyFunction { + const strategy = options?.reconnectStrategy; + if (strategy === false || typeof strategy === 'number') { + return () => strategy; } - #reconnectStrategy(retries: number, cause: Error) { - if (this.#options.reconnectStrategy === false) { - return false; - } else if (typeof this.#options.reconnectStrategy === 'number') { - return this.#options.reconnectStrategy; - } else if (this.#options.reconnectStrategy) { - try { - const retryIn = this.#options.reconnectStrategy(retries, cause); - if (retryIn !== false && !(retryIn instanceof Error) && typeof retryIn !== 'number') { - throw new TypeError(`Reconnect strategy should return \`false | Error | number\`, got ${retryIn} instead`); - } - - return retryIn; - } catch (err) { - this.emit('error', err); - } + if (strategy) { + return (retries, cause) => { + try { + const retryIn = strategy(retries, cause); + if (retryIn !== false && !(retryIn instanceof Error) && typeof retryIn !== 'number') { + throw new TypeError(`Reconnect strategy should return \`false | Error | number\`, got ${retryIn} instead`); + } + return retryIn; + } catch (err) { + this.emit('error', err); + return this.defaultReconnectStrategy(retries); } - - return Math.min(retries * 50, 500); + }; } - #shouldReconnect(retries: number, cause: Error) { - const retryIn = this.#reconnectStrategy(retries, cause); - if (retryIn === false) { - this.#isOpen = false; - this.emit('error', cause); - return cause; - } else if (retryIn instanceof Error) { - this.#isOpen = false; - this.emit('error', cause); - return new ReconnectStrategyError(retryIn, cause); - } - - return retryIn; + return this.defaultReconnectStrategy; + } + + #createSocketFactory(options?: RedisSocketOptions) { + // TLS + if (options?.tls === true) { + const withDefaults: tls.ConnectionOptions = { + ...options, + port: options?.port ?? 6379, + // https://nodejs.org/api/tls.html#tlsconnectoptions-callback "Any socket.connect() option not already listed" + // @types/node is... incorrect... + // @ts-expect-error + noDelay: options?.noDelay ?? true, + // https://nodejs.org/api/tls.html#tlsconnectoptions-callback "Any socket.connect() option not already listed" + // @types/node is... incorrect... + // @ts-expect-error + keepAlive: options?.keepAlive ?? true, + // https://nodejs.org/api/tls.html#tlsconnectoptions-callback "Any socket.connect() option not already listed" + // @types/node is... incorrect... + // @ts-expect-error + keepAliveInitialDelay: options?.keepAliveInitialDelay ?? 5000, + timeout: undefined, + onread: undefined, + readable: true, + writable: true + }; + return { + create() { + return tls.connect(withDefaults); + }, + event: 'secureConnect' + }; } - async connect(): Promise { - if (this.#isOpen) { - throw new Error('Socket already opened'); - } - - this.#isOpen = true; - return this.#connect(); + // IPC + if (options && 'path' in options) { + const withDefaults: net.IpcNetConnectOpts = { + ...options, + timeout: undefined, + onread: undefined, + readable: true, + writable: true + }; + return { + create() { + return net.createConnection(withDefaults); + }, + event: 'connect' + }; } - async #connect(): Promise { - let retries = 0; - do { - try { - this.#socket = await this.#createSocket(); - this.#writableNeedDrain = false; - this.emit('connect'); - - try { - await this.#initiator(); - } catch (err) { - this.#socket.destroy(); - this.#socket = undefined; - throw err; - } - this.#isReady = true; - this.emit('ready'); - } catch (err) { - const retryIn = this.#shouldReconnect(retries++, err as Error); - if (typeof retryIn !== 'number') { - throw retryIn; - } - - this.emit('error', err); - await promiseTimeout(retryIn); - this.emit('reconnecting'); - } - } while (this.#isOpen && !this.#isReady); + // TCP + const withDefaults: net.TcpNetConnectOpts = { + ...options, + port: options?.port ?? 6379, + noDelay: options?.noDelay ?? true, + keepAlive: options?.keepAlive ?? true, + keepAliveInitialDelay: options?.keepAliveInitialDelay ?? 5000, + timeout: undefined, + onread: undefined, + readable: true, + writable: true + }; + return { + create() { + return net.createConnection(withDefaults); + }, + event: 'connect' + }; + } + + #shouldReconnect(retries: number, cause: Error) { + const retryIn = this.#reconnectStrategy(retries, cause); + if (retryIn === false) { + this.#isOpen = false; + this.emit('error', cause); + return cause; + } else if (retryIn instanceof Error) { + this.#isOpen = false; + this.emit('error', cause); + return new ReconnectStrategyError(retryIn, cause); } - #createSocket(): Promise { - return new Promise((resolve, reject) => { - const { connectEvent, socket } = RedisSocket.#isTlsSocket(this.#options) ? - this.#createTlsSocket() : - this.#createNetSocket(); - - if (this.#options.connectTimeout) { - socket.setTimeout(this.#options.connectTimeout, () => socket.destroy(new ConnectionTimeoutError())); - } - - if (this.#isSocketUnrefed) { - socket.unref(); - } - - socket - .setNoDelay(this.#options.noDelay) - .once('error', reject) - .once(connectEvent, () => { - socket - .setTimeout(0) - // https://github.com/nodejs/node/issues/31663 - .setKeepAlive(this.#options.keepAlive !== false, this.#options.keepAlive || 0) - .off('error', reject) - .once('error', (err: Error) => this.#onSocketError(err)) - .once('close', hadError => { - if (!hadError && this.#isOpen && this.#socket === socket) { - this.#onSocketError(new SocketClosedUnexpectedlyError()); - } - }) - .on('drain', () => { - this.#writableNeedDrain = false; - this.emit('drain'); - }) - .on('data', data => this.emit('data', data)); - - resolve(socket); - }); - }); - } + return retryIn; + } - #createNetSocket(): CreateSocketReturn { - return { - connectEvent: 'connect', - socket: net.connect(this.#options as net.NetConnectOpts) // TODO - }; + async connect(): Promise { + if (this.#isOpen) { + throw new Error('Socket already opened'); } - #createTlsSocket(): CreateSocketReturn { - return { - connectEvent: 'secureConnect', - socket: tls.connect(this.#options as tls.ConnectionOptions) // TODO - }; - } + this.#isOpen = true; + return this.#connect(); + } + + async #connect(): Promise { + let retries = 0; + do { + try { + this.#socket = await this.#createSocket(); + this.emit('connect'); + + try { + await this.#initiator(); + } catch (err) { + this.#socket.destroy(); + this.#socket = undefined; + throw err; + } + this.#isReady = true; + this.emit('ready'); + } catch (err) { + const retryIn = this.#shouldReconnect(retries++, err as Error); + if (typeof retryIn !== 'number') { + throw retryIn; + } - #onSocketError(err: Error): void { - const wasReady = this.#isReady; - this.#isReady = false; this.emit('error', err); - - if (!wasReady || !this.#isOpen || typeof this.#shouldReconnect(0, err) !== 'number') return; - + await setTimeout(retryIn); this.emit('reconnecting'); - this.#connect().catch(() => { - // the error was already emitted, silently ignore it - }); + } + } while (this.#isOpen && !this.#isReady); + } + + async #createSocket(): Promise { + const socket = this.#socketFactory.create(); + + let onTimeout; + if (this.#connectTimeout !== undefined) { + onTimeout = () => socket.destroy(new ConnectionTimeoutError()); + socket.once('timeout', onTimeout); + socket.setTimeout(this.#connectTimeout); } - writeCommand(args: RedisCommandArguments): void { - if (!this.#socket) { - throw new ClientClosedError(); - } - - for (const toWrite of args) { - this.#writableNeedDrain = !this.#socket.write(toWrite); - } + if (this.#isSocketUnrefed) { + socket.unref(); } - disconnect(): void { - if (!this.#isOpen) { - throw new ClientClosedError(); - } + await once(socket, this.#socketFactory.event); - this.#isOpen = false; - this.#disconnect(); + if (onTimeout) { + socket.removeListener('timeout', onTimeout); } - #disconnect(): void { - this.#isReady = false; + socket + .once('error', err => this.#onSocketError(err)) + .once('close', hadError => { + if (hadError || !this.#isOpen || this.#socket !== socket) return; + this.#onSocketError(new SocketClosedUnexpectedlyError()); + }) + .on('drain', () => this.emit('drain')) + .on('data', data => this.emit('data', data)); + + return socket; + } + + #onSocketError(err: Error): void { + const wasReady = this.#isReady; + this.#isReady = false; + this.emit('error', err); + + if (!wasReady || !this.#isOpen || typeof this.#shouldReconnect(0, err) !== 'number') return; + + this.emit('reconnecting'); + this.#connect().catch(() => { + // the error was already emitted, silently ignore it + }); + } + + write(iterable: Iterable>) { + if (!this.#socket) return; + + this.#socket.cork(); + for (const args of iterable) { + for (const toWrite of args) { + this.#socket.write(toWrite); + } + + if (this.#socket.writableNeedDrain) break; + } + this.#socket.uncork(); + } - if (this.#socket) { - this.#socket.destroy(); - this.#socket = undefined; - } - - this.emit('end'); + async quit(fn: () => Promise): Promise { + if (!this.#isOpen) { + throw new ClientClosedError(); } - async quit(fn: () => Promise): Promise { - if (!this.#isOpen) { - throw new ClientClosedError(); - } + this.#isOpen = false; + const reply = await fn(); + this.destroySocket(); + return reply; + } - this.#isOpen = false; - const reply = await fn(); - this.#disconnect(); - return reply; + close() { + if (!this.#isOpen) { + throw new ClientClosedError(); } - #isCorked = false; + this.#isOpen = false; + } - cork(): void { - if (!this.#socket || this.#isCorked) { - return; - } + destroy() { + if (!this.#isOpen) { + throw new ClientClosedError(); + } - this.#socket.cork(); - this.#isCorked = true; + this.#isOpen = false; + this.destroySocket(); + } - setImmediate(() => { - this.#socket?.uncork(); - this.#isCorked = false; - }); - } + destroySocket() { + this.#isReady = false; - ref(): void { - this.#isSocketUnrefed = false; - this.#socket?.ref(); + if (this.#socket) { + this.#socket.destroy(); + this.#socket = undefined; } - unref(): void { - this.#isSocketUnrefed = true; - this.#socket?.unref(); - } + this.emit('end'); + } + + ref() { + this.#isSocketUnrefed = false; + this.#socket?.ref(); + } + + unref() { + this.#isSocketUnrefed = true; + this.#socket?.unref(); + } + + defaultReconnectStrategy(retries: number) { + // Generate a random jitter between 0 – 200 ms: + const jitter = Math.floor(Math.random() * 200); + // Delay is an exponential back off, (times^2) * 50 ms, with a maximum value of 2000 ms: + const delay = Math.min(Math.pow(2, retries) * 50, 2000); + + return delay + jitter; + } } diff --git a/packages/client/lib/cluster/cluster-slots.ts b/packages/client/lib/cluster/cluster-slots.ts index 45c96a80b50..824cf2ae813 100644 --- a/packages/client/lib/cluster/cluster-slots.ts +++ b/packages/client/lib/cluster/cluster-slots.ts @@ -1,621 +1,614 @@ -import RedisClient, { InstantiableRedisClient, RedisClientType } from '../client'; import { RedisClusterClientOptions, RedisClusterOptions } from '.'; -import { RedisCommandArgument, RedisFunctions, RedisModules, RedisScripts } from '../commands'; import { RootNodesUnavailableError } from '../errors'; -import { ClusterSlotsNode } from '../commands/CLUSTER_SLOTS'; -import { types } from 'util'; -import { ChannelListeners, PubSubType, PubSubTypeListeners } from '../client/pub-sub'; -import { EventEmitter } from 'stream'; - -// We need to use 'require', because it's not possible with Typescript to import -// function that are exported as 'module.exports = function`, without esModuleInterop -// set to true. -const calculateSlot = require('cluster-key-slot'); +import RedisClient, { RedisClientOptions, RedisClientType } from '../client'; +import { EventEmitter } from 'node:stream'; +import { ChannelListeners, PUBSUB_TYPE, PubSubTypeListeners } from '../client/pub-sub'; +import { RedisArgument, RedisFunctions, RedisModules, RedisScripts, RespVersions, TypeMapping } from '../RESP/types'; +import calculateSlot from 'cluster-key-slot'; +import { RedisSocketOptions } from '../client/socket'; interface NodeAddress { - host: string; - port: number; + host: string; + port: number; } export type NodeAddressMap = { - [address: string]: NodeAddress; + [address: string]: NodeAddress; } | ((address: string) => NodeAddress | undefined); -type ValueOrPromise = T | Promise; - -type ClientOrPromise< - M extends RedisModules, - F extends RedisFunctions, - S extends RedisScripts -> = ValueOrPromise>; - export interface Node< - M extends RedisModules, - F extends RedisFunctions, - S extends RedisScripts + M extends RedisModules, + F extends RedisFunctions, + S extends RedisScripts, + RESP extends RespVersions, + TYPE_MAPPING extends TypeMapping > { - address: string; - client?: ClientOrPromise; + address: string; + client?: RedisClientType; + connectPromise?: Promise>; } export interface ShardNode< - M extends RedisModules, - F extends RedisFunctions, - S extends RedisScripts -> extends Node { - id: string; - host: string; - port: number; - readonly: boolean; + M extends RedisModules, + F extends RedisFunctions, + S extends RedisScripts, + RESP extends RespVersions, + TYPE_MAPPING extends TypeMapping +> extends Node, NodeAddress { + id: string; + readonly: boolean; } export interface MasterNode< - M extends RedisModules, - F extends RedisFunctions, - S extends RedisScripts -> extends ShardNode { - pubSubClient?: ClientOrPromise; + M extends RedisModules, + F extends RedisFunctions, + S extends RedisScripts, + RESP extends RespVersions, + TYPE_MAPPING extends TypeMapping +> extends ShardNode { + pubSub?: { + connectPromise?: Promise>; + client: RedisClientType; + }; } export interface Shard< - M extends RedisModules, - F extends RedisFunctions, - S extends RedisScripts + M extends RedisModules, + F extends RedisFunctions, + S extends RedisScripts, + RESP extends RespVersions, + TYPE_MAPPING extends TypeMapping > { - master: MasterNode; - replicas?: Array>; - nodesIterator?: IterableIterator>; + master: MasterNode; + replicas?: Array>; + nodesIterator?: IterableIterator>; } type ShardWithReplicas< - M extends RedisModules, - F extends RedisFunctions, - S extends RedisScripts -> = Shard & Required, 'replicas'>>; - -export type PubSubNode< - M extends RedisModules, - F extends RedisFunctions, - S extends RedisScripts -> = Required>; + M extends RedisModules, + F extends RedisFunctions, + S extends RedisScripts, + RESP extends RespVersions, + TYPE_MAPPING extends TypeMapping +> = Shard & Required, 'replicas'>>; + +type PubSubNode< + M extends RedisModules, + F extends RedisFunctions, + S extends RedisScripts, + RESP extends RespVersions, + TYPE_MAPPING extends TypeMapping +> = ( + Omit, 'client'> & + Required, 'client'>> +); type PubSubToResubscribe = Record< - PubSubType.CHANNELS | PubSubType.PATTERNS, - PubSubTypeListeners + PUBSUB_TYPE['CHANNELS'] | PUBSUB_TYPE['PATTERNS'], + PubSubTypeListeners >; export type OnShardedChannelMovedError = ( - err: unknown, - channel: string, - listeners?: ChannelListeners + err: unknown, + channel: string, + listeners?: ChannelListeners ) => void; export default class RedisClusterSlots< - M extends RedisModules, - F extends RedisFunctions, - S extends RedisScripts + M extends RedisModules, + F extends RedisFunctions, + S extends RedisScripts, + RESP extends RespVersions, + TYPE_MAPPING extends TypeMapping > { - static #SLOTS = 16384; - - readonly #options: RedisClusterOptions; - readonly #Client: InstantiableRedisClient; - readonly #emit: EventEmitter['emit']; - slots = new Array>(RedisClusterSlots.#SLOTS); - shards = new Array>(); - masters = new Array>(); - replicas = new Array>(); - readonly nodeByAddress = new Map | ShardNode>(); - pubSubNode?: PubSubNode; - - #isOpen = false; - - get isOpen() { - return this.#isOpen; - } - - constructor( - options: RedisClusterOptions, - emit: EventEmitter['emit'] - ) { - this.#options = options; - this.#Client = RedisClient.extend(options); - this.#emit = emit; + static #SLOTS = 16384; + + readonly #options; + readonly #clientFactory; + readonly #emit: EventEmitter['emit']; + slots = new Array>(RedisClusterSlots.#SLOTS); + masters = new Array>(); + replicas = new Array>(); + readonly nodeByAddress = new Map | ShardNode>(); + pubSubNode?: PubSubNode; + + #isOpen = false; + + get isOpen() { + return this.#isOpen; + } + + constructor( + options: RedisClusterOptions, + emit: EventEmitter['emit'] + ) { + this.#options = options; + this.#clientFactory = RedisClient.factory(options); + this.#emit = emit; + } + + async connect() { + if (this.#isOpen) { + throw new Error('Cluster already open'); } - async connect() { - if (this.#isOpen) { - throw new Error('Cluster already open'); - } - - this.#isOpen = true; - try { - await this.#discoverWithRootNodes(); - } catch (err) { - this.#isOpen = false; - throw err; - } + this.#isOpen = true; + try { + await this.#discoverWithRootNodes(); + } catch (err) { + this.#isOpen = false; + throw err; } + } - async #discoverWithRootNodes() { - let start = Math.floor(Math.random() * this.#options.rootNodes.length); - for (let i = start; i < this.#options.rootNodes.length; i++) { - if (await this.#discover(this.#options.rootNodes[i])) return; - } - - for (let i = 0; i < start; i++) { - if (await this.#discover(this.#options.rootNodes[i])) return; - } - - throw new RootNodesUnavailableError(); + async #discoverWithRootNodes() { + let start = Math.floor(Math.random() * this.#options.rootNodes.length); + for (let i = start; i < this.#options.rootNodes.length; i++) { + if (!this.#isOpen) throw new Error('Cluster closed'); + if (await this.#discover(this.#options.rootNodes[i])) return; } - #resetSlots() { - this.slots = new Array(RedisClusterSlots.#SLOTS); - this.shards = []; - this.masters = []; - this.replicas = []; - this.#randomNodeIterator = undefined; + for (let i = 0; i < start; i++) { + if (!this.#isOpen) throw new Error('Cluster closed'); + if (await this.#discover(this.#options.rootNodes[i])) return; } - async #discover(rootNode?: RedisClusterClientOptions) { - const addressesInUse = new Set(); + throw new RootNodesUnavailableError(); + } + + #resetSlots() { + this.slots = new Array(RedisClusterSlots.#SLOTS); + this.masters = []; + this.replicas = []; + this._randomNodeIterator = undefined; + } + + async #discover(rootNode: RedisClusterClientOptions) { + this.#resetSlots(); + try { + const addressesInUse = new Set(), + promises: Array> = [], + eagerConnect = this.#options.minimizeConnections !== true; + + for (const { from, to, master, replicas } of await this.#getShards(rootNode)) { + const shard: Shard = { + master: this.#initiateSlotNode(master, false, eagerConnect, addressesInUse, promises) + }; - try { - const shards = await this.#getShards(rootNode), - promises: Array> = [], - eagerConnect = this.#options.minimizeConnections !== true; - this.#resetSlots(); - for (const { from, to, master, replicas } of shards) { - const shard: Shard = { - master: this.#initiateSlotNode(master, false, eagerConnect, addressesInUse, promises) - }; - - if (this.#options.useReplicas) { - shard.replicas = replicas.map(replica => - this.#initiateSlotNode(replica, true, eagerConnect, addressesInUse, promises) - ); - } - - this.shards.push(shard); - - for (let i = from; i <= to; i++) { - this.slots[i] = shard; - } - } - - if (this.pubSubNode && !addressesInUse.has(this.pubSubNode.address)) { - if (types.isPromise(this.pubSubNode.client)) { - promises.push( - this.pubSubNode.client.then(client => client.disconnect()) - ); - this.pubSubNode = undefined; - } else { - promises.push(this.pubSubNode.client.disconnect()); - - const channelsListeners = this.pubSubNode.client.getPubSubListeners(PubSubType.CHANNELS), - patternsListeners = this.pubSubNode.client.getPubSubListeners(PubSubType.PATTERNS); - - if (channelsListeners.size || patternsListeners.size) { - promises.push( - this.#initiatePubSubClient({ - [PubSubType.CHANNELS]: channelsListeners, - [PubSubType.PATTERNS]: patternsListeners - }) - ); - } - } - } - - for (const [address, node] of this.nodeByAddress.entries()) { - if (addressesInUse.has(address)) continue; - - if (node.client) { - promises.push( - this.#execOnNodeClient(node.client, client => client.disconnect()) - ); - } - - const { pubSubClient } = node as MasterNode; - if (pubSubClient) { - promises.push( - this.#execOnNodeClient(pubSubClient, client => client.disconnect()) - ); - } - - this.nodeByAddress.delete(address); - } - - await Promise.all(promises); - - return true; - } catch (err) { - this.#emit('error', err); - return false; + if (this.#options.useReplicas) { + shard.replicas = replicas.map(replica => + this.#initiateSlotNode(replica, true, eagerConnect, addressesInUse, promises) + ); } - } - async #getShards(rootNode?: RedisClusterClientOptions) { - const client = new this.#Client( - this.#clientOptionsDefaults(rootNode, true) - ); + for (let i = from; i <= to; i++) { + this.slots[i] = shard; + } + } - client.on('error', err => this.#emit('error', err)); + if (this.pubSubNode && !addressesInUse.has(this.pubSubNode.address)) { + const channelsListeners = this.pubSubNode.client.getPubSubListeners(PUBSUB_TYPE.CHANNELS), + patternsListeners = this.pubSubNode.client.getPubSubListeners(PUBSUB_TYPE.PATTERNS); - await client.connect(); + this.pubSubNode.client.destroy(); - try { - // using `CLUSTER SLOTS` and not `CLUSTER SHARDS` to support older versions - return await client.clusterSlots(); - } finally { - await client.disconnect(); + if (channelsListeners.size || patternsListeners.size) { + promises.push( + this.#initiatePubSubClient({ + [PUBSUB_TYPE.CHANNELS]: channelsListeners, + [PUBSUB_TYPE.PATTERNS]: patternsListeners + }) + ); } - } + } - #getNodeAddress(address: string): NodeAddress | undefined { - switch (typeof this.#options.nodeAddressMap) { - case 'object': - return this.#options.nodeAddressMap[address]; + for (const [address, node] of this.nodeByAddress.entries()) { + if (addressesInUse.has(address)) continue; - case 'function': - return this.#options.nodeAddressMap(address); + if (node.client) { + node.client.destroy(); } - } - #clientOptionsDefaults( - options?: RedisClusterClientOptions, - disableReconnect?: boolean - ): RedisClusterClientOptions | undefined { - let result: RedisClusterClientOptions | undefined; - if (this.#options.defaults) { - let socket; - if (this.#options.defaults.socket) { - socket = { - ...this.#options.defaults.socket, - ...options?.socket - }; - } else { - socket = options?.socket; - } - - result = { - ...this.#options.defaults, - ...options, - socket - }; - } else { - result = options; - } - - if (disableReconnect) { - result ??= {}; - result.socket ??= {}; - result.socket.reconnectStrategy = false; + const { pubSub } = node as MasterNode; + if (pubSub) { + pubSub.client.destroy(); } - return result; - } - - #initiateSlotNode( - { id, ip, port }: ClusterSlotsNode, - readonly: boolean, - eagerConnent: boolean, - addressesInUse: Set, - promises: Array> - ) { - const address = `${ip}:${port}`; - addressesInUse.add(address); - - let node = this.nodeByAddress.get(address); - if (!node) { - node = { - id, - host: ip, - port, - address, - readonly, - client: undefined - }; - - if (eagerConnent) { - promises.push(this.#createNodeClient(node)); - } - - this.nodeByAddress.set(address, node); - } + this.nodeByAddress.delete(address); + } - (readonly ? this.replicas : this.masters).push(node); + await Promise.all(promises); - return node; + return true; + } catch (err) { + this.#emit('error', err); + return false; } - - async #createClient( - node: ShardNode, - readonly = node.readonly - ) { - const client = new this.#Client( - this.#clientOptionsDefaults({ - socket: this.#getNodeAddress(node.address) ?? { - host: node.host, - port: node.port - }, - readonly - }) - ); - client.on('error', err => this.#emit('error', err)); - - await client.connect(); - - return client; + } + + async #getShards(rootNode: RedisClusterClientOptions) { + const options = this.#clientOptionsDefaults(rootNode)!; + options.socket ??= {}; + options.socket.reconnectStrategy = false; + options.RESP = this.#options.RESP; + options.commandOptions = undefined; + + // TODO: find a way to avoid type casting + const client = await this.#clientFactory(options as RedisClientOptions) + .on('error', err => this.#emit('error', err)) + .connect(); + + try { + // switch to `CLUSTER SHARDS` when Redis 7.0 will be the minimum supported version + return await client.clusterSlots(); + } finally { + client.destroy(); } + } - #createNodeClient(node: ShardNode) { - const promise = this.#createClient(node) - .then(client => { - node.client = client; - return client; - }) - .catch(err => { - node.client = undefined; - throw err; - }); - node.client = promise; - return promise; - } + #getNodeAddress(address: string): NodeAddress | undefined { + switch (typeof this.#options.nodeAddressMap) { + case 'object': + return this.#options.nodeAddressMap[address]; - nodeClient(node: ShardNode) { - return node.client ?? this.#createNodeClient(node); + case 'function': + return this.#options.nodeAddressMap(address); } - - #runningRediscoverPromise?: Promise; - - async rediscover(startWith: RedisClientType): Promise { - this.#runningRediscoverPromise ??= this.#rediscover(startWith) - .finally(() => this.#runningRediscoverPromise = undefined); - return this.#runningRediscoverPromise; + } + + #clientOptionsDefaults(options?: RedisClientOptions) { + if (!this.#options.defaults) return options; + + let socket; + if (this.#options.defaults.socket) { + socket = options?.socket ? { + ...this.#options.defaults.socket, + ...options.socket + } : this.#options.defaults.socket; + } else { + socket = options?.socket; } - async #rediscover(startWith: RedisClientType): Promise { - if (await this.#discover(startWith.options)) return; - - return this.#discoverWithRootNodes(); + return { + ...this.#options.defaults, + ...options, + socket: socket as RedisSocketOptions + }; + } + + #initiateSlotNode( + shard: NodeAddress & { id: string; }, + readonly: boolean, + eagerConnent: boolean, + addressesInUse: Set, + promises: Array> + ) { + const address = `${shard.host}:${shard.port}`; + + let node = this.nodeByAddress.get(address); + if (!node) { + node = { + ...shard, + address, + readonly, + client: undefined, + connectPromise: undefined + }; + + if (eagerConnent) { + promises.push(this.#createNodeClient(node)); + } + + this.nodeByAddress.set(address, node); } - quit(): Promise { - return this.#destroy(client => client.quit()); + if (!addressesInUse.has(address)) { + addressesInUse.add(address); + (readonly ? this.replicas : this.masters).push(node); } - disconnect(): Promise { - return this.#destroy(client => client.disconnect()); + return node; + } + + #createClient(node: ShardNode, readonly = node.readonly) { + return this.#clientFactory( + this.#clientOptionsDefaults({ + socket: this.#getNodeAddress(node.address) ?? { + host: node.host, + port: node.port + }, + readonly + }) + ).on('error', err => console.error(err)); + } + + #createNodeClient(node: ShardNode, readonly?: boolean) { + const client = node.client = this.#createClient(node, readonly); + return node.connectPromise = client.connect() + .finally(() => node.connectPromise = undefined); + } + + nodeClient(node: ShardNode) { + return ( + node.connectPromise ?? // if the node is connecting + node.client ?? // if the node is connected + this.#createNodeClient(node) // if the not is disconnected + ); + } + + #runningRediscoverPromise?: Promise; + + async rediscover(startWith: RedisClientType): Promise { + this.#runningRediscoverPromise ??= this.#rediscover(startWith) + .finally(() => this.#runningRediscoverPromise = undefined); + return this.#runningRediscoverPromise; + } + + async #rediscover(startWith: RedisClientType): Promise { + if (await this.#discover(startWith.options!)) return; + + return this.#discoverWithRootNodes(); + } + + /** + * @deprecated Use `close` instead. + */ + quit(): Promise { + return this.#destroy(client => client.quit()); + } + + /** + * @deprecated Use `destroy` instead. + */ + disconnect(): Promise { + return this.#destroy(client => client.disconnect()); + } + + close() { + return this.#destroy(client => client.close()); + } + + destroy() { + this.#isOpen = false; + + for (const client of this.#clients()) { + client.destroy(); } - async #destroy(fn: (client: RedisClientType) => Promise): Promise { - this.#isOpen = false; - - const promises = []; - for (const { master, replicas } of this.shards) { - if (master.client) { - promises.push( - this.#execOnNodeClient(master.client, fn) - ); - } - - if (master.pubSubClient) { - promises.push( - this.#execOnNodeClient(master.pubSubClient, fn) - ); - } - - if (replicas) { - for (const { client } of replicas) { - if (client) { - promises.push( - this.#execOnNodeClient(client, fn) - ); - } - } - } - } + if (this.pubSubNode) { + this.pubSubNode.client.destroy(); + this.pubSubNode = undefined; + } - if (this.pubSubNode) { - promises.push(this.#execOnNodeClient(this.pubSubNode.client, fn)); - this.pubSubNode = undefined; - } + this.#resetSlots(); + this.nodeByAddress.clear(); + } - this.#resetSlots(); - this.nodeByAddress.clear(); + *#clients() { + for (const master of this.masters) { + if (master.client) { + yield master.client; + } - await Promise.allSettled(promises); + if (master.pubSub) { + yield master.pubSub.client; + } } - #execOnNodeClient( - client: ClientOrPromise, - fn: (client: RedisClientType) => Promise - ) { - return types.isPromise(client) ? - client.then(fn) : - fn(client); + for (const replica of this.replicas) { + if (replica.client) { + yield replica.client; + } } + } - getClient( - firstKey: RedisCommandArgument | undefined, - isReadonly: boolean | undefined - ): ClientOrPromise { - if (!firstKey) { - return this.nodeClient(this.getRandomNode()); - } + async #destroy(fn: (client: RedisClientType) => Promise): Promise { + this.#isOpen = false; - const slotNumber = calculateSlot(firstKey); - if (!isReadonly) { - return this.nodeClient(this.slots[slotNumber].master); - } + const promises = []; + for (const client of this.#clients()) { + promises.push(fn(client)); + } - return this.nodeClient(this.getSlotRandomNode(slotNumber)); + if (this.pubSubNode) { + promises.push(fn(this.pubSubNode.client)); + this.pubSubNode = undefined; } - *#iterateAllNodes() { - let i = Math.floor(Math.random() * (this.masters.length + this.replicas.length)); - if (i < this.masters.length) { - do { - yield this.masters[i]; - } while (++i < this.masters.length); - - for (const replica of this.replicas) { - yield replica; - } - } else { - i -= this.masters.length; - do { - yield this.replicas[i]; - } while (++i < this.replicas.length); - } + this.#resetSlots(); + this.nodeByAddress.clear(); - while (true) { - for (const master of this.masters) { - yield master; - } + await Promise.allSettled(promises); + } - for (const replica of this.replicas) { - yield replica; - } - } + getClient( + firstKey: RedisArgument | undefined, + isReadonly: boolean | undefined + ) { + if (!firstKey) { + return this.nodeClient(this.getRandomNode()); } - #randomNodeIterator?: IterableIterator>; - - getRandomNode() { - this.#randomNodeIterator ??= this.#iterateAllNodes(); - return this.#randomNodeIterator.next().value as ShardNode; + const slotNumber = calculateSlot(firstKey); + if (!isReadonly) { + return this.nodeClient(this.slots[slotNumber].master); } - *#slotNodesIterator(slot: ShardWithReplicas) { - let i = Math.floor(Math.random() * (1 + slot.replicas.length)); - if (i < slot.replicas.length) { - do { - yield slot.replicas[i]; - } while (++i < slot.replicas.length); - } - - while (true) { - yield slot.master; - - for (const replica of slot.replicas) { - yield replica; - } - } + return this.nodeClient(this.getSlotRandomNode(slotNumber)); + } + + *#iterateAllNodes() { + let i = Math.floor(Math.random() * (this.masters.length + this.replicas.length)); + if (i < this.masters.length) { + do { + yield this.masters[i]; + } while (++i < this.masters.length); + + for (const replica of this.replicas) { + yield replica; + } + } else { + i -= this.masters.length; + do { + yield this.replicas[i]; + } while (++i < this.replicas.length); } - getSlotRandomNode(slotNumber: number) { - const slot = this.slots[slotNumber]; - if (!slot.replicas?.length) { - return slot.master; - } + while (true) { + for (const master of this.masters) { + yield master; + } - slot.nodesIterator ??= this.#slotNodesIterator(slot as ShardWithReplicas); - return slot.nodesIterator.next().value as ShardNode; + for (const replica of this.replicas) { + yield replica; + } } + } - getMasterByAddress(address: string) { - const master = this.nodeByAddress.get(address); - if (!master) return; - - return this.nodeClient(master); - } + _randomNodeIterator?: IterableIterator>; - getPubSubClient() { - return this.pubSubNode ? - this.pubSubNode.client : - this.#initiatePubSubClient(); - } + getRandomNode() { + this._randomNodeIterator ??= this.#iterateAllNodes(); + return this._randomNodeIterator.next().value as ShardNode; + } - async #initiatePubSubClient(toResubscribe?: PubSubToResubscribe) { - const index = Math.floor(Math.random() * (this.masters.length + this.replicas.length)), - node = index < this.masters.length ? - this.masters[index] : - this.replicas[index - this.masters.length]; - - this.pubSubNode = { - address: node.address, - client: this.#createClient(node, true) - .then(async client => { - if (toResubscribe) { - await Promise.all([ - client.extendPubSubListeners(PubSubType.CHANNELS, toResubscribe[PubSubType.CHANNELS]), - client.extendPubSubListeners(PubSubType.PATTERNS, toResubscribe[PubSubType.PATTERNS]) - ]); - } - - this.pubSubNode!.client = client; - return client; - }) - .catch(err => { - this.pubSubNode = undefined; - throw err; - }) - }; - - return this.pubSubNode.client as Promise>; + *#slotNodesIterator(slot: ShardWithReplicas) { + let i = Math.floor(Math.random() * (1 + slot.replicas.length)); + if (i < slot.replicas.length) { + do { + yield slot.replicas[i]; + } while (++i < slot.replicas.length); } - async executeUnsubscribeCommand( - unsubscribe: (client: RedisClientType) => Promise - ): Promise { - const client = await this.getPubSubClient(); - await unsubscribe(client); + while (true) { + yield slot.master; - if (!client.isPubSubActive && client.isOpen) { - await client.disconnect(); - this.pubSubNode = undefined; - } + for (const replica of slot.replicas) { + yield replica; + } } + } - getShardedPubSubClient(channel: string) { - const { master } = this.slots[calculateSlot(channel)]; - return master.pubSubClient ?? this.#initiateShardedPubSubClient(master); + getSlotRandomNode(slotNumber: number) { + const slot = this.slots[slotNumber]; + if (!slot.replicas?.length) { + return slot.master; } - #initiateShardedPubSubClient(master: MasterNode) { - const promise = this.#createClient(master, true) - .then(client => { - client.on('server-sunsubscribe', async (channel, listeners) => { - try { - await this.rediscover(client); - const redirectTo = await this.getShardedPubSubClient(channel); - redirectTo.extendPubSubChannelListeners( - PubSubType.SHARDED, - channel, - listeners - ); - } catch (err) { - this.#emit('sharded-shannel-moved-error', err, channel, listeners); - } - }); - - master.pubSubClient = client; - return client; - }) - .catch(err => { - master.pubSubClient = undefined; - throw err; - }); + slot.nodesIterator ??= this.#slotNodesIterator(slot as ShardWithReplicas); + return slot.nodesIterator.next().value as ShardNode; + } + + getMasterByAddress(address: string) { + const master = this.nodeByAddress.get(address); + if (!master) return; + + return this.nodeClient(master); + } + + getPubSubClient() { + if (!this.pubSubNode) return this.#initiatePubSubClient(); + + return this.pubSubNode.connectPromise ?? this.pubSubNode.client; + } + + async #initiatePubSubClient(toResubscribe?: PubSubToResubscribe) { + const index = Math.floor(Math.random() * (this.masters.length + this.replicas.length)), + node = index < this.masters.length ? + this.masters[index] : + this.replicas[index - this.masters.length], + client = this.#createClient(node, true); + + this.pubSubNode = { + address: node.address, + client, + connectPromise: client.connect() + .then(async client => { + if (toResubscribe) { + await Promise.all([ + client.extendPubSubListeners(PUBSUB_TYPE.CHANNELS, toResubscribe[PUBSUB_TYPE.CHANNELS]), + client.extendPubSubListeners(PUBSUB_TYPE.PATTERNS, toResubscribe[PUBSUB_TYPE.PATTERNS]) + ]); + } + + this.pubSubNode!.connectPromise = undefined; + return client; + }) + .catch(err => { + this.pubSubNode = undefined; + throw err; + }) + }; + + return this.pubSubNode.connectPromise!; + } + + async executeUnsubscribeCommand( + unsubscribe: (client: RedisClientType) => Promise + ): Promise { + const client = await this.getPubSubClient(); + await unsubscribe(client); + + if (!client.isPubSubActive) { + client.destroy(); + this.pubSubNode = undefined; + } + } - master.pubSubClient = promise; + getShardedPubSubClient(channel: string) { + const { master } = this.slots[calculateSlot(channel)]; + if (!master.pubSub) return this.#initiateShardedPubSubClient(master); + return master.pubSub.connectPromise ?? master.pubSub.client; + } - return promise; - } + async #initiateShardedPubSubClient(master: MasterNode) { + const client = this.#createClient(master, true) + .on('server-sunsubscribe', async (channel, listeners) => { + try { + await this.rediscover(client); + const redirectTo = await this.getShardedPubSubClient(channel); + await redirectTo.extendPubSubChannelListeners( + PUBSUB_TYPE.SHARDED, + channel, + listeners + ); + } catch (err) { + this.#emit('sharded-shannel-moved-error', err, channel, listeners); + } + }); + + master.pubSub = { + client, + connectPromise: client.connect() + .then(client => { + master.pubSub!.connectPromise = undefined; + return client; + }) + .catch(err => { + master.pubSub = undefined; + throw err; + }) + }; + + return master.pubSub.connectPromise!; + } + + async executeShardedUnsubscribeCommand( + channel: string, + unsubscribe: (client: RedisClientType) => Promise + ) { + const { master } = this.slots[calculateSlot(channel)]; + if (!master.pubSub) return; - async executeShardedUnsubscribeCommand( - channel: string, - unsubscribe: (client: RedisClientType) => Promise - ): Promise { - const { master } = this.slots[calculateSlot(channel)]; - if (!master.pubSubClient) return Promise.resolve(); + const client = master.pubSub.connectPromise ? + await master.pubSub.connectPromise : + master.pubSub.client; - const client = await master.pubSubClient; - await unsubscribe(client); + await unsubscribe(client); - if (!client.isPubSubActive && client.isOpen) { - await client.disconnect(); - master.pubSubClient = undefined; - } + if (!client.isPubSubActive) { + client.destroy(); + master.pubSub = undefined; } + } } diff --git a/packages/client/lib/cluster/commands.ts b/packages/client/lib/cluster/commands.ts deleted file mode 100644 index 9027c5c0b5e..00000000000 --- a/packages/client/lib/cluster/commands.ts +++ /dev/null @@ -1,670 +0,0 @@ - -import * as APPEND from '../commands/APPEND'; -import * as BITCOUNT from '../commands/BITCOUNT'; -import * as BITFIELD_RO from '../commands/BITFIELD_RO'; -import * as BITFIELD from '../commands/BITFIELD'; -import * as BITOP from '../commands/BITOP'; -import * as BITPOS from '../commands/BITPOS'; -import * as BLMOVE from '../commands/BLMOVE'; -import * as BLMPOP from '../commands/BLMPOP'; -import * as BLPOP from '../commands/BLPOP'; -import * as BRPOP from '../commands/BRPOP'; -import * as BRPOPLPUSH from '../commands/BRPOPLPUSH'; -import * as BZMPOP from '../commands/BZMPOP'; -import * as BZPOPMAX from '../commands/BZPOPMAX'; -import * as BZPOPMIN from '../commands/BZPOPMIN'; -import * as COPY from '../commands/COPY'; -import * as DECR from '../commands/DECR'; -import * as DECRBY from '../commands/DECRBY'; -import * as DEL from '../commands/DEL'; -import * as DUMP from '../commands/DUMP'; -import * as EVAL_RO from '../commands/EVAL_RO'; -import * as EVAL from '../commands/EVAL'; -import * as EVALSHA_RO from '../commands/EVALSHA_RO'; -import * as EVALSHA from '../commands/EVALSHA'; -import * as EXISTS from '../commands/EXISTS'; -import * as EXPIRE from '../commands/EXPIRE'; -import * as EXPIREAT from '../commands/EXPIREAT'; -import * as EXPIRETIME from '../commands/EXPIRETIME'; -import * as FCALL_RO from '../commands/FCALL_RO'; -import * as FCALL from '../commands/FCALL'; -import * as GEOADD from '../commands/GEOADD'; -import * as GEODIST from '../commands/GEODIST'; -import * as GEOHASH from '../commands/GEOHASH'; -import * as GEOPOS from '../commands/GEOPOS'; -import * as GEORADIUS_RO_WITH from '../commands/GEORADIUS_RO_WITH'; -import * as GEORADIUS_RO from '../commands/GEORADIUS_RO'; -import * as GEORADIUS_WITH from '../commands/GEORADIUS_WITH'; -import * as GEORADIUS from '../commands/GEORADIUS'; -import * as GEORADIUSBYMEMBER_RO_WITH from '../commands/GEORADIUSBYMEMBER_RO_WITH'; -import * as GEORADIUSBYMEMBER_RO from '../commands/GEORADIUSBYMEMBER_RO'; -import * as GEORADIUSBYMEMBER_WITH from '../commands/GEORADIUSBYMEMBER_WITH'; -import * as GEORADIUSBYMEMBER from '../commands/GEORADIUSBYMEMBER'; -import * as GEORADIUSBYMEMBERSTORE from '../commands/GEORADIUSBYMEMBERSTORE'; -import * as GEORADIUSSTORE from '../commands/GEORADIUSSTORE'; -import * as GEOSEARCH_WITH from '../commands/GEOSEARCH_WITH'; -import * as GEOSEARCH from '../commands/GEOSEARCH'; -import * as GEOSEARCHSTORE from '../commands/GEOSEARCHSTORE'; -import * as GET from '../commands/GET'; -import * as GETBIT from '../commands/GETBIT'; -import * as GETDEL from '../commands/GETDEL'; -import * as GETEX from '../commands/GETEX'; -import * as GETRANGE from '../commands/GETRANGE'; -import * as GETSET from '../commands/GETSET'; -import * as HDEL from '../commands/HDEL'; -import * as HEXISTS from '../commands/HEXISTS'; -import * as HEXPIRE from '../commands/HEXPIRE'; -import * as HEXPIREAT from '../commands/HEXPIREAT'; -import * as HEXPIRETIME from '../commands/HEXPIRETIME'; -import * as HGET from '../commands/HGET'; -import * as HGETALL from '../commands/HGETALL'; -import * as HINCRBY from '../commands/HINCRBY'; -import * as HINCRBYFLOAT from '../commands/HINCRBYFLOAT'; -import * as HKEYS from '../commands/HKEYS'; -import * as HLEN from '../commands/HLEN'; -import * as HMGET from '../commands/HMGET'; -import * as HPERSIST from '../commands/HPERSIST'; -import * as HPEXPIRE from '../commands/HPEXPIRE'; -import * as HPEXPIREAT from '../commands/HPEXPIREAT'; -import * as HPEXPIRETIME from '../commands/HPEXPIRETIME'; -import * as HPTTL from '../commands/HPTTL'; -import * as HRANDFIELD_COUNT_WITHVALUES from '../commands/HRANDFIELD_COUNT_WITHVALUES'; -import * as HRANDFIELD_COUNT from '../commands/HRANDFIELD_COUNT'; -import * as HRANDFIELD from '../commands/HRANDFIELD'; -import * as HSCAN from '../commands/HSCAN'; -import * as HSCAN_NOVALUES from '../commands/HSCAN_NOVALUES'; -import * as HSET from '../commands/HSET'; -import * as HSETNX from '../commands/HSETNX'; -import * as HSTRLEN from '../commands/HSTRLEN'; -import * as HTTL from '../commands/HTTL'; -import * as HVALS from '../commands/HVALS'; -import * as INCR from '../commands/INCR'; -import * as INCRBY from '../commands/INCRBY'; -import * as INCRBYFLOAT from '../commands/INCRBYFLOAT'; -import * as LCS_IDX_WITHMATCHLEN from '../commands/LCS_IDX_WITHMATCHLEN'; -import * as LCS_IDX from '../commands/LCS_IDX'; -import * as LCS_LEN from '../commands/LCS_LEN'; -import * as LCS from '../commands/LCS'; -import * as LINDEX from '../commands/LINDEX'; -import * as LINSERT from '../commands/LINSERT'; -import * as LLEN from '../commands/LLEN'; -import * as LMOVE from '../commands/LMOVE'; -import * as LMPOP from '../commands/LMPOP'; -import * as LPOP_COUNT from '../commands/LPOP_COUNT'; -import * as LPOP from '../commands/LPOP'; -import * as LPOS_COUNT from '../commands/LPOS_COUNT'; -import * as LPOS from '../commands/LPOS'; -import * as LPUSH from '../commands/LPUSH'; -import * as LPUSHX from '../commands/LPUSHX'; -import * as LRANGE from '../commands/LRANGE'; -import * as LREM from '../commands/LREM'; -import * as LSET from '../commands/LSET'; -import * as LTRIM from '../commands/LTRIM'; -import * as MGET from '../commands/MGET'; -import * as MIGRATE from '../commands/MIGRATE'; -import * as MSET from '../commands/MSET'; -import * as MSETNX from '../commands/MSETNX'; -import * as OBJECT_ENCODING from '../commands/OBJECT_ENCODING'; -import * as OBJECT_FREQ from '../commands/OBJECT_FREQ'; -import * as OBJECT_IDLETIME from '../commands/OBJECT_IDLETIME'; -import * as OBJECT_REFCOUNT from '../commands/OBJECT_REFCOUNT'; -import * as PERSIST from '../commands/PERSIST'; -import * as PEXPIRE from '../commands/PEXPIRE'; -import * as PEXPIREAT from '../commands/PEXPIREAT'; -import * as PEXPIRETIME from '../commands/PEXPIRETIME'; -import * as PFADD from '../commands/PFADD'; -import * as PFCOUNT from '../commands/PFCOUNT'; -import * as PFMERGE from '../commands/PFMERGE'; -import * as PSETEX from '../commands/PSETEX'; -import * as PTTL from '../commands/PTTL'; -import * as PUBLISH from '../commands/PUBLISH'; -import * as RENAME from '../commands/RENAME'; -import * as RENAMENX from '../commands/RENAMENX'; -import * as RESTORE from '../commands/RESTORE'; -import * as RPOP_COUNT from '../commands/RPOP_COUNT'; -import * as RPOP from '../commands/RPOP'; -import * as RPOPLPUSH from '../commands/RPOPLPUSH'; -import * as RPUSH from '../commands/RPUSH'; -import * as RPUSHX from '../commands/RPUSHX'; -import * as SADD from '../commands/SADD'; -import * as SCARD from '../commands/SCARD'; -import * as SDIFF from '../commands/SDIFF'; -import * as SDIFFSTORE from '../commands/SDIFFSTORE'; -import * as SET from '../commands/SET'; -import * as SETBIT from '../commands/SETBIT'; -import * as SETEX from '../commands/SETEX'; -import * as SETNX from '../commands/SETNX'; -import * as SETRANGE from '../commands/SETRANGE'; -import * as SINTER from '../commands/SINTER'; -import * as SINTERCARD from '../commands/SINTERCARD'; -import * as SINTERSTORE from '../commands/SINTERSTORE'; -import * as SISMEMBER from '../commands/SISMEMBER'; -import * as SMEMBERS from '../commands/SMEMBERS'; -import * as SMISMEMBER from '../commands/SMISMEMBER'; -import * as SMOVE from '../commands/SMOVE'; -import * as SORT_RO from '../commands/SORT_RO'; -import * as SORT_STORE from '../commands/SORT_STORE'; -import * as SORT from '../commands/SORT'; -import * as SPOP from '../commands/SPOP'; -import * as SPUBLISH from '../commands/SPUBLISH'; -import * as SRANDMEMBER_COUNT from '../commands/SRANDMEMBER_COUNT'; -import * as SRANDMEMBER from '../commands/SRANDMEMBER'; -import * as SREM from '../commands/SREM'; -import * as SSCAN from '../commands/SSCAN'; -import * as STRLEN from '../commands/STRLEN'; -import * as SUNION from '../commands/SUNION'; -import * as SUNIONSTORE from '../commands/SUNIONSTORE'; -import * as TOUCH from '../commands/TOUCH'; -import * as TTL from '../commands/TTL'; -import * as TYPE from '../commands/TYPE'; -import * as UNLINK from '../commands/UNLINK'; -import * as WATCH from '../commands/WATCH'; -import * as XACK from '../commands/XACK'; -import * as XADD from '../commands/XADD'; -import * as XAUTOCLAIM_JUSTID from '../commands/XAUTOCLAIM_JUSTID'; -import * as XAUTOCLAIM from '../commands/XAUTOCLAIM'; -import * as XCLAIM_JUSTID from '../commands/XCLAIM_JUSTID'; -import * as XCLAIM from '../commands/XCLAIM'; -import * as XDEL from '../commands/XDEL'; -import * as XGROUP_CREATE from '../commands/XGROUP_CREATE'; -import * as XGROUP_CREATECONSUMER from '../commands/XGROUP_CREATECONSUMER'; -import * as XGROUP_DELCONSUMER from '../commands/XGROUP_DELCONSUMER'; -import * as XGROUP_DESTROY from '../commands/XGROUP_DESTROY'; -import * as XGROUP_SETID from '../commands/XGROUP_SETID'; -import * as XINFO_CONSUMERS from '../commands/XINFO_CONSUMERS'; -import * as XINFO_GROUPS from '../commands/XINFO_GROUPS'; -import * as XINFO_STREAM from '../commands/XINFO_STREAM'; -import * as XLEN from '../commands/XLEN'; -import * as XPENDING_RANGE from '../commands/XPENDING_RANGE'; -import * as XPENDING from '../commands/XPENDING'; -import * as XRANGE from '../commands/XRANGE'; -import * as XREAD from '../commands/XREAD'; -import * as XREADGROUP from '../commands/XREADGROUP'; -import * as XREVRANGE from '../commands/XREVRANGE'; -import * as XSETID from '../commands/XSETID'; -import * as XTRIM from '../commands/XTRIM'; -import * as ZADD from '../commands/ZADD'; -import * as ZCARD from '../commands/ZCARD'; -import * as ZCOUNT from '../commands/ZCOUNT'; -import * as ZDIFF_WITHSCORES from '../commands/ZDIFF_WITHSCORES'; -import * as ZDIFF from '../commands/ZDIFF'; -import * as ZDIFFSTORE from '../commands/ZDIFFSTORE'; -import * as ZINCRBY from '../commands/ZINCRBY'; -import * as ZINTER_WITHSCORES from '../commands/ZINTER_WITHSCORES'; -import * as ZINTER from '../commands/ZINTER'; -import * as ZINTERCARD from '../commands/ZINTERCARD'; -import * as ZINTERSTORE from '../commands/ZINTERSTORE'; -import * as ZLEXCOUNT from '../commands/ZLEXCOUNT'; -import * as ZMPOP from '../commands/ZMPOP'; -import * as ZMSCORE from '../commands/ZMSCORE'; -import * as ZPOPMAX_COUNT from '../commands/ZPOPMAX_COUNT'; -import * as ZPOPMAX from '../commands/ZPOPMAX'; -import * as ZPOPMIN_COUNT from '../commands/ZPOPMIN_COUNT'; -import * as ZPOPMIN from '../commands/ZPOPMIN'; -import * as ZRANDMEMBER_COUNT_WITHSCORES from '../commands/ZRANDMEMBER_COUNT_WITHSCORES'; -import * as ZRANDMEMBER_COUNT from '../commands/ZRANDMEMBER_COUNT'; -import * as ZRANDMEMBER from '../commands/ZRANDMEMBER'; -import * as ZRANGE_WITHSCORES from '../commands/ZRANGE_WITHSCORES'; -import * as ZRANGE from '../commands/ZRANGE'; -import * as ZRANGEBYLEX from '../commands/ZRANGEBYLEX'; -import * as ZRANGEBYSCORE_WITHSCORES from '../commands/ZRANGEBYSCORE_WITHSCORES'; -import * as ZRANGEBYSCORE from '../commands/ZRANGEBYSCORE'; -import * as ZRANGESTORE from '../commands/ZRANGESTORE'; -import * as ZRANK from '../commands/ZRANK'; -import * as ZREM from '../commands/ZREM'; -import * as ZREMRANGEBYLEX from '../commands/ZREMRANGEBYLEX'; -import * as ZREMRANGEBYRANK from '../commands/ZREMRANGEBYRANK'; -import * as ZREMRANGEBYSCORE from '../commands/ZREMRANGEBYSCORE'; -import * as ZREVRANK from '../commands/ZREVRANK'; -import * as ZSCAN from '../commands/ZSCAN'; -import * as ZSCORE from '../commands/ZSCORE'; -import * as ZUNION_WITHSCORES from '../commands/ZUNION_WITHSCORES'; -import * as ZUNION from '../commands/ZUNION'; -import * as ZUNIONSTORE from '../commands/ZUNIONSTORE'; - -export default { - APPEND, - append: APPEND, - BITCOUNT, - bitCount: BITCOUNT, - BITFIELD_RO, - bitFieldRo: BITFIELD_RO, - BITFIELD, - bitField: BITFIELD, - BITOP, - bitOp: BITOP, - BITPOS, - bitPos: BITPOS, - BLMOVE, - blMove: BLMOVE, - BLMPOP, - blmPop: BLMPOP, - BLPOP, - blPop: BLPOP, - BRPOP, - brPop: BRPOP, - BRPOPLPUSH, - brPopLPush: BRPOPLPUSH, - BZMPOP, - bzmPop: BZMPOP, - BZPOPMAX, - bzPopMax: BZPOPMAX, - BZPOPMIN, - bzPopMin: BZPOPMIN, - COPY, - copy: COPY, - DECR, - decr: DECR, - DECRBY, - decrBy: DECRBY, - DEL, - del: DEL, - DUMP, - dump: DUMP, - EVAL_RO, - evalRo: EVAL_RO, - EVAL, - eval: EVAL, - EVALSHA, - evalSha: EVALSHA, - EVALSHA_RO, - evalShaRo: EVALSHA_RO, - EXISTS, - exists: EXISTS, - EXPIRE, - expire: EXPIRE, - EXPIREAT, - expireAt: EXPIREAT, - EXPIRETIME, - expireTime: EXPIRETIME, - FCALL_RO, - fCallRo: FCALL_RO, - FCALL, - fCall: FCALL, - GEOADD, - geoAdd: GEOADD, - GEODIST, - geoDist: GEODIST, - GEOHASH, - geoHash: GEOHASH, - GEOPOS, - geoPos: GEOPOS, - GEORADIUS_RO_WITH, - geoRadiusRoWith: GEORADIUS_RO_WITH, - GEORADIUS_RO, - geoRadiusRo: GEORADIUS_RO, - GEORADIUS_WITH, - geoRadiusWith: GEORADIUS_WITH, - GEORADIUS, - geoRadius: GEORADIUS, - GEORADIUSBYMEMBER_RO_WITH, - geoRadiusByMemberRoWith: GEORADIUSBYMEMBER_RO_WITH, - GEORADIUSBYMEMBER_RO, - geoRadiusByMemberRo: GEORADIUSBYMEMBER_RO, - GEORADIUSBYMEMBER_WITH, - geoRadiusByMemberWith: GEORADIUSBYMEMBER_WITH, - GEORADIUSBYMEMBER, - geoRadiusByMember: GEORADIUSBYMEMBER, - GEORADIUSBYMEMBERSTORE, - geoRadiusByMemberStore: GEORADIUSBYMEMBERSTORE, - GEORADIUSSTORE, - geoRadiusStore: GEORADIUSSTORE, - GEOSEARCH_WITH, - geoSearchWith: GEOSEARCH_WITH, - GEOSEARCH, - geoSearch: GEOSEARCH, - GEOSEARCHSTORE, - geoSearchStore: GEOSEARCHSTORE, - GET, - get: GET, - GETBIT, - getBit: GETBIT, - GETDEL, - getDel: GETDEL, - GETEX, - getEx: GETEX, - GETRANGE, - getRange: GETRANGE, - GETSET, - getSet: GETSET, - HDEL, - hDel: HDEL, - HEXISTS, - hExists: HEXISTS, - HEXPIRE, - hExpire: HEXPIRE, - HEXPIREAT, - hExpireAt: HEXPIREAT, - HEXPIRETIME, - hExpireTime: HEXPIRETIME, - HGET, - hGet: HGET, - HGETALL, - hGetAll: HGETALL, - HINCRBY, - hIncrBy: HINCRBY, - HINCRBYFLOAT, - hIncrByFloat: HINCRBYFLOAT, - HKEYS, - hKeys: HKEYS, - HLEN, - hLen: HLEN, - HMGET, - hmGet: HMGET, - HPERSIST, - hPersist: HPERSIST, - HPEXPIRE, - hpExpire: HPEXPIRE, - HPEXPIREAT, - hpExpireAt: HPEXPIREAT, - HPEXPIRETIME, - hpExpireTime: HPEXPIRETIME, - HPTTL, - hpTTL: HPTTL, - HRANDFIELD_COUNT_WITHVALUES, - hRandFieldCountWithValues: HRANDFIELD_COUNT_WITHVALUES, - HRANDFIELD_COUNT, - hRandFieldCount: HRANDFIELD_COUNT, - HRANDFIELD, - hRandField: HRANDFIELD, - HSCAN, - hScan: HSCAN, - HSCAN_NOVALUES, - hScanNoValues: HSCAN_NOVALUES, - HSET, - hSet: HSET, - HSETNX, - hSetNX: HSETNX, - HSTRLEN, - hStrLen: HSTRLEN, - HTTL, - hTTL: HTTL, - HVALS, - hVals: HVALS, - INCR, - incr: INCR, - INCRBY, - incrBy: INCRBY, - INCRBYFLOAT, - incrByFloat: INCRBYFLOAT, - LCS_IDX_WITHMATCHLEN, - lcsIdxWithMatchLen: LCS_IDX_WITHMATCHLEN, - LCS_IDX, - lcsIdx: LCS_IDX, - LCS_LEN, - lcsLen: LCS_LEN, - LCS, - lcs: LCS, - LINDEX, - lIndex: LINDEX, - LINSERT, - lInsert: LINSERT, - LLEN, - lLen: LLEN, - LMOVE, - lMove: LMOVE, - LMPOP, - lmPop: LMPOP, - LPOP_COUNT, - lPopCount: LPOP_COUNT, - LPOP, - lPop: LPOP, - LPOS_COUNT, - lPosCount: LPOS_COUNT, - LPOS, - lPos: LPOS, - LPUSH, - lPush: LPUSH, - LPUSHX, - lPushX: LPUSHX, - LRANGE, - lRange: LRANGE, - LREM, - lRem: LREM, - LSET, - lSet: LSET, - LTRIM, - lTrim: LTRIM, - MGET, - mGet: MGET, - MIGRATE, - migrate: MIGRATE, - MSET, - mSet: MSET, - MSETNX, - mSetNX: MSETNX, - OBJECT_ENCODING, - objectEncoding: OBJECT_ENCODING, - OBJECT_FREQ, - objectFreq: OBJECT_FREQ, - OBJECT_IDLETIME, - objectIdleTime: OBJECT_IDLETIME, - OBJECT_REFCOUNT, - objectRefCount: OBJECT_REFCOUNT, - PERSIST, - persist: PERSIST, - PEXPIRE, - pExpire: PEXPIRE, - PEXPIREAT, - pExpireAt: PEXPIREAT, - PEXPIRETIME, - pExpireTime: PEXPIRETIME, - PFADD, - pfAdd: PFADD, - PFCOUNT, - pfCount: PFCOUNT, - PFMERGE, - pfMerge: PFMERGE, - PSETEX, - pSetEx: PSETEX, - PTTL, - pTTL: PTTL, - PUBLISH, - publish: PUBLISH, - RENAME, - rename: RENAME, - RENAMENX, - renameNX: RENAMENX, - RESTORE, - restore: RESTORE, - RPOP_COUNT, - rPopCount: RPOP_COUNT, - RPOP, - rPop: RPOP, - RPOPLPUSH, - rPopLPush: RPOPLPUSH, - RPUSH, - rPush: RPUSH, - RPUSHX, - rPushX: RPUSHX, - SADD, - sAdd: SADD, - SCARD, - sCard: SCARD, - SDIFF, - sDiff: SDIFF, - SDIFFSTORE, - sDiffStore: SDIFFSTORE, - SINTER, - sInter: SINTER, - SINTERCARD, - sInterCard: SINTERCARD, - SINTERSTORE, - sInterStore: SINTERSTORE, - SET, - set: SET, - SETBIT, - setBit: SETBIT, - SETEX, - setEx: SETEX, - SETNX, - setNX: SETNX, - SETRANGE, - setRange: SETRANGE, - SISMEMBER, - sIsMember: SISMEMBER, - SMEMBERS, - sMembers: SMEMBERS, - SMISMEMBER, - smIsMember: SMISMEMBER, - SMOVE, - sMove: SMOVE, - SORT_RO, - sortRo: SORT_RO, - SORT_STORE, - sortStore: SORT_STORE, - SORT, - sort: SORT, - SPOP, - sPop: SPOP, - SPUBLISH, - sPublish: SPUBLISH, - SRANDMEMBER_COUNT, - sRandMemberCount: SRANDMEMBER_COUNT, - SRANDMEMBER, - sRandMember: SRANDMEMBER, - SREM, - sRem: SREM, - SSCAN, - sScan: SSCAN, - STRLEN, - strLen: STRLEN, - SUNION, - sUnion: SUNION, - SUNIONSTORE, - sUnionStore: SUNIONSTORE, - TOUCH, - touch: TOUCH, - TTL, - ttl: TTL, - TYPE, - type: TYPE, - UNLINK, - unlink: UNLINK, - WATCH, - watch: WATCH, - XACK, - xAck: XACK, - XADD, - xAdd: XADD, - XAUTOCLAIM_JUSTID, - xAutoClaimJustId: XAUTOCLAIM_JUSTID, - XAUTOCLAIM, - xAutoClaim: XAUTOCLAIM, - XCLAIM, - xClaim: XCLAIM, - XCLAIM_JUSTID, - xClaimJustId: XCLAIM_JUSTID, - XDEL, - xDel: XDEL, - XGROUP_CREATE, - xGroupCreate: XGROUP_CREATE, - XGROUP_CREATECONSUMER, - xGroupCreateConsumer: XGROUP_CREATECONSUMER, - XGROUP_DELCONSUMER, - xGroupDelConsumer: XGROUP_DELCONSUMER, - XGROUP_DESTROY, - xGroupDestroy: XGROUP_DESTROY, - XGROUP_SETID, - xGroupSetId: XGROUP_SETID, - XINFO_CONSUMERS, - xInfoConsumers: XINFO_CONSUMERS, - XINFO_GROUPS, - xInfoGroups: XINFO_GROUPS, - XINFO_STREAM, - xInfoStream: XINFO_STREAM, - XLEN, - xLen: XLEN, - XPENDING_RANGE, - xPendingRange: XPENDING_RANGE, - XPENDING, - xPending: XPENDING, - XRANGE, - xRange: XRANGE, - XREAD, - xRead: XREAD, - XREADGROUP, - xReadGroup: XREADGROUP, - XREVRANGE, - xRevRange: XREVRANGE, - XSETID, - xSetId: XSETID, - XTRIM, - xTrim: XTRIM, - ZADD, - zAdd: ZADD, - ZCARD, - zCard: ZCARD, - ZCOUNT, - zCount: ZCOUNT, - ZDIFF_WITHSCORES, - zDiffWithScores: ZDIFF_WITHSCORES, - ZDIFF, - zDiff: ZDIFF, - ZDIFFSTORE, - zDiffStore: ZDIFFSTORE, - ZINCRBY, - zIncrBy: ZINCRBY, - ZINTER_WITHSCORES, - zInterWithScores: ZINTER_WITHSCORES, - ZINTER, - zInter: ZINTER, - ZINTERCARD, - zInterCard: ZINTERCARD, - ZINTERSTORE, - zInterStore: ZINTERSTORE, - ZLEXCOUNT, - zLexCount: ZLEXCOUNT, - ZMPOP, - zmPop: ZMPOP, - ZMSCORE, - zmScore: ZMSCORE, - ZPOPMAX_COUNT, - zPopMaxCount: ZPOPMAX_COUNT, - ZPOPMAX, - zPopMax: ZPOPMAX, - ZPOPMIN_COUNT, - zPopMinCount: ZPOPMIN_COUNT, - ZPOPMIN, - zPopMin: ZPOPMIN, - ZRANDMEMBER_COUNT_WITHSCORES, - zRandMemberCountWithScores: ZRANDMEMBER_COUNT_WITHSCORES, - ZRANDMEMBER_COUNT, - zRandMemberCount: ZRANDMEMBER_COUNT, - ZRANDMEMBER, - zRandMember: ZRANDMEMBER, - ZRANGE_WITHSCORES, - zRangeWithScores: ZRANGE_WITHSCORES, - ZRANGE, - zRange: ZRANGE, - ZRANGEBYLEX, - zRangeByLex: ZRANGEBYLEX, - ZRANGEBYSCORE_WITHSCORES, - zRangeByScoreWithScores: ZRANGEBYSCORE_WITHSCORES, - ZRANGEBYSCORE, - zRangeByScore: ZRANGEBYSCORE, - ZRANGESTORE, - zRangeStore: ZRANGESTORE, - ZRANK, - zRank: ZRANK, - ZREM, - zRem: ZREM, - ZREMRANGEBYLEX, - zRemRangeByLex: ZREMRANGEBYLEX, - ZREMRANGEBYRANK, - zRemRangeByRank: ZREMRANGEBYRANK, - ZREMRANGEBYSCORE, - zRemRangeByScore: ZREMRANGEBYSCORE, - ZREVRANK, - zRevRank: ZREVRANK, - ZSCAN, - zScan: ZSCAN, - ZSCORE, - zScore: ZSCORE, - ZUNION_WITHSCORES, - zUnionWithScores: ZUNION_WITHSCORES, - ZUNION, - zUnion: ZUNION, - ZUNIONSTORE, - zUnionStore: ZUNIONSTORE -}; diff --git a/packages/client/lib/cluster/index.spec.ts b/packages/client/lib/cluster/index.spec.ts index 569d716272a..4db5f32e853 100644 --- a/packages/client/lib/cluster/index.spec.ts +++ b/packages/client/lib/cluster/index.spec.ts @@ -1,389 +1,342 @@ -import { strict as assert } from 'assert'; +import { strict as assert } from 'node:assert'; import testUtils, { GLOBAL, waitTillBeenCalled } from '../test-utils'; import RedisCluster from '.'; -import { ClusterSlotStates } from '../commands/CLUSTER_SETSLOT'; -import { commandOptions } from '../command-options'; import { SQUARE_SCRIPT } from '../client/index.spec'; import { RootNodesUnavailableError } from '../errors'; import { spy } from 'sinon'; -import { promiseTimeout } from '../utils'; import RedisClient from '../client'; describe('Cluster', () => { - testUtils.testWithCluster('sendCommand', async cluster => { - assert.equal( - await cluster.sendCommand(undefined, true, ['PING']), - 'PONG' - ); - }, GLOBAL.CLUSTERS.OPEN); + testUtils.testWithCluster('sendCommand', async cluster => { + assert.equal( + await cluster.sendCommand(undefined, true, ['PING']), + 'PONG' + ); + }, GLOBAL.CLUSTERS.OPEN); + + testUtils.testWithCluster('isOpen', async cluster => { + assert.equal(cluster.isOpen, true); + await cluster.destroy(); + assert.equal(cluster.isOpen, false); + }, GLOBAL.CLUSTERS.OPEN); + + testUtils.testWithCluster('connect should throw if already connected', async cluster => { + await assert.rejects(cluster.connect()); + }, GLOBAL.CLUSTERS.OPEN); + + testUtils.testWithCluster('multi', async cluster => { + const key = 'key'; + assert.deepEqual( + await cluster.multi() + .set(key, 'value') + .get(key) + .exec(), + ['OK', 'value'] + ); + }, GLOBAL.CLUSTERS.OPEN); + + testUtils.testWithCluster('scripts', async cluster => { + const [, reply] = await Promise.all([ + cluster.set('key', '2'), + cluster.square('key') + ]); + + assert.equal(reply, 4); + }, { + ...GLOBAL.CLUSTERS.OPEN, + clusterConfiguration: { + scripts: { + square: SQUARE_SCRIPT + } + } + }); + + it('should throw RootNodesUnavailableError', async () => { + const cluster = RedisCluster.create({ + rootNodes: [] + }); - testUtils.testWithCluster('isOpen', async cluster => { - assert.equal(cluster.isOpen, true); - await cluster.disconnect(); - assert.equal(cluster.isOpen, false); - }, GLOBAL.CLUSTERS.OPEN); + try { + await assert.rejects( + cluster.connect(), + RootNodesUnavailableError + ); + } catch (err) { + await cluster.disconnect(); + throw err; + } + }); + + testUtils.testWithCluster('should handle live resharding', async cluster => { + const slot = 12539, + key = 'key', + value = 'value'; + await cluster.set(key, value); + + const importing = cluster.slots[0].master, + migrating = cluster.slots[slot].master, + [importingClient, migratingClient] = await Promise.all([ + cluster.nodeClient(importing), + cluster.nodeClient(migrating) + ]); + + await Promise.all([ + importingClient.clusterSetSlot(slot, 'IMPORTING', migrating.id), + migratingClient.clusterSetSlot(slot, 'MIGRATING', importing.id) + ]); + + // should be able to get the key from the migrating node + assert.equal( + await cluster.get(key), + value + ); + + await migratingClient.migrate( + importing.host, + importing.port, + key, + 0, + 10 + ); + + // should be able to get the key from the importing node using `ASKING` + assert.equal( + await cluster.get(key), + value + ); + + await Promise.all([ + importingClient.clusterSetSlot(slot, 'NODE', importing.id), + migratingClient.clusterSetSlot(slot, 'NODE', importing.id), + ]); + + // should handle `MOVED` errors + assert.equal( + await cluster.get(key), + value + ); + }, { + serverArguments: [], + numberOfMasters: 2 + }); + + testUtils.testWithCluster('getRandomNode should spread the the load evenly', async cluster => { + const totalNodes = cluster.masters.length + cluster.replicas.length, + ids = new Set(); + for (let i = 0; i < totalNodes; i++) { + ids.add(cluster.getRandomNode().id); + } + + assert.equal(ids.size, totalNodes); + }, GLOBAL.CLUSTERS.WITH_REPLICAS); + + testUtils.testWithCluster('getSlotRandomNode should spread the the load evenly', async cluster => { + const totalNodes = 1 + cluster.slots[0].replicas!.length, + ids = new Set(); + for (let i = 0; i < totalNodes; i++) { + ids.add(cluster.getSlotRandomNode(0).id); + } + + assert.equal(ids.size, totalNodes); + }, GLOBAL.CLUSTERS.WITH_REPLICAS); + + testUtils.testWithCluster('cluster topology', async cluster => { + assert.equal(cluster.slots.length, 16384); + const { numberOfMasters, numberOfReplicas } = GLOBAL.CLUSTERS.WITH_REPLICAS; + assert.equal(cluster.masters.length, numberOfMasters); + assert.equal(cluster.replicas.length, numberOfReplicas * numberOfMasters); + assert.equal(cluster.nodeByAddress.size, numberOfMasters + numberOfMasters * numberOfReplicas); + }, GLOBAL.CLUSTERS.WITH_REPLICAS); + + testUtils.testWithCluster('getMasters should be backwards competiable (without `minimizeConnections`)', async cluster => { + const masters = cluster.getMasters(); + assert.ok(Array.isArray(masters)); + for (const master of masters) { + assert.equal(typeof master.id, 'string'); + assert.ok(master.client instanceof RedisClient); + } + }, { + ...GLOBAL.CLUSTERS.OPEN, + clusterConfiguration: { + minimizeConnections: undefined // reset to default + } + }); + + testUtils.testWithCluster('getSlotMaster should be backwards competiable (without `minimizeConnections`)', async cluster => { + const master = cluster.getSlotMaster(0); + assert.equal(typeof master.id, 'string'); + assert.ok(master.client instanceof RedisClient); + }, { + ...GLOBAL.CLUSTERS.OPEN, + clusterConfiguration: { + minimizeConnections: undefined // reset to default + } + }); + + testUtils.testWithCluster('should throw CROSSSLOT error', async cluster => { + await assert.rejects(cluster.mGet(['a', 'b'])); + }, GLOBAL.CLUSTERS.OPEN); + + describe('minimizeConnections', () => { + testUtils.testWithCluster('false', async cluster => { + for (const master of cluster.masters) { + assert.ok(master.client instanceof RedisClient); + } + }, { + ...GLOBAL.CLUSTERS.OPEN, + clusterConfiguration: { + minimizeConnections: false + } + }); - testUtils.testWithCluster('connect should throw if already connected', async cluster => { - await assert.rejects(cluster.connect()); - }, GLOBAL.CLUSTERS.OPEN); + testUtils.testWithCluster('true', async cluster => { + for (const master of cluster.masters) { + assert.equal(master.client, undefined); + } + }, { + ...GLOBAL.CLUSTERS.OPEN, + clusterConfiguration: { + minimizeConnections: true + } + }); + }); - testUtils.testWithCluster('multi', async cluster => { - const key = 'key'; - assert.deepEqual( - await cluster.multi() - .set(key, 'value') - .get(key) - .exec(), - ['OK', 'value'] - ); + describe('PubSub', () => { + testUtils.testWithCluster('subscribe & unsubscribe', async cluster => { + const listener = spy(); + + await cluster.subscribe('channel', listener); + + await Promise.all([ + waitTillBeenCalled(listener), + cluster.publish('channel', 'message') + ]); + + assert.ok(listener.calledOnceWithExactly('message', 'channel')); + + await cluster.unsubscribe('channel', listener); + + assert.equal(cluster.pubSubNode, undefined); }, GLOBAL.CLUSTERS.OPEN); - testUtils.testWithCluster('scripts', async cluster => { - assert.equal( - await cluster.square(2), - 4 - ); - }, { - ...GLOBAL.CLUSTERS.OPEN, - clusterConfiguration: { - scripts: { - square: SQUARE_SCRIPT - } - } - }); + testUtils.testWithCluster('psubscribe & punsubscribe', async cluster => { + const listener = spy(); - it('should throw RootNodesUnavailableError', async () => { - const cluster = RedisCluster.create({ - rootNodes: [] - }); - - try { - await assert.rejects( - cluster.connect(), - RootNodesUnavailableError - ); - } catch (err) { - await cluster.disconnect(); - throw err; - } - }); + await cluster.pSubscribe('channe*', listener); - testUtils.testWithCluster('should handle live resharding', async cluster => { - const slot = 12539, - key = 'key', - value = 'value'; - await cluster.set(key, value); - - const importing = cluster.slots[0].master, - migrating = cluster.slots[slot].master, - [ importingClient, migratingClient ] = await Promise.all([ - cluster.nodeClient(importing), - cluster.nodeClient(migrating) - ]); - - await Promise.all([ - importingClient.clusterSetSlot(slot, ClusterSlotStates.IMPORTING, migrating.id), - migratingClient.clusterSetSlot(slot, ClusterSlotStates.MIGRATING, importing.id) - ]); + await Promise.all([ + waitTillBeenCalled(listener), + cluster.publish('channel', 'message') + ]); - // should be able to get the key from the migrating node - assert.equal( - await cluster.get(key), - value - ); + assert.ok(listener.calledOnceWithExactly('message', 'channel')); - await migratingClient.migrate( - importing.host, - importing.port, - key, - 0, - 10 - ); + await cluster.pUnsubscribe('channe*', listener); - // should be able to get the key from the importing node using `ASKING` - assert.equal( - await cluster.get(key), - value - ); + assert.equal(cluster.pubSubNode, undefined); + }, GLOBAL.CLUSTERS.OPEN); - await Promise.all([ - importingClient.clusterSetSlot(slot, ClusterSlotStates.NODE, importing.id), - migratingClient.clusterSetSlot(slot, ClusterSlotStates.NODE, importing.id), + testUtils.testWithCluster('should move listeners when PubSub node disconnects from the cluster', async cluster => { + const listener = spy(); + await cluster.subscribe('channel', listener); + + assert.ok(cluster.pubSubNode); + const [migrating, importing] = cluster.masters[0].address === cluster.pubSubNode.address ? + cluster.masters : + [cluster.masters[1], cluster.masters[0]], + [migratingClient, importingClient] = await Promise.all([ + cluster.nodeClient(migrating), + cluster.nodeClient(importing) ]); - // should handle `MOVED` errors - assert.equal( - await cluster.get(key), - value + const range = cluster.slots[0].master === migrating ? { + key: 'bar', // 5061 + start: 0, + end: 8191 + } : { + key: 'foo', // 12182 + start: 8192, + end: 16383 + }; + + // TODO: is there a better way to migrate slots without causing CLUSTERDOWN? + const promises: Array> = []; + for (let i = range.start; i <= range.end; i++) { + promises.push( + migratingClient.clusterSetSlot(i, 'NODE', importing.id), + importingClient.clusterSetSlot(i, 'NODE', importing.id) ); - }, { - serverArguments: [], - numberOfMasters: 2 - }); + } + await Promise.all(promises); - testUtils.testWithCluster('getRandomNode should spread the the load evenly', async cluster => { - const totalNodes = cluster.masters.length + cluster.replicas.length, - ids = new Set(); - for (let i = 0; i < totalNodes; i++) { - ids.add(cluster.getRandomNode().id); - } - - assert.equal(ids.size, totalNodes); - }, GLOBAL.CLUSTERS.WITH_REPLICAS); - - testUtils.testWithCluster('getSlotRandomNode should spread the the load evenly', async cluster => { - const totalNodes = 1 + cluster.slots[0].replicas!.length, - ids = new Set(); - for (let i = 0; i < totalNodes; i++) { - ids.add(cluster.getSlotRandomNode(0).id); - } - - assert.equal(ids.size, totalNodes); - }, GLOBAL.CLUSTERS.WITH_REPLICAS); - - testUtils.testWithCluster('cluster topology', async cluster => { - assert.equal(cluster.slots.length, 16384); - const { numberOfMasters, numberOfReplicas } = GLOBAL.CLUSTERS.WITH_REPLICAS; - assert.equal(cluster.shards.length, numberOfMasters); - assert.equal(cluster.masters.length, numberOfMasters); - assert.equal(cluster.replicas.length, numberOfReplicas * numberOfMasters); - assert.equal(cluster.nodeByAddress.size, numberOfMasters + numberOfMasters * numberOfReplicas); - }, GLOBAL.CLUSTERS.WITH_REPLICAS); - - testUtils.testWithCluster('getMasters should be backwards competiable (without `minimizeConnections`)', async cluster => { - const masters = cluster.getMasters(); - assert.ok(Array.isArray(masters)); - for (const master of masters) { - assert.equal(typeof master.id, 'string'); - assert.ok(master.client instanceof RedisClient); - } - }, { - ...GLOBAL.CLUSTERS.OPEN, - clusterConfiguration: { - minimizeConnections: undefined // reset to default - } - }); + // make sure to cause `MOVED` error + await cluster.get(range.key); - testUtils.testWithCluster('getSlotMaster should be backwards competiable (without `minimizeConnections`)', async cluster => { - const master = cluster.getSlotMaster(0); - assert.equal(typeof master.id, 'string'); - assert.ok(master.client instanceof RedisClient); + await Promise.all([ + cluster.publish('channel', 'message'), + waitTillBeenCalled(listener) + ]); + + assert.ok(listener.calledOnceWithExactly('message', 'channel')); }, { - ...GLOBAL.CLUSTERS.OPEN, - clusterConfiguration: { - minimizeConnections: undefined // reset to default - } + serverArguments: [], + numberOfMasters: 2, + minimumDockerVersion: [7] }); - testUtils.testWithCluster('should throw CROSSSLOT error', async cluster => { - await assert.rejects(cluster.mGet(['a', 'b'])); - }, GLOBAL.CLUSTERS.OPEN); + testUtils.testWithCluster('ssubscribe & sunsubscribe', async cluster => { + const listener = spy(); - testUtils.testWithCluster('should send commands with commandOptions to correct cluster slot (without redirections)', async cluster => { - // 'a' and 'b' hash to different cluster slots (see previous unit test) - // -> maxCommandRedirections 0: rejects on MOVED/ASK reply - await cluster.set(commandOptions({ isolated: true }), 'a', '1'), - await cluster.set(commandOptions({ isolated: true }), 'b', '2'), + await cluster.sSubscribe('channel', listener); - assert.equal(await cluster.get('a'), '1'); - assert.equal(await cluster.get('b'), '2'); + await Promise.all([ + waitTillBeenCalled(listener), + cluster.sPublish('channel', 'message') + ]); + + assert.ok(listener.calledOnceWithExactly('message', 'channel')); + + await cluster.sUnsubscribe('channel', listener); + + // 10328 is the slot of `channel` + assert.equal(cluster.slots[10328].master.pubSub, undefined); }, { - ...GLOBAL.CLUSTERS.OPEN, - clusterConfiguration: { - maxCommandRedirections: 0 - } + ...GLOBAL.CLUSTERS.OPEN, + minimumDockerVersion: [7] }); - describe('minimizeConnections', () => { - testUtils.testWithCluster('false', async cluster => { - for (const master of cluster.masters) { - assert.ok(master.client instanceof RedisClient); - } - }, { - ...GLOBAL.CLUSTERS.OPEN, - clusterConfiguration: { - minimizeConnections: false - } - }); - - testUtils.testWithCluster('true', async cluster => { - for (const master of cluster.masters) { - assert.equal(master.client, undefined); - } - }, { - ...GLOBAL.CLUSTERS.OPEN, - clusterConfiguration: { - minimizeConnections: true - } - }); - }); + testUtils.testWithCluster('should handle sharded-channel-moved events', async cluster => { + const SLOT = 10328, + migrating = cluster.slots[SLOT].master, + importing = cluster.masters.find(master => master !== migrating)!, + [migratingClient, importingClient] = await Promise.all([ + cluster.nodeClient(migrating), + cluster.nodeClient(importing) + ]); - describe('PubSub', () => { - testUtils.testWithCluster('subscribe & unsubscribe', async cluster => { - const listener = spy(); - - await cluster.subscribe('channel', listener); - - await Promise.all([ - waitTillBeenCalled(listener), - cluster.publish('channel', 'message') - ]); - - assert.ok(listener.calledOnceWithExactly('message', 'channel')); - - await cluster.unsubscribe('channel', listener); - - assert.equal(cluster.pubSubNode, undefined); - }, GLOBAL.CLUSTERS.OPEN); - - testUtils.testWithCluster('concurrent UNSUBSCRIBE does not throw an error (#2685)', async cluster => { - const listener = spy(); - await Promise.all([ - cluster.subscribe('1', listener), - cluster.subscribe('2', listener) - ]); - await Promise.all([ - cluster.unsubscribe('1', listener), - cluster.unsubscribe('2', listener) - ]); - }, GLOBAL.CLUSTERS.OPEN); - - testUtils.testWithCluster('psubscribe & punsubscribe', async cluster => { - const listener = spy(); - - await cluster.pSubscribe('channe*', listener); - - await Promise.all([ - waitTillBeenCalled(listener), - cluster.publish('channel', 'message') - ]); - - assert.ok(listener.calledOnceWithExactly('message', 'channel')); - - await cluster.pUnsubscribe('channe*', listener); - - assert.equal(cluster.pubSubNode, undefined); - }, GLOBAL.CLUSTERS.OPEN); - - testUtils.testWithCluster('should move listeners when PubSub node disconnects from the cluster', async cluster => { - const listener = spy(); - await cluster.subscribe('channel', listener); - - assert.ok(cluster.pubSubNode); - const [ migrating, importing ] = cluster.masters[0].address === cluster.pubSubNode.address ? - cluster.masters : - [cluster.masters[1], cluster.masters[0]], - [ migratingClient, importingClient ] = await Promise.all([ - cluster.nodeClient(migrating), - cluster.nodeClient(importing) - ]); - - const range = cluster.slots[0].master === migrating ? { - key: 'bar', // 5061 - start: 0, - end: 8191 - } : { - key: 'foo', // 12182 - start: 8192, - end: 16383 - }; - - await Promise.all([ - migratingClient.clusterDelSlotsRange(range), - importingClient.clusterDelSlotsRange(range), - importingClient.clusterAddSlotsRange(range) - ]); - - // wait for migrating node to be notified about the new topology - while ((await migratingClient.clusterInfo()).state !== 'ok') { - await promiseTimeout(50); - } - - // make sure to cause `MOVED` error - await cluster.get(range.key); - - await Promise.all([ - cluster.publish('channel', 'message'), - waitTillBeenCalled(listener) - ]); - - assert.ok(listener.calledOnceWithExactly('message', 'channel')); - }, { - serverArguments: [], - numberOfMasters: 2, - minimumDockerVersion: [7] - }); - - testUtils.testWithCluster('ssubscribe & sunsubscribe', async cluster => { - const listener = spy(); - - await cluster.sSubscribe('channel', listener); - - await Promise.all([ - waitTillBeenCalled(listener), - cluster.sPublish('channel', 'message') - ]); - - assert.ok(listener.calledOnceWithExactly('message', 'channel')); - - await cluster.sUnsubscribe('channel', listener); - - // 10328 is the slot of `channel` - assert.equal(cluster.slots[10328].master.pubSubClient, undefined); - }, { - ...GLOBAL.CLUSTERS.OPEN, - minimumDockerVersion: [7] - }); - - testUtils.testWithCluster('concurrent SUNSUBCRIBE does not throw an error (#2685)', async cluster => { - const listener = spy(); - await Promise.all([ - await cluster.sSubscribe('1', listener), - await cluster.sSubscribe('2', listener) - ]); - await Promise.all([ - cluster.sUnsubscribe('1', listener), - cluster.sUnsubscribe('2', listener) - ]); - }, { - ...GLOBAL.CLUSTERS.OPEN, - minimumDockerVersion: [7] - }); - - testUtils.testWithCluster('should handle sharded-channel-moved events', async cluster => { - const SLOT = 10328, - migrating = cluster.slots[SLOT].master, - importing = cluster.masters.find(master => master !== migrating)!, - [ migratingClient, importingClient ] = await Promise.all([ - cluster.nodeClient(migrating), - cluster.nodeClient(importing) - ]); - - await Promise.all([ - migratingClient.clusterDelSlots(SLOT), - importingClient.clusterDelSlots(SLOT), - importingClient.clusterAddSlots(SLOT) - ]); - - // wait for migrating node to be notified about the new topology - while ((await migratingClient.clusterInfo()).state !== 'ok') { - await promiseTimeout(50); - } - - const listener = spy(); - - // will trigger `MOVED` error - await cluster.sSubscribe('channel', listener); - - await Promise.all([ - waitTillBeenCalled(listener), - cluster.sPublish('channel', 'message') - ]); - - assert.ok(listener.calledOnceWithExactly('message', 'channel')); - }, { - serverArguments: [], - minimumDockerVersion: [7] - }); + await Promise.all([ + migratingClient.clusterDelSlots(SLOT), + importingClient.clusterDelSlots(SLOT), + importingClient.clusterAddSlots(SLOT), + // cause "topology refresh" on both nodes + migratingClient.clusterSetSlot(SLOT, 'NODE', importing.id), + importingClient.clusterSetSlot(SLOT, 'NODE', importing.id) + ]); + + const listener = spy(); + + // will trigger `MOVED` error + await cluster.sSubscribe('channel', listener); + + await Promise.all([ + waitTillBeenCalled(listener), + cluster.sPublish('channel', 'message') + ]); + + assert.ok(listener.calledOnceWithExactly('message', 'channel')); + }, { + serverArguments: [], + minimumDockerVersion: [7] }); + }); }); diff --git a/packages/client/lib/cluster/index.ts b/packages/client/lib/cluster/index.ts index 49ac293d6cf..7d01b1a20fe 100644 --- a/packages/client/lib/cluster/index.ts +++ b/packages/client/lib/cluster/index.ts @@ -1,424 +1,679 @@ -import COMMANDS from './commands'; -import { RedisCommand, RedisCommandArgument, RedisCommandArguments, RedisCommandRawReply, RedisCommandReply, RedisFunctions, RedisModules, RedisExtensions, RedisScript, RedisScripts, RedisCommandSignature, RedisFunction } from '../commands'; -import { ClientCommandOptions, RedisClientOptions, RedisClientType, WithFunctions, WithModules, WithScripts } from '../client'; +import { RedisClientOptions, RedisClientType } from '../client'; +import { CommandOptions } from '../client/commands-queue'; +import { Command, CommandArguments, CommanderConfig, CommandSignature, /*CommandPolicies, CommandWithPoliciesSignature,*/ TypeMapping, RedisArgument, RedisFunction, RedisFunctions, RedisModules, RedisScript, RedisScripts, ReplyUnion, RespVersions } from '../RESP/types'; +import COMMANDS from '../commands'; +import { EventEmitter } from 'node:events'; +import { attachConfig, functionArgumentsPrefix, getTransformReply, scriptArgumentsPrefix } from '../commander'; import RedisClusterSlots, { NodeAddressMap, ShardNode } from './cluster-slots'; -import { attachExtensions, transformCommandReply, attachCommands, transformCommandArguments } from '../commander'; -import { EventEmitter } from 'events'; -import RedisClusterMultiCommand, { InstantiableRedisClusterMultiCommandType, RedisClusterMultiCommandType } from './multi-command'; -import { RedisMultiQueuedCommand } from '../multi-command'; +import RedisClusterMultiCommand, { RedisClusterMultiCommandType } from './multi-command'; import { PubSubListener } from '../client/pub-sub'; import { ErrorReply } from '../errors'; +import { RedisTcpSocketOptions } from '../client/socket'; + +interface ClusterCommander< + M extends RedisModules, + F extends RedisFunctions, + S extends RedisScripts, + RESP extends RespVersions, + TYPE_MAPPING extends TypeMapping, + // POLICIES extends CommandPolicies +> extends CommanderConfig { + commandOptions?: ClusterCommandOptions; +} export type RedisClusterClientOptions = Omit< - RedisClientOptions, - 'modules' | 'functions' | 'scripts' | 'database' + RedisClientOptions, + keyof ClusterCommander >; export interface RedisClusterOptions< - M extends RedisModules = Record, - F extends RedisFunctions = Record, - S extends RedisScripts = Record -> extends RedisExtensions { - /** - * Should contain details for some of the cluster nodes that the client will use to discover - * the "cluster topology". We recommend including details for at least 3 nodes here. - */ - rootNodes: Array; - /** - * Default values used for every client in the cluster. Use this to specify global values, - * for example: ACL credentials, timeouts, TLS configuration etc. - */ - defaults?: Partial; - /** - * When `true`, `.connect()` will only discover the cluster topology, without actually connecting to all the nodes. - * Useful for short-term or PubSub-only connections. - */ - minimizeConnections?: boolean; - /** - * When `true`, distribute load by executing readonly commands (such as `GET`, `GEOSEARCH`, etc.) across all cluster nodes. When `false`, only use master nodes. - */ - useReplicas?: boolean; - /** - * The maximum number of times a command will be redirected due to `MOVED` or `ASK` errors. - */ - maxCommandRedirections?: number; - /** - * Mapping between the addresses in the cluster (see `CLUSTER SHARDS`) and the addresses the client should connect to - * Useful when the cluster is running on another network - * - */ - nodeAddressMap?: NodeAddressMap; + M extends RedisModules = RedisModules, + F extends RedisFunctions = RedisFunctions, + S extends RedisScripts = RedisScripts, + RESP extends RespVersions = RespVersions, + TYPE_MAPPING extends TypeMapping = TypeMapping, + // POLICIES extends CommandPolicies = CommandPolicies +> extends ClusterCommander { + /** + * Should contain details for some of the cluster nodes that the client will use to discover + * the "cluster topology". We recommend including details for at least 3 nodes here. + */ + rootNodes: Array; + /** + * Default values used for every client in the cluster. Use this to specify global values, + * for example: ACL credentials, timeouts, TLS configuration etc. + */ + defaults?: Partial; + /** + * When `true`, `.connect()` will only discover the cluster topology, without actually connecting to all the nodes. + * Useful for short-term or PubSub-only connections. + */ + minimizeConnections?: boolean; + /** + * When `true`, distribute load by executing readonly commands (such as `GET`, `GEOSEARCH`, etc.) across all cluster nodes. When `false`, only use master nodes. + */ + // TODO: replicas only mode? + useReplicas?: boolean; + /** + * The maximum number of times a command will be redirected due to `MOVED` or `ASK` errors. + */ + maxCommandRedirections?: number; + /** + * Mapping between the addresses in the cluster (see `CLUSTER SHARDS`) and the addresses the client should connect to + * Useful when the cluster is running on another network + */ + nodeAddressMap?: NodeAddressMap; } -type WithCommands = { - [P in keyof typeof COMMANDS]: RedisCommandSignature<(typeof COMMANDS)[P]>; +// remove once request & response policies are ready +type ClusterCommand< + NAME extends PropertyKey, + COMMAND extends Command +> = COMMAND['FIRST_KEY_INDEX'] extends undefined ? ( + COMMAND['IS_FORWARD_COMMAND'] extends true ? NAME : never +) : NAME; + +// CommandWithPoliciesSignature<(typeof COMMANDS)[P], RESP, TYPE_MAPPING, POLICIES> +type WithCommands< + RESP extends RespVersions, + TYPE_MAPPING extends TypeMapping +> = { + [P in keyof typeof COMMANDS as ClusterCommand]: CommandSignature<(typeof COMMANDS)[P], RESP, TYPE_MAPPING>; }; -export type RedisClusterType< - M extends RedisModules = Record, - F extends RedisFunctions = Record, - S extends RedisScripts = Record -> = RedisCluster & WithCommands & WithModules & WithFunctions & WithScripts; - -export default class RedisCluster< - M extends RedisModules, - F extends RedisFunctions, - S extends RedisScripts -> extends EventEmitter { - static extractFirstKey( - command: RedisCommand, - originalArgs: Array, - redisArgs: RedisCommandArguments - ): RedisCommandArgument | undefined { - if (command.FIRST_KEY_INDEX === undefined) { - return undefined; - } else if (typeof command.FIRST_KEY_INDEX === 'number') { - return redisArgs[command.FIRST_KEY_INDEX]; - } - - return command.FIRST_KEY_INDEX(...originalArgs); - } - - static create< - M extends RedisModules, - F extends RedisFunctions, - S extends RedisScripts - >(options?: RedisClusterOptions): RedisClusterType { - return new (attachExtensions({ - BaseClass: RedisCluster, - modulesExecutor: RedisCluster.prototype.commandsExecutor, - modules: options?.modules, - functionsExecutor: RedisCluster.prototype.functionsExecutor, - functions: options?.functions, - scriptsExecutor: RedisCluster.prototype.scriptsExecutor, - scripts: options?.scripts - }))(options); - } - - readonly #options: RedisClusterOptions; - - readonly #slots: RedisClusterSlots; - - get slots() { - return this.#slots.slots; - } - - get shards() { - return this.#slots.shards; - } - - get masters() { - return this.#slots.masters; - } - - get replicas() { - return this.#slots.replicas; - } - - get nodeByAddress() { - return this.#slots.nodeByAddress; - } - - get pubSubNode() { - return this.#slots.pubSubNode; - } - - readonly #Multi: InstantiableRedisClusterMultiCommandType; - - get isOpen() { - return this.#slots.isOpen; - } - - constructor(options: RedisClusterOptions) { - super(); - - this.#options = options; - this.#slots = new RedisClusterSlots(options, this.emit.bind(this)); - this.#Multi = RedisClusterMultiCommand.extend(options); - } - - duplicate(overrides?: Partial>): RedisClusterType { - return new (Object.getPrototypeOf(this).constructor)({ - ...this.#options, - ...overrides - }); - } - - connect() { - return this.#slots.connect(); - } - - async commandsExecutor( - command: C, - args: Array - ): Promise> { - const { jsArgs, args: redisArgs, options } = transformCommandArguments(command, args); - return transformCommandReply( - command, - await this.sendCommand( - RedisCluster.extractFirstKey(command, jsArgs, redisArgs), - command.IS_READ_ONLY, - redisArgs, - options - ), - redisArgs.preserve - ); - } +type WithModules< + M extends RedisModules, + RESP extends RespVersions, + TYPE_MAPPING extends TypeMapping +> = { + [P in keyof M]: { + [C in keyof M[P] as ClusterCommand]: CommandSignature; + }; +}; - async sendCommand( - firstKey: RedisCommandArgument | undefined, - isReadonly: boolean | undefined, - args: RedisCommandArguments, - options?: ClientCommandOptions - ): Promise { - return this.#execute( - firstKey, - isReadonly, - client => client.sendCommand(args, options) - ); - } +type WithFunctions< + F extends RedisFunctions, + RESP extends RespVersions, + TYPE_MAPPING extends TypeMapping +> = { + [L in keyof F]: { + [C in keyof F[L] as ClusterCommand]: CommandSignature; + }; +}; - async functionsExecutor( - fn: F, - args: Array, - name: string, - ): Promise> { - const { args: redisArgs, options } = transformCommandArguments(fn, args); - return transformCommandReply( - fn, - await this.executeFunction( - name, - fn, - args, - redisArgs, - options - ), - redisArgs.preserve - ); - } +type WithScripts< + S extends RedisScripts, + RESP extends RespVersions, + TYPE_MAPPING extends TypeMapping +> = { + [P in keyof S as ClusterCommand]: CommandSignature; +}; - async executeFunction( - name: string, - fn: RedisFunction, - originalArgs: Array, - redisArgs: RedisCommandArguments, - options?: ClientCommandOptions - ): Promise { - return this.#execute( - RedisCluster.extractFirstKey(fn, originalArgs, redisArgs), - fn.IS_READ_ONLY, - client => client.executeFunction(name, fn, redisArgs, options) - ); - } +export type RedisClusterType< + M extends RedisModules = {}, + F extends RedisFunctions = {}, + S extends RedisScripts = {}, + RESP extends RespVersions = 2, + TYPE_MAPPING extends TypeMapping = {}, + // POLICIES extends CommandPolicies = {} +> = ( + RedisCluster & + WithCommands & + WithModules & + WithFunctions & + WithScripts +); + +export interface ClusterCommandOptions< + TYPE_MAPPING extends TypeMapping = TypeMapping + // POLICIES extends CommandPolicies = CommandPolicies +> extends CommandOptions { + // policies?: POLICIES; +} - async scriptsExecutor(script: S, args: Array): Promise> { - const { args: redisArgs, options } = transformCommandArguments(script, args); - return transformCommandReply( - script, - await this.executeScript( - script, - args, - redisArgs, - options - ), - redisArgs.preserve - ); - } +type ProxyCluster = RedisCluster; - async executeScript( - script: RedisScript, - originalArgs: Array, - redisArgs: RedisCommandArguments, - options?: ClientCommandOptions - ): Promise { - return this.#execute( - RedisCluster.extractFirstKey(script, originalArgs, redisArgs), - script.IS_READ_ONLY, - client => client.executeScript(script, redisArgs, options) - ); - } +type NamespaceProxyCluster = { _self: ProxyCluster }; - async #execute( - firstKey: RedisCommandArgument | undefined, - isReadonly: boolean | undefined, - executor: (client: RedisClientType) => Promise - ): Promise { - const maxCommandRedirections = this.#options.maxCommandRedirections ?? 16; - let client = await this.#slots.getClient(firstKey, isReadonly); - for (let i = 0;; i++) { - try { - return await executor(client); - } catch (err) { - if (++i > maxCommandRedirections || !(err instanceof ErrorReply)) { - throw err; - } - - if (err.message.startsWith('ASK')) { - const address = err.message.substring(err.message.lastIndexOf(' ') + 1); - let redirectTo = await this.#slots.getMasterByAddress(address); - if (!redirectTo) { - await this.#slots.rediscover(client); - redirectTo = await this.#slots.getMasterByAddress(address); - } - - if (!redirectTo) { - throw new Error(`Cannot find node ${address}`); - } - - await redirectTo.asking(); - client = redirectTo; - continue; - } else if (err.message.startsWith('MOVED')) { - await this.#slots.rediscover(client); - client = await this.#slots.getClient(firstKey, isReadonly); - continue; - } - - throw err; - } +export default class RedisCluster< + M extends RedisModules, + F extends RedisFunctions, + S extends RedisScripts, + RESP extends RespVersions, + TYPE_MAPPING extends TypeMapping, + // POLICIES extends CommandPolicies +> extends EventEmitter { + static extractFirstKey( + command: C, + args: Parameters, + redisArgs: Array + ) { + let key: RedisArgument | undefined; + switch (typeof command.FIRST_KEY_INDEX) { + case 'number': + key = redisArgs[command.FIRST_KEY_INDEX]; + break; + + case 'function': + key = command.FIRST_KEY_INDEX(...args); + break; + } + + return key; + } + + static #createCommand(command: Command, resp: RespVersions) { + const transformReply = getTransformReply(command, resp); + return async function (this: ProxyCluster, ...args: Array) { + const redisArgs = command.transformArguments(...args); + const typeMapping = this._commandOptions?.typeMapping; + + const firstKey = RedisCluster.extractFirstKey( + command, + args, + redisArgs + ); + + const reply = await this.sendCommand( + firstKey, + command.IS_READ_ONLY, + redisArgs, + this._commandOptions, + // command.POLICIES + ); + + return transformReply ? + transformReply(reply, redisArgs.preserve, typeMapping) : + reply; + }; + } + + static #createModuleCommand(command: Command, resp: RespVersions) { + const transformReply = getTransformReply(command, resp); + return async function (this: NamespaceProxyCluster, ...args: Array) { + const redisArgs = command.transformArguments(...args); + const typeMapping = this._self._commandOptions?.typeMapping; + + const firstKey = RedisCluster.extractFirstKey( + command, + args, + redisArgs + ); + + const reply = await this._self.sendCommand( + firstKey, + command.IS_READ_ONLY, + redisArgs, + this._self._commandOptions, + // command.POLICIES + ); + + return transformReply ? + transformReply(reply, redisArgs.preserve, typeMapping) : + reply; + }; + } + + static #createFunctionCommand(name: string, fn: RedisFunction, resp: RespVersions) { + const prefix = functionArgumentsPrefix(name, fn), + transformReply = getTransformReply(fn, resp); + return async function (this: NamespaceProxyCluster, ...args: Array) { + const fnArgs = fn.transformArguments(...args); + const redisArgs = prefix.concat(fnArgs); + const typeMapping = this._self._commandOptions?.typeMapping; + + const firstKey = RedisCluster.extractFirstKey( + fn, + args, + fnArgs + ); + + const reply = await this._self.sendCommand( + firstKey, + fn.IS_READ_ONLY, + redisArgs, + this._self._commandOptions, + // fn.POLICIES + ); + + return transformReply ? + transformReply(reply, fnArgs.preserve, typeMapping) : + reply; + }; + } + + static #createScriptCommand(script: RedisScript, resp: RespVersions) { + const prefix = scriptArgumentsPrefix(script), + transformReply = getTransformReply(script, resp); + return async function (this: ProxyCluster, ...args: Array) { + const scriptArgs = script.transformArguments(...args); + const redisArgs = prefix.concat(scriptArgs); + const typeMapping = this._commandOptions?.typeMapping; + + const firstKey = RedisCluster.extractFirstKey( + script, + args, + scriptArgs + ); + + const reply = await this.executeScript( + script, + firstKey, + script.IS_READ_ONLY, + redisArgs, + this._commandOptions, + // script.POLICIES + ); + + return transformReply ? + transformReply(reply, scriptArgs.preserve, typeMapping) : + reply; + }; + } + + static factory< + M extends RedisModules = {}, + F extends RedisFunctions = {}, + S extends RedisScripts = {}, + RESP extends RespVersions = 2, + TYPE_MAPPING extends TypeMapping = {}, + // POLICIES extends CommandPolicies = {} + >(config?: ClusterCommander) { + const Cluster = attachConfig({ + BaseClass: RedisCluster, + commands: COMMANDS, + createCommand: RedisCluster.#createCommand, + createModuleCommand: RedisCluster.#createModuleCommand, + createFunctionCommand: RedisCluster.#createFunctionCommand, + createScriptCommand: RedisCluster.#createScriptCommand, + config + }); + + Cluster.prototype.Multi = RedisClusterMultiCommand.extend(config); + + return (options?: Omit>) => { + // returning a "proxy" to prevent the namespaces._self to leak between "proxies" + return Object.create(new Cluster(options)) as RedisClusterType; + }; + } + + static create< + M extends RedisModules = {}, + F extends RedisFunctions = {}, + S extends RedisScripts = {}, + RESP extends RespVersions = 2, + TYPE_MAPPING extends TypeMapping = {}, + // POLICIES extends CommandPolicies = {} + >(options?: RedisClusterOptions) { + return RedisCluster.factory(options)(options); + } + + readonly #options: RedisClusterOptions; + + readonly #slots: RedisClusterSlots; + + private _self = this; + private _commandOptions?: ClusterCommandOptions; + + /** + * An array of the cluster slots, each slot contain its `master` and `replicas`. + * Use with {@link RedisCluster.prototype.nodeClient} to get the client for a specific node (master or replica). + */ + get slots() { + return this._self.#slots.slots; + } + + /** + * An array of the cluster masters. + * Use with {@link RedisCluster.prototype.nodeClient} to get the client for a specific master node. + */ + get masters() { + return this._self.#slots.masters; + } + + /** + * An array of the cluster replicas. + * Use with {@link RedisCluster.prototype.nodeClient} to get the client for a specific replica node. + */ + get replicas() { + return this._self.#slots.replicas; + } + + /** + * A map form a node address (`:`) to its shard, each shard contain its `master` and `replicas`. + * Use with {@link RedisCluster.prototype.nodeClient} to get the client for a specific node (master or replica). + */ + get nodeByAddress() { + return this._self.#slots.nodeByAddress; + } + + /** + * The current pub/sub node. + */ + get pubSubNode() { + return this._self.#slots.pubSubNode; + } + + get isOpen() { + return this._self.#slots.isOpen; + } + + constructor(options: RedisClusterOptions) { + super(); + + this.#options = options; + this.#slots = new RedisClusterSlots(options, this.emit.bind(this)); + + if (options?.commandOptions) { + this._commandOptions = options.commandOptions; + } + } + + duplicate< + _M extends RedisModules = M, + _F extends RedisFunctions = F, + _S extends RedisScripts = S, + _RESP extends RespVersions = RESP, + _TYPE_MAPPING extends TypeMapping = TYPE_MAPPING + >(overrides?: Partial>) { + return new (Object.getPrototypeOf(this).constructor)({ + ...this._self.#options, + commandOptions: this._commandOptions, + ...overrides + }) as RedisClusterType<_M, _F, _S, _RESP, _TYPE_MAPPING>; + } + + async connect() { + await this._self.#slots.connect(); + return this as unknown as RedisClusterType; + } + + withCommandOptions< + OPTIONS extends ClusterCommandOptions, + TYPE_MAPPING extends TypeMapping, + // POLICIES extends CommandPolicies + >(options: OPTIONS) { + const proxy = Object.create(this); + proxy._commandOptions = options; + return proxy as RedisClusterType< + M, + F, + S, + RESP, + TYPE_MAPPING extends TypeMapping ? TYPE_MAPPING : {} + // POLICIES extends CommandPolicies ? POLICIES : {} + >; + } + + private _commandOptionsProxy< + K extends keyof ClusterCommandOptions, + V extends ClusterCommandOptions[K] + >( + key: K, + value: V + ) { + const proxy = Object.create(this); + proxy._commandOptions = Object.create(this._commandOptions ?? null); + proxy._commandOptions[key] = value; + return proxy as RedisClusterType< + M, + F, + S, + RESP, + K extends 'typeMapping' ? V extends TypeMapping ? V : {} : TYPE_MAPPING + // K extends 'policies' ? V extends CommandPolicies ? V : {} : POLICIES + >; + } + + /** + * Override the `typeMapping` command option + */ + withTypeMapping(typeMapping: TYPE_MAPPING) { + return this._commandOptionsProxy('typeMapping', typeMapping); + } + + // /** + // * Override the `policies` command option + // * TODO + // */ + // withPolicies (policies: POLICIES) { + // return this._commandOptionsProxy('policies', policies); + // } + + async #execute( + firstKey: RedisArgument | undefined, + isReadonly: boolean | undefined, + fn: (client: RedisClientType) => Promise + ): Promise { + const maxCommandRedirections = this.#options.maxCommandRedirections ?? 16; + let client = await this.#slots.getClient(firstKey, isReadonly), + i = 0; + while (true) { + try { + return await fn(client); + } catch (err) { + // TODO: error class + if (++i > maxCommandRedirections || !(err instanceof Error)) { + throw err; } - } - - MULTI(routing?: RedisCommandArgument): RedisClusterMultiCommandType { - return new this.#Multi( - (commands: Array, firstKey?: RedisCommandArgument, chainId?: symbol) => { - return this.#execute( - firstKey, - false, - client => client.multiExecutor(commands, undefined, chainId) - ); - }, - routing - ); - } - - multi = this.MULTI; - async SUBSCRIBE( - channels: string | Array, - listener: PubSubListener, - bufferMode?: T - ) { - return (await this.#slots.getPubSubClient()) - .SUBSCRIBE(channels, listener, bufferMode); - } - - subscribe = this.SUBSCRIBE; - - async UNSUBSCRIBE( - channels?: string | Array, - listener?: PubSubListener, - bufferMode?: T - ) { - return this.#slots.executeUnsubscribeCommand(client => - client.UNSUBSCRIBE(channels, listener, bufferMode) - ); - } - - unsubscribe = this.UNSUBSCRIBE; - - async PSUBSCRIBE( - patterns: string | Array, - listener: PubSubListener, - bufferMode?: T - ) { - return (await this.#slots.getPubSubClient()) - .PSUBSCRIBE(patterns, listener, bufferMode); - } - - pSubscribe = this.PSUBSCRIBE; - - async PUNSUBSCRIBE( - patterns?: string | Array, - listener?: PubSubListener, - bufferMode?: T - ) { - return this.#slots.executeUnsubscribeCommand(client => - client.PUNSUBSCRIBE(patterns, listener, bufferMode) - ); - } - - pUnsubscribe = this.PUNSUBSCRIBE; - - async SSUBSCRIBE( - channels: string | Array, - listener: PubSubListener, - bufferMode?: T - ) { - const maxCommandRedirections = this.#options.maxCommandRedirections ?? 16, - firstChannel = Array.isArray(channels) ? channels[0] : channels; - let client = await this.#slots.getShardedPubSubClient(firstChannel); - for (let i = 0;; i++) { - try { - return await client.SSUBSCRIBE(channels, listener, bufferMode); - } catch (err) { - if (++i > maxCommandRedirections || !(err instanceof ErrorReply)) { - throw err; - } - - if (err.message.startsWith('MOVED')) { - await this.#slots.rediscover(client); - client = await this.#slots.getShardedPubSubClient(firstChannel); - continue; - } - - throw err; - } + if (err.message.startsWith('ASK')) { + const address = err.message.substring(err.message.lastIndexOf(' ') + 1); + let redirectTo = await this.#slots.getMasterByAddress(address); + if (!redirectTo) { + await this.#slots.rediscover(client); + redirectTo = await this.#slots.getMasterByAddress(address); + } + + if (!redirectTo) { + throw new Error(`Cannot find node ${address}`); + } + + await redirectTo.asking(); + client = redirectTo; + continue; + } + + if (err.message.startsWith('MOVED')) { + await this.#slots.rediscover(client); + client = await this.#slots.getClient(firstKey, isReadonly); + continue; } - } - - sSubscribe = this.SSUBSCRIBE; - - SUNSUBSCRIBE( - channels: string | Array, - listener?: PubSubListener, - bufferMode?: T - ) { - return this.#slots.executeShardedUnsubscribeCommand( - Array.isArray(channels) ? channels[0] : channels, - client => client.SUNSUBSCRIBE(channels, listener, bufferMode) - ); - } - - sUnsubscribe = this.SUNSUBSCRIBE; - - quit(): Promise { - return this.#slots.quit(); - } - - disconnect(): Promise { - return this.#slots.disconnect(); - } - - nodeClient(node: ShardNode) { - return this.#slots.nodeClient(node); - } - - getRandomNode() { - return this.#slots.getRandomNode(); - } - getSlotRandomNode(slot: number) { - return this.#slots.getSlotRandomNode(slot); - } + throw err; + } + } + } + + async sendCommand( + firstKey: RedisArgument | undefined, + isReadonly: boolean | undefined, + args: CommandArguments, + options?: ClusterCommandOptions, + // defaultPolicies?: CommandPolicies + ): Promise { + return this._self.#execute( + firstKey, + isReadonly, + client => client.sendCommand(args, options) + ); + } + + executeScript( + script: RedisScript, + firstKey: RedisArgument | undefined, + isReadonly: boolean | undefined, + args: Array, + options?: CommandOptions + ) { + return this._self.#execute( + firstKey, + isReadonly, + client => client.executeScript(script, args, options) + ); + } + + MULTI(routing?: RedisArgument) { + type Multi = new (...args: ConstructorParameters) => RedisClusterMultiCommandType<[], M, F, S, RESP, TYPE_MAPPING>; + return new ((this as any).Multi as Multi)( + async (firstKey, isReadonly, commands) => { + const client = await this._self.#slots.getClient(firstKey, isReadonly); + return client._executeMulti(commands); + }, + async (firstKey, isReadonly, commands) => { + const client = await this._self.#slots.getClient(firstKey, isReadonly); + return client._executePipeline(commands); + }, + routing, + this._commandOptions?.typeMapping + ); + } + + multi = this.MULTI; + + async SUBSCRIBE( + channels: string | Array, + listener: PubSubListener, + bufferMode?: T + ) { + return (await this._self.#slots.getPubSubClient()) + .SUBSCRIBE(channels, listener, bufferMode); + } + + subscribe = this.SUBSCRIBE; + + async UNSUBSCRIBE( + channels?: string | Array, + listener?: PubSubListener, + bufferMode?: T + ) { + return this._self.#slots.executeUnsubscribeCommand(client => + client.UNSUBSCRIBE(channels, listener, bufferMode) + ); + } + + unsubscribe = this.UNSUBSCRIBE; + + async PSUBSCRIBE( + patterns: string | Array, + listener: PubSubListener, + bufferMode?: T + ) { + return (await this._self.#slots.getPubSubClient()) + .PSUBSCRIBE(patterns, listener, bufferMode); + } + + pSubscribe = this.PSUBSCRIBE; + + async PUNSUBSCRIBE( + patterns?: string | Array, + listener?: PubSubListener, + bufferMode?: T + ) { + return this._self.#slots.executeUnsubscribeCommand(client => + client.PUNSUBSCRIBE(patterns, listener, bufferMode) + ); + } + + pUnsubscribe = this.PUNSUBSCRIBE; + + async SSUBSCRIBE( + channels: string | Array, + listener: PubSubListener, + bufferMode?: T + ) { + const maxCommandRedirections = this._self.#options.maxCommandRedirections ?? 16, + firstChannel = Array.isArray(channels) ? channels[0] : channels; + let client = await this._self.#slots.getShardedPubSubClient(firstChannel); + for (let i = 0; ; i++) { + try { + return await client.SSUBSCRIBE(channels, listener, bufferMode); + } catch (err) { + if (++i > maxCommandRedirections || !(err instanceof ErrorReply)) { + throw err; + } - /** - * @deprecated use `.masters` instead - */ - getMasters() { - return this.masters; - } + if (err.message.startsWith('MOVED')) { + await this._self.#slots.rediscover(client); + client = await this._self.#slots.getShardedPubSubClient(firstChannel); + continue; + } - /** - * @deprecated use `.slots[]` instead - */ - getSlotMaster(slot: number) { - return this.slots[slot].master; - } + throw err; + } + } + } + + sSubscribe = this.SSUBSCRIBE; + + SUNSUBSCRIBE( + channels: string | Array, + listener: PubSubListener, + bufferMode?: T + ) { + return this._self.#slots.executeShardedUnsubscribeCommand( + Array.isArray(channels) ? channels[0] : channels, + client => client.SUNSUBSCRIBE(channels, listener, bufferMode) + ); + } + + sUnsubscribe = this.SUNSUBSCRIBE; + + /** + * @deprecated Use `close` instead. + */ + quit() { + return this._self.#slots.quit(); + } + + /** + * @deprecated Use `destroy` instead. + */ + disconnect() { + return this._self.#slots.disconnect(); + } + + close() { + return this._self.#slots.close(); + } + + destroy() { + return this._self.#slots.destroy(); + } + + nodeClient(node: ShardNode) { + return this._self.#slots.nodeClient(node); + } + + /** + * Returns a random node from the cluster. + * Userful for running "forward" commands (like PUBLISH) on a random node. + */ + getRandomNode() { + return this._self.#slots.getRandomNode(); + } + + /** + * Get a random node from a slot. + * Useful for running readonly commands on a slot. + */ + getSlotRandomNode(slot: number) { + return this._self.#slots.getSlotRandomNode(slot); + } + + /** + * @deprecated use `.masters` instead + * TODO + */ + getMasters() { + return this.masters; + } + + /** + * @deprecated use `.slots[]` instead + * TODO + */ + getSlotMaster(slot: number) { + return this.slots[slot].master; + } } - -attachCommands({ - BaseClass: RedisCluster, - commands: COMMANDS, - executor: RedisCluster.prototype.commandsExecutor -}); diff --git a/packages/client/lib/cluster/multi-command.ts b/packages/client/lib/cluster/multi-command.ts index ef3c7590ec7..2b02e8d7df2 100644 --- a/packages/client/lib/cluster/multi-command.ts +++ b/packages/client/lib/cluster/multi-command.ts @@ -1,141 +1,262 @@ -import COMMANDS from './commands'; -import { RedisCommand, RedisCommandArgument, RedisCommandArguments, RedisCommandRawReply, RedisFunctions, RedisModules, RedisExtensions, RedisScript, RedisScripts, ExcludeMappedString, RedisFunction } from '../commands'; -import RedisMultiCommand, { RedisMultiQueuedCommand } from '../multi-command'; -import { attachCommands, attachExtensions } from '../commander'; +import COMMANDS from '../commands'; +import RedisMultiCommand, { MULTI_REPLY, MultiReply, MultiReplyType, RedisMultiQueuedCommand } from '../multi-command'; +import { ReplyWithTypeMapping, CommandReply, Command, CommandArguments, CommanderConfig, RedisFunctions, RedisModules, RedisScripts, RespVersions, TransformReply, RedisScript, RedisFunction, TypeMapping, RedisArgument } from '../RESP/types'; +import { attachConfig, functionArgumentsPrefix, getTransformReply } from '../commander'; import RedisCluster from '.'; -type RedisClusterMultiCommandSignature< - C extends RedisCommand, - M extends RedisModules, - F extends RedisFunctions, - S extends RedisScripts -> = (...args: Parameters) => RedisClusterMultiCommandType; +type CommandSignature< + REPLIES extends Array, + C extends Command, + M extends RedisModules, + F extends RedisFunctions, + S extends RedisScripts, + RESP extends RespVersions, + TYPE_MAPPING extends TypeMapping +> = (...args: Parameters) => RedisClusterMultiCommandType< + [...REPLIES, ReplyWithTypeMapping, TYPE_MAPPING>], + M, + F, + S, + RESP, + TYPE_MAPPING +>; type WithCommands< - M extends RedisModules, - F extends RedisFunctions, - S extends RedisScripts + REPLIES extends Array, + M extends RedisModules, + F extends RedisFunctions, + S extends RedisScripts, + RESP extends RespVersions, + TYPE_MAPPING extends TypeMapping > = { - [P in keyof typeof COMMANDS]: RedisClusterMultiCommandSignature<(typeof COMMANDS)[P], M, F, S>; + [P in keyof typeof COMMANDS]: CommandSignature; }; type WithModules< - M extends RedisModules, - F extends RedisFunctions, - S extends RedisScripts + REPLIES extends Array, + M extends RedisModules, + F extends RedisFunctions, + S extends RedisScripts, + RESP extends RespVersions, + TYPE_MAPPING extends TypeMapping > = { - [P in keyof M as ExcludeMappedString

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

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

]: RedisClusterMultiCommandSignature; + [P in keyof S]: CommandSignature; }; export type RedisClusterMultiCommandType< - M extends RedisModules, - F extends RedisFunctions, - S extends RedisScripts -> = RedisClusterMultiCommand & WithCommands & WithModules & WithFunctions & WithScripts; - -export type InstantiableRedisClusterMultiCommandType< - M extends RedisModules, - F extends RedisFunctions, - S extends RedisScripts -> = new (...args: ConstructorParameters) => RedisClusterMultiCommandType; - -export type RedisClusterMultiExecutor = (queue: Array, firstKey?: RedisCommandArgument, chainId?: symbol) => Promise>; - -export default class RedisClusterMultiCommand { - readonly #multi = new RedisMultiCommand(); - readonly #executor: RedisClusterMultiExecutor; - #firstKey: RedisCommandArgument | undefined; - - static extend< - M extends RedisModules, - F extends RedisFunctions, - S extends RedisScripts - >(extensions?: RedisExtensions): InstantiableRedisClusterMultiCommandType { - return attachExtensions({ - BaseClass: RedisClusterMultiCommand, - modulesExecutor: RedisClusterMultiCommand.prototype.commandsExecutor, - modules: extensions?.modules, - functionsExecutor: RedisClusterMultiCommand.prototype.functionsExecutor, - functions: extensions?.functions, - scriptsExecutor: RedisClusterMultiCommand.prototype.scriptsExecutor, - scripts: extensions?.scripts - }); - } - - constructor(executor: RedisClusterMultiExecutor, firstKey?: RedisCommandArgument) { - this.#executor = executor; - this.#firstKey = firstKey; - } - - commandsExecutor(command: RedisCommand, args: Array): this { - const transformedArguments = command.transformArguments(...args); - this.#firstKey ??= RedisCluster.extractFirstKey(command, args, transformedArguments); - return this.addCommand(undefined, transformedArguments, command.transformReply); - } - - addCommand( - firstKey: RedisCommandArgument | undefined, - args: RedisCommandArguments, - transformReply?: RedisCommand['transformReply'] - ): this { - this.#firstKey ??= firstKey; - this.#multi.addCommand(args, transformReply); - return this; - } - - functionsExecutor(fn: RedisFunction, args: Array, name: string): this { - const transformedArguments = this.#multi.addFunction(name, fn, args); - this.#firstKey ??= RedisCluster.extractFirstKey(fn, args, transformedArguments); - return this; - } - - scriptsExecutor(script: RedisScript, args: Array): this { - const transformedArguments = this.#multi.addScript(script, args); - this.#firstKey ??= RedisCluster.extractFirstKey(script, args, transformedArguments); - return this; - } - - async exec(execAsPipeline = false): Promise> { - if (execAsPipeline) { - return this.execAsPipeline(); - } - - return this.#multi.handleExecReplies( - await this.#executor(this.#multi.queue, this.#firstKey, RedisMultiCommand.generateChainId()) - ); - } + REPLIES extends Array, + M extends RedisModules, + F extends RedisFunctions, + S extends RedisScripts, + RESP extends RespVersions, + TYPE_MAPPING extends TypeMapping +> = ( + RedisClusterMultiCommand & + WithCommands & + WithModules & + WithFunctions & + WithScripts +); - EXEC = this.exec; +export type ClusterMultiExecute = ( + firstKey: RedisArgument | undefined, + isReadonly: boolean | undefined, + commands: Array +) => Promise>; - async execAsPipeline(): Promise> { - return this.#multi.transformReplies( - await this.#executor(this.#multi.queue, this.#firstKey) +export default class RedisClusterMultiCommand { + static #createCommand(command: Command, resp: RespVersions) { + const transformReply = getTransformReply(command, resp); + return function (this: RedisClusterMultiCommand, ...args: Array) { + const redisArgs = command.transformArguments(...args); + const firstKey = RedisCluster.extractFirstKey( + command, + args, + redisArgs + ); + return this.addCommand( + firstKey, + command.IS_READ_ONLY, + redisArgs, + transformReply + ); + }; + } + + static #createModuleCommand(command: Command, resp: RespVersions) { + const transformReply = getTransformReply(command, resp); + return function (this: { _self: RedisClusterMultiCommand }, ...args: Array) { + const redisArgs = command.transformArguments(...args), + firstKey = RedisCluster.extractFirstKey( + command, + args, + redisArgs ); - } -} + return this._self.addCommand( + firstKey, + command.IS_READ_ONLY, + redisArgs, + transformReply + ); + }; + } + + static #createFunctionCommand(name: string, fn: RedisFunction, resp: RespVersions) { + const prefix = functionArgumentsPrefix(name, fn), + transformReply = getTransformReply(fn, resp); + return function (this: { _self: RedisClusterMultiCommand }, ...args: Array) { + const fnArgs = fn.transformArguments(...args); + const redisArgs: CommandArguments = prefix.concat(fnArgs); + const firstKey = RedisCluster.extractFirstKey( + fn, + args, + fnArgs + ); + redisArgs.preserve = fnArgs.preserve; + return this._self.addCommand( + firstKey, + fn.IS_READ_ONLY, + redisArgs, + transformReply + ); + }; + } + + static #createScriptCommand(script: RedisScript, resp: RespVersions) { + const transformReply = getTransformReply(script, resp); + return function (this: RedisClusterMultiCommand, ...args: Array) { + const scriptArgs = script.transformArguments(...args); + this.#setState( + RedisCluster.extractFirstKey( + script, + args, + scriptArgs + ), + script.IS_READ_ONLY + ); + this.#multi.addScript( + script, + scriptArgs, + transformReply + ); + return this; + }; + } + + static extend< + M extends RedisModules = Record, + F extends RedisFunctions = Record, + S extends RedisScripts = Record, + RESP extends RespVersions = 2 + >(config?: CommanderConfig) { + return attachConfig({ + BaseClass: RedisClusterMultiCommand, + commands: COMMANDS, + createCommand: RedisClusterMultiCommand.#createCommand, + createModuleCommand: RedisClusterMultiCommand.#createModuleCommand, + createFunctionCommand: RedisClusterMultiCommand.#createFunctionCommand, + createScriptCommand: RedisClusterMultiCommand.#createScriptCommand, + config + }); + } -attachCommands({ - BaseClass: RedisClusterMultiCommand, - commands: COMMANDS, - executor: RedisClusterMultiCommand.prototype.commandsExecutor -}); + readonly #multi = new RedisMultiCommand(); + readonly #executeMulti: ClusterMultiExecute; + readonly #executePipeline: ClusterMultiExecute; + #firstKey: RedisArgument | undefined; + #isReadonly: boolean | undefined = true; + readonly #typeMapping?: TypeMapping; + + constructor( + executeMulti: ClusterMultiExecute, + executePipeline: ClusterMultiExecute, + routing: RedisArgument | undefined, + typeMapping?: TypeMapping + ) { + this.#executeMulti = executeMulti; + this.#executePipeline = executePipeline; + this.#firstKey = routing; + this.#typeMapping = typeMapping; + } + + #setState( + firstKey: RedisArgument | undefined, + isReadonly: boolean | undefined, + ) { + this.#firstKey ??= firstKey; + this.#isReadonly &&= isReadonly; + } + + addCommand( + firstKey: RedisArgument | undefined, + isReadonly: boolean | undefined, + args: CommandArguments, + transformReply?: TransformReply + ) { + this.#setState(firstKey, isReadonly); + this.#multi.addCommand(args, transformReply); + return this; + } + + async exec(execAsPipeline = false) { + if (execAsPipeline) return this.execAsPipeline(); + + return this.#multi.transformReplies( + await this.#executeMulti( + this.#firstKey, + this.#isReadonly, + this.#multi.queue + ), + this.#typeMapping + ) as MultiReplyType; + } + + EXEC = this.exec; + + execTyped(execAsPipeline = false) { + return this.exec(execAsPipeline); + } + + async execAsPipeline() { + if (this.#multi.queue.length === 0) return [] as MultiReplyType; + + return this.#multi.transformReplies( + await this.#executePipeline( + this.#firstKey, + this.#isReadonly, + this.#multi.queue + ), + this.#typeMapping + ) as MultiReplyType; + } + + execAsPipelineTyped() { + return this.execAsPipeline(); + } +} diff --git a/packages/client/lib/command-options.ts b/packages/client/lib/command-options.ts deleted file mode 100644 index 8f91130b557..00000000000 --- a/packages/client/lib/command-options.ts +++ /dev/null @@ -1,14 +0,0 @@ -const symbol = Symbol('Command Options'); - -export type CommandOptions = T & { - readonly [symbol]: true; -}; - -export function commandOptions(options: T): CommandOptions { - (options as any)[symbol] = true; - return options as CommandOptions; -} - -export function isCommandOptions(options: any): options is CommandOptions { - return options?.[symbol] === true; -} diff --git a/packages/client/lib/commander.ts b/packages/client/lib/commander.ts index c04f41e1eb0..4434317d267 100644 --- a/packages/client/lib/commander.ts +++ b/packages/client/lib/commander.ts @@ -1,165 +1,124 @@ - -import { ClientCommandOptions } from './client'; -import { CommandOptions, isCommandOptions } from './command-options'; -import { RedisCommand, RedisCommandArgument, RedisCommandArguments, RedisCommandReply, RedisFunction, RedisFunctions, RedisModules, RedisScript, RedisScripts } from './commands'; - -type Instantiable = new (...args: Array) => T; - -type CommandsExecutor = - (command: C, args: Array, name: string) => unknown; - -interface AttachCommandsConfig { - BaseClass: Instantiable; - commands: Record; - executor: CommandsExecutor; +import { Command, CommanderConfig, RedisCommands, RedisFunction, RedisFunctions, RedisModules, RedisScript, RedisScripts, RespVersions } from './RESP/types'; + +interface AttachConfigOptions< + M extends RedisModules, + F extends RedisFunctions, + S extends RedisScripts, + RESP extends RespVersions +> { + BaseClass: new (...args: any) => any; + commands: RedisCommands; + createCommand(command: Command, resp: RespVersions): (...args: any) => any; + createModuleCommand(command: Command, resp: RespVersions): (...args: any) => any; + createFunctionCommand(name: string, fn: RedisFunction, resp: RespVersions): (...args: any) => any; + createScriptCommand(script: RedisScript, resp: RespVersions): (...args: any) => any; + config?: CommanderConfig; } -export function attachCommands({ - BaseClass, - commands, - executor -}: AttachCommandsConfig): void { - for (const [name, command] of Object.entries(commands)) { - BaseClass.prototype[name] = function (...args: Array): unknown { - return executor.call(this, command, args, name); - }; - } +/* FIXME: better error message / link */ +function throwResp3SearchModuleUnstableError() { + throw new Error('Some RESP3 results for Redis Query Engine responses may change. Refer to the readme for guidance'); } -interface AttachExtensionsConfig { - BaseClass: T; - modulesExecutor: CommandsExecutor; - modules?: RedisModules; - functionsExecutor: CommandsExecutor; - functions?: RedisFunctions; - scriptsExecutor: CommandsExecutor; - scripts?: RedisScripts; -} - -export function attachExtensions(config: AttachExtensionsConfig): any { - let Commander; +export function attachConfig< + M extends RedisModules, + F extends RedisFunctions, + S extends RedisScripts, + RESP extends RespVersions +>({ + BaseClass, + commands, + createCommand, + createModuleCommand, + createFunctionCommand, + createScriptCommand, + config +}: AttachConfigOptions) { + const RESP = config?.RESP ?? 2, + Class: any = class extends BaseClass {}; + + for (const [name, command] of Object.entries(commands)) { + Class.prototype[name] = createCommand(command, RESP); + } + + if (config?.modules) { + for (const [moduleName, module] of Object.entries(config.modules)) { + const fns = Object.create(null); + for (const [name, command] of Object.entries(module)) { + if (config.RESP == 3 && command.unstableResp3 && !config.unstableResp3) { + fns[name] = throwResp3SearchModuleUnstableError; + } else { + fns[name] = createModuleCommand(command, RESP); + } + } - if (config.modules) { - Commander = attachWithNamespaces({ - BaseClass: config.BaseClass, - namespaces: config.modules, - executor: config.modulesExecutor - }); + attachNamespace(Class.prototype, moduleName, fns); } + } - if (config.functions) { - Commander = attachWithNamespaces({ - BaseClass: Commander ?? config.BaseClass, - namespaces: config.functions, - executor: config.functionsExecutor - }); - } + if (config?.functions) { + for (const [library, commands] of Object.entries(config.functions)) { + const fns = Object.create(null); + for (const [name, command] of Object.entries(commands)) { + fns[name] = createFunctionCommand(name, command, RESP); + } - if (config.scripts) { - Commander ??= class extends config.BaseClass {}; - attachCommands({ - BaseClass: Commander, - commands: config.scripts, - executor: config.scriptsExecutor - }); + attachNamespace(Class.prototype, library, fns); } + } - return Commander ?? config.BaseClass; -} + if (config?.scripts) { + for (const [name, script] of Object.entries(config.scripts)) { + Class.prototype[name] = createScriptCommand(script, RESP); + } + } -interface AttachWithNamespacesConfig { - BaseClass: Instantiable; - namespaces: Record>; - executor: CommandsExecutor; + return Class; } -function attachWithNamespaces({ - BaseClass, - namespaces, - executor -}: AttachWithNamespacesConfig): any { - const Commander = class extends BaseClass { - constructor(...args: Array) { - super(...args); - - for (const namespace of Object.keys(namespaces)) { - this[namespace] = Object.create(this[namespace], { - self: { - value: this - } - }); - } - } - }; - - for (const [namespace, commands] of Object.entries(namespaces)) { - Commander.prototype[namespace] = {}; - for (const [name, command] of Object.entries(commands)) { - Commander.prototype[namespace][name] = function (...args: Array): unknown { - return executor.call(this.self, command, args, name); - }; - } +function attachNamespace(prototype: any, name: PropertyKey, fns: any) { + Object.defineProperty(prototype, name, { + get() { + const value = Object.create(fns); + value._self = this; + Object.defineProperty(this, name, { value }); + return value; } - - return Commander; + }); } -export function transformCommandArguments( - command: RedisCommand, - args: Array -): { - jsArgs: Array; - args: RedisCommandArguments; - options: CommandOptions | undefined; -} { - let options; - if (isCommandOptions(args[0])) { - options = args[0]; - args = args.slice(1); - } +export function getTransformReply(command: Command, resp: RespVersions) { + switch (typeof command.transformReply) { + case 'function': + return command.transformReply; - return { - jsArgs: args, - args: command.transformArguments(...args), - options - }; + case 'object': + return command.transformReply[resp]; + } } -export function transformLegacyCommandArguments(args: Array): Array { - return args.flat().map(arg => { - return typeof arg === 'number' || arg instanceof Date ? - arg.toString() : - arg; - }); -} +export function functionArgumentsPrefix(name: string, fn: RedisFunction) { + const prefix: Array = [ + fn.IS_READ_ONLY ? 'FCALL_RO' : 'FCALL', + name + ]; -export function transformCommandReply( - command: C, - rawReply: unknown, - preserved: unknown -): RedisCommandReply { - if (!command.transformReply) { - return rawReply as RedisCommandReply; - } + if (fn.NUMBER_OF_KEYS !== undefined) { + prefix.push(fn.NUMBER_OF_KEYS.toString()); + } - return command.transformReply(rawReply, preserved); + return prefix; } -export function fCallArguments( - name: RedisCommandArgument, - fn: RedisFunction, - args: RedisCommandArguments -): RedisCommandArguments { - const actualArgs: RedisCommandArguments = [ - fn.IS_READ_ONLY ? 'FCALL_RO' : 'FCALL', - name - ]; - - if (fn.NUMBER_OF_KEYS !== undefined) { - actualArgs.push(fn.NUMBER_OF_KEYS.toString()); - } +export function scriptArgumentsPrefix(script: RedisScript) { + const prefix: Array = [ + script.IS_READ_ONLY ? 'EVALSHA_RO' : 'EVALSHA', + script.SHA1 + ]; - actualArgs.push(...args); + if (script.NUMBER_OF_KEYS !== undefined) { + prefix.push(script.NUMBER_OF_KEYS.toString()); + } - return actualArgs; + return prefix; } diff --git a/packages/client/lib/commands/ACL_CAT.spec.ts b/packages/client/lib/commands/ACL_CAT.spec.ts index 521871a1c6b..2ce9d7db922 100644 --- a/packages/client/lib/commands/ACL_CAT.spec.ts +++ b/packages/client/lib/commands/ACL_CAT.spec.ts @@ -1,23 +1,31 @@ -import { strict as assert } from 'assert'; -import testUtils from '../test-utils'; -import { transformArguments } from './ACL_CAT'; +import { strict as assert } from 'node:assert'; +import testUtils, { GLOBAL } from '../test-utils'; +import ACL_CAT from './ACL_CAT'; describe('ACL CAT', () => { - testUtils.isVersionGreaterThanHook([6]); + testUtils.isVersionGreaterThanHook([6]); - describe('transformArguments', () => { - it('simple', () => { - assert.deepEqual( - transformArguments(), - ['ACL', 'CAT'] - ); - }); + describe('transformArguments', () => { + it('simple', () => { + assert.deepEqual( + ACL_CAT.transformArguments(), + ['ACL', 'CAT'] + ); + }); - it('with categoryName', () => { - assert.deepEqual( - transformArguments('dangerous'), - ['ACL', 'CAT', 'dangerous'] - ); - }); + it('with categoryName', () => { + assert.deepEqual( + ACL_CAT.transformArguments('dangerous'), + ['ACL', 'CAT', 'dangerous'] + ); }); + }); + + testUtils.testWithClient('client.aclCat', async client => { + const categories = await client.aclCat(); + assert.ok(Array.isArray(categories)); + for (const category of categories) { + assert.equal(typeof category, 'string'); + } + }, GLOBAL.SERVERS.OPEN); }); diff --git a/packages/client/lib/commands/ACL_CAT.ts b/packages/client/lib/commands/ACL_CAT.ts index 161546cfbe9..dd4762239a7 100644 --- a/packages/client/lib/commands/ACL_CAT.ts +++ b/packages/client/lib/commands/ACL_CAT.ts @@ -1,13 +1,16 @@ -import { RedisCommandArgument, RedisCommandArguments } from '.'; +import { RedisArgument, ArrayReply, BlobStringReply, Command } from '../RESP/types'; -export function transformArguments(categoryName?: RedisCommandArgument): RedisCommandArguments { - const args: RedisCommandArguments = ['ACL', 'CAT']; +export default { + FIRST_KEY_INDEX: undefined, + IS_READ_ONLY: true, + transformArguments(categoryName?: RedisArgument) { + const args: Array = ['ACL', 'CAT']; if (categoryName) { - args.push(categoryName); + args.push(categoryName); } return args; -} - -export declare function transformReply(): Array; + }, + transformReply: undefined as unknown as () => ArrayReply +} as const satisfies Command; diff --git a/packages/client/lib/commands/ACL_DELUSER.spec.ts b/packages/client/lib/commands/ACL_DELUSER.spec.ts index 5c5ea2fa2a3..d6acbb22230 100644 --- a/packages/client/lib/commands/ACL_DELUSER.spec.ts +++ b/packages/client/lib/commands/ACL_DELUSER.spec.ts @@ -1,30 +1,30 @@ -import { strict as assert } from 'assert'; +import { strict as assert } from 'node:assert'; import testUtils, { GLOBAL } from '../test-utils'; -import { transformArguments } from './ACL_DELUSER'; +import ACL_DELUSER from './ACL_DELUSER'; describe('ACL DELUSER', () => { - testUtils.isVersionGreaterThanHook([6]); + testUtils.isVersionGreaterThanHook([6]); - describe('transformArguments', () => { - it('string', () => { - assert.deepEqual( - transformArguments('username'), - ['ACL', 'DELUSER', 'username'] - ); - }); + describe('transformArguments', () => { + it('string', () => { + assert.deepEqual( + ACL_DELUSER.transformArguments('username'), + ['ACL', 'DELUSER', 'username'] + ); + }); - it('array', () => { - assert.deepEqual( - transformArguments(['1', '2']), - ['ACL', 'DELUSER', '1', '2'] - ); - }); + it('array', () => { + assert.deepEqual( + ACL_DELUSER.transformArguments(['1', '2']), + ['ACL', 'DELUSER', '1', '2'] + ); }); + }); - testUtils.testWithClient('client.aclDelUser', async client => { - assert.equal( - await client.aclDelUser('dosenotexists'), - 0 - ); - }, GLOBAL.SERVERS.OPEN); + testUtils.testWithClient('client.aclDelUser', async client => { + assert.equal( + typeof await client.aclDelUser('user'), + 'number' + ); + }, GLOBAL.SERVERS.OPEN); }); diff --git a/packages/client/lib/commands/ACL_DELUSER.ts b/packages/client/lib/commands/ACL_DELUSER.ts index 25ed1a10300..c0f8e15d672 100644 --- a/packages/client/lib/commands/ACL_DELUSER.ts +++ b/packages/client/lib/commands/ACL_DELUSER.ts @@ -1,10 +1,11 @@ -import { RedisCommandArgument, RedisCommandArguments } from '.'; -import { pushVerdictArguments } from './generic-transformers'; +import { NumberReply, Command } from '../RESP/types'; +import { RedisVariadicArgument, pushVariadicArguments } from './generic-transformers'; -export function transformArguments( - username: RedisCommandArgument | Array -): RedisCommandArguments { - return pushVerdictArguments(['ACL', 'DELUSER'], username); -} - -export declare function transformReply(): number; +export default { + FIRST_KEY_INDEX: undefined, + IS_READ_ONLY: true, + transformArguments(username: RedisVariadicArgument) { + return pushVariadicArguments(['ACL', 'DELUSER'], username); + }, + transformReply: undefined as unknown as () => NumberReply +} as const satisfies Command; diff --git a/packages/client/lib/commands/ACL_DRYRUN.spec.ts b/packages/client/lib/commands/ACL_DRYRUN.spec.ts index 3154689c29e..519092e0114 100644 --- a/packages/client/lib/commands/ACL_DRYRUN.spec.ts +++ b/packages/client/lib/commands/ACL_DRYRUN.spec.ts @@ -1,21 +1,21 @@ -import { strict as assert } from 'assert'; +import { strict as assert } from 'node:assert'; import testUtils, { GLOBAL } from '../test-utils'; -import { transformArguments } from './ACL_DRYRUN'; +import ACL_DRYRUN from './ACL_DRYRUN'; describe('ACL DRYRUN', () => { - testUtils.isVersionGreaterThanHook([7]); + testUtils.isVersionGreaterThanHook([7]); - it('transformArguments', () => { - assert.deepEqual( - transformArguments('default', ['GET', 'key']), - ['ACL', 'DRYRUN', 'default', 'GET', 'key'] - ); - }); + it('transformArguments', () => { + assert.deepEqual( + ACL_DRYRUN.transformArguments('default', ['GET', 'key']), + ['ACL', 'DRYRUN', 'default', 'GET', 'key'] + ); + }); - testUtils.testWithClient('client.aclDryRun', async client => { - assert.equal( - await client.aclDryRun('default', ['GET', 'key']), - 'OK' - ); - }, GLOBAL.SERVERS.OPEN); + testUtils.testWithClient('client.aclDryRun', async client => { + assert.equal( + await client.aclDryRun('default', ['GET', 'key']), + 'OK' + ); + }, GLOBAL.SERVERS.OPEN); }); diff --git a/packages/client/lib/commands/ACL_DRYRUN.ts b/packages/client/lib/commands/ACL_DRYRUN.ts index 95eed95066c..257f0fe61e2 100644 --- a/packages/client/lib/commands/ACL_DRYRUN.ts +++ b/packages/client/lib/commands/ACL_DRYRUN.ts @@ -1,18 +1,16 @@ -import { RedisCommandArgument, RedisCommandArguments } from '.'; +import { RedisArgument, SimpleStringReply, BlobStringReply, Command } from '../RESP/types'; -export const IS_READ_ONLY = true; - -export function transformArguments( - username: RedisCommandArgument, - command: Array -): RedisCommandArguments { +export default { + FIRST_KEY_INDEX: undefined, + IS_READ_ONLY: true, + transformArguments(username: RedisArgument, command: Array) { return [ - 'ACL', - 'DRYRUN', - username, - ...command + 'ACL', + 'DRYRUN', + username, + ...command ]; -} - -export declare function transformReply(): RedisCommandArgument; + }, + transformReply: undefined as unknown as () => SimpleStringReply<'OK'> | BlobStringReply +} as const satisfies Command; diff --git a/packages/client/lib/commands/ACL_GENPASS.spec.ts b/packages/client/lib/commands/ACL_GENPASS.spec.ts index 3b2a022f972..44c1e167eb7 100644 --- a/packages/client/lib/commands/ACL_GENPASS.spec.ts +++ b/packages/client/lib/commands/ACL_GENPASS.spec.ts @@ -1,23 +1,30 @@ -import { strict as assert } from 'assert'; -import testUtils from '../test-utils'; -import { transformArguments } from './ACL_GENPASS'; +import { strict as assert } from 'node:assert'; +import testUtils, { GLOBAL } from '../test-utils'; +import ACL_GENPASS from './ACL_GENPASS'; describe('ACL GENPASS', () => { - testUtils.isVersionGreaterThanHook([6]); + testUtils.isVersionGreaterThanHook([6]); - describe('transformArguments', () => { - it('simple', () => { - assert.deepEqual( - transformArguments(), - ['ACL', 'GENPASS'] - ); - }); + describe('transformArguments', () => { + it('simple', () => { + assert.deepEqual( + ACL_GENPASS.transformArguments(), + ['ACL', 'GENPASS'] + ); + }); - it('with bits', () => { - assert.deepEqual( - transformArguments(128), - ['ACL', 'GENPASS', '128'] - ); - }); + it('with bits', () => { + assert.deepEqual( + ACL_GENPASS.transformArguments(128), + ['ACL', 'GENPASS', '128'] + ); }); + }); + + testUtils.testWithClient('client.aclGenPass', async client => { + assert.equal( + typeof await client.aclGenPass(), + 'string' + ); + }, GLOBAL.SERVERS.OPEN); }); diff --git a/packages/client/lib/commands/ACL_GENPASS.ts b/packages/client/lib/commands/ACL_GENPASS.ts index 91a71e220e0..be89ff90a9a 100644 --- a/packages/client/lib/commands/ACL_GENPASS.ts +++ b/packages/client/lib/commands/ACL_GENPASS.ts @@ -1,13 +1,17 @@ -import { RedisCommandArgument, RedisCommandArguments } from '.'; +import { BlobStringReply, Command } from '../RESP/types'; -export function transformArguments(bits?: number): RedisCommandArguments { +export default { + FIRST_KEY_INDEX: undefined, + IS_READ_ONLY: true, + transformArguments(bits?: number) { const args = ['ACL', 'GENPASS']; if (bits) { - args.push(bits.toString()); + args.push(bits.toString()); } return args; -} + }, + transformReply: undefined as unknown as () => BlobStringReply +} as const satisfies Command; -export declare function transformReply(): RedisCommandArgument; diff --git a/packages/client/lib/commands/ACL_GETUSER.spec.ts b/packages/client/lib/commands/ACL_GETUSER.spec.ts index 4cd693db9ce..47351571127 100644 --- a/packages/client/lib/commands/ACL_GETUSER.spec.ts +++ b/packages/client/lib/commands/ACL_GETUSER.spec.ts @@ -1,34 +1,34 @@ -import { strict as assert } from 'assert'; +import { strict as assert } from 'node:assert'; import testUtils, { GLOBAL } from '../test-utils'; -import { transformArguments } from './ACL_GETUSER'; +import ACL_GETUSER from './ACL_GETUSER'; describe('ACL GETUSER', () => { - testUtils.isVersionGreaterThanHook([6]); + testUtils.isVersionGreaterThanHook([6]); - it('transformArguments', () => { - assert.deepEqual( - transformArguments('username'), - ['ACL', 'GETUSER', 'username'] - ); - }); + it('transformArguments', () => { + assert.deepEqual( + ACL_GETUSER.transformArguments('username'), + ['ACL', 'GETUSER', 'username'] + ); + }); - testUtils.testWithClient('client.aclGetUser', async client => { - const reply = await client.aclGetUser('default'); + testUtils.testWithClient('client.aclGetUser', async client => { + const reply = await client.aclGetUser('default'); - assert.ok(Array.isArray(reply.passwords)); - assert.equal(typeof reply.commands, 'string'); - assert.ok(Array.isArray(reply.flags)); + assert.ok(Array.isArray(reply.passwords)); + assert.equal(typeof reply.commands, 'string'); + assert.ok(Array.isArray(reply.flags)); - if (testUtils.isVersionGreaterThan([7])) { - assert.equal(typeof reply.keys, 'string'); - assert.equal(typeof reply.channels, 'string'); - assert.ok(Array.isArray(reply.selectors)); - } else { - assert.ok(Array.isArray(reply.keys)); + if (testUtils.isVersionGreaterThan([7])) { + assert.equal(typeof reply.keys, 'string'); + assert.equal(typeof reply.channels, 'string'); + assert.ok(Array.isArray(reply.selectors)); + } else { + assert.ok(Array.isArray(reply.keys)); - if (testUtils.isVersionGreaterThan([6, 2])) { - assert.ok(Array.isArray(reply.channels)); - } - } - }, GLOBAL.SERVERS.OPEN); + if (testUtils.isVersionGreaterThan([6, 2])) { + assert.ok(Array.isArray(reply.channels)); + } + } + }, GLOBAL.SERVERS.OPEN); }); diff --git a/packages/client/lib/commands/ACL_GETUSER.ts b/packages/client/lib/commands/ACL_GETUSER.ts index 818a945bac1..cbbf48a4c69 100644 --- a/packages/client/lib/commands/ACL_GETUSER.ts +++ b/packages/client/lib/commands/ACL_GETUSER.ts @@ -1,40 +1,43 @@ -import { RedisCommandArgument, RedisCommandArguments } from '.'; +import { RedisArgument, TuplesToMapReply, BlobStringReply, ArrayReply, UnwrapReply, Resp2Reply, Command } from '../RESP/types'; -export function transformArguments(username: RedisCommandArgument): RedisCommandArguments { - return ['ACL', 'GETUSER', username]; -} - -type AclGetUserRawReply = [ - 'flags', - Array, - 'passwords', - Array, - 'commands', - RedisCommandArgument, - 'keys', - Array | RedisCommandArgument, - 'channels', - Array | RedisCommandArgument, - 'selectors' | undefined, - Array> | undefined -]; +type AclUser = TuplesToMapReply<[ + [BlobStringReply<'flags'>, ArrayReply], + [BlobStringReply<'passwords'>, ArrayReply], + [BlobStringReply<'commands'>, BlobStringReply], + /** changed to BlobStringReply in 7.0 */ + [BlobStringReply<'keys'>, ArrayReply | BlobStringReply], + /** added in 6.2, changed to BlobStringReply in 7.0 */ + [BlobStringReply<'channels'>, ArrayReply | BlobStringReply], + /** added in 7.0 */ + [BlobStringReply<'selectors'>, ArrayReply, BlobStringReply], + [BlobStringReply<'keys'>, BlobStringReply], + [BlobStringReply<'channels'>, BlobStringReply] + ]>>], +]>; -interface AclUser { - flags: Array; - passwords: Array; - commands: RedisCommandArgument; - keys: Array | RedisCommandArgument; - channels: Array | RedisCommandArgument; - selectors?: Array>; -} - -export function transformReply(reply: AclGetUserRawReply): AclUser { - return { - flags: reply[1], - passwords: reply[3], - commands: reply[5], - keys: reply[7], - channels: reply[9], - selectors: reply[11] - }; -} +export default { + FIRST_KEY_INDEX: undefined, + IS_READ_ONLY: true, + transformArguments(username: RedisArgument) { + return ['ACL', 'GETUSER', username]; + }, + transformReply: { + 2: (reply: UnwrapReply>) => ({ + flags: reply[1], + passwords: reply[3], + commands: reply[5], + keys: reply[7], + channels: reply[9], + selectors: (reply[11] as unknown as UnwrapReply)?.map(selector => { + const inferred = selector as unknown as UnwrapReply; + return { + commands: inferred[1], + keys: inferred[3], + channels: inferred[5] + }; + }) + }), + 3: undefined as unknown as () => AclUser + } +} as const satisfies Command; diff --git a/packages/client/lib/commands/ACL_LIST.spec.ts b/packages/client/lib/commands/ACL_LIST.spec.ts index 9f9156db7a2..b188cae30b0 100644 --- a/packages/client/lib/commands/ACL_LIST.spec.ts +++ b/packages/client/lib/commands/ACL_LIST.spec.ts @@ -1,14 +1,22 @@ -import { strict as assert } from 'assert'; -import testUtils from '../test-utils'; -import { transformArguments } from './ACL_LIST'; +import { strict as assert } from 'node:assert'; +import testUtils, { GLOBAL } from '../test-utils'; +import ACL_LIST from './ACL_LIST'; describe('ACL LIST', () => { - testUtils.isVersionGreaterThanHook([6]); + testUtils.isVersionGreaterThanHook([6]); - it('transformArguments', () => { - assert.deepEqual( - transformArguments(), - ['ACL', 'LIST'] - ); - }); + it('transformArguments', () => { + assert.deepEqual( + ACL_LIST.transformArguments(), + ['ACL', 'LIST'] + ); + }); + + testUtils.testWithClient('client.aclList', async client => { + const users = await client.aclList(); + assert.ok(Array.isArray(users)); + for (const user of users) { + assert.equal(typeof user, 'string'); + } + }, GLOBAL.SERVERS.OPEN); }); diff --git a/packages/client/lib/commands/ACL_LIST.ts b/packages/client/lib/commands/ACL_LIST.ts index ae523fe9ce9..1a831a4987c 100644 --- a/packages/client/lib/commands/ACL_LIST.ts +++ b/packages/client/lib/commands/ACL_LIST.ts @@ -1,7 +1,10 @@ -import { RedisCommandArgument, RedisCommandArguments } from '.'; +import { ArrayReply, BlobStringReply, Command } from '../RESP/types'; -export function transformArguments(): RedisCommandArguments { +export default { + FIRST_KEY_INDEX: undefined, + IS_READ_ONLY: true, + transformArguments() { return ['ACL', 'LIST']; -} - -export declare function transformReply(): Array; + }, + transformReply: undefined as unknown as () => ArrayReply +} as const satisfies Command; diff --git a/packages/client/lib/commands/ACL_LOAD.spec.ts b/packages/client/lib/commands/ACL_LOAD.spec.ts index 703d5eeb252..68552164ce0 100644 --- a/packages/client/lib/commands/ACL_LOAD.spec.ts +++ b/packages/client/lib/commands/ACL_LOAD.spec.ts @@ -1,14 +1,14 @@ -import { strict as assert } from 'assert'; +import { strict as assert } from 'node:assert'; import testUtils from '../test-utils'; -import { transformArguments } from './ACL_SAVE'; +import ACL_LOAD from './ACL_LOAD'; -describe('ACL SAVE', () => { - testUtils.isVersionGreaterThanHook([6]); +describe('ACL LOAD', () => { + testUtils.isVersionGreaterThanHook([6]); - it('transformArguments', () => { - assert.deepEqual( - transformArguments(), - ['ACL', 'SAVE'] - ); - }); + it('transformArguments', () => { + assert.deepEqual( + ACL_LOAD.transformArguments(), + ['ACL', 'LOAD'] + ); + }); }); diff --git a/packages/client/lib/commands/ACL_LOAD.ts b/packages/client/lib/commands/ACL_LOAD.ts index 88309102b95..39587829b17 100644 --- a/packages/client/lib/commands/ACL_LOAD.ts +++ b/packages/client/lib/commands/ACL_LOAD.ts @@ -1,7 +1,10 @@ -import { RedisCommandArgument, RedisCommandArguments } from '.'; +import { SimpleStringReply, Command } from '../RESP/types'; -export function transformArguments(): RedisCommandArguments { +export default { + FIRST_KEY_INDEX: undefined, + IS_READ_ONLY: true, + transformArguments() { return ['ACL', 'LOAD']; -} - -export declare function transformReply(): RedisCommandArgument; + }, + transformReply: undefined as unknown as () => SimpleStringReply<'OK'> +} as const satisfies Command; diff --git a/packages/client/lib/commands/ACL_LOG.spec.ts b/packages/client/lib/commands/ACL_LOG.spec.ts index a8296d31da6..b85a7076f4a 100644 --- a/packages/client/lib/commands/ACL_LOG.spec.ts +++ b/packages/client/lib/commands/ACL_LOG.spec.ts @@ -1,53 +1,50 @@ -import { strict as assert } from 'assert'; -import testUtils from '../test-utils'; -import { transformArguments, transformReply } from './ACL_LOG'; +import { strict as assert } from 'node:assert'; +import testUtils, { GLOBAL } from '../test-utils'; +import ACL_LOG from './ACL_LOG'; describe('ACL LOG', () => { - testUtils.isVersionGreaterThanHook([6]); + testUtils.isVersionGreaterThanHook([6]); - describe('transformArguments', () => { - it('simple', () => { - assert.deepEqual( - transformArguments(), - ['ACL', 'LOG'] - ); - }); - - it('with count', () => { - assert.deepEqual( - transformArguments(10), - ['ACL', 'LOG', '10'] - ); - }); + describe('transformArguments', () => { + it('simple', () => { + assert.deepEqual( + ACL_LOG.transformArguments(), + ['ACL', 'LOG'] + ); }); - it('transformReply', () => { - assert.deepEqual( - transformReply([[ - 'count', - 1, - 'reason', - 'auth', - 'context', - 'toplevel', - 'object', - 'AUTH', - 'username', - 'someuser', - 'age-seconds', - '4.096', - 'client-info', - 'id=6 addr=127.0.0.1:63026 fd=8 name= age=9 idle=0 flags=N db=0 sub=0 psub=0 multi=-1 qbuf=48 qbuf-free=32720 obl=0 oll=0 omem=0 events=r cmd=auth user=default' - ]]), - [{ - count: 1, - reason: 'auth', - context: 'toplevel', - object: 'AUTH', - username: 'someuser', - ageSeconds: 4.096, - clientInfo: 'id=6 addr=127.0.0.1:63026 fd=8 name= age=9 idle=0 flags=N db=0 sub=0 psub=0 multi=-1 qbuf=48 qbuf-free=32720 obl=0 oll=0 omem=0 events=r cmd=auth user=default' - }] - ); + it('with count', () => { + assert.deepEqual( + ACL_LOG.transformArguments(10), + ['ACL', 'LOG', '10'] + ); }); + }); + + testUtils.testWithClient('client.aclLog', async client => { + // make sure to create one log + await assert.rejects( + client.auth({ + username: 'incorrect', + password: 'incorrect' + }) + ); + + const logs = await client.aclLog(); + assert.ok(Array.isArray(logs)); + for (const log of logs) { + assert.equal(typeof log.count, 'number'); + assert.equal(typeof log.reason, 'string'); + assert.equal(typeof log.context, 'string'); + assert.equal(typeof log.object, 'string'); + assert.equal(typeof log.username, 'string'); + assert.equal(typeof log['age-seconds'], 'number'); + assert.equal(typeof log['client-info'], 'string'); + if (testUtils.isVersionGreaterThan([7, 2])) { + assert.equal(typeof log['entry-id'], 'number'); + assert.equal(typeof log['timestamp-created'], 'number'); + assert.equal(typeof log['timestamp-last-updated'], 'number'); + } + } + }, GLOBAL.SERVERS.OPEN); }); diff --git a/packages/client/lib/commands/ACL_LOG.ts b/packages/client/lib/commands/ACL_LOG.ts index 0fd9aa6f19d..0f0a976e093 100644 --- a/packages/client/lib/commands/ACL_LOG.ts +++ b/packages/client/lib/commands/ACL_LOG.ts @@ -1,50 +1,52 @@ -import { RedisCommandArgument, RedisCommandArguments } from '.'; +import { ArrayReply, TuplesToMapReply, BlobStringReply, NumberReply, DoubleReply, UnwrapReply, Resp2Reply, Command, TypeMapping } from '../RESP/types'; +import { transformDoubleReply } from './generic-transformers'; -export function transformArguments(count?: number): RedisCommandArguments { +export type AclLogReply = ArrayReply, NumberReply], + [BlobStringReply<'reason'>, BlobStringReply], + [BlobStringReply<'context'>, BlobStringReply], + [BlobStringReply<'object'>, BlobStringReply], + [BlobStringReply<'username'>, BlobStringReply], + [BlobStringReply<'age-seconds'>, DoubleReply], + [BlobStringReply<'client-info'>, BlobStringReply], + /** added in 7.0 */ + [BlobStringReply<'entry-id'>, NumberReply], + /** added in 7.0 */ + [BlobStringReply<'timestamp-created'>, NumberReply], + /** added in 7.0 */ + [BlobStringReply<'timestamp-last-updated'>, NumberReply] +]>>; + +export default { + FIRST_KEY_INDEX: undefined, + IS_READ_ONLY: true, + transformArguments(count?: number) { const args = ['ACL', 'LOG']; - if (count) { - args.push(count.toString()); + if (count !== undefined) { + args.push(count.toString()); } return args; -} - -type AclLogRawReply = [ - _: RedisCommandArgument, - count: number, - _: RedisCommandArgument, - reason: RedisCommandArgument, - _: RedisCommandArgument, - context: RedisCommandArgument, - _: RedisCommandArgument, - object: RedisCommandArgument, - _: RedisCommandArgument, - username: RedisCommandArgument, - _: RedisCommandArgument, - ageSeconds: RedisCommandArgument, - _: RedisCommandArgument, - clientInfo: RedisCommandArgument -]; - -interface AclLog { - count: number; - reason: RedisCommandArgument; - context: RedisCommandArgument; - object: RedisCommandArgument; - username: RedisCommandArgument; - ageSeconds: number; - clientInfo: RedisCommandArgument; -} - -export function transformReply(reply: Array): Array { - return reply.map(log => ({ - count: log[1], - reason: log[3], - context: log[5], - object: log[7], - username: log[9], - ageSeconds: Number(log[11]), - clientInfo: log[13] - })); -} + }, + transformReply: { + 2: (reply: UnwrapReply>, preserve?: any, typeMapping?: TypeMapping) => { + return reply.map(item => { + const inferred = item as unknown as UnwrapReply; + return { + count: inferred[1], + reason: inferred[3], + context: inferred[5], + object: inferred[7], + username: inferred[9], + 'age-seconds': transformDoubleReply[2](inferred[11], preserve, typeMapping), + 'client-info': inferred[13], + 'entry-id': inferred[15], + 'timestamp-created': inferred[17], + 'timestamp-last-updated': inferred[19] + }; + }) + }, + 3: undefined as unknown as () => AclLogReply + } +} as const satisfies Command; diff --git a/packages/client/lib/commands/ACL_LOG_RESET.spec.ts b/packages/client/lib/commands/ACL_LOG_RESET.spec.ts index 5d26e45d04f..8849440c1a6 100644 --- a/packages/client/lib/commands/ACL_LOG_RESET.spec.ts +++ b/packages/client/lib/commands/ACL_LOG_RESET.spec.ts @@ -1,14 +1,21 @@ -import { strict as assert } from 'assert'; -import testUtils from '../test-utils'; -import { transformArguments } from './ACL_LOG_RESET'; +import { strict as assert } from 'node:assert'; +import testUtils, { GLOBAL } from '../test-utils'; +import ACL_LOG_RESET from './ACL_LOG_RESET'; describe('ACL LOG RESET', () => { - testUtils.isVersionGreaterThanHook([6]); + testUtils.isVersionGreaterThanHook([6]); - it('transformArguments', () => { - assert.deepEqual( - transformArguments(), - ['ACL', 'LOG', 'RESET'] - ); - }); + it('transformArguments', () => { + assert.deepEqual( + ACL_LOG_RESET.transformArguments(), + ['ACL', 'LOG', 'RESET'] + ); + }); + + testUtils.testWithClient('client.aclLogReset', async client => { + assert.equal( + await client.aclLogReset(), + 'OK' + ); + }, GLOBAL.SERVERS.OPEN); }); diff --git a/packages/client/lib/commands/ACL_LOG_RESET.ts b/packages/client/lib/commands/ACL_LOG_RESET.ts index 8ff0be4f8b9..91d58d538e9 100644 --- a/packages/client/lib/commands/ACL_LOG_RESET.ts +++ b/packages/client/lib/commands/ACL_LOG_RESET.ts @@ -1,7 +1,11 @@ -import { RedisCommandArgument, RedisCommandArguments } from '.'; +import { SimpleStringReply, Command } from '../RESP/types'; +import ACL_LOG from './ACL_LOG'; -export function transformArguments(): RedisCommandArguments { +export default { + FIRST_KEY_INDEX: undefined, + IS_READ_ONLY: ACL_LOG.IS_READ_ONLY, + transformArguments() { return ['ACL', 'LOG', 'RESET']; -} - -export declare function transformReply(): RedisCommandArgument; + }, + transformReply: undefined as unknown as () => SimpleStringReply<'OK'> +} as const satisfies Command; diff --git a/packages/client/lib/commands/ACL_SAVE.spec.ts b/packages/client/lib/commands/ACL_SAVE.spec.ts index f4de312bb7a..1fe402867e7 100644 --- a/packages/client/lib/commands/ACL_SAVE.spec.ts +++ b/packages/client/lib/commands/ACL_SAVE.spec.ts @@ -1,14 +1,14 @@ -import { strict as assert } from 'assert'; +import { strict as assert } from 'node:assert'; import testUtils from '../test-utils'; -import { transformArguments } from './ACL_LOAD'; +import ACL_SAVE from './ACL_SAVE'; -describe('ACL LOAD', () => { - testUtils.isVersionGreaterThanHook([6]); +describe('ACL SAVE', () => { + testUtils.isVersionGreaterThanHook([6]); - it('transformArguments', () => { - assert.deepEqual( - transformArguments(), - ['ACL', 'LOAD'] - ); - }); + it('transformArguments', () => { + assert.deepEqual( + ACL_SAVE.transformArguments(), + ['ACL', 'SAVE'] + ); + }); }); diff --git a/packages/client/lib/commands/ACL_SAVE.ts b/packages/client/lib/commands/ACL_SAVE.ts index e57cd697297..8c2e2dab115 100644 --- a/packages/client/lib/commands/ACL_SAVE.ts +++ b/packages/client/lib/commands/ACL_SAVE.ts @@ -1,7 +1,10 @@ -import { RedisCommandArgument, RedisCommandArguments } from '.'; +import { SimpleStringReply, Command } from '../RESP/types'; -export function transformArguments(): RedisCommandArguments { +export default { + FIRST_KEY_INDEX: undefined, + IS_READ_ONLY: true, + transformArguments() { return ['ACL', 'SAVE']; -} - -export declare function transformReply(): RedisCommandArgument; + }, + transformReply: undefined as unknown as () => SimpleStringReply<'OK'> +} as const satisfies Command; diff --git a/packages/client/lib/commands/ACL_SETUSER.spec.ts b/packages/client/lib/commands/ACL_SETUSER.spec.ts index 9c8ea8a59e0..10aea62ed02 100644 --- a/packages/client/lib/commands/ACL_SETUSER.spec.ts +++ b/packages/client/lib/commands/ACL_SETUSER.spec.ts @@ -1,23 +1,23 @@ -import { strict as assert } from 'assert'; +import { strict as assert } from 'node:assert'; import testUtils from '../test-utils'; -import { transformArguments } from './ACL_SETUSER'; +import ACL_SETUSER from './ACL_SETUSER'; describe('ACL SETUSER', () => { - testUtils.isVersionGreaterThanHook([6]); + testUtils.isVersionGreaterThanHook([6]); - describe('transformArguments', () => { - it('string', () => { - assert.deepEqual( - transformArguments('username', 'allkeys'), - ['ACL', 'SETUSER', 'username', 'allkeys'] - ); - }); + describe('transformArguments', () => { + it('string', () => { + assert.deepEqual( + ACL_SETUSER.transformArguments('username', 'allkeys'), + ['ACL', 'SETUSER', 'username', 'allkeys'] + ); + }); - it('array', () => { - assert.deepEqual( - transformArguments('username', ['allkeys', 'allchannels']), - ['ACL', 'SETUSER', 'username', 'allkeys', 'allchannels'] - ); - }); + it('array', () => { + assert.deepEqual( + ACL_SETUSER.transformArguments('username', ['allkeys', 'allchannels']), + ['ACL', 'SETUSER', 'username', 'allkeys', 'allchannels'] + ); }); + }); }); diff --git a/packages/client/lib/commands/ACL_SETUSER.ts b/packages/client/lib/commands/ACL_SETUSER.ts index a12cc8ed24e..c99fec3d9ba 100644 --- a/packages/client/lib/commands/ACL_SETUSER.ts +++ b/packages/client/lib/commands/ACL_SETUSER.ts @@ -1,11 +1,11 @@ -import { RedisCommandArgument, RedisCommandArguments } from '.'; -import { pushVerdictArguments } from './generic-transformers'; +import { RedisArgument, SimpleStringReply, Command } from '../RESP/types'; +import { RedisVariadicArgument, pushVariadicArguments } from './generic-transformers'; -export function transformArguments( - username: RedisCommandArgument, - rule: RedisCommandArgument | Array -): RedisCommandArguments { - return pushVerdictArguments(['ACL', 'SETUSER', username], rule); -} - -export declare function transformReply(): RedisCommandArgument; +export default { + FIRST_KEY_INDEX: undefined, + IS_READ_ONLY: true, + transformArguments(username: RedisArgument, rule: RedisVariadicArgument) { + return pushVariadicArguments(['ACL', 'SETUSER', username], rule); + }, + transformReply: undefined as unknown as () => SimpleStringReply<'OK'> +} as const satisfies Command; diff --git a/packages/client/lib/commands/ACL_USERS.spec.ts b/packages/client/lib/commands/ACL_USERS.spec.ts index 35e06ce8494..2d433d4ec1e 100644 --- a/packages/client/lib/commands/ACL_USERS.spec.ts +++ b/packages/client/lib/commands/ACL_USERS.spec.ts @@ -1,14 +1,14 @@ -import { strict as assert } from 'assert'; +import { strict as assert } from 'node:assert'; import testUtils from '../test-utils'; -import { transformArguments } from './ACL_USERS'; +import ACL_USERS from './ACL_USERS'; describe('ACL USERS', () => { - testUtils.isVersionGreaterThanHook([6]); + testUtils.isVersionGreaterThanHook([6]); - it('transformArguments', () => { - assert.deepEqual( - transformArguments(), - ['ACL', 'USERS'] - ); - }); + it('transformArguments', () => { + assert.deepEqual( + ACL_USERS.transformArguments(), + ['ACL', 'USERS'] + ); + }); }); diff --git a/packages/client/lib/commands/ACL_USERS.ts b/packages/client/lib/commands/ACL_USERS.ts index 7970a262e26..ee8c619f25f 100644 --- a/packages/client/lib/commands/ACL_USERS.ts +++ b/packages/client/lib/commands/ACL_USERS.ts @@ -1,7 +1,10 @@ -import { RedisCommandArgument, RedisCommandArguments } from '.'; +import { ArrayReply, BlobStringReply, Command } from '../RESP/types'; -export function transformArguments(): RedisCommandArguments { +export default { + FIRST_KEY_INDEX: undefined, + IS_READ_ONLY: true, + transformArguments() { return ['ACL', 'USERS']; -} - -export declare function transformReply(): Array; + }, + transformReply: undefined as unknown as () => ArrayReply +} as const satisfies Command; diff --git a/packages/client/lib/commands/ACL_WHOAMI.spec.ts b/packages/client/lib/commands/ACL_WHOAMI.spec.ts index 32eb327beea..24a5cbd1d63 100644 --- a/packages/client/lib/commands/ACL_WHOAMI.spec.ts +++ b/packages/client/lib/commands/ACL_WHOAMI.spec.ts @@ -1,14 +1,14 @@ -import { strict as assert } from 'assert'; +import { strict as assert } from 'node:assert'; import testUtils from '../test-utils'; -import { transformArguments } from './ACL_WHOAMI'; +import ACL_WHOAMI from './ACL_WHOAMI'; describe('ACL WHOAMI', () => { - testUtils.isVersionGreaterThanHook([6]); + testUtils.isVersionGreaterThanHook([6]); - it('transformArguments', () => { - assert.deepEqual( - transformArguments(), - ['ACL', 'WHOAMI'] - ); - }); + it('transformArguments', () => { + assert.deepEqual( + ACL_WHOAMI.transformArguments(), + ['ACL', 'WHOAMI'] + ); + }); }); diff --git a/packages/client/lib/commands/ACL_WHOAMI.ts b/packages/client/lib/commands/ACL_WHOAMI.ts index 3c41171638e..81a1c84a3c6 100644 --- a/packages/client/lib/commands/ACL_WHOAMI.ts +++ b/packages/client/lib/commands/ACL_WHOAMI.ts @@ -1,7 +1,10 @@ -import { RedisCommandArgument, RedisCommandArguments } from '.'; +import { BlobStringReply, Command } from '../RESP/types'; -export function transformArguments(): RedisCommandArguments { +export default { + FIRST_KEY_INDEX: undefined, + IS_READ_ONLY: true, + transformArguments() { return ['ACL', 'WHOAMI']; -} - -export declare function transformReply(): RedisCommandArgument; + }, + transformReply: undefined as unknown as () => BlobStringReply +} as const satisfies Command; diff --git a/packages/client/lib/commands/APPEND.spec.ts b/packages/client/lib/commands/APPEND.spec.ts index 23353866843..ca18a00ac42 100644 --- a/packages/client/lib/commands/APPEND.spec.ts +++ b/packages/client/lib/commands/APPEND.spec.ts @@ -1,11 +1,22 @@ -import { strict as assert } from 'assert'; -import { transformArguments } from './APPEND'; +import { strict as assert } from 'node:assert'; +import testUtils, { GLOBAL } from '../test-utils'; +import APPEND from './APPEND'; describe('APPEND', () => { - it('transformArguments', () => { - assert.deepEqual( - transformArguments('key', 'value'), - ['APPEND', 'key', 'value'] - ); - }); + it('transformArguments', () => { + assert.deepEqual( + APPEND.transformArguments('key', 'value'), + ['APPEND', 'key', 'value'] + ); + }); + + testUtils.testAll('append', async client => { + assert.equal( + await client.append('key', 'value'), + 5 + ); + }, { + client: GLOBAL.SERVERS.OPEN, + cluster: GLOBAL.CLUSTERS.OPEN + }); }); diff --git a/packages/client/lib/commands/APPEND.ts b/packages/client/lib/commands/APPEND.ts index 66f7fc84798..1bc01024998 100644 --- a/packages/client/lib/commands/APPEND.ts +++ b/packages/client/lib/commands/APPEND.ts @@ -1,12 +1,10 @@ -import { RedisCommandArgument, RedisCommandArguments } from '.'; +import { RedisArgument, NumberReply, Command } from '../RESP/types'; -export const FIRST_KEY_INDEX = 1; - -export function transformArguments( - key: RedisCommandArgument, - value: RedisCommandArgument -): RedisCommandArguments { +export default { + FIRST_KEY_INDEX: 1, + IS_READ_ONLY: false, + transformArguments(key: RedisArgument, value: RedisArgument) { return ['APPEND', key, value]; -} - -export declare function transformReply(): number; + }, + transformReply: undefined as unknown as () => NumberReply +} as const satisfies Command; diff --git a/packages/client/lib/commands/ASKING.spec.ts b/packages/client/lib/commands/ASKING.spec.ts index 3da2015199e..bd83bec599f 100644 --- a/packages/client/lib/commands/ASKING.spec.ts +++ b/packages/client/lib/commands/ASKING.spec.ts @@ -1,11 +1,11 @@ -import { strict as assert } from 'assert'; -import { transformArguments } from './ASKING'; +import { strict as assert } from 'node:assert'; +import ASKING from './ASKING'; describe('ASKING', () => { - it('transformArguments', () => { - assert.deepEqual( - transformArguments(), - ['ASKING'] - ); - }); + it('transformArguments', () => { + assert.deepEqual( + ASKING.transformArguments(), + ['ASKING'] + ); + }); }); diff --git a/packages/client/lib/commands/ASKING.ts b/packages/client/lib/commands/ASKING.ts index 8a87806fe62..c6ada477ee4 100644 --- a/packages/client/lib/commands/ASKING.ts +++ b/packages/client/lib/commands/ASKING.ts @@ -1,7 +1,10 @@ -import { RedisCommandArguments, RedisCommandArgument } from '.'; +import { SimpleStringReply, Command } from '../RESP/types'; -export function transformArguments(): RedisCommandArguments { +export default { + FIRST_KEY_INDEX: undefined, + IS_READ_ONLY: true, + transformArguments() { return ['ASKING']; -} - -export declare function transformReply(): RedisCommandArgument; + }, + transformReply: undefined as unknown as () => SimpleStringReply<'OK'> +} as const satisfies Command; diff --git a/packages/client/lib/commands/AUTH.spec.ts b/packages/client/lib/commands/AUTH.spec.ts index 1907488346e..2da016ba873 100644 --- a/packages/client/lib/commands/AUTH.spec.ts +++ b/packages/client/lib/commands/AUTH.spec.ts @@ -1,25 +1,25 @@ -import { strict as assert } from 'assert'; -import { transformArguments } from './AUTH'; +import { strict as assert } from 'node:assert'; +import AUTH from './AUTH'; describe('AUTH', () => { - describe('transformArguments', () => { - it('password only', () => { - assert.deepEqual( - transformArguments({ - password: 'password' - }), - ['AUTH', 'password'] - ); - }); + describe('transformArguments', () => { + it('password only', () => { + assert.deepEqual( + AUTH.transformArguments({ + password: 'password' + }), + ['AUTH', 'password'] + ); + }); - it('username & password', () => { - assert.deepEqual( - transformArguments({ - username: 'username', - password: 'password' - }), - ['AUTH', 'username', 'password'] - ); - }); + it('username & password', () => { + assert.deepEqual( + AUTH.transformArguments({ + username: 'username', + password: 'password' + }), + ['AUTH', 'username', 'password'] + ); }); + }); }); diff --git a/packages/client/lib/commands/AUTH.ts b/packages/client/lib/commands/AUTH.ts index 49b0df6d313..4c7a0b0c765 100644 --- a/packages/client/lib/commands/AUTH.ts +++ b/packages/client/lib/commands/AUTH.ts @@ -1,16 +1,23 @@ -import { RedisCommandArgument, RedisCommandArguments } from '.'; +import { RedisArgument, SimpleStringReply, Command } from '../RESP/types'; export interface AuthOptions { - username?: RedisCommandArgument; - password: RedisCommandArgument; + username?: RedisArgument; + password: RedisArgument; } -export function transformArguments({ username, password }: AuthOptions): RedisCommandArguments { - if (!username) { - return ['AUTH', password]; +export default { + FIRST_KEY_INDEX: undefined, + IS_READ_ONLY: true, + transformArguments({ username, password }: AuthOptions) { + const args: Array = ['AUTH']; + + if (username !== undefined) { + args.push(username); } - return ['AUTH', username, password]; -} + args.push(password); -export declare function transformReply(): RedisCommandArgument; + return args; + }, + transformReply: undefined as unknown as () => SimpleStringReply<'OK'> +} as const satisfies Command; diff --git a/packages/client/lib/commands/BGREWRITEAOF.spec.ts b/packages/client/lib/commands/BGREWRITEAOF.spec.ts index d0e150e155b..5447fc70a79 100644 --- a/packages/client/lib/commands/BGREWRITEAOF.spec.ts +++ b/packages/client/lib/commands/BGREWRITEAOF.spec.ts @@ -1,11 +1,19 @@ -import { strict as assert } from 'assert'; -import { transformArguments } from './BGREWRITEAOF'; +import { strict as assert } from 'node:assert'; +import testUtils, { GLOBAL } from '../test-utils'; +import BGREWRITEAOF from './BGREWRITEAOF'; describe('BGREWRITEAOF', () => { - it('transformArguments', () => { - assert.deepEqual( - transformArguments(), - ['BGREWRITEAOF'] - ); - }); + it('transformArguments', () => { + assert.deepEqual( + BGREWRITEAOF.transformArguments(), + ['BGREWRITEAOF'] + ); + }); + + testUtils.testWithClient('client.bgRewriteAof', async client => { + assert.equal( + typeof await client.bgRewriteAof(), + 'string' + ); + }, GLOBAL.SERVERS.OPEN); }); diff --git a/packages/client/lib/commands/BGREWRITEAOF.ts b/packages/client/lib/commands/BGREWRITEAOF.ts index be4ec2546ab..5f9a46e318f 100644 --- a/packages/client/lib/commands/BGREWRITEAOF.ts +++ b/packages/client/lib/commands/BGREWRITEAOF.ts @@ -1,7 +1,10 @@ -import { RedisCommandArgument, RedisCommandArguments } from '.'; +import { SimpleStringReply, Command } from '../RESP/types'; -export function transformArguments(): RedisCommandArguments { +export default { + FIRST_KEY_INDEX: undefined, + IS_READ_ONLY: true, + transformArguments() { return ['BGREWRITEAOF']; -} - -export declare function transformReply(): RedisCommandArgument; + }, + transformReply: undefined as unknown as () => SimpleStringReply +} as const satisfies Command; diff --git a/packages/client/lib/commands/BGSAVE.spec.ts b/packages/client/lib/commands/BGSAVE.spec.ts index 8e4de5eef5b..7944722dd56 100644 --- a/packages/client/lib/commands/BGSAVE.spec.ts +++ b/packages/client/lib/commands/BGSAVE.spec.ts @@ -1,23 +1,32 @@ -import { strict as assert } from 'assert'; -import { describe } from 'mocha'; -import { transformArguments } from './BGSAVE'; +import { strict as assert } from 'node:assert'; +import testUtils, { GLOBAL } from '../test-utils'; +import BGSAVE from './BGSAVE'; describe('BGSAVE', () => { - describe('transformArguments', () => { - it('simple', () => { - assert.deepEqual( - transformArguments(), - ['BGSAVE'] - ); - }); + describe('transformArguments', () => { + it('simple', () => { + assert.deepEqual( + BGSAVE.transformArguments(), + ['BGSAVE'] + ); + }); - it('with SCHEDULE', () => { - assert.deepEqual( - transformArguments({ - SCHEDULE: true - }), - ['BGSAVE', 'SCHEDULE'] - ); - }); + it('with SCHEDULE', () => { + assert.deepEqual( + BGSAVE.transformArguments({ + SCHEDULE: true + }), + ['BGSAVE', 'SCHEDULE'] + ); }); + }); + + testUtils.testWithClient('client.bgSave', async client => { + assert.equal( + typeof await client.bgSave({ + SCHEDULE: true // using `SCHEDULE` to make sure it won't throw an error + }), + 'string' + ); + }, GLOBAL.SERVERS.OPEN); }); diff --git a/packages/client/lib/commands/BGSAVE.ts b/packages/client/lib/commands/BGSAVE.ts index 9c90f3485be..dc0f5056708 100644 --- a/packages/client/lib/commands/BGSAVE.ts +++ b/packages/client/lib/commands/BGSAVE.ts @@ -1,17 +1,20 @@ -import { RedisCommandArgument, RedisCommandArguments } from '.'; +import { SimpleStringReply, Command } from '../RESP/types'; -interface BgSaveOptions { - SCHEDULE?: true; +export interface BgSaveOptions { + SCHEDULE?: boolean; } -export function transformArguments(options?: BgSaveOptions): RedisCommandArguments { +export default { + FIRST_KEY_INDEX: undefined, + IS_READ_ONLY: true, + transformArguments(options?: BgSaveOptions) { const args = ['BGSAVE']; - + if (options?.SCHEDULE) { - args.push('SCHEDULE'); + args.push('SCHEDULE'); } return args; -} - -export declare function transformReply(): RedisCommandArgument; + }, + transformReply: undefined as unknown as () => SimpleStringReply +} as const satisfies Command; diff --git a/packages/client/lib/commands/BITCOUNT.spec.ts b/packages/client/lib/commands/BITCOUNT.spec.ts index 76e7b03f7c9..ceb6476a31c 100644 --- a/packages/client/lib/commands/BITCOUNT.spec.ts +++ b/packages/client/lib/commands/BITCOUNT.spec.ts @@ -1,44 +1,47 @@ -import { strict as assert } from 'assert'; +import { strict as assert } from 'node:assert'; import testUtils, { GLOBAL } from '../test-utils'; -import { transformArguments } from './BITCOUNT'; +import BITCOUNT from './BITCOUNT'; describe('BITCOUNT', () => { - describe('transformArguments', () => { - it('simple', () => { - assert.deepEqual( - transformArguments('key'), - ['BITCOUNT', 'key'] - ); - }); + describe('transformArguments', () => { + it('simple', () => { + assert.deepEqual( + BITCOUNT.transformArguments('key'), + ['BITCOUNT', 'key'] + ); + }); - describe('with range', () => { - it('simple', () => { - assert.deepEqual( - transformArguments('key', { - start: 0, - end: 1 - }), - ['BITCOUNT', 'key', '0', '1'] - ); - }); + describe('with range', () => { + it('simple', () => { + assert.deepEqual( + BITCOUNT.transformArguments('key', { + start: 0, + end: 1 + }), + ['BITCOUNT', 'key', '0', '1'] + ); + }); - it('with mode', () => { - assert.deepEqual( - transformArguments('key', { - start: 0, - end: 1, - mode: 'BIT' - }), - ['BITCOUNT', 'key', '0', '1', 'BIT'] - ); - }); - }); + it('with mode', () => { + assert.deepEqual( + BITCOUNT.transformArguments('key', { + start: 0, + end: 1, + mode: 'BIT' + }), + ['BITCOUNT', 'key', '0', '1', 'BIT'] + ); + }); }); + }); - testUtils.testWithClient('client.bitCount', async client => { - assert.equal( - await client.bitCount('key'), - 0 - ); - }, GLOBAL.SERVERS.OPEN); + testUtils.testAll('bitCount', async client => { + assert.equal( + await client.bitCount('key'), + 0 + ); + }, { + client: GLOBAL.SERVERS.OPEN, + cluster: GLOBAL.CLUSTERS.OPEN + }); }); diff --git a/packages/client/lib/commands/BITCOUNT.ts b/packages/client/lib/commands/BITCOUNT.ts index 4bbd4f00911..6ec6b89be8b 100644 --- a/packages/client/lib/commands/BITCOUNT.ts +++ b/packages/client/lib/commands/BITCOUNT.ts @@ -1,33 +1,29 @@ -import { RedisCommandArgument, RedisCommandArguments } from '.'; +import { RedisArgument, NumberReply, Command } from '../RESP/types'; -export const FIRST_KEY_INDEX = 1; - -export const IS_READ_ONLY = true; - -interface BitCountRange { - start: number; - end: number; - mode?: 'BYTE' | 'BIT'; +export interface BitCountRange { + start: number; + end: number; + mode?: 'BYTE' | 'BIT'; } -export function transformArguments( - key: RedisCommandArgument, - range?: BitCountRange -): RedisCommandArguments { +export default { + FIRST_KEY_INDEX: 1, + IS_READ_ONLY: true, + transformArguments(key: RedisArgument, range?: BitCountRange) { const args = ['BITCOUNT', key]; if (range) { - args.push( - range.start.toString(), - range.end.toString() - ); - - if (range.mode) { - args.push(range.mode); - } + args.push( + range.start.toString(), + range.end.toString() + ); + + if (range.mode) { + args.push(range.mode); + } } return args; -} - -export declare function transformReply(): number; + }, + transformReply: undefined as unknown as () => NumberReply +} as const satisfies Command; diff --git a/packages/client/lib/commands/BITFIELD.spec.ts b/packages/client/lib/commands/BITFIELD.spec.ts index aaf0f93e501..7f805755493 100644 --- a/packages/client/lib/commands/BITFIELD.spec.ts +++ b/packages/client/lib/commands/BITFIELD.spec.ts @@ -1,46 +1,55 @@ -import { strict as assert } from 'assert'; +import { strict as assert } from 'node:assert'; import testUtils, { GLOBAL } from '../test-utils'; -import { transformArguments } from './BITFIELD'; +import BITFIELD from './BITFIELD'; describe('BITFIELD', () => { - it('transformArguments', () => { - assert.deepEqual( - transformArguments('key', [{ - operation: 'OVERFLOW', - behavior: 'WRAP' - }, { - operation: 'GET', - encoding: 'i8', - offset: 0 - }, { - operation: 'OVERFLOW', - behavior: 'SAT' - }, { - operation: 'SET', - encoding: 'i16', - offset: 1, - value: 0 - }, { - operation: 'OVERFLOW', - behavior: 'FAIL' - }, { - operation: 'INCRBY', - encoding: 'i32', - offset: 2, - increment: 1 - }]), - ['BITFIELD', 'key', 'OVERFLOW', 'WRAP', 'GET', 'i8', '0', 'OVERFLOW', 'SAT', 'SET', 'i16', '1', '0', 'OVERFLOW', 'FAIL', 'INCRBY', 'i32', '2', '1'] - ); - }); + it('transformArguments', () => { + assert.deepEqual( + BITFIELD.transformArguments('key', [{ + operation: 'OVERFLOW', + behavior: 'WRAP' + }, { + operation: 'GET', + encoding: 'i8', + offset: 0 + }, { + operation: 'OVERFLOW', + behavior: 'SAT' + }, { + operation: 'SET', + encoding: 'i16', + offset: 1, + value: 0 + }, { + operation: 'OVERFLOW', + behavior: 'FAIL' + }, { + operation: 'INCRBY', + encoding: 'i32', + offset: 2, + increment: 1 + }]), + ['BITFIELD', 'key', 'OVERFLOW', 'WRAP', 'GET', 'i8', '0', 'OVERFLOW', 'SAT', 'SET', 'i16', '1', '0', 'OVERFLOW', 'FAIL', 'INCRBY', 'i32', '2', '1'] + ); + }); - testUtils.testWithClient('client.bitField', async client => { - assert.deepEqual( - await client.bitField('key', [{ - operation: 'GET', - encoding: 'i8', - offset: 0 - }]), - [0] - ); - }, GLOBAL.SERVERS.OPEN); + testUtils.testAll('bitField', async client => { + const a = client.bitField('key', [{ + operation: 'GET', + encoding: 'i8', + offset: 0 + }]); + + assert.deepEqual( + await client.bitField('key', [{ + operation: 'GET', + encoding: 'i8', + offset: 0 + }]), + [0] + ); + }, { + client: GLOBAL.SERVERS.OPEN, + cluster: GLOBAL.CLUSTERS.OPEN + }); }); diff --git a/packages/client/lib/commands/BITFIELD.ts b/packages/client/lib/commands/BITFIELD.ts index 6a477b89f01..5d7d4bf7282 100644 --- a/packages/client/lib/commands/BITFIELD.ts +++ b/packages/client/lib/commands/BITFIELD.ts @@ -1,80 +1,87 @@ -export const FIRST_KEY_INDEX = 1; +import { RedisArgument, ArrayReply, NumberReply, NullReply, Command } from '../RESP/types'; export type BitFieldEncoding = `${'i' | 'u'}${number}`; export interface BitFieldOperation { - operation: S; + operation: S; } export interface BitFieldGetOperation extends BitFieldOperation<'GET'> { - encoding: BitFieldEncoding; - offset: number | string; + encoding: BitFieldEncoding; + offset: number | string; } -interface BitFieldSetOperation extends BitFieldOperation<'SET'> { - encoding: BitFieldEncoding; - offset: number | string; - value: number; +export interface BitFieldSetOperation extends BitFieldOperation<'SET'> { + encoding: BitFieldEncoding; + offset: number | string; + value: number; } -interface BitFieldIncrByOperation extends BitFieldOperation<'INCRBY'> { - encoding: BitFieldEncoding; - offset: number | string; - increment: number; +export interface BitFieldIncrByOperation extends BitFieldOperation<'INCRBY'> { + encoding: BitFieldEncoding; + offset: number | string; + increment: number; } -interface BitFieldOverflowOperation extends BitFieldOperation<'OVERFLOW'> { - behavior: string; +export interface BitFieldOverflowOperation extends BitFieldOperation<'OVERFLOW'> { + behavior: string; } -type BitFieldOperations = Array< - BitFieldGetOperation | - BitFieldSetOperation | - BitFieldIncrByOperation | - BitFieldOverflowOperation +export type BitFieldOperations = Array< + BitFieldGetOperation | + BitFieldSetOperation | + BitFieldIncrByOperation | + BitFieldOverflowOperation >; -export function transformArguments(key: string, operations: BitFieldOperations): Array { +export type BitFieldRoOperations = Array< + Omit +>; + +export default { + FIRST_KEY_INDEX: 1, + IS_READ_ONLY: false, + transformArguments(key: RedisArgument, operations: BitFieldOperations) { const args = ['BITFIELD', key]; for (const options of operations) { - switch (options.operation) { - case 'GET': - args.push( - 'GET', - options.encoding, - options.offset.toString() - ); - break; + switch (options.operation) { + case 'GET': + args.push( + 'GET', + options.encoding, + options.offset.toString() + ); + break; - case 'SET': - args.push( - 'SET', - options.encoding, - options.offset.toString(), - options.value.toString() - ); - break; + case 'SET': + args.push( + 'SET', + options.encoding, + options.offset.toString(), + options.value.toString() + ); + break; - case 'INCRBY': - args.push( - 'INCRBY', - options.encoding, - options.offset.toString(), - options.increment.toString() - ); - break; + case 'INCRBY': + args.push( + 'INCRBY', + options.encoding, + options.offset.toString(), + options.increment.toString() + ); + break; - case 'OVERFLOW': - args.push( - 'OVERFLOW', - options.behavior - ); - break; - } + case 'OVERFLOW': + args.push( + 'OVERFLOW', + options.behavior + ); + break; + } } return args; -} - -export declare function transformReply(): Array; + }, + transformReply: undefined as unknown as () => ArrayReply +} as const satisfies Command; diff --git a/packages/client/lib/commands/BITFIELD_RO.spec.ts b/packages/client/lib/commands/BITFIELD_RO.spec.ts index 98399d5f235..0793100193f 100644 --- a/packages/client/lib/commands/BITFIELD_RO.spec.ts +++ b/packages/client/lib/commands/BITFIELD_RO.spec.ts @@ -1,27 +1,30 @@ -import { strict as assert } from 'assert'; +import { strict as assert } from 'node:assert'; import testUtils, { GLOBAL } from '../test-utils'; -import { transformArguments } from './BITFIELD_RO'; +import BITFIELD_RO from './BITFIELD_RO'; -describe('BITFIELD RO', () => { - testUtils.isVersionGreaterThanHook([6, 2]); +describe('BITFIELD_RO', () => { + testUtils.isVersionGreaterThanHook([6, 2]); - it('transformArguments', () => { - assert.deepEqual( - transformArguments('key', [{ - encoding: 'i8', - offset: 0 - }]), - ['BITFIELD_RO', 'key', 'GET', 'i8', '0'] - ); - }); + it('transformArguments', () => { + assert.deepEqual( + BITFIELD_RO.transformArguments('key', [{ + encoding: 'i8', + offset: 0 + }]), + ['BITFIELD_RO', 'key', 'GET', 'i8', '0'] + ); + }); - testUtils.testWithClient('client.bitFieldRo', async client => { - assert.deepEqual( - await client.bitFieldRo('key', [{ - encoding: 'i8', - offset: 0 - }]), - [0] - ); - }, GLOBAL.SERVERS.OPEN); + testUtils.testAll('bitFieldRo', async client => { + assert.deepEqual( + await client.bitFieldRo('key', [{ + encoding: 'i8', + offset: 0 + }]), + [0] + ); + }, { + client: GLOBAL.SERVERS.OPEN, + cluster: GLOBAL.CLUSTERS.OPEN + }); }); diff --git a/packages/client/lib/commands/BITFIELD_RO.ts b/packages/client/lib/commands/BITFIELD_RO.ts index efd4eac188e..99e500abc04 100644 --- a/packages/client/lib/commands/BITFIELD_RO.ts +++ b/packages/client/lib/commands/BITFIELD_RO.ts @@ -1,26 +1,25 @@ +import { RedisArgument, ArrayReply, NumberReply, Command } from '../RESP/types'; import { BitFieldGetOperation } from './BITFIELD'; -export const FIRST_KEY_INDEX = 1; - -export const IS_READ_ONLY = true; - -type BitFieldRoOperations = Array< - Omit & - Partial> +export type BitFieldRoOperations = Array< + Omit >; -export function transformArguments(key: string, operations: BitFieldRoOperations): Array { +export default { + FIRST_KEY_INDEX: 1, + IS_READ_ONLY: true, + transformArguments(key: RedisArgument, operations: BitFieldRoOperations) { const args = ['BITFIELD_RO', key]; for (const operation of operations) { - args.push( - 'GET', - operation.encoding, - operation.offset.toString() - ); + args.push( + 'GET', + operation.encoding, + operation.offset.toString() + ); } return args; -} - -export declare function transformReply(): Array; + }, + transformReply: undefined as unknown as () => ArrayReply +} as const satisfies Command; diff --git a/packages/client/lib/commands/BITOP.spec.ts b/packages/client/lib/commands/BITOP.spec.ts index 554530d56f4..4df1782467f 100644 --- a/packages/client/lib/commands/BITOP.spec.ts +++ b/packages/client/lib/commands/BITOP.spec.ts @@ -1,35 +1,31 @@ -import { strict as assert } from 'assert'; +import { strict as assert } from 'node:assert'; import testUtils, { GLOBAL } from '../test-utils'; -import { transformArguments } from './BITOP'; +import BITOP from './BITOP'; describe('BITOP', () => { - describe('transformArguments', () => { - it('single key', () => { - assert.deepEqual( - transformArguments('AND', 'destKey', 'key'), - ['BITOP', 'AND', 'destKey', 'key'] - ); - }); - - it('multiple keys', () => { - assert.deepEqual( - transformArguments('AND', 'destKey', ['1', '2']), - ['BITOP', 'AND', 'destKey', '1', '2'] - ); - }); + describe('transformArguments', () => { + it('single key', () => { + assert.deepEqual( + BITOP.transformArguments('AND', 'destKey', 'key'), + ['BITOP', 'AND', 'destKey', 'key'] + ); }); - testUtils.testWithClient('client.bitOp', async client => { - assert.equal( - await client.bitOp('AND', 'destKey', 'key'), - 0 - ); - }, GLOBAL.SERVERS.OPEN); + it('multiple keys', () => { + assert.deepEqual( + BITOP.transformArguments('AND', 'destKey', ['1', '2']), + ['BITOP', 'AND', 'destKey', '1', '2'] + ); + }); + }); - testUtils.testWithCluster('cluster.bitOp', async cluster => { - assert.equal( - await cluster.bitOp('AND', '{tag}destKey', '{tag}key'), - 0 - ); - }, GLOBAL.CLUSTERS.OPEN); + testUtils.testAll('bitOp', async client => { + assert.equal( + await client.bitOp('AND', '{tag}destKey', '{tag}key'), + 0 + ); + }, { + client: GLOBAL.SERVERS.OPEN, + cluster: GLOBAL.CLUSTERS.OPEN + }); }); diff --git a/packages/client/lib/commands/BITOP.ts b/packages/client/lib/commands/BITOP.ts index e2953303d41..4c34845699e 100644 --- a/packages/client/lib/commands/BITOP.ts +++ b/packages/client/lib/commands/BITOP.ts @@ -1,16 +1,17 @@ -import { RedisCommandArgument, RedisCommandArguments } from '.'; -import { pushVerdictArguments } from './generic-transformers'; +import { NumberReply, Command, RedisArgument } from '../RESP/types'; +import { RedisVariadicArgument, pushVariadicArguments } from './generic-transformers'; -export const FIRST_KEY_INDEX = 2; +export type BitOperations = 'AND' | 'OR' | 'XOR' | 'NOT'; -type BitOperations = 'AND' | 'OR' | 'XOR' | 'NOT'; - -export function transformArguments( +export default { + FIRST_KEY_INDEX: 2, + IS_READ_ONLY: false, + transformArguments( operation: BitOperations, - destKey: RedisCommandArgument, - key: RedisCommandArgument | Array -): RedisCommandArguments { - return pushVerdictArguments(['BITOP', operation, destKey], key); -} - -export declare function transformReply(): number; + destKey: RedisArgument, + key: RedisVariadicArgument + ) { + return pushVariadicArguments(['BITOP', operation, destKey], key); + }, + transformReply: undefined as unknown as () => NumberReply +} as const satisfies Command; diff --git a/packages/client/lib/commands/BITPOS.spec.ts b/packages/client/lib/commands/BITPOS.spec.ts index 2a0758fe5da..61940560057 100644 --- a/packages/client/lib/commands/BITPOS.spec.ts +++ b/packages/client/lib/commands/BITPOS.spec.ts @@ -1,49 +1,45 @@ -import { strict as assert } from 'assert'; +import { strict as assert } from 'node:assert'; import testUtils, { GLOBAL } from '../test-utils'; -import { transformArguments } from './BITPOS'; +import BITPOS from './BITPOS'; describe('BITPOS', () => { - describe('transformArguments', () => { - it('simple', () => { - assert.deepEqual( - transformArguments('key', 1), - ['BITPOS', 'key', '1'] - ); - }); - - it('with start', () => { - assert.deepEqual( - transformArguments('key', 1, 1), - ['BITPOS', 'key', '1', '1'] - ); - }); + describe('transformArguments', () => { + it('simple', () => { + assert.deepEqual( + BITPOS.transformArguments('key', 1), + ['BITPOS', 'key', '1'] + ); + }); - it('with start and end', () => { - assert.deepEqual( - transformArguments('key', 1, 1, -1), - ['BITPOS', 'key', '1', '1', '-1'] - ); - }); + it('with start', () => { + assert.deepEqual( + BITPOS.transformArguments('key', 1, 1), + ['BITPOS', 'key', '1', '1'] + ); + }); - it('with start, end and mode', () => { - assert.deepEqual( - transformArguments('key', 1, 1, -1, 'BIT'), - ['BITPOS', 'key', '1', '1', '-1', 'BIT'] - ); - }); + it('with start and end', () => { + assert.deepEqual( + BITPOS.transformArguments('key', 1, 1, -1), + ['BITPOS', 'key', '1', '1', '-1'] + ); }); - testUtils.testWithClient('client.bitPos', async client => { - assert.equal( - await client.bitPos('key', 1, 1), - -1 - ); - }, GLOBAL.SERVERS.OPEN); + it('with start, end and mode', () => { + assert.deepEqual( + BITPOS.transformArguments('key', 1, 1, -1, 'BIT'), + ['BITPOS', 'key', '1', '1', '-1', 'BIT'] + ); + }); + }); - testUtils.testWithCluster('cluster.bitPos', async cluster => { - assert.equal( - await cluster.bitPos('key', 1, 1), - -1 - ); - }, GLOBAL.CLUSTERS.OPEN); + testUtils.testAll('bitPos', async client => { + assert.equal( + await client.bitPos('key', 1, 1), + -1 + ); + }, { + client: GLOBAL.SERVERS.OPEN, + cluster: GLOBAL.CLUSTERS.OPEN + }); }); diff --git a/packages/client/lib/commands/BITPOS.ts b/packages/client/lib/commands/BITPOS.ts index a9a035fd9f2..5d6276dffc0 100644 --- a/packages/client/lib/commands/BITPOS.ts +++ b/packages/client/lib/commands/BITPOS.ts @@ -1,32 +1,31 @@ -import { RedisCommandArgument, RedisCommandArguments } from '.'; +import { RedisArgument, NumberReply, Command } from '../RESP/types'; import { BitValue } from './generic-transformers'; -export const FIRST_KEY_INDEX = 1; - -export const IS_READ_ONLY = true; - -export function transformArguments( - key: RedisCommandArgument, +export default { + FIRST_KEY_INDEX: 1, + IS_READ_ONLY: true, + transformArguments( + key: RedisArgument, bit: BitValue, start?: number, end?: number, mode?: 'BYTE' | 'BIT' -): RedisCommandArguments { + ) { const args = ['BITPOS', key, bit.toString()]; - if (typeof start === 'number') { - args.push(start.toString()); + if (start !== undefined) { + args.push(start.toString()); } - if (typeof end === 'number') { - args.push(end.toString()); + if (end !== undefined) { + args.push(end.toString()); } if (mode) { - args.push(mode); + args.push(mode); } return args; -} - -export declare function transformReply(): number; + }, + transformReply: undefined as unknown as () => NumberReply +} as const satisfies Command; diff --git a/packages/client/lib/commands/BLMOVE.spec.ts b/packages/client/lib/commands/BLMOVE.spec.ts index 3b86c1ec91e..0eca8c61005 100644 --- a/packages/client/lib/commands/BLMOVE.spec.ts +++ b/packages/client/lib/commands/BLMOVE.spec.ts @@ -1,43 +1,35 @@ -import { strict as assert } from 'assert'; -import testUtils, { GLOBAL } from '../test-utils'; -import { transformArguments } from './BLMOVE'; -import { commandOptions } from '../../index'; +import { strict as assert } from 'node:assert'; +import testUtils, { GLOBAL, BLOCKING_MIN_VALUE } from '../test-utils'; +import BLMOVE from './BLMOVE'; describe('BLMOVE', () => { - testUtils.isVersionGreaterThanHook([6, 2]); + testUtils.isVersionGreaterThanHook([6, 2]); - it('transformArguments', () => { - assert.deepEqual( - transformArguments('source', 'destination', 'LEFT', 'RIGHT', 0), - ['BLMOVE', 'source', 'destination', 'LEFT', 'RIGHT', '0'] - ); - }); + it('transformArguments', () => { + assert.deepEqual( + BLMOVE.transformArguments('source', 'destination', 'LEFT', 'RIGHT', 0), + ['BLMOVE', 'source', 'destination', 'LEFT', 'RIGHT', '0'] + ); + }); - testUtils.testWithClient('client.blMove', async client => { - const [blMoveReply] = await Promise.all([ - client.blMove(commandOptions({ - isolated: true - }), 'source', 'destination', 'LEFT', 'RIGHT', 0), - client.lPush('source', 'element') - ]); + testUtils.testAll('blMove - null', async client => { + assert.equal( + await client.blMove('{tag}source', '{tag}destination', 'LEFT', 'RIGHT', BLOCKING_MIN_VALUE), + null + ); + }, { + client: GLOBAL.SERVERS.OPEN, + cluster: GLOBAL.CLUSTERS.OPEN + }); - assert.equal( - blMoveReply, - 'element' - ); - }, GLOBAL.SERVERS.OPEN); - - testUtils.testWithCluster('cluster.blMove', async cluster => { - const [blMoveReply] = await Promise.all([ - cluster.blMove(commandOptions({ - isolated: true - }), '{tag}source', '{tag}destination', 'LEFT', 'RIGHT', 0), - cluster.lPush('{tag}source', 'element') - ]); - - assert.equal( - blMoveReply, - 'element' - ); - }, GLOBAL.CLUSTERS.OPEN); + testUtils.testAll('blMove - with member', async client => { + const [, reply] = await Promise.all([ + client.lPush('{tag}source', 'element'), + client.blMove('{tag}source', '{tag}destination', 'LEFT', 'RIGHT', BLOCKING_MIN_VALUE) + ]); + assert.equal(reply, 'element'); + }, { + client: GLOBAL.SERVERS.OPEN, + cluster: GLOBAL.CLUSTERS.OPEN + }); }); diff --git a/packages/client/lib/commands/BLMOVE.ts b/packages/client/lib/commands/BLMOVE.ts index ee808e70fcc..c7e4844375f 100644 --- a/packages/client/lib/commands/BLMOVE.ts +++ b/packages/client/lib/commands/BLMOVE.ts @@ -1,23 +1,24 @@ -import { RedisCommandArgument, RedisCommandArguments } from '.'; +import { RedisArgument, BlobStringReply, NullReply, Command } from '../RESP/types'; import { ListSide } from './generic-transformers'; -export const FIRST_KEY_INDEX = 1; - -export function transformArguments( - source: RedisCommandArgument, - destination: RedisCommandArgument, - sourceDirection: ListSide, - destinationDirection: ListSide, +export default { + FIRST_KEY_INDEX: 1, + IS_READ_ONLY: false, + transformArguments( + source: RedisArgument, + destination: RedisArgument, + sourceSide: ListSide, + destinationSide: ListSide, timeout: number -): RedisCommandArguments { + ) { return [ - 'BLMOVE', - source, - destination, - sourceDirection, - destinationDirection, - timeout.toString() + 'BLMOVE', + source, + destination, + sourceSide, + destinationSide, + timeout.toString() ]; -} - -export declare function transformReply(): RedisCommandArgument | null; + }, + transformReply: undefined as unknown as () => BlobStringReply | NullReply +} as const satisfies Command; diff --git a/packages/client/lib/commands/BLMPOP.spec.ts b/packages/client/lib/commands/BLMPOP.spec.ts index 15853a771b0..b40556b1e46 100644 --- a/packages/client/lib/commands/BLMPOP.spec.ts +++ b/packages/client/lib/commands/BLMPOP.spec.ts @@ -1,32 +1,49 @@ -import { strict as assert } from 'assert'; -import testUtils, { GLOBAL } from '../test-utils'; -import { transformArguments } from './BLMPOP'; +import { strict as assert } from 'node:assert'; +import testUtils, { GLOBAL, BLOCKING_MIN_VALUE } from '../test-utils'; +import BLMPOP from './BLMPOP'; describe('BLMPOP', () => { - testUtils.isVersionGreaterThanHook([7]); + testUtils.isVersionGreaterThanHook([7]); - describe('transformArguments', () => { - it('simple', () => { - assert.deepEqual( - transformArguments(0, 'key', 'LEFT'), - ['BLMPOP', '0', '1', 'key', 'LEFT'] - ); - }); + describe('transformArguments', () => { + it('simple', () => { + assert.deepEqual( + BLMPOP.transformArguments(0, 'key', 'LEFT'), + ['BLMPOP', '0', '1', 'key', 'LEFT'] + ); + }); - it('with COUNT', () => { - assert.deepEqual( - transformArguments(0, 'key', 'LEFT', { - COUNT: 2 - }), - ['BLMPOP', '0', '1', 'key', 'LEFT', 'COUNT', '2'] - ); - }); + it('with COUNT', () => { + assert.deepEqual( + BLMPOP.transformArguments(0, 'key', 'LEFT', { + COUNT: 1 + }), + ['BLMPOP', '0', '1', 'key', 'LEFT', 'COUNT', '1'] + ); }); + }); + + testUtils.testAll('blmPop - null', async client => { + assert.equal( + await client.blmPop(BLOCKING_MIN_VALUE, 'key', 'RIGHT'), + null + ); + }, { + client: GLOBAL.SERVERS.OPEN, + cluster: GLOBAL.CLUSTERS.OPEN + }); - testUtils.testWithClient('client.blmPop', async client => { - assert.deepEqual( - await client.blmPop(1, 'key', 'RIGHT'), - null - ); - }, GLOBAL.SERVERS.OPEN); + testUtils.testAll('blmPop - with member', async client => { + const [, reply] = await Promise.all([ + client.lPush('key', 'element'), + client.blmPop(BLOCKING_MIN_VALUE, 'key', 'RIGHT') + ]); + assert.deepEqual(reply, [ + 'key', + ['element'] + ]); + }, { + client: GLOBAL.SERVERS.OPEN, + cluster: GLOBAL.CLUSTERS.OPEN + }); }); diff --git a/packages/client/lib/commands/BLMPOP.ts b/packages/client/lib/commands/BLMPOP.ts index 11bfad8b99b..3122e908600 100644 --- a/packages/client/lib/commands/BLMPOP.ts +++ b/packages/client/lib/commands/BLMPOP.ts @@ -1,20 +1,17 @@ -import { RedisCommandArgument, RedisCommandArguments } from '.'; -import { transformLMPopArguments, LMPopOptions, ListSide } from './generic-transformers'; +import { Command } from '../RESP/types'; +import LMPOP, { LMPopArguments, transformLMPopArguments } from './LMPOP'; -export const FIRST_KEY_INDEX = 3; - -export function transformArguments( +export default { + FIRST_KEY_INDEX: 3, + IS_READ_ONLY: false, + transformArguments( timeout: number, - keys: RedisCommandArgument | Array, - side: ListSide, - options?: LMPopOptions -): RedisCommandArguments { + ...args: LMPopArguments + ) { return transformLMPopArguments( - ['BLMPOP', timeout.toString()], - keys, - side, - options + ['BLMPOP', timeout.toString()], + ...args ); -} - -export { transformReply } from './LMPOP'; + }, + transformReply: LMPOP.transformReply +} as const satisfies Command; diff --git a/packages/client/lib/commands/BLPOP.spec.ts b/packages/client/lib/commands/BLPOP.spec.ts index 84920c851e1..4bcc08d0fc9 100644 --- a/packages/client/lib/commands/BLPOP.spec.ts +++ b/packages/client/lib/commands/BLPOP.spec.ts @@ -1,79 +1,46 @@ -import { strict as assert } from 'assert'; -import testUtils, { GLOBAL } from '../test-utils'; -import { transformArguments, transformReply } from './BLPOP'; -import { commandOptions } from '../../index'; +import { strict as assert } from 'node:assert'; +import testUtils, { GLOBAL, BLOCKING_MIN_VALUE } from '../test-utils'; +import BLPOP from './BLPOP'; describe('BLPOP', () => { - describe('transformArguments', () => { - it('single', () => { - assert.deepEqual( - transformArguments('key', 0), - ['BLPOP', 'key', '0'] - ); - }); - - it('multiple', () => { - assert.deepEqual( - transformArguments(['key1', 'key2'], 0), - ['BLPOP', 'key1', 'key2', '0'] - ); - }); + describe('transformArguments', () => { + it('single', () => { + assert.deepEqual( + BLPOP.transformArguments('key', 0), + ['BLPOP', 'key', '0'] + ); }); - describe('transformReply', () => { - it('null', () => { - assert.equal( - transformReply(null), - null - ); - }); - - it('member', () => { - assert.deepEqual( - transformReply(['key', 'element']), - { - key: 'key', - element: 'element' - } - ); - }); + it('multiple', () => { + assert.deepEqual( + BLPOP.transformArguments(['1', '2'], 0), + ['BLPOP', '1', '2', '0'] + ); }); - - testUtils.testWithClient('client.blPop', async client => { - const [ blPopReply ] = await Promise.all([ - client.blPop( - commandOptions({ isolated: true }), - 'key', - 1 - ), - client.lPush('key', 'element'), - ]); - - assert.deepEqual( - blPopReply, - { - key: 'key', - element: 'element' - } - ); - }, GLOBAL.SERVERS.OPEN); - - testUtils.testWithCluster('cluster.blPop', async cluster => { - const [ blPopReply ] = await Promise.all([ - cluster.blPop( - commandOptions({ isolated: true }), - 'key', - 1 - ), - cluster.lPush('key', 'element') - ]); - - assert.deepEqual( - blPopReply, - { - key: 'key', - element: 'element' - } - ); - }, GLOBAL.CLUSTERS.OPEN); + }); + + testUtils.testAll('blPop - null', async client => { + assert.equal( + await client.blPop('key', BLOCKING_MIN_VALUE), + null + ); + }, { + client: GLOBAL.SERVERS.OPEN, + cluster: GLOBAL.CLUSTERS.OPEN + }); + + testUtils.testAll('blPop - with member', async client => { + const [, reply] = await Promise.all([ + client.lPush('key', 'element'), + client.blPop('key', 1) + ]); + + assert.deepEqual(reply, { + key: 'key', + element: 'element' + }); + }, { + client: GLOBAL.SERVERS.OPEN, + cluster: GLOBAL.CLUSTERS.OPEN + }); }); diff --git a/packages/client/lib/commands/BLPOP.ts b/packages/client/lib/commands/BLPOP.ts index 46ef41ad6f0..c9f8b4775eb 100644 --- a/packages/client/lib/commands/BLPOP.ts +++ b/packages/client/lib/commands/BLPOP.ts @@ -1,31 +1,23 @@ -import { RedisCommandArgument, RedisCommandArguments } from '.'; -import { pushVerdictArguments } from './generic-transformers'; - -export const FIRST_KEY_INDEX = 1; - -export function transformArguments( - keys: RedisCommandArgument | Array, +import { UnwrapReply, NullReply, TuplesReply, BlobStringReply, Command } from '../RESP/types'; +import { RedisVariadicArgument, pushVariadicArguments } from './generic-transformers'; + +export default { + FIRST_KEY_INDEX: 1, + IS_READ_ONLY: true, + transformArguments( + key: RedisVariadicArgument, timeout: number -): RedisCommandArguments { - const args = pushVerdictArguments(['BLPOP'], keys); - + ) { + const args = pushVariadicArguments(['BLPOP'], key); args.push(timeout.toString()); - return args; -} - -type BLPopRawReply = null | [RedisCommandArgument, RedisCommandArgument]; - -type BLPopReply = null | { - key: RedisCommandArgument; - element: RedisCommandArgument; -}; - -export function transformReply(reply: BLPopRawReply): BLPopReply { + }, + transformReply(reply: UnwrapReply>) { if (reply === null) return null; return { - key: reply[0], - element: reply[1] + key: reply[0], + element: reply[1] }; -} + } +} as const satisfies Command; diff --git a/packages/client/lib/commands/BRPOP.spec.ts b/packages/client/lib/commands/BRPOP.spec.ts index fc203e1abdf..21631d763f4 100644 --- a/packages/client/lib/commands/BRPOP.spec.ts +++ b/packages/client/lib/commands/BRPOP.spec.ts @@ -1,79 +1,46 @@ -import { strict as assert } from 'assert'; -import testUtils, { GLOBAL } from '../test-utils'; -import { transformArguments, transformReply } from './BRPOP'; -import { commandOptions } from '../../index'; +import { strict as assert } from 'node:assert'; +import testUtils, { GLOBAL, BLOCKING_MIN_VALUE } from '../test-utils'; +import BRPOP from './BRPOP'; describe('BRPOP', () => { - describe('transformArguments', () => { - it('single', () => { - assert.deepEqual( - transformArguments('key', 0), - ['BRPOP', 'key', '0'] - ); - }); - - it('multiple', () => { - assert.deepEqual( - transformArguments(['key1', 'key2'], 0), - ['BRPOP', 'key1', 'key2', '0'] - ); - }); + describe('transformArguments', () => { + it('single', () => { + assert.deepEqual( + BRPOP.transformArguments('key', 0), + ['BRPOP', 'key', '0'] + ); }); - describe('transformReply', () => { - it('null', () => { - assert.equal( - transformReply(null), - null - ); - }); - - it('member', () => { - assert.deepEqual( - transformReply(['key', 'element']), - { - key: 'key', - element: 'element' - } - ); - }); + it('multiple', () => { + assert.deepEqual( + BRPOP.transformArguments(['1', '2'], 0), + ['BRPOP', '1', '2', '0'] + ); }); - - testUtils.testWithClient('client.brPop', async client => { - const [ brPopReply ] = await Promise.all([ - client.brPop( - commandOptions({ isolated: true }), - 'key', - 1 - ), - client.lPush('key', 'element'), - ]); - - assert.deepEqual( - brPopReply, - { - key: 'key', - element: 'element' - } - ); - }, GLOBAL.SERVERS.OPEN); - - testUtils.testWithCluster('cluster.brPop', async cluster => { - const [ brPopReply ] = await Promise.all([ - cluster.brPop( - commandOptions({ isolated: true }), - 'key', - 1 - ), - cluster.lPush('key', 'element'), - ]); - - assert.deepEqual( - brPopReply, - { - key: 'key', - element: 'element' - } - ); - }, GLOBAL.CLUSTERS.OPEN); + }); + + testUtils.testAll('brPop - null', async client => { + assert.equal( + await client.brPop('key', BLOCKING_MIN_VALUE), + null + ); + }, { + client: GLOBAL.SERVERS.OPEN, + cluster: GLOBAL.CLUSTERS.OPEN + }); + + testUtils.testAll('brPopblPop - with member', async client => { + const [, reply] = await Promise.all([ + client.lPush('key', 'element'), + client.brPop('key', 1) + ]); + + assert.deepEqual(reply, { + key: 'key', + element: 'element' + }); + }, { + client: GLOBAL.SERVERS.OPEN, + cluster: GLOBAL.CLUSTERS.OPEN + }); }); diff --git a/packages/client/lib/commands/BRPOP.ts b/packages/client/lib/commands/BRPOP.ts index b30e7e2cc29..f9c8aaa5037 100644 --- a/packages/client/lib/commands/BRPOP.ts +++ b/packages/client/lib/commands/BRPOP.ts @@ -1,17 +1,17 @@ -import { RedisCommandArgument, RedisCommandArguments } from '.'; -import { pushVerdictArguments } from './generic-transformers'; +import { Command } from '../RESP/types'; +import { RedisVariadicArgument, pushVariadicArguments } from './generic-transformers'; +import BLPOP from './BLPOP'; -export const FIRST_KEY_INDEX = 1; - -export function transformArguments( - key: RedisCommandArgument | Array, +export default { + FIRST_KEY_INDEX: 1, + IS_READ_ONLY: true, + transformArguments( + key: RedisVariadicArgument, timeout: number -): RedisCommandArguments { - const args = pushVerdictArguments(['BRPOP'], key); - + ) { + const args = pushVariadicArguments(['BRPOP'], key); args.push(timeout.toString()); - return args; -} - -export { transformReply } from './BLPOP'; + }, + transformReply: BLPOP.transformReply +} as const satisfies Command; diff --git a/packages/client/lib/commands/BRPOPLPUSH.spec.ts b/packages/client/lib/commands/BRPOPLPUSH.spec.ts index 214af4553ac..1f6dc48bfea 100644 --- a/packages/client/lib/commands/BRPOPLPUSH.spec.ts +++ b/packages/client/lib/commands/BRPOPLPUSH.spec.ts @@ -1,47 +1,42 @@ -import { strict as assert } from 'assert'; -import testUtils, { GLOBAL } from '../test-utils'; -import { transformArguments } from './BRPOPLPUSH'; -import { commandOptions } from '../../index'; +import { strict as assert } from 'node:assert'; +import testUtils, { GLOBAL, BLOCKING_MIN_VALUE } from '../test-utils'; +import BRPOPLPUSH from './BRPOPLPUSH'; describe('BRPOPLPUSH', () => { - it('transformArguments', () => { - assert.deepEqual( - transformArguments('source', 'destination', 0), - ['BRPOPLPUSH', 'source', 'destination', '0'] - ); - }); + it('transformArguments', () => { + assert.deepEqual( + BRPOPLPUSH.transformArguments('source', 'destination', 0), + ['BRPOPLPUSH', 'source', 'destination', '0'] + ); + }); - testUtils.testWithClient('client.brPopLPush', async client => { - const [ popReply ] = await Promise.all([ - client.brPopLPush( - commandOptions({ isolated: true }), - 'source', - 'destination', - 0 - ), - client.lPush('source', 'element') - ]); + testUtils.testAll('brPopLPush - null', async client => { + assert.equal( + await client.brPopLPush( + '{tag}source', + '{tag}destination', + BLOCKING_MIN_VALUE + ), + null + ); + }, { + client: GLOBAL.SERVERS.OPEN, + cluster: GLOBAL.CLUSTERS.OPEN + }); - assert.equal( - popReply, - 'element' - ); - }, GLOBAL.SERVERS.OPEN); + testUtils.testAll('brPopLPush - with member', async client => { + const [, reply] = await Promise.all([ + client.lPush('{tag}source', 'element'), + client.brPopLPush( + '{tag}source', + '{tag}destination', + 0 + ) + ]); - testUtils.testWithCluster('cluster.brPopLPush', async cluster => { - const [ popReply ] = await Promise.all([ - cluster.brPopLPush( - commandOptions({ isolated: true }), - '{tag}source', - '{tag}destination', - 0 - ), - cluster.lPush('{tag}source', 'element') - ]); - - assert.equal( - popReply, - 'element' - ); - }, GLOBAL.CLUSTERS.OPEN); + assert.equal(reply, 'element'); + }, { + client: GLOBAL.SERVERS.OPEN, + cluster: GLOBAL.CLUSTERS.OPEN + }); }); diff --git a/packages/client/lib/commands/BRPOPLPUSH.ts b/packages/client/lib/commands/BRPOPLPUSH.ts index 72c3e4aa5b2..d342ea75725 100644 --- a/packages/client/lib/commands/BRPOPLPUSH.ts +++ b/packages/client/lib/commands/BRPOPLPUSH.ts @@ -1,13 +1,14 @@ -import { RedisCommandArgument, RedisCommandArguments } from '.'; +import { RedisArgument, BlobStringReply, NullReply, Command } from '../RESP/types'; -export const FIRST_KEY_INDEX = 1; - -export function transformArguments( - source: RedisCommandArgument, - destination: RedisCommandArgument, +export default { + FIRST_KEY_INDEX: 1, + IS_READ_ONLY: false, + transformArguments( + source: RedisArgument, + destination: RedisArgument, timeout: number -): RedisCommandArguments { + ) { return ['BRPOPLPUSH', source, destination, timeout.toString()]; -} - -export declare function transformReply(): RedisCommandArgument | null; + }, + transformReply: undefined as unknown as () => BlobStringReply | NullReply +} as const satisfies Command; diff --git a/packages/client/lib/commands/BZMPOP.spec.ts b/packages/client/lib/commands/BZMPOP.spec.ts index 0e381c114f2..554e6898d62 100644 --- a/packages/client/lib/commands/BZMPOP.spec.ts +++ b/packages/client/lib/commands/BZMPOP.spec.ts @@ -1,32 +1,55 @@ -import { strict as assert } from 'assert'; -import testUtils, { GLOBAL } from '../test-utils'; -import { transformArguments } from './BZMPOP'; +import { strict as assert } from 'node:assert'; +import testUtils, { GLOBAL, BLOCKING_MIN_VALUE } from '../test-utils'; +import BZMPOP from './BZMPOP'; describe('BZMPOP', () => { - testUtils.isVersionGreaterThanHook([7]); + testUtils.isVersionGreaterThanHook([7]); - describe('transformArguments', () => { - it('simple', () => { - assert.deepEqual( - transformArguments(0, 'key', 'MIN'), - ['BZMPOP', '0', '1', 'key', 'MIN'] - ); - }); + describe('transformArguments', () => { + it('simple', () => { + assert.deepEqual( + BZMPOP.transformArguments(0, 'key', 'MIN'), + ['BZMPOP', '0', '1', 'key', 'MIN'] + ); + }); - it('with COUNT', () => { - assert.deepEqual( - transformArguments(0, 'key', 'MIN', { - COUNT: 2 - }), - ['BZMPOP', '0', '1', 'key', 'MIN', 'COUNT', '2'] - ); - }); + it('with COUNT', () => { + assert.deepEqual( + BZMPOP.transformArguments(0, 'key', 'MIN', { + COUNT: 2 + }), + ['BZMPOP', '0', '1', 'key', 'MIN', 'COUNT', '2'] + ); }); + }); + + testUtils.testAll('bzmPop - null', async client => { + assert.equal( + await client.bzmPop(BLOCKING_MIN_VALUE, 'key', 'MAX'), + null + ); + }, { + client: GLOBAL.SERVERS.OPEN, + cluster: GLOBAL.SERVERS.OPEN + }); - testUtils.testWithClient('client.bzmPop', async client => { - assert.deepEqual( - await client.bzmPop(1, 'key', 'MAX'), - null - ); - }, GLOBAL.SERVERS.OPEN); + testUtils.testAll('bzmPop - with member', async client => { + const key = 'key', + member = { + value: 'a', + score: 1 + }, + [, reply] = await Promise.all([ + client.zAdd(key, member), + client.bzmPop(BLOCKING_MIN_VALUE, key, 'MAX') + ]); + + assert.deepEqual(reply, { + key, + members: [member] + }); + }, { + client: GLOBAL.SERVERS.OPEN, + cluster: GLOBAL.SERVERS.OPEN + }); }); diff --git a/packages/client/lib/commands/BZMPOP.ts b/packages/client/lib/commands/BZMPOP.ts index e4e9699cbd4..030aa20c66b 100644 --- a/packages/client/lib/commands/BZMPOP.ts +++ b/packages/client/lib/commands/BZMPOP.ts @@ -1,20 +1,11 @@ -import { RedisCommandArgument, RedisCommandArguments } from '.'; -import { SortedSetSide, transformZMPopArguments, ZMPopOptions } from './generic-transformers'; +import { Command } from '../RESP/types'; +import ZMPOP, { ZMPopArguments, transformZMPopArguments } from './ZMPOP'; -export const FIRST_KEY_INDEX = 3; - -export function transformArguments( - timeout: number, - keys: RedisCommandArgument | Array, - side: SortedSetSide, - options?: ZMPopOptions -): RedisCommandArguments { - return transformZMPopArguments( - ['BZMPOP', timeout.toString()], - keys, - side, - options - ); -} - -export { transformReply } from './ZMPOP'; +export default { + FIRST_KEY_INDEX: 3, + IS_READ_ONLY: false, + transformArguments(timeout: number, ...args: ZMPopArguments) { + return transformZMPopArguments(['BZMPOP', timeout.toString()], ...args); + }, + transformReply: ZMPOP.transformReply +} as const satisfies Command; diff --git a/packages/client/lib/commands/BZPOPMAX.spec.ts b/packages/client/lib/commands/BZPOPMAX.spec.ts index d5c17437122..1f0a4d44f07 100644 --- a/packages/client/lib/commands/BZPOPMAX.spec.ts +++ b/packages/client/lib/commands/BZPOPMAX.spec.ts @@ -1,65 +1,51 @@ -import { strict as assert } from 'assert'; -import testUtils, { GLOBAL } from '../test-utils'; -import { transformArguments, transformReply } from './BZPOPMAX'; -import { commandOptions } from '../../index'; +import { strict as assert } from 'node:assert'; +import testUtils, { GLOBAL, BLOCKING_MIN_VALUE } from '../test-utils'; +import BZPOPMAX from './BZPOPMAX'; describe('BZPOPMAX', () => { - describe('transformArguments', () => { - it('single', () => { - assert.deepEqual( - transformArguments('key', 0), - ['BZPOPMAX', 'key', '0'] - ); - }); - - it('multiple', () => { - assert.deepEqual( - transformArguments(['1', '2'], 0), - ['BZPOPMAX', '1', '2', '0'] - ); - }); + describe('transformArguments', () => { + it('single', () => { + assert.deepEqual( + BZPOPMAX.transformArguments('key', 0), + ['BZPOPMAX', 'key', '0'] + ); }); - describe('transformReply', () => { - it('null', () => { - assert.equal( - transformReply(null), - null - ); - }); - - it('member', () => { - assert.deepEqual( - transformReply(['key', 'value', '1']), - { - key: 'key', - value: 'value', - score: 1 - } - ); - }); + it('multiple', () => { + assert.deepEqual( + BZPOPMAX.transformArguments(['1', '2'], 0), + ['BZPOPMAX', '1', '2', '0'] + ); }); + }); - testUtils.testWithClient('client.bzPopMax', async client => { - const [ bzPopMaxReply ] = await Promise.all([ - client.bzPopMax( - commandOptions({ isolated: true }), - 'key', - 1 - ), - client.zAdd('key', [{ - value: '1', - score: 1 - }]) - ]); + testUtils.testAll('bzPopMax - null', async client => { + assert.equal( + await client.bzPopMax('key', BLOCKING_MIN_VALUE), + null + ); + }, { + client: GLOBAL.SERVERS.OPEN, + cluster: GLOBAL.SERVERS.OPEN + }); - assert.deepEqual( - bzPopMaxReply, - { - key: 'key', - value: '1', - score: 1 - } - ); - }, GLOBAL.SERVERS.OPEN); + testUtils.testAll('bzPopMax - with member', async client => { + const key = 'key', + member = { + value: 'a', + score: 1 + }, + [, reply] = await Promise.all([ + client.zAdd(key, member), + client.bzPopMax(key, BLOCKING_MIN_VALUE) + ]); + + assert.deepEqual(reply, { + key, + ...member + }); + }, { + client: GLOBAL.SERVERS.OPEN, + cluster: GLOBAL.SERVERS.OPEN + }); }); diff --git a/packages/client/lib/commands/BZPOPMAX.ts b/packages/client/lib/commands/BZPOPMAX.ts index 94a30fb8dce..792a5592574 100644 --- a/packages/client/lib/commands/BZPOPMAX.ts +++ b/packages/client/lib/commands/BZPOPMAX.ts @@ -1,29 +1,43 @@ -import { RedisCommandArgument, RedisCommandArguments } from '.'; -import { pushVerdictArguments, transformNumberInfinityReply, ZMember } from './generic-transformers'; - -export const FIRST_KEY_INDEX = 1; - -export function transformArguments( - key: RedisCommandArgument | Array, - timeout: number -): RedisCommandArguments { - const args = pushVerdictArguments(['BZPOPMAX'], key); - - args.push(timeout.toString()); - - return args; +import { RedisArgument, NullReply, TuplesReply, BlobStringReply, DoubleReply, UnwrapReply, Command, TypeMapping } from '../RESP/types'; +import { RedisVariadicArgument, pushVariadicArguments, transformDoubleReply } from './generic-transformers'; + +export function transformBZPopArguments( + command: RedisArgument, + key: RedisVariadicArgument, + timeout: number +) { + const args = pushVariadicArguments([command], key); + args.push(timeout.toString()); + return args; } -type ZMemberRawReply = [key: RedisCommandArgument, value: RedisCommandArgument, score: RedisCommandArgument] | null; - -type BZPopMaxReply = (ZMember & { key: RedisCommandArgument }) | null; - -export function transformReply(reply: ZMemberRawReply): BZPopMaxReply | null { - if (!reply) return null; - - return { +export type BZPopArguments = typeof transformBZPopArguments extends (_: any, ...args: infer T) => any ? T : never; + +export default { + FIRST_KEY_INDEX: 1, + IS_READ_ONLY: false, + transformArguments(...args: BZPopArguments) { + return transformBZPopArguments('BZPOPMAX', ...args); + }, + transformReply: { + 2( + reply: UnwrapReply>, + preserve?: any, + typeMapping?: TypeMapping + ) { + return reply === null ? null : { key: reply[0], value: reply[1], - score: transformNumberInfinityReply(reply[2]) - }; -} + score: transformDoubleReply[2](reply[2], preserve, typeMapping) + }; + }, + 3(reply: UnwrapReply>) { + return reply === null ? null : { + key: reply[0], + value: reply[1], + score: reply[2] + }; + } + } +} as const satisfies Command; + diff --git a/packages/client/lib/commands/BZPOPMIN.spec.ts b/packages/client/lib/commands/BZPOPMIN.spec.ts index 0573a4ac898..7f39f7d1896 100644 --- a/packages/client/lib/commands/BZPOPMIN.spec.ts +++ b/packages/client/lib/commands/BZPOPMIN.spec.ts @@ -1,65 +1,51 @@ -import { strict as assert } from 'assert'; -import testUtils, { GLOBAL } from '../test-utils'; -import { transformArguments, transformReply } from './BZPOPMIN'; -import { commandOptions } from '../../index'; +import { strict as assert } from 'node:assert'; +import testUtils, { GLOBAL, BLOCKING_MIN_VALUE } from '../test-utils'; +import BZPOPMIN from './BZPOPMIN'; describe('BZPOPMIN', () => { - describe('transformArguments', () => { - it('single', () => { - assert.deepEqual( - transformArguments('key', 0), - ['BZPOPMIN', 'key', '0'] - ); - }); - - it('multiple', () => { - assert.deepEqual( - transformArguments(['1', '2'], 0), - ['BZPOPMIN', '1', '2', '0'] - ); - }); + describe('transformArguments', () => { + it('single', () => { + assert.deepEqual( + BZPOPMIN.transformArguments('key', 0), + ['BZPOPMIN', 'key', '0'] + ); }); - describe('transformReply', () => { - it('null', () => { - assert.equal( - transformReply(null), - null - ); - }); - - it('member', () => { - assert.deepEqual( - transformReply(['key', 'value', '1']), - { - key: 'key', - value: 'value', - score: 1 - } - ); - }); + it('multiple', () => { + assert.deepEqual( + BZPOPMIN.transformArguments(['1', '2'], 0), + ['BZPOPMIN', '1', '2', '0'] + ); }); + }); - testUtils.testWithClient('client.bzPopMin', async client => { - const [ bzPopMinReply ] = await Promise.all([ - client.bzPopMin( - commandOptions({ isolated: true }), - 'key', - 1 - ), - client.zAdd('key', [{ - value: '1', - score: 1 - }]) - ]); + testUtils.testAll('bzPopMin - null', async client => { + assert.equal( + await client.bzPopMin('key', BLOCKING_MIN_VALUE), + null + ); + }, { + client: GLOBAL.SERVERS.OPEN, + cluster: GLOBAL.SERVERS.OPEN + }); - assert.deepEqual( - bzPopMinReply, - { - key: 'key', - value: '1', - score: 1 - } - ); - }, GLOBAL.SERVERS.OPEN); + testUtils.testAll('bzPopMin - with member', async client => { + const key = 'key', + member = { + value: 'a', + score: 1 + }, + [, reply] = await Promise.all([ + client.zAdd(key, member), + client.bzPopMin(key, BLOCKING_MIN_VALUE) + ]); + + assert.deepEqual(reply, { + key, + ...member + }); + }, { + client: GLOBAL.SERVERS.OPEN, + cluster: GLOBAL.SERVERS.OPEN + }); }); diff --git a/packages/client/lib/commands/BZPOPMIN.ts b/packages/client/lib/commands/BZPOPMIN.ts index 40cb3d5dc75..f27e623528e 100644 --- a/packages/client/lib/commands/BZPOPMIN.ts +++ b/packages/client/lib/commands/BZPOPMIN.ts @@ -1,17 +1,12 @@ -import { RedisCommandArgument, RedisCommandArguments } from '.'; -import { pushVerdictArguments } from './generic-transformers'; +import { Command } from '../RESP/types'; +import BZPOPMAX, { BZPopArguments, transformBZPopArguments } from './BZPOPMAX'; + +export default { + FIRST_KEY_INDEX: BZPOPMAX.FIRST_KEY_INDEX, + IS_READ_ONLY: BZPOPMAX.IS_READ_ONLY, + transformArguments(...args: BZPopArguments) { + return transformBZPopArguments('BZPOPMIN', ...args); + }, + transformReply: BZPOPMAX.transformReply +} as const satisfies Command; -export const FIRST_KEY_INDEX = 1; - -export function transformArguments( - key: RedisCommandArgument | Array, - timeout: number -): RedisCommandArguments { - const args = pushVerdictArguments(['BZPOPMIN'], key); - - args.push(timeout.toString()); - - return args; -} - -export { transformReply } from './BZPOPMAX'; diff --git a/packages/client/lib/commands/CLIENT_CACHING.spec.ts b/packages/client/lib/commands/CLIENT_CACHING.spec.ts index d9cb9a3f796..34023f98922 100644 --- a/packages/client/lib/commands/CLIENT_CACHING.spec.ts +++ b/packages/client/lib/commands/CLIENT_CACHING.spec.ts @@ -1,20 +1,20 @@ -import { strict as assert } from 'assert'; -import { transformArguments } from './CLIENT_CACHING'; +import { strict as assert } from 'node:assert'; +import CLIENT_CACHING from './CLIENT_CACHING'; describe('CLIENT CACHING', () => { - describe('transformArguments', () => { - it('true', () => { - assert.deepEqual( - transformArguments(true), - ['CLIENT', 'CACHING', 'YES'] - ); - }); + describe('transformArguments', () => { + it('true', () => { + assert.deepEqual( + CLIENT_CACHING.transformArguments(true), + ['CLIENT', 'CACHING', 'YES'] + ); + }); - it('false', () => { - assert.deepEqual( - transformArguments(false), - ['CLIENT', 'CACHING', 'NO'] - ); - }); + it('false', () => { + assert.deepEqual( + CLIENT_CACHING.transformArguments(false), + ['CLIENT', 'CACHING', 'NO'] + ); }); + }); }); diff --git a/packages/client/lib/commands/CLIENT_CACHING.ts b/packages/client/lib/commands/CLIENT_CACHING.ts index bc2fbe41e9d..505ae152f85 100644 --- a/packages/client/lib/commands/CLIENT_CACHING.ts +++ b/packages/client/lib/commands/CLIENT_CACHING.ts @@ -1,11 +1,14 @@ -import { RedisCommandArguments } from '.'; +import { SimpleStringReply, Command } from '../RESP/types'; -export function transformArguments(value: boolean): RedisCommandArguments { +export default { + FIRST_KEY_INDEX: undefined, + IS_READ_ONLY: true, + transformArguments(value: boolean) { return [ - 'CLIENT', - 'CACHING', - value ? 'YES' : 'NO' + 'CLIENT', + 'CACHING', + value ? 'YES' : 'NO' ]; -} - -export declare function transformReply(): 'OK' | Buffer; + }, + transformReply: undefined as unknown as () => SimpleStringReply<'OK'> +} as const satisfies Command; diff --git a/packages/client/lib/commands/CLIENT_GETNAME.spec.ts b/packages/client/lib/commands/CLIENT_GETNAME.spec.ts index 0a09713882f..8975f1fee9c 100644 --- a/packages/client/lib/commands/CLIENT_GETNAME.spec.ts +++ b/packages/client/lib/commands/CLIENT_GETNAME.spec.ts @@ -1,11 +1,19 @@ -import { strict as assert } from 'assert'; -import { transformArguments } from './CLIENT_GETNAME'; +import { strict as assert } from 'node:assert'; +import testUtils, { GLOBAL } from '../test-utils'; +import CLIENT_GETNAME from './CLIENT_GETNAME'; describe('CLIENT GETNAME', () => { - it('transformArguments', () => { - assert.deepEqual( - transformArguments(), - ['CLIENT', 'GETNAME'] - ); - }); + it('transformArguments', () => { + assert.deepEqual( + CLIENT_GETNAME.transformArguments(), + ['CLIENT', 'GETNAME'] + ); + }); + + testUtils.testWithClient('client.clientGetName', async client => { + assert.equal( + await client.clientGetName(), + null + ); + }, GLOBAL.SERVERS.OPEN); }); diff --git a/packages/client/lib/commands/CLIENT_GETNAME.ts b/packages/client/lib/commands/CLIENT_GETNAME.ts index da00539d7fb..c46b576407b 100644 --- a/packages/client/lib/commands/CLIENT_GETNAME.ts +++ b/packages/client/lib/commands/CLIENT_GETNAME.ts @@ -1,7 +1,13 @@ -import { RedisCommandArguments } from '.'; +import { BlobStringReply, NullReply, Command } from '../RESP/types'; -export function transformArguments(): RedisCommandArguments { - return ['CLIENT', 'GETNAME']; -} - -export declare function transformReply(): string | null; +export default { + FIRST_KEY_INDEX: undefined, + IS_READ_ONLY: true, + transformArguments() { + return [ + 'CLIENT', + 'GETNAME' + ]; + }, + transformReply: undefined as unknown as () => BlobStringReply | NullReply +} as const satisfies Command; diff --git a/packages/client/lib/commands/CLIENT_GETREDIR.spec.ts b/packages/client/lib/commands/CLIENT_GETREDIR.spec.ts index 09dd9677e32..5cfedf2a4e7 100644 --- a/packages/client/lib/commands/CLIENT_GETREDIR.spec.ts +++ b/packages/client/lib/commands/CLIENT_GETREDIR.spec.ts @@ -1,11 +1,11 @@ -import { strict as assert } from 'assert'; -import { transformArguments } from './CLIENT_GETREDIR'; +import { strict as assert } from 'node:assert'; +import CLIENT_GETREDIR from './CLIENT_GETREDIR'; describe('CLIENT GETREDIR', () => { - it('transformArguments', () => { - assert.deepEqual( - transformArguments(), - ['CLIENT', 'GETREDIR'] - ); - }); + it('transformArguments', () => { + assert.deepEqual( + CLIENT_GETREDIR.transformArguments(), + ['CLIENT', 'GETREDIR'] + ); + }); }); diff --git a/packages/client/lib/commands/CLIENT_GETREDIR.ts b/packages/client/lib/commands/CLIENT_GETREDIR.ts index d192adf284a..ae0b601b4e8 100644 --- a/packages/client/lib/commands/CLIENT_GETREDIR.ts +++ b/packages/client/lib/commands/CLIENT_GETREDIR.ts @@ -1,7 +1,10 @@ -import { RedisCommandArguments } from '.'; +import { NumberReply, Command } from '../RESP/types'; -export function transformArguments(): RedisCommandArguments { - return ['CLIENT', 'GETREDIR']; -} - -export declare function transformReply(): number; +export default { + FIRST_KEY_INDEX: undefined, + IS_READ_ONLY: true, + transformArguments() { + return ['CLIENT', 'GETREDIR'] + }, + transformReply: undefined as unknown as () => NumberReply +} as const satisfies Command; diff --git a/packages/client/lib/commands/CLIENT_ID.spec.ts b/packages/client/lib/commands/CLIENT_ID.spec.ts index 6792a8c31be..7b51e6bd930 100644 --- a/packages/client/lib/commands/CLIENT_ID.spec.ts +++ b/packages/client/lib/commands/CLIENT_ID.spec.ts @@ -1,19 +1,19 @@ -import { strict as assert } from 'assert'; +import { strict as assert } from 'node:assert'; import testUtils, { GLOBAL } from '../test-utils'; -import { transformArguments } from './CLIENT_ID'; +import CLIENT_ID from './CLIENT_ID'; describe('CLIENT ID', () => { - it('transformArguments', () => { - assert.deepEqual( - transformArguments(), - ['CLIENT', 'ID'] - ); - }); + it('transformArguments', () => { + assert.deepEqual( + CLIENT_ID.transformArguments(), + ['CLIENT', 'ID'] + ); + }); - testUtils.testWithClient('client.clientId', async client => { - assert.equal( - typeof (await client.clientId()), - 'number' - ); - }, GLOBAL.SERVERS.OPEN); + testUtils.testWithClient('client.clientId', async client => { + assert.equal( + typeof (await client.clientId()), + 'number' + ); + }, GLOBAL.SERVERS.OPEN); }); diff --git a/packages/client/lib/commands/CLIENT_ID.ts b/packages/client/lib/commands/CLIENT_ID.ts index a57e392ade6..165ab1931eb 100644 --- a/packages/client/lib/commands/CLIENT_ID.ts +++ b/packages/client/lib/commands/CLIENT_ID.ts @@ -1,7 +1,10 @@ -export const IS_READ_ONLY = true; +import { NumberReply, Command } from '../RESP/types'; -export function transformArguments(): Array { +export default { + FIRST_KEY_INDEX: undefined, + IS_READ_ONLY: true, + transformArguments() { return ['CLIENT', 'ID']; -} - -export declare function transformReply(): number; + }, + transformReply: undefined as unknown as () => NumberReply +} as const satisfies Command; diff --git a/packages/client/lib/commands/CLIENT_INFO.spec.ts b/packages/client/lib/commands/CLIENT_INFO.spec.ts index ccb99017cf3..0aba384aa3a 100644 --- a/packages/client/lib/commands/CLIENT_INFO.spec.ts +++ b/packages/client/lib/commands/CLIENT_INFO.spec.ts @@ -1,50 +1,50 @@ -import { strict as assert } from 'assert'; -import { transformArguments, transformReply } from './CLIENT_INFO'; +import { strict as assert } from 'node:assert'; +import CLIENT_INFO from './CLIENT_INFO'; import testUtils, { GLOBAL } from '../test-utils'; describe('CLIENT INFO', () => { - testUtils.isVersionGreaterThanHook([6, 2]); + testUtils.isVersionGreaterThanHook([6, 2]); - it('transformArguments', () => { - assert.deepEqual( - transformArguments(), - ['CLIENT', 'INFO'] - ); - }); + it('transformArguments', () => { + assert.deepEqual( + CLIENT_INFO.transformArguments(), + ['CLIENT', 'INFO'] + ); + }); - testUtils.testWithClient('client.clientInfo', async client => { - const reply = await client.clientInfo(); - assert.equal(typeof reply.id, 'number'); - assert.equal(typeof reply.addr, 'string'); - assert.equal(typeof reply.laddr, 'string'); - assert.equal(typeof reply.fd, 'number'); - assert.equal(typeof reply.name, 'string'); - assert.equal(typeof reply.age, 'number'); - assert.equal(typeof reply.idle, 'number'); - assert.equal(typeof reply.flags, 'string'); - assert.equal(typeof reply.db, 'number'); - assert.equal(typeof reply.sub, 'number'); - assert.equal(typeof reply.psub, 'number'); - assert.equal(typeof reply.multi, 'number'); - assert.equal(typeof reply.qbuf, 'number'); - assert.equal(typeof reply.qbufFree, 'number'); - assert.equal(typeof reply.argvMem, 'number'); - assert.equal(typeof reply.obl, 'number'); - assert.equal(typeof reply.oll, 'number'); - assert.equal(typeof reply.omem, 'number'); - assert.equal(typeof reply.totMem, 'number'); - assert.equal(typeof reply.events, 'string'); - assert.equal(typeof reply.cmd, 'string'); - assert.equal(typeof reply.user, 'string'); - assert.equal(typeof reply.redir, 'number'); + testUtils.testWithClient('client.clientInfo', async client => { + const reply = await client.clientInfo(); + assert.equal(typeof reply.id, 'number'); + assert.equal(typeof reply.addr, 'string'); + assert.equal(typeof reply.laddr, 'string'); + assert.equal(typeof reply.fd, 'number'); + assert.equal(typeof reply.name, 'string'); + assert.equal(typeof reply.age, 'number'); + assert.equal(typeof reply.idle, 'number'); + assert.equal(typeof reply.flags, 'string'); + assert.equal(typeof reply.db, 'number'); + assert.equal(typeof reply.sub, 'number'); + assert.equal(typeof reply.psub, 'number'); + assert.equal(typeof reply.multi, 'number'); + assert.equal(typeof reply.qbuf, 'number'); + assert.equal(typeof reply.qbufFree, 'number'); + assert.equal(typeof reply.argvMem, 'number'); + assert.equal(typeof reply.obl, 'number'); + assert.equal(typeof reply.oll, 'number'); + assert.equal(typeof reply.omem, 'number'); + assert.equal(typeof reply.totMem, 'number'); + assert.equal(typeof reply.events, 'string'); + assert.equal(typeof reply.cmd, 'string'); + assert.equal(typeof reply.user, 'string'); + assert.equal(typeof reply.redir, 'number'); - if (testUtils.isVersionGreaterThan([7, 0])) { - assert.equal(typeof reply.multiMem, 'number'); - assert.equal(typeof reply.resp, 'number'); - } + if (testUtils.isVersionGreaterThan([7, 0])) { + assert.equal(typeof reply.multiMem, 'number'); + assert.equal(typeof reply.resp, 'number'); - if (testUtils.isVersionGreaterThan([7, 0, 3])) { - assert.equal(typeof reply.ssub, 'number'); - } - }, GLOBAL.SERVERS.OPEN); + if (testUtils.isVersionGreaterThan([7, 0, 3])) { + assert.equal(typeof reply.ssub, 'number'); + } + } + }, GLOBAL.SERVERS.OPEN); }); diff --git a/packages/client/lib/commands/CLIENT_INFO.ts b/packages/client/lib/commands/CLIENT_INFO.ts index fd823542f86..88721e2f8b9 100644 --- a/packages/client/lib/commands/CLIENT_INFO.ts +++ b/packages/client/lib/commands/CLIENT_INFO.ts @@ -1,94 +1,116 @@ -export const IS_READ_ONLY = true; - -export function transformArguments(): Array { - return ['CLIENT', 'INFO']; -} +import { Command, VerbatimStringReply } from '../RESP/types'; export interface ClientInfoReply { - id: number; - addr: string; - laddr?: string; // 6.2 - fd: number; - name: string; - age: number; - idle: number; - flags: string; - db: number; - sub: number; - psub: number; - ssub?: number; // 7.0.3 - multi: number; - qbuf: number; - qbufFree: number; - argvMem?: number; // 6.0 - multiMem?: number; // 7.0 - obl: number; - oll: number; - omem: number; - totMem?: number; // 6.0 - events: string; - cmd: string; - user?: string; // 6.0 - redir?: number; // 6.2 - resp?: number; // 7.0 - // 7.2 - libName?: string; - libVer?: string; + id: number; + addr: string; + /** + * available since 6.2 + */ + laddr?: string; + fd: number; + name: string; + age: number; + idle: number; + flags: string; + db: number; + sub: number; + psub: number; + /** + * available since 7.0.3 + */ + ssub?: number; + multi: number; + qbuf: number; + qbufFree: number; + /** + * available since 6.0 + */ + argvMem?: number; + /** + * available since 7.0 + */ + multiMem?: number; + obl: number; + oll: number; + omem: number; + /** + * available since 6.0 + */ + totMem?: number; + events: string; + cmd: string; + /** + * available since 6.0 + */ + user?: string; + /** + * available since 6.2 + */ + redir?: number; + /** + * available since 7.0 + */ + resp?: number; } const CLIENT_INFO_REGEX = /([^\s=]+)=([^\s]*)/g; -export function transformReply(rawReply: string): ClientInfoReply { +export default { + FIRST_KEY_INDEX: undefined, + IS_READ_ONLY: true, + transformArguments() { + return ['CLIENT', 'INFO'] + }, + transformReply(rawReply: VerbatimStringReply) { const map: Record = {}; - for (const item of rawReply.matchAll(CLIENT_INFO_REGEX)) { - map[item[1]] = item[2]; + for (const item of rawReply.toString().matchAll(CLIENT_INFO_REGEX)) { + map[item[1]] = item[2]; } const reply: ClientInfoReply = { - id: Number(map.id), - addr: map.addr, - fd: Number(map.fd), - name: map.name, - age: Number(map.age), - idle: Number(map.idle), - flags: map.flags, - db: Number(map.db), - sub: Number(map.sub), - psub: Number(map.psub), - multi: Number(map.multi), - qbuf: Number(map.qbuf), - qbufFree: Number(map['qbuf-free']), - argvMem: Number(map['argv-mem']), - obl: Number(map.obl), - oll: Number(map.oll), - omem: Number(map.omem), - totMem: Number(map['tot-mem']), - events: map.events, - cmd: map.cmd, - user: map.user, - libName: map['lib-name'], - libVer: map['lib-ver'], + id: Number(map.id), + addr: map.addr, + fd: Number(map.fd), + name: map.name, + age: Number(map.age), + idle: Number(map.idle), + flags: map.flags, + db: Number(map.db), + sub: Number(map.sub), + psub: Number(map.psub), + multi: Number(map.multi), + qbuf: Number(map.qbuf), + qbufFree: Number(map['qbuf-free']), + argvMem: Number(map['argv-mem']), + obl: Number(map.obl), + oll: Number(map.oll), + omem: Number(map.omem), + totMem: Number(map['tot-mem']), + events: map.events, + cmd: map.cmd, + user: map.user }; if (map.laddr !== undefined) { - reply.laddr = map.laddr; + reply.laddr = map.laddr; } if (map.redir !== undefined) { - reply.redir = Number(map.redir); + reply.redir = Number(map.redir); } if (map.ssub !== undefined) { - reply.ssub = Number(map.ssub); + reply.ssub = Number(map.ssub); } if (map['multi-mem'] !== undefined) { - reply.multiMem = Number(map['multi-mem']); + reply.multiMem = Number(map['multi-mem']); } if (map.resp !== undefined) { - reply.resp = Number(map.resp); + reply.resp = Number(map.resp); } return reply; -} + } +} as const satisfies Command; diff --git a/packages/client/lib/commands/CLIENT_KILL.spec.ts b/packages/client/lib/commands/CLIENT_KILL.spec.ts index 733aaca858b..79254af41f9 100644 --- a/packages/client/lib/commands/CLIENT_KILL.spec.ts +++ b/packages/client/lib/commands/CLIENT_KILL.spec.ts @@ -1,120 +1,120 @@ -import { strict as assert } from 'assert'; -import { ClientKillFilters, transformArguments } from './CLIENT_KILL'; +import { strict as assert } from 'node:assert'; +import CLIENT_KILL, { CLIENT_KILL_FILTERS } from './CLIENT_KILL'; describe('CLIENT KILL', () => { - describe('transformArguments', () => { - it('ADDRESS', () => { - assert.deepEqual( - transformArguments({ - filter: ClientKillFilters.ADDRESS, - address: 'ip:6379' - }), - ['CLIENT', 'KILL', 'ADDR', 'ip:6379'] - ); - }); + describe('transformArguments', () => { + it('ADDRESS', () => { + assert.deepEqual( + CLIENT_KILL.transformArguments({ + filter: CLIENT_KILL_FILTERS.ADDRESS, + address: 'ip:6379' + }), + ['CLIENT', 'KILL', 'ADDR', 'ip:6379'] + ); + }); + + it('LOCAL_ADDRESS', () => { + assert.deepEqual( + CLIENT_KILL.transformArguments({ + filter: CLIENT_KILL_FILTERS.LOCAL_ADDRESS, + localAddress: 'ip:6379' + }), + ['CLIENT', 'KILL', 'LADDR', 'ip:6379'] + ); + }); - it('LOCAL_ADDRESS', () => { - assert.deepEqual( - transformArguments({ - filter: ClientKillFilters.LOCAL_ADDRESS, - localAddress: 'ip:6379' - }), - ['CLIENT', 'KILL', 'LADDR', 'ip:6379'] - ); - }); + describe('ID', () => { + it('string', () => { + assert.deepEqual( + CLIENT_KILL.transformArguments({ + filter: CLIENT_KILL_FILTERS.ID, + id: '1' + }), + ['CLIENT', 'KILL', 'ID', '1'] + ); + }); - describe('ID', () => { - it('string', () => { - assert.deepEqual( - transformArguments({ - filter: ClientKillFilters.ID, - id: '1' - }), - ['CLIENT', 'KILL', 'ID', '1'] - ); - }); + it('number', () => { + assert.deepEqual( + CLIENT_KILL.transformArguments({ + filter: CLIENT_KILL_FILTERS.ID, + id: 1 + }), + ['CLIENT', 'KILL', 'ID', '1'] + ); + }); + }); - it('number', () => { - assert.deepEqual( - transformArguments({ - filter: ClientKillFilters.ID, - id: 1 - }), - ['CLIENT', 'KILL', 'ID', '1'] - ); - }); - }); + it('TYPE', () => { + assert.deepEqual( + CLIENT_KILL.transformArguments({ + filter: CLIENT_KILL_FILTERS.TYPE, + type: 'master' + }), + ['CLIENT', 'KILL', 'TYPE', 'master'] + ); + }); - it('TYPE', () => { - assert.deepEqual( - transformArguments({ - filter: ClientKillFilters.TYPE, - type: 'master' - }), - ['CLIENT', 'KILL', 'TYPE', 'master'] - ); - }); + it('USER', () => { + assert.deepEqual( + CLIENT_KILL.transformArguments({ + filter: CLIENT_KILL_FILTERS.USER, + username: 'username' + }), + ['CLIENT', 'KILL', 'USER', 'username'] + ); + }); - it('USER', () => { - assert.deepEqual( - transformArguments({ - filter: ClientKillFilters.USER, - username: 'username' - }), - ['CLIENT', 'KILL', 'USER', 'username'] - ); - }); + it('MAXAGE', () => { + assert.deepEqual( + CLIENT_KILL.transformArguments({ + filter: CLIENT_KILL_FILTERS.MAXAGE, + maxAge: 10 + }), + ['CLIENT', 'KILL', 'MAXAGE', '10'] + ); + }); - it('MAXAGE', () => { - assert.deepEqual( - transformArguments({ - filter: ClientKillFilters.MAXAGE, - maxAge: 10 - }), - ['CLIENT', 'KILL', 'MAXAGE', '10'] - ); - }); + describe('SKIP_ME', () => { + it('undefined', () => { + assert.deepEqual( + CLIENT_KILL.transformArguments(CLIENT_KILL_FILTERS.SKIP_ME), + ['CLIENT', 'KILL', 'SKIPME'] + ); + }); - describe('SKIP_ME', () => { - it('undefined', () => { - assert.deepEqual( - transformArguments(ClientKillFilters.SKIP_ME), - ['CLIENT', 'KILL', 'SKIPME'] - ); - }); + it('true', () => { + assert.deepEqual( + CLIENT_KILL.transformArguments({ + filter: CLIENT_KILL_FILTERS.SKIP_ME, + skipMe: true + }), + ['CLIENT', 'KILL', 'SKIPME', 'yes'] + ); + }); - it('true', () => { - assert.deepEqual( - transformArguments({ - filter: ClientKillFilters.SKIP_ME, - skipMe: true - }), - ['CLIENT', 'KILL', 'SKIPME', 'yes'] - ); - }); + it('false', () => { + assert.deepEqual( + CLIENT_KILL.transformArguments({ + filter: CLIENT_KILL_FILTERS.SKIP_ME, + skipMe: false + }), + ['CLIENT', 'KILL', 'SKIPME', 'no'] + ); + }); + }); - it('false', () => { - assert.deepEqual( - transformArguments({ - filter: ClientKillFilters.SKIP_ME, - skipMe: false - }), - ['CLIENT', 'KILL', 'SKIPME', 'no'] - ); - }); - }); - - it('TYPE & SKIP_ME', () => { - assert.deepEqual( - transformArguments([ - { - filter: ClientKillFilters.TYPE, - type: 'master' - }, - ClientKillFilters.SKIP_ME - ]), - ['CLIENT', 'KILL', 'TYPE', 'master', 'SKIPME'] - ); - }); + it('TYPE & SKIP_ME', () => { + assert.deepEqual( + CLIENT_KILL.transformArguments([ + { + filter: CLIENT_KILL_FILTERS.TYPE, + type: 'master' + }, + CLIENT_KILL_FILTERS.SKIP_ME + ]), + ['CLIENT', 'KILL', 'TYPE', 'master', 'SKIPME'] + ); }); + }); }); diff --git a/packages/client/lib/commands/CLIENT_KILL.ts b/packages/client/lib/commands/CLIENT_KILL.ts index b1a53df64d8..c5eb5304c57 100644 --- a/packages/client/lib/commands/CLIENT_KILL.ts +++ b/packages/client/lib/commands/CLIENT_KILL.ts @@ -1,104 +1,109 @@ -import { RedisCommandArguments } from '.'; - -export enum ClientKillFilters { - ADDRESS = 'ADDR', - LOCAL_ADDRESS = 'LADDR', - ID = 'ID', - TYPE = 'TYPE', - USER = 'USER', - SKIP_ME = 'SKIPME', - MAXAGE = 'MAXAGE' +import { RedisArgument, NumberReply, Command } from '../RESP/types'; + +export const CLIENT_KILL_FILTERS = { + ADDRESS: 'ADDR', + LOCAL_ADDRESS: 'LADDR', + ID: 'ID', + TYPE: 'TYPE', + USER: 'USER', + SKIP_ME: 'SKIPME', + MAXAGE: 'MAXAGE' +} as const; + +type CLIENT_KILL_FILTERS = typeof CLIENT_KILL_FILTERS; + +export interface ClientKillFilterCommon { + filter: T; } -interface KillFilter { - filter: T; +export interface ClientKillAddress extends ClientKillFilterCommon { + address: `${string}:${number}`; } -interface KillAddress extends KillFilter { - address: `${string}:${number}`; +export interface ClientKillLocalAddress extends ClientKillFilterCommon { + localAddress: `${string}:${number}`; } -interface KillLocalAddress extends KillFilter { - localAddress: `${string}:${number}`; +export interface ClientKillId extends ClientKillFilterCommon { + id: number | `${number}`; } -interface KillId extends KillFilter { - id: number | `${number}`; +export interface ClientKillType extends ClientKillFilterCommon { + type: 'normal' | 'master' | 'replica' | 'pubsub'; } -interface KillType extends KillFilter { - type: 'normal' | 'master' | 'replica' | 'pubsub'; +export interface ClientKillUser extends ClientKillFilterCommon { + username: string; } -interface KillUser extends KillFilter { - username: string; -} - -type KillSkipMe = ClientKillFilters.SKIP_ME | (KillFilter & { - skipMe: boolean; +export type ClientKillSkipMe = CLIENT_KILL_FILTERS['SKIP_ME'] | (ClientKillFilterCommon & { + skipMe: boolean; }); -interface KillMaxAge extends KillFilter { - maxAge: number; +export interface ClientKillMaxAge extends ClientKillFilterCommon { + maxAge: number; } -type KillFilters = KillAddress | KillLocalAddress | KillId | KillType | KillUser | KillSkipMe | KillMaxAge; +export type ClientKillFilter = ClientKillAddress | ClientKillLocalAddress | ClientKillId | ClientKillType | ClientKillUser | ClientKillSkipMe | ClientKillMaxAge; -export function transformArguments(filters: KillFilters | Array): RedisCommandArguments { +export default { + FIRST_KEY_INDEX: undefined, + IS_READ_ONLY: true, + transformArguments(filters: ClientKillFilter | Array) { const args = ['CLIENT', 'KILL']; if (Array.isArray(filters)) { - for (const filter of filters) { - pushFilter(args, filter); - } + for (const filter of filters) { + pushFilter(args, filter); + } } else { - pushFilter(args, filters); + pushFilter(args, filters); } return args; + }, + transformReply: undefined as unknown as () => NumberReply +} as const satisfies Command; + +function pushFilter(args: Array, filter: ClientKillFilter): void { + if (filter === CLIENT_KILL_FILTERS.SKIP_ME) { + args.push('SKIPME'); + return; + } + + args.push(filter.filter); + + switch (filter.filter) { + case CLIENT_KILL_FILTERS.ADDRESS: + args.push(filter.address); + break; + + case CLIENT_KILL_FILTERS.LOCAL_ADDRESS: + args.push(filter.localAddress); + break; + + case CLIENT_KILL_FILTERS.ID: + args.push( + typeof filter.id === 'number' ? + filter.id.toString() : + filter.id + ); + break; + + case CLIENT_KILL_FILTERS.TYPE: + args.push(filter.type); + break; + + case CLIENT_KILL_FILTERS.USER: + args.push(filter.username); + break; + + case CLIENT_KILL_FILTERS.SKIP_ME: + args.push(filter.skipMe ? 'yes' : 'no'); + break; + + case CLIENT_KILL_FILTERS.MAXAGE: + args.push(filter.maxAge.toString()); + break; + } } - -function pushFilter(args: RedisCommandArguments, filter: KillFilters): void { - if (filter === ClientKillFilters.SKIP_ME) { - args.push('SKIPME'); - return; - } - - args.push(filter.filter); - - switch(filter.filter) { - case ClientKillFilters.ADDRESS: - args.push(filter.address); - break; - - case ClientKillFilters.LOCAL_ADDRESS: - args.push(filter.localAddress); - break; - - case ClientKillFilters.ID: - args.push( - typeof filter.id === 'number' ? - filter.id.toString() : - filter.id - ); - break; - - case ClientKillFilters.TYPE: - args.push(filter.type); - break; - - case ClientKillFilters.USER: - args.push(filter.username); - break; - - case ClientKillFilters.SKIP_ME: - args.push(filter.skipMe ? 'yes' : 'no'); - break; - - case ClientKillFilters.MAXAGE: - args.push(filter.maxAge.toString()); - break; - } -} - -export declare function transformReply(): number; diff --git a/packages/client/lib/commands/CLIENT_LIST.spec.ts b/packages/client/lib/commands/CLIENT_LIST.spec.ts index c9c720e12ef..e967a8dc0ff 100644 --- a/packages/client/lib/commands/CLIENT_LIST.spec.ts +++ b/packages/client/lib/commands/CLIENT_LIST.spec.ts @@ -1,78 +1,77 @@ -import { strict as assert } from 'assert'; -import { transformArguments, transformReply } from './CLIENT_LIST'; +import { strict as assert } from 'node:assert'; +import CLIENT_LIST from './CLIENT_LIST'; import testUtils, { GLOBAL } from '../test-utils'; describe('CLIENT LIST', () => { - describe('transformArguments', () => { - it('simple', () => { - assert.deepEqual( - transformArguments(), - ['CLIENT', 'LIST'] - ); - }); - - it('with TYPE', () => { - assert.deepEqual( - transformArguments({ - TYPE: 'NORMAL' - }), - ['CLIENT', 'LIST', 'TYPE', 'NORMAL'] - ); - }); - - it('with ID', () => { - assert.deepEqual( - transformArguments({ - ID: ['1', '2'] - }), - ['CLIENT', 'LIST', 'ID', '1', '2'] - ); - }); + describe('transformArguments', () => { + it('simple', () => { + assert.deepEqual( + CLIENT_LIST.transformArguments(), + ['CLIENT', 'LIST'] + ); }); - testUtils.testWithClient('client.clientList', async client => { - const reply = await client.clientList(); - assert.ok(Array.isArray(reply)); - - for (const item of reply) { - assert.equal(typeof item.id, 'number'); - assert.equal(typeof item.addr, 'string'); - assert.equal(typeof item.fd, 'number'); - assert.equal(typeof item.name, 'string'); - assert.equal(typeof item.age, 'number'); - assert.equal(typeof item.idle, 'number'); - assert.equal(typeof item.flags, 'string'); - assert.equal(typeof item.db, 'number'); - assert.equal(typeof item.sub, 'number'); - assert.equal(typeof item.psub, 'number'); - assert.equal(typeof item.multi, 'number'); - assert.equal(typeof item.qbuf, 'number'); - assert.equal(typeof item.qbufFree, 'number'); - assert.equal(typeof item.obl, 'number'); - assert.equal(typeof item.oll, 'number'); - assert.equal(typeof item.omem, 'number'); - assert.equal(typeof item.events, 'string'); - assert.equal(typeof item.cmd, 'string'); + it('with TYPE', () => { + assert.deepEqual( + CLIENT_LIST.transformArguments({ + TYPE: 'NORMAL' + }), + ['CLIENT', 'LIST', 'TYPE', 'NORMAL'] + ); + }); - if (testUtils.isVersionGreaterThan([6, 0])) { - assert.equal(typeof item.argvMem, 'number'); - assert.equal(typeof item.totMem, 'number'); - assert.equal(typeof item.user, 'string'); - } + it('with ID', () => { + assert.deepEqual( + CLIENT_LIST.transformArguments({ + ID: ['1', '2'] + }), + ['CLIENT', 'LIST', 'ID', '1', '2'] + ); + }); + }); - if (testUtils.isVersionGreaterThan([6, 2])) { - assert.equal(typeof item.redir, 'number'); - assert.equal(typeof item.laddr, 'string'); - } + testUtils.testWithClient('client.clientList', async client => { + const reply = await client.clientList(); + assert.ok(Array.isArray(reply)); + for (const item of reply) { + assert.equal(typeof item.id, 'number'); + assert.equal(typeof item.addr, 'string'); + assert.equal(typeof item.fd, 'number'); + assert.equal(typeof item.name, 'string'); + assert.equal(typeof item.age, 'number'); + assert.equal(typeof item.idle, 'number'); + assert.equal(typeof item.flags, 'string'); + assert.equal(typeof item.db, 'number'); + assert.equal(typeof item.sub, 'number'); + assert.equal(typeof item.psub, 'number'); + assert.equal(typeof item.multi, 'number'); + assert.equal(typeof item.qbuf, 'number'); + assert.equal(typeof item.qbufFree, 'number'); + assert.equal(typeof item.obl, 'number'); + assert.equal(typeof item.oll, 'number'); + assert.equal(typeof item.omem, 'number'); + assert.equal(typeof item.events, 'string'); + assert.equal(typeof item.cmd, 'string'); - if (testUtils.isVersionGreaterThan([7, 0])) { - assert.equal(typeof item.multiMem, 'number'); - assert.equal(typeof item.resp, 'number'); - } + if (testUtils.isVersionGreaterThan([6, 0])) { + assert.equal(typeof item.argvMem, 'number'); + assert.equal(typeof item.totMem, 'number'); + assert.equal(typeof item.user, 'string'); + + if (testUtils.isVersionGreaterThan([6, 2])) { + assert.equal(typeof item.redir, 'number'); + assert.equal(typeof item.laddr, 'string'); + + if (testUtils.isVersionGreaterThan([7, 0])) { + assert.equal(typeof item.multiMem, 'number'); + assert.equal(typeof item.resp, 'number'); if (testUtils.isVersionGreaterThan([7, 0, 3])) { - assert.equal(typeof item.ssub, 'number'); + assert.equal(typeof item.ssub, 'number'); } + } } - }, GLOBAL.SERVERS.OPEN); + } + } + }, GLOBAL.SERVERS.OPEN); }); diff --git a/packages/client/lib/commands/CLIENT_LIST.ts b/packages/client/lib/commands/CLIENT_LIST.ts index 6f71dc7d999..dc43fb8855d 100644 --- a/packages/client/lib/commands/CLIENT_LIST.ts +++ b/packages/client/lib/commands/CLIENT_LIST.ts @@ -1,43 +1,44 @@ -import { RedisCommandArguments, RedisCommandArgument } from '.'; -import { pushVerdictArguments } from './generic-transformers'; -import { transformReply as transformClientInfoReply, ClientInfoReply } from './CLIENT_INFO'; +import { RedisArgument, VerbatimStringReply, Command } from '../RESP/types'; +import { pushVariadicArguments } from './generic-transformers'; +import CLIENT_INFO, { ClientInfoReply } from './CLIENT_INFO'; -interface ListFilterType { - TYPE: 'NORMAL' | 'MASTER' | 'REPLICA' | 'PUBSUB'; - ID?: never; +export interface ListFilterType { + TYPE: 'NORMAL' | 'MASTER' | 'REPLICA' | 'PUBSUB'; + ID?: never; } -interface ListFilterId { - ID: Array; - TYPE?: never; +export interface ListFilterId { + ID: Array; + TYPE?: never; } export type ListFilter = ListFilterType | ListFilterId; -export const IS_READ_ONLY = true; - -export function transformArguments(filter?: ListFilter): RedisCommandArguments { - let args: RedisCommandArguments = ['CLIENT', 'LIST']; +export default { + FIRST_KEY_INDEX: undefined, + IS_READ_ONLY: true, + transformArguments(filter?: ListFilter) { + let args: Array = ['CLIENT', 'LIST']; if (filter) { - if (filter.TYPE !== undefined) { - args.push('TYPE', filter.TYPE); - } else { - args.push('ID'); - args = pushVerdictArguments(args, filter.ID); - } + if (filter.TYPE !== undefined) { + args.push('TYPE', filter.TYPE); + } else { + args.push('ID'); + args = pushVariadicArguments(args, filter.ID); + } } return args; -} - -export function transformReply(rawReply: string): Array { - const split = rawReply.split('\n'), - length = split.length - 1, - reply: Array = []; + }, + transformReply(rawReply: VerbatimStringReply): Array { + const split = rawReply.toString().split('\n'), + length = split.length - 1, + reply: Array = []; for (let i = 0; i < length; i++) { - reply.push(transformClientInfoReply(split[i])); + reply.push(CLIENT_INFO.transformReply(split[i] as unknown as VerbatimStringReply)); } - + return reply; -} + } +} as const satisfies Command; diff --git a/packages/client/lib/commands/CLIENT_NO-EVICT.spec.ts b/packages/client/lib/commands/CLIENT_NO-EVICT.spec.ts index df8903f0646..5de4dfd7604 100644 --- a/packages/client/lib/commands/CLIENT_NO-EVICT.spec.ts +++ b/packages/client/lib/commands/CLIENT_NO-EVICT.spec.ts @@ -1,30 +1,30 @@ -import { strict as assert } from 'assert'; +import { strict as assert } from 'node:assert'; import testUtils, { GLOBAL } from '../test-utils'; -import { transformArguments } from './CLIENT_NO-EVICT'; +import CLIENT_NO_EVICT from './CLIENT_NO-EVICT'; describe('CLIENT NO-EVICT', () => { - testUtils.isVersionGreaterThanHook([7]); + testUtils.isVersionGreaterThanHook([7]); - describe('transformArguments', () => { - it('true', () => { - assert.deepEqual( - transformArguments(true), - ['CLIENT', 'NO-EVICT', 'ON'] - ); - }); + describe('transformArguments', () => { + it('true', () => { + assert.deepEqual( + CLIENT_NO_EVICT.transformArguments(true), + ['CLIENT', 'NO-EVICT', 'ON'] + ); + }); - it('false', () => { - assert.deepEqual( - transformArguments(false), - ['CLIENT', 'NO-EVICT', 'OFF'] - ); - }); + it('false', () => { + assert.deepEqual( + CLIENT_NO_EVICT.transformArguments(false), + ['CLIENT', 'NO-EVICT', 'OFF'] + ); }); + }); - testUtils.testWithClient('client.clientNoEvict', async client => { - assert.equal( - await client.clientNoEvict(true), - 'OK' - ); - }, GLOBAL.SERVERS.OPEN); + testUtils.testWithClient('client.clientNoEvict', async client => { + assert.equal( + await client.clientNoEvict(true), + 'OK' + ); + }, GLOBAL.SERVERS.OPEN); }); diff --git a/packages/client/lib/commands/CLIENT_NO-EVICT.ts b/packages/client/lib/commands/CLIENT_NO-EVICT.ts index 86edbde1d23..82aa50074ba 100644 --- a/packages/client/lib/commands/CLIENT_NO-EVICT.ts +++ b/packages/client/lib/commands/CLIENT_NO-EVICT.ts @@ -1,11 +1,14 @@ -import { RedisCommandArguments } from '.'; +import { SimpleStringReply, Command } from '../RESP/types'; -export function transformArguments(value: boolean): RedisCommandArguments { +export default { + FIRST_KEY_INDEX: undefined, + IS_READ_ONLY: true, + transformArguments(value: boolean) { return [ - 'CLIENT', - 'NO-EVICT', - value ? 'ON' : 'OFF' + 'CLIENT', + 'NO-EVICT', + value ? 'ON' : 'OFF' ]; -} - -export declare function transformReply(): 'OK' | Buffer; + }, + transformReply: undefined as unknown as () => SimpleStringReply<'OK'> +} as const satisfies Command; diff --git a/packages/client/lib/commands/CLIENT_NO-TOUCH.spec.ts b/packages/client/lib/commands/CLIENT_NO-TOUCH.spec.ts index 80ee0ada1fd..e58c22d9c6e 100644 --- a/packages/client/lib/commands/CLIENT_NO-TOUCH.spec.ts +++ b/packages/client/lib/commands/CLIENT_NO-TOUCH.spec.ts @@ -1,30 +1,30 @@ import { strict as assert } from 'assert'; import testUtils, { GLOBAL } from '../test-utils'; -import { transformArguments } from './CLIENT_NO-TOUCH'; +import CLIENT_NO_TOUCH from './CLIENT_NO-TOUCH'; describe('CLIENT NO-TOUCH', () => { - testUtils.isVersionGreaterThanHook([7, 2]); + testUtils.isVersionGreaterThanHook([7, 2]); - describe('transformArguments', () => { - it('true', () => { - assert.deepEqual( - transformArguments(true), - ['CLIENT', 'NO-TOUCH', 'ON'] - ); - }); + describe('transformArguments', () => { + it('true', () => { + assert.deepEqual( + CLIENT_NO_TOUCH.transformArguments(true), + ['CLIENT', 'NO-TOUCH', 'ON'] + ); + }); - it('false', () => { - assert.deepEqual( - transformArguments(false), - ['CLIENT', 'NO-TOUCH', 'OFF'] - ); - }); + it('false', () => { + assert.deepEqual( + CLIENT_NO_TOUCH.transformArguments(false), + ['CLIENT', 'NO-TOUCH', 'OFF'] + ); }); + }); - testUtils.testWithClient('client.clientNoTouch', async client => { - assert.equal( - await client.clientNoTouch(true), - 'OK' - ); - }, GLOBAL.SERVERS.OPEN); + testUtils.testWithClient('client.clientNoTouch', async client => { + assert.equal( + await client.clientNoTouch(true), + 'OK' + ); + }, GLOBAL.SERVERS.OPEN); }); diff --git a/packages/client/lib/commands/CLIENT_NO-TOUCH.ts b/packages/client/lib/commands/CLIENT_NO-TOUCH.ts index d11f693dbab..a6fc5eb1765 100644 --- a/packages/client/lib/commands/CLIENT_NO-TOUCH.ts +++ b/packages/client/lib/commands/CLIENT_NO-TOUCH.ts @@ -1,11 +1,15 @@ -import { RedisCommandArguments } from '.'; +import { SimpleStringReply, Command } from '../RESP/types'; -export function transformArguments(value: boolean): RedisCommandArguments { +export default { + FIRST_KEY_INDEX: undefined, + IS_READ_ONLY: true, + transformArguments(value: boolean) { return [ - 'CLIENT', - 'NO-TOUCH', - value ? 'ON' : 'OFF' + 'CLIENT', + 'NO-TOUCH', + value ? 'ON' : 'OFF' ]; -} + }, + transformReply: undefined as unknown as () => SimpleStringReply<'OK'> +} as const satisfies Command; -export declare function transformReply(): 'OK' | Buffer; diff --git a/packages/client/lib/commands/CLIENT_PAUSE.spec.ts b/packages/client/lib/commands/CLIENT_PAUSE.spec.ts index 1376ff41eed..a30f9075072 100644 --- a/packages/client/lib/commands/CLIENT_PAUSE.spec.ts +++ b/packages/client/lib/commands/CLIENT_PAUSE.spec.ts @@ -1,28 +1,28 @@ -import { strict as assert } from 'assert'; +import { strict as assert } from 'node:assert'; import testUtils, { GLOBAL } from '../test-utils'; -import { transformArguments } from './CLIENT_PAUSE'; +import CLIENT_PAUSE from './CLIENT_PAUSE'; describe('CLIENT PAUSE', () => { - describe('transformArguments', () => { - it('simple', () => { - assert.deepEqual( - transformArguments(0), - ['CLIENT', 'PAUSE', '0'] - ); - }); + describe('transformArguments', () => { + it('simple', () => { + assert.deepEqual( + CLIENT_PAUSE.transformArguments(0), + ['CLIENT', 'PAUSE', '0'] + ); + }); - it('with mode', () => { - assert.deepEqual( - transformArguments(0, 'ALL'), - ['CLIENT', 'PAUSE', '0', 'ALL'] - ); - }); + it('with mode', () => { + assert.deepEqual( + CLIENT_PAUSE.transformArguments(0, 'ALL'), + ['CLIENT', 'PAUSE', '0', 'ALL'] + ); }); + }); - testUtils.testWithClient('client.clientPause', async client => { - assert.equal( - await client.clientPause(0), - 'OK' - ); - }, GLOBAL.SERVERS.OPEN); + testUtils.testWithClient('client.clientPause', async client => { + assert.equal( + await client.clientPause(0), + 'OK' + ); + }, GLOBAL.SERVERS.OPEN); }); diff --git a/packages/client/lib/commands/CLIENT_PAUSE.ts b/packages/client/lib/commands/CLIENT_PAUSE.ts index 090002272c9..87b4177ed8c 100644 --- a/packages/client/lib/commands/CLIENT_PAUSE.ts +++ b/packages/client/lib/commands/CLIENT_PAUSE.ts @@ -1,20 +1,20 @@ -import { RedisCommandArguments } from '.'; +import { SimpleStringReply, Command } from '../RESP/types'; -export function transformArguments( - timeout: number, - mode?: 'WRITE' | 'ALL' -): RedisCommandArguments { +export default { + FIRST_KEY_INDEX: undefined, + IS_READ_ONLY: true, + transformArguments(timeout: number, mode?: 'WRITE' | 'ALL') { const args = [ - 'CLIENT', - 'PAUSE', - timeout.toString() + 'CLIENT', + 'PAUSE', + timeout.toString() ]; if (mode) { - args.push(mode); + args.push(mode); } return args; -} - -export declare function transformReply(): 'OK' | Buffer; + }, + transformReply: undefined as unknown as () => SimpleStringReply<'OK'> +} as const satisfies Command; diff --git a/packages/client/lib/commands/CLIENT_SETNAME.spec.ts b/packages/client/lib/commands/CLIENT_SETNAME.spec.ts index 96618f3f79f..8e6b914791d 100644 --- a/packages/client/lib/commands/CLIENT_SETNAME.spec.ts +++ b/packages/client/lib/commands/CLIENT_SETNAME.spec.ts @@ -1,11 +1,20 @@ -import { strict as assert } from 'assert'; -import { transformArguments } from './CLIENT_SETNAME'; +import { strict as assert } from 'node:assert'; +import testUtils, { GLOBAL } from '../test-utils'; + +import CLIENT_SETNAME from './CLIENT_SETNAME'; describe('CLIENT SETNAME', () => { - it('transformArguments', () => { - assert.deepEqual( - transformArguments('name'), - ['CLIENT', 'SETNAME', 'name'] - ); - }); + it('transformArguments', () => { + assert.deepEqual( + CLIENT_SETNAME.transformArguments('name'), + ['CLIENT', 'SETNAME', 'name'] + ); + }); + + testUtils.testWithClient('client.clientSetName', async client => { + assert.equal( + await client.clientSetName('name'), + 'OK' + ); + }, GLOBAL.SERVERS.OPEN); }); diff --git a/packages/client/lib/commands/CLIENT_SETNAME.ts b/packages/client/lib/commands/CLIENT_SETNAME.ts index f5cf1c786fb..e2e2a921958 100644 --- a/packages/client/lib/commands/CLIENT_SETNAME.ts +++ b/packages/client/lib/commands/CLIENT_SETNAME.ts @@ -1,7 +1,10 @@ -import { RedisCommandArgument, RedisCommandArguments } from '.'; +import { RedisArgument, SimpleStringReply, Command } from '../RESP/types'; -export function transformArguments(name: RedisCommandArgument): RedisCommandArguments { +export default { + FIRST_KEY_INDEX: undefined, + IS_READ_ONLY: true, + transformArguments(name: RedisArgument) { return ['CLIENT', 'SETNAME', name]; -} - -export declare function transformReply(): RedisCommandArgument; + }, + transformReply: undefined as unknown as () => SimpleStringReply<'OK'> +} as const satisfies Command; diff --git a/packages/client/lib/commands/CLIENT_TRACKING.spec.ts b/packages/client/lib/commands/CLIENT_TRACKING.spec.ts index bbd0b13e777..98fe091fb1b 100644 --- a/packages/client/lib/commands/CLIENT_TRACKING.spec.ts +++ b/packages/client/lib/commands/CLIENT_TRACKING.spec.ts @@ -1,101 +1,101 @@ -import { strict as assert } from 'assert'; +import { strict as assert } from 'node:assert'; import testUtils, { GLOBAL } from '../test-utils'; -import { transformArguments } from './CLIENT_TRACKING'; +import CLIENT_TRACKING from './CLIENT_TRACKING'; describe('CLIENT TRACKING', () => { - testUtils.isVersionGreaterThanHook([6]); + testUtils.isVersionGreaterThanHook([6]); - describe('transformArguments', () => { - describe('true', () => { - it('simple', () => { - assert.deepEqual( - transformArguments(true), - ['CLIENT', 'TRACKING', 'ON'] - ); - }); + describe('transformArguments', () => { + describe('true', () => { + it('simple', () => { + assert.deepEqual( + CLIENT_TRACKING.transformArguments(true), + ['CLIENT', 'TRACKING', 'ON'] + ); + }); - it('with REDIRECT', () => { - assert.deepEqual( - transformArguments(true, { - REDIRECT: 1 - }), - ['CLIENT', 'TRACKING', 'ON', 'REDIRECT', '1'] - ); - }); + it('with REDIRECT', () => { + assert.deepEqual( + CLIENT_TRACKING.transformArguments(true, { + REDIRECT: 1 + }), + ['CLIENT', 'TRACKING', 'ON', 'REDIRECT', '1'] + ); + }); - describe('with BCAST', () => { - it('simple', () => { - assert.deepEqual( - transformArguments(true, { - BCAST: true - }), - ['CLIENT', 'TRACKING', 'ON', 'BCAST'] - ); - }); + describe('with BCAST', () => { + it('simple', () => { + assert.deepEqual( + CLIENT_TRACKING.transformArguments(true, { + BCAST: true + }), + ['CLIENT', 'TRACKING', 'ON', 'BCAST'] + ); + }); - describe('with PREFIX', () => { - it('string', () => { - assert.deepEqual( - transformArguments(true, { - BCAST: true, - PREFIX: 'prefix' - }), - ['CLIENT', 'TRACKING', 'ON', 'BCAST', 'PREFIX', 'prefix'] - ); - }); + describe('with PREFIX', () => { + it('string', () => { + assert.deepEqual( + CLIENT_TRACKING.transformArguments(true, { + BCAST: true, + PREFIX: 'prefix' + }), + ['CLIENT', 'TRACKING', 'ON', 'BCAST', 'PREFIX', 'prefix'] + ); + }); - it('array', () => { - assert.deepEqual( - transformArguments(true, { - BCAST: true, - PREFIX: ['1', '2'] - }), - ['CLIENT', 'TRACKING', 'ON', 'BCAST', 'PREFIX', '1', 'PREFIX', '2'] - ); - }); - }); - }); + it('array', () => { + assert.deepEqual( + CLIENT_TRACKING.transformArguments(true, { + BCAST: true, + PREFIX: ['1', '2'] + }), + ['CLIENT', 'TRACKING', 'ON', 'BCAST', 'PREFIX', '1', 'PREFIX', '2'] + ); + }); + }); + }); - it('with OPTIN', () => { - assert.deepEqual( - transformArguments(true, { - OPTIN: true - }), - ['CLIENT', 'TRACKING', 'ON', 'OPTIN'] - ); - }); + it('with OPTIN', () => { + assert.deepEqual( + CLIENT_TRACKING.transformArguments(true, { + OPTIN: true + }), + ['CLIENT', 'TRACKING', 'ON', 'OPTIN'] + ); + }); - it('with OPTOUT', () => { - assert.deepEqual( - transformArguments(true, { - OPTOUT: true - }), - ['CLIENT', 'TRACKING', 'ON', 'OPTOUT'] - ); - }); + it('with OPTOUT', () => { + assert.deepEqual( + CLIENT_TRACKING.transformArguments(true, { + OPTOUT: true + }), + ['CLIENT', 'TRACKING', 'ON', 'OPTOUT'] + ); + }); - it('with NOLOOP', () => { - assert.deepEqual( - transformArguments(true, { - NOLOOP: true - }), - ['CLIENT', 'TRACKING', 'ON', 'NOLOOP'] - ); - }); - }); + it('with NOLOOP', () => { + assert.deepEqual( + CLIENT_TRACKING.transformArguments(true, { + NOLOOP: true + }), + ['CLIENT', 'TRACKING', 'ON', 'NOLOOP'] + ); + }); + }); - it('false', () => { - assert.deepEqual( - transformArguments(false), - ['CLIENT', 'TRACKING', 'OFF'] - ); - }); + it('false', () => { + assert.deepEqual( + CLIENT_TRACKING.transformArguments(false), + ['CLIENT', 'TRACKING', 'OFF'] + ); }); + }); - testUtils.testWithClient('client.clientTracking', async client => { - assert.equal( - await client.clientTracking(false), - 'OK' - ); - }, GLOBAL.SERVERS.OPEN); + testUtils.testWithClient('client.clientTracking', async client => { + assert.equal( + await client.clientTracking(false), + 'OK' + ); + }, GLOBAL.SERVERS.OPEN); }); diff --git a/packages/client/lib/commands/CLIENT_TRACKING.ts b/packages/client/lib/commands/CLIENT_TRACKING.ts index c70702706e4..a783ce35894 100644 --- a/packages/client/lib/commands/CLIENT_TRACKING.ts +++ b/packages/client/lib/commands/CLIENT_TRACKING.ts @@ -1,83 +1,87 @@ -import { RedisCommandArgument, RedisCommandArguments } from '.'; +import { RedisArgument, SimpleStringReply, Command } from '../RESP/types'; +import { RedisVariadicArgument } from './generic-transformers'; interface CommonOptions { - REDIRECT?: number; - NOLOOP?: boolean; + REDIRECT?: number; + NOLOOP?: boolean; } interface BroadcastOptions { - BCAST?: boolean; - PREFIX?: RedisCommandArgument | Array; + BCAST?: boolean; + PREFIX?: RedisVariadicArgument; } interface OptInOptions { - OPTIN?: boolean; + OPTIN?: boolean; } interface OptOutOptions { - OPTOUT?: boolean; + OPTOUT?: boolean; } -type ClientTrackingOptions = CommonOptions & ( - BroadcastOptions | - OptInOptions | - OptOutOptions +export type ClientTrackingOptions = CommonOptions & ( + BroadcastOptions | + OptInOptions | + OptOutOptions ); -export function transformArguments( +export default { + FIRST_KEY_INDEX: undefined, + IS_READ_ONLY: true, + transformArguments( mode: M, - options?: M extends true ? ClientTrackingOptions : undefined -): RedisCommandArguments { - const args: RedisCommandArguments = [ - 'CLIENT', - 'TRACKING', - mode ? 'ON' : 'OFF' + options?: M extends true ? ClientTrackingOptions : never + ) { + const args: Array = [ + 'CLIENT', + 'TRACKING', + mode ? 'ON' : 'OFF' ]; if (mode) { - if (options?.REDIRECT) { - args.push( - 'REDIRECT', - options.REDIRECT.toString() - ); - } + if (options?.REDIRECT) { + args.push( + 'REDIRECT', + options.REDIRECT.toString() + ); + } - if (isBroadcast(options)) { - args.push('BCAST'); + if (isBroadcast(options)) { + args.push('BCAST'); - if (options?.PREFIX) { - if (Array.isArray(options.PREFIX)) { - for (const prefix of options.PREFIX) { - args.push('PREFIX', prefix); - } - } else { - args.push('PREFIX', options.PREFIX); - } + if (options?.PREFIX) { + if (Array.isArray(options.PREFIX)) { + for (const prefix of options.PREFIX) { + args.push('PREFIX', prefix); } - } else if (isOptIn(options)) { - args.push('OPTIN'); - } else if (isOptOut(options)) { - args.push('OPTOUT'); + } else { + args.push('PREFIX', options.PREFIX); + } } + } else if (isOptIn(options)) { + args.push('OPTIN'); + } else if (isOptOut(options)) { + args.push('OPTOUT'); + } - if (options?.NOLOOP) { - args.push('NOLOOP'); - } + if (options?.NOLOOP) { + args.push('NOLOOP'); + } } return args; -} + }, + transformReply: undefined as unknown as () => SimpleStringReply<'OK'> +} as const satisfies Command; function isBroadcast(options?: ClientTrackingOptions): options is BroadcastOptions { - return (options as BroadcastOptions)?.BCAST === true; + return (options as BroadcastOptions)?.BCAST === true; } function isOptIn(options?: ClientTrackingOptions): options is OptInOptions { - return (options as OptInOptions)?.OPTIN === true; + return (options as OptInOptions)?.OPTIN === true; } function isOptOut(options?: ClientTrackingOptions): options is OptOutOptions { - return (options as OptOutOptions)?.OPTOUT === true; + return (options as OptOutOptions)?.OPTOUT === true; } - -export declare function transformReply(): 'OK' | Buffer; diff --git a/packages/client/lib/commands/CLIENT_TRACKINGINFO.spec.ts b/packages/client/lib/commands/CLIENT_TRACKINGINFO.spec.ts index 49bffe7612d..1cefbd27d53 100644 --- a/packages/client/lib/commands/CLIENT_TRACKINGINFO.spec.ts +++ b/packages/client/lib/commands/CLIENT_TRACKINGINFO.spec.ts @@ -1,25 +1,25 @@ -import { strict as assert } from 'assert'; +import { strict as assert } from 'node:assert'; import testUtils, { GLOBAL } from '../test-utils'; -import { transformArguments } from './CLIENT_TRACKINGINFO'; +import CLIENT_TRACKINGINFO from './CLIENT_TRACKINGINFO'; describe('CLIENT TRACKINGINFO', () => { - testUtils.isVersionGreaterThanHook([6, 2]); + testUtils.isVersionGreaterThanHook([6, 2]); - it('transformArguments', () => { - assert.deepEqual( - transformArguments(), - ['CLIENT', 'TRACKINGINFO'] - ); - }); + it('transformArguments', () => { + assert.deepEqual( + CLIENT_TRACKINGINFO.transformArguments(), + ['CLIENT', 'TRACKINGINFO'] + ); + }); - testUtils.testWithClient('client.clientTrackingInfo', async client => { - assert.deepEqual( - await client.clientTrackingInfo(), - { - flags: new Set(['off']), - redirect: -1, - prefixes: [] - } - ); - }, GLOBAL.SERVERS.OPEN); + testUtils.testWithClient('client.clientTrackingInfo', async client => { + assert.deepEqual( + await client.clientTrackingInfo(), + { + flags: ['off'], + redirect: -1, + prefixes: [] + } + ); + }, GLOBAL.SERVERS.OPEN); }); diff --git a/packages/client/lib/commands/CLIENT_TRACKINGINFO.ts b/packages/client/lib/commands/CLIENT_TRACKINGINFO.ts index 7c883fc6997..d969ba0219e 100644 --- a/packages/client/lib/commands/CLIENT_TRACKINGINFO.ts +++ b/packages/client/lib/commands/CLIENT_TRACKINGINFO.ts @@ -1,28 +1,23 @@ -import { RedisCommandArguments } from '.'; +import { TuplesToMapReply, BlobStringReply, SetReply, NumberReply, ArrayReply, UnwrapReply, Resp2Reply, Command } from '../RESP/types'; -export function transformArguments(): RedisCommandArguments { - return ['CLIENT', 'TRACKINGINFO']; -} - -type RawReply = [ - 'flags', - Array, - 'redirect', - number, - 'prefixes', - Array -]; +type TrackingInfo = TuplesToMapReply<[ + [BlobStringReply<'flags'>, SetReply], + [BlobStringReply<'redirect'>, NumberReply], + [BlobStringReply<'prefixes'>, ArrayReply] +]>; -interface Reply { - flags: Set; - redirect: number; - prefixes: Array; -} - -export function transformReply(reply: RawReply): Reply { - return { - flags: new Set(reply[1]), - redirect: reply[3], - prefixes: reply[5] - }; -} +export default { + FIRST_KEY_INDEX: undefined, + IS_READ_ONLY: true, + transformArguments() { + return ['CLIENT', 'TRACKINGINFO']; + }, + transformReply: { + 2: (reply: UnwrapReply>) => ({ + flags: reply[1], + redirect: reply[3], + prefixes: reply[5] + }), + 3: undefined as unknown as () => TrackingInfo + } +} as const satisfies Command; diff --git a/packages/client/lib/commands/CLIENT_UNPAUSE.spec.ts b/packages/client/lib/commands/CLIENT_UNPAUSE.spec.ts index 73c731ee87f..bddf3ca0f02 100644 --- a/packages/client/lib/commands/CLIENT_UNPAUSE.spec.ts +++ b/packages/client/lib/commands/CLIENT_UNPAUSE.spec.ts @@ -1,21 +1,21 @@ -import { strict as assert } from 'assert'; +import { strict as assert } from 'node:assert'; import testUtils, { GLOBAL } from '../test-utils'; -import { transformArguments } from './CLIENT_UNPAUSE'; +import CLIENT_UNPAUSE from './CLIENT_UNPAUSE'; describe('CLIENT UNPAUSE', () => { - testUtils.isVersionGreaterThanHook([6, 2]); + testUtils.isVersionGreaterThanHook([6, 2]); - it('transformArguments', () => { - assert.deepEqual( - transformArguments(), - ['CLIENT', 'UNPAUSE'] - ); - }); + it('transformArguments', () => { + assert.deepEqual( + CLIENT_UNPAUSE.transformArguments(), + ['CLIENT', 'UNPAUSE'] + ); + }); - testUtils.testWithClient('client.unpause', async client => { - assert.equal( - await client.clientUnpause(), - 'OK' - ); - }, GLOBAL.SERVERS.OPEN); + testUtils.testWithClient('client.clientUnpause', async client => { + assert.equal( + await client.clientUnpause(), + 'OK' + ); + }, GLOBAL.SERVERS.OPEN); }); diff --git a/packages/client/lib/commands/CLIENT_UNPAUSE.ts b/packages/client/lib/commands/CLIENT_UNPAUSE.ts index e139436d004..9da0a9a8bbe 100644 --- a/packages/client/lib/commands/CLIENT_UNPAUSE.ts +++ b/packages/client/lib/commands/CLIENT_UNPAUSE.ts @@ -1,7 +1,10 @@ -import { RedisCommandArguments } from '.'; +import { SimpleStringReply, Command } from '../RESP/types'; -export function transformArguments(): RedisCommandArguments { +export default { + FIRST_KEY_INDEX: undefined, + IS_READ_ONLY: true, + transformArguments() { return ['CLIENT', 'UNPAUSE']; -} - -export declare function transformReply(): 'OK' | Buffer; + }, + transformReply: undefined as unknown as () => SimpleStringReply<'OK'> +} as const satisfies Command; diff --git a/packages/client/lib/commands/CLUSTER_ADDSLOTS.spec.ts b/packages/client/lib/commands/CLUSTER_ADDSLOTS.spec.ts index c16476de436..56f7b2a85e7 100644 --- a/packages/client/lib/commands/CLUSTER_ADDSLOTS.spec.ts +++ b/packages/client/lib/commands/CLUSTER_ADDSLOTS.spec.ts @@ -1,20 +1,20 @@ -import { strict as assert } from 'assert'; -import { transformArguments } from './CLUSTER_ADDSLOTS'; +import { strict as assert } from 'node:assert'; +import CLUSTER_ADDSLOTS from './CLUSTER_ADDSLOTS'; describe('CLUSTER ADDSLOTS', () => { - describe('transformArguments', () => { - it('single', () => { - assert.deepEqual( - transformArguments(0), - ['CLUSTER', 'ADDSLOTS', '0'] - ); - }); + describe('transformArguments', () => { + it('single', () => { + assert.deepEqual( + CLUSTER_ADDSLOTS.transformArguments(0), + ['CLUSTER', 'ADDSLOTS', '0'] + ); + }); - it('multiple', () => { - assert.deepEqual( - transformArguments([0, 1]), - ['CLUSTER', 'ADDSLOTS', '0', '1'] - ); - }); + it('multiple', () => { + assert.deepEqual( + CLUSTER_ADDSLOTS.transformArguments([0, 1]), + ['CLUSTER', 'ADDSLOTS', '0', '1'] + ); }); + }); }); diff --git a/packages/client/lib/commands/CLUSTER_ADDSLOTS.ts b/packages/client/lib/commands/CLUSTER_ADDSLOTS.ts index 6cd357fb823..dc42c2f13e8 100644 --- a/packages/client/lib/commands/CLUSTER_ADDSLOTS.ts +++ b/packages/client/lib/commands/CLUSTER_ADDSLOTS.ts @@ -1,11 +1,14 @@ -import { RedisCommandArguments } from '.'; -import { pushVerdictNumberArguments } from './generic-transformers'; +import { SimpleStringReply, Command } from '../RESP/types'; +import { pushVariadicNumberArguments } from './generic-transformers'; -export function transformArguments(slots: number | Array): RedisCommandArguments { - return pushVerdictNumberArguments( - ['CLUSTER', 'ADDSLOTS'], - slots +export default { + FIRST_KEY_INDEX: undefined, + IS_READ_ONLY: true, + transformArguments(slots: number | Array) { + return pushVariadicNumberArguments( + ['CLUSTER', 'ADDSLOTS'], + slots ); -} - -export declare function transformReply(): string; + }, + transformReply: undefined as unknown as () => SimpleStringReply<'OK'> +} as const satisfies Command; diff --git a/packages/client/lib/commands/CLUSTER_ADDSLOTSRANGE.spec.ts b/packages/client/lib/commands/CLUSTER_ADDSLOTSRANGE.spec.ts index ebd1e3445ff..6af6f586e99 100644 --- a/packages/client/lib/commands/CLUSTER_ADDSLOTSRANGE.spec.ts +++ b/packages/client/lib/commands/CLUSTER_ADDSLOTSRANGE.spec.ts @@ -1,29 +1,32 @@ -import { strict as assert } from 'assert'; -import { transformArguments } from './CLUSTER_ADDSLOTSRANGE'; +import { strict as assert } from 'node:assert'; +import testUtils from '../test-utils'; +import CLUSTER_ADDSLOTSRANGE from './CLUSTER_ADDSLOTSRANGE'; describe('CLUSTER ADDSLOTSRANGE', () => { - describe('transformArguments', () => { - it('single', () => { - assert.deepEqual( - transformArguments({ - start: 0, - end: 1 - }), - ['CLUSTER', 'ADDSLOTSRANGE', '0', '1'] - ); - }); + testUtils.isVersionGreaterThanHook([7, 0]); - it('multiple', () => { - assert.deepEqual( - transformArguments([{ - start: 0, - end: 1 - }, { - start: 2, - end: 3 - }]), - ['CLUSTER', 'ADDSLOTSRANGE', '0', '1', '2', '3'] - ); - }); + describe('transformArguments', () => { + it('single', () => { + assert.deepEqual( + CLUSTER_ADDSLOTSRANGE.transformArguments({ + start: 0, + end: 1 + }), + ['CLUSTER', 'ADDSLOTSRANGE', '0', '1'] + ); }); + + it('multiple', () => { + assert.deepEqual( + CLUSTER_ADDSLOTSRANGE.transformArguments([{ + start: 0, + end: 1 + }, { + start: 2, + end: 3 + }]), + ['CLUSTER', 'ADDSLOTSRANGE', '0', '1', '2', '3'] + ); + }); + }); }); diff --git a/packages/client/lib/commands/CLUSTER_ADDSLOTSRANGE.ts b/packages/client/lib/commands/CLUSTER_ADDSLOTSRANGE.ts index 6a8d6dc668f..5cf649a30da 100644 --- a/packages/client/lib/commands/CLUSTER_ADDSLOTSRANGE.ts +++ b/packages/client/lib/commands/CLUSTER_ADDSLOTSRANGE.ts @@ -1,13 +1,14 @@ -import { RedisCommandArguments } from '.'; +import { SimpleStringReply, Command } from '../RESP/types'; import { pushSlotRangesArguments, SlotRange } from './generic-transformers'; -export function transformArguments( - ranges: SlotRange | Array -): RedisCommandArguments { +export default { + FIRST_KEY_INDEX: undefined, + IS_READ_ONLY: true, + transformArguments(ranges: SlotRange | Array) { return pushSlotRangesArguments( - ['CLUSTER', 'ADDSLOTSRANGE'], - ranges + ['CLUSTER', 'ADDSLOTSRANGE'], + ranges ); -} - -export declare function transformReply(): 'OK'; + }, + transformReply: undefined as unknown as () => SimpleStringReply<'OK'> +} as const satisfies Command; diff --git a/packages/client/lib/commands/CLUSTER_BUMPEPOCH.spec.ts b/packages/client/lib/commands/CLUSTER_BUMPEPOCH.spec.ts index edb68b3b3b0..d21bc47c5d0 100644 --- a/packages/client/lib/commands/CLUSTER_BUMPEPOCH.spec.ts +++ b/packages/client/lib/commands/CLUSTER_BUMPEPOCH.spec.ts @@ -1,20 +1,20 @@ -import { strict as assert } from 'assert'; +import { strict as assert } from 'node:assert'; import testUtils, { GLOBAL } from '../test-utils'; -import { transformArguments } from './CLUSTER_BUMPEPOCH'; +import CLUSTER_BUMPEPOCH from './CLUSTER_BUMPEPOCH'; describe('CLUSTER BUMPEPOCH', () => { - it('transformArguments', () => { - assert.deepEqual( - transformArguments(), - ['CLUSTER', 'BUMPEPOCH'] - ); - }); + it('transformArguments', () => { + assert.deepEqual( + CLUSTER_BUMPEPOCH.transformArguments(), + ['CLUSTER', 'BUMPEPOCH'] + ); + }); - testUtils.testWithCluster('clusterNode.clusterBumpEpoch', async cluster => { - const client = await cluster.nodeClient(cluster.masters[0]); - assert.equal( - typeof await client.clusterBumpEpoch(), - 'string' - ); - }, GLOBAL.SERVERS.OPEN); + testUtils.testWithCluster('clusterNode.clusterBumpEpoch', async cluster => { + const client = await cluster.nodeClient(cluster.masters[0]); + assert.equal( + typeof await client.clusterBumpEpoch(), + 'string' + ); + }, GLOBAL.SERVERS.OPEN); }); diff --git a/packages/client/lib/commands/CLUSTER_BUMPEPOCH.ts b/packages/client/lib/commands/CLUSTER_BUMPEPOCH.ts index 7f81c8fdc42..94f7e3b56f9 100644 --- a/packages/client/lib/commands/CLUSTER_BUMPEPOCH.ts +++ b/packages/client/lib/commands/CLUSTER_BUMPEPOCH.ts @@ -1,5 +1,10 @@ -export function transformArguments(): Array { - return ['CLUSTER', 'BUMPEPOCH']; -} +import { SimpleStringReply, Command } from '../RESP/types'; -export declare function transformReply(): 'BUMPED' | 'STILL'; +export default { + FIRST_KEY_INDEX: undefined, + IS_READ_ONLY: true, + transformArguments() { + return ['CLUSTER', 'BUMPEPOCH']; + }, + transformReply: undefined as unknown as () => SimpleStringReply<'BUMPED' | 'STILL'> +} as const satisfies Command; diff --git a/packages/client/lib/commands/CLUSTER_COUNT-FAILURE-REPORTS.spec.ts b/packages/client/lib/commands/CLUSTER_COUNT-FAILURE-REPORTS.spec.ts index 558110d0a28..93c2aca7804 100644 --- a/packages/client/lib/commands/CLUSTER_COUNT-FAILURE-REPORTS.spec.ts +++ b/packages/client/lib/commands/CLUSTER_COUNT-FAILURE-REPORTS.spec.ts @@ -1,22 +1,21 @@ -import { strict as assert } from 'assert'; +import { strict as assert } from 'node:assert'; import testUtils, { GLOBAL } from '../test-utils'; -import { transformArguments } from './CLUSTER_COUNT-FAILURE-REPORTS'; +import CLUSTER_COUNT_FAILURE_REPORTS from './CLUSTER_COUNT-FAILURE-REPORTS'; describe('CLUSTER COUNT-FAILURE-REPORTS', () => { - it('transformArguments', () => { - assert.deepEqual( - transformArguments('0'), - ['CLUSTER', 'COUNT-FAILURE-REPORTS', '0'] - ); - }); + it('transformArguments', () => { + assert.deepEqual( + CLUSTER_COUNT_FAILURE_REPORTS.transformArguments('0'), + ['CLUSTER', 'COUNT-FAILURE-REPORTS', '0'] + ); + }); - testUtils.testWithCluster('clusterNode.clusterCountFailureReports', async cluster => { - const client = await cluster.nodeClient(cluster.masters[0]); - assert.equal( - typeof await client.clusterCountFailureReports( - await client.clusterMyId() - ), - 'number' - ); - }, GLOBAL.CLUSTERS.OPEN); + testUtils.testWithCluster('clusterNode.clusterCountFailureReports', async cluster => { + const [master] = cluster.masters, + client = await cluster.nodeClient(master); + assert.equal( + typeof await client.clusterCountFailureReports(master.id), + 'number' + ); + }, GLOBAL.CLUSTERS.OPEN); }); diff --git a/packages/client/lib/commands/CLUSTER_COUNT-FAILURE-REPORTS.ts b/packages/client/lib/commands/CLUSTER_COUNT-FAILURE-REPORTS.ts index 3fbc33052f8..a005694713d 100644 --- a/packages/client/lib/commands/CLUSTER_COUNT-FAILURE-REPORTS.ts +++ b/packages/client/lib/commands/CLUSTER_COUNT-FAILURE-REPORTS.ts @@ -1,5 +1,10 @@ -export function transformArguments(nodeId: string): Array { - return ['CLUSTER', 'COUNT-FAILURE-REPORTS', nodeId]; -} +import { RedisArgument, NumberReply, Command } from '../RESP/types'; -export declare function transformReply(): number; +export default { + FIRST_KEY_INDEX: undefined, + IS_READ_ONLY: true, + transformArguments(nodeId: RedisArgument) { + return ['CLUSTER', 'COUNT-FAILURE-REPORTS', nodeId]; + }, + transformReply: undefined as unknown as () => NumberReply +} as const satisfies Command; diff --git a/packages/client/lib/commands/CLUSTER_COUNTKEYSINSLOT.spec.ts b/packages/client/lib/commands/CLUSTER_COUNTKEYSINSLOT.spec.ts index 27ecbcfffa3..180a120e153 100644 --- a/packages/client/lib/commands/CLUSTER_COUNTKEYSINSLOT.spec.ts +++ b/packages/client/lib/commands/CLUSTER_COUNTKEYSINSLOT.spec.ts @@ -1,20 +1,20 @@ -import { strict as assert } from 'assert'; +import { strict as assert } from 'node:assert'; import testUtils, { GLOBAL } from '../test-utils'; -import { transformArguments } from './CLUSTER_COUNTKEYSINSLOT'; +import CLUSTER_COUNTKEYSINSLOT from './CLUSTER_COUNTKEYSINSLOT'; describe('CLUSTER COUNTKEYSINSLOT', () => { - it('transformArguments', () => { - assert.deepEqual( - transformArguments(0), - ['CLUSTER', 'COUNTKEYSINSLOT', '0'] - ); - }); + it('transformArguments', () => { + assert.deepEqual( + CLUSTER_COUNTKEYSINSLOT.transformArguments(0), + ['CLUSTER', 'COUNTKEYSINSLOT', '0'] + ); + }); - testUtils.testWithCluster('clusterNode.clusterCountKeysInSlot', async cluster => { - const client = await cluster.nodeClient(cluster.masters[0]); - assert.equal( - typeof await client.clusterCountKeysInSlot(0), - 'number' - ); - }, GLOBAL.CLUSTERS.OPEN); + testUtils.testWithCluster('clusterNode.clusterCountKeysInSlot', async cluster => { + const client = await cluster.nodeClient(cluster.masters[0]); + assert.equal( + typeof await client.clusterCountKeysInSlot(0), + 'number' + ); + }, GLOBAL.CLUSTERS.OPEN); }); diff --git a/packages/client/lib/commands/CLUSTER_COUNTKEYSINSLOT.ts b/packages/client/lib/commands/CLUSTER_COUNTKEYSINSLOT.ts index a5ff75e58a9..61f46230e89 100644 --- a/packages/client/lib/commands/CLUSTER_COUNTKEYSINSLOT.ts +++ b/packages/client/lib/commands/CLUSTER_COUNTKEYSINSLOT.ts @@ -1,5 +1,10 @@ -export function transformArguments(slot: number): Array { - return ['CLUSTER', 'COUNTKEYSINSLOT', slot.toString()]; -} +import { NumberReply, Command } from '../RESP/types'; -export declare function transformReply(): number; +export default { + FIRST_KEY_INDEX: undefined, + IS_READ_ONLY: true, + transformArguments(slot: number) { + return ['CLUSTER', 'COUNTKEYSINSLOT', slot.toString()]; + }, + transformReply: undefined as unknown as () => NumberReply +} as const satisfies Command; diff --git a/packages/client/lib/commands/CLUSTER_DELSLOTS.spec.ts b/packages/client/lib/commands/CLUSTER_DELSLOTS.spec.ts index 85d13f4ed3d..59e40217b9c 100644 --- a/packages/client/lib/commands/CLUSTER_DELSLOTS.spec.ts +++ b/packages/client/lib/commands/CLUSTER_DELSLOTS.spec.ts @@ -1,20 +1,20 @@ -import { strict as assert } from 'assert'; -import { transformArguments } from './CLUSTER_DELSLOTS'; +import { strict as assert } from 'node:assert'; +import CLUSTER_DELSLOTS from './CLUSTER_DELSLOTS'; describe('CLUSTER DELSLOTS', () => { - describe('transformArguments', () => { - it('single', () => { - assert.deepEqual( - transformArguments(0), - ['CLUSTER', 'DELSLOTS', '0'] - ); - }); + describe('transformArguments', () => { + it('single', () => { + assert.deepEqual( + CLUSTER_DELSLOTS.transformArguments(0), + ['CLUSTER', 'DELSLOTS', '0'] + ); + }); - it('multiple', () => { - assert.deepEqual( - transformArguments([0, 1]), - ['CLUSTER', 'DELSLOTS', '0', '1'] - ); - }); + it('multiple', () => { + assert.deepEqual( + CLUSTER_DELSLOTS.transformArguments([0, 1]), + ['CLUSTER', 'DELSLOTS', '0', '1'] + ); }); + }); }); diff --git a/packages/client/lib/commands/CLUSTER_DELSLOTS.ts b/packages/client/lib/commands/CLUSTER_DELSLOTS.ts index bf8d9c18900..6a6bbb76085 100644 --- a/packages/client/lib/commands/CLUSTER_DELSLOTS.ts +++ b/packages/client/lib/commands/CLUSTER_DELSLOTS.ts @@ -1,11 +1,14 @@ -import { RedisCommandArguments } from '.'; -import { pushVerdictNumberArguments } from './generic-transformers'; +import { SimpleStringReply, Command } from '../RESP/types'; +import { pushVariadicNumberArguments } from './generic-transformers'; -export function transformArguments(slots: number | Array): RedisCommandArguments { - return pushVerdictNumberArguments( - ['CLUSTER', 'DELSLOTS'], - slots +export default { + FIRST_KEY_INDEX: undefined, + IS_READ_ONLY: true, + transformArguments(slots: number | Array) { + return pushVariadicNumberArguments( + ['CLUSTER', 'DELSLOTS'], + slots ); -} - -export declare function transformReply(): 'OK'; + }, + transformReply: undefined as unknown as () => SimpleStringReply<'OK'> +} as const satisfies Command; diff --git a/packages/client/lib/commands/CLUSTER_DELSLOTSRANGE.spec.ts b/packages/client/lib/commands/CLUSTER_DELSLOTSRANGE.spec.ts index 8fd50d01a54..2615f394b87 100644 --- a/packages/client/lib/commands/CLUSTER_DELSLOTSRANGE.spec.ts +++ b/packages/client/lib/commands/CLUSTER_DELSLOTSRANGE.spec.ts @@ -1,29 +1,29 @@ -import { strict as assert } from 'assert'; -import { transformArguments } from './CLUSTER_DELSLOTSRANGE'; +import { strict as assert } from 'node:assert'; +import CLUSTER_DELSLOTSRANGE from './CLUSTER_DELSLOTSRANGE'; describe('CLUSTER DELSLOTSRANGE', () => { - describe('transformArguments', () => { - it('single', () => { - assert.deepEqual( - transformArguments({ - start: 0, - end: 1 - }), - ['CLUSTER', 'DELSLOTSRANGE', '0', '1'] - ); - }); + describe('transformArguments', () => { + it('single', () => { + assert.deepEqual( + CLUSTER_DELSLOTSRANGE.transformArguments({ + start: 0, + end: 1 + }), + ['CLUSTER', 'DELSLOTSRANGE', '0', '1'] + ); + }); - it('multiple', () => { - assert.deepEqual( - transformArguments([{ - start: 0, - end: 1 - }, { - start: 2, - end: 3 - }]), - ['CLUSTER', 'DELSLOTSRANGE', '0', '1', '2', '3'] - ); - }); + it('multiple', () => { + assert.deepEqual( + CLUSTER_DELSLOTSRANGE.transformArguments([{ + start: 0, + end: 1 + }, { + start: 2, + end: 3 + }]), + ['CLUSTER', 'DELSLOTSRANGE', '0', '1', '2', '3'] + ); }); + }); }); diff --git a/packages/client/lib/commands/CLUSTER_DELSLOTSRANGE.ts b/packages/client/lib/commands/CLUSTER_DELSLOTSRANGE.ts index b136113c65f..e28ca9c8405 100644 --- a/packages/client/lib/commands/CLUSTER_DELSLOTSRANGE.ts +++ b/packages/client/lib/commands/CLUSTER_DELSLOTSRANGE.ts @@ -1,13 +1,14 @@ -import { RedisCommandArguments } from '.'; +import { SimpleStringReply, Command } from '../RESP/types'; import { pushSlotRangesArguments, SlotRange } from './generic-transformers'; -export function transformArguments( - ranges: SlotRange | Array -): RedisCommandArguments { +export default { + FIRST_KEY_INDEX: undefined, + IS_READ_ONLY: true, + transformArguments(ranges: SlotRange | Array) { return pushSlotRangesArguments( - ['CLUSTER', 'DELSLOTSRANGE'], - ranges + ['CLUSTER', 'DELSLOTSRANGE'], + ranges ); -} - -export declare function transformReply(): 'OK'; + }, + transformReply: undefined as unknown as () => SimpleStringReply<'OK'> +} as const satisfies Command; diff --git a/packages/client/lib/commands/CLUSTER_FAILOVER.spec.ts b/packages/client/lib/commands/CLUSTER_FAILOVER.spec.ts index 578ff56b9cd..ac18a9a7f8f 100644 --- a/packages/client/lib/commands/CLUSTER_FAILOVER.spec.ts +++ b/packages/client/lib/commands/CLUSTER_FAILOVER.spec.ts @@ -1,20 +1,22 @@ -import { strict as assert } from 'assert'; -import { FailoverModes, transformArguments } from './CLUSTER_FAILOVER'; +import { strict as assert } from 'node:assert'; +import CLUSTER_FAILOVER, { FAILOVER_MODES } from './CLUSTER_FAILOVER'; describe('CLUSTER FAILOVER', () => { - describe('transformArguments', () => { - it('simple', () => { - assert.deepEqual( - transformArguments(), - ['CLUSTER', 'FAILOVER'] - ); - }); - - it('with mode', () => { - assert.deepEqual( - transformArguments(FailoverModes.FORCE), - ['CLUSTER', 'FAILOVER', 'FORCE'] - ); - }); + describe('transformArguments', () => { + it('simple', () => { + assert.deepEqual( + CLUSTER_FAILOVER.transformArguments(), + ['CLUSTER', 'FAILOVER'] + ); }); + + it('with mode', () => { + assert.deepEqual( + CLUSTER_FAILOVER.transformArguments({ + mode: FAILOVER_MODES.FORCE + }), + ['CLUSTER', 'FAILOVER', 'FORCE'] + ); + }); + }); }); diff --git a/packages/client/lib/commands/CLUSTER_FAILOVER.ts b/packages/client/lib/commands/CLUSTER_FAILOVER.ts index 9bc4b69f343..63f79a246ba 100644 --- a/packages/client/lib/commands/CLUSTER_FAILOVER.ts +++ b/packages/client/lib/commands/CLUSTER_FAILOVER.ts @@ -1,16 +1,27 @@ -export enum FailoverModes { - FORCE = 'FORCE', - TAKEOVER = 'TAKEOVER' +import { SimpleStringReply, Command } from '../RESP/types'; + +export const FAILOVER_MODES = { + FORCE: 'FORCE', + TAKEOVER: 'TAKEOVER' +} as const; + +export type FailoverMode = typeof FAILOVER_MODES[keyof typeof FAILOVER_MODES]; + +export interface ClusterFailoverOptions { + mode?: FailoverMode; } -export function transformArguments(mode?: FailoverModes): Array { +export default { + FIRST_KEY_INDEX: undefined, + IS_READ_ONLY: true, + transformArguments(options?: ClusterFailoverOptions) { const args = ['CLUSTER', 'FAILOVER']; - if (mode) { - args.push(mode); + if (options?.mode) { + args.push(options.mode); } return args; -} - -export declare function transformReply(): 'OK'; + }, + transformReply: undefined as unknown as () => SimpleStringReply<'OK'> +} as const satisfies Command; diff --git a/packages/client/lib/commands/CLUSTER_FLUSHSLOTS.spec.ts b/packages/client/lib/commands/CLUSTER_FLUSHSLOTS.spec.ts index f91a9a70cfd..fbc4346136d 100644 --- a/packages/client/lib/commands/CLUSTER_FLUSHSLOTS.spec.ts +++ b/packages/client/lib/commands/CLUSTER_FLUSHSLOTS.spec.ts @@ -1,11 +1,11 @@ -import { strict as assert } from 'assert'; -import { transformArguments } from './CLUSTER_FLUSHSLOTS'; +import { strict as assert } from 'node:assert'; +import CLUSTER_FLUSHSLOTS from './CLUSTER_FLUSHSLOTS'; describe('CLUSTER FLUSHSLOTS', () => { - it('transformArguments', () => { - assert.deepEqual( - transformArguments(), - ['CLUSTER', 'FLUSHSLOTS'] - ); - }); + it('transformArguments', () => { + assert.deepEqual( + CLUSTER_FLUSHSLOTS.transformArguments(), + ['CLUSTER', 'FLUSHSLOTS'] + ); + }); }); diff --git a/packages/client/lib/commands/CLUSTER_FLUSHSLOTS.ts b/packages/client/lib/commands/CLUSTER_FLUSHSLOTS.ts index dfb1e1ccde8..327ed7b7d17 100644 --- a/packages/client/lib/commands/CLUSTER_FLUSHSLOTS.ts +++ b/packages/client/lib/commands/CLUSTER_FLUSHSLOTS.ts @@ -1,5 +1,10 @@ -export function transformArguments(): Array { - return ['CLUSTER', 'FLUSHSLOTS']; -} +import { SimpleStringReply, Command } from '../RESP/types'; -export declare function transformReply(): 'OK'; +export default { + FIRST_KEY_INDEX: undefined, + IS_READ_ONLY: true, + transformArguments() { + return ['CLUSTER', 'FLUSHSLOTS']; + }, + transformReply: undefined as unknown as () => SimpleStringReply<'OK'> +} as const satisfies Command; diff --git a/packages/client/lib/commands/CLUSTER_FORGET.spec.ts b/packages/client/lib/commands/CLUSTER_FORGET.spec.ts index cadcdb678f3..a9a923b01ee 100644 --- a/packages/client/lib/commands/CLUSTER_FORGET.spec.ts +++ b/packages/client/lib/commands/CLUSTER_FORGET.spec.ts @@ -1,11 +1,11 @@ -import { strict as assert } from 'assert'; -import { transformArguments } from './CLUSTER_FORGET'; +import { strict as assert } from 'node:assert'; +import CLUSTER_FORGET from './CLUSTER_FORGET'; describe('CLUSTER FORGET', () => { - it('transformArguments', () => { - assert.deepEqual( - transformArguments('0'), - ['CLUSTER', 'FORGET', '0'] - ); - }); + it('transformArguments', () => { + assert.deepEqual( + CLUSTER_FORGET.transformArguments('0'), + ['CLUSTER', 'FORGET', '0'] + ); + }); }); diff --git a/packages/client/lib/commands/CLUSTER_FORGET.ts b/packages/client/lib/commands/CLUSTER_FORGET.ts index fc557073aeb..a51c039563b 100644 --- a/packages/client/lib/commands/CLUSTER_FORGET.ts +++ b/packages/client/lib/commands/CLUSTER_FORGET.ts @@ -1,5 +1,10 @@ -export function transformArguments(nodeId: string): Array { - return ['CLUSTER', 'FORGET', nodeId]; -} +import { RedisArgument, SimpleStringReply, Command } from '../RESP/types'; -export declare function transformReply(): 'OK'; +export default { + FIRST_KEY_INDEX: undefined, + IS_READ_ONLY: true, + transformArguments(nodeId: RedisArgument) { + return ['CLUSTER', 'FORGET', nodeId]; + }, + transformReply: undefined as unknown as () => SimpleStringReply<'OK'> +} as const satisfies Command; diff --git a/packages/client/lib/commands/CLUSTER_GETKEYSINSLOT.spec.ts b/packages/client/lib/commands/CLUSTER_GETKEYSINSLOT.spec.ts index 957b7de20cb..f1a4e2c3bcc 100644 --- a/packages/client/lib/commands/CLUSTER_GETKEYSINSLOT.spec.ts +++ b/packages/client/lib/commands/CLUSTER_GETKEYSINSLOT.spec.ts @@ -1,21 +1,25 @@ -import { strict as assert } from 'assert'; +import { strict as assert } from 'node:assert'; import testUtils, { GLOBAL } from '../test-utils'; -import { transformArguments } from './CLUSTER_GETKEYSINSLOT'; +import CLUSTER_GETKEYSINSLOT from './CLUSTER_GETKEYSINSLOT'; describe('CLUSTER GETKEYSINSLOT', () => { - it('transformArguments', () => { - assert.deepEqual( - transformArguments(0, 10), - ['CLUSTER', 'GETKEYSINSLOT', '0', '10'] - ); - }); + it('transformArguments', () => { + assert.deepEqual( + CLUSTER_GETKEYSINSLOT.transformArguments(0, 10), + ['CLUSTER', 'GETKEYSINSLOT', '0', '10'] + ); + }); - testUtils.testWithCluster('clusterNode.clusterGetKeysInSlot', async cluster => { - const client = await cluster.nodeClient(cluster.masters[0]), - reply = await client.clusterGetKeysInSlot(0, 1); - assert.ok(Array.isArray(reply)); - for (const item of reply) { - assert.equal(typeof item, 'string'); - } - }, GLOBAL.CLUSTERS.OPEN); + testUtils.testWithCluster('clusterNode.clusterGetKeysInSlot', async cluster => { + const slot = 12539, // "key" slot + client = await cluster.nodeClient(cluster.slots[slot].master), + [, reply] = await Promise.all([ + client.set('key', 'value'), + client.clusterGetKeysInSlot(slot, 1), + ]) + assert.ok(Array.isArray(reply)); + for (const item of reply) { + assert.equal(typeof item, 'string'); + } + }, GLOBAL.CLUSTERS.OPEN); }); diff --git a/packages/client/lib/commands/CLUSTER_GETKEYSINSLOT.ts b/packages/client/lib/commands/CLUSTER_GETKEYSINSLOT.ts index ec75b7b7336..c19cd225e04 100644 --- a/packages/client/lib/commands/CLUSTER_GETKEYSINSLOT.ts +++ b/packages/client/lib/commands/CLUSTER_GETKEYSINSLOT.ts @@ -1,5 +1,10 @@ -export function transformArguments(slot: number, count: number): Array { - return ['CLUSTER', 'GETKEYSINSLOT', slot.toString(), count.toString()]; -} +import { ArrayReply, BlobStringReply, Command } from '../RESP/types'; -export declare function transformReply(): Array; +export default { + FIRST_KEY_INDEX: undefined, + IS_READ_ONLY: true, + transformArguments(slot: number, count: number) { + return ['CLUSTER', 'GETKEYSINSLOT', slot.toString(), count.toString()]; + }, + transformReply: undefined as unknown as () => ArrayReply +} as const satisfies Command; diff --git a/packages/client/lib/commands/CLUSTER_INFO.spec.ts b/packages/client/lib/commands/CLUSTER_INFO.spec.ts index 69d5c4a8c56..f7c708663fc 100644 --- a/packages/client/lib/commands/CLUSTER_INFO.spec.ts +++ b/packages/client/lib/commands/CLUSTER_INFO.spec.ts @@ -1,55 +1,20 @@ -import { strict as assert } from 'assert'; +import { strict as assert } from 'node:assert'; import testUtils, { GLOBAL } from '../test-utils'; -import { transformArguments, transformReply } from './CLUSTER_INFO'; +import CLUSTER_INFO from './CLUSTER_INFO'; describe('CLUSTER INFO', () => { - it('transformArguments', () => { - assert.deepEqual( - transformArguments(), - ['CLUSTER', 'INFO'] - ); - }); + it('transformArguments', () => { + assert.deepEqual( + CLUSTER_INFO.transformArguments(), + ['CLUSTER', 'INFO'] + ); + }); - it('transformReply', () => { - assert.deepEqual( - transformReply([ - 'cluster_state:ok', - 'cluster_slots_assigned:16384', - 'cluster_slots_ok:16384', - 'cluster_slots_pfail:0', - 'cluster_slots_fail:0', - 'cluster_known_nodes:6', - 'cluster_size:3', - 'cluster_current_epoch:6', - 'cluster_my_epoch:2', - 'cluster_stats_messages_sent:1483972', - 'cluster_stats_messages_received:1483968' - ].join('\r\n')), - { - state: 'ok', - slots: { - assigned: 16384, - ok: 16384, - pfail: 0, - fail: 0 - }, - knownNodes: 6, - size: 3, - currentEpoch: 6, - myEpoch: 2, - stats: { - messagesSent: 1483972, - messagesReceived: 1483968 - } - } - ); - }); - - testUtils.testWithCluster('clusterNode.clusterInfo', async cluster => { - const client = await cluster.nodeClient(cluster.masters[0]); - assert.notEqual( - await client.clusterInfo(), - null - ); - }, GLOBAL.CLUSTERS.OPEN); + testUtils.testWithCluster('clusterNode.clusterInfo', async cluster => { + const client = await cluster.nodeClient(cluster.masters[0]); + assert.equal( + typeof await client.clusterInfo(), + 'string' + ); + }, GLOBAL.CLUSTERS.OPEN); }); diff --git a/packages/client/lib/commands/CLUSTER_INFO.ts b/packages/client/lib/commands/CLUSTER_INFO.ts index 634515f927c..4605efbe81a 100644 --- a/packages/client/lib/commands/CLUSTER_INFO.ts +++ b/packages/client/lib/commands/CLUSTER_INFO.ts @@ -1,47 +1,10 @@ -export function transformArguments(): Array { - return ['CLUSTER', 'INFO']; -} - -interface ClusterInfoReply { - state: string; - slots: { - assigned: number; - ok: number; - pfail: number; - fail: number; - }; - knownNodes: number; - size: number; - currentEpoch: number; - myEpoch: number; - stats: { - messagesSent: number; - messagesReceived: number; - }; -} - -export function transformReply(reply: string): ClusterInfoReply { - const lines = reply.split('\r\n'); +import { VerbatimStringReply, Command } from '../RESP/types'; - return { - state: extractLineValue(lines[0]), - slots: { - assigned: Number(extractLineValue(lines[1])), - ok: Number(extractLineValue(lines[2])), - pfail: Number(extractLineValue(lines[3])), - fail: Number(extractLineValue(lines[4])) - }, - knownNodes: Number(extractLineValue(lines[5])), - size: Number(extractLineValue(lines[6])), - currentEpoch: Number(extractLineValue(lines[7])), - myEpoch: Number(extractLineValue(lines[8])), - stats: { - messagesSent: Number(extractLineValue(lines[9])), - messagesReceived: Number(extractLineValue(lines[10])) - } - }; -} - -export function extractLineValue(line: string): string { - return line.substring(line.indexOf(':') + 1); -} +export default { + FIRST_KEY_INDEX: undefined, + IS_READ_ONLY: true, + transformArguments() { + return ['CLUSTER', 'INFO']; + }, + transformReply: undefined as unknown as () => VerbatimStringReply +} as const satisfies Command; diff --git a/packages/client/lib/commands/CLUSTER_KEYSLOT.spec.ts b/packages/client/lib/commands/CLUSTER_KEYSLOT.spec.ts index 3bbc9f9cb2d..d582c616cd1 100644 --- a/packages/client/lib/commands/CLUSTER_KEYSLOT.spec.ts +++ b/packages/client/lib/commands/CLUSTER_KEYSLOT.spec.ts @@ -1,20 +1,20 @@ -import { strict as assert } from 'assert'; +import { strict as assert } from 'node:assert'; import testUtils, { GLOBAL } from '../test-utils'; -import { transformArguments } from './CLUSTER_KEYSLOT'; +import CLUSTER_KEYSLOT from './CLUSTER_KEYSLOT'; describe('CLUSTER KEYSLOT', () => { - it('transformArguments', () => { - assert.deepEqual( - transformArguments('key'), - ['CLUSTER', 'KEYSLOT', 'key'] - ); - }); + it('transformArguments', () => { + assert.deepEqual( + CLUSTER_KEYSLOT.transformArguments('key'), + ['CLUSTER', 'KEYSLOT', 'key'] + ); + }); - testUtils.testWithCluster('clusterNode.clusterKeySlot', async cluster => { - const client = await cluster.nodeClient(cluster.masters[0]); - assert.equal( - typeof await client.clusterKeySlot('key'), - 'number' - ); - }, GLOBAL.CLUSTERS.OPEN); + testUtils.testWithCluster('clusterNode.clusterKeySlot', async cluster => { + const client = await cluster.nodeClient(cluster.masters[0]); + assert.equal( + typeof await client.clusterKeySlot('key'), + 'number' + ); + }, GLOBAL.CLUSTERS.OPEN); }); diff --git a/packages/client/lib/commands/CLUSTER_KEYSLOT.ts b/packages/client/lib/commands/CLUSTER_KEYSLOT.ts index 0af524ff128..81e84430116 100644 --- a/packages/client/lib/commands/CLUSTER_KEYSLOT.ts +++ b/packages/client/lib/commands/CLUSTER_KEYSLOT.ts @@ -1,5 +1,10 @@ -export function transformArguments(key: string): Array { - return ['CLUSTER', 'KEYSLOT', key]; -} +import { Command, NumberReply, RedisArgument } from '../RESP/types'; -export declare function transformReply(): number; +export default { + FIRST_KEY_INDEX: undefined, + IS_READ_ONLY: true, + transformArguments(key: RedisArgument) { + return ['CLUSTER', 'KEYSLOT', key]; + }, + transformReply: undefined as unknown as () => NumberReply +} as const satisfies Command; diff --git a/packages/client/lib/commands/CLUSTER_LINKS.spec.ts b/packages/client/lib/commands/CLUSTER_LINKS.spec.ts index 982973e8ea5..d94231634e0 100644 --- a/packages/client/lib/commands/CLUSTER_LINKS.spec.ts +++ b/packages/client/lib/commands/CLUSTER_LINKS.spec.ts @@ -1,28 +1,28 @@ -import { strict as assert } from 'assert'; +import { strict as assert } from 'node:assert'; import testUtils, { GLOBAL } from '../test-utils'; -import { transformArguments } from './CLUSTER_LINKS'; +import CLUSTER_LINKS from './CLUSTER_LINKS'; describe('CLUSTER LINKS', () => { - testUtils.isVersionGreaterThanHook([7]); + testUtils.isVersionGreaterThanHook([7]); - it('transformArguments', () => { - assert.deepEqual( - transformArguments(), - ['CLUSTER', 'LINKS'] - ); - }); + it('transformArguments', () => { + assert.deepEqual( + CLUSTER_LINKS.transformArguments(), + ['CLUSTER', 'LINKS'] + ); + }); - testUtils.testWithCluster('clusterNode.clusterLinks', async cluster => { - const client = await cluster.nodeClient(cluster.masters[0]), - links = await client.clusterLinks(); - assert.ok(Array.isArray(links)); - for (const link of links) { - assert.equal(typeof link.direction, 'string'); - assert.equal(typeof link.node, 'string'); - assert.equal(typeof link.createTime, 'number'); - assert.equal(typeof link.events, 'string'); - assert.equal(typeof link.sendBufferAllocated, 'number'); - assert.equal(typeof link.sendBufferUsed, 'number'); - } - }, GLOBAL.CLUSTERS.OPEN); + testUtils.testWithCluster('clusterNode.clusterLinks', async cluster => { + const client = await cluster.nodeClient(cluster.masters[0]), + links = await client.clusterLinks(); + assert.ok(Array.isArray(links)); + for (const link of links) { + assert.equal(typeof link.direction, 'string'); + assert.equal(typeof link.node, 'string'); + assert.equal(typeof link['create-time'], 'number'); + assert.equal(typeof link.events, 'string'); + assert.equal(typeof link['send-buffer-allocated'], 'number'); + assert.equal(typeof link['send-buffer-used'], 'number'); + } + }, GLOBAL.CLUSTERS.OPEN); }); diff --git a/packages/client/lib/commands/CLUSTER_LINKS.ts b/packages/client/lib/commands/CLUSTER_LINKS.ts index 9a5608c102f..df83f3f7a11 100644 --- a/packages/client/lib/commands/CLUSTER_LINKS.ts +++ b/packages/client/lib/commands/CLUSTER_LINKS.ts @@ -1,38 +1,32 @@ -export function transformArguments(): Array { - return ['CLUSTER', 'LINKS']; -} - -type ClusterLinksRawReply = Array<[ - 'direction', - string, - 'node', - string, - 'createTime', - number, - 'events', - string, - 'send-buffer-allocated', - number, - 'send-buffer-used', - number -]>; +import { ArrayReply, TuplesToMapReply, BlobStringReply, NumberReply, UnwrapReply, Resp2Reply, Command } from '../RESP/types'; -type ClusterLinksReply = Array<{ - direction: string; - node: string; - createTime: number; - events: string; - sendBufferAllocated: number; - sendBufferUsed: number; -}>; +type ClusterLinksReply = ArrayReply, BlobStringReply], + [BlobStringReply<'node'>, BlobStringReply], + [BlobStringReply<'create-time'>, NumberReply], + [BlobStringReply<'events'>, BlobStringReply], + [BlobStringReply<'send-buffer-allocated'>, NumberReply], + [BlobStringReply<'send-buffer-used'>, NumberReply], +]>>; -export function transformReply(reply: ClusterLinksRawReply): ClusterLinksReply { - return reply.map(peerLink => ({ - direction: peerLink[1], - node: peerLink[3], - createTime: Number(peerLink[5]), - events: peerLink[7], - sendBufferAllocated: Number(peerLink[9]), - sendBufferUsed: Number(peerLink[11]) - })); -} +export default { + FIRST_KEY_INDEX: undefined, + IS_READ_ONLY: true, + transformArguments() { + return ['CLUSTER', 'LINKS']; + }, + transformReply: { + 2: (reply: UnwrapReply>) => reply.map(link => { + const unwrapped = link as unknown as UnwrapReply; + return { + direction: unwrapped[1], + node: unwrapped[3], + 'create-time': unwrapped[5], + events: unwrapped[7], + 'send-buffer-allocated': unwrapped[9], + 'send-buffer-used': unwrapped[11] + }; + }), + 3: undefined as unknown as () => ClusterLinksReply + } +} as const satisfies Command; diff --git a/packages/client/lib/commands/CLUSTER_MEET.spec.ts b/packages/client/lib/commands/CLUSTER_MEET.spec.ts index 50a5393efa2..0b678f009f7 100644 --- a/packages/client/lib/commands/CLUSTER_MEET.spec.ts +++ b/packages/client/lib/commands/CLUSTER_MEET.spec.ts @@ -1,11 +1,11 @@ -import { strict as assert } from 'assert'; -import { transformArguments } from './CLUSTER_MEET'; +import { strict as assert } from 'node:assert'; +import CLUSTER_MEET from './CLUSTER_MEET'; describe('CLUSTER MEET', () => { - it('transformArguments', () => { - assert.deepEqual( - transformArguments('127.0.0.1', 6379), - ['CLUSTER', 'MEET', '127.0.0.1', '6379'] - ); - }); + it('transformArguments', () => { + assert.deepEqual( + CLUSTER_MEET.transformArguments('127.0.0.1', 6379), + ['CLUSTER', 'MEET', '127.0.0.1', '6379'] + ); + }); }); diff --git a/packages/client/lib/commands/CLUSTER_MEET.ts b/packages/client/lib/commands/CLUSTER_MEET.ts index e6ce1c1fce4..df72599d40b 100644 --- a/packages/client/lib/commands/CLUSTER_MEET.ts +++ b/packages/client/lib/commands/CLUSTER_MEET.ts @@ -1,5 +1,10 @@ -export function transformArguments(ip: string, port: number): Array { - return ['CLUSTER', 'MEET', ip, port.toString()]; -} +import { SimpleStringReply, Command } from '../RESP/types'; -export declare function transformReply(): 'OK'; +export default { + FIRST_KEY_INDEX: undefined, + IS_READ_ONLY: true, + transformArguments(host: string, port: number) { + return ['CLUSTER', 'MEET', host, port.toString()]; + }, + transformReply: undefined as unknown as () => SimpleStringReply<'OK'> +} as const satisfies Command; diff --git a/packages/client/lib/commands/CLUSTER_MYID.spec.ts b/packages/client/lib/commands/CLUSTER_MYID.spec.ts index f427d7058e2..74540e98ab7 100644 --- a/packages/client/lib/commands/CLUSTER_MYID.spec.ts +++ b/packages/client/lib/commands/CLUSTER_MYID.spec.ts @@ -1,21 +1,21 @@ -import { strict as assert } from 'assert'; +import { strict as assert } from 'node:assert'; import testUtils, { GLOBAL } from '../test-utils'; -import { transformArguments } from './CLUSTER_MYID'; +import CLUSTER_MYID from './CLUSTER_MYID'; describe('CLUSTER MYID', () => { - it('transformArguments', () => { - assert.deepEqual( - transformArguments(), - ['CLUSTER', 'MYID'] - ); - }); + it('transformArguments', () => { + assert.deepEqual( + CLUSTER_MYID.transformArguments(), + ['CLUSTER', 'MYID'] + ); + }); - testUtils.testWithCluster('clusterNode.clusterMyId', async cluster => { - const [master] = cluster.masters, - client = await cluster.nodeClient(master); - assert.equal( - await client.clusterMyId(), - master.id - ); - }, GLOBAL.CLUSTERS.OPEN); + testUtils.testWithCluster('clusterNode.clusterMyId', async cluster => { + const [master] = cluster.masters, + client = await cluster.nodeClient(master); + assert.equal( + await client.clusterMyId(), + master.id + ); + }, GLOBAL.CLUSTERS.OPEN); }); diff --git a/packages/client/lib/commands/CLUSTER_MYID.ts b/packages/client/lib/commands/CLUSTER_MYID.ts index 2b61684634d..73711b47ebb 100644 --- a/packages/client/lib/commands/CLUSTER_MYID.ts +++ b/packages/client/lib/commands/CLUSTER_MYID.ts @@ -1,5 +1,10 @@ -export function transformArguments(): Array { - return ['CLUSTER', 'MYID']; -} +import { BlobStringReply, Command } from '../RESP/types'; -export declare function transformReply(): string; +export default { + FIRST_KEY_INDEX: undefined, + IS_READ_ONLY: true, + transformArguments() { + return ['CLUSTER', 'MYID']; + }, + transformReply: undefined as unknown as () => BlobStringReply +} as const satisfies Command; diff --git a/packages/client/lib/commands/CLUSTER_MYSHARDID.spec.ts b/packages/client/lib/commands/CLUSTER_MYSHARDID.spec.ts index 180289870ca..e64f2e3777a 100644 --- a/packages/client/lib/commands/CLUSTER_MYSHARDID.spec.ts +++ b/packages/client/lib/commands/CLUSTER_MYSHARDID.spec.ts @@ -1,22 +1,22 @@ import { strict as assert } from 'assert'; import testUtils, { GLOBAL } from '../test-utils'; -import { transformArguments } from './CLUSTER_MYSHARDID'; +import CLUSTER_MYSHARDID from './CLUSTER_MYSHARDID'; describe('CLUSTER MYSHARDID', () => { - testUtils.isVersionGreaterThanHook([7, 2]); + testUtils.isVersionGreaterThanHook([7, 2]); - it('transformArguments', () => { - assert.deepEqual( - transformArguments(), - ['CLUSTER', 'MYSHARDID'] - ); - }); + it('transformArguments', () => { + assert.deepEqual( + CLUSTER_MYSHARDID.transformArguments(), + ['CLUSTER', 'MYSHARDID'] + ); + }); - testUtils.testWithCluster('clusterNode.clusterMyShardId', async cluster => { - const client = await cluster.nodeClient(cluster.masters[0]); - assert.equal( - typeof await client.clusterMyShardId(), - 'string' - ); - }, GLOBAL.CLUSTERS.OPEN); + testUtils.testWithCluster('clusterNode.clusterMyShardId', async cluster => { + const client = await cluster.nodeClient(cluster.masters[0]); + assert.equal( + typeof await client.clusterMyShardId(), + 'string' + ); + }, GLOBAL.CLUSTERS.OPEN); }); diff --git a/packages/client/lib/commands/CLUSTER_MYSHARDID.ts b/packages/client/lib/commands/CLUSTER_MYSHARDID.ts index 1c4f8b82f56..0c38b61634f 100644 --- a/packages/client/lib/commands/CLUSTER_MYSHARDID.ts +++ b/packages/client/lib/commands/CLUSTER_MYSHARDID.ts @@ -1,7 +1,11 @@ -export const IS_READ_ONLY = true; +import { BlobStringReply, Command } from '../RESP/types'; -export function transformArguments() { +export default { + FIRST_KEY_INDEX: undefined, + IS_READ_ONLY: true, + transformArguments() { return ['CLUSTER', 'MYSHARDID']; -} + }, + transformReply: undefined as unknown as () => BlobStringReply +} as const satisfies Command; -export declare function transformReply(): string | Buffer; diff --git a/packages/client/lib/commands/CLUSTER_NODES.spec.ts b/packages/client/lib/commands/CLUSTER_NODES.spec.ts index 5c6cb74d6cb..99db17a23e6 100644 --- a/packages/client/lib/commands/CLUSTER_NODES.spec.ts +++ b/packages/client/lib/commands/CLUSTER_NODES.spec.ts @@ -1,145 +1,20 @@ -import { strict as assert } from 'assert'; -import { RedisClusterNodeLinkStates, transformArguments, transformReply } from './CLUSTER_NODES'; +import { strict as assert } from 'node:assert'; +import testUtils, { GLOBAL } from '../test-utils'; +import CLUSTER_NODES from './CLUSTER_NODES'; describe('CLUSTER NODES', () => { - it('transformArguments', () => { - assert.deepEqual( - transformArguments(), - ['CLUSTER', 'NODES'] - ); - }); + it('transformArguments', () => { + assert.deepEqual( + CLUSTER_NODES.transformArguments(), + ['CLUSTER', 'NODES'] + ); + }); - describe('transformReply', () => { - it('simple', () => { - assert.deepEqual( - transformReply([ - 'master 127.0.0.1:30001@31001 myself,master - 0 0 1 connected 0-16384', - 'slave 127.0.0.1:30002@31002 slave master 0 0 1 connected', - '' - ].join('\n')), - [{ - id: 'master', - address: '127.0.0.1:30001@31001', - host: '127.0.0.1', - port: 30001, - cport: 31001, - flags: ['myself', 'master'], - pingSent: 0, - pongRecv: 0, - configEpoch: 1, - linkState: RedisClusterNodeLinkStates.CONNECTED, - slots: [{ - from: 0, - to: 16384 - }], - replicas: [{ - id: 'slave', - address: '127.0.0.1:30002@31002', - host: '127.0.0.1', - port: 30002, - cport: 31002, - flags: ['slave'], - pingSent: 0, - pongRecv: 0, - configEpoch: 1, - linkState: RedisClusterNodeLinkStates.CONNECTED - }] - }] - ); - }); - - it('should support addresses without cport', () => { - assert.deepEqual( - transformReply( - 'id 127.0.0.1:30001 master - 0 0 0 connected 0-16384\n' - ), - [{ - id: 'id', - address: '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('should support ipv6 addresses', () => { - assert.deepEqual( - transformReply( - 'id 2a02:6b8:c21:330d:0:1589:ebbe:b1a0:6379@16379 master - 0 0 0 connected 0-549\n' - ), - [{ - id: 'id', - address: '2a02:6b8:c21:330d:0:1589:ebbe:b1a0:6379@16379', - host: '2a02:6b8:c21:330d:0:1589:ebbe:b1a0', - port: 6379, - cport: 16379, - flags: ['master'], - pingSent: 0, - pongRecv: 0, - configEpoch: 0, - linkState: RedisClusterNodeLinkStates.CONNECTED, - slots: [{ - from: 0, - to: 549 - }], - replicas: [] - }] - ); - }); - - it.skip('with importing slots', () => { - assert.deepEqual( - transformReply( - 'id 127.0.0.1:30001@31001 master - 0 0 0 connected 0-<-16384\n' - ), - [{ - id: 'id', - address: '127.0.0.1:30001@31001', - host: '127.0.0.1', - port: 30001, - cport: 31001, - flags: ['master'], - pingSent: 0, - pongRecv: 0, - configEpoch: 0, - linkState: RedisClusterNodeLinkStates.CONNECTED, - slots: [], // TODO - replicas: [] - }] - ); - }); - - it.skip('with migrating slots', () => { - assert.deepEqual( - transformReply( - 'id 127.0.0.1:30001@31001 master - 0 0 0 connected 0->-16384\n' - ), - [{ - id: 'id', - address: '127.0.0.1:30001@31001', - host: '127.0.0.1', - port: 30001, - cport: 31001, - flags: ['master'], - pingSent: 0, - pongRecv: 0, - configEpoch: 0, - linkState: RedisClusterNodeLinkStates.CONNECTED, - slots: [], // TODO - replicas: [] - }] - ); - }); - }); + testUtils.testWithCluster('clusterNode.clusterNodes', async cluster => { + const client = await cluster.nodeClient(cluster.masters[0]); + assert.equal( + typeof await client.clusterNodes(), + 'string' + ); + }, GLOBAL.CLUSTERS.OPEN); }); diff --git a/packages/client/lib/commands/CLUSTER_NODES.ts b/packages/client/lib/commands/CLUSTER_NODES.ts index 7c433da5f12..64dd5056232 100644 --- a/packages/client/lib/commands/CLUSTER_NODES.ts +++ b/packages/client/lib/commands/CLUSTER_NODES.ts @@ -1,105 +1,10 @@ -export function transformArguments(): Array { - return ['CLUSTER', 'NODES']; -} - -export enum RedisClusterNodeLinkStates { - CONNECTED = 'connected', - DISCONNECTED = 'disconnected' -} - -interface RedisClusterNodeAddress { - host: string; - port: number; - cport: number | null; -} - -export interface RedisClusterReplicaNode extends RedisClusterNodeAddress { - id: string; - address: string; - flags: Array; - pingSent: number; - pongRecv: number; - configEpoch: number; - linkState: RedisClusterNodeLinkStates; -} - -export interface RedisClusterMasterNode extends RedisClusterReplicaNode { - slots: Array<{ - from: number; - to: number; - }>; - replicas: Array; -} - -export function transformReply(reply: string): Array { - const lines = reply.split('\n'); - lines.pop(); // last line is empty - - const mastersMap = new Map(), - replicasMap = new Map>(); +import { VerbatimStringReply, Command } from '../RESP/types'; - for (const line of lines) { - const [id, address, flags, masterId, pingSent, pongRecv, configEpoch, linkState, ...slots] = line.split(' '), - node = { - id, - address, - ...transformNodeAddress(address), - flags: flags.split(','), - pingSent: Number(pingSent), - pongRecv: Number(pongRecv), - configEpoch: Number(configEpoch), - linkState: (linkState as RedisClusterNodeLinkStates) - }; - - if (masterId === '-') { - let replicas = replicasMap.get(id); - if (!replicas) { - replicas = []; - replicasMap.set(id, replicas); - } - - mastersMap.set(id, { - ...node, - slots: slots.map(slot => { - // TODO: importing & exporting (https://redis.io/commands/cluster-nodes#special-slot-entries) - const [fromString, toString] = slot.split('-', 2), - from = Number(fromString); - return { - from, - to: toString ? Number(toString) : from - }; - }), - replicas - }); - } else { - const replicas = replicasMap.get(masterId); - if (!replicas) { - replicasMap.set(masterId, [node]); - } else { - replicas.push(node); - } - } - } - - return [...mastersMap.values()]; -} - -function transformNodeAddress(address: string): RedisClusterNodeAddress { - const indexOfColon = address.lastIndexOf(':'), - indexOfAt = address.indexOf('@', indexOfColon), - host = address.substring(0, indexOfColon); - - if (indexOfAt === -1) { - return { - host, - port: Number(address.substring(indexOfColon + 1)), - cport: null - }; - } - - return { - host: address.substring(0, indexOfColon), - port: Number(address.substring(indexOfColon + 1, indexOfAt)), - cport: Number(address.substring(indexOfAt + 1)) - }; -} +export default { + FIRST_KEY_INDEX: undefined, + IS_READ_ONLY: true, + transformArguments() { + return ['CLUSTER', 'NODES']; + }, + transformReply: undefined as unknown as () => VerbatimStringReply +} as const satisfies Command; diff --git a/packages/client/lib/commands/CLUSTER_REPLICAS.spec.ts b/packages/client/lib/commands/CLUSTER_REPLICAS.spec.ts index 6c902dc0d82..1a48f360885 100644 --- a/packages/client/lib/commands/CLUSTER_REPLICAS.spec.ts +++ b/packages/client/lib/commands/CLUSTER_REPLICAS.spec.ts @@ -1,11 +1,21 @@ -import { strict as assert } from 'assert'; -import { transformArguments } from './CLUSTER_REPLICAS'; +import { strict as assert } from 'node:assert'; +import testUtils, { GLOBAL } from '../test-utils'; +import CLUSTER_REPLICAS from './CLUSTER_REPLICAS'; describe('CLUSTER REPLICAS', () => { - it('transformArguments', () => { - assert.deepEqual( - transformArguments('0'), - ['CLUSTER', 'REPLICAS', '0'] - ); - }); + it('transformArguments', () => { + assert.deepEqual( + CLUSTER_REPLICAS.transformArguments('0'), + ['CLUSTER', 'REPLICAS', '0'] + ); + }); + + testUtils.testWithCluster('clusterNode.clusterReplicas', async cluster => { + const client = await cluster.nodeClient(cluster.masters[0]), + reply = await client.clusterReplicas(cluster.masters[0].id); + assert.ok(Array.isArray(reply)); + for (const replica of reply) { + assert.equal(typeof replica, 'string'); + } + }, GLOBAL.CLUSTERS.OPEN); }); diff --git a/packages/client/lib/commands/CLUSTER_REPLICAS.ts b/packages/client/lib/commands/CLUSTER_REPLICAS.ts index a4130125fbf..8e0fe2cdfd9 100644 --- a/packages/client/lib/commands/CLUSTER_REPLICAS.ts +++ b/packages/client/lib/commands/CLUSTER_REPLICAS.ts @@ -1,5 +1,10 @@ -export function transformArguments(nodeId: string): Array { - return ['CLUSTER', 'REPLICAS', nodeId]; -} +import { RedisArgument, ArrayReply, BlobStringReply, Command } from '../RESP/types'; -export { transformReply } from './CLUSTER_NODES'; +export default { + FIRST_KEY_INDEX: undefined, + IS_READ_ONLY: true, + transformArguments(nodeId: RedisArgument) { + return ['CLUSTER', 'REPLICAS', nodeId]; + }, + transformReply: undefined as unknown as () => ArrayReply +} as const satisfies Command; diff --git a/packages/client/lib/commands/CLUSTER_REPLICATE.spec.ts b/packages/client/lib/commands/CLUSTER_REPLICATE.spec.ts index 926b7dd0a77..80935385a88 100644 --- a/packages/client/lib/commands/CLUSTER_REPLICATE.spec.ts +++ b/packages/client/lib/commands/CLUSTER_REPLICATE.spec.ts @@ -1,11 +1,11 @@ -import { strict as assert } from 'assert'; -import { transformArguments } from './CLUSTER_REPLICATE'; +import { strict as assert } from 'node:assert'; +import CLUSTER_REPLICATE from './CLUSTER_REPLICATE'; describe('CLUSTER REPLICATE', () => { - it('transformArguments', () => { - assert.deepEqual( - transformArguments('0'), - ['CLUSTER', 'REPLICATE', '0'] - ); - }); + it('transformArguments', () => { + assert.deepEqual( + CLUSTER_REPLICATE.transformArguments('0'), + ['CLUSTER', 'REPLICATE', '0'] + ); + }); }); diff --git a/packages/client/lib/commands/CLUSTER_REPLICATE.ts b/packages/client/lib/commands/CLUSTER_REPLICATE.ts index c74e1ec5960..7431142024c 100644 --- a/packages/client/lib/commands/CLUSTER_REPLICATE.ts +++ b/packages/client/lib/commands/CLUSTER_REPLICATE.ts @@ -1,5 +1,10 @@ -export function transformArguments(nodeId: string): Array { - return ['CLUSTER', 'REPLICATE', nodeId]; -} +import { RedisArgument, SimpleStringReply, Command } from '../RESP/types'; -export declare function transformReply(): 'OK'; +export default { + FIRST_KEY_INDEX: undefined, + IS_READ_ONLY: true, + transformArguments(nodeId: RedisArgument) { + return ['CLUSTER', 'REPLICATE', nodeId]; + }, + transformReply: undefined as unknown as () => SimpleStringReply +} as const satisfies Command; diff --git a/packages/client/lib/commands/CLUSTER_RESET.spec.ts b/packages/client/lib/commands/CLUSTER_RESET.spec.ts index 340da7457c1..190bdaf69e1 100644 --- a/packages/client/lib/commands/CLUSTER_RESET.spec.ts +++ b/packages/client/lib/commands/CLUSTER_RESET.spec.ts @@ -1,20 +1,22 @@ -import { strict as assert } from 'assert'; -import { transformArguments } from './CLUSTER_RESET'; +import { strict as assert } from 'node:assert'; +import CLUSTER_RESET from './CLUSTER_RESET'; describe('CLUSTER RESET', () => { - describe('transformArguments', () => { - it('simple', () => { - assert.deepEqual( - transformArguments(), - ['CLUSTER', 'RESET'] - ); - }); + describe('transformArguments', () => { + it('simple', () => { + assert.deepEqual( + CLUSTER_RESET.transformArguments(), + ['CLUSTER', 'RESET'] + ); + }); - it('with mode', () => { - assert.deepEqual( - transformArguments('HARD'), - ['CLUSTER', 'RESET', 'HARD'] - ); - }); + it('with mode', () => { + assert.deepEqual( + CLUSTER_RESET.transformArguments({ + mode: 'HARD' + }), + ['CLUSTER', 'RESET', 'HARD'] + ); }); + }); }); diff --git a/packages/client/lib/commands/CLUSTER_RESET.ts b/packages/client/lib/commands/CLUSTER_RESET.ts index c6901e045dc..7aaac9d3b0d 100644 --- a/packages/client/lib/commands/CLUSTER_RESET.ts +++ b/packages/client/lib/commands/CLUSTER_RESET.ts @@ -1,11 +1,20 @@ -export function transformArguments(mode?: 'HARD' | 'SOFT'): Array { +import { SimpleStringReply, Command } from '../RESP/types'; + +export interface ClusterResetOptions { + mode?: 'HARD' | 'SOFT'; +} + +export default { + FIRST_KEY_INDEX: undefined, + IS_READ_ONLY: true, + transformArguments(options?: ClusterResetOptions) { const args = ['CLUSTER', 'RESET']; - if (mode) { - args.push(mode); + if (options?.mode) { + args.push(options.mode); } return args; -} - -export declare function transformReply(): string; + }, + transformReply: undefined as unknown as () => SimpleStringReply<'OK'> +} as const satisfies Command; diff --git a/packages/client/lib/commands/CLUSTER_SAVECONFIG.spec.ts b/packages/client/lib/commands/CLUSTER_SAVECONFIG.spec.ts index 81ba4aa2509..ece8087e8e4 100644 --- a/packages/client/lib/commands/CLUSTER_SAVECONFIG.spec.ts +++ b/packages/client/lib/commands/CLUSTER_SAVECONFIG.spec.ts @@ -1,20 +1,20 @@ -import { strict as assert } from 'assert'; +import { strict as assert } from 'node:assert'; import testUtils, { GLOBAL } from '../test-utils'; -import { transformArguments } from './CLUSTER_SAVECONFIG'; +import CLUSTER_SAVECONFIG from './CLUSTER_SAVECONFIG'; describe('CLUSTER SAVECONFIG', () => { - it('transformArguments', () => { - assert.deepEqual( - transformArguments(), - ['CLUSTER', 'SAVECONFIG'] - ); - }); + it('transformArguments', () => { + assert.deepEqual( + CLUSTER_SAVECONFIG.transformArguments(), + ['CLUSTER', 'SAVECONFIG'] + ); + }); - testUtils.testWithCluster('clusterNode.clusterSaveConfig', async cluster => { - const client = await cluster.nodeClient(cluster.masters[0]); - assert.equal( - await client.clusterSaveConfig(), - 'OK' - ); - }, GLOBAL.CLUSTERS.OPEN); + testUtils.testWithCluster('clusterNode.clusterSaveConfig', async cluster => { + const client = await cluster.nodeClient(cluster.masters[0]); + assert.equal( + await client.clusterSaveConfig(), + 'OK' + ); + }, GLOBAL.CLUSTERS.OPEN); }); diff --git a/packages/client/lib/commands/CLUSTER_SAVECONFIG.ts b/packages/client/lib/commands/CLUSTER_SAVECONFIG.ts index 7e7fb181cc6..489ffd27e48 100644 --- a/packages/client/lib/commands/CLUSTER_SAVECONFIG.ts +++ b/packages/client/lib/commands/CLUSTER_SAVECONFIG.ts @@ -1,5 +1,11 @@ -export function transformArguments(): Array { - return ['CLUSTER', 'SAVECONFIG']; -} +import { SimpleStringReply, Command } from '../RESP/types'; + +export default { + FIRST_KEY_INDEX: undefined, + IS_READ_ONLY: true, + transformArguments() { + return ['CLUSTER', 'SAVECONFIG']; + }, + transformReply: undefined as unknown as () => SimpleStringReply<'OK'> +} as const satisfies Command; -export declare function transformReply(): 'OK'; diff --git a/packages/client/lib/commands/CLUSTER_SET-CONFIG-EPOCH.spec.ts b/packages/client/lib/commands/CLUSTER_SET-CONFIG-EPOCH.spec.ts index dd241574168..39cf026d0ef 100644 --- a/packages/client/lib/commands/CLUSTER_SET-CONFIG-EPOCH.spec.ts +++ b/packages/client/lib/commands/CLUSTER_SET-CONFIG-EPOCH.spec.ts @@ -1,11 +1,11 @@ -import { strict as assert } from 'assert'; -import { transformArguments } from './CLUSTER_SET-CONFIG-EPOCH'; +import { strict as assert } from 'node:assert'; +import CLUSTER_SET_CONFIG_EPOCH from './CLUSTER_SET-CONFIG-EPOCH'; describe('CLUSTER SET-CONFIG-EPOCH', () => { - it('transformArguments', () => { - assert.deepEqual( - transformArguments(0), - ['CLUSTER', 'SET-CONFIG-EPOCH', '0'] - ); - }); + it('transformArguments', () => { + assert.deepEqual( + CLUSTER_SET_CONFIG_EPOCH.transformArguments(0), + ['CLUSTER', 'SET-CONFIG-EPOCH', '0'] + ); + }); }); diff --git a/packages/client/lib/commands/CLUSTER_SET-CONFIG-EPOCH.ts b/packages/client/lib/commands/CLUSTER_SET-CONFIG-EPOCH.ts index c50a6b9d3a5..2a650840c44 100644 --- a/packages/client/lib/commands/CLUSTER_SET-CONFIG-EPOCH.ts +++ b/packages/client/lib/commands/CLUSTER_SET-CONFIG-EPOCH.ts @@ -1,5 +1,10 @@ -export function transformArguments(configEpoch: number): Array { - return ['CLUSTER', 'SET-CONFIG-EPOCH', configEpoch.toString()]; -} +import { SimpleStringReply, Command } from '../RESP/types'; -export declare function transformReply(): 'OK'; +export default { + FIRST_KEY_INDEX: undefined, + IS_READ_ONLY: true, + transformArguments(configEpoch: number) { + return ['CLUSTER', 'SET-CONFIG-EPOCH', configEpoch.toString() ]; + }, + transformReply: undefined as unknown as () => SimpleStringReply<'OK'> +} as const satisfies Command; diff --git a/packages/client/lib/commands/CLUSTER_SETSLOT.spec.ts b/packages/client/lib/commands/CLUSTER_SETSLOT.spec.ts index 0f46aafd13e..7bce6d74b4a 100644 --- a/packages/client/lib/commands/CLUSTER_SETSLOT.spec.ts +++ b/packages/client/lib/commands/CLUSTER_SETSLOT.spec.ts @@ -1,20 +1,20 @@ -import { strict as assert } from 'assert'; -import { ClusterSlotStates, transformArguments } from './CLUSTER_SETSLOT'; +import { strict as assert } from 'node:assert'; +import CLUSTER_SETSLOT, { CLUSTER_SLOT_STATES } from './CLUSTER_SETSLOT'; describe('CLUSTER SETSLOT', () => { - describe('transformArguments', () => { - it('simple', () => { - assert.deepEqual( - transformArguments(0, ClusterSlotStates.IMPORTING), - ['CLUSTER', 'SETSLOT', '0', 'IMPORTING'] - ); - }); + describe('transformArguments', () => { + it('simple', () => { + assert.deepEqual( + CLUSTER_SETSLOT.transformArguments(0, CLUSTER_SLOT_STATES.IMPORTING), + ['CLUSTER', 'SETSLOT', '0', 'IMPORTING'] + ); + }); - it('with nodeId', () => { - assert.deepEqual( - transformArguments(0, ClusterSlotStates.IMPORTING, 'nodeId'), - ['CLUSTER', 'SETSLOT', '0', 'IMPORTING', 'nodeId'] - ); - }); + it('with nodeId', () => { + assert.deepEqual( + CLUSTER_SETSLOT.transformArguments(0, CLUSTER_SLOT_STATES.IMPORTING, 'nodeId'), + ['CLUSTER', 'SETSLOT', '0', 'IMPORTING', 'nodeId'] + ); }); + }); }); diff --git a/packages/client/lib/commands/CLUSTER_SETSLOT.ts b/packages/client/lib/commands/CLUSTER_SETSLOT.ts index c01505c71a3..ad04513688e 100644 --- a/packages/client/lib/commands/CLUSTER_SETSLOT.ts +++ b/packages/client/lib/commands/CLUSTER_SETSLOT.ts @@ -1,22 +1,25 @@ -export enum ClusterSlotStates { - IMPORTING = 'IMPORTING', - MIGRATING = 'MIGRATING', - STABLE = 'STABLE', - NODE = 'NODE' -} +import { RedisArgument, SimpleStringReply, Command } from '../RESP/types'; -export function transformArguments( - slot: number, - state: ClusterSlotStates, - nodeId?: string -): Array { - const args = ['CLUSTER', 'SETSLOT', slot.toString(), state]; +export const CLUSTER_SLOT_STATES = { + IMPORTING: 'IMPORTING', + MIGRATING: 'MIGRATING', + STABLE: 'STABLE', + NODE: 'NODE' +} as const; + +export type ClusterSlotState = typeof CLUSTER_SLOT_STATES[keyof typeof CLUSTER_SLOT_STATES]; + +export default { + FIRST_KEY_INDEX: undefined, + IS_READ_ONLY: true, + transformArguments(slot: number, state: ClusterSlotState, nodeId?: RedisArgument) { + const args: Array = ['CLUSTER', 'SETSLOT', slot.toString(), state]; if (nodeId) { - args.push(nodeId); + args.push(nodeId); } return args; -} - -export declare function transformReply(): 'OK'; + }, + transformReply: undefined as unknown as () => SimpleStringReply<'OK'> +} as const satisfies Command; diff --git a/packages/client/lib/commands/CLUSTER_SLOTS.spec.ts b/packages/client/lib/commands/CLUSTER_SLOTS.spec.ts index 6efbfe13ce1..198dfdc6c1b 100644 --- a/packages/client/lib/commands/CLUSTER_SLOTS.spec.ts +++ b/packages/client/lib/commands/CLUSTER_SLOTS.spec.ts @@ -1,76 +1,30 @@ -import { strict as assert } from 'assert'; -import { transformArguments, transformReply } from './CLUSTER_SLOTS'; +import { strict as assert } from 'node:assert'; +import testUtils, { GLOBAL } from '../test-utils'; +import CLUSTER_SLOTS from './CLUSTER_SLOTS'; describe('CLUSTER SLOTS', () => { - it('transformArguments', () => { - assert.deepEqual( - transformArguments(), - ['CLUSTER', 'SLOTS'] - ); - }); + it('transformArguments', () => { + assert.deepEqual( + CLUSTER_SLOTS.transformArguments(), + ['CLUSTER', 'SLOTS'] + ); + }); - it('transformReply', () => { - assert.deepEqual( - transformReply([ - [ - 0, - 5460, - ['127.0.0.1', 30001, '09dbe9720cda62f7865eabc5fd8857c5d2678366'], - ['127.0.0.1', 30004, '821d8ca00d7ccf931ed3ffc7e3db0599d2271abf'] - ], - [ - 5461, - 10922, - ['127.0.0.1', 30002, 'c9d93d9f2c0c524ff34cc11838c2003d8c29e013'], - ['127.0.0.1', 30005, 'faadb3eb99009de4ab72ad6b6ed87634c7ee410f'] - ], - [ - 10923, - 16383, - ['127.0.0.1', 30003, '044ec91f325b7595e76dbcb18cc688b6a5b434a1'], - ['127.0.0.1', 30006, '58e6e48d41228013e5d9c1c37c5060693925e97e'] - ] - ]), - [{ - from: 0, - to: 5460, - master: { - ip: '127.0.0.1', - port: 30001, - id: '09dbe9720cda62f7865eabc5fd8857c5d2678366' - }, - replicas: [{ - ip: '127.0.0.1', - port: 30004, - id: '821d8ca00d7ccf931ed3ffc7e3db0599d2271abf' - }] - }, { - from: 5461, - to: 10922, - master: { - ip: '127.0.0.1', - port: 30002, - id: 'c9d93d9f2c0c524ff34cc11838c2003d8c29e013' - }, - replicas: [{ - ip: '127.0.0.1', - port: 30005, - id: 'faadb3eb99009de4ab72ad6b6ed87634c7ee410f' - }] - }, { - from: 10923, - to: 16383, - master: { - ip: '127.0.0.1', - port: 30003, - id: '044ec91f325b7595e76dbcb18cc688b6a5b434a1' - }, - replicas: [{ - ip: '127.0.0.1', - port: 30006, - id: '58e6e48d41228013e5d9c1c37c5060693925e97e' - }] - }] - ); - }); + testUtils.testWithCluster('clusterNode.clusterSlots', async cluster => { + const client = await cluster.nodeClient(cluster.masters[0]), + slots = await client.clusterSlots(); + assert.ok(Array.isArray(slots)); + for (const { from, to, master, replicas } of slots) { + assert.equal(typeof from, 'number'); + assert.equal(typeof to, 'number'); + assert.equal(typeof master.host, 'string'); + assert.equal(typeof master.port, 'number'); + assert.equal(typeof master.id, 'string'); + for (const replica of replicas) { + assert.equal(typeof replica.host, 'string'); + assert.equal(typeof replica.port, 'number'); + assert.equal(typeof replica.id, 'string'); + } + } + }, GLOBAL.CLUSTERS.WITH_REPLICAS); }); diff --git a/packages/client/lib/commands/CLUSTER_SLOTS.ts b/packages/client/lib/commands/CLUSTER_SLOTS.ts index 20d9782dd9e..1b523328bbb 100644 --- a/packages/client/lib/commands/CLUSTER_SLOTS.ts +++ b/packages/client/lib/commands/CLUSTER_SLOTS.ts @@ -1,46 +1,41 @@ -import { RedisCommandArguments } from '.'; +import { TuplesReply, BlobStringReply, NumberReply, ArrayReply, UnwrapReply, Command } from '../RESP/types'; -export function transformArguments(): RedisCommandArguments { - return ['CLUSTER', 'SLOTS']; -} - -type ClusterSlotsRawNode = [ip: string, port: number, id: string]; - -type ClusterSlotsRawReply = Array<[ - from: number, - to: number, - master: ClusterSlotsRawNode, - ...replicas: Array +type RawNode = TuplesReply<[ + host: BlobStringReply, + port: NumberReply, + id: BlobStringReply ]>; -export interface ClusterSlotsNode { - ip: string; - port: number; - id: string; -}; +type ClusterSlotsRawReply = ArrayReply<[ + from: NumberReply, + to: NumberReply, + master: RawNode, + ...replicas: Array +]>; -export type ClusterSlotsReply = Array<{ - from: number; - to: number; - master: ClusterSlotsNode; - replicas: Array; -}>; +export type ClusterSlotsNode = ReturnType; -export function transformReply(reply: ClusterSlotsRawReply): ClusterSlotsReply { - return reply.map(([from, to, master, ...replicas]) => { - return { - from, - to, - master: transformNode(master), - replicas: replicas.map(transformNode) - }; - }); -} +export default { + FIRST_KEY_INDEX: undefined, + IS_READ_ONLY: true, + transformArguments() { + return ['CLUSTER', 'SLOTS']; + }, + transformReply(reply: UnwrapReply) { + return reply.map(([from, to, master, ...replicas]) => ({ + from, + to, + master: transformNode(master), + replicas: replicas.map(transformNode) + })); + } +} as const satisfies Command; -function transformNode([ip, port, id]: ClusterSlotsRawNode): ClusterSlotsNode { - return { - ip, - port, - id - }; +function transformNode(node: RawNode) { + const [host, port, id] = node as unknown as UnwrapReply; + return { + host, + port, + id + }; } diff --git a/packages/client/lib/commands/COMMAND.spec.ts b/packages/client/lib/commands/COMMAND.spec.ts index baad79845ab..860ffc30685 100644 --- a/packages/client/lib/commands/COMMAND.spec.ts +++ b/packages/client/lib/commands/COMMAND.spec.ts @@ -1,17 +1,17 @@ -import { strict as assert } from 'assert'; -import testUtils, { GLOBAL } from '../test-utils'; -import { transformArguments } from './COMMAND'; -import { assertPingCommand } from './COMMAND_INFO.spec'; +// import { strict as assert } from 'node:assert'; +// import testUtils, { GLOBAL } from '../test-utils'; +// import { transformArguments } from './COMMAND'; +// import { assertPingCommand } from './COMMAND_INFO.spec'; -describe('COMMAND', () => { - it('transformArguments', () => { - assert.deepEqual( - transformArguments(), - ['COMMAND'] - ); - }); +// describe('COMMAND', () => { +// it('transformArguments', () => { +// assert.deepEqual( +// transformArguments(), +// ['COMMAND'] +// ); +// }); - testUtils.testWithClient('client.command', async client => { - assertPingCommand((await client.command()).find(command => command.name === 'ping')); - }, GLOBAL.SERVERS.OPEN); -}); +// testUtils.testWithClient('client.command', async client => { +// assertPingCommand((await client.command()).find(command => command.name === 'ping')); +// }, GLOBAL.SERVERS.OPEN); +// }); diff --git a/packages/client/lib/commands/COMMAND.ts b/packages/client/lib/commands/COMMAND.ts index b6ee50b2f4c..d9a960107a2 100644 --- a/packages/client/lib/commands/COMMAND.ts +++ b/packages/client/lib/commands/COMMAND.ts @@ -1,12 +1,13 @@ -import { RedisCommandArguments } from '.'; +import { ArrayReply, Command, UnwrapReply } from '../RESP/types'; import { CommandRawReply, CommandReply, transformCommandReply } from './generic-transformers'; -export const IS_READ_ONLY = true; - -export function transformArguments(): RedisCommandArguments { +export default { + IS_READ_ONLY: true, + transformArguments() { return ['COMMAND']; -} - -export function transformReply(reply: Array): Array { + }, + // TODO: This works, as we don't currently handle any of the items returned as a map + transformReply(reply: UnwrapReply>): Array { return reply.map(transformCommandReply); -} + } +} as const satisfies Command; \ No newline at end of file diff --git a/packages/client/lib/commands/COMMAND_COUNT.spec.ts b/packages/client/lib/commands/COMMAND_COUNT.spec.ts index 71482382f67..05bd29f223c 100644 --- a/packages/client/lib/commands/COMMAND_COUNT.spec.ts +++ b/packages/client/lib/commands/COMMAND_COUNT.spec.ts @@ -1,19 +1,19 @@ -import { strict as assert } from 'assert'; +import { strict as assert } from 'node:assert'; import testUtils, { GLOBAL } from '../test-utils'; -import { transformArguments } from './COMMAND_COUNT'; +import COMMAND_COUNT from './COMMAND_COUNT'; describe('COMMAND COUNT', () => { - it('transformArguments', () => { - assert.deepEqual( - transformArguments(), - ['COMMAND', 'COUNT'] - ); - }); + it('transformArguments', () => { + assert.deepEqual( + COMMAND_COUNT.transformArguments(), + ['COMMAND', 'COUNT'] + ); + }); - testUtils.testWithClient('client.commandCount', async client => { - assert.equal( - typeof await client.commandCount(), - 'number' - ); - }, GLOBAL.SERVERS.OPEN); + testUtils.testWithClient('client.commandCount', async client => { + assert.equal( + typeof await client.commandCount(), + 'number' + ); + }, GLOBAL.SERVERS.OPEN); }); diff --git a/packages/client/lib/commands/COMMAND_COUNT.ts b/packages/client/lib/commands/COMMAND_COUNT.ts index 34c6a088da6..10b0fdefe09 100644 --- a/packages/client/lib/commands/COMMAND_COUNT.ts +++ b/packages/client/lib/commands/COMMAND_COUNT.ts @@ -1,9 +1,10 @@ -import { RedisCommandArguments } from '.'; +import { NumberReply, Command } from '../RESP/types'; -export const IS_READ_ONLY = true; - -export function transformArguments(): RedisCommandArguments { +export default { + FIRST_KEY_INDEX: undefined, + IS_READ_ONLY: true, + transformArguments() { return ['COMMAND', 'COUNT']; -} - -export declare function transformReply(): number; + }, + transformReply: undefined as unknown as () => NumberReply +} as const satisfies Command; diff --git a/packages/client/lib/commands/COMMAND_GETKEYS.spec.ts b/packages/client/lib/commands/COMMAND_GETKEYS.spec.ts index a92d032c5d6..d5b9f60790d 100644 --- a/packages/client/lib/commands/COMMAND_GETKEYS.spec.ts +++ b/packages/client/lib/commands/COMMAND_GETKEYS.spec.ts @@ -1,19 +1,19 @@ -import { strict as assert } from 'assert'; +import { strict as assert } from 'node:assert'; import testUtils, { GLOBAL } from '../test-utils'; -import { transformArguments } from './COMMAND_GETKEYS'; +import COMMAND_GETKEYS from './COMMAND_GETKEYS'; describe('COMMAND GETKEYS', () => { - it('transformArguments', () => { - assert.deepEqual( - transformArguments(['GET', 'key']), - ['COMMAND', 'GETKEYS', 'GET', 'key'] - ); - }); + it('transformArguments', () => { + assert.deepEqual( + COMMAND_GETKEYS.transformArguments(['GET', 'key']), + ['COMMAND', 'GETKEYS', 'GET', 'key'] + ); + }); - testUtils.testWithClient('client.commandGetKeys', async client => { - assert.deepEqual( - await client.commandGetKeys(['GET', 'key']), - ['key'] - ); - }, GLOBAL.SERVERS.OPEN); + testUtils.testWithClient('client.commandGetKeys', async client => { + assert.deepEqual( + await client.commandGetKeys(['GET', 'key']), + ['key'] + ); + }, GLOBAL.SERVERS.OPEN); }); diff --git a/packages/client/lib/commands/COMMAND_GETKEYS.ts b/packages/client/lib/commands/COMMAND_GETKEYS.ts index 6762fe4b58a..55cca415b8d 100644 --- a/packages/client/lib/commands/COMMAND_GETKEYS.ts +++ b/packages/client/lib/commands/COMMAND_GETKEYS.ts @@ -1,9 +1,10 @@ -import { RedisCommandArgument, RedisCommandArguments } from '.'; +import { RedisArgument, ArrayReply, BlobStringReply, Command } from '../RESP/types'; -export const IS_READ_ONLY = true; - -export function transformArguments(args: Array): RedisCommandArguments { +export default { + FIRST_KEY_INDEX: undefined, + IS_READ_ONLY: true, + transformArguments(args: Array) { return ['COMMAND', 'GETKEYS', ...args]; -} - -export declare function transformReply(): Array; + }, + transformReply: undefined as unknown as () => ArrayReply +} as const satisfies Command; diff --git a/packages/client/lib/commands/COMMAND_GETKEYSANDFLAGS.spec.ts b/packages/client/lib/commands/COMMAND_GETKEYSANDFLAGS.spec.ts index d568ed0e508..49652762d65 100644 --- a/packages/client/lib/commands/COMMAND_GETKEYSANDFLAGS.spec.ts +++ b/packages/client/lib/commands/COMMAND_GETKEYSANDFLAGS.spec.ts @@ -1,24 +1,24 @@ -import { strict as assert } from 'assert'; -import testUtils, { GLOBAL } from '../test-utils'; -import { transformArguments } from './COMMAND_GETKEYSANDFLAGS'; +// import { strict as assert } from 'node:assert'; +// import testUtils, { GLOBAL } from '../test-utils'; +// import { transformArguments } from './COMMAND_GETKEYSANDFLAGS'; -describe('COMMAND GETKEYSANDFLAGS', () => { - testUtils.isVersionGreaterThanHook([7]); +// describe('COMMAND GETKEYSANDFLAGS', () => { +// testUtils.isVersionGreaterThanHook([7]); - it('transformArguments', () => { - assert.deepEqual( - transformArguments(['GET', 'key']), - ['COMMAND', 'GETKEYSANDFLAGS', 'GET', 'key'] - ); - }); +// it('transformArguments', () => { +// assert.deepEqual( +// transformArguments(['GET', 'key']), +// ['COMMAND', 'GETKEYSANDFLAGS', 'GET', 'key'] +// ); +// }); - testUtils.testWithClient('client.commandGetKeysAndFlags', async client => { - assert.deepEqual( - await client.commandGetKeysAndFlags(['GET', 'key']), - [{ - key: 'key', - flags: ['RO', 'access'] - }] - ); - }, GLOBAL.SERVERS.OPEN); -}); +// testUtils.testWithClient('client.commandGetKeysAndFlags', async client => { +// assert.deepEqual( +// await client.commandGetKeysAndFlags(['GET', 'key']), +// [{ +// key: 'key', +// flags: ['RO', 'access'] +// }] +// ); +// }, GLOBAL.SERVERS.OPEN); +// }); diff --git a/packages/client/lib/commands/COMMAND_GETKEYSANDFLAGS.ts b/packages/client/lib/commands/COMMAND_GETKEYSANDFLAGS.ts index 96b28186ccd..a032190c16e 100644 --- a/packages/client/lib/commands/COMMAND_GETKEYSANDFLAGS.ts +++ b/packages/client/lib/commands/COMMAND_GETKEYSANDFLAGS.ts @@ -1,24 +1,23 @@ -import { RedisCommandArgument, RedisCommandArguments } from '.'; +import { RedisArgument, ArrayReply, TuplesReply, BlobStringReply, SetReply, UnwrapReply, Command } from '../RESP/types'; -export const IS_READ_ONLY = true; +export type CommandGetKeysAndFlagsRawReply = ArrayReply +]>>; -export function transformArguments(args: Array): RedisCommandArguments { +export default { + FIRST_KEY_INDEX: undefined, + IS_READ_ONLY: true, + transformArguments(args: Array) { return ['COMMAND', 'GETKEYSANDFLAGS', ...args]; -} - -type KeysAndFlagsRawReply = Array<[ - RedisCommandArgument, - RedisCommandArguments -]>; - -type KeysAndFlagsReply = Array<{ - key: RedisCommandArgument; - flags: RedisCommandArguments; -}>; - -export function transformReply(reply: KeysAndFlagsRawReply): KeysAndFlagsReply { - return reply.map(([key, flags]) => ({ + }, + transformReply(reply: UnwrapReply) { + return reply.map(entry => { + const [key, flags] = entry as unknown as UnwrapReply; + return { key, flags - })); -} + }; + }); + } +} as const satisfies Command; diff --git a/packages/client/lib/commands/COMMAND_INFO.spec.ts b/packages/client/lib/commands/COMMAND_INFO.spec.ts index c54a5d0aeb3..fd8c22ae803 100644 --- a/packages/client/lib/commands/COMMAND_INFO.spec.ts +++ b/packages/client/lib/commands/COMMAND_INFO.spec.ts @@ -1,49 +1,49 @@ -import { strict as assert } from 'assert'; -import testUtils, { GLOBAL } from '../test-utils'; -import { transformArguments } from './COMMAND_INFO'; -import { CommandCategories, CommandFlags, CommandReply } from './generic-transformers'; +// import { strict as assert } from 'node:assert'; +// import testUtils, { GLOBAL } from '../test-utils'; +// import { transformArguments } from './COMMAND_INFO'; +// import { CommandCategories, CommandFlags, CommandReply } from './generic-transformers'; -export function assertPingCommand(commandInfo: CommandReply | null | undefined): void { - assert.deepEqual( - commandInfo, - { - name: 'ping', - arity: -1, - flags: new Set( - testUtils.isVersionGreaterThan([7]) ? - [CommandFlags.FAST] : - [CommandFlags.STALE, CommandFlags.FAST] - ), - firstKeyIndex: 0, - lastKeyIndex: 0, - step: 0, - categories: new Set( - testUtils.isVersionGreaterThan([6]) ? - [CommandCategories.FAST, CommandCategories.CONNECTION] : - [] - ) - } - ); -} +// export function assertPingCommand(commandInfo: CommandReply | null | undefined): void { +// assert.deepEqual( +// commandInfo, +// { +// name: 'ping', +// arity: -1, +// flags: new Set( +// testUtils.isVersionGreaterThan([7]) ? +// [CommandFlags.FAST] : +// [CommandFlags.STALE, CommandFlags.FAST] +// ), +// firstKeyIndex: 0, +// lastKeyIndex: 0, +// step: 0, +// categories: new Set( +// testUtils.isVersionGreaterThan([6]) ? +// [CommandCategories.FAST, CommandCategories.CONNECTION] : +// [] +// ) +// } +// ); +// } -describe('COMMAND INFO', () => { - it('transformArguments', () => { - assert.deepEqual( - transformArguments(['PING']), - ['COMMAND', 'INFO', 'PING'] - ); - }); +// describe('COMMAND INFO', () => { +// it('transformArguments', () => { +// assert.deepEqual( +// transformArguments(['PING']), +// ['COMMAND', 'INFO', 'PING'] +// ); +// }); - describe('client.commandInfo', () => { - testUtils.testWithClient('PING', async client => { - assertPingCommand((await client.commandInfo(['PING']))[0]); - }, GLOBAL.SERVERS.OPEN); +// describe('client.commandInfo', () => { +// testUtils.testWithClient('PING', async client => { +// assertPingCommand((await client.commandInfo(['PING']))[0]); +// }, GLOBAL.SERVERS.OPEN); - testUtils.testWithClient('DOSE_NOT_EXISTS', async client => { - assert.deepEqual( - await client.commandInfo(['DOSE_NOT_EXISTS']), - [null] - ); - }, GLOBAL.SERVERS.OPEN); - }); -}); +// testUtils.testWithClient('DOSE_NOT_EXISTS', async client => { +// assert.deepEqual( +// await client.commandInfo(['DOSE_NOT_EXISTS']), +// [null] +// ); +// }, GLOBAL.SERVERS.OPEN); +// }); +// }); diff --git a/packages/client/lib/commands/COMMAND_INFO.ts b/packages/client/lib/commands/COMMAND_INFO.ts index 6f84d0edaf9..5dbd00e8056 100644 --- a/packages/client/lib/commands/COMMAND_INFO.ts +++ b/packages/client/lib/commands/COMMAND_INFO.ts @@ -1,12 +1,14 @@ -import { RedisCommandArguments } from '.'; +import { ArrayReply, Command, UnwrapReply } from '../RESP/types'; import { CommandRawReply, CommandReply, transformCommandReply } from './generic-transformers'; -export const IS_READ_ONLY = true; - -export function transformArguments(commands: Array): RedisCommandArguments { +export default { + FIRST_KEY_INDEX: undefined, + IS_READ_ONLY: true, + transformArguments(commands: Array) { return ['COMMAND', 'INFO', ...commands]; -} - -export function transformReply(reply: Array): Array { + }, + // TODO: This works, as we don't currently handle any of the items returned as a map + transformReply(reply: UnwrapReply>): Array { return reply.map(command => command ? transformCommandReply(command) : null); -} + } +} as const satisfies Command; \ No newline at end of file diff --git a/packages/client/lib/commands/COMMAND_LIST.spec.ts b/packages/client/lib/commands/COMMAND_LIST.spec.ts index eef747d9378..28a9a203bc8 100644 --- a/packages/client/lib/commands/COMMAND_LIST.spec.ts +++ b/packages/client/lib/commands/COMMAND_LIST.spec.ts @@ -1,56 +1,62 @@ -import { strict as assert } from 'assert'; +import { strict as assert } from 'node:assert'; import testUtils, { GLOBAL } from '../test-utils'; -import { transformArguments, FilterBy } from './COMMAND_LIST'; +import COMMAND_LIST from './COMMAND_LIST'; describe('COMMAND LIST', () => { - testUtils.isVersionGreaterThanHook([7]); + testUtils.isVersionGreaterThanHook([7]); - describe('transformArguments', () => { - it('simple', () => { - assert.deepEqual( - transformArguments(), - ['COMMAND', 'LIST'] - ); - }); + describe('transformArguments', () => { + it('simple', () => { + assert.deepEqual( + COMMAND_LIST.transformArguments(), + ['COMMAND', 'LIST'] + ); + }); - describe('with FILTERBY', () => { - it('MODULE', () => { - assert.deepEqual( - transformArguments({ - filterBy: FilterBy.MODULE, - value: 'json' - }), - ['COMMAND', 'LIST', 'FILTERBY', 'MODULE', 'json'] - ); - }); + describe('with FILTERBY', () => { + it('MODULE', () => { + assert.deepEqual( + COMMAND_LIST.transformArguments({ + FILTERBY: { + type: 'MODULE', + value: 'JSON' + } + }), + ['COMMAND', 'LIST', 'FILTERBY', 'MODULE', 'JSON'] + ); + }); - it('ACLCAT', () => { - assert.deepEqual( - transformArguments({ - filterBy: FilterBy.ACLCAT, - value: 'admin' - }), - ['COMMAND', 'LIST', 'FILTERBY', 'ACLCAT', 'admin'] - ); - }); + it('ACLCAT', () => { + assert.deepEqual( + COMMAND_LIST.transformArguments({ + FILTERBY: { + type: 'ACLCAT', + value: 'admin' + } + }), + ['COMMAND', 'LIST', 'FILTERBY', 'ACLCAT', 'admin'] + ); + }); - it('PATTERN', () => { - assert.deepEqual( - transformArguments({ - filterBy: FilterBy.PATTERN, - value: 'a*' - }), - ['COMMAND', 'LIST', 'FILTERBY', 'PATTERN', 'a*'] - ); - }); - }); + it('PATTERN', () => { + assert.deepEqual( + COMMAND_LIST.transformArguments({ + FILTERBY: { + type: 'PATTERN', + value: 'a*' + } + }), + ['COMMAND', 'LIST', 'FILTERBY', 'PATTERN', 'a*'] + ); + }); }); + }); - testUtils.testWithClient('client.commandList', async client => { - const commandList = await client.commandList(); - assert.ok(Array.isArray(commandList)); - for (const command of commandList) { - assert.ok(typeof command === 'string'); - } - }, GLOBAL.SERVERS.OPEN); + testUtils.testWithClient('client.commandList', async client => { + const commandList = await client.commandList(); + assert.ok(Array.isArray(commandList)); + for (const command of commandList) { + assert.ok(typeof command === 'string'); + } + }, GLOBAL.SERVERS.OPEN); }); diff --git a/packages/client/lib/commands/COMMAND_LIST.ts b/packages/client/lib/commands/COMMAND_LIST.ts index a197bd1a4c6..e73cfdc1a0b 100644 --- a/packages/client/lib/commands/COMMAND_LIST.ts +++ b/packages/client/lib/commands/COMMAND_LIST.ts @@ -1,31 +1,35 @@ -import { RedisCommandArguments } from '.'; +import { RedisArgument, ArrayReply, BlobStringReply, Command } from '../RESP/types'; -export const IS_READ_ONLY = true; +export const COMMAND_LIST_FILTER_BY = { + MODULE: 'MODULE', + ACLCAT: 'ACLCAT', + PATTERN: 'PATTERN' +} as const; -export enum FilterBy { - MODULE = 'MODULE', - ACLCAT = 'ACLCAT', - PATTERN = 'PATTERN' -} +export type CommandListFilterBy = typeof COMMAND_LIST_FILTER_BY[keyof typeof COMMAND_LIST_FILTER_BY]; -interface Filter { - filterBy: FilterBy; - value: string; +export interface CommandListOptions { + FILTERBY?: { + type: CommandListFilterBy; + value: RedisArgument; + }; } - -export function transformArguments(filter?: Filter): RedisCommandArguments { - const args = ['COMMAND', 'LIST']; - - if (filter) { - args.push( - 'FILTERBY', - filter.filterBy, - filter.value - ); +export default { + FIRST_KEY_INDEX: undefined, + IS_READ_ONLY: true, + transformArguments(options?: CommandListOptions) { + const args: Array = ['COMMAND', 'LIST']; + + if (options?.FILTERBY) { + args.push( + 'FILTERBY', + options.FILTERBY.type, + options.FILTERBY.value + ); } return args; -} - -export declare function transformReply(): Array; + }, + transformReply: undefined as unknown as () => ArrayReply +} as const satisfies Command; diff --git a/packages/client/lib/commands/CONFIG_GET.spec.ts b/packages/client/lib/commands/CONFIG_GET.spec.ts index 83b5c410cfb..94bb2fadcb9 100644 --- a/packages/client/lib/commands/CONFIG_GET.spec.ts +++ b/packages/client/lib/commands/CONFIG_GET.spec.ts @@ -1,11 +1,31 @@ -import { strict as assert } from 'assert'; -import { transformArguments } from './CONFIG_GET'; +import { strict as assert } from 'node:assert'; +import testUtils, { GLOBAL } from '../test-utils'; +import CONFIG_GET from './CONFIG_GET'; describe('CONFIG GET', () => { - it('transformArguments', () => { - assert.deepEqual( - transformArguments('*'), - ['CONFIG', 'GET', '*'] - ); + describe('transformArguments', () => { + it('string', () => { + assert.deepEqual( + CONFIG_GET.transformArguments('*'), + ['CONFIG', 'GET', '*'] + ); }); + + it('Array', () => { + assert.deepEqual( + CONFIG_GET.transformArguments(['1', '2']), + ['CONFIG', 'GET', '1', '2'] + ); + }); + }); + + + testUtils.testWithClient('client.configGet', async client => { + const config = await client.configGet('*'); + assert.equal(typeof config, 'object'); + for (const [key, value] of Object.entries(config)) { + assert.equal(typeof key, 'string'); + assert.equal(typeof value, 'string'); + } + }, GLOBAL.SERVERS.OPEN); }); diff --git a/packages/client/lib/commands/CONFIG_GET.ts b/packages/client/lib/commands/CONFIG_GET.ts index 3afc0eddfd0..72fb6e56f5f 100644 --- a/packages/client/lib/commands/CONFIG_GET.ts +++ b/packages/client/lib/commands/CONFIG_GET.ts @@ -1,5 +1,14 @@ -export function transformArguments(parameter: string): Array { - return ['CONFIG', 'GET', parameter]; -} +import { MapReply, BlobStringReply, Command } from '../RESP/types'; +import { RedisVariadicArgument, pushVariadicArguments, transformTuplesReply } from './generic-transformers'; -export { transformTuplesReply as transformReply } from './generic-transformers'; +export default { + FIRST_KEY_INDEX: undefined, + IS_READ_ONLY: true, + transformArguments(parameters: RedisVariadicArgument) { + return pushVariadicArguments(['CONFIG', 'GET'], parameters); + }, + transformReply: { + 2: transformTuplesReply, + 3: undefined as unknown as () => MapReply + } +} as const satisfies Command; diff --git a/packages/client/lib/commands/CONFIG_RESETSTAT.spec.ts b/packages/client/lib/commands/CONFIG_RESETSTAT.spec.ts index d3f3048b944..c0699e182fc 100644 --- a/packages/client/lib/commands/CONFIG_RESETSTAT.spec.ts +++ b/packages/client/lib/commands/CONFIG_RESETSTAT.spec.ts @@ -1,11 +1,11 @@ -import { strict as assert } from 'assert'; -import { transformArguments } from './CONFIG_RESETSTAT'; +import { strict as assert } from 'node:assert'; +import CONFIG_RESETSTAT from './CONFIG_RESETSTAT'; describe('CONFIG RESETSTAT', () => { - it('transformArguments', () => { - assert.deepEqual( - transformArguments(), - ['CONFIG', 'RESETSTAT'] - ); - }); + it('transformArguments', () => { + assert.deepEqual( + CONFIG_RESETSTAT.transformArguments(), + ['CONFIG', 'RESETSTAT'] + ); + }); }); diff --git a/packages/client/lib/commands/CONFIG_RESETSTAT.ts b/packages/client/lib/commands/CONFIG_RESETSTAT.ts index aba54bc3c7b..4d5deb18b47 100644 --- a/packages/client/lib/commands/CONFIG_RESETSTAT.ts +++ b/packages/client/lib/commands/CONFIG_RESETSTAT.ts @@ -1,5 +1,10 @@ -export function transformArguments(): Array { - return ['CONFIG', 'RESETSTAT']; -} +import { SimpleStringReply, Command } from '../RESP/types'; -export declare function transformReply(): string; +export default { + FIRST_KEY_INDEX: undefined, + IS_READ_ONLY: true, + transformArguments() { + return ['CONFIG', 'RESETSTAT']; + }, + transformReply: undefined as unknown as () => SimpleStringReply +} as const satisfies Command; diff --git a/packages/client/lib/commands/CONFIG_REWRITE.spec.ts b/packages/client/lib/commands/CONFIG_REWRITE.spec.ts index cbc3e5b59d8..d612ae216bc 100644 --- a/packages/client/lib/commands/CONFIG_REWRITE.spec.ts +++ b/packages/client/lib/commands/CONFIG_REWRITE.spec.ts @@ -1,11 +1,11 @@ -import { strict as assert } from 'assert'; -import { transformArguments } from './CONFIG_REWRITE'; +import { strict as assert } from 'node:assert'; +import CONFIG_REWRITE from './CONFIG_REWRITE'; describe('CONFIG REWRITE', () => { - it('transformArguments', () => { - assert.deepEqual( - transformArguments(), - ['CONFIG', 'REWRITE'] - ); - }); + it('transformArguments', () => { + assert.deepEqual( + CONFIG_REWRITE.transformArguments(), + ['CONFIG', 'REWRITE'] + ); + }); }); diff --git a/packages/client/lib/commands/CONFIG_REWRITE.ts b/packages/client/lib/commands/CONFIG_REWRITE.ts index 67984adf300..6fbc4b1fa27 100644 --- a/packages/client/lib/commands/CONFIG_REWRITE.ts +++ b/packages/client/lib/commands/CONFIG_REWRITE.ts @@ -1,5 +1,10 @@ -export function transformArguments(): Array { - return ['CONFIG', 'REWRITE']; -} +import { SimpleStringReply, Command } from '../RESP/types'; -export declare function transformReply(): string; +export default { + FIRST_KEY_INDEX: undefined, + IS_READ_ONLY: true, + transformArguments() { + return ['CONFIG', 'REWRITE']; + }, + transformReply: undefined as unknown as () => SimpleStringReply +} as const satisfies Command; diff --git a/packages/client/lib/commands/CONFIG_SET.spec.ts b/packages/client/lib/commands/CONFIG_SET.spec.ts index 93a7a6ff25e..060183f58d1 100644 --- a/packages/client/lib/commands/CONFIG_SET.spec.ts +++ b/packages/client/lib/commands/CONFIG_SET.spec.ts @@ -1,24 +1,32 @@ -import { strict as assert } from 'assert'; -import { transformArguments } from './CONFIG_SET'; +import { strict as assert } from 'node:assert'; +import testUtils, { GLOBAL } from '../test-utils'; +import CONFIG_SET from './CONFIG_SET'; describe('CONFIG SET', () => { - describe('transformArguments', () => { - it('set one parameter (old version)', () => { - assert.deepEqual( - transformArguments('parameter', 'value'), - ['CONFIG', 'SET', 'parameter', 'value'] - ); - }); + describe('transformArguments', () => { + it('set one parameter (old version)', () => { + assert.deepEqual( + CONFIG_SET.transformArguments('parameter', 'value'), + ['CONFIG', 'SET', 'parameter', 'value'] + ); + }); - it('set muiltiple parameters', () => { - assert.deepEqual( - transformArguments({ - 1: 'a', - 2: 'b', - 3: 'c' - }), - ['CONFIG', 'SET', '1', 'a', '2', 'b', '3', 'c'] - ); - }); + it('set muiltiple parameters', () => { + assert.deepEqual( + CONFIG_SET.transformArguments({ + 1: 'a', + 2: 'b', + 3: 'c' + }), + ['CONFIG', 'SET', '1', 'a', '2', 'b', '3', 'c'] + ); }); + }); + + testUtils.testWithClient('client.configSet', async client => { + assert.equal( + await client.configSet('maxmemory', '0'), + 'OK' + ); + }, GLOBAL.SERVERS.OPEN); }); diff --git a/packages/client/lib/commands/CONFIG_SET.ts b/packages/client/lib/commands/CONFIG_SET.ts index 41f40d035d2..c7072245e22 100644 --- a/packages/client/lib/commands/CONFIG_SET.ts +++ b/packages/client/lib/commands/CONFIG_SET.ts @@ -1,23 +1,26 @@ -import { RedisCommandArgument, RedisCommandArguments } from '.'; +import { SimpleStringReply, Command, RedisArgument } from '../RESP/types'; -type SingleParameter = [parameter: RedisCommandArgument, value: RedisCommandArgument]; +type SingleParameter = [parameter: RedisArgument, value: RedisArgument]; -type MultipleParameters = [config: Record]; +type MultipleParameters = [config: Record]; -export function transformArguments( +export default { + FIRST_KEY_INDEX: undefined, + IS_READ_ONLY: true, + transformArguments( ...[parameterOrConfig, value]: SingleParameter | MultipleParameters -): RedisCommandArguments { - const args: RedisCommandArguments = ['CONFIG', 'SET']; - - if (typeof parameterOrConfig === 'string') { - args.push(parameterOrConfig, value!); + ) { + const args: Array = ['CONFIG', 'SET']; + + if (typeof parameterOrConfig === 'string' || parameterOrConfig instanceof Buffer) { + args.push(parameterOrConfig, value!); } else { - for (const [key, value] of Object.entries(parameterOrConfig)) { - args.push(key, value); - } + for (const [key, value] of Object.entries(parameterOrConfig)) { + args.push(key, value); + } } - + return args; -} - -export declare function transformReply(): string; + }, + transformReply: undefined as unknown as () => SimpleStringReply +} as const satisfies Command; diff --git a/packages/client/lib/commands/COPY.spec.ts b/packages/client/lib/commands/COPY.spec.ts index 0d68e969cdb..c4c26c30dc2 100644 --- a/packages/client/lib/commands/COPY.spec.ts +++ b/packages/client/lib/commands/COPY.spec.ts @@ -1,67 +1,54 @@ -import { strict as assert } from 'assert'; +import { strict as assert } from 'node:assert'; import testUtils, { GLOBAL } from '../test-utils'; -import { transformArguments, transformReply } from './COPY'; +import COPY from './COPY'; describe('COPY', () => { - testUtils.isVersionGreaterThanHook([6, 2]); - - describe('transformArguments', () => { - it('simple', () => { - assert.deepEqual( - transformArguments('source', 'destination'), - ['COPY', 'source', 'destination'] - ); - }); - - it('with destination DB flag', () => { - assert.deepEqual( - transformArguments('source', 'destination', { - destinationDb: 1 - }), - ['COPY', 'source', 'destination', 'DB', '1'] - ); - }); - - it('with replace flag', () => { - assert.deepEqual( - transformArguments('source', 'destination', { - replace: true - }), - ['COPY', 'source', 'destination', 'REPLACE'] - ); - }); - - it('with both flags', () => { - assert.deepEqual( - transformArguments('source', 'destination', { - destinationDb: 1, - replace: true - }), - ['COPY', 'source', 'destination', 'DB', '1', 'REPLACE'] - ); - }); + testUtils.isVersionGreaterThanHook([6, 2]); + + describe('transformArguments', () => { + it('simple', () => { + assert.deepEqual( + COPY.transformArguments('source', 'destination'), + ['COPY', 'source', 'destination'] + ); }); - describe('transformReply', () => { - it('0', () => { - assert.equal( - transformReply(0), - false - ); - }); + it('with destination DB flag', () => { + assert.deepEqual( + COPY.transformArguments('source', 'destination', { + DB: 1 + }), + ['COPY', 'source', 'destination', 'DB', '1'] + ); + }); - it('1', () => { - assert.equal( - transformReply(1), - true - ); - }); + it('with replace flag', () => { + assert.deepEqual( + COPY.transformArguments('source', 'destination', { + REPLACE: true + }), + ['COPY', 'source', 'destination', 'REPLACE'] + ); }); - testUtils.testWithClient('client.copy', async client => { - assert.equal( - await client.copy('source', 'destination'), - false - ); - }, GLOBAL.SERVERS.OPEN); + it('with both flags', () => { + assert.deepEqual( + COPY.transformArguments('source', 'destination', { + DB: 1, + REPLACE: true + }), + ['COPY', 'source', 'destination', 'DB', '1', 'REPLACE'] + ); + }); + }); + + testUtils.testAll('copy', async client => { + assert.equal( + await client.copy('{tag}source', '{tag}destination'), + 0 + ); + }, { + client: GLOBAL.SERVERS.OPEN, + cluster: GLOBAL.CLUSTERS.OPEN + }); }); diff --git a/packages/client/lib/commands/COPY.ts b/packages/client/lib/commands/COPY.ts index b1e212a9956..a65948cf944 100644 --- a/packages/client/lib/commands/COPY.ts +++ b/packages/client/lib/commands/COPY.ts @@ -1,28 +1,25 @@ -import { RedisCommandArgument, RedisCommandArguments } from '.'; +import { RedisArgument, NumberReply, Command } from '../RESP/types'; -interface CopyCommandOptions { - destinationDb?: number; - replace?: boolean; +export interface CopyCommandOptions { + DB?: number; + REPLACE?: boolean; } -export const FIRST_KEY_INDEX = 1; - -export function transformArguments( - source: RedisCommandArgument, - destination: RedisCommandArgument, - options?: CopyCommandOptions -): RedisCommandArguments { +export default { + FIRST_KEY_INDEX: 1, + IS_READ_ONLY: false, + transformArguments(source: RedisArgument, destination: RedisArgument, options?: CopyCommandOptions) { const args = ['COPY', source, destination]; - if (options?.destinationDb) { - args.push('DB', options.destinationDb.toString()); + if (options?.DB) { + args.push('DB', options.DB.toString()); } - if (options?.replace) { - args.push('REPLACE'); + if (options?.REPLACE) { + args.push('REPLACE'); } return args; -} - -export { transformBooleanReply as transformReply } from './generic-transformers'; + }, + transformReply: undefined as unknown as () => NumberReply +} as const satisfies Command; diff --git a/packages/client/lib/commands/DBSIZE.spec.ts b/packages/client/lib/commands/DBSIZE.spec.ts index a014a46e6e2..bd668d166e7 100644 --- a/packages/client/lib/commands/DBSIZE.spec.ts +++ b/packages/client/lib/commands/DBSIZE.spec.ts @@ -1,19 +1,19 @@ -import { strict as assert } from 'assert'; +import { strict as assert } from 'node:assert'; import testUtils, { GLOBAL } from '../test-utils'; -import { transformArguments } from './DBSIZE'; +import DBSIZE from './DBSIZE'; describe('DBSIZE', () => { - it('transformArguments', () => { - assert.deepEqual( - transformArguments(), - ['DBSIZE'] - ); - }); + it('transformArguments', () => { + assert.deepEqual( + DBSIZE.transformArguments(), + ['DBSIZE'] + ); + }); - testUtils.testWithClient('client.dbSize', async client => { - assert.equal( - await client.dbSize(), - 0 - ); - }, GLOBAL.SERVERS.OPEN); + testUtils.testWithClient('client.dbSize', async client => { + assert.equal( + await client.dbSize(), + 0 + ); + }, GLOBAL.SERVERS.OPEN); }); diff --git a/packages/client/lib/commands/DBSIZE.ts b/packages/client/lib/commands/DBSIZE.ts index 6b442ec33a2..54770831ab0 100644 --- a/packages/client/lib/commands/DBSIZE.ts +++ b/packages/client/lib/commands/DBSIZE.ts @@ -1,7 +1,10 @@ -export const IS_READ_ONLY = true; +import { NumberReply, Command } from '../RESP/types'; -export function transformArguments(): Array { +export default { + FIRST_KEY_INDEX: undefined, + IS_READ_ONLY: true, + transformArguments() { return ['DBSIZE']; -} - -export declare function transformReply(): number; + }, + transformReply: undefined as unknown as () => NumberReply +} as const satisfies Command; diff --git a/packages/client/lib/commands/DECR.spec.ts b/packages/client/lib/commands/DECR.spec.ts index 75e1205feda..80d6c8eb55e 100644 --- a/packages/client/lib/commands/DECR.spec.ts +++ b/packages/client/lib/commands/DECR.spec.ts @@ -1,19 +1,22 @@ -import { strict as assert } from 'assert'; +import { strict as assert } from 'node:assert'; import testUtils, { GLOBAL } from '../test-utils'; -import { transformArguments } from './DECR'; +import DECR from './DECR'; describe('DECR', () => { - it('transformArguments', () => { - assert.deepEqual( - transformArguments('key'), - ['DECR', 'key'] - ); - }); + it('transformArguments', () => { + assert.deepEqual( + DECR.transformArguments('key'), + ['DECR', 'key'] + ); + }); - testUtils.testWithClient('client.decr', async client => { - assert.equal( - await client.decr('key'), - -1 - ); - }, GLOBAL.SERVERS.OPEN); + testUtils.testAll('decr', async client => { + assert.equal( + await client.decr('key'), + -1 + ); + }, { + client: GLOBAL.SERVERS.OPEN, + cluster: GLOBAL.CLUSTERS.OPEN + }); }); diff --git a/packages/client/lib/commands/DECR.ts b/packages/client/lib/commands/DECR.ts index 2b5f2c4bb5c..540148b7eed 100644 --- a/packages/client/lib/commands/DECR.ts +++ b/packages/client/lib/commands/DECR.ts @@ -1,9 +1,9 @@ -import { RedisCommandArgument, RedisCommandArguments } from '.'; +import { RedisArgument, NumberReply, Command } from '../RESP/types'; -export const FIRST_KEY_INDEX = 1; - -export function transformArguments(key: RedisCommandArgument): RedisCommandArguments { +export default { + FIRST_KEY_INDEX: 1, + transformArguments(key: RedisArgument) { return ['DECR', key]; -} - -export declare function transformReply(): number; + }, + transformReply: undefined as unknown as () => NumberReply +} as const satisfies Command; diff --git a/packages/client/lib/commands/DECRBY.spec.ts b/packages/client/lib/commands/DECRBY.spec.ts index d2c23e94728..fc0c1033187 100644 --- a/packages/client/lib/commands/DECRBY.spec.ts +++ b/packages/client/lib/commands/DECRBY.spec.ts @@ -1,19 +1,22 @@ -import { strict as assert } from 'assert'; +import { strict as assert } from 'node:assert'; import testUtils, { GLOBAL } from '../test-utils'; -import { transformArguments } from './DECRBY'; +import DECRBY from './DECRBY'; describe('DECRBY', () => { - it('transformArguments', () => { - assert.deepEqual( - transformArguments('key', 2), - ['DECRBY', 'key', '2'] - ); - }); + it('transformArguments', () => { + assert.deepEqual( + DECRBY.transformArguments('key', 2), + ['DECRBY', 'key', '2'] + ); + }); - testUtils.testWithClient('client.decrBy', async client => { - assert.equal( - await client.decrBy('key', 2), - -2 - ); - }, GLOBAL.SERVERS.OPEN); + testUtils.testAll('decrBy', async client => { + assert.equal( + await client.decrBy('key', 2), + -2 + ); + }, { + client: GLOBAL.SERVERS.OPEN, + cluster: GLOBAL.CLUSTERS.OPEN + }); }); diff --git a/packages/client/lib/commands/DECRBY.ts b/packages/client/lib/commands/DECRBY.ts index afe4d79f0a1..77d56939dd5 100644 --- a/packages/client/lib/commands/DECRBY.ts +++ b/packages/client/lib/commands/DECRBY.ts @@ -1,12 +1,9 @@ -import { RedisCommandArgument, RedisCommandArguments } from '.'; +import { RedisArgument, NumberReply, Command } from '../RESP/types'; -export const FIRST_KEY_INDEX = 1; - -export function transformArguments( - key: RedisCommandArgument, - decrement: number -): RedisCommandArguments { +export default { + FIRST_KEY_INDEX: 1, + transformArguments(key: RedisArgument, decrement: number) { return ['DECRBY', key, decrement.toString()]; -} - -export declare function transformReply(): number; + }, + transformReply: undefined as unknown as () => NumberReply +} as const satisfies Command; diff --git a/packages/client/lib/commands/DEL.spec.ts b/packages/client/lib/commands/DEL.spec.ts index 75a29a8f641..caac8ac13b5 100644 --- a/packages/client/lib/commands/DEL.spec.ts +++ b/packages/client/lib/commands/DEL.spec.ts @@ -1,28 +1,31 @@ -import { strict as assert } from 'assert'; +import { strict as assert } from 'node:assert'; import testUtils, { GLOBAL } from '../test-utils'; -import { transformArguments } from './DEL'; +import DEL from './DEL'; describe('DEL', () => { - describe('transformArguments', () => { - it('string', () => { - assert.deepEqual( - transformArguments('key'), - ['DEL', 'key'] - ); - }); + describe('transformArguments', () => { + it('string', () => { + assert.deepEqual( + DEL.transformArguments('key'), + ['DEL', 'key'] + ); + }); - it('array', () => { - assert.deepEqual( - transformArguments(['key1', 'key2']), - ['DEL', 'key1', 'key2'] - ); - }); + it('array', () => { + assert.deepEqual( + DEL.transformArguments(['key1', 'key2']), + ['DEL', 'key1', 'key2'] + ); }); + }); - testUtils.testWithClient('client.del', async client => { - assert.equal( - await client.del('key'), - 0 - ); - }, GLOBAL.SERVERS.OPEN); + testUtils.testAll('del', async client => { + assert.equal( + await client.del('key'), + 0 + ); + }, { + client: GLOBAL.SERVERS.OPEN, + cluster: GLOBAL.CLUSTERS.OPEN + }); }); diff --git a/packages/client/lib/commands/DEL.ts b/packages/client/lib/commands/DEL.ts index d60abe0f28e..f59a5ba2e89 100644 --- a/packages/client/lib/commands/DEL.ts +++ b/packages/client/lib/commands/DEL.ts @@ -1,12 +1,11 @@ -import { RedisCommandArgument, RedisCommandArguments } from '.'; -import { pushVerdictArguments } from './generic-transformers'; +import { NumberReply, Command } from '../RESP/types'; +import { RedisVariadicArgument, pushVariadicArguments } from './generic-transformers'; -export const FIRST_KEY_INDEX = 1; - -export function transformArguments( - keys: RedisCommandArgument | Array -): RedisCommandArguments { - return pushVerdictArguments(['DEL'], keys); -} - -export declare function transformReply(): number; +export default { + FIRST_KEY_INDEX: 1, + IS_READ_ONLY: false, + transformArguments(keys: RedisVariadicArgument) { + return pushVariadicArguments(['DEL'], keys); + }, + transformReply: undefined as unknown as () => NumberReply +} as const satisfies Command; diff --git a/packages/client/lib/commands/DISCARD.spec.ts b/packages/client/lib/commands/DISCARD.spec.ts index b01f9d650d9..76e0abd57af 100644 --- a/packages/client/lib/commands/DISCARD.spec.ts +++ b/packages/client/lib/commands/DISCARD.spec.ts @@ -1,11 +1,11 @@ -import { strict as assert } from 'assert'; -import { transformArguments } from './DISCARD'; +import { strict as assert } from 'node:assert'; +import DISCARD from './DISCARD'; describe('DISCARD', () => { - it('transformArguments', () => { - assert.deepEqual( - transformArguments(), - ['DISCARD'] - ); - }); + it('transformArguments', () => { + assert.deepEqual( + DISCARD.transformArguments(), + ['DISCARD'] + ); + }); }); diff --git a/packages/client/lib/commands/DISCARD.ts b/packages/client/lib/commands/DISCARD.ts index acad8a722e1..e153070c989 100644 --- a/packages/client/lib/commands/DISCARD.ts +++ b/packages/client/lib/commands/DISCARD.ts @@ -1,7 +1,8 @@ -import { RedisCommandArgument } from '.'; +import { SimpleStringReply, Command } from '../RESP/types'; -export function transformArguments(): Array { +export default { + transformArguments() { return ['DISCARD']; -} - -export declare function transformReply(): RedisCommandArgument; + }, + transformReply: undefined as unknown as () => SimpleStringReply +} as const satisfies Command; diff --git a/packages/client/lib/commands/DUMP.spec.ts b/packages/client/lib/commands/DUMP.spec.ts index aebbf4f3f7c..15be3fae086 100644 --- a/packages/client/lib/commands/DUMP.spec.ts +++ b/packages/client/lib/commands/DUMP.spec.ts @@ -1,11 +1,14 @@ -import { strict as assert } from 'assert'; +import { strict as assert } from 'node:assert'; import testUtils, { GLOBAL } from '../test-utils'; describe('DUMP', () => { - testUtils.testWithClient('client.dump', async client => { - assert.equal( - await client.dump('key'), - null - ); - }, GLOBAL.SERVERS.OPEN); + testUtils.testAll('client.dump', async client => { + assert.equal( + await client.dump('key'), + null + ); + }, { + client: GLOBAL.SERVERS.OPEN, + cluster: GLOBAL.CLUSTERS.OPEN + }); }); diff --git a/packages/client/lib/commands/DUMP.ts b/packages/client/lib/commands/DUMP.ts index fd4354db45c..06b12f06d09 100644 --- a/packages/client/lib/commands/DUMP.ts +++ b/packages/client/lib/commands/DUMP.ts @@ -1,9 +1,10 @@ -import { RedisCommandArgument, RedisCommandArguments } from '.'; +import { RedisArgument, BlobStringReply, Command } from '../RESP/types'; -export const FIRST_KEY_INDEX = 1; - -export function transformArguments(key: RedisCommandArgument): RedisCommandArguments { +export default { + FIRST_KEY_INDEX: 1, + IS_READ_ONLY: true, + transformArguments(key: RedisArgument) { return ['DUMP', key]; -} - -export declare function transformReply(): RedisCommandArgument; + }, + transformReply: undefined as unknown as () => BlobStringReply +} as const satisfies Command; diff --git a/packages/client/lib/commands/ECHO.spec.ts b/packages/client/lib/commands/ECHO.spec.ts index 27f6b2a17d3..c0d0725282c 100644 --- a/packages/client/lib/commands/ECHO.spec.ts +++ b/packages/client/lib/commands/ECHO.spec.ts @@ -1,19 +1,19 @@ -import { strict as assert } from 'assert'; +import { strict as assert } from 'node:assert'; import testUtils, { GLOBAL } from '../test-utils'; -import { transformArguments } from './ECHO'; +import ECHO from './ECHO'; describe('ECHO', () => { - it('transformArguments', () => { - assert.deepEqual( - transformArguments('message'), - ['ECHO', 'message'] - ); - }); + it('transformArguments', () => { + assert.deepEqual( + ECHO.transformArguments('message'), + ['ECHO', 'message'] + ); + }); - testUtils.testWithClient('client.echo', async client => { - assert.equal( - await client.echo('message'), - 'message' - ); - }, GLOBAL.SERVERS.OPEN); + testUtils.testWithClient('client.echo', async client => { + assert.equal( + await client.echo('message'), + 'message' + ); + }, GLOBAL.SERVERS.OPEN); }); diff --git a/packages/client/lib/commands/ECHO.ts b/packages/client/lib/commands/ECHO.ts index 7a837307e2b..dfe5ec13000 100644 --- a/packages/client/lib/commands/ECHO.ts +++ b/packages/client/lib/commands/ECHO.ts @@ -1,9 +1,10 @@ -import { RedisCommandArgument, RedisCommandArguments } from '.'; +import { RedisArgument, BlobStringReply, Command } from '../RESP/types'; -export const IS_READ_ONLY = true; - -export function transformArguments(message: RedisCommandArgument): RedisCommandArguments { +export default { + FIRST_KEY_INDEX: undefined, + IS_READ_ONLY: true, + transformArguments(message: RedisArgument) { return ['ECHO', message]; -} - -export declare function transformReply(): RedisCommandArgument; + }, + transformReply: undefined as unknown as () => BlobStringReply +} as const satisfies Command; diff --git a/packages/client/lib/commands/EVAL.spec.ts b/packages/client/lib/commands/EVAL.spec.ts index 7aa029362fd..2aea64e0991 100644 --- a/packages/client/lib/commands/EVAL.spec.ts +++ b/packages/client/lib/commands/EVAL.spec.ts @@ -1,29 +1,25 @@ -import { strict as assert } from 'assert'; +import { strict as assert } from 'node:assert'; import testUtils, { GLOBAL } from '../test-utils'; -import { transformArguments } from './EVAL'; +import EVAL from './EVAL'; describe('EVAL', () => { - it('transformArguments', () => { - assert.deepEqual( - transformArguments('return KEYS[1] + ARGV[1]', { - keys: ['key'], - arguments: ['argument'] - }), - ['EVAL', 'return KEYS[1] + ARGV[1]', '1', 'key', 'argument'] - ); - }); + it('transformArguments', () => { + assert.deepEqual( + EVAL.transformArguments('return KEYS[1] + ARGV[1]', { + keys: ['key'], + arguments: ['argument'] + }), + ['EVAL', 'return KEYS[1] + ARGV[1]', '1', 'key', 'argument'] + ); + }); - testUtils.testWithClient('client.eval', async client => { - assert.equal( - await client.eval('return 1'), - 1 - ); - }, GLOBAL.SERVERS.OPEN); - - testUtils.testWithCluster('cluster.eval', async cluster => { - assert.equal( - await cluster.eval('return 1'), - 1 - ); - }, GLOBAL.CLUSTERS.OPEN); + testUtils.testAll('eval', async client => { + assert.equal( + await client.eval('return 1'), + 1 + ); + }, { + client: GLOBAL.SERVERS.OPEN, + cluster: GLOBAL.CLUSTERS.OPEN + }); }); diff --git a/packages/client/lib/commands/EVAL.ts b/packages/client/lib/commands/EVAL.ts index a82f8bf0aad..21684e7a313 100644 --- a/packages/client/lib/commands/EVAL.ts +++ b/packages/client/lib/commands/EVAL.ts @@ -1,7 +1,33 @@ -import { evalFirstKeyIndex, EvalOptions, pushEvalArguments } from './generic-transformers'; +import { RedisArgument, ReplyUnion, Command } from '../RESP/types'; -export const FIRST_KEY_INDEX = evalFirstKeyIndex; +export interface EvalOptions { + keys?: Array; + arguments?: Array; +} + +export function transformEvalArguments( + command: RedisArgument, + script: RedisArgument, + options?: EvalOptions +) { + const args = [command, script]; + + if (options?.keys) { + args.push(options.keys.length.toString(), ...options.keys); + } else { + args.push('0'); + } -export function transformArguments(script: string, options?: EvalOptions): Array { - return pushEvalArguments(['EVAL', script], options); + if (options?.arguments) { + args.push(...options.arguments); + } + + return args; } + +export default { + FIRST_KEY_INDEX: (_, options?: EvalOptions) => options?.keys?.[0], + IS_READ_ONLY: false, + transformArguments: transformEvalArguments.bind(undefined, 'EVAL'), + transformReply: undefined as unknown as () => ReplyUnion +} as const satisfies Command; diff --git a/packages/client/lib/commands/EVALSHA.spec.ts b/packages/client/lib/commands/EVALSHA.spec.ts index 08b330ac4f5..81d3a0ec2b0 100644 --- a/packages/client/lib/commands/EVALSHA.spec.ts +++ b/packages/client/lib/commands/EVALSHA.spec.ts @@ -1,14 +1,14 @@ -import { strict as assert } from 'assert'; -import { transformArguments } from './EVALSHA'; +import { strict as assert } from 'node:assert'; +import EVALSHA from './EVALSHA'; describe('EVALSHA', () => { - it('transformArguments', () => { - assert.deepEqual( - transformArguments('sha1', { - keys: ['key'], - arguments: ['argument'] - }), - ['EVALSHA', 'sha1', '1', 'key', 'argument'] - ); - }); + it('transformArguments', () => { + assert.deepEqual( + EVALSHA.transformArguments('sha1', { + keys: ['key'], + arguments: ['argument'] + }), + ['EVALSHA', 'sha1', '1', 'key', 'argument'] + ); + }); }); diff --git a/packages/client/lib/commands/EVALSHA.ts b/packages/client/lib/commands/EVALSHA.ts index 24f7060a052..dc4127f90da 100644 --- a/packages/client/lib/commands/EVALSHA.ts +++ b/packages/client/lib/commands/EVALSHA.ts @@ -1,7 +1,9 @@ -import { evalFirstKeyIndex, EvalOptions, pushEvalArguments } from './generic-transformers'; +import { Command } from '../RESP/types'; +import EVAL, { transformEvalArguments } from './EVAL'; -export const FIRST_KEY_INDEX = evalFirstKeyIndex; - -export function transformArguments(sha1: string, options?: EvalOptions): Array { - return pushEvalArguments(['EVALSHA', sha1], options); -} +export default { + FIRST_KEY_INDEX: EVAL.FIRST_KEY_INDEX, + IS_READ_ONLY: false, + transformArguments: transformEvalArguments.bind(undefined, 'EVALSHA'), + transformReply: EVAL.transformReply +} as const satisfies Command; diff --git a/packages/client/lib/commands/EVALSHA_RO.spec.ts b/packages/client/lib/commands/EVALSHA_RO.spec.ts index 939a4a209cb..20b4a27e0d1 100644 --- a/packages/client/lib/commands/EVALSHA_RO.spec.ts +++ b/packages/client/lib/commands/EVALSHA_RO.spec.ts @@ -1,17 +1,17 @@ -import { strict as assert } from 'assert'; +import { strict as assert } from 'node:assert'; import testUtils from '../test-utils'; -import { transformArguments } from './EVALSHA_RO'; +import EVALSHA_RO from './EVALSHA_RO'; describe('EVALSHA_RO', () => { - testUtils.isVersionGreaterThanHook([7]); + testUtils.isVersionGreaterThanHook([7]); - it('transformArguments', () => { - assert.deepEqual( - transformArguments('sha1', { - keys: ['key'], - arguments: ['argument'] - }), - ['EVALSHA_RO', 'sha1', '1', 'key', 'argument'] - ); - }); + it('transformArguments', () => { + assert.deepEqual( + EVALSHA_RO.transformArguments('sha1', { + keys: ['key'], + arguments: ['argument'] + }), + ['EVALSHA_RO', 'sha1', '1', 'key', 'argument'] + ); + }); }); diff --git a/packages/client/lib/commands/EVALSHA_RO.ts b/packages/client/lib/commands/EVALSHA_RO.ts index c3fcc3dc9cf..fe9042bd5fd 100644 --- a/packages/client/lib/commands/EVALSHA_RO.ts +++ b/packages/client/lib/commands/EVALSHA_RO.ts @@ -1,9 +1,9 @@ -import { evalFirstKeyIndex, EvalOptions, pushEvalArguments } from './generic-transformers'; +import { Command } from '../RESP/types'; +import EVAL, { transformEvalArguments } from './EVAL'; -export const FIRST_KEY_INDEX = evalFirstKeyIndex; - -export const IS_READ_ONLY = true; - -export function transformArguments(sha1: string, options?: EvalOptions): Array { - return pushEvalArguments(['EVALSHA_RO', sha1], options); -} +export default { + FIRST_KEY_INDEX: EVAL.FIRST_KEY_INDEX, + IS_READ_ONLY: true, + transformArguments: transformEvalArguments.bind(undefined, 'EVALSHA_RO'), + transformReply: EVAL.transformReply +} as const satisfies Command; diff --git a/packages/client/lib/commands/EVAL_RO.spec.ts b/packages/client/lib/commands/EVAL_RO.spec.ts index f71d0b2b24a..3f071e80681 100644 --- a/packages/client/lib/commands/EVAL_RO.spec.ts +++ b/packages/client/lib/commands/EVAL_RO.spec.ts @@ -1,31 +1,27 @@ -import { strict as assert } from 'assert'; +import { strict as assert } from 'node:assert'; import testUtils, { GLOBAL } from '../test-utils'; -import { transformArguments } from './EVAL_RO'; +import EVAL_RO from './EVAL_RO'; describe('EVAL_RO', () => { - testUtils.isVersionGreaterThanHook([7]); + testUtils.isVersionGreaterThanHook([7]); - it('transformArguments', () => { - assert.deepEqual( - transformArguments('return KEYS[1] + ARGV[1]', { - keys: ['key'], - arguments: ['argument'] - }), - ['EVAL_RO', 'return KEYS[1] + ARGV[1]', '1', 'key', 'argument'] - ); - }); + it('transformArguments', () => { + assert.deepEqual( + EVAL_RO.transformArguments('return KEYS[1] + ARGV[1]', { + keys: ['key'], + arguments: ['argument'] + }), + ['EVAL_RO', 'return KEYS[1] + ARGV[1]', '1', 'key', 'argument'] + ); + }); - testUtils.testWithClient('client.evalRo', async client => { - assert.equal( - await client.evalRo('return 1'), - 1 - ); - }, GLOBAL.SERVERS.OPEN); - - testUtils.testWithCluster('cluster.evalRo', async cluster => { - assert.equal( - await cluster.evalRo('return 1'), - 1 - ); - }, GLOBAL.CLUSTERS.OPEN); + testUtils.testAll('evalRo', async cluster => { + assert.equal( + await cluster.evalRo('return 1'), + 1 + ); + }, { + client: GLOBAL.SERVERS.OPEN, + cluster: GLOBAL.CLUSTERS.OPEN + }); }); diff --git a/packages/client/lib/commands/EVAL_RO.ts b/packages/client/lib/commands/EVAL_RO.ts index 590c3af04f3..2608e28f789 100644 --- a/packages/client/lib/commands/EVAL_RO.ts +++ b/packages/client/lib/commands/EVAL_RO.ts @@ -1,9 +1,9 @@ -import { evalFirstKeyIndex, EvalOptions, pushEvalArguments } from './generic-transformers'; +import { Command } from '../RESP/types'; +import EVAL, { transformEvalArguments } from './EVAL'; -export const FIRST_KEY_INDEX = evalFirstKeyIndex; - -export const IS_READ_ONLY = true; - -export function transformArguments(script: string, options?: EvalOptions): Array { - return pushEvalArguments(['EVAL_RO', script], options); -} +export default { + FIRST_KEY_INDEX: EVAL.FIRST_KEY_INDEX, + IS_READ_ONLY: true, + transformArguments: transformEvalArguments.bind(undefined, 'EVAL_RO'), + transformReply: EVAL.transformReply +} as const satisfies Command; diff --git a/packages/client/lib/commands/EXISTS.spec.ts b/packages/client/lib/commands/EXISTS.spec.ts index be1a808225e..695795697f1 100644 --- a/packages/client/lib/commands/EXISTS.spec.ts +++ b/packages/client/lib/commands/EXISTS.spec.ts @@ -1,28 +1,31 @@ -import { strict as assert } from 'assert'; +import { strict as assert } from 'node:assert'; import testUtils, { GLOBAL } from '../test-utils'; -import { transformArguments } from './EXISTS'; +import EXISTS from './EXISTS'; describe('EXISTS', () => { - describe('transformArguments', () => { - it('string', () => { - assert.deepEqual( - transformArguments('key'), - ['EXISTS', 'key'] - ); - }); + describe('transformArguments', () => { + it('string', () => { + assert.deepEqual( + EXISTS.transformArguments('key'), + ['EXISTS', 'key'] + ); + }); - it('array', () => { - assert.deepEqual( - transformArguments(['1', '2']), - ['EXISTS', '1', '2'] - ); - }); + it('array', () => { + assert.deepEqual( + EXISTS.transformArguments(['1', '2']), + ['EXISTS', '1', '2'] + ); }); + }); - testUtils.testWithClient('client.exists', async client => { - assert.equal( - await client.exists('key'), - 0 - ); - }, GLOBAL.SERVERS.OPEN); + testUtils.testAll('exists', async client => { + assert.equal( + await client.exists('key'), + 0 + ); + }, { + client: GLOBAL.SERVERS.OPEN, + cluster: GLOBAL.CLUSTERS.OPEN + }); }); diff --git a/packages/client/lib/commands/EXISTS.ts b/packages/client/lib/commands/EXISTS.ts index 3bbc72ada46..a077943b8d2 100644 --- a/packages/client/lib/commands/EXISTS.ts +++ b/packages/client/lib/commands/EXISTS.ts @@ -1,14 +1,11 @@ -import { RedisCommandArgument, RedisCommandArguments } from '.'; -import { pushVerdictArguments } from './generic-transformers'; - -export const FIRST_KEY_INDEX = 1; - -export const IS_READ_ONLY = true; - -export function transformArguments( - keys: RedisCommandArgument | Array -): RedisCommandArguments { - return pushVerdictArguments(['EXISTS'], keys); -} - -export declare function transformReply(): number; +import { NumberReply, Command } from '../RESP/types'; +import { RedisVariadicArgument, pushVariadicArguments } from './generic-transformers'; + +export default { + FIRST_KEY_INDEX: 1, + IS_READ_ONLY: true, + transformArguments(keys: RedisVariadicArgument) { + return pushVariadicArguments(['EXISTS'], keys); + }, + transformReply: undefined as unknown as () => NumberReply +} as const satisfies Command; diff --git a/packages/client/lib/commands/EXPIRE.spec.ts b/packages/client/lib/commands/EXPIRE.spec.ts index 39f9d70bd93..817e37cca45 100644 --- a/packages/client/lib/commands/EXPIRE.spec.ts +++ b/packages/client/lib/commands/EXPIRE.spec.ts @@ -1,28 +1,31 @@ -import { strict as assert } from 'assert'; +import { strict as assert } from 'node:assert'; import testUtils, { GLOBAL } from '../test-utils'; -import { transformArguments } from './EXPIRE'; +import EXPIRE from './EXPIRE'; describe('EXPIRE', () => { - describe('transformArguments', () => { - it('simple', () => { - assert.deepEqual( - transformArguments('key', 1), - ['EXPIRE', 'key', '1'] - ); - }); + describe('transformArguments', () => { + it('simple', () => { + assert.deepEqual( + EXPIRE.transformArguments('key', 1), + ['EXPIRE', 'key', '1'] + ); + }); - it('with set option', () => { - assert.deepEqual( - transformArguments('key', 1, 'NX'), - ['EXPIRE', 'key', '1', 'NX'] - ); - }); + it('with set option', () => { + assert.deepEqual( + EXPIRE.transformArguments('key', 1, 'NX'), + ['EXPIRE', 'key', '1', 'NX'] + ); }); + }); - testUtils.testWithClient('client.expire', async client => { - assert.equal( - await client.expire('key', 0), - false - ); - }, GLOBAL.SERVERS.OPEN); + testUtils.testAll('expire', async client => { + assert.equal( + await client.expire('key', 0), + 0 + ); + }, { + client: GLOBAL.SERVERS.OPEN, + cluster: GLOBAL.CLUSTERS.OPEN + }); }); diff --git a/packages/client/lib/commands/EXPIRE.ts b/packages/client/lib/commands/EXPIRE.ts index d9e85595c3b..3e57769bd6e 100644 --- a/packages/client/lib/commands/EXPIRE.ts +++ b/packages/client/lib/commands/EXPIRE.ts @@ -1,19 +1,20 @@ -import { RedisCommandArgument, RedisCommandArguments } from '.'; +import { RedisArgument, NumberReply, Command } from '../RESP/types'; -export const FIRST_KEY_INDEX = 1; - -export function transformArguments( - key: RedisCommandArgument, +export default { + FIRST_KEY_INDEX: 1, + IS_READ_ONLY: true, + transformArguments( + key: RedisArgument, seconds: number, mode?: 'NX' | 'XX' | 'GT' | 'LT' -): RedisCommandArguments { + ) { const args = ['EXPIRE', key, seconds.toString()]; if (mode) { - args.push(mode); + args.push(mode); } return args; -} - -export { transformBooleanReply as transformReply } from './generic-transformers'; + }, + transformReply: undefined as unknown as () => NumberReply +} as const satisfies Command; diff --git a/packages/client/lib/commands/EXPIREAT.spec.ts b/packages/client/lib/commands/EXPIREAT.spec.ts index 0335b36f5f5..31efdcea398 100644 --- a/packages/client/lib/commands/EXPIREAT.spec.ts +++ b/packages/client/lib/commands/EXPIREAT.spec.ts @@ -1,36 +1,39 @@ -import { strict as assert } from 'assert'; +import { strict as assert } from 'node:assert'; import testUtils, { GLOBAL } from '../test-utils'; -import { transformArguments } from './EXPIREAT'; +import EXPIREAT from './EXPIREAT'; describe('EXPIREAT', () => { - describe('transformArguments', () => { - it('number', () => { - assert.deepEqual( - transformArguments('key', 1), - ['EXPIREAT', 'key', '1'] - ); - }); + describe('transformArguments', () => { + it('number', () => { + assert.deepEqual( + EXPIREAT.transformArguments('key', 1), + ['EXPIREAT', 'key', '1'] + ); + }); + + it('date', () => { + const d = new Date(); + assert.deepEqual( + EXPIREAT.transformArguments('key', d), + ['EXPIREAT', 'key', Math.floor(d.getTime() / 1000).toString()] + ); + }); - it('date', () => { - const d = new Date(); - assert.deepEqual( - transformArguments('key', d), - ['EXPIREAT', 'key', Math.floor(d.getTime() / 1000).toString()] - ); - }); - - it('with set option', () => { - assert.deepEqual( - transformArguments('key', 1, 'GT'), - ['EXPIREAT', 'key', '1', 'GT'] - ); - }); + it('with set option', () => { + assert.deepEqual( + EXPIREAT.transformArguments('key', 1, 'GT'), + ['EXPIREAT', 'key', '1', 'GT'] + ); }); + }); - testUtils.testWithClient('client.expireAt', async client => { - assert.equal( - await client.expireAt('key', 1), - false - ); - }, GLOBAL.SERVERS.OPEN); + testUtils.testAll('expireAt', async client => { + assert.equal( + await client.expireAt('key', 1), + 0 + ); + }, { + client: GLOBAL.SERVERS.OPEN, + cluster: GLOBAL.CLUSTERS.OPEN + }); }); diff --git a/packages/client/lib/commands/EXPIREAT.ts b/packages/client/lib/commands/EXPIREAT.ts index 84e7760fe7f..9a959a87f99 100644 --- a/packages/client/lib/commands/EXPIREAT.ts +++ b/packages/client/lib/commands/EXPIREAT.ts @@ -1,24 +1,21 @@ -import { RedisCommandArgument, RedisCommandArguments } from '.'; +import { RedisArgument, NumberReply, Command } from '../RESP/types'; import { transformEXAT } from './generic-transformers'; -export const FIRST_KEY_INDEX = 1; - -export function transformArguments( - key: RedisCommandArgument, +export default { + FIRST_KEY_INDEX: 1, + IS_READ_ONLY: true, + transformArguments( + key: RedisArgument, timestamp: number | Date, mode?: 'NX' | 'XX' | 'GT' | 'LT' -): RedisCommandArguments { - const args = [ - 'EXPIREAT', - key, - transformEXAT(timestamp) - ]; + ) { + const args = ['EXPIREAT', key, transformEXAT(timestamp)]; if (mode) { - args.push(mode); + args.push(mode); } return args; -} - -export { transformBooleanReply as transformReply } from './generic-transformers'; + }, + transformReply: undefined as unknown as () => NumberReply +} as const satisfies Command; diff --git a/packages/client/lib/commands/EXPIRETIME.spec.ts b/packages/client/lib/commands/EXPIRETIME.spec.ts index 1d63e759a5d..3c202d2427f 100644 --- a/packages/client/lib/commands/EXPIRETIME.spec.ts +++ b/packages/client/lib/commands/EXPIRETIME.spec.ts @@ -1,21 +1,24 @@ -import { strict as assert } from 'assert'; +import { strict as assert } from 'node:assert'; import testUtils, { GLOBAL } from '../test-utils'; -import { transformArguments } from './EXPIRETIME'; +import EXPIRETIME from './EXPIRETIME'; describe('EXPIRETIME', () => { - testUtils.isVersionGreaterThanHook([7]); + testUtils.isVersionGreaterThanHook([7]); - it('transformArguments', () => { - assert.deepEqual( - transformArguments('key'), - ['EXPIRETIME', 'key'] - ); - }); + it('transformArguments', () => { + assert.deepEqual( + EXPIRETIME.transformArguments('key'), + ['EXPIRETIME', 'key'] + ); + }); - testUtils.testWithClient('client.expireTime', async client => { - assert.equal( - await client.expireTime('key'), - -2 - ); - }, GLOBAL.SERVERS.OPEN); + testUtils.testAll('expireTime', async client => { + assert.equal( + await client.expireTime('key'), + -2 + ); + }, { + client: GLOBAL.SERVERS.OPEN, + cluster: GLOBAL.CLUSTERS.OPEN + }); }); diff --git a/packages/client/lib/commands/EXPIRETIME.ts b/packages/client/lib/commands/EXPIRETIME.ts index 8c1bb075995..d6ac35aeb3d 100644 --- a/packages/client/lib/commands/EXPIRETIME.ts +++ b/packages/client/lib/commands/EXPIRETIME.ts @@ -1,9 +1,10 @@ -import { RedisCommandArgument, RedisCommandArguments } from '.'; +import { RedisArgument, NumberReply, Command } from '../RESP/types'; -export const FIRST_KEY_INDEX = 1; - -export function transformArguments(key: RedisCommandArgument): RedisCommandArguments { +export default { + FIRST_KEY_INDEX: 1, + IS_READ_ONLY: true, + transformArguments(key: RedisArgument) { return ['EXPIRETIME', key]; -} - -export declare function transformReply(): number; + }, + transformReply: undefined as unknown as () => NumberReply +} as const satisfies Command; diff --git a/packages/client/lib/commands/FAILOVER.spec.ts b/packages/client/lib/commands/FAILOVER.spec.ts index 16094a0dbc3..96602caff91 100644 --- a/packages/client/lib/commands/FAILOVER.spec.ts +++ b/packages/client/lib/commands/FAILOVER.spec.ts @@ -1,72 +1,72 @@ -import { strict as assert } from 'assert'; -import { transformArguments } from './FAILOVER'; +import { strict as assert } from 'node:assert'; +import FAILOVER from './FAILOVER'; describe('FAILOVER', () => { - describe('transformArguments', () => { - it('simple', () => { - assert.deepEqual( - transformArguments(), - ['FAILOVER'] - ); - }); + describe('transformArguments', () => { + it('simple', () => { + assert.deepEqual( + FAILOVER.transformArguments(), + ['FAILOVER'] + ); + }); - describe('with TO', () => { - it('simple', () => { - assert.deepEqual( - transformArguments({ - TO: { - host: 'host', - port: 6379 - } - }), - ['FAILOVER', 'TO', 'host', '6379'] - ); - }); + describe('with TO', () => { + it('simple', () => { + assert.deepEqual( + FAILOVER.transformArguments({ + TO: { + host: 'host', + port: 6379 + } + }), + ['FAILOVER', 'TO', 'host', '6379'] + ); + }); - it('with FORCE', () => { - assert.deepEqual( - transformArguments({ - TO: { - host: 'host', - port: 6379, - FORCE: true - } - }), - ['FAILOVER', 'TO', 'host', '6379', 'FORCE'] - ); - }); - }); + it('with FORCE', () => { + assert.deepEqual( + FAILOVER.transformArguments({ + TO: { + host: 'host', + port: 6379, + FORCE: true + } + }), + ['FAILOVER', 'TO', 'host', '6379', 'FORCE'] + ); + }); + }); - it('with ABORT', () => { - assert.deepEqual( - transformArguments({ - ABORT: true - }), - ['FAILOVER', 'ABORT'] - ); - }); + it('with ABORT', () => { + assert.deepEqual( + FAILOVER.transformArguments({ + ABORT: true + }), + ['FAILOVER', 'ABORT'] + ); + }); - it('with TIMEOUT', () => { - assert.deepEqual( - transformArguments({ - TIMEOUT: 1 - }), - ['FAILOVER', 'TIMEOUT', '1'] - ); - }); + it('with TIMEOUT', () => { + assert.deepEqual( + FAILOVER.transformArguments({ + TIMEOUT: 1 + }), + ['FAILOVER', 'TIMEOUT', '1'] + ); + }); - it('with TO, ABORT, TIMEOUT', () => { - assert.deepEqual( - transformArguments({ - TO: { - host: 'host', - port: 6379 - }, - ABORT: true, - TIMEOUT: 1 - }), - ['FAILOVER', 'TO', 'host', '6379', 'ABORT', 'TIMEOUT', '1'] - ); - }); + it('with TO, ABORT, TIMEOUT', () => { + assert.deepEqual( + FAILOVER.transformArguments({ + TO: { + host: 'host', + port: 6379 + }, + ABORT: true, + TIMEOUT: 1 + }), + ['FAILOVER', 'TO', 'host', '6379', 'ABORT', 'TIMEOUT', '1'] + ); }); + }); }); diff --git a/packages/client/lib/commands/FAILOVER.ts b/packages/client/lib/commands/FAILOVER.ts index c31dbc063de..0c1e710d321 100644 --- a/packages/client/lib/commands/FAILOVER.ts +++ b/packages/client/lib/commands/FAILOVER.ts @@ -1,33 +1,36 @@ +import { SimpleStringReply, Command } from '../RESP/types'; + interface FailoverOptions { - TO?: { - host: string; - port: number; - FORCE?: true; - }; - ABORT?: true; - TIMEOUT?: number; + TO?: { + host: string; + port: number; + FORCE?: true; + }; + ABORT?: true; + TIMEOUT?: number; } -export function transformArguments(options?: FailoverOptions): Array { +export default { + transformArguments(options?: FailoverOptions) { const args = ['FAILOVER']; if (options?.TO) { - args.push('TO', options.TO.host, options.TO.port.toString()); + args.push('TO', options.TO.host, options.TO.port.toString()); - if (options.TO.FORCE) { - args.push('FORCE'); - } + if (options.TO.FORCE) { + args.push('FORCE'); + } } if (options?.ABORT) { - args.push('ABORT'); + args.push('ABORT'); } if (options?.TIMEOUT) { - args.push('TIMEOUT', options.TIMEOUT.toString()); + args.push('TIMEOUT', options.TIMEOUT.toString()); } return args; -} - -export declare function transformReply(): string; + }, + transformReply: undefined as unknown as () => SimpleStringReply +} as const satisfies Command; diff --git a/packages/client/lib/commands/FCALL.spec.ts b/packages/client/lib/commands/FCALL.spec.ts index fd29f07527d..286f2a371bf 100644 --- a/packages/client/lib/commands/FCALL.spec.ts +++ b/packages/client/lib/commands/FCALL.spec.ts @@ -1,29 +1,30 @@ -import { strict as assert } from 'assert'; +import { strict as assert } from 'node:assert'; import testUtils, { GLOBAL } from '../test-utils'; -import { MATH_FUNCTION, loadMathFunction } from '../client/index.spec'; -import { transformArguments } from './FCALL'; +import { MATH_FUNCTION, loadMathFunction } from './FUNCTION_LOAD.spec'; +import FCALL from './FCALL'; describe('FCALL', () => { - testUtils.isVersionGreaterThanHook([7]); + testUtils.isVersionGreaterThanHook([7]); - it('transformArguments', () => { - assert.deepEqual( - transformArguments('function', { - keys: ['key'], - arguments: ['argument'] - }), - ['FCALL', 'function', '1', 'key', 'argument'] - ); - }); + it('transformArguments', () => { + assert.deepEqual( + FCALL.transformArguments('function', { + keys: ['key'], + arguments: ['argument'] + }), + ['FCALL', 'function', '1', 'key', 'argument'] + ); + }); - testUtils.testWithClient('client.fCall', async client => { - await loadMathFunction(client); + testUtils.testWithClient('client.fCall', async client => { + const [,, reply] = await Promise.all([ + loadMathFunction(client), + client.set('key', '2'), + client.fCall(MATH_FUNCTION.library.square.NAME, { + keys: ['key'] + }) + ]); - assert.equal( - await client.fCall(MATH_FUNCTION.library.square.NAME, { - arguments: ['2'] - }), - 4 - ); - }, GLOBAL.SERVERS.OPEN); + assert.equal(reply, 4); + }, GLOBAL.SERVERS.OPEN); }); diff --git a/packages/client/lib/commands/FCALL.ts b/packages/client/lib/commands/FCALL.ts index a4cadedb6f9..ba371a81b13 100644 --- a/packages/client/lib/commands/FCALL.ts +++ b/packages/client/lib/commands/FCALL.ts @@ -1,7 +1,9 @@ -import { evalFirstKeyIndex, EvalOptions, pushEvalArguments } from './generic-transformers'; +import { Command } from '../RESP/types'; +import EVAL, { transformEvalArguments } from './EVAL'; -export const FIRST_KEY_INDEX = evalFirstKeyIndex; - -export function transformArguments(fn: string, options?: EvalOptions): Array { - return pushEvalArguments(['FCALL', fn], options); -} +export default { + FIRST_KEY_INDEX: EVAL.FIRST_KEY_INDEX, + IS_READ_ONLY: false, + transformArguments: transformEvalArguments.bind(undefined, 'FCALL'), + transformReply: EVAL.transformReply +} as const satisfies Command; diff --git a/packages/client/lib/commands/FCALL_RO.spec.ts b/packages/client/lib/commands/FCALL_RO.spec.ts index 18665f92aa6..57edcebebef 100644 --- a/packages/client/lib/commands/FCALL_RO.spec.ts +++ b/packages/client/lib/commands/FCALL_RO.spec.ts @@ -1,29 +1,30 @@ -import { strict as assert } from 'assert'; +import { strict as assert } from 'node:assert'; import testUtils, { GLOBAL } from '../test-utils'; -import { MATH_FUNCTION, loadMathFunction } from '../client/index.spec'; -import { transformArguments } from './FCALL_RO'; +import { MATH_FUNCTION, loadMathFunction } from './FUNCTION_LOAD.spec'; +import FCALL_RO from './FCALL_RO'; describe('FCALL_RO', () => { - testUtils.isVersionGreaterThanHook([7]); + testUtils.isVersionGreaterThanHook([7]); - it('transformArguments', () => { - assert.deepEqual( - transformArguments('function', { - keys: ['key'], - arguments: ['argument'] - }), - ['FCALL_RO', 'function', '1', 'key', 'argument'] - ); - }); + it('transformArguments', () => { + assert.deepEqual( + FCALL_RO.transformArguments('function', { + keys: ['key'], + arguments: ['argument'] + }), + ['FCALL_RO', 'function', '1', 'key', 'argument'] + ); + }); - testUtils.testWithClient('client.fCallRo', async client => { - await loadMathFunction(client); + testUtils.testWithClient('client.fCallRo', async client => { + const [,, reply] = await Promise.all([ + loadMathFunction(client), + client.set('key', '2'), + client.fCallRo(MATH_FUNCTION.library.square.NAME, { + keys: ['key'] + }) + ]); - assert.equal( - await client.fCallRo(MATH_FUNCTION.library.square.NAME, { - arguments: ['2'] - }), - 4 - ); - }, GLOBAL.SERVERS.OPEN); + assert.equal(reply, 4); + }, GLOBAL.SERVERS.OPEN); }); diff --git a/packages/client/lib/commands/FCALL_RO.ts b/packages/client/lib/commands/FCALL_RO.ts index 66b79aa8833..ec002a79f82 100644 --- a/packages/client/lib/commands/FCALL_RO.ts +++ b/packages/client/lib/commands/FCALL_RO.ts @@ -1,9 +1,9 @@ -import { evalFirstKeyIndex, EvalOptions, pushEvalArguments } from './generic-transformers'; +import { Command } from '../RESP/types'; +import EVAL, { transformEvalArguments } from './EVAL'; -export const FIRST_KEY_INDEX = evalFirstKeyIndex; - -export const IS_READ_ONLY = true; - -export function transformArguments(fn: string, options?: EvalOptions): Array { - return pushEvalArguments(['FCALL_RO', fn], options); -} +export default { + FIRST_KEY_INDEX: EVAL.FIRST_KEY_INDEX, + IS_READ_ONLY: false, + transformArguments: transformEvalArguments.bind(undefined, 'FCALL_RO'), + transformReply: EVAL.transformReply +} as const satisfies Command; diff --git a/packages/client/lib/commands/FLUSHALL.spec.ts b/packages/client/lib/commands/FLUSHALL.spec.ts index db5bb72e9cb..63ad38dd7de 100644 --- a/packages/client/lib/commands/FLUSHALL.spec.ts +++ b/packages/client/lib/commands/FLUSHALL.spec.ts @@ -1,35 +1,35 @@ -import { strict as assert } from 'assert'; +import { strict as assert } from 'node:assert'; import testUtils, { GLOBAL } from '../test-utils'; -import { RedisFlushModes, transformArguments } from './FLUSHALL'; +import FLUSHALL, { REDIS_FLUSH_MODES } from './FLUSHALL'; describe('FLUSHALL', () => { - describe('transformArguments', () => { - it('default', () => { - assert.deepEqual( - transformArguments(), - ['FLUSHALL'] - ); - }); + describe('transformArguments', () => { + it('default', () => { + assert.deepEqual( + FLUSHALL.transformArguments(), + ['FLUSHALL'] + ); + }); - it('ASYNC', () => { - assert.deepEqual( - transformArguments(RedisFlushModes.ASYNC), - ['FLUSHALL', 'ASYNC'] - ); - }); + it('ASYNC', () => { + assert.deepEqual( + FLUSHALL.transformArguments(REDIS_FLUSH_MODES.ASYNC), + ['FLUSHALL', 'ASYNC'] + ); + }); - it('SYNC', () => { - assert.deepEqual( - transformArguments(RedisFlushModes.SYNC), - ['FLUSHALL', 'SYNC'] - ); - }); + it('SYNC', () => { + assert.deepEqual( + FLUSHALL.transformArguments(REDIS_FLUSH_MODES.SYNC), + ['FLUSHALL', 'SYNC'] + ); }); + }); - testUtils.testWithClient('client.flushAll', async client => { - assert.equal( - await client.flushAll(), - 'OK' - ); - }, GLOBAL.SERVERS.OPEN); + testUtils.testWithClient('client.flushAll', async client => { + assert.equal( + await client.flushAll(), + 'OK' + ); + }, GLOBAL.SERVERS.OPEN); }); diff --git a/packages/client/lib/commands/FLUSHALL.ts b/packages/client/lib/commands/FLUSHALL.ts index 967096bb9bd..5e6484a991e 100644 --- a/packages/client/lib/commands/FLUSHALL.ts +++ b/packages/client/lib/commands/FLUSHALL.ts @@ -1,16 +1,23 @@ -export enum RedisFlushModes { - ASYNC = 'ASYNC', - SYNC = 'SYNC' -} +import { SimpleStringReply, Command } from '../RESP/types'; -export function transformArguments(mode?: RedisFlushModes): Array { - const args = ['FLUSHALL']; +export const REDIS_FLUSH_MODES = { + ASYNC: 'ASYNC', + SYNC: 'SYNC' +} as const; + +export type RedisFlushMode = typeof REDIS_FLUSH_MODES[keyof typeof REDIS_FLUSH_MODES]; +export default { + FIRST_KEY_INDEX: undefined, + IS_READ_ONLY: false, + transformArguments(mode?: RedisFlushMode) { + const args = ['FLUSHALL']; + if (mode) { - args.push(mode); + args.push(mode); } return args; -} - -export declare function transformReply(): string; + }, + transformReply: undefined as unknown as () => SimpleStringReply +} as const satisfies Command; diff --git a/packages/client/lib/commands/FLUSHDB.spec.ts b/packages/client/lib/commands/FLUSHDB.spec.ts index bf460e9e7a8..ad09ecfc945 100644 --- a/packages/client/lib/commands/FLUSHDB.spec.ts +++ b/packages/client/lib/commands/FLUSHDB.spec.ts @@ -1,36 +1,36 @@ -import { strict as assert } from 'assert'; +import { strict as assert } from 'node:assert'; import testUtils, { GLOBAL } from '../test-utils'; -import { RedisFlushModes } from './FLUSHALL'; -import { transformArguments } from './FLUSHDB'; +import FLUSHDB from './FLUSHDB'; +import { REDIS_FLUSH_MODES } from './FLUSHALL'; describe('FLUSHDB', () => { - describe('transformArguments', () => { - it('default', () => { - assert.deepEqual( - transformArguments(), - ['FLUSHDB'] - ); - }); + describe('transformArguments', () => { + it('default', () => { + assert.deepEqual( + FLUSHDB.transformArguments(), + ['FLUSHDB'] + ); + }); - it('ASYNC', () => { - assert.deepEqual( - transformArguments(RedisFlushModes.ASYNC), - ['FLUSHDB', 'ASYNC'] - ); - }); + it('ASYNC', () => { + assert.deepEqual( + FLUSHDB.transformArguments(REDIS_FLUSH_MODES.ASYNC), + ['FLUSHDB', 'ASYNC'] + ); + }); - it('SYNC', () => { - assert.deepEqual( - transformArguments(RedisFlushModes.SYNC), - ['FLUSHDB', 'SYNC'] - ); - }); + it('SYNC', () => { + assert.deepEqual( + FLUSHDB.transformArguments(REDIS_FLUSH_MODES.SYNC), + ['FLUSHDB', 'SYNC'] + ); }); + }); - testUtils.testWithClient('client.flushDb', async client => { - assert.equal( - await client.flushDb(), - 'OK' - ); - }, GLOBAL.SERVERS.OPEN); + testUtils.testWithClient('client.flushDb', async client => { + assert.equal( + await client.flushDb(), + 'OK' + ); + }, GLOBAL.SERVERS.OPEN); }); diff --git a/packages/client/lib/commands/FLUSHDB.ts b/packages/client/lib/commands/FLUSHDB.ts index 5b8060df9eb..75c7a66f190 100644 --- a/packages/client/lib/commands/FLUSHDB.ts +++ b/packages/client/lib/commands/FLUSHDB.ts @@ -1,13 +1,17 @@ -import { RedisFlushModes } from './FLUSHALL'; +import { SimpleStringReply, Command } from '../RESP/types'; +import { RedisFlushMode } from './FLUSHALL'; -export function transformArguments(mode?: RedisFlushModes): Array { +export default { + FIRST_KEY_INDEX: undefined, + IS_READ_ONLY: false, + transformArguments(mode?: RedisFlushMode) { const args = ['FLUSHDB']; - + if (mode) { - args.push(mode); + args.push(mode); } return args; -} - -export declare function transformReply(): string; + }, + transformReply: undefined as unknown as () => SimpleStringReply +} as const satisfies Command; diff --git a/packages/client/lib/commands/FUNCTION_DELETE.spec.ts b/packages/client/lib/commands/FUNCTION_DELETE.spec.ts index 563b9aa0a58..1172e84b956 100644 --- a/packages/client/lib/commands/FUNCTION_DELETE.spec.ts +++ b/packages/client/lib/commands/FUNCTION_DELETE.spec.ts @@ -1,24 +1,24 @@ -import { strict as assert } from 'assert'; -import { MATH_FUNCTION, loadMathFunction } from '../client/index.spec'; +import { strict as assert } from 'node:assert'; import testUtils, { GLOBAL } from '../test-utils'; -import { transformArguments } from './FUNCTION_DELETE'; +import FUNCTION_DELETE from './FUNCTION_DELETE'; +import { MATH_FUNCTION, loadMathFunction } from './FUNCTION_LOAD.spec'; describe('FUNCTION DELETE', () => { - testUtils.isVersionGreaterThanHook([7]); + testUtils.isVersionGreaterThanHook([7]); - it('transformArguments', () => { - assert.deepEqual( - transformArguments('library'), - ['FUNCTION', 'DELETE', 'library'] - ); - }); + it('transformArguments', () => { + assert.deepEqual( + FUNCTION_DELETE.transformArguments('library'), + ['FUNCTION', 'DELETE', 'library'] + ); + }); - testUtils.testWithClient('client.functionDelete', async client => { - await loadMathFunction(client); + testUtils.testWithClient('client.functionDelete', async client => { + await loadMathFunction(client); - assert.equal( - await client.functionDelete(MATH_FUNCTION.name), - 'OK' - ); - }, GLOBAL.SERVERS.OPEN); + assert.equal( + await client.functionDelete(MATH_FUNCTION.name), + 'OK' + ); + }, GLOBAL.SERVERS.OPEN); }); diff --git a/packages/client/lib/commands/FUNCTION_DELETE.ts b/packages/client/lib/commands/FUNCTION_DELETE.ts index 4aa6be40e12..1061cded17c 100644 --- a/packages/client/lib/commands/FUNCTION_DELETE.ts +++ b/packages/client/lib/commands/FUNCTION_DELETE.ts @@ -1,7 +1,10 @@ -import { RedisCommandArguments } from '.'; +import { RedisArgument, SimpleStringReply, Command } from '../RESP/types'; -export function transformArguments(library: string): RedisCommandArguments { +export default { + FIRST_KEY_INDEX: undefined, + IS_READ_ONLY: false, + transformArguments(library: RedisArgument) { return ['FUNCTION', 'DELETE', library]; -} - -export declare function transformReply(): 'OK'; + }, + transformReply: undefined as unknown as () => SimpleStringReply +} as const satisfies Command; diff --git a/packages/client/lib/commands/FUNCTION_DUMP.spec.ts b/packages/client/lib/commands/FUNCTION_DUMP.spec.ts index 360ec6b7453..4d4e885e4f2 100644 --- a/packages/client/lib/commands/FUNCTION_DUMP.spec.ts +++ b/packages/client/lib/commands/FUNCTION_DUMP.spec.ts @@ -1,21 +1,21 @@ -import { strict as assert } from 'assert'; +import { strict as assert } from 'node:assert'; import testUtils, { GLOBAL } from '../test-utils'; -import { transformArguments } from './FUNCTION_DUMP'; +import FUNCTION_DUMP from './FUNCTION_DUMP'; describe('FUNCTION DUMP', () => { - testUtils.isVersionGreaterThanHook([7]); + testUtils.isVersionGreaterThanHook([7]); - it('transformArguments', () => { - assert.deepEqual( - transformArguments(), - ['FUNCTION', 'DUMP'] - ); - }); + it('transformArguments', () => { + assert.deepEqual( + FUNCTION_DUMP.transformArguments(), + ['FUNCTION', 'DUMP'] + ); + }); - testUtils.testWithClient('client.functionDump', async client => { - assert.equal( - typeof await client.functionDump(), - 'string' - ); - }, GLOBAL.SERVERS.OPEN); + testUtils.testWithClient('client.functionDump', async client => { + assert.equal( + typeof await client.functionDump(), + 'string' + ); + }, GLOBAL.SERVERS.OPEN); }); diff --git a/packages/client/lib/commands/FUNCTION_DUMP.ts b/packages/client/lib/commands/FUNCTION_DUMP.ts index f608e078c27..8f6ff047fa7 100644 --- a/packages/client/lib/commands/FUNCTION_DUMP.ts +++ b/packages/client/lib/commands/FUNCTION_DUMP.ts @@ -1,7 +1,10 @@ -import { RedisCommandArgument, RedisCommandArguments } from '.'; +import { BlobStringReply, Command } from '../RESP/types'; -export function transformArguments(): RedisCommandArguments { +export default { + FIRST_KEY_INDEX: undefined, + IS_READ_ONLY: true, + transformArguments() { return ['FUNCTION', 'DUMP']; -} - -export declare function transformReply(): RedisCommandArgument; + }, + transformReply: undefined as unknown as () => BlobStringReply +} as const satisfies Command; diff --git a/packages/client/lib/commands/FUNCTION_FLUSH.spec.ts b/packages/client/lib/commands/FUNCTION_FLUSH.spec.ts index 12009d03363..5601784ed6a 100644 --- a/packages/client/lib/commands/FUNCTION_FLUSH.spec.ts +++ b/packages/client/lib/commands/FUNCTION_FLUSH.spec.ts @@ -1,30 +1,30 @@ -import { strict as assert } from 'assert'; +import { strict as assert } from 'node:assert'; import testUtils, { GLOBAL } from '../test-utils'; -import { transformArguments } from './FUNCTION_FLUSH'; +import FUNCTION_FLUSH from './FUNCTION_FLUSH'; describe('FUNCTION FLUSH', () => { - testUtils.isVersionGreaterThanHook([7]); + testUtils.isVersionGreaterThanHook([7]); - describe('transformArguments', () => { - it('simple', () => { - assert.deepEqual( - transformArguments(), - ['FUNCTION', 'FLUSH'] - ); - }); + describe('transformArguments', () => { + it('simple', () => { + assert.deepEqual( + FUNCTION_FLUSH.transformArguments(), + ['FUNCTION', 'FLUSH'] + ); + }); - it('with mode', () => { - assert.deepEqual( - transformArguments('SYNC'), - ['FUNCTION', 'FLUSH', 'SYNC'] - ); - }); + it('with mode', () => { + assert.deepEqual( + FUNCTION_FLUSH.transformArguments('SYNC'), + ['FUNCTION', 'FLUSH', 'SYNC'] + ); }); + }); - testUtils.testWithClient('client.functionFlush', async client => { - assert.equal( - await client.functionFlush(), - 'OK' - ); - }, GLOBAL.SERVERS.OPEN); + testUtils.testWithClient('client.functionFlush', async client => { + assert.equal( + await client.functionFlush(), + 'OK' + ); + }, GLOBAL.SERVERS.OPEN); }); diff --git a/packages/client/lib/commands/FUNCTION_FLUSH.ts b/packages/client/lib/commands/FUNCTION_FLUSH.ts index 143282de97f..844d3586d90 100644 --- a/packages/client/lib/commands/FUNCTION_FLUSH.ts +++ b/packages/client/lib/commands/FUNCTION_FLUSH.ts @@ -1,13 +1,17 @@ -import { RedisCommandArguments } from '.'; +import { SimpleStringReply, Command } from '../RESP/types'; +import { RedisFlushMode } from './FLUSHALL'; -export function transformArguments(mode?: 'ASYNC' | 'SYNC'): RedisCommandArguments { +export default { + FIRST_KEY_INDEX: undefined, + IS_READ_ONLY: false, + transformArguments(mode?: RedisFlushMode) { const args = ['FUNCTION', 'FLUSH']; - + if (mode) { - args.push(mode); + args.push(mode); } return args; -} - -export declare function transformReply(): 'OK'; + }, + transformReply: undefined as unknown as () => SimpleStringReply<'OK'> +} as const satisfies Command; diff --git a/packages/client/lib/commands/FUNCTION_KILL.spec.ts b/packages/client/lib/commands/FUNCTION_KILL.spec.ts index df4848fc82e..be231e41180 100644 --- a/packages/client/lib/commands/FUNCTION_KILL.spec.ts +++ b/packages/client/lib/commands/FUNCTION_KILL.spec.ts @@ -1,14 +1,14 @@ -import { strict as assert } from 'assert'; +import { strict as assert } from 'node:assert'; import testUtils from '../test-utils'; -import { transformArguments } from './FUNCTION_KILL'; +import FUNCTION_KILL from './FUNCTION_KILL'; describe('FUNCTION KILL', () => { - testUtils.isVersionGreaterThanHook([7]); + testUtils.isVersionGreaterThanHook([7]); - it('transformArguments', () => { - assert.deepEqual( - transformArguments(), - ['FUNCTION', 'KILL'] - ); - }); + it('transformArguments', () => { + assert.deepEqual( + FUNCTION_KILL.transformArguments(), + ['FUNCTION', 'KILL'] + ); + }); }); diff --git a/packages/client/lib/commands/FUNCTION_KILL.ts b/packages/client/lib/commands/FUNCTION_KILL.ts index 517272e8376..f452b0b80d9 100644 --- a/packages/client/lib/commands/FUNCTION_KILL.ts +++ b/packages/client/lib/commands/FUNCTION_KILL.ts @@ -1,7 +1,10 @@ -import { RedisCommandArguments } from '.'; +import { SimpleStringReply, Command } from '../RESP/types'; -export function transformArguments(): RedisCommandArguments { +export default { + FIRST_KEY_INDEX: undefined, + IS_READ_ONLY: true, + transformArguments() { return ['FUNCTION', 'KILL']; -} - -export declare function transformReply(): 'OK'; + }, + transformReply: undefined as unknown as () => SimpleStringReply +} as const satisfies Command; diff --git a/packages/client/lib/commands/FUNCTION_LIST.spec.ts b/packages/client/lib/commands/FUNCTION_LIST.spec.ts index 80723d070de..e269d3150b6 100644 --- a/packages/client/lib/commands/FUNCTION_LIST.spec.ts +++ b/packages/client/lib/commands/FUNCTION_LIST.spec.ts @@ -1,41 +1,45 @@ -import { strict as assert } from 'assert'; +import { strict as assert } from 'node:assert'; import testUtils, { GLOBAL } from '../test-utils'; -import { MATH_FUNCTION, loadMathFunction } from '../client/index.spec'; -import { transformArguments } from './FUNCTION_LIST'; +import FUNCTION_LIST from './FUNCTION_LIST'; +import { MATH_FUNCTION, loadMathFunction } from './FUNCTION_LOAD.spec'; describe('FUNCTION LIST', () => { - testUtils.isVersionGreaterThanHook([7]); + testUtils.isVersionGreaterThanHook([7]); - describe('transformArguments', () => { - it('simple', () => { - assert.deepEqual( - transformArguments(), - ['FUNCTION', 'LIST'] - ); - }); + describe('transformArguments', () => { + it('simple', () => { + assert.deepEqual( + FUNCTION_LIST.transformArguments(), + ['FUNCTION', 'LIST'] + ); + }); - it('with pattern', () => { - assert.deepEqual( - transformArguments('patter*'), - ['FUNCTION', 'LIST', 'patter*'] - ); - }); + it('with LIBRARYNAME', () => { + assert.deepEqual( + FUNCTION_LIST.transformArguments({ + LIBRARYNAME: 'patter*' + }), + ['FUNCTION', 'LIST', 'LIBRARYNAME', 'patter*'] + ); }); + }); + + testUtils.testWithClient('client.functionList', async client => { + const [, reply] = await Promise.all([ + loadMathFunction(client), + client.functionList() + ]); - testUtils.testWithClient('client.functionList', async client => { - await loadMathFunction(client); + reply[0].library_name; - assert.deepEqual( - await client.functionList(), - [{ - libraryName: MATH_FUNCTION.name, - engine: MATH_FUNCTION.engine, - functions: [{ - name: MATH_FUNCTION.library.square.NAME, - description: null, - flags: ['no-writes'] - }] - }] - ); - }, GLOBAL.SERVERS.OPEN); + assert.deepEqual(reply, [{ + library_name: MATH_FUNCTION.name, + engine: MATH_FUNCTION.engine, + functions: [{ + name: MATH_FUNCTION.library.square.NAME, + description: null, + flags: ['no-writes'] + }] + }]); + }, GLOBAL.SERVERS.OPEN); }); diff --git a/packages/client/lib/commands/FUNCTION_LIST.ts b/packages/client/lib/commands/FUNCTION_LIST.ts index d6a39dc726d..07c1ff2a000 100644 --- a/packages/client/lib/commands/FUNCTION_LIST.ts +++ b/packages/client/lib/commands/FUNCTION_LIST.ts @@ -1,16 +1,51 @@ -import { RedisCommandArguments } from '.'; -import { FunctionListItemReply, FunctionListRawItemReply, transformFunctionListItemReply } from './generic-transformers'; +import { RedisArgument, TuplesToMapReply, BlobStringReply, ArrayReply, NullReply, SetReply, UnwrapReply, Resp2Reply, CommandArguments, Command } from '../RESP/types'; -export function transformArguments(pattern?: string): RedisCommandArguments { - const args = ['FUNCTION', 'LIST']; +export interface FunctionListOptions { + LIBRARYNAME?: RedisArgument; +} + +export type FunctionListReplyItem = [ + [BlobStringReply<'library_name'>, BlobStringReply | NullReply], + [BlobStringReply<'engine'>, BlobStringReply], + [BlobStringReply<'functions'>, ArrayReply, BlobStringReply], + [BlobStringReply<'description'>, BlobStringReply | NullReply], + [BlobStringReply<'flags'>, SetReply], + ]>>] +]; + +export type FunctionListReply = ArrayReply>; + +export default { + FIRST_KEY_INDEX: undefined, + IS_READ_ONLY: false, + transformArguments(options?: FunctionListOptions) { + const args: CommandArguments = ['FUNCTION', 'LIST']; - if (pattern) { - args.push(pattern); + if (options?.LIBRARYNAME) { + args.push('LIBRARYNAME', options.LIBRARYNAME); } return args; -} - -export function transformReply(reply: Array): Array { - return reply.map(transformFunctionListItemReply); -} + }, + transformReply: { + 2: (reply: UnwrapReply>) => { + return reply.map(library => { + const unwrapped = library as unknown as UnwrapReply; + return { + library_name: unwrapped[1], + engine: unwrapped[3], + functions: (unwrapped[5] as unknown as UnwrapReply).map(fn => { + const unwrapped = fn as unknown as UnwrapReply; + return { + name: unwrapped[1], + description: unwrapped[3], + flags: unwrapped[5] + }; + }) + }; + }); + }, + 3: undefined as unknown as () => FunctionListReply + } +} as const satisfies Command; diff --git a/packages/client/lib/commands/FUNCTION_LIST_WITHCODE.spec.ts b/packages/client/lib/commands/FUNCTION_LIST_WITHCODE.spec.ts index 56e6102a4b4..8ff40582460 100644 --- a/packages/client/lib/commands/FUNCTION_LIST_WITHCODE.spec.ts +++ b/packages/client/lib/commands/FUNCTION_LIST_WITHCODE.spec.ts @@ -1,42 +1,48 @@ -import { strict as assert } from 'assert'; +import { strict as assert } from 'node:assert'; import testUtils, { GLOBAL } from '../test-utils'; -import { MATH_FUNCTION, loadMathFunction } from '../client/index.spec'; -import { transformArguments } from './FUNCTION_LIST_WITHCODE'; +import FUNCTION_LIST_WITHCODE from './FUNCTION_LIST_WITHCODE'; +import { MATH_FUNCTION, loadMathFunction } from './FUNCTION_LOAD.spec'; describe('FUNCTION LIST WITHCODE', () => { - testUtils.isVersionGreaterThanHook([7]); + testUtils.isVersionGreaterThanHook([7]); - describe('transformArguments', () => { - it('simple', () => { - assert.deepEqual( - transformArguments(), - ['FUNCTION', 'LIST', 'WITHCODE'] - ); - }); + describe('transformArguments', () => { + it('simple', () => { + assert.deepEqual( + FUNCTION_LIST_WITHCODE.transformArguments(), + ['FUNCTION', 'LIST', 'WITHCODE'] + ); + }); - it('with pattern', () => { - assert.deepEqual( - transformArguments('patter*'), - ['FUNCTION', 'LIST', 'patter*', 'WITHCODE'] - ); - }); + it('with LIBRARYNAME', () => { + assert.deepEqual( + FUNCTION_LIST_WITHCODE.transformArguments({ + LIBRARYNAME: 'patter*' + }), + ['FUNCTION', 'LIST', 'LIBRARYNAME', 'patter*', 'WITHCODE'] + ); }); + }); + + testUtils.testWithClient('client.functionListWithCode', async client => { + const [, reply] = await Promise.all([ + loadMathFunction(client), + client.functionListWithCode() + ]); - testUtils.testWithClient('client.functionListWithCode', async client => { - await loadMathFunction(client); + const a = reply[0]; - assert.deepEqual( - await client.functionListWithCode(), - [{ - libraryName: MATH_FUNCTION.name, - engine: MATH_FUNCTION.engine, - functions: [{ - name: MATH_FUNCTION.library.square.NAME, - description: null, - flags: ['no-writes'] - }], - libraryCode: MATH_FUNCTION.code - }] - ); - }, GLOBAL.SERVERS.OPEN); + const b = a.functions[0].description; + + assert.deepEqual(reply, [{ + library_name: MATH_FUNCTION.name, + engine: MATH_FUNCTION.engine, + functions: [{ + name: MATH_FUNCTION.library.square.NAME, + description: null, + flags: ['no-writes'] + }], + library_code: MATH_FUNCTION.code + }]); + }, GLOBAL.SERVERS.OPEN); }); diff --git a/packages/client/lib/commands/FUNCTION_LIST_WITHCODE.ts b/packages/client/lib/commands/FUNCTION_LIST_WITHCODE.ts index 0d763301e87..47a02a3da8a 100644 --- a/packages/client/lib/commands/FUNCTION_LIST_WITHCODE.ts +++ b/packages/client/lib/commands/FUNCTION_LIST_WITHCODE.ts @@ -1,26 +1,38 @@ -import { RedisCommandArguments } from '.'; -import { transformArguments as transformFunctionListArguments } from './FUNCTION_LIST'; -import { FunctionListItemReply, FunctionListRawItemReply, transformFunctionListItemReply } from './generic-transformers'; +import { TuplesToMapReply, BlobStringReply, ArrayReply, UnwrapReply, Resp2Reply, Command } from '../RESP/types'; +import FUNCTION_LIST, { FunctionListReplyItem } from './FUNCTION_LIST'; -export function transformArguments(pattern?: string): RedisCommandArguments { - const args = transformFunctionListArguments(pattern); - args.push('WITHCODE'); - return args; -} +export type FunctionListWithCodeReply = ArrayReply, BlobStringReply], +]>>; -type FunctionListWithCodeRawItemReply = [ - ...FunctionListRawItemReply, - 'library_code', - string -]; - -interface FunctionListWithCodeItemReply extends FunctionListItemReply { - libraryCode: string; -} - -export function transformReply(reply: Array): Array { - return reply.map(library => ({ - ...transformFunctionListItemReply(library as unknown as FunctionListRawItemReply), - libraryCode: library[7] - })); -} +export default { + FIRST_KEY_INDEX: FUNCTION_LIST.FIRST_KEY_INDEX, + IS_READ_ONLY: FUNCTION_LIST.IS_READ_ONLY, + transformArguments(...args: Parameters) { + const redisArgs = FUNCTION_LIST.transformArguments(...args); + redisArgs.push('WITHCODE'); + return redisArgs; + }, + transformReply: { + 2: (reply: UnwrapReply>) => { + return reply.map(library => { + const unwrapped = library as unknown as UnwrapReply; + return { + library_name: unwrapped[1], + engine: unwrapped[3], + functions: (unwrapped[5] as unknown as UnwrapReply).map(fn => { + const unwrapped = fn as unknown as UnwrapReply; + return { + name: unwrapped[1], + description: unwrapped[3], + flags: unwrapped[5] + }; + }), + library_code: unwrapped[7] + }; + }); + }, + 3: undefined as unknown as () => FunctionListWithCodeReply + } +} as const satisfies Command; diff --git a/packages/client/lib/commands/FUNCTION_LOAD.spec.ts b/packages/client/lib/commands/FUNCTION_LOAD.spec.ts index 7be371c6b9c..fe896bdf8c8 100644 --- a/packages/client/lib/commands/FUNCTION_LOAD.spec.ts +++ b/packages/client/lib/commands/FUNCTION_LOAD.spec.ts @@ -1,36 +1,77 @@ -import { strict as assert } from 'assert'; +import { strict as assert } from 'node:assert'; import testUtils, { GLOBAL } from '../test-utils'; -import { MATH_FUNCTION } from '../client/index.spec'; -import { transformArguments } from './FUNCTION_LOAD'; +import FUNCTION_LOAD from './FUNCTION_LOAD'; +import { RedisClientType } from '../client'; +import { NumberReply, RedisFunctions, RedisModules, RedisScripts, RespVersions } from '../RESP/types'; + + + +export const MATH_FUNCTION = { + name: 'math', + engine: 'LUA', + code: + `#!LUA name=math + redis.register_function { + function_name = "square", + callback = function(keys, args) + local number = redis.call('GET', keys[1]) + return number * number + end, + flags = { "no-writes" } + }`, + library: { + square: { + NAME: 'square', + IS_READ_ONLY: true, + NUMBER_OF_KEYS: 1, + FIRST_KEY_INDEX: 0, + transformArguments(key: string) { + return [key]; + }, + transformReply: undefined as unknown as () => NumberReply + } + } +}; + +export function loadMathFunction< + M extends RedisModules, + F extends RedisFunctions, + S extends RedisScripts, + RESP extends RespVersions +>( + client: RedisClientType +) { + return client.functionLoad( + MATH_FUNCTION.code, + { REPLACE: true } + ); +} describe('FUNCTION LOAD', () => { - testUtils.isVersionGreaterThanHook([7]); - - describe('transformArguments', () => { - it('simple', () => { - assert.deepEqual( - transformArguments( 'code'), - ['FUNCTION', 'LOAD', 'code'] - ); - }); - - it('with REPLACE', () => { - assert.deepEqual( - transformArguments('code', { - REPLACE: true - }), - ['FUNCTION', 'LOAD', 'REPLACE', 'code'] - ); - }); + testUtils.isVersionGreaterThanHook([7]); + + describe('transformArguments', () => { + it('simple', () => { + assert.deepEqual( + FUNCTION_LOAD.transformArguments('code'), + ['FUNCTION', 'LOAD', 'code'] + ); + }); + + it('with REPLACE', () => { + assert.deepEqual( + FUNCTION_LOAD.transformArguments('code', { + REPLACE: true + }), + ['FUNCTION', 'LOAD', 'REPLACE', 'code'] + ); }); + }); - testUtils.testWithClient('client.functionLoad', async client => { - assert.equal( - await client.functionLoad( - MATH_FUNCTION.code, - { REPLACE: true } - ), - MATH_FUNCTION.name - ); - }, GLOBAL.SERVERS.OPEN); + testUtils.testWithClient('client.functionLoad', async client => { + assert.equal( + await loadMathFunction(client), + MATH_FUNCTION.name + ); + }, GLOBAL.SERVERS.OPEN); }); diff --git a/packages/client/lib/commands/FUNCTION_LOAD.ts b/packages/client/lib/commands/FUNCTION_LOAD.ts index 7ab58d58598..32b030909ad 100644 --- a/packages/client/lib/commands/FUNCTION_LOAD.ts +++ b/packages/client/lib/commands/FUNCTION_LOAD.ts @@ -1,22 +1,22 @@ -import { RedisCommandArguments } from '.'; +import { RedisArgument, CommandArguments, BlobStringReply, Command } from '../RESP/types'; -interface FunctionLoadOptions { - REPLACE?: boolean; +export interface FunctionLoadOptions { + REPLACE?: boolean; } -export function transformArguments( - code: string, - options?: FunctionLoadOptions -): RedisCommandArguments { - const args = ['FUNCTION', 'LOAD']; +export default { + FIRST_KEY_INDEX: undefined, + IS_READ_ONLY: false, + transformArguments(code: RedisArgument, options?: FunctionLoadOptions) { + const args: CommandArguments = ['FUNCTION', 'LOAD']; if (options?.REPLACE) { - args.push('REPLACE'); + args.push('REPLACE'); } args.push(code); return args; -} - -export declare function transformReply(): string; + }, + transformReply: undefined as unknown as () => BlobStringReply +} as const satisfies Command; diff --git a/packages/client/lib/commands/FUNCTION_RESTORE.spec.ts b/packages/client/lib/commands/FUNCTION_RESTORE.spec.ts index a5c2e2dcc72..465e99b6104 100644 --- a/packages/client/lib/commands/FUNCTION_RESTORE.spec.ts +++ b/packages/client/lib/commands/FUNCTION_RESTORE.spec.ts @@ -1,37 +1,40 @@ -import { strict as assert } from 'assert'; +import { strict as assert } from 'node:assert'; import testUtils, { GLOBAL } from '../test-utils'; -import { transformArguments } from './FUNCTION_RESTORE'; +import FUNCTION_RESTORE from './FUNCTION_RESTORE'; +import { RESP_TYPES } from '../RESP/decoder'; describe('FUNCTION RESTORE', () => { - testUtils.isVersionGreaterThanHook([7]); + testUtils.isVersionGreaterThanHook([7]); - describe('transformArguments', () => { - it('simple', () => { - assert.deepEqual( - transformArguments('dump'), - ['FUNCTION', 'RESTORE', 'dump'] - ); - }); + describe('transformArguments', () => { + it('simple', () => { + assert.deepEqual( + FUNCTION_RESTORE.transformArguments('dump'), + ['FUNCTION', 'RESTORE', 'dump'] + ); + }); - it('with mode', () => { - assert.deepEqual( - transformArguments('dump', 'APPEND'), - ['FUNCTION', 'RESTORE', 'dump', 'APPEND'] - ); - }); + it('with mode', () => { + assert.deepEqual( + FUNCTION_RESTORE.transformArguments('dump', { + mode: 'APPEND' + }), + ['FUNCTION', 'RESTORE', 'dump', 'APPEND'] + ); }); + }); - testUtils.testWithClient('client.functionRestore', async client => { - assert.equal( - await client.functionRestore( - await client.functionDump( - client.commandOptions({ - returnBuffers: true - }) - ), - 'FLUSH' - ), - 'OK' - ); - }, GLOBAL.SERVERS.OPEN); + testUtils.testWithClient('client.functionRestore', async client => { + assert.equal( + await client.functionRestore( + await client.withTypeMapping({ + [RESP_TYPES.BLOB_STRING]: Buffer + }).functionDump(), + { + mode: 'REPLACE' + } + ), + 'OK' + ); + }, GLOBAL.SERVERS.OPEN); }); diff --git a/packages/client/lib/commands/FUNCTION_RESTORE.ts b/packages/client/lib/commands/FUNCTION_RESTORE.ts index bc9c41e262d..8c875530562 100644 --- a/packages/client/lib/commands/FUNCTION_RESTORE.ts +++ b/packages/client/lib/commands/FUNCTION_RESTORE.ts @@ -1,16 +1,20 @@ -import { RedisCommandArgument, RedisCommandArguments } from '.'; +import { SimpleStringReply, Command, RedisArgument } from '../RESP/types'; -export function transformArguments( - dump: RedisCommandArgument, - mode?: 'FLUSH' | 'APPEND' | 'REPLACE' -): RedisCommandArguments { +export interface FunctionRestoreOptions { + mode?: 'FLUSH' | 'APPEND' | 'REPLACE'; +} + +export default { + FIRST_KEY_INDEX: undefined, + IS_READ_ONLY: false, + transformArguments(dump: RedisArgument, options?: FunctionRestoreOptions) { const args = ['FUNCTION', 'RESTORE', dump]; - if (mode) { - args.push(mode); + if (options?.mode) { + args.push(options.mode); } return args; -} - -export declare function transformReply(): 'OK'; + }, + transformReply: undefined as unknown as () => SimpleStringReply<'OK'> +} as const satisfies Command; diff --git a/packages/client/lib/commands/FUNCTION_STATS.spec.ts b/packages/client/lib/commands/FUNCTION_STATS.spec.ts index a5e26b5fecc..b48b012614f 100644 --- a/packages/client/lib/commands/FUNCTION_STATS.spec.ts +++ b/packages/client/lib/commands/FUNCTION_STATS.spec.ts @@ -1,25 +1,25 @@ -import { strict as assert } from 'assert'; +import { strict as assert } from 'node:assert'; import testUtils, { GLOBAL } from '../test-utils'; -import { transformArguments } from './FUNCTION_STATS'; +import FUNCTION_STATS from './FUNCTION_STATS'; describe('FUNCTION STATS', () => { - testUtils.isVersionGreaterThanHook([7]); + testUtils.isVersionGreaterThanHook([7]); - it('transformArguments', () => { - assert.deepEqual( - transformArguments(), - ['FUNCTION', 'STATS'] - ); - }); + it('transformArguments', () => { + assert.deepEqual( + FUNCTION_STATS.transformArguments(), + ['FUNCTION', 'STATS'] + ); + }); - testUtils.testWithClient('client.functionStats', async client => { - const stats = await client.functionStats(); - assert.equal(stats.runningScript, null); - assert.equal(typeof stats.engines, 'object'); - for (const [engine, { librariesCount, functionsCount }] of Object.entries(stats.engines)) { - assert.equal(typeof engine, 'string'); - assert.equal(typeof librariesCount, 'number'); - assert.equal(typeof functionsCount, 'number'); - } - }, GLOBAL.SERVERS.OPEN); + testUtils.testWithClient('client.functionStats', async client => { + const stats = await client.functionStats(); + assert.equal(stats.running_script, null); + assert.equal(typeof stats.engines, 'object'); + for (const [engine, { libraries_count, functions_count }] of Object.entries(stats.engines)) { + assert.equal(typeof engine, 'string'); + assert.equal(typeof libraries_count, 'number'); + assert.equal(typeof functions_count, 'number'); + } + }, GLOBAL.SERVERS.OPEN); }); diff --git a/packages/client/lib/commands/FUNCTION_STATS.ts b/packages/client/lib/commands/FUNCTION_STATS.ts index bb5c957e78b..138d1fb82d5 100644 --- a/packages/client/lib/commands/FUNCTION_STATS.ts +++ b/packages/client/lib/commands/FUNCTION_STATS.ts @@ -1,56 +1,70 @@ -import { RedisCommandArguments } from '.'; +import { Command, TuplesToMapReply, BlobStringReply, NullReply, NumberReply, MapReply, Resp2Reply, UnwrapReply } from '../RESP/types'; +import { isNullReply } from './generic-transformers'; -export function transformArguments(): RedisCommandArguments { +type RunningScript = NullReply | TuplesToMapReply<[ + [BlobStringReply<'name'>, BlobStringReply], + [BlobStringReply<'command'>, BlobStringReply], + [BlobStringReply<'duration_ms'>, NumberReply] +]>; + +type Engine = TuplesToMapReply<[ + [BlobStringReply<'libraries_count'>, NumberReply], + [BlobStringReply<'functions_count'>, NumberReply] +]>; + +type Engines = MapReply; + +type FunctionStatsReply = TuplesToMapReply<[ + [BlobStringReply<'running_script'>, RunningScript], + [BlobStringReply<'engines'>, Engines] +]>; + +export default { + IS_READ_ONLY: true, + FIRST_KEY_INDEX: undefined, + transformArguments() { return ['FUNCTION', 'STATS']; -} + }, + transformReply: { + 2: (reply: UnwrapReply>) => { + return { + running_script: transformRunningScript(reply[1]), + engines: transformEngines(reply[3]) + }; + }, + 3: undefined as unknown as () => FunctionStatsReply + } +} as const satisfies Command; -type FunctionStatsRawReply = [ - 'running_script', - null | [ - 'name', - string, - 'command', - string, - 'duration_ms', - number - ], - 'engines', - Array // "flat tuples" (there is no way to type that) - // ...[string, [ - // 'libraries_count', - // number, - // 'functions_count', - // number - // ]] -]; - -interface FunctionStatsReply { - runningScript: null | { - name: string; - command: string; - durationMs: number; - }; - engines: Record; +function transformRunningScript(reply: Resp2Reply) { + if (isNullReply(reply)) { + return null; + } + + const unwraped = reply as unknown as UnwrapReply; + return { + name: unwraped[1], + command: unwraped[3], + duration_ms: unwraped[5] + }; } -export function transformReply(reply: FunctionStatsRawReply): FunctionStatsReply { - const engines = Object.create(null); - for (let i = 0; i < reply[3].length; i++) { - engines[reply[3][i]] = { - librariesCount: reply[3][++i][1], - functionsCount: reply[3][i][3] - }; - } - - return { - runningScript: reply[1] === null ? null : { - name: reply[1][1], - command: reply[1][3], - durationMs: reply[1][5] - }, - engines +function transformEngines(reply: Resp2Reply) { + const unwraped = reply as unknown as UnwrapReply; + + const engines: Record = Object.create(null); + for (let i = 0; i < unwraped.length; i++) { + const name = unwraped[i] as BlobStringReply, + stats = unwraped[++i] as Resp2Reply, + unwrapedStats = stats as unknown as UnwrapReply; + engines[name.toString()] = { + libraries_count: unwrapedStats[1], + functions_count: unwrapedStats[3] }; + } + + return engines; } diff --git a/packages/client/lib/commands/GEOADD.spec.ts b/packages/client/lib/commands/GEOADD.spec.ts index 6425c881c9d..14195ed289c 100644 --- a/packages/client/lib/commands/GEOADD.spec.ts +++ b/packages/client/lib/commands/GEOADD.spec.ts @@ -1,95 +1,100 @@ -import { strict as assert } from 'assert'; +import { strict as assert } from 'node:assert'; import testUtils, { GLOBAL } from '../test-utils'; -import { transformArguments } from './GEOADD'; +import GEOADD from './GEOADD'; describe('GEOADD', () => { - describe('transformArguments', () => { - it('one member', () => { - assert.deepEqual( - transformArguments('key', { - member: 'member', - longitude: 1, - latitude: 2 - }), - ['GEOADD', 'key', '1', '2', 'member'] - ); - }); + describe('transformArguments', () => { + it('one member', () => { + assert.deepEqual( + GEOADD.transformArguments('key', { + member: 'member', + longitude: 1, + latitude: 2 + }), + ['GEOADD', 'key', '1', '2', 'member'] + ); + }); - it('multiple members', () => { - assert.deepEqual( - transformArguments('key', [{ - longitude: 1, - latitude: 2, - member: '3', - }, { - longitude: 4, - latitude: 5, - member: '6', - }]), - ['GEOADD', 'key', '1', '2', '3', '4', '5', '6'] - ); - }); + it('multiple members', () => { + assert.deepEqual( + GEOADD.transformArguments('key', [{ + longitude: 1, + latitude: 2, + member: '3', + }, { + longitude: 4, + latitude: 5, + member: '6', + }]), + ['GEOADD', 'key', '1', '2', '3', '4', '5', '6'] + ); + }); - it('with NX', () => { - assert.deepEqual( - transformArguments('key', { - longitude: 1, - latitude: 2, - member: 'member' - }, { - NX: true - }), - ['GEOADD', 'key', 'NX', '1', '2', 'member'] - ); - }); + it('with condition', () => { + assert.deepEqual( + GEOADD.transformArguments('key', { + longitude: 1, + latitude: 2, + member: 'member' + }, { + condition: 'NX' + }), + ['GEOADD', 'key', 'NX', '1', '2', 'member'] + ); + }); - it('with CH', () => { - assert.deepEqual( - transformArguments('key', { - longitude: 1, - latitude: 2, - member: 'member' - }, { - CH: true - }), - ['GEOADD', 'key', 'CH', '1', '2', 'member'] - ); - }); + it('with NX (backwards compatibility)', () => { + assert.deepEqual( + GEOADD.transformArguments('key', { + longitude: 1, + latitude: 2, + member: 'member' + }, { + NX: true + }), + ['GEOADD', 'key', 'NX', '1', '2', 'member'] + ); + }); - it('with XX, CH', () => { - assert.deepEqual( - transformArguments('key', { - longitude: 1, - latitude: 2, - member: 'member' - }, { - XX: true, - CH: true - }), - ['GEOADD', 'key', 'XX', 'CH', '1', '2', 'member'] - ); - }); + it('with CH', () => { + assert.deepEqual( + GEOADD.transformArguments('key', { + longitude: 1, + latitude: 2, + member: 'member' + }, { + CH: true + }), + ['GEOADD', 'key', 'CH', '1', '2', 'member'] + ); }); - testUtils.testWithClient('client.geoAdd', async client => { - assert.equal( - await client.geoAdd('key', { - member: 'member', - longitude: 1, - latitude: 2 - }), - 1 - ); - }, GLOBAL.SERVERS.OPEN); + it('with condition, CH', () => { + assert.deepEqual( + GEOADD.transformArguments('key', { + longitude: 1, + latitude: 2, + member: 'member' + }, { + condition: 'XX', + CH: true + }), + ['GEOADD', 'key', 'XX', 'CH', '1', '2', 'member'] + ); + }); + }); - testUtils.testWithCluster('cluster.geoAdd', async cluster => { - assert.equal( - await cluster.geoAdd('key', { - member: 'member', - longitude: 1, - latitude: 2 - }), - 1 - ); - }, GLOBAL.CLUSTERS.OPEN); + testUtils.testAll('geoAdd', async client => { + assert.equal( + await client.geoAdd('key', { + member: 'member', + longitude: 1, + latitude: 2 + }), + 1 + ); + }, { + client: GLOBAL.SERVERS.OPEN, + cluster: GLOBAL.CLUSTERS.OPEN + }); }); diff --git a/packages/client/lib/commands/GEOADD.ts b/packages/client/lib/commands/GEOADD.ts index daccb0842e0..f89f6b80e82 100644 --- a/packages/client/lib/commands/GEOADD.ts +++ b/packages/client/lib/commands/GEOADD.ts @@ -1,53 +1,65 @@ -import { RedisCommandArgument, RedisCommandArguments } from '.'; -import { GeoCoordinates } from './generic-transformers'; +import { RedisArgument, CommandArguments, NumberReply, Command } from '../RESP/types'; +import { GeoCoordinates } from './GEOSEARCH'; -export const FIRST_KEY_INDEX = 1; - -interface GeoMember extends GeoCoordinates { - member: RedisCommandArgument; -} - -interface NX { - NX?: true; +export interface GeoMember extends GeoCoordinates { + member: RedisArgument; } -interface XX { - XX?: true; +export interface GeoAddOptions { + condition?: 'NX' | 'XX'; + /** + * @deprecated Use `{ condition: 'NX' }` instead. + */ + NX?: boolean; + /** + * @deprecated Use `{ condition: 'XX' }` instead. + */ + XX?: boolean; + CH?: boolean; } -type SetGuards = NX | XX; - -interface GeoAddCommonOptions { - CH?: true; -} - -type GeoAddOptions = SetGuards & GeoAddCommonOptions; - -export function transformArguments( - key: RedisCommandArgument, toAdd: GeoMember | Array, +export default { + FIRST_KEY_INDEX: 1, + IS_READ_ONLY: false, + transformArguments( + key: RedisArgument, + toAdd: GeoMember | Array, options?: GeoAddOptions -): RedisCommandArguments { + ) { const args = ['GEOADD', key]; - if ((options as NX)?.NX) { - args.push('NX'); - } else if ((options as XX)?.XX) { - args.push('XX'); + if (options?.condition) { + args.push(options.condition); + } else if (options?.NX) { + args.push('NX'); + } else if (options?.XX) { + args.push('XX'); } if (options?.CH) { - args.push('CH'); + args.push('CH'); } - for (const { longitude, latitude, member } of (Array.isArray(toAdd) ? toAdd : [toAdd])) { - args.push( - longitude.toString(), - latitude.toString(), - member - ); + if (Array.isArray(toAdd)) { + for (const member of toAdd) { + pushMember(args, member); + } + } else { + pushMember(args, toAdd); } return args; -} + }, + transformReply: undefined as unknown as () => NumberReply +} as const satisfies Command; -export declare function transformReply(): number; +function pushMember( + args: CommandArguments, + { longitude, latitude, member }: GeoMember +) { + args.push( + longitude.toString(), + latitude.toString(), + member + ); +} diff --git a/packages/client/lib/commands/GEODIST.spec.ts b/packages/client/lib/commands/GEODIST.spec.ts index bbc62480ee1..eb5a1ef801e 100644 --- a/packages/client/lib/commands/GEODIST.spec.ts +++ b/packages/client/lib/commands/GEODIST.spec.ts @@ -1,57 +1,54 @@ -import { strict as assert } from 'assert'; +import { strict as assert } from 'node:assert'; import testUtils, { GLOBAL } from '../test-utils'; -import { transformArguments } from './GEODIST'; +import GEODIST from './GEODIST'; describe('GEODIST', () => { - describe('transformArguments', () => { - it('simple', () => { - assert.deepEqual( - transformArguments('key', '1', '2'), - ['GEODIST', 'key', '1', '2'] - ); - }); - - it('with unit', () => { - assert.deepEqual( - transformArguments('key', '1', '2', 'm'), - ['GEODIST', 'key', '1', '2', 'm'] - ); - }); + describe('transformArguments', () => { + it('simple', () => { + assert.deepEqual( + GEODIST.transformArguments('key', '1', '2'), + ['GEODIST', 'key', '1', '2'] + ); }); - describe('client.geoDist', () => { - testUtils.testWithClient('null', async client => { - assert.equal( - await client.geoDist('key', '1', '2'), - null - ); - }, GLOBAL.SERVERS.OPEN); + it('with unit', () => { + assert.deepEqual( + GEODIST.transformArguments('key', '1', '2', 'm'), + ['GEODIST', 'key', '1', '2', 'm'] + ); + }); + }); - testUtils.testWithClient('with value', async client => { - const [, dist] = await Promise.all([ - client.geoAdd('key', [{ - member: '1', - longitude: 1, - latitude: 1 - }, { - member: '2', - longitude: 2, - latitude: 2 - }]), - client.geoDist('key', '1', '2') - ]); + testUtils.testAll('geoDist null', async client => { + assert.equal( + await client.geoDist('key', '1', '2'), + null + ); + }, { + client: GLOBAL.SERVERS.OPEN, + cluster: GLOBAL.CLUSTERS.OPEN + }); - assert.equal( - dist, - 157270.0561 - ); - }, GLOBAL.SERVERS.OPEN); - }); + testUtils.testAll('geoDist with member', async client => { + const [, dist] = await Promise.all([ + client.geoAdd('key', [{ + member: '1', + longitude: 1, + latitude: 1 + }, { + member: '2', + longitude: 2, + latitude: 2 + }]), + client.geoDist('key', '1', '2') + ]); - testUtils.testWithCluster('cluster.geoDist', async cluster => { - assert.equal( - await cluster.geoDist('key', '1', '2'), - null - ); - }, GLOBAL.CLUSTERS.OPEN); + assert.equal( + dist, + 157270.0561 + ); + }, { + client: GLOBAL.SERVERS.OPEN, + cluster: GLOBAL.CLUSTERS.OPEN + }); }); diff --git a/packages/client/lib/commands/GEODIST.ts b/packages/client/lib/commands/GEODIST.ts index 5dbf8ece9cc..3e684d67579 100644 --- a/packages/client/lib/commands/GEODIST.ts +++ b/packages/client/lib/commands/GEODIST.ts @@ -1,25 +1,24 @@ -import { RedisCommandArgument, RedisCommandArguments } from '.'; -import { GeoUnits } from './generic-transformers'; +import { RedisArgument, BlobStringReply, NullReply, Command } from '../RESP/types'; +import { GeoUnits } from './GEOSEARCH'; -export const FIRST_KEY_INDEX = 1; - -export const IS_READ_ONLY = true; - -export function transformArguments( - key: RedisCommandArgument, - member1: RedisCommandArgument, - member2: RedisCommandArgument, +export default { + FIRST_KEY_INDEX: 1, + IS_READ_ONLY: true, + transformArguments( + key: RedisArgument, + member1: RedisArgument, + member2: RedisArgument, unit?: GeoUnits -): RedisCommandArguments { + ) { const args = ['GEODIST', key, member1, member2]; if (unit) { - args.push(unit); + args.push(unit); } return args; -} - -export function transformReply(reply: RedisCommandArgument | null): number | null { + }, + transformReply(reply: BlobStringReply | NullReply) { return reply === null ? null : Number(reply); -} + } +} as const satisfies Command; diff --git a/packages/client/lib/commands/GEOHASH.spec.ts b/packages/client/lib/commands/GEOHASH.spec.ts index c421c148f43..8efe55d89b6 100644 --- a/packages/client/lib/commands/GEOHASH.spec.ts +++ b/packages/client/lib/commands/GEOHASH.spec.ts @@ -1,35 +1,31 @@ -import { strict as assert } from 'assert'; +import { strict as assert } from 'node:assert'; import testUtils, { GLOBAL } from '../test-utils'; -import { transformArguments } from './GEOHASH'; +import GEOHASH from './GEOHASH'; describe('GEOHASH', () => { - describe('transformArguments', () => { - it('single member', () => { - assert.deepEqual( - transformArguments('key', 'member'), - ['GEOHASH', 'key', 'member'] - ); - }); - - it('multiple members', () => { - assert.deepEqual( - transformArguments('key', ['1', '2']), - ['GEOHASH', 'key', '1', '2'] - ); - }); + describe('transformArguments', () => { + it('single member', () => { + assert.deepEqual( + GEOHASH.transformArguments('key', 'member'), + ['GEOHASH', 'key', 'member'] + ); }); - testUtils.testWithClient('client.geoHash', async client => { - assert.deepEqual( - await client.geoHash('key', 'member'), - [null] - ); - }, GLOBAL.SERVERS.OPEN); + it('multiple members', () => { + assert.deepEqual( + GEOHASH.transformArguments('key', ['1', '2']), + ['GEOHASH', 'key', '1', '2'] + ); + }); + }); - testUtils.testWithCluster('cluster.geoHash', async cluster => { - assert.deepEqual( - await cluster.geoHash('key', 'member'), - [null] - ); - }, GLOBAL.CLUSTERS.OPEN); + testUtils.testAll('geoHash', async client => { + assert.deepEqual( + await client.geoHash('key', 'member'), + [null] + ); + }, { + client: GLOBAL.SERVERS.OPEN, + cluster: GLOBAL.CLUSTERS.OPEN + }); }); diff --git a/packages/client/lib/commands/GEOHASH.ts b/packages/client/lib/commands/GEOHASH.ts index 55e22c497e2..d8d2732e512 100644 --- a/packages/client/lib/commands/GEOHASH.ts +++ b/packages/client/lib/commands/GEOHASH.ts @@ -1,15 +1,14 @@ -import { RedisCommandArgument, RedisCommandArguments } from '.'; -import { pushVerdictArguments } from './generic-transformers'; - -export const FIRST_KEY_INDEX = 1; - -export const IS_READ_ONLY = true; - -export function transformArguments( - key: RedisCommandArgument, - member: RedisCommandArgument | Array -): RedisCommandArguments { - return pushVerdictArguments(['GEOHASH', key], member); -} - -export declare function transformReply(): Array; +import { RedisArgument, ArrayReply, BlobStringReply, Command } from '../RESP/types'; +import { RedisVariadicArgument, pushVariadicArguments } from './generic-transformers'; + +export default { + FIRST_KEY_INDEX: 1, + IS_READ_ONLY: true, + transformArguments( + key: RedisArgument, + member: RedisVariadicArgument + ) { + return pushVariadicArguments(['GEOHASH', key], member); + }, + transformReply: undefined as unknown as () => ArrayReply +} as const satisfies Command; diff --git a/packages/client/lib/commands/GEOPOS.spec.ts b/packages/client/lib/commands/GEOPOS.spec.ts index 9c08ccd08f5..20ad5c5c942 100644 --- a/packages/client/lib/commands/GEOPOS.spec.ts +++ b/packages/client/lib/commands/GEOPOS.spec.ts @@ -1,73 +1,51 @@ -import { strict as assert } from 'assert'; +import { strict as assert } from 'node:assert'; import testUtils, { GLOBAL } from '../test-utils'; -import { transformArguments, transformReply } from './GEOPOS'; +import GEOPOS from './GEOPOS'; describe('GEOPOS', () => { - describe('transformArguments', () => { - it('single member', () => { - assert.deepEqual( - transformArguments('key', 'member'), - ['GEOPOS', 'key', 'member'] - ); - }); - - it('multiple members', () => { - assert.deepEqual( - transformArguments('key', ['1', '2']), - ['GEOPOS', 'key', '1', '2'] - ); - }); + describe('transformArguments', () => { + it('single member', () => { + assert.deepEqual( + GEOPOS.transformArguments('key', 'member'), + ['GEOPOS', 'key', 'member'] + ); }); - describe('transformReply', () => { - it('null', () => { - assert.deepEqual( - transformReply([null]), - [null] - ); - }); - - it('with member', () => { - assert.deepEqual( - transformReply([['1', '2']]), - [{ - longitude: '1', - latitude: '2' - }] - ); - }); + it('multiple members', () => { + assert.deepEqual( + GEOPOS.transformArguments('key', ['1', '2']), + ['GEOPOS', 'key', '1', '2'] + ); }); - - describe('client.geoPos', () => { - testUtils.testWithClient('null', async client => { - assert.deepEqual( - await client.geoPos('key', 'member'), - [null] - ); - }, GLOBAL.SERVERS.OPEN); - - testUtils.testWithClient('with member', async client => { - const coordinates = { - longitude: '-122.06429868936538696', - latitude: '37.37749628831998194' - }; - - await client.geoAdd('key', { - member: 'member', - ...coordinates - }); - - assert.deepEqual( - await client.geoPos('key', 'member'), - [coordinates] - ); - }, GLOBAL.SERVERS.OPEN); + }); + + testUtils.testAll('geoPos null', async client => { + assert.deepEqual( + await client.geoPos('key', 'member'), + [null] + ); + }, { + client: GLOBAL.SERVERS.OPEN, + cluster: GLOBAL.CLUSTERS.OPEN + }); + + testUtils.testAll('geoPos with member', async client => { + const coordinates = { + longitude: '-122.06429868936538696', + latitude: '37.37749628831998194' + }; + + await client.geoAdd('key', { + member: 'member', + ...coordinates }); - testUtils.testWithCluster('cluster.geoPos', async cluster => { - assert.deepEqual( - await cluster.geoPos('key', 'member'), - [null] - ); - }, GLOBAL.CLUSTERS.OPEN); + assert.deepEqual( + await client.geoPos('key', 'member'), + [coordinates] + ); + }, { + client: GLOBAL.SERVERS.OPEN, + cluster: GLOBAL.CLUSTERS.OPEN + }); }); diff --git a/packages/client/lib/commands/GEOPOS.ts b/packages/client/lib/commands/GEOPOS.ts index 0a5f079deeb..30273c64c18 100644 --- a/packages/client/lib/commands/GEOPOS.ts +++ b/packages/client/lib/commands/GEOPOS.ts @@ -1,27 +1,22 @@ -import { RedisCommandArgument, RedisCommandArguments } from '.'; -import { pushVerdictArguments } from './generic-transformers'; +import { RedisArgument, ArrayReply, TuplesReply, BlobStringReply, NullReply, UnwrapReply, Command } from '../RESP/types'; +import { RedisVariadicArgument, pushVariadicArguments } from './generic-transformers'; -export const FIRST_KEY_INDEX = 1; - -export const IS_READ_ONLY = true; - -export function transformArguments( - key: RedisCommandArgument, - member: RedisCommandArgument | Array -): RedisCommandArguments { - return pushVerdictArguments(['GEOPOS', key], member); -} - -type GeoCoordinatesRawReply = Array<[RedisCommandArgument, RedisCommandArgument] | null>; - -interface GeoCoordinates { - longitude: RedisCommandArgument; - latitude: RedisCommandArgument; -} - -export function transformReply(reply: GeoCoordinatesRawReply): Array { - return reply.map(coordinates => coordinates === null ? null : { - longitude: coordinates[0], - latitude: coordinates[1] +export default { + FIRST_KEY_INDEX: 1, + IS_READ_ONLY: true, + transformArguments( + key: RedisArgument, + member: RedisVariadicArgument + ) { + return pushVariadicArguments(['GEOPOS', key], member); + }, + transformReply(reply: UnwrapReply | NullReply>>) { + return reply.map(item => { + const unwrapped = item as unknown as UnwrapReply; + return unwrapped === null ? null : { + longitude: unwrapped[0], + latitude: unwrapped[1] + }; }); -} + } +} as const satisfies Command; diff --git a/packages/client/lib/commands/GEORADIUS.spec.ts b/packages/client/lib/commands/GEORADIUS.spec.ts index 786b2665029..533e48f6898 100644 --- a/packages/client/lib/commands/GEORADIUS.spec.ts +++ b/packages/client/lib/commands/GEORADIUS.spec.ts @@ -1,35 +1,28 @@ -import { strict as assert } from 'assert'; +import { strict as assert } from 'node:assert'; import testUtils, { GLOBAL } from '../test-utils'; -import { transformArguments } from './GEORADIUS'; +import GEORADIUS from './GEORADIUS'; describe('GEORADIUS', () => { - it('transformArguments', () => { - assert.deepEqual( - transformArguments('key', { - longitude: 1, - latitude: 2 - }, 3 , 'm'), - ['GEORADIUS', 'key', '1', '2', '3', 'm'] - ); - }); + it('transformArguments', () => { + assert.deepEqual( + GEORADIUS.transformArguments('key', { + longitude: 1, + latitude: 2 + }, 3, 'm'), + ['GEORADIUS', 'key', '1', '2', '3', 'm'] + ); + }); - testUtils.testWithClient('client.geoRadius', async client => { - assert.deepEqual( - await client.geoRadius('key', { - longitude: 1, - latitude: 2 - }, 3 , 'm'), - [] - ); - }, GLOBAL.SERVERS.OPEN); - - testUtils.testWithCluster('cluster.geoRadius', async cluster => { - assert.deepEqual( - await cluster.geoRadius('key', { - longitude: 1, - latitude: 2 - }, 3 , 'm'), - [] - ); - }, GLOBAL.CLUSTERS.OPEN); + testUtils.testAll('geoRadius', async client => { + assert.deepEqual( + await client.geoRadius('key', { + longitude: 1, + latitude: 2 + }, 3, 'm'), + [] + ); + }, { + client: GLOBAL.SERVERS.OPEN, + cluster: GLOBAL.CLUSTERS.OPEN + }); }); diff --git a/packages/client/lib/commands/GEORADIUS.ts b/packages/client/lib/commands/GEORADIUS.ts index f47cf508848..e777432f090 100644 --- a/packages/client/lib/commands/GEORADIUS.ts +++ b/packages/client/lib/commands/GEORADIUS.ts @@ -1,25 +1,32 @@ -import { RedisCommandArgument, RedisCommandArguments } from '.'; -import { GeoSearchOptions, GeoCoordinates, pushGeoRadiusArguments, GeoUnits } from './generic-transformers'; +import { RedisArgument, ArrayReply, BlobStringReply, Command } from '../RESP/types'; +import { GeoCoordinates, GeoUnits, GeoSearchOptions, pushGeoSearchOptions } from './GEOSEARCH'; -export const FIRST_KEY_INDEX = 1; +export function transformGeoRadiusArguments( + command: RedisArgument, + key: RedisArgument, + from: GeoCoordinates, + radius: number, + unit: GeoUnits, + options?: GeoSearchOptions +) { + const args = [ + command, + key, + from.longitude.toString(), + from.latitude.toString(), + radius.toString(), + unit + ]; -export const IS_READ_ONLY = true; + pushGeoSearchOptions(args, options); -export function transformArguments( - key: RedisCommandArgument, - coordinates: GeoCoordinates, - radius: number, - unit: GeoUnits, - options?: GeoSearchOptions -): RedisCommandArguments { - return pushGeoRadiusArguments( - ['GEORADIUS'], - key, - coordinates, - radius, - unit, - options - ); + return args; } -export declare function transformReply(): Array; +export default { + FIRST_KEY_INDEX: 1, + IS_READ_ONLY: false, + transformArguments: transformGeoRadiusArguments.bind(undefined, 'GEORADIUS'), + transformReply: undefined as unknown as () => ArrayReply +} as const satisfies Command; + diff --git a/packages/client/lib/commands/GEORADIUSBYMEMBER.spec.ts b/packages/client/lib/commands/GEORADIUSBYMEMBER.spec.ts index 8cc4212c839..57349a79acb 100644 --- a/packages/client/lib/commands/GEORADIUSBYMEMBER.spec.ts +++ b/packages/client/lib/commands/GEORADIUSBYMEMBER.spec.ts @@ -1,26 +1,22 @@ -import { strict as assert } from 'assert'; +import { strict as assert } from 'node:assert'; import testUtils, { GLOBAL } from '../test-utils'; -import { transformArguments } from './GEORADIUSBYMEMBER'; +import GEORADIUSBYMEMBER from './GEORADIUSBYMEMBER'; describe('GEORADIUSBYMEMBER', () => { - it('transformArguments', () => { - assert.deepEqual( - transformArguments('key', 'member', 3 , 'm'), - ['GEORADIUSBYMEMBER', 'key', 'member', '3', 'm'] - ); - }); + it('transformArguments', () => { + assert.deepEqual( + GEORADIUSBYMEMBER.transformArguments('key', 'member', 3, 'm'), + ['GEORADIUSBYMEMBER', 'key', 'member', '3', 'm'] + ); + }); - testUtils.testWithClient('client.geoRadiusByMember', async client => { - assert.deepEqual( - await client.geoRadiusByMember('key', 'member', 3 , 'm'), - [] - ); - }, GLOBAL.SERVERS.OPEN); - - testUtils.testWithCluster('cluster.geoRadiusByMember', async cluster => { - assert.deepEqual( - await cluster.geoRadiusByMember('key', 'member', 3 , 'm'), - [] - ); - }, GLOBAL.CLUSTERS.OPEN); + testUtils.testAll('geoRadiusByMember', async client => { + assert.deepEqual( + await client.geoRadiusByMember('key', 'member', 3, 'm'), + [] + ); + }, { + client: GLOBAL.SERVERS.OPEN, + cluster: GLOBAL.CLUSTERS.OPEN + }); }); diff --git a/packages/client/lib/commands/GEORADIUSBYMEMBER.ts b/packages/client/lib/commands/GEORADIUSBYMEMBER.ts index 96bb622fb85..13b52a30630 100644 --- a/packages/client/lib/commands/GEORADIUSBYMEMBER.ts +++ b/packages/client/lib/commands/GEORADIUSBYMEMBER.ts @@ -1,25 +1,30 @@ -import { RedisCommandArgument, RedisCommandArguments } from '.'; -import { GeoSearchOptions, pushGeoRadiusArguments, GeoUnits } from './generic-transformers'; +import { RedisArgument, ArrayReply, BlobStringReply, Command } from '../RESP/types'; +import { GeoUnits, GeoSearchOptions, pushGeoSearchOptions } from './GEOSEARCH'; -export const FIRST_KEY_INDEX = 1; +export function transformGeoRadiusByMemberArguments( + command: RedisArgument, + key: RedisArgument, + from: RedisArgument, + radius: number, + unit: GeoUnits, + options?: GeoSearchOptions +) { + const args = [ + command, + key, + from, + radius.toString(), + unit + ]; -export const IS_READ_ONLY = true; + pushGeoSearchOptions(args, options); -export function transformArguments( - key: RedisCommandArgument, - member: string, - radius: number, - unit: GeoUnits, - options?: GeoSearchOptions -): RedisCommandArguments { - return pushGeoRadiusArguments( - ['GEORADIUSBYMEMBER'], - key, - member, - radius, - unit, - options - ); + return args; } -export declare function transformReply(): Array; +export default { + FIRST_KEY_INDEX: 1, + IS_READ_ONLY: false, + transformArguments: transformGeoRadiusByMemberArguments.bind(undefined, 'GEORADIUSBYMEMBER'), + transformReply: undefined as unknown as () => ArrayReply +} as const satisfies Command; diff --git a/packages/client/lib/commands/GEORADIUSBYMEMBERSTORE.spec.ts b/packages/client/lib/commands/GEORADIUSBYMEMBERSTORE.spec.ts deleted file mode 100644 index 100ecc03368..00000000000 --- a/packages/client/lib/commands/GEORADIUSBYMEMBERSTORE.spec.ts +++ /dev/null @@ -1,53 +0,0 @@ -import { strict as assert } from 'assert'; -import testUtils, { GLOBAL } from '../test-utils'; -import { transformArguments, transformReply } from './GEORADIUSBYMEMBERSTORE'; - -describe('GEORADIUSBYMEMBERSTORE', () => { - describe('transformArguments', () => { - it('STORE', () => { - assert.deepEqual( - transformArguments('key', 'member', 3 , 'm', 'dest', { - SORT: 'ASC', - COUNT: { - value: 1, - ANY: true - } - }), - ['GEORADIUSBYMEMBER', 'key', 'member', '3', 'm', 'ASC', 'COUNT', '1', 'ANY', 'STORE', 'dest'] - ); - }); - - it('STOREDIST', () => { - assert.deepEqual( - transformArguments('key', 'member', 3 , 'm', 'dest', { STOREDIST: true }), - ['GEORADIUSBYMEMBER', 'key', 'member', '3', 'm', 'STOREDIST', 'dest'] - ); - }); - }); - - testUtils.testWithClient('client.geoRadiusByMemberStore', async client => { - await client.geoAdd('source', { - longitude: 1, - latitude: 1, - member: 'member' - }); - - assert.equal( - await client.geoRadiusByMemberStore('source', 'member', 3 , 'm', 'dest'), - 1 - ); - }, GLOBAL.SERVERS.OPEN); - - testUtils.testWithCluster('cluster.geoRadiusByMemberStore', async cluster => { - await cluster.geoAdd('{tag}source', { - longitude: 1, - latitude: 1, - member: 'member' - }); - - assert.equal( - await cluster.geoRadiusByMemberStore('{tag}source', 'member', 3 , 'm','{tag}destination'), - 1 - ); - }, GLOBAL.CLUSTERS.OPEN); -}); diff --git a/packages/client/lib/commands/GEORADIUSBYMEMBERSTORE.ts b/packages/client/lib/commands/GEORADIUSBYMEMBERSTORE.ts deleted file mode 100644 index 28f3c25fac9..00000000000 --- a/packages/client/lib/commands/GEORADIUSBYMEMBERSTORE.ts +++ /dev/null @@ -1,25 +0,0 @@ -import { RedisCommandArgument, RedisCommandArguments } from '.'; -import { GeoUnits, GeoRadiusStoreOptions, pushGeoRadiusStoreArguments } from './generic-transformers'; - -export { FIRST_KEY_INDEX, IS_READ_ONLY } from './GEORADIUSBYMEMBER'; - -export function transformArguments( - key: RedisCommandArgument, - member: string, - radius: number, - unit: GeoUnits, - destination: RedisCommandArgument, - options?: GeoRadiusStoreOptions, -): RedisCommandArguments { - return pushGeoRadiusStoreArguments( - ['GEORADIUSBYMEMBER'], - key, - member, - radius, - unit, - destination, - options - ); -} - -export declare function transformReply(): number diff --git a/packages/client/lib/commands/GEORADIUSBYMEMBER_RO.spec.ts b/packages/client/lib/commands/GEORADIUSBYMEMBER_RO.spec.ts index f3a47856e86..abf10013973 100644 --- a/packages/client/lib/commands/GEORADIUSBYMEMBER_RO.spec.ts +++ b/packages/client/lib/commands/GEORADIUSBYMEMBER_RO.spec.ts @@ -1,26 +1,22 @@ -import { strict as assert } from 'assert'; +import { strict as assert } from 'node:assert'; import testUtils, { GLOBAL } from '../test-utils'; -import { transformArguments } from './GEORADIUSBYMEMBER_RO'; +import GEORADIUSBYMEMBER_RO from './GEORADIUSBYMEMBER_RO'; describe('GEORADIUSBYMEMBER_RO', () => { - it('transformArguments', () => { - assert.deepEqual( - transformArguments('key', 'member', 3 , 'm'), - ['GEORADIUSBYMEMBER_RO', 'key', 'member', '3', 'm'] - ); - }); + it('transformArguments', () => { + assert.deepEqual( + GEORADIUSBYMEMBER_RO.transformArguments('key', 'member', 3, 'm'), + ['GEORADIUSBYMEMBER_RO', 'key', 'member', '3', 'm'] + ); + }); - testUtils.testWithClient('client.geoRadiusByMemberRo', async client => { - assert.deepEqual( - await client.geoRadiusByMemberRo('key', 'member', 3 , 'm'), - [] - ); - }, GLOBAL.SERVERS.OPEN); - - testUtils.testWithCluster('cluster.geoRadiusByMemberRo', async cluster => { - assert.deepEqual( - await cluster.geoRadiusByMemberRo('key', 'member', 3 , 'm'), - [] - ); - }, GLOBAL.CLUSTERS.OPEN); + testUtils.testAll('geoRadiusByMemberRo', async client => { + assert.deepEqual( + await client.geoRadiusByMemberRo('key', 'member', 3, 'm'), + [] + ); + }, { + client: GLOBAL.SERVERS.OPEN, + cluster: GLOBAL.CLUSTERS.OPEN + }); }); diff --git a/packages/client/lib/commands/GEORADIUSBYMEMBER_RO.ts b/packages/client/lib/commands/GEORADIUSBYMEMBER_RO.ts index 63f29ae65b5..7f85ed15df7 100644 --- a/packages/client/lib/commands/GEORADIUSBYMEMBER_RO.ts +++ b/packages/client/lib/commands/GEORADIUSBYMEMBER_RO.ts @@ -1,25 +1,9 @@ -import { RedisCommandArgument, RedisCommandArguments } from '.'; -import { GeoSearchOptions, pushGeoRadiusArguments, GeoUnits } from './generic-transformers'; - -export const FIRST_KEY_INDEX = 1; - -export const IS_READ_ONLY = true; - -export function transformArguments( - key: RedisCommandArgument, - member: string, - radius: number, - unit: GeoUnits, - options?: GeoSearchOptions -): RedisCommandArguments { - return pushGeoRadiusArguments( - ['GEORADIUSBYMEMBER_RO'], - key, - member, - radius, - unit, - options - ); -} - -export declare function transformReply(): Array; +import { Command } from '../RESP/types'; +import GEORADIUSBYMEMBER, { transformGeoRadiusByMemberArguments } from './GEORADIUSBYMEMBER'; + +export default { + FIRST_KEY_INDEX: GEORADIUSBYMEMBER.FIRST_KEY_INDEX, + IS_READ_ONLY: true, + transformArguments: transformGeoRadiusByMemberArguments.bind(undefined, 'GEORADIUSBYMEMBER_RO'), + transformReply: GEORADIUSBYMEMBER.transformReply +} as const satisfies Command; diff --git a/packages/client/lib/commands/GEORADIUSBYMEMBER_RO_WITH.spec.ts b/packages/client/lib/commands/GEORADIUSBYMEMBER_RO_WITH.spec.ts index 7904a763998..bcf91266365 100644 --- a/packages/client/lib/commands/GEORADIUSBYMEMBER_RO_WITH.spec.ts +++ b/packages/client/lib/commands/GEORADIUSBYMEMBER_RO_WITH.spec.ts @@ -1,31 +1,44 @@ -import { strict as assert } from 'assert'; +import { strict as assert } from 'node:assert'; import testUtils, { GLOBAL } from '../test-utils'; -import { RedisCommandArguments } from '.'; -import { GeoReplyWith } from './generic-transformers'; -import { transformArguments } from './GEORADIUSBYMEMBER_RO_WITH'; +import GEORADIUSBYMEMBER_RO_WITH from './GEORADIUSBYMEMBER_RO_WITH'; +import { CommandArguments } from '../RESP/types'; +import { GEO_REPLY_WITH } from './GEOSEARCH_WITH'; describe('GEORADIUSBYMEMBER_RO WITH', () => { - it('transformArguments', () => { - const expectedReply: RedisCommandArguments = ['GEORADIUSBYMEMBER_RO', 'key', 'member', '3', 'm', 'WITHDIST']; - expectedReply.preserve = ['WITHDIST']; + it('transformArguments', () => { + const expectedReply: CommandArguments = ['GEORADIUSBYMEMBER_RO', 'key', 'member', '3', 'm', 'WITHDIST']; + expectedReply.preserve = ['WITHDIST']; - assert.deepEqual( - transformArguments('key', 'member', 3 , 'm', [GeoReplyWith.DISTANCE]), - expectedReply - ); - }); + assert.deepEqual( + GEORADIUSBYMEMBER_RO_WITH.transformArguments('key', 'member', 3, 'm', [ + GEO_REPLY_WITH.DISTANCE + ]), + expectedReply + ); + }); - testUtils.testWithClient('client.geoRadiusByMemberRoWith', async client => { - assert.deepEqual( - await client.geoRadiusByMemberRoWith('key', 'member', 3 , 'm', [GeoReplyWith.DISTANCE]), - [] - ); - }, GLOBAL.SERVERS.OPEN); + testUtils.testAll('geoRadiusByMemberRoWith', async client => { + const [, reply] = await Promise.all([ + client.geoAdd('key', { + member: 'member', + longitude: 1, + latitude: 2 + }), + client.geoRadiusByMemberRoWith('key', 'member', 1, 'm', [ + GEO_REPLY_WITH.HASH, + GEO_REPLY_WITH.DISTANCE, + GEO_REPLY_WITH.COORDINATES + ]) + ]); - testUtils.testWithCluster('cluster.geoRadiusByMemberRoWith', async cluster => { - assert.deepEqual( - await cluster.geoRadiusByMemberRoWith('key', 'member', 3 , 'm', [GeoReplyWith.DISTANCE]), - [] - ); - }, GLOBAL.CLUSTERS.OPEN); + assert.equal(reply.length, 1); + assert.equal(reply[0].member, 'member'); + assert.equal(typeof reply[0].distance, 'string'); + assert.equal(typeof reply[0].hash, 'number'); + assert.equal(typeof reply[0].coordinates?.longitude, 'string'); + assert.equal(typeof reply[0].coordinates?.latitude, 'string'); + }, { + client: GLOBAL.SERVERS.OPEN, + cluster: GLOBAL.CLUSTERS.OPEN + }); }); diff --git a/packages/client/lib/commands/GEORADIUSBYMEMBER_RO_WITH.ts b/packages/client/lib/commands/GEORADIUSBYMEMBER_RO_WITH.ts index 6061be734b5..5fb945a1f9c 100644 --- a/packages/client/lib/commands/GEORADIUSBYMEMBER_RO_WITH.ts +++ b/packages/client/lib/commands/GEORADIUSBYMEMBER_RO_WITH.ts @@ -1,30 +1,9 @@ -import { RedisCommandArgument, RedisCommandArguments } from '.'; -import { GeoReplyWith, GeoSearchOptions, GeoUnits } from './generic-transformers'; -import { transformArguments as geoRadiusTransformArguments } from './GEORADIUSBYMEMBER_RO'; - -export { FIRST_KEY_INDEX, IS_READ_ONLY } from './GEORADIUSBYMEMBER_RO'; - -export function transformArguments( - key: RedisCommandArgument, - member: string, - radius: number, - unit: GeoUnits, - replyWith: Array, - options?: GeoSearchOptions -): RedisCommandArguments { - const args: RedisCommandArguments = geoRadiusTransformArguments( - key, - member, - radius, - unit, - options - ); - - args.push(...replyWith); - - args.preserve = replyWith; - - return args; -} - -export { transformGeoMembersWithReply as transformReply } from './generic-transformers'; +import { Command } from '../RESP/types'; +import GEORADIUSBYMEMBER_WITH, { transformGeoRadiusByMemberWithArguments } from './GEORADIUSBYMEMBER_WITH'; + +export default { + FIRST_KEY_INDEX: GEORADIUSBYMEMBER_WITH.FIRST_KEY_INDEX, + IS_READ_ONLY: true, + transformArguments: transformGeoRadiusByMemberWithArguments.bind(undefined, 'GEORADIUSBYMEMBER_RO'), + transformReply: GEORADIUSBYMEMBER_WITH.transformReply +} as const satisfies Command; diff --git a/packages/client/lib/commands/GEORADIUSBYMEMBER_STORE.spec.ts b/packages/client/lib/commands/GEORADIUSBYMEMBER_STORE.spec.ts new file mode 100644 index 00000000000..3d44060f205 --- /dev/null +++ b/packages/client/lib/commands/GEORADIUSBYMEMBER_STORE.spec.ts @@ -0,0 +1,39 @@ +import { strict as assert } from 'node:assert'; +import testUtils, { GLOBAL } from '../test-utils'; +import GEORADIUSBYMEMBER_STORE from './GEORADIUSBYMEMBER_STORE'; + +describe('GEORADIUSBYMEMBER STORE', () => { + describe('transformArguments', () => { + it('STORE', () => { + assert.deepEqual( + GEORADIUSBYMEMBER_STORE.transformArguments('key', 'member', 3, 'm', 'destination'), + ['GEORADIUSBYMEMBER', 'key', 'member', '3', 'm', 'STORE', 'destination'] + ); + }); + + it('STOREDIST', () => { + assert.deepEqual( + GEORADIUSBYMEMBER_STORE.transformArguments('key', 'member', 3, 'm', 'destination', { + STOREDIST: true + }), + ['GEORADIUSBYMEMBER', 'key', 'member', '3', 'm', 'STOREDIST', 'destination'] + ); + }); + }); + + testUtils.testAll('geoRadiusByMemberStore', async client => { + const [, reply] = await Promise.all([ + client.geoAdd('{tag}source', { + longitude: 1, + latitude: 2, + member: 'member' + }), + client.geoRadiusByMemberStore('{tag}source', 'member', 3, 'm', '{tag}destination') + ]); + + assert.equal(reply, 1); + }, { + client: GLOBAL.SERVERS.OPEN, + cluster: GLOBAL.CLUSTERS.OPEN + }); +}); diff --git a/packages/client/lib/commands/GEORADIUSBYMEMBER_STORE.ts b/packages/client/lib/commands/GEORADIUSBYMEMBER_STORE.ts new file mode 100644 index 00000000000..90419963110 --- /dev/null +++ b/packages/client/lib/commands/GEORADIUSBYMEMBER_STORE.ts @@ -0,0 +1,31 @@ +import { RedisArgument, NumberReply, Command } from '../RESP/types'; +import GEORADIUSBYMEMBER, { transformGeoRadiusByMemberArguments } from './GEORADIUSBYMEMBER'; +import { GeoSearchOptions, GeoUnits } from './GEOSEARCH'; + +export interface GeoRadiusStoreOptions extends GeoSearchOptions { + STOREDIST?: boolean; +} + +export default { + FIRST_KEY_INDEX: GEORADIUSBYMEMBER.FIRST_KEY_INDEX, + IS_READ_ONLY: GEORADIUSBYMEMBER.IS_READ_ONLY, + transformArguments( + key: RedisArgument, + from: RedisArgument, + radius: number, + unit: GeoUnits, + destination: RedisArgument, + options?: GeoRadiusStoreOptions + ) { + const args = transformGeoRadiusByMemberArguments('GEORADIUSBYMEMBER', key, from, radius, unit, options); + + if (options?.STOREDIST) { + args.push('STOREDIST', destination); + } else { + args.push('STORE', destination); + } + + return args; + }, + transformReply: undefined as unknown as () => NumberReply +} as const satisfies Command; diff --git a/packages/client/lib/commands/GEORADIUSBYMEMBER_WITH.spec.ts b/packages/client/lib/commands/GEORADIUSBYMEMBER_WITH.spec.ts index 24bffd9e89f..ffe3b2efff3 100644 --- a/packages/client/lib/commands/GEORADIUSBYMEMBER_WITH.spec.ts +++ b/packages/client/lib/commands/GEORADIUSBYMEMBER_WITH.spec.ts @@ -1,31 +1,44 @@ -import { strict as assert } from 'assert'; +import { strict as assert } from 'node:assert'; import testUtils, { GLOBAL } from '../test-utils'; -import { RedisCommandArguments } from '.'; -import { GeoReplyWith } from './generic-transformers'; -import { transformArguments } from './GEORADIUSBYMEMBER_WITH'; +import GEORADIUSBYMEMBER_WITH from './GEORADIUSBYMEMBER_WITH'; +import { CommandArguments } from '../RESP/types'; +import { GEO_REPLY_WITH } from './GEOSEARCH_WITH'; describe('GEORADIUSBYMEMBER WITH', () => { - it('transformArguments', () => { - const expectedReply: RedisCommandArguments = ['GEORADIUSBYMEMBER', 'key', 'member', '3', 'm', 'WITHDIST']; - expectedReply.preserve = ['WITHDIST']; + it('transformArguments', () => { + const expectedReply: CommandArguments = ['GEORADIUSBYMEMBER', 'key', 'member', '3', 'm', 'WITHDIST']; + expectedReply.preserve = ['WITHDIST']; - assert.deepEqual( - transformArguments('key', 'member', 3 , 'm', [GeoReplyWith.DISTANCE]), - expectedReply - ); - }); + assert.deepEqual( + GEORADIUSBYMEMBER_WITH.transformArguments('key', 'member', 3, 'm', [ + GEO_REPLY_WITH.DISTANCE + ]), + expectedReply + ); + }); - testUtils.testWithClient('client.geoRadiusByMemberWith', async client => { - assert.deepEqual( - await client.geoRadiusByMemberWith('key', 'member', 3 , 'm', [GeoReplyWith.DISTANCE]), - [] - ); - }, GLOBAL.SERVERS.OPEN); + testUtils.testAll('geoRadiusByMemberWith', async client => { + const [, reply] = await Promise.all([ + client.geoAdd('key', { + member: 'member', + longitude: 1, + latitude: 2 + }), + client.geoRadiusByMemberWith('key', 'member', 1, 'm', [ + GEO_REPLY_WITH.HASH, + GEO_REPLY_WITH.DISTANCE, + GEO_REPLY_WITH.COORDINATES + ]) + ]); - testUtils.testWithCluster('cluster.geoRadiusByMemberWith', async cluster => { - assert.deepEqual( - await cluster.geoRadiusByMemberWith('key', 'member', 3 , 'm', [GeoReplyWith.DISTANCE]), - [] - ); - }, GLOBAL.CLUSTERS.OPEN); + assert.equal(reply.length, 1); + assert.equal(reply[0].member, 'member'); + assert.equal(typeof reply[0].distance, 'string'); + assert.equal(typeof reply[0].hash, 'number'); + assert.equal(typeof reply[0].coordinates!.longitude, 'string'); + assert.equal(typeof reply[0].coordinates!.latitude, 'string'); + }, { + client: GLOBAL.SERVERS.OPEN, + cluster: GLOBAL.CLUSTERS.OPEN + }); }); diff --git a/packages/client/lib/commands/GEORADIUSBYMEMBER_WITH.ts b/packages/client/lib/commands/GEORADIUSBYMEMBER_WITH.ts index 7d7dbe06a54..be9472a438f 100644 --- a/packages/client/lib/commands/GEORADIUSBYMEMBER_WITH.ts +++ b/packages/client/lib/commands/GEORADIUSBYMEMBER_WITH.ts @@ -1,30 +1,36 @@ -import { RedisCommandArgument, RedisCommandArguments } from '.'; -import { GeoReplyWith, GeoSearchOptions, GeoUnits } from './generic-transformers'; -import { transformArguments as transformGeoRadiusArguments } from './GEORADIUSBYMEMBER'; +import { RedisArgument, CommandArguments, Command } from '../RESP/types'; +import GEORADIUSBYMEMBER from './GEORADIUSBYMEMBER'; +import { GeoSearchOptions, GeoUnits, pushGeoSearchOptions } from './GEOSEARCH'; +import GEOSEARCH_WITH, { GeoReplyWith } from './GEOSEARCH_WITH'; -export { FIRST_KEY_INDEX, IS_READ_ONLY } from './GEORADIUSBYMEMBER'; +export function transformGeoRadiusByMemberWithArguments( + command: RedisArgument, + key: RedisArgument, + from: RedisArgument, + radius: number, + unit: GeoUnits, + replyWith: Array, + options?: GeoSearchOptions +) { + const args: CommandArguments = [ + command, + key, + from, + radius.toString(), + unit + ]; -export function transformArguments( - key: RedisCommandArgument, - member: string, - radius: number, - unit: GeoUnits, - replyWith: Array, - options?: GeoSearchOptions -): RedisCommandArguments { - const args: RedisCommandArguments = transformGeoRadiusArguments( - key, - member, - radius, - unit, - options - ); + pushGeoSearchOptions(args, options); - args.push(...replyWith); + args.push(...replyWith); + args.preserve = replyWith; - args.preserve = replyWith; - - return args; + return args; } -export { transformGeoMembersWithReply as transformReply } from './generic-transformers'; +export default { + FIRST_KEY_INDEX: GEORADIUSBYMEMBER.FIRST_KEY_INDEX, + IS_READ_ONLY: GEORADIUSBYMEMBER.IS_READ_ONLY, + transformArguments: transformGeoRadiusByMemberWithArguments.bind(undefined, 'GEORADIUSBYMEMBER'), + transformReply: GEOSEARCH_WITH.transformReply +} as const satisfies Command; \ No newline at end of file diff --git a/packages/client/lib/commands/GEORADIUSSTORE.spec.ts b/packages/client/lib/commands/GEORADIUSSTORE.spec.ts deleted file mode 100644 index 4c6372732e5..00000000000 --- a/packages/client/lib/commands/GEORADIUSSTORE.spec.ts +++ /dev/null @@ -1,53 +0,0 @@ -import { strict as assert } from 'assert'; -import testUtils, { GLOBAL } from '../test-utils'; -import { transformArguments, transformReply } from './GEORADIUSSTORE'; - -describe('GEORADIUSSTORE', () => { - describe('transformArguments', () => { - it('STORE', () => { - assert.deepEqual( - transformArguments('key', {longitude: 1, latitude: 2}, 3 , 'm', 'dest', { - SORT: 'ASC', - COUNT: { - value: 1, - ANY: true - } - }), - ['GEORADIUS', 'key', '1', '2', '3', 'm', 'ASC', 'COUNT', '1', 'ANY', 'STORE', 'dest'] - ); - }); - - it('STOREDIST', () => { - assert.deepEqual( - transformArguments('key', {longitude: 1, latitude: 2}, 3 , 'm', 'dest', { STOREDIST: true }), - ['GEORADIUS', 'key', '1', '2', '3', 'm', 'STOREDIST', 'dest'] - ); - }); - }); - - testUtils.testWithClient('client.geoRadiusStore', async client => { - await client.geoAdd('source', { - longitude: 1, - latitude: 1, - member: 'member' - }); - - assert.equal( - await client.geoRadiusStore('source', {longitude: 1, latitude: 1}, 3 , 'm', 'dest'), - 1 - ); - }, GLOBAL.SERVERS.OPEN); - - testUtils.testWithCluster('cluster.geoRadiusStore', async cluster => { - await cluster.geoAdd('{tag}source', { - longitude: 1, - latitude: 1, - member: 'member' - }); - - assert.equal( - await cluster.geoRadiusStore('{tag}source', {longitude: 1, latitude: 1}, 3 , 'm', '{tag}destination'), - 1 - ); - }, GLOBAL.CLUSTERS.OPEN); -}); diff --git a/packages/client/lib/commands/GEORADIUSSTORE.ts b/packages/client/lib/commands/GEORADIUSSTORE.ts deleted file mode 100644 index ad2317aa3af..00000000000 --- a/packages/client/lib/commands/GEORADIUSSTORE.ts +++ /dev/null @@ -1,25 +0,0 @@ -import { RedisCommandArgument, RedisCommandArguments } from '.'; -import { GeoCoordinates, GeoUnits, GeoRadiusStoreOptions, pushGeoRadiusStoreArguments } from './generic-transformers'; - -export { FIRST_KEY_INDEX, IS_READ_ONLY } from './GEORADIUS'; - -export function transformArguments( - key: RedisCommandArgument, - coordinates: GeoCoordinates, - radius: number, - unit: GeoUnits, - destination: RedisCommandArgument, - options?: GeoRadiusStoreOptions, -): RedisCommandArguments { - return pushGeoRadiusStoreArguments( - ['GEORADIUS'], - key, - coordinates, - radius, - unit, - destination, - options - ); -} - -export declare function transformReply(): number; diff --git a/packages/client/lib/commands/GEORADIUS_RO.spec.ts b/packages/client/lib/commands/GEORADIUS_RO.spec.ts index b3cdca18d3f..43a2ef1d583 100644 --- a/packages/client/lib/commands/GEORADIUS_RO.spec.ts +++ b/packages/client/lib/commands/GEORADIUS_RO.spec.ts @@ -1,35 +1,28 @@ -import { strict as assert } from 'assert'; +import { strict as assert } from 'node:assert'; import testUtils, { GLOBAL } from '../test-utils'; -import { transformArguments } from './GEORADIUS_RO'; +import GEORADIUS_RO from './GEORADIUS_RO'; describe('GEORADIUS_RO', () => { - it('transformArguments', () => { - assert.deepEqual( - transformArguments('key', { - longitude: 1, - latitude: 2 - }, 3 , 'm'), - ['GEORADIUS_RO', 'key', '1', '2', '3', 'm'] - ); - }); + it('transformArguments', () => { + assert.deepEqual( + GEORADIUS_RO.transformArguments('key', { + longitude: 1, + latitude: 2 + }, 3, 'm'), + ['GEORADIUS_RO', 'key', '1', '2', '3', 'm'] + ); + }); - testUtils.testWithClient('client.geoRadiusRo', async client => { - assert.deepEqual( - await client.geoRadiusRo('key', { - longitude: 1, - latitude: 2 - }, 3 , 'm'), - [] - ); - }, GLOBAL.SERVERS.OPEN); - - testUtils.testWithCluster('cluster.geoRadiusRo', async cluster => { - assert.deepEqual( - await cluster.geoRadiusRo('key', { - longitude: 1, - latitude: 2 - }, 3 , 'm'), - [] - ); - }, GLOBAL.CLUSTERS.OPEN); + testUtils.testAll('geoRadiusRo', async client => { + assert.deepEqual( + await client.geoRadiusRo('key', { + longitude: 1, + latitude: 2 + }, 3, 'm'), + [] + ); + }, { + client: GLOBAL.SERVERS.OPEN, + cluster: GLOBAL.CLUSTERS.OPEN + }); }); diff --git a/packages/client/lib/commands/GEORADIUS_RO.ts b/packages/client/lib/commands/GEORADIUS_RO.ts index ac378a5150b..be8bb4b530d 100644 --- a/packages/client/lib/commands/GEORADIUS_RO.ts +++ b/packages/client/lib/commands/GEORADIUS_RO.ts @@ -1,25 +1,9 @@ -import { RedisCommandArgument, RedisCommandArguments } from '.'; -import { GeoSearchOptions, GeoCoordinates, pushGeoRadiusArguments, GeoUnits } from './generic-transformers'; - -export const FIRST_KEY_INDEX = 1; - -export const IS_READ_ONLY = true; - -export function transformArguments( - key: RedisCommandArgument, - coordinates: GeoCoordinates, - radius: number, - unit: GeoUnits, - options?: GeoSearchOptions -): RedisCommandArguments { - return pushGeoRadiusArguments( - ['GEORADIUS_RO'], - key, - coordinates, - radius, - unit, - options - ); -} - -export declare function transformReply(): Array; +import { Command } from '../RESP/types'; +import GEORADIUS, { transformGeoRadiusArguments } from './GEORADIUS'; + +export default { + FIRST_KEY_INDEX: GEORADIUS.FIRST_KEY_INDEX, + IS_READ_ONLY: true, + transformArguments: transformGeoRadiusArguments.bind(undefined, 'GEORADIUS_RO'), + transformReply: GEORADIUS.transformReply +} as const satisfies Command; diff --git a/packages/client/lib/commands/GEORADIUS_RO_WITH.spec.ts b/packages/client/lib/commands/GEORADIUS_RO_WITH.spec.ts index 21b00ff90b8..cb0540d8a18 100644 --- a/packages/client/lib/commands/GEORADIUS_RO_WITH.spec.ts +++ b/packages/client/lib/commands/GEORADIUS_RO_WITH.spec.ts @@ -1,40 +1,48 @@ -import { strict as assert } from 'assert'; +import { strict as assert } from 'node:assert'; import testUtils, { GLOBAL } from '../test-utils'; -import { RedisCommandArguments } from '.'; -import { GeoReplyWith } from './generic-transformers'; -import { transformArguments } from './GEORADIUS_RO_WITH'; +import GEORADIUS_RO_WITH from './GEORADIUS_RO_WITH'; +import { GEO_REPLY_WITH } from './GEOSEARCH_WITH'; +import { CommandArguments } from '../RESP/types'; describe('GEORADIUS_RO WITH', () => { - it('transformArguments', () => { - const expectedReply: RedisCommandArguments = ['GEORADIUS_RO', 'key', '1', '2', '3', 'm', 'WITHDIST']; - expectedReply.preserve = ['WITHDIST']; + it('transformArguments', () => { + const expectedReply: CommandArguments = ['GEORADIUS_RO', 'key', '1', '2', '3', 'm', 'WITHDIST']; + expectedReply.preserve = ['WITHDIST']; - assert.deepEqual( - transformArguments('key', { - longitude: 1, - latitude: 2 - }, 3 , 'm', [GeoReplyWith.DISTANCE]), - expectedReply - ); - }); + assert.deepEqual( + GEORADIUS_RO_WITH.transformArguments('key', { + longitude: 1, + latitude: 2 + }, 3, 'm', [GEO_REPLY_WITH.DISTANCE]), + expectedReply + ); + }); - testUtils.testWithClient('client.geoRadiusRoWith', async client => { - assert.deepEqual( - await client.geoRadiusRoWith('key', { - longitude: 1, - latitude: 2 - }, 3 , 'm', [GeoReplyWith.DISTANCE]), - [] - ); - }, GLOBAL.SERVERS.OPEN); + testUtils.testAll('geoRadiusRoWith', async client => { + const [, reply] = await Promise.all([ + client.geoAdd('key', { + member: 'member', + longitude: 1, + latitude: 2 + }), + client.geoRadiusRoWith('key', { + longitude: 1, + latitude: 2 + }, 1, 'm', [ + GEO_REPLY_WITH.HASH, + GEO_REPLY_WITH.DISTANCE, + GEO_REPLY_WITH.COORDINATES + ]) + ]); - testUtils.testWithCluster('cluster.geoRadiusReadOnlyWith', async cluster => { - assert.deepEqual( - await cluster.geoRadiusRoWith('key', { - longitude: 1, - latitude: 2 - }, 3 , 'm', [GeoReplyWith.DISTANCE]), - [] - ); - }, GLOBAL.CLUSTERS.OPEN); + assert.equal(reply.length, 1); + assert.equal(reply[0].member, 'member'); + assert.equal(typeof reply[0].distance, 'string'); + assert.equal(typeof reply[0].hash, 'number'); + assert.equal(typeof reply[0].coordinates!.longitude, 'string'); + assert.equal(typeof reply[0].coordinates!.latitude, 'string'); + }, { + client: GLOBAL.SERVERS.OPEN, + cluster: GLOBAL.CLUSTERS.OPEN + }); }); diff --git a/packages/client/lib/commands/GEORADIUS_RO_WITH.ts b/packages/client/lib/commands/GEORADIUS_RO_WITH.ts index 424e5fcd998..37cf594ce9d 100644 --- a/packages/client/lib/commands/GEORADIUS_RO_WITH.ts +++ b/packages/client/lib/commands/GEORADIUS_RO_WITH.ts @@ -1,30 +1,9 @@ -import { RedisCommandArgument, RedisCommandArguments } from '.'; -import { GeoReplyWith, GeoSearchOptions, GeoCoordinates, GeoUnits } from './generic-transformers'; -import { transformArguments as transformGeoRadiusRoArguments } from './GEORADIUS_RO'; - -export { FIRST_KEY_INDEX, IS_READ_ONLY } from './GEORADIUS_RO'; - -export function transformArguments( - key: RedisCommandArgument, - coordinates: GeoCoordinates, - radius: number, - unit: GeoUnits, - replyWith: Array, - options?: GeoSearchOptions -): RedisCommandArguments { - const args: RedisCommandArguments = transformGeoRadiusRoArguments( - key, - coordinates, - radius, - unit, - options - ); - - args.push(...replyWith); - - args.preserve = replyWith; - - return args; -} - -export { transformGeoMembersWithReply as transformReply } from './generic-transformers'; +import { Command } from '../RESP/types'; +import GEORADIUS_WITH, { transformGeoRadiusWithArguments } from './GEORADIUS_WITH'; + +export default { + FIRST_KEY_INDEX: GEORADIUS_WITH.FIRST_KEY_INDEX, + IS_READ_ONLY: true, + transformArguments: transformGeoRadiusWithArguments.bind(undefined, 'GEORADIUS_RO'), + transformReply: GEORADIUS_WITH.transformReply +} as const satisfies Command; diff --git a/packages/client/lib/commands/GEORADIUS_STORE.spec.ts b/packages/client/lib/commands/GEORADIUS_STORE.spec.ts new file mode 100644 index 00000000000..04a7d28aa95 --- /dev/null +++ b/packages/client/lib/commands/GEORADIUS_STORE.spec.ts @@ -0,0 +1,48 @@ +import { strict as assert } from 'node:assert'; +import testUtils, { GLOBAL } from '../test-utils'; +import GEORADIUS_STORE from './GEORADIUS_STORE'; + +describe('GEORADIUS STORE', () => { + describe('transformArguments', () => { + it('STORE', () => { + assert.deepEqual( + GEORADIUS_STORE.transformArguments('key', { + longitude: 1, + latitude: 2 + }, 3, 'm', 'destination'), + ['GEORADIUS', 'key', '1', '2', '3', 'm', 'STORE', 'destination'] + ); + }); + + it('STOREDIST', () => { + assert.deepEqual( + GEORADIUS_STORE.transformArguments('key', { + longitude: 1, + latitude: 2 + }, 3, 'm', 'destination', { + STOREDIST: true + }), + ['GEORADIUS', 'key', '1', '2', '3', 'm', 'STOREDIST', 'destination'] + ); + }); + }); + + testUtils.testAll('geoRadiusStore', async client => { + const [, reply] = await Promise.all([ + client.geoAdd('{tag}source', { + longitude: 1, + latitude: 2, + member: 'member' + }), + client.geoRadiusStore('{tag}source', { + longitude: 1, + latitude: 2 + }, 1, 'm', '{tag}destination') + ]); + + assert.equal(reply, 1); + }, { + client: GLOBAL.SERVERS.OPEN, + cluster: GLOBAL.CLUSTERS.OPEN + }); +}); diff --git a/packages/client/lib/commands/GEORADIUS_STORE.ts b/packages/client/lib/commands/GEORADIUS_STORE.ts new file mode 100644 index 00000000000..3a553ebf8be --- /dev/null +++ b/packages/client/lib/commands/GEORADIUS_STORE.ts @@ -0,0 +1,31 @@ +import { RedisArgument, NumberReply, Command } from '../RESP/types'; +import GEORADIUS, { transformGeoRadiusArguments } from './GEORADIUS'; +import { GeoCoordinates, GeoSearchOptions, GeoUnits } from './GEOSEARCH'; + +export interface GeoRadiusStoreOptions extends GeoSearchOptions { + STOREDIST?: boolean; +} + +export default { + FIRST_KEY_INDEX: GEORADIUS.FIRST_KEY_INDEX, + IS_READ_ONLY: GEORADIUS.IS_READ_ONLY, + transformArguments( + key: RedisArgument, + from: GeoCoordinates, + radius: number, + unit: GeoUnits, + destination: RedisArgument, + options?: GeoRadiusStoreOptions + ) { + const args = transformGeoRadiusArguments('GEORADIUS', key, from, radius, unit, options); + + if (options?.STOREDIST) { + args.push('STOREDIST', destination); + } else { + args.push('STORE', destination); + } + + return args; + }, + transformReply: undefined as unknown as () => NumberReply +} as const satisfies Command; diff --git a/packages/client/lib/commands/GEORADIUS_WITH.spec.ts b/packages/client/lib/commands/GEORADIUS_WITH.spec.ts index 44366198beb..bdbfc9c1f3a 100644 --- a/packages/client/lib/commands/GEORADIUS_WITH.spec.ts +++ b/packages/client/lib/commands/GEORADIUS_WITH.spec.ts @@ -1,40 +1,48 @@ -import { strict as assert } from 'assert'; +import { strict as assert } from 'node:assert'; import testUtils, { GLOBAL } from '../test-utils'; -import { RedisCommandArguments } from '.'; -import { GeoReplyWith } from './generic-transformers'; -import { transformArguments } from './GEORADIUS_WITH'; +import GEORADIUS_WITH from './GEORADIUS_WITH'; +import { GEO_REPLY_WITH } from './GEOSEARCH_WITH'; +import { CommandArguments } from '../RESP/types'; describe('GEORADIUS WITH', () => { - it('transformArguments', () => { - const expectedReply: RedisCommandArguments = ['GEORADIUS', 'key', '1', '2', '3', 'm', 'WITHDIST']; - expectedReply.preserve = ['WITHDIST']; + it('transformArguments', () => { + const expectedReply: CommandArguments = ['GEORADIUS', 'key', '1', '2', '3', 'm', 'WITHDIST']; + expectedReply.preserve = ['WITHDIST']; - assert.deepEqual( - transformArguments('key', { - longitude: 1, - latitude: 2 - }, 3 , 'm', [GeoReplyWith.DISTANCE]), - expectedReply - ); - }); + assert.deepEqual( + GEORADIUS_WITH.transformArguments('key', { + longitude: 1, + latitude: 2 + }, 3, 'm', [GEO_REPLY_WITH.DISTANCE]), + expectedReply + ); + }); - testUtils.testWithClient('client.geoRadiusWith', async client => { - assert.deepEqual( - await client.geoRadiusWith('key', { - longitude: 1, - latitude: 2 - }, 3 , 'm', [GeoReplyWith.DISTANCE]), - [] - ); - }, GLOBAL.SERVERS.OPEN); + testUtils.testAll('geoRadiusWith', async client => { + const [, reply] = await Promise.all([ + client.geoAdd('key', { + member: 'member', + longitude: 1, + latitude: 2 + }), + client.geoRadiusWith('key', { + longitude: 1, + latitude: 2 + }, 1, 'm', [ + GEO_REPLY_WITH.HASH, + GEO_REPLY_WITH.DISTANCE, + GEO_REPLY_WITH.COORDINATES + ]) + ]); - testUtils.testWithCluster('cluster.geoRadiusWith', async cluster => { - assert.deepEqual( - await cluster.geoRadiusWith('key', { - longitude: 1, - latitude: 2 - }, 3 , 'm', [GeoReplyWith.DISTANCE]), - [] - ); - }, GLOBAL.CLUSTERS.OPEN); + assert.equal(reply.length, 1); + assert.equal(reply[0].member, 'member'); + assert.equal(typeof reply[0].distance, 'string'); + assert.equal(typeof reply[0].hash, 'number'); + assert.equal(typeof reply[0].coordinates?.longitude, 'string'); + assert.equal(typeof reply[0].coordinates?.latitude, 'string'); + }, { + client: GLOBAL.SERVERS.OPEN, + cluster: GLOBAL.CLUSTERS.OPEN + }); }); diff --git a/packages/client/lib/commands/GEORADIUS_WITH.ts b/packages/client/lib/commands/GEORADIUS_WITH.ts index dc3f4288f01..d72d8d49322 100644 --- a/packages/client/lib/commands/GEORADIUS_WITH.ts +++ b/packages/client/lib/commands/GEORADIUS_WITH.ts @@ -1,30 +1,33 @@ -import { RedisCommandArgument, RedisCommandArguments } from '.'; -import { GeoReplyWith, GeoSearchOptions, GeoCoordinates, GeoUnits } from './generic-transformers'; -import { transformArguments as transformGeoRadiusArguments } from './GEORADIUS'; +import { CommandArguments, Command, RedisArgument } from '../RESP/types'; +import GEORADIUS, { transformGeoRadiusArguments } from './GEORADIUS'; +import { GeoCoordinates, GeoSearchOptions, GeoUnits } from './GEOSEARCH'; +import GEOSEARCH_WITH, { GeoReplyWith } from './GEOSEARCH_WITH'; -export { FIRST_KEY_INDEX, IS_READ_ONLY } from './GEORADIUS'; - -export function transformArguments( - key: RedisCommandArgument, - coordinates: GeoCoordinates, - radius: number, - unit: GeoUnits, - replyWith: Array, - options?: GeoSearchOptions -): RedisCommandArguments { - const args: RedisCommandArguments = transformGeoRadiusArguments( - key, - coordinates, - radius, - unit, - options - ); - - args.push(...replyWith); - - args.preserve = replyWith; - - return args; +export function transformGeoRadiusWithArguments( + command: RedisArgument, + key: RedisArgument, + from: GeoCoordinates, + radius: number, + unit: GeoUnits, + replyWith: Array, + options?: GeoSearchOptions +) { + const args: CommandArguments = transformGeoRadiusArguments( + command, + key, + from, + radius, + unit, + options + ); + args.push(...replyWith); + args.preserve = replyWith; + return args; } -export { transformGeoMembersWithReply as transformReply } from './generic-transformers'; +export default { + FIRST_KEY_INDEX: GEORADIUS.FIRST_KEY_INDEX, + IS_READ_ONLY: GEORADIUS.IS_READ_ONLY, + transformArguments: transformGeoRadiusWithArguments.bind(undefined, 'GEORADIUS'), + transformReply: GEOSEARCH_WITH.transformReply +} as const satisfies Command; diff --git a/packages/client/lib/commands/GEOSEARCH.spec.ts b/packages/client/lib/commands/GEOSEARCH.spec.ts index ec0d4bcc4f8..49f076880a6 100644 --- a/packages/client/lib/commands/GEOSEARCH.spec.ts +++ b/packages/client/lib/commands/GEOSEARCH.spec.ts @@ -1,37 +1,87 @@ -import { strict as assert } from 'assert'; +import { strict as assert } from 'node:assert'; import testUtils, { GLOBAL } from '../test-utils'; -import { transformArguments } from './GEOSEARCH'; +import GEOSEARCH from './GEOSEARCH'; describe('GEOSEARCH', () => { - testUtils.isVersionGreaterThanHook([6, 2]); + testUtils.isVersionGreaterThanHook([6, 2]); - it('transformArguments', () => { - assert.deepEqual( - transformArguments('key', 'member', { - radius: 1, - unit: 'm' - }), - ['GEOSEARCH', 'key', 'FROMMEMBER', 'member', 'BYRADIUS', '1', 'm'] - ); + describe('transformArguments', () => { + it('FROMMEMBER, BYRADIUS, without options', () => { + assert.deepEqual( + GEOSEARCH.transformArguments('key', 'member', { + radius: 1, + unit: 'm' + }), + ['GEOSEARCH', 'key', 'FROMMEMBER', 'member', 'BYRADIUS', '1', 'm'] + ); + }); + + it('FROMLONLAT, BYBOX, without options', () => { + assert.deepEqual( + GEOSEARCH.transformArguments('key', { + longitude: 1, + latitude: 2 + }, { + width: 1, + height: 2, + unit: 'm' + }), + ['GEOSEARCH', 'key', 'FROMLONLAT', '1', '2', 'BYBOX', '1', '2', 'm'] + ); }); - testUtils.testWithClient('client.geoSearch', async client => { + it('with SORT', () => { + assert.deepEqual( + GEOSEARCH.transformArguments('key', 'member', { + radius: 1, + unit: 'm' + }, { + SORT: 'ASC' + }), + ['GEOSEARCH', 'key', 'FROMMEMBER', 'member', 'BYRADIUS', '1', 'm', 'ASC'] + ); + }); + + describe('with COUNT', () => { + it('number', () => { assert.deepEqual( - await client.geoSearch('key', 'member', { - radius: 1, - unit: 'm' - }), - [] + GEOSEARCH.transformArguments('key', 'member', { + radius: 1, + unit: 'm' + }, { + COUNT: 1 + }), + ['GEOSEARCH', 'key', 'FROMMEMBER', 'member', 'BYRADIUS', '1', 'm', 'COUNT', '1'] ); - }, GLOBAL.SERVERS.OPEN); + }); - testUtils.testWithCluster('cluster.geoSearch', async cluster => { + it('with ANY', () => { assert.deepEqual( - await cluster.geoSearch('key', 'member', { - radius: 1, - unit: 'm' - }), - [] + GEOSEARCH.transformArguments('key', 'member', { + radius: 1, + unit: 'm' + }, { + COUNT: { + value: 1, + ANY: true + } + }), + ['GEOSEARCH', 'key', 'FROMMEMBER', 'member', 'BYRADIUS', '1', 'm', 'COUNT', '1', 'ANY'] ); - }, GLOBAL.CLUSTERS.OPEN); + }); + }); + }); + + testUtils.testAll('geoSearch', async client => { + assert.deepEqual( + await client.geoSearch('key', 'member', { + radius: 1, + unit: 'm' + }), + [] + ); + }, { + client: GLOBAL.SERVERS.OPEN, + cluster: GLOBAL.CLUSTERS.OPEN + }); }); diff --git a/packages/client/lib/commands/GEOSEARCH.ts b/packages/client/lib/commands/GEOSEARCH.ts index a02a21391f6..c4deaa37e6c 100644 --- a/packages/client/lib/commands/GEOSEARCH.ts +++ b/packages/client/lib/commands/GEOSEARCH.ts @@ -1,17 +1,94 @@ -import { RedisCommandArgument, RedisCommandArguments } from '.'; -import { GeoSearchFrom, GeoSearchBy, GeoSearchOptions, pushGeoSearchArguments } from './generic-transformers'; +import { RedisArgument, CommandArguments, ArrayReply, BlobStringReply, Command } from '../RESP/types'; -export const FIRST_KEY_INDEX = 1; +export type GeoUnits = 'm' | 'km' | 'mi' | 'ft'; -export const IS_READ_ONLY = true; +export interface GeoCoordinates { + longitude: RedisArgument | number; + latitude: RedisArgument | number; +} + +export type GeoSearchFrom = RedisArgument | GeoCoordinates; + +export interface GeoSearchByRadius { + radius: number; + unit: GeoUnits; +} + +export interface GeoSearchByBox { + width: number; + height: number; + unit: GeoUnits; +} + +export type GeoSearchBy = GeoSearchByRadius | GeoSearchByBox; + +export function pushGeoSearchArguments( + args: CommandArguments, + key: RedisArgument, + from: GeoSearchFrom, + by: GeoSearchBy, + options?: GeoSearchOptions +) { + args.push(key); + + if (typeof from === 'string' || from instanceof Buffer) { + args.push('FROMMEMBER', from); + } else { + args.push('FROMLONLAT', from.longitude.toString(), from.latitude.toString()); + } + + if ('radius' in by) { + args.push('BYRADIUS', by.radius.toString(), by.unit); + } else { + args.push('BYBOX', by.width.toString(), by.height.toString(), by.unit); + } + + pushGeoSearchOptions(args, options); -export function transformArguments( - key: RedisCommandArgument, + return args; +} + +export type GeoCountArgument = number | { + value: number; + ANY?: boolean; +}; + +export interface GeoSearchOptions { + SORT?: 'ASC' | 'DESC'; + COUNT?: GeoCountArgument; +} + +export function pushGeoSearchOptions( + args: CommandArguments, + options?: GeoSearchOptions +) { + if (options?.SORT) { + args.push(options.SORT); + } + + if (options?.COUNT) { + if (typeof options.COUNT === 'number') { + args.push('COUNT', options.COUNT.toString()); + } else { + args.push('COUNT', options.COUNT.value.toString()); + + if (options.COUNT.ANY) { + args.push('ANY'); + } + } + } +} + +export default { + FIRST_KEY_INDEX: 1, + IS_READ_ONLY: true, + transformArguments( + key: RedisArgument, from: GeoSearchFrom, by: GeoSearchBy, options?: GeoSearchOptions -): RedisCommandArguments { + ) { return pushGeoSearchArguments(['GEOSEARCH'], key, from, by, options); -} - -export declare function transformReply(): Array; + }, + transformReply: undefined as unknown as () => ArrayReply +} as const satisfies Command; diff --git a/packages/client/lib/commands/GEOSEARCHSTORE.spec.ts b/packages/client/lib/commands/GEOSEARCHSTORE.spec.ts index eb32fa134e4..c66d3e8e45e 100644 --- a/packages/client/lib/commands/GEOSEARCHSTORE.spec.ts +++ b/packages/client/lib/commands/GEOSEARCHSTORE.spec.ts @@ -1,81 +1,44 @@ -import { strict as assert } from 'assert'; +import { strict as assert } from 'node:assert'; import testUtils, { GLOBAL } from '../test-utils'; -import { transformArguments, transformReply } from './GEOSEARCHSTORE'; +import GEOSEARCHSTORE from './GEOSEARCHSTORE'; describe('GEOSEARCHSTORE', () => { - testUtils.isVersionGreaterThanHook([6, 2]); - - describe('transformArguments', () => { - it('simple', () => { - assert.deepEqual( - transformArguments('destination', 'source', 'member', { - radius: 1, - unit: 'm' - }, { - SORT: 'ASC', - COUNT: { - value: 1, - ANY: true - } - }), - ['GEOSEARCHSTORE', 'destination', 'source', 'FROMMEMBER', 'member', 'BYRADIUS', '1', 'm', 'ASC', 'COUNT', '1', 'ANY'] - ); - }); - - it('with STOREDIST', () => { - assert.deepEqual( - transformArguments('destination', 'source', 'member', { - radius: 1, - unit: 'm' - }, { - SORT: 'ASC', - COUNT: { - value: 1, - ANY: true - }, - STOREDIST: true - }), - ['GEOSEARCHSTORE', 'destination', 'source', 'FROMMEMBER', 'member', 'BYRADIUS', '1', 'm', 'ASC', 'COUNT', '1', 'ANY', 'STOREDIST'] - ); - }); + testUtils.isVersionGreaterThanHook([6, 2]); + + describe('transformArguments', () => { + it('simple', () => { + assert.deepEqual( + GEOSEARCHSTORE.transformArguments('source', 'destination', 'member', { + radius: 1, + unit: 'm' + }), + ['GEOSEARCHSTORE', 'source', 'destination', 'FROMMEMBER', 'member', 'BYRADIUS', '1', 'm'] + ); }); - it('transformReply with empty array (https://github.com/redis/redis/issues/9261)', () => { - assert.throws( - () => (transformReply as any)([]), - TypeError - ); + it('with STOREDIST', () => { + assert.deepEqual( + GEOSEARCHSTORE.transformArguments('destination', 'source', 'member', { + radius: 1, + unit: 'm' + }, { + STOREDIST: true + }), + ['GEOSEARCHSTORE', 'destination', 'source', 'FROMMEMBER', 'member', 'BYRADIUS', '1', 'm', 'STOREDIST'] + ); }); - - testUtils.testWithClient('client.geoSearchStore', async client => { - await client.geoAdd('source', { - longitude: 1, - latitude: 1, - member: 'member' - }); - - assert.equal( - await client.geoSearchStore('destination', 'source', 'member', { - radius: 1, - unit: 'm' - }), - 1 - ); - }, GLOBAL.SERVERS.OPEN); - - testUtils.testWithCluster('cluster.geoSearchStore', async cluster => { - await cluster.geoAdd('{tag}source', { - longitude: 1, - latitude: 1, - member: 'member' - }); - - assert.equal( - await cluster.geoSearchStore('{tag}destination', '{tag}source', 'member', { - radius: 1, - unit: 'm' - }), - 1 - ); - }, GLOBAL.CLUSTERS.OPEN); + }); + + testUtils.testAll('geoSearchStore', async client => { + assert.equal( + await client.geoSearchStore('{tag}destination', '{tag}source', 'member', { + radius: 1, + unit: 'm' + }), + 0 + ); + }, { + client: GLOBAL.SERVERS.OPEN, + cluster: GLOBAL.CLUSTERS.OPEN + }); }); diff --git a/packages/client/lib/commands/GEOSEARCHSTORE.ts b/packages/client/lib/commands/GEOSEARCHSTORE.ts index 7a91450cd9e..15635560217 100644 --- a/packages/client/lib/commands/GEOSEARCHSTORE.ts +++ b/packages/client/lib/commands/GEOSEARCHSTORE.ts @@ -1,38 +1,27 @@ -import { RedisCommandArgument, RedisCommandArguments } from '.'; -import { GeoSearchFrom, GeoSearchBy, GeoSearchOptions, pushGeoSearchArguments } from './generic-transformers'; +import { RedisArgument, NumberReply, Command } from '../RESP/types'; +import { GeoSearchFrom, GeoSearchBy, GeoSearchOptions, pushGeoSearchArguments } from './GEOSEARCH'; -export { FIRST_KEY_INDEX, IS_READ_ONLY } from './GEOSEARCH'; - -interface GeoSearchStoreOptions extends GeoSearchOptions { - STOREDIST?: true; +export interface GeoSearchStoreOptions extends GeoSearchOptions { + STOREDIST?: boolean; } -export function transformArguments( - destination: RedisCommandArgument, - source: RedisCommandArgument, +export default { + FIRST_KEY_INDEX: 1, + IS_READ_ONLY: false, + transformArguments( + destination: RedisArgument, + source: RedisArgument, from: GeoSearchFrom, by: GeoSearchBy, options?: GeoSearchStoreOptions -): RedisCommandArguments { - const args = pushGeoSearchArguments( - ['GEOSEARCHSTORE', destination], - source, - from, - by, - options - ); + ) { + const args = pushGeoSearchArguments(['GEOSEARCHSTORE', destination], source, from, by, options); if (options?.STOREDIST) { - args.push('STOREDIST'); + args.push('STOREDIST'); } return args; -} - -export function transformReply(reply: number): number { - if (typeof reply !== 'number') { - throw new TypeError(`https://github.com/redis/redis/issues/9261`); - } - - return reply; -} + }, + transformReply: undefined as unknown as () => NumberReply +} as const satisfies Command; diff --git a/packages/client/lib/commands/GEOSEARCH_WITH.spec.ts b/packages/client/lib/commands/GEOSEARCH_WITH.spec.ts index c1f5213775a..e27fb295aaf 100644 --- a/packages/client/lib/commands/GEOSEARCH_WITH.spec.ts +++ b/packages/client/lib/commands/GEOSEARCH_WITH.spec.ts @@ -1,42 +1,49 @@ -import { strict as assert } from 'assert'; +import { strict as assert } from 'node:assert'; import testUtils, { GLOBAL } from '../test-utils'; -import { RedisCommandArguments } from '.'; -import { GeoReplyWith } from './generic-transformers'; -import { transformArguments } from './GEOSEARCH_WITH'; +import GEOSEARCH_WITH, { GEO_REPLY_WITH } from './GEOSEARCH_WITH'; +import { CommandArguments } from '../RESP/types'; describe('GEOSEARCH WITH', () => { - testUtils.isVersionGreaterThanHook([6, 2]); + testUtils.isVersionGreaterThanHook([6, 2]); - it('transformArguments', () => { - const expectedReply: RedisCommandArguments = ['GEOSEARCH', 'key', 'FROMMEMBER', 'member', 'BYRADIUS', '1', 'm', 'WITHDIST']; - expectedReply.preserve = ['WITHDIST']; + it('transformArguments', () => { + const expectedReply: CommandArguments = ['GEOSEARCH', 'key', 'FROMMEMBER', 'member', 'BYRADIUS', '1', 'm', 'WITHDIST']; + expectedReply.preserve = ['WITHDIST']; - assert.deepEqual( - transformArguments('key', 'member', { - radius: 1, - unit: 'm' - }, [GeoReplyWith.DISTANCE]), - expectedReply - ); - }); + assert.deepEqual( + GEOSEARCH_WITH.transformArguments('key', 'member', { + radius: 1, + unit: 'm' + }, [GEO_REPLY_WITH.DISTANCE]), + expectedReply + ); + }); - testUtils.testWithClient('client.geoSearchWith', async client => { - assert.deepEqual( - await client.geoSearchWith('key', 'member', { - radius: 1, - unit: 'm' - }, [GeoReplyWith.DISTANCE]), - [] - ); - }, GLOBAL.SERVERS.OPEN); + testUtils.testAll('.geoSearchWith', async client => { + const [ , reply ] = await Promise.all([ + client.geoAdd('key', { + member: 'member', + longitude: 1, + latitude: 2 + }), + client.geoSearchWith('key', 'member', { + radius: 1, + unit: 'm' + }, [ + GEO_REPLY_WITH.HASH, + GEO_REPLY_WITH.DISTANCE, + GEO_REPLY_WITH.COORDINATES + ]) + ]); - testUtils.testWithCluster('cluster.geoSearchWith', async cluster => { - assert.deepEqual( - await cluster.geoSearchWith('key', 'member', { - radius: 1, - unit: 'm' - }, [GeoReplyWith.DISTANCE]), - [] - ); - }, GLOBAL.CLUSTERS.OPEN); + assert.equal(reply.length, 1); + assert.equal(reply[0].member, 'member'); + assert.equal(typeof reply[0].distance, 'string'); + assert.equal(typeof reply[0].hash, 'number'); + assert.equal(typeof reply[0].coordinates!.longitude, 'string'); + assert.equal(typeof reply[0].coordinates!.latitude, 'string'); + }, { + client: GLOBAL.SERVERS.OPEN, + cluster: GLOBAL.CLUSTERS.OPEN + }); }); diff --git a/packages/client/lib/commands/GEOSEARCH_WITH.ts b/packages/client/lib/commands/GEOSEARCH_WITH.ts index d7a5f456a94..19088230f0f 100644 --- a/packages/client/lib/commands/GEOSEARCH_WITH.ts +++ b/packages/client/lib/commands/GEOSEARCH_WITH.ts @@ -1,23 +1,73 @@ -import { RedisCommandArgument, RedisCommandArguments } from '.'; -import { GeoSearchFrom, GeoSearchBy, GeoReplyWith, GeoSearchOptions } from './generic-transformers'; -import { transformArguments as geoSearchTransformArguments } from './GEOSEARCH'; +import { RedisArgument, ArrayReply, TuplesReply, BlobStringReply, NumberReply, DoubleReply, UnwrapReply, Command } from '../RESP/types'; +import GEOSEARCH, { GeoSearchBy, GeoSearchFrom, GeoSearchOptions } from './GEOSEARCH'; -export { FIRST_KEY_INDEX, IS_READ_ONLY } from './GEOSEARCH'; +export const GEO_REPLY_WITH = { + DISTANCE: 'WITHDIST', + HASH: 'WITHHASH', + COORDINATES: 'WITHCOORD' +} as const; -export function transformArguments( - key: RedisCommandArgument, +export type GeoReplyWith = typeof GEO_REPLY_WITH[keyof typeof GEO_REPLY_WITH]; + +export interface GeoReplyWithMember { + member: BlobStringReply; + distance?: BlobStringReply; + hash?: NumberReply; + coordinates?: { + longitude: DoubleReply; + latitude: DoubleReply; + }; +} + +export default { + FIRST_KEY_INDEX: GEOSEARCH.FIRST_KEY_INDEX, + IS_READ_ONLY: GEOSEARCH.IS_READ_ONLY, + transformArguments( + key: RedisArgument, from: GeoSearchFrom, by: GeoSearchBy, replyWith: Array, options?: GeoSearchOptions -): RedisCommandArguments { - const args: RedisCommandArguments = geoSearchTransformArguments(key, from, by, options); - + ) { + const args = GEOSEARCH.transformArguments(key, from, by, options); args.push(...replyWith); - args.preserve = replyWith; - return args; -} + }, + transformReply( + reply: UnwrapReply]>>>, + replyWith: Array + ) { + const replyWithSet = new Set(replyWith); + let index = 0; + const distanceIndex = replyWithSet.has(GEO_REPLY_WITH.DISTANCE) && ++index, + hashIndex = replyWithSet.has(GEO_REPLY_WITH.HASH) && ++index, + coordinatesIndex = replyWithSet.has(GEO_REPLY_WITH.COORDINATES) && ++index; + + return reply.map(raw => { + const unwrapped = raw as unknown as UnwrapReply; + + const item: GeoReplyWithMember = { + member: unwrapped[0] + }; + + if (distanceIndex) { + item.distance = unwrapped[distanceIndex]; + } + + if (hashIndex) { + item.hash = unwrapped[hashIndex]; + } + + if (coordinatesIndex) { + const [longitude, latitude] = unwrapped[coordinatesIndex]; + item.coordinates = { + longitude, + latitude + }; + } -export { transformGeoMembersWithReply as transformReply } from './generic-transformers'; + return item; + }); + } +} as const satisfies Command; diff --git a/packages/client/lib/commands/GET.spec.ts b/packages/client/lib/commands/GET.spec.ts index 2946ea19b60..4bd74183222 100644 --- a/packages/client/lib/commands/GET.spec.ts +++ b/packages/client/lib/commands/GET.spec.ts @@ -1,33 +1,22 @@ -import { strict as assert } from 'assert'; -import RedisClient from '../client'; +import { strict as assert } from 'node:assert'; import testUtils, { GLOBAL } from '../test-utils'; -import { transformArguments } from './GET'; +import GET from './GET'; describe('GET', () => { - it('transformArguments', () => { - assert.deepEqual( - transformArguments('key'), - ['GET', 'key'] - ); - }); + it('transformArguments', () => { + assert.deepEqual( + GET.transformArguments('key'), + ['GET', 'key'] + ); + }); - testUtils.testWithClient('client.get', async client => { - const a = await client.get( - 'key' - ); - - - - assert.equal( - await client.get('key'), - null - ); - }, GLOBAL.SERVERS.OPEN); - - testUtils.testWithCluster('cluster.get', async cluster => { - assert.equal( - await cluster.get('key'), - null - ); - }, GLOBAL.CLUSTERS.OPEN); + testUtils.testAll('get', async client => { + assert.equal( + await client.get('key'), + null + ); + }, { + client: GLOBAL.SERVERS.OPEN, + cluster: GLOBAL.CLUSTERS.OPEN + }); }); diff --git a/packages/client/lib/commands/GET.ts b/packages/client/lib/commands/GET.ts index 127b0a56349..bb3db4f76d9 100644 --- a/packages/client/lib/commands/GET.ts +++ b/packages/client/lib/commands/GET.ts @@ -1,11 +1,10 @@ -import { RedisCommandArgument, RedisCommandArguments } from '.'; +import { RedisArgument, BlobStringReply, NullReply, Command } from '../RESP/types'; -export const FIRST_KEY_INDEX = 1; - -export const IS_READ_ONLY = true; - -export function transformArguments(key: RedisCommandArgument): RedisCommandArguments { +export default { + FIRST_KEY_INDEX: 1, + IS_READ_ONLY: true, + transformArguments(key: RedisArgument) { return ['GET', key]; -} - -export declare function transformReply(): RedisCommandArgument | null; + }, + transformReply: undefined as unknown as () => BlobStringReply | NullReply +} as const satisfies Command; diff --git a/packages/client/lib/commands/GETBIT.spec.ts b/packages/client/lib/commands/GETBIT.spec.ts index 4206084eced..ac39222b918 100644 --- a/packages/client/lib/commands/GETBIT.spec.ts +++ b/packages/client/lib/commands/GETBIT.spec.ts @@ -1,26 +1,22 @@ -import { strict as assert } from 'assert'; +import { strict as assert } from 'node:assert'; import testUtils, { GLOBAL } from '../test-utils'; -import { transformArguments } from './GETBIT'; +import GETBIT from './GETBIT'; describe('GETBIT', () => { - it('transformArguments', () => { - assert.deepEqual( - transformArguments('key', 0), - ['GETBIT', 'key', '0'] - ); - }); + it('transformArguments', () => { + assert.deepEqual( + GETBIT.transformArguments('key', 0), + ['GETBIT', 'key', '0'] + ); + }); - testUtils.testWithClient('client.getBit', async client => { - assert.equal( - await client.getBit('key', 0), - 0 - ); - }, GLOBAL.SERVERS.OPEN); - - testUtils.testWithCluster('cluster.getBit', async cluster => { - assert.equal( - await cluster.getBit('key', 0), - 0 - ); - }, GLOBAL.CLUSTERS.OPEN); + testUtils.testAll('getBit', async client => { + assert.equal( + await client.getBit('key', 0), + 0 + ); + }, { + client: GLOBAL.SERVERS.OPEN, + cluster: GLOBAL.CLUSTERS.OPEN + }); }); diff --git a/packages/client/lib/commands/GETBIT.ts b/packages/client/lib/commands/GETBIT.ts index 67f67f39b19..d8ece8f523a 100644 --- a/packages/client/lib/commands/GETBIT.ts +++ b/packages/client/lib/commands/GETBIT.ts @@ -1,15 +1,11 @@ -import { RedisCommandArgument, RedisCommandArguments } from '.'; +import { NumberReply, Command, RedisArgument } from '../RESP/types'; import { BitValue } from './generic-transformers'; -export const FIRST_KEY_INDEX = 1; - -export const IS_READ_ONLY = true; - -export function transformArguments( - key: RedisCommandArgument, - offset: number -): RedisCommandArguments { +export default { + FIRST_KEY_INDEX: 1, + IS_READ_ONLY: true, + transformArguments(key: RedisArgument, offset: number) { return ['GETBIT', key, offset.toString()]; -} - -export declare function transformReply(): BitValue; + }, + transformReply: undefined as unknown as () => NumberReply +} as const satisfies Command; diff --git a/packages/client/lib/commands/GETDEL.spec.ts b/packages/client/lib/commands/GETDEL.spec.ts index db3a486696a..311f15e554d 100644 --- a/packages/client/lib/commands/GETDEL.spec.ts +++ b/packages/client/lib/commands/GETDEL.spec.ts @@ -1,28 +1,24 @@ -import { strict as assert } from 'assert'; +import { strict as assert } from 'node:assert'; import testUtils, { GLOBAL } from '../test-utils'; -import { transformArguments } from './GETDEL'; +import GETDEL from './GETDEL'; describe('GETDEL', () => { - testUtils.isVersionGreaterThanHook([6, 2]); + testUtils.isVersionGreaterThanHook([6, 2]); - it('transformArguments', () => { - assert.deepEqual( - transformArguments('key'), - ['GETDEL', 'key'] - ); - }); + it('transformArguments', () => { + assert.deepEqual( + GETDEL.transformArguments('key'), + ['GETDEL', 'key'] + ); + }); - testUtils.testWithClient('client.getDel', async client => { - assert.equal( - await client.getDel('key'), - null - ); - }, GLOBAL.SERVERS.OPEN); - - testUtils.testWithCluster('cluster.getDel', async cluster => { - assert.equal( - await cluster.getDel('key'), - null - ); - }, GLOBAL.CLUSTERS.OPEN); + testUtils.testAll('getDel', async client => { + assert.equal( + await client.getDel('key'), + null + ); + }, { + client: GLOBAL.SERVERS.OPEN, + cluster: GLOBAL.CLUSTERS.OPEN + }); }); diff --git a/packages/client/lib/commands/GETDEL.ts b/packages/client/lib/commands/GETDEL.ts index 2d91e6cc025..c11fd047df4 100644 --- a/packages/client/lib/commands/GETDEL.ts +++ b/packages/client/lib/commands/GETDEL.ts @@ -1,9 +1,10 @@ -import { RedisCommandArgument, RedisCommandArguments } from '.'; +import { RedisArgument, BlobStringReply, NullReply, Command } from '../RESP/types'; -export const FIRST_KEY_INDEX = 1; - -export function transformArguments(key: RedisCommandArgument): RedisCommandArguments { +export default { + FIRST_KEY_INDEX: 1, + IS_READ_ONLY: true, + transformArguments(key: RedisArgument) { return ['GETDEL', key]; -} - -export declare function transformReply(): RedisCommandArgument | null; + }, + transformReply: undefined as unknown as () => BlobStringReply | NullReply +} as const satisfies Command; diff --git a/packages/client/lib/commands/GETEX.spec.ts b/packages/client/lib/commands/GETEX.spec.ts index 1bf86089da1..302d034b961 100644 --- a/packages/client/lib/commands/GETEX.spec.ts +++ b/packages/client/lib/commands/GETEX.spec.ts @@ -1,96 +1,122 @@ -import { strict as assert } from 'assert'; +import { strict as assert } from 'node:assert'; import testUtils, { GLOBAL } from '../test-utils'; -import { transformArguments } from './GETEX'; +import GETEX from './GETEX'; describe('GETEX', () => { - testUtils.isVersionGreaterThanHook([6, 2]); + testUtils.isVersionGreaterThanHook([6, 2]); - describe('transformArguments', () => { - it('EX', () => { - assert.deepEqual( - transformArguments('key', { - EX: 1 - }), - ['GETEX', 'key', 'EX', '1'] - ); - }); + describe('transformArguments', () => { + it('EX | PX', () => { + assert.deepEqual( + GETEX.transformArguments('key', { + type: 'EX', + value: 1 + }), + ['GETEX', 'key', 'EX', '1'] + ); + }); - it('PX', () => { - assert.deepEqual( - transformArguments('key', { - PX: 1 - }), - ['GETEX', 'key', 'PX', '1'] - ); - }); + it('EX (backwards compatibility)', () => { + assert.deepEqual( + GETEX.transformArguments('key', { + EX: 1 + }), + ['GETEX', 'key', 'EX', '1'] + ); + }); - describe('EXAT', () => { - it('number', () => { - assert.deepEqual( - transformArguments('key', { - EXAT: 1 - }), - ['GETEX', 'key', 'EXAT', '1'] - ); - }); + it('PX (backwards compatibility)', () => { + assert.deepEqual( + GETEX.transformArguments('key', { + PX: 1 + }), + ['GETEX', 'key', 'PX', '1'] + ); + }); - it('date', () => { - const d = new Date(); - assert.deepEqual( - transformArguments('key', { - EXAT: d - }), - ['GETEX', 'key', 'EXAT', Math.floor(d.getTime() / 1000).toString()] - ); - }); - }); + describe('EXAT | PXAT', () => { + it('number', () => { + assert.deepEqual( + GETEX.transformArguments('key', { + type: 'EXAT', + value: 1 + }), + ['GETEX', 'key', 'EXAT', '1'] + ); + }); - describe('PXAT', () => { - it('number', () => { - assert.deepEqual( - transformArguments('key', { - PXAT: 1 - }), - ['GETEX', 'key', 'PXAT', '1'] - ); - }); + it('date', () => { + const d = new Date(); + assert.deepEqual( + GETEX.transformArguments('key', { + EXAT: d + }), + ['GETEX', 'key', 'EXAT', Math.floor(d.getTime() / 1000).toString()] + ); + }); + }); - it('date', () => { - const d = new Date(); - assert.deepEqual( - transformArguments('key', { - PXAT: d - }), - ['GETEX', 'key', 'PXAT', d.getTime().toString()] - ); - }); - }); + describe('EXAT (backwards compatibility)', () => { + it('number', () => { + assert.deepEqual( + GETEX.transformArguments('key', { + EXAT: 1 + }), + ['GETEX', 'key', 'EXAT', '1'] + ); + }); - it('PERSIST', () => { - assert.deepEqual( - transformArguments('key', { - PERSIST: true - }), - ['GETEX', 'key', 'PERSIST'] - ); - }); + it('date', () => { + const d = new Date(); + assert.deepEqual( + GETEX.transformArguments('key', { + EXAT: d + }), + ['GETEX', 'key', 'EXAT', Math.floor(d.getTime() / 1000).toString()] + ); + }); }); - testUtils.testWithClient('client.getEx', async client => { - assert.equal( - await client.getEx('key', { - PERSIST: true - }), - null + describe('PXAT (backwards compatibility)', () => { + it('number', () => { + assert.deepEqual( + GETEX.transformArguments('key', { + PXAT: 1 + }), + ['GETEX', 'key', 'PXAT', '1'] ); - }, GLOBAL.SERVERS.OPEN); + }); - testUtils.testWithCluster('cluster.getEx', async cluster => { - assert.equal( - await cluster.getEx('key', { - PERSIST: true - }), - null + it('date', () => { + const d = new Date(); + assert.deepEqual( + GETEX.transformArguments('key', { + PXAT: d + }), + ['GETEX', 'key', 'PXAT', d.getTime().toString()] ); - }, GLOBAL.CLUSTERS.OPEN); + }); + }); + + it('PERSIST (backwards compatibility)', () => { + assert.deepEqual( + GETEX.transformArguments('key', { + PERSIST: true + }), + ['GETEX', 'key', 'PERSIST'] + ); + }); + }); + + testUtils.testAll('getEx', async client => { + assert.equal( + await client.getEx('key', { + type: 'PERSIST' + }), + null + ); + }, { + client: GLOBAL.SERVERS.OPEN, + cluster: GLOBAL.CLUSTERS.OPEN + }); }); diff --git a/packages/client/lib/commands/GETEX.ts b/packages/client/lib/commands/GETEX.ts index 5b3cec6d887..8244350eddb 100644 --- a/packages/client/lib/commands/GETEX.ts +++ b/packages/client/lib/commands/GETEX.ts @@ -1,39 +1,78 @@ -import { RedisCommandArgument, RedisCommandArguments } from '.'; +import { RedisArgument, BlobStringReply, NullReply, Command } from '../RESP/types'; import { transformEXAT, transformPXAT } from './generic-transformers'; -export const FIRST_KEY_INDEX = 1; - -type GetExModes = { - EX: number; +export type GetExOptions = { + type: 'EX' | 'PX'; + value: number; +} | { + type: 'EXAT' | 'PXAT'; + value: number | Date; +} | { + type: 'PERSIST'; +} | { + /** + * @deprecated Use `{ type: 'EX', value: number }` instead. + */ + EX: number; } | { - PX: number; + /** + * @deprecated Use `{ type: 'PX', value: number }` instead. + */ + PX: number; } | { - EXAT: number | Date; + /** + * @deprecated Use `{ type: 'EXAT', value: number | Date }` instead. + */ + EXAT: number | Date; } | { - PXAT: number | Date; + /** + * @deprecated Use `{ type: 'PXAT', value: number | Date }` instead. + */ + PXAT: number | Date; } | { - PERSIST: true; + /** + * @deprecated Use `{ type: 'PERSIST' }` instead. + */ + PERSIST: true; }; -export function transformArguments( - key: RedisCommandArgument, - mode: GetExModes -): RedisCommandArguments { +export default { + FIRST_KEY_INDEX: 1, + IS_READ_ONLY: true, + transformArguments(key: RedisArgument, options: GetExOptions) { const args = ['GETEX', key]; - if ('EX' in mode) { - args.push('EX', mode.EX.toString()); - } else if ('PX' in mode) { - args.push('PX', mode.PX.toString()); - } else if ('EXAT' in mode) { - args.push('EXAT', transformEXAT(mode.EXAT)); - } else if ('PXAT' in mode) { - args.push('PXAT', transformPXAT(mode.PXAT)); - } else { // PERSIST + if ('type' in options) { + switch (options.type) { + case 'EX': + case 'PX': + args.push(options.type, options.value.toString()); + break; + + case 'EXAT': + case 'PXAT': + args.push(options.type, transformEXAT(options.value)); + break; + + case 'PERSIST': + args.push('PERSIST'); + break; + } + } else { + if ('EX' in options) { + args.push('EX', options.EX.toString()); + } else if ('PX' in options) { + args.push('PX', options.PX.toString()); + } else if ('EXAT' in options) { + args.push('EXAT', transformEXAT(options.EXAT)); + } else if ('PXAT' in options) { + args.push('PXAT', transformPXAT(options.PXAT)); + } else { // PERSIST args.push('PERSIST'); + } } - + return args; -} - -export declare function transformReply(): RedisCommandArgument | null; + }, + transformReply: undefined as unknown as () => BlobStringReply | NullReply +} as const satisfies Command; diff --git a/packages/client/lib/commands/GETRANGE.spec.ts b/packages/client/lib/commands/GETRANGE.spec.ts index 0c9dbc2c70f..2aac1ca16d9 100644 --- a/packages/client/lib/commands/GETRANGE.spec.ts +++ b/packages/client/lib/commands/GETRANGE.spec.ts @@ -1,26 +1,22 @@ -import { strict as assert } from 'assert'; +import { strict as assert } from 'node:assert'; import testUtils, { GLOBAL } from '../test-utils'; -import { transformArguments } from './GETRANGE'; +import GETRANGE from './GETRANGE'; describe('GETRANGE', () => { - it('transformArguments', () => { - assert.deepEqual( - transformArguments('key', 0, -1), - ['GETRANGE', 'key', '0', '-1'] - ); - }); + it('transformArguments', () => { + assert.deepEqual( + GETRANGE.transformArguments('key', 0, -1), + ['GETRANGE', 'key', '0', '-1'] + ); + }); - testUtils.testWithClient('client.getRange', async client => { - assert.equal( - await client.getRange('key', 0, -1), - '' - ); - }, GLOBAL.SERVERS.OPEN); - - testUtils.testWithCluster('cluster.lTrim', async cluster => { - assert.equal( - await cluster.getRange('key', 0, -1), - '' - ); - }, GLOBAL.CLUSTERS.OPEN); + testUtils.testAll('getRange', async client => { + assert.equal( + await client.getRange('key', 0, -1), + '' + ); + }, { + client: GLOBAL.SERVERS.OPEN, + cluster: GLOBAL.CLUSTERS.OPEN + }); }); diff --git a/packages/client/lib/commands/GETRANGE.ts b/packages/client/lib/commands/GETRANGE.ts index 2d12d937cc6..e5357cd120b 100644 --- a/packages/client/lib/commands/GETRANGE.ts +++ b/packages/client/lib/commands/GETRANGE.ts @@ -1,15 +1,10 @@ -import { RedisCommandArgument, RedisCommandArguments } from '.'; +import { RedisArgument, BlobStringReply, NullReply, Command } from '../RESP/types'; -export const FIRST_KEY_INDEX = 1; - -export const IS_READ_ONLY = true; - -export function transformArguments( - key: RedisCommandArgument, - start: number, - end: number -): RedisCommandArguments { +export default { + FIRST_KEY_INDEX: 1, + IS_READ_ONLY: true, + transformArguments(key: RedisArgument, start: number, end: number) { return ['GETRANGE', key, start.toString(), end.toString()]; -} - -export declare function transformReply(): RedisCommandArgument; + }, + transformReply: undefined as unknown as () => BlobStringReply | NullReply +} as const satisfies Command; diff --git a/packages/client/lib/commands/GETSET.spec.ts b/packages/client/lib/commands/GETSET.spec.ts index 73fbcec57ea..6583ec34f75 100644 --- a/packages/client/lib/commands/GETSET.spec.ts +++ b/packages/client/lib/commands/GETSET.spec.ts @@ -1,26 +1,22 @@ -import { strict as assert } from 'assert'; +import { strict as assert } from 'node:assert'; import testUtils, { GLOBAL } from '../test-utils'; -import { transformArguments } from './GETSET'; +import GETSET from './GETSET'; describe('GETSET', () => { - it('transformArguments', () => { - assert.deepEqual( - transformArguments('key', 'value'), - ['GETSET', 'key', 'value'] - ); - }); + it('transformArguments', () => { + assert.deepEqual( + GETSET.transformArguments('key', 'value'), + ['GETSET', 'key', 'value'] + ); + }); - testUtils.testWithClient('client.getSet', async client => { - assert.equal( - await client.getSet('key', 'value'), - null - ); - }, GLOBAL.SERVERS.OPEN); - - testUtils.testWithCluster('cluster.getSet', async cluster => { - assert.equal( - await cluster.getSet('key', 'value'), - null - ); - }, GLOBAL.CLUSTERS.OPEN); + testUtils.testAll('getSet', async client => { + assert.equal( + await client.getSet('key', 'value'), + null + ); + }, { + client: GLOBAL.SERVERS.OPEN, + cluster: GLOBAL.CLUSTERS.OPEN + }); }); diff --git a/packages/client/lib/commands/GETSET.ts b/packages/client/lib/commands/GETSET.ts index 87d111792c6..bbe920181b2 100644 --- a/packages/client/lib/commands/GETSET.ts +++ b/packages/client/lib/commands/GETSET.ts @@ -1,12 +1,10 @@ -import { RedisCommandArgument, RedisCommandArguments } from '.'; +import { RedisArgument, BlobStringReply, NullReply, Command } from '../RESP/types'; -export const FIRST_KEY_INDEX = 1; - -export function transformArguments( - key: RedisCommandArgument, - value: RedisCommandArgument -): RedisCommandArguments { +export default { + FIRST_KEY_INDEX: 1, + IS_READ_ONLY: true, + transformArguments(key: RedisArgument, value: RedisArgument) { return ['GETSET', key, value]; -} - -export declare function transformReply(): RedisCommandArgument | null; + }, + transformReply: undefined as unknown as () => BlobStringReply | NullReply +} as const satisfies Command; diff --git a/packages/client/lib/commands/HDEL.spec.ts b/packages/client/lib/commands/HDEL.spec.ts index eb24bcfacbd..9f69485d9fe 100644 --- a/packages/client/lib/commands/HDEL.spec.ts +++ b/packages/client/lib/commands/HDEL.spec.ts @@ -1,28 +1,31 @@ -import { strict as assert } from 'assert'; +import { strict as assert } from 'node:assert'; import testUtils, { GLOBAL } from '../test-utils'; -import { transformArguments } from './HDEL'; +import HDEL from './HDEL'; describe('HDEL', () => { - describe('transformArguments', () => { - it('string', () => { - assert.deepEqual( - transformArguments('key', 'field'), - ['HDEL', 'key', 'field'] - ); - }); + describe('transformArguments', () => { + it('string', () => { + assert.deepEqual( + HDEL.transformArguments('key', 'field'), + ['HDEL', 'key', 'field'] + ); + }); - it('array', () => { - assert.deepEqual( - transformArguments('key', ['1', '2']), - ['HDEL', 'key', '1', '2'] - ); - }); + it('array', () => { + assert.deepEqual( + HDEL.transformArguments('key', ['1', '2']), + ['HDEL', 'key', '1', '2'] + ); }); + }); - testUtils.testWithClient('client.hDel', async client => { - assert.equal( - await client.hDel('key', 'field'), - 0 - ); - }, GLOBAL.SERVERS.OPEN); + testUtils.testAll('hDel', async client => { + assert.equal( + await client.hDel('key', 'field'), + 0 + ); + }, { + client: GLOBAL.SERVERS.OPEN, + cluster: GLOBAL.CLUSTERS.OPEN + }); }); diff --git a/packages/client/lib/commands/HDEL.ts b/packages/client/lib/commands/HDEL.ts index 1a994e109d6..64aa55edda6 100644 --- a/packages/client/lib/commands/HDEL.ts +++ b/packages/client/lib/commands/HDEL.ts @@ -1,13 +1,10 @@ -import { RedisCommandArgument, RedisCommandArguments } from '.'; -import { pushVerdictArguments } from './generic-transformers'; +import { RedisArgument, NumberReply, Command } from '../RESP/types'; +import { RedisVariadicArgument, pushVariadicArguments } from './generic-transformers'; -export const FIRST_KEY_INDEX = 1; - -export function transformArguments( - key: RedisCommandArgument, - field: RedisCommandArgument | Array -): RedisCommandArguments { - return pushVerdictArguments(['HDEL', key], field); -} - -export declare function transformReply(): number; +export default { + FIRST_KEY_INDEX: 1, + transformArguments(key: RedisArgument, field: RedisVariadicArgument) { + return pushVariadicArguments(['HDEL', key], field); + }, + transformReply: undefined as unknown as () => NumberReply +} as const satisfies Command; diff --git a/packages/client/lib/commands/HELLO.spec.ts b/packages/client/lib/commands/HELLO.spec.ts index 12d6d98c7c9..f7f117f18c7 100644 --- a/packages/client/lib/commands/HELLO.spec.ts +++ b/packages/client/lib/commands/HELLO.spec.ts @@ -1,76 +1,71 @@ -import { strict as assert } from 'assert'; +import { strict as assert } from 'node:assert'; import testUtils, { GLOBAL } from '../test-utils'; -import { transformArguments } from './HELLO'; +import HELLO from './HELLO'; describe('HELLO', () => { - testUtils.isVersionGreaterThanHook([6]); + testUtils.isVersionGreaterThanHook([6]); - describe('transformArguments', () => { - it('simple', () => { - assert.deepEqual( - transformArguments(), - ['HELLO'] - ); - }); - - it('with protover', () => { - assert.deepEqual( - transformArguments({ - protover: 3 - }), - ['HELLO', '3'] - ); - }); + describe('transformArguments', () => { + it('simple', () => { + assert.deepEqual( + HELLO.transformArguments(), + ['HELLO'] + ); + }); - it('with protover, auth', () => { - assert.deepEqual( - transformArguments({ - protover: 3, - auth: { - username: 'username', - password: 'password' - } - }), - ['HELLO', '3', 'AUTH', 'username', 'password'] - ); - }); + it('with protover', () => { + assert.deepEqual( + HELLO.transformArguments(3), + ['HELLO', '3'] + ); + }); - it('with protover, clientName', () => { - assert.deepEqual( - transformArguments({ - protover: 3, - clientName: 'clientName' - }), - ['HELLO', '3', 'SETNAME', 'clientName'] - ); - }); + it('with protover, AUTH', () => { + assert.deepEqual( + HELLO.transformArguments(3, { + AUTH: { + username: 'username', + password: 'password' + } + }), + ['HELLO', '3', 'AUTH', 'username', 'password'] + ); + }); - it('with protover, auth, clientName', () => { - assert.deepEqual( - transformArguments({ - protover: 3, - auth: { - username: 'username', - password: 'password' - }, - clientName: 'clientName' - }), - ['HELLO', '3', 'AUTH', 'username', 'password', 'SETNAME', 'clientName'] - ); - }); + it('with protover, SETNAME', () => { + assert.deepEqual( + HELLO.transformArguments(3, { + SETNAME: 'name' + }), + ['HELLO', '3', 'SETNAME', 'name'] + ); }); - testUtils.testWithClient('client.hello', async client => { - const reply = await client.hello(); - assert.equal(reply.server, 'redis'); - assert.equal(typeof reply.version, 'string'); - assert.equal(reply.proto, 2); - assert.equal(typeof reply.id, 'number'); - assert.equal(reply.mode, 'standalone'); - assert.equal(reply.role, 'master'); - assert.deepEqual(reply.modules, []); - }, { - ...GLOBAL.SERVERS.OPEN, - minimumDockerVersion: [6, 2] + it('with protover, AUTH, SETNAME', () => { + assert.deepEqual( + HELLO.transformArguments(3, { + AUTH: { + username: 'username', + password: 'password' + }, + SETNAME: 'name' + }), + ['HELLO', '3', 'AUTH', 'username', 'password', 'SETNAME', 'name'] + ); }); + }); + + testUtils.testWithClient('client.hello', async client => { + const reply = await client.hello(); + assert.equal(reply.server, 'redis'); + assert.equal(typeof reply.version, 'string'); + assert.equal(reply.proto, 2); + assert.equal(typeof reply.id, 'number'); + assert.equal(reply.mode, 'standalone'); + assert.equal(reply.role, 'master'); + assert.ok(reply.modules instanceof Array); + }, { + ...GLOBAL.SERVERS.OPEN, + minimumDockerVersion: [6, 2] + }); }); diff --git a/packages/client/lib/commands/HELLO.ts b/packages/client/lib/commands/HELLO.ts index d943f2e4c35..0fb2960d028 100644 --- a/packages/client/lib/commands/HELLO.ts +++ b/packages/client/lib/commands/HELLO.ts @@ -1,65 +1,59 @@ -import { RedisCommandArgument, RedisCommandArguments } from '.'; -import { AuthOptions } from './AUTH'; - -interface HelloOptions { - protover: number; - auth?: Required; - clientName?: string; +import { RedisArgument, RespVersions, TuplesToMapReply, BlobStringReply, NumberReply, ArrayReply, UnwrapReply, Resp2Reply, Command } from '../RESP/types'; + +export interface HelloOptions { + protover?: RespVersions; + AUTH?: { + username: RedisArgument; + password: RedisArgument; + }; + SETNAME?: string; } -export function transformArguments(options?: HelloOptions): RedisCommandArguments { - const args: RedisCommandArguments = ['HELLO']; - - if (options) { - args.push(options.protover.toString()); - - if (options.auth) { - args.push('AUTH', options.auth.username, options.auth.password); - } - - if (options.clientName) { - args.push('SETNAME', options.clientName); - } +export type HelloReply = TuplesToMapReply<[ + [BlobStringReply<'server'>, BlobStringReply], + [BlobStringReply<'version'>, BlobStringReply], + [BlobStringReply<'proto'>, NumberReply], + [BlobStringReply<'id'>, NumberReply], + [BlobStringReply<'mode'>, BlobStringReply], + [BlobStringReply<'role'>, BlobStringReply], + [BlobStringReply<'modules'>, ArrayReply] +]>; + +export default { + transformArguments(protover?: RespVersions, options?: HelloOptions) { + const args: Array = ['HELLO']; + + if (protover) { + args.push(protover.toString()); + + if (options?.AUTH) { + args.push( + 'AUTH', + options.AUTH.username, + options.AUTH.password + ); + } + + if (options?.SETNAME) { + args.push( + 'SETNAME', + options.SETNAME + ); + } } - + return args; -} - -type HelloRawReply = [ - _: never, - server: RedisCommandArgument, - _: never, - version: RedisCommandArgument, - _: never, - proto: number, - _: never, - id: number, - _: never, - mode: RedisCommandArgument, - _: never, - role: RedisCommandArgument, - _: never, - modules: Array -]; - -interface HelloTransformedReply { - server: RedisCommandArgument; - version: RedisCommandArgument; - proto: number; - id: number; - mode: RedisCommandArgument; - role: RedisCommandArgument; - modules: Array; -} - -export function transformReply(reply: HelloRawReply): HelloTransformedReply { - return { - server: reply[1], - version: reply[3], - proto: reply[5], - id: reply[7], - mode: reply[9], - role: reply[11], - modules: reply[13] - }; -} + }, + transformReply: { + 2: (reply: UnwrapReply>) => ({ + server: reply[1], + version: reply[3], + proto: reply[5], + id: reply[7], + mode: reply[9], + role: reply[11], + modules: reply[13] + }), + 3: undefined as unknown as () => HelloReply + } +} as const satisfies Command; diff --git a/packages/client/lib/commands/HEXISTS.spec.ts b/packages/client/lib/commands/HEXISTS.spec.ts index 3764319c123..69ca6fa765f 100644 --- a/packages/client/lib/commands/HEXISTS.spec.ts +++ b/packages/client/lib/commands/HEXISTS.spec.ts @@ -1,19 +1,22 @@ -import { strict as assert } from 'assert'; +import { strict as assert } from 'node:assert'; import testUtils, { GLOBAL } from '../test-utils'; -import { transformArguments } from './HEXISTS'; +import HEXISTS from './HEXISTS'; describe('HEXISTS', () => { - it('transformArguments', () => { - assert.deepEqual( - transformArguments('key', 'field'), - ['HEXISTS', 'key', 'field'] - ); - }); + it('transformArguments', () => { + assert.deepEqual( + HEXISTS.transformArguments('key', 'field'), + ['HEXISTS', 'key', 'field'] + ); + }); - testUtils.testWithClient('client.hExists', async client => { - assert.equal( - await client.hExists('key', 'field'), - false - ); - }, GLOBAL.SERVERS.OPEN); + testUtils.testAll('hExists', async client => { + assert.equal( + await client.hExists('key', 'field'), + 0 + ); + }, { + client: GLOBAL.SERVERS.OPEN, + cluster: GLOBAL.CLUSTERS.OPEN + }); }); diff --git a/packages/client/lib/commands/HEXISTS.ts b/packages/client/lib/commands/HEXISTS.ts index 289be20aa83..dc7e937ee78 100644 --- a/packages/client/lib/commands/HEXISTS.ts +++ b/packages/client/lib/commands/HEXISTS.ts @@ -1,12 +1,10 @@ -import { RedisCommandArgument, RedisCommandArguments } from '.'; +import { RedisArgument, NumberReply, Command } from '../RESP/types'; -export const FIRST_KEY_INDEX = 1; - -export function transformArguments( - key: RedisCommandArgument, - field: RedisCommandArgument -): RedisCommandArguments { +export default { + FIRST_KEY_INDEX: 1, + IS_READ_ONLY: true, + transformArguments(key: RedisArgument, field: RedisArgument) { return ['HEXISTS', key, field]; -} - -export { transformBooleanReply as transformReply } from './generic-transformers'; + }, + transformReply: undefined as unknown as () => NumberReply<0 | 1> +} as const satisfies Command; diff --git a/packages/client/lib/commands/HEXPIRE.spec.ts b/packages/client/lib/commands/HEXPIRE.spec.ts index 3714f617f58..71c48b7e884 100644 --- a/packages/client/lib/commands/HEXPIRE.spec.ts +++ b/packages/client/lib/commands/HEXPIRE.spec.ts @@ -1,6 +1,6 @@ import { strict as assert } from 'node:assert'; import testUtils, { GLOBAL } from '../test-utils'; -import { transformArguments } from './HEXPIRE'; +import HEXPIRE from './HEXPIRE'; import { HASH_EXPIRATION_TIME } from './HEXPIRETIME'; describe('HEXPIRE', () => { @@ -9,21 +9,21 @@ describe('HEXPIRE', () => { describe('transformArguments', () => { it('string', () => { assert.deepEqual( - transformArguments('key', 'field', 1), + HEXPIRE.transformArguments('key', 'field', 1), ['HEXPIRE', 'key', '1', 'FIELDS', '1', 'field'] ); }); it('array', () => { assert.deepEqual( - transformArguments('key', ['field1', 'field2'], 1), + HEXPIRE.transformArguments('key', ['field1', 'field2'], 1), ['HEXPIRE', 'key', '1', 'FIELDS', '2', 'field1', 'field2'] ); }); it('with set option', () => { assert.deepEqual( - transformArguments('key', ['field1'], 1, 'NX'), + HEXPIRE.transformArguments('key', ['field1'], 1, 'NX'), ['HEXPIRE', 'key', '1', 'NX', 'FIELDS', '1', 'field1'] ); }); diff --git a/packages/client/lib/commands/HEXPIRE.ts b/packages/client/lib/commands/HEXPIRE.ts index 938f9039939..34b52c1db68 100644 --- a/packages/client/lib/commands/HEXPIRE.ts +++ b/packages/client/lib/commands/HEXPIRE.ts @@ -1,44 +1,35 @@ -import { RedisCommandArgument } from '.'; -import { pushVerdictArgument } from './generic-transformers'; +import { Command, RedisArgument } from '../RESP/types'; +import { pushVariadicArgument } from './generic-transformers'; -/** - * @readonly - * @enum {number} - */ export const HASH_EXPIRATION = { - /** @property {number} */ /** The field does not exist */ FIELD_NOT_EXISTS: -2, - /** @property {number} */ /** Specified NX | XX | GT | LT condition not met */ CONDITION_NOT_MET: 0, - /** @property {number} */ /** Expiration time was set or updated */ UPDATED: 1, - /** @property {number} */ /** Field deleted because the specified expiration time is in the past */ DELETED: 2 } as const; export type HashExpiration = typeof HASH_EXPIRATION[keyof typeof HASH_EXPIRATION]; -export const FIRST_KEY_INDEX = 1; +export default { + FIRST_KEY_INDEX: 1, + transformArguments(key: RedisArgument, + fields: RedisArgument | Array, + seconds: number, + mode?: 'NX' | 'XX' | 'GT' | 'LT', + ) { + const args = ['HEXPIRE', key, seconds.toString()]; -export function transformArguments( - key: RedisCommandArgument, - fields: RedisCommandArgument| Array, - seconds: number, - mode?: 'NX' | 'XX' | 'GT' | 'LT', -) { - const args = ['HEXPIRE', key, seconds.toString()]; + if (mode) { + args.push(mode); + } - if (mode) { - args.push(mode); - } + args.push('FIELDS'); - args.push('FIELDS'); - - return pushVerdictArgument(args, fields); -} - -export declare function transformReply(): Array; \ No newline at end of file + return pushVariadicArgument(args, fields); + }, + transformReply: undefined as unknown as () => Array +} as const satisfies Command; diff --git a/packages/client/lib/commands/HEXPIREAT.spec.ts b/packages/client/lib/commands/HEXPIREAT.spec.ts index 1c65fb61773..1f87300214c 100644 --- a/packages/client/lib/commands/HEXPIREAT.spec.ts +++ b/packages/client/lib/commands/HEXPIREAT.spec.ts @@ -1,6 +1,6 @@ import { strict as assert } from 'node:assert'; import testUtils, { GLOBAL } from '../test-utils'; -import { transformArguments } from './HEXPIREAT'; +import HEXPIREAT from './HEXPIREAT'; import { HASH_EXPIRATION_TIME } from './HEXPIRETIME'; describe('HEXPIREAT', () => { @@ -9,14 +9,14 @@ describe('HEXPIREAT', () => { describe('transformArguments', () => { it('string + number', () => { assert.deepEqual( - transformArguments('key', 'field', 1), + HEXPIREAT.transformArguments('key', 'field', 1), ['HEXPIREAT', 'key', '1', 'FIELDS', '1', 'field'] ); }); it('array + number', () => { assert.deepEqual( - transformArguments('key', ['field1', 'field2'], 1), + HEXPIREAT.transformArguments('key', ['field1', 'field2'], 1), ['HEXPIREAT', 'key', '1', 'FIELDS', '2', 'field1', 'field2'] ); }); @@ -25,14 +25,14 @@ describe('HEXPIREAT', () => { const d = new Date(); assert.deepEqual( - transformArguments('key', ['field1'], d), + HEXPIREAT.transformArguments('key', ['field1'], d), ['HEXPIREAT', 'key', Math.floor(d.getTime() / 1000).toString(), 'FIELDS', '1', 'field1'] ); }); it('with set option', () => { assert.deepEqual( - transformArguments('key', 'field1', 1, 'GT'), + HEXPIREAT.transformArguments('key', 'field1', 1, 'GT'), ['HEXPIREAT', 'key', '1', 'GT', 'FIELDS', '1', 'field1'] ); }); diff --git a/packages/client/lib/commands/HEXPIREAT.ts b/packages/client/lib/commands/HEXPIREAT.ts index 58c52d3a1f6..5a49951f1cd 100644 --- a/packages/client/lib/commands/HEXPIREAT.ts +++ b/packages/client/lib/commands/HEXPIREAT.ts @@ -1,28 +1,28 @@ -import { RedisCommandArgument } from '.'; -import { pushVerdictArgument, transformEXAT } from './generic-transformers'; +import { Command, RedisArgument } from '../RESP/types'; +import { pushVariadicArgument, RedisVariadicArgument, transformEXAT } from './generic-transformers'; import { HashExpiration } from './HEXPIRE'; -export const FIRST_KEY_INDEX = 1; - -export function transformArguments( - key: RedisCommandArgument, - fields: RedisCommandArgument | Array, - timestamp: number | Date, - mode?: 'NX' | 'XX' | 'GT' | 'LT' -) { - const args = [ - 'HEXPIREAT', - key, - transformEXAT(timestamp) - ]; - - if (mode) { - args.push(mode); - } - - args.push('FIELDS') - - return pushVerdictArgument(args, fields); -} - -export declare function transformReply(): Array; \ No newline at end of file +export default { + FIRST_KEY_INDEX: 1, + transformArguments( + key: RedisArgument, + fields: RedisVariadicArgument, + timestamp: number | Date, + mode?: 'NX' | 'XX' | 'GT' | 'LT' + ) { + const args = [ + 'HEXPIREAT', + key, + transformEXAT(timestamp) + ]; + + if (mode) { + args.push(mode); + } + + args.push('FIELDS') + + return pushVariadicArgument(args, fields); + }, + transformReply: undefined as unknown as () => Array +} as const satisfies Command; diff --git a/packages/client/lib/commands/HEXPIRETIME.spec.ts b/packages/client/lib/commands/HEXPIRETIME.spec.ts index 9c3eb024bed..2335ec91726 100644 --- a/packages/client/lib/commands/HEXPIRETIME.spec.ts +++ b/packages/client/lib/commands/HEXPIRETIME.spec.ts @@ -1,6 +1,6 @@ import { strict as assert } from 'node:assert'; import testUtils, { GLOBAL } from '../test-utils'; -import { HASH_EXPIRATION_TIME, transformArguments } from './HEXPIRETIME'; +import HEXPIRETIME, { HASH_EXPIRATION_TIME } from './HEXPIRETIME'; describe('HEXPIRETIME', () => { testUtils.isVersionGreaterThanHook([7, 4]); @@ -8,14 +8,14 @@ describe('HEXPIRETIME', () => { describe('transformArguments', () => { it('string', () => { assert.deepEqual( - transformArguments('key', 'field'), + HEXPIRETIME.transformArguments('key', 'field'), ['HEXPIRETIME', 'key', 'FIELDS', '1', 'field'] ); }); it('array', () => { assert.deepEqual( - transformArguments('key', ['field1', 'field2']), + HEXPIRETIME.transformArguments('key', ['field1', 'field2']), ['HEXPIRETIME', 'key', 'FIELDS', '2', 'field1', 'field2'] ); }); diff --git a/packages/client/lib/commands/HEXPIRETIME.ts b/packages/client/lib/commands/HEXPIRETIME.ts index 01764b1032d..7edf1309002 100644 --- a/packages/client/lib/commands/HEXPIRETIME.ts +++ b/packages/client/lib/commands/HEXPIRETIME.ts @@ -1,21 +1,18 @@ -import { RedisCommandArgument } from '.'; -import { pushVerdictArgument } from './generic-transformers'; +import { ArrayReply, Command, NumberReply, RedisArgument } from '../RESP/types'; +import { pushVariadicArgument, RedisVariadicArgument } from './generic-transformers'; export const HASH_EXPIRATION_TIME = { - /** @property {number} */ /** The field does not exist */ FIELD_NOT_EXISTS: -2, - /** @property {number} */ /** The field exists but has no associated expire */ NO_EXPIRATION: -1, } as const; -export const FIRST_KEY_INDEX = 1 - -export const IS_READ_ONLY = true; - -export function transformArguments(key: RedisCommandArgument, fields: RedisCommandArgument | Array) { - return pushVerdictArgument(['HEXPIRETIME', key, 'FIELDS'], fields); -} - -export declare function transformReply(): Array; \ No newline at end of file +export default { + FIRST_KEY_INDEX: 1, + IS_READ_ONLY: true, + transformArguments(key: RedisArgument, fields: RedisVariadicArgument) { + return pushVariadicArgument(['HEXPIRETIME', key, 'FIELDS'], fields); + }, + transformReply: undefined as unknown as () => ArrayReply +} as const satisfies Command; diff --git a/packages/client/lib/commands/HGET.spec.ts b/packages/client/lib/commands/HGET.spec.ts index 6b6d0a3ee22..397f22b5604 100644 --- a/packages/client/lib/commands/HGET.spec.ts +++ b/packages/client/lib/commands/HGET.spec.ts @@ -1,19 +1,22 @@ -import { strict as assert } from 'assert'; +import { strict as assert } from 'node:assert'; import testUtils, { GLOBAL } from '../test-utils'; -import { transformArguments } from './HGET'; +import HGET from './HGET'; describe('HGET', () => { - it('transformArguments', () => { - assert.deepEqual( - transformArguments('key', 'field'), - ['HGET', 'key', 'field'] - ); - }); + it('transformArguments', () => { + assert.deepEqual( + HGET.transformArguments('key', 'field'), + ['HGET', 'key', 'field'] + ); + }); - testUtils.testWithClient('client.hGet', async client => { - assert.equal( - await client.hGet('key', 'field'), - null - ); - }, GLOBAL.SERVERS.OPEN); + testUtils.testAll('hGet', async client => { + assert.equal( + await client.hGet('key', 'field'), + null + ); + }, { + client: GLOBAL.SERVERS.OPEN, + cluster: GLOBAL.CLUSTERS.OPEN + }); }); diff --git a/packages/client/lib/commands/HGET.ts b/packages/client/lib/commands/HGET.ts index fcfd31e6172..d83f84e24fa 100644 --- a/packages/client/lib/commands/HGET.ts +++ b/packages/client/lib/commands/HGET.ts @@ -1,14 +1,10 @@ -import { RedisCommandArgument, RedisCommandArguments } from '.'; +import { RedisArgument, BlobStringReply, NullReply, Command } from '../RESP/types'; -export const FIRST_KEY_INDEX = 1; - -export const IS_READ_ONLY = true; - -export function transformArguments( - key: RedisCommandArgument, - field: RedisCommandArgument -): RedisCommandArguments { +export default { + FIRST_KEY_INDEX: 1, + IS_READ_ONLY: true, + transformArguments(key: RedisArgument, field: RedisArgument) { return ['HGET', key, field]; -} - -export declare function transformReply(): RedisCommandArgument | undefined; + }, + transformReply: undefined as unknown as () => BlobStringReply | NullReply +} as const satisfies Command; diff --git a/packages/client/lib/commands/HGETALL.spec.ts b/packages/client/lib/commands/HGETALL.spec.ts index fcd1a30457c..93d122bae07 100644 --- a/packages/client/lib/commands/HGETALL.spec.ts +++ b/packages/client/lib/commands/HGETALL.spec.ts @@ -1,41 +1,34 @@ -import { strict as assert } from 'assert'; +import { strict as assert } from 'node:assert'; import testUtils, { GLOBAL } from '../test-utils'; -import { transformReply } from './HGETALL'; describe('HGETALL', () => { - describe('transformReply', () => { - it('empty', () => { - assert.deepEqual( - transformReply([]), - Object.create(null) - ); - }); - it('with values', () => { - assert.deepEqual( - transformReply(['key1', 'value1', 'key2', 'value2']), - Object.create(null, { - key1: { - value: 'value1', - configurable: true, - enumerable: true, - writable: true - }, - key2: { - value: 'value2', - configurable: true, - enumerable: true, - writable: true - } - }) - ); - }); - }); + testUtils.testAll('hGetAll empty', async client => { + assert.deepEqual( + await client.hGetAll('key'), + Object.create(null) + ); + }, { + client: GLOBAL.SERVERS.OPEN, + cluster: GLOBAL.CLUSTERS.OPEN + }); - testUtils.testWithClient('client.hGetAll', async client => { - assert.deepEqual( - await client.hGetAll('key'), - Object.create(null) - ); - }, GLOBAL.SERVERS.OPEN); + testUtils.testAll('hGetAll with value', async client => { + const [, reply] = await Promise.all([ + client.hSet('key', 'field', 'value'), + client.hGetAll('key') + ]); + assert.deepEqual( + reply, + Object.create(null, { + field: { + value: 'value', + enumerable: true + } + }) + ); + }, { + client: GLOBAL.SERVERS.OPEN, + cluster: GLOBAL.CLUSTERS.OPEN + }); }); diff --git a/packages/client/lib/commands/HGETALL.ts b/packages/client/lib/commands/HGETALL.ts index bf51760ff0e..f1f0ac50bcb 100644 --- a/packages/client/lib/commands/HGETALL.ts +++ b/packages/client/lib/commands/HGETALL.ts @@ -1,13 +1,15 @@ -import { RedisCommandArgument, RedisCommandArguments } from '.'; +import { RedisArgument, MapReply, BlobStringReply, Command } from '../RESP/types'; +import { transformTuplesReply } from './generic-transformers'; -export const FIRST_KEY_INDEX = 1; - -export const IS_READ_ONLY = true; - -export const TRANSFORM_LEGACY_REPLY = true; - -export function transformArguments(key: RedisCommandArgument): RedisCommandArguments { +export default { + FIRST_KEY_INDEX: 1, + IS_READ_ONLY: true, + transformArguments(key: RedisArgument) { return ['HGETALL', key]; -} - -export { transformTuplesReply as transformReply } from './generic-transformers'; + }, + TRANSFORM_LEGACY_REPLY: true, + transformReply: { + 2: transformTuplesReply, + 3: undefined as unknown as () => MapReply + } +} as const satisfies Command; diff --git a/packages/client/lib/commands/HINCRBY.spec.ts b/packages/client/lib/commands/HINCRBY.spec.ts index de406217921..7718fe955eb 100644 --- a/packages/client/lib/commands/HINCRBY.spec.ts +++ b/packages/client/lib/commands/HINCRBY.spec.ts @@ -1,19 +1,22 @@ -import { strict as assert } from 'assert'; +import { strict as assert } from 'node:assert'; import testUtils, { GLOBAL } from '../test-utils'; -import { transformArguments } from './HINCRBY'; +import HINCRBY from './HINCRBY'; describe('HINCRBY', () => { - it('transformArguments', () => { - assert.deepEqual( - transformArguments('key', 'field', 1), - ['HINCRBY', 'key', 'field', '1'] - ); - }); + it('transformArguments', () => { + assert.deepEqual( + HINCRBY.transformArguments('key', 'field', 1), + ['HINCRBY', 'key', 'field', '1'] + ); + }); - testUtils.testWithClient('client.hIncrBy', async client => { - assert.equal( - await client.hIncrBy('key', 'field', 1), - 1 - ); - }, GLOBAL.SERVERS.OPEN); + testUtils.testAll('hIncrBy', async client => { + assert.equal( + await client.hIncrBy('key', 'field', 1), + 1 + ); + }, { + client: GLOBAL.SERVERS.OPEN, + cluster: GLOBAL.CLUSTERS.OPEN + }); }); diff --git a/packages/client/lib/commands/HINCRBY.ts b/packages/client/lib/commands/HINCRBY.ts index b2cf6eefe89..cb7f62ebef5 100644 --- a/packages/client/lib/commands/HINCRBY.ts +++ b/packages/client/lib/commands/HINCRBY.ts @@ -1,13 +1,18 @@ -import { RedisCommandArgument, RedisCommandArguments } from '.'; +import { RedisArgument, NumberReply, Command } from '../RESP/types'; -export const FIRST_KEY_INDEX = 1; - -export function transformArguments( - key: RedisCommandArgument, - field: RedisCommandArgument, +export default { + FIRST_KEY_INDEX: 1, + transformArguments( + key: RedisArgument, + field: RedisArgument, increment: number -): RedisCommandArguments { - return ['HINCRBY', key, field, increment.toString()]; -} - -export declare function transformReply(): number; + ) { + return [ + 'HINCRBY', + key, + field, + increment.toString() + ]; + }, + transformReply: undefined as unknown as () => NumberReply +} as const satisfies Command; diff --git a/packages/client/lib/commands/HINCRBYFLOAT.spec.ts b/packages/client/lib/commands/HINCRBYFLOAT.spec.ts index bd0147a3481..6c265dc6d10 100644 --- a/packages/client/lib/commands/HINCRBYFLOAT.spec.ts +++ b/packages/client/lib/commands/HINCRBYFLOAT.spec.ts @@ -1,19 +1,22 @@ -import { strict as assert } from 'assert'; +import { strict as assert } from 'node:assert'; import testUtils, { GLOBAL } from '../test-utils'; -import { transformArguments } from './HINCRBYFLOAT'; +import HINCRBYFLOAT from './HINCRBYFLOAT'; describe('HINCRBYFLOAT', () => { - it('transformArguments', () => { - assert.deepEqual( - transformArguments('key', 'field', 1.5), - ['HINCRBYFLOAT', 'key', 'field', '1.5'] - ); - }); + it('transformArguments', () => { + assert.deepEqual( + HINCRBYFLOAT.transformArguments('key', 'field', 1.5), + ['HINCRBYFLOAT', 'key', 'field', '1.5'] + ); + }); - testUtils.testWithClient('client.hIncrByFloat', async client => { - assert.equal( - await client.hIncrByFloat('key', 'field', 1.5), - '1.5' - ); - }, GLOBAL.SERVERS.OPEN); + testUtils.testAll('hIncrByFloat', async client => { + assert.equal( + await client.hIncrByFloat('key', 'field', 1.5), + '1.5' + ); + }, { + client: GLOBAL.SERVERS.OPEN, + cluster: GLOBAL.CLUSTERS.OPEN + }); }); diff --git a/packages/client/lib/commands/HINCRBYFLOAT.ts b/packages/client/lib/commands/HINCRBYFLOAT.ts index 0e2de6e9b29..a4eea75c827 100644 --- a/packages/client/lib/commands/HINCRBYFLOAT.ts +++ b/packages/client/lib/commands/HINCRBYFLOAT.ts @@ -1,13 +1,18 @@ -import { RedisCommandArgument, RedisCommandArguments } from '.'; +import { RedisArgument, BlobStringReply, Command } from '../RESP/types'; -export const FIRST_KEY_INDEX = 1; - -export function transformArguments( - key: RedisCommandArgument, - field: RedisCommandArgument, +export default { + FIRST_KEY_INDEX: 1, + transformArguments( + key: RedisArgument, + field: RedisArgument, increment: number -): RedisCommandArguments { - return ['HINCRBYFLOAT', key, field, increment.toString()]; -} - -export declare function transformReply(): number; + ) { + return [ + 'HINCRBYFLOAT', + key, + field, + increment.toString() + ]; + }, + transformReply: undefined as unknown as () => BlobStringReply +} as const satisfies Command; diff --git a/packages/client/lib/commands/HKEYS.spec.ts b/packages/client/lib/commands/HKEYS.spec.ts index f94538f67d3..dada7b4d6fd 100644 --- a/packages/client/lib/commands/HKEYS.spec.ts +++ b/packages/client/lib/commands/HKEYS.spec.ts @@ -1,19 +1,22 @@ -import { strict as assert } from 'assert'; +import { strict as assert } from 'node:assert'; import testUtils, { GLOBAL } from '../test-utils'; -import { transformArguments } from './HKEYS'; +import HKEYS from './HKEYS'; describe('HKEYS', () => { - it('transformArguments', () => { - assert.deepEqual( - transformArguments('key'), - ['HKEYS', 'key'] - ); - }); + it('transformArguments', () => { + assert.deepEqual( + HKEYS.transformArguments('key'), + ['HKEYS', 'key'] + ); + }); - testUtils.testWithClient('client.hKeys', async client => { - assert.deepEqual( - await client.hKeys('key'), - [] - ); - }, GLOBAL.SERVERS.OPEN); + testUtils.testAll('hKeys', async client => { + assert.deepEqual( + await client.hKeys('key'), + [] + ); + }, { + client: GLOBAL.SERVERS.OPEN, + cluster: GLOBAL.CLUSTERS.OPEN + }); }); diff --git a/packages/client/lib/commands/HKEYS.ts b/packages/client/lib/commands/HKEYS.ts index 3d629733d0e..00af43f7a40 100644 --- a/packages/client/lib/commands/HKEYS.ts +++ b/packages/client/lib/commands/HKEYS.ts @@ -1,9 +1,10 @@ -import { RedisCommandArgument, RedisCommandArguments } from '.'; +import { RedisArgument, ArrayReply, BlobStringReply, Command } from '../RESP/types'; -export const FIRST_KEY_INDEX = 1; - -export function transformArguments(key: RedisCommandArgument): RedisCommandArguments { +export default { + FIRST_KEY_INDEX: 1, + IS_READ_ONLY: true, + transformArguments(key: RedisArgument) { return ['HKEYS', key]; -} - -export declare function transformReply(): Array; + }, + transformReply: undefined as unknown as () => ArrayReply +} as const satisfies Command; diff --git a/packages/client/lib/commands/HLEN.spec.ts b/packages/client/lib/commands/HLEN.spec.ts index be9d4b13a7d..2457a261299 100644 --- a/packages/client/lib/commands/HLEN.spec.ts +++ b/packages/client/lib/commands/HLEN.spec.ts @@ -1,19 +1,22 @@ -import { strict as assert } from 'assert'; +import { strict as assert } from 'node:assert'; import testUtils, { GLOBAL } from '../test-utils'; -import { transformArguments } from './HLEN'; +import HLEN from './HLEN'; describe('HLEN', () => { - it('transformArguments', () => { - assert.deepEqual( - transformArguments('key'), - ['HLEN', 'key'] - ); - }); + it('transformArguments', () => { + assert.deepEqual( + HLEN.transformArguments('key'), + ['HLEN', 'key'] + ); + }); - testUtils.testWithClient('client.hLen', async client => { - assert.equal( - await client.hLen('key'), - 0 - ); - }, GLOBAL.SERVERS.OPEN); + testUtils.testAll('hLen', async client => { + assert.equal( + await client.hLen('key'), + 0 + ); + }, { + client: GLOBAL.SERVERS.OPEN, + cluster: GLOBAL.CLUSTERS.OPEN + }); }); diff --git a/packages/client/lib/commands/HLEN.ts b/packages/client/lib/commands/HLEN.ts index 15a93d408d7..8f156d303e2 100644 --- a/packages/client/lib/commands/HLEN.ts +++ b/packages/client/lib/commands/HLEN.ts @@ -1,9 +1,10 @@ -import { RedisCommandArgument, RedisCommandArguments } from '.'; +import { RedisArgument, NumberReply, Command } from '../RESP/types'; -export const FIRST_KEY_INDEX = 1; - -export function transformArguments(key: RedisCommandArgument): RedisCommandArguments { +export default { + FIRST_KEY_INDEX: 1, + IS_READ_ONLY: true, + transformArguments(key: RedisArgument) { return ['HLEN', key]; -} - -export declare function transformReply(): number; + }, + transformReply: undefined as unknown as () => NumberReply +} as const satisfies Command; diff --git a/packages/client/lib/commands/HMGET.spec.ts b/packages/client/lib/commands/HMGET.spec.ts index a7c934b760d..99d94a6d375 100644 --- a/packages/client/lib/commands/HMGET.spec.ts +++ b/packages/client/lib/commands/HMGET.spec.ts @@ -1,28 +1,31 @@ -import { strict as assert } from 'assert'; +import { strict as assert } from 'node:assert'; import testUtils, { GLOBAL } from '../test-utils'; -import { transformArguments } from './HMGET'; +import HMGET from './HMGET'; describe('HMGET', () => { - describe('transformArguments', () => { - it('string', () => { - assert.deepEqual( - transformArguments('key', 'field'), - ['HMGET', 'key', 'field'] - ); - }); + describe('transformArguments', () => { + it('string', () => { + assert.deepEqual( + HMGET.transformArguments('key', 'field'), + ['HMGET', 'key', 'field'] + ); + }); - it('array', () => { - assert.deepEqual( - transformArguments('key', ['field1', 'field2']), - ['HMGET', 'key', 'field1', 'field2'] - ); - }); + it('array', () => { + assert.deepEqual( + HMGET.transformArguments('key', ['field1', 'field2']), + ['HMGET', 'key', 'field1', 'field2'] + ); }); + }); - testUtils.testWithClient('client.hmGet', async client => { - assert.deepEqual( - await client.hmGet('key', 'field'), - [null] - ); - }, GLOBAL.SERVERS.OPEN); + testUtils.testAll('hmGet', async client => { + assert.deepEqual( + await client.hmGet('key', 'field'), + [null] + ); + }, { + client: GLOBAL.SERVERS.OPEN, + cluster: GLOBAL.CLUSTERS.OPEN + }); }); diff --git a/packages/client/lib/commands/HMGET.ts b/packages/client/lib/commands/HMGET.ts index 64b4014abeb..df28a64be09 100644 --- a/packages/client/lib/commands/HMGET.ts +++ b/packages/client/lib/commands/HMGET.ts @@ -1,15 +1,14 @@ -import { RedisCommandArgument, RedisCommandArguments } from '.'; -import { pushVerdictArguments } from './generic-transformers'; - -export const FIRST_KEY_INDEX = 1; - -export const IS_READ_ONLY = true; - -export function transformArguments( - key: RedisCommandArgument, - fields: RedisCommandArgument | Array -): RedisCommandArguments { - return pushVerdictArguments(['HMGET', key], fields); -} - -export declare function transformReply(): Array; +import { RedisArgument, ArrayReply, BlobStringReply, NullReply, Command } from '../RESP/types'; +import { RedisVariadicArgument, pushVariadicArguments } from './generic-transformers'; + +export default { + FIRST_KEY_INDEX: 1, + IS_READ_ONLY: true, + transformArguments( + key: RedisArgument, + fields: RedisVariadicArgument + ) { + return pushVariadicArguments(['HMGET', key], fields); + }, + transformReply: undefined as unknown as () => ArrayReply +} as const satisfies Command; diff --git a/packages/client/lib/commands/HPERSIST.spec.ts b/packages/client/lib/commands/HPERSIST.spec.ts index 8cf3f1fe221..05e225e8ead 100644 --- a/packages/client/lib/commands/HPERSIST.spec.ts +++ b/packages/client/lib/commands/HPERSIST.spec.ts @@ -1,6 +1,6 @@ import { strict as assert } from 'node:assert'; import testUtils, { GLOBAL } from '../test-utils'; -import { transformArguments } from './HPERSIST'; +import HPERSIST from './HPERSIST'; import { HASH_EXPIRATION_TIME } from './HEXPIRETIME'; describe('HPERSIST', () => { @@ -9,14 +9,14 @@ describe('HPERSIST', () => { describe('transformArguments', () => { it('string', () => { assert.deepEqual( - transformArguments('key', 'field'), + HPERSIST.transformArguments('key', 'field'), ['HPERSIST', 'key', 'FIELDS', '1', 'field'] ); }); it('array', () => { assert.deepEqual( - transformArguments('key', ['field1', 'field2']), + HPERSIST.transformArguments('key', ['field1', 'field2']), ['HPERSIST', 'key', 'FIELDS', '2', 'field1', 'field2'] ); }); diff --git a/packages/client/lib/commands/HPERSIST.ts b/packages/client/lib/commands/HPERSIST.ts index 862a7548ac1..3843fd80a5e 100644 --- a/packages/client/lib/commands/HPERSIST.ts +++ b/packages/client/lib/commands/HPERSIST.ts @@ -1,10 +1,11 @@ -import { RedisCommandArgument } from '.'; -import { pushVerdictArgument } from './generic-transformers'; +import { ArrayReply, Command, NullReply, NumberReply, RedisArgument } from '../RESP/types'; +import { pushVariadicArgument, RedisVariadicArgument } from './generic-transformers'; -export const FIRST_KEY_INDEX = 1; - -export function transformArguments(key: RedisCommandArgument, fields: RedisCommandArgument | Array) { - return pushVerdictArgument(['HPERSIST', key, 'FIELDS'], fields); -} - -export declare function transformReply(): Array | null; \ No newline at end of file +export default { + FIRST_KEY_INDEX: 1, + IS_READ_ONLY: true, + transformArguments(key: RedisArgument, fields: RedisVariadicArgument) { + return pushVariadicArgument(['HPERSIST', key, 'FIELDS'], fields); + }, + transformReply: undefined as unknown as () => ArrayReply | NullReply +} as const satisfies Command; diff --git a/packages/client/lib/commands/HPEXPIRE.spec.ts b/packages/client/lib/commands/HPEXPIRE.spec.ts index 852d9f5bd21..febcb0bc96b 100644 --- a/packages/client/lib/commands/HPEXPIRE.spec.ts +++ b/packages/client/lib/commands/HPEXPIRE.spec.ts @@ -1,6 +1,6 @@ import { strict as assert } from 'node:assert'; import testUtils, { GLOBAL } from '../test-utils'; -import { transformArguments } from './HPEXPIRE'; +import HPEXPIRE from './HPEXPIRE'; import { HASH_EXPIRATION_TIME } from './HEXPIRETIME'; describe('HEXPIRE', () => { @@ -9,21 +9,21 @@ describe('HEXPIRE', () => { describe('transformArguments', () => { it('string', () => { assert.deepEqual( - transformArguments('key', 'field', 1), + HPEXPIRE.transformArguments('key', 'field', 1), ['HPEXPIRE', 'key', '1', 'FIELDS', '1', 'field'] ); }); it('array', () => { assert.deepEqual( - transformArguments('key', ['field1', 'field2'], 1), + HPEXPIRE.transformArguments('key', ['field1', 'field2'], 1), ['HPEXPIRE', 'key', '1', 'FIELDS', '2', 'field1', 'field2'] ); }); it('with set option', () => { assert.deepEqual( - transformArguments('key', ['field1'], 1, 'NX'), + HPEXPIRE.transformArguments('key', ['field1'], 1, 'NX'), ['HPEXPIRE', 'key', '1', 'NX', 'FIELDS', '1', 'field1'] ); }); diff --git a/packages/client/lib/commands/HPEXPIRE.ts b/packages/client/lib/commands/HPEXPIRE.ts index afbb056ed4e..58624f9163a 100644 --- a/packages/client/lib/commands/HPEXPIRE.ts +++ b/packages/client/lib/commands/HPEXPIRE.ts @@ -1,24 +1,24 @@ -import { RedisCommandArgument } from '.'; -import { pushVerdictArgument } from './generic-transformers'; +import { ArrayReply, Command, NullReply, RedisArgument } from '../RESP/types'; +import { pushVariadicArgument, RedisVariadicArgument } from './generic-transformers'; import { HashExpiration } from "./HEXPIRE"; -export const FIRST_KEY_INDEX = 1; - -export function transformArguments( - key: RedisCommandArgument, - fields: RedisCommandArgument | Array, - ms: number, - mode?: 'NX' | 'XX' | 'GT' | 'LT', -) { - const args = ['HPEXPIRE', key, ms.toString()]; - - if (mode) { - args.push(mode); - } - - args.push('FIELDS') - - return pushVerdictArgument(args, fields); -} - -export declare function transformReply(): Array | null; \ No newline at end of file +export default { + FIRST_KEY_INDEX: 1, + transformArguments( + key: RedisArgument, + fields: RedisVariadicArgument, + ms: number, + mode?: 'NX' | 'XX' | 'GT' | 'LT', + ) { + const args = ['HPEXPIRE', key, ms.toString()]; + + if (mode) { + args.push(mode); + } + + args.push('FIELDS') + + return pushVariadicArgument(args, fields); + }, + transformReply: undefined as unknown as () => ArrayReply | NullReply +} as const satisfies Command; diff --git a/packages/client/lib/commands/HPEXPIREAT.spec.ts b/packages/client/lib/commands/HPEXPIREAT.spec.ts index 9747cca1a2d..f91bf967cf8 100644 --- a/packages/client/lib/commands/HPEXPIREAT.spec.ts +++ b/packages/client/lib/commands/HPEXPIREAT.spec.ts @@ -1,6 +1,6 @@ import { strict as assert } from 'node:assert'; import testUtils, { GLOBAL } from '../test-utils'; -import { transformArguments } from './HPEXPIREAT'; +import HPEXPIREAT from './HPEXPIREAT'; import { HASH_EXPIRATION_TIME } from './HEXPIRETIME'; describe('HPEXPIREAT', () => { @@ -9,14 +9,14 @@ describe('HPEXPIREAT', () => { describe('transformArguments', () => { it('string + number', () => { assert.deepEqual( - transformArguments('key', 'field', 1), + HPEXPIREAT.transformArguments('key', 'field', 1), ['HPEXPIREAT', 'key', '1', 'FIELDS', '1', 'field'] ); }); it('array + number', () => { assert.deepEqual( - transformArguments('key', ['field1', 'field2'], 1), + HPEXPIREAT.transformArguments('key', ['field1', 'field2'], 1), ['HPEXPIREAT', 'key', '1', 'FIELDS', '2', 'field1', 'field2'] ); }); @@ -24,14 +24,14 @@ describe('HPEXPIREAT', () => { it('date', () => { const d = new Date(); assert.deepEqual( - transformArguments('key', ['field1'], d), + HPEXPIREAT.transformArguments('key', ['field1'], d), ['HPEXPIREAT', 'key', d.getTime().toString(), 'FIELDS', '1', 'field1'] ); }); it('with set option', () => { assert.deepEqual( - transformArguments('key', ['field1'], 1, 'XX'), + HPEXPIREAT.transformArguments('key', ['field1'], 1, 'XX'), ['HPEXPIREAT', 'key', '1', 'XX', 'FIELDS', '1', 'field1'] ); }); diff --git a/packages/client/lib/commands/HPEXPIREAT.ts b/packages/client/lib/commands/HPEXPIREAT.ts index b6e01d8ee5c..a6250d99432 100644 --- a/packages/client/lib/commands/HPEXPIREAT.ts +++ b/packages/client/lib/commands/HPEXPIREAT.ts @@ -1,25 +1,24 @@ -import { RedisCommandArgument } from '.'; -import { pushVerdictArgument, transformEXAT, transformPXAT } from './generic-transformers'; +import { ArrayReply, Command, NullReply, RedisArgument } from '../RESP/types'; +import { pushVariadicArgument, RedisVariadicArgument, transformPXAT } from './generic-transformers'; import { HashExpiration } from './HEXPIRE'; -export const FIRST_KEY_INDEX = 1; -export const IS_READ_ONLY = true; +export default { + FIRST_KEY_INDEX: 1, + transformArguments( + key: RedisArgument, + fields: RedisVariadicArgument, + timestamp: number | Date, + mode?: 'NX' | 'XX' | 'GT' | 'LT' + ) { + const args = ['HPEXPIREAT', key, transformPXAT(timestamp)]; -export function transformArguments( - key: RedisCommandArgument, - fields: RedisCommandArgument | Array, - timestamp: number | Date, - mode?: 'NX' | 'XX' | 'GT' | 'LT' -) { - const args = ['HPEXPIREAT', key, transformPXAT(timestamp)]; + if (mode) { + args.push(mode); + } - if (mode) { - args.push(mode); - } + args.push('FIELDS') - args.push('FIELDS') - - return pushVerdictArgument(args, fields); -} - -export declare function transformReply(): Array | null; \ No newline at end of file + return pushVariadicArgument(args, fields); + }, + transformReply: undefined as unknown as () => ArrayReply | NullReply +} as const satisfies Command; diff --git a/packages/client/lib/commands/HPEXPIRETIME.spec.ts b/packages/client/lib/commands/HPEXPIRETIME.spec.ts index ff03b73c71d..a66988c428c 100644 --- a/packages/client/lib/commands/HPEXPIRETIME.spec.ts +++ b/packages/client/lib/commands/HPEXPIRETIME.spec.ts @@ -1,6 +1,6 @@ import { strict as assert } from 'node:assert'; import testUtils, { GLOBAL } from '../test-utils'; -import { transformArguments } from './HPEXPIRETIME'; +import HPEXPIRETIME from './HPEXPIRETIME'; import { HASH_EXPIRATION_TIME } from './HEXPIRETIME'; describe('HPEXPIRETIME', () => { @@ -9,14 +9,14 @@ describe('HPEXPIRETIME', () => { describe('transformArguments', () => { it('string', () => { assert.deepEqual( - transformArguments('key', 'field'), + HPEXPIRETIME.transformArguments('key', 'field'), ['HPEXPIRETIME', 'key', 'FIELDS', '1', 'field'] ); }); it('array', () => { assert.deepEqual( - transformArguments('key', ['field1', 'field2']), + HPEXPIRETIME.transformArguments('key', ['field1', 'field2']), ['HPEXPIRETIME', 'key', 'FIELDS', '2', 'field1', 'field2'] ); }); diff --git a/packages/client/lib/commands/HPEXPIRETIME.ts b/packages/client/lib/commands/HPEXPIRETIME.ts index 22a794ccefa..acdccf25119 100644 --- a/packages/client/lib/commands/HPEXPIRETIME.ts +++ b/packages/client/lib/commands/HPEXPIRETIME.ts @@ -1,11 +1,11 @@ -import { RedisCommandArgument } from '.'; -import { pushVerdictArgument } from './generic-transformers'; +import { ArrayReply, Command, NullReply, NumberReply, RedisArgument } from '../RESP/types'; +import { pushVariadicArgument, RedisVariadicArgument } from './generic-transformers'; -export const FIRST_KEY_INDEX = 1; -export const IS_READ_ONLY = true; - -export function transformArguments(key: RedisCommandArgument, fields: RedisCommandArgument | Array) { - return pushVerdictArgument(['HPEXPIRETIME', key, 'FIELDS'], fields); -} - -export declare function transformReply(): Array | null; \ No newline at end of file +export default { + FIRST_KEY_INDEX: 1, + IS_READ_ONLY: true, + transformArguments(key: RedisArgument, fields: RedisVariadicArgument) { + return pushVariadicArgument(['HPEXPIRETIME', key, 'FIELDS'], fields); + }, + transformReply: undefined as unknown as () => ArrayReply | NullReply +} as const satisfies Command; diff --git a/packages/client/lib/commands/HPTTL.spec.ts b/packages/client/lib/commands/HPTTL.spec.ts index ddca26ea85b..7280ef841ca 100644 --- a/packages/client/lib/commands/HPTTL.spec.ts +++ b/packages/client/lib/commands/HPTTL.spec.ts @@ -1,6 +1,6 @@ import { strict as assert } from 'node:assert'; import testUtils, { GLOBAL } from '../test-utils'; -import { transformArguments } from './HPTTL'; +import HPTTL from './HPTTL'; import { HASH_EXPIRATION_TIME } from './HEXPIRETIME'; describe('HPTTL', () => { @@ -9,14 +9,14 @@ describe('HPTTL', () => { describe('transformArguments', () => { it('string', () => { assert.deepEqual( - transformArguments('key', 'field'), + HPTTL.transformArguments('key', 'field'), ['HPTTL', 'key', 'FIELDS', '1', 'field'] ); }); it('array', () => { assert.deepEqual( - transformArguments('key', ['field1', 'field2']), + HPTTL.transformArguments('key', ['field1', 'field2']), ['HPTTL', 'key', 'FIELDS', '2', 'field1', 'field2'] ); }); diff --git a/packages/client/lib/commands/HPTTL.ts b/packages/client/lib/commands/HPTTL.ts index 988b805c0c9..4ab069db74e 100644 --- a/packages/client/lib/commands/HPTTL.ts +++ b/packages/client/lib/commands/HPTTL.ts @@ -1,11 +1,11 @@ -import { RedisCommandArgument } from '.'; -import { pushVerdictArgument } from './generic-transformers'; +import { ArrayReply, Command, NullReply, NumberReply, RedisArgument } from '../RESP/types'; +import { pushVariadicArgument, RedisVariadicArgument } from './generic-transformers'; -export const FIRST_KEY_INDEX = 1; -export const IS_READ_ONLY = true; - -export function transformArguments(key: RedisCommandArgument, fields: RedisCommandArgument | Array) { - return pushVerdictArgument(['HPTTL', key, 'FIELDS'], fields); -} - -export declare function transformReply(): Array | null; +export default { + FIRST_KEY_INDEX: 1, + IS_READ_ONLY: true, + transformArguments(key: RedisArgument, fields: RedisVariadicArgument) { + return pushVariadicArgument(['HPTTL', key, 'FIELDS'], fields); + }, + transformReply: undefined as unknown as () => ArrayReply | NullReply +} as const satisfies Command; diff --git a/packages/client/lib/commands/HRANDFIELD.spec.ts b/packages/client/lib/commands/HRANDFIELD.spec.ts index df0a4fc7a1d..33f2d281803 100644 --- a/packages/client/lib/commands/HRANDFIELD.spec.ts +++ b/packages/client/lib/commands/HRANDFIELD.spec.ts @@ -1,21 +1,24 @@ -import { strict as assert } from 'assert'; +import { strict as assert } from 'node:assert'; import testUtils, { GLOBAL } from '../test-utils'; -import { transformArguments } from './HRANDFIELD'; +import HRANDFIELD from './HRANDFIELD'; describe('HRANDFIELD', () => { - testUtils.isVersionGreaterThanHook([6, 2]); + testUtils.isVersionGreaterThanHook([6, 2]); - it('transformArguments', () => { - assert.deepEqual( - transformArguments('key'), - ['HRANDFIELD', 'key'] - ); - }); + it('transformArguments', () => { + assert.deepEqual( + HRANDFIELD.transformArguments('key'), + ['HRANDFIELD', 'key'] + ); + }); - testUtils.testWithClient('client.hRandField', async client => { - assert.equal( - await client.hRandField('key'), - null - ); - }, GLOBAL.SERVERS.OPEN); + testUtils.testAll('hRandField', async client => { + assert.equal( + await client.hRandField('key'), + null + ); + }, { + client: GLOBAL.SERVERS.OPEN, + cluster: GLOBAL.CLUSTERS.OPEN + }); }); diff --git a/packages/client/lib/commands/HRANDFIELD.ts b/packages/client/lib/commands/HRANDFIELD.ts index a2c70aabd52..be878e244a0 100644 --- a/packages/client/lib/commands/HRANDFIELD.ts +++ b/packages/client/lib/commands/HRANDFIELD.ts @@ -1,11 +1,10 @@ -import { RedisCommandArgument, RedisCommandArguments } from '.'; +import { RedisArgument, BlobStringReply, NullReply, Command } from '../RESP/types'; -export const FIRST_KEY_INDEX = 1; - -export const IS_READ_ONLY = true; - -export function transformArguments(key: RedisCommandArgument): RedisCommandArguments { +export default { + FIRST_KEY_INDEX: 1, + IS_READ_ONLY: true, + transformArguments(key: RedisArgument) { return ['HRANDFIELD', key]; -} - -export declare function transformReply(): RedisCommandArgument | null; + }, + transformReply: undefined as unknown as () => BlobStringReply | NullReply +} as const satisfies Command; diff --git a/packages/client/lib/commands/HRANDFIELD_COUNT.spec.ts b/packages/client/lib/commands/HRANDFIELD_COUNT.spec.ts index 4bfce0f7e23..99788dc4962 100644 --- a/packages/client/lib/commands/HRANDFIELD_COUNT.spec.ts +++ b/packages/client/lib/commands/HRANDFIELD_COUNT.spec.ts @@ -1,21 +1,24 @@ -import { strict as assert } from 'assert'; +import { strict as assert } from 'node:assert'; import testUtils, { GLOBAL } from '../test-utils'; -import { transformArguments } from './HRANDFIELD_COUNT'; +import HRANDFIELD_COUNT from './HRANDFIELD_COUNT'; describe('HRANDFIELD COUNT', () => { - testUtils.isVersionGreaterThanHook([6, 2, 5]); + testUtils.isVersionGreaterThanHook([6, 2, 5]); - it('transformArguments', () => { - assert.deepEqual( - transformArguments('key', 1), - ['HRANDFIELD', 'key', '1'] - ); - }); + it('transformArguments', () => { + assert.deepEqual( + HRANDFIELD_COUNT.transformArguments('key', 1), + ['HRANDFIELD', 'key', '1'] + ); + }); - testUtils.testWithClient('client.hRandFieldCount', async client => { - assert.deepEqual( - await client.hRandFieldCount('key', 1), - [] - ); - }, GLOBAL.SERVERS.OPEN); + testUtils.testAll('hRandFieldCount', async client => { + assert.deepEqual( + await client.hRandFieldCount('key', 1), + [] + ); + }, { + client: GLOBAL.SERVERS.OPEN, + cluster: GLOBAL.CLUSTERS.OPEN + }); }); diff --git a/packages/client/lib/commands/HRANDFIELD_COUNT.ts b/packages/client/lib/commands/HRANDFIELD_COUNT.ts index 01b8df63273..4b6f42a115b 100644 --- a/packages/client/lib/commands/HRANDFIELD_COUNT.ts +++ b/packages/client/lib/commands/HRANDFIELD_COUNT.ts @@ -1,16 +1,10 @@ -import { RedisCommandArgument, RedisCommandArguments } from '.'; -import { transformArguments as transformHRandFieldArguments } from './HRANDFIELD'; +import { RedisArgument, ArrayReply, BlobStringReply, Command } from '../RESP/types'; -export { FIRST_KEY_INDEX, IS_READ_ONLY } from './HRANDFIELD'; - -export function transformArguments( - key: RedisCommandArgument, - count: number -): RedisCommandArguments { - return [ - ...transformHRandFieldArguments(key), - count.toString() - ]; -} - -export declare function transformReply(): Array; +export default { + FIRST_KEY_INDEX: 1, + IS_READ_ONLY: true, + transformArguments(key: RedisArgument, count: number) { + return ['HRANDFIELD', key, count.toString()]; + }, + transformReply: undefined as unknown as () => ArrayReply +} as const satisfies Command; diff --git a/packages/client/lib/commands/HRANDFIELD_COUNT_WITHVALUES.spec.ts b/packages/client/lib/commands/HRANDFIELD_COUNT_WITHVALUES.spec.ts index c4e6409a726..e69de29bb2d 100644 --- a/packages/client/lib/commands/HRANDFIELD_COUNT_WITHVALUES.spec.ts +++ b/packages/client/lib/commands/HRANDFIELD_COUNT_WITHVALUES.spec.ts @@ -1,21 +0,0 @@ -import { strict as assert } from 'assert'; -import testUtils, { GLOBAL } from '../test-utils'; -import { transformArguments } from './HRANDFIELD_COUNT_WITHVALUES'; - -describe('HRANDFIELD COUNT WITHVALUES', () => { - testUtils.isVersionGreaterThanHook([6, 2, 5]); - - it('transformArguments', () => { - assert.deepEqual( - transformArguments('key', 1), - ['HRANDFIELD', 'key', '1', 'WITHVALUES'] - ); - }); - - testUtils.testWithClient('client.hRandFieldCountWithValues', async client => { - assert.deepEqual( - await client.hRandFieldCountWithValues('key', 1), - Object.create(null) - ); - }, GLOBAL.SERVERS.OPEN); -}); diff --git a/packages/client/lib/commands/HRANDFIELD_COUNT_WITHVALUES.ts b/packages/client/lib/commands/HRANDFIELD_COUNT_WITHVALUES.ts index 3e09dbb9a14..ab36183c4ad 100644 --- a/packages/client/lib/commands/HRANDFIELD_COUNT_WITHVALUES.ts +++ b/packages/client/lib/commands/HRANDFIELD_COUNT_WITHVALUES.ts @@ -1,16 +1,39 @@ -import { RedisCommandArgument, RedisCommandArguments } from '.'; -import { transformArguments as transformHRandFieldCountArguments } from './HRANDFIELD_COUNT'; +import { RedisArgument, ArrayReply, TuplesReply, BlobStringReply, UnwrapReply, Command } from '../RESP/types'; -export { FIRST_KEY_INDEX, IS_READ_ONLY } from './HRANDFIELD_COUNT'; +export type HRandFieldCountWithValuesReply = Array<{ + field: BlobStringReply; + value: BlobStringReply; +}>; -export function transformArguments( - key: RedisCommandArgument, - count: number -): RedisCommandArguments { - return [ - ...transformHRandFieldCountArguments(key, count), - 'WITHVALUES' - ]; -} +export default { + FIRST_KEY_INDEX: 1, + IS_READ_ONLY: true, + transformArguments(key: RedisArgument, count: number) { + return ['HRANDFIELD', key, count.toString(), 'WITHVALUES']; + }, + transformReply: { + 2: (rawReply: UnwrapReply>) => { + const reply: HRandFieldCountWithValuesReply = []; -export { transformTuplesReply as transformReply } from './generic-transformers'; + let i = 0; + while (i < rawReply.length) { + reply.push({ + field: rawReply[i++], + value: rawReply[i++] + }); + } + + return reply; + }, + 3: (reply: UnwrapReply>>) => { + return reply.map(entry => { + const [field, value] = entry as unknown as UnwrapReply; + return { + field, + value + }; + }) satisfies HRandFieldCountWithValuesReply; + } + } +} as const satisfies Command; + \ No newline at end of file diff --git a/packages/client/lib/commands/HSCAN.spec.ts b/packages/client/lib/commands/HSCAN.spec.ts index 6757888a875..a5f3cdca16c 100644 --- a/packages/client/lib/commands/HSCAN.spec.ts +++ b/packages/client/lib/commands/HSCAN.spec.ts @@ -1,90 +1,82 @@ -import { strict as assert } from 'assert'; +import { strict as assert } from 'node:assert'; import testUtils, { GLOBAL } from '../test-utils'; -import { transformArguments, transformReply } from './HSCAN'; +import HSCAN from './HSCAN'; describe('HSCAN', () => { - describe('transformArguments', () => { - it('cusror only', () => { - assert.deepEqual( - transformArguments('key', 0), - ['HSCAN', 'key', '0'] - ); - }); - - it('with MATCH', () => { - assert.deepEqual( - transformArguments('key', 0, { - MATCH: 'pattern' - }), - ['HSCAN', 'key', '0', 'MATCH', 'pattern'] - ); - }); - - it('with COUNT', () => { - assert.deepEqual( - transformArguments('key', 0, { - COUNT: 1 - }), - ['HSCAN', 'key', '0', 'COUNT', '1'] - ); - }); + describe('transformArguments', () => { + it('cusror only', () => { + assert.deepEqual( + HSCAN.transformArguments('key', '0'), + ['HSCAN', 'key', '0'] + ); + }); - it('with MATCH & COUNT', () => { - assert.deepEqual( - transformArguments('key', 0, { - MATCH: 'pattern', - COUNT: 1 - }), - ['HSCAN', 'key', '0', 'MATCH', 'pattern', 'COUNT', '1'] - ); - }); + it('with MATCH', () => { + assert.deepEqual( + HSCAN.transformArguments('key', '0', { + MATCH: 'pattern' + }), + ['HSCAN', 'key', '0', 'MATCH', 'pattern'] + ); }); - describe('transformReply', () => { - it('without tuples', () => { - assert.deepEqual( - transformReply(['0', []]), - { - cursor: 0, - tuples: [] - } - ); - }); + it('with COUNT', () => { + assert.deepEqual( + HSCAN.transformArguments('key', '0', { + COUNT: 1 + }), + ['HSCAN', 'key', '0', 'COUNT', '1'] + ); + }); - it('with tuples', () => { - assert.deepEqual( - transformReply(['0', ['field', 'value']]), - { - cursor: 0, - tuples: [{ - field: 'field', - value: 'value' - }] - } - ); - }); + it('with MATCH & COUNT', () => { + assert.deepEqual( + HSCAN.transformArguments('key', '0', { + MATCH: 'pattern', + COUNT: 1 + }), + ['HSCAN', 'key', '0', 'MATCH', 'pattern', 'COUNT', '1'] + ); }); + }); - testUtils.testWithClient('client.hScan', async client => { - assert.deepEqual( - await client.hScan('key', 0), - { - cursor: 0, - tuples: [] - } - ); + describe('transformReply', () => { + it('without tuples', () => { + assert.deepEqual( + HSCAN.transformReply(['0' as any, []]), + { + cursor: '0', + entries: [] + } + ); + }); + + it('with tuples', () => { + assert.deepEqual( + HSCAN.transformReply(['0' as any, ['field', 'value'] as any]), + { + cursor: '0', + entries: [{ + field: 'field', + value: 'value' + }] + } + ); + }); + }); - await Promise.all([ - client.hSet('key', 'a', '1'), - client.hSet('key', 'b', '2') - ]); + testUtils.testWithClient('client.hScan', async client => { + const [, reply] = await Promise.all([ + client.hSet('key', 'field', 'value'), + client.hScan('key', '0') + ]); - assert.deepEqual( - await client.hScan('key', 0), - { - cursor: 0, - tuples: [{field: 'a', value: '1'}, {field: 'b', value: '2'}] - } - ); - }, GLOBAL.SERVERS.OPEN); + assert.deepEqual(reply, { + cursor: '0', + entries: [{ + field: 'field', + value: 'value' + }] + }); + }, GLOBAL.SERVERS.OPEN); }); diff --git a/packages/client/lib/commands/HSCAN.ts b/packages/client/lib/commands/HSCAN.ts index 5167693b604..db52db99fef 100644 --- a/packages/client/lib/commands/HSCAN.ts +++ b/packages/client/lib/commands/HSCAN.ts @@ -1,44 +1,34 @@ -import { RedisCommandArgument, RedisCommandArguments } from '.'; -import { ScanOptions, pushScanArguments } from './generic-transformers'; +import { RedisArgument, BlobStringReply, Command } from '../RESP/types'; +import { ScanCommonOptions, pushScanArguments } from './SCAN'; -export const FIRST_KEY_INDEX = 1; - -export const IS_READ_ONLY = true; - -export function transformArguments( - key: RedisCommandArgument, - cursor: number, - options?: ScanOptions -): RedisCommandArguments { - return pushScanArguments([ - 'HSCAN', - key - ], cursor, options); -} - -export type HScanRawReply = [RedisCommandArgument, Array]; - -export interface HScanTuple { - field: RedisCommandArgument; - value: RedisCommandArgument; -} - -interface HScanReply { - cursor: number; - tuples: Array; +export interface HScanEntry { + field: BlobStringReply; + value: BlobStringReply; } -export function transformReply([cursor, rawTuples]: HScanRawReply): HScanReply { - const parsedTuples = []; - for (let i = 0; i < rawTuples.length; i += 2) { - parsedTuples.push({ - field: rawTuples[i], - value: rawTuples[i + 1] - }); +export default { + FIRST_KEY_INDEX: 1, + IS_READ_ONLY: true, + transformArguments( + key: RedisArgument, + cursor: RedisArgument, + options?: ScanCommonOptions + ) { + return pushScanArguments(['HSCAN', key], cursor, options); + }, + transformReply([cursor, rawEntries]: [BlobStringReply, Array]) { + const entries = []; + let i = 0; + while (i < rawEntries.length) { + entries.push({ + field: rawEntries[i++], + value: rawEntries[i++] + } satisfies HScanEntry); } return { - cursor: Number(cursor), - tuples: parsedTuples + cursor, + entries }; -} + } +} as const satisfies Command; diff --git a/packages/client/lib/commands/HSCAN_NOVALUES.spec.ts b/packages/client/lib/commands/HSCAN_NOVALUES.spec.ts index 7e05b841e43..1283a116dc5 100644 --- a/packages/client/lib/commands/HSCAN_NOVALUES.spec.ts +++ b/packages/client/lib/commands/HSCAN_NOVALUES.spec.ts @@ -1,79 +1,82 @@ -import { strict as assert } from 'assert'; +import { strict as assert } from 'node:assert'; import testUtils, { GLOBAL } from '../test-utils'; -import { transformArguments, transformReply } from './HSCAN_NOVALUES'; +import HSCAN_NOVALUES from './HSCAN_NOVALUES'; +import { BlobStringReply } from '../RESP/types'; describe('HSCAN_NOVALUES', () => { - testUtils.isVersionGreaterThanHook([7, 4]); - - describe('transformArguments', () => { - it('cusror only', () => { - assert.deepEqual( - transformArguments('key', 0), - ['HSCAN', 'key', '0', 'NOVALUES'] - ); - }); + testUtils.isVersionGreaterThanHook([7,4]); + + describe('transformArguments', () => { + it('cusror only', () => { + assert.deepEqual( + HSCAN_NOVALUES.transformArguments('key', '0'), + ['HSCAN', 'key', '0', 'NOVALUES'] + ); + }); + + it('with MATCH', () => { + assert.deepEqual( + HSCAN_NOVALUES.transformArguments('key', '0', { + MATCH: 'pattern' + }), + ['HSCAN', 'key', '0', 'MATCH', 'pattern', 'NOVALUES'] + ); + }); - it('with MATCH', () => { - assert.deepEqual( - transformArguments('key', 0, { - MATCH: 'pattern' - }), - ['HSCAN', 'key', '0', 'MATCH', 'pattern', 'NOVALUES'] - ); - }); + it('with COUNT', () => { + assert.deepEqual( + HSCAN_NOVALUES.transformArguments('key', '0', { + COUNT: 1 + }), + ['HSCAN', 'key', '0', 'COUNT', '1', 'NOVALUES'] + ); + }); - it('with COUNT', () => { - assert.deepEqual( - transformArguments('key', 0, { - COUNT: 1 - }), - ['HSCAN', 'key', '0', 'COUNT', '1', 'NOVALUES'] - ); - }); + it('with MATCH & COUNT', () => { + assert.deepEqual( + HSCAN_NOVALUES.transformArguments('key', '0', { + MATCH: 'pattern', + COUNT: 1 + }), + ['HSCAN', 'key', '0', 'MATCH', 'pattern', 'COUNT', '1', 'NOVALUES'] + ); }); + }); - describe('transformReply', () => { - it('without keys', () => { - assert.deepEqual( - transformReply(['0', []]), - { - cursor: 0, - keys: [] - } - ); - }); + describe('transformReply', () => { + it('without keys', () => { + assert.deepEqual( + HSCAN_NOVALUES.transformReply(['0' as any, []]), + { + cursor: '0', + fields: [] + } + ); + }); - it('with keys', () => { - assert.deepEqual( - transformReply(['0', ['key1', 'key2']]), - { - cursor: 0, - keys: ['key1', 'key2'] - } - ); - }); + it('with keys', () => { + assert.deepEqual( + HSCAN_NOVALUES.transformReply(['0' as any, ['key1', 'key2'] as any]), + { + cursor: '0', + fields: ['key1', 'key2'] + } + ); }); + }); - testUtils.testWithClient('client.hScanNoValues', async client => { - assert.deepEqual( - await client.hScanNoValues('key', 0), - { - cursor: 0, - keys: [] - } - ); - await Promise.all([ - client.hSet('key', 'a', '1'), - client.hSet('key', 'b', '2') - ]); + testUtils.testWithClient('client.hScanNoValues', async client => { + const [, reply] = await Promise.all([ + client.hSet('key', 'field', 'value'), + client.hScanNoValues('key', '0') + ]); - assert.deepEqual( - await client.hScanNoValues('key', 0), - { - cursor: 0, - keys: ['a', 'b'] - } - ); - }, GLOBAL.SERVERS.OPEN); + assert.deepEqual(reply, { + cursor: '0', + fields: [ + 'field', + ] + }); + }, GLOBAL.SERVERS.OPEN); }); diff --git a/packages/client/lib/commands/HSCAN_NOVALUES.ts b/packages/client/lib/commands/HSCAN_NOVALUES.ts index 37a929754c6..35ff861338c 100644 --- a/packages/client/lib/commands/HSCAN_NOVALUES.ts +++ b/packages/client/lib/commands/HSCAN_NOVALUES.ts @@ -1,27 +1,22 @@ -import { RedisCommandArgument, RedisCommandArguments } from '.'; -import { ScanOptions } from './generic-transformers'; -import { HScanRawReply, transformArguments as transformHScanArguments } from './HSCAN'; +import { RedisArgument, BlobStringReply, Command } from '../RESP/types'; +import { ScanCommonOptions, pushScanArguments } from './SCAN'; -export { FIRST_KEY_INDEX, IS_READ_ONLY } from './HSCAN'; - -export function transformArguments( - key: RedisCommandArgument, - cursor: number, - options?: ScanOptions -): RedisCommandArguments { - const args = transformHScanArguments(key, cursor, options); +export default { + FIRST_KEY_INDEX: 1, + IS_READ_ONLY: true, + transformArguments( + key: RedisArgument, + cursor: RedisArgument, + options?: ScanCommonOptions + ) { + const args = pushScanArguments(['HSCAN', key], cursor, options); args.push('NOVALUES'); return args; -} - -interface HScanNoValuesReply { - cursor: number; - keys: Array; -} - -export function transformReply([cursor, rawData]: HScanRawReply): HScanNoValuesReply { + }, + transformReply([cursor, fields]: [BlobStringReply, Array]) { return { - cursor: Number(cursor), - keys: rawData + cursor, + fields }; -} + } +} as const satisfies Command; diff --git a/packages/client/lib/commands/HSET.spec.ts b/packages/client/lib/commands/HSET.spec.ts index 73bc966f87a..eb5fdcd9b32 100644 --- a/packages/client/lib/commands/HSET.spec.ts +++ b/packages/client/lib/commands/HSET.spec.ts @@ -1,74 +1,70 @@ -import { strict as assert } from 'assert'; -import { transformArguments } from './HSET'; +import { strict as assert } from 'node:assert'; import testUtils, { GLOBAL } from '../test-utils'; +import HSET from './HSET'; describe('HSET', () => { - describe('transformArguments', () => { - describe('field, value', () => { - it('string', () => { - assert.deepEqual( - transformArguments('key', 'field', 'value'), - ['HSET', 'key', 'field', 'value'] - ); - }); - - it('number', () => { - assert.deepEqual( - transformArguments('key', 1, 2), - ['HSET', 'key', '1', '2'] - ); - }); + describe('transformArguments', () => { + describe('field, value', () => { + it('string', () => { + assert.deepEqual( + HSET.transformArguments('key', 'field', 'value'), + ['HSET', 'key', 'field', 'value'] + ); + }); - it('Buffer', () => { - assert.deepEqual( - transformArguments(Buffer.from('key'), Buffer.from('field'), Buffer.from('value')), - ['HSET', Buffer.from('key'), Buffer.from('field'), Buffer.from('value')] - ); - }); - }); + it('number', () => { + assert.deepEqual( + HSET.transformArguments('key', 1, 2), + ['HSET', 'key', '1', '2'] + ); + }); - it('Map', () => { - assert.deepEqual( - transformArguments('key', new Map([['field', 'value']])), - ['HSET', 'key', 'field', 'value'] - ); - }); + it('Buffer', () => { + assert.deepEqual( + HSET.transformArguments(Buffer.from('key'), Buffer.from('field'), Buffer.from('value')), + ['HSET', Buffer.from('key'), Buffer.from('field'), Buffer.from('value')] + ); + }); + }); - it('Array', () => { - assert.deepEqual( - transformArguments('key', [['field', 'value']]), - ['HSET', 'key', 'field', 'value'] - ); - }); + it('Map', () => { + assert.deepEqual( + HSET.transformArguments('key', new Map([['field', 'value']])), + ['HSET', 'key', 'field', 'value'] + ); + }); - describe('Object', () => { - it('string', () => { - assert.deepEqual( - transformArguments('key', { field: 'value' }), - ['HSET', 'key', 'field', 'value'] - ); - }); - - it('Buffer', () => { - assert.deepEqual( - transformArguments('key', { field: Buffer.from('value') }), - ['HSET', 'key', 'field', Buffer.from('value')] - ); - }); - }); + it('Array', () => { + assert.deepEqual( + HSET.transformArguments('key', [['field', 'value']]), + ['HSET', 'key', 'field', 'value'] + ); }); - testUtils.testWithClient('client.hSet', async client => { - assert.equal( - await client.hSet('key', 'field', 'value'), - 1 + describe('Object', () => { + it('string', () => { + assert.deepEqual( + HSET.transformArguments('key', { field: 'value' }), + ['HSET', 'key', 'field', 'value'] ); - }, GLOBAL.SERVERS.OPEN); + }); - testUtils.testWithCluster('cluster.hSet', async cluster => { - assert.equal( - await cluster.hSet('key', { field: 'value' }), - 1 + it('Buffer', () => { + assert.deepEqual( + HSET.transformArguments('key', { field: Buffer.from('value') }), + ['HSET', 'key', 'field', Buffer.from('value')] ); - }, GLOBAL.CLUSTERS.OPEN); + }); + }); + }); + + testUtils.testAll('hSet', async client => { + assert.equal( + await client.hSet('key', 'field', 'value'), + 1 + ); + }, { + client: GLOBAL.SERVERS.OPEN, + cluster: GLOBAL.CLUSTERS.OPEN + }); }); diff --git a/packages/client/lib/commands/HSET.ts b/packages/client/lib/commands/HSET.ts index 261ef98c779..e14aa9d06f6 100644 --- a/packages/client/lib/commands/HSET.ts +++ b/packages/client/lib/commands/HSET.ts @@ -1,73 +1,75 @@ -import { RedisCommandArgument, RedisCommandArguments } from '.'; +import { RedisArgument, NumberReply, Command } from '../RESP/types'; -export const FIRST_KEY_INDEX = 1; +export type HashTypes = RedisArgument | number; -type Types = RedisCommandArgument | number; +type HSETObject = Record; -type HSETObject = Record; +type HSETMap = Map; -type HSETMap = Map; +type HSETTuples = Array<[HashTypes, HashTypes]> | Array; -type HSETTuples = Array<[Types, Types]> | Array; +type GenericArguments = [key: RedisArgument]; -type GenericArguments = [key: RedisCommandArgument]; - -type SingleFieldArguments = [...generic: GenericArguments, field: Types, value: Types]; +type SingleFieldArguments = [...generic: GenericArguments, field: HashTypes, value: HashTypes]; type MultipleFieldsArguments = [...generic: GenericArguments, value: HSETObject | HSETMap | HSETTuples]; -export function transformArguments(...[ key, value, fieldValue ]: SingleFieldArguments | MultipleFieldsArguments): RedisCommandArguments { - const args: RedisCommandArguments = ['HSET', key]; +export type HSETArguments = SingleFieldArguments | MultipleFieldsArguments; + +export default { + FIRST_KEY_INDEX: 1, + transformArguments(...[key, value, fieldValue]: SingleFieldArguments | MultipleFieldsArguments) { + const args: Array = ['HSET', key]; - if (typeof value === 'string' || typeof value === 'number' || Buffer.isBuffer(value)) { - args.push( - convertValue(value), - convertValue(fieldValue!) - ); + if (typeof value === 'string' || typeof value === 'number' || value instanceof Buffer) { + args.push( + convertValue(value), + convertValue(fieldValue!) + ); } else if (value instanceof Map) { - pushMap(args, value); + pushMap(args, value); } else if (Array.isArray(value)) { - pushTuples(args, value); + pushTuples(args, value); } else { - pushObject(args, value); + pushObject(args, value); } return args; + }, + transformReply: undefined as unknown as () => NumberReply +} as const satisfies Command; + +function pushMap(args: Array, map: HSETMap): void { + for (const [key, value] of map.entries()) { + args.push( + convertValue(key), + convertValue(value) + ); + } } -function pushMap(args: RedisCommandArguments, map: HSETMap): void { - for (const [key, value] of map.entries()) { - args.push( - convertValue(key), - convertValue(value) - ); +function pushTuples(args: Array, tuples: HSETTuples): void { + for (const tuple of tuples) { + if (Array.isArray(tuple)) { + pushTuples(args, tuple); + continue; } -} - -function pushTuples(args: RedisCommandArguments, tuples: HSETTuples): void { - for (const tuple of tuples) { - if (Array.isArray(tuple)) { - pushTuples(args, tuple); - continue; - } - args.push(convertValue(tuple)); - } + args.push(convertValue(tuple)); + } } -function pushObject(args: RedisCommandArguments, object: HSETObject): void { - for (const key of Object.keys(object)) { - args.push( - convertValue(key), - convertValue(object[key]) - ); - } +function pushObject(args: Array, object: HSETObject): void { + for (const key of Object.keys(object)) { + args.push( + convertValue(key), + convertValue(object[key]) + ); + } } -function convertValue(value: Types): RedisCommandArgument { - return typeof value === 'number' ? - value.toString() : - value; +function convertValue(value: HashTypes): RedisArgument { + return typeof value === 'number' ? + value.toString() : + value; } - -export declare function transformReply(): number; diff --git a/packages/client/lib/commands/HSETNX.spec.ts b/packages/client/lib/commands/HSETNX.spec.ts index 190fa50ae97..522732624e1 100644 --- a/packages/client/lib/commands/HSETNX.spec.ts +++ b/packages/client/lib/commands/HSETNX.spec.ts @@ -1,19 +1,22 @@ -import { strict as assert } from 'assert'; +import { strict as assert } from 'node:assert'; import testUtils, { GLOBAL } from '../test-utils'; -import { transformArguments } from './HSETNX'; +import HSETNX from './HSETNX'; describe('HSETNX', () => { - it('transformArguments', () => { - assert.deepEqual( - transformArguments('key', 'field', 'value'), - ['HSETNX', 'key', 'field', 'value'] - ); - }); + it('transformArguments', () => { + assert.deepEqual( + HSETNX.transformArguments('key', 'field', 'value'), + ['HSETNX', 'key', 'field', 'value'] + ); + }); - testUtils.testWithClient('client.hSetNX', async client => { - assert.equal( - await client.hSetNX('key', 'field', 'value'), - true - ); - }, GLOBAL.SERVERS.OPEN); + testUtils.testAll('hSetNX', async client => { + assert.equal( + await client.hSetNX('key', 'field', 'value'), + 1 + ); + }, { + client: GLOBAL.SERVERS.OPEN, + cluster: GLOBAL.CLUSTERS.OPEN + }); }); diff --git a/packages/client/lib/commands/HSETNX.ts b/packages/client/lib/commands/HSETNX.ts index 9ac6ef0edd8..d26c42a76b4 100644 --- a/packages/client/lib/commands/HSETNX.ts +++ b/packages/client/lib/commands/HSETNX.ts @@ -1,13 +1,14 @@ -import { RedisCommandArgument, RedisCommandArguments } from '.'; +import { RedisArgument, Command, NumberReply } from '../RESP/types'; -export const FIRST_KEY_INDEX = 1; - -export function transformArguments( - key: RedisCommandArgument, - field: RedisCommandArgument, - value: RedisCommandArgument -): RedisCommandArguments { +export default { + FIRST_KEY_INDEX: 1, + IS_READ_ONLY: true, + transformArguments( + key: RedisArgument, + field: RedisArgument, + value: RedisArgument + ) { return ['HSETNX', key, field, value]; -} - -export { transformBooleanReply as transformReply } from './generic-transformers'; + }, + transformReply: undefined as unknown as () => NumberReply<0 | 1> +} as const satisfies Command; diff --git a/packages/client/lib/commands/HSTRLEN.spec.ts b/packages/client/lib/commands/HSTRLEN.spec.ts index 79c3150211e..59b737b692b 100644 --- a/packages/client/lib/commands/HSTRLEN.spec.ts +++ b/packages/client/lib/commands/HSTRLEN.spec.ts @@ -1,19 +1,22 @@ -import { strict as assert } from 'assert'; +import { strict as assert } from 'node:assert'; import testUtils, { GLOBAL } from '../test-utils'; -import { transformArguments } from './HSTRLEN'; +import HSTRLEN from './HSTRLEN'; describe('HSTRLEN', () => { - it('transformArguments', () => { - assert.deepEqual( - transformArguments('key', 'field'), - ['HSTRLEN', 'key', 'field'] - ); - }); + it('transformArguments', () => { + assert.deepEqual( + HSTRLEN.transformArguments('key', 'field'), + ['HSTRLEN', 'key', 'field'] + ); + }); - testUtils.testWithClient('client.hStrLen', async client => { - assert.equal( - await client.hStrLen('key', 'field'), - 0 - ); - }, GLOBAL.SERVERS.OPEN); + testUtils.testAll('hStrLen', async client => { + assert.equal( + await client.hStrLen('key', 'field'), + 0 + ); + }, { + client: GLOBAL.SERVERS.OPEN, + cluster: GLOBAL.CLUSTERS.OPEN + }); }); diff --git a/packages/client/lib/commands/HSTRLEN.ts b/packages/client/lib/commands/HSTRLEN.ts index a820e6c5643..843c4baf7ef 100644 --- a/packages/client/lib/commands/HSTRLEN.ts +++ b/packages/client/lib/commands/HSTRLEN.ts @@ -1,12 +1,10 @@ -import { RedisCommandArgument, RedisCommandArguments } from '.'; +import { RedisArgument, ArrayReply, BlobStringReply, Command } from '../RESP/types'; -export const FIRST_KEY_INDEX = 1; - -export function transformArguments( - key: RedisCommandArgument, - field: RedisCommandArgument -): RedisCommandArguments { +export default { + FIRST_KEY_INDEX: 1, + IS_READ_ONLY: true, + transformArguments(key: RedisArgument, field: RedisArgument) { return ['HSTRLEN', key, field]; -} - -export declare function transformReply(): number; + }, + transformReply: undefined as unknown as () => ArrayReply +} as const satisfies Command; diff --git a/packages/client/lib/commands/HTTL.spec.ts b/packages/client/lib/commands/HTTL.spec.ts index 21b8b329a5d..df74c8a728e 100644 --- a/packages/client/lib/commands/HTTL.spec.ts +++ b/packages/client/lib/commands/HTTL.spec.ts @@ -1,6 +1,6 @@ import { strict as assert } from 'node:assert'; import testUtils, { GLOBAL } from '../test-utils'; -import { transformArguments } from './HTTL'; +import HTTL from './HTTL'; import { HASH_EXPIRATION_TIME } from './HEXPIRETIME'; describe('HTTL', () => { @@ -9,14 +9,14 @@ describe('HTTL', () => { describe('transformArguments', () => { it('string', () => { assert.deepEqual( - transformArguments('key', 'field'), + HTTL.transformArguments('key', 'field'), ['HTTL', 'key', 'FIELDS', '1', 'field'] ); }); it('array', () => { assert.deepEqual( - transformArguments('key', ['field1', 'field2']), + HTTL.transformArguments('key', ['field1', 'field2']), ['HTTL', 'key', 'FIELDS', '2', 'field1', 'field2'] ); }); diff --git a/packages/client/lib/commands/HTTL.ts b/packages/client/lib/commands/HTTL.ts index d3eedd0db0e..66b50ff3e68 100644 --- a/packages/client/lib/commands/HTTL.ts +++ b/packages/client/lib/commands/HTTL.ts @@ -1,11 +1,11 @@ -import { RedisCommandArgument } from '.'; -import { pushVerdictArgument } from './generic-transformers'; +import { ArrayReply, Command, NullReply, NumberReply, RedisArgument } from '../RESP/types'; +import { pushVariadicArgument, RedisVariadicArgument } from './generic-transformers'; -export const FIRST_KEY_INDEX = 1; -export const IS_READ_ONLY = true; - -export function transformArguments(key: RedisCommandArgument, fields: RedisCommandArgument | Array) { - return pushVerdictArgument(['HTTL', key, 'FIELDS'], fields); -} - -export declare function transformReply(): Array | null; +export default { + FIRST_KEY_INDEX: 1, + IS_READ_ONLY: true, + transformArguments(key: RedisArgument, fields: RedisVariadicArgument) { + return pushVariadicArgument(['HTTL', key, 'FIELDS'], fields); + }, + transformReply: undefined as unknown as () => ArrayReply | NullReply +} as const satisfies Command; diff --git a/packages/client/lib/commands/HVALS.spec.ts b/packages/client/lib/commands/HVALS.spec.ts index d0a6c39ce5f..922aa588137 100644 --- a/packages/client/lib/commands/HVALS.spec.ts +++ b/packages/client/lib/commands/HVALS.spec.ts @@ -1,19 +1,22 @@ -import { strict as assert } from 'assert'; +import { strict as assert } from 'node:assert'; import testUtils, { GLOBAL } from '../test-utils'; -import { transformArguments } from './HVALS'; +import HVALS from './HVALS'; describe('HVALS', () => { - it('transformArguments', () => { - assert.deepEqual( - transformArguments('key'), - ['HVALS', 'key'] - ); - }); + it('transformArguments', () => { + assert.deepEqual( + HVALS.transformArguments('key'), + ['HVALS', 'key'] + ); + }); - testUtils.testWithClient('client.hVals', async client => { - assert.deepEqual( - await client.hVals('key'), - [] - ); - }, GLOBAL.SERVERS.OPEN); + testUtils.testAll('hVals', async client => { + assert.deepEqual( + await client.hVals('key'), + [] + ); + }, { + client: GLOBAL.SERVERS.OPEN, + cluster: GLOBAL.CLUSTERS.OPEN + }); }); diff --git a/packages/client/lib/commands/HVALS.ts b/packages/client/lib/commands/HVALS.ts index ef63fdc7f8a..ee4193f5cec 100644 --- a/packages/client/lib/commands/HVALS.ts +++ b/packages/client/lib/commands/HVALS.ts @@ -1,9 +1,10 @@ -import { RedisCommandArgument, RedisCommandArguments } from '.'; +import { RedisArgument, ArrayReply, BlobStringReply, Command } from '../RESP/types'; -export const FIRST_KEY_INDEX = 1; - -export function transformArguments(key: RedisCommandArgument): RedisCommandArguments { +export default { + FIRST_KEY_INDEX: 1, + IS_READ_ONLY: true, + transformArguments(key: RedisArgument) { return ['HVALS', key]; -} - -export declare function transformReply(): Array; + }, + transformReply: undefined as unknown as () => ArrayReply +} as const satisfies Command; diff --git a/packages/client/lib/commands/INCR.spec.ts b/packages/client/lib/commands/INCR.spec.ts index 321d83edc54..67129760245 100644 --- a/packages/client/lib/commands/INCR.spec.ts +++ b/packages/client/lib/commands/INCR.spec.ts @@ -1,19 +1,22 @@ -import { strict as assert } from 'assert'; +import { strict as assert } from 'node:assert'; import testUtils, { GLOBAL } from '../test-utils'; -import { transformArguments } from './INCR'; +import INCR from './INCR'; describe('INCR', () => { - it('transformArguments', () => { - assert.deepEqual( - transformArguments('key'), - ['INCR', 'key'] - ); - }); + it('transformArguments', () => { + assert.deepEqual( + INCR.transformArguments('key'), + ['INCR', 'key'] + ); + }); - testUtils.testWithClient('client.incr', async client => { - assert.equal( - await client.incr('key'), - 1 - ); - }, GLOBAL.SERVERS.OPEN); + testUtils.testAll('incr', async client => { + assert.equal( + await client.incr('key'), + 1 + ); + }, { + client: GLOBAL.SERVERS.OPEN, + cluster: GLOBAL.CLUSTERS.OPEN + }); }); diff --git a/packages/client/lib/commands/INCR.ts b/packages/client/lib/commands/INCR.ts index 2f9a9adfe2b..eb38256c9dc 100644 --- a/packages/client/lib/commands/INCR.ts +++ b/packages/client/lib/commands/INCR.ts @@ -1,9 +1,9 @@ -import { RedisCommandArgument, RedisCommandArguments } from '.'; +import { RedisArgument, NumberReply, Command } from '../RESP/types'; -export const FIRST_KEY_INDEX = 1; - -export function transformArguments(key: RedisCommandArgument): RedisCommandArguments { +export default { + FIRST_KEY_INDEX: 1, + transformArguments(key: RedisArgument) { return ['INCR', key]; -} - -export declare function transformReply(): number; + }, + transformReply: undefined as unknown as () => NumberReply +} as const satisfies Command; diff --git a/packages/client/lib/commands/INCRBY.spec.ts b/packages/client/lib/commands/INCRBY.spec.ts index a671d0ec259..d66c01acce5 100644 --- a/packages/client/lib/commands/INCRBY.spec.ts +++ b/packages/client/lib/commands/INCRBY.spec.ts @@ -1,19 +1,22 @@ -import { strict as assert } from 'assert'; +import { strict as assert } from 'node:assert'; import testUtils, { GLOBAL } from '../test-utils'; -import { transformArguments } from './INCRBY'; +import INCRBY from './INCRBY'; -describe('INCR', () => { - it('transformArguments', () => { - assert.deepEqual( - transformArguments('key', 1), - ['INCRBY', 'key', '1'] - ); - }); +describe('INCRBY', () => { + it('transformArguments', () => { + assert.deepEqual( + INCRBY.transformArguments('key', 1), + ['INCRBY', 'key', '1'] + ); + }); - testUtils.testWithClient('client.incrBy', async client => { - assert.equal( - await client.incrBy('key', 1), - 1 - ); - }, GLOBAL.SERVERS.OPEN); + testUtils.testAll('incrBy', async client => { + assert.equal( + await client.incrBy('key', 1), + 1 + ); + }, { + client: GLOBAL.SERVERS.OPEN, + cluster: GLOBAL.CLUSTERS.OPEN + }); }); diff --git a/packages/client/lib/commands/INCRBY.ts b/packages/client/lib/commands/INCRBY.ts index 75c61156d6b..5e94348fe63 100644 --- a/packages/client/lib/commands/INCRBY.ts +++ b/packages/client/lib/commands/INCRBY.ts @@ -1,12 +1,9 @@ -import { RedisCommandArgument, RedisCommandArguments } from '.'; +import { RedisArgument, NumberReply, Command } from '../RESP/types'; -export const FIRST_KEY_INDEX = 1; - -export function transformArguments( - key: RedisCommandArgument, - increment: number -): RedisCommandArguments { +export default { + FIRST_KEY_INDEX: 1, + transformArguments(key: RedisArgument, increment: number) { return ['INCRBY', key, increment.toString()]; -} - -export declare function transformReply(): number; + }, + transformReply: undefined as unknown as () => NumberReply +} as const satisfies Command; diff --git a/packages/client/lib/commands/INCRBYFLOAT.spec.ts b/packages/client/lib/commands/INCRBYFLOAT.spec.ts index b2dd5aa5da9..8bdd9c332de 100644 --- a/packages/client/lib/commands/INCRBYFLOAT.spec.ts +++ b/packages/client/lib/commands/INCRBYFLOAT.spec.ts @@ -1,19 +1,22 @@ -import { strict as assert } from 'assert'; +import { strict as assert } from 'node:assert'; import testUtils, { GLOBAL } from '../test-utils'; -import { transformArguments } from './INCRBYFLOAT'; +import INCRBYFLOAT from './INCRBYFLOAT'; describe('INCRBYFLOAT', () => { - it('transformArguments', () => { - assert.deepEqual( - transformArguments('key', 1.5), - ['INCRBYFLOAT', 'key', '1.5'] - ); - }); + it('transformArguments', () => { + assert.deepEqual( + INCRBYFLOAT.transformArguments('key', 1.5), + ['INCRBYFLOAT', 'key', '1.5'] + ); + }); - testUtils.testWithClient('client.incrByFloat', async client => { - assert.equal( - await client.incrByFloat('key', 1.5), - '1.5' - ); - }, GLOBAL.SERVERS.OPEN); + testUtils.testAll('incrByFloat', async client => { + assert.equal( + await client.incrByFloat('key', 1.5), + '1.5' + ); + }, { + client: GLOBAL.SERVERS.OPEN, + cluster: GLOBAL.CLUSTERS.OPEN + }); }); diff --git a/packages/client/lib/commands/INCRBYFLOAT.ts b/packages/client/lib/commands/INCRBYFLOAT.ts index ace3702339e..16f59e68c17 100644 --- a/packages/client/lib/commands/INCRBYFLOAT.ts +++ b/packages/client/lib/commands/INCRBYFLOAT.ts @@ -1,12 +1,9 @@ -import { RedisCommandArgument, RedisCommandArguments } from '.'; +import { RedisArgument, BlobStringReply, Command } from '../RESP/types'; -export const FIRST_KEY_INDEX = 1; - -export function transformArguments( - key: RedisCommandArgument, - increment: number -): RedisCommandArguments { +export default { + FIRST_KEY_INDEX: 1, + transformArguments(key: RedisArgument, increment: number) { return ['INCRBYFLOAT', key, increment.toString()]; -} - -export declare function transformReply(): RedisCommandArgument; + }, + transformReply: undefined as unknown as () => BlobStringReply +} as const satisfies Command; diff --git a/packages/client/lib/commands/INFO.spec.ts b/packages/client/lib/commands/INFO.spec.ts index 118682c7da1..c4555778729 100644 --- a/packages/client/lib/commands/INFO.spec.ts +++ b/packages/client/lib/commands/INFO.spec.ts @@ -1,20 +1,28 @@ -import { strict as assert } from 'assert'; -import { transformArguments } from './INFO'; +import { strict as assert } from 'node:assert'; +import testUtils, { GLOBAL } from '../test-utils'; +import INFO from './INFO'; describe('INFO', () => { - describe('transformArguments', () => { - it('simple', () => { - assert.deepEqual( - transformArguments(), - ['INFO'] - ); - }); + describe('transformArguments', () => { + it('simple', () => { + assert.deepEqual( + INFO.transformArguments(), + ['INFO'] + ); + }); - it('server section', () => { - assert.deepEqual( - transformArguments('server'), - ['INFO', 'server'] - ); - }); + it('server section', () => { + assert.deepEqual( + INFO.transformArguments('server'), + ['INFO', 'server'] + ); }); + }); + + testUtils.testWithClient('client.info', async client => { + assert.equal( + typeof await client.info(), + 'string' + ); + }, GLOBAL.SERVERS.OPEN); }); diff --git a/packages/client/lib/commands/INFO.ts b/packages/client/lib/commands/INFO.ts index 8ab24221b26..9877c0cf66b 100644 --- a/packages/client/lib/commands/INFO.ts +++ b/packages/client/lib/commands/INFO.ts @@ -1,13 +1,16 @@ -export const IS_READ_ONLY = true; +import { RedisArgument, VerbatimStringReply, Command } from '../RESP/types'; -export function transformArguments(section?: string): Array { - const args = ['INFO']; +export default { + FIRST_KEY_INDEX: undefined, + IS_READ_ONLY: true, + transformArguments(section?: RedisArgument) { + const args: Array = ['INFO']; if (section) { - args.push(section); + args.push(section); } return args; -} - -export declare function transformReply(): string; + }, + transformReply: undefined as unknown as () => VerbatimStringReply +} as const satisfies Command; diff --git a/packages/client/lib/commands/KEYS.spec.ts b/packages/client/lib/commands/KEYS.spec.ts index c066331ea7c..8100559a7e9 100644 --- a/packages/client/lib/commands/KEYS.spec.ts +++ b/packages/client/lib/commands/KEYS.spec.ts @@ -1,11 +1,11 @@ -import { strict as assert } from 'assert'; +import { strict as assert } from 'node:assert'; import testUtils, { GLOBAL } from '../test-utils'; describe('KEYS', () => { - testUtils.testWithClient('client.keys', async client => { - assert.deepEqual( - await client.keys('pattern'), - [] - ); - }, GLOBAL.SERVERS.OPEN); + testUtils.testWithClient('keys', async client => { + assert.deepEqual( + await client.keys('pattern'), + [] + ); + }, GLOBAL.SERVERS.OPEN); }); diff --git a/packages/client/lib/commands/KEYS.ts b/packages/client/lib/commands/KEYS.ts index c96ee001436..488ba1154c9 100644 --- a/packages/client/lib/commands/KEYS.ts +++ b/packages/client/lib/commands/KEYS.ts @@ -1,7 +1,10 @@ -import { RedisCommandArgument, RedisCommandArguments } from '.'; +import { RedisArgument, ArrayReply, BlobStringReply, Command } from '../RESP/types'; -export function transformArguments(pattern: RedisCommandArgument): RedisCommandArguments { +export default { + FIRST_KEY_INDEX: undefined, + IS_READ_ONLY: true, + transformArguments(pattern: RedisArgument) { return ['KEYS', pattern]; -} - -export declare function transformReply(): Array; + }, + transformReply: undefined as unknown as () => ArrayReply +} as const satisfies Command; diff --git a/packages/client/lib/commands/LASTSAVE.spec.ts b/packages/client/lib/commands/LASTSAVE.spec.ts index a6b4863f39e..74cf30705fa 100644 --- a/packages/client/lib/commands/LASTSAVE.spec.ts +++ b/packages/client/lib/commands/LASTSAVE.spec.ts @@ -1,16 +1,19 @@ -import { strict as assert } from 'assert'; +import { strict as assert } from 'node:assert'; import testUtils, { GLOBAL } from '../test-utils'; -import { transformArguments } from './LASTSAVE'; +import LASTSAVE from './LASTSAVE'; describe('LASTSAVE', () => { - it('transformArguments', () => { - assert.deepEqual( - transformArguments(), - ['LASTSAVE'] - ); - }); + it('transformArguments', () => { + assert.deepEqual( + LASTSAVE.transformArguments(), + ['LASTSAVE'] + ); + }); - testUtils.testWithClient('client.lastSave', async client => { - assert.ok((await client.lastSave()) instanceof Date); - }, GLOBAL.SERVERS.OPEN); + testUtils.testWithClient('client.lastSave', async client => { + assert.equal( + typeof await client.lastSave(), + 'number' + ); + }, GLOBAL.SERVERS.OPEN); }); diff --git a/packages/client/lib/commands/LASTSAVE.ts b/packages/client/lib/commands/LASTSAVE.ts index 76944d3548b..a65161f78b9 100644 --- a/packages/client/lib/commands/LASTSAVE.ts +++ b/packages/client/lib/commands/LASTSAVE.ts @@ -1,9 +1,10 @@ -export const IS_READ_ONLY = true; +import { NumberReply, Command } from '../RESP/types'; -export function transformArguments(): Array { +export default { + FIRST_KEY_INDEX: undefined, + IS_READ_ONLY: true, + transformArguments() { return ['LASTSAVE']; -} - -export function transformReply(reply: number): Date { - return new Date(reply); -} + }, + transformReply: undefined as unknown as () => NumberReply +} as const satisfies Command; diff --git a/packages/client/lib/commands/LATENCY_DOCTOR.spec.ts b/packages/client/lib/commands/LATENCY_DOCTOR.spec.ts index 3888ff8bd36..00eabfb4cb3 100644 --- a/packages/client/lib/commands/LATENCY_DOCTOR.spec.ts +++ b/packages/client/lib/commands/LATENCY_DOCTOR.spec.ts @@ -1,19 +1,19 @@ -import {strict as assert} from 'assert'; -import testUtils, {GLOBAL} from '../test-utils'; -import { transformArguments } from './LATENCY_DOCTOR'; +import { strict as assert } from 'node:assert'; +import testUtils, { GLOBAL } from '../test-utils'; +import LATENCY_DOCTOR from './LATENCY_DOCTOR'; describe('LATENCY DOCTOR', () => { - it('transformArguments', () => { - assert.deepEqual( - transformArguments(), - ['LATENCY', 'DOCTOR'] - ); - }); + it('transformArguments', () => { + assert.deepEqual( + LATENCY_DOCTOR.transformArguments(), + ['LATENCY', 'DOCTOR'] + ); + }); - testUtils.testWithClient('client.latencyDoctor', async client => { - assert.equal( - typeof (await client.latencyDoctor()), - 'string' - ); - }, GLOBAL.SERVERS.OPEN); + testUtils.testWithClient('client.latencyDoctor', async client => { + assert.equal( + typeof await client.latencyDoctor(), + 'string' + ); + }, GLOBAL.SERVERS.OPEN); }); diff --git a/packages/client/lib/commands/LATENCY_DOCTOR.ts b/packages/client/lib/commands/LATENCY_DOCTOR.ts index d2106c06114..96dc6b65702 100644 --- a/packages/client/lib/commands/LATENCY_DOCTOR.ts +++ b/packages/client/lib/commands/LATENCY_DOCTOR.ts @@ -1,5 +1,10 @@ -export function transformArguments(): Array { - return ['LATENCY', 'DOCTOR']; -} +import { BlobStringReply, Command } from '../RESP/types'; -export declare function transformReply(): string; +export default { + FIRST_KEY_INDEX: undefined, + IS_READ_ONLY: true, + transformArguments() { + return ['LATENCY', 'DOCTOR']; + }, + transformReply: undefined as unknown as () => BlobStringReply +} as const satisfies Command; diff --git a/packages/client/lib/commands/LATENCY_GRAPH.spec.ts b/packages/client/lib/commands/LATENCY_GRAPH.spec.ts index 21755a253b3..03ad8e2c886 100644 --- a/packages/client/lib/commands/LATENCY_GRAPH.spec.ts +++ b/packages/client/lib/commands/LATENCY_GRAPH.spec.ts @@ -1,28 +1,26 @@ -import { strict as assert } from 'assert'; +import { strict as assert } from 'node:assert'; import testUtils, { GLOBAL } from '../test-utils'; -import { transformArguments } from './LATENCY_GRAPH'; +import LATENCY_GRAPH from './LATENCY_GRAPH'; describe('LATENCY GRAPH', () => { - it('transformArguments', () => { - assert.deepEqual( - transformArguments('command'), - [ - 'LATENCY', - 'GRAPH', - 'command' - ] - ); - }); + it('transformArguments', () => { + assert.deepEqual( + LATENCY_GRAPH.transformArguments('command'), + [ + 'LATENCY', + 'GRAPH', + 'command' + ] + ); + }); - testUtils.testWithClient('client.latencyGraph', async client => { - await Promise.all([ - client.configSet('latency-monitor-threshold', '1'), - client.sendCommand(['DEBUG', 'SLEEP', '0.001']) - ]); + testUtils.testWithClient('client.latencyGraph', async client => { + const [,, reply] = await Promise.all([ + client.configSet('latency-monitor-threshold', '1'), + client.sendCommand(['DEBUG', 'SLEEP', '0.001']), + client.latencyGraph('command') + ]); - assert.equal( - typeof await client.latencyGraph('command'), - 'string' - ); - }, GLOBAL.SERVERS.OPEN); + assert.equal(typeof reply, 'string'); + }, GLOBAL.SERVERS.OPEN); }); diff --git a/packages/client/lib/commands/LATENCY_GRAPH.ts b/packages/client/lib/commands/LATENCY_GRAPH.ts index e4e078b90f2..7d5f54288b6 100644 --- a/packages/client/lib/commands/LATENCY_GRAPH.ts +++ b/packages/client/lib/commands/LATENCY_GRAPH.ts @@ -1,25 +1,31 @@ -import { RedisCommandArguments } from '.'; +import { BlobStringReply, Command } from '../RESP/types'; -export type EventType = - 'active-defrag-cycle' - | 'aof-fsync-always' - | 'aof-stat' - | 'aof-rewrite-diff-write' - | 'aof-rename' - | 'aof-write' - | 'aof-write-active-child' - | 'aof-write-alone' - | 'aof-write-pending-fsync' - | 'command' - | 'expire-cycle' - | 'eviction-cycle' - | 'eviction-del' - | 'fast-command' - | 'fork' - | 'rdb-unlink-temp-file'; +export const LATENCY_EVENTS = { + ACTIVE_DEFRAG_CYCLE: 'active-defrag-cycle', + AOF_FSYNC_ALWAYS: 'aof-fsync-always', + AOF_STAT: 'aof-stat', + AOF_REWRITE_DIFF_WRITE: 'aof-rewrite-diff-write', + AOF_RENAME: 'aof-rename', + AOF_WRITE: 'aof-write', + AOF_WRITE_ACTIVE_CHILD: 'aof-write-active-child', + AOF_WRITE_ALONE: 'aof-write-alone', + AOF_WRITE_PENDING_FSYNC: 'aof-write-pending-fsync', + COMMAND: 'command', + EXPIRE_CYCLE: 'expire-cycle', + EVICTION_CYCLE: 'eviction-cycle', + EVICTION_DEL: 'eviction-del', + FAST_COMMAND: 'fast-command', + FORK: 'fork', + RDB_UNLINK_TEMP_FILE: 'rdb-unlink-temp-file' +} as const; -export function transformArguments(event: EventType): RedisCommandArguments { - return ['LATENCY', 'GRAPH', event]; -} +export type LatencyEvent = typeof LATENCY_EVENTS[keyof typeof LATENCY_EVENTS]; -export declare function transformReply(): string; +export default { + FIRST_KEY_INDEX: undefined, + IS_READ_ONLY: true, + transformArguments(event: LatencyEvent) { + return ['LATENCY', 'GRAPH', event]; + }, + transformReply: undefined as unknown as () => BlobStringReply +} as const satisfies Command; diff --git a/packages/client/lib/commands/LATENCY_HISTORY.spec.ts b/packages/client/lib/commands/LATENCY_HISTORY.spec.ts index e79e969b261..509c856e28f 100644 --- a/packages/client/lib/commands/LATENCY_HISTORY.spec.ts +++ b/packages/client/lib/commands/LATENCY_HISTORY.spec.ts @@ -1,26 +1,26 @@ -import {strict as assert} from 'assert'; -import testUtils, {GLOBAL} from '../test-utils'; -import { transformArguments } from './LATENCY_HISTORY'; +import { strict as assert } from 'assert'; +import testUtils, { GLOBAL } from '../test-utils'; +import LATENCY_HISTORY from './LATENCY_HISTORY'; describe('LATENCY HISTORY', () => { - it('transformArguments', () => { - assert.deepEqual( - transformArguments('command'), - ['LATENCY', 'HISTORY', 'command'] - ); - }); + it('transformArguments', () => { + assert.deepEqual( + LATENCY_HISTORY.transformArguments('command'), + ['LATENCY', 'HISTORY', 'command'] + ); + }); - testUtils.testWithClient('client.latencyHistory', async client => { - await Promise.all([ - client.configSet('latency-monitor-threshold', '100'), - client.sendCommand(['DEBUG', 'SLEEP', '1']) - ]); - - const latencyHisRes = await client.latencyHistory('command'); - assert.ok(Array.isArray(latencyHisRes)); - for (const [timestamp, latency] of latencyHisRes) { - assert.equal(typeof timestamp, 'number'); - assert.equal(typeof latency, 'number'); - } - }, GLOBAL.SERVERS.OPEN); + testUtils.testWithClient('client.latencyHistory', async client => { + const [,, reply] = await Promise.all([ + client.configSet('latency-monitor-threshold', '100'), + client.sendCommand(['DEBUG', 'SLEEP', '1']), + client.latencyHistory('command') + ]); + + assert.ok(Array.isArray(reply)); + for (const [timestamp, latency] of reply) { + assert.equal(typeof timestamp, 'number'); + assert.equal(typeof latency, 'number'); + } + }, GLOBAL.SERVERS.OPEN); }); diff --git a/packages/client/lib/commands/LATENCY_HISTORY.ts b/packages/client/lib/commands/LATENCY_HISTORY.ts index c0b1964553e..df5b1772b2d 100644 --- a/packages/client/lib/commands/LATENCY_HISTORY.ts +++ b/packages/client/lib/commands/LATENCY_HISTORY.ts @@ -1,27 +1,33 @@ -export type EventType = ( - 'active-defrag-cycle' | - 'aof-fsync-always' | - 'aof-stat' | - 'aof-rewrite-diff-write' | - 'aof-rename' | - 'aof-write' | - 'aof-write-active-child' | - 'aof-write-alone' | - 'aof-write-pending-fsync' | - 'command' | - 'expire-cycle' | - 'eviction-cycle' | - 'eviction-del' | - 'fast-command' | - 'fork' | - 'rdb-unlink-temp-file' +import { ArrayReply, TuplesReply, NumberReply, Command } from '../RESP/types'; + +export type LatencyEventType = ( + 'active-defrag-cycle' | + 'aof-fsync-always' | + 'aof-stat' | + 'aof-rewrite-diff-write' | + 'aof-rename' | + 'aof-write' | + 'aof-write-active-child' | + 'aof-write-alone' | + 'aof-write-pending-fsync' | + 'command' | + 'expire-cycle' | + 'eviction-cycle' | + 'eviction-del' | + 'fast-command' | + 'fork' | + 'rdb-unlink-temp-file' ); -export function transformArguments(event: EventType) { +export default { + FIRST_KEY_INDEX: undefined, + IS_READ_ONLY: true, + transformArguments(event: LatencyEventType) { return ['LATENCY', 'HISTORY', event]; -} + }, + transformReply: undefined as unknown as () => ArrayReply> +} as const satisfies Command; -export declare function transformReply(): Array<[ - timestamp: number, - latency: number, -]>; diff --git a/packages/client/lib/commands/LATENCY_LATEST.spec.ts b/packages/client/lib/commands/LATENCY_LATEST.spec.ts index 4087f212139..f85a3ccc8bd 100644 --- a/packages/client/lib/commands/LATENCY_LATEST.spec.ts +++ b/packages/client/lib/commands/LATENCY_LATEST.spec.ts @@ -1,27 +1,27 @@ -import {strict as assert} from 'assert'; -import testUtils, {GLOBAL} from '../test-utils'; -import { transformArguments } from './LATENCY_LATEST'; +import { strict as assert } from 'node:assert'; +import testUtils, { GLOBAL } from '../test-utils'; +import LATENCY_LATEST from './LATENCY_LATEST'; describe('LATENCY LATEST', () => { - it('transformArguments', () => { - assert.deepEqual( - transformArguments(), - ['LATENCY', 'LATEST'] - ); - }); + it('transformArguments', () => { + assert.deepEqual( + LATENCY_LATEST.transformArguments(), + ['LATENCY', 'LATEST'] + ); + }); - testUtils.testWithClient('client.latencyLatest', async client => { - await Promise.all([ - client.configSet('latency-monitor-threshold', '100'), - client.sendCommand(['DEBUG', 'SLEEP', '1']) - ]); - const latency = await client.latencyLatest(); - assert.ok(Array.isArray(latency)); - for (const [name, timestamp, latestLatency, allTimeLatency] of latency) { - assert.equal(typeof name, 'string'); - assert.equal(typeof timestamp, 'number'); - assert.equal(typeof latestLatency, 'number'); - assert.equal(typeof allTimeLatency, 'number'); - } - }, GLOBAL.SERVERS.OPEN); + testUtils.testWithClient('client.latencyLatest', async client => { + const [,, reply] = await Promise.all([ + client.configSet('latency-monitor-threshold', '100'), + client.sendCommand(['DEBUG', 'SLEEP', '1']), + client.latencyLatest() + ]); + assert.ok(Array.isArray(reply)); + for (const [name, timestamp, latestLatency, allTimeLatency] of reply) { + assert.equal(typeof name, 'string'); + assert.equal(typeof timestamp, 'number'); + assert.equal(typeof latestLatency, 'number'); + assert.equal(typeof allTimeLatency, 'number'); + } + }, GLOBAL.SERVERS.OPEN); }); diff --git a/packages/client/lib/commands/LATENCY_LATEST.ts b/packages/client/lib/commands/LATENCY_LATEST.ts index 3e4dd6236c6..29548af30dc 100644 --- a/packages/client/lib/commands/LATENCY_LATEST.ts +++ b/packages/client/lib/commands/LATENCY_LATEST.ts @@ -1,12 +1,16 @@ -import { RedisCommandArguments } from '.'; +import { ArrayReply, BlobStringReply, NumberReply, Command } from '../RESP/types'; -export function transformArguments(): RedisCommandArguments { +export default { + FIRST_KEY_INDEX: undefined, + IS_READ_ONLY: true, + transformArguments() { return ['LATENCY', 'LATEST']; -} + }, + transformReply: undefined as unknown as () => ArrayReply<[ + name: BlobStringReply, + timestamp: NumberReply, + latestLatency: NumberReply, + allTimeLatency: NumberReply + ]> +} as const satisfies Command; -export declare function transformReply(): Array<[ - name: string, - timestamp: number, - latestLatency: number, - allTimeLatency: number -]>; diff --git a/packages/client/lib/commands/LCS.spec.ts b/packages/client/lib/commands/LCS.spec.ts index a4d9035571e..ff9d63db1b1 100644 --- a/packages/client/lib/commands/LCS.spec.ts +++ b/packages/client/lib/commands/LCS.spec.ts @@ -1,28 +1,24 @@ -import { strict as assert } from 'assert'; +import { strict as assert } from 'node:assert'; import testUtils, { GLOBAL } from '../test-utils'; -import { transformArguments } from './LCS'; +import LCS from './LCS'; describe('LCS', () => { - testUtils.isVersionGreaterThanHook([7]); + testUtils.isVersionGreaterThanHook([7]); - it('transformArguments', () => { - assert.deepEqual( - transformArguments('1', '2'), - ['LCS', '1', '2'] - ); - }); + it('transformArguments', () => { + assert.deepEqual( + LCS.transformArguments('1', '2'), + ['LCS', '1', '2'] + ); + }); - testUtils.testWithClient('client.lcs', async client => { - assert.equal( - await client.lcs('1', '2'), - '' - ); - }, GLOBAL.SERVERS.OPEN); - - testUtils.testWithCluster('cluster.lcs', async cluster => { - assert.equal( - await cluster.lcs('{tag}1', '{tag}2'), - '' - ); - }, GLOBAL.CLUSTERS.OPEN); + testUtils.testAll('lcs', async client => { + assert.equal( + await client.lcs('{tag}1', '{tag}2'), + '' + ); + }, { + client: GLOBAL.SERVERS.OPEN, + cluster: GLOBAL.CLUSTERS.OPEN + }); }); diff --git a/packages/client/lib/commands/LCS.ts b/packages/client/lib/commands/LCS.ts index b075b73e8a8..b798f12aa37 100644 --- a/packages/client/lib/commands/LCS.ts +++ b/packages/client/lib/commands/LCS.ts @@ -1,18 +1,13 @@ -import { RedisCommandArgument, RedisCommandArguments } from '.'; - -export const FIRST_KEY_INDEX = 1; - -export const IS_READ_ONLY = true; - -export function transformArguments( - key1: RedisCommandArgument, - key2: RedisCommandArgument -): RedisCommandArguments { - return [ - 'LCS', - key1, - key2 - ]; -} - -export declare function transformReply(): string | Buffer; +import { RedisArgument, BlobStringReply, Command } from '../RESP/types'; + +export default { + FIRST_KEY_INDEX: 1, + IS_READ_ONLY: true, + transformArguments( + key1: RedisArgument, + key2: RedisArgument + ) { + return ['LCS', key1, key2]; + }, + transformReply: undefined as unknown as () => BlobStringReply +} as const satisfies Command; diff --git a/packages/client/lib/commands/LCS_IDX.spec.ts b/packages/client/lib/commands/LCS_IDX.spec.ts index fc3ee54f7c0..2f60a205faa 100644 --- a/packages/client/lib/commands/LCS_IDX.spec.ts +++ b/packages/client/lib/commands/LCS_IDX.spec.ts @@ -1,41 +1,34 @@ -import { strict as assert } from 'assert'; +import { strict as assert } from 'node:assert'; import testUtils, { GLOBAL } from '../test-utils'; -import { transformArguments } from './LCS_IDX'; +import LCS_IDX from './LCS_IDX'; -describe('LCS_IDX', () => { - testUtils.isVersionGreaterThanHook([7]); +describe('LCS IDX', () => { + testUtils.isVersionGreaterThanHook([7]); - it('transformArguments', () => { - assert.deepEqual( - transformArguments('1', '2'), - ['LCS', '1', '2', 'IDX'] - ); - }); + it('transformArguments', () => { + assert.deepEqual( + LCS_IDX.transformArguments('1', '2'), + ['LCS', '1', '2', 'IDX'] + ); + }); - testUtils.testWithClient('client.lcsIdx', async client => { - const [, reply] = await Promise.all([ - client.mSet({ - '1': 'abc', - '2': 'bc' - }), - client.lcsIdx('1', '2') - ]); + testUtils.testWithClient('client.lcsIdx', async client => { + const [, reply] = await Promise.all([ + client.mSet({ + '1': 'abc', + '2': 'bc' + }), + client.lcsIdx('1', '2') + ]); - assert.deepEqual( - reply, - { - matches: [{ - key1: { - start: 1, - end: 2 - }, - key2: { - start: 0, - end: 1 - } - }], - length: 2 - } - ); - }, GLOBAL.SERVERS.OPEN); + assert.deepEqual( + reply, + { + matches: [ + [[1, 2], [0, 1]] + ], + len: 2 + } + ); + }, GLOBAL.SERVERS.OPEN); }); diff --git a/packages/client/lib/commands/LCS_IDX.ts b/packages/client/lib/commands/LCS_IDX.ts index 262a02ba4c6..0c266fffe1c 100644 --- a/packages/client/lib/commands/LCS_IDX.ts +++ b/packages/client/lib/commands/LCS_IDX.ts @@ -1,42 +1,50 @@ -import { RedisCommandArgument, RedisCommandArguments } from '.'; -import { RangeReply, RawRangeReply, transformRangeReply } from './generic-transformers'; -import { transformArguments as transformLcsArguments } from './LCS'; +import { RedisArgument, TuplesToMapReply, BlobStringReply, ArrayReply, NumberReply, UnwrapReply, Resp2Reply, Command, TuplesReply } from '../RESP/types'; +import LCS from './LCS'; -export { FIRST_KEY_INDEX, IS_READ_ONLY } from './LCS'; +export interface LcsIdxOptions { + MINMATCHLEN?: number; +} + +export type LcsIdxRange = TuplesReply<[ + start: NumberReply, + end: NumberReply +]>; + +export type LcsIdxMatches = ArrayReply< + TuplesReply<[ + key1: LcsIdxRange, + key2: LcsIdxRange + ]> +>; + +export type LcsIdxReply = TuplesToMapReply<[ + [BlobStringReply<'matches'>, LcsIdxMatches], + [BlobStringReply<'len'>, NumberReply] +]>; + +export default { + FIRST_KEY_INDEX: LCS.FIRST_KEY_INDEX, + IS_READ_ONLY: LCS.IS_READ_ONLY, + transformArguments( + key1: RedisArgument, + key2: RedisArgument, + options?: LcsIdxOptions + ) { + const args = LCS.transformArguments(key1, key2); -export function transformArguments( - key1: RedisCommandArgument, - key2: RedisCommandArgument -): RedisCommandArguments { - const args = transformLcsArguments(key1, key2); args.push('IDX'); - return args; -} -type RawReply = [ - 'matches', - Array<[ - key1: RawRangeReply, - key2: RawRangeReply - ]>, - 'len', - number -]; - -interface Reply { - matches: Array<{ - key1: RangeReply; - key2: RangeReply; - }>; - length: number; -} + if (options?.MINMATCHLEN) { + args.push('MINMATCHLEN', options.MINMATCHLEN.toString()); + } -export function transformReply(reply: RawReply): Reply { - return { - matches: reply[1].map(([key1, key2]) => ({ - key1: transformRangeReply(key1), - key2: transformRangeReply(key2) - })), - length: reply[3] - }; -} + return args; + }, + transformReply: { + 2: (reply: UnwrapReply>) => ({ + matches: reply[1], + len: reply[3] + }), + 3: undefined as unknown as () => LcsIdxReply + } +} as const satisfies Command; diff --git a/packages/client/lib/commands/LCS_IDX_WITHMATCHLEN.spec.ts b/packages/client/lib/commands/LCS_IDX_WITHMATCHLEN.spec.ts index 8be9b993135..39ba17e8f2c 100644 --- a/packages/client/lib/commands/LCS_IDX_WITHMATCHLEN.spec.ts +++ b/packages/client/lib/commands/LCS_IDX_WITHMATCHLEN.spec.ts @@ -1,42 +1,34 @@ -import { strict as assert } from 'assert'; +import { strict as assert } from 'node:assert'; import testUtils, { GLOBAL } from '../test-utils'; -import { transformArguments } from './LCS_IDX_WITHMATCHLEN'; +import LCS_IDX_WITHMATCHLEN from './LCS_IDX_WITHMATCHLEN'; -describe('LCS_IDX_WITHMATCHLEN', () => { - testUtils.isVersionGreaterThanHook([7]); +describe('LCS IDX WITHMATCHLEN', () => { + testUtils.isVersionGreaterThanHook([7]); - it('transformArguments', () => { - assert.deepEqual( - transformArguments('1', '2'), - ['LCS', '1', '2', 'IDX', 'WITHMATCHLEN'] - ); - }); + it('transformArguments', () => { + assert.deepEqual( + LCS_IDX_WITHMATCHLEN.transformArguments('1', '2'), + ['LCS', '1', '2', 'IDX', 'WITHMATCHLEN'] + ); + }); - testUtils.testWithClient('client.lcsIdxWithMatchLen', async client => { - const [, reply] = await Promise.all([ - client.mSet({ - '1': 'abc', - '2': 'bc' - }), - client.lcsIdxWithMatchLen('1', '2') - ]); + testUtils.testWithClient('client.lcsIdxWithMatchLen', async client => { + const [, reply] = await Promise.all([ + client.mSet({ + '1': 'abc', + '2': 'bc' + }), + client.lcsIdxWithMatchLen('1', '2') + ]); - assert.deepEqual( - reply, - { - matches: [{ - key1: { - start: 1, - end: 2 - }, - key2: { - start: 0, - end: 1 - }, - length: 2 - }], - length: 2 - } - ); - }, GLOBAL.SERVERS.OPEN); + assert.deepEqual( + reply, + { + matches: [ + [[1, 2], [0, 1], 2] + ], + len: 2 + } + ); + }, GLOBAL.SERVERS.OPEN); }); diff --git a/packages/client/lib/commands/LCS_IDX_WITHMATCHLEN.ts b/packages/client/lib/commands/LCS_IDX_WITHMATCHLEN.ts index 989870d6ca2..4e645852035 100644 --- a/packages/client/lib/commands/LCS_IDX_WITHMATCHLEN.ts +++ b/packages/client/lib/commands/LCS_IDX_WITHMATCHLEN.ts @@ -1,45 +1,36 @@ -import { RedisCommandArgument, RedisCommandArguments } from '.'; -import { RangeReply, RawRangeReply, transformRangeReply } from './generic-transformers'; -import { transformArguments as transformLcsArguments } from './LCS'; +import { RedisArgument, TuplesToMapReply, BlobStringReply, ArrayReply, TuplesReply, NumberReply, UnwrapReply, Resp2Reply, Command } from '../RESP/types'; +import LCS_IDX, { LcsIdxOptions, LcsIdxRange } from './LCS_IDX'; -export { FIRST_KEY_INDEX, IS_READ_ONLY } from './LCS'; +export type LcsIdxWithMatchLenMatches = ArrayReply< + TuplesReply<[ + key1: LcsIdxRange, + key2: LcsIdxRange, + len: NumberReply + ]> +>; -export function transformArguments( - key1: RedisCommandArgument, - key2: RedisCommandArgument -): RedisCommandArguments { - const args = transformLcsArguments(key1, key2); - args.push('IDX', 'WITHMATCHLEN'); - return args; -} - -type RawReply = [ - 'matches', - Array<[ - key1: RawRangeReply, - key2: RawRangeReply, - length: number - ]>, - 'len', - number -]; +export type LcsIdxWithMatchLenReply = TuplesToMapReply<[ + [BlobStringReply<'matches'>, LcsIdxWithMatchLenMatches], + [BlobStringReply<'len'>, NumberReply] +]>; -interface Reply { - matches: Array<{ - key1: RangeReply; - key2: RangeReply; - length: number; - }>; - length: number; -} - -export function transformReply(reply: RawReply): Reply { - return { - matches: reply[1].map(([key1, key2, length]) => ({ - key1: transformRangeReply(key1), - key2: transformRangeReply(key2), - length - })), - length: reply[3] - }; -} +export default { + FIRST_KEY_INDEX: LCS_IDX.FIRST_KEY_INDEX, + IS_READ_ONLY: LCS_IDX.IS_READ_ONLY, + transformArguments( + key1: RedisArgument, + key2: RedisArgument, + options?: LcsIdxOptions + ) { + const args = LCS_IDX.transformArguments(key1, key2); + args.push('WITHMATCHLEN'); + return args; + }, + transformReply: { + 2: (reply: UnwrapReply>) => ({ + matches: reply[1], + len: reply[3] + }), + 3: undefined as unknown as () => LcsIdxWithMatchLenReply + } +} as const satisfies Command; diff --git a/packages/client/lib/commands/LCS_LEN.spec.ts b/packages/client/lib/commands/LCS_LEN.spec.ts index bf4eefd3301..9dc163a68a9 100644 --- a/packages/client/lib/commands/LCS_LEN.spec.ts +++ b/packages/client/lib/commands/LCS_LEN.spec.ts @@ -1,28 +1,24 @@ -import { strict as assert } from 'assert'; +import { strict as assert } from 'node:assert'; import testUtils, { GLOBAL } from '../test-utils'; -import { transformArguments } from './LCS_LEN'; +import LCS_LEN from './LCS_LEN'; describe('LCS_LEN', () => { - testUtils.isVersionGreaterThanHook([7]); + testUtils.isVersionGreaterThanHook([7]); - it('transformArguments', () => { - assert.deepEqual( - transformArguments('1', '2'), - ['LCS', '1', '2', 'LEN'] - ); - }); + it('transformArguments', () => { + assert.deepEqual( + LCS_LEN.transformArguments('1', '2'), + ['LCS', '1', '2', 'LEN'] + ); + }); - testUtils.testWithClient('client.lcsLen', async client => { - assert.equal( - await client.lcsLen('1', '2'), - 0 - ); - }, GLOBAL.SERVERS.OPEN); - - testUtils.testWithCluster('cluster.lcsLen', async cluster => { - assert.equal( - await cluster.lcsLen('{tag}1', '{tag}2'), - 0 - ); - }, GLOBAL.CLUSTERS.OPEN); + testUtils.testAll('lcsLen', async client => { + assert.equal( + await client.lcsLen('{tag}1', '{tag}2'), + 0 + ); + }, { + client: GLOBAL.SERVERS.OPEN, + cluster: GLOBAL.CLUSTERS.OPEN + }); }); diff --git a/packages/client/lib/commands/LCS_LEN.ts b/packages/client/lib/commands/LCS_LEN.ts index a5121e4c13f..d5d0e77e4d6 100644 --- a/packages/client/lib/commands/LCS_LEN.ts +++ b/packages/client/lib/commands/LCS_LEN.ts @@ -1,15 +1,16 @@ -import { RedisCommandArgument, RedisCommandArguments } from '.'; -import { transformArguments as transformLcsArguments } from './LCS'; +import { RedisArgument, NumberReply, Command } from '../RESP/types'; +import LCS from './LCS'; -export { FIRST_KEY_INDEX, IS_READ_ONLY } from './LCS'; - -export function transformArguments( - key1: RedisCommandArgument, - key2: RedisCommandArgument -): RedisCommandArguments { - const args = transformLcsArguments(key1, key2); +export default { + FIRST_KEY_INDEX: LCS.FIRST_KEY_INDEX, + IS_READ_ONLY: LCS.IS_READ_ONLY, + transformArguments( + key1: RedisArgument, + key2: RedisArgument + ) { + const args = LCS.transformArguments(key1, key2); args.push('LEN'); return args; -} - -export declare function transformReply(): number; + }, + transformReply: undefined as unknown as () => NumberReply +} as const satisfies Command; diff --git a/packages/client/lib/commands/LINDEX.spec.ts b/packages/client/lib/commands/LINDEX.spec.ts index aa3aafa789b..60346b85f21 100644 --- a/packages/client/lib/commands/LINDEX.spec.ts +++ b/packages/client/lib/commands/LINDEX.spec.ts @@ -1,36 +1,22 @@ -import { strict as assert } from 'assert'; +import { strict as assert } from 'node:assert'; import testUtils, { GLOBAL } from '../test-utils'; -import { transformArguments } from './LINDEX'; -describe('LINDEX', () => { - it('transformArguments', () => { - assert.deepEqual( - transformArguments('key', 0), - ['LINDEX', 'key', '0'] - ); - }); - - describe('client.lIndex', () => { - testUtils.testWithClient('null', async client => { - assert.equal( - await client.lIndex('key', 0), - null - ); - }, GLOBAL.SERVERS.OPEN); +import LINDEX from './LINDEX'; - 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); - }); +describe('LINDEX', () => { + it('transformArguments', () => { + assert.deepEqual( + LINDEX.transformArguments('key', 0), + ['LINDEX', 'key', '0'] + ); + }); - testUtils.testWithCluster('cluster.lIndex', async cluster => { - assert.equal( - await cluster.lIndex('key', 0), - null - ); - }, GLOBAL.CLUSTERS.OPEN); + testUtils.testAll('lIndex', async client => { + assert.equal( + await client.lIndex('key', 0), + null + ); + }, { + client: GLOBAL.SERVERS.OPEN, + cluster: GLOBAL.CLUSTERS.OPEN + }); }); \ No newline at end of file diff --git a/packages/client/lib/commands/LINDEX.ts b/packages/client/lib/commands/LINDEX.ts index 8e74ad8aae6..0478bf9dc41 100644 --- a/packages/client/lib/commands/LINDEX.ts +++ b/packages/client/lib/commands/LINDEX.ts @@ -1,14 +1,10 @@ -import { RedisCommandArgument, RedisCommandArguments } from '.'; +import { RedisArgument, BlobStringReply, NullReply, Command } from '../RESP/types'; -export const FIRST_KEY_INDEX = 1; - -export const IS_READ_ONLY = true; - -export function transformArguments( - key: RedisCommandArgument, - index: number -): RedisCommandArguments { +export default { + FIRST_KEY_INDEX: 1, + IS_READ_ONLY: true, + transformArguments(key: RedisArgument, index: number) { return ['LINDEX', key, index.toString()]; -} - -export declare function transformReply(): RedisCommandArgument | null; + }, + transformReply: undefined as unknown as () => BlobStringReply | NullReply +} as const satisfies Command; diff --git a/packages/client/lib/commands/LINSERT.spec.ts b/packages/client/lib/commands/LINSERT.spec.ts index 6454cc48536..6cafa3f5a84 100644 --- a/packages/client/lib/commands/LINSERT.spec.ts +++ b/packages/client/lib/commands/LINSERT.spec.ts @@ -1,26 +1,22 @@ -import { strict as assert } from 'assert'; +import { strict as assert } from 'node:assert'; import testUtils, { GLOBAL } from '../test-utils'; -import { transformArguments } from './LINSERT'; +import LINSERT from './LINSERT'; describe('LINSERT', () => { - it('transformArguments', () => { - assert.deepEqual( - transformArguments('key', 'BEFORE', 'pivot', 'element'), - ['LINSERT', 'key', 'BEFORE', 'pivot', 'element'] - ); - }); + it('transformArguments', () => { + assert.deepEqual( + LINSERT.transformArguments('key', 'BEFORE', 'pivot', 'element'), + ['LINSERT', 'key', 'BEFORE', 'pivot', 'element'] + ); + }); - testUtils.testWithClient('client.lInsert', async client => { - assert.equal( - await client.lInsert('key', 'BEFORE', 'pivot', 'element'), - 0 - ); - }, GLOBAL.SERVERS.OPEN); - - testUtils.testWithCluster('cluster.lInsert', async cluster => { - assert.equal( - await cluster.lInsert('key', 'BEFORE', 'pivot', 'element'), - 0 - ); - }, GLOBAL.CLUSTERS.OPEN); + testUtils.testAll('lInsert', async client => { + assert.equal( + await client.lInsert('key', 'BEFORE', 'pivot', 'element'), + 0 + ); + }, { + client: GLOBAL.SERVERS.OPEN, + cluster: GLOBAL.CLUSTERS.OPEN + }); }); diff --git a/packages/client/lib/commands/LINSERT.ts b/packages/client/lib/commands/LINSERT.ts index 0a8e1f32ba4..4bdc77de5a4 100644 --- a/packages/client/lib/commands/LINSERT.ts +++ b/packages/client/lib/commands/LINSERT.ts @@ -1,22 +1,23 @@ -import { RedisCommandArgument, RedisCommandArguments } from '.'; - -export const FIRST_KEY_INDEX = 1; +import { RedisArgument, NumberReply, Command } from '../RESP/types'; type LInsertPosition = 'BEFORE' | 'AFTER'; -export function transformArguments( - key: RedisCommandArgument, +export default { + FIRST_KEY_INDEX: 1, + IS_READ_ONLY: true, + transformArguments( + key: RedisArgument, position: LInsertPosition, - pivot: RedisCommandArgument, - element: RedisCommandArgument -): RedisCommandArguments { + pivot: RedisArgument, + element: RedisArgument + ) { return [ - 'LINSERT', - key, - position, - pivot, - element + 'LINSERT', + key, + position, + pivot, + element ]; -} - -export declare function transformReply(): number; + }, + transformReply: undefined as unknown as () => NumberReply +} as const satisfies Command; diff --git a/packages/client/lib/commands/LLEN.spec.ts b/packages/client/lib/commands/LLEN.spec.ts index fb126ddad55..f6ac9a73cc3 100644 --- a/packages/client/lib/commands/LLEN.spec.ts +++ b/packages/client/lib/commands/LLEN.spec.ts @@ -1,26 +1,22 @@ -import { strict as assert } from 'assert'; +import { strict as assert } from 'node:assert'; import testUtils, { GLOBAL } from '../test-utils'; -import { transformArguments } from './LLEN'; +import LLEN from './LLEN'; describe('LLEN', () => { - it('transformArguments', () => { - assert.deepEqual( - transformArguments('key'), - ['LLEN', 'key'] - ); - }); + it('transformArguments', () => { + assert.deepEqual( + LLEN.transformArguments('key'), + ['LLEN', 'key'] + ); + }); - testUtils.testWithClient('client.lLen', async client => { - assert.equal( - await client.lLen('key'), - 0 - ); - }, GLOBAL.SERVERS.OPEN); - - testUtils.testWithCluster('cluster.lLen', async cluster => { - assert.equal( - await cluster.lLen('key'), - 0 - ); - }, GLOBAL.CLUSTERS.OPEN); + testUtils.testAll('lLen', async client => { + assert.equal( + await client.lLen('key'), + 0 + ); + }, { + client: GLOBAL.SERVERS.OPEN, + cluster: GLOBAL.CLUSTERS.OPEN + }); }); diff --git a/packages/client/lib/commands/LLEN.ts b/packages/client/lib/commands/LLEN.ts index 3410e57d424..dda59ddf291 100644 --- a/packages/client/lib/commands/LLEN.ts +++ b/packages/client/lib/commands/LLEN.ts @@ -1,11 +1,10 @@ -import { RedisCommandArgument, RedisCommandArguments } from '.'; +import { RedisArgument, NumberReply, Command } from '../RESP/types'; -export const FIRST_KEY_INDEX = 1; - -export const IS_READ_ONLY = true; - -export function transformArguments(key: RedisCommandArgument): RedisCommandArguments { +export default { + FIRST_KEY_INDEX: 1, + IS_READ_ONLY: true, + transformArguments(key: RedisArgument) { return ['LLEN', key]; -} - -export declare function transformReply(): number; + }, + transformReply: undefined as unknown as () => NumberReply +} as const satisfies Command; diff --git a/packages/client/lib/commands/LMOVE.spec.ts b/packages/client/lib/commands/LMOVE.spec.ts index f1d418c394e..86740aa7b77 100644 --- a/packages/client/lib/commands/LMOVE.spec.ts +++ b/packages/client/lib/commands/LMOVE.spec.ts @@ -1,28 +1,24 @@ -import { strict as assert } from 'assert'; +import { strict as assert } from 'node:assert'; import testUtils, { GLOBAL } from '../test-utils'; -import { transformArguments } from './LMOVE'; +import LMOVE from './LMOVE'; describe('LMOVE', () => { - testUtils.isVersionGreaterThanHook([6, 2]); + testUtils.isVersionGreaterThanHook([6, 2]); - it('transformArguments', () => { - assert.deepEqual( - transformArguments('source', 'destination', 'LEFT', 'RIGHT'), - ['LMOVE', 'source', 'destination', 'LEFT', 'RIGHT'] - ); - }); + it('transformArguments', () => { + assert.deepEqual( + LMOVE.transformArguments('source', 'destination', 'LEFT', 'RIGHT'), + ['LMOVE', 'source', 'destination', 'LEFT', 'RIGHT'] + ); + }); - testUtils.testWithClient('client.lMove', async client => { - assert.equal( - await client.lMove('source', 'destination', 'LEFT', 'RIGHT'), - null - ); - }, GLOBAL.SERVERS.OPEN); - - testUtils.testWithCluster('cluster.lMove', async cluster => { - assert.equal( - await cluster.lMove('{tag}source', '{tag}destination', 'LEFT', 'RIGHT'), - null - ); - }, GLOBAL.CLUSTERS.OPEN); + testUtils.testAll('lMove', async client => { + assert.equal( + await client.lMove('{tag}source', '{tag}destination', 'LEFT', 'RIGHT'), + null + ); + }, { + client: GLOBAL.SERVERS.OPEN, + cluster: GLOBAL.CLUSTERS.OPEN + }); }); diff --git a/packages/client/lib/commands/LMOVE.ts b/packages/client/lib/commands/LMOVE.ts index 849c6385f5a..95adc71a0ff 100644 --- a/packages/client/lib/commands/LMOVE.ts +++ b/packages/client/lib/commands/LMOVE.ts @@ -1,21 +1,22 @@ -import { RedisCommandArgument, RedisCommandArguments } from '.'; +import { RedisArgument, BlobStringReply, NullReply, Command } from '../RESP/types'; import { ListSide } from './generic-transformers'; -export const FIRST_KEY_INDEX = 1; - -export function transformArguments( - source: RedisCommandArgument, - destination: RedisCommandArgument, +export default { + FIRST_KEY_INDEX: 1, + IS_READ_ONLY: false, + transformArguments( + source: RedisArgument, + destination: RedisArgument, sourceSide: ListSide, destinationSide: ListSide -): RedisCommandArguments { + ) { return [ - 'LMOVE', - source, - destination, - sourceSide, - destinationSide, + 'LMOVE', + source, + destination, + sourceSide, + destinationSide, ]; -} - -export declare function transformReply(): RedisCommandArgument | null; + }, + transformReply: undefined as unknown as () => BlobStringReply | NullReply +} as const satisfies Command; diff --git a/packages/client/lib/commands/LMPOP.spec.ts b/packages/client/lib/commands/LMPOP.spec.ts index 5675ee9a285..faf39e053ef 100644 --- a/packages/client/lib/commands/LMPOP.spec.ts +++ b/packages/client/lib/commands/LMPOP.spec.ts @@ -1,32 +1,50 @@ -import { strict as assert } from 'assert'; +import { strict as assert } from 'node:assert'; import testUtils, { GLOBAL } from '../test-utils'; -import { transformArguments } from './LMPOP'; +import LMPOP from './LMPOP'; describe('LMPOP', () => { - testUtils.isVersionGreaterThanHook([7]); + testUtils.isVersionGreaterThanHook([7]); - describe('transformArguments', () => { - it('simple', () => { - assert.deepEqual( - transformArguments('key', 'LEFT'), - ['LMPOP', '1', 'key', 'LEFT'] - ); - }); + describe('transformArguments', () => { + it('simple', () => { + assert.deepEqual( + LMPOP.transformArguments('key', 'LEFT'), + ['LMPOP', '1', 'key', 'LEFT'] + ); + }); - it('with COUNT', () => { - assert.deepEqual( - transformArguments('key', 'LEFT', { - COUNT: 2 - }), - ['LMPOP', '1', 'key', 'LEFT', 'COUNT', '2'] - ); - }); + it('with COUNT', () => { + assert.deepEqual( + LMPOP.transformArguments('key', 'LEFT', { + COUNT: 2 + }), + ['LMPOP', '1', 'key', 'LEFT', 'COUNT', '2'] + ); }); + }); + + testUtils.testAll('lmPop - null', async client => { + assert.equal( + await client.lmPop('key', 'RIGHT'), + null + ); + }, { + client: GLOBAL.SERVERS.OPEN, + cluster: GLOBAL.SERVERS.OPEN + }); + + testUtils.testAll('lmPop - with member', async client => { + const [, reply] = await Promise.all([ + client.lPush('key', 'element'), + client.lmPop('key', 'RIGHT') + ]); - testUtils.testWithClient('client.lmPop', async client => { - assert.deepEqual( - await client.lmPop('key', 'RIGHT'), - null - ); - }, GLOBAL.SERVERS.OPEN); + assert.deepEqual(reply, [ + 'key', + ['element'] + ]); + }, { + client: GLOBAL.SERVERS.OPEN, + cluster: GLOBAL.SERVERS.OPEN + }); }); diff --git a/packages/client/lib/commands/LMPOP.ts b/packages/client/lib/commands/LMPOP.ts index 29d868b982f..49f7272ec46 100644 --- a/packages/client/lib/commands/LMPOP.ts +++ b/packages/client/lib/commands/LMPOP.ts @@ -1,22 +1,37 @@ -import { RedisCommandArgument, RedisCommandArguments } from '.'; -import { transformLMPopArguments, LMPopOptions, ListSide } from './generic-transformers'; +import { CommandArguments, NullReply, TuplesReply, BlobStringReply, Command } from '../RESP/types'; +import { ListSide, RedisVariadicArgument, pushVariadicArgument } from './generic-transformers'; -export const FIRST_KEY_INDEX = 2; +export interface LMPopOptions { + COUNT?: number; +} + +export function transformLMPopArguments( + args: CommandArguments, + keys: RedisVariadicArgument, + side: ListSide, + options?: LMPopOptions +): CommandArguments { + args = pushVariadicArgument(args, keys); + + args.push(side); -export function transformArguments( - keys: RedisCommandArgument | Array, - side: ListSide, - options?: LMPopOptions -): RedisCommandArguments { - return transformLMPopArguments( - ['LMPOP'], - keys, - side, - options - ); + if (options?.COUNT !== undefined) { + args.push('COUNT', options.COUNT.toString()); + } + + return args; } -export declare function transformReply(): null | [ - key: string, - elements: Array -]; +export type LMPopArguments = typeof transformLMPopArguments extends (_: any, ...args: infer T) => any ? T : never; + +export default { + FIRST_KEY_INDEX: 2, + IS_READ_ONLY: false, + transformArguments(...args: LMPopArguments) { + return transformLMPopArguments(['LMPOP'], ...args); + }, + transformReply: undefined as unknown as () => NullReply | TuplesReply<[ + key: BlobStringReply, + elements: Array + ]> +} as const satisfies Command; diff --git a/packages/client/lib/commands/LOLWUT.spec.ts b/packages/client/lib/commands/LOLWUT.spec.ts index db335893302..b05c4168f6f 100644 --- a/packages/client/lib/commands/LOLWUT.spec.ts +++ b/packages/client/lib/commands/LOLWUT.spec.ts @@ -1,35 +1,35 @@ -import { strict as assert } from 'assert'; +import { strict as assert } from 'node:assert'; import testUtils, { GLOBAL } from '../test-utils'; -import { transformArguments } from './LOLWUT'; +import LOLWUT from './LOLWUT'; describe('LOLWUT', () => { - describe('transformArguments', () => { - it('simple', () => { - assert.deepEqual( - transformArguments(), - ['LOLWUT'] - ); - }); + describe('transformArguments', () => { + it('simple', () => { + assert.deepEqual( + LOLWUT.transformArguments(), + ['LOLWUT'] + ); + }); - it('with version', () => { - assert.deepEqual( - transformArguments(5), - ['LOLWUT', 'VERSION', '5'] - ); - }); + it('with version', () => { + assert.deepEqual( + LOLWUT.transformArguments(5), + ['LOLWUT', 'VERSION', '5'] + ); + }); - it('with version and optional arguments', () => { - assert.deepEqual( - transformArguments(5, 1, 2, 3), - ['LOLWUT', 'VERSION', '5', '1', '2', '3'] - ); - }); + it('with version and optional arguments', () => { + assert.deepEqual( + LOLWUT.transformArguments(5, 1, 2, 3), + ['LOLWUT', 'VERSION', '5', '1', '2', '3'] + ); }); + }); - testUtils.testWithClient('client.LOLWUT', async client => { - assert.equal( - typeof (await client.LOLWUT()), - 'string' - ); - }, GLOBAL.SERVERS.OPEN); + testUtils.testWithClient('client.LOLWUT', async client => { + assert.equal( + typeof (await client.LOLWUT()), + 'string' + ); + }, GLOBAL.SERVERS.OPEN); }); diff --git a/packages/client/lib/commands/LOLWUT.ts b/packages/client/lib/commands/LOLWUT.ts index 5d5fc726065..7a6c8329d69 100644 --- a/packages/client/lib/commands/LOLWUT.ts +++ b/packages/client/lib/commands/LOLWUT.ts @@ -1,19 +1,20 @@ -import { RedisCommandArgument } from '.'; +import { BlobStringReply, Command } from '../RESP/types'; -export const IS_READ_ONLY = true; - -export function transformArguments(version?: number, ...optionalArguments: Array): Array { +export default { + FIRST_KEY_INDEX: undefined, + IS_READ_ONLY: true, + transformArguments(version?: number, ...optionalArguments: Array) { const args = ['LOLWUT']; if (version) { - args.push( - 'VERSION', - version.toString(), - ...optionalArguments.map(String), - ); + args.push( + 'VERSION', + version.toString(), + ...optionalArguments.map(String), + ); } return args; -} - -export declare function transformReply(): RedisCommandArgument; + }, + transformReply: undefined as unknown as () => BlobStringReply +} as const satisfies Command; diff --git a/packages/client/lib/commands/LPOP.spec.ts b/packages/client/lib/commands/LPOP.spec.ts index d694fb10588..944e559b15f 100644 --- a/packages/client/lib/commands/LPOP.spec.ts +++ b/packages/client/lib/commands/LPOP.spec.ts @@ -1,26 +1,22 @@ -import { strict as assert } from 'assert'; +import { strict as assert } from 'node:assert'; import testUtils, { GLOBAL } from '../test-utils'; -import { transformArguments } from './LPOP'; +import LPOP from './LPOP'; describe('LPOP', () => { - it('transformArguments', () => { - assert.deepEqual( - transformArguments('key'), - ['LPOP', 'key'] - ); - }); + it('transformArguments', () => { + assert.deepEqual( + LPOP.transformArguments('key'), + ['LPOP', 'key'] + ); + }); - testUtils.testWithClient('client.lPop', async client => { - assert.equal( - await client.lPop('key'), - null - ); - }, GLOBAL.SERVERS.OPEN); - - testUtils.testWithCluster('cluster.lPop', async cluster => { - assert.equal( - await cluster.lPop('key'), - null - ); - }, GLOBAL.CLUSTERS.OPEN); + testUtils.testAll('lPop', async client => { + assert.equal( + await client.lPop('key'), + null + ); + }, { + client: GLOBAL.SERVERS.OPEN, + cluster: GLOBAL.CLUSTERS.OPEN + }); }); diff --git a/packages/client/lib/commands/LPOP.ts b/packages/client/lib/commands/LPOP.ts index 5dd1bea5196..7c85c30f9a1 100644 --- a/packages/client/lib/commands/LPOP.ts +++ b/packages/client/lib/commands/LPOP.ts @@ -1,9 +1,9 @@ -import { RedisCommandArgument, RedisCommandArguments } from '.'; +import { RedisArgument, BlobStringReply, NullReply, Command } from '../RESP/types'; -export const FIRST_KEY_INDEX = 1; - -export function transformArguments(key: RedisCommandArgument): RedisCommandArguments { +export default { + FIRST_KEY_INDEX: 1, + transformArguments(key: RedisArgument) { return ['LPOP', key]; -} - -export declare function transformReply(): RedisCommandArgument | null; + }, + transformReply: undefined as unknown as () => BlobStringReply | NullReply +} as const satisfies Command; diff --git a/packages/client/lib/commands/LPOP_COUNT.spec.ts b/packages/client/lib/commands/LPOP_COUNT.spec.ts index 9d87fad3862..8286a504428 100644 --- a/packages/client/lib/commands/LPOP_COUNT.spec.ts +++ b/packages/client/lib/commands/LPOP_COUNT.spec.ts @@ -1,28 +1,24 @@ -import { strict as assert } from 'assert'; +import { strict as assert } from 'node:assert'; import testUtils, { GLOBAL } from '../test-utils'; -import { transformArguments } from './LPOP_COUNT'; +import LPOP_COUNT from './LPOP_COUNT'; describe('LPOP COUNT', () => { - testUtils.isVersionGreaterThanHook([6, 2]); + testUtils.isVersionGreaterThanHook([6, 2]); - it('transformArguments', () => { - assert.deepEqual( - transformArguments('key', 1), - ['LPOP', 'key', '1'] - ); - }); + it('transformArguments', () => { + assert.deepEqual( + LPOP_COUNT.transformArguments('key', 1), + ['LPOP', 'key', '1'] + ); + }); - testUtils.testWithClient('client.lPopCount', async client => { - assert.equal( - await client.lPopCount('key', 1), - null - ); - }, GLOBAL.SERVERS.OPEN); - - testUtils.testWithCluster('cluster.lPopCount', async cluster => { - assert.equal( - await cluster.lPopCount('key', 1), - null - ); - }, GLOBAL.CLUSTERS.OPEN); + testUtils.testAll('lPopCount', async client => { + assert.equal( + await client.lPopCount('key', 1), + null + ); + }, { + client: GLOBAL.SERVERS.OPEN, + cluster: GLOBAL.CLUSTERS.OPEN + }); }); diff --git a/packages/client/lib/commands/LPOP_COUNT.ts b/packages/client/lib/commands/LPOP_COUNT.ts index 021517b018a..a1536e78dcb 100644 --- a/packages/client/lib/commands/LPOP_COUNT.ts +++ b/packages/client/lib/commands/LPOP_COUNT.ts @@ -1,12 +1,10 @@ -import { RedisCommandArgument, RedisCommandArguments } from '.'; +import { RedisArgument, NullReply, ArrayReply, BlobStringReply, Command } from '../RESP/types'; -export const FIRST_KEY_INDEX = 1; - -export function transformArguments( - key: RedisCommandArgument, - count: number -): RedisCommandArguments { +export default { + FIRST_KEY_INDEX: 2, + IS_READ_ONLY: false, + transformArguments(key: RedisArgument, count: number) { return ['LPOP', key, count.toString()]; -} - -export declare function transformReply(): Array | null; + }, + transformReply: undefined as unknown as () => NullReply | ArrayReply +} as const satisfies Command; diff --git a/packages/client/lib/commands/LPOS.spec.ts b/packages/client/lib/commands/LPOS.spec.ts index 6b6050f2c3b..94c0bb3c034 100644 --- a/packages/client/lib/commands/LPOS.spec.ts +++ b/packages/client/lib/commands/LPOS.spec.ts @@ -1,58 +1,54 @@ -import { strict as assert } from 'assert'; +import { strict as assert } from 'node:assert'; import testUtils, { GLOBAL } from '../test-utils'; -import { transformArguments } from './LPOS'; +import LPOS from './LPOS'; describe('LPOS', () => { - testUtils.isVersionGreaterThanHook([6, 0, 6]); + testUtils.isVersionGreaterThanHook([6, 0, 6]); - describe('transformArguments', () => { - it('simple', () => { - assert.deepEqual( - transformArguments('key', 'element'), - ['LPOS', 'key', 'element'] - ); - }); - - it('with RANK', () => { - assert.deepEqual( - transformArguments('key', 'element', { - RANK: 0 - }), - ['LPOS', 'key', 'element', 'RANK', '0'] - ); - }); + describe('transformArguments', () => { + it('simple', () => { + assert.deepEqual( + LPOS.transformArguments('key', 'element'), + ['LPOS', 'key', 'element'] + ); + }); - it('with MAXLEN', () => { - assert.deepEqual( - transformArguments('key', 'element', { - MAXLEN: 10 - }), - ['LPOS', 'key', 'element', 'MAXLEN', '10'] - ); - }); + it('with RANK', () => { + assert.deepEqual( + LPOS.transformArguments('key', 'element', { + RANK: 0 + }), + ['LPOS', 'key', 'element', 'RANK', '0'] + ); + }); - it('with RANK, MAXLEN', () => { - assert.deepEqual( - transformArguments('key', 'element', { - RANK: 0, - MAXLEN: 10 - }), - ['LPOS', 'key', 'element', 'RANK', '0', 'MAXLEN', '10'] - ); - }); + it('with MAXLEN', () => { + assert.deepEqual( + LPOS.transformArguments('key', 'element', { + MAXLEN: 10 + }), + ['LPOS', 'key', 'element', 'MAXLEN', '10'] + ); }); - testUtils.testWithClient('client.lPos', async client => { - assert.equal( - await client.lPos('key', 'element'), - null - ); - }, GLOBAL.SERVERS.OPEN); + it('with RANK, MAXLEN', () => { + assert.deepEqual( + LPOS.transformArguments('key', 'element', { + RANK: 0, + MAXLEN: 10 + }), + ['LPOS', 'key', 'element', 'RANK', '0', 'MAXLEN', '10'] + ); + }); + }); - testUtils.testWithCluster('cluster.lPos', async cluster => { - assert.equal( - await cluster.lPos('key', 'element'), - null - ); - }, GLOBAL.CLUSTERS.OPEN); + testUtils.testAll('lPos', async client => { + assert.equal( + await client.lPos('key', 'element'), + null + ); + }, { + client: GLOBAL.SERVERS.OPEN, + cluster: GLOBAL.CLUSTERS.OPEN + }); }); diff --git a/packages/client/lib/commands/LPOS.ts b/packages/client/lib/commands/LPOS.ts index 1f2e34ab88e..047d80d7c29 100644 --- a/packages/client/lib/commands/LPOS.ts +++ b/packages/client/lib/commands/LPOS.ts @@ -1,30 +1,31 @@ -import { RedisCommandArgument, RedisCommandArguments } from '.'; - -export const FIRST_KEY_INDEX = 1; - -export const IS_READ_ONLY = true; +import { RedisArgument, NumberReply, NullReply, Command } from '../RESP/types'; export interface LPosOptions { - RANK?: number; - MAXLEN?: number; + RANK?: number; + MAXLEN?: number; } -export function transformArguments( - key: RedisCommandArgument, - element: RedisCommandArgument, +export default { + FIRST_KEY_INDEX: 1, + IS_READ_ONLY: true, + transformArguments( + key: RedisArgument, + element: RedisArgument, options?: LPosOptions -): RedisCommandArguments { + ) { const args = ['LPOS', key, element]; - if (typeof options?.RANK === 'number') { + if (options) { + if (typeof options.RANK === 'number') { args.push('RANK', options.RANK.toString()); - } + } - if (typeof options?.MAXLEN === 'number') { + if (typeof options.MAXLEN === 'number') { args.push('MAXLEN', options.MAXLEN.toString()); + } } return args; -} - -export declare function transformReply(): number | null; + }, + transformReply: undefined as unknown as () => NumberReply | NullReply +} as const satisfies Command; diff --git a/packages/client/lib/commands/LPOS_COUNT.spec.ts b/packages/client/lib/commands/LPOS_COUNT.spec.ts index 4b01f2f59b9..747ffbc01cf 100644 --- a/packages/client/lib/commands/LPOS_COUNT.spec.ts +++ b/packages/client/lib/commands/LPOS_COUNT.spec.ts @@ -1,58 +1,54 @@ -import { strict as assert } from 'assert'; +import { strict as assert } from 'node:assert'; import testUtils, { GLOBAL } from '../test-utils'; -import { transformArguments } from './LPOS_COUNT'; +import LPOS_COUNT from './LPOS_COUNT'; describe('LPOS COUNT', () => { - testUtils.isVersionGreaterThanHook([6, 0, 6]); + testUtils.isVersionGreaterThanHook([6, 0, 6]); - describe('transformArguments', () => { - it('simple', () => { - assert.deepEqual( - transformArguments('key', 'element', 0), - ['LPOS', 'key', 'element', 'COUNT', '0'] - ); - }); - - it('with RANK', () => { - assert.deepEqual( - transformArguments('key', 'element', 0, { - RANK: 0 - }), - ['LPOS', 'key', 'element', 'RANK', '0', 'COUNT', '0'] - ); - }); + describe('transformArguments', () => { + it('simple', () => { + assert.deepEqual( + LPOS_COUNT.transformArguments('key', 'element', 0), + ['LPOS', 'key', 'element', 'COUNT', '0'] + ); + }); - it('with MAXLEN', () => { - assert.deepEqual( - transformArguments('key', 'element', 0, { - MAXLEN: 10 - }), - ['LPOS', 'key', 'element', 'COUNT', '0', 'MAXLEN', '10'] - ); - }); + it('with RANK', () => { + assert.deepEqual( + LPOS_COUNT.transformArguments('key', 'element', 0, { + RANK: 0 + }), + ['LPOS', 'key', 'element', 'RANK', '0', 'COUNT', '0'] + ); + }); - it('with RANK, MAXLEN', () => { - assert.deepEqual( - transformArguments('key', 'element', 0, { - RANK: 0, - MAXLEN: 10 - }), - ['LPOS', 'key', 'element', 'RANK', '0', 'COUNT', '0', 'MAXLEN', '10'] - ); - }); + it('with MAXLEN', () => { + assert.deepEqual( + LPOS_COUNT.transformArguments('key', 'element', 0, { + MAXLEN: 10 + }), + ['LPOS', 'key', 'element', 'COUNT', '0', 'MAXLEN', '10'] + ); }); - testUtils.testWithClient('client.lPosCount', async client => { - assert.deepEqual( - await client.lPosCount('key', 'element', 0), - [] - ); - }, GLOBAL.SERVERS.OPEN); + it('with RANK, MAXLEN', () => { + assert.deepEqual( + LPOS_COUNT.transformArguments('key', 'element', 0, { + RANK: 0, + MAXLEN: 10 + }), + ['LPOS', 'key', 'element', 'RANK', '0', 'COUNT', '0', 'MAXLEN', '10'] + ); + }); + }); - testUtils.testWithCluster('cluster.lPosCount', async cluster => { - assert.deepEqual( - await cluster.lPosCount('key', 'element', 0), - [] - ); - }, GLOBAL.CLUSTERS.OPEN); + testUtils.testAll('lPosCount', async client => { + assert.deepEqual( + await client.lPosCount('key', 'element', 0), + [] + ); + }, { + client: GLOBAL.SERVERS.OPEN, + cluster: GLOBAL.CLUSTERS.OPEN + }); }); diff --git a/packages/client/lib/commands/LPOS_COUNT.ts b/packages/client/lib/commands/LPOS_COUNT.ts index 0549df82db5..1b057cff1f6 100644 --- a/packages/client/lib/commands/LPOS_COUNT.ts +++ b/packages/client/lib/commands/LPOS_COUNT.ts @@ -1,27 +1,28 @@ -import { RedisCommandArgument, RedisCommandArguments } from '.'; -import { LPosOptions } from './LPOS'; +import { RedisArgument, ArrayReply, NumberReply, Command } from '../RESP/types'; +import LPOS, { LPosOptions } from './LPOS'; -export { FIRST_KEY_INDEX, IS_READ_ONLY } from './LPOS'; - -export function transformArguments( - key: RedisCommandArgument, - element: RedisCommandArgument, +export default { + FIRST_KEY_INDEX: LPOS.FIRST_KEY_INDEX, + IS_READ_ONLY: LPOS.IS_READ_ONLY, + transformArguments( + key: RedisArgument, + element: RedisArgument, count: number, options?: LPosOptions -): RedisCommandArguments { + ) { const args = ['LPOS', key, element]; - if (typeof options?.RANK === 'number') { - args.push('RANK', options.RANK.toString()); + if (options?.RANK !== undefined) { + args.push('RANK', options.RANK.toString()); } args.push('COUNT', count.toString()); - if (typeof options?.MAXLEN === 'number') { - args.push('MAXLEN', options.MAXLEN.toString()); + if (options?.MAXLEN !== undefined) { + args.push('MAXLEN', options.MAXLEN.toString()); } return args; -} - -export declare function transformReply(): Array; + }, + transformReply: undefined as unknown as () => ArrayReply +} as const satisfies Command; diff --git a/packages/client/lib/commands/LPUSH.spec.ts b/packages/client/lib/commands/LPUSH.spec.ts index b5b1f5084eb..8d2ddb5ccc2 100644 --- a/packages/client/lib/commands/LPUSH.spec.ts +++ b/packages/client/lib/commands/LPUSH.spec.ts @@ -1,35 +1,31 @@ -import { strict as assert } from 'assert'; +import { strict as assert } from 'node:assert'; import testUtils, { GLOBAL } from '../test-utils'; -import { transformArguments } from './LPUSH'; +import LPUSH from './LPUSH'; describe('LPUSH', () => { - describe('transformArguments', () => { - it('string', () => { - assert.deepEqual( - transformArguments('key', 'field'), - ['LPUSH', 'key', 'field'] - ); - }); - - it('array', () => { - assert.deepEqual( - transformArguments('key', ['1', '2']), - ['LPUSH', 'key', '1', '2'] - ); - }); + describe('transformArguments', () => { + it('string', () => { + assert.deepEqual( + LPUSH.transformArguments('key', 'field'), + ['LPUSH', 'key', 'field'] + ); }); - testUtils.testWithClient('client.lPush', async client => { - assert.equal( - await client.lPush('key', 'field'), - 1 - ); - }, GLOBAL.SERVERS.OPEN); + it('array', () => { + assert.deepEqual( + LPUSH.transformArguments('key', ['1', '2']), + ['LPUSH', 'key', '1', '2'] + ); + }); + }); - testUtils.testWithCluster('cluster.lPush', async cluster => { - assert.equal( - await cluster.lPush('key', 'field'), - 1 - ); - }, GLOBAL.CLUSTERS.OPEN); + testUtils.testAll('lPush', async client => { + assert.equal( + await client.lPush('key', 'field'), + 1 + ); + }, { + client: GLOBAL.SERVERS.OPEN, + cluster: GLOBAL.CLUSTERS.OPEN + }); }); diff --git a/packages/client/lib/commands/LPUSH.ts b/packages/client/lib/commands/LPUSH.ts index 7144b146e27..714db2ae9a9 100644 --- a/packages/client/lib/commands/LPUSH.ts +++ b/packages/client/lib/commands/LPUSH.ts @@ -1,12 +1,10 @@ -import { RedisCommandArgument, RedisCommandArguments } from '.'; -import { pushVerdictArguments } from './generic-transformers'; +import { RedisArgument, NumberReply, Command } from '../RESP/types'; +import { RedisVariadicArgument, pushVariadicArguments } from './generic-transformers'; -export const FIRST_KEY_INDEX = 1; - -export function transformArguments( - key: RedisCommandArgument, - elements: RedisCommandArgument | Array -): RedisCommandArguments { - return pushVerdictArguments(['LPUSH', key], elements);} - -export declare function transformReply(): number; +export default { + FIRST_KEY_INDEX: 1, + transformArguments(key: RedisArgument, elements: RedisVariadicArgument) { + return pushVariadicArguments(['LPUSH', key], elements); + }, + transformReply: undefined as unknown as () => NumberReply +} as const satisfies Command; diff --git a/packages/client/lib/commands/LPUSHX.spec.ts b/packages/client/lib/commands/LPUSHX.spec.ts index d978e5a588f..f7dafe025c7 100644 --- a/packages/client/lib/commands/LPUSHX.spec.ts +++ b/packages/client/lib/commands/LPUSHX.spec.ts @@ -1,35 +1,31 @@ -import { strict as assert } from 'assert'; +import { strict as assert } from 'node:assert'; import testUtils, { GLOBAL } from '../test-utils'; -import { transformArguments } from './LPUSHX'; +import LPUSHX from './LPUSHX'; describe('LPUSHX', () => { - describe('transformArguments', () => { - it('string', () => { - assert.deepEqual( - transformArguments('key', 'element'), - ['LPUSHX', 'key', 'element'] - ); - }); - - it('array', () => { - assert.deepEqual( - transformArguments('key', ['1', '2']), - ['LPUSHX', 'key', '1', '2'] - ); - }); + describe('transformArguments', () => { + it('string', () => { + assert.deepEqual( + LPUSHX.transformArguments('key', 'element'), + ['LPUSHX', 'key', 'element'] + ); }); - testUtils.testWithClient('client.lPushX', async client => { - assert.equal( - await client.lPushX('key', 'element'), - 0 - ); - }, GLOBAL.SERVERS.OPEN); + it('array', () => { + assert.deepEqual( + LPUSHX.transformArguments('key', ['1', '2']), + ['LPUSHX', 'key', '1', '2'] + ); + }); + }); - testUtils.testWithCluster('cluster.lPushX', async cluster => { - assert.equal( - await cluster.lPushX('key', 'element'), - 0 - ); - }, GLOBAL.CLUSTERS.OPEN); + testUtils.testAll('lPushX', async client => { + assert.equal( + await client.lPushX('key', 'element'), + 0 + ); + }, { + client: GLOBAL.SERVERS.OPEN, + cluster: GLOBAL.CLUSTERS.OPEN + }); }); diff --git a/packages/client/lib/commands/LPUSHX.ts b/packages/client/lib/commands/LPUSHX.ts index 0b518add6da..d6dceda6d02 100644 --- a/packages/client/lib/commands/LPUSHX.ts +++ b/packages/client/lib/commands/LPUSHX.ts @@ -1,13 +1,10 @@ -import { RedisCommandArgument, RedisCommandArguments } from '.'; -import { pushVerdictArguments } from './generic-transformers'; +import { RedisArgument, NumberReply, Command } from '../RESP/types'; +import { RedisVariadicArgument, pushVariadicArguments } from './generic-transformers'; -export const FIRST_KEY_INDEX = 1; - -export function transformArguments( - key: RedisCommandArgument, - element: RedisCommandArgument | Array -): RedisCommandArguments { - return pushVerdictArguments(['LPUSHX', key], element); -} - -export declare function transformReply(): number; +export default { + FIRST_KEY_INDEX: 1, + transformArguments(key: RedisArgument, elements: RedisVariadicArgument) { + return pushVariadicArguments(['LPUSHX', key], elements); + }, + transformReply: undefined as unknown as () => NumberReply +} as const satisfies Command; diff --git a/packages/client/lib/commands/LRANGE.spec.ts b/packages/client/lib/commands/LRANGE.spec.ts index dffe6087b80..3e768cc0731 100644 --- a/packages/client/lib/commands/LRANGE.spec.ts +++ b/packages/client/lib/commands/LRANGE.spec.ts @@ -1,27 +1,22 @@ - -import { strict as assert } from 'assert'; +import { strict as assert } from 'node:assert'; import testUtils, { GLOBAL } from '../test-utils'; -import { transformArguments } from './LRANGE'; +import LRANGE from './LRANGE'; describe('LRANGE', () => { - it('transformArguments', () => { - assert.deepEqual( - transformArguments('key', 0, -1), - ['LRANGE', 'key', '0', '-1'] - ); - }); - - testUtils.testWithClient('client.lRange', async client => { - assert.deepEqual( - await client.lRange('key', 0, -1), - [] - ); - }, GLOBAL.SERVERS.OPEN); + it('transformArguments', () => { + assert.deepEqual( + LRANGE.transformArguments('key', 0, -1), + ['LRANGE', 'key', '0', '-1'] + ); + }); - testUtils.testWithCluster('cluster.lRange', async cluster => { - assert.deepEqual( - await cluster.lRange('key', 0, -1), - [] - ); - }, GLOBAL.CLUSTERS.OPEN); + testUtils.testAll('lRange', async client => { + assert.deepEqual( + await client.lRange('key', 0, -1), + [] + ); + }, { + client: GLOBAL.SERVERS.OPEN, + cluster: GLOBAL.CLUSTERS.OPEN + }); }); diff --git a/packages/client/lib/commands/LRANGE.ts b/packages/client/lib/commands/LRANGE.ts index df12c57d804..4b4b3488baa 100644 --- a/packages/client/lib/commands/LRANGE.ts +++ b/packages/client/lib/commands/LRANGE.ts @@ -1,20 +1,19 @@ -import { RedisCommandArgument, RedisCommandArguments } from '.'; +import { RedisArgument, ArrayReply, BlobStringReply, Command } from '../RESP/types'; -export const FIRST_KEY_INDEX = 1; - -export const IS_READ_ONLY = true; - -export function transformArguments( - key: RedisCommandArgument, +export default { + FIRST_KEY_INDEX: 1, + IS_READ_ONLY: true, + transformArguments( + key: RedisArgument, start: number, stop: number -): RedisCommandArguments { +) { return [ - 'LRANGE', - key, - start.toString(), - stop.toString() + 'LRANGE', + key, + start.toString(), + stop.toString() ]; -} - -export declare function transformReply(): Array; + }, + transformReply: undefined as unknown as () => ArrayReply +} as const satisfies Command; diff --git a/packages/client/lib/commands/LREM.spec.ts b/packages/client/lib/commands/LREM.spec.ts index 3405f4beb07..1a35dab7c54 100644 --- a/packages/client/lib/commands/LREM.spec.ts +++ b/packages/client/lib/commands/LREM.spec.ts @@ -1,27 +1,22 @@ - -import { strict as assert } from 'assert'; +import { strict as assert } from 'node:assert'; import testUtils, { GLOBAL } from '../test-utils'; -import { transformArguments } from './LREM'; +import LREM from './LREM'; describe('LREM', () => { - it('transformArguments', () => { - assert.deepEqual( - transformArguments('key', 0, 'element'), - ['LREM', 'key', '0', 'element'] - ); - }); - - testUtils.testWithClient('client.lRem', async client => { - assert.equal( - await client.lRem('key', 0, 'element'), - 0 - ); - }, GLOBAL.SERVERS.OPEN); + it('transformArguments', () => { + assert.deepEqual( + LREM.transformArguments('key', 0, 'element'), + ['LREM', 'key', '0', 'element'] + ); + }); - testUtils.testWithCluster('cluster.lRem', async cluster => { - assert.equal( - await cluster.lRem('key', 0, 'element'), - 0 - ); - }, GLOBAL.CLUSTERS.OPEN); + testUtils.testAll('lRem', async client => { + assert.equal( + await client.lRem('key', 0, 'element'), + 0 + ); + }, { + client: GLOBAL.SERVERS.OPEN, + cluster: GLOBAL.CLUSTERS.OPEN + }); }); diff --git a/packages/client/lib/commands/LREM.ts b/packages/client/lib/commands/LREM.ts index b4951334888..dbd2002be8b 100644 --- a/packages/client/lib/commands/LREM.ts +++ b/packages/client/lib/commands/LREM.ts @@ -1,18 +1,19 @@ -import { RedisCommandArgument, RedisCommandArguments } from '.'; +import { RedisArgument, NumberReply, Command } from '../RESP/types'; -export const FIRST_KEY_INDEX = 1; - -export function transformArguments( - key: RedisCommandArgument, +export default { + FIRST_KEY_INDEX: 1, + IS_READ_ONLY: true, + transformArguments( + key: RedisArgument, count: number, - element: RedisCommandArgument -): RedisCommandArguments { + element: RedisArgument + ) { return [ - 'LREM', - key, - count.toString(), - element + 'LREM', + key, + count.toString(), + element ]; -} - -export declare function transformReply(): number; + }, + transformReply: undefined as unknown as () => NumberReply +} as const satisfies Command; diff --git a/packages/client/lib/commands/LSET.spec.ts b/packages/client/lib/commands/LSET.spec.ts index d7241032cc6..ae4b1bf9f2f 100644 --- a/packages/client/lib/commands/LSET.spec.ts +++ b/packages/client/lib/commands/LSET.spec.ts @@ -1,28 +1,23 @@ -import { strict as assert } from 'assert'; +import { strict as assert } from 'node:assert'; import testUtils, { GLOBAL } from '../test-utils'; -import { transformArguments } from './LSET'; +import LSET from './LSET'; describe('LSET', () => { - it('transformArguments', () => { - assert.deepEqual( - transformArguments('key', 0, 'element'), - ['LSET', 'key', '0', 'element'] - ); - }); + it('transformArguments', () => { + assert.deepEqual( + LSET.transformArguments('key', 0, 'element'), + ['LSET', 'key', '0', 'element'] + ); + }); - testUtils.testWithClient('client.lSet', async client => { - await client.lPush('key', 'element'); - assert.equal( - await client.lSet('key', 0, 'element'), - 'OK' - ); - }, GLOBAL.SERVERS.OPEN); - - testUtils.testWithCluster('cluster.lSet', async cluster => { - await cluster.lPush('key', 'element'); - assert.equal( - await cluster.lSet('key', 0, 'element'), - 'OK' - ); - }, GLOBAL.CLUSTERS.OPEN); + testUtils.testAll('lSet', async client => { + await client.lPush('key', 'element'); + assert.equal( + await client.lSet('key', 0, 'element'), + 'OK' + ); + }, { + client: GLOBAL.SERVERS.OPEN, + cluster: GLOBAL.CLUSTERS.OPEN + }); }); diff --git a/packages/client/lib/commands/LSET.ts b/packages/client/lib/commands/LSET.ts index 33c7b4cc060..edb86baae0f 100644 --- a/packages/client/lib/commands/LSET.ts +++ b/packages/client/lib/commands/LSET.ts @@ -1,18 +1,19 @@ -import { RedisCommandArgument, RedisCommandArguments } from '.'; +import { RedisArgument, SimpleStringReply, Command } from '../RESP/types'; -export const FIRST_KEY_INDEX = 1; - -export function transformArguments( - key: RedisCommandArgument, +export default { + FIRST_KEY_INDEX: 1, + IS_READ_ONLY: true, + transformArguments( + key: RedisArgument, index: number, - element: RedisCommandArgument -): RedisCommandArguments { + element: RedisArgument + ) { return [ - 'LSET', - key, - index.toString(), - element + 'LSET', + key, + index.toString(), + element ]; -} - -export declare function transformReply(): RedisCommandArgument; + }, + transformReply: undefined as unknown as () => SimpleStringReply<'OK'> +} as const satisfies Command; diff --git a/packages/client/lib/commands/LTRIM.spec.ts b/packages/client/lib/commands/LTRIM.spec.ts index 5b6ac5d3660..3edc3669737 100644 --- a/packages/client/lib/commands/LTRIM.spec.ts +++ b/packages/client/lib/commands/LTRIM.spec.ts @@ -1,26 +1,22 @@ -import { strict as assert } from 'assert'; +import { strict as assert } from 'node:assert'; import testUtils, { GLOBAL } from '../test-utils'; -import { transformArguments } from './LTRIM'; +import LTRIM from './LTRIM'; describe('LTRIM', () => { - it('transformArguments', () => { - assert.deepEqual( - transformArguments('key', 0, -1), - ['LTRIM', 'key', '0', '-1'] - ); - }); + it('transformArguments', () => { + assert.deepEqual( + LTRIM.transformArguments('key', 0, -1), + ['LTRIM', 'key', '0', '-1'] + ); + }); - testUtils.testWithClient('client.lTrim', async client => { - assert.equal( - await client.lTrim('key', 0, -1), - 'OK' - ); - }, GLOBAL.SERVERS.OPEN); - - testUtils.testWithCluster('cluster.lTrim', async cluster => { - assert.equal( - await cluster.lTrim('key', 0, -1), - 'OK' - ); - }, GLOBAL.CLUSTERS.OPEN); + testUtils.testAll('lTrim', async client => { + assert.equal( + await client.lTrim('key', 0, -1), + 'OK' + ); + }, { + client: GLOBAL.SERVERS.OPEN, + cluster: GLOBAL.CLUSTERS.OPEN + }); }); diff --git a/packages/client/lib/commands/LTRIM.ts b/packages/client/lib/commands/LTRIM.ts index 668497cdde6..b80aa6264e9 100644 --- a/packages/client/lib/commands/LTRIM.ts +++ b/packages/client/lib/commands/LTRIM.ts @@ -1,18 +1,18 @@ -import { RedisCommandArgument, RedisCommandArguments } from '.'; +import { RedisArgument, SimpleStringReply, Command } from '../RESP/types'; -export const FIRST_KEY_INDEX = 1; - -export function transformArguments( - key: RedisCommandArgument, +export default { + FIRST_KEY_INDEX: 1, + transformArguments( + key: RedisArgument, start: number, stop: number -): RedisCommandArguments { + ) { return [ - 'LTRIM', - key, - start.toString(), - stop.toString() + 'LTRIM', + key, + start.toString(), + stop.toString() ]; -} - -export declare function transformReply(): RedisCommandArgument; + }, + transformReply: undefined as unknown as () => SimpleStringReply<'OK'> +} as const satisfies Command; diff --git a/packages/client/lib/commands/MEMORY_DOCTOR.spec.ts b/packages/client/lib/commands/MEMORY_DOCTOR.spec.ts index ad97047606c..e1d718d7aab 100644 --- a/packages/client/lib/commands/MEMORY_DOCTOR.spec.ts +++ b/packages/client/lib/commands/MEMORY_DOCTOR.spec.ts @@ -1,19 +1,19 @@ -import { strict as assert } from 'assert'; +import { strict as assert } from 'node:assert'; import testUtils, { GLOBAL } from '../test-utils'; -import { transformArguments } from './MEMORY_DOCTOR'; +import MEMORY_DOCTOR from './MEMORY_DOCTOR'; describe('MEMORY DOCTOR', () => { - it('transformArguments', () => { - assert.deepEqual( - transformArguments(), - ['MEMORY', 'DOCTOR'] - ); - }); + it('transformArguments', () => { + assert.deepEqual( + MEMORY_DOCTOR.transformArguments(), + ['MEMORY', 'DOCTOR'] + ); + }); - testUtils.testWithClient('client.memoryDoctor', async client => { - assert.equal( - typeof (await client.memoryDoctor()), - 'string' - ); - }, GLOBAL.SERVERS.OPEN); + testUtils.testWithClient('client.memoryDoctor', async client => { + assert.equal( + typeof (await client.memoryDoctor()), + 'string' + ); + }, GLOBAL.SERVERS.OPEN); }); diff --git a/packages/client/lib/commands/MEMORY_DOCTOR.ts b/packages/client/lib/commands/MEMORY_DOCTOR.ts index 95a37246ffa..6f8eb29ef2b 100644 --- a/packages/client/lib/commands/MEMORY_DOCTOR.ts +++ b/packages/client/lib/commands/MEMORY_DOCTOR.ts @@ -1,5 +1,10 @@ -export function transformArguments(): Array { - return ['MEMORY', 'DOCTOR']; -} +import { BlobStringReply, Command } from '../RESP/types'; -export declare function transformReply(): string; +export default { + FIRST_KEY_INDEX: undefined, + IS_READ_ONLY: true, + transformArguments() { + return ['MEMORY', 'DOCTOR']; + }, + transformReply: undefined as unknown as () => BlobStringReply +} as const satisfies Command; diff --git a/packages/client/lib/commands/MEMORY_MALLOC-STATS.spec.ts b/packages/client/lib/commands/MEMORY_MALLOC-STATS.spec.ts index ce866f1e116..8484fcbf0b7 100644 --- a/packages/client/lib/commands/MEMORY_MALLOC-STATS.spec.ts +++ b/packages/client/lib/commands/MEMORY_MALLOC-STATS.spec.ts @@ -1,19 +1,19 @@ -import { strict as assert } from 'assert'; +import { strict as assert } from 'node:assert'; import testUtils, { GLOBAL } from '../test-utils'; -import { transformArguments } from './MEMORY_MALLOC-STATS'; +import MEMORY_MALLOC_STATS from './MEMORY_MALLOC-STATS'; describe('MEMORY MALLOC-STATS', () => { - it('transformArguments', () => { - assert.deepEqual( - transformArguments(), - ['MEMORY', 'MALLOC-STATS'] - ); - }); + it('transformArguments', () => { + assert.deepEqual( + MEMORY_MALLOC_STATS.transformArguments(), + ['MEMORY', 'MALLOC-STATS'] + ); + }); - testUtils.testWithClient('client.memoryMallocStats', async client => { - assert.equal( - typeof (await client.memoryDoctor()), - 'string' - ); - }, GLOBAL.SERVERS.OPEN); + testUtils.testWithClient('client.memoryMallocStats', async client => { + assert.equal( + typeof (await client.memoryMallocStats()), + 'string' + ); + }, GLOBAL.SERVERS.OPEN); }); diff --git a/packages/client/lib/commands/MEMORY_MALLOC-STATS.ts b/packages/client/lib/commands/MEMORY_MALLOC-STATS.ts index 3977e3a1de4..31b01a49b5c 100644 --- a/packages/client/lib/commands/MEMORY_MALLOC-STATS.ts +++ b/packages/client/lib/commands/MEMORY_MALLOC-STATS.ts @@ -1,5 +1,11 @@ -export function transformArguments(): Array { +import { BlobStringReply, Command } from '../RESP/types'; + +export default { + FIRST_KEY_INDEX: undefined, + IS_READ_ONLY: true, + transformArguments() { return ['MEMORY', 'MALLOC-STATS']; -} + }, + transformReply: undefined as unknown as () => BlobStringReply +} as const satisfies Command; -export declare function transformReply(): string; diff --git a/packages/client/lib/commands/MEMORY_PURGE.spec.ts b/packages/client/lib/commands/MEMORY_PURGE.spec.ts index 5d34331feb6..41b01f87c4b 100644 --- a/packages/client/lib/commands/MEMORY_PURGE.spec.ts +++ b/packages/client/lib/commands/MEMORY_PURGE.spec.ts @@ -1,19 +1,19 @@ -import { strict as assert } from 'assert'; +import { strict as assert } from 'node:assert'; import testUtils, { GLOBAL } from '../test-utils'; -import { transformArguments } from './MEMORY_PURGE'; +import MEMORY_PURGE from './MEMORY_PURGE'; describe('MEMORY PURGE', () => { - it('transformArguments', () => { - assert.deepEqual( - transformArguments(), - ['MEMORY', 'PURGE'] - ); - }); + it('transformArguments', () => { + assert.deepEqual( + MEMORY_PURGE.transformArguments(), + ['MEMORY', 'PURGE'] + ); + }); - testUtils.testWithClient('client.memoryPurge', async client => { - assert.equal( - await client.memoryPurge(), - 'OK' - ); - }, GLOBAL.SERVERS.OPEN); + testUtils.testWithClient('client.memoryPurge', async client => { + assert.equal( + await client.memoryPurge(), + 'OK' + ); + }, GLOBAL.SERVERS.OPEN); }); diff --git a/packages/client/lib/commands/MEMORY_PURGE.ts b/packages/client/lib/commands/MEMORY_PURGE.ts index cfa38179273..b4c48d40102 100644 --- a/packages/client/lib/commands/MEMORY_PURGE.ts +++ b/packages/client/lib/commands/MEMORY_PURGE.ts @@ -1,5 +1,11 @@ -export function transformArguments(): Array { +import { SimpleStringReply, Command } from '../RESP/types'; + +export default { + FIRST_KEY_INDEX: undefined, + IS_READ_ONLY: false, + transformArguments() { return ['MEMORY', 'PURGE']; -} + }, + transformReply: undefined as unknown as () => SimpleStringReply<'OK'> +} as const satisfies Command; -export declare function transformReply(): string; diff --git a/packages/client/lib/commands/MEMORY_STATS.spec.ts b/packages/client/lib/commands/MEMORY_STATS.spec.ts index 12aa21181e6..6d5f5b8690b 100644 --- a/packages/client/lib/commands/MEMORY_STATS.spec.ts +++ b/packages/client/lib/commands/MEMORY_STATS.spec.ts @@ -1,108 +1,46 @@ -import { strict as assert } from 'assert'; -import { transformArguments, transformReply } from './MEMORY_STATS'; +import { strict as assert } from 'node:assert'; +import testUtils, { GLOBAL } from '../test-utils'; +import MEMORY_STATS from './MEMORY_STATS'; describe('MEMORY STATS', () => { - it('transformArguments', () => { - assert.deepEqual( - transformArguments(), - ['MEMORY', 'STATS'] - ); - }); + it('transformArguments', () => { + assert.deepEqual( + MEMORY_STATS.transformArguments(), + ['MEMORY', 'STATS'] + ); + }); - it('transformReply', () => { - assert.deepEqual( - transformReply([ - 'peak.allocated', - 952728, - 'total.allocated', - 892904, - 'startup.allocated', - 809952, - 'replication.backlog', - 0, - 'clients.slaves', - 0, - 'clients.normal', - 41000, - 'aof.buffer', - 0, - 'lua.caches', - 0, - 'db.0', - [ - 'overhead.hashtable.main', - 72, - 'overhead.hashtable.expires', - 0 - ], - 'overhead.total', - 850952, - 'keys.count', - 0, - 'keys.bytes-per-key', - 0, - 'dataset.bytes', - 41952, - 'dataset.percentage', - '50.573825836181641', - 'peak.percentage', - '93.720771789550781', - 'allocator.allocated', - 937632, - 'allocator.active', - 1191936, - 'allocator.resident', - 4005888, - 'allocator-fragmentation.ratio', - '1.2712193727493286', - 'allocator-fragmentation.bytes', - 254304, - 'allocator-rss.ratio', - '3.3608248233795166', - 'allocator-rss.bytes', - 2813952, - 'rss-overhead.ratio', - '2.4488751888275146', - 'rss-overhead.bytes', - 5804032, - 'fragmentation', - '11.515504837036133', - 'fragmentation.bytes', - 8958032 - ]), - { - peakAllocated: 952728, - totalAllocated: 892904, - startupAllocated: 809952, - replicationBacklog: 0, - clientsReplicas: 0, - clientsNormal: 41000, - aofBuffer: 0, - luaCaches: 0, - overheadTotal: 850952, - keysCount: 0, - keysBytesPerKey: 0, - datasetBytes: 41952, - datasetPercentage: 50.573825836181641, - peakPercentage: 93.720771789550781, - allocatorAllocated: 937632, - allocatorActive: 1191936, - allocatorResident: 4005888, - allocatorFragmentationRatio: 1.2712193727493286, - allocatorFragmentationBytes: 254304, - allocatorRssRatio: 3.3608248233795166, - allocatorRssBytes: 2813952, - rssOverheadRatio: 2.4488751888275146, - rssOverheadBytes: 5804032, - fragmentation: 11.515504837036133, - fragmentationBytes: 8958032, - db: { - 0: { - overheadHashtableMain: 72, - overheadHashtableExpires: 0 - } - } - } - ); - }); + testUtils.testWithClient('client.memoryStats', async client => { + const memoryStats = await client.memoryStats(); + assert.equal(typeof memoryStats['peak.allocated'], 'number'); + assert.equal(typeof memoryStats['total.allocated'], 'number'); + assert.equal(typeof memoryStats['startup.allocated'], 'number'); + assert.equal(typeof memoryStats['replication.backlog'], 'number'); + assert.equal(typeof memoryStats['clients.slaves'], 'number'); + assert.equal(typeof memoryStats['clients.normal'], 'number'); + assert.equal(typeof memoryStats['aof.buffer'], 'number'); + assert.equal(typeof memoryStats['lua.caches'], 'number'); + assert.equal(typeof memoryStats['overhead.total'], 'number'); + assert.equal(typeof memoryStats['keys.count'], 'number'); + assert.equal(typeof memoryStats['keys.bytes-per-key'], 'number'); + assert.equal(typeof memoryStats['dataset.bytes'], 'number'); + assert.equal(typeof memoryStats['dataset.percentage'], 'number'); + assert.equal(typeof memoryStats['peak.percentage'], 'number'); + assert.equal(typeof memoryStats['allocator.allocated'], 'number'); + assert.equal(typeof memoryStats['allocator.active'], 'number'); + assert.equal(typeof memoryStats['allocator.resident'], 'number'); + assert.equal(typeof memoryStats['allocator-fragmentation.ratio'], 'number', 'allocator-fragmentation.ratio'); + assert.equal(typeof memoryStats['allocator-fragmentation.bytes'], 'number'); + assert.equal(typeof memoryStats['allocator-rss.ratio'], 'number', 'allocator-rss.ratio'); + assert.equal(typeof memoryStats['allocator-rss.bytes'], 'number'); + assert.equal(typeof memoryStats['rss-overhead.ratio'], 'number', 'rss-overhead.ratio'); + assert.equal(typeof memoryStats['rss-overhead.bytes'], 'number'); + assert.equal(typeof memoryStats['fragmentation'], 'number', 'fragmentation'); + assert.equal(typeof memoryStats['fragmentation.bytes'], 'number'); + + if (testUtils.isVersionGreaterThan([7])) { + assert.equal(typeof memoryStats['cluster.links'], 'number'); + assert.equal(typeof memoryStats['functions.caches'], 'number'); + } + }, GLOBAL.SERVERS.OPEN); }); diff --git a/packages/client/lib/commands/MEMORY_STATS.ts b/packages/client/lib/commands/MEMORY_STATS.ts index 8ae83d2239a..f38a0e5f29b 100644 --- a/packages/client/lib/commands/MEMORY_STATS.ts +++ b/packages/client/lib/commands/MEMORY_STATS.ts @@ -1,93 +1,68 @@ -export function transformArguments(): Array { - return ['MEMORY', 'STATS']; -} - -interface MemoryStatsReply { - peakAllocated: number; - totalAllocated: number; - startupAllocated: number; - replicationBacklog: number; - clientsReplicas: number; - clientsNormal: number; - aofBuffer: number; - luaCaches: number; - overheadTotal: number; - keysCount: number; - keysBytesPerKey: number; - datasetBytes: number; - datasetPercentage: number; - peakPercentage: number; - allocatorAllocated?: number, - allocatorActive?: number; - allocatorResident?: number; - allocatorFragmentationRatio?: number; - allocatorFragmentationBytes?: number; - allocatorRssRatio?: number; - allocatorRssBytes?: number; - rssOverheadRatio?: number; - rssOverheadBytes?: number; - fragmentation?: number; - fragmentationBytes: number; - db: { - [key: number]: { - overheadHashtableMain: number; - overheadHashtableExpires: number; - }; - }; -} - -const FIELDS_MAPPING = { - 'peak.allocated': 'peakAllocated', - 'total.allocated': 'totalAllocated', - 'startup.allocated': 'startupAllocated', - 'replication.backlog': 'replicationBacklog', - 'clients.slaves': 'clientsReplicas', - 'clients.normal': 'clientsNormal', - 'aof.buffer': 'aofBuffer', - 'lua.caches': 'luaCaches', - 'overhead.total': 'overheadTotal', - 'keys.count': 'keysCount', - 'keys.bytes-per-key': 'keysBytesPerKey', - 'dataset.bytes': 'datasetBytes', - 'dataset.percentage': 'datasetPercentage', - 'peak.percentage': 'peakPercentage', - 'allocator.allocated': 'allocatorAllocated', - 'allocator.active': 'allocatorActive', - 'allocator.resident': 'allocatorResident', - 'allocator-fragmentation.ratio': 'allocatorFragmentationRatio', - 'allocator-fragmentation.bytes': 'allocatorFragmentationBytes', - 'allocator-rss.ratio': 'allocatorRssRatio', - 'allocator-rss.bytes': 'allocatorRssBytes', - 'rss-overhead.ratio': 'rssOverheadRatio', - 'rss-overhead.bytes': 'rssOverheadBytes', - 'fragmentation': 'fragmentation', - 'fragmentation.bytes': 'fragmentationBytes' - }, - DB_FIELDS_MAPPING = { - 'overhead.hashtable.main': 'overheadHashtableMain', - 'overhead.hashtable.expires': 'overheadHashtableExpires' - }; +import { TuplesToMapReply, BlobStringReply, NumberReply, DoubleReply, ArrayReply, UnwrapReply, Command, TypeMapping } from '../RESP/types'; +import { transformDoubleReply } from './generic-transformers'; -export function transformReply(rawReply: Array>): MemoryStatsReply { - const reply: any = { - db: {} - }; +export type MemoryStatsReply = TuplesToMapReply<[ + [BlobStringReply<'peak.allocated'>, NumberReply], + [BlobStringReply<'total.allocated'>, NumberReply], + [BlobStringReply<'startup.allocated'>, NumberReply], + [BlobStringReply<'replication.backlog'>, NumberReply], + [BlobStringReply<'clients.slaves'>, NumberReply], + [BlobStringReply<'clients.normal'>, NumberReply], + /** added in 7.0 */ + [BlobStringReply<'cluster.links'>, NumberReply], + [BlobStringReply<'aof.buffer'>, NumberReply], + [BlobStringReply<'lua.caches'>, NumberReply], + /** added in 7.0 */ + [BlobStringReply<'functions.caches'>, NumberReply], + // FIXME: 'db.0', and perhaps others' is here and is a map that should be handled? + [BlobStringReply<'overhead.total'>, NumberReply], + [BlobStringReply<'keys.count'>, NumberReply], + [BlobStringReply<'keys.bytes-per-key'>, NumberReply], + [BlobStringReply<'dataset.bytes'>, NumberReply], + [BlobStringReply<'dataset.percentage'>, DoubleReply], + [BlobStringReply<'peak.percentage'>, DoubleReply], + [BlobStringReply<'allocator.allocated'>, NumberReply], + [BlobStringReply<'allocator.active'>, NumberReply], + [BlobStringReply<'allocator.resident'>, NumberReply], + [BlobStringReply<'allocator-fragmentation.ratio'>, DoubleReply], + [BlobStringReply<'allocator-fragmentation.bytes'>, NumberReply], + [BlobStringReply<'allocator-rss.ratio'>, DoubleReply], + [BlobStringReply<'allocator-rss.bytes'>, NumberReply], + [BlobStringReply<'rss-overhead.ratio'>, DoubleReply], + [BlobStringReply<'rss-overhead.bytes'>, NumberReply], + [BlobStringReply<'fragmentation'>, DoubleReply], + [BlobStringReply<'fragmentation.bytes'>, NumberReply] +]>; - for (let i = 0; i < rawReply.length; i += 2) { - const key = rawReply[i] as string; - if (key.startsWith('db.')) { - const dbTuples = rawReply[i + 1] as Array, - db: any = {}; - for (let j = 0; j < dbTuples.length; j += 2) { - db[DB_FIELDS_MAPPING[dbTuples[j] as keyof typeof DB_FIELDS_MAPPING]] = dbTuples[j + 1]; - } +export default { + FIRST_KEY_INDEX: undefined, + IS_READ_ONLY: true, + transformArguments() { + return ['MEMORY', 'STATS']; + }, + transformReply: { + 2: (rawReply: UnwrapReply>, preserve?: any, typeMapping?: TypeMapping) => { + const reply: any = {}; - reply.db[key.substring(3)] = db; - continue; + let i = 0; + while (i < rawReply.length) { + switch(rawReply[i].toString()) { + case 'dataset.percentage': + case 'peak.percentage': + case 'allocator-fragmentation.ratio': + case 'allocator-rss.ratio': + case 'rss-overhead.ratio': + case 'fragmentation': + reply[rawReply[i++] as any] = transformDoubleReply[2](rawReply[i++] as unknown as BlobStringReply, preserve, typeMapping); + break; + default: + reply[rawReply[i++] as any] = rawReply[i++]; } + + } - reply[FIELDS_MAPPING[key as keyof typeof FIELDS_MAPPING]] = Number(rawReply[i + 1]); - } - - return reply as MemoryStatsReply; -} + return reply as MemoryStatsReply; + }, + 3: undefined as unknown as () => MemoryStatsReply + } +} as const satisfies Command; diff --git a/packages/client/lib/commands/MEMORY_USAGE.spec.ts b/packages/client/lib/commands/MEMORY_USAGE.spec.ts index fe5ff404d93..250a6884259 100644 --- a/packages/client/lib/commands/MEMORY_USAGE.spec.ts +++ b/packages/client/lib/commands/MEMORY_USAGE.spec.ts @@ -1,30 +1,30 @@ -import { strict as assert } from 'assert'; +import { strict as assert } from 'node:assert'; import testUtils, { GLOBAL } from '../test-utils'; -import { transformArguments } from './MEMORY_USAGE'; +import MEMORY_USAGE from './MEMORY_USAGE'; describe('MEMORY USAGE', () => { - describe('transformArguments', () => { - it('simple', () => { - assert.deepEqual( - transformArguments('key'), - ['MEMORY', 'USAGE', 'key'] - ); - }); + describe('transformArguments', () => { + it('simple', () => { + assert.deepEqual( + MEMORY_USAGE.transformArguments('key'), + ['MEMORY', 'USAGE', 'key'] + ); + }); - it('with SAMPLES', () => { - assert.deepEqual( - transformArguments('key', { - SAMPLES: 1 - }), - ['MEMORY', 'USAGE', 'key', 'SAMPLES', '1'] - ); - }); + it('with SAMPLES', () => { + assert.deepEqual( + MEMORY_USAGE.transformArguments('key', { + SAMPLES: 1 + }), + ['MEMORY', 'USAGE', 'key', 'SAMPLES', '1'] + ); }); + }); - testUtils.testWithClient('client.memoryUsage', async client => { - assert.equal( - await client.memoryUsage('key'), - null - ); - }, GLOBAL.SERVERS.OPEN); + testUtils.testWithClient('client.memoryUsage', async client => { + assert.equal( + await client.memoryUsage('key'), + null + ); + }, GLOBAL.SERVERS.OPEN); }); diff --git a/packages/client/lib/commands/MEMORY_USAGE.ts b/packages/client/lib/commands/MEMORY_USAGE.ts index 959cdb0a0c4..78b079c2c13 100644 --- a/packages/client/lib/commands/MEMORY_USAGE.ts +++ b/packages/client/lib/commands/MEMORY_USAGE.ts @@ -1,19 +1,20 @@ -export const FIRST_KEY_INDEX = 1; +import { NumberReply, NullReply, Command, RedisArgument } from '../RESP/types'; -export const IS_READ_ONLY = true; - -interface MemoryUsageOptions { - SAMPLES?: number; +export interface MemoryUsageOptions { + SAMPLES?: number; } -export function transformArguments(key: string, options?: MemoryUsageOptions): Array { +export default { + FIRST_KEY_INDEX: 1, + IS_READ_ONLY: true, + transformArguments(key: RedisArgument, options?: MemoryUsageOptions) { const args = ['MEMORY', 'USAGE', key]; if (options?.SAMPLES) { - args.push('SAMPLES', options.SAMPLES.toString()); + args.push('SAMPLES', options.SAMPLES.toString()); } - + return args; -} - -export declare function transformReply(): number | null; + }, + transformReply: undefined as unknown as () => NumberReply | NullReply +} as const satisfies Command; diff --git a/packages/client/lib/commands/MGET.spec.ts b/packages/client/lib/commands/MGET.spec.ts index 9ff47895f4e..296702a1a03 100644 --- a/packages/client/lib/commands/MGET.spec.ts +++ b/packages/client/lib/commands/MGET.spec.ts @@ -1,26 +1,22 @@ -import { strict as assert } from 'assert'; +import { strict as assert } from 'node:assert'; import testUtils, { GLOBAL } from '../test-utils'; -import { transformArguments } from './MGET'; +import MGET from './MGET'; describe('MGET', () => { - it('transformArguments', () => { - assert.deepEqual( - transformArguments(['1', '2']), - ['MGET', '1', '2'] - ); - }); + it('transformArguments', () => { + assert.deepEqual( + MGET.transformArguments(['1', '2']), + ['MGET', '1', '2'] + ); + }); - testUtils.testWithClient('client.mGet', async client => { - assert.deepEqual( - await client.mGet(['key']), - [null] - ); - }, GLOBAL.SERVERS.OPEN); - - testUtils.testWithCluster('cluster.mGet', async cluster => { - assert.deepEqual( - await cluster.mGet(['key']), - [null] - ); - }, GLOBAL.CLUSTERS.OPEN); + testUtils.testAll('mGet', async client => { + assert.deepEqual( + await client.mGet(['key']), + [null] + ); + }, { + client: GLOBAL.SERVERS.OPEN, + cluster: GLOBAL.CLUSTERS.OPEN + }); }); diff --git a/packages/client/lib/commands/MGET.ts b/packages/client/lib/commands/MGET.ts index 6635a2ca20c..0f0f9e52ffb 100644 --- a/packages/client/lib/commands/MGET.ts +++ b/packages/client/lib/commands/MGET.ts @@ -1,13 +1,10 @@ -import { RedisCommandArgument, RedisCommandArguments } from '.'; +import { RedisArgument, BlobStringReply, NullReply, Command } from '../RESP/types'; -export const FIRST_KEY_INDEX = 1; - -export const IS_READ_ONLY = true; - -export function transformArguments( - keys: Array -): RedisCommandArguments { +export default { + FIRST_KEY_INDEX: 1, + IS_READ_ONLY: true, + transformArguments(keys: Array) { return ['MGET', ...keys]; -} - -export declare function transformReply(): Array; + }, + transformReply: undefined as unknown as () => Array +} as const satisfies Command; diff --git a/packages/client/lib/commands/MIGRATE.spec.ts b/packages/client/lib/commands/MIGRATE.spec.ts index ca7ceb48b39..880d59a09c7 100644 --- a/packages/client/lib/commands/MIGRATE.spec.ts +++ b/packages/client/lib/commands/MIGRATE.spec.ts @@ -1,76 +1,76 @@ -import { strict as assert } from 'assert'; -import { transformArguments } from './MIGRATE'; +import { strict as assert } from 'node:assert'; +import MIGRATE from './MIGRATE'; describe('MIGRATE', () => { - describe('transformArguments', () => { - it('single key', () => { - assert.deepEqual( - transformArguments('127.0.0.1', 6379, 'key', 0, 10), - ['MIGRATE', '127.0.0.1', '6379', 'key', '0', '10'] - ); - }); + describe('transformArguments', () => { + it('single key', () => { + assert.deepEqual( + MIGRATE.transformArguments('127.0.0.1', 6379, 'key', 0, 10), + ['MIGRATE', '127.0.0.1', '6379', 'key', '0', '10'] + ); + }); + + it('multiple keys', () => { + assert.deepEqual( + MIGRATE.transformArguments('127.0.0.1', 6379, ['1', '2'], 0, 10), + ['MIGRATE', '127.0.0.1', '6379', '', '0', '10', 'KEYS', '1', '2'] + ); + }); - it('multiple keys', () => { - assert.deepEqual( - transformArguments('127.0.0.1', 6379, ['1', '2'], 0, 10), - ['MIGRATE', '127.0.0.1', '6379', '', '0', '10', 'KEYS', '1', '2'] - ); - }); + it('with COPY', () => { + assert.deepEqual( + MIGRATE.transformArguments('127.0.0.1', 6379, 'key', 0, 10, { + COPY: true + }), + ['MIGRATE', '127.0.0.1', '6379', 'key', '0', '10', 'COPY'] + ); + }); - it('with COPY', () => { - assert.deepEqual( - transformArguments('127.0.0.1', 6379, 'key', 0, 10, { - COPY: true - }), - ['MIGRATE', '127.0.0.1', '6379', 'key', '0', '10', 'COPY'] - ); - }); + it('with REPLACE', () => { + assert.deepEqual( + MIGRATE.transformArguments('127.0.0.1', 6379, 'key', 0, 10, { + REPLACE: true + }), + ['MIGRATE', '127.0.0.1', '6379', 'key', '0', '10', 'REPLACE'] + ); + }); - it('with REPLACE', () => { - assert.deepEqual( - transformArguments('127.0.0.1', 6379, 'key', 0, 10, { - REPLACE: true - }), - ['MIGRATE', '127.0.0.1', '6379', 'key', '0', '10', 'REPLACE'] - ); - }); - - describe('with AUTH', () => { - it('password only', () => { - assert.deepEqual( - transformArguments('127.0.0.1', 6379, 'key', 0, 10, { - AUTH: { - password: 'password' - } - }), - ['MIGRATE', '127.0.0.1', '6379', 'key', '0', '10', 'AUTH', 'password'] - ); - }); + describe('with AUTH', () => { + it('password only', () => { + assert.deepEqual( + MIGRATE.transformArguments('127.0.0.1', 6379, 'key', 0, 10, { + AUTH: { + password: 'password' + } + }), + ['MIGRATE', '127.0.0.1', '6379', 'key', '0', '10', 'AUTH', 'password'] + ); + }); - it('username & password', () => { - assert.deepEqual( - transformArguments('127.0.0.1', 6379, 'key', 0, 10, { - AUTH: { - username: 'username', - password: 'password' - } - }), - ['MIGRATE', '127.0.0.1', '6379', 'key', '0', '10', 'AUTH2', 'username', 'password'] - ); - }); - }); + it('username & password', () => { + assert.deepEqual( + MIGRATE.transformArguments('127.0.0.1', 6379, 'key', 0, 10, { + AUTH: { + username: 'username', + password: 'password' + } + }), + ['MIGRATE', '127.0.0.1', '6379', 'key', '0', '10', 'AUTH2', 'username', 'password'] + ); + }); + }); - it('with COPY, REPLACE, AUTH', () => { - assert.deepEqual( - transformArguments('127.0.0.1', 6379, 'key', 0, 10, { - COPY: true, - REPLACE: true, - AUTH: { - password: 'password' - } - }), - ['MIGRATE', '127.0.0.1', '6379', 'key', '0', '10', 'COPY', 'REPLACE', 'AUTH', 'password'] - ); - }); + it('with COPY, REPLACE, AUTH', () => { + assert.deepEqual( + MIGRATE.transformArguments('127.0.0.1', 6379, 'key', 0, 10, { + COPY: true, + REPLACE: true, + AUTH: { + password: 'password' + } + }), + ['MIGRATE', '127.0.0.1', '6379', 'key', '0', '10', 'COPY', 'REPLACE', 'AUTH', 'password'] + ); }); + }); }); diff --git a/packages/client/lib/commands/MIGRATE.ts b/packages/client/lib/commands/MIGRATE.ts index aaff3164081..4821f93fcfb 100644 --- a/packages/client/lib/commands/MIGRATE.ts +++ b/packages/client/lib/commands/MIGRATE.ts @@ -1,65 +1,67 @@ -import { RedisCommandArgument, RedisCommandArguments } from '.'; +import { RedisArgument, SimpleStringReply, Command } from '../RESP/types'; import { AuthOptions } from './AUTH'; -interface MigrateOptions { - COPY?: true; - REPLACE?: true; - AUTH?: AuthOptions; +export interface MigrateOptions { + COPY?: true; + REPLACE?: true; + AUTH?: AuthOptions; } -export function transformArguments( - host: RedisCommandArgument, +export default { + IS_READ_ONLY: false, + transformArguments( + host: RedisArgument, port: number, - key: RedisCommandArgument | Array, + key: RedisArgument | Array, destinationDb: number, timeout: number, options?: MigrateOptions -): RedisCommandArguments { + ) { const args = ['MIGRATE', host, port.toString()], - isKeyArray = Array.isArray(key); - + isKeyArray = Array.isArray(key); + if (isKeyArray) { - args.push(''); + args.push(''); } else { - args.push(key); + args.push(key); } - + args.push( - destinationDb.toString(), - timeout.toString() + destinationDb.toString(), + timeout.toString() ); - + if (options?.COPY) { - args.push('COPY'); + args.push('COPY'); } - + if (options?.REPLACE) { - args.push('REPLACE'); + args.push('REPLACE'); } - + if (options?.AUTH) { - if (options.AUTH.username) { - args.push( - 'AUTH2', - options.AUTH.username, - options.AUTH.password - ); - } else { - args.push( - 'AUTH', - options.AUTH.password - ); - } - } - - if (isKeyArray) { + if (options.AUTH.username) { args.push( - 'KEYS', - ...key + 'AUTH2', + options.AUTH.username, + options.AUTH.password ); + } else { + args.push( + 'AUTH', + options.AUTH.password + ); + } } - + + if (isKeyArray) { + args.push( + 'KEYS', + ...key + ); + } + return args; -} - -export declare function transformReply(): string; + }, + transformReply: undefined as unknown as () => SimpleStringReply<'OK'> +} as const satisfies Command; diff --git a/packages/client/lib/commands/MODULE_LIST.spec.ts b/packages/client/lib/commands/MODULE_LIST.spec.ts index eeeb774ebff..9c1d3874381 100644 --- a/packages/client/lib/commands/MODULE_LIST.spec.ts +++ b/packages/client/lib/commands/MODULE_LIST.spec.ts @@ -1,11 +1,11 @@ -import { strict as assert } from 'assert'; -import { transformArguments } from './MODULE_LIST'; +import { strict as assert } from 'node:assert'; +import MODULE_LIST from './MODULE_LIST'; describe('MODULE LIST', () => { - it('transformArguments', () => { - assert.deepEqual( - transformArguments(), - ['MODULE', 'LIST'] - ); - }); + it('transformArguments', () => { + assert.deepEqual( + MODULE_LIST.transformArguments(), + ['MODULE', 'LIST'] + ); + }); }); diff --git a/packages/client/lib/commands/MODULE_LIST.ts b/packages/client/lib/commands/MODULE_LIST.ts index d75b2428308..5ddd4e91ff6 100644 --- a/packages/client/lib/commands/MODULE_LIST.ts +++ b/packages/client/lib/commands/MODULE_LIST.ts @@ -1,5 +1,26 @@ -export function transformArguments(): Array { - return ['MODULE', 'LIST']; -} +import { ArrayReply, TuplesToMapReply, BlobStringReply, NumberReply, UnwrapReply, Resp2Reply, Command } from '../RESP/types'; + +export type ModuleListReply = ArrayReply, BlobStringReply], + [BlobStringReply<'ver'>, NumberReply], +]>>; -export declare function transformReply(): string; +export default { + FIRST_KEY_INDEX: undefined, + IS_READ_ONLY: true, + transformArguments() { + return ['MODULE', 'LIST']; + }, + transformReply: { + 2: (reply: UnwrapReply>) => { + return reply.map(module => { + const unwrapped = module as unknown as UnwrapReply; + return { + name: unwrapped[1], + ver: unwrapped[3] + }; + }); + }, + 3: undefined as unknown as () => ModuleListReply + } +} as const satisfies Command; diff --git a/packages/client/lib/commands/MODULE_LOAD.spec.ts b/packages/client/lib/commands/MODULE_LOAD.spec.ts index 5a99a232ca4..3b7b9dd00a3 100644 --- a/packages/client/lib/commands/MODULE_LOAD.spec.ts +++ b/packages/client/lib/commands/MODULE_LOAD.spec.ts @@ -1,20 +1,20 @@ -import { strict as assert } from 'assert'; -import { transformArguments } from './MODULE_LOAD'; +import { strict as assert } from 'node:assert'; +import MODULE_LOAD from './MODULE_LOAD'; describe('MODULE LOAD', () => { - describe('transformArguments', () => { - it('simple', () => { - assert.deepEqual( - transformArguments('path'), - ['MODULE', 'LOAD', 'path'] - ); - }); + describe('transformArguments', () => { + it('simple', () => { + assert.deepEqual( + MODULE_LOAD.transformArguments('path'), + ['MODULE', 'LOAD', 'path'] + ); + }); - it('with module args', () => { - assert.deepEqual( - transformArguments('path', ['1', '2']), - ['MODULE', 'LOAD', 'path', '1', '2'] - ); - }); + it('with module args', () => { + assert.deepEqual( + MODULE_LOAD.transformArguments('path', ['1', '2']), + ['MODULE', 'LOAD', 'path', '1', '2'] + ); }); + }); }); diff --git a/packages/client/lib/commands/MODULE_LOAD.ts b/packages/client/lib/commands/MODULE_LOAD.ts index b44b4b57ce6..82c5b81a905 100644 --- a/packages/client/lib/commands/MODULE_LOAD.ts +++ b/packages/client/lib/commands/MODULE_LOAD.ts @@ -1,11 +1,16 @@ -export function transformArguments(path: string, moduleArgs?: Array): Array { +import { RedisArgument, SimpleStringReply, Command } from '../RESP/types'; + +export default { + FIRST_KEY_INDEX: undefined, + IS_READ_ONLY: true, + transformArguments(path: RedisArgument, moduleArguments?: Array) { const args = ['MODULE', 'LOAD', path]; - if (moduleArgs) { - args.push(...moduleArgs); + if (moduleArguments) { + return args.concat(moduleArguments); } - + return args; -} - -export declare function transformReply(): string; + }, + transformReply: undefined as unknown as () => SimpleStringReply<'OK'> +} as const satisfies Command; diff --git a/packages/client/lib/commands/MODULE_UNLOAD.spec.ts b/packages/client/lib/commands/MODULE_UNLOAD.spec.ts index d8af96c54f1..e33fb3a5f4f 100644 --- a/packages/client/lib/commands/MODULE_UNLOAD.spec.ts +++ b/packages/client/lib/commands/MODULE_UNLOAD.spec.ts @@ -1,11 +1,11 @@ -import { strict as assert } from 'assert'; -import { transformArguments } from './MODULE_UNLOAD'; +import { strict as assert } from 'node:assert'; +import MODULE_UNLOAD from './MODULE_UNLOAD'; describe('MODULE UNLOAD', () => { - it('transformArguments', () => { - assert.deepEqual( - transformArguments('name'), - ['MODULE', 'UNLOAD', 'name'] - ); - }); + it('transformArguments', () => { + assert.deepEqual( + MODULE_UNLOAD.transformArguments('name'), + ['MODULE', 'UNLOAD', 'name'] + ); + }); }); diff --git a/packages/client/lib/commands/MODULE_UNLOAD.ts b/packages/client/lib/commands/MODULE_UNLOAD.ts index d5927778fe6..3a2bc05c0e7 100644 --- a/packages/client/lib/commands/MODULE_UNLOAD.ts +++ b/packages/client/lib/commands/MODULE_UNLOAD.ts @@ -1,5 +1,10 @@ -export function transformArguments(name: string): Array { - return ['MODULE', 'UNLOAD', name]; -} +import { RedisArgument, SimpleStringReply, Command } from '../RESP/types'; -export declare function transformReply(): string; +export default { + FIRST_KEY_INDEX: undefined, + IS_READ_ONLY: true, + transformArguments(name: RedisArgument) { + return ['MODULE', 'UNLOAD', name]; + }, + transformReply: undefined as unknown as () => SimpleStringReply<'OK'> +} as const satisfies Command; diff --git a/packages/client/lib/commands/MOVE.spec.ts b/packages/client/lib/commands/MOVE.spec.ts index f7fdc481cbf..e767568e72b 100644 --- a/packages/client/lib/commands/MOVE.spec.ts +++ b/packages/client/lib/commands/MOVE.spec.ts @@ -1,19 +1,19 @@ -import { strict as assert } from 'assert'; +import { strict as assert } from 'node:assert'; import testUtils, { GLOBAL } from '../test-utils'; -import { transformArguments } from './MOVE'; +import MOVE from './MOVE'; describe('MOVE', () => { - it('transformArguments', () => { - assert.deepEqual( - transformArguments('key', 1), - ['MOVE', 'key', '1'] - ); - }); + it('transformArguments', () => { + assert.deepEqual( + MOVE.transformArguments('key', 1), + ['MOVE', 'key', '1'] + ); + }); - testUtils.testWithClient('client.move', async client => { - assert.equal( - await client.move('key', 1), - false - ); - }, GLOBAL.SERVERS.OPEN); + testUtils.testWithClient('client.move', async client => { + assert.equal( + await client.move('key', 1), + 0 + ); + }, GLOBAL.SERVERS.OPEN); }); diff --git a/packages/client/lib/commands/MOVE.ts b/packages/client/lib/commands/MOVE.ts index 17cc6742c5f..60aac4b1457 100644 --- a/packages/client/lib/commands/MOVE.ts +++ b/packages/client/lib/commands/MOVE.ts @@ -1,7 +1,9 @@ -export const FIRST_KEY_INDEX = 1; +import { RedisArgument, NumberReply, Command } from '../RESP/types'; -export function transformArguments(key: string, db: number): Array { +export default { + FIRST_KEY_INDEX: 1, + transformArguments(key: RedisArgument, db: number) { return ['MOVE', key, db.toString()]; -} - -export { transformBooleanReply as transformReply } from './generic-transformers'; + }, + transformReply: undefined as unknown as () => NumberReply +} as const satisfies Command; diff --git a/packages/client/lib/commands/MSET.spec.ts b/packages/client/lib/commands/MSET.spec.ts index 0568f38487e..5435e283108 100644 --- a/packages/client/lib/commands/MSET.spec.ts +++ b/packages/client/lib/commands/MSET.spec.ts @@ -1,42 +1,38 @@ -import { strict as assert } from 'assert'; +import { strict as assert } from 'node:assert'; import testUtils, { GLOBAL } from '../test-utils'; -import { transformArguments } from './MSET'; +import MSET from './MSET'; describe('MSET', () => { - describe('transformArguments', () => { - it("['key1', 'value1', 'key2', 'value2']", () => { - assert.deepEqual( - transformArguments(['key1', 'value1', 'key2', 'value2']), - ['MSET', 'key1', 'value1', 'key2', 'value2'] - ); - }); - - it("[['key1', 'value1'], ['key2', 'value2']]", () => { - assert.deepEqual( - transformArguments([['key1', 'value1'], ['key2', 'value2']]), - ['MSET', 'key1', 'value1', 'key2', 'value2'] - ); - }); + describe('transformArguments', () => { + it("['key1', 'value1', 'key2', 'value2']", () => { + assert.deepEqual( + MSET.transformArguments(['key1', 'value1', 'key2', 'value2']), + ['MSET', 'key1', 'value1', 'key2', 'value2'] + ); + }); - it("{key1: 'value1'. key2: 'value2'}", () => { - assert.deepEqual( - transformArguments({ key1: 'value1', key2: 'value2' }), - ['MSET', 'key1', 'value1', 'key2', 'value2'] - ); - }); + it("[['key1', 'value1'], ['key2', 'value2']]", () => { + assert.deepEqual( + MSET.transformArguments([['key1', 'value1'], ['key2', 'value2']]), + ['MSET', 'key1', 'value1', 'key2', 'value2'] + ); }); - testUtils.testWithClient('client.mSet', async client => { - assert.equal( - await client.mSet(['key1', 'value1', 'key2', 'value2']), - 'OK' - ); - }, GLOBAL.SERVERS.OPEN); + it("{key1: 'value1'. key2: 'value2'}", () => { + assert.deepEqual( + MSET.transformArguments({ key1: 'value1', key2: 'value2' }), + ['MSET', 'key1', 'value1', 'key2', 'value2'] + ); + }); + }); - testUtils.testWithCluster('cluster.mSet', async cluster => { - assert.equal( - await cluster.mSet(['{key}1', 'value1', '{key}2', 'value2']), - 'OK' - ); - }, GLOBAL.CLUSTERS.OPEN); + testUtils.testAll('mSet', async client => { + assert.equal( + await client.mSet(['{tag}key1', 'value1', '{tag}key2', 'value2']), + 'OK' + ); + }, { + client: GLOBAL.SERVERS.OPEN, + cluster: GLOBAL.CLUSTERS.OPEN + }); }); diff --git a/packages/client/lib/commands/MSET.ts b/packages/client/lib/commands/MSET.ts index bd7111659d1..136fde3e7f7 100644 --- a/packages/client/lib/commands/MSET.ts +++ b/packages/client/lib/commands/MSET.ts @@ -1,24 +1,27 @@ -import { RedisCommandArgument, RedisCommandArguments } from '.'; - -export const FIRST_KEY_INDEX = 1; +import { RedisArgument, SimpleStringReply, Command } from '../RESP/types'; export type MSetArguments = - Array<[RedisCommandArgument, RedisCommandArgument]> | - Array | - Record; + Array<[RedisArgument, RedisArgument]> | + Array | + Record; -export function transformArguments(toSet: MSetArguments): RedisCommandArguments { - const args: RedisCommandArguments = ['MSET']; +export function mSetArguments(command: string, toSet: MSetArguments) { + const args: Array = [command]; - if (Array.isArray(toSet)) { - args.push(...toSet.flat()); - } else { - for (const key of Object.keys(toSet)) { - args.push(key, toSet[key]); - } + if (Array.isArray(toSet)) { + args.push(...toSet.flat()); + } else { + for (const tuple of Object.entries(toSet)) { + args.push(...tuple); } + } - return args; + return args; } -export declare function transformReply(): RedisCommandArgument; +export default { + FIRST_KEY_INDEX: 1, + IS_READ_ONLY: true, + transformArguments: mSetArguments.bind(undefined, 'MSET'), + transformReply: undefined as unknown as () => SimpleStringReply<'OK'> +} as const satisfies Command; diff --git a/packages/client/lib/commands/MSETNX.spec.ts b/packages/client/lib/commands/MSETNX.spec.ts index 854a9affd8a..1583d04a94e 100644 --- a/packages/client/lib/commands/MSETNX.spec.ts +++ b/packages/client/lib/commands/MSETNX.spec.ts @@ -1,42 +1,38 @@ -import { strict as assert } from 'assert'; +import { strict as assert } from 'node:assert'; import testUtils, { GLOBAL } from '../test-utils'; -import { transformArguments } from './MSETNX'; +import MSETNX from './MSETNX'; describe('MSETNX', () => { - describe('transformArguments', () => { - it("['key1', 'value1', 'key2', 'value2']", () => { - assert.deepEqual( - transformArguments(['key1', 'value1', 'key2', 'value2']), - ['MSETNX', 'key1', 'value1', 'key2', 'value2'] - ); - }); - - it("[['key1', 'value1'], ['key2', 'value2']]", () => { - assert.deepEqual( - transformArguments([['key1', 'value1'], ['key2', 'value2']]), - ['MSETNX', 'key1', 'value1', 'key2', 'value2'] - ); - }); + describe('transformArguments', () => { + it("['key1', 'value1', 'key2', 'value2']", () => { + assert.deepEqual( + MSETNX.transformArguments(['key1', 'value1', 'key2', 'value2']), + ['MSETNX', 'key1', 'value1', 'key2', 'value2'] + ); + }); - it("{key1: 'value1'. key2: 'value2'}", () => { - assert.deepEqual( - transformArguments({ key1: 'value1', key2: 'value2' }), - ['MSETNX', 'key1', 'value1', 'key2', 'value2'] - ); - }); + it("[['key1', 'value1'], ['key2', 'value2']]", () => { + assert.deepEqual( + MSETNX.transformArguments([['key1', 'value1'], ['key2', 'value2']]), + ['MSETNX', 'key1', 'value1', 'key2', 'value2'] + ); }); - testUtils.testWithClient('client.mSetNX', async client => { - assert.equal( - await client.mSetNX(['key1', 'value1', 'key2', 'value2']), - true - ); - }, GLOBAL.SERVERS.OPEN); + it("{key1: 'value1'. key2: 'value2'}", () => { + assert.deepEqual( + MSETNX.transformArguments({ key1: 'value1', key2: 'value2' }), + ['MSETNX', 'key1', 'value1', 'key2', 'value2'] + ); + }); + }); - testUtils.testWithCluster('cluster.mSetNX', async cluster => { - assert.equal( - await cluster.mSetNX(['{key}1', 'value1', '{key}2', 'value2']), - true - ); - }, GLOBAL.CLUSTERS.OPEN); + testUtils.testAll('mSetNX', async client => { + assert.equal( + await client.mSetNX(['{key}1', 'value1', '{key}2', 'value2']), + 1 + ); + }, { + client: GLOBAL.SERVERS.OPEN, + cluster: GLOBAL.CLUSTERS.OPEN + }); }); diff --git a/packages/client/lib/commands/MSETNX.ts b/packages/client/lib/commands/MSETNX.ts index 0ef33936114..4867b3ea092 100644 --- a/packages/client/lib/commands/MSETNX.ts +++ b/packages/client/lib/commands/MSETNX.ts @@ -1,20 +1,9 @@ -import { RedisCommandArguments } from '.'; -import { MSetArguments } from './MSET'; - -export const FIRST_KEY_INDEX = 1; - -export function transformArguments(toSet: MSetArguments): RedisCommandArguments { - const args: RedisCommandArguments = ['MSETNX']; - - if (Array.isArray(toSet)) { - args.push(...toSet.flat()); - } else { - for (const key of Object.keys(toSet)) { - args.push(key, toSet[key]); - } - } - - return args; -} - -export { transformBooleanReply as transformReply } from './generic-transformers'; +import { SimpleStringReply, Command } from '../RESP/types'; +import { mSetArguments } from './MSET'; + +export default { + FIRST_KEY_INDEX: 1, + IS_READ_ONLY: true, + transformArguments: mSetArguments.bind(undefined, 'MSETNX'), + transformReply: undefined as unknown as () => SimpleStringReply<'OK'> +} as const satisfies Command; diff --git a/packages/client/lib/commands/OBJECT_ENCODING.spec.ts b/packages/client/lib/commands/OBJECT_ENCODING.spec.ts index 6f42969d547..48146f12924 100644 --- a/packages/client/lib/commands/OBJECT_ENCODING.spec.ts +++ b/packages/client/lib/commands/OBJECT_ENCODING.spec.ts @@ -1,19 +1,22 @@ -import { strict as assert } from 'assert'; +import { strict as assert } from 'node:assert'; import testUtils, { GLOBAL } from '../test-utils'; -import { transformArguments } from './OBJECT_ENCODING'; +import OBJECT_ENCODING from './OBJECT_ENCODING'; describe('OBJECT ENCODING', () => { - it('transformArguments', () => { - assert.deepEqual( - transformArguments('key'), - ['OBJECT', 'ENCODING', 'key'] - ); - }); + it('transformArguments', () => { + assert.deepEqual( + OBJECT_ENCODING.transformArguments('key'), + ['OBJECT', 'ENCODING', 'key'] + ); + }); - testUtils.testWithClient('client.objectEncoding', async client => { - assert.equal( - await client.objectEncoding('key'), - null - ); - }, GLOBAL.SERVERS.OPEN); + testUtils.testAll('objectEncoding', async client => { + assert.equal( + await client.objectEncoding('key'), + null + ); + }, { + client: GLOBAL.SERVERS.OPEN, + cluster: GLOBAL.CLUSTERS.OPEN + }); }); diff --git a/packages/client/lib/commands/OBJECT_ENCODING.ts b/packages/client/lib/commands/OBJECT_ENCODING.ts index ac219ae89ed..e74c3f99ebd 100644 --- a/packages/client/lib/commands/OBJECT_ENCODING.ts +++ b/packages/client/lib/commands/OBJECT_ENCODING.ts @@ -1,11 +1,10 @@ -import { RedisCommandArgument, RedisCommandArguments } from '.'; +import { RedisArgument, BlobStringReply, NullReply, Command } from '../RESP/types'; -export const FIRST_KEY_INDEX = 2; - -export const IS_READ_ONLY = true; - -export function transformArguments(key: RedisCommandArgument): RedisCommandArguments { +export default { + FIRST_KEY_INDEX: 2, + IS_READ_ONLY: true, + transformArguments(key: RedisArgument) { return ['OBJECT', 'ENCODING', key]; -} - -export declare function transformReply(): string | null; + }, + transformReply: undefined as unknown as () => BlobStringReply | NullReply +} as const satisfies Command; diff --git a/packages/client/lib/commands/OBJECT_FREQ.spec.ts b/packages/client/lib/commands/OBJECT_FREQ.spec.ts index 6d2513cf18c..cbad72c308d 100644 --- a/packages/client/lib/commands/OBJECT_FREQ.spec.ts +++ b/packages/client/lib/commands/OBJECT_FREQ.spec.ts @@ -1,19 +1,22 @@ -import { strict as assert } from 'assert'; +import { strict as assert } from 'node:assert'; import testUtils, { GLOBAL } from '../test-utils'; -import { transformArguments } from './OBJECT_FREQ'; +import OBJECT_FREQ from './OBJECT_FREQ'; describe('OBJECT FREQ', () => { - it('transformArguments', () => { - assert.deepEqual( - transformArguments('key'), - ['OBJECT', 'FREQ', 'key'] - ); - }); + it('transformArguments', () => { + assert.deepEqual( + OBJECT_FREQ.transformArguments('key'), + ['OBJECT', 'FREQ', 'key'] + ); + }); - testUtils.testWithClient('client.objectFreq', async client => { - assert.equal( - await client.objectFreq('key'), - null - ); - }, GLOBAL.SERVERS.OPEN); + testUtils.testAll('client.objectFreq', async client => { + assert.equal( + await client.objectFreq('key'), + null + ); + }, { + client: GLOBAL.SERVERS.OPEN, + cluster: GLOBAL.CLUSTERS.OPEN + }); }); diff --git a/packages/client/lib/commands/OBJECT_FREQ.ts b/packages/client/lib/commands/OBJECT_FREQ.ts index 071d16f2748..b6757c6c57b 100644 --- a/packages/client/lib/commands/OBJECT_FREQ.ts +++ b/packages/client/lib/commands/OBJECT_FREQ.ts @@ -1,11 +1,10 @@ -import { RedisCommandArgument, RedisCommandArguments } from '.'; +import { RedisArgument, NumberReply, NullReply, Command } from '../RESP/types'; -export const FIRST_KEY_INDEX = 2; - -export const IS_READ_ONLY = true; - -export function transformArguments(key: RedisCommandArgument): RedisCommandArguments { +export default { + FIRST_KEY_INDEX: 2, + IS_READ_ONLY: true, + transformArguments(key: RedisArgument) { return ['OBJECT', 'FREQ', key]; -} - -export declare function transformReply(): number | null; + }, + transformReply: undefined as unknown as () => NumberReply | NullReply +} as const satisfies Command; diff --git a/packages/client/lib/commands/OBJECT_IDLETIME.spec.ts b/packages/client/lib/commands/OBJECT_IDLETIME.spec.ts index 61529e1366b..51866427c81 100644 --- a/packages/client/lib/commands/OBJECT_IDLETIME.spec.ts +++ b/packages/client/lib/commands/OBJECT_IDLETIME.spec.ts @@ -1,19 +1,22 @@ -import { strict as assert } from 'assert'; +import { strict as assert } from 'node:assert'; import testUtils, { GLOBAL } from '../test-utils'; -import { transformArguments } from './OBJECT_IDLETIME'; +import OBJECT_IDLETIME from './OBJECT_IDLETIME'; describe('OBJECT IDLETIME', () => { - it('transformArguments', () => { - assert.deepEqual( - transformArguments('key'), - ['OBJECT', 'IDLETIME', 'key'] - ); - }); + it('transformArguments', () => { + assert.deepEqual( + OBJECT_IDLETIME.transformArguments('key'), + ['OBJECT', 'IDLETIME', 'key'] + ); + }); - testUtils.testWithClient('client.objectIdleTime', async client => { - assert.equal( - await client.objectIdleTime('key'), - null - ); - }, GLOBAL.SERVERS.OPEN); + testUtils.testAll('client.objectIdleTime', async client => { + assert.equal( + await client.objectIdleTime('key'), + null + ); + }, { + client: GLOBAL.SERVERS.OPEN, + cluster: GLOBAL.CLUSTERS.OPEN + }); }); diff --git a/packages/client/lib/commands/OBJECT_IDLETIME.ts b/packages/client/lib/commands/OBJECT_IDLETIME.ts index 38847d6f4cf..f0fef24e2d8 100644 --- a/packages/client/lib/commands/OBJECT_IDLETIME.ts +++ b/packages/client/lib/commands/OBJECT_IDLETIME.ts @@ -1,11 +1,10 @@ -import { RedisCommandArgument, RedisCommandArguments } from '.'; +import { RedisArgument, NumberReply, NullReply, Command } from '../RESP/types'; -export const FIRST_KEY_INDEX = 2; - -export const IS_READ_ONLY = true; - -export function transformArguments(key: RedisCommandArgument): RedisCommandArguments { +export default { + FIRST_KEY_INDEX: 2, + IS_READ_ONLY: true, + transformArguments(key: RedisArgument) { return ['OBJECT', 'IDLETIME', key]; -} - -export declare function transformReply(): number | null; + }, + transformReply: undefined as unknown as () => NumberReply | NullReply +} as const satisfies Command; diff --git a/packages/client/lib/commands/OBJECT_REFCOUNT.spec.ts b/packages/client/lib/commands/OBJECT_REFCOUNT.spec.ts index 199dca3fe82..f4309786eb1 100644 --- a/packages/client/lib/commands/OBJECT_REFCOUNT.spec.ts +++ b/packages/client/lib/commands/OBJECT_REFCOUNT.spec.ts @@ -1,19 +1,22 @@ -import { strict as assert } from 'assert'; +import { strict as assert } from 'node:assert'; import testUtils, { GLOBAL } from '../test-utils'; -import { transformArguments } from './OBJECT_REFCOUNT'; +import OBJECT_REFCOUNT from './OBJECT_REFCOUNT'; describe('OBJECT REFCOUNT', () => { - it('transformArguments', () => { - assert.deepEqual( - transformArguments('key'), - ['OBJECT', 'REFCOUNT', 'key'] - ); - }); + it('transformArguments', () => { + assert.deepEqual( + OBJECT_REFCOUNT.transformArguments('key'), + ['OBJECT', 'REFCOUNT', 'key'] + ); + }); - testUtils.testWithClient('client.objectRefCount', async client => { - assert.equal( - await client.objectRefCount('key'), - null - ); - }, GLOBAL.SERVERS.OPEN); + testUtils.testAll('client.objectRefCount', async client => { + assert.equal( + await client.objectRefCount('key'), + null + ); + }, { + client: GLOBAL.SERVERS.OPEN, + cluster: GLOBAL.CLUSTERS.OPEN + }); }); diff --git a/packages/client/lib/commands/OBJECT_REFCOUNT.ts b/packages/client/lib/commands/OBJECT_REFCOUNT.ts index 9fd259b5b90..c8381a76bf7 100644 --- a/packages/client/lib/commands/OBJECT_REFCOUNT.ts +++ b/packages/client/lib/commands/OBJECT_REFCOUNT.ts @@ -1,11 +1,10 @@ -import { RedisCommandArgument, RedisCommandArguments } from '.'; +import { RedisArgument, NumberReply, NullReply, Command } from '../RESP/types'; -export const FIRST_KEY_INDEX = 2; - -export const IS_READ_ONLY = true; - -export function transformArguments(key: RedisCommandArgument): RedisCommandArguments { +export default { + FIRST_KEY_INDEX: 2, + IS_READ_ONLY: true, + transformArguments(key: RedisArgument) { return ['OBJECT', 'REFCOUNT', key]; -} - -export declare function transformReply(): number | null; + }, + transformReply: undefined as unknown as () => NumberReply | NullReply +} as const satisfies Command; diff --git a/packages/client/lib/commands/PERSIST.spec.ts b/packages/client/lib/commands/PERSIST.spec.ts index 4e53bd85a6c..d778a1c0a5f 100644 --- a/packages/client/lib/commands/PERSIST.spec.ts +++ b/packages/client/lib/commands/PERSIST.spec.ts @@ -1,19 +1,22 @@ -import { strict as assert } from 'assert'; +import { strict as assert } from 'node:assert'; import testUtils, { GLOBAL } from '../test-utils'; -import { transformArguments } from './PERSIST'; +import PERSIST from './PERSIST'; describe('PERSIST', () => { - it('transformArguments', () => { - assert.deepEqual( - transformArguments('key'), - ['PERSIST', 'key'] - ); - }); + it('transformArguments', () => { + assert.deepEqual( + PERSIST.transformArguments('key'), + ['PERSIST', 'key'] + ); + }); - testUtils.testWithClient('client.persist', async client => { - assert.equal( - await client.persist('key'), - false - ); - }, GLOBAL.SERVERS.OPEN); + testUtils.testAll('persist', async client => { + assert.equal( + await client.persist('key'), + 0 + ); + }, { + client: GLOBAL.SERVERS.OPEN, + cluster: GLOBAL.CLUSTERS.OPEN + }); }); diff --git a/packages/client/lib/commands/PERSIST.ts b/packages/client/lib/commands/PERSIST.ts index d7c9f8623e4..64637fabc14 100644 --- a/packages/client/lib/commands/PERSIST.ts +++ b/packages/client/lib/commands/PERSIST.ts @@ -1,9 +1,9 @@ -import { RedisCommandArgument, RedisCommandArguments } from '.'; +import { RedisArgument, NumberReply, Command } from '../RESP/types'; -export const FIRST_KEY_INDEX = 1; - -export function transformArguments(key: RedisCommandArgument): RedisCommandArguments { +export default { + FIRST_KEY_INDEX: 1, + transformArguments(key: RedisArgument) { return ['PERSIST', key]; -} - -export { transformBooleanReply as transformReply } from './generic-transformers'; + }, + transformReply: undefined as unknown as () => NumberReply +} as const satisfies Command; diff --git a/packages/client/lib/commands/PEXPIRE.spec.ts b/packages/client/lib/commands/PEXPIRE.spec.ts index 03bde656103..61bfe69e9a1 100644 --- a/packages/client/lib/commands/PEXPIRE.spec.ts +++ b/packages/client/lib/commands/PEXPIRE.spec.ts @@ -1,28 +1,31 @@ -import { strict as assert } from 'assert'; +import { strict as assert } from 'node:assert'; import testUtils, { GLOBAL } from '../test-utils'; -import { transformArguments } from './PEXPIRE'; +import PEXPIRE from './PEXPIRE'; describe('PEXPIRE', () => { - describe('transformArguments', () => { - it('simple', () => { - assert.deepEqual( - transformArguments('key', 1), - ['PEXPIRE', 'key', '1'] - ); - }); + describe('transformArguments', () => { + it('simple', () => { + assert.deepEqual( + PEXPIRE.transformArguments('key', 1), + ['PEXPIRE', 'key', '1'] + ); + }); - it('with set option', () => { - assert.deepEqual( - transformArguments('key', 1, 'GT'), - ['PEXPIRE', 'key', '1', 'GT'] - ); - }); + it('with set option', () => { + assert.deepEqual( + PEXPIRE.transformArguments('key', 1, 'GT'), + ['PEXPIRE', 'key', '1', 'GT'] + ); }); + }); - testUtils.testWithClient('client.pExpire', async client => { - assert.equal( - await client.pExpire('key', 1), - false - ); - }, GLOBAL.SERVERS.OPEN); + testUtils.testAll('pExpire', async client => { + assert.equal( + await client.pExpire('key', 1), + 0 + ); + }, { + client: GLOBAL.SERVERS.OPEN, + cluster: GLOBAL.CLUSTERS.OPEN + }); }); diff --git a/packages/client/lib/commands/PEXPIRE.ts b/packages/client/lib/commands/PEXPIRE.ts index cbb5666a514..4d64281e922 100644 --- a/packages/client/lib/commands/PEXPIRE.ts +++ b/packages/client/lib/commands/PEXPIRE.ts @@ -1,19 +1,20 @@ -import { RedisCommandArgument, RedisCommandArguments } from '.'; +import { RedisArgument, NumberReply, Command } from '../RESP/types'; -export const FIRST_KEY_INDEX = 1; - -export function transformArguments( - key: RedisCommandArgument, - milliseconds: number, +export default { + FIRST_KEY_INDEX: 1, + IS_READ_ONLY: true, + transformArguments( + key: RedisArgument, + ms: number, mode?: 'NX' | 'XX' | 'GT' | 'LT' -): RedisCommandArguments { - const args = ['PEXPIRE', key, milliseconds.toString()]; + ) { + const args = ['PEXPIRE', key, ms.toString()]; if (mode) { - args.push(mode); + args.push(mode); } return args; -} - -export { transformBooleanReply as transformReply } from './generic-transformers'; + }, + transformReply: undefined as unknown as () => NumberReply +} as const satisfies Command; diff --git a/packages/client/lib/commands/PEXPIREAT.spec.ts b/packages/client/lib/commands/PEXPIREAT.spec.ts index fec03c8fb75..117c5b512e7 100644 --- a/packages/client/lib/commands/PEXPIREAT.spec.ts +++ b/packages/client/lib/commands/PEXPIREAT.spec.ts @@ -1,36 +1,39 @@ -import { strict as assert } from 'assert'; +import { strict as assert } from 'node:assert'; import testUtils, { GLOBAL } from '../test-utils'; -import { transformArguments } from './PEXPIREAT'; +import PEXPIREAT from './PEXPIREAT'; describe('PEXPIREAT', () => { - describe('transformArguments', () => { - it('number', () => { - assert.deepEqual( - transformArguments('key', 1), - ['PEXPIREAT', 'key', '1'] - ); - }); + describe('transformArguments', () => { + it('number', () => { + assert.deepEqual( + PEXPIREAT.transformArguments('key', 1), + ['PEXPIREAT', 'key', '1'] + ); + }); - it('date', () => { - const d = new Date(); - assert.deepEqual( - transformArguments('key', d), - ['PEXPIREAT', 'key', d.getTime().toString()] - ); - }); + it('date', () => { + const d = new Date(); + assert.deepEqual( + PEXPIREAT.transformArguments('key', d), + ['PEXPIREAT', 'key', d.getTime().toString()] + ); + }); - it('with set option', () => { - assert.deepEqual( - transformArguments('key', 1, 'XX'), - ['PEXPIREAT', 'key', '1', 'XX'] - ); - }); + it('with set option', () => { + assert.deepEqual( + PEXPIREAT.transformArguments('key', 1, 'XX'), + ['PEXPIREAT', 'key', '1', 'XX'] + ); }); + }); - testUtils.testWithClient('client.pExpireAt', async client => { - assert.equal( - await client.pExpireAt('key', 1), - false - ); - }, GLOBAL.SERVERS.OPEN); + testUtils.testAll('pExpireAt', async client => { + assert.equal( + await client.pExpireAt('key', 1), + 0 + ); + }, { + client: GLOBAL.SERVERS.OPEN, + cluster: GLOBAL.CLUSTERS.OPEN + }); }); diff --git a/packages/client/lib/commands/PEXPIREAT.ts b/packages/client/lib/commands/PEXPIREAT.ts index da912ec4fcb..cbae589fba6 100644 --- a/packages/client/lib/commands/PEXPIREAT.ts +++ b/packages/client/lib/commands/PEXPIREAT.ts @@ -1,24 +1,21 @@ -import { RedisCommandArgument, RedisCommandArguments } from '.'; +import { RedisArgument, NumberReply, Command } from '../RESP/types'; import { transformPXAT } from './generic-transformers'; -export const FIRST_KEY_INDEX = 1; - -export function transformArguments( - key: RedisCommandArgument, - millisecondsTimestamp: number | Date, +export default { + FIRST_KEY_INDEX: 1, + IS_READ_ONLY: true, + transformArguments( + key: RedisArgument, + msTimestamp: number | Date, mode?: 'NX' | 'XX' | 'GT' | 'LT' -): RedisCommandArguments { - const args = [ - 'PEXPIREAT', - key, - transformPXAT(millisecondsTimestamp) - ]; + ) { + const args = ['PEXPIREAT', key, transformPXAT(msTimestamp)]; if (mode) { - args.push(mode); + args.push(mode); } return args; -} - -export { transformBooleanReply as transformReply } from './generic-transformers'; + }, + transformReply: undefined as unknown as () => NumberReply +} as const satisfies Command; diff --git a/packages/client/lib/commands/PEXPIRETIME.spec.ts b/packages/client/lib/commands/PEXPIRETIME.spec.ts index a2fd7f03f82..25a8293ec59 100644 --- a/packages/client/lib/commands/PEXPIRETIME.spec.ts +++ b/packages/client/lib/commands/PEXPIRETIME.spec.ts @@ -1,21 +1,24 @@ -import { strict as assert } from 'assert'; +import { strict as assert } from 'node:assert'; import testUtils, { GLOBAL } from '../test-utils'; -import { transformArguments } from './PEXPIRETIME'; +import PEXPIRETIME from './PEXPIRETIME'; describe('PEXPIRETIME', () => { - testUtils.isVersionGreaterThanHook([7]); + testUtils.isVersionGreaterThanHook([7]); - it('transformArguments', () => { - assert.deepEqual( - transformArguments('key'), - ['PEXPIRETIME', 'key'] - ); - }); + it('transformArguments', () => { + assert.deepEqual( + PEXPIRETIME.transformArguments('key'), + ['PEXPIRETIME', 'key'] + ); + }); - testUtils.testWithClient('client.pExpireTime', async client => { - assert.equal( - await client.pExpireTime('key'), - -2 - ); - }, GLOBAL.SERVERS.OPEN); + testUtils.testAll('pExpireTime', async client => { + assert.equal( + await client.pExpireTime('key'), + -2 + ); + }, { + client: GLOBAL.SERVERS.OPEN, + cluster: GLOBAL.CLUSTERS.OPEN + }); }); diff --git a/packages/client/lib/commands/PEXPIRETIME.ts b/packages/client/lib/commands/PEXPIRETIME.ts index 4c1acba8f04..e228351fa0e 100644 --- a/packages/client/lib/commands/PEXPIRETIME.ts +++ b/packages/client/lib/commands/PEXPIRETIME.ts @@ -1,9 +1,10 @@ -import { RedisCommandArgument, RedisCommandArguments } from '.'; +import { RedisArgument, NumberReply, Command } from '../RESP/types'; -export const FIRST_KEY_INDEX = 1; - -export function transformArguments(key: RedisCommandArgument): RedisCommandArguments { +export default { + FIRST_KEY_INDEX: 1, + IS_READ_ONLY: true, + transformArguments(key: RedisArgument) { return ['PEXPIRETIME', key]; -} - -export declare function transformReply(): number; + }, + transformReply: undefined as unknown as () => NumberReply +} as const satisfies Command; diff --git a/packages/client/lib/commands/PFADD.spec.ts b/packages/client/lib/commands/PFADD.spec.ts index 8c0e752fd50..04ba44164df 100644 --- a/packages/client/lib/commands/PFADD.spec.ts +++ b/packages/client/lib/commands/PFADD.spec.ts @@ -1,28 +1,31 @@ -import { strict as assert } from 'assert'; +import { strict as assert } from 'node:assert'; import testUtils, { GLOBAL } from '../test-utils'; -import { transformArguments } from './PFADD'; +import PFADD from './PFADD'; describe('PFADD', () => { - describe('transformArguments', () => { - it('string', () => { - assert.deepEqual( - transformArguments('key', 'element'), - ['PFADD', 'key', 'element'] - ); - }); + describe('transformArguments', () => { + it('string', () => { + assert.deepEqual( + PFADD.transformArguments('key', 'element'), + ['PFADD', 'key', 'element'] + ); + }); - it('array', () => { - assert.deepEqual( - transformArguments('key', ['1', '2']), - ['PFADD', 'key', '1', '2'] - ); - }); + it('array', () => { + assert.deepEqual( + PFADD.transformArguments('key', ['1', '2']), + ['PFADD', 'key', '1', '2'] + ); }); + }); - testUtils.testWithClient('client.pfAdd', async client => { - assert.equal( - await client.pfAdd('key', '1'), - true - ); - }, GLOBAL.SERVERS.OPEN); + testUtils.testAll('pfAdd', async client => { + assert.equal( + await client.pfAdd('key', '1'), + 1 + ); + }, { + client: GLOBAL.SERVERS.OPEN, + cluster: GLOBAL.CLUSTERS.OPEN + }); }); diff --git a/packages/client/lib/commands/PFADD.ts b/packages/client/lib/commands/PFADD.ts index 8c8985de890..d7f198fbe55 100644 --- a/packages/client/lib/commands/PFADD.ts +++ b/packages/client/lib/commands/PFADD.ts @@ -1,13 +1,14 @@ -import { RedisCommandArgument, RedisCommandArguments } from '.'; -import { pushVerdictArguments } from './generic-transformers'; +import { RedisArgument, NumberReply, Command } from '../RESP/types'; +import { RedisVariadicArgument, pushVariadicArguments } from './generic-transformers'; -export const FIRST_KEY_INDEX = 1; +export default { + FIRST_KEY_INDEX: 1, + IS_READ_ONLY: true, + transformArguments(key: RedisArgument, element?: RedisVariadicArgument) { + const args = ['PFADD', key]; + if (!element) return args; -export function transformArguments( - key: RedisCommandArgument, - element: RedisCommandArgument | Array -): RedisCommandArguments { - return pushVerdictArguments(['PFADD', key], element); -} - -export { transformBooleanReply as transformReply } from './generic-transformers'; + return pushVariadicArguments(args, element); + }, + transformReply: undefined as unknown as () => NumberReply +} as const satisfies Command; diff --git a/packages/client/lib/commands/PFCOUNT.spec.ts b/packages/client/lib/commands/PFCOUNT.spec.ts index a1ea06c4494..ebb54ea1d72 100644 --- a/packages/client/lib/commands/PFCOUNT.spec.ts +++ b/packages/client/lib/commands/PFCOUNT.spec.ts @@ -1,28 +1,31 @@ -import { strict as assert } from 'assert'; +import { strict as assert } from 'node:assert'; import testUtils, { GLOBAL } from '../test-utils'; -import { transformArguments } from './PFCOUNT'; +import PFCOUNT from './PFCOUNT'; describe('PFCOUNT', () => { - describe('transformArguments', () => { - it('string', () => { - assert.deepEqual( - transformArguments('key'), - ['PFCOUNT', 'key'] - ); - }); + describe('transformArguments', () => { + it('string', () => { + assert.deepEqual( + PFCOUNT.transformArguments('key'), + ['PFCOUNT', 'key'] + ); + }); - it('array', () => { - assert.deepEqual( - transformArguments(['1', '2']), - ['PFCOUNT', '1', '2'] - ); - }); + it('array', () => { + assert.deepEqual( + PFCOUNT.transformArguments(['1', '2']), + ['PFCOUNT', '1', '2'] + ); }); + }); - testUtils.testWithClient('client.pfCount', async client => { - assert.equal( - await client.pfCount('key'), - 0 - ); - }, GLOBAL.SERVERS.OPEN); + testUtils.testAll('pfCount', async client => { + assert.equal( + await client.pfCount('key'), + 0 + ); + }, { + client: GLOBAL.SERVERS.OPEN, + cluster: GLOBAL.CLUSTERS.OPEN + }); }); diff --git a/packages/client/lib/commands/PFCOUNT.ts b/packages/client/lib/commands/PFCOUNT.ts index a4cf2dbcb26..5b46eb00d92 100644 --- a/packages/client/lib/commands/PFCOUNT.ts +++ b/packages/client/lib/commands/PFCOUNT.ts @@ -1,12 +1,11 @@ -import { RedisCommandArgument, RedisCommandArguments } from '.'; -import { pushVerdictArguments } from './generic-transformers'; +import { NumberReply, Command } from '../RESP/types'; +import { RedisVariadicArgument, pushVariadicArguments } from './generic-transformers'; -export const FIRST_KEY_INDEX = 1; - -export function transformArguments( - key: RedisCommandArgument | Array -): RedisCommandArguments { - return pushVerdictArguments(['PFCOUNT'], key); -} - -export declare function transformReply(): number; +export default { + FIRST_KEY_INDEX: 1, + IS_READ_ONLY: true, + transformArguments(key: RedisVariadicArgument) { + return pushVariadicArguments(['PFCOUNT'], key); + }, + transformReply: undefined as unknown as () => NumberReply +} as const satisfies Command; diff --git a/packages/client/lib/commands/PFMERGE.spec.ts b/packages/client/lib/commands/PFMERGE.spec.ts index 881fc5f5439..bb2444b3ef1 100644 --- a/packages/client/lib/commands/PFMERGE.spec.ts +++ b/packages/client/lib/commands/PFMERGE.spec.ts @@ -1,28 +1,31 @@ -import { strict as assert } from 'assert'; +import { strict as assert } from 'node:assert'; import testUtils, { GLOBAL } from '../test-utils'; -import { transformArguments } from './PFMERGE'; +import PFMERGE from './PFMERGE'; describe('PFMERGE', () => { - describe('transformArguments', () => { - it('string', () => { - assert.deepEqual( - transformArguments('destination', 'source'), - ['PFMERGE', 'destination', 'source'] - ); - }); + describe('transformArguments', () => { + it('string', () => { + assert.deepEqual( + PFMERGE.transformArguments('destination', 'source'), + ['PFMERGE', 'destination', 'source'] + ); + }); - it('array', () => { - assert.deepEqual( - transformArguments('destination', ['1', '2']), - ['PFMERGE', 'destination', '1', '2'] - ); - }); + it('array', () => { + assert.deepEqual( + PFMERGE.transformArguments('destination', ['1', '2']), + ['PFMERGE', 'destination', '1', '2'] + ); }); + }); - testUtils.testWithClient('client.pfMerge', async client => { - assert.equal( - await client.pfMerge('destination', 'source'), - 'OK' - ); - }, GLOBAL.SERVERS.OPEN); + testUtils.testAll('pfMerge', async client => { + assert.equal( + await client.pfMerge('{tag}destination', '{tag}source'), + 'OK' + ); + }, { + client: GLOBAL.SERVERS.OPEN, + cluster: GLOBAL.CLUSTERS.OPEN + }); }); diff --git a/packages/client/lib/commands/PFMERGE.ts b/packages/client/lib/commands/PFMERGE.ts index e934062b3fe..eeeeb5173db 100644 --- a/packages/client/lib/commands/PFMERGE.ts +++ b/packages/client/lib/commands/PFMERGE.ts @@ -1,10 +1,16 @@ -import { RedisCommandArguments } from '.'; -import { pushVerdictArguments } from './generic-transformers'; +import { RedisArgument, SimpleStringReply, Command } from '../RESP/types'; +import { RedisVariadicArgument, pushVariadicArguments } from './generic-transformers'; -export const FIRST_KEY_INDEX = 1; +export default { + FIRST_KEY_INDEX: 1, + transformArguments( + destination: RedisArgument, + source?: RedisVariadicArgument + ) { + const args = ['PFMERGE', destination]; + if (!source) return args; -export function transformArguments(destination: string, source: string | Array): RedisCommandArguments { - return pushVerdictArguments(['PFMERGE', destination], source); -} - -export declare function transformReply(): string; + return pushVariadicArguments(args, source); + }, + transformReply: undefined as unknown as () => SimpleStringReply +} as const satisfies Command; diff --git a/packages/client/lib/commands/PING.spec.ts b/packages/client/lib/commands/PING.spec.ts index 06cbae43a13..0cd75a6a8de 100644 --- a/packages/client/lib/commands/PING.spec.ts +++ b/packages/client/lib/commands/PING.spec.ts @@ -1,37 +1,31 @@ -import { strict as assert } from 'assert'; +import { strict as assert } from 'node:assert'; import testUtils, { GLOBAL } from '../test-utils'; -import { transformArguments } from './PING'; +import PING from './PING'; describe('PING', () => { - describe('transformArguments', () => { - it('default', () => { - assert.deepEqual( - transformArguments(), - ['PING'] - ); - }); - - it('with message', () => { - assert.deepEqual( - transformArguments('message'), - ['PING', 'message'] - ); - }); + describe('transformArguments', () => { + it('default', () => { + assert.deepEqual( + PING.transformArguments(), + ['PING'] + ); }); - describe('client.ping', () => { - testUtils.testWithClient('string', async client => { - assert.equal( - await client.ping(), - 'PONG' - ); - }, GLOBAL.SERVERS.OPEN); - - testUtils.testWithClient('buffer', async client => { - assert.deepEqual( - await client.ping(client.commandOptions({ returnBuffers: true })), - Buffer.from('PONG') - ); - }, GLOBAL.SERVERS.OPEN); + it('with message', () => { + assert.deepEqual( + PING.transformArguments('message'), + ['PING', 'message'] + ); }); + }); + + testUtils.testAll('ping', async client => { + assert.equal( + await client.ping(), + 'PONG' + ); + }, { + client: GLOBAL.SERVERS.OPEN, + cluster: GLOBAL.CLUSTERS.OPEN + }); }); diff --git a/packages/client/lib/commands/PING.ts b/packages/client/lib/commands/PING.ts index 95fa006122d..7f6fd31047e 100644 --- a/packages/client/lib/commands/PING.ts +++ b/packages/client/lib/commands/PING.ts @@ -1,12 +1,15 @@ -import { RedisCommandArgument, RedisCommandArguments } from '.'; +import { RedisArgument, SimpleStringReply, BlobStringReply, Command } from '../RESP/types'; -export function transformArguments(message?: RedisCommandArgument): RedisCommandArguments { - const args: RedisCommandArguments = ['PING']; +export default { + FIRST_KEY_INDEX: undefined, + IS_READ_ONLY: true, + transformArguments(message?: RedisArgument) { + const args: Array = ['PING']; if (message) { - args.push(message); + args.push(message); } return args; -} - -export declare function transformReply(): RedisCommandArgument; + }, + transformReply: undefined as unknown as () => SimpleStringReply | BlobStringReply +} as const satisfies Command; diff --git a/packages/client/lib/commands/PSETEX.spec.ts b/packages/client/lib/commands/PSETEX.spec.ts index f6262ed8709..fd7bcd2dff2 100644 --- a/packages/client/lib/commands/PSETEX.spec.ts +++ b/packages/client/lib/commands/PSETEX.spec.ts @@ -1,27 +1,22 @@ -import { strict as assert } from 'assert'; +import { strict as assert } from 'node:assert'; import testUtils, { GLOBAL } from '../test-utils'; -import { transformArguments } from './PSETEX'; +import PSETEX from './PSETEX'; describe('PSETEX', () => { - it('transformArguments', () => { - assert.deepEqual( - transformArguments('key', 1, 'value'), - ['PSETEX', 'key', '1', 'value'] - ); - }); + it('transformArguments', () => { + assert.deepEqual( + PSETEX.transformArguments('key', 1, 'value'), + ['PSETEX', 'key', '1', 'value'] + ); + }); - testUtils.testWithClient('client.pSetEx', async client => { - const a = await client.pSetEx('key', 1, 'value'); - assert.equal( - await client.pSetEx('key', 1, 'value'), - 'OK' - ); - }, GLOBAL.SERVERS.OPEN); - - testUtils.testWithCluster('cluster.pSetEx', async cluster => { - assert.equal( - await cluster.pSetEx('key', 1, 'value'), - 'OK' - ); - }, GLOBAL.CLUSTERS.OPEN); + testUtils.testAll('pSetEx', async client => { + assert.equal( + await client.pSetEx('key', 1, 'value'), + 'OK' + ); + }, { + client: GLOBAL.SERVERS.OPEN, + cluster: GLOBAL.CLUSTERS.OPEN + }); }); diff --git a/packages/client/lib/commands/PSETEX.ts b/packages/client/lib/commands/PSETEX.ts index f2739b6e274..4e345a1a1c9 100644 --- a/packages/client/lib/commands/PSETEX.ts +++ b/packages/client/lib/commands/PSETEX.ts @@ -1,18 +1,18 @@ -import { RedisCommandArgument, RedisCommandArguments } from '.'; +import { RedisArgument, SimpleStringReply, Command } from '../RESP/types'; -export const FIRST_KEY_INDEX = 1; - -export function transformArguments( - key: RedisCommandArgument, - milliseconds: number, - value: RedisCommandArgument -): RedisCommandArguments { +export default { + FIRST_KEY_INDEX: 1, + transformArguments( + key: RedisArgument, + ms: number, + value: RedisArgument + ) { return [ - 'PSETEX', - key, - milliseconds.toString(), - value + 'PSETEX', + key, + ms.toString(), + value ]; -} - -export declare function transformReply(): RedisCommandArgument; + }, + transformReply: undefined as unknown as () => SimpleStringReply<'OK'> +} as const satisfies Command; diff --git a/packages/client/lib/commands/PTTL.spec.ts b/packages/client/lib/commands/PTTL.spec.ts index e65421de590..65a9f5dd0ff 100644 --- a/packages/client/lib/commands/PTTL.spec.ts +++ b/packages/client/lib/commands/PTTL.spec.ts @@ -1,19 +1,22 @@ -import { strict as assert } from 'assert'; +import { strict as assert } from 'node:assert'; import testUtils, { GLOBAL } from '../test-utils'; -import { transformArguments } from './PTTL'; +import PTTL from './PTTL'; describe('PTTL', () => { - it('transformArguments', () => { - assert.deepEqual( - transformArguments('key'), - ['PTTL', 'key'] - ); - }); + it('transformArguments', () => { + assert.deepEqual( + PTTL.transformArguments('key'), + ['PTTL', 'key'] + ); + }); - testUtils.testWithClient('client.pTTL', async client => { - assert.equal( - await client.pTTL('key'), - -2 - ); - }, GLOBAL.SERVERS.OPEN); + testUtils.testAll('pTTL', async client => { + assert.equal( + await client.pTTL('key'), + -2 + ); + }, { + client: GLOBAL.SERVERS.OPEN, + cluster: GLOBAL.CLUSTERS.OPEN + }); }); diff --git a/packages/client/lib/commands/PTTL.ts b/packages/client/lib/commands/PTTL.ts index a2975623f7a..35854337877 100644 --- a/packages/client/lib/commands/PTTL.ts +++ b/packages/client/lib/commands/PTTL.ts @@ -1,11 +1,10 @@ -import { RedisCommandArgument, RedisCommandArguments } from '.'; +import { RedisArgument, NumberReply, Command } from '../RESP/types'; -export const FIRST_KEY_INDEX = 1; - -export const IS_READ_ONLY = true; - -export function transformArguments(key: RedisCommandArgument): RedisCommandArguments { +export default { + FIRST_KEY_INDEX: 1, + IS_READ_ONLY: true, + transformArguments(key: RedisArgument) { return ['PTTL', key]; -} - -export declare function transformReply(): number; + }, + transformReply: undefined as unknown as () => NumberReply +} as const satisfies Command; diff --git a/packages/client/lib/commands/PUBLISH.spec.ts b/packages/client/lib/commands/PUBLISH.spec.ts index b2084e668ba..ec1f108e5ee 100644 --- a/packages/client/lib/commands/PUBLISH.spec.ts +++ b/packages/client/lib/commands/PUBLISH.spec.ts @@ -1,19 +1,19 @@ -import { strict as assert } from 'assert'; +import { strict as assert } from 'node:assert'; import testUtils, { GLOBAL } from '../test-utils'; -import { transformArguments } from './PUBLISH'; +import PUBLISH from './PUBLISH'; describe('PUBLISH', () => { - it('transformArguments', () => { - assert.deepEqual( - transformArguments('channel', 'message'), - ['PUBLISH', 'channel', 'message'] - ); - }); + it('transformArguments', () => { + assert.deepEqual( + PUBLISH.transformArguments('channel', 'message'), + ['PUBLISH', 'channel', 'message'] + ); + }); - testUtils.testWithClient('client.publish', async client => { - assert.equal( - await client.publish('channel', 'message'), - 0 - ); - }, GLOBAL.SERVERS.OPEN); + testUtils.testWithClient('client.publish', async client => { + assert.equal( + await client.publish('channel', 'message'), + 0 + ); + }, GLOBAL.SERVERS.OPEN); }); diff --git a/packages/client/lib/commands/PUBLISH.ts b/packages/client/lib/commands/PUBLISH.ts index 7862a0936cb..e790ff16c4d 100644 --- a/packages/client/lib/commands/PUBLISH.ts +++ b/packages/client/lib/commands/PUBLISH.ts @@ -1,12 +1,11 @@ -import { RedisCommandArgument, RedisCommandArguments } from '.'; +import { RedisArgument, NumberReply, Command } from '../RESP/types'; -export const IS_READ_ONLY = true; - -export function transformArguments( - channel: RedisCommandArgument, - message: RedisCommandArgument -): RedisCommandArguments { +export default { + FIRST_KEY_INDEX: undefined, + IS_READ_ONLY: true, + IS_FORWARD_COMMAND: true, + transformArguments(channel: RedisArgument, message: RedisArgument) { return ['PUBLISH', channel, message]; -} - -export declare function transformReply(): number; + }, + transformReply: undefined as unknown as () => NumberReply +} as const satisfies Command; diff --git a/packages/client/lib/commands/PUBSUB_CHANNELS.spec.ts b/packages/client/lib/commands/PUBSUB_CHANNELS.spec.ts index c427eab4850..2fe02523ed1 100644 --- a/packages/client/lib/commands/PUBSUB_CHANNELS.spec.ts +++ b/packages/client/lib/commands/PUBSUB_CHANNELS.spec.ts @@ -1,28 +1,28 @@ -import { strict as assert } from 'assert'; +import { strict as assert } from 'node:assert'; import testUtils, { GLOBAL } from '../test-utils'; -import { transformArguments } from './PUBSUB_CHANNELS'; +import PUBSUB_CHANNELS from './PUBSUB_CHANNELS'; describe('PUBSUB CHANNELS', () => { - describe('transformArguments', () => { - it('simple', () => { - assert.deepEqual( - transformArguments(), - ['PUBSUB', 'CHANNELS'] - ); - }); + describe('transformArguments', () => { + it('simple', () => { + assert.deepEqual( + PUBSUB_CHANNELS.transformArguments(), + ['PUBSUB', 'CHANNELS'] + ); + }); - it('with pattern', () => { - assert.deepEqual( - transformArguments('patter*'), - ['PUBSUB', 'CHANNELS', 'patter*'] - ); - }); + it('with pattern', () => { + assert.deepEqual( + PUBSUB_CHANNELS.transformArguments('patter*'), + ['PUBSUB', 'CHANNELS', 'patter*'] + ); }); + }); - testUtils.testWithClient('client.pubSubChannels', async client => { - assert.deepEqual( - await client.pubSubChannels(), - [] - ); - }, GLOBAL.SERVERS.OPEN); + testUtils.testWithClient('client.pubSubChannels', async client => { + assert.deepEqual( + await client.pubSubChannels(), + [] + ); + }, GLOBAL.SERVERS.OPEN); }); diff --git a/packages/client/lib/commands/PUBSUB_CHANNELS.ts b/packages/client/lib/commands/PUBSUB_CHANNELS.ts index 86a144ede8e..4bf7abd75dc 100644 --- a/packages/client/lib/commands/PUBSUB_CHANNELS.ts +++ b/packages/client/lib/commands/PUBSUB_CHANNELS.ts @@ -1,13 +1,17 @@ -export const IS_READ_ONLY = true; +import { RedisArgument, ArrayReply, BlobStringReply, Command } from '../RESP/types'; -export function transformArguments(pattern?: string): Array { - const args = ['PUBSUB', 'CHANNELS']; +export default { + FIRST_KEY_INDEX: undefined, + IS_READ_ONLY: true, + transformArguments(pattern?: RedisArgument) { + const args: Array = ['PUBSUB', 'CHANNELS']; if (pattern) { - args.push(pattern); + args.push(pattern); } return args; -} + }, + transformReply: undefined as unknown as () => ArrayReply +} as const satisfies Command; -export declare function transformReply(): Array; diff --git a/packages/client/lib/commands/PUBSUB_NUMPAT.spec.ts b/packages/client/lib/commands/PUBSUB_NUMPAT.spec.ts index d738b916c60..43a2b4b5c0e 100644 --- a/packages/client/lib/commands/PUBSUB_NUMPAT.spec.ts +++ b/packages/client/lib/commands/PUBSUB_NUMPAT.spec.ts @@ -1,19 +1,19 @@ -import { strict as assert } from 'assert'; +import { strict as assert } from 'node:assert'; import testUtils, { GLOBAL } from '../test-utils'; -import { transformArguments } from './PUBSUB_NUMPAT'; +import PUBSUB_NUMPAT from './PUBSUB_NUMPAT'; describe('PUBSUB NUMPAT', () => { - it('transformArguments', () => { - assert.deepEqual( - transformArguments(), - ['PUBSUB', 'NUMPAT'] - ); - }); + it('transformArguments', () => { + assert.deepEqual( + PUBSUB_NUMPAT.transformArguments(), + ['PUBSUB', 'NUMPAT'] + ); + }); - testUtils.testWithClient('client.pubSubNumPat', async client => { - assert.equal( - await client.pubSubNumPat(), - 0 - ); - }, GLOBAL.SERVERS.OPEN); + testUtils.testWithClient('client.pubSubNumPat', async client => { + assert.equal( + await client.pubSubNumPat(), + 0 + ); + }, GLOBAL.SERVERS.OPEN); }); diff --git a/packages/client/lib/commands/PUBSUB_NUMPAT.ts b/packages/client/lib/commands/PUBSUB_NUMPAT.ts index 15be6aa1b18..e8a0738dc72 100644 --- a/packages/client/lib/commands/PUBSUB_NUMPAT.ts +++ b/packages/client/lib/commands/PUBSUB_NUMPAT.ts @@ -1,7 +1,10 @@ -export const IS_READ_ONLY = true; +import { NumberReply, Command } from '../RESP/types'; -export function transformArguments(): Array { +export default { + FIRST_KEY_INDEX: undefined, + IS_READ_ONLY: true, + transformArguments() { return ['PUBSUB', 'NUMPAT']; -} - -export declare function transformReply(): string; + }, + transformReply: undefined as unknown as () => NumberReply +} as const satisfies Command; diff --git a/packages/client/lib/commands/PUBSUB_NUMSUB.spec.ts b/packages/client/lib/commands/PUBSUB_NUMSUB.spec.ts index e35558ef865..151dc219288 100644 --- a/packages/client/lib/commands/PUBSUB_NUMSUB.spec.ts +++ b/packages/client/lib/commands/PUBSUB_NUMSUB.spec.ts @@ -1,35 +1,35 @@ -import { strict as assert } from 'assert'; +import { strict as assert } from 'node:assert'; import testUtils, { GLOBAL } from '../test-utils'; -import { transformArguments } from './PUBSUB_NUMSUB'; +import PUBSUB_NUMSUB from './PUBSUB_NUMSUB'; describe('PUBSUB NUMSUB', () => { - describe('transformArguments', () => { - it('simple', () => { - assert.deepEqual( - transformArguments(), - ['PUBSUB', 'NUMSUB'] - ); - }); + describe('transformArguments', () => { + it('simple', () => { + assert.deepEqual( + PUBSUB_NUMSUB.transformArguments(), + ['PUBSUB', 'NUMSUB'] + ); + }); - it('string', () => { - assert.deepEqual( - transformArguments('channel'), - ['PUBSUB', 'NUMSUB', 'channel'] - ); - }); + it('string', () => { + assert.deepEqual( + PUBSUB_NUMSUB.transformArguments('channel'), + ['PUBSUB', 'NUMSUB', 'channel'] + ); + }); - it('array', () => { - assert.deepEqual( - transformArguments(['1', '2']), - ['PUBSUB', 'NUMSUB', '1', '2'] - ); - }); + it('array', () => { + assert.deepEqual( + PUBSUB_NUMSUB.transformArguments(['1', '2']), + ['PUBSUB', 'NUMSUB', '1', '2'] + ); }); + }); - testUtils.testWithClient('client.pubSubNumSub', async client => { - assert.deepEqual( - await client.pubSubNumSub(), - Object.create(null) - ); - }, GLOBAL.SERVERS.OPEN); + testUtils.testWithClient('client.pubSubNumSub', async client => { + assert.deepEqual( + await client.pubSubNumSub(), + Object.create(null) + ); + }, GLOBAL.SERVERS.OPEN); }); diff --git a/packages/client/lib/commands/PUBSUB_NUMSUB.ts b/packages/client/lib/commands/PUBSUB_NUMSUB.ts index f47238f8882..1f7c41f5bdd 100644 --- a/packages/client/lib/commands/PUBSUB_NUMSUB.ts +++ b/packages/client/lib/commands/PUBSUB_NUMSUB.ts @@ -1,24 +1,23 @@ -import { pushVerdictArguments } from './generic-transformers'; -import { RedisCommandArgument, RedisCommandArguments } from '.'; +import { ArrayReply, BlobStringReply, NumberReply, UnwrapReply, Command } from '../RESP/types'; +import { RedisVariadicArgument, pushVariadicArguments } from './generic-transformers'; -export const IS_READ_ONLY = true; - -export function transformArguments( - channels?: Array | RedisCommandArgument -): RedisCommandArguments { +export default { + FIRST_KEY_INDEX: undefined, + IS_READ_ONLY: true, + transformArguments(channels?: RedisVariadicArgument) { const args = ['PUBSUB', 'NUMSUB']; - if (channels) return pushVerdictArguments(args, channels); + if (channels) return pushVariadicArguments(args, channels); return args; -} - -export function transformReply(rawReply: Array): Record { - const transformedReply = Object.create(null); - - for (let i = 0; i < rawReply.length; i +=2) { - transformedReply[rawReply[i]] = rawReply[i + 1]; + }, + transformReply(rawReply: UnwrapReply>) { + const reply = Object.create(null); + let i = 0; + while (i < rawReply.length) { + reply[rawReply[i++].toString()] = rawReply[i++].toString(); } - return transformedReply; -} + return reply as Record; + } +} as const satisfies Command; diff --git a/packages/client/lib/commands/PUBSUB_SHARDCHANNELS.spec.ts b/packages/client/lib/commands/PUBSUB_SHARDCHANNELS.spec.ts index 1e5f2292b39..77982b34670 100644 --- a/packages/client/lib/commands/PUBSUB_SHARDCHANNELS.spec.ts +++ b/packages/client/lib/commands/PUBSUB_SHARDCHANNELS.spec.ts @@ -1,30 +1,30 @@ -import { strict as assert } from 'assert'; +import { strict as assert } from 'node:assert'; import testUtils, { GLOBAL } from '../test-utils'; -import { transformArguments } from './PUBSUB_SHARDCHANNELS'; +import PUBSUB_SHARDCHANNELS from './PUBSUB_SHARDCHANNELS'; describe('PUBSUB SHARDCHANNELS', () => { - testUtils.isVersionGreaterThanHook([7]); - - describe('transformArguments', () => { - it('without pattern', () => { - assert.deepEqual( - transformArguments(), - ['PUBSUB', 'SHARDCHANNELS'] - ); - }); + testUtils.isVersionGreaterThanHook([7]); - it('with pattern', () => { - assert.deepEqual( - transformArguments('patter*'), - ['PUBSUB', 'SHARDCHANNELS', 'patter*'] - ); - }); + describe('transformArguments', () => { + it('without pattern', () => { + assert.deepEqual( + PUBSUB_SHARDCHANNELS.transformArguments(), + ['PUBSUB', 'SHARDCHANNELS'] + ); }); - testUtils.testWithClient('client.pubSubShardChannels', async client => { - assert.deepEqual( - await client.pubSubShardChannels(), - [] - ); - }, GLOBAL.SERVERS.OPEN); + it('with pattern', () => { + assert.deepEqual( + PUBSUB_SHARDCHANNELS.transformArguments('patter*'), + ['PUBSUB', 'SHARDCHANNELS', 'patter*'] + ); + }); + }); + + testUtils.testWithClient('client.pubSubShardChannels', async client => { + assert.deepEqual( + await client.pubSubShardChannels(), + [] + ); + }, GLOBAL.SERVERS.OPEN); }); diff --git a/packages/client/lib/commands/PUBSUB_SHARDCHANNELS.ts b/packages/client/lib/commands/PUBSUB_SHARDCHANNELS.ts index e998677848a..74d78c02614 100644 --- a/packages/client/lib/commands/PUBSUB_SHARDCHANNELS.ts +++ b/packages/client/lib/commands/PUBSUB_SHARDCHANNELS.ts @@ -1,13 +1,16 @@ -import { RedisCommandArgument, RedisCommandArguments } from '.'; +import { RedisArgument, ArrayReply, BlobStringReply, Command } from '../RESP/types'; -export const IS_READ_ONLY = true; +export default { + FIRST_KEY_INDEX: undefined, + IS_READ_ONLY: true, + transformArguments(pattern?: RedisArgument) { + const args: Array = ['PUBSUB', 'SHARDCHANNELS']; -export function transformArguments( - pattern?: RedisCommandArgument -): RedisCommandArguments { - const args: RedisCommandArguments = ['PUBSUB', 'SHARDCHANNELS']; - if (pattern) args.push(pattern); - return args; -} + if (pattern) { + args.push(pattern); + } -export declare function transformReply(): Array; + return args; + }, + transformReply: undefined as unknown as () => ArrayReply +} as const satisfies Command; diff --git a/packages/client/lib/commands/PUBSUB_SHARDNUMSUB.spec.ts b/packages/client/lib/commands/PUBSUB_SHARDNUMSUB.spec.ts index fea1373b55d..e036a6eae5b 100644 --- a/packages/client/lib/commands/PUBSUB_SHARDNUMSUB.spec.ts +++ b/packages/client/lib/commands/PUBSUB_SHARDNUMSUB.spec.ts @@ -1,48 +1,48 @@ import { strict as assert } from 'assert'; import testUtils, { GLOBAL } from '../test-utils'; -import { transformArguments } from './PUBSUB_SHARDNUMSUB'; +import PUBSUB_SHARDNUMSUB from './PUBSUB_SHARDNUMSUB'; describe('PUBSUB SHARDNUMSUB', () => { - testUtils.isVersionGreaterThanHook([7]); + testUtils.isVersionGreaterThanHook([7]); - describe('transformArguments', () => { - it('simple', () => { - assert.deepEqual( - transformArguments(), - ['PUBSUB', 'SHARDNUMSUB'] - ); - }); + describe('transformArguments', () => { + it('simple', () => { + assert.deepEqual( + PUBSUB_SHARDNUMSUB.transformArguments(), + ['PUBSUB', 'SHARDNUMSUB'] + ); + }); - it('string', () => { - assert.deepEqual( - transformArguments('channel'), - ['PUBSUB', 'SHARDNUMSUB', 'channel'] - ); - }); + it('string', () => { + assert.deepEqual( + PUBSUB_SHARDNUMSUB.transformArguments('channel'), + ['PUBSUB', 'SHARDNUMSUB', 'channel'] + ); + }); - it('array', () => { - assert.deepEqual( - transformArguments(['1', '2']), - ['PUBSUB', 'SHARDNUMSUB', '1', '2'] - ); - }); + it('array', () => { + assert.deepEqual( + PUBSUB_SHARDNUMSUB.transformArguments(['1', '2']), + ['PUBSUB', 'SHARDNUMSUB', '1', '2'] + ); }); + }); - testUtils.testWithClient('client.pubSubShardNumSub', async client => { - assert.deepEqual( - await client.pubSubShardNumSub(['foo', 'bar']), - Object.create(null, { - foo: { - value: 0, - configurable: true, - enumerable: true - }, - bar: { - value: 0, - configurable: true, - enumerable: true - } - }) - ); - }, GLOBAL.SERVERS.OPEN); + testUtils.testWithClient('client.pubSubShardNumSub', async client => { + assert.deepEqual( + await client.pubSubShardNumSub(['foo', 'bar']), + Object.create(null, { + foo: { + value: 0, + configurable: true, + enumerable: true + }, + bar: { + value: 0, + configurable: true, + enumerable: true + } + }) + ); + }, GLOBAL.SERVERS.OPEN); }); diff --git a/packages/client/lib/commands/PUBSUB_SHARDNUMSUB.ts b/packages/client/lib/commands/PUBSUB_SHARDNUMSUB.ts index 4d7f4d8a71e..0ef82477006 100644 --- a/packages/client/lib/commands/PUBSUB_SHARDNUMSUB.ts +++ b/packages/client/lib/commands/PUBSUB_SHARDNUMSUB.ts @@ -1,24 +1,24 @@ -import { pushVerdictArguments } from './generic-transformers'; -import { RedisCommandArgument, RedisCommandArguments } from '.'; +import { ArrayReply, BlobStringReply, NumberReply, UnwrapReply, Command } from '../RESP/types'; +import { RedisVariadicArgument, pushVariadicArguments } from './generic-transformers'; -export const IS_READ_ONLY = true; - -export function transformArguments( - channels?: Array | RedisCommandArgument -): RedisCommandArguments { +export default { + FIRST_KEY_INDEX: undefined, + IS_READ_ONLY: true, + transformArguments(channels?: RedisVariadicArgument) { const args = ['PUBSUB', 'SHARDNUMSUB']; - if (channels) return pushVerdictArguments(args, channels); + if (channels) return pushVariadicArguments(args, channels); return args; -} - -export function transformReply(rawReply: Array): Record { - const transformedReply = Object.create(null); + }, + transformReply(reply: UnwrapReply>) { + const transformedReply: Record = Object.create(null); - for (let i = 0; i < rawReply.length; i += 2) { - transformedReply[rawReply[i]] = rawReply[i + 1]; + for (let i = 0; i < reply.length; i += 2) { + transformedReply[(reply[i] as BlobStringReply).toString()] = reply[i + 1] as NumberReply; } - + return transformedReply; -} + } +} as const satisfies Command; + diff --git a/packages/client/lib/commands/RANDOMKEY.spec.ts b/packages/client/lib/commands/RANDOMKEY.spec.ts index 81c42b2fd83..31de60d7a99 100644 --- a/packages/client/lib/commands/RANDOMKEY.spec.ts +++ b/packages/client/lib/commands/RANDOMKEY.spec.ts @@ -1,19 +1,22 @@ -import { strict as assert } from 'assert'; +import { strict as assert } from 'node:assert'; import testUtils, { GLOBAL } from '../test-utils'; -import { transformArguments } from './RANDOMKEY'; +import RANDOMKEY from './RANDOMKEY'; describe('RANDOMKEY', () => { - it('transformArguments', () => { - assert.deepEqual( - transformArguments(), - ['RANDOMKEY'] - ); - }); + it('transformArguments', () => { + assert.deepEqual( + RANDOMKEY.transformArguments(), + ['RANDOMKEY'] + ); + }); - testUtils.testWithClient('client.randomKey', async client => { - assert.equal( - await client.randomKey(), - null - ); - }, GLOBAL.SERVERS.OPEN); + testUtils.testAll('randomKey', async client => { + assert.equal( + await client.randomKey(), + null + ); + }, { + client: GLOBAL.SERVERS.OPEN, + cluster: GLOBAL.CLUSTERS.OPEN + }); }); diff --git a/packages/client/lib/commands/RANDOMKEY.ts b/packages/client/lib/commands/RANDOMKEY.ts index f2d511d4dec..42028ebbe38 100644 --- a/packages/client/lib/commands/RANDOMKEY.ts +++ b/packages/client/lib/commands/RANDOMKEY.ts @@ -1,9 +1,10 @@ -import { RedisCommandArgument, RedisCommandArguments } from '.'; +import { NumberReply, Command } from '../RESP/types'; -export const IS_READ_ONLY = true; - -export function transformArguments(): RedisCommandArguments { +export default { + FIRST_KEY_INDEX: undefined, + IS_READ_ONLY: true, + transformArguments() { return ['RANDOMKEY']; -} - -export declare function transformReply(): RedisCommandArgument | null; + }, + transformReply: undefined as unknown as () => NumberReply +} as const satisfies Command; diff --git a/packages/client/lib/commands/READONLY.spec.ts b/packages/client/lib/commands/READONLY.spec.ts index aa4db47f81a..14bb047349a 100644 --- a/packages/client/lib/commands/READONLY.spec.ts +++ b/packages/client/lib/commands/READONLY.spec.ts @@ -1,11 +1,11 @@ -import { strict as assert } from 'assert'; -import { transformArguments } from './READONLY'; +import { strict as assert } from 'node:assert'; +import READONLY from './READONLY'; describe('READONLY', () => { - it('transformArguments', () => { - assert.deepEqual( - transformArguments(), - ['READONLY'] - ); - }); + it('transformArguments', () => { + assert.deepEqual( + READONLY.transformArguments(), + ['READONLY'] + ); + }); }); diff --git a/packages/client/lib/commands/READONLY.ts b/packages/client/lib/commands/READONLY.ts index db7db881628..bb15834550e 100644 --- a/packages/client/lib/commands/READONLY.ts +++ b/packages/client/lib/commands/READONLY.ts @@ -1,5 +1,10 @@ -export function transformArguments(): Array { - return ['READONLY']; -} +import { SimpleStringReply, Command } from '../RESP/types'; -export declare function transformReply(): string; +export default { + FIRST_KEY_INDEX: undefined, + IS_READ_ONLY: true, + transformArguments() { + return ['READONLY']; + }, + transformReply: undefined as unknown as () => SimpleStringReply +} as const satisfies Command; diff --git a/packages/client/lib/commands/READWRITE.spec.ts b/packages/client/lib/commands/READWRITE.spec.ts index 6ce4a3ee56a..94a88a0dcc7 100644 --- a/packages/client/lib/commands/READWRITE.spec.ts +++ b/packages/client/lib/commands/READWRITE.spec.ts @@ -1,11 +1,11 @@ -import { strict as assert } from 'assert'; -import { transformArguments } from './READWRITE'; +import { strict as assert } from 'node:assert'; +import READWRITE from './READWRITE'; describe('READWRITE', () => { - it('transformArguments', () => { - assert.deepEqual( - transformArguments(), - ['READWRITE'] - ); - }); + it('transformArguments', () => { + assert.deepEqual( + READWRITE.transformArguments(), + ['READWRITE'] + ); + }); }); diff --git a/packages/client/lib/commands/READWRITE.ts b/packages/client/lib/commands/READWRITE.ts index 60dc865e89e..fe70e15d4c8 100644 --- a/packages/client/lib/commands/READWRITE.ts +++ b/packages/client/lib/commands/READWRITE.ts @@ -1,5 +1,10 @@ -export function transformArguments(): Array { - return ['READWRITE']; -} +import { SimpleStringReply, Command } from '../RESP/types'; -export declare function transformReply(): string; +export default { + FIRST_KEY_INDEX: undefined, + IS_READ_ONLY: true, + transformArguments() { + return ['READWRITE']; + }, + transformReply: undefined as unknown as () => SimpleStringReply +} as const satisfies Command; diff --git a/packages/client/lib/commands/RENAME.spec.ts b/packages/client/lib/commands/RENAME.spec.ts index 49e0af600f6..cf3dccbf3e7 100644 --- a/packages/client/lib/commands/RENAME.spec.ts +++ b/packages/client/lib/commands/RENAME.spec.ts @@ -1,21 +1,24 @@ -import { strict as assert } from 'assert'; +import { strict as assert } from 'node:assert'; import testUtils, { GLOBAL } from '../test-utils'; -import { transformArguments } from './RENAME'; +import RENAME from './RENAME'; describe('RENAME', () => { - it('transformArguments', () => { - assert.deepEqual( - transformArguments('from', 'to'), - ['RENAME', 'from', 'to'] - ); - }); + it('transformArguments', () => { + assert.deepEqual( + RENAME.transformArguments('source', 'destination'), + ['RENAME', 'source', 'destination'] + ); + }); - testUtils.testWithClient('client.rename', async client => { - await client.set('from', 'value'); - - assert.equal( - await client.rename('from', 'to'), - 'OK' - ); - }, GLOBAL.SERVERS.OPEN); + testUtils.testAll('rename', async client => { + const [, reply] = await Promise.all([ + client.set('{tag}source', 'value'), + client.rename('{tag}source', '{tag}destination') + ]); + + assert.equal(reply, 'OK'); + }, { + client: GLOBAL.SERVERS.OPEN, + cluster: GLOBAL.CLUSTERS.OPEN + }); }); diff --git a/packages/client/lib/commands/RENAME.ts b/packages/client/lib/commands/RENAME.ts index 2d1134084fb..16e883d0532 100644 --- a/packages/client/lib/commands/RENAME.ts +++ b/packages/client/lib/commands/RENAME.ts @@ -1,12 +1,10 @@ -import { RedisCommandArgument, RedisCommandArguments } from '.'; +import { RedisArgument, SimpleStringReply, Command } from '../RESP/types'; -export const FIRST_KEY_INDEX = 1; - -export function transformArguments( - key: RedisCommandArgument, - newKey: RedisCommandArgument -): RedisCommandArguments { +export default { + FIRST_KEY_INDEX: 1, + IS_READ_ONLY: true, + transformArguments(key: RedisArgument, newKey: RedisArgument) { return ['RENAME', key, newKey]; -} - -export declare function transformReply(): RedisCommandArgument; + }, + transformReply: undefined as unknown as () => SimpleStringReply +} as const satisfies Command; diff --git a/packages/client/lib/commands/RENAMENX.spec.ts b/packages/client/lib/commands/RENAMENX.spec.ts index 6345eb5bd09..5f83933e957 100644 --- a/packages/client/lib/commands/RENAMENX.spec.ts +++ b/packages/client/lib/commands/RENAMENX.spec.ts @@ -1,21 +1,24 @@ -import { strict as assert } from 'assert'; +import { strict as assert } from 'node:assert'; import testUtils, { GLOBAL } from '../test-utils'; -import { transformArguments } from './RENAMENX'; +import RENAMENX from './RENAMENX'; describe('RENAMENX', () => { - it('transformArguments', () => { - assert.deepEqual( - transformArguments('from', 'to'), - ['RENAMENX', 'from', 'to'] - ); - }); + it('transformArguments', () => { + assert.deepEqual( + RENAMENX.transformArguments('source', 'destination'), + ['RENAMENX', 'source', 'destination'] + ); + }); - testUtils.testWithClient('client.renameNX', async client => { - await client.set('from', 'value'); + testUtils.testAll('renameNX', async client => { + const [, reply] = await Promise.all([ + client.set('{tag}source', 'value'), + client.renameNX('{tag}source', '{tag}destination') + ]); - assert.equal( - await client.renameNX('from', 'to'), - true - ); - }, GLOBAL.SERVERS.OPEN); + assert.equal(reply, 1); + }, { + client: GLOBAL.SERVERS.OPEN, + cluster: GLOBAL.CLUSTERS.OPEN + }); }); diff --git a/packages/client/lib/commands/RENAMENX.ts b/packages/client/lib/commands/RENAMENX.ts index 322ff0a88cc..3a4f155d5a7 100644 --- a/packages/client/lib/commands/RENAMENX.ts +++ b/packages/client/lib/commands/RENAMENX.ts @@ -1,12 +1,10 @@ -import { RedisCommandArgument, RedisCommandArguments } from '.'; +import { RedisArgument, NumberReply, Command } from '../RESP/types'; -export const FIRST_KEY_INDEX = 1; - -export function transformArguments( - key: RedisCommandArgument, - newKey: RedisCommandArgument -): RedisCommandArguments { +export default { + FIRST_KEY_INDEX: 1, + IS_READ_ONLY: true, + transformArguments(key: RedisArgument, newKey: RedisArgument) { return ['RENAMENX', key, newKey]; -} - -export { transformBooleanReply as transformReply } from './generic-transformers'; + }, + transformReply: undefined as unknown as () => NumberReply +} as const satisfies Command; diff --git a/packages/client/lib/commands/REPLICAOF.spec.ts b/packages/client/lib/commands/REPLICAOF.spec.ts index ab1906944cf..77dbe060450 100644 --- a/packages/client/lib/commands/REPLICAOF.spec.ts +++ b/packages/client/lib/commands/REPLICAOF.spec.ts @@ -1,11 +1,11 @@ -import { strict as assert } from 'assert'; -import { transformArguments } from './REPLICAOF'; +import { strict as assert } from 'node:assert'; +import REPLICAOF from './REPLICAOF'; describe('REPLICAOF', () => { - it('transformArguments', () => { - assert.deepEqual( - transformArguments('host', 1), - ['REPLICAOF', 'host', '1'] - ); - }); + it('transformArguments', () => { + assert.deepEqual( + REPLICAOF.transformArguments('host', 1), + ['REPLICAOF', 'host', '1'] + ); + }); }); diff --git a/packages/client/lib/commands/REPLICAOF.ts b/packages/client/lib/commands/REPLICAOF.ts index bd452e0f371..4e2f69f7265 100644 --- a/packages/client/lib/commands/REPLICAOF.ts +++ b/packages/client/lib/commands/REPLICAOF.ts @@ -1,5 +1,10 @@ -export function transformArguments(host: string, port: number): Array { - return ['REPLICAOF', host, port.toString()]; -} +import { SimpleStringReply, Command } from '../RESP/types'; -export declare function transformReply(): string; +export default { + FIRST_KEY_INDEX: undefined, + IS_READ_ONLY: true, + transformArguments(host: string, port: number) { + return ['REPLICAOF', host, port.toString()]; + }, + transformReply: undefined as unknown as () => SimpleStringReply +} as const satisfies Command; diff --git a/packages/client/lib/commands/RESTORE-ASKING.spec.ts b/packages/client/lib/commands/RESTORE-ASKING.spec.ts index de9fce5c628..855196b9776 100644 --- a/packages/client/lib/commands/RESTORE-ASKING.spec.ts +++ b/packages/client/lib/commands/RESTORE-ASKING.spec.ts @@ -1,11 +1,11 @@ -import { strict as assert } from 'assert'; -import { transformArguments } from './RESTORE-ASKING'; +import { strict as assert } from 'node:assert'; +import RESTORE_ASKING from './RESTORE-ASKING'; describe('RESTORE-ASKING', () => { - it('transformArguments', () => { - assert.deepEqual( - transformArguments(), - ['RESTORE-ASKING'] - ); - }); + it('transformArguments', () => { + assert.deepEqual( + RESTORE_ASKING.transformArguments(), + ['RESTORE-ASKING'] + ); + }); }); diff --git a/packages/client/lib/commands/RESTORE-ASKING.ts b/packages/client/lib/commands/RESTORE-ASKING.ts index d53d8541cd7..14f6dcbeab3 100644 --- a/packages/client/lib/commands/RESTORE-ASKING.ts +++ b/packages/client/lib/commands/RESTORE-ASKING.ts @@ -1,5 +1,10 @@ -export function transformArguments(): Array { - return ['RESTORE-ASKING']; -} +import { SimpleStringReply, Command } from '../RESP/types'; -export declare function transformReply(): string; +export default { + FIRST_KEY_INDEX: undefined, + IS_READ_ONLY: true, + transformArguments() { + return ['RESTORE-ASKING']; + }, + transformReply: undefined as unknown as () => SimpleStringReply +} as const satisfies Command; diff --git a/packages/client/lib/commands/RESTORE.spec.ts b/packages/client/lib/commands/RESTORE.spec.ts index 89d42f3d4de..6b814e7325a 100644 --- a/packages/client/lib/commands/RESTORE.spec.ts +++ b/packages/client/lib/commands/RESTORE.spec.ts @@ -1,74 +1,84 @@ import { strict as assert } from 'assert'; import testUtils, { GLOBAL } from '../test-utils'; -import { transformArguments } from './RESTORE'; +import RESTORE from './RESTORE'; +import { RESP_TYPES } from '../RESP/decoder'; describe('RESTORE', () => { - describe('transformArguments', () => { - it('simple', () => { - assert.deepEqual( - transformArguments('key', 0, 'value'), - ['RESTORE', 'key', '0', 'value'] - ); - }); + describe('transformArguments', () => { + it('simple', () => { + assert.deepEqual( + RESTORE.transformArguments('key', 0, 'value'), + ['RESTORE', 'key', '0', 'value'] + ); + }); - it('with REPLACE', () => { - assert.deepEqual( - transformArguments('key', 0, 'value', { - REPLACE: true - }), - ['RESTORE', 'key', '0', 'value', 'REPLACE'] - ); - }); + it('with REPLACE', () => { + assert.deepEqual( + RESTORE.transformArguments('key', 0, 'value', { + REPLACE: true + }), + ['RESTORE', 'key', '0', 'value', 'REPLACE'] + ); + }); - it('with ABSTTL', () => { - assert.deepEqual( - transformArguments('key', 0, 'value', { - ABSTTL: true - }), - ['RESTORE', 'key', '0', 'value', 'ABSTTL'] - ); - }); + it('with ABSTTL', () => { + assert.deepEqual( + RESTORE.transformArguments('key', 0, 'value', { + ABSTTL: true + }), + ['RESTORE', 'key', '0', 'value', 'ABSTTL'] + ); + }); - it('with IDLETIME', () => { - assert.deepEqual( - transformArguments('key', 0, 'value', { - IDLETIME: 1 - }), - ['RESTORE', 'key', '0', 'value', 'IDLETIME', '1'] - ); - }); + it('with IDLETIME', () => { + assert.deepEqual( + RESTORE.transformArguments('key', 0, 'value', { + IDLETIME: 1 + }), + ['RESTORE', 'key', '0', 'value', 'IDLETIME', '1'] + ); + }); - it('with FREQ', () => { - assert.deepEqual( - transformArguments('key', 0, 'value', { - FREQ: 1 - }), - ['RESTORE', 'key', '0', 'value', 'FREQ', '1'] - ); - }); + it('with FREQ', () => { + assert.deepEqual( + RESTORE.transformArguments('key', 0, 'value', { + FREQ: 1 + }), + ['RESTORE', 'key', '0', 'value', 'FREQ', '1'] + ); + }); - it('with REPLACE, ABSTTL, IDLETIME and FREQ', () => { - assert.deepEqual( - transformArguments('key', 0, 'value', { - REPLACE: true, - ABSTTL: true, - IDLETIME: 1, - FREQ: 2 - }), - ['RESTORE', 'key', '0', 'value', 'REPLACE', 'ABSTTL', 'IDLETIME', '1', 'FREQ', '2'] - ); - }); + it('with REPLACE, ABSTTL, IDLETIME and FREQ', () => { + assert.deepEqual( + RESTORE.transformArguments('key', 0, 'value', { + REPLACE: true, + ABSTTL: true, + IDLETIME: 1, + FREQ: 2 + }), + ['RESTORE', 'key', '0', 'value', 'REPLACE', 'ABSTTL', 'IDLETIME', '1', 'FREQ', '2'] + ); }); + }); - testUtils.testWithClient('client.restore', async client => { - const [, dump] = await Promise.all([ - client.set('source', 'value'), - client.dump(client.commandOptions({ returnBuffers: true }), 'source') - ]); + testUtils.testWithClient('client.restore', async client => { + const [, dump] = await Promise.all([ + client.set('source', 'value'), + client.dump('source') + ]); - assert.equal( - await client.restore('destination', 0, dump), - 'OK' - ); - }, GLOBAL.SERVERS.OPEN); + assert.equal( + await client.restore('destination', 0, dump), + 'OK' + ); + }, { + ...GLOBAL.SERVERS.OPEN, + clientOptions: { + commandOptions: { + typeMapping: { + [RESP_TYPES.BLOB_STRING]: Buffer + } + } + } + }); }); diff --git a/packages/client/lib/commands/RESTORE.ts b/packages/client/lib/commands/RESTORE.ts index d9ac11c424b..b24c5b569f9 100644 --- a/packages/client/lib/commands/RESTORE.ts +++ b/packages/client/lib/commands/RESTORE.ts @@ -1,39 +1,40 @@ -import { RedisCommandArgument, RedisCommandArguments } from '.'; +import { RedisArgument, SimpleStringReply, Command } from '../RESP/types'; -export const FIRST_KEY_INDEX = 1; - -interface RestoreOptions { - REPLACE?: true; - ABSTTL?: true; - IDLETIME?: number; - FREQ?: number; +export interface RestoreOptions { + REPLACE?: boolean; + ABSTTL?: boolean; + IDLETIME?: number; + FREQ?: number; } -export function transformArguments( - key: RedisCommandArgument, +export default { + FIRST_KEY_INDEX: 1, + IS_READ_ONLY: false, + transformArguments( + key: RedisArgument, ttl: number, - serializedValue: RedisCommandArgument, + serializedValue: RedisArgument, options?: RestoreOptions -): RedisCommandArguments { + ) { const args = ['RESTORE', key, ttl.toString(), serializedValue]; if (options?.REPLACE) { - args.push('REPLACE'); + args.push('REPLACE'); } if (options?.ABSTTL) { - args.push('ABSTTL'); + args.push('ABSTTL'); } if (options?.IDLETIME) { - args.push('IDLETIME', options.IDLETIME.toString()); + args.push('IDLETIME', options.IDLETIME.toString()); } if (options?.FREQ) { - args.push('FREQ', options.FREQ.toString()); + args.push('FREQ', options.FREQ.toString()); } return args; -} - -export declare function transformReply(): 'OK'; + }, + transformReply: undefined as unknown as () => SimpleStringReply<'OK'> +} as const satisfies Command; diff --git a/packages/client/lib/commands/ROLE.spec.ts b/packages/client/lib/commands/ROLE.spec.ts index 2e6d9b163ae..c57ba5ba1f0 100644 --- a/packages/client/lib/commands/ROLE.spec.ts +++ b/packages/client/lib/commands/ROLE.spec.ts @@ -1,69 +1,69 @@ -import { strict as assert } from 'assert'; +import { strict as assert } from 'node:assert'; import testUtils, { GLOBAL } from '../test-utils'; -import { transformArguments, transformReply } from './ROLE'; +import ROLE from './ROLE'; describe('ROLE', () => { - it('transformArguments', () => { - assert.deepEqual( - transformArguments(), - ['ROLE'] - ); - }); + it('transformArguments', () => { + assert.deepEqual( + ROLE.transformArguments(), + ['ROLE'] + ); + }); - describe('transformReply', () => { - it('master', () => { - assert.deepEqual( - transformReply(['master', 3129659, [['127.0.0.1', '9001', '3129242'], ['127.0.0.1', '9002', '3129543']]]), - { - role: 'master', - replicationOffest: 3129659, - replicas: [{ - ip: '127.0.0.1', - port: 9001, - replicationOffest: 3129242 - }, { - ip: '127.0.0.1', - port: 9002, - replicationOffest: 3129543 - }] - } - ); - }); + describe('transformReply', () => { + it('master', () => { + assert.deepEqual( + ROLE.transformReply(['master', 3129659, [['127.0.0.1', '9001', '3129242'], ['127.0.0.1', '9002', '3129543']]] as any), + { + role: 'master', + replicationOffest: 3129659, + replicas: [{ + host: '127.0.0.1', + port: 9001, + replicationOffest: 3129242 + }, { + host: '127.0.0.1', + port: 9002, + replicationOffest: 3129543 + }] + } + ); + }); - it('replica', () => { - assert.deepEqual( - transformReply(['slave', '127.0.0.1', 9000, 'connected', 3167038]), - { - role: 'slave', - master: { - ip: '127.0.0.1', - port: 9000 - }, - state: 'connected', - dataReceived: 3167038 - } - ); - }); + it('replica', () => { + assert.deepEqual( + ROLE.transformReply(['slave', '127.0.0.1', 9000, 'connected', 3167038] as any), + { + role: 'slave', + master: { + host: '127.0.0.1', + port: 9000 + }, + state: 'connected', + dataReceived: 3167038 + } + ); + }); - it('sentinel', () => { - assert.deepEqual( - transformReply(['sentinel', ['resque-master', 'html-fragments-master', 'stats-master', 'metadata-master']]), - { - role: 'sentinel', - masterNames: ['resque-master', 'html-fragments-master', 'stats-master', 'metadata-master'] - } - ); - }); + it('sentinel', () => { + assert.deepEqual( + ROLE.transformReply(['sentinel', ['resque-master', 'html-fragments-master', 'stats-master', 'metadata-master']] as any), + { + role: 'sentinel', + masterNames: ['resque-master', 'html-fragments-master', 'stats-master', 'metadata-master'] + } + ); }); + }); - testUtils.testWithClient('client.role', async client => { - assert.deepEqual( - await client.role(), - { - role: 'master', - replicationOffest: 0, - replicas: [] - } - ); - }, GLOBAL.SERVERS.OPEN); + testUtils.testWithClient('client.role', async client => { + assert.deepEqual( + await client.role(), + { + role: 'master', + replicationOffest: 0, + replicas: [] + } + ); + }, GLOBAL.SERVERS.OPEN); }); diff --git a/packages/client/lib/commands/ROLE.ts b/packages/client/lib/commands/ROLE.ts index b1d6041fdfa..7828e53fb61 100644 --- a/packages/client/lib/commands/ROLE.ts +++ b/packages/client/lib/commands/ROLE.ts @@ -1,75 +1,70 @@ -export const IS_READ_ONLY = true; +import { BlobStringReply, NumberReply, ArrayReply, TuplesReply, UnwrapReply, Command } from '../RESP/types'; -export function transformArguments(): Array { - return ['ROLE']; -} - -interface RoleReplyInterface { - role: T; -} - -type RoleMasterRawReply = ['master', number, Array<[string, string, string]>]; - -interface RoleMasterReply extends RoleReplyInterface<'master'> { - replicationOffest: number; - replicas: Array<{ - ip: string; - port: number; - replicationOffest: number; - }>; -} - -type RoleReplicaState = 'connect' | 'connecting' | 'sync' | 'connected'; - -type RoleReplicaRawReply = ['slave', string, number, RoleReplicaState, number]; - -interface RoleReplicaReply extends RoleReplyInterface<'slave'> { - master: { - ip: string; - port: number; - }; - state: RoleReplicaState; - dataReceived: number; -} +type MasterRole = [ + role: BlobStringReply<'master'>, + replicationOffest: NumberReply, + replicas: ArrayReply> +]; -type RoleSentinelRawReply = ['sentinel', Array]; +type SlaveRole = [ + role: BlobStringReply<'slave'>, + masterHost: BlobStringReply, + masterPort: NumberReply, + state: BlobStringReply<'connect' | 'connecting' | 'sync' | 'connected'>, + dataReceived: NumberReply +]; -interface RoleSentinelReply extends RoleReplyInterface<'sentinel'> { - masterNames: Array; -} +type SentinelRole = [ + role: BlobStringReply<'sentinel'>, + masterNames: ArrayReply +]; -type RoleRawReply = RoleMasterRawReply | RoleReplicaRawReply | RoleSentinelRawReply; +type Role = TuplesReply; -type RoleReply = RoleMasterReply | RoleReplicaReply | RoleSentinelReply; - -export function transformReply(reply: RoleRawReply): RoleReply { - switch (reply[0]) { - case 'master': +export default { + FIRST_KEY_INDEX: undefined, + IS_READ_ONLY: true, + transformArguments() { + return ['ROLE']; + }, + transformReply(reply: UnwrapReply) { + switch (reply[0] as unknown as UnwrapReply) { + case 'master': { + const [role, replicationOffest, replicas] = reply as MasterRole; + return { + role, + replicationOffest, + replicas: (replicas as unknown as UnwrapReply).map(replica => { + const [host, port, replicationOffest] = replica as unknown as UnwrapReply; return { - role: 'master', - replicationOffest: reply[1], - replicas: reply[2].map(([ip, port, replicationOffest]) => ({ - ip, - port: Number(port), - replicationOffest: Number(replicationOffest) - })) + host, + port: Number(port), + replicationOffest: Number(replicationOffest) }; + }) + }; + } - case 'slave': - return { - role: 'slave', - master: { - ip: reply[1], - port: reply[2] - }, - state: reply[3], - dataReceived: reply[4] - }; + case 'slave': { + const [role, masterHost, masterPort, state, dataReceived] = reply as SlaveRole; + return { + role, + master: { + host: masterHost, + port: masterPort + }, + state, + dataReceived, + }; + } - case 'sentinel': - return { - role: 'sentinel', - masterNames: reply[1] - }; + case 'sentinel': { + const [role, masterNames] = reply as SentinelRole; + return { + role, + masterNames + }; + } } -} + } +} as const satisfies Command; diff --git a/packages/client/lib/commands/RPOP.spec.ts b/packages/client/lib/commands/RPOP.spec.ts index 6e57afa3216..8ac5cb290f4 100644 --- a/packages/client/lib/commands/RPOP.spec.ts +++ b/packages/client/lib/commands/RPOP.spec.ts @@ -1,26 +1,22 @@ -import { strict as assert } from 'assert'; +import { strict as assert } from 'node:assert'; import testUtils, { GLOBAL } from '../test-utils'; -import { transformArguments } from './RPOP'; +import RPOP from './RPOP'; describe('RPOP', () => { - it('transformArguments', () => { - assert.deepEqual( - transformArguments('key'), - ['RPOP', 'key'] - ); - }); + it('transformArguments', () => { + assert.deepEqual( + RPOP.transformArguments('key'), + ['RPOP', 'key'] + ); + }); - testUtils.testWithClient('client.rPop', async client => { - assert.equal( - await client.rPop('key'), - null - ); - }, GLOBAL.SERVERS.OPEN); - - testUtils.testWithCluster('cluster.rPop', async cluster => { - assert.equal( - await cluster.rPop('key'), - null - ); - }, GLOBAL.CLUSTERS.OPEN); + testUtils.testAll('rPop', async client => { + assert.equal( + await client.rPop('key'), + null + ); + }, { + client: GLOBAL.SERVERS.OPEN, + cluster: GLOBAL.CLUSTERS.OPEN + }); }); diff --git a/packages/client/lib/commands/RPOP.ts b/packages/client/lib/commands/RPOP.ts index ed696b6d522..f7d0b33d3af 100644 --- a/packages/client/lib/commands/RPOP.ts +++ b/packages/client/lib/commands/RPOP.ts @@ -1,9 +1,9 @@ -import { RedisCommandArgument, RedisCommandArguments } from '.'; +import { RedisArgument, BlobStringReply, NullReply, Command } from '../RESP/types'; -export const FIRST_KEY_INDEX = 1; - -export function transformArguments(key: RedisCommandArgument): RedisCommandArguments { +export default { + FIRST_KEY_INDEX: 1, + transformArguments(key: RedisArgument) { return ['RPOP', key]; -} - -export declare function transformReply(): RedisCommandArgument | null; + }, + transformReply: undefined as unknown as () => BlobStringReply | NullReply +} as const satisfies Command; diff --git a/packages/client/lib/commands/RPOPLPUSH.spec.ts b/packages/client/lib/commands/RPOPLPUSH.spec.ts index cef3049bd91..59458fc0aa8 100644 --- a/packages/client/lib/commands/RPOPLPUSH.spec.ts +++ b/packages/client/lib/commands/RPOPLPUSH.spec.ts @@ -1,26 +1,22 @@ -import { strict as assert } from 'assert'; +import { strict as assert } from 'node:assert'; import testUtils, { GLOBAL } from '../test-utils'; -import { transformArguments } from './RPOPLPUSH'; +import RPOPLPUSH from './RPOPLPUSH'; describe('RPOPLPUSH', () => { - it('transformArguments', () => { - assert.deepEqual( - transformArguments('source', 'destination'), - ['RPOPLPUSH', 'source', 'destination'] - ); - }); + it('transformArguments', () => { + assert.deepEqual( + RPOPLPUSH.transformArguments('source', 'destination'), + ['RPOPLPUSH', 'source', 'destination'] + ); + }); - testUtils.testWithClient('client.rPopLPush', async client => { - assert.equal( - await client.rPopLPush('source', 'destination'), - null - ); - }, GLOBAL.SERVERS.OPEN); - - testUtils.testWithCluster('cluster.rPopLPush', async cluster => { - assert.equal( - await cluster.rPopLPush('{tag}source', '{tag}destination'), - null - ); - }, GLOBAL.CLUSTERS.OPEN); + testUtils.testAll('rPopLPush', async client => { + assert.equal( + await client.rPopLPush('{tag}source', '{tag}destination'), + null + ); + }, { + client: GLOBAL.SERVERS.OPEN, + cluster: GLOBAL.CLUSTERS.OPEN + }); }); diff --git a/packages/client/lib/commands/RPOPLPUSH.ts b/packages/client/lib/commands/RPOPLPUSH.ts index da45f6f6024..1a5e1cc59bc 100644 --- a/packages/client/lib/commands/RPOPLPUSH.ts +++ b/packages/client/lib/commands/RPOPLPUSH.ts @@ -1,12 +1,12 @@ -import { RedisCommandArgument, RedisCommandArguments } from '.'; +import { RedisArgument, BlobStringReply, NullReply, Command } from '../RESP/types'; -export const FIRST_KEY_INDEX = 1; - -export function transformArguments( - source: RedisCommandArgument, - destination: RedisCommandArgument -): RedisCommandArguments { +export default { + FIRST_KEY_INDEX: 1, + transformArguments( + source: RedisArgument, + destination: RedisArgument + ) { return ['RPOPLPUSH', source, destination]; -} - -export declare function transformReply(): RedisCommandArgument | null; + }, + transformReply: undefined as unknown as () => BlobStringReply | NullReply +} as const satisfies Command; diff --git a/packages/client/lib/commands/RPOP_COUNT.spec.ts b/packages/client/lib/commands/RPOP_COUNT.spec.ts index 3657a608039..14f1854b8bc 100644 --- a/packages/client/lib/commands/RPOP_COUNT.spec.ts +++ b/packages/client/lib/commands/RPOP_COUNT.spec.ts @@ -1,28 +1,24 @@ -import { strict as assert } from 'assert'; +import { strict as assert } from 'node:assert'; import testUtils, { GLOBAL } from '../test-utils'; -import { transformArguments } from './RPOP_COUNT'; +import RPOP_COUNT from './RPOP_COUNT'; describe('RPOP COUNT', () => { - testUtils.isVersionGreaterThanHook([6, 2]); + testUtils.isVersionGreaterThanHook([6, 2]); - it('transformArguments', () => { - assert.deepEqual( - transformArguments('key', 1), - ['RPOP', 'key', '1'] - ); - }); + it('transformArguments', () => { + assert.deepEqual( + RPOP_COUNT.transformArguments('key', 1), + ['RPOP', 'key', '1'] + ); + }); - testUtils.testWithClient('client.rPopCount', async client => { - assert.equal( - await client.rPopCount('key', 1), - null - ); - }, GLOBAL.SERVERS.OPEN); - - testUtils.testWithCluster('cluster.rPopCount', async cluster => { - assert.equal( - await cluster.rPopCount('key', 1), - null - ); - }, GLOBAL.CLUSTERS.OPEN); + testUtils.testAll('rPopCount', async client => { + assert.equal( + await client.rPopCount('key', 1), + null + ); + }, { + client: GLOBAL.SERVERS.OPEN, + cluster: GLOBAL.CLUSTERS.OPEN + }); }); diff --git a/packages/client/lib/commands/RPOP_COUNT.ts b/packages/client/lib/commands/RPOP_COUNT.ts index b3bc778ee5c..b60dec6ab9d 100644 --- a/packages/client/lib/commands/RPOP_COUNT.ts +++ b/packages/client/lib/commands/RPOP_COUNT.ts @@ -1,12 +1,9 @@ -import { RedisCommandArgument, RedisCommandArguments } from '.'; +import { RedisArgument, ArrayReply, BlobStringReply, NullReply, Command } from '../RESP/types'; -export const FIRST_KEY_INDEX = 1; - -export function transformArguments( - key: RedisCommandArgument, - count: number -): RedisCommandArguments { +export default { + FIRST_KEY_INDEX: 1, + transformArguments(key: RedisArgument, count: number) { return ['RPOP', key, count.toString()]; -} - -export declare function transformReply(): Array | null; + }, + transformReply: undefined as unknown as () => ArrayReply | NullReply +} as const satisfies Command; diff --git a/packages/client/lib/commands/RPUSH.spec.ts b/packages/client/lib/commands/RPUSH.spec.ts index afa5c1c6400..06078d75951 100644 --- a/packages/client/lib/commands/RPUSH.spec.ts +++ b/packages/client/lib/commands/RPUSH.spec.ts @@ -1,35 +1,31 @@ -import { strict as assert } from 'assert'; +import { strict as assert } from 'node:assert'; import testUtils, { GLOBAL } from '../test-utils'; -import { transformArguments } from './RPUSH'; +import RPUSH from './RPUSH'; describe('RPUSH', () => { - describe('transformArguments', () => { - it('string', () => { - assert.deepEqual( - transformArguments('key', 'element'), - ['RPUSH', 'key', 'element'] - ); - }); - - it('array', () => { - assert.deepEqual( - transformArguments('key', ['1', '2']), - ['RPUSH', 'key', '1', '2'] - ); - }); + describe('transformArguments', () => { + it('string', () => { + assert.deepEqual( + RPUSH.transformArguments('key', 'element'), + ['RPUSH', 'key', 'element'] + ); }); - testUtils.testWithClient('client.rPush', async client => { - assert.equal( - await client.rPush('key', 'element'), - 1 - ); - }, GLOBAL.SERVERS.OPEN); + it('array', () => { + assert.deepEqual( + RPUSH.transformArguments('key', ['1', '2']), + ['RPUSH', 'key', '1', '2'] + ); + }); + }); - testUtils.testWithCluster('cluster.rPush', async cluster => { - assert.equal( - await cluster.rPush('key', 'element'), - 1 - ); - }, GLOBAL.CLUSTERS.OPEN); + testUtils.testAll('rPush', async client => { + assert.equal( + await client.rPush('key', 'element'), + 1 + ); + }, { + client: GLOBAL.SERVERS.OPEN, + cluster: GLOBAL.CLUSTERS.OPEN + }); }); diff --git a/packages/client/lib/commands/RPUSH.ts b/packages/client/lib/commands/RPUSH.ts index 15e282f0892..4b048777389 100644 --- a/packages/client/lib/commands/RPUSH.ts +++ b/packages/client/lib/commands/RPUSH.ts @@ -1,13 +1,13 @@ -import { RedisCommandArgument, RedisCommandArguments } from '.'; -import { pushVerdictArguments } from './generic-transformers'; +import { RedisArgument, NumberReply, Command } from '../RESP/types'; +import { RedisVariadicArgument, pushVariadicArguments } from './generic-transformers'; -export const FIRST_KEY_INDEX = 1; - -export function transformArguments( - key: RedisCommandArgument, - element: RedisCommandArgument | Array -): RedisCommandArguments { - return pushVerdictArguments(['RPUSH', key], element); -} - -export declare function transformReply(): number; +export default { + FIRST_KEY_INDEX: 1, + transformArguments( + key: RedisArgument, + element: RedisVariadicArgument + ) { + return pushVariadicArguments(['RPUSH', key], element); + }, + transformReply: undefined as unknown as () => NumberReply +} as const satisfies Command; diff --git a/packages/client/lib/commands/RPUSHX.spec.ts b/packages/client/lib/commands/RPUSHX.spec.ts index ee2041de6f2..5adaa8b263a 100644 --- a/packages/client/lib/commands/RPUSHX.spec.ts +++ b/packages/client/lib/commands/RPUSHX.spec.ts @@ -1,35 +1,31 @@ -import { strict as assert } from 'assert'; +import { strict as assert } from 'node:assert'; import testUtils, { GLOBAL } from '../test-utils'; -import { transformArguments } from './RPUSHX'; +import RPUSHX from './RPUSHX'; describe('RPUSHX', () => { - describe('transformArguments', () => { - it('string', () => { - assert.deepEqual( - transformArguments('key', 'element'), - ['RPUSHX', 'key', 'element'] - ); - }); - - it('array', () => { - assert.deepEqual( - transformArguments('key', ['1', '2']), - ['RPUSHX', 'key', '1', '2'] - ); - }); + describe('transformArguments', () => { + it('string', () => { + assert.deepEqual( + RPUSHX.transformArguments('key', 'element'), + ['RPUSHX', 'key', 'element'] + ); }); - testUtils.testWithClient('client.rPushX', async client => { - assert.equal( - await client.rPushX('key', 'element'), - 0 - ); - }, GLOBAL.SERVERS.OPEN); + it('array', () => { + assert.deepEqual( + RPUSHX.transformArguments('key', ['1', '2']), + ['RPUSHX', 'key', '1', '2'] + ); + }); + }); - testUtils.testWithCluster('cluster.rPushX', async cluster => { - assert.equal( - await cluster.rPushX('key', 'element'), - 0 - ); - }, GLOBAL.CLUSTERS.OPEN); + testUtils.testAll('rPushX', async client => { + assert.equal( + await client.rPushX('key', 'element'), + 0 + ); + }, { + client: GLOBAL.SERVERS.OPEN, + cluster: GLOBAL.CLUSTERS.OPEN + }); }); diff --git a/packages/client/lib/commands/RPUSHX.ts b/packages/client/lib/commands/RPUSHX.ts index 29253cd6edb..00b29624b0d 100644 --- a/packages/client/lib/commands/RPUSHX.ts +++ b/packages/client/lib/commands/RPUSHX.ts @@ -1,13 +1,13 @@ -import { RedisCommandArgument, RedisCommandArguments } from '.'; -import { pushVerdictArguments } from './generic-transformers'; +import { RedisArgument, NumberReply, Command } from '../RESP/types'; +import { RedisVariadicArgument, pushVariadicArguments } from './generic-transformers'; -export const FIRST_KEY_INDEX = 1; - -export function transformArguments( - key: RedisCommandArgument, - element: RedisCommandArgument | Array -): RedisCommandArguments { - return pushVerdictArguments(['RPUSHX', key], element); -} - -export declare function transformReply(): number; +export default { + FIRST_KEY_INDEX: 1, + transformArguments( + key: RedisArgument, + element: RedisVariadicArgument + ) { + return pushVariadicArguments(['RPUSHX', key], element); + }, + transformReply: undefined as unknown as () => NumberReply +} as const satisfies Command; diff --git a/packages/client/lib/commands/SADD.spec.ts b/packages/client/lib/commands/SADD.spec.ts index 4533f6f9ad5..77adc1c18ce 100644 --- a/packages/client/lib/commands/SADD.spec.ts +++ b/packages/client/lib/commands/SADD.spec.ts @@ -1,28 +1,31 @@ -import { strict as assert } from 'assert'; +import { strict as assert } from 'node:assert'; import testUtils, { GLOBAL } from '../test-utils'; -import { transformArguments } from './SADD'; +import SADD from './SADD'; describe('SADD', () => { - describe('transformArguments', () => { - it('string', () => { - assert.deepEqual( - transformArguments('key', 'member'), - ['SADD', 'key', 'member'] - ); - }); + describe('transformArguments', () => { + it('string', () => { + assert.deepEqual( + SADD.transformArguments('key', 'member'), + ['SADD', 'key', 'member'] + ); + }); - it('array', () => { - assert.deepEqual( - transformArguments('key', ['1', '2']), - ['SADD', 'key', '1', '2'] - ); - }); + it('array', () => { + assert.deepEqual( + SADD.transformArguments('key', ['1', '2']), + ['SADD', 'key', '1', '2'] + ); }); + }); - testUtils.testWithClient('client.sAdd', async client => { - assert.equal( - await client.sAdd('key', 'member'), - 1 - ); - }, GLOBAL.SERVERS.OPEN); + testUtils.testAll('sAdd', async client => { + assert.equal( + await client.sAdd('key', 'member'), + 1 + ); + }, { + client: GLOBAL.SERVERS.OPEN, + cluster: GLOBAL.CLUSTERS.OPEN + }); }); diff --git a/packages/client/lib/commands/SADD.ts b/packages/client/lib/commands/SADD.ts index 7d7121e5391..2ff5e9263c3 100644 --- a/packages/client/lib/commands/SADD.ts +++ b/packages/client/lib/commands/SADD.ts @@ -1,13 +1,10 @@ -import { RedisCommandArgument, RedisCommandArguments } from '.'; -import { pushVerdictArguments } from './generic-transformers'; +import { RedisArgument, NumberReply, Command } from '../RESP/types'; +import { RedisVariadicArgument, pushVariadicArguments } from './generic-transformers'; -export const FIRST_KEY_INDEX = 1; - -export function transformArguments( - key: RedisCommandArgument, - members: RedisCommandArgument | Array -): RedisCommandArguments { - return pushVerdictArguments(['SADD', key], members); -} - -export declare function transformReply(): number; +export default { + FIRST_KEY_INDEX: 1, + transformArguments(key: RedisArgument, members: RedisVariadicArgument) { + return pushVariadicArguments(['SADD', key], members); + }, + transformReply: undefined as unknown as () => NumberReply +} as const satisfies Command; diff --git a/packages/client/lib/commands/SAVE.spec.ts b/packages/client/lib/commands/SAVE.spec.ts index 1e1987b5ab8..5c014da7edb 100644 --- a/packages/client/lib/commands/SAVE.spec.ts +++ b/packages/client/lib/commands/SAVE.spec.ts @@ -1,11 +1,11 @@ -import { strict as assert } from 'assert'; -import { transformArguments } from './SAVE'; +import { strict as assert } from 'node:assert'; +import SAVE from './SAVE'; describe('SAVE', () => { - it('transformArguments', () => { - assert.deepEqual( - transformArguments(), - ['SAVE'] - ); - }); + it('transformArguments', () => { + assert.deepEqual( + SAVE.transformArguments(), + ['SAVE'] + ); + }); }); diff --git a/packages/client/lib/commands/SAVE.ts b/packages/client/lib/commands/SAVE.ts index 3d75c29df90..ee6cccd35a0 100644 --- a/packages/client/lib/commands/SAVE.ts +++ b/packages/client/lib/commands/SAVE.ts @@ -1,7 +1,10 @@ -import { RedisCommandArgument } from '.'; +import { SimpleStringReply, Command } from '../RESP/types'; -export function transformArguments(): Array { +export default { + FIRST_KEY_INDEX: undefined, + IS_READ_ONLY: true, + transformArguments() { return ['SAVE']; -} - -export declare function transformReply(): RedisCommandArgument; + }, + transformReply: undefined as unknown as () => SimpleStringReply +} as const satisfies Command; diff --git a/packages/client/lib/commands/SCAN.spec.ts b/packages/client/lib/commands/SCAN.spec.ts index 7657b744e02..f4dd865d113 100644 --- a/packages/client/lib/commands/SCAN.spec.ts +++ b/packages/client/lib/commands/SCAN.spec.ts @@ -1,84 +1,62 @@ -import { strict as assert } from 'assert'; +import { strict as assert } from 'node:assert'; import testUtils, { GLOBAL } from '../test-utils'; -import { transformArguments, transformReply } from './SCAN'; +import SCAN from './SCAN'; describe('SCAN', () => { - describe('transformArguments', () => { - it('cusror only', () => { - assert.deepEqual( - transformArguments(0), - ['SCAN', '0'] - ); - }); - - it('with MATCH', () => { - assert.deepEqual( - transformArguments(0, { - MATCH: 'pattern' - }), - ['SCAN', '0', 'MATCH', 'pattern'] - ); - }); - - it('with COUNT', () => { - assert.deepEqual( - transformArguments(0, { - COUNT: 1 - }), - ['SCAN', '0', 'COUNT', '1'] - ); - }); - - it('with TYPE', () => { - assert.deepEqual( - transformArguments(0, { - TYPE: 'stream' - }), - ['SCAN', '0', 'TYPE', 'stream'] - ); - }); + describe('transformArguments', () => { + it('cusror only', () => { + assert.deepEqual( + SCAN.transformArguments('0'), + ['SCAN', '0'] + ); + }); - it('with MATCH & COUNT & TYPE', () => { - assert.deepEqual( - transformArguments(0, { - MATCH: 'pattern', - COUNT: 1, - TYPE: 'stream' - }), - ['SCAN', '0', 'MATCH', 'pattern', 'COUNT', '1', 'TYPE', 'stream'] - ); - }); + it('with MATCH', () => { + assert.deepEqual( + SCAN.transformArguments('0', { + MATCH: 'pattern' + }), + ['SCAN', '0', 'MATCH', 'pattern'] + ); }); - describe('transformReply', () => { - it('without keys', () => { - assert.deepEqual( - transformReply(['0', []]), - { - cursor: 0, - keys: [] - } - ); - }); + it('with COUNT', () => { + assert.deepEqual( + SCAN.transformArguments('0', { + COUNT: 1 + }), + ['SCAN', '0', 'COUNT', '1'] + ); + }); - it('with keys', () => { - assert.deepEqual( - transformReply(['0', ['key']]), - { - cursor: 0, - keys: ['key'] - } - ); - }); + it('with TYPE', () => { + assert.deepEqual( + SCAN.transformArguments('0', { + TYPE: 'stream' + }), + ['SCAN', '0', 'TYPE', 'stream'] + ); }); - testUtils.testWithClient('client.scan', async client => { - assert.deepEqual( - await client.scan(0), - { - cursor: 0, - keys: [] - } - ); - }, GLOBAL.SERVERS.OPEN); + it('with MATCH & COUNT & TYPE', () => { + assert.deepEqual( + SCAN.transformArguments('0', { + MATCH: 'pattern', + COUNT: 1, + TYPE: 'stream' + }), + ['SCAN', '0', 'MATCH', 'pattern', 'COUNT', '1', 'TYPE', 'stream'] + ); + }); + }); + + testUtils.testWithClient('client.scan', async client => { + assert.deepEqual( + await client.scan('0'), + { + cursor: '0', + keys: [] + } + ); + }, GLOBAL.SERVERS.OPEN); }); diff --git a/packages/client/lib/commands/SCAN.ts b/packages/client/lib/commands/SCAN.ts index ee5908eb9bd..13f54440443 100644 --- a/packages/client/lib/commands/SCAN.ts +++ b/packages/client/lib/commands/SCAN.ts @@ -1,34 +1,48 @@ -import { RedisCommandArgument, RedisCommandArguments } from '.'; -import { ScanOptions, pushScanArguments } from './generic-transformers'; +import { RedisArgument, CommandArguments, BlobStringReply, ArrayReply, Command } from '../RESP/types'; -export const IS_READ_ONLY = true; -export interface ScanCommandOptions extends ScanOptions { - TYPE?: RedisCommandArgument; +export interface ScanCommonOptions { + MATCH?: string; + COUNT?: number; } -export function transformArguments( - cursor: number, - options?: ScanCommandOptions -): RedisCommandArguments { - const args = pushScanArguments(['SCAN'], cursor, options); +export function pushScanArguments( + args: CommandArguments, + cursor: RedisArgument, + options?: ScanOptions +): CommandArguments { + args.push(cursor.toString()); - if (options?.TYPE) { - args.push('TYPE', options.TYPE); - } + if (options?.MATCH) { + args.push('MATCH', options.MATCH); + } - return args; -} + if (options?.COUNT) { + args.push('COUNT', options.COUNT.toString()); + } -type ScanRawReply = [string, Array]; + return args; +} -export interface ScanReply { - cursor: number; - keys: Array; +export interface ScanOptions extends ScanCommonOptions { + TYPE?: RedisArgument; } -export function transformReply([cursor, keys]: ScanRawReply): ScanReply { +export default { + FIRST_KEY_INDEX: undefined, + IS_READ_ONLY: true, + transformArguments(cursor: RedisArgument, options?: ScanOptions) { + const args = pushScanArguments(['SCAN'], cursor, options); + + if (options?.TYPE) { + args.push('TYPE', options.TYPE); + } + + return args; + }, + transformReply([cursor, keys]: [BlobStringReply, ArrayReply]) { return { - cursor: Number(cursor), - keys + cursor, + keys }; -} + } +} as const satisfies Command; diff --git a/packages/client/lib/commands/SCARD.spec.ts b/packages/client/lib/commands/SCARD.spec.ts index afc21c6b00c..5029f340d96 100644 --- a/packages/client/lib/commands/SCARD.spec.ts +++ b/packages/client/lib/commands/SCARD.spec.ts @@ -1,19 +1,22 @@ -import { strict as assert } from 'assert'; +import { strict as assert } from 'node:assert'; import testUtils, { GLOBAL } from '../test-utils'; -import { transformArguments } from './SCARD'; +import SCARD from './SCARD'; describe('SCARD', () => { - it('transformArguments', () => { - assert.deepEqual( - transformArguments('key'), - ['SCARD', 'key'] - ); - }); + it('transformArguments', () => { + assert.deepEqual( + SCARD.transformArguments('key'), + ['SCARD', 'key'] + ); + }); - testUtils.testWithClient('client.sCard', async client => { - assert.equal( - await client.sCard('key'), - 0 - ); - }, GLOBAL.SERVERS.OPEN); + testUtils.testAll('sCard', async client => { + assert.equal( + await client.sCard('key'), + 0 + ); + }, { + client: GLOBAL.SERVERS.OPEN, + cluster: GLOBAL.CLUSTERS.OPEN + }); }); diff --git a/packages/client/lib/commands/SCARD.ts b/packages/client/lib/commands/SCARD.ts index 0d3ce49b6b2..c13d042ba60 100644 --- a/packages/client/lib/commands/SCARD.ts +++ b/packages/client/lib/commands/SCARD.ts @@ -1,7 +1,10 @@ -export const FIRST_KEY_INDEX = 1; +import { RedisArgument, NumberReply, Command } from '../RESP/types'; -export function transformArguments(key: string): Array { +export default { + FIRST_KEY_INDEX: 1, + IS_READ_ONLY: true, + transformArguments(key: RedisArgument) { return ['SCARD', key]; -} - -export declare function transformReply(): number; + }, + transformReply: undefined as unknown as () => NumberReply +} as const satisfies Command; diff --git a/packages/client/lib/commands/SCRIPT_DEBUG.spec.ts b/packages/client/lib/commands/SCRIPT_DEBUG.spec.ts index 192f90f75a5..4e07f2c250c 100644 --- a/packages/client/lib/commands/SCRIPT_DEBUG.spec.ts +++ b/packages/client/lib/commands/SCRIPT_DEBUG.spec.ts @@ -1,19 +1,19 @@ -import { strict as assert } from 'assert'; +import { strict as assert } from 'node:assert'; import testUtils, { GLOBAL } from '../test-utils'; -import { transformArguments } from './SCRIPT_DEBUG'; +import SCRIPT_DEBUG from './SCRIPT_DEBUG'; describe('SCRIPT DEBUG', () => { - it('transformArguments', () => { - assert.deepEqual( - transformArguments('NO'), - ['SCRIPT', 'DEBUG', 'NO'] - ); - }); + it('transformArguments', () => { + assert.deepEqual( + SCRIPT_DEBUG.transformArguments('NO'), + ['SCRIPT', 'DEBUG', 'NO'] + ); + }); - testUtils.testWithClient('client.scriptDebug', async client => { - assert.equal( - await client.scriptDebug('NO'), - 'OK' - ); - }, GLOBAL.SERVERS.OPEN); + testUtils.testWithClient('client.scriptDebug', async client => { + assert.equal( + await client.scriptDebug('NO'), + 'OK' + ); + }, GLOBAL.SERVERS.OPEN); }); diff --git a/packages/client/lib/commands/SCRIPT_DEBUG.ts b/packages/client/lib/commands/SCRIPT_DEBUG.ts index e9e1e909d59..3c49ff709d5 100644 --- a/packages/client/lib/commands/SCRIPT_DEBUG.ts +++ b/packages/client/lib/commands/SCRIPT_DEBUG.ts @@ -1,5 +1,10 @@ -export function transformArguments(mode: 'YES' | 'SYNC' | 'NO'): Array { - return ['SCRIPT', 'DEBUG', mode]; -} +import { SimpleStringReply, Command } from '../RESP/types'; -export declare function transformReply(): string; +export default { + FIRST_KEY_INDEX: undefined, + IS_READ_ONLY: true, + transformArguments(mode: 'YES' | 'SYNC' | 'NO') { + return ['SCRIPT', 'DEBUG', mode]; + }, + transformReply: undefined as unknown as () => SimpleStringReply<'OK'> +} as const satisfies Command; diff --git a/packages/client/lib/commands/SCRIPT_EXISTS.spec.ts b/packages/client/lib/commands/SCRIPT_EXISTS.spec.ts index e0fbbcc5537..8afdbb5f581 100644 --- a/packages/client/lib/commands/SCRIPT_EXISTS.spec.ts +++ b/packages/client/lib/commands/SCRIPT_EXISTS.spec.ts @@ -1,28 +1,28 @@ -import { strict as assert } from 'assert'; +import { strict as assert } from 'node:assert'; import testUtils, { GLOBAL } from '../test-utils'; -import { transformArguments } from './SCRIPT_EXISTS'; +import SCRIPT_EXISTS from './SCRIPT_EXISTS'; describe('SCRIPT EXISTS', () => { - describe('transformArguments', () => { - it('string', () => { - assert.deepEqual( - transformArguments('sha1'), - ['SCRIPT', 'EXISTS', 'sha1'] - ); - }); + describe('transformArguments', () => { + it('string', () => { + assert.deepEqual( + SCRIPT_EXISTS.transformArguments('sha1'), + ['SCRIPT', 'EXISTS', 'sha1'] + ); + }); - it('array', () => { - assert.deepEqual( - transformArguments(['1', '2']), - ['SCRIPT', 'EXISTS', '1', '2'] - ); - }); + it('array', () => { + assert.deepEqual( + SCRIPT_EXISTS.transformArguments(['1', '2']), + ['SCRIPT', 'EXISTS', '1', '2'] + ); }); + }); - testUtils.testWithClient('client.scriptExists', async client => { - assert.deepEqual( - await client.scriptExists('sha1'), - [false] - ); - }, GLOBAL.SERVERS.OPEN); + testUtils.testWithClient('client.scriptExists', async client => { + assert.deepEqual( + await client.scriptExists('sha1'), + [0] + ); + }, GLOBAL.SERVERS.OPEN); }); diff --git a/packages/client/lib/commands/SCRIPT_EXISTS.ts b/packages/client/lib/commands/SCRIPT_EXISTS.ts index cee889215d3..ab0a293d8de 100644 --- a/packages/client/lib/commands/SCRIPT_EXISTS.ts +++ b/packages/client/lib/commands/SCRIPT_EXISTS.ts @@ -1,8 +1,11 @@ -import { RedisCommandArguments } from '.'; -import { pushVerdictArguments } from './generic-transformers'; +import { ArrayReply, NumberReply, Command } from '../RESP/types'; +import { RedisVariadicArgument, pushVariadicArguments } from './generic-transformers'; -export function transformArguments(sha1: string | Array): RedisCommandArguments { - return pushVerdictArguments(['SCRIPT', 'EXISTS'], sha1); -} - -export { transformBooleanArrayReply as transformReply } from './generic-transformers'; +export default { + FIRST_KEY_INDEX: undefined, + IS_READ_ONLY: true, + transformArguments(sha1: RedisVariadicArgument) { + return pushVariadicArguments(['SCRIPT', 'EXISTS'], sha1); + }, + transformReply: undefined as unknown as () => ArrayReply +} as const satisfies Command; diff --git a/packages/client/lib/commands/SCRIPT_FLUSH.spec.ts b/packages/client/lib/commands/SCRIPT_FLUSH.spec.ts index ae156e937d1..ccc14ecc285 100644 --- a/packages/client/lib/commands/SCRIPT_FLUSH.spec.ts +++ b/packages/client/lib/commands/SCRIPT_FLUSH.spec.ts @@ -1,28 +1,28 @@ -import { strict as assert } from 'assert'; +import { strict as assert } from 'node:assert'; import testUtils, { GLOBAL } from '../test-utils'; -import { transformArguments } from './SCRIPT_FLUSH'; +import SCRIPT_FLUSH from './SCRIPT_FLUSH'; describe('SCRIPT FLUSH', () => { - describe('transformArguments', () => { - it('simple', () => { - assert.deepEqual( - transformArguments(), - ['SCRIPT', 'FLUSH'] - ); - }); + describe('transformArguments', () => { + it('simple', () => { + assert.deepEqual( + SCRIPT_FLUSH.transformArguments(), + ['SCRIPT', 'FLUSH'] + ); + }); - it('with mode', () => { - assert.deepEqual( - transformArguments('SYNC'), - ['SCRIPT', 'FLUSH', 'SYNC'] - ); - }); + it('with mode', () => { + assert.deepEqual( + SCRIPT_FLUSH.transformArguments('SYNC'), + ['SCRIPT', 'FLUSH', 'SYNC'] + ); }); + }); - testUtils.testWithClient('client.scriptFlush', async client => { - assert.equal( - await client.scriptFlush(), - 'OK' - ); - }, GLOBAL.SERVERS.OPEN); + testUtils.testWithClient('client.scriptFlush', async client => { + assert.equal( + await client.scriptFlush(), + 'OK' + ); + }, GLOBAL.SERVERS.OPEN); }); diff --git a/packages/client/lib/commands/SCRIPT_FLUSH.ts b/packages/client/lib/commands/SCRIPT_FLUSH.ts index 2c220e9e3d1..f5426395628 100644 --- a/packages/client/lib/commands/SCRIPT_FLUSH.ts +++ b/packages/client/lib/commands/SCRIPT_FLUSH.ts @@ -1,11 +1,16 @@ -export function transformArguments(mode?: 'ASYNC' | 'SYNC'): Array { +import { SimpleStringReply, Command } from '../RESP/types'; + +export default { + FIRST_KEY_INDEX: undefined, + IS_READ_ONLY: true, + transformArguments(mode?: 'ASYNC' | 'SYNC') { const args = ['SCRIPT', 'FLUSH']; if (mode) { - args.push(mode); + args.push(mode); } return args; -} - -export declare function transformReply(): string; + }, + transformReply: undefined as unknown as () => SimpleStringReply<'OK'> +} as const satisfies Command; diff --git a/packages/client/lib/commands/SCRIPT_KILL.spec.ts b/packages/client/lib/commands/SCRIPT_KILL.spec.ts index e57265aa61a..1499c97ac07 100644 --- a/packages/client/lib/commands/SCRIPT_KILL.spec.ts +++ b/packages/client/lib/commands/SCRIPT_KILL.spec.ts @@ -1,11 +1,11 @@ -import { strict as assert } from 'assert'; -import { transformArguments } from './SCRIPT_KILL'; +import { strict as assert } from 'node:assert'; +import SCRIPT_KILL from './SCRIPT_KILL'; describe('SCRIPT KILL', () => { - it('transformArguments', () => { - assert.deepEqual( - transformArguments(), - ['SCRIPT', 'KILL'] - ); - }); + it('transformArguments', () => { + assert.deepEqual( + SCRIPT_KILL.transformArguments(), + ['SCRIPT', 'KILL'] + ); + }); }); diff --git a/packages/client/lib/commands/SCRIPT_KILL.ts b/packages/client/lib/commands/SCRIPT_KILL.ts index c0a53da8681..ac025b788bb 100644 --- a/packages/client/lib/commands/SCRIPT_KILL.ts +++ b/packages/client/lib/commands/SCRIPT_KILL.ts @@ -1,5 +1,10 @@ -export function transformArguments(): Array { - return ['SCRIPT', 'KILL']; -} +import { SimpleStringReply, Command } from '../RESP/types'; -export declare function transformReply(): string; +export default { + FIRST_KEY_INDEX: undefined, + IS_READ_ONLY: true, + transformArguments() { + return ['SCRIPT', 'KILL']; + }, + transformReply: undefined as unknown as () => SimpleStringReply<'OK'> +} as const satisfies Command; diff --git a/packages/client/lib/commands/SCRIPT_LOAD.spec.ts b/packages/client/lib/commands/SCRIPT_LOAD.spec.ts index 062f3c201e1..d964859d2ff 100644 --- a/packages/client/lib/commands/SCRIPT_LOAD.spec.ts +++ b/packages/client/lib/commands/SCRIPT_LOAD.spec.ts @@ -1,23 +1,23 @@ -import { strict as assert } from 'assert'; -import { scriptSha1 } from '../lua-script'; +import { strict as assert } from 'node:assert'; import testUtils, { GLOBAL } from '../test-utils'; -import { transformArguments } from './SCRIPT_LOAD'; +import SCRIPT_LOAD from './SCRIPT_LOAD'; +import { scriptSha1 } from '../lua-script'; describe('SCRIPT LOAD', () => { - const SCRIPT = 'return 1;', - SCRIPT_SHA1 = scriptSha1(SCRIPT); + const SCRIPT = 'return 1;', + SCRIPT_SHA1 = scriptSha1(SCRIPT); - it('transformArguments', () => { - assert.deepEqual( - transformArguments(SCRIPT), - ['SCRIPT', 'LOAD', SCRIPT] - ); - }); + it('transformArguments', () => { + assert.deepEqual( + SCRIPT_LOAD.transformArguments(SCRIPT), + ['SCRIPT', 'LOAD', SCRIPT] + ); + }); - testUtils.testWithClient('client.scriptLoad', async client => { - assert.equal( - await client.scriptLoad(SCRIPT), - SCRIPT_SHA1 - ); - }, GLOBAL.SERVERS.OPEN); + testUtils.testWithClient('client.scriptLoad', async client => { + assert.equal( + await client.scriptLoad(SCRIPT), + SCRIPT_SHA1 + ); + }, GLOBAL.SERVERS.OPEN); }); diff --git a/packages/client/lib/commands/SCRIPT_LOAD.ts b/packages/client/lib/commands/SCRIPT_LOAD.ts index 7cb28c1ec7f..90028b13a5f 100644 --- a/packages/client/lib/commands/SCRIPT_LOAD.ts +++ b/packages/client/lib/commands/SCRIPT_LOAD.ts @@ -1,5 +1,10 @@ -export function transformArguments(script: string): Array { - return ['SCRIPT', 'LOAD', script]; -} +import { BlobStringReply, Command, RedisArgument } from '../RESP/types'; -export declare function transformReply(): string; +export default { + FIRST_KEY_INDEX: undefined, + IS_READ_ONLY: true, + transformArguments(script: RedisArgument) { + return ['SCRIPT', 'LOAD', script]; + }, + transformReply: undefined as unknown as () => BlobStringReply +} as const satisfies Command; diff --git a/packages/client/lib/commands/SDIFF.spec.ts b/packages/client/lib/commands/SDIFF.spec.ts index 340906e9350..83ac6dc1da1 100644 --- a/packages/client/lib/commands/SDIFF.spec.ts +++ b/packages/client/lib/commands/SDIFF.spec.ts @@ -1,28 +1,31 @@ -import { strict as assert } from 'assert'; +import { strict as assert } from 'node:assert'; import testUtils, { GLOBAL } from '../test-utils'; -import { transformArguments } from './SDIFF'; +import SDIFF from './SDIFF'; describe('SDIFF', () => { - describe('transformArguments', () => { - it('string', () => { - assert.deepEqual( - transformArguments('key'), - ['SDIFF', 'key'] - ); - }); + describe('transformArguments', () => { + it('string', () => { + assert.deepEqual( + SDIFF.transformArguments('key'), + ['SDIFF', 'key'] + ); + }); - it('array', () => { - assert.deepEqual( - transformArguments(['1', '2']), - ['SDIFF', '1', '2'] - ); - }); + it('array', () => { + assert.deepEqual( + SDIFF.transformArguments(['1', '2']), + ['SDIFF', '1', '2'] + ); }); + }); - testUtils.testWithClient('client.sDiff', async client => { - assert.deepEqual( - await client.sDiff('key'), - [] - ); - }, GLOBAL.SERVERS.OPEN); + testUtils.testAll('sDiff', async client => { + assert.deepEqual( + await client.sDiff('key'), + [] + ); + }, { + client: GLOBAL.SERVERS.OPEN, + cluster: GLOBAL.CLUSTERS.OPEN + }); }); diff --git a/packages/client/lib/commands/SDIFF.ts b/packages/client/lib/commands/SDIFF.ts index 9c4f3b4820b..918cbf7fa15 100644 --- a/packages/client/lib/commands/SDIFF.ts +++ b/packages/client/lib/commands/SDIFF.ts @@ -1,14 +1,11 @@ -import { RedisCommandArgument, RedisCommandArguments } from '.'; -import { pushVerdictArguments } from './generic-transformers'; - -export const FIRST_KEY_INDEX = 1; - -export const IS_READ_ONLY = true; - -export function transformArguments( - keys: RedisCommandArgument | Array -): RedisCommandArguments { - return pushVerdictArguments(['SDIFF'], keys); -} - -export declare function transformReply(): Array; +import { ArrayReply, BlobStringReply, Command } from '../RESP/types'; +import { RedisVariadicArgument, pushVariadicArguments } from './generic-transformers'; + +export default { + FIRST_KEY_INDEX: 1, + IS_READ_ONLY: true, + transformArguments(keys: RedisVariadicArgument) { + return pushVariadicArguments(['SDIFF'], keys); + }, + transformReply: undefined as unknown as () => ArrayReply +} as const satisfies Command; diff --git a/packages/client/lib/commands/SDIFFSTORE.spec.ts b/packages/client/lib/commands/SDIFFSTORE.spec.ts index 263b4f43f64..613a9eb590b 100644 --- a/packages/client/lib/commands/SDIFFSTORE.spec.ts +++ b/packages/client/lib/commands/SDIFFSTORE.spec.ts @@ -1,28 +1,31 @@ -import { strict as assert } from 'assert'; +import { strict as assert } from 'node:assert'; import testUtils, { GLOBAL } from '../test-utils'; -import { transformArguments } from './SDIFFSTORE'; +import SDIFFSTORE from './SDIFFSTORE'; describe('SDIFFSTORE', () => { - describe('transformArguments', () => { - it('string', () => { - assert.deepEqual( - transformArguments('destination', 'key'), - ['SDIFFSTORE', 'destination', 'key'] - ); - }); + describe('transformArguments', () => { + it('string', () => { + assert.deepEqual( + SDIFFSTORE.transformArguments('destination', 'key'), + ['SDIFFSTORE', 'destination', 'key'] + ); + }); - it('array', () => { - assert.deepEqual( - transformArguments('destination', ['1', '2']), - ['SDIFFSTORE', 'destination', '1', '2'] - ); - }); + it('array', () => { + assert.deepEqual( + SDIFFSTORE.transformArguments('destination', ['1', '2']), + ['SDIFFSTORE', 'destination', '1', '2'] + ); }); + }); - testUtils.testWithClient('client.sDiffStore', async client => { - assert.equal( - await client.sDiffStore('destination', 'key'), - 0 - ); - }, GLOBAL.SERVERS.OPEN); + testUtils.testAll('sDiffStore', async client => { + assert.equal( + await client.sDiffStore('{tag}destination', '{tag}key'), + 0 + ); + }, { + client: GLOBAL.SERVERS.OPEN, + cluster: GLOBAL.CLUSTERS.OPEN + }); }); diff --git a/packages/client/lib/commands/SDIFFSTORE.ts b/packages/client/lib/commands/SDIFFSTORE.ts index a927e12ef0e..15f0ccb499a 100644 --- a/packages/client/lib/commands/SDIFFSTORE.ts +++ b/packages/client/lib/commands/SDIFFSTORE.ts @@ -1,13 +1,10 @@ -import { RedisCommandArgument, RedisCommandArguments } from '.'; -import { pushVerdictArguments } from './generic-transformers'; +import { RedisArgument, NumberReply, Command } from '../RESP/types'; +import { RedisVariadicArgument, pushVariadicArguments } from './generic-transformers'; -export const FIRST_KEY_INDEX = 1; - -export function transformArguments( - destination: RedisCommandArgument, - keys: RedisCommandArgument | Array -): RedisCommandArguments { - return pushVerdictArguments(['SDIFFSTORE', destination], keys); -} - -export declare function transformReply(): number; +export default { + FIRST_KEY_INDEX: 1, + transformArguments(destination: RedisArgument, keys: RedisVariadicArgument) { + return pushVariadicArguments(['SDIFFSTORE', destination], keys); + }, + transformReply: undefined as unknown as () => NumberReply +} as const satisfies Command; diff --git a/packages/client/lib/commands/SET.spec.ts b/packages/client/lib/commands/SET.spec.ts index 0b3331fd3a4..4364eb7391a 100644 --- a/packages/client/lib/commands/SET.spec.ts +++ b/packages/client/lib/commands/SET.spec.ts @@ -1,129 +1,164 @@ -import { strict as assert } from 'assert'; +import { strict as assert } from 'node:assert'; import testUtils, { GLOBAL } from '../test-utils'; -import { transformArguments } from './SET'; +import SET from './SET'; describe('SET', () => { - describe('transformArguments', () => { - it('string', () => { - assert.deepEqual( - transformArguments('key', 'value'), - ['SET', 'key', 'value'] - ); - }); + describe('transformArguments', () => { + describe('value', () => { + it('string', () => { + assert.deepEqual( + SET.transformArguments('key', 'value'), + ['SET', 'key', 'value'] + ); + }); + + it('number', () => { + assert.deepEqual( + SET.transformArguments('key', 0), + ['SET', 'key', '0'] + ); + }); + }); + + describe('expiration', () => { + it('\'KEEPTTL\'', () => { + assert.deepEqual( + SET.transformArguments('key', 'value', { + expiration: 'KEEPTTL' + }), + ['SET', 'key', 'value', 'KEEPTTL'] + ); + }); - it('number', () => { - assert.deepEqual( - transformArguments('key', 0), - ['SET', 'key', '0'] - ); - }); + it('{ type: \'KEEPTTL\' }', () => { + assert.deepEqual( + SET.transformArguments('key', 'value', { + expiration: { + type: 'KEEPTTL' + } + }), + ['SET', 'key', 'value', 'KEEPTTL'] + ); + }); - describe('TTL', () => { - it('with EX', () => { - assert.deepEqual( - transformArguments('key', 'value', { - EX: 0 - }), - ['SET', 'key', 'value', 'EX', '0'] - ); - }); + it('{ type: \'EX\' }', () => { + assert.deepEqual( + SET.transformArguments('key', 'value', { + expiration: { + type: 'EX', + value: 0 + } + }), + ['SET', 'key', 'value', 'EX', '0'] + ); + }); - it('with PX', () => { - assert.deepEqual( - transformArguments('key', 'value', { - PX: 0 - }), - ['SET', 'key', 'value', 'PX', '0'] - ); - }); + it('with EX (backwards compatibility)', () => { + assert.deepEqual( + SET.transformArguments('key', 'value', { + EX: 0 + }), + ['SET', 'key', 'value', 'EX', '0'] + ); + }); - it('with EXAT', () => { - assert.deepEqual( - transformArguments('key', 'value', { - EXAT: 0 - }), - ['SET', 'key', 'value', 'EXAT', '0'] - ); - }); + it('with PX (backwards compatibility)', () => { + assert.deepEqual( + SET.transformArguments('key', 'value', { + PX: 0 + }), + ['SET', 'key', 'value', 'PX', '0'] + ); + }); - it('with PXAT', () => { - assert.deepEqual( - transformArguments('key', 'value', { - PXAT: 0 - }), - ['SET', 'key', 'value', 'PXAT', '0'] - ); - }); + it('with EXAT (backwards compatibility)', () => { + assert.deepEqual( + SET.transformArguments('key', 'value', { + EXAT: 0 + }), + ['SET', 'key', 'value', 'EXAT', '0'] + ); + }); - it('with KEEPTTL', () => { - assert.deepEqual( - transformArguments('key', 'value', { - KEEPTTL: true - }), - ['SET', 'key', 'value', 'KEEPTTL'] - ); - }); - }); + it('with PXAT (backwards compatibility)', () => { + assert.deepEqual( + SET.transformArguments('key', 'value', { + PXAT: 0 + }), + ['SET', 'key', 'value', 'PXAT', '0'] + ); + }); - describe('Guards', () => { - it('with NX', () => { - assert.deepEqual( - transformArguments('key', 'value', { - NX: true - }), - ['SET', 'key', 'value', 'NX'] - ); - }); + it('with KEEPTTL (backwards compatibility)', () => { + assert.deepEqual( + SET.transformArguments('key', 'value', { + KEEPTTL: true + }), + ['SET', 'key', 'value', 'KEEPTTL'] + ); + }); + }); - it('with XX', () => { - assert.deepEqual( - transformArguments('key', 'value', { - XX: true - }), - ['SET', 'key', 'value', 'XX'] - ); - }); - }); + describe('condition', () => { + it('with condition', () => { + assert.deepEqual( + SET.transformArguments('key', 'value', { + condition: 'NX' + }), + ['SET', 'key', 'value', 'NX'] + ); + }); - it('with GET', () => { - assert.deepEqual( - transformArguments('key', 'value', { - GET: true - }), - ['SET', 'key', 'value', 'GET'] - ); - }); + it('with NX (backwards compatibility)', () => { + assert.deepEqual( + SET.transformArguments('key', 'value', { + NX: true + }), + ['SET', 'key', 'value', 'NX'] + ); + }); - it('with EX, NX, GET', () => { - assert.deepEqual( - transformArguments('key', 'value', { - EX: 1, - NX: true, - GET: true - }), - ['SET', 'key', 'value', 'EX', '1', 'NX', 'GET'] - ); - }); + it('with XX (backwards compatibility)', () => { + assert.deepEqual( + SET.transformArguments('key', 'value', { + XX: true + }), + ['SET', 'key', 'value', 'XX'] + ); + }); }); - describe('client.set', () => { - testUtils.testWithClient('simple', async client => { - assert.equal( - await client.set('key', 'value'), - 'OK' - ); - }, GLOBAL.SERVERS.OPEN); + it('with GET', () => { + assert.deepEqual( + SET.transformArguments('key', 'value', { + GET: true + }), + ['SET', 'key', 'value', 'GET'] + ); + }); - testUtils.testWithClient('with GET on empty key', async client => { - assert.equal( - await client.set('key', 'value', { - GET: true - }), - null - ); - }, { - ...GLOBAL.SERVERS.OPEN, - minimumDockerVersion: [6, 2] - }); + it('with expiration, condition, GET', () => { + assert.deepEqual( + SET.transformArguments('key', 'value', { + expiration: { + type: 'EX', + value: 0 + }, + condition: 'NX', + GET: true + }), + ['SET', 'key', 'value', 'EX', '0', 'NX', 'GET'] + ); }); + }); + + testUtils.testAll('set', async client => { + assert.equal( + await client.set('key', 'value'), + 'OK' + ); + }, { + client: GLOBAL.SERVERS.OPEN, + cluster: GLOBAL.CLUSTERS.OPEN + }); }); diff --git a/packages/client/lib/commands/SET.ts b/packages/client/lib/commands/SET.ts index 08ae56552b9..cede62e7055 100644 --- a/packages/client/lib/commands/SET.ts +++ b/packages/client/lib/commands/SET.ts @@ -1,63 +1,91 @@ -import { RedisCommandArgument, RedisCommandArguments } from '.'; +import { RedisArgument, SimpleStringReply, BlobStringReply, NullReply, Command } from '../RESP/types'; -export const FIRST_KEY_INDEX = 1; +export interface SetOptions { + expiration?: { + type: 'EX' | 'PX' | 'EXAT' | 'PXAT'; + value: number; + } | { + type: 'KEEPTTL'; + } | 'KEEPTTL'; + /** + * @deprecated Use `expiration` { type: 'EX', value: number } instead + */ + EX?: number; + /** + * @deprecated Use `expiration` { type: 'PX', value: number } instead + */ + PX?: number; + /** + * @deprecated Use `expiration` { type: 'EXAT', value: number } instead + */ + EXAT?: number; + /** + * @deprecated Use `expiration` { type: 'PXAT', value: number } instead + */ + PXAT?: number; + /** + * @deprecated Use `expiration` 'KEEPTTL' instead + */ + KEEPTTL?: boolean; -type MaximumOneOf = - K extends keyof T ? { [P in K]?: T[K] } & Partial, never>> : never; - -type SetTTL = MaximumOneOf<{ - EX: number; - PX: number; - EXAT: number; - PXAT: number; - KEEPTTL: true; -}>; - -type SetGuards = MaximumOneOf<{ - NX: true; - XX: true; -}>; - -interface SetCommonOptions { - GET?: true; + condition?: 'NX' | 'XX'; + /** + * @deprecated Use `{ condition: 'NX' }` instead. + */ + NX?: boolean; + /** + * @deprecated Use `{ condition: 'XX' }` instead. + */ + XX?: boolean; + + GET?: boolean; } -export type SetOptions = SetTTL & SetGuards & SetCommonOptions; - -export function transformArguments( - key: RedisCommandArgument, - value: RedisCommandArgument | number, - options?: SetOptions -): RedisCommandArguments { +export default { + FIRST_KEY_INDEX: 1, + transformArguments(key: RedisArgument, value: RedisArgument | number, options?: SetOptions) { const args = [ - 'SET', - key, - typeof value === 'number' ? value.toString() : value + 'SET', + key, + typeof value === 'number' ? value.toString() : value ]; - if (options?.EX !== undefined) { - args.push('EX', options.EX.toString()); + if (options?.expiration) { + if (typeof options.expiration === 'string') { + args.push(options.expiration); + } else if (options.expiration.type === 'KEEPTTL') { + args.push('KEEPTTL'); + } else { + args.push( + options.expiration.type, + options.expiration.value.toString() + ); + } + } else if (options?.EX !== undefined) { + args.push('EX', options.EX.toString()); } else if (options?.PX !== undefined) { - args.push('PX', options.PX.toString()); + args.push('PX', options.PX.toString()); } else if (options?.EXAT !== undefined) { - args.push('EXAT', options.EXAT.toString()); + args.push('EXAT', options.EXAT.toString()); } else if (options?.PXAT !== undefined) { - args.push('PXAT', options.PXAT.toString()); + args.push('PXAT', options.PXAT.toString()); } else if (options?.KEEPTTL) { - args.push('KEEPTTL'); + args.push('KEEPTTL'); } - if (options?.NX) { - args.push('NX'); + if (options?.condition) { + args.push(options.condition); + } else if (options?.NX) { + args.push('NX'); } else if (options?.XX) { - args.push('XX'); + args.push('XX'); } if (options?.GET) { - args.push('GET'); + args.push('GET'); } return args; -} - -export declare function transformReply(): RedisCommandArgument | null; + }, + transformReply: undefined as unknown as () => SimpleStringReply<'OK'> | BlobStringReply | NullReply +} as const satisfies Command; diff --git a/packages/client/lib/commands/SETBIT.spec.ts b/packages/client/lib/commands/SETBIT.spec.ts index 43fbff7c2d9..e4470bb1528 100644 --- a/packages/client/lib/commands/SETBIT.spec.ts +++ b/packages/client/lib/commands/SETBIT.spec.ts @@ -1,26 +1,22 @@ -import { strict as assert } from 'assert'; +import { strict as assert } from 'node:assert'; import testUtils, { GLOBAL } from '../test-utils'; -import { transformArguments } from './SETBIT'; +import SETBIT from './SETBIT'; describe('SETBIT', () => { - it('transformArguments', () => { - assert.deepEqual( - transformArguments('key', 0, 1), - ['SETBIT', 'key', '0', '1'] - ); - }); + it('transformArguments', () => { + assert.deepEqual( + SETBIT.transformArguments('key', 0, 1), + ['SETBIT', 'key', '0', '1'] + ); + }); - testUtils.testWithClient('client.setBit', async client => { - assert.equal( - await client.setBit('key', 0, 1), - 0 - ); - }, GLOBAL.SERVERS.OPEN); - - testUtils.testWithCluster('cluster.setBit', async cluster => { - assert.equal( - await cluster.setBit('key', 0, 1), - 0 - ); - }, GLOBAL.CLUSTERS.OPEN); + testUtils.testAll('setBit', async client => { + assert.equal( + await client.setBit('key', 0, 1), + 0 + ); + }, { + client: GLOBAL.SERVERS.OPEN, + cluster: GLOBAL.CLUSTERS.OPEN + }); }); diff --git a/packages/client/lib/commands/SETBIT.ts b/packages/client/lib/commands/SETBIT.ts index 94f463330a8..5b3ec6173dc 100644 --- a/packages/client/lib/commands/SETBIT.ts +++ b/packages/client/lib/commands/SETBIT.ts @@ -1,14 +1,15 @@ -import { RedisCommandArgument, RedisCommandArguments } from '.'; +import { RedisArgument, NumberReply, Command } from '../RESP/types'; import { BitValue } from './generic-transformers'; -export const FIRST_KEY_INDEX = 1; - -export function transformArguments( - key: RedisCommandArgument, +export default { + FIRST_KEY_INDEX: 1, + IS_READ_ONLY: false, + transformArguments( + key: RedisArgument, offset: number, value: BitValue -): RedisCommandArguments { + ) { return ['SETBIT', key, offset.toString(), value.toString()]; -} - -export declare function transformReply(): BitValue; + }, + transformReply: undefined as unknown as () => NumberReply +} as const satisfies Command; diff --git a/packages/client/lib/commands/SETEX.spec.ts b/packages/client/lib/commands/SETEX.spec.ts index bca298c6c04..00f204cc713 100644 --- a/packages/client/lib/commands/SETEX.spec.ts +++ b/packages/client/lib/commands/SETEX.spec.ts @@ -1,26 +1,22 @@ -import { strict as assert } from 'assert'; +import { strict as assert } from 'node:assert'; import testUtils, { GLOBAL } from '../test-utils'; -import { transformArguments } from './SETEX'; +import SETEX from './SETEX'; describe('SETEX', () => { - it('transformArguments', () => { - assert.deepEqual( - transformArguments('key', 1, 'value'), - ['SETEX', 'key', '1', 'value'] - ); - }); + it('transformArguments', () => { + assert.deepEqual( + SETEX.transformArguments('key', 1, 'value'), + ['SETEX', 'key', '1', 'value'] + ); + }); - testUtils.testWithClient('client.setEx', async client => { - assert.equal( - await client.setEx('key', 1, 'value'), - 'OK' - ); - }, GLOBAL.SERVERS.OPEN); - - testUtils.testWithCluster('cluster.setEx', async cluster => { - assert.equal( - await cluster.setEx('key', 1, 'value'), - 'OK' - ); - }, GLOBAL.CLUSTERS.OPEN); + testUtils.testAll('setEx', async client => { + assert.equal( + await client.setEx('key', 1, 'value'), + 'OK' + ); + }, { + client: GLOBAL.SERVERS.OPEN, + cluster: GLOBAL.CLUSTERS.OPEN + }); }); diff --git a/packages/client/lib/commands/SETEX.ts b/packages/client/lib/commands/SETEX.ts index bb3068501f0..bbd77e5d990 100644 --- a/packages/client/lib/commands/SETEX.ts +++ b/packages/client/lib/commands/SETEX.ts @@ -1,18 +1,18 @@ -import { RedisCommandArgument, RedisCommandArguments } from '.'; +import { RedisArgument, SimpleStringReply, Command } from '../RESP/types'; -export const FIRST_KEY_INDEX = 1; - -export function transformArguments( - key: RedisCommandArgument, +export default { + FIRST_KEY_INDEX: 1, + transformArguments( + key: RedisArgument, seconds: number, - value: RedisCommandArgument -): RedisCommandArguments { + value: RedisArgument + ) { return [ - 'SETEX', - key, - seconds.toString(), - value + 'SETEX', + key, + seconds.toString(), + value ]; -} - -export declare function transformReply(): RedisCommandArgument; + }, + transformReply: undefined as unknown as () => SimpleStringReply<'OK'> +} as const satisfies Command; diff --git a/packages/client/lib/commands/SETNX .spec.ts b/packages/client/lib/commands/SETNX .spec.ts index c5bdfcffa2c..5cfca29ba62 100644 --- a/packages/client/lib/commands/SETNX .spec.ts +++ b/packages/client/lib/commands/SETNX .spec.ts @@ -1,26 +1,22 @@ -import { strict as assert } from 'assert'; +import { strict as assert } from 'node:assert'; import testUtils, { GLOBAL } from '../test-utils'; -import { transformArguments } from './SETNX'; +import SETNX from './SETNX'; describe('SETNX', () => { - it('transformArguments', () => { - assert.deepEqual( - transformArguments('key', 'value'), - ['SETNX', 'key', 'value'] - ); - }); + it('transformArguments', () => { + assert.deepEqual( + SETNX.transformArguments('key', 'value'), + ['SETNX', 'key', 'value'] + ); + }); - testUtils.testWithClient('client.setNX', async client => { - assert.equal( - await client.setNX('key', 'value'), - true - ); - }, GLOBAL.SERVERS.OPEN); - - testUtils.testWithCluster('cluster.setNX', async cluster => { - assert.equal( - await cluster.setNX('key', 'value'), - true - ); - }, GLOBAL.CLUSTERS.OPEN); + testUtils.testAll('setNX', async client => { + assert.equal( + await client.setNX('key', 'value'), + 1 + ); + }, { + client: GLOBAL.SERVERS.OPEN, + cluster: GLOBAL.CLUSTERS.OPEN + }); }); diff --git a/packages/client/lib/commands/SETNX.ts b/packages/client/lib/commands/SETNX.ts index b01d45dc32f..0940efad693 100644 --- a/packages/client/lib/commands/SETNX.ts +++ b/packages/client/lib/commands/SETNX.ts @@ -1,12 +1,9 @@ -import { RedisCommandArgument, RedisCommandArguments } from '.'; +import { RedisArgument, NumberReply, Command } from '../RESP/types'; -export const FIRST_KEY_INDEX = 1; - -export function transformArguments( - key: RedisCommandArgument, - value: RedisCommandArgument -): RedisCommandArguments { +export default { + FIRST_KEY_INDEX: 1, + transformArguments(key: RedisArgument, value: RedisArgument) { return ['SETNX', key, value]; -} - -export { transformBooleanReply as transformReply } from './generic-transformers'; + }, + transformReply: undefined as unknown as () => NumberReply +} as const satisfies Command; diff --git a/packages/client/lib/commands/SETRANGE.spec.ts b/packages/client/lib/commands/SETRANGE.spec.ts index 398b7730404..01d3545a359 100644 --- a/packages/client/lib/commands/SETRANGE.spec.ts +++ b/packages/client/lib/commands/SETRANGE.spec.ts @@ -1,26 +1,22 @@ -import { strict as assert } from 'assert'; +import { strict as assert } from 'node:assert'; import testUtils, { GLOBAL } from '../test-utils'; -import { transformArguments } from './SETRANGE'; +import SETRANGE from './SETRANGE'; describe('SETRANGE', () => { - it('transformArguments', () => { - assert.deepEqual( - transformArguments('key', 0, 'value'), - ['SETRANGE', 'key', '0', 'value'] - ); - }); + it('transformArguments', () => { + assert.deepEqual( + SETRANGE.transformArguments('key', 0, 'value'), + ['SETRANGE', 'key', '0', 'value'] + ); + }); - testUtils.testWithClient('client.setRange', async client => { - assert.equal( - await client.setRange('key', 0, 'value'), - 5 - ); - }, GLOBAL.SERVERS.OPEN); - - testUtils.testWithCluster('cluster.setRange', async cluster => { - assert.equal( - await cluster.setRange('key', 0, 'value'), - 5 - ); - }, GLOBAL.CLUSTERS.OPEN); + testUtils.testAll('setRange', async client => { + assert.equal( + await client.setRange('key', 0, 'value'), + 5 + ); + }, { + client: GLOBAL.SERVERS.OPEN, + cluster: GLOBAL.CLUSTERS.OPEN + }); }); diff --git a/packages/client/lib/commands/SETRANGE.ts b/packages/client/lib/commands/SETRANGE.ts index 038a8a5dd7f..1951a82c07d 100644 --- a/packages/client/lib/commands/SETRANGE.ts +++ b/packages/client/lib/commands/SETRANGE.ts @@ -1,13 +1,18 @@ -import { RedisCommandArgument, RedisCommandArguments } from '.'; +import { RedisArgument, NumberReply, Command } from '../RESP/types'; -export const FIRST_KEY_INDEX = 1; - -export function transformArguments( - key: RedisCommandArgument, +export default { + FIRST_KEY_INDEX: 1, + transformArguments( + key: RedisArgument, offset: number, - value: RedisCommandArgument -): RedisCommandArguments { - return ['SETRANGE', key, offset.toString(), value]; -} - -export declare function transformReply(): number; + value: RedisArgument + ) { + return [ + 'SETRANGE', + key, + offset.toString(), + value + ]; + }, + transformReply: undefined as unknown as () => NumberReply +} as const satisfies Command; diff --git a/packages/client/lib/commands/SHUTDOWN.spec.ts b/packages/client/lib/commands/SHUTDOWN.spec.ts index d58cf4443c7..7dd46a5d534 100644 --- a/packages/client/lib/commands/SHUTDOWN.spec.ts +++ b/packages/client/lib/commands/SHUTDOWN.spec.ts @@ -1,27 +1,49 @@ -import { strict as assert } from 'assert'; -import { transformArguments } from './SHUTDOWN'; +import { strict as assert } from 'node:assert'; +import SHUTDOWN from './SHUTDOWN'; describe('SHUTDOWN', () => { - describe('transformArguments', () => { - it('simple', () => { - assert.deepEqual( - transformArguments(), - ['SHUTDOWN'] - ); - }); + describe('transformArguments', () => { + it('simple', () => { + assert.deepEqual( + SHUTDOWN.transformArguments(), + ['SHUTDOWN'] + ); + }); + + it('with mode', () => { + assert.deepEqual( + SHUTDOWN.transformArguments({ + mode: 'NOSAVE' + }), + ['SHUTDOWN', 'NOSAVE'] + ); + }); - it('NOSAVE', () => { - assert.deepEqual( - transformArguments('NOSAVE'), - ['SHUTDOWN', 'NOSAVE'] - ); - }); + it('with NOW', () => { + assert.deepEqual( + SHUTDOWN.transformArguments({ + NOW: true + }), + ['SHUTDOWN', 'NOW'] + ); + }); + + it('with FORCE', () => { + assert.deepEqual( + SHUTDOWN.transformArguments({ + FORCE: true + }), + ['SHUTDOWN', 'FORCE'] + ); + }); - it('SAVE', () => { - assert.deepEqual( - transformArguments('SAVE'), - ['SHUTDOWN', 'SAVE'] - ); - }); + it('with ABORT', () => { + assert.deepEqual( + SHUTDOWN.transformArguments({ + ABORT: true + }), + ['SHUTDOWN', 'ABORT'] + ); }); + }); }); diff --git a/packages/client/lib/commands/SHUTDOWN.ts b/packages/client/lib/commands/SHUTDOWN.ts index 1990d05a2ed..e0f3d08ce81 100644 --- a/packages/client/lib/commands/SHUTDOWN.ts +++ b/packages/client/lib/commands/SHUTDOWN.ts @@ -1,11 +1,35 @@ -export function transformArguments(mode?: 'NOSAVE' | 'SAVE'): Array { - const args = ['SHUTDOWN']; +import { SimpleStringReply, Command } from '../RESP/types'; - if (mode) { - args.push(mode); +export interface ShutdownOptions { + mode?: 'NOSAVE' | 'SAVE'; + NOW?: boolean; + FORCE?: boolean; + ABORT?: boolean; +} + +export default { + FIRST_KEY_INDEX: undefined, + IS_READ_ONLY: false, + transformArguments(options?: ShutdownOptions) { + const args = ['SHUTDOWN'] + + if (options?.mode) { + args.push(options.mode); } - return args; -} + if (options?.NOW) { + args.push('NOW'); + } -export declare function transformReply(): void; + if (options?.FORCE) { + args.push('FORCE'); + } + + if (options?.ABORT) { + args.push('ABORT'); + } + + return args; + }, + transformReply: undefined as unknown as () => void | SimpleStringReply +} as const satisfies Command; diff --git a/packages/client/lib/commands/SINTER.spec.ts b/packages/client/lib/commands/SINTER.spec.ts index 2324eac3ee8..5b66fdd3f89 100644 --- a/packages/client/lib/commands/SINTER.spec.ts +++ b/packages/client/lib/commands/SINTER.spec.ts @@ -1,28 +1,31 @@ -import { strict as assert } from 'assert'; +import { strict as assert } from 'node:assert'; import testUtils, { GLOBAL } from '../test-utils'; -import { transformArguments } from './SINTER'; +import SINTER from './SINTER'; describe('SINTER', () => { - describe('transformArguments', () => { - it('string', () => { - assert.deepEqual( - transformArguments('key'), - ['SINTER', 'key'] - ); - }); + describe('transformArguments', () => { + it('string', () => { + assert.deepEqual( + SINTER.transformArguments('key'), + ['SINTER', 'key'] + ); + }); - it('array', () => { - assert.deepEqual( - transformArguments(['1', '2']), - ['SINTER', '1', '2'] - ); - }); + it('array', () => { + assert.deepEqual( + SINTER.transformArguments(['1', '2']), + ['SINTER', '1', '2'] + ); }); + }); - testUtils.testWithClient('client.sInter', async client => { - assert.deepEqual( - await client.sInter('key'), - [] - ); - }, GLOBAL.SERVERS.OPEN); + testUtils.testAll('sInter', async client => { + assert.deepEqual( + await client.sInter('key'), + [] + ); + }, { + client: GLOBAL.SERVERS.OPEN, + cluster: GLOBAL.CLUSTERS.OPEN + }); }); diff --git a/packages/client/lib/commands/SINTER.ts b/packages/client/lib/commands/SINTER.ts index fe1feee7ade..f3f27de2e38 100644 --- a/packages/client/lib/commands/SINTER.ts +++ b/packages/client/lib/commands/SINTER.ts @@ -1,14 +1,11 @@ -import { RedisCommandArgument, RedisCommandArguments } from '.'; -import { pushVerdictArguments } from './generic-transformers'; - -export const FIRST_KEY_INDEX = 1; - -export const IS_READ_ONLY = true; - -export function transformArguments( - keys: RedisCommandArgument | Array -): RedisCommandArguments { - return pushVerdictArguments(['SINTER'], keys); -} - -export declare function transformReply(): Array; +import { ArrayReply, BlobStringReply, Command } from '../RESP/types'; +import { RedisVariadicArgument, pushVariadicArguments } from './generic-transformers'; + +export default { + FIRST_KEY_INDEX: 1, + IS_READ_ONLY: true, + transformArguments(keys: RedisVariadicArgument) { + return pushVariadicArguments(['SINTER'], keys); + }, + transformReply: undefined as unknown as () => ArrayReply +} as const satisfies Command; diff --git a/packages/client/lib/commands/SINTERCARD.spec.ts b/packages/client/lib/commands/SINTERCARD.spec.ts index a93699f6a13..cddb886088a 100644 --- a/packages/client/lib/commands/SINTERCARD.spec.ts +++ b/packages/client/lib/commands/SINTERCARD.spec.ts @@ -1,30 +1,42 @@ -import { strict as assert } from 'assert'; +import { strict as assert } from 'node:assert'; import testUtils, { GLOBAL } from '../test-utils'; -import { transformArguments } from './SINTERCARD'; +import SINTERCARD from './SINTERCARD'; describe('SINTERCARD', () => { - testUtils.isVersionGreaterThanHook([7]); + testUtils.isVersionGreaterThanHook([7]); - describe('transformArguments', () => { - it('simple', () => { - assert.deepEqual( - transformArguments(['1', '2']), - ['SINTERCARD', '2', '1', '2'] - ); - }); + describe('transformArguments', () => { + it('simple', () => { + assert.deepEqual( + SINTERCARD.transformArguments(['1', '2']), + ['SINTERCARD', '2', '1', '2'] + ); + }); + + it('with limit (backwards compatibility)', () => { + assert.deepEqual( + SINTERCARD.transformArguments(['1', '2'], 1), + ['SINTERCARD', '2', '1', '2', 'LIMIT', '1'] + ); + }); - it('with limit', () => { - assert.deepEqual( - transformArguments(['1', '2'], 1), - ['SINTERCARD', '2', '1', '2', 'LIMIT', '1'] - ); - }); + it('with LIMIT', () => { + assert.deepEqual( + SINTERCARD.transformArguments(['1', '2'], { + LIMIT: 1 + }), + ['SINTERCARD', '2', '1', '2', 'LIMIT', '1'] + ); }); + }); - testUtils.testWithClient('client.sInterCard', async client => { - assert.deepEqual( - await client.sInterCard('key'), - 0 - ); - }, GLOBAL.SERVERS.OPEN); + testUtils.testAll('sInterCard', async client => { + assert.deepEqual( + await client.sInterCard('key'), + 0 + ); + }, { + client: GLOBAL.SERVERS.OPEN, + cluster: GLOBAL.CLUSTERS.OPEN + }); }); diff --git a/packages/client/lib/commands/SINTERCARD.ts b/packages/client/lib/commands/SINTERCARD.ts index ddb7e5b00ef..626bc1048c3 100644 --- a/packages/client/lib/commands/SINTERCARD.ts +++ b/packages/client/lib/commands/SINTERCARD.ts @@ -1,21 +1,26 @@ -import { RedisCommandArgument, RedisCommandArguments } from '.'; -import { pushVerdictArgument } from './generic-transformers'; +import { NumberReply, Command } from '../RESP/types'; +import { RedisVariadicArgument, pushVariadicArgument } from './generic-transformers'; -export const FIRST_KEY_INDEX = 2; - -export const IS_READ_ONLY = true; +export interface SInterCardOptions { + LIMIT?: number; +} -export function transformArguments( - keys: Array | RedisCommandArgument, - limit?: number -): RedisCommandArguments { - const args = pushVerdictArgument(['SINTERCARD'], keys); +export default { + FIRST_KEY_INDEX: 2, + IS_READ_ONLY: true, + transformArguments( + keys: RedisVariadicArgument, + options?: SInterCardOptions | number // `number` for backwards compatibility + ) { + const args = pushVariadicArgument(['SINTERCARD'], keys); - if (limit) { - args.push('LIMIT', limit.toString()); + if (typeof options === 'number') { // backwards compatibility + args.push('LIMIT', options.toString()); + } else if (options?.LIMIT !== undefined) { + args.push('LIMIT', options.LIMIT.toString()); } return args; -} - -export declare function transformReply(): number; + }, + transformReply: undefined as unknown as () => NumberReply +} as const satisfies Command; diff --git a/packages/client/lib/commands/SINTERSTORE.spec.ts b/packages/client/lib/commands/SINTERSTORE.spec.ts index c4a6a095e7d..05416742ee9 100644 --- a/packages/client/lib/commands/SINTERSTORE.spec.ts +++ b/packages/client/lib/commands/SINTERSTORE.spec.ts @@ -1,28 +1,31 @@ -import { strict as assert } from 'assert'; +import { strict as assert } from 'node:assert'; import testUtils, { GLOBAL } from '../test-utils'; -import { transformArguments } from './SINTERSTORE'; +import SINTERSTORE from './SINTERSTORE'; describe('SINTERSTORE', () => { - describe('transformArguments', () => { - it('string', () => { - assert.deepEqual( - transformArguments('destination', 'key'), - ['SINTERSTORE', 'destination', 'key'] - ); - }); + describe('transformArguments', () => { + it('string', () => { + assert.deepEqual( + SINTERSTORE.transformArguments('destination', 'key'), + ['SINTERSTORE', 'destination', 'key'] + ); + }); - it('array', () => { - assert.deepEqual( - transformArguments('destination', ['1', '2']), - ['SINTERSTORE', 'destination', '1', '2'] - ); - }); + it('array', () => { + assert.deepEqual( + SINTERSTORE.transformArguments('destination', ['1', '2']), + ['SINTERSTORE', 'destination', '1', '2'] + ); }); + }); - testUtils.testWithClient('client.sInterStore', async client => { - assert.equal( - await client.sInterStore('destination', 'key'), - 0 - ); - }, GLOBAL.SERVERS.OPEN); + testUtils.testAll('sInterStore', async client => { + assert.equal( + await client.sInterStore('{tag}destination', '{tag}key'), + 0 + ); + }, { + client: GLOBAL.SERVERS.OPEN, + cluster: GLOBAL.CLUSTERS.OPEN + }); }); diff --git a/packages/client/lib/commands/SINTERSTORE.ts b/packages/client/lib/commands/SINTERSTORE.ts index 02bf9d061a0..744e0b18456 100644 --- a/packages/client/lib/commands/SINTERSTORE.ts +++ b/packages/client/lib/commands/SINTERSTORE.ts @@ -1,13 +1,14 @@ -import { RedisCommandArgument, RedisCommandArguments } from '.'; -import { pushVerdictArguments } from './generic-transformers'; +import { RedisArgument, NumberReply, Command } from '../RESP/types'; +import { RedisVariadicArgument, pushVariadicArguments } from './generic-transformers'; -export const FIRST_KEY_INDEX = 1; - -export function transformArguments( - destination: RedisCommandArgument, - keys: RedisCommandArgument | Array -): RedisCommandArguments { - return pushVerdictArguments(['SINTERSTORE', destination], keys); -} - -export declare function transformReply(): Array; +export default { + FIRST_KEY_INDEX: 1, + IS_READ_ONLY: false, + transformArguments( + destination: RedisArgument, + keys: RedisVariadicArgument + ) { + return pushVariadicArguments(['SINTERSTORE', destination], keys); + }, + transformReply: undefined as unknown as () => NumberReply +} as const satisfies Command; diff --git a/packages/client/lib/commands/SISMEMBER.spec.ts b/packages/client/lib/commands/SISMEMBER.spec.ts index 8d18c83697a..0c1b92614cb 100644 --- a/packages/client/lib/commands/SISMEMBER.spec.ts +++ b/packages/client/lib/commands/SISMEMBER.spec.ts @@ -1,19 +1,22 @@ -import { strict as assert } from 'assert'; +import { strict as assert } from 'node:assert'; import testUtils, { GLOBAL } from '../test-utils'; -import { transformArguments } from './SISMEMBER'; +import SISMEMBER from './SISMEMBER'; describe('SISMEMBER', () => { - it('transformArguments', () => { - assert.deepEqual( - transformArguments('key', 'member'), - ['SISMEMBER', 'key', 'member'] - ); - }); + it('transformArguments', () => { + assert.deepEqual( + SISMEMBER.transformArguments('key', 'member'), + ['SISMEMBER', 'key', 'member'] + ); + }); - testUtils.testWithClient('client.sIsMember', async client => { - assert.equal( - await client.sIsMember('key', 'member'), - false - ); - }, GLOBAL.SERVERS.OPEN); + testUtils.testAll('sIsMember', async client => { + assert.equal( + await client.sIsMember('key', 'member'), + 0 + ); + }, { + client: GLOBAL.SERVERS.OPEN, + cluster: GLOBAL.CLUSTERS.OPEN + }); }); diff --git a/packages/client/lib/commands/SISMEMBER.ts b/packages/client/lib/commands/SISMEMBER.ts index 4d40c63250e..0687d19de30 100644 --- a/packages/client/lib/commands/SISMEMBER.ts +++ b/packages/client/lib/commands/SISMEMBER.ts @@ -1,12 +1,10 @@ -import { RedisCommandArgument, RedisCommandArguments } from '.'; +import { NumberReply, Command, RedisArgument } from '../RESP/types'; -export const FIRST_KEY_INDEX = 1; - -export function transformArguments( - key: RedisCommandArgument, - member: RedisCommandArgument -): RedisCommandArguments { +export default { + FIRST_KEY_INDEX: 1, + IS_READ_ONLY: true, + transformArguments(key: RedisArgument, member: RedisArgument) { return ['SISMEMBER', key, member]; -} - -export { transformBooleanReply as transformReply } from './generic-transformers'; + }, + transformReply: undefined as unknown as () => NumberReply +} as const satisfies Command; diff --git a/packages/client/lib/commands/SMEMBERS.spec.ts b/packages/client/lib/commands/SMEMBERS.spec.ts index b9c58c9eebb..016b01ff747 100644 --- a/packages/client/lib/commands/SMEMBERS.spec.ts +++ b/packages/client/lib/commands/SMEMBERS.spec.ts @@ -1,19 +1,22 @@ -import { strict as assert } from 'assert'; +import { strict as assert } from 'node:assert'; import testUtils, { GLOBAL } from '../test-utils'; -import { transformArguments } from './SMEMBERS'; +import SMEMBERS from './SMEMBERS'; describe('SMEMBERS', () => { - it('transformArguments', () => { - assert.deepEqual( - transformArguments('key'), - ['SMEMBERS', 'key'] - ); - }); + it('transformArguments', () => { + assert.deepEqual( + SMEMBERS.transformArguments('key'), + ['SMEMBERS', 'key'] + ); + }); - testUtils.testWithClient('client.sMembers', async client => { - assert.deepEqual( - await client.sMembers('key'), - [] - ); - }, GLOBAL.SERVERS.OPEN); + testUtils.testAll('sMembers', async client => { + assert.deepEqual( + await client.sMembers('key'), + [] + ); + }, { + client: GLOBAL.SERVERS.OPEN, + cluster: GLOBAL.CLUSTERS.OPEN + }); }); diff --git a/packages/client/lib/commands/SMEMBERS.ts b/packages/client/lib/commands/SMEMBERS.ts index 7950a4c073a..391c83af6c6 100644 --- a/packages/client/lib/commands/SMEMBERS.ts +++ b/packages/client/lib/commands/SMEMBERS.ts @@ -1,9 +1,13 @@ -import { RedisCommandArgument, RedisCommandArguments } from '.'; +import { RedisArgument, ArrayReply, BlobStringReply, SetReply, Command } from '../RESP/types'; -export const FIRST_KEY_INDEX = 1; - -export function transformArguments(key: RedisCommandArgument): RedisCommandArguments { +export default { + FIRST_KEY_INDEX: 1, + IS_READ_ONLY: true, + transformArguments(key: RedisArgument) { return ['SMEMBERS', key]; -} - -export declare function transformReply(): Array; + }, + transformReply: { + 2: undefined as unknown as () => ArrayReply, + 3: undefined as unknown as () => SetReply + } +} as const satisfies Command; diff --git a/packages/client/lib/commands/SMISMEMBER.spec.ts b/packages/client/lib/commands/SMISMEMBER.spec.ts index e3728134029..273ab05dd75 100644 --- a/packages/client/lib/commands/SMISMEMBER.spec.ts +++ b/packages/client/lib/commands/SMISMEMBER.spec.ts @@ -1,21 +1,24 @@ -import { strict as assert } from 'assert'; +import { strict as assert } from 'node:assert'; import testUtils, { GLOBAL } from '../test-utils'; -import { transformArguments } from './SMISMEMBER'; +import SMISMEMBER from './SMISMEMBER'; describe('SMISMEMBER', () => { - testUtils.isVersionGreaterThanHook([6, 2]); + testUtils.isVersionGreaterThanHook([6, 2]); - it('transformArguments', () => { - assert.deepEqual( - transformArguments('key', ['1', '2']), - ['SMISMEMBER', 'key', '1', '2'] - ); - }); + it('transformArguments', () => { + assert.deepEqual( + SMISMEMBER.transformArguments('key', ['1', '2']), + ['SMISMEMBER', 'key', '1', '2'] + ); + }); - testUtils.testWithClient('client.smIsMember', async client => { - assert.deepEqual( - await client.smIsMember('key', ['1', '2']), - [false, false] - ); - }, GLOBAL.SERVERS.OPEN); + testUtils.testAll('smIsMember', async client => { + assert.deepEqual( + await client.smIsMember('key', ['1', '2']), + [0, 0] + ); + }, { + client: GLOBAL.SERVERS.OPEN, + cluster: GLOBAL.CLUSTERS.OPEN + }); }); diff --git a/packages/client/lib/commands/SMISMEMBER.ts b/packages/client/lib/commands/SMISMEMBER.ts index 175120bdfb9..bdf48d45ab4 100644 --- a/packages/client/lib/commands/SMISMEMBER.ts +++ b/packages/client/lib/commands/SMISMEMBER.ts @@ -1,12 +1,10 @@ -import { RedisCommandArgument, RedisCommandArguments } from '.'; +import { RedisArgument, ArrayReply, NumberReply, Command } from '../RESP/types'; -export const FIRST_KEY_INDEX = 1; - -export function transformArguments( - key: RedisCommandArgument, - members: Array -): RedisCommandArguments { +export default { + FIRST_KEY_INDEX: 1, + IS_READ_ONLY: true, + transformArguments(key: RedisArgument, members: Array) { return ['SMISMEMBER', key, ...members]; -} - -export { transformBooleanArrayReply as transformReply } from './generic-transformers'; + }, + transformReply: undefined as unknown as () => ArrayReply +} as const satisfies Command; diff --git a/packages/client/lib/commands/SMOVE.spec.ts b/packages/client/lib/commands/SMOVE.spec.ts index e3308ee8143..7ff2f773a7b 100644 --- a/packages/client/lib/commands/SMOVE.spec.ts +++ b/packages/client/lib/commands/SMOVE.spec.ts @@ -1,19 +1,22 @@ -import { strict as assert } from 'assert'; +import { strict as assert } from 'node:assert'; import testUtils, { GLOBAL } from '../test-utils'; -import { transformArguments } from './SMOVE'; +import SMOVE from './SMOVE'; describe('SMOVE', () => { - it('transformArguments', () => { - assert.deepEqual( - transformArguments('source', 'destination', 'member'), - ['SMOVE', 'source', 'destination', 'member'] - ); - }); + it('transformArguments', () => { + assert.deepEqual( + SMOVE.transformArguments('source', 'destination', 'member'), + ['SMOVE', 'source', 'destination', 'member'] + ); + }); - testUtils.testWithClient('client.sMove', async client => { - assert.equal( - await client.sMove('source', 'destination', 'member'), - false - ); - }, GLOBAL.SERVERS.OPEN); + testUtils.testAll('sMove', async client => { + assert.equal( + await client.sMove('{tag}source', '{tag}destination', 'member'), + 0 + ); + }, { + client: GLOBAL.SERVERS.OPEN, + cluster: GLOBAL.CLUSTERS.OPEN + }); }); diff --git a/packages/client/lib/commands/SMOVE.ts b/packages/client/lib/commands/SMOVE.ts index 83c4027dbd5..183b363fb90 100644 --- a/packages/client/lib/commands/SMOVE.ts +++ b/packages/client/lib/commands/SMOVE.ts @@ -1,13 +1,14 @@ -import { RedisCommandArgument, RedisCommandArguments } from '.'; +import { RedisArgument, NumberReply, Command } from '../RESP/types'; -export const FIRST_KEY_INDEX = 1; - -export function transformArguments( - source: RedisCommandArgument, - destination: RedisCommandArgument, - member: RedisCommandArgument -): RedisCommandArguments { +export default { + FIRST_KEY_INDEX: 1, + IS_READ_ONLY: false, + transformArguments( + source: RedisArgument, + destination: RedisArgument, + member: RedisArgument + ) { return ['SMOVE', source, destination, member]; -} - -export { transformBooleanReply as transformReply } from './generic-transformers'; + }, + transformReply: undefined as unknown as () => NumberReply +} as const satisfies Command; diff --git a/packages/client/lib/commands/SORT.spec.ts b/packages/client/lib/commands/SORT.spec.ts index 4967b020ad5..4fce8113755 100644 --- a/packages/client/lib/commands/SORT.spec.ts +++ b/packages/client/lib/commands/SORT.spec.ts @@ -1,96 +1,99 @@ -import { strict as assert } from 'assert'; +import { strict as assert } from 'node:assert'; import testUtils, { GLOBAL } from '../test-utils'; -import { transformArguments } from './SORT'; +import SORT from './SORT'; describe('SORT', () => { - describe('transformArguments', () => { - it('simple', () => { - assert.deepEqual( - transformArguments('key'), - ['SORT', 'key'] - ); - }); + describe('transformArguments', () => { + it('simple', () => { + assert.deepEqual( + SORT.transformArguments('key'), + ['SORT', 'key'] + ); + }); - it('with BY', () => { - assert.deepEqual( - transformArguments('key', { - BY: 'pattern' - }), - ['SORT', 'key', 'BY', 'pattern'] - ); - }); + it('with BY', () => { + assert.deepEqual( + SORT.transformArguments('key', { + BY: 'pattern' + }), + ['SORT', 'key', 'BY', 'pattern'] + ); + }); - it('with LIMIT', () => { - assert.deepEqual( - transformArguments('key', { - LIMIT: { - offset: 0, - count: 1 - } - }), - ['SORT', 'key', 'LIMIT', '0', '1'] - ); - }); + it('with LIMIT', () => { + assert.deepEqual( + SORT.transformArguments('key', { + LIMIT: { + offset: 0, + count: 1 + } + }), + ['SORT', 'key', 'LIMIT', '0', '1'] + ); + }); - describe('with GET', () => { - it('string', () => { - assert.deepEqual( - transformArguments('key', { - GET: 'pattern' - }), - ['SORT', 'key', 'GET', 'pattern'] - ); - }); + describe('with GET', () => { + it('string', () => { + assert.deepEqual( + SORT.transformArguments('key', { + GET: 'pattern' + }), + ['SORT', 'key', 'GET', 'pattern'] + ); + }); - it('array', () => { - assert.deepEqual( - transformArguments('key', { - GET: ['1', '2'] - }), - ['SORT', 'key', 'GET', '1', 'GET', '2'] - ); - }); - }); + it('array', () => { + assert.deepEqual( + SORT.transformArguments('key', { + GET: ['1', '2'] + }), + ['SORT', 'key', 'GET', '1', 'GET', '2'] + ); + }); + }); - it('with DIRECTION', () => { - assert.deepEqual( - transformArguments('key', { - DIRECTION: 'ASC' - }), - ['SORT', 'key', 'ASC'] - ); - }); + it('with DIRECTION', () => { + assert.deepEqual( + SORT.transformArguments('key', { + DIRECTION: 'ASC' + }), + ['SORT', 'key', 'ASC'] + ); + }); - it('with ALPHA', () => { - assert.deepEqual( - transformArguments('key', { - ALPHA: true - }), - ['SORT', 'key', 'ALPHA'] - ); - }); + it('with ALPHA', () => { + assert.deepEqual( + SORT.transformArguments('key', { + ALPHA: true + }), + ['SORT', 'key', 'ALPHA'] + ); + }); - it('with BY, LIMIT, GET, DIRECTION, ALPHA', () => { - assert.deepEqual( - transformArguments('key', { - BY: 'pattern', - LIMIT: { - offset: 0, - count: 1 - }, - GET: 'pattern', - DIRECTION: 'ASC', - ALPHA: true - }), - ['SORT', 'key', 'BY', 'pattern', 'LIMIT', '0', '1', 'GET', 'pattern', 'ASC', 'ALPHA'] - ); - }); + it('with BY, LIMIT, GET, DIRECTION, ALPHA', () => { + assert.deepEqual( + SORT.transformArguments('key', { + BY: 'pattern', + LIMIT: { + offset: 0, + count: 1 + }, + GET: 'pattern', + DIRECTION: 'ASC', + ALPHA: true + }), + ['SORT', 'key', 'BY', 'pattern', 'LIMIT', '0', '1', 'GET', 'pattern', 'ASC', 'ALPHA'] + ); }); + }); - testUtils.testWithClient('client.sort', async client => { - assert.deepEqual( - await client.sort('key'), - [] - ); - }, GLOBAL.SERVERS.OPEN); + testUtils.testAll('sort', async client => { + assert.deepEqual( + await client.sort('key'), + [] + ); + }, { + client: GLOBAL.SERVERS.OPEN, + cluster: GLOBAL.CLUSTERS.OPEN + }); }); diff --git a/packages/client/lib/commands/SORT.ts b/packages/client/lib/commands/SORT.ts index 15e95bde677..b71383943e9 100644 --- a/packages/client/lib/commands/SORT.ts +++ b/packages/client/lib/commands/SORT.ts @@ -1,13 +1,59 @@ -import { RedisCommandArguments } from '.'; -import { pushSortArguments, SortOptions } from './generic-transformers'; +import { RedisArgument, ArrayReply, BlobStringReply, Command } from '../RESP/types'; -export const FIRST_KEY_INDEX = 1; +export interface SortOptions { + BY?: RedisArgument; + LIMIT?: { + offset: number; + count: number; + }; + GET?: RedisArgument | Array; + DIRECTION?: 'ASC' | 'DESC'; + ALPHA?: boolean; +} + +export function transformSortArguments( + command: RedisArgument, + key: RedisArgument, + options?: SortOptions +) { + const args: Array = [command, key]; + + if (options?.BY) { + args.push('BY', options.BY); + } + + if (options?.LIMIT) { + args.push( + 'LIMIT', + options.LIMIT.offset.toString(), + options.LIMIT.count.toString() + ); + } + + if (options?.GET) { + if (Array.isArray(options.GET)) { + for (const pattern of options.GET) { + args.push('GET', pattern); + } + } else { + args.push('GET', options.GET); + } + } + + if (options?.DIRECTION) { + args.push(options.DIRECTION); + } + + if (options?.ALPHA) { + args.push('ALPHA'); + } -export function transformArguments( - key: string, - options?: SortOptions -): RedisCommandArguments { - return pushSortArguments(['SORT', key], options); + return args; } -export declare function transformReply(): Array; +export default { + FIRST_KEY_INDEX: 1, + IS_READ_ONLY: true, + transformArguments: transformSortArguments.bind(undefined, 'SORT'), + transformReply: undefined as unknown as () => ArrayReply +} as const satisfies Command; diff --git a/packages/client/lib/commands/SORT_RO.spec.ts b/packages/client/lib/commands/SORT_RO.spec.ts index fe3ca1240d7..963416ae639 100644 --- a/packages/client/lib/commands/SORT_RO.spec.ts +++ b/packages/client/lib/commands/SORT_RO.spec.ts @@ -1,98 +1,101 @@ -import { strict as assert } from 'assert'; +import { strict as assert } from 'node:assert'; import testUtils, { GLOBAL } from '../test-utils'; -import { transformArguments } from './SORT_RO'; +import SORT_RO from './SORT_RO'; describe('SORT_RO', () => { - testUtils.isVersionGreaterThanHook([7]); + testUtils.isVersionGreaterThanHook([7]); - describe('transformArguments', () => { - it('simple', () => { - assert.deepEqual( - transformArguments('key'), - ['SORT_RO', 'key'] - ); - }); + describe('transformArguments', () => { + it('simple', () => { + assert.deepEqual( + SORT_RO.transformArguments('key'), + ['SORT_RO', 'key'] + ); + }); - it('with BY', () => { - assert.deepEqual( - transformArguments('key', { - BY: 'pattern' - }), - ['SORT_RO', 'key', 'BY', 'pattern'] - ); - }); + it('with BY', () => { + assert.deepEqual( + SORT_RO.transformArguments('key', { + BY: 'pattern' + }), + ['SORT_RO', 'key', 'BY', 'pattern'] + ); + }); - it('with LIMIT', () => { - assert.deepEqual( - transformArguments('key', { - LIMIT: { - offset: 0, - count: 1 - } - }), - ['SORT_RO', 'key', 'LIMIT', '0', '1'] - ); - }); + it('with LIMIT', () => { + assert.deepEqual( + SORT_RO.transformArguments('key', { + LIMIT: { + offset: 0, + count: 1 + } + }), + ['SORT_RO', 'key', 'LIMIT', '0', '1'] + ); + }); - describe('with GET', () => { - it('string', () => { - assert.deepEqual( - transformArguments('key', { - GET: 'pattern' - }), - ['SORT_RO', 'key', 'GET', 'pattern'] - ); - }); + describe('with GET', () => { + it('string', () => { + assert.deepEqual( + SORT_RO.transformArguments('key', { + GET: 'pattern' + }), + ['SORT_RO', 'key', 'GET', 'pattern'] + ); + }); - it('array', () => { - assert.deepEqual( - transformArguments('key', { - GET: ['1', '2'] - }), - ['SORT_RO', 'key', 'GET', '1', 'GET', '2'] - ); - }); - }); + it('array', () => { + assert.deepEqual( + SORT_RO.transformArguments('key', { + GET: ['1', '2'] + }), + ['SORT_RO', 'key', 'GET', '1', 'GET', '2'] + ); + }); + }); - it('with DIRECTION', () => { - assert.deepEqual( - transformArguments('key', { - DIRECTION: 'ASC' - }), - ['SORT_RO', 'key', 'ASC'] - ); - }); + it('with DIRECTION', () => { + assert.deepEqual( + SORT_RO.transformArguments('key', { + DIRECTION: 'ASC' + }), + ['SORT_RO', 'key', 'ASC'] + ); + }); - it('with ALPHA', () => { - assert.deepEqual( - transformArguments('key', { - ALPHA: true - }), - ['SORT_RO', 'key', 'ALPHA'] - ); - }); + it('with ALPHA', () => { + assert.deepEqual( + SORT_RO.transformArguments('key', { + ALPHA: true + }), + ['SORT_RO', 'key', 'ALPHA'] + ); + }); - it('with BY, LIMIT, GET, DIRECTION, ALPHA', () => { - assert.deepEqual( - transformArguments('key', { - BY: 'pattern', - LIMIT: { - offset: 0, - count: 1 - }, - GET: 'pattern', - DIRECTION: 'ASC', - ALPHA: true, - }), - ['SORT_RO', 'key', 'BY', 'pattern', 'LIMIT', '0', '1', 'GET', 'pattern', 'ASC', 'ALPHA'] - ); - }); + it('with BY, LIMIT, GET, DIRECTION, ALPHA', () => { + assert.deepEqual( + SORT_RO.transformArguments('key', { + BY: 'pattern', + LIMIT: { + offset: 0, + count: 1 + }, + GET: 'pattern', + DIRECTION: 'ASC', + ALPHA: true, + }), + ['SORT_RO', 'key', 'BY', 'pattern', 'LIMIT', '0', '1', 'GET', 'pattern', 'ASC', 'ALPHA'] + ); }); + }); - testUtils.testWithClient('client.sortRo', async client => { - assert.deepEqual( - await client.sortRo('key'), - [] - ); - }, GLOBAL.SERVERS.OPEN); + testUtils.testAll('sortRo', async client => { + assert.deepEqual( + await client.sortRo('key'), + [] + ); + }, { + client: GLOBAL.SERVERS.OPEN, + cluster: GLOBAL.CLUSTERS.OPEN + }); }); diff --git a/packages/client/lib/commands/SORT_RO.ts b/packages/client/lib/commands/SORT_RO.ts index 4af7acd80d7..459a0bbc03d 100644 --- a/packages/client/lib/commands/SORT_RO.ts +++ b/packages/client/lib/commands/SORT_RO.ts @@ -1,15 +1,9 @@ -import { RedisCommandArguments } from '.'; -import { pushSortArguments, SortOptions } from "./generic-transformers"; - -export const FIRST_KEY_INDEX = 1; - -export const IS_READ_ONLY = true; - -export function transformArguments( - key: string, - options?: SortOptions -): RedisCommandArguments { - return pushSortArguments(['SORT_RO', key], options); -} - -export declare function transformReply(): Array; +import { Command } from '../RESP/types'; +import SORT, { transformSortArguments } from './SORT'; + +export default { + FIRST_KEY_INDEX: SORT.FIRST_KEY_INDEX, + IS_READ_ONLY: true, + transformArguments: transformSortArguments.bind(undefined, 'SORT_RO'), + transformReply: SORT.transformReply +} as const satisfies Command; diff --git a/packages/client/lib/commands/SORT_STORE.spec.ts b/packages/client/lib/commands/SORT_STORE.spec.ts index d078135255d..49efd4c6ad8 100644 --- a/packages/client/lib/commands/SORT_STORE.spec.ts +++ b/packages/client/lib/commands/SORT_STORE.spec.ts @@ -1,96 +1,99 @@ -import { strict as assert } from 'assert'; +import { strict as assert } from 'node:assert'; import testUtils, { GLOBAL } from '../test-utils'; -import { transformArguments } from './SORT_STORE'; +import SORT_STORE from './SORT_STORE'; describe('SORT STORE', () => { - describe('transformArguments', () => { - it('simple', () => { - assert.deepEqual( - transformArguments('source', 'destination'), - ['SORT', 'source', 'STORE', 'destination'] - ); - }); + describe('transformArguments', () => { + it('simple', () => { + assert.deepEqual( + SORT_STORE.transformArguments('source', 'destination'), + ['SORT', 'source', 'STORE', 'destination'] + ); + }); - it('with BY', () => { - assert.deepEqual( - transformArguments('source', 'destination', { - BY: 'pattern' - }), - ['SORT', 'source', 'BY', 'pattern', 'STORE', 'destination'] - ); - }); + it('with BY', () => { + assert.deepEqual( + SORT_STORE.transformArguments('source', 'destination', { + BY: 'pattern' + }), + ['SORT', 'source', 'BY', 'pattern', 'STORE', 'destination'] + ); + }); - it('with LIMIT', () => { - assert.deepEqual( - transformArguments('source', 'destination', { - LIMIT: { - offset: 0, - count: 1 - } - }), - ['SORT', 'source', 'LIMIT', '0', '1', 'STORE', 'destination'] - ); - }); + it('with LIMIT', () => { + assert.deepEqual( + SORT_STORE.transformArguments('source', 'destination', { + LIMIT: { + offset: 0, + count: 1 + } + }), + ['SORT', 'source', 'LIMIT', '0', '1', 'STORE', 'destination'] + ); + }); - describe('with GET', () => { - it('string', () => { - assert.deepEqual( - transformArguments('source', 'destination', { - GET: 'pattern' - }), - ['SORT', 'source', 'GET', 'pattern', 'STORE', 'destination'] - ); - }); + describe('with GET', () => { + it('string', () => { + assert.deepEqual( + SORT_STORE.transformArguments('source', 'destination', { + GET: 'pattern' + }), + ['SORT', 'source', 'GET', 'pattern', 'STORE', 'destination'] + ); + }); - it('array', () => { - assert.deepEqual( - transformArguments('source', 'destination', { - GET: ['1', '2'] - }), - ['SORT', 'source', 'GET', '1', 'GET', '2', 'STORE', 'destination'] - ); - }); - }); + it('array', () => { + assert.deepEqual( + SORT_STORE.transformArguments('source', 'destination', { + GET: ['1', '2'] + }), + ['SORT', 'source', 'GET', '1', 'GET', '2', 'STORE', 'destination'] + ); + }); + }); - it('with DIRECTION', () => { - assert.deepEqual( - transformArguments('source', 'destination', { - DIRECTION: 'ASC' - }), - ['SORT', 'source', 'ASC', 'STORE', 'destination'] - ); - }); + it('with DIRECTION', () => { + assert.deepEqual( + SORT_STORE.transformArguments('source', 'destination', { + DIRECTION: 'ASC' + }), + ['SORT', 'source', 'ASC', 'STORE', 'destination'] + ); + }); - it('with ALPHA', () => { - assert.deepEqual( - transformArguments('source', 'destination', { - ALPHA: true - }), - ['SORT', 'source', 'ALPHA', 'STORE', 'destination'] - ); - }); + it('with ALPHA', () => { + assert.deepEqual( + SORT_STORE.transformArguments('source', 'destination', { + ALPHA: true + }), + ['SORT', 'source', 'ALPHA', 'STORE', 'destination'] + ); + }); - it('with BY, LIMIT, GET, DIRECTION, ALPHA', () => { - assert.deepEqual( - transformArguments('source', 'destination', { - BY: 'pattern', - LIMIT: { - offset: 0, - count: 1 - }, - GET: 'pattern', - DIRECTION: 'ASC', - ALPHA: true - }), - ['SORT', 'source', 'BY', 'pattern', 'LIMIT', '0', '1', 'GET', 'pattern', 'ASC', 'ALPHA', 'STORE', 'destination'] - ); - }); + it('with BY, LIMIT, GET, DIRECTION, ALPHA', () => { + assert.deepEqual( + SORT_STORE.transformArguments('source', 'destination', { + BY: 'pattern', + LIMIT: { + offset: 0, + count: 1 + }, + GET: 'pattern', + DIRECTION: 'ASC', + ALPHA: true + }), + ['SORT', 'source', 'BY', 'pattern', 'LIMIT', '0', '1', 'GET', 'pattern', 'ASC', 'ALPHA', 'STORE', 'destination'] + ); }); + }); - testUtils.testWithClient('client.sortStore', async client => { - assert.equal( - await client.sortStore('source', 'destination'), - 0 - ); - }, GLOBAL.SERVERS.OPEN); + testUtils.testAll('sortStore', async client => { + assert.equal( + await client.sortStore('{tag}source', '{tag}destination'), + 0 + ); + }, { + client: GLOBAL.SERVERS.OPEN, + cluster: GLOBAL.CLUSTERS.OPEN + }); }); diff --git a/packages/client/lib/commands/SORT_STORE.ts b/packages/client/lib/commands/SORT_STORE.ts index 9acaf023175..b6ad709fb60 100644 --- a/packages/client/lib/commands/SORT_STORE.ts +++ b/packages/client/lib/commands/SORT_STORE.ts @@ -1,17 +1,17 @@ -import { RedisCommandArguments } from '.'; -import { SortOptions } from './generic-transformers'; -import { transformArguments as transformSortArguments } from './SORT'; +import { RedisArgument, NumberReply, Command } from '../RESP/types'; +import SORT, { SortOptions } from './SORT'; -export const FIRST_KEY_INDEX = 1; - -export function transformArguments( - source: string, - destination: string, +export default { + FIRST_KEY_INDEX: 1, + IS_READ_ONLY: false, + transformArguments( + source: RedisArgument, + destination: RedisArgument, options?: SortOptions -): RedisCommandArguments { - const args = transformSortArguments(source, options); + ) { + const args = SORT.transformArguments(source, options); args.push('STORE', destination); return args; -} - -export declare function transformReply(): number; + }, + transformReply: undefined as unknown as () => NumberReply +} as const satisfies Command; diff --git a/packages/client/lib/commands/SPOP.spec.ts b/packages/client/lib/commands/SPOP.spec.ts index 6a384d181fc..896c1c820ac 100644 --- a/packages/client/lib/commands/SPOP.spec.ts +++ b/packages/client/lib/commands/SPOP.spec.ts @@ -1,28 +1,22 @@ -import { strict as assert } from 'assert'; +import { strict as assert } from 'node:assert'; import testUtils, { GLOBAL } from '../test-utils'; -import { transformArguments } from './SPOP'; +import SPOP from './SPOP'; describe('SPOP', () => { - describe('transformArguments', () => { - it('simple', () => { - assert.deepEqual( - transformArguments('key'), - ['SPOP', 'key'] - ); - }); + it('transformArguments', () => { + assert.deepEqual( + SPOP.transformArguments('key'), + ['SPOP', 'key'] + ); + }); - it('with count', () => { - assert.deepEqual( - transformArguments('key', 2), - ['SPOP', 'key', '2'] - ); - }); - }); - - testUtils.testWithClient('client.sPop', async client => { - assert.equal( - await client.sPop('key'), - null - ); - }, GLOBAL.SERVERS.OPEN); + testUtils.testAll('sPop', async client => { + assert.equal( + await client.sPop('key'), + null + ); + }, { + client: GLOBAL.SERVERS.OPEN, + cluster: GLOBAL.CLUSTERS.OPEN + }); }); diff --git a/packages/client/lib/commands/SPOP.ts b/packages/client/lib/commands/SPOP.ts index 38ce8573f3f..4b061a86306 100644 --- a/packages/client/lib/commands/SPOP.ts +++ b/packages/client/lib/commands/SPOP.ts @@ -1,18 +1,10 @@ -import { RedisCommandArgument, RedisCommandArguments } from '.'; - -export const FIRST_KEY_INDEX = 1; - -export function transformArguments( - key: RedisCommandArgument, - count?: number -): RedisCommandArguments { - const args = ['SPOP', key]; - - if (typeof count === 'number') { - args.push(count.toString()); - } - - return args; -} - -export declare function transformReply(): Array; +import { RedisArgument, BlobStringReply, NullReply, Command } from '../RESP/types'; + +export default { + FIRST_KEY_INDEX: 1, + IS_READ_ONLY: false, + transformArguments(key: RedisArgument) { + return ['SPOP', key]; + }, + transformReply: undefined as unknown as () => BlobStringReply | NullReply +} as const satisfies Command; diff --git a/packages/client/lib/commands/SPOP_COUNT.spec.ts b/packages/client/lib/commands/SPOP_COUNT.spec.ts new file mode 100644 index 00000000000..ddad816b420 --- /dev/null +++ b/packages/client/lib/commands/SPOP_COUNT.spec.ts @@ -0,0 +1,22 @@ +import { strict as assert } from 'node:assert'; +import testUtils, { GLOBAL } from '../test-utils'; +import SPOP_COUNT from './SPOP_COUNT'; + +describe('SPOP_COUNT', () => { + it('transformArguments', () => { + assert.deepEqual( + SPOP_COUNT.transformArguments('key', 1), + ['SPOP', 'key', '1'] + ); + }); + + testUtils.testAll('sPopCount', async client => { + assert.deepEqual( + await client.sPopCount('key', 1), + [] + ); + }, { + client: GLOBAL.SERVERS.OPEN, + cluster: GLOBAL.CLUSTERS.OPEN + }); +}); diff --git a/packages/client/lib/commands/SPOP_COUNT.ts b/packages/client/lib/commands/SPOP_COUNT.ts new file mode 100644 index 00000000000..4c68ae8d08e --- /dev/null +++ b/packages/client/lib/commands/SPOP_COUNT.ts @@ -0,0 +1,10 @@ +import { RedisArgument, BlobStringReply, NullReply, Command } from '../RESP/types'; + +export default { + FIRST_KEY_INDEX: 1, + IS_READ_ONLY: false, + transformArguments(key: RedisArgument, count: number) { + return ['SPOP', key, count.toString()]; + }, + transformReply: undefined as unknown as () => BlobStringReply | NullReply +} as const satisfies Command; diff --git a/packages/client/lib/commands/SPUBLISH.spec.ts b/packages/client/lib/commands/SPUBLISH.spec.ts index 60b6ce2dad0..741372d0154 100644 --- a/packages/client/lib/commands/SPUBLISH.spec.ts +++ b/packages/client/lib/commands/SPUBLISH.spec.ts @@ -1,21 +1,24 @@ -import { strict as assert } from 'assert'; +import { strict as assert } from 'node:assert'; import testUtils, { GLOBAL } from '../test-utils'; -import { transformArguments } from './SPUBLISH'; +import SPUBLISH from './SPUBLISH'; describe('SPUBLISH', () => { - testUtils.isVersionGreaterThanHook([7]); - - it('transformArguments', () => { - assert.deepEqual( - transformArguments('channel', 'message'), - ['SPUBLISH', 'channel', 'message'] - ); - }); + testUtils.isVersionGreaterThanHook([7]); - testUtils.testWithClient('client.sPublish', async client => { - assert.equal( - await client.sPublish('channel', 'message'), - 0 - ); - }, GLOBAL.SERVERS.OPEN); + it('transformArguments', () => { + assert.deepEqual( + SPUBLISH.transformArguments('channel', 'message'), + ['SPUBLISH', 'channel', 'message'] + ); + }); + + testUtils.testAll('sPublish', async client => { + assert.equal( + await client.sPublish('channel', 'message'), + 0 + ); + }, { + client: GLOBAL.SERVERS.OPEN, + cluster: GLOBAL.CLUSTERS.OPEN + }); }); diff --git a/packages/client/lib/commands/SPUBLISH.ts b/packages/client/lib/commands/SPUBLISH.ts index 42a7ab49072..19d84b03c6f 100644 --- a/packages/client/lib/commands/SPUBLISH.ts +++ b/packages/client/lib/commands/SPUBLISH.ts @@ -1,14 +1,10 @@ -import { RedisCommandArgument, RedisCommandArguments } from '.'; +import { RedisArgument, NumberReply, Command } from '../RESP/types'; -export const IS_READ_ONLY = true; - -export const FIRST_KEY_INDEX = 1; - -export function transformArguments( - channel: RedisCommandArgument, - message: RedisCommandArgument -): RedisCommandArguments { +export default { + FIRST_KEY_INDEX: 1, + IS_READ_ONLY: true, + transformArguments(channel: RedisArgument, message: RedisArgument) { return ['SPUBLISH', channel, message]; -} - -export declare function transformReply(): number; + }, + transformReply: undefined as unknown as () => NumberReply +} as const satisfies Command; diff --git a/packages/client/lib/commands/SRANDMEMBER.spec.ts b/packages/client/lib/commands/SRANDMEMBER.spec.ts index 291271540be..a7df01f0eb9 100644 --- a/packages/client/lib/commands/SRANDMEMBER.spec.ts +++ b/packages/client/lib/commands/SRANDMEMBER.spec.ts @@ -1,19 +1,22 @@ -import { strict as assert } from 'assert'; +import { strict as assert } from 'node:assert'; import testUtils, { GLOBAL } from '../test-utils'; -import { transformArguments } from './SRANDMEMBER'; +import SRANDMEMBER from './SRANDMEMBER'; describe('SRANDMEMBER', () => { - it('transformArguments', () => { - assert.deepEqual( - transformArguments('key'), - ['SRANDMEMBER', 'key'] - ); - }); + it('transformArguments', () => { + assert.deepEqual( + SRANDMEMBER.transformArguments('key'), + ['SRANDMEMBER', 'key'] + ); + }); - testUtils.testWithClient('client.sRandMember', async client => { - assert.equal( - await client.sRandMember('key'), - null - ); - }, GLOBAL.SERVERS.OPEN); + testUtils.testAll('sRandMember', async client => { + assert.equal( + await client.sRandMember('key'), + null + ); + }, { + client: GLOBAL.SERVERS.OPEN, + cluster: GLOBAL.CLUSTERS.OPEN + }); }); diff --git a/packages/client/lib/commands/SRANDMEMBER.ts b/packages/client/lib/commands/SRANDMEMBER.ts index d84e61993e5..6a2373ae927 100644 --- a/packages/client/lib/commands/SRANDMEMBER.ts +++ b/packages/client/lib/commands/SRANDMEMBER.ts @@ -1,9 +1,10 @@ -import { RedisCommandArgument, RedisCommandArguments } from '.'; +import { RedisArgument, BlobStringReply, NullReply, Command } from '../RESP/types'; -export const FIRST_KEY_INDEX = 1; - -export function transformArguments(key: RedisCommandArgument): RedisCommandArguments { +export default { + FIRST_KEY_INDEX: 1, + IS_READ_ONLY: true, + transformArguments(key: RedisArgument) { return ['SRANDMEMBER', key]; -} - -export declare function transformReply(): RedisCommandArgument | null; + }, + transformReply: undefined as unknown as () => BlobStringReply | NullReply +} as const satisfies Command; diff --git a/packages/client/lib/commands/SRANDMEMBER_COUNT.spec.ts b/packages/client/lib/commands/SRANDMEMBER_COUNT.spec.ts index d3d787b3e63..364eb640341 100644 --- a/packages/client/lib/commands/SRANDMEMBER_COUNT.spec.ts +++ b/packages/client/lib/commands/SRANDMEMBER_COUNT.spec.ts @@ -1,19 +1,22 @@ -import { strict as assert } from 'assert'; +import { strict as assert } from 'node:assert'; import testUtils, { GLOBAL } from '../test-utils'; -import { transformArguments } from './SRANDMEMBER_COUNT'; +import SRANDMEMBER_COUNT from './SRANDMEMBER_COUNT'; describe('SRANDMEMBER COUNT', () => { - it('transformArguments', () => { - assert.deepEqual( - transformArguments('key', 1), - ['SRANDMEMBER', 'key', '1'] - ); - }); + it('transformArguments', () => { + assert.deepEqual( + SRANDMEMBER_COUNT.transformArguments('key', 1), + ['SRANDMEMBER', 'key', '1'] + ); + }); - testUtils.testWithClient('client.sRandMemberCount', async client => { - assert.deepEqual( - await client.sRandMemberCount('key', 1), - [] - ); - }, GLOBAL.SERVERS.OPEN); + testUtils.testAll('sRandMemberCount', async client => { + assert.deepEqual( + await client.sRandMemberCount('key', 1), + [] + ); + }, { + client: GLOBAL.SERVERS.OPEN, + cluster: GLOBAL.CLUSTERS.OPEN + }); }); diff --git a/packages/client/lib/commands/SRANDMEMBER_COUNT.ts b/packages/client/lib/commands/SRANDMEMBER_COUNT.ts index d265d89e9a6..778f3d8f629 100644 --- a/packages/client/lib/commands/SRANDMEMBER_COUNT.ts +++ b/packages/client/lib/commands/SRANDMEMBER_COUNT.ts @@ -1,16 +1,13 @@ -import { RedisCommandArgument, RedisCommandArguments } from '.'; -import { transformArguments as transformSRandMemberArguments } from './SRANDMEMBER'; +import { RedisArgument, ArrayReply, BlobStringReply, Command } from '../RESP/types'; +import SRANDMEMBER from './SRANDMEMBER'; -export { FIRST_KEY_INDEX } from './SRANDMEMBER'; - -export function transformArguments( - key: RedisCommandArgument, - count: number -): RedisCommandArguments { - return [ - ...transformSRandMemberArguments(key), - count.toString() - ]; -} - -export declare function transformReply(): Array; +export default { + FIRST_KEY_INDEX: SRANDMEMBER.FIRST_KEY_INDEX, + IS_READ_ONLY: SRANDMEMBER.IS_READ_ONLY, + transformArguments(key: RedisArgument, count: number) { + const args = SRANDMEMBER.transformArguments(key); + args.push(count.toString()); + return args; + }, + transformReply: undefined as unknown as () => ArrayReply +} as const satisfies Command; diff --git a/packages/client/lib/commands/SREM.spec.ts b/packages/client/lib/commands/SREM.spec.ts index d53d7b0334d..f17c6fb118e 100644 --- a/packages/client/lib/commands/SREM.spec.ts +++ b/packages/client/lib/commands/SREM.spec.ts @@ -1,28 +1,31 @@ -import { strict as assert } from 'assert'; +import { strict as assert } from 'node:assert'; import testUtils, { GLOBAL } from '../test-utils'; -import { transformArguments } from './SREM'; +import SREM from './SREM'; describe('SREM', () => { - describe('transformArguments', () => { - it('string', () => { - assert.deepEqual( - transformArguments('key', 'member'), - ['SREM', 'key', 'member'] - ); - }); + describe('transformArguments', () => { + it('string', () => { + assert.deepEqual( + SREM.transformArguments('key', 'member'), + ['SREM', 'key', 'member'] + ); + }); - it('array', () => { - assert.deepEqual( - transformArguments('key', ['1', '2']), - ['SREM', 'key', '1', '2'] - ); - }); + it('array', () => { + assert.deepEqual( + SREM.transformArguments('key', ['1', '2']), + ['SREM', 'key', '1', '2'] + ); }); + }); - testUtils.testWithClient('client.sRem', async client => { - assert.equal( - await client.sRem('key', 'member'), - 0 - ); - }, GLOBAL.SERVERS.OPEN); + testUtils.testAll('sRem', async client => { + assert.equal( + await client.sRem('key', 'member'), + 0 + ); + }, { + client: GLOBAL.SERVERS.OPEN, + cluster: GLOBAL.CLUSTERS.OPEN + }); }); diff --git a/packages/client/lib/commands/SREM.ts b/packages/client/lib/commands/SREM.ts index 34aebdf02e3..daa95493d02 100644 --- a/packages/client/lib/commands/SREM.ts +++ b/packages/client/lib/commands/SREM.ts @@ -1,13 +1,11 @@ -import { RedisCommandArgument, RedisCommandArguments } from '.'; -import { pushVerdictArguments } from './generic-transformers'; +import { NumberReply, Command, RedisArgument } from '../RESP/types'; +import { RedisVariadicArgument, pushVariadicArguments } from './generic-transformers'; -export const FIRST_KEY_INDEX = 1; - -export function transformArguments( - key: RedisCommandArgument, - members: RedisCommandArgument | Array -): RedisCommandArguments { - return pushVerdictArguments(['SREM', key], members); -} - -export declare function transformReply(): number; +export default { + FIRST_KEY_INDEX: 1, + IS_READ_ONLY: false, + transformArguments(key: RedisArgument, members: RedisVariadicArgument) { + return pushVariadicArguments(['SREM', key], members); + }, + transformReply: undefined as unknown as () => NumberReply +} as const satisfies Command; diff --git a/packages/client/lib/commands/SSCAN.spec.ts b/packages/client/lib/commands/SSCAN.spec.ts index 71a90bf81d8..29a13306fde 100644 --- a/packages/client/lib/commands/SSCAN.spec.ts +++ b/packages/client/lib/commands/SSCAN.spec.ts @@ -1,74 +1,55 @@ -import { strict as assert } from 'assert'; +import { strict as assert } from 'node:assert'; import testUtils, { GLOBAL } from '../test-utils'; -import { transformArguments, transformReply } from './SSCAN'; +import SSCAN from './SSCAN'; describe('SSCAN', () => { - describe('transformArguments', () => { - it('cusror only', () => { - assert.deepEqual( - transformArguments('key', 0), - ['SSCAN', 'key', '0'] - ); - }); - - it('with MATCH', () => { - assert.deepEqual( - transformArguments('key', 0, { - MATCH: 'pattern' - }), - ['SSCAN', 'key', '0', 'MATCH', 'pattern'] - ); - }); - - it('with COUNT', () => { - assert.deepEqual( - transformArguments('key', 0, { - COUNT: 1 - }), - ['SSCAN', 'key', '0', 'COUNT', '1'] - ); - }); + describe('transformArguments', () => { + it('cusror only', () => { + assert.deepEqual( + SSCAN.transformArguments('key', '0'), + ['SSCAN', 'key', '0'] + ); + }); - it('with MATCH & COUNT', () => { - assert.deepEqual( - transformArguments('key', 0, { - MATCH: 'pattern', - COUNT: 1 - }), - ['SSCAN', 'key', '0', 'MATCH', 'pattern', 'COUNT', '1'] - ); - }); + it('with MATCH', () => { + assert.deepEqual( + SSCAN.transformArguments('key', '0', { + MATCH: 'pattern' + }), + ['SSCAN', 'key', '0', 'MATCH', 'pattern'] + ); }); - describe('transformReply', () => { - it('without members', () => { - assert.deepEqual( - transformReply(['0', []]), - { - cursor: 0, - members: [] - } - ); - }); + it('with COUNT', () => { + assert.deepEqual( + SSCAN.transformArguments('key', '0', { + COUNT: 1 + }), + ['SSCAN', 'key', '0', 'COUNT', '1'] + ); + }); - it('with members', () => { - assert.deepEqual( - transformReply(['0', ['member']]), - { - cursor: 0, - members: ['member'] - } - ); - }); + it('with MATCH & COUNT', () => { + assert.deepEqual( + SSCAN.transformArguments('key', '0', { + MATCH: 'pattern', + COUNT: 1 + }), + ['SSCAN', 'key', '0', 'MATCH', 'pattern', 'COUNT', '1'] + ); }); + }); - testUtils.testWithClient('client.sScan', async client => { - assert.deepEqual( - await client.sScan('key', 0), - { - cursor: 0, - members: [] - } - ); - }, GLOBAL.SERVERS.OPEN); + testUtils.testAll('sScan', async client => { + assert.deepEqual( + await client.sScan('key', '0'), + { + cursor: '0', + members: [] + } + ); + }, { + client: GLOBAL.SERVERS.OPEN, + cluster: GLOBAL.CLUSTERS.OPEN + }); }); diff --git a/packages/client/lib/commands/SSCAN.ts b/packages/client/lib/commands/SSCAN.ts index 9b3938f159b..f47144d834c 100644 --- a/packages/client/lib/commands/SSCAN.ts +++ b/packages/client/lib/commands/SSCAN.ts @@ -1,31 +1,20 @@ -import { RedisCommandArgument, RedisCommandArguments } from '.'; -import { ScanOptions, pushScanArguments } from './generic-transformers'; +import { RedisArgument, BlobStringReply, Command } from '../RESP/types'; +import { ScanCommonOptions, pushScanArguments } from './SCAN'; -export const FIRST_KEY_INDEX = 1; - -export const IS_READ_ONLY = true; - -export function transformArguments( - key: RedisCommandArgument, - cursor: number, - options?: ScanOptions -): RedisCommandArguments { - return pushScanArguments([ - 'SSCAN', - key, - ], cursor, options); -} - -type SScanRawReply = [string, Array]; - -interface SScanReply { - cursor: number; - members: Array; -} - -export function transformReply([cursor, members]: SScanRawReply): SScanReply { +export default { + FIRST_KEY_INDEX: 1, + IS_READ_ONLY: true, + transformArguments( + key: RedisArgument, + cursor: RedisArgument, + options?: ScanCommonOptions + ) { + return pushScanArguments(['SSCAN', key], cursor, options); + }, + transformReply([cursor, members]: [BlobStringReply, Array]) { return { - cursor: Number(cursor), - members + cursor, + members }; -} + } +} as const satisfies Command; diff --git a/packages/client/lib/commands/STRLEN.spec.ts b/packages/client/lib/commands/STRLEN.spec.ts index 519c68d3e5d..b07c07b909a 100644 --- a/packages/client/lib/commands/STRLEN.spec.ts +++ b/packages/client/lib/commands/STRLEN.spec.ts @@ -1,26 +1,22 @@ -import { strict as assert } from 'assert'; +import { strict as assert } from 'node:assert'; import testUtils, { GLOBAL } from '../test-utils'; -import { transformArguments } from './STRLEN'; +import STRLEN from './STRLEN'; describe('STRLEN', () => { - it('transformArguments', () => { - assert.deepEqual( - transformArguments('key'), - ['STRLEN', 'key'] - ); - }); + it('transformArguments', () => { + assert.deepEqual( + STRLEN.transformArguments('key'), + ['STRLEN', 'key'] + ); + }); - testUtils.testWithClient('client.strLen', async client => { - assert.equal( - await client.strLen('key'), - 0 - ); - }, GLOBAL.SERVERS.OPEN); - - testUtils.testWithCluster('cluster.strLen', async cluster => { - assert.equal( - await cluster.strLen('key'), - 0 - ); - }, GLOBAL.CLUSTERS.OPEN); + testUtils.testAll('strLen', async client => { + assert.equal( + await client.strLen('key'), + 0 + ); + }, { + client: GLOBAL.SERVERS.OPEN, + cluster: GLOBAL.CLUSTERS.OPEN + }); }); diff --git a/packages/client/lib/commands/STRLEN.ts b/packages/client/lib/commands/STRLEN.ts index de88340d8b6..594530ff6bf 100644 --- a/packages/client/lib/commands/STRLEN.ts +++ b/packages/client/lib/commands/STRLEN.ts @@ -1,11 +1,10 @@ -import { RedisCommandArgument, RedisCommandArguments } from '.'; +import { RedisArgument, NumberReply, Command } from '../RESP/types'; -export const FIRST_KEY_INDEX = 1; - -export const IS_READ_ONLY = true; - -export function transformArguments(key: RedisCommandArgument): RedisCommandArguments { +export default { + FIRST_KEY_INDEX: 1, + IS_READ_ONLY: true, + transformArguments(key: RedisArgument) { return ['STRLEN', key]; -} - -export declare function transformReply(): number; + }, + transformReply: undefined as unknown as () => NumberReply +} as const satisfies Command; diff --git a/packages/client/lib/commands/SUNION.spec.ts b/packages/client/lib/commands/SUNION.spec.ts index 2918607c1d6..ff00c44a1b1 100644 --- a/packages/client/lib/commands/SUNION.spec.ts +++ b/packages/client/lib/commands/SUNION.spec.ts @@ -1,28 +1,31 @@ -import { strict as assert } from 'assert'; +import { strict as assert } from 'node:assert'; import testUtils, { GLOBAL } from '../test-utils'; -import { transformArguments } from './SUNION'; +import SUNION from './SUNION'; describe('SUNION', () => { - describe('transformArguments', () => { - it('string', () => { - assert.deepEqual( - transformArguments('key'), - ['SUNION', 'key'] - ); - }); + describe('transformArguments', () => { + it('string', () => { + assert.deepEqual( + SUNION.transformArguments('key'), + ['SUNION', 'key'] + ); + }); - it('array', () => { - assert.deepEqual( - transformArguments(['1', '2']), - ['SUNION', '1', '2'] - ); - }); + it('array', () => { + assert.deepEqual( + SUNION.transformArguments(['1', '2']), + ['SUNION', '1', '2'] + ); }); + }); - testUtils.testWithClient('client.sUnion', async client => { - assert.deepEqual( - await client.sUnion('key'), - [] - ); - }, GLOBAL.SERVERS.OPEN); + testUtils.testAll('sUnion', async client => { + assert.deepEqual( + await client.sUnion('key'), + [] + ); + }, { + client: GLOBAL.SERVERS.OPEN, + cluster: GLOBAL.CLUSTERS.OPEN + }); }); diff --git a/packages/client/lib/commands/SUNION.ts b/packages/client/lib/commands/SUNION.ts index 52c112e6610..42042217e2b 100644 --- a/packages/client/lib/commands/SUNION.ts +++ b/packages/client/lib/commands/SUNION.ts @@ -1,14 +1,11 @@ -import { RedisCommandArgument, RedisCommandArguments } from '.'; -import { pushVerdictArguments } from './generic-transformers'; - -export const FIRST_KEY_INDEX = 1; - -export const IS_READ_ONLY = true; - -export function transformArguments( - keys: RedisCommandArgument | Array -): RedisCommandArguments { - return pushVerdictArguments(['SUNION'], keys); -} - -export declare function transformReply(): Array; +import { ArrayReply, BlobStringReply, Command } from '../RESP/types'; +import { RedisVariadicArgument, pushVariadicArguments } from './generic-transformers'; + +export default { + FIRST_KEY_INDEX: 1, + IS_READ_ONLY: true, + transformArguments(keys: RedisVariadicArgument) { + return pushVariadicArguments(['SUNION'], keys); + }, + transformReply: undefined as unknown as () => ArrayReply +} as const satisfies Command; diff --git a/packages/client/lib/commands/SUNIONSTORE.spec.ts b/packages/client/lib/commands/SUNIONSTORE.spec.ts index 142533eea2b..790fd78060a 100644 --- a/packages/client/lib/commands/SUNIONSTORE.spec.ts +++ b/packages/client/lib/commands/SUNIONSTORE.spec.ts @@ -1,28 +1,31 @@ -import { strict as assert } from 'assert'; +import { strict as assert } from 'node:assert'; import testUtils, { GLOBAL } from '../test-utils'; -import { transformArguments } from './SUNIONSTORE'; +import SUNIONSTORE from './SUNIONSTORE'; describe('SUNIONSTORE', () => { - describe('transformArguments', () => { - it('string', () => { - assert.deepEqual( - transformArguments('destination', 'key'), - ['SUNIONSTORE', 'destination', 'key'] - ); - }); + describe('transformArguments', () => { + it('string', () => { + assert.deepEqual( + SUNIONSTORE.transformArguments('destination', 'key'), + ['SUNIONSTORE', 'destination', 'key'] + ); + }); - it('array', () => { - assert.deepEqual( - transformArguments('destination', ['1', '2']), - ['SUNIONSTORE', 'destination', '1', '2'] - ); - }); + it('array', () => { + assert.deepEqual( + SUNIONSTORE.transformArguments('destination', ['1', '2']), + ['SUNIONSTORE', 'destination', '1', '2'] + ); }); + }); - testUtils.testWithClient('client.sUnionStore', async client => { - assert.equal( - await client.sUnionStore('destination', 'key'), - 0 - ); - }, GLOBAL.SERVERS.OPEN); + testUtils.testAll('sUnionStore', async client => { + assert.equal( + await client.sUnionStore('{tag}destination', '{tag}key'), + 0 + ); + }, { + client: GLOBAL.SERVERS.OPEN, + cluster: GLOBAL.CLUSTERS.OPEN + }); }); diff --git a/packages/client/lib/commands/SUNIONSTORE.ts b/packages/client/lib/commands/SUNIONSTORE.ts index 94df6771a04..9adaa9288f3 100644 --- a/packages/client/lib/commands/SUNIONSTORE.ts +++ b/packages/client/lib/commands/SUNIONSTORE.ts @@ -1,13 +1,14 @@ -import { RedisCommandArgument, RedisCommandArguments } from '.'; -import { pushVerdictArguments } from './generic-transformers'; +import { RedisArgument, NumberReply, Command } from '../RESP/types'; +import { RedisVariadicArgument, pushVariadicArguments } from './generic-transformers'; -export const FIRST_KEY_INDEX = 1; - -export function transformArguments( - destination: RedisCommandArgument, - keys: RedisCommandArgument | Array -): RedisCommandArguments { - return pushVerdictArguments(['SUNIONSTORE', destination], keys); -} - -export declare function transformReply(): number; +export default { + FIRST_KEY_INDEX: 1, + IS_READ_ONLY: false, + transformArguments( + destination: RedisArgument, + keys: RedisVariadicArgument + ) { + return pushVariadicArguments(['SUNIONSTORE', destination], keys); + }, + transformReply: undefined as unknown as () => NumberReply +} as const satisfies Command; diff --git a/packages/client/lib/commands/SWAPDB.spec.ts b/packages/client/lib/commands/SWAPDB.spec.ts index add87512a64..9331854c13b 100644 --- a/packages/client/lib/commands/SWAPDB.spec.ts +++ b/packages/client/lib/commands/SWAPDB.spec.ts @@ -1,19 +1,19 @@ -import { strict as assert } from 'assert'; +import { strict as assert } from 'node:assert'; import testUtils, { GLOBAL } from '../test-utils'; -import { transformArguments } from './SWAPDB'; +import SWAPDB from './SWAPDB'; describe('SWAPDB', () => { - it('transformArguments', () => { - assert.deepEqual( - transformArguments(0, 1), - ['SWAPDB', '0', '1'] - ); - }); + it('transformArguments', () => { + assert.deepEqual( + SWAPDB.transformArguments(0, 1), + ['SWAPDB', '0', '1'] + ); + }); - testUtils.testWithClient('client.swapDb', async client => { - assert.equal( - await client.swapDb(0, 1), - 'OK' - ); - }, GLOBAL.SERVERS.OPEN); + testUtils.testWithClient('client.swapDb', async client => { + assert.equal( + await client.swapDb(0, 1), + 'OK' + ); + }, GLOBAL.SERVERS.OPEN); }); diff --git a/packages/client/lib/commands/SWAPDB.ts b/packages/client/lib/commands/SWAPDB.ts index 7f13d6b008e..f3b768e95f4 100644 --- a/packages/client/lib/commands/SWAPDB.ts +++ b/packages/client/lib/commands/SWAPDB.ts @@ -1,5 +1,11 @@ -export function transformArguments(index1: number, index2: number): Array { +import { SimpleStringReply, Command } from '../RESP/types'; + +export default { + FIRST_KEY_INDEX: undefined, + IS_READ_ONLY: false, + transformArguments(index1: number, index2: number) { return ['SWAPDB', index1.toString(), index2.toString()]; -} + }, + transformReply: undefined as unknown as () => SimpleStringReply<'OK'> +} as const satisfies Command; -export declare function transformReply(): string; diff --git a/packages/client/lib/commands/TIME.spec.ts b/packages/client/lib/commands/TIME.spec.ts index bbaa7942db0..d9dd9667ea4 100644 --- a/packages/client/lib/commands/TIME.spec.ts +++ b/packages/client/lib/commands/TIME.spec.ts @@ -1,18 +1,19 @@ -import { strict as assert } from 'assert'; +import { strict as assert } from 'node:assert'; import testUtils, { GLOBAL } from '../test-utils'; -import { transformArguments } from './TIME'; +import TIME from './TIME'; describe('TIME', () => { - it('transformArguments', () => { - assert.deepEqual( - transformArguments(), - ['TIME'] - ); - }); + it('transformArguments', () => { + assert.deepEqual( + TIME.transformArguments(), + ['TIME'] + ); + }); - testUtils.testWithClient('client.time', async client => { - const reply = await client.time(); - assert.ok(reply instanceof Date); - assert.ok(typeof reply.microseconds === 'number'); - }, GLOBAL.SERVERS.OPEN); + testUtils.testWithClient('client.time', async client => { + const reply = await client.time(); + assert.ok(Array.isArray(reply)); + assert.equal(typeof reply[0], 'string'); + assert.equal(typeof reply[1], 'string'); + }, GLOBAL.SERVERS.OPEN); }); diff --git a/packages/client/lib/commands/TIME.ts b/packages/client/lib/commands/TIME.ts index 1a364d6d8be..d4dc67ae483 100644 --- a/packages/client/lib/commands/TIME.ts +++ b/packages/client/lib/commands/TIME.ts @@ -1,15 +1,13 @@ -export function transformArguments(): Array { - return ['TIME']; -} - -interface TimeReply extends Date { - microseconds: number; -} +import { BlobStringReply, Command } from '../RESP/types'; -export function transformReply(reply: [string, string]): TimeReply { - const seconds = Number(reply[0]), - microseconds = Number(reply[1]), - d: Partial = new Date(seconds * 1000 + microseconds / 1000); - d.microseconds = microseconds; - return d as TimeReply; -} +export default { + FIRST_KEY_INDEX: undefined, + IS_READ_ONLY: true, + transformArguments() { + return ['TIME']; + }, + transformReply: undefined as unknown as () => [ + unixTimestamp: BlobStringReply<`${number}`>, + microseconds: BlobStringReply<`${number}`> + ] +} as const satisfies Command; diff --git a/packages/client/lib/commands/TOUCH.spec.ts b/packages/client/lib/commands/TOUCH.spec.ts index 578c49587d7..48e77900ee3 100644 --- a/packages/client/lib/commands/TOUCH.spec.ts +++ b/packages/client/lib/commands/TOUCH.spec.ts @@ -1,28 +1,31 @@ -import { strict as assert } from 'assert'; +import { strict as assert } from 'node:assert'; import testUtils, { GLOBAL } from '../test-utils'; -import { transformArguments } from './TOUCH'; +import TOUCH from './TOUCH'; describe('TOUCH', () => { - describe('transformArguments', () => { - it('string', () => { - assert.deepEqual( - transformArguments('key'), - ['TOUCH', 'key'] - ); - }); + describe('transformArguments', () => { + it('string', () => { + assert.deepEqual( + TOUCH.transformArguments('key'), + ['TOUCH', 'key'] + ); + }); - it('array', () => { - assert.deepEqual( - transformArguments(['1', '2']), - ['TOUCH', '1', '2'] - ); - }); + it('array', () => { + assert.deepEqual( + TOUCH.transformArguments(['1', '2']), + ['TOUCH', '1', '2'] + ); }); + }); - testUtils.testWithClient('client.touch', async client => { - assert.equal( - await client.touch('key'), - 0 - ); - }, GLOBAL.SERVERS.OPEN); + testUtils.testAll('touch', async client => { + assert.equal( + await client.touch('key'), + 0 + ); + }, { + client: GLOBAL.SERVERS.OPEN, + cluster: GLOBAL.CLUSTERS.OPEN + }); }); diff --git a/packages/client/lib/commands/TOUCH.ts b/packages/client/lib/commands/TOUCH.ts index e67dff8e932..c1c19402f8b 100644 --- a/packages/client/lib/commands/TOUCH.ts +++ b/packages/client/lib/commands/TOUCH.ts @@ -1,12 +1,11 @@ -import { RedisCommandArgument, RedisCommandArguments } from '.'; -import { pushVerdictArguments } from './generic-transformers'; +import { NumberReply, Command } from '../RESP/types'; +import { RedisVariadicArgument, pushVariadicArguments } from './generic-transformers'; -export const FIRST_KEY_INDEX = 1; - -export function transformArguments( - key: RedisCommandArgument | Array -): RedisCommandArguments { - return pushVerdictArguments(['TOUCH'], key); -} - -export declare function transformReply(): number; +export default { + FIRST_KEY_INDEX: 1, + IS_READ_ONLY: false, + transformArguments(key: RedisVariadicArgument) { + return pushVariadicArguments(['TOUCH'], key); + }, + transformReply: undefined as unknown as () => NumberReply +} as const satisfies Command; diff --git a/packages/client/lib/commands/TTL.spec.ts b/packages/client/lib/commands/TTL.spec.ts index e37a6ab714b..6b709226a2b 100644 --- a/packages/client/lib/commands/TTL.spec.ts +++ b/packages/client/lib/commands/TTL.spec.ts @@ -1,19 +1,22 @@ -import { strict as assert } from 'assert'; +import { strict as assert } from 'node:assert'; import testUtils, { GLOBAL } from '../test-utils'; -import { transformArguments } from './TTL'; +import TTL from './TTL'; describe('TTL', () => { - it('transformArguments', () => { - assert.deepEqual( - transformArguments('key'), - ['TTL', 'key'] - ); - }); + it('transformArguments', () => { + assert.deepEqual( + TTL.transformArguments('key'), + ['TTL', 'key'] + ); + }); - testUtils.testWithClient('client.ttl', async client => { - assert.equal( - await client.ttl('key'), - -2 - ); - }, GLOBAL.SERVERS.OPEN); + testUtils.testAll('ttl', async client => { + assert.equal( + await client.ttl('key'), + -2 + ); + }, { + client: GLOBAL.SERVERS.OPEN, + cluster: GLOBAL.CLUSTERS.OPEN + }); }); diff --git a/packages/client/lib/commands/TTL.ts b/packages/client/lib/commands/TTL.ts index 29586f31fa8..65c3b7b026f 100644 --- a/packages/client/lib/commands/TTL.ts +++ b/packages/client/lib/commands/TTL.ts @@ -1,11 +1,10 @@ -import { RedisCommandArgument, RedisCommandArguments } from '.'; +import { RedisArgument, NumberReply, Command } from '../RESP/types'; -export const FIRST_KEY_INDEX = 1; - -export const IS_READ_ONLY = true; - -export function transformArguments(key: RedisCommandArgument): RedisCommandArguments { +export default { + FIRST_KEY_INDEX: 1, + IS_READ_ONLY: true, + transformArguments(key: RedisArgument) { return ['TTL', key]; -} - -export declare function transformReply(): number; + }, + transformReply: undefined as unknown as () => NumberReply +} as const satisfies Command; diff --git a/packages/client/lib/commands/TYPE.spec.ts b/packages/client/lib/commands/TYPE.spec.ts index 1040bf979b3..45cf1cfc1c9 100644 --- a/packages/client/lib/commands/TYPE.spec.ts +++ b/packages/client/lib/commands/TYPE.spec.ts @@ -1,19 +1,22 @@ -import { strict as assert } from 'assert'; +import { strict as assert } from 'node:assert'; import testUtils, { GLOBAL } from '../test-utils'; -import { transformArguments } from './TYPE'; +import TYPE from './TYPE'; describe('TYPE', () => { - it('transformArguments', () => { - assert.deepEqual( - transformArguments('key'), - ['TYPE', 'key'] - ); - }); + it('transformArguments', () => { + assert.deepEqual( + TYPE.transformArguments('key'), + ['TYPE', 'key'] + ); + }); - testUtils.testWithClient('client.type', async client => { - assert.equal( - await client.type('key'), - 'none' - ); - }, GLOBAL.SERVERS.OPEN); + testUtils.testAll('type', async client => { + assert.equal( + await client.type('key'), + 'none' + ); + }, { + client: GLOBAL.SERVERS.OPEN, + cluster: GLOBAL.CLUSTERS.OPEN + }); }); diff --git a/packages/client/lib/commands/TYPE.ts b/packages/client/lib/commands/TYPE.ts index 10cd3f99b0e..09f6887492c 100644 --- a/packages/client/lib/commands/TYPE.ts +++ b/packages/client/lib/commands/TYPE.ts @@ -1,11 +1,10 @@ -import { RedisCommandArgument, RedisCommandArguments } from '.'; +import { RedisArgument, SimpleStringReply, Command } from '../RESP/types'; -export const FIRST_KEY_INDEX = 1; - -export const IS_READ_ONLY = true; - -export function transformArguments(key: RedisCommandArgument): RedisCommandArguments { +export default { + FIRST_KEY_INDEX: 1, + IS_READ_ONLY: true, + transformArguments(key: RedisArgument) { return ['TYPE', key]; -} - -export declare function transformReply(): RedisCommandArgument; + }, + transformReply: undefined as unknown as () => SimpleStringReply +} as const satisfies Command; diff --git a/packages/client/lib/commands/UNLINK.spec.ts b/packages/client/lib/commands/UNLINK.spec.ts index e8355407d8f..1e374783007 100644 --- a/packages/client/lib/commands/UNLINK.spec.ts +++ b/packages/client/lib/commands/UNLINK.spec.ts @@ -1,28 +1,31 @@ -import { strict as assert } from 'assert'; +import { strict as assert } from 'node:assert'; import testUtils, { GLOBAL } from '../test-utils'; -import { transformArguments } from './UNLINK'; +import UNLINK from './UNLINK'; describe('UNLINK', () => { - describe('transformArguments', () => { - it('string', () => { - assert.deepEqual( - transformArguments('key'), - ['UNLINK', 'key'] - ); - }); + describe('transformArguments', () => { + it('string', () => { + assert.deepEqual( + UNLINK.transformArguments('key'), + ['UNLINK', 'key'] + ); + }); - it('array', () => { - assert.deepEqual( - transformArguments(['1', '2']), - ['UNLINK', '1', '2'] - ); - }); + it('array', () => { + assert.deepEqual( + UNLINK.transformArguments(['1', '2']), + ['UNLINK', '1', '2'] + ); }); + }); - testUtils.testWithClient('client.unlink', async client => { - assert.equal( - await client.unlink('key'), - 0 - ); - }, GLOBAL.SERVERS.OPEN); + testUtils.testAll('unlink', async client => { + assert.equal( + await client.unlink('key'), + 0 + ); + }, { + client: GLOBAL.SERVERS.OPEN, + cluster: GLOBAL.CLUSTERS.OPEN + }); }); diff --git a/packages/client/lib/commands/UNLINK.ts b/packages/client/lib/commands/UNLINK.ts index 53b0360e2df..2346573f397 100644 --- a/packages/client/lib/commands/UNLINK.ts +++ b/packages/client/lib/commands/UNLINK.ts @@ -1,12 +1,11 @@ -import { RedisCommandArgument, RedisCommandArguments } from '.'; -import { pushVerdictArguments } from './generic-transformers'; +import { NumberReply, Command } from '../RESP/types'; +import { RedisVariadicArgument, pushVariadicArguments } from './generic-transformers'; -export const FIRST_KEY_INDEX = 1; - -export function transformArguments( - key: RedisCommandArgument | Array -): RedisCommandArguments { - return pushVerdictArguments(['UNLINK'], key); -} - -export declare function transformReply(): number; +export default { + FIRST_KEY_INDEX: 1, + IS_READ_ONLY: false, + transformArguments(key: RedisVariadicArgument) { + return pushVariadicArguments(['UNLINK'], key); + }, + transformReply: undefined as unknown as () => NumberReply +} as const satisfies Command; diff --git a/packages/client/lib/commands/UNWATCH.spec.ts b/packages/client/lib/commands/UNWATCH.spec.ts deleted file mode 100644 index 109ed0fa7c0..00000000000 --- a/packages/client/lib/commands/UNWATCH.spec.ts +++ /dev/null @@ -1,19 +0,0 @@ -import { strict as assert } from 'assert'; -import testUtils, { GLOBAL } from '../test-utils'; -import { transformArguments } from './UNWATCH'; - -describe('UNWATCH', () => { - it('transformArguments', () => { - assert.deepEqual( - transformArguments(), - ['UNWATCH'] - ); - }); - - testUtils.testWithClient('client.unwatch', async client => { - assert.equal( - await client.unwatch(), - 'OK' - ); - }, GLOBAL.SERVERS.OPEN); -}); diff --git a/packages/client/lib/commands/UNWATCH.ts b/packages/client/lib/commands/UNWATCH.ts deleted file mode 100644 index ce42e7697bf..00000000000 --- a/packages/client/lib/commands/UNWATCH.ts +++ /dev/null @@ -1,5 +0,0 @@ -export function transformArguments(): Array { - return ['UNWATCH']; -} - -export declare function transformReply(): string; diff --git a/packages/client/lib/commands/WAIT.spec.ts b/packages/client/lib/commands/WAIT.spec.ts index c85ef598612..61b197c90ba 100644 --- a/packages/client/lib/commands/WAIT.spec.ts +++ b/packages/client/lib/commands/WAIT.spec.ts @@ -1,19 +1,19 @@ -import { strict as assert } from 'assert'; +import { strict as assert } from 'node:assert'; import testUtils, { GLOBAL } from '../test-utils'; -import { transformArguments } from './WAIT'; +import WAIT from './WAIT'; describe('WAIT', () => { - it('transformArguments', () => { - assert.deepEqual( - transformArguments(0, 1), - ['WAIT', '0', '1'] - ); - }); + it('transformArguments', () => { + assert.deepEqual( + WAIT.transformArguments(0, 1), + ['WAIT', '0', '1'] + ); + }); - testUtils.testWithClient('client.wait', async client => { - assert.equal( - await client.wait(0, 1), - 0 - ); - }, GLOBAL.SERVERS.OPEN); + testUtils.testWithClient('client.wait', async client => { + assert.equal( + await client.wait(0, 1), + 0 + ); + }, GLOBAL.SERVERS.OPEN); }); diff --git a/packages/client/lib/commands/WAIT.ts b/packages/client/lib/commands/WAIT.ts index dff51ed9680..21c39a643e5 100644 --- a/packages/client/lib/commands/WAIT.ts +++ b/packages/client/lib/commands/WAIT.ts @@ -1,7 +1,10 @@ -export const FIRST_KEY_INDEX = 1; +import { NumberReply, Command } from '../RESP/types'; -export function transformArguments(numberOfReplicas: number, timeout: number): Array { +export default { + FIRST_KEY_INDEX: undefined, + IS_READ_ONLY: true, + transformArguments(numberOfReplicas: number, timeout: number) { return ['WAIT', numberOfReplicas.toString(), timeout.toString()]; -} - -export declare function transformReply(): number; + }, + transformReply: undefined as unknown as () => NumberReply +} as const satisfies Command; diff --git a/packages/client/lib/commands/WATCH.spec.ts b/packages/client/lib/commands/WATCH.spec.ts deleted file mode 100644 index acaa062874f..00000000000 --- a/packages/client/lib/commands/WATCH.spec.ts +++ /dev/null @@ -1,20 +0,0 @@ -import { strict as assert } from 'assert'; -import { transformArguments } from './WATCH'; - -describe('WATCH', () => { - describe('transformArguments', () => { - it('string', () => { - assert.deepEqual( - transformArguments('key'), - ['WATCH', 'key'] - ); - }); - - it('array', () => { - assert.deepEqual( - transformArguments(['1', '2']), - ['WATCH', '1', '2'] - ); - }); - }); -}); diff --git a/packages/client/lib/commands/WATCH.ts b/packages/client/lib/commands/WATCH.ts deleted file mode 100644 index 58c6dfd1dad..00000000000 --- a/packages/client/lib/commands/WATCH.ts +++ /dev/null @@ -1,10 +0,0 @@ -import { RedisCommandArguments } from '.'; -import { pushVerdictArguments } from './generic-transformers'; - -export const FIRST_KEY_INDEX = 1; - -export function transformArguments(key: string | Array): RedisCommandArguments { - return pushVerdictArguments(['WATCH'], key); -} - -export declare function transformReply(): string; diff --git a/packages/client/lib/commands/XACK.spec.ts b/packages/client/lib/commands/XACK.spec.ts index 0586a5921fd..81a7954ffd6 100644 --- a/packages/client/lib/commands/XACK.spec.ts +++ b/packages/client/lib/commands/XACK.spec.ts @@ -1,28 +1,31 @@ -import { strict as assert } from 'assert'; +import { strict as assert } from 'node:assert'; import testUtils, { GLOBAL } from '../test-utils'; -import { transformArguments } from './XACK'; +import XACK from './XACK'; describe('XACK', () => { - describe('transformArguments', () => { - it('string', () => { - assert.deepEqual( - transformArguments('key', 'group', '1-0'), - ['XACK', 'key', 'group', '1-0'] - ); - }); + describe('transformArguments', () => { + it('string', () => { + assert.deepEqual( + XACK.transformArguments('key', 'group', '0-0'), + ['XACK', 'key', 'group', '0-0'] + ); + }); - it('array', () => { - assert.deepEqual( - transformArguments('key', 'group', ['1-0', '2-0']), - ['XACK', 'key', 'group', '1-0', '2-0'] - ); - }); + it('array', () => { + assert.deepEqual( + XACK.transformArguments('key', 'group', ['0-0', '1-0']), + ['XACK', 'key', 'group', '0-0', '1-0'] + ); }); + }); - testUtils.testWithClient('client.xAck', async client => { - assert.equal( - await client.xAck('key', 'group', '1-0'), - 0 - ); - }, GLOBAL.SERVERS.OPEN); + testUtils.testAll('xAck', async client => { + assert.equal( + await client.xAck('key', 'group', '0-0'), + 0 + ); + }, { + client: GLOBAL.SERVERS.OPEN, + cluster: GLOBAL.CLUSTERS.OPEN + }); }); diff --git a/packages/client/lib/commands/XACK.ts b/packages/client/lib/commands/XACK.ts index 670d810fc00..89b2d581201 100644 --- a/packages/client/lib/commands/XACK.ts +++ b/packages/client/lib/commands/XACK.ts @@ -1,14 +1,15 @@ -import { RedisCommandArgument, RedisCommandArguments } from '.'; -import { pushVerdictArguments } from './generic-transformers'; +import { NumberReply, Command, RedisArgument } from '../RESP/types'; +import { RedisVariadicArgument, pushVariadicArguments } from './generic-transformers'; -export const FIRST_KEY_INDEX = 1; - -export function transformArguments( - key: RedisCommandArgument, - group: RedisCommandArgument, - id: RedisCommandArgument | Array -): RedisCommandArguments { - return pushVerdictArguments(['XACK', key, group], id); -} - -export declare function transformReply(): number; +export default { + FIRST_KEY_INDEX: 1, + IS_READ_ONLY: false, + transformArguments( + key: RedisArgument, + group: RedisArgument, + id: RedisVariadicArgument + ) { + return pushVariadicArguments(['XACK', key, group], id); + }, + transformReply: undefined as unknown as () => NumberReply +} as const satisfies Command; diff --git a/packages/client/lib/commands/XADD.spec.ts b/packages/client/lib/commands/XADD.spec.ts index 4b556ecc27c..10c6f4daa56 100644 --- a/packages/client/lib/commands/XADD.spec.ts +++ b/packages/client/lib/commands/XADD.spec.ts @@ -1,118 +1,93 @@ -import { strict as assert } from 'assert'; +import { strict as assert } from 'node:assert'; import testUtils, { GLOBAL } from '../test-utils'; -import { transformArguments } from './XADD'; +import XADD from './XADD'; describe('XADD', () => { - describe('transformArguments', () => { - it('single field', () => { - assert.deepEqual( - transformArguments('key', '*', { - field: 'value' - }), - ['XADD', 'key', '*', 'field', 'value'] - ); - }); - - it('multiple fields', () => { - assert.deepEqual( - transformArguments('key', '*', { - '1': 'I', - '2': 'II' - }), - ['XADD', 'key', '*', '1', 'I', '2', 'II'] - ); - }); - - it('with NOMKSTREAM', () => { - assert.deepEqual( - transformArguments('key', '*', { - field: 'value' - }, { - NOMKSTREAM: true - }), - ['XADD', 'key', 'NOMKSTREAM', '*', 'field', 'value'] - ); - }); + describe('transformArguments', () => { + it('single field', () => { + assert.deepEqual( + XADD.transformArguments('key', '*', { + field: 'value' + }), + ['XADD', 'key', '*', 'field', 'value'] + ); + }); - it('with TRIM', () => { - assert.deepEqual( - transformArguments('key', '*', { - field: 'value' - }, { - TRIM: { - threshold: 1000 - } - }), - ['XADD', 'key', '1000', '*', 'field', 'value'] - ); - }); + it('multiple fields', () => { + assert.deepEqual( + XADD.transformArguments('key', '*', { + '1': 'I', + '2': 'II' + }), + ['XADD', 'key', '*', '1', 'I', '2', 'II'] + ); + }); - it('with TRIM.strategy', () => { - assert.deepEqual( - transformArguments('key', '*', { - field: 'value' - }, { - TRIM: { - strategy: 'MAXLEN', - threshold: 1000 - } - }), - ['XADD', 'key', 'MAXLEN', '1000', '*','field', 'value'] - ); - }); + it('with TRIM', () => { + assert.deepEqual( + XADD.transformArguments('key', '*', { + field: 'value' + }, { + TRIM: { + threshold: 1000 + } + }), + ['XADD', 'key', '1000', '*', 'field', 'value'] + ); + }); - it('with TRIM.strategyModifier', () => { - assert.deepEqual( - transformArguments('key', '*', { - field: 'value' - }, { - TRIM: { - strategyModifier: '=', - threshold: 1000 - } - }), - ['XADD', 'key', '=', '1000', '*', 'field', 'value'] - ); - }); + it('with TRIM.strategy', () => { + assert.deepEqual( + XADD.transformArguments('key', '*', { + field: 'value' + }, { + TRIM: { + strategy: 'MAXLEN', + threshold: 1000 + } + }), + ['XADD', 'key', 'MAXLEN', '1000', '*', 'field', 'value'] + ); + }); - it('with TRIM.limit', () => { - assert.deepEqual( - transformArguments('key', '*', { - field: 'value' - }, { - TRIM: { - threshold: 1000, - limit: 1 - } - }), - ['XADD', 'key', '1000', 'LIMIT', '1', '*', 'field', 'value'] - ); - }); + it('with TRIM.strategyModifier', () => { + assert.deepEqual( + XADD.transformArguments('key', '*', { + field: 'value' + }, { + TRIM: { + strategyModifier: '=', + threshold: 1000 + } + }), + ['XADD', 'key', '=', '1000', '*', 'field', 'value'] + ); + }); - it('with NOMKSTREAM, TRIM, TRIM.*', () => { - assert.deepEqual( - transformArguments('key', '*', { - field: 'value' - }, { - NOMKSTREAM: true, - TRIM: { - strategy: 'MAXLEN', - strategyModifier: '=', - threshold: 1000, - limit: 1 - } - }), - ['XADD', 'key', 'NOMKSTREAM', 'MAXLEN', '=', '1000', 'LIMIT', '1', '*', 'field', 'value'] - ); - }); + it('with TRIM.limit', () => { + assert.deepEqual( + XADD.transformArguments('key', '*', { + field: 'value' + }, { + TRIM: { + threshold: 1000, + limit: 1 + } + }), + ['XADD', 'key', '1000', 'LIMIT', '1', '*', 'field', 'value'] + ); }); + }); - testUtils.testWithClient('client.xAdd', async client => { - assert.equal( - typeof await client.xAdd('key', '*', { - field: 'value' - }), - 'string' - ); - }, GLOBAL.SERVERS.OPEN); + testUtils.testAll('xAdd', async client => { + assert.equal( + typeof await client.xAdd('key', '*', { + field: 'value' + }), + 'string' + ); + }, { + client: GLOBAL.SERVERS.OPEN, + cluster: GLOBAL.CLUSTERS.OPEN + }); }); diff --git a/packages/client/lib/commands/XADD.ts b/packages/client/lib/commands/XADD.ts index e7a1b6804ff..b681069c729 100644 --- a/packages/client/lib/commands/XADD.ts +++ b/packages/client/lib/commands/XADD.ts @@ -1,52 +1,55 @@ -import { RedisCommandArgument, RedisCommandArguments } from '.'; - -export const FIRST_KEY_INDEX = 1; - -interface XAddOptions { - NOMKSTREAM?: true; - TRIM?: { - strategy?: 'MAXLEN' | 'MINID'; - strategyModifier?: '=' | '~'; - threshold: number; - limit?: number; - }; +import { RedisArgument, BlobStringReply, Command, CommandArguments } from '../RESP/types'; + +export interface XAddOptions { + TRIM?: { + strategy?: 'MAXLEN' | 'MINID'; + strategyModifier?: '=' | '~'; + threshold: number; + limit?: number; + }; } -export function transformArguments( - key: RedisCommandArgument, - id: RedisCommandArgument, - message: Record, - options?: XAddOptions -): RedisCommandArguments { - const args = ['XADD', key]; - - if (options?.NOMKSTREAM) { - args.push('NOMKSTREAM'); +export function pushXAddArguments( + args: CommandArguments, + id: RedisArgument, + message: Record, + options?: XAddOptions +) { + if (options?.TRIM) { + if (options.TRIM.strategy) { + args.push(options.TRIM.strategy); } - if (options?.TRIM) { - if (options.TRIM.strategy) { - args.push(options.TRIM.strategy); - } - - if (options.TRIM.strategyModifier) { - args.push(options.TRIM.strategyModifier); - } + if (options.TRIM.strategyModifier) { + args.push(options.TRIM.strategyModifier); + } - args.push(options.TRIM.threshold.toString()); + args.push(options.TRIM.threshold.toString()); - if (options.TRIM.limit) { - args.push('LIMIT', options.TRIM.limit.toString()); - } + if (options.TRIM.limit) { + args.push('LIMIT', options.TRIM.limit.toString()); } + } - args.push(id); + args.push(id); - for (const [key, value] of Object.entries(message)) { - args.push(key, value); - } + for (const [key, value] of Object.entries(message)) { + args.push(key, value); + } - return args; + return args; } -export declare function transformReply(): string; +export default { + FIRST_KEY_INDEX: 1, + IS_READ_ONLY: false, + transformArguments( + key: RedisArgument, + id: RedisArgument, + message: Record, + options?: XAddOptions + ) { + return pushXAddArguments(['XADD', key], id, message, options); + }, + transformReply: undefined as unknown as () => BlobStringReply +} as const satisfies Command; diff --git a/packages/client/lib/commands/XADD_NOMKSTREAM.spec.ts b/packages/client/lib/commands/XADD_NOMKSTREAM.spec.ts new file mode 100644 index 00000000000..a3dd5602a3b --- /dev/null +++ b/packages/client/lib/commands/XADD_NOMKSTREAM.spec.ts @@ -0,0 +1,95 @@ +import { strict as assert } from 'node:assert'; +import testUtils, { GLOBAL } from '../test-utils'; +import XADD_NOMKSTREAM from './XADD_NOMKSTREAM'; + +describe('XADD NOMKSTREAM', () => { + testUtils.isVersionGreaterThanHook([6, 2]); + + describe('transformArguments', () => { + it('single field', () => { + assert.deepEqual( + XADD_NOMKSTREAM.transformArguments('key', '*', { + field: 'value' + }), + ['XADD', 'key', 'NOMKSTREAM', '*', 'field', 'value'] + ); + }); + + it('multiple fields', () => { + assert.deepEqual( + XADD_NOMKSTREAM.transformArguments('key', '*', { + '1': 'I', + '2': 'II' + }), + ['XADD', 'key', 'NOMKSTREAM', '*', '1', 'I', '2', 'II'] + ); + }); + + it('with TRIM', () => { + assert.deepEqual( + XADD_NOMKSTREAM.transformArguments('key', '*', { + field: 'value' + }, { + TRIM: { + threshold: 1000 + } + }), + ['XADD', 'key', 'NOMKSTREAM', '1000', '*', 'field', 'value'] + ); + }); + + it('with TRIM.strategy', () => { + assert.deepEqual( + XADD_NOMKSTREAM.transformArguments('key', '*', { + field: 'value' + }, { + TRIM: { + strategy: 'MAXLEN', + threshold: 1000 + } + }), + ['XADD', 'key', 'NOMKSTREAM', 'MAXLEN', '1000', '*', 'field', 'value'] + ); + }); + + it('with TRIM.strategyModifier', () => { + assert.deepEqual( + XADD_NOMKSTREAM.transformArguments('key', '*', { + field: 'value' + }, { + TRIM: { + strategyModifier: '=', + threshold: 1000 + } + }), + ['XADD', 'key', 'NOMKSTREAM', '=', '1000', '*', 'field', 'value'] + ); + }); + + it('with TRIM.limit', () => { + assert.deepEqual( + XADD_NOMKSTREAM.transformArguments('key', '*', { + field: 'value' + }, { + TRIM: { + threshold: 1000, + limit: 1 + } + }), + ['XADD', 'key', 'NOMKSTREAM', '1000', 'LIMIT', '1', '*', 'field', 'value'] + ); + }); + }); + + testUtils.testAll('xAddNoMkStream', async client => { + assert.equal( + await client.xAddNoMkStream('key', '*', { + field: 'value' + }), + null + ); + }, { + client: GLOBAL.SERVERS.OPEN, + cluster: GLOBAL.CLUSTERS.OPEN + }); +}); diff --git a/packages/client/lib/commands/XADD_NOMKSTREAM.ts b/packages/client/lib/commands/XADD_NOMKSTREAM.ts new file mode 100644 index 00000000000..65e7dd566e3 --- /dev/null +++ b/packages/client/lib/commands/XADD_NOMKSTREAM.ts @@ -0,0 +1,16 @@ +import { RedisArgument, BlobStringReply, NullReply, Command } from '../RESP/types'; +import { XAddOptions, pushXAddArguments } from './XADD'; + +export default { + FIRST_KEY_INDEX: 1, + IS_READ_ONLY: false, + transformArguments( + key: RedisArgument, + id: RedisArgument, + message: Record, + options?: XAddOptions + ) { + return pushXAddArguments(['XADD', key, 'NOMKSTREAM'], id, message, options); + }, + transformReply: undefined as unknown as () => BlobStringReply | NullReply +} as const satisfies Command; diff --git a/packages/client/lib/commands/XAUTOCLAIM.spec.ts b/packages/client/lib/commands/XAUTOCLAIM.spec.ts index bae914bda05..256c58cc4d6 100644 --- a/packages/client/lib/commands/XAUTOCLAIM.spec.ts +++ b/packages/client/lib/commands/XAUTOCLAIM.spec.ts @@ -1,98 +1,68 @@ -import { strict as assert } from 'assert'; +import { strict as assert } from 'node:assert'; import testUtils, { GLOBAL } from '../test-utils'; -import { transformArguments } from './XAUTOCLAIM'; +import XAUTOCLAIM from './XAUTOCLAIM'; describe('XAUTOCLAIM', () => { - testUtils.isVersionGreaterThanHook([6, 2]); + testUtils.isVersionGreaterThanHook([6, 2]); - describe('transformArguments', () => { - it('simple', () => { - assert.deepEqual( - transformArguments('key', 'group', 'consumer', 1, '0-0'), - ['XAUTOCLAIM', 'key', 'group', 'consumer', '1', '0-0'] - ); - }); - - it('with COUNT', () => { - assert.deepEqual( - transformArguments('key', 'group', 'consumer', 1, '0-0', { - COUNT: 1 - }), - ['XAUTOCLAIM', 'key', 'group', 'consumer', '1', '0-0', 'COUNT', '1'] - ); - }); + describe('transformArguments', () => { + it('simple', () => { + assert.deepEqual( + XAUTOCLAIM.transformArguments('key', 'group', 'consumer', 1, '0-0'), + ['XAUTOCLAIM', 'key', 'group', 'consumer', '1', '0-0'] + ); }); - testUtils.testWithClient('client.xAutoClaim without messages', async client => { - const [,, reply] = await Promise.all([ - client.xGroupCreate('key', 'group', '$', { MKSTREAM: true }), - client.xGroupCreateConsumer('key', 'group', 'consumer'), - client.xAutoClaim('key', 'group', 'consumer', 1, '0-0') - ]); - - assert.deepEqual(reply, { - nextId: '0-0', - messages: [] - }); - }, GLOBAL.SERVERS.OPEN); - - testUtils.testWithClient('client.xAutoClaim with messages', async client => { - const [,, id,, reply] = await Promise.all([ - client.xGroupCreate('key', 'group', '$', { MKSTREAM: true }), - client.xGroupCreateConsumer('key', 'group', 'consumer'), - client.xAdd('key', '*', { foo: 'bar' }), - client.xReadGroup('group', 'consumer', { key: 'key', id: '>' }), - client.xAutoClaim('key', 'group', 'consumer', 0, '0-0') - ]); + it('with COUNT', () => { + assert.deepEqual( + XAUTOCLAIM.transformArguments('key', 'group', 'consumer', 1, '0-0', { + COUNT: 1 + }), + ['XAUTOCLAIM', 'key', 'group', 'consumer', '1', '0-0', 'COUNT', '1'] + ); + }); + }); - assert.deepEqual(reply, { - nextId: '0-0', - messages: [{ - id, - message: Object.create(null, { - foo: { - value: 'bar', - configurable: true, - enumerable: true - } - }) - }] - }); - }, GLOBAL.SERVERS.OPEN); + testUtils.testAll('xAutoClaim', async client => { + const message = Object.create(null, { + field: { + value: 'value', + enumerable: true + } + }); - testUtils.testWithClient('client.xAutoClaim with trimmed messages', async client => { - const [,,,,, id,, reply] = await Promise.all([ - client.xGroupCreate('key', 'group', '$', { MKSTREAM: true }), - client.xGroupCreateConsumer('key', 'group', 'consumer'), - client.xAdd('key', '*', { foo: 'bar' }), - client.xReadGroup('group', 'consumer', { key: 'key', id: '>' }), - client.xTrim('key', 'MAXLEN', 0), - client.xAdd('key', '*', { bar: 'baz' }), - client.xReadGroup('group', 'consumer', { key: 'key', id: '>' }), - client.xAutoClaim('key', 'group', 'consumer', 0, '0-0') - ]); + const [, id1, id2, , , reply] = await Promise.all([ + client.xGroupCreate('key', 'group', '$', { + MKSTREAM: true + }), + client.xAdd('key', '*', message), + client.xAdd('key', '*', message), + client.xReadGroup('group', 'consumer', { + key: 'key', + id: '>' + }), + client.xTrim('key', 'MAXLEN', 1), + client.xAutoClaim('key', 'group', 'consumer', 0, '0-0') + ]); - assert.deepEqual(reply, { - nextId: '0-0', - messages: testUtils.isVersionGreaterThan([7, 0]) ? [{ - id, - message: Object.create(null, { - bar: { - value: 'baz', - configurable: true, - enumerable: true - } - }) - }] : [null, { - id, - message: Object.create(null, { - bar: { - value: 'baz', - configurable: true, - enumerable: true - } - }) - }] - }); - }, GLOBAL.SERVERS.OPEN); + assert.deepEqual(reply, { + nextId: '0-0', + ...(testUtils.isVersionGreaterThan([7, 0]) ? { + messages: [{ + id: id2, + message + }], + deletedMessages: [id1] + } : { + messages: [null, { + id: id2, + message + }], + deletedMessages: undefined + }) + }); + }, { + client: GLOBAL.SERVERS.OPEN, + cluster: GLOBAL.CLUSTERS.OPEN + }); }); diff --git a/packages/client/lib/commands/XAUTOCLAIM.ts b/packages/client/lib/commands/XAUTOCLAIM.ts index 831563981a6..7d33142de32 100644 --- a/packages/client/lib/commands/XAUTOCLAIM.ts +++ b/packages/client/lib/commands/XAUTOCLAIM.ts @@ -1,39 +1,47 @@ -import { RedisCommandArgument, RedisCommandArguments } from '.'; -import { StreamMessagesNullReply, transformStreamMessagesNullReply } from './generic-transformers'; - -export const FIRST_KEY_INDEX = 1; +import { RedisArgument, TuplesReply, BlobStringReply, ArrayReply, NullReply, UnwrapReply, Command, TypeMapping } from '../RESP/types'; +import { StreamMessageRawReply, transformStreamMessageNullReply } from './generic-transformers'; export interface XAutoClaimOptions { - COUNT?: number; + COUNT?: number; } -export function transformArguments( - key: RedisCommandArgument, - group: RedisCommandArgument, - consumer: RedisCommandArgument, +export type XAutoClaimRawReply = TuplesReply<[ + nextId: BlobStringReply, + messages: ArrayReply, + deletedMessages: ArrayReply +]>; + +export default { + FIRST_KEY_INDEX: 1, + IS_READ_ONLY: false, + transformArguments( + key: RedisArgument, + group: RedisArgument, + consumer: RedisArgument, minIdleTime: number, - start: string, + start: RedisArgument, options?: XAutoClaimOptions -): RedisCommandArguments { - const args = ['XAUTOCLAIM', key, group, consumer, minIdleTime.toString(), start]; + ) { + const args = [ + 'XAUTOCLAIM', + key, + group, + consumer, + minIdleTime.toString(), + start + ]; if (options?.COUNT) { - args.push('COUNT', options.COUNT.toString()); + args.push('COUNT', options.COUNT.toString()); } return args; -} - -type XAutoClaimRawReply = [RedisCommandArgument, Array]; - -interface XAutoClaimReply { - nextId: RedisCommandArgument; - messages: StreamMessagesNullReply; -} - -export function transformReply(reply: XAutoClaimRawReply): XAutoClaimReply { + }, + transformReply(reply: UnwrapReply, preserve?: any, typeMapping?: TypeMapping) { return { - nextId: reply[0], - messages: transformStreamMessagesNullReply(reply[1]) + nextId: reply[0], + messages: (reply[1] as unknown as UnwrapReply).map(transformStreamMessageNullReply.bind(undefined, typeMapping)), + deletedMessages: reply[2] }; -} + } +} as const satisfies Command; diff --git a/packages/client/lib/commands/XAUTOCLAIM_JUSTID.spec.ts b/packages/client/lib/commands/XAUTOCLAIM_JUSTID.spec.ts index 9aa24cd04a4..96ceb1d8116 100644 --- a/packages/client/lib/commands/XAUTOCLAIM_JUSTID.spec.ts +++ b/packages/client/lib/commands/XAUTOCLAIM_JUSTID.spec.ts @@ -1,31 +1,37 @@ -import { strict as assert } from 'assert'; +import { strict as assert } from 'node:assert'; import testUtils, { GLOBAL } from '../test-utils'; -import { transformArguments } from './XAUTOCLAIM_JUSTID'; +import XAUTOCLAIM_JUSTID from './XAUTOCLAIM_JUSTID'; describe('XAUTOCLAIM JUSTID', () => { - testUtils.isVersionGreaterThanHook([6, 2]); + testUtils.isVersionGreaterThanHook([6, 2]); - it('transformArguments', () => { - assert.deepEqual( - transformArguments('key', 'group', 'consumer', 1, '0-0'), - ['XAUTOCLAIM', 'key', 'group', 'consumer', '1', '0-0', 'JUSTID'] - ); - }); + it('transformArguments', () => { + assert.deepEqual( + XAUTOCLAIM_JUSTID.transformArguments('key', 'group', 'consumer', 1, '0-0'), + ['XAUTOCLAIM', 'key', 'group', 'consumer', '1', '0-0', 'JUSTID'] + ); + }); - testUtils.testWithClient('client.xAutoClaimJustId', async client => { - await Promise.all([ - client.xGroupCreate('key', 'group', '$', { - MKSTREAM: true - }), - client.xGroupCreateConsumer('key', 'group', 'consumer'), - ]); + testUtils.testWithClient('client.xAutoClaimJustId', async client => { + const [, , id, , reply] = await Promise.all([ + client.xGroupCreate('key', 'group', '$', { + MKSTREAM: true + }), + client.xGroupCreateConsumer('key', 'group', 'consumer'), + client.xAdd('key', '*', { + field: 'value' + }), + client.xReadGroup('group', 'consumer', { + key: 'key', + id: '>' + }), + client.xAutoClaimJustId('key', 'group', 'consumer', 0, '0-0') + ]); - assert.deepEqual( - await client.xAutoClaimJustId('key', 'group', 'consumer', 1, '0-0'), - { - nextId: '0-0', - messages: [] - } - ); - }, GLOBAL.SERVERS.OPEN); + assert.deepEqual(reply, { + nextId: '0-0', + messages: [id], + deletedMessages: testUtils.isVersionGreaterThan([7, 0]) ? [] : undefined + }); + }, GLOBAL.SERVERS.OPEN); }); diff --git a/packages/client/lib/commands/XAUTOCLAIM_JUSTID.ts b/packages/client/lib/commands/XAUTOCLAIM_JUSTID.ts index a30ac1579e7..e2832f23536 100644 --- a/packages/client/lib/commands/XAUTOCLAIM_JUSTID.ts +++ b/packages/client/lib/commands/XAUTOCLAIM_JUSTID.ts @@ -1,25 +1,25 @@ -import { RedisCommandArgument, RedisCommandArguments } from '.'; -import { transformArguments as transformXAutoClaimArguments } from './XAUTOCLAIM'; +import { TuplesReply, BlobStringReply, ArrayReply, UnwrapReply, Command } from '../RESP/types'; +import XAUTOCLAIM from './XAUTOCLAIM'; -export { FIRST_KEY_INDEX } from './XAUTOCLAIM'; +type XAutoClaimJustIdRawReply = TuplesReply<[ + nextId: BlobStringReply, + messages: ArrayReply, + deletedMessages: ArrayReply +]>; -export function transformArguments(...args: Parameters): RedisCommandArguments { - return [ - ...transformXAutoClaimArguments(...args), - 'JUSTID' - ]; -} - -type XAutoClaimJustIdRawReply = [RedisCommandArgument, Array]; - -interface XAutoClaimJustIdReply { - nextId: RedisCommandArgument; - messages: Array; -} - -export function transformReply(reply: XAutoClaimJustIdRawReply): XAutoClaimJustIdReply { +export default { + FIRST_KEY_INDEX: XAUTOCLAIM.FIRST_KEY_INDEX, + IS_READ_ONLY: XAUTOCLAIM.IS_READ_ONLY, + transformArguments(...args: Parameters) { + const redisArgs = XAUTOCLAIM.transformArguments(...args); + redisArgs.push('JUSTID'); + return redisArgs; + }, + transformReply(reply: UnwrapReply) { return { - nextId: reply[0], - messages: reply[1] + nextId: reply[0], + messages: reply[1], + deletedMessages: reply[2] }; -} + } +} as const satisfies Command; diff --git a/packages/client/lib/commands/XCLAIM.spec.ts b/packages/client/lib/commands/XCLAIM.spec.ts index 6626e84c731..e8fde2e1a1a 100644 --- a/packages/client/lib/commands/XCLAIM.spec.ts +++ b/packages/client/lib/commands/XCLAIM.spec.ts @@ -1,120 +1,125 @@ -import { strict as assert } from 'assert'; +import { strict as assert } from 'node:assert'; import testUtils, { GLOBAL } from '../test-utils'; -import { transformArguments } from './XCLAIM'; +import XCLAIM from './XCLAIM'; describe('XCLAIM', () => { - describe('transformArguments', () => { - it('single id (string)', () => { - assert.deepEqual( - transformArguments('key', 'group', 'consumer', 1, '0-0'), - ['XCLAIM', 'key', 'group', 'consumer', '1', '0-0'] - ); - }); - - it('multiple ids (array)', () => { - assert.deepEqual( - transformArguments('key', 'group', 'consumer', 1, ['0-0', '1-0']), - ['XCLAIM', 'key', 'group', 'consumer', '1', '0-0', '1-0'] - ); - }); - - it('with IDLE', () => { - assert.deepEqual( - transformArguments('key', 'group', 'consumer', 1, '0-0', { - IDLE: 1 - }), - ['XCLAIM', 'key', 'group', 'consumer', '1', '0-0', 'IDLE', '1'] - ); - }); - - it('with TIME (number)', () => { - assert.deepEqual( - transformArguments('key', 'group', 'consumer', 1, '0-0', { - TIME: 1 - }), - ['XCLAIM', 'key', 'group', 'consumer', '1', '0-0', 'TIME', '1'] - ); - }); - - it('with TIME (date)', () => { - const d = new Date(); - assert.deepEqual( - transformArguments('key', 'group', 'consumer', 1, '0-0', { - TIME: d - }), - ['XCLAIM', 'key', 'group', 'consumer', '1', '0-0', 'TIME', d.getTime().toString()] - ); - }); + describe('transformArguments', () => { + it('single id (string)', () => { + assert.deepEqual( + XCLAIM.transformArguments('key', 'group', 'consumer', 1, '0-0'), + ['XCLAIM', 'key', 'group', 'consumer', '1', '0-0'] + ); + }); - it('with RETRYCOUNT', () => { - assert.deepEqual( - transformArguments('key', 'group', 'consumer', 1, '0-0', { - RETRYCOUNT: 1 - }), - ['XCLAIM', 'key', 'group', 'consumer', '1', '0-0', 'RETRYCOUNT', '1'] - ); - }); + it('multiple ids (array)', () => { + assert.deepEqual( + XCLAIM.transformArguments('key', 'group', 'consumer', 1, ['0-0', '1-0']), + ['XCLAIM', 'key', 'group', 'consumer', '1', '0-0', '1-0'] + ); + }); - it('with FORCE', () => { - assert.deepEqual( - transformArguments('key', 'group', 'consumer', 1, '0-0', { - FORCE: true - }), - ['XCLAIM', 'key', 'group', 'consumer', '1', '0-0', 'FORCE'] - ); - }); + it('with IDLE', () => { + assert.deepEqual( + XCLAIM.transformArguments('key', 'group', 'consumer', 1, '0-0', { + IDLE: 1 + }), + ['XCLAIM', 'key', 'group', 'consumer', '1', '0-0', 'IDLE', '1'] + ); + }); + + describe('with TIME', () => { + it('number', () => { + assert.deepEqual( + XCLAIM.transformArguments('key', 'group', 'consumer', 1, '0-0', { + TIME: 1 + }), + ['XCLAIM', 'key', 'group', 'consumer', '1', '0-0', 'TIME', '1'] + ); + }); + + it('Date', () => { + const d = new Date(); + assert.deepEqual( + XCLAIM.transformArguments('key', 'group', 'consumer', 1, '0-0', { + TIME: d + }), + ['XCLAIM', 'key', 'group', 'consumer', '1', '0-0', 'TIME', d.getTime().toString()] + ); + }); + }); - it('with IDLE, TIME, RETRYCOUNT, FORCE, JUSTID', () => { - assert.deepEqual( - transformArguments('key', 'group', 'consumer', 1, '0-0', { - IDLE: 1, - TIME: 1, - RETRYCOUNT: 1, - FORCE: true - }), - ['XCLAIM', 'key', 'group', 'consumer', '1', '0-0', 'IDLE', '1', 'TIME', '1', 'RETRYCOUNT', '1', 'FORCE'] - ); - }); + it('with RETRYCOUNT', () => { + assert.deepEqual( + XCLAIM.transformArguments('key', 'group', 'consumer', 1, '0-0', { + RETRYCOUNT: 1 + }), + ['XCLAIM', 'key', 'group', 'consumer', '1', '0-0', 'RETRYCOUNT', '1'] + ); }); - testUtils.testWithClient('client.xClaim', async client => { - await client.xGroupCreate('key', 'group', '$', { - MKSTREAM: true - }); + it('with FORCE', () => { + assert.deepEqual( + XCLAIM.transformArguments('key', 'group', 'consumer', 1, '0-0', { + FORCE: true + }), + ['XCLAIM', 'key', 'group', 'consumer', '1', '0-0', 'FORCE'] + ); + }); - assert.deepEqual( - await client.xClaim('key', 'group', 'consumer', 0, '0-0'), - [] - ); - }, GLOBAL.SERVERS.OPEN); + it('with LASTID', () => { + assert.deepEqual( + XCLAIM.transformArguments('key', 'group', 'consumer', 1, '0-0', { + LASTID: '0-0' + }), + ['XCLAIM', 'key', 'group', 'consumer', '1', '0-0', 'LASTID', '0-0'] + ); + }); - testUtils.testWithClient('client.xClaim with a message', async client => { - await client.xGroupCreate('key', 'group', '$', { MKSTREAM: true }); - const id = await client.xAdd('key', '*', { foo: 'bar' }); - await client.xReadGroup('group', 'consumer', { key: 'key', id: '>' }); + it('with IDLE, TIME, RETRYCOUNT, FORCE, LASTID', () => { + assert.deepEqual( + XCLAIM.transformArguments('key', 'group', 'consumer', 1, '0-0', { + IDLE: 1, + TIME: 1, + RETRYCOUNT: 1, + FORCE: true, + LASTID: '0-0' + }), + ['XCLAIM', 'key', 'group', 'consumer', '1', '0-0', 'IDLE', '1', 'TIME', '1', 'RETRYCOUNT', '1', 'FORCE', 'LASTID', '0-0'] + ); + }); + }); - assert.deepEqual( - await client.xClaim('key', 'group', 'consumer', 0, id), - [{ - id, - message: Object.create(null, { 'foo': { - value: 'bar', - configurable: true, - enumerable: true - } }) - }] - ); - }, GLOBAL.SERVERS.OPEN); + testUtils.testAll('xClaim', async client => { + const message = Object.create(null, { + field: { + value: 'value', + enumerable: true + } + }); - testUtils.testWithClient('client.xClaim with a trimmed message', async client => { - await client.xGroupCreate('key', 'group', '$', { MKSTREAM: true }); - const id = await client.xAdd('key', '*', { foo: 'bar' }); - await client.xReadGroup('group', 'consumer', { key: 'key', id: '>' }); - await client.xTrim('key', 'MAXLEN', 0); + const [, , , , , reply] = await Promise.all([ + client.xGroupCreate('key', 'group', '$', { + MKSTREAM: true + }), + client.xAdd('key', '1-0', message), + client.xAdd('key', '2-0', message), + client.xReadGroup('group', 'consumer', { + key: 'key', + id: '>' + }), + client.xTrim('key', 'MAXLEN', 1), + client.xClaim('key', 'group', 'consumer', 0, ['1-0', '2-0']) + ]); - assert.deepEqual( - await client.xClaim('key', 'group', 'consumer', 0, id), - testUtils.isVersionGreaterThan([7, 0]) ? []: [null] - ); - }, GLOBAL.SERVERS.OPEN); + assert.deepEqual(reply, [ + ...(testUtils.isVersionGreaterThan([7, 0]) ? [] : [null]), + { + id: '2-0', + message + } + ]); + }, { + client: GLOBAL.SERVERS.OPEN, + cluster: GLOBAL.CLUSTERS.OPEN + }); }); diff --git a/packages/client/lib/commands/XCLAIM.ts b/packages/client/lib/commands/XCLAIM.ts index e7b458e2376..eb9c0b325e1 100644 --- a/packages/client/lib/commands/XCLAIM.ts +++ b/packages/client/lib/commands/XCLAIM.ts @@ -1,48 +1,60 @@ -import { RedisCommandArgument, RedisCommandArguments } from '.'; -import { pushVerdictArguments } from './generic-transformers'; - -export const FIRST_KEY_INDEX = 1; +import { RedisArgument, ArrayReply, NullReply, UnwrapReply, Command, TypeMapping } from '../RESP/types'; +import { RedisVariadicArgument, pushVariadicArguments, StreamMessageRawReply, transformStreamMessageNullReply } from './generic-transformers'; export interface XClaimOptions { - IDLE?: number; - TIME?: number | Date; - RETRYCOUNT?: number; - FORCE?: true; + IDLE?: number; + TIME?: number | Date; + RETRYCOUNT?: number; + FORCE?: boolean; + LASTID?: RedisArgument; } -export function transformArguments( - key: RedisCommandArgument, - group: RedisCommandArgument, - consumer: RedisCommandArgument, +export default { + FIRST_KEY_INDEX: 1, + IS_READ_ONLY: false, + transformArguments( + key: RedisArgument, + group: RedisArgument, + consumer: RedisArgument, minIdleTime: number, - id: RedisCommandArgument | Array, + id: RedisVariadicArgument, options?: XClaimOptions -): RedisCommandArguments { - const args = pushVerdictArguments( - ['XCLAIM', key, group, consumer, minIdleTime.toString()], - id + ) { + const args = pushVariadicArguments( + ['XCLAIM', key, group, consumer, minIdleTime.toString()], + id ); - if (options?.IDLE) { - args.push('IDLE', options.IDLE.toString()); + if (options?.IDLE !== undefined) { + args.push('IDLE', options.IDLE.toString()); } - if (options?.TIME) { - args.push( - 'TIME', - (typeof options.TIME === 'number' ? options.TIME : options.TIME.getTime()).toString() - ); + if (options?.TIME !== undefined) { + args.push( + 'TIME', + (options.TIME instanceof Date ? options.TIME.getTime() : options.TIME).toString() + ); } - if (options?.RETRYCOUNT) { - args.push('RETRYCOUNT', options.RETRYCOUNT.toString()); + if (options?.RETRYCOUNT !== undefined) { + args.push('RETRYCOUNT', options.RETRYCOUNT.toString()); } if (options?.FORCE) { - args.push('FORCE'); + args.push('FORCE'); } - return args; -} + if (options?.LASTID !== undefined) { + args.push('LASTID', options.LASTID); + } -export { transformStreamMessagesNullReply as transformReply } from './generic-transformers'; + return args; + }, + transformReply( + reply: UnwrapReply>, + preserve?: any, + typeMapping?: TypeMapping + ) { + return reply.map(transformStreamMessageNullReply.bind(undefined, typeMapping)); + } +} as const satisfies Command; diff --git a/packages/client/lib/commands/XCLAIM_JUSTID.spec.ts b/packages/client/lib/commands/XCLAIM_JUSTID.spec.ts index 619f876d53d..6b580ac3c1b 100644 --- a/packages/client/lib/commands/XCLAIM_JUSTID.spec.ts +++ b/packages/client/lib/commands/XCLAIM_JUSTID.spec.ts @@ -1,23 +1,24 @@ -import { strict as assert } from 'assert'; +import { strict as assert } from 'node:assert'; import testUtils, { GLOBAL } from '../test-utils'; -import { transformArguments } from './XCLAIM_JUSTID'; +import XCLAIM_JUSTID from './XCLAIM_JUSTID'; describe('XCLAIM JUSTID', () => { - it('transformArguments', () => { - assert.deepEqual( - transformArguments('key', 'group', 'consumer', 1, '0-0'), - ['XCLAIM', 'key', 'group', 'consumer', '1', '0-0', 'JUSTID'] - ); - }); + it('transformArguments', () => { + assert.deepEqual( + XCLAIM_JUSTID.transformArguments('key', 'group', 'consumer', 1, '0-0'), + ['XCLAIM', 'key', 'group', 'consumer', '1', '0-0', 'JUSTID'] + ); + }); - testUtils.testWithClient('client.xClaimJustId', async client => { - await client.xGroupCreate('key', 'group', '$', { - MKSTREAM: true - }); + // TODO: test with messages + testUtils.testWithClient('client.xClaimJustId', async client => { + const [, reply] = await Promise.all([ + client.xGroupCreate('key', 'group', '$', { + MKSTREAM: true + }), + client.xClaimJustId('key', 'group', 'consumer', 1, '0-0') + ]); - assert.deepEqual( - await client.xClaimJustId('key', 'group', 'consumer', 1, '0-0'), - [] - ); - }, GLOBAL.SERVERS.OPEN); + assert.deepEqual(reply, []); + }, GLOBAL.SERVERS.OPEN); }); diff --git a/packages/client/lib/commands/XCLAIM_JUSTID.ts b/packages/client/lib/commands/XCLAIM_JUSTID.ts index 50d0d5a0366..6200c9106e1 100644 --- a/packages/client/lib/commands/XCLAIM_JUSTID.ts +++ b/packages/client/lib/commands/XCLAIM_JUSTID.ts @@ -1,13 +1,13 @@ -import { RedisCommandArgument, RedisCommandArguments } from '.'; -import { transformArguments as transformXClaimArguments } from './XCLAIM'; +import { ArrayReply, BlobStringReply, Command } from '../RESP/types'; +import XCLAIM from './XCLAIM'; -export { FIRST_KEY_INDEX } from './XCLAIM'; - -export function transformArguments(...args: Parameters): RedisCommandArguments { - return [ - ...transformXClaimArguments(...args), - 'JUSTID' - ]; -} - -export declare function transformReply(): Array; +export default { + FIRST_KEY_INDEX: XCLAIM.FIRST_KEY_INDEX, + IS_READ_ONLY: XCLAIM.IS_READ_ONLY, + transformArguments(...args: Parameters) { + const redisArgs = XCLAIM.transformArguments(...args); + redisArgs.push('JUSTID'); + return redisArgs; + }, + transformReply: undefined as unknown as () => ArrayReply +} as const satisfies Command; diff --git a/packages/client/lib/commands/XDEL.spec.ts b/packages/client/lib/commands/XDEL.spec.ts index 00f9e2f9c67..15875d3b7b3 100644 --- a/packages/client/lib/commands/XDEL.spec.ts +++ b/packages/client/lib/commands/XDEL.spec.ts @@ -1,28 +1,31 @@ -import { strict as assert } from 'assert'; +import { strict as assert } from 'node:assert'; import testUtils, { GLOBAL } from '../test-utils'; -import { transformArguments } from './XDEL'; +import XDEL from './XDEL'; describe('XDEL', () => { - describe('transformArguments', () => { - it('string', () => { - assert.deepEqual( - transformArguments('key', '0-0'), - ['XDEL', 'key', '0-0'] - ); - }); + describe('transformArguments', () => { + it('string', () => { + assert.deepEqual( + XDEL.transformArguments('key', '0-0'), + ['XDEL', 'key', '0-0'] + ); + }); - it('array', () => { - assert.deepEqual( - transformArguments('key', ['0-0', '1-0']), - ['XDEL', 'key', '0-0', '1-0'] - ); - }); + it('array', () => { + assert.deepEqual( + XDEL.transformArguments('key', ['0-0', '1-0']), + ['XDEL', 'key', '0-0', '1-0'] + ); }); + }); - testUtils.testWithClient('client.xDel', async client => { - assert.equal( - await client.xDel('key', '0-0'), - 0 - ); - }, GLOBAL.SERVERS.OPEN); + testUtils.testAll('xDel', async client => { + assert.equal( + await client.xDel('key', '0-0'), + 0 + ); + }, { + client: GLOBAL.SERVERS.OPEN, + cluster: GLOBAL.CLUSTERS.OPEN + }); }); diff --git a/packages/client/lib/commands/XDEL.ts b/packages/client/lib/commands/XDEL.ts index 82b30d21092..acc9198c2b8 100644 --- a/packages/client/lib/commands/XDEL.ts +++ b/packages/client/lib/commands/XDEL.ts @@ -1,13 +1,11 @@ -import { RedisCommandArgument, RedisCommandArguments } from '.'; -import { pushVerdictArguments } from './generic-transformers'; +import { RedisArgument, NumberReply, Command } from '../RESP/types'; +import { RedisVariadicArgument, pushVariadicArguments } from './generic-transformers'; -export const FIRST_KEY_INDEX = 1; - -export function transformArguments( - key: RedisCommandArgument, - id: RedisCommandArgument | Array -): RedisCommandArguments { - return pushVerdictArguments(['XDEL', key], id); -} - -export declare function transformReply(): number; +export default { + FIRST_KEY_INDEX: 1, + IS_READ_ONLY: false, + transformArguments(key: RedisArgument, id: RedisVariadicArgument) { + return pushVariadicArguments(['XDEL', key], id); + }, + transformReply: undefined as unknown as () => NumberReply +} as const satisfies Command; diff --git a/packages/client/lib/commands/XGROUP_CREATE.spec.ts b/packages/client/lib/commands/XGROUP_CREATE.spec.ts index 57516e44cc8..5c9071289c9 100644 --- a/packages/client/lib/commands/XGROUP_CREATE.spec.ts +++ b/packages/client/lib/commands/XGROUP_CREATE.spec.ts @@ -1,32 +1,44 @@ -import { strict as assert } from 'assert'; +import { strict as assert } from 'node:assert'; import testUtils, { GLOBAL } from '../test-utils'; -import { transformArguments } from './XGROUP_CREATE'; +import XGROUP_CREATE from './XGROUP_CREATE'; describe('XGROUP CREATE', () => { - describe('transformArguments', () => { - it('simple', () => { - assert.deepEqual( - transformArguments('key', 'group', '$'), - ['XGROUP', 'CREATE', 'key', 'group', '$'] - ); - }); + describe('transformArguments', () => { + it('simple', () => { + assert.deepEqual( + XGROUP_CREATE.transformArguments('key', 'group', '$'), + ['XGROUP', 'CREATE', 'key', 'group', '$'] + ); + }); + + it('with MKSTREAM', () => { + assert.deepEqual( + XGROUP_CREATE.transformArguments('key', 'group', '$', { + MKSTREAM: true + }), + ['XGROUP', 'CREATE', 'key', 'group', '$', 'MKSTREAM'] + ); + }); - it('with MKSTREAM', () => { - assert.deepEqual( - transformArguments('key', 'group', '$', { - MKSTREAM: true - }), - ['XGROUP', 'CREATE', 'key', 'group', '$', 'MKSTREAM'] - ); - }); + it('with ENTRIESREAD', () => { + assert.deepEqual( + XGROUP_CREATE.transformArguments('key', 'group', '$', { + ENTRIESREAD: 1 + }), + ['XGROUP', 'CREATE', 'key', 'group', '$', 'ENTRIESREAD', '1'] + ); }); + }); - testUtils.testWithClient('client.xGroupCreate', async client => { - assert.equal( - await client.xGroupCreate('key', 'group', '$', { - MKSTREAM: true - }), - 'OK' - ); - }, GLOBAL.SERVERS.OPEN); + testUtils.testAll('xGroupCreate', async client => { + assert.equal( + await client.xGroupCreate('key', 'group', '$', { + MKSTREAM: true + }), + 'OK' + ); + }, { + client: GLOBAL.SERVERS.OPEN, + cluster: GLOBAL.CLUSTERS.OPEN + }); }); diff --git a/packages/client/lib/commands/XGROUP_CREATE.ts b/packages/client/lib/commands/XGROUP_CREATE.ts index 8cfd4e262e4..a04fcbeb044 100644 --- a/packages/client/lib/commands/XGROUP_CREATE.ts +++ b/packages/client/lib/commands/XGROUP_CREATE.ts @@ -1,24 +1,34 @@ -import { RedisCommandArgument, RedisCommandArguments } from '.'; +import { RedisArgument, SimpleStringReply, Command } from '../RESP/types'; -export const FIRST_KEY_INDEX = 2; - -interface XGroupCreateOptions { - MKSTREAM?: true; +export interface XGroupCreateOptions { + MKSTREAM?: boolean; + /** + * added in 7.0 + */ + ENTRIESREAD?: number; } -export function transformArguments( - key: RedisCommandArgument, - group: RedisCommandArgument, - id: RedisCommandArgument, +export default { + FIRST_KEY_INDEX: 2, + IS_READ_ONLY: false, + transformArguments( + key: RedisArgument, + group: RedisArgument, + id: RedisArgument, options?: XGroupCreateOptions -): RedisCommandArguments { + ) { const args = ['XGROUP', 'CREATE', key, group, id]; if (options?.MKSTREAM) { - args.push('MKSTREAM'); + args.push('MKSTREAM'); + } + + if (options?.ENTRIESREAD) { + args.push('ENTRIESREAD', options.ENTRIESREAD.toString()); } return args; -} + }, + transformReply: undefined as unknown as () => SimpleStringReply<'OK'> +} as const satisfies Command; -export declare function transformReply(): RedisCommandArgument; diff --git a/packages/client/lib/commands/XGROUP_CREATECONSUMER.spec.ts b/packages/client/lib/commands/XGROUP_CREATECONSUMER.spec.ts index 62443345188..3c3ecbda0a7 100644 --- a/packages/client/lib/commands/XGROUP_CREATECONSUMER.spec.ts +++ b/packages/client/lib/commands/XGROUP_CREATECONSUMER.spec.ts @@ -1,25 +1,28 @@ -import { strict as assert } from 'assert'; +import { strict as assert } from 'node:assert'; import testUtils, { GLOBAL } from '../test-utils'; -import { transformArguments } from './XGROUP_CREATECONSUMER'; +import XGROUP_CREATECONSUMER from './XGROUP_CREATECONSUMER'; describe('XGROUP CREATECONSUMER', () => { - testUtils.isVersionGreaterThanHook([6, 2]); + testUtils.isVersionGreaterThanHook([6, 2]); - it('transformArguments', () => { - assert.deepEqual( - transformArguments('key', 'group', 'consumer'), - ['XGROUP', 'CREATECONSUMER', 'key', 'group', 'consumer'] - ); - }); + it('transformArguments', () => { + assert.deepEqual( + XGROUP_CREATECONSUMER.transformArguments('key', 'group', 'consumer'), + ['XGROUP', 'CREATECONSUMER', 'key', 'group', 'consumer'] + ); + }); - testUtils.testWithClient('client.xGroupCreateConsumer', async client => { - await client.xGroupCreate('key', 'group', '$', { - MKSTREAM: true - }); + testUtils.testAll('xGroupCreateConsumer', async client => { + const [, reply] = await Promise.all([ + client.xGroupCreate('key', 'group', '$', { + MKSTREAM: true + }), + client.xGroupCreateConsumer('key', 'group', 'consumer') + ]); - assert.equal( - await client.xGroupCreateConsumer('key', 'group', 'consumer'), - true - ); - }, GLOBAL.SERVERS.OPEN); + assert.equal(reply, 1); + }, { + client: GLOBAL.SERVERS.OPEN, + cluster: GLOBAL.CLUSTERS.OPEN + }); }); diff --git a/packages/client/lib/commands/XGROUP_CREATECONSUMER.ts b/packages/client/lib/commands/XGROUP_CREATECONSUMER.ts index 2b816a6b480..8fd21ca60de 100644 --- a/packages/client/lib/commands/XGROUP_CREATECONSUMER.ts +++ b/packages/client/lib/commands/XGROUP_CREATECONSUMER.ts @@ -1,13 +1,14 @@ -import { RedisCommandArgument, RedisCommandArguments } from '.'; +import { RedisArgument, Command, NumberReply } from '../RESP/types'; -export const FIRST_KEY_INDEX = 2; - -export function transformArguments( - key: RedisCommandArgument, - group: RedisCommandArgument, - consumer: RedisCommandArgument -): RedisCommandArguments { +export default { + FIRST_KEY_INDEX: 2, + IS_READ_ONLY: false, + transformArguments( + key: RedisArgument, + group: RedisArgument, + consumer: RedisArgument + ) { return ['XGROUP', 'CREATECONSUMER', key, group, consumer]; -} - -export { transformBooleanReply as transformReply } from './generic-transformers'; + }, + transformReply: undefined as unknown as () => NumberReply +} as const satisfies Command; diff --git a/packages/client/lib/commands/XGROUP_DELCONSUMER.spec.ts b/packages/client/lib/commands/XGROUP_DELCONSUMER.spec.ts index d071aedf64f..afc524eef86 100644 --- a/packages/client/lib/commands/XGROUP_DELCONSUMER.spec.ts +++ b/packages/client/lib/commands/XGROUP_DELCONSUMER.spec.ts @@ -1,23 +1,26 @@ -import { strict as assert } from 'assert'; +import { strict as assert } from 'node:assert'; import testUtils, { GLOBAL } from '../test-utils'; -import { transformArguments } from './XGROUP_DELCONSUMER'; +import XGROUP_DELCONSUMER from './XGROUP_DELCONSUMER'; describe('XGROUP DELCONSUMER', () => { - it('transformArguments', () => { - assert.deepEqual( - transformArguments('key', 'group', 'consumer'), - ['XGROUP', 'DELCONSUMER', 'key', 'group', 'consumer'] - ); - }); + it('transformArguments', () => { + assert.deepEqual( + XGROUP_DELCONSUMER.transformArguments('key', 'group', 'consumer'), + ['XGROUP', 'DELCONSUMER', 'key', 'group', 'consumer'] + ); + }); - testUtils.testWithClient('client.xGroupDelConsumer', async client => { - await client.xGroupCreate('key', 'group', '$', { - MKSTREAM: true - }); + testUtils.testAll('xGroupDelConsumer', async client => { + const [, reply] = await Promise.all([ + client.xGroupCreate('key', 'group', '$', { + MKSTREAM: true + }), + client.xGroupDelConsumer('key', 'group', 'consumer') + ]); - assert.equal( - await client.xGroupDelConsumer('key', 'group', 'consumer'), - 0 - ); - }, GLOBAL.SERVERS.OPEN); + assert.equal(reply, 0); + }, { + client: GLOBAL.SERVERS.OPEN, + cluster: GLOBAL.CLUSTERS.OPEN + }); }); diff --git a/packages/client/lib/commands/XGROUP_DELCONSUMER.ts b/packages/client/lib/commands/XGROUP_DELCONSUMER.ts index 4e4fc096d07..53007270e05 100644 --- a/packages/client/lib/commands/XGROUP_DELCONSUMER.ts +++ b/packages/client/lib/commands/XGROUP_DELCONSUMER.ts @@ -1,13 +1,14 @@ -import { RedisCommandArgument, RedisCommandArguments } from '.'; +import { RedisArgument, NumberReply, Command } from '../RESP/types'; -export const FIRST_KEY_INDEX = 2; - -export function transformArguments( - key: RedisCommandArgument, - group: RedisCommandArgument, - consumer: RedisCommandArgument -): RedisCommandArguments { +export default { + FIRST_KEY_INDEX: 2, + IS_READ_ONLY: false, + transformArguments( + key: RedisArgument, + group: RedisArgument, + consumer: RedisArgument + ) { return ['XGROUP', 'DELCONSUMER', key, group, consumer]; -} - -export declare function transformReply(): number; + }, + transformReply: undefined as unknown as () => NumberReply +} as const satisfies Command; diff --git a/packages/client/lib/commands/XGROUP_DESTROY.spec.ts b/packages/client/lib/commands/XGROUP_DESTROY.spec.ts index ea8e7b7be98..6ec90834518 100644 --- a/packages/client/lib/commands/XGROUP_DESTROY.spec.ts +++ b/packages/client/lib/commands/XGROUP_DESTROY.spec.ts @@ -1,23 +1,26 @@ -import { strict as assert } from 'assert'; +import { strict as assert } from 'node:assert'; import testUtils, { GLOBAL } from '../test-utils'; -import { transformArguments } from './XGROUP_DESTROY'; +import XGROUP_DESTROY from './XGROUP_DESTROY'; describe('XGROUP DESTROY', () => { - it('transformArguments', () => { - assert.deepEqual( - transformArguments('key', 'group'), - ['XGROUP', 'DESTROY', 'key', 'group'] - ); - }); + it('transformArguments', () => { + assert.deepEqual( + XGROUP_DESTROY.transformArguments('key', 'group'), + ['XGROUP', 'DESTROY', 'key', 'group'] + ); + }); - testUtils.testWithClient('client.xGroupDestroy', async client => { - await client.xGroupCreate('key', 'group', '$', { - MKSTREAM: true - }); + testUtils.testAll('xGroupDestroy', async client => { + const [, reply] = await Promise.all([ + client.xGroupCreate('key', 'group', '$', { + MKSTREAM: true + }), + client.xGroupDestroy('key', 'group') + ]); - assert.equal( - await client.xGroupDestroy('key', 'group'), - true - ); - }, GLOBAL.SERVERS.OPEN); + assert.equal(reply, 1); + }, { + client: GLOBAL.SERVERS.OPEN, + cluster: GLOBAL.CLUSTERS.OPEN + }); }); diff --git a/packages/client/lib/commands/XGROUP_DESTROY.ts b/packages/client/lib/commands/XGROUP_DESTROY.ts index 85910c02471..6c14d9ae2bd 100644 --- a/packages/client/lib/commands/XGROUP_DESTROY.ts +++ b/packages/client/lib/commands/XGROUP_DESTROY.ts @@ -1,12 +1,13 @@ -import { RedisCommandArgument, RedisCommandArguments } from '.'; +import { RedisArgument, NumberReply, Command } from '../RESP/types'; -export const FIRST_KEY_INDEX = 2; - -export function transformArguments( - key: RedisCommandArgument, - group: RedisCommandArgument -): RedisCommandArguments { +export default { + FIRST_KEY_INDEX: 2, + IS_READ_ONLY: false, + transformArguments( + key: RedisArgument, + group: RedisArgument + ) { return ['XGROUP', 'DESTROY', key, group]; -} - -export { transformBooleanReply as transformReply } from './generic-transformers'; + }, + transformReply: undefined as unknown as () => NumberReply +} as const satisfies Command; diff --git a/packages/client/lib/commands/XGROUP_SETID.spec.ts b/packages/client/lib/commands/XGROUP_SETID.spec.ts index 8df51f5401d..891a796d14d 100644 --- a/packages/client/lib/commands/XGROUP_SETID.spec.ts +++ b/packages/client/lib/commands/XGROUP_SETID.spec.ts @@ -1,23 +1,26 @@ -import { strict as assert } from 'assert'; +import { strict as assert } from 'node:assert'; import testUtils, { GLOBAL } from '../test-utils'; -import { transformArguments } from './XGROUP_SETID'; +import XGROUP_SETID from './XGROUP_SETID'; describe('XGROUP SETID', () => { - it('transformArguments', () => { - assert.deepEqual( - transformArguments('key', 'group', '0'), - ['XGROUP', 'SETID', 'key', 'group', '0'] - ); - }); + it('transformArguments', () => { + assert.deepEqual( + XGROUP_SETID.transformArguments('key', 'group', '0'), + ['XGROUP', 'SETID', 'key', 'group', '0'] + ); + }); - testUtils.testWithClient('client.xGroupSetId', async client => { - await client.xGroupCreate('key', 'group', '$', { - MKSTREAM: true - }); + testUtils.testAll('xGroupSetId', async client => { + const [, reply] = await Promise.all([ + client.xGroupCreate('key', 'group', '$', { + MKSTREAM: true + }), + client.xGroupSetId('key', 'group', '0') + ]); - assert.equal( - await client.xGroupSetId('key', 'group', '0'), - 'OK' - ); - }, GLOBAL.SERVERS.OPEN); + assert.equal(reply, 'OK'); + }, { + client: GLOBAL.SERVERS.OPEN, + cluster: GLOBAL.CLUSTERS.OPEN + }); }); diff --git a/packages/client/lib/commands/XGROUP_SETID.ts b/packages/client/lib/commands/XGROUP_SETID.ts index e732fc8d7bf..a23b4144335 100644 --- a/packages/client/lib/commands/XGROUP_SETID.ts +++ b/packages/client/lib/commands/XGROUP_SETID.ts @@ -1,13 +1,26 @@ -import { RedisCommandArgument, RedisCommandArguments } from '.'; +import { RedisArgument, SimpleStringReply, Command } from '../RESP/types'; -export const FIRST_KEY_INDEX = 2; - -export function transformArguments( - key: RedisCommandArgument, - group: RedisCommandArgument, - id: RedisCommandArgument -): RedisCommandArguments { - return ['XGROUP', 'SETID', key, group, id]; +export interface XGroupSetIdOptions { + /** added in 7.0 */ + ENTRIESREAD?: number; } -export declare function transformReply(): RedisCommandArgument; +export default { + FIRST_KEY_INDEX: 2, + IS_READ_ONLY: false, + transformArguments( + key: RedisArgument, + group: RedisArgument, + id: RedisArgument, + options?: XGroupSetIdOptions + ) { + const args = ['XGROUP', 'SETID', key, group, id]; + + if (options?.ENTRIESREAD) { + args.push('ENTRIESREAD', options.ENTRIESREAD.toString()); + } + + return args; + }, + transformReply: undefined as unknown as () => SimpleStringReply<'OK'> +} as const satisfies Command; diff --git a/packages/client/lib/commands/XINFO_CONSUMERS.spec.ts b/packages/client/lib/commands/XINFO_CONSUMERS.spec.ts index a2c58999773..86abdbb1493 100644 --- a/packages/client/lib/commands/XINFO_CONSUMERS.spec.ts +++ b/packages/client/lib/commands/XINFO_CONSUMERS.spec.ts @@ -1,43 +1,38 @@ -import { strict as assert } from 'assert'; +import { strict as assert } from 'node:assert'; import testUtils, { GLOBAL } from '../test-utils'; -import { transformArguments, transformReply } from './XINFO_CONSUMERS'; +import XINFO_CONSUMERS from './XINFO_CONSUMERS'; describe('XINFO CONSUMERS', () => { - it('transformArguments', () => { - assert.deepEqual( - transformArguments('key', 'group'), - ['XINFO', 'CONSUMERS', 'key', 'group'] - ); - }); + it('transformArguments', () => { + assert.deepEqual( + XINFO_CONSUMERS.transformArguments('key', 'group'), + ['XINFO', 'CONSUMERS', 'key', 'group'] + ); + }); - it('transformReply', () => { - assert.deepEqual( - transformReply([ - ['name', 'Alice', 'pending', 1, 'idle', 9104628, 'inactive', 9281221], - ['name', 'Bob', 'pending', 1, 'idle', 83841983, 'inactive', 7213871] - ]), - [{ - name: 'Alice', - pending: 1, - idle: 9104628, - inactive: 9281221, - }, { - name: 'Bob', - pending: 1, - idle: 83841983, - inactive: 7213871, - }] - ); - }); + testUtils.testAll('xInfoConsumers', async client => { + const [, , reply] = await Promise.all([ + client.xGroupCreate('key', 'group', '$', { + MKSTREAM: true + }), + // using `XREADGROUP` and not `XGROUP CREATECONSUMER` because the latter was introduced in Redis 6.2 + client.xReadGroup('group', 'consumer', { + key: 'key', + id: '0-0' + }), + client.xInfoConsumers('key', 'group') + ]); - testUtils.testWithClient('client.xInfoConsumers', async client => { - await client.xGroupCreate('key', 'group', '$', { - MKSTREAM: true - }); - - assert.deepEqual( - await client.xInfoConsumers('key', 'group'), - [] - ); - }, GLOBAL.SERVERS.OPEN); + for (const consumer of reply) { + assert.equal(typeof consumer.name, 'string'); + assert.equal(typeof consumer.pending, 'number'); + assert.equal(typeof consumer.idle, 'number'); + if (testUtils.isVersionGreaterThan([7, 2])) { + assert.equal(typeof consumer.inactive, 'number'); + } + } + }, { + client: GLOBAL.SERVERS.OPEN, + cluster: GLOBAL.CLUSTERS.OPEN + }); }); diff --git a/packages/client/lib/commands/XINFO_CONSUMERS.ts b/packages/client/lib/commands/XINFO_CONSUMERS.ts index 9b3893cc93c..ca0076d6335 100644 --- a/packages/client/lib/commands/XINFO_CONSUMERS.ts +++ b/packages/client/lib/commands/XINFO_CONSUMERS.ts @@ -1,28 +1,34 @@ -import { RedisCommandArgument, RedisCommandArguments } from '.'; +import { RedisArgument, ArrayReply, TuplesToMapReply, BlobStringReply, NumberReply, UnwrapReply, Resp2Reply, Command } from '../RESP/types'; -export const FIRST_KEY_INDEX = 2; +export type XInfoConsumersReply = ArrayReply, BlobStringReply], + [BlobStringReply<'pending'>, NumberReply], + [BlobStringReply<'idle'>, NumberReply], + /** added in 7.2 */ + [BlobStringReply<'inactive'>, NumberReply] +]>>; -export const IS_READ_ONLY = true; - -export function transformArguments( - key: RedisCommandArgument, - group: RedisCommandArgument -): RedisCommandArguments { +export default { + FIRST_KEY_INDEX: 2, + IS_READ_ONLY: true, + transformArguments( + key: RedisArgument, + group: RedisArgument + ) { return ['XINFO', 'CONSUMERS', key, group]; -} - -type XInfoConsumersReply = Array<{ - name: RedisCommandArgument; - pending: number; - idle: number; - inactive: number; -}>; - -export function transformReply(rawReply: Array): XInfoConsumersReply { - return rawReply.map(consumer => ({ - name: consumer[1], - pending: consumer[3], - idle: consumer[5], - inactive: consumer[7] - })); -} + }, + transformReply: { + 2: (reply: UnwrapReply>) => { + return reply.map(consumer => { + const unwrapped = consumer as unknown as UnwrapReply; + return { + name: unwrapped[1], + pending: unwrapped[3], + idle: unwrapped[5], + inactive: unwrapped[7] + }; + }); + }, + 3: undefined as unknown as () => XInfoConsumersReply + } +} as const satisfies Command; diff --git a/packages/client/lib/commands/XINFO_GROUPS.spec.ts b/packages/client/lib/commands/XINFO_GROUPS.spec.ts index dea8ac58d9c..1bee02a0e6d 100644 --- a/packages/client/lib/commands/XINFO_GROUPS.spec.ts +++ b/packages/client/lib/commands/XINFO_GROUPS.spec.ts @@ -1,48 +1,36 @@ -import { strict as assert } from 'assert'; +import { strict as assert } from 'node:assert'; import testUtils, { GLOBAL } from '../test-utils'; -import { transformArguments, transformReply } from './XINFO_GROUPS'; +import XINFO_GROUPS from './XINFO_GROUPS'; describe('XINFO GROUPS', () => { - it('transformArguments', () => { - assert.deepEqual( - transformArguments('key'), - ['XINFO', 'GROUPS', 'key'] - ); - }); + it('transformArguments', () => { + assert.deepEqual( + XINFO_GROUPS.transformArguments('key'), + ['XINFO', 'GROUPS', 'key'] + ); + }); - it('transformReply', () => { - assert.deepEqual( - transformReply([ - ['name', 'mygroup', 'consumers', 2, 'pending', 2, 'last-delivered-id', '1588152489012-0'], - ['name', 'some-other-group', 'consumers', 1, 'pending', 0, 'last-delivered-id', '1588152498034-0'] - ]), - [{ - name: 'mygroup', - consumers: 2, - pending: 2, - lastDeliveredId: '1588152489012-0' - }, { - name: 'some-other-group', - consumers: 1, - pending: 0, - lastDeliveredId: '1588152498034-0' - }] - ); - }); - - testUtils.testWithClient('client.xInfoGroups', async client => { - await client.xGroupCreate('key', 'group', '$', { - MKSTREAM: true - }); - - assert.deepEqual( - await client.xInfoGroups('key'), - [{ - name: 'group', - consumers: 0, - pending: 0, - lastDeliveredId: '0-0' - }] - ); - }, GLOBAL.SERVERS.OPEN); + testUtils.testAll('xInfoGroups', async client => { + const [, reply] = await Promise.all([ + client.xGroupCreate('key', 'group', '$', { + MKSTREAM: true + }), + client.xInfoGroups('key') + ]); + + assert.deepEqual( + reply, + [{ + name: 'group', + consumers: 0, + pending: 0, + 'last-delivered-id': '0-0', + 'entries-read': testUtils.isVersionGreaterThan([7, 0]) ? null : undefined, + lag: testUtils.isVersionGreaterThan([7, 0]) ? 0 : undefined + }] + ); + }, { + client: GLOBAL.SERVERS.OPEN, + cluster: GLOBAL.CLUSTERS.OPEN + }); }); diff --git a/packages/client/lib/commands/XINFO_GROUPS.ts b/packages/client/lib/commands/XINFO_GROUPS.ts index dcf504c8ce7..24661ecde84 100644 --- a/packages/client/lib/commands/XINFO_GROUPS.ts +++ b/packages/client/lib/commands/XINFO_GROUPS.ts @@ -1,25 +1,36 @@ -import { RedisCommandArgument, RedisCommandArguments } from '.'; +import { RedisArgument, ArrayReply, TuplesToMapReply, BlobStringReply, NumberReply, NullReply, UnwrapReply, Resp2Reply, Command } from '../RESP/types'; -export const FIRST_KEY_INDEX = 2; +export type XInfoGroupsReply = ArrayReply, BlobStringReply], + [BlobStringReply<'consumers'>, NumberReply], + [BlobStringReply<'pending'>, NumberReply], + [BlobStringReply<'last-delivered-id'>, NumberReply], + /** added in 7.0 */ + [BlobStringReply<'entries-read'>, NumberReply | NullReply], + /** added in 7.0 */ + [BlobStringReply<'lag'>, NumberReply], +]>>; -export const IS_READ_ONLY = true; - -export function transformArguments(key: RedisCommandArgument): RedisCommandArguments { +export default { + FIRST_KEY_INDEX: 2, + IS_READ_ONLY: true, + transformArguments(key: RedisArgument) { return ['XINFO', 'GROUPS', key]; -} - -type XInfoGroupsReply = Array<{ - name: RedisCommandArgument; - consumers: number; - pending: number; - lastDeliveredId: RedisCommandArgument; -}>; - -export function transformReply(rawReply: Array): XInfoGroupsReply { - return rawReply.map(group => ({ - name: group[1], - consumers: group[3], - pending: group[5], - lastDeliveredId: group[7] - })); -} + }, + transformReply: { + 2: (reply: UnwrapReply>) => { + return reply.map(group => { + const unwrapped = group as unknown as UnwrapReply; + return { + name: unwrapped[1], + consumers: unwrapped[3], + pending: unwrapped[5], + 'last-delivered-id': unwrapped[7], + 'entries-read': unwrapped[9], + lag: unwrapped[11] + }; + }); + }, + 3: undefined as unknown as () => XInfoGroupsReply + } +} as const satisfies Command; diff --git a/packages/client/lib/commands/XINFO_STREAM.spec.ts b/packages/client/lib/commands/XINFO_STREAM.spec.ts index ca8d44f2875..9e6939092e2 100644 --- a/packages/client/lib/commands/XINFO_STREAM.spec.ts +++ b/packages/client/lib/commands/XINFO_STREAM.spec.ts @@ -1,72 +1,39 @@ -import { strict as assert } from 'assert'; +import { strict as assert } from 'node:assert'; import testUtils, { GLOBAL } from '../test-utils'; -import { transformArguments, transformReply } from './XINFO_STREAM'; +import XINFO_STREAM from './XINFO_STREAM'; describe('XINFO STREAM', () => { - it('transformArguments', () => { - assert.deepEqual( - transformArguments('key'), - ['XINFO', 'STREAM', 'key'] - ); - }); - - it('transformReply', () => { - assert.deepEqual( - transformReply([ - 'length', 2, - 'radix-tree-keys', 1, - 'radix-tree-nodes', 2, - 'last-generated-id', '1538385846314-0', - 'groups', 2, - 'first-entry', ['1538385820729-0', ['foo', 'bar']], - 'last-entry', ['1538385846314-0', ['field', 'value']] - ]), - { - length: 2, - radixTreeKeys: 1, - radixTreeNodes: 2, - groups: 2, - lastGeneratedId: '1538385846314-0', - firstEntry: { - id: '1538385820729-0', - message: Object.create(null, { - foo: { - value: 'bar', - configurable: true, - enumerable: true - } - }) - }, - lastEntry: { - id: '1538385846314-0', - message: Object.create(null, { - field: { - value: 'value', - configurable: true, - enumerable: true - } - }) - } - } - ); - }); + it('transformArguments', () => { + assert.deepEqual( + XINFO_STREAM.transformArguments('key'), + ['XINFO', 'STREAM', 'key'] + ); + }); - testUtils.testWithClient('client.xInfoStream', async client => { - await client.xGroupCreate('key', 'group', '$', { - MKSTREAM: true - }); + testUtils.testAll('xInfoStream', async client => { + const [, reply] = await Promise.all([ + client.xGroupCreate('key', 'group', '$', { + MKSTREAM: true + }), + client.xInfoStream('key') + ]); - assert.deepEqual( - await client.xInfoStream('key'), - { - length: 0, - radixTreeKeys: 0, - radixTreeNodes: 1, - groups: 1, - lastGeneratedId: '0-0', - firstEntry: null, - lastEntry: null - } - ); - }, GLOBAL.SERVERS.OPEN); + assert.deepEqual(reply, { + length: 0, + 'radix-tree-keys': 0, + 'radix-tree-nodes': 1, + 'last-generated-id': '0-0', + ...testUtils.isVersionGreaterThan([7, 0]) && { + 'max-deleted-entry-id': '0-0', + 'entries-added': 0, + 'recorded-first-entry-id': '0-0', + }, + groups: 1, + 'first-entry': null, + 'last-entry': null + }); + }, { + client: GLOBAL.SERVERS.OPEN, + cluster: GLOBAL.CLUSTERS.OPEN + }); }); diff --git a/packages/client/lib/commands/XINFO_STREAM.ts b/packages/client/lib/commands/XINFO_STREAM.ts index e9de25be8cb..04721d0ad32 100644 --- a/packages/client/lib/commands/XINFO_STREAM.ts +++ b/packages/client/lib/commands/XINFO_STREAM.ts @@ -1,64 +1,82 @@ -import { RedisCommandArgument, RedisCommandArguments } from '.'; -import { StreamMessageReply, transformTuplesReply } from './generic-transformers'; +import { RedisArgument, TuplesToMapReply, BlobStringReply, NumberReply, NullReply, TuplesReply, ArrayReply, UnwrapReply, Command } from '../RESP/types'; +import { isNullReply, transformTuplesReply } from './generic-transformers'; -export const FIRST_KEY_INDEX = 2; +export type XInfoStreamReply = TuplesToMapReply<[ + [BlobStringReply<'length'>, NumberReply], + [BlobStringReply<'radix-tree-keys'>, NumberReply], + [BlobStringReply<'radix-tree-nodes'>, NumberReply], + [BlobStringReply<'last-generated-id'>, BlobStringReply], + /** added in 7.2 */ + [BlobStringReply<'max-deleted-entry-id'>, BlobStringReply], + /** added in 7.2 */ + [BlobStringReply<'entries-added'>, NumberReply], + /** added in 7.2 */ + [BlobStringReply<'recorded-first-entry-id'>, BlobStringReply], + [BlobStringReply<'groups'>, NumberReply], + [BlobStringReply<'first-entry'>, ReturnType], + [BlobStringReply<'last-entry'>, ReturnType] +]>; -export const IS_READ_ONLY = true; - -export function transformArguments(key: RedisCommandArgument): RedisCommandArguments { +export default { + FIRST_KEY_INDEX: 2, + IS_READ_ONLY: true, + transformArguments(key: RedisArgument) { return ['XINFO', 'STREAM', key]; -} - -interface XInfoStreamReply { - length: number; - radixTreeKeys: number; - radixTreeNodes: number; - groups: number; - lastGeneratedId: RedisCommandArgument; - firstEntry: StreamMessageReply | null; - lastEntry: StreamMessageReply | null; -} - -export function transformReply(rawReply: Array): XInfoStreamReply { - const parsedReply: Partial = {}; + }, + transformReply: { + // TODO: is there a "type safe" way to do it? + 2(reply: any) { + const parsedReply: Partial = {}; - for (let i = 0; i < rawReply.length; i+= 2) { - switch (rawReply[i]) { - case 'length': - parsedReply.length = rawReply[i + 1]; - break; + for (let i = 0; i < reply.length; i += 2) { + switch (reply[i]) { + case 'first-entry': + case 'last-entry': + parsedReply[reply[i] as ('first-entry' | 'last-entry')] = transformEntry(reply[i + 1]) as any; + break; - case 'radix-tree-keys': - parsedReply.radixTreeKeys = rawReply[i + 1]; - break; - - case 'radix-tree-nodes': - parsedReply.radixTreeNodes = rawReply[i + 1]; - break; + default: + parsedReply[reply[i] as keyof typeof parsedReply] = reply[i + 1]; + break; + } + } - case 'groups': - parsedReply.groups = rawReply[i + 1]; - break; + return parsedReply as XInfoStreamReply['DEFAULT']; + }, + 3(reply: any) { + if (reply instanceof Map) { + reply.set( + 'first-entry', + transformEntry(reply.get('first-entry')) + ); + reply.set( + 'last-entry', + transformEntry(reply.get('last-entry')) + ); + } else if (reply instanceof Array) { + reply[17] = transformEntry(reply[17]); + reply[19] = transformEntry(reply[19]); + } else { + reply['first-entry'] = transformEntry(reply['first-entry']); + reply['last-entry'] = transformEntry(reply['last-entry']); + } - case 'last-generated-id': - parsedReply.lastGeneratedId = rawReply[i + 1]; - break; + return reply as XInfoStreamReply; + } + } +} as const satisfies Command; - case 'first-entry': - parsedReply.firstEntry = rawReply[i + 1] ? { - id: rawReply[i + 1][0], - message: transformTuplesReply(rawReply[i + 1][1]) - } : null; - break; +type RawEntry = TuplesReply<[ + id: BlobStringReply, + message: ArrayReply +]> | NullReply; - case 'last-entry': - parsedReply.lastEntry = rawReply[i + 1] ? { - id: rawReply[i + 1][0], - message: transformTuplesReply(rawReply[i + 1][1]) - } : null; - break; - } - } +function transformEntry(entry: RawEntry) { + if (isNullReply(entry)) return entry; - return parsedReply as XInfoStreamReply; + const [id, message] = entry as unknown as UnwrapReply; + return { + id, + message: transformTuplesReply(message) + }; } diff --git a/packages/client/lib/commands/XLEN.spec.ts b/packages/client/lib/commands/XLEN.spec.ts index 178024ba89e..164a6d6f094 100644 --- a/packages/client/lib/commands/XLEN.spec.ts +++ b/packages/client/lib/commands/XLEN.spec.ts @@ -1,19 +1,22 @@ -import { strict as assert } from 'assert'; +import { strict as assert } from 'node:assert'; import testUtils, { GLOBAL } from '../test-utils'; -import { transformArguments } from './XLEN'; +import XLEN from './XLEN'; describe('XLEN', () => { - it('transformArguments', () => { - assert.deepEqual( - transformArguments('key'), - ['XLEN', 'key'] - ); - }); + it('transformArguments', () => { + assert.deepEqual( + XLEN.transformArguments('key'), + ['XLEN', 'key'] + ); + }); - testUtils.testWithClient('client.xLen', async client => { - assert.equal( - await client.xLen('key'), - 0 - ); - }, GLOBAL.SERVERS.OPEN); + testUtils.testAll('xLen', async client => { + assert.equal( + await client.xLen('key'), + 0 + ); + }, { + client: GLOBAL.SERVERS.OPEN, + cluster: GLOBAL.CLUSTERS.OPEN + }); }); diff --git a/packages/client/lib/commands/XLEN.ts b/packages/client/lib/commands/XLEN.ts index fda4192c8a0..d2ed566a190 100644 --- a/packages/client/lib/commands/XLEN.ts +++ b/packages/client/lib/commands/XLEN.ts @@ -1,11 +1,10 @@ -import { RedisCommandArgument, RedisCommandArguments } from '.'; +import { RedisArgument, NumberReply, Command } from '../RESP/types'; -export const FIRST_KEY_INDEX = 1; - -export const IS_READ_ONLY = true; - -export function transformArguments(key: RedisCommandArgument): RedisCommandArguments { +export default { + FIRST_KEY_INDEX: 1, + IS_READ_ONLY: true, + transformArguments(key: RedisArgument) { return ['XLEN', key]; -} - -export declare function transformReply(): number; + }, + transformReply: undefined as unknown as () => NumberReply +} as const satisfies Command; diff --git a/packages/client/lib/commands/XPENDING.spec.ts b/packages/client/lib/commands/XPENDING.spec.ts index b1fef2a217f..e6600cce383 100644 --- a/packages/client/lib/commands/XPENDING.spec.ts +++ b/packages/client/lib/commands/XPENDING.spec.ts @@ -1,62 +1,60 @@ -import { strict as assert } from 'assert'; +import { strict as assert } from 'node:assert'; import testUtils, { GLOBAL } from '../test-utils'; -import { transformArguments } from './XPENDING'; +import XPENDING from './XPENDING'; describe('XPENDING', () => { - describe('transformArguments', () => { - it('transformArguments', () => { - assert.deepEqual( - transformArguments('key', 'group'), - ['XPENDING', 'key', 'group'] - ); - }); + describe('transformArguments', () => { + it('transformArguments', () => { + assert.deepEqual( + XPENDING.transformArguments('key', 'group'), + ['XPENDING', 'key', 'group'] + ); }); + }); - describe('client.xPending', () => { - testUtils.testWithClient('simple', async client => { - await client.xGroupCreate('key', 'group', '$', { - MKSTREAM: true - }); + describe('client.xPending', () => { + testUtils.testWithClient('simple', async client => { + const [, reply] = await Promise.all([ + client.xGroupCreate('key', 'group', '$', { + MKSTREAM: true + }), + client.xPending('key', 'group') + ]); - assert.deepEqual( - await client.xPending('key', 'group'), - { - pending: 0, - firstId: null, - lastId: null, - consumers: null - } - ); - }, GLOBAL.SERVERS.OPEN); + assert.deepEqual(reply, { + pending: 0, + firstId: null, + lastId: null, + consumers: null + }); + }, GLOBAL.SERVERS.OPEN); - testUtils.testWithClient('with consumers', async client => { - const [,, id] = await Promise.all([ - client.xGroupCreate('key', 'group', '$', { - MKSTREAM: true - }), - client.xGroupCreateConsumer('key', 'group', 'consumer'), - client.xAdd('key', '*', { field: 'value' }), - client.xReadGroup('group', 'consumer', { - key: 'key', - id: '>' - }) - ]); + testUtils.testWithClient('with consumers', async client => { + const [, , id, , reply] = await Promise.all([ + client.xGroupCreate('key', 'group', '$', { + MKSTREAM: true + }), + client.xGroupCreateConsumer('key', 'group', 'consumer'), + client.xAdd('key', '*', { field: 'value' }), + client.xReadGroup('group', 'consumer', { + key: 'key', + id: '>' + }), + client.xPending('key', 'group') + ]); - assert.deepEqual( - await client.xPending('key', 'group'), - { - pending: 1, - firstId: id, - lastId: id, - consumers: [{ - name: 'consumer', - deliveriesCounter: 1 - }] - } - ); - }, { - ...GLOBAL.SERVERS.OPEN, - minimumDockerVersion: [6, 2] - }); + assert.deepEqual(reply, { + pending: 1, + firstId: id, + lastId: id, + consumers: [{ + name: 'consumer', + deliveriesCounter: 1 + }] + }); + }, { + ...GLOBAL.SERVERS.OPEN, + minimumDockerVersion: [6, 2] }); + }); }); diff --git a/packages/client/lib/commands/XPENDING.ts b/packages/client/lib/commands/XPENDING.ts index ac56e429410..a6ca4f5a774 100644 --- a/packages/client/lib/commands/XPENDING.ts +++ b/packages/client/lib/commands/XPENDING.ts @@ -1,44 +1,34 @@ -import { RedisCommandArgument, RedisCommandArguments } from '.'; +import { RedisArgument, BlobStringReply, NullReply, ArrayReply, TuplesReply, NumberReply, UnwrapReply, Command } from '../RESP/types'; -export const FIRST_KEY_INDEX = 1; +type XPendingRawReply = TuplesReply<[ + pending: NumberReply, + firstId: BlobStringReply | NullReply, + lastId: BlobStringReply | NullReply, + consumers: ArrayReply> | NullReply +]>; -export const IS_READ_ONLY = true; - -export function transformArguments( - key: RedisCommandArgument, - group: RedisCommandArgument -): RedisCommandArguments { +export default { + FIRST_KEY_INDEX: 1, + IS_READ_ONLY: true, + transformArguments(key: RedisArgument, group: RedisArgument) { return ['XPENDING', key, group]; -} - -type XPendingRawReply = [ - pending: number, - firstId: RedisCommandArgument | null, - lastId: RedisCommandArgument | null, - consumers: Array<[ - name: RedisCommandArgument, - deliveriesCounter: RedisCommandArgument - ]> | null -]; - -interface XPendingReply { - pending: number; - firstId: RedisCommandArgument | null; - lastId: RedisCommandArgument | null; - consumers: Array<{ - name: RedisCommandArgument; - deliveriesCounter: number; - }> | null; -} - -export function transformReply(reply: XPendingRawReply): XPendingReply { + }, + transformReply(reply: UnwrapReply) { + const consumers = reply[3] as unknown as UnwrapReply; return { - pending: reply[0], - firstId: reply[1], - lastId: reply[2], - consumers: reply[3] === null ? null : reply[3].map(([name, deliveriesCounter]) => ({ - name, - deliveriesCounter: Number(deliveriesCounter) - })) - }; -} + pending: reply[0], + firstId: reply[1], + lastId: reply[2], + consumers: consumers === null ? null : consumers.map(consumer => { + const [name, deliveriesCounter] = consumer as unknown as UnwrapReply; + return { + name, + deliveriesCounter: Number(deliveriesCounter) + }; + }) + } + } +} as const satisfies Command; diff --git a/packages/client/lib/commands/XPENDING_RANGE.spec.ts b/packages/client/lib/commands/XPENDING_RANGE.spec.ts index 0b57c704bb0..a66484fd2e6 100644 --- a/packages/client/lib/commands/XPENDING_RANGE.spec.ts +++ b/packages/client/lib/commands/XPENDING_RANGE.spec.ts @@ -1,53 +1,66 @@ -import { strict as assert } from 'assert'; +import { strict as assert } from 'node:assert'; import testUtils, { GLOBAL } from '../test-utils'; -import { transformArguments } from './XPENDING_RANGE'; +import XPENDING_RANGE from './XPENDING_RANGE'; describe('XPENDING RANGE', () => { - describe('transformArguments', () => { - it('simple', () => { - assert.deepEqual( - transformArguments('key', 'group', '-', '+', 1), - ['XPENDING', 'key', 'group', '-', '+', '1'] - ); - }); + describe('transformArguments', () => { + it('simple', () => { + assert.deepEqual( + XPENDING_RANGE.transformArguments('key', 'group', '-', '+', 1), + ['XPENDING', 'key', 'group', '-', '+', '1'] + ); + }); - it('with IDLE', () => { - assert.deepEqual( - transformArguments('key', 'group', '-', '+', 1, { - IDLE: 1, - }), - ['XPENDING', 'key', 'group', 'IDLE', '1', '-', '+', '1'] - ); - }); + it('with IDLE', () => { + assert.deepEqual( + XPENDING_RANGE.transformArguments('key', 'group', '-', '+', 1, { + IDLE: 1, + }), + ['XPENDING', 'key', 'group', 'IDLE', '1', '-', '+', '1'] + ); + }); - it('with consumer', () => { - assert.deepEqual( - transformArguments('key', 'group', '-', '+', 1, { - consumer: 'consumer' - }), - ['XPENDING', 'key', 'group', '-', '+', '1', 'consumer'] - ); - }); + it('with consumer', () => { + assert.deepEqual( + XPENDING_RANGE.transformArguments('key', 'group', '-', '+', 1, { + consumer: 'consumer' + }), + ['XPENDING', 'key', 'group', '-', '+', '1', 'consumer'] + ); + }); - it('with IDLE, consumer', () => { - assert.deepEqual( - transformArguments('key', 'group', '-', '+', 1, { - IDLE: 1, - consumer: 'consumer' - }), - ['XPENDING', 'key', 'group', 'IDLE', '1', '-', '+', '1', 'consumer'] - ); - }); + it('with IDLE, consumer', () => { + assert.deepEqual( + XPENDING_RANGE.transformArguments('key', 'group', '-', '+', 1, { + IDLE: 1, + consumer: 'consumer' + }), + ['XPENDING', 'key', 'group', 'IDLE', '1', '-', '+', '1', 'consumer'] + ); }); + }); - testUtils.testWithClient('client.xPendingRange', async client => { - await client.xGroupCreate('key', 'group', '$', { - MKSTREAM: true - }); + testUtils.testAll('xPendingRange', async client => { + const [, id, , reply] = await Promise.all([ + client.xGroupCreate('key', 'group', '$', { + MKSTREAM: true + }), + client.xAdd('key', '*', { field: 'value' }), + client.xReadGroup('group', 'consumer', { + key: 'key', + id: '>' + }), + client.xPendingRange('key', 'group', '-', '+', 1) + ]); - assert.deepEqual( - await client.xPendingRange('key', 'group', '-', '+', 1), - [] - ); - }, GLOBAL.SERVERS.OPEN); + assert.ok(Array.isArray(reply)); + assert.equal(reply.length, 1); + assert.equal(reply[0].id, id); + assert.equal(reply[0].consumer, 'consumer'); + assert.equal(typeof reply[0].millisecondsSinceLastDelivery, 'number'); + assert.equal(reply[0].deliveriesCounter, 1); + }, { + client: GLOBAL.SERVERS.OPEN, + cluster: GLOBAL.CLUSTERS.OPEN + }); }); diff --git a/packages/client/lib/commands/XPENDING_RANGE.ts b/packages/client/lib/commands/XPENDING_RANGE.ts index 87660de545d..60a28e5172d 100644 --- a/packages/client/lib/commands/XPENDING_RANGE.ts +++ b/packages/client/lib/commands/XPENDING_RANGE.ts @@ -1,56 +1,55 @@ -import { RedisCommandArgument, RedisCommandArguments } from '.'; +import { RedisArgument, ArrayReply, TuplesReply, BlobStringReply, NumberReply, UnwrapReply, Command } from '../RESP/types'; -export const FIRST_KEY_INDEX = 1; - -export const IS_READ_ONLY = true; - -interface XPendingRangeOptions { - IDLE?: number; - consumer?: RedisCommandArgument; +export interface XPendingRangeOptions { + IDLE?: number; + consumer?: RedisArgument; } -export function transformArguments( - key: RedisCommandArgument, - group: RedisCommandArgument, - start: string, - end: string, +type XPendingRangeRawReply = ArrayReply>; + +export default { + FIRST_KEY_INDEX: 1, + IS_READ_ONLY: true, + transformArguments( + key: RedisArgument, + group: RedisArgument, + start: RedisArgument, + end: RedisArgument, count: number, options?: XPendingRangeOptions -): RedisCommandArguments { + ) { const args = ['XPENDING', key, group]; - if (options?.IDLE) { - args.push('IDLE', options.IDLE.toString()); + if (options?.IDLE !== undefined) { + args.push('IDLE', options.IDLE.toString()); } - args.push(start, end, count.toString()); + args.push( + start, + end, + count.toString() + ); if (options?.consumer) { - args.push(options.consumer); + args.push(options.consumer); } return args; -} - -type XPendingRangeRawReply = Array<[ - id: RedisCommandArgument, - consumer: RedisCommandArgument, - millisecondsSinceLastDelivery: number, - deliveriesCounter: number -]>; - -type XPendingRangeReply = Array<{ - id: RedisCommandArgument; - owner: RedisCommandArgument; - millisecondsSinceLastDelivery: number; - deliveriesCounter: number; -}>; - -export function transformReply(reply: XPendingRangeRawReply): XPendingRangeReply { - return reply.map(([id, owner, millisecondsSinceLastDelivery, deliveriesCounter]) => ({ - id, - owner, - millisecondsSinceLastDelivery, - deliveriesCounter - })); -} + }, + transformReply(reply: UnwrapReply) { + return reply.map(pending => { + const unwrapped = pending as unknown as UnwrapReply; + return { + id: unwrapped[0], + consumer: unwrapped[1], + millisecondsSinceLastDelivery: unwrapped[2], + deliveriesCounter: unwrapped[3] + }; + }); + } +} as const satisfies Command; diff --git a/packages/client/lib/commands/XRANGE.spec.ts b/packages/client/lib/commands/XRANGE.spec.ts index 01c713e9595..ebfe271db86 100644 --- a/packages/client/lib/commands/XRANGE.spec.ts +++ b/packages/client/lib/commands/XRANGE.spec.ts @@ -1,30 +1,45 @@ -import { strict as assert } from 'assert'; +import { strict as assert } from 'node:assert'; import testUtils, { GLOBAL } from '../test-utils'; -import { transformArguments } from './XRANGE'; +import XRANGE from './XRANGE'; describe('XRANGE', () => { - describe('transformArguments', () => { - it('simple', () => { - assert.deepEqual( - transformArguments('key', '-', '+'), - ['XRANGE', 'key', '-', '+'] - ); - }); + describe('transformArguments', () => { + it('simple', () => { + assert.deepEqual( + XRANGE.transformArguments('key', '-', '+'), + ['XRANGE', 'key', '-', '+'] + ); + }); + + it('with COUNT', () => { + assert.deepEqual( + XRANGE.transformArguments('key', '-', '+', { + COUNT: 1 + }), + ['XRANGE', 'key', '-', '+', 'COUNT', '1'] + ); + }); + }); - it('with COUNT', () => { - assert.deepEqual( - transformArguments('key', '-', '+', { - COUNT: 1 - }), - ['XRANGE', 'key', '-', '+', 'COUNT', '1'] - ); - }); + testUtils.testAll('xRange', async client => { + const message = Object.create(null, { + field: { + value: 'value', + enumerable: true + } }); - testUtils.testWithClient('client.xRange', async client => { - assert.deepEqual( - await client.xRange('key', '+', '-'), - [] - ); - }, GLOBAL.SERVERS.OPEN); + const [id, reply] = await Promise.all([ + client.xAdd('key', '*', message), + client.xRange('key', '-', '+') + ]); + + assert.deepEqual(reply, [{ + id, + message + }]); + }, { + client: GLOBAL.SERVERS.OPEN, + cluster: GLOBAL.CLUSTERS.OPEN + }); }); diff --git a/packages/client/lib/commands/XRANGE.ts b/packages/client/lib/commands/XRANGE.ts index ae56639f769..fb65160d810 100644 --- a/packages/client/lib/commands/XRANGE.ts +++ b/packages/client/lib/commands/XRANGE.ts @@ -1,26 +1,35 @@ -import { RedisCommandArgument, RedisCommandArguments } from '.'; +import { RedisArgument, ArrayReply, UnwrapReply, Command, TypeMapping } from '../RESP/types'; +import { StreamMessageRawReply, transformStreamMessageReply } from './generic-transformers'; -export const FIRST_KEY_INDEX = 1; - -export const IS_READ_ONLY = true; - -interface XRangeOptions { - COUNT?: number; +export interface XRangeOptions { + COUNT?: number; } -export function transformArguments( - key: RedisCommandArgument, - start: RedisCommandArgument, - end: RedisCommandArgument, - options?: XRangeOptions -): RedisCommandArguments { - const args = ['XRANGE', key, start, end]; +export function transformXRangeArguments( + command: RedisArgument, + key: RedisArgument, + start: RedisArgument, + end: RedisArgument, + options?: XRangeOptions +) { + const args = [command, key, start, end]; - if (options?.COUNT) { - args.push('COUNT', options.COUNT.toString()); - } + if (options?.COUNT) { + args.push('COUNT', options.COUNT.toString()); + } - return args; + return args; } -export { transformStreamMessagesReply as transformReply } from './generic-transformers'; +export default { + FIRST_KEY_INDEX: 1, + IS_READ_ONLY: true, + transformArguments: transformXRangeArguments.bind(undefined, 'XRANGE'), + transformReply( + reply: UnwrapReply>, + preserve?: any, + typeMapping?: TypeMapping + ) { + return reply.map(transformStreamMessageReply.bind(undefined, typeMapping)); + } +} as const satisfies Command; diff --git a/packages/client/lib/commands/XREAD.spec.ts b/packages/client/lib/commands/XREAD.spec.ts index b607f53532e..09784a56e6d 100644 --- a/packages/client/lib/commands/XREAD.spec.ts +++ b/packages/client/lib/commands/XREAD.spec.ts @@ -1,103 +1,134 @@ -import { strict as assert } from 'assert'; +import { strict as assert } from 'node:assert'; import testUtils, { GLOBAL } from '../test-utils'; -import { FIRST_KEY_INDEX, transformArguments } from './XREAD'; +import XREAD from './XREAD'; describe('XREAD', () => { - describe('FIRST_KEY_INDEX', () => { - it('single stream', () => { - assert.equal( - FIRST_KEY_INDEX({ key: 'key', id: '' }), - 'key' - ); - }); + describe('FIRST_KEY_INDEX', () => { + it('single stream', () => { + assert.equal( + XREAD.FIRST_KEY_INDEX({ + key: 'key', + id: '' + }), + 'key' + ); + }); - it('multiple streams', () => { - assert.equal( - FIRST_KEY_INDEX([{ key: '1', id: '' }, { key: '2', id: '' }]), - '1' - ); - }); + it('multiple streams', () => { + assert.equal( + XREAD.FIRST_KEY_INDEX([{ + key: '1', + id: '' + }, { + key: '2', + id: '' + }]), + '1' + ); }); + }); - describe('transformArguments', () => { - it('single stream', () => { - assert.deepEqual( - transformArguments({ - key: 'key', - id: '0' - }), - ['XREAD', 'STREAMS', 'key', '0'] - ); - }); + describe('transformArguments', () => { + it('single stream', () => { + assert.deepEqual( + XREAD.transformArguments({ + key: 'key', + id: '0-0' + }), + ['XREAD', 'STREAMS', 'key', '0-0'] + ); + }); - it('multiple streams', () => { - assert.deepEqual( - transformArguments([{ - key: '1', - id: '0' - }, { - key: '2', - id: '0' - }]), - ['XREAD', 'STREAMS', '1', '2', '0', '0'] - ); - }); + it('multiple streams', () => { + assert.deepEqual( + XREAD.transformArguments([{ + key: '1', + id: '0-0' + }, { + key: '2', + id: '0-0' + }]), + ['XREAD', 'STREAMS', '1', '2', '0-0', '0-0'] + ); + }); + + it('with COUNT', () => { + assert.deepEqual( + XREAD.transformArguments({ + key: 'key', + id: '0-0' + }, { + COUNT: 1 + }), + ['XREAD', 'COUNT', '1', 'STREAMS', 'key', '0-0'] + ); + }); + + it('with BLOCK', () => { + assert.deepEqual( + XREAD.transformArguments({ + key: 'key', + id: '0-0' + }, { + BLOCK: 0 + }), + ['XREAD', 'BLOCK', '0', 'STREAMS', 'key', '0-0'] + ); + }); + + it('with COUNT, BLOCK', () => { + assert.deepEqual( + XREAD.transformArguments({ + key: 'key', + id: '0-0' + }, { + COUNT: 1, + BLOCK: 0 + }), + ['XREAD', 'COUNT', '1', 'BLOCK', '0', 'STREAMS', 'key', '0-0'] + ); + }); + }); - it('with COUNT', () => { - assert.deepEqual( - transformArguments({ - key: 'key', - id: '0' - }, { - COUNT: 1 - }), - ['XREAD', 'COUNT', '1', 'STREAMS', 'key', '0'] - ); - }); - it('with BLOCK', () => { - assert.deepEqual( - transformArguments({ - key: 'key', - id: '0' - }, { - BLOCK: 0 - }), - ['XREAD', 'BLOCK', '0', 'STREAMS', 'key', '0'] - ); - }); + testUtils.testAll('client.xRead', async client => { + const message = { field: 'value' }, + [id, reply] = await Promise.all([ + client.xAdd('key', '*', message), + client.xRead({ + key: 'key', + id: '0-0' + }), + ]) - it('with COUNT, BLOCK', () => { - assert.deepEqual( - transformArguments({ - key: 'key', - id: '0' - }, { - COUNT: 1, - BLOCK: 0 - }), - ['XREAD', 'COUNT', '1', 'BLOCK', '0', 'STREAMS', 'key', '0'] - ); - }); + // FUTURE resp3 compatible + const obj = Object.assign(Object.create(null), { + 'key': [{ + id: id, + message: Object.create(null, { + field: { + value: 'value', + configurable: true, + enumerable: true + } + }) + }] }); - testUtils.testWithClient('client.xRead', async client => { - assert.equal( - await client.xRead({ - key: 'key', - id: '0' - }), - null - ); - }, GLOBAL.SERVERS.OPEN); + // v4 compatible + const expected = [{ + name: 'key', + messages: [{ + id: id, + message: Object.assign(Object.create(null), { + field: 'value' + }) + }] + }]; - testUtils.testWithCluster('cluster.xRead', async cluster => { - assert.equal( - await cluster.xRead({ - key: 'key', - id: '0' - }), - null - ); - }, GLOBAL.CLUSTERS.OPEN); + assert.deepStrictEqual(reply, expected); + }, { + client: GLOBAL.SERVERS.OPEN, + cluster: GLOBAL.CLUSTERS.OPEN + }); }); diff --git a/packages/client/lib/commands/XREAD.ts b/packages/client/lib/commands/XREAD.ts index e5f85dbe7fe..97679376c19 100644 --- a/packages/client/lib/commands/XREAD.ts +++ b/packages/client/lib/commands/XREAD.ts @@ -1,46 +1,58 @@ -import { RedisCommandArgument, RedisCommandArguments } from '.'; +import { Command, RedisArgument, ReplyUnion } from '../RESP/types'; +import { transformStreamsMessagesReplyResp2 } from './generic-transformers'; -export const FIRST_KEY_INDEX = (streams: Array | XReadStream): RedisCommandArgument => { - return Array.isArray(streams) ? streams[0].key : streams.key; -}; +export interface XReadStream { + key: RedisArgument; + id: RedisArgument; +} -export const IS_READ_ONLY = true; +export type XReadStreams = Array | XReadStream; -interface XReadStream { - key: RedisCommandArgument; - id: RedisCommandArgument; +export function pushXReadStreams(args: Array, streams: XReadStreams) { + args.push('STREAMS'); + + if (Array.isArray(streams)) { + const keysStart = args.length, + idsStart = keysStart + streams.length; + for (let i = 0; i < streams.length; i++) { + const stream = streams[i]; + args[keysStart + i] = stream.key; + args[idsStart + i] = stream.id; + } + } else { + args.push(streams.key, streams.id); + } } -interface XReadOptions { - COUNT?: number; - BLOCK?: number; +export interface XReadOptions { + COUNT?: number; + BLOCK?: number; } -export function transformArguments( - streams: Array | XReadStream, - options?: XReadOptions -): RedisCommandArguments { - const args: RedisCommandArguments = ['XREAD']; +export default { + FIRST_KEY_INDEX(streams: XReadStreams) { + return Array.isArray(streams) ? streams[0].key : streams.key; + }, + IS_READ_ONLY: true, + transformArguments(streams: XReadStreams, options?: XReadOptions) { + const args: Array = ['XREAD']; if (options?.COUNT) { - args.push('COUNT', options.COUNT.toString()); + args.push('COUNT', options.COUNT.toString()); } - if (typeof options?.BLOCK === 'number') { - args.push('BLOCK', options.BLOCK.toString()); + if (options?.BLOCK !== undefined) { + args.push('BLOCK', options.BLOCK.toString()); } - args.push('STREAMS'); - - const streamsArray = Array.isArray(streams) ? streams : [streams], - argsLength = args.length; - for (let i = 0; i < streamsArray.length; i++) { - const stream = streamsArray[i]; - args[argsLength + i] = stream.key; - args[argsLength + streamsArray.length + i] = stream.id; - } + pushXReadStreams(args, streams); return args; -} + }, + transformReply: { + 2: transformStreamsMessagesReplyResp2, + 3: undefined as unknown as () => ReplyUnion + }, + unstableResp3: true +} as const satisfies Command; -export { transformStreamsMessagesReply as transformReply } from './generic-transformers'; diff --git a/packages/client/lib/commands/XREADGROUP.spec.ts b/packages/client/lib/commands/XREADGROUP.spec.ts index fa196d504ad..004a48ddbe3 100644 --- a/packages/client/lib/commands/XREADGROUP.spec.ts +++ b/packages/client/lib/commands/XREADGROUP.spec.ts @@ -1,153 +1,157 @@ -import { strict as assert } from 'assert'; +import { strict as assert } from 'node:assert'; import testUtils, { GLOBAL } from '../test-utils'; -import { FIRST_KEY_INDEX, transformArguments } from './XREADGROUP'; +import XREADGROUP from './XREADGROUP'; describe('XREADGROUP', () => { - describe('FIRST_KEY_INDEX', () => { - it('single stream', () => { - assert.equal( - FIRST_KEY_INDEX('', '', { key: 'key', id: '' }), - 'key' - ); - }); + describe('FIRST_KEY_INDEX', () => { + it('single stream', () => { + assert.equal( + XREADGROUP.FIRST_KEY_INDEX('', '', { key: 'key', id: '' }), + 'key' + ); + }); - it('multiple streams', () => { - assert.equal( - FIRST_KEY_INDEX('', '', [{ key: '1', id: '' }, { key: '2', id: '' }]), - '1' - ); - }); + it('multiple streams', () => { + assert.equal( + XREADGROUP.FIRST_KEY_INDEX('', '', [{ key: '1', id: '' }, { key: '2', id: '' }]), + '1' + ); }); + }); - describe('transformArguments', () => { - it('single stream', () => { - assert.deepEqual( - transformArguments('group', 'consumer', { - key: 'key', - id: '0' - }), - ['XREADGROUP', 'GROUP', 'group', 'consumer', 'STREAMS', 'key', '0'] - ); - }); + describe('transformArguments', () => { + it('single stream', () => { + assert.deepEqual( + XREADGROUP.transformArguments('group', 'consumer', { + key: 'key', + id: '0-0' + }), + ['XREADGROUP', 'GROUP', 'group', 'consumer', 'STREAMS', 'key', '0-0'] + ); + }); - it('multiple streams', () => { - assert.deepEqual( - transformArguments('group', 'consumer', [{ - key: '1', - id: '0' - }, { - key: '2', - id: '0' - }]), - ['XREADGROUP', 'GROUP', 'group', 'consumer', 'STREAMS', '1', '2', '0', '0'] - ); - }); + it('multiple streams', () => { + assert.deepEqual( + XREADGROUP.transformArguments('group', 'consumer', [{ + key: '1', + id: '0-0' + }, { + key: '2', + id: '0-0' + }]), + ['XREADGROUP', 'GROUP', 'group', 'consumer', 'STREAMS', '1', '2', '0-0', '0-0'] + ); + }); - it('with COUNT', () => { - assert.deepEqual( - transformArguments('group', 'consumer', { - key: 'key', - id: '0' - }, { - COUNT: 1 - }), - ['XREADGROUP', 'GROUP', 'group', 'consumer', 'COUNT', '1', 'STREAMS', 'key', '0'] - ); - }); + it('with COUNT', () => { + assert.deepEqual( + XREADGROUP.transformArguments('group', 'consumer', { + key: 'key', + id: '0-0' + }, { + COUNT: 1 + }), + ['XREADGROUP', 'GROUP', 'group', 'consumer', 'COUNT', '1', 'STREAMS', 'key', '0-0'] + ); + }); - it('with BLOCK', () => { - assert.deepEqual( - transformArguments('group', 'consumer', { - key: 'key', - id: '0' - }, { - BLOCK: 0 - }), - ['XREADGROUP', 'GROUP', 'group', 'consumer', 'BLOCK', '0', 'STREAMS', 'key', '0'] - ); - }); + it('with BLOCK', () => { + assert.deepEqual( + XREADGROUP.transformArguments('group', 'consumer', { + key: 'key', + id: '0-0' + }, { + BLOCK: 0 + }), + ['XREADGROUP', 'GROUP', 'group', 'consumer', 'BLOCK', '0', 'STREAMS', 'key', '0-0'] + ); + }); - it('with NOACK', () => { - assert.deepEqual( - transformArguments('group', 'consumer', { - key: 'key', - id: '0' - }, { - NOACK: true - }), - ['XREADGROUP', 'GROUP', 'group', 'consumer', 'NOACK', 'STREAMS', 'key', '0'] - ); - }); + it('with NOACK', () => { + assert.deepEqual( + XREADGROUP.transformArguments('group', 'consumer', { + key: 'key', + id: '0-0' + }, { + NOACK: true + }), + ['XREADGROUP', 'GROUP', 'group', 'consumer', 'NOACK', 'STREAMS', 'key', '0-0'] + ); + }); - it('with COUNT, BLOCK, NOACK', () => { - assert.deepEqual( - transformArguments('group', 'consumer', { - key: 'key', - id: '0' - }, { - COUNT: 1, - BLOCK: 0, - NOACK: true - }), - ['XREADGROUP', 'GROUP', 'group', 'consumer', 'COUNT', '1', 'BLOCK', '0', 'NOACK', 'STREAMS', 'key', '0'] - ); - }); + it('with COUNT, BLOCK, NOACK', () => { + assert.deepEqual( + XREADGROUP.transformArguments('group', 'consumer', { + key: 'key', + id: '0-0' + }, { + COUNT: 1, + BLOCK: 0, + NOACK: true + }), + ['XREADGROUP', 'GROUP', 'group', 'consumer', 'COUNT', '1', 'BLOCK', '0', 'NOACK', 'STREAMS', 'key', '0-0'] + ); }); + }); + + testUtils.testAll('xReadGroup - null', async client => { + const [, readGroupReply] = await Promise.all([ + client.xGroupCreate('key', 'group', '$', { + MKSTREAM: true + }), + client.xReadGroup('group', 'consumer', { + key: 'key', + id: '>' + }) + ]); - describe('client.xReadGroup', () => { - testUtils.testWithClient('null', async client => { - const [, readGroupReply] = await Promise.all([ - client.xGroupCreate('key', 'group', '$', { - MKSTREAM: true - }), - client.xReadGroup('group', 'consumer', { - key: 'key', - id: '>' - }) - ]); + assert.equal(readGroupReply, null); + }, { + client: GLOBAL.SERVERS.OPEN, + cluster: GLOBAL.CLUSTERS.OPEN + }); - assert.equal(readGroupReply, null); - }, GLOBAL.SERVERS.OPEN); + testUtils.testAll('xReadGroup - with a message', async client => { + const [, id, readGroupReply] = await Promise.all([ + client.xGroupCreate('key', 'group', '$', { + MKSTREAM: true + }), + client.xAdd('key', '*', { field: 'value' }), + client.xReadGroup('group', 'consumer', { + key: 'key', + id: '>' + }) + ]); - testUtils.testWithClient('with a message', async client => { - const [, id, readGroupReply] = await Promise.all([ - client.xGroupCreate('key', 'group', '$', { - MKSTREAM: true - }), - client.xAdd('key', '*', { field: 'value' }), - client.xReadGroup('group', 'consumer', { - key: 'key', - id: '>' - }) - ]); - assert.deepEqual(readGroupReply, [{ - name: 'key', - messages: [{ - id, - message: Object.create(null, { - field: { - value: 'value', - configurable: true, - enumerable: true - } - }) - }] - }]); - }, GLOBAL.SERVERS.OPEN); + // FUTURE resp3 compatible + const obj = Object.assign(Object.create(null), { + 'key': [{ + id: id, + message: Object.create(null, { + field: { + value: 'value', + configurable: true, + enumerable: true + } + }) + }] }); - testUtils.testWithCluster('cluster.xReadGroup', async cluster => { - const [, readGroupReply] = await Promise.all([ - cluster.xGroupCreate('key', 'group', '$', { - MKSTREAM: true - }), - cluster.xReadGroup('group', 'consumer', { - key: 'key', - id: '>' - }) - ]); + // v4 compatible + const expected = [{ + name: 'key', + messages: [{ + id: id, + message: Object.assign(Object.create(null), { + field: 'value' + }) + }] + }]; - assert.equal(readGroupReply, null); - }, GLOBAL.CLUSTERS.OPEN); + assert.deepStrictEqual(readGroupReply, expected); + }, { + client: GLOBAL.SERVERS.OPEN, + cluster: GLOBAL.CLUSTERS.OPEN + }); }); diff --git a/packages/client/lib/commands/XREADGROUP.ts b/packages/client/lib/commands/XREADGROUP.ts index e90e698a2ad..296480f9e3a 100644 --- a/packages/client/lib/commands/XREADGROUP.ts +++ b/packages/client/lib/commands/XREADGROUP.ts @@ -1,57 +1,49 @@ -import { RedisCommandArgument, RedisCommandArguments } from '.'; - -export interface XReadGroupStream { - key: RedisCommandArgument; - id: RedisCommandArgument; -} +import { Command, RedisArgument, ReplyUnion } from '../RESP/types'; +import { transformStreamsMessagesReplyResp2 } from './generic-transformers'; +import XREAD, { XReadStreams, pushXReadStreams } from './XREAD'; export interface XReadGroupOptions { - COUNT?: number; - BLOCK?: number; - NOACK?: true; + COUNT?: number; + BLOCK?: number; + NOACK?: boolean; } -export const FIRST_KEY_INDEX = ( - _group: RedisCommandArgument, - _consumer: RedisCommandArgument, - streams: Array | XReadGroupStream -): RedisCommandArgument => { - return Array.isArray(streams) ? streams[0].key : streams.key; -}; - -export const IS_READ_ONLY = true; - -export function transformArguments( - group: RedisCommandArgument, - consumer: RedisCommandArgument, - streams: Array | XReadGroupStream, +export default { + FIRST_KEY_INDEX( + _group: RedisArgument, + _consumer: RedisArgument, + streams: XReadStreams + ) { + return XREAD.FIRST_KEY_INDEX(streams); + }, + IS_READ_ONLY: true, + transformArguments( + group: RedisArgument, + consumer: RedisArgument, + streams: XReadStreams, options?: XReadGroupOptions -): RedisCommandArguments { + ) { const args = ['XREADGROUP', 'GROUP', group, consumer]; - if (options?.COUNT) { - args.push('COUNT', options.COUNT.toString()); + if (options?.COUNT !== undefined) { + args.push('COUNT', options.COUNT.toString()); } - if (typeof options?.BLOCK === 'number') { - args.push('BLOCK', options.BLOCK.toString()); + if (options?.BLOCK !== undefined) { + args.push('BLOCK', options.BLOCK.toString()); } if (options?.NOACK) { - args.push('NOACK'); + args.push('NOACK'); } - args.push('STREAMS'); - - const streamsArray = Array.isArray(streams) ? streams : [streams], - argsLength = args.length; - for (let i = 0; i < streamsArray.length; i++) { - const stream = streamsArray[i]; - args[argsLength + i] = stream.key; - args[argsLength + streamsArray.length + i] = stream.id; - } + pushXReadStreams(args, streams); return args; -} - -export { transformStreamsMessagesReply as transformReply } from './generic-transformers'; + }, + transformReply: { + 2: transformStreamsMessagesReplyResp2, + 3: undefined as unknown as () => ReplyUnion + }, + unstableResp3: true, +} as const satisfies Command; diff --git a/packages/client/lib/commands/XREVRANGE.spec.ts b/packages/client/lib/commands/XREVRANGE.spec.ts index fd6e1a3adfe..c9f3043af38 100644 --- a/packages/client/lib/commands/XREVRANGE.spec.ts +++ b/packages/client/lib/commands/XREVRANGE.spec.ts @@ -1,30 +1,45 @@ -import { strict as assert } from 'assert'; +import { strict as assert } from 'node:assert'; import testUtils, { GLOBAL } from '../test-utils'; -import { transformArguments } from './XREVRANGE'; +import XREVRANGE from './XREVRANGE'; describe('XREVRANGE', () => { - describe('transformArguments', () => { - it('simple', () => { - assert.deepEqual( - transformArguments('key', '-', '+'), - ['XREVRANGE', 'key', '-', '+'] - ); - }); + describe('transformArguments', () => { + it('simple', () => { + assert.deepEqual( + XREVRANGE.transformArguments('key', '-', '+'), + ['XREVRANGE', 'key', '-', '+'] + ); + }); + + it('with COUNT', () => { + assert.deepEqual( + XREVRANGE.transformArguments('key', '-', '+', { + COUNT: 1 + }), + ['XREVRANGE', 'key', '-', '+', 'COUNT', '1'] + ); + }); + }); - it('with COUNT', () => { - assert.deepEqual( - transformArguments('key', '-', '+', { - COUNT: 1 - }), - ['XREVRANGE', 'key', '-', '+', 'COUNT', '1'] - ); - }); + testUtils.testAll('xRevRange', async client => { + const message = Object.create(null, { + field: { + value: 'value', + enumerable: true + } }); - testUtils.testWithClient('client.xRevRange', async client => { - assert.deepEqual( - await client.xRevRange('key', '+', '-'), - [] - ); - }, GLOBAL.SERVERS.OPEN); + const [id, reply] = await Promise.all([ + client.xAdd('key', '*', message), + client.xRange('key', '-', '+') + ]); + + assert.deepEqual(reply, [{ + id, + message + }]); + }, { + client: GLOBAL.SERVERS.OPEN, + cluster: GLOBAL.CLUSTERS.OPEN + }); }); diff --git a/packages/client/lib/commands/XREVRANGE.ts b/packages/client/lib/commands/XREVRANGE.ts index 96bbeba83ce..86e4d8abc9e 100644 --- a/packages/client/lib/commands/XREVRANGE.ts +++ b/packages/client/lib/commands/XREVRANGE.ts @@ -1,26 +1,13 @@ -import { RedisCommandArgument, RedisCommandArguments } from '.'; +import { Command } from '../RESP/types'; +import XRANGE, { transformXRangeArguments } from './XRANGE'; -export const FIRST_KEY_INDEX = 1; - -export const IS_READ_ONLY = true; - -interface XRangeRevOptions { - COUNT?: number; -} - -export function transformArguments( - key: RedisCommandArgument, - start: RedisCommandArgument, - end: RedisCommandArgument, - options?: XRangeRevOptions -): RedisCommandArguments { - const args = ['XREVRANGE', key, start, end]; - - if (options?.COUNT) { - args.push('COUNT', options.COUNT.toString()); - } - - return args; +export interface XRevRangeOptions { + COUNT?: number; } -export { transformStreamMessagesReply as transformReply } from './generic-transformers'; +export default { + FIRST_KEY_INDEX: XRANGE.FIRST_KEY_INDEX, + IS_READ_ONLY: XRANGE.IS_READ_ONLY, + transformArguments: transformXRangeArguments.bind(undefined, 'XREVRANGE'), + transformReply: XRANGE.transformReply +} as const satisfies Command; diff --git a/packages/client/lib/commands/XSETID.spec.ts b/packages/client/lib/commands/XSETID.spec.ts index e69de29bb2d..5ebbc943816 100644 --- a/packages/client/lib/commands/XSETID.spec.ts +++ b/packages/client/lib/commands/XSETID.spec.ts @@ -0,0 +1,46 @@ +import { strict as assert } from 'node:assert'; +import testUtils, { GLOBAL } from '../test-utils'; +import XSETID from './XSETID'; + +describe('XSETID', () => { + describe('transformArguments', () => { + it('simple', () => { + assert.deepEqual( + XSETID.transformArguments('key', '0-0'), + ['XSETID', 'key', '0-0'] + ); + }); + + it('with ENTRIESADDED', () => { + assert.deepEqual( + XSETID.transformArguments('key', '0-0', { + ENTRIESADDED: 1 + }), + ['XSETID', 'key', '0-0', 'ENTRIESADDED', '1'] + ); + }); + + it('with MAXDELETEDID', () => { + assert.deepEqual( + XSETID.transformArguments('key', '0-0', { + MAXDELETEDID: '1-1' + }), + ['XSETID', 'key', '0-0', 'MAXDELETEDID', '1-1'] + ); + }); + }); + + testUtils.testAll('xSetId', async client => { + const id = await client.xAdd('key', '*', { + field: 'value' + }); + + assert.equal( + await client.xSetId('key', id), + 'OK' + ); + }, { + client: GLOBAL.SERVERS.OPEN, + cluster: GLOBAL.CLUSTERS.OPEN + }); +}); diff --git a/packages/client/lib/commands/XSETID.ts b/packages/client/lib/commands/XSETID.ts index 76acc7ebab4..a2ac8af0011 100644 --- a/packages/client/lib/commands/XSETID.ts +++ b/packages/client/lib/commands/XSETID.ts @@ -1,28 +1,31 @@ -import { RedisCommandArgument, RedisCommandArguments } from '.'; - -export const FIRST_KEY_INDEX = 1; - -interface XSetIdOptions { - ENTRIESADDED?: number; - MAXDELETEDID?: RedisCommandArgument; +import { RedisArgument, SimpleStringReply, Command } from '../RESP/types'; +export interface XSetIdOptions { + /** added in 7.0 */ + ENTRIESADDED?: number; + /** added in 7.0 */ + MAXDELETEDID?: RedisArgument; } -export function transformArguments( - key: RedisCommandArgument, - lastId: RedisCommandArgument, +export default { + FIRST_KEY_INDEX: 1, + IS_READ_ONLY: false, + transformArguments( + key: RedisArgument, + lastId: RedisArgument, options?: XSetIdOptions -): RedisCommandArguments { + ) { const args = ['XSETID', key, lastId]; if (options?.ENTRIESADDED) { - args.push('ENTRIESADDED', options.ENTRIESADDED.toString()); + args.push('ENTRIESADDED', options.ENTRIESADDED.toString()); } if (options?.MAXDELETEDID) { - args.push('MAXDELETEDID', options.MAXDELETEDID); + args.push('MAXDELETEDID', options.MAXDELETEDID); } return args; -} + }, + transformReply: undefined as unknown as () => SimpleStringReply<'OK'> +} as const satisfies Command; -export declare function transformReply(): 'OK'; diff --git a/packages/client/lib/commands/XTRIM.spec.ts b/packages/client/lib/commands/XTRIM.spec.ts index a8f8078eb28..a5a2fdf23c5 100644 --- a/packages/client/lib/commands/XTRIM.spec.ts +++ b/packages/client/lib/commands/XTRIM.spec.ts @@ -1,49 +1,52 @@ -import { strict as assert } from 'assert'; +import { strict as assert } from 'node:assert'; import testUtils, { GLOBAL } from '../test-utils'; -import { transformArguments } from './XTRIM'; +import XTRIM from './XTRIM'; describe('XTRIM', () => { - describe('transformArguments', () => { - it('simple', () => { - assert.deepEqual( - transformArguments('key', 'MAXLEN', 1), - ['XTRIM', 'key', 'MAXLEN', '1'] - ); - }); + describe('transformArguments', () => { + it('simple', () => { + assert.deepEqual( + XTRIM.transformArguments('key', 'MAXLEN', 1), + ['XTRIM', 'key', 'MAXLEN', '1'] + ); + }); - it('with strategyModifier', () => { - assert.deepEqual( - transformArguments('key', 'MAXLEN', 1, { - strategyModifier: '=' - }), - ['XTRIM', 'key', 'MAXLEN', '=', '1'] - ); - }); + it('with strategyModifier', () => { + assert.deepEqual( + XTRIM.transformArguments('key', 'MAXLEN', 1, { + strategyModifier: '=' + }), + ['XTRIM', 'key', 'MAXLEN', '=', '1'] + ); + }); - it('with LIMIT', () => { - assert.deepEqual( - transformArguments('key', 'MAXLEN', 1, { - LIMIT: 1 - }), - ['XTRIM', 'key', 'MAXLEN', '1', 'LIMIT', '1'] - ); - }); + it('with LIMIT', () => { + assert.deepEqual( + XTRIM.transformArguments('key', 'MAXLEN', 1, { + LIMIT: 1 + }), + ['XTRIM', 'key', 'MAXLEN', '1', 'LIMIT', '1'] + ); + }); - it('with strategyModifier, LIMIT', () => { - assert.deepEqual( - transformArguments('key', 'MAXLEN', 1, { - strategyModifier: '=', - LIMIT: 1 - }), - ['XTRIM', 'key', 'MAXLEN', '=', '1', 'LIMIT', '1'] - ); - }); + it('with strategyModifier, LIMIT', () => { + assert.deepEqual( + XTRIM.transformArguments('key', 'MAXLEN', 1, { + strategyModifier: '=', + LIMIT: 1 + }), + ['XTRIM', 'key', 'MAXLEN', '=', '1', 'LIMIT', '1'] + ); }); + }); - testUtils.testWithClient('client.xTrim', async client => { - assert.equal( - await client.xTrim('key', 'MAXLEN', 1), - 0 - ); - }, GLOBAL.SERVERS.OPEN); + testUtils.testAll('xTrim', async client => { + assert.equal( + await client.xTrim('key', 'MAXLEN', 1), + 0 + ); + }, { + client: GLOBAL.SERVERS.OPEN, + cluster: GLOBAL.CLUSTERS.OPEN, + }); }); diff --git a/packages/client/lib/commands/XTRIM.ts b/packages/client/lib/commands/XTRIM.ts index 15b934c56ef..0512323a32a 100644 --- a/packages/client/lib/commands/XTRIM.ts +++ b/packages/client/lib/commands/XTRIM.ts @@ -1,31 +1,33 @@ -import { RedisCommandArgument, RedisCommandArguments } from '.'; +import { NumberReply, Command, RedisArgument } from '../RESP/types'; -export const FIRST_KEY_INDEX = 1; - -interface XTrimOptions { - strategyModifier?: '=' | '~'; - LIMIT?: number; +export interface XTrimOptions { + strategyModifier?: '=' | '~'; + /** added in 6.2 */ + LIMIT?: number; } -export function transformArguments( - key: RedisCommandArgument, +export default { + FIRST_KEY_INDEX: 1, + IS_READ_ONLY: false, + transformArguments( + key: RedisArgument, strategy: 'MAXLEN' | 'MINID', threshold: number, options?: XTrimOptions -): RedisCommandArguments { + ) { const args = ['XTRIM', key, strategy]; if (options?.strategyModifier) { - args.push(options.strategyModifier); + args.push(options.strategyModifier); } args.push(threshold.toString()); if (options?.LIMIT) { - args.push('LIMIT', options.LIMIT.toString()); + args.push('LIMIT', options.LIMIT.toString()); } return args; -} - -export declare function transformReply(): number; + }, + transformReply: undefined as unknown as () => NumberReply +} as const satisfies Command; diff --git a/packages/client/lib/commands/ZADD.spec.ts b/packages/client/lib/commands/ZADD.spec.ts index 4f497bdca90..5f64cb13b92 100644 --- a/packages/client/lib/commands/ZADD.spec.ts +++ b/packages/client/lib/commands/ZADD.spec.ts @@ -1,127 +1,145 @@ -import { strict as assert } from 'assert'; +import { strict as assert } from 'node:assert'; import testUtils, { GLOBAL } from '../test-utils'; -import { transformArguments } from './ZADD'; +import ZADD from './ZADD'; describe('ZADD', () => { - describe('transformArguments', () => { - it('single member', () => { - assert.deepEqual( - transformArguments('key', { - value: '1', - score: 1 - }), - ['ZADD', 'key', '1', '1'] - ); - }); + describe('transformArguments', () => { + it('single member', () => { + assert.deepEqual( + ZADD.transformArguments('key', { + value: '1', + score: 1 + }), + ['ZADD', 'key', '1', '1'] + ); + }); + + it('multiple members', () => { + assert.deepEqual( + ZADD.transformArguments('key', [{ + value: '1', + score: 1 + }, { + value: '2', + score: 2 + }]), + ['ZADD', 'key', '1', '1', '2', '2'] + ); + }); - it('multiple members', () => { - assert.deepEqual( - transformArguments('key', [{ - value: '1', - score: 1 - }, { - value: '2', - score: 2 - }]), - ['ZADD', 'key', '1', '1', '2', '2'] - ); - }); + describe('with condition', () => { + it('condition property', () => { + assert.deepEqual( + ZADD.transformArguments('key', { + value: '1', + score: 1 + }, { + condition: 'NX' + }), + ['ZADD', 'key', 'NX', '1', '1'] + ); + }); - it('with NX', () => { - assert.deepEqual( - transformArguments('key', { - value: '1', - score: 1 - }, { - NX: true - }), - ['ZADD', 'key', 'NX', '1', '1'] - ); - }); + it('with NX (backwards compatibility)', () => { + assert.deepEqual( + ZADD.transformArguments('key', { + value: '1', + score: 1 + }, { + NX: true + }), + ['ZADD', 'key', 'NX', '1', '1'] + ); + }); - it('with XX', () => { - assert.deepEqual( - transformArguments('key', { - value: '1', - score: 1 - }, { - XX: true - }), - ['ZADD', 'key', 'XX', '1', '1'] - ); - }); + it('with XX (backwards compatibility)', () => { + assert.deepEqual( + ZADD.transformArguments('key', { + value: '1', + score: 1 + }, { + XX: true + }), + ['ZADD', 'key', 'XX', '1', '1'] + ); + }); + }); - it('with GT', () => { - assert.deepEqual( - transformArguments('key', { - value: '1', - score: 1 - }, { - GT: true - }), - ['ZADD', 'key', 'GT', '1', '1'] - ); - }); + describe('with comparison', () => { + it('with LT', () => { + assert.deepEqual( + ZADD.transformArguments('key', { + value: '1', + score: 1 + }, { + comparison: 'LT' + }), + ['ZADD', 'key', 'LT', '1', '1'] + ); + }); - it('with LT', () => { - assert.deepEqual( - transformArguments('key', { - value: '1', - score: 1 - }, { - LT: true - }), - ['ZADD', 'key', 'LT', '1', '1'] - ); - }); + it('with LT (backwards compatibility)', () => { + assert.deepEqual( + ZADD.transformArguments('key', { + value: '1', + score: 1 + }, { + LT: true + }), + ['ZADD', 'key', 'LT', '1', '1'] + ); + }); - it('with CH', () => { - assert.deepEqual( - transformArguments('key', { - value: '1', - score: 1 - }, { - CH: true - }), - ['ZADD', 'key', 'CH', '1', '1'] - ); - }); + it('with GT (backwards compatibility)', () => { + assert.deepEqual( + ZADD.transformArguments('key', { + value: '1', + score: 1 + }, { + GT: true + }), + ['ZADD', 'key', 'GT', '1', '1'] + ); + }); + }); - it('with INCR', () => { - assert.deepEqual( - transformArguments('key', { - value: '1', - score: 1 - }, { - INCR: true - }), - ['ZADD', 'key', 'INCR', '1', '1'] - ); - }); + it('with CH', () => { + assert.deepEqual( + ZADD.transformArguments('key', { + value: '1', + score: 1 + }, { + CH: true + }), + ['ZADD', 'key', 'CH', '1', '1'] + ); + }); - it('with XX, GT, CH, INCR', () => { - assert.deepEqual( - transformArguments('key', { - value: '1', - score: 1 - }, { - XX: true, - GT: true, - CH: true, - INCR: true - }), - ['ZADD', 'key', 'XX', 'GT', 'CH', 'INCR', '1', '1'] - ); - }); + it('with condition, comparison, CH', () => { + assert.deepEqual( + ZADD.transformArguments('key', { + value: '1', + score: 1 + }, { + condition: 'XX', + comparison: 'LT', + CH: true + }), + ['ZADD', 'key', 'XX', 'LT', 'CH', '1', '1'] + ); }); + }); - testUtils.testWithClient('client.zAdd', async client => { - assert.equal( - await client.zAdd('key', { - value: '1', - score: 1 - }), - 1 - ); - }, GLOBAL.SERVERS.OPEN); + testUtils.testAll('zAdd', async client => { + assert.equal( + await client.zAdd('key', { + value: 'a', + score: 1 + }), + 1 + ); + }, { + client: GLOBAL.SERVERS.OPEN, + cluster: GLOBAL.CLUSTERS.OPEN + }); }); diff --git a/packages/client/lib/commands/ZADD.ts b/packages/client/lib/commands/ZADD.ts index 9ac67d59cce..0c5602bf988 100644 --- a/packages/client/lib/commands/ZADD.ts +++ b/packages/client/lib/commands/ZADD.ts @@ -1,71 +1,82 @@ -import { RedisCommandArgument, RedisCommandArguments } from '.'; -import { transformNumberInfinityArgument, ZMember } from './generic-transformers'; - -export const FIRST_KEY_INDEX = 1; - -interface NX { - NX?: true; -} - -interface XX { - XX?: true; -} - -interface LT { - LT?: true; -} - -interface GT { - GT?: true; -} - -interface CH { - CH?: true; +import { RedisArgument, Command } from '../RESP/types'; +import { SortedSetMember, transformDoubleArgument, transformDoubleReply } from './generic-transformers'; + +export interface ZAddOptions { + condition?: 'NX' | 'XX'; + /** + * @deprecated Use `{ condition: 'NX' }` instead. + */ + NX?: boolean; + /** + * @deprecated Use `{ condition: 'XX' }` instead. + */ + XX?: boolean; + comparison?: 'LT' | 'GT'; + /** + * @deprecated Use `{ comparison: 'LT' }` instead. + */ + LT?: boolean; + /** + * @deprecated Use `{ comparison: 'GT' }` instead. + */ + GT?: boolean; + CH?: boolean; } -interface INCR { - INCR?: true; -} - -type ZAddOptions = (NX | (XX & LT & GT)) & CH & INCR; - -export function transformArguments( - key: RedisCommandArgument, - members: ZMember | Array, +export default { + FIRST_KEY_INDEX: 1, + transformArguments( + key: RedisArgument, + members: SortedSetMember | Array, options?: ZAddOptions -): RedisCommandArguments { + ) { const args = ['ZADD', key]; - if ((options)?.NX) { - args.push('NX'); - } else { - if ((options)?.XX) { - args.push('XX'); - } - - if ((options)?.GT) { - args.push('GT'); - } else if ((options)?.LT) { - args.push('LT'); - } + if (options?.condition) { + args.push(options.condition); + } else if (options?.NX) { + args.push('NX'); + } else if (options?.XX) { + args.push('XX'); + } + + if (options?.comparison) { + args.push(options.comparison); + } else if (options?.LT) { + args.push('LT'); + } else if (options?.GT) { + args.push('GT'); } - if ((options)?.CH) { - args.push('CH'); + if (options?.CH) { + args.push('CH'); } - if ((options)?.INCR) { - args.push('INCR'); - } - - for (const { score, value } of (Array.isArray(members) ? members : [members])) { - args.push( - transformNumberInfinityArgument(score), - value - ); - } + pushMembers(args, members); return args; + }, + transformReply: transformDoubleReply +} as const satisfies Command; + +export function pushMembers( + args: Array, + members: SortedSetMember | Array) { + if (Array.isArray(members)) { + for (const member of members) { + pushMember(args, member); + } + } else { + pushMember(args, members); + } } -export { transformNumberInfinityReply as transformReply } from './generic-transformers'; +function pushMember( + args: Array, + member: SortedSetMember +) { + args.push( + transformDoubleArgument(member.score), + member.value + ); +} diff --git a/packages/client/lib/commands/ZADD_INCR.spec.ts b/packages/client/lib/commands/ZADD_INCR.spec.ts new file mode 100644 index 00000000000..c6ffcb796f3 --- /dev/null +++ b/packages/client/lib/commands/ZADD_INCR.spec.ts @@ -0,0 +1,93 @@ +import { strict as assert } from 'node:assert'; +import testUtils, { GLOBAL } from '../test-utils'; +import ZADD_INCR from './ZADD_INCR'; + +describe('ZADD INCR', () => { + describe('transformArguments', () => { + it('single member', () => { + assert.deepEqual( + ZADD_INCR.transformArguments('key', { + value: '1', + score: 1 + }), + ['ZADD', 'key', 'INCR', '1', '1'] + ); + }); + + it('multiple members', () => { + assert.deepEqual( + ZADD_INCR.transformArguments('key', [{ + value: '1', + score: 1 + }, { + value: '2', + score: 2 + }]), + ['ZADD', 'key', 'INCR', '1', '1', '2', '2'] + ); + }); + + it('with condition', () => { + assert.deepEqual( + ZADD_INCR.transformArguments('key', { + value: '1', + score: 1 + }, { + condition: 'NX' + }), + ['ZADD', 'key', 'NX', 'INCR', '1', '1'] + ); + }); + + it('with comparison', () => { + assert.deepEqual( + ZADD_INCR.transformArguments('key', { + value: '1', + score: 1 + }, { + comparison: 'LT' + }), + ['ZADD', 'key', 'LT', 'INCR', '1', '1'] + ); + }); + + it('with CH', () => { + assert.deepEqual( + ZADD_INCR.transformArguments('key', { + value: '1', + score: 1 + }, { + CH: true + }), + ['ZADD', 'key', 'CH', 'INCR', '1', '1'] + ); + }); + + it('with condition, comparison, CH', () => { + assert.deepEqual( + ZADD_INCR.transformArguments('key', { + value: '1', + score: 1 + }, { + condition: 'XX', + comparison: 'LT', + CH: true + }), + ['ZADD', 'key', 'XX', 'LT', 'CH', 'INCR', '1', '1'] + ); + }); + }); + + testUtils.testAll('zAddIncr', async client => { + assert.equal( + await client.zAddIncr('key', { + value: 'a', + score: 1 + }), + 1 + ); + }, { + client: GLOBAL.SERVERS.OPEN, + cluster: GLOBAL.CLUSTERS.OPEN + }); +}); diff --git a/packages/client/lib/commands/ZADD_INCR.ts b/packages/client/lib/commands/ZADD_INCR.ts new file mode 100644 index 00000000000..8fb10721674 --- /dev/null +++ b/packages/client/lib/commands/ZADD_INCR.ts @@ -0,0 +1,39 @@ +import { RedisArgument, Command } from '../RESP/types'; +import { pushMembers } from './ZADD'; +import { SortedSetMember, transformNullableDoubleReply } from './generic-transformers'; + +export interface ZAddOptions { + condition?: 'NX' | 'XX'; + comparison?: 'LT' | 'GT'; + CH?: boolean; +} + +export default { + FIRST_KEY_INDEX: 1, + transformArguments( + key: RedisArgument, + members: SortedSetMember | Array, + options?: ZAddOptions + ) { + const args = ['ZADD', key]; + + if (options?.condition) { + args.push(options.condition); + } + + if (options?.comparison) { + args.push(options.comparison); + } + + if (options?.CH) { + args.push('CH'); + } + + args.push('INCR'); + + pushMembers(args, members); + + return args; + }, + transformReply: transformNullableDoubleReply +} as const satisfies Command; diff --git a/packages/client/lib/commands/ZCARD.spec.ts b/packages/client/lib/commands/ZCARD.spec.ts index 2e90da772b6..ff4bb881fb7 100644 --- a/packages/client/lib/commands/ZCARD.spec.ts +++ b/packages/client/lib/commands/ZCARD.spec.ts @@ -1,19 +1,22 @@ -import { strict as assert } from 'assert'; +import { strict as assert } from 'node:assert'; import testUtils, { GLOBAL } from '../test-utils'; -import { transformArguments } from './ZCARD'; +import ZCARD from './ZCARD'; describe('ZCARD', () => { - it('transformArguments', () => { - assert.deepEqual( - transformArguments('key'), - ['ZCARD', 'key'] - ); - }); + it('transformArguments', () => { + assert.deepEqual( + ZCARD.transformArguments('key'), + ['ZCARD', 'key'] + ); + }); - testUtils.testWithClient('client.zCard', async client => { - assert.equal( - await client.zCard('key'), - 0 - ); - }, GLOBAL.SERVERS.OPEN); + testUtils.testAll('zCard', async client => { + assert.equal( + await client.zCard('key'), + 0 + ); + }, { + client: GLOBAL.SERVERS.OPEN, + cluster: GLOBAL.CLUSTERS.OPEN + }); }); diff --git a/packages/client/lib/commands/ZCARD.ts b/packages/client/lib/commands/ZCARD.ts index f208c181369..c11cb69db3c 100644 --- a/packages/client/lib/commands/ZCARD.ts +++ b/packages/client/lib/commands/ZCARD.ts @@ -1,11 +1,10 @@ -import { RedisCommandArgument, RedisCommandArguments } from '.'; +import { RedisArgument, NumberReply, Command } from '../RESP/types'; -export const FIRST_KEY_INDEX = 1; - -export const IS_READ_ONLY = true; - -export function transformArguments(key: RedisCommandArgument): RedisCommandArguments { +export default { + FIRST_KEY_INDEX: 1, + IS_READ_ONLY: true, + transformArguments(key: RedisArgument) { return ['ZCARD', key]; -} - -export declare function transformReply(): number; + }, + transformReply: undefined as unknown as () => NumberReply +} as const satisfies Command; diff --git a/packages/client/lib/commands/ZCOUNT.spec.ts b/packages/client/lib/commands/ZCOUNT.spec.ts index e185ed3cd45..357f24bd796 100644 --- a/packages/client/lib/commands/ZCOUNT.spec.ts +++ b/packages/client/lib/commands/ZCOUNT.spec.ts @@ -1,19 +1,22 @@ -import { strict as assert } from 'assert'; +import { strict as assert } from 'node:assert'; import testUtils, { GLOBAL } from '../test-utils'; -import { transformArguments } from './ZCOUNT'; +import ZCOUNT from './ZCOUNT'; describe('ZCOUNT', () => { - it('transformArguments', () => { - assert.deepEqual( - transformArguments('key', 0, 1), - ['ZCOUNT', 'key', '0', '1'] - ); - }); + it('transformArguments', () => { + assert.deepEqual( + ZCOUNT.transformArguments('key', 0, 1), + ['ZCOUNT', 'key', '0', '1'] + ); + }); - testUtils.testWithClient('client.zCount', async client => { - assert.equal( - await client.zCount('key', 0, 1), - 0 - ); - }, GLOBAL.SERVERS.OPEN); + testUtils.testAll('zCount', async client => { + assert.equal( + await client.zCount('key', 0, 1), + 0 + ); + }, { + client: GLOBAL.SERVERS.OPEN, + cluster: GLOBAL.SERVERS.OPEN + }); }); diff --git a/packages/client/lib/commands/ZCOUNT.ts b/packages/client/lib/commands/ZCOUNT.ts index f9700cc9099..187a316b15a 100644 --- a/packages/client/lib/commands/ZCOUNT.ts +++ b/packages/client/lib/commands/ZCOUNT.ts @@ -1,21 +1,20 @@ -import { RedisCommandArgument, RedisCommandArguments } from '.'; -import { transformStringNumberInfinityArgument } from './generic-transformers'; +import { RedisArgument, NumberReply, Command } from '../RESP/types'; +import { transformStringDoubleArgument } from './generic-transformers'; -export const FIRST_KEY_INDEX = 1; - -export const IS_READ_ONLY = true; - -export function transformArguments( - key: RedisCommandArgument, - min: RedisCommandArgument | number, - max: RedisCommandArgument | number -): RedisCommandArguments { +export default { + FIRST_KEY_INDEX: 1, + IS_READ_ONLY: true, + transformArguments( + key: RedisArgument, + min: number | RedisArgument, + max: number | RedisArgument + ) { return [ - 'ZCOUNT', - key, - transformStringNumberInfinityArgument(min), - transformStringNumberInfinityArgument(max) + 'ZCOUNT', + key, + transformStringDoubleArgument(min), + transformStringDoubleArgument(max) ]; -} - -export declare function transformReply(): number; + }, + transformReply: undefined as unknown as () => NumberReply +} as const satisfies Command; diff --git a/packages/client/lib/commands/ZDIFF.spec.ts b/packages/client/lib/commands/ZDIFF.spec.ts index 8bb1a101f53..a285190398c 100644 --- a/packages/client/lib/commands/ZDIFF.spec.ts +++ b/packages/client/lib/commands/ZDIFF.spec.ts @@ -1,30 +1,33 @@ -import { strict as assert } from 'assert'; +import { strict as assert } from 'node:assert'; import testUtils, { GLOBAL } from '../test-utils'; -import { transformArguments } from './ZDIFF'; +import ZDIFF from './ZDIFF'; describe('ZDIFF', () => { - testUtils.isVersionGreaterThanHook([6, 2]); + testUtils.isVersionGreaterThanHook([6, 2]); - describe('transformArguments', () => { - it('string', () => { - assert.deepEqual( - transformArguments('key'), - ['ZDIFF', '1', 'key'] - ); - }); + describe('transformArguments', () => { + it('string', () => { + assert.deepEqual( + ZDIFF.transformArguments('key'), + ['ZDIFF', '1', 'key'] + ); + }); - it('array', () => { - assert.deepEqual( - transformArguments(['1', '2']), - ['ZDIFF', '2', '1', '2'] - ); - }); + it('array', () => { + assert.deepEqual( + ZDIFF.transformArguments(['1', '2']), + ['ZDIFF', '2', '1', '2'] + ); }); + }); - testUtils.testWithClient('client.zDiff', async client => { - assert.deepEqual( - await client.zDiff('key'), - [] - ); - }, GLOBAL.SERVERS.OPEN); + testUtils.testAll('zDiff', async client => { + assert.deepEqual( + await client.zDiff('key'), + [] + ); + }, { + client: GLOBAL.SERVERS.OPEN, + cluster: GLOBAL.CLUSTERS.OPEN + }); }); diff --git a/packages/client/lib/commands/ZDIFF.ts b/packages/client/lib/commands/ZDIFF.ts index f3818a139f1..f16c8717cdb 100644 --- a/packages/client/lib/commands/ZDIFF.ts +++ b/packages/client/lib/commands/ZDIFF.ts @@ -1,14 +1,11 @@ -import { RedisCommandArgument, RedisCommandArguments } from '.'; -import { pushVerdictArgument } from './generic-transformers'; - -export const FIRST_KEY_INDEX = 2; - -export const IS_READ_ONLY = true; - -export function transformArguments( - keys: Array | RedisCommandArgument -): RedisCommandArguments { - return pushVerdictArgument(['ZDIFF'], keys); -} - -export declare function transformReply(): Array; +import { ArrayReply, BlobStringReply, Command } from '../RESP/types'; +import { RedisVariadicArgument, pushVariadicArgument } from './generic-transformers'; + +export default { + FIRST_KEY_INDEX: 2, + IS_READ_ONLY: true, + transformArguments(keys: RedisVariadicArgument) { + return pushVariadicArgument(['ZDIFF'], keys); + }, + transformReply: undefined as unknown as () => ArrayReply +} as const satisfies Command; diff --git a/packages/client/lib/commands/ZDIFFSTORE.spec.ts b/packages/client/lib/commands/ZDIFFSTORE.spec.ts index c63902b2666..1bd779302bf 100644 --- a/packages/client/lib/commands/ZDIFFSTORE.spec.ts +++ b/packages/client/lib/commands/ZDIFFSTORE.spec.ts @@ -1,30 +1,33 @@ -import { strict as assert } from 'assert'; +import { strict as assert } from 'node:assert'; import testUtils, { GLOBAL } from '../test-utils'; -import { transformArguments } from './ZDIFFSTORE'; +import ZDIFFSTORE from './ZDIFFSTORE'; describe('ZDIFFSTORE', () => { - testUtils.isVersionGreaterThanHook([6, 2]); + testUtils.isVersionGreaterThanHook([6, 2]); - describe('transformArguments', () => { - it('string', () => { - assert.deepEqual( - transformArguments('destination', 'key'), - ['ZDIFFSTORE', 'destination', '1', 'key'] - ); - }); + describe('transformArguments', () => { + it('string', () => { + assert.deepEqual( + ZDIFFSTORE.transformArguments('destination', 'key'), + ['ZDIFFSTORE', 'destination', '1', 'key'] + ); + }); - it('array', () => { - assert.deepEqual( - transformArguments('destination', ['1', '2']), - ['ZDIFFSTORE', 'destination', '2', '1', '2'] - ); - }); + it('array', () => { + assert.deepEqual( + ZDIFFSTORE.transformArguments('destination', ['1', '2']), + ['ZDIFFSTORE', 'destination', '2', '1', '2'] + ); }); + }); - testUtils.testWithClient('client.zDiffStore', async client => { - assert.equal( - await client.zDiffStore('destination', 'key'), - 0 - ); - }, GLOBAL.SERVERS.OPEN); + testUtils.testAll('zDiffStore', async client => { + assert.equal( + await client.zDiffStore('{tag}destination', '{tag}key'), + 0 + ); + }, { + client: GLOBAL.SERVERS.OPEN, + cluster: GLOBAL.CLUSTERS.OPEN + }); }); diff --git a/packages/client/lib/commands/ZDIFFSTORE.ts b/packages/client/lib/commands/ZDIFFSTORE.ts index 3b9af9511c5..e4614a1cb14 100644 --- a/packages/client/lib/commands/ZDIFFSTORE.ts +++ b/packages/client/lib/commands/ZDIFFSTORE.ts @@ -1,13 +1,14 @@ -import { RedisCommandArgument, RedisCommandArguments } from '.'; -import { pushVerdictArgument } from './generic-transformers'; +import { RedisArgument, NumberReply, Command } from '../RESP/types'; +import { RedisVariadicArgument, pushVariadicArgument } from './generic-transformers'; -export const FIRST_KEY_INDEX = 1; - -export function transformArguments( - destination: RedisCommandArgument, - keys: Array | RedisCommandArgument -): RedisCommandArguments { - return pushVerdictArgument(['ZDIFFSTORE', destination], keys); -} - -export declare function transformReply(): number; +export default { + FIRST_KEY_INDEX: 2, + IS_READ_ONLY: true, + transformArguments( + destination: RedisArgument, + inputKeys: RedisVariadicArgument + ) { + return pushVariadicArgument(['ZDIFFSTORE', destination], inputKeys); + }, + transformReply: undefined as unknown as () => NumberReply +} as const satisfies Command; diff --git a/packages/client/lib/commands/ZDIFF_WITHSCORES.spec.ts b/packages/client/lib/commands/ZDIFF_WITHSCORES.spec.ts index 3b9cb725aaa..4fcd1f978a3 100644 --- a/packages/client/lib/commands/ZDIFF_WITHSCORES.spec.ts +++ b/packages/client/lib/commands/ZDIFF_WITHSCORES.spec.ts @@ -1,30 +1,33 @@ -import { strict as assert } from 'assert'; +import { strict as assert } from 'node:assert'; import testUtils, { GLOBAL } from '../test-utils'; -import { transformArguments } from './ZDIFF_WITHSCORES'; +import ZDIFF_WITHSCORES from './ZDIFF_WITHSCORES'; describe('ZDIFF WITHSCORES', () => { - testUtils.isVersionGreaterThanHook([6, 2]); + testUtils.isVersionGreaterThanHook([6, 2]); - describe('transformArguments', () => { - it('string', () => { - assert.deepEqual( - transformArguments('key'), - ['ZDIFF', '1', 'key', 'WITHSCORES'] - ); - }); + describe('transformArguments', () => { + it('string', () => { + assert.deepEqual( + ZDIFF_WITHSCORES.transformArguments('key'), + ['ZDIFF', '1', 'key', 'WITHSCORES'] + ); + }); - it('array', () => { - assert.deepEqual( - transformArguments(['1', '2']), - ['ZDIFF', '2', '1', '2', 'WITHSCORES'] - ); - }); + it('array', () => { + assert.deepEqual( + ZDIFF_WITHSCORES.transformArguments(['1', '2']), + ['ZDIFF', '2', '1', '2', 'WITHSCORES'] + ); }); + }); - testUtils.testWithClient('client.zDiffWithScores', async client => { - assert.deepEqual( - await client.zDiffWithScores('key'), - [] - ); - }, GLOBAL.SERVERS.OPEN); + testUtils.testAll('zDiffWithScores', async client => { + assert.deepEqual( + await client.zDiffWithScores('key'), + [] + ); + }, { + client: GLOBAL.SERVERS.OPEN, + cluster: GLOBAL.CLUSTERS.OPEN + }); }); diff --git a/packages/client/lib/commands/ZDIFF_WITHSCORES.ts b/packages/client/lib/commands/ZDIFF_WITHSCORES.ts index 9caa13c9f8b..f971e828574 100644 --- a/packages/client/lib/commands/ZDIFF_WITHSCORES.ts +++ b/packages/client/lib/commands/ZDIFF_WITHSCORES.ts @@ -1,13 +1,14 @@ -import { RedisCommandArguments } from '.'; -import { transformArguments as transformZDiffArguments } from './ZDIFF'; +import { Command } from '../RESP/types'; +import ZDIFF from './ZDIFF'; +import { transformSortedSetReply } from './generic-transformers'; -export { FIRST_KEY_INDEX, IS_READ_ONLY } from './ZDIFF'; - -export function transformArguments(...args: Parameters): RedisCommandArguments { - return [ - ...transformZDiffArguments(...args), - 'WITHSCORES' - ]; -} - -export { transformSortedSetWithScoresReply as transformReply } from './generic-transformers'; +export default { + FIRST_KEY_INDEX: ZDIFF.FIRST_KEY_INDEX, + IS_READ_ONLY: ZDIFF.IS_READ_ONLY, + transformArguments(...args: Parameters) { + const redisArgs = ZDIFF.transformArguments(...args); + redisArgs.push('WITHSCORES'); + return redisArgs; + }, + transformReply: transformSortedSetReply +} as const satisfies Command; diff --git a/packages/client/lib/commands/ZINCRBY.spec.ts b/packages/client/lib/commands/ZINCRBY.spec.ts index bf2a34b0965..fea3c7a2f71 100644 --- a/packages/client/lib/commands/ZINCRBY.spec.ts +++ b/packages/client/lib/commands/ZINCRBY.spec.ts @@ -1,19 +1,22 @@ -import { strict as assert } from 'assert'; +import { strict as assert } from 'node:assert'; import testUtils, { GLOBAL } from '../test-utils'; -import { transformArguments } from './ZINCRBY'; +import ZINCRBY from './ZINCRBY'; describe('ZINCRBY', () => { - it('transformArguments', () => { - assert.deepEqual( - transformArguments('key', 1, 'member'), - ['ZINCRBY', 'key', '1', 'member'] - ); - }); + it('transformArguments', () => { + assert.deepEqual( + ZINCRBY.transformArguments('key', 1, 'member'), + ['ZINCRBY', 'key', '1', 'member'] + ); + }); - testUtils.testWithClient('client.zIncrBy', async client => { - assert.equal( - await client.zIncrBy('destination', 1, 'member'), - 1 - ); - }, GLOBAL.SERVERS.OPEN); + testUtils.testAll('zIncrBy', async client => { + assert.equal( + await client.zIncrBy('destination', 1, 'member'), + 1 + ); + }, { + client: GLOBAL.SERVERS.OPEN, + cluster: GLOBAL.CLUSTERS.OPEN + }); }); diff --git a/packages/client/lib/commands/ZINCRBY.ts b/packages/client/lib/commands/ZINCRBY.ts index 68d89351391..d9e43845016 100644 --- a/packages/client/lib/commands/ZINCRBY.ts +++ b/packages/client/lib/commands/ZINCRBY.ts @@ -1,19 +1,19 @@ -import { RedisCommandArgument, RedisCommandArguments } from '.'; -import { transformNumberInfinityArgument } from './generic-transformers'; +import { RedisArgument, Command } from '../RESP/types'; +import { transformDoubleArgument, transformDoubleReply } from './generic-transformers'; -export const FIRST_KEY_INDEX = 1; - -export function transformArguments( - key: RedisCommandArgument, +export default { + FIRST_KEY_INDEX: 1, + transformArguments( + key: RedisArgument, increment: number, - member: RedisCommandArgument -): RedisCommandArguments { + member: RedisArgument + ) { return [ - 'ZINCRBY', - key, - transformNumberInfinityArgument(increment), - member + 'ZINCRBY', + key, + transformDoubleArgument(increment), + member ]; -} - -export { transformNumberInfinityReply as transformReply } from './generic-transformers'; + }, + transformReply: transformDoubleReply +} as const satisfies Command; diff --git a/packages/client/lib/commands/ZINTER.spec.ts b/packages/client/lib/commands/ZINTER.spec.ts index 4d2d86c8869..0d678ce1151 100644 --- a/packages/client/lib/commands/ZINTER.spec.ts +++ b/packages/client/lib/commands/ZINTER.spec.ts @@ -1,58 +1,65 @@ -import { strict as assert } from 'assert'; +import { strict as assert } from 'node:assert'; import testUtils, { GLOBAL } from '../test-utils'; -import { transformArguments } from './ZINTER'; +import ZINTER from './ZINTER'; describe('ZINTER', () => { - testUtils.isVersionGreaterThanHook([6, 2]); + testUtils.isVersionGreaterThanHook([6, 2]); - describe('transformArguments', () => { - it('key (string)', () => { - assert.deepEqual( - transformArguments('key'), - ['ZINTER', '1', 'key'] - ); - }); + describe('transformArguments', () => { + it('key (string)', () => { + assert.deepEqual( + ZINTER.transformArguments('key'), + ['ZINTER', '1', 'key'] + ); + }); - it('keys (array)', () => { - assert.deepEqual( - transformArguments(['1', '2']), - ['ZINTER', '2', '1', '2'] - ); - }); + it('keys (Array)', () => { + assert.deepEqual( + ZINTER.transformArguments(['1', '2']), + ['ZINTER', '2', '1', '2'] + ); + }); - it('with WEIGHTS', () => { - assert.deepEqual( - transformArguments('key', { - WEIGHTS: [1] - }), - ['ZINTER', '1', 'key', 'WEIGHTS', '1'] - ); - }); + it('key & weight', () => { + assert.deepEqual( + ZINTER.transformArguments({ + key: 'key', + weight: 1 + }), + ['ZINTER', '1', 'key', 'WEIGHTS', '1'] + ); + }); - it('with AGGREGATE', () => { - assert.deepEqual( - transformArguments('key', { - AGGREGATE: 'SUM' - }), - ['ZINTER', '1', 'key', 'AGGREGATE', 'SUM'] - ); - }); + it('keys & weights', () => { + assert.deepEqual( + ZINTER.transformArguments([{ + key: 'a', + weight: 1 + }, { + key: 'b', + weight: 2 + }]), + ['ZINTER', '2', 'a', 'b', 'WEIGHTS', '1', '2'] + ); + }); - it('with WEIGHTS, AGGREGATE', () => { - assert.deepEqual( - transformArguments('key', { - WEIGHTS: [1], - AGGREGATE: 'SUM' - }), - ['ZINTER', '1', 'key', 'WEIGHTS', '1', 'AGGREGATE', 'SUM'] - ); - }); + it('with AGGREGATE', () => { + assert.deepEqual( + ZINTER.transformArguments('key', { + AGGREGATE: 'SUM' + }), + ['ZINTER', '1', 'key', 'AGGREGATE', 'SUM'] + ); }); + }); - testUtils.testWithClient('client.zInter', async client => { - assert.deepEqual( - await client.zInter('key'), - [] - ); - }, GLOBAL.SERVERS.OPEN); + testUtils.testAll('zInter', async client => { + assert.deepEqual( + await client.zInter('key'), + [] + ); + }, { + client: GLOBAL.SERVERS.OPEN, + cluster: GLOBAL.CLUSTERS.OPEN + }); }); diff --git a/packages/client/lib/commands/ZINTER.ts b/packages/client/lib/commands/ZINTER.ts index 88d7f801882..392c3a96c65 100644 --- a/packages/client/lib/commands/ZINTER.ts +++ b/packages/client/lib/commands/ZINTER.ts @@ -1,33 +1,39 @@ -import { RedisCommandArgument, RedisCommandArguments } from '.'; -import { pushVerdictArgument } from './generic-transformers'; +import { RedisArgument, ArrayReply, BlobStringReply, Command } from '../RESP/types'; +import { ZKeys, pushZKeysArguments } from './generic-transformers'; -export const FIRST_KEY_INDEX = 2; +export type ZInterKeyAndWeight = { + key: RedisArgument; + weight: number; +}; -export const IS_READ_ONLY = true; +export type ZInterKeys = T | [T, ...Array]; -interface ZInterOptions { - WEIGHTS?: Array; - AGGREGATE?: 'SUM' | 'MIN' | 'MAX'; +export interface ZInterOptions { + AGGREGATE?: 'SUM' | 'MIN' | 'MAX'; } -export function transformArguments( - keys: Array | RedisCommandArgument, - options?: ZInterOptions -): RedisCommandArguments { - const args = pushVerdictArgument(['ZINTER'], keys); - - if (options?.WEIGHTS) { - args.push( - 'WEIGHTS', - ...options.WEIGHTS.map(weight => weight.toString()) - ); - } +export function pushZInterArguments( + args: Array, + keys: ZKeys, + options?: ZInterOptions +) { + args = pushZKeysArguments(args, keys); - if (options?.AGGREGATE) { - args.push('AGGREGATE', options.AGGREGATE); - } + if (options?.AGGREGATE) { + args.push('AGGREGATE', options.AGGREGATE); + } - return args; + return args; } -export declare function transformReply(): Array; +export default { + FIRST_KEY_INDEX: 2, + IS_READ_ONLY: true, + transformArguments( + keys: ZInterKeys | ZInterKeys, + options?: ZInterOptions + ) { + return pushZInterArguments(['ZINTER'], keys, options); + }, + transformReply: undefined as unknown as () => ArrayReply +} as const satisfies Command; diff --git a/packages/client/lib/commands/ZINTERCARD.spec.ts b/packages/client/lib/commands/ZINTERCARD.spec.ts index 492c1a90433..312e2825ff4 100644 --- a/packages/client/lib/commands/ZINTERCARD.spec.ts +++ b/packages/client/lib/commands/ZINTERCARD.spec.ts @@ -1,30 +1,44 @@ -import { strict as assert } from 'assert'; +import { strict as assert } from 'node:assert'; import testUtils, { GLOBAL } from '../test-utils'; -import { transformArguments } from './ZINTERCARD'; +import ZINTERCARD from './ZINTERCARD'; describe('ZINTERCARD', () => { - testUtils.isVersionGreaterThanHook([7]); + testUtils.isVersionGreaterThanHook([7]); - describe('transformArguments', () => { - it('simple', () => { - assert.deepEqual( - transformArguments(['1', '2']), - ['ZINTERCARD', '2', '1', '2'] - ); - }); - - it('with limit', () => { - assert.deepEqual( - transformArguments(['1', '2'], 1), - ['ZINTERCARD', '2', '1', '2', 'LIMIT', '1'] - ); - }); + describe('transformArguments', () => { + it('simple', () => { + assert.deepEqual( + ZINTERCARD.transformArguments(['1', '2']), + ['ZINTERCARD', '2', '1', '2'] + ); }); - testUtils.testWithClient('client.zInterCard', async client => { + describe('with LIMIT', () => { + it('plain number (backwards compatibility)', () => { + assert.deepEqual( + ZINTERCARD.transformArguments(['1', '2'], 1), + ['ZINTERCARD', '2', '1', '2', 'LIMIT', '1'] + ); + }); + + it('{ LIMIT: number }', () => { assert.deepEqual( - await client.zInterCard('key'), - 0 + ZINTERCARD.transformArguments(['1', '2'], { + LIMIT: 1 + }), + ['ZINTERCARD', '2', '1', '2', 'LIMIT', '1'] ); - }, GLOBAL.SERVERS.OPEN); + }); + }); + }); + + testUtils.testAll('zInterCard', async client => { + assert.deepEqual( + await client.zInterCard('key'), + 0 + ); + }, { + client: GLOBAL.SERVERS.OPEN, + cluster: GLOBAL.CLUSTERS.OPEN + }); }); diff --git a/packages/client/lib/commands/ZINTERCARD.ts b/packages/client/lib/commands/ZINTERCARD.ts index ff45ab2aa01..9953d88eecc 100644 --- a/packages/client/lib/commands/ZINTERCARD.ts +++ b/packages/client/lib/commands/ZINTERCARD.ts @@ -1,21 +1,27 @@ -import { RedisCommandArgument, RedisCommandArguments } from '.'; -import { pushVerdictArgument } from './generic-transformers'; +import { NumberReply, Command } from '../RESP/types'; +import { RedisVariadicArgument, pushVariadicArgument } from './generic-transformers'; -export const FIRST_KEY_INDEX = 2; - -export const IS_READ_ONLY = true; +export interface ZInterCardOptions { + LIMIT?: number; +} -export function transformArguments( - keys: Array | RedisCommandArgument, - limit?: number -): RedisCommandArguments { - const args = pushVerdictArgument(['ZINTERCARD'], keys); +export default { + FIRST_KEY_INDEX: 2, + IS_READ_ONLY: true, + transformArguments( + keys: RedisVariadicArgument, + options?: ZInterCardOptions['LIMIT'] | ZInterCardOptions + ) { + const args = pushVariadicArgument(['ZINTERCARD'], keys); - if (limit) { - args.push('LIMIT', limit.toString()); + // backwards compatibility + if (typeof options === 'number') { + args.push('LIMIT', options.toString()); + } else if (options?.LIMIT) { + args.push('LIMIT', options.LIMIT.toString()); } return args; -} - -export declare function transformReply(): number; + }, + transformReply: undefined as unknown as () => NumberReply +} as const satisfies Command; diff --git a/packages/client/lib/commands/ZINTERSTORE.spec.ts b/packages/client/lib/commands/ZINTERSTORE.spec.ts index 224961f0786..27914ca0327 100644 --- a/packages/client/lib/commands/ZINTERSTORE.spec.ts +++ b/packages/client/lib/commands/ZINTERSTORE.spec.ts @@ -1,56 +1,63 @@ -import { strict as assert } from 'assert'; +import { strict as assert } from 'node:assert'; import testUtils, { GLOBAL } from '../test-utils'; -import { transformArguments } from './ZINTERSTORE'; +import ZINTERSTORE from './ZINTERSTORE'; describe('ZINTERSTORE', () => { - describe('transformArguments', () => { - it('key (string)', () => { - assert.deepEqual( - transformArguments('destination', 'key'), - ['ZINTERSTORE', 'destination', '1', 'key'] - ); - }); + describe('transformArguments', () => { + it('key (string)', () => { + assert.deepEqual( + ZINTERSTORE.transformArguments('destination', 'source'), + ['ZINTERSTORE', 'destination', '1', 'source'] + ); + }); - it('keys (array)', () => { - assert.deepEqual( - transformArguments('destination', ['1', '2']), - ['ZINTERSTORE', 'destination', '2', '1', '2'] - ); - }); + it('keys (Array)', () => { + assert.deepEqual( + ZINTERSTORE.transformArguments('destination', ['1', '2']), + ['ZINTERSTORE', 'destination', '2', '1', '2'] + ); + }); - it('with WEIGHTS', () => { - assert.deepEqual( - transformArguments('destination', 'key', { - WEIGHTS: [1] - }), - ['ZINTERSTORE', 'destination', '1', 'key', 'WEIGHTS', '1'] - ); - }); + it('key & weight', () => { + assert.deepEqual( + ZINTERSTORE.transformArguments('destination', { + key: 'source', + weight: 1 + }), + ['ZINTERSTORE', 'destination', '1', 'source', 'WEIGHTS', '1'] + ); + }); - it('with AGGREGATE', () => { - assert.deepEqual( - transformArguments('destination', 'key', { - AGGREGATE: 'SUM' - }), - ['ZINTERSTORE', 'destination', '1', 'key', 'AGGREGATE', 'SUM'] - ); - }); + it('keys & weights', () => { + assert.deepEqual( + ZINTERSTORE.transformArguments('destination', [{ + key: 'a', + weight: 1 + }, { + key: 'b', + weight: 2 + }]), + ['ZINTERSTORE', 'destination', '2', 'a', 'b', 'WEIGHTS', '1', '2'] + ); + }); - it('with WEIGHTS, AGGREGATE', () => { - assert.deepEqual( - transformArguments('destination', 'key', { - WEIGHTS: [1], - AGGREGATE: 'SUM' - }), - ['ZINTERSTORE', 'destination', '1', 'key', 'WEIGHTS', '1', 'AGGREGATE', 'SUM'] - ); - }); + it('with AGGREGATE', () => { + assert.deepEqual( + ZINTERSTORE.transformArguments('destination', 'source', { + AGGREGATE: 'SUM' + }), + ['ZINTERSTORE', 'destination', '1', 'source', 'AGGREGATE', 'SUM'] + ); }); + }); - testUtils.testWithClient('client.zInterStore', async client => { - assert.equal( - await client.zInterStore('destination', 'key'), - 0 - ); - }, GLOBAL.SERVERS.OPEN); + testUtils.testAll('zInterStore', async client => { + assert.equal( + await client.zInterStore('{tag}destination', '{tag}key'), + 0 + ); + }, { + client: GLOBAL.SERVERS.OPEN, + cluster: GLOBAL.CLUSTERS.OPEN + }); }); diff --git a/packages/client/lib/commands/ZINTERSTORE.ts b/packages/client/lib/commands/ZINTERSTORE.ts index 540f10ae2d8..a5334566d73 100644 --- a/packages/client/lib/commands/ZINTERSTORE.ts +++ b/packages/client/lib/commands/ZINTERSTORE.ts @@ -1,32 +1,17 @@ -import { RedisCommandArgument, RedisCommandArguments } from '.'; -import { pushVerdictArgument } from './generic-transformers'; -export const FIRST_KEY_INDEX = 1; +import { RedisArgument, NumberReply, Command } from '../RESP/types'; +import { pushZInterArguments, ZInterOptions } from './ZINTER'; +import { ZKeys } from './generic-transformers'; -interface ZInterStoreOptions { - WEIGHTS?: Array; - AGGREGATE?: 'SUM' | 'MIN' | 'MAX'; -} - -export function transformArguments( - destination: RedisCommandArgument, - keys: Array | RedisCommandArgument, - options?: ZInterStoreOptions -): RedisCommandArguments { - const args = pushVerdictArgument(['ZINTERSTORE', destination], keys); - - if (options?.WEIGHTS) { - args.push( - 'WEIGHTS', - ...options.WEIGHTS.map(weight => weight.toString()) - ); - } - - if (options?.AGGREGATE) { - args.push('AGGREGATE', options.AGGREGATE); - } - - return args; -} - -export declare function transformReply(): number; +export default { + FIRST_KEY_INDEX: 1, + IS_READ_ONLY: false, + transformArguments( + destination: RedisArgument, + keys: ZKeys, + options?: ZInterOptions + ) { + return pushZInterArguments(['ZINTERSTORE', destination], keys, options); + }, + transformReply: undefined as unknown as () => NumberReply +} as const satisfies Command; diff --git a/packages/client/lib/commands/ZINTER_WITHSCORES.spec.ts b/packages/client/lib/commands/ZINTER_WITHSCORES.spec.ts index 0eaeb26a244..05790510e49 100644 --- a/packages/client/lib/commands/ZINTER_WITHSCORES.spec.ts +++ b/packages/client/lib/commands/ZINTER_WITHSCORES.spec.ts @@ -1,58 +1,65 @@ -import { strict as assert } from 'assert'; +import { strict as assert } from 'node:assert'; import testUtils, { GLOBAL } from '../test-utils'; -import { transformArguments } from './ZINTER_WITHSCORES'; +import ZINTER_WITHSCORES from './ZINTER_WITHSCORES'; describe('ZINTER WITHSCORES', () => { - testUtils.isVersionGreaterThanHook([6, 2]); + testUtils.isVersionGreaterThanHook([6, 2]); - describe('transformArguments', () => { - it('key (string)', () => { - assert.deepEqual( - transformArguments('key'), - ['ZINTER', '1', 'key', 'WITHSCORES'] - ); - }); + describe('transformArguments', () => { + it('key (string)', () => { + assert.deepEqual( + ZINTER_WITHSCORES.transformArguments('key'), + ['ZINTER', '1', 'key', 'WITHSCORES'] + ); + }); - it('keys (array)', () => { - assert.deepEqual( - transformArguments(['1', '2']), - ['ZINTER', '2', '1', '2', 'WITHSCORES'] - ); - }); + it('keys (Array)', () => { + assert.deepEqual( + ZINTER_WITHSCORES.transformArguments(['1', '2']), + ['ZINTER', '2', '1', '2', 'WITHSCORES'] + ); + }); - it('with WEIGHTS', () => { - assert.deepEqual( - transformArguments('key', { - WEIGHTS: [1] - }), - ['ZINTER', '1', 'key', 'WEIGHTS', '1', 'WITHSCORES'] - ); - }); + it('key & weight', () => { + assert.deepEqual( + ZINTER_WITHSCORES.transformArguments({ + key: 'key', + weight: 1 + }), + ['ZINTER', '1', 'key', 'WEIGHTS', '1', 'WITHSCORES'] + ); + }); - it('with AGGREGATE', () => { - assert.deepEqual( - transformArguments('key', { - AGGREGATE: 'SUM' - }), - ['ZINTER', '1', 'key', 'AGGREGATE', 'SUM', 'WITHSCORES'] - ); - }); + it('keys & weights', () => { + assert.deepEqual( + ZINTER_WITHSCORES.transformArguments([{ + key: 'a', + weight: 1 + }, { + key: 'b', + weight: 2 + }]), + ['ZINTER', '2', 'a', 'b', 'WEIGHTS', '1', '2', 'WITHSCORES'] + ); + }); - it('with WEIGHTS, AGGREGATE', () => { - assert.deepEqual( - transformArguments('key', { - WEIGHTS: [1], - AGGREGATE: 'SUM' - }), - ['ZINTER', '1', 'key', 'WEIGHTS', '1', 'AGGREGATE', 'SUM', 'WITHSCORES'] - ); - }); + it('with AGGREGATE', () => { + assert.deepEqual( + ZINTER_WITHSCORES.transformArguments('key', { + AGGREGATE: 'SUM' + }), + ['ZINTER', '1', 'key', 'AGGREGATE', 'SUM', 'WITHSCORES'] + ); }); + }); - testUtils.testWithClient('client.zInterWithScores', async client => { - assert.deepEqual( - await client.zInterWithScores('key'), - [] - ); - }, GLOBAL.SERVERS.OPEN); + testUtils.testAll('zInterWithScores', async client => { + assert.deepEqual( + await client.zInterWithScores('key'), + [] + ); + }, { + client: GLOBAL.SERVERS.OPEN, + cluster: GLOBAL.CLUSTERS.OPEN + }); }); diff --git a/packages/client/lib/commands/ZINTER_WITHSCORES.ts b/packages/client/lib/commands/ZINTER_WITHSCORES.ts index c9416e9222a..b287649fbc0 100644 --- a/packages/client/lib/commands/ZINTER_WITHSCORES.ts +++ b/packages/client/lib/commands/ZINTER_WITHSCORES.ts @@ -1,13 +1,14 @@ -import { RedisCommandArguments } from '.'; -import { transformArguments as transformZInterArguments } from './ZINTER'; +import { Command } from '../RESP/types'; +import ZINTER from './ZINTER'; +import { transformSortedSetReply } from './generic-transformers'; -export { FIRST_KEY_INDEX, IS_READ_ONLY } from './ZINTER'; - -export function transformArguments(...args: Parameters): RedisCommandArguments { - return [ - ...transformZInterArguments(...args), - 'WITHSCORES' - ]; -} - -export { transformSortedSetWithScoresReply as transformReply } from './generic-transformers'; +export default { + FIRST_KEY_INDEX: ZINTER.FIRST_KEY_INDEX, + IS_READ_ONLY: ZINTER.IS_READ_ONLY, + transformArguments(...args: Parameters) { + const redisArgs = ZINTER.transformArguments(...args); + redisArgs.push('WITHSCORES'); + return redisArgs; + }, + transformReply: transformSortedSetReply +} as const satisfies Command; diff --git a/packages/client/lib/commands/ZLEXCOUNT.spec.ts b/packages/client/lib/commands/ZLEXCOUNT.spec.ts index 85809f1a9a9..d0a76075579 100644 --- a/packages/client/lib/commands/ZLEXCOUNT.spec.ts +++ b/packages/client/lib/commands/ZLEXCOUNT.spec.ts @@ -1,19 +1,22 @@ -import { strict as assert } from 'assert'; +import { strict as assert } from 'node:assert'; import testUtils, { GLOBAL } from '../test-utils'; -import { transformArguments } from './ZLEXCOUNT'; +import ZLEXCOUNT from './ZLEXCOUNT'; describe('ZLEXCOUNT', () => { - it('transformArguments', () => { - assert.deepEqual( - transformArguments('key', '[a', '[b'), - ['ZLEXCOUNT', 'key', '[a', '[b'] - ); - }); + it('transformArguments', () => { + assert.deepEqual( + ZLEXCOUNT.transformArguments('key', '[a', '[b'), + ['ZLEXCOUNT', 'key', '[a', '[b'] + ); + }); - testUtils.testWithClient('client.zLexCount', async client => { - assert.equal( - await client.zLexCount('key', '[a', '[b'), - 0 - ); - }, GLOBAL.SERVERS.OPEN); + testUtils.testAll('zLexCount', async client => { + assert.equal( + await client.zLexCount('key', '[a', '[b'), + 0 + ); + }, { + client: GLOBAL.SERVERS.OPEN, + cluster: GLOBAL.CLUSTERS.OPEN + }); }); diff --git a/packages/client/lib/commands/ZLEXCOUNT.ts b/packages/client/lib/commands/ZLEXCOUNT.ts index e2fbcdbb42b..26c9e0d70ac 100644 --- a/packages/client/lib/commands/ZLEXCOUNT.ts +++ b/packages/client/lib/commands/ZLEXCOUNT.ts @@ -1,20 +1,19 @@ -import { RedisCommandArgument, RedisCommandArguments } from '.'; +import { RedisArgument, NumberReply, Command } from '../RESP/types'; -export const FIRST_KEY_INDEX = 1; - -export const IS_READ_ONLY = true; - -export function transformArguments( - key: RedisCommandArgument, - min: RedisCommandArgument, - max: RedisCommandArgument -): RedisCommandArguments { +export default { + FIRST_KEY_INDEX: 1, + IS_READ_ONLY: true, + transformArguments( + key: RedisArgument, + min: RedisArgument, + max: RedisArgument + ) { return [ - 'ZLEXCOUNT', - key, - min, - max + 'ZLEXCOUNT', + key, + min, + max ]; -} - -export declare function transformReply(): number; + }, + transformReply: undefined as unknown as () => NumberReply +} as const satisfies Command; diff --git a/packages/client/lib/commands/ZMPOP.spec.ts b/packages/client/lib/commands/ZMPOP.spec.ts index 9a0c9676c20..6335fca81cb 100644 --- a/packages/client/lib/commands/ZMPOP.spec.ts +++ b/packages/client/lib/commands/ZMPOP.spec.ts @@ -1,32 +1,55 @@ -import { strict as assert } from 'assert'; +import { strict as assert } from 'node:assert'; import testUtils, { GLOBAL } from '../test-utils'; -import { transformArguments } from './ZMPOP'; +import ZMPOP from './ZMPOP'; describe('ZMPOP', () => { - testUtils.isVersionGreaterThanHook([7]); + testUtils.isVersionGreaterThanHook([7]); - describe('transformArguments', () => { - it('simple', () => { - assert.deepEqual( - transformArguments('key', 'MIN'), - ['ZMPOP', '1', 'key', 'MIN'] - ); - }); + describe('transformArguments', () => { + it('simple', () => { + assert.deepEqual( + ZMPOP.transformArguments('key', 'MIN'), + ['ZMPOP', '1', 'key', 'MIN'] + ); + }); - it('with score and count', () => { - assert.deepEqual( - transformArguments('key', 'MIN', { - COUNT: 2 - }), - ['ZMPOP', '1', 'key', 'MIN', 'COUNT', '2'] - ); - }); + it('with count', () => { + assert.deepEqual( + ZMPOP.transformArguments('key', 'MIN', { + COUNT: 2 + }), + ['ZMPOP', '1', 'key', 'MIN', 'COUNT', '2'] + ); }); + }); + + testUtils.testAll('zmPop - null', async client => { + assert.equal( + await client.zmPop('key', 'MIN'), + null + ); + }, { + client: GLOBAL.SERVERS.OPEN, + cluster: GLOBAL.CLUSTERS.OPEN + }); + + testUtils.testAll('zmPop - with members', async client => { + const members = [{ + value: '1', + score: 1 + }]; - testUtils.testWithClient('client.zmPop', async client => { - assert.deepEqual( - await client.zmPop('key', 'MIN'), - null - ); - }, GLOBAL.SERVERS.OPEN); + const [, reply] = await Promise.all([ + client.zAdd('key', members), + client.zmPop('key', 'MIN') + ]); + + assert.deepEqual(reply, { + key: 'key', + members + }); + }, { + client: GLOBAL.SERVERS.OPEN, + cluster: GLOBAL.CLUSTERS.OPEN + }); }); diff --git a/packages/client/lib/commands/ZMPOP.ts b/packages/client/lib/commands/ZMPOP.ts index 0baa46bbf0b..57d2cccdaca 100644 --- a/packages/client/lib/commands/ZMPOP.ts +++ b/packages/client/lib/commands/ZMPOP.ts @@ -1,34 +1,61 @@ -import { RedisCommandArgument, RedisCommandArguments } from '.'; -import { SortedSetSide, transformSortedSetMemberReply, transformZMPopArguments, ZMember, ZMPopOptions } from './generic-transformers'; - -export const FIRST_KEY_INDEX = 2; - -export function transformArguments( - keys: RedisCommandArgument | Array, - side: SortedSetSide, - options?: ZMPopOptions -): RedisCommandArguments { - return transformZMPopArguments( - ['ZMPOP'], - keys, - side, - options - ); +import { RedisArgument, NullReply, TuplesReply, BlobStringReply, DoubleReply, ArrayReply, UnwrapReply, Resp2Reply, Command, TypeMapping } from '../RESP/types'; +import { pushVariadicArgument, RedisVariadicArgument, SortedSetSide, transformSortedSetReply, transformDoubleReply } from './generic-transformers'; + +export interface ZMPopOptions { + COUNT?: number; } -type ZMPopRawReply = null | [ - key: string, - elements: Array<[RedisCommandArgument, RedisCommandArgument]> -]; +export type ZMPopRawReply = NullReply | TuplesReply<[ + key: BlobStringReply, + members: ArrayReply> +]>; -type ZMPopReply = null | { - key: string, - elements: Array -}; +export function transformZMPopArguments( + args: Array, + keys: RedisVariadicArgument, + side: SortedSetSide, + options?: ZMPopOptions +) { + args = pushVariadicArgument(args, keys); -export function transformReply(reply: ZMPopRawReply): ZMPopReply { - return reply === null ? null : { - key: reply[0], - elements: reply[1].map(transformSortedSetMemberReply) - }; + args.push(side); + + if (options?.COUNT) { + args.push('COUNT', options.COUNT.toString()); + } + + return args; } + +export type ZMPopArguments = typeof transformZMPopArguments extends (_: any, ...args: infer T) => any ? T : never; + +export default { + FIRST_KEY_INDEX: 2, + IS_READ_ONLY: false, + transformArguments(...args: ZMPopArguments) { + return transformZMPopArguments(['ZMPOP'], ...args); + }, + transformReply: { + 2(reply: UnwrapReply>, preserve?: any, typeMapping?: TypeMapping) { + return reply === null ? null : { + key: reply[0], + members: (reply[1] as unknown as UnwrapReply).map(member => { + const [value, score] = member as unknown as UnwrapReply; + return { + value, + score: transformDoubleReply[2](score, preserve, typeMapping) + }; + }) + }; + }, + 3(reply: UnwrapReply) { + return reply === null ? null : { + key: reply[0], + members: transformSortedSetReply[3](reply[1]) + }; + } + } +} as const satisfies Command; diff --git a/packages/client/lib/commands/ZMSCORE.spec.ts b/packages/client/lib/commands/ZMSCORE.spec.ts index 228c8e9d6f6..5035724b117 100644 --- a/packages/client/lib/commands/ZMSCORE.spec.ts +++ b/packages/client/lib/commands/ZMSCORE.spec.ts @@ -1,30 +1,33 @@ -import { strict as assert } from 'assert'; +import { strict as assert } from 'node:assert'; import testUtils, { GLOBAL } from '../test-utils'; -import { transformArguments } from './ZMSCORE'; +import ZMSCORE from './ZMSCORE'; describe('ZMSCORE', () => { - testUtils.isVersionGreaterThanHook([6, 2]); + testUtils.isVersionGreaterThanHook([6, 2]); - describe('transformArguments', () => { - it('string', () => { - assert.deepEqual( - transformArguments('key', 'member'), - ['ZMSCORE', 'key', 'member'] - ); - }); + describe('transformArguments', () => { + it('string', () => { + assert.deepEqual( + ZMSCORE.transformArguments('key', 'member'), + ['ZMSCORE', 'key', 'member'] + ); + }); - it('array', () => { - assert.deepEqual( - transformArguments('key', ['1', '2']), - ['ZMSCORE', 'key', '1', '2'] - ); - }); + it('array', () => { + assert.deepEqual( + ZMSCORE.transformArguments('key', ['1', '2']), + ['ZMSCORE', 'key', '1', '2'] + ); }); + }); - testUtils.testWithClient('client.zmScore', async client => { - assert.deepEqual( - await client.zmScore('key', 'member'), - [null] - ); - }, GLOBAL.SERVERS.OPEN); + testUtils.testAll('zmScore', async client => { + assert.deepEqual( + await client.zmScore('key', 'member'), + [null] + ); + }, { + client: GLOBAL.SERVERS.OPEN, + cluster: GLOBAL.CLUSTERS.OPEN + }); }); diff --git a/packages/client/lib/commands/ZMSCORE.ts b/packages/client/lib/commands/ZMSCORE.ts index 6c8c9dace31..00ade13b011 100644 --- a/packages/client/lib/commands/ZMSCORE.ts +++ b/packages/client/lib/commands/ZMSCORE.ts @@ -1,15 +1,19 @@ -import { RedisCommandArgument, RedisCommandArguments } from '.'; -import { pushVerdictArguments } from './generic-transformers'; +import { RedisArgument, ArrayReply, NullReply, BlobStringReply, DoubleReply, UnwrapReply, Command, TypeMapping } from '../RESP/types'; +import { createTransformNullableDoubleReplyResp2Func, pushVariadicArguments, RedisVariadicArgument } from './generic-transformers'; -export const FIRST_KEY_INDEX = 1; - -export const IS_READ_ONLY = true; - -export function transformArguments( - key: RedisCommandArgument, - member: RedisCommandArgument | Array -): RedisCommandArguments { - return pushVerdictArguments(['ZMSCORE', key], member); -} - -export { transformNumberInfinityNullArrayReply as transformReply } from './generic-transformers'; +export default { + FIRST_KEY_INDEX: 1, + IS_READ_ONLY: true, + transformArguments( + key: RedisArgument, + member: RedisVariadicArgument + ) { + return pushVariadicArguments(['ZMSCORE', key], member); + }, + transformReply: { + 2: (reply: UnwrapReply>, preserve?: any, typeMapping?: TypeMapping) => { + return reply.map(createTransformNullableDoubleReplyResp2Func(preserve, typeMapping)); + }, + 3: undefined as unknown as () => ArrayReply + } +} as const satisfies Command; diff --git a/packages/client/lib/commands/ZPOPMAX.spec.ts b/packages/client/lib/commands/ZPOPMAX.spec.ts index 18fba23a3e9..609ccb826b5 100644 --- a/packages/client/lib/commands/ZPOPMAX.spec.ts +++ b/packages/client/lib/commands/ZPOPMAX.spec.ts @@ -1,41 +1,39 @@ -import { strict as assert } from 'assert'; +import { strict as assert } from 'node:assert'; import testUtils, { GLOBAL } from '../test-utils'; -import { transformArguments, transformReply } from './ZPOPMAX'; +import ZPOPMAX from './ZPOPMAX'; describe('ZPOPMAX', () => { - it('transformArguments', () => { - assert.deepEqual( - transformArguments('key'), - ['ZPOPMAX', 'key'] - ); - }); + it('transformArguments', () => { + assert.deepEqual( + ZPOPMAX.transformArguments('key'), + ['ZPOPMAX', 'key'] + ); + }); - it('transformReply', () => { - assert.deepEqual( - transformReply(['value', '1']), - { - value: 'value', - score: 1 - } - ); - }); + testUtils.testAll('zPopMax - null', async client => { + assert.equal( + await client.zPopMax('key'), + null + ); + }, { + client: GLOBAL.SERVERS.OPEN, + cluster: GLOBAL.SERVERS.OPEN + }); - describe('client.zPopMax', () => { - testUtils.testWithClient('null', async client => { - assert.equal( - await client.zPopMax('key'), - null - ); - }, GLOBAL.SERVERS.OPEN); + testUtils.testAll('zPopMax - with member', async client => { + const member = { + value: 'value', + score: 1 + }; - testUtils.testWithClient('member', async client => { - const member = { score: 1, value: 'value' }, - [, zPopMaxReply] = await Promise.all([ - client.zAdd('key', member), - client.zPopMax('key') - ]); + const [, reply] = await Promise.all([ + client.zAdd('key', member), + client.zPopMax('key') + ]); - assert.deepEqual(zPopMaxReply, member); - }, GLOBAL.SERVERS.OPEN); - }); + assert.deepEqual(reply, member); + }, { + client: GLOBAL.SERVERS.OPEN, + cluster: GLOBAL.SERVERS.OPEN + }); }); diff --git a/packages/client/lib/commands/ZPOPMAX.ts b/packages/client/lib/commands/ZPOPMAX.ts index 811166a690c..130309347a6 100644 --- a/packages/client/lib/commands/ZPOPMAX.ts +++ b/packages/client/lib/commands/ZPOPMAX.ts @@ -1,12 +1,28 @@ -import { RedisCommandArgument, RedisCommandArguments } from '.'; +import { RedisArgument, TuplesReply, BlobStringReply, DoubleReply, UnwrapReply, Command, TypeMapping } from '../RESP/types'; +import { transformDoubleReply } from './generic-transformers'; -export const FIRST_KEY_INDEX = 1; +export default { + FIRST_KEY_INDEX: 1, + IS_READ_ONLY: false, + transformArguments(key: RedisArgument) { + return ['ZPOPMAX', key]; + }, + transformReply: { + 2: (reply: UnwrapReply>, preserve?: any, typeMapping?: TypeMapping) => { + if (reply.length === 0) return null; -export function transformArguments(key: RedisCommandArgument): RedisCommandArguments { - return [ - 'ZPOPMAX', - key - ]; -} + return { + value: reply[0], + score: transformDoubleReply[2](reply[1], preserve, typeMapping), + }; + }, + 3: (reply: UnwrapReply>) => { + if (reply.length === 0) return null; -export { transformSortedSetMemberNullReply as transformReply } from './generic-transformers'; + return { + value: reply[0], + score: reply[1] + }; + } + } +} as const satisfies Command; diff --git a/packages/client/lib/commands/ZPOPMAX_COUNT.spec.ts b/packages/client/lib/commands/ZPOPMAX_COUNT.spec.ts index b282d0d3199..b653b1f3f1a 100644 --- a/packages/client/lib/commands/ZPOPMAX_COUNT.spec.ts +++ b/packages/client/lib/commands/ZPOPMAX_COUNT.spec.ts @@ -1,19 +1,32 @@ -import { strict as assert } from 'assert'; +import { strict as assert } from 'node:assert'; import testUtils, { GLOBAL } from '../test-utils'; -import { transformArguments } from './ZPOPMAX_COUNT'; +import ZPOPMAX_COUNT from './ZPOPMAX_COUNT'; describe('ZPOPMAX COUNT', () => { - it('transformArguments', () => { - assert.deepEqual( - transformArguments('key', 1), - ['ZPOPMAX', 'key', '1'] - ); - }); + it('transformArguments', () => { + assert.deepEqual( + ZPOPMAX_COUNT.transformArguments('key', 1), + ['ZPOPMAX', 'key', '1'] + ); + }); - testUtils.testWithClient('client.zPopMaxCount', async client => { - assert.deepEqual( - await client.zPopMaxCount('key', 1), - [] - ); - }, GLOBAL.SERVERS.OPEN); + testUtils.testAll('zPopMaxCount', async client => { + const members = [{ + value: '1', + score: 1 + }, { + value: '2', + score: 2 + }]; + + const [ , reply] = await Promise.all([ + client.zAdd('key', members), + client.zPopMaxCount('key', members.length) + ]); + + assert.deepEqual(reply, members.reverse()); + }, { + client: GLOBAL.SERVERS.OPEN, + cluster: GLOBAL.SERVERS.OPEN + }); }); diff --git a/packages/client/lib/commands/ZPOPMAX_COUNT.ts b/packages/client/lib/commands/ZPOPMAX_COUNT.ts index 875bcfb9147..00d39536ae1 100644 --- a/packages/client/lib/commands/ZPOPMAX_COUNT.ts +++ b/packages/client/lib/commands/ZPOPMAX_COUNT.ts @@ -1,16 +1,11 @@ -import { RedisCommandArgument, RedisCommandArguments } from '.'; -import { transformArguments as transformZPopMaxArguments } from './ZPOPMAX'; +import { RedisArgument, Command } from '../RESP/types'; +import { transformSortedSetReply } from './generic-transformers'; -export { FIRST_KEY_INDEX } from './ZPOPMAX'; - -export function transformArguments( - key: RedisCommandArgument, - count: number -): RedisCommandArguments { - return [ - ...transformZPopMaxArguments(key), - count.toString() - ]; -} - -export { transformSortedSetWithScoresReply as transformReply } from './generic-transformers'; +export default { + FIRST_KEY_INDEX: 1, + IS_READ_ONLY: false, + transformArguments(key: RedisArgument, count: number) { + return ['ZPOPMAX', key, count.toString()]; + }, + transformReply: transformSortedSetReply +} as const satisfies Command; diff --git a/packages/client/lib/commands/ZPOPMIN.spec.ts b/packages/client/lib/commands/ZPOPMIN.spec.ts index 624b7054404..0b2c57d28b9 100644 --- a/packages/client/lib/commands/ZPOPMIN.spec.ts +++ b/packages/client/lib/commands/ZPOPMIN.spec.ts @@ -1,41 +1,39 @@ -import { strict as assert } from 'assert'; +import { strict as assert } from 'node:assert'; import testUtils, { GLOBAL } from '../test-utils'; -import { transformArguments, transformReply } from './ZPOPMIN'; +import ZPOPMIN from './ZPOPMIN'; describe('ZPOPMIN', () => { - it('transformArguments', () => { - assert.deepEqual( - transformArguments('key'), - ['ZPOPMIN', 'key'] - ); - }); + it('transformArguments', () => { + assert.deepEqual( + ZPOPMIN.transformArguments('key'), + ['ZPOPMIN', 'key'] + ); + }); - it('transformReply', () => { - assert.deepEqual( - transformReply(['value', '1']), - { - value: 'value', - score: 1 - } - ); - }); + testUtils.testAll('zPopMin - null', async client => { + assert.equal( + await client.zPopMin('key'), + null + ); + }, { + client: GLOBAL.SERVERS.OPEN, + cluster: GLOBAL.SERVERS.OPEN + }); - describe('client.zPopMin', () => { - testUtils.testWithClient('null', async client => { - assert.equal( - await client.zPopMin('key'), - null - ); - }, GLOBAL.SERVERS.OPEN); + testUtils.testAll('zPopMax - with member', async client => { + const member = { + value: 'value', + score: 1 + }; - testUtils.testWithClient('member', async client => { - const member = { score: 1, value: 'value' }, - [, zPopMinReply] = await Promise.all([ - client.zAdd('key', member), - client.zPopMin('key') - ]); + const [, reply] = await Promise.all([ + client.zAdd('key', member), + client.zPopMin('key') + ]); - assert.deepEqual(zPopMinReply, member); - }, GLOBAL.SERVERS.OPEN); - }); + assert.deepEqual(reply, member); + }, { + client: GLOBAL.SERVERS.OPEN, + cluster: GLOBAL.SERVERS.OPEN + }); }); diff --git a/packages/client/lib/commands/ZPOPMIN.ts b/packages/client/lib/commands/ZPOPMIN.ts index 053ffd2d2ce..b9da85cc974 100644 --- a/packages/client/lib/commands/ZPOPMIN.ts +++ b/packages/client/lib/commands/ZPOPMIN.ts @@ -1,12 +1,11 @@ -import { RedisCommandArgument, RedisCommandArguments } from '.'; +import { RedisArgument, Command } from '../RESP/types'; +import ZPOPMAX from './ZPOPMAX'; -export const FIRST_KEY_INDEX = 1; - -export function transformArguments(key: RedisCommandArgument): RedisCommandArguments { - return [ - 'ZPOPMIN', - key - ]; -} - -export { transformSortedSetMemberNullReply as transformReply } from './generic-transformers'; +export default { + FIRST_KEY_INDEX: 1, + IS_READ_ONLY: false, + transformArguments(key: RedisArgument) { + return ['ZPOPMIN', key]; + }, + transformReply: ZPOPMAX.transformReply +} as const satisfies Command; diff --git a/packages/client/lib/commands/ZPOPMIN_COUNT.spec.ts b/packages/client/lib/commands/ZPOPMIN_COUNT.spec.ts index 6d40002ab72..fa3d9e2a975 100644 --- a/packages/client/lib/commands/ZPOPMIN_COUNT.spec.ts +++ b/packages/client/lib/commands/ZPOPMIN_COUNT.spec.ts @@ -1,19 +1,32 @@ -import { strict as assert } from 'assert'; +import { strict as assert } from 'node:assert'; import testUtils, { GLOBAL } from '../test-utils'; -import { transformArguments } from './ZPOPMIN_COUNT'; +import ZPOPMIN_COUNT from './ZPOPMIN_COUNT'; describe('ZPOPMIN COUNT', () => { - it('transformArguments', () => { - assert.deepEqual( - transformArguments('key', 1), - ['ZPOPMIN', 'key', '1'] - ); - }); + it('transformArguments', () => { + assert.deepEqual( + ZPOPMIN_COUNT.transformArguments('key', 1), + ['ZPOPMIN', 'key', '1'] + ); + }); - testUtils.testWithClient('client.zPopMinCount', async client => { - assert.deepEqual( - await client.zPopMinCount('key', 1), - [] - ); - }, GLOBAL.SERVERS.OPEN); + testUtils.testAll('zPopMinCount', async client => { + const members = [{ + value: '1', + score: 1 + }, { + value: '2', + score: 2 + }]; + + const [ , reply] = await Promise.all([ + client.zAdd('key', members), + client.zPopMinCount('key', members.length) + ]); + + assert.deepEqual(reply, members); + }, { + client: GLOBAL.SERVERS.OPEN, + cluster: GLOBAL.SERVERS.OPEN + }); }); diff --git a/packages/client/lib/commands/ZPOPMIN_COUNT.ts b/packages/client/lib/commands/ZPOPMIN_COUNT.ts index 54125ade0ac..2433686da56 100644 --- a/packages/client/lib/commands/ZPOPMIN_COUNT.ts +++ b/packages/client/lib/commands/ZPOPMIN_COUNT.ts @@ -1,16 +1,11 @@ -import { RedisCommandArgument, RedisCommandArguments } from '.'; -import { transformArguments as transformZPopMinArguments } from './ZPOPMIN'; +import { RedisArgument, Command } from '../RESP/types'; +import { transformSortedSetReply } from './generic-transformers'; -export { FIRST_KEY_INDEX } from './ZPOPMIN'; - -export function transformArguments( - key: RedisCommandArgument, - count: number -): RedisCommandArguments { - return [ - ...transformZPopMinArguments(key), - count.toString() - ]; -} - -export { transformSortedSetWithScoresReply as transformReply } from './generic-transformers'; +export default { + FIRST_KEY_INDEX: 1, + IS_READ_ONLY: false, + transformArguments(key: RedisArgument, count: number) { + return ['ZPOPMIN', key, count.toString()]; + }, + transformReply: transformSortedSetReply +} as const satisfies Command; diff --git a/packages/client/lib/commands/ZRANDMEMBER.spec.ts b/packages/client/lib/commands/ZRANDMEMBER.spec.ts index c57d26f830e..519850f5eff 100644 --- a/packages/client/lib/commands/ZRANDMEMBER.spec.ts +++ b/packages/client/lib/commands/ZRANDMEMBER.spec.ts @@ -1,21 +1,24 @@ -import { strict as assert } from 'assert'; +import { strict as assert } from 'node:assert'; import testUtils, { GLOBAL } from '../test-utils'; -import { transformArguments } from './ZRANDMEMBER'; +import ZRANDMEMBER from './ZRANDMEMBER'; describe('ZRANDMEMBER', () => { - testUtils.isVersionGreaterThanHook([6, 2]); + testUtils.isVersionGreaterThanHook([6, 2]); - it('transformArguments', () => { - assert.deepEqual( - transformArguments('key'), - ['ZRANDMEMBER', 'key'] - ); - }); + it('transformArguments', () => { + assert.deepEqual( + ZRANDMEMBER.transformArguments('key'), + ['ZRANDMEMBER', 'key'] + ); + }); - testUtils.testWithClient('client.zRandMember', async client => { - assert.equal( - await client.zRandMember('key'), - null - ); - }, GLOBAL.SERVERS.OPEN); + testUtils.testAll('zRandMember', async client => { + assert.equal( + await client.zRandMember('key'), + null + ); + }, { + client: GLOBAL.SERVERS.OPEN, + cluster: GLOBAL.CLUSTERS.OPEN + }); }); diff --git a/packages/client/lib/commands/ZRANDMEMBER.ts b/packages/client/lib/commands/ZRANDMEMBER.ts index 00420872c0c..449eb281c66 100644 --- a/packages/client/lib/commands/ZRANDMEMBER.ts +++ b/packages/client/lib/commands/ZRANDMEMBER.ts @@ -1,11 +1,10 @@ -import { RedisCommandArgument, RedisCommandArguments } from '.'; +import { RedisArgument, BlobStringReply, NullReply, Command } from '../RESP/types'; -export const FIRST_KEY_INDEX = 1; - -export const IS_READ_ONLY = true; - -export function transformArguments(key: RedisCommandArgument): RedisCommandArguments { +export default { + FIRST_KEY_INDEX: 1, + IS_READ_ONLY: true, + transformArguments(key: RedisArgument) { return ['ZRANDMEMBER', key]; -} - -export declare function transformReply(): RedisCommandArgument | null; + }, + transformReply: undefined as unknown as () => BlobStringReply | NullReply +} as const satisfies Command; diff --git a/packages/client/lib/commands/ZRANDMEMBER_COUNT.spec.ts b/packages/client/lib/commands/ZRANDMEMBER_COUNT.spec.ts index 10db0727b23..2d0f4b9ced8 100644 --- a/packages/client/lib/commands/ZRANDMEMBER_COUNT.spec.ts +++ b/packages/client/lib/commands/ZRANDMEMBER_COUNT.spec.ts @@ -1,21 +1,24 @@ -import { strict as assert } from 'assert'; +import { strict as assert } from 'node:assert'; import testUtils, { GLOBAL } from '../test-utils'; -import { transformArguments } from './ZRANDMEMBER_COUNT'; +import ZRANDMEMBER_COUNT from './ZRANDMEMBER_COUNT'; describe('ZRANDMEMBER COUNT', () => { - testUtils.isVersionGreaterThanHook([6, 2, 5]); + testUtils.isVersionGreaterThanHook([6, 2, 5]); - it('transformArguments', () => { - assert.deepEqual( - transformArguments('key', 1), - ['ZRANDMEMBER', 'key', '1'] - ); - }); + it('transformArguments', () => { + assert.deepEqual( + ZRANDMEMBER_COUNT.transformArguments('key', 1), + ['ZRANDMEMBER', 'key', '1'] + ); + }); - testUtils.testWithClient('client.zRandMemberCount', async client => { - assert.deepEqual( - await client.zRandMemberCount('key', 1), - [] - ); - }, GLOBAL.SERVERS.OPEN); + testUtils.testAll('zRandMemberCount', async client => { + assert.deepEqual( + await client.zRandMemberCount('key', 1), + [] + ); + }, { + client: GLOBAL.SERVERS.OPEN, + cluster: GLOBAL.CLUSTERS.OPEN + }); }); diff --git a/packages/client/lib/commands/ZRANDMEMBER_COUNT.ts b/packages/client/lib/commands/ZRANDMEMBER_COUNT.ts index 3aa91902c62..89b921f007a 100644 --- a/packages/client/lib/commands/ZRANDMEMBER_COUNT.ts +++ b/packages/client/lib/commands/ZRANDMEMBER_COUNT.ts @@ -1,16 +1,13 @@ -import { RedisCommandArgument, RedisCommandArguments } from '.'; -import { transformArguments as transformZRandMemberArguments } from './ZRANDMEMBER'; +import { RedisArgument, ArrayReply, BlobStringReply, Command } from '../RESP/types'; +import ZRANDMEMBER from './ZRANDMEMBER'; -export { FIRST_KEY_INDEX, IS_READ_ONLY } from './ZRANDMEMBER'; - -export function transformArguments( - key: RedisCommandArgument, - count: number -): RedisCommandArguments { - return [ - ...transformZRandMemberArguments(key), - count.toString() - ]; -} - -export declare function transformReply(): Array; +export default { + FIRST_KEY_INDEX: ZRANDMEMBER.FIRST_KEY_INDEX, + IS_READ_ONLY: ZRANDMEMBER.IS_READ_ONLY, + transformArguments(key: RedisArgument, count: number) { + const args = ZRANDMEMBER.transformArguments(key); + args.push(count.toString()); + return args; + }, + transformReply: undefined as unknown as () => ArrayReply +} as const satisfies Command; diff --git a/packages/client/lib/commands/ZRANDMEMBER_COUNT_WITHSCORES.spec.ts b/packages/client/lib/commands/ZRANDMEMBER_COUNT_WITHSCORES.spec.ts index 5b5ec1f500f..aeeea3f6e71 100644 --- a/packages/client/lib/commands/ZRANDMEMBER_COUNT_WITHSCORES.spec.ts +++ b/packages/client/lib/commands/ZRANDMEMBER_COUNT_WITHSCORES.spec.ts @@ -1,21 +1,24 @@ -import { strict as assert } from 'assert'; +import { strict as assert } from 'node:assert'; import testUtils, { GLOBAL } from '../test-utils'; -import { transformArguments } from './ZRANDMEMBER_COUNT_WITHSCORES'; +import ZRANDMEMBER_COUNT_WITHSCORES from './ZRANDMEMBER_COUNT_WITHSCORES'; describe('ZRANDMEMBER COUNT WITHSCORES', () => { - testUtils.isVersionGreaterThanHook([6, 2, 5]); + testUtils.isVersionGreaterThanHook([6, 2, 5]); - it('transformArguments', () => { - assert.deepEqual( - transformArguments('key', 1), - ['ZRANDMEMBER', 'key', '1', 'WITHSCORES'] - ); - }); + it('transformArguments', () => { + assert.deepEqual( + ZRANDMEMBER_COUNT_WITHSCORES.transformArguments('key', 1), + ['ZRANDMEMBER', 'key', '1', 'WITHSCORES'] + ); + }); - testUtils.testWithClient('client.zRandMemberCountWithScores', async client => { - assert.deepEqual( - await client.zRandMemberCountWithScores('key', 1), - [] - ); - }, GLOBAL.SERVERS.OPEN); + testUtils.testAll('zRandMemberCountWithScores', async client => { + assert.deepEqual( + await client.zRandMemberCountWithScores('key', 1), + [] + ); + }, { + client: GLOBAL.SERVERS.OPEN, + cluster: GLOBAL.CLUSTERS.OPEN + }); }); diff --git a/packages/client/lib/commands/ZRANDMEMBER_COUNT_WITHSCORES.ts b/packages/client/lib/commands/ZRANDMEMBER_COUNT_WITHSCORES.ts index cc9d2bc26ee..14c28d4b6c6 100644 --- a/packages/client/lib/commands/ZRANDMEMBER_COUNT_WITHSCORES.ts +++ b/packages/client/lib/commands/ZRANDMEMBER_COUNT_WITHSCORES.ts @@ -1,13 +1,14 @@ -import { RedisCommandArguments } from '.'; -import { transformArguments as transformZRandMemberCountArguments } from './ZRANDMEMBER_COUNT'; +import { Command, RedisArgument } from '../RESP/types'; +import ZRANDMEMBER_COUNT from './ZRANDMEMBER_COUNT'; +import { transformSortedSetReply } from './generic-transformers'; -export { FIRST_KEY_INDEX, IS_READ_ONLY } from './ZRANDMEMBER_COUNT'; - -export function transformArguments(...args: Parameters): RedisCommandArguments { - return [ - ...transformZRandMemberCountArguments(...args), - 'WITHSCORES' - ]; -} - -export { transformSortedSetWithScoresReply as transformReply } from './generic-transformers'; +export default { + FIRST_KEY_INDEX: ZRANDMEMBER_COUNT.FIRST_KEY_INDEX, + IS_READ_ONLY: ZRANDMEMBER_COUNT.IS_READ_ONLY, + transformArguments(key: RedisArgument, count: number) { + const args = ZRANDMEMBER_COUNT.transformArguments(key, count); + args.push('WITHSCORES'); + return args; + }, + transformReply: transformSortedSetReply +} as const satisfies Command; diff --git a/packages/client/lib/commands/ZRANGE.spec.ts b/packages/client/lib/commands/ZRANGE.spec.ts index a280aff0033..db940062b2f 100644 --- a/packages/client/lib/commands/ZRANGE.spec.ts +++ b/packages/client/lib/commands/ZRANGE.spec.ts @@ -1,74 +1,77 @@ -import { strict as assert } from 'assert'; +import { strict as assert } from 'node:assert'; import testUtils, { GLOBAL } from '../test-utils'; -import { transformArguments } from './ZRANGE'; +import ZRANGE from './ZRANGE'; describe('ZRANGE', () => { - describe('transformArguments', () => { - it('simple', () => { - assert.deepEqual( - transformArguments('src', 0, 1), - ['ZRANGE', 'src', '0', '1'] - ); - }); + describe('transformArguments', () => { + it('simple', () => { + assert.deepEqual( + ZRANGE.transformArguments('src', 0, 1), + ['ZRANGE', 'src', '0', '1'] + ); + }); - it('with BYSCORE', () => { - assert.deepEqual( - transformArguments('src', 0, 1, { - BY: 'SCORE' - }), - ['ZRANGE', 'src', '0', '1', 'BYSCORE'] - ); - }); + it('with BYSCORE', () => { + assert.deepEqual( + ZRANGE.transformArguments('src', 0, 1, { + BY: 'SCORE' + }), + ['ZRANGE', 'src', '0', '1', 'BYSCORE'] + ); + }); - it('with BYLEX', () => { - assert.deepEqual( - transformArguments('src', 0, 1, { - BY: 'LEX' - }), - ['ZRANGE', 'src', '0', '1', 'BYLEX'] - ); - }); + it('with BYLEX', () => { + assert.deepEqual( + ZRANGE.transformArguments('src', 0, 1, { + BY: 'LEX' + }), + ['ZRANGE', 'src', '0', '1', 'BYLEX'] + ); + }); - it('with REV', () => { - assert.deepEqual( - transformArguments('src', 0, 1, { - REV: true - }), - ['ZRANGE', 'src', '0', '1', 'REV'] - ); - }); + it('with REV', () => { + assert.deepEqual( + ZRANGE.transformArguments('src', 0, 1, { + REV: true + }), + ['ZRANGE', 'src', '0', '1', 'REV'] + ); + }); - it('with LIMIT', () => { - assert.deepEqual( - transformArguments('src', 0, 1, { - LIMIT: { - offset: 0, - count: 1 - } - }), - ['ZRANGE', 'src', '0', '1', 'LIMIT', '0', '1'] - ); - }); + it('with LIMIT', () => { + assert.deepEqual( + ZRANGE.transformArguments('src', 0, 1, { + LIMIT: { + offset: 0, + count: 1 + } + }), + ['ZRANGE', 'src', '0', '1', 'LIMIT', '0', '1'] + ); + }); - it('with BY & REV & LIMIT', () => { - assert.deepEqual( - transformArguments('src', 0, 1, { - BY: 'SCORE', - REV: true, - LIMIT: { - offset: 0, - count: 1 - } - }), - ['ZRANGE', 'src', '0', '1', 'BYSCORE', 'REV', 'LIMIT', '0', '1'] - ); - }); + it('with BY & REV & LIMIT', () => { + assert.deepEqual( + ZRANGE.transformArguments('src', 0, 1, { + BY: 'SCORE', + REV: true, + LIMIT: { + offset: 0, + count: 1 + } + }), + ['ZRANGE', 'src', '0', '1', 'BYSCORE', 'REV', 'LIMIT', '0', '1'] + ); }); + }); - testUtils.testWithClient('client.zRange', async client => { - assert.deepEqual( - await client.zRange('src', 0, 1), - [] - ); - }, GLOBAL.SERVERS.OPEN); + testUtils.testAll('zRange', async client => { + assert.deepEqual( + await client.zRange('src', 0, 1), + [] + ); + }, { + client: GLOBAL.SERVERS.OPEN, + cluster: GLOBAL.CLUSTERS.OPEN + }); }); diff --git a/packages/client/lib/commands/ZRANGE.ts b/packages/client/lib/commands/ZRANGE.ts index 83f09aaa1b0..557044b67da 100644 --- a/packages/client/lib/commands/ZRANGE.ts +++ b/packages/client/lib/commands/ZRANGE.ts @@ -1,51 +1,54 @@ -import { RedisCommandArgument, RedisCommandArguments } from '.'; -import { transformStringNumberInfinityArgument } from './generic-transformers'; - -export const FIRST_KEY_INDEX = 1; - -export const IS_READ_ONLY = true; - -interface ZRangeOptions { - BY?: 'SCORE' | 'LEX'; - REV?: true; - LIMIT?: { - offset: number; - count: number; - }; +import { RedisArgument, ArrayReply, BlobStringReply, Command } from '../RESP/types'; +import { transformStringDoubleArgument } from './generic-transformers'; + +export interface ZRangeOptions { + BY?: 'SCORE' | 'LEX'; + REV?: boolean; + LIMIT?: { + offset: number; + count: number; + }; } -export function transformArguments( - key: RedisCommandArgument, - min: RedisCommandArgument | number, - max: RedisCommandArgument | number, +export default { + FIRST_KEY_INDEX: 1, + IS_READ_ONLY: true, + transformArguments( + key: RedisArgument, + min: RedisArgument | number, + max: RedisArgument | number, options?: ZRangeOptions -): RedisCommandArguments { + ) { const args = [ - 'ZRANGE', - key, - transformStringNumberInfinityArgument(min), - transformStringNumberInfinityArgument(max) + 'ZRANGE', + key, + transformStringDoubleArgument(min), + transformStringDoubleArgument(max) ]; switch (options?.BY) { - case 'SCORE': - args.push('BYSCORE'); - break; + case 'SCORE': + args.push('BYSCORE'); + break; - case 'LEX': - args.push('BYLEX'); - break; + case 'LEX': + args.push('BYLEX'); + break; } if (options?.REV) { - args.push('REV'); + args.push('REV'); } if (options?.LIMIT) { - args.push('LIMIT', options.LIMIT.offset.toString(), options.LIMIT.count.toString()); + args.push( + 'LIMIT', + options.LIMIT.offset.toString(), + options.LIMIT.count.toString() + ); } return args; -} - -export declare function transformReply(): Array; + }, + transformReply: undefined as unknown as () => ArrayReply +} as const satisfies Command; diff --git a/packages/client/lib/commands/ZRANGEBYLEX.spec.ts b/packages/client/lib/commands/ZRANGEBYLEX.spec.ts index fe7b7d5a16e..f3f6f4bc0e5 100644 --- a/packages/client/lib/commands/ZRANGEBYLEX.spec.ts +++ b/packages/client/lib/commands/ZRANGEBYLEX.spec.ts @@ -1,33 +1,36 @@ -import { strict as assert } from 'assert'; +import { strict as assert } from 'node:assert'; import testUtils, { GLOBAL } from '../test-utils'; -import { transformArguments } from './ZRANGEBYLEX'; +import ZRANGEBYLEX from './ZRANGEBYLEX'; describe('ZRANGEBYLEX', () => { - describe('transformArguments', () => { - it('simple', () => { - assert.deepEqual( - transformArguments('src', '-', '+'), - ['ZRANGEBYLEX', 'src', '-', '+'] - ); - }); + describe('transformArguments', () => { + it('simple', () => { + assert.deepEqual( + ZRANGEBYLEX.transformArguments('src', '-', '+'), + ['ZRANGEBYLEX', 'src', '-', '+'] + ); + }); - it('with LIMIT', () => { - assert.deepEqual( - transformArguments('src', '-', '+', { - LIMIT: { - offset: 0, - count: 1 - } - }), - ['ZRANGEBYLEX', 'src', '-', '+', 'LIMIT', '0', '1'] - ); - }); + it('with LIMIT', () => { + assert.deepEqual( + ZRANGEBYLEX.transformArguments('src', '-', '+', { + LIMIT: { + offset: 0, + count: 1 + } + }), + ['ZRANGEBYLEX', 'src', '-', '+', 'LIMIT', '0', '1'] + ); }); + }); - testUtils.testWithClient('client.zRangeByLex', async client => { - assert.deepEqual( - await client.zRangeByLex('src', '-', '+'), - [] - ); - }, GLOBAL.SERVERS.OPEN); + testUtils.testAll('zRangeByLex', async client => { + assert.deepEqual( + await client.zRangeByLex('src', '-', '+'), + [] + ); + }, { + client: GLOBAL.SERVERS.OPEN, + cluster: GLOBAL.CLUSTERS.OPEN + }); }); diff --git a/packages/client/lib/commands/ZRANGEBYLEX.ts b/packages/client/lib/commands/ZRANGEBYLEX.ts index d6e621a562f..afe7718f3c3 100644 --- a/packages/client/lib/commands/ZRANGEBYLEX.ts +++ b/packages/client/lib/commands/ZRANGEBYLEX.ts @@ -1,35 +1,34 @@ -import { RedisCommandArgument, RedisCommandArguments } from '.'; -import { transformStringNumberInfinityArgument } from './generic-transformers'; - -export const FIRST_KEY_INDEX = 1; - -export const IS_READ_ONLY = true; +import { RedisArgument, ArrayReply, BlobStringReply, Command } from '../RESP/types'; +import { transformStringDoubleArgument } from './generic-transformers'; export interface ZRangeByLexOptions { - LIMIT?: { - offset: number; - count: number; - }; + LIMIT?: { + offset: number; + count: number; + }; } -export function transformArguments( - key: RedisCommandArgument, - min: RedisCommandArgument, - max: RedisCommandArgument, +export default { + FIRST_KEY_INDEX: 1, + IS_READ_ONLY: true, + transformArguments( + key: RedisArgument, + min: RedisArgument, + max: RedisArgument, options?: ZRangeByLexOptions -): RedisCommandArguments { + ) { const args = [ - 'ZRANGEBYLEX', - key, - transformStringNumberInfinityArgument(min), - transformStringNumberInfinityArgument(max) + 'ZRANGEBYLEX', + key, + transformStringDoubleArgument(min), + transformStringDoubleArgument(max) ]; if (options?.LIMIT) { - args.push('LIMIT', options.LIMIT.offset.toString(), options.LIMIT.count.toString()); + args.push('LIMIT', options.LIMIT.offset.toString(), options.LIMIT.count.toString()); } return args; -} - -export declare function transformReply(): Array; + }, + transformReply: undefined as unknown as () => ArrayReply +} as const satisfies Command; diff --git a/packages/client/lib/commands/ZRANGEBYSCORE.spec.ts b/packages/client/lib/commands/ZRANGEBYSCORE.spec.ts index a3484326306..61267ea7f2f 100644 --- a/packages/client/lib/commands/ZRANGEBYSCORE.spec.ts +++ b/packages/client/lib/commands/ZRANGEBYSCORE.spec.ts @@ -1,33 +1,36 @@ -import { strict as assert } from 'assert'; +import { strict as assert } from 'node:assert'; import testUtils, { GLOBAL } from '../test-utils'; -import { transformArguments } from './ZRANGEBYSCORE'; +import ZRANGEBYSCORE from './ZRANGEBYSCORE'; describe('ZRANGEBYSCORE', () => { - describe('transformArguments', () => { - it('simple', () => { - assert.deepEqual( - transformArguments('src', 0, 1), - ['ZRANGEBYSCORE', 'src', '0', '1'] - ); - }); + describe('transformArguments', () => { + it('simple', () => { + assert.deepEqual( + ZRANGEBYSCORE.transformArguments('src', 0, 1), + ['ZRANGEBYSCORE', 'src', '0', '1'] + ); + }); - it('with LIMIT', () => { - assert.deepEqual( - transformArguments('src', 0, 1, { - LIMIT: { - offset: 0, - count: 1 - } - }), - ['ZRANGEBYSCORE', 'src', '0', '1', 'LIMIT', '0', '1'] - ); - }); + it('with LIMIT', () => { + assert.deepEqual( + ZRANGEBYSCORE.transformArguments('src', 0, 1, { + LIMIT: { + offset: 0, + count: 1 + } + }), + ['ZRANGEBYSCORE', 'src', '0', '1', 'LIMIT', '0', '1'] + ); }); + }); - testUtils.testWithClient('client.zRangeByScore', async client => { - assert.deepEqual( - await client.zRangeByScore('src', 0, 1), - [] - ); - }, GLOBAL.SERVERS.OPEN); + testUtils.testAll('zRangeByScore', async client => { + assert.deepEqual( + await client.zRangeByScore('src', 0, 1), + [] + ); + }, { + client: GLOBAL.SERVERS.OPEN, + cluster: GLOBAL.CLUSTERS.OPEN + }); }); diff --git a/packages/client/lib/commands/ZRANGEBYSCORE.ts b/packages/client/lib/commands/ZRANGEBYSCORE.ts index 5ab7d7ac727..e54c96380de 100644 --- a/packages/client/lib/commands/ZRANGEBYSCORE.ts +++ b/packages/client/lib/commands/ZRANGEBYSCORE.ts @@ -1,35 +1,36 @@ -import { RedisCommandArgument, RedisCommandArguments } from '.'; -import { transformStringNumberInfinityArgument } from './generic-transformers'; - -export const FIRST_KEY_INDEX = 1; - -export const IS_READ_ONLY = true; +import { RedisArgument, ArrayReply, BlobStringReply, Command } from '../RESP/types'; +import { transformStringDoubleArgument } from './generic-transformers'; export interface ZRangeByScoreOptions { - LIMIT?: { - offset: number; - count: number; - }; + LIMIT?: { + offset: number; + count: number; + }; } -export function transformArguments( - key: RedisCommandArgument, +export declare function transformReply(): Array; + +export default { + FIRST_KEY_INDEX: 1, + IS_READ_ONLY: true, + transformArguments( + key: RedisArgument, min: string | number, max: string | number, options?: ZRangeByScoreOptions -): RedisCommandArguments { + ) { const args = [ - 'ZRANGEBYSCORE', - key, - transformStringNumberInfinityArgument(min), - transformStringNumberInfinityArgument(max) + 'ZRANGEBYSCORE', + key, + transformStringDoubleArgument(min), + transformStringDoubleArgument(max) ]; if (options?.LIMIT) { - args.push('LIMIT', options.LIMIT.offset.toString(), options.LIMIT.count.toString()); + args.push('LIMIT', options.LIMIT.offset.toString(), options.LIMIT.count.toString()); } return args; -} - -export declare function transformReply(): Array; + }, + transformReply: undefined as unknown as () => ArrayReply +} as const satisfies Command; diff --git a/packages/client/lib/commands/ZRANGEBYSCORE_WITHSCORES.spec.ts b/packages/client/lib/commands/ZRANGEBYSCORE_WITHSCORES.spec.ts index 3552d3e2535..e70a97b0372 100644 --- a/packages/client/lib/commands/ZRANGEBYSCORE_WITHSCORES.spec.ts +++ b/packages/client/lib/commands/ZRANGEBYSCORE_WITHSCORES.spec.ts @@ -1,33 +1,36 @@ -import { strict as assert } from 'assert'; +import { strict as assert } from 'node:assert'; import testUtils, { GLOBAL } from '../test-utils'; -import { transformArguments } from './ZRANGEBYSCORE_WITHSCORES'; +import ZRANGEBYSCORE_WITHSCORES from './ZRANGEBYSCORE_WITHSCORES'; describe('ZRANGEBYSCORE WITHSCORES', () => { - describe('transformArguments', () => { - it('simple', () => { - assert.deepEqual( - transformArguments('src', 0, 1), - ['ZRANGEBYSCORE', 'src', '0', '1', 'WITHSCORES'] - ); - }); + describe('transformArguments', () => { + it('simple', () => { + assert.deepEqual( + ZRANGEBYSCORE_WITHSCORES.transformArguments('src', 0, 1), + ['ZRANGEBYSCORE', 'src', '0', '1', 'WITHSCORES'] + ); + }); - it('with LIMIT', () => { - assert.deepEqual( - transformArguments('src', 0, 1, { - LIMIT: { - offset: 0, - count: 1 - } - }), - ['ZRANGEBYSCORE', 'src', '0', '1', 'LIMIT', '0', '1', 'WITHSCORES'] - ); - }); + it('with LIMIT', () => { + assert.deepEqual( + ZRANGEBYSCORE_WITHSCORES.transformArguments('src', 0, 1, { + LIMIT: { + offset: 0, + count: 1 + } + }), + ['ZRANGEBYSCORE', 'src', '0', '1', 'LIMIT', '0', '1', 'WITHSCORES'] + ); }); + }); - testUtils.testWithClient('client.zRangeByScoreWithScores', async client => { - assert.deepEqual( - await client.zRangeByScoreWithScores('src', 0, 1), - [] - ); - }, GLOBAL.SERVERS.OPEN); + testUtils.testAll('zRangeByScoreWithScores', async client => { + assert.deepEqual( + await client.zRangeByScoreWithScores('src', 0, 1), + [] + ); + }, { + client: GLOBAL.SERVERS.OPEN, + cluster: GLOBAL.CLUSTERS.OPEN + }); }); diff --git a/packages/client/lib/commands/ZRANGEBYSCORE_WITHSCORES.ts b/packages/client/lib/commands/ZRANGEBYSCORE_WITHSCORES.ts index c7266f1c062..bfbe09c6e26 100644 --- a/packages/client/lib/commands/ZRANGEBYSCORE_WITHSCORES.ts +++ b/packages/client/lib/commands/ZRANGEBYSCORE_WITHSCORES.ts @@ -1,18 +1,14 @@ -import { RedisCommandArgument, RedisCommandArguments } from '.'; -import { ZRangeByScoreOptions, transformArguments as transformZRangeByScoreArguments } from './ZRANGEBYSCORE'; +import { Command } from '../RESP/types'; +import ZRANGEBYSCORE from './ZRANGEBYSCORE'; +import { transformSortedSetReply } from './generic-transformers'; -export { FIRST_KEY_INDEX, IS_READ_ONLY } from './ZRANGEBYSCORE'; - -export function transformArguments( - key: RedisCommandArgument, - min: string | number, - max: string | number, - options?: ZRangeByScoreOptions -): RedisCommandArguments { - return [ - ...transformZRangeByScoreArguments(key, min, max, options), - 'WITHSCORES' - ]; -} - -export { transformSortedSetWithScoresReply as transformReply } from './generic-transformers'; +export default { + FIRST_KEY_INDEX: ZRANGEBYSCORE.FIRST_KEY_INDEX, + IS_READ_ONLY: ZRANGEBYSCORE.IS_READ_ONLY, + transformArguments(...args: Parameters) { + const redisArgs = ZRANGEBYSCORE.transformArguments(...args); + redisArgs.push('WITHSCORES'); + return redisArgs; + }, + transformReply: transformSortedSetReply +} as const satisfies Command; diff --git a/packages/client/lib/commands/ZRANGESTORE.spec.ts b/packages/client/lib/commands/ZRANGESTORE.spec.ts index 7af253e539f..51315d3463b 100644 --- a/packages/client/lib/commands/ZRANGESTORE.spec.ts +++ b/packages/client/lib/commands/ZRANGESTORE.spec.ts @@ -1,92 +1,81 @@ -import { strict as assert } from 'assert'; +import { strict as assert } from 'node:assert'; import testUtils, { GLOBAL } from '../test-utils'; -import { transformArguments, transformReply } from './ZRANGESTORE'; +import ZRANGESTORE from './ZRANGESTORE'; describe('ZRANGESTORE', () => { - testUtils.isVersionGreaterThanHook([6, 2]); + testUtils.isVersionGreaterThanHook([6, 2]); - describe('transformArguments', () => { - it('simple', () => { - assert.deepEqual( - transformArguments('dst', 'src', 0, 1), - ['ZRANGESTORE', 'dst', 'src', '0', '1'] - ); - }); - - it('with BYSCORE', () => { - assert.deepEqual( - transformArguments('dst', 'src', 0, 1, { - BY: 'SCORE' - }), - ['ZRANGESTORE', 'dst', 'src', '0', '1', 'BYSCORE'] - ); - }); + describe('transformArguments', () => { + it('simple', () => { + assert.deepEqual( + ZRANGESTORE.transformArguments('destination', 'source', 0, 1), + ['ZRANGESTORE', 'destination', 'source', '0', '1'] + ); + }); - it('with BYLEX', () => { - assert.deepEqual( - transformArguments('dst', 'src', 0, 1, { - BY: 'LEX' - }), - ['ZRANGESTORE', 'dst', 'src', '0', '1', 'BYLEX'] - ); - }); + it('with BYSCORE', () => { + assert.deepEqual( + ZRANGESTORE.transformArguments('destination', 'source', 0, 1, { + BY: 'SCORE' + }), + ['ZRANGESTORE', 'destination', 'source', '0', '1', 'BYSCORE'] + ); + }); - it('with REV', () => { - assert.deepEqual( - transformArguments('dst', 'src', 0, 1, { - REV: true - }), - ['ZRANGESTORE', 'dst', 'src', '0', '1', 'REV'] - ); - }); + it('with BYLEX', () => { + assert.deepEqual( + ZRANGESTORE.transformArguments('destination', 'source', 0, 1, { + BY: 'LEX' + }), + ['ZRANGESTORE', 'destination', 'source', '0', '1', 'BYLEX'] + ); + }); - it('with LIMIT', () => { - assert.deepEqual( - transformArguments('dst', 'src', 0, 1, { - LIMIT: { - offset: 0, - count: 1 - } - }), - ['ZRANGESTORE', 'dst', 'src', '0', '1', 'LIMIT', '0', '1'] - ); - }); + it('with REV', () => { + assert.deepEqual( + ZRANGESTORE.transformArguments('destination', 'source', 0, 1, { + REV: true + }), + ['ZRANGESTORE', 'destination', 'source', '0', '1', 'REV'] + ); + }); - it('with BY & REV & LIMIT', () => { - assert.deepEqual( - transformArguments('dst', 'src', 0, 1, { - BY: 'SCORE', - REV: true, - LIMIT: { - offset: 0, - count: 1 - }, - WITHSCORES: true - }), - ['ZRANGESTORE', 'dst', 'src', '0', '1', 'BYSCORE', 'REV', 'LIMIT', '0', '1', 'WITHSCORES'] - ); - }); + it('with LIMIT', () => { + assert.deepEqual( + ZRANGESTORE.transformArguments('destination', 'source', 0, 1, { + LIMIT: { + offset: 0, + count: 1 + } + }), + ['ZRANGESTORE', 'destination', 'source', '0', '1', 'LIMIT', '0', '1'] + ); }); - describe('transformReply', () => { - it('should throw TypeError when reply is not a number', () => { - assert.throws( - // eslint-disable-next-line @typescript-eslint/no-explicit-any - () => (transformReply as any)([]), - TypeError - ); - }); + it('with BY & REV & LIMIT', () => { + assert.deepEqual( + ZRANGESTORE.transformArguments('destination', 'source', 0, 1, { + BY: 'SCORE', + REV: true, + LIMIT: { + offset: 0, + count: 1 + } + }), + ['ZRANGESTORE', 'destination', 'source', '0', '1', 'BYSCORE', 'REV', 'LIMIT', '0', '1'] + ); }); + }); - testUtils.testWithClient('client.zRangeStore', async client => { - await client.zAdd('src', { - score: 0.5, - value: 'value' - }); + testUtils.testWithClient('client.zRangeStore', async client => { + const [, reply] = await Promise.all([ + client.zAdd('{tag}source', { + score: 1, + value: '1' + }), + client.zRangeStore('{tag}destination', '{tag}source', 0, 1) + ]); - assert.equal( - await client.zRangeStore('dst', 'src', 0, 1), - 1 - ); - }, GLOBAL.SERVERS.OPEN); + assert.equal(reply, 1); + }, GLOBAL.SERVERS.OPEN); }); diff --git a/packages/client/lib/commands/ZRANGESTORE.ts b/packages/client/lib/commands/ZRANGESTORE.ts index 28067ceabe0..96f10120b87 100644 --- a/packages/client/lib/commands/ZRANGESTORE.ts +++ b/packages/client/lib/commands/ZRANGESTORE.ts @@ -1,62 +1,52 @@ -import { RedisCommandArgument, RedisCommandArguments } from '.'; -import { transformStringNumberInfinityArgument } from './generic-transformers'; - -export const FIRST_KEY_INDEX = 1; - -interface ZRangeStoreOptions { - BY?: 'SCORE' | 'LEX'; - REV?: true; - LIMIT?: { - offset: number; - count: number; - }; - WITHSCORES?: true; +import { RedisArgument, NumberReply, Command } from '../RESP/types'; +import { transformStringDoubleArgument } from './generic-transformers'; + +export interface ZRangeStoreOptions { + BY?: 'SCORE' | 'LEX'; + REV?: true; + LIMIT?: { + offset: number; + count: number; + }; } -export function transformArguments( - dst: RedisCommandArgument, - src: RedisCommandArgument, - min: RedisCommandArgument | number, - max: RedisCommandArgument | number, +export default { + FIRST_KEY_INDEX: 1, + IS_READ_ONLY: false, + transformArguments( + destination: RedisArgument, + source: RedisArgument, + min: RedisArgument | number, + max: RedisArgument | number, options?: ZRangeStoreOptions -): RedisCommandArguments { + ) { const args = [ - 'ZRANGESTORE', - dst, - src, - transformStringNumberInfinityArgument(min), - transformStringNumberInfinityArgument(max) + 'ZRANGESTORE', + destination, + source, + transformStringDoubleArgument(min), + transformStringDoubleArgument(max) ]; switch (options?.BY) { - case 'SCORE': - args.push('BYSCORE'); - break; + case 'SCORE': + args.push('BYSCORE'); + break; - case 'LEX': - args.push('BYLEX'); - break; + case 'LEX': + args.push('BYLEX'); + break; } if (options?.REV) { - args.push('REV'); + args.push('REV'); } if (options?.LIMIT) { - args.push('LIMIT', options.LIMIT.offset.toString(), options.LIMIT.count.toString()); - } - - if (options?.WITHSCORES) { - args.push('WITHSCORES'); + args.push('LIMIT', options.LIMIT.offset.toString(), options.LIMIT.count.toString()); } return args; -} - -export function transformReply(reply: number): number { - if (typeof reply !== 'number') { - throw new TypeError(`Upgrade to Redis 6.2.5 and up (https://github.com/redis/redis/pull/9089)`); - } - - return reply; -} + }, + transformReply: undefined as unknown as () => NumberReply +} as const satisfies Command; diff --git a/packages/client/lib/commands/ZRANGE_WITHSCORES.spec.ts b/packages/client/lib/commands/ZRANGE_WITHSCORES.spec.ts index d9b07e19dda..038c150a675 100644 --- a/packages/client/lib/commands/ZRANGE_WITHSCORES.spec.ts +++ b/packages/client/lib/commands/ZRANGE_WITHSCORES.spec.ts @@ -1,65 +1,75 @@ -import { strict as assert } from 'assert'; +import { strict as assert } from 'node:assert'; import testUtils, { GLOBAL } from '../test-utils'; -import { transformArguments } from './ZRANGE_WITHSCORES'; +import ZRANGE_WITHSCORES from './ZRANGE_WITHSCORES'; describe('ZRANGE WITHSCORES', () => { - describe('transformArguments', () => { - it('simple', () => { - assert.deepEqual( - transformArguments('src', 0, 1), - ['ZRANGE', 'src', '0', '1', 'WITHSCORES'] - ); - }); + describe('transformArguments', () => { + it('simple', () => { + assert.deepEqual( + ZRANGE_WITHSCORES.transformArguments('src', 0, 1), + ['ZRANGE', 'src', '0', '1', 'WITHSCORES'] + ); + }); - it('with BY', () => { - assert.deepEqual( - transformArguments('src', 0, 1, { - BY: 'SCORE' - }), - ['ZRANGE', 'src', '0', '1', 'BYSCORE', 'WITHSCORES'] - ); - }); + it('with BY', () => { + assert.deepEqual( + ZRANGE_WITHSCORES.transformArguments('src', 0, 1, { + BY: 'SCORE' + }), + ['ZRANGE', 'src', '0', '1', 'BYSCORE', 'WITHSCORES'] + ); + }); - it('with REV', () => { - assert.deepEqual( - transformArguments('src', 0, 1, { - REV: true - }), - ['ZRANGE', 'src', '0', '1', 'REV', 'WITHSCORES'] - ); - }); + it('with REV', () => { + assert.deepEqual( + ZRANGE_WITHSCORES.transformArguments('src', 0, 1, { + REV: true + }), + ['ZRANGE', 'src', '0', '1', 'REV', 'WITHSCORES'] + ); + }); - it('with LIMIT', () => { - assert.deepEqual( - transformArguments('src', 0, 1, { - LIMIT: { - offset: 0, - count: 1 - } - }), - ['ZRANGE', 'src', '0', '1', 'LIMIT', '0', '1', 'WITHSCORES'] - ); - }); + it('with LIMIT', () => { + assert.deepEqual( + ZRANGE_WITHSCORES.transformArguments('src', 0, 1, { + LIMIT: { + offset: 0, + count: 1 + } + }), + ['ZRANGE', 'src', '0', '1', 'LIMIT', '0', '1', 'WITHSCORES'] + ); + }); - it('with BY & REV & LIMIT', () => { - assert.deepEqual( - transformArguments('src', 0, 1, { - BY: 'SCORE', - REV: true, - LIMIT: { - offset: 0, - count: 1 - } - }), - ['ZRANGE', 'src', '0', '1', 'BYSCORE', 'REV', 'LIMIT', '0', '1', 'WITHSCORES'] - ); - }); + it('with BY & REV & LIMIT', () => { + assert.deepEqual( + ZRANGE_WITHSCORES.transformArguments('src', 0, 1, { + BY: 'SCORE', + REV: true, + LIMIT: { + offset: 0, + count: 1 + } + }), + ['ZRANGE', 'src', '0', '1', 'BYSCORE', 'REV', 'LIMIT', '0', '1', 'WITHSCORES'] + ); }); + }); + + testUtils.testAll('zRangeWithScores', async client => { + const members = [{ + value: '1', + score: 1 + }]; + + const [, reply] = await Promise.all([ + client.zAdd('key', members), + client.zRangeWithScores('key', 0, 1) + ]); - testUtils.testWithClient('client.zRangeWithScores', async client => { - assert.deepEqual( - await client.zRangeWithScores('src', 0, 1), - [] - ); - }, GLOBAL.SERVERS.OPEN); + assert.deepEqual(reply, members); + }, { + client: GLOBAL.SERVERS.OPEN, + cluster: GLOBAL.CLUSTERS.OPEN + }); }); diff --git a/packages/client/lib/commands/ZRANGE_WITHSCORES.ts b/packages/client/lib/commands/ZRANGE_WITHSCORES.ts index 23ea4d6337c..cfa90e99ea4 100644 --- a/packages/client/lib/commands/ZRANGE_WITHSCORES.ts +++ b/packages/client/lib/commands/ZRANGE_WITHSCORES.ts @@ -1,13 +1,15 @@ -import { RedisCommandArguments } from '.'; -import { transformArguments as transformZRangeArguments } from './ZRANGE'; +import { Command } from '../RESP/types'; +import ZRANGE from './ZRANGE'; +import { transformSortedSetReply } from './generic-transformers'; -export { FIRST_KEY_INDEX, IS_READ_ONLY } from './ZRANGE'; +export default { + FIRST_KEY_INDEX: ZRANGE.FIRST_KEY_INDEX, + IS_READ_ONLY: ZRANGE.IS_READ_ONLY, + transformArguments(...args: Parameters) { + const redisArgs = ZRANGE.transformArguments(...args); + redisArgs.push('WITHSCORES'); + return redisArgs; + }, + transformReply: transformSortedSetReply +} as const satisfies Command; -export function transformArguments(...args: Parameters): RedisCommandArguments { - return [ - ...transformZRangeArguments(...args), - 'WITHSCORES' - ]; -} - -export { transformSortedSetWithScoresReply as transformReply } from './generic-transformers'; diff --git a/packages/client/lib/commands/ZRANK.spec.ts b/packages/client/lib/commands/ZRANK.spec.ts index 0c81517a7d6..9341709bda3 100644 --- a/packages/client/lib/commands/ZRANK.spec.ts +++ b/packages/client/lib/commands/ZRANK.spec.ts @@ -1,19 +1,22 @@ -import { strict as assert } from 'assert'; +import { strict as assert } from 'node:assert'; import testUtils, { GLOBAL } from '../test-utils'; -import { transformArguments } from './ZRANK'; +import ZRANK from './ZRANK'; describe('ZRANK', () => { - it('transformArguments', () => { - assert.deepEqual( - transformArguments('key', 'member'), - ['ZRANK', 'key', 'member'] - ); - }); + it('transformArguments', () => { + assert.deepEqual( + ZRANK.transformArguments('key', 'member'), + ['ZRANK', 'key', 'member'] + ); + }); - testUtils.testWithClient('client.zRank', async client => { - assert.equal( - await client.zRank('key', 'member'), - null - ); - }, GLOBAL.SERVERS.OPEN); + testUtils.testAll('zRank', async client => { + assert.equal( + await client.zRank('key', 'member'), + null + ); + }, { + client: GLOBAL.SERVERS.OPEN, + cluster: GLOBAL.CLUSTERS.OPEN + }); }); diff --git a/packages/client/lib/commands/ZRANK.ts b/packages/client/lib/commands/ZRANK.ts index 33439ea4b55..11184c0a28f 100644 --- a/packages/client/lib/commands/ZRANK.ts +++ b/packages/client/lib/commands/ZRANK.ts @@ -1,14 +1,10 @@ -import { RedisCommandArgument, RedisCommandArguments } from '.'; +import { RedisArgument, NumberReply, NullReply, Command } from '../RESP/types'; -export const FIRST_KEY_INDEX = 1; - -export const IS_READ_ONLY = true; - -export function transformArguments( - key: RedisCommandArgument, - member: RedisCommandArgument -): RedisCommandArguments { +export default { + FIRST_KEY_INDEX: 1, + IS_READ_ONLY: true, + transformArguments(key: RedisArgument, member: RedisArgument) { return ['ZRANK', key, member]; -} - -export declare function transformReply(): number | null; + }, + transformReply: undefined as unknown as () => NumberReply | NullReply +} as const satisfies Command; diff --git a/packages/client/lib/commands/ZRANK_WITHSCORE.spec.ts b/packages/client/lib/commands/ZRANK_WITHSCORE.spec.ts new file mode 100644 index 00000000000..b571e0f7071 --- /dev/null +++ b/packages/client/lib/commands/ZRANK_WITHSCORE.spec.ts @@ -0,0 +1,46 @@ +import { strict as assert } from 'node:assert'; +import testUtils, { GLOBAL } from '../test-utils'; +import ZRANK_WITHSCORE from './ZRANK_WITHSCORE'; + +describe('ZRANK WITHSCORE', () => { + testUtils.isVersionGreaterThanHook([7, 2]); + + it('transformArguments', () => { + assert.deepEqual( + ZRANK_WITHSCORE.transformArguments('key', 'member'), + ['ZRANK', 'key', 'member', 'WITHSCORE'] + ); + }); + + testUtils.testAll('zRankWithScore - null', async client => { + assert.equal( + await client.zRankWithScore('key', 'member'), + null + ); + }, { + client: GLOBAL.SERVERS.OPEN, + cluster: GLOBAL.CLUSTERS.OPEN + }); + + testUtils.testAll('zRankWithScore - with member', async client => { + const member = { + value: '1', + score: 1 + } + + const [, reply] = await Promise.all([ + client.zAdd('key', member), + client.zRankWithScore('key', member.value) + ]) + assert.deepEqual( + reply, + { + rank: 0, + score: 1 + } + ); + }, { + client: GLOBAL.SERVERS.OPEN, + cluster: GLOBAL.CLUSTERS.OPEN + }); +}); diff --git a/packages/client/lib/commands/ZRANK_WITHSCORE.ts b/packages/client/lib/commands/ZRANK_WITHSCORE.ts new file mode 100644 index 00000000000..39c788535e3 --- /dev/null +++ b/packages/client/lib/commands/ZRANK_WITHSCORE.ts @@ -0,0 +1,30 @@ +import { NullReply, TuplesReply, NumberReply, BlobStringReply, DoubleReply, UnwrapReply, Command } from '../RESP/types'; +import ZRANK from './ZRANK'; + +export default { + FIRST_KEY_INDEX: ZRANK.FIRST_KEY_INDEX, + IS_READ_ONLY: ZRANK.IS_READ_ONLY, + transformArguments(...args: Parameters) { + const redisArgs = ZRANK.transformArguments(...args); + redisArgs.push('WITHSCORE'); + return redisArgs; + }, + transformReply: { + 2: (reply: UnwrapReply>) => { + if (reply === null) return null; + + return { + rank: reply[0], + score: Number(reply[1]) + }; + }, + 3: (reply: UnwrapReply>) => { + if (reply === null) return null; + + return { + rank: reply[0], + score: reply[1] + }; + } + } +} as const satisfies Command; diff --git a/packages/client/lib/commands/ZREM.spec.ts b/packages/client/lib/commands/ZREM.spec.ts index 3ac001708a0..4b203c9f4eb 100644 --- a/packages/client/lib/commands/ZREM.spec.ts +++ b/packages/client/lib/commands/ZREM.spec.ts @@ -1,28 +1,31 @@ -import { strict as assert } from 'assert'; +import { strict as assert } from 'node:assert'; import testUtils, { GLOBAL } from '../test-utils'; -import { transformArguments } from './ZREM'; +import ZREM from './ZREM'; describe('ZREM', () => { - describe('transformArguments', () => { - it('string', () => { - assert.deepEqual( - transformArguments('key', 'member'), - ['ZREM', 'key', 'member'] - ); - }); + describe('transformArguments', () => { + it('string', () => { + assert.deepEqual( + ZREM.transformArguments('key', 'member'), + ['ZREM', 'key', 'member'] + ); + }); - it('array', () => { - assert.deepEqual( - transformArguments('key', ['1', '2']), - ['ZREM', 'key', '1', '2'] - ); - }); + it('array', () => { + assert.deepEqual( + ZREM.transformArguments('key', ['1', '2']), + ['ZREM', 'key', '1', '2'] + ); }); + }); - testUtils.testWithClient('client.zRem', async client => { - assert.equal( - await client.zRem('key', 'member'), - 0 - ); - }, GLOBAL.SERVERS.OPEN); + testUtils.testAll('zRem', async client => { + assert.equal( + await client.zRem('key', 'member'), + 0 + ); + }, { + client: GLOBAL.SERVERS.OPEN, + cluster: GLOBAL.CLUSTERS.OPEN + }); }); diff --git a/packages/client/lib/commands/ZREM.ts b/packages/client/lib/commands/ZREM.ts index 7ab92c4a78f..54f55841fce 100644 --- a/packages/client/lib/commands/ZREM.ts +++ b/packages/client/lib/commands/ZREM.ts @@ -1,13 +1,14 @@ -import { RedisCommandArgument, RedisCommandArguments } from '.'; -import { pushVerdictArguments } from './generic-transformers'; +import { RedisArgument, NumberReply, Command } from '../RESP/types'; +import { RedisVariadicArgument, pushVariadicArguments } from './generic-transformers'; -export const FIRST_KEY_INDEX = 1; - -export function transformArguments( - key: RedisCommandArgument, - member: RedisCommandArgument | Array -): RedisCommandArguments { - return pushVerdictArguments(['ZREM', key], member); -} - -export declare function transformReply(): number; +export default { + FIRST_KEY_INDEX: 1, + IS_READ_ONLY: false, + transformArguments( + key: RedisArgument, + member: RedisVariadicArgument + ) { + return pushVariadicArguments(['ZREM', key], member); + }, + transformReply: undefined as unknown as () => NumberReply +} as const satisfies Command; diff --git a/packages/client/lib/commands/ZREMRANGEBYLEX.spec.ts b/packages/client/lib/commands/ZREMRANGEBYLEX.spec.ts index b59c9e9f3b0..9f29c3cdcf7 100644 --- a/packages/client/lib/commands/ZREMRANGEBYLEX.spec.ts +++ b/packages/client/lib/commands/ZREMRANGEBYLEX.spec.ts @@ -1,19 +1,22 @@ -import { strict as assert } from 'assert'; +import { strict as assert } from 'node:assert'; import testUtils, { GLOBAL } from '../test-utils'; -import { transformArguments } from './ZREMRANGEBYLEX'; +import ZREMRANGEBYLEX from './ZREMRANGEBYLEX'; describe('ZREMRANGEBYLEX', () => { - it('transformArguments', () => { - assert.deepEqual( - transformArguments('key', '[a', '[b'), - ['ZREMRANGEBYLEX', 'key', '[a', '[b'] - ); - }); + it('transformArguments', () => { + assert.deepEqual( + ZREMRANGEBYLEX.transformArguments('key', '[a', '[b'), + ['ZREMRANGEBYLEX', 'key', '[a', '[b'] + ); + }); - testUtils.testWithClient('client.zRemRangeByLex', async client => { - assert.equal( - await client.zRemRangeByLex('key', '[a', '[b'), - 0 - ); - }, GLOBAL.SERVERS.OPEN); + testUtils.testAll('zRemRangeByLex', async client => { + assert.equal( + await client.zRemRangeByLex('key', '[a', '[b'), + 0 + ); + }, { + client: GLOBAL.SERVERS.OPEN, + cluster: GLOBAL.CLUSTERS.OPEN + }); }); diff --git a/packages/client/lib/commands/ZREMRANGEBYLEX.ts b/packages/client/lib/commands/ZREMRANGEBYLEX.ts index f1f3908f538..e3cd7013ac2 100644 --- a/packages/client/lib/commands/ZREMRANGEBYLEX.ts +++ b/packages/client/lib/commands/ZREMRANGEBYLEX.ts @@ -1,19 +1,20 @@ -import { RedisCommandArgument, RedisCommandArguments } from '.'; -import { transformStringNumberInfinityArgument } from './generic-transformers'; +import { NumberReply, Command, RedisArgument } from '../RESP/types'; +import { transformStringDoubleArgument } from './generic-transformers'; -export const FIRST_KEY_INDEX = 1; - -export function transformArguments( - key: RedisCommandArgument, - min: RedisCommandArgument | number, - max: RedisCommandArgument | number -): RedisCommandArguments { +export default { + FIRST_KEY_INDEX: 1, + IS_READ_ONLY: false, + transformArguments( + key: RedisArgument, + min: RedisArgument | number, + max: RedisArgument | number + ) { return [ - 'ZREMRANGEBYLEX', - key, - transformStringNumberInfinityArgument(min), - transformStringNumberInfinityArgument(max) + 'ZREMRANGEBYLEX', + key, + transformStringDoubleArgument(min), + transformStringDoubleArgument(max) ]; -} - -export declare function transformReply(): number; + }, + transformReply: undefined as unknown as () => NumberReply +} as const satisfies Command; diff --git a/packages/client/lib/commands/ZREMRANGEBYRANK.spec.ts b/packages/client/lib/commands/ZREMRANGEBYRANK.spec.ts index c659dadb790..12627083e1a 100644 --- a/packages/client/lib/commands/ZREMRANGEBYRANK.spec.ts +++ b/packages/client/lib/commands/ZREMRANGEBYRANK.spec.ts @@ -1,19 +1,22 @@ -import { strict as assert } from 'assert'; +import { strict as assert } from 'node:assert'; import testUtils, { GLOBAL } from '../test-utils'; -import { transformArguments } from './ZREMRANGEBYRANK'; +import ZREMRANGEBYRANK from './ZREMRANGEBYRANK'; describe('ZREMRANGEBYRANK', () => { - it('transformArguments', () => { - assert.deepEqual( - transformArguments('key', 0, 1), - ['ZREMRANGEBYRANK', 'key', '0', '1'] - ); - }); + it('transformArguments', () => { + assert.deepEqual( + ZREMRANGEBYRANK.transformArguments('key', 0, 1), + ['ZREMRANGEBYRANK', 'key', '0', '1'] + ); + }); - testUtils.testWithClient('client.zRemRangeByRank', async client => { - assert.equal( - await client.zRemRangeByRank('key', 0, 1), - 0 - ); - }, GLOBAL.SERVERS.OPEN); + testUtils.testAll('zRemRangeByRank', async client => { + assert.equal( + await client.zRemRangeByRank('key', 0, 1), + 0 + ); + }, { + client: GLOBAL.SERVERS.OPEN, + cluster: GLOBAL.CLUSTERS.OPEN + }); }); diff --git a/packages/client/lib/commands/ZREMRANGEBYRANK.ts b/packages/client/lib/commands/ZREMRANGEBYRANK.ts index c50d06e3bf6..986de33060e 100644 --- a/packages/client/lib/commands/ZREMRANGEBYRANK.ts +++ b/packages/client/lib/commands/ZREMRANGEBYRANK.ts @@ -1,13 +1,13 @@ -import { RedisCommandArgument, RedisCommandArguments } from '.'; +import { RedisArgument, NumberReply, Command } from '../RESP/types'; -export const FIRST_KEY_INDEX = 1; - -export function transformArguments( - key: RedisCommandArgument, +export default { + FIRST_KEY_INDEX: 1, + IS_READ_ONLY: false, + transformArguments( + key: RedisArgument, start: number, - stop: number -): RedisCommandArguments { + stop: number) { return ['ZREMRANGEBYRANK', key, start.toString(), stop.toString()]; -} - -export declare function transformReply(): number; + }, + transformReply: undefined as unknown as () => NumberReply +} as const satisfies Command; diff --git a/packages/client/lib/commands/ZREMRANGEBYSCORE.spec.ts b/packages/client/lib/commands/ZREMRANGEBYSCORE.spec.ts index 988fd7690c9..fb3ba4e718c 100644 --- a/packages/client/lib/commands/ZREMRANGEBYSCORE.spec.ts +++ b/packages/client/lib/commands/ZREMRANGEBYSCORE.spec.ts @@ -1,19 +1,22 @@ -import { strict as assert } from 'assert'; +import { strict as assert } from 'node:assert'; import testUtils, { GLOBAL } from '../test-utils'; -import { transformArguments } from './ZREMRANGEBYSCORE'; +import ZREMRANGEBYSCORE from './ZREMRANGEBYSCORE'; describe('ZREMRANGEBYSCORE', () => { - it('transformArguments', () => { - assert.deepEqual( - transformArguments('key', 0, 1), - ['ZREMRANGEBYSCORE', 'key', '0', '1'] - ); - }); + it('transformArguments', () => { + assert.deepEqual( + ZREMRANGEBYSCORE.transformArguments('key', 0, 1), + ['ZREMRANGEBYSCORE', 'key', '0', '1'] + ); + }); - testUtils.testWithClient('client.zRemRangeByScore', async client => { - assert.equal( - await client.zRemRangeByScore('key', 0, 1), - 0 - ); - }, GLOBAL.SERVERS.OPEN); + testUtils.testAll('zRemRangeByScore', async client => { + assert.equal( + await client.zRemRangeByScore('key', 0, 1), + 0 + ); + }, { + client: GLOBAL.SERVERS.OPEN, + cluster: GLOBAL.CLUSTERS.OPEN + }); }); diff --git a/packages/client/lib/commands/ZREMRANGEBYSCORE.ts b/packages/client/lib/commands/ZREMRANGEBYSCORE.ts index 12d1eff811e..7050f2627a7 100644 --- a/packages/client/lib/commands/ZREMRANGEBYSCORE.ts +++ b/packages/client/lib/commands/ZREMRANGEBYSCORE.ts @@ -1,19 +1,20 @@ -import { RedisCommandArgument, RedisCommandArguments } from '.'; -import { transformStringNumberInfinityArgument } from './generic-transformers'; +import { RedisArgument, NumberReply, Command } from '../RESP/types'; +import { transformStringDoubleArgument } from './generic-transformers'; -export const FIRST_KEY_INDEX = 1; - -export function transformArguments( - key: RedisCommandArgument, - min: RedisCommandArgument | number, - max: RedisCommandArgument | number, -): RedisCommandArguments { +export default { + FIRST_KEY_INDEX: 1, + IS_READ_ONLY: false, + transformArguments( + key: RedisArgument, + min: RedisArgument | number, + max: RedisArgument | number, + ) { return [ - 'ZREMRANGEBYSCORE', - key, - transformStringNumberInfinityArgument(min), - transformStringNumberInfinityArgument(max) + 'ZREMRANGEBYSCORE', + key, + transformStringDoubleArgument(min), + transformStringDoubleArgument(max) ]; -} - -export declare function transformReply(): number; + }, + transformReply: undefined as unknown as () => NumberReply +} as const satisfies Command; diff --git a/packages/client/lib/commands/ZREVRANK.spec.ts b/packages/client/lib/commands/ZREVRANK.spec.ts index d9fef0d70a4..418773b6003 100644 --- a/packages/client/lib/commands/ZREVRANK.spec.ts +++ b/packages/client/lib/commands/ZREVRANK.spec.ts @@ -1,19 +1,22 @@ -import { strict as assert } from 'assert'; +import { strict as assert } from 'node:assert'; import testUtils, { GLOBAL } from '../test-utils'; -import { transformArguments } from './ZREVRANK'; +import ZREVRANK from './ZREVRANK'; describe('ZREVRANK', () => { - it('transformArguments', () => { - assert.deepEqual( - transformArguments('key', 'member'), - ['ZREVRANK', 'key', 'member'] - ); - }); + it('transformArguments', () => { + assert.deepEqual( + ZREVRANK.transformArguments('key', 'member'), + ['ZREVRANK', 'key', 'member'] + ); + }); - testUtils.testWithClient('client.zRevRank', async client => { - assert.equal( - await client.zRevRank('key', 'member'), - null - ); - }, GLOBAL.SERVERS.OPEN); + testUtils.testAll('zRevRank', async client => { + assert.equal( + await client.zRevRank('key', 'member'), + null + ); + }, { + client: GLOBAL.SERVERS.OPEN, + cluster: GLOBAL.CLUSTERS.OPEN + }); }); diff --git a/packages/client/lib/commands/ZREVRANK.ts b/packages/client/lib/commands/ZREVRANK.ts index b88936c0c92..3bf52d21de5 100644 --- a/packages/client/lib/commands/ZREVRANK.ts +++ b/packages/client/lib/commands/ZREVRANK.ts @@ -1,14 +1,10 @@ -import { RedisCommandArgument, RedisCommandArguments } from '.'; +import { NumberReply, NullReply, Command, RedisArgument } from '../RESP/types'; -export const FIRST_KEY_INDEX = 1; - -export const IS_READ_ONLY = true; - -export function transformArguments( - key: RedisCommandArgument, - member: RedisCommandArgument -): RedisCommandArguments { +export default { + FIRST_KEY_INDEX: 1, + IS_READ_ONLY: true, + transformArguments(key: RedisArgument, member: RedisArgument) { return ['ZREVRANK', key, member]; -} - -export declare function transformReply(): number | null; + }, + transformReply: undefined as unknown as () => NumberReply | NullReply +} as const satisfies Command; diff --git a/packages/client/lib/commands/ZSCAN.spec.ts b/packages/client/lib/commands/ZSCAN.spec.ts index afa221a1ef3..ebeaad2a4d0 100644 --- a/packages/client/lib/commands/ZSCAN.spec.ts +++ b/packages/client/lib/commands/ZSCAN.spec.ts @@ -1,77 +1,52 @@ -import { strict as assert } from 'assert'; +import { strict as assert } from 'node:assert'; import testUtils, { GLOBAL } from '../test-utils'; -import { transformArguments, transformReply } from './ZSCAN'; +import ZSCAN from './ZSCAN'; describe('ZSCAN', () => { - describe('transformArguments', () => { - it('cusror only', () => { - assert.deepEqual( - transformArguments('key', 0), - ['ZSCAN', 'key', '0'] - ); - }); - - it('with MATCH', () => { - assert.deepEqual( - transformArguments('key', 0, { - MATCH: 'pattern' - }), - ['ZSCAN', 'key', '0', 'MATCH', 'pattern'] - ); - }); - - it('with COUNT', () => { - assert.deepEqual( - transformArguments('key', 0, { - COUNT: 1 - }), - ['ZSCAN', 'key', '0', 'COUNT', '1'] - ); - }); + describe('transformArguments', () => { + it('cusror only', () => { + assert.deepEqual( + ZSCAN.transformArguments('key', '0'), + ['ZSCAN', 'key', '0'] + ); + }); - it('with MATCH & COUNT', () => { - assert.deepEqual( - transformArguments('key', 0, { - MATCH: 'pattern', - COUNT: 1 - }), - ['ZSCAN', 'key', '0', 'MATCH', 'pattern', 'COUNT', '1'] - ); - }); + it('with MATCH', () => { + assert.deepEqual( + ZSCAN.transformArguments('key', '0', { + MATCH: 'pattern' + }), + ['ZSCAN', 'key', '0', 'MATCH', 'pattern'] + ); }); - describe('transformReply', () => { - it('without members', () => { - assert.deepEqual( - transformReply(['0', []]), - { - cursor: 0, - members: [] - } - ); - }); + it('with COUNT', () => { + assert.deepEqual( + ZSCAN.transformArguments('key', '0', { + COUNT: 1 + }), + ['ZSCAN', 'key', '0', 'COUNT', '1'] + ); + }); - it('with members', () => { - assert.deepEqual( - transformReply(['0', ['member', '-inf']]), - { - cursor: 0, - members: [{ - value: 'member', - score: -Infinity - }] - } - ); - }); + it('with MATCH & COUNT', () => { + assert.deepEqual( + ZSCAN.transformArguments('key', '0', { + MATCH: 'pattern', + COUNT: 1 + }), + ['ZSCAN', 'key', '0', 'MATCH', 'pattern', 'COUNT', '1'] + ); }); + }); - testUtils.testWithClient('client.zScan', async client => { - assert.deepEqual( - await client.zScan('key', 0), - { - cursor: 0, - members: [] - } - ); - }, GLOBAL.SERVERS.OPEN); + testUtils.testWithClient('zScan', async client => { + assert.deepEqual( + await client.zScan('key', '0'), + { + cursor: '0', + members: [] + } + ); + }, GLOBAL.SERVERS.OPEN); }); diff --git a/packages/client/lib/commands/ZSCAN.ts b/packages/client/lib/commands/ZSCAN.ts index f6fa17c2d4e..853cdf098f6 100644 --- a/packages/client/lib/commands/ZSCAN.ts +++ b/packages/client/lib/commands/ZSCAN.ts @@ -1,39 +1,26 @@ -import { RedisCommandArgument, RedisCommandArguments } from '.'; -import { ScanOptions, transformNumberInfinityReply, pushScanArguments, ZMember } from './generic-transformers'; +import { RedisArgument, ArrayReply, BlobStringReply, Command } from '../RESP/types'; +import { ScanCommonOptions, pushScanArguments } from './SCAN'; +import { transformSortedSetReply } from './generic-transformers'; -export const FIRST_KEY_INDEX = 1; - -export const IS_READ_ONLY = true; - -export function transformArguments( - key: RedisCommandArgument, - cursor: number, - options?: ScanOptions -): RedisCommandArguments { - return pushScanArguments([ - 'ZSCAN', - key - ], cursor, options); +export interface HScanEntry { + field: BlobStringReply; + value: BlobStringReply; } -type ZScanRawReply = [RedisCommandArgument, Array]; - -interface ZScanReply { - cursor: number; - members: Array; -} - -export function transformReply([cursor, rawMembers]: ZScanRawReply): ZScanReply { - const parsedMembers: Array = []; - for (let i = 0; i < rawMembers.length; i += 2) { - parsedMembers.push({ - value: rawMembers[i], - score: transformNumberInfinityReply(rawMembers[i + 1]) - }); - } - +export default { + FIRST_KEY_INDEX: 1, + IS_READ_ONLY: true, + transformArguments( + key: RedisArgument, + cursor: RedisArgument, + options?: ScanCommonOptions + ) { + return pushScanArguments(['ZSCAN', key], cursor, options); + }, + transformReply([cursor, rawMembers]: [BlobStringReply, ArrayReply]) { return { - cursor: Number(cursor), - members: parsedMembers + cursor, + members: transformSortedSetReply[2](rawMembers) }; -} + } +} as const satisfies Command; diff --git a/packages/client/lib/commands/ZSCORE.spec.ts b/packages/client/lib/commands/ZSCORE.spec.ts index fe2a1c6a7c5..3d8df6640c8 100644 --- a/packages/client/lib/commands/ZSCORE.spec.ts +++ b/packages/client/lib/commands/ZSCORE.spec.ts @@ -1,19 +1,22 @@ -import { strict as assert } from 'assert'; +import { strict as assert } from 'node:assert'; import testUtils, { GLOBAL } from '../test-utils'; -import { transformArguments } from './ZSCORE'; +import ZSCORE from './ZSCORE'; describe('ZSCORE', () => { - it('transformArguments', () => { - assert.deepEqual( - transformArguments('key', 'member'), - ['ZSCORE', 'key', 'member'] - ); - }); + it('transformArguments', () => { + assert.deepEqual( + ZSCORE.transformArguments('key', 'member'), + ['ZSCORE', 'key', 'member'] + ); + }); - testUtils.testWithClient('client.zScore', async client => { - assert.equal( - await client.zScore('key', 'member'), - null - ); - }, GLOBAL.SERVERS.OPEN); + testUtils.testAll('zScore', async client => { + assert.equal( + await client.zScore('key', 'member'), + null + ); + }, { + client: GLOBAL.SERVERS.OPEN, + cluster: GLOBAL.CLUSTERS.OPEN + }); }); diff --git a/packages/client/lib/commands/ZSCORE.ts b/packages/client/lib/commands/ZSCORE.ts index 118abc10850..0d3db752e1c 100644 --- a/packages/client/lib/commands/ZSCORE.ts +++ b/packages/client/lib/commands/ZSCORE.ts @@ -1,14 +1,12 @@ -import { RedisCommandArgument, RedisCommandArguments } from '.'; -export const FIRST_KEY_INDEX = 1; +import { RedisArgument, Command } from '../RESP/types'; +import { transformNullableDoubleReply } from './generic-transformers'; -export const IS_READ_ONLY = true; - -export function transformArguments( - key: RedisCommandArgument, - member: RedisCommandArgument -): RedisCommandArguments { +export default { + FIRST_KEY_INDEX: 1, + IS_READ_ONLY: true, + transformArguments(key: RedisArgument, member: RedisArgument) { return ['ZSCORE', key, member]; -} - -export { transformNumberInfinityNullReply as transformReply } from './generic-transformers'; + }, + transformReply: transformNullableDoubleReply +} as const satisfies Command; diff --git a/packages/client/lib/commands/ZUNION.spec.ts b/packages/client/lib/commands/ZUNION.spec.ts index c53498cbf65..f66bb7abc63 100644 --- a/packages/client/lib/commands/ZUNION.spec.ts +++ b/packages/client/lib/commands/ZUNION.spec.ts @@ -1,48 +1,65 @@ -import { strict as assert } from 'assert'; +import { strict as assert } from 'node:assert'; import testUtils, { GLOBAL } from '../test-utils'; -import { transformArguments } from './ZUNION'; +import ZUNION from './ZUNION'; describe('ZUNION', () => { - testUtils.isVersionGreaterThanHook([6, 2]); + testUtils.isVersionGreaterThanHook([6, 2]); - describe('transformArguments', () => { - it('key (string)', () => { - assert.deepEqual( - transformArguments('key'), - ['ZUNION', '1', 'key'] - ); - }); + describe('transformArguments', () => { + it('key (string)', () => { + assert.deepEqual( + ZUNION.transformArguments('key'), + ['ZUNION', '1', 'key'] + ); + }); - it('keys (array)', () => { - assert.deepEqual( - transformArguments(['1', '2']), - ['ZUNION', '2', '1', '2'] - ); - }); + it('keys (Array)', () => { + assert.deepEqual( + ZUNION.transformArguments(['1', '2']), + ['ZUNION', '2', '1', '2'] + ); + }); - it('with WEIGHTS', () => { - assert.deepEqual( - transformArguments('key', { - WEIGHTS: [1] - }), - ['ZUNION', '1', 'key', 'WEIGHTS', '1'] - ); - }); + it('key & weight', () => { + assert.deepEqual( + ZUNION.transformArguments({ + key: 'key', + weight: 1 + }), + ['ZUNION', '1', 'key', 'WEIGHTS', '1'] + ); + }); + + it('keys & weights', () => { + assert.deepEqual( + ZUNION.transformArguments([{ + key: 'a', + weight: 1 + }, { + key: 'b', + weight: 2 + }]), + ['ZUNION', '2', 'a', 'b', 'WEIGHTS', '1', '2'] + ); + }); - it('with AGGREGATE', () => { - assert.deepEqual( - transformArguments('key', { - AGGREGATE: 'SUM' - }), - ['ZUNION', '1', 'key', 'AGGREGATE', 'SUM'] - ); - }); + it('with AGGREGATE', () => { + assert.deepEqual( + ZUNION.transformArguments('key', { + AGGREGATE: 'SUM' + }), + ['ZUNION', '1', 'key', 'AGGREGATE', 'SUM'] + ); }); + }); - testUtils.testWithClient('client.zUnion', async client => { - assert.deepEqual( - await client.zUnion('key'), - [] - ); - }, GLOBAL.SERVERS.OPEN); + testUtils.testAll('zUnion', async client => { + assert.deepEqual( + await client.zUnion('key'), + [] + ); + }, { + client: GLOBAL.SERVERS.OPEN, + cluster: GLOBAL.CLUSTERS.OPEN + }); }); diff --git a/packages/client/lib/commands/ZUNION.ts b/packages/client/lib/commands/ZUNION.ts index f329348cc8b..09614b9dc01 100644 --- a/packages/client/lib/commands/ZUNION.ts +++ b/packages/client/lib/commands/ZUNION.ts @@ -1,30 +1,24 @@ -import { RedisCommandArgument, RedisCommandArguments } from '.'; -import { pushVerdictArgument } from './generic-transformers'; +import { ArrayReply, BlobStringReply, Command } from '../RESP/types'; +import { ZKeys, pushZKeysArguments } from './generic-transformers'; -export const FIRST_KEY_INDEX = 2; - -export const IS_READ_ONLY = true; - -interface ZUnionOptions { - WEIGHTS?: Array; - AGGREGATE?: 'SUM' | 'MIN' | 'MAX'; +export interface ZUnionOptions { + AGGREGATE?: 'SUM' | 'MIN' | 'MAX'; } -export function transformArguments( - keys: Array | RedisCommandArgument, +export default { + FIRST_KEY_INDEX: 2, + IS_READ_ONLY: true, + transformArguments( + keys: ZKeys, options?: ZUnionOptions -): RedisCommandArguments { - const args = pushVerdictArgument(['ZUNION'], keys); - - if (options?.WEIGHTS) { - args.push('WEIGHTS', ...options.WEIGHTS.map(weight => weight.toString())); - } + ) { + const args = pushZKeysArguments(['ZUNION'], keys); if (options?.AGGREGATE) { - args.push('AGGREGATE', options.AGGREGATE); + args.push('AGGREGATE', options.AGGREGATE); } return args; -} - -export declare function transformReply(): Array; + }, + transformReply: undefined as unknown as () => ArrayReply +} as const satisfies Command; diff --git a/packages/client/lib/commands/ZUNIONSTORE.spec.ts b/packages/client/lib/commands/ZUNIONSTORE.spec.ts index 8f11828b221..7a01e80f0c0 100644 --- a/packages/client/lib/commands/ZUNIONSTORE.spec.ts +++ b/packages/client/lib/commands/ZUNIONSTORE.spec.ts @@ -1,56 +1,63 @@ -import { strict as assert } from 'assert'; +import { strict as assert } from 'node:assert'; import testUtils, { GLOBAL } from '../test-utils'; -import { transformArguments } from './ZUNIONSTORE'; +import ZUNIONSTORE from './ZUNIONSTORE'; describe('ZUNIONSTORE', () => { - describe('transformArguments', () => { - it('key (string)', () => { - assert.deepEqual( - transformArguments('destination', 'key'), - ['ZUNIONSTORE', 'destination', '1', 'key'] - ); - }); + describe('transformArguments', () => { + it('key (string)', () => { + assert.deepEqual( + ZUNIONSTORE.transformArguments('destination', 'source'), + ['ZUNIONSTORE', 'destination', '1', 'source'] + ); + }); - it('keys (array)', () => { - assert.deepEqual( - transformArguments('destination', ['1', '2']), - ['ZUNIONSTORE', 'destination', '2', '1', '2'] - ); - }); + it('keys (Array)', () => { + assert.deepEqual( + ZUNIONSTORE.transformArguments('destination', ['1', '2']), + ['ZUNIONSTORE', 'destination', '2', '1', '2'] + ); + }); - it('with WEIGHTS', () => { - assert.deepEqual( - transformArguments('destination', 'key', { - WEIGHTS: [1] - }), - ['ZUNIONSTORE', 'destination', '1', 'key', 'WEIGHTS', '1'] - ); - }); + it('key & weight', () => { + assert.deepEqual( + ZUNIONSTORE.transformArguments('destination', { + key: 'source', + weight: 1 + }), + ['ZUNIONSTORE', 'destination', '1', 'source', 'WEIGHTS', '1'] + ); + }); - it('with AGGREGATE', () => { - assert.deepEqual( - transformArguments('destination', 'key', { - AGGREGATE: 'SUM' - }), - ['ZUNIONSTORE', 'destination', '1', 'key', 'AGGREGATE', 'SUM'] - ); - }); + it('keys & weights', () => { + assert.deepEqual( + ZUNIONSTORE.transformArguments('destination', [{ + key: 'a', + weight: 1 + }, { + key: 'b', + weight: 2 + }]), + ['ZUNIONSTORE', 'destination', '2', 'a', 'b', 'WEIGHTS', '1', '2'] + ); + }); - it('with WEIGHTS, AGGREGATE', () => { - assert.deepEqual( - transformArguments('destination', 'key', { - WEIGHTS: [1], - AGGREGATE: 'SUM' - }), - ['ZUNIONSTORE', 'destination', '1', 'key', 'WEIGHTS', '1', 'AGGREGATE', 'SUM'] - ); - }); + it('with AGGREGATE', () => { + assert.deepEqual( + ZUNIONSTORE.transformArguments('destination', 'source', { + AGGREGATE: 'SUM' + }), + ['ZUNIONSTORE', 'destination', '1', 'source', 'AGGREGATE', 'SUM'] + ); }); + }); - testUtils.testWithClient('client.zUnionStore', async client => { - assert.equal( - await client.zUnionStore('destination', 'key'), - 0 - ); - }, GLOBAL.SERVERS.OPEN); + testUtils.testAll('zUnionStore', async client => { + assert.equal( + await client.zUnionStore('{tag}destination', '{tag}key'), + 0 + ); + }, { + client: GLOBAL.SERVERS.OPEN, + cluster: GLOBAL.CLUSTERS.OPEN + }); }); diff --git a/packages/client/lib/commands/ZUNIONSTORE.ts b/packages/client/lib/commands/ZUNIONSTORE.ts index 2a42e21bc87..a14d3ba31c9 100644 --- a/packages/client/lib/commands/ZUNIONSTORE.ts +++ b/packages/client/lib/commands/ZUNIONSTORE.ts @@ -1,29 +1,25 @@ -import { RedisCommandArgument, RedisCommandArguments } from '.'; -import { pushVerdictArgument } from './generic-transformers'; +import { RedisArgument, NumberReply, Command, } from '../RESP/types'; +import { ZKeys, pushZKeysArguments } from './generic-transformers'; -export const FIRST_KEY_INDEX = 1; - -interface ZUnionOptions { - WEIGHTS?: Array; - AGGREGATE?: 'SUM' | 'MIN' | 'MAX'; +export interface ZUnionOptions { + AGGREGATE?: 'SUM' | 'MIN' | 'MAX'; } -export function transformArguments( - destination: RedisCommandArgument, - keys: Array | RedisCommandArgument, +export default { + FIRST_KEY_INDEX: 1, + IS_READ_ONLY: false, + transformArguments( + destination: RedisArgument, + keys: ZKeys, options?: ZUnionOptions -): RedisCommandArguments { - const args = pushVerdictArgument(['ZUNIONSTORE', destination], keys); - - if (options?.WEIGHTS) { - args.push('WEIGHTS', ...options.WEIGHTS.map(weight => weight.toString())); - } + ) { + const args = pushZKeysArguments(['ZUNIONSTORE', destination], keys); if (options?.AGGREGATE) { - args.push('AGGREGATE', options.AGGREGATE); + args.push('AGGREGATE', options.AGGREGATE); } return args; -} - -export declare function transformReply(): number; + }, + transformReply: undefined as unknown as () => NumberReply +} as const satisfies Command; diff --git a/packages/client/lib/commands/ZUNION_WITHSCORES.spec.ts b/packages/client/lib/commands/ZUNION_WITHSCORES.spec.ts index 3786a97963d..bbf3ec676e8 100644 --- a/packages/client/lib/commands/ZUNION_WITHSCORES.spec.ts +++ b/packages/client/lib/commands/ZUNION_WITHSCORES.spec.ts @@ -1,48 +1,65 @@ -import { strict as assert } from 'assert'; +import { strict as assert } from 'node:assert'; import testUtils, { GLOBAL } from '../test-utils'; -import { transformArguments } from './ZUNION_WITHSCORES'; +import ZUNION_WITHSCORES from './ZUNION_WITHSCORES'; describe('ZUNION WITHSCORES', () => { - testUtils.isVersionGreaterThanHook([6, 2]); + testUtils.isVersionGreaterThanHook([6, 2]); - describe('transformArguments', () => { - it('key (string)', () => { - assert.deepEqual( - transformArguments('key'), - ['ZUNION', '1', 'key', 'WITHSCORES'] - ); - }); + describe('transformArguments', () => { + it('key (string)', () => { + assert.deepEqual( + ZUNION_WITHSCORES.transformArguments('key'), + ['ZUNION', '1', 'key', 'WITHSCORES'] + ); + }); - it('keys (array)', () => { - assert.deepEqual( - transformArguments(['1', '2']), - ['ZUNION', '2', '1', '2', 'WITHSCORES'] - ); - }); + it('keys (Array)', () => { + assert.deepEqual( + ZUNION_WITHSCORES.transformArguments(['1', '2']), + ['ZUNION', '2', '1', '2', 'WITHSCORES'] + ); + }); - it('with WEIGHTS', () => { - assert.deepEqual( - transformArguments('key', { - WEIGHTS: [1] - }), - ['ZUNION', '1', 'key', 'WEIGHTS', '1', 'WITHSCORES'] - ); - }); + it('key & weight', () => { + assert.deepEqual( + ZUNION_WITHSCORES.transformArguments({ + key: 'key', + weight: 1 + }), + ['ZUNION', '1', 'key', 'WEIGHTS', '1', 'WITHSCORES'] + ); + }); + + it('keys & weights', () => { + assert.deepEqual( + ZUNION_WITHSCORES.transformArguments([{ + key: 'a', + weight: 1 + }, { + key: 'b', + weight: 2 + }]), + ['ZUNION', '2', 'a', 'b', 'WEIGHTS', '1', '2', 'WITHSCORES'] + ); + }); - it('with AGGREGATE', () => { - assert.deepEqual( - transformArguments('key', { - AGGREGATE: 'SUM' - }), - ['ZUNION', '1', 'key', 'AGGREGATE', 'SUM', 'WITHSCORES'] - ); - }); + it('with AGGREGATE', () => { + assert.deepEqual( + ZUNION_WITHSCORES.transformArguments('key', { + AGGREGATE: 'SUM' + }), + ['ZUNION', '1', 'key', 'AGGREGATE', 'SUM', 'WITHSCORES'] + ); }); + }); - testUtils.testWithClient('client.zUnionWithScores', async client => { - assert.deepEqual( - await client.zUnionWithScores('key'), - [] - ); - }, GLOBAL.SERVERS.OPEN); + testUtils.testAll('zUnionWithScores', async client => { + assert.deepEqual( + await client.zUnionWithScores('key'), + [] + ); + }, { + client: GLOBAL.SERVERS.OPEN, + cluster: GLOBAL.CLUSTERS.OPEN + }); }); diff --git a/packages/client/lib/commands/ZUNION_WITHSCORES.ts b/packages/client/lib/commands/ZUNION_WITHSCORES.ts index 168cc929ac8..d0895a3de76 100644 --- a/packages/client/lib/commands/ZUNION_WITHSCORES.ts +++ b/packages/client/lib/commands/ZUNION_WITHSCORES.ts @@ -1,13 +1,14 @@ -import { RedisCommandArguments } from '.'; -import { transformArguments as transformZUnionArguments } from './ZUNION'; +import { Command } from '../RESP/types'; +import ZUNION from './ZUNION'; +import { transformSortedSetReply } from './generic-transformers'; -export { FIRST_KEY_INDEX, IS_READ_ONLY } from './ZUNION'; - -export function transformArguments(...args: Parameters): RedisCommandArguments { - return [ - ...transformZUnionArguments(...args), - 'WITHSCORES' - ]; -} - -export { transformSortedSetWithScoresReply as transformReply } from './generic-transformers'; +export default { + FIRST_KEY_INDEX: ZUNION.FIRST_KEY_INDEX, + IS_READ_ONLY: ZUNION.IS_READ_ONLY, + transformArguments(...args: Parameters) { + const redisArgs = ZUNION.transformArguments(...args); + redisArgs.push('WITHSCORES'); + return redisArgs; + }, + transformReply: transformSortedSetReply +} as const satisfies Command; diff --git a/packages/client/lib/commands/generic-transformers.spec.ts b/packages/client/lib/commands/generic-transformers.spec.ts index 60caf26eaad..5f990d4e34d 100644 --- a/packages/client/lib/commands/generic-transformers.spec.ts +++ b/packages/client/lib/commands/generic-transformers.spec.ts @@ -1,718 +1,685 @@ -import { strict as assert } from 'assert'; -import { - transformBooleanReply, - transformBooleanArrayReply, - pushScanArguments, - transformNumberInfinityReply, - transformNumberInfinityNullReply, - transformNumberInfinityArgument, - transformStringNumberInfinityArgument, - transformTuplesReply, - transformStreamMessagesReply, - transformStreamMessagesNullReply, - transformStreamsMessagesReply, - transformSortedSetWithScoresReply, - pushGeoCountArgument, - pushGeoSearchArguments, - GeoReplyWith, - transformGeoMembersWithReply, - transformEXAT, - transformPXAT, - pushEvalArguments, - pushVerdictArguments, - pushVerdictNumberArguments, - pushVerdictArgument, - pushOptionalVerdictArgument, - transformCommandReply, - CommandFlags, - CommandCategories, - pushSlotRangesArguments -} from './generic-transformers'; - -describe('Generic Transformers', () => { - describe('transformBooleanReply', () => { - it('0', () => { - assert.equal( - transformBooleanReply(0), - false - ); - }); - - it('1', () => { - assert.equal( - transformBooleanReply(1), - true - ); - }); - }); - - describe('transformBooleanArrayReply', () => { - it('empty array', () => { - assert.deepEqual( - transformBooleanArrayReply([]), - [] - ); - }); - - it('0, 1', () => { - assert.deepEqual( - transformBooleanArrayReply([0, 1]), - [false, true] - ); - }); - }); - - describe('pushScanArguments', () => { - it('cusror only', () => { - assert.deepEqual( - pushScanArguments([], 0), - ['0'] - ); - }); - - it('with MATCH', () => { - assert.deepEqual( - pushScanArguments([], 0, { - MATCH: 'pattern' - }), - ['0', 'MATCH', 'pattern'] - ); - }); - - it('with COUNT', () => { - assert.deepEqual( - pushScanArguments([], 0, { - COUNT: 1 - }), - ['0', 'COUNT', '1'] - ); - }); - - it('with MATCH & COUNT', () => { - assert.deepEqual( - pushScanArguments([], 0, { - MATCH: 'pattern', - COUNT: 1 - }), - ['0', 'MATCH', 'pattern', 'COUNT', '1'] - ); - }); - }); - - describe('transformNumberInfinityReply', () => { - it('0.5', () => { - assert.equal( - transformNumberInfinityReply('0.5'), - 0.5 - ); - }); - - it('+inf', () => { - assert.equal( - transformNumberInfinityReply('+inf'), - Infinity - ); - }); - - it('-inf', () => { - assert.equal( - transformNumberInfinityReply('-inf'), - -Infinity - ); - }); - }); - - describe('transformNumberInfinityNullReply', () => { - it('null', () => { - assert.equal( - transformNumberInfinityNullReply(null), - null - ); - }); - - it('1', () => { - assert.equal( - transformNumberInfinityNullReply('1'), - 1 - ); - }); - }); - - describe('transformNumberInfinityArgument', () => { - it('0.5', () => { - assert.equal( - transformNumberInfinityArgument(0.5), - '0.5' - ); - }); - - it('Infinity', () => { - assert.equal( - transformNumberInfinityArgument(Infinity), - '+inf' - ); - }); - - it('-Infinity', () => { - assert.equal( - transformNumberInfinityArgument(-Infinity), - '-inf' - ); - }); - }); - - describe('transformStringNumberInfinityArgument', () => { - it("'0.5'", () => { - assert.equal( - transformStringNumberInfinityArgument('0.5'), - '0.5' - ); - }); - - it('0.5', () => { - assert.equal( - transformStringNumberInfinityArgument(0.5), - '0.5' - ); - }); - }); - - it('transformTuplesReply', () => { - assert.deepEqual( - transformTuplesReply(['key1', 'value1', 'key2', 'value2']), - Object.create(null, { - key1: { - value: 'value1', - configurable: true, - enumerable: true - }, - key2: { - value: 'value2', - configurable: true, - enumerable: true - } - }) - ); - }); - - it('transformStreamMessagesReply', () => { - assert.deepEqual( - transformStreamMessagesReply([['0-0', ['0key', '0value']], ['1-0', ['1key', '1value']]]), - [{ - id: '0-0', - message: Object.create(null, { - '0key': { - value: '0value', - configurable: true, - enumerable: true - } - }) - }, { - id: '1-0', - message: Object.create(null, { - '1key': { - value: '1value', - configurable: true, - enumerable: true - } - }) - }] - ); - }); - - it('transformStreamMessagesNullReply', () => { - assert.deepEqual( - transformStreamMessagesNullReply([null, ['0-0', ['0key', '0value']]]), - [null, { - id: '0-0', - message: Object.create(null, { - '0key': { - value: '0value', - configurable: true, - enumerable: true - } - }) - }] - ); - }); - - it('transformStreamMessagesNullReply', () => { - assert.deepEqual( - transformStreamMessagesNullReply([null, ['0-1', ['11key', '11value']]]), - [null, { - id: '0-1', - message: Object.create(null, { - '11key': { - value: '11value', - configurable: true, - enumerable: true - } - }) - }] - ); - }); - - describe('transformStreamsMessagesReply', () => { - it('null', () => { - assert.equal( - transformStreamsMessagesReply(null), - null - ); - }); - - it('with messages', () => { - assert.deepEqual( - transformStreamsMessagesReply([['stream1', [['0-1', ['11key', '11value']], ['1-1', ['12key', '12value']]]], ['stream2', [['0-2', ['2key1', '2value1', '2key2', '2value2']]]]]), - [{ - name: 'stream1', - messages: [{ - id: '0-1', - message: Object.create(null, { - '11key': { - value: '11value', - configurable: true, - enumerable: true - } - }) - }, { - id: '1-1', - message: Object.create(null, { - '12key': { - value: '12value', - configurable: true, - enumerable: true - } - }) - }] - }, { - name: 'stream2', - messages: [{ - id: '0-2', - message: Object.create(null, { - '2key1': { - value: '2value1', - configurable: true, - enumerable: true - }, - '2key2': { - value: '2value2', - configurable: true, - enumerable: true - } - }) - }] - }] - ); - }); - }); - - it('transformSortedSetWithScoresReply', () => { - assert.deepEqual( - transformSortedSetWithScoresReply(['member1', '0.5', 'member2', '+inf', 'member3', '-inf']), - [{ - value: 'member1', - score: 0.5 - }, { - value: 'member2', - score: Infinity - }, { - value: 'member3', - score: -Infinity - }] - ); - }); - - describe('pushGeoCountArgument', () => { - it('undefined', () => { - assert.deepEqual( - pushGeoCountArgument([], undefined), - [] - ); - }); - - it('number', () => { - assert.deepEqual( - pushGeoCountArgument([], 1), - ['COUNT', '1'] - ); - }); - - describe('with COUNT', () => { - it('number', () => { - assert.deepEqual( - pushGeoCountArgument([], 1), - ['COUNT', '1'] - ); - }); - - describe('object', () => { - it('value', () => { - assert.deepEqual( - pushGeoCountArgument([], { value: 1 }), - ['COUNT', '1'] - ); - }); - - it('value, ANY', () => { - assert.deepEqual( - pushGeoCountArgument([], { - value: 1, - ANY: true - }), - ['COUNT', '1', 'ANY'] - ); - }); - }); - }); - }); - - describe('pushGeoSearchArguments', () => { - it('FROMMEMBER, BYRADIUS', () => { - assert.deepEqual( - pushGeoSearchArguments([], 'key', 'member', { - radius: 1, - unit: 'm' - }), - ['key', 'FROMMEMBER', 'member', 'BYRADIUS', '1', 'm'] - ); - }); - - it('FROMLONLAT, BYBOX', () => { - assert.deepEqual( - pushGeoSearchArguments([], 'key', { - longitude: 1, - latitude: 2 - }, { - width: 1, - height: 2, - unit: 'm' - }), - ['key', 'FROMLONLAT', '1', '2', 'BYBOX', '1', '2', 'm'] - ); - }); - - it('with SORT', () => { - assert.deepEqual( - pushGeoSearchArguments([], 'key', 'member', { - radius: 1, - unit: 'm' - }, { - SORT: 'ASC' - }), - ['key', 'FROMMEMBER', 'member', 'BYRADIUS', '1', 'm', 'ASC'] - ); - }); - }); - - describe('transformGeoMembersWithReply', () => { - it('DISTANCE', () => { - assert.deepEqual( - transformGeoMembersWithReply([ - [ - '1', - '2' - ], - [ - '3', - '4' - ] - ], [GeoReplyWith.DISTANCE]), - [{ - member: '1', - distance: '2' - }, { - member: '3', - distance: '4' - }] - ); - }); - - it('HASH', () => { - assert.deepEqual( - transformGeoMembersWithReply([ - [ - '1', - 2 - ], - [ - '3', - 4 - ] - ], [GeoReplyWith.HASH]), - [{ - member: '1', - hash: 2 - }, { - member: '3', - hash: 4 - }] - ); - }); - - it('COORDINATES', () => { - assert.deepEqual( - transformGeoMembersWithReply([ - [ - '1', - [ - '2', - '3' - ] - ], - [ - '4', - [ - '5', - '6' - ] - ] - ], [GeoReplyWith.COORDINATES]), - [{ - member: '1', - coordinates: { - longitude: '2', - latitude: '3' - } - }, { - member: '4', - coordinates: { - longitude: '5', - latitude: '6' - } - }] - ); - }); - - it('DISTANCE, HASH, COORDINATES', () => { - assert.deepEqual( - transformGeoMembersWithReply([ - [ - '1', - '2', - 3, - [ - '4', - '5' - ] - ], - [ - '6', - '7', - 8, - [ - '9', - '10' - ] - ] - ], [GeoReplyWith.DISTANCE, GeoReplyWith.HASH, GeoReplyWith.COORDINATES]), - [{ - member: '1', - distance: '2', - hash: 3, - coordinates: { - longitude: '4', - latitude: '5' - } - }, { - member: '6', - distance: '7', - hash: 8, - coordinates: { - longitude: '9', - latitude: '10' - } - }] - ); - }); - }); - - describe('transformEXAT', () => { - it('number', () => { - assert.equal( - transformEXAT(1), - '1' - ); - }); - - it('date', () => { - const d = new Date(); - assert.equal( - transformEXAT(d), - Math.floor(d.getTime() / 1000).toString() - ); - }); - }); - - describe('transformPXAT', () => { - it('number', () => { - assert.equal( - transformPXAT(1), - '1' - ); - }); - - it('date', () => { - const d = new Date(); - assert.equal( - transformPXAT(d), - d.getTime().toString() - ); - }); - }); - - describe('pushEvalArguments', () => { - it('empty', () => { - assert.deepEqual( - pushEvalArguments([]), - ['0'] - ); - }); - - it('with keys', () => { - assert.deepEqual( - pushEvalArguments([], { - keys: ['key'] - }), - ['1', 'key'] - ); - }); - - it('with arguments', () => { - assert.deepEqual( - pushEvalArguments([], { - arguments: ['argument'] - }), - ['0', 'argument'] - ); - }); - - it('with keys and arguments', () => { - assert.deepEqual( - pushEvalArguments([], { - keys: ['key'], - arguments: ['argument'] - }), - ['1', 'key', 'argument'] - ); - }); - }); - - describe('pushVerdictArguments', () => { - it('string', () => { - assert.deepEqual( - pushVerdictArguments([], 'string'), - ['string'] - ); - }); - - it('array', () => { - assert.deepEqual( - pushVerdictArguments([], ['1', '2']), - ['1', '2'] - ); - }); - }); - - describe('pushVerdictNumberArguments', () => { - it('number', () => { - assert.deepEqual( - pushVerdictNumberArguments([], 0), - ['0'] - ); - }); - - it('array', () => { - assert.deepEqual( - pushVerdictNumberArguments([], [0, 1]), - ['0', '1'] - ); - }); - }); - - describe('pushVerdictArgument', () => { - it('string', () => { - assert.deepEqual( - pushVerdictArgument([], 'string'), - ['1', 'string'] - ); - }); - - it('array', () => { - assert.deepEqual( - pushVerdictArgument([], ['1', '2']), - ['2', '1', '2'] - ); - }); - }); - - describe('pushOptionalVerdictArgument', () => { - it('undefined', () => { - assert.deepEqual( - pushOptionalVerdictArgument([], 'name', undefined), - [] - ); - }); - - it('string', () => { - assert.deepEqual( - pushOptionalVerdictArgument([], 'name', 'string'), - ['name', '1', 'string'] - ); - }); - - it('array', () => { - assert.deepEqual( - pushOptionalVerdictArgument([], 'name', ['1', '2']), - ['name', '2', '1', '2'] - ); - }); - }); - - it('transformCommandReply', () => { - assert.deepEqual( - transformCommandReply([ - 'ping', - -1, - [CommandFlags.STALE, CommandFlags.FAST], - 0, - 0, - 0, - [CommandCategories.FAST, CommandCategories.CONNECTION] - ]), - { - name: 'ping', - arity: -1, - flags: new Set([CommandFlags.STALE, CommandFlags.FAST]), - firstKeyIndex: 0, - lastKeyIndex: 0, - step: 0, - categories: new Set([CommandCategories.FAST, CommandCategories.CONNECTION]) - } - ); - }); - - describe('pushSlotRangesArguments', () => { - it('single range', () => { - assert.deepEqual( - pushSlotRangesArguments([], { - start: 0, - end: 1 - }), - ['0', '1'] - ); - }); - - it('multiple ranges', () => { - assert.deepEqual( - pushSlotRangesArguments([], [{ - start: 0, - end: 1 - }, { - start: 2, - end: 3 - }]), - ['0', '1', '2', '3'] - ); - }); - }); -}); +// import { strict as assert } from 'node:assert'; +// import { +// transformBooleanReply, +// transformBooleanArrayReply, +// pushScanArguments, +// transformNumberInfinityReply, +// transformNumberInfinityNullReply, +// transformNumberInfinityArgument, +// transformStringNumberInfinityArgument, +// transformTuplesReply, +// transformStreamMessagesReply, +// transformStreamsMessagesReply, +// transformSortedSetWithScoresReply, +// pushGeoCountArgument, +// pushGeoSearchArguments, +// GeoReplyWith, +// transformGeoMembersWithReply, +// transformEXAT, +// transformPXAT, +// pushEvalArguments, +// pushVariadicArguments, +// pushVariadicNumberArguments, +// pushVariadicArgument, +// pushOptionalVariadicArgument, +// transformCommandReply, +// CommandFlags, +// CommandCategories, +// pushSlotRangesArguments +// } from './generic-transformers'; + +// describe('Generic Transformers', () => { +// describe('transformBooleanReply', () => { +// it('0', () => { +// assert.equal( +// transformBooleanReply(0), +// false +// ); +// }); + +// it('1', () => { +// assert.equal( +// transformBooleanReply(1), +// true +// ); +// }); +// }); + +// describe('transformBooleanArrayReply', () => { +// it('empty array', () => { +// assert.deepEqual( +// transformBooleanArrayReply([]), +// [] +// ); +// }); + +// it('0, 1', () => { +// assert.deepEqual( +// transformBooleanArrayReply([0, 1]), +// [false, true] +// ); +// }); +// }); + +// describe('pushScanArguments', () => { +// it('cusror only', () => { +// assert.deepEqual( +// pushScanArguments([], 0), +// ['0'] +// ); +// }); + +// it('with MATCH', () => { +// assert.deepEqual( +// pushScanArguments([], 0, { +// MATCH: 'pattern' +// }), +// ['0', 'MATCH', 'pattern'] +// ); +// }); + +// it('with COUNT', () => { +// assert.deepEqual( +// pushScanArguments([], 0, { +// COUNT: 1 +// }), +// ['0', 'COUNT', '1'] +// ); +// }); + +// it('with MATCH & COUNT', () => { +// assert.deepEqual( +// pushScanArguments([], 0, { +// MATCH: 'pattern', +// COUNT: 1 +// }), +// ['0', 'MATCH', 'pattern', 'COUNT', '1'] +// ); +// }); +// }); + +// describe('transformNumberInfinityReply', () => { +// it('0.5', () => { +// assert.equal( +// transformNumberInfinityReply('0.5'), +// 0.5 +// ); +// }); + +// it('+inf', () => { +// assert.equal( +// transformNumberInfinityReply('+inf'), +// Infinity +// ); +// }); + +// it('-inf', () => { +// assert.equal( +// transformNumberInfinityReply('-inf'), +// -Infinity +// ); +// }); +// }); + +// describe('transformNumberInfinityNullReply', () => { +// it('null', () => { +// assert.equal( +// transformNumberInfinityNullReply(null), +// null +// ); +// }); + +// it('1', () => { +// assert.equal( +// transformNumberInfinityNullReply('1'), +// 1 +// ); +// }); +// }); + +// describe('transformNumberInfinityArgument', () => { +// it('0.5', () => { +// assert.equal( +// transformNumberInfinityArgument(0.5), +// '0.5' +// ); +// }); + +// it('Infinity', () => { +// assert.equal( +// transformNumberInfinityArgument(Infinity), +// '+inf' +// ); +// }); + +// it('-Infinity', () => { +// assert.equal( +// transformNumberInfinityArgument(-Infinity), +// '-inf' +// ); +// }); +// }); + +// describe('transformStringNumberInfinityArgument', () => { +// it("'0.5'", () => { +// assert.equal( +// transformStringNumberInfinityArgument('0.5'), +// '0.5' +// ); +// }); + +// it('0.5', () => { +// assert.equal( +// transformStringNumberInfinityArgument(0.5), +// '0.5' +// ); +// }); +// }); + +// it('transformTuplesReply', () => { +// assert.deepEqual( +// transformTuplesReply(['key1', 'value1', 'key2', 'value2']), +// Object.create(null, { +// key1: { +// value: 'value1', +// configurable: true, +// enumerable: true +// }, +// key2: { +// value: 'value2', +// configurable: true, +// enumerable: true +// } +// }) +// ); +// }); + +// it('transformStreamMessagesReply', () => { +// assert.deepEqual( +// transformStreamMessagesReply([['0-0', ['0key', '0value']], ['1-0', ['1key', '1value']]]), +// [{ +// id: '0-0', +// message: Object.create(null, { +// '0key': { +// value: '0value', +// configurable: true, +// enumerable: true +// } +// }) +// }, { +// id: '1-0', +// message: Object.create(null, { +// '1key': { +// value: '1value', +// configurable: true, +// enumerable: true +// } +// }) +// }] +// ); +// }); + +// describe('transformStreamsMessagesReply', () => { +// it('null', () => { +// assert.equal( +// transformStreamsMessagesReply(null), +// null +// ); +// }); + +// it('with messages', () => { +// assert.deepEqual( +// transformStreamsMessagesReply([['stream1', [['0-1', ['11key', '11value']], ['1-1', ['12key', '12value']]]], ['stream2', [['0-2', ['2key1', '2value1', '2key2', '2value2']]]]]), +// [{ +// name: 'stream1', +// messages: [{ +// id: '0-1', +// message: Object.create(null, { +// '11key': { +// value: '11value', +// configurable: true, +// enumerable: true +// } +// }) +// }, { +// id: '1-1', +// message: Object.create(null, { +// '12key': { +// value: '12value', +// configurable: true, +// enumerable: true +// } +// }) +// }] +// }, { +// name: 'stream2', +// messages: [{ +// id: '0-2', +// message: Object.create(null, { +// '2key1': { +// value: '2value1', +// configurable: true, +// enumerable: true +// }, +// '2key2': { +// value: '2value2', +// configurable: true, +// enumerable: true +// } +// }) +// }] +// }] +// ); +// }); +// }); + +// it('transformSortedSetWithScoresReply', () => { +// assert.deepEqual( +// transformSortedSetWithScoresReply(['member1', '0.5', 'member2', '+inf', 'member3', '-inf']), +// [{ +// value: 'member1', +// score: 0.5 +// }, { +// value: 'member2', +// score: Infinity +// }, { +// value: 'member3', +// score: -Infinity +// }] +// ); +// }); + +// describe('pushGeoCountArgument', () => { +// it('undefined', () => { +// assert.deepEqual( +// pushGeoCountArgument([], undefined), +// [] +// ); +// }); + +// it('number', () => { +// assert.deepEqual( +// pushGeoCountArgument([], 1), +// ['COUNT', '1'] +// ); +// }); + +// describe('with COUNT', () => { +// it('number', () => { +// assert.deepEqual( +// pushGeoCountArgument([], 1), +// ['COUNT', '1'] +// ); +// }); + +// describe('object', () => { +// it('value', () => { +// assert.deepEqual( +// pushGeoCountArgument([], { value: 1 }), +// ['COUNT', '1'] +// ); +// }); + +// it('value, ANY', () => { +// assert.deepEqual( +// pushGeoCountArgument([], { +// value: 1, +// ANY: true +// }), +// ['COUNT', '1', 'ANY'] +// ); +// }); +// }); +// }); +// }); + +// describe('pushGeoSearchArguments', () => { +// it('FROMMEMBER, BYRADIUS', () => { +// assert.deepEqual( +// pushGeoSearchArguments([], 'key', 'member', { +// radius: 1, +// unit: 'm' +// }), +// ['key', 'FROMMEMBER', 'member', 'BYRADIUS', '1', 'm'] +// ); +// }); + +// it('FROMLONLAT, BYBOX', () => { +// assert.deepEqual( +// pushGeoSearchArguments([], 'key', { +// longitude: 1, +// latitude: 2 +// }, { +// width: 1, +// height: 2, +// unit: 'm' +// }), +// ['key', 'FROMLONLAT', '1', '2', 'BYBOX', '1', '2', 'm'] +// ); +// }); + +// it('with SORT', () => { +// assert.deepEqual( +// pushGeoSearchArguments([], 'key', 'member', { +// radius: 1, +// unit: 'm' +// }, { +// SORT: 'ASC' +// }), +// ['key', 'FROMMEMBER', 'member', 'BYRADIUS', '1', 'm', 'ASC'] +// ); +// }); +// }); + +// describe('transformGeoMembersWithReply', () => { +// it('DISTANCE', () => { +// assert.deepEqual( +// transformGeoMembersWithReply([ +// [ +// '1', +// '2' +// ], +// [ +// '3', +// '4' +// ] +// ], [GeoReplyWith.DISTANCE]), +// [{ +// member: '1', +// distance: '2' +// }, { +// member: '3', +// distance: '4' +// }] +// ); +// }); + +// it('HASH', () => { +// assert.deepEqual( +// transformGeoMembersWithReply([ +// [ +// '1', +// 2 +// ], +// [ +// '3', +// 4 +// ] +// ], [GeoReplyWith.HASH]), +// [{ +// member: '1', +// hash: 2 +// }, { +// member: '3', +// hash: 4 +// }] +// ); +// }); + +// it('COORDINATES', () => { +// assert.deepEqual( +// transformGeoMembersWithReply([ +// [ +// '1', +// [ +// '2', +// '3' +// ] +// ], +// [ +// '4', +// [ +// '5', +// '6' +// ] +// ] +// ], [GeoReplyWith.COORDINATES]), +// [{ +// member: '1', +// coordinates: { +// longitude: '2', +// latitude: '3' +// } +// }, { +// member: '4', +// coordinates: { +// longitude: '5', +// latitude: '6' +// } +// }] +// ); +// }); + +// it('DISTANCE, HASH, COORDINATES', () => { +// assert.deepEqual( +// transformGeoMembersWithReply([ +// [ +// '1', +// '2', +// 3, +// [ +// '4', +// '5' +// ] +// ], +// [ +// '6', +// '7', +// 8, +// [ +// '9', +// '10' +// ] +// ] +// ], [GeoReplyWith.DISTANCE, GeoReplyWith.HASH, GeoReplyWith.COORDINATES]), +// [{ +// member: '1', +// distance: '2', +// hash: 3, +// coordinates: { +// longitude: '4', +// latitude: '5' +// } +// }, { +// member: '6', +// distance: '7', +// hash: 8, +// coordinates: { +// longitude: '9', +// latitude: '10' +// } +// }] +// ); +// }); +// }); + +// describe('transformEXAT', () => { +// it('number', () => { +// assert.equal( +// transformEXAT(1), +// '1' +// ); +// }); + +// it('date', () => { +// const d = new Date(); +// assert.equal( +// transformEXAT(d), +// Math.floor(d.getTime() / 1000).toString() +// ); +// }); +// }); + +// describe('transformPXAT', () => { +// it('number', () => { +// assert.equal( +// transformPXAT(1), +// '1' +// ); +// }); + +// it('date', () => { +// const d = new Date(); +// assert.equal( +// transformPXAT(d), +// d.getTime().toString() +// ); +// }); +// }); + +// describe('pushEvalArguments', () => { +// it('empty', () => { +// assert.deepEqual( +// pushEvalArguments([]), +// ['0'] +// ); +// }); + +// it('with keys', () => { +// assert.deepEqual( +// pushEvalArguments([], { +// keys: ['key'] +// }), +// ['1', 'key'] +// ); +// }); + +// it('with arguments', () => { +// assert.deepEqual( +// pushEvalArguments([], { +// arguments: ['argument'] +// }), +// ['0', 'argument'] +// ); +// }); + +// it('with keys and arguments', () => { +// assert.deepEqual( +// pushEvalArguments([], { +// keys: ['key'], +// arguments: ['argument'] +// }), +// ['1', 'key', 'argument'] +// ); +// }); +// }); + +// describe('pushVariadicArguments', () => { +// it('string', () => { +// assert.deepEqual( +// pushVariadicArguments([], 'string'), +// ['string'] +// ); +// }); + +// it('array', () => { +// assert.deepEqual( +// pushVariadicArguments([], ['1', '2']), +// ['1', '2'] +// ); +// }); +// }); + +// describe('pushVariadicNumberArguments', () => { +// it('number', () => { +// assert.deepEqual( +// pushVariadicNumberArguments([], 0), +// ['0'] +// ); +// }); + +// it('array', () => { +// assert.deepEqual( +// pushVariadicNumberArguments([], [0, 1]), +// ['0', '1'] +// ); +// }); +// }); + +// describe('pushVariadicArgument', () => { +// it('string', () => { +// assert.deepEqual( +// pushVariadicArgument([], 'string'), +// ['1', 'string'] +// ); +// }); + +// it('array', () => { +// assert.deepEqual( +// pushVariadicArgument([], ['1', '2']), +// ['2', '1', '2'] +// ); +// }); +// }); + +// describe('pushOptionalVariadicArgument', () => { +// it('undefined', () => { +// assert.deepEqual( +// pushOptionalVariadicArgument([], 'name', undefined), +// [] +// ); +// }); + +// it('string', () => { +// assert.deepEqual( +// pushOptionalVariadicArgument([], 'name', 'string'), +// ['name', '1', 'string'] +// ); +// }); + +// it('array', () => { +// assert.deepEqual( +// pushOptionalVariadicArgument([], 'name', ['1', '2']), +// ['name', '2', '1', '2'] +// ); +// }); +// }); + +// it('transformCommandReply', () => { +// assert.deepEqual( +// transformCommandReply([ +// 'ping', +// -1, +// [CommandFlags.STALE, CommandFlags.FAST], +// 0, +// 0, +// 0, +// [CommandCategories.FAST, CommandCategories.CONNECTION] +// ]), +// { +// name: 'ping', +// arity: -1, +// flags: new Set([CommandFlags.STALE, CommandFlags.FAST]), +// firstKeyIndex: 0, +// lastKeyIndex: 0, +// step: 0, +// categories: new Set([CommandCategories.FAST, CommandCategories.CONNECTION]) +// } +// ); +// }); + +// describe('pushSlotRangesArguments', () => { +// it('single range', () => { +// assert.deepEqual( +// pushSlotRangesArguments([], { +// start: 0, +// end: 1 +// }), +// ['0', '1'] +// ); +// }); + +// it('multiple ranges', () => { +// assert.deepEqual( +// pushSlotRangesArguments([], [{ +// start: 0, +// end: 1 +// }, { +// start: 2, +// end: 3 +// }]), +// ['0', '1', '2', '3'] +// ); +// }); +// }); +// }); diff --git a/packages/client/lib/commands/generic-transformers.ts b/packages/client/lib/commands/generic-transformers.ts index 4cf610a036e..cc7100d90e6 100644 --- a/packages/client/lib/commands/generic-transformers.ts +++ b/packages/client/lib/commands/generic-transformers.ts @@ -1,697 +1,641 @@ -import { RedisCommandArgument, RedisCommandArguments } from '.'; +import { RESP_TYPES } from '../RESP/decoder'; +import { UnwrapReply, ArrayReply, BlobStringReply, BooleanReply, CommandArguments, DoubleReply, NullReply, NumberReply, RedisArgument, TuplesReply, MapReply, TypeMapping } from '../RESP/types'; -export function transformBooleanReply(reply: number): boolean { - return reply === 1; +export function isNullReply(reply: unknown): reply is NullReply { + return reply === null; } -export function transformBooleanArrayReply(reply: Array): Array { - return reply.map(transformBooleanReply); +export function isArrayReply(reply: unknown): reply is ArrayReply { + return Array.isArray(reply); } -export type BitValue = 0 | 1; +export const transformBooleanReply = { + 2: (reply: NumberReply<0 | 1>) => reply as unknown as UnwrapReply === 1, + 3: undefined as unknown as () => BooleanReply +}; -export interface ScanOptions { - MATCH?: string; - COUNT?: number; -} +export const transformBooleanArrayReply = { + 2: (reply: ArrayReply>) => { + return (reply as unknown as UnwrapReply).map(transformBooleanReply[2]); + }, + 3: undefined as unknown as () => ArrayReply +}; -export function pushScanArguments( - args: RedisCommandArguments, - cursor: number, - options?: ScanOptions -): RedisCommandArguments { - args.push(cursor.toString()); +export type BitValue = 0 | 1; - if (options?.MATCH) { - args.push('MATCH', options.MATCH); - } +export function transformDoubleArgument(num: number): string { + switch (num) { + case Infinity: + return '+inf'; + + case -Infinity: + return '-inf'; + + default: + return num.toString(); + } +} + +export function transformStringDoubleArgument(num: RedisArgument | number): RedisArgument { + if (typeof num !== 'number') return num; + + return transformDoubleArgument(num); +} + +export const transformDoubleReply = { + 2: (reply: BlobStringReply, preserve?: any, typeMapping?: TypeMapping): DoubleReply => { + const double = typeMapping ? typeMapping[RESP_TYPES.DOUBLE] : undefined; + + switch (double) { + case String: { + return reply as unknown as DoubleReply; + } + default: { + let ret: number; + + switch (reply.toString()) { + case 'inf': + case '+inf': + ret = Infinity; + + case '-inf': + ret = -Infinity; + + case 'nan': + ret = NaN; + + default: + ret = Number(reply); + } - if (options?.COUNT) { - args.push('COUNT', options.COUNT.toString()); + return ret as unknown as DoubleReply; + } } + }, + 3: undefined as unknown as () => DoubleReply +}; - return args; +export function createTransformDoubleReplyResp2Func(preserve?: any, typeMapping?: TypeMapping) { + return (reply: BlobStringReply) => { + return transformDoubleReply[2](reply, preserve, typeMapping); + } } -export function transformNumberInfinityReply(reply: RedisCommandArgument): number { - switch (reply.toString()) { - case '+inf': - return Infinity; - - case '-inf': - return -Infinity; +export const transformDoubleArrayReply = { + 2: (reply: Array, preserve?: any, typeMapping?: TypeMapping) => { + return reply.map(createTransformDoubleReplyResp2Func(preserve, typeMapping)); + }, + 3: undefined as unknown as () => ArrayReply +} - default: - return Number(reply); - } +export function createTransformNullableDoubleReplyResp2Func(preserve?: any, typeMapping?: TypeMapping) { + return (reply: BlobStringReply | NullReply) => { + return transformNullableDoubleReply[2](reply, preserve, typeMapping); + } } -export function transformNumberInfinityNullReply(reply: RedisCommandArgument | null): number | null { +export const transformNullableDoubleReply = { + 2: (reply: BlobStringReply | NullReply, preserve?: any, typeMapping?: TypeMapping) => { if (reply === null) return null; + + return transformDoubleReply[2](reply as BlobStringReply, preserve, typeMapping); + }, + 3: undefined as unknown as () => DoubleReply | NullReply +}; - return transformNumberInfinityReply(reply); +export interface Stringable { + toString(): string; } -export function transformNumberInfinityNullArrayReply(reply: Array): Array { - return reply.map(transformNumberInfinityNullReply); +export function createTransformTuplesReplyFunc(preserve?: any, typeMapping?: TypeMapping) { + return (reply: ArrayReply) => { + return transformTuplesReply(reply, preserve, typeMapping); + }; } -export function transformNumberInfinityArgument(num: number): string { - switch (num) { - case Infinity: - return '+inf'; +export function transformTuplesReply( + reply: ArrayReply, + preserve?: any, + typeMapping?: TypeMapping +): MapReply { + const mapType = typeMapping ? typeMapping[RESP_TYPES.MAP] : undefined; - case -Infinity: - return '-inf'; + const inferred = reply as unknown as UnwrapReply - default: - return num.toString(); + switch (mapType) { + case Array: { + return reply as unknown as MapReply; } -} - -export function transformStringNumberInfinityArgument(num: RedisCommandArgument | number): RedisCommandArgument { - if (typeof num !== 'number') return num; - - return transformNumberInfinityArgument(num); -} + case Map: { + const ret = new Map; -export function transformTuplesReply( - reply: Array -): Record { - const message = Object.create(null); + for (let i = 0; i < inferred.length; i += 2) { + ret.set(inferred[i].toString(), inferred[i + 1] as any); + } - for (let i = 0; i < reply.length; i += 2) { - message[reply[i].toString()] = reply[i + 1]; + return ret as unknown as MapReply;; } + default: { + const ret: Record = Object.create(null); - return message; -} - -export interface StreamMessageReply { - id: RedisCommandArgument; - message: Record; -} - -export function transformStreamMessageReply([id, message]: Array): StreamMessageReply { - return { - id, - message: transformTuplesReply(message) - }; -} - -export function transformStreamMessageNullReply(reply: Array): StreamMessageReply | null { - if (reply === null) return null; - return transformStreamMessageReply(reply); -} - + for (let i = 0; i < inferred.length; i += 2) { + ret[inferred[i].toString()] = inferred[i + 1] as any; + } -export type StreamMessagesReply = Array; -export function transformStreamMessagesReply(reply: Array): StreamMessagesReply { - return reply.map(transformStreamMessageReply); + return ret as unknown as MapReply;; + } + } } -export type StreamMessagesNullReply = Array; -export function transformStreamMessagesNullReply(reply: Array): StreamMessagesNullReply { - return reply.map(transformStreamMessageNullReply); +export interface SortedSetMember { + value: RedisArgument; + score: number; } -export type StreamsMessagesReply = Array<{ - name: RedisCommandArgument; - messages: StreamMessagesReply; -}> | null; - -export function transformStreamsMessagesReply(reply: Array | null): StreamsMessagesReply | null { - if (reply === null) return null; +export type SortedSetSide = 'MIN' | 'MAX'; - return reply.map(([name, rawMessages]) => ({ - name, - messages: transformStreamMessagesReply(rawMessages) - })); -} +export const transformSortedSetReply = { + 2: (reply: ArrayReply, preserve?: any, typeMapping?: TypeMapping) => { + const inferred = reply as unknown as UnwrapReply, + members = []; + for (let i = 0; i < inferred.length; i += 2) { + members.push({ + value: inferred[i], + score: transformDoubleReply[2](inferred[i + 1], preserve, typeMapping) + }); + } -export interface ZMember { - score: number; - value: RedisCommandArgument; + return members; + }, + 3: (reply: ArrayReply>) => { + return (reply as unknown as UnwrapReply).map(member => { + const [value, score] = member as unknown as UnwrapReply; + return { + value, + score + }; + }); + } } -export function transformSortedSetMemberNullReply( - reply: [RedisCommandArgument, RedisCommandArgument] | [] -): ZMember | null { - if (!reply.length) return null; +export type ListSide = 'LEFT' | 'RIGHT'; - return transformSortedSetMemberReply(reply); +export function transformEXAT(EXAT: number | Date): string { + return (typeof EXAT === 'number' ? EXAT : Math.floor(EXAT.getTime() / 1000)).toString(); } -export function transformSortedSetMemberReply( - reply: [RedisCommandArgument, RedisCommandArgument] -): ZMember { - return { - value: reply[0], - score: transformNumberInfinityReply(reply[1]) - }; +export function transformPXAT(PXAT: number | Date): string { + return (typeof PXAT === 'number' ? PXAT : PXAT.getTime()).toString(); } -export function transformSortedSetWithScoresReply(reply: Array): Array { - const members = []; - - for (let i = 0; i < reply.length; i += 2) { - members.push({ - value: reply[i], - score: transformNumberInfinityReply(reply[i + 1]) - }); - } - - return members; +export interface EvalOptions { + keys?: Array; + arguments?: Array; } -export type SortedSetSide = 'MIN' | 'MAX'; - -export interface ZMPopOptions { - COUNT?: number; +export function evalFirstKeyIndex(options?: EvalOptions): string | undefined { + return options?.keys?.[0]; } -export function transformZMPopArguments( - args: RedisCommandArguments, - keys: RedisCommandArgument | Array, - side: SortedSetSide, - options?: ZMPopOptions -): RedisCommandArguments { - pushVerdictArgument(args, keys); - - args.push(side); +export function pushEvalArguments(args: Array, options?: EvalOptions): Array { + if (options?.keys) { + args.push( + options.keys.length.toString(), + ...options.keys + ); + } else { + args.push('0'); + } - if (options?.COUNT) { - args.push('COUNT', options.COUNT.toString()); - } + if (options?.arguments) { + args.push(...options.arguments); + } - return args; + return args; } -export type ListSide = 'LEFT' | 'RIGHT'; +export function pushVariadicArguments(args: CommandArguments, value: RedisVariadicArgument): CommandArguments { + if (Array.isArray(value)) { + // https://github.com/redis/node-redis/pull/2160 + args = args.concat(value); + } else { + args.push(value); + } -export interface LMPopOptions { - COUNT?: number; + return args; } -export function transformLMPopArguments( - args: RedisCommandArguments, - keys: RedisCommandArgument | Array, - side: ListSide, - options?: LMPopOptions -): RedisCommandArguments { - pushVerdictArgument(args, keys); - - args.push(side); - - if (options?.COUNT) { - args.push('COUNT', options.COUNT.toString()); +export function pushVariadicNumberArguments( + args: CommandArguments, + value: number | Array +): CommandArguments { + if (Array.isArray(value)) { + for (const item of value) { + args.push(item.toString()); } + } else { + args.push(value.toString()); + } - return args; + return args; } -type GeoCountArgument = number | { - value: number; - ANY?: true -}; - -export function pushGeoCountArgument( - args: RedisCommandArguments, - count: GeoCountArgument | undefined -): RedisCommandArguments { - if (typeof count === 'number') { - args.push('COUNT', count.toString()); - } else if (count) { - args.push('COUNT', count.value.toString()); - - if (count.ANY) { - args.push('ANY'); - } - } - - return args; -} +export type RedisVariadicArgument = RedisArgument | Array; -export type GeoUnits = 'm' | 'km' | 'mi' | 'ft'; +export function pushVariadicArgument( + args: Array, + value: RedisVariadicArgument +): CommandArguments { + if (Array.isArray(value)) { + args.push(value.length.toString(), ...value); + } else { + args.push('1', value); + } -export interface GeoCoordinates { - longitude: string | number; - latitude: string | number; + return args; } -type GeoSearchFromMember = string; - -export type GeoSearchFrom = GeoSearchFromMember | GeoCoordinates; +export function pushOptionalVariadicArgument( + args: CommandArguments, + name: RedisArgument, + value?: RedisVariadicArgument +): CommandArguments { + if (value === undefined) return args; -interface GeoSearchByRadius { - radius: number; - unit: GeoUnits; -} + args.push(name); -interface GeoSearchByBox { - width: number; - height: number; - unit: GeoUnits; + return pushVariadicArgument(args, value); } -export type GeoSearchBy = GeoSearchByRadius | GeoSearchByBox; - -export interface GeoSearchOptions { - SORT?: 'ASC' | 'DESC'; - COUNT?: GeoCountArgument; +export enum CommandFlags { + WRITE = 'write', // command may result in modifications + READONLY = 'readonly', // command will never modify keys + DENYOOM = 'denyoom', // reject command if currently out of memory + ADMIN = 'admin', // server admin command + PUBSUB = 'pubsub', // pubsub-related command + NOSCRIPT = 'noscript', // deny this command from scripts + RANDOM = 'random', // command has random results, dangerous for scripts + SORT_FOR_SCRIPT = 'sort_for_script', // if called from script, sort output + LOADING = 'loading', // allow command while database is loading + STALE = 'stale', // allow command while replica has stale data + SKIP_MONITOR = 'skip_monitor', // do not show this command in MONITOR + ASKING = 'asking', // cluster related - accept even if importing + FAST = 'fast', // command operates in constant or log(N) time. Used for latency monitoring. + MOVABLEKEYS = 'movablekeys' // keys have no pre-determined position. You must discover keys yourself. } -export function pushGeoSearchArguments( - args: RedisCommandArguments, - key: RedisCommandArgument, - from: GeoSearchFrom, - by: GeoSearchBy, - options?: GeoSearchOptions -): RedisCommandArguments { - args.push(key); - - if (typeof from === 'string') { - args.push('FROMMEMBER', from); - } else { - args.push('FROMLONLAT', from.longitude.toString(), from.latitude.toString()); - } - - if ('radius' in by) { - args.push('BYRADIUS', by.radius.toString()); - } else { - args.push('BYBOX', by.width.toString(), by.height.toString()); - } - - args.push(by.unit); - - if (options?.SORT) { - args.push(options.SORT); - } - - pushGeoCountArgument(args, options?.COUNT); - - return args; +export enum CommandCategories { + KEYSPACE = '@keyspace', + READ = '@read', + WRITE = '@write', + SET = '@set', + SORTEDSET = '@sortedset', + LIST = '@list', + HASH = '@hash', + STRING = '@string', + BITMAP = '@bitmap', + HYPERLOGLOG = '@hyperloglog', + GEO = '@geo', + STREAM = '@stream', + PUBSUB = '@pubsub', + ADMIN = '@admin', + FAST = '@fast', + SLOW = '@slow', + BLOCKING = '@blocking', + DANGEROUS = '@dangerous', + CONNECTION = '@connection', + TRANSACTION = '@transaction', + SCRIPTING = '@scripting' } -export function pushGeoRadiusArguments( - args: RedisCommandArguments, - key: RedisCommandArgument, - from: GeoSearchFrom, - radius: number, - unit: GeoUnits, - options?: GeoSearchOptions -): RedisCommandArguments { - args.push(key); - - if (typeof from === 'string') { - args.push(from); - } else { - args.push( - from.longitude.toString(), - from.latitude.toString() - ); - } - - args.push( - radius.toString(), - unit - ); - - if (options?.SORT) { - args.push(options.SORT); - } +export type CommandRawReply = [ + name: string, + arity: number, + flags: Array, + firstKeyIndex: number, + lastKeyIndex: number, + step: number, + categories: Array +]; - pushGeoCountArgument(args, options?.COUNT); +export type CommandReply = { + name: string, + arity: number, + flags: Set, + firstKeyIndex: number, + lastKeyIndex: number, + step: number, + categories: Set +}; - return args; +export function transformCommandReply( + this: void, + [name, arity, flags, firstKeyIndex, lastKeyIndex, step, categories]: CommandRawReply +): CommandReply { + return { + name, + arity, + flags: new Set(flags), + firstKeyIndex, + lastKeyIndex, + step, + categories: new Set(categories) + }; } -export interface GeoRadiusStoreOptions extends GeoSearchOptions { - STOREDIST?: boolean; +export enum RedisFunctionFlags { + NO_WRITES = 'no-writes', + ALLOW_OOM = 'allow-oom', + ALLOW_STALE = 'allow-stale', + NO_CLUSTER = 'no-cluster' } -export function pushGeoRadiusStoreArguments( - args: RedisCommandArguments, - key: RedisCommandArgument, - from: GeoSearchFrom, - radius: number, - unit: GeoUnits, - destination: RedisCommandArgument, - options?: GeoRadiusStoreOptions -): RedisCommandArguments { - pushGeoRadiusArguments(args, key, from, radius, unit, options); - - if (options?.STOREDIST) { - args.push('STOREDIST', destination); - } else { - args.push('STORE', destination); - } +export type FunctionListRawItemReply = [ + 'library_name', + string, + 'engine', + string, + 'functions', + Array<[ + 'name', + string, + 'description', + string | null, + 'flags', + Array + ]> +]; - return args; +export interface FunctionListItemReply { + libraryName: string; + engine: string; + functions: Array<{ + name: string; + description: string | null; + flags: Array; + }>; } -export enum GeoReplyWith { - DISTANCE = 'WITHDIST', - HASH = 'WITHHASH', - COORDINATES = 'WITHCOORD' +export function transformFunctionListItemReply(reply: FunctionListRawItemReply): FunctionListItemReply { + return { + libraryName: reply[1], + engine: reply[3], + functions: reply[5].map(fn => ({ + name: fn[1], + description: fn[3], + flags: fn[5] + })) + }; } -export interface GeoReplyWithMember { - member: string; - distance?: number; - hash?: string; - coordinates?: { - longitude: string; - latitude: string; - }; +export interface SlotRange { + start: number; + end: number; } -export function transformGeoMembersWithReply(reply: Array>, replyWith: Array): Array { - const replyWithSet = new Set(replyWith); - - let index = 0; - const distanceIndex = replyWithSet.has(GeoReplyWith.DISTANCE) && ++index, - hashIndex = replyWithSet.has(GeoReplyWith.HASH) && ++index, - coordinatesIndex = replyWithSet.has(GeoReplyWith.COORDINATES) && ++index; - - return reply.map(member => { - const transformedMember: GeoReplyWithMember = { - member: member[0] - }; - - if (distanceIndex) { - transformedMember.distance = member[distanceIndex]; - } - - if (hashIndex) { - transformedMember.hash = member[hashIndex]; - } - - if (coordinatesIndex) { - const [longitude, latitude] = member[coordinatesIndex]; - transformedMember.coordinates = { - longitude, - latitude - }; - } - - return transformedMember; - }); +function pushSlotRangeArguments( + args: CommandArguments, + range: SlotRange +): void { + args.push( + range.start.toString(), + range.end.toString() + ); } -export function transformEXAT(EXAT: number | Date): string { - return (typeof EXAT === 'number' ? EXAT : Math.floor(EXAT.getTime() / 1000)).toString(); -} +export function pushSlotRangesArguments( + args: CommandArguments, + ranges: SlotRange | Array +): CommandArguments { + if (Array.isArray(ranges)) { + for (const range of ranges) { + pushSlotRangeArguments(args, range); + } + } else { + pushSlotRangeArguments(args, ranges); + } -export function transformPXAT(PXAT: number | Date): string { - return (typeof PXAT === 'number' ? PXAT : PXAT.getTime()).toString(); + return args; } -export interface EvalOptions { - keys?: Array; - arguments?: Array; -} +export type RawRangeReply = [ + start: number, + end: number +]; -export function evalFirstKeyIndex(options?: EvalOptions): string | undefined { - return options?.keys?.[0]; +export interface RangeReply { + start: number; + end: number; } -export function pushEvalArguments(args: Array, options?: EvalOptions): Array { - if (options?.keys) { - args.push( - options.keys.length.toString(), - ...options.keys - ); - } else { - args.push('0'); - } - - if (options?.arguments) { - args.push(...options.arguments); - } - - return args; +export function transformRangeReply([start, end]: RawRangeReply): RangeReply { + return { + start, + end + }; } -export function pushVerdictArguments(args: RedisCommandArguments, value: RedisCommandArgument | Array): RedisCommandArguments { - if (Array.isArray(value)) { - // https://github.com/redis/node-redis/pull/2160 - args = args.concat(value); - } else { - args.push(value); - } - - return args; -} +export type ZKeyAndWeight = { + key: RedisArgument; + weight: number; +}; -export function pushVerdictNumberArguments( - args: RedisCommandArguments, - value: number | Array -): RedisCommandArguments { - if (Array.isArray(value)) { - for (const item of value) { - args.push(item.toString()); +export type ZVariadicKeys = T | [T, ...Array]; + +export type ZKeys = ZVariadicKeys | ZVariadicKeys; + +export function pushZKeysArguments( + args: CommandArguments, + keys: ZKeys +) { + if (Array.isArray(keys)) { + args.push(keys.length.toString()); + + if (keys.length) { + if (isPlainKeys(keys)) { + args = args.concat(keys); + } else { + const start = args.length; + args[start + keys.length] = 'WEIGHTS'; + for (let i = 0; i < keys.length; i++) { + const index = start + i; + args[index] = keys[i].key; + args[index + 1 + keys.length] = transformDoubleArgument(keys[i].weight); } - } else { - args.push(value.toString()); + } } + } else { + args.push('1'); - return args; -} - -export function pushVerdictArgument( - args: RedisCommandArguments, - value: RedisCommandArgument | Array -): RedisCommandArguments { - if (Array.isArray(value)) { - args.push(value.length.toString(), ...value); + if (isPlainKey(keys)) { + args.push(keys); } else { - args.push('1', value); + args.push( + keys.key, + 'WEIGHTS', + transformDoubleArgument(keys.weight) + ); } + } - return args; -} - -export function pushOptionalVerdictArgument( - args: RedisCommandArguments, - name: RedisCommandArgument, - value: undefined | RedisCommandArgument | Array -): RedisCommandArguments { - if (value === undefined) return args; - - args.push(name); - - return pushVerdictArgument(args, value); + return args; } -export enum CommandFlags { - WRITE = 'write', // command may result in modifications - READONLY = 'readonly', // command will never modify keys - DENYOOM = 'denyoom', // reject command if currently out of memory - ADMIN = 'admin', // server admin command - PUBSUB = 'pubsub', // pubsub-related command - NOSCRIPT = 'noscript', // deny this command from scripts - RANDOM = 'random', // command has random results, dangerous for scripts - SORT_FOR_SCRIPT = 'sort_for_script', // if called from script, sort output - LOADING = 'loading', // allow command while database is loading - STALE = 'stale', // allow command while replica has stale data - SKIP_MONITOR = 'skip_monitor', // do not show this command in MONITOR - ASKING = 'asking', // cluster related - accept even if importing - FAST = 'fast', // command operates in constant or log(N) time. Used for latency monitoring. - MOVABLEKEYS = 'movablekeys' // keys have no pre-determined position. You must discover keys yourself. +function isPlainKey(key: RedisArgument | ZKeyAndWeight): key is RedisArgument { + return typeof key === 'string' || key instanceof Buffer; } -export enum CommandCategories { - KEYSPACE = '@keyspace', - READ = '@read', - WRITE = '@write', - SET = '@set', - SORTEDSET = '@sortedset', - LIST = '@list', - HASH = '@hash', - STRING = '@string', - BITMAP = '@bitmap', - HYPERLOGLOG = '@hyperloglog', - GEO = '@geo', - STREAM = '@stream', - PUBSUB = '@pubsub', - ADMIN = '@admin', - FAST = '@fast', - SLOW = '@slow', - BLOCKING = '@blocking', - DANGEROUS = '@dangerous', - CONNECTION = '@connection', - TRANSACTION = '@transaction', - SCRIPTING = '@scripting' +function isPlainKeys(keys: Array | Array): keys is Array { + return isPlainKey(keys[0]); } -export type CommandRawReply = [ - name: string, - arity: number, - flags: Array, - firstKeyIndex: number, - lastKeyIndex: number, - step: number, - categories: Array -]; +export type StreamMessageRawReply = TuplesReply<[ + id: BlobStringReply, + message: ArrayReply +]>; -export type CommandReply = { - name: string, - arity: number, - flags: Set, - firstKeyIndex: number, - lastKeyIndex: number, - step: number, - categories: Set +export type StreamMessageReply = { + id: BlobStringReply, + message: MapReply, }; -export function transformCommandReply( - this: void, - [name, arity, flags, firstKeyIndex, lastKeyIndex, step, categories]: CommandRawReply -): CommandReply { - return { - name, - arity, - flags: new Set(flags), - firstKeyIndex, - lastKeyIndex, - step, - categories: new Set(categories) - }; +export function transformStreamMessageReply(typeMapping: TypeMapping | undefined, reply: StreamMessageRawReply): StreamMessageReply { + const [ id, message ] = reply as unknown as UnwrapReply; + return { + id: id, + message: transformTuplesReply(message, undefined, typeMapping) + }; } -export enum RedisFunctionFlags { - NO_WRITES = 'no-writes', - ALLOW_OOM = 'allow-oom', - ALLOW_STALE = 'allow-stale', - NO_CLUSTER = 'no-cluster' +export function transformStreamMessageNullReply(typeMapping: TypeMapping | undefined, reply: StreamMessageRawReply | NullReply) { + return isNullReply(reply) ? reply : transformStreamMessageReply(typeMapping, reply); } -export type FunctionListRawItemReply = [ - 'library_name', - string, - 'engine', - string, - 'functions', - Array<[ - 'name', - string, - 'description', - string | null, - 'flags', - Array - ]> -]; +export type StreamMessagesReply = Array; -export interface FunctionListItemReply { - libraryName: string; - engine: string; - functions: Array<{ - name: string; - description: string | null; - flags: Array; - }>; -} +export type StreamsMessagesReply = Array<{ + name: BlobStringReply | string; + messages: StreamMessagesReply; +}> | null; -export function transformFunctionListItemReply(reply: FunctionListRawItemReply): FunctionListItemReply { - return { - libraryName: reply[1], - engine: reply[3], - functions: reply[5].map(fn => ({ - name: fn[1], - description: fn[3], - flags: fn[5] - })) - }; -} - -export interface SortOptions { - BY?: string; - LIMIT?: { - offset: number; - count: number; - }, - GET?: string | Array; - DIRECTION?: 'ASC' | 'DESC'; - ALPHA?: true; -} - -export function pushSortArguments( - args: RedisCommandArguments, - options?: SortOptions -): RedisCommandArguments { - if (options?.BY) { - args.push('BY', options.BY); +export function transformStreamMessagesReply( + r: ArrayReply, + typeMapping?: TypeMapping +): StreamMessagesReply { + const reply = r as unknown as UnwrapReply; + + return reply.map(transformStreamMessageReply.bind(undefined, typeMapping)); +} + +type StreamMessagesRawReply = TuplesReply<[name: BlobStringReply, ArrayReply]>; +type StreamsMessagesRawReply2 = ArrayReply; + +export function transformStreamsMessagesReplyResp2( + reply: UnwrapReply, + preserve?: any, + typeMapping?: TypeMapping +): StreamsMessagesReply | NullReply { + // FUTURE: resposne type if resp3 was working, reverting to old v4 for now + //: MapReply | NullReply { + if (reply === null) return null as unknown as NullReply; + + switch (typeMapping? typeMapping[RESP_TYPES.MAP] : undefined) { +/* FUTURE: a response type for when resp3 is working properly + case Map: { + const ret = new Map(); + + for (let i=0; i < reply.length; i++) { + const stream = reply[i] as unknown as UnwrapReply; + + const name = stream[0]; + const rawMessages = stream[1]; + + ret.set(name.toString(), transformStreamMessagesReply(rawMessages, typeMapping)); + } + + return ret as unknown as MapReply; } - - if (options?.LIMIT) { - args.push( - 'LIMIT', - options.LIMIT.offset.toString(), - options.LIMIT.count.toString() - ); + case Array: { + const ret: Array = []; + + for (let i=0; i < reply.length; i++) { + const stream = reply[i] as unknown as UnwrapReply; + + const name = stream[0]; + const rawMessages = stream[1]; + + ret.push(name); + ret.push(transformStreamMessagesReply(rawMessages, typeMapping)); + } + + return ret as unknown as MapReply; } - - if (options?.GET) { - for (const pattern of (typeof options.GET === 'string' ? [options.GET] : options.GET)) { - args.push('GET', pattern); - } + default: { + const ret: Record = Object.create(null); + + for (let i=0; i < reply.length; i++) { + const stream = reply[i] as unknown as UnwrapReply; + + const name = stream[0] as unknown as UnwrapReply; + const rawMessages = stream[1]; + + ret[name.toString()] = transformStreamMessagesReply(rawMessages); + } + + return ret as unknown as MapReply; } +*/ + // V4 compatible response type + default: { + const ret: StreamsMessagesReply = []; - if (options?.DIRECTION) { - args.push(options.DIRECTION); - } + for (let i=0; i < reply.length; i++) { + const stream = reply[i] as unknown as UnwrapReply; - if (options?.ALPHA) { - args.push('ALPHA'); - } + ret.push({ + name: stream[0], + messages: transformStreamMessagesReply(stream[1]) + }); + } - return args; + return ret; + } + } } -export interface SlotRange { - start: number; - end: number; -} +type StreamsMessagesRawReply3 = MapReply>; -function pushSlotRangeArguments( - args: RedisCommandArguments, - range: SlotRange -): void { - args.push( - range.start.toString(), - range.end.toString() - ); -} +export function transformStreamsMessagesReplyResp3(reply: UnwrapReply): MapReply | NullReply { + if (reply === null) return null as unknown as NullReply; + + if (reply instanceof Map) { + const ret = new Map(); -export function pushSlotRangesArguments( - args: RedisCommandArguments, - ranges: SlotRange | Array -): RedisCommandArguments { - if (Array.isArray(ranges)) { - for (const range of ranges) { - pushSlotRangeArguments(args, range); - } - } else { - pushSlotRangeArguments(args, ranges); + for (const [n, rawMessages] of reply) { + const name = n as unknown as UnwrapReply; + + ret.set(name.toString(), transformStreamMessagesReply(rawMessages)); } - return args; -} + return ret as unknown as MapReply + } else if (reply instanceof Array) { + const ret = []; -export type RawRangeReply = [ - start: number, - end: number -]; + for (let i=0; i < reply.length; i += 2) { + const name = reply[i] as BlobStringReply; + const rawMessages = reply[i+1] as ArrayReply; -export interface RangeReply { - start: number; - end: number; -} + ret.push(name); + ret.push(transformStreamMessagesReply(rawMessages)); + } -export function transformRangeReply([start, end]: RawRangeReply): RangeReply { - return { - start, - end - }; + return ret as unknown as MapReply + } else { + const ret = Object.create(null); + for (const [name, rawMessages] of Object.entries(reply)) { + ret[name] = transformStreamMessagesReply(rawMessages); + } + + return ret as unknown as MapReply + } } diff --git a/packages/client/lib/commands/index.ts b/packages/client/lib/commands/index.ts index 60f9720c8d1..024ee2191b8 100644 --- a/packages/client/lib/commands/index.ts +++ b/packages/client/lib/commands/index.ts @@ -1,91 +1,1032 @@ -import { ClientCommandOptions } from '../client'; -import { CommandOptions } from '../command-options'; -import { RedisScriptConfig, SHA1 } from '../lua-script'; +import type { RedisCommands } from '../RESP/types'; +import ACL_CAT from './ACL_CAT'; +import ACL_DELUSER from './ACL_DELUSER'; +import ACL_DRYRUN from './ACL_DRYRUN'; +import ACL_GENPASS from './ACL_GENPASS'; +import ACL_GETUSER from './ACL_GETUSER'; +import ACL_LIST from './ACL_LIST'; +import ACL_LOAD from './ACL_LOAD'; +import ACL_LOG_RESET from './ACL_LOG_RESET'; +import ACL_LOG from './ACL_LOG'; +import ACL_SAVE from './ACL_SAVE'; +import ACL_SETUSER from './ACL_SETUSER'; +import ACL_USERS from './ACL_USERS'; +import ACL_WHOAMI from './ACL_WHOAMI'; +import APPEND from './APPEND'; +import ASKING from './ASKING'; +import AUTH from './AUTH'; +import BGREWRITEAOF from './BGREWRITEAOF'; +import BGSAVE from './BGSAVE'; +import BITCOUNT from './BITCOUNT'; +import BITFIELD_RO from './BITFIELD_RO'; +import BITFIELD from './BITFIELD'; +import BITOP from './BITOP'; +import BITPOS from './BITPOS'; +import BLMOVE from './BLMOVE'; +import BLMPOP from './BLMPOP'; +import BLPOP from './BLPOP'; +import BRPOP from './BRPOP'; +import BRPOPLPUSH from './BRPOPLPUSH'; +import BZMPOP from './BZMPOP'; +import BZPOPMAX from './BZPOPMAX'; +import BZPOPMIN from './BZPOPMIN'; +import CLIENT_CACHING from './CLIENT_CACHING'; +import CLIENT_GETNAME from './CLIENT_GETNAME'; +import CLIENT_GETREDIR from './CLIENT_GETREDIR'; +import CLIENT_ID from './CLIENT_ID'; +import CLIENT_INFO from './CLIENT_INFO'; +import CLIENT_KILL from './CLIENT_KILL'; +import CLIENT_LIST from './CLIENT_LIST'; +import CLIENT_NO_EVICT from './CLIENT_NO-EVICT'; +import CLIENT_NO_TOUCH from './CLIENT_NO-TOUCH'; +import CLIENT_PAUSE from './CLIENT_PAUSE'; +import CLIENT_SETNAME from './CLIENT_SETNAME'; +import CLIENT_TRACKING from './CLIENT_TRACKING'; +import CLIENT_TRACKINGINFO from './CLIENT_TRACKINGINFO'; +import CLIENT_UNPAUSE from './CLIENT_UNPAUSE'; +import CLUSTER_ADDSLOTS from './CLUSTER_ADDSLOTS'; +import CLUSTER_ADDSLOTSRANGE from './CLUSTER_ADDSLOTSRANGE'; +import CLUSTER_BUMPEPOCH from './CLUSTER_BUMPEPOCH'; +import CLUSTER_COUNT_FAILURE_REPORTS from './CLUSTER_COUNT-FAILURE-REPORTS'; +import CLUSTER_COUNTKEYSINSLOT from './CLUSTER_COUNTKEYSINSLOT'; +import CLUSTER_DELSLOTS from './CLUSTER_DELSLOTS'; +import CLUSTER_DELSLOTSRANGE from './CLUSTER_DELSLOTSRANGE'; +import CLUSTER_FAILOVER from './CLUSTER_FAILOVER'; +import CLUSTER_FLUSHSLOTS from './CLUSTER_FLUSHSLOTS'; +import CLUSTER_FORGET from './CLUSTER_FORGET'; +import CLUSTER_GETKEYSINSLOT from './CLUSTER_GETKEYSINSLOT'; +import CLUSTER_INFO from './CLUSTER_INFO'; +import CLUSTER_KEYSLOT from './CLUSTER_KEYSLOT'; +import CLUSTER_LINKS from './CLUSTER_LINKS'; +import CLUSTER_MEET from './CLUSTER_MEET'; +import CLUSTER_MYID from './CLUSTER_MYID'; +import CLUSTER_MYSHARDID from './CLUSTER_MYSHARDID'; +import CLUSTER_NODES from './CLUSTER_NODES'; +import CLUSTER_REPLICAS from './CLUSTER_REPLICAS'; +import CLUSTER_REPLICATE from './CLUSTER_REPLICATE'; +import CLUSTER_RESET from './CLUSTER_RESET'; +import CLUSTER_SAVECONFIG from './CLUSTER_SAVECONFIG'; +import CLUSTER_SET_CONFIG_EPOCH from './CLUSTER_SET-CONFIG-EPOCH'; +import CLUSTER_SETSLOT from './CLUSTER_SETSLOT'; +import CLUSTER_SLOTS from './CLUSTER_SLOTS'; +import COMMAND_COUNT from './COMMAND_COUNT'; +import COMMAND_GETKEYS from './COMMAND_GETKEYS'; +import COMMAND_GETKEYSANDFLAGS from './COMMAND_GETKEYSANDFLAGS'; +import COMMAND_INFO from './COMMAND_INFO'; +import COMMAND_LIST from './COMMAND_LIST'; +import COMMAND from './COMMAND'; +import CONFIG_GET from './CONFIG_GET'; +import CONFIG_RESETASTAT from './CONFIG_RESETSTAT'; +import CONFIG_REWRITE from './CONFIG_REWRITE'; +import CONFIG_SET from './CONFIG_SET'; +import COPY from './COPY'; +import DBSIZE from './DBSIZE'; +import DECR from './DECR'; +import DECRBY from './DECRBY'; +import DEL from './DEL'; +import DUMP from './DUMP'; +import ECHO from './ECHO'; +import EVAL_RO from './EVAL_RO'; +import EVAL from './EVAL'; +import EVALSHA_RO from './EVALSHA_RO'; +import EVALSHA from './EVALSHA'; +import GEOADD from './GEOADD'; +import GEODIST from './GEODIST'; +import GEOHASH from './GEOHASH'; +import GEOPOS from './GEOPOS'; +import GEORADIUS_RO_WITH from './GEORADIUS_RO_WITH'; +import GEORADIUS_RO from './GEORADIUS_RO'; +import GEORADIUS_STORE from './GEORADIUS_STORE'; +import GEORADIUS_WITH from './GEORADIUS_WITH'; +import GEORADIUS from './GEORADIUS'; +import GEORADIUSBYMEMBER_RO_WITH from './GEORADIUSBYMEMBER_RO_WITH'; +import GEORADIUSBYMEMBER_RO from './GEORADIUSBYMEMBER_RO'; +import GEORADIUSBYMEMBER_STORE from './GEORADIUSBYMEMBER_STORE'; +import GEORADIUSBYMEMBER_WITH from './GEORADIUSBYMEMBER_WITH'; +import GEORADIUSBYMEMBER from './GEORADIUSBYMEMBER'; +import GEOSEARCH_WITH from './GEOSEARCH_WITH'; +import GEOSEARCH from './GEOSEARCH'; +import GEOSEARCHSTORE from './GEOSEARCHSTORE'; +import GET from './GET'; +import GETBIT from './GETBIT'; +import GETDEL from './GETDEL'; +import GETEX from './GETEX'; +import GETRANGE from './GETRANGE'; +import GETSET from './GETSET'; +import EXISTS from './EXISTS'; +import EXPIRE from './EXPIRE'; +import EXPIREAT from './EXPIREAT'; +import EXPIRETIME from './EXPIRETIME'; +import FLUSHALL from './FLUSHALL'; +import FLUSHDB from './FLUSHDB'; +import FCALL from './FCALL'; +import FCALL_RO from './FCALL_RO'; +import FUNCTION_DELETE from './FUNCTION_DELETE'; +import FUNCTION_DUMP from './FUNCTION_DUMP'; +import FUNCTION_FLUSH from './FUNCTION_FLUSH'; +import FUNCTION_KILL from './FUNCTION_KILL'; +import FUNCTION_LIST_WITHCODE from './FUNCTION_LIST_WITHCODE'; +import FUNCTION_LIST from './FUNCTION_LIST'; +import FUNCTION_LOAD from './FUNCTION_LOAD'; +import FUNCTION_RESTORE from './FUNCTION_RESTORE'; +import FUNCTION_STATS from './FUNCTION_STATS'; +import HDEL from './HDEL'; +import HELLO from './HELLO'; +import HEXISTS from './HEXISTS'; +import HEXPIRE from './HEXPIRE'; +import HEXPIREAT from './HEXPIREAT'; +import HEXPIRETIME from './HEXPIRETIME'; +import HGET from './HGET'; +import HGETALL from './HGETALL'; +import HINCRBY from './HINCRBY'; +import HINCRBYFLOAT from './HINCRBYFLOAT'; +import HKEYS from './HKEYS'; +import HLEN from './HLEN'; +import HMGET from './HMGET'; +import HPERSIST from './HPERSIST'; +import HPEXPIRE from './HPEXPIRE'; +import HPEXPIREAT from './HPEXPIREAT'; +import HPEXPIRETIME from './HPEXPIRETIME'; +import HPTTL from './HPTTL'; +import HRANDFIELD_COUNT_WITHVALUES from './HRANDFIELD_COUNT_WITHVALUES'; +import HRANDFIELD_COUNT from './HRANDFIELD_COUNT'; +import HRANDFIELD from './HRANDFIELD'; +import HSCAN from './HSCAN'; +import HSCAN_NOVALUES from './HSCAN_NOVALUES'; +import HSET from './HSET'; +import HSETNX from './HSETNX'; +import HSTRLEN from './HSTRLEN'; +import HTTL from './HTTL'; +import HVALS from './HVALS'; +import INCR from './INCR'; +import INCRBY from './INCRBY'; +import INCRBYFLOAT from './INCRBYFLOAT'; +import INFO from './INFO'; +import KEYS from './KEYS'; +import LASTSAVE from './LASTSAVE'; +import LATENCY_DOCTOR from './LATENCY_DOCTOR'; +import LATENCY_GRAPH from './LATENCY_GRAPH'; +import LATENCY_HISTORY from './LATENCY_HISTORY'; +import LATENCY_LATEST from './LATENCY_LATEST'; +import LCS_IDX_WITHMATCHLEN from './LCS_IDX_WITHMATCHLEN'; +import LCS_IDX from './LCS_IDX'; +import LCS_LEN from './LCS_LEN'; +import LCS from './LCS'; +import LINDEX from './LINDEX'; +import LINSERT from './LINSERT'; +import LLEN from './LLEN'; +import LMOVE from './LMOVE'; +import LMPOP from './LMPOP'; +import LOLWUT from './LOLWUT'; +import LPOP_COUNT from './LPOP_COUNT'; +import LPOP from './LPOP'; +import LPOS_COUNT from './LPOS_COUNT'; +import LPOS from './LPOS'; +import LPUSH from './LPUSH'; +import LPUSHX from './LPUSHX'; +import LRANGE from './LRANGE'; +import LREM from './LREM'; +import LSET from './LSET'; +import LTRIM from './LTRIM'; +import MEMORY_DOCTOR from './MEMORY_DOCTOR'; +import MEMORY_MALLOC_STATS from './MEMORY_MALLOC-STATS'; +import MEMORY_PURGE from './MEMORY_PURGE'; +import MEMORY_STATS from './MEMORY_STATS'; +import MEMORY_USAGE from './MEMORY_USAGE'; +import MGET from './MGET'; +import MIGRATE from './MIGRATE'; +import MODULE_LIST from './MODULE_LIST'; +import MODULE_LOAD from './MODULE_LOAD'; +import MODULE_UNLOAD from './MODULE_UNLOAD'; +import MOVE from './MOVE'; +import MSET from './MSET'; +import MSETNX from './MSETNX'; +import OBJECT_ENCODING from './OBJECT_ENCODING'; +import OBJECT_FREQ from './OBJECT_FREQ'; +import OBJECT_IDLETIME from './OBJECT_IDLETIME'; +import OBJECT_REFCOUNT from './OBJECT_REFCOUNT'; +import PERSIST from './PERSIST'; +import PEXPIRE from './PEXPIRE'; +import PEXPIREAT from './PEXPIREAT'; +import PEXPIRETIME from './PEXPIRETIME'; +import PFADD from './PFADD'; +import PFCOUNT from './PFCOUNT'; +import PFMERGE from './PFMERGE'; +import PING from './PING'; +import PSETEX from './PSETEX'; +import PTTL from './PTTL'; +import PUBLISH from './PUBLISH'; +import PUBSUB_CHANNELS from './PUBSUB_CHANNELS'; +import PUBSUB_NUMPAT from './PUBSUB_NUMPAT'; +import PUBSUB_NUMSUB from './PUBSUB_NUMSUB'; +import PUBSUB_SHARDNUMSUB from './PUBSUB_SHARDNUMSUB'; +import PUBSUB_SHARDCHANNELS from './PUBSUB_SHARDCHANNELS'; +import RANDOMKEY from './RANDOMKEY'; +import READONLY from './READONLY'; +import RENAME from './RENAME'; +import RENAMENX from './RENAMENX'; +import REPLICAOF from './REPLICAOF'; +import RESTORE_ASKING from './RESTORE-ASKING'; +import RESTORE from './RESTORE'; +import ROLE from './ROLE'; +import RPOP_COUNT from './RPOP_COUNT'; +import RPOP from './RPOP'; +import RPOPLPUSH from './RPOPLPUSH'; +import RPUSH from './RPUSH'; +import RPUSHX from './RPUSHX'; +import SADD from './SADD'; +import SCAN from './SCAN'; +import SCARD from './SCARD'; +import SCRIPT_DEBUG from './SCRIPT_DEBUG'; +import SCRIPT_EXISTS from './SCRIPT_EXISTS'; +import SCRIPT_FLUSH from './SCRIPT_FLUSH'; +import SCRIPT_KILL from './SCRIPT_KILL'; +import SCRIPT_LOAD from './SCRIPT_LOAD'; +import SDIFF from './SDIFF'; +import SDIFFSTORE from './SDIFFSTORE'; +import SET from './SET'; +import SETBIT from './SETBIT'; +import SETEX from './SETEX'; +import SETNX from './SETNX'; +import SETRANGE from './SETRANGE'; +import SINTER from './SINTER'; +import SINTERCARD from './SINTERCARD'; +import SINTERSTORE from './SINTERSTORE'; +import SISMEMBER from './SISMEMBER'; +import SMEMBERS from './SMEMBERS'; +import SMISMEMBER from './SMISMEMBER'; +import SMOVE from './SMOVE'; +import SORT_RO from './SORT_RO'; +import SORT_STORE from './SORT_STORE'; +import SORT from './SORT'; +import SPOP_COUNT from './SPOP_COUNT'; +import SPOP from './SPOP'; +import SPUBLISH from './SPUBLISH'; +import SRANDMEMBER_COUNT from './SRANDMEMBER_COUNT'; +import SRANDMEMBER from './SRANDMEMBER'; +import SREM from './SREM'; +import SSCAN from './SSCAN'; +import STRLEN from './STRLEN'; +import SUNION from './SUNION'; +import SUNIONSTORE from './SUNIONSTORE'; +import SWAPDB from './SWAPDB'; +import TIME from './TIME'; +import TOUCH from './TOUCH'; +import TTL from './TTL'; +import TYPE from './TYPE'; +import UNLINK from './UNLINK'; +import WAIT from './WAIT'; +import XACK from './XACK'; +import XADD_NOMKSTREAM from './XADD_NOMKSTREAM'; +import XADD from './XADD'; +import XAUTOCLAIM_JUSTID from './XAUTOCLAIM_JUSTID'; +import XAUTOCLAIM from './XAUTOCLAIM'; +import XCLAIM_JUSTID from './XCLAIM_JUSTID'; +import XCLAIM from './XCLAIM'; +import XDEL from './XDEL'; +import XGROUP_CREATE from './XGROUP_CREATE'; +import XGROUP_CREATECONSUMER from './XGROUP_CREATECONSUMER'; +import XGROUP_DELCONSUMER from './XGROUP_DELCONSUMER'; +import XGROUP_DESTROY from './XGROUP_DESTROY'; +import XGROUP_SETID from './XGROUP_SETID'; +import XINFO_CONSUMERS from './XINFO_CONSUMERS'; +import XINFO_GROUPS from './XINFO_GROUPS'; +import XINFO_STREAM from './XINFO_STREAM'; +import XLEN from './XLEN'; +import XPENDING_RANGE from './XPENDING_RANGE'; +import XPENDING from './XPENDING'; +import XRANGE from './XRANGE'; +import XREAD from './XREAD'; +import XREADGROUP from './XREADGROUP'; +import XREVRANGE from './XREVRANGE'; +import XSETID from './XSETID'; +import XTRIM from './XTRIM'; +import ZADD_INCR from './ZADD_INCR'; +import ZADD from './ZADD'; +import ZCARD from './ZCARD'; +import ZCOUNT from './ZCOUNT'; +import ZDIFF_WITHSCORES from './ZDIFF_WITHSCORES'; +import ZDIFF from './ZDIFF'; +import ZDIFFSTORE from './ZDIFFSTORE'; +import ZINCRBY from './ZINCRBY'; +import ZINTER_WITHSCORES from './ZINTER_WITHSCORES'; +import ZINTER from './ZINTER'; +import ZINTERCARD from './ZINTERCARD'; +import ZINTERSTORE from './ZINTERSTORE'; +import ZLEXCOUNT from './ZLEXCOUNT'; +import ZMPOP from './ZMPOP'; +import ZMSCORE from './ZMSCORE'; +import ZPOPMAX_COUNT from './ZPOPMAX_COUNT'; +import ZPOPMAX from './ZPOPMAX'; +import ZPOPMIN_COUNT from './ZPOPMIN_COUNT'; +import ZPOPMIN from './ZPOPMIN'; +import ZRANDMEMBER_COUNT_WITHSCORES from './ZRANDMEMBER_COUNT_WITHSCORES'; +import ZRANDMEMBER_COUNT from './ZRANDMEMBER_COUNT'; +import ZRANDMEMBER from './ZRANDMEMBER'; +import ZRANGE_WITHSCORES from './ZRANGE_WITHSCORES'; +import ZRANGE from './ZRANGE'; +import ZRANGEBYLEX from './ZRANGEBYLEX'; +import ZRANGEBYSCORE_WITHSCORES from './ZRANGEBYSCORE_WITHSCORES'; +import ZRANGEBYSCORE from './ZRANGEBYSCORE'; +import ZRANGESTORE from './ZRANGESTORE'; +import ZREMRANGEBYSCORE from './ZREMRANGEBYSCORE'; +import ZRANK_WITHSCORE from './ZRANK_WITHSCORE'; +import ZRANK from './ZRANK'; +import ZREM from './ZREM'; +import ZREMRANGEBYLEX from './ZREMRANGEBYLEX'; +import ZREMRANGEBYRANK from './ZREMRANGEBYRANK'; +import ZREVRANK from './ZREVRANK'; +import ZSCAN from './ZSCAN'; +import ZSCORE from './ZSCORE'; +import ZUNION_WITHSCORES from './ZUNION_WITHSCORES'; +import ZUNION from './ZUNION'; +import ZUNIONSTORE from './ZUNIONSTORE'; -export type RedisCommandRawReply = string | number | Buffer | null | undefined | Array; - -export type RedisCommandArgument = string | Buffer; - -export type RedisCommandArguments = Array & { preserve?: unknown }; - -export interface RedisCommand { - FIRST_KEY_INDEX?: number | ((...args: Array) => RedisCommandArgument | undefined); - IS_READ_ONLY?: boolean; - TRANSFORM_LEGACY_REPLY?: boolean; - transformArguments(this: void, ...args: Array): RedisCommandArguments; - transformReply?(this: void, reply: any, preserved?: any): any; -} - -export type RedisCommandReply = - C['transformReply'] extends (...args: any) => infer T ? T : RedisCommandRawReply; - -export type ConvertArgumentType = - Type extends RedisCommandArgument ? ( - Type extends (string & ToType) ? Type : ToType - ) : ( - Type extends Set ? Set> : ( - Type extends Map ? Map> : ( - Type extends Array ? Array> : ( - Type extends Date ? Type : ( - Type extends Record ? { - [Property in keyof Type]: ConvertArgumentType - } : Type - ) - ) - ) - ) - ); - -export type RedisCommandSignature< - Command extends RedisCommand, - Params extends Array = Parameters -> = >( - ...args: Params | [options: Options, ...rest: Params] -) => Promise< - ConvertArgumentType< - RedisCommandReply, - Options['returnBuffers'] extends true ? Buffer : string - > ->; - -export interface RedisCommands { - [command: string]: RedisCommand; -} - -export interface RedisModule { - [command: string]: RedisCommand; -} - -export interface RedisModules { - [module: string]: RedisModule; -} - -export interface RedisFunction extends RedisCommand { - NUMBER_OF_KEYS?: number; -} - -export interface RedisFunctionLibrary { - [fn: string]: RedisFunction; -} - -export interface RedisFunctions { - [library: string]: RedisFunctionLibrary; -} - -export type RedisScript = RedisScriptConfig & SHA1; - -export interface RedisScripts { - [script: string]: RedisScript; -} - -export interface RedisExtensions< - M extends RedisModules = RedisModules, - F extends RedisFunctions = RedisFunctions, - S extends RedisScripts = RedisScripts -> { - modules?: M; - functions?: F; - scripts?: S; -} - -export type ExcludeMappedString = string extends S ? never : S; +export default { + ACL_CAT, + aclCat: ACL_CAT, + ACL_DELUSER, + aclDelUser: ACL_DELUSER, + ACL_DRYRUN, + aclDryRun: ACL_DRYRUN, + ACL_GENPASS, + aclGenPass: ACL_GENPASS, + ACL_GETUSER, + aclGetUser: ACL_GETUSER, + ACL_LIST, + aclList: ACL_LIST, + ACL_LOAD, + aclLoad: ACL_LOAD, + ACL_LOG_RESET, + aclLogReset: ACL_LOG_RESET, + ACL_LOG, + aclLog: ACL_LOG, + ACL_SAVE, + aclSave: ACL_SAVE, + ACL_SETUSER, + aclSetUser: ACL_SETUSER, + ACL_USERS, + aclUsers: ACL_USERS, + ACL_WHOAMI, + aclWhoAmI: ACL_WHOAMI, + APPEND, + append: APPEND, + ASKING, + asking: ASKING, + AUTH, + auth: AUTH, + BGREWRITEAOF, + bgRewriteAof: BGREWRITEAOF, + BGSAVE, + bgSave: BGSAVE, + BITCOUNT, + bitCount: BITCOUNT, + BITFIELD_RO, + bitFieldRo: BITFIELD_RO, + BITFIELD, + bitField: BITFIELD, + BITOP, + bitOp: BITOP, + BITPOS, + bitPos: BITPOS, + BLMOVE, + blMove: BLMOVE, + BLMPOP, + blmPop: BLMPOP, + BLPOP, + blPop: BLPOP, + BRPOP, + brPop: BRPOP, + BRPOPLPUSH, + brPopLPush: BRPOPLPUSH, + BZMPOP, + bzmPop: BZMPOP, + BZPOPMAX, + bzPopMax: BZPOPMAX, + BZPOPMIN, + bzPopMin: BZPOPMIN, + CLIENT_CACHING, + clientCaching: CLIENT_CACHING, + CLIENT_GETNAME, + clientGetName: CLIENT_GETNAME, + CLIENT_GETREDIR, + clientGetRedir: CLIENT_GETREDIR, + CLIENT_ID, + clientId: CLIENT_ID, + CLIENT_INFO, + clientInfo: CLIENT_INFO, + CLIENT_KILL, + clientKill: CLIENT_KILL, + CLIENT_LIST, + clientList: CLIENT_LIST, + 'CLIENT_NO-EVICT': CLIENT_NO_EVICT, + clientNoEvict: CLIENT_NO_EVICT, + 'CLIENT_NO-TOUCH': CLIENT_NO_TOUCH, + clientNoTouch: CLIENT_NO_TOUCH, + CLIENT_PAUSE, + clientPause: CLIENT_PAUSE, + CLIENT_SETNAME, + clientSetName: CLIENT_SETNAME, + CLIENT_TRACKING, + clientTracking: CLIENT_TRACKING, + CLIENT_TRACKINGINFO, + clientTrackingInfo: CLIENT_TRACKINGINFO, + CLIENT_UNPAUSE, + clientUnpause: CLIENT_UNPAUSE, + CLUSTER_ADDSLOTS, + clusterAddSlots: CLUSTER_ADDSLOTS, + CLUSTER_ADDSLOTSRANGE, + clusterAddSlotsRange: CLUSTER_ADDSLOTSRANGE, + CLUSTER_BUMPEPOCH, + clusterBumpEpoch: CLUSTER_BUMPEPOCH, + 'CLUSTER_COUNT-FAILURE-REPORTS': CLUSTER_COUNT_FAILURE_REPORTS, + clusterCountFailureReports: CLUSTER_COUNT_FAILURE_REPORTS, + CLUSTER_COUNTKEYSINSLOT, + clusterCountKeysInSlot: CLUSTER_COUNTKEYSINSLOT, + CLUSTER_DELSLOTS, + clusterDelSlots: CLUSTER_DELSLOTS, + CLUSTER_DELSLOTSRANGE, + clusterDelSlotsRange: CLUSTER_DELSLOTSRANGE, + CLUSTER_FAILOVER, + clusterFailover: CLUSTER_FAILOVER, + CLUSTER_FLUSHSLOTS, + clusterFlushSlots: CLUSTER_FLUSHSLOTS, + CLUSTER_FORGET, + clusterForget: CLUSTER_FORGET, + CLUSTER_GETKEYSINSLOT, + clusterGetKeysInSlot: CLUSTER_GETKEYSINSLOT, + CLUSTER_INFO, + clusterInfo: CLUSTER_INFO, + CLUSTER_KEYSLOT, + clusterKeySlot: CLUSTER_KEYSLOT, + CLUSTER_LINKS, + clusterLinks: CLUSTER_LINKS, + CLUSTER_MEET, + clusterMeet: CLUSTER_MEET, + CLUSTER_MYID, + clusterMyId: CLUSTER_MYID, + CLUSTER_MYSHARDID, + clusterMyShardId: CLUSTER_MYSHARDID, + CLUSTER_NODES, + clusterNodes: CLUSTER_NODES, + CLUSTER_REPLICAS, + clusterReplicas: CLUSTER_REPLICAS, + CLUSTER_REPLICATE, + clusterReplicate: CLUSTER_REPLICATE, + CLUSTER_RESET, + clusterReset: CLUSTER_RESET, + CLUSTER_SAVECONFIG, + clusterSaveConfig: CLUSTER_SAVECONFIG, + 'CLUSTER_SET-CONFIG-EPOCH': CLUSTER_SET_CONFIG_EPOCH, + clusterSetConfigEpoch: CLUSTER_SET_CONFIG_EPOCH, + CLUSTER_SETSLOT, + clusterSetSlot: CLUSTER_SETSLOT, + CLUSTER_SLOTS, + clusterSlots: CLUSTER_SLOTS, + COMMAND_COUNT, + commandCount: COMMAND_COUNT, + COMMAND_GETKEYS, + commandGetKeys: COMMAND_GETKEYS, + COMMAND_GETKEYSANDFLAGS, + commandGetKeysAndFlags: COMMAND_GETKEYSANDFLAGS, + COMMAND_INFO, + commandInfo: COMMAND_INFO, + COMMAND_LIST, + commandList: COMMAND_LIST, + COMMAND, + command: COMMAND, + CONFIG_GET, + configGet: CONFIG_GET, + CONFIG_RESETASTAT, + configResetStat: CONFIG_RESETASTAT, + CONFIG_REWRITE, + configRewrite: CONFIG_REWRITE, + CONFIG_SET, + configSet: CONFIG_SET, + COPY, + copy: COPY, + DBSIZE, + dbSize: DBSIZE, + DECR, + decr: DECR, + DECRBY, + decrBy: DECRBY, + DEL, + del: DEL, + DUMP, + dump: DUMP, + ECHO, + echo: ECHO, + EVAL_RO, + evalRo: EVAL_RO, + EVAL, + eval: EVAL, + EVALSHA_RO, + evalShaRo: EVALSHA_RO, + EVALSHA, + evalSha: EVALSHA, + EXISTS, + exists: EXISTS, + EXPIRE, + expire: EXPIRE, + EXPIREAT, + expireAt: EXPIREAT, + EXPIRETIME, + expireTime: EXPIRETIME, + FLUSHALL, + flushAll: FLUSHALL, + FLUSHDB, + flushDb: FLUSHDB, + FCALL, + fCall: FCALL, + FCALL_RO, + fCallRo: FCALL_RO, + FUNCTION_DELETE, + functionDelete: FUNCTION_DELETE, + FUNCTION_DUMP, + functionDump: FUNCTION_DUMP, + FUNCTION_FLUSH, + functionFlush: FUNCTION_FLUSH, + FUNCTION_KILL, + functionKill: FUNCTION_KILL, + FUNCTION_LIST_WITHCODE, + functionListWithCode: FUNCTION_LIST_WITHCODE, + FUNCTION_LIST, + functionList: FUNCTION_LIST, + FUNCTION_LOAD, + functionLoad: FUNCTION_LOAD, + FUNCTION_RESTORE, + functionRestore: FUNCTION_RESTORE, + FUNCTION_STATS, + functionStats: FUNCTION_STATS, + GEOADD, + geoAdd: GEOADD, + GEODIST, + geoDist: GEODIST, + GEOHASH, + geoHash: GEOHASH, + GEOPOS, + geoPos: GEOPOS, + GEORADIUS_RO_WITH, + geoRadiusRoWith: GEORADIUS_RO_WITH, + GEORADIUS_RO, + geoRadiusRo: GEORADIUS_RO, + GEORADIUS_STORE, + geoRadiusStore: GEORADIUS_STORE, + GEORADIUS_WITH, + geoRadiusWith: GEORADIUS_WITH, + GEORADIUS, + geoRadius: GEORADIUS, + GEORADIUSBYMEMBER_RO_WITH, + geoRadiusByMemberRoWith: GEORADIUSBYMEMBER_RO_WITH, + GEORADIUSBYMEMBER_RO, + geoRadiusByMemberRo: GEORADIUSBYMEMBER_RO, + GEORADIUSBYMEMBER_STORE, + geoRadiusByMemberStore: GEORADIUSBYMEMBER_STORE, + GEORADIUSBYMEMBER_WITH, + geoRadiusByMemberWith: GEORADIUSBYMEMBER_WITH, + GEORADIUSBYMEMBER, + geoRadiusByMember: GEORADIUSBYMEMBER, + GEOSEARCH_WITH, + geoSearchWith: GEOSEARCH_WITH, + GEOSEARCH, + geoSearch: GEOSEARCH, + GEOSEARCHSTORE, + geoSearchStore: GEOSEARCHSTORE, + GET, + get: GET, + GETBIT, + getBit: GETBIT, + GETDEL, + getDel: GETDEL, + GETEX, + getEx: GETEX, + GETRANGE, + getRange: GETRANGE, + GETSET, + getSet: GETSET, + HDEL, + hDel: HDEL, + HELLO, + hello: HELLO, + HEXISTS, + hExists: HEXISTS, + HEXPIRE, + hExpire: HEXPIRE, + HEXPIREAT, + hExpireAt: HEXPIREAT, + HEXPIRETIME, + hExpireTime: HEXPIRETIME, + HGET, + hGet: HGET, + HGETALL, + hGetAll: HGETALL, + HINCRBY, + hIncrBy: HINCRBY, + HINCRBYFLOAT, + hIncrByFloat: HINCRBYFLOAT, + HKEYS, + hKeys: HKEYS, + HLEN, + hLen: HLEN, + HMGET, + hmGet: HMGET, + HPERSIST, + hPersist: HPERSIST, + HPEXPIRE, + hpExpire: HPEXPIRE, + HPEXPIREAT, + hpExpireAt: HPEXPIREAT, + HPEXPIRETIME, + hpExpireTime: HPEXPIRETIME, + HPTTL, + hpTTL: HPTTL, + HRANDFIELD_COUNT_WITHVALUES, + hRandFieldCountWithValues: HRANDFIELD_COUNT_WITHVALUES, + HRANDFIELD_COUNT, + hRandFieldCount: HRANDFIELD_COUNT, + HRANDFIELD, + hRandField: HRANDFIELD, + HSCAN, + hScan: HSCAN, + HSCAN_NOVALUES, + hScanNoValues: HSCAN_NOVALUES, + HSET, + hSet: HSET, + HSETNX, + hSetNX: HSETNX, + HSTRLEN, + hStrLen: HSTRLEN, + HTTL, + hTTL: HTTL, + HVALS, + hVals: HVALS, + INCR, + incr: INCR, + INCRBY, + incrBy: INCRBY, + INCRBYFLOAT, + incrByFloat: INCRBYFLOAT, + INFO, + info: INFO, + KEYS, + keys: KEYS, + LASTSAVE, + lastSave: LASTSAVE, + LATENCY_DOCTOR, + latencyDoctor: LATENCY_DOCTOR, + LATENCY_GRAPH, + latencyGraph: LATENCY_GRAPH, + LATENCY_HISTORY, + latencyHistory: LATENCY_HISTORY, + LATENCY_LATEST, + latencyLatest: LATENCY_LATEST, + LCS_IDX_WITHMATCHLEN, + lcsIdxWithMatchLen: LCS_IDX_WITHMATCHLEN, + LCS_IDX, + lcsIdx: LCS_IDX, + LCS_LEN, + lcsLen: LCS_LEN, + LCS, + lcs: LCS, + LINDEX, + lIndex: LINDEX, + LINSERT, + lInsert: LINSERT, + LLEN, + lLen: LLEN, + LMOVE, + lMove: LMOVE, + LMPOP, + lmPop: LMPOP, + LOLWUT, + LPOP_COUNT, + lPopCount: LPOP_COUNT, + LPOP, + lPop: LPOP, + LPOS_COUNT, + lPosCount: LPOS_COUNT, + LPOS, + lPos: LPOS, + LPUSH, + lPush: LPUSH, + LPUSHX, + lPushX: LPUSHX, + LRANGE, + lRange: LRANGE, + LREM, + lRem: LREM, + LSET, + lSet: LSET, + LTRIM, + lTrim: LTRIM, + MEMORY_DOCTOR, + memoryDoctor: MEMORY_DOCTOR, + 'MEMORY_MALLOC-STATS': MEMORY_MALLOC_STATS, + memoryMallocStats: MEMORY_MALLOC_STATS, + MEMORY_PURGE, + memoryPurge: MEMORY_PURGE, + MEMORY_STATS, + memoryStats: MEMORY_STATS, + MEMORY_USAGE, + memoryUsage: MEMORY_USAGE, + MGET, + mGet: MGET, + MIGRATE, + migrate: MIGRATE, + MODULE_LIST, + moduleList: MODULE_LIST, + MODULE_LOAD, + moduleLoad: MODULE_LOAD, + MODULE_UNLOAD, + moduleUnload: MODULE_UNLOAD, + MOVE, + move: MOVE, + MSET, + mSet: MSET, + MSETNX, + mSetNX: MSETNX, + OBJECT_ENCODING, + objectEncoding: OBJECT_ENCODING, + OBJECT_FREQ, + objectFreq: OBJECT_FREQ, + OBJECT_IDLETIME, + objectIdleTime: OBJECT_IDLETIME, + OBJECT_REFCOUNT, + objectRefCount: OBJECT_REFCOUNT, + PERSIST, + persist: PERSIST, + PEXPIRE, + pExpire: PEXPIRE, + PEXPIREAT, + pExpireAt: PEXPIREAT, + PEXPIRETIME, + pExpireTime: PEXPIRETIME, + PFADD, + pfAdd: PFADD, + PFCOUNT, + pfCount: PFCOUNT, + PFMERGE, + pfMerge: PFMERGE, + PING, + /** + * ping jsdoc + */ + ping: PING, + PSETEX, + pSetEx: PSETEX, + PTTL, + pTTL: PTTL, + PUBLISH, + publish: PUBLISH, + PUBSUB_CHANNELS, + pubSubChannels: PUBSUB_CHANNELS, + PUBSUB_NUMPAT, + pubSubNumPat: PUBSUB_NUMPAT, + PUBSUB_NUMSUB, + pubSubNumSub: PUBSUB_NUMSUB, + PUBSUB_SHARDNUMSUB, + pubSubShardNumSub: PUBSUB_SHARDNUMSUB, + PUBSUB_SHARDCHANNELS, + pubSubShardChannels: PUBSUB_SHARDCHANNELS, + RANDOMKEY, + randomKey: RANDOMKEY, + READONLY, + readonly: READONLY, + RENAME, + rename: RENAME, + RENAMENX, + renameNX: RENAMENX, + REPLICAOF, + replicaOf: REPLICAOF, + 'RESTORE-ASKING': RESTORE_ASKING, + restoreAsking: RESTORE_ASKING, + RESTORE, + restore: RESTORE, + RPOP_COUNT, + rPopCount: RPOP_COUNT, + ROLE, + role: ROLE, + RPOP, + rPop: RPOP, + RPOPLPUSH, + rPopLPush: RPOPLPUSH, + RPUSH, + rPush: RPUSH, + RPUSHX, + rPushX: RPUSHX, + SADD, + sAdd: SADD, + SCAN, + scan: SCAN, + SCARD, + sCard: SCARD, + SCRIPT_DEBUG, + scriptDebug: SCRIPT_DEBUG, + SCRIPT_EXISTS, + scriptExists: SCRIPT_EXISTS, + SCRIPT_FLUSH, + scriptFlush: SCRIPT_FLUSH, + SCRIPT_KILL, + scriptKill: SCRIPT_KILL, + SCRIPT_LOAD, + scriptLoad: SCRIPT_LOAD, + SDIFF, + sDiff: SDIFF, + SDIFFSTORE, + sDiffStore: SDIFFSTORE, + SET, + set: SET, + SETBIT, + setBit: SETBIT, + SETEX, + setEx: SETEX, + SETNX, + setNX: SETNX, + SETRANGE, + setRange: SETRANGE, + SINTER, + sInter: SINTER, + SINTERCARD, + sInterCard: SINTERCARD, + SINTERSTORE, + sInterStore: SINTERSTORE, + SISMEMBER, + sIsMember: SISMEMBER, + SMEMBERS, + sMembers: SMEMBERS, + SMISMEMBER, + smIsMember: SMISMEMBER, + SMOVE, + sMove: SMOVE, + SORT_RO, + sortRo: SORT_RO, + SORT_STORE, + sortStore: SORT_STORE, + SORT, + sort: SORT, + SPOP_COUNT, + sPopCount: SPOP_COUNT, + SPOP, + sPop: SPOP, + SPUBLISH, + sPublish: SPUBLISH, + SRANDMEMBER_COUNT, + sRandMemberCount: SRANDMEMBER_COUNT, + SRANDMEMBER, + sRandMember: SRANDMEMBER, + SREM, + sRem: SREM, + SSCAN, + sScan: SSCAN, + STRLEN, + strLen: STRLEN, + SUNION, + sUnion: SUNION, + SUNIONSTORE, + sUnionStore: SUNIONSTORE, + SWAPDB, + swapDb: SWAPDB, + TIME, + time: TIME, + TOUCH, + touch: TOUCH, + TTL, + ttl: TTL, + TYPE, + type: TYPE, + UNLINK, + unlink: UNLINK, + WAIT, + wait: WAIT, + XACK, + xAck: XACK, + XADD_NOMKSTREAM, + xAddNoMkStream: XADD_NOMKSTREAM, + XADD, + xAdd: XADD, + XAUTOCLAIM_JUSTID, + xAutoClaimJustId: XAUTOCLAIM_JUSTID, + XAUTOCLAIM, + xAutoClaim: XAUTOCLAIM, + XCLAIM_JUSTID, + xClaimJustId: XCLAIM_JUSTID, + XCLAIM, + xClaim: XCLAIM, + XDEL, + xDel: XDEL, + XGROUP_CREATE, + xGroupCreate: XGROUP_CREATE, + XGROUP_CREATECONSUMER, + xGroupCreateConsumer: XGROUP_CREATECONSUMER, + XGROUP_DELCONSUMER, + xGroupDelConsumer: XGROUP_DELCONSUMER, + XGROUP_DESTROY, + xGroupDestroy: XGROUP_DESTROY, + XGROUP_SETID, + xGroupSetId: XGROUP_SETID, + XINFO_CONSUMERS, + xInfoConsumers: XINFO_CONSUMERS, + XINFO_GROUPS, + xInfoGroups: XINFO_GROUPS, + XINFO_STREAM, + xInfoStream: XINFO_STREAM, + XLEN, + xLen: XLEN, + XPENDING_RANGE, + xPendingRange: XPENDING_RANGE, + XPENDING, + xPending: XPENDING, + XRANGE, + xRange: XRANGE, + XREAD, + xRead: XREAD, + XREADGROUP, + xReadGroup: XREADGROUP, + XREVRANGE, + xRevRange: XREVRANGE, + XSETID, + xSetId: XSETID, + XTRIM, + xTrim: XTRIM, + ZADD_INCR, + zAddIncr: ZADD_INCR, + ZADD, + zAdd: ZADD, + ZCARD, + zCard: ZCARD, + ZCOUNT, + zCount: ZCOUNT, + ZDIFF_WITHSCORES, + zDiffWithScores: ZDIFF_WITHSCORES, + ZDIFF, + zDiff: ZDIFF, + ZDIFFSTORE, + zDiffStore: ZDIFFSTORE, + ZINCRBY, + zIncrBy: ZINCRBY, + ZINTER_WITHSCORES, + zInterWithScores: ZINTER_WITHSCORES, + ZINTER, + zInter: ZINTER, + ZINTERCARD, + zInterCard: ZINTERCARD, + ZINTERSTORE, + zInterStore: ZINTERSTORE, + ZLEXCOUNT, + zLexCount: ZLEXCOUNT, + ZMPOP, + zmPop: ZMPOP, + ZMSCORE, + zmScore: ZMSCORE, + ZPOPMAX_COUNT, + zPopMaxCount: ZPOPMAX_COUNT, + ZPOPMAX, + zPopMax: ZPOPMAX, + ZPOPMIN_COUNT, + zPopMinCount: ZPOPMIN_COUNT, + ZPOPMIN, + zPopMin: ZPOPMIN, + ZRANDMEMBER_COUNT_WITHSCORES, + zRandMemberCountWithScores: ZRANDMEMBER_COUNT_WITHSCORES, + ZRANDMEMBER_COUNT, + zRandMemberCount: ZRANDMEMBER_COUNT, + ZRANDMEMBER, + zRandMember: ZRANDMEMBER, + ZRANGE_WITHSCORES, + zRangeWithScores: ZRANGE_WITHSCORES, + ZRANGE, + zRange: ZRANGE, + ZRANGEBYLEX, + zRangeByLex: ZRANGEBYLEX, + ZRANGEBYSCORE_WITHSCORES, + zRangeByScoreWithScores: ZRANGEBYSCORE_WITHSCORES, + ZRANGEBYSCORE, + zRangeByScore: ZRANGEBYSCORE, + ZRANGESTORE, + zRangeStore: ZRANGESTORE, + ZRANK_WITHSCORE, + zRankWithScore: ZRANK_WITHSCORE, + ZRANK, + zRank: ZRANK, + ZREM, + zRem: ZREM, + ZREMRANGEBYLEX, + zRemRangeByLex: ZREMRANGEBYLEX, + ZREMRANGEBYRANK, + zRemRangeByRank: ZREMRANGEBYRANK, + ZREMRANGEBYSCORE, + zRemRangeByScore: ZREMRANGEBYSCORE, + ZREVRANK, + zRevRank: ZREVRANK, + ZSCAN, + zScan: ZSCAN, + ZSCORE, + zScore: ZSCORE, + ZUNION_WITHSCORES, + zUnionWithScores: ZUNION_WITHSCORES, + ZUNION, + zUnion: ZUNION, + ZUNIONSTORE, + zUnionStore: ZUNIONSTORE +} as const satisfies RedisCommands; diff --git a/packages/client/lib/errors.ts b/packages/client/lib/errors.ts index aa97d9cf26d..8af4c5e5bed 100644 --- a/packages/client/lib/errors.ts +++ b/packages/client/lib/errors.ts @@ -1,84 +1,88 @@ -import { RedisCommandRawReply } from './commands'; - export class AbortError extends Error { - constructor() { - super('The command was aborted'); - } + constructor() { + super('The command was aborted'); + } } export class WatchError extends Error { - constructor() { - super('One (or more) of the watched keys has been changed'); - } + constructor(message = 'One (or more) of the watched keys has been changed') { + super(message); + } } export class ConnectionTimeoutError extends Error { - constructor() { - super('Connection timeout'); - } + constructor() { + super('Connection timeout'); + } } export class ClientClosedError extends Error { - constructor() { - super('The client is closed'); - } + constructor() { + super('The client is closed'); + } } export class ClientOfflineError extends Error { - constructor() { - super('The client is offline'); - } + constructor() { + super('The client is offline'); + } } export class DisconnectsClientError extends Error { - constructor() { - super('Disconnects client'); - } + constructor() { + super('Disconnects client'); + } } export class SocketClosedUnexpectedlyError extends Error { - constructor() { - super('Socket closed unexpectedly'); - } + constructor() { + super('Socket closed unexpectedly'); + } } export class RootNodesUnavailableError extends Error { - constructor() { - super('All the root nodes are unavailable'); - } + constructor() { + super('All the root nodes are unavailable'); + } } export class ReconnectStrategyError extends Error { - originalError: Error; - socketError: unknown; + originalError: Error; + socketError: unknown; - constructor(originalError: Error, socketError: unknown) { - super(originalError.message); - this.originalError = originalError; - this.socketError = socketError; - } + constructor(originalError: Error, socketError: unknown) { + super(originalError.message); + this.originalError = originalError; + this.socketError = socketError; + } } export class ErrorReply extends Error { - constructor(message: string) { - super(message); - this.stack = undefined; - } + constructor(message: string) { + super(message); + this.stack = undefined; + } } +export class SimpleError extends ErrorReply {} + +export class BlobError extends ErrorReply {} + +export class TimeoutError extends Error {} + export class MultiErrorReply extends ErrorReply { - replies; - errorIndexes; + replies: Array; + errorIndexes: Array; - constructor(replies: Array, errorIndexes: Array) { - super(`${errorIndexes.length} commands failed, see .replies and .errorIndexes for more information`); - this.replies = replies; - this.errorIndexes = errorIndexes; - } + constructor(replies: Array, errorIndexes: Array) { + super(`${errorIndexes.length} commands failed, see .replies and .errorIndexes for more information`); + this.replies = replies; + this.errorIndexes = errorIndexes; + } - *errors() { - for (const index of this.errorIndexes) { - yield this.replies[index]; - } + *errors() { + for (const index of this.errorIndexes) { + yield this.replies[index]; } + } } diff --git a/packages/client/lib/lua-script.ts b/packages/client/lib/lua-script.ts index da19417ec25..6d395b71232 100644 --- a/packages/client/lib/lua-script.ts +++ b/packages/client/lib/lua-script.ts @@ -1,22 +1,22 @@ -import { createHash } from 'crypto'; -import { RedisCommand } from './commands'; +import { createHash } from 'node:crypto'; +import { Command } from './RESP/types'; -export interface RedisScriptConfig extends RedisCommand { - SCRIPT: string; - NUMBER_OF_KEYS?: number; +export type RedisScriptConfig = Command & { + SCRIPT: string | Buffer; + NUMBER_OF_KEYS?: number; } export interface SHA1 { - SHA1: string; + SHA1: string; } export function defineScript(script: S): S & SHA1 { - return { - ...script, - SHA1: scriptSha1(script.SCRIPT) - }; + return { + ...script, + SHA1: scriptSha1(script.SCRIPT) + }; } -export function scriptSha1(script: string): string { - return createHash('sha1').update(script).digest('hex'); +export function scriptSha1(script: RedisScriptConfig['SCRIPT']): string { + return createHash('sha1').update(script).digest('hex'); } diff --git a/packages/client/lib/multi-command.spec.ts b/packages/client/lib/multi-command.spec.ts index b0f79c6e157..7e77f88d10b 100644 --- a/packages/client/lib/multi-command.spec.ts +++ b/packages/client/lib/multi-command.spec.ts @@ -1,96 +1,77 @@ -import { strict as assert } from 'assert'; +import { strict as assert } from 'node:assert'; import RedisMultiCommand from './multi-command'; -import { WatchError } from './errors'; import { SQUARE_SCRIPT } from './client/index.spec'; describe('Multi Command', () => { - it('generateChainId', () => { - assert.equal( - typeof RedisMultiCommand.generateChainId(), - 'symbol' - ); - }); - - it('addCommand', () => { - const multi = new RedisMultiCommand(); - multi.addCommand(['PING']); - - assert.deepEqual( - multi.queue[0].args, - ['PING'] - ); - }); + it('addCommand', () => { + const multi = new RedisMultiCommand(); + multi.addCommand(['PING']); - it('addScript', () => { - const multi = new RedisMultiCommand(); + assert.deepEqual( + multi.queue[0].args, + ['PING'] + ); + }); - multi.addScript(SQUARE_SCRIPT, ['1']); - assert.equal( - multi.scriptsInUse.has(SQUARE_SCRIPT.SHA1), - true - ); - assert.deepEqual( - multi.queue[0].args, - ['EVAL', SQUARE_SCRIPT.SCRIPT, '0', '1'] - ); + describe('addScript', () => { + const multi = new RedisMultiCommand(); - multi.addScript(SQUARE_SCRIPT, ['2']); - assert.equal( - multi.scriptsInUse.has(SQUARE_SCRIPT.SHA1), - true - ); - assert.deepEqual( - multi.queue[1].args, - ['EVALSHA', SQUARE_SCRIPT.SHA1, '0', '2'] - ); + it('should use EVAL', () => { + multi.addScript(SQUARE_SCRIPT, ['1']); + assert.deepEqual( + Array.from(multi.queue.at(-1).args), + ['EVAL', SQUARE_SCRIPT.SCRIPT, '1', '1'] + ); }); - describe('exec', () => { - it('without commands', () => { - assert.deepEqual( - new RedisMultiCommand().queue, - [] - ); - }); + it('should use EVALSHA', () => { + multi.addScript(SQUARE_SCRIPT, ['2']); + assert.deepEqual( + Array.from(multi.queue.at(-1).args), + ['EVALSHA', SQUARE_SCRIPT.SHA1, '1', '2'] + ); + }); - it('with commands', () => { - const multi = new RedisMultiCommand(); - multi.addCommand(['PING']); + it('without NUMBER_OF_KEYS', () => { + multi.addScript({ + ...SQUARE_SCRIPT, + NUMBER_OF_KEYS: undefined + }, ['2']); + assert.deepEqual( + Array.from(multi.queue.at(-1).args), + ['EVALSHA', SQUARE_SCRIPT.SHA1, '2'] + ); + }); + }); - assert.deepEqual( - multi.queue, - [{ - args: ['PING'], - transformReply: undefined - }] - ); - }); + describe('exec', () => { + it('without commands', () => { + assert.deepEqual( + new RedisMultiCommand().queue, + [] + ); }); - describe('handleExecReplies', () => { - it('WatchError', () => { - assert.throws( - () => new RedisMultiCommand().handleExecReplies([null]), - WatchError - ); - }); + it('with commands', () => { + const multi = new RedisMultiCommand(); + multi.addCommand(['PING']); - it('with replies', () => { - const multi = new RedisMultiCommand(); - multi.addCommand(['PING']); - assert.deepEqual( - multi.handleExecReplies(['OK', 'QUEUED', ['PONG']]), - ['PONG'] - ); - }); + assert.deepEqual( + multi.queue, + [{ + args: ['PING'], + transformReply: undefined + }] + ); }); + }); - it('transformReplies', () => { - const multi = new RedisMultiCommand(); - multi.addCommand(['PING'], (reply: string) => reply.substring(0, 2)); - assert.deepEqual( - multi.transformReplies(['PONG']), - ['PO'] - ); - }); + it('transformReplies', () => { + const multi = new RedisMultiCommand(); + multi.addCommand(['PING'], (reply: string) => reply.substring(0, 2)); + assert.deepEqual( + multi.transformReplies(['PONG']), + ['PO'] + ); + }); }); diff --git a/packages/client/lib/multi-command.ts b/packages/client/lib/multi-command.ts index 642c2ea36c0..a3ff4c99407 100644 --- a/packages/client/lib/multi-command.ts +++ b/packages/client/lib/multi-command.ts @@ -1,95 +1,64 @@ -import { fCallArguments } from './commander'; -import { RedisCommand, RedisCommandArguments, RedisCommandRawReply, RedisFunction, RedisScript } from './commands'; -import { ErrorReply, MultiErrorReply, WatchError } from './errors'; +import { CommandArguments, RedisScript, ReplyUnion, TransformReply, TypeMapping } from './RESP/types'; +import { ErrorReply, MultiErrorReply } from './errors'; + +export type MULTI_REPLY = { + GENERIC: 'generic'; + TYPED: 'typed'; +}; + +export type MultiReply = MULTI_REPLY[keyof MULTI_REPLY]; + +export type MultiReplyType = T extends MULTI_REPLY['TYPED'] ? REPLIES : Array; export interface RedisMultiQueuedCommand { - args: RedisCommandArguments; - transformReply?: RedisCommand['transformReply']; + args: CommandArguments; + transformReply?: TransformReply; } export default class RedisMultiCommand { - static generateChainId(): symbol { - return Symbol('RedisMultiCommand Chain Id'); - } + readonly queue: Array = []; - readonly queue: Array = []; + readonly scriptsInUse = new Set(); - readonly scriptsInUse = new Set(); + addCommand(args: CommandArguments, transformReply?: TransformReply) { + this.queue.push({ + args, + transformReply + }); + } - addCommand(args: RedisCommandArguments, transformReply?: RedisCommand['transformReply']): void { - this.queue.push({ - args, - transformReply - }); + addScript(script: RedisScript, args: CommandArguments, transformReply?: TransformReply) { + const redisArgs: CommandArguments = []; + redisArgs.preserve = args.preserve; + if (this.scriptsInUse.has(script.SHA1)) { + redisArgs.push('EVALSHA', script.SHA1); + } else { + this.scriptsInUse.add(script.SHA1); + redisArgs.push('EVAL', script.SCRIPT); } - addFunction(name: string, fn: RedisFunction, args: Array): RedisCommandArguments { - const transformedArguments = fCallArguments( - name, - fn, - fn.transformArguments(...args) - ); - this.queue.push({ - args: transformedArguments, - transformReply: fn.transformReply - }); - return transformedArguments; + if (script.NUMBER_OF_KEYS !== undefined) { + redisArgs.push(script.NUMBER_OF_KEYS.toString()); } - addScript(script: RedisScript, args: Array): RedisCommandArguments { - const transformedArguments: RedisCommandArguments = []; - if (this.scriptsInUse.has(script.SHA1)) { - transformedArguments.push( - 'EVALSHA', - script.SHA1 - ); - } else { - this.scriptsInUse.add(script.SHA1); - transformedArguments.push( - 'EVAL', - script.SCRIPT - ); - } - - if (script.NUMBER_OF_KEYS !== undefined) { - transformedArguments.push(script.NUMBER_OF_KEYS.toString()); - } + redisArgs.push(...args); - const scriptArguments = script.transformArguments(...args); - transformedArguments.push(...scriptArguments); - if (scriptArguments.preserve) { - transformedArguments.preserve = scriptArguments.preserve; + this.addCommand(redisArgs, transformReply); + } + + transformReplies(rawReplies: Array, typeMapping?: TypeMapping): Array { + const errorIndexes: Array = [], + replies = rawReplies.map((reply, i) => { + if (reply instanceof ErrorReply) { + errorIndexes.push(i); + return reply; } - this.addCommand( - transformedArguments, - script.transformReply - ); + const { transformReply, args } = this.queue[i]; + return transformReply ? transformReply(reply, args.preserve, typeMapping) : reply; + }); - return transformedArguments; - } - - handleExecReplies(rawReplies: Array): Array { - const execReply = rawReplies[rawReplies.length - 1] as (null | Array); - if (execReply === null) { - throw new WatchError(); - } - - return this.transformReplies(execReply); - } - - transformReplies(rawReplies: Array): Array { - const errorIndexes: Array = [], - replies = rawReplies.map((reply, i) => { - if (reply instanceof ErrorReply) { - errorIndexes.push(i); - return reply; - } - const { transformReply, args } = this.queue[i]; - return transformReply ? transformReply(reply, args.preserve) : reply; - }); - - if (errorIndexes.length) throw new MultiErrorReply(replies, errorIndexes); - return replies; - } + if (errorIndexes.length) throw new MultiErrorReply(replies, errorIndexes); + return replies; + } } diff --git a/packages/client/lib/sentinel/commands/SENTINEL_MASTER.ts b/packages/client/lib/sentinel/commands/SENTINEL_MASTER.ts new file mode 100644 index 00000000000..b260dcfba7d --- /dev/null +++ b/packages/client/lib/sentinel/commands/SENTINEL_MASTER.ts @@ -0,0 +1,12 @@ +import { RedisArgument, MapReply, BlobStringReply, Command } from '../../RESP/types'; +import { transformTuplesReply } from '../../commands/generic-transformers'; + +export default { + transformArguments(dbname: RedisArgument) { + return ['SENTINEL', 'MASTER', dbname]; + }, + transformReply: { + 2: transformTuplesReply, + 3: undefined as unknown as () => MapReply + } +} as const satisfies Command; diff --git a/packages/client/lib/sentinel/commands/SENTINEL_MONITOR.ts b/packages/client/lib/sentinel/commands/SENTINEL_MONITOR.ts new file mode 100644 index 00000000000..14caecd924a --- /dev/null +++ b/packages/client/lib/sentinel/commands/SENTINEL_MONITOR.ts @@ -0,0 +1,8 @@ +import { RedisArgument, SimpleStringReply, Command } from '../../RESP/types'; + +export default { + transformArguments(dbname: RedisArgument, host: RedisArgument, port: RedisArgument, quorum: RedisArgument) { + return ['SENTINEL', 'MONITOR', dbname, host, port, quorum]; + }, + transformReply: undefined as unknown as () => SimpleStringReply<'OK'> +} as const satisfies Command; diff --git a/packages/client/lib/sentinel/commands/SENTINEL_REPLICAS.ts b/packages/client/lib/sentinel/commands/SENTINEL_REPLICAS.ts new file mode 100644 index 00000000000..3d002896355 --- /dev/null +++ b/packages/client/lib/sentinel/commands/SENTINEL_REPLICAS.ts @@ -0,0 +1,23 @@ +import { RedisArgument, ArrayReply, BlobStringReply, MapReply, Command, TypeMapping, UnwrapReply } from '../../RESP/types'; +import { transformTuplesReply } from '../../commands/generic-transformers'; + +export default { + transformArguments(dbname: RedisArgument) { + return ['SENTINEL', 'REPLICAS', dbname]; + }, + transformReply: { + 2: (reply: ArrayReply>, preserve?: any, typeMapping?: TypeMapping) => { + const inferred = reply as unknown as UnwrapReply; + const initial: Array> = []; + + return inferred.reduce( + (sentinels: Array>, x: ArrayReply) => { + sentinels.push(transformTuplesReply(x, undefined, typeMapping)); + return sentinels; + }, + initial + ); + }, + 3: undefined as unknown as () => ArrayReply> + } +} as const satisfies Command; diff --git a/packages/client/lib/sentinel/commands/SENTINEL_SENTINELS.ts b/packages/client/lib/sentinel/commands/SENTINEL_SENTINELS.ts new file mode 100644 index 00000000000..22c1e0123fc --- /dev/null +++ b/packages/client/lib/sentinel/commands/SENTINEL_SENTINELS.ts @@ -0,0 +1,23 @@ +import { RedisArgument, ArrayReply, MapReply, BlobStringReply, Command, TypeMapping, UnwrapReply } from '../../RESP/types'; +import { transformTuplesReply } from '../../commands/generic-transformers'; + +export default { + transformArguments(dbname: RedisArgument) { + return ['SENTINEL', 'SENTINELS', dbname]; + }, + transformReply: { + 2: (reply: ArrayReply>, preserve?: any, typeMapping?: TypeMapping) => { + const inferred = reply as unknown as UnwrapReply; + const initial: Array> = []; + + return inferred.reduce( + (sentinels: Array>, x: ArrayReply) => { + sentinels.push(transformTuplesReply(x, undefined, typeMapping)); + return sentinels; + }, + initial + ); + }, + 3: undefined as unknown as () => ArrayReply> + } +} as const satisfies Command; diff --git a/packages/client/lib/sentinel/commands/SENTINEL_SET.ts b/packages/client/lib/sentinel/commands/SENTINEL_SET.ts new file mode 100644 index 00000000000..41037819869 --- /dev/null +++ b/packages/client/lib/sentinel/commands/SENTINEL_SET.ts @@ -0,0 +1,19 @@ +import { RedisArgument, SimpleStringReply, Command } from '../../RESP/types'; + +export type SentinelSetOptions = Array<{ + option: RedisArgument; + value: RedisArgument; +}>; + +export default { + transformArguments(dbname: RedisArgument, options: SentinelSetOptions) { + const args = ['SENTINEL', 'SET', dbname]; + + for (const option of options) { + args.push(option.option, option.value); + } + + return args; + }, + transformReply: undefined as unknown as () => SimpleStringReply<'OK'> +} as const satisfies Command; diff --git a/packages/client/lib/sentinel/commands/index.ts b/packages/client/lib/sentinel/commands/index.ts new file mode 100644 index 00000000000..1fc16f872f6 --- /dev/null +++ b/packages/client/lib/sentinel/commands/index.ts @@ -0,0 +1,19 @@ +import { RedisCommands } from '../../RESP/types'; +import SENTINEL_MASTER from './SENTINEL_MASTER'; +import SENTINEL_MONITOR from './SENTINEL_MONITOR'; +import SENTINEL_REPLICAS from './SENTINEL_REPLICAS'; +import SENTINEL_SENTINELS from './SENTINEL_SENTINELS'; +import SENTINEL_SET from './SENTINEL_SET'; + +export default { + SENTINEL_SENTINELS, + sentinelSentinels: SENTINEL_SENTINELS, + SENTINEL_MASTER, + sentinelMaster: SENTINEL_MASTER, + SENTINEL_REPLICAS, + sentinelReplicas: SENTINEL_REPLICAS, + SENTINEL_MONITOR, + sentinelMonitor: SENTINEL_MONITOR, + SENTINEL_SET, + sentinelSet: SENTINEL_SET +} as const satisfies RedisCommands; diff --git a/packages/client/lib/sentinel/index.spec.ts b/packages/client/lib/sentinel/index.spec.ts new file mode 100644 index 00000000000..1fba8d6b42f --- /dev/null +++ b/packages/client/lib/sentinel/index.spec.ts @@ -0,0 +1,1276 @@ +import { strict as assert } from 'node:assert'; +import { setTimeout } from 'node:timers/promises'; +import { WatchError } from "../errors"; +import { RedisSentinelConfig, SentinelFramework } from "./test-util"; +import { RedisNode, RedisSentinelClientType, RedisSentinelEvent, RedisSentinelType } from "./types"; +import { RedisSentinelFactory } from '.'; +import { RedisClientType } from '../client'; +import { RedisModules, RedisFunctions, RedisScripts, RespVersions, TypeMapping, NumberReply } from '../RESP/types'; + +import { promisify } from 'node:util'; +import { exec } from 'node:child_process'; +import { RESP_TYPES } from '../RESP/decoder'; +import { defineScript } from '../lua-script'; +import { MATH_FUNCTION } from '../commands/FUNCTION_LOAD.spec'; +import RedisBloomModules from '@redis/bloom'; +import { RedisTcpSocketOptions } from '../client/socket'; + +const execAsync = promisify(exec); + +const SQUARE_SCRIPT = defineScript({ + SCRIPT: + `local number = redis.call('GET', KEYS[1]) + return number * number`, + NUMBER_OF_KEYS: 1, + FIRST_KEY_INDEX: 0, + transformArguments(key: string) { + return [key]; + }, + transformReply: undefined as unknown as () => NumberReply +}); + +/* used to ensure test environment resets to normal state + i.e. + - all redis nodes are active and are part of the topology + before allowing things to continue. +*/ + +async function steadyState(frame: SentinelFramework) { + let checkedMaster = false; + let checkedReplicas = false; + while (!checkedMaster || !checkedReplicas) { + if (!checkedMaster) { + const master = await frame.sentinelMaster(); + if (master?.flags === 'master') { + checkedMaster = true; + } + } + + if (!checkedReplicas) { + const replicas = (await frame.sentinelReplicas()); + checkedReplicas = true; + for (const replica of replicas!) { + checkedReplicas &&= (replica.flags === 'slave'); + } + } + } + + let nodeResolve, nodeReject; + const nodePromise = new Promise((res, rej) => { + nodeResolve = res; + nodeReject = rej; + }) + + const seenNodes = new Set(); + let sentinel: RedisSentinelType | undefined; + const tracer = []; + + try { + sentinel = frame.getSentinelClient({ replicaPoolSize: 1, scanInterval: 2000 }, false) + .on('topology-change', (event: RedisSentinelEvent) => { + if (event.type == "MASTER_CHANGE" || event.type == "REPLICA_ADD") { + seenNodes.add(event.node.port); + if (seenNodes.size == frame.getAllNodesPort().length) { + nodeResolve(); + } + } + }).on('error', err => { }); + sentinel.setTracer(tracer); + await sentinel.connect(); + + await nodePromise; + + await sentinel.flushAll(); + } finally { + if (sentinel !== undefined) { + sentinel.destroy(); + } + } +} + +["redis-sentinel-test-password", undefined].forEach(function (password) { + describe.skip(`Sentinel - password = ${password}`, () => { + const config: RedisSentinelConfig = { sentinelName: "test", numberOfNodes: 3, password: password }; + const frame = new SentinelFramework(config); + let tracer = new Array(); + let stopMeasuringBlocking = false; + let longestDelta = 0; + let longestTestDelta = 0; + let last: number; + + before(async function () { + this.timeout(15000); + + last = Date.now(); + + function deltaMeasurer() { + const delta = Date.now() - last; + if (delta > longestDelta) { + longestDelta = delta; + } + if (delta > longestTestDelta) { + longestTestDelta = delta; + } + if (!stopMeasuringBlocking) { + last = Date.now(); + setImmediate(deltaMeasurer); + } + } + + setImmediate(deltaMeasurer); + + await frame.spawnRedisSentinel(); + }); + + after(async function () { + this.timeout(15000); + + stopMeasuringBlocking = true; + + await frame.cleanup(); + }) + + describe('Sentinel Client', function () { + let sentinel: RedisSentinelType< RedisModules, RedisFunctions, RedisScripts, RespVersions, TypeMapping> | undefined; + + beforeEach(async function () { + this.timeout(0); + + await frame.getAllRunning(); + + await steadyState(frame); + longestTestDelta = 0; + }) + + afterEach(async function () { + this.timeout(30000); + + // avoid errors in afterEach that end testing + if (sentinel !== undefined) { + sentinel.on('error', () => { }); + } + + if (this!.currentTest!.state === 'failed') { + console.log(`longest event loop blocked delta: ${longestDelta}`); + console.log(`longest event loop blocked in failing test: ${longestTestDelta}`); + console.log("trace:"); + for (const line of tracer) { + console.log(line); + } + console.log(`sentinel object state:`) + console.log(`master: ${JSON.stringify(sentinel?.getMasterNode())}`) + console.log(`replicas: ${JSON.stringify(sentinel?.getReplicaNodes().entries)}`) + const results = await Promise.all([ + frame.sentinelSentinels(), + frame.sentinelMaster(), + frame.sentinelReplicas() + ]) + console.log(`sentinel sentinels:\n${JSON.stringify(results[0], undefined, '\t')}`); + console.log(`sentinel master:\n${JSON.stringify(results[1], undefined, '\t')}`); + console.log(`sentinel replicas:\n${JSON.stringify(results[2], undefined, '\t')}`); + const { stdout, stderr } = await execAsync("docker ps -a"); + console.log(`docker stdout:\n${stdout}`); + + const ids = frame.getAllDockerIds(); + console.log("docker logs"); + + for (const [id, port] of ids) { + console.log(`${id}/${port}\n`); + const { stdout, stderr } = await execAsync(`docker logs ${id}`, {maxBuffer: 8192 * 8192 * 4}); + console.log(stdout); + } + } + tracer.length = 0; + + if (sentinel !== undefined) { + await sentinel.destroy(); + sentinel = undefined; + } + }) + + it('basic bootstrap', async function () { + sentinel = frame.getSentinelClient(); + await sentinel.connect(); + + await assert.doesNotReject(sentinel.set('x', 1)); + + }); + + it('basic teardown worked', async function () { + const nodePorts = frame.getAllNodesPort(); + const sentinelPorts = frame.getAllSentinelsPort(); + + assert.notEqual(nodePorts.length, 0); + assert.notEqual(sentinelPorts.length, 0); + + sentinel = frame.getSentinelClient(); + await sentinel.connect(); + + await assert.doesNotReject(sentinel.get('x')); + }); + + it('try to connect multiple times', async function () { + sentinel = frame.getSentinelClient(); + const connectPromise = sentinel.connect(); + await assert.rejects(sentinel.connect()); + await connectPromise; + }); + + it('with type mapping', async function () { + const commandOptions = { + typeMapping: { + [RESP_TYPES.SIMPLE_STRING]: Buffer + } + } + sentinel = frame.getSentinelClient({ commandOptions: commandOptions }); + await sentinel.connect(); + + const resp = await sentinel.ping(); + assert.deepEqual(resp, Buffer.from('PONG')) + }) + + it('with a script', async function () { + const options = { + scripts: { + square: SQUARE_SCRIPT + } + } + + sentinel = frame.getSentinelClient(options); + await sentinel.connect(); + + const [, reply] = await Promise.all([ + sentinel.set('key', '2'), + sentinel.square('key') + ]); + + assert.equal(reply, 4); + }) + + it('multi with a script', async function () { + const options = { + scripts: { + square: SQUARE_SCRIPT + } + } + + sentinel = frame.getSentinelClient(options); + await sentinel.connect(); + + const reply = await sentinel.multi().set('key', 2).square('key').exec(); + + assert.deepEqual(reply, ['OK', 4]); + }) + + it('with a function', async function () { + const options = { + functions: { + math: MATH_FUNCTION.library + } + } + sentinel = frame.getSentinelClient(options); + await sentinel.connect(); + + await sentinel.functionLoad( + MATH_FUNCTION.code, + { REPLACE: true } + ); + + await sentinel.set('key', '2'); + const resp = await sentinel.math.square('key'); + + assert.equal(resp, 4); + }) + + it('multi with a function', async function () { + const options = { + functions: { + math: MATH_FUNCTION.library + } + } + sentinel = frame.getSentinelClient(options); + await sentinel.connect(); + + await sentinel.functionLoad( + MATH_FUNCTION.code, + { REPLACE: true } + ); + + const reply = await sentinel.multi().set('key', 2).math.square('key').exec(); + assert.deepEqual(reply, ['OK', 4]); + }) + + it('with a module', async function () { + const options = { + modules: RedisBloomModules + } + sentinel = frame.getSentinelClient(options); + await sentinel.connect(); + + const resp = await sentinel.bf.add('key', 'item') + assert.equal(resp, true); + }) + + it('multi with a module', async function () { + const options = { + modules: RedisBloomModules + } + sentinel = frame.getSentinelClient(options); + await sentinel.connect(); + + const resp = await sentinel.multi().bf.add('key', 'item').exec(); + assert.deepEqual(resp, [true]); + }) + + it('many readers', async function () { + this.timeout(10000); + + sentinel = frame.getSentinelClient({ replicaPoolSize: 8 }); + await sentinel.connect(); + + await sentinel.set("x", 1); + for (let i = 0; i < 10; i++) { + if (await sentinel.get("x") == "1") { + break; + } + await setTimeout(1000); + } + + const promises: Array> = []; + for (let i = 0; i < 500; i++) { + promises.push(sentinel.get("x")); + } + + const resp = await Promise.all(promises); + assert.equal(resp.length, 500); + for (let i = 0; i < 500; i++) { + assert.equal(resp[i], "1", `failed on match at ${i}`); + } + }); + + it('use', async function () { + this.timeout(30000); + + sentinel = frame.getSentinelClient({ replicaPoolSize: 1 }); + sentinel.on("error", () => { }); + await sentinel.connect(); + + await sentinel.use( + async (client: RedisSentinelClientType, ) => { + const masterNode = sentinel!.getMasterNode(); + await frame.stopNode(masterNode!.port.toString()); + await assert.doesNotReject(client.get('x')); + } + ); + }); + + it('use with script', async function () { + this.timeout(10000); + + const options = { + scripts: { + square: SQUARE_SCRIPT + } + } + + sentinel = frame.getSentinelClient(options); + await sentinel.connect(); + + const reply = await sentinel.use( + async (client: RedisSentinelClientType) => { + assert.equal(await client.set('key', '2'), 'OK'); + assert.equal(await client.get('key'), '2'); + return client.square('key') + } + ); + + assert.equal(reply, 4); + }) + + it('use with a function', async function () { + this.timeout(10000); + + const options = { + functions: { + math: MATH_FUNCTION.library + } + } + sentinel = frame.getSentinelClient(options); + await sentinel.connect(); + + await sentinel.functionLoad( + MATH_FUNCTION.code, + { REPLACE: true } + ); + + const reply = await sentinel.use( + async (client: RedisSentinelClientType) => { + await client.set('key', '2'); + return client.math.square('key'); + } + ); + + assert.equal(reply, 4); + }) + + it('use with a module', async function () { + const options = { + modules: RedisBloomModules + } + sentinel = frame.getSentinelClient(options); + await sentinel.connect(); + + const reply = await sentinel.use( + async (client: RedisSentinelClientType) => { + return client.bf.add('key', 'item'); + } + ); + + assert.equal(reply, true); + }) + + it('block on pool', async function () { + this.timeout(30000); + + sentinel = frame.getSentinelClient({ replicaPoolSize: 1 }); + sentinel.on("error", () => { }); + await sentinel.connect(); + + const promise = sentinel.use( + async client => { + await setTimeout(1000); + return await client.get("x"); + } + ) + + await sentinel.set("x", 1); + assert.equal(await promise, null); + }); + + it('reserve client, takes a client out of pool', async function () { + this.timeout(30000); + + sentinel = frame.getSentinelClient({ masterPoolSize: 2, reserveClient: true }); + await sentinel.connect(); + + const promise1 = sentinel.use( + async client => { + const val = await client.get("x"); + await client.set("x", 2); + return val; + } + ) + + const promise2 = sentinel.use( + async client => { + return client.get("x"); + } + ) + + await sentinel.set("x", 1); + assert.equal(await promise1, "1"); + assert.equal(await promise2, "2"); + }) + + it('multiple clients', async function () { + this.timeout(30000); + + sentinel = frame.getSentinelClient({ masterPoolSize: 2 }); + sentinel.on("error", () => { }); + await sentinel.connect(); + + let set = false; + + const promise = sentinel.use( + async client => { + await sentinel!.set("x", 1); + await client.get("x"); + } + ) + + await assert.doesNotReject(promise); + }); + + // by taking a lease, we know we will block on master as no clients are available, but as read occuring, means replica read occurs + it('replica reads', async function () { + this.timeout(30000); + + sentinel = frame.getSentinelClient({ replicaPoolSize: 1 }); + sentinel.on("error", () => { }); + await sentinel.connect(); + + const clientLease = await sentinel.aquire(); + clientLease.set('x', 456); + + let matched = false; + /* waits for replication */ + for (let i = 0; i < 15; i++) { + try { + assert.equal(await sentinel.get("x"), '456'); + matched = true; + break; + } catch (err) { + await setTimeout(1000); + } + } + + clientLease.release(); + + assert.equal(matched, true); + }); + + it('pipeline', async function () { + this.timeout(30000); + + sentinel = frame.getSentinelClient({ replicaPoolSize: 1 }); + await sentinel.connect(); + + const resp = await sentinel.multi().set('x', 1).get('x').execAsPipeline(); + + assert.deepEqual(resp, ['OK', '1']); + }) + + it('use - watch - clean', async function () { + this.timeout(30000); + + sentinel = frame.getSentinelClient({ masterPoolSize: 2 }); + await sentinel.connect(); + + let promise = sentinel.use(async (client) => { + await client.set("x", 1); + await client.watch("x"); + return client.multi().get("x").exec(); + }); + + assert.deepEqual(await promise, ['1']); + }); + + it('use - watch - dirty', async function () { + this.timeout(30000); + + sentinel = frame.getSentinelClient({ masterPoolSize: 2 }); + await sentinel.connect(); + + let promise = sentinel.use(async (client) => { + await client.set('x', 1); + await client.watch('x'); + await sentinel!.set('x', 2); + return client.multi().get('x').exec(); + }); + + await assert.rejects(promise, new WatchError()); + }); + + it('lease - watch - clean', async function () { + sentinel = frame.getSentinelClient({ masterPoolSize: 2 }); + await sentinel.connect(); + + const leasedClient = await sentinel.aquire(); + await leasedClient.set('x', 1); + await leasedClient.watch('x'); + assert.deepEqual(await leasedClient.multi().get('x').exec(), ['1']) + }); + + it('lease - watch - dirty', async function () { + sentinel = frame.getSentinelClient({ masterPoolSize: 2 }); + await sentinel.connect(); + + const leasedClient = await sentinel.aquire(); + await leasedClient.set('x', 1); + await leasedClient.watch('x'); + await leasedClient.set('x', 2); + + await assert.rejects(leasedClient.multi().get('x').exec(), new WatchError()); + }); + + + it('watch does not carry through leases', async function () { + this.timeout(10000); + sentinel = frame.getSentinelClient(); + await sentinel.connect(); + + // each of these commands is an independent lease + assert.equal(await sentinel.use(client => client.watch("x")), 'OK') + assert.equal(await sentinel.use(client => client.set('x', 1)), 'OK'); + assert.deepEqual(await sentinel.use(client => client.multi().get('x').exec()), ['1']); + }); + + // stops master to force sentinel to update + it('stop master', async function () { + this.timeout(30000); + + sentinel = frame.getSentinelClient(); + sentinel.setTracer(tracer); + sentinel.on("error", () => { }); + await sentinel.connect(); + + tracer.push(`connected`); + + let masterChangeResolve; + const masterChangePromise = new Promise((res) => { + masterChangeResolve = res; + }) + + const masterNode = await sentinel.getMasterNode(); + sentinel.on('topology-change', (event: RedisSentinelEvent) => { + tracer.push(`got topology-change event: ${JSON.stringify(event)}`); + if (event.type === "MASTER_CHANGE" && event.node.port != masterNode!.port) { + tracer.push(`got expected master change event`); + masterChangeResolve(event.node); + } + }); + + tracer.push(`stopping master node`); + await frame.stopNode(masterNode!.port.toString()); + tracer.push(`stopped master node`); + + tracer.push(`waiting on master change promise`); + const newMaster = await masterChangePromise as RedisNode; + tracer.push(`got new master node of ${newMaster.port}`); + assert.notEqual(masterNode!.port, newMaster.port); + }); + + // if master changes, client should make sure user knows watches are invalid + it('watch across master change', async function () { + this.timeout(30000); + + sentinel = frame.getSentinelClient({ masterPoolSize: 2 }); + sentinel.setTracer(tracer); + sentinel.on("error", () => { }); + await sentinel.connect(); + + tracer.push("connected"); + + const client = await sentinel.aquire(); + tracer.push("aquired lease"); + + await client.set("x", 1); + await client.watch("x"); + + tracer.push("did a watch on lease"); + + let resolve; + const promise = new Promise((res) => { + resolve = res; + }) + + const masterNode = sentinel.getMasterNode(); + tracer.push(`got masterPort as ${masterNode!.port}`); + + sentinel.on('topology-change', (event: RedisSentinelEvent) => { + tracer.push(`got topology-change event: ${JSON.stringify(event)}`); + if (event.type === "MASTER_CHANGE" && event.node.port != masterNode!.port) { + tracer.push("resolving promise"); + resolve(event.node); + } + }); + + tracer.push("stopping master node"); + await frame.stopNode(masterNode!.port.toString()); + tracer.push("stopped master node and waiting on promise"); + + const newMaster = await promise as RedisNode; + tracer.push(`promise returned, newMaster = ${JSON.stringify(newMaster)}`); + assert.notEqual(masterNode!.port, newMaster.port); + tracer.push(`newMaster does not equal old master`); + + tracer.push(`waiting to assert that a multi/exec now fails`); + await assert.rejects(async () => { await client.multi().get("x").exec() }, new Error("sentinel config changed in middle of a WATCH Transaction")); + tracer.push(`asserted that a multi/exec now fails`); + }); + + // same as above, but set a watch before and after master change, shouldn't change the fact that watches are invalid + it('watch before and after master change', async function () { + this.timeout(30000); + + sentinel = frame.getSentinelClient({ masterPoolSize: 2 }); + sentinel.setTracer(tracer); + sentinel.on("error", () => { }); + await sentinel.connect(); + tracer.push("connected"); + + const client = await sentinel.aquire(); + tracer.push("got leased client"); + await client.set("x", 1); + await client.watch("x"); + + tracer.push("set and watched x"); + + let resolve; + const promise = new Promise((res) => { + resolve = res; + }) + + const masterNode = sentinel.getMasterNode(); + tracer.push(`initial masterPort = ${masterNode!.port} `); + + sentinel.on('topology-change', (event: RedisSentinelEvent) => { + tracer.push(`got topology-change event: ${JSON.stringify(event)}`); + if (event.type === "MASTER_CHANGE" && event.node.port != masterNode!.port) { + tracer.push("got a master change event that is not the same as before"); + resolve(event.node); + } + }); + + tracer.push("stopping master"); + await frame.stopNode(masterNode!.port.toString()); + tracer.push("stopped master"); + + tracer.push("waiting on master change promise"); + const newMaster = await promise as RedisNode; + tracer.push(`got master change port as ${newMaster.port}`); + assert.notEqual(masterNode!.port, newMaster.port); + + tracer.push("watching again, shouldn't matter"); + await client.watch("y"); + + tracer.push("expecting multi to be rejected"); + await assert.rejects(async () => { await client.multi().get("x").exec() }, new Error("sentinel config changed in middle of a WATCH Transaction")); + tracer.push("multi was rejected"); + }); + + it('plain pubsub - channel', async function () { + this.timeout(30000); + + sentinel = frame.getSentinelClient(); + sentinel.setTracer(tracer); + await sentinel.connect(); + tracer.push(`connected`); + + let pubSubResolve; + const pubSubPromise = new Promise((res) => { + pubSubResolve = res; + }); + + let tester = false; + await sentinel.subscribe('test', () => { + tracer.push(`got pubsub message`); + tester = true; + pubSubResolve(1); + }) + + tracer.push(`publishing pubsub message`); + await sentinel.publish('test', 'hello world'); + tracer.push(`waiting on pubsub promise`); + await pubSubPromise; + tracer.push(`got pubsub promise`); + assert.equal(tester, true); + + // now unsubscribe + tester = false + tracer.push(`unsubscribing pubsub listener`); + await sentinel.unsubscribe('test') + tracer.push(`pubishing pubsub message`); + await sentinel.publish('test', 'hello world'); + await setTimeout(1000); + + tracer.push(`ensuring pubsub was unsubscribed via an assert`); + assert.equal(tester, false); + }); + + it('plain pubsub - pattern', async function () { + this.timeout(30000); + + sentinel = frame.getSentinelClient(); + sentinel.setTracer(tracer); + await sentinel.connect(); + tracer.push(`connected`); + + let pubSubResolve; + const pubSubPromise = new Promise((res) => { + pubSubResolve = res; + }); + + let tester = false; + await sentinel.pSubscribe('test*', () => { + tracer.push(`got pubsub message`); + tester = true; + pubSubResolve(1); + }) + + tracer.push(`publishing pubsub message`); + await sentinel.publish('testy', 'hello world'); + tracer.push(`waiting on pubsub promise`); + await pubSubPromise; + tracer.push(`got pubsub promise`); + assert.equal(tester, true); + + // now unsubscribe + tester = false + tracer.push(`unsubscribing pubsub listener`); + await sentinel.pUnsubscribe('test*'); + tracer.push(`pubishing pubsub message`); + await sentinel.publish('testy', 'hello world'); + await setTimeout(1000); + + tracer.push(`ensuring pubsub was unsubscribed via an assert`); + assert.equal(tester, false); + }); + + // pubsub continues to work, even with a master change + it('pubsub - channel - with master change', async function () { + this.timeout(30000); + + sentinel = frame.getSentinelClient(); + sentinel.setTracer(tracer); + sentinel.on("error", () => { }); + await sentinel.connect(); + tracer.push(`connected`); + + let pubSubResolve; + const pubSubPromise = new Promise((res) => { + pubSubResolve = res; + }) + + let tester = false; + await sentinel.subscribe('test', () => { + tracer.push(`got pubsub message`); + tester = true; + pubSubResolve(1); + }) + + let masterChangeResolve; + const masterChangePromise = new Promise((res) => { + masterChangeResolve = res; + }) + + const masterNode = sentinel.getMasterNode(); + tracer.push(`got masterPort as ${masterNode!.port}`); + + sentinel.on('topology-change', (event: RedisSentinelEvent) => { + tracer.push(`got topology-change event: ${JSON.stringify(event)}`); + if (event.type === "MASTER_CHANGE" && event.node.port != masterNode!.port) { + tracer.push("got a master change event that is not the same as before"); + masterChangeResolve(event.node); + } + }); + + tracer.push("stopping master"); + await frame.stopNode(masterNode!.port.toString()); + tracer.push("stopped master and waiting on change promise"); + + const newMaster = await masterChangePromise as RedisNode; + tracer.push(`got master change port as ${newMaster.port}`); + assert.notEqual(masterNode!.port, newMaster.port); + + tracer.push(`publishing pubsub message`); + await sentinel.publish('test', 'hello world'); + tracer.push(`published pubsub message and waiting pn pubsub promise`); + await pubSubPromise; + tracer.push(`got pubsub promise`); + + assert.equal(tester, true); + + // now unsubscribe + tester = false + await sentinel.unsubscribe('test') + await sentinel.publish('test', 'hello world'); + await setTimeout(1000); + + assert.equal(tester, false); + }); + + it('pubsub - pattern - with master change', async function () { + this.timeout(30000); + + sentinel = frame.getSentinelClient(); + sentinel.setTracer(tracer); + sentinel.on("error", () => { }); + await sentinel.connect(); + tracer.push(`connected`); + + let pubSubResolve; + const pubSubPromise = new Promise((res) => { + pubSubResolve = res; + }) + + let tester = false; + await sentinel.pSubscribe('test*', () => { + tracer.push(`got pubsub message`); + tester = true; + pubSubResolve(1); + }) + + let masterChangeResolve; + const masterChangePromise = new Promise((res) => { + masterChangeResolve = res; + }) + + const masterNode = sentinel.getMasterNode(); + tracer.push(`got masterPort as ${masterNode!.port}`); + + sentinel.on('topology-change', (event: RedisSentinelEvent) => { + tracer.push(`got topology-change event: ${JSON.stringify(event)}`); + if (event.type === "MASTER_CHANGE" && event.node.port != masterNode!.port) { + tracer.push("got a master change event that is not the same as before"); + masterChangeResolve(event.node); + } + }); + + tracer.push("stopping master"); + await frame.stopNode(masterNode!.port.toString()); + tracer.push("stopped master and waiting on master change promise"); + + const newMaster = await masterChangePromise as RedisNode; + tracer.push(`got master change port as ${newMaster.port}`); + assert.notEqual(masterNode!.port, newMaster.port); + + tracer.push(`publishing pubsub message`); + await sentinel.publish('testy', 'hello world'); + tracer.push(`published pubsub message and waiting on pubsub promise`); + await pubSubPromise; + tracer.push(`got pubsub promise`); + assert.equal(tester, true); + + // now unsubscribe + tester = false + await sentinel.pUnsubscribe('test*'); + await sentinel.publish('testy', 'hello world'); + await setTimeout(1000); + + assert.equal(tester, false); + }); + + // if we stop a node, the comand should "retry" until we reconfigure topology and execute on new topology + it('command immeaditely after stopping master', async function () { + this.timeout(30000); + + sentinel = frame.getSentinelClient(); + sentinel.setTracer(tracer); + sentinel.on("error", () => { }); + await sentinel.connect(); + + tracer.push("connected"); + + let masterChangeResolve; + const masterChangePromise = new Promise((res) => { + masterChangeResolve = res; + }) + + const masterNode = sentinel.getMasterNode(); + tracer.push(`original master port = ${masterNode!.port}`); + + let changeCount = 0; + sentinel.on('topology-change', (event: RedisSentinelEvent) => { + tracer.push(`got topology-change event: ${JSON.stringify(event)}`); + if (event.type === "MASTER_CHANGE" && event.node.port != masterNode!.port) { + changeCount++; + tracer.push(`got topology-change event we expected`); + masterChangeResolve(event.node); + } + }); + + tracer.push(`stopping masterNode`); + await frame.stopNode(masterNode!.port.toString()); + tracer.push(`stopped masterNode`); + assert.equal(await sentinel.set('x', 123), 'OK'); + tracer.push(`did the set operation`); + const presumamblyNewMaster = sentinel.getMasterNode(); + tracer.push(`new master node seems to be ${presumamblyNewMaster?.port} and waiting on master change promise`); + + const newMaster = await masterChangePromise as RedisNode; + tracer.push(`got new masternode event saying master is at ${newMaster.port}`); + assert.notEqual(masterNode!.port, newMaster.port); + + tracer.push(`doing the get`); + const val = await sentinel.get('x'); + tracer.push(`did the get and got ${val}`); + const newestMaster = sentinel.getMasterNode() + tracer.push(`after get, we see master as ${newestMaster?.port}`); + + switch (changeCount) { + case 1: + // if we only changed masters once, we should have the proper value + assert.equal(val, '123'); + break; + case 2: + // we changed masters twice quickly, so probably didn't replicate + // therefore, this is soewhat flakey, but the above is the common case + assert(val == '123' || val == null); + break; + default: + assert(false, "unexpected case"); + } + }); + + it('shutdown sentinel node', async function () { + this.timeout(30000); + + sentinel = frame.getSentinelClient(); + sentinel.setTracer(tracer); + sentinel.on("error", () => { }); + await sentinel.connect(); + tracer.push("connected"); + + let sentinelChangeResolve; + const sentinelChangePromise = new Promise((res) => { + sentinelChangeResolve = res; + }) + + const sentinelNode = sentinel.getSentinelNode(); + tracer.push(`sentinelNode = ${sentinelNode?.port}`) + + sentinel.on('topology-change', (event: RedisSentinelEvent) => { + tracer.push(`got topology-change event: ${JSON.stringify(event)}`); + if (event.type === "SENTINEL_CHANGE") { + tracer.push("got sentinel change event"); + sentinelChangeResolve(event.node); + } + }); + + tracer.push("Stopping sentinel node"); + await frame.stopSentinel(sentinelNode!.port.toString()); + tracer.push("Stopped sentinel node and waiting on sentinel change promise"); + const newSentinel = await sentinelChangePromise as RedisNode; + tracer.push("got sentinel change promise"); + assert.notEqual(sentinelNode!.port, newSentinel.port); + }); + + it('timer works, and updates sentinel list', async function () { + this.timeout(30000); + + sentinel = frame.getSentinelClient({ scanInterval: 1000 }); + sentinel.setTracer(tracer); + await sentinel.connect(); + tracer.push("connected"); + + let sentinelChangeResolve; + const sentinelChangePromise = new Promise((res) => { + sentinelChangeResolve = res; + }) + + sentinel.on('topology-change', (event: RedisSentinelEvent) => { + tracer.push(`got topology-change event: ${JSON.stringify(event)}`); + if (event.type === "SENTINE_LIST_CHANGE" && event.size == 4) { + tracer.push(`got sentinel list change event with right size`); + sentinelChangeResolve(event.size); + } + }); + + tracer.push(`adding sentinel`); + await frame.addSentinel(); + tracer.push(`added sentinel and waiting on sentinel change promise`); + const newSentinelSize = await sentinelChangePromise as number; + + assert.equal(newSentinelSize, 4); + }); + + it('stop replica, bring back replica', async function () { + this.timeout(30000); + + sentinel = frame.getSentinelClient({ replicaPoolSize: 1 }); + sentinel.setTracer(tracer); + sentinel.on('error', err => { }); + await sentinel.connect(); + tracer.push("connected"); + + let sentinelRemoveResolve; + const sentinelRemovePromise = new Promise((res) => { + sentinelRemoveResolve = res; + }) + + const replicaPort = await frame.getRandonNonMasterNode(); + + sentinel.on('topology-change', (event: RedisSentinelEvent) => { + tracer.push(`got topology-change event: ${JSON.stringify(event)}`); + if (event.type === "REPLICA_REMOVE") { + if (event.node.port.toString() == replicaPort) { + tracer.push("got expected replica removed event"); + sentinelRemoveResolve(event.node); + } else { + tracer.push(`got replica removed event for a different node: ${event.node.port}`); + } + } + }); + + tracer.push(`replicaPort = ${replicaPort} and stopping it`); + await frame.stopNode(replicaPort); + tracer.push("stopped replica and waiting on sentinel removed promise"); + const stoppedNode = await sentinelRemovePromise as RedisNode; + tracer.push("got removed promise"); + assert.equal(stoppedNode.port, Number(replicaPort)); + + let sentinelRestartedResolve; + const sentinelRestartedPromise = new Promise((res) => { + sentinelRestartedResolve = res; + }) + + sentinel.on('topology-change', (event: RedisSentinelEvent) => { + tracer.push(`got topology-change event: ${JSON.stringify(event)}`); + if (event.type === "REPLICA_ADD") { + tracer.push("got replica added event"); + sentinelRestartedResolve(event.node); + } + }); + + tracer.push("restarting replica"); + await frame.restartNode(replicaPort); + tracer.push("restarted replica and waiting on restart promise"); + const restartedNode = await sentinelRestartedPromise as RedisNode; + tracer.push("got restarted promise"); + assert.equal(restartedNode.port, Number(replicaPort)); + }) + + it('add a node / new replica', async function () { + this.timeout(30000); + + sentinel = frame.getSentinelClient({ scanInterval: 2000, replicaPoolSize: 1 }); + sentinel.setTracer(tracer); + // need to handle errors, as the spawning a new docker node can cause existing connections to time out + sentinel.on('error', err => { }); + await sentinel.connect(); + tracer.push("connected"); + + let nodeAddedResolve: (value: RedisNode) => void; + const nodeAddedPromise = new Promise((res) => { + nodeAddedResolve = res as (value: RedisNode) => void; + }); + + const portSet = new Set(); + for (const port of frame.getAllNodesPort()) { + portSet.add(port); + } + + // "on" and not "once" as due to connection timeouts, can happen multiple times, and want right one + sentinel.on('topology-change', (event: RedisSentinelEvent) => { + tracer.push(`got topology-change event: ${JSON.stringify(event)}`); + if (event.type === "REPLICA_ADD") { + if (!portSet.has(event.node.port)) { + tracer.push("got expected replica added event"); + nodeAddedResolve(event.node); + } + } + }); + + tracer.push("adding node"); + await frame.addNode(); + tracer.push("added node and waiting on added promise"); + await nodeAddedPromise; + }) + }) + + describe('Sentinel Factory', function () { + let master: RedisClientType | undefined; + let replica: RedisClientType | undefined; + + beforeEach(async function () { + this.timeout(0); + + await frame.getAllRunning(); + + await steadyState(frame); + longestTestDelta = 0; + }) + + afterEach(async function () { + if (this!.currentTest!.state === 'failed') { + console.log(`longest event loop blocked delta: ${longestDelta}`); + console.log(`longest event loop blocked in failing test: ${longestTestDelta}`); + console.log("trace:"); + for (const line of tracer) { + console.log(line); + } + const results = await Promise.all([ + frame.sentinelSentinels(), + frame.sentinelMaster(), + frame.sentinelReplicas() + ]) + console.log(`sentinel sentinels:\n${JSON.stringify(results[0], undefined, '\t')}`); + console.log(`sentinel master:\n${JSON.stringify(results[1], undefined, '\t')}`); + console.log(`sentinel replicas:\n${JSON.stringify(results[2], undefined, '\t')}`); + const { stdout, stderr } = await execAsync("docker ps -a"); + console.log(`docker stdout:\n${stdout}`); + console.log(`docker stderr:\n${stderr}`); + } + tracer.length = 0; + + if (master !== undefined) { + if (master.isOpen) { + master.destroy(); + } + master = undefined; + } + + if (replica !== undefined) { + if (replica.isOpen) { + replica.destroy(); + } + replica = undefined; + } + }) + + it('sentinel factory - master', async function () { + const sentinelPorts = frame.getAllSentinelsPort(); + const sentinels: Array = []; + for (const port of sentinelPorts) { + sentinels.push({ host: "localhost", port: port }); + } + + const factory = new RedisSentinelFactory({ name: frame.config.sentinelName, sentinelRootNodes: sentinels, sentinelClientOptions: {password: password}, nodeClientOptions: {password: password} }) + await factory.updateSentinelRootNodes(); + + master = await factory.getMasterClient(); + await master.connect(); + + assert.equal(await master.set("x", 1), 'OK'); + }) + + it('sentinel factory - replica', async function () { + const sentinelPorts = frame.getAllSentinelsPort(); + const sentinels: Array = []; + + for (const port of sentinelPorts) { + sentinels.push({ host: "localhost", port: port }); + } + + const factory = new RedisSentinelFactory({ name: frame.config.sentinelName, sentinelRootNodes: sentinels, sentinelClientOptions: {password: password}, nodeClientOptions: {password: password} }) + await factory.updateSentinelRootNodes(); + + const masterNode = await factory.getMasterNode(); + replica = await factory.getReplicaClient(); + const replicaSocketOptions = replica.options?.socket as unknown as RedisTcpSocketOptions | undefined; + assert.notEqual(masterNode.port, replicaSocketOptions?.port) + }) + + it('sentinel factory - bad node', async function () { + const factory = new RedisSentinelFactory({ name: frame.config.sentinelName, sentinelRootNodes: [{ host: "locahost", port: 1 }] }); + await assert.rejects(factory.updateSentinelRootNodes(), new Error("Couldn't connect to any sentinel node")); + }) + + it('sentinel factory - invalid db name', async function () { + this.timeout(15000); + + const sentinelPorts = frame.getAllSentinelsPort(); + const sentinels: Array = []; + + for (const port of sentinelPorts) { + sentinels.push({ host: "localhost", port: port }); + } + + const factory = new RedisSentinelFactory({ name: "invalid-name", sentinelRootNodes: sentinels, sentinelClientOptions: {password: password}, nodeClientOptions: {password: password} }) + await assert.rejects(factory.updateSentinelRootNodes(), new Error("ERR No such master with that name")); + }) + + it('sentinel factory - no available nodes', async function () { + this.timeout(15000); + + const sentinelPorts = frame.getAllSentinelsPort(); + const sentinels: Array = []; + + for (const port of sentinelPorts) { + sentinels.push({ host: "localhost", port: port }); + } + + const factory = new RedisSentinelFactory({ name: frame.config.sentinelName, sentinelRootNodes: sentinels, sentinelClientOptions: {password: password}, nodeClientOptions: {password: password} }) + + for (const node of frame.getAllNodesPort()) { + await frame.stopNode(node.toString()); + } + + await setTimeout(1000); + + await assert.rejects(factory.getMasterNode(), new Error("Master Node Not Enumerated")); + }) + }) + }) +}); diff --git a/packages/client/lib/sentinel/index.ts b/packages/client/lib/sentinel/index.ts new file mode 100644 index 00000000000..b71514e9358 --- /dev/null +++ b/packages/client/lib/sentinel/index.ts @@ -0,0 +1,1487 @@ +import { EventEmitter } from 'node:events'; +import { CommandArguments, RedisArgument, RedisFunctions, RedisModules, RedisScript, RedisScripts, ReplyUnion, RespVersions, TypeMapping } from '../RESP/types'; +import RedisClient, { RedisClientOptions, RedisClientType } from '../client'; +import { CommandOptions } from '../client/commands-queue'; +import { attachConfig } from '../commander'; +import COMMANDS from '../commands'; +import { ClientErrorEvent, NamespaceProxySentinel, NamespaceProxySentinelClient, ProxySentinel, ProxySentinelClient, RedisNode, RedisSentinelClientType, RedisSentinelEvent, RedisSentinelOptions, RedisSentinelType, SentinelCommander } from './types'; +import { clientSocketToNode, createCommand, createFunctionCommand, createModuleCommand, createNodeList, createScriptCommand, parseNode } from './utils'; +import { RedisMultiQueuedCommand } from '../multi-command'; +import RedisSentinelMultiCommand, { RedisSentinelMultiCommandType } from './multi-commands'; +import { PubSubListener } from '../client/pub-sub'; +import { PubSubProxy } from './pub-sub-proxy'; +import { setTimeout } from 'node:timers/promises'; +import RedisSentinelModule from './module' +import { RedisVariadicArgument } from '../commands/generic-transformers'; +import { WaitQueue } from './wait-queue'; +import { TcpNetConnectOpts } from 'node:net'; +import { RedisTcpSocketOptions } from '../client/socket'; + +interface ClientInfo { + id: number; +} + +export class RedisSentinelClient< + M extends RedisModules, + F extends RedisFunctions, + S extends RedisScripts, + RESP extends RespVersions, + TYPE_MAPPING extends TypeMapping +> { + #clientInfo: ClientInfo | undefined; + #internal: RedisSentinelInternal; + readonly _self: RedisSentinelClient; + + get isOpen() { + return this._self.#internal.isOpen; + } + + get isReady() { + return this._self.#internal.isReady; + } + + get commandOptions() { + return this._self.#commandOptions; + } + + #commandOptions?: CommandOptions; + + constructor( + internal: RedisSentinelInternal, + clientInfo: ClientInfo, + commandOptions?: CommandOptions + ) { + this._self = this; + this.#internal = internal; + this.#clientInfo = clientInfo; + this.#commandOptions = commandOptions; + } + + static factory< + M extends RedisModules = {}, + F extends RedisFunctions = {}, + S extends RedisScripts = {}, + RESP extends RespVersions = 2, + TYPE_MAPPING extends TypeMapping = {} + >(config?: SentinelCommander) { + const SentinelClient = attachConfig({ + BaseClass: RedisSentinelClient, + commands: COMMANDS, + createCommand: createCommand, + createModuleCommand: createModuleCommand, + createFunctionCommand: createFunctionCommand, + createScriptCommand: createScriptCommand, + config + }); + + SentinelClient.prototype.Multi = RedisSentinelMultiCommand.extend(config); + + return ( + internal: RedisSentinelInternal, + clientInfo: ClientInfo, + commandOptions?: CommandOptions + ) => { + // returning a "proxy" to prevent the namespaces._self to leak between "proxies" + return Object.create(new SentinelClient(internal, clientInfo, commandOptions)) as RedisSentinelClientType; + }; + } + + static create< + M extends RedisModules = {}, + F extends RedisFunctions = {}, + S extends RedisScripts = {}, + RESP extends RespVersions = 2, + TYPE_MAPPING extends TypeMapping = {} + >( + internal: RedisSentinelInternal, + clientInfo: ClientInfo, + commandOptions?: CommandOptions, + options?: RedisSentinelOptions + ) { + return RedisSentinelClient.factory(options)(internal, clientInfo, commandOptions); + } + + withCommandOptions< + OPTIONS extends CommandOptions, + TYPE_MAPPING extends TypeMapping + >(options: OPTIONS) { + const proxy = Object.create(this); + proxy._commandOptions = options; + return proxy as RedisSentinelClientType< + M, + F, + S, + RESP, + TYPE_MAPPING extends TypeMapping ? TYPE_MAPPING : {} + >; + } + + private _commandOptionsProxy< + K extends keyof CommandOptions, + V extends CommandOptions[K] + >( + key: K, + value: V + ) { + const proxy = Object.create(this); + proxy._commandOptions = Object.create(this._self.#commandOptions ?? null); + proxy._commandOptions[key] = value; + return proxy as RedisSentinelClientType< + M, + F, + S, + RESP, + K extends 'typeMapping' ? V extends TypeMapping ? V : {} : TYPE_MAPPING + >; + } + + /** + * Override the `typeMapping` command option + */ + withTypeMapping(typeMapping: TYPE_MAPPING) { + return this._commandOptionsProxy('typeMapping', typeMapping); + } + + async _execute( + isReadonly: boolean | undefined, + fn: (client: RedisClient) => Promise + ): Promise { + if (this._self.#clientInfo === undefined) { + throw new Error("Attempted execution on released RedisSentinelClient lease"); + } + + return await this._self.#internal.execute(fn, this._self.#clientInfo); + } + + async sendCommand( + isReadonly: boolean | undefined, + args: CommandArguments, + options?: CommandOptions, + ): Promise { + return this._execute( + isReadonly, + client => client.sendCommand(args, options) + ); + } + + executeScript( + script: RedisScript, + isReadonly: boolean | undefined, + args: Array, + options?: CommandOptions + ) { + return this._execute( + isReadonly, + client => client.executeScript(script, args, options) + ); + } + + /** + * @internal + */ + async _executePipeline( + isReadonly: boolean | undefined, + commands: Array + ) { + return this._execute( + isReadonly, + client => client._executePipeline(commands) + ); + } + + /**f + * @internal + */ + async _executeMulti( + isReadonly: boolean | undefined, + commands: Array + ) { + return this._execute( + isReadonly, + client => client._executeMulti(commands) + ); + } + + MULTI(): RedisSentinelMultiCommandType<[], M, F, S, RESP, TYPE_MAPPING> { + return new (this as any).Multi(this); + } + + multi = this.MULTI; + + WATCH(key: RedisVariadicArgument) { + if (this._self.#clientInfo === undefined) { + throw new Error("Attempted execution on released RedisSentinelClient lease"); + } + + return this._execute( + false, + client => client.watch(key) + ) + } + + watch = this.WATCH; + + UNWATCH() { + if (this._self.#clientInfo === undefined) { + throw new Error('Attempted execution on released RedisSentinelClient lease'); + } + + return this._execute( + false, + client => client.unwatch() + ) + } + + unwatch = this.UNWATCH; + + release() { + if (this._self.#clientInfo === undefined) { + throw new Error('RedisSentinelClient lease already released'); + } + + const result = this._self.#internal.releaseClientLease(this._self.#clientInfo); + this._self.#clientInfo = undefined; + return result; + } +} + +export default class RedisSentinel< + M extends RedisModules, + F extends RedisFunctions, + S extends RedisScripts, + RESP extends RespVersions, + TYPE_MAPPING extends TypeMapping +> extends EventEmitter { + readonly _self: RedisSentinel; + + #internal: RedisSentinelInternal; + #options: RedisSentinelOptions; + + get isOpen() { + return this._self.#internal.isOpen; + } + + get isReady() { + return this._self.#internal.isReady; + } + + get commandOptions() { + return this._self.#commandOptions; + } + + #commandOptions?: CommandOptions; + + #trace: (msg: string) => unknown = () => { }; + + #reservedClientInfo?: ClientInfo; + #masterClientCount = 0; + #masterClientInfo?: ClientInfo; + + constructor(options: RedisSentinelOptions) { + super(); + + this._self = this; + + this.#options = options; + + if (options?.commandOptions) { + this.#commandOptions = options.commandOptions; + } + + this.#internal = new RedisSentinelInternal(options); + this.#internal.on('error', err => this.emit('error', err)); + + /* pass through underling events */ + /* TODO: perhaps make this a struct and one vent, instead of multiple events */ + this.#internal.on('topology-change', (event: RedisSentinelEvent) => { + if (!this.emit('topology-change', event)) { + this._self.#trace(`RedisSentinel: re-emit for topology-change for ${event.type} event returned false`); + } + }); + } + + static factory< + M extends RedisModules = {}, + F extends RedisFunctions = {}, + S extends RedisScripts = {}, + RESP extends RespVersions = 2, + TYPE_MAPPING extends TypeMapping = {} + >(config?: SentinelCommander) { + const Sentinel = attachConfig({ + BaseClass: RedisSentinel, + commands: COMMANDS, + createCommand: createCommand, + createModuleCommand: createModuleCommand, + createFunctionCommand: createFunctionCommand, + createScriptCommand: createScriptCommand, + config + }); + + Sentinel.prototype.Multi = RedisSentinelMultiCommand.extend(config); + + return (options?: Omit>) => { + // returning a "proxy" to prevent the namespaces.self to leak between "proxies" + return Object.create(new Sentinel(options)) as RedisSentinelType; + }; + } + + static create< + M extends RedisModules = {}, + F extends RedisFunctions = {}, + S extends RedisScripts = {}, + RESP extends RespVersions = 2, + TYPE_MAPPING extends TypeMapping = {} + >(options?: RedisSentinelOptions) { + return RedisSentinel.factory(options)(options); + } + + withCommandOptions< + OPTIONS extends CommandOptions, + TYPE_MAPPING extends TypeMapping, + >(options: OPTIONS) { + const proxy = Object.create(this); + proxy._commandOptions = options; + return proxy as RedisSentinelType< + M, + F, + S, + RESP, + TYPE_MAPPING extends TypeMapping ? TYPE_MAPPING : {} + >; + } + + private _commandOptionsProxy< + K extends keyof CommandOptions, + V extends CommandOptions[K] + >( + key: K, + value: V + ) { + const proxy = Object.create(this._self); + proxy._commandOptions = Object.create(this._self.#commandOptions ?? null); + proxy._commandOptions[key] = value; + return proxy as RedisSentinelType< + M, + F, + S, + RESP, + K extends 'typeMapping' ? V extends TypeMapping ? V : {} : TYPE_MAPPING + >; + } + + /** + * Override the `typeMapping` command option + */ + withTypeMapping(typeMapping: TYPE_MAPPING) { + return this._commandOptionsProxy('typeMapping', typeMapping); + } + + async connect() { + await this._self.#internal.connect(); + + if (this._self.#options.reserveClient) { + this._self.#reservedClientInfo = await this._self.#internal.getClientLease(); + } + + return this as unknown as RedisSentinelType; + } + + async _execute( + isReadonly: boolean | undefined, + fn: (client: RedisClient) => Promise + ): Promise { + let clientInfo: ClientInfo | undefined; + if (!isReadonly || !this._self.#internal.useReplicas) { + if (this._self.#reservedClientInfo) { + clientInfo = this._self.#reservedClientInfo; + } else { + this._self.#masterClientInfo ??= await this._self.#internal.getClientLease(); + clientInfo = this._self.#masterClientInfo; + this._self.#masterClientCount++; + } + } + + try { + return await this._self.#internal.execute(fn, clientInfo); + } finally { + if ( + clientInfo !== undefined && + clientInfo === this._self.#masterClientInfo && + --this._self.#masterClientCount === 0 + ) { + const promise = this._self.#internal.releaseClientLease(clientInfo); + this._self.#masterClientInfo = undefined; + if (promise) await promise; + } + } + } + + async use(fn: (sentinelClient: RedisSentinelClientType) => Promise) { + const clientInfo = await this._self.#internal.getClientLease(); + + try { + return await fn( + RedisSentinelClient.create(this._self.#internal, clientInfo, this._self.#commandOptions, this._self.#options) + ); + } finally { + const promise = this._self.#internal.releaseClientLease(clientInfo); + if (promise) await promise; + } + } + + async sendCommand( + isReadonly: boolean | undefined, + args: CommandArguments, + options?: CommandOptions, + ): Promise { + return this._execute( + isReadonly, + client => client.sendCommand(args, options) + ); + } + + executeScript( + script: RedisScript, + isReadonly: boolean | undefined, + args: Array, + options?: CommandOptions + ) { + return this._execute( + isReadonly, + client => client.executeScript(script, args, options) + ); + } + + /** + * @internal + */ + async _executePipeline( + isReadonly: boolean | undefined, + commands: Array + ) { + return this._execute( + isReadonly, + client => client._executePipeline(commands) + ); + } + + /**f + * @internal + */ + async _executeMulti( + isReadonly: boolean | undefined, + commands: Array + ) { + return this._execute( + isReadonly, + client => client._executeMulti(commands) + ); + } + + MULTI(): RedisSentinelMultiCommandType<[], M, F, S, RESP, TYPE_MAPPING> { + return new (this as any).Multi(this); + } + + multi = this.MULTI; + + async close() { + return this._self.#internal.close(); + } + + destroy() { + return this._self.#internal.destroy(); + } + + async SUBSCRIBE( + channels: string | Array, + listener: PubSubListener, + bufferMode?: T + ) { + return this._self.#internal.subscribe(channels, listener, bufferMode); + } + + subscribe = this.SUBSCRIBE; + + async UNSUBSCRIBE( + channels?: string | Array, + listener?: PubSubListener, + bufferMode?: T + ) { + return this._self.#internal.unsubscribe(channels, listener, bufferMode); + } + + unsubscribe = this.UNSUBSCRIBE; + + async PSUBSCRIBE( + patterns: string | Array, + listener: PubSubListener, + bufferMode?: T + ) { + return this._self.#internal.pSubscribe(patterns, listener, bufferMode); + } + + pSubscribe = this.PSUBSCRIBE; + + async PUNSUBSCRIBE( + patterns?: string | Array, + listener?: PubSubListener, + bufferMode?: T + ) { + return this._self.#internal.pUnsubscribe(patterns, listener, bufferMode); + } + + pUnsubscribe = this.PUNSUBSCRIBE; + + async aquire(): Promise> { + const clientInfo = await this._self.#internal.getClientLease(); + return RedisSentinelClient.create(this._self.#internal, clientInfo, this._self.#commandOptions, this._self.#options); + } + + getSentinelNode(): RedisNode | undefined { + return this._self.#internal.getSentinelNode(); + } + + getMasterNode(): RedisNode | undefined { + return this._self.#internal.getMasterNode(); + } + + getReplicaNodes(): Map { + return this._self.#internal.getReplicaNodes(); + } + + setTracer(tracer?: Array) { + if (tracer) { + this._self.#trace = (msg: string) => { tracer.push(msg) }; + } else { + this._self.#trace = () => { }; + } + + this._self.#internal.setTracer(tracer); + } +} + +class RedisSentinelInternal< + M extends RedisModules, + F extends RedisFunctions, + S extends RedisScripts, + RESP extends RespVersions, + TYPE_MAPPING extends TypeMapping +> extends EventEmitter { + #isOpen = false; + + get isOpen() { + return this.#isOpen; + } + + #isReady = false; + + get isReady() { + return this.#isReady; + } + + readonly #name: string; + readonly #nodeClientOptions: RedisClientOptions; + readonly #sentinelClientOptions: RedisClientOptions; + readonly #scanInterval: number; + readonly #passthroughClientErrorEvents: boolean; + + #anotherReset = false; + + #configEpoch: number = 0; + + #sentinelRootNodes: Array; + #sentinelClient?: RedisClientType; + + #masterClients: Array> = []; + #masterClientQueue: WaitQueue; + readonly #masterPoolSize: number; + + #replicaClients: Array> = []; + #replicaClientsIdx: number = 0; + readonly #replicaPoolSize: number; + + get useReplicas() { + return this.#replicaPoolSize > 0; + } + + #connectPromise?: Promise; + #maxCommandRediscovers: number; + readonly #pubSubProxy: PubSubProxy; + + #scanTimer?: NodeJS.Timeout + + #destroy = false; + + #trace: (msg: string) => unknown = () => { }; + + constructor(options: RedisSentinelOptions) { + super(); + + this.#name = options.name; + + this.#sentinelRootNodes = Array.from(options.sentinelRootNodes); + this.#maxCommandRediscovers = options.maxCommandRediscovers ?? 16; + this.#masterPoolSize = options.masterPoolSize ?? 1; + this.#replicaPoolSize = options.replicaPoolSize ?? 0; + this.#scanInterval = options.scanInterval ?? 0; + this.#passthroughClientErrorEvents = options.passthroughClientErrorEvents ?? false; + + this.#nodeClientOptions = options.nodeClientOptions ? Object.assign({} as RedisClientOptions, options.nodeClientOptions) : {}; + if (this.#nodeClientOptions.url !== undefined) { + throw new Error("invalid nodeClientOptions for Sentinel"); + } + + this.#sentinelClientOptions = options.sentinelClientOptions ? Object.assign({} as RedisClientOptions, options.sentinelClientOptions) : {}; + this.#sentinelClientOptions.modules = RedisSentinelModule; + + if (this.#sentinelClientOptions.url !== undefined) { + throw new Error("invalid sentinelClientOptions for Sentinel"); + } + + this.#masterClientQueue = new WaitQueue(); + for (let i = 0; i < this.#masterPoolSize; i++) { + this.#masterClientQueue.push(i); + } + + /* persistent object for life of sentinel object */ + this.#pubSubProxy = new PubSubProxy( + this.#nodeClientOptions, + err => this.emit('error', err) + ); + } + + #createClient(node: RedisNode, clientOptions: RedisClientOptions, reconnectStrategy?: undefined | false) { + return RedisClient.create({ + ...clientOptions, + socket: { + ...clientOptions.socket, + host: node.host, + port: node.port, + reconnectStrategy + } + }); + } + + getClientLease(): ClientInfo | Promise { + const id = this.#masterClientQueue.shift(); + if (id !== undefined) { + return { id }; + } + + return this.#masterClientQueue.wait().then(id => ({ id })); + } + + releaseClientLease(clientInfo: ClientInfo) { + const client = this.#masterClients[clientInfo.id]; + // client can be undefined if releasing in middle of a reconfigure + if (client !== undefined) { + const dirtyPromise = client.resetIfDirty(); + if (dirtyPromise) { + return dirtyPromise + .then(() => this.#masterClientQueue.push(clientInfo.id)); + } + } + + this.#masterClientQueue.push(clientInfo.id); + } + + async connect() { + if (this.#isOpen) { + throw new Error("already attempting to open") + } + + try { + this.#isOpen = true; + + this.#connectPromise = this.#connect(); + await this.#connectPromise; + this.#isReady = true; + } finally { + this.#connectPromise = undefined; + if (this.#scanInterval > 0) { + this.#scanTimer = setInterval(this.#reset.bind(this), this.#scanInterval); + } + } + } + + async #connect() { + let count = 0; + while (true) { + this.#trace("starting connect loop"); + + if (this.#destroy) { + this.#trace("in #connect and want to destroy") + return; + } + try { + this.#anotherReset = false; + await this.transform(this.analyze(await this.observe())); + if (this.#anotherReset) { + this.#trace("#connect: anotherReset is true, so continuing"); + continue; + } + + this.#trace("#connect: returning"); + return; + } catch (e: any) { + this.#trace(`#connect: exception ${e.message}`); + if (!this.#isReady && count > this.#maxCommandRediscovers) { + throw e; + } + + if (e.message !== 'no valid master node') { + console.log(e); + } + await setTimeout(1000); + } finally { + this.#trace("finished connect"); + } + } + } + + async execute( + fn: (client: RedisClientType) => Promise, + clientInfo?: ClientInfo + ): Promise { + let iter = 0; + + while (true) { + if (this.#connectPromise !== undefined) { + await this.#connectPromise; + } + + const client = this.#getClient(clientInfo); + + if (!client.isReady) { + await this.#reset(); + continue; + } + const sockOpts = client.options?.socket as TcpNetConnectOpts | undefined; + this.#trace("attemping to send command to " + sockOpts?.host + ":" + sockOpts?.port) + + try { + /* + // force testing of READONLY errors + if (clientInfo !== undefined) { + if (Math.floor(Math.random() * 10) < 1) { + console.log("throwing READONLY error"); + throw new Error("READONLY You can't write against a read only replica."); + } + } + */ + return await fn(client); + } catch (err) { + if (++iter > this.#maxCommandRediscovers || !(err instanceof Error)) { + throw err; + } + + /* + rediscover and retry if doing a command against a "master" + a) READONLY error (topology has changed) but we haven't been notified yet via pubsub + b) client is "not ready" (disconnected), which means topology might have changed, but sentinel might not see it yet + */ + if (clientInfo !== undefined && (err.message.startsWith('READONLY') || !client.isReady)) { + await this.#reset(); + continue; + } + + throw err; + } + } + } + + async #createPubSub(client: RedisClientType) { + /* Whenever sentinels or slaves get added, or when slave configuration changes, reconfigure */ + await client.pSubscribe(['switch-master', '[-+]sdown', '+slave', '+sentinel', '[-+]odown', '+slave-reconf-done'], (message, channel) => { + this.#handlePubSubControlChannel(channel, message); + }, true); + + return client; + } + + async #handlePubSubControlChannel(channel: Buffer, message: Buffer) { + this.#trace("pubsub control channel message on " + channel); + this.#reset(); + } + + // if clientInfo is defined, it corresponds to a master client in the #masterClients array, otherwise loop around replicaClients + #getClient(clientInfo?: ClientInfo): RedisClientType { + if (clientInfo !== undefined) { + return this.#masterClients[clientInfo.id]; + } + + if (this.#replicaClientsIdx >= this.#replicaClients.length) { + this.#replicaClientsIdx = 0; + } + + if (this.#replicaClients.length == 0) { + throw new Error("no replicas available for read"); + } + + return this.#replicaClients[this.#replicaClientsIdx++]; + } + + async #reset() { + /* closing / don't reset */ + if (this.#isReady == false || this.#destroy == true) { + return; + } + + // already in #connect() + if (this.#connectPromise !== undefined) { + this.#anotherReset = true; + return await this.#connectPromise; + } + + try { + this.#connectPromise = this.#connect(); + return await this.#connectPromise; + } finally { + this.#trace("finished reconfgure"); + this.#connectPromise = undefined; + } + } + + async close() { + this.#destroy = true; + + if (this.#connectPromise != undefined) { + await this.#connectPromise; + } + + this.#isReady = false; + + if (this.#scanTimer) { + clearInterval(this.#scanTimer); + this.#scanTimer = undefined; + } + + const promises = []; + + if (this.#sentinelClient !== undefined) { + if (this.#sentinelClient.isOpen) { + promises.push(this.#sentinelClient.close()); + } + this.#sentinelClient = undefined; + } + + for (const client of this.#masterClients) { + if (client.isOpen) { + promises.push(client.close()); + } + } + + this.#masterClients = []; + + for (const client of this.#replicaClients) { + if (client.isOpen) { + promises.push(client.close()); + } + } + + this.#replicaClients = []; + + await Promise.all(promises); + + this.#pubSubProxy.destroy(); + + this.#isOpen = false; + } + + // destroy has to be async because its stopping others async events, timers and the like + // and shouldn't return until its finished. + async destroy() { + this.#destroy = true; + + if (this.#connectPromise != undefined) { + await this.#connectPromise; + } + + this.#isReady = false; + + if (this.#scanTimer) { + clearInterval(this.#scanTimer); + this.#scanTimer = undefined; + } + + if (this.#sentinelClient !== undefined) { + if (this.#sentinelClient.isOpen) { + this.#sentinelClient.destroy(); + } + this.#sentinelClient = undefined; + } + + for (const client of this.#masterClients) { + if (client.isOpen) { + client.destroy(); + } + } + this.#masterClients = []; + + for (const client of this.#replicaClients) { + if (client.isOpen) { + client.destroy(); + } + } + this.#replicaClients = []; + + this.#pubSubProxy.destroy(); + + this.#isOpen = false + this.#destroy = false; + } + + async subscribe( + channels: string | Array, + listener: PubSubListener, + bufferMode?: T + ) { + return this.#pubSubProxy.subscribe(channels, listener, bufferMode); + } + + async unsubscribe( + channels?: string | Array, + listener?: PubSubListener, + bufferMode?: T + ) { + return this.#pubSubProxy.unsubscribe(channels, listener, bufferMode); + } + + async pSubscribe( + patterns: string | Array, + listener: PubSubListener, + bufferMode?: T + ) { + return this.#pubSubProxy.pSubscribe(patterns, listener, bufferMode); + } + + async pUnsubscribe( + patterns?: string | Array, + listener?: PubSubListener, + bufferMode?: T + ) { + return this.#pubSubProxy.pUnsubscribe(patterns, listener, bufferMode); + } + + // observe/analyze/transform remediation functions + async observe() { + for (const node of this.#sentinelRootNodes) { + let client: RedisClientType | undefined; + try { + this.#trace(`observe: trying to connect to sentinel: ${node.host}:${node.port}`) + client = this.#createClient(node, this.#sentinelClientOptions, false) as unknown as RedisClientType; + client.on('error', (err) => this.emit('error', `obseve client error: ${err}`)); + await client.connect(); + this.#trace(`observe: connected to sentinel`) + + const [sentinelData, masterData, replicaData] = await Promise.all([ + client.sentinel.sentinelSentinels(this.#name), + client.sentinel.sentinelMaster(this.#name), + client.sentinel.sentinelReplicas(this.#name) + ]); + + this.#trace("observe: got all sentinel data"); + + const ret = { + sentinelConnected: node, + sentinelData: sentinelData, + masterData: masterData, + replicaData: replicaData, + currentMaster: this.getMasterNode(), + currentReplicas: this.getReplicaNodes(), + currentSentinel: this.getSentinelNode(), + replicaPoolSize: this.#replicaPoolSize, + useReplicas: this.useReplicas + } + + return ret; + } catch (err) { + this.#trace(`observe: error ${err}`); + this.emit('error', err); + } finally { + if (client !== undefined && client.isOpen) { + this.#trace(`observe: destroying sentinel client`); + client.destroy(); + } + } + } + + this.#trace(`observe: none of the sentinels are available`); + throw new Error('None of the sentinels are available'); + } + + analyze(observed: Awaited["observe"]>>) { + let master = parseNode(observed.masterData); + if (master === undefined) { + this.#trace(`analyze: no valid master node because ${observed.masterData.flags}`); + throw new Error("no valid master node"); + } + + if (master.host === observed.currentMaster?.host && master.port === observed.currentMaster?.port) { + this.#trace(`analyze: master node hasn't changed from ${observed.currentMaster?.host}:${observed.currentMaster?.port}`); + master = undefined; + } else { + this.#trace(`analyze: master node has changed to ${master.host}:${master.port} from ${observed.currentMaster?.host}:${observed.currentMaster?.port}`); + } + + let sentinel: RedisNode | undefined = observed.sentinelConnected; + if (sentinel.host === observed.currentSentinel?.host && sentinel.port === observed.currentSentinel.port) { + this.#trace(`analyze: sentinel node hasn't changed`); + sentinel = undefined; + } else { + this.#trace(`analyze: sentinel node has changed to ${sentinel.host}:${sentinel.port}`); + } + + const replicasToClose: Array = []; + const replicasToOpen = new Map(); + + const desiredSet = new Set(); + const seen = new Set(); + + if (observed.useReplicas) { + const replicaList = createNodeList(observed.replicaData) + + for (const node of replicaList) { + desiredSet.add(JSON.stringify(node)); + } + + for (const [node, value] of observed.currentReplicas) { + if (!desiredSet.has(JSON.stringify(node))) { + replicasToClose.push(node); + this.#trace(`analyze: adding ${node.host}:${node.port} to replicsToClose`); + } else { + seen.add(JSON.stringify(node)); + if (value != observed.replicaPoolSize) { + replicasToOpen.set(node, observed.replicaPoolSize - value); + this.#trace(`analyze: adding ${node.host}:${node.port} to replicsToOpen`); + } + } + } + + for (const node of replicaList) { + if (!seen.has(JSON.stringify(node))) { + replicasToOpen.set(node, observed.replicaPoolSize); + this.#trace(`analyze: adding ${node.host}:${node.port} to replicsToOpen`); + } + } + } + + const ret = { + sentinelList: [observed.sentinelConnected].concat(createNodeList(observed.sentinelData)), + epoch: Number(observed.masterData['config-epoch']), + + sentinelToOpen: sentinel, + masterToOpen: master, + replicasToClose: replicasToClose, + replicasToOpen: replicasToOpen, + }; + + return ret; + } + + async transform(analyzed: ReturnType["analyze"]>) { + this.#trace("transform: enter"); + + let promises: Array> = []; + + if (analyzed.sentinelToOpen) { + this.#trace(`transform: opening a new sentinel`); + if (this.#sentinelClient !== undefined && this.#sentinelClient.isOpen) { + this.#trace(`transform: destroying old sentinel as open`); + this.#sentinelClient.destroy() + this.#sentinelClient = undefined; + } else { + this.#trace(`transform: not destroying old sentinel as not open`); + } + + this.#trace(`transform: creating new sentinel to ${analyzed.sentinelToOpen.host}:${analyzed.sentinelToOpen.port}`); + const node = analyzed.sentinelToOpen; + const client = this.#createClient(analyzed.sentinelToOpen, this.#sentinelClientOptions, false); + client.on('error', (err: Error) => { + if (this.#passthroughClientErrorEvents) { + this.emit('error', new Error(`Sentinel Client (${node.host}:${node.port}): ${err.message}`, { cause: err })); + } + const event: ClientErrorEvent = { + type: 'SENTINEL', + node: clientSocketToNode(client.options!.socket!), + error: err + }; + this.emit('client-error', event); + this.#reset(); + }); + this.#sentinelClient = client; + + this.#trace(`transform: adding sentinel client connect() to promise list`); + const promise = this.#sentinelClient.connect().then((client) => { return this.#createPubSub(client) }); + promises.push(promise); + + this.#trace(`created sentinel client to ${analyzed.sentinelToOpen.host}:${analyzed.sentinelToOpen.port}`); + const event: RedisSentinelEvent = { + type: "SENTINEL_CHANGE", + node: analyzed.sentinelToOpen + } + this.#trace(`transform: emiting topology-change event for sentinel_change`); + if (!this.emit('topology-change', event)) { + this.#trace(`transform: emit for topology-change for sentinel_change returned false`); + } + } + + if (analyzed.masterToOpen) { + this.#trace(`transform: opening a new master`); + const masterPromises = []; + const masterWatches: Array = []; + + this.#trace(`transform: destroying old masters if open`); + for (const client of this.#masterClients) { + masterWatches.push(client.isWatching); + + if (client.isOpen) { + client.destroy() + } + } + + this.#masterClients = []; + + this.#trace(`transform: creating all master clients and adding connect promises`); + for (let i = 0; i < this.#masterPoolSize; i++) { + const node = analyzed.masterToOpen; + const client = this.#createClient(analyzed.masterToOpen, this.#nodeClientOptions); + client.on('error', (err: Error) => { + if (this.#passthroughClientErrorEvents) { + this.emit('error', new Error(`Master Client (${node.host}:${node.port}): ${err.message}`, { cause: err })); + } + const event: ClientErrorEvent = { + type: "MASTER", + node: clientSocketToNode(client.options!.socket!), + error: err + }; + this.emit('client-error', event); + }); + + if (masterWatches[i]) { + client.setDirtyWatch("sentinel config changed in middle of a WATCH Transaction"); + } + this.#masterClients.push(client); + masterPromises.push(client.connect()); + + this.#trace(`created master client to ${analyzed.masterToOpen.host}:${analyzed.masterToOpen.port}`); + } + + this.#trace(`transform: adding promise to change #pubSubProxy node`); + masterPromises.push(this.#pubSubProxy.changeNode(analyzed.masterToOpen)); + promises.push(...masterPromises); + const event: RedisSentinelEvent = { + type: "MASTER_CHANGE", + node: analyzed.masterToOpen + } + this.#trace(`transform: emiting topology-change event for master_change`); + if (!this.emit('topology-change', event)) { + this.#trace(`transform: emit for topology-change for master_change returned false`); + } + this.#configEpoch++; + } + + const replicaCloseSet = new Set(); + for (const node of analyzed.replicasToClose) { + const str = JSON.stringify(node); + replicaCloseSet.add(str); + } + + const newClientList: Array> = []; + const removedSet = new Set(); + + for (const replica of this.#replicaClients) { + const node = clientSocketToNode(replica.options!.socket!); + const str = JSON.stringify(node); + + if (replicaCloseSet.has(str) || !replica.isOpen) { + if (replica.isOpen) { + const sockOpts = replica.options?.socket as TcpNetConnectOpts | undefined; + this.#trace(`destroying replica client to ${sockOpts?.host}:${sockOpts?.port}`); + replica.destroy() + } + if (!removedSet.has(str)) { + const event: RedisSentinelEvent = { + type: "REPLICA_REMOVE", + node: node + } + this.emit('topology-change', event); + removedSet.add(str); + } + } else { + newClientList.push(replica); + } + } + this.#replicaClients = newClientList; + + if (analyzed.replicasToOpen.size != 0) { + for (const [node, size] of analyzed.replicasToOpen) { + for (let i = 0; i < size; i++) { + const client = this.#createClient(node, this.#nodeClientOptions); + client.on('error', (err: Error) => { + if (this.#passthroughClientErrorEvents) { + this.emit('error', new Error(`Replica Client (${node.host}:${node.port}): ${err.message}`, { cause: err })); + } + const event: ClientErrorEvent = { + type: "REPLICA", + node: clientSocketToNode(client.options!.socket!), + error: err + }; + this.emit('client-error', event); + }); + + this.#replicaClients.push(client); + promises.push(client.connect()); + + this.#trace(`created replica client to ${node.host}:${node.port}`); + } + const event: RedisSentinelEvent = { + type: "REPLICA_ADD", + node: node + } + this.emit('topology-change', event); + } + } + + if (analyzed.sentinelList.length != this.#sentinelRootNodes.length) { + this.#sentinelRootNodes = analyzed.sentinelList; + const event: RedisSentinelEvent = { + type: "SENTINE_LIST_CHANGE", + size: analyzed.sentinelList.length + } + this.emit('topology-change', event); + } + + await Promise.all(promises); + this.#trace("transform: exit"); + } + + // introspection functions + getMasterNode(): RedisNode | undefined { + if (this.#masterClients.length == 0) { + return undefined; + } + + for (const master of this.#masterClients) { + if (master.isReady) { + return clientSocketToNode(master.options!.socket!); + } + } + + return undefined; + } + + getSentinelNode(): RedisNode | undefined { + if (this.#sentinelClient === undefined) { + return undefined; + } + + return clientSocketToNode(this.#sentinelClient.options!.socket!); + } + + getReplicaNodes(): Map { + const ret = new Map(); + const initialMap = new Map(); + + for (const replica of this.#replicaClients) { + const node = clientSocketToNode(replica.options!.socket!); + const hash = JSON.stringify(node); + + if (replica.isReady) { + initialMap.set(hash, (initialMap.get(hash) ?? 0) + 1); + } else { + if (!initialMap.has(hash)) { + initialMap.set(hash, 0); + } + } + } + + for (const [key, value] of initialMap) { + ret.set(JSON.parse(key) as RedisNode, value); + } + + return ret; + } + + setTracer(tracer?: Array) { + if (tracer) { + this.#trace = (msg: string) => { tracer.push(msg) }; + } else { + // empty function is faster than testing if something is defined or not + this.#trace = () => { }; + } + } +} + +export class RedisSentinelFactory extends EventEmitter { + options: RedisSentinelOptions; + #sentinelRootNodes: Array; + #replicaIdx: number = -1; + + constructor(options: RedisSentinelOptions) { + super(); + + this.options = options; + this.#sentinelRootNodes = options.sentinelRootNodes; + } + + async updateSentinelRootNodes() { + for (const node of this.#sentinelRootNodes) { + const client = RedisClient.create({ + ...this.options.sentinelClientOptions, + socket: { + ...this.options.sentinelClientOptions?.socket, + host: node.host, + port: node.port, + reconnectStrategy: false + }, + modules: RedisSentinelModule + }).on('error', (err) => this.emit(`updateSentinelRootNodes: ${err}`)); + try { + await client.connect(); + } catch { + if (client.isOpen) { + client.destroy(); + } + continue; + } + + try { + const sentinelData = await client.sentinel.sentinelSentinels(this.options.name); + this.#sentinelRootNodes = [node].concat(createNodeList(sentinelData)); + return; + } finally { + client.destroy(); + } + } + + throw new Error("Couldn't connect to any sentinel node"); + } + + async getMasterNode() { + let connected = false; + + for (const node of this.#sentinelRootNodes) { + const client = RedisClient.create({ + ...this.options.sentinelClientOptions, + socket: { + ...this.options.sentinelClientOptions?.socket, + host: node.host, + port: node.port, + reconnectStrategy: false + }, + modules: RedisSentinelModule + }).on('error', err => this.emit(`getMasterNode: ${err}`)); + + try { + await client.connect(); + } catch { + if (client.isOpen) { + client.destroy(); + } + continue; + } + + connected = true; + + try { + const masterData = await client.sentinel.sentinelMaster(this.options.name); + + let master = parseNode(masterData); + if (master === undefined) { + continue; + } + + return master; + } finally { + client.destroy(); + } + } + + if (connected) { + throw new Error("Master Node Not Enumerated"); + } + + throw new Error("couldn't connect to any sentinels"); + } + + async getMasterClient() { + const master = await this.getMasterNode(); + return RedisClient.create({ + ...this.options.nodeClientOptions, + socket: { + ...this.options.nodeClientOptions?.socket, + host: master.host, + port: master.port + } + }); + } + + async getReplicaNodes() { + let connected = false; + + for (const node of this.#sentinelRootNodes) { + const client = RedisClient.create({ + ...this.options.sentinelClientOptions, + socket: { + ...this.options.sentinelClientOptions?.socket, + host: node.host, + port: node.port, + reconnectStrategy: false + }, + modules: RedisSentinelModule + }).on('error', err => this.emit(`getReplicaNodes: ${err}`)); + + try { + await client.connect(); + } catch { + if (client.isOpen) { + client.destroy(); + } + continue; + } + + connected = true; + + try { + const replicaData = await client.sentinel.sentinelReplicas(this.options.name); + + const replicas = createNodeList(replicaData); + if (replicas.length == 0) { + continue; + } + + return replicas; + } finally { + client.destroy(); + } + } + + if (connected) { + throw new Error("No Replicas Nodes Enumerated"); + } + + throw new Error("couldn't connect to any sentinels"); + } + + async getReplicaClient() { + const replicas = await this.getReplicaNodes(); + if (replicas.length == 0) { + throw new Error("no available replicas"); + } + + this.#replicaIdx++; + if (this.#replicaIdx >= replicas.length) { + this.#replicaIdx = 0; + } + + return RedisClient.create({ + ...this.options.nodeClientOptions, + socket: { + ...this.options.nodeClientOptions?.socket, + host: replicas[this.#replicaIdx].host, + port: replicas[this.#replicaIdx].port + } + }); + } +} diff --git a/packages/client/lib/sentinel/module.ts b/packages/client/lib/sentinel/module.ts new file mode 100644 index 00000000000..e6e98e72f6d --- /dev/null +++ b/packages/client/lib/sentinel/module.ts @@ -0,0 +1,7 @@ + +import { RedisModules } from '../RESP/types'; +import sentinel from './commands'; + +export default { + sentinel +} as const satisfies RedisModules; diff --git a/packages/client/lib/sentinel/multi-commands.ts b/packages/client/lib/sentinel/multi-commands.ts new file mode 100644 index 00000000000..bf616370bf3 --- /dev/null +++ b/packages/client/lib/sentinel/multi-commands.ts @@ -0,0 +1,219 @@ +import COMMANDS from '../commands'; +import RedisMultiCommand, { MULTI_REPLY, MultiReply, MultiReplyType } from '../multi-command'; +import { ReplyWithTypeMapping, CommandReply, Command, CommandArguments, CommanderConfig, RedisFunctions, RedisModules, RedisScripts, RespVersions, TransformReply, RedisScript, RedisFunction, TypeMapping } from '../RESP/types'; +import { attachConfig, functionArgumentsPrefix, getTransformReply } from '../commander'; +import { RedisSentinelType } from './types'; + +type CommandSignature< + REPLIES extends Array, + C extends Command, + M extends RedisModules, + F extends RedisFunctions, + S extends RedisScripts, + RESP extends RespVersions, + TYPE_MAPPING extends TypeMapping +> = (...args: Parameters) => RedisSentinelMultiCommandType< + [...REPLIES, ReplyWithTypeMapping, TYPE_MAPPING>], + M, + F, + S, + RESP, + TYPE_MAPPING +>; + +type WithCommands< + REPLIES extends Array, + M extends RedisModules, + F extends RedisFunctions, + S extends RedisScripts, + RESP extends RespVersions, + TYPE_MAPPING extends TypeMapping +> = { + [P in keyof typeof COMMANDS]: CommandSignature; +}; + +type WithModules< + REPLIES extends Array, + M extends RedisModules, + F extends RedisFunctions, + S extends RedisScripts, + RESP extends RespVersions, + TYPE_MAPPING extends TypeMapping +> = { + [P in keyof M]: { + [C in keyof M[P]]: CommandSignature; + }; +}; + +type WithFunctions< + REPLIES extends Array, + M extends RedisModules, + F extends RedisFunctions, + S extends RedisScripts, + RESP extends RespVersions, + TYPE_MAPPING extends TypeMapping +> = { + [L in keyof F]: { + [C in keyof F[L]]: CommandSignature; + }; +}; + +type WithScripts< + REPLIES extends Array, + M extends RedisModules, + F extends RedisFunctions, + S extends RedisScripts, + RESP extends RespVersions, + TYPE_MAPPING extends TypeMapping +> = { + [P in keyof S]: CommandSignature; +}; + +export type RedisSentinelMultiCommandType< + REPLIES extends Array, + M extends RedisModules, + F extends RedisFunctions, + S extends RedisScripts, + RESP extends RespVersions, + TYPE_MAPPING extends TypeMapping +> = ( + RedisSentinelMultiCommand & + WithCommands & + WithModules & + WithFunctions & + WithScripts +); + +export default class RedisSentinelMultiCommand { + private static _createCommand(command: Command, resp: RespVersions) { + const transformReply = getTransformReply(command, resp); + return function (this: RedisSentinelMultiCommand, ...args: Array) { + const redisArgs = command.transformArguments(...args); + return this.addCommand( + command.IS_READ_ONLY, + redisArgs, + transformReply + ); + }; + } + + private static _createModuleCommand(command: Command, resp: RespVersions) { + const transformReply = getTransformReply(command, resp); + return function (this: { _self: RedisSentinelMultiCommand }, ...args: Array) { + const redisArgs = command.transformArguments(...args); + return this._self.addCommand( + command.IS_READ_ONLY, + redisArgs, + transformReply + ); + }; + } + + private static _createFunctionCommand(name: string, fn: RedisFunction, resp: RespVersions) { + const prefix = functionArgumentsPrefix(name, fn), + transformReply = getTransformReply(fn, resp); + return function (this: { _self: RedisSentinelMultiCommand }, ...args: Array) { + const fnArgs = fn.transformArguments(...args); + const redisArgs: CommandArguments = prefix.concat(fnArgs); + redisArgs.preserve = fnArgs.preserve; + return this._self.addCommand( + fn.IS_READ_ONLY, + redisArgs, + transformReply + ); + }; + } + + private static _createScriptCommand(script: RedisScript, resp: RespVersions) { + const transformReply = getTransformReply(script, resp); + return function (this: RedisSentinelMultiCommand, ...args: Array) { + const scriptArgs = script.transformArguments(...args); + this._setState( + script.IS_READ_ONLY + ); + this._multi.addScript( + script, + scriptArgs, + transformReply + ); + return this; + }; + } + + static extend< + M extends RedisModules = Record, + F extends RedisFunctions = Record, + S extends RedisScripts = Record, + RESP extends RespVersions = 2 + >(config?: CommanderConfig) { + return attachConfig({ + BaseClass: RedisSentinelMultiCommand, + commands: COMMANDS, + createCommand: RedisSentinelMultiCommand._createCommand, + createModuleCommand: RedisSentinelMultiCommand._createModuleCommand, + createFunctionCommand: RedisSentinelMultiCommand._createFunctionCommand, + createScriptCommand: RedisSentinelMultiCommand._createScriptCommand, + config + }); + } + + private readonly _multi = new RedisMultiCommand(); + private readonly _sentinel: RedisSentinelType + private _isReadonly: boolean | undefined = true; + private readonly _typeMapping?: TypeMapping; + + constructor(sentinel: RedisSentinelType, typeMapping: TypeMapping) { + this._sentinel = sentinel; + this._typeMapping = typeMapping; + } + + private _setState( + isReadonly: boolean | undefined, + ) { + this._isReadonly &&= isReadonly; + } + + addCommand( + isReadonly: boolean | undefined, + args: CommandArguments, + transformReply?: TransformReply + ) { + this._setState(isReadonly); + this._multi.addCommand(args, transformReply); + return this; + } + + async exec(execAsPipeline = false) { + if (execAsPipeline) return this.execAsPipeline(); + + return this._multi.transformReplies( + await this._sentinel._executeMulti( + this._isReadonly, + this._multi.queue + ), + this._typeMapping + ) as MultiReplyType; + } + + EXEC = this.exec; + + execTyped(execAsPipeline = false) { + return this.exec(execAsPipeline); + } + + async execAsPipeline() { + if (this._multi.queue.length === 0) return [] as MultiReplyType; + + return this._multi.transformReplies( + await this._sentinel._executePipeline( + this._isReadonly, + this._multi.queue + ), + this._typeMapping + ) as MultiReplyType; + } + + execAsPipelineTyped() { + return this.execAsPipeline(); + } +} diff --git a/packages/client/lib/sentinel/pub-sub-proxy.ts b/packages/client/lib/sentinel/pub-sub-proxy.ts new file mode 100644 index 00000000000..68a6c3b58e6 --- /dev/null +++ b/packages/client/lib/sentinel/pub-sub-proxy.ts @@ -0,0 +1,209 @@ +import EventEmitter from 'node:events'; +import { RedisModules, RedisFunctions, RedisScripts, RespVersions, TypeMapping } from '../RESP/types'; +import { RedisClientOptions } from '../client'; +import { PUBSUB_TYPE, PubSubListener, PubSubTypeListeners } from '../client/pub-sub'; +import { RedisNode } from './types'; +import RedisClient from '../client'; + +type Client = RedisClient< + RedisModules, + RedisFunctions, + RedisScripts, + RespVersions, + TypeMapping +>; + +type Subscriptions = Record< + PUBSUB_TYPE['CHANNELS'] | PUBSUB_TYPE['PATTERNS'], + PubSubTypeListeners +>; + +type PubSubState = { + client: Client; + connectPromise: Promise | undefined; +}; + +type OnError = (err: unknown) => unknown; + +export class PubSubProxy extends EventEmitter { + #clientOptions; + #onError; + + #node?: RedisNode; + #state?: PubSubState; + #subscriptions?: Subscriptions; + + constructor(clientOptions: RedisClientOptions, onError: OnError) { + super(); + + this.#clientOptions = clientOptions; + this.#onError = onError; + } + + #createClient() { + if (this.#node === undefined) { + throw new Error("pubSubProxy: didn't define node to do pubsub against"); + } + + return new RedisClient({ + ...this.#clientOptions, + socket: { + ...this.#clientOptions.socket, + host: this.#node.host, + port: this.#node.port + } + }); + } + + async #initiatePubSubClient(withSubscriptions = false) { + const client = this.#createClient() + .on('error', this.#onError); + + const connectPromise = client.connect() + .then(async client => { + if (this.#state?.client !== client) { + // if pubsub was deactivated while connecting (`this.#pubSubClient === undefined`) + // or if the node changed (`this.#pubSubClient.client !== client`) + client.destroy(); + return this.#state?.connectPromise; + } + + if (withSubscriptions && this.#subscriptions) { + await Promise.all([ + client.extendPubSubListeners(PUBSUB_TYPE.CHANNELS, this.#subscriptions[PUBSUB_TYPE.CHANNELS]), + client.extendPubSubListeners(PUBSUB_TYPE.PATTERNS, this.#subscriptions[PUBSUB_TYPE.PATTERNS]) + ]); + } + + if (this.#state.client !== client) { + // if the node changed (`this.#pubSubClient.client !== client`) + client.destroy(); + return this.#state?.connectPromise; + } + + this.#state!.connectPromise = undefined; + return client; + }) + .catch(err => { + this.#state = undefined; + throw err; + }); + + this.#state = { + client, + connectPromise + }; + + return connectPromise; + } + + #getPubSubClient() { + if (!this.#state) return this.#initiatePubSubClient(); + + return ( + this.#state.connectPromise ?? + this.#state.client + ); + } + + async changeNode(node: RedisNode) { + this.#node = node; + + if (!this.#state) return; + + // if `connectPromise` is undefined, `this.#subscriptions` is already set + // and `this.#state.client` might not have the listeners set yet + if (this.#state.connectPromise === undefined) { + this.#subscriptions = { + [PUBSUB_TYPE.CHANNELS]: this.#state.client.getPubSubListeners(PUBSUB_TYPE.CHANNELS), + [PUBSUB_TYPE.PATTERNS]: this.#state.client.getPubSubListeners(PUBSUB_TYPE.PATTERNS) + }; + + this.#state.client.destroy(); + } + + await this.#initiatePubSubClient(true); + } + + #executeCommand(fn: (client: Client) => T) { + const client = this.#getPubSubClient(); + if (client instanceof RedisClient) { + return fn(client); + } + + return client.then(client => { + // if pubsub was deactivated while connecting + if (client === undefined) return; + + return fn(client); + }).catch(err => { + if (this.#state?.client.isPubSubActive) { + this.#state.client.destroy(); + this.#state = undefined; + } + + throw err; + }); + } + + subscribe( + channels: string | Array, + listener: PubSubListener, + bufferMode?: T + ) { + return this.#executeCommand( + client => client.SUBSCRIBE(channels, listener, bufferMode) + ); + } + + #unsubscribe(fn: (client: Client) => Promise) { + return this.#executeCommand(async client => { + const reply = await fn(client); + + if (!client.isPubSubActive) { + client.destroy(); + this.#state = undefined; + } + + return reply; + }); + } + + async unsubscribe( + channels?: string | Array, + listener?: PubSubListener, + bufferMode?: T + ) { + return this.#unsubscribe(client => client.UNSUBSCRIBE(channels, listener, bufferMode)); + } + + async pSubscribe( + patterns: string | Array, + listener: PubSubListener, + bufferMode?: T + ) { + return this.#executeCommand( + client => client.PSUBSCRIBE(patterns, listener, bufferMode) + ); + } + + async pUnsubscribe( + patterns?: string | Array, + listener?: PubSubListener, + bufferMode?: T + ) { + return this.#unsubscribe(client => client.PUNSUBSCRIBE(patterns, listener, bufferMode)); + } + + destroy() { + this.#subscriptions = undefined; + if (this.#state === undefined) return; + + // `connectPromise` already handles the case of `this.#pubSubState = undefined` + if (!this.#state.connectPromise) { + this.#state.client.destroy(); + } + + this.#state = undefined; + } +} diff --git a/packages/client/lib/sentinel/test-util.ts b/packages/client/lib/sentinel/test-util.ts new file mode 100644 index 00000000000..25dd4c4371a --- /dev/null +++ b/packages/client/lib/sentinel/test-util.ts @@ -0,0 +1,605 @@ +import { createConnection, Socket } from 'node:net'; +import { setTimeout } from 'node:timers/promises'; +import { once } from 'node:events'; +import { promisify } from 'node:util'; +import { exec } from 'node:child_process'; +import { RedisSentinelOptions, RedisSentinelType } from './types'; +import RedisClient from '../client'; +import RedisSentinel from '.'; +import { RedisArgument, RedisFunctions, RedisModules, RedisScripts, RespVersions, TypeMapping } from '../RESP/types'; +const execAsync = promisify(exec); +import RedisSentinelModule from './module' + +interface ErrorWithCode extends Error { + code: string; +} + +async function isPortAvailable(port: number): Promise { + var socket: Socket | undefined = undefined; + try { + socket = createConnection({ port }); + await once(socket, 'connect'); + } catch (err) { + if (err instanceof Error && (err as ErrorWithCode).code === 'ECONNREFUSED') { + return true; + } + } finally { + if (socket !== undefined) { + socket.end(); + } + } + + return false; +} + +const portIterator = (async function* (): AsyncIterableIterator { + for (let i = 6379; i < 65535; i++) { + if (await isPortAvailable(i)) { + yield i; + } + } + + throw new Error('All ports are in use'); +})(); + +export interface RedisServerDockerConfig { + image: string; + version: string; +} + +export interface RedisServerDocker { + port: number; + dockerId: string; +} + +abstract class DockerBase { + async spawnRedisServerDocker({ image, version }: RedisServerDockerConfig, serverArguments: Array, environment?: string): Promise { + const port = (await portIterator.next()).value; + let cmdLine = `docker run --init -d --network host `; + if (environment !== undefined) { + cmdLine += `-e ${environment} `; + } + cmdLine += `${image}:${version} ${serverArguments.join(' ')}`; + cmdLine = cmdLine.replace('{port}', `--port ${port.toString()}`); + // console.log("spawnRedisServerDocker: cmdLine = " + cmdLine); + const { stdout, stderr } = await execAsync(cmdLine); + + if (!stdout) { + throw new Error(`docker run error - ${stderr}`); + } + + while (await isPortAvailable(port)) { + await setTimeout(50); + } + + return { + port, + dockerId: stdout.trim() + }; + } + + async dockerRemove(dockerId: string): Promise { + try { + await this.dockerStop(dockerId); `` + } catch (err) { + // its ok if stop failed, as we are just going to remove, will just be slower + console.log(`dockerStop failed in remove: ${err}`); + } + + const { stderr } = await execAsync(`docker rm -f ${dockerId}`); + if (stderr) { + console.log("docker rm failed"); + throw new Error(`docker rm error - ${stderr}`); + } + } + + async dockerStop(dockerId: string): Promise { + /* this is an optimization to get around slow docker stop times, but will fail if container is already stopped */ + try { + await execAsync(`docker exec ${dockerId} /bin/bash -c "kill -SIGINT 1"`); + } catch (err) { + /* this will fail if container is already not running, can be ignored */ + } + + let ret = await execAsync(`docker stop ${dockerId}`); + if (ret.stderr) { + throw new Error(`docker stop error - ${ret.stderr}`); + } + } + + async dockerStart(dockerId: string): Promise { + const { stderr } = await execAsync(`docker start ${dockerId}`); + if (stderr) { + throw new Error(`docker start error - ${stderr}`); + } + } +} + +export interface RedisSentinelConfig { + numberOfNodes?: number; + nodeDockerConfig?: RedisServerDockerConfig; + nodeServerArguments?: Array + + numberOfSentinels?: number; + sentinelDockerConfig?: RedisServerDockerConfig; + sentinelServerArgument?: Array + + sentinelName: string; + sentinelQuorum?: number; + + password?: string; +} + +type ArrayElement = + ArrayType extends readonly (infer ElementType)[] ? ElementType : never; + +export interface SentinelController { + getMaster(): Promise; + getMasterPort(): Promise; + getRandomNode(): string; + getRandonNonMasterNode(): Promise; + getNodePort(id: string): number; + getAllNodesPort(): Array; + getSentinelPort(id: string): number; + getAllSentinelsPort(): Array; + getSetinel(i: number): string; + stopNode(id: string): Promise; + restartNode(id: string): Promise; + stopSentinel(id: string): Promise; + restartSentinel(id: string): Promise; + getSentinelClient(opts?: Partial>): RedisSentinelType<{}, {}, {}, 2, {}>; +} + +export class SentinelFramework extends DockerBase { + #nodeList: Awaited> = []; + /* port -> docker info/client */ + #nodeMap: Map>>>; + #sentinelList: Awaited> = []; + /* port -> docker info/client */ + #sentinelMap: Map>>>; + + config: RedisSentinelConfig; + + #spawned: boolean = false; + + get spawned() { + return this.#spawned; + } + + constructor(config: RedisSentinelConfig) { + super(); + + this.config = config; + + this.#nodeMap = new Map>>>(); + this.#sentinelMap = new Map>>>(); + } + + getSentinelClient(opts?: Partial>, errors = true) { + if (opts?.sentinelRootNodes !== undefined) { + throw new Error("cannot specify sentinelRootNodes here"); + } + if (opts?.name !== undefined) { + throw new Error("cannot specify sentinel db name here"); + } + + const options: RedisSentinelOptions = { + name: this.config.sentinelName, + sentinelRootNodes: this.#sentinelList.map((sentinel) => { return { host: '127.0.0.1', port: sentinel.docker.port } }), + passthroughClientErrorEvents: errors + } + + if (this.config.password !== undefined) { + options.nodeClientOptions = {password: this.config.password}; + options.sentinelClientOptions = {password: this.config.password}; + } + + if (opts) { + Object.assign(options, opts); + } + + return RedisSentinel.create(options); + } + + async spawnRedisSentinel() { + if (this.#spawned) { + return; + } + + if (this.#nodeMap.size != 0 || this.#sentinelMap.size != 0) { + throw new Error("inconsistent state with partial setup"); + } + + this.#nodeList = await this.spawnRedisSentinelNodes(); + this.#nodeList.map((value) => this.#nodeMap.set(value.docker.port.toString(), value)); + + this.#sentinelList = await this.spawnRedisSentinelSentinels(); + this.#sentinelList.map((value) => this.#sentinelMap.set(value.docker.port.toString(), value)); + + this.#spawned = true; + } + + async cleanup() { + if (!this.#spawned) { + return; + } + + return Promise.all( + [...this.#nodeMap!.values(), ...this.#sentinelMap!.values()].map( + async ({ docker, client }) => { + if (client.isOpen) { + client.destroy(); + } + this.dockerRemove(docker.dockerId); + } + ) + ).finally(async () => { + this.#spawned = false; + this.#nodeMap.clear(); + this.#sentinelMap.clear(); + }); + } + + protected async spawnRedisSentinelNodeDocker() { + const imageInfo: RedisServerDockerConfig = this.config.nodeDockerConfig ?? { image: "redis/redis-stack-server", version: "latest" }; + const serverArguments: Array = this.config.nodeServerArguments ?? []; + let environment; + if (this.config.password !== undefined) { + environment = `REDIS_ARGS="{port} --requirepass ${this.config.password}"`; + } else { + environment = 'REDIS_ARGS="{port}"'; + } + + const docker = await this.spawnRedisServerDocker(imageInfo, serverArguments, environment); + const client = await RedisClient.create({ + password: this.config.password, + socket: { + port: docker.port + } + }).on("error", () => { }).connect(); + + return { + docker, + client + }; + } + + protected async spawnRedisSentinelNodes() { + const master = await this.spawnRedisSentinelNodeDocker(); + + const promises: Array> = []; + + for (let i = 0; i < (this.config.numberOfNodes ?? 0) - 1; i++) { + promises.push( + this.spawnRedisSentinelNodeDocker().then(async node => { + if (this.config.password !== undefined) { + await node.client.configSet({'masterauth': this.config.password}) + } + await node.client.replicaOf('127.0.0.1', master.docker.port); + return node; + }) + ); + } + + return [ + master, + ...await Promise.all(promises) + ]; + } + + protected async spawnRedisSentinelSentinelDocker() { + const imageInfo: RedisServerDockerConfig = this.config.sentinelDockerConfig ?? { image: "redis", version: "latest" } + let serverArguments: Array; + if (this.config.password === undefined) { + serverArguments = this.config.sentinelServerArgument ?? + [ + "/bin/bash", + "-c", + "\"touch /tmp/sentinel.conf ; /usr/local/bin/redis-sentinel /tmp/sentinel.conf {port} \"" + ]; + } else { + serverArguments = this.config.sentinelServerArgument ?? + [ + "/bin/bash", + "-c", + `"touch /tmp/sentinel.conf ; /usr/local/bin/redis-sentinel /tmp/sentinel.conf {port} --requirepass ${this.config.password}"` + ]; + } + + const docker = await this.spawnRedisServerDocker(imageInfo, serverArguments); + const client = await RedisClient.create({ + modules: RedisSentinelModule, + password: this.config.password, + socket: { + port: docker.port + } + }).on("error", () => { }).connect(); + + return { + docker, + client + }; + } + + protected async spawnRedisSentinelSentinels() { + const quorum = this.config.sentinelQuorum?.toString() ?? "2"; + const node = this.#nodeList[0]; + + const promises: Array> = []; + + for (let i = 0; i < (this.config.numberOfSentinels ?? 3); i++) { + promises.push( + this.spawnRedisSentinelSentinelDocker().then(async sentinel => { + await sentinel.client.sentinel.sentinelMonitor(this.config.sentinelName, '127.0.0.1', node.docker.port.toString(), quorum); + const options: Array<{option: RedisArgument, value: RedisArgument}> = []; + options.push({ option: "down-after-milliseconds", value: "100" }); + options.push({ option: "failover-timeout", value: "5000" }); + if (this.config.password !== undefined) { + options.push({ option: "auth-pass", value: this.config.password }); + } + await sentinel.client.sentinel.sentinelSet(this.config.sentinelName, options) + return sentinel; + }) + ); + } + + return [ + ...await Promise.all(promises) + ] + } + + async getAllRunning() { + for (const port of this.getAllNodesPort()) { + let first = true; + while (await isPortAvailable(port)) { + if (!first) { + console.log(`problematic restart ${port}`); + await setTimeout(500); + } else { + first = false; + } + await this.restartNode(port.toString()); + } + } + + for (const port of this.getAllSentinelsPort()) { + let first = true; + while (await isPortAvailable(port)) { + if (!first) { + await setTimeout(500); + } else { + first = false; + } + await this.restartSentinel(port.toString()); + } + } + } + + async addSentinel() { + const quorum = this.config.sentinelQuorum?.toString() ?? "2"; + const node = this.#nodeList[0]; + const sentinel = await this.spawnRedisSentinelSentinelDocker(); + + await sentinel.client.sentinel.sentinelMonitor(this.config.sentinelName, '127.0.0.1', node.docker.port.toString(), quorum); + const options: Array<{option: RedisArgument, value: RedisArgument}> = []; + options.push({ option: "down-after-milliseconds", value: "100" }); + options.push({ option: "failover-timeout", value: "5000" }); + if (this.config.password !== undefined) { + options.push({ option: "auth-pass", value: this.config.password }); + } + await sentinel.client.sentinel.sentinelSet(this.config.sentinelName, options); + + this.#sentinelList.push(sentinel); + this.#sentinelMap.set(sentinel.docker.port.toString(), sentinel); + } + + async addNode() { + const masterPort = await this.getMasterPort(); + const newNode = await this.spawnRedisSentinelNodeDocker(); + + if (this.config.password !== undefined) { + await newNode.client.configSet({'masterauth': this.config.password}) + } + await newNode.client.replicaOf('127.0.0.1', masterPort); + + this.#nodeList.push(newNode); + this.#nodeMap.set(newNode.docker.port.toString(), newNode); + } + + async getMaster(tracer?: Array): Promise { + for (const sentinel of this.#sentinelMap!.values()) { + let info; + + try { + if (!sentinel.client.isReady) { + continue; + } + + info = await sentinel.client.sentinel.sentinelMaster(this.config.sentinelName); + if (tracer) { + tracer.push('getMaster: master data returned from sentinel'); + tracer.push(JSON.stringify(info, undefined, '\t')) + } + } catch (err) { + console.log("getMaster: sentinelMaster call failed: " + err); + continue; + } + + const master = this.#nodeMap.get(info.port); + if (master === undefined) { + throw new Error(`couldn't find master node for ${info.port}`); + } + + if (tracer) { + tracer.push(`getMaster: master port is either ${info.port} or ${master.docker.port}`); + } + + if (!master.client.isOpen) { + throw new Error(`Sentinel's expected master node (${info.port}) is now down`); + } + + return info.port; + } + + throw new Error("Couldn't get master"); + } + + async getMasterPort(tracer?: Array): Promise { + const data = await this.getMaster(tracer) + + return this.#nodeMap.get(data!)!.docker.port; + } + + getRandomNode() { + return this.#nodeList[Math.floor(Math.random() * this.#nodeList.length)].docker.port.toString(); + } + + async getRandonNonMasterNode(): Promise { + const masterPort = await this.getMasterPort(); + while (true) { + const node = this.#nodeList[Math.floor(Math.random() * this.#nodeList.length)]; + if (node.docker.port != masterPort) { + return node.docker.port.toString(); + } + } + } + + async stopNode(id: string) { +// console.log(`stopping node ${id}`); + let node = this.#nodeMap.get(id); + if (node === undefined) { + throw new Error("unknown node: " + id); + } + + if (node.client.isOpen) { + node.client.destroy(); + } + + return await this.dockerStop(node.docker.dockerId); + } + + async restartNode(id: string) { + let node = this.#nodeMap.get(id); + if (node === undefined) { + throw new Error("unknown node: " + id); + } + + await this.dockerStart(node.docker.dockerId); + if (!node.client.isOpen) { + node.client = await RedisClient.create({ + password: this.config.password, + socket: { + port: node.docker.port + } + }).on("error", () => { }).connect(); + } + } + + async stopSentinel(id: string) { + let sentinel = this.#sentinelMap.get(id); + if (sentinel === undefined) { + throw new Error("unknown sentinel: " + id); + } + + if (sentinel.client.isOpen) { + sentinel.client.destroy(); + } + + return await this.dockerStop(sentinel.docker.dockerId); + } + + async restartSentinel(id: string) { + let sentinel = this.#sentinelMap.get(id); + if (sentinel === undefined) { + throw new Error("unknown sentinel: " + id); + } + + await this.dockerStart(sentinel.docker.dockerId); + if (!sentinel.client.isOpen) { + sentinel.client = await RedisClient.create({ + modules: RedisSentinelModule, + password: this.config.password, + socket: { + port: sentinel.docker.port + } + }).on("error", () => { }).connect(); + } + } + + getNodePort(id: string) { + let node = this.#nodeMap.get(id); + if (node === undefined) { + throw new Error("unknown node: " + id); + } + + return node.docker.port; + } + + getAllNodesPort() { + let ports: Array = []; + for (const node of this.#nodeList) { + ports.push(node.docker.port); + } + + return ports + } + + getAllDockerIds() { + let ids = new Map(); + for (const node of this.#nodeList) { + ids.set(node.docker.dockerId, node.docker.port); + } + + return ids; + } + + getSentinelPort(id: string) { + let sentinel = this.#sentinelMap.get(id); + if (sentinel === undefined) { + throw new Error("unknown sentinel: " + id); + } + + return sentinel.docker.port; + } + + getAllSentinelsPort() { + let ports: Array = []; + for (const sentinel of this.#sentinelList) { + ports.push(sentinel.docker.port); + } + + return ports + } + + getSetinel(i: number): string { + return this.#sentinelList[i].docker.port.toString(); + } + + sentinelSentinels() { + for (const sentinel of this.#sentinelList) { + if (sentinel.client.isReady) { + return sentinel.client.sentinel.sentinelSentinels(this.config.sentinelName); + } + } + } + + sentinelMaster() { + for (const sentinel of this.#sentinelList) { + if (sentinel.client.isReady) { + return sentinel.client.sentinel.sentinelMaster(this.config.sentinelName); + } + } + } + + sentinelReplicas() { + for (const sentinel of this.#sentinelList) { + if (sentinel.client.isReady) { + return sentinel.client.sentinel.sentinelReplicas(this.config.sentinelName); + } + } + } +} diff --git a/packages/client/lib/sentinel/types.ts b/packages/client/lib/sentinel/types.ts new file mode 100644 index 00000000000..1f868ec5177 --- /dev/null +++ b/packages/client/lib/sentinel/types.ts @@ -0,0 +1,175 @@ +import { RedisClientOptions } from '../client'; +import { CommandOptions } from '../client/commands-queue'; +import { CommandSignature, CommanderConfig, RedisFunctions, RedisModules, RedisScripts, RespVersions, TypeMapping } from '../RESP/types'; +import COMMANDS from '../commands'; +import RedisSentinel, { RedisSentinelClient } from '.'; +import { RedisTcpSocketOptions } from '../client/socket'; + +export interface RedisNode { + host: string; + port: number; +} + +export interface RedisSentinelOptions< + M extends RedisModules = RedisModules, + F extends RedisFunctions = RedisFunctions, + S extends RedisScripts = RedisScripts, + RESP extends RespVersions = RespVersions, + TYPE_MAPPING extends TypeMapping = TypeMapping +> extends SentinelCommander { + /** + * The sentinel identifier for a particular database cluster + */ + name: string; + /** + * An array of root nodes that are part of the sentinel cluster, which will be used to get the topology. Each element in the array is a client configuration object. There is no need to specify every node in the cluster: 3 should be enough to reliably connect and obtain the sentinel configuration from the server + */ + sentinelRootNodes: Array; + /** + * The maximum number of times a command will retry due to topology changes. + */ + maxCommandRediscovers?: number; + /** + * The configuration values for every node in the cluster. Use this for example when specifying an ACL user to connect with + */ + nodeClientOptions?: RedisClientOptions; + /** + * The configuration values for every sentinel in the cluster. Use this for example when specifying an ACL user to connect with + */ + sentinelClientOptions?: RedisClientOptions; + /** + * The number of clients connected to the master node + */ + masterPoolSize?: number; + /** + * The number of clients connected to each replica node. + * When greater than 0, the client will distribute the load by executing read-only commands (such as `GET`, `GEOSEARCH`, etc.) across all the cluster nodes. + */ + replicaPoolSize?: number; + /** + * TODO + */ + scanInterval?: number; + /** + * TODO + */ + passthroughClientErrorEvents?: boolean; + /** + * When `true`, one client will be reserved for the sentinel object. + * When `false`, the sentinel object will wait for the first available client from the pool. + */ + reserveClient?: boolean; +} + +export interface SentinelCommander< + M extends RedisModules, + F extends RedisFunctions, + S extends RedisScripts, + RESP extends RespVersions, + TYPE_MAPPING extends TypeMapping, + // POLICIES extends CommandPolicies +> extends CommanderConfig { + commandOptions?: CommandOptions; +} + +export type RedisSentinelClientOptions = Omit< + RedisClientOptions, + keyof SentinelCommander +>; + +type WithCommands< + RESP extends RespVersions, + TYPE_MAPPING extends TypeMapping +> = { + [P in keyof typeof COMMANDS]: CommandSignature<(typeof COMMANDS)[P], RESP, TYPE_MAPPING>; +}; + +type WithModules< + M extends RedisModules, + RESP extends RespVersions, + TYPE_MAPPING extends TypeMapping +> = { + [P in keyof M]: { + [C in keyof M[P]]: CommandSignature; + }; +}; + +type WithFunctions< + F extends RedisFunctions, + RESP extends RespVersions, + TYPE_MAPPING extends TypeMapping +> = { + [L in keyof F]: { + [C in keyof F[L]]: CommandSignature; + }; +}; + +type WithScripts< + S extends RedisScripts, + RESP extends RespVersions, + TYPE_MAPPING extends TypeMapping +> = { + [P in keyof S]: CommandSignature; +}; + +export type RedisSentinelClientType< + M extends RedisModules = {}, + F extends RedisFunctions = {}, + S extends RedisScripts = {}, + RESP extends RespVersions = 2, + TYPE_MAPPING extends TypeMapping = {}, +> = ( + RedisSentinelClient & + WithCommands & + WithModules & + WithFunctions & + WithScripts +); + +export type RedisSentinelType< + M extends RedisModules = {}, + F extends RedisFunctions = {}, + S extends RedisScripts = {}, + RESP extends RespVersions = 2, + TYPE_MAPPING extends TypeMapping = {}, + // POLICIES extends CommandPolicies = {} +> = ( + RedisSentinel & + WithCommands & + WithModules & + WithFunctions & + WithScripts +); + +export interface SentinelCommandOptions< + TYPE_MAPPING extends TypeMapping = TypeMapping +> extends CommandOptions {} + +export type ProxySentinel = RedisSentinel; +export type ProxySentinelClient = RedisSentinelClient; +export type NamespaceProxySentinel = { _self: ProxySentinel }; +export type NamespaceProxySentinelClient = { _self: ProxySentinelClient }; + +export type NodeInfo = { + ip: any, + port: any, + flags: any, +}; + +export type RedisSentinelEvent = NodeChangeEvent | SizeChangeEvent; + +export type NodeChangeEvent = { + type: "SENTINEL_CHANGE" | "MASTER_CHANGE" | "REPLICA_ADD" | "REPLICA_REMOVE"; + node: RedisNode; +} + +export type SizeChangeEvent = { + type: "SENTINE_LIST_CHANGE"; + size: Number; +} + +export type ClientErrorEvent = { + type: 'MASTER' | 'REPLICA' | 'SENTINEL' | 'PUBSUBPROXY'; + node: RedisNode; + error: Error; +} diff --git a/packages/client/lib/sentinel/utils.ts b/packages/client/lib/sentinel/utils.ts new file mode 100644 index 00000000000..b4d430b1b44 --- /dev/null +++ b/packages/client/lib/sentinel/utils.ts @@ -0,0 +1,114 @@ +import { ArrayReply, Command, RedisFunction, RedisScript, RespVersions, UnwrapReply } from '../RESP/types'; +import { RedisSocketOptions, RedisTcpSocketOptions } from '../client/socket'; +import { functionArgumentsPrefix, getTransformReply, scriptArgumentsPrefix } from '../commander'; +import { NamespaceProxySentinel, NamespaceProxySentinelClient, ProxySentinel, ProxySentinelClient, RedisNode } from './types'; + +/* TODO: should use map interface, would need a transform reply probably? as resp2 is list form, which this depends on */ +export function parseNode(node: Record): RedisNode | undefined{ + + if (node.flags.includes("s_down") || node.flags.includes("disconnected") || node.flags.includes("failover_in_progress")) { + return undefined; + } + + return { host: node.ip, port: Number(node.port) }; +} + +export function createNodeList(nodes: UnwrapReply>>) { + var nodeList: Array = []; + + for (const nodeData of nodes) { + const node = parseNode(nodeData) + if (node === undefined) { + continue; + } + nodeList.push(node); + } + + return nodeList; +} + +export function clientSocketToNode(socket: RedisSocketOptions): RedisNode { + const s = socket as RedisTcpSocketOptions; + + return { + host: s.host!, + port: s.port! + } +} + +export function createCommand(command: Command, resp: RespVersions) { + const transformReply = getTransformReply(command, resp); + return async function (this: T, ...args: Array) { + const redisArgs = command.transformArguments(...args); + const typeMapping = this._self.commandOptions?.typeMapping; + + const reply = await this._self.sendCommand( + command.IS_READ_ONLY, + redisArgs, + this._self.commandOptions + ); + + return transformReply ? + transformReply(reply, redisArgs.preserve, typeMapping) : + reply; + }; +} + +export function createFunctionCommand(name: string, fn: RedisFunction, resp: RespVersions) { + const prefix = functionArgumentsPrefix(name, fn), + transformReply = getTransformReply(fn, resp); + return async function (this: T, ...args: Array) { + const fnArgs = fn.transformArguments(...args); + const redisArgs = prefix.concat(fnArgs); + const typeMapping = this._self._self.commandOptions?.typeMapping; + + const reply = await this._self._self.sendCommand( + fn.IS_READ_ONLY, + redisArgs, + this._self._self.commandOptions + ); + + return transformReply ? + transformReply(reply, fnArgs.preserve, typeMapping) : + reply; + } +}; + +export function createModuleCommand(command: Command, resp: RespVersions) { + const transformReply = getTransformReply(command, resp); + return async function (this: T, ...args: Array) { + const redisArgs = command.transformArguments(...args); + const typeMapping = this._self._self.commandOptions?.typeMapping; + + const reply = await this._self._self.sendCommand( + command.IS_READ_ONLY, + redisArgs, + this._self._self.commandOptions + ); + + return transformReply ? + transformReply(reply, redisArgs.preserve, typeMapping) : + reply; + } +}; + +export function createScriptCommand(script: RedisScript, resp: RespVersions) { + const prefix = scriptArgumentsPrefix(script), + transformReply = getTransformReply(script, resp); + return async function (this: T, ...args: Array) { + const scriptArgs = script.transformArguments(...args); + const redisArgs = prefix.concat(scriptArgs); + const typeMapping = this._self.commandOptions?.typeMapping; + + const reply = await this._self.executeScript( + script, + script.IS_READ_ONLY, + redisArgs, + this._self.commandOptions + ); + + return transformReply ? + transformReply(reply, scriptArgs.preserve, typeMapping) : + reply; + }; +} diff --git a/packages/client/lib/sentinel/wait-queue.ts b/packages/client/lib/sentinel/wait-queue.ts new file mode 100644 index 00000000000..138801eb4d9 --- /dev/null +++ b/packages/client/lib/sentinel/wait-queue.ts @@ -0,0 +1,24 @@ +import { SinglyLinkedList } from '../client/linked-list'; + +export class WaitQueue { + #list = new SinglyLinkedList(); + #queue = new SinglyLinkedList<(item: T) => unknown>(); + + push(value: T) { + const resolve = this.#queue.shift(); + if (resolve !== undefined) { + resolve(value); + return; + } + + this.#list.push(value); + } + + shift() { + return this.#list.shift(); + } + + wait() { + return new Promise(resolve => this.#queue.push(resolve)); + } +} diff --git a/packages/client/lib/test-utils.ts b/packages/client/lib/test-utils.ts index fbbac3e0b71..29eb03cb73d 100644 --- a/packages/client/lib/test-utils.ts +++ b/packages/client/lib/test-utils.ts @@ -1,11 +1,11 @@ import TestUtils from '@redis/test-utils'; import { SinonSpy } from 'sinon'; -import { promiseTimeout } from './utils'; +import { setTimeout } from 'node:timers/promises'; const utils = new TestUtils({ - dockerImageName: 'redis', + dockerImageName: 'redis/redis-stack', dockerImageVersionArgument: 'redis-version', - defaultDockerVersion: '7.4-rc2' + defaultDockerVersion: '7.4.0-v1' }); export default utils; @@ -15,49 +15,55 @@ const DEBUG_MODE_ARGS = utils.isVersionGreaterThan([7]) ? []; export const GLOBAL = { - SERVERS: { - OPEN: { - serverArguments: [...DEBUG_MODE_ARGS] - }, - PASSWORD: { - serverArguments: ['--requirepass', 'password', ...DEBUG_MODE_ARGS], - clientOptions: { - password: 'password' - } - } + SERVERS: { + OPEN: { + serverArguments: [...DEBUG_MODE_ARGS] + }, + PASSWORD: { + serverArguments: ['--requirepass', 'password', ...DEBUG_MODE_ARGS], + clientOptions: { + password: 'password' + } + } + }, + CLUSTERS: { + OPEN: { + serverArguments: [...DEBUG_MODE_ARGS] }, - CLUSTERS: { - OPEN: { - serverArguments: [...DEBUG_MODE_ARGS] - }, - PASSWORD: { - serverArguments: ['--requirepass', 'password', ...DEBUG_MODE_ARGS], - clusterConfiguration: { - defaults: { - password: 'password' - } - } - }, - WITH_REPLICAS: { - serverArguments: [...DEBUG_MODE_ARGS], - numberOfMasters: 2, - numberOfReplicas: 1, - clusterConfiguration: { - useReplicas: true - } + PASSWORD: { + serverArguments: ['--requirepass', 'password', ...DEBUG_MODE_ARGS], + clusterConfiguration: { + defaults: { + password: 'password' } + } + }, + WITH_REPLICAS: { + serverArguments: [...DEBUG_MODE_ARGS], + numberOfMasters: 2, + numberOfReplicas: 1, + clusterConfiguration: { + useReplicas: true + } } + } }; export async function waitTillBeenCalled(spy: SinonSpy): Promise { - const start = process.hrtime.bigint(), - calls = spy.callCount; + const start = process.hrtime.bigint(), + calls = spy.callCount; - do { - if (process.hrtime.bigint() - start > 1_000_000_000) { - throw new Error('Waiting for more than 1 second'); - } + do { + if (process.hrtime.bigint() - start > 1_000_000_000) { + throw new Error('Waiting for more than 1 second'); + } - await promiseTimeout(50); - } while (spy.callCount === calls); + await setTimeout(50); + } while (spy.callCount === calls); } + +export const BLOCKING_MIN_VALUE = ( + utils.isVersionGreaterThan([7]) ? Number.MIN_VALUE : + utils.isVersionGreaterThan([6]) ? 0.01 : + 1 +); diff --git a/packages/client/lib/utils.ts b/packages/client/lib/utils.ts deleted file mode 100644 index 55bed419813..00000000000 --- a/packages/client/lib/utils.ts +++ /dev/null @@ -1,3 +0,0 @@ -export function promiseTimeout(ms: number): Promise { - return new Promise(resolve => setTimeout(resolve, ms)); -} diff --git a/packages/client/package.json b/packages/client/package.json index e344edd52c3..cb82f67bd53 100644 --- a/packages/client/package.json +++ b/packages/client/package.json @@ -1,42 +1,26 @@ { "name": "@redis/client", - "version": "1.6.0", + "version": "2.0.0-next.4", "license": "MIT", "main": "./dist/index.js", "types": "./dist/index.d.ts", "files": [ - "dist/" + "dist/", + "!dist/tsconfig.tsbuildinfo" ], "scripts": { - "test": "nyc -r text-summary -r lcov mocha -r source-map-support/register -r ts-node/register './lib/**/*.spec.ts'", - "build": "tsc", - "lint": "eslint ./*.ts ./lib/**/*.ts", - "documentation": "typedoc" + "test": "nyc -r text-summary -r lcov mocha -r tsx './lib/**/*.spec.ts'" }, "dependencies": { - "cluster-key-slot": "1.1.2", - "generic-pool": "3.9.0", - "yallist": "4.0.0" + "cluster-key-slot": "1.1.2" }, "devDependencies": { - "@istanbuljs/nyc-config-typescript": "^1.0.2", "@redis/test-utils": "*", - "@types/node": "^20.6.2", - "@types/sinon": "^10.0.16", - "@types/yallist": "^4.0.1", - "@typescript-eslint/eslint-plugin": "^6.7.2", - "@typescript-eslint/parser": "^6.7.2", - "eslint": "^8.49.0", - "nyc": "^15.1.0", - "release-it": "^16.1.5", - "sinon": "^16.0.0", - "source-map-support": "^0.5.21", - "ts-node": "^10.9.1", - "typedoc": "^0.25.1", - "typescript": "^5.2.2" + "@types/sinon": "^17.0.3", + "sinon": "^17.0.1" }, "engines": { - "node": ">=14" + "node": ">= 18" }, "repository": { "type": "git", diff --git a/packages/client/tsconfig.json b/packages/client/tsconfig.json index c71595c5702..8caa47300d4 100644 --- a/packages/client/tsconfig.json +++ b/packages/client/tsconfig.json @@ -5,16 +5,13 @@ }, "include": [ "./index.ts", - "./lib/**/*.ts", - "./package.json" + "./lib/**/*.ts" ], "exclude": [ "./lib/test-utils.ts", - "./lib/**/*.spec.ts" + "./lib/**/*.spec.ts", + "./lib/sentinel/test-util.ts" ], - "ts-node": { - "transpileOnly": true - }, "typedocOptions": { "entryPoints": [ "./index.ts", diff --git a/packages/graph/.release-it.json b/packages/graph/.release-it.json index 530d8f355d4..7797dd0b4dd 100644 --- a/packages/graph/.release-it.json +++ b/packages/graph/.release-it.json @@ -5,6 +5,7 @@ "tagAnnotation": "Release ${tagName}" }, "npm": { + "versionArgs": ["--workspaces-update=false"], "publishArgs": ["--access", "public"] } } diff --git a/packages/graph/lib/commands/CONFIG_GET.spec.ts b/packages/graph/lib/commands/CONFIG_GET.spec.ts index 6e1fa74e219..42c7739f5d7 100644 --- a/packages/graph/lib/commands/CONFIG_GET.spec.ts +++ b/packages/graph/lib/commands/CONFIG_GET.spec.ts @@ -1,22 +1,22 @@ -import { strict as assert } from 'assert'; +import { strict as assert } from 'node:assert'; import testUtils, { GLOBAL } from '../test-utils'; -import { transformArguments } from './CONFIG_GET'; +import CONFIG_GET from './CONFIG_GET'; -describe('CONFIG GET', () => { - it('transformArguments', () => { - assert.deepEqual( - transformArguments('TIMEOUT'), - ['GRAPH.CONFIG', 'GET', 'TIMEOUT'] - ); - }); +describe('GRAPH.CONFIG GET', () => { + it('transformArguments', () => { + assert.deepEqual( + CONFIG_GET.transformArguments('TIMEOUT'), + ['GRAPH.CONFIG', 'GET', 'TIMEOUT'] + ); + }); - testUtils.testWithClient('client.graph.configGet', async client => { - assert.deepEqual( - await client.graph.configGet('TIMEOUT'), - [ - 'TIMEOUT', - 0 - ] - ); - }, GLOBAL.SERVERS.OPEN); + testUtils.testWithClient('client.graph.configGet', async client => { + assert.deepEqual( + await client.graph.configGet('TIMEOUT'), + [ + 'TIMEOUT', + 0 + ] + ); + }, GLOBAL.SERVERS.OPEN); }); diff --git a/packages/graph/lib/commands/CONFIG_GET.ts b/packages/graph/lib/commands/CONFIG_GET.ts index ce80a1148ed..c7ed037e1a1 100644 --- a/packages/graph/lib/commands/CONFIG_GET.ts +++ b/packages/graph/lib/commands/CONFIG_GET.ts @@ -1,12 +1,15 @@ -export const IS_READ_ONLY = true; +import { RedisArgument, TuplesReply, ArrayReply, BlobStringReply, NumberReply, Command } from '@redis/client/dist/lib/RESP/types'; -export function transformArguments(configKey: string): Array { - return ['GRAPH.CONFIG', 'GET', configKey]; -} - -type ConfigItem = [ - configKey: string, - value: number -]; +type ConfigItemReply = TuplesReply<[ + configKey: BlobStringReply, + value: NumberReply +]>; -export declare function transformReply(): ConfigItem | Array; +export default { + FIRST_KEY_INDEX: undefined, + IS_READ_ONLY: true, + transformArguments(configKey: RedisArgument) { + return ['GRAPH.CONFIG', 'GET', configKey]; + }, + transformReply: undefined as unknown as () => ConfigItemReply | ArrayReply +} as const satisfies Command; diff --git a/packages/graph/lib/commands/CONFIG_SET.spec.ts b/packages/graph/lib/commands/CONFIG_SET.spec.ts index 51dce0a8cd9..5ed51e78a29 100644 --- a/packages/graph/lib/commands/CONFIG_SET.spec.ts +++ b/packages/graph/lib/commands/CONFIG_SET.spec.ts @@ -1,19 +1,19 @@ -import { strict as assert } from 'assert'; +import { strict as assert } from 'node:assert'; import testUtils, { GLOBAL } from '../test-utils'; -import { transformArguments } from './CONFIG_SET'; +import CONFIG_SET from './CONFIG_SET'; -describe('CONFIG SET', () => { - it('transformArguments', () => { - assert.deepEqual( - transformArguments('TIMEOUT', 0), - ['GRAPH.CONFIG', 'SET', 'TIMEOUT', '0'] - ); - }); +describe('GRAPH.CONFIG SET', () => { + it('transformArguments', () => { + assert.deepEqual( + CONFIG_SET.transformArguments('TIMEOUT', 0), + ['GRAPH.CONFIG', 'SET', 'TIMEOUT', '0'] + ); + }); - testUtils.testWithClient('client.graph.configSet', async client => { - assert.equal( - await client.graph.configSet('TIMEOUT', 0), - 'OK' - ); - }, GLOBAL.SERVERS.OPEN); + testUtils.testWithClient('client.graph.configSet', async client => { + assert.equal( + await client.graph.configSet('TIMEOUT', 0), + 'OK' + ); + }, GLOBAL.SERVERS.OPEN); }); diff --git a/packages/graph/lib/commands/CONFIG_SET.ts b/packages/graph/lib/commands/CONFIG_SET.ts index ac81449ad15..ba23ac2f1a7 100644 --- a/packages/graph/lib/commands/CONFIG_SET.ts +++ b/packages/graph/lib/commands/CONFIG_SET.ts @@ -1,10 +1,15 @@ -export function transformArguments(configKey: string, value: number): Array { +import { RedisArgument, SimpleStringReply, Command } from '@redis/client/dist/lib/RESP/types'; + +export default { + FIRST_KEY_INDEX: undefined, + IS_READ_ONLY: false, + transformArguments(configKey: RedisArgument, value: number) { return [ - 'GRAPH.CONFIG', - 'SET', - configKey, - value.toString() + 'GRAPH.CONFIG', + 'SET', + configKey, + value.toString() ]; -} - -export declare function transformReply(): 'OK'; + }, + transformReply: undefined as unknown as () => SimpleStringReply<'OK'> +} as const satisfies Command; diff --git a/packages/graph/lib/commands/DELETE.spec.ts b/packages/graph/lib/commands/DELETE.spec.ts index e51ac2bfab8..6fe24fd827a 100644 --- a/packages/graph/lib/commands/DELETE.spec.ts +++ b/packages/graph/lib/commands/DELETE.spec.ts @@ -1,21 +1,21 @@ -import { strict as assert } from 'assert'; +import { strict as assert } from 'node:assert'; import testUtils, { GLOBAL } from '../test-utils'; -import { transformArguments } from './DELETE'; +import DELETE from './DELETE'; -describe('', () => { - it('transformArguments', () => { - assert.deepEqual( - transformArguments('key'), - ['GRAPH.DELETE', 'key'] - ); - }); +describe('GRAPH.DELETE', () => { + it('transformArguments', () => { + assert.deepEqual( + DELETE.transformArguments('key'), + ['GRAPH.DELETE', 'key'] + ); + }); - testUtils.testWithClient('client.graph.delete', async client => { - await client.graph.query('key', 'RETURN 1'); + testUtils.testWithClient('client.graph.delete', async client => { + const [, reply] = await Promise.all([ + client.graph.query('key', 'RETURN 1'), + client.graph.delete('key') + ]); - assert.equal( - typeof await client.graph.delete('key'), - 'string' - ); - }, GLOBAL.SERVERS.OPEN); + assert.equal(typeof reply, 'string'); + }, GLOBAL.SERVERS.OPEN); }); diff --git a/packages/graph/lib/commands/DELETE.ts b/packages/graph/lib/commands/DELETE.ts index 240708143c6..f5f99fb92cc 100644 --- a/packages/graph/lib/commands/DELETE.ts +++ b/packages/graph/lib/commands/DELETE.ts @@ -1,7 +1,10 @@ -export const FIRST_KEY_INDEX = 1; +import { RedisArgument, BlobStringReply, Command } from '@redis/client/dist/lib/RESP/types'; -export function transformArguments(key: string): Array { +export default { + FIRST_KEY_INDEX: 1, + IS_READ_ONLY: false, + transformArguments(key: RedisArgument) { return ['GRAPH.DELETE', key]; -} - -export declare function transformReply(): string; + }, + transformReply: undefined as unknown as () => BlobStringReply +} as const satisfies Command; diff --git a/packages/graph/lib/commands/EXPLAIN.spec.ts b/packages/graph/lib/commands/EXPLAIN.spec.ts index 86d89b212cb..04bf838a4de 100644 --- a/packages/graph/lib/commands/EXPLAIN.spec.ts +++ b/packages/graph/lib/commands/EXPLAIN.spec.ts @@ -1,21 +1,23 @@ -import { strict as assert } from 'assert'; +import { strict as assert } from 'node:assert'; import testUtils, { GLOBAL } from '../test-utils'; -import { transformArguments } from './EXPLAIN'; +import EXPLAIN from './EXPLAIN'; -describe('EXPLAIN', () => { - it('transformArguments', () => { - assert.deepEqual( - transformArguments('key', 'RETURN 0'), - ['GRAPH.EXPLAIN', 'key', 'RETURN 0'] - ); - }); +describe('GRAPH.EXPLAIN', () => { + it('transformArguments', () => { + assert.deepEqual( + EXPLAIN.transformArguments('key', 'RETURN 0'), + ['GRAPH.EXPLAIN', 'key', 'RETURN 0'] + ); + }); - testUtils.testWithClient('client.graph.explain', async client => { - const [, reply] = await Promise.all([ - client.graph.query('key', 'RETURN 0'), // make sure to create a graph first - client.graph.explain('key', 'RETURN 0') - ]); - assert.ok(Array.isArray(reply)); - assert.ok(!reply.find(x => typeof x !== 'string')); - }, GLOBAL.SERVERS.OPEN); + testUtils.testWithClient('client.graph.explain', async client => { + const [, reply] = await Promise.all([ + client.graph.query('key', 'RETURN 0'), // make sure to create a graph first + client.graph.explain('key', 'RETURN 0') + ]); + assert.ok(Array.isArray(reply)); + for (const item of reply) { + assert.equal(typeof item, 'string'); + } + }, GLOBAL.SERVERS.OPEN); }); diff --git a/packages/graph/lib/commands/EXPLAIN.ts b/packages/graph/lib/commands/EXPLAIN.ts index ebea9ca900d..99a73bf04bf 100644 --- a/packages/graph/lib/commands/EXPLAIN.ts +++ b/packages/graph/lib/commands/EXPLAIN.ts @@ -1,9 +1,10 @@ -export const FIRST_KEY_INDEX = 1; +import { RedisArgument, ArrayReply, BlobStringReply, Command } from '@redis/client/dist/lib/RESP/types'; -export const IS_READ_ONLY = true; - -export function transformArguments(key: string, query: string): Array { +export default { + FIRST_KEY_INDEX: 1, + IS_READ_ONLY: true, + transformArguments(key: RedisArgument, query: RedisArgument) { return ['GRAPH.EXPLAIN', key, query]; -} - -export declare function transformReply(): Array; + }, + transformReply: undefined as unknown as () => ArrayReply +} as const satisfies Command; diff --git a/packages/graph/lib/commands/LIST.spec.ts b/packages/graph/lib/commands/LIST.spec.ts index d4fab0358b9..36745efc470 100644 --- a/packages/graph/lib/commands/LIST.spec.ts +++ b/packages/graph/lib/commands/LIST.spec.ts @@ -1,19 +1,19 @@ -import { strict as assert } from 'assert'; +import { strict as assert } from 'node:assert'; import testUtils, { GLOBAL } from '../test-utils'; -import { transformArguments } from './LIST'; +import LIST from './LIST'; -describe('LIST', () => { - it('transformArguments', () => { - assert.deepEqual( - transformArguments(), - ['GRAPH.LIST'] - ); - }); +describe('GRAPH.LIST', () => { + it('transformArguments', () => { + assert.deepEqual( + LIST.transformArguments(), + ['GRAPH.LIST'] + ); + }); - testUtils.testWithClient('client.graph.list', async client => { - assert.deepEqual( - await client.graph.list(), - [] - ); - }, GLOBAL.SERVERS.OPEN); + testUtils.testWithClient('client.graph.list', async client => { + assert.deepEqual( + await client.graph.list(), + [] + ); + }, GLOBAL.SERVERS.OPEN); }); diff --git a/packages/graph/lib/commands/LIST.ts b/packages/graph/lib/commands/LIST.ts index 1939d43d889..01a868854be 100644 --- a/packages/graph/lib/commands/LIST.ts +++ b/packages/graph/lib/commands/LIST.ts @@ -1,7 +1,10 @@ -export const IS_READ_ONLY = true; +import { ArrayReply, BlobStringReply, Command } from '@redis/client/dist/lib/RESP/types'; -export function transformArguments(): Array { +export default { + FIRST_KEY_INDEX: undefined, + IS_READ_ONLY: true, + transformArguments() { return ['GRAPH.LIST']; -} - -export declare function transformReply(): Array; + }, + transformReply: undefined as unknown as () => ArrayReply +} as const satisfies Command; diff --git a/packages/graph/lib/commands/PROFILE.spec.ts b/packages/graph/lib/commands/PROFILE.spec.ts index 80857eb0ab9..a758365d56e 100644 --- a/packages/graph/lib/commands/PROFILE.spec.ts +++ b/packages/graph/lib/commands/PROFILE.spec.ts @@ -1,18 +1,20 @@ -import { strict as assert } from 'assert'; +import { strict as assert } from 'node:assert'; import testUtils, { GLOBAL } from '../test-utils'; -import { transformArguments } from './PROFILE'; +import PROFILE from './PROFILE'; -describe('PROFILE', () => { - it('transformArguments', () => { - assert.deepEqual( - transformArguments('key', 'RETURN 0'), - ['GRAPH.PROFILE', 'key', 'RETURN 0'] - ); - }); +describe('GRAPH.PROFILE', () => { + it('transformArguments', () => { + assert.deepEqual( + PROFILE.transformArguments('key', 'RETURN 0'), + ['GRAPH.PROFILE', 'key', 'RETURN 0'] + ); + }); - testUtils.testWithClient('client.graph.profile', async client => { - const reply = await client.graph.profile('key', 'RETURN 0'); - assert.ok(Array.isArray(reply)); - assert.ok(!reply.find(x => typeof x !== 'string')); - }, GLOBAL.SERVERS.OPEN); + testUtils.testWithClient('client.graph.profile', async client => { + const reply = await client.graph.profile('key', 'RETURN 0'); + assert.ok(Array.isArray(reply)); + for (const item of reply) { + assert.equal(typeof item, 'string'); + } + }, GLOBAL.SERVERS.OPEN); }); diff --git a/packages/graph/lib/commands/PROFILE.ts b/packages/graph/lib/commands/PROFILE.ts index c964452f497..2aa1e83dfb0 100644 --- a/packages/graph/lib/commands/PROFILE.ts +++ b/packages/graph/lib/commands/PROFILE.ts @@ -1,9 +1,10 @@ -export const FIRST_KEY_INDEX = 1; +import { RedisArgument, ArrayReply, BlobStringReply, Command } from '@redis/client/dist/lib/RESP/types'; -export const IS_READ_ONLY = true; - -export function transformArguments(key: string, query: string): Array { +export default { + FIRST_KEY_INDEX: 1, + IS_READ_ONLY: true, + transformArguments(key: RedisArgument, query: RedisArgument) { return ['GRAPH.PROFILE', key, query]; -} - -export declare function transformReply(): Array; + }, + transformReply: undefined as unknown as () => ArrayReply +} as const satisfies Command; diff --git a/packages/graph/lib/commands/QUERY.spec.ts b/packages/graph/lib/commands/QUERY.spec.ts index c8a9a20372b..62c9bcaaefe 100644 --- a/packages/graph/lib/commands/QUERY.spec.ts +++ b/packages/graph/lib/commands/QUERY.spec.ts @@ -1,17 +1,63 @@ -import { strict as assert } from 'assert'; +import { strict as assert } from 'node:assert'; import testUtils, { GLOBAL } from '../test-utils'; -import { transformArguments } from './QUERY'; +import QUERY from './QUERY'; -describe('QUERY', () => { - it('transformArguments', () => { +describe('GRAPH.QUERY', () => { + describe('transformArguments', () => { + it('simple', () => { + assert.deepEqual( + QUERY.transformArguments('key', 'query'), + ['GRAPH.QUERY', 'key', 'query'] + ); + }); + + describe('params', () => { + it('all types', () => { assert.deepEqual( - transformArguments('key', 'query'), - ['GRAPH.QUERY', 'key', 'query'] + QUERY.transformArguments('key', 'query', { + params: { + null: null, + string: '"\\', + number: 0, + boolean: false, + array: [0], + object: {a: 0} + } + }), + ['GRAPH.QUERY', 'key', 'CYPHER null=null string="\\"\\\\" number=0 boolean=false array=[0] object={a:0} query'] ); + }); + + it('TypeError', () => { + assert.throws(() => { + QUERY.transformArguments('key', 'query', { + params: { + a: Symbol() + } + }) + }, TypeError); + }); }); - - testUtils.testWithClient('client.graph.query', async client => { - const { data } = await client.graph.query('key', 'RETURN 0'); - assert.deepEqual(data, [[0]]); - }, GLOBAL.SERVERS.OPEN); + + it('TIMEOUT', () => { + assert.deepEqual( + QUERY.transformArguments('key', 'query', { + TIMEOUT: 1 + }), + ['GRAPH.QUERY', 'key', 'query', 'TIMEOUT', '1'] + ); + }); + + it('compact', () => { + assert.deepEqual( + QUERY.transformArguments('key', 'query', undefined, true), + ['GRAPH.QUERY', 'key', 'query', '--compact'] + ); + }); + }); + + testUtils.testWithClient('client.graph.query', async client => { + const { data } = await client.graph.query('key', 'RETURN 0'); + assert.deepEqual(data, [[0]]); + }, GLOBAL.SERVERS.OPEN); }); diff --git a/packages/graph/lib/commands/QUERY.ts b/packages/graph/lib/commands/QUERY.ts index 741cc6a3601..8a052354610 100644 --- a/packages/graph/lib/commands/QUERY.ts +++ b/packages/graph/lib/commands/QUERY.ts @@ -1,55 +1,102 @@ -import { RedisCommandArgument, RedisCommandArguments } from '@redis/client/dist/lib/commands/index'; -import { pushQueryArguments, QueryOptionsBackwardCompatible } from '.'; - -export const FIRST_KEY_INDEX = 1; - -export function transformArguments( - graph: RedisCommandArgument, - query: RedisCommandArgument, - options?: QueryOptionsBackwardCompatible, - compact?: boolean -): RedisCommandArguments { - return pushQueryArguments( - ['GRAPH.QUERY'], - graph, - query, - options, - compact - ); -} +import { RedisArgument, ArrayReply, BlobStringReply, NumberReply, NullReply, TuplesReply, UnwrapReply, Command } from '@redis/client/dist/lib/RESP/types'; -type Headers = Array; +type Headers = ArrayReply; -type Data = Array; +type Data = ArrayReply; -type Metadata = Array; +type Metadata = ArrayReply; -type QueryRawReply = [ - headers: Headers, - data: Data, - metadata: Metadata +type QueryRawReply = TuplesReply<[ + headers: Headers, + data: Data, + metadata: Metadata ] | [ - metadata: Metadata -]; - -export type QueryReply = { - headers: undefined; - data: undefined; - metadata: Metadata; -} | { - headers: Headers; - data: Data; - metadata: Metadata; + metadata: Metadata +]>; + +type QueryParam = null | string | number | boolean | QueryParams | Array; + +type QueryParams = { + [key: string]: QueryParam; }; -export function transformReply(reply: QueryRawReply): QueryReply { +export interface QueryOptions { + params?: QueryParams; + TIMEOUT?: number; +} + +export function transformQueryArguments( + command: RedisArgument, + graph: RedisArgument, + query: RedisArgument, + options?: QueryOptions, + compact?: boolean +) { + const args = [ + command, + graph, + options?.params ? + `CYPHER ${queryParamsToString(options.params)} ${query}` : + query + ]; + + if (options?.TIMEOUT !== undefined) { + args.push('TIMEOUT', options.TIMEOUT.toString()); + } + + if (compact) { + args.push('--compact'); + } + + return args; +} + +function queryParamsToString(params: QueryParams) { + return Object.entries(params) + .map(([key, value]) => `${key}=${queryParamToString(value)}`) + .join(' '); +} + +function queryParamToString(param: QueryParam): string { + if (param === null) { + return 'null'; + } + + switch (typeof param) { + case 'string': + return `"${param.replace(/["\\]/g, '\\$&')}"`; + + case 'number': + case 'boolean': + return param.toString(); + } + + if (Array.isArray(param)) { + return `[${param.map(queryParamToString).join(',')}]`; + } else if (typeof param === 'object') { + const body = []; + for (const [key, value] of Object.entries(param)) { + body.push(`${key}:${queryParamToString(value)}`); + } + return `{${body.join(',')}}`; + } else { + throw new TypeError(`Unexpected param type ${typeof param} ${param}`) + } +} + +export default { + FIRST_KEY_INDEX: 1, + IS_READ_ONLY: false, + transformArguments: transformQueryArguments.bind(undefined, 'GRAPH.QUERY'), + transformReply(reply: UnwrapReply) { return reply.length === 1 ? { - headers: undefined, - data: undefined, - metadata: reply[0] + headers: undefined, + data: undefined, + metadata: reply[0] } : { - headers: reply[0], - data: reply[1], - metadata: reply[2] + headers: reply[0], + data: reply[1], + metadata: reply[2] }; -} + } +} as const satisfies Command; diff --git a/packages/graph/lib/commands/RO_QUERY.spec.ts b/packages/graph/lib/commands/RO_QUERY.spec.ts index 1d76b1bd652..13829543552 100644 --- a/packages/graph/lib/commands/RO_QUERY.spec.ts +++ b/packages/graph/lib/commands/RO_QUERY.spec.ts @@ -1,20 +1,20 @@ -import { strict as assert } from 'assert'; +import { strict as assert } from 'node:assert'; import testUtils, { GLOBAL } from '../test-utils'; -import { transformArguments } from './RO_QUERY'; +import RO_QUERY from './RO_QUERY'; -describe('RO_QUERY', () => { - it('transformArguments', () => { - assert.deepEqual( - transformArguments('key', 'query'), - ['GRAPH.RO_QUERY', 'key', 'query'] - ); - }); +describe('GRAPH.RO_QUERY', () => { + it('transformArguments', () => { + assert.deepEqual( + RO_QUERY.transformArguments('key', 'query'), + ['GRAPH.RO_QUERY', 'key', 'query'] + ); + }); - testUtils.testWithClient('client.graph.roQuery', async client => { - const [, { data }] = await Promise.all([ - client.graph.query('key', 'RETURN 0'), // make sure to create a graph first - client.graph.roQuery('key', 'RETURN 0') - ]); - assert.deepEqual(data, [[0]]); - }, GLOBAL.SERVERS.OPEN); + testUtils.testWithClient('client.graph.roQuery', async client => { + const [, { data }] = await Promise.all([ + client.graph.query('key', 'RETURN 0'), // make sure to create a graph first + client.graph.roQuery('key', 'RETURN 0') + ]); + assert.deepEqual(data, [[0]]); + }, GLOBAL.SERVERS.OPEN); }); \ No newline at end of file diff --git a/packages/graph/lib/commands/RO_QUERY.ts b/packages/graph/lib/commands/RO_QUERY.ts index d4dda9dee27..5987f511b7d 100644 --- a/packages/graph/lib/commands/RO_QUERY.ts +++ b/packages/graph/lib/commands/RO_QUERY.ts @@ -1,23 +1,9 @@ -import { RedisCommandArgument, RedisCommandArguments } from '@redis/client/dist/lib/commands'; -import { pushQueryArguments, QueryOptionsBackwardCompatible } from '.'; - -export { FIRST_KEY_INDEX } from './QUERY'; - -export const IS_READ_ONLY = true; - -export function transformArguments( - graph: RedisCommandArgument, - query: RedisCommandArgument, - options?: QueryOptionsBackwardCompatible, - compact?: boolean -): RedisCommandArguments { - return pushQueryArguments( - ['GRAPH.RO_QUERY'], - graph, - query, - options, - compact - ); -} - -export { transformReply } from './QUERY'; +import { Command } from '@redis/client/dist/lib/RESP/types'; +import QUERY, { transformQueryArguments } from './QUERY'; + +export default { + FIRST_KEY_INDEX: QUERY.FIRST_KEY_INDEX, + IS_READ_ONLY: true, + transformArguments: transformQueryArguments.bind(undefined, 'GRAPH.RO_QUERY'), + transformReply: QUERY.transformReply +} as const satisfies Command; diff --git a/packages/graph/lib/commands/SLOWLOG.spec.ts b/packages/graph/lib/commands/SLOWLOG.spec.ts index e3083b994d6..c1c77286a26 100644 --- a/packages/graph/lib/commands/SLOWLOG.spec.ts +++ b/packages/graph/lib/commands/SLOWLOG.spec.ts @@ -1,18 +1,20 @@ -import { strict as assert } from 'assert'; +import { strict as assert } from 'node:assert'; import testUtils, { GLOBAL } from '../test-utils'; -import { transformArguments } from './SLOWLOG'; +import SLOWLOG from './SLOWLOG'; -describe('SLOWLOG', () => { - it('transformArguments', () => { - assert.deepEqual( - transformArguments('key'), - ['GRAPH.SLOWLOG', 'key'] - ); - }); +describe('GRAPH.SLOWLOG', () => { + it('transformArguments', () => { + assert.deepEqual( + SLOWLOG.transformArguments('key'), + ['GRAPH.SLOWLOG', 'key'] + ); + }); - testUtils.testWithClient('client.graph.slowLog', async client => { - await client.graph.query('key', 'RETURN 1'); - const reply = await client.graph.slowLog('key'); - assert.equal(reply.length, 1); - }, GLOBAL.SERVERS.OPEN); + testUtils.testWithClient('client.graph.slowLog', async client => { + const [, reply] = await Promise.all([ + client.graph.query('key', 'RETURN 1'), + client.graph.slowLog('key') + ]); + assert.equal(reply.length, 1); + }, GLOBAL.SERVERS.OPEN); }); diff --git a/packages/graph/lib/commands/SLOWLOG.ts b/packages/graph/lib/commands/SLOWLOG.ts index 6ae87af89bf..52927f6040b 100644 --- a/packages/graph/lib/commands/SLOWLOG.ts +++ b/packages/graph/lib/commands/SLOWLOG.ts @@ -1,30 +1,27 @@ -export const IS_READ_ONLY = true; +import { RedisArgument, ArrayReply, TuplesReply, BlobStringReply, UnwrapReply, Command } from '@redis/client/dist/lib/RESP/types'; -export const FIRST_KEY_INDEX = 1; +type SlowLogRawReply = ArrayReply>; -export function transformArguments(key: string) { +export default { + FIRST_KEY_INDEX: 1, + IS_READ_ONLY: true, + transformArguments(key: RedisArgument) { return ['GRAPH.SLOWLOG', key]; -} - -type SlowLogRawReply = Array<[ - timestamp: string, - command: string, - query: string, - took: string -]>; - -type SlowLogReply = Array<{ - timestamp: Date; - command: string; - query: string; - took: number; -}>; - -export function transformReply(logs: SlowLogRawReply): SlowLogReply { - return logs.map(([timestamp, command, query, took]) => ({ - timestamp: new Date(Number(timestamp) * 1000), + }, + transformReply(reply: UnwrapReply) { + return reply.map(log => { + const [timestamp, command, query, took] = log as unknown as UnwrapReply; + return { + timestamp: Number(timestamp), command, query, took: Number(took) - })); -} + }; + }); + } +} as const satisfies Command; diff --git a/packages/graph/lib/commands/index.spec.ts b/packages/graph/lib/commands/index.spec.ts deleted file mode 100644 index a688c49dd39..00000000000 --- a/packages/graph/lib/commands/index.spec.ts +++ /dev/null @@ -1,62 +0,0 @@ -import { strict as assert } from 'assert'; -import { pushQueryArguments } from '.'; - -describe('pushQueryArguments', () => { - it('simple', () => { - assert.deepEqual( - pushQueryArguments(['GRAPH.QUERY'], 'graph', 'query'), - ['GRAPH.QUERY', 'graph', 'query'] - ); - }); - - describe('params', () => { - it('all types', () => { - assert.deepEqual( - pushQueryArguments(['GRAPH.QUERY'], 'graph', 'query', { - params: { - null: null, - string: '"\\', - number: 0, - boolean: false, - array: [0], - object: {a: 0} - } - }), - ['GRAPH.QUERY', 'graph', 'CYPHER null=null string="\\"\\\\" number=0 boolean=false array=[0] object={a:0} query'] - ); - }); - - it('TypeError', () => { - assert.throws(() => { - pushQueryArguments(['GRAPH.QUERY'], 'graph', 'query', { - params: { - a: undefined as any - } - }) - }, TypeError); - }); - }); - - it('TIMEOUT backward compatible', () => { - assert.deepEqual( - pushQueryArguments(['GRAPH.QUERY'], 'graph', 'query', 1), - ['GRAPH.QUERY', 'graph', 'query', 'TIMEOUT', '1'] - ); - }); - - it('TIMEOUT', () => { - assert.deepEqual( - pushQueryArguments(['GRAPH.QUERY'], 'graph', 'query', { - TIMEOUT: 1 - }), - ['GRAPH.QUERY', 'graph', 'query', 'TIMEOUT', '1'] - ); - }); - - it('compact', () => { - assert.deepEqual( - pushQueryArguments(['GRAPH.QUERY'], 'graph', 'query', undefined, true), - ['GRAPH.QUERY', 'graph', 'query', '--compact'] - ); - }); -}); diff --git a/packages/graph/lib/commands/index.ts b/packages/graph/lib/commands/index.ts index 2acf9089ee6..e93356aa951 100644 --- a/packages/graph/lib/commands/index.ts +++ b/packages/graph/lib/commands/index.ts @@ -1,114 +1,31 @@ -import * as CONFIG_GET from './CONFIG_GET'; -import * as CONFIG_SET from './CONFIG_SET';; -import * as DELETE from './DELETE'; -import * as EXPLAIN from './EXPLAIN'; -import * as LIST from './LIST'; -import * as PROFILE from './PROFILE'; -import * as QUERY from './QUERY'; -import * as RO_QUERY from './RO_QUERY'; -import * as SLOWLOG from './SLOWLOG'; -import { RedisCommandArgument, RedisCommandArguments } from '@redis/client/dist/lib/commands'; +import type { RedisCommands } from '@redis/client/dist/lib/RESP/types'; +import CONFIG_GET from './CONFIG_GET'; +import CONFIG_SET from './CONFIG_SET';; +import DELETE from './DELETE'; +import EXPLAIN from './EXPLAIN'; +import LIST from './LIST'; +import PROFILE from './PROFILE'; +import QUERY from './QUERY'; +import RO_QUERY from './RO_QUERY'; +import SLOWLOG from './SLOWLOG'; export default { - CONFIG_GET, - configGet: CONFIG_GET, - CONFIG_SET, - configSet: CONFIG_SET, - DELETE, - delete: DELETE, - EXPLAIN, - explain: EXPLAIN, - LIST, - list: LIST, - PROFILE, - profile: PROFILE, - QUERY, - query: QUERY, - RO_QUERY, - roQuery: RO_QUERY, - SLOWLOG, - slowLog: SLOWLOG -}; - -type QueryParam = null | string | number | boolean | QueryParams | Array; - -type QueryParams = { - [key: string]: QueryParam; -}; - -export interface QueryOptions { - params?: QueryParams; - TIMEOUT?: number; -} - -export type QueryOptionsBackwardCompatible = QueryOptions | number; - -export function pushQueryArguments( - args: RedisCommandArguments, - graph: RedisCommandArgument, - query: RedisCommandArgument, - options?: QueryOptionsBackwardCompatible, - compact?: boolean -): RedisCommandArguments { - args.push(graph); - - if (typeof options === 'number') { - args.push(query); - pushTimeout(args, options); - } else { - args.push( - options?.params ? - `CYPHER ${queryParamsToString(options.params)} ${query}` : - query - ); - - if (options?.TIMEOUT !== undefined) { - pushTimeout(args, options.TIMEOUT); - } - } - - if (compact) { - args.push('--compact'); - } - - return args; -} - -function pushTimeout(args: RedisCommandArguments, timeout: number): void { - args.push('TIMEOUT', timeout.toString()); -} - -function queryParamsToString(params: QueryParams): string { - const parts = []; - for (const [key, value] of Object.entries(params)) { - parts.push(`${key}=${queryParamToString(value)}`); - } - return parts.join(' '); -} - -function queryParamToString(param: QueryParam): string { - if (param === null) { - return 'null'; - } - - switch (typeof param) { - case 'string': - return `"${param.replace(/["\\]/g, '\\$&')}"`; - - case 'number': - case 'boolean': - return param.toString(); - } - - if (Array.isArray(param)) { - return `[${param.map(queryParamToString).join(',')}]`; - } else if (typeof param === 'object') { - const body = []; - for (const [key, value] of Object.entries(param)) { - body.push(`${key}:${queryParamToString(value)}`); - } - return `{${body.join(',')}}`; - } else { - throw new TypeError(`Unexpected param type ${typeof param} ${param}`) - } -} + CONFIG_GET, + configGet: CONFIG_GET, + CONFIG_SET, + configSet: CONFIG_SET, + DELETE, + delete: DELETE, + EXPLAIN, + explain: EXPLAIN, + LIST, + list: LIST, + PROFILE, + profile: PROFILE, + QUERY, + query: QUERY, + RO_QUERY, + roQuery: RO_QUERY, + SLOWLOG, + slowLog: SLOWLOG +} as const satisfies RedisCommands; diff --git a/packages/graph/lib/graph.spec.ts b/packages/graph/lib/graph.spec.ts index 495c6d17a8a..ab506c43a4b 100644 --- a/packages/graph/lib/graph.spec.ts +++ b/packages/graph/lib/graph.spec.ts @@ -1,148 +1,148 @@ -import { strict as assert } from 'assert'; +import { strict as assert } from 'node:assert'; import testUtils, { GLOBAL } from './test-utils'; import Graph from './graph'; describe('Graph', () => { - testUtils.testWithClient('null', async client => { - const graph = new Graph(client as any, 'graph'), - { data } = await graph.query('RETURN null AS key'); - - assert.deepEqual( - data, - [{ key: null }] - ); - }, GLOBAL.SERVERS.OPEN); - - testUtils.testWithClient('string', async client => { - const graph = new Graph(client as any, 'graph'), - { data } = await graph.query('RETURN "string" AS key'); - - assert.deepEqual( - data, - [{ key: 'string' }] - ); - }, GLOBAL.SERVERS.OPEN); - - testUtils.testWithClient('integer', async client => { - const graph = new Graph(client as any, 'graph'), - { data } = await graph.query('RETURN 0 AS key'); - - assert.deepEqual( - data, - [{ key: 0 }] - ); - }, GLOBAL.SERVERS.OPEN); - - testUtils.testWithClient('boolean', async client => { - const graph = new Graph(client as any, 'graph'), - { data } = await graph.query('RETURN false AS key'); - - assert.deepEqual( - data, - [{ key: false }] - ); - }, GLOBAL.SERVERS.OPEN); - - testUtils.testWithClient('double', async client => { - const graph = new Graph(client as any, 'graph'), - { data } = await graph.query('RETURN 0.1 AS key'); - - assert.deepEqual( - data, - [{ key: 0.1 }] - ); - }, GLOBAL.SERVERS.OPEN); - - testUtils.testWithClient('array', async client => { - const graph = new Graph(client as any, 'graph'), - { data } = await graph.query('RETURN [null] AS key'); - - assert.deepEqual( - data, - [{ key: [null] }] - ); - }, GLOBAL.SERVERS.OPEN); - - testUtils.testWithClient('edge', async client => { - const graph = new Graph(client as any, 'graph'); - - // check with and without metadata cache - for (let i = 0; i < 2; i++) { - const { data } = await graph.query('CREATE ()-[edge :edge]->() RETURN edge'); - assert.ok(Array.isArray(data)); - assert.equal(data.length, 1); - assert.equal(typeof data[0].edge.id, 'number'); - assert.equal(data[0].edge.relationshipType, 'edge'); - assert.equal(typeof data[0].edge.sourceId, 'number'); - assert.equal(typeof data[0].edge.destinationId, 'number'); - assert.deepEqual(data[0].edge.properties, {}); - } - - }, GLOBAL.SERVERS.OPEN); - - testUtils.testWithClient('node', async client => { - const graph = new Graph(client as any, 'graph'); - - // check with and without metadata cache - for (let i = 0; i < 2; i++) { - const { data } = await graph.query('CREATE (node :node { p: 0 }) RETURN node'); - assert.ok(Array.isArray(data)); - assert.equal(data.length, 1); - assert.equal(typeof data[0].node.id, 'number'); - assert.deepEqual(data[0].node.labels, ['node']); - assert.deepEqual(data[0].node.properties, { p: 0 }); - } - }, GLOBAL.SERVERS.OPEN); - - testUtils.testWithClient('path', async client => { - const graph = new Graph(client as any, 'graph'), - [, { data }] = await Promise.all([ - await graph.query('CREATE ()-[:edge]->()'), - await graph.roQuery('MATCH path = ()-[:edge]->() RETURN path') - ]); - - assert.ok(Array.isArray(data)); - assert.equal(data.length, 1); - - assert.ok(Array.isArray(data[0].path.nodes)); - assert.equal(data[0].path.nodes.length, 2); - for (const node of data[0].path.nodes) { - assert.equal(typeof node.id, 'number'); - assert.deepEqual(node.labels, []); - assert.deepEqual(node.properties, {}); - } - - assert.ok(Array.isArray(data[0].path.edges)); - assert.equal(data[0].path.edges.length, 1); - for (const edge of data[0].path.edges) { - assert.equal(typeof edge.id, 'number'); - assert.equal(edge.relationshipType, 'edge'); - assert.equal(typeof edge.sourceId, 'number'); - assert.equal(typeof edge.destinationId, 'number'); - assert.deepEqual(edge.properties, {}); - } - }, GLOBAL.SERVERS.OPEN); - - testUtils.testWithClient('map', async client => { - const graph = new Graph(client as any, 'graph'), - { data } = await graph.query('RETURN { key: "value" } AS map'); - - assert.deepEqual(data, [{ - map: { - key: 'value' - } - }]); - }, GLOBAL.SERVERS.OPEN); - - testUtils.testWithClient('point', async client => { - const graph = new Graph(client as any, 'graph'), - { data } = await graph.query('RETURN point({ latitude: 1, longitude: 2 }) AS point'); - - assert.deepEqual(data, [{ - point: { - latitude: 1, - longitude: 2 - } - }]); - }, GLOBAL.SERVERS.OPEN); + testUtils.testWithClient('null', async client => { + const graph = new Graph(client as any, 'graph'), + { data } = await graph.query('RETURN null AS key'); + + assert.deepEqual( + data, + [{ key: null }] + ); + }, GLOBAL.SERVERS.OPEN); + + testUtils.testWithClient('string', async client => { + const graph = new Graph(client as any, 'graph'), + { data } = await graph.query('RETURN "string" AS key'); + + assert.deepEqual( + data, + [{ key: 'string' }] + ); + }, GLOBAL.SERVERS.OPEN); + + testUtils.testWithClient('integer', async client => { + const graph = new Graph(client as any, 'graph'), + { data } = await graph.query('RETURN 0 AS key'); + + assert.deepEqual( + data, + [{ key: 0 }] + ); + }, GLOBAL.SERVERS.OPEN); + + testUtils.testWithClient('boolean', async client => { + const graph = new Graph(client as any, 'graph'), + { data } = await graph.query('RETURN false AS key'); + + assert.deepEqual( + data, + [{ key: false }] + ); + }, GLOBAL.SERVERS.OPEN); + + testUtils.testWithClient('double', async client => { + const graph = new Graph(client as any, 'graph'), + { data } = await graph.query('RETURN 0.1 AS key'); + + assert.deepEqual( + data, + [{ key: 0.1 }] + ); + }, GLOBAL.SERVERS.OPEN); + + testUtils.testWithClient('array', async client => { + const graph = new Graph(client as any, 'graph'), + { data } = await graph.query('RETURN [null] AS key'); + + assert.deepEqual( + data, + [{ key: [null] }] + ); + }, GLOBAL.SERVERS.OPEN); + + testUtils.testWithClient('edge', async client => { + const graph = new Graph(client as any, 'graph'); + + // check with and without metadata cache + for (let i = 0; i < 2; i++) { + const { data } = await graph.query('CREATE ()-[edge :edge]->() RETURN edge'); + assert.ok(Array.isArray(data)); + assert.equal(data.length, 1); + assert.equal(typeof data[0].edge.id, 'number'); + assert.equal(data[0].edge.relationshipType, 'edge'); + assert.equal(typeof data[0].edge.sourceId, 'number'); + assert.equal(typeof data[0].edge.destinationId, 'number'); + assert.deepEqual(data[0].edge.properties, {}); + } + + }, GLOBAL.SERVERS.OPEN); + + testUtils.testWithClient('node', async client => { + const graph = new Graph(client as any, 'graph'); + + // check with and without metadata cache + for (let i = 0; i < 2; i++) { + const { data } = await graph.query('CREATE (node :node { p: 0 }) RETURN node'); + assert.ok(Array.isArray(data)); + assert.equal(data.length, 1); + assert.equal(typeof data[0].node.id, 'number'); + assert.deepEqual(data[0].node.labels, ['node']); + assert.deepEqual(data[0].node.properties, { p: 0 }); + } + }, GLOBAL.SERVERS.OPEN); + + testUtils.testWithClient('path', async client => { + const graph = new Graph(client as any, 'graph'), + [, { data }] = await Promise.all([ + await graph.query('CREATE ()-[:edge]->()'), + await graph.roQuery('MATCH path = ()-[:edge]->() RETURN path') + ]); + + assert.ok(Array.isArray(data)); + assert.equal(data.length, 1); + + assert.ok(Array.isArray(data[0].path.nodes)); + assert.equal(data[0].path.nodes.length, 2); + for (const node of data[0].path.nodes) { + assert.equal(typeof node.id, 'number'); + assert.deepEqual(node.labels, []); + assert.deepEqual(node.properties, {}); + } + + assert.ok(Array.isArray(data[0].path.edges)); + assert.equal(data[0].path.edges.length, 1); + for (const edge of data[0].path.edges) { + assert.equal(typeof edge.id, 'number'); + assert.equal(edge.relationshipType, 'edge'); + assert.equal(typeof edge.sourceId, 'number'); + assert.equal(typeof edge.destinationId, 'number'); + assert.deepEqual(edge.properties, {}); + } + }, GLOBAL.SERVERS.OPEN); + + testUtils.testWithClient('map', async client => { + const graph = new Graph(client as any, 'graph'), + { data } = await graph.query('RETURN { key: "value" } AS map'); + + assert.deepEqual(data, [{ + map: { + key: 'value' + } + }]); + }, GLOBAL.SERVERS.OPEN); + + testUtils.testWithClient('point', async client => { + const graph = new Graph(client as any, 'graph'), + { data } = await graph.query('RETURN point({ latitude: 1, longitude: 2 }) AS point'); + + assert.deepEqual(data, [{ + point: { + latitude: 1, + longitude: 2 + } + }]); + }, GLOBAL.SERVERS.OPEN); }); diff --git a/packages/graph/lib/graph.ts b/packages/graph/lib/graph.ts index a95338bd8f3..348c8b7155f 100644 --- a/packages/graph/lib/graph.ts +++ b/packages/graph/lib/graph.ts @@ -1,359 +1,359 @@ -import { RedisClientType } from '@redis/client/dist/lib/client/index'; -import { RedisCommandArgument, RedisFunctions, RedisScripts } from '@redis/client/dist/lib/commands'; -import { QueryOptions } from './commands'; -import { QueryReply } from './commands/QUERY'; +import { RedisClientType } from '@redis/client'; +import { RedisArgument, RedisFunctions, RedisScripts } from '@redis/client/dist/lib/RESP/types'; +import QUERY, { QueryOptions } from './commands/QUERY'; interface GraphMetadata { - labels: Array; - relationshipTypes: Array; - propertyKeys: Array; + labels: Array; + relationshipTypes: Array; + propertyKeys: Array; } // https://github.com/RedisGraph/RedisGraph/blob/master/src/resultset/formatters/resultset_formatter.h#L20 enum GraphValueTypes { - UNKNOWN = 0, - NULL = 1, - STRING = 2, - INTEGER = 3, - BOOLEAN = 4, - DOUBLE = 5, - ARRAY = 6, - EDGE = 7, - NODE = 8, - PATH = 9, - MAP = 10, - POINT = 11 + UNKNOWN = 0, + NULL = 1, + STRING = 2, + INTEGER = 3, + BOOLEAN = 4, + DOUBLE = 5, + ARRAY = 6, + EDGE = 7, + NODE = 8, + PATH = 9, + MAP = 10, + POINT = 11 } type GraphEntityRawProperties = Array<[ - id: number, - ...value: GraphRawValue + id: number, + ...value: GraphRawValue ]>; type GraphEdgeRawValue = [ - GraphValueTypes.EDGE, - [ - id: number, - relationshipTypeId: number, - sourceId: number, - destinationId: number, - properties: GraphEntityRawProperties - ] + GraphValueTypes.EDGE, + [ + id: number, + relationshipTypeId: number, + sourceId: number, + destinationId: number, + properties: GraphEntityRawProperties + ] ]; type GraphNodeRawValue = [ - GraphValueTypes.NODE, - [ - id: number, - labelIds: Array, - properties: GraphEntityRawProperties - ] + GraphValueTypes.NODE, + [ + id: number, + labelIds: Array, + properties: GraphEntityRawProperties + ] ]; type GraphPathRawValue = [ - GraphValueTypes.PATH, - [ - nodes: [ - GraphValueTypes.ARRAY, - Array - ], - edges: [ - GraphValueTypes.ARRAY, - Array - ] + GraphValueTypes.PATH, + [ + nodes: [ + GraphValueTypes.ARRAY, + Array + ], + edges: [ + GraphValueTypes.ARRAY, + Array ] + ] ]; type GraphMapRawValue = [ - GraphValueTypes.MAP, - Array + GraphValueTypes.MAP, + Array ]; type GraphRawValue = [ - GraphValueTypes.NULL, - null + GraphValueTypes.NULL, + null ] | [ - GraphValueTypes.STRING, - string + GraphValueTypes.STRING, + string ] | [ - GraphValueTypes.INTEGER, - number + GraphValueTypes.INTEGER, + number ] | [ - GraphValueTypes.BOOLEAN, - string + GraphValueTypes.BOOLEAN, + string ] | [ - GraphValueTypes.DOUBLE, - string + GraphValueTypes.DOUBLE, + string ] | [ - GraphValueTypes.ARRAY, - Array + GraphValueTypes.ARRAY, + Array ] | GraphEdgeRawValue | GraphNodeRawValue | GraphPathRawValue | GraphMapRawValue | [ - GraphValueTypes.POINT, - [ - latitude: string, - longitude: string - ] + GraphValueTypes.POINT, + [ + latitude: string, + longitude: string + ] ]; type GraphEntityProperties = Record; interface GraphEdge { - id: number; - relationshipType: string; - sourceId: number; - destinationId: number; - properties: GraphEntityProperties; + id: number; + relationshipType: string; + sourceId: number; + destinationId: number; + properties: GraphEntityProperties; } interface GraphNode { - id: number; - labels: Array; - properties: GraphEntityProperties; + id: number; + labels: Array; + properties: GraphEntityProperties; } interface GraphPath { - nodes: Array; - edges: Array; + nodes: Array; + edges: Array; } type GraphMap = { - [key: string]: GraphValue; + [key: string]: GraphValue; }; type GraphValue = null | string | number | boolean | Array | { } | GraphEdge | GraphNode | GraphPath | GraphMap | { - latitude: string; - longitude: string; + latitude: string; + longitude: string; }; -export type GraphReply = Omit & { - data?: Array; +export type GraphReply = { + data?: Array; }; export type GraphClientType = RedisClientType<{ - graph: { - query: typeof import('./commands/QUERY'), - roQuery: typeof import('./commands/RO_QUERY') - } + graph: { + query: typeof QUERY, + roQuery: typeof import('./commands/RO_QUERY.js').default + } }, RedisFunctions, RedisScripts>; export default class Graph { - #client: GraphClientType; - #name: string; - #metadata?: GraphMetadata; - - constructor( - client: GraphClientType, - name: string - ) { - this.#client = client; - this.#name = name; - } - - async query( - query: RedisCommandArgument, - options?: QueryOptions - ) { - return this.#parseReply( - await this.#client.graph.query( - this.#name, - query, - options, - true - ) - ); - } - - async roQuery( - query: RedisCommandArgument, - options?: QueryOptions - ) { - return this.#parseReply( - await this.#client.graph.roQuery( - this.#name, - query, - options, - true - ) - ); - } - - #setMetadataPromise?: Promise; - - #updateMetadata(): Promise { - this.#setMetadataPromise ??= this.#setMetadata() - .finally(() => this.#setMetadataPromise = undefined); - return this.#setMetadataPromise; - } - - // DO NOT use directly, use #updateMetadata instead - async #setMetadata(): Promise { - const [labels, relationshipTypes, propertyKeys] = await Promise.all([ - this.#client.graph.roQuery(this.#name, 'CALL db.labels()'), - this.#client.graph.roQuery(this.#name, 'CALL db.relationshipTypes()'), - this.#client.graph.roQuery(this.#name, 'CALL db.propertyKeys()') - ]); - - this.#metadata = { - labels: this.#cleanMetadataArray(labels.data as Array<[string]>), - relationshipTypes: this.#cleanMetadataArray(relationshipTypes.data as Array<[string]>), - propertyKeys: this.#cleanMetadataArray(propertyKeys.data as Array<[string]>) - }; - - return this.#metadata; - } - - #cleanMetadataArray(arr: Array<[string]>): Array { - return arr.map(([value]) => value); - } - - #getMetadata( - key: T, - id: number - ): GraphMetadata[T][number] | Promise { - return this.#metadata?.[key][id] ?? this.#getMetadataAsync(key, id); - } - - // DO NOT use directly, use #getMetadata instead - async #getMetadataAsync( - key: T, - id: number - ): Promise { - const value = (await this.#updateMetadata())[key][id]; - if (value === undefined) throw new Error(`Cannot find value from ${key}[${id}]`); + #client: GraphClientType; + #name: string; + #metadata?: GraphMetadata; + + constructor( + client: GraphClientType, + name: string + ) { + this.#client = client; + this.#name = name; + } + + async query( + query: RedisArgument, + options?: QueryOptions + ) { + return this.#parseReply( + await this.#client.graph.query( + this.#name, + query, + options, + true + ) + ); + } + + async roQuery( + query: RedisArgument, + options?: QueryOptions + ) { + return this.#parseReply( + await this.#client.graph.roQuery( + this.#name, + query, + options, + true + ) + ); + } + + #setMetadataPromise?: Promise; + + #updateMetadata(): Promise { + this.#setMetadataPromise ??= this.#setMetadata() + .finally(() => this.#setMetadataPromise = undefined); + return this.#setMetadataPromise; + } + + // DO NOT use directly, use #updateMetadata instead + async #setMetadata(): Promise { + const [labels, relationshipTypes, propertyKeys] = await Promise.all([ + this.#client.graph.roQuery(this.#name, 'CALL db.labels()'), + this.#client.graph.roQuery(this.#name, 'CALL db.relationshipTypes()'), + this.#client.graph.roQuery(this.#name, 'CALL db.propertyKeys()') + ]); + + this.#metadata = { + labels: this.#cleanMetadataArray(labels.data as Array<[string]>), + relationshipTypes: this.#cleanMetadataArray(relationshipTypes.data as Array<[string]>), + propertyKeys: this.#cleanMetadataArray(propertyKeys.data as Array<[string]>) + }; + + return this.#metadata; + } + + #cleanMetadataArray(arr: Array<[string]>): Array { + return arr.map(([value]) => value); + } + + #getMetadata( + key: T, + id: number + ): GraphMetadata[T][number] | Promise { + return this.#metadata?.[key][id] ?? this.#getMetadataAsync(key, id); + } + + // DO NOT use directly, use #getMetadata instead + async #getMetadataAsync( + key: T, + id: number + ): Promise { + const value = (await this.#updateMetadata())[key][id]; + if (value === undefined) throw new Error(`Cannot find value from ${key}[${id}]`); + return value; + } + + // TODO: reply type + async #parseReply(reply: any): Promise> { + if (!reply.data) return reply; + + const promises: Array> = [], + parsed = { + metadata: reply.metadata, + data: reply.data!.map((row: any) => { + const data: Record = {}; + for (let i = 0; i < row.length; i++) { + data[reply.headers[i][1]] = this.#parseValue(row[i], promises); + } + + return data as unknown as T; + }) + }; + + if (promises.length) await Promise.all(promises); + + return parsed; + } + + #parseValue([valueType, value]: GraphRawValue, promises: Array>): GraphValue { + switch (valueType) { + case GraphValueTypes.NULL: + return null; + + case GraphValueTypes.STRING: + case GraphValueTypes.INTEGER: return value; - } - async #parseReply(reply: QueryReply): Promise> { - if (!reply.data) return reply; + case GraphValueTypes.BOOLEAN: + return value === 'true'; - const promises: Array> = [], - parsed = { - metadata: reply.metadata, - data: reply.data!.map((row: any) => { - const data: Record = {}; - for (let i = 0; i < row.length; i++) { - data[reply.headers[i][1]] = this.#parseValue(row[i], promises); - } + case GraphValueTypes.DOUBLE: + return parseFloat(value); - return data as unknown as T; - }) - }; + case GraphValueTypes.ARRAY: + return value.map(x => this.#parseValue(x, promises)); - if (promises.length) await Promise.all(promises); + case GraphValueTypes.EDGE: + return this.#parseEdge(value, promises); - return parsed; - } - - #parseValue([valueType, value]: GraphRawValue, promises: Array>): GraphValue { - switch (valueType) { - case GraphValueTypes.NULL: - return null; - - case GraphValueTypes.STRING: - case GraphValueTypes.INTEGER: - return value; - - case GraphValueTypes.BOOLEAN: - return value === 'true'; - - case GraphValueTypes.DOUBLE: - return parseFloat(value); - - case GraphValueTypes.ARRAY: - return value.map(x => this.#parseValue(x, promises)); - - case GraphValueTypes.EDGE: - return this.#parseEdge(value, promises); - - case GraphValueTypes.NODE: - return this.#parseNode(value, promises); - - case GraphValueTypes.PATH: - return { - nodes: value[0][1].map(([, node]) => this.#parseNode(node, promises)), - edges: value[1][1].map(([, edge]) => this.#parseEdge(edge, promises)) - }; - - case GraphValueTypes.MAP: - const map: GraphMap = {}; - for (let i = 0; i < value.length; i++) { - map[value[i++] as string] = this.#parseValue(value[i] as GraphRawValue, promises); - } - - return map; - - case GraphValueTypes.POINT: - return { - latitude: parseFloat(value[0]), - longitude: parseFloat(value[1]) - }; + case GraphValueTypes.NODE: + return this.#parseNode(value, promises); - default: - throw new Error(`unknown scalar type: ${valueType}`); - } - } + case GraphValueTypes.PATH: + return { + nodes: value[0][1].map(([, node]) => this.#parseNode(node, promises)), + edges: value[1][1].map(([, edge]) => this.#parseEdge(edge, promises)) + }; - #parseEdge([ - id, - relationshipTypeId, - sourceId, - destinationId, - properties - ]: GraphEdgeRawValue[1], promises: Array>): GraphEdge { - const edge = { - id, - sourceId, - destinationId, - properties: this.#parseProperties(properties, promises) - } as GraphEdge; - - const relationshipType = this.#getMetadata('relationshipTypes', relationshipTypeId); - if (relationshipType instanceof Promise) { - promises.push( - relationshipType.then(value => edge.relationshipType = value) - ); - } else { - edge.relationshipType = relationshipType; + case GraphValueTypes.MAP: + const map: GraphMap = {}; + for (let i = 0; i < value.length; i++) { + map[value[i++] as string] = this.#parseValue(value[i] as GraphRawValue, promises); } - return edge; - } - - #parseNode([ - id, - labelIds, - properties - ]: GraphNodeRawValue[1], promises: Array>): GraphNode { - const labels = new Array(labelIds.length); - for (let i = 0; i < labelIds.length; i++) { - const value = this.#getMetadata('labels', labelIds[i]); - if (value instanceof Promise) { - promises.push(value.then(value => labels[i] = value)); - } else { - labels[i] = value; - } - } + return map; + case GraphValueTypes.POINT: return { - id, - labels, - properties: this.#parseProperties(properties, promises) + latitude: parseFloat(value[0]), + longitude: parseFloat(value[1]) }; + + default: + throw new Error(`unknown scalar type: ${valueType}`); + } + } + + #parseEdge([ + id, + relationshipTypeId, + sourceId, + destinationId, + properties + ]: GraphEdgeRawValue[1], promises: Array>): GraphEdge { + const edge = { + id, + sourceId, + destinationId, + properties: this.#parseProperties(properties, promises) + } as GraphEdge; + + const relationshipType = this.#getMetadata('relationshipTypes', relationshipTypeId); + if (relationshipType instanceof Promise) { + promises.push( + relationshipType.then(value => edge.relationshipType = value) + ); + } else { + edge.relationshipType = relationshipType; } - #parseProperties(raw: GraphEntityRawProperties, promises: Array>): GraphEntityProperties { - const parsed: GraphEntityProperties = {}; - for (const [id, type, value] of raw) { - const parsedValue = this.#parseValue([type, value] as GraphRawValue, promises), - key = this.#getMetadata('propertyKeys', id); - if (key instanceof Promise) { - promises.push(key.then(key => parsed[key] = parsedValue)); - } else { - parsed[key] = parsedValue; - } - } + return edge; + } + + #parseNode([ + id, + labelIds, + properties + ]: GraphNodeRawValue[1], promises: Array>): GraphNode { + const labels = new Array(labelIds.length); + for (let i = 0; i < labelIds.length; i++) { + const value = this.#getMetadata('labels', labelIds[i]); + if (value instanceof Promise) { + promises.push(value.then(value => labels[i] = value)); + } else { + labels[i] = value; + } + } - return parsed; + return { + id, + labels, + properties: this.#parseProperties(properties, promises) + }; + } + + #parseProperties(raw: GraphEntityRawProperties, promises: Array>): GraphEntityProperties { + const parsed: GraphEntityProperties = {}; + for (const [id, type, value] of raw) { + const parsedValue = this.#parseValue([type, value] as GraphRawValue, promises), + key = this.#getMetadata('propertyKeys', id); + if (key instanceof Promise) { + promises.push(key.then(key => parsed[key] = parsedValue)); + } else { + parsed[key] = parsedValue; + } } + + return parsed; + } } diff --git a/packages/graph/lib/test-utils.ts b/packages/graph/lib/test-utils.ts index 56c0af56a2e..2aa9384dbe6 100644 --- a/packages/graph/lib/test-utils.ts +++ b/packages/graph/lib/test-utils.ts @@ -2,19 +2,20 @@ import TestUtils from '@redis/test-utils'; import RedisGraph from '.'; export default new TestUtils({ - dockerImageName: 'redislabs/redisgraph', - dockerImageVersionArgument: 'redisgraph-version' + dockerImageName: 'redis/redis-stack', + dockerImageVersionArgument: 'redisgraph-version', + defaultDockerVersion: '7.4.0-v1' }); export const GLOBAL = { - SERVERS: { - OPEN: { - serverArguments: ['--loadmodule /usr/lib/redis/modules/redisgraph.so'], - clientOptions: { - modules: { - graph: RedisGraph - } - } + SERVERS: { + OPEN: { + serverArguments: [], + clientOptions: { + modules: { + graph: RedisGraph } + } } + } }; diff --git a/packages/graph/package.json b/packages/graph/package.json index 95cce6b8a86..54b6aad6493 100644 --- a/packages/graph/package.json +++ b/packages/graph/package.json @@ -1,30 +1,24 @@ { "name": "@redis/graph", - "version": "1.1.1", + "version": "2.0.0-next.2", "license": "MIT", - "main": "./dist/index.js", - "types": "./dist/index.d.ts", + "main": "./dist/lib/index.js", + "types": "./dist/lib/index.d.ts", "files": [ - "dist/" + "dist/", + "!dist/tsconfig.tsbuildinfo" ], "scripts": { - "test": "nyc -r text-summary -r lcov mocha -r source-map-support/register -r ts-node/register './lib/**/*.spec.ts'", - "build": "tsc", - "documentation": "typedoc" + "test-disable": "nyc -r text-summary -r lcov mocha -r tsx './lib/**/*.spec.ts'" }, "peerDependencies": { - "@redis/client": "^1.0.0" + "@redis/client": "^2.0.0-next.4" }, "devDependencies": { - "@istanbuljs/nyc-config-typescript": "^1.0.2", - "@redis/test-utils": "*", - "@types/node": "^20.6.2", - "nyc": "^15.1.0", - "release-it": "^16.1.5", - "source-map-support": "^0.5.21", - "ts-node": "^10.9.1", - "typedoc": "^0.25.1", - "typescript": "^5.2.2" + "@redis/test-utils": "*" + }, + "engines": { + "node": ">= 18" }, "repository": { "type": "git", diff --git a/packages/json/.npmignore b/packages/json/.npmignore deleted file mode 100644 index bbef2b404fb..00000000000 --- a/packages/json/.npmignore +++ /dev/null @@ -1,6 +0,0 @@ -.nyc_output/ -coverage/ -lib/ -.nycrc.json -.release-it.json -tsconfig.json diff --git a/packages/json/.release-it.json b/packages/json/.release-it.json index ab495a49b13..8de2f3696e3 100644 --- a/packages/json/.release-it.json +++ b/packages/json/.release-it.json @@ -5,6 +5,7 @@ "tagAnnotation": "Release ${tagName}" }, "npm": { + "versionArgs": ["--workspaces-update=false"], "publishArgs": ["--access", "public"] } } diff --git a/packages/json/README.md b/packages/json/README.md index e7f70174116..86996a68370 100644 --- a/packages/json/README.md +++ b/packages/json/README.md @@ -1,12 +1,14 @@ # @redis/json -This package provides support for the [RedisJSON](https://redis.io/docs/stack/json/) 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. +This package provides support for the [RedisJSON](https://redis.io/docs/data-types/json/) module, which adds JSON as a native data type to Redis. -To use these extra commands, your Redis server must have the RedisJSON module installed. +Should be used with [`redis`/`@redis/client`](https://github.com/redis/node-redis). + +:warning: 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. +For a complete example, see [`managing-json.js`](https://github.com/redis/node-redis/blob/master/examples/managing-json.js) in the [examples folder](https://github.com/redis/node-redis/tree/master/examples). ### Storing JSON Documents in Redis @@ -15,33 +17,27 @@ The [`JSON.SET`](https://redis.io/commands/json.set/) command stores a JSON valu 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: [ - { + 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://redis.io/docs/stack/json/path/). +For more information about RedisJSON's path syntax, [check out the documentation](https://redis.io/docs/data-types/json/path/). ### Retrieving JSON Documents from Redis -With RedisJSON, we can retrieve all or part(s) of a JSON document using the [`JSON.GET`](https://redis.io/commands/json.get/) command and one or more JSON Paths. Let's get the name and age of one of the pets: +With RedisJSON, we can retrieve all or part(s) of a JSON document using the [`JSON.GET`](https://redis.io/commands/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', { diff --git a/packages/json/lib/commands/ARRAPPEND.spec.ts b/packages/json/lib/commands/ARRAPPEND.spec.ts index ab53837a000..3bdd967e237 100644 --- a/packages/json/lib/commands/ARRAPPEND.spec.ts +++ b/packages/json/lib/commands/ARRAPPEND.spec.ts @@ -1,30 +1,30 @@ -import { strict as assert } from 'assert'; +import { strict as assert } from 'node:assert'; import testUtils, { GLOBAL } from '../test-utils'; -import { transformArguments } from './ARRAPPEND'; +import ARRAPPEND from './ARRAPPEND'; -describe('ARRAPPEND', () => { - describe('transformArguments', () => { - it('single JSON', () => { - assert.deepEqual( - transformArguments('key', '$', 1), - ['JSON.ARRAPPEND', 'key', '$', '1'] - ); - }); +describe('JSON.ARRAPPEND', () => { + describe('transformArguments', () => { + it('single element', () => { + assert.deepEqual( + ARRAPPEND.transformArguments('key', '$', 'value'), + ['JSON.ARRAPPEND', 'key', '$', '"value"'] + ); + }); - it('multiple JSONs', () => { - assert.deepEqual( - transformArguments('key', '$', 1, 2), - ['JSON.ARRAPPEND', 'key', '$', '1', '2'] - ); - }); + it('multiple elements', () => { + assert.deepEqual( + ARRAPPEND.transformArguments('key', '$', 1, 2), + ['JSON.ARRAPPEND', 'key', '$', '1', '2'] + ); }); + }); - testUtils.testWithClient('client.json.arrAppend', async client => { - await client.json.set('key', '$', []); + testUtils.testWithClient('client.json.arrAppend', async client => { + const [, reply] = await Promise.all([ + client.json.set('key', '$', []), + client.json.arrAppend('key', '$', 'value') + ]); - assert.deepEqual( - await client.json.arrAppend('key', '$', 1), - [1] - ); - }, GLOBAL.SERVERS.OPEN); + assert.deepEqual(reply, [1]); + }, GLOBAL.SERVERS.OPEN); }); diff --git a/packages/json/lib/commands/ARRAPPEND.ts b/packages/json/lib/commands/ARRAPPEND.ts index 2935d192996..6f486a301d7 100644 --- a/packages/json/lib/commands/ARRAPPEND.ts +++ b/packages/json/lib/commands/ARRAPPEND.ts @@ -1,15 +1,27 @@ import { RedisJSON, transformRedisJsonArgument } from '.'; +import { RedisArgument, NumberReply, ArrayReply, NullReply, Command } from '@redis/client/dist/lib/RESP/types'; -export const FIRST_KEY_INDEX = 1; +export default { + FIRST_KEY_INDEX: 1, + IS_READ_ONLY: false, + transformArguments( + key: RedisArgument, + path: RedisArgument, + json: RedisJSON, + ...jsons: Array + ) { + const args = new Array(4 + jsons.length); + args[0] = 'JSON.ARRAPPEND'; + args[1] = key; + args[2] = path; + args[3] = transformRedisJsonArgument(json); -export function transformArguments(key: string, path: string, ...jsons: Array): Array { - const args = ['JSON.ARRAPPEND', key, path]; - - for (const json of jsons) { - args.push(transformRedisJsonArgument(json)); + let argsIndex = 4; + for (let i = 0; i < jsons.length; i++) { + args[argsIndex++] = transformRedisJsonArgument(jsons[i]); } return args; -} - -export declare function transformReply(): number | Array; + }, + transformReply: undefined as unknown as () => NumberReply | ArrayReply +} as const satisfies Command; diff --git a/packages/json/lib/commands/ARRINDEX.spec.ts b/packages/json/lib/commands/ARRINDEX.spec.ts index 7a47d67126a..cb946b62515 100644 --- a/packages/json/lib/commands/ARRINDEX.spec.ts +++ b/packages/json/lib/commands/ARRINDEX.spec.ts @@ -1,37 +1,48 @@ -import { strict as assert } from 'assert'; +import { strict as assert } from 'node:assert'; import testUtils, { GLOBAL } from '../test-utils'; -import { transformArguments } from './ARRINDEX'; +import ARRINDEX from './ARRINDEX'; -describe('ARRINDEX', () => { - describe('transformArguments', () => { - it('simple', () => { - assert.deepEqual( - transformArguments('key', '$', 'json'), - ['JSON.ARRINDEX', 'key', '$', '"json"'] - ); - }); - - it('with start', () => { - assert.deepEqual( - transformArguments('key', '$', 'json', 1), - ['JSON.ARRINDEX', 'key', '$', '"json"', '1'] - ); - }); - - it('with start, end', () => { - assert.deepEqual( - transformArguments('key', '$', 'json', 1, 2), - ['JSON.ARRINDEX', 'key', '$', '"json"', '1', '2'] - ); - }); +describe('JSON.ARRINDEX', () => { + describe('transformArguments', () => { + it('simple', () => { + assert.deepEqual( + ARRINDEX.transformArguments('key', '$', 'value'), + ['JSON.ARRINDEX', 'key', '$', '"value"'] + ); }); - testUtils.testWithClient('client.json.arrIndex', async client => { - await client.json.set('key', '$', []); + describe('with range', () => { + it('start only', () => { + assert.deepEqual( + ARRINDEX.transformArguments('key', '$', 'value', { + range: { + start: 0 + } + }), + ['JSON.ARRINDEX', 'key', '$', '"value"', '0'] + ); + }); + it('with start and stop', () => { assert.deepEqual( - await client.json.arrIndex('key', '$', 'json'), - [-1] + ARRINDEX.transformArguments('key', '$', 'value', { + range: { + start: 0, + stop: 1 + } + }), + ['JSON.ARRINDEX', 'key', '$', '"value"', '0', '1'] ); - }, GLOBAL.SERVERS.OPEN); + }); + }); + }); + + testUtils.testWithClient('client.json.arrIndex', async client => { + const [, reply] = await Promise.all([ + client.json.set('key', '$', []), + client.json.arrIndex('key', '$', 'value') + ]); + + assert.deepEqual(reply, [-1]); + }, GLOBAL.SERVERS.OPEN); }); diff --git a/packages/json/lib/commands/ARRINDEX.ts b/packages/json/lib/commands/ARRINDEX.ts index 5860b59cb3c..77c54b92522 100644 --- a/packages/json/lib/commands/ARRINDEX.ts +++ b/packages/json/lib/commands/ARRINDEX.ts @@ -1,21 +1,33 @@ +import { RedisArgument, NumberReply, ArrayReply, NullReply, Command } from '@redis/client/dist/lib/RESP/types'; import { RedisJSON, transformRedisJsonArgument } from '.'; -export const FIRST_KEY_INDEX = 1; - -export const IS_READ_ONLY = true; +export interface JsonArrIndexOptions { + range?: { + start: number; + stop?: number; + }; +} -export function transformArguments(key: string, path: string, json: RedisJSON, start?: number, stop?: number): Array { +export default { + FIRST_KEY_INDEX: 1, + IS_READ_ONLY: true, + transformArguments( + key: RedisArgument, + path: RedisArgument, + json: RedisJSON, + options?: JsonArrIndexOptions + ) { const args = ['JSON.ARRINDEX', key, path, transformRedisJsonArgument(json)]; - if (start !== undefined && start !== null) { - args.push(start.toString()); + if (options?.range) { + args.push(options.range.start.toString()); - if (stop !== undefined && stop !== null) { - args.push(stop.toString()); - } + if (options.range.stop !== undefined) { + args.push(options.range.stop.toString()); + } } return args; -} - -export declare function transformReply(): number | Array; + }, + transformReply: undefined as unknown as () => NumberReply | ArrayReply +} as const satisfies Command; diff --git a/packages/json/lib/commands/ARRINSERT.spec.ts b/packages/json/lib/commands/ARRINSERT.spec.ts index 4b9d58b2cac..efa824b3738 100644 --- a/packages/json/lib/commands/ARRINSERT.spec.ts +++ b/packages/json/lib/commands/ARRINSERT.spec.ts @@ -1,30 +1,30 @@ -import { strict as assert } from 'assert'; +import { strict as assert } from 'node:assert'; import testUtils, { GLOBAL } from '../test-utils'; -import { transformArguments } from './ARRINSERT'; +import ARRINSERT from './ARRINSERT'; -describe('ARRINSERT', () => { - describe('transformArguments', () => { - it('single JSON', () => { - assert.deepEqual( - transformArguments('key', '$', 0, 'json'), - ['JSON.ARRINSERT', 'key', '$', '0', '"json"'] - ); - }); +describe('JSON.ARRINSERT', () => { + describe('transformArguments', () => { + it('single element', () => { + assert.deepEqual( + ARRINSERT.transformArguments('key', '$', 0, 'value'), + ['JSON.ARRINSERT', 'key', '$', '0', '"value"'] + ); + }); - it('multiple JSONs', () => { - assert.deepEqual( - transformArguments('key', '$', 0, '1', '2'), - ['JSON.ARRINSERT', 'key', '$', '0', '"1"', '"2"'] - ); - }); + it('multiple elements', () => { + assert.deepEqual( + ARRINSERT.transformArguments('key', '$', 0, '1', '2'), + ['JSON.ARRINSERT', 'key', '$', '0', '"1"', '"2"'] + ); }); + }); - testUtils.testWithClient('client.json.arrInsert', async client => { - await client.json.set('key', '$', []); + testUtils.testWithClient('client.json.arrInsert', async client => { + const [, reply] = await Promise.all([ + client.json.set('key', '$', []), + client.json.arrInsert('key', '$', 0, 'value') + ]); - assert.deepEqual( - await client.json.arrInsert('key', '$', 0, 'json'), - [1] - ); - }, GLOBAL.SERVERS.OPEN); + assert.deepEqual(reply, [1]); + }, GLOBAL.SERVERS.OPEN); }); diff --git a/packages/json/lib/commands/ARRINSERT.ts b/packages/json/lib/commands/ARRINSERT.ts index 85857657019..c0891884725 100644 --- a/packages/json/lib/commands/ARRINSERT.ts +++ b/packages/json/lib/commands/ARRINSERT.ts @@ -1,15 +1,29 @@ +import { RedisArgument, NumberReply, ArrayReply, NullReply, Command } from '@redis/client/dist/lib/RESP/types'; import { RedisJSON, transformRedisJsonArgument } from '.'; -export const FIRST_KEY_INDEX = 1; +export default { + FIRST_KEY_INDEX: 1, + IS_READ_ONLY: false, + transformArguments( + key: RedisArgument, + path: RedisArgument, + index: number, + json: RedisJSON, + ...jsons: Array + ) { + const args = new Array(4 + jsons.length); + args[0] = 'JSON.ARRINSERT'; + args[1] = key; + args[2] = path; + args[3] = index.toString(); + args[4] = transformRedisJsonArgument(json); -export function transformArguments(key: string, path: string, index: number, ...jsons: Array): Array { - const args = ['JSON.ARRINSERT', key, path, index.toString()]; - - for (const json of jsons) { - args.push(transformRedisJsonArgument(json)); + let argsIndex = 5; + for (let i = 0; i < jsons.length; i++) { + args[argsIndex++] = transformRedisJsonArgument(jsons[i]); } return args; -} - -export declare function transformReply(): number | Array; + }, + transformReply: undefined as unknown as () => NumberReply | ArrayReply +} as const satisfies Command; diff --git a/packages/json/lib/commands/ARRLEN.spec.ts b/packages/json/lib/commands/ARRLEN.spec.ts index f0a3ec40a4c..5ecb01b2ce5 100644 --- a/packages/json/lib/commands/ARRLEN.spec.ts +++ b/packages/json/lib/commands/ARRLEN.spec.ts @@ -1,30 +1,32 @@ -import { strict as assert } from 'assert'; +import { strict as assert } from 'node:assert'; import testUtils, { GLOBAL } from '../test-utils'; -import { transformArguments } from './ARRLEN'; +import ARRLEN from './ARRLEN'; -describe('ARRLEN', () => { - describe('transformArguments', () => { - it('without path', () => { - assert.deepEqual( - transformArguments('key'), - ['JSON.ARRLEN', 'key'] - ); - }); +describe('JSON.ARRLEN', () => { + describe('transformArguments', () => { + it('simple', () => { + assert.deepEqual( + ARRLEN.transformArguments('key'), + ['JSON.ARRLEN', 'key'] + ); + }); - it('with path', () => { - assert.deepEqual( - transformArguments('key', '$'), - ['JSON.ARRLEN', 'key', '$'] - ); - }); + it('with path', () => { + assert.deepEqual( + ARRLEN.transformArguments('key', { + path: '$' + }), + ['JSON.ARRLEN', 'key', '$'] + ); }); + }); - testUtils.testWithClient('client.json.arrLen', async client => { - await client.json.set('key', '$', []); + testUtils.testWithClient('client.json.arrLen', async client => { + const [, reply] = await Promise.all([ + client.json.set('key', '$', []), + client.json.arrLen('key') + ]); - assert.deepEqual( - await client.json.arrLen('key', '$'), - [0] - ); - }, GLOBAL.SERVERS.OPEN); + assert.equal(reply, 0); + }, GLOBAL.SERVERS.OPEN); }); diff --git a/packages/json/lib/commands/ARRLEN.ts b/packages/json/lib/commands/ARRLEN.ts index 818397b7f8d..d30032c7d86 100644 --- a/packages/json/lib/commands/ARRLEN.ts +++ b/packages/json/lib/commands/ARRLEN.ts @@ -1,15 +1,20 @@ -export const FIRST_KEY_INDEX = 1; +import { RedisArgument, ArrayReply, NumberReply, NullReply, Command } from '@redis/client/dist/lib/RESP/types'; -export const IS_READ_ONLY = true; +export interface JsonArrLenOptions { + path?: RedisArgument; +} -export function transformArguments(key: string, path?: string): Array { +export default { + FIRST_KEY_INDEX: 1, + IS_READ_ONLY: true, + transformArguments(key: RedisArgument, options?: JsonArrLenOptions) { const args = ['JSON.ARRLEN', key]; - if (path) { - args.push(path); + if (options?.path !== undefined) { + args.push(options.path); } return args; -} - -export declare function transformReply(): number | Array; + }, + transformReply: undefined as unknown as () => NumberReply | ArrayReply +} as const satisfies Command; diff --git a/packages/json/lib/commands/ARRPOP.spec.ts b/packages/json/lib/commands/ARRPOP.spec.ts index 7c2ec365eb6..1b069ba3928 100644 --- a/packages/json/lib/commands/ARRPOP.spec.ts +++ b/packages/json/lib/commands/ARRPOP.spec.ts @@ -1,57 +1,66 @@ -import { strict as assert } from 'assert'; +import { strict as assert } from 'node:assert'; import testUtils, { GLOBAL } from '../test-utils'; -import { transformArguments } from './ARRPOP'; - -describe('ARRPOP', () => { - describe('transformArguments', () => { - it('key', () => { - assert.deepEqual( - transformArguments('key'), - ['JSON.ARRPOP', 'key'] - ); - }); - - it('key, path', () => { - assert.deepEqual( - transformArguments('key', '$'), - ['JSON.ARRPOP', 'key', '$'] - ); - }); - - it('key, path, index', () => { - assert.deepEqual( - transformArguments('key', '$', 0), - ['JSON.ARRPOP', 'key', '$', '0'] - ); - }); +import ARRPOP from './ARRPOP'; + +describe('JSON.ARRPOP', () => { + describe('transformArguments', () => { + it('simple', () => { + assert.deepEqual( + ARRPOP.transformArguments('key'), + ['JSON.ARRPOP', 'key'] + ); + }); + + it('with path', () => { + assert.deepEqual( + ARRPOP.transformArguments('key', { + path: '$' + }), + ['JSON.ARRPOP', 'key', '$'] + ); }); - describe('client.json.arrPop', () => { - testUtils.testWithClient('null', async client => { - await client.json.set('key', '.', []); - - assert.equal( - await client.json.arrPop('key', '.'), - null - ); - }, GLOBAL.SERVERS.OPEN); - - testUtils.testWithClient('with value', async client => { - await client.json.set('key', '.', ['value']); - - assert.equal( - await client.json.arrPop('key', '.'), - 'value' - ); - }, GLOBAL.SERVERS.OPEN); - - testUtils.testWithClient('array', async client => { - await client.json.set('key', '$', ['value']); - - assert.deepEqual( - await client.json.arrPop('key', '$'), - ['value'] - ); - }, GLOBAL.SERVERS.OPEN); + it('with path and index', () => { + assert.deepEqual( + ARRPOP.transformArguments('key', { + path: '$', + index: 0 + }), + ['JSON.ARRPOP', 'key', '$', '0'] + ); }); + }); + + describe('client.json.arrPop', () => { + testUtils.testWithClient('without path and value', async client => { + const [, reply] = await Promise.all([ + client.json.set('key', '$', []), + client.json.arrPop('key') + ]); + + assert.equal(reply, null); + }, GLOBAL.SERVERS.OPEN); + + testUtils.testWithClient('. path with value', async client => { + const [, reply] = await Promise.all([ + client.json.set('key', '.', ['value']), + client.json.arrPop('key', { + path: '.' + }) + ]); + + assert.equal(reply, 'value'); + }, GLOBAL.SERVERS.OPEN); + + testUtils.testWithClient('$ path with value', async client => { + const [, reply] = await Promise.all([ + client.json.set('key', '$', ['value']), + client.json.arrPop('key', { + path: '$' + }) + ]); + + assert.deepEqual(reply, ['value']); + }, GLOBAL.SERVERS.OPEN); + }); }); diff --git a/packages/json/lib/commands/ARRPOP.ts b/packages/json/lib/commands/ARRPOP.ts index 18830c0d314..4eafe9fdde4 100644 --- a/packages/json/lib/commands/ARRPOP.ts +++ b/packages/json/lib/commands/ARRPOP.ts @@ -1,27 +1,32 @@ -import { RedisJSON, transformRedisJsonNullReply } from '.'; +import { RedisArgument, ArrayReply, NullReply, BlobStringReply, Command, UnwrapReply } from '@redis/client/dist/lib/RESP/types'; +import { isArrayReply } from '@redis/client/dist/lib/commands/generic-transformers'; +import { transformRedisJsonNullReply } from '.'; -export const FIRST_KEY_INDEX = 1; +export interface RedisArrPopOptions { + path: RedisArgument; + index?: number; +} -export function transformArguments(key: string, path?: string, index?: number): Array { +export default { + FIRST_KEY_INDEX: 1, + IS_READ_ONLY: false, + transformArguments(key: RedisArgument, options?: RedisArrPopOptions) { const args = ['JSON.ARRPOP', key]; - if (path) { - args.push(path); + if (options) { + args.push(options.path); - if (index !== undefined && index !== null) { - args.push(index.toString()); - } + if (options.index !== undefined) { + args.push(options.index.toString()); + } } - + return args; -} - -export function transformReply(reply: null | string | Array): null | RedisJSON | Array { - if (reply === null) return null; + }, + transformReply(reply: NullReply | BlobStringReply | ArrayReply) { + return isArrayReply(reply) ? + (reply as unknown as UnwrapReply).map(item => transformRedisJsonNullReply(item)) : + transformRedisJsonNullReply(reply); + } +} as const satisfies Command; - if (Array.isArray(reply)) { - return reply.map(transformRedisJsonNullReply); - } - - return transformRedisJsonNullReply(reply); -} diff --git a/packages/json/lib/commands/ARRTRIM.spec.ts b/packages/json/lib/commands/ARRTRIM.spec.ts index c254e1b6a03..4c2f72aaa50 100644 --- a/packages/json/lib/commands/ARRTRIM.spec.ts +++ b/packages/json/lib/commands/ARRTRIM.spec.ts @@ -1,21 +1,21 @@ -import { strict as assert } from 'assert'; +import { strict as assert } from 'node:assert'; import testUtils, { GLOBAL } from '../test-utils'; -import { transformArguments } from './ARRTRIM'; +import ARRTRIM from './ARRTRIM'; -describe('ARRTRIM', () => { - it('transformArguments', () => { - assert.deepEqual( - transformArguments('key', '$', 0, 1), - ['JSON.ARRTRIM', 'key', '$', '0', '1'] - ); - }); +describe('JSON.ARRTRIM', () => { + it('transformArguments', () => { + assert.deepEqual( + ARRTRIM.transformArguments('key', '$', 0, 1), + ['JSON.ARRTRIM', 'key', '$', '0', '1'] + ); + }); - testUtils.testWithClient('client.json.arrTrim', async client => { - await client.json.set('key', '$', []); + testUtils.testWithClient('client.json.arrTrim', async client => { + const [, reply] = await Promise.all([ + client.json.set('key', '$', []), + client.json.arrTrim('key', '$', 0, 1) + ]); - assert.deepEqual( - await client.json.arrTrim('key', '$', 0, 1), - [0] - ); - }, GLOBAL.SERVERS.OPEN); + assert.deepEqual(reply, [0]); + }, GLOBAL.SERVERS.OPEN); }); diff --git a/packages/json/lib/commands/ARRTRIM.ts b/packages/json/lib/commands/ARRTRIM.ts index 2de444eeebd..ab31f159491 100644 --- a/packages/json/lib/commands/ARRTRIM.ts +++ b/packages/json/lib/commands/ARRTRIM.ts @@ -1,7 +1,10 @@ -export const FIRST_KEY_INDEX = 1; +import { RedisArgument, ArrayReply, NumberReply, NullReply, Command } from '@redis/client/dist/lib/RESP/types'; -export function transformArguments(key: string, path: string, start: number, stop: number): Array { +export default { + FIRST_KEY_INDEX: 1, + IS_READ_ONLY: false, + transformArguments(key: RedisArgument, path: RedisArgument, start: number, stop: number) { return ['JSON.ARRTRIM', key, path, start.toString(), stop.toString()]; -} - -export declare function transformReply(): number | Array; + }, + transformReply: undefined as unknown as () => NumberReply | ArrayReply +} as const satisfies Command; diff --git a/packages/json/lib/commands/CLEAR.spec.ts b/packages/json/lib/commands/CLEAR.spec.ts new file mode 100644 index 00000000000..983e6bec2d9 --- /dev/null +++ b/packages/json/lib/commands/CLEAR.spec.ts @@ -0,0 +1,32 @@ +import { strict as assert } from 'node:assert'; +import testUtils, { GLOBAL } from '../test-utils'; +import CLEAR from './CLEAR'; + +describe('JSON.CLEAR', () => { + describe('transformArguments', () => { + it('simple', () => { + assert.deepEqual( + CLEAR.transformArguments('key'), + ['JSON.CLEAR', 'key'] + ); + }); + + it('with path', () => { + assert.deepEqual( + CLEAR.transformArguments('key', { + path: '$' + }), + ['JSON.CLEAR', 'key', '$'] + ); + }); + }); + + testUtils.testWithClient('client.json.clear', async client => { + const [, reply] = await Promise.all([ + client.json.set('key', '$', null), + client.json.clear('key') + ]); + + assert.equal(reply, 0); + }, GLOBAL.SERVERS.OPEN); +}); diff --git a/packages/json/lib/commands/CLEAR.ts b/packages/json/lib/commands/CLEAR.ts new file mode 100644 index 00000000000..23e86d900e7 --- /dev/null +++ b/packages/json/lib/commands/CLEAR.ts @@ -0,0 +1,20 @@ +import { RedisArgument, NumberReply, Command } from '@redis/client/dist/lib/RESP/types'; + +export interface JsonClearOptions { + path?: RedisArgument; +} + +export default { + FIRST_KEY_INDEX: 1, + IS_READ_ONLY: false, + transformArguments(key: RedisArgument, options?: JsonClearOptions) { + const args = ['JSON.CLEAR', key]; + + if (options?.path !== undefined) { + args.push(options.path); + } + + return args; + }, + transformReply: undefined as unknown as () => NumberReply +} as const satisfies Command; diff --git a/packages/json/lib/commands/DEBUG_MEMORY.spec.ts b/packages/json/lib/commands/DEBUG_MEMORY.spec.ts index 468c994f2f5..c41d07cb27e 100644 --- a/packages/json/lib/commands/DEBUG_MEMORY.spec.ts +++ b/packages/json/lib/commands/DEBUG_MEMORY.spec.ts @@ -1,28 +1,30 @@ -import { strict as assert } from 'assert'; +import { strict as assert } from 'node:assert'; import testUtils, { GLOBAL } from '../test-utils'; -import { transformArguments } from './DEBUG_MEMORY'; +import DEBUG_MEMORY from './DEBUG_MEMORY'; -describe('DEBUG MEMORY', () => { - describe('transformArguments', () => { - it('without path', () => { - assert.deepEqual( - transformArguments('key'), - ['JSON.DEBUG', 'MEMORY', 'key'] - ); - }); +describe('JSON.DEBUG MEMORY', () => { + describe('transformArguments', () => { + it('simple', () => { + assert.deepEqual( + DEBUG_MEMORY.transformArguments('key'), + ['JSON.DEBUG', 'MEMORY', 'key'] + ); + }); - it('with path', () => { - assert.deepEqual( - transformArguments('key', '$'), - ['JSON.DEBUG', 'MEMORY', 'key', '$'] - ); - }); + it('with path', () => { + assert.deepEqual( + DEBUG_MEMORY.transformArguments('key', { + path: '$' + }), + ['JSON.DEBUG', 'MEMORY', 'key', '$'] + ); }); + }); - testUtils.testWithClient('client.json.arrTrim', async client => { - assert.deepEqual( - await client.json.debugMemory('key', '$'), - [] - ); - }, GLOBAL.SERVERS.OPEN); + testUtils.testWithClient('client.json.debugMemory', async client => { + assert.equal( + await client.json.debugMemory('key'), + 0 + ); + }, GLOBAL.SERVERS.OPEN); }); diff --git a/packages/json/lib/commands/DEBUG_MEMORY.ts b/packages/json/lib/commands/DEBUG_MEMORY.ts index da60b1d9529..c2e730b9dc2 100644 --- a/packages/json/lib/commands/DEBUG_MEMORY.ts +++ b/packages/json/lib/commands/DEBUG_MEMORY.ts @@ -1,13 +1,20 @@ -export const FIRST_KEY_INDEX = 2; +import { RedisArgument, NumberReply, Command } from '@redis/client/dist/lib/RESP/types'; -export function transformArguments(key: string, path?: string): Array { +export interface JsonDebugMemoryOptions { + path?: RedisArgument; +} + +export default { + FIRST_KEY_INDEX: 2, + IS_READ_ONLY: false, + transformArguments(key: RedisArgument, options?: JsonDebugMemoryOptions) { const args = ['JSON.DEBUG', 'MEMORY', key]; - if (path) { - args.push(path); + if (options?.path !== undefined) { + args.push(options.path); } return args; -} - -export declare function transformReply(): number; + }, + transformReply: undefined as unknown as () => NumberReply +} as const satisfies Command; diff --git a/packages/json/lib/commands/DEL.spec.ts b/packages/json/lib/commands/DEL.spec.ts index a957b9584ac..18f6a8f2db6 100644 --- a/packages/json/lib/commands/DEL.spec.ts +++ b/packages/json/lib/commands/DEL.spec.ts @@ -1,28 +1,31 @@ -import { strict as assert } from 'assert'; +import { strict as assert } from 'node:assert'; import testUtils, { GLOBAL } from '../test-utils'; -import { transformArguments } from './DEL'; +import DEL from './DEL'; -describe('DEL', () => { - describe('transformArguments', () => { - it('key', () => { - assert.deepEqual( - transformArguments('key'), - ['JSON.DEL', 'key'] - ); - }); +describe('JSON.DEL', () => { + describe('transformArguments', () => { + it('simple', () => { + assert.deepEqual( + DEL.transformArguments('key'), + ['JSON.DEL', 'key'] + ); + }); - it('key, path', () => { - assert.deepEqual( - transformArguments('key', '$.path'), - ['JSON.DEL', 'key', '$.path'] - ); - }); + it('with path', () => { + assert.deepEqual( + DEL.transformArguments('key', { + path: '$.path' + }), + ['JSON.DEL', 'key', '$.path'] + ); }); + }); - testUtils.testWithClient('client.json.del', async client => { - assert.deepEqual( - await client.json.del('key'), - 0 - ); - }, GLOBAL.SERVERS.OPEN); + testUtils.testWithClient('client.json.del', async client => { + assert.equal( + await client.json.del('key'), + 0 + ); + }, GLOBAL.SERVERS.OPEN); }); + diff --git a/packages/json/lib/commands/DEL.ts b/packages/json/lib/commands/DEL.ts index 090d4dbe853..f6952a8dc63 100644 --- a/packages/json/lib/commands/DEL.ts +++ b/packages/json/lib/commands/DEL.ts @@ -1,13 +1,20 @@ -export const FIRST_KEY_INDEX = 1; +import { RedisArgument, NumberReply, Command } from '@redis/client/dist/lib/RESP/types'; -export function transformArguments(key: string, path?: string): Array { +export interface JsonDelOptions { + path?: RedisArgument +} + +export default { + FIRST_KEY_INDEX: 1, + IS_READ_ONLY: false, + transformArguments(key: RedisArgument, options?: JsonDelOptions) { const args = ['JSON.DEL', key]; - if (path) { - args.push(path); + if (options?.path !== undefined) { + args.push(options.path); } return args; -} - -export declare function transformReply(): number; + }, + transformReply: undefined as unknown as () => NumberReply +} as const satisfies Command; diff --git a/packages/json/lib/commands/FORGET.spec.ts b/packages/json/lib/commands/FORGET.spec.ts index 923bb997fc8..04066ec43a9 100644 --- a/packages/json/lib/commands/FORGET.spec.ts +++ b/packages/json/lib/commands/FORGET.spec.ts @@ -1,28 +1,30 @@ -import { strict as assert } from 'assert'; +import { strict as assert } from 'node:assert'; import testUtils, { GLOBAL } from '../test-utils'; -import { transformArguments } from './FORGET'; +import FORGET from './FORGET'; -describe('FORGET', () => { - describe('transformArguments', () => { - it('key', () => { - assert.deepEqual( - transformArguments('key'), - ['JSON.FORGET', 'key'] - ); - }); +describe('JSON.FORGET', () => { + describe('transformArguments', () => { + it('key', () => { + assert.deepEqual( + FORGET.transformArguments('key'), + ['JSON.FORGET', 'key'] + ); + }); - it('key, path', () => { - assert.deepEqual( - transformArguments('key', '$.path'), - ['JSON.FORGET', 'key', '$.path'] - ); - }); + it('key, path', () => { + assert.deepEqual( + FORGET.transformArguments('key', { + path: '$.path' + }), + ['JSON.FORGET', 'key', '$.path'] + ); }); + }); - testUtils.testWithClient('client.json.forget', async client => { - assert.deepEqual( - await client.json.forget('key'), - 0 - ); - }, GLOBAL.SERVERS.OPEN); + testUtils.testWithClient('client.json.forget', async client => { + assert.equal( + await client.json.forget('key'), + 0 + ); + }, GLOBAL.SERVERS.OPEN); }); diff --git a/packages/json/lib/commands/FORGET.ts b/packages/json/lib/commands/FORGET.ts index cb2df3d605d..68335ee92e9 100644 --- a/packages/json/lib/commands/FORGET.ts +++ b/packages/json/lib/commands/FORGET.ts @@ -1,13 +1,20 @@ -export const FIRST_KEY_INDEX = 1; +import { RedisArgument, NumberReply, Command } from '@redis/client/dist/lib/RESP/types'; -export function transformArguments(key: string, path?: string): Array { +export interface JsonForgetOptions { + path?: RedisArgument; +} + +export default { + FIRST_KEY_INDEX: 1, + IS_READ_ONLY: false, + transformArguments(key: RedisArgument, options?: JsonForgetOptions) { const args = ['JSON.FORGET', key]; - if (path) { - args.push(path); + if (options?.path !== undefined) { + args.push(options.path); } return args; -} - -export declare function transformReply(): number; + }, + transformReply: undefined as unknown as () => NumberReply +} as const satisfies Command; diff --git a/packages/json/lib/commands/GET.spec.ts b/packages/json/lib/commands/GET.spec.ts index ed831689a93..6d6ff14f67f 100644 --- a/packages/json/lib/commands/GET.spec.ts +++ b/packages/json/lib/commands/GET.spec.ts @@ -1,78 +1,37 @@ -import { strict as assert } from 'assert'; +import { strict as assert } from 'node:assert'; import testUtils, { GLOBAL } from '../test-utils'; -import { transformArguments } from './GET'; - -describe('GET', () => { - describe('transformArguments', () => { - describe('path', () => { - it('string', () => { - assert.deepEqual( - transformArguments('key', { path: '$' }), - ['JSON.GET', 'key', '$'] - ); - }); - - it('array', () => { - assert.deepEqual( - transformArguments('key', { path: ['$.1', '$.2'] }), - ['JSON.GET', 'key', '$.1', '$.2'] - ); - }); - }); - - it('key', () => { - assert.deepEqual( - transformArguments('key'), - ['JSON.GET', 'key'] - ); - }); - - it('INDENT', () => { - assert.deepEqual( - transformArguments('key', { INDENT: 'indent' }), - ['JSON.GET', 'key', 'INDENT', 'indent'] - ); - }); - - it('NEWLINE', () => { - assert.deepEqual( - transformArguments('key', { NEWLINE: 'newline' }), - ['JSON.GET', 'key', 'NEWLINE', 'newline'] - ); - }); - - it('SPACE', () => { - assert.deepEqual( - transformArguments('key', { SPACE: 'space' }), - ['JSON.GET', 'key', 'SPACE', 'space'] - ); - }); - - it('NOESCAPE', () => { - assert.deepEqual( - transformArguments('key', { NOESCAPE: true }), - ['JSON.GET', 'key', 'NOESCAPE'] - ); - }); - - it('INDENT, NEWLINE, SPACE, NOESCAPE, path', () => { - assert.deepEqual( - transformArguments('key', { - path: '$.path', - INDENT: 'indent', - NEWLINE: 'newline', - SPACE: 'space', - NOESCAPE: true - }), - ['JSON.GET', 'key', '$.path', 'INDENT', 'indent', 'NEWLINE', 'newline', 'SPACE', 'space', 'NOESCAPE'] - ); - }); +import GET from './GET'; + +describe('JSON.GET', () => { + describe('transformArguments', () => { + it('simple', () => { + assert.deepEqual( + GET.transformArguments('key'), + ['JSON.GET', 'key'] + ); }); - testUtils.testWithClient('client.json.get', async client => { - assert.equal( - await client.json.get('key'), - null + describe('with path', () => { + it('string', () => { + assert.deepEqual( + GET.transformArguments('key', { path: '$' }), + ['JSON.GET', 'key', '$'] ); - }, GLOBAL.SERVERS.OPEN); + }); + + it('array', () => { + assert.deepEqual( + GET.transformArguments('key', { path: ['$.1', '$.2'] }), + ['JSON.GET', 'key', '$.1', '$.2'] + ); + }); + }); + }); + + testUtils.testWithClient('client.json.get', async client => { + assert.equal( + await client.json.get('key'), + null + ); + }, GLOBAL.SERVERS.OPEN); }); diff --git a/packages/json/lib/commands/GET.ts b/packages/json/lib/commands/GET.ts index 21bad09568b..b7bcc52e3cb 100644 --- a/packages/json/lib/commands/GET.ts +++ b/packages/json/lib/commands/GET.ts @@ -1,42 +1,22 @@ -import { pushVerdictArguments } from '@redis/client/dist/lib/commands/generic-transformers'; -import { RedisCommandArguments } from '@redis/client/dist/lib/commands'; +import { RedisArgument, Command } from '@redis/client/dist/lib/RESP/types'; +import { RedisVariadicArgument, pushVariadicArguments } from '@redis/client/dist/lib/commands/generic-transformers'; +import { transformRedisJsonNullReply } from '.'; -export const FIRST_KEY_INDEX = 1; - -export const IS_READ_ONLY = true; - -interface GetOptions { - path?: string | Array; - INDENT?: string; - NEWLINE?: string; - SPACE?: string; - NOESCAPE?: true; +export interface JsonGetOptions { + path?: RedisVariadicArgument; } -export function transformArguments(key: string, options?: GetOptions): RedisCommandArguments { - let args: RedisCommandArguments = ['JSON.GET', key]; - - if (options?.path) { - args = pushVerdictArguments(args, options.path); - } - - if (options?.INDENT) { - args.push('INDENT', options.INDENT); - } - - if (options?.NEWLINE) { - args.push('NEWLINE', options.NEWLINE); - } - - if (options?.SPACE) { - args.push('SPACE', options.SPACE); - } +export default { + FIRST_KEY_INDEX: 1, + IS_READ_ONLY: false, + transformArguments(key: RedisArgument, options?: JsonGetOptions) { + let args = ['JSON.GET', key]; - if (options?.NOESCAPE) { - args.push('NOESCAPE'); + if (options?.path !== undefined) { + args = pushVariadicArguments(args, options.path); } return args; -} - -export { transformRedisJsonNullReply as transformReply } from '.'; + }, + transformReply: transformRedisJsonNullReply +} as const satisfies Command; diff --git a/packages/json/lib/commands/MERGE.spec.ts b/packages/json/lib/commands/MERGE.spec.ts index ee5e6fff86d..56f5d25e7d5 100644 --- a/packages/json/lib/commands/MERGE.spec.ts +++ b/packages/json/lib/commands/MERGE.spec.ts @@ -1,21 +1,19 @@ -import { strict as assert } from 'assert'; +import { strict as assert } from 'node:assert'; import testUtils, { GLOBAL } from '../test-utils'; -import { transformArguments } from './MERGE'; +import MERGE from './MERGE'; -describe('MERGE', () => { - testUtils.isVersionGreaterThanHook([2, 6]); +describe('JSON.MERGE', () => { + it('transformArguments', () => { + assert.deepEqual( + MERGE.transformArguments('key', '$', 'value'), + ['JSON.MERGE', 'key', '$', '"value"'] + ); + }); - it('transformArguments', () => { - assert.deepEqual( - transformArguments('key', '$', 1), - ['JSON.MERGE', 'key', '$', '1'] - ); - }); - - testUtils.testWithClient('client.json.merge', async client => { - assert.equal( - await client.json.merge('key', '$', 'json'), - 'OK' - ); - }, GLOBAL.SERVERS.OPEN); + testUtils.testWithClient('client.json.merge', async client => { + assert.equal( + await client.json.merge('key', '$', 'value'), + 'OK' + ); + }, GLOBAL.SERVERS.OPEN); }); diff --git a/packages/json/lib/commands/MERGE.ts b/packages/json/lib/commands/MERGE.ts index 81cce7f006b..90cd080a06e 100644 --- a/packages/json/lib/commands/MERGE.ts +++ b/packages/json/lib/commands/MERGE.ts @@ -1,9 +1,16 @@ +import { SimpleStringReply, Command, RedisArgument } from '@redis/client/dist/lib/RESP/types'; import { RedisJSON, transformRedisJsonArgument } from '.'; -export const FIRST_KEY_INDEX = 1; - -export function transformArguments(key: string, path: string, json: RedisJSON): Array { - return ['JSON.MERGE', key, path, transformRedisJsonArgument(json)]; -} - -export declare function transformReply(): 'OK'; +export default { + FIRST_KEY_INDEX: 1, + IS_READ_ONLY: false, + transformArguments(key: RedisArgument, path: RedisArgument, value: RedisJSON) { + return [ + 'JSON.MERGE', + key, + path, + transformRedisJsonArgument(value) + ]; + }, + transformReply: undefined as unknown as () => SimpleStringReply<'OK'> +} as const satisfies Command; diff --git a/packages/json/lib/commands/MGET.spec.ts b/packages/json/lib/commands/MGET.spec.ts index 456e160dd50..1bfaecd6da9 100644 --- a/packages/json/lib/commands/MGET.spec.ts +++ b/packages/json/lib/commands/MGET.spec.ts @@ -1,19 +1,19 @@ -import { strict as assert } from 'assert'; +import { strict as assert } from 'node:assert'; import testUtils, { GLOBAL } from '../test-utils'; -import { transformArguments } from './MGET'; +import MGET from './MGET'; -describe('MGET', () => { - it('transformArguments', () => { - assert.deepEqual( - transformArguments(['1', '2'], '$'), - ['JSON.MGET', '1', '2', '$'] - ); - }); +describe('JSON.MGET', () => { + it('transformArguments', () => { + assert.deepEqual( + MGET.transformArguments(['1', '2'], '$'), + ['JSON.MGET', '1', '2', '$'] + ); + }); - testUtils.testWithClient('client.json.mGet', async client => { - assert.deepEqual( - await client.json.mGet(['1', '2'], '$'), - [null, null] - ); - }, GLOBAL.SERVERS.OPEN); + testUtils.testWithClient('client.json.mGet', async client => { + assert.deepEqual( + await client.json.mGet(['1', '2'], '$'), + [null, null] + ); + }, GLOBAL.SERVERS.OPEN); }); diff --git a/packages/json/lib/commands/MGET.ts b/packages/json/lib/commands/MGET.ts index 34ca8da289f..a7aea82ac2e 100644 --- a/packages/json/lib/commands/MGET.ts +++ b/packages/json/lib/commands/MGET.ts @@ -1,17 +1,17 @@ -import { RedisJSON, transformRedisJsonNullReply } from '.'; +import { RedisArgument, UnwrapReply, ArrayReply, NullReply, BlobStringReply, Command } from '@redis/client/dist/lib/RESP/types'; +import { transformRedisJsonNullReply } from '.'; -export const FIRST_KEY_INDEX = 1; - -export const IS_READ_ONLY = true; - -export function transformArguments(keys: Array, path: string): Array { +export default { + FIRST_KEY_INDEX: 1, + IS_READ_ONLY: true, + transformArguments(keys: Array, path: RedisArgument) { return [ - 'JSON.MGET', - ...keys, - path + 'JSON.MGET', + ...keys, + path ]; -} - -export function transformReply(reply: Array): Array { - return reply.map(transformRedisJsonNullReply); -} + }, + transformReply(reply: UnwrapReply>) { + return reply.map(json => transformRedisJsonNullReply(json)) + } +} as const satisfies Command; diff --git a/packages/json/lib/commands/MSET.spec.ts b/packages/json/lib/commands/MSET.spec.ts index 53d4d822505..360890234c8 100644 --- a/packages/json/lib/commands/MSET.spec.ts +++ b/packages/json/lib/commands/MSET.spec.ts @@ -1,35 +1,35 @@ -import { strict as assert } from 'assert'; +import { strict as assert } from 'node:assert'; import testUtils, { GLOBAL } from '../test-utils'; -import { transformArguments } from './MSET'; +import MSET from './MSET'; -describe('MSET', () => { - it('transformArguments', () => { - assert.deepEqual( - transformArguments([{ - key: '1', - path: '$', - value: 1 - }, { - key: '2', - path: '$', - value: '2' - }]), - ['JSON.MSET', '1', '$', '1', '2', '$', '"2"'] - ); - }); +describe('JSON.MSET', () => { + it('transformArguments', () => { + assert.deepEqual( + MSET.transformArguments([{ + key: '1', + path: '$', + value: 1 + }, { + key: '2', + path: '$', + value: '2' + }]), + ['JSON.MSET', '1', '$', '1', '2', '$', '"2"'] + ); + }); - testUtils.testWithClient('client.json.mSet', async client => { - assert.deepEqual( - await client.json.mSet([{ - key: '1', - path: '$', - value: 1 - }, { - key: '2', - path: '$', - value: '2' - }]), - 'OK' - ); - }, GLOBAL.SERVERS.OPEN); + testUtils.testWithClient('client.json.mSet', async client => { + assert.equal( + await client.json.mSet([{ + key: '1', + path: '$', + value: 1 + }, { + key: '2', + path: '$', + value: '2' + }]), + 'OK' + ); + }, GLOBAL.SERVERS.OPEN); }); diff --git a/packages/json/lib/commands/MSET.ts b/packages/json/lib/commands/MSET.ts index 67228f264d2..a081bfd543a 100644 --- a/packages/json/lib/commands/MSET.ts +++ b/packages/json/lib/commands/MSET.ts @@ -1,28 +1,28 @@ +import { RedisArgument, SimpleStringReply, Command } from '@redis/client/dist/lib/RESP/types'; import { RedisJSON, transformRedisJsonArgument } from '.'; -import { RedisCommandArgument } from '@redis/client/dist/lib/commands'; -export const FIRST_KEY_INDEX = 1; - -interface JsonMSetItem { - key: RedisCommandArgument; - path: RedisCommandArgument; - value: RedisJSON; +export interface JsonMSetItem { + key: RedisArgument; + path: RedisArgument; + value: RedisJSON; } -export function transformArguments(items: Array): Array { - - const args = new Array(1 + items.length * 3); +export default { + FIRST_KEY_INDEX: 1, + IS_READ_ONLY: false, + transformArguments(items: Array) { + const args = new Array(1 + items.length * 3); args[0] = 'JSON.MSET'; let argsIndex = 1; for (let i = 0; i < items.length; i++) { - const item = items[i]; - args[argsIndex++] = item.key; - args[argsIndex++] = item.path; - args[argsIndex++] = transformRedisJsonArgument(item.value); + const item = items[i]; + args[argsIndex++] = item.key; + args[argsIndex++] = item.path; + args[argsIndex++] = transformRedisJsonArgument(item.value); } return args; -} - -export declare function transformReply(): 'OK'; + }, + transformReply: undefined as unknown as () => SimpleStringReply<'OK'> +} as const satisfies Command; diff --git a/packages/json/lib/commands/NUMINCRBY.spec.ts b/packages/json/lib/commands/NUMINCRBY.spec.ts index 56dede68bde..d0bffd2bd2a 100644 --- a/packages/json/lib/commands/NUMINCRBY.spec.ts +++ b/packages/json/lib/commands/NUMINCRBY.spec.ts @@ -1,21 +1,21 @@ -import { strict as assert } from 'assert'; +import { strict as assert } from 'node:assert'; import testUtils, { GLOBAL } from '../test-utils'; -import { transformArguments } from './NUMINCRBY'; +import NUMINCRBY from './NUMINCRBY'; -describe('NUMINCRBY', () => { - it('transformArguments', () => { - assert.deepEqual( - transformArguments('key', '$', 1), - ['JSON.NUMINCRBY', 'key', '$', '1'] - ); - }); +describe('JSON.NUMINCRBY', () => { + it('transformArguments', () => { + assert.deepEqual( + NUMINCRBY.transformArguments('key', '$', 1), + ['JSON.NUMINCRBY', 'key', '$', '1'] + ); + }); - testUtils.testWithClient('client.json.numIncrBy', async client => { - await client.json.set('key', '$', 0); + testUtils.testWithClient('client.json.numIncrBy', async client => { + const [, reply] = await Promise.all([ + client.json.set('key', '$', 0), + client.json.numIncrBy('key', '$', 1) + ]); - assert.deepEqual( - await client.json.numIncrBy('key', '$', 1), - [1] - ); - }, GLOBAL.SERVERS.OPEN); + assert.deepEqual(reply, [1]); + }, GLOBAL.SERVERS.OPEN); }); diff --git a/packages/json/lib/commands/NUMINCRBY.ts b/packages/json/lib/commands/NUMINCRBY.ts index e3d8887ea3d..65cc7db68a9 100644 --- a/packages/json/lib/commands/NUMINCRBY.ts +++ b/packages/json/lib/commands/NUMINCRBY.ts @@ -1,7 +1,15 @@ -export const FIRST_KEY_INDEX = 1; +import { RedisArgument, ArrayReply, NumberReply, DoubleReply, NullReply, BlobStringReply, UnwrapReply, Command } from '@redis/client/dist/lib/RESP/types'; -export function transformArguments(key: string, path: string, by: number): Array { +export default { + FIRST_KEY_INDEX: 1, + IS_READ_ONLY: false, + transformArguments(key: RedisArgument, path: RedisArgument, by: number) { return ['JSON.NUMINCRBY', key, path, by.toString()]; -} - -export { transformNumbersReply as transformReply } from '.'; + }, + transformReply: { + 2: (reply: UnwrapReply) => { + return JSON.parse(reply.toString()) as number | Array; + }, + 3: undefined as unknown as () => ArrayReply + } +} as const satisfies Command; diff --git a/packages/json/lib/commands/NUMMULTBY.spec.ts b/packages/json/lib/commands/NUMMULTBY.spec.ts index 3e2581a3cd8..9767c2b0979 100644 --- a/packages/json/lib/commands/NUMMULTBY.spec.ts +++ b/packages/json/lib/commands/NUMMULTBY.spec.ts @@ -1,21 +1,21 @@ -import { strict as assert } from 'assert'; +import { strict as assert } from 'node:assert'; import testUtils, { GLOBAL } from '../test-utils'; -import { transformArguments } from './NUMMULTBY'; +import NUMMULTBY from './NUMMULTBY'; -describe('NUMMULTBY', () => { - it('transformArguments', () => { - assert.deepEqual( - transformArguments('key', '$', 2), - ['JSON.NUMMULTBY', 'key', '$', '2'] - ); - }); +describe('JSON.NUMMULTBY', () => { + it('transformArguments', () => { + assert.deepEqual( + NUMMULTBY.transformArguments('key', '$', 2), + ['JSON.NUMMULTBY', 'key', '$', '2'] + ); + }); - testUtils.testWithClient('client.json.numMultBy', async client => { - await client.json.set('key', '$', 1); + testUtils.testWithClient('client.json.numMultBy', async client => { + const [, reply] = await Promise.all([ + client.json.set('key', '$', 1), + client.json.numMultBy('key', '$', 2) + ]); - assert.deepEqual( - await client.json.numMultBy('key', '$', 2), - [2] - ); - }, GLOBAL.SERVERS.OPEN); + assert.deepEqual(reply, [2]); + }, GLOBAL.SERVERS.OPEN); }); diff --git a/packages/json/lib/commands/NUMMULTBY.ts b/packages/json/lib/commands/NUMMULTBY.ts index 2082916619a..255685a9a50 100644 --- a/packages/json/lib/commands/NUMMULTBY.ts +++ b/packages/json/lib/commands/NUMMULTBY.ts @@ -1,7 +1,11 @@ -export const FIRST_KEY_INDEX = 1; +import { RedisArgument, Command } from '@redis/client/dist/lib/RESP/types'; +import NUMINCRBY from './NUMINCRBY'; -export function transformArguments(key: string, path: string, by: number): Array { +export default { + FIRST_KEY_INDEX: 1, + IS_READ_ONLY: false, + transformArguments(key: RedisArgument, path: RedisArgument, by: number) { return ['JSON.NUMMULTBY', key, path, by.toString()]; -} - -export { transformNumbersReply as transformReply } from '.'; + }, + transformReply: NUMINCRBY.transformReply +} as const satisfies Command; diff --git a/packages/json/lib/commands/OBJKEYS.spec.ts b/packages/json/lib/commands/OBJKEYS.spec.ts index 6288c112392..dc984cb2ce9 100644 --- a/packages/json/lib/commands/OBJKEYS.spec.ts +++ b/packages/json/lib/commands/OBJKEYS.spec.ts @@ -1,28 +1,30 @@ -import { strict as assert } from 'assert'; +import { strict as assert } from 'node:assert'; import testUtils, { GLOBAL } from '../test-utils'; -import { transformArguments } from './OBJKEYS'; +import OBJKEYS from './OBJKEYS'; -describe('OBJKEYS', () => { - describe('transformArguments', () => { - it('without path', () => { - assert.deepEqual( - transformArguments('key'), - ['JSON.OBJKEYS', 'key'] - ); - }); +describe('JSON.OBJKEYS', () => { + describe('transformArguments', () => { + it('simple', () => { + assert.deepEqual( + OBJKEYS.transformArguments('key'), + ['JSON.OBJKEYS', 'key'] + ); + }); - it('with path', () => { - assert.deepEqual( - transformArguments('key', '$'), - ['JSON.OBJKEYS', 'key', '$'] - ); - }); + it('with path', () => { + assert.deepEqual( + OBJKEYS.transformArguments('key', { + path: '$' + }), + ['JSON.OBJKEYS', 'key', '$'] + ); }); + }); - // testUtils.testWithClient('client.json.objKeys', async client => { - // assert.deepEqual( - // await client.json.objKeys('key', '$'), - // [null] - // ); - // }, GLOBAL.SERVERS.OPEN); + testUtils.testWithClient('client.json.objKeys', async client => { + assert.equal( + await client.json.objKeys('key'), + null + ); + }, GLOBAL.SERVERS.OPEN); }); diff --git a/packages/json/lib/commands/OBJKEYS.ts b/packages/json/lib/commands/OBJKEYS.ts index a9465c9160c..fb8ae6bb034 100644 --- a/packages/json/lib/commands/OBJKEYS.ts +++ b/packages/json/lib/commands/OBJKEYS.ts @@ -1,13 +1,20 @@ -export const FIRST_KEY_INDEX = 1; +import { RedisArgument, ArrayReply, BlobStringReply, NullReply, Command } from '@redis/client/dist/lib/RESP/types'; -export function transformArguments(key: string, path?: string): Array { +export interface JsonObjKeysOptions { + path?: RedisArgument; +} + +export default { + FIRST_KEY_INDEX: 1, + IS_READ_ONLY: false, + transformArguments(key: RedisArgument, options?: JsonObjKeysOptions) { const args = ['JSON.OBJKEYS', key]; - if (path) { - args.push(path); + if (options?.path !== undefined) { + args.push(options.path); } return args; -} - -export declare function transformReply(): Array | null | Array | null>; + }, + transformReply: undefined as unknown as () => ArrayReply | ArrayReply | NullReply> +} as const satisfies Command; diff --git a/packages/json/lib/commands/OBJLEN.spec.ts b/packages/json/lib/commands/OBJLEN.spec.ts index 35b6589c875..8f616107fd5 100644 --- a/packages/json/lib/commands/OBJLEN.spec.ts +++ b/packages/json/lib/commands/OBJLEN.spec.ts @@ -1,28 +1,30 @@ -import { strict as assert } from 'assert'; +import { strict as assert } from 'node:assert'; import testUtils, { GLOBAL } from '../test-utils'; -import { transformArguments } from './OBJLEN'; +import OBJLEN from './OBJLEN'; -describe('OBJLEN', () => { - describe('transformArguments', () => { - it('without path', () => { - assert.deepEqual( - transformArguments('key'), - ['JSON.OBJLEN', 'key'] - ); - }); +describe('JSON.OBJLEN', () => { + describe('transformArguments', () => { + it('simple', () => { + assert.deepEqual( + OBJLEN.transformArguments('key'), + ['JSON.OBJLEN', 'key'] + ); + }); - it('with path', () => { - assert.deepEqual( - transformArguments('key', '$'), - ['JSON.OBJLEN', 'key', '$'] - ); - }); + it('with path', () => { + assert.deepEqual( + OBJLEN.transformArguments('key', { + path: '$' + }), + ['JSON.OBJLEN', 'key', '$'] + ); }); + }); - // testUtils.testWithClient('client.json.objLen', async client => { - // assert.equal( - // await client.json.objLen('key', '$'), - // [null] - // ); - // }, GLOBAL.SERVERS.OPEN); + testUtils.testWithClient('client.json.objLen', async client => { + assert.equal( + await client.json.objLen('key'), + null + ); + }, GLOBAL.SERVERS.OPEN); }); diff --git a/packages/json/lib/commands/OBJLEN.ts b/packages/json/lib/commands/OBJLEN.ts index aa800e97f71..f9c45e336ad 100644 --- a/packages/json/lib/commands/OBJLEN.ts +++ b/packages/json/lib/commands/OBJLEN.ts @@ -1,13 +1,20 @@ -export const FIRST_KEY_INDEX = 1; +import { RedisArgument, NumberReply, ArrayReply, NullReply, Command } from '@redis/client/dist/lib/RESP/types'; -export function transformArguments(key: string, path?: string): Array { +export interface JsonObjLenOptions { + path?: RedisArgument; +} + +export default { + FIRST_KEY_INDEX: 1, + IS_READ_ONLY: true, + transformArguments(key: RedisArgument, options?: JsonObjLenOptions) { const args = ['JSON.OBJLEN', key]; - if (path) { - args.push(path); + if (options?.path !== undefined) { + args.push(options.path); } return args; -} - -export declare function transformReply(): number | null | Array; + }, + transformReply: undefined as unknown as () => NumberReply | ArrayReply +} as const satisfies Command; diff --git a/packages/json/lib/commands/RESP.spec.ts b/packages/json/lib/commands/RESP.spec.ts index 8b70962d1c5..6dfc3360326 100644 --- a/packages/json/lib/commands/RESP.spec.ts +++ b/packages/json/lib/commands/RESP.spec.ts @@ -1,4 +1,4 @@ -import { strict as assert } from 'assert'; +import { strict as assert } from 'node:assert'; import testUtils, { GLOBAL } from '../test-utils'; import { transformArguments } from './RESP'; diff --git a/packages/json/lib/commands/SET.spec.ts b/packages/json/lib/commands/SET.spec.ts index 8f8586a2047..15e27073281 100644 --- a/packages/json/lib/commands/SET.spec.ts +++ b/packages/json/lib/commands/SET.spec.ts @@ -1,35 +1,35 @@ -import { strict as assert } from 'assert'; +import { strict as assert } from 'node:assert'; import testUtils, { GLOBAL } from '../test-utils'; -import { transformArguments } from './SET'; +import SET from './SET'; -describe('SET', () => { - describe('transformArguments', () => { - it('transformArguments', () => { - assert.deepEqual( - transformArguments('key', '$', 'json'), - ['JSON.SET', 'key', '$', '"json"'] - ); - }); +describe('JSON.SET', () => { + describe('transformArguments', () => { + it('transformArguments', () => { + assert.deepEqual( + SET.transformArguments('key', '$', 'json'), + ['JSON.SET', 'key', '$', '"json"'] + ); + }); - it('NX', () => { - assert.deepEqual( - transformArguments('key', '$', 'json', { NX: true }), - ['JSON.SET', 'key', '$', '"json"', 'NX'] - ); - }); + it('NX', () => { + assert.deepEqual( + SET.transformArguments('key', '$', 'json', { NX: true }), + ['JSON.SET', 'key', '$', '"json"', 'NX'] + ); + }); - it('XX', () => { - assert.deepEqual( - transformArguments('key', '$', 'json', { XX: true }), - ['JSON.SET', 'key', '$', '"json"', 'XX'] - ); - }); + it('XX', () => { + assert.deepEqual( + SET.transformArguments('key', '$', 'json', { XX: true }), + ['JSON.SET', 'key', '$', '"json"', 'XX'] + ); }); + }); - testUtils.testWithClient('client.json.mGet', async client => { - assert.equal( - await client.json.set('key', '$', 'json'), - 'OK' - ); - }, GLOBAL.SERVERS.OPEN); + testUtils.testWithClient('client.json.set', async client => { + assert.equal( + await client.json.set('key', '$', 'json'), + 'OK' + ); + }, GLOBAL.SERVERS.OPEN); }); diff --git a/packages/json/lib/commands/SET.ts b/packages/json/lib/commands/SET.ts index f50a42bf5db..78aea4b3545 100644 --- a/packages/json/lib/commands/SET.ts +++ b/packages/json/lib/commands/SET.ts @@ -1,25 +1,38 @@ +import { RedisArgument, SimpleStringReply, NullReply, Command } from '@redis/client/dist/lib/RESP/types'; import { RedisJSON, transformRedisJsonArgument } from '.'; -export const FIRST_KEY_INDEX = 1; - -interface NX { - NX: true; -} - -interface XX { - XX: true; +export interface JsonSetOptions { + condition?: 'NX' | 'XX'; + /** + * @deprecated Use `{ condition: 'NX' }` instead. + */ + NX?: boolean; + /** + * @deprecated Use `{ condition: 'XX' }` instead. + */ + XX?: boolean; } -export function transformArguments(key: string, path: string, json: RedisJSON, options?: NX | XX): Array { +export default { + FIRST_KEY_INDEX: 1, + IS_READ_ONLY: false, + transformArguments( + key: RedisArgument, + path: RedisArgument, + json: RedisJSON, + options?: JsonSetOptions + ) { const args = ['JSON.SET', key, path, transformRedisJsonArgument(json)]; - if ((options)?.NX) { - args.push('NX'); - } else if ((options)?.XX) { - args.push('XX'); + if (options?.condition) { + args.push(options?.condition); + } else if (options?.NX) { + args.push('NX'); + } else if (options?.XX) { + args.push('XX'); } return args; -} - -export declare function transformReply(): 'OK' | null; + }, + transformReply: undefined as unknown as () => SimpleStringReply<'OK'> | NullReply +} as const satisfies Command; diff --git a/packages/json/lib/commands/STRAPPEND.spec.ts b/packages/json/lib/commands/STRAPPEND.spec.ts index a37eaa1d91c..0d8bdb57185 100644 --- a/packages/json/lib/commands/STRAPPEND.spec.ts +++ b/packages/json/lib/commands/STRAPPEND.spec.ts @@ -1,30 +1,32 @@ -import { strict as assert } from 'assert'; +import { strict as assert } from 'node:assert'; import testUtils, { GLOBAL } from '../test-utils'; -import { transformArguments } from './STRAPPEND'; +import STRAPPEND from './STRAPPEND'; -describe('STRAPPEND', () => { - describe('transformArguments', () => { - it('without path', () => { - assert.deepEqual( - transformArguments('key', 'append'), - ['JSON.STRAPPEND', 'key', '"append"'] - ); - }); +describe('JSON.STRAPPEND', () => { + describe('transformArguments', () => { + it('simple', () => { + assert.deepEqual( + STRAPPEND.transformArguments('key', 'append'), + ['JSON.STRAPPEND', 'key', '"append"'] + ); + }); - it('with path', () => { - assert.deepEqual( - transformArguments('key', '$', 'append'), - ['JSON.STRAPPEND', 'key', '$', '"append"'] - ); - }); + it('with path', () => { + assert.deepEqual( + STRAPPEND.transformArguments('key', 'append', { + path: '$' + }), + ['JSON.STRAPPEND', 'key', '$', '"append"'] + ); }); + }); - testUtils.testWithClient('client.json.strAppend', async client => { - await client.json.set('key', '$', ''); + testUtils.testWithClient('client.json.strAppend', async client => { + const [, reply] = await Promise.all([ + client.json.set('key', '$', ''), + client.json.strAppend('key', 'append') + ]); - assert.deepEqual( - await client.json.strAppend('key', '$', 'append'), - [6] - ); - }, GLOBAL.SERVERS.OPEN); + assert.deepEqual(reply, 6); + }, GLOBAL.SERVERS.OPEN); }); diff --git a/packages/json/lib/commands/STRAPPEND.ts b/packages/json/lib/commands/STRAPPEND.ts index eea384c93fd..12ee7cc394c 100644 --- a/packages/json/lib/commands/STRAPPEND.ts +++ b/packages/json/lib/commands/STRAPPEND.ts @@ -1,21 +1,22 @@ +import { RedisArgument, Command, NullReply, NumberReply, ArrayReply } from '@redis/client/dist/lib/RESP/types'; import { transformRedisJsonArgument } from '.'; -export const FIRST_KEY_INDEX = 1; - -type AppendArguments = [key: string, append: string]; - -type AppendWithPathArguments = [key: string, path: string, append: string]; +export interface JsonStrAppendOptions { + path?: RedisArgument; +} -export function transformArguments(...[key, pathOrAppend, append]: AppendArguments | AppendWithPathArguments): Array { +export default { + FIRST_KEY_INDEX: 1, + IS_READ_ONLY: false, + transformArguments(key: RedisArgument, append: string, options?: JsonStrAppendOptions) { const args = ['JSON.STRAPPEND', key]; - if (append !== undefined && append !== null) { - args.push(pathOrAppend, transformRedisJsonArgument(append)); - } else { - args.push(transformRedisJsonArgument(pathOrAppend)); + if (options?.path !== undefined) { + args.push(options.path); } + args.push(transformRedisJsonArgument(append)); return args; -} - -export declare function transformReply(): number | Array; + }, + transformReply: undefined as unknown as () => NumberReply | ArrayReply +} as const satisfies Command; diff --git a/packages/json/lib/commands/STRLEN.spec.ts b/packages/json/lib/commands/STRLEN.spec.ts index cf163d3c19e..e058e48a635 100644 --- a/packages/json/lib/commands/STRLEN.spec.ts +++ b/packages/json/lib/commands/STRLEN.spec.ts @@ -1,30 +1,32 @@ -import { strict as assert } from 'assert'; +import { strict as assert } from 'node:assert'; import testUtils, { GLOBAL } from '../test-utils'; -import { transformArguments } from './STRLEN'; +import STRLEN from './STRLEN'; -describe('STRLEN', () => { - describe('transformArguments', () => { - it('without path', () => { - assert.deepEqual( - transformArguments('key'), - ['JSON.STRLEN', 'key'] - ); - }); +describe('JSON.STRLEN', () => { + describe('transformArguments', () => { + it('simple', () => { + assert.deepEqual( + STRLEN.transformArguments('key'), + ['JSON.STRLEN', 'key'] + ); + }); - it('with path', () => { - assert.deepEqual( - transformArguments('key', '$'), - ['JSON.STRLEN', 'key', '$'] - ); - }); + it('with path', () => { + assert.deepEqual( + STRLEN.transformArguments('key', { + path: '$' + }), + ['JSON.STRLEN', 'key', '$'] + ); }); + }); - testUtils.testWithClient('client.json.strLen', async client => { - await client.json.set('key', '$', ''); + testUtils.testWithClient('client.json.strLen', async client => { + const [, reply] = await Promise.all([ + client.json.set('key', '$', ''), + client.json.strLen('key') + ]); - assert.deepEqual( - await client.json.strLen('key', '$'), - [0] - ); - }, GLOBAL.SERVERS.OPEN); + assert.deepEqual(reply, 0); + }, GLOBAL.SERVERS.OPEN); }); diff --git a/packages/json/lib/commands/STRLEN.ts b/packages/json/lib/commands/STRLEN.ts index 93f5d563baf..3b514d9caba 100644 --- a/packages/json/lib/commands/STRLEN.ts +++ b/packages/json/lib/commands/STRLEN.ts @@ -1,15 +1,20 @@ -export const FIRST_KEY_INDEX = 1; +import { RedisArgument, ArrayReply, NumberReply, NullReply, Command } from '@redis/client/dist/lib/RESP/types'; -export const IS_READ_ONLY = true; +export interface JsonStrLenOptions { + path?: RedisArgument; +} -export function transformArguments(key: string, path?: string): Array { +export default { + FIRST_KEY_INDEX: 1, + IS_READ_ONLY: true, + transformArguments(key: RedisArgument, options?: JsonStrLenOptions) { const args = ['JSON.STRLEN', key]; - if (path) { - args.push(path); + if (options?.path) { + args.push(options.path); } return args; -} - -export declare function transformReply(): number; + }, + transformReply: undefined as unknown as () => NumberReply | ArrayReply +} as const satisfies Command; diff --git a/packages/json/lib/commands/TOGGLE.spec.ts b/packages/json/lib/commands/TOGGLE.spec.ts new file mode 100644 index 00000000000..c8a78877906 --- /dev/null +++ b/packages/json/lib/commands/TOGGLE.spec.ts @@ -0,0 +1,21 @@ +import { strict as assert } from 'node:assert'; +import testUtils, { GLOBAL } from '../test-utils'; +import TOGGLE from './TOGGLE'; + +describe('JSON.TOGGLE', () => { + it('transformArguments', () => { + assert.deepEqual( + TOGGLE.transformArguments('key', '$'), + ['JSON.TOGGLE', 'key', '$'] + ); + }); + + testUtils.testWithClient('client.json.toggle', async client => { + const [, reply] = await Promise.all([ + client.json.set('key', '$', true), + client.json.toggle('key', '$') + ]); + + assert.deepEqual(reply, [0]); + }, GLOBAL.SERVERS.OPEN); +}); diff --git a/packages/json/lib/commands/TOGGLE.ts b/packages/json/lib/commands/TOGGLE.ts new file mode 100644 index 00000000000..2a8df3eba36 --- /dev/null +++ b/packages/json/lib/commands/TOGGLE.ts @@ -0,0 +1,10 @@ +import { RedisArgument, ArrayReply, NumberReply, NullReply, Command, } from '@redis/client/dist/lib/RESP/types'; + +export default { + FIRST_KEY_INDEX: 1, + IS_READ_ONLY: false, + transformArguments(key: RedisArgument, path: RedisArgument) { + return ['JSON.TOGGLE', key, path]; + }, + transformReply: undefined as unknown as () => NumberReply | NullReply | ArrayReply +} as const satisfies Command; diff --git a/packages/json/lib/commands/TYPE.spec.ts b/packages/json/lib/commands/TYPE.spec.ts index 5cecfb827a7..103ce59de6e 100644 --- a/packages/json/lib/commands/TYPE.spec.ts +++ b/packages/json/lib/commands/TYPE.spec.ts @@ -1,28 +1,30 @@ -import { strict as assert } from 'assert'; +import { strict as assert } from 'node:assert'; import testUtils, { GLOBAL } from '../test-utils'; -import { transformArguments } from './TYPE'; +import TYPE from './TYPE'; -describe('TYPE', () => { - describe('transformArguments', () => { - it('without path', () => { - assert.deepEqual( - transformArguments('key'), - ['JSON.TYPE', 'key'] - ); - }); +describe('JSON.TYPE', () => { + describe('transformArguments', () => { + it('simple', () => { + assert.deepEqual( + TYPE.transformArguments('key'), + ['JSON.TYPE', 'key'] + ); + }); - it('with path', () => { - assert.deepEqual( - transformArguments('key', '$'), - ['JSON.TYPE', 'key', '$'] - ); - }); + it('with path', () => { + assert.deepEqual( + TYPE.transformArguments('key', { + path: '$' + }), + ['JSON.TYPE', 'key', '$'] + ); }); + }); - // testUtils.testWithClient('client.json.type', async client => { - // assert.deepEqual( - // await client.json.type('key', '$'), - // [null] - // ); - // }, GLOBAL.SERVERS.OPEN); + testUtils.testWithClient('client.json.type', async client => { + assert.equal( + await client.json.type('key'), + null + ); + }, GLOBAL.SERVERS.OPEN); }); diff --git a/packages/json/lib/commands/TYPE.ts b/packages/json/lib/commands/TYPE.ts index 7fd55f625dc..c2eea9856e1 100644 --- a/packages/json/lib/commands/TYPE.ts +++ b/packages/json/lib/commands/TYPE.ts @@ -1,13 +1,27 @@ -export const FIRST_KEY_INDEX = 1; +import { NullReply, BlobStringReply, ArrayReply, Command, RedisArgument, UnwrapReply } from '@redis/client/dist/lib/RESP/types'; -export function transformArguments(key: string, path?: string): Array { +export interface JsonTypeOptions { + path?: RedisArgument; +} + +export default { + FIRST_KEY_INDEX: 1, + IS_READ_ONLY: true, + transformArguments(key: RedisArgument, options?: JsonTypeOptions) { const args = ['JSON.TYPE', key]; - if (path) { - args.push(path); + if (options?.path) { + args.push(options.path); } return args; -} + }, + transformReply: { + 2: undefined as unknown as () => NullReply | BlobStringReply | ArrayReply, + // TODO: RESP3 wraps the response in another array, but only returns 1 + 3: (reply: UnwrapReply>>) => { + return reply[0]; + } + }, +} as const satisfies Command; -export declare function transformReply(): string | null | Array; diff --git a/packages/json/lib/commands/index.ts b/packages/json/lib/commands/index.ts index 9d0a82ec271..2724ff2565c 100644 --- a/packages/json/lib/commands/index.ts +++ b/packages/json/lib/commands/index.ts @@ -1,96 +1,100 @@ -import * as ARRAPPEND from './ARRAPPEND'; -import * as ARRINDEX from './ARRINDEX'; -import * as ARRINSERT from './ARRINSERT'; -import * as ARRLEN from './ARRLEN'; -import * as ARRPOP from './ARRPOP'; -import * as ARRTRIM from './ARRTRIM'; -import * as DEBUG_MEMORY from './DEBUG_MEMORY'; -import * as DEL from './DEL'; -import * as FORGET from './FORGET'; -import * as GET from './GET'; -import * as MERGE from './MERGE'; -import * as MGET from './MGET'; -import * as MSET from './MSET'; -import * as NUMINCRBY from './NUMINCRBY'; -import * as NUMMULTBY from './NUMMULTBY'; -import * as OBJKEYS from './OBJKEYS'; -import * as OBJLEN from './OBJLEN'; -import * as RESP from './RESP'; -import * as SET from './SET'; -import * as STRAPPEND from './STRAPPEND'; -import * as STRLEN from './STRLEN'; -import * as TYPE from './TYPE'; +import { BlobStringReply, NullReply, UnwrapReply } from '@redis/client/dist/lib/RESP/types'; +import ARRAPPEND from './ARRAPPEND'; +import ARRINDEX from './ARRINDEX'; +import ARRINSERT from './ARRINSERT'; +import ARRLEN from './ARRLEN'; +import ARRPOP from './ARRPOP'; +import ARRTRIM from './ARRTRIM'; +import CLEAR from './CLEAR'; +import DEBUG_MEMORY from './DEBUG_MEMORY'; +import DEL from './DEL'; +import FORGET from './FORGET'; +import GET from './GET'; +import MERGE from './MERGE'; +import MGET from './MGET'; +import MSET from './MSET'; +import NUMINCRBY from './NUMINCRBY'; +import NUMMULTBY from './NUMMULTBY'; +import OBJKEYS from './OBJKEYS'; +import OBJLEN from './OBJLEN'; +// import RESP from './RESP'; +import SET from './SET'; +import STRAPPEND from './STRAPPEND'; +import STRLEN from './STRLEN'; +import TOGGLE from './TOGGLE'; +import TYPE from './TYPE'; +import { isNullReply } from '@redis/client/dist/lib/commands/generic-transformers'; export default { - ARRAPPEND, - arrAppend: ARRAPPEND, - ARRINDEX, - arrIndex: ARRINDEX, - ARRINSERT, - arrInsert: ARRINSERT, - ARRLEN, - arrLen: ARRLEN, - ARRPOP, - arrPop: ARRPOP, - ARRTRIM, - arrTrim: ARRTRIM, - DEBUG_MEMORY, - debugMemory: DEBUG_MEMORY, - DEL, - del: DEL, - FORGET, - forget: FORGET, - GET, - get: GET, - MERGE, - merge: MERGE, - MGET, - mGet: MGET, - MSET, - mSet: MSET, - NUMINCRBY, - numIncrBy: NUMINCRBY, - NUMMULTBY, - numMultBy: NUMMULTBY, - OBJKEYS, - objKeys: OBJKEYS, - OBJLEN, - objLen: OBJLEN, - RESP, - resp: RESP, - SET, - set: SET, - STRAPPEND, - strAppend: STRAPPEND, - STRLEN, - strLen: STRLEN, - TYPE, - type: TYPE + ARRAPPEND, + arrAppend: ARRAPPEND, + ARRINDEX, + arrIndex: ARRINDEX, + ARRINSERT, + arrInsert: ARRINSERT, + ARRLEN, + arrLen: ARRLEN, + ARRPOP, + arrPop: ARRPOP, + ARRTRIM, + arrTrim: ARRTRIM, + CLEAR, + clear: CLEAR, + DEBUG_MEMORY, + debugMemory: DEBUG_MEMORY, + DEL, + del: DEL, + FORGET, + forget: FORGET, + GET, + get: GET, + MERGE, + merge: MERGE, + MGET, + mGet: MGET, + MSET, + mSet: MSET, + NUMINCRBY, + numIncrBy: NUMINCRBY, + /** + * @deprecated since JSON version 2.0 + */ + NUMMULTBY, + /** + * @deprecated since JSON version 2.0 + */ + numMultBy: NUMMULTBY, + OBJKEYS, + objKeys: OBJKEYS, + OBJLEN, + objLen: OBJLEN, + // RESP, + // resp: RESP, + SET, + set: SET, + STRAPPEND, + strAppend: STRAPPEND, + STRLEN, + strLen: STRLEN, + TOGGLE, + toggle: TOGGLE, + TYPE, + type: TYPE }; -// https://github.com/Microsoft/TypeScript/issues/3496#issuecomment-128553540 -// eslint-disable-next-line @typescript-eslint/no-empty-interface -interface RedisJSONArray extends Array {} -interface RedisJSONObject { - [key: string]: RedisJSON; - [key: number]: RedisJSON; -} -export type RedisJSON = null | boolean | number | string | Date | RedisJSONArray | RedisJSONObject; +export type RedisJSON = null | boolean | number | string | Date | Array | { + [key: string]: RedisJSON; + [key: number]: RedisJSON; +}; export function transformRedisJsonArgument(json: RedisJSON): string { - return JSON.stringify(json); + return JSON.stringify(json); } -export function transformRedisJsonReply(json: string): RedisJSON { - return JSON.parse(json); -} - -export function transformRedisJsonNullReply(json: string | null): RedisJSON | null { - if (json === null) return null; - - return transformRedisJsonReply(json); +export function transformRedisJsonReply(json: BlobStringReply): RedisJSON { + return JSON.parse((json as unknown as UnwrapReply).toString()); } -export function transformNumbersReply(reply: string): number | Array { - return JSON.parse(reply); +export function transformRedisJsonNullReply(json: NullReply | BlobStringReply): NullReply | RedisJSON { + return isNullReply(json) ? json : transformRedisJsonReply(json); } diff --git a/packages/json/lib/test-utils.ts b/packages/json/lib/test-utils.ts index 55426890e00..0ac30c521b2 100644 --- a/packages/json/lib/test-utils.ts +++ b/packages/json/lib/test-utils.ts @@ -2,20 +2,20 @@ import TestUtils from '@redis/test-utils'; import RedisJSON from '.'; export default new TestUtils({ - dockerImageName: 'redislabs/rejson', - dockerImageVersionArgument: 'rejson-version', - defaultDockerVersion: '2.6.9' + dockerImageName: 'redis/redis-stack', + dockerImageVersionArgument: 'redisgraph-version', + defaultDockerVersion: '7.4.0-v1' }); export const GLOBAL = { - SERVERS: { - OPEN: { - serverArguments: ['--loadmodule /usr/lib/redis/modules/rejson.so'], - clientOptions: { - modules: { - json: RedisJSON - } - } + SERVERS: { + OPEN: { + serverArguments: [], + clientOptions: { + modules: { + json: RedisJSON } + } } + } }; diff --git a/packages/json/package.json b/packages/json/package.json index ad60cc13c26..53711a5c0b6 100644 --- a/packages/json/package.json +++ b/packages/json/package.json @@ -1,30 +1,24 @@ { "name": "@redis/json", - "version": "1.0.7", + "version": "2.0.0-next.2", "license": "MIT", - "main": "./dist/index.js", - "types": "./dist/index.d.ts", + "main": "./dist/lib/index.js", + "types": "./dist/lib/index.d.ts", "files": [ - "dist/" + "dist/", + "!dist/tsconfig.tsbuildinfo" ], "scripts": { - "test": "nyc -r text-summary -r lcov mocha -r source-map-support/register -r ts-node/register './lib/**/*.spec.ts'", - "build": "tsc", - "documentation": "typedoc" + "test": "nyc -r text-summary -r lcov mocha -r tsx './lib/**/*.spec.ts'" }, "peerDependencies": { - "@redis/client": "^1.0.0" + "@redis/client": "^2.0.0-next.4" }, "devDependencies": { - "@istanbuljs/nyc-config-typescript": "^1.0.2", - "@redis/test-utils": "*", - "@types/node": "^20.6.2", - "nyc": "^15.1.0", - "release-it": "^16.1.5", - "source-map-support": "^0.5.21", - "ts-node": "^10.9.1", - "typedoc": "^0.25.1", - "typescript": "^5.2.2" + "@redis/test-utils": "*" + }, + "engines": { + "node": ">= 18" }, "repository": { "type": "git", diff --git a/.release-it.json b/packages/redis/.release-it.json similarity index 100% rename from .release-it.json rename to packages/redis/.release-it.json diff --git a/packages/redis/README.md b/packages/redis/README.md new file mode 100644 index 00000000000..76929ffa48b --- /dev/null +++ b/packages/redis/README.md @@ -0,0 +1,203 @@ +# Node-Redis + +## Usage + +### Basic Example + +```javascript +import { createClient } from 'redis'; + +const client = await createClient() + .on('error', err => console.log('Redis Client Error', err)) + .connect(); + +await client.set('key', 'value'); +const value = await client.get('key'); +await client.close(); +``` + +> :warning: You **MUST** listen to `error` events. If a client doesn't have at least one `error` listener registered and an `error` occurs, that error will be thrown and the Node.js process will exit. See the [`EventEmitter` docs](https://nodejs.org/api/events.html#error-events) for more details. + +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]`: + +```javascript +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.): + +```javascript +// 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: + +```javascript +await client.set('key', 'value', { + expiration: { + type: 'EX', + value: 10 + }, + condition: 'NX' +}); +``` + +> NOTE: command modifiers that change the reply type (e.g. `WITHSCORES` for `ZDIFF`) are exposed as separate commands (e.g. `ZDIFF_WITHSCORES`/`zDiffWithScores`). + +Replies will be mapped to useful data structures: + +```javascript +await client.hGetAll('key'); // { field1: 'value1', field2: 'value2' } +await client.hVals('key'); // ['value1', 'value2'] +``` + +> NOTE: you can change the default type mapping. See the [Type Mapping](../../docs/command-options.md#type-mapping) documentation for more information. + +### Unsupported Redis Commands + +If you want to run commands and/or use arguments that Node Redis doesn't know about (yet!) use `.sendCommand()`: + +```javascript +await client.sendCommand(['SET', 'key', 'value', 'EX', '10', 'NX']); // 'OK' +await client.sendCommand(['HGETALL', 'key']); // ['key1', 'field1', 'key2', 'field2'] +``` + +### Disconnecting + +#### `.close()` + +Gracefully close a client's connection to Redis. +Wait for commands in process, but reject any new commands. + +```javascript +const [ping, get] = await Promise.all([ + client.ping(), + client.get('key'), + client.close() +]); // ['PONG', null] + +try { + await client.get('key'); +} catch (err) { + // ClientClosedError +} +``` + +> `.close()` is just like `.quit()` which was depreacted v5. See the [relevant section in the migration guide](../../docs/v4-to-v5.md#Quit-VS-Disconnect) for more information. + +#### `.destroy()` + +Forcibly close a client's connection to Redis. + +```javascript +try { + const promise = Promise.all([ + client.ping(), + client.get('key') + ]); + + client.destroy(); + + await promise; +} catch (err) { + // DisconnectsClientError +} + +try { + await client.get('key'); +} catch (err) { + // ClientClosedError +} +``` + +> `.destroy()` is just like `.disconnect()` which was depreated in v5. See the [relevant section in the migration guide](../../docs/v4-to-v5.md#Quit-VS-Disconnect) for more information. + +### Auto-Pipelining + +Node Redis will automatically pipeline requests that are made during the same "tick". + +```javascript +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()`. + +```javascript +await Promise.all([ + client.set('Tm9kZSBSZWRpcw==', 'users:1'), + client.sAdd('users:1:tokens', 'Tm9kZSBSZWRpcw==') +]); +``` + +### Connection State + +To client exposes 2 `boolean`s that track the client state: +1. `isOpen` - the client is either connecting or connected. +2. `isReady` - the client is connected and ready to send + +### Events + +The client extends `EventEmitter` and emits the following events: + +| Name | When | Listener arguments | +|-------------------------|------------------------------------------------------------------------------------|------------------------------------------------------------| +| `connect` | Initiating a connection to the server | *No arguments* | +| `ready` | Client is ready to use | *No arguments* | +| `end` | Connection has been closed (via `.quit()` or `.disconnect()`) | *No arguments* | +| `error` | An error has occurred—usually a network issue such as "Socket closed unexpectedly" | `(error: Error)` | +| `reconnecting` | Client is trying to reconnect to the server | *No arguments* | +| `sharded-channel-moved` | See [here](../../docs/pub-sub.md#sharded-channel-moved-event) | See [here](../../docs/pub-sub.md#sharded-channel-moved-event) | + +> :warning: You **MUST** listen to `error` events. If a client doesn't have at least one `error` listener registered and an `error` occurs, that error will be thrown and the Node.js process will exit. See the [`EventEmitter` docs](https://nodejs.org/api/events.html#error-events) for more details. + +### Read more + +- [Transactions (`MULTI`/`EXEC`)](../../docs/transactions.md). +- [Pub/Sub](../../docs/pub-sub.md). +- [Scan Iterators](../../docs/scan-iterators.md). +- [Programmability](../../docs/programmability.md). +- [Command Options](../../docs/command-options.md). +- [Pool](../../docs/pool.md). +- [Clustering](../../docs/clustering.md). +- [Sentinel](../../docs/sentinel.md). +- [FAQ](../../docs/FAQ.md). + +## Supported Redis versions + +Node Redis is supported with the following versions of Redis: + +| Version | Supported | +|---------|--------------------| +| 7.2.z | :heavy_check_mark: | +| 7.0.z | :heavy_check_mark: | +| 6.2.z | :heavy_check_mark: | +| 6.0.z | :heavy_check_mark: | +| 5.0.z | :heavy_check_mark: | +| < 5.0 | :x: | + +> Node Redis should work with older versions of Redis, but it is not fully tested and we cannot offer support. + +## Contributing + +If you'd like to contribute, check out the [contributing guide](../../CONTRIBUTING.md). + +Thank you to all the people who already contributed to Node Redis! + +[![Contributors](https://contrib.rocks/image?repo=redis/node-redis)](https://github.com/redis/node-redis/graphs/contributors) + +## License + +This repository is licensed under the "MIT" license. See [LICENSE](../../LICENSE). diff --git a/packages/redis/index.ts b/packages/redis/index.ts new file mode 100644 index 00000000000..7586846d12c --- /dev/null +++ b/packages/redis/index.ts @@ -0,0 +1,87 @@ +import { + RedisModules, + RedisFunctions, + RedisScripts, + RespVersions, + TypeMapping, + createClient as _createClient, + RedisClientOptions, + RedisClientType as _RedisClientType, + createCluster as _createCluster, + RedisClusterOptions, + RedisClusterType as _RedisClusterType, +} from '@redis/client'; +import RedisBloomModules from '@redis/bloom'; +import RedisGraph from '@redis/graph'; +import RedisJSON from '@redis/json'; +import RediSearch from '@redis/search'; +import RedisTimeSeries from '@redis/time-series'; + +// export * from '@redis/client'; +// export * from '@redis/bloom'; +// export * from '@redis/graph'; +// export * from '@redis/json'; +// export * from '@redis/search'; +// export * from '@redis/time-series'; + +const modules = { + ...RedisBloomModules, + graph: RedisGraph, + json: RedisJSON, + ft: RediSearch, + ts: RedisTimeSeries +}; + +export type RedisDefaultModules = typeof modules; + +export type RedisClientType< + M extends RedisModules = RedisDefaultModules, + F extends RedisFunctions = {}, + S extends RedisScripts = {}, + RESP extends RespVersions = 2, + TYPE_MAPPING extends TypeMapping = {} +> = _RedisClientType; + +export function createClient< + M extends RedisModules, + F extends RedisFunctions, + S extends RedisScripts, + RESP extends RespVersions, + TYPE_MAPPING extends TypeMapping +>( + options?: RedisClientOptions +): _RedisClientType { + return _createClient({ + ...options, + modules: { + ...modules, + ...(options?.modules as M) + } + }); +} + +export type RedisClusterType< + M extends RedisModules = RedisDefaultModules, + F extends RedisFunctions = {}, + S extends RedisScripts = {}, + RESP extends RespVersions = 2, + TYPE_MAPPING extends TypeMapping = {} +> = _RedisClusterType; + +export function createCluster< + M extends RedisModules, + F extends RedisFunctions, + S extends RedisScripts, + RESP extends RespVersions, + TYPE_MAPPING extends TypeMapping +>( + options: RedisClusterOptions +): RedisClusterType { + return _createCluster({ + ...options, + modules: { + ...modules, + ...(options?.modules as M) + } + }); +} diff --git a/packages/redis/package.json b/packages/redis/package.json new file mode 100644 index 00000000000..0603e6c9480 --- /dev/null +++ b/packages/redis/package.json @@ -0,0 +1,34 @@ +{ + "name": "redis", + "description": "A modern, high performance Redis client", + "version": "5.0.0-next.4", + "license": "MIT", + "main": "./dist/index.js", + "types": "./dist/index.d.ts", + "files": [ + "dist/", + "!dist/tsconfig.tsbuildinfo" + ], + "dependencies": { + "@redis/bloom": "2.0.0-next.3", + "@redis/client": "2.0.0-next.4", + "@redis/graph": "2.0.0-next.2", + "@redis/json": "2.0.0-next.2", + "@redis/search": "2.0.0-next.2", + "@redis/time-series": "2.0.0-next.2" + }, + "engines": { + "node": ">= 18" + }, + "repository": { + "type": "git", + "url": "git://github.com/redis/node-redis.git" + }, + "bugs": { + "url": "https://github.com/redis/node-redis/issues" + }, + "homepage": "https://github.com/redis/node-redis", + "keywords": [ + "redis" + ] +} diff --git a/packages/redis/tsconfig.json b/packages/redis/tsconfig.json new file mode 100644 index 00000000000..50da0ba733a --- /dev/null +++ b/packages/redis/tsconfig.json @@ -0,0 +1,9 @@ +{ + "extends": "../../tsconfig.base.json", + "compilerOptions": { + "outDir": "./dist" + }, + "include": [ + "./index.ts" + ] +} diff --git a/packages/search/.npmignore b/packages/search/.npmignore deleted file mode 100644 index bbef2b404fb..00000000000 --- a/packages/search/.npmignore +++ /dev/null @@ -1,6 +0,0 @@ -.nyc_output/ -coverage/ -lib/ -.nycrc.json -.release-it.json -tsconfig.json diff --git a/packages/search/.release-it.json b/packages/search/.release-it.json index 72cb1016ef4..3996a524e3b 100644 --- a/packages/search/.release-it.json +++ b/packages/search/.release-it.json @@ -5,6 +5,7 @@ "tagAnnotation": "Release ${tagName}" }, "npm": { + "versionArgs": ["--workspaces-update=false"], "publishArgs": ["--access", "public"] } } diff --git a/packages/search/README.md b/packages/search/README.md index 60186ba7f92..70a91fdeb2f 100644 --- a/packages/search/README.md +++ b/packages/search/README.md @@ -1,18 +1,20 @@ # @redis/search -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. +This package provides support for the [RediSearch](https://redis.io/docs/interact/search-and-query/) module, which adds indexing and querying support for data stored in Redis Hashes or as JSON documents with the [RedisJSON](https://redis.io/docs/data-types/json/) module. -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. +Should be used with [`redis`/`@redis/client`](https://github.com/redis/node-redis). + +:warning: 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. +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 [examples folder](https://github.com/redis/node-redis/tree/master/examples). ### 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://redis.io/commands/ft.create) 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`: +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://redis.io/commands/ft.create) 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', { diff --git a/packages/search/lib/commands/AGGREGATE.spec.ts b/packages/search/lib/commands/AGGREGATE.spec.ts index 5b34d7dc16f..50ef44f2bd6 100644 --- a/packages/search/lib/commands/AGGREGATE.spec.ts +++ b/packages/search/lib/commands/AGGREGATE.spec.ts @@ -1,516 +1,521 @@ -import { strict as assert } from 'assert'; +import { strict as assert } from 'node:assert'; import testUtils, { GLOBAL } from '../test-utils'; -import { AggregateGroupByReducers, AggregateSteps, transformArguments } from './AGGREGATE'; -import { SchemaFieldTypes } from '.'; +import AGGREGATE from './AGGREGATE'; describe('AGGREGATE', () => { - describe('transformArguments', () => { - it('without options', () => { + describe('transformArguments', () => { + it('without options', () => { + assert.deepEqual( + AGGREGATE.transformArguments('index', '*'), + ['FT.AGGREGATE', 'index', '*'] + ); + }); + + it('with VERBATIM', () => { + assert.deepEqual( + AGGREGATE.transformArguments('index', '*', { + VERBATIM: true + }), + ['FT.AGGREGATE', 'index', '*', 'VERBATIM'] + ); + }); + + it('with ADDSCORES', () => { + assert.deepEqual( + AGGREGATE.transformArguments('index', '*', { ADDSCORES: true }), + ['FT.AGGREGATE', 'index', '*', 'ADDSCORES'] + ); + }); + + describe('with LOAD', () => { + describe('single', () => { + describe('without alias', () => { + it('string', () => { assert.deepEqual( - transformArguments('index', '*'), - ['FT.AGGREGATE', 'index', '*'] + AGGREGATE.transformArguments('index', '*', { + LOAD: '@property' + }), + ['FT.AGGREGATE', 'index', '*', 'LOAD', '1', '@property'] ); - }); + }); - it('with VERBATIM', () => { + it('{ identifier: string }', () => { assert.deepEqual( - transformArguments('index', '*', { VERBATIM: true }), - ['FT.AGGREGATE', 'index', '*', 'VERBATIM'] + AGGREGATE.transformArguments('index', '*', { + LOAD: { + identifier: '@property' + } + }), + ['FT.AGGREGATE', 'index', '*', 'LOAD', '1', '@property'] ); + }); }); - it('with ADDSCORES', () => { - assert.deepEqual( - transformArguments('index', '*', { ADDSCORES: true }), - ['FT.AGGREGATE', 'index', '*', 'ADDSCORES'] - ); + it('with alias', () => { + assert.deepEqual( + AGGREGATE.transformArguments('index', '*', { + LOAD: { + identifier: '@property', + AS: 'alias' + } + }), + ['FT.AGGREGATE', 'index', '*', 'LOAD', '3', '@property', 'AS', 'alias'] + ); }); + }); + + it('multiple', () => { + assert.deepEqual( + AGGREGATE.transformArguments('index', '*', { + LOAD: ['@1', '@2'] + }), + ['FT.AGGREGATE', 'index', '*', 'LOAD', '2', '@1', '@2'] + ); + }); + }); - describe('with LOAD', () => { - describe('single', () => { - describe('without alias', () => { - it('string', () => { - assert.deepEqual( - transformArguments('index', '*', { LOAD: '@property' }), - ['FT.AGGREGATE', 'index', '*', 'LOAD', '1', '@property'] - ); - }); - - it('{ identifier: string }', () => { - assert.deepEqual( - transformArguments('index', '*', { - LOAD: { - identifier: '@property' - } - }), - ['FT.AGGREGATE', 'index', '*', 'LOAD', '1', '@property'] - ); - }); - }); - - it('with alias', () => { - assert.deepEqual( - transformArguments('index', '*', { - LOAD: { - identifier: '@property', - AS: 'alias' - } - }), - ['FT.AGGREGATE', 'index', '*', 'LOAD', '3', '@property', 'AS', 'alias'] - ); - }); + describe('with STEPS', () => { + describe('GROUPBY', () => { + describe('COUNT', () => { + describe('without properties', () => { + it('without alias', () => { + assert.deepEqual( + AGGREGATE.transformArguments('index', '*', { + STEPS: [{ + type: 'GROUPBY', + REDUCE: { + type: 'COUNT' + } + }] + }), + ['FT.AGGREGATE', 'index', '*', 'GROUPBY', '0', 'REDUCE', 'COUNT', '0'] + ); + }); + + it('with alias', () => { + assert.deepEqual( + AGGREGATE.transformArguments('index', '*', { + STEPS: [{ + type: 'GROUPBY', + REDUCE: { + type: 'COUNT', + AS: 'count' + } + }] + }), + ['FT.AGGREGATE', 'index', '*', 'GROUPBY', '0', 'REDUCE', 'COUNT', '0', 'AS', 'count'] + ); + }); + }); + + describe('with properties', () => { + it('single', () => { + assert.deepEqual( + AGGREGATE.transformArguments('index', '*', { + STEPS: [{ + type: 'GROUPBY', + properties: '@property', + REDUCE: { + type: 'COUNT' + } + }] + }), + ['FT.AGGREGATE', 'index', '*', 'GROUPBY', '1', '@property', 'REDUCE', 'COUNT', '0'] + ); }); it('multiple', () => { - assert.deepEqual( - transformArguments('index', '*', { LOAD: ['@1', '@2'] }), - ['FT.AGGREGATE', 'index', '*', 'LOAD', '2', '@1', '@2'] - ); + assert.deepEqual( + AGGREGATE.transformArguments('index', '*', { + STEPS: [{ + type: 'GROUPBY', + properties: ['@1', '@2'], + REDUCE: { + type: 'COUNT' + } + }] + }), + ['FT.AGGREGATE', 'index', '*', 'GROUPBY', '2', '@1', '@2', 'REDUCE', 'COUNT', '0'] + ); }); + }); }); - describe('with STEPS', () => { - describe('GROUPBY', () => { - describe('COUNT', () => { - describe('without properties', () => { - it('without alias', () => { - assert.deepEqual( - transformArguments('index', '*', { - STEPS: [{ - type: AggregateSteps.GROUPBY, - REDUCE: { - type: AggregateGroupByReducers.COUNT - } - }] - }), - ['FT.AGGREGATE', 'index', '*', 'GROUPBY', '0', 'REDUCE', 'COUNT', '0'] - ); - }); - - it('with alias', () => { - assert.deepEqual( - transformArguments('index', '*', { - STEPS: [{ - type: AggregateSteps.GROUPBY, - REDUCE: { - type: AggregateGroupByReducers.COUNT, - AS: 'count' - } - }] - }), - ['FT.AGGREGATE', 'index', '*', 'GROUPBY', '0', 'REDUCE', 'COUNT', '0', 'AS', 'count'] - ); - }); - }); - - describe('with properties', () => { - it('single', () => { - assert.deepEqual( - transformArguments('index', '*', { - STEPS: [{ - type: AggregateSteps.GROUPBY, - properties: '@property', - REDUCE: { - type: AggregateGroupByReducers.COUNT - } - }] - }), - ['FT.AGGREGATE', 'index', '*', 'GROUPBY', '1', '@property', 'REDUCE', 'COUNT', '0'] - ); - }); - - it('multiple', () => { - assert.deepEqual( - transformArguments('index', '*', { - STEPS: [{ - type: AggregateSteps.GROUPBY, - properties: ['@1', '@2'], - REDUCE: { - type: AggregateGroupByReducers.COUNT - } - }] - }), - ['FT.AGGREGATE', 'index', '*', 'GROUPBY', '2', '@1', '@2', 'REDUCE', 'COUNT', '0'] - ); - }); - }); - }); - - it('COUNT_DISTINCT', () => { - assert.deepEqual( - transformArguments('index', '*', { - STEPS: [{ - type: AggregateSteps.GROUPBY, - REDUCE: { - type: AggregateGroupByReducers.COUNT_DISTINCT, - property: '@property' - } - }] - }), - ['FT.AGGREGATE', 'index', '*', 'GROUPBY', '0', 'REDUCE', 'COUNT_DISTINCT', '1', '@property'] - ); - }); - - it('COUNT_DISTINCTISH', () => { - assert.deepEqual( - transformArguments('index', '*', { - STEPS: [{ - type: AggregateSteps.GROUPBY, - REDUCE: { - type: AggregateGroupByReducers.COUNT_DISTINCTISH, - property: '@property' - } - }] - }), - ['FT.AGGREGATE', 'index', '*', 'GROUPBY', '0', 'REDUCE', 'COUNT_DISTINCTISH', '1', '@property'] - ); - }); - - it('SUM', () => { - assert.deepEqual( - transformArguments('index', '*', { - STEPS: [{ - type: AggregateSteps.GROUPBY, - REDUCE: { - type: AggregateGroupByReducers.SUM, - property: '@property' - } - }] - }), - ['FT.AGGREGATE', 'index', '*', 'GROUPBY', '0', 'REDUCE', 'SUM', '1', '@property'] - ); - }); - - it('MIN', () => { - assert.deepEqual( - transformArguments('index', '*', { - STEPS: [{ - type: AggregateSteps.GROUPBY, - REDUCE: { - type: AggregateGroupByReducers.MIN, - property: '@property' - } - }] - }), - ['FT.AGGREGATE', 'index', '*', 'GROUPBY', '0', 'REDUCE', 'MIN', '1', '@property'] - ); - }); - - it('MAX', () => { - assert.deepEqual( - transformArguments('index', '*', { - STEPS: [{ - type: AggregateSteps.GROUPBY, - REDUCE: { - type: AggregateGroupByReducers.MAX, - property: '@property' - } - }] - }), - ['FT.AGGREGATE', 'index', '*', 'GROUPBY', '0', 'REDUCE', 'MAX', '1', '@property'] - ); - }); - - it('AVG', () => { - assert.deepEqual( - transformArguments('index', '*', { - STEPS: [{ - type: AggregateSteps.GROUPBY, - REDUCE: { - type: AggregateGroupByReducers.AVG, - property: '@property' - } - }] - }), - ['FT.AGGREGATE', 'index', '*', 'GROUPBY', '0', 'REDUCE', 'AVG', '1', '@property'] - ); - }); - - it('STDDEV', () => { - assert.deepEqual( - transformArguments('index', '*', { - STEPS: [{ - type: AggregateSteps.GROUPBY, - REDUCE: { - type: AggregateGroupByReducers.STDDEV, - property: '@property' - } - }] - }), - ['FT.AGGREGATE', 'index', '*', 'GROUPBY', '0', 'REDUCE', 'STDDEV', '1', '@property'] - ); - }); - - it('QUANTILE', () => { - assert.deepEqual( - transformArguments('index', '*', { - STEPS: [{ - type: AggregateSteps.GROUPBY, - REDUCE: { - type: AggregateGroupByReducers.QUANTILE, - property: '@property', - quantile: 0.5 - } - }] - }), - ['FT.AGGREGATE', 'index', '*', 'GROUPBY', '0', 'REDUCE', 'QUANTILE', '2', '@property', '0.5'] - ); - }); - - it('TO_LIST', () => { - assert.deepEqual( - transformArguments('index', '*', { - STEPS: [{ - type: AggregateSteps.GROUPBY, - REDUCE: { - type: AggregateGroupByReducers.TO_LIST, - property: '@property' - } - }] - }), - ['FT.AGGREGATE', 'index', '*', 'GROUPBY', '0', 'REDUCE', 'TOLIST', '1', '@property'] - ); - }); - - describe('FIRST_VALUE', () => { - it('simple', () => { - assert.deepEqual( - transformArguments('index', '*', { - STEPS: [{ - type: AggregateSteps.GROUPBY, - REDUCE: { - type: AggregateGroupByReducers.FIRST_VALUE, - property: '@property' - } - }] - }), - ['FT.AGGREGATE', 'index', '*', 'GROUPBY', '0', 'REDUCE', 'FIRST_VALUE', '1', '@property'] - ); - }); - - describe('with BY', () => { - describe('without direction', () => { - it('string', () => { - assert.deepEqual( - transformArguments('index', '*', { - STEPS: [{ - type: AggregateSteps.GROUPBY, - REDUCE: { - type: AggregateGroupByReducers.FIRST_VALUE, - property: '@property', - BY: '@by' - } - }] - }), - ['FT.AGGREGATE', 'index', '*', 'GROUPBY', '0', 'REDUCE', 'FIRST_VALUE', '3', '@property', 'BY', '@by'] - ); - }); - - - it('{ property: string }', () => { - assert.deepEqual( - transformArguments('index', '*', { - STEPS: [{ - type: AggregateSteps.GROUPBY, - REDUCE: { - type: AggregateGroupByReducers.FIRST_VALUE, - property: '@property', - BY: { - property: '@by' - } - } - }] - }), - ['FT.AGGREGATE', 'index', '*', 'GROUPBY', '0', 'REDUCE', 'FIRST_VALUE', '3', '@property', 'BY', '@by'] - ); - }); - }); - - it('with direction', () => { - assert.deepEqual( - transformArguments('index', '*', { - STEPS: [{ - type: AggregateSteps.GROUPBY, - REDUCE: { - type: AggregateGroupByReducers.FIRST_VALUE, - property: '@property', - BY: { - property: '@by', - direction: 'ASC' - } - } - }] - }), - ['FT.AGGREGATE', 'index', '*', 'GROUPBY', '0', 'REDUCE', 'FIRST_VALUE', '4', '@property', 'BY', '@by', 'ASC'] - ); - }); - }); - }); - - it('RANDOM_SAMPLE', () => { - assert.deepEqual( - transformArguments('index', '*', { - STEPS: [{ - type: AggregateSteps.GROUPBY, - REDUCE: { - type: AggregateGroupByReducers.RANDOM_SAMPLE, - property: '@property', - sampleSize: 1 - } - }] - }), - ['FT.AGGREGATE', 'index', '*', 'GROUPBY', '0', 'REDUCE', 'RANDOM_SAMPLE', '2', '@property', '1'] - ); - }); - }); + it('COUNT_DISTINCT', () => { + assert.deepEqual( + AGGREGATE.transformArguments('index', '*', { + STEPS: [{ + type: 'GROUPBY', + REDUCE: { + type: 'COUNT_DISTINCT', + property: '@property' + } + }] + }), + ['FT.AGGREGATE', 'index', '*', 'GROUPBY', '0', 'REDUCE', 'COUNT_DISTINCT', '1', '@property'] + ); + }); - describe('SORTBY', () => { - it('string', () => { - assert.deepEqual( - transformArguments('index', '*', { - STEPS: [{ - type: AggregateSteps.SORTBY, - BY: '@by' - }] - }), - ['FT.AGGREGATE', 'index', '*', 'SORTBY', '1', '@by'] - ); - }); - - it('Array', () => { - assert.deepEqual( - transformArguments('index', '*', { - STEPS: [{ - type: AggregateSteps.SORTBY, - BY: ['@1', '@2'] - }] - }), - ['FT.AGGREGATE', 'index', '*', 'SORTBY', '2', '@1', '@2'] - ); - }); - - it('with MAX', () => { - assert.deepEqual( - transformArguments('index', '*', { - STEPS: [{ - type: AggregateSteps.SORTBY, - BY: '@by', - MAX: 1 - }] - }), - ['FT.AGGREGATE', 'index', '*', 'SORTBY', '1', '@by', 'MAX', '1'] - ); - }); - }); + it('COUNT_DISTINCTISH', () => { + assert.deepEqual( + AGGREGATE.transformArguments('index', '*', { + STEPS: [{ + type: 'GROUPBY', + REDUCE: { + type: 'COUNT_DISTINCTISH', + property: '@property' + } + }] + }), + ['FT.AGGREGATE', 'index', '*', 'GROUPBY', '0', 'REDUCE', 'COUNT_DISTINCTISH', '1', '@property'] + ); + }); - describe('APPLY', () => { - assert.deepEqual( - transformArguments('index', '*', { - STEPS: [{ - type: AggregateSteps.APPLY, - expression: '@field + 1', - AS: 'as' - }] - }), - ['FT.AGGREGATE', 'index', '*', 'APPLY', '@field + 1', 'AS', 'as'] - ); - }); + it('SUM', () => { + assert.deepEqual( + AGGREGATE.transformArguments('index', '*', { + STEPS: [{ + type: 'GROUPBY', + REDUCE: { + type: 'SUM', + property: '@property' + } + }] + }), + ['FT.AGGREGATE', 'index', '*', 'GROUPBY', '0', 'REDUCE', 'SUM', '1', '@property'] + ); + }); - describe('LIMIT', () => { + it('MIN', () => { + assert.deepEqual( + AGGREGATE.transformArguments('index', '*', { + STEPS: [{ + type: 'GROUPBY', + REDUCE: { + type: 'MIN', + property: '@property' + } + }] + }), + ['FT.AGGREGATE', 'index', '*', 'GROUPBY', '0', 'REDUCE', 'MIN', '1', '@property'] + ); + }); + + it('MAX', () => { + assert.deepEqual( + AGGREGATE.transformArguments('index', '*', { + STEPS: [{ + type: 'GROUPBY', + REDUCE: { + type: 'MAX', + property: '@property' + } + }] + }), + ['FT.AGGREGATE', 'index', '*', 'GROUPBY', '0', 'REDUCE', 'MAX', '1', '@property'] + ); + }); + + it('AVG', () => { + assert.deepEqual( + AGGREGATE.transformArguments('index', '*', { + STEPS: [{ + type: 'GROUPBY', + REDUCE: { + type: 'AVG', + property: '@property' + } + }] + }), + ['FT.AGGREGATE', 'index', '*', 'GROUPBY', '0', 'REDUCE', 'AVG', '1', '@property'] + ); + }); + + it('STDDEV', () => { + assert.deepEqual( + AGGREGATE.transformArguments('index', '*', { + STEPS: [{ + type: 'GROUPBY', + REDUCE: { + type: 'STDDEV', + property: '@property' + } + }] + }), + ['FT.AGGREGATE', 'index', '*', 'GROUPBY', '0', 'REDUCE', 'STDDEV', '1', '@property'] + ); + }); + + it('QUANTILE', () => { + assert.deepEqual( + AGGREGATE.transformArguments('index', '*', { + STEPS: [{ + type: 'GROUPBY', + REDUCE: { + type: 'QUANTILE', + property: '@property', + quantile: 0.5 + } + }] + }), + ['FT.AGGREGATE', 'index', '*', 'GROUPBY', '0', 'REDUCE', 'QUANTILE', '2', '@property', '0.5'] + ); + }); + + it('TOLIST', () => { + assert.deepEqual( + AGGREGATE.transformArguments('index', '*', { + STEPS: [{ + type: 'GROUPBY', + REDUCE: { + type: 'TOLIST', + property: '@property' + } + }] + }), + ['FT.AGGREGATE', 'index', '*', 'GROUPBY', '0', 'REDUCE', 'TOLIST', '1', '@property'] + ); + }); + + describe('FIRST_VALUE', () => { + it('simple', () => { + assert.deepEqual( + AGGREGATE.transformArguments('index', '*', { + STEPS: [{ + type: 'GROUPBY', + REDUCE: { + type: 'FIRST_VALUE', + property: '@property' + } + }] + }), + ['FT.AGGREGATE', 'index', '*', 'GROUPBY', '0', 'REDUCE', 'FIRST_VALUE', '1', '@property'] + ); + }); + + describe('with BY', () => { + describe('without direction', () => { + it('string', () => { assert.deepEqual( - transformArguments('index', '*', { - STEPS: [{ - type: AggregateSteps.LIMIT, - from: 0, - size: 1 - }] - }), - ['FT.AGGREGATE', 'index', '*', 'LIMIT', '0', '1'] + AGGREGATE.transformArguments('index', '*', { + STEPS: [{ + type: 'GROUPBY', + REDUCE: { + type: 'FIRST_VALUE', + property: '@property', + BY: '@by' + } + }] + }), + ['FT.AGGREGATE', 'index', '*', 'GROUPBY', '0', 'REDUCE', 'FIRST_VALUE', '3', '@property', 'BY', '@by'] ); - }); + }); + - describe('FILTER', () => { + it('{ property: string }', () => { assert.deepEqual( - transformArguments('index', '*', { - STEPS: [{ - type: AggregateSteps.FILTER, - expression: '@field != ""' - }] - }), - ['FT.AGGREGATE', 'index', '*', 'FILTER', '@field != ""'] + AGGREGATE.transformArguments('index', '*', { + STEPS: [{ + type: 'GROUPBY', + REDUCE: { + type: 'FIRST_VALUE', + property: '@property', + BY: { + property: '@by' + } + } + }] + }), + ['FT.AGGREGATE', 'index', '*', 'GROUPBY', '0', 'REDUCE', 'FIRST_VALUE', '3', '@property', 'BY', '@by'] ); + }); }); - }); - it('with PARAMS', () => { - assert.deepEqual( - transformArguments('index', '*', { - PARAMS: { - param: 'value' + it('with direction', () => { + assert.deepEqual( + AGGREGATE.transformArguments('index', '*', { + STEPS: [{ + type: 'GROUPBY', + REDUCE: { + type: 'FIRST_VALUE', + property: '@property', + BY: { + property: '@by', + direction: 'ASC' + } } + }] }), - ['FT.AGGREGATE', 'index', '*', 'PARAMS', '2', 'param', 'value'] - ); + ['FT.AGGREGATE', 'index', '*', 'GROUPBY', '0', 'REDUCE', 'FIRST_VALUE', '4', '@property', 'BY', '@by', 'ASC'] + ); + }); + }); }); - it('with DIALECT', () => { - assert.deepEqual( - transformArguments('index', '*', { - DIALECT: 1 - }), - ['FT.AGGREGATE', 'index', '*', 'DIALECT', '1'] - ); + it('RANDOM_SAMPLE', () => { + assert.deepEqual( + AGGREGATE.transformArguments('index', '*', { + STEPS: [{ + type: 'GROUPBY', + REDUCE: { + type: 'RANDOM_SAMPLE', + property: '@property', + sampleSize: 1 + } + }] + }), + ['FT.AGGREGATE', 'index', '*', 'GROUPBY', '0', 'REDUCE', 'RANDOM_SAMPLE', '2', '@property', '1'] + ); + }); + }); + + describe('SORTBY', () => { + it('string', () => { + assert.deepEqual( + AGGREGATE.transformArguments('index', '*', { + STEPS: [{ + type: 'SORTBY', + BY: '@by' + }] + }), + ['FT.AGGREGATE', 'index', '*', 'SORTBY', '1', '@by'] + ); }); - it('with TIMEOUT', () => { - assert.deepEqual( - transformArguments('index', '*', { TIMEOUT: 10 }), - ['FT.AGGREGATE', 'index', '*', 'TIMEOUT', '10'] - ); + it('Array', () => { + assert.deepEqual( + AGGREGATE.transformArguments('index', '*', { + STEPS: [{ + type: 'SORTBY', + BY: ['@1', '@2'] + }] + }), + ['FT.AGGREGATE', 'index', '*', 'SORTBY', '2', '@1', '@2'] + ); }); - }); - testUtils.testWithClient('client.ft.aggregate', async client => { - await Promise.all([ - client.ft.create('index', { - field: SchemaFieldTypes.NUMERIC + it('with MAX', () => { + assert.deepEqual( + AGGREGATE.transformArguments('index', '*', { + STEPS: [{ + type: 'SORTBY', + BY: '@by', + MAX: 1 + }] }), - client.hSet('1', 'field', '1'), - client.hSet('2', 'field', '2') - ]); + ['FT.AGGREGATE', 'index', '*', 'SORTBY', '3', '@by', 'MAX', '1'] + ); + }); + }); + describe('APPLY', () => { assert.deepEqual( - await client.ft.aggregate('index', '*', { - STEPS: [{ - type: AggregateSteps.GROUPBY, - REDUCE: [{ - type: AggregateGroupByReducers.SUM, - property: '@field', - AS: 'sum' - }, { - type: AggregateGroupByReducers.AVG, - property: '@field', - AS: 'avg' - }] - }] - }), - { - total: 1, - results: [ - Object.create(null, { - sum: { - value: '3', - configurable: true, - enumerable: true - }, - avg: { - value: '1.5', - configurable: true, - enumerable: true - } - }) - ] - } + AGGREGATE.transformArguments('index', '*', { + STEPS: [{ + type: 'APPLY', + expression: '@field + 1', + AS: 'as' + }] + }), + ['FT.AGGREGATE', 'index', '*', 'APPLY', '@field + 1', 'AS', 'as'] + ); + }); + + describe('LIMIT', () => { + assert.deepEqual( + AGGREGATE.transformArguments('index', '*', { + STEPS: [{ + type: 'LIMIT', + from: 0, + size: 1 + }] + }), + ['FT.AGGREGATE', 'index', '*', 'LIMIT', '0', '1'] + ); + }); + + describe('FILTER', () => { + assert.deepEqual( + AGGREGATE.transformArguments('index', '*', { + STEPS: [{ + type: 'FILTER', + expression: '@field != ""' + }] + }), + ['FT.AGGREGATE', 'index', '*', 'FILTER', '@field != ""'] ); - }, GLOBAL.SERVERS.OPEN); + }); + }); + + it('with PARAMS', () => { + assert.deepEqual( + AGGREGATE.transformArguments('index', '*', { + PARAMS: { + param: 'value' + } + }), + ['FT.AGGREGATE', 'index', '*', 'PARAMS', '2', 'param', 'value'] + ); + }); + + it('with DIALECT', () => { + assert.deepEqual( + AGGREGATE.transformArguments('index', '*', { + DIALECT: 1 + }), + ['FT.AGGREGATE', 'index', '*', 'DIALECT', '1'] + ); + }); + + it('with TIMEOUT', () => { + assert.deepEqual( + AGGREGATE.transformArguments('index', '*', { TIMEOUT: 10 }), + ['FT.AGGREGATE', 'index', '*', 'TIMEOUT', '10'] + ); + }); + }); + + testUtils.testWithClient('client.ft.aggregate', async client => { + await Promise.all([ + client.ft.create('index', { + field: 'NUMERIC' + }), + client.hSet('1', 'field', '1'), + client.hSet('2', 'field', '2') + ]); + + assert.deepEqual( + await client.ft.aggregate('index', '*', { + STEPS: [{ + type: 'GROUPBY', + REDUCE: [{ + type: 'SUM', + property: '@field', + AS: 'sum' + }, { + type: 'AVG', + property: '@field', + AS: 'avg' + }] + }] + }), + { + total: 1, + results: [ + Object.create(null, { + sum: { + value: '3', + configurable: true, + enumerable: true + }, + avg: { + value: '1.5', + configurable: true, + enumerable: true + } + }) + ] + } + ); + }, GLOBAL.SERVERS.OPEN); }); diff --git a/packages/search/lib/commands/AGGREGATE.ts b/packages/search/lib/commands/AGGREGATE.ts index 0cab9b25d48..cb9652622ad 100644 --- a/packages/search/lib/commands/AGGREGATE.ts +++ b/packages/search/lib/commands/AGGREGATE.ts @@ -1,316 +1,329 @@ -import { RedisCommandArgument, RedisCommandArguments } from '@redis/client/dist/lib/commands'; -import { pushVerdictArgument, transformTuplesReply } from '@redis/client/dist/lib/commands/generic-transformers'; -import { Params, PropertyName, pushArgumentsWithLength, pushParamsArgs, pushSortByArguments, SortByProperty } from '.'; - -export enum AggregateSteps { - GROUPBY = 'GROUPBY', - SORTBY = 'SORTBY', - APPLY = 'APPLY', - LIMIT = 'LIMIT', - FILTER = 'FILTER' +import { ArrayReply, BlobStringReply, Command, MapReply, NumberReply, RedisArgument, ReplyUnion, TypeMapping, UnwrapReply } from '@redis/client/dist/lib/RESP/types'; +import { RediSearchProperty } from './CREATE'; +import { FtSearchParams, pushParamsArgument } from './SEARCH'; +import { pushVariadicArgument, transformTuplesReply } from '@redis/client/dist/lib/commands/generic-transformers'; + +type LoadField = RediSearchProperty | { + identifier: RediSearchProperty; + AS?: RedisArgument; } -interface AggregateStep { - type: T; +export const FT_AGGREGATE_STEPS = { + GROUPBY: 'GROUPBY', + SORTBY: 'SORTBY', + APPLY: 'APPLY', + LIMIT: 'LIMIT', + FILTER: 'FILTER' +} as const; + +type FT_AGGREGATE_STEPS = typeof FT_AGGREGATE_STEPS; + +export type FtAggregateStep = FT_AGGREGATE_STEPS[keyof FT_AGGREGATE_STEPS]; + +interface AggregateStep { + type: T; } -export enum AggregateGroupByReducers { - COUNT = 'COUNT', - COUNT_DISTINCT = 'COUNT_DISTINCT', - COUNT_DISTINCTISH = 'COUNT_DISTINCTISH', - SUM = 'SUM', - MIN = 'MIN', - MAX = 'MAX', - AVG = 'AVG', - STDDEV = 'STDDEV', - QUANTILE = 'QUANTILE', - TOLIST = 'TOLIST', - TO_LIST = 'TOLIST', - FIRST_VALUE = 'FIRST_VALUE', - RANDOM_SAMPLE = 'RANDOM_SAMPLE' +export const FT_AGGREGATE_GROUP_BY_REDUCERS = { + COUNT: 'COUNT', + COUNT_DISTINCT: 'COUNT_DISTINCT', + COUNT_DISTINCTISH: 'COUNT_DISTINCTISH', + SUM: 'SUM', + MIN: 'MIN', + MAX: 'MAX', + AVG: 'AVG', + STDDEV: 'STDDEV', + QUANTILE: 'QUANTILE', + TOLIST: 'TOLIST', + FIRST_VALUE: 'FIRST_VALUE', + RANDOM_SAMPLE: 'RANDOM_SAMPLE' +} as const; + +type FT_AGGREGATE_GROUP_BY_REDUCERS = typeof FT_AGGREGATE_GROUP_BY_REDUCERS; + +export type FtAggregateGroupByReducer = FT_AGGREGATE_GROUP_BY_REDUCERS[keyof FT_AGGREGATE_GROUP_BY_REDUCERS]; + +interface GroupByReducer { + type: T; + AS?: RedisArgument; } -interface GroupByReducer { - type: T; - AS?: string; +interface GroupByReducerWithProperty extends GroupByReducer { + property: RediSearchProperty; } -type CountReducer = GroupByReducer; +type CountReducer = GroupByReducer; -interface CountDistinctReducer extends GroupByReducer { - property: PropertyName; -} +type CountDistinctReducer = GroupByReducerWithProperty; -interface CountDistinctishReducer extends GroupByReducer { - property: PropertyName; -} +type CountDistinctishReducer = GroupByReducerWithProperty; -interface SumReducer extends GroupByReducer { - property: PropertyName; -} +type SumReducer = GroupByReducerWithProperty; -interface MinReducer extends GroupByReducer { - property: PropertyName; -} +type MinReducer = GroupByReducerWithProperty; -interface MaxReducer extends GroupByReducer { - property: PropertyName; -} +type MaxReducer = GroupByReducerWithProperty; -interface AvgReducer extends GroupByReducer { - property: PropertyName; -} +type AvgReducer = GroupByReducerWithProperty; -interface StdDevReducer extends GroupByReducer { - property: PropertyName; -} +type StdDevReducer = GroupByReducerWithProperty; -interface QuantileReducer extends GroupByReducer { - property: PropertyName; - quantile: number; +interface QuantileReducer extends GroupByReducerWithProperty { + quantile: number; } -interface ToListReducer extends GroupByReducer { - property: PropertyName; -} +type ToListReducer = GroupByReducerWithProperty; -interface FirstValueReducer extends GroupByReducer { - property: PropertyName; - BY?: PropertyName | { - property: PropertyName; - direction?: 'ASC' | 'DESC'; - }; +interface FirstValueReducer extends GroupByReducerWithProperty { + BY?: RediSearchProperty | { + property: RediSearchProperty; + direction?: 'ASC' | 'DESC'; + }; } -interface RandomSampleReducer extends GroupByReducer { - property: PropertyName; - sampleSize: number; +interface RandomSampleReducer extends GroupByReducerWithProperty { + sampleSize: number; } type GroupByReducers = CountReducer | CountDistinctReducer | CountDistinctishReducer | SumReducer | MinReducer | MaxReducer | AvgReducer | StdDevReducer | QuantileReducer | ToListReducer | FirstValueReducer | RandomSampleReducer; -interface GroupByStep extends AggregateStep { - properties?: PropertyName | Array; - REDUCE: GroupByReducers | Array; +interface GroupByStep extends AggregateStep { + properties?: RediSearchProperty | Array; + REDUCE: GroupByReducers | Array; } -interface SortStep extends AggregateStep { - BY: SortByProperty | Array; - MAX?: number; -} +type SortByProperty = RedisArgument | { + BY: RediSearchProperty; + DIRECTION?: 'ASC' | 'DESC'; +}; -interface ApplyStep extends AggregateStep { - expression: string; - AS: string; +interface SortStep extends AggregateStep { + BY: SortByProperty | Array; + MAX?: number; } -interface LimitStep extends AggregateStep { - from: number; - size: number; +interface ApplyStep extends AggregateStep { + expression: RedisArgument; + AS: RedisArgument; } -interface FilterStep extends AggregateStep { - expression: string; +interface LimitStep extends AggregateStep { + from: number; + size: number; } -type LoadField = PropertyName | { - identifier: PropertyName; - AS?: string; +interface FilterStep extends AggregateStep { + expression: RedisArgument; } -export interface AggregateOptions { - VERBATIM?: boolean; - ADDSCORES?: boolean; - LOAD?: LoadField | Array; - STEPS?: Array; - PARAMS?: Params; - DIALECT?: number; - TIMEOUT?: number; +export interface FtAggregateOptions { + VERBATIM?: boolean; + ADDSCORES?: boolean; + LOAD?: LoadField | Array; + TIMEOUT?: number; + STEPS?: Array; + PARAMS?: FtSearchParams; + DIALECT?: number; } -export const FIRST_KEY_INDEX = 1; +export type AggregateRawReply = [ + total: UnwrapReply, + ...results: UnwrapReply>> +]; -export const IS_READ_ONLY = true; +export interface AggregateReply { + total: number; + results: Array>; +}; + +export default { + FIRST_KEY_INDEX: undefined, + IS_READ_ONLY: false, + transformArguments(index: RedisArgument, query: RedisArgument, options?: FtAggregateOptions) { + const args = ['FT.AGGREGATE', index, query]; + + return pushAggregateOptions(args, options); + }, + transformReply: { + 2: (rawReply: AggregateRawReply, preserve?: any, typeMapping?: TypeMapping): AggregateReply => { + const results: Array> = []; + for (let i = 1; i < rawReply.length; i++) { + results.push( + transformTuplesReply(rawReply[i] as ArrayReply, preserve, typeMapping) + ); + } + + return { + total: Number(rawReply[0]), + results + }; + }, + 3: undefined as unknown as () => ReplyUnion + }, + unstableResp3: true +} as const satisfies Command; + +export function pushAggregateOptions(args: Array, options?: FtAggregateOptions) { + if (options?.VERBATIM) { + args.push('VERBATIM'); + } + + if (options?.ADDSCORES) { + args.push('ADDSCORES'); + } + + if (options?.LOAD) { + const length = args.push('LOAD', ''); + + if (Array.isArray(options.LOAD)) { + for (const load of options.LOAD) { + pushLoadField(args, load); + } + } else { + pushLoadField(args, options.LOAD); + } -export function transformArguments( - index: string, - query: string, - options?: AggregateOptions -): RedisCommandArguments { - return pushAggregatehOptions( - ['FT.AGGREGATE', index, query], - options - ); -} + args[length - 1] = (args.length - length).toString(); + } -export function pushAggregatehOptions( - args: RedisCommandArguments, - options?: AggregateOptions -): RedisCommandArguments { - if (options?.VERBATIM) { - args.push('VERBATIM'); - } + if (options?.TIMEOUT !== undefined) { + args.push('TIMEOUT', options.TIMEOUT.toString()); + } - if (options?.ADDSCORES) { - args.push('ADDSCORES'); - } + if (options?.STEPS) { + for (const step of options.STEPS) { + args.push(step.type); + switch (step.type) { + case FT_AGGREGATE_STEPS.GROUPBY: + if (!step.properties) { + args.push('0'); + } else { + pushVariadicArgument(args, step.properties); + } - if (options?.LOAD) { - args.push('LOAD'); - pushArgumentsWithLength(args, () => { - if (Array.isArray(options.LOAD)) { - for (const load of options.LOAD) { - pushLoadField(args, load); - } - } else { - pushLoadField(args, options.LOAD!); + if (Array.isArray(step.REDUCE)) { + for (const reducer of step.REDUCE) { + pushGroupByReducer(args, reducer); } - }); - } + } else { + pushGroupByReducer(args, step.REDUCE); + } - if (options?.STEPS) { - for (const step of options.STEPS) { - switch (step.type) { - case AggregateSteps.GROUPBY: - args.push('GROUPBY'); - if (!step.properties) { - args.push('0'); - } else { - pushVerdictArgument(args, step.properties); - } - - if (Array.isArray(step.REDUCE)) { - for (const reducer of step.REDUCE) { - pushGroupByReducer(args, reducer); - } - } else { - pushGroupByReducer(args, step.REDUCE); - } - - break; - - case AggregateSteps.SORTBY: - pushSortByArguments(args, 'SORTBY', step.BY); - - if (step.MAX) { - args.push('MAX', step.MAX.toString()); - } - - break; - - case AggregateSteps.APPLY: - args.push('APPLY', step.expression, 'AS', step.AS); - break; - - case AggregateSteps.LIMIT: - args.push('LIMIT', step.from.toString(), step.size.toString()); - break; - - case AggregateSteps.FILTER: - args.push('FILTER', step.expression); - break; + break; + + case FT_AGGREGATE_STEPS.SORTBY: + const length = args.push(''); + + if (Array.isArray(step.BY)) { + for (const by of step.BY) { + pushSortByProperty(args, by); } - } - } + } else { + pushSortByProperty(args, step.BY); + } - pushParamsArgs(args, options?.PARAMS); + if (step.MAX) { + args.push('MAX', step.MAX.toString()); + } - if (options?.DIALECT) { - args.push('DIALECT', options.DIALECT.toString()); - } + args[length - 1] = (args.length - length).toString(); + + break; - if (options?.TIMEOUT !== undefined) { - args.push('TIMEOUT', options.TIMEOUT.toString()); + case FT_AGGREGATE_STEPS.APPLY: + args.push(step.expression, 'AS', step.AS); + break; + + case FT_AGGREGATE_STEPS.LIMIT: + args.push(step.from.toString(), step.size.toString()); + break; + + case FT_AGGREGATE_STEPS.FILTER: + args.push(step.expression); + break; + } } + } + + pushParamsArgument(args, options?.PARAMS); - return args; + if (options?.DIALECT !== undefined) { + args.push('DIALECT', options.DIALECT.toString()); + } + + return args; } -function pushLoadField(args: RedisCommandArguments, toLoad: LoadField): void { - if (typeof toLoad === 'string') { - args.push(toLoad); - } else { - args.push(toLoad.identifier); +function pushLoadField(args: Array, toLoad: LoadField) { + if (typeof toLoad === 'string' || toLoad instanceof Buffer) { + args.push(toLoad); + } else { + args.push(toLoad.identifier); - if (toLoad.AS) { - args.push('AS', toLoad.AS); - } + if (toLoad.AS) { + args.push('AS', toLoad.AS); } + } } -function pushGroupByReducer(args: RedisCommandArguments, reducer: GroupByReducers): void { - args.push('REDUCE', reducer.type); - - switch (reducer.type) { - case AggregateGroupByReducers.COUNT: - args.push('0'); - break; - - case AggregateGroupByReducers.COUNT_DISTINCT: - case AggregateGroupByReducers.COUNT_DISTINCTISH: - case AggregateGroupByReducers.SUM: - case AggregateGroupByReducers.MIN: - case AggregateGroupByReducers.MAX: - case AggregateGroupByReducers.AVG: - case AggregateGroupByReducers.STDDEV: - case AggregateGroupByReducers.TOLIST: - args.push('1', reducer.property); - break; - - case AggregateGroupByReducers.QUANTILE: - args.push('2', reducer.property, reducer.quantile.toString()); - break; - - case AggregateGroupByReducers.FIRST_VALUE: { - pushArgumentsWithLength(args, () => { - args.push(reducer.property); - - if (reducer.BY) { - args.push('BY'); - if (typeof reducer.BY === 'string') { - args.push(reducer.BY); - } else { - args.push(reducer.BY.property); - - if (reducer.BY.direction) { - args.push(reducer.BY.direction); - } - } - } - }); - break; +function pushGroupByReducer(args: Array, reducer: GroupByReducers) { + args.push('REDUCE', reducer.type); + + switch (reducer.type) { + case FT_AGGREGATE_GROUP_BY_REDUCERS.COUNT: + args.push('0'); + break; + + case FT_AGGREGATE_GROUP_BY_REDUCERS.COUNT_DISTINCT: + case FT_AGGREGATE_GROUP_BY_REDUCERS.COUNT_DISTINCTISH: + case FT_AGGREGATE_GROUP_BY_REDUCERS.SUM: + case FT_AGGREGATE_GROUP_BY_REDUCERS.MIN: + case FT_AGGREGATE_GROUP_BY_REDUCERS.MAX: + case FT_AGGREGATE_GROUP_BY_REDUCERS.AVG: + case FT_AGGREGATE_GROUP_BY_REDUCERS.STDDEV: + case FT_AGGREGATE_GROUP_BY_REDUCERS.TOLIST: + args.push('1', reducer.property); + break; + + case FT_AGGREGATE_GROUP_BY_REDUCERS.QUANTILE: + args.push('2', reducer.property, reducer.quantile.toString()); + break; + + case FT_AGGREGATE_GROUP_BY_REDUCERS.FIRST_VALUE: { + const length = args.push('', reducer.property) - 1; + if (reducer.BY) { + args.push('BY'); + if (typeof reducer.BY === 'string' || reducer.BY instanceof Buffer) { + args.push(reducer.BY); + } else { + args.push(reducer.BY.property); + if (reducer.BY.direction) { + args.push(reducer.BY.direction); + } } + } - case AggregateGroupByReducers.RANDOM_SAMPLE: - args.push('2', reducer.property, reducer.sampleSize.toString()); - break; - } - - if (reducer.AS) { - args.push('AS', reducer.AS); + args[length - 1] = (args.length - length).toString(); + break; } -} -export type AggregateRawReply = [ - total: number, - ...results: Array> -]; + case FT_AGGREGATE_GROUP_BY_REDUCERS.RANDOM_SAMPLE: + args.push('2', reducer.property, reducer.sampleSize.toString()); + break; + } -export interface AggregateReply { - total: number; - results: Array>; + if (reducer.AS) { + args.push('AS', reducer.AS); + } } -export function transformReply(rawReply: AggregateRawReply): AggregateReply { - const results: Array> = []; - for (let i = 1; i < rawReply.length; i++) { - results.push( - transformTuplesReply(rawReply[i] as Array) - ); +function pushSortByProperty(args: Array, sortBy: SortByProperty) { + if (typeof sortBy === 'string' || sortBy instanceof Buffer) { + args.push(sortBy); + } else { + args.push(sortBy.BY); + if (sortBy.DIRECTION) { + args.push(sortBy.DIRECTION); } - - return { - total: rawReply[0], - results - }; + } } diff --git a/packages/search/lib/commands/AGGREGATE_WITHCURSOR.spec.ts b/packages/search/lib/commands/AGGREGATE_WITHCURSOR.spec.ts index 65396f3f790..9db3d945f97 100644 --- a/packages/search/lib/commands/AGGREGATE_WITHCURSOR.spec.ts +++ b/packages/search/lib/commands/AGGREGATE_WITHCURSOR.spec.ts @@ -1,37 +1,47 @@ -import { strict as assert } from 'assert'; +import { strict as assert } from 'node:assert'; import testUtils, { GLOBAL } from '../test-utils'; -import { transformArguments } from './AGGREGATE_WITHCURSOR'; -import { SchemaFieldTypes } from '.'; +import AGGREGATE_WITHCURSOR from './AGGREGATE_WITHCURSOR'; describe('AGGREGATE WITHCURSOR', () => { - describe('transformArguments', () => { - it('without options', () => { - assert.deepEqual( - transformArguments('index', '*'), - ['FT.AGGREGATE', 'index', '*', 'WITHCURSOR'] - ); - }); + describe('transformArguments', () => { + it('without options', () => { + assert.deepEqual( + AGGREGATE_WITHCURSOR.transformArguments('index', '*'), + ['FT.AGGREGATE', 'index', '*', 'WITHCURSOR'] + ); + }); + + it('with COUNT', () => { + assert.deepEqual( + AGGREGATE_WITHCURSOR.transformArguments('index', '*', { + COUNT: 1 + }), + ['FT.AGGREGATE', 'index', '*', 'WITHCURSOR', 'COUNT', '1'] + ); + }); - it('with COUNT', () => { - assert.deepEqual( - transformArguments('index', '*', { COUNT: 1 }), - ['FT.AGGREGATE', 'index', '*', 'WITHCURSOR', 'COUNT', '1'] - ); - }); + it('with MAXIDLE', () => { + assert.deepEqual( + AGGREGATE_WITHCURSOR.transformArguments('index', '*', { + MAXIDLE: 1 + }), + ['FT.AGGREGATE', 'index', '*', 'WITHCURSOR', 'MAXIDLE', '1'] + ); }); + }); - testUtils.testWithClient('client.ft.aggregateWithCursor', async client => { - await client.ft.create('index', { - field: SchemaFieldTypes.NUMERIC - }); + testUtils.testWithClient('client.ft.aggregateWithCursor', async client => { + await client.ft.create('index', { + field: 'NUMERIC' + }); - assert.deepEqual( - await client.ft.aggregateWithCursor('index', '*'), - { - total: 0, - results: [], - cursor: 0 - } - ); - }, GLOBAL.SERVERS.OPEN); + assert.deepEqual( + await client.ft.aggregateWithCursor('index', '*'), + { + total: 0, + results: [], + cursor: 0 + } + ); + }, GLOBAL.SERVERS.OPEN); }); diff --git a/packages/search/lib/commands/AGGREGATE_WITHCURSOR.ts b/packages/search/lib/commands/AGGREGATE_WITHCURSOR.ts index 63f6ee8f187..cffb86b8b44 100644 --- a/packages/search/lib/commands/AGGREGATE_WITHCURSOR.ts +++ b/packages/search/lib/commands/AGGREGATE_WITHCURSOR.ts @@ -1,44 +1,47 @@ -import { - AggregateOptions, - AggregateRawReply, - AggregateReply, - transformArguments as transformAggregateArguments, - transformReply as transformAggregateReply -} from './AGGREGATE'; - -export { FIRST_KEY_INDEX, IS_READ_ONLY } from './AGGREGATE'; - -interface AggregateWithCursorOptions extends AggregateOptions { - COUNT?: number; -} - -export function transformArguments( - index: string, - query: string, - options?: AggregateWithCursorOptions -) { - const args = transformAggregateArguments(index, query, options); - - args.push('WITHCURSOR'); - if (options?.COUNT) { - args.push('COUNT', options.COUNT.toString()); - } +import { RedisArgument, Command, ReplyUnion, NumberReply } from '@redis/client/dist/lib/RESP/types'; +import AGGREGATE, { AggregateRawReply, AggregateReply, FtAggregateOptions } from './AGGREGATE'; - return args; +export interface FtAggregateWithCursorOptions extends FtAggregateOptions { + COUNT?: number; + MAXIDLE?: number; } + type AggregateWithCursorRawReply = [ - result: AggregateRawReply, - cursor: number + result: AggregateRawReply, + cursor: NumberReply ]; -interface AggregateWithCursorReply extends AggregateReply { - cursor: number; +export interface AggregateWithCursorReply extends AggregateReply { + cursor: NumberReply; } -export function transformReply(reply: AggregateWithCursorRawReply): AggregateWithCursorReply { - return { - ...transformAggregateReply(reply[0]), +export default { + FIRST_KEY_INDEX: AGGREGATE.FIRST_KEY_INDEX, + IS_READ_ONLY: AGGREGATE.IS_READ_ONLY, + transformArguments(index: RedisArgument, query: RedisArgument, options?: FtAggregateWithCursorOptions) { + const args = AGGREGATE.transformArguments(index, query, options); + args.push('WITHCURSOR'); + + if (options?.COUNT !== undefined) { + args.push('COUNT', options.COUNT.toString()); + } + + if(options?.MAXIDLE !== undefined) { + args.push('MAXIDLE', options.MAXIDLE.toString()); + } + + return args; + }, + transformReply: { + 2: (reply: AggregateWithCursorRawReply): AggregateWithCursorReply => { + return { + ...AGGREGATE.transformReply[2](reply[0]), cursor: reply[1] - }; -} + }; + }, + 3: undefined as unknown as () => ReplyUnion + }, + unstableResp3: true +} as const satisfies Command; + diff --git a/packages/search/lib/commands/ALIASADD.spec.ts b/packages/search/lib/commands/ALIASADD.spec.ts index 7bb2452838b..3a5d02175f9 100644 --- a/packages/search/lib/commands/ALIASADD.spec.ts +++ b/packages/search/lib/commands/ALIASADD.spec.ts @@ -1,11 +1,24 @@ -import { strict as assert } from 'assert'; -import { transformArguments } from './ALIASADD'; +import { strict as assert } from 'node:assert'; +import testUtils, { GLOBAL } from '../test-utils'; +import ALIASADD from './ALIASADD'; +import { SCHEMA_FIELD_TYPE } from './CREATE'; -describe('ALIASADD', () => { - it('transformArguments', () => { - assert.deepEqual( - transformArguments('alias', 'index'), - ['FT.ALIASADD', 'alias', 'index'] - ); - }); +describe('FT.ALIASADD', () => { + it('transformArguments', () => { + assert.deepEqual( + ALIASADD.transformArguments('alias', 'index'), + ['FT.ALIASADD', 'alias', 'index'] + ); + }); + + testUtils.testWithClient('client.ft.aliasAdd', async client => { + const [, reply] = await Promise.all([ + client.ft.create('index', { + field: SCHEMA_FIELD_TYPE.TEXT + }), + client.ft.aliasAdd('alias', 'index') + ]); + + assert.equal(reply, 'OK'); + }, GLOBAL.SERVERS.OPEN); }); diff --git a/packages/search/lib/commands/ALIASADD.ts b/packages/search/lib/commands/ALIASADD.ts index 552c1add695..648e1fef97e 100644 --- a/packages/search/lib/commands/ALIASADD.ts +++ b/packages/search/lib/commands/ALIASADD.ts @@ -1,5 +1,10 @@ -export function transformArguments(name: string, index: string): Array { - return ['FT.ALIASADD', name, index]; -} +import { RedisArgument, SimpleStringReply, Command } from '@redis/client/dist/lib/RESP/types'; -export declare function transformReply(): 'OK'; +export default { + FIRST_KEY_INDEX: undefined, + IS_READ_ONLY: true, + transformArguments(alias: RedisArgument, index: RedisArgument) { + return ['FT.ALIASADD', alias, index]; + }, + transformReply: undefined as unknown as () => SimpleStringReply<'OK'> +} as const satisfies Command; diff --git a/packages/search/lib/commands/ALIASDEL.spec.ts b/packages/search/lib/commands/ALIASDEL.spec.ts index 5255ba835db..3842d01b145 100644 --- a/packages/search/lib/commands/ALIASDEL.spec.ts +++ b/packages/search/lib/commands/ALIASDEL.spec.ts @@ -1,11 +1,25 @@ -import { strict as assert } from 'assert'; -import { transformArguments } from './ALIASDEL'; +import { strict as assert } from 'node:assert'; +import testUtils, { GLOBAL } from '../test-utils'; +import ALIASDEL from './ALIASDEL'; +import { SCHEMA_FIELD_TYPE } from './CREATE'; -describe('ALIASDEL', () => { - it('transformArguments', () => { - assert.deepEqual( - transformArguments('alias', 'index'), - ['FT.ALIASDEL', 'alias', 'index'] - ); - }); +describe('FT.ALIASDEL', () => { + it('transformArguments', () => { + assert.deepEqual( + ALIASDEL.transformArguments('alias'), + ['FT.ALIASDEL', 'alias'] + ); + }); + + testUtils.testWithClient('client.ft.aliasAdd', async client => { + const [, , reply] = await Promise.all([ + client.ft.create('index', { + field: SCHEMA_FIELD_TYPE.TEXT + }), + client.ft.aliasAdd('alias', 'index'), + client.ft.aliasDel('alias') + ]); + + assert.equal(reply, 'OK'); + }, GLOBAL.SERVERS.OPEN); }); diff --git a/packages/search/lib/commands/ALIASDEL.ts b/packages/search/lib/commands/ALIASDEL.ts index 434b4df3dea..40cc45a19de 100644 --- a/packages/search/lib/commands/ALIASDEL.ts +++ b/packages/search/lib/commands/ALIASDEL.ts @@ -1,5 +1,10 @@ -export function transformArguments(name: string, index: string): Array { - return ['FT.ALIASDEL', name, index]; -} +import { RedisArgument, SimpleStringReply, Command } from '@redis/client/dist/lib/RESP/types'; -export declare function transformReply(): 'OK'; +export default { + FIRST_KEY_INDEX: undefined, + IS_READ_ONLY: true, + transformArguments(alias: RedisArgument) { + return ['FT.ALIASDEL', alias]; + }, + transformReply: undefined as unknown as () => SimpleStringReply<'OK'> +} as const satisfies Command; diff --git a/packages/search/lib/commands/ALIASUPDATE.spec.ts b/packages/search/lib/commands/ALIASUPDATE.spec.ts index 79421b1a20d..a0e7431af6b 100644 --- a/packages/search/lib/commands/ALIASUPDATE.spec.ts +++ b/packages/search/lib/commands/ALIASUPDATE.spec.ts @@ -1,11 +1,24 @@ -import { strict as assert } from 'assert'; -import { transformArguments } from './ALIASUPDATE'; +import { strict as assert } from 'node:assert'; +import testUtils, { GLOBAL } from '../test-utils'; +import ALIASUPDATE from './ALIASUPDATE'; +import { SCHEMA_FIELD_TYPE } from './CREATE'; -describe('ALIASUPDATE', () => { - it('transformArguments', () => { - assert.deepEqual( - transformArguments('alias', 'index'), - ['FT.ALIASUPDATE', 'alias', 'index'] - ); - }); +describe('FT.ALIASUPDATE', () => { + it('transformArguments', () => { + assert.deepEqual( + ALIASUPDATE.transformArguments('alias', 'index'), + ['FT.ALIASUPDATE', 'alias', 'index'] + ); + }); + + testUtils.testWithClient('client.ft.aliasUpdate', async client => { + const [, reply] = await Promise.all([ + client.ft.create('index', { + field: SCHEMA_FIELD_TYPE.TEXT + }), + client.ft.aliasUpdate('alias', 'index') + ]); + + assert.equal(reply, 'OK'); + }, GLOBAL.SERVERS.OPEN); }); diff --git a/packages/search/lib/commands/ALIASUPDATE.ts b/packages/search/lib/commands/ALIASUPDATE.ts index ac64ef57c3f..e2b72cfe649 100644 --- a/packages/search/lib/commands/ALIASUPDATE.ts +++ b/packages/search/lib/commands/ALIASUPDATE.ts @@ -1,5 +1,10 @@ -export function transformArguments(name: string, index: string): Array { - return ['FT.ALIASUPDATE', name, index]; -} +import { SimpleStringReply, Command, RedisArgument } from '@redis/client/dist/lib/RESP/types'; -export declare function transformReply(): 'OK'; +export default { + FIRST_KEY_INDEX: undefined, + IS_READ_ONLY: true, + transformArguments(alias: RedisArgument, index: RedisArgument) { + return ['FT.ALIASUPDATE', alias, index]; + }, + transformReply: undefined as unknown as () => SimpleStringReply<'OK'> +} as const satisfies Command; diff --git a/packages/search/lib/commands/ALTER.spec.ts b/packages/search/lib/commands/ALTER.spec.ts index e9724757ad7..6cac0be40c8 100644 --- a/packages/search/lib/commands/ALTER.spec.ts +++ b/packages/search/lib/commands/ALTER.spec.ts @@ -1,37 +1,35 @@ -import { strict as assert } from 'assert'; +import { strict as assert } from 'node:assert'; import testUtils, { GLOBAL } from '../test-utils'; -import { transformArguments } from './ALTER'; -import { SchemaFieldTypes } from '.'; +import ALTER from './ALTER'; +import { SCHEMA_FIELD_TYPE } from './CREATE'; -describe('ALTER', () => { - describe('transformArguments', () => { - it('with NOINDEX', () => { - assert.deepEqual( - transformArguments('index', { - field: { - type: SchemaFieldTypes.TEXT, - NOINDEX: true, - SORTABLE: 'UNF', - AS: 'text' - } - }), - ['FT.ALTER', 'index', 'SCHEMA', 'ADD', 'field', 'AS', 'text', 'TEXT', 'SORTABLE', 'UNF', 'NOINDEX'] - ); - }); +describe('FT.ALTER', () => { + describe('transformArguments', () => { + it('with NOINDEX', () => { + assert.deepEqual( + ALTER.transformArguments('index', { + field: { + type: SCHEMA_FIELD_TYPE.TEXT, + NOINDEX: true, + SORTABLE: 'UNF', + AS: 'text' + } + }), + ['FT.ALTER', 'index', 'SCHEMA', 'ADD', 'field', 'AS', 'text', 'TEXT', 'SORTABLE', 'UNF', 'NOINDEX'] + ); }); + }); - testUtils.testWithClient('client.ft.create', async client => { - await Promise.all([ - client.ft.create('index', { - title: SchemaFieldTypes.TEXT - }), - ]); + testUtils.testWithClient('client.ft.create', async client => { + const [, reply] = await Promise.all([ + client.ft.create('index', { + title: SCHEMA_FIELD_TYPE.TEXT + }), + client.ft.alter('index', { + body: SCHEMA_FIELD_TYPE.TEXT + }) + ]); - assert.equal( - await client.ft.alter('index', { - body: SchemaFieldTypes.TEXT - }), - 'OK' - ); - }, GLOBAL.SERVERS.OPEN); + assert.equal(reply, 'OK'); + }, GLOBAL.SERVERS.OPEN); }); diff --git a/packages/search/lib/commands/ALTER.ts b/packages/search/lib/commands/ALTER.ts index bb4c5202c65..d5587b2397c 100644 --- a/packages/search/lib/commands/ALTER.ts +++ b/packages/search/lib/commands/ALTER.ts @@ -1,10 +1,13 @@ -import { RediSearchSchema, pushSchema } from '.'; +import { RedisArgument, SimpleStringReply, Command } from '@redis/client/dist/lib/RESP/types'; +import { RediSearchSchema, pushSchema } from './CREATE'; -export function transformArguments(index: string, schema: RediSearchSchema): Array { +export default { + FIRST_KEY_INDEX: undefined, + IS_READ_ONLY: true, + transformArguments(index: RedisArgument, schema: RediSearchSchema) { const args = ['FT.ALTER', index, 'SCHEMA', 'ADD']; pushSchema(args, schema); - return args; -} - -export declare function transformReply(): 'OK'; + }, + transformReply: undefined as unknown as () => SimpleStringReply<'OK'> +} as const satisfies Command; diff --git a/packages/search/lib/commands/CONFIG_GET.spec.ts b/packages/search/lib/commands/CONFIG_GET.spec.ts index 8614f443426..7ef2a3536b9 100644 --- a/packages/search/lib/commands/CONFIG_GET.spec.ts +++ b/packages/search/lib/commands/CONFIG_GET.spec.ts @@ -1,25 +1,25 @@ -import { strict as assert } from 'assert'; +import { strict as assert } from 'node:assert'; import testUtils, { GLOBAL } from '../test-utils'; -import { transformArguments } from './CONFIG_GET'; +import CONFIG_GET from './CONFIG_GET'; -describe('CONFIG GET', () => { - it('transformArguments', () => { - assert.deepEqual( - transformArguments('TIMEOUT'), - ['FT.CONFIG', 'GET', 'TIMEOUT'] - ); - }); +describe('FT.CONFIG GET', () => { + it('transformArguments', () => { + assert.deepEqual( + CONFIG_GET.transformArguments('TIMEOUT'), + ['FT.CONFIG', 'GET', 'TIMEOUT'] + ); + }); - testUtils.testWithClient('client.ft.configGet', async client => { - assert.deepEqual( - await client.ft.configGet('TIMEOUT'), - Object.create(null, { - TIMEOUT: { - value: '500', - configurable: true, - enumerable: true - } - }) - ); - }, GLOBAL.SERVERS.OPEN); + testUtils.testWithClient('client.ft.configGet', async client => { + assert.deepEqual( + await client.ft.configGet('TIMEOUT'), + Object.create(null, { + TIMEOUT: { + value: '500', + configurable: true, + enumerable: true + } + }) + ); + }, GLOBAL.SERVERS.OPEN); }); diff --git a/packages/search/lib/commands/CONFIG_GET.ts b/packages/search/lib/commands/CONFIG_GET.ts index fbf1f1164b9..f96461e8694 100644 --- a/packages/search/lib/commands/CONFIG_GET.ts +++ b/packages/search/lib/commands/CONFIG_GET.ts @@ -1,16 +1,18 @@ -export function transformArguments(option: string) { - return ['FT.CONFIG', 'GET', option]; -} - -interface ConfigGetReply { - [option: string]: string | null; -} +import { ArrayReply, TuplesReply, BlobStringReply, NullReply, UnwrapReply, Command } from '@redis/client/dist/lib/RESP/types'; -export function transformReply(rawReply: Array<[string, string | null]>): ConfigGetReply { - const transformedReply: ConfigGetReply = Object.create(null); - for (const [key, value] of rawReply) { - transformedReply[key] = value; +export default { + FIRST_KEY_INDEX: undefined, + IS_READ_ONLY: true, + transformArguments(option: string) { + return ['FT.CONFIG', 'GET', option]; + }, + transformReply(reply: UnwrapReply>>) { + const transformedReply: Record = Object.create(null); + for (const item of reply) { + const [key, value] = item as unknown as UnwrapReply; + transformedReply[key.toString()] = value; } return transformedReply; -} + } +} as const satisfies Command; diff --git a/packages/search/lib/commands/CONFIG_SET.spec.ts b/packages/search/lib/commands/CONFIG_SET.spec.ts index 59cb63a3d8e..3b20f2eac5d 100644 --- a/packages/search/lib/commands/CONFIG_SET.spec.ts +++ b/packages/search/lib/commands/CONFIG_SET.spec.ts @@ -1,12 +1,19 @@ -import { strict as assert } from 'assert'; +import { strict as assert } from 'node:assert'; import testUtils, { GLOBAL } from '../test-utils'; -import { transformArguments } from './CONFIG_SET'; +import CONFIG_SET from './CONFIG_SET'; -describe('CONFIG SET', () => { - it('transformArguments', () => { - assert.deepEqual( - transformArguments('TIMEOUT', '500'), - ['FT.CONFIG', 'SET', 'TIMEOUT', '500'] - ); - }); +describe('FT.CONFIG SET', () => { + it('transformArguments', () => { + assert.deepEqual( + CONFIG_SET.transformArguments('TIMEOUT', '500'), + ['FT.CONFIG', 'SET', 'TIMEOUT', '500'] + ); + }); + + testUtils.testWithClient('client.ft.configSet', async client => { + assert.deepEqual( + await client.ft.configSet('TIMEOUT', '500'), + 'OK' + ); + }, GLOBAL.SERVERS.OPEN); }); diff --git a/packages/search/lib/commands/CONFIG_SET.ts b/packages/search/lib/commands/CONFIG_SET.ts index 93b76d79edf..ac001bf68a6 100644 --- a/packages/search/lib/commands/CONFIG_SET.ts +++ b/packages/search/lib/commands/CONFIG_SET.ts @@ -1,5 +1,14 @@ -export function transformArguments(option: string, value: string): Array { - return ['FT.CONFIG', 'SET', option, value]; -} +import { RedisArgument, SimpleStringReply, Command } from '@redis/client/dist/lib/RESP/types'; -export declare function transformReply(): 'OK'; +// using `string & {}` to avoid TS widening the type to `string` +// TODO +type FtConfigProperties = 'a' | 'b' | (string & {}) | Buffer; + +export default { + FIRST_KEY_INDEX: undefined, + IS_READ_ONLY: true, + transformArguments(property: FtConfigProperties, value: RedisArgument) { + return ['FT.CONFIG', 'SET', property, value]; + }, + transformReply: undefined as unknown as () => SimpleStringReply<'OK'> +} as const satisfies Command; diff --git a/packages/search/lib/commands/CREATE.spec.ts b/packages/search/lib/commands/CREATE.spec.ts index 50c5c011c89..bc48691bd58 100644 --- a/packages/search/lib/commands/CREATE.spec.ts +++ b/packages/search/lib/commands/CREATE.spec.ts @@ -1,490 +1,475 @@ -import { strict as assert } from 'assert'; +import { strict as assert } from 'node:assert'; import testUtils, { GLOBAL } from '../test-utils'; -import { transformArguments } from './CREATE'; -import { SchemaFieldTypes, SchemaTextFieldPhonetics, RedisSearchLanguages, VectorAlgorithms, SCHEMA_GEO_SHAPE_COORD_SYSTEM } from '.'; +import CREATE, { SCHEMA_FIELD_TYPE, SCHEMA_TEXT_FIELD_PHONETIC, SCHEMA_VECTOR_FIELD_ALGORITHM, REDISEARCH_LANGUAGE } from './CREATE'; + +describe('FT.CREATE', () => { + describe('transformArguments', () => { + it('simple', () => { + assert.deepEqual( + CREATE.transformArguments('index', {}), + ['FT.CREATE', 'index', 'SCHEMA'] + ); + }); -describe('CREATE', () => { - describe('transformArguments', () => { - it('simple', () => { - assert.deepEqual( - transformArguments('index', {}), - ['FT.CREATE', 'index', 'SCHEMA'] - ); + describe('with fields', () => { + describe('TEXT', () => { + it('without options', () => { + assert.deepEqual( + CREATE.transformArguments('index', { + field: SCHEMA_FIELD_TYPE.TEXT + }), + ['FT.CREATE', 'index', 'SCHEMA', 'field', 'TEXT'] + ); }); - describe('with fields', () => { - describe('TEXT', () => { - it('without options', () => { - assert.deepEqual( - transformArguments('index', { - field: SchemaFieldTypes.TEXT - }), - ['FT.CREATE', 'index', 'SCHEMA', 'field', 'TEXT'] - ); - }); - - it('with NOSTEM', () => { - assert.deepEqual( - transformArguments('index', { - field: { - type: SchemaFieldTypes.TEXT, - NOSTEM: true - } - }), - ['FT.CREATE', 'index', 'SCHEMA', 'field', 'TEXT', 'NOSTEM'] - ); - }); - - it('with WEIGHT', () => { - assert.deepEqual( - transformArguments('index', { - field: { - type: SchemaFieldTypes.TEXT, - WEIGHT: 1 - } - }), - ['FT.CREATE', 'index', 'SCHEMA', 'field', 'TEXT', 'WEIGHT', '1'] - ); - }); - - it('with PHONETIC', () => { - assert.deepEqual( - transformArguments('index', { - field: { - type: SchemaFieldTypes.TEXT, - PHONETIC: SchemaTextFieldPhonetics.DM_EN - } - }), - ['FT.CREATE', 'index', 'SCHEMA', 'field', 'TEXT', 'PHONETIC', SchemaTextFieldPhonetics.DM_EN] - ); - }); - - it('with WITHSUFFIXTRIE', () => { - assert.deepEqual( - transformArguments('index', { - field: { - type: SchemaFieldTypes.TEXT, - WITHSUFFIXTRIE: true - } - }), - ['FT.CREATE', 'index', 'SCHEMA', 'field', 'TEXT', 'WITHSUFFIXTRIE'] - ); - }); - - it('with INDEXEMPTY', () => { - assert.deepEqual( - transformArguments('index', { - field: { - type: SchemaFieldTypes.TEXT, - INDEXEMPTY: true - } - }), - ['FT.CREATE', 'index', 'SCHEMA', 'field', 'TEXT', 'INDEXEMPTY'] - ); - }); - }); - - it('NUMERIC', () => { - assert.deepEqual( - transformArguments('index', { - field: SchemaFieldTypes.NUMERIC - }), - ['FT.CREATE', 'index', 'SCHEMA', 'field', 'NUMERIC'] - ); - }); - - it('GEO', () => { - assert.deepEqual( - transformArguments('index', { - field: SchemaFieldTypes.GEO - }), - ['FT.CREATE', 'index', 'SCHEMA', 'field', 'GEO'] - ); - }); - - describe('TAG', () => { - describe('without options', () => { - it('SchemaFieldTypes.TAG', () => { - assert.deepEqual( - transformArguments('index', { - field: SchemaFieldTypes.TAG - }), - ['FT.CREATE', 'index', 'SCHEMA', 'field', 'TAG'] - ); - }); - - it('{ type: SchemaFieldTypes.TAG }', () => { - assert.deepEqual( - transformArguments('index', { - field: { - type: SchemaFieldTypes.TAG - } - }), - ['FT.CREATE', 'index', 'SCHEMA', 'field', 'TAG'] - ); - }); - }); - - it('with SEPARATOR', () => { - assert.deepEqual( - transformArguments('index', { - field: { - type: SchemaFieldTypes.TAG, - SEPARATOR: 'separator' - } - }), - ['FT.CREATE', 'index', 'SCHEMA', 'field', 'TAG', 'SEPARATOR', 'separator'] - ); - }); - - it('with CASESENSITIVE', () => { - assert.deepEqual( - transformArguments('index', { - field: { - type: SchemaFieldTypes.TAG, - CASESENSITIVE: true - } - }), - ['FT.CREATE', 'index', 'SCHEMA', 'field', 'TAG', 'CASESENSITIVE'] - ); - }); - - it('with WITHSUFFIXTRIE', () => { - assert.deepEqual( - transformArguments('index', { - field: { - type: SchemaFieldTypes.TAG, - WITHSUFFIXTRIE: true - } - }), - ['FT.CREATE', 'index', 'SCHEMA', 'field', 'TAG', 'WITHSUFFIXTRIE'] - ); - }); - - it('with INDEXEMPTY', () => { - assert.deepEqual( - transformArguments('index', { - field: { - type: SchemaFieldTypes.TAG, - INDEXEMPTY: true - } - }), - ['FT.CREATE', 'index', 'SCHEMA', 'field', 'TAG', 'INDEXEMPTY'] - ); - }); - }); - - describe('VECTOR', () => { - it('Flat algorithm', () => { - assert.deepEqual( - transformArguments('index', { - field: { - type: SchemaFieldTypes.VECTOR, - ALGORITHM: VectorAlgorithms.FLAT, - TYPE: 'FLOAT32', - DIM: 2, - DISTANCE_METRIC: 'L2', - INITIAL_CAP: 1000000, - BLOCK_SIZE: 1000 - } - }), - [ - 'FT.CREATE', 'index', 'SCHEMA', 'field', 'VECTOR', 'FLAT', '10', 'TYPE', - 'FLOAT32', 'DIM', '2', 'DISTANCE_METRIC', 'L2', 'INITIAL_CAP', '1000000', - 'BLOCK_SIZE', '1000' - ] - ); - }); - - it('HNSW algorithm', () => { - assert.deepEqual( - transformArguments('index', { - field: { - type: SchemaFieldTypes.VECTOR, - ALGORITHM: VectorAlgorithms.HNSW, - TYPE: 'FLOAT32', - DIM: 2, - DISTANCE_METRIC: 'L2', - INITIAL_CAP: 1000000, - M: 40, - EF_CONSTRUCTION: 250, - EF_RUNTIME: 20 - } - }), - [ - 'FT.CREATE', 'index', 'SCHEMA', 'field', 'VECTOR', 'HNSW', '14', 'TYPE', - 'FLOAT32', 'DIM', '2', 'DISTANCE_METRIC', 'L2', 'INITIAL_CAP', '1000000', - 'M', '40', 'EF_CONSTRUCTION', '250', 'EF_RUNTIME', '20' - ] - ); - }); - }); - - describe('GEOSHAPE', () => { - describe('without options', () => { - it('SCHEMA_FIELD_TYPE.GEOSHAPE', () => { - assert.deepEqual( - transformArguments('index', { - field: SchemaFieldTypes.GEOSHAPE - }), - ['FT.CREATE', 'index', 'SCHEMA', 'field', 'GEOSHAPE'] - ); - }); - - it('{ type: SCHEMA_FIELD_TYPE.GEOSHAPE }', () => { - assert.deepEqual( - transformArguments('index', { - field: { - type: SchemaFieldTypes.GEOSHAPE - } - }), - ['FT.CREATE', 'index', 'SCHEMA', 'field', 'GEOSHAPE'] - ); - }); - }); - - it('with COORD_SYSTEM', () => { - assert.deepEqual( - transformArguments('index', { - field: { - type: SchemaFieldTypes.GEOSHAPE, - COORD_SYSTEM: SCHEMA_GEO_SHAPE_COORD_SYSTEM.SPHERICAL - } - }), - ['FT.CREATE', 'index', 'SCHEMA', 'field', 'GEOSHAPE', 'COORD_SYSTEM', 'SPHERICAL'] - ); - }); - }); - - describe('with generic options', () => { - it('with AS', () => { - assert.deepEqual( - transformArguments('index', { - field: { - type: SchemaFieldTypes.TEXT, - AS: 'as' - } - }), - ['FT.CREATE', 'index', 'SCHEMA', 'field', 'AS', 'as', 'TEXT'] - ); - }); - - describe('with SORTABLE', () => { - it('true', () => { - assert.deepEqual( - transformArguments('index', { - field: { - type: SchemaFieldTypes.TEXT, - SORTABLE: true - } - }), - ['FT.CREATE', 'index', 'SCHEMA', 'field', 'TEXT', 'SORTABLE'] - ); - }); - - it('UNF', () => { - assert.deepEqual( - transformArguments('index', { - field: { - type: SchemaFieldTypes.TEXT, - SORTABLE: 'UNF' - } - }), - ['FT.CREATE', 'index', 'SCHEMA', 'field', 'TEXT', 'SORTABLE', 'UNF'] - ); - }); - }); - - it('with NOINDEX', () => { - assert.deepEqual( - transformArguments('index', { - field: { - type: SchemaFieldTypes.TEXT, - NOINDEX: true - } - }), - ['FT.CREATE', 'index', 'SCHEMA', 'field', 'TEXT', 'NOINDEX'] - ); - }); - - it('with INDEXMISSING', () => { - assert.deepEqual( - transformArguments('index', { - field: { - type: SchemaFieldTypes.TEXT, - INDEXMISSING: true - } - }), - ['FT.CREATE', 'index', 'SCHEMA', 'field', 'TEXT', 'INDEXMISSING'] - ); - }); - }); + it('with NOSTEM', () => { + assert.deepEqual( + CREATE.transformArguments('index', { + field: { + type: SCHEMA_FIELD_TYPE.TEXT, + NOSTEM: true + } + }), + ['FT.CREATE', 'index', 'SCHEMA', 'field', 'TEXT', 'NOSTEM'] + ); }); - it('with ON', () => { - assert.deepEqual( - transformArguments('index', {}, { - ON: 'HASH' - }), - ['FT.CREATE', 'index', 'ON', 'HASH', 'SCHEMA'] - ); + it('with WEIGHT', () => { + assert.deepEqual( + CREATE.transformArguments('index', { + field: { + type: SCHEMA_FIELD_TYPE.TEXT, + WEIGHT: 1 + } + }), + ['FT.CREATE', 'index', 'SCHEMA', 'field', 'TEXT', 'WEIGHT', '1'] + ); }); - describe('with PREFIX', () => { - it('string', () => { - assert.deepEqual( - transformArguments('index', {}, { - PREFIX: 'prefix' - }), - ['FT.CREATE', 'index', 'PREFIX', '1', 'prefix', 'SCHEMA'] - ); - }); - - it('Array', () => { - assert.deepEqual( - transformArguments('index', {}, { - PREFIX: ['1', '2'] - }), - ['FT.CREATE', 'index', 'PREFIX', '2', '1', '2', 'SCHEMA'] - ); - }); + it('with PHONETIC', () => { + assert.deepEqual( + CREATE.transformArguments('index', { + field: { + type: SCHEMA_FIELD_TYPE.TEXT, + PHONETIC: SCHEMA_TEXT_FIELD_PHONETIC.DM_EN + } + }), + ['FT.CREATE', 'index', 'SCHEMA', 'field', 'TEXT', 'PHONETIC', SCHEMA_TEXT_FIELD_PHONETIC.DM_EN] + ); }); - it('with FILTER', () => { - assert.deepEqual( - transformArguments('index', {}, { - FILTER: '@field != ""' - }), - ['FT.CREATE', 'index', 'FILTER', '@field != ""', 'SCHEMA'] - ); + it('with WITHSUFFIXTRIE', () => { + assert.deepEqual( + CREATE.transformArguments('index', { + field: { + type: SCHEMA_FIELD_TYPE.TEXT, + WITHSUFFIXTRIE: true + } + }), + ['FT.CREATE', 'index', 'SCHEMA', 'field', 'TEXT', 'WITHSUFFIXTRIE'] + ); }); + }); + + it('NUMERIC', () => { + assert.deepEqual( + CREATE.transformArguments('index', { + field: SCHEMA_FIELD_TYPE.NUMERIC + }), + ['FT.CREATE', 'index', 'SCHEMA', 'field', 'NUMERIC'] + ); + }); + + it('GEO', () => { + assert.deepEqual( + CREATE.transformArguments('index', { + field: SCHEMA_FIELD_TYPE.GEO + }), + ['FT.CREATE', 'index', 'SCHEMA', 'field', 'GEO'] + ); + }); - it('with LANGUAGE', () => { + describe('TAG', () => { + describe('without options', () => { + it('SCHEMA_FIELD_TYPE.TAG', () => { assert.deepEqual( - transformArguments('index', {}, { - LANGUAGE: RedisSearchLanguages.ARABIC - }), - ['FT.CREATE', 'index', 'LANGUAGE', RedisSearchLanguages.ARABIC, 'SCHEMA'] + CREATE.transformArguments('index', { + field: SCHEMA_FIELD_TYPE.TAG + }), + ['FT.CREATE', 'index', 'SCHEMA', 'field', 'TAG'] ); - }); + }); - it('with LANGUAGE_FIELD', () => { + it('{ type: SCHEMA_FIELD_TYPE.TAG }', () => { assert.deepEqual( - transformArguments('index', {}, { - LANGUAGE_FIELD: '@field' - }), - ['FT.CREATE', 'index', 'LANGUAGE_FIELD', '@field', 'SCHEMA'] + CREATE.transformArguments('index', { + field: { + type: SCHEMA_FIELD_TYPE.TAG + } + }), + ['FT.CREATE', 'index', 'SCHEMA', 'field', 'TAG'] ); + }); }); - it('with SCORE', () => { - assert.deepEqual( - transformArguments('index', {}, { - SCORE: 1 - }), - ['FT.CREATE', 'index', 'SCORE', '1', 'SCHEMA'] - ); + it('with SEPARATOR', () => { + assert.deepEqual( + CREATE.transformArguments('index', { + field: { + type: SCHEMA_FIELD_TYPE.TAG, + SEPARATOR: 'separator' + } + }), + ['FT.CREATE', 'index', 'SCHEMA', 'field', 'TAG', 'SEPARATOR', 'separator'] + ); }); - it('with SCORE_FIELD', () => { - assert.deepEqual( - transformArguments('index', {}, { - SCORE_FIELD: '@field' - }), - ['FT.CREATE', 'index', 'SCORE_FIELD', '@field', 'SCHEMA'] - ); + it('with CASESENSITIVE', () => { + assert.deepEqual( + CREATE.transformArguments('index', { + field: { + type: SCHEMA_FIELD_TYPE.TAG, + CASESENSITIVE: true + } + }), + ['FT.CREATE', 'index', 'SCHEMA', 'field', 'TAG', 'CASESENSITIVE'] + ); }); - it('with MAXTEXTFIELDS', () => { - assert.deepEqual( - transformArguments('index', {}, { - MAXTEXTFIELDS: true - }), - ['FT.CREATE', 'index', 'MAXTEXTFIELDS', 'SCHEMA'] - ); + it('with WITHSUFFIXTRIE', () => { + assert.deepEqual( + CREATE.transformArguments('index', { + field: { + type: SCHEMA_FIELD_TYPE.TAG, + WITHSUFFIXTRIE: true + } + }), + ['FT.CREATE', 'index', 'SCHEMA', 'field', 'TAG', 'WITHSUFFIXTRIE'] + ); }); - it('with TEMPORARY', () => { - assert.deepEqual( - transformArguments('index', {}, { - TEMPORARY: 1 - }), - ['FT.CREATE', 'index', 'TEMPORARY', '1', 'SCHEMA'] - ); + it('with INDEXEMPTY', () => { + assert.deepEqual( + CREATE.transformArguments('index', { + field: { + type: SCHEMA_FIELD_TYPE.TAG, + INDEXEMPTY: true + } + }), + ['FT.CREATE', 'index', 'SCHEMA', 'field', 'TAG', 'INDEXEMPTY'] + ); }); - - it('with NOOFFSETS', () => { - assert.deepEqual( - transformArguments('index', {}, { - NOOFFSETS: true - }), - ['FT.CREATE', 'index', 'NOOFFSETS', 'SCHEMA'] - ); + }); + + describe('VECTOR', () => { + it('Flat algorithm', () => { + assert.deepEqual( + CREATE.transformArguments('index', { + field: { + type: SCHEMA_FIELD_TYPE.VECTOR, + ALGORITHM: SCHEMA_VECTOR_FIELD_ALGORITHM.FLAT, + TYPE: 'FLOAT32', + DIM: 2, + DISTANCE_METRIC: 'L2', + INITIAL_CAP: 1000000, + BLOCK_SIZE: 1000 + } + }), + [ + 'FT.CREATE', 'index', 'SCHEMA', 'field', 'VECTOR', 'FLAT', '10', 'TYPE', + 'FLOAT32', 'DIM', '2', 'DISTANCE_METRIC', 'L2', 'INITIAL_CAP', '1000000', + 'BLOCK_SIZE', '1000' + ] + ); }); - it('with NOHL', () => { - assert.deepEqual( - transformArguments('index', {}, { - NOHL: true - }), - ['FT.CREATE', 'index', 'NOHL', 'SCHEMA'] - ); + it('HNSW algorithm', () => { + assert.deepEqual( + CREATE.transformArguments('index', { + field: { + type: SCHEMA_FIELD_TYPE.VECTOR, + ALGORITHM: SCHEMA_VECTOR_FIELD_ALGORITHM.HNSW, + TYPE: 'FLOAT32', + DIM: 2, + DISTANCE_METRIC: 'L2', + INITIAL_CAP: 1000000, + M: 40, + EF_CONSTRUCTION: 250, + EF_RUNTIME: 20 + } + }), + [ + 'FT.CREATE', 'index', 'SCHEMA', 'field', 'VECTOR', 'HNSW', '14', 'TYPE', + 'FLOAT32', 'DIM', '2', 'DISTANCE_METRIC', 'L2', 'INITIAL_CAP', '1000000', + 'M', '40', 'EF_CONSTRUCTION', '250', 'EF_RUNTIME', '20' + ] + ); }); + }); - it('with NOFIELDS', () => { + describe('GEOSHAPE', () => { + describe('without options', () => { + it('SCHEMA_FIELD_TYPE.GEOSHAPE', () => { assert.deepEqual( - transformArguments('index', {}, { - NOFIELDS: true - }), - ['FT.CREATE', 'index', 'NOFIELDS', 'SCHEMA'] + CREATE.transformArguments('index', { + field: SCHEMA_FIELD_TYPE.GEOSHAPE + }), + ['FT.CREATE', 'index', 'SCHEMA', 'field', 'GEOSHAPE'] ); - }); + }); - it('with NOFREQS', () => { + it('{ type: SCHEMA_FIELD_TYPE.GEOSHAPE }', () => { assert.deepEqual( - transformArguments('index', {}, { - NOFREQS: true - }), - ['FT.CREATE', 'index', 'NOFREQS', 'SCHEMA'] + CREATE.transformArguments('index', { + field: { + type: SCHEMA_FIELD_TYPE.GEOSHAPE + } + }), + ['FT.CREATE', 'index', 'SCHEMA', 'field', 'GEOSHAPE'] ); + }); }); - it('with SKIPINITIALSCAN', () => { - assert.deepEqual( - transformArguments('index', {}, { - SKIPINITIALSCAN: true - }), - ['FT.CREATE', 'index', 'SKIPINITIALSCAN', 'SCHEMA'] - ); + it('with COORD_SYSTEM', () => { + assert.deepEqual( + CREATE.transformArguments('index', { + field: { + type: SCHEMA_FIELD_TYPE.GEOSHAPE, + COORD_SYSTEM: 'SPHERICAL' + } + }), + ['FT.CREATE', 'index', 'SCHEMA', 'field', 'GEOSHAPE', 'COORD_SYSTEM', 'SPHERICAL'] + ); + }); + }); + + it('with AS', () => { + assert.deepEqual( + CREATE.transformArguments('index', { + field: { + type: SCHEMA_FIELD_TYPE.TEXT, + AS: 'as' + } + }), + ['FT.CREATE', 'index', 'SCHEMA', 'field', 'AS', 'as', 'TEXT'] + ); + }); + + describe('with SORTABLE', () => { + it('true', () => { + assert.deepEqual( + CREATE.transformArguments('index', { + field: { + type: SCHEMA_FIELD_TYPE.TEXT, + SORTABLE: true + } + }), + ['FT.CREATE', 'index', 'SCHEMA', 'field', 'TEXT', 'SORTABLE'] + ); }); - describe('with STOPWORDS', () => { - it('string', () => { - assert.deepEqual( - transformArguments('index', {}, { - STOPWORDS: 'stopword' - }), - ['FT.CREATE', 'index', 'STOPWORDS', '1', 'stopword', 'SCHEMA'] - ); - }); - - it('Array', () => { - assert.deepEqual( - transformArguments('index', {}, { - STOPWORDS: ['1', '2'] - }), - ['FT.CREATE', 'index', 'STOPWORDS', '2', '1', '2', 'SCHEMA'] - ); - }); + it('UNF', () => { + assert.deepEqual( + CREATE.transformArguments('index', { + field: { + type: SCHEMA_FIELD_TYPE.TEXT, + SORTABLE: 'UNF' + } + }), + ['FT.CREATE', 'index', 'SCHEMA', 'field', 'TEXT', 'SORTABLE', 'UNF'] + ); }); + }); + + it('with NOINDEX', () => { + assert.deepEqual( + CREATE.transformArguments('index', { + field: { + type: SCHEMA_FIELD_TYPE.TEXT, + NOINDEX: true + } + }), + ['FT.CREATE', 'index', 'SCHEMA', 'field', 'TEXT', 'NOINDEX'] + ); + }); + + it('with INDEXMISSING', () => { + assert.deepEqual( + CREATE.transformArguments('index', { + field: { + type: SCHEMA_FIELD_TYPE.TEXT, + INDEXMISSING: true + } + }), + ['FT.CREATE', 'index', 'SCHEMA', 'field', 'TEXT', 'INDEXMISSING'] + ); + }); }); - testUtils.testWithClient('client.ft.create', async client => { - assert.equal( - await client.ft.create('index', { - field: SchemaFieldTypes.TEXT - }), - 'OK' + it('with ON', () => { + assert.deepEqual( + CREATE.transformArguments('index', {}, { + ON: 'HASH' + }), + ['FT.CREATE', 'index', 'ON', 'HASH', 'SCHEMA'] + ); + }); + + describe('with PREFIX', () => { + it('string', () => { + assert.deepEqual( + CREATE.transformArguments('index', {}, { + PREFIX: 'prefix' + }), + ['FT.CREATE', 'index', 'PREFIX', '1', 'prefix', 'SCHEMA'] + ); + }); + + it('Array', () => { + assert.deepEqual( + CREATE.transformArguments('index', {}, { + PREFIX: ['1', '2'] + }), + ['FT.CREATE', 'index', 'PREFIX', '2', '1', '2', 'SCHEMA'] + ); + }); + }); + + it('with FILTER', () => { + assert.deepEqual( + CREATE.transformArguments('index', {}, { + FILTER: '@field != ""' + }), + ['FT.CREATE', 'index', 'FILTER', '@field != ""', 'SCHEMA'] + ); + }); + + it('with LANGUAGE', () => { + assert.deepEqual( + CREATE.transformArguments('index', {}, { + LANGUAGE: REDISEARCH_LANGUAGE.ARABIC + }), + ['FT.CREATE', 'index', 'LANGUAGE', REDISEARCH_LANGUAGE.ARABIC, 'SCHEMA'] + ); + }); + + it('with LANGUAGE_FIELD', () => { + assert.deepEqual( + CREATE.transformArguments('index', {}, { + LANGUAGE_FIELD: '@field' + }), + ['FT.CREATE', 'index', 'LANGUAGE_FIELD', '@field', 'SCHEMA'] + ); + }); + + it('with SCORE', () => { + assert.deepEqual( + CREATE.transformArguments('index', {}, { + SCORE: 1 + }), + ['FT.CREATE', 'index', 'SCORE', '1', 'SCHEMA'] + ); + }); + + it('with SCORE_FIELD', () => { + assert.deepEqual( + CREATE.transformArguments('index', {}, { + SCORE_FIELD: '@field' + }), + ['FT.CREATE', 'index', 'SCORE_FIELD', '@field', 'SCHEMA'] + ); + }); + + it('with MAXTEXTFIELDS', () => { + assert.deepEqual( + CREATE.transformArguments('index', {}, { + MAXTEXTFIELDS: true + }), + ['FT.CREATE', 'index', 'MAXTEXTFIELDS', 'SCHEMA'] + ); + }); + + it('with TEMPORARY', () => { + assert.deepEqual( + CREATE.transformArguments('index', {}, { + TEMPORARY: 1 + }), + ['FT.CREATE', 'index', 'TEMPORARY', '1', 'SCHEMA'] + ); + }); + + it('with NOOFFSETS', () => { + assert.deepEqual( + CREATE.transformArguments('index', {}, { + NOOFFSETS: true + }), + ['FT.CREATE', 'index', 'NOOFFSETS', 'SCHEMA'] + ); + }); + + it('with NOHL', () => { + assert.deepEqual( + CREATE.transformArguments('index', {}, { + NOHL: true + }), + ['FT.CREATE', 'index', 'NOHL', 'SCHEMA'] + ); + }); + + it('with NOFIELDS', () => { + assert.deepEqual( + CREATE.transformArguments('index', {}, { + NOFIELDS: true + }), + ['FT.CREATE', 'index', 'NOFIELDS', 'SCHEMA'] + ); + }); + + it('with NOFREQS', () => { + assert.deepEqual( + CREATE.transformArguments('index', {}, { + NOFREQS: true + }), + ['FT.CREATE', 'index', 'NOFREQS', 'SCHEMA'] + ); + }); + + it('with SKIPINITIALSCAN', () => { + assert.deepEqual( + CREATE.transformArguments('index', {}, { + SKIPINITIALSCAN: true + }), + ['FT.CREATE', 'index', 'SKIPINITIALSCAN', 'SCHEMA'] + ); + }); + + describe('with STOPWORDS', () => { + it('string', () => { + assert.deepEqual( + CREATE.transformArguments('index', {}, { + STOPWORDS: 'stopword' + }), + ['FT.CREATE', 'index', 'STOPWORDS', '1', 'stopword', 'SCHEMA'] ); - }, GLOBAL.SERVERS.OPEN); + }); + + it('Array', () => { + assert.deepEqual( + CREATE.transformArguments('index', {}, { + STOPWORDS: ['1', '2'] + }), + ['FT.CREATE', 'index', 'STOPWORDS', '2', '1', '2', 'SCHEMA'] + ); + }); + }); + }); + + testUtils.testWithClient('client.ft.create', async client => { + assert.equal( + await client.ft.create('index', { + field: SCHEMA_FIELD_TYPE.TEXT + }), + 'OK' + ); + }, GLOBAL.SERVERS.OPEN); }); diff --git a/packages/search/lib/commands/CREATE.ts b/packages/search/lib/commands/CREATE.ts index 21662c28d7d..2951e56f090 100644 --- a/packages/search/lib/commands/CREATE.ts +++ b/packages/search/lib/commands/CREATE.ts @@ -1,52 +1,323 @@ -import { pushOptionalVerdictArgument } from '@redis/client/dist/lib/commands/generic-transformers'; -import { RedisSearchLanguages, PropertyName, RediSearchSchema, pushSchema } from '.'; - -interface CreateOptions { - ON?: 'HASH' | 'JSON'; - PREFIX?: string | Array; - FILTER?: string; - LANGUAGE?: RedisSearchLanguages; - LANGUAGE_FIELD?: PropertyName; - SCORE?: number; - SCORE_FIELD?: PropertyName; - // PAYLOAD_FIELD?: string; - MAXTEXTFIELDS?: true; - TEMPORARY?: number; - NOOFFSETS?: true; - NOHL?: true; - NOFIELDS?: true; - NOFREQS?: true; - SKIPINITIALSCAN?: true; - STOPWORDS?: string | Array; +import { RedisArgument, SimpleStringReply, Command, CommandArguments } from '@redis/client/lib/RESP/types'; +import { RedisVariadicArgument, pushOptionalVariadicArgument } from '@redis/client/lib/commands/generic-transformers'; + +export const SCHEMA_FIELD_TYPE = { + TEXT: 'TEXT', + NUMERIC: 'NUMERIC', + GEO: 'GEO', + TAG: 'TAG', + VECTOR: 'VECTOR', + GEOSHAPE: 'GEOSHAPE' +} as const; + +export type SchemaFieldType = typeof SCHEMA_FIELD_TYPE[keyof typeof SCHEMA_FIELD_TYPE]; + +interface SchemaField { + type: T; + AS?: RedisArgument; + INDEXMISSING?: boolean; +} + +interface SchemaCommonField extends SchemaField { + SORTABLE?: boolean | 'UNF' + NOINDEX?: boolean; +} + +export const SCHEMA_TEXT_FIELD_PHONETIC = { + DM_EN: 'dm:en', + DM_FR: 'dm:fr', + FM_PT: 'dm:pt', + DM_ES: 'dm:es' +} as const; + +export type SchemaTextFieldPhonetic = typeof SCHEMA_TEXT_FIELD_PHONETIC[keyof typeof SCHEMA_TEXT_FIELD_PHONETIC]; + +interface SchemaTextField extends SchemaCommonField { + NOSTEM?: boolean; + WEIGHT?: number; + PHONETIC?: SchemaTextFieldPhonetic; + WITHSUFFIXTRIE?: boolean; + INDEXEMPTY?: boolean; +} + +interface SchemaNumericField extends SchemaCommonField {} + +interface SchemaGeoField extends SchemaCommonField {} + +interface SchemaTagField extends SchemaCommonField { + SEPARATOR?: RedisArgument; + CASESENSITIVE?: boolean; + WITHSUFFIXTRIE?: boolean; + INDEXEMPTY?: boolean; +} + +export const SCHEMA_VECTOR_FIELD_ALGORITHM = { + FLAT: 'FLAT', + HNSW: 'HNSW' +} as const; + +export type SchemaVectorFieldAlgorithm = typeof SCHEMA_VECTOR_FIELD_ALGORITHM[keyof typeof SCHEMA_VECTOR_FIELD_ALGORITHM]; + +interface SchemaVectorField extends SchemaField { + ALGORITHM: SchemaVectorFieldAlgorithm; + TYPE: string; + DIM: number; + DISTANCE_METRIC: 'L2' | 'IP' | 'COSINE'; + INITIAL_CAP?: number; +} + +interface SchemaFlatVectorField extends SchemaVectorField { + ALGORITHM: typeof SCHEMA_VECTOR_FIELD_ALGORITHM['FLAT']; + BLOCK_SIZE?: number; } -export function transformArguments(index: string, schema: RediSearchSchema, options?: CreateOptions): Array { +interface SchemaHNSWVectorField extends SchemaVectorField { + ALGORITHM: typeof SCHEMA_VECTOR_FIELD_ALGORITHM['HNSW']; + M?: number; + EF_CONSTRUCTION?: number; + EF_RUNTIME?: number; +} + +export const SCHEMA_GEO_SHAPE_COORD_SYSTEM = { + SPHERICAL: 'SPHERICAL', + FLAT: 'FLAT' +} as const; + +export type SchemaGeoShapeFieldCoordSystem = typeof SCHEMA_GEO_SHAPE_COORD_SYSTEM[keyof typeof SCHEMA_GEO_SHAPE_COORD_SYSTEM]; + +interface SchemaGeoShapeField extends SchemaField { + COORD_SYSTEM?: SchemaGeoShapeFieldCoordSystem; +} + +export interface RediSearchSchema { + [field: string]: ( + SchemaTextField | + SchemaNumericField | + SchemaGeoField | + SchemaTagField | + SchemaFlatVectorField | + SchemaHNSWVectorField | + SchemaGeoShapeField | + SchemaFieldType + ); +} + +function pushCommonSchemaFieldOptions(args: CommandArguments, fieldOptions: SchemaCommonField) { + if (fieldOptions.SORTABLE) { + args.push('SORTABLE'); + + if (fieldOptions.SORTABLE === 'UNF') { + args.push('UNF'); + } + } + + if (fieldOptions.NOINDEX) { + args.push('NOINDEX'); + } +} + +export function pushSchema(args: CommandArguments, schema: RediSearchSchema) { + for (const [field, fieldOptions] of Object.entries(schema)) { + args.push(field); + + if (typeof fieldOptions === 'string') { + args.push(fieldOptions); + continue; + } + + if (fieldOptions.AS) { + args.push('AS', fieldOptions.AS); + } + + args.push(fieldOptions.type); + + if (fieldOptions.INDEXMISSING) { + args.push('INDEXMISSING'); + } + + switch (fieldOptions.type) { + case SCHEMA_FIELD_TYPE.TEXT: + if (fieldOptions.NOSTEM) { + args.push('NOSTEM'); + } + + if (fieldOptions.WEIGHT) { + args.push('WEIGHT', fieldOptions.WEIGHT.toString()); + } + + if (fieldOptions.PHONETIC) { + args.push('PHONETIC', fieldOptions.PHONETIC); + } + + if (fieldOptions.WITHSUFFIXTRIE) { + args.push('WITHSUFFIXTRIE'); + } + + if (fieldOptions.INDEXEMPTY) { + args.push('INDEXEMPTY'); + } + + pushCommonSchemaFieldOptions(args, fieldOptions) + break; + + case SCHEMA_FIELD_TYPE.NUMERIC: + case SCHEMA_FIELD_TYPE.GEO: + pushCommonSchemaFieldOptions(args, fieldOptions) + break; + + case SCHEMA_FIELD_TYPE.TAG: + if (fieldOptions.SEPARATOR) { + args.push('SEPARATOR', fieldOptions.SEPARATOR); + } + + if (fieldOptions.CASESENSITIVE) { + args.push('CASESENSITIVE'); + } + + if (fieldOptions.WITHSUFFIXTRIE) { + args.push('WITHSUFFIXTRIE'); + } + + if (fieldOptions.INDEXEMPTY) { + args.push('INDEXEMPTY'); + } + + pushCommonSchemaFieldOptions(args, fieldOptions) + break; + + case SCHEMA_FIELD_TYPE.VECTOR: + args.push(fieldOptions.ALGORITHM); + + const lengthIndex = args.push('') - 1; + + args.push( + 'TYPE', fieldOptions.TYPE, + 'DIM', fieldOptions.DIM.toString(), + 'DISTANCE_METRIC', fieldOptions.DISTANCE_METRIC + ); + + if (fieldOptions.INITIAL_CAP) { + args.push('INITIAL_CAP', fieldOptions.INITIAL_CAP.toString()); + } + + switch (fieldOptions.ALGORITHM) { + case SCHEMA_VECTOR_FIELD_ALGORITHM.FLAT: + if (fieldOptions.BLOCK_SIZE) { + args.push('BLOCK_SIZE', fieldOptions.BLOCK_SIZE.toString()); + } + + break; + + case SCHEMA_VECTOR_FIELD_ALGORITHM.HNSW: + if (fieldOptions.M) { + args.push('M', fieldOptions.M.toString()); + } + + if (fieldOptions.EF_CONSTRUCTION) { + args.push('EF_CONSTRUCTION', fieldOptions.EF_CONSTRUCTION.toString()); + } + + if (fieldOptions.EF_RUNTIME) { + args.push('EF_RUNTIME', fieldOptions.EF_RUNTIME.toString()); + } + + break; + } + args[lengthIndex] = (args.length - lengthIndex - 1).toString(); + + break; + + case SCHEMA_FIELD_TYPE.GEOSHAPE: + if (fieldOptions.COORD_SYSTEM !== undefined) { + args.push('COORD_SYSTEM', fieldOptions.COORD_SYSTEM); + } + + break; + } + } +} + +export const REDISEARCH_LANGUAGE = { + ARABIC: 'Arabic', + BASQUE: 'Basque', + CATALANA: 'Catalan', + DANISH: 'Danish', + DUTCH: 'Dutch', + ENGLISH: 'English', + FINNISH: 'Finnish', + FRENCH: 'French', + GERMAN: 'German', + GREEK: 'Greek', + HUNGARIAN: 'Hungarian', + INDONESAIN: 'Indonesian', + IRISH: 'Irish', + ITALIAN: 'Italian', + LITHUANIAN: 'Lithuanian', + NEPALI: 'Nepali', + NORWEIGAN: 'Norwegian', + PORTUGUESE: 'Portuguese', + ROMANIAN: 'Romanian', + RUSSIAN: 'Russian', + SPANISH: 'Spanish', + SWEDISH: 'Swedish', + TAMIL: 'Tamil', + TURKISH: 'Turkish', + CHINESE: 'Chinese' +} as const; + +export type RediSearchLanguage = typeof REDISEARCH_LANGUAGE[keyof typeof REDISEARCH_LANGUAGE]; + +export type RediSearchProperty = `${'@' | '$.'}${string}`; + +export interface CreateOptions { + ON?: 'HASH' | 'JSON'; + PREFIX?: RedisVariadicArgument; + FILTER?: RedisArgument; + LANGUAGE?: RediSearchLanguage; + LANGUAGE_FIELD?: RediSearchProperty; + SCORE?: number; + SCORE_FIELD?: RediSearchProperty; + // PAYLOAD_FIELD?: string; + MAXTEXTFIELDS?: boolean; + TEMPORARY?: number; + NOOFFSETS?: boolean; + NOHL?: boolean; + NOFIELDS?: boolean; + NOFREQS?: boolean; + SKIPINITIALSCAN?: boolean; + STOPWORDS?: RedisVariadicArgument; +} + +export default { + FIRST_KEY_INDEX: undefined, + IS_READ_ONLY: true, + transformArguments(index: RedisArgument, schema: RediSearchSchema, options?: CreateOptions) { const args = ['FT.CREATE', index]; if (options?.ON) { - args.push('ON', options.ON); + args.push('ON', options.ON); } - pushOptionalVerdictArgument(args, 'PREFIX', options?.PREFIX); + pushOptionalVariadicArgument(args, 'PREFIX', options?.PREFIX); if (options?.FILTER) { - args.push('FILTER', options.FILTER); + args.push('FILTER', options.FILTER); } if (options?.LANGUAGE) { - args.push('LANGUAGE', options.LANGUAGE); + args.push('LANGUAGE', options.LANGUAGE); } if (options?.LANGUAGE_FIELD) { - args.push('LANGUAGE_FIELD', options.LANGUAGE_FIELD); + args.push('LANGUAGE_FIELD', options.LANGUAGE_FIELD); } if (options?.SCORE) { - args.push('SCORE', options.SCORE.toString()); + args.push('SCORE', options.SCORE.toString()); } if (options?.SCORE_FIELD) { - args.push('SCORE_FIELD', options.SCORE_FIELD); + args.push('SCORE_FIELD', options.SCORE_FIELD); } // if (options?.PAYLOAD_FIELD) { @@ -54,38 +325,38 @@ export function transformArguments(index: string, schema: RediSearchSchema, opti // } if (options?.MAXTEXTFIELDS) { - args.push('MAXTEXTFIELDS'); + args.push('MAXTEXTFIELDS'); } if (options?.TEMPORARY) { - args.push('TEMPORARY', options.TEMPORARY.toString()); + args.push('TEMPORARY', options.TEMPORARY.toString()); } if (options?.NOOFFSETS) { - args.push('NOOFFSETS'); + args.push('NOOFFSETS'); } if (options?.NOHL) { - args.push('NOHL'); + args.push('NOHL'); } if (options?.NOFIELDS) { - args.push('NOFIELDS'); + args.push('NOFIELDS'); } if (options?.NOFREQS) { - args.push('NOFREQS'); + args.push('NOFREQS'); } if (options?.SKIPINITIALSCAN) { - args.push('SKIPINITIALSCAN'); + args.push('SKIPINITIALSCAN'); } - pushOptionalVerdictArgument(args, 'STOPWORDS', options?.STOPWORDS); + pushOptionalVariadicArgument(args, 'STOPWORDS', options?.STOPWORDS); args.push('SCHEMA'); pushSchema(args, schema); return args; -} - -export declare function transformReply(): 'OK'; + }, + transformReply: undefined as unknown as () => SimpleStringReply<'OK'> +} as const satisfies Command; diff --git a/packages/search/lib/commands/CURSOR_DEL.spec.ts b/packages/search/lib/commands/CURSOR_DEL.spec.ts index d89725ef80d..8e9a7cf9aec 100644 --- a/packages/search/lib/commands/CURSOR_DEL.spec.ts +++ b/packages/search/lib/commands/CURSOR_DEL.spec.ts @@ -1,33 +1,32 @@ -import { strict as assert } from 'assert'; -import { SchemaFieldTypes } from '.'; +import { strict as assert } from 'node:assert'; import testUtils, { GLOBAL } from '../test-utils'; -import { transformArguments } from './CURSOR_DEL'; +import CURSOR_DEL from './CURSOR_DEL'; +import { SCHEMA_FIELD_TYPE } from './CREATE'; -describe('CURSOR DEL', () => { - it('transformArguments', () => { - assert.deepEqual( - transformArguments('index', 0), - ['FT.CURSOR', 'DEL', 'index', '0'] - ); - }); +describe('FT.CURSOR DEL', () => { + it('transformArguments', () => { + assert.deepEqual( + CURSOR_DEL.transformArguments('index', 0), + ['FT.CURSOR', 'DEL', 'index', '0'] + ); + }); - testUtils.testWithClient('client.ft.cursorDel', async client => { - const [ ,, { cursor } ] = await Promise.all([ - client.ft.create('idx', { - field: { - type: SchemaFieldTypes.TEXT - } - }), - client.hSet('key', 'field', 'value'), - client.ft.aggregateWithCursor('idx', '*', { - COUNT: 1 - }) - ]); + testUtils.testWithClient('client.ft.cursorDel', async client => { + const [, , { cursor }] = await Promise.all([ + client.ft.create('idx', { + field: { + type: SCHEMA_FIELD_TYPE.TEXT + } + }), + client.hSet('key', 'field', 'value'), + client.ft.aggregateWithCursor('idx', '*', { + COUNT: 1 + }) + ]); - - assert.equal( - await client.ft.cursorDel('idx', cursor), - 'OK' - ); - }, GLOBAL.SERVERS.OPEN); + assert.equal( + await client.ft.cursorDel('idx', cursor), + 'OK' + ); + }, GLOBAL.SERVERS.OPEN); }); diff --git a/packages/search/lib/commands/CURSOR_DEL.ts b/packages/search/lib/commands/CURSOR_DEL.ts index 22c850f2a89..afccd695ff3 100644 --- a/packages/search/lib/commands/CURSOR_DEL.ts +++ b/packages/search/lib/commands/CURSOR_DEL.ts @@ -1,14 +1,10 @@ -import { RedisCommandArgument } from '@redis/client/dist/lib/commands'; +import { SimpleStringReply, Command, RedisArgument, NumberReply, UnwrapReply } from '@redis/client/dist/lib/RESP/types'; -export const FIRST_KEY_INDEX = 1; - -export function transformArguments(index: RedisCommandArgument, cursorId: number) { - return [ - 'FT.CURSOR', - 'DEL', - index, - cursorId.toString() - ]; -} - -export declare function transformReply(): 'OK'; +export default { + FIRST_KEY_INDEX: undefined, + IS_READ_ONLY: true, + transformArguments(index: RedisArgument, cursorId: UnwrapReply) { + return ['FT.CURSOR', 'DEL', index, cursorId.toString()]; + }, + transformReply: undefined as unknown as () => SimpleStringReply<'OK'> +} as const satisfies Command; diff --git a/packages/search/lib/commands/CURSOR_READ.spec.ts b/packages/search/lib/commands/CURSOR_READ.spec.ts index bb68e2b6396..5999d4a7c18 100644 --- a/packages/search/lib/commands/CURSOR_READ.spec.ts +++ b/packages/search/lib/commands/CURSOR_READ.spec.ts @@ -1,45 +1,44 @@ -import { strict as assert } from 'assert'; -import { SchemaFieldTypes } from '.'; +import { strict as assert } from 'node:assert'; import testUtils, { GLOBAL } from '../test-utils'; -import { transformArguments } from './CURSOR_READ'; +import CURSOR_READ from './CURSOR_READ'; -describe('CURSOR READ', () => { - describe('transformArguments', () => { - it('without options', () => { - assert.deepEqual( - transformArguments('index', 0), - ['FT.CURSOR', 'READ', 'index', '0'] - ); - }); +describe('FT.CURSOR READ', () => { + describe('transformArguments', () => { + it('without options', () => { + assert.deepEqual( + CURSOR_READ.transformArguments('index', 0), + ['FT.CURSOR', 'READ', 'index', '0'] + ); + }); - it('with COUNT', () => { - assert.deepEqual( - transformArguments('index', 0, { COUNT: 1 }), - ['FT.CURSOR', 'READ', 'index', '0', 'COUNT', '1'] - ); - }); + it('with COUNT', () => { + assert.deepEqual( + CURSOR_READ.transformArguments('index', 0, { + COUNT: 1 + }), + ['FT.CURSOR', 'READ', 'index', '0', 'COUNT', '1'] + ); }); + }); - testUtils.testWithClient('client.ft.cursorRead', async client => { - const [, , { cursor }] = await Promise.all([ - client.ft.create('idx', { - field: { - type: SchemaFieldTypes.TEXT - } - }), - client.hSet('key', 'field', 'value'), - client.ft.aggregateWithCursor('idx', '*', { - COUNT: 1 - }) - ]); + testUtils.testWithClient('client.ft.cursorRead', async client => { + const [, , { cursor }] = await Promise.all([ + client.ft.create('idx', { + field: 'TEXT' + }), + client.hSet('key', 'field', 'value'), + client.ft.aggregateWithCursor('idx', '*', { + COUNT: 1 + }) + ]); - assert.deepEqual( - await client.ft.cursorRead('idx', cursor), - { - total: 0, - results: [], - cursor: 0 - } - ); - }, GLOBAL.SERVERS.OPEN); + assert.deepEqual( + await client.ft.cursorRead('idx', cursor), + { + total: 0, + results: [], + cursor: 0 + } + ); + }, GLOBAL.SERVERS.OPEN); }); diff --git a/packages/search/lib/commands/CURSOR_READ.ts b/packages/search/lib/commands/CURSOR_READ.ts index 35cf1bc4f06..d08b22ba90d 100644 --- a/packages/search/lib/commands/CURSOR_READ.ts +++ b/packages/search/lib/commands/CURSOR_READ.ts @@ -1,30 +1,22 @@ -import { RedisCommandArgument, RedisCommandArguments } from '@redis/client/dist/lib/commands'; +import { RedisArgument, Command, UnwrapReply, NumberReply } from '@redis/client/dist/lib/RESP/types'; +import AGGREGATE_WITHCURSOR from './AGGREGATE_WITHCURSOR'; -export const FIRST_KEY_INDEX = 1; - -export const IS_READ_ONLY = true; - -interface CursorReadOptions { - COUNT?: number; +export interface FtCursorReadOptions { + COUNT?: number; } -export function transformArguments( - index: RedisCommandArgument, - cursor: number, - options?: CursorReadOptions -): RedisCommandArguments { - const args = [ - 'FT.CURSOR', - 'READ', - index, - cursor.toString() - ]; +export default { + FIRST_KEY_INDEX: undefined, + IS_READ_ONLY: true, + transformArguments(index: RedisArgument, cursor: UnwrapReply, options?: FtCursorReadOptions) { + const args = ['FT.CURSOR', 'READ', index, cursor.toString()]; - if (options?.COUNT) { - args.push('COUNT', options.COUNT.toString()); + if (options?.COUNT !== undefined) { + args.push('COUNT', options.COUNT.toString()); } return args; -} - -export { transformReply } from './AGGREGATE_WITHCURSOR'; + }, + transformReply: AGGREGATE_WITHCURSOR.transformReply, + unstableResp3: true +} as const satisfies Command; diff --git a/packages/search/lib/commands/DICTADD.spec.ts b/packages/search/lib/commands/DICTADD.spec.ts index b5f29dd4083..c18502ea4d0 100644 --- a/packages/search/lib/commands/DICTADD.spec.ts +++ b/packages/search/lib/commands/DICTADD.spec.ts @@ -1,28 +1,28 @@ -import { strict as assert } from 'assert'; +import { strict as assert } from 'node:assert'; import testUtils, { GLOBAL } from '../test-utils'; -import { transformArguments } from './DICTADD'; +import DICTADD from './DICTADD'; -describe('DICTADD', () => { - describe('transformArguments', () => { - it('string', () => { - assert.deepEqual( - transformArguments('dictionary', 'term'), - ['FT.DICTADD', 'dictionary', 'term'] - ); - }); +describe('FT.DICTADD', () => { + describe('transformArguments', () => { + it('string', () => { + assert.deepEqual( + DICTADD.transformArguments('dictionary', 'term'), + ['FT.DICTADD', 'dictionary', 'term'] + ); + }); - it('Array', () => { - assert.deepEqual( - transformArguments('dictionary', ['1', '2']), - ['FT.DICTADD', 'dictionary', '1', '2'] - ); - }); + it('Array', () => { + assert.deepEqual( + DICTADD.transformArguments('dictionary', ['1', '2']), + ['FT.DICTADD', 'dictionary', '1', '2'] + ); }); + }); - testUtils.testWithClient('client.ft.dictAdd', async client => { - assert.equal( - await client.ft.dictAdd('dictionary', 'term'), - 1 - ); - }, GLOBAL.SERVERS.OPEN); + testUtils.testWithClient('client.ft.dictAdd', async client => { + assert.equal( + await client.ft.dictAdd('dictionary', 'term'), + 1 + ); + }, GLOBAL.SERVERS.OPEN); }); diff --git a/packages/search/lib/commands/DICTADD.ts b/packages/search/lib/commands/DICTADD.ts index 60af11fd41f..f633d58b1f3 100644 --- a/packages/search/lib/commands/DICTADD.ts +++ b/packages/search/lib/commands/DICTADD.ts @@ -1,8 +1,11 @@ -import { RedisCommandArguments } from '@redis/client/dist/lib/commands'; -import { pushVerdictArguments } from '@redis/client/dist/lib/commands/generic-transformers'; +import { RedisArgument, NumberReply, Command } from '@redis/client/dist/lib/RESP/types'; +import { pushVariadicArguments, RedisVariadicArgument } from '@redis/client/dist/lib/commands/generic-transformers'; -export function transformArguments(dictionary: string, term: string | Array): RedisCommandArguments { - return pushVerdictArguments(['FT.DICTADD', dictionary], term); -} - -export declare function transformReply(): number; +export default { + FIRST_KEY_INDEX: undefined, + IS_READ_ONLY: true, + transformArguments(dictionary: RedisArgument, term: RedisVariadicArgument) { + return pushVariadicArguments(['FT.DICTADD', dictionary], term); + }, + transformReply: undefined as unknown as () => NumberReply +} as const satisfies Command; diff --git a/packages/search/lib/commands/DICTDEL.spec.ts b/packages/search/lib/commands/DICTDEL.spec.ts index 5ffa6b6b84f..a7ca1b35cd3 100644 --- a/packages/search/lib/commands/DICTDEL.spec.ts +++ b/packages/search/lib/commands/DICTDEL.spec.ts @@ -1,28 +1,28 @@ -import { strict as assert } from 'assert'; +import { strict as assert } from 'node:assert'; import testUtils, { GLOBAL } from '../test-utils'; -import { transformArguments } from './DICTDEL'; +import DICTDEL from './DICTDEL'; -describe('DICTDEL', () => { - describe('transformArguments', () => { - it('string', () => { - assert.deepEqual( - transformArguments('dictionary', 'term'), - ['FT.DICTDEL', 'dictionary', 'term'] - ); - }); +describe('FT.DICTDEL', () => { + describe('transformArguments', () => { + it('string', () => { + assert.deepEqual( + DICTDEL.transformArguments('dictionary', 'term'), + ['FT.DICTDEL', 'dictionary', 'term'] + ); + }); - it('Array', () => { - assert.deepEqual( - transformArguments('dictionary', ['1', '2']), - ['FT.DICTDEL', 'dictionary', '1', '2'] - ); - }); + it('Array', () => { + assert.deepEqual( + DICTDEL.transformArguments('dictionary', ['1', '2']), + ['FT.DICTDEL', 'dictionary', '1', '2'] + ); }); + }); - testUtils.testWithClient('client.ft.dictDel', async client => { - assert.equal( - await client.ft.dictDel('dictionary', 'term'), - 0 - ); - }, GLOBAL.SERVERS.OPEN); + testUtils.testWithClient('client.ft.dictDel', async client => { + assert.equal( + await client.ft.dictDel('dictionary', 'term'), + 0 + ); + }, GLOBAL.SERVERS.OPEN); }); diff --git a/packages/search/lib/commands/DICTDEL.ts b/packages/search/lib/commands/DICTDEL.ts index a1b728f1926..087211751ee 100644 --- a/packages/search/lib/commands/DICTDEL.ts +++ b/packages/search/lib/commands/DICTDEL.ts @@ -1,8 +1,11 @@ -import { RedisCommandArguments } from '@redis/client/dist/lib/commands'; -import { pushVerdictArguments } from '@redis/client/dist/lib/commands/generic-transformers'; +import { RedisArgument, NumberReply, Command } from '@redis/client/dist/lib/RESP/types'; +import { pushVariadicArguments, RedisVariadicArgument } from '@redis/client/dist/lib/commands/generic-transformers'; -export function transformArguments(dictionary: string, term: string | Array): RedisCommandArguments { - return pushVerdictArguments(['FT.DICTDEL', dictionary], term); -} - -export declare function transformReply(): number; +export default { + FIRST_KEY_INDEX: undefined, + IS_READ_ONLY: true, + transformArguments(dictionary: RedisArgument, term: RedisVariadicArgument) { + return pushVariadicArguments(['FT.DICTDEL', dictionary], term); + }, + transformReply: undefined as unknown as () => NumberReply +} as const satisfies Command; diff --git a/packages/search/lib/commands/DICTDUMP.spec.ts b/packages/search/lib/commands/DICTDUMP.spec.ts index 9896fb9440d..fe8e9441189 100644 --- a/packages/search/lib/commands/DICTDUMP.spec.ts +++ b/packages/search/lib/commands/DICTDUMP.spec.ts @@ -1,21 +1,21 @@ -import { strict as assert } from 'assert'; +import { strict as assert } from 'node:assert'; import testUtils, { GLOBAL } from '../test-utils'; -import { transformArguments } from './DICTDUMP'; +import DICTDUMP from './DICTDUMP'; -describe('DICTDUMP', () => { - it('transformArguments', () => { - assert.deepEqual( - transformArguments('dictionary'), - ['FT.DICTDUMP', 'dictionary'] - ); - }); +describe('FT.DICTDUMP', () => { + it('transformArguments', () => { + assert.deepEqual( + DICTDUMP.transformArguments('dictionary'), + ['FT.DICTDUMP', 'dictionary'] + ); + }); - testUtils.testWithClient('client.ft.dictDump', async client => { - await client.ft.dictAdd('dictionary', 'string') + testUtils.testWithClient('client.ft.dictDump', async client => { + const [, reply] = await Promise.all([ + client.ft.dictAdd('dictionary', 'string'), + client.ft.dictDump('dictionary') + ]); - assert.deepEqual( - await client.ft.dictDump('dictionary'), - ['string'] - ); - }, GLOBAL.SERVERS.OPEN); + assert.deepEqual(reply, ['string']); + }, GLOBAL.SERVERS.OPEN); }); diff --git a/packages/search/lib/commands/DICTDUMP.ts b/packages/search/lib/commands/DICTDUMP.ts index 1427bb42cb7..f542403cc57 100644 --- a/packages/search/lib/commands/DICTDUMP.ts +++ b/packages/search/lib/commands/DICTDUMP.ts @@ -1,5 +1,13 @@ -export function transformArguments(dictionary: string): Array { - return ['FT.DICTDUMP', dictionary]; -} +import { RedisArgument, ArrayReply, SetReply, BlobStringReply, Command } from '@redis/client/dist/lib/RESP/types'; -export declare function transformReply(): Array; +export default { + FIRST_KEY_INDEX: undefined, + IS_READ_ONLY: true, + transformArguments(dictionary: RedisArgument) { + return ['FT.DICTDUMP', dictionary]; + }, + transformReply: { + 2: undefined as unknown as () => ArrayReply, + 3: undefined as unknown as () => SetReply + } +} as const satisfies Command; diff --git a/packages/search/lib/commands/DROPINDEX.spec.ts b/packages/search/lib/commands/DROPINDEX.spec.ts index 6a60a5d851f..5fcbaca08ce 100644 --- a/packages/search/lib/commands/DROPINDEX.spec.ts +++ b/packages/search/lib/commands/DROPINDEX.spec.ts @@ -1,33 +1,33 @@ -import { strict as assert } from 'assert'; +import { strict as assert } from 'node:assert'; import testUtils, { GLOBAL } from '../test-utils'; -import { SchemaFieldTypes } from '.'; -import { transformArguments } from './DROPINDEX'; +import DROPINDEX from './DROPINDEX'; +import { SCHEMA_FIELD_TYPE } from './CREATE'; -describe('DROPINDEX', () => { - describe('transformArguments', () => { - it('without options', () => { - assert.deepEqual( - transformArguments('index'), - ['FT.DROPINDEX', 'index'] - ); - }); +describe('FT.DROPINDEX', () => { + describe('transformArguments', () => { + it('without options', () => { + assert.deepEqual( + DROPINDEX.transformArguments('index'), + ['FT.DROPINDEX', 'index'] + ); + }); - it('with DD', () => { - assert.deepEqual( - transformArguments('index', { DD: true }), - ['FT.DROPINDEX', 'index', 'DD'] - ); - }); + it('with DD', () => { + assert.deepEqual( + DROPINDEX.transformArguments('index', { DD: true }), + ['FT.DROPINDEX', 'index', 'DD'] + ); }); + }); - testUtils.testWithClient('client.ft.dropIndex', async client => { - await client.ft.create('index', { - field: SchemaFieldTypes.TEXT - }); + testUtils.testWithClient('client.ft.dropIndex', async client => { + const [, reply] = await Promise.all([ + client.ft.create('index', { + field: SCHEMA_FIELD_TYPE.TEXT + }), + client.ft.dropIndex('index') + ]); - assert.equal( - await client.ft.dropIndex('index'), - 'OK' - ); - }, GLOBAL.SERVERS.OPEN); + assert.equal(reply, 'OK'); + }, GLOBAL.SERVERS.OPEN); }); diff --git a/packages/search/lib/commands/DROPINDEX.ts b/packages/search/lib/commands/DROPINDEX.ts index 7897a9dd82e..64fe9711e7f 100644 --- a/packages/search/lib/commands/DROPINDEX.ts +++ b/packages/search/lib/commands/DROPINDEX.ts @@ -1,15 +1,23 @@ -interface DropIndexOptions { - DD?: true; +import { RedisArgument, SimpleStringReply, NumberReply, Command } from '@redis/client/dist/lib/RESP/types'; + +export interface FtDropIndexOptions { + DD?: true; } -export function transformArguments(index: string, options?: DropIndexOptions): Array { +export default { + FIRST_KEY_INDEX: undefined, + IS_READ_ONLY: true, + transformArguments(index: RedisArgument, options?: FtDropIndexOptions) { const args = ['FT.DROPINDEX', index]; if (options?.DD) { - args.push('DD'); + args.push('DD'); } return args; -} - -export declare function transformReply(): 'OK'; + }, + transformReply: { + 2: undefined as unknown as () => SimpleStringReply<'OK'>, + 3: undefined as unknown as () => NumberReply + } +} as const satisfies Command; diff --git a/packages/search/lib/commands/EXPLAIN.spec.ts b/packages/search/lib/commands/EXPLAIN.spec.ts index d24f5fe4ac5..e8b3555957f 100644 --- a/packages/search/lib/commands/EXPLAIN.spec.ts +++ b/packages/search/lib/commands/EXPLAIN.spec.ts @@ -1,33 +1,46 @@ -import { strict as assert } from 'assert'; -import { transformArguments } from './EXPLAIN'; +import { strict as assert } from 'node:assert'; +import EXPLAIN from './EXPLAIN'; +import testUtils, { GLOBAL } from '../test-utils'; +import { SCHEMA_FIELD_TYPE } from './CREATE'; describe('EXPLAIN', () => { - describe('transformArguments', () => { - it('simple', () => { - assert.deepEqual( - transformArguments('index', '*'), - ['FT.EXPLAIN', 'index', '*'] - ); - }); + describe('transformArguments', () => { + it('simple', () => { + assert.deepEqual( + EXPLAIN.transformArguments('index', '*'), + ['FT.EXPLAIN', 'index', '*'] + ); + }); - it('with PARAMS', () => { - assert.deepEqual( - transformArguments('index', '*', { - PARAMS: { - param: 'value' - } - }), - ['FT.EXPLAIN', 'index', '*', 'PARAMS', '2', 'param', 'value'] - ); - }); + it('with PARAMS', () => { + assert.deepEqual( + EXPLAIN.transformArguments('index', '*', { + PARAMS: { + param: 'value' + } + }), + ['FT.EXPLAIN', 'index', '*', 'PARAMS', '2', 'param', 'value'] + ); + }); - it('with DIALECT', () => { - assert.deepEqual( - transformArguments('index', '*', { - DIALECT: 1 - }), - ['FT.EXPLAIN', 'index', '*', 'DIALECT', '1'] - ); - }); + it('with DIALECT', () => { + assert.deepEqual( + EXPLAIN.transformArguments('index', '*', { + DIALECT: 1 + }), + ['FT.EXPLAIN', 'index', '*', 'DIALECT', '1'] + ); }); + }); + + testUtils.testWithClient('client.ft.dropIndex', async client => { + const [, reply] = await Promise.all([ + client.ft.create('index', { + field: SCHEMA_FIELD_TYPE.TEXT + }), + client.ft.explain('index', '*') + ]); + + assert.equal(reply, '\n'); + }, GLOBAL.SERVERS.OPEN); }); diff --git a/packages/search/lib/commands/EXPLAIN.ts b/packages/search/lib/commands/EXPLAIN.ts index ab3935ff979..0ad84feb68d 100644 --- a/packages/search/lib/commands/EXPLAIN.ts +++ b/packages/search/lib/commands/EXPLAIN.ts @@ -1,26 +1,28 @@ -import { Params, pushParamsArgs } from "."; +import { RedisArgument, SimpleStringReply, Command } from '@redis/client/dist/lib/RESP/types'; +import { FtSearchParams, pushParamsArgument } from './SEARCH'; -export const IS_READ_ONLY = true; - -interface ExplainOptions { - PARAMS?: Params; - DIALECT?: number; +export interface FtExplainOptions { + PARAMS?: FtSearchParams; + DIALECT?: number; } -export function transformArguments( - index: string, - query: string, - options?: ExplainOptions -): Array { +export default { + FIRST_KEY_INDEX: undefined, + IS_READ_ONLY: true, + transformArguments( + index: RedisArgument, + query: RedisArgument, + options?: FtExplainOptions + ) { const args = ['FT.EXPLAIN', index, query]; - pushParamsArgs(args, options?.PARAMS); + pushParamsArgument(args, options?.PARAMS); if (options?.DIALECT) { - args.push('DIALECT', options.DIALECT.toString()); + args.push('DIALECT', options.DIALECT.toString()); } return args; -} - -export declare function transformReply(): string; + }, + transformReply: undefined as unknown as () => SimpleStringReply +} as const satisfies Command; diff --git a/packages/search/lib/commands/EXPLAINCLI.spec.ts b/packages/search/lib/commands/EXPLAINCLI.spec.ts index 238ef44eaaa..3bffcf5fe5b 100644 --- a/packages/search/lib/commands/EXPLAINCLI.spec.ts +++ b/packages/search/lib/commands/EXPLAINCLI.spec.ts @@ -1,11 +1,11 @@ -import { strict as assert } from 'assert'; -import { transformArguments } from './EXPLAINCLI'; +import { strict as assert } from 'node:assert'; +import EXPLAINCLI from './EXPLAINCLI'; describe('EXPLAINCLI', () => { - it('transformArguments', () => { - assert.deepEqual( - transformArguments('index', '*'), - ['FT.EXPLAINCLI', 'index', '*'] - ); - }); + it('transformArguments', () => { + assert.deepEqual( + EXPLAINCLI.transformArguments('index', '*'), + ['FT.EXPLAINCLI', 'index', '*'] + ); + }); }); diff --git a/packages/search/lib/commands/EXPLAINCLI.ts b/packages/search/lib/commands/EXPLAINCLI.ts index db97fb9c8da..e16866991b9 100644 --- a/packages/search/lib/commands/EXPLAINCLI.ts +++ b/packages/search/lib/commands/EXPLAINCLI.ts @@ -1,7 +1,10 @@ -export const IS_READ_ONLY = true; +import { RedisArgument, ArrayReply, BlobStringReply, Command } from '@redis/client/dist/lib/RESP/types'; -export function transformArguments(index: string, query: string): Array { +export default { + FIRST_KEY_INDEX: undefined, + IS_READ_ONLY: true, + transformArguments(index: RedisArgument, query: RedisArgument) { return ['FT.EXPLAINCLI', index, query]; -} - -export declare function transformReply(): Array; + }, + transformReply: undefined as unknown as () => ArrayReply +} as const satisfies Command; diff --git a/packages/search/lib/commands/INFO.spec.ts b/packages/search/lib/commands/INFO.spec.ts index e026b44e264..e7c7c897a84 100644 --- a/packages/search/lib/commands/INFO.spec.ts +++ b/packages/search/lib/commands/INFO.spec.ts @@ -1,26 +1,28 @@ -import { strict as assert } from 'assert'; -import { SchemaFieldTypes } from '.'; +import { strict as assert } from 'node:assert'; import testUtils, { GLOBAL } from '../test-utils'; -import { transformArguments } from './INFO'; +import INFO, { InfoReply } from './INFO'; +import { SCHEMA_FIELD_TYPE } from './CREATE'; describe('INFO', () => { it('transformArguments', () => { assert.deepEqual( - transformArguments('index'), + INFO.transformArguments('index'), ['FT.INFO', 'index'] ); }); testUtils.testWithClient('client.ft.info', async client => { await client.ft.create('index', { - field: SchemaFieldTypes.TEXT + field: SCHEMA_FIELD_TYPE.TEXT }); + const ret = await client.ft.info('index'); + // effectively testing that stopwords_list is not in ret assert.deepEqual( - await client.ft.info('index'), + ret, { - indexName: 'index', - indexOptions: [], - indexDefinition: Object.create(null, { + index_name: 'index', + index_options: [], + index_definition: Object.create(null, { default_score: { value: '1', configurable: true, @@ -59,41 +61,48 @@ describe('INFO', () => { enumerable: true } })], - numDocs: '0', - maxDocId: '0', - numTerms: '0', - numRecords: '0', - invertedSzMb: '0', - vectorIndexSzMb: '0', - totalInvertedIndexBlocks: '0', - offsetVectorsSzMb: '0', - docTableSizeMb: '0', - sortableValuesSizeMb: '0', - keyTableSizeMb: '0', - recordsPerDocAvg: '-nan', - bytesPerRecordAvg: '-nan', - offsetsPerTermAvg: '-nan', - offsetBitsPerRecordAvg: '-nan', - hashIndexingFailures: '0', - indexing: '0', - percentIndexed: '1', - gcStats: { - bytesCollected: '0', - totalMsRun: '0', - totalCycles: '0', - averageCycleTimeMs: '-nan', - lastRunTimeMs: '0', - gcNumericTreesMissed: '0', - gcBlocksDenied: '0' + num_docs: 0, + max_doc_id: 0, + num_terms: 0, + num_records: 0, + inverted_sz_mb: 0, + vector_index_sz_mb: 0, + total_inverted_index_blocks: 0, + offset_vectors_sz_mb: 0, + doc_table_size_mb: 0, + sortable_values_size_mb: 0, + key_table_size_mb: 0, + records_per_doc_avg: NaN, + bytes_per_record_avg: NaN, + cleaning: 0, + offsets_per_term_avg: NaN, + offset_bits_per_record_avg: NaN, + geoshapes_sz_mb: 0, + hash_indexing_failures: 0, + indexing: 0, + percent_indexed: 1, + number_of_uses: 1, + tag_overhead_sz_mb: 0, + text_overhead_sz_mb: 0, + total_index_memory_sz_mb: 0, + total_indexing_time: 0, + gc_stats: { + bytes_collected: 0, + total_ms_run: 0, + total_cycles: 0, + average_cycle_time_ms: NaN, + last_run_time_ms: 0, + gc_numeric_trees_missed: 0, + gc_blocks_denied: 0 }, - cursorStats: { - globalIdle: 0, - globalTotal: 0, - indexCapacity: 128, - idnexTotal: 0 + cursor_stats: { + global_idle: 0, + global_total: 0, + index_capacity: 128, + index_total: 0 }, - stopWords: undefined } ); + }, GLOBAL.SERVERS.OPEN); }); diff --git a/packages/search/lib/commands/INFO.ts b/packages/search/lib/commands/INFO.ts index 269d12d51cf..52b87769cef 100644 --- a/packages/search/lib/commands/INFO.ts +++ b/packages/search/lib/commands/INFO.ts @@ -1,167 +1,163 @@ -import { RedisCommandArgument } from '@redis/client/dist/lib/commands'; -import { transformTuplesReply } from '@redis/client/dist/lib/commands/generic-transformers'; +import { RedisArgument } from "@redis/client"; +import { ArrayReply, BlobStringReply, Command, DoubleReply, MapReply, NullReply, NumberReply, ReplyUnion, SimpleStringReply, TypeMapping } from "@redis/client/dist/lib/RESP/types"; +import { createTransformTuplesReplyFunc, transformDoubleReply } from "@redis/client/dist/lib/commands/generic-transformers"; +import { TuplesReply } from '@redis/client/lib/RESP/types'; -export function transformArguments(index: string): Array { +export default { + FIRST_KEY_INDEX: undefined, + IS_READ_ONLY: true, + transformArguments(index: RedisArgument) { return ['FT.INFO', index]; + }, + transformReply: { + 2: transformV2Reply, + 3: undefined as unknown as () => ReplyUnion + }, + unstableResp3: true +} as const satisfies Command; + +export interface InfoReply { + index_name: SimpleStringReply; + index_options: ArrayReply; + index_definition: MapReply; + attributes: Array>; + num_docs: NumberReply + max_doc_id: NumberReply; + num_terms: NumberReply; + num_records: NumberReply; + inverted_sz_mb: DoubleReply; + vector_index_sz_mb: DoubleReply; + total_inverted_index_blocks: NumberReply; + offset_vectors_sz_mb: DoubleReply; + doc_table_size_mb: DoubleReply; + sortable_values_size_mb: DoubleReply; + key_table_size_mb: DoubleReply; + tag_overhead_sz_mb: DoubleReply; + text_overhead_sz_mb: DoubleReply; + total_index_memory_sz_mb: DoubleReply; + geoshapes_sz_mb: DoubleReply; + records_per_doc_avg: DoubleReply; + bytes_per_record_avg: DoubleReply; + offsets_per_term_avg: DoubleReply; + offset_bits_per_record_avg: DoubleReply; + hash_indexing_failures: NumberReply; + total_indexing_time: DoubleReply; + indexing: NumberReply; + percent_indexed: DoubleReply; + number_of_uses: NumberReply; + cleaning: NumberReply; + gc_stats: { + bytes_collected: DoubleReply; + total_ms_run: DoubleReply; + total_cycles: DoubleReply; + average_cycle_time_ms: DoubleReply; + last_run_time_ms: DoubleReply; + gc_numeric_trees_missed: DoubleReply; + gc_blocks_denied: DoubleReply; + }; + cursor_stats: { + global_idle: NumberReply; + global_total: NumberReply; + index_capacity: NumberReply; + index_total: NumberReply; + }; + stopwords_list?: ArrayReply | TuplesReply<[NullReply]>; } -type InfoRawReply = [ - 'index_name', - RedisCommandArgument, - 'index_options', - Array, - 'index_definition', - Array, - 'attributes', - Array>, - 'num_docs', - RedisCommandArgument, - 'max_doc_id', - RedisCommandArgument, - 'num_terms', - RedisCommandArgument, - 'num_records', - RedisCommandArgument, - 'inverted_sz_mb', - RedisCommandArgument, - 'vector_index_sz_mb', - RedisCommandArgument, - 'total_inverted_index_blocks', - RedisCommandArgument, - 'offset_vectors_sz_mb', - RedisCommandArgument, - 'doc_table_size_mb', - RedisCommandArgument, - 'sortable_values_size_mb', - RedisCommandArgument, - 'key_table_size_mb', - RedisCommandArgument, - 'records_per_doc_avg', - RedisCommandArgument, - 'bytes_per_record_avg', - RedisCommandArgument, - 'offsets_per_term_avg', - RedisCommandArgument, - 'offset_bits_per_record_avg', - RedisCommandArgument, - 'hash_indexing_failures', - RedisCommandArgument, - 'indexing', - RedisCommandArgument, - 'percent_indexed', - RedisCommandArgument, - 'gc_stats', - [ - 'bytes_collected', - RedisCommandArgument, - 'total_ms_run', - RedisCommandArgument, - 'total_cycles', - RedisCommandArgument, - 'average_cycle_time_ms', - RedisCommandArgument, - 'last_run_time_ms', - RedisCommandArgument, - 'gc_numeric_trees_missed', - RedisCommandArgument, - 'gc_blocks_denied', - RedisCommandArgument - ], - 'cursor_stats', - [ - 'global_idle', - number, - 'global_total', - number, - 'index_capacity', - number, - 'index_total', - number - ], - 'stopwords_list'?, - Array? -]; +function transformV2Reply(reply: Array, preserve?: any, typeMapping?: TypeMapping): InfoReply { + const myTransformFunc = createTransformTuplesReplyFunc(preserve, typeMapping); -interface InfoReply { - indexName: RedisCommandArgument; - indexOptions: Array; - indexDefinition: Record; - attributes: Array>; - numDocs: RedisCommandArgument; - maxDocId: RedisCommandArgument; - numTerms: RedisCommandArgument; - numRecords: RedisCommandArgument; - invertedSzMb: RedisCommandArgument; - vectorIndexSzMb: RedisCommandArgument; - totalInvertedIndexBlocks: RedisCommandArgument; - offsetVectorsSzMb: RedisCommandArgument; - docTableSizeMb: RedisCommandArgument; - sortableValuesSizeMb: RedisCommandArgument; - keyTableSizeMb: RedisCommandArgument; - recordsPerDocAvg: RedisCommandArgument; - bytesPerRecordAvg: RedisCommandArgument; - offsetsPerTermAvg: RedisCommandArgument; - offsetBitsPerRecordAvg: RedisCommandArgument; - hashIndexingFailures: RedisCommandArgument; - indexing: RedisCommandArgument; - percentIndexed: RedisCommandArgument; - gcStats: { - bytesCollected: RedisCommandArgument; - totalMsRun: RedisCommandArgument; - totalCycles: RedisCommandArgument; - averageCycleTimeMs: RedisCommandArgument; - lastRunTimeMs: RedisCommandArgument; - gcNumericTreesMissed: RedisCommandArgument; - gcBlocksDenied: RedisCommandArgument; - }; - cursorStats: { - globalIdle: number; - globalTotal: number; - indexCapacity: number; - idnexTotal: number; - }; - stopWords: Array | undefined; -} + const ret = {} as unknown as InfoReply; + + for (let i=0; i < reply.length; i += 2) { + const key = reply[i].toString() as keyof InfoReply; + + switch (key) { + case 'index_name': + case 'index_options': + case 'num_docs': + case 'max_doc_id': + case 'num_terms': + case 'num_records': + case 'total_inverted_index_blocks': + case 'hash_indexing_failures': + case 'indexing': + case 'number_of_uses': + case 'cleaning': + case 'stopwords_list': + ret[key] = reply[i+1]; + break; + case 'inverted_sz_mb': + case 'vector_index_sz_mb': + case 'offset_vectors_sz_mb': + case 'doc_table_size_mb': + case 'sortable_values_size_mb': + case 'key_table_size_mb': + case 'text_overhead_sz_mb': + case 'tag_overhead_sz_mb': + case 'total_index_memory_sz_mb': + case 'geoshapes_sz_mb': + case 'records_per_doc_avg': + case 'bytes_per_record_avg': + case 'offsets_per_term_avg': + case 'offset_bits_per_record_avg': + case 'total_indexing_time': + case 'percent_indexed': + ret[key] = transformDoubleReply[2](reply[i+1], undefined, typeMapping) as DoubleReply; + break; + case 'index_definition': + ret[key] = myTransformFunc(reply[i+1]); + break; + case 'attributes': + ret[key] = (reply[i+1] as Array>).map(attribute => myTransformFunc(attribute)); + break; + case 'gc_stats': { + const innerRet = {} as unknown as InfoReply['gc_stats']; + + const array = reply[i+1]; + + for (let i=0; i < array.length; i += 2) { + const innerKey = array[i].toString() as keyof InfoReply['gc_stats']; + + switch (innerKey) { + case 'bytes_collected': + case 'total_ms_run': + case 'total_cycles': + case 'average_cycle_time_ms': + case 'last_run_time_ms': + case 'gc_numeric_trees_missed': + case 'gc_blocks_denied': + innerRet[innerKey] = transformDoubleReply[2](array[i+1], undefined, typeMapping) as DoubleReply; + break; + } + } + + ret[key] = innerRet; + break; + } + case 'cursor_stats': { + const innerRet = {} as unknown as InfoReply['cursor_stats']; + + const array = reply[i+1]; + + for (let i=0; i < array.length; i += 2) { + const innerKey = array[i].toString() as keyof InfoReply['cursor_stats']; + + switch (innerKey) { + case 'global_idle': + case 'global_total': + case 'index_capacity': + case 'index_total': + innerRet[innerKey] = array[i+1]; + break; + } + } + + ret[key] = innerRet; + break; + } + } + } -export function transformReply(rawReply: InfoRawReply): InfoReply { - return { - indexName: rawReply[1], - indexOptions: rawReply[3], - indexDefinition: transformTuplesReply(rawReply[5]), - attributes: rawReply[7].map(attribute => transformTuplesReply(attribute)), - numDocs: rawReply[9], - maxDocId: rawReply[11], - numTerms: rawReply[13], - numRecords: rawReply[15], - invertedSzMb: rawReply[17], - vectorIndexSzMb: rawReply[19], - totalInvertedIndexBlocks: rawReply[21], - offsetVectorsSzMb: rawReply[23], - docTableSizeMb: rawReply[25], - sortableValuesSizeMb: rawReply[27], - keyTableSizeMb: rawReply[29], - recordsPerDocAvg: rawReply[31], - bytesPerRecordAvg: rawReply[33], - offsetsPerTermAvg: rawReply[35], - offsetBitsPerRecordAvg: rawReply[37], - hashIndexingFailures: rawReply[39], - indexing: rawReply[41], - percentIndexed: rawReply[43], - gcStats: { - bytesCollected: rawReply[45][1], - totalMsRun: rawReply[45][3], - totalCycles: rawReply[45][5], - averageCycleTimeMs: rawReply[45][7], - lastRunTimeMs: rawReply[45][9], - gcNumericTreesMissed: rawReply[45][11], - gcBlocksDenied: rawReply[45][13] - }, - cursorStats: { - globalIdle: rawReply[47][1], - globalTotal: rawReply[47][3], - indexCapacity: rawReply[47][5], - idnexTotal: rawReply[47][7] - }, - stopWords: rawReply[49] - }; + return ret; } diff --git a/packages/search/lib/commands/PROFILE_AGGREGATE.spec.ts b/packages/search/lib/commands/PROFILE_AGGREGATE.spec.ts index c3d6f990ff7..8644ca5201e 100644 --- a/packages/search/lib/commands/PROFILE_AGGREGATE.spec.ts +++ b/packages/search/lib/commands/PROFILE_AGGREGATE.spec.ts @@ -1,25 +1,25 @@ -import { strict as assert } from 'assert'; +import { strict as assert } from 'node:assert'; import testUtils, { GLOBAL } from '../test-utils'; -import { SchemaFieldTypes } from '.'; -import { transformArguments } from './PROFILE_AGGREGATE'; -import { AggregateSteps } from './AGGREGATE'; +import { FT_AGGREGATE_STEPS } from './AGGREGATE'; +import PROFILE_AGGREGATE from './PROFILE_AGGREGATE'; +import { SCHEMA_FIELD_TYPE } from './CREATE'; describe('PROFILE AGGREGATE', () => { describe('transformArguments', () => { it('without options', () => { assert.deepEqual( - transformArguments('index', 'query'), + PROFILE_AGGREGATE.transformArguments('index', 'query'), ['FT.PROFILE', 'index', 'AGGREGATE', 'QUERY', 'query'] ); }); it('with options', () => { assert.deepEqual( - transformArguments('index', 'query', { + PROFILE_AGGREGATE.transformArguments('index', 'query', { LIMITED: true, VERBATIM: true, STEPS: [{ - type: AggregateSteps.SORTBY, + type: FT_AGGREGATE_STEPS.SORTBY, BY: '@by' }] }), @@ -32,13 +32,14 @@ describe('PROFILE AGGREGATE', () => { testUtils.testWithClient('client.ft.search', async client => { await Promise.all([ client.ft.create('index', { - field: SchemaFieldTypes.NUMERIC + field: SCHEMA_FIELD_TYPE.NUMERIC }), client.hSet('1', 'field', '1'), client.hSet('2', 'field', '2') ]); const res = await client.ft.profileAggregate('index', '*'); + assert.deepEqual('None', res.profile.warning); assert.ok(typeof res.profile.iteratorsProfile.counter === 'number'); assert.ok(typeof res.profile.parsingTime === 'string'); assert.ok(res.results.total == 1); diff --git a/packages/search/lib/commands/PROFILE_AGGREGATE.ts b/packages/search/lib/commands/PROFILE_AGGREGATE.ts index b28e06ade91..b6a8db38665 100644 --- a/packages/search/lib/commands/PROFILE_AGGREGATE.ts +++ b/packages/search/lib/commands/PROFILE_AGGREGATE.ts @@ -1,29 +1,38 @@ -import { pushAggregatehOptions, AggregateOptions, transformReply as transformAggregateReply, AggregateRawReply } from './AGGREGATE'; -import { ProfileOptions, ProfileRawReply, ProfileReply, transformProfile } from '.'; +// import { pushAggregatehOptions, AggregateOptions, transformReply as transformAggregateReply, AggregateRawReply } from './AGGREGATE'; +// import { ProfileOptions, ProfileRawReply, ProfileReply, transformProfile } from '.'; -export const IS_READ_ONLY = true; +import { Command, ReplyUnion } from "@redis/client/dist/lib/RESP/types"; +import AGGREGATE, { AggregateRawReply, FtAggregateOptions, pushAggregateOptions } from "./AGGREGATE"; +import { ProfileOptions, ProfileRawReply, ProfileReply, transformProfile } from "./PROFILE_SEARCH"; -export function transformArguments( - index: string, - query: string, - options?: ProfileOptions & AggregateOptions -): Array { - const args = ['FT.PROFILE', index, 'AGGREGATE']; - - if (options?.LIMITED) { +export default { + FIRST_KEY_INDEX: undefined, + IS_READ_ONLY: true, + transformArguments( + index: string, + query: string, + options?: ProfileOptions & FtAggregateOptions + ) { + const args = ['FT.PROFILE', index, 'AGGREGATE']; + + if (options?.LIMITED) { args.push('LIMITED'); - } - - args.push('QUERY', query); - pushAggregatehOptions(args, options) - return args; -} + } + + args.push('QUERY', query); -type ProfileAggeregateRawReply = ProfileRawReply; + return pushAggregateOptions(args, options) + }, + transformReply: { + 2: (reply: ProfileAggeregateRawReply): ProfileReply => { + return { + results: AGGREGATE.transformReply[2](reply[0]), + profile: transformProfile(reply[1]) + } + }, + 3: undefined as unknown as () => ReplyUnion + }, + unstableResp3: true + } as const satisfies Command; -export function transformReply(reply: ProfileAggeregateRawReply): ProfileReply { - return { - results: transformAggregateReply(reply[0]), - profile: transformProfile(reply[1]) - }; -} + type ProfileAggeregateRawReply = ProfileRawReply; \ No newline at end of file diff --git a/packages/search/lib/commands/PROFILE_SEARCH.spec.ts b/packages/search/lib/commands/PROFILE_SEARCH.spec.ts index 6d7c5adda1e..a6e2a968d43 100644 --- a/packages/search/lib/commands/PROFILE_SEARCH.spec.ts +++ b/packages/search/lib/commands/PROFILE_SEARCH.spec.ts @@ -1,20 +1,21 @@ -import { strict as assert } from 'assert'; +import { strict as assert } from 'node:assert'; import testUtils, { GLOBAL } from '../test-utils'; -import { SchemaFieldTypes } from '.'; -import { transformArguments } from './PROFILE_SEARCH'; +import PROFILE_SEARCH from './PROFILE_SEARCH'; +import { SCHEMA_FIELD_TYPE } from './CREATE'; + describe('PROFILE SEARCH', () => { describe('transformArguments', () => { it('without options', () => { assert.deepEqual( - transformArguments('index', 'query'), + PROFILE_SEARCH.transformArguments('index', 'query'), ['FT.PROFILE', 'index', 'SEARCH', 'QUERY', 'query'] ); }); it('with options', () => { assert.deepEqual( - transformArguments('index', 'query', { + PROFILE_SEARCH.transformArguments('index', 'query', { LIMITED: true, VERBATIM: true, INKEYS: 'key' @@ -28,12 +29,13 @@ describe('PROFILE SEARCH', () => { testUtils.testWithClient('client.ft.search', async client => { await Promise.all([ client.ft.create('index', { - field: SchemaFieldTypes.NUMERIC + field: SCHEMA_FIELD_TYPE.NUMERIC }), client.hSet('1', 'field', '1') ]); const res = await client.ft.profileSearch('index', '*'); + assert.strictEqual('None', res.profile.warning); assert.ok(typeof res.profile.iteratorsProfile.counter === 'number'); assert.ok(typeof res.profile.parsingTime === 'string'); assert.ok(res.results.total == 1); diff --git a/packages/search/lib/commands/PROFILE_SEARCH.ts b/packages/search/lib/commands/PROFILE_SEARCH.ts index 94fba8a6a54..5b9e918083b 100644 --- a/packages/search/lib/commands/PROFILE_SEARCH.ts +++ b/packages/search/lib/commands/PROFILE_SEARCH.ts @@ -1,29 +1,152 @@ -import { SearchOptions, SearchRawReply, transformReply as transformSearchReply } from './SEARCH'; -import { pushSearchOptions, ProfileOptions, ProfileRawReply, ProfileReply, transformProfile } from '.'; -import { RedisCommandArguments } from '@redis/client/dist/lib/commands'; +// import { SearchOptions, SearchRawReply, transformReply as transformSearchReply } from './SEARCH'; +// import { pushSearchOptions, ProfileOptions, ProfileRawReply, ProfileReply, transformProfile } from '.'; +// import { RedisCommandArguments } from '@redis/client/dist/lib/commands'; -export const IS_READ_ONLY = true; +import { Command, RedisArgument, ReplyUnion } from "@redis/client/dist/lib/RESP/types"; +import SEARCH, { FtSearchOptions, SearchRawReply, SearchReply, pushSearchOptions } from "./SEARCH"; +import { AggregateReply } from "./AGGREGATE"; -export function transformArguments( - index: string, - query: string, - options?: ProfileOptions & SearchOptions -): RedisCommandArguments { - let args: RedisCommandArguments = ['FT.PROFILE', index, 'SEARCH']; +export type ProfileRawReply = [ + results: T, + profile: [ + _: string, + TotalProfileTime: string, + _: string, + ParsingTime: string, + _: string, + PipelineCreationTime: string, + _: string, + IteratorsProfile: Array + ] +]; + +type ProfileSearchRawReply = ProfileRawReply; + +export interface ProfileOptions { + LIMITED?: true; +} + +export default { + FIRST_KEY_INDEX: undefined, + IS_READ_ONLY: true, + transformArguments( + index: RedisArgument, + query: RedisArgument, + options?: ProfileOptions & FtSearchOptions + ) { + let args: Array = ['FT.PROFILE', index, 'SEARCH']; if (options?.LIMITED) { - args.push('LIMITED'); + args.push('LIMITED'); } args.push('QUERY', query); + return pushSearchOptions(args, options); + }, + transformReply: { + 2: (reply: ProfileSearchRawReply, withoutDocuments: boolean): ProfileReply => { + return { + results: SEARCH.transformReply[2](reply[0]), + profile: transformProfile(reply[1]) + } + }, + 3: undefined as unknown as () => ReplyUnion + }, + unstableResp3: true +} as const satisfies Command; + +export interface ProfileReply { + results: SearchReply | AggregateReply; + profile: ProfileData; } -type ProfileSearchRawReply = ProfileRawReply; +interface ChildIterator { + type?: string, + counter?: number, + term?: string, + size?: number, + time?: string, + childIterators?: Array +} -export function transformReply(reply: ProfileSearchRawReply, withoutDocuments: boolean): ProfileReply { - return { - results: transformSearchReply(reply[0], withoutDocuments), - profile: transformProfile(reply[1]) - }; +interface IteratorsProfile { + type?: string, + counter?: number, + queryType?: string, + time?: string, + childIterators?: Array } + +interface ProfileData { + totalProfileTime: string, + parsingTime: string, + pipelineCreationTime: string, + warning: string, + iteratorsProfile: IteratorsProfile +} + +export function transformProfile(reply: Array): ProfileData{ + return { + totalProfileTime: reply[0][1], + parsingTime: reply[1][1], + pipelineCreationTime: reply[2][1], + warning: reply[3][1] ? reply[3][1] : 'None', + iteratorsProfile: transformIterators(reply[4][1]) + }; +} + +function transformIterators(IteratorsProfile: Array): IteratorsProfile { + var res: IteratorsProfile = {}; + for (let i = 0; i < IteratorsProfile.length; i += 2) { + const value = IteratorsProfile[i+1]; + switch (IteratorsProfile[i]) { + case 'Type': + res.type = value; + break; + case 'Counter': + res.counter = value; + break; + case 'Time': + res.time = value; + break; + case 'Query type': + res.queryType = value; + break; + case 'Child iterators': + res.childIterators = value.map(transformChildIterators); + break; + } + } + + return res; +} + +function transformChildIterators(IteratorsProfile: Array): ChildIterator { + var res: ChildIterator = {}; + for (let i = 1; i < IteratorsProfile.length; i += 2) { + const value = IteratorsProfile[i+1]; + switch (IteratorsProfile[i]) { + case 'Type': + res.type = value; + break; + case 'Counter': + res.counter = value; + break; + case 'Time': + res.time = value; + break; + case 'Size': + res.size = value; + break; + case 'Term': + res.term = value; + break; + case 'Child iterators': + res.childIterators = value.map(transformChildIterators); + break; + } + } + + return res; +} \ No newline at end of file diff --git a/packages/search/lib/commands/SEARCH.spec.ts b/packages/search/lib/commands/SEARCH.spec.ts index 931458b3a25..257dbb79515 100644 --- a/packages/search/lib/commands/SEARCH.spec.ts +++ b/packages/search/lib/commands/SEARCH.spec.ts @@ -1,300 +1,327 @@ -import { strict as assert } from 'assert'; -import { RedisSearchLanguages, SchemaFieldTypes } from '.'; +import { strict as assert } from 'node:assert'; import testUtils, { GLOBAL } from '../test-utils'; -import { transformArguments } from './SEARCH'; +import SEARCH from './SEARCH'; -describe('SEARCH', () => { - describe('transformArguments', () => { - it('without options', () => { - assert.deepEqual( - transformArguments('index', 'query'), - ['FT.SEARCH', 'index', 'query'] - ); - }); - - it('with VERBATIM', () => { - assert.deepEqual( - transformArguments('index', 'query', { VERBATIM: true }), - ['FT.SEARCH', 'index', 'query', 'VERBATIM'] - ); - }); - - it('with NOSTOPWORDS', () => { - assert.deepEqual( - transformArguments('index', 'query', { NOSTOPWORDS: true }), - ['FT.SEARCH', 'index', 'query', 'NOSTOPWORDS'] - ); - }); - - it('with INKEYS', () => { - assert.deepEqual( - transformArguments('index', 'query', { INKEYS: 'key' }), - ['FT.SEARCH', 'index', 'query', 'INKEYS', '1', 'key'] - ); - }); - - it('with INFIELDS', () => { - assert.deepEqual( - transformArguments('index', 'query', { INFIELDS: 'field' }), - ['FT.SEARCH', 'index', 'query', 'INFIELDS', '1', 'field'] - ); - }); +describe('FT.SEARCH', () => { + describe('transformArguments', () => { + it('without options', () => { + assert.deepEqual( + SEARCH.transformArguments('index', 'query'), + ['FT.SEARCH', 'index', 'query'] + ); + }); - it('with RETURN', () => { - assert.deepEqual( - transformArguments('index', 'query', { RETURN: 'return' }), - ['FT.SEARCH', 'index', 'query', 'RETURN', '1', 'return'] - ); - }); + it('with VERBATIM', () => { + assert.deepEqual( + SEARCH.transformArguments('index', 'query', { + VERBATIM: true + }), + ['FT.SEARCH', 'index', 'query', 'VERBATIM'] + ); + }); - describe('with SUMMARIZE', () => { - it('true', () => { - assert.deepEqual( - transformArguments('index', 'query', { SUMMARIZE: true }), - ['FT.SEARCH', 'index', 'query', 'SUMMARIZE'] - ); - }); + it('with NOSTOPWORDS', () => { + assert.deepEqual( + SEARCH.transformArguments('index', 'query', { + NOSTOPWORDS: true + }), + ['FT.SEARCH', 'index', 'query', 'NOSTOPWORDS'] + ); + }); - describe('with FIELDS', () => { - it('string', () => { - assert.deepEqual( - transformArguments('index', 'query', { - SUMMARIZE: { - FIELDS: ['@field'] - } - }), - ['FT.SEARCH', 'index', 'query', 'SUMMARIZE', 'FIELDS', '1', '@field'] - ); - }); + it('with INKEYS', () => { + assert.deepEqual( + SEARCH.transformArguments('index', 'query', { + INKEYS: 'key' + }), + ['FT.SEARCH', 'index', 'query', 'INKEYS', '1', 'key'] + ); + }); - it('Array', () => { - assert.deepEqual( - transformArguments('index', 'query', { - SUMMARIZE: { - FIELDS: ['@1', '@2'] - } - }), - ['FT.SEARCH', 'index', 'query', 'SUMMARIZE', 'FIELDS', '2', '@1', '@2'] - ); - }); - }); + it('with INFIELDS', () => { + assert.deepEqual( + SEARCH.transformArguments('index', 'query', { + INFIELDS: 'field' + }), + ['FT.SEARCH', 'index', 'query', 'INFIELDS', '1', 'field'] + ); + }); - it('with FRAGS', () => { - assert.deepEqual( - transformArguments('index', 'query', { - SUMMARIZE: { - FRAGS: 1 - } - }), - ['FT.SEARCH', 'index', 'query', 'SUMMARIZE', 'FRAGS', '1'] - ); - }); + it('with RETURN', () => { + assert.deepEqual( + SEARCH.transformArguments('index', 'query', { + RETURN: 'return' + }), + ['FT.SEARCH', 'index', 'query', 'RETURN', '1', 'return'] + ); + }); - it('with LEN', () => { - assert.deepEqual( - transformArguments('index', 'query', { - SUMMARIZE: { - LEN: 1 - } - }), - ['FT.SEARCH', 'index', 'query', 'SUMMARIZE', 'LEN', '1'] - ); - }); + describe('with SUMMARIZE', () => { + it('true', () => { + assert.deepEqual( + SEARCH.transformArguments('index', 'query', { + SUMMARIZE: true + }), + ['FT.SEARCH', 'index', 'query', 'SUMMARIZE'] + ); + }); - it('with SEPARATOR', () => { - assert.deepEqual( - transformArguments('index', 'query', { - SUMMARIZE: { - SEPARATOR: 'separator' - } - }), - ['FT.SEARCH', 'index', 'query', 'SUMMARIZE', 'SEPARATOR', 'separator'] - ); - }); + describe('with FIELDS', () => { + it('string', () => { + assert.deepEqual( + SEARCH.transformArguments('index', 'query', { + SUMMARIZE: { + FIELDS: '@field' + } + }), + ['FT.SEARCH', 'index', 'query', 'SUMMARIZE', 'FIELDS', '1', '@field'] + ); }); - describe('with HIGHLIGHT', () => { - it('true', () => { - assert.deepEqual( - transformArguments('index', 'query', { HIGHLIGHT: true }), - ['FT.SEARCH', 'index', 'query', 'HIGHLIGHT'] - ); - }); + it('Array', () => { + assert.deepEqual( + SEARCH.transformArguments('index', 'query', { + SUMMARIZE: { + FIELDS: ['@1', '@2'] + } + }), + ['FT.SEARCH', 'index', 'query', 'SUMMARIZE', 'FIELDS', '2', '@1', '@2'] + ); + }); + }); - describe('with FIELDS', () => { - it('string', () => { - assert.deepEqual( - transformArguments('index', 'query', { - HIGHLIGHT: { - FIELDS: ['@field'] - } - }), - ['FT.SEARCH', 'index', 'query', 'HIGHLIGHT', 'FIELDS', '1', '@field'] - ); - }); + it('with FRAGS', () => { + assert.deepEqual( + SEARCH.transformArguments('index', 'query', { + SUMMARIZE: { + FRAGS: 1 + } + }), + ['FT.SEARCH', 'index', 'query', 'SUMMARIZE', 'FRAGS', '1'] + ); + }); - it('Array', () => { - assert.deepEqual( - transformArguments('index', 'query', { - HIGHLIGHT: { - FIELDS: ['@1', '@2'] - } - }), - ['FT.SEARCH', 'index', 'query', 'HIGHLIGHT', 'FIELDS', '2', '@1', '@2'] - ); - }); - }); + it('with LEN', () => { + assert.deepEqual( + SEARCH.transformArguments('index', 'query', { + SUMMARIZE: { + LEN: 1 + } + }), + ['FT.SEARCH', 'index', 'query', 'SUMMARIZE', 'LEN', '1'] + ); + }); - it('with TAGS', () => { - assert.deepEqual( - transformArguments('index', 'query', { - HIGHLIGHT: { - TAGS: { - open: 'open', - close: 'close' - } - } - }), - ['FT.SEARCH', 'index', 'query', 'HIGHLIGHT', 'TAGS', 'open', 'close'] - ); - }); - }); + it('with SEPARATOR', () => { + assert.deepEqual( + SEARCH.transformArguments('index', 'query', { + SUMMARIZE: { + SEPARATOR: 'separator' + } + }), + ['FT.SEARCH', 'index', 'query', 'SUMMARIZE', 'SEPARATOR', 'separator'] + ); + }); + }); - it('with SLOP', () => { - assert.deepEqual( - transformArguments('index', 'query', { SLOP: 1 }), - ['FT.SEARCH', 'index', 'query', 'SLOP', '1'] - ); - }); + describe('with HIGHLIGHT', () => { + it('true', () => { + assert.deepEqual( + SEARCH.transformArguments('index', 'query', { + HIGHLIGHT: true + }), + ['FT.SEARCH', 'index', 'query', 'HIGHLIGHT'] + ); + }); - it('with INORDER', () => { - assert.deepEqual( - transformArguments('index', 'query', { INORDER: true }), - ['FT.SEARCH', 'index', 'query', 'INORDER'] - ); + describe('with FIELDS', () => { + it('string', () => { + assert.deepEqual( + SEARCH.transformArguments('index', 'query', { + HIGHLIGHT: { + FIELDS: ['@field'] + } + }), + ['FT.SEARCH', 'index', 'query', 'HIGHLIGHT', 'FIELDS', '1', '@field'] + ); }); - it('with LANGUAGE', () => { - assert.deepEqual( - transformArguments('index', 'query', { LANGUAGE: RedisSearchLanguages.ARABIC }), - ['FT.SEARCH', 'index', 'query', 'LANGUAGE', RedisSearchLanguages.ARABIC] - ); + it('Array', () => { + assert.deepEqual( + SEARCH.transformArguments('index', 'query', { + HIGHLIGHT: { + FIELDS: ['@1', '@2'] + } + }), + ['FT.SEARCH', 'index', 'query', 'HIGHLIGHT', 'FIELDS', '2', '@1', '@2'] + ); }); + }); - it('with EXPANDER', () => { - assert.deepEqual( - transformArguments('index', 'query', { EXPANDER: 'expender' }), - ['FT.SEARCH', 'index', 'query', 'EXPANDER', 'expender'] - ); - }); + it('with TAGS', () => { + assert.deepEqual( + SEARCH.transformArguments('index', 'query', { + HIGHLIGHT: { + TAGS: { + open: 'open', + close: 'close' + } + } + }), + ['FT.SEARCH', 'index', 'query', 'HIGHLIGHT', 'TAGS', 'open', 'close'] + ); + }); + }); - it('with SCORER', () => { - assert.deepEqual( - transformArguments('index', 'query', { SCORER: 'scorer' }), - ['FT.SEARCH', 'index', 'query', 'SCORER', 'scorer'] - ); - }); + it('with SLOP', () => { + assert.deepEqual( + SEARCH.transformArguments('index', 'query', { + SLOP: 1 + }), + ['FT.SEARCH', 'index', 'query', 'SLOP', '1'] + ); + }); - it('with SORTBY', () => { - assert.deepEqual( - transformArguments('index', 'query', { SORTBY: '@by' }), - ['FT.SEARCH', 'index', 'query', 'SORTBY', '@by'] - ); - }); + it('with TIMEOUT', () => { + assert.deepEqual( + SEARCH.transformArguments('index', 'query', { + TIMEOUT: 1 + }), + ['FT.SEARCH', 'index', 'query', 'TIMEOUT', '1'] + ); + }); - it('with LIMIT', () => { - assert.deepEqual( - transformArguments('index', 'query', { - LIMIT: { - from: 0, - size: 1 - } - }), - ['FT.SEARCH', 'index', 'query', 'LIMIT', '0', '1'] - ); - }); + it('with INORDER', () => { + assert.deepEqual( + SEARCH.transformArguments('index', 'query', { + INORDER: true + }), + ['FT.SEARCH', 'index', 'query', 'INORDER'] + ); + }); - it('with PARAMS', () => { - assert.deepEqual( - transformArguments('index', 'query', { - PARAMS: { - param: 'value' - } - }), - ['FT.SEARCH', 'index', 'query', 'PARAMS', '2', 'param', 'value'] - ); - }); + it('with LANGUAGE', () => { + assert.deepEqual( + SEARCH.transformArguments('index', 'query', { + LANGUAGE: 'Arabic' + }), + ['FT.SEARCH', 'index', 'query', 'LANGUAGE', 'Arabic'] + ); + }); - it('with DIALECT', () => { - assert.deepEqual( - transformArguments('index', 'query', { - DIALECT: 1 - }), - ['FT.SEARCH', 'index', 'query', 'DIALECT', '1'] - ); - }); + it('with EXPANDER', () => { + assert.deepEqual( + SEARCH.transformArguments('index', 'query', { + EXPANDER: 'expender' + }), + ['FT.SEARCH', 'index', 'query', 'EXPANDER', 'expender'] + ); + }); - it('with TIMEOUT', () => { - assert.deepEqual( - transformArguments('index', 'query', { - TIMEOUT: 5 - }), - ['FT.SEARCH', 'index', 'query', 'TIMEOUT', '5'] - ); - }); + it('with SCORER', () => { + assert.deepEqual( + SEARCH.transformArguments('index', 'query', { + SCORER: 'scorer' + }), + ['FT.SEARCH', 'index', 'query', 'SCORER', 'scorer'] + ); }); - describe('client.ft.search', () => { - testUtils.testWithClient('without optional options', async client => { - await Promise.all([ - client.ft.create('index', { - field: SchemaFieldTypes.NUMERIC - }), - client.hSet('1', 'field', '1') - ]); + it('with SORTBY', () => { + assert.deepEqual( + SEARCH.transformArguments('index', 'query', { + SORTBY: '@by' + }), + ['FT.SEARCH', 'index', 'query', 'SORTBY', '@by'] + ); + }); - assert.deepEqual( - await client.ft.search('index', '*'), - { - total: 1, - documents: [{ - id: '1', - value: Object.create(null, { - field: { - value: '1', - configurable: true, - enumerable: true - } - }) - }] - } - ); - }, GLOBAL.SERVERS.OPEN); + it('with LIMIT', () => { + assert.deepEqual( + SEARCH.transformArguments('index', 'query', { + LIMIT: { + from: 0, + size: 1 + } + }), + ['FT.SEARCH', 'index', 'query', 'LIMIT', '0', '1'] + ); + }); - testUtils.testWithClient('RETURN []', async client => { - await Promise.all([ - client.ft.create('index', { - field: SchemaFieldTypes.NUMERIC - }), - client.hSet('1', 'field', '1'), - client.hSet('2', 'field', '2') - ]); + it('with PARAMS', () => { + assert.deepEqual( + SEARCH.transformArguments('index', 'query', { + PARAMS: { + string: 'string', + buffer: Buffer.from('buffer'), + number: 1 + } + }), + ['FT.SEARCH', 'index', 'query', 'PARAMS', '6', 'string', 'string', 'buffer', Buffer.from('buffer'), 'number', '1'] + ); + }); - assert.deepEqual( - await client.ft.search('index', '*', { - RETURN: [] - }), - { - total: 2, - documents: [{ - id: '1', - value: Object.create(null) - }, { - id: '2', - value: Object.create(null) - }] - } - ); - }, GLOBAL.SERVERS.OPEN); + it('with DIALECT', () => { + assert.deepEqual( + SEARCH.transformArguments('index', 'query', { + DIALECT: 1 + }), + ['FT.SEARCH', 'index', 'query', 'DIALECT', '1'] + ); }); + }); + + describe('client.ft.search', () => { + testUtils.testWithClient('without optional options', async client => { + await Promise.all([ + client.ft.create('index', { + field: 'TEXT' + }), + client.hSet('1', 'field', '1') + ]); + + assert.deepEqual( + await client.ft.search('index', '*'), + { + total: 1, + documents: [{ + id: '1', + value: Object.create(null, { + field: { + value: '1', + configurable: true, + enumerable: true + } + }) + }] + } + ); + }, GLOBAL.SERVERS.OPEN); + + testUtils.testWithClient('RETURN []', async client => { + await Promise.all([ + client.ft.create('index', { + field: 'TEXT' + }), + client.hSet('1', 'field', '1'), + client.hSet('2', 'field', '2') + ]); + + assert.deepEqual( + await client.ft.search('index', '*', { + RETURN: [] + }), + { + total: 2, + documents: [{ + id: '1', + value: Object.create(null) + }, { + id: '2', + value: Object.create(null) + }] + } + ); + }, GLOBAL.SERVERS.OPEN); + }); }); diff --git a/packages/search/lib/commands/SEARCH.ts b/packages/search/lib/commands/SEARCH.ts index ff7ab7e201d..1e5e8ec91f5 100644 --- a/packages/search/lib/commands/SEARCH.ts +++ b/packages/search/lib/commands/SEARCH.ts @@ -1,109 +1,222 @@ -import { RedisCommandArguments } from '@redis/client/dist/lib/commands'; -import { pushSearchOptions, RedisSearchLanguages, Params, PropertyName, SortByProperty, SearchReply } from '.'; - -export const FIRST_KEY_INDEX = 1; - -export const IS_READ_ONLY = true; - -export interface SearchOptions { - VERBATIM?: true; - NOSTOPWORDS?: true; - // WITHSCORES?: true; - // WITHPAYLOADS?: true; - WITHSORTKEYS?: true; - // FILTER?: { - // field: string; - // min: number | string; - // max: number | string; - // }; - // GEOFILTER?: { - // field: string; - // lon: number; - // lat: number; - // radius: number; - // unit: 'm' | 'km' | 'mi' | 'ft'; - // }; - INKEYS?: string | Array; - INFIELDS?: string | Array; - RETURN?: string | Array; - SUMMARIZE?: true | { - FIELDS?: PropertyName | Array; - FRAGS?: number; - LEN?: number; - SEPARATOR?: string; - }; - HIGHLIGHT?: true | { - FIELDS?: PropertyName | Array; - TAGS?: { - open: string; - close: string; - }; - }; - SLOP?: number; - INORDER?: true; - LANGUAGE?: RedisSearchLanguages; - EXPANDER?: string; - SCORER?: string; - // EXPLAINSCORE?: true; // TODO: WITHSCORES - // PAYLOAD?: ; - SORTBY?: SortByProperty; - // MSORTBY?: SortByProperty | Array; - LIMIT?: { - from: number | string; - size: number | string; +import { RedisArgument, Command, ReplyUnion } from '@redis/client/dist/lib/RESP/types'; +import { RedisVariadicArgument, pushOptionalVariadicArgument } from '@redis/client/dist/lib/commands/generic-transformers'; +import { RediSearchProperty, RediSearchLanguage } from './CREATE'; + +export type FtSearchParams = Record; + +export function pushParamsArgument(args: Array, params?: FtSearchParams) { + if (params) { + const length = args.push('PARAMS', ''); + for (const key in params) { + if (!Object.hasOwn(params, key)) continue; + + const value = params[key]; + args.push( + key, + typeof value === 'number' ? value.toString() : value + ); + } + + args[length - 1] = (args.length - length).toString(); + } +} + +export interface FtSearchOptions { + VERBATIM?: boolean; + NOSTOPWORDS?: boolean; + INKEYS?: RedisVariadicArgument; + INFIELDS?: RedisVariadicArgument; + RETURN?: RedisVariadicArgument; + SUMMARIZE?: boolean | { + FIELDS?: RediSearchProperty | Array; + FRAGS?: number; + LEN?: number; + SEPARATOR?: RedisArgument; + }; + HIGHLIGHT?: boolean | { + FIELDS?: RediSearchProperty | Array; + TAGS?: { + open: RedisArgument; + close: RedisArgument; }; - PARAMS?: Params; - DIALECT?: number; - TIMEOUT?: number; + }; + SLOP?: number; + TIMEOUT?: number; + INORDER?: boolean; + LANGUAGE?: RediSearchLanguage; + EXPANDER?: RedisArgument; + SCORER?: RedisArgument; + SORTBY?: RedisArgument | { + BY: RediSearchProperty; + DIRECTION?: 'ASC' | 'DESC'; + }; + LIMIT?: { + from: number | RedisArgument; + size: number | RedisArgument; + }; + PARAMS?: FtSearchParams; + DIALECT?: number; } -export function transformArguments( - index: string, - query: string, - options?: SearchOptions -): RedisCommandArguments { - return pushSearchOptions( - ['FT.SEARCH', index, query], - options - ); +export function pushSearchOptions(args: Array, options?: FtSearchOptions) { + if (options?.VERBATIM) { + args.push('VERBATIM'); + } + + if (options?.NOSTOPWORDS) { + args.push('NOSTOPWORDS'); + } + + pushOptionalVariadicArgument(args, 'INKEYS', options?.INKEYS); + pushOptionalVariadicArgument(args, 'INFIELDS', options?.INFIELDS); + pushOptionalVariadicArgument(args, 'RETURN', options?.RETURN); + + if (options?.SUMMARIZE) { + args.push('SUMMARIZE'); + + if (typeof options.SUMMARIZE === 'object') { + pushOptionalVariadicArgument(args, 'FIELDS', options.SUMMARIZE.FIELDS); + + if (options.SUMMARIZE.FRAGS !== undefined) { + args.push('FRAGS', options.SUMMARIZE.FRAGS.toString()); + } + + if (options.SUMMARIZE.LEN !== undefined) { + args.push('LEN', options.SUMMARIZE.LEN.toString()); + } + + if (options.SUMMARIZE.SEPARATOR !== undefined) { + args.push('SEPARATOR', options.SUMMARIZE.SEPARATOR); + } + } + } + + if (options?.HIGHLIGHT) { + args.push('HIGHLIGHT'); + + if (typeof options.HIGHLIGHT === 'object') { + pushOptionalVariadicArgument(args, 'FIELDS', options.HIGHLIGHT.FIELDS); + + if (options.HIGHLIGHT.TAGS) { + args.push('TAGS', options.HIGHLIGHT.TAGS.open, options.HIGHLIGHT.TAGS.close); + } + } + } + + if (options?.SLOP !== undefined) { + args.push('SLOP', options.SLOP.toString()); + } + + if (options?.TIMEOUT !== undefined) { + args.push('TIMEOUT', options.TIMEOUT.toString()); + } + + if (options?.INORDER) { + args.push('INORDER'); + } + + if (options?.LANGUAGE) { + args.push('LANGUAGE', options.LANGUAGE); + } + + if (options?.EXPANDER) { + args.push('EXPANDER', options.EXPANDER); + } + + if (options?.SCORER) { + args.push('SCORER', options.SCORER); + } + + if (options?.SORTBY) { + args.push('SORTBY'); + + if (typeof options.SORTBY === 'string' || options.SORTBY instanceof Buffer) { + args.push(options.SORTBY); + } else { + args.push(options.SORTBY.BY); + + if (options.SORTBY.DIRECTION) { + args.push(options.SORTBY.DIRECTION); + } + } + } + + if (options?.LIMIT) { + args.push('LIMIT', options.LIMIT.from.toString(), options.LIMIT.size.toString()); + } + + pushParamsArgument(args, options?.PARAMS); + + if (options?.DIALECT !== undefined) { + args.push('DIALECT', options.DIALECT.toString()); + } + + return args; } -export type SearchRawReply = Array; +export default { + FIRST_KEY_INDEX: undefined, + IS_READ_ONLY: true, + transformArguments(index: RedisArgument, query: RedisArgument, options?: FtSearchOptions) { + const args = ['FT.SEARCH', index, query]; + + return pushSearchOptions(args, options); + }, + transformReply: { + 2: (reply: SearchRawReply): SearchReply => { + const withoutDocuments = (reply[0] + 1 == reply.length) -export function transformReply(reply: SearchRawReply, withoutDocuments: boolean): SearchReply { - const documents = []; - let i = 1; - while (i < reply.length) { + const documents = []; + let i = 1; + while (i < reply.length) { documents.push({ - id: reply[i++], - value: withoutDocuments ? Object.create(null) : documentValue(reply[i++]) + id: reply[i++], + value: withoutDocuments ? Object.create(null) : documentValue(reply[i++]) }); - } - - return { + } + + return { total: reply[0], documents - }; + }; + }, + 3: undefined as unknown as () => ReplyUnion + }, + unstableResp3: true +} as const satisfies Command; + +export type SearchRawReply = Array; + +interface SearchDocumentValue { + [key: string]: string | number | null | Array | SearchDocumentValue; +} + +export interface SearchReply { + total: number; + documents: Array<{ + id: string; + value: SearchDocumentValue; + }>; } function documentValue(tuples: any) { - const message = Object.create(null); - - let i = 0; - while (i < tuples.length) { - const key = tuples[i++], - value = tuples[i++]; - if (key === '$') { // might be a JSON reply - try { - Object.assign(message, JSON.parse(value)); - continue; - } catch { - // set as a regular property if not a valid JSON - } - } - - message[key] = value; - } + const message = Object.create(null); + + let i = 0; + while (i < tuples.length) { + const key = tuples[i++], + value = tuples[i++]; + if (key === '$') { // might be a JSON reply + try { + Object.assign(message, JSON.parse(value)); + continue; + } catch { + // set as a regular property if not a valid JSON + } + } + + message[key] = value; + } - return message; + return message; } diff --git a/packages/search/lib/commands/SEARCH_NOCONTENT.spec.ts b/packages/search/lib/commands/SEARCH_NOCONTENT.spec.ts index da5a6feaba7..be998b9e63d 100644 --- a/packages/search/lib/commands/SEARCH_NOCONTENT.spec.ts +++ b/packages/search/lib/commands/SEARCH_NOCONTENT.spec.ts @@ -1,45 +1,34 @@ import { strict as assert } from 'assert'; -import { SchemaFieldTypes } from '.'; import testUtils, { GLOBAL } from '../test-utils'; -import { transformArguments, transformReply } from './SEARCH_NOCONTENT'; +import SEARCH_NOCONTENT from './SEARCH_NOCONTENT'; -describe('SEARCH_NOCONTENT', () => { - describe('transformArguments', () => { - it('without options', () => { - assert.deepEqual( - transformArguments('index', 'query'), - ['FT.SEARCH', 'index', 'query', 'NOCONTENT'] - ); - }); +describe('FT.SEARCH NOCONTENT', () => { + describe('transformArguments', () => { + it('without options', () => { + assert.deepEqual( + SEARCH_NOCONTENT.transformArguments('index', 'query'), + ['FT.SEARCH', 'index', 'query', 'NOCONTENT'] + ); }); + }); - describe('transformReply', () => { - it('returns total and keys', () => { - assert.deepEqual(transformReply([3, '1', '2', '3']), { - total: 3, - documents: ['1', '2', '3'] - }) - }); - }); - - describe('client.ft.searchNoContent', () => { - testUtils.testWithClient('returns total and keys', async client => { - await Promise.all([ - client.ft.create('index', { - field: SchemaFieldTypes.TEXT - }), - client.hSet('1', 'field', 'field1'), - client.hSet('2', 'field', 'field2'), - client.hSet('3', 'field', 'field3') - ]); + describe('client.ft.searchNoContent', () => { + testUtils.testWithClient('returns total and keys', async client => { + await Promise.all([ + client.ft.create('index', { + field: 'TEXT' + }), + client.hSet('1', 'field', 'field1'), + client.hSet('2', 'field', 'field2') + ]); - assert.deepEqual( - await client.ft.searchNoContent('index', '*'), - { - total: 3, - documents: ['1','2','3'] - } - ); - }, GLOBAL.SERVERS.OPEN); - }); + assert.deepEqual( + await client.ft.searchNoContent('index', '*'), + { + total: 2, + documents: ['1', '2'] + } + ); + }, GLOBAL.SERVERS.OPEN); + }); }); diff --git a/packages/search/lib/commands/SEARCH_NOCONTENT.ts b/packages/search/lib/commands/SEARCH_NOCONTENT.ts index ab50ae2b9ff..4ee959b9d71 100644 --- a/packages/search/lib/commands/SEARCH_NOCONTENT.ts +++ b/packages/search/lib/commands/SEARCH_NOCONTENT.ts @@ -1,30 +1,27 @@ -import { RedisCommandArguments } from "@redis/client/dist/lib/commands"; -import { pushSearchOptions } from "."; -import { SearchOptions, SearchRawReply } from "./SEARCH"; +import { Command, ReplyUnion } from '@redis/client/dist/lib/RESP/types'; +import SEARCH, { SearchRawReply } from './SEARCH'; -export const FIRST_KEY_INDEX = 1; - -export const IS_READ_ONLY = true; - -export function transformArguments( - index: string, - query: string, - options?: SearchOptions -): RedisCommandArguments { - return pushSearchOptions( - ['FT.SEARCH', index, query, 'NOCONTENT'], - options - ); -} - -export interface SearchNoContentReply { - total: number; - documents: Array; -}; - -export function transformReply(reply: SearchRawReply): SearchNoContentReply { - return { +export default { + FIRST_KEY_INDEX: SEARCH.FIRST_KEY_INDEX, + IS_READ_ONLY: SEARCH.IS_READ_ONLY, + transformArguments(...args: Parameters) { + const redisArgs = SEARCH.transformArguments(...args); + redisArgs.push('NOCONTENT'); + return redisArgs; + }, + transformReply: { + 2: (reply: SearchRawReply): SearchNoContentReply => { + return { total: reply[0], documents: reply.slice(1) - }; -} + } + }, + 3: undefined as unknown as () => ReplyUnion + }, + unstableResp3: true +} as const satisfies Command; + +export interface SearchNoContentReply { + total: number; + documents: Array; +}; \ No newline at end of file diff --git a/packages/search/lib/commands/SPELLCHECK.spec.ts b/packages/search/lib/commands/SPELLCHECK.spec.ts index acabbe8a87c..a70ee964920 100644 --- a/packages/search/lib/commands/SPELLCHECK.spec.ts +++ b/packages/search/lib/commands/SPELLCHECK.spec.ts @@ -1,80 +1,79 @@ -import { strict as assert } from 'assert'; +import { strict as assert } from 'node:assert'; import testUtils, { GLOBAL } from '../test-utils'; -import { SchemaFieldTypes } from '.'; -import { transformArguments } from './SPELLCHECK'; +import SPELLCHECK from './SPELLCHECK'; -describe('SPELLCHECK', () => { - describe('transformArguments', () => { - it('without options', () => { - assert.deepEqual( - transformArguments('index', 'query'), - ['FT.SPELLCHECK', 'index', 'query'] - ); - }); - - it('with DISTANCE', () => { - assert.deepEqual( - transformArguments('index', 'query', { DISTANCE: 2 }), - ['FT.SPELLCHECK', 'index', 'query', 'DISTANCE', '2'] - ); - }); - - describe('with TERMS', () => { - it('single', () => { - assert.deepEqual( - transformArguments('index', 'query', { - TERMS: { - mode: 'INCLUDE', - dictionary: 'dictionary' - } - }), - ['FT.SPELLCHECK', 'index', 'query', 'TERMS', 'INCLUDE', 'dictionary'] - ); - }); - - it('multiple', () => { - assert.deepEqual( - transformArguments('index', 'query', { - TERMS: [{ - mode: 'INCLUDE', - dictionary: 'include' - }, { - mode: 'EXCLUDE', - dictionary: 'exclude' - }] - }), - ['FT.SPELLCHECK', 'index', 'query', 'TERMS', 'INCLUDE', 'include', 'TERMS', 'EXCLUDE', 'exclude'] - ); - }); - }); +describe('FT.SPELLCHECK', () => { + describe('transformArguments', () => { + it('without options', () => { + assert.deepEqual( + SPELLCHECK.transformArguments('index', 'query'), + ['FT.SPELLCHECK', 'index', 'query'] + ); + }); - it('with DIALECT', () => { - assert.deepEqual( - transformArguments('index', 'query', { - DIALECT: 1 - }), - ['FT.SPELLCHECK', 'index', 'query', 'DIALECT', '1'] - ); - }); + it('with DISTANCE', () => { + assert.deepEqual( + SPELLCHECK.transformArguments('index', 'query', { + DISTANCE: 2 + }), + ['FT.SPELLCHECK', 'index', 'query', 'DISTANCE', '2'] + ); }); - testUtils.testWithClient('client.ft.spellCheck', async client => { - await Promise.all([ - client.ft.create('index', { - field: SchemaFieldTypes.TEXT - }), - client.hSet('key', 'field', 'query') - ]); + describe('with TERMS', () => { + it('single', () => { + assert.deepEqual( + SPELLCHECK.transformArguments('index', 'query', { + TERMS: { + mode: 'INCLUDE', + dictionary: 'dictionary' + } + }), + ['FT.SPELLCHECK', 'index', 'query', 'TERMS', 'INCLUDE', 'dictionary'] + ); + }); + it('multiple', () => { assert.deepEqual( - await client.ft.spellCheck('index', 'quer'), - [{ - term: 'quer', - suggestions: [{ - score: 1, - suggestion: 'query' - }] + SPELLCHECK.transformArguments('index', 'query', { + TERMS: [{ + mode: 'INCLUDE', + dictionary: 'include' + }, { + mode: 'EXCLUDE', + dictionary: 'exclude' }] + }), + ['FT.SPELLCHECK', 'index', 'query', 'TERMS', 'INCLUDE', 'include', 'TERMS', 'EXCLUDE', 'exclude'] ); - }, GLOBAL.SERVERS.OPEN); + }); + }); + + it('with DIALECT', () => { + assert.deepEqual( + SPELLCHECK.transformArguments('index', 'query', { + DIALECT: 1 + }), + ['FT.SPELLCHECK', 'index', 'query', 'DIALECT', '1'] + ); + }); + }); + + testUtils.testWithClient('client.ft.spellCheck', async client => { + const [,, reply] = await Promise.all([ + client.ft.create('index', { + field: 'TEXT' + }), + client.hSet('key', 'field', 'query'), + client.ft.spellCheck('index', 'quer') + ]); + + assert.deepEqual(reply, [{ + term: 'quer', + suggestions: [{ + score: 1, + suggestion: 'query' + }] + }]); + }, GLOBAL.SERVERS.OPEN); }); diff --git a/packages/search/lib/commands/SPELLCHECK.ts b/packages/search/lib/commands/SPELLCHECK.ts index c9317a8b4fe..f52e74ba0f6 100644 --- a/packages/search/lib/commands/SPELLCHECK.ts +++ b/packages/search/lib/commands/SPELLCHECK.ts @@ -1,62 +1,71 @@ -interface SpellCheckTerms { - mode: 'INCLUDE' | 'EXCLUDE'; - dictionary: string; +import { RedisArgument, CommandArguments, Command, ReplyUnion } from '@redis/client/dist/lib/RESP/types'; + +export interface Terms { + mode: 'INCLUDE' | 'EXCLUDE'; + dictionary: RedisArgument; } -interface SpellCheckOptions { - DISTANCE?: number; - TERMS?: SpellCheckTerms | Array; - DIALECT?: number; +export interface FtSpellCheckOptions { + DISTANCE?: number; + TERMS?: Terms | Array; + DIALECT?: number; } -export function transformArguments(index: string, query: string, options?: SpellCheckOptions): Array { +export default { + FIRST_KEY_INDEX: undefined, + IS_READ_ONLY: true, + transformArguments(index: RedisArgument, query: RedisArgument, options?: FtSpellCheckOptions) { const args = ['FT.SPELLCHECK', index, query]; if (options?.DISTANCE) { - args.push('DISTANCE', options.DISTANCE.toString()); + args.push('DISTANCE', options.DISTANCE.toString()); } if (options?.TERMS) { - if (Array.isArray(options.TERMS)) { - for (const term of options.TERMS) { - pushTerms(args, term); - } - } else { - pushTerms(args, options.TERMS); + if (Array.isArray(options.TERMS)) { + for (const term of options.TERMS) { + pushTerms(args, term); } + } else { + pushTerms(args, options.TERMS); + } } if (options?.DIALECT) { - args.push('DIALECT', options.DIALECT.toString()); + args.push('DIALECT', options.DIALECT.toString()); } return args; -} - -function pushTerms(args: Array, { mode, dictionary }: SpellCheckTerms): void { - args.push('TERMS', mode, dictionary); -} + }, + transformReply: { + 2: (rawReply: SpellCheckRawReply): SpellCheckReply => { + return rawReply.map(([, term, suggestions]) => ({ + term, + suggestions: suggestions.map(([score, suggestion]) => ({ + score: Number(score), + suggestion + })) + })); + }, + 3: undefined as unknown as () => ReplyUnion, + }, + unstableResp3: true +} as const satisfies Command; type SpellCheckRawReply = Array<[ - _: string, - term: string, - suggestions: Array<[score: string, suggestion: string]> + _: string, + term: string, + suggestions: Array<[score: string, suggestion: string]> ]>; type SpellCheckReply = Array<{ - term: string, - suggestions: Array<{ - score: number, - suggestion: string - }> + term: string, + suggestions: Array<{ + score: number, + suggestion: string + }> }>; -export function transformReply(rawReply: SpellCheckRawReply): SpellCheckReply { - return rawReply.map(([, term, suggestions]) => ({ - term, - suggestions: suggestions.map(([score, suggestion]) => ({ - score: Number(score), - suggestion - })) - })); +function pushTerms(args: CommandArguments, { mode, dictionary }: Terms) { + args.push('TERMS', mode, dictionary); } diff --git a/packages/search/lib/commands/SUGADD.spec.ts b/packages/search/lib/commands/SUGADD.spec.ts index 23294eb4abd..24e03d37796 100644 --- a/packages/search/lib/commands/SUGADD.spec.ts +++ b/packages/search/lib/commands/SUGADD.spec.ts @@ -1,35 +1,35 @@ -import { strict as assert } from 'assert'; +import { strict as assert } from 'node:assert'; import testUtils, { GLOBAL } from '../test-utils'; -import { transformArguments } from './SUGADD'; +import SUGADD from './SUGADD'; -describe('SUGADD', () => { - describe('transformArguments', () => { - it('without options', () => { - assert.deepEqual( - transformArguments('key', 'string', 1), - ['FT.SUGADD', 'key', 'string', '1'] - ); - }); +describe('FT.SUGADD', () => { + describe('transformArguments', () => { + it('without options', () => { + assert.deepEqual( + SUGADD.transformArguments('key', 'string', 1), + ['FT.SUGADD', 'key', 'string', '1'] + ); + }); - it('with INCR', () => { - assert.deepEqual( - transformArguments('key', 'string', 1, { INCR: true }), - ['FT.SUGADD', 'key', 'string', '1', 'INCR'] - ); - }); + it('with INCR', () => { + assert.deepEqual( + SUGADD.transformArguments('key', 'string', 1, { INCR: true }), + ['FT.SUGADD', 'key', 'string', '1', 'INCR'] + ); + }); - it('with PAYLOAD', () => { - assert.deepEqual( - transformArguments('key', 'string', 1, { PAYLOAD: 'payload' }), - ['FT.SUGADD', 'key', 'string', '1', 'PAYLOAD', 'payload'] - ); - }); + it('with PAYLOAD', () => { + assert.deepEqual( + SUGADD.transformArguments('key', 'string', 1, { PAYLOAD: 'payload' }), + ['FT.SUGADD', 'key', 'string', '1', 'PAYLOAD', 'payload'] + ); }); + }); - testUtils.testWithClient('client.ft.sugAdd', async client => { - assert.equal( - await client.ft.sugAdd('key', 'string', 1), - 1 - ); - }, GLOBAL.SERVERS.OPEN); + testUtils.testWithClient('client.ft.sugAdd', async client => { + assert.equal( + await client.ft.sugAdd('key', 'string', 1), + 1 + ); + }, GLOBAL.SERVERS.OPEN); }); diff --git a/packages/search/lib/commands/SUGADD.ts b/packages/search/lib/commands/SUGADD.ts index d68f0d98841..c18cd7846ed 100644 --- a/packages/search/lib/commands/SUGADD.ts +++ b/packages/search/lib/commands/SUGADD.ts @@ -1,20 +1,25 @@ -interface SugAddOptions { - INCR?: true; - PAYLOAD?: string; +import { RedisArgument, NumberReply, Command } from '@redis/client/dist/lib/RESP/types'; + +export interface FtSugAddOptions { + INCR?: boolean; + PAYLOAD?: RedisArgument; } -export function transformArguments(key: string, string: string, score: number, options?: SugAddOptions): Array { +export default { + FIRST_KEY_INDEX: 1, + IS_READ_ONLY: true, + transformArguments(key: RedisArgument, string: RedisArgument, score: number, options?: FtSugAddOptions) { const args = ['FT.SUGADD', key, string, score.toString()]; if (options?.INCR) { - args.push('INCR'); + args.push('INCR'); } if (options?.PAYLOAD) { - args.push('PAYLOAD', options.PAYLOAD); + args.push('PAYLOAD', options.PAYLOAD); } return args; -} - -export declare function transformReply(): number; + }, + transformReply: undefined as unknown as () => NumberReply +} as const satisfies Command; diff --git a/packages/search/lib/commands/SUGDEL.spec.ts b/packages/search/lib/commands/SUGDEL.spec.ts index 3d89e3b9a72..ea92c2a1a49 100644 --- a/packages/search/lib/commands/SUGDEL.spec.ts +++ b/packages/search/lib/commands/SUGDEL.spec.ts @@ -1,19 +1,19 @@ -import { strict as assert } from 'assert'; +import { strict as assert } from 'node:assert'; import testUtils, { GLOBAL } from '../test-utils'; -import { transformArguments } from './SUGDEL'; +import SUGDEL from './SUGDEL'; -describe('SUGDEL', () => { - it('transformArguments', () => { - assert.deepEqual( - transformArguments('key', 'string'), - ['FT.SUGDEL', 'key', 'string'] - ); - }); +describe('FT.SUGDEL', () => { + it('transformArguments', () => { + assert.deepEqual( + SUGDEL.transformArguments('key', 'string'), + ['FT.SUGDEL', 'key', 'string'] + ); + }); - testUtils.testWithClient('client.ft.sugDel', async client => { - assert.equal( - await client.ft.sugDel('key', 'string'), - false - ); - }, GLOBAL.SERVERS.OPEN); + testUtils.testWithClient('client.ft.sugDel', async client => { + assert.equal( + await client.ft.sugDel('key', 'string'), + 0 + ); + }, GLOBAL.SERVERS.OPEN); }); diff --git a/packages/search/lib/commands/SUGDEL.ts b/packages/search/lib/commands/SUGDEL.ts index b522acdfd47..5829ec40a2c 100644 --- a/packages/search/lib/commands/SUGDEL.ts +++ b/packages/search/lib/commands/SUGDEL.ts @@ -1,5 +1,10 @@ -export function transformArguments(key: string, string: string): Array { - return ['FT.SUGDEL', key, string]; -} +import { RedisArgument, NumberReply, Command } from '@redis/client/dist/lib/RESP/types'; -export { transformBooleanReply as transformReply } from '@redis/client/dist/lib/commands/generic-transformers'; +export default { + FIRST_KEY_INDEX: 1, + IS_READ_ONLY: true, + transformArguments(key: RedisArgument, string: RedisArgument) { + return ['FT.SUGDEL', key, string]; + }, + transformReply: undefined as unknown as () => NumberReply<0 | 1> +} as const satisfies Command; diff --git a/packages/search/lib/commands/SUGGET.spec.ts b/packages/search/lib/commands/SUGGET.spec.ts index c24c2ff0863..6ea4c03f325 100644 --- a/packages/search/lib/commands/SUGGET.spec.ts +++ b/packages/search/lib/commands/SUGGET.spec.ts @@ -1,46 +1,46 @@ -import { strict as assert } from 'assert'; +import { strict as assert } from 'node:assert'; import testUtils, { GLOBAL } from '../test-utils'; -import { transformArguments } from './SUGGET'; +import SUGGET from './SUGGET'; -describe('SUGGET', () => { - describe('transformArguments', () => { - it('without options', () => { - assert.deepEqual( - transformArguments('key', 'prefix'), - ['FT.SUGGET', 'key', 'prefix'] - ); - }); +describe('FT.SUGGET', () => { + describe('transformArguments', () => { + it('without options', () => { + assert.deepEqual( + SUGGET.transformArguments('key', 'prefix'), + ['FT.SUGGET', 'key', 'prefix'] + ); + }); - it('with FUZZY', () => { - assert.deepEqual( - transformArguments('key', 'prefix', { FUZZY: true }), - ['FT.SUGGET', 'key', 'prefix', 'FUZZY'] - ); - }); + it('with FUZZY', () => { + assert.deepEqual( + SUGGET.transformArguments('key', 'prefix', { FUZZY: true }), + ['FT.SUGGET', 'key', 'prefix', 'FUZZY'] + ); + }); - it('with MAX', () => { - assert.deepEqual( - transformArguments('key', 'prefix', { MAX: 10 }), - ['FT.SUGGET', 'key', 'prefix', 'MAX', '10'] - ); - }); + it('with MAX', () => { + assert.deepEqual( + SUGGET.transformArguments('key', 'prefix', { MAX: 10 }), + ['FT.SUGGET', 'key', 'prefix', 'MAX', '10'] + ); }); + }); - describe('client.ft.sugGet', () => { - testUtils.testWithClient('null', async client => { - assert.equal( - await client.ft.sugGet('key', 'prefix'), - null - ); - }, GLOBAL.SERVERS.OPEN); + describe('client.ft.sugGet', () => { + testUtils.testWithClient('null', async client => { + assert.equal( + await client.ft.sugGet('key', 'prefix'), + null + ); + }, GLOBAL.SERVERS.OPEN); - testUtils.testWithClient('with suggestions', async client => { - await client.ft.sugAdd('key', 'string', 1); + testUtils.testWithClient('with suggestions', async client => { + const [, reply] = await Promise.all([ + client.ft.sugAdd('key', 'string', 1), + client.ft.sugGet('key', 's') + ]); - assert.deepEqual( - await client.ft.sugGet('key', 'string'), - ['string'] - ); - }, GLOBAL.SERVERS.OPEN); - }); + assert.deepEqual(reply, ['string']); + }, GLOBAL.SERVERS.OPEN); + }); }); diff --git a/packages/search/lib/commands/SUGGET.ts b/packages/search/lib/commands/SUGGET.ts index 558cedeaa08..53dc57a86aa 100644 --- a/packages/search/lib/commands/SUGGET.ts +++ b/packages/search/lib/commands/SUGGET.ts @@ -1,22 +1,25 @@ -export const IS_READ_ONLY = true; +import { NullReply, ArrayReply, BlobStringReply, Command, RedisArgument } from '@redis/client/dist/lib/RESP/types'; -export interface SugGetOptions { - FUZZY?: true; - MAX?: number; +export interface FtSugGetOptions { + FUZZY?: boolean; + MAX?: number; } -export function transformArguments(key: string, prefix: string, options?: SugGetOptions): Array { +export default { + FIRST_KEY_INDEX: 1, + IS_READ_ONLY: true, + transformArguments(key: RedisArgument, prefix: RedisArgument, options?: FtSugGetOptions) { const args = ['FT.SUGGET', key, prefix]; if (options?.FUZZY) { - args.push('FUZZY'); + args.push('FUZZY'); } - if (options?.MAX) { - args.push('MAX', options.MAX.toString()); + if (options?.MAX !== undefined) { + args.push('MAX', options.MAX.toString()); } return args; -} - -export declare function transformReply(): null | Array; + }, + transformReply: undefined as unknown as () => NullReply | ArrayReply +} as const satisfies Command; diff --git a/packages/search/lib/commands/SUGGET_WITHPAYLOADS.spec.ts b/packages/search/lib/commands/SUGGET_WITHPAYLOADS.spec.ts index a4a87ebe895..42a427ce1f4 100644 --- a/packages/search/lib/commands/SUGGET_WITHPAYLOADS.spec.ts +++ b/packages/search/lib/commands/SUGGET_WITHPAYLOADS.spec.ts @@ -1,33 +1,35 @@ -import { strict as assert } from 'assert'; +import { strict as assert } from 'node:assert'; import testUtils, { GLOBAL } from '../test-utils'; -import { transformArguments } from './SUGGET_WITHPAYLOADS'; +import SUGGET_WITHPAYLOADS from './SUGGET_WITHPAYLOADS'; -describe('SUGGET WITHPAYLOADS', () => { - it('transformArguments', () => { - assert.deepEqual( - transformArguments('key', 'prefix'), - ['FT.SUGGET', 'key', 'prefix', 'WITHPAYLOADS'] - ); - }); +describe('FT.SUGGET WITHPAYLOADS', () => { + it('transformArguments', () => { + assert.deepEqual( + SUGGET_WITHPAYLOADS.transformArguments('key', 'prefix'), + ['FT.SUGGET', 'key', 'prefix', 'WITHPAYLOADS'] + ); + }); - describe('client.ft.sugGetWithPayloads', () => { - testUtils.testWithClient('null', async client => { - assert.equal( - await client.ft.sugGetWithPayloads('key', 'prefix'), - null - ); - }, GLOBAL.SERVERS.OPEN); + describe('client.ft.sugGetWithPayloads', () => { + testUtils.testWithClient('null', async client => { + assert.equal( + await client.ft.sugGetWithPayloads('key', 'prefix'), + null + ); + }, GLOBAL.SERVERS.OPEN); - testUtils.testWithClient('with suggestions', async client => { - await client.ft.sugAdd('key', 'string', 1, { PAYLOAD: 'payload' }); + testUtils.testWithClient('with suggestions', async client => { + const [, reply] = await Promise.all([ + client.ft.sugAdd('key', 'string', 1, { + PAYLOAD: 'payload' + }), + client.ft.sugGetWithPayloads('key', 'string') + ]); - assert.deepEqual( - await client.ft.sugGetWithPayloads('key', 'string'), - [{ - suggestion: 'string', - payload: 'payload' - }] - ); - }, GLOBAL.SERVERS.OPEN); - }); + assert.deepEqual(reply, [{ + suggestion: 'string', + payload: 'payload' + }]); + }, GLOBAL.SERVERS.OPEN); + }); }); diff --git a/packages/search/lib/commands/SUGGET_WITHPAYLOADS.ts b/packages/search/lib/commands/SUGGET_WITHPAYLOADS.ts index 7eaff4697e1..d8b097f3dbc 100644 --- a/packages/search/lib/commands/SUGGET_WITHPAYLOADS.ts +++ b/packages/search/lib/commands/SUGGET_WITHPAYLOADS.ts @@ -1,29 +1,31 @@ -import { SugGetOptions, transformArguments as transformSugGetArguments } from './SUGGET'; +import { NullReply, ArrayReply, BlobStringReply, UnwrapReply, Command } from '@redis/client/dist/lib/RESP/types'; +import { isNullReply } from '@redis/client/dist/lib/commands/generic-transformers'; +import SUGGET from './SUGGET'; -export { IS_READ_ONLY } from './SUGGET'; +export default { + FIRST_KEY_INDEX: SUGGET.FIRST_KEY_INDEX, + IS_READ_ONLY: SUGGET.IS_READ_ONLY, + transformArguments(...args: Parameters) { + const transformedArguments = SUGGET.transformArguments(...args); + transformedArguments.push('WITHPAYLOADS'); + return transformedArguments; + }, + transformReply(reply: NullReply | UnwrapReply>) { + if (isNullReply(reply)) return null; -export function transformArguments(key: string, prefix: string, options?: SugGetOptions): Array { - return [ - ...transformSugGetArguments(key, prefix, options), - 'WITHPAYLOADS' - ]; -} - -export interface SuggestionWithPayload { - suggestion: string; - payload: string | null; -} - -export function transformReply(rawReply: Array | null): Array | null { - if (rawReply === null) return null; - - const transformedReply = []; - for (let i = 0; i < rawReply.length; i += 2) { - transformedReply.push({ - suggestion: rawReply[i]!, - payload: rawReply[i + 1] - }); + const transformedReply: Array<{ + suggestion: BlobStringReply; + payload: BlobStringReply; + }> = new Array(reply.length / 2); + let replyIndex = 0, + arrIndex = 0; + while (replyIndex < reply.length) { + transformedReply[arrIndex++] = { + suggestion: reply[replyIndex++], + payload: reply[replyIndex++] + }; } return transformedReply; -} + } +} as const satisfies Command; diff --git a/packages/search/lib/commands/SUGGET_WITHSCORES.spec.ts b/packages/search/lib/commands/SUGGET_WITHSCORES.spec.ts index e60daa917a9..6969be7729d 100644 --- a/packages/search/lib/commands/SUGGET_WITHSCORES.spec.ts +++ b/packages/search/lib/commands/SUGGET_WITHSCORES.spec.ts @@ -1,33 +1,33 @@ -import { strict as assert } from 'assert'; +import { strict as assert } from 'node:assert'; import testUtils, { GLOBAL } from '../test-utils'; -import { transformArguments } from './SUGGET_WITHSCORES'; +import SUGGET_WITHSCORES from './SUGGET_WITHSCORES'; -describe('SUGGET WITHSCORES', () => { - it('transformArguments', () => { - assert.deepEqual( - transformArguments('key', 'prefix'), - ['FT.SUGGET', 'key', 'prefix', 'WITHSCORES'] - ); - }); +describe('FT.SUGGET WITHSCORES', () => { + it('transformArguments', () => { + assert.deepEqual( + SUGGET_WITHSCORES.transformArguments('key', 'prefix'), + ['FT.SUGGET', 'key', 'prefix', 'WITHSCORES'] + ); + }); - describe('client.ft.sugGetWithScores', () => { - testUtils.testWithClient('null', async client => { - assert.equal( - await client.ft.sugGetWithScores('key', 'prefix'), - null - ); - }, GLOBAL.SERVERS.OPEN); + describe('client.ft.sugGetWithScores', () => { + testUtils.testWithClient('null', async client => { + assert.equal( + await client.ft.sugGetWithScores('key', 'prefix'), + null + ); + }, GLOBAL.SERVERS.OPEN); - testUtils.testWithClient('with suggestions', async client => { - await client.ft.sugAdd('key', 'string', 1); + testUtils.testWithClient('with suggestions', async client => { + const [, reply] = await Promise.all([ + client.ft.sugAdd('key', 'string', 1), + client.ft.sugGetWithScores('key', 's') + ]); - assert.deepEqual( - await client.ft.sugGetWithScores('key', 'string'), - [{ - suggestion: 'string', - score: 2147483648 - }] - ); - }, GLOBAL.SERVERS.OPEN); - }); + assert.ok(Array.isArray(reply)); + assert.equal(reply.length, 1); + assert.equal(reply[0].suggestion, 'string'); + assert.equal(typeof reply[0].score, 'number'); + }, GLOBAL.SERVERS.OPEN); + }); }); diff --git a/packages/search/lib/commands/SUGGET_WITHSCORES.ts b/packages/search/lib/commands/SUGGET_WITHSCORES.ts index bad5bff2999..9d24d95cbb0 100644 --- a/packages/search/lib/commands/SUGGET_WITHSCORES.ts +++ b/packages/search/lib/commands/SUGGET_WITHSCORES.ts @@ -1,29 +1,50 @@ -import { SugGetOptions, transformArguments as transformSugGetArguments } from './SUGGET'; +import { NullReply, ArrayReply, BlobStringReply, DoubleReply, UnwrapReply, Command, TypeMapping } from '@redis/client/dist/lib/RESP/types'; +import { isNullReply, transformDoubleReply } from '@redis/client/dist/lib/commands/generic-transformers'; +import SUGGET from './SUGGET'; -export { IS_READ_ONLY } from './SUGGET'; - -export function transformArguments(key: string, prefix: string, options?: SugGetOptions): Array { - return [ - ...transformSugGetArguments(key, prefix, options), - 'WITHSCORES' - ]; +type SuggestScore = { + suggestion: BlobStringReply; + score: DoubleReply; } -export interface SuggestionWithScores { - suggestion: string; - score: number; -} +export default { + FIRST_KEY_INDEX: SUGGET.FIRST_KEY_INDEX, + IS_READ_ONLY: SUGGET.IS_READ_ONLY, + transformArguments(...args: Parameters) { + const transformedArguments = SUGGET.transformArguments(...args); + transformedArguments.push('WITHSCORES'); + return transformedArguments; + }, + transformReply: { + 2: (reply: NullReply | UnwrapReply>, preserve?: any, typeMapping?: TypeMapping) => { + if (isNullReply(reply)) return null; -export function transformReply(rawReply: Array | null): Array | null { - if (rawReply === null) return null; + const transformedReply: Array = new Array(reply.length / 2); + let replyIndex = 0, + arrIndex = 0; + while (replyIndex < reply.length) { + transformedReply[arrIndex++] = { + suggestion: reply[replyIndex++], + score: transformDoubleReply[2](reply[replyIndex++], preserve, typeMapping) + }; + } - const transformedReply = []; - for (let i = 0; i < rawReply.length; i += 2) { - transformedReply.push({ - suggestion: rawReply[i], - score: Number(rawReply[i + 1]) - }); - } + return transformedReply; + }, + 3: (reply: UnwrapReply>) => { + if (isNullReply(reply)) return null; + + const transformedReply: Array = new Array(reply.length / 2); + let replyIndex = 0, + arrIndex = 0; + while (replyIndex < reply.length) { + transformedReply[arrIndex++] = { + suggestion: reply[replyIndex++] as BlobStringReply, + score: reply[replyIndex++] as DoubleReply + }; + } - return transformedReply; -} + return transformedReply; + } + } +} as const satisfies Command; diff --git a/packages/search/lib/commands/SUGGET_WITHSCORES_WITHPAYLOADS.spec.ts b/packages/search/lib/commands/SUGGET_WITHSCORES_WITHPAYLOADS.spec.ts index 0900d91b8d9..98aad1c8028 100644 --- a/packages/search/lib/commands/SUGGET_WITHSCORES_WITHPAYLOADS.spec.ts +++ b/packages/search/lib/commands/SUGGET_WITHSCORES_WITHPAYLOADS.spec.ts @@ -1,34 +1,36 @@ -import { strict as assert } from 'assert'; +import { strict as assert } from 'node:assert'; import testUtils, { GLOBAL } from '../test-utils'; -import { transformArguments } from './SUGGET_WITHSCORES_WITHPAYLOADS'; +import SUGGET_WITHSCORES_WITHPAYLOADS from './SUGGET_WITHSCORES_WITHPAYLOADS'; -describe('SUGGET WITHSCORES WITHPAYLOADS', () => { - it('transformArguments', () => { - assert.deepEqual( - transformArguments('key', 'prefix'), - ['FT.SUGGET', 'key', 'prefix', 'WITHSCORES', 'WITHPAYLOADS'] - ); - }); +describe('FT.SUGGET WITHSCORES WITHPAYLOADS', () => { + it('transformArguments', () => { + assert.deepEqual( + SUGGET_WITHSCORES_WITHPAYLOADS.transformArguments('key', 'prefix'), + ['FT.SUGGET', 'key', 'prefix', 'WITHSCORES', 'WITHPAYLOADS'] + ); + }); - describe('client.ft.sugGetWithScoresWithPayloads', () => { - testUtils.testWithClient('null', async client => { - assert.equal( - await client.ft.sugGetWithScoresWithPayloads('key', 'prefix'), - null - ); - }, GLOBAL.SERVERS.OPEN); + describe('client.ft.sugGetWithScoresWithPayloads', () => { + testUtils.testWithClient('null', async client => { + assert.equal( + await client.ft.sugGetWithScoresWithPayloads('key', 'prefix'), + null + ); + }, GLOBAL.SERVERS.OPEN); - testUtils.testWithClient('with suggestions', async client => { - await client.ft.sugAdd('key', 'string', 1, { PAYLOAD: 'payload' }); + testUtils.testWithClient('with suggestions', async client => { + const [, reply] = await Promise.all([ + client.ft.sugAdd('key', 'string', 1, { + PAYLOAD: 'payload' + }), + client.ft.sugGetWithScoresWithPayloads('key', 'string') + ]); - assert.deepEqual( - await client.ft.sugGetWithScoresWithPayloads('key', 'string'), - [{ - suggestion: 'string', - score: 2147483648, - payload: 'payload' - }] - ); - }, GLOBAL.SERVERS.OPEN); - }); + assert.ok(Array.isArray(reply)); + assert.equal(reply.length, 1); + assert.equal(reply[0].suggestion, 'string'); + assert.equal(typeof reply[0].score, 'number'); + assert.equal(reply[0].payload, 'payload'); + }, GLOBAL.SERVERS.OPEN); + }); }); diff --git a/packages/search/lib/commands/SUGGET_WITHSCORES_WITHPAYLOADS.ts b/packages/search/lib/commands/SUGGET_WITHSCORES_WITHPAYLOADS.ts index 3b2fe7667b7..1e125eb15fa 100644 --- a/packages/search/lib/commands/SUGGET_WITHSCORES_WITHPAYLOADS.ts +++ b/packages/search/lib/commands/SUGGET_WITHSCORES_WITHPAYLOADS.ts @@ -1,30 +1,56 @@ -import { SugGetOptions, transformArguments as transformSugGetArguments } from './SUGGET'; -import { SuggestionWithPayload } from './SUGGET_WITHPAYLOADS'; -import { SuggestionWithScores } from './SUGGET_WITHSCORES'; +import { NullReply, ArrayReply, BlobStringReply, DoubleReply, UnwrapReply, Command, TypeMapping } from '@redis/client/dist/lib/RESP/types'; +import { isNullReply, transformDoubleReply } from '@redis/client/dist/lib/commands/generic-transformers'; +import SUGGET from './SUGGET'; -export { IS_READ_ONLY } from './SUGGET'; - -export function transformArguments(key: string, prefix: string, options?: SugGetOptions): Array { - return [ - ...transformSugGetArguments(key, prefix, options), - 'WITHSCORES', - 'WITHPAYLOADS' - ]; +type SuggestScoreWithPayload = { + suggestion: BlobStringReply; + score: DoubleReply; + payload: BlobStringReply; } -type SuggestionWithScoresAndPayloads = SuggestionWithScores & SuggestionWithPayload; +export default { + FIRST_KEY_INDEX: SUGGET.FIRST_KEY_INDEX, + IS_READ_ONLY: SUGGET.IS_READ_ONLY, + transformArguments(...args: Parameters) { + const transformedArguments = SUGGET.transformArguments(...args); + transformedArguments.push( + 'WITHSCORES', + 'WITHPAYLOADS' + ); + return transformedArguments; + }, + transformReply: { + 2: (reply: NullReply | UnwrapReply>, preserve?: any, typeMapping?: TypeMapping) => { + if (isNullReply(reply)) return null; -export function transformReply(rawReply: Array | null): Array | null { - if (rawReply === null) return null; + const transformedReply: Array = new Array(reply.length / 3); + let replyIndex = 0, + arrIndex = 0; + while (replyIndex < reply.length) { + transformedReply[arrIndex++] = { + suggestion: reply[replyIndex++], + score: transformDoubleReply[2](reply[replyIndex++], preserve, typeMapping), + payload: reply[replyIndex++] + }; + } - const transformedReply = []; - for (let i = 0; i < rawReply.length; i += 3) { - transformedReply.push({ - suggestion: rawReply[i]!, - score: Number(rawReply[i + 1]!), - payload: rawReply[i + 2] - }); - } + return transformedReply; + }, + 3: (reply: NullReply | UnwrapReply>) => { + if (isNullReply(reply)) return null; - return transformedReply; -} + const transformedReply: Array = new Array(reply.length / 3); + let replyIndex = 0, + arrIndex = 0; + while (replyIndex < reply.length) { + transformedReply[arrIndex++] = { + suggestion: reply[replyIndex++] as BlobStringReply, + score: reply[replyIndex++] as DoubleReply, + payload: reply[replyIndex++] as BlobStringReply + }; + } + + return transformedReply; + } + } +} as const satisfies Command; diff --git a/packages/search/lib/commands/SUGLEN.spec.ts b/packages/search/lib/commands/SUGLEN.spec.ts index 2ea680df953..6e6d5e1fc5c 100644 --- a/packages/search/lib/commands/SUGLEN.spec.ts +++ b/packages/search/lib/commands/SUGLEN.spec.ts @@ -1,19 +1,19 @@ -import { strict as assert } from 'assert'; +import { strict as assert } from 'node:assert'; import testUtils, { GLOBAL } from '../test-utils'; -import { transformArguments } from './SUGLEN'; +import SUGLEN from './SUGLEN'; -describe('SUGLEN', () => { - it('transformArguments', () => { - assert.deepEqual( - transformArguments('key'), - ['FT.SUGLEN', 'key'] - ); - }); +describe('FT.SUGLEN', () => { + it('transformArguments', () => { + assert.deepEqual( + SUGLEN.transformArguments('key'), + ['FT.SUGLEN', 'key'] + ); + }); - testUtils.testWithClient('client.ft.sugLen', async client => { - assert.equal( - await client.ft.sugLen('key'), - 0 - ); - }, GLOBAL.SERVERS.OPEN); + testUtils.testWithClient('client.ft.sugLen', async client => { + assert.equal( + await client.ft.sugLen('key'), + 0 + ); + }, GLOBAL.SERVERS.OPEN); }); diff --git a/packages/search/lib/commands/SUGLEN.ts b/packages/search/lib/commands/SUGLEN.ts index 15b3da61261..85dde8cfb70 100644 --- a/packages/search/lib/commands/SUGLEN.ts +++ b/packages/search/lib/commands/SUGLEN.ts @@ -1,7 +1,10 @@ -export const IS_READ_ONLY = true; +import { RedisArgument, NumberReply, Command } from '@redis/client/dist/lib/RESP/types'; -export function transformArguments(key: string): Array { +export default { + FIRST_KEY_INDEX: 1, + IS_READ_ONLY: true, + transformArguments(key: RedisArgument) { return ['FT.SUGLEN', key]; -} - -export declare function transformReply(): number; + }, + transformReply: undefined as unknown as () => NumberReply +} as const satisfies Command; diff --git a/packages/search/lib/commands/SYNDUMP.spec.ts b/packages/search/lib/commands/SYNDUMP.spec.ts index 472db54bcf8..59c010a8d6d 100644 --- a/packages/search/lib/commands/SYNDUMP.spec.ts +++ b/packages/search/lib/commands/SYNDUMP.spec.ts @@ -1,24 +1,24 @@ -import { strict as assert } from 'assert'; +import { strict as assert } from 'node:assert'; import testUtils, { GLOBAL } from '../test-utils'; -import { transformArguments } from './SYNDUMP'; -import { SchemaFieldTypes } from '.'; +import SYNDUMP from './SYNDUMP'; +import { SCHEMA_FIELD_TYPE } from './CREATE'; -describe('SYNDUMP', () => { - it('transformArguments', () => { - assert.deepEqual( - transformArguments('index'), - ['FT.SYNDUMP', 'index'] - ); - }); +describe('FT.SYNDUMP', () => { + it('transformArguments', () => { + assert.deepEqual( + SYNDUMP.transformArguments('index'), + ['FT.SYNDUMP', 'index'] + ); + }); - testUtils.testWithClient('client.ft.synDump', async client => { - await client.ft.create('index', { - field: SchemaFieldTypes.TEXT - }); + testUtils.testWithClient('client.ft.synDump', async client => { + const [, reply] = await Promise.all([ + client.ft.create('index', { + field: SCHEMA_FIELD_TYPE.TEXT + }), + client.ft.synDump('index') + ]); - assert.deepEqual( - await client.ft.synDump('index'), - [] - ); - }, GLOBAL.SERVERS.OPEN); + assert.deepEqual(reply, {}); + }, GLOBAL.SERVERS.OPEN); }); diff --git a/packages/search/lib/commands/SYNDUMP.ts b/packages/search/lib/commands/SYNDUMP.ts index 5f1e71aaf78..2fe7540fda5 100644 --- a/packages/search/lib/commands/SYNDUMP.ts +++ b/packages/search/lib/commands/SYNDUMP.ts @@ -1,5 +1,22 @@ -export function transformArguments(index: string): Array { - return ['FT.SYNDUMP', index]; -} +import { RedisArgument, MapReply, BlobStringReply, ArrayReply, UnwrapReply, Command } from '@redis/client/dist/lib/RESP/types'; -export declare function transformReply(): Array; +export default { + FIRST_KEY_INDEX: undefined, + IS_READ_ONLY: true, + transformArguments(index: RedisArgument) { + return ['FT.SYNDUMP', index]; + }, + transformReply: { + 2: (reply: UnwrapReply>>) => { + const result: Record> = {}; + let i = 0; + while (i < reply.length) { + const key = (reply[i++] as unknown as UnwrapReply).toString(), + value = reply[i++] as unknown as ArrayReply; + result[key] = value; + } + return result; + }, + 3: undefined as unknown as () => MapReply> + } +} as const satisfies Command; diff --git a/packages/search/lib/commands/SYNUPDATE.spec.ts b/packages/search/lib/commands/SYNUPDATE.spec.ts index 19ac9b85e54..e901ae9fe3f 100644 --- a/packages/search/lib/commands/SYNUPDATE.spec.ts +++ b/packages/search/lib/commands/SYNUPDATE.spec.ts @@ -1,40 +1,42 @@ -import { strict as assert } from 'assert'; +import { strict as assert } from 'node:assert'; import testUtils, { GLOBAL } from '../test-utils'; -import { transformArguments } from './SYNUPDATE'; -import { SchemaFieldTypes } from '.'; +import SYNUPDATE from './SYNUPDATE'; +import { SCHEMA_FIELD_TYPE } from './CREATE'; -describe('SYNUPDATE', () => { - describe('transformArguments', () => { - it('single term', () => { - assert.deepEqual( - transformArguments('index', 'groupId', 'term'), - ['FT.SYNUPDATE', 'index', 'groupId', 'term'] - ); - }); +describe('FT.SYNUPDATE', () => { + describe('transformArguments', () => { + it('single term', () => { + assert.deepEqual( + SYNUPDATE.transformArguments('index', 'groupId', 'term'), + ['FT.SYNUPDATE', 'index', 'groupId', 'term'] + ); + }); - it('multiple terms', () => { - assert.deepEqual( - transformArguments('index', 'groupId', ['1', '2']), - ['FT.SYNUPDATE', 'index', 'groupId', '1', '2'] - ); - }); + it('multiple terms', () => { + assert.deepEqual( + SYNUPDATE.transformArguments('index', 'groupId', ['1', '2']), + ['FT.SYNUPDATE', 'index', 'groupId', '1', '2'] + ); + }); - it('with SKIPINITIALSCAN', () => { - assert.deepEqual( - transformArguments('index', 'groupId', 'term', { SKIPINITIALSCAN: true }), - ['FT.SYNUPDATE', 'index', 'groupId', 'SKIPINITIALSCAN', 'term'] - ); - }); + it('with SKIPINITIALSCAN', () => { + assert.deepEqual( + SYNUPDATE.transformArguments('index', 'groupId', 'term', { + SKIPINITIALSCAN: true + }), + ['FT.SYNUPDATE', 'index', 'groupId', 'SKIPINITIALSCAN', 'term'] + ); }); + }); - testUtils.testWithClient('client.ft.synUpdate', async client => { - await client.ft.create('index', { - field: SchemaFieldTypes.TEXT - }); + testUtils.testWithClient('client.ft.synUpdate', async client => { + const [, reply] = await Promise.all([ + client.ft.create('index', { + field: SCHEMA_FIELD_TYPE.TEXT + }), + client.ft.synUpdate('index', 'groupId', 'term') + ]); - assert.equal( - await client.ft.synUpdate('index', 'groupId', 'term'), - 'OK' - ); - }, GLOBAL.SERVERS.OPEN); + assert.equal(reply, 'OK'); + }, GLOBAL.SERVERS.OPEN); }); diff --git a/packages/search/lib/commands/SYNUPDATE.ts b/packages/search/lib/commands/SYNUPDATE.ts index 3384ea59d94..926d8e58e1c 100644 --- a/packages/search/lib/commands/SYNUPDATE.ts +++ b/packages/search/lib/commands/SYNUPDATE.ts @@ -1,23 +1,26 @@ -import { pushVerdictArguments } from '@redis/client/dist/lib/commands/generic-transformers'; -import { RedisCommandArguments } from '@redis/client/dist/lib/commands'; +import { SimpleStringReply, Command, RedisArgument } from '@redis/client/dist/lib/RESP/types'; +import { RedisVariadicArgument, pushVariadicArguments } from '@redis/client/dist/lib/commands/generic-transformers'; -interface SynUpdateOptions { - SKIPINITIALSCAN?: true; +export interface FtSynUpdateOptions { + SKIPINITIALSCAN?: boolean; } -export function transformArguments( - index: string, - groupId: string, - terms: string | Array, - options?: SynUpdateOptions -): RedisCommandArguments { +export default { + FIRST_KEY_INDEX: undefined, + IS_READ_ONLY: true, + transformArguments( + index: RedisArgument, + groupId: RedisArgument, + terms: RedisVariadicArgument, + options?: FtSynUpdateOptions + ) { const args = ['FT.SYNUPDATE', index, groupId]; if (options?.SKIPINITIALSCAN) { - args.push('SKIPINITIALSCAN'); + args.push('SKIPINITIALSCAN'); } - return pushVerdictArguments(args, terms); -} - -export declare function transformReply(): 'OK'; + return pushVariadicArguments(args, terms); + }, + transformReply: undefined as unknown as () => SimpleStringReply<'OK'> +} as const satisfies Command; diff --git a/packages/search/lib/commands/TAGVALS.spec.ts b/packages/search/lib/commands/TAGVALS.spec.ts index d59bfcfe3ea..dbc6203f93e 100644 --- a/packages/search/lib/commands/TAGVALS.spec.ts +++ b/packages/search/lib/commands/TAGVALS.spec.ts @@ -1,24 +1,24 @@ -import { strict as assert } from 'assert'; +import { strict as assert } from 'node:assert'; import testUtils, { GLOBAL } from '../test-utils'; -import { SchemaFieldTypes } from '.'; -import { transformArguments } from './TAGVALS'; +import TAGVALS from './TAGVALS'; +import { SCHEMA_FIELD_TYPE } from './CREATE'; -describe('TAGVALS', () => { - it('transformArguments', () => { - assert.deepEqual( - transformArguments('index', '@field'), - ['FT.TAGVALS', 'index', '@field'] - ); - }); +describe('FT.TAGVALS', () => { + it('transformArguments', () => { + assert.deepEqual( + TAGVALS.transformArguments('index', '@field'), + ['FT.TAGVALS', 'index', '@field'] + ); + }); - testUtils.testWithClient('client.ft.tagVals', async client => { - await client.ft.create('index', { - field: SchemaFieldTypes.TAG - }); + testUtils.testWithClient('client.ft.tagVals', async client => { + const [, reply] = await Promise.all([ + client.ft.create('index', { + field: SCHEMA_FIELD_TYPE.TAG + }), + client.ft.tagVals('index', 'field') + ]); - assert.deepEqual( - await client.ft.tagVals('index', 'field'), - [] - ); - }, GLOBAL.SERVERS.OPEN); + assert.deepEqual(reply, []); + }, GLOBAL.SERVERS.OPEN); }); diff --git a/packages/search/lib/commands/TAGVALS.ts b/packages/search/lib/commands/TAGVALS.ts index 54342f0c9e5..8a6e73c97b8 100644 --- a/packages/search/lib/commands/TAGVALS.ts +++ b/packages/search/lib/commands/TAGVALS.ts @@ -1,5 +1,13 @@ -export function transformArguments(index: string, fieldName: string): Array { - return ['FT.TAGVALS', index, fieldName]; -} +import { RedisArgument, ArrayReply, SetReply, BlobStringReply, Command } from '@redis/client/dist/lib/RESP/types'; -export declare function transformReply(): Array; +export default { + FIRST_KEY_INDEX: undefined, + IS_READ_ONLY: true, + transformArguments(index: RedisArgument, fieldName: RedisArgument) { + return ['FT.TAGVALS', index, fieldName]; + }, + transformReply: { + 2: undefined as unknown as () => ArrayReply, + 3: undefined as unknown as () => SetReply + } +} as const satisfies Command; diff --git a/packages/search/lib/commands/_LIST.spec.ts b/packages/search/lib/commands/_LIST.spec.ts index 602c29975f2..a7f13b011ac 100644 --- a/packages/search/lib/commands/_LIST.spec.ts +++ b/packages/search/lib/commands/_LIST.spec.ts @@ -1,19 +1,19 @@ -import { strict as assert } from 'assert'; +import { strict as assert } from 'node:assert'; import testUtils, { GLOBAL } from '../test-utils'; -import { transformArguments } from './_LIST'; +import _LIST from './_LIST'; describe('_LIST', () => { - it('transformArguments', () => { - assert.deepEqual( - transformArguments(), - ['FT._LIST'] - ); - }); + it('transformArguments', () => { + assert.deepEqual( + _LIST.transformArguments(), + ['FT._LIST'] + ); + }); - testUtils.testWithClient('client.ft._list', async client => { - assert.deepEqual( - await client.ft._list(), - [] - ); - }, GLOBAL.SERVERS.OPEN); + testUtils.testWithClient('client.ft._list', async client => { + assert.deepEqual( + await client.ft._list(), + [] + ); + }, GLOBAL.SERVERS.OPEN); }); diff --git a/packages/search/lib/commands/_LIST.ts b/packages/search/lib/commands/_LIST.ts index 588ec837c3b..efb6c31acce 100644 --- a/packages/search/lib/commands/_LIST.ts +++ b/packages/search/lib/commands/_LIST.ts @@ -1,5 +1,13 @@ -export function transformArguments(): Array { - return ['FT._LIST']; -} +import { ArrayReply, SetReply, BlobStringReply, Command } from '@redis/client/dist/lib/RESP/types'; -export declare function transformReply(): Array; +export default { + FIRST_KEY_INDEX: undefined, + IS_READ_ONLY: true, + transformArguments() { + return ['FT._LIST']; + }, + transformReply: { + 2: undefined as unknown as () => ArrayReply, + 3: undefined as unknown as () => SetReply + } +} as const satisfies Command; diff --git a/packages/search/lib/commands/index.spec.ts b/packages/search/lib/commands/index.spec.ts index 4c54a0dfdf2..04808932c59 100644 --- a/packages/search/lib/commands/index.spec.ts +++ b/packages/search/lib/commands/index.spec.ts @@ -1,5 +1,6 @@ -import { strict as assert } from 'assert'; -import { pushArgumentsWithLength, pushSortByArguments } from '.'; +import { strict as assert } from 'node:assert'; + +/* import { pushArgumentsWithLength, pushSortByArguments } from '.'; describe('pushSortByArguments', () => { describe('single', () => { @@ -44,3 +45,4 @@ it('pushArgumentsWithLength', () => { ['a', '2', 'b', 'c'] ); }); +*/ \ No newline at end of file diff --git a/packages/search/lib/commands/index.ts b/packages/search/lib/commands/index.ts index f907e1999e6..00706a70c2e 100644 --- a/packages/search/lib/commands/index.ts +++ b/packages/search/lib/commands/index.ts @@ -1,690 +1,105 @@ -import * as _LIST from './_LIST'; -import * as ALTER from './ALTER'; -import * as AGGREGATE_WITHCURSOR from './AGGREGATE_WITHCURSOR'; -import * as AGGREGATE from './AGGREGATE'; -import * as ALIASADD from './ALIASADD'; -import * as ALIASDEL from './ALIASDEL'; -import * as ALIASUPDATE from './ALIASUPDATE'; -import * as CONFIG_GET from './CONFIG_GET'; -import * as CONFIG_SET from './CONFIG_SET'; -import * as CREATE from './CREATE'; -import * as CURSOR_DEL from './CURSOR_DEL'; -import * as CURSOR_READ from './CURSOR_READ'; -import * as DICTADD from './DICTADD'; -import * as DICTDEL from './DICTDEL'; -import * as DICTDUMP from './DICTDUMP'; -import * as DROPINDEX from './DROPINDEX'; -import * as EXPLAIN from './EXPLAIN'; -import * as EXPLAINCLI from './EXPLAINCLI'; -import * as INFO from './INFO'; -import * as PROFILESEARCH from './PROFILE_SEARCH'; -import * as PROFILEAGGREGATE from './PROFILE_AGGREGATE'; -import * as SEARCH from './SEARCH'; -import * as SEARCH_NOCONTENT from './SEARCH_NOCONTENT'; -import * as SPELLCHECK from './SPELLCHECK'; -import * as SUGADD from './SUGADD'; -import * as SUGDEL from './SUGDEL'; -import * as SUGGET_WITHPAYLOADS from './SUGGET_WITHPAYLOADS'; -import * as SUGGET_WITHSCORES_WITHPAYLOADS from './SUGGET_WITHSCORES_WITHPAYLOADS'; -import * as SUGGET_WITHSCORES from './SUGGET_WITHSCORES'; -import * as SUGGET from './SUGGET'; -import * as SUGLEN from './SUGLEN'; -import * as SYNDUMP from './SYNDUMP'; -import * as SYNUPDATE from './SYNUPDATE'; -import * as TAGVALS from './TAGVALS'; -import { RedisCommandArgument, RedisCommandArguments } from '@redis/client/dist/lib/commands'; -import { pushOptionalVerdictArgument, pushVerdictArgument } from '@redis/client/dist/lib/commands/generic-transformers'; -import { SearchOptions } from './SEARCH'; +import _LIST from './_LIST'; +import ALTER from './ALTER'; +import AGGREGATE_WITHCURSOR from './AGGREGATE_WITHCURSOR'; +import AGGREGATE from './AGGREGATE'; +import ALIASADD from './ALIASADD'; +import ALIASDEL from './ALIASDEL'; +import ALIASUPDATE from './ALIASUPDATE'; +import CONFIG_GET from './CONFIG_GET'; +import CONFIG_SET from './CONFIG_SET'; +import CREATE from './CREATE'; +import CURSOR_DEL from './CURSOR_DEL'; +import CURSOR_READ from './CURSOR_READ'; +import DICTADD from './DICTADD'; +import DICTDEL from './DICTDEL'; +import DICTDUMP from './DICTDUMP'; +import DROPINDEX from './DROPINDEX'; +import EXPLAIN from './EXPLAIN'; +import EXPLAINCLI from './EXPLAINCLI'; +import INFO from './INFO'; +import PROFILESEARCH from './PROFILE_SEARCH'; +import PROFILEAGGREGATE from './PROFILE_AGGREGATE'; +import SEARCH_NOCONTENT from './SEARCH_NOCONTENT'; +import SEARCH from './SEARCH'; +import SPELLCHECK from './SPELLCHECK'; +import SUGADD from './SUGADD'; +import SUGDEL from './SUGDEL'; +import SUGGET_WITHPAYLOADS from './SUGGET_WITHPAYLOADS'; +import SUGGET_WITHSCORES_WITHPAYLOADS from './SUGGET_WITHSCORES_WITHPAYLOADS'; +import SUGGET_WITHSCORES from './SUGGET_WITHSCORES'; +import SUGGET from './SUGGET'; +import SUGLEN from './SUGLEN'; +import SYNDUMP from './SYNDUMP'; +import SYNUPDATE from './SYNUPDATE'; +import TAGVALS from './TAGVALS'; export default { - _LIST, - _list: _LIST, - ALTER, - alter: ALTER, - AGGREGATE_WITHCURSOR, - aggregateWithCursor: AGGREGATE_WITHCURSOR, - AGGREGATE, - aggregate: AGGREGATE, - ALIASADD, - aliasAdd: ALIASADD, - ALIASDEL, - aliasDel: ALIASDEL, - ALIASUPDATE, - aliasUpdate: ALIASUPDATE, - CONFIG_GET, - configGet: CONFIG_GET, - CONFIG_SET, - configSet: CONFIG_SET, - CREATE, - create: CREATE, - CURSOR_DEL, - cursorDel: CURSOR_DEL, - CURSOR_READ, - cursorRead: CURSOR_READ, - DICTADD, - dictAdd: DICTADD, - DICTDEL, - dictDel: DICTDEL, - DICTDUMP, - dictDump: DICTDUMP, - DROPINDEX, - dropIndex: DROPINDEX, - EXPLAIN, - explain: EXPLAIN, - EXPLAINCLI, - explainCli: EXPLAINCLI, - INFO, - info: INFO, - PROFILESEARCH, - profileSearch: PROFILESEARCH, - PROFILEAGGREGATE, - profileAggregate: PROFILEAGGREGATE, - SEARCH, - search: SEARCH, - SEARCH_NOCONTENT, - searchNoContent: SEARCH_NOCONTENT, - SPELLCHECK, - spellCheck: SPELLCHECK, - SUGADD, - sugAdd: SUGADD, - SUGDEL, - sugDel: SUGDEL, - SUGGET_WITHPAYLOADS, - sugGetWithPayloads: SUGGET_WITHPAYLOADS, - SUGGET_WITHSCORES_WITHPAYLOADS, - sugGetWithScoresWithPayloads: SUGGET_WITHSCORES_WITHPAYLOADS, - SUGGET_WITHSCORES, - sugGetWithScores: SUGGET_WITHSCORES, - SUGGET, - sugGet: SUGGET, - SUGLEN, - sugLen: SUGLEN, - SYNDUMP, - synDump: SYNDUMP, - SYNUPDATE, - synUpdate: SYNUPDATE, - TAGVALS, - tagVals: TAGVALS + _LIST, + _list: _LIST, + ALTER, + alter: ALTER, + AGGREGATE_WITHCURSOR, + aggregateWithCursor: AGGREGATE_WITHCURSOR, + AGGREGATE, + aggregate: AGGREGATE, + ALIASADD, + aliasAdd: ALIASADD, + ALIASDEL, + aliasDel: ALIASDEL, + ALIASUPDATE, + aliasUpdate: ALIASUPDATE, + CONFIG_GET, + configGet: CONFIG_GET, + CONFIG_SET, + configSet: CONFIG_SET, + CREATE, + create: CREATE, + CURSOR_DEL, + cursorDel: CURSOR_DEL, + CURSOR_READ, + cursorRead: CURSOR_READ, + DICTADD, + dictAdd: DICTADD, + DICTDEL, + dictDel: DICTDEL, + DICTDUMP, + dictDump: DICTDUMP, + DROPINDEX, + dropIndex: DROPINDEX, + EXPLAIN, + explain: EXPLAIN, + EXPLAINCLI, + explainCli: EXPLAINCLI, + INFO, + info: INFO, + PROFILESEARCH, + profileSearch: PROFILESEARCH, + PROFILEAGGREGATE, + profileAggregate: PROFILEAGGREGATE, + SEARCH_NOCONTENT, + searchNoContent: SEARCH_NOCONTENT, + SEARCH, + search: SEARCH, + SPELLCHECK, + spellCheck: SPELLCHECK, + SUGADD, + sugAdd: SUGADD, + SUGDEL, + sugDel: SUGDEL, + SUGGET_WITHPAYLOADS, + sugGetWithPayloads: SUGGET_WITHPAYLOADS, + SUGGET_WITHSCORES_WITHPAYLOADS, + sugGetWithScoresWithPayloads: SUGGET_WITHSCORES_WITHPAYLOADS, + SUGGET_WITHSCORES, + sugGetWithScores: SUGGET_WITHSCORES, + SUGGET, + sugGet: SUGGET, + SUGLEN, + sugLen: SUGLEN, + SYNDUMP, + synDump: SYNDUMP, + SYNUPDATE, + synUpdate: SYNUPDATE, + TAGVALS, + tagVals: TAGVALS }; - -export enum RedisSearchLanguages { - ARABIC = 'Arabic', - BASQUE = 'Basque', - CATALANA = 'Catalan', - DANISH = 'Danish', - DUTCH = 'Dutch', - ENGLISH = 'English', - FINNISH = 'Finnish', - FRENCH = 'French', - GERMAN = 'German', - GREEK = 'Greek', - HUNGARIAN = 'Hungarian', - INDONESAIN = 'Indonesian', - IRISH = 'Irish', - ITALIAN = 'Italian', - LITHUANIAN = 'Lithuanian', - NEPALI = 'Nepali', - NORWEIGAN = 'Norwegian', - PORTUGUESE = 'Portuguese', - ROMANIAN = 'Romanian', - RUSSIAN = 'Russian', - SPANISH = 'Spanish', - SWEDISH = 'Swedish', - TAMIL = 'Tamil', - TURKISH = 'Turkish', - CHINESE = 'Chinese' -} - -export type PropertyName = `${'@' | '$.'}${string}`; - -export type SortByProperty = string | { - BY: string; - DIRECTION?: 'ASC' | 'DESC'; -}; - -export function pushSortByProperty(args: RedisCommandArguments, sortBy: SortByProperty): void { - if (typeof sortBy === 'string') { - args.push(sortBy); - } else { - args.push(sortBy.BY); - - if (sortBy.DIRECTION) { - args.push(sortBy.DIRECTION); - } - } -} - -export function pushSortByArguments(args: RedisCommandArguments, name: string, sortBy: SortByProperty | Array): RedisCommandArguments { - const lengthBefore = args.push( - name, - '' // will be overwritten - ); - - if (Array.isArray(sortBy)) { - for (const field of sortBy) { - pushSortByProperty(args, field); - } - } else { - pushSortByProperty(args, sortBy); - } - - args[lengthBefore - 1] = (args.length - lengthBefore).toString(); - - return args; -} - -export function pushArgumentsWithLength(args: RedisCommandArguments, fn: (args: RedisCommandArguments) => void): RedisCommandArguments { - const lengthIndex = args.push('') - 1; - fn(args); - args[lengthIndex] = (args.length - lengthIndex - 1).toString(); - return args; -} - -export enum SchemaFieldTypes { - TEXT = 'TEXT', - NUMERIC = 'NUMERIC', - GEO = 'GEO', - TAG = 'TAG', - VECTOR = 'VECTOR', - GEOSHAPE = 'GEOSHAPE' -} - -type CreateSchemaField< - T extends SchemaFieldTypes, - E = Record -> = T | ({ - type: T; - AS?: string; - INDEXMISSING?: boolean; -} & E); - -type CommonFieldArguments = { - SORTABLE?: boolean | 'UNF'; - NOINDEX?: boolean; -}; - -type CreateSchemaCommonField< - T extends SchemaFieldTypes, - E = Record -> = CreateSchemaField< - T, - (CommonFieldArguments & E) ->; - -function pushCommonFieldArguments(args: RedisCommandArguments, fieldOptions: CommonFieldArguments) { - if (fieldOptions.SORTABLE) { - args.push('SORTABLE'); - - if (fieldOptions.SORTABLE === 'UNF') { - args.push('UNF'); - } - } - - if (fieldOptions.NOINDEX) { - args.push('NOINDEX'); - } -} - -export enum SchemaTextFieldPhonetics { - DM_EN = 'dm:en', - DM_FR = 'dm:fr', - FM_PT = 'dm:pt', - DM_ES = 'dm:es' -} - -type CreateSchemaTextField = CreateSchemaCommonField; - -type CreateSchemaNumericField = CreateSchemaCommonField; - -type CreateSchemaGeoField = CreateSchemaCommonField; - -type CreateSchemaTagField = CreateSchemaCommonField; - -export enum VectorAlgorithms { - FLAT = 'FLAT', - HNSW = 'HNSW' -} - -type CreateSchemaVectorField< - T extends VectorAlgorithms, - A extends Record -> = CreateSchemaField; - -type CreateSchemaFlatVectorField = CreateSchemaVectorField; - -type CreateSchemaHNSWVectorField = CreateSchemaVectorField; - -export const SCHEMA_GEO_SHAPE_COORD_SYSTEM = { - SPHERICAL: 'SPHERICAL', - FLAT: 'FLAT' -} as const; - -export type SchemaGeoShapeFieldCoordSystem = typeof SCHEMA_GEO_SHAPE_COORD_SYSTEM[keyof typeof SCHEMA_GEO_SHAPE_COORD_SYSTEM]; - -type CreateSchemaGeoShapeField = CreateSchemaCommonField; - -export interface RediSearchSchema { - [field: string]: - CreateSchemaTextField | - CreateSchemaNumericField | - CreateSchemaGeoField | - CreateSchemaTagField | - CreateSchemaFlatVectorField | - CreateSchemaHNSWVectorField | - CreateSchemaGeoShapeField; -} - -export function pushSchema(args: RedisCommandArguments, schema: RediSearchSchema) { - for (const [field, fieldOptions] of Object.entries(schema)) { - args.push(field); - - if (typeof fieldOptions === 'string') { - args.push(fieldOptions); - continue; - } - - if (fieldOptions.AS) { - args.push('AS', fieldOptions.AS); - } - - args.push(fieldOptions.type); - - switch (fieldOptions.type) { - case SchemaFieldTypes.TEXT: - if (fieldOptions.NOSTEM) { - args.push('NOSTEM'); - } - - if (fieldOptions.WEIGHT) { - args.push('WEIGHT', fieldOptions.WEIGHT.toString()); - } - - if (fieldOptions.PHONETIC) { - args.push('PHONETIC', fieldOptions.PHONETIC); - } - - if (fieldOptions.WITHSUFFIXTRIE) { - args.push('WITHSUFFIXTRIE'); - } - - pushCommonFieldArguments(args, fieldOptions); - - if (fieldOptions.INDEXEMPTY) { - args.push('INDEXEMPTY'); - } - - break; - - case SchemaFieldTypes.NUMERIC: - case SchemaFieldTypes.GEO: - pushCommonFieldArguments(args, fieldOptions); - break; - - case SchemaFieldTypes.TAG: - if (fieldOptions.SEPARATOR) { - args.push('SEPARATOR', fieldOptions.SEPARATOR); - } - - if (fieldOptions.CASESENSITIVE) { - args.push('CASESENSITIVE'); - } - - if (fieldOptions.WITHSUFFIXTRIE) { - args.push('WITHSUFFIXTRIE'); - } - - pushCommonFieldArguments(args, fieldOptions); - - if (fieldOptions.INDEXEMPTY) { - args.push('INDEXEMPTY'); - } - - break; - - case SchemaFieldTypes.VECTOR: - args.push(fieldOptions.ALGORITHM); - - pushArgumentsWithLength(args, () => { - args.push( - 'TYPE', fieldOptions.TYPE, - 'DIM', fieldOptions.DIM.toString(), - 'DISTANCE_METRIC', fieldOptions.DISTANCE_METRIC - ); - - if (fieldOptions.INITIAL_CAP) { - args.push('INITIAL_CAP', fieldOptions.INITIAL_CAP.toString()); - } - - switch (fieldOptions.ALGORITHM) { - case VectorAlgorithms.FLAT: - if (fieldOptions.BLOCK_SIZE) { - args.push('BLOCK_SIZE', fieldOptions.BLOCK_SIZE.toString()); - } - - break; - - case VectorAlgorithms.HNSW: - if (fieldOptions.M) { - args.push('M', fieldOptions.M.toString()); - } - - if (fieldOptions.EF_CONSTRUCTION) { - args.push('EF_CONSTRUCTION', fieldOptions.EF_CONSTRUCTION.toString()); - } - - if (fieldOptions.EF_RUNTIME) { - args.push('EF_RUNTIME', fieldOptions.EF_RUNTIME.toString()); - } - - break; - } - }); - - break; - - case SchemaFieldTypes.GEOSHAPE: - if (fieldOptions.COORD_SYSTEM !== undefined) { - args.push('COORD_SYSTEM', fieldOptions.COORD_SYSTEM); - } - - pushCommonFieldArguments(args, fieldOptions); - - break; - } - - if (fieldOptions.INDEXMISSING) { - args.push('INDEXMISSING'); - } - } -} - -export type Params = Record; - -export function pushParamsArgs( - args: RedisCommandArguments, - params?: Params -): RedisCommandArguments { - if (params) { - const enrties = Object.entries(params); - args.push('PARAMS', (enrties.length * 2).toString()); - for (const [key, value] of enrties) { - args.push(key, typeof value === 'number' ? value.toString() : value); - } - } - - return args; -} - -export function pushSearchOptions( - args: RedisCommandArguments, - options?: SearchOptions -): RedisCommandArguments { - if (options?.VERBATIM) { - args.push('VERBATIM'); - } - - if (options?.NOSTOPWORDS) { - args.push('NOSTOPWORDS'); - } - - // if (options?.WITHSCORES) { - // args.push('WITHSCORES'); - // } - - // if (options?.WITHPAYLOADS) { - // args.push('WITHPAYLOADS'); - // } - - pushOptionalVerdictArgument(args, 'INKEYS', options?.INKEYS); - pushOptionalVerdictArgument(args, 'INFIELDS', options?.INFIELDS); - pushOptionalVerdictArgument(args, 'RETURN', options?.RETURN); - - if (options?.SUMMARIZE) { - args.push('SUMMARIZE'); - - if (typeof options.SUMMARIZE === 'object') { - if (options.SUMMARIZE.FIELDS) { - args.push('FIELDS'); - pushVerdictArgument(args, options.SUMMARIZE.FIELDS); - } - - if (options.SUMMARIZE.FRAGS) { - args.push('FRAGS', options.SUMMARIZE.FRAGS.toString()); - } - - if (options.SUMMARIZE.LEN) { - args.push('LEN', options.SUMMARIZE.LEN.toString()); - } - - if (options.SUMMARIZE.SEPARATOR) { - args.push('SEPARATOR', options.SUMMARIZE.SEPARATOR); - } - } - } - - if (options?.HIGHLIGHT) { - args.push('HIGHLIGHT'); - - if (typeof options.HIGHLIGHT === 'object') { - if (options.HIGHLIGHT.FIELDS) { - args.push('FIELDS'); - pushVerdictArgument(args, options.HIGHLIGHT.FIELDS); - } - - if (options.HIGHLIGHT.TAGS) { - args.push('TAGS', options.HIGHLIGHT.TAGS.open, options.HIGHLIGHT.TAGS.close); - } - } - } - - if (options?.SLOP) { - args.push('SLOP', options.SLOP.toString()); - } - - if (options?.INORDER) { - args.push('INORDER'); - } - - if (options?.LANGUAGE) { - args.push('LANGUAGE', options.LANGUAGE); - } - - if (options?.EXPANDER) { - args.push('EXPANDER', options.EXPANDER); - } - - if (options?.SCORER) { - args.push('SCORER', options.SCORER); - } - - // if (options?.EXPLAINSCORE) { - // args.push('EXPLAINSCORE'); - // } - - // if (options?.PAYLOAD) { - // args.push('PAYLOAD', options.PAYLOAD); - // } - - if (options?.SORTBY) { - args.push('SORTBY'); - pushSortByProperty(args, options.SORTBY); - } - - // if (options?.MSORTBY) { - // pushSortByArguments(args, 'MSORTBY', options.MSORTBY); - // } - - if (options?.LIMIT) { - args.push( - 'LIMIT', - options.LIMIT.from.toString(), - options.LIMIT.size.toString() - ); - } - - if (options?.PARAMS) { - pushParamsArgs(args, options.PARAMS); - } - - if (options?.DIALECT) { - args.push('DIALECT', options.DIALECT.toString()); - } - - if (options?.RETURN?.length === 0) { - args.preserve = true; - } - - if (options?.TIMEOUT !== undefined) { - args.push('TIMEOUT', options.TIMEOUT.toString()); - } - - return args; -} - -interface SearchDocumentValue { - [key: string]: string | number | null | Array | SearchDocumentValue; -} - -export interface SearchReply { - total: number; - documents: Array<{ - id: string; - value: SearchDocumentValue; - }>; -} - -export interface ProfileOptions { - LIMITED?: true; -} - -export type ProfileRawReply = [ - results: T, - profile: [ - _: string, - TotalProfileTime: string, - _: string, - ParsingTime: string, - _: string, - PipelineCreationTime: string, - _: string, - IteratorsProfile: Array - ] -]; - -export interface ProfileReply { - results: SearchReply | AGGREGATE.AggregateReply; - profile: ProfileData; -} - -interface ChildIterator { - type?: string, - counter?: number, - term?: string, - size?: number, - time?: string, - childIterators?: Array -} - -interface IteratorsProfile { - type?: string, - counter?: number, - queryType?: string, - time?: string, - childIterators?: Array -} - -interface ProfileData { - totalProfileTime: string, - parsingTime: string, - pipelineCreationTime: string, - iteratorsProfile: IteratorsProfile -} - -export function transformProfile(reply: Array): ProfileData{ - return { - totalProfileTime: reply[0][1], - parsingTime: reply[1][1], - pipelineCreationTime: reply[2][1], - iteratorsProfile: transformIterators(reply[3][1]) - }; -} - -function transformIterators(IteratorsProfile: Array): IteratorsProfile { - var res: IteratorsProfile = {}; - for (let i = 0; i < IteratorsProfile.length; i += 2) { - const value = IteratorsProfile[i+1]; - switch (IteratorsProfile[i]) { - case 'Type': - res.type = value; - break; - case 'Counter': - res.counter = value; - break; - case 'Time': - res.time = value; - break; - case 'Query type': - res.queryType = value; - break; - case 'Child iterators': - res.childIterators = value.map(transformChildIterators); - break; - } - } - - return res; -} - -function transformChildIterators(IteratorsProfile: Array): ChildIterator { - var res: ChildIterator = {}; - for (let i = 1; i < IteratorsProfile.length; i += 2) { - const value = IteratorsProfile[i+1]; - switch (IteratorsProfile[i]) { - case 'Type': - res.type = value; - break; - case 'Counter': - res.counter = value; - break; - case 'Time': - res.time = value; - break; - case 'Size': - res.size = value; - break; - case 'Term': - res.term = value; - break; - case 'Child iterators': - res.childIterators = value.map(transformChildIterators); - break; - } - } - - return res; -} diff --git a/packages/search/lib/index.ts b/packages/search/lib/index.ts index 0f84c11466f..34d57e8ae5e 100644 --- a/packages/search/lib/index.ts +++ b/packages/search/lib/index.ts @@ -1,5 +1,7 @@ export { default } from './commands'; -export { RediSearchSchema, RedisSearchLanguages, SchemaFieldTypes, SchemaTextFieldPhonetics, SearchReply, VectorAlgorithms } from './commands'; -export { AggregateGroupByReducers, AggregateSteps } from './commands/AGGREGATE'; -export { SearchOptions } from './commands/SEARCH'; +export { SCHEMA_FIELD_TYPE, SchemaFieldType } from './commands/CREATE'; + +// export { RediSearchSchema, RedisSearchLanguages, SchemaFieldTypes, SchemaTextFieldPhonetics, SearchReply, VectorAlgorithms } from './commands'; +// export { AggregateGroupByReducers, AggregateSteps } from './commands/AGGREGATE'; +// export { SearchOptions } from './commands/SEARCH'; diff --git a/packages/search/lib/test-utils.ts b/packages/search/lib/test-utils.ts index 9e0af209103..ce43a37bc21 100644 --- a/packages/search/lib/test-utils.ts +++ b/packages/search/lib/test-utils.ts @@ -2,15 +2,15 @@ import TestUtils from '@redis/test-utils'; import RediSearch from '.'; export default new TestUtils({ - dockerImageName: 'redislabs/redisearch', + dockerImageName: 'redis/redis-stack', dockerImageVersionArgument: 'redisearch-version', - defaultDockerVersion: '2.4.9' + defaultDockerVersion: '7.4.0-v1' }); export const GLOBAL = { SERVERS: { OPEN: { - serverArguments: ['--loadmodule /usr/lib/redis/modules/redisearch.so'], + serverArguments: [], clientOptions: { modules: { ft: RediSearch diff --git a/packages/search/package.json b/packages/search/package.json index aaf9bc50f11..ea6c8a6b4c2 100644 --- a/packages/search/package.json +++ b/packages/search/package.json @@ -1,30 +1,24 @@ { "name": "@redis/search", - "version": "1.2.0", + "version": "2.0.0-next.2", "license": "MIT", - "main": "./dist/index.js", - "types": "./dist/index.d.ts", + "main": "./dist/lib/index.js", + "types": "./dist/lib/index.d.ts", "files": [ - "dist/" + "dist/", + "!dist/tsconfig.tsbuildinfo" ], "scripts": { - "test": "nyc -r text-summary -r lcov mocha -r source-map-support/register -r ts-node/register './lib/**/*.spec.ts'", - "build": "tsc", - "documentation": "typedoc" + "test": "nyc -r text-summary -r lcov mocha -r tsx './lib/**/*.spec.ts'" }, "peerDependencies": { - "@redis/client": "^1.0.0" + "@redis/client": "^2.0.0-next.4" }, "devDependencies": { - "@istanbuljs/nyc-config-typescript": "^1.0.2", - "@redis/test-utils": "*", - "@types/node": "^20.6.2", - "nyc": "^15.1.0", - "release-it": "^16.1.5", - "source-map-support": "^0.5.21", - "ts-node": "^10.9.1", - "typedoc": "^0.25.1", - "typescript": "^5.2.2" + "@redis/test-utils": "*" + }, + "engines": { + "node": ">= 18" }, "repository": { "type": "git", diff --git a/packages/test-utils/lib/dockers.ts b/packages/test-utils/lib/dockers.ts index a7e1c610eee..a1cb63eb7bf 100644 --- a/packages/test-utils/lib/dockers.ts +++ b/packages/test-utils/lib/dockers.ts @@ -1,260 +1,256 @@ -import { createConnection } from 'net'; -import { once } from 'events'; -import RedisClient from '@redis/client/dist/lib/client'; -import { promiseTimeout } from '@redis/client/dist/lib/utils'; -import { ClusterSlotsReply } from '@redis/client/dist/lib/commands/CLUSTER_SLOTS'; -import * as path from 'path'; -import { promisify } from 'util'; -import { exec } from 'child_process'; +import { createConnection } from 'node:net'; +import { once } from 'node:events'; +import { createClient } from '@redis/client/index'; +import { setTimeout } from 'node:timers/promises'; +// import { ClusterSlotsReply } from '@redis/client/dist/lib/commands/CLUSTER_SLOTS'; +import { promisify } from 'node:util'; +import { exec } from 'node:child_process'; const execAsync = promisify(exec); interface ErrorWithCode extends Error { - code: string; + code: string; } async function isPortAvailable(port: number): Promise { - try { - const socket = createConnection({ port }); - await once(socket, 'connect'); - socket.end(); - } catch (err) { - if (err instanceof Error && (err as ErrorWithCode).code === 'ECONNREFUSED') { - return true; - } + try { + const socket = createConnection({ port }); + await once(socket, 'connect'); + socket.end(); + } catch (err) { + if (err instanceof Error && (err as ErrorWithCode).code === 'ECONNREFUSED') { + return true; } + } - return false; + return false; } -const portIterator = (async function*(): AsyncIterableIterator { - for (let i = 6379; i < 65535; i++) { - if (await isPortAvailable(i)) { - yield i; - } +const portIterator = (async function* (): AsyncIterableIterator { + for (let i = 6379; i < 65535; i++) { + if (await isPortAvailable(i)) { + yield i; } + } - throw new Error('All ports are in use'); + throw new Error('All ports are in use'); })(); export interface RedisServerDockerConfig { - image: string; - version: string; + image: string; + version: string; } export interface RedisServerDocker { - port: number; - dockerId: string; + port: number; + dockerId: string; } -// ".." cause it'll be in `./dist` -const DOCKER_FODLER_PATH = path.join(__dirname, '../docker'); - async function spawnRedisServerDocker({ image, version }: RedisServerDockerConfig, serverArguments: Array): Promise { - const port = (await portIterator.next()).value, - { stdout, stderr } = await execAsync( - 'docker run -d --network host $(' + - `docker build ${DOCKER_FODLER_PATH} -q ` + - `--build-arg IMAGE=${image}:${version} ` + - `--build-arg REDIS_ARGUMENTS="--save '' --port ${port.toString()} ${serverArguments.join(' ')}"` + - ')' - ); + const port = (await portIterator.next()).value, + { stdout, stderr } = await execAsync( + `docker run -e REDIS_ARGS="--port ${port.toString()} ${serverArguments.join(' ')}" -d --network host ${image}:${version}` + ); - if (!stdout) { - throw new Error(`docker run error - ${stderr}`); - } + if (!stdout) { + throw new Error(`docker run error - ${stderr}`); + } - while (await isPortAvailable(port)) { - await promiseTimeout(50); - } + while (await isPortAvailable(port)) { + await setTimeout(50); + } - return { - port, - dockerId: stdout.trim() - }; + return { + port, + dockerId: stdout.trim() + }; } const RUNNING_SERVERS = new Map, ReturnType>(); export function spawnRedisServer(dockerConfig: RedisServerDockerConfig, serverArguments: Array): Promise { - const runningServer = RUNNING_SERVERS.get(serverArguments); - if (runningServer) { - return runningServer; - } - - const dockerPromise = spawnRedisServerDocker(dockerConfig, serverArguments); - RUNNING_SERVERS.set(serverArguments, dockerPromise); - return dockerPromise; + const runningServer = RUNNING_SERVERS.get(serverArguments); + if (runningServer) { + return runningServer; + } + + const dockerPromise = spawnRedisServerDocker(dockerConfig, serverArguments); + RUNNING_SERVERS.set(serverArguments, dockerPromise); + return dockerPromise; } async function dockerRemove(dockerId: string): Promise { - const { stderr } = await execAsync(`docker rm -f ${dockerId}`); - if (stderr) { - throw new Error(`docker rm error - ${stderr}`); - } + const { stderr } = await execAsync(`docker rm -f ${dockerId}`); + if (stderr) { + throw new Error(`docker rm error - ${stderr}`); + } } after(() => { - return Promise.all( - [...RUNNING_SERVERS.values()].map(async dockerPromise => - await dockerRemove((await dockerPromise).dockerId) - ) - ); + return Promise.all( + [...RUNNING_SERVERS.values()].map(async dockerPromise => + await dockerRemove((await dockerPromise).dockerId) + ) + ); }); export interface RedisClusterDockersConfig extends RedisServerDockerConfig { - numberOfMasters?: number; - numberOfReplicas?: number; + numberOfMasters?: number; + numberOfReplicas?: number; } async function spawnRedisClusterNodeDockers( - dockersConfig: RedisClusterDockersConfig, - serverArguments: Array, - fromSlot: number, - toSlot: number + dockersConfig: RedisClusterDockersConfig, + serverArguments: Array, + fromSlot: number, + toSlot: number ) { - const range: Array = []; - for (let i = fromSlot; i < toSlot; i++) { - range.push(i); - } - - const master = await spawnRedisClusterNodeDocker( - dockersConfig, - serverArguments - ); + const range: Array = []; + for (let i = fromSlot; i < toSlot; i++) { + range.push(i); + } + + const master = await spawnRedisClusterNodeDocker( + dockersConfig, + serverArguments + ); + + await master.client.clusterAddSlots(range); + + if (!dockersConfig.numberOfReplicas) return [master]; + + const replicasPromises: Array> = []; + for (let i = 0; i < (dockersConfig.numberOfReplicas ?? 0); i++) { + replicasPromises.push( + spawnRedisClusterNodeDocker(dockersConfig, [ + ...serverArguments, + '--cluster-enabled', + 'yes', + '--cluster-node-timeout', + '5000' + ]).then(async replica => { + await replica.client.clusterMeet('127.0.0.1', master.docker.port); + + while ((await replica.client.clusterSlots()).length === 0) { + await setTimeout(50); + } - await master.client.clusterAddSlots(range); - - if (!dockersConfig.numberOfReplicas) return [master]; - - const replicasPromises: Array> = []; - for (let i = 0; i < (dockersConfig.numberOfReplicas ?? 0); i++) { - replicasPromises.push( - spawnRedisClusterNodeDocker(dockersConfig, [ - ...serverArguments, - '--cluster-enabled', - 'yes', - '--cluster-node-timeout', - '5000' - ]).then(async replica => { - await replica.client.clusterMeet('127.0.0.1', master.docker.port); - - while ((await replica.client.clusterSlots()).length === 0) { - await promiseTimeout(50); - } - - await replica.client.clusterReplicate( - await master.client.clusterMyId() - ); - - return replica; - }) + await replica.client.clusterReplicate( + await master.client.clusterMyId() ); - } - return [ - master, - ...await Promise.all(replicasPromises) - ]; + return replica; + }) + ); + } + + return [ + master, + ...await Promise.all(replicasPromises) + ]; } async function spawnRedisClusterNodeDocker( - dockersConfig: RedisClusterDockersConfig, - serverArguments: Array + dockersConfig: RedisClusterDockersConfig, + serverArguments: Array ) { - const docker = await spawnRedisServerDocker(dockersConfig, [ - ...serverArguments, - '--cluster-enabled', - 'yes', - '--cluster-node-timeout', - '5000' - ]), - client = RedisClient.create({ - socket: { - port: docker.port - } - }); - - await client.connect(); - - return { - docker, - client - }; + const docker = await spawnRedisServerDocker(dockersConfig, [ + ...serverArguments, + '--cluster-enabled', + 'yes', + '--cluster-node-timeout', + '5000' + ]), + client = createClient({ + socket: { + port: docker.port + } + }); + + await client.connect(); + + return { + docker, + client + }; } const SLOTS = 16384; async function spawnRedisClusterDockers( - dockersConfig: RedisClusterDockersConfig, - serverArguments: Array + dockersConfig: RedisClusterDockersConfig, + serverArguments: Array ): Promise> { - const numberOfMasters = dockersConfig.numberOfMasters ?? 2, - slotsPerNode = Math.floor(SLOTS / numberOfMasters), - spawnPromises: Array> = []; - for (let i = 0; i < numberOfMasters; i++) { - const fromSlot = i * slotsPerNode, - toSlot = i === numberOfMasters - 1 ? SLOTS : fromSlot + slotsPerNode; - spawnPromises.push( - spawnRedisClusterNodeDockers( - dockersConfig, - serverArguments, - fromSlot, - toSlot - ) - ); - } + const numberOfMasters = dockersConfig.numberOfMasters ?? 2, + slotsPerNode = Math.floor(SLOTS / numberOfMasters), + spawnPromises: Array> = []; + for (let i = 0; i < numberOfMasters; i++) { + const fromSlot = i * slotsPerNode, + toSlot = i === numberOfMasters - 1 ? SLOTS : fromSlot + slotsPerNode; + spawnPromises.push( + spawnRedisClusterNodeDockers( + dockersConfig, + serverArguments, + fromSlot, + toSlot + ) + ); + } - const nodes = (await Promise.all(spawnPromises)).flat(), - meetPromises: Array> = []; - for (let i = 1; i < nodes.length; i++) { - meetPromises.push( - nodes[i].client.clusterMeet('127.0.0.1', nodes[0].docker.port) - ); - } + const nodes = (await Promise.all(spawnPromises)).flat(), + meetPromises: Array> = []; + for (let i = 1; i < nodes.length; i++) { + meetPromises.push( + nodes[i].client.clusterMeet('127.0.0.1', nodes[0].docker.port) + ); + } - await Promise.all(meetPromises); + await Promise.all(meetPromises); - await Promise.all( - nodes.map(async ({ client }) => { - while (totalNodes(await client.clusterSlots()) !== nodes.length) { - await promiseTimeout(50); - } - - return client.disconnect(); - }) - ); + await Promise.all( + nodes.map(async ({ client }) => { + while ( + totalNodes(await client.clusterSlots()) !== nodes.length || + !(await client.sendCommand(['CLUSTER', 'INFO'])).startsWith('cluster_state:ok') // TODO + ) { + await setTimeout(50); + } + + client.destroy(); + }) + ); - return nodes.map(({ docker }) => docker); + return nodes.map(({ docker }) => docker); } -function totalNodes(slots: ClusterSlotsReply) { - let total = slots.length; - for (const slot of slots) { - total += slot.replicas.length; - } +// TODO: type ClusterSlotsReply +function totalNodes(slots: any) { + let total = slots.length; + for (const slot of slots) { + total += slot.replicas.length; + } - return total; + return total; } const RUNNING_CLUSTERS = new Map, ReturnType>(); export function spawnRedisCluster(dockersConfig: RedisClusterDockersConfig, serverArguments: Array): Promise> { - const runningCluster = RUNNING_CLUSTERS.get(serverArguments); - if (runningCluster) { - return runningCluster; - } - - const dockersPromise = spawnRedisClusterDockers(dockersConfig, serverArguments); - RUNNING_CLUSTERS.set(serverArguments, dockersPromise); - return dockersPromise; + const runningCluster = RUNNING_CLUSTERS.get(serverArguments); + if (runningCluster) { + return runningCluster; + } + + const dockersPromise = spawnRedisClusterDockers(dockersConfig, serverArguments); + RUNNING_CLUSTERS.set(serverArguments, dockersPromise); + return dockersPromise; } after(() => { - return Promise.all( - [...RUNNING_CLUSTERS.values()].map(async dockersPromise => { - return Promise.all( - (await dockersPromise).map(({ dockerId }) => dockerRemove(dockerId)) - ); - }) - ); + return Promise.all( + [...RUNNING_CLUSTERS.values()].map(async dockersPromise => { + return Promise.all( + (await dockersPromise).map(({ dockerId }) => dockerRemove(dockerId)) + ); + }) + ); }); diff --git a/packages/test-utils/lib/index.ts b/packages/test-utils/lib/index.ts index b9195c5717a..87ba34db7ef 100644 --- a/packages/test-utils/lib/index.ts +++ b/packages/test-utils/lib/index.ts @@ -1,226 +1,339 @@ -import { RedisModules, RedisFunctions, RedisScripts } from '@redis/client/lib/commands'; -import RedisClient, { RedisClientOptions, RedisClientType } from '@redis/client/lib/client'; -import RedisCluster, { RedisClusterOptions, RedisClusterType } from '@redis/client/lib/cluster'; -import { RedisSocketCommonOptions } from '@redis/client/lib/client/socket'; +import { + RedisModules, + RedisFunctions, + RedisScripts, + RespVersions, + TypeMapping, + // CommandPolicies, + createClient, + RedisClientOptions, + RedisClientType, + RedisPoolOptions, + RedisClientPoolType, + createClientPool, + createCluster, + RedisClusterOptions, + RedisClusterType +} from '@redis/client/index'; import { RedisServerDockerConfig, spawnRedisServer, spawnRedisCluster } from './dockers'; import yargs from 'yargs'; import { hideBin } from 'yargs/helpers'; interface TestUtilsConfig { - dockerImageName: string; - dockerImageVersionArgument: string; - defaultDockerVersion?: string; + dockerImageName: string; + dockerImageVersionArgument: string; + defaultDockerVersion?: string; } interface CommonTestOptions { - minimumDockerVersion?: Array; + serverArguments: Array; + minimumDockerVersion?: Array; } interface ClientTestOptions< - M extends RedisModules, - F extends RedisFunctions, - S extends RedisScripts + M extends RedisModules, + F extends RedisFunctions, + S extends RedisScripts, + RESP extends RespVersions, + TYPE_MAPPING extends TypeMapping +> extends CommonTestOptions { + clientOptions?: Partial>; + disableClientSetup?: boolean; +} + +interface ClientPoolTestOptions< + M extends RedisModules, + F extends RedisFunctions, + S extends RedisScripts, + RESP extends RespVersions, + TYPE_MAPPING extends TypeMapping > extends CommonTestOptions { - serverArguments: Array; - clientOptions?: Partial, 'socket'> & { socket: RedisSocketCommonOptions }>; - disableClientSetup?: boolean; + clientOptions?: Partial>; + poolOptions?: RedisPoolOptions; } interface ClusterTestOptions< - M extends RedisModules, - F extends RedisFunctions, - S extends RedisScripts + M extends RedisModules, + F extends RedisFunctions, + S extends RedisScripts, + RESP extends RespVersions, + TYPE_MAPPING extends TypeMapping + // POLICIES extends CommandPolicies > extends CommonTestOptions { - serverArguments: Array; - clusterConfiguration?: Partial>; - numberOfMasters?: number; - numberOfReplicas?: number; + clusterConfiguration?: Partial>; + numberOfMasters?: number; + numberOfReplicas?: number; +} + +interface AllTestOptions< + M extends RedisModules, + F extends RedisFunctions, + S extends RedisScripts, + RESP extends RespVersions, + TYPE_MAPPING extends TypeMapping + // POLICIES extends CommandPolicies +> { + client: ClientTestOptions; + cluster: ClusterTestOptions; } interface Version { - string: string; - numbers: Array; + string: string; + numbers: Array; } export default class TestUtils { - static #parseVersionNumber(version: string): Array { - if (version === 'latest' || version === 'edge') return [Infinity]; - - const dashIndex = version.indexOf('-'); - return (dashIndex === -1 ? version : version.substring(0, dashIndex)) - .split('.') - .map(x => { - const value = Number(x); - if (Number.isNaN(value)) { - throw new TypeError(`${version} is not a valid redis version`); - } - - return value; - }); - } + static #parseVersionNumber(version: string): Array { + if (version === 'latest' || version === 'edge') return [Infinity]; - static #getVersion(argumentName: string, defaultVersion = 'latest'): Version { - return yargs(hideBin(process.argv)) - .option(argumentName, { - type: 'string', - default: defaultVersion - }) - .coerce(argumentName, (version: string) => { - return { - string: version, - numbers: TestUtils.#parseVersionNumber(version) - }; - }) - .demandOption(argumentName) - .parseSync()[argumentName]; - } + const dashIndex = version.indexOf('-'); + return (dashIndex === -1 ? version : version.substring(0, dashIndex)) + .split('.') + .map(x => { + const value = Number(x); + if (Number.isNaN(value)) { + throw new TypeError(`${version} is not a valid redis version`); + } - readonly #VERSION_NUMBERS: Array; - readonly #DOCKER_IMAGE: RedisServerDockerConfig; + return value; + }); + } - constructor(config: TestUtilsConfig) { - const { string, numbers } = TestUtils.#getVersion(config.dockerImageVersionArgument, config.defaultDockerVersion); - this.#VERSION_NUMBERS = numbers; - this.#DOCKER_IMAGE = { - image: config.dockerImageName, - version: string + static #getVersion(argumentName: string, defaultVersion = 'latest'): Version { + return yargs(hideBin(process.argv)) + .option(argumentName, { + type: 'string', + default: defaultVersion + }) + .coerce(argumentName, (version: string) => { + return { + string: version, + numbers: TestUtils.#parseVersionNumber(version) }; - } + }) + .demandOption(argumentName) + .parseSync()[argumentName]; + } - isVersionGreaterThan(minimumVersion: Array | undefined): boolean { - if (minimumVersion === undefined) return true; + readonly #VERSION_NUMBERS: Array; + readonly #DOCKER_IMAGE: RedisServerDockerConfig; - const lastIndex = Math.min(this.#VERSION_NUMBERS.length, minimumVersion.length) - 1; - for (let i = 0; i < lastIndex; i++) { - if (this.#VERSION_NUMBERS[i] > minimumVersion[i]) { - return true; - } else if (minimumVersion[i] > this.#VERSION_NUMBERS[i]) { - return false; - } - } + constructor(config: TestUtilsConfig) { + const { string, numbers } = TestUtils.#getVersion(config.dockerImageVersionArgument, config.defaultDockerVersion); + this.#VERSION_NUMBERS = numbers; + this.#DOCKER_IMAGE = { + image: config.dockerImageName, + version: string + }; + } - return this.#VERSION_NUMBERS[lastIndex] >= minimumVersion[lastIndex]; + isVersionGreaterThan(minimumVersion: Array | undefined): boolean { + if (minimumVersion === undefined) return true; + + const lastIndex = Math.min(this.#VERSION_NUMBERS.length, minimumVersion.length) - 1; + for (let i = 0; i < lastIndex; i++) { + if (this.#VERSION_NUMBERS[i] > minimumVersion[i]) { + return true; + } else if (minimumVersion[i] > this.#VERSION_NUMBERS[i]) { + return false; + } } - isVersionGreaterThanHook(minimumVersion: Array | undefined): void { - const isVersionGreaterThan = this.isVersionGreaterThan.bind(this); - before(function () { - if (!isVersionGreaterThan(minimumVersion)) { - return this.skip(); - } - }); + return this.#VERSION_NUMBERS[lastIndex] >= minimumVersion[lastIndex]; + } + + isVersionGreaterThanHook(minimumVersion: Array | undefined): void { + const isVersionGreaterThan = this.isVersionGreaterThan.bind(this); + before(function () { + if (!isVersionGreaterThan(minimumVersion)) { + return this.skip(); + } + }); + } + + testWithClient< + M extends RedisModules = {}, + F extends RedisFunctions = {}, + S extends RedisScripts = {}, + RESP extends RespVersions = 2, + TYPE_MAPPING extends TypeMapping = {} + >( + title: string, + fn: (client: RedisClientType) => unknown, + options: ClientTestOptions + ): void { + let dockerPromise: ReturnType; + if (this.isVersionGreaterThan(options.minimumDockerVersion)) { + const dockerImage = this.#DOCKER_IMAGE; + before(function () { + this.timeout(30000); + + dockerPromise = spawnRedisServer(dockerImage, options.serverArguments); + return dockerPromise; + }); } - testWithClient< - M extends RedisModules, - F extends RedisFunctions, - S extends RedisScripts - >( - title: string, - fn: (client: RedisClientType) => unknown, - options: ClientTestOptions - ): void { - let dockerPromise: ReturnType; - if (this.isVersionGreaterThan(options.minimumDockerVersion)) { - const dockerImage = this.#DOCKER_IMAGE; - before(function () { - this.timeout(30000); - - dockerPromise = spawnRedisServer(dockerImage, options.serverArguments); - return dockerPromise; - }); + it(title, async function () { + if (!dockerPromise) return this.skip(); + + const client = createClient({ + ...options.clientOptions, + socket: { + ...options.clientOptions?.socket, + // TODO + // @ts-ignore + port: (await dockerPromise).port } + }); - it(title, async function() { - if (!dockerPromise) return this.skip(); + if (options.disableClientSetup) { + return fn(client); + } - const client = RedisClient.create({ - ...options?.clientOptions, - socket: { - ...options?.clientOptions?.socket, - port: (await dockerPromise).port - } - }); + await client.connect(); - if (options.disableClientSetup) { - return fn(client); - } + try { + await client.flushAll(); + await fn(client); + } finally { + if (client.isOpen) { + await client.flushAll(); + client.destroy(); + } + } + }); + } - await client.connect(); + testWithClientPool< + M extends RedisModules = {}, + F extends RedisFunctions = {}, + S extends RedisScripts = {}, + RESP extends RespVersions = 2, + TYPE_MAPPING extends TypeMapping = {} + >( + title: string, + fn: (client: RedisClientPoolType) => unknown, + options: ClientPoolTestOptions + ): void { + let dockerPromise: ReturnType; + if (this.isVersionGreaterThan(options.minimumDockerVersion)) { + const dockerImage = this.#DOCKER_IMAGE; + before(function () { + this.timeout(30000); - try { - await client.flushAll(); - await fn(client); - } finally { - if (client.isOpen) { - await client.flushAll(); - await client.disconnect(); - } - } - }); + dockerPromise = spawnRedisServer(dockerImage, options.serverArguments); + return dockerPromise; + }); } - static async #clusterFlushAll< - M extends RedisModules, - F extends RedisFunctions, - S extends RedisScripts - >(cluster: RedisClusterType): Promise { - return Promise.all( - cluster.masters.map(async ({ client }) => { - if (client) { - await (await client).flushAll(); - } - }) - ); - } + it(title, async function () { + if (!dockerPromise) return this.skip(); + + const pool = createClientPool({ + ...options.clientOptions, + socket: { + ...options.clientOptions?.socket, + // TODO + // @ts-ignore + port: (await dockerPromise).port + } + }, options.poolOptions); + + await pool.connect(); - testWithCluster< - M extends RedisModules, - F extends RedisFunctions, - S extends RedisScripts - >( - title: string, - fn: (cluster: RedisClusterType) => unknown, - options: ClusterTestOptions - ): void { - let dockersPromise: ReturnType; - if (this.isVersionGreaterThan(options.minimumDockerVersion)) { - const dockerImage = this.#DOCKER_IMAGE; - before(function () { - this.timeout(30000); - - dockersPromise = spawnRedisCluster({ - ...dockerImage, - numberOfMasters: options?.numberOfMasters, - numberOfReplicas: options?.numberOfReplicas - }, options.serverArguments); - return dockersPromise; - }); + try { + await pool.flushAll(); + await fn(pool); + } finally { + await pool.flushAll(); + pool.destroy(); + } + }); + } + + static async #clusterFlushAll< + M extends RedisModules, + F extends RedisFunctions, + S extends RedisScripts, + RESP extends RespVersions, + TYPE_MAPPING extends TypeMapping + // POLICIES extends CommandPolicies + >(cluster: RedisClusterType): Promise { + return Promise.all( + cluster.masters.map(async master => { + if (master.client) { + await (await cluster.nodeClient(master)).flushAll(); } + }) + ); + } + + testWithCluster< + M extends RedisModules = {}, + F extends RedisFunctions = {}, + S extends RedisScripts = {}, + RESP extends RespVersions = 2, + TYPE_MAPPING extends TypeMapping = {} + // POLICIES extends CommandPolicies = {} + >( + title: string, + fn: (cluster: RedisClusterType) => unknown, + options: ClusterTestOptions + ): void { + let dockersPromise: ReturnType; + if (this.isVersionGreaterThan(options.minimumDockerVersion)) { + const dockerImage = this.#DOCKER_IMAGE; + before(function () { + this.timeout(30000); + + dockersPromise = spawnRedisCluster({ + ...dockerImage, + numberOfMasters: options.numberOfMasters, + numberOfReplicas: options.numberOfReplicas + }, options.serverArguments); + return dockersPromise; + }); + } - it(title, async function () { - if (!dockersPromise) return this.skip(); - - const dockers = await dockersPromise, - cluster = RedisCluster.create({ - rootNodes: dockers.map(({ port }) => ({ - socket: { - port - } - })), - minimizeConnections: true, - ...options.clusterConfiguration - }); - - await cluster.connect(); - - try { - await TestUtils.#clusterFlushAll(cluster); - await fn(cluster); - } finally { - await TestUtils.#clusterFlushAll(cluster); - await cluster.disconnect(); + it(title, async function () { + if (!dockersPromise) return this.skip(); + + const dockers = await dockersPromise, + cluster = createCluster({ + rootNodes: dockers.map(({ port }) => ({ + socket: { + port } + })), + minimizeConnections: true, + ...options.clusterConfiguration }); - } + + await cluster.connect(); + + try { + await TestUtils.#clusterFlushAll(cluster); + await fn(cluster); + } finally { + await TestUtils.#clusterFlushAll(cluster); + cluster.destroy(); + } + }); + } + + testAll< + M extends RedisModules = {}, + F extends RedisFunctions = {}, + S extends RedisScripts = {}, + RESP extends RespVersions = 2, + TYPE_MAPPING extends TypeMapping = {} + // POLICIES extends CommandPolicies = {} + >( + title: string, + fn: (client: RedisClientType | RedisClusterType) => unknown, + options: AllTestOptions + ) { + this.testWithClient(`client.${title}`, fn, options.client); + this.testWithCluster(`cluster.${title}`, fn, options.cluster); + } } diff --git a/packages/test-utils/package.json b/packages/test-utils/package.json index 2f4e366536e..5e291211b6c 100644 --- a/packages/test-utils/package.json +++ b/packages/test-utils/package.json @@ -1,24 +1,13 @@ { "name": "@redis/test-utils", "private": true, - "main": "./dist/index.js", - "types": "./dist/index.d.ts", - "scripts": { - "build": "tsc" - }, + "main": "./dist/lib/index.js", + "types": "./dist/lib/index.d.ts", "peerDependencies": { - "@redis/client": "^1.0.0" + "@redis/client": "*" }, "devDependencies": { - "@istanbuljs/nyc-config-typescript": "^1.0.2", - "@types/mocha": "^10.0.1", - "@types/node": "^20.6.2", - "@types/yargs": "^17.0.24", - "mocha": "^10.2.0", - "nyc": "^15.1.0", - "source-map-support": "^0.5.21", - "ts-node": "^10.9.1", - "typescript": "^5.2.2", + "@types/yargs": "^17.0.32", "yargs": "^17.7.2" } } diff --git a/packages/test-utils/tsconfig.json b/packages/test-utils/tsconfig.json index 14fda1d8711..6bb104668fc 100644 --- a/packages/test-utils/tsconfig.json +++ b/packages/test-utils/tsconfig.json @@ -5,5 +5,8 @@ }, "include": [ "./lib/**/*.ts" - ] + ], + "references": [{ + "path": "../client" + }] } diff --git a/packages/time-series/.npmignore b/packages/time-series/.npmignore deleted file mode 100644 index bbef2b404fb..00000000000 --- a/packages/time-series/.npmignore +++ /dev/null @@ -1,6 +0,0 @@ -.nyc_output/ -coverage/ -lib/ -.nycrc.json -.release-it.json -tsconfig.json diff --git a/packages/time-series/.release-it.json b/packages/time-series/.release-it.json index b5a7c08d24f..6c59e8955cf 100644 --- a/packages/time-series/.release-it.json +++ b/packages/time-series/.release-it.json @@ -5,6 +5,7 @@ "tagAnnotation": "Release ${tagName}" }, "npm": { + "versionArgs": ["--workspaces-update=false"], "publishArgs": ["--access", "public"] } } diff --git a/packages/time-series/README.md b/packages/time-series/README.md index 5923979cd48..ff42bfb6b3d 100644 --- a/packages/time-series/README.md +++ b/packages/time-series/README.md @@ -1,8 +1,10 @@ # @redis/time-series -This package provides support for the [RedisTimeSeries](https://redistimeseries.io) module, which adds a time series data structure to Redis. It extends the [Node Redis client](https://github.com/redis/node-redis) to include functions for each of the RedisTimeSeries commands. +This package provides support for the [RedisTimeSeries](https://redis.io/docs/data-types/timeseries/) module, which adds a time series data structure to Redis. -To use these extra commands, your Redis server must have the RedisTimeSeries module installed. +Should be used with [`redis`/`@redis/client`](https://github.com/redis/node-redis). + +:warning: To use these extra commands, your Redis server must have the RedisTimeSeries module installed. ## Usage @@ -20,20 +22,18 @@ import { createClient } from 'redis'; import { TimeSeriesDuplicatePolicies, TimeSeriesEncoding, TimeSeriesAggregationType } from '@redis/time-series'; ... - - const created = await client.ts.create('temperature', { - RETENTION: 86400000, // 1 day in milliseconds - ENCODING: TimeSeriesEncoding.UNCOMPRESSED, // No compression - When not specified, the option is set to COMPRESSED - DUPLICATE_POLICY: TimeSeriesDuplicatePolicies.BLOCK, // No duplicates - When not specified: set to the global DUPLICATE_POLICY configuration of the database (which by default, is BLOCK). - }); - - if (created === 'OK') { - console.log('Created timeseries.'); - } else { - console.log('Error creating timeseries :('); - process.exit(1); - } - +const created = await client.ts.create('temperature', { + RETENTION: 86400000, // 1 day in milliseconds + ENCODING: TimeSeriesEncoding.UNCOMPRESSED, // No compression - When not specified, the option is set to COMPRESSED + DUPLICATE_POLICY: TimeSeriesDuplicatePolicies.BLOCK, // No duplicates - When not specified: set to the global DUPLICATE_POLICY configuration of the database (which by default, is BLOCK). +}); + +if (created === 'OK') { + console.log('Created timeseries.'); +} else { + console.log('Error creating timeseries :('); + process.exit(1); +} ``` ### Adding new value to a Time Series data structure in Redis @@ -43,33 +43,31 @@ With RedisTimeSeries, we can add a single value to time series data structure us ```javascript let value = Math.floor(Math.random() * 1000) + 1; // Random data point value - let currentTimestamp = 1640995200000; // Jan 1 2022 00:00:00 - let num = 0; - - while (num < 10000) { - // Add a new value to the timeseries, providing our own timestamp: - // https://redis.io/commands/ts.add/ - await client.ts.add('temperature', currentTimestamp, value); - console.log(`Added timestamp ${currentTimestamp}, value ${value}.`); - - num += 1; - value = Math.floor(Math.random() * 1000) + 1; // Get another random value - currentTimestamp += 1000; // Move on one second. - } - - // Add multiple values to the timeseries in round trip to the server: - // https://redis.io/commands/ts.madd/ - const response = await client.ts.mAdd([{ - key: 'temperature', - timestamp: currentTimestamp + 60000, - value: Math.floor(Math.random() * 1000) + 1 - }, { - key: 'temperature', - timestamp: currentTimestamp + 120000, - value: Math.floor(Math.random() * 1000) + 1 - }]); - - +let currentTimestamp = 1640995200000; // Jan 1 2022 00:00:00 +let num = 0; + +while (num < 10000) { + // Add a new value to the timeseries, providing our own timestamp: + // https://redis.io/commands/ts.add/ + await client.ts.add('temperature', currentTimestamp, value); + console.log(`Added timestamp ${currentTimestamp}, value ${value}.`); + + num += 1; + value = Math.floor(Math.random() * 1000) + 1; // Get another random value + currentTimestamp += 1000; // Move on one second. +} + +// Add multiple values to the timeseries in round trip to the server: +// https://redis.io/commands/ts.madd/ +const response = await client.ts.mAdd([{ + key: 'temperature', + timestamp: currentTimestamp + 60000, + value: Math.floor(Math.random() * 1000) + 1 +}, { + key: 'temperature', + timestamp: currentTimestamp + 120000, + value: Math.floor(Math.random() * 1000) + 1 +}]); ``` ### Retrieving Time Series data from Redis @@ -77,31 +75,29 @@ let value = Math.floor(Math.random() * 1000) + 1; // Random data point value With RedisTimeSeries, we can retrieve the time series data using the [`TS.RANGE`](https://redis.io/commands/ts.range/) command by passing the criteria as follows: ```javascript - // Query the timeseries with TS.RANGE: - // https://redis.io/commands/ts.range/ - const fromTimestamp = 1640995200000; // Jan 1 2022 00:00:00 - const toTimestamp = 1640995260000; // Jan 1 2022 00:01:00 - const rangeResponse = await client.ts.range('temperature', fromTimestamp, toTimestamp, { - // Group into 10 second averages. - AGGREGATION: { - type: TimeSeriesAggregationType.AVERAGE, - timeBucket: 10000 - } - }); - - console.log('RANGE RESPONSE:'); - // rangeResponse looks like: - // [ - // { timestamp: 1640995200000, value: 356.8 }, - // { timestamp: 1640995210000, value: 534.8 }, - // { timestamp: 1640995220000, value: 481.3 }, - // { timestamp: 1640995230000, value: 437 }, - // { timestamp: 1640995240000, value: 507.3 }, - // { timestamp: 1640995250000, value: 581.2 }, - // { timestamp: 1640995260000, value: 600 } - // ] - +// https://redis.io/commands/ts.range/ +const fromTimestamp = 1640995200000; // Jan 1 2022 00:00:00 +const toTimestamp = 1640995260000; // Jan 1 2022 00:01:00 +const rangeResponse = await client.ts.range('temperature', fromTimestamp, toTimestamp, { + // Group into 10 second averages. + AGGREGATION: { + type: TimeSeriesAggregationType.AVERAGE, + timeBucket: 10000 + } +}); + +console.log('RANGE RESPONSE:'); +// rangeResponse looks like: +// [ +// { timestamp: 1640995200000, value: 356.8 }, +// { timestamp: 1640995210000, value: 534.8 }, +// { timestamp: 1640995220000, value: 481.3 }, +// { timestamp: 1640995230000, value: 437 }, +// { timestamp: 1640995240000, value: 507.3 }, +// { timestamp: 1640995250000, value: 581.2 }, +// { timestamp: 1640995260000, value: 600 } +// ] ``` ### Altering Time Series data Stored in Redis @@ -111,12 +107,10 @@ RedisTimeSeries includes commands that can update values in a time series data s Using the [`TS.ALTER`](https://redis.io/commands/ts.alter/) command, we can update time series retention like this: ```javascript - - // https://redis.io/commands/ts.alter/ - const alterResponse = await client.ts.alter('temperature', { - RETENTION: 0 // Keep the entries forever - }); - +// https://redis.io/commands/ts.alter/ +const alterResponse = await client.ts.alter('temperature', { + RETENTION: 0 // Keep the entries forever +}); ``` ### Retrieving Information about the timeseries Stored in Redis @@ -126,26 +120,24 @@ RedisTimeSeries also includes commands that can help to view the information on Using the [`TS.INFO`](https://redis.io/commands/ts.info/) command, we can view timeseries information like this: ```javascript - - // Get some information about the state of the timeseries. - // https://redis.io/commands/ts.info/ - const tsInfo = await client.ts.info('temperature'); - - // tsInfo looks like this: - // { - // totalSamples: 1440, - // memoryUsage: 28904, - // firstTimestamp: 1641508920000, - // lastTimestamp: 1641595320000, - // retentionTime: 86400000, - // chunkCount: 7, - // chunkSize: 4096, - // chunkType: 'uncompressed', - // duplicatePolicy: 'block', - // labels: [], - // sourceKey: null, - // rules: [] - // } - +// Get some information about the state of the timeseries. +// https://redis.io/commands/ts.info/ +const tsInfo = await client.ts.info('temperature'); + +// tsInfo looks like this: +// { +// totalSamples: 1440, +// memoryUsage: 28904, +// firstTimestamp: 1641508920000, +// lastTimestamp: 1641595320000, +// retentionTime: 86400000, +// chunkCount: 7, +// chunkSize: 4096, +// chunkType: 'uncompressed', +// duplicatePolicy: 'block', +// labels: [], +// sourceKey: null, +// rules: [] +// } ``` diff --git a/packages/time-series/lib/commands/ADD.spec.ts b/packages/time-series/lib/commands/ADD.spec.ts index 07e67c1adec..7dcf031c2b2 100644 --- a/packages/time-series/lib/commands/ADD.spec.ts +++ b/packages/time-series/lib/commands/ADD.spec.ts @@ -1,90 +1,93 @@ -import { strict as assert } from 'assert'; +import { strict as assert } from 'node:assert'; import testUtils, { GLOBAL } from '../test-utils'; -import { transformArguments } from './ADD'; -import { TimeSeriesDuplicatePolicies, TimeSeriesEncoding } from '.'; +import ADD from './ADD'; +import { TIME_SERIES_ENCODING, TIME_SERIES_DUPLICATE_POLICIES } from '.'; -describe('ADD', () => { - describe('transformArguments', () => { - it('without options', () => { - assert.deepEqual( - transformArguments('key', '*', 1), - ['TS.ADD', 'key', '*', '1'] - ); - }); +describe('TS.ADD', () => { + describe('transformArguments', () => { + it('without options', () => { + assert.deepEqual( + ADD.transformArguments('key', '*', 1), + ['TS.ADD', 'key', '*', '1'] + ); + }); - it('with RETENTION', () => { - assert.deepEqual( - transformArguments('key', '*', 1, { - RETENTION: 1 - }), - ['TS.ADD', 'key', '*', '1', 'RETENTION', '1'] - ); - }); + it('with RETENTION', () => { + assert.deepEqual( + ADD.transformArguments('key', '*', 1, { + RETENTION: 1 + }), + ['TS.ADD', 'key', '*', '1', 'RETENTION', '1'] + ); + }); - it('with ENCODING', () => { - assert.deepEqual( - transformArguments('key', '*', 1, { - ENCODING: TimeSeriesEncoding.UNCOMPRESSED - }), - ['TS.ADD', 'key', '*', '1', 'ENCODING', 'UNCOMPRESSED'] - ); - }); + it('with ENCODING', () => { + assert.deepEqual( + ADD.transformArguments('key', '*', 1, { + ENCODING: TIME_SERIES_ENCODING.UNCOMPRESSED + }), + ['TS.ADD', 'key', '*', '1', 'ENCODING', 'UNCOMPRESSED'] + ); + }); - it('with CHUNK_SIZE', () => { - assert.deepEqual( - transformArguments('key', '*', 1, { - CHUNK_SIZE: 1 - }), - ['TS.ADD', 'key', '*', '1', 'CHUNK_SIZE', '1'] - ); - }); + it('with CHUNK_SIZE', () => { + assert.deepEqual( + ADD.transformArguments('key', '*', 1, { + CHUNK_SIZE: 1 + }), + ['TS.ADD', 'key', '*', '1', 'CHUNK_SIZE', '1'] + ); + }); - it('with ON_DUPLICATE', () => { - assert.deepEqual( - transformArguments('key', '*', 1, { - ON_DUPLICATE: TimeSeriesDuplicatePolicies.BLOCK - }), - ['TS.ADD', 'key', '*', '1', 'ON_DUPLICATE', 'BLOCK'] - ); - }); + it('with ON_DUPLICATE', () => { + assert.deepEqual( + ADD.transformArguments('key', '*', 1, { + ON_DUPLICATE: TIME_SERIES_DUPLICATE_POLICIES.BLOCK + }), + ['TS.ADD', 'key', '*', '1', 'ON_DUPLICATE', 'BLOCK'] + ); + }); - it('with LABELS', () => { - assert.deepEqual( - transformArguments('key', '*', 1, { - LABELS: { label: 'value' } - }), - ['TS.ADD', 'key', '*', '1', 'LABELS', 'label', 'value'] - ); - }); + it('with LABELS', () => { + assert.deepEqual( + ADD.transformArguments('key', '*', 1, { + LABELS: { label: 'value' } + }), + ['TS.ADD', 'key', '*', '1', 'LABELS', 'label', 'value'] + ); + }); - it('with IGNORE', () => { - assert.deepEqual( - transformArguments('key', '*', 1, { - IGNORE: { MAX_TIME_DIFF: 1, MAX_VAL_DIFF: 1} - }), - ['TS.ADD', 'key', '*', '1', 'IGNORE', '1', '1'] - ) - }); + it ('with IGNORE', () => { + assert.deepEqual( + ADD.transformArguments('key', '*', 1, { + IGNORE: { + maxTimeDiff: 1, + maxValDiff: 1 + } + }), + ['TS.ADD', 'key', '*', '1', 'IGNORE', '1', '1'] + ) + }); - it('with RETENTION, ENCODING, CHUNK_SIZE, ON_DUPLICATE, LABELS, IGNORE', () => { - assert.deepEqual( - transformArguments('key', '*', 1, { - RETENTION: 1, - ENCODING: TimeSeriesEncoding.UNCOMPRESSED, - CHUNK_SIZE: 1, - ON_DUPLICATE: TimeSeriesDuplicatePolicies.BLOCK, - LABELS: { label: 'value' }, - IGNORE: { MAX_TIME_DIFF: 1, MAX_VAL_DIFF: 1} - }), - ['TS.ADD', 'key', '*', '1', 'RETENTION', '1', 'ENCODING', 'UNCOMPRESSED', 'CHUNK_SIZE', '1', 'ON_DUPLICATE', 'BLOCK', 'LABELS', 'label', 'value', 'IGNORE', '1', '1'] - ); - }); + it('with RETENTION, ENCODING, CHUNK_SIZE, ON_DUPLICATE, LABELS, IGNORE', () => { + assert.deepEqual( + ADD.transformArguments('key', '*', 1, { + RETENTION: 1, + ENCODING: TIME_SERIES_ENCODING.UNCOMPRESSED, + CHUNK_SIZE: 1, + ON_DUPLICATE: TIME_SERIES_DUPLICATE_POLICIES.BLOCK, + LABELS: { label: 'value' }, + IGNORE: { maxTimeDiff: 1, maxValDiff: 1} + }), + ['TS.ADD', 'key', '*', '1', 'RETENTION', '1', 'ENCODING', 'UNCOMPRESSED', 'CHUNK_SIZE', '1', 'ON_DUPLICATE', 'BLOCK', 'LABELS', 'label', 'value', 'IGNORE', '1', '1'] + ); }); + }); - testUtils.testWithClient('client.ts.add', async client => { - assert.equal( - await client.ts.add('key', 0, 1), - 0 - ); - }, GLOBAL.SERVERS.OPEN); + testUtils.testWithClient('client.ts.add', async client => { + assert.equal( + await client.ts.add('key', 0, 1), + 0 + ); + }, GLOBAL.SERVERS.OPEN); }); diff --git a/packages/time-series/lib/commands/ADD.ts b/packages/time-series/lib/commands/ADD.ts index 3ed185b9b75..1842dcfc346 100644 --- a/packages/time-series/lib/commands/ADD.ts +++ b/packages/time-series/lib/commands/ADD.ts @@ -1,38 +1,45 @@ +import { RedisArgument, NumberReply, Command } from '@redis/client/dist/lib/RESP/types'; import { - transformTimestampArgument, - pushRetentionArgument, - TimeSeriesEncoding, - pushEncodingArgument, - pushChunkSizeArgument, - TimeSeriesDuplicatePolicies, - Labels, - pushLabelsArgument, - Timestamp, - pushIgnoreArgument, + transformTimestampArgument, + pushRetentionArgument, + TimeSeriesEncoding, + pushEncodingArgument, + pushChunkSizeArgument, + TimeSeriesDuplicatePolicies, + Labels, + pushLabelsArgument, + Timestamp, + pushIgnoreArgument } from '.'; export interface TsIgnoreOptions { - MAX_TIME_DIFF: number; - MAX_VAL_DIFF: number; + maxTimeDiff: number; + maxValDiff: number; } -interface AddOptions { - RETENTION?: number; - ENCODING?: TimeSeriesEncoding; - CHUNK_SIZE?: number; - ON_DUPLICATE?: TimeSeriesDuplicatePolicies; - LABELS?: Labels; - IGNORE?: TsIgnoreOptions; +export interface TsAddOptions { + RETENTION?: number; + ENCODING?: TimeSeriesEncoding; + CHUNK_SIZE?: number; + ON_DUPLICATE?: TimeSeriesDuplicatePolicies; + LABELS?: Labels; + IGNORE?: TsIgnoreOptions; } -export const FIRST_KEY_INDEX = 1; - -export function transformArguments(key: string, timestamp: Timestamp, value: number, options?: AddOptions): Array { +export default { + FIRST_KEY_INDEX: 1, + IS_READ_ONLY: false, + transformArguments( + key: RedisArgument, + timestamp: Timestamp, + value: number, + options?: TsAddOptions + ) { const args = [ - 'TS.ADD', - key, - transformTimestampArgument(timestamp), - value.toString() + 'TS.ADD', + key, + transformTimestampArgument(timestamp), + value.toString() ]; pushRetentionArgument(args, options?.RETENTION); @@ -42,7 +49,7 @@ export function transformArguments(key: string, timestamp: Timestamp, value: num pushChunkSizeArgument(args, options?.CHUNK_SIZE); if (options?.ON_DUPLICATE) { - args.push('ON_DUPLICATE', options.ON_DUPLICATE); + args.push('ON_DUPLICATE', options.ON_DUPLICATE); } pushLabelsArgument(args, options?.LABELS); @@ -50,6 +57,6 @@ export function transformArguments(key: string, timestamp: Timestamp, value: num pushIgnoreArgument(args, options?.IGNORE); return args; -} - -export declare function transformReply(): number; + }, + transformReply: undefined as unknown as () => NumberReply +} as const satisfies Command; diff --git a/packages/time-series/lib/commands/ALTER.spec.ts b/packages/time-series/lib/commands/ALTER.spec.ts index 7add3eeec3a..1b24111156b 100644 --- a/packages/time-series/lib/commands/ALTER.spec.ts +++ b/packages/time-series/lib/commands/ALTER.spec.ts @@ -1,82 +1,85 @@ -import { strict as assert } from 'assert'; -import { TimeSeriesDuplicatePolicies } from '.'; +import { strict as assert } from 'node:assert'; import testUtils, { GLOBAL } from '../test-utils'; -import { transformArguments } from './ALTER'; +import ALTER from './ALTER'; +import { TIME_SERIES_DUPLICATE_POLICIES } from '.'; -describe('ALTER', () => { - describe('transformArguments', () => { - it('without options', () => { - assert.deepEqual( - transformArguments('key'), - ['TS.ALTER', 'key'] - ); - }); +describe('TS.ALTER', () => { + describe('transformArguments', () => { + it('without options', () => { + assert.deepEqual( + ALTER.transformArguments('key'), + ['TS.ALTER', 'key'] + ); + }); - it('with RETENTION', () => { - assert.deepEqual( - transformArguments('key', { - RETENTION: 1 - }), - ['TS.ALTER', 'key', 'RETENTION', '1'] - ); - }); + it('with RETENTION', () => { + assert.deepEqual( + ALTER.transformArguments('key', { + RETENTION: 1 + }), + ['TS.ALTER', 'key', 'RETENTION', '1'] + ); + }); - it('with CHUNK_SIZE', () => { - assert.deepEqual( - transformArguments('key', { - CHUNK_SIZE: 1 - }), - ['TS.ALTER', 'key', 'CHUNK_SIZE', '1'] - ); - }); + it('with CHUNK_SIZE', () => { + assert.deepEqual( + ALTER.transformArguments('key', { + CHUNK_SIZE: 1 + }), + ['TS.ALTER', 'key', 'CHUNK_SIZE', '1'] + ); + }); - it('with DUPLICATE_POLICY', () => { - assert.deepEqual( - transformArguments('key', { - DUPLICATE_POLICY: TimeSeriesDuplicatePolicies.BLOCK - }), - ['TS.ALTER', 'key', 'DUPLICATE_POLICY', 'BLOCK'] - ); - }); + it('with DUPLICATE_POLICY', () => { + assert.deepEqual( + ALTER.transformArguments('key', { + DUPLICATE_POLICY: TIME_SERIES_DUPLICATE_POLICIES.BLOCK + }), + ['TS.ALTER', 'key', 'DUPLICATE_POLICY', 'BLOCK'] + ); + }); - it('with LABELS', () => { - assert.deepEqual( - transformArguments('key', { - LABELS: { label: 'value' } - }), - ['TS.ALTER', 'key', 'LABELS', 'label', 'value'] - ); - }); + it('with LABELS', () => { + assert.deepEqual( + ALTER.transformArguments('key', { + LABELS: { label: 'value' } + }), + ['TS.ALTER', 'key', 'LABELS', 'label', 'value'] + ); + }); - it('with IGNORE with MAX_TIME_DIFF', () => { - assert.deepEqual( - transformArguments('key', { - IGNORE: { MAX_TIME_DIFF: 1, MAX_VAL_DIFF: 1} - }), - ['TS.ALTER', 'key', 'IGNORE', '1', '1'] - ) - }); + it('with IGNORE with MAX_TIME_DIFF', () => { + assert.deepEqual( + ALTER.transformArguments('key', { + IGNORE: { + maxTimeDiff: 1, + maxValDiff: 1 + } + }), + ['TS.ALTER', 'key', 'IGNORE', '1', '1'] + ) + }); - it('with RETENTION, CHUNK_SIZE, DUPLICATE_POLICY, LABELS, IGNORE', () => { - assert.deepEqual( - transformArguments('key', { - RETENTION: 1, - CHUNK_SIZE: 1, - DUPLICATE_POLICY: TimeSeriesDuplicatePolicies.BLOCK, - LABELS: { label: 'value' }, - IGNORE: { MAX_TIME_DIFF: 1, MAX_VAL_DIFF: 1} - }), - ['TS.ALTER', 'key', 'RETENTION', '1', 'CHUNK_SIZE', '1', 'DUPLICATE_POLICY', 'BLOCK', 'LABELS', 'label', 'value', 'IGNORE', '1', '1'] - ); - }); + it('with RETENTION, CHUNK_SIZE, DUPLICATE_POLICY, LABELS, IGNORE', () => { + assert.deepEqual( + ALTER.transformArguments('key', { + RETENTION: 1, + CHUNK_SIZE: 1, + DUPLICATE_POLICY: TIME_SERIES_DUPLICATE_POLICIES.BLOCK, + LABELS: { label: 'value' }, + IGNORE: { maxTimeDiff: 1, maxValDiff: 1} + }), + ['TS.ALTER', 'key', 'RETENTION', '1', 'CHUNK_SIZE', '1', 'DUPLICATE_POLICY', 'BLOCK', 'LABELS', 'label', 'value', 'IGNORE', '1', '1'] + ); }); + }); - testUtils.testWithClient('client.ts.alter', async client => { - await client.ts.create('key'); + testUtils.testWithClient('client.ts.alter', async client => { + const [, reply] = await Promise.all([ + client.ts.create('key'), + client.ts.alter('key') + ]); - assert.equal( - await client.ts.alter('key', { RETENTION: 1 }), - 'OK' - ); - }, GLOBAL.SERVERS.OPEN); + assert.equal(reply, 'OK'); + }, GLOBAL.SERVERS.OPEN); }); diff --git a/packages/time-series/lib/commands/ALTER.ts b/packages/time-series/lib/commands/ALTER.ts index 576153a0cca..f77edb5c43f 100644 --- a/packages/time-series/lib/commands/ALTER.ts +++ b/packages/time-series/lib/commands/ALTER.ts @@ -1,17 +1,13 @@ -import { pushRetentionArgument, Labels, pushLabelsArgument, TimeSeriesDuplicatePolicies, pushChunkSizeArgument, pushDuplicatePolicy, pushIgnoreArgument } from '.'; -import { TsIgnoreOptions } from './ADD'; +import { RedisArgument, SimpleStringReply, Command } from '@redis/client/dist/lib/RESP/types'; +import { TsCreateOptions } from './CREATE'; +import { pushRetentionArgument, pushChunkSizeArgument, pushDuplicatePolicy, pushLabelsArgument, pushIgnoreArgument } from '.'; -export const FIRST_KEY_INDEX = 1; +export type TsAlterOptions = Pick; -interface AlterOptions { - RETENTION?: number; - CHUNK_SIZE?: number; - DUPLICATE_POLICY?: TimeSeriesDuplicatePolicies; - LABELS?: Labels; - IGNORE?: TsIgnoreOptions; -} - -export function transformArguments(key: string, options?: AlterOptions): Array { +export default { + FIRST_KEY_INDEX: 1, + IS_READ_ONLY: false, + transformArguments(key: RedisArgument, options?: TsAlterOptions) { const args = ['TS.ALTER', key]; pushRetentionArgument(args, options?.RETENTION); @@ -25,6 +21,6 @@ export function transformArguments(key: string, options?: AlterOptions): Array SimpleStringReply<'OK'> +} as const satisfies Command; diff --git a/packages/time-series/lib/commands/CREATE.spec.ts b/packages/time-series/lib/commands/CREATE.spec.ts index eb7a1c6a637..feff9cbdd7b 100644 --- a/packages/time-series/lib/commands/CREATE.spec.ts +++ b/packages/time-series/lib/commands/CREATE.spec.ts @@ -1,90 +1,93 @@ -import { strict as assert } from 'assert'; -import { TimeSeriesDuplicatePolicies, TimeSeriesEncoding } from '.'; +import { strict as assert } from 'node:assert'; import testUtils, { GLOBAL } from '../test-utils'; -import { transformArguments } from './CREATE'; +import CREATE from './CREATE'; +import { TIME_SERIES_ENCODING, TIME_SERIES_DUPLICATE_POLICIES } from '.'; -describe('CREATE', () => { - describe('transformArguments', () => { - it('without options', () => { - assert.deepEqual( - transformArguments('key'), - ['TS.CREATE', 'key'] - ); - }); +describe('TS.CREATE', () => { + describe('transformArguments', () => { + it('without options', () => { + assert.deepEqual( + CREATE.transformArguments('key'), + ['TS.CREATE', 'key'] + ); + }); + + it('with RETENTION', () => { + assert.deepEqual( + CREATE.transformArguments('key', { + RETENTION: 1 + }), + ['TS.CREATE', 'key', 'RETENTION', '1'] + ); + }); - it('with RETENTION', () => { - assert.deepEqual( - transformArguments('key', { - RETENTION: 1 - }), - ['TS.CREATE', 'key', 'RETENTION', '1'] - ); - }); + it('with ENCODING', () => { + assert.deepEqual( + CREATE.transformArguments('key', { + ENCODING: TIME_SERIES_ENCODING.UNCOMPRESSED + }), + ['TS.CREATE', 'key', 'ENCODING', 'UNCOMPRESSED'] + ); + }); - it('with ENCODING', () => { - assert.deepEqual( - transformArguments('key', { - ENCODING: TimeSeriesEncoding.UNCOMPRESSED - }), - ['TS.CREATE', 'key', 'ENCODING', 'UNCOMPRESSED'] - ); - }); + it('with CHUNK_SIZE', () => { + assert.deepEqual( + CREATE.transformArguments('key', { + CHUNK_SIZE: 1 + }), + ['TS.CREATE', 'key', 'CHUNK_SIZE', '1'] + ); + }); - it('with CHUNK_SIZE', () => { - assert.deepEqual( - transformArguments('key', { - CHUNK_SIZE: 1 - }), - ['TS.CREATE', 'key', 'CHUNK_SIZE', '1'] - ); - }); + it('with DUPLICATE_POLICY', () => { + assert.deepEqual( + CREATE.transformArguments('key', { + DUPLICATE_POLICY: TIME_SERIES_DUPLICATE_POLICIES.BLOCK + }), + ['TS.CREATE', 'key', 'DUPLICATE_POLICY', 'BLOCK'] + ); + }); - it('with DUPLICATE_POLICY', () => { - assert.deepEqual( - transformArguments('key', { - DUPLICATE_POLICY: TimeSeriesDuplicatePolicies.BLOCK - }), - ['TS.CREATE', 'key', 'DUPLICATE_POLICY', 'BLOCK'] - ); - }); + it('with LABELS', () => { + assert.deepEqual( + CREATE.transformArguments('key', { + LABELS: { label: 'value' } + }), + ['TS.CREATE', 'key', 'LABELS', 'label', 'value'] + ); + }); - it('with LABELS', () => { - assert.deepEqual( - transformArguments('key', { - LABELS: { label: 'value' } - }), - ['TS.CREATE', 'key', 'LABELS', 'label', 'value'] - ); - }); - - it('with IGNORE with MAX_TIME_DIFF', () => { - assert.deepEqual( - transformArguments('key', { - IGNORE: { MAX_TIME_DIFF: 1, MAX_VAL_DIFF: 1} - }), - ['TS.CREATE', 'key', 'IGNORE', '1', '1'] - ) - }); + it('with IGNORE with MAX_TIME_DIFF', () => { + assert.deepEqual( + CREATE.transformArguments('key', { + IGNORE: { + maxTimeDiff: 1, + maxValDiff: 1 + } + }), + ['TS.CREATE', 'key', 'IGNORE', '1', '1'] + ) + }); - it('with RETENTION, ENCODING, CHUNK_SIZE, DUPLICATE_POLICY, LABELS, IGNORE', () => { - assert.deepEqual( - transformArguments('key', { - RETENTION: 1, - ENCODING: TimeSeriesEncoding.UNCOMPRESSED, - CHUNK_SIZE: 1, - DUPLICATE_POLICY: TimeSeriesDuplicatePolicies.BLOCK, - LABELS: { label: 'value' }, - IGNORE: { MAX_TIME_DIFF: 1, MAX_VAL_DIFF: 1} - }), - ['TS.CREATE', 'key', 'RETENTION', '1', 'ENCODING', 'UNCOMPRESSED', 'CHUNK_SIZE', '1', 'DUPLICATE_POLICY', 'BLOCK', 'LABELS', 'label', 'value', 'IGNORE', '1', '1'] - ); - }); + it('with RETENTION, ENCODING, CHUNK_SIZE, DUPLICATE_POLICY, LABELS, IGNORE', () => { + assert.deepEqual( + CREATE.transformArguments('key', { + RETENTION: 1, + ENCODING: TIME_SERIES_ENCODING.UNCOMPRESSED, + CHUNK_SIZE: 1, + DUPLICATE_POLICY: TIME_SERIES_DUPLICATE_POLICIES.BLOCK, + LABELS: { label: 'value' }, + IGNORE: { maxTimeDiff: 1, maxValDiff: 1} + }), + ['TS.CREATE', 'key', 'RETENTION', '1', 'ENCODING', 'UNCOMPRESSED', 'CHUNK_SIZE', '1', 'DUPLICATE_POLICY', 'BLOCK', 'LABELS', 'label', 'value', 'IGNORE', '1', '1'] + ); }); + }); - testUtils.testWithClient('client.ts.create', async client => { - assert.equal( - await client.ts.create('key'), - 'OK' - ); - }, GLOBAL.SERVERS.OPEN); + testUtils.testWithClient('client.ts.create', async client => { + assert.equal( + await client.ts.create('key'), + 'OK' + ); + }, GLOBAL.SERVERS.OPEN); }); diff --git a/packages/time-series/lib/commands/CREATE.ts b/packages/time-series/lib/commands/CREATE.ts index a84d4b5f9fb..abb84de12a2 100644 --- a/packages/time-series/lib/commands/CREATE.ts +++ b/packages/time-series/lib/commands/CREATE.ts @@ -1,28 +1,30 @@ +import { RedisArgument, SimpleStringReply, Command } from '@redis/client/dist/lib/RESP/types'; import { - pushRetentionArgument, - TimeSeriesEncoding, - pushEncodingArgument, - pushChunkSizeArgument, - TimeSeriesDuplicatePolicies, - Labels, - pushLabelsArgument, - pushDuplicatePolicy, - pushIgnoreArgument + pushRetentionArgument, + TimeSeriesEncoding, + pushEncodingArgument, + pushChunkSizeArgument, + TimeSeriesDuplicatePolicies, + pushDuplicatePolicy, + Labels, + pushLabelsArgument, + pushIgnoreArgument } from '.'; import { TsIgnoreOptions } from './ADD'; -export const FIRST_KEY_INDEX = 1; - -interface CreateOptions { - RETENTION?: number; - ENCODING?: TimeSeriesEncoding; - CHUNK_SIZE?: number; - DUPLICATE_POLICY?: TimeSeriesDuplicatePolicies; - LABELS?: Labels; - IGNORE?: TsIgnoreOptions; +export interface TsCreateOptions { + RETENTION?: number; + ENCODING?: TimeSeriesEncoding; + CHUNK_SIZE?: number; + DUPLICATE_POLICY?: TimeSeriesDuplicatePolicies; + LABELS?: Labels; + IGNORE?: TsIgnoreOptions; } -export function transformArguments(key: string, options?: CreateOptions): Array { +export default { + FIRST_KEY_INDEX: 1, + IS_READ_ONLY: false, + transformArguments(key: RedisArgument, options?: TsCreateOptions) { const args = ['TS.CREATE', key]; pushRetentionArgument(args, options?.RETENTION); @@ -38,6 +40,6 @@ export function transformArguments(key: string, options?: CreateOptions): Array< pushIgnoreArgument(args, options?.IGNORE); return args; -} - -export declare function transformReply(): 'OK'; + }, + transformReply: undefined as unknown as () => SimpleStringReply<'OK'> +} as const satisfies Command; diff --git a/packages/time-series/lib/commands/CREATERULE.spec.ts b/packages/time-series/lib/commands/CREATERULE.spec.ts index 65457898181..f1e5b934506 100644 --- a/packages/time-series/lib/commands/CREATERULE.spec.ts +++ b/packages/time-series/lib/commands/CREATERULE.spec.ts @@ -1,34 +1,31 @@ -import { strict as assert } from 'assert'; -import { TimeSeriesAggregationType } from '.'; +import { strict as assert } from 'node:assert'; import testUtils, { GLOBAL } from '../test-utils'; -import { transformArguments } from './CREATERULE'; +import CREATERULE, { TIME_SERIES_AGGREGATION_TYPE } from './CREATERULE'; -describe('CREATERULE', () => { - describe('transformArguments', () => { - it('without options', () => { - assert.deepEqual( - transformArguments('source', 'destination', TimeSeriesAggregationType.AVERAGE, 1), - ['TS.CREATERULE', 'source', 'destination', 'AGGREGATION', 'AVG', '1'] - ); - }); +describe('TS.CREATERULE', () => { + describe('transformArguments', () => { + it('without options', () => { + assert.deepEqual( + CREATERULE.transformArguments('source', 'destination', TIME_SERIES_AGGREGATION_TYPE.AVG, 1), + ['TS.CREATERULE', 'source', 'destination', 'AGGREGATION', 'AVG', '1'] + ); + }); - it('with alignTimestamp', () => { - assert.deepEqual( - transformArguments('source', 'destination', TimeSeriesAggregationType.AVERAGE, 1, 1), - ['TS.CREATERULE', 'source', 'destination', 'AGGREGATION', 'AVG', '1', '1'] - ); - }); + it('with alignTimestamp', () => { + assert.deepEqual( + CREATERULE.transformArguments('source', 'destination', TIME_SERIES_AGGREGATION_TYPE.AVG, 1, 1), + ['TS.CREATERULE', 'source', 'destination', 'AGGREGATION', 'AVG', '1', '1'] + ); }); + }); - testUtils.testWithClient('client.ts.createRule', async client => { - await Promise.all([ - client.ts.create('source'), - client.ts.create('destination') - ]); + testUtils.testWithClient('client.ts.createRule', async client => { + const [, , reply] = await Promise.all([ + client.ts.create('source'), + client.ts.create('destination'), + client.ts.createRule('source', 'destination', TIME_SERIES_AGGREGATION_TYPE.AVG, 1) + ]); - assert.equal( - await client.ts.createRule('source', 'destination', TimeSeriesAggregationType.AVERAGE, 1), - 'OK' - ); - }, GLOBAL.SERVERS.OPEN); + assert.equal(reply, 'OK'); + }, GLOBAL.SERVERS.OPEN); }); diff --git a/packages/time-series/lib/commands/CREATERULE.ts b/packages/time-series/lib/commands/CREATERULE.ts index 87b8579a6ee..bd074d7107c 100644 --- a/packages/time-series/lib/commands/CREATERULE.ts +++ b/packages/time-series/lib/commands/CREATERULE.ts @@ -1,28 +1,47 @@ -import { TimeSeriesAggregationType } from '.'; +import { RedisArgument, SimpleStringReply, Command } from '@redis/client/dist/lib/RESP/types'; -export const FIRST_KEY_INDEX = 1; +export const TIME_SERIES_AGGREGATION_TYPE = { + AVG: 'AVG', + FIRST: 'FIRST', + LAST: 'LAST', + MIN: 'MIN', + MAX: 'MAX', + SUM: 'SUM', + RANGE: 'RANGE', + COUNT: 'COUNT', + STD_P: 'STD.P', + STD_S: 'STD.S', + VAR_P: 'VAR.P', + VAR_S: 'VAR.S', + TWA: 'TWA' +} as const; -export function transformArguments( - sourceKey: string, - destinationKey: string, +export type TimeSeriesAggregationType = typeof TIME_SERIES_AGGREGATION_TYPE[keyof typeof TIME_SERIES_AGGREGATION_TYPE]; + +export default { + FIRST_KEY_INDEX: 1, + IS_READ_ONLY: false, + transformArguments( + sourceKey: RedisArgument, + destinationKey: RedisArgument, aggregationType: TimeSeriesAggregationType, bucketDuration: number, alignTimestamp?: number -): Array { + ) { const args = [ - 'TS.CREATERULE', - sourceKey, - destinationKey, - 'AGGREGATION', - aggregationType, - bucketDuration.toString() + 'TS.CREATERULE', + sourceKey, + destinationKey, + 'AGGREGATION', + aggregationType, + bucketDuration.toString() ]; - if (alignTimestamp) { - args.push(alignTimestamp.toString()); + if (alignTimestamp !== undefined) { + args.push(alignTimestamp.toString()); } return args; -} - -export declare function transformReply(): 'OK'; + }, + transformReply: undefined as unknown as () => SimpleStringReply<'OK'> +} as const satisfies Command; diff --git a/packages/time-series/lib/commands/DECRBY.spec.ts b/packages/time-series/lib/commands/DECRBY.spec.ts index 345e651404b..dbce98b2acd 100644 --- a/packages/time-series/lib/commands/DECRBY.spec.ts +++ b/packages/time-series/lib/commands/DECRBY.spec.ts @@ -1,81 +1,92 @@ -import { strict as assert } from 'assert'; +import { strict as assert } from 'node:assert'; import testUtils, { GLOBAL } from '../test-utils'; -import { transformArguments } from './DECRBY'; +import DECRBY from './DECRBY'; -describe('DECRBY', () => { - describe('transformArguments', () => { - it('without options', () => { - assert.deepEqual( - transformArguments('key', 1), - ['TS.DECRBY', 'key', '1'] - ); - }); +describe('TS.DECRBY', () => { + describe('transformArguments', () => { + it('without options', () => { + assert.deepEqual( + DECRBY.transformArguments('key', 1), + ['TS.DECRBY', 'key', '1'] + ); + }); - it('with TIMESTAMP', () => { - assert.deepEqual( - transformArguments('key', 1, { - TIMESTAMP: '*' - }), - ['TS.DECRBY', 'key', '1', 'TIMESTAMP', '*'] - ); - }); + it('with TIMESTAMP', () => { + assert.deepEqual( + DECRBY.transformArguments('key', 1, { + TIMESTAMP: '*' + }), + ['TS.DECRBY', 'key', '1', 'TIMESTAMP', '*'] + ); + }); - it('with RETENTION', () => { - assert.deepEqual( - transformArguments('key', 1, { - RETENTION: 1 - }), - ['TS.DECRBY', 'key', '1', 'RETENTION', '1'] - ); - }); + it('with RETENTION', () => { + assert.deepEqual( + DECRBY.transformArguments('key', 1, { + RETENTION: 1 + }), + ['TS.DECRBY', 'key', '1', 'RETENTION', '1'] + ); + }); - it('with UNCOMPRESSED', () => { - assert.deepEqual( - transformArguments('key', 1, { - UNCOMPRESSED: true - }), - ['TS.DECRBY', 'key', '1', 'UNCOMPRESSED'] - ); - }); + it('with UNCOMPRESSED', () => { + assert.deepEqual( + DECRBY.transformArguments('key', 1, { + UNCOMPRESSED: true + }), + ['TS.DECRBY', 'key', '1', 'UNCOMPRESSED'] + ); + }); - it('with CHUNK_SIZE', () => { - assert.deepEqual( - transformArguments('key', 1, { - CHUNK_SIZE: 100 - }), - ['TS.DECRBY', 'key', '1', 'CHUNK_SIZE', '100'] - ); - }); + it('with CHUNK_SIZE', () => { + assert.deepEqual( + DECRBY.transformArguments('key', 1, { + CHUNK_SIZE: 100 + }), + ['TS.DECRBY', 'key', '1', 'CHUNK_SIZE', '100'] + ); + }); - it('with LABELS', () => { - assert.deepEqual( - transformArguments('key', 1, { - LABELS: { label: 'value' } - }), - ['TS.DECRBY', 'key', '1', 'LABELS', 'label', 'value'] - ); - }); + it('with LABELS', () => { + assert.deepEqual( + DECRBY.transformArguments('key', 1, { + LABELS: { label: 'value' } + }), + ['TS.DECRBY', 'key', '1', 'LABELS', 'label', 'value'] + ); + }); - it('with TIMESTAMP, RETENTION, UNCOMPRESSED, CHUNK_SIZE and LABELS', () => { - assert.deepEqual( - transformArguments('key', 1, { - TIMESTAMP: '*', - RETENTION: 1, - UNCOMPRESSED: true, - CHUNK_SIZE: 2, - LABELS: { label: 'value' } - }), - ['TS.DECRBY', 'key', '1', 'TIMESTAMP', '*', 'RETENTION', '1', 'UNCOMPRESSED', 'CHUNK_SIZE', '2', 'LABELS', 'label', 'value'] - ); - }); + it ('with IGNORE', () => { + assert.deepEqual( + DECRBY.transformArguments('key', 1, { + IGNORE: { + maxTimeDiff: 1, + maxValDiff: 1 + } + }), + ['TS.DECRBY', 'key', '1', 'IGNORE', '1', '1'] + ) + }); + + it('with TIMESTAMP, RETENTION, UNCOMPRESSED, CHUNK_SIZE and LABELS', () => { + assert.deepEqual( + DECRBY.transformArguments('key', 1, { + TIMESTAMP: '*', + RETENTION: 1, + UNCOMPRESSED: true, + CHUNK_SIZE: 2, + LABELS: { label: 'value' }, + IGNORE: { maxTimeDiff: 1, maxValDiff: 1 } + }), + ['TS.DECRBY', 'key', '1', 'TIMESTAMP', '*', 'RETENTION', '1', 'UNCOMPRESSED', 'CHUNK_SIZE', '2', 'LABELS', 'label', 'value', 'IGNORE', '1', '1'] + ); }); + }); - testUtils.testWithClient('client.ts.decrBy', async client => { - assert.equal( - await client.ts.decrBy('key', 1, { - TIMESTAMP: 0 - }), - 0 - ); - }, GLOBAL.SERVERS.OPEN); + testUtils.testWithClient('client.ts.decrBy', async client => { + assert.equal( + typeof await client.ts.decrBy('key', 1), + 'number' + ); + }, GLOBAL.SERVERS.OPEN); }); diff --git a/packages/time-series/lib/commands/DECRBY.ts b/packages/time-series/lib/commands/DECRBY.ts index 07b5b6f45c0..a5ee01efb06 100644 --- a/packages/time-series/lib/commands/DECRBY.ts +++ b/packages/time-series/lib/commands/DECRBY.ts @@ -1,10 +1,9 @@ -import { RedisCommandArguments } from '@redis/client/dist/lib/commands'; -import { IncrDecrOptions, transformIncrDecrArguments } from '.'; +import { Command } from '@redis/client/dist/lib/RESP/types'; +import INCRBY, { transformIncrByArguments } from './INCRBY'; -export const FIRST_KEY_INDEX = 1; - -export function transformArguments(key: string, value: number, options?: IncrDecrOptions): RedisCommandArguments { - return transformIncrDecrArguments('TS.DECRBY', key, value, options); -} - -export declare function transformReply(): number; +export default { + FIRST_KEY_INDEX: INCRBY.FIRST_KEY_INDEX, + IS_READ_ONLY: INCRBY.IS_READ_ONLY, + transformArguments: transformIncrByArguments.bind(undefined, 'TS.DECRBY'), + transformReply: INCRBY.transformReply +} as const satisfies Command; diff --git a/packages/time-series/lib/commands/DEL.spec.ts b/packages/time-series/lib/commands/DEL.spec.ts index 0fc4b465807..afe6be77c4b 100644 --- a/packages/time-series/lib/commands/DEL.spec.ts +++ b/packages/time-series/lib/commands/DEL.spec.ts @@ -1,21 +1,21 @@ -import { strict as assert } from 'assert'; +import { strict as assert } from 'node:assert'; import testUtils, { GLOBAL } from '../test-utils'; -import { transformArguments } from './DEL'; +import DEL from './DEL'; -describe('DEL', () => { - it('transformArguments', () => { - assert.deepEqual( - transformArguments('key', '-', '+'), - ['TS.DEL', 'key', '-', '+'] - ); - }); +describe('TS.DEL', () => { + it('transformArguments', () => { + assert.deepEqual( + DEL.transformArguments('key', '-', '+'), + ['TS.DEL', 'key', '-', '+'] + ); + }); - testUtils.testWithClient('client.ts.del', async client => { - await client.ts.create('key'); + testUtils.testWithClient('client.ts.del', async client => { + const [, reply] = await Promise.all([ + client.ts.create('key'), + client.ts.del('key', '-', '+') + ]); - assert.equal( - await client.ts.del('key', '-', '+'), - 0 - ); - }, GLOBAL.SERVERS.OPEN); + assert.equal(reply, 0); + }, GLOBAL.SERVERS.OPEN); }); diff --git a/packages/time-series/lib/commands/DEL.ts b/packages/time-series/lib/commands/DEL.ts index 347954c21de..26c3e610f17 100644 --- a/packages/time-series/lib/commands/DEL.ts +++ b/packages/time-series/lib/commands/DEL.ts @@ -1,15 +1,16 @@ -import { RedisCommandArguments } from '@redis/client/dist/lib/commands'; import { Timestamp, transformTimestampArgument } from '.'; +import { RedisArgument, NumberReply, Command, } from '@redis/client/dist/lib/RESP/types'; -export const FIRTS_KEY_INDEX = 1; - -export function transformArguments(key: string, fromTimestamp: Timestamp, toTimestamp: Timestamp): RedisCommandArguments { +export default { + FIRST_KEY_INDEX: 1, + IS_READ_ONLY: false, + transformArguments(key: RedisArgument, fromTimestamp: Timestamp, toTimestamp: Timestamp) { return [ - 'TS.DEL', - key, - transformTimestampArgument(fromTimestamp), - transformTimestampArgument(toTimestamp) + 'TS.DEL', + key, + transformTimestampArgument(fromTimestamp), + transformTimestampArgument(toTimestamp) ]; -} - -export declare function transformReply(): number; + }, + transformReply: undefined as unknown as () => NumberReply +} as const satisfies Command; diff --git a/packages/time-series/lib/commands/DELETERULE.spec.ts b/packages/time-series/lib/commands/DELETERULE.spec.ts index 9364bea711c..8c8568c8567 100644 --- a/packages/time-series/lib/commands/DELETERULE.spec.ts +++ b/packages/time-series/lib/commands/DELETERULE.spec.ts @@ -1,26 +1,24 @@ -import { strict as assert } from 'assert'; -import { TimeSeriesAggregationType } from '.'; +import { strict as assert } from 'node:assert'; import testUtils, { GLOBAL } from '../test-utils'; -import { transformArguments } from './DELETERULE'; +import DELETERULE from './DELETERULE'; +import { TIME_SERIES_AGGREGATION_TYPE } from './CREATERULE'; -describe('DELETERULE', () => { - it('transformArguments', () => { - assert.deepEqual( - transformArguments('source', 'destination'), - ['TS.DELETERULE', 'source', 'destination'] - ); - }); +describe('TS.DELETERULE', () => { + it('transformArguments', () => { + assert.deepEqual( + DELETERULE.transformArguments('source', 'destination'), + ['TS.DELETERULE', 'source', 'destination'] + ); + }); - testUtils.testWithClient('client.ts.deleteRule', async client => { - await Promise.all([ - client.ts.create('source'), - client.ts.create('destination'), - client.ts.createRule('source', 'destination', TimeSeriesAggregationType.AVERAGE, 1) - ]); + testUtils.testWithClient('client.ts.deleteRule', async client => { + const [, , , reply] = await Promise.all([ + client.ts.create('source'), + client.ts.create('destination'), + client.ts.createRule('source', 'destination', TIME_SERIES_AGGREGATION_TYPE.AVG, 1), + client.ts.deleteRule('source', 'destination') + ]); - assert.equal( - await client.ts.deleteRule('source', 'destination'), - 'OK' - ); - }, GLOBAL.SERVERS.OPEN); + assert.equal(reply, 'OK'); + }, GLOBAL.SERVERS.OPEN); }); diff --git a/packages/time-series/lib/commands/DELETERULE.ts b/packages/time-series/lib/commands/DELETERULE.ts index 7d2cfaeed94..5cf88897f7d 100644 --- a/packages/time-series/lib/commands/DELETERULE.ts +++ b/packages/time-series/lib/commands/DELETERULE.ts @@ -1,11 +1,14 @@ -export const FIRST_KEY_INDEX = 1; +import { RedisArgument, SimpleStringReply, Command } from '@redis/client/dist/lib/RESP/types'; -export function transformArguments(sourceKey: string, destinationKey: string): Array { +export default { + FIRST_KEY_INDEX: 1, + IS_READ_ONLY: false, + transformArguments(sourceKey: RedisArgument, destinationKey: RedisArgument) { return [ - 'TS.DELETERULE', - sourceKey, - destinationKey + 'TS.DELETERULE', + sourceKey, + destinationKey ]; -} - -export declare function transformReply(): 'OK'; + }, + transformReply: undefined as unknown as () => SimpleStringReply<'OK'> +} as const satisfies Command; diff --git a/packages/time-series/lib/commands/GET.spec.ts b/packages/time-series/lib/commands/GET.spec.ts index 29634cd775a..a1f47346bc2 100644 --- a/packages/time-series/lib/commands/GET.spec.ts +++ b/packages/time-series/lib/commands/GET.spec.ts @@ -1,46 +1,46 @@ -import { strict as assert } from 'assert'; +import { strict as assert } from 'node:assert'; import testUtils, { GLOBAL } from '../test-utils'; -import { transformArguments } from './GET'; +import GET from './GET'; -describe('GET', () => { - describe('transformArguments', () => { - it('without options', () => { - assert.deepEqual( - transformArguments('key'), - ['TS.GET', 'key'] - ); - }); +describe('TS.GET', () => { + describe('transformArguments', () => { + it('without options', () => { + assert.deepEqual( + GET.transformArguments('key'), + ['TS.GET', 'key'] + ); + }); - it('with LATEST', () => { - assert.deepEqual( - transformArguments('key', { - LATEST: true - }), - ['TS.GET', 'key', 'LATEST'] - ); - }); + it('with LATEST', () => { + assert.deepEqual( + GET.transformArguments('key', { + LATEST: true + }), + ['TS.GET', 'key', 'LATEST'] + ); }); + }); - describe('client.ts.get', () => { - testUtils.testWithClient('null', async client => { - await client.ts.create('key'); + describe('client.ts.get', () => { + testUtils.testWithClient('null', async client => { + const [, reply] = await Promise.all([ + client.ts.create('key'), + client.ts.get('key') + ]); - assert.equal( - await client.ts.get('key'), - null - ); - }, GLOBAL.SERVERS.OPEN); + assert.equal(reply, null); + }, GLOBAL.SERVERS.OPEN); - testUtils.testWithClient('with samples', async client => { - await client.ts.add('key', 0, 1); + testUtils.testWithClient('with sample', async client => { + const [, reply] = await Promise.all([ + client.ts.add('key', 0, 1), + client.ts.get('key') + ]); - assert.deepEqual( - await client.ts.get('key'), - { - timestamp: 0, - value: 1 - } - ); - }, GLOBAL.SERVERS.OPEN); - }); + assert.deepEqual(reply, { + timestamp: 0, + value: 1 + }); + }, GLOBAL.SERVERS.OPEN); + }); }); diff --git a/packages/time-series/lib/commands/GET.ts b/packages/time-series/lib/commands/GET.ts index 6d74f97c9cd..78e5e3bced0 100644 --- a/packages/time-series/lib/commands/GET.ts +++ b/packages/time-series/lib/commands/GET.ts @@ -1,20 +1,35 @@ -import { RedisCommandArguments } from '@redis/client/dist/lib/commands'; -import { pushLatestArgument, SampleRawReply, SampleReply, transformSampleReply } from '.'; +import { RedisArgument, TuplesReply, NumberReply, DoubleReply, UnwrapReply, Resp2Reply, Command } from '@redis/client/dist/lib/RESP/types'; -export const FIRST_KEY_INDEX = 1; - -export const IS_READ_ONLY = true; - -interface GetOptions { - LATEST?: boolean; +export interface TsGetOptions { + LATEST?: boolean; } -export function transformArguments(key: string, options?: GetOptions): RedisCommandArguments { - return pushLatestArgument(['TS.GET', key], options?.LATEST); -} +export type TsGetReply = TuplesReply<[]> | TuplesReply<[NumberReply, DoubleReply]>; -export function transformReply(reply: [] | SampleRawReply): null | SampleReply { - if (reply.length === 0) return null; +export default { + FIRST_KEY_INDEX: 1, + IS_READ_ONLY: true, + transformArguments(key: RedisArgument, options?: TsGetOptions) { + const args = ['TS.GET', key]; + + if (options?.LATEST) { + args.push('LATEST'); + } - return transformSampleReply(reply); -} + return args; + }, + transformReply: { + 2(reply: UnwrapReply>) { + return reply.length === 0 ? null : { + timestamp: reply[0], + value: Number(reply[1]) + }; + }, + 3(reply: UnwrapReply) { + return reply.length === 0 ? null : { + timestamp: reply[0], + value: reply[1] + }; + } + } +} as const satisfies Command; diff --git a/packages/time-series/lib/commands/INCRBY.spec.ts b/packages/time-series/lib/commands/INCRBY.spec.ts index acaa4cd3329..33163a72c82 100644 --- a/packages/time-series/lib/commands/INCRBY.spec.ts +++ b/packages/time-series/lib/commands/INCRBY.spec.ts @@ -1,91 +1,102 @@ -import { strict as assert } from 'assert'; +import { strict as assert } from 'node:assert'; import testUtils, { GLOBAL } from '../test-utils'; -import { transformArguments } from './INCRBY'; +import INCRBY from './INCRBY'; -describe('INCRBY', () => { - describe('transformArguments', () => { - it('without options', () => { - assert.deepEqual( - transformArguments('key', 1), - ['TS.INCRBY', 'key', '1'] - ); - }); +describe('TS.INCRBY', () => { + describe('transformArguments', () => { + it('without options', () => { + assert.deepEqual( + INCRBY.transformArguments('key', 1), + ['TS.INCRBY', 'key', '1'] + ); + }); - it('with TIMESTAMP', () => { - assert.deepEqual( - transformArguments('key', 1, { - TIMESTAMP: '*' - }), - ['TS.INCRBY', 'key', '1', 'TIMESTAMP', '*'] - ); - }); + it('with TIMESTAMP', () => { + assert.deepEqual( + INCRBY.transformArguments('key', 1, { + TIMESTAMP: '*' + }), + ['TS.INCRBY', 'key', '1', 'TIMESTAMP', '*'] + ); + }); - it('with RETENTION', () => { - assert.deepEqual( - transformArguments('key', 1, { - RETENTION: 1 - }), - ['TS.INCRBY', 'key', '1', 'RETENTION', '1'] - ); - }); + it('with RETENTION', () => { + assert.deepEqual( + INCRBY.transformArguments('key', 1, { + RETENTION: 1 + }), + ['TS.INCRBY', 'key', '1', 'RETENTION', '1'] + ); + }); - it('with UNCOMPRESSED', () => { - assert.deepEqual( - transformArguments('key', 1, { - UNCOMPRESSED: true - }), - ['TS.INCRBY', 'key', '1', 'UNCOMPRESSED'] - ); - }); + it('with UNCOMPRESSED', () => { + assert.deepEqual( + INCRBY.transformArguments('key', 1, { + UNCOMPRESSED: true + }), + ['TS.INCRBY', 'key', '1', 'UNCOMPRESSED'] + ); + }); - it('without UNCOMPRESSED', () => { - assert.deepEqual( - transformArguments('key', 1, { - UNCOMPRESSED: false - }), - ['TS.INCRBY', 'key', '1'] - ); - }); + it('without UNCOMPRESSED', () => { + assert.deepEqual( + INCRBY.transformArguments('key', 1, { + UNCOMPRESSED: false + }), + ['TS.INCRBY', 'key', '1'] + ); + }); - it('with CHUNK_SIZE', () => { - assert.deepEqual( - transformArguments('key', 1, { - CHUNK_SIZE: 1 - }), - ['TS.INCRBY', 'key', '1', 'CHUNK_SIZE', '1'] - ); - }); + it('with CHUNK_SIZE', () => { + assert.deepEqual( + INCRBY.transformArguments('key', 1, { + CHUNK_SIZE: 1 + }), + ['TS.INCRBY', 'key', '1', 'CHUNK_SIZE', '1'] + ); + }); - it('with LABELS', () => { - assert.deepEqual( - transformArguments('key', 1, { - LABELS: { label: 'value' } - }), - ['TS.INCRBY', 'key', '1', 'LABELS', 'label', 'value'] - ); - }); + it('with LABELS', () => { + assert.deepEqual( + INCRBY.transformArguments('key', 1, { + LABELS: { label: 'value' } + }), + ['TS.INCRBY', 'key', '1', 'LABELS', 'label', 'value'] + ); + }); - it('with TIMESTAMP, RETENTION, UNCOMPRESSED, CHUNK_SIZE and LABELS', () => { - assert.deepEqual( - transformArguments('key', 1, { - TIMESTAMP: '*', - RETENTION: 1, - UNCOMPRESSED: true, - CHUNK_SIZE: 1, - LABELS: { label: 'value' } - }), - ['TS.INCRBY', 'key', '1', 'TIMESTAMP', '*', 'RETENTION', '1', 'UNCOMPRESSED', - 'CHUNK_SIZE', '1', 'LABELS', 'label', 'value'] - ); - }); + it ('with IGNORE', () => { + assert.deepEqual( + INCRBY.transformArguments('key', 1, { + IGNORE: { + maxTimeDiff: 1, + maxValDiff: 1 + } + }), + ['TS.INCRBY', 'key', '1', 'IGNORE', '1', '1'] + ) + }); + + it('with TIMESTAMP, RETENTION, UNCOMPRESSED, CHUNK_SIZE and LABELS', () => { + assert.deepEqual( + INCRBY.transformArguments('key', 1, { + TIMESTAMP: '*', + RETENTION: 1, + UNCOMPRESSED: true, + CHUNK_SIZE: 1, + LABELS: { label: 'value' }, + IGNORE: { maxTimeDiff: 1, maxValDiff: 1 } + }), + ['TS.INCRBY', 'key', '1', 'TIMESTAMP', '*', 'RETENTION', '1', 'UNCOMPRESSED', + 'CHUNK_SIZE', '1', 'LABELS', 'label', 'value', 'IGNORE', '1', '1'] + ); }); + }); - testUtils.testWithClient('client.ts.incrBy', async client => { - assert.equal( - await client.ts.incrBy('key', 1, { - TIMESTAMP: 0 - }), - 0 - ); - }, GLOBAL.SERVERS.OPEN); + testUtils.testWithClient('client.ts.incrBy', async client => { + assert.equal( + typeof await client.ts.incrBy('key', 1), + 'number' + ); + }, GLOBAL.SERVERS.OPEN); }); diff --git a/packages/time-series/lib/commands/INCRBY.ts b/packages/time-series/lib/commands/INCRBY.ts index 1f96801305f..3160d3906d3 100644 --- a/packages/time-series/lib/commands/INCRBY.ts +++ b/packages/time-series/lib/commands/INCRBY.ts @@ -1,10 +1,50 @@ -import { RedisCommandArguments } from '@redis/client/dist/lib/commands'; -import { IncrDecrOptions, transformIncrDecrArguments } from '.'; +import { RedisArgument, NumberReply, Command } from '@redis/client/dist/lib/RESP/types'; +import { Timestamp, transformTimestampArgument, pushRetentionArgument, pushChunkSizeArgument, Labels, pushLabelsArgument, pushIgnoreArgument } from '.'; +import { TsIgnoreOptions } from './ADD'; -export const FIRST_KEY_INDEX = 1; +export interface TsIncrByOptions { + TIMESTAMP?: Timestamp; + RETENTION?: number; + UNCOMPRESSED?: boolean; + CHUNK_SIZE?: number; + LABELS?: Labels; + IGNORE?: TsIgnoreOptions; +} + +export function transformIncrByArguments( + command: RedisArgument, + key: RedisArgument, + value: number, + options?: TsIncrByOptions +) { + const args = [ + command, + key, + value.toString() + ]; + + if (options?.TIMESTAMP !== undefined && options?.TIMESTAMP !== null) { + args.push('TIMESTAMP', transformTimestampArgument(options.TIMESTAMP)); + } + + pushRetentionArgument(args, options?.RETENTION); + + if (options?.UNCOMPRESSED) { + args.push('UNCOMPRESSED'); + } + + pushChunkSizeArgument(args, options?.CHUNK_SIZE); + + pushLabelsArgument(args, options?.LABELS); + + pushIgnoreArgument(args, options?.IGNORE); -export function transformArguments(key: string, value: number, options?: IncrDecrOptions): RedisCommandArguments { - return transformIncrDecrArguments('TS.INCRBY', key, value, options); + return args; } -export declare function transformReply(): number; +export default { + FIRST_KEY_INDEX: 1, + IS_READ_ONLY: false, + transformArguments: transformIncrByArguments.bind(undefined, 'TS.INCRBY'), + transformReply: undefined as unknown as () => NumberReply +} as const satisfies Command; diff --git a/packages/time-series/lib/commands/INFO.spec.ts b/packages/time-series/lib/commands/INFO.spec.ts index c02cdd6da4d..e4295b80fa4 100644 --- a/packages/time-series/lib/commands/INFO.spec.ts +++ b/packages/time-series/lib/commands/INFO.spec.ts @@ -1,12 +1,13 @@ -import { strict as assert } from 'assert'; -import { TimeSeriesAggregationType, TimeSeriesDuplicatePolicies } from '.'; +import { strict as assert } from 'node:assert'; +import { TIME_SERIES_DUPLICATE_POLICIES } from '.'; import testUtils, { GLOBAL } from '../test-utils'; -import { InfoReply, transformArguments } from './INFO'; +import INFO, { InfoReply } from './INFO'; +import { TIME_SERIES_AGGREGATION_TYPE } from './CREATERULE'; -describe('INFO', () => { +describe('TS.INFO', () => { it('transformArguments', () => { assert.deepEqual( - transformArguments('key'), + INFO.transformArguments('key'), ['TS.INFO', 'key'] ); }); @@ -15,14 +16,14 @@ describe('INFO', () => { await Promise.all([ client.ts.create('key', { LABELS: { id: '1' }, - DUPLICATE_POLICY: TimeSeriesDuplicatePolicies.LAST + DUPLICATE_POLICY: TIME_SERIES_DUPLICATE_POLICIES.LAST }), client.ts.create('key2'), - client.ts.createRule('key', 'key2', TimeSeriesAggregationType.COUNT, 5), + client.ts.createRule('key', 'key2', TIME_SERIES_AGGREGATION_TYPE.COUNT, 5), client.ts.add('key', 1, 10) ]); - assertInfo(await client.ts.info('key')); + assertInfo(await client.ts.info('key') as any); }, GLOBAL.SERVERS.OPEN); }); diff --git a/packages/time-series/lib/commands/INFO.ts b/packages/time-series/lib/commands/INFO.ts index 25ce3ef54ea..91d4e4bbad3 100644 --- a/packages/time-series/lib/commands/INFO.ts +++ b/packages/time-series/lib/commands/INFO.ts @@ -1,82 +1,128 @@ -import { TimeSeriesAggregationType, TimeSeriesDuplicatePolicies } from '.'; +import { ArrayReply, BlobStringReply, Command, DoubleReply, NumberReply, ReplyUnion, SimpleStringReply, TypeMapping } from "@redis/client/lib/RESP/types"; +import { TimeSeriesDuplicatePolicies } from "."; +import { TimeSeriesAggregationType } from "./CREATERULE"; +import { transformDoubleReply } from '@redis/client/lib/commands/generic-transformers'; -export const FIRST_KEY_INDEX = 1; +export type InfoRawReplyTypes = SimpleStringReply | + NumberReply | + TimeSeriesDuplicatePolicies | null | + Array<[name: BlobStringReply, value: BlobStringReply]> | + BlobStringReply | + Array<[key: BlobStringReply, timeBucket: NumberReply, aggregationType: TimeSeriesAggregationType]> | + DoubleReply -export const IS_READ_ONLY = true; +export type InfoRawReply = Array; -export function transformArguments(key: string): Array { - return ['TS.INFO', key]; -} - -export type InfoRawReply = [ - 'totalSamples', - number, - 'memoryUsage', - number, - 'firstTimestamp', - number, - 'lastTimestamp', - number, - 'retentionTime', - number, - 'chunkCount', - number, - 'chunkSize', - number, - 'chunkType', - string, - 'duplicatePolicy', - TimeSeriesDuplicatePolicies | null, - 'labels', - Array<[name: string, value: string]>, - 'sourceKey', - string | null, - 'rules', - Array<[key: string, timeBucket: number, aggregationType: TimeSeriesAggregationType]> +export type InfoRawReplyOld = [ + 'totalSamples', + NumberReply, + 'memoryUsage', + NumberReply, + 'firstTimestamp', + NumberReply, + 'lastTimestamp', + NumberReply, + 'retentionTime', + NumberReply, + 'chunkCount', + NumberReply, + 'chunkSize', + NumberReply, + 'chunkType', + SimpleStringReply, + 'duplicatePolicy', + TimeSeriesDuplicatePolicies | null, + 'labels', + ArrayReply<[name: BlobStringReply, value: BlobStringReply]>, + 'sourceKey', + BlobStringReply | null, + 'rules', + ArrayReply<[key: BlobStringReply, timeBucket: NumberReply, aggregationType: TimeSeriesAggregationType]>, + 'ignoreMaxTimeDiff', + NumberReply, + 'ignoreMaxValDiff', + DoubleReply, ]; export interface InfoReply { - totalSamples: number; - memoryUsage: number; - firstTimestamp: number; - lastTimestamp: number; - retentionTime: number; - chunkCount: number; - chunkSize: number; - chunkType: string; - duplicatePolicy: TimeSeriesDuplicatePolicies | null; - labels: Array<{ - name: string; - value: string; - }>; - sourceKey: string | null; - rules: Array<{ - key: string; - timeBucket: number; - aggregationType: TimeSeriesAggregationType - }>; + totalSamples: NumberReply; + memoryUsage: NumberReply; + firstTimestamp: NumberReply; + lastTimestamp: NumberReply; + retentionTime: NumberReply; + chunkCount: NumberReply; + chunkSize: NumberReply; + chunkType: SimpleStringReply; + duplicatePolicy: TimeSeriesDuplicatePolicies | null; + labels: Array<{ + name: BlobStringReply; + value: BlobStringReply; + }>; + sourceKey: BlobStringReply | null; + rules: Array<{ + key: BlobStringReply; + timeBucket: NumberReply; + aggregationType: TimeSeriesAggregationType + }>; + /** Added in 7.4 */ + ignoreMaxTimeDiff: NumberReply; + /** Added in 7.4 */ + ignoreMaxValDiff: DoubleReply; } -export function transformReply(reply: InfoRawReply): InfoReply { - return { - totalSamples: reply[1], - memoryUsage: reply[3], - firstTimestamp: reply[5], - lastTimestamp: reply[7], - retentionTime: reply[9], - chunkCount: reply[11], - chunkSize: reply[13], - chunkType: reply[15], - duplicatePolicy: reply[17], - labels: reply[19].map(([name, value]) => ({ - name, - value - })), - sourceKey: reply[21], - rules: reply[23].map(([key, timeBucket, aggregationType]) => ({ - key, - timeBucket, - aggregationType - })) - }; -} +export default { + FIRST_KEY_INDEX: 1, + IS_READ_ONLY: true, + transformArguments(key: string) { + return ['TS.INFO', key]; + }, + transformReply: { + 2: (reply: InfoRawReply, _, typeMapping?: TypeMapping): InfoReply => { + const ret = {} as any; + + for (let i=0; i < reply.length; i += 2) { + const key = (reply[i] as any).toString(); + + switch (key) { + case 'totalSamples': + case 'memoryUsage': + case 'firstTimestamp': + case 'lastTimestamp': + case 'retentionTime': + case 'chunkCount': + case 'chunkSize': + case 'chunkType': + case 'duplicatePolicy': + case 'sourceKey': + case 'ignoreMaxTimeDiff': + ret[key] = reply[i+1]; + break; + case 'labels': + ret[key] = (reply[i+1] as Array<[name: BlobStringReply, value: BlobStringReply]>).map( + ([name, value]) => ({ + name, + value + }) + ); + break; + case 'rules': + ret[key] = (reply[i+1] as Array<[key: BlobStringReply, timeBucket: NumberReply, aggregationType: TimeSeriesAggregationType]>).map( + ([key, timeBucket, aggregationType]) => ({ + key, + timeBucket, + aggregationType + }) + ); + break; + case 'ignoreMaxValDiff': + ret[key] = transformDoubleReply[2](reply[27] as unknown as BlobStringReply, undefined, typeMapping); + break; + } + } + + return ret; + }, + 3: undefined as unknown as () => ReplyUnion + }, + unstableResp3: true + } as const satisfies Command; \ No newline at end of file diff --git a/packages/time-series/lib/commands/INFO_DEBUG.spec.ts b/packages/time-series/lib/commands/INFO_DEBUG.spec.ts index 666689f5194..674f91c60a7 100644 --- a/packages/time-series/lib/commands/INFO_DEBUG.spec.ts +++ b/packages/time-series/lib/commands/INFO_DEBUG.spec.ts @@ -1,30 +1,31 @@ -import { strict as assert } from 'assert'; -import { TimeSeriesAggregationType, TimeSeriesDuplicatePolicies } from '.'; +import { strict as assert } from 'node:assert'; +import { TIME_SERIES_DUPLICATE_POLICIES } from '.'; import testUtils, { GLOBAL } from '../test-utils'; import { assertInfo } from './INFO.spec'; -import { transformArguments } from './INFO_DEBUG'; +import INFO_DEBUG from './INFO_DEBUG'; +import { TIME_SERIES_AGGREGATION_TYPE } from './CREATERULE'; -describe('INFO_DEBUG', () => { +describe('TS.INFO_DEBUG', () => { it('transformArguments', () => { assert.deepEqual( - transformArguments('key'), + INFO_DEBUG.transformArguments('key'), ['TS.INFO', 'key', 'DEBUG'] ); }); - testUtils.testWithClient('client.ts.get', async client => { + testUtils.testWithClient('client.ts.infoDebug', async client => { await Promise.all([ client.ts.create('key', { LABELS: { id: '1' }, - DUPLICATE_POLICY: TimeSeriesDuplicatePolicies.LAST + DUPLICATE_POLICY: TIME_SERIES_DUPLICATE_POLICIES.LAST }), client.ts.create('key2'), - client.ts.createRule('key', 'key2', TimeSeriesAggregationType.COUNT, 5), + client.ts.createRule('key', 'key2', TIME_SERIES_AGGREGATION_TYPE.COUNT, 5), client.ts.add('key', 1, 10) ]); const infoDebug = await client.ts.infoDebug('key'); - assertInfo(infoDebug); + assertInfo(infoDebug as any); assert.equal(typeof infoDebug.keySelfName, 'string'); assert.ok(Array.isArray(infoDebug.chunks)); for (const chunk of infoDebug.chunks) { diff --git a/packages/time-series/lib/commands/INFO_DEBUG.ts b/packages/time-series/lib/commands/INFO_DEBUG.ts index 20d6ff5e242..fb2b28b8072 100644 --- a/packages/time-series/lib/commands/INFO_DEBUG.ts +++ b/packages/time-series/lib/commands/INFO_DEBUG.ts @@ -1,57 +1,79 @@ -import { - transformArguments as transformInfoArguments, - InfoRawReply, - InfoReply, - transformReply as transformInfoReply -} from './INFO'; +import { BlobStringReply, Command, NumberReply, SimpleStringReply, TypeMapping } from "@redis/client/lib/RESP/types"; +import INFO, { InfoRawReply, InfoRawReplyTypes, InfoReply } from "./INFO"; +import { ReplyUnion } from '@redis/client/lib/RESP/types'; -export { IS_READ_ONLY, FIRST_KEY_INDEX } from './INFO'; - -export function transformArguments(key: string): Array { - const args = transformInfoArguments(key); - args.push('DEBUG'); - return args; -} +type chunkType = Array<[ + 'startTimestamp', + NumberReply, + 'endTimestamp', + NumberReply, + 'samples', + NumberReply, + 'size', + NumberReply, + 'bytesPerSample', + SimpleStringReply +]>; type InfoDebugRawReply = [ - ...InfoRawReply, - 'keySelfName', - string, - 'chunks', - Array<[ - 'startTimestamp', - number, - 'endTimestamp', - number, - 'samples', - number, - 'size', - number, - 'bytesPerSample', - string - ]> + ...InfoRawReply, + 'keySelfName', + BlobStringReply, + 'Chunks', + chunkType ]; -interface InfoDebugReply extends InfoReply { - keySelfName: string; - chunks: Array<{ - startTimestamp: number; - endTimestamp: number; - samples: number; - size: number; - bytesPerSample: string; - }>; -} +export type InfoDebugRawReplyType = InfoRawReplyTypes | chunkType -export function transformReply(rawReply: InfoDebugRawReply): InfoDebugReply { - const reply = transformInfoReply(rawReply as unknown as InfoRawReply); - (reply as InfoDebugReply).keySelfName = rawReply[25]; - (reply as InfoDebugReply).chunks = rawReply[27].map(chunk => ({ - startTimestamp: chunk[1], - endTimestamp: chunk[3], - samples: chunk[5], - size: chunk[7], - bytesPerSample: chunk[9] - })); - return reply as InfoDebugReply; +export interface InfoDebugReply extends InfoReply { + keySelfName: BlobStringReply, + chunks: Array<{ + startTimestamp: NumberReply; + endTimestamp: NumberReply; + samples: NumberReply; + size: NumberReply; + bytesPerSample: SimpleStringReply; + }>; } + +export default { + FIRST_KEY_INDEX: INFO.FIRST_KEY_INDEX, + IS_READ_ONLY: INFO.IS_READ_ONLY, + transformArguments(key: string) { + const args = INFO.transformArguments(key); + args.push('DEBUG'); + return args; + }, + transformReply: { + 2: (reply: InfoDebugRawReply, _, typeMapping?: TypeMapping): InfoDebugReply => { + const ret = INFO.transformReply[2](reply as unknown as InfoRawReply, _, typeMapping) as any; + + for (let i=0; i < reply.length; i += 2) { + const key = (reply[i] as any).toString(); + + switch (key) { + case 'keySelfName': { + ret[key] = reply[i+1]; + break; + } + case 'Chunks': { + ret['chunks'] = (reply[i+1] as chunkType).map( + chunk => ({ + startTimestamp: chunk[1], + endTimestamp: chunk[3], + samples: chunk[5], + size: chunk[7], + bytesPerSample: chunk[9] + }) + ); + break; + } + } + } + + return ret; + }, + 3: undefined as unknown as () => ReplyUnion + }, + unstableResp3: true +} as const satisfies Command; \ No newline at end of file diff --git a/packages/time-series/lib/commands/MADD.spec.ts b/packages/time-series/lib/commands/MADD.spec.ts index eed014f2b14..bbe358e5438 100644 --- a/packages/time-series/lib/commands/MADD.spec.ts +++ b/packages/time-series/lib/commands/MADD.spec.ts @@ -1,39 +1,41 @@ -import { strict as assert } from 'assert'; +import { strict as assert } from 'node:assert'; import testUtils, { GLOBAL } from '../test-utils'; -import { transformArguments } from './MADD'; +import MADD from './MADD'; +import { SimpleError } from '@redis/client/lib/errors'; -describe('MADD', () => { - it('transformArguments', () => { - assert.deepEqual( - transformArguments([{ - key: '1', - timestamp: 0, - value: 0 - }, { - key: '2', - timestamp: 1, - value: 1 - }]), - ['TS.MADD', '1', '0', '0', '2', '1', '1'] - ); - }); +describe('TS.MADD', () => { + it('transformArguments', () => { + assert.deepEqual( + MADD.transformArguments([{ + key: '1', + timestamp: 0, + value: 0 + }, { + key: '2', + timestamp: 1, + value: 1 + }]), + ['TS.MADD', '1', '0', '0', '2', '1', '1'] + ); + }); - // Should we check empty array? + testUtils.testWithClient('client.ts.mAdd', async client => { + const [, reply] = await Promise.all([ + client.ts.create('key'), + client.ts.mAdd([{ + key: 'key', + timestamp: 0, + value: 1 + }, { + key: 'key', + timestamp: 0, + value: 1 + }]) + ]); - testUtils.testWithClient('client.ts.mAdd', async client => { - await client.ts.create('key'); - - assert.deepEqual( - await client.ts.mAdd([{ - key: 'key', - timestamp: 0, - value: 0 - }, { - key: 'key', - timestamp: 1, - value: 1 - }]), - [0, 1] - ); - }, GLOBAL.SERVERS.OPEN); + assert.ok(Array.isArray(reply)); + assert.equal(reply.length, 2); + assert.equal(reply[0], 0); + assert.ok(reply[1] instanceof SimpleError); + }, GLOBAL.SERVERS.OPEN); }); diff --git a/packages/time-series/lib/commands/MADD.ts b/packages/time-series/lib/commands/MADD.ts index 426eae7e3f3..59c1ed59bdb 100644 --- a/packages/time-series/lib/commands/MADD.ts +++ b/packages/time-series/lib/commands/MADD.ts @@ -1,25 +1,27 @@ import { Timestamp, transformTimestampArgument } from '.'; +import { ArrayReply, NumberReply, SimpleErrorReply, Command } from '@redis/client/dist/lib/RESP/types'; -export const FIRST_KEY_INDEX = 1; - -interface MAddSample { - key: string; - timestamp: Timestamp; - value: number; +export interface TsMAddSample { + key: string; + timestamp: Timestamp; + value: number; } -export function transformArguments(toAdd: Array): Array { +export default { + FIRST_KEY_INDEX: 1, + IS_READ_ONLY: false, + transformArguments(toAdd: Array) { const args = ['TS.MADD']; for (const { key, timestamp, value } of toAdd) { - args.push( - key, - transformTimestampArgument(timestamp), - value.toString() - ); + args.push( + key, + transformTimestampArgument(timestamp), + value.toString() + ); } return args; -} - -export declare function transformReply(): Array; + }, + transformReply: undefined as unknown as () => ArrayReply +} as const satisfies Command; diff --git a/packages/time-series/lib/commands/MGET.spec.ts b/packages/time-series/lib/commands/MGET.spec.ts index 61da3b96383..b2de0486cfe 100644 --- a/packages/time-series/lib/commands/MGET.spec.ts +++ b/packages/time-series/lib/commands/MGET.spec.ts @@ -1,40 +1,45 @@ -import { strict as assert } from 'assert'; +import { strict as assert } from 'node:assert'; import testUtils, { GLOBAL } from '../test-utils'; -import { transformArguments } from './MGET'; +import MGET from './MGET'; -describe('MGET', () => { - describe('transformArguments', () => { - it('without options', () => { - assert.deepEqual( - transformArguments('label=value'), - ['TS.MGET', 'FILTER', 'label=value'] - ); - }); +describe('TS.MGET', () => { + describe('transformArguments', () => { + it('without options', () => { + assert.deepEqual( + MGET.transformArguments('label=value'), + ['TS.MGET', 'FILTER', 'label=value'] + ); + }); - it('with LATEST', () => { - assert.deepEqual( - transformArguments('label=value', { - LATEST: true - }), - ['TS.MGET', 'LATEST', 'FILTER', 'label=value'] - ); - }); + it('with LATEST', () => { + assert.deepEqual( + MGET.transformArguments('label=value', { + LATEST: true + }), + ['TS.MGET', 'LATEST', 'FILTER', 'label=value'] + ); }); + }); - testUtils.testWithClient('client.ts.mGet', async client => { - await client.ts.add('key', 0, 0, { - LABELS: { label: 'value' } - }); + testUtils.testWithClient('client.ts.mGet', async client => { + const [, reply] = await Promise.all([ + client.ts.add('key', 0, 0, { + LABELS: { label: 'value' } + }), + client.ts.mGet('label=value') + ]); - assert.deepEqual( - await client.ts.mGet('label=value'), - [{ - key: 'key', - sample: { - timestamp: 0, - value: 0 - } - }] - ); - }, GLOBAL.SERVERS.OPEN); + assert.deepStrictEqual(reply, Object.create(null, { + key: { + configurable: true, + enumerable: true, + value: { + sample: { + timestamp: 0, + value: 0 + } + } + } + })); + }, GLOBAL.SERVERS.OPEN); }); diff --git a/packages/time-series/lib/commands/MGET.ts b/packages/time-series/lib/commands/MGET.ts index 67315722eb6..2b04b29589b 100644 --- a/packages/time-series/lib/commands/MGET.ts +++ b/packages/time-series/lib/commands/MGET.ts @@ -1,31 +1,61 @@ -import { RedisCommandArguments } from '@redis/client/dist/lib/commands'; -import { Filter, pushFilterArgument, pushLatestArgument, RawLabels, SampleRawReply, SampleReply, transformSampleReply } from '.'; +import { CommandArguments, Command, BlobStringReply, ArrayReply, Resp2Reply, MapReply, TuplesReply, TypeMapping } from '@redis/client/dist/lib/RESP/types'; +import { RedisVariadicArgument, pushVariadicArguments } from '@redis/client/dist/lib/commands/generic-transformers'; +import { resp2MapToValue, resp3MapToValue, SampleRawReply, transformSampleReply } from '.'; -export const IS_READ_ONLY = true; +export interface TsMGetOptions { + LATEST?: boolean; +} + +export function pushLatestArgument(args: CommandArguments, latest?: boolean) { + if (latest) { + args.push('LATEST'); + } -export interface MGetOptions { - LATEST?: boolean; + return args; } -export function transformArguments(filter: Filter, options?: MGetOptions): RedisCommandArguments { - const args = pushLatestArgument(['TS.MGET'], options?.LATEST); - return pushFilterArgument(args, filter); +export function pushFilterArgument(args: CommandArguments, filter: RedisVariadicArgument) { + args.push('FILTER'); + return pushVariadicArguments(args, filter); } -export type MGetRawReply = Array<[ - key: string, - labels: RawLabels, - sample: SampleRawReply -]>; +export type MGetRawReply2 = ArrayReply< + TuplesReply<[ + key: BlobStringReply, + labels: never, + sample: Resp2Reply + ]> +>; -export interface MGetReply { - key: string, - sample: SampleReply -} +export type MGetRawReply3 = MapReply< + BlobStringReply, + TuplesReply<[ + labels: never, + sample: SampleRawReply + ]> +>; -export function transformReply(reply: MGetRawReply): Array { - return reply.map(([key, _, sample]) => ({ - key, - sample: transformSampleReply(sample) - })); -} +export default { + FIRST_KEY_INDEX: undefined, + IS_READ_ONLY: true, + transformArguments(filter: RedisVariadicArgument, options?: TsMGetOptions) { + const args = pushLatestArgument(['TS.MGET'], options?.LATEST); + return pushFilterArgument(args, filter); + }, + transformReply: { + 2(reply: MGetRawReply2, _, typeMapping?: TypeMapping) { + return resp2MapToValue(reply, ([,, sample]) => { + return { + sample: transformSampleReply[2](sample) + }; + }, typeMapping); + }, + 3(reply: MGetRawReply3) { + return resp3MapToValue(reply, ([, sample]) => { + return { + sample: transformSampleReply[3](sample) + }; + }); + } + } +} as const satisfies Command; diff --git a/packages/time-series/lib/commands/MGET_SELECTED_LABELS.spec.ts b/packages/time-series/lib/commands/MGET_SELECTED_LABELS.spec.ts new file mode 100644 index 00000000000..d9820027bb9 --- /dev/null +++ b/packages/time-series/lib/commands/MGET_SELECTED_LABELS.spec.ts @@ -0,0 +1,46 @@ +import { strict as assert } from 'node:assert'; +import testUtils, { GLOBAL } from '../test-utils'; +import MGET_SELECTED_LABELS from './MGET_SELECTED_LABELS'; + +describe('TS.MGET_SELECTED_LABELS', () => { + it('transformArguments', () => { + assert.deepEqual( + MGET_SELECTED_LABELS.transformArguments('label=value', 'label'), + ['TS.MGET', 'SELECTED_LABELS', 'label', 'FILTER', 'label=value'] + ); + }); + + testUtils.testWithClient('client.ts.mGetSelectedLabels', async client => { + const [, reply] = await Promise.all([ + client.ts.add('key', 0, 0, { + LABELS: { label: 'value' } + }), + client.ts.mGetSelectedLabels('label=value', ['label', 'NX']) + ]); + + assert.deepStrictEqual(reply, Object.create(null, { + key: { + configurable: true, + enumerable: true, + value: { + labels: Object.create(null, { + label: { + configurable: true, + enumerable: true, + value: 'value' + }, + NX: { + configurable: true, + enumerable: true, + value: null + } + }), + sample: { + timestamp: 0, + value: 0 + } + } + } + })); + }, GLOBAL.SERVERS.OPEN); +}); diff --git a/packages/time-series/lib/commands/MGET_SELECTED_LABELS.ts b/packages/time-series/lib/commands/MGET_SELECTED_LABELS.ts new file mode 100644 index 00000000000..d132972d879 --- /dev/null +++ b/packages/time-series/lib/commands/MGET_SELECTED_LABELS.ts @@ -0,0 +1,16 @@ +import { Command, BlobStringReply, NullReply } from '@redis/client/dist/lib/RESP/types'; +import { RedisVariadicArgument } from '@redis/client/dist/lib/commands/generic-transformers'; +import { TsMGetOptions, pushLatestArgument, pushFilterArgument } from './MGET'; +import { pushSelectedLabelsArguments } from '.'; +import { createTransformMGetLabelsReply } from './MGET_WITHLABELS'; + +export default { + FIRST_KEY_INDEX: undefined, + IS_READ_ONLY: true, + transformArguments(filter: RedisVariadicArgument, selectedLabels: RedisVariadicArgument, options?: TsMGetOptions) { + let args = pushLatestArgument(['TS.MGET'], options?.LATEST); + args = pushSelectedLabelsArguments(args, selectedLabels); + return pushFilterArgument(args, filter); + }, + transformReply: createTransformMGetLabelsReply(), +} as const satisfies Command; diff --git a/packages/time-series/lib/commands/MGET_WITHLABELS.spec.ts b/packages/time-series/lib/commands/MGET_WITHLABELS.spec.ts index 55fcfde409d..d3e51d2cab6 100644 --- a/packages/time-series/lib/commands/MGET_WITHLABELS.spec.ts +++ b/packages/time-series/lib/commands/MGET_WITHLABELS.spec.ts @@ -1,39 +1,41 @@ -import { strict as assert } from 'assert'; +import { strict as assert } from 'node:assert'; import testUtils, { GLOBAL } from '../test-utils'; -import { transformArguments } from './MGET_WITHLABELS'; +import MGET_WITHLABELS from './MGET_WITHLABELS'; -describe('MGET_WITHLABELS', () => { - describe('transformArguments', () => { - it('without options', () => { - assert.deepEqual( - transformArguments('label=value'), - ['TS.MGET', 'WITHLABELS', 'FILTER', 'label=value'] - ); - }); +describe('TS.MGET_WITHLABELS', () => { + it('transformArguments', () => { + assert.deepEqual( + MGET_WITHLABELS.transformArguments('label=value'), + ['TS.MGET', 'WITHLABELS', 'FILTER', 'label=value'] + ); + }); - it('with SELECTED_LABELS', () => { - assert.deepEqual( - transformArguments('label=value', { SELECTED_LABELS: 'label' }), - ['TS.MGET', 'SELECTED_LABELS', 'label', 'FILTER', 'label=value'] - ); - }); - }); - - testUtils.testWithClient('client.ts.mGetWithLabels', async client => { - await client.ts.add('key', 0, 0, { - LABELS: { label: 'value' } - }); - - assert.deepEqual( - await client.ts.mGetWithLabels('label=value'), - [{ - key: 'key', - labels: { label: 'value'}, - sample: { - timestamp: 0, - value: 0 - } - }] - ); - }, GLOBAL.SERVERS.OPEN); + testUtils.testWithClient('client.ts.mGetWithLabels', async client => { + const [, reply] = await Promise.all([ + client.ts.add('key', 0, 0, { + LABELS: { label: 'value' } + }), + client.ts.mGetWithLabels('label=value') + ]); + + assert.deepStrictEqual(reply, Object.create(null, { + key: { + configurable: true, + enumerable: true, + value: { + labels: Object.create(null, { + label: { + configurable: true, + enumerable: true, + value: 'value' + } + }), + sample: { + timestamp: 0, + value: 0 + } + } + } + })); + }, GLOBAL.SERVERS.OPEN); }); diff --git a/packages/time-series/lib/commands/MGET_WITHLABELS.ts b/packages/time-series/lib/commands/MGET_WITHLABELS.ts index 232c17a0ada..679a536f2ab 100644 --- a/packages/time-series/lib/commands/MGET_WITHLABELS.ts +++ b/packages/time-series/lib/commands/MGET_WITHLABELS.ts @@ -1,37 +1,61 @@ -import { - SelectedLabels, - pushWithLabelsArgument, - Labels, - transformLablesReply, - transformSampleReply, - Filter, - pushFilterArgument -} from '.'; -import { MGetOptions, MGetRawReply, MGetReply } from './MGET'; -import { RedisCommandArguments } from '@redis/client/dist/lib/commands'; +import { Command, BlobStringReply, ArrayReply, Resp2Reply, MapReply, TuplesReply, TypeMapping } from '@redis/client/dist/lib/RESP/types'; +import { RedisVariadicArgument } from '@redis/client/dist/lib/commands/generic-transformers'; +import { TsMGetOptions, pushLatestArgument, pushFilterArgument } from './MGET'; +import { RawLabelValue, resp2MapToValue, resp3MapToValue, SampleRawReply, transformRESP2Labels, transformSampleReply } from '.'; -export const IS_READ_ONLY = true; - -interface MGetWithLabelsOptions extends MGetOptions { - SELECTED_LABELS?: SelectedLabels; +export interface TsMGetWithLabelsOptions extends TsMGetOptions { + SELECTED_LABELS?: RedisVariadicArgument; } -export function transformArguments( - filter: Filter, - options?: MGetWithLabelsOptions -): RedisCommandArguments { - const args = pushWithLabelsArgument(['TS.MGET'], options?.SELECTED_LABELS); - return pushFilterArgument(args, filter); -} +export type MGetLabelsRawReply2 = ArrayReply< + TuplesReply<[ + key: BlobStringReply, + labels: ArrayReply< + TuplesReply<[ + label: BlobStringReply, + value: T + ]> + >, + sample: Resp2Reply + ]> +>; -export interface MGetWithLabelsReply extends MGetReply { - labels: Labels; -}; +export type MGetLabelsRawReply3 = MapReply< + BlobStringReply, + TuplesReply<[ + labels: MapReply, + sample: SampleRawReply + ]> +>; -export function transformReply(reply: MGetRawReply): Array { - return reply.map(([key, labels, sample]) => ({ - key, - labels: transformLablesReply(labels), - sample: transformSampleReply(sample) - })); +export function createTransformMGetLabelsReply() { + return { + 2(reply: MGetLabelsRawReply2, _, typeMapping?: TypeMapping) { + return resp2MapToValue(reply, ([, labels, sample]) => { + return { + labels: transformRESP2Labels(labels), + sample: transformSampleReply[2](sample) + }; + }, typeMapping); + }, + 3(reply: MGetLabelsRawReply3) { + return resp3MapToValue(reply, ([labels, sample]) => { + return { + labels, + sample: transformSampleReply[3](sample) + }; + }); + } + } satisfies Command['transformReply']; } + +export default { + FIRST_KEY_INDEX: undefined, + IS_READ_ONLY: true, + transformArguments(filter: RedisVariadicArgument, options?: TsMGetOptions) { + const args = pushLatestArgument(['TS.MGET'], options?.LATEST); + args.push('WITHLABELS'); + return pushFilterArgument(args, filter); + }, + transformReply: createTransformMGetLabelsReply(), +} as const satisfies Command; diff --git a/packages/time-series/lib/commands/MRANGE.spec.ts b/packages/time-series/lib/commands/MRANGE.spec.ts index 4228cc06fb7..9d41763eb02 100644 --- a/packages/time-series/lib/commands/MRANGE.spec.ts +++ b/packages/time-series/lib/commands/MRANGE.spec.ts @@ -1,50 +1,62 @@ -import { strict as assert } from 'assert'; +import { strict as assert } from 'node:assert'; import testUtils, { GLOBAL } from '../test-utils'; -import { transformArguments } from './MRANGE'; -import { TimeSeriesAggregationType, TimeSeriesReducers } from '.'; +import MRANGE from './MRANGE'; +import { TIME_SERIES_AGGREGATION_TYPE } from './CREATERULE'; -describe('MRANGE', () => { - it('transformArguments', () => { - assert.deepEqual( - transformArguments('-', '+', 'label=value', { - FILTER_BY_TS: [0], - FILTER_BY_VALUE: { - min: 0, - max: 1 - }, - COUNT: 1, - ALIGN: '-', - AGGREGATION: { - type: TimeSeriesAggregationType.AVERAGE, - timeBucket: 1 - }, - GROUPBY: { - label: 'label', - reducer: TimeSeriesReducers.SUM - }, - }), - ['TS.MRANGE', '-', '+', 'FILTER_BY_TS', '0', 'FILTER_BY_VALUE', '0', '1', - 'COUNT', '1', 'ALIGN', '-', 'AGGREGATION', 'AVG', '1', 'FILTER', 'label=value', - 'GROUPBY', 'label', 'REDUCE', 'SUM'] - ); - }); +describe('TS.MRANGE', () => { + it('transformArguments', () => { + assert.deepEqual( + MRANGE.transformArguments('-', '+', 'label=value', { + LATEST: true, + FILTER_BY_TS: [0], + FILTER_BY_VALUE: { + min: 0, + max: 1 + }, + COUNT: 1, + ALIGN: '-', + AGGREGATION: { + type: TIME_SERIES_AGGREGATION_TYPE.AVG, + timeBucket: 1 + } + }), + [ + 'TS.MRANGE', '-', '+', + 'LATEST', + 'FILTER_BY_TS', '0', + 'FILTER_BY_VALUE', '0', '1', + 'COUNT', '1', + 'ALIGN', '-', + 'AGGREGATION', 'AVG', '1', + 'FILTER', 'label=value' + ] + ); + }); - testUtils.testWithClient('client.ts.mRange', async client => { - await client.ts.add('key', 0, 0, { - LABELS: { label: 'value'} - }); + testUtils.testWithClient('client.ts.mRange', async client => { + const [, reply] = await Promise.all([ + client.ts.add('key', 0, 0, { + LABELS: { + label: 'value' + } + }), + client.ts.mRange('-', '+', 'label=value', { + COUNT: 1 + }) + ]); - assert.deepEqual( - await client.ts.mRange('-', '+', 'label=value', { - COUNT: 1 - }), - [{ - key: 'key', - samples: [{ - timestamp: 0, - value: 0 - }] - }] - ); - }, GLOBAL.SERVERS.OPEN); + assert.deepStrictEqual( + reply, + Object.create(null, { + key: { + configurable: true, + enumerable: true, + value: [{ + timestamp: 0, + value: 0 + }] + } + }) + ); + }, GLOBAL.SERVERS.OPEN); }); diff --git a/packages/time-series/lib/commands/MRANGE.ts b/packages/time-series/lib/commands/MRANGE.ts index d589ac0332a..bbc93a70dad 100644 --- a/packages/time-series/lib/commands/MRANGE.ts +++ b/packages/time-series/lib/commands/MRANGE.ts @@ -1,21 +1,58 @@ -import { RedisCommandArguments } from '@redis/client/dist/lib/commands'; -import { MRangeOptions, Timestamp, pushMRangeArguments, Filter } from '.'; +import { Command, ArrayReply, BlobStringReply, Resp2Reply, MapReply, TuplesReply, TypeMapping, RedisArgument } from '@redis/client/dist/lib/RESP/types'; +import { RedisVariadicArgument } from '@redis/client/dist/lib/commands/generic-transformers'; +import { resp2MapToValue, resp3MapToValue, SampleRawReply, Timestamp, transformSamplesReply } from '.'; +import { TsRangeOptions, pushRangeArguments } from './RANGE'; +import { pushFilterArgument } from './MGET'; -export const IS_READ_ONLY = true; +export type TsMRangeRawReply2 = ArrayReply< + TuplesReply<[ + key: BlobStringReply, + labels: never, // empty array without WITHLABELS or SELECTED_LABELS + samples: ArrayReply> + ]> +>; -export function transformArguments( +export type TsMRangeRawReply3 = MapReply< + BlobStringReply, + TuplesReply<[ + labels: never, // empty hash without WITHLABELS or SELECTED_LABELS + metadata: never, // ?! + samples: ArrayReply + ]> +>; + +export function createTransformMRangeArguments(command: RedisArgument) { + return ( fromTimestamp: Timestamp, toTimestamp: Timestamp, - filters: Filter, - options?: MRangeOptions -): RedisCommandArguments { - return pushMRangeArguments( - ['TS.MRANGE'], - fromTimestamp, - toTimestamp, - filters, - options + filter: RedisVariadicArgument, + options?: TsRangeOptions + ) => { + const args = pushRangeArguments( + [command], + fromTimestamp, + toTimestamp, + options ); + + return pushFilterArgument(args, filter); + }; } -export { transformMRangeReply as transformReply } from '.'; +export default { + FIRST_KEY_INDEX: undefined, + IS_READ_ONLY: true, + transformArguments: createTransformMRangeArguments('TS.MRANGE'), + transformReply: { + 2(reply: TsMRangeRawReply2, _?: any, typeMapping?: TypeMapping) { + return resp2MapToValue(reply, ([_key, _labels, samples]) => { + return transformSamplesReply[2](samples); + }, typeMapping); + }, + 3(reply: TsMRangeRawReply3) { + return resp3MapToValue(reply, ([_labels, _metadata, samples]) => { + return transformSamplesReply[3](samples); + }); + } + }, +} as const satisfies Command; diff --git a/packages/time-series/lib/commands/MRANGE_GROUPBY.spec.ts b/packages/time-series/lib/commands/MRANGE_GROUPBY.spec.ts new file mode 100644 index 00000000000..c0d05425ff4 --- /dev/null +++ b/packages/time-series/lib/commands/MRANGE_GROUPBY.spec.ts @@ -0,0 +1,66 @@ +import { strict as assert } from 'node:assert'; +import testUtils, { GLOBAL } from '../test-utils'; +import MRANGE_GROUPBY, { TIME_SERIES_REDUCERS } from './MRANGE_GROUPBY'; +import { TIME_SERIES_AGGREGATION_TYPE } from './CREATERULE'; + +describe('TS.MRANGE_GROUPBY', () => { + it('transformArguments', () => { + assert.deepEqual( + MRANGE_GROUPBY.transformArguments('-', '+', 'label=value', { + REDUCE: TIME_SERIES_REDUCERS.AVG, + label: 'label' + }, { + LATEST: true, + FILTER_BY_TS: [0], + FILTER_BY_VALUE: { + min: 0, + max: 1 + }, + COUNT: 1, + ALIGN: '-', + AGGREGATION: { + type: TIME_SERIES_AGGREGATION_TYPE.AVG, + timeBucket: 1 + } + }), + [ + 'TS.MRANGE', '-', '+', + 'LATEST', + 'FILTER_BY_TS', '0', + 'FILTER_BY_VALUE', '0', '1', + 'COUNT', '1', + 'ALIGN', '-', 'AGGREGATION', 'AVG', '1', + 'FILTER', 'label=value', + 'GROUPBY', 'label', 'REDUCE', 'AVG' + ] + ); + }); + + testUtils.testWithClient('client.ts.mRangeGroupBy', async client => { + const [, reply] = await Promise.all([ + client.ts.add('key', 0, 0, { + LABELS: { label: 'value' } + }), + client.ts.mRangeGroupBy('-', '+', 'label=value', { + REDUCE: TIME_SERIES_REDUCERS.AVG, + label: 'label' + }) + ]); + + assert.deepStrictEqual( + reply, + Object.create(null, { + 'label=value': { + configurable: true, + enumerable: true, + value: { + samples: [{ + timestamp: 0, + value: 0 + }] + } + } + }) + ); + }, GLOBAL.SERVERS.OPEN); +}); diff --git a/packages/time-series/lib/commands/MRANGE_GROUPBY.ts b/packages/time-series/lib/commands/MRANGE_GROUPBY.ts new file mode 100644 index 00000000000..3b4e94eac20 --- /dev/null +++ b/packages/time-series/lib/commands/MRANGE_GROUPBY.ts @@ -0,0 +1,108 @@ +import { Command, ArrayReply, BlobStringReply, Resp2Reply, MapReply, TuplesReply, TypeMapping, RedisArgument, TuplesToMapReply, UnwrapReply } from '@redis/client/dist/lib/RESP/types'; +import { RedisVariadicArgument } from '@redis/client/dist/lib/commands/generic-transformers'; +import { resp2MapToValue, resp3MapToValue, SampleRawReply, Timestamp, transformSamplesReply } from '.'; +import { TsRangeOptions, pushRangeArguments } from './RANGE'; +import { pushFilterArgument } from './MGET'; + +export const TIME_SERIES_REDUCERS = { + AVG: 'AVG', + SUM: 'SUM', + MIN: 'MIN', + MAX: 'MAX', + RANGE: 'RANGE', + COUNT: 'COUNT', + STD_P: 'STD.P', + STD_S: 'STD.S', + VAR_P: 'VAR.P', + VAR_S: 'VAR.S' +} as const; + +export type TimeSeriesReducer = typeof TIME_SERIES_REDUCERS[keyof typeof TIME_SERIES_REDUCERS]; + +export interface TsMRangeGroupBy { + label: RedisArgument; + REDUCE: TimeSeriesReducer; +} + +export function pushGroupByArguments(args: Array, groupBy: TsMRangeGroupBy) { + args.push('GROUPBY', groupBy.label, 'REDUCE', groupBy.REDUCE); +} + +export type TsMRangeGroupByRawReply2 = ArrayReply< + TuplesReply<[ + key: BlobStringReply, + labels: never, // empty array without WITHLABELS or SELECTED_LABELS + samples: ArrayReply> + ]> +>; + +export type TsMRangeGroupByRawMetadataReply3 = TuplesToMapReply<[ + [BlobStringReply<'sources'>, ArrayReply] +]>; + +export type TsMRangeGroupByRawReply3 = MapReply< + BlobStringReply, + TuplesReply<[ + labels: never, // empty hash without WITHLABELS or SELECTED_LABELS + metadata1: never, // ?! + metadata2: TsMRangeGroupByRawMetadataReply3, + samples: ArrayReply + ]> +>; + +export function createTransformMRangeGroupByArguments(command: RedisArgument) { + return ( + fromTimestamp: Timestamp, + toTimestamp: Timestamp, + filter: RedisVariadicArgument, + groupBy: TsMRangeGroupBy, + options?: TsRangeOptions + ) => { + let args = pushRangeArguments( + [command], + fromTimestamp, + toTimestamp, + options + ); + + args = pushFilterArgument(args, filter); + + pushGroupByArguments(args, groupBy); + + return args; + }; +} + +export function extractResp3MRangeSources(raw: TsMRangeGroupByRawMetadataReply3) { + const unwrappedMetadata2 = raw as unknown as UnwrapReply; + if (unwrappedMetadata2 instanceof Map) { + return unwrappedMetadata2.get('sources')!; + } else if (unwrappedMetadata2 instanceof Array) { + return unwrappedMetadata2[1]; + } else { + return unwrappedMetadata2.sources; + } +} + +export default { + FIRST_KEY_INDEX: undefined, + IS_READ_ONLY: true, + transformArguments: createTransformMRangeGroupByArguments('TS.MRANGE'), + transformReply: { + 2(reply: TsMRangeGroupByRawReply2, _?: any, typeMapping?: TypeMapping) { + return resp2MapToValue(reply, ([_key, _labels, samples]) => { + return { + samples: transformSamplesReply[2](samples) + }; + }, typeMapping); + }, + 3(reply: TsMRangeGroupByRawReply3) { + return resp3MapToValue(reply, ([_labels, _metadata1, metadata2, samples]) => { + return { + sources: extractResp3MRangeSources(metadata2), + samples: transformSamplesReply[3](samples) + }; + }); + } + }, +} as const satisfies Command; diff --git a/packages/time-series/lib/commands/MRANGE_SELECTED_LABELS.spec.ts b/packages/time-series/lib/commands/MRANGE_SELECTED_LABELS.spec.ts new file mode 100644 index 00000000000..5c15bad89e8 --- /dev/null +++ b/packages/time-series/lib/commands/MRANGE_SELECTED_LABELS.spec.ts @@ -0,0 +1,72 @@ +import { strict as assert } from 'node:assert'; +import testUtils, { GLOBAL } from '../test-utils'; +import MRANGE_SELECTED_LABELS from './MRANGE_SELECTED_LABELS'; +import { TIME_SERIES_AGGREGATION_TYPE } from './CREATERULE'; + +describe('TS.MRANGE_SELECTED_LABELS', () => { + it('transformArguments', () => { + assert.deepEqual( + MRANGE_SELECTED_LABELS.transformArguments('-', '+', 'label', 'label=value', { + FILTER_BY_TS: [0], + FILTER_BY_VALUE: { + min: 0, + max: 1 + }, + COUNT: 1, + ALIGN: '-', + AGGREGATION: { + type: TIME_SERIES_AGGREGATION_TYPE.AVG, + timeBucket: 1 + } + }), + [ + 'TS.MRANGE', '-', '+', + 'FILTER_BY_TS', '0', + 'FILTER_BY_VALUE', '0', '1', + 'COUNT', '1', + 'ALIGN', '-', 'AGGREGATION', 'AVG', '1', + 'SELECTED_LABELS', 'label', + 'FILTER', 'label=value' + ] + ); + }); + + testUtils.testWithClient('client.ts.mRangeSelectedLabels', async client => { + const [, reply] = await Promise.all([ + client.ts.add('key', 0, 0, { + LABELS: { label: 'value' } + }), + client.ts.mRangeSelectedLabels('-', '+', ['label', 'NX'], 'label=value', { + COUNT: 1 + }) + ]); + + assert.deepStrictEqual( + reply, + Object.create(null, { + key: { + configurable: true, + enumerable: true, + value: { + labels: Object.create(null, { + label: { + configurable: true, + enumerable: true, + value: 'value' + }, + NX: { + configurable: true, + enumerable: true, + value: null + } + }), + samples: [{ + timestamp: 0, + value: 0 + }] + } + } + }) + ); + }, GLOBAL.SERVERS.OPEN); +}); diff --git a/packages/time-series/lib/commands/MRANGE_SELECTED_LABELS.ts b/packages/time-series/lib/commands/MRANGE_SELECTED_LABELS.ts new file mode 100644 index 00000000000..f91f9583330 --- /dev/null +++ b/packages/time-series/lib/commands/MRANGE_SELECTED_LABELS.ts @@ -0,0 +1,70 @@ +import { Command, ArrayReply, BlobStringReply, Resp2Reply, MapReply, TuplesReply, TypeMapping, NullReply, RedisArgument } from '@redis/client/dist/lib/RESP/types'; +import { RedisVariadicArgument } from '@redis/client/dist/lib/commands/generic-transformers'; +import { pushSelectedLabelsArguments, resp2MapToValue, resp3MapToValue, SampleRawReply, Timestamp, transformRESP2Labels, transformSamplesReply } from '.'; +import { TsRangeOptions, pushRangeArguments } from './RANGE'; +import { pushFilterArgument } from './MGET'; + +export type TsMRangeSelectedLabelsRawReply2 = ArrayReply< + TuplesReply<[ + key: BlobStringReply, + labels: ArrayReply>, + samples: ArrayReply> + ]> +>; + +export type TsMRangeSelectedLabelsRawReply3 = MapReply< + BlobStringReply, + TuplesReply<[ + labels: MapReply, + metadata: never, // ?! + samples: ArrayReply + ]> +>; + +export function createTransformMRangeSelectedLabelsArguments(command: RedisArgument) { + return ( + fromTimestamp: Timestamp, + toTimestamp: Timestamp, + selectedLabels: RedisVariadicArgument, + filter: RedisVariadicArgument, + options?: TsRangeOptions + ) => { + let args = pushRangeArguments( + [command], + fromTimestamp, + toTimestamp, + options + ); + + args = pushSelectedLabelsArguments(args, selectedLabels); + + return pushFilterArgument(args, filter); + }; +} + +export default { + FIRST_KEY_INDEX: undefined, + IS_READ_ONLY: true, + transformArguments: createTransformMRangeSelectedLabelsArguments('TS.MRANGE'), + transformReply: { + 2(reply: TsMRangeSelectedLabelsRawReply2, _?: any, typeMapping?: TypeMapping) { + return resp2MapToValue(reply, ([_key, labels, samples]) => { + return { + labels: transformRESP2Labels(labels, typeMapping), + samples: transformSamplesReply[2](samples) + }; + }, typeMapping); + }, + 3(reply: TsMRangeSelectedLabelsRawReply3) { + return resp3MapToValue(reply, ([_key, labels, samples]) => { + return { + labels, + samples: transformSamplesReply[3](samples) + }; + }); + } + }, +} as const satisfies Command; diff --git a/packages/time-series/lib/commands/MRANGE_SELECTED_LABELS_GROUPBY.spec.ts b/packages/time-series/lib/commands/MRANGE_SELECTED_LABELS_GROUPBY.spec.ts new file mode 100644 index 00000000000..90090a851aa --- /dev/null +++ b/packages/time-series/lib/commands/MRANGE_SELECTED_LABELS_GROUPBY.spec.ts @@ -0,0 +1,80 @@ +import { strict as assert } from 'node:assert'; +import testUtils, { GLOBAL } from '../test-utils'; +import MRANGE_SELECTED_LABELS_GROUPBY from './MRANGE_SELECTED_LABELS_GROUPBY'; +import { TIME_SERIES_REDUCERS } from './MRANGE_GROUPBY'; +import { TIME_SERIES_AGGREGATION_TYPE } from './CREATERULE'; + +describe('TS.MRANGE_SELECTED_LABELS_GROUPBY', () => { + it('transformArguments', () => { + assert.deepEqual( + MRANGE_SELECTED_LABELS_GROUPBY.transformArguments('-', '+', 'label', 'label=value', { + REDUCE: TIME_SERIES_REDUCERS.AVG, + label: 'label' + }, { + LATEST: true, + FILTER_BY_TS: [0], + FILTER_BY_VALUE: { + min: 0, + max: 1 + }, + COUNT: 1, + ALIGN: '-', + AGGREGATION: { + type: TIME_SERIES_AGGREGATION_TYPE.AVG, + timeBucket: 1 + } + }), + [ + 'TS.MRANGE', '-', '+', + 'LATEST', + 'FILTER_BY_TS', '0', + 'FILTER_BY_VALUE', '0', '1', + 'COUNT', '1', + 'ALIGN', '-', 'AGGREGATION', 'AVG', '1', + 'SELECTED_LABELS', 'label', + 'FILTER', 'label=value', + 'GROUPBY', 'label', 'REDUCE', 'AVG' + ] + ); + }); + + testUtils.testWithClient('client.ts.mRangeSelectedLabelsGroupBy', async client => { + const [, reply] = await Promise.all([ + client.ts.add('key', 0, 0, { + LABELS: { label: 'value' } + }), + client.ts.mRangeSelectedLabelsGroupBy('-', '+', ['label', 'NX'], 'label=value', { + REDUCE: TIME_SERIES_REDUCERS.AVG, + label: 'label' + }) + ]); + + assert.deepStrictEqual( + reply, + Object.create(null, { + 'label=value': { + configurable: true, + enumerable: true, + value: { + labels: Object.create(null, { + label: { + configurable: true, + enumerable: true, + value: 'value' + }, + NX: { + configurable: true, + enumerable: true, + value: null + } + }), + samples: [{ + timestamp: 0, + value: 0 + }] + } + } + }) + ); + }, GLOBAL.SERVERS.OPEN); +}); diff --git a/packages/time-series/lib/commands/MRANGE_SELECTED_LABELS_GROUPBY.ts b/packages/time-series/lib/commands/MRANGE_SELECTED_LABELS_GROUPBY.ts new file mode 100644 index 00000000000..7a798c41137 --- /dev/null +++ b/packages/time-series/lib/commands/MRANGE_SELECTED_LABELS_GROUPBY.ts @@ -0,0 +1,63 @@ +import { Command, ArrayReply, BlobStringReply, MapReply, TuplesReply, RedisArgument, NullReply } from '@redis/client/dist/lib/RESP/types'; +import { RedisVariadicArgument } from '@redis/client/dist/lib/commands/generic-transformers'; +import { pushSelectedLabelsArguments, resp3MapToValue, SampleRawReply, Timestamp, transformSamplesReply } from '.'; +import { TsRangeOptions, pushRangeArguments } from './RANGE'; +import { extractResp3MRangeSources, pushGroupByArguments, TsMRangeGroupBy, TsMRangeGroupByRawMetadataReply3 } from './MRANGE_GROUPBY'; +import { pushFilterArgument } from './MGET'; +import MRANGE_SELECTED_LABELS from './MRANGE_SELECTED_LABELS'; + +export type TsMRangeWithLabelsGroupByRawReply3 = MapReply< + BlobStringReply, + TuplesReply<[ + labels: MapReply, + metadata: never, // ?! + metadata2: TsMRangeGroupByRawMetadataReply3, + samples: ArrayReply + ]> +>; + +export function createMRangeSelectedLabelsGroupByTransformArguments( + command: RedisArgument +) { + return ( + fromTimestamp: Timestamp, + toTimestamp: Timestamp, + selectedLabels: RedisVariadicArgument, + filter: RedisVariadicArgument, + groupBy: TsMRangeGroupBy, + options?: TsRangeOptions + ) => { + let args = pushRangeArguments( + [command], + fromTimestamp, + toTimestamp, + options + ); + + args = pushSelectedLabelsArguments(args, selectedLabels); + + args = pushFilterArgument(args, filter); + + pushGroupByArguments(args, groupBy); + + return args; + }; +} + +export default { + FIRST_KEY_INDEX: undefined, + IS_READ_ONLY: true, + transformArguments: createMRangeSelectedLabelsGroupByTransformArguments('TS.MRANGE'), + transformReply: { + 2: MRANGE_SELECTED_LABELS.transformReply[2], + 3(reply: TsMRangeWithLabelsGroupByRawReply3) { + return resp3MapToValue(reply, ([labels, _metadata, metadata2, samples]) => { + return { + labels, + sources: extractResp3MRangeSources(metadata2), + samples: transformSamplesReply[3](samples) + }; + }); + } + }, +} as const satisfies Command; diff --git a/packages/time-series/lib/commands/MRANGE_WITHLABELS.spec.ts b/packages/time-series/lib/commands/MRANGE_WITHLABELS.spec.ts index 983114f840e..fabf04b60dc 100644 --- a/packages/time-series/lib/commands/MRANGE_WITHLABELS.spec.ts +++ b/packages/time-series/lib/commands/MRANGE_WITHLABELS.spec.ts @@ -1,52 +1,68 @@ -import { strict as assert } from 'assert'; +import { strict as assert } from 'node:assert'; import testUtils, { GLOBAL } from '../test-utils'; -import { transformArguments } from './MRANGE_WITHLABELS'; -import { TimeSeriesAggregationType, TimeSeriesReducers } from '.'; +import MRANGE_WITHLABELS from './MRANGE_WITHLABELS'; +import { TIME_SERIES_AGGREGATION_TYPE } from './CREATERULE'; -describe('MRANGE_WITHLABELS', () => { - it('transformArguments', () => { - assert.deepEqual( - transformArguments('-', '+', 'label=value', { - FILTER_BY_TS: [0], - FILTER_BY_VALUE: { - min: 0, - max: 1 - }, - SELECTED_LABELS: ['label'], - COUNT: 1, - ALIGN: '-', - AGGREGATION: { - type: TimeSeriesAggregationType.AVERAGE, - timeBucket: 1 - }, - GROUPBY: { - label: 'label', - reducer: TimeSeriesReducers.SUM - }, - }), - ['TS.MRANGE', '-', '+', 'FILTER_BY_TS', '0', 'FILTER_BY_VALUE', '0', '1', - 'COUNT', '1', 'ALIGN', '-', 'AGGREGATION', 'AVG', '1', 'SELECTED_LABELS', 'label', - 'FILTER', 'label=value', 'GROUPBY', 'label', 'REDUCE', 'SUM'] - ); - }); +describe('TS.MRANGE_WITHLABELS', () => { + it('transformArguments', () => { + assert.deepEqual( + MRANGE_WITHLABELS.transformArguments('-', '+', 'label=value', { + LATEST: true, + FILTER_BY_TS: [0], + FILTER_BY_VALUE: { + min: 0, + max: 1 + }, + COUNT: 1, + ALIGN: '-', + AGGREGATION: { + type: TIME_SERIES_AGGREGATION_TYPE.AVG, + timeBucket: 1 + } + }), + [ + 'TS.MRANGE', '-', '+', + 'LATEST', + 'FILTER_BY_TS', '0', + 'FILTER_BY_VALUE', '0', '1', + 'COUNT', '1', + 'ALIGN', '-', + 'AGGREGATION', 'AVG', '1', + 'WITHLABELS', + 'FILTER', 'label=value' + ] + ); + }); - testUtils.testWithClient('client.ts.mRangeWithLabels', async client => { - await client.ts.add('key', 0, 0, { - LABELS: { label: 'value'} - }); + testUtils.testWithClient('client.ts.mRangeWithLabels', async client => { + const [, reply] = await Promise.all([ + client.ts.add('key', 0, 0, { + LABELS: { label: 'value' } + }), + client.ts.mRangeWithLabels('-', '+', 'label=value') + ]); - assert.deepEqual( - await client.ts.mRangeWithLabels('-', '+', 'label=value', { - COUNT: 1 + assert.deepStrictEqual( + reply, + Object.create(null, { + key: { + configurable: true, + enumerable: true, + value: { + labels: Object.create(null, { + label: { + configurable: true, + enumerable: true, + value: 'value' + } }), - [{ - key: 'key', - labels: { label: 'value' }, - samples: [{ - timestamp: 0, - value: 0 - }] + samples: [{ + timestamp: 0, + value: 0 }] - ); - }, GLOBAL.SERVERS.OPEN); + } + } + }) + ); + }, GLOBAL.SERVERS.OPEN); }); diff --git a/packages/time-series/lib/commands/MRANGE_WITHLABELS.ts b/packages/time-series/lib/commands/MRANGE_WITHLABELS.ts index 16b7920e82c..ab7a4ec8f6a 100644 --- a/packages/time-series/lib/commands/MRANGE_WITHLABELS.ts +++ b/packages/time-series/lib/commands/MRANGE_WITHLABELS.ts @@ -1,21 +1,78 @@ -import { RedisCommandArguments } from '@redis/client/dist/lib/commands'; -import { Timestamp, MRangeWithLabelsOptions, pushMRangeWithLabelsArguments } from '.'; +import { Command, UnwrapReply, ArrayReply, BlobStringReply, Resp2Reply, MapReply, TuplesReply, TypeMapping, RedisArgument } from '@redis/client/dist/lib/RESP/types'; +import { RedisVariadicArgument } from '@redis/client/dist/lib/commands/generic-transformers'; +import { resp2MapToValue, resp3MapToValue, SampleRawReply, Timestamp, transformSamplesReply } from '.'; +import { TsRangeOptions, pushRangeArguments } from './RANGE'; +import { pushFilterArgument } from './MGET'; -export const IS_READ_ONLY = true; +export type TsMRangeWithLabelsRawReply2 = ArrayReply< + TuplesReply<[ + key: BlobStringReply, + labels: ArrayReply>, + samples: ArrayReply> + ]> +>; -export function transformArguments( +export type TsMRangeWithLabelsRawReply3 = MapReply< + BlobStringReply, + TuplesReply<[ + labels: MapReply, + metadata: never, // ?! + samples: ArrayReply + ]> +>; + +export function createTransformMRangeWithLabelsArguments(command: RedisArgument) { + return ( fromTimestamp: Timestamp, toTimestamp: Timestamp, - filters: string | Array, - options?: MRangeWithLabelsOptions -): RedisCommandArguments { - return pushMRangeWithLabelsArguments( - ['TS.MRANGE'], - fromTimestamp, - toTimestamp, - filters, - options + filter: RedisVariadicArgument, + options?: TsRangeOptions + ) => { + const args = pushRangeArguments( + [command], + fromTimestamp, + toTimestamp, + options ); + + args.push('WITHLABELS'); + + return pushFilterArgument(args, filter); + }; } -export { transformMRangeWithLabelsReply as transformReply } from '.'; +export default { + FIRST_KEY_INDEX: undefined, + IS_READ_ONLY: true, + transformArguments: createTransformMRangeWithLabelsArguments('TS.MRANGE'), + transformReply: { + 2(reply: TsMRangeWithLabelsRawReply2, _?: any, typeMapping?: TypeMapping) { + return resp2MapToValue(reply, ([_key, labels, samples]) => { + const unwrappedLabels = labels as unknown as UnwrapReply; + // TODO: use Map type mapping for labels + const labelsObject: Record = Object.create(null); + for (const tuple of unwrappedLabels) { + const [key, value] = tuple as unknown as UnwrapReply; + const unwrappedKey = key as unknown as UnwrapReply; + labelsObject[unwrappedKey.toString()] = value; + } + + return { + labels: labelsObject, + samples: transformSamplesReply[2](samples) + }; + }, typeMapping); + }, + 3(reply: TsMRangeWithLabelsRawReply3) { + return resp3MapToValue(reply, ([labels, _metadata, samples]) => { + return { + labels, + samples: transformSamplesReply[3](samples) + }; + }); + } + }, +} as const satisfies Command; diff --git a/packages/time-series/lib/commands/MRANGE_WITHLABELS_GROUPBY.spec.ts b/packages/time-series/lib/commands/MRANGE_WITHLABELS_GROUPBY.spec.ts new file mode 100644 index 00000000000..755c3aca320 --- /dev/null +++ b/packages/time-series/lib/commands/MRANGE_WITHLABELS_GROUPBY.spec.ts @@ -0,0 +1,77 @@ +import { strict as assert } from 'node:assert'; +import testUtils, { GLOBAL } from '../test-utils'; +import MRANGE_WITHLABELS_GROUPBY from './MRANGE_WITHLABELS_GROUPBY'; +import { TIME_SERIES_REDUCERS } from './MRANGE_GROUPBY'; +import { TIME_SERIES_AGGREGATION_TYPE } from './CREATERULE'; + +describe('TS.MRANGE_WITHLABELS_GROUPBY', () => { + it('transformArguments', () => { + assert.deepEqual( + MRANGE_WITHLABELS_GROUPBY.transformArguments('-', '+', 'label=value', { + label: 'label', + REDUCE: TIME_SERIES_REDUCERS.AVG + }, { + LATEST: true, + FILTER_BY_TS: [0], + FILTER_BY_VALUE: { + min: 0, + max: 1 + }, + COUNT: 1, + ALIGN: '-', + AGGREGATION: { + type: TIME_SERIES_AGGREGATION_TYPE.AVG, + timeBucket: 1 + } + }), + [ + 'TS.MRANGE', '-', '+', + 'LATEST', + 'FILTER_BY_TS', '0', + 'FILTER_BY_VALUE', '0', '1', + 'COUNT', '1', + 'ALIGN', '-', + 'AGGREGATION', 'AVG', '1', + 'WITHLABELS', + 'FILTER', 'label=value', + 'GROUPBY', 'label', 'REDUCE', 'AVG' + ] + ); + }); + + testUtils.testWithClient('client.ts.mRangeWithLabelsGroupBy', async client => { + const [, reply] = await Promise.all([ + client.ts.add('key', 0, 0, { + LABELS: { label: 'value' } + }), + client.ts.mRangeWithLabelsGroupBy('-', '+', 'label=value', { + label: 'label', + REDUCE: TIME_SERIES_REDUCERS.AVG + }) + ]); + + assert.deepStrictEqual( + reply, + Object.create(null, { + 'label=value': { + configurable: true, + enumerable: true, + value: { + labels: Object.create(null, { + label: { + configurable: true, + enumerable: true, + value: 'value' + } + }), + sources: ['key'], + samples: [{ + timestamp: 0, + value: 0 + }] + } + } + }) + ); + }, GLOBAL.SERVERS.OPEN); +}); diff --git a/packages/time-series/lib/commands/MRANGE_WITHLABELS_GROUPBY.ts b/packages/time-series/lib/commands/MRANGE_WITHLABELS_GROUPBY.ts new file mode 100644 index 00000000000..7c5e0af368b --- /dev/null +++ b/packages/time-series/lib/commands/MRANGE_WITHLABELS_GROUPBY.ts @@ -0,0 +1,79 @@ +import { Command, ArrayReply, BlobStringReply, Resp2Reply, MapReply, TuplesReply, TypeMapping, RedisArgument } from '@redis/client/dist/lib/RESP/types'; +import { RedisVariadicArgument } from '@redis/client/dist/lib/commands/generic-transformers'; +import { resp2MapToValue, resp3MapToValue, SampleRawReply, Timestamp, transformRESP2LabelsWithSources, transformSamplesReply } from '.'; +import { TsRangeOptions, pushRangeArguments } from './RANGE'; +import { extractResp3MRangeSources, pushGroupByArguments, TsMRangeGroupBy, TsMRangeGroupByRawMetadataReply3 } from './MRANGE_GROUPBY'; +import { pushFilterArgument } from './MGET'; + +export type TsMRangeWithLabelsGroupByRawReply2 = ArrayReply< + TuplesReply<[ + key: BlobStringReply, + labels: ArrayReply>, + samples: ArrayReply> + ]> +>; + +export type TsMRangeWithLabelsGroupByRawReply3 = MapReply< + BlobStringReply, + TuplesReply<[ + labels: MapReply, + metadata: never, // ?! + metadata2: TsMRangeGroupByRawMetadataReply3, + samples: ArrayReply + ]> +>; + +export function createMRangeWithLabelsGroupByTransformArguments(command: RedisArgument) { + return ( + fromTimestamp: Timestamp, + toTimestamp: Timestamp, + filter: RedisVariadicArgument, + groupBy: TsMRangeGroupBy, + options?: TsRangeOptions + ) => { + let args = pushRangeArguments( + [command], + fromTimestamp, + toTimestamp, + options + ); + + args.push('WITHLABELS'); + + args = pushFilterArgument(args, filter); + + pushGroupByArguments(args, groupBy); + + return args; + }; +} + +export default { + FIRST_KEY_INDEX: undefined, + IS_READ_ONLY: true, + transformArguments: createMRangeWithLabelsGroupByTransformArguments('TS.MRANGE'), + transformReply: { + 2(reply: TsMRangeWithLabelsGroupByRawReply2, _?: any, typeMapping?: TypeMapping) { + return resp2MapToValue(reply, ([_key, labels, samples]) => { + const transformed = transformRESP2LabelsWithSources(labels); + return { + labels: transformed.labels, + sources: transformed.sources, + samples: transformSamplesReply[2](samples) + }; + }, typeMapping); + }, + 3(reply: TsMRangeWithLabelsGroupByRawReply3) { + return resp3MapToValue(reply, ([labels, _metadata, metadata2, samples]) => { + return { + labels, + sources: extractResp3MRangeSources(metadata2), + samples: transformSamplesReply[3](samples) + }; + }); + } + }, +} as const satisfies Command; diff --git a/packages/time-series/lib/commands/MREVRANGE.spec.ts b/packages/time-series/lib/commands/MREVRANGE.spec.ts index 6e5825d36d6..8d6b8d3c148 100644 --- a/packages/time-series/lib/commands/MREVRANGE.spec.ts +++ b/packages/time-series/lib/commands/MREVRANGE.spec.ts @@ -1,50 +1,62 @@ -import { strict as assert } from 'assert'; +import { strict as assert } from 'node:assert'; import testUtils, { GLOBAL } from '../test-utils'; -import { transformArguments } from './MREVRANGE'; -import { TimeSeriesAggregationType, TimeSeriesReducers } from '.'; +import MREVRANGE from './MREVRANGE'; +import { TIME_SERIES_AGGREGATION_TYPE } from './CREATERULE'; -describe('MREVRANGE', () => { - it('transformArguments', () => { - assert.deepEqual( - transformArguments('-', '+', 'label=value', { - FILTER_BY_TS: [0], - FILTER_BY_VALUE: { - min: 0, - max: 1 - }, - COUNT: 1, - ALIGN: '-', - AGGREGATION: { - type: TimeSeriesAggregationType.AVERAGE, - timeBucket: 1 - }, - GROUPBY: { - label: 'label', - reducer: TimeSeriesReducers.SUM - }, - }), - ['TS.MREVRANGE', '-', '+', 'FILTER_BY_TS', '0', 'FILTER_BY_VALUE', '0', '1', - 'COUNT', '1', 'ALIGN', '-', 'AGGREGATION', 'AVG', '1', 'FILTER', 'label=value', - 'GROUPBY', 'label', 'REDUCE', 'SUM'] - ); - }); +describe('TS.MREVRANGE', () => { + it('transformArguments', () => { + assert.deepEqual( + MREVRANGE.transformArguments('-', '+', 'label=value', { + LATEST: true, + FILTER_BY_TS: [0], + FILTER_BY_VALUE: { + min: 0, + max: 1 + }, + COUNT: 1, + ALIGN: '-', + AGGREGATION: { + type: TIME_SERIES_AGGREGATION_TYPE.AVG, + timeBucket: 1 + } + }), + [ + 'TS.MREVRANGE', '-', '+', + 'LATEST', + 'FILTER_BY_TS', '0', + 'FILTER_BY_VALUE', '0', '1', + 'COUNT', '1', + 'ALIGN', '-', + 'AGGREGATION', 'AVG', '1', + 'FILTER', 'label=value' + ] + ); + }); - testUtils.testWithClient('client.ts.mRevRange', async client => { - await client.ts.add('key', 0, 0, { - LABELS: { label: 'value'} - }); + testUtils.testWithClient('client.ts.mRevRange', async client => { + const [, reply] = await Promise.all([ + client.ts.add('key', 0, 0, { + LABELS: { + label: 'value' + } + }), + client.ts.mRevRange('-', '+', 'label=value', { + COUNT: 1 + }) + ]); - assert.deepEqual( - await client.ts.mRevRange('-', '+', 'label=value', { - COUNT: 1 - }), - [{ - key: 'key', - samples: [{ - timestamp: 0, - value: 0 - }] - }] - ); - }, GLOBAL.SERVERS.OPEN); + assert.deepStrictEqual( + reply, + Object.create(null, { + key: { + configurable: true, + enumerable: true, + value: [{ + timestamp: 0, + value: 0 + }] + } + }) + ); + }, GLOBAL.SERVERS.OPEN); }); diff --git a/packages/time-series/lib/commands/MREVRANGE.ts b/packages/time-series/lib/commands/MREVRANGE.ts index 127c052ffe0..097176e6832 100644 --- a/packages/time-series/lib/commands/MREVRANGE.ts +++ b/packages/time-series/lib/commands/MREVRANGE.ts @@ -1,21 +1,9 @@ -import { RedisCommandArguments } from '@redis/client/dist/lib/commands'; -import { MRangeOptions, Timestamp, pushMRangeArguments, Filter } from '.'; +import { Command } from '@redis/client/dist/lib/RESP/types'; +import MRANGE, { createTransformMRangeArguments } from './MRANGE'; -export const IS_READ_ONLY = true; - -export function transformArguments( - fromTimestamp: Timestamp, - toTimestamp: Timestamp, - filters: Filter, - options?: MRangeOptions -): RedisCommandArguments { - return pushMRangeArguments( - ['TS.MREVRANGE'], - fromTimestamp, - toTimestamp, - filters, - options - ); -} - -export { transformMRangeReply as transformReply } from '.'; +export default { + FIRST_KEY_INDEX: MRANGE.FIRST_KEY_INDEX, + IS_READ_ONLY: MRANGE.IS_READ_ONLY, + transformArguments: createTransformMRangeArguments('TS.MREVRANGE'), + transformReply: MRANGE.transformReply, +} as const satisfies Command; diff --git a/packages/time-series/lib/commands/MREVRANGE_GROUPBY.spec.ts b/packages/time-series/lib/commands/MREVRANGE_GROUPBY.spec.ts new file mode 100644 index 00000000000..9ccebc6c517 --- /dev/null +++ b/packages/time-series/lib/commands/MREVRANGE_GROUPBY.spec.ts @@ -0,0 +1,67 @@ +import { strict as assert } from 'node:assert'; +import testUtils, { GLOBAL } from '../test-utils'; +import MREVRANGE_GROUPBY from './MREVRANGE_GROUPBY'; +import { TIME_SERIES_REDUCERS } from './MRANGE_GROUPBY'; +import { TIME_SERIES_AGGREGATION_TYPE } from './CREATERULE'; + +describe('TS.MREVRANGE_GROUPBY', () => { + it('transformArguments', () => { + assert.deepEqual( + MREVRANGE_GROUPBY.transformArguments('-', '+', 'label=value', { + REDUCE: TIME_SERIES_REDUCERS.AVG, + label: 'label' + }, { + LATEST: true, + FILTER_BY_TS: [0], + FILTER_BY_VALUE: { + min: 0, + max: 1 + }, + COUNT: 1, + ALIGN: '-', + AGGREGATION: { + type: TIME_SERIES_AGGREGATION_TYPE.AVG, + timeBucket: 1 + } + }), + [ + 'TS.MREVRANGE', '-', '+', + 'LATEST', + 'FILTER_BY_TS', '0', + 'FILTER_BY_VALUE', '0', '1', + 'COUNT', '1', + 'ALIGN', '-', 'AGGREGATION', 'AVG', '1', + 'FILTER', 'label=value', + 'GROUPBY', 'label', 'REDUCE', 'AVG' + ] + ); + }); + + testUtils.testWithClient('client.ts.mRevRangeGroupBy', async client => { + const [, reply] = await Promise.all([ + client.ts.add('key', 0, 0, { + LABELS: { label: 'value' } + }), + client.ts.mRevRangeGroupBy('-', '+', 'label=value', { + REDUCE: TIME_SERIES_REDUCERS.AVG, + label: 'label' + }) + ]); + + assert.deepStrictEqual( + reply, + Object.create(null, { + 'label=value': { + configurable: true, + enumerable: true, + value: { + samples: [{ + timestamp: 0, + value: 0 + }] + } + } + }) + ); + }, GLOBAL.SERVERS.OPEN); +}); diff --git a/packages/time-series/lib/commands/MREVRANGE_GROUPBY.ts b/packages/time-series/lib/commands/MREVRANGE_GROUPBY.ts new file mode 100644 index 00000000000..24b2e6142f6 --- /dev/null +++ b/packages/time-series/lib/commands/MREVRANGE_GROUPBY.ts @@ -0,0 +1,9 @@ +import { Command } from '@redis/client/dist/lib/RESP/types'; +import MRANGE_GROUPBY, { createTransformMRangeGroupByArguments } from './MRANGE_GROUPBY'; + +export default { + FIRST_KEY_INDEX: MRANGE_GROUPBY.FIRST_KEY_INDEX, + IS_READ_ONLY: MRANGE_GROUPBY.IS_READ_ONLY, + transformArguments: createTransformMRangeGroupByArguments('TS.MREVRANGE'), + transformReply: MRANGE_GROUPBY.transformReply, +} as const satisfies Command; diff --git a/packages/time-series/lib/commands/MREVRANGE_SELECTED_LABELS.spec.ts b/packages/time-series/lib/commands/MREVRANGE_SELECTED_LABELS.spec.ts new file mode 100644 index 00000000000..f0533010b84 --- /dev/null +++ b/packages/time-series/lib/commands/MREVRANGE_SELECTED_LABELS.spec.ts @@ -0,0 +1,73 @@ +import { strict as assert } from 'node:assert'; +import testUtils, { GLOBAL } from '../test-utils'; +import MREVRANGE_SELECTED_LABELS from './MREVRANGE_SELECTED_LABELS'; +import { TIME_SERIES_AGGREGATION_TYPE } from './CREATERULE'; + +describe('TS.MREVRANGE_SELECTED_LABELS', () => { + it('transformArguments', () => { + assert.deepEqual( + MREVRANGE_SELECTED_LABELS.transformArguments('-', '+', 'label', 'label=value', { + FILTER_BY_TS: [0], + FILTER_BY_VALUE: { + min: 0, + max: 1 + }, + COUNT: 1, + ALIGN: '-', + AGGREGATION: { + type: TIME_SERIES_AGGREGATION_TYPE.AVG, + timeBucket: 1 + } + }), + [ + 'TS.MREVRANGE', '-', '+', + 'FILTER_BY_TS', '0', + 'FILTER_BY_VALUE', '0', '1', + 'COUNT', '1', + 'ALIGN', '-', 'AGGREGATION', 'AVG', '1', + 'SELECTED_LABELS', 'label', + 'FILTER', 'label=value' + ] + ); + }); + + testUtils.testWithClient('client.ts.mRevRangeSelectedLabels', async client => { + const [, reply] = await Promise.all([ + client.ts.add('key', 0, 0, { + LABELS: { label: 'value' } + }), + client.ts.mRevRangeSelectedLabels('-', '+', ['label', 'NX'], 'label=value', { + COUNT: 1 + }) + ]); + + assert.deepStrictEqual( + reply, + Object.create(null, { + key: { + configurable: true, + enumerable: true, + value: { + labels: Object.create(null, { + label: { + configurable: true, + enumerable: true, + value: 'value' + }, + NX: { + configurable: true, + enumerable: true, + value: null + } + }), + samples: [{ + timestamp: 0, + value: 0 + }] + } + + } + }) + ); + }, GLOBAL.SERVERS.OPEN); +}); diff --git a/packages/time-series/lib/commands/MREVRANGE_SELECTED_LABELS.ts b/packages/time-series/lib/commands/MREVRANGE_SELECTED_LABELS.ts new file mode 100644 index 00000000000..8656b768c28 --- /dev/null +++ b/packages/time-series/lib/commands/MREVRANGE_SELECTED_LABELS.ts @@ -0,0 +1,9 @@ +import { Command } from '@redis/client/dist/lib/RESP/types'; +import MRANGE_SELECTED_LABELS, { createTransformMRangeSelectedLabelsArguments } from './MRANGE_SELECTED_LABELS'; + +export default { + FIRST_KEY_INDEX: MRANGE_SELECTED_LABELS.FIRST_KEY_INDEX, + IS_READ_ONLY: MRANGE_SELECTED_LABELS.IS_READ_ONLY, + transformArguments: createTransformMRangeSelectedLabelsArguments('TS.MREVRANGE'), + transformReply: MRANGE_SELECTED_LABELS.transformReply, +} as const satisfies Command; diff --git a/packages/time-series/lib/commands/MREVRANGE_SELECTED_LABELS_GROUPBY.spec.ts b/packages/time-series/lib/commands/MREVRANGE_SELECTED_LABELS_GROUPBY.spec.ts new file mode 100644 index 00000000000..34ef4ff79a0 --- /dev/null +++ b/packages/time-series/lib/commands/MREVRANGE_SELECTED_LABELS_GROUPBY.spec.ts @@ -0,0 +1,80 @@ +import { strict as assert } from 'node:assert'; +import testUtils, { GLOBAL } from '../test-utils'; +import MREVRANGE_SELECTED_LABELS_GROUPBY from './MREVRANGE_SELECTED_LABELS_GROUPBY'; +import { TIME_SERIES_REDUCERS } from './MRANGE_GROUPBY'; +import { TIME_SERIES_AGGREGATION_TYPE } from './CREATERULE'; + +describe('TS.MREVRANGE_SELECTED_LABELS_GROUPBY', () => { + it('transformArguments', () => { + assert.deepEqual( + MREVRANGE_SELECTED_LABELS_GROUPBY.transformArguments('-', '+', 'label', 'label=value', { + REDUCE: TIME_SERIES_REDUCERS.AVG, + label: 'label' + }, { + LATEST: true, + FILTER_BY_TS: [0], + FILTER_BY_VALUE: { + min: 0, + max: 1 + }, + COUNT: 1, + ALIGN: '-', + AGGREGATION: { + type: TIME_SERIES_AGGREGATION_TYPE.AVG, + timeBucket: 1 + } + }), + [ + 'TS.MREVRANGE', '-', '+', + 'LATEST', + 'FILTER_BY_TS', '0', + 'FILTER_BY_VALUE', '0', '1', + 'COUNT', '1', + 'ALIGN', '-', 'AGGREGATION', 'AVG', '1', + 'SELECTED_LABELS', 'label', + 'FILTER', 'label=value', + 'GROUPBY', 'label', 'REDUCE', 'AVG' + ] + ); + }); + + testUtils.testWithClient('client.ts.mRevRangeSelectedLabelsGroupBy', async client => { + const [, reply] = await Promise.all([ + client.ts.add('key', 0, 0, { + LABELS: { label: 'value' } + }), + client.ts.mRevRangeSelectedLabelsGroupBy('-', '+', ['label', 'NX'], 'label=value', { + REDUCE: TIME_SERIES_REDUCERS.AVG, + label: 'label' + }) + ]); + + assert.deepStrictEqual( + reply, + Object.create(null, { + 'label=value': { + configurable: true, + enumerable: true, + value: { + labels: Object.create(null, { + label: { + configurable: true, + enumerable: true, + value: 'value' + }, + NX: { + configurable: true, + enumerable: true, + value: null + } + }), + samples: [{ + timestamp: 0, + value: 0 + }] + } + } + }) + ); + }, GLOBAL.SERVERS.OPEN); +}); diff --git a/packages/time-series/lib/commands/MREVRANGE_SELECTED_LABELS_GROUPBY.ts b/packages/time-series/lib/commands/MREVRANGE_SELECTED_LABELS_GROUPBY.ts new file mode 100644 index 00000000000..f47330367b7 --- /dev/null +++ b/packages/time-series/lib/commands/MREVRANGE_SELECTED_LABELS_GROUPBY.ts @@ -0,0 +1,9 @@ +import { Command } from '@redis/client/dist/lib/RESP/types'; +import MRANGE_SELECTED_LABELS_GROUPBY, { createMRangeSelectedLabelsGroupByTransformArguments } from './MRANGE_SELECTED_LABELS_GROUPBY'; + +export default { + FIRST_KEY_INDEX: MRANGE_SELECTED_LABELS_GROUPBY.FIRST_KEY_INDEX, + IS_READ_ONLY: MRANGE_SELECTED_LABELS_GROUPBY.IS_READ_ONLY, + transformArguments: createMRangeSelectedLabelsGroupByTransformArguments('TS.MREVRANGE'), + transformReply: MRANGE_SELECTED_LABELS_GROUPBY.transformReply, +} as const satisfies Command; diff --git a/packages/time-series/lib/commands/MREVRANGE_WITHLABELS.spec.ts b/packages/time-series/lib/commands/MREVRANGE_WITHLABELS.spec.ts index 7e80e965d4e..eb88f233e43 100644 --- a/packages/time-series/lib/commands/MREVRANGE_WITHLABELS.spec.ts +++ b/packages/time-series/lib/commands/MREVRANGE_WITHLABELS.spec.ts @@ -1,52 +1,68 @@ -import { strict as assert } from 'assert'; +import { strict as assert } from 'node:assert'; import testUtils, { GLOBAL } from '../test-utils'; -import { transformArguments } from './MREVRANGE_WITHLABELS'; -import { TimeSeriesAggregationType, TimeSeriesReducers } from '.'; +import MREVRANGE_WITHLABELS from './MREVRANGE_WITHLABELS'; +import { TIME_SERIES_AGGREGATION_TYPE } from './CREATERULE'; -describe('MREVRANGE_WITHLABELS', () => { - it('transformArguments', () => { - assert.deepEqual( - transformArguments('-', '+', 'label=value', { - FILTER_BY_TS: [0], - FILTER_BY_VALUE: { - min: 0, - max: 1 - }, - SELECTED_LABELS: ['label'], - COUNT: 1, - ALIGN: '-', - AGGREGATION: { - type: TimeSeriesAggregationType.AVERAGE, - timeBucket: 1 - }, - GROUPBY: { - label: 'label', - reducer: TimeSeriesReducers.SUM - }, - }), - ['TS.MREVRANGE', '-', '+', 'FILTER_BY_TS', '0', 'FILTER_BY_VALUE', '0', '1', - 'COUNT', '1', 'ALIGN', '-', 'AGGREGATION', 'AVG', '1', 'SELECTED_LABELS', 'label', - 'FILTER', 'label=value', 'GROUPBY', 'label', 'REDUCE', 'SUM'] - ); - }); +describe('TS.MREVRANGE_WITHLABELS', () => { + it('transformArguments', () => { + assert.deepEqual( + MREVRANGE_WITHLABELS.transformArguments('-', '+', 'label=value', { + LATEST: true, + FILTER_BY_TS: [0], + FILTER_BY_VALUE: { + min: 0, + max: 1 + }, + COUNT: 1, + ALIGN: '-', + AGGREGATION: { + type: TIME_SERIES_AGGREGATION_TYPE.AVG, + timeBucket: 1 + } + }), + [ + 'TS.MREVRANGE', '-', '+', + 'LATEST', + 'FILTER_BY_TS', '0', + 'FILTER_BY_VALUE', '0', '1', + 'COUNT', '1', + 'ALIGN', '-', + 'AGGREGATION', 'AVG', '1', + 'WITHLABELS', + 'FILTER', 'label=value' + ] + ); + }); - testUtils.testWithClient('client.ts.mRevRangeWithLabels', async client => { - await client.ts.add('key', 0, 0, { - LABELS: { label: 'value'} - }); + testUtils.testWithClient('client.ts.mRevRangeWithLabels', async client => { + const [, reply] = await Promise.all([ + client.ts.add('key', 0, 0, { + LABELS: { label: 'value' } + }), + client.ts.mRevRangeWithLabels('-', '+', 'label=value') + ]); - assert.deepEqual( - await client.ts.mRevRangeWithLabels('-', '+', 'label=value', { - COUNT: 1 + assert.deepStrictEqual( + reply, + Object.create(null, { + key: { + configurable: true, + enumerable: true, + value: { + labels: Object.create(null, { + label: { + configurable: true, + enumerable: true, + value: 'value' + } }), - [{ - key: 'key', - labels: { label: 'value' }, - samples: [{ - timestamp: 0, - value: 0 - }] + samples: [{ + timestamp: 0, + value: 0 }] - ); - }, GLOBAL.SERVERS.OPEN); + } + } + }) + ); + }, GLOBAL.SERVERS.OPEN); }); diff --git a/packages/time-series/lib/commands/MREVRANGE_WITHLABELS.ts b/packages/time-series/lib/commands/MREVRANGE_WITHLABELS.ts index 21a0ebc69c3..81356d845fd 100644 --- a/packages/time-series/lib/commands/MREVRANGE_WITHLABELS.ts +++ b/packages/time-series/lib/commands/MREVRANGE_WITHLABELS.ts @@ -1,21 +1,9 @@ -import { RedisCommandArguments } from '@redis/client/dist/lib/commands'; -import { Timestamp, MRangeWithLabelsOptions, pushMRangeWithLabelsArguments, Filter } from '.'; +import { Command } from '@redis/client/dist/lib/RESP/types'; +import MRANGE_WITHLABELS, { createTransformMRangeWithLabelsArguments } from './MRANGE_WITHLABELS'; -export const IS_READ_ONLY = true; - -export function transformArguments( - fromTimestamp: Timestamp, - toTimestamp: Timestamp, - filters: Filter, - options?: MRangeWithLabelsOptions -): RedisCommandArguments { - return pushMRangeWithLabelsArguments( - ['TS.MREVRANGE'], - fromTimestamp, - toTimestamp, - filters, - options - ); -} - -export { transformMRangeWithLabelsReply as transformReply } from '.'; +export default { + FIRST_KEY_INDEX: MRANGE_WITHLABELS.FIRST_KEY_INDEX, + IS_READ_ONLY: MRANGE_WITHLABELS.IS_READ_ONLY, + transformArguments: createTransformMRangeWithLabelsArguments('TS.MREVRANGE'), + transformReply: MRANGE_WITHLABELS.transformReply, +} as const satisfies Command; diff --git a/packages/time-series/lib/commands/MREVRANGE_WITHLABELS_GROUPBY.spec.ts b/packages/time-series/lib/commands/MREVRANGE_WITHLABELS_GROUPBY.spec.ts new file mode 100644 index 00000000000..da2c358b330 --- /dev/null +++ b/packages/time-series/lib/commands/MREVRANGE_WITHLABELS_GROUPBY.spec.ts @@ -0,0 +1,77 @@ +import { strict as assert } from 'node:assert'; +import testUtils, { GLOBAL } from '../test-utils'; +import MREVRANGE_WITHLABELS_GROUPBY from './MREVRANGE_WITHLABELS_GROUPBY'; +import { TIME_SERIES_REDUCERS } from './MRANGE_GROUPBY'; +import { TIME_SERIES_AGGREGATION_TYPE } from './CREATERULE'; + +describe('TS.MREVRANGE_WITHLABELS_GROUPBY', () => { + it('transformArguments', () => { + assert.deepEqual( + MREVRANGE_WITHLABELS_GROUPBY.transformArguments('-', '+', 'label=value', { + label: 'label', + REDUCE: TIME_SERIES_REDUCERS.AVG + }, { + LATEST: true, + FILTER_BY_TS: [0], + FILTER_BY_VALUE: { + min: 0, + max: 1 + }, + COUNT: 1, + ALIGN: '-', + AGGREGATION: { + type: TIME_SERIES_AGGREGATION_TYPE.AVG, + timeBucket: 1 + } + }), + [ + 'TS.MREVRANGE', '-', '+', + 'LATEST', + 'FILTER_BY_TS', '0', + 'FILTER_BY_VALUE', '0', '1', + 'COUNT', '1', + 'ALIGN', '-', + 'AGGREGATION', 'AVG', '1', + 'WITHLABELS', + 'FILTER', 'label=value', + 'GROUPBY', 'label', 'REDUCE', 'AVG' + ] + ); + }); + + testUtils.testWithClient('client.ts.mRevRangeWithLabelsGroupBy', async client => { + const [, reply] = await Promise.all([ + client.ts.add('key', 0, 0, { + LABELS: { label: 'value' } + }), + client.ts.mRevRangeWithLabelsGroupBy('-', '+', 'label=value', { + label: 'label', + REDUCE: TIME_SERIES_REDUCERS.AVG + }) + ]); + + assert.deepStrictEqual( + reply, + Object.create(null, { + 'label=value': { + configurable: true, + enumerable: true, + value: { + labels: Object.create(null, { + label: { + configurable: true, + enumerable: true, + value: 'value' + } + }), + sources: ['key'], + samples: [{ + timestamp: 0, + value: 0 + }] + } + } + }) + ); + }, GLOBAL.SERVERS.OPEN); +}); diff --git a/packages/time-series/lib/commands/MREVRANGE_WITHLABELS_GROUPBY.ts b/packages/time-series/lib/commands/MREVRANGE_WITHLABELS_GROUPBY.ts new file mode 100644 index 00000000000..b3d49643fd0 --- /dev/null +++ b/packages/time-series/lib/commands/MREVRANGE_WITHLABELS_GROUPBY.ts @@ -0,0 +1,9 @@ +import { Command } from '@redis/client/dist/lib/RESP/types'; +import MRANGE_WITHLABELS_GROUPBY, { createMRangeWithLabelsGroupByTransformArguments } from './MRANGE_WITHLABELS_GROUPBY'; + +export default { + FIRST_KEY_INDEX: MRANGE_WITHLABELS_GROUPBY.FIRST_KEY_INDEX, + IS_READ_ONLY: MRANGE_WITHLABELS_GROUPBY.IS_READ_ONLY, + transformArguments: createMRangeWithLabelsGroupByTransformArguments('TS.MREVRANGE'), + transformReply: MRANGE_WITHLABELS_GROUPBY.transformReply, +} as const satisfies Command; diff --git a/packages/time-series/lib/commands/QUERYINDEX.spec.ts b/packages/time-series/lib/commands/QUERYINDEX.spec.ts index 010c5c8f639..74f201bb74b 100644 --- a/packages/time-series/lib/commands/QUERYINDEX.spec.ts +++ b/packages/time-series/lib/commands/QUERYINDEX.spec.ts @@ -1,34 +1,34 @@ -import { strict as assert } from 'assert'; +import { strict as assert } from 'node:assert'; import testUtils, { GLOBAL } from '../test-utils'; -import { transformArguments } from './QUERYINDEX'; +import QUERYINDEX from './QUERYINDEX'; -describe('QUERYINDEX', () => { - describe('transformArguments', () => { - it('single filter', () => { - assert.deepEqual( - transformArguments('*'), - ['TS.QUERYINDEX', '*'] - ); - }); - - it('multiple filters', () => { - assert.deepEqual( - transformArguments(['a=1', 'b=2']), - ['TS.QUERYINDEX', 'a=1', 'b=2'] - ); - }); +describe('TS.QUERYINDEX', () => { + describe('transformArguments', () => { + it('single filter', () => { + assert.deepEqual( + QUERYINDEX.transformArguments('*'), + ['TS.QUERYINDEX', '*'] + ); }); - testUtils.testWithClient('client.ts.queryIndex', async client => { - await client.ts.create('key', { - LABELS: { - label: 'value' - } - }); + it('multiple filters', () => { + assert.deepEqual( + QUERYINDEX.transformArguments(['a=1', 'b=2']), + ['TS.QUERYINDEX', 'a=1', 'b=2'] + ); + }); + }); + + testUtils.testWithClient('client.ts.queryIndex', async client => { + const [, reply] = await Promise.all([ + client.ts.create('key', { + LABELS: { + label: 'value' + } + }), + client.ts.queryIndex('label=value') + ]); - assert.deepEqual( - await client.ts.queryIndex('label=value'), - ['key'] - ); - }, GLOBAL.SERVERS.OPEN); + assert.deepEqual(reply, ['key']); + }, GLOBAL.SERVERS.OPEN); }); diff --git a/packages/time-series/lib/commands/QUERYINDEX.ts b/packages/time-series/lib/commands/QUERYINDEX.ts index 46eb5647040..86c2a3c5a7e 100644 --- a/packages/time-series/lib/commands/QUERYINDEX.ts +++ b/packages/time-series/lib/commands/QUERYINDEX.ts @@ -1,11 +1,14 @@ -import { RedisCommandArguments } from '@redis/client/dist/lib/commands'; -import { pushVerdictArguments } from '@redis/client/dist/lib/commands/generic-transformers'; -import { Filter } from '.'; +import { ArrayReply, BlobStringReply, SetReply, Command } from '@redis/client/dist/lib/RESP/types'; +import { RedisVariadicArgument, pushVariadicArguments } from '@redis/client/dist/lib/commands/generic-transformers'; -export const IS_READ_ONLY = true; - -export function transformArguments(filter: Filter): RedisCommandArguments { - return pushVerdictArguments(['TS.QUERYINDEX'], filter); -} - -export declare function transformReply(): Array; +export default { + FIRST_KEY_INDEX: undefined, + IS_READ_ONLY: true, + transformArguments(filter: RedisVariadicArgument) { + return pushVariadicArguments(['TS.QUERYINDEX'], filter); + }, + transformReply: { + 2: undefined as unknown as () => ArrayReply, + 3: undefined as unknown as () => SetReply + } +} as const satisfies Command; diff --git a/packages/time-series/lib/commands/RANGE.spec.ts b/packages/time-series/lib/commands/RANGE.spec.ts index 1e6a9958806..bc5d38d740b 100644 --- a/packages/time-series/lib/commands/RANGE.spec.ts +++ b/packages/time-series/lib/commands/RANGE.spec.ts @@ -1,38 +1,40 @@ -import { strict as assert } from 'assert'; +import { strict as assert } from 'node:assert'; import testUtils, { GLOBAL } from '../test-utils'; -import { transformArguments } from './RANGE'; -import { TimeSeriesAggregationType } from '.'; +import RANGE from './RANGE'; +import { TIME_SERIES_AGGREGATION_TYPE } from './CREATERULE'; -describe('RANGE', () => { - it('transformArguments', () => { - assert.deepEqual( - transformArguments('key', '-', '+', { - FILTER_BY_TS: [0], - FILTER_BY_VALUE: { - min: 1, - max: 2 - }, - COUNT: 1, - ALIGN: '-', - AGGREGATION: { - type: TimeSeriesAggregationType.AVERAGE, - timeBucket: 1 - } - }), - ['TS.RANGE', 'key', '-', '+', 'FILTER_BY_TS', '0', 'FILTER_BY_VALUE', - '1', '2', 'COUNT', '1', 'ALIGN', '-', 'AGGREGATION', 'AVG', '1'] - ); - }); +describe('TS.RANGE', () => { + it('transformArguments', () => { + assert.deepEqual( + RANGE.transformArguments('key', '-', '+', { + FILTER_BY_TS: [0], + FILTER_BY_VALUE: { + min: 1, + max: 2 + }, + COUNT: 1, + ALIGN: '-', + AGGREGATION: { + type: TIME_SERIES_AGGREGATION_TYPE.AVG, + timeBucket: 1 + } + }), + [ + 'TS.RANGE', 'key', '-', '+', 'FILTER_BY_TS', '0', 'FILTER_BY_VALUE', + '1', '2', 'COUNT', '1', 'ALIGN', '-', 'AGGREGATION', 'AVG', '1' + ] + ); + }); - testUtils.testWithClient('client.ts.range', async client => { - await client.ts.add('key', 1, 2); + testUtils.testWithClient('client.ts.range', async client => { + const [, reply] = await Promise.all([ + client.ts.add('key', 1, 2), + client.ts.range('key', '-', '+') + ]); - assert.deepEqual( - await client.ts.range('key', '-', '+'), - [{ - timestamp: 1, - value: 2 - }] - ); - }, GLOBAL.SERVERS.OPEN); + assert.deepEqual(reply, [{ + timestamp: 1, + value: 2 + }]); + }, GLOBAL.SERVERS.OPEN); }); diff --git a/packages/time-series/lib/commands/RANGE.ts b/packages/time-series/lib/commands/RANGE.ts index e6ce256bbe6..084073fefe6 100644 --- a/packages/time-series/lib/commands/RANGE.ts +++ b/packages/time-series/lib/commands/RANGE.ts @@ -1,24 +1,119 @@ -import { RedisCommandArguments } from '@redis/client/dist/lib/commands'; -import { RangeOptions, Timestamp, pushRangeArguments, SampleRawReply, SampleReply, transformRangeReply } from '.'; - -export const FIRST_KEY_INDEX = 1; - -export const IS_READ_ONLY = true; - -export function transformArguments( - key: string, - fromTimestamp: Timestamp, - toTimestamp: Timestamp, - options?: RangeOptions -): RedisCommandArguments { - return pushRangeArguments( - ['TS.RANGE', key], - fromTimestamp, - toTimestamp, - options +import { CommandArguments, RedisArgument, Command } from '@redis/client/dist/lib/RESP/types'; +import { Timestamp, transformTimestampArgument, SamplesRawReply, transformSamplesReply } from '.'; +import { TimeSeriesAggregationType } from './CREATERULE'; +import { Resp2Reply } from '@redis/client/dist/lib/RESP/types'; + +export const TIME_SERIES_BUCKET_TIMESTAMP = { + LOW: '-', + MIDDLE: '~', + END: '+' +}; + +export type TimeSeriesBucketTimestamp = typeof TIME_SERIES_BUCKET_TIMESTAMP[keyof typeof TIME_SERIES_BUCKET_TIMESTAMP]; + +export interface TsRangeOptions { + LATEST?: boolean; + FILTER_BY_TS?: Array; + FILTER_BY_VALUE?: { + min: number; + max: number; + }; + COUNT?: number; + ALIGN?: Timestamp; + AGGREGATION?: { + ALIGN?: Timestamp; + type: TimeSeriesAggregationType; + timeBucket: Timestamp; + BUCKETTIMESTAMP?: TimeSeriesBucketTimestamp; + EMPTY?: boolean; + }; +} + +export function pushRangeArguments( + args: CommandArguments, + fromTimestamp: Timestamp, + toTimestamp: Timestamp, + options?: TsRangeOptions +) { + args.push( + transformTimestampArgument(fromTimestamp), + transformTimestampArgument(toTimestamp) + ); + + if (options?.LATEST) { + args.push('LATEST'); + } + + if (options?.FILTER_BY_TS) { + args.push('FILTER_BY_TS'); + for (const timestamp of options.FILTER_BY_TS) { + args.push(transformTimestampArgument(timestamp)); + } + } + + if (options?.FILTER_BY_VALUE) { + args.push( + 'FILTER_BY_VALUE', + options.FILTER_BY_VALUE.min.toString(), + options.FILTER_BY_VALUE.max.toString() ); + } + + if (options?.COUNT !== undefined) { + args.push('COUNT', options.COUNT.toString()); + } + + if (options?.AGGREGATION) { + if (options?.ALIGN !== undefined) { + args.push('ALIGN', transformTimestampArgument(options.ALIGN)); + } + + args.push( + 'AGGREGATION', + options.AGGREGATION.type, + transformTimestampArgument(options.AGGREGATION.timeBucket) + ); + + if (options.AGGREGATION.BUCKETTIMESTAMP) { + args.push( + 'BUCKETTIMESTAMP', + options.AGGREGATION.BUCKETTIMESTAMP + ); + } + + if (options.AGGREGATION.EMPTY) { + args.push('EMPTY'); + } + } + + return args; } -export function transformReply(reply: Array): Array { - return transformRangeReply(reply); +export function transformRangeArguments( + command: RedisArgument, + key: RedisArgument, + fromTimestamp: Timestamp, + toTimestamp: Timestamp, + options?: TsRangeOptions +) { + return pushRangeArguments( + [command, key], + fromTimestamp, + toTimestamp, + options + ); } + +export default { + FIRST_KEY_INDEX: 1, + IS_READ_ONLY: true, + transformArguments: transformRangeArguments.bind(undefined, 'TS.RANGE'), + transformReply: { + 2(reply: Resp2Reply) { + return transformSamplesReply[2](reply); + }, + 3(reply: SamplesRawReply) { + return transformSamplesReply[3](reply); + } + } +} as const satisfies Command; diff --git a/packages/time-series/lib/commands/REVRANGE.spec.ts b/packages/time-series/lib/commands/REVRANGE.spec.ts index ffd90268c81..c371e8306b0 100644 --- a/packages/time-series/lib/commands/REVRANGE.spec.ts +++ b/packages/time-series/lib/commands/REVRANGE.spec.ts @@ -1,106 +1,40 @@ -import { strict as assert } from 'assert'; +import { strict as assert } from 'node:assert'; import testUtils, { GLOBAL } from '../test-utils'; -import { transformArguments } from './REVRANGE'; -import { TimeSeriesAggregationType } from '.'; - -describe('REVRANGE', () => { - describe('transformArguments', () => { - it('without options', () => { - assert.deepEqual( - transformArguments('key', '-', '+'), - ['TS.REVRANGE', 'key', '-', '+'] - ); - }); - - it('with FILTER_BY_TS', () => { - assert.deepEqual( - transformArguments('key', '-', '+', { - FILTER_BY_TS: [0] - }), - ['TS.REVRANGE', 'key', '-', '+', 'FILTER_BY_TS', '0'] - ); - }); - - it('with FILTER_BY_VALUE', () => { - assert.deepEqual( - transformArguments('key', '-', '+', { - FILTER_BY_VALUE: { - min: 1, - max: 2 - } - }), - ['TS.REVRANGE', 'key', '-', '+', 'FILTER_BY_VALUE', '1', '2'] - ); - }); - - it('with COUNT', () => { - assert.deepEqual( - transformArguments('key', '-', '+', { - COUNT: 1 - }), - ['TS.REVRANGE', 'key', '-', '+', 'COUNT', '1'] - ); - }); - - it('with ALIGN', () => { - assert.deepEqual( - transformArguments('key', '-', '+', { - ALIGN: '-' - }), - ['TS.REVRANGE', 'key', '-', '+', 'ALIGN', '-'] - ); - }); - - it('with AGGREGATION', () => { - assert.deepEqual( - transformArguments('key', '-', '+', { - AGGREGATION: { - type: TimeSeriesAggregationType.AVERAGE, - timeBucket: 1 - } - }), - ['TS.REVRANGE', 'key', '-', '+', 'AGGREGATION', 'AVG', '1'] - ); - }); - - it('with FILTER_BY_TS, FILTER_BY_VALUE, COUNT, ALIGN, AGGREGATION', () => { - assert.deepEqual( - transformArguments('key', '-', '+', { - FILTER_BY_TS: [0], - FILTER_BY_VALUE: { - min: 1, - max: 2 - }, - COUNT: 1, - ALIGN: '-', - AGGREGATION: { - type: TimeSeriesAggregationType.AVERAGE, - timeBucket: 1 - } - }), - [ - 'TS.REVRANGE', 'key', '-', '+', 'FILTER_BY_TS', '0', 'FILTER_BY_VALUE', - '1', '2', 'COUNT', '1', 'ALIGN', '-', 'AGGREGATION', 'AVG', '1' - ] - ); - }); - }); - - testUtils.testWithClient('client.ts.revRange', async client => { - await Promise.all([ - client.ts.add('key', 0, 1), - client.ts.add('key', 1, 2) - ]); - - assert.deepEqual( - await client.ts.revRange('key', '-', '+'), - [{ - timestamp: 1, - value: 2 - }, { - timestamp: 0, - value: 1 - }] - ); - }, GLOBAL.SERVERS.OPEN); +import REVRANGE from './REVRANGE'; +import { TIME_SERIES_AGGREGATION_TYPE } from '../index'; + +describe('TS.REVRANGE', () => { + it('transformArguments', () => { + assert.deepEqual( + REVRANGE.transformArguments('key', '-', '+', { + FILTER_BY_TS: [0], + FILTER_BY_VALUE: { + min: 1, + max: 2 + }, + COUNT: 1, + ALIGN: '-', + AGGREGATION: { + type: TIME_SERIES_AGGREGATION_TYPE.AVG, + timeBucket: 1 + } + }), + [ + 'TS.REVRANGE', 'key', '-', '+', 'FILTER_BY_TS', '0', 'FILTER_BY_VALUE', + '1', '2', 'COUNT', '1', 'ALIGN', '-', 'AGGREGATION', 'AVG', '1' + ] + ); + }); + + testUtils.testWithClient('client.ts.revRange', async client => { + const [, reply] = await Promise.all([ + client.ts.add('key', 1, 2), + client.ts.revRange('key', '-', '+') + ]); + + assert.deepEqual(reply, [{ + timestamp: 1, + value: 2 + }]); + }, GLOBAL.SERVERS.OPEN); }); diff --git a/packages/time-series/lib/commands/REVRANGE.ts b/packages/time-series/lib/commands/REVRANGE.ts index 9179756b5de..1097223080b 100644 --- a/packages/time-series/lib/commands/REVRANGE.ts +++ b/packages/time-series/lib/commands/REVRANGE.ts @@ -1,24 +1,9 @@ -import { RedisCommandArguments } from '@redis/client/dist/lib/commands'; -import { RangeOptions, Timestamp, pushRangeArguments, SampleRawReply, SampleReply, transformRangeReply } from '.'; - -export const FIRST_KEY_INDEX = 1; - -export const IS_READ_ONLY = true; - -export function transformArguments( - key: string, - fromTimestamp: Timestamp, - toTimestamp: Timestamp, - options?: RangeOptions -): RedisCommandArguments { - return pushRangeArguments( - ['TS.REVRANGE', key], - fromTimestamp, - toTimestamp, - options - ); -} - -export function transformReply(reply: Array): Array { - return transformRangeReply(reply); -} +import { Command } from '@redis/client/dist/lib/RESP/types'; +import RANGE, { transformRangeArguments } from './RANGE'; + +export default { + FIRST_KEY_INDEX: RANGE.FIRST_KEY_INDEX, + IS_READ_ONLY: RANGE.IS_READ_ONLY, + transformArguments: transformRangeArguments.bind(undefined, 'TS.REVRANGE'), + transformReply: RANGE.transformReply +} as const satisfies Command; diff --git a/packages/time-series/lib/commands/index.spec.ts b/packages/time-series/lib/commands/index.spec.ts index a29eefe860a..ff7f4afad68 100644 --- a/packages/time-series/lib/commands/index.spec.ts +++ b/packages/time-series/lib/commands/index.spec.ts @@ -1,439 +1,423 @@ -import { RedisCommandArguments } from '@redis/client/dist/lib/commands'; -import { strict as assert } from 'assert'; -import { - transformTimestampArgument, - pushRetentionArgument, - TimeSeriesEncoding, - pushEncodingArgument, - pushChunkSizeArgument, - pushDuplicatePolicy, - pushLabelsArgument, - transformIncrDecrArguments, - transformSampleReply, - TimeSeriesAggregationType, - pushRangeArguments, - pushMRangeGroupByArguments, - TimeSeriesReducers, - pushFilterArgument, - pushMRangeArguments, - pushWithLabelsArgument, - pushMRangeWithLabelsArguments, - transformRangeReply, - transformMRangeReply, - transformMRangeWithLabelsReply, - TimeSeriesDuplicatePolicies, - pushLatestArgument, - TimeSeriesBucketTimestamp -} from '.'; - -describe('transformTimestampArgument', () => { - it('number', () => { - assert.equal( - transformTimestampArgument(0), - '0' - ); - }); - - it('Date', () => { - assert.equal( - transformTimestampArgument(new Date(0)), - '0' - ); - }); - - it('string', () => { - assert.equal( - transformTimestampArgument('*'), - '*' - ); - }); -}); - -function testOptionalArgument(fn: (args: RedisCommandArguments) => unknown): void { - it('undefined', () => { - assert.deepEqual( - fn([]), - [] - ); - }); -} - -describe('pushRetentionArgument', () => { - testOptionalArgument(pushRetentionArgument); - - it('number', () => { - assert.deepEqual( - pushRetentionArgument([], 1), - ['RETENTION', '1'] - ); - }); -}); - -describe('pushEncodingArgument', () => { - testOptionalArgument(pushEncodingArgument); - - it('UNCOMPRESSED', () => { - assert.deepEqual( - pushEncodingArgument([], TimeSeriesEncoding.UNCOMPRESSED), - ['ENCODING', 'UNCOMPRESSED'] - ); - }); -}); - -describe('pushChunkSizeArgument', () => { - testOptionalArgument(pushChunkSizeArgument); - - it('number', () => { - assert.deepEqual( - pushChunkSizeArgument([], 1), - ['CHUNK_SIZE', '1'] - ); - }); -}); - -describe('pushDuplicatePolicy', () => { - testOptionalArgument(pushDuplicatePolicy); - - it('BLOCK', () => { - assert.deepEqual( - pushDuplicatePolicy([], TimeSeriesDuplicatePolicies.BLOCK), - ['DUPLICATE_POLICY', 'BLOCK'] - ); - }); -}); - -describe('pushLabelsArgument', () => { - testOptionalArgument(pushLabelsArgument); - - it("{ label: 'value' }", () => { - assert.deepEqual( - pushLabelsArgument([], { label: 'value' }), - ['LABELS', 'label', 'value'] - ); - }); -}); - -describe('transformIncrDecrArguments', () => { - it('without options', () => { - assert.deepEqual( - transformIncrDecrArguments('TS.INCRBY', 'key', 1), - ['TS.INCRBY', 'key', '1'] - ); - }); - - it('with TIMESTAMP', () => { - assert.deepEqual( - transformIncrDecrArguments('TS.INCRBY', 'key', 1, { - TIMESTAMP: '*' - }), - ['TS.INCRBY', 'key', '1', 'TIMESTAMP', '*'] - ); - }); - - it('with UNCOMPRESSED', () => { - assert.deepEqual( - transformIncrDecrArguments('TS.INCRBY', 'key', 1, { - UNCOMPRESSED: true - }), - ['TS.INCRBY', 'key', '1', 'UNCOMPRESSED'] - ); - }); - - it('with UNCOMPRESSED false', () => { - assert.deepEqual( - transformIncrDecrArguments('TS.INCRBY', 'key', 1, { - UNCOMPRESSED: false - }), - ['TS.INCRBY', 'key', '1'] - ); - }); -}); - -it('transformSampleReply', () => { - assert.deepEqual( - transformSampleReply([1, '1.1']), - { - timestamp: 1, - value: 1.1 - } - ); -}); - -describe('pushRangeArguments', () => { - it('without options', () => { - assert.deepEqual( - pushRangeArguments([], '-', '+'), - ['-', '+'] - ); - }); - - describe('with FILTER_BY_TS', () => { - it('string', () => { - assert.deepEqual( - pushRangeArguments([], '-', '+', { - FILTER_BY_TS: ['ts'] - }), - ['-', '+', 'FILTER_BY_TS', 'ts'] - ); - }); - - it('Array', () => { - assert.deepEqual( - pushRangeArguments([], '-', '+', { - FILTER_BY_TS: ['1', '2'] - }), - ['-', '+', 'FILTER_BY_TS', '1', '2'] - ); - }); - }); - - it('with FILTER_BY_VALUE', () => { - assert.deepEqual( - pushRangeArguments([], '-', '+', { - FILTER_BY_VALUE: { - min: 1, - max: 2 - } - }), - ['-', '+', 'FILTER_BY_VALUE', '1', '2'] - ); - }); - - it('with COUNT', () => { - assert.deepEqual( - pushRangeArguments([], '-', '+', { - COUNT: 1 - }), - ['-', '+', 'COUNT', '1'] - ); - }); - - it('with ALIGN', () => { - assert.deepEqual( - pushRangeArguments([], '-', '+', { - ALIGN: 1 - }), - ['-', '+', 'ALIGN', '1'] - ); - }); - - describe('with AGGREGATION', () => { - it('without options', () => { - assert.deepEqual( - pushRangeArguments([], '-', '+', { - AGGREGATION: { - type: TimeSeriesAggregationType.FIRST, - timeBucket: 1 - } - }), - ['-', '+', 'AGGREGATION', 'FIRST', '1'] - ); - }); - - it('with BUCKETTIMESTAMP', () => { - assert.deepEqual( - pushRangeArguments([], '-', '+', { - AGGREGATION: { - type: TimeSeriesAggregationType.FIRST, - timeBucket: 1, - BUCKETTIMESTAMP: TimeSeriesBucketTimestamp.LOW - } - }), - ['-', '+', 'AGGREGATION', 'FIRST', '1', 'BUCKETTIMESTAMP', '-'] - ); - }); - - it('with BUCKETTIMESTAMP', () => { - assert.deepEqual( - pushRangeArguments([], '-', '+', { - AGGREGATION: { - type: TimeSeriesAggregationType.FIRST, - timeBucket: 1, - EMPTY: true - } - }), - ['-', '+', 'AGGREGATION', 'FIRST', '1', 'EMPTY'] - ); - }); - }); - - it('with FILTER_BY_TS, FILTER_BY_VALUE, COUNT, ALIGN, AGGREGATION', () => { - assert.deepEqual( - pushRangeArguments([], '-', '+', { - FILTER_BY_TS: ['ts'], - FILTER_BY_VALUE: { - min: 1, - max: 2 - }, - COUNT: 1, - ALIGN: 1, - AGGREGATION: { - type: TimeSeriesAggregationType.FIRST, - timeBucket: 1, - BUCKETTIMESTAMP: TimeSeriesBucketTimestamp.LOW, - EMPTY: true - } - }), - ['-', '+', 'FILTER_BY_TS', 'ts', 'FILTER_BY_VALUE', '1', '2', - 'COUNT', '1', 'ALIGN', '1', 'AGGREGATION', 'FIRST', '1', 'BUCKETTIMESTAMP', '-', 'EMPTY'] - ); - }); -}); - -describe('pushMRangeGroupByArguments', () => { - it('undefined', () => { - assert.deepEqual( - pushMRangeGroupByArguments([]), - [] - ); - }); - - it('with GROUPBY', () => { - assert.deepEqual( - pushMRangeGroupByArguments([], { - label: 'label', - reducer: TimeSeriesReducers.MAXIMUM - }), - ['GROUPBY', 'label', 'REDUCE', 'MAX'] - ); - }); -}); - -describe('pushFilterArgument', () => { - it('string', () => { - assert.deepEqual( - pushFilterArgument([], 'label=value'), - ['FILTER', 'label=value'] - ); - }); - - it('Array', () => { - assert.deepEqual( - pushFilterArgument([], ['1=1', '2=2']), - ['FILTER', '1=1', '2=2'] - ); - }); -}); - -describe('pushMRangeArguments', () => { - it('without options', () => { - assert.deepEqual( - pushMRangeArguments([], '-', '+', 'label=value'), - ['-', '+', 'FILTER', 'label=value'] - ); - }); - - it('with GROUPBY', () => { - assert.deepEqual( - pushMRangeArguments([], '-', '+', 'label=value', { - GROUPBY: { - label: 'label', - reducer: TimeSeriesReducers.MAXIMUM - } - }), - ['-', '+', 'FILTER', 'label=value', 'GROUPBY', 'label', 'REDUCE', 'MAX'] - ); - }); -}); - -describe('pushWithLabelsArgument', () => { - it('without selected labels', () => { - assert.deepEqual( - pushWithLabelsArgument([]), - ['WITHLABELS'] - ); - }); - - it('with selected labels', () => { - assert.deepEqual( - pushWithLabelsArgument([], ['label']), - ['SELECTED_LABELS', 'label'] - ); - }); -}); - -it('pushMRangeWithLabelsArguments', () => { - assert.deepEqual( - pushMRangeWithLabelsArguments([], '-', '+', 'label=value'), - ['-', '+', 'WITHLABELS', 'FILTER', 'label=value'] - ); -}); - -it('transformRangeReply', () => { - assert.deepEqual( - transformRangeReply([[1, '1.1'], [2, '2.2']]), - [{ - timestamp: 1, - value: 1.1 - }, { - timestamp: 2, - value: 2.2 - }] - ); -}); - -describe('transformMRangeReply', () => { - assert.deepEqual( - transformMRangeReply([[ - 'key', - [], - [[1, '1.1'], [2, '2.2']] - ]]), - [{ - key: 'key', - samples: [{ - timestamp: 1, - value: 1.1 - }, { - timestamp: 2, - value: 2.2 - }] - }] - ); -}); - -describe('transformMRangeWithLabelsReply', () => { - assert.deepEqual( - transformMRangeWithLabelsReply([[ - 'key', - [['label', 'value']], - [[1, '1.1'], [2, '2.2']] - ]]), - [{ - key: 'key', - labels: { - label: 'value' - }, - samples: [{ - timestamp: 1, - value: 1.1 - }, { - timestamp: 2, - value: 2.2 - }] - }] - ); -}); - -describe('pushLatestArgument', () => { - it('undefined', () => { - assert.deepEqual( - pushLatestArgument([]), - [] - ); - }); - - it('false', () => { - assert.deepEqual( - pushLatestArgument([], false), - [] - ); - }); - - it('true', () => { - assert.deepEqual( - pushLatestArgument([], true), - ['LATEST'] - ); - }); -}) +// import { RedisCommandArguments } from '@redis/client/dist/lib/commands'; +// import { strict as assert } from 'node:assert'; +// import { +// transformTimestampArgument, +// pushRetentionArgument, +// TimeSeriesEncoding, +// pushEncodingArgument, +// pushChunkSizeArgument, +// pushDuplicatePolicy, +// pushLabelsArgument, +// transformIncrDecrArguments, +// transformSampleReply, +// TimeSeriesAggregationType, +// pushRangeArguments, +// pushMRangeGroupByArguments, +// TimeSeriesReducers, +// pushFilterArgument, +// pushMRangeArguments, +// pushWithLabelsArgument, +// pushMRangeWithLabelsArguments, +// transformRangeReply, +// transformMRangeReply, +// transformMRangeWithLabelsReply, +// TimeSeriesDuplicatePolicies, +// pushLatestArgument, +// TimeSeriesBucketTimestamp +// } from '.'; + +// describe('transformTimestampArgument', () => { +// it('number', () => { +// assert.equal( +// transformTimestampArgument(0), +// '0' +// ); +// }); + +// it('Date', () => { +// assert.equal( +// transformTimestampArgument(new Date(0)), +// '0' +// ); +// }); + +// it('string', () => { +// assert.equal( +// transformTimestampArgument('*'), +// '*' +// ); +// }); +// }); + +// function testOptionalArgument(fn: (args: RedisCommandArguments) => unknown): void { +// it('undefined', () => { +// assert.deepEqual( +// fn([]), +// [] +// ); +// }); +// } + +// describe('pushRetentionArgument', () => { +// testOptionalArgument(pushRetentionArgument); + +// it('number', () => { +// assert.deepEqual( +// pushRetentionArgument([], 1), +// ['RETENTION', '1'] +// ); +// }); +// }); + +// describe('pushEncodingArgument', () => { +// testOptionalArgument(pushEncodingArgument); + +// it('UNCOMPRESSED', () => { +// assert.deepEqual( +// pushEncodingArgument([], TimeSeriesEncoding.UNCOMPRESSED), +// ['ENCODING', 'UNCOMPRESSED'] +// ); +// }); +// }); + +// describe('pushChunkSizeArgument', () => { +// testOptionalArgument(pushChunkSizeArgument); + +// it('number', () => { +// assert.deepEqual( +// pushChunkSizeArgument([], 1), +// ['CHUNK_SIZE', '1'] +// ); +// }); +// }); + +// describe('pushDuplicatePolicy', () => { +// testOptionalArgument(pushDuplicatePolicy); + +// it('BLOCK', () => { +// assert.deepEqual( +// pushDuplicatePolicy([], TimeSeriesDuplicatePolicies.BLOCK), +// ['DUPLICATE_POLICY', 'BLOCK'] +// ); +// }); +// }); + +// describe('pushLabelsArgument', () => { +// testOptionalArgument(pushLabelsArgument); + +// it("{ label: 'value' }", () => { +// assert.deepEqual( +// pushLabelsArgument([], { label: 'value' }), +// ['LABELS', 'label', 'value'] +// ); +// }); +// }); + +// describe('transformIncrDecrArguments', () => { +// it('without options', () => { +// assert.deepEqual( +// transformIncrDecrArguments('TS.INCRBY', 'key', 1), +// ['TS.INCRBY', 'key', '1'] +// ); +// }); + +// it('with TIMESTAMP', () => { +// assert.deepEqual( +// transformIncrDecrArguments('TS.INCRBY', 'key', 1, { +// TIMESTAMP: '*' +// }), +// ['TS.INCRBY', 'key', '1', 'TIMESTAMP', '*'] +// ); +// }); + +// it('with UNCOMPRESSED', () => { +// assert.deepEqual( +// transformIncrDecrArguments('TS.INCRBY', 'key', 1, { +// UNCOMPRESSED: true +// }), +// ['TS.INCRBY', 'key', '1', 'UNCOMPRESSED'] +// ); +// }); + +// it('with UNCOMPRESSED false', () => { +// assert.deepEqual( +// transformIncrDecrArguments('TS.INCRBY', 'key', 1, { +// UNCOMPRESSED: false +// }), +// ['TS.INCRBY', 'key', '1'] +// ); +// }); +// }); + +// it('transformSampleReply', () => { +// assert.deepEqual( +// transformSampleReply([1, '1.1']), +// { +// timestamp: 1, +// value: 1.1 +// } +// ); +// }); + +// describe('pushRangeArguments', () => { +// it('without options', () => { +// assert.deepEqual( +// pushRangeArguments([], '-', '+'), +// ['-', '+'] +// ); +// }); + +// describe('with FILTER_BY_TS', () => { +// it('string', () => { +// assert.deepEqual( +// pushRangeArguments([], '-', '+', { +// FILTER_BY_TS: ['ts'] +// }), +// ['-', '+', 'FILTER_BY_TS', 'ts'] +// ); +// }); + +// it('Array', () => { +// assert.deepEqual( +// pushRangeArguments([], '-', '+', { +// FILTER_BY_TS: ['1', '2'] +// }), +// ['-', '+', 'FILTER_BY_TS', '1', '2'] +// ); +// }); +// }); + +// it('with FILTER_BY_VALUE', () => { +// assert.deepEqual( +// pushRangeArguments([], '-', '+', { +// FILTER_BY_VALUE: { +// min: 1, +// max: 2 +// } +// }), +// ['-', '+', 'FILTER_BY_VALUE', '1', '2'] +// ); +// }); + +// it('with COUNT', () => { +// assert.deepEqual( +// pushRangeArguments([], '-', '+', { +// COUNT: 1 +// }), +// ['-', '+', 'COUNT', '1'] +// ); +// }); + +// it('with ALIGN', () => { +// assert.deepEqual( +// pushRangeArguments([], '-', '+', { +// ALIGN: 1 +// }), +// ['-', '+', 'ALIGN', '1'] +// ); +// }); + +// describe('with AGGREGATION', () => { +// it('without options', () => { +// assert.deepEqual( +// pushRangeArguments([], '-', '+', { +// AGGREGATION: { +// type: TimeSeriesAggregationType.FIRST, +// timeBucket: 1 +// } +// }), +// ['-', '+', 'AGGREGATION', 'FIRST', '1'] +// ); +// }); + +// it('with BUCKETTIMESTAMP', () => { +// assert.deepEqual( +// pushRangeArguments([], '-', '+', { +// AGGREGATION: { +// type: TimeSeriesAggregationType.FIRST, +// timeBucket: 1, +// BUCKETTIMESTAMP: TimeSeriesBucketTimestamp.LOW +// } +// }), +// ['-', '+', 'AGGREGATION', 'FIRST', '1', 'BUCKETTIMESTAMP', '-'] +// ); +// }); + +// it('with BUCKETTIMESTAMP', () => { +// assert.deepEqual( +// pushRangeArguments([], '-', '+', { +// AGGREGATION: { +// type: TimeSeriesAggregationType.FIRST, +// timeBucket: 1, +// EMPTY: true +// } +// }), +// ['-', '+', 'AGGREGATION', 'FIRST', '1', 'EMPTY'] +// ); +// }); +// }); + +// it('with FILTER_BY_TS, FILTER_BY_VALUE, COUNT, ALIGN, AGGREGATION', () => { +// assert.deepEqual( +// pushRangeArguments([], '-', '+', { +// FILTER_BY_TS: ['ts'], +// FILTER_BY_VALUE: { +// min: 1, +// max: 2 +// }, +// COUNT: 1, +// ALIGN: 1, +// AGGREGATION: { +// type: TimeSeriesAggregationType.FIRST, +// timeBucket: 1, +// BUCKETTIMESTAMP: TimeSeriesBucketTimestamp.LOW, +// EMPTY: true +// } +// }), +// ['-', '+', 'FILTER_BY_TS', 'ts', 'FILTER_BY_VALUE', '1', '2', +// 'COUNT', '1', 'ALIGN', '1', 'AGGREGATION', 'FIRST', '1', 'BUCKETTIMESTAMP', '-', 'EMPTY'] +// ); +// }); +// }); + +// describe('pushMRangeGroupByArguments', () => { +// it('undefined', () => { +// assert.deepEqual( +// pushMRangeGroupByArguments([]), +// [] +// ); +// }); + +// it('with GROUPBY', () => { +// assert.deepEqual( +// pushMRangeGroupByArguments([], { +// label: 'label', +// reducer: TimeSeriesReducers.MAXIMUM +// }), +// ['GROUPBY', 'label', 'REDUCE', 'MAX'] +// ); +// }); +// }); + +// describe('pushFilterArgument', () => { +// it('string', () => { +// assert.deepEqual( +// pushFilterArgument([], 'label=value'), +// ['FILTER', 'label=value'] +// ); +// }); + +// it('Array', () => { +// assert.deepEqual( +// pushFilterArgument([], ['1=1', '2=2']), +// ['FILTER', '1=1', '2=2'] +// ); +// }); +// }); + +// describe('pushMRangeArguments', () => { +// it('without options', () => { +// assert.deepEqual( +// pushMRangeArguments([], '-', '+', 'label=value'), +// ['-', '+', 'FILTER', 'label=value'] +// ); +// }); + +// it('with GROUPBY', () => { +// assert.deepEqual( +// pushMRangeArguments([], '-', '+', 'label=value', { +// GROUPBY: { +// label: 'label', +// reducer: TimeSeriesReducers.MAXIMUM +// } +// }), +// ['-', '+', 'FILTER', 'label=value', 'GROUPBY', 'label', 'REDUCE', 'MAX'] +// ); +// }); +// }); + +// it('pushMRangeWithLabelsArguments', () => { +// assert.deepEqual( +// pushMRangeWithLabelsArguments([], '-', '+', 'label=value'), +// ['-', '+', 'WITHLABELS', 'FILTER', 'label=value'] +// ); +// }); + +// it('transformRangeReply', () => { +// assert.deepEqual( +// transformRangeReply([[1, '1.1'], [2, '2.2']]), +// [{ +// timestamp: 1, +// value: 1.1 +// }, { +// timestamp: 2, +// value: 2.2 +// }] +// ); +// }); + +// describe('transformMRangeReply', () => { +// assert.deepEqual( +// transformMRangeReply([[ +// 'key', +// [], +// [[1, '1.1'], [2, '2.2']] +// ]]), +// [{ +// key: 'key', +// samples: [{ +// timestamp: 1, +// value: 1.1 +// }, { +// timestamp: 2, +// value: 2.2 +// }] +// }] +// ); +// }); + +// describe('transformMRangeWithLabelsReply', () => { +// assert.deepEqual( +// transformMRangeWithLabelsReply([[ +// 'key', +// [['label', 'value']], +// [[1, '1.1'], [2, '2.2']] +// ]]), +// [{ +// key: 'key', +// labels: { +// label: 'value' +// }, +// samples: [{ +// timestamp: 1, +// value: 1.1 +// }, { +// timestamp: 2, +// value: 2.2 +// }] +// }] +// ); +// }); + +// describe('pushLatestArgument', () => { +// it('undefined', () => { +// assert.deepEqual( +// pushLatestArgument([]), +// [] +// ); +// }); + +// it('false', () => { +// assert.deepEqual( +// pushLatestArgument([], false), +// [] +// ); +// }); + +// it('true', () => { +// assert.deepEqual( +// pushLatestArgument([], true), +// ['LATEST'] +// ); +// }); +// }) diff --git a/packages/time-series/lib/commands/index.ts b/packages/time-series/lib/commands/index.ts index ca382498060..5b9d97b6566 100644 --- a/packages/time-series/lib/commands/index.ts +++ b/packages/time-series/lib/commands/index.ts @@ -1,473 +1,399 @@ -import * as ADD from './ADD'; -import * as ALTER from './ALTER'; -import * as CREATE from './CREATE'; -import * as CREATERULE from './CREATERULE'; -import * as DECRBY from './DECRBY'; -import * as DEL from './DEL'; -import * as DELETERULE from './DELETERULE'; -import * as GET from './GET'; -import * as INCRBY from './INCRBY'; -import * as INFO_DEBUG from './INFO_DEBUG'; -import * as INFO from './INFO'; -import * as MADD from './MADD'; -import * as MGET from './MGET'; -import * as MGET_WITHLABELS from './MGET_WITHLABELS'; -import * as QUERYINDEX from './QUERYINDEX'; -import * as RANGE from './RANGE'; -import * as REVRANGE from './REVRANGE'; -import * as MRANGE from './MRANGE'; -import * as MRANGE_WITHLABELS from './MRANGE_WITHLABELS'; -import * as MREVRANGE from './MREVRANGE'; -import * as MREVRANGE_WITHLABELS from './MREVRANGE_WITHLABELS'; -import { RedisCommandArguments } from '@redis/client/dist/lib/commands'; -import { pushVerdictArguments } from '@redis/client/dist/lib/commands/generic-transformers'; +import type { DoubleReply, NumberReply, RedisArgument, RedisCommands, TuplesReply, UnwrapReply, Resp2Reply, ArrayReply, BlobStringReply, MapReply, NullReply, TypeMapping, ReplyUnion, RespType } from '@redis/client/lib/RESP/types'; +import ADD, { TsIgnoreOptions } from './ADD'; +import ALTER from './ALTER'; +import CREATE from './CREATE'; +import CREATERULE from './CREATERULE'; +import DECRBY from './DECRBY'; +import DEL from './DEL'; +import DELETERULE from './DELETERULE'; +import GET from './GET'; +import INCRBY from './INCRBY'; +import INFO_DEBUG from './INFO_DEBUG'; +import INFO from './INFO'; +import MADD from './MADD'; +import MGET_SELECTED_LABELS from './MGET_SELECTED_LABELS'; +import MGET_WITHLABELS from './MGET_WITHLABELS'; +import MGET from './MGET'; +import MRANGE_GROUPBY from './MRANGE_GROUPBY'; +import MRANGE_SELECTED_LABELS_GROUPBY from './MRANGE_SELECTED_LABELS_GROUPBY'; +import MRANGE_SELECTED_LABELS from './MRANGE_SELECTED_LABELS'; +import MRANGE_WITHLABELS_GROUPBY from './MRANGE_WITHLABELS_GROUPBY'; +import MRANGE_WITHLABELS from './MRANGE_WITHLABELS'; +import MRANGE from './MRANGE'; +import MREVRANGE_GROUPBY from './MREVRANGE_GROUPBY'; +import MREVRANGE_SELECTED_LABELS_GROUPBY from './MREVRANGE_SELECTED_LABELS_GROUPBY'; +import MREVRANGE_SELECTED_LABELS from './MREVRANGE_SELECTED_LABELS'; +import MREVRANGE_WITHLABELS_GROUPBY from './MREVRANGE_WITHLABELS_GROUPBY'; +import MREVRANGE_WITHLABELS from './MREVRANGE_WITHLABELS'; +import MREVRANGE from './MREVRANGE'; +import QUERYINDEX from './QUERYINDEX'; +import RANGE from './RANGE'; +import REVRANGE from './REVRANGE'; +import { RedisVariadicArgument, pushVariadicArguments } from '@redis/client/lib/commands/generic-transformers'; +import { RESP_TYPES } from '@redis/client/lib/RESP/decoder'; export default { - ADD, - add: ADD, - ALTER, - alter: ALTER, - CREATE, - create: CREATE, - CREATERULE, - createRule: CREATERULE, - DECRBY, - decrBy: DECRBY, - DEL, - del: DEL, - DELETERULE, - deleteRule: DELETERULE, - GET, - get: GET, - INCRBY, - incrBy: INCRBY, - INFO_DEBUG, - infoDebug: INFO_DEBUG, - INFO, - info: INFO, - MADD, - mAdd: MADD, - MGET, - mGet: MGET, - MGET_WITHLABELS, - mGetWithLabels: MGET_WITHLABELS, - QUERYINDEX, - queryIndex: QUERYINDEX, - RANGE, - range: RANGE, - REVRANGE, - revRange: REVRANGE, - MRANGE, - mRange: MRANGE, - MRANGE_WITHLABELS, - mRangeWithLabels: MRANGE_WITHLABELS, - MREVRANGE, - mRevRange: MREVRANGE, - MREVRANGE_WITHLABELS, - mRevRangeWithLabels: MREVRANGE_WITHLABELS -}; - -export enum TimeSeriesAggregationType { - AVG = 'AVG', - // @deprecated - AVERAGE = 'AVG', - FIRST = 'FIRST', - LAST = 'LAST', - MIN = 'MIN', - // @deprecated - MINIMUM = 'MIN', - MAX = 'MAX', - // @deprecated - MAXIMUM = 'MAX', - SUM = 'SUM', - RANGE = 'RANGE', - COUNT = 'COUNT', - STD_P = 'STD.P', - STD_S = 'STD.S', - VAR_P = 'VAR.P', - VAR_S = 'VAR.S', - TWA = 'TWA' + ADD, + add: ADD, + ALTER, + alter: ALTER, + CREATE, + create: CREATE, + CREATERULE, + createRule: CREATERULE, + DECRBY, + decrBy: DECRBY, + DEL, + del: DEL, + DELETERULE, + deleteRule: DELETERULE, + GET, + get: GET, + INCRBY, + incrBy: INCRBY, + INFO_DEBUG, + infoDebug: INFO_DEBUG, + INFO, + info: INFO, + MADD, + mAdd: MADD, + MGET_SELECTED_LABELS, + mGetSelectedLabels: MGET_SELECTED_LABELS, + MGET_WITHLABELS, + mGetWithLabels: MGET_WITHLABELS, + MGET, + mGet: MGET, + MRANGE_GROUPBY, + mRangeGroupBy: MRANGE_GROUPBY, + MRANGE_SELECTED_LABELS_GROUPBY, + mRangeSelectedLabelsGroupBy: MRANGE_SELECTED_LABELS_GROUPBY, + MRANGE_SELECTED_LABELS, + mRangeSelectedLabels: MRANGE_SELECTED_LABELS, + MRANGE_WITHLABELS_GROUPBY, + mRangeWithLabelsGroupBy: MRANGE_WITHLABELS_GROUPBY, + MRANGE_WITHLABELS, + mRangeWithLabels: MRANGE_WITHLABELS, + MRANGE, + mRange: MRANGE, + MREVRANGE_GROUPBY, + mRevRangeGroupBy: MREVRANGE_GROUPBY, + MREVRANGE_SELECTED_LABELS_GROUPBY, + mRevRangeSelectedLabelsGroupBy: MREVRANGE_SELECTED_LABELS_GROUPBY, + MREVRANGE_SELECTED_LABELS, + mRevRangeSelectedLabels: MREVRANGE_SELECTED_LABELS, + MREVRANGE_WITHLABELS_GROUPBY, + mRevRangeWithLabelsGroupBy: MREVRANGE_WITHLABELS_GROUPBY, + MREVRANGE_WITHLABELS, + mRevRangeWithLabels: MREVRANGE_WITHLABELS, + MREVRANGE, + mRevRange: MREVRANGE, + QUERYINDEX, + queryIndex: QUERYINDEX, + RANGE, + range: RANGE, + REVRANGE, + revRange: REVRANGE +} as const satisfies RedisCommands; + +export function pushIgnoreArgument(args: Array, ignore?: TsIgnoreOptions) { + if (ignore !== undefined) { + args.push('IGNORE', ignore.maxTimeDiff.toString(), ignore.maxValDiff.toString()); + } } -export enum TimeSeriesDuplicatePolicies { - BLOCK = 'BLOCK', - FIRST = 'FIRST', - LAST = 'LAST', - MIN = 'MIN', - MAX = 'MAX', - SUM = 'SUM' +export function pushRetentionArgument(args: Array, retention?: number) { + if (retention !== undefined) { + args.push('RETENTION', retention.toString()); + } } -export enum TimeSeriesReducers { - AVG = 'AVG', - SUM = 'SUM', - MIN = 'MIN', - // @deprecated - MINIMUM = 'MIN', - MAX = 'MAX', - // @deprecated - MAXIMUM = 'MAX', - RANGE = 'range', - COUNT = 'COUNT', - STD_P = 'STD.P', - STD_S = 'STD.S', - VAR_P = 'VAR.P', - VAR_S = 'VAR.S', -} +export const TIME_SERIES_ENCODING = { + COMPRESSED: 'COMPRESSED', + UNCOMPRESSED: 'UNCOMPRESSED' +} as const; -export type Timestamp = number | Date | string; +export type TimeSeriesEncoding = typeof TIME_SERIES_ENCODING[keyof typeof TIME_SERIES_ENCODING]; -export function transformTimestampArgument(timestamp: Timestamp): string { - if (typeof timestamp === 'string') return timestamp; - - return ( - typeof timestamp === 'number' ? - timestamp : - timestamp.getTime() - ).toString(); -} - -export function pushIgnoreArgument(args: RedisCommandArguments, ignore?: ADD.TsIgnoreOptions) { - if (ignore !== undefined) { - args.push('IGNORE', ignore.MAX_TIME_DIFF.toString(), ignore.MAX_VAL_DIFF.toString()); +export function pushEncodingArgument(args: Array, encoding?: TimeSeriesEncoding) { + if (encoding !== undefined) { + args.push('ENCODING', encoding); } } -export function pushRetentionArgument(args: RedisCommandArguments, retention?: number): RedisCommandArguments { - if (retention !== undefined) { - args.push( - 'RETENTION', - retention.toString() - ); - } - - return args; +export function pushChunkSizeArgument(args: Array, chunkSize?: number) { + if (chunkSize !== undefined) { + args.push('CHUNK_SIZE', chunkSize.toString()); + } } -export enum TimeSeriesEncoding { - COMPRESSED = 'COMPRESSED', - UNCOMPRESSED = 'UNCOMPRESSED' -} +export const TIME_SERIES_DUPLICATE_POLICIES = { + BLOCK: 'BLOCK', + FIRST: 'FIRST', + LAST: 'LAST', + MIN: 'MIN', + MAX: 'MAX', + SUM: 'SUM' +} as const; -export function pushEncodingArgument(args: RedisCommandArguments, encoding?: TimeSeriesEncoding): RedisCommandArguments { - if (encoding !== undefined) { - args.push( - 'ENCODING', - encoding - ); - } +export type TimeSeriesDuplicatePolicies = typeof TIME_SERIES_DUPLICATE_POLICIES[keyof typeof TIME_SERIES_DUPLICATE_POLICIES]; - return args; +export function pushDuplicatePolicy(args: Array, duplicatePolicy?: TimeSeriesDuplicatePolicies) { + if (duplicatePolicy !== undefined) { + args.push('DUPLICATE_POLICY', duplicatePolicy); + } } -export function pushChunkSizeArgument(args: RedisCommandArguments, chunkSize?: number): RedisCommandArguments { - if (chunkSize !== undefined) { - args.push( - 'CHUNK_SIZE', - chunkSize.toString() - ); - } - - return args; -} +export type Timestamp = number | Date | string; -export function pushDuplicatePolicy(args: RedisCommandArguments, duplicatePolicy?: TimeSeriesDuplicatePolicies): RedisCommandArguments { - if (duplicatePolicy !== undefined) { - args.push( - 'DUPLICATE_POLICY', - duplicatePolicy - ); - } +export function transformTimestampArgument(timestamp: Timestamp): string { + if (typeof timestamp === 'string') return timestamp; - return args; + return ( + typeof timestamp === 'number' ? + timestamp : + timestamp.getTime() + ).toString(); } -export type RawLabels = Array<[label: string, value: string]>; - export type Labels = { - [label: string]: string; + [label: string]: string; }; -export function transformLablesReply(reply: RawLabels): Labels { - const labels: Labels = {}; +export function pushLabelsArgument(args: Array, labels?: Labels) { + if (labels) { + args.push('LABELS'); - for (const [key, value] of reply) { - labels[key] = value; + for (const [label, value] of Object.entries(labels)) { + args.push(label, value); } + } - return labels -} - -export function pushLabelsArgument(args: RedisCommandArguments, labels?: Labels): RedisCommandArguments { - if (labels) { - args.push('LABELS'); - - for (const [label, value] of Object.entries(labels)) { - args.push(label, value); - } - } - - return args; -} - -export interface IncrDecrOptions { - TIMESTAMP?: Timestamp; - RETENTION?: number; - UNCOMPRESSED?: boolean; - CHUNK_SIZE?: number; - LABELS?: Labels; -} - -export function transformIncrDecrArguments( - command: 'TS.INCRBY' | 'TS.DECRBY', - key: string, - value: number, - options?: IncrDecrOptions -): RedisCommandArguments { - const args = [ - command, - key, - value.toString() - ]; - - if (options?.TIMESTAMP !== undefined && options?.TIMESTAMP !== null) { - args.push('TIMESTAMP', transformTimestampArgument(options.TIMESTAMP)); - } - - pushRetentionArgument(args, options?.RETENTION); - - if (options?.UNCOMPRESSED) { - args.push('UNCOMPRESSED'); - } - - pushChunkSizeArgument(args, options?.CHUNK_SIZE); - - pushLabelsArgument(args, options?.LABELS); - - return args; + return args; } -export type SampleRawReply = [timestamp: number, value: string]; - -export interface SampleReply { - timestamp: number; - value: number; -} +export type SampleRawReply = TuplesReply<[timestamp: NumberReply, value: DoubleReply]>; -export function transformSampleReply(reply: SampleRawReply): SampleReply { +export const transformSampleReply = { + 2(reply: Resp2Reply) { + const [ timestamp, value ] = reply as unknown as UnwrapReply; return { - timestamp: reply[0], - value: Number(reply[1]) - }; -} - -export enum TimeSeriesBucketTimestamp { - LOW = '-', - HIGH = '+', - MID = '~' -} - -export interface RangeOptions { - LATEST?: boolean; - FILTER_BY_TS?: Array; - FILTER_BY_VALUE?: { - min: number; - max: number; + timestamp, + value: Number(value) // TODO: use double type mapping instead }; - COUNT?: number; - ALIGN?: Timestamp; - AGGREGATION?: { - type: TimeSeriesAggregationType; - timeBucket: Timestamp; - BUCKETTIMESTAMP?: TimeSeriesBucketTimestamp; - EMPTY?: boolean; + }, + 3(reply: SampleRawReply) { + const [ timestamp, value ] = reply as unknown as UnwrapReply; + return { + timestamp, + value }; -} - -export function pushRangeArguments( - args: RedisCommandArguments, - fromTimestamp: Timestamp, - toTimestamp: Timestamp, - options?: RangeOptions -): RedisCommandArguments { - args.push( - transformTimestampArgument(fromTimestamp), - transformTimestampArgument(toTimestamp) - ); + } +}; - pushLatestArgument(args, options?.LATEST); +export type SamplesRawReply = ArrayReply; - if (options?.FILTER_BY_TS) { - args.push('FILTER_BY_TS'); - for (const ts of options.FILTER_BY_TS) { - args.push(transformTimestampArgument(ts)); - } - } +export const transformSamplesReply = { + 2(reply: Resp2Reply) { + return (reply as unknown as UnwrapReply) + .map(sample => transformSampleReply[2](sample)); + }, + 3(reply: SamplesRawReply) { + return (reply as unknown as UnwrapReply) + .map(sample => transformSampleReply[3](sample)); + } +}; - if (options?.FILTER_BY_VALUE) { - args.push( - 'FILTER_BY_VALUE', - options.FILTER_BY_VALUE.min.toString(), - options.FILTER_BY_VALUE.max.toString() - ); +// TODO: move to @redis/client? +export function resp2MapToValue< + RAW_VALUE extends TuplesReply<[key: BlobStringReply, ...rest: Array]>, + TRANSFORMED +>( + wrappedReply: ArrayReply, + parseFunc: (rawValue: UnwrapReply) => TRANSFORMED, + typeMapping?: TypeMapping +): MapReply { + const reply = wrappedReply as unknown as UnwrapReply; + switch (typeMapping?.[RESP_TYPES.MAP]) { + case Map: { + const ret = new Map(); + for (const wrappedTuple of reply) { + const tuple = wrappedTuple as unknown as UnwrapReply; + const key = tuple[0] as unknown as UnwrapReply; + ret.set(key.toString(), parseFunc(tuple)); + } + return ret as never; } - - if (options?.COUNT) { - args.push( - 'COUNT', - options.COUNT.toString() - ); + case Array: { + for (const wrappedTuple of reply) { + const tuple = wrappedTuple as unknown as UnwrapReply; + (tuple[1] as unknown as TRANSFORMED) = parseFunc(tuple); + } + return reply as never; } - - if (options?.ALIGN) { - args.push( - 'ALIGN', - transformTimestampArgument(options.ALIGN) - ); + default: { + const ret: Record = Object.create(null); + for (const wrappedTuple of reply) { + const tuple = wrappedTuple as unknown as UnwrapReply; + const key = tuple[0] as unknown as UnwrapReply; + ret[key.toString()] = parseFunc(tuple); + } + return ret as never; } - - if (options?.AGGREGATION) { - args.push( - 'AGGREGATION', - options.AGGREGATION.type, - transformTimestampArgument(options.AGGREGATION.timeBucket) - ); - - if (options.AGGREGATION.BUCKETTIMESTAMP) { - args.push( - 'BUCKETTIMESTAMP', - options.AGGREGATION.BUCKETTIMESTAMP - ); - } - - if (options.AGGREGATION.EMPTY) { - args.push('EMPTY'); - } + } +} + +export function resp3MapToValue< + RAW_VALUE extends RespType, // TODO: simplify types + TRANSFORMED +>( + wrappedReply: MapReply, + parseFunc: (rawValue: UnwrapReply) => TRANSFORMED +): MapReply { + const reply = wrappedReply as unknown as UnwrapReply; + if (reply instanceof Array) { + for (let i = 1; i < reply.length; i += 2) { + (reply[i] as unknown as TRANSFORMED) = parseFunc(reply[i] as unknown as UnwrapReply); } - - return args; -} - -interface MRangeGroupBy { - label: string; - reducer: TimeSeriesReducers; -} - -export function pushMRangeGroupByArguments(args: RedisCommandArguments, groupBy?: MRangeGroupBy): RedisCommandArguments { - if (groupBy) { - args.push( - 'GROUPBY', - groupBy.label, - 'REDUCE', - groupBy.reducer - ); + } else if (reply instanceof Map) { + for (const [key, value] of reply.entries()) { + (reply as unknown as Map).set( + key, + parseFunc(value as unknown as UnwrapReply) + ); } - - return args; -} - -export type Filter = string | Array; - -export function pushFilterArgument(args: RedisCommandArguments, filter: string | Array): RedisCommandArguments { - args.push('FILTER'); - return pushVerdictArguments(args, filter); -} - -export interface MRangeOptions extends RangeOptions { - GROUPBY?: MRangeGroupBy; -} - -export function pushMRangeArguments( - args: RedisCommandArguments, - fromTimestamp: Timestamp, - toTimestamp: Timestamp, - filter: Filter, - options?: MRangeOptions -): RedisCommandArguments { - args = pushRangeArguments(args, fromTimestamp, toTimestamp, options); - args = pushFilterArgument(args, filter); - return pushMRangeGroupByArguments(args, options?.GROUPBY); -} - -export type SelectedLabels = string | Array; - -export function pushWithLabelsArgument(args: RedisCommandArguments, selectedLabels?: SelectedLabels): RedisCommandArguments { - if (!selectedLabels) { - args.push('WITHLABELS'); - } else { - args.push('SELECTED_LABELS'); - args = pushVerdictArguments(args, selectedLabels); + } else { + for (const [key, value] of Object.entries(reply)) { + (reply[key] as unknown as TRANSFORMED) = parseFunc(value as unknown as UnwrapReply); } - - return args; -} - -export interface MRangeWithLabelsOptions extends MRangeOptions { - SELECTED_LABELS?: SelectedLabels; -} - -export function pushMRangeWithLabelsArguments( - args: RedisCommandArguments, - fromTimestamp: Timestamp, - toTimestamp: Timestamp, - filter: Filter, - options?: MRangeWithLabelsOptions -): RedisCommandArguments { - args = pushRangeArguments(args, fromTimestamp, toTimestamp, options); - args = pushWithLabelsArgument(args, options?.SELECTED_LABELS); - args = pushFilterArgument(args, filter); - return pushMRangeGroupByArguments(args, options?.GROUPBY); + } + return reply as never; +} + +export function pushSelectedLabelsArguments( + args: Array, + selectedLabels: RedisVariadicArgument +) { + args.push('SELECTED_LABELS'); + return pushVariadicArguments(args, selectedLabels); +} + +export type RawLabelValue = BlobStringReply | NullReply; + +export type RawLabels = ArrayReply>; + +export function transformRESP2Labels( + labels: RawLabels, + typeMapping?: TypeMapping +): MapReply { + const unwrappedLabels = labels as unknown as UnwrapReply; + switch (typeMapping?.[RESP_TYPES.MAP]) { + case Map: + const map = new Map(); + for (const tuple of unwrappedLabels) { + const [key, value] = tuple as unknown as UnwrapReply; + const unwrappedKey = key as unknown as UnwrapReply; + map.set(unwrappedKey.toString(), value); + } + return map as never; + + case Array: + return unwrappedLabels.flat() as never; + + case Object: + default: + const labelsObject: Record = Object.create(null); + for (const tuple of unwrappedLabels) { + const [key, value] = tuple as unknown as UnwrapReply; + const unwrappedKey = key as unknown as UnwrapReply; + labelsObject[unwrappedKey.toString()] = value; + } + return labelsObject as never; + } } -export function transformRangeReply(reply: Array): Array { - return reply.map(transformSampleReply); -} +export function transformRESP2LabelsWithSources( + labels: RawLabels, + typeMapping?: TypeMapping +) { + const unwrappedLabels = labels as unknown as UnwrapReply; + const to = unwrappedLabels.length - 2; // ignore __reducer__ and __source__ + let transformedLabels: MapReply; + switch (typeMapping?.[RESP_TYPES.MAP]) { + case Map: + const map = new Map(); + for (let i = 0; i < to; i++) { + const [key, value] = unwrappedLabels[i] as unknown as UnwrapReply; + const unwrappedKey = key as unknown as UnwrapReply; + map.set(unwrappedKey.toString(), value); + } + transformedLabels = map as never; + break; + + case Array: + transformedLabels = unwrappedLabels.slice(0, to).flat() as never; + break; + + case Object: + default: + const labelsObject: Record = Object.create(null); + for (let i = 0; i < to; i++) { + const [key, value] = unwrappedLabels[i] as unknown as UnwrapReply; + const unwrappedKey = key as unknown as UnwrapReply; + labelsObject[unwrappedKey.toString()] = value; + } + transformedLabels = labelsObject as never; + break; + } -type MRangeRawReply = Array<[ - key: string, - labels: RawLabels, - samples: Array -]>; + const sourcesTuple = unwrappedLabels[unwrappedLabels.length - 1]; + const unwrappedSourcesTuple = sourcesTuple as unknown as UnwrapReply; + // the __source__ label will never be null + const transformedSources = transformRESP2Sources(unwrappedSourcesTuple[1] as BlobStringReply); -interface MRangeReplyItem { - key: string; - samples: Array; + return { + labels: transformedLabels, + sources: transformedSources + }; } -export function transformMRangeReply(reply: MRangeRawReply): Array { - const args = []; - - for (const [key, _, sample] of reply) { - args.push({ - key, - samples: sample.map(transformSampleReply) - }); - } - - return args; -} -export interface MRangeWithLabelsReplyItem extends MRangeReplyItem { - labels: Labels; -} +function transformRESP2Sources(sourcesRaw: BlobStringReply) { + // if a label contains "," this function will produce incorrcet results.. + // there is not much we can do about it, and we assume most users won't be using "," in their labels.. + + const unwrappedSources = sourcesRaw as unknown as UnwrapReply; + if (typeof unwrappedSources === 'string') { + return unwrappedSources.split(','); + } -export function transformMRangeWithLabelsReply(reply: MRangeRawReply): Array { - const args = []; + const indexOfComma = unwrappedSources.indexOf(','); + if (indexOfComma === -1) { + return [unwrappedSources]; + } - for (const [key, labels, samples] of reply) { - args.push({ - key, - labels: transformLablesReply(labels), - samples: samples.map(transformSampleReply) - }); + const sourcesArray = [ + unwrappedSources.subarray(0, indexOfComma) + ]; + + let previousComma = indexOfComma + 1; + while (true) { + const indexOf = unwrappedSources.indexOf(',', previousComma); + if (indexOf === -1) { + sourcesArray.push( + unwrappedSources.subarray(previousComma) + ); + break; } - return args; -} - -export function pushLatestArgument(args: RedisCommandArguments, latest?: boolean): RedisCommandArguments { - if (latest) { - args.push('LATEST'); - } + const source = unwrappedSources.subarray( + previousComma, + indexOf + ); + sourcesArray.push(source); + previousComma = indexOf + 1; + } - return args; + return sourcesArray; } diff --git a/packages/time-series/lib/index.ts b/packages/time-series/lib/index.ts index 6002556ca1a..bd0be1e9cea 100644 --- a/packages/time-series/lib/index.ts +++ b/packages/time-series/lib/index.ts @@ -1,9 +1,7 @@ -export { default } from './commands'; - export { - TimeSeriesDuplicatePolicies, - TimeSeriesEncoding, - TimeSeriesAggregationType, - TimeSeriesReducers, - TimeSeriesBucketTimestamp + default, + TIME_SERIES_ENCODING, TimeSeriesEncoding, + TIME_SERIES_DUPLICATE_POLICIES, TimeSeriesDuplicatePolicies } from './commands'; +export { TIME_SERIES_AGGREGATION_TYPE, TimeSeriesAggregationType } from './commands/CREATERULE'; +export { TIME_SERIES_BUCKET_TIMESTAMP, TimeSeriesBucketTimestamp } from './commands/RANGE'; diff --git a/packages/time-series/lib/test-utils.ts b/packages/time-series/lib/test-utils.ts index 6d534ccccef..1cb5c8ed97b 100644 --- a/packages/time-series/lib/test-utils.ts +++ b/packages/time-series/lib/test-utils.ts @@ -2,19 +2,20 @@ import TestUtils from '@redis/test-utils'; import TimeSeries from '.'; export default new TestUtils({ - dockerImageName: 'redislabs/redistimeseries', - dockerImageVersionArgument: 'timeseries-version' + dockerImageName: 'redis/redis-stack', + dockerImageVersionArgument: 'timeseries-version', + defaultDockerVersion: '7.4.0-v1' }); export const GLOBAL = { - SERVERS: { - OPEN: { - serverArguments: ['--loadmodule /usr/lib/redis/modules/redistimeseries.so'], - clientOptions: { - modules: { - ts: TimeSeries - } - } + SERVERS: { + OPEN: { + serverArguments: [], + clientOptions: { + modules: { + ts: TimeSeries } + } } + } }; diff --git a/packages/time-series/package.json b/packages/time-series/package.json index 65ee1e99c23..e0317fd231f 100644 --- a/packages/time-series/package.json +++ b/packages/time-series/package.json @@ -1,30 +1,24 @@ { "name": "@redis/time-series", - "version": "1.1.0", + "version": "2.0.0-next.2", "license": "MIT", - "main": "./dist/index.js", - "types": "./dist/index.d.ts", + "main": "./dist/lib/index.js", + "types": "./dist/lib/index.d.ts", "files": [ - "dist/" + "dist/", + "!dist/tsconfig.tsbuildinfo" ], "scripts": { - "test": "nyc -r text-summary -r lcov mocha -r source-map-support/register -r ts-node/register './lib/**/*.spec.ts'", - "build": "tsc", - "documentation": "typedoc" + "test": "nyc -r text-summary -r lcov mocha -r tsx './lib/**/*.spec.ts'" }, "peerDependencies": { - "@redis/client": "^1.0.0" + "@redis/client": "^2.0.0-next.4" }, "devDependencies": { - "@istanbuljs/nyc-config-typescript": "^1.0.2", - "@redis/test-utils": "*", - "@types/node": "^20.6.2", - "nyc": "^15.1.0", - "release-it": "^16.1.5", - "source-map-support": "^0.5.21", - "ts-node": "^10.9.1", - "typedoc": "^0.25.1", - "typescript": "^5.2.2" + "@redis/test-utils": "*" + }, + "engines": { + "node": ">= 18" }, "repository": { "type": "git", diff --git a/tsconfig.base.json b/tsconfig.base.json index 1157be947b9..bd2bcac0845 100644 --- a/tsconfig.base.json +++ b/tsconfig.base.json @@ -1,13 +1,20 @@ { - "extends": "@tsconfig/node14/tsconfig.json", "compilerOptions": { + "lib": ["ES2023"], + "module": "NodeNext", + "moduleResolution": "NodeNext", + "target": "ES2022", + + "strict": true, + "forceConsistentCasingInFileNames": true, + "noUnusedLocals": true, + "esModuleInterop": true, + "skipLibCheck": true, + + "composite": true, + "sourceMap": true, "declaration": true, - "allowJs": true, - "useDefineForClassFields": true, - "esModuleInterop": false, - "resolveJsonModule": true - }, - "ts-node": { - "files": true + "declarationMap": true, + "allowJs": true } } diff --git a/tsconfig.json b/tsconfig.json index 285b7ff0a97..a578fefa54f 100644 --- a/tsconfig.json +++ b/tsconfig.json @@ -1,9 +1,20 @@ { - "extends": "./tsconfig.base.json", - "compilerOptions": { - "outDir": "./dist" - }, - "include": [ - "./index.ts" - ] + "files": [], + "references": [{ + "path": "./packages/client" + }, { + "path": "./packages/test-utils" + }, { + "path": "./packages/bloom" + }, { + "path": "./packages/graph" + }, { + "path": "./packages/json" + }, { + "path": "./packages/search" + }, { + "path": "./packages/time-series" + }, { + "path": "./packages/redis" + }] } From c07b4db7db47f68f5710f9361936a8ee1ccfd186 Mon Sep 17 00:00:00 2001 From: Shaya Potter Date: Tue, 15 Oct 2024 18:10:35 +0300 Subject: [PATCH 1465/1748] fix generating docs (#2853) --- package.json | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/package.json b/package.json index c626d4a48e8..7ab2a557ff2 100644 --- a/package.json +++ b/package.json @@ -7,7 +7,7 @@ "scripts": { "test": "npm run test -ws --if-present", "build": "tsc --build", - "documentation": "typedoc", + "documentation": "typedoc --out ./documentation", "gh-pages": "gh-pages -d ./documentation -e ./documentation -u 'documentation-bot '" }, "devDependencies": { From dca39e14021eb1bbe1ae5eadeea5e9de692e47a0 Mon Sep 17 00:00:00 2001 From: dmaier-redislabs Date: Tue, 15 Oct 2024 17:30:06 +0200 Subject: [PATCH 1466/1748] Release client@5.0.0-next.5 --- packages/client/package.json | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/packages/client/package.json b/packages/client/package.json index cb82f67bd53..9d028aa2bb2 100644 --- a/packages/client/package.json +++ b/packages/client/package.json @@ -1,6 +1,6 @@ { "name": "@redis/client", - "version": "2.0.0-next.4", + "version": "5.0.0-next.5", "license": "MIT", "main": "./dist/index.js", "types": "./dist/index.d.ts", From accc5c47a875512782e17c49caf5f6f59ee9acf1 Mon Sep 17 00:00:00 2001 From: dmaier-redislabs Date: Tue, 15 Oct 2024 17:34:46 +0200 Subject: [PATCH 1467/1748] Updated the Bloom package to use client@5.0.0-next.5 --- packages/bloom/package.json | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/packages/bloom/package.json b/packages/bloom/package.json index 850ad802a5d..26b580d8933 100644 --- a/packages/bloom/package.json +++ b/packages/bloom/package.json @@ -12,7 +12,7 @@ "test": "nyc -r text-summary -r lcov mocha -r tsx './lib/**/*.spec.ts'" }, "peerDependencies": { - "@redis/client": "^2.0.0-next.4" + "@redis/client": "^5.0.0-next.5" }, "devDependencies": { "@redis/test-utils": "*" From 4bb8cf52091b7cbd248dfa93d3910a21e90f14d4 Mon Sep 17 00:00:00 2001 From: dmaier-redislabs Date: Tue, 15 Oct 2024 17:36:26 +0200 Subject: [PATCH 1468/1748] Release bloom@5.0.0-next.5 --- packages/bloom/package.json | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/packages/bloom/package.json b/packages/bloom/package.json index 26b580d8933..cbcbc8ce2bd 100644 --- a/packages/bloom/package.json +++ b/packages/bloom/package.json @@ -1,6 +1,6 @@ { "name": "@redis/bloom", - "version": "2.0.0-next.3", + "version": "5.0.0-next.5", "license": "MIT", "main": "./dist/lib/index.js", "types": "./dist/lib/index.d.ts", From c7b40889ac5e6cd53d1d02465fdb712b1ddafbdd Mon Sep 17 00:00:00 2001 From: dmaier-redislabs Date: Tue, 15 Oct 2024 17:37:31 +0200 Subject: [PATCH 1469/1748] Updated the Graph package to use client@5.0.0-next.5 --- packages/graph/package.json | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/packages/graph/package.json b/packages/graph/package.json index 54b6aad6493..a1417829155 100644 --- a/packages/graph/package.json +++ b/packages/graph/package.json @@ -12,7 +12,7 @@ "test-disable": "nyc -r text-summary -r lcov mocha -r tsx './lib/**/*.spec.ts'" }, "peerDependencies": { - "@redis/client": "^2.0.0-next.4" + "@redis/client": "^5.0.0-next.5" }, "devDependencies": { "@redis/test-utils": "*" From 875ed36d3a21c60d2d8cb3fae4cf1099d163d7d3 Mon Sep 17 00:00:00 2001 From: dmaier-redislabs Date: Tue, 15 Oct 2024 17:38:48 +0200 Subject: [PATCH 1470/1748] Release graph@5.0.0-next.5 --- packages/graph/package.json | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/packages/graph/package.json b/packages/graph/package.json index a1417829155..c62abb23892 100644 --- a/packages/graph/package.json +++ b/packages/graph/package.json @@ -1,6 +1,6 @@ { "name": "@redis/graph", - "version": "2.0.0-next.2", + "version": "5.0.0-next.5", "license": "MIT", "main": "./dist/lib/index.js", "types": "./dist/lib/index.d.ts", From a4466d2049d3e769f4930f8d35b2d21b8ed25795 Mon Sep 17 00:00:00 2001 From: dmaier-redislabs Date: Tue, 15 Oct 2024 17:40:01 +0200 Subject: [PATCH 1471/1748] Updated the JSON package to use client@5.0.0-next.5 --- packages/json/package.json | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/packages/json/package.json b/packages/json/package.json index 53711a5c0b6..0da4d3c683d 100644 --- a/packages/json/package.json +++ b/packages/json/package.json @@ -12,7 +12,7 @@ "test": "nyc -r text-summary -r lcov mocha -r tsx './lib/**/*.spec.ts'" }, "peerDependencies": { - "@redis/client": "^2.0.0-next.4" + "@redis/client": "^5.0.0-next.5" }, "devDependencies": { "@redis/test-utils": "*" From 91476f9ba758200b317c80f0d56407d7cd222126 Mon Sep 17 00:00:00 2001 From: dmaier-redislabs Date: Tue, 15 Oct 2024 17:40:46 +0200 Subject: [PATCH 1472/1748] Release json@5.0.0-next.5 --- packages/json/package.json | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/packages/json/package.json b/packages/json/package.json index 0da4d3c683d..6cc43916497 100644 --- a/packages/json/package.json +++ b/packages/json/package.json @@ -1,6 +1,6 @@ { "name": "@redis/json", - "version": "2.0.0-next.2", + "version": "5.0.0-next.5", "license": "MIT", "main": "./dist/lib/index.js", "types": "./dist/lib/index.d.ts", From c0a418140eb4fb147a05ce7647f0f440ec426070 Mon Sep 17 00:00:00 2001 From: dmaier-redislabs Date: Tue, 15 Oct 2024 17:42:24 +0200 Subject: [PATCH 1473/1748] Updated the Search package to use client@5.0.0-next.5 --- packages/search/package.json | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/packages/search/package.json b/packages/search/package.json index ea6c8a6b4c2..d891a19fa10 100644 --- a/packages/search/package.json +++ b/packages/search/package.json @@ -12,7 +12,7 @@ "test": "nyc -r text-summary -r lcov mocha -r tsx './lib/**/*.spec.ts'" }, "peerDependencies": { - "@redis/client": "^2.0.0-next.4" + "@redis/client": "^5.0.0-next.5" }, "devDependencies": { "@redis/test-utils": "*" From d3e720ffbd507963f0969a829f4200d4307ce02c Mon Sep 17 00:00:00 2001 From: dmaier-redislabs Date: Tue, 15 Oct 2024 17:43:16 +0200 Subject: [PATCH 1474/1748] Release search@5.0.0-next.5 --- packages/search/package.json | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/packages/search/package.json b/packages/search/package.json index d891a19fa10..c5b75110f0f 100644 --- a/packages/search/package.json +++ b/packages/search/package.json @@ -1,6 +1,6 @@ { "name": "@redis/search", - "version": "2.0.0-next.2", + "version": "5.0.0-next.5", "license": "MIT", "main": "./dist/lib/index.js", "types": "./dist/lib/index.d.ts", From 9fbd346a88fecf1ab6c0fd00661548022f3677d1 Mon Sep 17 00:00:00 2001 From: dmaier-redislabs Date: Tue, 15 Oct 2024 17:45:22 +0200 Subject: [PATCH 1475/1748] Updated the TimeSeries package to use client@5.0.0-next.5 --- packages/time-series/package.json | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/packages/time-series/package.json b/packages/time-series/package.json index e0317fd231f..b1592f33966 100644 --- a/packages/time-series/package.json +++ b/packages/time-series/package.json @@ -12,7 +12,7 @@ "test": "nyc -r text-summary -r lcov mocha -r tsx './lib/**/*.spec.ts'" }, "peerDependencies": { - "@redis/client": "^2.0.0-next.4" + "@redis/client": "^5.0.0-next.5" }, "devDependencies": { "@redis/test-utils": "*" From e01f8496f9b406168998c6a24db1524e6143cc9b Mon Sep 17 00:00:00 2001 From: dmaier-redislabs Date: Tue, 15 Oct 2024 17:46:09 +0200 Subject: [PATCH 1476/1748] Release time-series@5.0.0-next.5 --- packages/time-series/package.json | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/packages/time-series/package.json b/packages/time-series/package.json index b1592f33966..1e41ee237c3 100644 --- a/packages/time-series/package.json +++ b/packages/time-series/package.json @@ -1,6 +1,6 @@ { "name": "@redis/time-series", - "version": "2.0.0-next.2", + "version": "5.0.0-next.5", "license": "MIT", "main": "./dist/lib/index.js", "types": "./dist/lib/index.d.ts", From 78a7f32e24970bb656402dcebfde4bd91476b97a Mon Sep 17 00:00:00 2001 From: dmaier-redislabs Date: Tue, 15 Oct 2024 17:48:26 +0200 Subject: [PATCH 1477/1748] Updated the main package to use the 5.0.0-next.5 sub-packages --- packages/redis/package.json | 12 ++++++------ 1 file changed, 6 insertions(+), 6 deletions(-) diff --git a/packages/redis/package.json b/packages/redis/package.json index 0603e6c9480..8c90e50d95a 100644 --- a/packages/redis/package.json +++ b/packages/redis/package.json @@ -10,12 +10,12 @@ "!dist/tsconfig.tsbuildinfo" ], "dependencies": { - "@redis/bloom": "2.0.0-next.3", - "@redis/client": "2.0.0-next.4", - "@redis/graph": "2.0.0-next.2", - "@redis/json": "2.0.0-next.2", - "@redis/search": "2.0.0-next.2", - "@redis/time-series": "2.0.0-next.2" + "@redis/bloom": "5.0.0-next.5", + "@redis/client": "5.0.0-next.5", + "@redis/graph": "5.0.0-next.5", + "@redis/json": "5.0.0-next.5", + "@redis/search": "5.0.0-next.5", + "@redis/time-series": "5.0.0-next.5" }, "engines": { "node": ">= 18" From 5ace34b9c958e7ee5b65c88c46cb43dafe4e66ec Mon Sep 17 00:00:00 2001 From: dmaier-redislabs Date: Tue, 15 Oct 2024 17:51:47 +0200 Subject: [PATCH 1478/1748] Release redis@5.0.0-next.5 --- packages/redis/package.json | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/packages/redis/package.json b/packages/redis/package.json index 8c90e50d95a..617a5bff357 100644 --- a/packages/redis/package.json +++ b/packages/redis/package.json @@ -1,7 +1,7 @@ { "name": "redis", "description": "A modern, high performance Redis client", - "version": "5.0.0-next.4", + "version": "5.0.0-next.5", "license": "MIT", "main": "./dist/index.js", "types": "./dist/index.d.ts", From 4708736f3b7a93fca2694dcd7a02f7d1cc99d789 Mon Sep 17 00:00:00 2001 From: Shaya Potter Date: Thu, 31 Oct 2024 18:16:59 +0200 Subject: [PATCH 1479/1748] new "transform arguments" API for better key and metadata extraction (#2733) * Parser support with all commands * remove "dist" from all imports for consistency * address most of my review comments * small tweak to multi type mapping handling * tweak multi commands / fix addScript cases * nits * addressed all in person review comments * revert addCommand/addScript changes to multi-commands addCommand needs to be there for sendCommand like ability within a multi. If its there, it might as well be used by createCommand() et al, to avoid repeating code. addScript is there (even though only used once), but now made private to keep the logic for bookkeeping near each other. --- package-lock.json | 2511 +++++------------ packages/bloom/lib/commands/bloom/ADD.spec.ts | 3 +- packages/bloom/lib/commands/bloom/ADD.ts | 12 +- .../bloom/lib/commands/bloom/CARD.spec.ts | 3 +- packages/bloom/lib/commands/bloom/CARD.ts | 9 +- .../bloom/lib/commands/bloom/EXISTS.spec.ts | 3 +- packages/bloom/lib/commands/bloom/EXISTS.ts | 12 +- .../bloom/lib/commands/bloom/INFO.spec.ts | 3 +- packages/bloom/lib/commands/bloom/INFO.ts | 17 +- .../bloom/lib/commands/bloom/INSERT.spec.ts | 15 +- packages/bloom/lib/commands/bloom/INSERT.ts | 28 +- .../lib/commands/bloom/LOADCHUNK.spec.ts | 3 +- .../bloom/lib/commands/bloom/LOADCHUNK.ts | 10 +- .../bloom/lib/commands/bloom/MADD.spec.ts | 3 +- packages/bloom/lib/commands/bloom/MADD.ts | 14 +- .../bloom/lib/commands/bloom/MEXISTS.spec.ts | 3 +- packages/bloom/lib/commands/bloom/MEXISTS.ts | 14 +- .../bloom/lib/commands/bloom/RESERVE.spec.ts | 9 +- packages/bloom/lib/commands/bloom/RESERVE.ts | 17 +- .../bloom/lib/commands/bloom/SCANDUMP.spec.ts | 3 +- packages/bloom/lib/commands/bloom/SCANDUMP.ts | 10 +- packages/bloom/lib/commands/bloom/index.ts | 2 +- .../commands/count-min-sketch/INCRBY.spec.ts | 5 +- .../lib/commands/count-min-sketch/INCRBY.ts | 20 +- .../commands/count-min-sketch/INFO.spec.ts | 3 +- .../lib/commands/count-min-sketch/INFO.ts | 9 +- .../count-min-sketch/INITBYDIM.spec.ts | 3 +- .../commands/count-min-sketch/INITBYDIM.ts | 10 +- .../count-min-sketch/INITBYPROB.spec.ts | 3 +- .../commands/count-min-sketch/INITBYPROB.ts | 10 +- .../commands/count-min-sketch/MERGE.spec.ts | 5 +- .../lib/commands/count-min-sketch/MERGE.ts | 24 +- .../commands/count-min-sketch/QUERY.spec.ts | 3 +- .../lib/commands/count-min-sketch/QUERY.ts | 12 +- .../lib/commands/count-min-sketch/index.ts | 2 +- .../bloom/lib/commands/cuckoo/ADD.spec.ts | 3 +- packages/bloom/lib/commands/cuckoo/ADD.ts | 12 +- .../bloom/lib/commands/cuckoo/ADDNX.spec.ts | 3 +- packages/bloom/lib/commands/cuckoo/ADDNX.ts | 12 +- .../bloom/lib/commands/cuckoo/COUNT.spec.ts | 3 +- packages/bloom/lib/commands/cuckoo/COUNT.ts | 10 +- .../bloom/lib/commands/cuckoo/DEL.spec.ts | 3 +- packages/bloom/lib/commands/cuckoo/DEL.ts | 12 +- .../bloom/lib/commands/cuckoo/EXISTS.spec.ts | 3 +- packages/bloom/lib/commands/cuckoo/EXISTS.ts | 12 +- .../bloom/lib/commands/cuckoo/INFO.spec.ts | 3 +- packages/bloom/lib/commands/cuckoo/INFO.ts | 9 +- .../bloom/lib/commands/cuckoo/INSERT.spec.ts | 3 +- packages/bloom/lib/commands/cuckoo/INSERT.ts | 25 +- .../lib/commands/cuckoo/INSERTNX.spec.ts | 3 +- .../bloom/lib/commands/cuckoo/INSERTNX.ts | 10 +- .../lib/commands/cuckoo/LOADCHUNK.spec.ts | 3 +- .../bloom/lib/commands/cuckoo/LOADCHUNK.ts | 10 +- .../bloom/lib/commands/cuckoo/RESERVE.spec.ts | 9 +- packages/bloom/lib/commands/cuckoo/RESERVE.ts | 19 +- .../lib/commands/cuckoo/SCANDUMP.spec.ts | 3 +- .../bloom/lib/commands/cuckoo/SCANDUMP.ts | 10 +- packages/bloom/lib/commands/cuckoo/index.ts | 2 +- .../bloom/lib/commands/t-digest/ADD.spec.ts | 3 +- packages/bloom/lib/commands/t-digest/ADD.ts | 13 +- .../lib/commands/t-digest/BYRANK.spec.ts | 3 +- .../bloom/lib/commands/t-digest/BYRANK.ts | 19 +- .../lib/commands/t-digest/BYREVRANK.spec.ts | 3 +- .../bloom/lib/commands/t-digest/BYREVRANK.ts | 8 +- .../bloom/lib/commands/t-digest/CDF.spec.ts | 3 +- packages/bloom/lib/commands/t-digest/CDF.ts | 15 +- .../lib/commands/t-digest/CREATE.spec.ts | 5 +- .../bloom/lib/commands/t-digest/CREATE.ts | 13 +- .../bloom/lib/commands/t-digest/INFO.spec.ts | 3 +- packages/bloom/lib/commands/t-digest/INFO.ts | 9 +- .../bloom/lib/commands/t-digest/MAX.spec.ts | 3 +- packages/bloom/lib/commands/t-digest/MAX.ts | 11 +- .../bloom/lib/commands/t-digest/MERGE.spec.ts | 9 +- packages/bloom/lib/commands/t-digest/MERGE.ts | 19 +- .../bloom/lib/commands/t-digest/MIN.spec.ts | 3 +- packages/bloom/lib/commands/t-digest/MIN.ts | 11 +- .../lib/commands/t-digest/QUANTILE.spec.ts | 3 +- .../bloom/lib/commands/t-digest/QUANTILE.ts | 15 +- .../bloom/lib/commands/t-digest/RANK.spec.ts | 3 +- packages/bloom/lib/commands/t-digest/RANK.ts | 17 +- .../bloom/lib/commands/t-digest/RESET.spec.ts | 3 +- packages/bloom/lib/commands/t-digest/RESET.ts | 9 +- .../lib/commands/t-digest/REVRANK.spec.ts | 3 +- .../bloom/lib/commands/t-digest/REVRANK.ts | 8 +- .../commands/t-digest/TRIMMED_MEAN.spec.ts | 3 +- .../lib/commands/t-digest/TRIMMED_MEAN.ts | 18 +- packages/bloom/lib/commands/t-digest/index.ts | 2 +- packages/bloom/lib/commands/top-k/ADD.spec.ts | 3 +- packages/bloom/lib/commands/top-k/ADD.ts | 12 +- .../bloom/lib/commands/top-k/COUNT.spec.ts | 3 +- packages/bloom/lib/commands/top-k/COUNT.ts | 12 +- .../bloom/lib/commands/top-k/INCRBY.spec.ts | 5 +- packages/bloom/lib/commands/top-k/INCRBY.ts | 20 +- .../bloom/lib/commands/top-k/INFO.spec.ts | 3 +- packages/bloom/lib/commands/top-k/INFO.ts | 11 +- .../bloom/lib/commands/top-k/LIST.spec.ts | 3 +- packages/bloom/lib/commands/top-k/LIST.ts | 9 +- .../lib/commands/top-k/LIST_WITHCOUNT.spec.ts | 3 +- .../lib/commands/top-k/LIST_WITHCOUNT.ts | 10 +- .../bloom/lib/commands/top-k/QUERY.spec.ts | 3 +- packages/bloom/lib/commands/top-k/QUERY.ts | 12 +- .../bloom/lib/commands/top-k/RESERVE.spec.ts | 5 +- packages/bloom/lib/commands/top-k/RESERVE.ts | 14 +- packages/bloom/lib/commands/top-k/index.ts | 2 +- packages/client/lib/RESP/encoder.ts | 2 +- packages/client/lib/RESP/types.ts | 9 +- packages/client/lib/client/commands-queue.ts | 8 +- packages/client/lib/client/index.spec.ts | 9 +- packages/client/lib/client/index.ts | 138 +- packages/client/lib/client/multi-command.ts | 70 +- packages/client/lib/client/parser.ts | 92 + packages/client/lib/client/pool.ts | 68 +- packages/client/lib/client/socket.ts | 2 +- packages/client/lib/cluster/index.ts | 162 +- packages/client/lib/cluster/multi-command.ts | 101 +- packages/client/lib/commander.ts | 6 +- packages/client/lib/commands/ACL_CAT.spec.ts | 5 +- packages/client/lib/commands/ACL_CAT.ts | 12 +- .../client/lib/commands/ACL_DELUSER.spec.ts | 5 +- packages/client/lib/commands/ACL_DELUSER.ts | 10 +- .../client/lib/commands/ACL_DRYRUN.spec.ts | 3 +- packages/client/lib/commands/ACL_DRYRUN.ts | 12 +- .../client/lib/commands/ACL_GENPASS.spec.ts | 5 +- packages/client/lib/commands/ACL_GENPASS.ts | 12 +- .../client/lib/commands/ACL_GETUSER.spec.ts | 3 +- packages/client/lib/commands/ACL_GETUSER.ts | 7 +- packages/client/lib/commands/ACL_LIST.spec.ts | 3 +- packages/client/lib/commands/ACL_LIST.ts | 7 +- packages/client/lib/commands/ACL_LOAD.spec.ts | 3 +- packages/client/lib/commands/ACL_LOAD.ts | 7 +- packages/client/lib/commands/ACL_LOG.spec.ts | 5 +- packages/client/lib/commands/ACL_LOG.ts | 14 +- .../client/lib/commands/ACL_LOG_RESET.spec.ts | 3 +- packages/client/lib/commands/ACL_LOG_RESET.ts | 7 +- packages/client/lib/commands/ACL_SAVE.spec.ts | 3 +- packages/client/lib/commands/ACL_SAVE.ts | 7 +- .../client/lib/commands/ACL_SETUSER.spec.ts | 5 +- packages/client/lib/commands/ACL_SETUSER.ts | 10 +- .../client/lib/commands/ACL_USERS.spec.ts | 3 +- packages/client/lib/commands/ACL_USERS.ts | 7 +- .../client/lib/commands/ACL_WHOAMI.spec.ts | 3 +- packages/client/lib/commands/ACL_WHOAMI.ts | 7 +- packages/client/lib/commands/APPEND.spec.ts | 3 +- packages/client/lib/commands/APPEND.ts | 7 +- packages/client/lib/commands/ASKING.spec.ts | 3 +- packages/client/lib/commands/ASKING.ts | 9 +- packages/client/lib/commands/AUTH.spec.ts | 5 +- packages/client/lib/commands/AUTH.ts | 15 +- .../client/lib/commands/BGREWRITEAOF.spec.ts | 3 +- packages/client/lib/commands/BGREWRITEAOF.ts | 7 +- packages/client/lib/commands/BGSAVE.spec.ts | 5 +- packages/client/lib/commands/BGSAVE.ts | 12 +- packages/client/lib/commands/BITCOUNT.spec.ts | 9 +- packages/client/lib/commands/BITCOUNT.ts | 19 +- packages/client/lib/commands/BITFIELD.spec.ts | 3 +- packages/client/lib/commands/BITFIELD.ts | 17 +- .../client/lib/commands/BITFIELD_RO.spec.ts | 5 +- packages/client/lib/commands/BITFIELD_RO.ts | 18 +- packages/client/lib/commands/BITOP.spec.ts | 5 +- packages/client/lib/commands/BITOP.ts | 11 +- packages/client/lib/commands/BITPOS.spec.ts | 11 +- packages/client/lib/commands/BITPOS.ts | 17 +- packages/client/lib/commands/BLMOVE.spec.ts | 3 +- packages/client/lib/commands/BLMOVE.ts | 16 +- packages/client/lib/commands/BLMPOP.spec.ts | 5 +- packages/client/lib/commands/BLMPOP.ts | 17 +- packages/client/lib/commands/BLPOP.spec.ts | 5 +- packages/client/lib/commands/BLPOP.ts | 15 +- packages/client/lib/commands/BRPOP.spec.ts | 5 +- packages/client/lib/commands/BRPOP.ts | 15 +- .../client/lib/commands/BRPOPLPUSH.spec.ts | 3 +- packages/client/lib/commands/BRPOPLPUSH.ts | 12 +- packages/client/lib/commands/BZMPOP.spec.ts | 5 +- packages/client/lib/commands/BZMPOP.ts | 9 +- packages/client/lib/commands/BZPOPMAX.spec.ts | 5 +- packages/client/lib/commands/BZPOPMAX.ts | 24 +- packages/client/lib/commands/BZPOPMIN.spec.ts | 5 +- packages/client/lib/commands/BZPOPMIN.ts | 11 +- .../lib/commands/CLIENT_CACHING.spec.ts | 5 +- .../client/lib/commands/CLIENT_CACHING.ts | 9 +- .../lib/commands/CLIENT_GETNAME.spec.ts | 3 +- .../client/lib/commands/CLIENT_GETNAME.ts | 10 +- .../lib/commands/CLIENT_GETREDIR.spec.ts | 3 +- .../client/lib/commands/CLIENT_GETREDIR.ts | 7 +- .../client/lib/commands/CLIENT_ID.spec.ts | 3 +- packages/client/lib/commands/CLIENT_ID.ts | 7 +- .../client/lib/commands/CLIENT_INFO.spec.ts | 3 +- packages/client/lib/commands/CLIENT_INFO.ts | 7 +- .../client/lib/commands/CLIENT_KILL.spec.ts | 23 +- packages/client/lib/commands/CLIENT_KILL.ts | 34 +- .../client/lib/commands/CLIENT_LIST.spec.ts | 7 +- packages/client/lib/commands/CLIENT_LIST.ts | 17 +- .../lib/commands/CLIENT_NO-EVICT.spec.ts | 5 +- .../client/lib/commands/CLIENT_NO-EVICT.ts | 9 +- .../lib/commands/CLIENT_NO-TOUCH.spec.ts | 5 +- .../client/lib/commands/CLIENT_NO-TOUCH.ts | 9 +- .../client/lib/commands/CLIENT_PAUSE.spec.ts | 5 +- packages/client/lib/commands/CLIENT_PAUSE.ts | 16 +- .../lib/commands/CLIENT_SETNAME.spec.ts | 3 +- .../client/lib/commands/CLIENT_SETNAME.ts | 7 +- .../lib/commands/CLIENT_TRACKING.spec.ts | 19 +- .../client/lib/commands/CLIENT_TRACKING.ts | 28 +- .../lib/commands/CLIENT_TRACKINGINFO.spec.ts | 3 +- .../lib/commands/CLIENT_TRACKINGINFO.ts | 7 +- .../lib/commands/CLIENT_UNPAUSE.spec.ts | 3 +- .../client/lib/commands/CLIENT_UNPAUSE.ts | 7 +- .../lib/commands/CLUSTER_ADDSLOTS.spec.ts | 5 +- .../client/lib/commands/CLUSTER_ADDSLOTS.ts | 12 +- .../commands/CLUSTER_ADDSLOTSRANGE.spec.ts | 5 +- .../lib/commands/CLUSTER_ADDSLOTSRANGE.ts | 13 +- .../lib/commands/CLUSTER_BUMPEPOCH.spec.ts | 3 +- .../client/lib/commands/CLUSTER_BUMPEPOCH.ts | 7 +- .../CLUSTER_COUNT-FAILURE-REPORTS.spec.ts | 3 +- .../commands/CLUSTER_COUNT-FAILURE-REPORTS.ts | 7 +- .../commands/CLUSTER_COUNTKEYSINSLOT.spec.ts | 3 +- .../lib/commands/CLUSTER_COUNTKEYSINSLOT.ts | 7 +- .../lib/commands/CLUSTER_DELSLOTS.spec.ts | 5 +- .../client/lib/commands/CLUSTER_DELSLOTS.ts | 12 +- .../commands/CLUSTER_DELSLOTSRANGE.spec.ts | 5 +- .../lib/commands/CLUSTER_DELSLOTSRANGE.ts | 13 +- .../lib/commands/CLUSTER_FAILOVER.spec.ts | 5 +- .../client/lib/commands/CLUSTER_FAILOVER.ts | 11 +- .../lib/commands/CLUSTER_FLUSHSLOTS.spec.ts | 3 +- .../client/lib/commands/CLUSTER_FLUSHSLOTS.ts | 7 +- .../lib/commands/CLUSTER_FORGET.spec.ts | 3 +- .../client/lib/commands/CLUSTER_FORGET.ts | 7 +- .../commands/CLUSTER_GETKEYSINSLOT.spec.ts | 3 +- .../lib/commands/CLUSTER_GETKEYSINSLOT.ts | 7 +- .../client/lib/commands/CLUSTER_INFO.spec.ts | 3 +- packages/client/lib/commands/CLUSTER_INFO.ts | 7 +- .../lib/commands/CLUSTER_KEYSLOT.spec.ts | 3 +- .../client/lib/commands/CLUSTER_KEYSLOT.ts | 7 +- .../client/lib/commands/CLUSTER_LINKS.spec.ts | 3 +- packages/client/lib/commands/CLUSTER_LINKS.ts | 7 +- .../client/lib/commands/CLUSTER_MEET.spec.ts | 3 +- packages/client/lib/commands/CLUSTER_MEET.ts | 7 +- .../client/lib/commands/CLUSTER_MYID.spec.ts | 3 +- packages/client/lib/commands/CLUSTER_MYID.ts | 7 +- .../lib/commands/CLUSTER_MYSHARDID.spec.ts | 3 +- .../client/lib/commands/CLUSTER_MYSHARDID.ts | 7 +- .../client/lib/commands/CLUSTER_NODES.spec.ts | 3 +- packages/client/lib/commands/CLUSTER_NODES.ts | 7 +- .../lib/commands/CLUSTER_REPLICAS.spec.ts | 3 +- .../client/lib/commands/CLUSTER_REPLICAS.ts | 7 +- .../lib/commands/CLUSTER_REPLICATE.spec.ts | 3 +- .../client/lib/commands/CLUSTER_REPLICATE.ts | 7 +- .../client/lib/commands/CLUSTER_RESET.spec.ts | 5 +- packages/client/lib/commands/CLUSTER_RESET.ts | 11 +- .../lib/commands/CLUSTER_SAVECONFIG.spec.ts | 3 +- .../client/lib/commands/CLUSTER_SAVECONFIG.ts | 7 +- .../commands/CLUSTER_SET-CONFIG-EPOCH.spec.ts | 3 +- .../lib/commands/CLUSTER_SET-CONFIG-EPOCH.ts | 7 +- .../lib/commands/CLUSTER_SETSLOT.spec.ts | 5 +- .../client/lib/commands/CLUSTER_SETSLOT.ts | 11 +- .../client/lib/commands/CLUSTER_SLOTS.spec.ts | 3 +- packages/client/lib/commands/CLUSTER_SLOTS.ts | 7 +- packages/client/lib/commands/COMMAND.ts | 6 +- .../client/lib/commands/COMMAND_COUNT.spec.ts | 3 +- packages/client/lib/commands/COMMAND_COUNT.ts | 7 +- .../lib/commands/COMMAND_GETKEYS.spec.ts | 3 +- .../client/lib/commands/COMMAND_GETKEYS.ts | 8 +- .../lib/commands/COMMAND_GETKEYSANDFLAGS.ts | 8 +- packages/client/lib/commands/COMMAND_INFO.ts | 7 +- .../client/lib/commands/COMMAND_LIST.spec.ts | 9 +- packages/client/lib/commands/COMMAND_LIST.ts | 11 +- .../client/lib/commands/CONFIG_GET.spec.ts | 5 +- packages/client/lib/commands/CONFIG_GET.ts | 10 +- .../lib/commands/CONFIG_RESETSTAT.spec.ts | 3 +- .../client/lib/commands/CONFIG_RESETSTAT.ts | 7 +- .../lib/commands/CONFIG_REWRITE.spec.ts | 3 +- .../client/lib/commands/CONFIG_REWRITE.ts | 7 +- .../client/lib/commands/CONFIG_SET.spec.ts | 5 +- packages/client/lib/commands/CONFIG_SET.ts | 14 +- packages/client/lib/commands/COPY.spec.ts | 9 +- packages/client/lib/commands/COPY.ts | 13 +- packages/client/lib/commands/DBSIZE.spec.ts | 3 +- packages/client/lib/commands/DBSIZE.ts | 7 +- packages/client/lib/commands/DECR.spec.ts | 3 +- packages/client/lib/commands/DECR.ts | 7 +- packages/client/lib/commands/DECRBY.spec.ts | 3 +- packages/client/lib/commands/DECRBY.ts | 8 +- packages/client/lib/commands/DEL.spec.ts | 5 +- packages/client/lib/commands/DEL.ts | 9 +- packages/client/lib/commands/DISCARD.spec.ts | 3 +- packages/client/lib/commands/DISCARD.ts | 5 +- packages/client/lib/commands/DUMP.spec.ts | 9 + packages/client/lib/commands/DUMP.ts | 7 +- packages/client/lib/commands/ECHO.spec.ts | 3 +- packages/client/lib/commands/ECHO.ts | 7 +- packages/client/lib/commands/EVAL.spec.ts | 3 +- packages/client/lib/commands/EVAL.ts | 22 +- packages/client/lib/commands/EVALSHA.spec.ts | 3 +- packages/client/lib/commands/EVALSHA.ts | 8 +- .../client/lib/commands/EVALSHA_RO.spec.ts | 3 +- packages/client/lib/commands/EVALSHA_RO.ts | 8 +- packages/client/lib/commands/EVAL_RO.spec.ts | 3 +- packages/client/lib/commands/EVAL_RO.ts | 8 +- packages/client/lib/commands/EXISTS.spec.ts | 7 +- packages/client/lib/commands/EXISTS.ts | 10 +- packages/client/lib/commands/EXPIRE.spec.ts | 5 +- packages/client/lib/commands/EXPIRE.ts | 14 +- packages/client/lib/commands/EXPIREAT.spec.ts | 7 +- packages/client/lib/commands/EXPIREAT.ts | 14 +- .../client/lib/commands/EXPIRETIME.spec.ts | 3 +- packages/client/lib/commands/EXPIRETIME.ts | 7 +- packages/client/lib/commands/FAILOVER.spec.ts | 13 +- packages/client/lib/commands/FAILOVER.ts | 15 +- packages/client/lib/commands/FCALL.spec.ts | 3 +- packages/client/lib/commands/FCALL.ts | 8 +- packages/client/lib/commands/FCALL_RO.spec.ts | 3 +- packages/client/lib/commands/FCALL_RO.ts | 8 +- packages/client/lib/commands/FLUSHALL.spec.ts | 7 +- packages/client/lib/commands/FLUSHALL.ts | 12 +- packages/client/lib/commands/FLUSHDB.spec.ts | 7 +- packages/client/lib/commands/FLUSHDB.ts | 12 +- .../lib/commands/FUNCTION_DELETE.spec.ts | 3 +- .../client/lib/commands/FUNCTION_DELETE.ts | 7 +- .../client/lib/commands/FUNCTION_DUMP.spec.ts | 3 +- packages/client/lib/commands/FUNCTION_DUMP.ts | 7 +- .../lib/commands/FUNCTION_FLUSH.spec.ts | 5 +- .../client/lib/commands/FUNCTION_FLUSH.ts | 13 +- .../client/lib/commands/FUNCTION_KILL.spec.ts | 3 +- packages/client/lib/commands/FUNCTION_KILL.ts | 7 +- .../client/lib/commands/FUNCTION_LIST.spec.ts | 5 +- packages/client/lib/commands/FUNCTION_LIST.ts | 13 +- .../commands/FUNCTION_LIST_WITHCODE.spec.ts | 5 +- .../lib/commands/FUNCTION_LIST_WITHCODE.ts | 9 +- .../client/lib/commands/FUNCTION_LOAD.spec.ts | 10 +- packages/client/lib/commands/FUNCTION_LOAD.ts | 15 +- .../lib/commands/FUNCTION_RESTORE.spec.ts | 5 +- .../client/lib/commands/FUNCTION_RESTORE.ts | 11 +- .../lib/commands/FUNCTION_STATS.spec.ts | 3 +- .../client/lib/commands/FUNCTION_STATS.ts | 7 +- packages/client/lib/commands/GEOADD.spec.ts | 13 +- packages/client/lib/commands/GEOADD.ts | 27 +- packages/client/lib/commands/GEODIST.spec.ts | 5 +- packages/client/lib/commands/GEODIST.ts | 13 +- packages/client/lib/commands/GEOHASH.spec.ts | 5 +- packages/client/lib/commands/GEOHASH.ts | 14 +- packages/client/lib/commands/GEOPOS.spec.ts | 5 +- packages/client/lib/commands/GEOPOS.ts | 14 +- .../client/lib/commands/GEORADIUS.spec.ts | 3 +- packages/client/lib/commands/GEORADIUS.ts | 27 +- .../lib/commands/GEORADIUSBYMEMBER.spec.ts | 3 +- .../client/lib/commands/GEORADIUSBYMEMBER.ts | 33 +- .../lib/commands/GEORADIUSBYMEMBER_RO.spec.ts | 3 +- .../lib/commands/GEORADIUSBYMEMBER_RO.ts | 10 +- .../GEORADIUSBYMEMBER_RO_WITH.spec.ts | 3 +- .../lib/commands/GEORADIUSBYMEMBER_RO_WITH.ts | 10 +- .../commands/GEORADIUSBYMEMBER_STORE.spec.ts | 5 +- .../lib/commands/GEORADIUSBYMEMBER_STORE.ts | 18 +- .../commands/GEORADIUSBYMEMBER_WITH.spec.ts | 3 +- .../lib/commands/GEORADIUSBYMEMBER_WITH.ts | 41 +- .../client/lib/commands/GEORADIUS_RO.spec.ts | 3 +- packages/client/lib/commands/GEORADIUS_RO.ts | 9 +- .../lib/commands/GEORADIUS_RO_WITH.spec.ts | 3 +- .../client/lib/commands/GEORADIUS_RO_WITH.ts | 10 +- .../lib/commands/GEORADIUS_STORE.spec.ts | 5 +- .../client/lib/commands/GEORADIUS_STORE.ts | 19 +- .../lib/commands/GEORADIUS_WITH.spec.ts | 3 +- .../client/lib/commands/GEORADIUS_WITH.ts | 39 +- .../client/lib/commands/GEOSEARCH.spec.ts | 11 +- packages/client/lib/commands/GEOSEARCH.ts | 47 +- .../lib/commands/GEOSEARCHSTORE.spec.ts | 5 +- .../client/lib/commands/GEOSEARCHSTORE.ts | 14 +- .../lib/commands/GEOSEARCH_WITH.spec.ts | 3 +- .../client/lib/commands/GEOSEARCH_WITH.ts | 12 +- packages/client/lib/commands/GET.spec.ts | 3 +- packages/client/lib/commands/GET.ts | 8 +- packages/client/lib/commands/GETBIT.spec.ts | 5 +- packages/client/lib/commands/GETBIT.ts | 9 +- packages/client/lib/commands/GETDEL.spec.ts | 3 +- packages/client/lib/commands/GETDEL.ts | 7 +- packages/client/lib/commands/GETEX.spec.ts | 21 +- packages/client/lib/commands/GETEX.ts | 25 +- packages/client/lib/commands/GETRANGE.spec.ts | 5 +- packages/client/lib/commands/GETRANGE.ts | 9 +- packages/client/lib/commands/GETSET.spec.ts | 3 +- packages/client/lib/commands/GETSET.ts | 8 +- packages/client/lib/commands/HDEL.spec.ts | 5 +- packages/client/lib/commands/HDEL.ts | 10 +- packages/client/lib/commands/HELLO.spec.ts | 11 +- packages/client/lib/commands/HELLO.ts | 13 +- packages/client/lib/commands/HEXISTS.spec.ts | 5 +- packages/client/lib/commands/HEXISTS.ts | 9 +- packages/client/lib/commands/HEXPIRE.spec.ts | 7 +- packages/client/lib/commands/HEXPIRE.ts | 30 +- .../client/lib/commands/HEXPIREAT.spec.ts | 9 +- packages/client/lib/commands/HEXPIREAT.ts | 32 +- .../client/lib/commands/HEXPIRETIME.spec.ts | 5 +- packages/client/lib/commands/HEXPIRETIME.ts | 15 +- packages/client/lib/commands/HGET.spec.ts | 3 +- packages/client/lib/commands/HGET.ts | 9 +- packages/client/lib/commands/HGETALL.ts | 8 +- packages/client/lib/commands/HINCRBY.spec.ts | 3 +- packages/client/lib/commands/HINCRBY.ts | 14 +- .../client/lib/commands/HINCRBYFLOAT.spec.ts | 3 +- packages/client/lib/commands/HINCRBYFLOAT.ts | 14 +- packages/client/lib/commands/HKEYS.spec.ts | 3 +- packages/client/lib/commands/HKEYS.ts | 8 +- packages/client/lib/commands/HLEN.spec.ts | 3 +- packages/client/lib/commands/HLEN.ts | 8 +- packages/client/lib/commands/HMGET.spec.ts | 7 +- packages/client/lib/commands/HMGET.ts | 14 +- packages/client/lib/commands/HPERSIST.spec.ts | 5 +- packages/client/lib/commands/HPERSIST.ts | 16 +- packages/client/lib/commands/HPEXPIRE.spec.ts | 7 +- packages/client/lib/commands/HPEXPIRE.ts | 25 +- .../client/lib/commands/HPEXPIREAT.spec.ts | 9 +- packages/client/lib/commands/HPEXPIREAT.ts | 18 +- .../client/lib/commands/HPEXPIRETIME.spec.ts | 5 +- packages/client/lib/commands/HPEXPIRETIME.ts | 15 +- packages/client/lib/commands/HPTTL.spec.ts | 5 +- packages/client/lib/commands/HPTTL.ts | 15 +- .../client/lib/commands/HRANDFIELD.spec.ts | 3 +- packages/client/lib/commands/HRANDFIELD.ts | 7 +- .../lib/commands/HRANDFIELD_COUNT.spec.ts | 3 +- .../client/lib/commands/HRANDFIELD_COUNT.ts | 8 +- .../commands/HRANDFIELD_COUNT_WITHVALUES.ts | 8 +- packages/client/lib/commands/HSCAN.spec.ts | 9 +- packages/client/lib/commands/HSCAN.ts | 11 +- .../lib/commands/HSCAN_NOVALUES.spec.ts | 10 +- .../client/lib/commands/HSCAN_NOVALUES.ts | 18 +- packages/client/lib/commands/HSET.spec.ts | 15 +- packages/client/lib/commands/HSET.ts | 31 +- packages/client/lib/commands/HSETNX.spec.ts | 3 +- packages/client/lib/commands/HSETNX.ts | 9 +- packages/client/lib/commands/HSTRLEN.spec.ts | 3 +- packages/client/lib/commands/HSTRLEN.ts | 9 +- packages/client/lib/commands/HTTL.spec.ts | 6 +- packages/client/lib/commands/HTTL.ts | 15 +- packages/client/lib/commands/HVALS.spec.ts | 5 +- packages/client/lib/commands/HVALS.ts | 8 +- packages/client/lib/commands/INCR.spec.ts | 3 +- packages/client/lib/commands/INCR.ts | 7 +- packages/client/lib/commands/INCRBY.spec.ts | 3 +- packages/client/lib/commands/INCRBY.ts | 8 +- .../client/lib/commands/INCRBYFLOAT.spec.ts | 3 +- packages/client/lib/commands/INCRBYFLOAT.ts | 8 +- packages/client/lib/commands/INFO.spec.ts | 5 +- packages/client/lib/commands/INFO.ts | 11 +- packages/client/lib/commands/KEYS.ts | 7 +- packages/client/lib/commands/LASTSAVE.spec.ts | 3 +- packages/client/lib/commands/LASTSAVE.ts | 7 +- .../lib/commands/LATENCY_DOCTOR.spec.ts | 3 +- .../client/lib/commands/LATENCY_DOCTOR.ts | 7 +- .../client/lib/commands/LATENCY_GRAPH.spec.ts | 3 +- packages/client/lib/commands/LATENCY_GRAPH.ts | 7 +- .../lib/commands/LATENCY_HISTORY.spec.ts | 3 +- .../client/lib/commands/LATENCY_HISTORY.ts | 7 +- .../lib/commands/LATENCY_LATEST.spec.ts | 3 +- .../client/lib/commands/LATENCY_LATEST.ts | 7 +- packages/client/lib/commands/LCS.spec.ts | 3 +- packages/client/lib/commands/LCS.ts | 8 +- packages/client/lib/commands/LCS_IDX.spec.ts | 3 +- packages/client/lib/commands/LCS_IDX.ts | 13 +- .../lib/commands/LCS_IDX_WITHMATCHLEN.spec.ts | 3 +- .../lib/commands/LCS_IDX_WITHMATCHLEN.ts | 17 +- packages/client/lib/commands/LCS_LEN.spec.ts | 3 +- packages/client/lib/commands/LCS_LEN.ts | 15 +- packages/client/lib/commands/LINDEX.spec.ts | 3 +- packages/client/lib/commands/LINDEX.ts | 9 +- packages/client/lib/commands/LINSERT.spec.ts | 3 +- packages/client/lib/commands/LINSERT.ts | 15 +- packages/client/lib/commands/LLEN.spec.ts | 3 +- packages/client/lib/commands/LLEN.ts | 8 +- packages/client/lib/commands/LMOVE.spec.ts | 3 +- packages/client/lib/commands/LMOVE.ts | 15 +- packages/client/lib/commands/LMPOP.spec.ts | 5 +- packages/client/lib/commands/LMPOP.ts | 30 +- packages/client/lib/commands/LOLWUT.spec.ts | 7 +- packages/client/lib/commands/LOLWUT.ts | 16 +- packages/client/lib/commands/LPOP.spec.ts | 3 +- packages/client/lib/commands/LPOP.ts | 7 +- .../client/lib/commands/LPOP_COUNT.spec.ts | 3 +- packages/client/lib/commands/LPOP_COUNT.ts | 8 +- packages/client/lib/commands/LPOS.spec.ts | 11 +- packages/client/lib/commands/LPOS.ts | 24 +- .../client/lib/commands/LPOS_COUNT.spec.ts | 15 +- packages/client/lib/commands/LPOS_COUNT.ts | 20 +- packages/client/lib/commands/LPUSH.spec.ts | 5 +- packages/client/lib/commands/LPUSH.ts | 10 +- packages/client/lib/commands/LPUSHX.spec.ts | 5 +- packages/client/lib/commands/LPUSHX.ts | 10 +- packages/client/lib/commands/LRANGE.spec.ts | 5 +- packages/client/lib/commands/LRANGE.ts | 18 +- packages/client/lib/commands/LREM.spec.ts | 3 +- packages/client/lib/commands/LREM.ts | 18 +- packages/client/lib/commands/LSET.spec.ts | 3 +- packages/client/lib/commands/LSET.ts | 17 +- packages/client/lib/commands/LTRIM.spec.ts | 3 +- packages/client/lib/commands/LTRIM.ts | 17 +- .../client/lib/commands/MEMORY_DOCTOR.spec.ts | 3 +- packages/client/lib/commands/MEMORY_DOCTOR.ts | 7 +- .../lib/commands/MEMORY_MALLOC-STATS.spec.ts | 3 +- .../lib/commands/MEMORY_MALLOC-STATS.ts | 7 +- .../client/lib/commands/MEMORY_PURGE.spec.ts | 3 +- packages/client/lib/commands/MEMORY_PURGE.ts | 7 +- .../client/lib/commands/MEMORY_STATS.spec.ts | 3 +- packages/client/lib/commands/MEMORY_STATS.ts | 7 +- .../client/lib/commands/MEMORY_USAGE.spec.ts | 5 +- packages/client/lib/commands/MEMORY_USAGE.ts | 11 +- packages/client/lib/commands/MGET.spec.ts | 5 +- packages/client/lib/commands/MGET.ts | 8 +- packages/client/lib/commands/MIGRATE.spec.ts | 15 +- packages/client/lib/commands/MIGRATE.ts | 30 +- .../client/lib/commands/MODULE_LIST.spec.ts | 3 +- packages/client/lib/commands/MODULE_LIST.ts | 7 +- .../client/lib/commands/MODULE_LOAD.spec.ts | 5 +- packages/client/lib/commands/MODULE_LOAD.ts | 11 +- .../client/lib/commands/MODULE_UNLOAD.spec.ts | 3 +- packages/client/lib/commands/MODULE_UNLOAD.ts | 7 +- packages/client/lib/commands/MOVE.spec.ts | 3 +- packages/client/lib/commands/MOVE.ts | 8 +- packages/client/lib/commands/MSET.spec.ts | 7 +- packages/client/lib/commands/MSET.ts | 32 +- packages/client/lib/commands/MSETNX.spec.ts | 7 +- packages/client/lib/commands/MSETNX.ts | 9 +- .../lib/commands/OBJECT_ENCODING.spec.ts | 3 +- .../client/lib/commands/OBJECT_ENCODING.ts | 7 +- .../client/lib/commands/OBJECT_FREQ.spec.ts | 3 +- packages/client/lib/commands/OBJECT_FREQ.ts | 7 +- .../lib/commands/OBJECT_IDLETIME.spec.ts | 3 +- .../client/lib/commands/OBJECT_IDLETIME.ts | 7 +- .../lib/commands/OBJECT_REFCOUNT.spec.ts | 3 +- .../client/lib/commands/OBJECT_REFCOUNT.ts | 7 +- packages/client/lib/commands/PERSIST.spec.ts | 3 +- packages/client/lib/commands/PERSIST.ts | 7 +- packages/client/lib/commands/PEXPIRE.spec.ts | 5 +- packages/client/lib/commands/PEXPIRE.ts | 13 +- .../client/lib/commands/PEXPIREAT.spec.ts | 7 +- packages/client/lib/commands/PEXPIREAT.ts | 13 +- .../client/lib/commands/PEXPIRETIME.spec.ts | 3 +- packages/client/lib/commands/PEXPIRETIME.ts | 7 +- packages/client/lib/commands/PFADD.spec.ts | 5 +- packages/client/lib/commands/PFADD.ts | 15 +- packages/client/lib/commands/PFCOUNT.spec.ts | 5 +- packages/client/lib/commands/PFCOUNT.ts | 9 +- packages/client/lib/commands/PFMERGE.spec.ts | 5 +- packages/client/lib/commands/PFMERGE.ts | 18 +- packages/client/lib/commands/PING.spec.ts | 5 +- packages/client/lib/commands/PING.ts | 11 +- packages/client/lib/commands/PSETEX.spec.ts | 3 +- packages/client/lib/commands/PSETEX.ts | 17 +- packages/client/lib/commands/PTTL.spec.ts | 3 +- packages/client/lib/commands/PTTL.ts | 7 +- packages/client/lib/commands/PUBLISH.spec.ts | 3 +- packages/client/lib/commands/PUBLISH.ts | 7 +- .../lib/commands/PUBSUB_CHANNELS.spec.ts | 5 +- .../client/lib/commands/PUBSUB_CHANNELS.ts | 11 +- .../client/lib/commands/PUBSUB_NUMPAT.spec.ts | 3 +- packages/client/lib/commands/PUBSUB_NUMPAT.ts | 7 +- .../client/lib/commands/PUBSUB_NUMSUB.spec.ts | 7 +- packages/client/lib/commands/PUBSUB_NUMSUB.ts | 15 +- .../lib/commands/PUBSUB_SHARDCHANNELS.spec.ts | 5 +- .../lib/commands/PUBSUB_SHARDCHANNELS.ts | 11 +- .../lib/commands/PUBSUB_SHARDNUMSUB.spec.ts | 7 +- .../client/lib/commands/PUBSUB_SHARDNUMSUB.ts | 14 +- .../client/lib/commands/RANDOMKEY.spec.ts | 3 +- packages/client/lib/commands/RANDOMKEY.ts | 7 +- packages/client/lib/commands/READONLY.spec.ts | 3 +- packages/client/lib/commands/READONLY.ts | 7 +- .../client/lib/commands/READWRITE.spec.ts | 3 +- packages/client/lib/commands/READWRITE.ts | 7 +- packages/client/lib/commands/RENAME.spec.ts | 3 +- packages/client/lib/commands/RENAME.ts | 7 +- packages/client/lib/commands/RENAMENX.spec.ts | 3 +- packages/client/lib/commands/RENAMENX.ts | 7 +- .../client/lib/commands/REPLICAOF.spec.ts | 3 +- packages/client/lib/commands/REPLICAOF.ts | 7 +- .../lib/commands/RESTORE-ASKING.spec.ts | 3 +- .../client/lib/commands/RESTORE-ASKING.ts | 7 +- packages/client/lib/commands/RESTORE.spec.ts | 13 +- packages/client/lib/commands/RESTORE.ts | 19 +- packages/client/lib/commands/ROLE.spec.ts | 3 +- packages/client/lib/commands/ROLE.ts | 7 +- packages/client/lib/commands/RPOP.spec.ts | 3 +- packages/client/lib/commands/RPOP.ts | 7 +- .../client/lib/commands/RPOPLPUSH.spec.ts | 3 +- packages/client/lib/commands/RPOPLPUSH.ts | 10 +- .../client/lib/commands/RPOP_COUNT.spec.ts | 3 +- packages/client/lib/commands/RPOP_COUNT.ts | 8 +- packages/client/lib/commands/RPUSH.spec.ts | 5 +- packages/client/lib/commands/RPUSH.ts | 13 +- packages/client/lib/commands/RPUSHX.spec.ts | 5 +- packages/client/lib/commands/RPUSHX.ts | 13 +- packages/client/lib/commands/SADD.spec.ts | 5 +- packages/client/lib/commands/SADD.ts | 10 +- packages/client/lib/commands/SAVE.spec.ts | 3 +- packages/client/lib/commands/SAVE.ts | 7 +- packages/client/lib/commands/SCAN.spec.ts | 11 +- packages/client/lib/commands/SCAN.ts | 27 +- packages/client/lib/commands/SCARD.spec.ts | 3 +- packages/client/lib/commands/SCARD.ts | 8 +- .../client/lib/commands/SCRIPT_DEBUG.spec.ts | 3 +- packages/client/lib/commands/SCRIPT_DEBUG.ts | 7 +- .../client/lib/commands/SCRIPT_EXISTS.spec.ts | 5 +- packages/client/lib/commands/SCRIPT_EXISTS.ts | 10 +- .../client/lib/commands/SCRIPT_FLUSH.spec.ts | 5 +- packages/client/lib/commands/SCRIPT_FLUSH.ts | 11 +- .../client/lib/commands/SCRIPT_KILL.spec.ts | 3 +- packages/client/lib/commands/SCRIPT_KILL.ts | 7 +- .../client/lib/commands/SCRIPT_LOAD.spec.ts | 3 +- packages/client/lib/commands/SCRIPT_LOAD.ts | 7 +- packages/client/lib/commands/SDIFF.spec.ts | 7 +- packages/client/lib/commands/SDIFF.ts | 10 +- .../client/lib/commands/SDIFFSTORE.spec.ts | 5 +- packages/client/lib/commands/SDIFFSTORE.ts | 10 +- packages/client/lib/commands/SET.spec.ts | 31 +- packages/client/lib/commands/SET.ts | 38 +- packages/client/lib/commands/SETBIT.spec.ts | 3 +- packages/client/lib/commands/SETBIT.ts | 12 +- packages/client/lib/commands/SETEX.spec.ts | 3 +- packages/client/lib/commands/SETEX.ts | 17 +- packages/client/lib/commands/SETNX .spec.ts | 3 +- packages/client/lib/commands/SETNX.ts | 8 +- packages/client/lib/commands/SETRANGE.spec.ts | 3 +- packages/client/lib/commands/SETRANGE.ts | 17 +- packages/client/lib/commands/SHUTDOWN.spec.ts | 11 +- packages/client/lib/commands/SHUTDOWN.ts | 17 +- packages/client/lib/commands/SINTER.spec.ts | 7 +- packages/client/lib/commands/SINTER.ts | 10 +- .../client/lib/commands/SINTERCARD.spec.ts | 7 +- packages/client/lib/commands/SINTERCARD.ts | 19 +- .../client/lib/commands/SINTERSTORE.spec.ts | 5 +- packages/client/lib/commands/SINTERSTORE.ts | 13 +- .../client/lib/commands/SISMEMBER.spec.ts | 5 +- packages/client/lib/commands/SISMEMBER.ts | 9 +- packages/client/lib/commands/SMEMBERS.spec.ts | 5 +- packages/client/lib/commands/SMEMBERS.ts | 8 +- .../client/lib/commands/SMISMEMBER.spec.ts | 5 +- packages/client/lib/commands/SMISMEMBER.ts | 9 +- packages/client/lib/commands/SMOVE.spec.ts | 3 +- packages/client/lib/commands/SMOVE.ts | 12 +- packages/client/lib/commands/SORT.spec.ts | 17 +- packages/client/lib/commands/SORT.ts | 27 +- packages/client/lib/commands/SORT_RO.spec.ts | 17 +- packages/client/lib/commands/SORT_RO.ts | 10 +- .../client/lib/commands/SORT_STORE.spec.ts | 17 +- packages/client/lib/commands/SORT_STORE.ts | 13 +- packages/client/lib/commands/SPOP.spec.ts | 3 +- packages/client/lib/commands/SPOP.ts | 7 +- .../client/lib/commands/SPOP_COUNT.spec.ts | 3 +- packages/client/lib/commands/SPOP_COUNT.ts | 8 +- packages/client/lib/commands/SPUBLISH.spec.ts | 3 +- packages/client/lib/commands/SPUBLISH.ts | 8 +- .../client/lib/commands/SRANDMEMBER.spec.ts | 3 +- packages/client/lib/commands/SRANDMEMBER.ts | 7 +- .../lib/commands/SRANDMEMBER_COUNT.spec.ts | 3 +- .../client/lib/commands/SRANDMEMBER_COUNT.ts | 9 +- packages/client/lib/commands/SREM.spec.ts | 5 +- packages/client/lib/commands/SREM.ts | 10 +- packages/client/lib/commands/SSCAN.spec.ts | 9 +- packages/client/lib/commands/SSCAN.ts | 11 +- packages/client/lib/commands/STRLEN.spec.ts | 3 +- packages/client/lib/commands/STRLEN.ts | 8 +- packages/client/lib/commands/SUNION.spec.ts | 5 +- packages/client/lib/commands/SUNION.ts | 10 +- .../client/lib/commands/SUNIONSTORE.spec.ts | 5 +- packages/client/lib/commands/SUNIONSTORE.ts | 13 +- packages/client/lib/commands/SWAPDB.spec.ts | 3 +- packages/client/lib/commands/SWAPDB.ts | 7 +- packages/client/lib/commands/TIME.spec.ts | 3 +- packages/client/lib/commands/TIME.ts | 7 +- packages/client/lib/commands/TOUCH.spec.ts | 5 +- packages/client/lib/commands/TOUCH.ts | 9 +- packages/client/lib/commands/TTL.spec.ts | 3 +- packages/client/lib/commands/TTL.ts | 7 +- packages/client/lib/commands/TYPE.spec.ts | 5 +- packages/client/lib/commands/TYPE.ts | 8 +- packages/client/lib/commands/UNLINK.spec.ts | 5 +- packages/client/lib/commands/UNLINK.ts | 9 +- packages/client/lib/commands/WAIT.spec.ts | 3 +- packages/client/lib/commands/WAIT.ts | 7 +- packages/client/lib/commands/XACK.spec.ts | 5 +- packages/client/lib/commands/XACK.ts | 16 +- packages/client/lib/commands/XADD.spec.ts | 13 +- packages/client/lib/commands/XADD.ts | 40 +- .../lib/commands/XADD_NOMKSTREAM.spec.ts | 13 +- .../client/lib/commands/XADD_NOMKSTREAM.ts | 15 +- .../client/lib/commands/XAUTOCLAIM.spec.ts | 5 +- packages/client/lib/commands/XAUTOCLAIM.ts | 20 +- .../lib/commands/XAUTOCLAIM_JUSTID.spec.ts | 3 +- .../client/lib/commands/XAUTOCLAIM_JUSTID.ts | 9 +- packages/client/lib/commands/XCLAIM.spec.ts | 19 +- packages/client/lib/commands/XCLAIM.ts | 27 +- .../client/lib/commands/XCLAIM_JUSTID.spec.ts | 3 +- packages/client/lib/commands/XCLAIM_JUSTID.ts | 9 +- packages/client/lib/commands/XDEL.spec.ts | 5 +- packages/client/lib/commands/XDEL.ts | 10 +- .../client/lib/commands/XGROUP_CREATE.spec.ts | 7 +- packages/client/lib/commands/XGROUP_CREATE.ts | 15 +- .../commands/XGROUP_CREATECONSUMER.spec.ts | 3 +- .../lib/commands/XGROUP_CREATECONSUMER.ts | 9 +- .../lib/commands/XGROUP_DELCONSUMER.spec.ts | 3 +- .../client/lib/commands/XGROUP_DELCONSUMER.ts | 9 +- .../lib/commands/XGROUP_DESTROY.spec.ts | 3 +- .../client/lib/commands/XGROUP_DESTROY.ts | 11 +- .../client/lib/commands/XGROUP_SETID.spec.ts | 3 +- packages/client/lib/commands/XGROUP_SETID.ts | 13 +- .../lib/commands/XINFO_CONSUMERS.spec.ts | 3 +- .../client/lib/commands/XINFO_CONSUMERS.ts | 11 +- .../client/lib/commands/XINFO_GROUPS.spec.ts | 3 +- packages/client/lib/commands/XINFO_GROUPS.ts | 7 +- .../client/lib/commands/XINFO_STREAM.spec.ts | 3 +- packages/client/lib/commands/XINFO_STREAM.ts | 7 +- packages/client/lib/commands/XLEN.spec.ts | 5 +- packages/client/lib/commands/XLEN.ts | 8 +- packages/client/lib/commands/XPENDING.spec.ts | 3 +- packages/client/lib/commands/XPENDING.ts | 9 +- .../lib/commands/XPENDING_RANGE.spec.ts | 9 +- .../client/lib/commands/XPENDING_RANGE.ts | 24 +- packages/client/lib/commands/XRANGE.spec.ts | 5 +- packages/client/lib/commands/XRANGE.ts | 15 +- packages/client/lib/commands/XREAD.spec.ts | 18 +- packages/client/lib/commands/XREAD.ts | 33 +- .../client/lib/commands/XREADGROUP.spec.ts | 19 +- packages/client/lib/commands/XREADGROUP.ts | 25 +- .../client/lib/commands/XREVRANGE.spec.ts | 5 +- packages/client/lib/commands/XREVRANGE.ts | 13 +- packages/client/lib/commands/XSETID.spec.ts | 7 +- packages/client/lib/commands/XSETID.ts | 15 +- packages/client/lib/commands/XTRIM.spec.ts | 9 +- packages/client/lib/commands/XTRIM.ts | 17 +- packages/client/lib/commands/ZADD.spec.ts | 21 +- packages/client/lib/commands/ZADD.ts | 36 +- .../client/lib/commands/ZADD_INCR.spec.ts | 13 +- packages/client/lib/commands/ZADD_INCR.ts | 20 +- packages/client/lib/commands/ZCARD.spec.ts | 3 +- packages/client/lib/commands/ZCARD.ts | 8 +- packages/client/lib/commands/ZCOUNT.spec.ts | 3 +- packages/client/lib/commands/ZCOUNT.ts | 16 +- packages/client/lib/commands/ZDIFF.spec.ts | 5 +- packages/client/lib/commands/ZDIFF.ts | 9 +- .../client/lib/commands/ZDIFFSTORE.spec.ts | 5 +- packages/client/lib/commands/ZDIFFSTORE.ts | 13 +- .../lib/commands/ZDIFF_WITHSCORES.spec.ts | 5 +- .../client/lib/commands/ZDIFF_WITHSCORES.ts | 12 +- packages/client/lib/commands/ZINCRBY.spec.ts | 3 +- packages/client/lib/commands/ZINCRBY.ts | 14 +- packages/client/lib/commands/ZINTER.spec.ts | 11 +- packages/client/lib/commands/ZINTER.ts | 24 +- .../client/lib/commands/ZINTERCARD.spec.ts | 7 +- packages/client/lib/commands/ZINTERCARD.ts | 16 +- .../client/lib/commands/ZINTERSTORE.spec.ts | 11 +- packages/client/lib/commands/ZINTERSTORE.ts | 11 +- .../lib/commands/ZINTER_WITHSCORES.spec.ts | 11 +- .../client/lib/commands/ZINTER_WITHSCORES.ts | 11 +- .../client/lib/commands/ZLEXCOUNT.spec.ts | 3 +- packages/client/lib/commands/ZLEXCOUNT.ts | 16 +- packages/client/lib/commands/ZMPOP.spec.ts | 5 +- packages/client/lib/commands/ZMPOP.ts | 30 +- packages/client/lib/commands/ZMSCORE.spec.ts | 5 +- packages/client/lib/commands/ZMSCORE.ts | 14 +- packages/client/lib/commands/ZPOPMAX.spec.ts | 3 +- packages/client/lib/commands/ZPOPMAX.ts | 7 +- .../client/lib/commands/ZPOPMAX_COUNT.spec.ts | 3 +- packages/client/lib/commands/ZPOPMAX_COUNT.ts | 8 +- packages/client/lib/commands/ZPOPMIN.spec.ts | 3 +- packages/client/lib/commands/ZPOPMIN.ts | 7 +- .../client/lib/commands/ZPOPMIN_COUNT.spec.ts | 3 +- packages/client/lib/commands/ZPOPMIN_COUNT.ts | 8 +- .../client/lib/commands/ZRANDMEMBER.spec.ts | 3 +- packages/client/lib/commands/ZRANDMEMBER.ts | 7 +- .../lib/commands/ZRANDMEMBER_COUNT.spec.ts | 3 +- .../client/lib/commands/ZRANDMEMBER_COUNT.ts | 9 +- .../ZRANDMEMBER_COUNT_WITHSCORES.spec.ts | 3 +- .../commands/ZRANDMEMBER_COUNT_WITHSCORES.ts | 11 +- packages/client/lib/commands/ZRANGE.spec.ts | 13 +- packages/client/lib/commands/ZRANGE.ts | 74 +- .../client/lib/commands/ZRANGEBYLEX.spec.ts | 5 +- packages/client/lib/commands/ZRANGEBYLEX.ts | 18 +- .../client/lib/commands/ZRANGEBYSCORE.spec.ts | 5 +- packages/client/lib/commands/ZRANGEBYSCORE.ts | 18 +- .../commands/ZRANGEBYSCORE_WITHSCORES.spec.ts | 5 +- .../lib/commands/ZRANGEBYSCORE_WITHSCORES.ts | 13 +- .../client/lib/commands/ZRANGESTORE.spec.ts | 13 +- packages/client/lib/commands/ZRANGESTORE.ts | 27 +- .../lib/commands/ZRANGE_WITHSCORES.spec.ts | 11 +- .../client/lib/commands/ZRANGE_WITHSCORES.ts | 13 +- packages/client/lib/commands/ZRANK.spec.ts | 3 +- packages/client/lib/commands/ZRANK.ts | 9 +- .../lib/commands/ZRANK_WITHSCORE.spec.ts | 3 +- .../client/lib/commands/ZRANK_WITHSCORE.ts | 11 +- packages/client/lib/commands/ZREM.spec.ts | 5 +- packages/client/lib/commands/ZREM.ts | 11 +- .../lib/commands/ZREMRANGEBYLEX.spec.ts | 3 +- .../client/lib/commands/ZREMRANGEBYLEX.ts | 13 +- .../lib/commands/ZREMRANGEBYRANK.spec.ts | 3 +- .../client/lib/commands/ZREMRANGEBYRANK.ts | 15 +- .../lib/commands/ZREMRANGEBYSCORE.spec.ts | 3 +- .../client/lib/commands/ZREMRANGEBYSCORE.ts | 13 +- packages/client/lib/commands/ZREVRANK.spec.ts | 3 +- packages/client/lib/commands/ZREVRANK.ts | 9 +- packages/client/lib/commands/ZSCAN.spec.ts | 9 +- packages/client/lib/commands/ZSCAN.ts | 11 +- packages/client/lib/commands/ZSCORE.spec.ts | 3 +- packages/client/lib/commands/ZSCORE.ts | 9 +- packages/client/lib/commands/ZUNION.spec.ts | 11 +- packages/client/lib/commands/ZUNION.ts | 16 +- .../client/lib/commands/ZUNIONSTORE.spec.ts | 11 +- packages/client/lib/commands/ZUNIONSTORE.ts | 19 +- .../lib/commands/ZUNION_WITHSCORES.spec.ts | 11 +- .../client/lib/commands/ZUNION_WITHSCORES.ts | 13 +- .../lib/commands/generic-transformers.ts | 93 +- packages/client/lib/multi-command.ts | 10 +- .../lib/sentinel/commands/SENTINEL_MASTER.ts | 5 +- .../lib/sentinel/commands/SENTINEL_MONITOR.ts | 5 +- .../sentinel/commands/SENTINEL_REPLICAS.ts | 5 +- .../sentinel/commands/SENTINEL_SENTINELS.ts | 5 +- .../lib/sentinel/commands/SENTINEL_SET.ts | 9 +- packages/client/lib/sentinel/index.spec.ts | 13 +- packages/client/lib/sentinel/index.ts | 26 +- .../client/lib/sentinel/multi-commands.ts | 107 +- packages/client/lib/sentinel/utils.ts | 70 +- packages/client/lib/test-utils.ts | 8 + .../graph/lib/commands/CONFIG_GET.spec.ts | 3 +- packages/graph/lib/commands/CONFIG_GET.ts | 9 +- .../graph/lib/commands/CONFIG_SET.spec.ts | 3 +- packages/graph/lib/commands/CONFIG_SET.ts | 14 +- packages/graph/lib/commands/DELETE.spec.ts | 3 +- packages/graph/lib/commands/DELETE.ts | 9 +- packages/graph/lib/commands/EXPLAIN.spec.ts | 3 +- packages/graph/lib/commands/EXPLAIN.ts | 10 +- packages/graph/lib/commands/LIST.spec.ts | 3 +- packages/graph/lib/commands/LIST.ts | 9 +- packages/graph/lib/commands/PROFILE.spec.ts | 3 +- packages/graph/lib/commands/PROFILE.ts | 10 +- packages/graph/lib/commands/QUERY.spec.ts | 11 +- packages/graph/lib/commands/QUERY.ts | 28 +- packages/graph/lib/commands/RO_QUERY.spec.ts | 3 +- packages/graph/lib/commands/RO_QUERY.ts | 7 +- packages/graph/lib/commands/SLOWLOG.spec.ts | 3 +- packages/graph/lib/commands/SLOWLOG.ts | 9 +- packages/graph/lib/commands/index.ts | 2 +- packages/graph/lib/graph.ts | 2 +- packages/json/lib/commands/ARRAPPEND.spec.ts | 5 +- packages/json/lib/commands/ARRAPPEND.ts | 20 +- packages/json/lib/commands/ARRINDEX.spec.ts | 7 +- packages/json/lib/commands/ARRINDEX.ts | 17 +- packages/json/lib/commands/ARRINSERT.spec.ts | 5 +- packages/json/lib/commands/ARRINSERT.ts | 21 +- packages/json/lib/commands/ARRLEN.spec.ts | 5 +- packages/json/lib/commands/ARRLEN.ts | 14 +- packages/json/lib/commands/ARRPOP.spec.ts | 7 +- packages/json/lib/commands/ARRPOP.ts | 17 +- packages/json/lib/commands/ARRTRIM.spec.ts | 3 +- packages/json/lib/commands/ARRTRIM.ts | 10 +- packages/json/lib/commands/CLEAR.spec.ts | 5 +- packages/json/lib/commands/CLEAR.ts | 13 +- .../json/lib/commands/DEBUG_MEMORY.spec.ts | 5 +- packages/json/lib/commands/DEBUG_MEMORY.ts | 13 +- packages/json/lib/commands/DEL.spec.ts | 5 +- packages/json/lib/commands/DEL.ts | 13 +- packages/json/lib/commands/FORGET.spec.ts | 5 +- packages/json/lib/commands/FORGET.ts | 13 +- packages/json/lib/commands/GET.spec.ts | 7 +- packages/json/lib/commands/GET.ts | 16 +- packages/json/lib/commands/MERGE.spec.ts | 3 +- packages/json/lib/commands/MERGE.ts | 15 +- packages/json/lib/commands/MGET.spec.ts | 3 +- packages/json/lib/commands/MGET.ts | 14 +- packages/json/lib/commands/MSET.spec.ts | 3 +- packages/json/lib/commands/MSET.ts | 18 +- packages/json/lib/commands/NUMINCRBY.spec.ts | 3 +- packages/json/lib/commands/NUMINCRBY.ts | 10 +- packages/json/lib/commands/NUMMULTBY.spec.ts | 3 +- packages/json/lib/commands/NUMMULTBY.ts | 10 +- packages/json/lib/commands/OBJKEYS.spec.ts | 5 +- packages/json/lib/commands/OBJKEYS.ts | 14 +- packages/json/lib/commands/OBJLEN.spec.ts | 5 +- packages/json/lib/commands/OBJLEN.ts | 14 +- packages/json/lib/commands/RESP.spec.ts | 7 +- packages/json/lib/commands/RESP.ts | 25 +- packages/json/lib/commands/SET.spec.ts | 7 +- packages/json/lib/commands/SET.ts | 19 +- packages/json/lib/commands/STRAPPEND.spec.ts | 5 +- packages/json/lib/commands/STRAPPEND.ts | 14 +- packages/json/lib/commands/STRLEN.spec.ts | 5 +- packages/json/lib/commands/STRLEN.ts | 13 +- packages/json/lib/commands/TOGGLE.spec.ts | 3 +- packages/json/lib/commands/TOGGLE.ts | 10 +- packages/json/lib/commands/TYPE.spec.ts | 5 +- packages/json/lib/commands/TYPE.ts | 14 +- packages/json/lib/commands/index.ts | 4 +- .../search/lib/commands/AGGREGATE.spec.ts | 70 +- packages/search/lib/commands/AGGREGATE.ts | 73 +- .../lib/commands/AGGREGATE_WITHCURSOR.spec.ts | 7 +- .../lib/commands/AGGREGATE_WITHCURSOR.ts | 17 +- packages/search/lib/commands/ALIASADD.spec.ts | 3 +- packages/search/lib/commands/ALIASADD.ts | 9 +- packages/search/lib/commands/ALIASDEL.spec.ts | 3 +- packages/search/lib/commands/ALIASDEL.ts | 9 +- .../search/lib/commands/ALIASUPDATE.spec.ts | 3 +- packages/search/lib/commands/ALIASUPDATE.ts | 9 +- packages/search/lib/commands/ALTER.spec.ts | 3 +- packages/search/lib/commands/ALTER.ts | 14 +- .../search/lib/commands/CONFIG_GET.spec.ts | 3 +- packages/search/lib/commands/CONFIG_GET.ts | 9 +- .../search/lib/commands/CONFIG_SET.spec.ts | 3 +- packages/search/lib/commands/CONFIG_SET.ts | 9 +- packages/search/lib/commands/CREATE.spec.ts | 83 +- packages/search/lib/commands/CREATE.ts | 103 +- .../search/lib/commands/CURSOR_DEL.spec.ts | 3 +- packages/search/lib/commands/CURSOR_DEL.ts | 9 +- .../search/lib/commands/CURSOR_READ.spec.ts | 5 +- packages/search/lib/commands/CURSOR_READ.ts | 13 +- packages/search/lib/commands/DICTADD.spec.ts | 5 +- packages/search/lib/commands/DICTADD.ts | 12 +- packages/search/lib/commands/DICTDEL.spec.ts | 5 +- packages/search/lib/commands/DICTDEL.ts | 12 +- packages/search/lib/commands/DICTDUMP.spec.ts | 3 +- packages/search/lib/commands/DICTDUMP.ts | 9 +- .../search/lib/commands/DROPINDEX.spec.ts | 5 +- packages/search/lib/commands/DROPINDEX.ts | 13 +- packages/search/lib/commands/EXPLAIN.spec.ts | 7 +- packages/search/lib/commands/EXPLAIN.ts | 18 +- .../search/lib/commands/EXPLAINCLI.spec.ts | 3 +- packages/search/lib/commands/EXPLAINCLI.ts | 9 +- packages/search/lib/commands/INFO.spec.ts | 3 +- packages/search/lib/commands/INFO.ts | 11 +- .../lib/commands/PROFILE_AGGREGATE.spec.ts | 5 +- .../search/lib/commands/PROFILE_AGGREGATE.ts | 21 +- .../lib/commands/PROFILE_SEARCH.spec.ts | 5 +- .../search/lib/commands/PROFILE_SEARCH.ts | 22 +- packages/search/lib/commands/SEARCH.spec.ts | 53 +- packages/search/lib/commands/SEARCH.ts | 77 +- .../lib/commands/SEARCH_NOCONTENT.spec.ts | 3 +- .../search/lib/commands/SEARCH_NOCONTENT.ts | 11 +- .../search/lib/commands/SPELLCHECK.spec.ts | 11 +- packages/search/lib/commands/SPELLCHECK.ts | 27 +- packages/search/lib/commands/SUGADD.spec.ts | 7 +- packages/search/lib/commands/SUGADD.ts | 16 +- packages/search/lib/commands/SUGDEL.spec.ts | 3 +- packages/search/lib/commands/SUGDEL.ts | 10 +- packages/search/lib/commands/SUGGET.spec.ts | 7 +- packages/search/lib/commands/SUGGET.ts | 16 +- .../lib/commands/SUGGET_WITHPAYLOADS.spec.ts | 3 +- .../lib/commands/SUGGET_WITHPAYLOADS.ts | 12 +- .../lib/commands/SUGGET_WITHSCORES.spec.ts | 3 +- .../search/lib/commands/SUGGET_WITHSCORES.ts | 12 +- .../SUGGET_WITHSCORES_WITHPAYLOADS.spec.ts | 3 +- .../SUGGET_WITHSCORES_WITHPAYLOADS.ts | 12 +- packages/search/lib/commands/SUGLEN.spec.ts | 3 +- packages/search/lib/commands/SUGLEN.ts | 8 +- packages/search/lib/commands/SYNDUMP.spec.ts | 3 +- packages/search/lib/commands/SYNDUMP.ts | 9 +- .../search/lib/commands/SYNUPDATE.spec.ts | 7 +- packages/search/lib/commands/SYNUPDATE.ts | 16 +- packages/search/lib/commands/TAGVALS.spec.ts | 3 +- packages/search/lib/commands/TAGVALS.ts | 9 +- packages/search/lib/commands/_LIST.spec.ts | 3 +- packages/search/lib/commands/_LIST.ts | 9 +- packages/test-utils/lib/dockers.ts | 2 +- packages/time-series/lib/commands/ADD.spec.ts | 17 +- packages/time-series/lib/commands/ADD.ts | 40 +- .../time-series/lib/commands/ALTER.spec.ts | 15 +- packages/time-series/lib/commands/ALTER.ts | 25 +- .../time-series/lib/commands/CREATE.spec.ts | 17 +- packages/time-series/lib/commands/CREATE.ts | 35 +- .../lib/commands/CREATERULE.spec.ts | 5 +- .../time-series/lib/commands/CREATERULE.ts | 22 +- .../time-series/lib/commands/DECRBY.spec.ts | 17 +- packages/time-series/lib/commands/DECRBY.ts | 12 +- packages/time-series/lib/commands/DEL.spec.ts | 3 +- packages/time-series/lib/commands/DEL.ts | 15 +- .../lib/commands/DELETERULE.spec.ts | 3 +- .../time-series/lib/commands/DELETERULE.ts | 13 +- packages/time-series/lib/commands/GET.spec.ts | 5 +- packages/time-series/lib/commands/GET.ts | 13 +- .../time-series/lib/commands/INCRBY.spec.ts | 19 +- packages/time-series/lib/commands/INCRBY.ts | 38 +- .../time-series/lib/commands/INFO.spec.ts | 3 +- packages/time-series/lib/commands/INFO.ts | 9 +- .../lib/commands/INFO_DEBUG.spec.ts | 3 +- .../time-series/lib/commands/INFO_DEBUG.ts | 14 +- .../time-series/lib/commands/MADD.spec.ts | 3 +- packages/time-series/lib/commands/MADD.ts | 17 +- .../time-series/lib/commands/MGET.spec.ts | 5 +- packages/time-series/lib/commands/MGET.ts | 26 +- .../lib/commands/MGET_SELECTED_LABELS.spec.ts | 3 +- .../lib/commands/MGET_SELECTED_LABELS.ts | 19 +- .../lib/commands/MGET_WITHLABELS.spec.ts | 3 +- .../lib/commands/MGET_WITHLABELS.ts | 17 +- .../time-series/lib/commands/MRANGE.spec.ts | 3 +- packages/time-series/lib/commands/MRANGE.ts | 21 +- .../lib/commands/MRANGE_GROUPBY.spec.ts | 3 +- .../lib/commands/MRANGE_GROUPBY.ts | 33 +- .../commands/MRANGE_SELECTED_LABELS.spec.ts | 3 +- .../lib/commands/MRANGE_SELECTED_LABELS.ts | 24 +- .../MRANGE_SELECTED_LABELS_GROUPBY.spec.ts | 3 +- .../MRANGE_SELECTED_LABELS_GROUPBY.ts | 30 +- .../lib/commands/MRANGE_WITHLABELS.spec.ts | 3 +- .../lib/commands/MRANGE_WITHLABELS.ts | 23 +- .../MRANGE_WITHLABELS_GROUPBY.spec.ts | 3 +- .../lib/commands/MRANGE_WITHLABELS_GROUPBY.ts | 30 +- .../lib/commands/MREVRANGE.spec.ts | 3 +- .../time-series/lib/commands/MREVRANGE.ts | 6 +- .../lib/commands/MREVRANGE_GROUPBY.spec.ts | 3 +- .../lib/commands/MREVRANGE_GROUPBY.ts | 5 +- .../MREVRANGE_SELECTED_LABELS.spec.ts | 3 +- .../lib/commands/MREVRANGE_SELECTED_LABELS.ts | 5 +- .../MREVRANGE_SELECTED_LABELS_GROUPBY.spec.ts | 3 +- .../MREVRANGE_SELECTED_LABELS_GROUPBY.ts | 5 +- .../lib/commands/MREVRANGE_WITHLABELS.spec.ts | 3 +- .../lib/commands/MREVRANGE_WITHLABELS.ts | 6 +- .../MREVRANGE_WITHLABELS_GROUPBY.spec.ts | 3 +- .../commands/MREVRANGE_WITHLABELS_GROUPBY.ts | 5 +- .../lib/commands/QUERYINDEX.spec.ts | 5 +- .../time-series/lib/commands/QUERYINDEX.ts | 12 +- .../time-series/lib/commands/RANGE.spec.ts | 3 +- packages/time-series/lib/commands/RANGE.ts | 49 +- .../time-series/lib/commands/REVRANGE.spec.ts | 3 +- packages/time-series/lib/commands/REVRANGE.ts | 10 +- .../time-series/lib/commands/index.spec.ts | 2 +- packages/time-series/lib/commands/index.ts | 41 +- 1016 files changed, 6345 insertions(+), 6540 deletions(-) create mode 100644 packages/client/lib/client/parser.ts diff --git a/package-lock.json b/package-lock.json index aefd0678434..ba18a98b6a5 100644 --- a/package-lock.json +++ b/package-lock.json @@ -23,9 +23,8 @@ }, "node_modules/@ampproject/remapping": { "version": "2.2.1", - "resolved": "https://registry.npmjs.org/@ampproject/remapping/-/remapping-2.2.1.tgz", - "integrity": "sha512-lFMjJTrFL3j7L9yBxwYfCq2k6qqwHyzuUl/XBnif78PWTJYyL/dfowQHWE3sp6U6ZzqWiiIZnpTMO96zhkjwtg==", "dev": true, + "license": "Apache-2.0", "dependencies": { "@jridgewell/gen-mapping": "^0.3.0", "@jridgewell/trace-mapping": "^0.3.9" @@ -36,9 +35,8 @@ }, "node_modules/@babel/code-frame": { "version": "7.23.5", - "resolved": "https://registry.npmjs.org/@babel/code-frame/-/code-frame-7.23.5.tgz", - "integrity": "sha512-CgH3s1a96LipHCmSUmYFPwY7MNx8C3avkq7i4Wl3cfa662ldtUe4VM1TPXX70pfmrlWTb6jLqTYrZyT2ZTJBgA==", "dev": true, + "license": "MIT", "dependencies": { "@babel/highlight": "^7.23.4", "chalk": "^2.4.2" @@ -49,9 +47,8 @@ }, "node_modules/@babel/code-frame/node_modules/ansi-styles": { "version": "3.2.1", - "resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-3.2.1.tgz", - "integrity": "sha512-VT0ZI6kZRdTh8YyJw3SMbYm/u+NqfsAxEpWO0Pf9sq8/e94WxxOpPKx9FR1FlyCtOVDNOQ+8ntlqFxiRc+r5qA==", "dev": true, + "license": "MIT", "dependencies": { "color-convert": "^1.9.0" }, @@ -61,9 +58,8 @@ }, "node_modules/@babel/code-frame/node_modules/chalk": { "version": "2.4.2", - "resolved": "https://registry.npmjs.org/chalk/-/chalk-2.4.2.tgz", - "integrity": "sha512-Mti+f9lpJNcwF4tWV8/OrTTtF1gZi+f8FqlyAdouralcFWFQWF2+NgCHShjkCb+IFBLq9buZwE1xckQU4peSuQ==", "dev": true, + "license": "MIT", "dependencies": { "ansi-styles": "^3.2.1", "escape-string-regexp": "^1.0.5", @@ -75,42 +71,37 @@ }, "node_modules/@babel/code-frame/node_modules/color-convert": { "version": "1.9.3", - "resolved": "https://registry.npmjs.org/color-convert/-/color-convert-1.9.3.tgz", - "integrity": "sha512-QfAUtd+vFdAtFQcC8CCyYt1fYWxSqAiK2cSD6zDB8N3cpsEBAvRxp9zOGg6G/SHHJYAT88/az/IuDGALsNVbGg==", "dev": true, + "license": "MIT", "dependencies": { "color-name": "1.1.3" } }, "node_modules/@babel/code-frame/node_modules/color-name": { "version": "1.1.3", - "resolved": "https://registry.npmjs.org/color-name/-/color-name-1.1.3.tgz", - "integrity": "sha512-72fSenhMw2HZMTVHeCA9KCmpEIbzWiQsjN+BHcBbS9vr1mtt+vJjPdksIBNUmKAW8TFUDPJK5SUU3QhE9NEXDw==", - "dev": true + "dev": true, + "license": "MIT" }, "node_modules/@babel/code-frame/node_modules/escape-string-regexp": { "version": "1.0.5", - "resolved": "https://registry.npmjs.org/escape-string-regexp/-/escape-string-regexp-1.0.5.tgz", - "integrity": "sha512-vbRorB5FUQWvla16U8R/qgaFIya2qGzwDrNmCZuYKrbdSUMG6I1ZCGQRefkRVhuOkIGVne7BQ35DSfo1qvJqFg==", "dev": true, + "license": "MIT", "engines": { "node": ">=0.8.0" } }, "node_modules/@babel/code-frame/node_modules/has-flag": { "version": "3.0.0", - "resolved": "https://registry.npmjs.org/has-flag/-/has-flag-3.0.0.tgz", - "integrity": "sha512-sKJf1+ceQBr4SMkvQnBDNDtf4TXpVhVGateu0t918bl30FnbE2m4vNLX+VWe/dpjlb+HugGYzW7uQXH98HPEYw==", "dev": true, + "license": "MIT", "engines": { "node": ">=4" } }, "node_modules/@babel/code-frame/node_modules/supports-color": { "version": "5.5.0", - "resolved": "https://registry.npmjs.org/supports-color/-/supports-color-5.5.0.tgz", - "integrity": "sha512-QjVjwdXIt408MIiAqCX4oUKsgU2EqAGzs2Ppkm4aQYbjm+ZEWEcW4SfFNTr4uMNZma0ey4f5lgLrkB0aX0QMow==", "dev": true, + "license": "MIT", "dependencies": { "has-flag": "^3.0.0" }, @@ -120,18 +111,16 @@ }, "node_modules/@babel/compat-data": { "version": "7.23.5", - "resolved": "https://registry.npmjs.org/@babel/compat-data/-/compat-data-7.23.5.tgz", - "integrity": "sha512-uU27kfDRlhfKl+w1U6vp16IuvSLtjAxdArVXPa9BvLkrr7CYIsxH5adpHObeAGY/41+syctUWOZ140a2Rvkgjw==", "dev": true, + "license": "MIT", "engines": { "node": ">=6.9.0" } }, "node_modules/@babel/core": { "version": "7.23.9", - "resolved": "https://registry.npmjs.org/@babel/core/-/core-7.23.9.tgz", - "integrity": "sha512-5q0175NOjddqpvvzU+kDiSOAk4PfdO6FvwCWoQ6RO7rTzEe8vlo+4HVfcnAREhD4npMs0e9uZypjTwzZPCf/cw==", "dev": true, + "license": "MIT", "dependencies": { "@ampproject/remapping": "^2.2.0", "@babel/code-frame": "^7.23.5", @@ -159,15 +148,13 @@ }, "node_modules/@babel/core/node_modules/convert-source-map": { "version": "2.0.0", - "resolved": "https://registry.npmjs.org/convert-source-map/-/convert-source-map-2.0.0.tgz", - "integrity": "sha512-Kvp459HrV2FEJ1CAsi1Ku+MY3kasH19TFykTz2xWmMeq6bk2NU3XXvfJ+Q61m0xktWwt+1HSYf3JZsTms3aRJg==", - "dev": true + "dev": true, + "license": "MIT" }, "node_modules/@babel/generator": { "version": "7.23.6", - "resolved": "https://registry.npmjs.org/@babel/generator/-/generator-7.23.6.tgz", - "integrity": "sha512-qrSfCYxYQB5owCmGLbl8XRpX1ytXlpueOb0N0UmQwA073KZxejgQTzAmJezxvpwQD9uGtK2shHdi55QT+MbjIw==", "dev": true, + "license": "MIT", "dependencies": { "@babel/types": "^7.23.6", "@jridgewell/gen-mapping": "^0.3.2", @@ -180,9 +167,8 @@ }, "node_modules/@babel/helper-compilation-targets": { "version": "7.23.6", - "resolved": "https://registry.npmjs.org/@babel/helper-compilation-targets/-/helper-compilation-targets-7.23.6.tgz", - "integrity": "sha512-9JB548GZoQVmzrFgp8o7KxdgkTGm6xs9DW0o/Pim72UDjzr5ObUQ6ZzYPqA+g9OTS2bBQoctLJrky0RDCAWRgQ==", "dev": true, + "license": "MIT", "dependencies": { "@babel/compat-data": "^7.23.5", "@babel/helper-validator-option": "^7.23.5", @@ -196,18 +182,16 @@ }, "node_modules/@babel/helper-environment-visitor": { "version": "7.22.20", - "resolved": "https://registry.npmjs.org/@babel/helper-environment-visitor/-/helper-environment-visitor-7.22.20.tgz", - "integrity": "sha512-zfedSIzFhat/gFhWfHtgWvlec0nqB9YEIVrpuwjruLlXfUSnA8cJB0miHKwqDnQ7d32aKo2xt88/xZptwxbfhA==", "dev": true, + "license": "MIT", "engines": { "node": ">=6.9.0" } }, "node_modules/@babel/helper-function-name": { "version": "7.23.0", - "resolved": "https://registry.npmjs.org/@babel/helper-function-name/-/helper-function-name-7.23.0.tgz", - "integrity": "sha512-OErEqsrxjZTJciZ4Oo+eoZqeW9UIiOcuYKRJA4ZAgV9myA+pOXhhmpfNCKjEH/auVfEYVFJ6y1Tc4r0eIApqiw==", "dev": true, + "license": "MIT", "dependencies": { "@babel/template": "^7.22.15", "@babel/types": "^7.23.0" @@ -218,9 +202,8 @@ }, "node_modules/@babel/helper-hoist-variables": { "version": "7.22.5", - "resolved": "https://registry.npmjs.org/@babel/helper-hoist-variables/-/helper-hoist-variables-7.22.5.tgz", - "integrity": "sha512-wGjk9QZVzvknA6yKIUURb8zY3grXCcOZt+/7Wcy8O2uctxhplmUPkOdlgoNhmdVee2c92JXbf1xpMtVNbfoxRw==", "dev": true, + "license": "MIT", "dependencies": { "@babel/types": "^7.22.5" }, @@ -230,9 +213,8 @@ }, "node_modules/@babel/helper-module-imports": { "version": "7.22.15", - "resolved": "https://registry.npmjs.org/@babel/helper-module-imports/-/helper-module-imports-7.22.15.tgz", - "integrity": "sha512-0pYVBnDKZO2fnSPCrgM/6WMc7eS20Fbok+0r88fp+YtWVLZrp4CkafFGIp+W0VKw4a22sgebPT99y+FDNMdP4w==", "dev": true, + "license": "MIT", "dependencies": { "@babel/types": "^7.22.15" }, @@ -242,9 +224,8 @@ }, "node_modules/@babel/helper-module-transforms": { "version": "7.23.3", - "resolved": "https://registry.npmjs.org/@babel/helper-module-transforms/-/helper-module-transforms-7.23.3.tgz", - "integrity": "sha512-7bBs4ED9OmswdfDzpz4MpWgSrV7FXlc3zIagvLFjS5H+Mk7Snr21vQ6QwrsoCGMfNC4e4LQPdoULEt4ykz0SRQ==", "dev": true, + "license": "MIT", "dependencies": { "@babel/helper-environment-visitor": "^7.22.20", "@babel/helper-module-imports": "^7.22.15", @@ -261,9 +242,8 @@ }, "node_modules/@babel/helper-simple-access": { "version": "7.22.5", - "resolved": "https://registry.npmjs.org/@babel/helper-simple-access/-/helper-simple-access-7.22.5.tgz", - "integrity": "sha512-n0H99E/K+Bika3++WNL17POvo4rKWZ7lZEp1Q+fStVbUi8nxPQEBOlTmCOxW/0JsS56SKKQ+ojAe2pHKJHN35w==", "dev": true, + "license": "MIT", "dependencies": { "@babel/types": "^7.22.5" }, @@ -273,9 +253,8 @@ }, "node_modules/@babel/helper-split-export-declaration": { "version": "7.22.6", - "resolved": "https://registry.npmjs.org/@babel/helper-split-export-declaration/-/helper-split-export-declaration-7.22.6.tgz", - "integrity": "sha512-AsUnxuLhRYsisFiaJwvp1QF+I3KjD5FOxut14q/GzovUe6orHLesW2C7d754kRm53h5gqrz6sFl6sxc4BVtE/g==", "dev": true, + "license": "MIT", "dependencies": { "@babel/types": "^7.22.5" }, @@ -285,36 +264,32 @@ }, "node_modules/@babel/helper-string-parser": { "version": "7.23.4", - "resolved": "https://registry.npmjs.org/@babel/helper-string-parser/-/helper-string-parser-7.23.4.tgz", - "integrity": "sha512-803gmbQdqwdf4olxrX4AJyFBV/RTr3rSmOj0rKwesmzlfhYNDEs+/iOcznzpNWlJlIlTJC2QfPFcHB6DlzdVLQ==", "dev": true, + "license": "MIT", "engines": { "node": ">=6.9.0" } }, "node_modules/@babel/helper-validator-identifier": { "version": "7.22.20", - "resolved": "https://registry.npmjs.org/@babel/helper-validator-identifier/-/helper-validator-identifier-7.22.20.tgz", - "integrity": "sha512-Y4OZ+ytlatR8AI+8KZfKuL5urKp7qey08ha31L8b3BwewJAoJamTzyvxPR/5D+KkdJCGPq/+8TukHBlY10FX9A==", "dev": true, + "license": "MIT", "engines": { "node": ">=6.9.0" } }, "node_modules/@babel/helper-validator-option": { "version": "7.23.5", - "resolved": "https://registry.npmjs.org/@babel/helper-validator-option/-/helper-validator-option-7.23.5.tgz", - "integrity": "sha512-85ttAOMLsr53VgXkTbkx8oA6YTfT4q7/HzXSLEYmjcSTJPMPQtvq1BD79Byep5xMUYbGRzEpDsjUf3dyp54IKw==", "dev": true, + "license": "MIT", "engines": { "node": ">=6.9.0" } }, "node_modules/@babel/helpers": { "version": "7.23.9", - "resolved": "https://registry.npmjs.org/@babel/helpers/-/helpers-7.23.9.tgz", - "integrity": "sha512-87ICKgU5t5SzOT7sBMfCOZQ2rHjRU+Pcb9BoILMYz600W6DkVRLFBPwQ18gwUVvggqXivaUakpnxWQGbpywbBQ==", "dev": true, + "license": "MIT", "dependencies": { "@babel/template": "^7.23.9", "@babel/traverse": "^7.23.9", @@ -326,9 +301,8 @@ }, "node_modules/@babel/highlight": { "version": "7.23.4", - "resolved": "https://registry.npmjs.org/@babel/highlight/-/highlight-7.23.4.tgz", - "integrity": "sha512-acGdbYSfp2WheJoJm/EBBBLh/ID8KDc64ISZ9DYtBmC8/Q204PZJLHyzeB5qMzJ5trcOkybd78M4x2KWsUq++A==", "dev": true, + "license": "MIT", "dependencies": { "@babel/helper-validator-identifier": "^7.22.20", "chalk": "^2.4.2", @@ -340,9 +314,8 @@ }, "node_modules/@babel/highlight/node_modules/ansi-styles": { "version": "3.2.1", - "resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-3.2.1.tgz", - "integrity": "sha512-VT0ZI6kZRdTh8YyJw3SMbYm/u+NqfsAxEpWO0Pf9sq8/e94WxxOpPKx9FR1FlyCtOVDNOQ+8ntlqFxiRc+r5qA==", "dev": true, + "license": "MIT", "dependencies": { "color-convert": "^1.9.0" }, @@ -352,9 +325,8 @@ }, "node_modules/@babel/highlight/node_modules/chalk": { "version": "2.4.2", - "resolved": "https://registry.npmjs.org/chalk/-/chalk-2.4.2.tgz", - "integrity": "sha512-Mti+f9lpJNcwF4tWV8/OrTTtF1gZi+f8FqlyAdouralcFWFQWF2+NgCHShjkCb+IFBLq9buZwE1xckQU4peSuQ==", "dev": true, + "license": "MIT", "dependencies": { "ansi-styles": "^3.2.1", "escape-string-regexp": "^1.0.5", @@ -366,42 +338,37 @@ }, "node_modules/@babel/highlight/node_modules/color-convert": { "version": "1.9.3", - "resolved": "https://registry.npmjs.org/color-convert/-/color-convert-1.9.3.tgz", - "integrity": "sha512-QfAUtd+vFdAtFQcC8CCyYt1fYWxSqAiK2cSD6zDB8N3cpsEBAvRxp9zOGg6G/SHHJYAT88/az/IuDGALsNVbGg==", "dev": true, + "license": "MIT", "dependencies": { "color-name": "1.1.3" } }, "node_modules/@babel/highlight/node_modules/color-name": { "version": "1.1.3", - "resolved": "https://registry.npmjs.org/color-name/-/color-name-1.1.3.tgz", - "integrity": "sha512-72fSenhMw2HZMTVHeCA9KCmpEIbzWiQsjN+BHcBbS9vr1mtt+vJjPdksIBNUmKAW8TFUDPJK5SUU3QhE9NEXDw==", - "dev": true + "dev": true, + "license": "MIT" }, "node_modules/@babel/highlight/node_modules/escape-string-regexp": { "version": "1.0.5", - "resolved": "https://registry.npmjs.org/escape-string-regexp/-/escape-string-regexp-1.0.5.tgz", - "integrity": "sha512-vbRorB5FUQWvla16U8R/qgaFIya2qGzwDrNmCZuYKrbdSUMG6I1ZCGQRefkRVhuOkIGVne7BQ35DSfo1qvJqFg==", "dev": true, + "license": "MIT", "engines": { "node": ">=0.8.0" } }, "node_modules/@babel/highlight/node_modules/has-flag": { "version": "3.0.0", - "resolved": "https://registry.npmjs.org/has-flag/-/has-flag-3.0.0.tgz", - "integrity": "sha512-sKJf1+ceQBr4SMkvQnBDNDtf4TXpVhVGateu0t918bl30FnbE2m4vNLX+VWe/dpjlb+HugGYzW7uQXH98HPEYw==", "dev": true, + "license": "MIT", "engines": { "node": ">=4" } }, "node_modules/@babel/highlight/node_modules/supports-color": { "version": "5.5.0", - "resolved": "https://registry.npmjs.org/supports-color/-/supports-color-5.5.0.tgz", - "integrity": "sha512-QjVjwdXIt408MIiAqCX4oUKsgU2EqAGzs2Ppkm4aQYbjm+ZEWEcW4SfFNTr4uMNZma0ey4f5lgLrkB0aX0QMow==", "dev": true, + "license": "MIT", "dependencies": { "has-flag": "^3.0.0" }, @@ -411,9 +378,8 @@ }, "node_modules/@babel/parser": { "version": "7.23.9", - "resolved": "https://registry.npmjs.org/@babel/parser/-/parser-7.23.9.tgz", - "integrity": "sha512-9tcKgqKbs3xGJ+NtKF2ndOBBLVwPjl1SHxPQkd36r3Dlirw3xWUeGaTbqr7uGZcTaxkVNwc+03SVP7aCdWrTlA==", "dev": true, + "license": "MIT", "bin": { "parser": "bin/babel-parser.js" }, @@ -423,9 +389,8 @@ }, "node_modules/@babel/template": { "version": "7.23.9", - "resolved": "https://registry.npmjs.org/@babel/template/-/template-7.23.9.tgz", - "integrity": "sha512-+xrD2BWLpvHKNmX2QbpdpsBaWnRxahMwJjO+KZk2JOElj5nSmKezyS1B4u+QbHMTX69t4ukm6hh9lsYQ7GHCKA==", "dev": true, + "license": "MIT", "dependencies": { "@babel/code-frame": "^7.23.5", "@babel/parser": "^7.23.9", @@ -437,9 +402,8 @@ }, "node_modules/@babel/traverse": { "version": "7.23.9", - "resolved": "https://registry.npmjs.org/@babel/traverse/-/traverse-7.23.9.tgz", - "integrity": "sha512-I/4UJ9vs90OkBtY6iiiTORVMyIhJ4kAVmsKo9KFc8UOxMeUfi2hvtIBsET5u9GizXE6/GFSuKCTNfgCswuEjRg==", "dev": true, + "license": "MIT", "dependencies": { "@babel/code-frame": "^7.23.5", "@babel/generator": "^7.23.6", @@ -458,9 +422,8 @@ }, "node_modules/@babel/types": { "version": "7.23.9", - "resolved": "https://registry.npmjs.org/@babel/types/-/types-7.23.9.tgz", - "integrity": "sha512-dQjSq/7HaSjRM43FFGnv5keM2HsxpmyV1PfaSVm0nzzjwwTmjOe6J4bC8e3+pTEIgHaHj+1ZlLThRJ2auc/w1Q==", "dev": true, + "license": "MIT", "dependencies": { "@babel/helper-string-parser": "^7.23.4", "@babel/helper-validator-identifier": "^7.22.20", @@ -470,270 +433,13 @@ "node": ">=6.9.0" } }, - "node_modules/@esbuild/aix-ppc64": { - "version": "0.19.12", - "resolved": "https://registry.npmjs.org/@esbuild/aix-ppc64/-/aix-ppc64-0.19.12.tgz", - "integrity": "sha512-bmoCYyWdEL3wDQIVbcyzRyeKLgk2WtWLTWz1ZIAZF/EGbNOwSA6ew3PftJ1PqMiOOGu0OyFMzG53L0zqIpPeNA==", - "cpu": [ - "ppc64" - ], - "dev": true, - "optional": true, - "os": [ - "aix" - ], - "engines": { - "node": ">=12" - } - }, - "node_modules/@esbuild/android-arm": { - "version": "0.19.12", - "resolved": "https://registry.npmjs.org/@esbuild/android-arm/-/android-arm-0.19.12.tgz", - "integrity": "sha512-qg/Lj1mu3CdQlDEEiWrlC4eaPZ1KztwGJ9B6J+/6G+/4ewxJg7gqj8eVYWvao1bXrqGiW2rsBZFSX3q2lcW05w==", - "cpu": [ - "arm" - ], - "dev": true, - "optional": true, - "os": [ - "android" - ], - "engines": { - "node": ">=12" - } - }, - "node_modules/@esbuild/android-arm64": { - "version": "0.19.12", - "resolved": "https://registry.npmjs.org/@esbuild/android-arm64/-/android-arm64-0.19.12.tgz", - "integrity": "sha512-P0UVNGIienjZv3f5zq0DP3Nt2IE/3plFzuaS96vihvD0Hd6H/q4WXUGpCxD/E8YrSXfNyRPbpTq+T8ZQioSuPA==", - "cpu": [ - "arm64" - ], - "dev": true, - "optional": true, - "os": [ - "android" - ], - "engines": { - "node": ">=12" - } - }, - "node_modules/@esbuild/android-x64": { - "version": "0.19.12", - "resolved": "https://registry.npmjs.org/@esbuild/android-x64/-/android-x64-0.19.12.tgz", - "integrity": "sha512-3k7ZoUW6Q6YqhdhIaq/WZ7HwBpnFBlW905Fa4s4qWJyiNOgT1dOqDiVAQFwBH7gBRZr17gLrlFCRzF6jFh7Kew==", - "cpu": [ - "x64" - ], - "dev": true, - "optional": true, - "os": [ - "android" - ], - "engines": { - "node": ">=12" - } - }, - "node_modules/@esbuild/darwin-arm64": { - "version": "0.19.12", - "resolved": "https://registry.npmjs.org/@esbuild/darwin-arm64/-/darwin-arm64-0.19.12.tgz", - "integrity": "sha512-B6IeSgZgtEzGC42jsI+YYu9Z3HKRxp8ZT3cqhvliEHovq8HSX2YX8lNocDn79gCKJXOSaEot9MVYky7AKjCs8g==", - "cpu": [ - "arm64" - ], - "dev": true, - "optional": true, - "os": [ - "darwin" - ], - "engines": { - "node": ">=12" - } - }, - "node_modules/@esbuild/darwin-x64": { - "version": "0.19.12", - "resolved": "https://registry.npmjs.org/@esbuild/darwin-x64/-/darwin-x64-0.19.12.tgz", - "integrity": "sha512-hKoVkKzFiToTgn+41qGhsUJXFlIjxI/jSYeZf3ugemDYZldIXIxhvwN6erJGlX4t5h417iFuheZ7l+YVn05N3A==", - "cpu": [ - "x64" - ], - "dev": true, - "optional": true, - "os": [ - "darwin" - ], - "engines": { - "node": ">=12" - } - }, - "node_modules/@esbuild/freebsd-arm64": { - "version": "0.19.12", - "resolved": "https://registry.npmjs.org/@esbuild/freebsd-arm64/-/freebsd-arm64-0.19.12.tgz", - "integrity": "sha512-4aRvFIXmwAcDBw9AueDQ2YnGmz5L6obe5kmPT8Vd+/+x/JMVKCgdcRwH6APrbpNXsPz+K653Qg8HB/oXvXVukA==", - "cpu": [ - "arm64" - ], - "dev": true, - "optional": true, - "os": [ - "freebsd" - ], - "engines": { - "node": ">=12" - } - }, - "node_modules/@esbuild/freebsd-x64": { - "version": "0.19.12", - "resolved": "https://registry.npmjs.org/@esbuild/freebsd-x64/-/freebsd-x64-0.19.12.tgz", - "integrity": "sha512-EYoXZ4d8xtBoVN7CEwWY2IN4ho76xjYXqSXMNccFSx2lgqOG/1TBPW0yPx1bJZk94qu3tX0fycJeeQsKovA8gg==", - "cpu": [ - "x64" - ], - "dev": true, - "optional": true, - "os": [ - "freebsd" - ], - "engines": { - "node": ">=12" - } - }, - "node_modules/@esbuild/linux-arm": { - "version": "0.19.12", - "resolved": "https://registry.npmjs.org/@esbuild/linux-arm/-/linux-arm-0.19.12.tgz", - "integrity": "sha512-J5jPms//KhSNv+LO1S1TX1UWp1ucM6N6XuL6ITdKWElCu8wXP72l9MM0zDTzzeikVyqFE6U8YAV9/tFyj0ti+w==", - "cpu": [ - "arm" - ], - "dev": true, - "optional": true, - "os": [ - "linux" - ], - "engines": { - "node": ">=12" - } - }, - "node_modules/@esbuild/linux-arm64": { - "version": "0.19.12", - "resolved": "https://registry.npmjs.org/@esbuild/linux-arm64/-/linux-arm64-0.19.12.tgz", - "integrity": "sha512-EoTjyYyLuVPfdPLsGVVVC8a0p1BFFvtpQDB/YLEhaXyf/5bczaGeN15QkR+O4S5LeJ92Tqotve7i1jn35qwvdA==", - "cpu": [ - "arm64" - ], - "dev": true, - "optional": true, - "os": [ - "linux" - ], - "engines": { - "node": ">=12" - } - }, - "node_modules/@esbuild/linux-ia32": { - "version": "0.19.12", - "resolved": "https://registry.npmjs.org/@esbuild/linux-ia32/-/linux-ia32-0.19.12.tgz", - "integrity": "sha512-Thsa42rrP1+UIGaWz47uydHSBOgTUnwBwNq59khgIwktK6x60Hivfbux9iNR0eHCHzOLjLMLfUMLCypBkZXMHA==", - "cpu": [ - "ia32" - ], - "dev": true, - "optional": true, - "os": [ - "linux" - ], - "engines": { - "node": ">=12" - } - }, - "node_modules/@esbuild/linux-loong64": { - "version": "0.19.12", - "resolved": "https://registry.npmjs.org/@esbuild/linux-loong64/-/linux-loong64-0.19.12.tgz", - "integrity": "sha512-LiXdXA0s3IqRRjm6rV6XaWATScKAXjI4R4LoDlvO7+yQqFdlr1Bax62sRwkVvRIrwXxvtYEHHI4dm50jAXkuAA==", - "cpu": [ - "loong64" - ], - "dev": true, - "optional": true, - "os": [ - "linux" - ], - "engines": { - "node": ">=12" - } - }, - "node_modules/@esbuild/linux-mips64el": { - "version": "0.19.12", - "resolved": "https://registry.npmjs.org/@esbuild/linux-mips64el/-/linux-mips64el-0.19.12.tgz", - "integrity": "sha512-fEnAuj5VGTanfJ07ff0gOA6IPsvrVHLVb6Lyd1g2/ed67oU1eFzL0r9WL7ZzscD+/N6i3dWumGE1Un4f7Amf+w==", - "cpu": [ - "mips64el" - ], - "dev": true, - "optional": true, - "os": [ - "linux" - ], - "engines": { - "node": ">=12" - } - }, - "node_modules/@esbuild/linux-ppc64": { - "version": "0.19.12", - "resolved": "https://registry.npmjs.org/@esbuild/linux-ppc64/-/linux-ppc64-0.19.12.tgz", - "integrity": "sha512-nYJA2/QPimDQOh1rKWedNOe3Gfc8PabU7HT3iXWtNUbRzXS9+vgB0Fjaqr//XNbd82mCxHzik2qotuI89cfixg==", - "cpu": [ - "ppc64" - ], - "dev": true, - "optional": true, - "os": [ - "linux" - ], - "engines": { - "node": ">=12" - } - }, - "node_modules/@esbuild/linux-riscv64": { - "version": "0.19.12", - "resolved": "https://registry.npmjs.org/@esbuild/linux-riscv64/-/linux-riscv64-0.19.12.tgz", - "integrity": "sha512-2MueBrlPQCw5dVJJpQdUYgeqIzDQgw3QtiAHUC4RBz9FXPrskyyU3VI1hw7C0BSKB9OduwSJ79FTCqtGMWqJHg==", - "cpu": [ - "riscv64" - ], - "dev": true, - "optional": true, - "os": [ - "linux" - ], - "engines": { - "node": ">=12" - } - }, - "node_modules/@esbuild/linux-s390x": { - "version": "0.19.12", - "resolved": "https://registry.npmjs.org/@esbuild/linux-s390x/-/linux-s390x-0.19.12.tgz", - "integrity": "sha512-+Pil1Nv3Umes4m3AZKqA2anfhJiVmNCYkPchwFJNEJN5QxmTs1uzyy4TvmDrCRNT2ApwSari7ZIgrPeUx4UZDg==", - "cpu": [ - "s390x" - ], - "dev": true, - "optional": true, - "os": [ - "linux" - ], - "engines": { - "node": ">=12" - } - }, "node_modules/@esbuild/linux-x64": { "version": "0.19.12", - "resolved": "https://registry.npmjs.org/@esbuild/linux-x64/-/linux-x64-0.19.12.tgz", - "integrity": "sha512-B71g1QpxfwBvNrfyJdVDexenDIt1CiDN1TIXLbhOw0KhJzE78KIFGX6OJ9MrtC0oOqMWf+0xop4qEU8JrJTwCg==", "cpu": [ "x64" ], "dev": true, + "license": "MIT", "optional": true, "os": [ "linux" @@ -742,113 +448,15 @@ "node": ">=12" } }, - "node_modules/@esbuild/netbsd-x64": { - "version": "0.19.12", - "resolved": "https://registry.npmjs.org/@esbuild/netbsd-x64/-/netbsd-x64-0.19.12.tgz", - "integrity": "sha512-3ltjQ7n1owJgFbuC61Oj++XhtzmymoCihNFgT84UAmJnxJfm4sYCiSLTXZtE00VWYpPMYc+ZQmB6xbSdVh0JWA==", - "cpu": [ - "x64" - ], - "dev": true, - "optional": true, - "os": [ - "netbsd" - ], - "engines": { - "node": ">=12" - } - }, - "node_modules/@esbuild/openbsd-x64": { - "version": "0.19.12", - "resolved": "https://registry.npmjs.org/@esbuild/openbsd-x64/-/openbsd-x64-0.19.12.tgz", - "integrity": "sha512-RbrfTB9SWsr0kWmb9srfF+L933uMDdu9BIzdA7os2t0TXhCRjrQyCeOt6wVxr79CKD4c+p+YhCj31HBkYcXebw==", - "cpu": [ - "x64" - ], - "dev": true, - "optional": true, - "os": [ - "openbsd" - ], - "engines": { - "node": ">=12" - } - }, - "node_modules/@esbuild/sunos-x64": { - "version": "0.19.12", - "resolved": "https://registry.npmjs.org/@esbuild/sunos-x64/-/sunos-x64-0.19.12.tgz", - "integrity": "sha512-HKjJwRrW8uWtCQnQOz9qcU3mUZhTUQvi56Q8DPTLLB+DawoiQdjsYq+j+D3s9I8VFtDr+F9CjgXKKC4ss89IeA==", - "cpu": [ - "x64" - ], - "dev": true, - "optional": true, - "os": [ - "sunos" - ], - "engines": { - "node": ">=12" - } - }, - "node_modules/@esbuild/win32-arm64": { - "version": "0.19.12", - "resolved": "https://registry.npmjs.org/@esbuild/win32-arm64/-/win32-arm64-0.19.12.tgz", - "integrity": "sha512-URgtR1dJnmGvX864pn1B2YUYNzjmXkuJOIqG2HdU62MVS4EHpU2946OZoTMnRUHklGtJdJZ33QfzdjGACXhn1A==", - "cpu": [ - "arm64" - ], - "dev": true, - "optional": true, - "os": [ - "win32" - ], - "engines": { - "node": ">=12" - } - }, - "node_modules/@esbuild/win32-ia32": { - "version": "0.19.12", - "resolved": "https://registry.npmjs.org/@esbuild/win32-ia32/-/win32-ia32-0.19.12.tgz", - "integrity": "sha512-+ZOE6pUkMOJfmxmBZElNOx72NKpIa/HFOMGzu8fqzQJ5kgf6aTGrcJaFsNiVMH4JKpMipyK+7k0n2UXN7a8YKQ==", - "cpu": [ - "ia32" - ], - "dev": true, - "optional": true, - "os": [ - "win32" - ], - "engines": { - "node": ">=12" - } - }, - "node_modules/@esbuild/win32-x64": { - "version": "0.19.12", - "resolved": "https://registry.npmjs.org/@esbuild/win32-x64/-/win32-x64-0.19.12.tgz", - "integrity": "sha512-T1QyPSDCyMXaO3pzBkF96E8xMkiRYbUEZADd29SyPGabqxMViNoii+NcK7eWJAEoU6RZyEm5lVSIjTmcdoB9HA==", - "cpu": [ - "x64" - ], - "dev": true, - "optional": true, - "os": [ - "win32" - ], - "engines": { - "node": ">=12" - } - }, "node_modules/@iarna/toml": { "version": "2.2.5", - "resolved": "https://registry.npmjs.org/@iarna/toml/-/toml-2.2.5.tgz", - "integrity": "sha512-trnsAYxU3xnS1gPHPyU961coFyLkh4gAD/0zQ5mymY4yOZ+CYvsPqUbOFSw0aDM4y0tV7tiFxL/1XfXPNC6IPg==", - "dev": true + "dev": true, + "license": "ISC" }, "node_modules/@istanbuljs/load-nyc-config": { "version": "1.1.0", - "resolved": "https://registry.npmjs.org/@istanbuljs/load-nyc-config/-/load-nyc-config-1.1.0.tgz", - "integrity": "sha512-VjeHSlIzpv/NyD3N0YuHfXOPDIixcA1q2ZV98wsMqcYlPmv2n3Yb2lYP9XMElnaFVXg5A7YLTeLu6V84uQDjmQ==", "dev": true, + "license": "ISC", "dependencies": { "camelcase": "^5.3.1", "find-up": "^4.1.0", @@ -862,18 +470,16 @@ }, "node_modules/@istanbuljs/load-nyc-config/node_modules/argparse": { "version": "1.0.10", - "resolved": "https://registry.npmjs.org/argparse/-/argparse-1.0.10.tgz", - "integrity": "sha512-o5Roy6tNG4SL/FOkCAN6RzjiakZS25RLYFrcMttJqbdd8BWrnA+fGz57iN5Pb06pvBGvl5gQ0B48dJlslXvoTg==", "dev": true, + "license": "MIT", "dependencies": { "sprintf-js": "~1.0.2" } }, "node_modules/@istanbuljs/load-nyc-config/node_modules/find-up": { "version": "4.1.0", - "resolved": "https://registry.npmjs.org/find-up/-/find-up-4.1.0.tgz", - "integrity": "sha512-PpOwAdQ/YlXQ2vj8a3h8IipDuYRi3wceVQQGYWxNINccq40Anw7BlsEXCMbt1Zt+OLA6Fq9suIpIWD0OsnISlw==", "dev": true, + "license": "MIT", "dependencies": { "locate-path": "^5.0.0", "path-exists": "^4.0.0" @@ -884,9 +490,8 @@ }, "node_modules/@istanbuljs/load-nyc-config/node_modules/js-yaml": { "version": "3.14.1", - "resolved": "https://registry.npmjs.org/js-yaml/-/js-yaml-3.14.1.tgz", - "integrity": "sha512-okMH7OXXJ7YrN9Ok3/SXrnu4iX9yOk+25nqX4imS2npuvTYDmo/QEZoqwZkYaIDk3jVvBOTOIEgEhaLOynBS9g==", "dev": true, + "license": "MIT", "dependencies": { "argparse": "^1.0.7", "esprima": "^4.0.0" @@ -897,9 +502,8 @@ }, "node_modules/@istanbuljs/load-nyc-config/node_modules/locate-path": { "version": "5.0.0", - "resolved": "https://registry.npmjs.org/locate-path/-/locate-path-5.0.0.tgz", - "integrity": "sha512-t7hw9pI+WvuwNJXwk5zVHpyhIqzg2qTlklJOf0mVxGSbe3Fp2VieZcduNYjaLDoy6p9uGpQEGWG87WpMKlNq8g==", "dev": true, + "license": "MIT", "dependencies": { "p-locate": "^4.1.0" }, @@ -909,9 +513,8 @@ }, "node_modules/@istanbuljs/load-nyc-config/node_modules/p-limit": { "version": "2.3.0", - "resolved": "https://registry.npmjs.org/p-limit/-/p-limit-2.3.0.tgz", - "integrity": "sha512-//88mFWSJx8lxCzwdAABTJL2MyWB12+eIY7MDL2SqLmAkeKU9qxRvWuSyTjm3FUmpBEMuFfckAIqEaVGUDxb6w==", "dev": true, + "license": "MIT", "dependencies": { "p-try": "^2.0.0" }, @@ -924,9 +527,8 @@ }, "node_modules/@istanbuljs/load-nyc-config/node_modules/p-locate": { "version": "4.1.0", - "resolved": "https://registry.npmjs.org/p-locate/-/p-locate-4.1.0.tgz", - "integrity": "sha512-R79ZZ/0wAxKGu3oYMlz8jy/kbhsNrS7SKZ7PxEHBgJ5+F2mtFW2fK2cOtBh1cHYkQsbzFV7I+EoRKe6Yt0oK7A==", "dev": true, + "license": "MIT", "dependencies": { "p-limit": "^2.2.0" }, @@ -936,9 +538,8 @@ }, "node_modules/@istanbuljs/nyc-config-typescript": { "version": "1.0.2", - "resolved": "https://registry.npmjs.org/@istanbuljs/nyc-config-typescript/-/nyc-config-typescript-1.0.2.tgz", - "integrity": "sha512-iKGIyMoyJuFnJRSVTZ78POIRvNnwZaWIf8vG4ZS3rQq58MMDrqEX2nnzx0R28V2X8JvmKYiqY9FP2hlJsm8A0w==", "dev": true, + "license": "ISC", "dependencies": { "@istanbuljs/schema": "^0.1.2" }, @@ -951,18 +552,16 @@ }, "node_modules/@istanbuljs/schema": { "version": "0.1.3", - "resolved": "https://registry.npmjs.org/@istanbuljs/schema/-/schema-0.1.3.tgz", - "integrity": "sha512-ZXRY4jNvVgSVQ8DL3LTcakaAtXwTVUxE81hslsyD2AtoXW/wVob10HkOJ1X/pAlcI7D+2YoZKg5do8G/w6RYgA==", "dev": true, + "license": "MIT", "engines": { "node": ">=8" } }, "node_modules/@jridgewell/gen-mapping": { "version": "0.3.3", - "resolved": "https://registry.npmjs.org/@jridgewell/gen-mapping/-/gen-mapping-0.3.3.tgz", - "integrity": "sha512-HLhSWOLRi875zjjMG/r+Nv0oCW8umGb0BgEhyX3dDX3egwZtB8PqLnjz3yedt8R5StBrzcg4aBpnh8UA9D1BoQ==", "dev": true, + "license": "MIT", "dependencies": { "@jridgewell/set-array": "^1.0.1", "@jridgewell/sourcemap-codec": "^1.4.10", @@ -974,33 +573,29 @@ }, "node_modules/@jridgewell/resolve-uri": { "version": "3.1.1", - "resolved": "https://registry.npmjs.org/@jridgewell/resolve-uri/-/resolve-uri-3.1.1.tgz", - "integrity": "sha512-dSYZh7HhCDtCKm4QakX0xFpsRDqjjtZf/kjI/v3T3Nwt5r8/qz/M19F9ySyOqU94SXBmeG9ttTul+YnR4LOxFA==", "dev": true, + "license": "MIT", "engines": { "node": ">=6.0.0" } }, "node_modules/@jridgewell/set-array": { "version": "1.1.2", - "resolved": "https://registry.npmjs.org/@jridgewell/set-array/-/set-array-1.1.2.tgz", - "integrity": "sha512-xnkseuNADM0gt2bs+BvhO0p78Mk762YnZdsuzFV018NoG1Sj1SCQvpSqa7XUaTam5vAGasABV9qXASMKnFMwMw==", "dev": true, + "license": "MIT", "engines": { "node": ">=6.0.0" } }, "node_modules/@jridgewell/sourcemap-codec": { "version": "1.4.15", - "resolved": "https://registry.npmjs.org/@jridgewell/sourcemap-codec/-/sourcemap-codec-1.4.15.tgz", - "integrity": "sha512-eF2rxCRulEKXHTRiDrDy6erMYWqNw4LPdQ8UQA4huuxaQsVeRPFl2oM8oDGxMFhJUWZf9McpLtJasDDZb/Bpeg==", - "dev": true + "dev": true, + "license": "MIT" }, "node_modules/@jridgewell/trace-mapping": { "version": "0.3.22", - "resolved": "https://registry.npmjs.org/@jridgewell/trace-mapping/-/trace-mapping-0.3.22.tgz", - "integrity": "sha512-Wf963MzWtA2sjrNt+g18IAln9lKnlRp+K2eH4jjIoF1wYeq3aMREpG09xhlhdzS0EjwU7qmUJYangWa+151vZw==", "dev": true, + "license": "MIT", "dependencies": { "@jridgewell/resolve-uri": "^3.1.0", "@jridgewell/sourcemap-codec": "^1.4.14" @@ -1008,9 +603,8 @@ }, "node_modules/@ljharb/through": { "version": "2.3.12", - "resolved": "https://registry.npmjs.org/@ljharb/through/-/through-2.3.12.tgz", - "integrity": "sha512-ajo/heTlG3QgC8EGP6APIejksVAYt4ayz4tqoP3MolFELzcH1x1fzwEYRJTPO0IELutZ5HQ0c26/GqAYy79u3g==", "dev": true, + "license": "MIT", "dependencies": { "call-bind": "^1.0.5" }, @@ -1020,9 +614,8 @@ }, "node_modules/@nodelib/fs.scandir": { "version": "2.1.5", - "resolved": "https://registry.npmjs.org/@nodelib/fs.scandir/-/fs.scandir-2.1.5.tgz", - "integrity": "sha512-vq24Bq3ym5HEQm2NKCr3yXDwjc7vTsEThRDnkp2DK9p1uqLR+DHurm/NOTo0KG7HYHU7eppKZj3MyqYuMBf62g==", "dev": true, + "license": "MIT", "dependencies": { "@nodelib/fs.stat": "2.0.5", "run-parallel": "^1.1.9" @@ -1033,18 +626,16 @@ }, "node_modules/@nodelib/fs.stat": { "version": "2.0.5", - "resolved": "https://registry.npmjs.org/@nodelib/fs.stat/-/fs.stat-2.0.5.tgz", - "integrity": "sha512-RkhPPp2zrqDAQA/2jNhnztcPAlv64XdhIp7a7454A5ovI7Bukxgt7MX7udwAu3zg1DcpPU0rz3VV1SeaqvY4+A==", "dev": true, + "license": "MIT", "engines": { "node": ">= 8" } }, "node_modules/@nodelib/fs.walk": { "version": "1.2.8", - "resolved": "https://registry.npmjs.org/@nodelib/fs.walk/-/fs.walk-1.2.8.tgz", - "integrity": "sha512-oGB+UxlgWcgQkgwo8GcEGwemoTFt3FIO9ababBmaGwXIoBKZ+GTy0pP185beGg7Llih/NSHSV2XAs1lnznocSg==", "dev": true, + "license": "MIT", "dependencies": { "@nodelib/fs.scandir": "2.1.5", "fastq": "^1.6.0" @@ -1055,18 +646,16 @@ }, "node_modules/@octokit/auth-token": { "version": "4.0.0", - "resolved": "https://registry.npmjs.org/@octokit/auth-token/-/auth-token-4.0.0.tgz", - "integrity": "sha512-tY/msAuJo6ARbK6SPIxZrPBms3xPbfwBrulZe0Wtr/DIY9lje2HeV1uoebShn6mx7SjCHif6EjMvoREj+gZ+SA==", "dev": true, + "license": "MIT", "engines": { "node": ">= 18" } }, "node_modules/@octokit/core": { "version": "5.1.0", - "resolved": "https://registry.npmjs.org/@octokit/core/-/core-5.1.0.tgz", - "integrity": "sha512-BDa2VAMLSh3otEiaMJ/3Y36GU4qf6GI+VivQ/P41NC6GHcdxpKlqV0ikSZ5gdQsmS3ojXeRx5vasgNTinF0Q4g==", "dev": true, + "license": "MIT", "dependencies": { "@octokit/auth-token": "^4.0.0", "@octokit/graphql": "^7.0.0", @@ -1082,9 +671,8 @@ }, "node_modules/@octokit/endpoint": { "version": "9.0.4", - "resolved": "https://registry.npmjs.org/@octokit/endpoint/-/endpoint-9.0.4.tgz", - "integrity": "sha512-DWPLtr1Kz3tv8L0UvXTDP1fNwM0S+z6EJpRcvH66orY6Eld4XBMCSYsaWp4xIm61jTWxK68BrR7ibO+vSDnZqw==", "dev": true, + "license": "MIT", "dependencies": { "@octokit/types": "^12.0.0", "universal-user-agent": "^6.0.0" @@ -1095,9 +683,8 @@ }, "node_modules/@octokit/graphql": { "version": "7.0.2", - "resolved": "https://registry.npmjs.org/@octokit/graphql/-/graphql-7.0.2.tgz", - "integrity": "sha512-OJ2iGMtj5Tg3s6RaXH22cJcxXRi7Y3EBqbHTBRq+PQAqfaS8f/236fUrWhfSn8P4jovyzqucxme7/vWSSZBX2Q==", "dev": true, + "license": "MIT", "dependencies": { "@octokit/request": "^8.0.1", "@octokit/types": "^12.0.0", @@ -1109,15 +696,13 @@ }, "node_modules/@octokit/openapi-types": { "version": "19.1.0", - "resolved": "https://registry.npmjs.org/@octokit/openapi-types/-/openapi-types-19.1.0.tgz", - "integrity": "sha512-6G+ywGClliGQwRsjvqVYpklIfa7oRPA0vyhPQG/1Feh+B+wU0vGH1JiJ5T25d3g1JZYBHzR2qefLi9x8Gt+cpw==", - "dev": true + "dev": true, + "license": "MIT" }, "node_modules/@octokit/plugin-paginate-rest": { "version": "9.1.5", - "resolved": "https://registry.npmjs.org/@octokit/plugin-paginate-rest/-/plugin-paginate-rest-9.1.5.tgz", - "integrity": "sha512-WKTQXxK+bu49qzwv4qKbMMRXej1DU2gq017euWyKVudA6MldaSSQuxtz+vGbhxV4CjxpUxjZu6rM2wfc1FiWVg==", "dev": true, + "license": "MIT", "dependencies": { "@octokit/types": "^12.4.0" }, @@ -1130,9 +715,8 @@ }, "node_modules/@octokit/plugin-request-log": { "version": "4.0.0", - "resolved": "https://registry.npmjs.org/@octokit/plugin-request-log/-/plugin-request-log-4.0.0.tgz", - "integrity": "sha512-2uJI1COtYCq8Z4yNSnM231TgH50bRkheQ9+aH8TnZanB6QilOnx8RMD2qsnamSOXtDj0ilxvevf5fGsBhBBzKA==", "dev": true, + "license": "MIT", "engines": { "node": ">= 18" }, @@ -1142,9 +726,8 @@ }, "node_modules/@octokit/plugin-rest-endpoint-methods": { "version": "10.2.0", - "resolved": "https://registry.npmjs.org/@octokit/plugin-rest-endpoint-methods/-/plugin-rest-endpoint-methods-10.2.0.tgz", - "integrity": "sha512-ePbgBMYtGoRNXDyKGvr9cyHjQ163PbwD0y1MkDJCpkO2YH4OeXX40c4wYHKikHGZcpGPbcRLuy0unPUuafco8Q==", "dev": true, + "license": "MIT", "dependencies": { "@octokit/types": "^12.3.0" }, @@ -1157,9 +740,8 @@ }, "node_modules/@octokit/request": { "version": "8.1.6", - "resolved": "https://registry.npmjs.org/@octokit/request/-/request-8.1.6.tgz", - "integrity": "sha512-YhPaGml3ncZC1NfXpP3WZ7iliL1ap6tLkAp6MvbK2fTTPytzVUyUesBBogcdMm86uRYO5rHaM1xIWxigWZ17MQ==", "dev": true, + "license": "MIT", "dependencies": { "@octokit/endpoint": "^9.0.0", "@octokit/request-error": "^5.0.0", @@ -1172,9 +754,8 @@ }, "node_modules/@octokit/request-error": { "version": "5.0.1", - "resolved": "https://registry.npmjs.org/@octokit/request-error/-/request-error-5.0.1.tgz", - "integrity": "sha512-X7pnyTMV7MgtGmiXBwmO6M5kIPrntOXdyKZLigNfQWSEQzVxR4a4vo49vJjTWX70mPndj8KhfT4Dx+2Ng3vnBQ==", "dev": true, + "license": "MIT", "dependencies": { "@octokit/types": "^12.0.0", "deprecation": "^2.0.0", @@ -1186,9 +767,8 @@ }, "node_modules/@octokit/rest": { "version": "20.0.2", - "resolved": "https://registry.npmjs.org/@octokit/rest/-/rest-20.0.2.tgz", - "integrity": "sha512-Ux8NDgEraQ/DMAU1PlAohyfBBXDwhnX2j33Z1nJNziqAfHi70PuxkFYIcIt8aIAxtRE7KVuKp8lSR8pA0J5iOQ==", "dev": true, + "license": "MIT", "dependencies": { "@octokit/core": "^5.0.0", "@octokit/plugin-paginate-rest": "^9.0.0", @@ -1201,27 +781,24 @@ }, "node_modules/@octokit/types": { "version": "12.4.0", - "resolved": "https://registry.npmjs.org/@octokit/types/-/types-12.4.0.tgz", - "integrity": "sha512-FLWs/AvZllw/AGVs+nJ+ELCDZZJk+kY0zMen118xhL2zD0s1etIUHm1odgjP7epxYU1ln7SZxEUWYop5bhsdgQ==", "dev": true, + "license": "MIT", "dependencies": { "@octokit/openapi-types": "^19.1.0" } }, "node_modules/@pnpm/config.env-replace": { "version": "1.1.0", - "resolved": "https://registry.npmjs.org/@pnpm/config.env-replace/-/config.env-replace-1.1.0.tgz", - "integrity": "sha512-htyl8TWnKL7K/ESFa1oW2UB5lVDxuF5DpM7tBi6Hu2LNL3mWkIzNLG6N4zoCUP1lCKNxWy/3iu8mS8MvToGd6w==", "dev": true, + "license": "MIT", "engines": { "node": ">=12.22.0" } }, "node_modules/@pnpm/network.ca-file": { "version": "1.0.2", - "resolved": "https://registry.npmjs.org/@pnpm/network.ca-file/-/network.ca-file-1.0.2.tgz", - "integrity": "sha512-YcPQ8a0jwYU9bTdJDpXjMi7Brhkr1mXsXrUJvjqM2mQDgkRiz8jFaQGOdaLxgjtUfQgZhKy/O3cG/YwmgKaxLA==", "dev": true, + "license": "MIT", "dependencies": { "graceful-fs": "4.2.10" }, @@ -1231,15 +808,13 @@ }, "node_modules/@pnpm/network.ca-file/node_modules/graceful-fs": { "version": "4.2.10", - "resolved": "https://registry.npmjs.org/graceful-fs/-/graceful-fs-4.2.10.tgz", - "integrity": "sha512-9ByhssR2fPVsNZj478qUUbKfmL0+t5BDVyjShtyZZLiK7ZDAArFFfopyOTj0M05wE2tJPisA4iTnnXl2YoPvOA==", - "dev": true + "dev": true, + "license": "ISC" }, "node_modules/@pnpm/npm-conf": { "version": "2.2.2", - "resolved": "https://registry.npmjs.org/@pnpm/npm-conf/-/npm-conf-2.2.2.tgz", - "integrity": "sha512-UA91GwWPhFExt3IizW6bOeY/pQ0BkuNwKjk9iQW9KqxluGCrg4VenZ0/L+2Y0+ZOtme72EVvg6v0zo3AMQRCeA==", "dev": true, + "license": "MIT", "dependencies": { "@pnpm/config.env-replace": "^1.1.0", "@pnpm/network.ca-file": "^1.0.1", @@ -1279,9 +854,8 @@ }, "node_modules/@sindresorhus/is": { "version": "5.6.0", - "resolved": "https://registry.npmjs.org/@sindresorhus/is/-/is-5.6.0.tgz", - "integrity": "sha512-TV7t8GKYaJWsn00tFDqBw8+Uqmr8A0fRU1tvTQhyZzGv0sJCGRQL3JGMI3ucuKo3XIZdUP+Lx7/gh2t3lewy7g==", "dev": true, + "license": "MIT", "engines": { "node": ">=14.16" }, @@ -1291,9 +865,8 @@ }, "node_modules/@sindresorhus/merge-streams": { "version": "1.0.0", - "resolved": "https://registry.npmjs.org/@sindresorhus/merge-streams/-/merge-streams-1.0.0.tgz", - "integrity": "sha512-rUV5WyJrJLoloD4NDN1V1+LDMDWOa4OTsT4yYJwQNpTU6FWxkxHpL7eu4w+DmiH8x/EAM1otkPE1+LaspIbplw==", "dev": true, + "license": "MIT", "engines": { "node": ">=18" }, @@ -1303,27 +876,24 @@ }, "node_modules/@sinonjs/commons": { "version": "3.0.1", - "resolved": "https://registry.npmjs.org/@sinonjs/commons/-/commons-3.0.1.tgz", - "integrity": "sha512-K3mCHKQ9sVh8o1C9cxkwxaOmXoAMlDxC1mYyHrjqOWEcBjYr76t96zL2zlj5dUGZ3HSw240X1qgH3Mjf1yJWpQ==", "dev": true, + "license": "BSD-3-Clause", "dependencies": { "type-detect": "4.0.8" } }, "node_modules/@sinonjs/fake-timers": { "version": "11.2.2", - "resolved": "https://registry.npmjs.org/@sinonjs/fake-timers/-/fake-timers-11.2.2.tgz", - "integrity": "sha512-G2piCSxQ7oWOxwGSAyFHfPIsyeJGXYtc6mFbnFA+kRXkiEnTl8c/8jul2S329iFBnDI9HGoeWWAZvuvOkZccgw==", "dev": true, + "license": "BSD-3-Clause", "dependencies": { "@sinonjs/commons": "^3.0.0" } }, "node_modules/@sinonjs/samsam": { "version": "8.0.0", - "resolved": "https://registry.npmjs.org/@sinonjs/samsam/-/samsam-8.0.0.tgz", - "integrity": "sha512-Bp8KUVlLp8ibJZrnvq2foVhP0IVX2CIprMJPK0vqGqgrDa0OHVKeZyBykqskkrdxV6yKBPmGasO8LVjAKR3Gew==", "dev": true, + "license": "BSD-3-Clause", "dependencies": { "@sinonjs/commons": "^2.0.0", "lodash.get": "^4.4.2", @@ -1332,24 +902,21 @@ }, "node_modules/@sinonjs/samsam/node_modules/@sinonjs/commons": { "version": "2.0.0", - "resolved": "https://registry.npmjs.org/@sinonjs/commons/-/commons-2.0.0.tgz", - "integrity": "sha512-uLa0j859mMrg2slwQYdO/AkrOfmH+X6LTVmNTS9CqexuE2IvVORIkSpJLqePAbEnKJ77aMmCwr1NUZ57120Xcg==", "dev": true, + "license": "BSD-3-Clause", "dependencies": { "type-detect": "4.0.8" } }, "node_modules/@sinonjs/text-encoding": { "version": "0.7.2", - "resolved": "https://registry.npmjs.org/@sinonjs/text-encoding/-/text-encoding-0.7.2.tgz", - "integrity": "sha512-sXXKG+uL9IrKqViTtao2Ws6dy0znu9sOaP1di/jKGW1M6VssO8vlpXCQcpZ+jisQ1tTFAC5Jo/EOzFbggBagFQ==", - "dev": true + "dev": true, + "license": "(Unlicense OR Apache-2.0)" }, "node_modules/@szmarczak/http-timer": { "version": "5.0.1", - "resolved": "https://registry.npmjs.org/@szmarczak/http-timer/-/http-timer-5.0.1.tgz", - "integrity": "sha512-+PmQX0PiAYPMeVYe237LJAYvOMYW1j2rH5YROyS3b4CTVJum34HfRvKvAzozHAQG0TnHNdUfY9nCeUyRAs//cw==", "dev": true, + "license": "MIT", "dependencies": { "defer-to-connect": "^2.0.1" }, @@ -1359,66 +926,57 @@ }, "node_modules/@tootallnate/quickjs-emscripten": { "version": "0.23.0", - "resolved": "https://registry.npmjs.org/@tootallnate/quickjs-emscripten/-/quickjs-emscripten-0.23.0.tgz", - "integrity": "sha512-C5Mc6rdnsaJDjO3UpGW/CQTHtCKaYlScZTly4JIu97Jxo/odCiH0ITnDXSJPTOrEKk/ycSZ0AOgTmkDtkOsvIA==", - "dev": true + "dev": true, + "license": "MIT" }, "node_modules/@types/http-cache-semantics": { "version": "4.0.4", - "resolved": "https://registry.npmjs.org/@types/http-cache-semantics/-/http-cache-semantics-4.0.4.tgz", - "integrity": "sha512-1m0bIFVc7eJWyve9S0RnuRgcQqF/Xd5QsUZAZeQFr1Q3/p9JWoQQEqmVy+DPTNpGXwhgIetAoYF8JSc33q29QA==", - "dev": true + "dev": true, + "license": "MIT" }, "node_modules/@types/mocha": { "version": "10.0.6", - "resolved": "https://registry.npmjs.org/@types/mocha/-/mocha-10.0.6.tgz", - "integrity": "sha512-dJvrYWxP/UcXm36Qn36fxhUKu8A/xMRXVT2cliFF1Z7UA9liG5Psj3ezNSZw+5puH2czDXRLcXQxf8JbJt0ejg==", - "dev": true + "dev": true, + "license": "MIT" }, "node_modules/@types/node": { "version": "20.11.16", - "resolved": "https://registry.npmjs.org/@types/node/-/node-20.11.16.tgz", - "integrity": "sha512-gKb0enTmRCzXSSUJDq6/sPcqrfCv2mkkG6Jt/clpn5eiCbKTY+SgZUxo+p8ZKMof5dCp9vHQUAB7wOUTod22wQ==", "dev": true, + "license": "MIT", "dependencies": { "undici-types": "~5.26.4" } }, "node_modules/@types/sinon": { "version": "17.0.3", - "resolved": "https://registry.npmjs.org/@types/sinon/-/sinon-17.0.3.tgz", - "integrity": "sha512-j3uovdn8ewky9kRBG19bOwaZbexJu/XjtkHyjvUgt4xfPFz18dcORIMqnYh66Fx3Powhcr85NT5+er3+oViapw==", "dev": true, + "license": "MIT", "dependencies": { "@types/sinonjs__fake-timers": "*" } }, "node_modules/@types/sinonjs__fake-timers": { "version": "8.1.5", - "resolved": "https://registry.npmjs.org/@types/sinonjs__fake-timers/-/sinonjs__fake-timers-8.1.5.tgz", - "integrity": "sha512-mQkU2jY8jJEF7YHjHvsQO8+3ughTL1mcnn96igfhONmR+fUPSKIkefQYpSe8bsly2Ep7oQbn/6VG5/9/0qcArQ==", - "dev": true + "dev": true, + "license": "MIT" }, "node_modules/@types/yargs": { "version": "17.0.32", - "resolved": "https://registry.npmjs.org/@types/yargs/-/yargs-17.0.32.tgz", - "integrity": "sha512-xQ67Yc/laOG5uMfX/093MRlGGCIBzZMarVa+gfNKJxWAIgykYpVGkBdbqEzGDDfCrVUj6Hiff4mTZ5BA6TmAog==", "dev": true, + "license": "MIT", "dependencies": { "@types/yargs-parser": "*" } }, "node_modules/@types/yargs-parser": { "version": "21.0.3", - "resolved": "https://registry.npmjs.org/@types/yargs-parser/-/yargs-parser-21.0.3.tgz", - "integrity": "sha512-I4q9QU9MQv4oEOz4tAHJtNz1cwuLxn2F3xcc2iV5WdqLPpUnj30aUuxt1mAxYTG+oe8CZMV/+6rU4S4gRDzqtQ==", - "dev": true + "dev": true, + "license": "MIT" }, "node_modules/agent-base": { "version": "7.1.0", - "resolved": "https://registry.npmjs.org/agent-base/-/agent-base-7.1.0.tgz", - "integrity": "sha512-o/zjMZRhJxny7OyEF+Op8X+efiELC7k7yOjMzgfzVqOzXqkBkWI79YoTdOtsuWd5BWhAGAuOY/Xa6xpiaWXiNg==", "dev": true, + "license": "MIT", "dependencies": { "debug": "^4.3.4" }, @@ -1428,9 +986,8 @@ }, "node_modules/aggregate-error": { "version": "3.1.0", - "resolved": "https://registry.npmjs.org/aggregate-error/-/aggregate-error-3.1.0.tgz", - "integrity": "sha512-4I7Td01quW/RpocfNayFdFVk1qSuoh0E7JrbRJ16nH01HhKFQ88INq9Sd+nd72zqRySlr9BmDA8xlEJ6vJMrYA==", "dev": true, + "license": "MIT", "dependencies": { "clean-stack": "^2.0.0", "indent-string": "^4.0.0" @@ -1441,27 +998,24 @@ }, "node_modules/ansi-align": { "version": "3.0.1", - "resolved": "https://registry.npmjs.org/ansi-align/-/ansi-align-3.0.1.tgz", - "integrity": "sha512-IOfwwBF5iczOjp/WeY4YxyjqAFMQoZufdQWDd19SEExbVLNXqvpzSJ/M7Za4/sCPmQ0+GRquoA7bGcINcxew6w==", "dev": true, + "license": "ISC", "dependencies": { "string-width": "^4.1.0" } }, "node_modules/ansi-colors": { "version": "4.1.1", - "resolved": "https://registry.npmjs.org/ansi-colors/-/ansi-colors-4.1.1.tgz", - "integrity": "sha512-JoX0apGbHaUJBNl6yF+p6JAFYZ666/hhCGKN5t9QFjbJQKUU/g8MNbFDbvfrgKXvI1QpZplPOnwIo99lX/AAmA==", "dev": true, + "license": "MIT", "engines": { "node": ">=6" } }, "node_modules/ansi-escapes": { "version": "4.3.2", - "resolved": "https://registry.npmjs.org/ansi-escapes/-/ansi-escapes-4.3.2.tgz", - "integrity": "sha512-gKXj5ALrKWQLsYG9jlTRmR/xKluxHV+Z9QEwNIgCfM1/uwPMCuzVVnh5mwTd+OuBZcwSIMbqssNWRm1lE51QaQ==", "dev": true, + "license": "MIT", "dependencies": { "type-fest": "^0.21.3" }, @@ -1474,9 +1028,8 @@ }, "node_modules/ansi-escapes/node_modules/type-fest": { "version": "0.21.3", - "resolved": "https://registry.npmjs.org/type-fest/-/type-fest-0.21.3.tgz", - "integrity": "sha512-t0rzBq87m3fVcduHDUFhKmyyX+9eo6WQjZvf51Ea/M0Q7+T374Jp1aUiyUl0GKxp8M/OETVHSDvmkyPgvX+X2w==", "dev": true, + "license": "(MIT OR CC0-1.0)", "engines": { "node": ">=10" }, @@ -1486,24 +1039,21 @@ }, "node_modules/ansi-regex": { "version": "5.0.1", - "resolved": "https://registry.npmjs.org/ansi-regex/-/ansi-regex-5.0.1.tgz", - "integrity": "sha512-quJQXlTSUGL2LH9SUXo8VwsY4soanhgo6LNSm84E1LBcE8s3O0wpdiRzyR9z/ZZJMlMWv37qOOb9pdJlMUEKFQ==", "dev": true, + "license": "MIT", "engines": { "node": ">=8" } }, "node_modules/ansi-sequence-parser": { "version": "1.1.1", - "resolved": "https://registry.npmjs.org/ansi-sequence-parser/-/ansi-sequence-parser-1.1.1.tgz", - "integrity": "sha512-vJXt3yiaUL4UU546s3rPXlsry/RnM730G1+HkpKE012AN0sx1eOrxSu95oKDIonskeLTijMgqWZ3uDEe3NFvyg==", - "dev": true + "dev": true, + "license": "MIT" }, "node_modules/ansi-styles": { "version": "4.3.0", - "resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-4.3.0.tgz", - "integrity": "sha512-zbB9rCJAT1rbjiVDb2hqKFHNYLxgtk8NURxZ3IZwD3F6NtxbXZQCnnSi1Lkx+IDohdPlFp222wVALIheZJQSEg==", "dev": true, + "license": "MIT", "dependencies": { "color-convert": "^2.0.1" }, @@ -1516,9 +1066,8 @@ }, "node_modules/anymatch": { "version": "3.1.3", - "resolved": "https://registry.npmjs.org/anymatch/-/anymatch-3.1.3.tgz", - "integrity": "sha512-KMReFUr0B4t+D+OBkjR3KYqvocp2XaSzO55UcB6mgQMd3KbcE+mWTyvVV7D/zsdEbNnV6acZUutkiHQXvTr1Rw==", "dev": true, + "license": "ISC", "dependencies": { "normalize-path": "^3.0.0", "picomatch": "^2.0.4" @@ -1529,9 +1078,8 @@ }, "node_modules/append-transform": { "version": "2.0.0", - "resolved": "https://registry.npmjs.org/append-transform/-/append-transform-2.0.0.tgz", - "integrity": "sha512-7yeyCEurROLQJFv5Xj4lEGTy0borxepjFv1g22oAdqFu//SrAlDl1O1Nxx15SH1RoliUml6p8dwJW9jvZughhg==", "dev": true, + "license": "MIT", "dependencies": { "default-require-extensions": "^3.0.0" }, @@ -1541,21 +1089,18 @@ }, "node_modules/archy": { "version": "1.0.0", - "resolved": "https://registry.npmjs.org/archy/-/archy-1.0.0.tgz", - "integrity": "sha512-Xg+9RwCg/0p32teKdGMPTPnVXKD0w3DfHnFTficozsAgsvq2XenPJq/MYpzzQ/v8zrOyJn6Ds39VA4JIDwFfqw==", - "dev": true + "dev": true, + "license": "MIT" }, "node_modules/argparse": { "version": "2.0.1", - "resolved": "https://registry.npmjs.org/argparse/-/argparse-2.0.1.tgz", - "integrity": "sha512-8+9WqebbFzpX9OR+Wa6O29asIogeRMzcGtAINdpMHHyAg10f05aSFVBbcEqGf/PXw1EjAZ+q2/bEBg3DvurK3Q==", - "dev": true + "dev": true, + "license": "Python-2.0" }, "node_modules/array-buffer-byte-length": { "version": "1.0.1", - "resolved": "https://registry.npmjs.org/array-buffer-byte-length/-/array-buffer-byte-length-1.0.1.tgz", - "integrity": "sha512-ahC5W1xgou+KTXix4sAO8Ki12Q+jf4i0+tmk3sC+zgcynshkHxzpXdImBehiUYKKKDwvfFiJl1tZt6ewscS1Mg==", "dev": true, + "license": "MIT", "dependencies": { "call-bind": "^1.0.5", "is-array-buffer": "^3.0.4" @@ -1569,9 +1114,8 @@ }, "node_modules/array-union": { "version": "1.0.2", - "resolved": "https://registry.npmjs.org/array-union/-/array-union-1.0.2.tgz", - "integrity": "sha512-Dxr6QJj/RdU/hCaBjOfxW+q6lyuVE6JFWIrAUpuOOhoJJoQ99cUn3igRaHVB5P9WrgFVN0FfArM3x0cueOU8ng==", "dev": true, + "license": "MIT", "dependencies": { "array-uniq": "^1.0.1" }, @@ -1581,18 +1125,16 @@ }, "node_modules/array-uniq": { "version": "1.0.3", - "resolved": "https://registry.npmjs.org/array-uniq/-/array-uniq-1.0.3.tgz", - "integrity": "sha512-MNha4BWQ6JbwhFhj03YK552f7cb3AzoE8SzeljgChvL1dl3IcvggXVz1DilzySZkCja+CXuZbdW7yATchWn8/Q==", "dev": true, + "license": "MIT", "engines": { "node": ">=0.10.0" } }, "node_modules/array.prototype.map": { "version": "1.0.6", - "resolved": "https://registry.npmjs.org/array.prototype.map/-/array.prototype.map-1.0.6.tgz", - "integrity": "sha512-nK1psgF2cXqP3wSyCSq0Hc7zwNq3sfljQqaG27r/7a7ooNUnn5nGq6yYWyks9jMO5EoFQ0ax80hSg6oXSRNXaw==", "dev": true, + "license": "MIT", "dependencies": { "call-bind": "^1.0.2", "define-properties": "^1.2.0", @@ -1609,9 +1151,8 @@ }, "node_modules/arraybuffer.prototype.slice": { "version": "1.0.3", - "resolved": "https://registry.npmjs.org/arraybuffer.prototype.slice/-/arraybuffer.prototype.slice-1.0.3.tgz", - "integrity": "sha512-bMxMKAjg13EBSVscxTaYA4mRc5t1UAXa2kXiGTNfZ079HIWXEkKmkgFrh/nJqamaLSrXO5H4WFFkPEaLJWbs3A==", "dev": true, + "license": "MIT", "dependencies": { "array-buffer-byte-length": "^1.0.1", "call-bind": "^1.0.5", @@ -1631,9 +1172,8 @@ }, "node_modules/ast-types": { "version": "0.13.4", - "resolved": "https://registry.npmjs.org/ast-types/-/ast-types-0.13.4.tgz", - "integrity": "sha512-x1FCFnFifvYDDzTaLII71vG5uvDwgtmDTEVWAxrgeiR8VjMONcCXJx7E+USjDtHlwFmt9MysbqgF9b9Vjr6w+w==", "dev": true, + "license": "MIT", "dependencies": { "tslib": "^2.0.1" }, @@ -1643,24 +1183,21 @@ }, "node_modules/async": { "version": "3.2.5", - "resolved": "https://registry.npmjs.org/async/-/async-3.2.5.tgz", - "integrity": "sha512-baNZyqaaLhyLVKm/DlvdW051MSgO6b8eVfIezl9E5PqWxFgzLm/wQntEW4zOytVburDEr0JlALEpdOFwvErLsg==", - "dev": true + "dev": true, + "license": "MIT" }, "node_modules/async-retry": { "version": "1.3.3", - "resolved": "https://registry.npmjs.org/async-retry/-/async-retry-1.3.3.tgz", - "integrity": "sha512-wfr/jstw9xNi/0teMHrRW7dsz3Lt5ARhYNZ2ewpadnhaIp5mbALhOAP+EAdsC7t4Z6wqsDVv9+W6gm1Dk9mEyw==", "dev": true, + "license": "MIT", "dependencies": { "retry": "0.13.1" } }, "node_modules/available-typed-arrays": { "version": "1.0.6", - "resolved": "https://registry.npmjs.org/available-typed-arrays/-/available-typed-arrays-1.0.6.tgz", - "integrity": "sha512-j1QzY8iPNPG4o4xmO3ptzpRxTciqD3MgEHtifP/YnJpIo58Xu+ne4BejlbkuaLfXn/nz6HFiw29bLpj2PNMdGg==", "dev": true, + "license": "MIT", "engines": { "node": ">= 0.4" }, @@ -1670,14 +1207,11 @@ }, "node_modules/balanced-match": { "version": "1.0.2", - "resolved": "https://registry.npmjs.org/balanced-match/-/balanced-match-1.0.2.tgz", - "integrity": "sha512-3oSeUO0TMV67hN1AmbXsK4yaqU7tjiHlbxRDZOpH0KW9+CeX4bRAaX0Anxt0tx2MrpRpWwQaPwIlISEJhYU5Pw==", - "dev": true + "dev": true, + "license": "MIT" }, "node_modules/base64-js": { "version": "1.5.1", - "resolved": "https://registry.npmjs.org/base64-js/-/base64-js-1.5.1.tgz", - "integrity": "sha512-AKpaYlHn8t4SVbOHCy+b5+KKgvR4vrsD8vbvrbiQJps7fKDTkjkDry6ji0rUJjC0kzbNePLwzxq8iypo41qeWA==", "dev": true, "funding": [ { @@ -1692,37 +1226,34 @@ "type": "consulting", "url": "https://feross.org/support" } - ] + ], + "license": "MIT" }, "node_modules/basic-ftp": { "version": "5.0.4", - "resolved": "https://registry.npmjs.org/basic-ftp/-/basic-ftp-5.0.4.tgz", - "integrity": "sha512-8PzkB0arJFV4jJWSGOYR+OEic6aeKMu/osRhBULN6RY0ykby6LKhbmuQ5ublvaas5BOwboah5D87nrHyuh8PPA==", "dev": true, + "license": "MIT", "engines": { "node": ">=10.0.0" } }, "node_modules/before-after-hook": { "version": "2.2.3", - "resolved": "https://registry.npmjs.org/before-after-hook/-/before-after-hook-2.2.3.tgz", - "integrity": "sha512-NzUnlZexiaH/46WDhANlyR2bXRopNg4F/zuSA3OpZnllCUgRaOF2znDioDWrmbNVsuZk6l9pMquQB38cfBZwkQ==", - "dev": true + "dev": true, + "license": "Apache-2.0" }, "node_modules/binary-extensions": { "version": "2.2.0", - "resolved": "https://registry.npmjs.org/binary-extensions/-/binary-extensions-2.2.0.tgz", - "integrity": "sha512-jDctJ/IVQbZoJykoeHbhXpOlNBqGNcwXJKJog42E5HDPUwQTSdjCHdihjj0DlnheQ7blbT6dHOafNAiS8ooQKA==", "dev": true, + "license": "MIT", "engines": { "node": ">=8" } }, "node_modules/bl": { "version": "4.1.0", - "resolved": "https://registry.npmjs.org/bl/-/bl-4.1.0.tgz", - "integrity": "sha512-1W07cM9gS6DcLperZfFSj+bWLtaPGSOHWhPiGzXmvVJbRLdG82sH/Kn8EtW1VqWVA54AKf2h5k5BbnIbwF3h6w==", "dev": true, + "license": "MIT", "dependencies": { "buffer": "^5.5.0", "inherits": "^2.0.4", @@ -1731,9 +1262,8 @@ }, "node_modules/boxen": { "version": "7.1.1", - "resolved": "https://registry.npmjs.org/boxen/-/boxen-7.1.1.tgz", - "integrity": "sha512-2hCgjEmP8YLWQ130n2FerGv7rYpfBmnmp9Uy2Le1vge6X3gZIfSmEzP5QTDElFxcvVcXlEn8Aq6MU/PZygIOog==", "dev": true, + "license": "MIT", "dependencies": { "ansi-align": "^3.0.1", "camelcase": "^7.0.1", @@ -1753,9 +1283,8 @@ }, "node_modules/boxen/node_modules/ansi-regex": { "version": "6.0.1", - "resolved": "https://registry.npmjs.org/ansi-regex/-/ansi-regex-6.0.1.tgz", - "integrity": "sha512-n5M855fKb2SsfMIiFFoVrABHJC8QtHwVx+mHWP3QcEqBHYienj5dHSgjbxtC0WEZXYt4wcD6zrQElDPhFuZgfA==", "dev": true, + "license": "MIT", "engines": { "node": ">=12" }, @@ -1765,9 +1294,8 @@ }, "node_modules/boxen/node_modules/ansi-styles": { "version": "6.2.1", - "resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-6.2.1.tgz", - "integrity": "sha512-bN798gFfQX+viw3R7yrGWRqnrN2oRkEkUjjl4JNn4E8GxxbjtG3FbrEIIY3l8/hrwUwIeCZvi4QuOTP4MErVug==", "dev": true, + "license": "MIT", "engines": { "node": ">=12" }, @@ -1777,9 +1305,8 @@ }, "node_modules/boxen/node_modules/camelcase": { "version": "7.0.1", - "resolved": "https://registry.npmjs.org/camelcase/-/camelcase-7.0.1.tgz", - "integrity": "sha512-xlx1yCK2Oc1APsPXDL2LdlNP6+uu8OCDdhOBSVT279M/S+y75O30C2VuD8T2ogdePBBl7PfPF4504tnLgX3zfw==", "dev": true, + "license": "MIT", "engines": { "node": ">=14.16" }, @@ -1789,9 +1316,8 @@ }, "node_modules/boxen/node_modules/chalk": { "version": "5.3.0", - "resolved": "https://registry.npmjs.org/chalk/-/chalk-5.3.0.tgz", - "integrity": "sha512-dLitG79d+GV1Nb/VYcCDFivJeK1hiukt9QjRNVOsUtTy1rR1YJsmpGGTZ3qJos+uw7WmWF4wUwBd9jxjocFC2w==", "dev": true, + "license": "MIT", "engines": { "node": "^12.17.0 || ^14.13 || >=16.0.0" }, @@ -1801,15 +1327,13 @@ }, "node_modules/boxen/node_modules/emoji-regex": { "version": "9.2.2", - "resolved": "https://registry.npmjs.org/emoji-regex/-/emoji-regex-9.2.2.tgz", - "integrity": "sha512-L18DaJsXSUk2+42pv8mLs5jJT2hqFkFE4j21wOmgbUqsZ2hL72NsUU785g9RXgo3s0ZNgVl42TiHp3ZtOv/Vyg==", - "dev": true + "dev": true, + "license": "MIT" }, "node_modules/boxen/node_modules/string-width": { "version": "5.1.2", - "resolved": "https://registry.npmjs.org/string-width/-/string-width-5.1.2.tgz", - "integrity": "sha512-HnLOCR3vjcY8beoNLtcjZ5/nxn2afmME6lhrDrebokqMap+XbeW8n9TXpPDOqdGK5qcI3oT0GKTW6wC7EMiVqA==", "dev": true, + "license": "MIT", "dependencies": { "eastasianwidth": "^0.2.0", "emoji-regex": "^9.2.2", @@ -1824,9 +1348,8 @@ }, "node_modules/boxen/node_modules/strip-ansi": { "version": "7.1.0", - "resolved": "https://registry.npmjs.org/strip-ansi/-/strip-ansi-7.1.0.tgz", - "integrity": "sha512-iq6eVVI64nQQTRYq2KtEg2d2uU7LElhTJwsH4YzIHZshxlgZms/wIc4VoDQTlG/IvVIrBKG06CrZnp0qv7hkcQ==", "dev": true, + "license": "MIT", "dependencies": { "ansi-regex": "^6.0.1" }, @@ -1839,9 +1362,8 @@ }, "node_modules/boxen/node_modules/type-fest": { "version": "2.19.0", - "resolved": "https://registry.npmjs.org/type-fest/-/type-fest-2.19.0.tgz", - "integrity": "sha512-RAH822pAdBgcNMAfWnCBU3CFZcfZ/i1eZjwFU/dsLKumyuuP3niueg2UAukXYF0E2AAoc82ZSSf9J0WQBinzHA==", "dev": true, + "license": "(MIT OR CC0-1.0)", "engines": { "node": ">=12.20" }, @@ -1851,9 +1373,8 @@ }, "node_modules/boxen/node_modules/wrap-ansi": { "version": "8.1.0", - "resolved": "https://registry.npmjs.org/wrap-ansi/-/wrap-ansi-8.1.0.tgz", - "integrity": "sha512-si7QWI6zUMq56bESFvagtmzMdGOtoxfR+Sez11Mobfc7tm+VkUckk9bW2UeffTGVUbOksxmSw0AA2gs8g71NCQ==", "dev": true, + "license": "MIT", "dependencies": { "ansi-styles": "^6.1.0", "string-width": "^5.0.1", @@ -1868,9 +1389,8 @@ }, "node_modules/brace-expansion": { "version": "1.1.11", - "resolved": "https://registry.npmjs.org/brace-expansion/-/brace-expansion-1.1.11.tgz", - "integrity": "sha512-iCuPHDFgrHX7H2vEI/5xpz07zSHB00TpugqhmYtVmMO6518mCuRMoOYFldEBl0g187ufozdaHgWKcYFb61qGiA==", "dev": true, + "license": "MIT", "dependencies": { "balanced-match": "^1.0.0", "concat-map": "0.0.1" @@ -1878,9 +1398,8 @@ }, "node_modules/braces": { "version": "3.0.2", - "resolved": "https://registry.npmjs.org/braces/-/braces-3.0.2.tgz", - "integrity": "sha512-b8um+L1RzM3WDSzvhm6gIz1yfTbBt6YTlcEKAvsmqCZZFw46z626lVj9j1yEPW33H5H+lBQpZMP1k8l+78Ha0A==", "dev": true, + "license": "MIT", "dependencies": { "fill-range": "^7.0.1" }, @@ -1890,14 +1409,11 @@ }, "node_modules/browser-stdout": { "version": "1.3.1", - "resolved": "https://registry.npmjs.org/browser-stdout/-/browser-stdout-1.3.1.tgz", - "integrity": "sha512-qhAVI1+Av2X7qelOfAIYwXONood6XlZE/fXaBSmW/T5SzLAmCgzi+eiWE7fUvbHaeNBQH13UftjpXxsfLkMpgw==", - "dev": true + "dev": true, + "license": "ISC" }, "node_modules/browserslist": { "version": "4.22.3", - "resolved": "https://registry.npmjs.org/browserslist/-/browserslist-4.22.3.tgz", - "integrity": "sha512-UAp55yfwNv0klWNapjs/ktHoguxuQNGnOzxYmfnXIS+8AsRDZkSDxg7R1AX3GKzn078SBI5dzwzj/Yx0Or0e3A==", "dev": true, "funding": [ { @@ -1913,6 +1429,7 @@ "url": "https://github.com/sponsors/ai" } ], + "license": "MIT", "dependencies": { "caniuse-lite": "^1.0.30001580", "electron-to-chromium": "^1.4.648", @@ -1928,8 +1445,6 @@ }, "node_modules/buffer": { "version": "5.7.1", - "resolved": "https://registry.npmjs.org/buffer/-/buffer-5.7.1.tgz", - "integrity": "sha512-EHcyIPBQ4BSGlvjB16k5KgAJ27CIsHY/2JBmCRReo48y9rQ3MaUzWX3KVlBa4U7MyX02HdVj0K7C3WaB3ju7FQ==", "dev": true, "funding": [ { @@ -1945,6 +1460,7 @@ "url": "https://feross.org/support" } ], + "license": "MIT", "dependencies": { "base64-js": "^1.3.1", "ieee754": "^1.1.13" @@ -1952,9 +1468,8 @@ }, "node_modules/bundle-name": { "version": "4.1.0", - "resolved": "https://registry.npmjs.org/bundle-name/-/bundle-name-4.1.0.tgz", - "integrity": "sha512-tjwM5exMg6BGRI+kNmTntNsvdZS1X8BFYS6tnJ2hdH0kVxM6/eVZ2xy+FqStSWvYmtfFMDLIxurorHwDKfDz5Q==", "dev": true, + "license": "MIT", "dependencies": { "run-applescript": "^7.0.0" }, @@ -1967,18 +1482,16 @@ }, "node_modules/cacheable-lookup": { "version": "7.0.0", - "resolved": "https://registry.npmjs.org/cacheable-lookup/-/cacheable-lookup-7.0.0.tgz", - "integrity": "sha512-+qJyx4xiKra8mZrcwhjMRMUhD5NR1R8esPkzIYxX96JiecFoxAXFuz/GpR3+ev4PE1WamHip78wV0vcmPQtp8w==", "dev": true, + "license": "MIT", "engines": { "node": ">=14.16" } }, "node_modules/cacheable-request": { "version": "10.2.14", - "resolved": "https://registry.npmjs.org/cacheable-request/-/cacheable-request-10.2.14.tgz", - "integrity": "sha512-zkDT5WAF4hSSoUgyfg5tFIxz8XQK+25W/TLVojJTMKBaxevLBBtLxgqguAuVQB8PVW79FVjHcU+GJ9tVbDZ9mQ==", "dev": true, + "license": "MIT", "dependencies": { "@types/http-cache-semantics": "^4.0.2", "get-stream": "^6.0.1", @@ -1994,9 +1507,8 @@ }, "node_modules/cacheable-request/node_modules/get-stream": { "version": "6.0.1", - "resolved": "https://registry.npmjs.org/get-stream/-/get-stream-6.0.1.tgz", - "integrity": "sha512-ts6Wi+2j3jQjqi70w5AlN8DFnkSwC+MqmxEzdEALB2qXZYV3X/b1CTfgPLGJNMeAWxdPfU8FO1ms3NUfaHCPYg==", "dev": true, + "license": "MIT", "engines": { "node": ">=10" }, @@ -2006,9 +1518,8 @@ }, "node_modules/caching-transform": { "version": "4.0.0", - "resolved": "https://registry.npmjs.org/caching-transform/-/caching-transform-4.0.0.tgz", - "integrity": "sha512-kpqOvwXnjjN44D89K5ccQC+RUrsy7jB/XLlRrx0D7/2HNcTPqzsb6XgYoErwko6QsV184CA2YgS1fxDiiDZMWA==", "dev": true, + "license": "MIT", "dependencies": { "hasha": "^5.0.0", "make-dir": "^3.0.0", @@ -2021,9 +1532,8 @@ }, "node_modules/call-bind": { "version": "1.0.5", - "resolved": "https://registry.npmjs.org/call-bind/-/call-bind-1.0.5.tgz", - "integrity": "sha512-C3nQxfFZxFRVoJoGKKI8y3MOEo129NQ+FgQ08iye+Mk4zNZZGdjfs06bVTr+DBSlA66Q2VEcMki/cUCP4SercQ==", "dev": true, + "license": "MIT", "dependencies": { "function-bind": "^1.1.2", "get-intrinsic": "^1.2.1", @@ -2035,26 +1545,22 @@ }, "node_modules/callsites": { "version": "3.1.0", - "resolved": "https://registry.npmjs.org/callsites/-/callsites-3.1.0.tgz", - "integrity": "sha512-P8BjAsXvZS+VIDUI11hHCQEv74YT67YUi5JJFNWIqL235sBmjX4+qx9Muvls5ivyNENctx46xQLQ3aTuE7ssaQ==", "dev": true, + "license": "MIT", "engines": { "node": ">=6" } }, "node_modules/camelcase": { "version": "5.3.1", - "resolved": "https://registry.npmjs.org/camelcase/-/camelcase-5.3.1.tgz", - "integrity": "sha512-L28STB170nwWS63UjtlEOE3dldQApaJXZkOI1uMFfzf3rRuPegHaHesyee+YxQ+W6SvRDQV6UrdOdRiR153wJg==", "dev": true, + "license": "MIT", "engines": { "node": ">=6" } }, "node_modules/caniuse-lite": { "version": "1.0.30001584", - "resolved": "https://registry.npmjs.org/caniuse-lite/-/caniuse-lite-1.0.30001584.tgz", - "integrity": "sha512-LOz7CCQ9M1G7OjJOF9/mzmqmj3jE/7VOmrfw6Mgs0E8cjOsbRXQJHsPBfmBOXDskXKrHLyyW3n7kpDW/4BsfpQ==", "dev": true, "funding": [ { @@ -2069,13 +1575,13 @@ "type": "github", "url": "https://github.com/sponsors/ai" } - ] + ], + "license": "CC-BY-4.0" }, "node_modules/chalk": { "version": "4.1.2", - "resolved": "https://registry.npmjs.org/chalk/-/chalk-4.1.2.tgz", - "integrity": "sha512-oKnbhFyRIXpUuez8iBMmyEa4nbj4IOQyuhc/wy9kY7/WVPcwIO9VA668Pu8RkO7+0G76SLROeyw9CpQ061i4mA==", "dev": true, + "license": "MIT", "dependencies": { "ansi-styles": "^4.1.0", "supports-color": "^7.1.0" @@ -2089,9 +1595,8 @@ }, "node_modules/chalk/node_modules/supports-color": { "version": "7.2.0", - "resolved": "https://registry.npmjs.org/supports-color/-/supports-color-7.2.0.tgz", - "integrity": "sha512-qpCAvRl9stuOHveKsn7HncJRvv501qIacKzQlO/+Lwxc9+0q2wLyv4Dfvt80/DPn2pqOBsJdDiogXGR9+OvwRw==", "dev": true, + "license": "MIT", "dependencies": { "has-flag": "^4.0.0" }, @@ -2101,14 +1606,11 @@ }, "node_modules/chardet": { "version": "0.7.0", - "resolved": "https://registry.npmjs.org/chardet/-/chardet-0.7.0.tgz", - "integrity": "sha512-mT8iDcrh03qDGRRmoA2hmBJnxpllMR+0/0qlzjqZES6NdiWDcZkCNAk4rPFZ9Q85r27unkiNNg8ZOiwZXBHwcA==", - "dev": true + "dev": true, + "license": "MIT" }, "node_modules/chokidar": { "version": "3.5.3", - "resolved": "https://registry.npmjs.org/chokidar/-/chokidar-3.5.3.tgz", - "integrity": "sha512-Dr3sfKRP6oTcjf2JmUmFJfeVMvXBdegxB0iVQ5eb2V10uFJUCAS8OByZdVAyVb8xXNz3GjjTgj9kLWsZTqE6kw==", "dev": true, "funding": [ { @@ -2116,6 +1618,7 @@ "url": "https://paulmillr.com/funding/" } ], + "license": "MIT", "dependencies": { "anymatch": "~3.1.2", "braces": "~3.0.2", @@ -2134,8 +1637,6 @@ }, "node_modules/ci-info": { "version": "3.9.0", - "resolved": "https://registry.npmjs.org/ci-info/-/ci-info-3.9.0.tgz", - "integrity": "sha512-NIxF55hv4nSqQswkAeiOi1r83xy8JldOFDTWiug55KBu9Jnblncd2U6ViHmYgHf01TPZS77NJBhBMKdWj9HQMQ==", "dev": true, "funding": [ { @@ -2143,24 +1644,23 @@ "url": "https://github.com/sponsors/sibiraj-s" } ], + "license": "MIT", "engines": { "node": ">=8" } }, "node_modules/clean-stack": { "version": "2.2.0", - "resolved": "https://registry.npmjs.org/clean-stack/-/clean-stack-2.2.0.tgz", - "integrity": "sha512-4diC9HaTE+KRAMWhDhrGOECgWZxoevMc5TlkObMqNSsVU62PYzXZ/SMTjzyGAFF1YusgxGcSWTEXBhp0CPwQ1A==", "dev": true, + "license": "MIT", "engines": { "node": ">=6" } }, "node_modules/cli-boxes": { "version": "3.0.0", - "resolved": "https://registry.npmjs.org/cli-boxes/-/cli-boxes-3.0.0.tgz", - "integrity": "sha512-/lzGpEWL/8PfI0BmBOPRwp0c/wFNX1RdUML3jK/RcSBA9T8mZDdQpqYBKtCFTOfQbwPqWEOpjqW+Fnayc0969g==", "dev": true, + "license": "MIT", "engines": { "node": ">=10" }, @@ -2170,9 +1670,8 @@ }, "node_modules/cli-cursor": { "version": "3.1.0", - "resolved": "https://registry.npmjs.org/cli-cursor/-/cli-cursor-3.1.0.tgz", - "integrity": "sha512-I/zHAwsKf9FqGoXM4WWRACob9+SNukZTd94DWF57E4toouRulbCxcUh6RKUEOQlYTHJnzkPMySvPNaaSLNfLZw==", "dev": true, + "license": "MIT", "dependencies": { "restore-cursor": "^3.1.0" }, @@ -2182,9 +1681,8 @@ }, "node_modules/cli-spinners": { "version": "2.9.2", - "resolved": "https://registry.npmjs.org/cli-spinners/-/cli-spinners-2.9.2.tgz", - "integrity": "sha512-ywqV+5MmyL4E7ybXgKys4DugZbX0FC6LnwrhjuykIjnK9k8OQacQ7axGKnjDXWNhns0xot3bZI5h55H8yo9cJg==", "dev": true, + "license": "MIT", "engines": { "node": ">=6" }, @@ -2194,18 +1692,16 @@ }, "node_modules/cli-width": { "version": "4.1.0", - "resolved": "https://registry.npmjs.org/cli-width/-/cli-width-4.1.0.tgz", - "integrity": "sha512-ouuZd4/dm2Sw5Gmqy6bGyNNNe1qt9RpmxveLSO7KcgsTnU7RXfsw+/bukWGo1abgBiMAic068rclZsO4IWmmxQ==", "dev": true, + "license": "ISC", "engines": { "node": ">= 12" } }, "node_modules/cliui": { "version": "7.0.4", - "resolved": "https://registry.npmjs.org/cliui/-/cliui-7.0.4.tgz", - "integrity": "sha512-OcRE68cOsVMXp1Yvonl/fzkQOyjLSu/8bhPDfQt0e0/Eb283TKP20Fs2MqoPsr9SwA595rRCA+QMzYc9nBP+JQ==", "dev": true, + "license": "ISC", "dependencies": { "string-width": "^4.2.0", "strip-ansi": "^6.0.0", @@ -2214,9 +1710,8 @@ }, "node_modules/cliui/node_modules/wrap-ansi": { "version": "7.0.0", - "resolved": "https://registry.npmjs.org/wrap-ansi/-/wrap-ansi-7.0.0.tgz", - "integrity": "sha512-YVGIj2kamLSTxw6NsZjoBxfSwsn0ycdesmc4p+Q21c5zPuZ1pl+NfxVdxPtdHvmNVOQ6XSYG4AUtyt/Fi7D16Q==", "dev": true, + "license": "MIT", "dependencies": { "ansi-styles": "^4.0.0", "string-width": "^4.1.0", @@ -2231,26 +1726,23 @@ }, "node_modules/clone": { "version": "1.0.4", - "resolved": "https://registry.npmjs.org/clone/-/clone-1.0.4.tgz", - "integrity": "sha512-JQHZ2QMW6l3aH/j6xCqQThY/9OH4D/9ls34cgkUBiEeocRTU04tHfKPBsUK1PqZCUQM7GiA0IIXJSuXHI64Kbg==", "dev": true, + "license": "MIT", "engines": { "node": ">=0.8" } }, "node_modules/cluster-key-slot": { "version": "1.1.2", - "resolved": "https://registry.npmjs.org/cluster-key-slot/-/cluster-key-slot-1.1.2.tgz", - "integrity": "sha512-RMr0FhtfXemyinomL4hrWcYJxmX6deFdCxpJzhDttxgO1+bcCnkk+9drydLVDmAMG7NE6aN/fl4F7ucU/90gAA==", + "license": "Apache-2.0", "engines": { "node": ">=0.10.0" } }, "node_modules/color-convert": { "version": "2.0.1", - "resolved": "https://registry.npmjs.org/color-convert/-/color-convert-2.0.1.tgz", - "integrity": "sha512-RRECPsj7iu/xb5oKYcsFHSppFNnsj/52OVTRKb4zP5onXwVF3zVmmToNcOfGC+CRDpfK/U584fMg38ZHCaElKQ==", "dev": true, + "license": "MIT", "dependencies": { "color-name": "~1.1.4" }, @@ -2260,36 +1752,31 @@ }, "node_modules/color-name": { "version": "1.1.4", - "resolved": "https://registry.npmjs.org/color-name/-/color-name-1.1.4.tgz", - "integrity": "sha512-dOy+3AuW3a2wNbZHIuMZpTcgjGuLU/uBL/ubcZF9OXbDo8ff4O8yVp5Bf0efS8uEoYo5q4Fx7dY9OgQGXgAsQA==", - "dev": true + "dev": true, + "license": "MIT" }, "node_modules/commander": { "version": "11.1.0", - "resolved": "https://registry.npmjs.org/commander/-/commander-11.1.0.tgz", - "integrity": "sha512-yPVavfyCcRhmorC7rWlkHn15b4wDVgVmBA7kV4QVBsF7kv/9TKJAbAXVTxvTnwP8HHKjRCJDClKbciiYS7p0DQ==", "dev": true, + "license": "MIT", "engines": { "node": ">=16" } }, "node_modules/commondir": { "version": "1.0.1", - "resolved": "https://registry.npmjs.org/commondir/-/commondir-1.0.1.tgz", - "integrity": "sha512-W9pAhw0ja1Edb5GVdIF1mjZw/ASI0AlShXM83UUGe2DVr5TdAPEA1OA8m/g8zWp9x6On7gqufY+FatDbC3MDQg==", - "dev": true + "dev": true, + "license": "MIT" }, "node_modules/concat-map": { "version": "0.0.1", - "resolved": "https://registry.npmjs.org/concat-map/-/concat-map-0.0.1.tgz", - "integrity": "sha512-/Srv4dswyQNBfohGpz9o6Yb3Gz3SrUDqBH5rTuhGR7ahtlbYKnVxw2bCFMRljaA7EXHaXZ8wsHdodFvbkhKmqg==", - "dev": true + "dev": true, + "license": "MIT" }, "node_modules/config-chain": { "version": "1.1.13", - "resolved": "https://registry.npmjs.org/config-chain/-/config-chain-1.1.13.tgz", - "integrity": "sha512-qj+f8APARXHrM0hraqXYb2/bOVSV4PvJQlNZ/DVj0QrmNM2q2euizkeuVckQ57J+W0mRH6Hvi+k50M4Jul2VRQ==", "dev": true, + "license": "MIT", "dependencies": { "ini": "^1.3.4", "proto-list": "~1.2.1" @@ -2297,15 +1784,13 @@ }, "node_modules/config-chain/node_modules/ini": { "version": "1.3.8", - "resolved": "https://registry.npmjs.org/ini/-/ini-1.3.8.tgz", - "integrity": "sha512-JV/yugV2uzW5iMRSiZAyDtQd+nxtUnjeLt0acNdw98kKLrvuRVyB80tsREOE7yvGVgalhZ6RNXCmEHkUKBKxew==", - "dev": true + "dev": true, + "license": "ISC" }, "node_modules/configstore": { "version": "6.0.0", - "resolved": "https://registry.npmjs.org/configstore/-/configstore-6.0.0.tgz", - "integrity": "sha512-cD31W1v3GqUlQvbBCGcXmd2Nj9SvLDOP1oQ0YFuLETufzSPaKp11rYBsSOm7rCsW3OnIRAFM3OxRhceaXNYHkA==", "dev": true, + "license": "BSD-2-Clause", "dependencies": { "dot-prop": "^6.0.1", "graceful-fs": "^4.2.6", @@ -2322,15 +1807,13 @@ }, "node_modules/convert-source-map": { "version": "1.9.0", - "resolved": "https://registry.npmjs.org/convert-source-map/-/convert-source-map-1.9.0.tgz", - "integrity": "sha512-ASFBup0Mz1uyiIjANan1jzLQami9z1PoYSZCiiYW2FczPbenXc45FZdBZLzOT+r6+iciuEModtmCti+hjaAk0A==", - "dev": true + "dev": true, + "license": "MIT" }, "node_modules/cosmiconfig": { "version": "9.0.0", - "resolved": "https://registry.npmjs.org/cosmiconfig/-/cosmiconfig-9.0.0.tgz", - "integrity": "sha512-itvL5h8RETACmOTFc4UfIyB2RfEHi71Ax6E/PivVxq9NseKbOWpeyHEOIbmAw1rs8Ak0VursQNww7lf7YtUwzg==", "dev": true, + "license": "MIT", "dependencies": { "env-paths": "^2.2.1", "import-fresh": "^3.3.0", @@ -2354,9 +1837,8 @@ }, "node_modules/cross-spawn": { "version": "7.0.3", - "resolved": "https://registry.npmjs.org/cross-spawn/-/cross-spawn-7.0.3.tgz", - "integrity": "sha512-iRDPJKUPVEND7dHPO8rkbOnPpyDygcDFtWjpeWNCgy8WP2rXcxXL8TskReQl6OrB2G7+UJrags1q15Fudc7G6w==", "dev": true, + "license": "MIT", "dependencies": { "path-key": "^3.1.0", "shebang-command": "^2.0.0", @@ -2368,9 +1850,8 @@ }, "node_modules/crypto-random-string": { "version": "4.0.0", - "resolved": "https://registry.npmjs.org/crypto-random-string/-/crypto-random-string-4.0.0.tgz", - "integrity": "sha512-x8dy3RnvYdlUcPOjkEHqozhiwzKNSq7GcPuXFbnyMOCHxX8V3OgIg/pYuabl2sbUPfIJaeAQB7PMOK8DFIdoRA==", "dev": true, + "license": "MIT", "dependencies": { "type-fest": "^1.0.1" }, @@ -2383,9 +1864,8 @@ }, "node_modules/crypto-random-string/node_modules/type-fest": { "version": "1.4.0", - "resolved": "https://registry.npmjs.org/type-fest/-/type-fest-1.4.0.tgz", - "integrity": "sha512-yGSza74xk0UG8k+pLh5oeoYirvIiWo5t0/o3zHHAO2tRDiZcxWP7fywNlXhqb6/r6sWvwi+RsyQMWhVLe4BVuA==", "dev": true, + "license": "(MIT OR CC0-1.0)", "engines": { "node": ">=10" }, @@ -2395,18 +1875,16 @@ }, "node_modules/data-uri-to-buffer": { "version": "4.0.1", - "resolved": "https://registry.npmjs.org/data-uri-to-buffer/-/data-uri-to-buffer-4.0.1.tgz", - "integrity": "sha512-0R9ikRb668HB7QDxT1vkpuUBtqc53YyAwMwGeUFKRojY/NWKvdZ+9UYtRfGmhqNbRkTSVpMbmyhXipFFv2cb/A==", "dev": true, + "license": "MIT", "engines": { "node": ">= 12" } }, "node_modules/debug": { "version": "4.3.4", - "resolved": "https://registry.npmjs.org/debug/-/debug-4.3.4.tgz", - "integrity": "sha512-PRWFHuSU3eDtQJPvnNY7Jcket1j0t5OuOsFzPPzsekD52Zl8qUfFIPEiswXqIvHWGVHOgX+7G/vCNNhehwxfkQ==", "dev": true, + "license": "MIT", "dependencies": { "ms": "2.1.2" }, @@ -2421,24 +1899,21 @@ }, "node_modules/debug/node_modules/ms": { "version": "2.1.2", - "resolved": "https://registry.npmjs.org/ms/-/ms-2.1.2.tgz", - "integrity": "sha512-sGkPx+VjMtmA6MX27oA4FBFELFCZZ4S4XqeGOXCv68tT+jb3vk/RyaKWP0PTKyWtmLSM0b+adUTEvbs1PEaH2w==", - "dev": true + "dev": true, + "license": "MIT" }, "node_modules/decamelize": { "version": "1.2.0", - "resolved": "https://registry.npmjs.org/decamelize/-/decamelize-1.2.0.tgz", - "integrity": "sha512-z2S+W9X73hAUUki+N+9Za2lBlun89zigOyGrsax+KUQ6wKW4ZoWpEYBkGhQjwAjjDCkWxhY0VKEhk8wzY7F5cA==", "dev": true, + "license": "MIT", "engines": { "node": ">=0.10.0" } }, "node_modules/decompress-response": { "version": "6.0.0", - "resolved": "https://registry.npmjs.org/decompress-response/-/decompress-response-6.0.0.tgz", - "integrity": "sha512-aW35yZM6Bb/4oJlZncMH2LCoZtJXTRxES17vE3hoRiowU2kWHaJKFkSBDnDR+cm9J+9QhXmREyIfv0pji9ejCQ==", "dev": true, + "license": "MIT", "dependencies": { "mimic-response": "^3.1.0" }, @@ -2451,9 +1926,8 @@ }, "node_modules/decompress-response/node_modules/mimic-response": { "version": "3.1.0", - "resolved": "https://registry.npmjs.org/mimic-response/-/mimic-response-3.1.0.tgz", - "integrity": "sha512-z0yWI+4FDrrweS8Zmt4Ej5HdJmky15+L2e6Wgn3+iK5fWzb6T3fhNFq2+MeTRb064c6Wr4N/wv0DzQTjNzHNGQ==", "dev": true, + "license": "MIT", "engines": { "node": ">=10" }, @@ -2463,18 +1937,16 @@ }, "node_modules/deep-extend": { "version": "0.6.0", - "resolved": "https://registry.npmjs.org/deep-extend/-/deep-extend-0.6.0.tgz", - "integrity": "sha512-LOHxIOaPYdHlJRtCQfDIVZtfw/ufM8+rVj649RIHzcm/vGwQRXFt6OPqIFWsm2XEMrNIEtWR64sY1LEKD2vAOA==", "dev": true, + "license": "MIT", "engines": { "node": ">=4.0.0" } }, "node_modules/default-browser": { "version": "5.2.1", - "resolved": "https://registry.npmjs.org/default-browser/-/default-browser-5.2.1.tgz", - "integrity": "sha512-WY/3TUME0x3KPYdRRxEJJvXRHV4PyPoUsxtZa78lwItwRQRHhd2U9xOscaT/YTf8uCXIAjeJOFBVEh/7FtD8Xg==", "dev": true, + "license": "MIT", "dependencies": { "bundle-name": "^4.1.0", "default-browser-id": "^5.0.0" @@ -2488,9 +1960,8 @@ }, "node_modules/default-browser-id": { "version": "5.0.0", - "resolved": "https://registry.npmjs.org/default-browser-id/-/default-browser-id-5.0.0.tgz", - "integrity": "sha512-A6p/pu/6fyBcA1TRz/GqWYPViplrftcW2gZC9q79ngNCKAeR/X3gcEdXQHl4KNXV+3wgIJ1CPkJQ3IHM6lcsyA==", "dev": true, + "license": "MIT", "engines": { "node": ">=18" }, @@ -2500,9 +1971,8 @@ }, "node_modules/default-require-extensions": { "version": "3.0.1", - "resolved": "https://registry.npmjs.org/default-require-extensions/-/default-require-extensions-3.0.1.tgz", - "integrity": "sha512-eXTJmRbm2TIt9MgWTsOH1wEuhew6XGZcMeGKCtLedIg/NCsg1iBePXkceTdK4Fii7pzmN9tGsZhKzZ4h7O/fxw==", "dev": true, + "license": "MIT", "dependencies": { "strip-bom": "^4.0.0" }, @@ -2515,9 +1985,8 @@ }, "node_modules/defaults": { "version": "1.0.4", - "resolved": "https://registry.npmjs.org/defaults/-/defaults-1.0.4.tgz", - "integrity": "sha512-eFuaLoy/Rxalv2kr+lqMlUnrDWV+3j4pljOIJgLIhI058IQfWJ7vXhyEIHu+HtC738klGALYxOKDO0bQP3tg8A==", "dev": true, + "license": "MIT", "dependencies": { "clone": "^1.0.2" }, @@ -2527,18 +1996,16 @@ }, "node_modules/defer-to-connect": { "version": "2.0.1", - "resolved": "https://registry.npmjs.org/defer-to-connect/-/defer-to-connect-2.0.1.tgz", - "integrity": "sha512-4tvttepXG1VaYGrRibk5EwJd1t4udunSOVMdLSAL6mId1ix438oPwPZMALY41FCijukO1L0twNcGsdzS7dHgDg==", "dev": true, + "license": "MIT", "engines": { "node": ">=10" } }, "node_modules/define-data-property": { "version": "1.1.1", - "resolved": "https://registry.npmjs.org/define-data-property/-/define-data-property-1.1.1.tgz", - "integrity": "sha512-E7uGkTzkk1d0ByLeSc6ZsFS79Axg+m1P/VsgYsxHgiuc3tFSj+MjMIwe90FC4lOAZzNBdY7kkO2P2wKdsQ1vgQ==", "dev": true, + "license": "MIT", "dependencies": { "get-intrinsic": "^1.2.1", "gopd": "^1.0.1", @@ -2550,9 +2017,8 @@ }, "node_modules/define-lazy-prop": { "version": "3.0.0", - "resolved": "https://registry.npmjs.org/define-lazy-prop/-/define-lazy-prop-3.0.0.tgz", - "integrity": "sha512-N+MeXYoqr3pOgn8xfyRPREN7gHakLYjhsHhWGT3fWAiL4IkAt0iDw14QiiEm2bE30c5XX5q0FtAA3CK5f9/BUg==", "dev": true, + "license": "MIT", "engines": { "node": ">=12" }, @@ -2562,9 +2028,8 @@ }, "node_modules/define-properties": { "version": "1.2.1", - "resolved": "https://registry.npmjs.org/define-properties/-/define-properties-1.2.1.tgz", - "integrity": "sha512-8QmQKqEASLd5nx0U1B1okLElbUuuttJ/AnYmRXbbbGDWh6uS208EjD4Xqq/I9wK7u0v6O08XhTWnt5XtEbR6Dg==", "dev": true, + "license": "MIT", "dependencies": { "define-data-property": "^1.0.1", "has-property-descriptors": "^1.0.0", @@ -2579,9 +2044,8 @@ }, "node_modules/degenerator": { "version": "5.0.1", - "resolved": "https://registry.npmjs.org/degenerator/-/degenerator-5.0.1.tgz", - "integrity": "sha512-TllpMR/t0M5sqCXfj85i4XaAzxmS5tVA16dqvdkMwGmzI+dXLXnw3J+3Vdv7VKw+ThlTMboK6i9rnZ6Nntj5CQ==", "dev": true, + "license": "MIT", "dependencies": { "ast-types": "^0.13.4", "escodegen": "^2.1.0", @@ -2593,24 +2057,21 @@ }, "node_modules/deprecation": { "version": "2.3.1", - "resolved": "https://registry.npmjs.org/deprecation/-/deprecation-2.3.1.tgz", - "integrity": "sha512-xmHIy4F3scKVwMsQ4WnVaS8bHOx0DmVwRywosKhaILI0ywMDWPtBSku2HNxRvF7jtwDRsoEwYQSfbxj8b7RlJQ==", - "dev": true + "dev": true, + "license": "ISC" }, "node_modules/diff": { "version": "5.0.0", - "resolved": "https://registry.npmjs.org/diff/-/diff-5.0.0.tgz", - "integrity": "sha512-/VTCrvm5Z0JGty/BWHljh+BAiw3IK+2j87NGMu8Nwc/f48WoDAC395uomO9ZD117ZOBaHmkX1oyLvkVM/aIT3w==", "dev": true, + "license": "BSD-3-Clause", "engines": { "node": ">=0.3.1" } }, "node_modules/dot-prop": { "version": "6.0.1", - "resolved": "https://registry.npmjs.org/dot-prop/-/dot-prop-6.0.1.tgz", - "integrity": "sha512-tE7ztYzXHIeyvc7N+hR3oi7FIbf/NIjVP9hmAt3yMXzrQ072/fpjGLx2GxNxGxUl5V73MEqYzioOMoVhGMJ5cA==", "dev": true, + "license": "MIT", "dependencies": { "is-obj": "^2.0.0" }, @@ -2623,51 +2084,44 @@ }, "node_modules/eastasianwidth": { "version": "0.2.0", - "resolved": "https://registry.npmjs.org/eastasianwidth/-/eastasianwidth-0.2.0.tgz", - "integrity": "sha512-I88TYZWc9XiYHRQ4/3c5rjjfgkjhLyW2luGIheGERbNQ6OY7yTybanSpDXZa8y7VUP9YmDcYa+eyq4ca7iLqWA==", - "dev": true + "dev": true, + "license": "MIT" }, "node_modules/electron-to-chromium": { "version": "1.4.656", - "resolved": "https://registry.npmjs.org/electron-to-chromium/-/electron-to-chromium-1.4.656.tgz", - "integrity": "sha512-9AQB5eFTHyR3Gvt2t/NwR0le2jBSUNwCnMbUCejFWHD+so4tH40/dRLgoE+jxlPeWS43XJewyvCv+I8LPMl49Q==", - "dev": true + "dev": true, + "license": "ISC" }, "node_modules/email-addresses": { "version": "5.0.0", - "resolved": "https://registry.npmjs.org/email-addresses/-/email-addresses-5.0.0.tgz", - "integrity": "sha512-4OIPYlA6JXqtVn8zpHpGiI7vE6EQOAg16aGnDMIAlZVinnoZ8208tW1hAbjWydgN/4PLTT9q+O1K6AH/vALJGw==", - "dev": true + "dev": true, + "license": "MIT" }, "node_modules/emoji-regex": { "version": "8.0.0", - "resolved": "https://registry.npmjs.org/emoji-regex/-/emoji-regex-8.0.0.tgz", - "integrity": "sha512-MSjYzcWNOA0ewAHpz0MxpYFvwg6yjy1NG3xteoqz644VCo/RPgnr1/GGt+ic3iJTzQ8Eu3TdM14SawnVUmGE6A==", - "dev": true + "dev": true, + "license": "MIT" }, "node_modules/env-paths": { "version": "2.2.1", - "resolved": "https://registry.npmjs.org/env-paths/-/env-paths-2.2.1.tgz", - "integrity": "sha512-+h1lkLKhZMTYjog1VEpJNG7NZJWcuc2DDk/qsqSTRRCOXiLjeQ1d1/udrUGhqMxUgAlwKNZ0cf2uqan5GLuS2A==", "dev": true, + "license": "MIT", "engines": { "node": ">=6" } }, "node_modules/error-ex": { "version": "1.3.2", - "resolved": "https://registry.npmjs.org/error-ex/-/error-ex-1.3.2.tgz", - "integrity": "sha512-7dFHNmqeFSEt2ZBsCriorKnn3Z2pj+fd9kmI6QoWw4//DL+icEBfc0U7qJCisqrTsKTjw4fNFy2pW9OqStD84g==", "dev": true, + "license": "MIT", "dependencies": { "is-arrayish": "^0.2.1" } }, "node_modules/es-abstract": { "version": "1.22.3", - "resolved": "https://registry.npmjs.org/es-abstract/-/es-abstract-1.22.3.tgz", - "integrity": "sha512-eiiY8HQeYfYH2Con2berK+To6GrK2RxbPawDkGq4UiCQQfZHb6wX9qQqkbpPqaxQFcl8d9QzZqo0tGE0VcrdwA==", "dev": true, + "license": "MIT", "dependencies": { "array-buffer-byte-length": "^1.0.0", "arraybuffer.prototype.slice": "^1.0.2", @@ -2718,24 +2172,21 @@ }, "node_modules/es-array-method-boxes-properly": { "version": "1.0.0", - "resolved": "https://registry.npmjs.org/es-array-method-boxes-properly/-/es-array-method-boxes-properly-1.0.0.tgz", - "integrity": "sha512-wd6JXUmyHmt8T5a2xreUwKcGPq6f1f+WwIJkijUqiGcJz1qqnZgP6XIK+QyIWU5lT7imeNxUll48bziG+TSYcA==", - "dev": true + "dev": true, + "license": "MIT" }, "node_modules/es-errors": { "version": "1.3.0", - "resolved": "https://registry.npmjs.org/es-errors/-/es-errors-1.3.0.tgz", - "integrity": "sha512-Zf5H2Kxt2xjTvbJvP2ZWLEICxA6j+hAmMzIlypy4xcBg1vKVnx89Wy0GbS+kf5cwCVFFzdCFh2XSCFNULS6csw==", "dev": true, + "license": "MIT", "engines": { "node": ">= 0.4" } }, "node_modules/es-get-iterator": { "version": "1.1.3", - "resolved": "https://registry.npmjs.org/es-get-iterator/-/es-get-iterator-1.1.3.tgz", - "integrity": "sha512-sPZmqHBe6JIiTfN5q2pEi//TwxmAFHwj/XEuYjTuse78i8KxaqMTTzxPoFKuzRpDpTJ+0NAbpfenkmH2rePtuw==", "dev": true, + "license": "MIT", "dependencies": { "call-bind": "^1.0.2", "get-intrinsic": "^1.1.3", @@ -2753,9 +2204,8 @@ }, "node_modules/es-set-tostringtag": { "version": "2.0.2", - "resolved": "https://registry.npmjs.org/es-set-tostringtag/-/es-set-tostringtag-2.0.2.tgz", - "integrity": "sha512-BuDyupZt65P9D2D2vA/zqcI3G5xRsklm5N3xCwuiy+/vKy8i0ifdsQP1sLgO4tZDSCaQUSnmC48khknGMV3D2Q==", "dev": true, + "license": "MIT", "dependencies": { "get-intrinsic": "^1.2.2", "has-tostringtag": "^1.0.0", @@ -2767,9 +2217,8 @@ }, "node_modules/es-to-primitive": { "version": "1.2.1", - "resolved": "https://registry.npmjs.org/es-to-primitive/-/es-to-primitive-1.2.1.tgz", - "integrity": "sha512-QCOllgZJtaUo9miYBcLChTUaHNjJF3PYs1VidD7AwiEj1kYxKeQTctLAezAOH5ZKRH0g2IgPn6KwB4IT8iRpvA==", "dev": true, + "license": "MIT", "dependencies": { "is-callable": "^1.1.4", "is-date-object": "^1.0.1", @@ -2784,16 +2233,14 @@ }, "node_modules/es6-error": { "version": "4.1.1", - "resolved": "https://registry.npmjs.org/es6-error/-/es6-error-4.1.1.tgz", - "integrity": "sha512-Um/+FxMr9CISWh0bi5Zv0iOD+4cFh5qLeks1qhAopKVAJw3drgKbKySikp7wGhDL0HPeaja0P5ULZrxLkniUVg==", - "dev": true + "dev": true, + "license": "MIT" }, "node_modules/esbuild": { "version": "0.19.12", - "resolved": "https://registry.npmjs.org/esbuild/-/esbuild-0.19.12.tgz", - "integrity": "sha512-aARqgq8roFBj054KvQr5f1sFu0D65G+miZRCuJyJ0G13Zwx7vRar5Zhn2tkQNzIXcBrNVsv/8stehpj+GAjgbg==", "dev": true, "hasInstallScript": true, + "license": "MIT", "bin": { "esbuild": "bin/esbuild" }, @@ -2828,18 +2275,16 @@ }, "node_modules/escalade": { "version": "3.1.1", - "resolved": "https://registry.npmjs.org/escalade/-/escalade-3.1.1.tgz", - "integrity": "sha512-k0er2gUkLf8O0zKJiAhmkTnJlTvINGv7ygDNPbeIsX/TJjGJZHuh9B2UxbsaEkmlEo9MfhrSzmhIlhRlI2GXnw==", "dev": true, + "license": "MIT", "engines": { "node": ">=6" } }, "node_modules/escape-goat": { "version": "4.0.0", - "resolved": "https://registry.npmjs.org/escape-goat/-/escape-goat-4.0.0.tgz", - "integrity": "sha512-2Sd4ShcWxbx6OY1IHyla/CVNwvg7XwZVoXZHcSu9w9SReNP1EzzD5T8NWKIR38fIqEns9kDWKUQTXXAmlDrdPg==", "dev": true, + "license": "MIT", "engines": { "node": ">=12" }, @@ -2849,9 +2294,8 @@ }, "node_modules/escape-string-regexp": { "version": "4.0.0", - "resolved": "https://registry.npmjs.org/escape-string-regexp/-/escape-string-regexp-4.0.0.tgz", - "integrity": "sha512-TtpcNJ3XAzx3Gq8sWRzJaVajRs0uVxA2YAkdb1jm2YkPz4G6egUFAyA3n5vtEIZefPk5Wa4UXbKuS5fKkJWdgA==", "dev": true, + "license": "MIT", "engines": { "node": ">=10" }, @@ -2861,9 +2305,8 @@ }, "node_modules/escodegen": { "version": "2.1.0", - "resolved": "https://registry.npmjs.org/escodegen/-/escodegen-2.1.0.tgz", - "integrity": "sha512-2NlIDTwUWJN0mRPQOdtQBzbUHvdGY2P1VXSyU83Q3xKxM7WHX2Ql8dKq782Q9TgQUNOLEzEYu9bzLNj1q88I5w==", "dev": true, + "license": "BSD-2-Clause", "dependencies": { "esprima": "^4.0.1", "estraverse": "^5.2.0", @@ -2882,9 +2325,8 @@ }, "node_modules/esprima": { "version": "4.0.1", - "resolved": "https://registry.npmjs.org/esprima/-/esprima-4.0.1.tgz", - "integrity": "sha512-eGuFFw7Upda+g4p+QHvnW0RyTX/SVeJBDM/gCtMARO0cLuT2HcEKnTPvhjV6aGeqrCB/sbNop0Kszm0jsaWU4A==", "dev": true, + "license": "BSD-2-Clause", "bin": { "esparse": "bin/esparse.js", "esvalidate": "bin/esvalidate.js" @@ -2895,27 +2337,24 @@ }, "node_modules/estraverse": { "version": "5.3.0", - "resolved": "https://registry.npmjs.org/estraverse/-/estraverse-5.3.0.tgz", - "integrity": "sha512-MMdARuVEQziNTeJD8DgMqmhwR11BRQ/cBP+pLtYdSTnf3MIO8fFeiINEbX36ZdNlfU/7A9f3gUw49B3oQsvwBA==", "dev": true, + "license": "BSD-2-Clause", "engines": { "node": ">=4.0" } }, "node_modules/esutils": { "version": "2.0.3", - "resolved": "https://registry.npmjs.org/esutils/-/esutils-2.0.3.tgz", - "integrity": "sha512-kVscqXk4OCp68SZ0dkgEKVi6/8ij300KBWTJq32P/dYeWTSwK41WyTxalN1eRmA5Z9UU/LX9D7FWSmV9SAYx6g==", "dev": true, + "license": "BSD-2-Clause", "engines": { "node": ">=0.10.0" } }, "node_modules/execa": { "version": "8.0.1", - "resolved": "https://registry.npmjs.org/execa/-/execa-8.0.1.tgz", - "integrity": "sha512-VyhnebXciFV2DESc+p6B+y0LjSm0krU4OgJN44qFAhBY0TJ+1V61tYD2+wHusZ6F9n5K+vl8k0sTy7PEfV4qpg==", "dev": true, + "license": "MIT", "dependencies": { "cross-spawn": "^7.0.3", "get-stream": "^8.0.1", @@ -2936,9 +2375,8 @@ }, "node_modules/execa/node_modules/is-stream": { "version": "3.0.0", - "resolved": "https://registry.npmjs.org/is-stream/-/is-stream-3.0.0.tgz", - "integrity": "sha512-LnQR4bZ9IADDRSkvpqMGvt/tEJWclzklNgSw48V5EAaAeDd6qGvN8ei6k5p0tvxSR171VmGyHuTiAOfxAbr8kA==", "dev": true, + "license": "MIT", "engines": { "node": "^12.20.0 || ^14.13.1 || >=16.0.0" }, @@ -2948,9 +2386,8 @@ }, "node_modules/execa/node_modules/signal-exit": { "version": "4.1.0", - "resolved": "https://registry.npmjs.org/signal-exit/-/signal-exit-4.1.0.tgz", - "integrity": "sha512-bzyZ1e88w9O1iNJbKnOlvYTrWPDl46O1bG0D3XInv+9tkPrxrN8jUUTiFlDkkmKWgn1M6CfIA13SuGqOa9Korw==", "dev": true, + "license": "ISC", "engines": { "node": ">=14" }, @@ -2960,9 +2397,8 @@ }, "node_modules/external-editor": { "version": "3.1.0", - "resolved": "https://registry.npmjs.org/external-editor/-/external-editor-3.1.0.tgz", - "integrity": "sha512-hMQ4CX1p1izmuLYyZqLMO/qGNw10wSv9QDCPfzXfyFrOaCSSoRfqE1Kf1s5an66J5JZC62NewG+mK49jOCtQew==", "dev": true, + "license": "MIT", "dependencies": { "chardet": "^0.7.0", "iconv-lite": "^0.4.24", @@ -2974,9 +2410,8 @@ }, "node_modules/fast-glob": { "version": "3.3.2", - "resolved": "https://registry.npmjs.org/fast-glob/-/fast-glob-3.3.2.tgz", - "integrity": "sha512-oX2ruAFQwf/Orj8m737Y5adxDQO0LAB7/S5MnxCdTNDd4p6BsyIVsv9JQsATbTSq8KHRpLwIHbVlUNatxd+1Ow==", "dev": true, + "license": "MIT", "dependencies": { "@nodelib/fs.stat": "^2.0.2", "@nodelib/fs.walk": "^1.2.3", @@ -2990,17 +2425,14 @@ }, "node_modules/fastq": { "version": "1.17.1", - "resolved": "https://registry.npmjs.org/fastq/-/fastq-1.17.1.tgz", - "integrity": "sha512-sRVD3lWVIXWg6By68ZN7vho9a1pQcN/WBFaAAsDDFzlJjvoGx0P8z7V1t72grFJfJhu3YPZBuu25f7Kaw2jN1w==", "dev": true, + "license": "ISC", "dependencies": { "reusify": "^1.0.4" } }, "node_modules/fetch-blob": { "version": "3.2.0", - "resolved": "https://registry.npmjs.org/fetch-blob/-/fetch-blob-3.2.0.tgz", - "integrity": "sha512-7yAQpD2UMJzLi1Dqv7qFYnPbaPx7ZfFK6PiIxQ4PfkGPyNyl2Ugx+a/umUonmKqjhM4DnfbMvdX6otXq83soQQ==", "dev": true, "funding": [ { @@ -3012,6 +2444,7 @@ "url": "https://paypal.me/jimmywarting" } ], + "license": "MIT", "dependencies": { "node-domexception": "^1.0.0", "web-streams-polyfill": "^3.0.3" @@ -3022,9 +2455,8 @@ }, "node_modules/figures": { "version": "5.0.0", - "resolved": "https://registry.npmjs.org/figures/-/figures-5.0.0.tgz", - "integrity": "sha512-ej8ksPF4x6e5wvK9yevct0UCXh8TTFlWGVLlgjZuoBH1HwjIfKE/IdL5mq89sFA7zELi1VhKpmtDnrs7zWyeyg==", "dev": true, + "license": "MIT", "dependencies": { "escape-string-regexp": "^5.0.0", "is-unicode-supported": "^1.2.0" @@ -3038,9 +2470,8 @@ }, "node_modules/figures/node_modules/escape-string-regexp": { "version": "5.0.0", - "resolved": "https://registry.npmjs.org/escape-string-regexp/-/escape-string-regexp-5.0.0.tgz", - "integrity": "sha512-/veY75JbMK4j1yjvuUxuVsiS/hr/4iHs9FTT6cgTexxdE0Ly/glccBAkloH/DofkjRbZU3bnoj38mOmhkZ0lHw==", "dev": true, + "license": "MIT", "engines": { "node": ">=12" }, @@ -3050,9 +2481,8 @@ }, "node_modules/figures/node_modules/is-unicode-supported": { "version": "1.3.0", - "resolved": "https://registry.npmjs.org/is-unicode-supported/-/is-unicode-supported-1.3.0.tgz", - "integrity": "sha512-43r2mRvz+8JRIKnWJ+3j8JtjRKZ6GmjzfaE/qiBJnikNnYv/6bagRJ1kUhNk8R5EX/GkobD+r+sfxCPJsiKBLQ==", "dev": true, + "license": "MIT", "engines": { "node": ">=12" }, @@ -3062,18 +2492,16 @@ }, "node_modules/filename-reserved-regex": { "version": "2.0.0", - "resolved": "https://registry.npmjs.org/filename-reserved-regex/-/filename-reserved-regex-2.0.0.tgz", - "integrity": "sha512-lc1bnsSr4L4Bdif8Xb/qrtokGbq5zlsms/CYH8PP+WtCkGNF65DPiQY8vG3SakEdRn8Dlnm+gW/qWKKjS5sZzQ==", "dev": true, + "license": "MIT", "engines": { "node": ">=4" } }, "node_modules/filenamify": { "version": "4.3.0", - "resolved": "https://registry.npmjs.org/filenamify/-/filenamify-4.3.0.tgz", - "integrity": "sha512-hcFKyUG57yWGAzu1CMt/dPzYZuv+jAJUT85bL8mrXvNe6hWj6yEHEc4EdcgiA6Z3oi1/9wXJdZPXF2dZNgwgOg==", "dev": true, + "license": "MIT", "dependencies": { "filename-reserved-regex": "^2.0.0", "strip-outer": "^1.0.1", @@ -3088,9 +2516,8 @@ }, "node_modules/fill-range": { "version": "7.0.1", - "resolved": "https://registry.npmjs.org/fill-range/-/fill-range-7.0.1.tgz", - "integrity": "sha512-qOo9F+dMUmC2Lcb4BbVvnKJxTPjCm+RRpe4gDuGrzkL7mEVl/djYSu2OdQ2Pa302N4oqkSg9ir6jaLWJ2USVpQ==", "dev": true, + "license": "MIT", "dependencies": { "to-regex-range": "^5.0.1" }, @@ -3100,9 +2527,8 @@ }, "node_modules/find-cache-dir": { "version": "3.3.2", - "resolved": "https://registry.npmjs.org/find-cache-dir/-/find-cache-dir-3.3.2.tgz", - "integrity": "sha512-wXZV5emFEjrridIgED11OoUKLxiYjAcqot/NJdAkOhlJ+vGzwhOAfcG5OX1jP+S0PcjEn8bdMJv+g2jwQ3Onig==", "dev": true, + "license": "MIT", "dependencies": { "commondir": "^1.0.1", "make-dir": "^3.0.2", @@ -3117,9 +2543,8 @@ }, "node_modules/find-up": { "version": "5.0.0", - "resolved": "https://registry.npmjs.org/find-up/-/find-up-5.0.0.tgz", - "integrity": "sha512-78/PXT1wlLLDgTzDs7sjq9hzz0vXD+zn+7wypEe4fXQxCmdmqfGsEPQxmiCSQI3ajFV91bVSsvNtrJRiW6nGng==", "dev": true, + "license": "MIT", "dependencies": { "locate-path": "^6.0.0", "path-exists": "^4.0.0" @@ -3133,27 +2558,24 @@ }, "node_modules/flat": { "version": "5.0.2", - "resolved": "https://registry.npmjs.org/flat/-/flat-5.0.2.tgz", - "integrity": "sha512-b6suED+5/3rTpUBdG1gupIl8MPFCAMA0QXwmljLhvCUKcUvdE4gWky9zpuGCcXHOsz4J9wPGNWq6OKpmIzz3hQ==", "dev": true, + "license": "BSD-3-Clause", "bin": { "flat": "cli.js" } }, "node_modules/for-each": { "version": "0.3.3", - "resolved": "https://registry.npmjs.org/for-each/-/for-each-0.3.3.tgz", - "integrity": "sha512-jqYfLp7mo9vIyQf8ykW2v7A+2N4QjeCeI5+Dz9XraiO1ign81wjiH7Fb9vSOWvQfNtmSa4H2RoQTrrXivdUZmw==", "dev": true, + "license": "MIT", "dependencies": { "is-callable": "^1.1.3" } }, "node_modules/foreground-child": { "version": "2.0.0", - "resolved": "https://registry.npmjs.org/foreground-child/-/foreground-child-2.0.0.tgz", - "integrity": "sha512-dCIq9FpEcyQyXKCkyzmlPTFNgrCzPudOe+mhvJU5zAtlBnGVy2yKxtfsxK2tQBThwq225jcvBjpw1Gr40uzZCA==", "dev": true, + "license": "ISC", "dependencies": { "cross-spawn": "^7.0.0", "signal-exit": "^3.0.2" @@ -3164,18 +2586,16 @@ }, "node_modules/form-data-encoder": { "version": "2.1.4", - "resolved": "https://registry.npmjs.org/form-data-encoder/-/form-data-encoder-2.1.4.tgz", - "integrity": "sha512-yDYSgNMraqvnxiEXO4hi88+YZxaHC6QKzb5N84iRCTDeRO7ZALpir/lVmf/uXUhnwUr2O4HU8s/n6x+yNjQkHw==", "dev": true, + "license": "MIT", "engines": { "node": ">= 14.17" } }, "node_modules/formdata-polyfill": { "version": "4.0.10", - "resolved": "https://registry.npmjs.org/formdata-polyfill/-/formdata-polyfill-4.0.10.tgz", - "integrity": "sha512-buewHzMvYL29jdeQTVILecSaZKnt/RJWjoZCF5OW60Z67/GmSLBkOFM7qh1PI3zFNtJbaZL5eQu1vLfazOwj4g==", "dev": true, + "license": "MIT", "dependencies": { "fetch-blob": "^3.1.2" }, @@ -3185,8 +2605,6 @@ }, "node_modules/fromentries": { "version": "1.3.2", - "resolved": "https://registry.npmjs.org/fromentries/-/fromentries-1.3.2.tgz", - "integrity": "sha512-cHEpEQHUg0f8XdtZCc2ZAhrHzKzT0MrFUTcvx+hfxYu7rGMDc5SKoXFh+n4YigxsHXRzc6OrCshdR1bWH6HHyg==", "dev": true, "funding": [ { @@ -3201,13 +2619,13 @@ "type": "consulting", "url": "https://feross.org/support" } - ] + ], + "license": "MIT" }, "node_modules/fs-extra": { "version": "11.2.0", - "resolved": "https://registry.npmjs.org/fs-extra/-/fs-extra-11.2.0.tgz", - "integrity": "sha512-PmDi3uwK5nFuXh7XDTlVnS17xJS7vW36is2+w3xcv8SVxiB4NyATf4ctkVY5bkSjX0Y4nbvZCq1/EjtEyr9ktw==", "dev": true, + "license": "MIT", "dependencies": { "graceful-fs": "^4.2.0", "jsonfile": "^6.0.1", @@ -3219,38 +2637,21 @@ }, "node_modules/fs.realpath": { "version": "1.0.0", - "resolved": "https://registry.npmjs.org/fs.realpath/-/fs.realpath-1.0.0.tgz", - "integrity": "sha512-OO0pH2lK6a0hZnAdau5ItzHPI6pUlvI7jMVnxUQRtw4owF2wk8lOSabtGDCTP4Ggrg2MbGnWO9X8K1t4+fGMDw==", - "dev": true - }, - "node_modules/fsevents": { - "version": "2.3.3", - "resolved": "https://registry.npmjs.org/fsevents/-/fsevents-2.3.3.tgz", - "integrity": "sha512-5xoDfX+fL7faATnagmWPpbFtwh/R77WmMMqqHGS65C3vvB0YHrgF+B1YmZ3441tMj5n63k0212XNoJwzlhffQw==", "dev": true, - "hasInstallScript": true, - "optional": true, - "os": [ - "darwin" - ], - "engines": { - "node": "^8.16.0 || ^10.6.0 || >=11.0.0" - } + "license": "ISC" }, "node_modules/function-bind": { "version": "1.1.2", - "resolved": "https://registry.npmjs.org/function-bind/-/function-bind-1.1.2.tgz", - "integrity": "sha512-7XHNxH7qX9xG5mIwxkhumTox/MIRNcOgDrxWsMt2pAr23WHp6MrRlN7FBSFpCpr+oVO0F744iUgR82nJMfG2SA==", "dev": true, + "license": "MIT", "funding": { "url": "https://github.com/sponsors/ljharb" } }, "node_modules/function.prototype.name": { "version": "1.1.6", - "resolved": "https://registry.npmjs.org/function.prototype.name/-/function.prototype.name-1.1.6.tgz", - "integrity": "sha512-Z5kx79swU5P27WEayXM1tBi5Ze/lbIyiNgU3qyXUOf9b2rgXYyF9Dy9Cx+IQv/Lc8WCG6L82zwUPpSS9hGehIg==", "dev": true, + "license": "MIT", "dependencies": { "call-bind": "^1.0.2", "define-properties": "^1.2.0", @@ -3266,36 +2667,32 @@ }, "node_modules/functions-have-names": { "version": "1.2.3", - "resolved": "https://registry.npmjs.org/functions-have-names/-/functions-have-names-1.2.3.tgz", - "integrity": "sha512-xckBUXyTIqT97tq2x2AMb+g163b5JFysYk0x4qxNFwbfQkmNZoiRHb6sPzI9/QV33WeuvVYBUIiD4NzNIyqaRQ==", "dev": true, + "license": "MIT", "funding": { "url": "https://github.com/sponsors/ljharb" } }, "node_modules/gensync": { "version": "1.0.0-beta.2", - "resolved": "https://registry.npmjs.org/gensync/-/gensync-1.0.0-beta.2.tgz", - "integrity": "sha512-3hN7NaskYvMDLQY55gnW3NQ+mesEAepTqlg+VEbj7zzqEMBVNhzcGYYeqFo/TlYz6eQiFcp1HcsCZO+nGgS8zg==", "dev": true, + "license": "MIT", "engines": { "node": ">=6.9.0" } }, "node_modules/get-caller-file": { "version": "2.0.5", - "resolved": "https://registry.npmjs.org/get-caller-file/-/get-caller-file-2.0.5.tgz", - "integrity": "sha512-DyFP3BM/3YHTQOCUL/w0OZHR0lpKeGrxotcHWcqNEdnltqFwXVfhEBQ94eIo34AfQpo0rGki4cyIiftY06h2Fg==", "dev": true, + "license": "ISC", "engines": { "node": "6.* || 8.* || >= 10.*" } }, "node_modules/get-east-asian-width": { "version": "1.2.0", - "resolved": "https://registry.npmjs.org/get-east-asian-width/-/get-east-asian-width-1.2.0.tgz", - "integrity": "sha512-2nk+7SIVb14QrgXFHcm84tD4bKQz0RxPuMT8Ag5KPOq7J5fEmAg0UbXdTOSHqNuHSU28k55qnceesxXRZGzKWA==", "dev": true, + "license": "MIT", "engines": { "node": ">=18" }, @@ -3305,9 +2702,8 @@ }, "node_modules/get-intrinsic": { "version": "1.2.3", - "resolved": "https://registry.npmjs.org/get-intrinsic/-/get-intrinsic-1.2.3.tgz", - "integrity": "sha512-JIcZczvcMVE7AUOP+X72bh8HqHBRxFdz5PDHYtNG/lE3yk9b3KZBJlwFcTyPYjg3L4RLLmZJzvjxhaZVapxFrQ==", "dev": true, + "license": "MIT", "dependencies": { "es-errors": "^1.0.0", "function-bind": "^1.1.2", @@ -3324,18 +2720,16 @@ }, "node_modules/get-package-type": { "version": "0.1.0", - "resolved": "https://registry.npmjs.org/get-package-type/-/get-package-type-0.1.0.tgz", - "integrity": "sha512-pjzuKtY64GYfWizNAJ0fr9VqttZkNiK2iS430LtIHzjBEr6bX8Am2zm4sW4Ro5wjWW5cAlRL1qAMTcXbjNAO2Q==", "dev": true, + "license": "MIT", "engines": { "node": ">=8.0.0" } }, "node_modules/get-stream": { "version": "8.0.1", - "resolved": "https://registry.npmjs.org/get-stream/-/get-stream-8.0.1.tgz", - "integrity": "sha512-VaUJspBffn/LMCJVoMvSAdmscJyS1auj5Zulnn5UoYcY531UWmdwhRWkcGKnGU93m5HSXP9LP2usOryrBtQowA==", "dev": true, + "license": "MIT", "engines": { "node": ">=16" }, @@ -3345,9 +2739,8 @@ }, "node_modules/get-symbol-description": { "version": "1.0.0", - "resolved": "https://registry.npmjs.org/get-symbol-description/-/get-symbol-description-1.0.0.tgz", - "integrity": "sha512-2EmdH1YvIQiZpltCNgkuiUnyukzxM/R6NDJX31Ke3BG1Nq5b0S2PhX59UKi9vZpPDQVdqn+1IcaAwnzTT5vCjw==", "dev": true, + "license": "MIT", "dependencies": { "call-bind": "^1.0.2", "get-intrinsic": "^1.1.1" @@ -3361,9 +2754,8 @@ }, "node_modules/get-tsconfig": { "version": "4.7.2", - "resolved": "https://registry.npmjs.org/get-tsconfig/-/get-tsconfig-4.7.2.tgz", - "integrity": "sha512-wuMsz4leaj5hbGgg4IvDU0bqJagpftG5l5cXIAvo8uZrqn0NJqwtfupTN00VnkQJPcIRrxYrm1Ue24btpCha2A==", "dev": true, + "license": "MIT", "dependencies": { "resolve-pkg-maps": "^1.0.0" }, @@ -3373,9 +2765,8 @@ }, "node_modules/get-uri": { "version": "6.0.2", - "resolved": "https://registry.npmjs.org/get-uri/-/get-uri-6.0.2.tgz", - "integrity": "sha512-5KLucCJobh8vBY1K07EFV4+cPZH3mrV9YeAruUseCQKHB58SGjjT2l9/eA9LD082IiuMjSlFJEcdJ27TXvbZNw==", "dev": true, + "license": "MIT", "dependencies": { "basic-ftp": "^5.0.2", "data-uri-to-buffer": "^6.0.0", @@ -3388,18 +2779,16 @@ }, "node_modules/get-uri/node_modules/data-uri-to-buffer": { "version": "6.0.1", - "resolved": "https://registry.npmjs.org/data-uri-to-buffer/-/data-uri-to-buffer-6.0.1.tgz", - "integrity": "sha512-MZd3VlchQkp8rdend6vrx7MmVDJzSNTBvghvKjirLkD+WTChA3KUf0jkE68Q4UyctNqI11zZO9/x2Yx+ub5Cvg==", "dev": true, + "license": "MIT", "engines": { "node": ">= 14" } }, "node_modules/get-uri/node_modules/fs-extra": { "version": "8.1.0", - "resolved": "https://registry.npmjs.org/fs-extra/-/fs-extra-8.1.0.tgz", - "integrity": "sha512-yhlQgA6mnOJUKOsRUFsgJdQCvkKhcz8tlZG5HBQfReYZy46OwLcY+Zia0mtdHsOo9y/hP+CxMN0TU9QxoOtG4g==", "dev": true, + "license": "MIT", "dependencies": { "graceful-fs": "^4.2.0", "jsonfile": "^4.0.0", @@ -3411,27 +2800,24 @@ }, "node_modules/get-uri/node_modules/jsonfile": { "version": "4.0.0", - "resolved": "https://registry.npmjs.org/jsonfile/-/jsonfile-4.0.0.tgz", - "integrity": "sha512-m6F1R3z8jjlf2imQHS2Qez5sjKWQzbuuhuJ/FKYFRZvPE3PuHcSMVZzfsLhGVOkfd20obL5SWEBew5ShlquNxg==", "dev": true, + "license": "MIT", "optionalDependencies": { "graceful-fs": "^4.1.6" } }, "node_modules/get-uri/node_modules/universalify": { "version": "0.1.2", - "resolved": "https://registry.npmjs.org/universalify/-/universalify-0.1.2.tgz", - "integrity": "sha512-rBJeI5CXAlmy1pV+617WB9J63U6XcazHHF2f2dbJix4XzpUF0RS3Zbj0FGIOCAva5P/d/GBOYaACQ1w+0azUkg==", "dev": true, + "license": "MIT", "engines": { "node": ">= 4.0.0" } }, "node_modules/gh-pages": { "version": "6.1.1", - "resolved": "https://registry.npmjs.org/gh-pages/-/gh-pages-6.1.1.tgz", - "integrity": "sha512-upnohfjBwN5hBP9w2dPE7HO5JJTHzSGMV1JrLrHvNuqmjoYHg6TBrCcnEoorjG/e0ejbuvnwyKMdTyM40PEByw==", "dev": true, + "license": "MIT", "dependencies": { "async": "^3.2.4", "commander": "^11.0.0", @@ -3451,9 +2837,8 @@ }, "node_modules/git-up": { "version": "7.0.0", - "resolved": "https://registry.npmjs.org/git-up/-/git-up-7.0.0.tgz", - "integrity": "sha512-ONdIrbBCFusq1Oy0sC71F5azx8bVkvtZtMJAsv+a6lz5YAmbNnLD6HAB4gptHZVLPR8S2/kVN6Gab7lryq5+lQ==", "dev": true, + "license": "MIT", "dependencies": { "is-ssh": "^1.4.0", "parse-url": "^8.1.0" @@ -3461,18 +2846,16 @@ }, "node_modules/git-url-parse": { "version": "14.0.0", - "resolved": "https://registry.npmjs.org/git-url-parse/-/git-url-parse-14.0.0.tgz", - "integrity": "sha512-NnLweV+2A4nCvn4U/m2AoYu0pPKlsmhK9cknG7IMwsjFY1S2jxM+mAhsDxyxfCIGfGaD+dozsyX4b6vkYc83yQ==", "dev": true, + "license": "MIT", "dependencies": { "git-up": "^7.0.0" } }, "node_modules/glob": { "version": "7.2.3", - "resolved": "https://registry.npmjs.org/glob/-/glob-7.2.3.tgz", - "integrity": "sha512-nFR0zLpU2YCaRxwoCJvL6UvCH2JFyFVIvwTLsIf21AuHlMskA1hhTdk+LlYJtOlYt9v6dvszD2BGRqBL+iQK9Q==", "dev": true, + "license": "ISC", "dependencies": { "fs.realpath": "^1.0.0", "inflight": "^1.0.4", @@ -3490,9 +2873,8 @@ }, "node_modules/glob-parent": { "version": "5.1.2", - "resolved": "https://registry.npmjs.org/glob-parent/-/glob-parent-5.1.2.tgz", - "integrity": "sha512-AOIgSQCepiJYwP3ARnGx+5VnTu2HBYdzbGP45eLw1vr3zB3vZLeyed1sC9hnbcOc9/SrMyM5RPQrkGz4aS9Zow==", "dev": true, + "license": "ISC", "dependencies": { "is-glob": "^4.0.1" }, @@ -3502,9 +2884,8 @@ }, "node_modules/global-dirs": { "version": "3.0.1", - "resolved": "https://registry.npmjs.org/global-dirs/-/global-dirs-3.0.1.tgz", - "integrity": "sha512-NBcGGFbBA9s1VzD41QXDG+3++t9Mn5t1FpLdhESY6oKY4gYTFpX4wO3sqGUa0Srjtbfj3szX0RnemmrVRUdULA==", "dev": true, + "license": "MIT", "dependencies": { "ini": "2.0.0" }, @@ -3517,18 +2898,16 @@ }, "node_modules/globals": { "version": "11.12.0", - "resolved": "https://registry.npmjs.org/globals/-/globals-11.12.0.tgz", - "integrity": "sha512-WOBp/EEGUiIsJSp7wcv/y6MO+lV9UoncWqxuFfm8eBwzWNgyfBd6Gz+IeKQ9jCmyhoH99g15M3T+QaVHFjizVA==", "dev": true, + "license": "MIT", "engines": { "node": ">=4" } }, "node_modules/globalthis": { "version": "1.0.3", - "resolved": "https://registry.npmjs.org/globalthis/-/globalthis-1.0.3.tgz", - "integrity": "sha512-sFdI5LyBiNTHjRd7cGPWapiHWMOXKyuBNX/cWJ3NfzrZQVa8GI/8cofCl74AOVqq9W5kNmguTIzJ/1s2gyI9wA==", "dev": true, + "license": "MIT", "dependencies": { "define-properties": "^1.1.3" }, @@ -3541,9 +2920,8 @@ }, "node_modules/globby": { "version": "6.1.0", - "resolved": "https://registry.npmjs.org/globby/-/globby-6.1.0.tgz", - "integrity": "sha512-KVbFv2TQtbzCoxAnfD6JcHZTYCzyliEaaeM/gH8qQdkKr5s0OP9scEgvdcngyk7AVdY6YVW/TJHd+lQ/Df3Daw==", "dev": true, + "license": "MIT", "dependencies": { "array-union": "^1.0.1", "glob": "^7.0.3", @@ -3557,9 +2935,8 @@ }, "node_modules/gopd": { "version": "1.0.1", - "resolved": "https://registry.npmjs.org/gopd/-/gopd-1.0.1.tgz", - "integrity": "sha512-d65bNlIadxvpb/A2abVdlqKqV563juRnZ1Wtk6s1sIR8uNsXR70xqIzVqxVf1eTqDunwT2MkczEeaezCKTZhwA==", "dev": true, + "license": "MIT", "dependencies": { "get-intrinsic": "^1.1.3" }, @@ -3569,9 +2946,8 @@ }, "node_modules/got": { "version": "13.0.0", - "resolved": "https://registry.npmjs.org/got/-/got-13.0.0.tgz", - "integrity": "sha512-XfBk1CxOOScDcMr9O1yKkNaQyy865NbYs+F7dr4H0LZMVgCj2Le59k6PqbNHoL5ToeaEQUYh6c6yMfVcc6SJxA==", "dev": true, + "license": "MIT", "dependencies": { "@sindresorhus/is": "^5.2.0", "@szmarczak/http-timer": "^5.0.1", @@ -3594,9 +2970,8 @@ }, "node_modules/got/node_modules/get-stream": { "version": "6.0.1", - "resolved": "https://registry.npmjs.org/get-stream/-/get-stream-6.0.1.tgz", - "integrity": "sha512-ts6Wi+2j3jQjqi70w5AlN8DFnkSwC+MqmxEzdEALB2qXZYV3X/b1CTfgPLGJNMeAWxdPfU8FO1ms3NUfaHCPYg==", "dev": true, + "license": "MIT", "engines": { "node": ">=10" }, @@ -3606,33 +2981,29 @@ }, "node_modules/graceful-fs": { "version": "4.2.11", - "resolved": "https://registry.npmjs.org/graceful-fs/-/graceful-fs-4.2.11.tgz", - "integrity": "sha512-RbJ5/jmFcNNCcDV5o9eTnBLJ/HszWV0P73bc+Ff4nS/rJj+YaS6IGyiOL0VoBYX+l1Wrl3k63h/KrH+nhJ0XvQ==", - "dev": true + "dev": true, + "license": "ISC" }, "node_modules/has-bigints": { "version": "1.0.2", - "resolved": "https://registry.npmjs.org/has-bigints/-/has-bigints-1.0.2.tgz", - "integrity": "sha512-tSvCKtBr9lkF0Ex0aQiP9N+OpV4zi2r/Nee5VkRDbaqv35RLYMzbwQfFSZZH0kR+Rd6302UJZ2p/bJCEoR3VoQ==", "dev": true, + "license": "MIT", "funding": { "url": "https://github.com/sponsors/ljharb" } }, "node_modules/has-flag": { "version": "4.0.0", - "resolved": "https://registry.npmjs.org/has-flag/-/has-flag-4.0.0.tgz", - "integrity": "sha512-EykJT/Q1KjTWctppgIAgfSO0tKVuZUjhgMr17kqTumMl6Afv3EISleU7qZUzoXDFTAHTDC4NOoG/ZxU3EvlMPQ==", "dev": true, + "license": "MIT", "engines": { "node": ">=8" } }, "node_modules/has-property-descriptors": { "version": "1.0.1", - "resolved": "https://registry.npmjs.org/has-property-descriptors/-/has-property-descriptors-1.0.1.tgz", - "integrity": "sha512-VsX8eaIewvas0xnvinAe9bw4WfIeODpGYikiWYLH+dma0Jw6KHYqWiWfhQlgOVK8D6PvjubK5Uc4P0iIhIcNVg==", "dev": true, + "license": "MIT", "dependencies": { "get-intrinsic": "^1.2.2" }, @@ -3642,9 +3013,8 @@ }, "node_modules/has-proto": { "version": "1.0.1", - "resolved": "https://registry.npmjs.org/has-proto/-/has-proto-1.0.1.tgz", - "integrity": "sha512-7qE+iP+O+bgF9clE5+UoBFzE65mlBiVj3tKCrlNQ0Ogwm0BjpT/gK4SlLYDMybDh5I3TCTKnPPa0oMG7JDYrhg==", "dev": true, + "license": "MIT", "engines": { "node": ">= 0.4" }, @@ -3654,9 +3024,8 @@ }, "node_modules/has-symbols": { "version": "1.0.3", - "resolved": "https://registry.npmjs.org/has-symbols/-/has-symbols-1.0.3.tgz", - "integrity": "sha512-l3LCuF6MgDNwTDKkdYGEihYjt5pRPbEg46rtlmnSPlUbgmB8LOIrKJbYYFBSbnPaJexMKtiPO8hmeRjRz2Td+A==", "dev": true, + "license": "MIT", "engines": { "node": ">= 0.4" }, @@ -3666,9 +3035,8 @@ }, "node_modules/has-tostringtag": { "version": "1.0.2", - "resolved": "https://registry.npmjs.org/has-tostringtag/-/has-tostringtag-1.0.2.tgz", - "integrity": "sha512-NqADB8VjPFLM2V0VvHUewwwsw0ZWBaIdgo+ieHtK3hasLz4qeCRjYcqfB6AQrBggRKppKF8L52/VqdVsO47Dlw==", "dev": true, + "license": "MIT", "dependencies": { "has-symbols": "^1.0.3" }, @@ -3681,9 +3049,8 @@ }, "node_modules/hasha": { "version": "5.2.2", - "resolved": "https://registry.npmjs.org/hasha/-/hasha-5.2.2.tgz", - "integrity": "sha512-Hrp5vIK/xr5SkeN2onO32H0MgNZ0f17HRNH39WfL0SYUNOTZ5Lz1TJ8Pajo/87dYGEFlLMm7mIc/k/s6Bvz9HQ==", "dev": true, + "license": "MIT", "dependencies": { "is-stream": "^2.0.0", "type-fest": "^0.8.0" @@ -3697,9 +3064,8 @@ }, "node_modules/hasown": { "version": "2.0.0", - "resolved": "https://registry.npmjs.org/hasown/-/hasown-2.0.0.tgz", - "integrity": "sha512-vUptKVTpIJhcczKBbgnS+RtcuYMB8+oNzPK2/Hp3hanz8JmpATdmmgLgSaadVREkDm+e2giHwY3ZRkyjSIDDFA==", "dev": true, + "license": "MIT", "dependencies": { "function-bind": "^1.1.2" }, @@ -3709,30 +3075,26 @@ }, "node_modules/he": { "version": "1.2.0", - "resolved": "https://registry.npmjs.org/he/-/he-1.2.0.tgz", - "integrity": "sha512-F/1DnUGPopORZi0ni+CvrCgHQ5FyEAHRLSApuYWMmrbSwoN2Mn/7k+Gl38gJnR7yyDZk6WLXwiGod1JOWNDKGw==", "dev": true, + "license": "MIT", "bin": { "he": "bin/he" } }, "node_modules/html-escaper": { "version": "2.0.2", - "resolved": "https://registry.npmjs.org/html-escaper/-/html-escaper-2.0.2.tgz", - "integrity": "sha512-H2iMtd0I4Mt5eYiapRdIDjp+XzelXQ0tFE4JS7YFwFevXXMmOp9myNrUvCg0D6ws8iqkRPBfKHgbwig1SmlLfg==", - "dev": true + "dev": true, + "license": "MIT" }, "node_modules/http-cache-semantics": { "version": "4.1.1", - "resolved": "https://registry.npmjs.org/http-cache-semantics/-/http-cache-semantics-4.1.1.tgz", - "integrity": "sha512-er295DKPVsV82j5kw1Gjt+ADA/XYHsajl82cGNQG2eyoPkvgUhX+nDIyelzhIWbbsXP39EHcI6l5tYs2FYqYXQ==", - "dev": true + "dev": true, + "license": "BSD-2-Clause" }, "node_modules/http-proxy-agent": { "version": "7.0.0", - "resolved": "https://registry.npmjs.org/http-proxy-agent/-/http-proxy-agent-7.0.0.tgz", - "integrity": "sha512-+ZT+iBxVUQ1asugqnD6oWoRiS25AkjNfG085dKJGtGxkdwLQrMKU5wJr2bOOFAXzKcTuqq+7fZlTMgG3SRfIYQ==", "dev": true, + "license": "MIT", "dependencies": { "agent-base": "^7.1.0", "debug": "^4.3.4" @@ -3743,9 +3105,8 @@ }, "node_modules/http2-wrapper": { "version": "2.2.1", - "resolved": "https://registry.npmjs.org/http2-wrapper/-/http2-wrapper-2.2.1.tgz", - "integrity": "sha512-V5nVw1PAOgfI3Lmeaj2Exmeg7fenjhRUgz1lPSezy1CuhPYbgQtbQj4jZfEAEMlaL+vupsvhjqCyjzob0yxsmQ==", "dev": true, + "license": "MIT", "dependencies": { "quick-lru": "^5.1.1", "resolve-alpn": "^1.2.0" @@ -3756,9 +3117,8 @@ }, "node_modules/https-proxy-agent": { "version": "7.0.2", - "resolved": "https://registry.npmjs.org/https-proxy-agent/-/https-proxy-agent-7.0.2.tgz", - "integrity": "sha512-NmLNjm6ucYwtcUmL7JQC1ZQ57LmHP4lT15FQ8D61nak1rO6DH+fz5qNK2Ap5UN4ZapYICE3/0KodcLYSPsPbaA==", "dev": true, + "license": "MIT", "dependencies": { "agent-base": "^7.0.2", "debug": "4" @@ -3769,18 +3129,16 @@ }, "node_modules/human-signals": { "version": "5.0.0", - "resolved": "https://registry.npmjs.org/human-signals/-/human-signals-5.0.0.tgz", - "integrity": "sha512-AXcZb6vzzrFAUE61HnN4mpLqd/cSIwNQjtNWR0euPm6y0iqx3G4gOXaIDdtdDwZmhwe82LA6+zinmW4UBWVePQ==", "dev": true, + "license": "Apache-2.0", "engines": { "node": ">=16.17.0" } }, "node_modules/iconv-lite": { "version": "0.4.24", - "resolved": "https://registry.npmjs.org/iconv-lite/-/iconv-lite-0.4.24.tgz", - "integrity": "sha512-v3MXnZAcvnywkTUEZomIActle7RXXeedOR31wwl7VlyoXO4Qi9arvSenNQWne1TcRwhCL1HwLI21bEqdpj8/rA==", "dev": true, + "license": "MIT", "dependencies": { "safer-buffer": ">= 2.1.2 < 3" }, @@ -3790,8 +3148,6 @@ }, "node_modules/ieee754": { "version": "1.2.1", - "resolved": "https://registry.npmjs.org/ieee754/-/ieee754-1.2.1.tgz", - "integrity": "sha512-dcyqhDvX1C46lXZcVqCpK+FtMRQVdIMN6/Df5js2zouUsqG7I6sFxitIC+7KYK29KdXOLHdu9zL4sFnoVQnqaA==", "dev": true, "funding": [ { @@ -3806,22 +3162,21 @@ "type": "consulting", "url": "https://feross.org/support" } - ] + ], + "license": "BSD-3-Clause" }, "node_modules/ignore": { "version": "5.3.1", - "resolved": "https://registry.npmjs.org/ignore/-/ignore-5.3.1.tgz", - "integrity": "sha512-5Fytz/IraMjqpwfd34ke28PTVMjZjJG2MPn5t7OE4eUCUNf8BAa7b5WUS9/Qvr6mwOQS7Mk6vdsMno5he+T8Xw==", "dev": true, + "license": "MIT", "engines": { "node": ">= 4" } }, "node_modules/import-fresh": { "version": "3.3.0", - "resolved": "https://registry.npmjs.org/import-fresh/-/import-fresh-3.3.0.tgz", - "integrity": "sha512-veYYhQa+D1QBKznvhUHxb8faxlrwUnxseDAbAp457E0wLNio2bOSKnjYDhMj+YiAq61xrMGhQk9iXVk5FzgQMw==", "dev": true, + "license": "MIT", "dependencies": { "parent-module": "^1.0.0", "resolve-from": "^4.0.0" @@ -3835,45 +3190,40 @@ }, "node_modules/import-fresh/node_modules/resolve-from": { "version": "4.0.0", - "resolved": "https://registry.npmjs.org/resolve-from/-/resolve-from-4.0.0.tgz", - "integrity": "sha512-pb/MYmXstAkysRFx8piNI1tGFNQIFA3vkE3Gq4EuA1dF6gHp/+vgZqsCGJapvy8N3Q+4o7FwvquPJcnZ7RYy4g==", "dev": true, + "license": "MIT", "engines": { "node": ">=4" } }, "node_modules/import-lazy": { "version": "4.0.0", - "resolved": "https://registry.npmjs.org/import-lazy/-/import-lazy-4.0.0.tgz", - "integrity": "sha512-rKtvo6a868b5Hu3heneU+L4yEQ4jYKLtjpnPeUdK7h0yzXGmyBTypknlkCvHFBqfX9YlorEiMM6Dnq/5atfHkw==", "dev": true, + "license": "MIT", "engines": { "node": ">=8" } }, "node_modules/imurmurhash": { "version": "0.1.4", - "resolved": "https://registry.npmjs.org/imurmurhash/-/imurmurhash-0.1.4.tgz", - "integrity": "sha512-JmXMZ6wuvDmLiHEml9ykzqO6lwFbof0GG4IkcGaENdCRDDmMVnny7s5HsIgHCbaq0w2MyPhDqkhTUgS2LU2PHA==", "dev": true, + "license": "MIT", "engines": { "node": ">=0.8.19" } }, "node_modules/indent-string": { "version": "4.0.0", - "resolved": "https://registry.npmjs.org/indent-string/-/indent-string-4.0.0.tgz", - "integrity": "sha512-EdDDZu4A2OyIK7Lr/2zG+w5jmbuk1DVBnEwREQvBzspBJkCEbRa8GxU1lghYcaGJCnRWibjDXlq779X1/y5xwg==", "dev": true, + "license": "MIT", "engines": { "node": ">=8" } }, "node_modules/inflight": { "version": "1.0.6", - "resolved": "https://registry.npmjs.org/inflight/-/inflight-1.0.6.tgz", - "integrity": "sha512-k92I/b08q4wvFscXCLvqfsHCrjrF7yiXsQuIVvVE7N82W3+aqpzuUdBbfhWcy/FZR3/4IgflMgKLOsvPDrGCJA==", "dev": true, + "license": "ISC", "dependencies": { "once": "^1.3.0", "wrappy": "1" @@ -3881,24 +3231,21 @@ }, "node_modules/inherits": { "version": "2.0.4", - "resolved": "https://registry.npmjs.org/inherits/-/inherits-2.0.4.tgz", - "integrity": "sha512-k/vGaX4/Yla3WzyMCvTQOXYeIHvqOKtnqBduzTHpzpQZzAskKMhZ2K+EnBiSM9zGSoIFeMpXKxa4dYeZIQqewQ==", - "dev": true + "dev": true, + "license": "ISC" }, "node_modules/ini": { "version": "2.0.0", - "resolved": "https://registry.npmjs.org/ini/-/ini-2.0.0.tgz", - "integrity": "sha512-7PnF4oN3CvZF23ADhA5wRaYEQpJ8qygSkbtTXWBeXWXmEVRXK+1ITciHWwHhsjv1TmW0MgacIv6hEi5pX5NQdA==", "dev": true, + "license": "ISC", "engines": { "node": ">=10" } }, "node_modules/inquirer": { "version": "9.2.12", - "resolved": "https://registry.npmjs.org/inquirer/-/inquirer-9.2.12.tgz", - "integrity": "sha512-mg3Fh9g2zfuVWJn6lhST0O7x4n03k7G8Tx5nvikJkbq8/CK47WDVm+UznF0G6s5Zi0KcyUisr6DU8T67N5U+1Q==", "dev": true, + "license": "MIT", "dependencies": { "@ljharb/through": "^2.3.11", "ansi-escapes": "^4.3.2", @@ -3922,9 +3269,8 @@ }, "node_modules/inquirer/node_modules/chalk": { "version": "5.3.0", - "resolved": "https://registry.npmjs.org/chalk/-/chalk-5.3.0.tgz", - "integrity": "sha512-dLitG79d+GV1Nb/VYcCDFivJeK1hiukt9QjRNVOsUtTy1rR1YJsmpGGTZ3qJos+uw7WmWF4wUwBd9jxjocFC2w==", "dev": true, + "license": "MIT", "engines": { "node": "^12.17.0 || ^14.13 || >=16.0.0" }, @@ -3934,18 +3280,16 @@ }, "node_modules/inquirer/node_modules/is-interactive": { "version": "1.0.0", - "resolved": "https://registry.npmjs.org/is-interactive/-/is-interactive-1.0.0.tgz", - "integrity": "sha512-2HvIEKRoqS62guEC+qBjpvRubdX910WCMuJTZ+I9yvqKU2/12eSL549HMwtabb4oupdj2sMP50k+XJfB/8JE6w==", "dev": true, + "license": "MIT", "engines": { "node": ">=8" } }, "node_modules/inquirer/node_modules/ora": { "version": "5.4.1", - "resolved": "https://registry.npmjs.org/ora/-/ora-5.4.1.tgz", - "integrity": "sha512-5b6Y85tPxZZ7QytO+BQzysW31HJku27cRIlkbAXaNx+BdcVi+LlRFmVXzeF6a7JCwJpyw5c4b+YSVImQIrBpuQ==", "dev": true, + "license": "MIT", "dependencies": { "bl": "^4.1.0", "chalk": "^4.1.0", @@ -3966,9 +3310,8 @@ }, "node_modules/inquirer/node_modules/ora/node_modules/chalk": { "version": "4.1.2", - "resolved": "https://registry.npmjs.org/chalk/-/chalk-4.1.2.tgz", - "integrity": "sha512-oKnbhFyRIXpUuez8iBMmyEa4nbj4IOQyuhc/wy9kY7/WVPcwIO9VA668Pu8RkO7+0G76SLROeyw9CpQ061i4mA==", "dev": true, + "license": "MIT", "dependencies": { "ansi-styles": "^4.1.0", "supports-color": "^7.1.0" @@ -3982,9 +3325,8 @@ }, "node_modules/inquirer/node_modules/supports-color": { "version": "7.2.0", - "resolved": "https://registry.npmjs.org/supports-color/-/supports-color-7.2.0.tgz", - "integrity": "sha512-qpCAvRl9stuOHveKsn7HncJRvv501qIacKzQlO/+Lwxc9+0q2wLyv4Dfvt80/DPn2pqOBsJdDiogXGR9+OvwRw==", "dev": true, + "license": "MIT", "dependencies": { "has-flag": "^4.0.0" }, @@ -3994,9 +3336,8 @@ }, "node_modules/internal-slot": { "version": "1.0.6", - "resolved": "https://registry.npmjs.org/internal-slot/-/internal-slot-1.0.6.tgz", - "integrity": "sha512-Xj6dv+PsbtwyPpEflsejS+oIZxmMlV44zAhG479uYu89MsjcYOhCFnNyKrkJrihbsiasQyY0afoCl/9BLR65bg==", "dev": true, + "license": "MIT", "dependencies": { "get-intrinsic": "^1.2.2", "hasown": "^2.0.0", @@ -4008,24 +3349,21 @@ }, "node_modules/interpret": { "version": "1.4.0", - "resolved": "https://registry.npmjs.org/interpret/-/interpret-1.4.0.tgz", - "integrity": "sha512-agE4QfB2Lkp9uICn7BAqoscw4SZP9kTE2hxiFI3jBPmXJfdqiahTbUuKGsMoN2GtqL9AxhYioAcVvgsb1HvRbA==", "dev": true, + "license": "MIT", "engines": { "node": ">= 0.10" } }, "node_modules/ip": { "version": "1.1.8", - "resolved": "https://registry.npmjs.org/ip/-/ip-1.1.8.tgz", - "integrity": "sha512-PuExPYUiu6qMBQb4l06ecm6T6ujzhmh+MeJcW9wa89PoAz5pvd4zPgN5WJV104mb6S2T1AwNIAaB70JNrLQWhg==", - "dev": true + "dev": true, + "license": "MIT" }, "node_modules/is-arguments": { "version": "1.1.1", - "resolved": "https://registry.npmjs.org/is-arguments/-/is-arguments-1.1.1.tgz", - "integrity": "sha512-8Q7EARjzEnKpt/PCD7e1cgUS0a6X8u5tdSiMqXhojOdoV9TsMsiO+9VLC5vAmO8N7/GmXn7yjR8qnA6bVAEzfA==", "dev": true, + "license": "MIT", "dependencies": { "call-bind": "^1.0.2", "has-tostringtag": "^1.0.0" @@ -4039,9 +3377,8 @@ }, "node_modules/is-array-buffer": { "version": "3.0.4", - "resolved": "https://registry.npmjs.org/is-array-buffer/-/is-array-buffer-3.0.4.tgz", - "integrity": "sha512-wcjaerHw0ydZwfhiKbXJWLDY8A7yV7KhjQOpb83hGgGfId/aQa4TOvwyzn2PuswW2gPCYEL/nEAiSVpdOj1lXw==", "dev": true, + "license": "MIT", "dependencies": { "call-bind": "^1.0.2", "get-intrinsic": "^1.2.1" @@ -4055,15 +3392,13 @@ }, "node_modules/is-arrayish": { "version": "0.2.1", - "resolved": "https://registry.npmjs.org/is-arrayish/-/is-arrayish-0.2.1.tgz", - "integrity": "sha512-zz06S8t0ozoDXMG+ube26zeCTNXcKIPJZJi8hBrF4idCLms4CG9QtK7qBl1boi5ODzFpjswb5JPmHCbMpjaYzg==", - "dev": true + "dev": true, + "license": "MIT" }, "node_modules/is-bigint": { "version": "1.0.4", - "resolved": "https://registry.npmjs.org/is-bigint/-/is-bigint-1.0.4.tgz", - "integrity": "sha512-zB9CruMamjym81i2JZ3UMn54PKGsQzsJeo6xvN3HJJ4CAsQNB6iRutp2To77OfCNuoxspsIhzaPoO1zyCEhFOg==", "dev": true, + "license": "MIT", "dependencies": { "has-bigints": "^1.0.1" }, @@ -4073,9 +3408,8 @@ }, "node_modules/is-binary-path": { "version": "2.1.0", - "resolved": "https://registry.npmjs.org/is-binary-path/-/is-binary-path-2.1.0.tgz", - "integrity": "sha512-ZMERYes6pDydyuGidse7OsHxtbI7WVeUEozgR/g7rd0xUimYNlvZRE/K2MgZTjWy725IfelLeVcEM97mmtRGXw==", "dev": true, + "license": "MIT", "dependencies": { "binary-extensions": "^2.0.0" }, @@ -4085,9 +3419,8 @@ }, "node_modules/is-boolean-object": { "version": "1.1.2", - "resolved": "https://registry.npmjs.org/is-boolean-object/-/is-boolean-object-1.1.2.tgz", - "integrity": "sha512-gDYaKHJmnj4aWxyj6YHyXVpdQawtVLHU5cb+eztPGczf6cjuTdwve5ZIEfgXqH4e57An1D1AKf8CZ3kYrQRqYA==", "dev": true, + "license": "MIT", "dependencies": { "call-bind": "^1.0.2", "has-tostringtag": "^1.0.0" @@ -4101,9 +3434,8 @@ }, "node_modules/is-callable": { "version": "1.2.7", - "resolved": "https://registry.npmjs.org/is-callable/-/is-callable-1.2.7.tgz", - "integrity": "sha512-1BC0BVFhS/p0qtw6enp8e+8OD0UrK0oFLztSjNzhcKA3WDuJxxAPXzPuPtKkjEY9UUoEWlX/8fgKeu2S8i9JTA==", "dev": true, + "license": "MIT", "engines": { "node": ">= 0.4" }, @@ -4113,9 +3445,8 @@ }, "node_modules/is-ci": { "version": "3.0.1", - "resolved": "https://registry.npmjs.org/is-ci/-/is-ci-3.0.1.tgz", - "integrity": "sha512-ZYvCgrefwqoQ6yTyYUbQu64HsITZ3NfKX1lzaEYdkTDcfKzzCI/wthRRYKkdjHKFVgNiXKAKm65Zo1pk2as/QQ==", "dev": true, + "license": "MIT", "dependencies": { "ci-info": "^3.2.0" }, @@ -4125,9 +3456,8 @@ }, "node_modules/is-core-module": { "version": "2.13.1", - "resolved": "https://registry.npmjs.org/is-core-module/-/is-core-module-2.13.1.tgz", - "integrity": "sha512-hHrIjvZsftOsvKSn2TRYl63zvxsgE0K+0mYMoH6gD4omR5IWB2KynivBQczo3+wF1cCkjzvptnI9Q0sPU66ilw==", "dev": true, + "license": "MIT", "dependencies": { "hasown": "^2.0.0" }, @@ -4137,9 +3467,8 @@ }, "node_modules/is-date-object": { "version": "1.0.5", - "resolved": "https://registry.npmjs.org/is-date-object/-/is-date-object-1.0.5.tgz", - "integrity": "sha512-9YQaSxsAiSwcvS33MBk3wTCVnWK+HhF8VZR2jRxehM16QcVOdHqPn4VPHmRK4lSr38n9JriurInLcP90xsYNfQ==", "dev": true, + "license": "MIT", "dependencies": { "has-tostringtag": "^1.0.0" }, @@ -4152,9 +3481,8 @@ }, "node_modules/is-docker": { "version": "3.0.0", - "resolved": "https://registry.npmjs.org/is-docker/-/is-docker-3.0.0.tgz", - "integrity": "sha512-eljcgEDlEns/7AXFosB5K/2nCM4P7FQPkGc/DWLy5rmFEWvZayGrik1d9/QIY5nJ4f9YsVvBkA6kJpHn9rISdQ==", "dev": true, + "license": "MIT", "bin": { "is-docker": "cli.js" }, @@ -4167,27 +3495,24 @@ }, "node_modules/is-extglob": { "version": "2.1.1", - "resolved": "https://registry.npmjs.org/is-extglob/-/is-extglob-2.1.1.tgz", - "integrity": "sha512-SbKbANkN603Vi4jEZv49LeVJMn4yGwsbzZworEoyEiutsN3nJYdbO36zfhGJ6QEDpOZIFkDtnq5JRxmvl3jsoQ==", "dev": true, + "license": "MIT", "engines": { "node": ">=0.10.0" } }, "node_modules/is-fullwidth-code-point": { "version": "3.0.0", - "resolved": "https://registry.npmjs.org/is-fullwidth-code-point/-/is-fullwidth-code-point-3.0.0.tgz", - "integrity": "sha512-zymm5+u+sCsSWyD9qNaejV3DFvhCKclKdizYaJUuHA83RLjb7nSuGnddCHGv0hk+KY7BMAlsWeK4Ueg6EV6XQg==", "dev": true, + "license": "MIT", "engines": { "node": ">=8" } }, "node_modules/is-glob": { "version": "4.0.3", - "resolved": "https://registry.npmjs.org/is-glob/-/is-glob-4.0.3.tgz", - "integrity": "sha512-xelSayHH36ZgE7ZWhli7pW34hNbNl8Ojv5KVmkJD4hBdD3th8Tfk9vYasLM+mXWOZhFkgZfxhLSnrwRr4elSSg==", "dev": true, + "license": "MIT", "dependencies": { "is-extglob": "^2.1.1" }, @@ -4197,9 +3522,8 @@ }, "node_modules/is-in-ci": { "version": "0.1.0", - "resolved": "https://registry.npmjs.org/is-in-ci/-/is-in-ci-0.1.0.tgz", - "integrity": "sha512-d9PXLEY0v1iJ64xLiQMJ51J128EYHAaOR4yZqQi8aHGfw6KgifM3/Viw1oZZ1GCVmb3gBuyhLyHj0HgR2DhSXQ==", "dev": true, + "license": "MIT", "bin": { "is-in-ci": "cli.js" }, @@ -4212,9 +3536,8 @@ }, "node_modules/is-inside-container": { "version": "1.0.0", - "resolved": "https://registry.npmjs.org/is-inside-container/-/is-inside-container-1.0.0.tgz", - "integrity": "sha512-KIYLCCJghfHZxqjYBE7rEy0OBuTd5xCHS7tHVgvCLkx7StIoaxwNW3hCALgEUjFfeRk+MG/Qxmp/vtETEF3tRA==", "dev": true, + "license": "MIT", "dependencies": { "is-docker": "^3.0.0" }, @@ -4230,9 +3553,8 @@ }, "node_modules/is-installed-globally": { "version": "0.4.0", - "resolved": "https://registry.npmjs.org/is-installed-globally/-/is-installed-globally-0.4.0.tgz", - "integrity": "sha512-iwGqO3J21aaSkC7jWnHP/difazwS7SFeIqxv6wEtLU8Y5KlzFTjyqcSIT0d8s4+dDhKytsk9PJZ2BkS5eZwQRQ==", "dev": true, + "license": "MIT", "dependencies": { "global-dirs": "^3.0.0", "is-path-inside": "^3.0.2" @@ -4246,9 +3568,8 @@ }, "node_modules/is-interactive": { "version": "2.0.0", - "resolved": "https://registry.npmjs.org/is-interactive/-/is-interactive-2.0.0.tgz", - "integrity": "sha512-qP1vozQRI+BMOPcjFzrjXuQvdak2pHNUMZoeG2eRbiSqyvbEf/wQtEOTOX1guk6E3t36RkaqiSt8A/6YElNxLQ==", "dev": true, + "license": "MIT", "engines": { "node": ">=12" }, @@ -4258,18 +3579,16 @@ }, "node_modules/is-map": { "version": "2.0.2", - "resolved": "https://registry.npmjs.org/is-map/-/is-map-2.0.2.tgz", - "integrity": "sha512-cOZFQQozTha1f4MxLFzlgKYPTyj26picdZTx82hbc/Xf4K/tZOOXSCkMvU4pKioRXGDLJRn0GM7Upe7kR721yg==", "dev": true, + "license": "MIT", "funding": { "url": "https://github.com/sponsors/ljharb" } }, "node_modules/is-negative-zero": { "version": "2.0.2", - "resolved": "https://registry.npmjs.org/is-negative-zero/-/is-negative-zero-2.0.2.tgz", - "integrity": "sha512-dqJvarLawXsFbNDeJW7zAz8ItJ9cd28YufuuFzh0G8pNHjJMnY08Dv7sYX2uF5UpQOwieAeOExEYAWWfu7ZZUA==", "dev": true, + "license": "MIT", "engines": { "node": ">= 0.4" }, @@ -4279,9 +3598,8 @@ }, "node_modules/is-npm": { "version": "6.0.0", - "resolved": "https://registry.npmjs.org/is-npm/-/is-npm-6.0.0.tgz", - "integrity": "sha512-JEjxbSmtPSt1c8XTkVrlujcXdKV1/tvuQ7GwKcAlyiVLeYFQ2VHat8xfrDJsIkhCdF/tZ7CiIR3sy141c6+gPQ==", "dev": true, + "license": "MIT", "engines": { "node": "^12.20.0 || ^14.13.1 || >=16.0.0" }, @@ -4291,18 +3609,16 @@ }, "node_modules/is-number": { "version": "7.0.0", - "resolved": "https://registry.npmjs.org/is-number/-/is-number-7.0.0.tgz", - "integrity": "sha512-41Cifkg6e8TylSpdtTpeLVMqvSBEVzTttHvERD741+pnZ8ANv0004MRL43QKPDlK9cGvNp6NZWZUBlbGXYxxng==", "dev": true, + "license": "MIT", "engines": { "node": ">=0.12.0" } }, "node_modules/is-number-object": { "version": "1.0.7", - "resolved": "https://registry.npmjs.org/is-number-object/-/is-number-object-1.0.7.tgz", - "integrity": "sha512-k1U0IRzLMo7ZlYIfzRu23Oh6MiIFasgpb9X76eqfFZAqwH44UI4KTBvBYIZ1dSL9ZzChTB9ShHfLkR4pdW5krQ==", "dev": true, + "license": "MIT", "dependencies": { "has-tostringtag": "^1.0.0" }, @@ -4315,36 +3631,32 @@ }, "node_modules/is-obj": { "version": "2.0.0", - "resolved": "https://registry.npmjs.org/is-obj/-/is-obj-2.0.0.tgz", - "integrity": "sha512-drqDG3cbczxxEJRoOXcOjtdp1J/lyp1mNn0xaznRs8+muBhgQcrnbspox5X5fOw0HnMnbfDzvnEMEtqDEJEo8w==", "dev": true, + "license": "MIT", "engines": { "node": ">=8" } }, "node_modules/is-path-inside": { "version": "3.0.3", - "resolved": "https://registry.npmjs.org/is-path-inside/-/is-path-inside-3.0.3.tgz", - "integrity": "sha512-Fd4gABb+ycGAmKou8eMftCupSir5lRxqf4aD/vd0cD2qc4HL07OjCeuHMr8Ro4CoMaeCKDB0/ECBOVWjTwUvPQ==", "dev": true, + "license": "MIT", "engines": { "node": ">=8" } }, "node_modules/is-plain-obj": { "version": "2.1.0", - "resolved": "https://registry.npmjs.org/is-plain-obj/-/is-plain-obj-2.1.0.tgz", - "integrity": "sha512-YWnfyRwxL/+SsrWYfOpUtz5b3YD+nyfkHvjbcanzk8zgyO4ASD67uVMRt8k5bM4lLMDnXfriRhOpemw+NfT1eA==", "dev": true, + "license": "MIT", "engines": { "node": ">=8" } }, "node_modules/is-regex": { "version": "1.1.4", - "resolved": "https://registry.npmjs.org/is-regex/-/is-regex-1.1.4.tgz", - "integrity": "sha512-kvRdxDsxZjhzUX07ZnLydzS1TU/TJlTUHHY4YLL87e37oUA49DfkLqgy+VjFocowy29cKvcSiu+kIv728jTTVg==", "dev": true, + "license": "MIT", "dependencies": { "call-bind": "^1.0.2", "has-tostringtag": "^1.0.0" @@ -4358,18 +3670,16 @@ }, "node_modules/is-set": { "version": "2.0.2", - "resolved": "https://registry.npmjs.org/is-set/-/is-set-2.0.2.tgz", - "integrity": "sha512-+2cnTEZeY5z/iXGbLhPrOAaK/Mau5k5eXq9j14CpRTftq0pAJu2MwVRSZhyZWBzx3o6X795Lz6Bpb6R0GKf37g==", "dev": true, + "license": "MIT", "funding": { "url": "https://github.com/sponsors/ljharb" } }, "node_modules/is-shared-array-buffer": { "version": "1.0.2", - "resolved": "https://registry.npmjs.org/is-shared-array-buffer/-/is-shared-array-buffer-1.0.2.tgz", - "integrity": "sha512-sqN2UDu1/0y6uvXyStCOzyhAjCSlHceFoMKJW8W9EU9cvic/QdsZ0kEU93HEy3IUEFZIiH/3w+AH/UQbPHNdhA==", "dev": true, + "license": "MIT", "dependencies": { "call-bind": "^1.0.2" }, @@ -4379,18 +3689,16 @@ }, "node_modules/is-ssh": { "version": "1.4.0", - "resolved": "https://registry.npmjs.org/is-ssh/-/is-ssh-1.4.0.tgz", - "integrity": "sha512-x7+VxdxOdlV3CYpjvRLBv5Lo9OJerlYanjwFrPR9fuGPjCiNiCzFgAWpiLAohSbsnH4ZAys3SBh+hq5rJosxUQ==", "dev": true, + "license": "MIT", "dependencies": { "protocols": "^2.0.1" } }, "node_modules/is-stream": { "version": "2.0.1", - "resolved": "https://registry.npmjs.org/is-stream/-/is-stream-2.0.1.tgz", - "integrity": "sha512-hFoiJiTl63nn+kstHGBtewWSKnQLpyb155KHheA1l39uvtO9nWIop1p3udqPcUd/xbF1VLMO4n7OI6p7RbngDg==", "dev": true, + "license": "MIT", "engines": { "node": ">=8" }, @@ -4400,9 +3708,8 @@ }, "node_modules/is-string": { "version": "1.0.7", - "resolved": "https://registry.npmjs.org/is-string/-/is-string-1.0.7.tgz", - "integrity": "sha512-tE2UXzivje6ofPW7l23cjDOMa09gb7xlAqG6jG5ej6uPV32TlWP3NKPigtaGeHNu9fohccRYvIiZMfOOnOYUtg==", "dev": true, + "license": "MIT", "dependencies": { "has-tostringtag": "^1.0.0" }, @@ -4415,9 +3722,8 @@ }, "node_modules/is-symbol": { "version": "1.0.4", - "resolved": "https://registry.npmjs.org/is-symbol/-/is-symbol-1.0.4.tgz", - "integrity": "sha512-C/CPBqKWnvdcxqIARxyOh4v1UUEOCHpgDa0WYgpKDFMszcrPcffg5uhwSgPCLD2WWxmq6isisz87tzT01tuGhg==", "dev": true, + "license": "MIT", "dependencies": { "has-symbols": "^1.0.2" }, @@ -4430,9 +3736,8 @@ }, "node_modules/is-typed-array": { "version": "1.1.13", - "resolved": "https://registry.npmjs.org/is-typed-array/-/is-typed-array-1.1.13.tgz", - "integrity": "sha512-uZ25/bUAlUY5fR4OKT4rZQEBrzQWYV9ZJYGGsUmEJ6thodVJ1HX64ePQ6Z0qPWP+m+Uq6e9UugrE38jeYsDSMw==", "dev": true, + "license": "MIT", "dependencies": { "which-typed-array": "^1.1.14" }, @@ -4445,15 +3750,13 @@ }, "node_modules/is-typedarray": { "version": "1.0.0", - "resolved": "https://registry.npmjs.org/is-typedarray/-/is-typedarray-1.0.0.tgz", - "integrity": "sha512-cyA56iCMHAh5CdzjJIa4aohJyeO1YbwLi3Jc35MmRU6poroFjIGZzUzupGiRPOjgHg9TLu43xbpwXk523fMxKA==", - "dev": true + "dev": true, + "license": "MIT" }, "node_modules/is-unicode-supported": { "version": "0.1.0", - "resolved": "https://registry.npmjs.org/is-unicode-supported/-/is-unicode-supported-0.1.0.tgz", - "integrity": "sha512-knxG2q4UC3u8stRGyAVJCOdxFmv5DZiRcdlIaAQXAbSfJya+OhopNotLQrstBhququ4ZpuKbDc/8S6mgXgPFPw==", "dev": true, + "license": "MIT", "engines": { "node": ">=10" }, @@ -4463,9 +3766,8 @@ }, "node_modules/is-weakref": { "version": "1.0.2", - "resolved": "https://registry.npmjs.org/is-weakref/-/is-weakref-1.0.2.tgz", - "integrity": "sha512-qctsuLZmIQ0+vSSMfoVvyFe2+GSEvnmZ2ezTup1SBse9+twCCeial6EEi3Nc2KFcf6+qz2FBPnjXsk8xhKSaPQ==", "dev": true, + "license": "MIT", "dependencies": { "call-bind": "^1.0.2" }, @@ -4475,18 +3777,16 @@ }, "node_modules/is-windows": { "version": "1.0.2", - "resolved": "https://registry.npmjs.org/is-windows/-/is-windows-1.0.2.tgz", - "integrity": "sha512-eXK1UInq2bPmjyX6e3VHIzMLobc4J94i4AWn+Hpq3OU5KkrRC96OAcR3PRJ/pGu6m8TRnBHP9dkXQVsT/COVIA==", "dev": true, + "license": "MIT", "engines": { "node": ">=0.10.0" } }, "node_modules/is-wsl": { "version": "3.1.0", - "resolved": "https://registry.npmjs.org/is-wsl/-/is-wsl-3.1.0.tgz", - "integrity": "sha512-UcVfVfaK4Sc4m7X3dUSoHoozQGBEFeDC+zVo06t98xe8CzHSZZBekNXH+tu0NalHolcJ/QAGqS46Hef7QXBIMw==", "dev": true, + "license": "MIT", "dependencies": { "is-inside-container": "^1.0.0" }, @@ -4499,21 +3799,18 @@ }, "node_modules/isarray": { "version": "2.0.5", - "resolved": "https://registry.npmjs.org/isarray/-/isarray-2.0.5.tgz", - "integrity": "sha512-xHjhDr3cNBK0BzdUJSPXZntQUx/mwMS5Rw4A7lPJ90XGAO6ISP/ePDNuo0vhqOZU+UD5JoodwCAAoZQd3FeAKw==", - "dev": true + "dev": true, + "license": "MIT" }, "node_modules/isexe": { "version": "2.0.0", - "resolved": "https://registry.npmjs.org/isexe/-/isexe-2.0.0.tgz", - "integrity": "sha512-RHxMLp9lnKHGHRng9QFhRCMbYAcVpn69smSGcq3f36xjgVVWThj4qqLbTLlq7Ssj8B+fIQ1EuCEGI2lKsyQeIw==", - "dev": true + "dev": true, + "license": "ISC" }, "node_modules/issue-parser": { "version": "6.0.0", - "resolved": "https://registry.npmjs.org/issue-parser/-/issue-parser-6.0.0.tgz", - "integrity": "sha512-zKa/Dxq2lGsBIXQ7CUZWTHfvxPC2ej0KfO7fIPqLlHB9J2hJ7rGhZ5rilhuufylr4RXYPzJUeFjKxz305OsNlA==", "dev": true, + "license": "MIT", "dependencies": { "lodash.capitalize": "^4.2.1", "lodash.escaperegexp": "^4.1.2", @@ -4527,18 +3824,16 @@ }, "node_modules/istanbul-lib-coverage": { "version": "3.2.2", - "resolved": "https://registry.npmjs.org/istanbul-lib-coverage/-/istanbul-lib-coverage-3.2.2.tgz", - "integrity": "sha512-O8dpsF+r0WV/8MNRKfnmrtCWhuKjxrq2w+jpzBL5UZKTi2LeVWnWOmWRxFlesJONmc+wLAGvKQZEOanko0LFTg==", "dev": true, + "license": "BSD-3-Clause", "engines": { "node": ">=8" } }, "node_modules/istanbul-lib-hook": { "version": "3.0.0", - "resolved": "https://registry.npmjs.org/istanbul-lib-hook/-/istanbul-lib-hook-3.0.0.tgz", - "integrity": "sha512-Pt/uge1Q9s+5VAZ+pCo16TYMWPBIl+oaNIjgLQxcX0itS6ueeaA+pEfThZpH8WxhFgCiEb8sAJY6MdUKgiIWaQ==", "dev": true, + "license": "BSD-3-Clause", "dependencies": { "append-transform": "^2.0.0" }, @@ -4548,9 +3843,8 @@ }, "node_modules/istanbul-lib-instrument": { "version": "4.0.3", - "resolved": "https://registry.npmjs.org/istanbul-lib-instrument/-/istanbul-lib-instrument-4.0.3.tgz", - "integrity": "sha512-BXgQl9kf4WTCPCCpmFGoJkz/+uhvm7h7PFKUYxh7qarQd3ER33vHG//qaE8eN25l07YqZPpHXU9I09l/RD5aGQ==", "dev": true, + "license": "BSD-3-Clause", "dependencies": { "@babel/core": "^7.7.5", "@istanbuljs/schema": "^0.1.2", @@ -4563,9 +3857,8 @@ }, "node_modules/istanbul-lib-processinfo": { "version": "2.0.3", - "resolved": "https://registry.npmjs.org/istanbul-lib-processinfo/-/istanbul-lib-processinfo-2.0.3.tgz", - "integrity": "sha512-NkwHbo3E00oybX6NGJi6ar0B29vxyvNwoC7eJ4G4Yq28UfY758Hgn/heV8VRFhevPED4LXfFz0DQ8z/0kw9zMg==", "dev": true, + "license": "ISC", "dependencies": { "archy": "^1.0.0", "cross-spawn": "^7.0.3", @@ -4580,9 +3873,8 @@ }, "node_modules/istanbul-lib-report": { "version": "3.0.1", - "resolved": "https://registry.npmjs.org/istanbul-lib-report/-/istanbul-lib-report-3.0.1.tgz", - "integrity": "sha512-GCfE1mtsHGOELCU8e/Z7YWzpmybrx/+dSTfLrvY8qRmaY6zXTKWn6WQIjaAFw069icm6GVMNkgu0NzI4iPZUNw==", "dev": true, + "license": "BSD-3-Clause", "dependencies": { "istanbul-lib-coverage": "^3.0.0", "make-dir": "^4.0.0", @@ -4594,9 +3886,8 @@ }, "node_modules/istanbul-lib-report/node_modules/lru-cache": { "version": "6.0.0", - "resolved": "https://registry.npmjs.org/lru-cache/-/lru-cache-6.0.0.tgz", - "integrity": "sha512-Jo6dJ04CmSjuznwJSS3pUeWmd/H0ffTlkXXgwZi+eq1UCmqQwCh+eLsYOYCwY991i2Fah4h1BEMCx4qThGbsiA==", "dev": true, + "license": "ISC", "dependencies": { "yallist": "^4.0.0" }, @@ -4606,9 +3897,8 @@ }, "node_modules/istanbul-lib-report/node_modules/make-dir": { "version": "4.0.0", - "resolved": "https://registry.npmjs.org/make-dir/-/make-dir-4.0.0.tgz", - "integrity": "sha512-hXdUTZYIVOt1Ex//jAQi+wTZZpUpwBj/0QsOzqegb3rGMMeJiSEu5xLHnYfBrRV4RH2+OCSOO95Is/7x1WJ4bw==", "dev": true, + "license": "MIT", "dependencies": { "semver": "^7.5.3" }, @@ -4621,9 +3911,8 @@ }, "node_modules/istanbul-lib-report/node_modules/semver": { "version": "7.5.4", - "resolved": "https://registry.npmjs.org/semver/-/semver-7.5.4.tgz", - "integrity": "sha512-1bCSESV6Pv+i21Hvpxp3Dx+pSD8lIPt8uVjRrxAUt/nbswYc+tK6Y2btiULjd4+fnq15PX+nqQDC7Oft7WkwcA==", "dev": true, + "license": "ISC", "dependencies": { "lru-cache": "^6.0.0" }, @@ -4636,9 +3925,8 @@ }, "node_modules/istanbul-lib-report/node_modules/supports-color": { "version": "7.2.0", - "resolved": "https://registry.npmjs.org/supports-color/-/supports-color-7.2.0.tgz", - "integrity": "sha512-qpCAvRl9stuOHveKsn7HncJRvv501qIacKzQlO/+Lwxc9+0q2wLyv4Dfvt80/DPn2pqOBsJdDiogXGR9+OvwRw==", "dev": true, + "license": "MIT", "dependencies": { "has-flag": "^4.0.0" }, @@ -4648,15 +3936,13 @@ }, "node_modules/istanbul-lib-report/node_modules/yallist": { "version": "4.0.0", - "resolved": "https://registry.npmjs.org/yallist/-/yallist-4.0.0.tgz", - "integrity": "sha512-3wdGidZyq5PB084XLES5TpOSRA3wjXAlIWMhum2kRcv/41Sn2emQ0dycQW4uZXLejwKvg6EsvbdlVL+FYEct7A==", - "dev": true + "dev": true, + "license": "ISC" }, "node_modules/istanbul-lib-source-maps": { "version": "4.0.1", - "resolved": "https://registry.npmjs.org/istanbul-lib-source-maps/-/istanbul-lib-source-maps-4.0.1.tgz", - "integrity": "sha512-n3s8EwkdFIJCG3BPKBYvskgXGoy88ARzvegkitk60NxRdwltLOTaH7CUiMRXvwYorl0Q712iEjcWB+fK/MrWVw==", "dev": true, + "license": "BSD-3-Clause", "dependencies": { "debug": "^4.1.1", "istanbul-lib-coverage": "^3.0.0", @@ -4668,9 +3954,8 @@ }, "node_modules/istanbul-reports": { "version": "3.1.6", - "resolved": "https://registry.npmjs.org/istanbul-reports/-/istanbul-reports-3.1.6.tgz", - "integrity": "sha512-TLgnMkKg3iTDsQ9PbPTdpfAK2DzjF9mqUG7RMgcQl8oFjad8ob4laGxv5XV5U9MAfx8D6tSJiUyuAwzLicaxlg==", "dev": true, + "license": "BSD-3-Clause", "dependencies": { "html-escaper": "^2.0.0", "istanbul-lib-report": "^3.0.0" @@ -4681,18 +3966,16 @@ }, "node_modules/iterate-iterator": { "version": "1.0.2", - "resolved": "https://registry.npmjs.org/iterate-iterator/-/iterate-iterator-1.0.2.tgz", - "integrity": "sha512-t91HubM4ZDQ70M9wqp+pcNpu8OyJ9UAtXntT/Bcsvp5tZMnz9vRa+IunKXeI8AnfZMTv0jNuVEmGeLSMjVvfPw==", "dev": true, + "license": "MIT", "funding": { "url": "https://github.com/sponsors/ljharb" } }, "node_modules/iterate-value": { "version": "1.0.2", - "resolved": "https://registry.npmjs.org/iterate-value/-/iterate-value-1.0.2.tgz", - "integrity": "sha512-A6fMAio4D2ot2r/TYzr4yUWrmwNdsN5xL7+HUiyACE4DXm+q8HtPcnFTp+NnW3k4N05tZ7FVYFFb2CR13NxyHQ==", "dev": true, + "license": "MIT", "dependencies": { "es-get-iterator": "^1.0.2", "iterate-iterator": "^1.0.1" @@ -4703,15 +3986,13 @@ }, "node_modules/js-tokens": { "version": "4.0.0", - "resolved": "https://registry.npmjs.org/js-tokens/-/js-tokens-4.0.0.tgz", - "integrity": "sha512-RdJUflcE3cUzKiMqQgsCu06FPu9UdIJO0beYbPhHN4k6apgJtifcoCtT9bcxOpYBtpD2kCM6Sbzg4CausW/PKQ==", - "dev": true + "dev": true, + "license": "MIT" }, "node_modules/js-yaml": { "version": "4.1.0", - "resolved": "https://registry.npmjs.org/js-yaml/-/js-yaml-4.1.0.tgz", - "integrity": "sha512-wpxZs9NoxZaJESJGIZTyDEaYpl0FKSA+FB9aJiyemKhMwkxQg63h4T1KJgUGHpTqPDNRcmmYLugrRjJlBtWvRA==", "dev": true, + "license": "MIT", "dependencies": { "argparse": "^2.0.1" }, @@ -4721,9 +4002,8 @@ }, "node_modules/jsesc": { "version": "2.5.2", - "resolved": "https://registry.npmjs.org/jsesc/-/jsesc-2.5.2.tgz", - "integrity": "sha512-OYu7XEzjkCQ3C5Ps3QIZsQfNpqoJyZZA99wd9aWd05NCtC5pWOkShK2mkL6HXQR6/Cy2lbNdPlZBpuQHXE63gA==", "dev": true, + "license": "MIT", "bin": { "jsesc": "bin/jsesc" }, @@ -4733,21 +4013,18 @@ }, "node_modules/json-buffer": { "version": "3.0.1", - "resolved": "https://registry.npmjs.org/json-buffer/-/json-buffer-3.0.1.tgz", - "integrity": "sha512-4bV5BfR2mqfQTJm+V5tPPdf+ZpuhiIvTuAB5g8kcrXOZpTT/QwwVRWBywX1ozr6lEuPdbHxwaJlm9G6mI2sfSQ==", - "dev": true + "dev": true, + "license": "MIT" }, "node_modules/json-parse-even-better-errors": { "version": "2.3.1", - "resolved": "https://registry.npmjs.org/json-parse-even-better-errors/-/json-parse-even-better-errors-2.3.1.tgz", - "integrity": "sha512-xyFwyhro/JEof6Ghe2iz2NcXoj2sloNsWr/XsERDK/oiPCfaNhl5ONfp+jQdAZRQQ0IJWNzH9zIZF7li91kh2w==", - "dev": true + "dev": true, + "license": "MIT" }, "node_modules/json5": { "version": "2.2.3", - "resolved": "https://registry.npmjs.org/json5/-/json5-2.2.3.tgz", - "integrity": "sha512-XmOWe7eyHYH14cLdVPoyg+GOH3rYX++KpzrylJwSW98t3Nk+U8XOl8FWKOgwtzdb8lXGf6zYwDUzeHMWfxasyg==", "dev": true, + "license": "MIT", "bin": { "json5": "lib/cli.js" }, @@ -4757,15 +4034,13 @@ }, "node_modules/jsonc-parser": { "version": "3.2.1", - "resolved": "https://registry.npmjs.org/jsonc-parser/-/jsonc-parser-3.2.1.tgz", - "integrity": "sha512-AilxAyFOAcK5wA1+LeaySVBrHsGQvUFCDWXKpZjzaL0PqW+xfBOttn8GNtWKFWqneyMZj41MWF9Kl6iPWLwgOA==", - "dev": true + "dev": true, + "license": "MIT" }, "node_modules/jsonfile": { "version": "6.1.0", - "resolved": "https://registry.npmjs.org/jsonfile/-/jsonfile-6.1.0.tgz", - "integrity": "sha512-5dgndWOriYSm5cnYaJNhalLNDKOqFwyDB/rr1E9ZsGciGvKPs8R2xYGCacuf3z6K1YKDz182fd+fY3cn3pMqXQ==", "dev": true, + "license": "MIT", "dependencies": { "universalify": "^2.0.0" }, @@ -4775,24 +4050,21 @@ }, "node_modules/just-extend": { "version": "6.2.0", - "resolved": "https://registry.npmjs.org/just-extend/-/just-extend-6.2.0.tgz", - "integrity": "sha512-cYofQu2Xpom82S6qD778jBDpwvvy39s1l/hrYij2u9AMdQcGRpaBu6kY4mVhuno5kJVi1DAz4aiphA2WI1/OAw==", - "dev": true + "dev": true, + "license": "MIT" }, "node_modules/keyv": { "version": "4.5.4", - "resolved": "https://registry.npmjs.org/keyv/-/keyv-4.5.4.tgz", - "integrity": "sha512-oxVHkHR/EJf2CNXnWxRLW6mg7JyCCUcG0DtEGmL2ctUo1PNTin1PUil+r/+4r5MpVgC/fn1kjsx7mjSujKqIpw==", "dev": true, + "license": "MIT", "dependencies": { "json-buffer": "3.0.1" } }, "node_modules/latest-version": { "version": "7.0.0", - "resolved": "https://registry.npmjs.org/latest-version/-/latest-version-7.0.0.tgz", - "integrity": "sha512-KvNT4XqAMzdcL6ka6Tl3i2lYeFDgXNCuIX+xNx6ZMVR1dFq+idXd9FLKNMOIx0t9mJ9/HudyX4oZWXZQ0UJHeg==", "dev": true, + "license": "MIT", "dependencies": { "package-json": "^8.1.0" }, @@ -4805,15 +4077,13 @@ }, "node_modules/lines-and-columns": { "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 + "dev": true, + "license": "MIT" }, "node_modules/locate-path": { "version": "6.0.0", - "resolved": "https://registry.npmjs.org/locate-path/-/locate-path-6.0.0.tgz", - "integrity": "sha512-iPZK6eYjbxRu3uB4/WZ3EsEIMJFMqAoopl3R+zuq0UjcAm/MO6KCweDgPfP3elTztoKP3KtnVHxTn2NHBSDVUw==", "dev": true, + "license": "MIT", "dependencies": { "p-locate": "^5.0.0" }, @@ -4826,57 +4096,48 @@ }, "node_modules/lodash": { "version": "4.17.21", - "resolved": "https://registry.npmjs.org/lodash/-/lodash-4.17.21.tgz", - "integrity": "sha512-v2kDEe57lecTulaDIuNTPy3Ry4gLGJ6Z1O3vE1krgXZNrsQ+LFTGHVxVjcXPs17LhbZVGedAJv8XZ1tvj5FvSg==", - "dev": true + "dev": true, + "license": "MIT" }, "node_modules/lodash.capitalize": { "version": "4.2.1", - "resolved": "https://registry.npmjs.org/lodash.capitalize/-/lodash.capitalize-4.2.1.tgz", - "integrity": "sha512-kZzYOKspf8XVX5AvmQF94gQW0lejFVgb80G85bU4ZWzoJ6C03PQg3coYAUpSTpQWelrZELd3XWgHzw4Ck5kaIw==", - "dev": true + "dev": true, + "license": "MIT" }, "node_modules/lodash.escaperegexp": { "version": "4.1.2", - "resolved": "https://registry.npmjs.org/lodash.escaperegexp/-/lodash.escaperegexp-4.1.2.tgz", - "integrity": "sha512-TM9YBvyC84ZxE3rgfefxUWiQKLilstD6k7PTGt6wfbtXF8ixIJLOL3VYyV/z+ZiPLsVxAsKAFVwWlWeb2Y8Yyw==", - "dev": true + "dev": true, + "license": "MIT" }, "node_modules/lodash.flattendeep": { "version": "4.4.0", - "resolved": "https://registry.npmjs.org/lodash.flattendeep/-/lodash.flattendeep-4.4.0.tgz", - "integrity": "sha512-uHaJFihxmJcEX3kT4I23ABqKKalJ/zDrDg0lsFtc1h+3uw49SIJ5beyhx5ExVRti3AvKoOJngIj7xz3oylPdWQ==", - "dev": true + "dev": true, + "license": "MIT" }, "node_modules/lodash.get": { "version": "4.4.2", - "resolved": "https://registry.npmjs.org/lodash.get/-/lodash.get-4.4.2.tgz", - "integrity": "sha512-z+Uw/vLuy6gQe8cfaFWD7p0wVv8fJl3mbzXh33RS+0oW2wvUqiRXiQ69gLWSLpgB5/6sU+r6BlQR0MBILadqTQ==", - "dev": true + "dev": true, + "license": "MIT" }, "node_modules/lodash.isplainobject": { "version": "4.0.6", - "resolved": "https://registry.npmjs.org/lodash.isplainobject/-/lodash.isplainobject-4.0.6.tgz", - "integrity": "sha512-oSXzaWypCMHkPC3NvBEaPHf0KsA5mvPrOPgQWDsbg8n7orZ290M0BmC/jgRZ4vcJ6DTAhjrsSYgdsW/F+MFOBA==", - "dev": true + "dev": true, + "license": "MIT" }, "node_modules/lodash.isstring": { "version": "4.0.1", - "resolved": "https://registry.npmjs.org/lodash.isstring/-/lodash.isstring-4.0.1.tgz", - "integrity": "sha512-0wJxfxH1wgO3GrbuP+dTTk7op+6L41QCXbGINEmD+ny/G/eCqGzxyCsh7159S+mgDDcoarnBw6PC1PS5+wUGgw==", - "dev": true + "dev": true, + "license": "MIT" }, "node_modules/lodash.uniqby": { "version": "4.7.0", - "resolved": "https://registry.npmjs.org/lodash.uniqby/-/lodash.uniqby-4.7.0.tgz", - "integrity": "sha512-e/zcLx6CSbmaEgFHCA7BnoQKyCtKMxnuWrJygbwPs/AIn+IMKl66L8/s+wBUn5LRw2pZx3bUHibiV1b6aTWIww==", - "dev": true + "dev": true, + "license": "MIT" }, "node_modules/log-symbols": { "version": "4.1.0", - "resolved": "https://registry.npmjs.org/log-symbols/-/log-symbols-4.1.0.tgz", - "integrity": "sha512-8XPvpAA8uyhfteu8pIvQxpJZ7SYYdpUivZpGy6sFsBuKRY/7rQGavedeB8aK+Zkyq6upMFVL/9AW6vOYzfRyLg==", "dev": true, + "license": "MIT", "dependencies": { "chalk": "^4.1.0", "is-unicode-supported": "^0.1.0" @@ -4890,9 +4151,8 @@ }, "node_modules/lowercase-keys": { "version": "3.0.0", - "resolved": "https://registry.npmjs.org/lowercase-keys/-/lowercase-keys-3.0.0.tgz", - "integrity": "sha512-ozCC6gdQ+glXOQsveKD0YsDy8DSQFjDTz4zyzEHNV5+JP5D62LmfDZ6o1cycFx9ouG940M5dE8C8CTewdj2YWQ==", "dev": true, + "license": "MIT", "engines": { "node": "^12.20.0 || ^14.13.1 || >=16.0.0" }, @@ -4902,24 +4162,21 @@ }, "node_modules/lru-cache": { "version": "5.1.1", - "resolved": "https://registry.npmjs.org/lru-cache/-/lru-cache-5.1.1.tgz", - "integrity": "sha512-KpNARQA3Iwv+jTA0utUVVbrh+Jlrr1Fv0e56GGzAFOXN7dk/FviaDW8LHmK52DlcH4WP2n6gI8vN1aesBFgo9w==", "dev": true, + "license": "ISC", "dependencies": { "yallist": "^3.0.2" } }, "node_modules/lunr": { "version": "2.3.9", - "resolved": "https://registry.npmjs.org/lunr/-/lunr-2.3.9.tgz", - "integrity": "sha512-zTU3DaZaF3Rt9rhN3uBMGQD3dD2/vFQqnvZCDv4dl5iOzq2IZQqTxu90r4E5J+nP70J3ilqVCrbho2eWaeW8Ow==", - "dev": true + "dev": true, + "license": "MIT" }, "node_modules/macos-release": { "version": "3.2.0", - "resolved": "https://registry.npmjs.org/macos-release/-/macos-release-3.2.0.tgz", - "integrity": "sha512-fSErXALFNsnowREYZ49XCdOHF8wOPWuFOGQrAhP7x5J/BqQv+B02cNsTykGpDgRVx43EKg++6ANmTaGTtW+hUA==", "dev": true, + "license": "MIT", "engines": { "node": "^12.20.0 || ^14.13.1 || >=16.0.0" }, @@ -4929,9 +4186,8 @@ }, "node_modules/make-dir": { "version": "3.1.0", - "resolved": "https://registry.npmjs.org/make-dir/-/make-dir-3.1.0.tgz", - "integrity": "sha512-g3FeP20LNwhALb/6Cz6Dd4F2ngze0jz7tbzrD2wAV+o9FeNHe4rL+yK2md0J/fiSf1sa1ADhXqi5+oVwOM/eGw==", "dev": true, + "license": "MIT", "dependencies": { "semver": "^6.0.0" }, @@ -4944,9 +4200,8 @@ }, "node_modules/marked": { "version": "4.3.0", - "resolved": "https://registry.npmjs.org/marked/-/marked-4.3.0.tgz", - "integrity": "sha512-PRsaiG84bK+AMvxziE/lCFss8juXjNaWzVbN5tXAm4XjeaS9NAHhop+PjQxz2A9h8Q4M/xGmzP8vqNwy6JeK0A==", "dev": true, + "license": "MIT", "bin": { "marked": "bin/marked.js" }, @@ -4956,24 +4211,21 @@ }, "node_modules/merge-stream": { "version": "2.0.0", - "resolved": "https://registry.npmjs.org/merge-stream/-/merge-stream-2.0.0.tgz", - "integrity": "sha512-abv/qOcuPfk3URPfDzmZU1LKmuw8kT+0nIHvKrKgFrwifol/doWcdA4ZqsWQ8ENrFKkd67Mfpo/LovbIUsbt3w==", - "dev": true + "dev": true, + "license": "MIT" }, "node_modules/merge2": { "version": "1.4.1", - "resolved": "https://registry.npmjs.org/merge2/-/merge2-1.4.1.tgz", - "integrity": "sha512-8q7VEgMJW4J8tcfVPy8g09NcQwZdbwFEqhe/WZkoIzjn/3TGDwtOCYtXGxA3O8tPzpczCCDgv+P2P5y00ZJOOg==", "dev": true, + "license": "MIT", "engines": { "node": ">= 8" } }, "node_modules/micromatch": { "version": "4.0.5", - "resolved": "https://registry.npmjs.org/micromatch/-/micromatch-4.0.5.tgz", - "integrity": "sha512-DMy+ERcEW2q8Z2Po+WNXuw3c5YaUSFjAO5GsJqfEl7UjvtIuFKO6ZrKvcItdy98dwFI2N1tg3zNIdKaQT+aNdA==", "dev": true, + "license": "MIT", "dependencies": { "braces": "^3.0.2", "picomatch": "^2.3.1" @@ -4984,18 +4236,16 @@ }, "node_modules/mime-db": { "version": "1.52.0", - "resolved": "https://registry.npmjs.org/mime-db/-/mime-db-1.52.0.tgz", - "integrity": "sha512-sPU4uV7dYlvtWJxwwxHD0PuihVNiE7TyAbQ5SWxDCB9mUYvOgroQOwYQQOKPJ8CIbE+1ETVlOoK1UC2nU3gYvg==", "dev": true, + "license": "MIT", "engines": { "node": ">= 0.6" } }, "node_modules/mime-types": { "version": "2.1.35", - "resolved": "https://registry.npmjs.org/mime-types/-/mime-types-2.1.35.tgz", - "integrity": "sha512-ZDY+bPm5zTTF+YpCrAU9nK0UgICYPT0QtT1NZWFv4s++TNkcgVaT0g6+4R2uI4MjQjzysHB1zxuWL50hzaeXiw==", "dev": true, + "license": "MIT", "dependencies": { "mime-db": "1.52.0" }, @@ -5005,9 +4255,8 @@ }, "node_modules/mimic-fn": { "version": "4.0.0", - "resolved": "https://registry.npmjs.org/mimic-fn/-/mimic-fn-4.0.0.tgz", - "integrity": "sha512-vqiC06CuhBTUdZH+RYl8sFrL096vA45Ok5ISO6sE/Mr1jRbGH4Csnhi8f3wKVl7x8mO4Au7Ir9D3Oyv1VYMFJw==", "dev": true, + "license": "MIT", "engines": { "node": ">=12" }, @@ -5017,9 +4266,8 @@ }, "node_modules/mimic-response": { "version": "4.0.0", - "resolved": "https://registry.npmjs.org/mimic-response/-/mimic-response-4.0.0.tgz", - "integrity": "sha512-e5ISH9xMYU0DzrT+jl8q2ze9D6eWBto+I8CNpe+VI+K2J/F/k3PdkdTdz4wvGVH4NTpo+NRYTVIuMQEMMcsLqg==", "dev": true, + "license": "MIT", "engines": { "node": "^12.20.0 || ^14.13.1 || >=16.0.0" }, @@ -5029,9 +4277,8 @@ }, "node_modules/minimatch": { "version": "3.1.2", - "resolved": "https://registry.npmjs.org/minimatch/-/minimatch-3.1.2.tgz", - "integrity": "sha512-J7p63hRiAjw1NDEww1W7i37+ByIrOWO5XQQAzZ3VOcL0PNybwpfmV/N05zFAzwQ9USyEcX6t3UO+K5aqBQOIHw==", "dev": true, + "license": "ISC", "dependencies": { "brace-expansion": "^1.1.7" }, @@ -5041,18 +4288,16 @@ }, "node_modules/minimist": { "version": "1.2.8", - "resolved": "https://registry.npmjs.org/minimist/-/minimist-1.2.8.tgz", - "integrity": "sha512-2yyAR8qBkN3YuheJanUpWC5U3bb5osDywNB8RzDVlDwDHbocAJveqqj1u8+SVD7jkWT4yvsHCpWqqWqAxb0zCA==", "dev": true, + "license": "MIT", "funding": { "url": "https://github.com/sponsors/ljharb" } }, "node_modules/mocha": { "version": "10.2.0", - "resolved": "https://registry.npmjs.org/mocha/-/mocha-10.2.0.tgz", - "integrity": "sha512-IDY7fl/BecMwFHzoqF2sg/SHHANeBoMMXFlS9r0OXKDssYE1M5O43wUY/9BVPeIvfH2zmEbBfseqN9gBQZzXkg==", "dev": true, + "license": "MIT", "dependencies": { "ansi-colors": "4.1.1", "browser-stdout": "1.3.1", @@ -5090,9 +4335,8 @@ }, "node_modules/mocha/node_modules/glob": { "version": "7.2.0", - "resolved": "https://registry.npmjs.org/glob/-/glob-7.2.0.tgz", - "integrity": "sha512-lmLf6gtyrPq8tTjSmrO94wBeQbFR3HbLHbuyD69wuyQkImp2hWqMGB47OX65FBkPffO641IP9jWa1z4ivqG26Q==", "dev": true, + "license": "ISC", "dependencies": { "fs.realpath": "^1.0.0", "inflight": "^1.0.4", @@ -5110,9 +4354,8 @@ }, "node_modules/mocha/node_modules/glob/node_modules/minimatch": { "version": "3.1.2", - "resolved": "https://registry.npmjs.org/minimatch/-/minimatch-3.1.2.tgz", - "integrity": "sha512-J7p63hRiAjw1NDEww1W7i37+ByIrOWO5XQQAzZ3VOcL0PNybwpfmV/N05zFAzwQ9USyEcX6t3UO+K5aqBQOIHw==", "dev": true, + "license": "ISC", "dependencies": { "brace-expansion": "^1.1.7" }, @@ -5122,9 +4365,8 @@ }, "node_modules/mocha/node_modules/minimatch": { "version": "5.0.1", - "resolved": "https://registry.npmjs.org/minimatch/-/minimatch-5.0.1.tgz", - "integrity": "sha512-nLDxIFRyhDblz3qMuq+SoRZED4+miJ/G+tdDrjkkkRnjAsBexeGpgjLEQ0blJy7rHhR2b93rhQY4SvyWu9v03g==", "dev": true, + "license": "ISC", "dependencies": { "brace-expansion": "^2.0.1" }, @@ -5134,33 +4376,29 @@ }, "node_modules/mocha/node_modules/minimatch/node_modules/brace-expansion": { "version": "2.0.1", - "resolved": "https://registry.npmjs.org/brace-expansion/-/brace-expansion-2.0.1.tgz", - "integrity": "sha512-XnAIvQ8eM+kC6aULx6wuQiwVsnzsi9d3WxzV3FpWTGA19F621kwdbsAcFKXgKUHZWsy+mY6iL1sHTxWEFCytDA==", "dev": true, + "license": "MIT", "dependencies": { "balanced-match": "^1.0.0" } }, "node_modules/ms": { "version": "2.1.3", - "resolved": "https://registry.npmjs.org/ms/-/ms-2.1.3.tgz", - "integrity": "sha512-6FlzubTLZG3J2a/NVCAleEhjzq5oxgHyaCU9yYXvcLsvoVaHJq/s5xXI6/XXP6tz7R9xAOtHnSO/tXtF3WRTlA==", - "dev": true + "dev": true, + "license": "MIT" }, "node_modules/mute-stream": { "version": "1.0.0", - "resolved": "https://registry.npmjs.org/mute-stream/-/mute-stream-1.0.0.tgz", - "integrity": "sha512-avsJQhyd+680gKXyG/sQc0nXaC6rBkPOfyHYcFb9+hdkqQkR9bdnkJ0AMZhke0oesPqIO+mFFJ+IdBc7mst4IA==", "dev": true, + "license": "ISC", "engines": { "node": "^14.17.0 || ^16.13.0 || >=18.0.0" } }, "node_modules/nanoid": { "version": "3.3.3", - "resolved": "https://registry.npmjs.org/nanoid/-/nanoid-3.3.3.tgz", - "integrity": "sha512-p1sjXuopFs0xg+fPASzQ28agW1oHD7xDsd9Xkf3T15H3c/cifrFHVwrh74PdoklAPi+i7MdRsE47vm2r6JoB+w==", "dev": true, + "license": "MIT", "bin": { "nanoid": "bin/nanoid.cjs" }, @@ -5170,18 +4408,16 @@ }, "node_modules/netmask": { "version": "2.0.2", - "resolved": "https://registry.npmjs.org/netmask/-/netmask-2.0.2.tgz", - "integrity": "sha512-dBpDMdxv9Irdq66304OLfEmQ9tbNRFnFTuZiLo+bD+r332bBmMJ8GBLXklIXXgxd3+v9+KUnZaUR5PJMa75Gsg==", "dev": true, + "license": "MIT", "engines": { "node": ">= 0.4.0" } }, "node_modules/new-github-release-url": { "version": "2.0.0", - "resolved": "https://registry.npmjs.org/new-github-release-url/-/new-github-release-url-2.0.0.tgz", - "integrity": "sha512-NHDDGYudnvRutt/VhKFlX26IotXe1w0cmkDm6JGquh5bz/bDTw0LufSmH/GxTjEdpHEO+bVKFTwdrcGa/9XlKQ==", "dev": true, + "license": "MIT", "dependencies": { "type-fest": "^2.5.1" }, @@ -5194,9 +4430,8 @@ }, "node_modules/new-github-release-url/node_modules/type-fest": { "version": "2.19.0", - "resolved": "https://registry.npmjs.org/type-fest/-/type-fest-2.19.0.tgz", - "integrity": "sha512-RAH822pAdBgcNMAfWnCBU3CFZcfZ/i1eZjwFU/dsLKumyuuP3niueg2UAukXYF0E2AAoc82ZSSf9J0WQBinzHA==", "dev": true, + "license": "(MIT OR CC0-1.0)", "engines": { "node": ">=12.20" }, @@ -5206,9 +4441,8 @@ }, "node_modules/nise": { "version": "5.1.9", - "resolved": "https://registry.npmjs.org/nise/-/nise-5.1.9.tgz", - "integrity": "sha512-qOnoujW4SV6e40dYxJOb3uvuoPHtmLzIk4TFo+j0jPJoC+5Z9xja5qH5JZobEPsa8+YYphMrOSwnrshEhG2qww==", "dev": true, + "license": "BSD-3-Clause", "dependencies": { "@sinonjs/commons": "^3.0.0", "@sinonjs/fake-timers": "^11.2.2", @@ -5219,8 +4453,6 @@ }, "node_modules/node-domexception": { "version": "1.0.0", - "resolved": "https://registry.npmjs.org/node-domexception/-/node-domexception-1.0.0.tgz", - "integrity": "sha512-/jKZoMpw0F8GRwl4/eLROPA3cfcXtLApP0QzLmUT/HuPCZWyB7IY9ZrMeKw2O/nFIqPQB3PVM9aYm0F312AXDQ==", "dev": true, "funding": [ { @@ -5232,15 +4464,15 @@ "url": "https://paypal.me/jimmywarting" } ], + "license": "MIT", "engines": { "node": ">=10.5.0" } }, "node_modules/node-fetch": { "version": "3.3.2", - "resolved": "https://registry.npmjs.org/node-fetch/-/node-fetch-3.3.2.tgz", - "integrity": "sha512-dRB78srN/l6gqWulah9SrxeYnxeddIG30+GOqK/9OlLVyLg3HPnr6SqOWTWOXKRwC2eGYCkZ59NNuSgvSrpgOA==", "dev": true, + "license": "MIT", "dependencies": { "data-uri-to-buffer": "^4.0.0", "fetch-blob": "^3.1.4", @@ -5256,9 +4488,8 @@ }, "node_modules/node-preload": { "version": "0.2.1", - "resolved": "https://registry.npmjs.org/node-preload/-/node-preload-0.2.1.tgz", - "integrity": "sha512-RM5oyBy45cLEoHqCeh+MNuFAxO0vTFBLskvQbOKnEE7YTTSN4tbN8QWDIPQ6L+WvKsB/qLEGpYe2ZZ9d4W9OIQ==", "dev": true, + "license": "MIT", "dependencies": { "process-on-spawn": "^1.0.0" }, @@ -5268,24 +4499,21 @@ }, "node_modules/node-releases": { "version": "2.0.14", - "resolved": "https://registry.npmjs.org/node-releases/-/node-releases-2.0.14.tgz", - "integrity": "sha512-y10wOWt8yZpqXmOgRo77WaHEmhYQYGNA6y421PKsKYWEK8aW+cqAphborZDhqfyKrbZEN92CN1X2KbafY2s7Yw==", - "dev": true + "dev": true, + "license": "MIT" }, "node_modules/normalize-path": { "version": "3.0.0", - "resolved": "https://registry.npmjs.org/normalize-path/-/normalize-path-3.0.0.tgz", - "integrity": "sha512-6eZs5Ls3WtCisHWp9S2GUy8dqkpGi4BVSz3GaqiE6ezub0512ESztXUwUB6C6IKbQkY2Pnb/mD4WYojCRwcwLA==", "dev": true, + "license": "MIT", "engines": { "node": ">=0.10.0" } }, "node_modules/normalize-url": { "version": "8.0.0", - "resolved": "https://registry.npmjs.org/normalize-url/-/normalize-url-8.0.0.tgz", - "integrity": "sha512-uVFpKhj5MheNBJRTiMZ9pE/7hD1QTeEvugSJW/OmLzAp78PB5O6adfMNTvmfKhXBkvCzC+rqifWcVYpGFwTjnw==", "dev": true, + "license": "MIT", "engines": { "node": ">=14.16" }, @@ -5295,9 +4523,8 @@ }, "node_modules/npm-run-path": { "version": "5.2.0", - "resolved": "https://registry.npmjs.org/npm-run-path/-/npm-run-path-5.2.0.tgz", - "integrity": "sha512-W4/tgAXFqFA0iL7fk0+uQ3g7wkL8xJmx3XdK0VGb4cHW//eZTtKGvFBBoRKVTpY7n6ze4NL9ly7rgXcHufqXKg==", "dev": true, + "license": "MIT", "dependencies": { "path-key": "^4.0.0" }, @@ -5310,9 +4537,8 @@ }, "node_modules/npm-run-path/node_modules/path-key": { "version": "4.0.0", - "resolved": "https://registry.npmjs.org/path-key/-/path-key-4.0.0.tgz", - "integrity": "sha512-haREypq7xkM7ErfgIyA0z+Bj4AGKlMSdlQE2jvJo6huWD1EdkKYV+G/T4nq0YEF2vgTT8kqMFKo1uHn950r4SQ==", "dev": true, + "license": "MIT", "engines": { "node": ">=12" }, @@ -5322,9 +4548,8 @@ }, "node_modules/nyc": { "version": "15.1.0", - "resolved": "https://registry.npmjs.org/nyc/-/nyc-15.1.0.tgz", - "integrity": "sha512-jMW04n9SxKdKi1ZMGhvUTHBN0EICCRkHemEoE5jm6mTYcqcdas0ATzgUgejlQUHMvpnOZqGB5Xxsv9KxJW1j8A==", "dev": true, + "license": "ISC", "dependencies": { "@istanbuljs/load-nyc-config": "^1.0.0", "@istanbuljs/schema": "^0.1.2", @@ -5363,9 +4588,8 @@ }, "node_modules/nyc/node_modules/cliui": { "version": "6.0.0", - "resolved": "https://registry.npmjs.org/cliui/-/cliui-6.0.0.tgz", - "integrity": "sha512-t6wbgtoCXvAzst7QgXxJYqPt0usEfbgQdftEPbLL/cvv6HPE5VgvqCuAIDR0NgU52ds6rFwqrgakNLrHEjCbrQ==", "dev": true, + "license": "ISC", "dependencies": { "string-width": "^4.2.0", "strip-ansi": "^6.0.0", @@ -5374,9 +4598,8 @@ }, "node_modules/nyc/node_modules/find-up": { "version": "4.1.0", - "resolved": "https://registry.npmjs.org/find-up/-/find-up-4.1.0.tgz", - "integrity": "sha512-PpOwAdQ/YlXQ2vj8a3h8IipDuYRi3wceVQQGYWxNINccq40Anw7BlsEXCMbt1Zt+OLA6Fq9suIpIWD0OsnISlw==", "dev": true, + "license": "MIT", "dependencies": { "locate-path": "^5.0.0", "path-exists": "^4.0.0" @@ -5387,9 +4610,8 @@ }, "node_modules/nyc/node_modules/locate-path": { "version": "5.0.0", - "resolved": "https://registry.npmjs.org/locate-path/-/locate-path-5.0.0.tgz", - "integrity": "sha512-t7hw9pI+WvuwNJXwk5zVHpyhIqzg2qTlklJOf0mVxGSbe3Fp2VieZcduNYjaLDoy6p9uGpQEGWG87WpMKlNq8g==", "dev": true, + "license": "MIT", "dependencies": { "p-locate": "^4.1.0" }, @@ -5399,9 +4621,8 @@ }, "node_modules/nyc/node_modules/p-limit": { "version": "2.3.0", - "resolved": "https://registry.npmjs.org/p-limit/-/p-limit-2.3.0.tgz", - "integrity": "sha512-//88mFWSJx8lxCzwdAABTJL2MyWB12+eIY7MDL2SqLmAkeKU9qxRvWuSyTjm3FUmpBEMuFfckAIqEaVGUDxb6w==", "dev": true, + "license": "MIT", "dependencies": { "p-try": "^2.0.0" }, @@ -5414,9 +4635,8 @@ }, "node_modules/nyc/node_modules/p-locate": { "version": "4.1.0", - "resolved": "https://registry.npmjs.org/p-locate/-/p-locate-4.1.0.tgz", - "integrity": "sha512-R79ZZ/0wAxKGu3oYMlz8jy/kbhsNrS7SKZ7PxEHBgJ5+F2mtFW2fK2cOtBh1cHYkQsbzFV7I+EoRKe6Yt0oK7A==", "dev": true, + "license": "MIT", "dependencies": { "p-limit": "^2.2.0" }, @@ -5426,15 +4646,13 @@ }, "node_modules/nyc/node_modules/y18n": { "version": "4.0.3", - "resolved": "https://registry.npmjs.org/y18n/-/y18n-4.0.3.tgz", - "integrity": "sha512-JKhqTOwSrqNA1NY5lSztJ1GrBiUodLMmIZuLiDaMRJ+itFd+ABVE8XBjOvIWL+rSqNDC74LCSFmlb/U4UZ4hJQ==", - "dev": true + "dev": true, + "license": "ISC" }, "node_modules/nyc/node_modules/yargs": { "version": "15.4.1", - "resolved": "https://registry.npmjs.org/yargs/-/yargs-15.4.1.tgz", - "integrity": "sha512-aePbxDmcYW++PaqBsJ+HYUFwCdv4LVvdnhBy78E57PIor8/OVvhMrADFFEDh8DHDFRv/O9i3lPhsENjO7QX0+A==", "dev": true, + "license": "MIT", "dependencies": { "cliui": "^6.0.0", "decamelize": "^1.2.0", @@ -5454,9 +4672,8 @@ }, "node_modules/nyc/node_modules/yargs-parser": { "version": "18.1.3", - "resolved": "https://registry.npmjs.org/yargs-parser/-/yargs-parser-18.1.3.tgz", - "integrity": "sha512-o50j0JeToy/4K6OZcaQmW6lyXXKhq7csREXcDwk2omFPJEwUNOVtJKvmDr9EI1fAJZUyZcRF7kxGBWmRXudrCQ==", "dev": true, + "license": "ISC", "dependencies": { "camelcase": "^5.0.0", "decamelize": "^1.2.0" @@ -5467,36 +4684,32 @@ }, "node_modules/object-assign": { "version": "4.1.1", - "resolved": "https://registry.npmjs.org/object-assign/-/object-assign-4.1.1.tgz", - "integrity": "sha512-rJgTQnkUnH1sFw8yT6VSU3zD3sWmu6sZhIseY8VX+GRu3P6F7Fu+JNDoXfklElbLJSnc3FUQHVe4cU5hj+BcUg==", "dev": true, + "license": "MIT", "engines": { "node": ">=0.10.0" } }, "node_modules/object-inspect": { "version": "1.13.1", - "resolved": "https://registry.npmjs.org/object-inspect/-/object-inspect-1.13.1.tgz", - "integrity": "sha512-5qoj1RUiKOMsCCNLV1CBiPYE10sziTsnmNxkAI/rZhiD63CF7IqdFGC/XzjWjpSgLf0LxXX3bDFIh0E18f6UhQ==", "dev": true, + "license": "MIT", "funding": { "url": "https://github.com/sponsors/ljharb" } }, "node_modules/object-keys": { "version": "1.1.1", - "resolved": "https://registry.npmjs.org/object-keys/-/object-keys-1.1.1.tgz", - "integrity": "sha512-NuAESUOUMrlIXOfHKzD6bpPu3tYt3xvjNdRIQ+FeT0lNb4K8WR70CaDxhuNguS2XG+GjkyMwOzsN5ZktImfhLA==", "dev": true, + "license": "MIT", "engines": { "node": ">= 0.4" } }, "node_modules/object.assign": { "version": "4.1.5", - "resolved": "https://registry.npmjs.org/object.assign/-/object.assign-4.1.5.tgz", - "integrity": "sha512-byy+U7gp+FVwmyzKPYhW2h5l3crpmGsxl7X2s8y43IgxvG4g3QZ6CffDtsNQy1WsmZpQbO+ybo0AlW7TY6DcBQ==", "dev": true, + "license": "MIT", "dependencies": { "call-bind": "^1.0.5", "define-properties": "^1.2.1", @@ -5512,18 +4725,16 @@ }, "node_modules/once": { "version": "1.4.0", - "resolved": "https://registry.npmjs.org/once/-/once-1.4.0.tgz", - "integrity": "sha512-lNaJgI+2Q5URQBkccEKHTQOPaXdUxnZZElQTZY0MFUAuaEqe1E+Nyvgdz/aIyNi6Z9MzO5dv1H8n58/GELp3+w==", "dev": true, + "license": "ISC", "dependencies": { "wrappy": "1" } }, "node_modules/onetime": { "version": "6.0.0", - "resolved": "https://registry.npmjs.org/onetime/-/onetime-6.0.0.tgz", - "integrity": "sha512-1FlR+gjXK7X+AsAHso35MnyN5KqGwJRi/31ft6x0M194ht7S+rWAvd7PHss9xSKMzE0asv1pyIHaJYq+BbacAQ==", "dev": true, + "license": "MIT", "dependencies": { "mimic-fn": "^4.0.0" }, @@ -5536,9 +4747,8 @@ }, "node_modules/open": { "version": "10.0.3", - "resolved": "https://registry.npmjs.org/open/-/open-10.0.3.tgz", - "integrity": "sha512-dtbI5oW7987hwC9qjJTyABldTaa19SuyJse1QboWv3b0qCcrrLNVDqBx1XgELAjh9QTVQaP/C5b1nhQebd1H2A==", "dev": true, + "license": "MIT", "dependencies": { "default-browser": "^5.2.1", "define-lazy-prop": "^3.0.0", @@ -5554,9 +4764,8 @@ }, "node_modules/ora": { "version": "8.0.1", - "resolved": "https://registry.npmjs.org/ora/-/ora-8.0.1.tgz", - "integrity": "sha512-ANIvzobt1rls2BDny5fWZ3ZVKyD6nscLvfFRpQgfWsythlcsVUC9kL0zq6j2Z5z9wwp1kd7wpsD/T9qNPVLCaQ==", "dev": true, + "license": "MIT", "dependencies": { "chalk": "^5.3.0", "cli-cursor": "^4.0.0", @@ -5577,9 +4786,8 @@ }, "node_modules/ora/node_modules/ansi-regex": { "version": "6.0.1", - "resolved": "https://registry.npmjs.org/ansi-regex/-/ansi-regex-6.0.1.tgz", - "integrity": "sha512-n5M855fKb2SsfMIiFFoVrABHJC8QtHwVx+mHWP3QcEqBHYienj5dHSgjbxtC0WEZXYt4wcD6zrQElDPhFuZgfA==", "dev": true, + "license": "MIT", "engines": { "node": ">=12" }, @@ -5589,9 +4797,8 @@ }, "node_modules/ora/node_modules/chalk": { "version": "5.3.0", - "resolved": "https://registry.npmjs.org/chalk/-/chalk-5.3.0.tgz", - "integrity": "sha512-dLitG79d+GV1Nb/VYcCDFivJeK1hiukt9QjRNVOsUtTy1rR1YJsmpGGTZ3qJos+uw7WmWF4wUwBd9jxjocFC2w==", "dev": true, + "license": "MIT", "engines": { "node": "^12.17.0 || ^14.13 || >=16.0.0" }, @@ -5601,9 +4808,8 @@ }, "node_modules/ora/node_modules/cli-cursor": { "version": "4.0.0", - "resolved": "https://registry.npmjs.org/cli-cursor/-/cli-cursor-4.0.0.tgz", - "integrity": "sha512-VGtlMu3x/4DOtIUwEkRezxUZ2lBacNJCHash0N0WeZDBS+7Ux1dm3XWAgWYxLJFMMdOeXMHXorshEFhbMSGelg==", "dev": true, + "license": "MIT", "dependencies": { "restore-cursor": "^4.0.0" }, @@ -5616,15 +4822,13 @@ }, "node_modules/ora/node_modules/emoji-regex": { "version": "10.3.0", - "resolved": "https://registry.npmjs.org/emoji-regex/-/emoji-regex-10.3.0.tgz", - "integrity": "sha512-QpLs9D9v9kArv4lfDEgg1X/gN5XLnf/A6l9cs8SPZLRZR3ZkY9+kwIQTxm+fsSej5UMYGE8fdoaZVIBlqG0XTw==", - "dev": true + "dev": true, + "license": "MIT" }, "node_modules/ora/node_modules/is-unicode-supported": { "version": "2.0.0", - "resolved": "https://registry.npmjs.org/is-unicode-supported/-/is-unicode-supported-2.0.0.tgz", - "integrity": "sha512-FRdAyx5lusK1iHG0TWpVtk9+1i+GjrzRffhDg4ovQ7mcidMQ6mj+MhKPmvh7Xwyv5gIS06ns49CA7Sqg7lC22Q==", "dev": true, + "license": "MIT", "engines": { "node": ">=18" }, @@ -5634,9 +4838,8 @@ }, "node_modules/ora/node_modules/log-symbols": { "version": "6.0.0", - "resolved": "https://registry.npmjs.org/log-symbols/-/log-symbols-6.0.0.tgz", - "integrity": "sha512-i24m8rpwhmPIS4zscNzK6MSEhk0DUWa/8iYQWxhffV8jkI4Phvs3F+quL5xvS0gdQR0FyTCMMH33Y78dDTzzIw==", "dev": true, + "license": "MIT", "dependencies": { "chalk": "^5.3.0", "is-unicode-supported": "^1.3.0" @@ -5650,9 +4853,8 @@ }, "node_modules/ora/node_modules/log-symbols/node_modules/is-unicode-supported": { "version": "1.3.0", - "resolved": "https://registry.npmjs.org/is-unicode-supported/-/is-unicode-supported-1.3.0.tgz", - "integrity": "sha512-43r2mRvz+8JRIKnWJ+3j8JtjRKZ6GmjzfaE/qiBJnikNnYv/6bagRJ1kUhNk8R5EX/GkobD+r+sfxCPJsiKBLQ==", "dev": true, + "license": "MIT", "engines": { "node": ">=12" }, @@ -5662,18 +4864,16 @@ }, "node_modules/ora/node_modules/mimic-fn": { "version": "2.1.0", - "resolved": "https://registry.npmjs.org/mimic-fn/-/mimic-fn-2.1.0.tgz", - "integrity": "sha512-OqbOk5oEQeAZ8WXWydlu9HJjz9WVdEIvamMCcXmuqUYjTknH/sqsWvhQ3vgwKFRR1HpjvNBKQ37nbJgYzGqGcg==", "dev": true, + "license": "MIT", "engines": { "node": ">=6" } }, "node_modules/ora/node_modules/onetime": { "version": "5.1.2", - "resolved": "https://registry.npmjs.org/onetime/-/onetime-5.1.2.tgz", - "integrity": "sha512-kbpaSSGJTWdAY5KPVeMOKXSrPtr8C8C7wodJbcsd51jRnmD+GZu8Y0VoU6Dm5Z4vWr0Ig/1NKuWRKf7j5aaYSg==", "dev": true, + "license": "MIT", "dependencies": { "mimic-fn": "^2.1.0" }, @@ -5686,9 +4886,8 @@ }, "node_modules/ora/node_modules/restore-cursor": { "version": "4.0.0", - "resolved": "https://registry.npmjs.org/restore-cursor/-/restore-cursor-4.0.0.tgz", - "integrity": "sha512-I9fPXU9geO9bHOt9pHHOhOkYerIMsmVaWB0rA2AI9ERh/+x/i7MV5HKBNrg+ljO5eoPVgCcnFuRjJ9uH6I/3eg==", "dev": true, + "license": "MIT", "dependencies": { "onetime": "^5.1.0", "signal-exit": "^3.0.2" @@ -5702,9 +4901,8 @@ }, "node_modules/ora/node_modules/string-width": { "version": "7.1.0", - "resolved": "https://registry.npmjs.org/string-width/-/string-width-7.1.0.tgz", - "integrity": "sha512-SEIJCWiX7Kg4c129n48aDRwLbFb2LJmXXFrWBG4NGaRtMQ3myKPKbwrD1BKqQn74oCoNMBVrfDEr5M9YxCsrkw==", "dev": true, + "license": "MIT", "dependencies": { "emoji-regex": "^10.3.0", "get-east-asian-width": "^1.0.0", @@ -5719,9 +4917,8 @@ }, "node_modules/ora/node_modules/strip-ansi": { "version": "7.1.0", - "resolved": "https://registry.npmjs.org/strip-ansi/-/strip-ansi-7.1.0.tgz", - "integrity": "sha512-iq6eVVI64nQQTRYq2KtEg2d2uU7LElhTJwsH4YzIHZshxlgZms/wIc4VoDQTlG/IvVIrBKG06CrZnp0qv7hkcQ==", "dev": true, + "license": "MIT", "dependencies": { "ansi-regex": "^6.0.1" }, @@ -5734,9 +4931,8 @@ }, "node_modules/os-name": { "version": "5.1.0", - "resolved": "https://registry.npmjs.org/os-name/-/os-name-5.1.0.tgz", - "integrity": "sha512-YEIoAnM6zFmzw3PQ201gCVCIWbXNyKObGlVvpAVvraAeOHnlYVKFssbA/riRX5R40WA6kKrZ7Dr7dWzO3nKSeQ==", "dev": true, + "license": "MIT", "dependencies": { "macos-release": "^3.1.0", "windows-release": "^5.0.1" @@ -5750,27 +4946,24 @@ }, "node_modules/os-tmpdir": { "version": "1.0.2", - "resolved": "https://registry.npmjs.org/os-tmpdir/-/os-tmpdir-1.0.2.tgz", - "integrity": "sha512-D2FR03Vir7FIu45XBY20mTb+/ZSWB00sjU9jdQXt83gDrI4Ztz5Fs7/yy74g2N5SVQY4xY1qDr4rNddwYRVX0g==", "dev": true, + "license": "MIT", "engines": { "node": ">=0.10.0" } }, "node_modules/p-cancelable": { "version": "3.0.0", - "resolved": "https://registry.npmjs.org/p-cancelable/-/p-cancelable-3.0.0.tgz", - "integrity": "sha512-mlVgR3PGuzlo0MmTdk4cXqXWlwQDLnONTAg6sm62XkMJEiRxN3GL3SffkYvqwonbkJBcrI7Uvv5Zh9yjvn2iUw==", "dev": true, + "license": "MIT", "engines": { "node": ">=12.20" } }, "node_modules/p-limit": { "version": "3.1.0", - "resolved": "https://registry.npmjs.org/p-limit/-/p-limit-3.1.0.tgz", - "integrity": "sha512-TYOanM3wGwNGsZN2cVTYPArw454xnXj5qmWF1bEoAc4+cU/ol7GVh7odevjp1FNHduHc3KZMcFduxU5Xc6uJRQ==", "dev": true, + "license": "MIT", "dependencies": { "yocto-queue": "^0.1.0" }, @@ -5783,9 +4976,8 @@ }, "node_modules/p-locate": { "version": "5.0.0", - "resolved": "https://registry.npmjs.org/p-locate/-/p-locate-5.0.0.tgz", - "integrity": "sha512-LaNjtRWUBY++zB5nE/NwcaoMylSPk+S+ZHNB1TzdbMJMny6dynpAGt7X/tl/QYq3TIeE6nxHppbo2LGymrG5Pw==", "dev": true, + "license": "MIT", "dependencies": { "p-limit": "^3.0.2" }, @@ -5798,9 +4990,8 @@ }, "node_modules/p-map": { "version": "3.0.0", - "resolved": "https://registry.npmjs.org/p-map/-/p-map-3.0.0.tgz", - "integrity": "sha512-d3qXVTF/s+W+CdJ5A29wywV2n8CQQYahlgz2bFiA+4eVNJbHJodPZ+/gXwPGh0bOqA+j8S+6+ckmvLGPk1QpxQ==", "dev": true, + "license": "MIT", "dependencies": { "aggregate-error": "^3.0.0" }, @@ -5810,18 +5001,16 @@ }, "node_modules/p-try": { "version": "2.2.0", - "resolved": "https://registry.npmjs.org/p-try/-/p-try-2.2.0.tgz", - "integrity": "sha512-R4nPAVTAU0B9D35/Gk3uJf/7XYbQcyohSKdvAxIRSNghFl4e71hVoGnBNQz9cWaXxO2I10KTC+3jMdvvoKw6dQ==", "dev": true, + "license": "MIT", "engines": { "node": ">=6" } }, "node_modules/pac-proxy-agent": { "version": "7.0.1", - "resolved": "https://registry.npmjs.org/pac-proxy-agent/-/pac-proxy-agent-7.0.1.tgz", - "integrity": "sha512-ASV8yU4LLKBAjqIPMbrgtaKIvxQri/yh2OpI+S6hVa9JRkUI3Y3NPFbfngDtY7oFtSMD3w31Xns89mDa3Feo5A==", "dev": true, + "license": "MIT", "dependencies": { "@tootallnate/quickjs-emscripten": "^0.23.0", "agent-base": "^7.0.2", @@ -5838,9 +5027,8 @@ }, "node_modules/pac-resolver": { "version": "7.0.0", - "resolved": "https://registry.npmjs.org/pac-resolver/-/pac-resolver-7.0.0.tgz", - "integrity": "sha512-Fd9lT9vJbHYRACT8OhCbZBbxr6KRSawSovFpy8nDGshaK99S/EBhVIHp9+crhxrsZOuvLpgL1n23iyPg6Rl2hg==", "dev": true, + "license": "MIT", "dependencies": { "degenerator": "^5.0.0", "ip": "^1.1.8", @@ -5852,9 +5040,8 @@ }, "node_modules/package-hash": { "version": "4.0.0", - "resolved": "https://registry.npmjs.org/package-hash/-/package-hash-4.0.0.tgz", - "integrity": "sha512-whdkPIooSu/bASggZ96BWVvZTRMOFxnyUG5PnTSGKoJE2gd5mbVNmR2Nj20QFzxYYgAXpoqC+AiXzl+UMRh7zQ==", "dev": true, + "license": "ISC", "dependencies": { "graceful-fs": "^4.1.15", "hasha": "^5.0.0", @@ -5867,9 +5054,8 @@ }, "node_modules/package-json": { "version": "8.1.1", - "resolved": "https://registry.npmjs.org/package-json/-/package-json-8.1.1.tgz", - "integrity": "sha512-cbH9IAIJHNj9uXi196JVsRlt7cHKak6u/e6AkL/bkRelZ7rlL3X1YKxsZwa36xipOEKAsdtmaG6aAJoM1fx2zA==", "dev": true, + "license": "MIT", "dependencies": { "got": "^12.1.0", "registry-auth-token": "^5.0.1", @@ -5885,9 +5071,8 @@ }, "node_modules/package-json/node_modules/get-stream": { "version": "6.0.1", - "resolved": "https://registry.npmjs.org/get-stream/-/get-stream-6.0.1.tgz", - "integrity": "sha512-ts6Wi+2j3jQjqi70w5AlN8DFnkSwC+MqmxEzdEALB2qXZYV3X/b1CTfgPLGJNMeAWxdPfU8FO1ms3NUfaHCPYg==", "dev": true, + "license": "MIT", "engines": { "node": ">=10" }, @@ -5897,9 +5082,8 @@ }, "node_modules/package-json/node_modules/got": { "version": "12.6.1", - "resolved": "https://registry.npmjs.org/got/-/got-12.6.1.tgz", - "integrity": "sha512-mThBblvlAF1d4O5oqyvN+ZxLAYwIJK7bpMxgYqPD9okW0C3qm5FFn7k811QrcuEBwaogR3ngOFoCfs6mRv7teQ==", "dev": true, + "license": "MIT", "dependencies": { "@sindresorhus/is": "^5.2.0", "@szmarczak/http-timer": "^5.0.1", @@ -5922,9 +5106,8 @@ }, "node_modules/package-json/node_modules/lru-cache": { "version": "6.0.0", - "resolved": "https://registry.npmjs.org/lru-cache/-/lru-cache-6.0.0.tgz", - "integrity": "sha512-Jo6dJ04CmSjuznwJSS3pUeWmd/H0ffTlkXXgwZi+eq1UCmqQwCh+eLsYOYCwY991i2Fah4h1BEMCx4qThGbsiA==", "dev": true, + "license": "ISC", "dependencies": { "yallist": "^4.0.0" }, @@ -5934,9 +5117,8 @@ }, "node_modules/package-json/node_modules/semver": { "version": "7.5.4", - "resolved": "https://registry.npmjs.org/semver/-/semver-7.5.4.tgz", - "integrity": "sha512-1bCSESV6Pv+i21Hvpxp3Dx+pSD8lIPt8uVjRrxAUt/nbswYc+tK6Y2btiULjd4+fnq15PX+nqQDC7Oft7WkwcA==", "dev": true, + "license": "ISC", "dependencies": { "lru-cache": "^6.0.0" }, @@ -5949,15 +5131,13 @@ }, "node_modules/package-json/node_modules/yallist": { "version": "4.0.0", - "resolved": "https://registry.npmjs.org/yallist/-/yallist-4.0.0.tgz", - "integrity": "sha512-3wdGidZyq5PB084XLES5TpOSRA3wjXAlIWMhum2kRcv/41Sn2emQ0dycQW4uZXLejwKvg6EsvbdlVL+FYEct7A==", - "dev": true + "dev": true, + "license": "ISC" }, "node_modules/parent-module": { "version": "1.0.1", - "resolved": "https://registry.npmjs.org/parent-module/-/parent-module-1.0.1.tgz", - "integrity": "sha512-GQ2EWRpQV8/o+Aw8YqtfZZPfNRWZYkbidE9k5rpl/hC3vtHHBfGm2Ifi6qWV+coDGkrUKZAxE3Lot5kcsRlh+g==", "dev": true, + "license": "MIT", "dependencies": { "callsites": "^3.0.0" }, @@ -5967,9 +5147,8 @@ }, "node_modules/parse-json": { "version": "5.2.0", - "resolved": "https://registry.npmjs.org/parse-json/-/parse-json-5.2.0.tgz", - "integrity": "sha512-ayCKvm/phCGxOkYRSCM82iDwct8/EonSEgCSxWxD7ve6jHggsFl4fZVQBPRNgQoKiuV/odhFrGzQXZwbifC8Rg==", "dev": true, + "license": "MIT", "dependencies": { "@babel/code-frame": "^7.0.0", "error-ex": "^1.3.1", @@ -5985,66 +5164,58 @@ }, "node_modules/parse-path": { "version": "7.0.0", - "resolved": "https://registry.npmjs.org/parse-path/-/parse-path-7.0.0.tgz", - "integrity": "sha512-Euf9GG8WT9CdqwuWJGdf3RkUcTBArppHABkO7Lm8IzRQp0e2r/kkFnmhu4TSK30Wcu5rVAZLmfPKSBBi9tWFog==", "dev": true, + "license": "MIT", "dependencies": { "protocols": "^2.0.0" } }, "node_modules/parse-url": { "version": "8.1.0", - "resolved": "https://registry.npmjs.org/parse-url/-/parse-url-8.1.0.tgz", - "integrity": "sha512-xDvOoLU5XRrcOZvnI6b8zA6n9O9ejNk/GExuz1yBuWUGn9KA97GI6HTs6u02wKara1CeVmZhH+0TZFdWScR89w==", "dev": true, + "license": "MIT", "dependencies": { "parse-path": "^7.0.0" } }, "node_modules/path-exists": { "version": "4.0.0", - "resolved": "https://registry.npmjs.org/path-exists/-/path-exists-4.0.0.tgz", - "integrity": "sha512-ak9Qy5Q7jYb2Wwcey5Fpvg2KoAc/ZIhLSLOSBmRmygPsGwkVVt0fZa0qrtMz+m6tJTAHfZQ8FnmB4MG4LWy7/w==", "dev": true, + "license": "MIT", "engines": { "node": ">=8" } }, "node_modules/path-is-absolute": { "version": "1.0.1", - "resolved": "https://registry.npmjs.org/path-is-absolute/-/path-is-absolute-1.0.1.tgz", - "integrity": "sha512-AVbw3UJ2e9bq64vSaS9Am0fje1Pa8pbGqTTsmXfaIiMpnr5DlDhfJOuLj9Sf95ZPVDAUerDfEk88MPmPe7UCQg==", "dev": true, + "license": "MIT", "engines": { "node": ">=0.10.0" } }, "node_modules/path-key": { "version": "3.1.1", - "resolved": "https://registry.npmjs.org/path-key/-/path-key-3.1.1.tgz", - "integrity": "sha512-ojmeN0qd+y0jszEtoY48r0Peq5dwMEkIlCOu6Q5f41lfkswXuKtYrhgoTpLnyIcHm24Uhqx+5Tqm2InSwLhE6Q==", "dev": true, + "license": "MIT", "engines": { "node": ">=8" } }, "node_modules/path-parse": { "version": "1.0.7", - "resolved": "https://registry.npmjs.org/path-parse/-/path-parse-1.0.7.tgz", - "integrity": "sha512-LDJzPVEEEPR+y48z93A0Ed0yXb8pAByGWo/k5YYdYgpY2/2EsOsksJrq7lOHxryrVOn1ejG6oAp8ahvOIQD8sw==", - "dev": true + "dev": true, + "license": "MIT" }, "node_modules/path-to-regexp": { "version": "6.2.1", - "resolved": "https://registry.npmjs.org/path-to-regexp/-/path-to-regexp-6.2.1.tgz", - "integrity": "sha512-JLyh7xT1kizaEvcaXOQwOc2/Yhw6KZOvPf1S8401UyLk86CU79LN3vl7ztXGm/pZ+YjoyAJ4rxmHwbkBXJX+yw==", - "dev": true + "dev": true, + "license": "MIT" }, "node_modules/path-type": { "version": "5.0.0", - "resolved": "https://registry.npmjs.org/path-type/-/path-type-5.0.0.tgz", - "integrity": "sha512-5HviZNaZcfqP95rwpv+1HDgUamezbqdSYTyzjTvwtJSnIH+3vnbmWsItli8OFEndS984VT55M3jduxZbX351gg==", "dev": true, + "license": "MIT", "engines": { "node": ">=12" }, @@ -6054,15 +5225,13 @@ }, "node_modules/picocolors": { "version": "1.0.0", - "resolved": "https://registry.npmjs.org/picocolors/-/picocolors-1.0.0.tgz", - "integrity": "sha512-1fygroTLlHu66zi26VoTDv8yRgm0Fccecssto+MhsZ0D/DGW2sm8E8AjW7NU5VVTRt5GxbeZ5qBuJr+HyLYkjQ==", - "dev": true + "dev": true, + "license": "ISC" }, "node_modules/picomatch": { "version": "2.3.1", - "resolved": "https://registry.npmjs.org/picomatch/-/picomatch-2.3.1.tgz", - "integrity": "sha512-JU3teHTNjmE2VCGFzuY8EXzCDVwEqB2a8fsIvwaStHhAWJEeVd1o1QD80CU6+ZdEXXSLbSsuLwJjkCBWqRQUVA==", "dev": true, + "license": "MIT", "engines": { "node": ">=8.6" }, @@ -6072,27 +5241,24 @@ }, "node_modules/pify": { "version": "2.3.0", - "resolved": "https://registry.npmjs.org/pify/-/pify-2.3.0.tgz", - "integrity": "sha512-udgsAY+fTnvv7kI7aaxbqwWNb0AHiB0qBO89PZKPkoTmGOgdbrHDKD+0B2X4uTfJ/FT1R09r9gTsjUjNJotuog==", "dev": true, + "license": "MIT", "engines": { "node": ">=0.10.0" } }, "node_modules/pinkie": { "version": "2.0.4", - "resolved": "https://registry.npmjs.org/pinkie/-/pinkie-2.0.4.tgz", - "integrity": "sha512-MnUuEycAemtSaeFSjXKW/aroV7akBbY+Sv+RkyqFjgAe73F+MR0TBWKBRDkmfWq/HiFmdavfZ1G7h4SPZXaCSg==", "dev": true, + "license": "MIT", "engines": { "node": ">=0.10.0" } }, "node_modules/pinkie-promise": { "version": "2.0.1", - "resolved": "https://registry.npmjs.org/pinkie-promise/-/pinkie-promise-2.0.1.tgz", - "integrity": "sha512-0Gni6D4UcLTbv9c57DfxDGdr41XfgUjqWZu492f0cIGr16zDU06BWP/RAEvOuo7CQ0CNjHaLlM59YJJFm3NWlw==", "dev": true, + "license": "MIT", "dependencies": { "pinkie": "^2.0.0" }, @@ -6102,9 +5268,8 @@ }, "node_modules/pkg-dir": { "version": "4.2.0", - "resolved": "https://registry.npmjs.org/pkg-dir/-/pkg-dir-4.2.0.tgz", - "integrity": "sha512-HRDzbaKjC+AOWVXxAU/x54COGeIv9eb+6CkDSQoNTt4XyWoIJvuPsXizxu/Fr23EiekbtZwmh1IcIG/l/a10GQ==", "dev": true, + "license": "MIT", "dependencies": { "find-up": "^4.0.0" }, @@ -6114,9 +5279,8 @@ }, "node_modules/pkg-dir/node_modules/find-up": { "version": "4.1.0", - "resolved": "https://registry.npmjs.org/find-up/-/find-up-4.1.0.tgz", - "integrity": "sha512-PpOwAdQ/YlXQ2vj8a3h8IipDuYRi3wceVQQGYWxNINccq40Anw7BlsEXCMbt1Zt+OLA6Fq9suIpIWD0OsnISlw==", "dev": true, + "license": "MIT", "dependencies": { "locate-path": "^5.0.0", "path-exists": "^4.0.0" @@ -6127,9 +5291,8 @@ }, "node_modules/pkg-dir/node_modules/locate-path": { "version": "5.0.0", - "resolved": "https://registry.npmjs.org/locate-path/-/locate-path-5.0.0.tgz", - "integrity": "sha512-t7hw9pI+WvuwNJXwk5zVHpyhIqzg2qTlklJOf0mVxGSbe3Fp2VieZcduNYjaLDoy6p9uGpQEGWG87WpMKlNq8g==", "dev": true, + "license": "MIT", "dependencies": { "p-locate": "^4.1.0" }, @@ -6139,9 +5302,8 @@ }, "node_modules/pkg-dir/node_modules/p-limit": { "version": "2.3.0", - "resolved": "https://registry.npmjs.org/p-limit/-/p-limit-2.3.0.tgz", - "integrity": "sha512-//88mFWSJx8lxCzwdAABTJL2MyWB12+eIY7MDL2SqLmAkeKU9qxRvWuSyTjm3FUmpBEMuFfckAIqEaVGUDxb6w==", "dev": true, + "license": "MIT", "dependencies": { "p-try": "^2.0.0" }, @@ -6154,9 +5316,8 @@ }, "node_modules/pkg-dir/node_modules/p-locate": { "version": "4.1.0", - "resolved": "https://registry.npmjs.org/p-locate/-/p-locate-4.1.0.tgz", - "integrity": "sha512-R79ZZ/0wAxKGu3oYMlz8jy/kbhsNrS7SKZ7PxEHBgJ5+F2mtFW2fK2cOtBh1cHYkQsbzFV7I+EoRKe6Yt0oK7A==", "dev": true, + "license": "MIT", "dependencies": { "p-limit": "^2.2.0" }, @@ -6166,9 +5327,8 @@ }, "node_modules/process-on-spawn": { "version": "1.0.0", - "resolved": "https://registry.npmjs.org/process-on-spawn/-/process-on-spawn-1.0.0.tgz", - "integrity": "sha512-1WsPDsUSMmZH5LeMLegqkPDrsGgsWwk1Exipy2hvB0o/F0ASzbpIctSCcZIK1ykJvtTJULEH+20WOFjMvGnCTg==", "dev": true, + "license": "MIT", "dependencies": { "fromentries": "^1.2.0" }, @@ -6178,9 +5338,8 @@ }, "node_modules/promise.allsettled": { "version": "1.0.7", - "resolved": "https://registry.npmjs.org/promise.allsettled/-/promise.allsettled-1.0.7.tgz", - "integrity": "sha512-hezvKvQQmsFkOdrZfYxUxkyxl8mgFQeT259Ajj9PXdbg9VzBCWrItOev72JyWxkCD5VSSqAeHmlN3tWx4DlmsA==", "dev": true, + "license": "MIT", "dependencies": { "array.prototype.map": "^1.0.5", "call-bind": "^1.0.2", @@ -6198,21 +5357,18 @@ }, "node_modules/proto-list": { "version": "1.2.4", - "resolved": "https://registry.npmjs.org/proto-list/-/proto-list-1.2.4.tgz", - "integrity": "sha512-vtK/94akxsTMhe0/cbfpR+syPuszcuwhqVjJq26CuNDgFGj682oRBXOP5MJpv2r7JtE8MsiepGIqvvOTBwn2vA==", - "dev": true + "dev": true, + "license": "ISC" }, "node_modules/protocols": { "version": "2.0.1", - "resolved": "https://registry.npmjs.org/protocols/-/protocols-2.0.1.tgz", - "integrity": "sha512-/XJ368cyBJ7fzLMwLKv1e4vLxOju2MNAIokcr7meSaNcVbWz/CPcW22cP04mwxOErdA5mwjA8Q6w/cdAQxVn7Q==", - "dev": true + "dev": true, + "license": "MIT" }, "node_modules/proxy-agent": { "version": "6.3.1", - "resolved": "https://registry.npmjs.org/proxy-agent/-/proxy-agent-6.3.1.tgz", - "integrity": "sha512-Rb5RVBy1iyqOtNl15Cw/llpeLH8bsb37gM1FUfKQ+Wck6xHlbAhWGUFiTRHtkjqGTA5pSHz6+0hrPW/oECihPQ==", "dev": true, + "license": "MIT", "dependencies": { "agent-base": "^7.0.2", "debug": "^4.3.4", @@ -6229,24 +5385,21 @@ }, "node_modules/proxy-agent/node_modules/lru-cache": { "version": "7.18.3", - "resolved": "https://registry.npmjs.org/lru-cache/-/lru-cache-7.18.3.tgz", - "integrity": "sha512-jumlc0BIUrS3qJGgIkWZsyfAM7NCWiBcCDhnd+3NNM5KbBmLTgHVfWBcg6W+rLUsIpzpERPsvwUP7CckAQSOoA==", "dev": true, + "license": "ISC", "engines": { "node": ">=12" } }, "node_modules/proxy-from-env": { "version": "1.1.0", - "resolved": "https://registry.npmjs.org/proxy-from-env/-/proxy-from-env-1.1.0.tgz", - "integrity": "sha512-D+zkORCbA9f1tdWRK0RaCR3GPv50cMxcrz4X8k5LTSUD1Dkw47mKJEZQNunItRTkWwgtaUSo1RVFRIG9ZXiFYg==", - "dev": true + "dev": true, + "license": "MIT" }, "node_modules/pupa": { "version": "3.1.0", - "resolved": "https://registry.npmjs.org/pupa/-/pupa-3.1.0.tgz", - "integrity": "sha512-FLpr4flz5xZTSJxSeaheeMKN/EDzMdK7b8PTOC6a5PYFKTucWbdqjgqaEyH0shFiSJrVB1+Qqi4Tk19ccU6Aug==", "dev": true, + "license": "MIT", "dependencies": { "escape-goat": "^4.0.0" }, @@ -6259,8 +5412,6 @@ }, "node_modules/queue-microtask": { "version": "1.2.3", - "resolved": "https://registry.npmjs.org/queue-microtask/-/queue-microtask-1.2.3.tgz", - "integrity": "sha512-NuaNSa6flKT5JaSYQzJok04JzTL1CA6aGhv5rfLW3PgqA+M2ChpZQnAC8h8i4ZFkBS8X5RqkDBHA7r4hej3K9A==", "dev": true, "funding": [ { @@ -6275,13 +5426,13 @@ "type": "consulting", "url": "https://feross.org/support" } - ] + ], + "license": "MIT" }, "node_modules/quick-lru": { "version": "5.1.1", - "resolved": "https://registry.npmjs.org/quick-lru/-/quick-lru-5.1.1.tgz", - "integrity": "sha512-WuyALRjWPDGtt/wzJiadO5AXY+8hZ80hVpe6MyivgraREW751X3SbhRvG3eLKOYN+8VEvqLcf3wdnt44Z4S4SA==", "dev": true, + "license": "MIT", "engines": { "node": ">=10" }, @@ -6291,18 +5442,16 @@ }, "node_modules/randombytes": { "version": "2.1.0", - "resolved": "https://registry.npmjs.org/randombytes/-/randombytes-2.1.0.tgz", - "integrity": "sha512-vYl3iOX+4CKUWuxGi9Ukhie6fsqXqS9FE2Zaic4tNFD2N2QQaXOMFbuKK4QmDHC0JO6B1Zp41J0LpT0oR68amQ==", "dev": true, + "license": "MIT", "dependencies": { "safe-buffer": "^5.1.0" } }, "node_modules/rc": { "version": "1.2.8", - "resolved": "https://registry.npmjs.org/rc/-/rc-1.2.8.tgz", - "integrity": "sha512-y3bGgqKj3QBdxLbLkomlohkvsA8gdAiUQlSBJnBhfn+BPxg4bc62d8TcBW15wavDfgexCgccckhcZvywyQYPOw==", "dev": true, + "license": "(BSD-2-Clause OR MIT OR Apache-2.0)", "dependencies": { "deep-extend": "^0.6.0", "ini": "~1.3.0", @@ -6315,24 +5464,21 @@ }, "node_modules/rc/node_modules/ini": { "version": "1.3.8", - "resolved": "https://registry.npmjs.org/ini/-/ini-1.3.8.tgz", - "integrity": "sha512-JV/yugV2uzW5iMRSiZAyDtQd+nxtUnjeLt0acNdw98kKLrvuRVyB80tsREOE7yvGVgalhZ6RNXCmEHkUKBKxew==", - "dev": true + "dev": true, + "license": "ISC" }, "node_modules/rc/node_modules/strip-json-comments": { "version": "2.0.1", - "resolved": "https://registry.npmjs.org/strip-json-comments/-/strip-json-comments-2.0.1.tgz", - "integrity": "sha512-4gB8na07fecVVkOI6Rs4e7T6NOTki5EmL7TUduTs6bu3EdnSycntVJ4re8kgZA+wx9IueI2Y11bfbgwtzuE0KQ==", "dev": true, + "license": "MIT", "engines": { "node": ">=0.10.0" } }, "node_modules/readable-stream": { "version": "3.6.2", - "resolved": "https://registry.npmjs.org/readable-stream/-/readable-stream-3.6.2.tgz", - "integrity": "sha512-9u/sniCrY3D5WdsERHzHE4G2YCXqoG5FTHUiCC4SIbr6XcLZBY05ya9EKjYek9O5xOAwjGq+1JdGBAS7Q9ScoA==", "dev": true, + "license": "MIT", "dependencies": { "inherits": "^2.0.3", "string_decoder": "^1.1.1", @@ -6344,9 +5490,8 @@ }, "node_modules/readdirp": { "version": "3.6.0", - "resolved": "https://registry.npmjs.org/readdirp/-/readdirp-3.6.0.tgz", - "integrity": "sha512-hOS089on8RduqdbhvQ5Z37A0ESjsqz6qnRcffsMU3495FuTdqSm+7bhJ29JvIOsBDEEnan5DPu9t3To9VRlMzA==", "dev": true, + "license": "MIT", "dependencies": { "picomatch": "^2.2.1" }, @@ -6356,8 +5501,6 @@ }, "node_modules/rechoir": { "version": "0.6.2", - "resolved": "https://registry.npmjs.org/rechoir/-/rechoir-0.6.2.tgz", - "integrity": "sha512-HFM8rkZ+i3zrV+4LQjwQ0W+ez98pApMGM3HUrN04j3CqzPOzl9nmP15Y8YXNm8QHGv/eacOVEjqhmWpkRV0NAw==", "dev": true, "dependencies": { "resolve": "^1.1.6" @@ -6372,9 +5515,8 @@ }, "node_modules/regexp.prototype.flags": { "version": "1.5.1", - "resolved": "https://registry.npmjs.org/regexp.prototype.flags/-/regexp.prototype.flags-1.5.1.tgz", - "integrity": "sha512-sy6TXMN+hnP/wMy+ISxg3krXx7BAtWVO4UouuCN/ziM9UEne0euamVNafDfvC83bRNr95y0V5iijeDQFUNpvrg==", "dev": true, + "license": "MIT", "dependencies": { "call-bind": "^1.0.2", "define-properties": "^1.2.0", @@ -6389,9 +5531,8 @@ }, "node_modules/registry-auth-token": { "version": "5.0.2", - "resolved": "https://registry.npmjs.org/registry-auth-token/-/registry-auth-token-5.0.2.tgz", - "integrity": "sha512-o/3ikDxtXaA59BmZuZrJZDJv8NMDGSj+6j6XaeBmHw8eY1i1qd9+6H+LjVvQXx3HN6aRCGa1cUdJ9RaJZUugnQ==", "dev": true, + "license": "MIT", "dependencies": { "@pnpm/npm-conf": "^2.1.0" }, @@ -6401,9 +5542,8 @@ }, "node_modules/registry-url": { "version": "6.0.1", - "resolved": "https://registry.npmjs.org/registry-url/-/registry-url-6.0.1.tgz", - "integrity": "sha512-+crtS5QjFRqFCoQmvGduwYWEBng99ZvmFvF+cUJkGYF1L1BfU8C6Zp9T7f5vPAwyLkUExpvK+ANVZmGU49qi4Q==", "dev": true, + "license": "MIT", "dependencies": { "rc": "1.2.8" }, @@ -6416,8 +5556,6 @@ }, "node_modules/release-it": { "version": "17.0.3", - "resolved": "https://registry.npmjs.org/release-it/-/release-it-17.0.3.tgz", - "integrity": "sha512-QjTCmvQm91pwLEbvavEs9jofHNe8thsb9Uimin+8DNSwFRdUd73p0Owy2PP/Dzh/EegRkKq/o+4Pn1xp8pC1og==", "dev": true, "funding": [ { @@ -6429,6 +5567,7 @@ "url": "https://opencollective.com/webpro" } ], + "license": "MIT", "dependencies": { "@iarna/toml": "2.2.5", "@octokit/rest": "20.0.2", @@ -6467,9 +5606,8 @@ }, "node_modules/release-it/node_modules/chalk": { "version": "5.3.0", - "resolved": "https://registry.npmjs.org/chalk/-/chalk-5.3.0.tgz", - "integrity": "sha512-dLitG79d+GV1Nb/VYcCDFivJeK1hiukt9QjRNVOsUtTy1rR1YJsmpGGTZ3qJos+uw7WmWF4wUwBd9jxjocFC2w==", "dev": true, + "license": "MIT", "engines": { "node": "^12.17.0 || ^14.13 || >=16.0.0" }, @@ -6479,9 +5617,8 @@ }, "node_modules/release-it/node_modules/globby": { "version": "14.0.0", - "resolved": "https://registry.npmjs.org/globby/-/globby-14.0.0.tgz", - "integrity": "sha512-/1WM/LNHRAOH9lZta77uGbq0dAEQM+XjNesWwhlERDVenqothRbnzTrL3/LrIoEPPjeUHC3vrS6TwoyxeHs7MQ==", "dev": true, + "license": "MIT", "dependencies": { "@sindresorhus/merge-streams": "^1.0.0", "fast-glob": "^3.3.2", @@ -6499,9 +5636,8 @@ }, "node_modules/release-it/node_modules/lru-cache": { "version": "6.0.0", - "resolved": "https://registry.npmjs.org/lru-cache/-/lru-cache-6.0.0.tgz", - "integrity": "sha512-Jo6dJ04CmSjuznwJSS3pUeWmd/H0ffTlkXXgwZi+eq1UCmqQwCh+eLsYOYCwY991i2Fah4h1BEMCx4qThGbsiA==", "dev": true, + "license": "ISC", "dependencies": { "yallist": "^4.0.0" }, @@ -6511,9 +5647,8 @@ }, "node_modules/release-it/node_modules/semver": { "version": "7.5.4", - "resolved": "https://registry.npmjs.org/semver/-/semver-7.5.4.tgz", - "integrity": "sha512-1bCSESV6Pv+i21Hvpxp3Dx+pSD8lIPt8uVjRrxAUt/nbswYc+tK6Y2btiULjd4+fnq15PX+nqQDC7Oft7WkwcA==", "dev": true, + "license": "ISC", "dependencies": { "lru-cache": "^6.0.0" }, @@ -6526,24 +5661,21 @@ }, "node_modules/release-it/node_modules/yallist": { "version": "4.0.0", - "resolved": "https://registry.npmjs.org/yallist/-/yallist-4.0.0.tgz", - "integrity": "sha512-3wdGidZyq5PB084XLES5TpOSRA3wjXAlIWMhum2kRcv/41Sn2emQ0dycQW4uZXLejwKvg6EsvbdlVL+FYEct7A==", - "dev": true + "dev": true, + "license": "ISC" }, "node_modules/release-it/node_modules/yargs-parser": { "version": "21.1.1", - "resolved": "https://registry.npmjs.org/yargs-parser/-/yargs-parser-21.1.1.tgz", - "integrity": "sha512-tVpsJW7DdjecAiFpbIB1e3qxIQsE6NoPc5/eTdrbbIC4h0LVsWhnoa3g+m2HclBIujHzsxZ4VJVA+GUuc2/LBw==", "dev": true, + "license": "ISC", "engines": { "node": ">=12" } }, "node_modules/release-zalgo": { "version": "1.0.0", - "resolved": "https://registry.npmjs.org/release-zalgo/-/release-zalgo-1.0.0.tgz", - "integrity": "sha512-gUAyHVHPPC5wdqX/LG4LWtRYtgjxyX78oanFNTMMyFEfOqdC54s3eE82imuWKbOeqYht2CrNf64Qb8vgmmtZGA==", "dev": true, + "license": "ISC", "dependencies": { "es6-error": "^4.0.1" }, @@ -6553,24 +5685,21 @@ }, "node_modules/require-directory": { "version": "2.1.1", - "resolved": "https://registry.npmjs.org/require-directory/-/require-directory-2.1.1.tgz", - "integrity": "sha512-fGxEI7+wsG9xrvdjsrlmL22OMTTiHRwAMroiEeMgq8gzoLC/PQr7RsRDSTLUg/bZAZtF+TVIkHc6/4RIKrui+Q==", "dev": true, + "license": "MIT", "engines": { "node": ">=0.10.0" } }, "node_modules/require-main-filename": { "version": "2.0.0", - "resolved": "https://registry.npmjs.org/require-main-filename/-/require-main-filename-2.0.0.tgz", - "integrity": "sha512-NKN5kMDylKuldxYLSUfrbo5Tuzh4hd+2E8NPPX02mZtn1VuREQToYe/ZdlJy+J3uCpfaiGF05e7B8W0iXbQHmg==", - "dev": true + "dev": true, + "license": "ISC" }, "node_modules/resolve": { "version": "1.22.8", - "resolved": "https://registry.npmjs.org/resolve/-/resolve-1.22.8.tgz", - "integrity": "sha512-oKWePCxqpd6FlLvGV1VU0x7bkPmmCNolxzjMf4NczoDnQcIWrAF+cPtZn5i6n+RfD2d9i0tzpKnG6Yk168yIyw==", "dev": true, + "license": "MIT", "dependencies": { "is-core-module": "^2.13.0", "path-parse": "^1.0.7", @@ -6585,33 +5714,29 @@ }, "node_modules/resolve-alpn": { "version": "1.2.1", - "resolved": "https://registry.npmjs.org/resolve-alpn/-/resolve-alpn-1.2.1.tgz", - "integrity": "sha512-0a1F4l73/ZFZOakJnQ3FvkJ2+gSTQWz/r2KE5OdDY0TxPm5h4GkqkWWfM47T7HsbnOtcJVEF4epCVy6u7Q3K+g==", - "dev": true + "dev": true, + "license": "MIT" }, "node_modules/resolve-from": { "version": "5.0.0", - "resolved": "https://registry.npmjs.org/resolve-from/-/resolve-from-5.0.0.tgz", - "integrity": "sha512-qYg9KP24dD5qka9J47d0aVky0N+b4fTU89LN9iDnjB5waksiC49rvMB0PrUJQGoTmH50XPiqOvAjDfaijGxYZw==", "dev": true, + "license": "MIT", "engines": { "node": ">=8" } }, "node_modules/resolve-pkg-maps": { "version": "1.0.0", - "resolved": "https://registry.npmjs.org/resolve-pkg-maps/-/resolve-pkg-maps-1.0.0.tgz", - "integrity": "sha512-seS2Tj26TBVOC2NIc2rOe2y2ZO7efxITtLZcGSOnHHNOQ7CkiUBfw0Iw2ck6xkIhPwLhKNLS8BO+hEpngQlqzw==", "dev": true, + "license": "MIT", "funding": { "url": "https://github.com/privatenumber/resolve-pkg-maps?sponsor=1" } }, "node_modules/responselike": { "version": "3.0.0", - "resolved": "https://registry.npmjs.org/responselike/-/responselike-3.0.0.tgz", - "integrity": "sha512-40yHxbNcl2+rzXvZuVkrYohathsSJlMTXKryG5y8uciHv1+xDLHQpgjG64JUO9nrEq2jGLH6IZ8BcZyw3wrweg==", "dev": true, + "license": "MIT", "dependencies": { "lowercase-keys": "^3.0.0" }, @@ -6624,9 +5749,8 @@ }, "node_modules/restore-cursor": { "version": "3.1.0", - "resolved": "https://registry.npmjs.org/restore-cursor/-/restore-cursor-3.1.0.tgz", - "integrity": "sha512-l+sSefzHpj5qimhFSE5a8nufZYAM3sBSVMAPtYkmC+4EH2anSGaEMXSD0izRQbu9nfyQ9y5JrVmp7E8oZrUjvA==", "dev": true, + "license": "MIT", "dependencies": { "onetime": "^5.1.0", "signal-exit": "^3.0.2" @@ -6637,18 +5761,16 @@ }, "node_modules/restore-cursor/node_modules/mimic-fn": { "version": "2.1.0", - "resolved": "https://registry.npmjs.org/mimic-fn/-/mimic-fn-2.1.0.tgz", - "integrity": "sha512-OqbOk5oEQeAZ8WXWydlu9HJjz9WVdEIvamMCcXmuqUYjTknH/sqsWvhQ3vgwKFRR1HpjvNBKQ37nbJgYzGqGcg==", "dev": true, + "license": "MIT", "engines": { "node": ">=6" } }, "node_modules/restore-cursor/node_modules/onetime": { "version": "5.1.2", - "resolved": "https://registry.npmjs.org/onetime/-/onetime-5.1.2.tgz", - "integrity": "sha512-kbpaSSGJTWdAY5KPVeMOKXSrPtr8C8C7wodJbcsd51jRnmD+GZu8Y0VoU6Dm5Z4vWr0Ig/1NKuWRKf7j5aaYSg==", "dev": true, + "license": "MIT", "dependencies": { "mimic-fn": "^2.1.0" }, @@ -6661,18 +5783,16 @@ }, "node_modules/retry": { "version": "0.13.1", - "resolved": "https://registry.npmjs.org/retry/-/retry-0.13.1.tgz", - "integrity": "sha512-XQBQ3I8W1Cge0Seh+6gjj03LbmRFWuoszgK9ooCpwYIrhhoO80pfq4cUkU5DkknwfOfFteRwlZ56PYOGYyFWdg==", "dev": true, + "license": "MIT", "engines": { "node": ">= 4" } }, "node_modules/reusify": { "version": "1.0.4", - "resolved": "https://registry.npmjs.org/reusify/-/reusify-1.0.4.tgz", - "integrity": "sha512-U9nH88a3fc/ekCF1l0/UP1IosiuIjyTh7hBvXVMHYgVcfGvt897Xguj2UOLDeI5BG2m7/uwyaLVT6fbtCwTyzw==", "dev": true, + "license": "MIT", "engines": { "iojs": ">=1.0.0", "node": ">=0.10.0" @@ -6680,9 +5800,8 @@ }, "node_modules/rimraf": { "version": "3.0.2", - "resolved": "https://registry.npmjs.org/rimraf/-/rimraf-3.0.2.tgz", - "integrity": "sha512-JZkJMZkAGFFPP2YqXZXPbMlMBgsxzE8ILs4lMIX/2o0L9UBw9O/Y3o6wFw/i9YLapcUJWwqbi3kdxIPdC62TIA==", "dev": true, + "license": "ISC", "dependencies": { "glob": "^7.1.3" }, @@ -6695,9 +5814,8 @@ }, "node_modules/run-applescript": { "version": "7.0.0", - "resolved": "https://registry.npmjs.org/run-applescript/-/run-applescript-7.0.0.tgz", - "integrity": "sha512-9by4Ij99JUr/MCFBUkDKLWK3G9HVXmabKz9U5MlIAIuvuzkiOicRYs8XJLxX+xahD+mLiiCYDqF9dKAgtzKP1A==", "dev": true, + "license": "MIT", "engines": { "node": ">=18" }, @@ -6707,17 +5825,14 @@ }, "node_modules/run-async": { "version": "3.0.0", - "resolved": "https://registry.npmjs.org/run-async/-/run-async-3.0.0.tgz", - "integrity": "sha512-540WwVDOMxA6dN6We19EcT9sc3hkXPw5mzRNGM3FkdN/vtE9NFvj5lFAPNwUDmJjXidm3v7TC1cTE7t17Ulm1Q==", "dev": true, + "license": "MIT", "engines": { "node": ">=0.12.0" } }, "node_modules/run-parallel": { "version": "1.2.0", - "resolved": "https://registry.npmjs.org/run-parallel/-/run-parallel-1.2.0.tgz", - "integrity": "sha512-5l4VyZR86LZ/lDxZTR6jqL8AFE2S0IFLMP26AbjsLVADxHdhB/c0GUsH+y39UfCi3dzz8OlQuPmnaJOMoDHQBA==", "dev": true, "funding": [ { @@ -6733,24 +5848,23 @@ "url": "https://feross.org/support" } ], + "license": "MIT", "dependencies": { "queue-microtask": "^1.2.2" } }, "node_modules/rxjs": { "version": "7.8.1", - "resolved": "https://registry.npmjs.org/rxjs/-/rxjs-7.8.1.tgz", - "integrity": "sha512-AA3TVj+0A2iuIoQkWEK/tqFjBq2j+6PO6Y0zJcvzLAFhEFIO3HL0vls9hWLncZbAAbK0mar7oZ4V079I/qPMxg==", "dev": true, + "license": "Apache-2.0", "dependencies": { "tslib": "^2.1.0" } }, "node_modules/safe-array-concat": { "version": "1.1.0", - "resolved": "https://registry.npmjs.org/safe-array-concat/-/safe-array-concat-1.1.0.tgz", - "integrity": "sha512-ZdQ0Jeb9Ofti4hbt5lX3T2JcAamT9hfzYU1MNB+z/jaEbB6wfFfPIR/zEORmZqobkCCJhSjodobH6WHNmJ97dg==", "dev": true, + "license": "MIT", "dependencies": { "call-bind": "^1.0.5", "get-intrinsic": "^1.2.2", @@ -6766,8 +5880,6 @@ }, "node_modules/safe-buffer": { "version": "5.2.1", - "resolved": "https://registry.npmjs.org/safe-buffer/-/safe-buffer-5.2.1.tgz", - "integrity": "sha512-rp3So07KcdmmKbGvgaNxQSJr7bGVSVk5S9Eq1F+ppbRo70+YeaDxkw5Dd8NPN+GD6bjnYm2VuPuCXmpuYvmCXQ==", "dev": true, "funding": [ { @@ -6782,13 +5894,13 @@ "type": "consulting", "url": "https://feross.org/support" } - ] + ], + "license": "MIT" }, "node_modules/safe-regex-test": { "version": "1.0.2", - "resolved": "https://registry.npmjs.org/safe-regex-test/-/safe-regex-test-1.0.2.tgz", - "integrity": "sha512-83S9w6eFq12BBIJYvjMux6/dkirb8+4zJRA9cxNBVb7Wq5fJBW+Xze48WqR8pxua7bDuAaaAxtVVd4Idjp1dBQ==", "dev": true, + "license": "MIT", "dependencies": { "call-bind": "^1.0.5", "get-intrinsic": "^1.2.2", @@ -6803,24 +5915,21 @@ }, "node_modules/safer-buffer": { "version": "2.1.2", - "resolved": "https://registry.npmjs.org/safer-buffer/-/safer-buffer-2.1.2.tgz", - "integrity": "sha512-YZo3K82SD7Riyi0E1EQPojLz7kpepnSQI9IyPbHHg1XXXevb5dJI7tpyN2ADxGcQbHG7vcyRHk0cbwqcQriUtg==", - "dev": true + "dev": true, + "license": "MIT" }, "node_modules/semver": { "version": "6.3.1", - "resolved": "https://registry.npmjs.org/semver/-/semver-6.3.1.tgz", - "integrity": "sha512-BR7VvDCVHO+q2xBEWskxS6DJE1qRnb7DxzUrogb71CWoSficBxYsiAGd+Kl0mmq/MprG9yArRkyrQxTO6XjMzA==", "dev": true, + "license": "ISC", "bin": { "semver": "bin/semver.js" } }, "node_modules/semver-diff": { "version": "4.0.0", - "resolved": "https://registry.npmjs.org/semver-diff/-/semver-diff-4.0.0.tgz", - "integrity": "sha512-0Ju4+6A8iOnpL/Thra7dZsSlOHYAHIeMxfhWQRI1/VLcT3WDBZKKtQt/QkBOsiIN9ZpuvHE6cGZ0x4glCMmfiA==", "dev": true, + "license": "MIT", "dependencies": { "semver": "^7.3.5" }, @@ -6833,9 +5942,8 @@ }, "node_modules/semver-diff/node_modules/lru-cache": { "version": "6.0.0", - "resolved": "https://registry.npmjs.org/lru-cache/-/lru-cache-6.0.0.tgz", - "integrity": "sha512-Jo6dJ04CmSjuznwJSS3pUeWmd/H0ffTlkXXgwZi+eq1UCmqQwCh+eLsYOYCwY991i2Fah4h1BEMCx4qThGbsiA==", "dev": true, + "license": "ISC", "dependencies": { "yallist": "^4.0.0" }, @@ -6845,9 +5953,8 @@ }, "node_modules/semver-diff/node_modules/semver": { "version": "7.5.4", - "resolved": "https://registry.npmjs.org/semver/-/semver-7.5.4.tgz", - "integrity": "sha512-1bCSESV6Pv+i21Hvpxp3Dx+pSD8lIPt8uVjRrxAUt/nbswYc+tK6Y2btiULjd4+fnq15PX+nqQDC7Oft7WkwcA==", "dev": true, + "license": "ISC", "dependencies": { "lru-cache": "^6.0.0" }, @@ -6860,30 +5967,26 @@ }, "node_modules/semver-diff/node_modules/yallist": { "version": "4.0.0", - "resolved": "https://registry.npmjs.org/yallist/-/yallist-4.0.0.tgz", - "integrity": "sha512-3wdGidZyq5PB084XLES5TpOSRA3wjXAlIWMhum2kRcv/41Sn2emQ0dycQW4uZXLejwKvg6EsvbdlVL+FYEct7A==", - "dev": true + "dev": true, + "license": "ISC" }, "node_modules/serialize-javascript": { "version": "6.0.0", - "resolved": "https://registry.npmjs.org/serialize-javascript/-/serialize-javascript-6.0.0.tgz", - "integrity": "sha512-Qr3TosvguFt8ePWqsvRfrKyQXIiW+nGbYpy8XK24NQHE83caxWt+mIymTT19DGFbNWNLfEwsrkSmN64lVWB9ag==", "dev": true, + "license": "BSD-3-Clause", "dependencies": { "randombytes": "^2.1.0" } }, "node_modules/set-blocking": { "version": "2.0.0", - "resolved": "https://registry.npmjs.org/set-blocking/-/set-blocking-2.0.0.tgz", - "integrity": "sha512-KiKBS8AnWGEyLzofFfmvKwpdPzqiy16LvQfK3yv/fVH7Bj13/wl3JSR1J+rfgRE9q7xUJK4qvgS8raSOeLUehw==", - "dev": true + "dev": true, + "license": "ISC" }, "node_modules/set-function-length": { "version": "1.2.0", - "resolved": "https://registry.npmjs.org/set-function-length/-/set-function-length-1.2.0.tgz", - "integrity": "sha512-4DBHDoyHlM1IRPGYcoxexgh67y4ueR53FKV1yyxwFMY7aCqcN/38M1+SwZ/qJQ8iLv7+ck385ot4CcisOAPT9w==", "dev": true, + "license": "MIT", "dependencies": { "define-data-property": "^1.1.1", "function-bind": "^1.1.2", @@ -6897,9 +6000,8 @@ }, "node_modules/set-function-name": { "version": "2.0.1", - "resolved": "https://registry.npmjs.org/set-function-name/-/set-function-name-2.0.1.tgz", - "integrity": "sha512-tMNCiqYVkXIZgc2Hnoy2IvC/f8ezc5koaRFkCjrpWzGpCd3qbZXPzVy9MAZzK1ch/X0jvSkojys3oqJN0qCmdA==", "dev": true, + "license": "MIT", "dependencies": { "define-data-property": "^1.0.1", "functions-have-names": "^1.2.3", @@ -6911,9 +6013,8 @@ }, "node_modules/shebang-command": { "version": "2.0.0", - "resolved": "https://registry.npmjs.org/shebang-command/-/shebang-command-2.0.0.tgz", - "integrity": "sha512-kHxr2zZpYtdmrN1qDjrrX/Z1rR1kG8Dx+gkpK1G4eXmvXswmcE1hTWBWYUzlraYw1/yZp6YuDY77YtvbN0dmDA==", "dev": true, + "license": "MIT", "dependencies": { "shebang-regex": "^3.0.0" }, @@ -6923,18 +6024,16 @@ }, "node_modules/shebang-regex": { "version": "3.0.0", - "resolved": "https://registry.npmjs.org/shebang-regex/-/shebang-regex-3.0.0.tgz", - "integrity": "sha512-7++dFhtcx3353uBaq8DDR4NuxBetBzC7ZQOhmTQInHEd6bSrXdiEyzCvG07Z44UYdLShWUyXt5M/yhz8ekcb1A==", "dev": true, + "license": "MIT", "engines": { "node": ">=8" } }, "node_modules/shelljs": { "version": "0.8.5", - "resolved": "https://registry.npmjs.org/shelljs/-/shelljs-0.8.5.tgz", - "integrity": "sha512-TiwcRcrkhHvbrZbnRcFYMLl30Dfov3HKqzp5tO5b4pt6G/SezKcYhmDg15zXVBswHmctSAQKznqNW2LO5tTDow==", "dev": true, + "license": "BSD-3-Clause", "dependencies": { "glob": "^7.0.0", "interpret": "^1.0.0", @@ -6949,9 +6048,8 @@ }, "node_modules/shiki": { "version": "0.14.7", - "resolved": "https://registry.npmjs.org/shiki/-/shiki-0.14.7.tgz", - "integrity": "sha512-dNPAPrxSc87ua2sKJ3H5dQ/6ZaY8RNnaAqK+t0eG7p0Soi2ydiqbGOTaZCqaYvA/uZYfS1LJnemt3Q+mSfcPCg==", "dev": true, + "license": "MIT", "dependencies": { "ansi-sequence-parser": "^1.1.0", "jsonc-parser": "^3.2.0", @@ -6961,9 +6059,8 @@ }, "node_modules/side-channel": { "version": "1.0.4", - "resolved": "https://registry.npmjs.org/side-channel/-/side-channel-1.0.4.tgz", - "integrity": "sha512-q5XPytqFEIKHkGdiMIrY10mvLRvnQh42/+GoBlFW3b2LXLE2xxJpZFdm94we0BaoV3RwJyGqg5wS7epxTv0Zvw==", "dev": true, + "license": "MIT", "dependencies": { "call-bind": "^1.0.0", "get-intrinsic": "^1.0.2", @@ -6975,15 +6072,13 @@ }, "node_modules/signal-exit": { "version": "3.0.7", - "resolved": "https://registry.npmjs.org/signal-exit/-/signal-exit-3.0.7.tgz", - "integrity": "sha512-wnD2ZE+l+SPC/uoS0vXeE9L1+0wuaMqKlfz9AMUo38JsyLSBWSFcHR1Rri62LZc12vLr1gb3jl7iwQhgwpAbGQ==", - "dev": true + "dev": true, + "license": "ISC" }, "node_modules/sinon": { "version": "17.0.1", - "resolved": "https://registry.npmjs.org/sinon/-/sinon-17.0.1.tgz", - "integrity": "sha512-wmwE19Lie0MLT+ZYNpDymasPHUKTaZHUH/pKEubRXIzySv9Atnlw+BUMGCzWgV7b7wO+Hw6f1TEOr0IUnmU8/g==", "dev": true, + "license": "BSD-3-Clause", "dependencies": { "@sinonjs/commons": "^3.0.0", "@sinonjs/fake-timers": "^11.2.2", @@ -6999,18 +6094,16 @@ }, "node_modules/sinon/node_modules/diff": { "version": "5.1.0", - "resolved": "https://registry.npmjs.org/diff/-/diff-5.1.0.tgz", - "integrity": "sha512-D+mk+qE8VC/PAUrlAU34N+VfXev0ghe5ywmpqrawphmVZc1bEfn56uo9qpyGp1p4xpzOHkSW4ztBd6L7Xx4ACw==", "dev": true, + "license": "BSD-3-Clause", "engines": { "node": ">=0.3.1" } }, "node_modules/sinon/node_modules/supports-color": { "version": "7.2.0", - "resolved": "https://registry.npmjs.org/supports-color/-/supports-color-7.2.0.tgz", - "integrity": "sha512-qpCAvRl9stuOHveKsn7HncJRvv501qIacKzQlO/+Lwxc9+0q2wLyv4Dfvt80/DPn2pqOBsJdDiogXGR9+OvwRw==", "dev": true, + "license": "MIT", "dependencies": { "has-flag": "^4.0.0" }, @@ -7020,9 +6113,8 @@ }, "node_modules/slash": { "version": "5.1.0", - "resolved": "https://registry.npmjs.org/slash/-/slash-5.1.0.tgz", - "integrity": "sha512-ZA6oR3T/pEyuqwMgAKT0/hAv8oAXckzbkmR0UkUosQ+Mc4RxGoJkRmwHgHufaenlyAgE1Mxgpdcrf75y6XcnDg==", "dev": true, + "license": "MIT", "engines": { "node": ">=14.16" }, @@ -7032,9 +6124,8 @@ }, "node_modules/smart-buffer": { "version": "4.2.0", - "resolved": "https://registry.npmjs.org/smart-buffer/-/smart-buffer-4.2.0.tgz", - "integrity": "sha512-94hK0Hh8rPqQl2xXc3HsaBoOXKV20MToPkcXvwbISWLEs+64sBq5kFgn2kJDHb1Pry9yrP0dxrCI9RRci7RXKg==", "dev": true, + "license": "MIT", "engines": { "node": ">= 6.0.0", "npm": ">= 3.0.0" @@ -7042,9 +6133,8 @@ }, "node_modules/socks": { "version": "2.7.1", - "resolved": "https://registry.npmjs.org/socks/-/socks-2.7.1.tgz", - "integrity": "sha512-7maUZy1N7uo6+WVEX6psASxtNlKaNVMlGQKkG/63nEDdLOWNbiUMoLK7X4uYoLhQstau72mLgfEWcXcwsaHbYQ==", "dev": true, + "license": "MIT", "dependencies": { "ip": "^2.0.0", "smart-buffer": "^4.2.0" @@ -7056,9 +6146,8 @@ }, "node_modules/socks-proxy-agent": { "version": "8.0.2", - "resolved": "https://registry.npmjs.org/socks-proxy-agent/-/socks-proxy-agent-8.0.2.tgz", - "integrity": "sha512-8zuqoLv1aP/66PHF5TqwJ7Czm3Yv32urJQHrVyhD7mmA6d61Zv8cIXQYPTWwmg6qlupnPvs/QKDmfa4P/qct2g==", "dev": true, + "license": "MIT", "dependencies": { "agent-base": "^7.0.2", "debug": "^4.3.4", @@ -7070,24 +6159,21 @@ }, "node_modules/socks/node_modules/ip": { "version": "2.0.0", - "resolved": "https://registry.npmjs.org/ip/-/ip-2.0.0.tgz", - "integrity": "sha512-WKa+XuLG1A1R0UWhl2+1XQSi+fZWMsYKffMZTTYsiZaUD8k2yDAj5atimTUD2TZkyCkNEeYE5NhFZmupOGtjYQ==", - "dev": true + "dev": true, + "license": "MIT" }, "node_modules/source-map": { "version": "0.6.1", - "resolved": "https://registry.npmjs.org/source-map/-/source-map-0.6.1.tgz", - "integrity": "sha512-UjgapumWlbMhkBgzT7Ykc5YXUT46F0iKu8SGXq0bcwP5dz/h0Plj6enJqjz1Zbq2l5WaqYnrVbwWOWMyF3F47g==", "dev": true, + "license": "BSD-3-Clause", "engines": { "node": ">=0.10.0" } }, "node_modules/spawn-wrap": { "version": "2.0.0", - "resolved": "https://registry.npmjs.org/spawn-wrap/-/spawn-wrap-2.0.0.tgz", - "integrity": "sha512-EeajNjfN9zMnULLwhZZQU3GWBoFNkbngTUPfaawT4RkMiviTxcX0qfhVbGey39mfctfDHkWtuecgQ8NJcyQWHg==", "dev": true, + "license": "ISC", "dependencies": { "foreground-child": "^2.0.0", "is-windows": "^1.0.2", @@ -7102,15 +6188,13 @@ }, "node_modules/sprintf-js": { "version": "1.0.3", - "resolved": "https://registry.npmjs.org/sprintf-js/-/sprintf-js-1.0.3.tgz", - "integrity": "sha512-D9cPgkvLlV3t3IzL0D0YLvGA9Ahk4PcvVwUbN0dSGr1aP0Nrt4AEnTUbuGvquEC0mA64Gqt1fzirlRs5ibXx8g==", - "dev": true + "dev": true, + "license": "BSD-3-Clause" }, "node_modules/stdin-discarder": { "version": "0.2.2", - "resolved": "https://registry.npmjs.org/stdin-discarder/-/stdin-discarder-0.2.2.tgz", - "integrity": "sha512-UhDfHmA92YAlNnCfhmq0VeNL5bDbiZGg7sZ2IvPsXubGkiNa9EC+tUTsjBRsYUAz87btI6/1wf4XoVvQ3uRnmQ==", "dev": true, + "license": "MIT", "engines": { "node": ">=18" }, @@ -7120,9 +6204,8 @@ }, "node_modules/stop-iteration-iterator": { "version": "1.0.0", - "resolved": "https://registry.npmjs.org/stop-iteration-iterator/-/stop-iteration-iterator-1.0.0.tgz", - "integrity": "sha512-iCGQj+0l0HOdZ2AEeBADlsRC+vsnDsZsbdSiH1yNSjcfKM7fdpCMfqAL/dwF5BLiw/XhRft/Wax6zQbhq2BcjQ==", "dev": true, + "license": "MIT", "dependencies": { "internal-slot": "^1.0.4" }, @@ -7132,18 +6215,16 @@ }, "node_modules/string_decoder": { "version": "1.3.0", - "resolved": "https://registry.npmjs.org/string_decoder/-/string_decoder-1.3.0.tgz", - "integrity": "sha512-hkRX8U1WjJFd8LsDJ2yQ/wWWxaopEsABU1XfkM8A+j0+85JAGppt16cr1Whg6KIbb4okU6Mql6BOj+uup/wKeA==", "dev": true, + "license": "MIT", "dependencies": { "safe-buffer": "~5.2.0" } }, "node_modules/string-width": { "version": "4.2.3", - "resolved": "https://registry.npmjs.org/string-width/-/string-width-4.2.3.tgz", - "integrity": "sha512-wKyQRQpjJ0sIp62ErSZdGsjMJWsap5oRNihHhu6G7JVO/9jIB6UyevL+tXuOqrng8j/cxKTWyWUwvSTriiZz/g==", "dev": true, + "license": "MIT", "dependencies": { "emoji-regex": "^8.0.0", "is-fullwidth-code-point": "^3.0.0", @@ -7155,9 +6236,8 @@ }, "node_modules/string.prototype.trim": { "version": "1.2.8", - "resolved": "https://registry.npmjs.org/string.prototype.trim/-/string.prototype.trim-1.2.8.tgz", - "integrity": "sha512-lfjY4HcixfQXOfaqCvcBuOIapyaroTXhbkfJN3gcB1OtyupngWK4sEET9Knd0cXd28kTUqu/kHoV4HKSJdnjiQ==", "dev": true, + "license": "MIT", "dependencies": { "call-bind": "^1.0.2", "define-properties": "^1.2.0", @@ -7172,9 +6252,8 @@ }, "node_modules/string.prototype.trimend": { "version": "1.0.7", - "resolved": "https://registry.npmjs.org/string.prototype.trimend/-/string.prototype.trimend-1.0.7.tgz", - "integrity": "sha512-Ni79DqeB72ZFq1uH/L6zJ+DKZTkOtPIHovb3YZHQViE+HDouuU4mBrLOLDn5Dde3RF8qw5qVETEjhu9locMLvA==", "dev": true, + "license": "MIT", "dependencies": { "call-bind": "^1.0.2", "define-properties": "^1.2.0", @@ -7186,9 +6265,8 @@ }, "node_modules/string.prototype.trimstart": { "version": "1.0.7", - "resolved": "https://registry.npmjs.org/string.prototype.trimstart/-/string.prototype.trimstart-1.0.7.tgz", - "integrity": "sha512-NGhtDFu3jCEm7B4Fy0DpLewdJQOZcQ0rGbwQ/+stjnrp2i+rlKeCvos9hOIeCmqwratM47OBxY7uFZzjxHXmrg==", "dev": true, + "license": "MIT", "dependencies": { "call-bind": "^1.0.2", "define-properties": "^1.2.0", @@ -7200,9 +6278,8 @@ }, "node_modules/strip-ansi": { "version": "6.0.1", - "resolved": "https://registry.npmjs.org/strip-ansi/-/strip-ansi-6.0.1.tgz", - "integrity": "sha512-Y38VPSHcqkFrCpFnQ9vuSXmquuv5oXOKpGeT6aGrr3o3Gc9AlVa6JBfUSOCnbxGGZF+/0ooI7KrPuUSztUdU5A==", "dev": true, + "license": "MIT", "dependencies": { "ansi-regex": "^5.0.1" }, @@ -7212,18 +6289,16 @@ }, "node_modules/strip-bom": { "version": "4.0.0", - "resolved": "https://registry.npmjs.org/strip-bom/-/strip-bom-4.0.0.tgz", - "integrity": "sha512-3xurFv5tEgii33Zi8Jtp55wEIILR9eh34FAW00PZf+JnSsTmV/ioewSgQl97JHvgjoRGwPShsWm+IdrxB35d0w==", "dev": true, + "license": "MIT", "engines": { "node": ">=8" } }, "node_modules/strip-final-newline": { "version": "3.0.0", - "resolved": "https://registry.npmjs.org/strip-final-newline/-/strip-final-newline-3.0.0.tgz", - "integrity": "sha512-dOESqjYr96iWYylGObzd39EuNTa5VJxyvVAEm5Jnh7KGo75V43Hk1odPQkNDyXNmUR6k+gEiDVXnjB8HJ3crXw==", "dev": true, + "license": "MIT", "engines": { "node": ">=12" }, @@ -7233,9 +6308,8 @@ }, "node_modules/strip-json-comments": { "version": "3.1.1", - "resolved": "https://registry.npmjs.org/strip-json-comments/-/strip-json-comments-3.1.1.tgz", - "integrity": "sha512-6fPc+R4ihwqP6N/aIv2f1gMH8lOVtWQHoqC4yK6oSDVVocumAsfCqjkXnqiYMhmMwS/mEHLp7Vehlt3ql6lEig==", "dev": true, + "license": "MIT", "engines": { "node": ">=8" }, @@ -7245,9 +6319,8 @@ }, "node_modules/strip-outer": { "version": "1.0.1", - "resolved": "https://registry.npmjs.org/strip-outer/-/strip-outer-1.0.1.tgz", - "integrity": "sha512-k55yxKHwaXnpYGsOzg4Vl8+tDrWylxDEpknGjhTiZB8dFRU5rTo9CAzeycivxV3s+zlTKwrs6WxMxR95n26kwg==", "dev": true, + "license": "MIT", "dependencies": { "escape-string-regexp": "^1.0.2" }, @@ -7257,18 +6330,16 @@ }, "node_modules/strip-outer/node_modules/escape-string-regexp": { "version": "1.0.5", - "resolved": "https://registry.npmjs.org/escape-string-regexp/-/escape-string-regexp-1.0.5.tgz", - "integrity": "sha512-vbRorB5FUQWvla16U8R/qgaFIya2qGzwDrNmCZuYKrbdSUMG6I1ZCGQRefkRVhuOkIGVne7BQ35DSfo1qvJqFg==", "dev": true, + "license": "MIT", "engines": { "node": ">=0.8.0" } }, "node_modules/supports-color": { "version": "8.1.1", - "resolved": "https://registry.npmjs.org/supports-color/-/supports-color-8.1.1.tgz", - "integrity": "sha512-MpUEN2OodtUzxvKQl72cUF7RQ5EiHsGvSsVG0ia9c5RbWGL2CI4C7EpPS8UTBIplnlzZiNuV56w+FuNxy3ty2Q==", "dev": true, + "license": "MIT", "dependencies": { "has-flag": "^4.0.0" }, @@ -7281,9 +6352,8 @@ }, "node_modules/supports-preserve-symlinks-flag": { "version": "1.0.0", - "resolved": "https://registry.npmjs.org/supports-preserve-symlinks-flag/-/supports-preserve-symlinks-flag-1.0.0.tgz", - "integrity": "sha512-ot0WnXS9fgdkgIcePe6RHNk1WA8+muPa6cSjeR3V8K27q9BB1rTE3R1p7Hv0z1ZyAc8s6Vvv8DIyWf681MAt0w==", "dev": true, + "license": "MIT", "engines": { "node": ">= 0.4" }, @@ -7293,9 +6363,8 @@ }, "node_modules/test-exclude": { "version": "6.0.0", - "resolved": "https://registry.npmjs.org/test-exclude/-/test-exclude-6.0.0.tgz", - "integrity": "sha512-cAGWPIyOHU6zlmg88jwm7VRyXnMN7iV68OGAbYDk/Mh/xC/pzVPlQtY6ngoIH/5/tciuhGfvESU8GrHrcxD56w==", "dev": true, + "license": "ISC", "dependencies": { "@istanbuljs/schema": "^0.1.2", "glob": "^7.1.4", @@ -7307,9 +6376,8 @@ }, "node_modules/tmp": { "version": "0.0.33", - "resolved": "https://registry.npmjs.org/tmp/-/tmp-0.0.33.tgz", - "integrity": "sha512-jRCJlojKnZ3addtTOjdIqoRuPEKBvNXcGYqzO6zWZX8KfKEpnGY5jfggJQ3EjKuu8D4bJRr0y+cYJFmYbImXGw==", "dev": true, + "license": "MIT", "dependencies": { "os-tmpdir": "~1.0.2" }, @@ -7319,18 +6387,16 @@ }, "node_modules/to-fast-properties": { "version": "2.0.0", - "resolved": "https://registry.npmjs.org/to-fast-properties/-/to-fast-properties-2.0.0.tgz", - "integrity": "sha512-/OaKK0xYrs3DmxRYqL/yDc+FxFUVYhDlXMhRmv3z915w2HF1tnN1omB354j8VUGO/hbRzyD6Y3sA7v7GS/ceog==", "dev": true, + "license": "MIT", "engines": { "node": ">=4" } }, "node_modules/to-regex-range": { "version": "5.0.1", - "resolved": "https://registry.npmjs.org/to-regex-range/-/to-regex-range-5.0.1.tgz", - "integrity": "sha512-65P7iz6X5yEr1cwcgvQxbbIw7Uk3gOy5dIdtZ4rDveLqhrdJP+Li/Hx6tyK0NEb+2GCyneCMJiGqrADCSNk8sQ==", "dev": true, + "license": "MIT", "dependencies": { "is-number": "^7.0.0" }, @@ -7340,9 +6406,8 @@ }, "node_modules/trim-repeated": { "version": "1.0.0", - "resolved": "https://registry.npmjs.org/trim-repeated/-/trim-repeated-1.0.0.tgz", - "integrity": "sha512-pkonvlKk8/ZuR0D5tLW8ljt5I8kmxp2XKymhepUeOdCEfKpZaktSArkLHZt76OB1ZvO9bssUsDty4SWhLvZpLg==", "dev": true, + "license": "MIT", "dependencies": { "escape-string-regexp": "^1.0.2" }, @@ -7352,24 +6417,21 @@ }, "node_modules/trim-repeated/node_modules/escape-string-regexp": { "version": "1.0.5", - "resolved": "https://registry.npmjs.org/escape-string-regexp/-/escape-string-regexp-1.0.5.tgz", - "integrity": "sha512-vbRorB5FUQWvla16U8R/qgaFIya2qGzwDrNmCZuYKrbdSUMG6I1ZCGQRefkRVhuOkIGVne7BQ35DSfo1qvJqFg==", "dev": true, + "license": "MIT", "engines": { "node": ">=0.8.0" } }, "node_modules/tslib": { "version": "2.6.2", - "resolved": "https://registry.npmjs.org/tslib/-/tslib-2.6.2.tgz", - "integrity": "sha512-AEYxH93jGFPn/a2iVAwW87VuUIkR1FVUKB77NwMF7nBTDkDrrT/Hpt/IrCJ0QXhW27jTBDcf5ZY7w6RiqTMw2Q==", - "dev": true + "dev": true, + "license": "0BSD" }, "node_modules/tsx": { "version": "4.7.0", - "resolved": "https://registry.npmjs.org/tsx/-/tsx-4.7.0.tgz", - "integrity": "sha512-I+t79RYPlEYlHn9a+KzwrvEwhJg35h/1zHsLC2JXvhC2mdynMv6Zxzvhv5EMV6VF5qJlLlkSnMVvdZV3PSIGcg==", "dev": true, + "license": "MIT", "dependencies": { "esbuild": "~0.19.10", "get-tsconfig": "^4.7.2" @@ -7386,27 +6448,24 @@ }, "node_modules/type-detect": { "version": "4.0.8", - "resolved": "https://registry.npmjs.org/type-detect/-/type-detect-4.0.8.tgz", - "integrity": "sha512-0fr/mIH1dlO+x7TlcMy+bIDqKPsw/70tVyeHW787goQjhmqaZe10uwLujubK9q9Lg6Fiho1KUKDYz0Z7k7g5/g==", "dev": true, + "license": "MIT", "engines": { "node": ">=4" } }, "node_modules/type-fest": { "version": "0.8.1", - "resolved": "https://registry.npmjs.org/type-fest/-/type-fest-0.8.1.tgz", - "integrity": "sha512-4dbzIzqvjtgiM5rw1k5rEHtBANKmdudhGyBEajN01fEyhaAIhsoKNy6y7+IN93IfpFtwY9iqi7kD+xwKhQsNJA==", "dev": true, + "license": "(MIT OR CC0-1.0)", "engines": { "node": ">=8" } }, "node_modules/typed-array-buffer": { "version": "1.0.0", - "resolved": "https://registry.npmjs.org/typed-array-buffer/-/typed-array-buffer-1.0.0.tgz", - "integrity": "sha512-Y8KTSIglk9OZEr8zywiIHG/kmQ7KWyjseXs1CbSo8vC42w7hg2HgYTxSWwP0+is7bWDc1H+Fo026CpHFwm8tkw==", "dev": true, + "license": "MIT", "dependencies": { "call-bind": "^1.0.2", "get-intrinsic": "^1.2.1", @@ -7418,9 +6477,8 @@ }, "node_modules/typed-array-byte-length": { "version": "1.0.0", - "resolved": "https://registry.npmjs.org/typed-array-byte-length/-/typed-array-byte-length-1.0.0.tgz", - "integrity": "sha512-Or/+kvLxNpeQ9DtSydonMxCx+9ZXOswtwJn17SNLvhptaXYDJvkFFP5zbfU/uLmvnBJlI4yrnXRxpdWH/M5tNA==", "dev": true, + "license": "MIT", "dependencies": { "call-bind": "^1.0.2", "for-each": "^0.3.3", @@ -7436,9 +6494,8 @@ }, "node_modules/typed-array-byte-offset": { "version": "1.0.0", - "resolved": "https://registry.npmjs.org/typed-array-byte-offset/-/typed-array-byte-offset-1.0.0.tgz", - "integrity": "sha512-RD97prjEt9EL8YgAgpOkf3O4IF9lhJFr9g0htQkm0rchFp/Vx7LW5Q8fSXXub7BXAODyUQohRMyOc3faCPd0hg==", "dev": true, + "license": "MIT", "dependencies": { "available-typed-arrays": "^1.0.5", "call-bind": "^1.0.2", @@ -7455,9 +6512,8 @@ }, "node_modules/typed-array-length": { "version": "1.0.4", - "resolved": "https://registry.npmjs.org/typed-array-length/-/typed-array-length-1.0.4.tgz", - "integrity": "sha512-KjZypGq+I/H7HI5HlOoGHkWUUGq+Q0TPhQurLbyrVrvnKTBgzLhIJ7j6J/XTQOi0d1RjyZ0wdas8bKs2p0x3Ng==", "dev": true, + "license": "MIT", "dependencies": { "call-bind": "^1.0.2", "for-each": "^0.3.3", @@ -7469,18 +6525,16 @@ }, "node_modules/typedarray-to-buffer": { "version": "3.1.5", - "resolved": "https://registry.npmjs.org/typedarray-to-buffer/-/typedarray-to-buffer-3.1.5.tgz", - "integrity": "sha512-zdu8XMNEDepKKR+XYOXAVPtWui0ly0NtohUscw+UmaHiAWT8hrV1rr//H6V+0DvJ3OQ19S979M0laLfX8rm82Q==", "dev": true, + "license": "MIT", "dependencies": { "is-typedarray": "^1.0.0" } }, "node_modules/typedoc": { "version": "0.25.7", - "resolved": "https://registry.npmjs.org/typedoc/-/typedoc-0.25.7.tgz", - "integrity": "sha512-m6A6JjQRg39p2ZVRIN3NKXgrN8vzlHhOS+r9ymUYtcUP/TIQPvWSq7YgE5ZjASfv5Vd5BW5xrir6Gm2XNNcOow==", "dev": true, + "license": "Apache-2.0", "dependencies": { "lunr": "^2.3.9", "marked": "^4.3.0", @@ -7499,18 +6553,16 @@ }, "node_modules/typedoc/node_modules/brace-expansion": { "version": "2.0.1", - "resolved": "https://registry.npmjs.org/brace-expansion/-/brace-expansion-2.0.1.tgz", - "integrity": "sha512-XnAIvQ8eM+kC6aULx6wuQiwVsnzsi9d3WxzV3FpWTGA19F621kwdbsAcFKXgKUHZWsy+mY6iL1sHTxWEFCytDA==", "dev": true, + "license": "MIT", "dependencies": { "balanced-match": "^1.0.0" } }, "node_modules/typedoc/node_modules/minimatch": { "version": "9.0.3", - "resolved": "https://registry.npmjs.org/minimatch/-/minimatch-9.0.3.tgz", - "integrity": "sha512-RHiac9mvaRw0x3AYRgDC1CxAP7HTcNrrECeA8YYJeWnpo+2Q5CegtZjaotWTWxDG3UeGA1coE05iH1mPjT/2mg==", "dev": true, + "license": "ISC", "dependencies": { "brace-expansion": "^2.0.1" }, @@ -7523,9 +6575,8 @@ }, "node_modules/typescript": { "version": "5.3.3", - "resolved": "https://registry.npmjs.org/typescript/-/typescript-5.3.3.tgz", - "integrity": "sha512-pXWcraxM0uxAS+tN0AG/BF2TyqmHO014Z070UsJ+pFvYuRSq8KH8DmWpnbXe0pEPDHXZV3FcAbJkijJ5oNEnWw==", "dev": true, + "license": "Apache-2.0", "bin": { "tsc": "bin/tsc", "tsserver": "bin/tsserver" @@ -7536,9 +6587,8 @@ }, "node_modules/unbox-primitive": { "version": "1.0.2", - "resolved": "https://registry.npmjs.org/unbox-primitive/-/unbox-primitive-1.0.2.tgz", - "integrity": "sha512-61pPlCD9h51VoreyJ0BReideM3MDKMKnh6+V9L08331ipq6Q8OFXZYiqP6n/tbHx4s5I9uRhcye6BrbkizkBDw==", "dev": true, + "license": "MIT", "dependencies": { "call-bind": "^1.0.2", "has-bigints": "^1.0.2", @@ -7551,15 +6601,13 @@ }, "node_modules/undici-types": { "version": "5.26.5", - "resolved": "https://registry.npmjs.org/undici-types/-/undici-types-5.26.5.tgz", - "integrity": "sha512-JlCMO+ehdEIKqlFxk6IfVoAUVmgz7cU7zD/h9XZ0qzeosSHmUJVOzSQvvYSYWXkFXC+IfLKSIffhv0sVZup6pA==", - "dev": true + "dev": true, + "license": "MIT" }, "node_modules/unicorn-magic": { "version": "0.1.0", - "resolved": "https://registry.npmjs.org/unicorn-magic/-/unicorn-magic-0.1.0.tgz", - "integrity": "sha512-lRfVq8fE8gz6QMBuDM6a+LO3IAzTi05H6gCVaUpir2E1Rwpo4ZUog45KpNXKC/Mn3Yb9UDuHumeFTo9iV/D9FQ==", "dev": true, + "license": "MIT", "engines": { "node": ">=18" }, @@ -7569,9 +6617,8 @@ }, "node_modules/unique-string": { "version": "3.0.0", - "resolved": "https://registry.npmjs.org/unique-string/-/unique-string-3.0.0.tgz", - "integrity": "sha512-VGXBUVwxKMBUznyffQweQABPRRW1vHZAbadFZud4pLFAqRGvv/96vafgjWFqzourzr8YonlQiPgH0YCJfawoGQ==", "dev": true, + "license": "MIT", "dependencies": { "crypto-random-string": "^4.0.0" }, @@ -7584,23 +6631,19 @@ }, "node_modules/universal-user-agent": { "version": "6.0.1", - "resolved": "https://registry.npmjs.org/universal-user-agent/-/universal-user-agent-6.0.1.tgz", - "integrity": "sha512-yCzhz6FN2wU1NiiQRogkTQszlQSlpWaw8SvVegAc+bDxbzHgh1vX8uIe8OYyMH6DwH+sdTJsgMl36+mSMdRJIQ==", - "dev": true + "dev": true, + "license": "ISC" }, "node_modules/universalify": { "version": "2.0.1", - "resolved": "https://registry.npmjs.org/universalify/-/universalify-2.0.1.tgz", - "integrity": "sha512-gptHNQghINnc/vTGIk0SOFGFNXw7JVrlRUtConJRlvaw6DuX0wO5Jeko9sWrMBhh+PsYAZ7oXAiOnf/UKogyiw==", "dev": true, + "license": "MIT", "engines": { "node": ">= 10.0.0" } }, "node_modules/update-browserslist-db": { "version": "1.0.13", - "resolved": "https://registry.npmjs.org/update-browserslist-db/-/update-browserslist-db-1.0.13.tgz", - "integrity": "sha512-xebP81SNcPuNpPP3uzeW1NYXxI3rxyJzF3pD6sH4jE7o/IX+WtSpwnVU+qIsDPyk0d3hmFQ7mjqc6AtV604hbg==", "dev": true, "funding": [ { @@ -7616,6 +6659,7 @@ "url": "https://github.com/sponsors/ai" } ], + "license": "MIT", "dependencies": { "escalade": "^3.1.1", "picocolors": "^1.0.0" @@ -7629,9 +6673,8 @@ }, "node_modules/update-notifier": { "version": "7.0.0", - "resolved": "https://registry.npmjs.org/update-notifier/-/update-notifier-7.0.0.tgz", - "integrity": "sha512-Hv25Bh+eAbOLlsjJreVPOs4vd51rrtCrmhyOJtbpAojro34jS4KQaEp4/EvlHJX7jSO42VvEFpkastVyXyIsdQ==", "dev": true, + "license": "BSD-2-Clause", "dependencies": { "boxen": "^7.1.1", "chalk": "^5.3.0", @@ -7655,9 +6698,8 @@ }, "node_modules/update-notifier/node_modules/chalk": { "version": "5.3.0", - "resolved": "https://registry.npmjs.org/chalk/-/chalk-5.3.0.tgz", - "integrity": "sha512-dLitG79d+GV1Nb/VYcCDFivJeK1hiukt9QjRNVOsUtTy1rR1YJsmpGGTZ3qJos+uw7WmWF4wUwBd9jxjocFC2w==", "dev": true, + "license": "MIT", "engines": { "node": "^12.17.0 || ^14.13 || >=16.0.0" }, @@ -7667,9 +6709,8 @@ }, "node_modules/update-notifier/node_modules/lru-cache": { "version": "6.0.0", - "resolved": "https://registry.npmjs.org/lru-cache/-/lru-cache-6.0.0.tgz", - "integrity": "sha512-Jo6dJ04CmSjuznwJSS3pUeWmd/H0ffTlkXXgwZi+eq1UCmqQwCh+eLsYOYCwY991i2Fah4h1BEMCx4qThGbsiA==", "dev": true, + "license": "ISC", "dependencies": { "yallist": "^4.0.0" }, @@ -7679,9 +6720,8 @@ }, "node_modules/update-notifier/node_modules/semver": { "version": "7.5.4", - "resolved": "https://registry.npmjs.org/semver/-/semver-7.5.4.tgz", - "integrity": "sha512-1bCSESV6Pv+i21Hvpxp3Dx+pSD8lIPt8uVjRrxAUt/nbswYc+tK6Y2btiULjd4+fnq15PX+nqQDC7Oft7WkwcA==", "dev": true, + "license": "ISC", "dependencies": { "lru-cache": "^6.0.0" }, @@ -7694,69 +6734,60 @@ }, "node_modules/update-notifier/node_modules/yallist": { "version": "4.0.0", - "resolved": "https://registry.npmjs.org/yallist/-/yallist-4.0.0.tgz", - "integrity": "sha512-3wdGidZyq5PB084XLES5TpOSRA3wjXAlIWMhum2kRcv/41Sn2emQ0dycQW4uZXLejwKvg6EsvbdlVL+FYEct7A==", - "dev": true + "dev": true, + "license": "ISC" }, "node_modules/url-join": { "version": "5.0.0", - "resolved": "https://registry.npmjs.org/url-join/-/url-join-5.0.0.tgz", - "integrity": "sha512-n2huDr9h9yzd6exQVnH/jU5mr+Pfx08LRXXZhkLLetAMESRj+anQsTAh940iMrIetKAmry9coFuZQ2jY8/p3WA==", "dev": true, + "license": "MIT", "engines": { "node": "^12.20.0 || ^14.13.1 || >=16.0.0" } }, "node_modules/util-deprecate": { "version": "1.0.2", - "resolved": "https://registry.npmjs.org/util-deprecate/-/util-deprecate-1.0.2.tgz", - "integrity": "sha512-EPD5q1uXyFxJpCrLnCc1nHnq3gOa6DZBocAIiI2TaSCA7VCJ1UJDMagCzIkXNsUYfD1daK//LTEQ8xiIbrHtcw==", - "dev": true + "dev": true, + "license": "MIT" }, "node_modules/uuid": { "version": "8.3.2", - "resolved": "https://registry.npmjs.org/uuid/-/uuid-8.3.2.tgz", - "integrity": "sha512-+NYs2QeMWy+GWFOEm9xnn6HCDp0l7QBD7ml8zLUmJ+93Q5NF0NocErnwkTkXVFNiX3/fpC6afS8Dhb/gz7R7eg==", "dev": true, + "license": "MIT", "bin": { "uuid": "dist/bin/uuid" } }, "node_modules/vscode-oniguruma": { "version": "1.7.0", - "resolved": "https://registry.npmjs.org/vscode-oniguruma/-/vscode-oniguruma-1.7.0.tgz", - "integrity": "sha512-L9WMGRfrjOhgHSdOYgCt/yRMsXzLDJSL7BPrOZt73gU0iWO4mpqzqQzOz5srxqTvMBaR0XZTSrVWo4j55Rc6cA==", - "dev": true + "dev": true, + "license": "MIT" }, "node_modules/vscode-textmate": { "version": "8.0.0", - "resolved": "https://registry.npmjs.org/vscode-textmate/-/vscode-textmate-8.0.0.tgz", - "integrity": "sha512-AFbieoL7a5LMqcnOF04ji+rpXadgOXnZsxQr//r83kLPr7biP7am3g9zbaZIaBGwBRWeSvoMD4mgPdX3e4NWBg==", - "dev": true + "dev": true, + "license": "MIT" }, "node_modules/wcwidth": { "version": "1.0.1", - "resolved": "https://registry.npmjs.org/wcwidth/-/wcwidth-1.0.1.tgz", - "integrity": "sha512-XHPEwS0q6TaxcvG85+8EYkbiCux2XtWG2mkc47Ng2A77BQu9+DqIOJldST4HgPkuea7dvKSj5VgX3P1d4rW8Tg==", "dev": true, + "license": "MIT", "dependencies": { "defaults": "^1.0.3" } }, "node_modules/web-streams-polyfill": { "version": "3.3.2", - "resolved": "https://registry.npmjs.org/web-streams-polyfill/-/web-streams-polyfill-3.3.2.tgz", - "integrity": "sha512-3pRGuxRF5gpuZc0W+EpwQRmCD7gRqcDOMt688KmdlDAgAyaB1XlN0zq2njfDNm44XVdIouE7pZ6GzbdyH47uIQ==", "dev": true, + "license": "MIT", "engines": { "node": ">= 8" } }, "node_modules/which": { "version": "2.0.2", - "resolved": "https://registry.npmjs.org/which/-/which-2.0.2.tgz", - "integrity": "sha512-BLI3Tl1TW3Pvl70l3yq3Y64i+awpwXqsGBYWkkqMtnbXgrMD+yj7rhW0kuEDxzJaYXGjEW5ogapKNMEKNMjibA==", "dev": true, + "license": "ISC", "dependencies": { "isexe": "^2.0.0" }, @@ -7769,9 +6800,8 @@ }, "node_modules/which-boxed-primitive": { "version": "1.0.2", - "resolved": "https://registry.npmjs.org/which-boxed-primitive/-/which-boxed-primitive-1.0.2.tgz", - "integrity": "sha512-bwZdv0AKLpplFY2KZRX6TvyuN7ojjr7lwkg6ml0roIy9YeuSr7JS372qlNW18UQYzgYK9ziGcerWqZOmEn9VNg==", "dev": true, + "license": "MIT", "dependencies": { "is-bigint": "^1.0.1", "is-boolean-object": "^1.1.0", @@ -7785,15 +6815,13 @@ }, "node_modules/which-module": { "version": "2.0.1", - "resolved": "https://registry.npmjs.org/which-module/-/which-module-2.0.1.tgz", - "integrity": "sha512-iBdZ57RDvnOR9AGBhML2vFZf7h8vmBjhoaZqODJBFWHVtKkDmKuHai3cx5PgVMrX5YDNp27AofYbAwctSS+vhQ==", - "dev": true + "dev": true, + "license": "ISC" }, "node_modules/which-typed-array": { "version": "1.1.14", - "resolved": "https://registry.npmjs.org/which-typed-array/-/which-typed-array-1.1.14.tgz", - "integrity": "sha512-VnXFiIW8yNn9kIHN88xvZ4yOWchftKDsRJ8fEPacX/wl1lOvBrhsJ/OeJCXq7B0AaijRuqgzSKalJoPk+D8MPg==", "dev": true, + "license": "MIT", "dependencies": { "available-typed-arrays": "^1.0.6", "call-bind": "^1.0.5", @@ -7810,9 +6838,8 @@ }, "node_modules/widest-line": { "version": "4.0.1", - "resolved": "https://registry.npmjs.org/widest-line/-/widest-line-4.0.1.tgz", - "integrity": "sha512-o0cyEG0e8GPzT4iGHphIOh0cJOV8fivsXxddQasHPHfoZf1ZexrfeA21w2NaEN1RHE+fXlfISmOE8R9N3u3Qig==", "dev": true, + "license": "MIT", "dependencies": { "string-width": "^5.0.1" }, @@ -7825,9 +6852,8 @@ }, "node_modules/widest-line/node_modules/ansi-regex": { "version": "6.0.1", - "resolved": "https://registry.npmjs.org/ansi-regex/-/ansi-regex-6.0.1.tgz", - "integrity": "sha512-n5M855fKb2SsfMIiFFoVrABHJC8QtHwVx+mHWP3QcEqBHYienj5dHSgjbxtC0WEZXYt4wcD6zrQElDPhFuZgfA==", "dev": true, + "license": "MIT", "engines": { "node": ">=12" }, @@ -7837,15 +6863,13 @@ }, "node_modules/widest-line/node_modules/emoji-regex": { "version": "9.2.2", - "resolved": "https://registry.npmjs.org/emoji-regex/-/emoji-regex-9.2.2.tgz", - "integrity": "sha512-L18DaJsXSUk2+42pv8mLs5jJT2hqFkFE4j21wOmgbUqsZ2hL72NsUU785g9RXgo3s0ZNgVl42TiHp3ZtOv/Vyg==", - "dev": true + "dev": true, + "license": "MIT" }, "node_modules/widest-line/node_modules/string-width": { "version": "5.1.2", - "resolved": "https://registry.npmjs.org/string-width/-/string-width-5.1.2.tgz", - "integrity": "sha512-HnLOCR3vjcY8beoNLtcjZ5/nxn2afmME6lhrDrebokqMap+XbeW8n9TXpPDOqdGK5qcI3oT0GKTW6wC7EMiVqA==", "dev": true, + "license": "MIT", "dependencies": { "eastasianwidth": "^0.2.0", "emoji-regex": "^9.2.2", @@ -7860,9 +6884,8 @@ }, "node_modules/widest-line/node_modules/strip-ansi": { "version": "7.1.0", - "resolved": "https://registry.npmjs.org/strip-ansi/-/strip-ansi-7.1.0.tgz", - "integrity": "sha512-iq6eVVI64nQQTRYq2KtEg2d2uU7LElhTJwsH4YzIHZshxlgZms/wIc4VoDQTlG/IvVIrBKG06CrZnp0qv7hkcQ==", "dev": true, + "license": "MIT", "dependencies": { "ansi-regex": "^6.0.1" }, @@ -7875,15 +6898,13 @@ }, "node_modules/wildcard-match": { "version": "5.1.2", - "resolved": "https://registry.npmjs.org/wildcard-match/-/wildcard-match-5.1.2.tgz", - "integrity": "sha512-qNXwI591Z88c8bWxp+yjV60Ch4F8Riawe3iGxbzquhy8Xs9m+0+SLFBGb/0yCTIDElawtaImC37fYZ+dr32KqQ==", - "dev": true + "dev": true, + "license": "ISC" }, "node_modules/windows-release": { "version": "5.1.1", - "resolved": "https://registry.npmjs.org/windows-release/-/windows-release-5.1.1.tgz", - "integrity": "sha512-NMD00arvqcq2nwqc5Q6KtrSRHK+fVD31erE5FEMahAw5PmVCgD7MUXodq3pdZSUkqA9Cda2iWx6s1XYwiJWRmw==", "dev": true, + "license": "MIT", "dependencies": { "execa": "^5.1.1" }, @@ -7896,9 +6917,8 @@ }, "node_modules/windows-release/node_modules/execa": { "version": "5.1.1", - "resolved": "https://registry.npmjs.org/execa/-/execa-5.1.1.tgz", - "integrity": "sha512-8uSpZZocAZRBAPIEINJj3Lo9HyGitllczc27Eh5YYojjMFMn8yHMDMaUHE2Jqfq05D/wucwI4JGURyXt1vchyg==", "dev": true, + "license": "MIT", "dependencies": { "cross-spawn": "^7.0.3", "get-stream": "^6.0.0", @@ -7919,9 +6939,8 @@ }, "node_modules/windows-release/node_modules/get-stream": { "version": "6.0.1", - "resolved": "https://registry.npmjs.org/get-stream/-/get-stream-6.0.1.tgz", - "integrity": "sha512-ts6Wi+2j3jQjqi70w5AlN8DFnkSwC+MqmxEzdEALB2qXZYV3X/b1CTfgPLGJNMeAWxdPfU8FO1ms3NUfaHCPYg==", "dev": true, + "license": "MIT", "engines": { "node": ">=10" }, @@ -7931,27 +6950,24 @@ }, "node_modules/windows-release/node_modules/human-signals": { "version": "2.1.0", - "resolved": "https://registry.npmjs.org/human-signals/-/human-signals-2.1.0.tgz", - "integrity": "sha512-B4FFZ6q/T2jhhksgkbEW3HBvWIfDW85snkQgawt07S7J5QXTk6BkNV+0yAeZrM5QpMAdYlocGoljn0sJ/WQkFw==", "dev": true, + "license": "Apache-2.0", "engines": { "node": ">=10.17.0" } }, "node_modules/windows-release/node_modules/mimic-fn": { "version": "2.1.0", - "resolved": "https://registry.npmjs.org/mimic-fn/-/mimic-fn-2.1.0.tgz", - "integrity": "sha512-OqbOk5oEQeAZ8WXWydlu9HJjz9WVdEIvamMCcXmuqUYjTknH/sqsWvhQ3vgwKFRR1HpjvNBKQ37nbJgYzGqGcg==", "dev": true, + "license": "MIT", "engines": { "node": ">=6" } }, "node_modules/windows-release/node_modules/npm-run-path": { "version": "4.0.1", - "resolved": "https://registry.npmjs.org/npm-run-path/-/npm-run-path-4.0.1.tgz", - "integrity": "sha512-S48WzZW777zhNIrn7gxOlISNAqi9ZC/uQFnRdbeIHhZhCA6UqpkOT8T1G7BvfdgP4Er8gF4sUbaS0i7QvIfCWw==", "dev": true, + "license": "MIT", "dependencies": { "path-key": "^3.0.0" }, @@ -7961,9 +6977,8 @@ }, "node_modules/windows-release/node_modules/onetime": { "version": "5.1.2", - "resolved": "https://registry.npmjs.org/onetime/-/onetime-5.1.2.tgz", - "integrity": "sha512-kbpaSSGJTWdAY5KPVeMOKXSrPtr8C8C7wodJbcsd51jRnmD+GZu8Y0VoU6Dm5Z4vWr0Ig/1NKuWRKf7j5aaYSg==", "dev": true, + "license": "MIT", "dependencies": { "mimic-fn": "^2.1.0" }, @@ -7976,24 +6991,21 @@ }, "node_modules/windows-release/node_modules/strip-final-newline": { "version": "2.0.0", - "resolved": "https://registry.npmjs.org/strip-final-newline/-/strip-final-newline-2.0.0.tgz", - "integrity": "sha512-BrpvfNAE3dcvq7ll3xVumzjKjZQ5tI1sEUIKr3Uoks0XUl45St3FlatVqef9prk4jRDzhW6WZg+3bk93y6pLjA==", "dev": true, + "license": "MIT", "engines": { "node": ">=6" } }, "node_modules/workerpool": { "version": "6.2.1", - "resolved": "https://registry.npmjs.org/workerpool/-/workerpool-6.2.1.tgz", - "integrity": "sha512-ILEIE97kDZvF9Wb9f6h5aXK4swSlKGUcOEGiIYb2OOu/IrDU9iwj0fD//SsA6E5ibwJxpEvhullJY4Sl4GcpAw==", - "dev": true + "dev": true, + "license": "Apache-2.0" }, "node_modules/wrap-ansi": { "version": "6.2.0", - "resolved": "https://registry.npmjs.org/wrap-ansi/-/wrap-ansi-6.2.0.tgz", - "integrity": "sha512-r6lPcBGxZXlIcymEu7InxDMhdW0KDxpLgoFLcguasxCaJ/SOIZwINatK9KY/tf+ZrlywOKU0UDj3ATXUBfxJXA==", "dev": true, + "license": "MIT", "dependencies": { "ansi-styles": "^4.0.0", "string-width": "^4.1.0", @@ -8005,15 +7017,13 @@ }, "node_modules/wrappy": { "version": "1.0.2", - "resolved": "https://registry.npmjs.org/wrappy/-/wrappy-1.0.2.tgz", - "integrity": "sha512-l4Sp/DRseor9wL6EvV2+TuQn63dMkPjZ/sp9XkghTEbV9KlPS1xUsZ3u7/IQO4wxtcFB4bgpQPRcR3QCvezPcQ==", - "dev": true + "dev": true, + "license": "ISC" }, "node_modules/write-file-atomic": { "version": "3.0.3", - "resolved": "https://registry.npmjs.org/write-file-atomic/-/write-file-atomic-3.0.3.tgz", - "integrity": "sha512-AvHcyZ5JnSfq3ioSyjrBkH9yW4m7Ayk8/9My/DD9onKeu/94fwrMocemO2QAJFAlnnDN+ZDS+ZjAR5ua1/PV/Q==", "dev": true, + "license": "ISC", "dependencies": { "imurmurhash": "^0.1.4", "is-typedarray": "^1.0.0", @@ -8023,9 +7033,8 @@ }, "node_modules/xdg-basedir": { "version": "5.1.0", - "resolved": "https://registry.npmjs.org/xdg-basedir/-/xdg-basedir-5.1.0.tgz", - "integrity": "sha512-GCPAHLvrIH13+c0SuacwvRYj2SxJXQ4kaVTT5xgL3kPrz56XxkF21IGhjSE1+W0aw7gpBWRGXLCPnPby6lSpmQ==", "dev": true, + "license": "MIT", "engines": { "node": ">=12" }, @@ -8035,24 +7044,21 @@ }, "node_modules/y18n": { "version": "5.0.8", - "resolved": "https://registry.npmjs.org/y18n/-/y18n-5.0.8.tgz", - "integrity": "sha512-0pfFzegeDWJHJIAmTLRP2DwHjdF5s7jo9tuztdQxAhINCdvS+3nGINqPd00AphqJR/0LhANUS6/+7SCb98YOfA==", "dev": true, + "license": "ISC", "engines": { "node": ">=10" } }, "node_modules/yallist": { "version": "3.1.1", - "resolved": "https://registry.npmjs.org/yallist/-/yallist-3.1.1.tgz", - "integrity": "sha512-a4UGQaWPH59mOXUYnAG2ewncQS4i4F43Tv3JoAM+s2VDAmS9NsK8GpDMLrCHPksFT7h3K6TOoUNn2pb7RoXx4g==", - "dev": true + "dev": true, + "license": "ISC" }, "node_modules/yargs": { "version": "16.2.0", - "resolved": "https://registry.npmjs.org/yargs/-/yargs-16.2.0.tgz", - "integrity": "sha512-D1mvvtDG0L5ft/jGWkLpG1+m0eQxOfaBvTNELraWj22wSVUMWxZUvYgJYcKh6jGGIkJFhH4IZPQhR4TKpc8mBw==", "dev": true, + "license": "MIT", "dependencies": { "cliui": "^7.0.2", "escalade": "^3.1.1", @@ -8068,18 +7074,16 @@ }, "node_modules/yargs-parser": { "version": "20.2.4", - "resolved": "https://registry.npmjs.org/yargs-parser/-/yargs-parser-20.2.4.tgz", - "integrity": "sha512-WOkpgNhPTlE73h4VFAFsOnomJVaovO8VqLDzy5saChRBFQFBoMYirowyW+Q9HB4HFF4Z7VZTiG3iSzJJA29yRA==", "dev": true, + "license": "ISC", "engines": { "node": ">=10" } }, "node_modules/yargs-unparser": { "version": "2.0.0", - "resolved": "https://registry.npmjs.org/yargs-unparser/-/yargs-unparser-2.0.0.tgz", - "integrity": "sha512-7pRTIA9Qc1caZ0bZ6RYRGbHJthJWuakf+WmHK0rVeLkNrrGhfoabBNdue6kdINI6r4if7ocq9aD/n7xwKOdzOA==", "dev": true, + "license": "MIT", "dependencies": { "camelcase": "^6.0.0", "decamelize": "^4.0.0", @@ -8092,9 +7096,8 @@ }, "node_modules/yargs-unparser/node_modules/camelcase": { "version": "6.3.0", - "resolved": "https://registry.npmjs.org/camelcase/-/camelcase-6.3.0.tgz", - "integrity": "sha512-Gmy6FhYlCY7uOElZUSbxo2UCDH8owEk996gkbrpsgGtrJLM3J7jGxl9Ic7Qwwj4ivOE5AWZWRMecDdF7hqGjFA==", "dev": true, + "license": "MIT", "engines": { "node": ">=10" }, @@ -8104,9 +7107,8 @@ }, "node_modules/yargs-unparser/node_modules/decamelize": { "version": "4.0.0", - "resolved": "https://registry.npmjs.org/decamelize/-/decamelize-4.0.0.tgz", - "integrity": "sha512-9iE1PgSik9HeIIw2JO94IidnE3eBoQrFJ3w7sFuzSX4DpmZ3v5sZpUiV5Swcf6mQEF+Y0ru8Neo+p+nyh2J+hQ==", "dev": true, + "license": "MIT", "engines": { "node": ">=10" }, @@ -8116,9 +7118,8 @@ }, "node_modules/yocto-queue": { "version": "0.1.0", - "resolved": "https://registry.npmjs.org/yocto-queue/-/yocto-queue-0.1.0.tgz", - "integrity": "sha512-rVksvsnNCdJ/ohGc6xgPwyN8eheCxsiLM8mxuE/t/mOVqJewPuO1miLpTHQiRgTKCLexL4MeAFVagts7HmNZ2Q==", "dev": true, + "license": "MIT", "engines": { "node": ">=10" }, @@ -8128,7 +7129,7 @@ }, "packages/bloom": { "name": "@redis/bloom", - "version": "2.0.0-next.3", + "version": "5.0.0-next.5", "license": "MIT", "devDependencies": { "@redis/test-utils": "*" @@ -8137,12 +7138,12 @@ "node": ">= 18" }, "peerDependencies": { - "@redis/client": "^2.0.0-next.4" + "@redis/client": "^5.0.0-next.5" } }, "packages/client": { "name": "@redis/client", - "version": "2.0.0-next.4", + "version": "5.0.0-next.5", "license": "MIT", "dependencies": { "cluster-key-slot": "1.1.2" @@ -8158,7 +7159,7 @@ }, "packages/graph": { "name": "@redis/graph", - "version": "2.0.0-next.2", + "version": "5.0.0-next.5", "license": "MIT", "devDependencies": { "@redis/test-utils": "*" @@ -8167,12 +7168,12 @@ "node": ">= 18" }, "peerDependencies": { - "@redis/client": "^2.0.0-next.4" + "@redis/client": "^5.0.0-next.5" } }, "packages/json": { "name": "@redis/json", - "version": "2.0.0-next.2", + "version": "5.0.0-next.5", "license": "MIT", "devDependencies": { "@redis/test-utils": "*" @@ -8181,19 +7182,19 @@ "node": ">= 18" }, "peerDependencies": { - "@redis/client": "^2.0.0-next.4" + "@redis/client": "^5.0.0-next.5" } }, "packages/redis": { - "version": "5.0.0-next.4", + "version": "5.0.0-next.5", "license": "MIT", "dependencies": { - "@redis/bloom": "2.0.0-next.3", - "@redis/client": "2.0.0-next.4", - "@redis/graph": "2.0.0-next.2", - "@redis/json": "2.0.0-next.2", - "@redis/search": "2.0.0-next.2", - "@redis/time-series": "2.0.0-next.2" + "@redis/bloom": "5.0.0-next.5", + "@redis/client": "5.0.0-next.5", + "@redis/graph": "5.0.0-next.5", + "@redis/json": "5.0.0-next.5", + "@redis/search": "5.0.0-next.5", + "@redis/time-series": "5.0.0-next.5" }, "engines": { "node": ">= 18" @@ -8201,7 +7202,7 @@ }, "packages/search": { "name": "@redis/search", - "version": "2.0.0-next.2", + "version": "5.0.0-next.5", "license": "MIT", "devDependencies": { "@redis/test-utils": "*" @@ -8210,7 +7211,7 @@ "node": ">= 18" }, "peerDependencies": { - "@redis/client": "^2.0.0-next.4" + "@redis/client": "^5.0.0-next.5" } }, "packages/test-utils": { @@ -8225,9 +7226,8 @@ }, "packages/test-utils/node_modules/cliui": { "version": "8.0.1", - "resolved": "https://registry.npmjs.org/cliui/-/cliui-8.0.1.tgz", - "integrity": "sha512-BSeNnyus75C4//NQ9gQt1/csTXyo/8Sb+afLAkzAptFuMsod9HFokGNudZpi/oQV73hnVK+sR+5PVRMd+Dr7YQ==", "dev": true, + "license": "ISC", "dependencies": { "string-width": "^4.2.0", "strip-ansi": "^6.0.1", @@ -8239,9 +7239,8 @@ }, "packages/test-utils/node_modules/wrap-ansi": { "version": "7.0.0", - "resolved": "https://registry.npmjs.org/wrap-ansi/-/wrap-ansi-7.0.0.tgz", - "integrity": "sha512-YVGIj2kamLSTxw6NsZjoBxfSwsn0ycdesmc4p+Q21c5zPuZ1pl+NfxVdxPtdHvmNVOQ6XSYG4AUtyt/Fi7D16Q==", "dev": true, + "license": "MIT", "dependencies": { "ansi-styles": "^4.0.0", "string-width": "^4.1.0", @@ -8256,9 +7255,8 @@ }, "packages/test-utils/node_modules/yargs": { "version": "17.7.2", - "resolved": "https://registry.npmjs.org/yargs/-/yargs-17.7.2.tgz", - "integrity": "sha512-7dSzzRQ++CKnNI/krKnYRV7JKKPUXMEh61soaHKg9mrWEhzFWhFnxPxGl+69cD1Ou63C13NUPCnmIcrvqCuM6w==", "dev": true, + "license": "MIT", "dependencies": { "cliui": "^8.0.1", "escalade": "^3.1.1", @@ -8274,16 +7272,15 @@ }, "packages/test-utils/node_modules/yargs-parser": { "version": "21.1.1", - "resolved": "https://registry.npmjs.org/yargs-parser/-/yargs-parser-21.1.1.tgz", - "integrity": "sha512-tVpsJW7DdjecAiFpbIB1e3qxIQsE6NoPc5/eTdrbbIC4h0LVsWhnoa3g+m2HclBIujHzsxZ4VJVA+GUuc2/LBw==", "dev": true, + "license": "ISC", "engines": { "node": ">=12" } }, "packages/time-series": { "name": "@redis/time-series", - "version": "2.0.0-next.2", + "version": "5.0.0-next.5", "license": "MIT", "devDependencies": { "@redis/test-utils": "*" @@ -8292,7 +7289,7 @@ "node": ">= 18" }, "peerDependencies": { - "@redis/client": "^2.0.0-next.4" + "@redis/client": "^5.0.0-next.5" } } } diff --git a/packages/bloom/lib/commands/bloom/ADD.spec.ts b/packages/bloom/lib/commands/bloom/ADD.spec.ts index 11267e2afdb..a229936c7df 100644 --- a/packages/bloom/lib/commands/bloom/ADD.spec.ts +++ b/packages/bloom/lib/commands/bloom/ADD.spec.ts @@ -1,11 +1,12 @@ import { strict as assert } from 'node:assert'; import testUtils, { GLOBAL } from '../../test-utils'; import ADD from './ADD'; +import { parseArgs } from '@redis/client/lib/commands/generic-transformers'; describe('BF.ADD', () => { it('transformArguments', () => { assert.deepEqual( - ADD.transformArguments('key', 'item'), + parseArgs(ADD, 'key', 'item'), ['BF.ADD', 'key', 'item'] ); }); diff --git a/packages/bloom/lib/commands/bloom/ADD.ts b/packages/bloom/lib/commands/bloom/ADD.ts index a9655754897..f394755acc4 100644 --- a/packages/bloom/lib/commands/bloom/ADD.ts +++ b/packages/bloom/lib/commands/bloom/ADD.ts @@ -1,11 +1,13 @@ -import { RedisArgument, Command } from '@redis/client/dist/lib/RESP/types'; -import { transformBooleanReply } from '@redis/client/dist/lib/commands/generic-transformers'; +import { CommandParser } from '@redis/client/lib/client/parser'; +import { RedisArgument, Command } from '@redis/client/lib/RESP/types'; +import { transformBooleanReply } from '@redis/client/lib/commands/generic-transformers'; export default { - FIRST_KEY_INDEX: 1, IS_READ_ONLY: false, - transformArguments(key: RedisArgument, item: RedisArgument) { - return ['BF.ADD', key, item]; + parseCommand(parser: CommandParser, key: RedisArgument, item: RedisArgument) { + parser.push('BF.ADD'); + parser.pushKey(key); + parser.push(item); }, transformReply: transformBooleanReply } as const satisfies Command; diff --git a/packages/bloom/lib/commands/bloom/CARD.spec.ts b/packages/bloom/lib/commands/bloom/CARD.spec.ts index b150f812574..32a28cdf6f7 100644 --- a/packages/bloom/lib/commands/bloom/CARD.spec.ts +++ b/packages/bloom/lib/commands/bloom/CARD.spec.ts @@ -1,11 +1,12 @@ import { strict as assert } from 'node:assert'; import testUtils, { GLOBAL } from '../../test-utils'; import CARD from './CARD'; +import { parseArgs } from '@redis/client/lib/commands/generic-transformers'; describe('BF.CARD', () => { it('transformArguments', () => { assert.deepEqual( - CARD.transformArguments('bloom'), + parseArgs(CARD, 'bloom'), ['BF.CARD', 'bloom'] ); }); diff --git a/packages/bloom/lib/commands/bloom/CARD.ts b/packages/bloom/lib/commands/bloom/CARD.ts index ddaa76cc1f8..1d206b30af9 100644 --- a/packages/bloom/lib/commands/bloom/CARD.ts +++ b/packages/bloom/lib/commands/bloom/CARD.ts @@ -1,10 +1,11 @@ -import { RedisArgument, NumberReply, Command } from '@redis/client/dist/lib/RESP/types'; +import { CommandParser } from '@redis/client/lib/client/parser'; +import { RedisArgument, NumberReply, Command } from '@redis/client/lib/RESP/types'; export default { - FIRST_KEY_INDEX: 1, IS_READ_ONLY: true, - transformArguments(key: RedisArgument) { - return ['BF.CARD', key]; + parseCommand(parser: CommandParser, key: RedisArgument) { + parser.push('BF.CARD'); + parser.pushKey(key); }, transformReply: undefined as unknown as () => NumberReply } as const satisfies Command; diff --git a/packages/bloom/lib/commands/bloom/EXISTS.spec.ts b/packages/bloom/lib/commands/bloom/EXISTS.spec.ts index 7db891b92bf..4d2cc70074a 100644 --- a/packages/bloom/lib/commands/bloom/EXISTS.spec.ts +++ b/packages/bloom/lib/commands/bloom/EXISTS.spec.ts @@ -1,11 +1,12 @@ import { strict as assert } from 'node:assert'; import testUtils, { GLOBAL } from '../../test-utils'; import EXISTS from './EXISTS'; +import { parseArgs } from '@redis/client/lib/commands/generic-transformers'; describe('BF.EXISTS', () => { it('transformArguments', () => { assert.deepEqual( - EXISTS.transformArguments('key', 'item'), + parseArgs(EXISTS, 'key', 'item'), ['BF.EXISTS', 'key', 'item'] ); }); diff --git a/packages/bloom/lib/commands/bloom/EXISTS.ts b/packages/bloom/lib/commands/bloom/EXISTS.ts index 9d28d671d61..3de18dd07cd 100644 --- a/packages/bloom/lib/commands/bloom/EXISTS.ts +++ b/packages/bloom/lib/commands/bloom/EXISTS.ts @@ -1,11 +1,13 @@ -import { RedisArgument, Command } from '@redis/client/dist/lib/RESP/types'; -import { transformBooleanReply } from '@redis/client/dist/lib/commands/generic-transformers'; +import { CommandParser } from '@redis/client/lib/client/parser'; +import { RedisArgument, Command } from '@redis/client/lib/RESP/types'; +import { transformBooleanReply } from '@redis/client/lib/commands/generic-transformers'; export default { - FIRST_KEY_INDEX: 1, IS_READ_ONLY: true, - transformArguments(key: RedisArgument, item: RedisArgument) { - return ['BF.EXISTS', key, item]; + parseCommand(parser: CommandParser, key: RedisArgument, item: RedisArgument) { + parser.push('BF.EXISTS'); + parser.pushKey(key); + parser.push(item); }, transformReply: transformBooleanReply } as const satisfies Command; diff --git a/packages/bloom/lib/commands/bloom/INFO.spec.ts b/packages/bloom/lib/commands/bloom/INFO.spec.ts index 4a17dab8d33..0dbe5cb1f43 100644 --- a/packages/bloom/lib/commands/bloom/INFO.spec.ts +++ b/packages/bloom/lib/commands/bloom/INFO.spec.ts @@ -1,11 +1,12 @@ import { strict as assert } from 'node:assert'; import testUtils, { GLOBAL } from '../../test-utils'; import INFO from './INFO'; +import { parseArgs } from '@redis/client/lib/commands/generic-transformers'; describe('BF.INFO', () => { it('transformArguments', () => { assert.deepEqual( - INFO.transformArguments('bloom'), + parseArgs(INFO, 'bloom'), ['BF.INFO', 'bloom'] ); }); diff --git a/packages/bloom/lib/commands/bloom/INFO.ts b/packages/bloom/lib/commands/bloom/INFO.ts index 208c999b970..b1e3f137c8c 100644 --- a/packages/bloom/lib/commands/bloom/INFO.ts +++ b/packages/bloom/lib/commands/bloom/INFO.ts @@ -1,4 +1,5 @@ -import { RedisArgument, Command, UnwrapReply, NullReply, NumberReply, TuplesToMapReply, Resp2Reply, SimpleStringReply, TypeMapping } from '@redis/client/dist/lib/RESP/types'; +import { CommandParser } from '@redis/client/lib/client/parser'; +import { RedisArgument, Command, UnwrapReply, NullReply, NumberReply, TuplesToMapReply, Resp2Reply, SimpleStringReply, TypeMapping } from '@redis/client/lib/RESP/types'; import { transformInfoV2Reply } from '.'; export type BfInfoReplyMap = TuplesToMapReply<[ @@ -9,19 +10,11 @@ export type BfInfoReplyMap = TuplesToMapReply<[ [SimpleStringReply<'Expansion rate'>, NullReply | NumberReply] ]>; -export interface BfInfoReply { - capacity: NumberReply; - size: NumberReply; - numberOfFilters: NumberReply; - numberOfInsertedItems: NumberReply; - expansionRate: NullReply | NumberReply; -} - export default { - FIRST_KEY_INDEX: 1, IS_READ_ONLY: true, - transformArguments(key: RedisArgument) { - return ['BF.INFO', key]; + parseCommand(parser: CommandParser, key: RedisArgument) { + parser.push('BF.INFO'); + parser.pushKey(key); }, transformReply: { 2: (reply: UnwrapReply>, _, typeMapping?: TypeMapping): BfInfoReplyMap => { diff --git a/packages/bloom/lib/commands/bloom/INSERT.spec.ts b/packages/bloom/lib/commands/bloom/INSERT.spec.ts index ccd81e070f1..a9b544a51ae 100644 --- a/packages/bloom/lib/commands/bloom/INSERT.spec.ts +++ b/packages/bloom/lib/commands/bloom/INSERT.spec.ts @@ -1,54 +1,55 @@ import { strict as assert } from 'node:assert'; import testUtils, { GLOBAL } from '../../test-utils'; import INSERT from './INSERT'; +import { parseArgs } from '@redis/client/lib/commands/generic-transformers'; describe('BF.INSERT', () => { describe('transformArguments', () => { it('simple', () => { assert.deepEqual( - INSERT.transformArguments('key', 'item'), + parseArgs(INSERT, 'key', 'item'), ['BF.INSERT', 'key', 'ITEMS', 'item'] ); }); it('with CAPACITY', () => { assert.deepEqual( - INSERT.transformArguments('key', 'item', { CAPACITY: 100 }), + parseArgs(INSERT, 'key', 'item', { CAPACITY: 100 }), ['BF.INSERT', 'key', 'CAPACITY', '100', 'ITEMS', 'item'] ); }); it('with ERROR', () => { assert.deepEqual( - INSERT.transformArguments('key', 'item', { ERROR: 0.01 }), + parseArgs(INSERT, 'key', 'item', { ERROR: 0.01 }), ['BF.INSERT', 'key', 'ERROR', '0.01', 'ITEMS', 'item'] ); }); it('with EXPANSION', () => { assert.deepEqual( - INSERT.transformArguments('key', 'item', { EXPANSION: 1 }), + parseArgs(INSERT, 'key', 'item', { EXPANSION: 1 }), ['BF.INSERT', 'key', 'EXPANSION', '1', 'ITEMS', 'item'] ); }); it('with NOCREATE', () => { assert.deepEqual( - INSERT.transformArguments('key', 'item', { NOCREATE: true }), + parseArgs(INSERT, 'key', 'item', { NOCREATE: true }), ['BF.INSERT', 'key', 'NOCREATE', 'ITEMS', 'item'] ); }); it('with NONSCALING', () => { assert.deepEqual( - INSERT.transformArguments('key', 'item', { NONSCALING: true }), + parseArgs(INSERT, 'key', 'item', { NONSCALING: true }), ['BF.INSERT', 'key', 'NONSCALING', 'ITEMS', 'item'] ); }); it('with CAPACITY, ERROR, EXPANSION, NOCREATE and NONSCALING', () => { assert.deepEqual( - INSERT.transformArguments('key', 'item', { + parseArgs(INSERT, 'key', 'item', { CAPACITY: 100, ERROR: 0.01, EXPANSION: 1, diff --git a/packages/bloom/lib/commands/bloom/INSERT.ts b/packages/bloom/lib/commands/bloom/INSERT.ts index dfeaf5f20de..14831f2f11a 100644 --- a/packages/bloom/lib/commands/bloom/INSERT.ts +++ b/packages/bloom/lib/commands/bloom/INSERT.ts @@ -1,6 +1,7 @@ -import { RedisArgument, Command } from '@redis/client/dist/lib/RESP/types'; -import { RedisVariadicArgument, pushVariadicArguments } from '@redis/client/dist/lib/commands/generic-transformers'; -import { transformBooleanArrayReply } from '@redis/client/dist/lib/commands/generic-transformers'; +import { CommandParser } from '@redis/client/lib/client/parser'; +import { RedisArgument, Command } from '@redis/client/lib/RESP/types'; +import { RedisVariadicArgument } from '@redis/client/lib/commands/generic-transformers'; +import { transformBooleanArrayReply } from '@redis/client/lib/commands/generic-transformers'; export interface BfInsertOptions { CAPACITY?: number; @@ -11,37 +12,38 @@ export interface BfInsertOptions { } export default { - FIRST_KEY_INDEX: 1, IS_READ_ONLY: false, - transformArguments( + parseCommand( + parser: CommandParser, key: RedisArgument, items: RedisVariadicArgument, options?: BfInsertOptions ) { - const args = ['BF.INSERT', key]; + parser.push('BF.INSERT'); + parser.pushKey(key); if (options?.CAPACITY !== undefined) { - args.push('CAPACITY', options.CAPACITY.toString()); + parser.push('CAPACITY', options.CAPACITY.toString()); } if (options?.ERROR !== undefined) { - args.push('ERROR', options.ERROR.toString()); + parser.push('ERROR', options.ERROR.toString()); } if (options?.EXPANSION !== undefined) { - args.push('EXPANSION', options.EXPANSION.toString()); + parser.push('EXPANSION', options.EXPANSION.toString()); } if (options?.NOCREATE) { - args.push('NOCREATE'); + parser.push('NOCREATE'); } if (options?.NONSCALING) { - args.push('NONSCALING'); + parser.push('NONSCALING'); } - args.push('ITEMS'); - return pushVariadicArguments(args, items); + parser.push('ITEMS'); + parser.pushVariadic(items); }, transformReply: transformBooleanArrayReply } as const satisfies Command; diff --git a/packages/bloom/lib/commands/bloom/LOADCHUNK.spec.ts b/packages/bloom/lib/commands/bloom/LOADCHUNK.spec.ts index f958863c0dc..40e24f96c39 100644 --- a/packages/bloom/lib/commands/bloom/LOADCHUNK.spec.ts +++ b/packages/bloom/lib/commands/bloom/LOADCHUNK.spec.ts @@ -2,11 +2,12 @@ import { strict as assert } from 'node:assert'; import testUtils, { GLOBAL } from '../../test-utils'; import LOADCHUNK from './LOADCHUNK'; import { RESP_TYPES } from '@redis/client'; +import { parseArgs } from '@redis/client/lib/commands/generic-transformers'; describe('BF.LOADCHUNK', () => { it('transformArguments', () => { assert.deepEqual( - LOADCHUNK.transformArguments('key', 0, ''), + parseArgs(LOADCHUNK, 'key', 0, ''), ['BF.LOADCHUNK', 'key', '0', ''] ); }); diff --git a/packages/bloom/lib/commands/bloom/LOADCHUNK.ts b/packages/bloom/lib/commands/bloom/LOADCHUNK.ts index feade2fac4c..47036e042af 100644 --- a/packages/bloom/lib/commands/bloom/LOADCHUNK.ts +++ b/packages/bloom/lib/commands/bloom/LOADCHUNK.ts @@ -1,10 +1,12 @@ -import { RedisArgument, SimpleStringReply, Command } from '@redis/client/dist/lib/RESP/types'; +import { CommandParser } from '@redis/client/lib/client/parser'; +import { RedisArgument, SimpleStringReply, Command } from '@redis/client/lib/RESP/types'; export default { - FIRST_KEY_INDEX: 1, IS_READ_ONLY: false, - transformArguments(key: RedisArgument, iterator: number, chunk: RedisArgument) { - return ['BF.LOADCHUNK', key, iterator.toString(), chunk]; + parseCommand(parser: CommandParser, key: RedisArgument, iterator: number, chunk: RedisArgument) { + parser.push('BF.LOADCHUNK'); + parser.pushKey(key); + parser.push(iterator.toString(), chunk); }, transformReply: undefined as unknown as () => SimpleStringReply<'OK'> } as const satisfies Command; diff --git a/packages/bloom/lib/commands/bloom/MADD.spec.ts b/packages/bloom/lib/commands/bloom/MADD.spec.ts index 5241a09485a..5eb39ee73d4 100644 --- a/packages/bloom/lib/commands/bloom/MADD.spec.ts +++ b/packages/bloom/lib/commands/bloom/MADD.spec.ts @@ -1,11 +1,12 @@ import { strict as assert } from 'node:assert'; import testUtils, { GLOBAL } from '../../test-utils'; import MADD from './MADD'; +import { parseArgs } from '@redis/client/lib/commands/generic-transformers'; describe('BF.MADD', () => { it('transformArguments', () => { assert.deepEqual( - MADD.transformArguments('key', ['1', '2']), + parseArgs(MADD, 'key', ['1', '2']), ['BF.MADD', 'key', '1', '2'] ); }); diff --git a/packages/bloom/lib/commands/bloom/MADD.ts b/packages/bloom/lib/commands/bloom/MADD.ts index afb122476fd..fda7419bf19 100644 --- a/packages/bloom/lib/commands/bloom/MADD.ts +++ b/packages/bloom/lib/commands/bloom/MADD.ts @@ -1,12 +1,14 @@ -import { RedisArgument, Command } from '@redis/client/dist/lib/RESP/types'; -import { RedisVariadicArgument, pushVariadicArguments } from '@redis/client/dist/lib/commands/generic-transformers'; -import { transformBooleanArrayReply } from '@redis/client/dist/lib/commands/generic-transformers'; +import { CommandParser } from '@redis/client/lib/client/parser'; +import { RedisArgument, Command } from '@redis/client/lib/RESP/types'; +import { RedisVariadicArgument } from '@redis/client/lib/commands/generic-transformers'; +import { transformBooleanArrayReply } from '@redis/client/lib/commands/generic-transformers'; export default { - FIRST_KEY_INDEX: 1, IS_READ_ONLY: false, - transformArguments(key: RedisArgument, items: RedisVariadicArgument) { - return pushVariadicArguments(['BF.MADD', key], items); + parseCommand(parser: CommandParser, key: RedisArgument, items: RedisVariadicArgument) { + parser.push('BF.MADD'); + parser.pushKey(key); + parser.pushVariadic(items); }, transformReply: transformBooleanArrayReply } as const satisfies Command; diff --git a/packages/bloom/lib/commands/bloom/MEXISTS.spec.ts b/packages/bloom/lib/commands/bloom/MEXISTS.spec.ts index 0f313ba636f..60c09b00f17 100644 --- a/packages/bloom/lib/commands/bloom/MEXISTS.spec.ts +++ b/packages/bloom/lib/commands/bloom/MEXISTS.spec.ts @@ -1,11 +1,12 @@ import { strict as assert } from 'node:assert'; import testUtils, { GLOBAL } from '../../test-utils'; import MEXISTS from './MEXISTS'; +import { parseArgs } from '@redis/client/lib/commands/generic-transformers'; describe('BF.MEXISTS', () => { it('transformArguments', () => { assert.deepEqual( - MEXISTS.transformArguments('key', ['1', '2']), + parseArgs(MEXISTS, 'key', ['1', '2']), ['BF.MEXISTS', 'key', '1', '2'] ); }); diff --git a/packages/bloom/lib/commands/bloom/MEXISTS.ts b/packages/bloom/lib/commands/bloom/MEXISTS.ts index a23b713b50c..acd85786f97 100644 --- a/packages/bloom/lib/commands/bloom/MEXISTS.ts +++ b/packages/bloom/lib/commands/bloom/MEXISTS.ts @@ -1,12 +1,14 @@ -import { RedisArgument, Command } from '@redis/client/dist/lib/RESP/types'; -import { RedisVariadicArgument, pushVariadicArguments } from '@redis/client/dist/lib/commands/generic-transformers'; -import { transformBooleanArrayReply } from '@redis/client/dist/lib/commands/generic-transformers'; +import { CommandParser } from '@redis/client/lib/client/parser'; +import { RedisArgument, Command } from '@redis/client/lib/RESP/types'; +import { RedisVariadicArgument } from '@redis/client/lib/commands/generic-transformers'; +import { transformBooleanArrayReply } from '@redis/client/lib/commands/generic-transformers'; export default { - FIRST_KEY_INDEX: 1, IS_READ_ONLY: true, - transformArguments(key: RedisArgument, items: RedisVariadicArgument) { - return pushVariadicArguments(['BF.MEXISTS', key], items); + parseCommand(parser: CommandParser, key: RedisArgument, items: RedisVariadicArgument) { + parser.push('BF.MEXISTS'); + parser.pushKey(key); + parser.pushVariadic(items); }, transformReply: transformBooleanArrayReply } as const satisfies Command; diff --git a/packages/bloom/lib/commands/bloom/RESERVE.spec.ts b/packages/bloom/lib/commands/bloom/RESERVE.spec.ts index caf40d4a48f..803577b350b 100644 --- a/packages/bloom/lib/commands/bloom/RESERVE.spec.ts +++ b/packages/bloom/lib/commands/bloom/RESERVE.spec.ts @@ -1,19 +1,20 @@ import { strict as assert } from 'node:assert'; import testUtils, { GLOBAL } from '../../test-utils'; import RESERVE from './RESERVE'; +import { parseArgs } from '@redis/client/lib/commands/generic-transformers'; describe('BF.RESERVE', () => { describe('transformArguments', () => { it('simple', () => { assert.deepEqual( - RESERVE.transformArguments('key', 0.01, 100), + parseArgs(RESERVE, 'key', 0.01, 100), ['BF.RESERVE', 'key', '0.01', '100'] ); }); it('with EXPANSION', () => { assert.deepEqual( - RESERVE.transformArguments('key', 0.01, 100, { + parseArgs(RESERVE, 'key', 0.01, 100, { EXPANSION: 1 }), ['BF.RESERVE', 'key', '0.01', '100', 'EXPANSION', '1'] @@ -22,7 +23,7 @@ describe('BF.RESERVE', () => { it('with NONSCALING', () => { assert.deepEqual( - RESERVE.transformArguments('key', 0.01, 100, { + parseArgs(RESERVE, 'key', 0.01, 100, { NONSCALING: true }), ['BF.RESERVE', 'key', '0.01', '100', 'NONSCALING'] @@ -31,7 +32,7 @@ describe('BF.RESERVE', () => { it('with EXPANSION and NONSCALING', () => { assert.deepEqual( - RESERVE.transformArguments('key', 0.01, 100, { + parseArgs(RESERVE, 'key', 0.01, 100, { EXPANSION: 1, NONSCALING: true }), diff --git a/packages/bloom/lib/commands/bloom/RESERVE.ts b/packages/bloom/lib/commands/bloom/RESERVE.ts index 6bccb1d1d13..aff155ab769 100644 --- a/packages/bloom/lib/commands/bloom/RESERVE.ts +++ b/packages/bloom/lib/commands/bloom/RESERVE.ts @@ -1,4 +1,5 @@ -import { RedisArgument, SimpleStringReply, Command } from '@redis/client/dist/lib/RESP/types'; +import { CommandParser } from '@redis/client/lib/client/parser'; +import { RedisArgument, SimpleStringReply, Command } from '@redis/client/lib/RESP/types'; export interface BfReserveOptions { EXPANSION?: number; @@ -6,25 +7,25 @@ export interface BfReserveOptions { } export default { - FIRST_KEY_INDEX: 1, IS_READ_ONLY: true, - transformArguments( + parseCommand( + parser: CommandParser, key: RedisArgument, errorRate: number, capacity: number, options?: BfReserveOptions ) { - const args = ['BF.RESERVE', key, errorRate.toString(), capacity.toString()]; + parser.push('BF.RESERVE'); + parser.pushKey(key); + parser.push(errorRate.toString(), capacity.toString()); if (options?.EXPANSION) { - args.push('EXPANSION', options.EXPANSION.toString()); + parser.push('EXPANSION', options.EXPANSION.toString()); } if (options?.NONSCALING) { - args.push('NONSCALING'); + parser.push('NONSCALING'); } - - return args; }, transformReply: undefined as unknown as () => SimpleStringReply<'OK'> } as const satisfies Command; diff --git a/packages/bloom/lib/commands/bloom/SCANDUMP.spec.ts b/packages/bloom/lib/commands/bloom/SCANDUMP.spec.ts index a7de98eabe7..a41a6e8e466 100644 --- a/packages/bloom/lib/commands/bloom/SCANDUMP.spec.ts +++ b/packages/bloom/lib/commands/bloom/SCANDUMP.spec.ts @@ -1,11 +1,12 @@ import { strict as assert } from 'node:assert'; import testUtils, { GLOBAL } from '../../test-utils'; import SCANDUMP from './SCANDUMP'; +import { parseArgs } from '@redis/client/lib/commands/generic-transformers'; describe('BF.SCANDUMP', () => { it('transformArguments', () => { assert.deepEqual( - SCANDUMP.transformArguments('key', 0), + parseArgs(SCANDUMP, 'key', 0), ['BF.SCANDUMP', 'key', '0'] ); }); diff --git a/packages/bloom/lib/commands/bloom/SCANDUMP.ts b/packages/bloom/lib/commands/bloom/SCANDUMP.ts index 588957b1743..37b1f57045c 100644 --- a/packages/bloom/lib/commands/bloom/SCANDUMP.ts +++ b/packages/bloom/lib/commands/bloom/SCANDUMP.ts @@ -1,10 +1,12 @@ -import { RedisArgument, TuplesReply, NumberReply, BlobStringReply, UnwrapReply, Command } from '@redis/client/dist/lib/RESP/types'; +import { CommandParser } from '@redis/client/lib/client/parser'; +import { RedisArgument, TuplesReply, NumberReply, BlobStringReply, UnwrapReply, Command } from '@redis/client/lib/RESP/types'; export default { - FIRST_KEY_INDEX: 1, IS_READ_ONLY: true, - transformArguments(key: RedisArgument, iterator: number) { - return ['BF.SCANDUMP', key, iterator.toString()]; + parseCommand(parser: CommandParser, key: RedisArgument, iterator: number) { + parser.push('BF.SCANDUMP'); + parser.pushKey(key); + parser.push(iterator.toString()); }, transformReply(reply: UnwrapReply>) { return { diff --git a/packages/bloom/lib/commands/bloom/index.ts b/packages/bloom/lib/commands/bloom/index.ts index a93f79c9c56..e87f57220dd 100644 --- a/packages/bloom/lib/commands/bloom/index.ts +++ b/packages/bloom/lib/commands/bloom/index.ts @@ -1,4 +1,4 @@ -import type { RedisCommands, TypeMapping } from '@redis/client/dist/lib/RESP/types'; +import type { RedisCommands, TypeMapping } from '@redis/client/lib/RESP/types'; import ADD from './ADD'; import CARD from './CARD'; diff --git a/packages/bloom/lib/commands/count-min-sketch/INCRBY.spec.ts b/packages/bloom/lib/commands/count-min-sketch/INCRBY.spec.ts index 1d2921cab75..44ccaf6046d 100644 --- a/packages/bloom/lib/commands/count-min-sketch/INCRBY.spec.ts +++ b/packages/bloom/lib/commands/count-min-sketch/INCRBY.spec.ts @@ -1,12 +1,13 @@ import { strict as assert } from 'node:assert'; import testUtils, { GLOBAL } from '../../test-utils'; import INCRBY from './INCRBY'; +import { parseArgs } from '@redis/client/lib/commands/generic-transformers'; describe('CMS.INCRBY', () => { describe('transformArguments', () => { it('single item', () => { assert.deepEqual( - INCRBY.transformArguments('key', { + parseArgs(INCRBY, 'key', { item: 'item', incrementBy: 1 }), @@ -16,7 +17,7 @@ describe('CMS.INCRBY', () => { it('multiple items', () => { assert.deepEqual( - INCRBY.transformArguments('key', [{ + parseArgs(INCRBY, 'key', [{ item: 'a', incrementBy: 1 }, { diff --git a/packages/bloom/lib/commands/count-min-sketch/INCRBY.ts b/packages/bloom/lib/commands/count-min-sketch/INCRBY.ts index 1dfbabbaa49..39cc52d31de 100644 --- a/packages/bloom/lib/commands/count-min-sketch/INCRBY.ts +++ b/packages/bloom/lib/commands/count-min-sketch/INCRBY.ts @@ -1,4 +1,5 @@ -import { RedisArgument, ArrayReply, NumberReply, Command } from '@redis/client/dist/lib/RESP/types'; +import { CommandParser } from '@redis/client/lib/client/parser'; +import { RedisArgument, ArrayReply, NumberReply, Command } from '@redis/client/lib/RESP/types'; export interface BfIncrByItem { item: RedisArgument; @@ -6,27 +7,26 @@ export interface BfIncrByItem { } export default { - FIRST_KEY_INDEX: 1, IS_READ_ONLY: false, - transformArguments( + parseCommand( + parser: CommandParser, key: RedisArgument, items: BfIncrByItem | Array ) { - const args = ['CMS.INCRBY', key]; + parser.push('CMS.INCRBY'); + parser.pushKey(key); if (Array.isArray(items)) { for (const item of items) { - pushIncrByItem(args, item); + pushIncrByItem(parser, item); } } else { - pushIncrByItem(args, items); + pushIncrByItem(parser, items); } - - return args; }, transformReply: undefined as unknown as () => ArrayReply } as const satisfies Command; -function pushIncrByItem(args: Array, { item, incrementBy }: BfIncrByItem): void { - args.push(item, incrementBy.toString()); +function pushIncrByItem(parser: CommandParser, { item, incrementBy }: BfIncrByItem): void { + parser.push(item, incrementBy.toString()); } diff --git a/packages/bloom/lib/commands/count-min-sketch/INFO.spec.ts b/packages/bloom/lib/commands/count-min-sketch/INFO.spec.ts index e650d78d2ed..cbc8065016a 100644 --- a/packages/bloom/lib/commands/count-min-sketch/INFO.spec.ts +++ b/packages/bloom/lib/commands/count-min-sketch/INFO.spec.ts @@ -1,11 +1,12 @@ import { strict as assert } from 'node:assert'; import testUtils, { GLOBAL } from '../../test-utils'; import INFO from './INFO'; +import { parseArgs } from '@redis/client/lib/commands/generic-transformers'; describe('CMS.INFO', () => { it('transformArguments', () => { assert.deepEqual( - INFO.transformArguments('key'), + parseArgs(INFO, 'key'), ['CMS.INFO', 'key'] ); }); diff --git a/packages/bloom/lib/commands/count-min-sketch/INFO.ts b/packages/bloom/lib/commands/count-min-sketch/INFO.ts index e4aae5bf47b..9b77409f2d1 100644 --- a/packages/bloom/lib/commands/count-min-sketch/INFO.ts +++ b/packages/bloom/lib/commands/count-min-sketch/INFO.ts @@ -1,4 +1,5 @@ -import { RedisArgument, TuplesToMapReply, NumberReply, UnwrapReply, Resp2Reply, Command, SimpleStringReply, TypeMapping } from '@redis/client/dist/lib/RESP/types'; +import { CommandParser } from '@redis/client/lib/client/parser'; +import { RedisArgument, TuplesToMapReply, NumberReply, UnwrapReply, Resp2Reply, Command, SimpleStringReply, TypeMapping } from '@redis/client/lib/RESP/types'; import { transformInfoV2Reply } from '../bloom'; export type CmsInfoReplyMap = TuplesToMapReply<[ @@ -14,10 +15,10 @@ export interface CmsInfoReply { } export default { - FIRST_KEY_INDEX: 1, IS_READ_ONLY: true, - transformArguments(key: RedisArgument) { - return ['CMS.INFO', key]; + parseCommand(parser: CommandParser, key: RedisArgument) { + parser.push('CMS.INFO'); + parser.pushKey(key); }, transformReply: { 2: (reply: UnwrapReply>, _, typeMapping?: TypeMapping): CmsInfoReply => { diff --git a/packages/bloom/lib/commands/count-min-sketch/INITBYDIM.spec.ts b/packages/bloom/lib/commands/count-min-sketch/INITBYDIM.spec.ts index a3d27c17df3..9fa1652a2e8 100644 --- a/packages/bloom/lib/commands/count-min-sketch/INITBYDIM.spec.ts +++ b/packages/bloom/lib/commands/count-min-sketch/INITBYDIM.spec.ts @@ -1,11 +1,12 @@ import { strict as assert } from 'node:assert'; import testUtils, { GLOBAL } from '../../test-utils'; import INITBYDIM from './INITBYDIM'; +import { parseArgs } from '@redis/client/lib/commands/generic-transformers'; describe('CMS.INITBYDIM', () => { it('transformArguments', () => { assert.deepEqual( - INITBYDIM.transformArguments('key', 1000, 5), + parseArgs(INITBYDIM, 'key', 1000, 5), ['CMS.INITBYDIM', 'key', '1000', '5'] ); }); diff --git a/packages/bloom/lib/commands/count-min-sketch/INITBYDIM.ts b/packages/bloom/lib/commands/count-min-sketch/INITBYDIM.ts index 60790d421e4..cd295d0696e 100644 --- a/packages/bloom/lib/commands/count-min-sketch/INITBYDIM.ts +++ b/packages/bloom/lib/commands/count-min-sketch/INITBYDIM.ts @@ -1,10 +1,12 @@ -import { RedisArgument, SimpleStringReply, Command } from '@redis/client/dist/lib/RESP/types'; +import { CommandParser } from '@redis/client/lib/client/parser'; +import { RedisArgument, SimpleStringReply, Command } from '@redis/client/lib/RESP/types'; export default { - FIRST_KEY_INDEX: 1, IS_READ_ONLY: false, - transformArguments(key: RedisArgument, width: number, depth: number) { - return ['CMS.INITBYDIM', key, width.toString(), depth.toString()]; + parseCommand(parser: CommandParser, key: RedisArgument, width: number, depth: number) { + parser.push('CMS.INITBYDIM'); + parser.pushKey(key); + parser.push(width.toString(), depth.toString()); }, transformReply: undefined as unknown as () => SimpleStringReply<'OK'> } as const satisfies Command; diff --git a/packages/bloom/lib/commands/count-min-sketch/INITBYPROB.spec.ts b/packages/bloom/lib/commands/count-min-sketch/INITBYPROB.spec.ts index 8df62020e89..b59bc14494f 100644 --- a/packages/bloom/lib/commands/count-min-sketch/INITBYPROB.spec.ts +++ b/packages/bloom/lib/commands/count-min-sketch/INITBYPROB.spec.ts @@ -1,11 +1,12 @@ import { strict as assert } from 'node:assert'; import testUtils, { GLOBAL } from '../../test-utils'; import INITBYPROB from './INITBYPROB'; +import { parseArgs } from '@redis/client/lib/commands/generic-transformers'; describe('CMS.INITBYPROB', () => { it('transformArguments', () => { assert.deepEqual( - INITBYPROB.transformArguments('key', 0.001, 0.01), + parseArgs(INITBYPROB, 'key', 0.001, 0.01), ['CMS.INITBYPROB', 'key', '0.001', '0.01'] ); }); diff --git a/packages/bloom/lib/commands/count-min-sketch/INITBYPROB.ts b/packages/bloom/lib/commands/count-min-sketch/INITBYPROB.ts index 7b21755f17d..e7e85d4100b 100644 --- a/packages/bloom/lib/commands/count-min-sketch/INITBYPROB.ts +++ b/packages/bloom/lib/commands/count-min-sketch/INITBYPROB.ts @@ -1,10 +1,12 @@ -import { RedisArgument, SimpleStringReply, Command } from '@redis/client/dist/lib/RESP/types'; +import { CommandParser } from '@redis/client/lib/client/parser'; +import { RedisArgument, SimpleStringReply, Command } from '@redis/client/lib/RESP/types'; export default { - FIRST_KEY_INDEX: 1, IS_READ_ONLY: false, - transformArguments(key: RedisArgument, error: number, probability: number) { - return ['CMS.INITBYPROB', key, error.toString(), probability.toString()]; + parseCommand(parser: CommandParser, key: RedisArgument, error: number, probability: number) { + parser.push('CMS.INITBYPROB'); + parser.pushKey(key); + parser.push(error.toString(), probability.toString()); }, transformReply: undefined as unknown as () => SimpleStringReply<'OK'> } as const satisfies Command; diff --git a/packages/bloom/lib/commands/count-min-sketch/MERGE.spec.ts b/packages/bloom/lib/commands/count-min-sketch/MERGE.spec.ts index eef4bd403ae..03e3d5c6364 100644 --- a/packages/bloom/lib/commands/count-min-sketch/MERGE.spec.ts +++ b/packages/bloom/lib/commands/count-min-sketch/MERGE.spec.ts @@ -1,19 +1,20 @@ import { strict as assert } from 'node:assert'; import testUtils, { GLOBAL } from '../../test-utils'; import MERGE from './MERGE'; +import { parseArgs } from '@redis/client/lib/commands/generic-transformers'; describe('CMS.MERGE', () => { describe('transformArguments', () => { it('without WEIGHTS', () => { assert.deepEqual( - MERGE.transformArguments('destination', ['source']), + parseArgs(MERGE, 'destination', ['source']), ['CMS.MERGE', 'destination', '1', 'source'] ); }); it('with WEIGHTS', () => { assert.deepEqual( - MERGE.transformArguments('destination', [{ + parseArgs(MERGE, 'destination', [{ name: 'source', weight: 1 }]), diff --git a/packages/bloom/lib/commands/count-min-sketch/MERGE.ts b/packages/bloom/lib/commands/count-min-sketch/MERGE.ts index 2e63065d1cc..cc0fc929070 100644 --- a/packages/bloom/lib/commands/count-min-sketch/MERGE.ts +++ b/packages/bloom/lib/commands/count-min-sketch/MERGE.ts @@ -1,4 +1,5 @@ -import { RedisArgument, SimpleStringReply, Command } from '@redis/client/dist/lib/RESP/types'; +import { CommandParser } from '@redis/client/lib/client/parser'; +import { RedisArgument, SimpleStringReply, Command } from '@redis/client/lib/RESP/types'; interface BfMergeSketch { name: RedisArgument; @@ -8,26 +9,27 @@ interface BfMergeSketch { export type BfMergeSketches = Array | Array; export default { - FIRST_KEY_INDEX: 1, IS_READ_ONLY: false, - transformArguments( + parseCommand( + parser: CommandParser, destination: RedisArgument, source: BfMergeSketches ) { - let args = ['CMS.MERGE', destination, source.length.toString()]; + parser.push('CMS.MERGE'); + parser.pushKey(destination); + parser.push(source.length.toString()); if (isPlainSketches(source)) { - args = args.concat(source); + parser.pushVariadic(source); } else { - const { length } = args; - args[length + source.length] = 'WEIGHTS'; for (let i = 0; i < source.length; i++) { - args[length + i] = source[i].name; - args[length + source.length + i + 1] = source[i].weight.toString(); + parser.push(source[i].name); + } + parser.push('WEIGHTS'); + for (let i = 0; i < source.length; i++) { + parser.push(source[i].weight.toString()) } } - - return args; }, transformReply: undefined as unknown as () => SimpleStringReply<'OK'> } as const satisfies Command; diff --git a/packages/bloom/lib/commands/count-min-sketch/QUERY.spec.ts b/packages/bloom/lib/commands/count-min-sketch/QUERY.spec.ts index cc9c913b563..e12a519e962 100644 --- a/packages/bloom/lib/commands/count-min-sketch/QUERY.spec.ts +++ b/packages/bloom/lib/commands/count-min-sketch/QUERY.spec.ts @@ -1,11 +1,12 @@ import { strict as assert } from 'node:assert'; import testUtils, { GLOBAL } from '../../test-utils'; import QUERY from './QUERY'; +import { parseArgs } from '@redis/client/lib/commands/generic-transformers'; describe('CMS.QUERY', () => { it('transformArguments', () => { assert.deepEqual( - QUERY.transformArguments('key', 'item'), + parseArgs(QUERY, 'key', 'item'), ['CMS.QUERY', 'key', 'item'] ); }); diff --git a/packages/bloom/lib/commands/count-min-sketch/QUERY.ts b/packages/bloom/lib/commands/count-min-sketch/QUERY.ts index 5d2905300b1..54d838c1935 100644 --- a/packages/bloom/lib/commands/count-min-sketch/QUERY.ts +++ b/packages/bloom/lib/commands/count-min-sketch/QUERY.ts @@ -1,11 +1,13 @@ -import { ArrayReply, NumberReply, Command, RedisArgument } from '@redis/client/dist/lib/RESP/types'; -import { RedisVariadicArgument, pushVariadicArguments } from '@redis/client/dist/lib/commands/generic-transformers'; +import { CommandParser } from '@redis/client/lib/client/parser'; +import { ArrayReply, NumberReply, Command, RedisArgument } from '@redis/client/lib/RESP/types'; +import { RedisVariadicArgument } from '@redis/client/lib/commands/generic-transformers'; export default { - FIRST_KEY_INDEX: 1, IS_READ_ONLY: true, - transformArguments(key: RedisArgument, items: RedisVariadicArgument) { - return pushVariadicArguments(['CMS.QUERY', key], items); + parseCommand(parser: CommandParser, key: RedisArgument, items: RedisVariadicArgument) { + parser.push('CMS.QUERY'); + parser.pushKey(key); + parser.pushVariadic(items); }, transformReply: undefined as unknown as () => ArrayReply } as const satisfies Command; diff --git a/packages/bloom/lib/commands/count-min-sketch/index.ts b/packages/bloom/lib/commands/count-min-sketch/index.ts index 4f0f395ca3d..1132a7524e1 100644 --- a/packages/bloom/lib/commands/count-min-sketch/index.ts +++ b/packages/bloom/lib/commands/count-min-sketch/index.ts @@ -1,4 +1,4 @@ -import type { RedisCommands } from '@redis/client/dist/lib/RESP/types'; +import type { RedisCommands } from '@redis/client/lib/RESP/types'; import INCRBY from './INCRBY'; import INFO from './INFO'; import INITBYDIM from './INITBYDIM'; diff --git a/packages/bloom/lib/commands/cuckoo/ADD.spec.ts b/packages/bloom/lib/commands/cuckoo/ADD.spec.ts index fa610cc6666..7fa518fea84 100644 --- a/packages/bloom/lib/commands/cuckoo/ADD.spec.ts +++ b/packages/bloom/lib/commands/cuckoo/ADD.spec.ts @@ -1,11 +1,12 @@ import { strict as assert } from 'node:assert'; import testUtils, { GLOBAL } from '../../test-utils'; import ADD from './ADD'; +import { parseArgs } from '@redis/client/lib/commands/generic-transformers'; describe('CF.ADD', () => { it('transformArguments', () => { assert.deepEqual( - ADD.transformArguments('key', 'item'), + parseArgs(ADD, 'key', 'item'), ['CF.ADD', 'key', 'item'] ); }); diff --git a/packages/bloom/lib/commands/cuckoo/ADD.ts b/packages/bloom/lib/commands/cuckoo/ADD.ts index 52e98a801d4..3d0dc77be2d 100644 --- a/packages/bloom/lib/commands/cuckoo/ADD.ts +++ b/packages/bloom/lib/commands/cuckoo/ADD.ts @@ -1,11 +1,13 @@ -import { RedisArgument, Command } from '@redis/client/dist/lib/RESP/types'; -import { transformBooleanReply } from '@redis/client/dist/lib/commands/generic-transformers'; +import { CommandParser } from '@redis/client/lib/client/parser'; +import { RedisArgument, Command } from '@redis/client/lib/RESP/types'; +import { transformBooleanReply } from '@redis/client/lib/commands/generic-transformers'; export default { - FIRST_KEY_INDEX: 1, IS_READ_ONLY: false, - transformArguments(key: RedisArgument, item: RedisArgument) { - return ['CF.ADD', key, item]; + parseCommand(parser: CommandParser, key: RedisArgument, item: RedisArgument) { + parser.push('CF.ADD'); + parser.pushKey(key); + parser.push(item); }, transformReply: transformBooleanReply } as const satisfies Command; diff --git a/packages/bloom/lib/commands/cuckoo/ADDNX.spec.ts b/packages/bloom/lib/commands/cuckoo/ADDNX.spec.ts index f50ad87dc15..c142733ce40 100644 --- a/packages/bloom/lib/commands/cuckoo/ADDNX.spec.ts +++ b/packages/bloom/lib/commands/cuckoo/ADDNX.spec.ts @@ -1,11 +1,12 @@ import { strict as assert } from 'node:assert'; import testUtils, { GLOBAL } from '../../test-utils'; import ADDNX from './ADDNX'; +import { parseArgs } from '@redis/client/lib/commands/generic-transformers'; describe('CF.ADDNX', () => { it('transformArguments', () => { assert.deepEqual( - ADDNX.transformArguments('key', 'item'), + parseArgs(ADDNX, 'key', 'item'), ['CF.ADDNX', 'key', 'item'] ); }); diff --git a/packages/bloom/lib/commands/cuckoo/ADDNX.ts b/packages/bloom/lib/commands/cuckoo/ADDNX.ts index c739077ee46..f358f1581ec 100644 --- a/packages/bloom/lib/commands/cuckoo/ADDNX.ts +++ b/packages/bloom/lib/commands/cuckoo/ADDNX.ts @@ -1,11 +1,13 @@ -import { RedisArgument, Command } from '@redis/client/dist/lib/RESP/types'; -import { transformBooleanReply } from '@redis/client/dist/lib/commands/generic-transformers'; +import { CommandParser } from '@redis/client/lib/client/parser'; +import { RedisArgument, Command } from '@redis/client/lib/RESP/types'; +import { transformBooleanReply } from '@redis/client/lib/commands/generic-transformers'; export default { - FIRST_KEY_INDEX: 1, IS_READ_ONLY: false, - transformArguments(key: RedisArgument, item: RedisArgument) { - return ['CF.ADDNX', key, item]; + parseCommand(parser: CommandParser, key: RedisArgument, item: RedisArgument) { + parser.push('CF.ADDNX'); + parser.pushKey(key); + parser.push(item); }, transformReply: transformBooleanReply } as const satisfies Command; diff --git a/packages/bloom/lib/commands/cuckoo/COUNT.spec.ts b/packages/bloom/lib/commands/cuckoo/COUNT.spec.ts index ff8d40f064e..9393494d852 100644 --- a/packages/bloom/lib/commands/cuckoo/COUNT.spec.ts +++ b/packages/bloom/lib/commands/cuckoo/COUNT.spec.ts @@ -1,11 +1,12 @@ import { strict as assert } from 'node:assert'; import testUtils, { GLOBAL } from '../../test-utils'; import COUNT from './COUNT'; +import { parseArgs } from '@redis/client/lib/commands/generic-transformers'; describe('CF.COUNT', () => { it('transformArguments', () => { assert.deepEqual( - COUNT.transformArguments('key', 'item'), + parseArgs(COUNT, 'key', 'item'), ['CF.COUNT', 'key', 'item'] ); }); diff --git a/packages/bloom/lib/commands/cuckoo/COUNT.ts b/packages/bloom/lib/commands/cuckoo/COUNT.ts index 2284edff174..512b0143272 100644 --- a/packages/bloom/lib/commands/cuckoo/COUNT.ts +++ b/packages/bloom/lib/commands/cuckoo/COUNT.ts @@ -1,10 +1,12 @@ -import { RedisArgument, NumberReply, Command } from '@redis/client/dist/lib/RESP/types'; +import { CommandParser } from '@redis/client/lib/client/parser'; +import { RedisArgument, NumberReply, Command } from '@redis/client/lib/RESP/types'; export default { - FIRST_KEY_INDEX: 1, IS_READ_ONLY: true, - transformArguments(key: RedisArgument, item: RedisArgument) { - return ['CF.COUNT', key, item]; + parseCommand(parser: CommandParser, key: RedisArgument, item: RedisArgument) { + parser.push('CF.COUNT'); + parser.pushKey(key); + parser.push(item); }, transformReply: undefined as unknown as () => NumberReply } as const satisfies Command; diff --git a/packages/bloom/lib/commands/cuckoo/DEL.spec.ts b/packages/bloom/lib/commands/cuckoo/DEL.spec.ts index e02b5636e12..41ed653bfc9 100644 --- a/packages/bloom/lib/commands/cuckoo/DEL.spec.ts +++ b/packages/bloom/lib/commands/cuckoo/DEL.spec.ts @@ -1,11 +1,12 @@ import { strict as assert } from 'node:assert'; import testUtils, { GLOBAL } from '../../test-utils'; import DEL from './DEL'; +import { parseArgs } from '@redis/client/lib/commands/generic-transformers'; describe('CF.DEL', () => { it('transformArguments', () => { assert.deepEqual( - DEL.transformArguments('key', 'item'), + parseArgs(DEL, 'key', 'item'), ['CF.DEL', 'key', 'item'] ); }); diff --git a/packages/bloom/lib/commands/cuckoo/DEL.ts b/packages/bloom/lib/commands/cuckoo/DEL.ts index 0af8ebc851b..0b2bdaea990 100644 --- a/packages/bloom/lib/commands/cuckoo/DEL.ts +++ b/packages/bloom/lib/commands/cuckoo/DEL.ts @@ -1,11 +1,13 @@ -import { RedisArgument, Command } from '@redis/client/dist/lib/RESP/types'; -import { transformBooleanReply } from '@redis/client/dist/lib/commands/generic-transformers'; +import { CommandParser } from '@redis/client/lib/client/parser'; +import { RedisArgument, Command } from '@redis/client/lib/RESP/types'; +import { transformBooleanReply } from '@redis/client/lib/commands/generic-transformers'; export default { - FIRST_KEY_INDEX: 1, IS_READ_ONLY: false, - transformArguments(key: RedisArgument, item: RedisArgument) { - return ['CF.DEL', key, item]; + parseCommand(parser: CommandParser, key: RedisArgument, item: RedisArgument) { + parser.push('CF.DEL'); + parser.pushKey(key); + parser.push(item); }, transformReply: transformBooleanReply } as const satisfies Command; diff --git a/packages/bloom/lib/commands/cuckoo/EXISTS.spec.ts b/packages/bloom/lib/commands/cuckoo/EXISTS.spec.ts index 899c11e8394..f77a9d69eff 100644 --- a/packages/bloom/lib/commands/cuckoo/EXISTS.spec.ts +++ b/packages/bloom/lib/commands/cuckoo/EXISTS.spec.ts @@ -1,11 +1,12 @@ import { strict as assert } from 'node:assert'; import testUtils, { GLOBAL } from '../../test-utils'; import EXISTS from './EXISTS'; +import { parseArgs } from '@redis/client/lib/commands/generic-transformers'; describe('CF.EXISTS', () => { it('transformArguments', () => { assert.deepEqual( - EXISTS.transformArguments('key', 'item'), + parseArgs(EXISTS, 'key', 'item'), ['CF.EXISTS', 'key', 'item'] ); }); diff --git a/packages/bloom/lib/commands/cuckoo/EXISTS.ts b/packages/bloom/lib/commands/cuckoo/EXISTS.ts index 8fd74ca47ca..ef93462990b 100644 --- a/packages/bloom/lib/commands/cuckoo/EXISTS.ts +++ b/packages/bloom/lib/commands/cuckoo/EXISTS.ts @@ -1,11 +1,13 @@ -import { RedisArgument, Command } from '@redis/client/dist/lib/RESP/types'; -import { transformBooleanReply } from '@redis/client/dist/lib/commands/generic-transformers'; +import { CommandParser } from '@redis/client/lib/client/parser'; +import { RedisArgument, Command } from '@redis/client/lib/RESP/types'; +import { transformBooleanReply } from '@redis/client/lib/commands/generic-transformers'; export default { - FIRST_KEY_INDEX: 1, IS_READ_ONLY: false, - transformArguments(key: RedisArgument, item: RedisArgument) { - return ['CF.EXISTS', key, item]; + parseCommand(parser: CommandParser, key: RedisArgument, item: RedisArgument) { + parser.push('CF.EXISTS'); + parser.pushKey(key); + parser.push(item); }, transformReply: transformBooleanReply } as const satisfies Command; diff --git a/packages/bloom/lib/commands/cuckoo/INFO.spec.ts b/packages/bloom/lib/commands/cuckoo/INFO.spec.ts index 222177c4650..c5503ed113b 100644 --- a/packages/bloom/lib/commands/cuckoo/INFO.spec.ts +++ b/packages/bloom/lib/commands/cuckoo/INFO.spec.ts @@ -1,11 +1,12 @@ import { strict as assert } from 'node:assert'; import testUtils, { GLOBAL } from '../../test-utils'; import INFO from './INFO'; +import { parseArgs } from '@redis/client/lib/commands/generic-transformers'; describe('CF.INFO', () => { it('transformArguments', () => { assert.deepEqual( - INFO.transformArguments('cuckoo'), + parseArgs(INFO, 'cuckoo'), ['CF.INFO', 'cuckoo'] ); }); diff --git a/packages/bloom/lib/commands/cuckoo/INFO.ts b/packages/bloom/lib/commands/cuckoo/INFO.ts index 70a7d80c6f2..15972b206ff 100644 --- a/packages/bloom/lib/commands/cuckoo/INFO.ts +++ b/packages/bloom/lib/commands/cuckoo/INFO.ts @@ -1,4 +1,5 @@ -import { RedisArgument, Command, NumberReply, TuplesToMapReply, UnwrapReply, Resp2Reply, SimpleStringReply, TypeMapping } from '@redis/client/dist/lib/RESP/types'; +import { CommandParser } from '@redis/client/lib/client/parser'; +import { RedisArgument, Command, NumberReply, TuplesToMapReply, UnwrapReply, Resp2Reply, SimpleStringReply, TypeMapping } from '@redis/client/lib/RESP/types'; import { transformInfoV2Reply } from '../bloom'; export type CfInfoReplyMap = TuplesToMapReply<[ @@ -13,10 +14,10 @@ export type CfInfoReplyMap = TuplesToMapReply<[ ]>; export default { - FIRST_KEY_INDEX: 1, IS_READ_ONLY: true, - transformArguments(key: RedisArgument) { - return ['CF.INFO', key]; + parseCommand(parser: CommandParser, key: RedisArgument) { + parser.push('CF.INFO'); + parser.pushKey(key); }, transformReply: { 2: (reply: UnwrapReply>, _, typeMapping?: TypeMapping): CfInfoReplyMap => { diff --git a/packages/bloom/lib/commands/cuckoo/INSERT.spec.ts b/packages/bloom/lib/commands/cuckoo/INSERT.spec.ts index 096cf547098..dc2bd574517 100644 --- a/packages/bloom/lib/commands/cuckoo/INSERT.spec.ts +++ b/packages/bloom/lib/commands/cuckoo/INSERT.spec.ts @@ -1,11 +1,12 @@ import { strict as assert } from 'node:assert'; import testUtils, { GLOBAL } from '../../test-utils'; import INSERT from './INSERT'; +import { parseArgs } from '@redis/client/lib/commands/generic-transformers'; describe('CF.INSERT', () => { it('transformArguments', () => { assert.deepEqual( - INSERT.transformArguments('key', 'item', { + parseArgs(INSERT, 'key', 'item', { CAPACITY: 100, NOCREATE: true }), diff --git a/packages/bloom/lib/commands/cuckoo/INSERT.ts b/packages/bloom/lib/commands/cuckoo/INSERT.ts index d6df64eea1a..75534e0a7fa 100644 --- a/packages/bloom/lib/commands/cuckoo/INSERT.ts +++ b/packages/bloom/lib/commands/cuckoo/INSERT.ts @@ -1,34 +1,37 @@ -import { Command, RedisArgument } from '@redis/client/dist/lib/RESP/types'; -import { RedisVariadicArgument, pushVariadicArguments, transformBooleanArrayReply } from '@redis/client/dist/lib/commands/generic-transformers'; +import { CommandParser } from '@redis/client/lib/client/parser'; +import { Command, RedisArgument } from '@redis/client/lib/RESP/types'; +import { RedisVariadicArgument, transformBooleanArrayReply } from '@redis/client/lib/commands/generic-transformers'; export interface CfInsertOptions { CAPACITY?: number; NOCREATE?: boolean; } -export function transofrmCfInsertArguments( - command: RedisArgument, +export function parseCfInsertArguments( + parser: CommandParser, key: RedisArgument, items: RedisVariadicArgument, options?: CfInsertOptions ) { - const args = [command, key]; + parser.pushKey(key); if (options?.CAPACITY !== undefined) { - args.push('CAPACITY', options.CAPACITY.toString()); + parser.push('CAPACITY', options.CAPACITY.toString()); } if (options?.NOCREATE) { - args.push('NOCREATE'); + parser.push('NOCREATE'); } - args.push('ITEMS'); - return pushVariadicArguments(args, items); + parser.push('ITEMS'); + parser.pushVariadic(items); } export default { - FIRST_KEY_INDEX: 1, IS_READ_ONLY: false, - transformArguments: transofrmCfInsertArguments.bind(undefined, 'CF.INSERT'), + parseCommand(...args: Parameters) { + args[0].push('CF.INSERT'); + parseCfInsertArguments(...args); + }, transformReply: transformBooleanArrayReply } as const satisfies Command; diff --git a/packages/bloom/lib/commands/cuckoo/INSERTNX.spec.ts b/packages/bloom/lib/commands/cuckoo/INSERTNX.spec.ts index 0f874278220..648d9be7ac8 100644 --- a/packages/bloom/lib/commands/cuckoo/INSERTNX.spec.ts +++ b/packages/bloom/lib/commands/cuckoo/INSERTNX.spec.ts @@ -1,11 +1,12 @@ import { strict as assert } from 'node:assert'; import testUtils, { GLOBAL } from '../../test-utils'; import INSERTNX from './INSERTNX'; +import { parseArgs } from '@redis/client/lib/commands/generic-transformers'; describe('CF.INSERTNX', () => { it('transformArguments', () => { assert.deepEqual( - INSERTNX.transformArguments('key', 'item', { + parseArgs(INSERTNX, 'key', 'item', { CAPACITY: 100, NOCREATE: true }), diff --git a/packages/bloom/lib/commands/cuckoo/INSERTNX.ts b/packages/bloom/lib/commands/cuckoo/INSERTNX.ts index 5cd56e794f9..581cfcd9e60 100644 --- a/packages/bloom/lib/commands/cuckoo/INSERTNX.ts +++ b/packages/bloom/lib/commands/cuckoo/INSERTNX.ts @@ -1,9 +1,11 @@ -import { Command } from '@redis/client/dist/lib/RESP/types'; -import INSERT, { transofrmCfInsertArguments } from './INSERT'; +import { Command } from '@redis/client/lib/RESP/types'; +import INSERT, { parseCfInsertArguments } from './INSERT'; export default { - FIRST_KEY_INDEX: INSERT.FIRST_KEY_INDEX, IS_READ_ONLY: INSERT.IS_READ_ONLY, - transformArguments: transofrmCfInsertArguments.bind(undefined, 'CF.INSERTNX'), + parseCommand(...args: Parameters) { + args[0].push('CF.INSERTNX'); + parseCfInsertArguments(...args); + }, transformReply: INSERT.transformReply } as const satisfies Command; diff --git a/packages/bloom/lib/commands/cuckoo/LOADCHUNK.spec.ts b/packages/bloom/lib/commands/cuckoo/LOADCHUNK.spec.ts index 5b880e0dd9d..5415c787dda 100644 --- a/packages/bloom/lib/commands/cuckoo/LOADCHUNK.spec.ts +++ b/packages/bloom/lib/commands/cuckoo/LOADCHUNK.spec.ts @@ -2,11 +2,12 @@ import { strict as assert } from 'node:assert'; import testUtils, { GLOBAL } from '../../test-utils'; import LOADCHUNK from './LOADCHUNK'; import { RESP_TYPES } from '@redis/client'; +import { parseArgs } from '@redis/client/lib/commands/generic-transformers'; describe('CF.LOADCHUNK', () => { it('transformArguments', () => { assert.deepEqual( - LOADCHUNK.transformArguments('item', 0, ''), + parseArgs(LOADCHUNK, 'item', 0, ''), ['CF.LOADCHUNK', 'item', '0', ''] ); }); diff --git a/packages/bloom/lib/commands/cuckoo/LOADCHUNK.ts b/packages/bloom/lib/commands/cuckoo/LOADCHUNK.ts index 08cb749b595..420774a6504 100644 --- a/packages/bloom/lib/commands/cuckoo/LOADCHUNK.ts +++ b/packages/bloom/lib/commands/cuckoo/LOADCHUNK.ts @@ -1,10 +1,12 @@ -import { SimpleStringReply, Command, RedisArgument } from '@redis/client/dist/lib/RESP/types'; +import { CommandParser } from '@redis/client/lib/client/parser'; +import { SimpleStringReply, Command, RedisArgument } from '@redis/client/lib/RESP/types'; export default { - FIRST_KEY_INDEX: 1, IS_READ_ONLY: false, - transformArguments(key: RedisArgument, iterator: number, chunk: RedisArgument) { - return ['CF.LOADCHUNK', key, iterator.toString(), chunk]; + parseCommand(parser: CommandParser, key: RedisArgument, iterator: number, chunk: RedisArgument) { + parser.push('CF.LOADCHUNK'); + parser.pushKey(key); + parser.push(iterator.toString(), chunk); }, transformReply: undefined as unknown as () => SimpleStringReply<'OK'> } as const satisfies Command; diff --git a/packages/bloom/lib/commands/cuckoo/RESERVE.spec.ts b/packages/bloom/lib/commands/cuckoo/RESERVE.spec.ts index b8f2556bc4f..53546e4156e 100644 --- a/packages/bloom/lib/commands/cuckoo/RESERVE.spec.ts +++ b/packages/bloom/lib/commands/cuckoo/RESERVE.spec.ts @@ -1,19 +1,20 @@ import { strict as assert } from 'node:assert'; import testUtils, { GLOBAL } from '../../test-utils'; import RESERVE from './RESERVE'; +import { parseArgs } from '@redis/client/lib/commands/generic-transformers'; describe('CF.RESERVE', () => { describe('transformArguments', () => { it('simple', () => { assert.deepEqual( - RESERVE.transformArguments('key', 4), + parseArgs(RESERVE, 'key', 4), ['CF.RESERVE', 'key', '4'] ); }); it('with EXPANSION', () => { assert.deepEqual( - RESERVE.transformArguments('key', 4, { + parseArgs(RESERVE, 'key', 4, { EXPANSION: 1 }), ['CF.RESERVE', 'key', '4', 'EXPANSION', '1'] @@ -22,7 +23,7 @@ describe('CF.RESERVE', () => { it('with BUCKETSIZE', () => { assert.deepEqual( - RESERVE.transformArguments('key', 4, { + parseArgs(RESERVE, 'key', 4, { BUCKETSIZE: 2 }), ['CF.RESERVE', 'key', '4', 'BUCKETSIZE', '2'] @@ -31,7 +32,7 @@ describe('CF.RESERVE', () => { it('with MAXITERATIONS', () => { assert.deepEqual( - RESERVE.transformArguments('key', 4, { + parseArgs(RESERVE, 'key', 4, { MAXITERATIONS: 1 }), ['CF.RESERVE', 'key', '4', 'MAXITERATIONS', '1'] diff --git a/packages/bloom/lib/commands/cuckoo/RESERVE.ts b/packages/bloom/lib/commands/cuckoo/RESERVE.ts index bc80daa0087..ba401dcdee9 100644 --- a/packages/bloom/lib/commands/cuckoo/RESERVE.ts +++ b/packages/bloom/lib/commands/cuckoo/RESERVE.ts @@ -1,4 +1,5 @@ -import { RedisArgument, SimpleStringReply, Command } from '@redis/client/dist/lib/RESP/types'; +import { CommandParser } from '@redis/client/lib/client/parser'; +import { RedisArgument, SimpleStringReply, Command } from '@redis/client/lib/RESP/types'; export interface CfReserveOptions { BUCKETSIZE?: number; @@ -7,28 +8,28 @@ export interface CfReserveOptions { } export default { - FIRST_KEY_INDEX: 1, IS_READ_ONLY: false, - transformArguments( + parseCommand( + parser: CommandParser, key: RedisArgument, capacity: number, options?: CfReserveOptions ) { - const args = ['CF.RESERVE', key, capacity.toString()]; + parser.push('CF.RESERVE'); + parser.pushKey(key); + parser.push(capacity.toString()); if (options?.BUCKETSIZE !== undefined) { - args.push('BUCKETSIZE', options.BUCKETSIZE.toString()); + parser.push('BUCKETSIZE', options.BUCKETSIZE.toString()); } if (options?.MAXITERATIONS !== undefined) { - args.push('MAXITERATIONS', options.MAXITERATIONS.toString()); + parser.push('MAXITERATIONS', options.MAXITERATIONS.toString()); } if (options?.EXPANSION !== undefined) { - args.push('EXPANSION', options.EXPANSION.toString()); + parser.push('EXPANSION', options.EXPANSION.toString()); } - - return args; }, transformReply: undefined as unknown as () => SimpleStringReply<'OK'> } as const satisfies Command; diff --git a/packages/bloom/lib/commands/cuckoo/SCANDUMP.spec.ts b/packages/bloom/lib/commands/cuckoo/SCANDUMP.spec.ts index e1bac59d323..60a57ac46ab 100644 --- a/packages/bloom/lib/commands/cuckoo/SCANDUMP.spec.ts +++ b/packages/bloom/lib/commands/cuckoo/SCANDUMP.spec.ts @@ -1,11 +1,12 @@ import { strict as assert } from 'node:assert'; import testUtils, { GLOBAL } from '../../test-utils'; import SCANDUMP from './SCANDUMP'; +import { parseArgs } from '@redis/client/lib/commands/generic-transformers'; describe('CF.SCANDUMP', () => { it('transformArguments', () => { assert.deepEqual( - SCANDUMP.transformArguments('key', 0), + parseArgs(SCANDUMP, 'key', 0), ['CF.SCANDUMP', 'key', '0'] ); }); diff --git a/packages/bloom/lib/commands/cuckoo/SCANDUMP.ts b/packages/bloom/lib/commands/cuckoo/SCANDUMP.ts index dc076689288..aab7a5eecc2 100644 --- a/packages/bloom/lib/commands/cuckoo/SCANDUMP.ts +++ b/packages/bloom/lib/commands/cuckoo/SCANDUMP.ts @@ -1,10 +1,12 @@ -import { RedisArgument, TuplesReply, NumberReply, BlobStringReply, NullReply, UnwrapReply, Command } from '@redis/client/dist/lib/RESP/types'; +import { CommandParser } from '@redis/client/lib/client/parser'; +import { RedisArgument, TuplesReply, NumberReply, BlobStringReply, NullReply, UnwrapReply, Command } from '@redis/client/lib/RESP/types'; export default { - FIRST_KEY_INDEX: 1, IS_READ_ONLY: true, - transformArguments(key: RedisArgument, iterator: number) { - return ['CF.SCANDUMP', key, iterator.toString()]; + parseCommand(parser: CommandParser, key: RedisArgument, iterator: number) { + parser.push('CF.SCANDUMP'); + parser.pushKey(key); + parser.push(iterator.toString()); }, transformReply(reply: UnwrapReply>) { return { diff --git a/packages/bloom/lib/commands/cuckoo/index.ts b/packages/bloom/lib/commands/cuckoo/index.ts index 62c63fe8d19..7ee99b41cd3 100644 --- a/packages/bloom/lib/commands/cuckoo/index.ts +++ b/packages/bloom/lib/commands/cuckoo/index.ts @@ -1,4 +1,4 @@ -import type { RedisCommands } from '@redis/client/dist/lib/RESP/types'; +import type { RedisCommands } from '@redis/client/lib/RESP/types'; import ADD from './ADD'; import ADDNX from './ADDNX'; import COUNT from './COUNT'; diff --git a/packages/bloom/lib/commands/t-digest/ADD.spec.ts b/packages/bloom/lib/commands/t-digest/ADD.spec.ts index 31d4957c6ad..7578fb9378b 100644 --- a/packages/bloom/lib/commands/t-digest/ADD.spec.ts +++ b/packages/bloom/lib/commands/t-digest/ADD.spec.ts @@ -1,11 +1,12 @@ import { strict as assert } from 'node:assert'; import testUtils, { GLOBAL } from '../../test-utils'; import ADD from './ADD'; +import { parseArgs } from '@redis/client/lib/commands/generic-transformers'; describe('TDIGEST.ADD', () => { it('transformArguments', () => { assert.deepEqual( - ADD.transformArguments('key', [1, 2]), + parseArgs(ADD, 'key', [1, 2]), ['TDIGEST.ADD', 'key', '1', '2'] ); }); diff --git a/packages/bloom/lib/commands/t-digest/ADD.ts b/packages/bloom/lib/commands/t-digest/ADD.ts index e7c6d7c4429..d9d67144a95 100644 --- a/packages/bloom/lib/commands/t-digest/ADD.ts +++ b/packages/bloom/lib/commands/t-digest/ADD.ts @@ -1,16 +1,15 @@ -import { SimpleStringReply, Command, RedisArgument } from '@redis/client/dist/lib/RESP/types'; +import { CommandParser } from '@redis/client/lib/client/parser'; +import { SimpleStringReply, Command, RedisArgument } from '@redis/client/lib/RESP/types'; export default { - FIRST_KEY_INDEX: 1, IS_READ_ONLY: false, - transformArguments(key: RedisArgument, values: Array) { - const args = ['TDIGEST.ADD', key]; + parseCommand(parser: CommandParser, key: RedisArgument, values: Array) { + parser.push('TDIGEST.ADD'); + parser.pushKey(key); for (const value of values) { - args.push(value.toString()); + parser.push(value.toString()); } - - return args; }, transformReply: undefined as unknown as () => SimpleStringReply<'OK'> } as const satisfies Command; diff --git a/packages/bloom/lib/commands/t-digest/BYRANK.spec.ts b/packages/bloom/lib/commands/t-digest/BYRANK.spec.ts index a6443d77432..81a2c75dff5 100644 --- a/packages/bloom/lib/commands/t-digest/BYRANK.spec.ts +++ b/packages/bloom/lib/commands/t-digest/BYRANK.spec.ts @@ -1,11 +1,12 @@ import { strict as assert } from 'node:assert'; import testUtils, { GLOBAL } from '../../test-utils'; import BYRANK from './BYRANK'; +import { parseArgs } from '@redis/client/lib/commands/generic-transformers'; describe('TDIGEST.BYRANK', () => { it('transformArguments', () => { assert.deepEqual( - BYRANK.transformArguments('key', [1, 2]), + parseArgs(BYRANK, 'key', [1, 2]), ['TDIGEST.BYRANK', 'key', '1', '2'] ); }); diff --git a/packages/bloom/lib/commands/t-digest/BYRANK.ts b/packages/bloom/lib/commands/t-digest/BYRANK.ts index 8b48acd1b1b..126c9963e71 100644 --- a/packages/bloom/lib/commands/t-digest/BYRANK.ts +++ b/packages/bloom/lib/commands/t-digest/BYRANK.ts @@ -1,24 +1,25 @@ -import { RedisArgument, Command } from '@redis/client/dist/lib/RESP/types'; -import { transformDoubleArrayReply } from '@redis/client/dist/lib/commands/generic-transformers'; +import { CommandParser } from '@redis/client/lib/client/parser'; +import { RedisArgument, Command } from '@redis/client/lib/RESP/types'; +import { transformDoubleArrayReply } from '@redis/client/lib/commands/generic-transformers'; export function transformByRankArguments( - command: RedisArgument, + parser: CommandParser, key: RedisArgument, ranks: Array ) { - const args = [command, key]; + parser.pushKey(key); for (const rank of ranks) { - args.push(rank.toString()); + parser.push(rank.toString()); } - - return args; } export default { - FIRST_KEY_INDEX: 1, IS_READ_ONLY: true, - transformArguments: transformByRankArguments.bind(undefined, 'TDIGEST.BYRANK'), + parseCommand(...args: Parameters) { + args[0].push('TDIGEST.BYRANK'); + transformByRankArguments(...args); + }, transformReply: transformDoubleArrayReply } as const satisfies Command; diff --git a/packages/bloom/lib/commands/t-digest/BYREVRANK.spec.ts b/packages/bloom/lib/commands/t-digest/BYREVRANK.spec.ts index f5bb4e62816..c8f794bef57 100644 --- a/packages/bloom/lib/commands/t-digest/BYREVRANK.spec.ts +++ b/packages/bloom/lib/commands/t-digest/BYREVRANK.spec.ts @@ -1,11 +1,12 @@ import { strict as assert } from 'node:assert'; import testUtils, { GLOBAL } from '../../test-utils'; import BYREVRANK from './BYREVRANK'; +import { parseArgs } from '@redis/client/lib/commands/generic-transformers'; describe('TDIGEST.BYREVRANK', () => { it('transformArguments', () => { assert.deepEqual( - BYREVRANK.transformArguments('key', [1, 2]), + parseArgs(BYREVRANK, 'key', [1, 2]), ['TDIGEST.BYREVRANK', 'key', '1', '2'] ); }); diff --git a/packages/bloom/lib/commands/t-digest/BYREVRANK.ts b/packages/bloom/lib/commands/t-digest/BYREVRANK.ts index 9f62b42d812..4995bf86cc0 100644 --- a/packages/bloom/lib/commands/t-digest/BYREVRANK.ts +++ b/packages/bloom/lib/commands/t-digest/BYREVRANK.ts @@ -1,9 +1,11 @@ -import { Command } from '@redis/client/dist/lib/RESP/types'; +import { Command } from '@redis/client/lib/RESP/types'; import BYRANK, { transformByRankArguments } from './BYRANK'; export default { - FIRST_KEY_INDEX: BYRANK.FIRST_KEY_INDEX, IS_READ_ONLY: BYRANK.IS_READ_ONLY, - transformArguments: transformByRankArguments.bind(undefined, 'TDIGEST.BYREVRANK'), + parseCommand(...args: Parameters) { + args[0].push('TDIGEST.BYREVRANK'); + transformByRankArguments(...args); + }, transformReply: BYRANK.transformReply } as const satisfies Command; diff --git a/packages/bloom/lib/commands/t-digest/CDF.spec.ts b/packages/bloom/lib/commands/t-digest/CDF.spec.ts index 09208deba11..2689bf2fc9a 100644 --- a/packages/bloom/lib/commands/t-digest/CDF.spec.ts +++ b/packages/bloom/lib/commands/t-digest/CDF.spec.ts @@ -1,11 +1,12 @@ import { strict as assert } from 'node:assert'; import testUtils, { GLOBAL } from '../../test-utils'; import CDF from './CDF'; +import { parseArgs } from '@redis/client/lib/commands/generic-transformers'; describe('TDIGEST.CDF', () => { it('transformArguments', () => { assert.deepEqual( - CDF.transformArguments('key', [1, 2]), + parseArgs(CDF, 'key', [1, 2]), ['TDIGEST.CDF', 'key', '1', '2'] ); }); diff --git a/packages/bloom/lib/commands/t-digest/CDF.ts b/packages/bloom/lib/commands/t-digest/CDF.ts index 0fbdedb3a47..e786dc4c8a8 100644 --- a/packages/bloom/lib/commands/t-digest/CDF.ts +++ b/packages/bloom/lib/commands/t-digest/CDF.ts @@ -1,17 +1,16 @@ -import { RedisArgument, Command } from '@redis/client/dist/lib/RESP/types'; -import { transformDoubleArrayReply } from '@redis/client/dist/lib/commands/generic-transformers'; +import { CommandParser } from '@redis/client/lib/client/parser'; +import { RedisArgument, Command } from '@redis/client/lib/RESP/types'; +import { transformDoubleArrayReply } from '@redis/client/lib/commands/generic-transformers'; export default { - FIRST_KEY_INDEX: 1, IS_READ_ONLY: true, - transformArguments(key: RedisArgument, values: Array) { - const args = ['TDIGEST.CDF', key]; + parseCommand(parser: CommandParser, key: RedisArgument, values: Array) { + parser.push('TDIGEST.CDF'); + parser.pushKey(key); for (const item of values) { - args.push(item.toString()); + parser.push(item.toString()); } - - return args; }, transformReply: transformDoubleArrayReply } as const satisfies Command; diff --git a/packages/bloom/lib/commands/t-digest/CREATE.spec.ts b/packages/bloom/lib/commands/t-digest/CREATE.spec.ts index 781b2a7e432..0f218e07ab8 100644 --- a/packages/bloom/lib/commands/t-digest/CREATE.spec.ts +++ b/packages/bloom/lib/commands/t-digest/CREATE.spec.ts @@ -1,19 +1,20 @@ import { strict as assert } from 'node:assert'; import testUtils, { GLOBAL } from '../../test-utils'; import CREATE from './CREATE'; +import { parseArgs } from '@redis/client/lib/commands/generic-transformers'; describe('TDIGEST.CREATE', () => { describe('transformArguments', () => { it('without options', () => { assert.deepEqual( - CREATE.transformArguments('key'), + parseArgs(CREATE, 'key'), ['TDIGEST.CREATE', 'key'] ); }); it('with COMPRESSION', () => { assert.deepEqual( - CREATE.transformArguments('key', { + parseArgs(CREATE, 'key', { COMPRESSION: 100 }), ['TDIGEST.CREATE', 'key', 'COMPRESSION', '100'] diff --git a/packages/bloom/lib/commands/t-digest/CREATE.ts b/packages/bloom/lib/commands/t-digest/CREATE.ts index 8b832487daa..fd160cce842 100644 --- a/packages/bloom/lib/commands/t-digest/CREATE.ts +++ b/packages/bloom/lib/commands/t-digest/CREATE.ts @@ -1,20 +1,19 @@ -import { RedisArgument, SimpleStringReply, Command } from '@redis/client/dist/lib/RESP/types'; +import { CommandParser } from '@redis/client/lib/client/parser'; +import { RedisArgument, SimpleStringReply, Command } from '@redis/client/lib/RESP/types'; export interface TDigestCreateOptions { COMPRESSION?: number; } export default { - FIRST_KEY_INDEX: 1, IS_READ_ONLY: false, - transformArguments(key: RedisArgument, options?: TDigestCreateOptions) { - const args = ['TDIGEST.CREATE', key]; + parseCommand(parser: CommandParser, key: RedisArgument, options?: TDigestCreateOptions) { + parser.push('TDIGEST.CREATE'); + parser.pushKey(key); if (options?.COMPRESSION !== undefined) { - args.push('COMPRESSION', options.COMPRESSION.toString()); + parser.push('COMPRESSION', options.COMPRESSION.toString()); } - - return args; }, transformReply: undefined as unknown as () => SimpleStringReply<'OK'> } as const satisfies Command; diff --git a/packages/bloom/lib/commands/t-digest/INFO.spec.ts b/packages/bloom/lib/commands/t-digest/INFO.spec.ts index 247f4ab0b61..d5b8b3e13ed 100644 --- a/packages/bloom/lib/commands/t-digest/INFO.spec.ts +++ b/packages/bloom/lib/commands/t-digest/INFO.spec.ts @@ -1,11 +1,12 @@ import { strict as assert } from 'node:assert'; import testUtils, { GLOBAL } from '../../test-utils'; import INFO from './INFO'; +import { parseArgs } from '@redis/client/lib/commands/generic-transformers'; describe('TDIGEST.INFO', () => { it('transformArguments', () => { assert.deepEqual( - INFO.transformArguments('key'), + parseArgs(INFO, 'key'), ['TDIGEST.INFO', 'key'] ); }); diff --git a/packages/bloom/lib/commands/t-digest/INFO.ts b/packages/bloom/lib/commands/t-digest/INFO.ts index c7c2357d2b4..43624e8f9b9 100644 --- a/packages/bloom/lib/commands/t-digest/INFO.ts +++ b/packages/bloom/lib/commands/t-digest/INFO.ts @@ -1,4 +1,5 @@ -import { RedisArgument, Command, NumberReply, TuplesToMapReply, UnwrapReply, Resp2Reply, SimpleStringReply, TypeMapping } from '@redis/client/dist/lib/RESP/types'; +import { CommandParser } from '@redis/client/lib/client/parser'; +import { RedisArgument, Command, NumberReply, TuplesToMapReply, UnwrapReply, Resp2Reply, SimpleStringReply, TypeMapping } from '@redis/client/lib/RESP/types'; import { transformInfoV2Reply } from '../bloom'; export type TdInfoReplyMap = TuplesToMapReply<[ @@ -14,10 +15,10 @@ export type TdInfoReplyMap = TuplesToMapReply<[ ]>; export default { - FIRST_KEY_INDEX: 1, IS_READ_ONLY: true, - transformArguments(key: RedisArgument) { - return ['TDIGEST.INFO', key]; + parseCommand(parser: CommandParser, key: RedisArgument) { + parser.push('TDIGEST.INFO'); + parser.pushKey(key); }, transformReply: { 2: (reply: UnwrapReply>, _, typeMapping?: TypeMapping): TdInfoReplyMap => { diff --git a/packages/bloom/lib/commands/t-digest/MAX.spec.ts b/packages/bloom/lib/commands/t-digest/MAX.spec.ts index caa92b0a6a0..920c9d11391 100644 --- a/packages/bloom/lib/commands/t-digest/MAX.spec.ts +++ b/packages/bloom/lib/commands/t-digest/MAX.spec.ts @@ -1,11 +1,12 @@ import { strict as assert } from 'node:assert'; import testUtils, { GLOBAL } from '../../test-utils'; import MAX from './MAX'; +import { parseArgs } from '@redis/client/lib/commands/generic-transformers'; describe('TDIGEST.MAX', () => { it('transformArguments', () => { assert.deepEqual( - MAX.transformArguments('key'), + parseArgs(MAX, 'key'), ['TDIGEST.MAX', 'key'] ); }); diff --git a/packages/bloom/lib/commands/t-digest/MAX.ts b/packages/bloom/lib/commands/t-digest/MAX.ts index ef778f832a6..64852d8e6dc 100644 --- a/packages/bloom/lib/commands/t-digest/MAX.ts +++ b/packages/bloom/lib/commands/t-digest/MAX.ts @@ -1,11 +1,12 @@ -import { RedisArgument, Command } from '@redis/client/dist/lib/RESP/types'; -import { transformDoubleReply } from '@redis/client/dist/lib/commands/generic-transformers'; +import { CommandParser } from '@redis/client/lib/client/parser'; +import { RedisArgument, Command } from '@redis/client/lib/RESP/types'; +import { transformDoubleReply } from '@redis/client/lib/commands/generic-transformers'; export default { - FIRST_KEY_INDEX: 1, IS_READ_ONLY: true, - transformArguments(key: RedisArgument) { - return ['TDIGEST.MAX', key]; + parseCommand(parser: CommandParser, key: RedisArgument) { + parser.push('TDIGEST.MAX'); + parser.pushKey(key); }, transformReply: transformDoubleReply } as const satisfies Command; diff --git a/packages/bloom/lib/commands/t-digest/MERGE.spec.ts b/packages/bloom/lib/commands/t-digest/MERGE.spec.ts index 1ee792e3a40..f2a7c1a1192 100644 --- a/packages/bloom/lib/commands/t-digest/MERGE.spec.ts +++ b/packages/bloom/lib/commands/t-digest/MERGE.spec.ts @@ -1,20 +1,21 @@ import { strict as assert } from 'node:assert'; import testUtils, { GLOBAL } from '../../test-utils'; import MERGE from './MERGE'; +import { parseArgs } from '@redis/client/lib/commands/generic-transformers'; describe('TDIGEST.MERGE', () => { describe('transformArguments', () => { describe('source', () => { it('string', () => { assert.deepEqual( - MERGE.transformArguments('destination', 'source'), + parseArgs(MERGE, 'destination', 'source'), ['TDIGEST.MERGE', 'destination', '1', 'source'] ); }); it('Array', () => { assert.deepEqual( - MERGE.transformArguments('destination', ['1', '2']), + parseArgs(MERGE, 'destination', ['1', '2']), ['TDIGEST.MERGE', 'destination', '2', '1', '2'] ); }); @@ -22,7 +23,7 @@ describe('TDIGEST.MERGE', () => { it('with COMPRESSION', () => { assert.deepEqual( - MERGE.transformArguments('destination', 'source', { + parseArgs(MERGE, 'destination', 'source', { COMPRESSION: 100 }), ['TDIGEST.MERGE', 'destination', '1', 'source', 'COMPRESSION', '100'] @@ -31,7 +32,7 @@ describe('TDIGEST.MERGE', () => { it('with OVERRIDE', () => { assert.deepEqual( - MERGE.transformArguments('destination', 'source', { + parseArgs(MERGE, 'destination', 'source', { OVERRIDE: true }), ['TDIGEST.MERGE', 'destination', '1', 'source', 'OVERRIDE'] diff --git a/packages/bloom/lib/commands/t-digest/MERGE.ts b/packages/bloom/lib/commands/t-digest/MERGE.ts index e9cd99aabf9..1031f0e9170 100644 --- a/packages/bloom/lib/commands/t-digest/MERGE.ts +++ b/packages/bloom/lib/commands/t-digest/MERGE.ts @@ -1,5 +1,6 @@ -import { RedisArgument, SimpleStringReply, Command } from '@redis/client/dist/lib/RESP/types'; -import { RedisVariadicArgument, pushVariadicArgument } from '@redis/client/dist/lib/commands/generic-transformers'; +import { CommandParser } from '@redis/client/lib/client/parser'; +import { RedisArgument, SimpleStringReply, Command } from '@redis/client/lib/RESP/types'; +import { RedisVariadicArgument } from '@redis/client/lib/commands/generic-transformers'; export interface TDigestMergeOptions { COMPRESSION?: number; @@ -7,24 +8,24 @@ export interface TDigestMergeOptions { } export default { - FIRST_KEY_INDEX: undefined, IS_READ_ONLY: false, - transformArguments( + parseCommand( + parser: CommandParser, destination: RedisArgument, source: RedisVariadicArgument, options?: TDigestMergeOptions ) { - const args = pushVariadicArgument(['TDIGEST.MERGE', destination], source); + parser.push('TDIGEST.MERGE'); + parser.pushKey(destination); + parser.pushKeysLength(source); if (options?.COMPRESSION !== undefined) { - args.push('COMPRESSION', options.COMPRESSION.toString()); + parser.push('COMPRESSION', options.COMPRESSION.toString()); } if (options?.OVERRIDE) { - args.push('OVERRIDE'); + parser.push('OVERRIDE'); } - - return args; }, transformReply: undefined as unknown as () => SimpleStringReply<'OK'> } as const satisfies Command; diff --git a/packages/bloom/lib/commands/t-digest/MIN.spec.ts b/packages/bloom/lib/commands/t-digest/MIN.spec.ts index 0d1637cc9b7..278248ea465 100644 --- a/packages/bloom/lib/commands/t-digest/MIN.spec.ts +++ b/packages/bloom/lib/commands/t-digest/MIN.spec.ts @@ -1,11 +1,12 @@ import { strict as assert } from 'node:assert'; import testUtils, { GLOBAL } from '../../test-utils'; import MIN from './MIN'; +import { parseArgs } from '@redis/client/lib/commands/generic-transformers'; describe('TDIGEST.MIN', () => { it('transformArguments', () => { assert.deepEqual( - MIN.transformArguments('key'), + parseArgs(MIN, 'key'), ['TDIGEST.MIN', 'key'] ); }); diff --git a/packages/bloom/lib/commands/t-digest/MIN.ts b/packages/bloom/lib/commands/t-digest/MIN.ts index 914998a1ec4..cc44dbbea4e 100644 --- a/packages/bloom/lib/commands/t-digest/MIN.ts +++ b/packages/bloom/lib/commands/t-digest/MIN.ts @@ -1,11 +1,12 @@ -import { RedisArgument, Command } from '@redis/client/dist/lib/RESP/types'; -import { transformDoubleReply } from '@redis/client/dist/lib/commands/generic-transformers'; +import { CommandParser } from '@redis/client/lib/client/parser'; +import { RedisArgument, Command } from '@redis/client/lib/RESP/types'; +import { transformDoubleReply } from '@redis/client/lib/commands/generic-transformers'; export default { - FIRST_KEY_INDEX: 1, IS_READ_ONLY: true, - transformArguments(key: RedisArgument) { - return ['TDIGEST.MIN', key]; + parseCommand(parser: CommandParser, key: RedisArgument) { + parser.push('TDIGEST.MIN'); + parser.pushKey(key); }, transformReply: transformDoubleReply } as const satisfies Command; diff --git a/packages/bloom/lib/commands/t-digest/QUANTILE.spec.ts b/packages/bloom/lib/commands/t-digest/QUANTILE.spec.ts index c427f8c4501..ac7249d12d9 100644 --- a/packages/bloom/lib/commands/t-digest/QUANTILE.spec.ts +++ b/packages/bloom/lib/commands/t-digest/QUANTILE.spec.ts @@ -1,11 +1,12 @@ import { strict as assert } from 'node:assert'; import testUtils, { GLOBAL } from '../../test-utils'; import QUANTILE from './QUANTILE'; +import { parseArgs } from '@redis/client/lib/commands/generic-transformers'; describe('TDIGEST.QUANTILE', () => { it('transformArguments', () => { assert.deepEqual( - QUANTILE.transformArguments('key', [1, 2]), + parseArgs(QUANTILE, 'key', [1, 2]), ['TDIGEST.QUANTILE', 'key', '1', '2'] ); }); diff --git a/packages/bloom/lib/commands/t-digest/QUANTILE.ts b/packages/bloom/lib/commands/t-digest/QUANTILE.ts index f7057a37d1c..962c3a9b4ca 100644 --- a/packages/bloom/lib/commands/t-digest/QUANTILE.ts +++ b/packages/bloom/lib/commands/t-digest/QUANTILE.ts @@ -1,17 +1,16 @@ -import { RedisArgument, Command } from '@redis/client/dist/lib/RESP/types'; -import { transformDoubleArrayReply } from '@redis/client/dist/lib/commands/generic-transformers'; +import { CommandParser } from '@redis/client/lib/client/parser'; +import { RedisArgument, Command } from '@redis/client/lib/RESP/types'; +import { transformDoubleArrayReply } from '@redis/client/lib/commands/generic-transformers'; export default { - FIRST_KEY_INDEX: 1, IS_READ_ONLY: true, - transformArguments(key: RedisArgument, quantiles: Array) { - const args = ['TDIGEST.QUANTILE', key]; + parseCommand(parser: CommandParser, key: RedisArgument, quantiles: Array) { + parser.push('TDIGEST.QUANTILE'); + parser.pushKey(key); for (const quantile of quantiles) { - args.push(quantile.toString()); + parser.push(quantile.toString()); } - - return args; }, transformReply: transformDoubleArrayReply } as const satisfies Command; diff --git a/packages/bloom/lib/commands/t-digest/RANK.spec.ts b/packages/bloom/lib/commands/t-digest/RANK.spec.ts index dcdae48cb06..f1747662f0a 100644 --- a/packages/bloom/lib/commands/t-digest/RANK.spec.ts +++ b/packages/bloom/lib/commands/t-digest/RANK.spec.ts @@ -1,11 +1,12 @@ import { strict as assert } from 'node:assert'; import testUtils, { GLOBAL } from '../../test-utils'; import RANK from './RANK'; +import { parseArgs } from '@redis/client/lib/commands/generic-transformers'; describe('TDIGEST.RANK', () => { it('transformArguments', () => { assert.deepEqual( - RANK.transformArguments('key', [1, 2]), + parseArgs(RANK, 'key', [1, 2]), ['TDIGEST.RANK', 'key', '1', '2'] ); }); diff --git a/packages/bloom/lib/commands/t-digest/RANK.ts b/packages/bloom/lib/commands/t-digest/RANK.ts index 8c18ad12778..316ca74b542 100644 --- a/packages/bloom/lib/commands/t-digest/RANK.ts +++ b/packages/bloom/lib/commands/t-digest/RANK.ts @@ -1,22 +1,23 @@ -import { RedisArgument, ArrayReply, NumberReply, Command } from '@redis/client/dist/lib/RESP/types'; +import { CommandParser } from '@redis/client/lib/client/parser'; +import { RedisArgument, ArrayReply, NumberReply, Command } from '@redis/client/lib/RESP/types'; export function transformRankArguments( - command: RedisArgument, + parser: CommandParser, key: RedisArgument, values: Array ) { - const args = [command, key]; + parser.pushKey(key); for (const value of values) { - args.push(value.toString()); + parser.push(value.toString()); } - - return args; } export default { - FIRST_KEY_INDEX: 1, IS_READ_ONLY: true, - transformArguments: transformRankArguments.bind(undefined, 'TDIGEST.RANK'), + parseCommand(...args: Parameters) { + args[0].push('TDIGEST.RANK'); + transformRankArguments(...args); + }, transformReply: undefined as unknown as () => ArrayReply } as const satisfies Command; diff --git a/packages/bloom/lib/commands/t-digest/RESET.spec.ts b/packages/bloom/lib/commands/t-digest/RESET.spec.ts index 072257113b9..8e1fc12e6e3 100644 --- a/packages/bloom/lib/commands/t-digest/RESET.spec.ts +++ b/packages/bloom/lib/commands/t-digest/RESET.spec.ts @@ -1,11 +1,12 @@ import { strict as assert } from 'node:assert'; import testUtils, { GLOBAL } from '../../test-utils'; import RESET from './RESET'; +import { parseArgs } from '@redis/client/lib/commands/generic-transformers'; describe('TDIGEST.RESET', () => { it('transformArguments', () => { assert.deepEqual( - RESET.transformArguments('key'), + parseArgs(RESET, 'key'), ['TDIGEST.RESET', 'key'] ); }); diff --git a/packages/bloom/lib/commands/t-digest/RESET.ts b/packages/bloom/lib/commands/t-digest/RESET.ts index 372a1efd488..571102bfc20 100644 --- a/packages/bloom/lib/commands/t-digest/RESET.ts +++ b/packages/bloom/lib/commands/t-digest/RESET.ts @@ -1,10 +1,11 @@ -import { RedisArgument, SimpleStringReply, Command } from '@redis/client/dist/lib/RESP/types'; +import { CommandParser } from '@redis/client/lib/client/parser'; +import { RedisArgument, SimpleStringReply, Command } from '@redis/client/lib/RESP/types'; export default { - FIRST_KEY_INDEX: 1, IS_READ_ONLY: false, - transformArguments(key: RedisArgument) { - return ['TDIGEST.RESET', key]; + parseCommand(parser: CommandParser, key: RedisArgument) { + parser.push('TDIGEST.RESET'); + parser.pushKey(key); }, transformReply: undefined as unknown as () => SimpleStringReply<'OK'> } as const satisfies Command; diff --git a/packages/bloom/lib/commands/t-digest/REVRANK.spec.ts b/packages/bloom/lib/commands/t-digest/REVRANK.spec.ts index baa1b94afa8..be7b23b2238 100644 --- a/packages/bloom/lib/commands/t-digest/REVRANK.spec.ts +++ b/packages/bloom/lib/commands/t-digest/REVRANK.spec.ts @@ -1,11 +1,12 @@ import { strict as assert } from 'node:assert'; import testUtils, { GLOBAL } from '../../test-utils'; import REVRANK from './REVRANK'; +import { parseArgs } from '@redis/client/lib/commands/generic-transformers'; describe('TDIGEST.REVRANK', () => { it('transformArguments', () => { assert.deepEqual( - REVRANK.transformArguments('key', [1, 2]), + parseArgs(REVRANK, 'key', [1, 2]), ['TDIGEST.REVRANK', 'key', '1', '2'] ); }); diff --git a/packages/bloom/lib/commands/t-digest/REVRANK.ts b/packages/bloom/lib/commands/t-digest/REVRANK.ts index 456b2be5a38..ca9301bb423 100644 --- a/packages/bloom/lib/commands/t-digest/REVRANK.ts +++ b/packages/bloom/lib/commands/t-digest/REVRANK.ts @@ -1,9 +1,11 @@ -import { Command } from '@redis/client/dist/lib/RESP/types'; +import { Command } from '@redis/client/lib/RESP/types'; import RANK, { transformRankArguments } from './RANK'; export default { - FIRST_KEY_INDEX: RANK.FIRST_KEY_INDEX, IS_READ_ONLY: RANK.IS_READ_ONLY, - transformArguments: transformRankArguments.bind(undefined, 'TDIGEST.REVRANK'), + parseCommand(...args: Parameters) { + args[0].push('TDIGEST.REVRANK'); + transformRankArguments(...args); + }, transformReply: RANK.transformReply } as const satisfies Command; diff --git a/packages/bloom/lib/commands/t-digest/TRIMMED_MEAN.spec.ts b/packages/bloom/lib/commands/t-digest/TRIMMED_MEAN.spec.ts index c43c0f47553..8e83c736476 100644 --- a/packages/bloom/lib/commands/t-digest/TRIMMED_MEAN.spec.ts +++ b/packages/bloom/lib/commands/t-digest/TRIMMED_MEAN.spec.ts @@ -1,11 +1,12 @@ import { strict as assert } from 'node:assert'; import testUtils, { GLOBAL } from '../../test-utils'; import TRIMMED_MEAN from './TRIMMED_MEAN'; +import { parseArgs } from '@redis/client/lib/commands/generic-transformers'; describe('TDIGEST.TRIMMED_MEAN', () => { it('transformArguments', () => { assert.deepEqual( - TRIMMED_MEAN.transformArguments('key', 0, 1), + parseArgs(TRIMMED_MEAN, 'key', 0, 1), ['TDIGEST.TRIMMED_MEAN', 'key', '0', '1'] ); }); diff --git a/packages/bloom/lib/commands/t-digest/TRIMMED_MEAN.ts b/packages/bloom/lib/commands/t-digest/TRIMMED_MEAN.ts index f91dd7d8093..f411198260a 100644 --- a/packages/bloom/lib/commands/t-digest/TRIMMED_MEAN.ts +++ b/packages/bloom/lib/commands/t-digest/TRIMMED_MEAN.ts @@ -1,20 +1,18 @@ -import { RedisArgument, Command } from '@redis/client/dist/lib/RESP/types'; -import { transformDoubleReply } from '@redis/client/dist/lib/commands/generic-transformers'; +import { CommandParser } from '@redis/client/lib/client/parser'; +import { RedisArgument, Command } from '@redis/client/lib/RESP/types'; +import { transformDoubleReply } from '@redis/client/lib/commands/generic-transformers'; export default { - FIRST_KEY_INDEX: 1, IS_READ_ONLY: true, - transformArguments( + parseCommand( + parser: CommandParser, key: RedisArgument, lowCutPercentile: number, highCutPercentile: number ) { - return [ - 'TDIGEST.TRIMMED_MEAN', - key, - lowCutPercentile.toString(), - highCutPercentile.toString() - ]; + parser.push('TDIGEST.TRIMMED_MEAN'); + parser.pushKey(key); + parser.push(lowCutPercentile.toString(), highCutPercentile.toString()); }, transformReply: transformDoubleReply } as const satisfies Command; diff --git a/packages/bloom/lib/commands/t-digest/index.ts b/packages/bloom/lib/commands/t-digest/index.ts index d180911dbf9..fb80b35d0a1 100644 --- a/packages/bloom/lib/commands/t-digest/index.ts +++ b/packages/bloom/lib/commands/t-digest/index.ts @@ -1,4 +1,4 @@ -import type { RedisCommands } from '@redis/client/dist/lib/RESP/types'; +import type { RedisCommands } from '@redis/client/lib/RESP/types'; import ADD from './ADD'; import BYRANK from './BYRANK'; import BYREVRANK from './BYREVRANK'; diff --git a/packages/bloom/lib/commands/top-k/ADD.spec.ts b/packages/bloom/lib/commands/top-k/ADD.spec.ts index 8f6f9300b36..15a7a9ce1dd 100644 --- a/packages/bloom/lib/commands/top-k/ADD.spec.ts +++ b/packages/bloom/lib/commands/top-k/ADD.spec.ts @@ -1,11 +1,12 @@ import { strict as assert } from 'node:assert'; import testUtils, { GLOBAL } from '../../test-utils'; import ADD from './ADD'; +import { parseArgs } from '@redis/client/lib/commands/generic-transformers'; describe('TOPK.ADD', () => { it('transformArguments', () => { assert.deepEqual( - ADD.transformArguments('key', 'item'), + parseArgs(ADD, 'key', 'item'), ['TOPK.ADD', 'key', 'item'] ); }); diff --git a/packages/bloom/lib/commands/top-k/ADD.ts b/packages/bloom/lib/commands/top-k/ADD.ts index 99982cc8e64..42b162165c7 100644 --- a/packages/bloom/lib/commands/top-k/ADD.ts +++ b/packages/bloom/lib/commands/top-k/ADD.ts @@ -1,11 +1,13 @@ -import { RedisArgument, ArrayReply, BlobStringReply, Command } from '@redis/client/dist/lib/RESP/types'; -import { RedisVariadicArgument, pushVariadicArguments } from '@redis/client/dist/lib/commands/generic-transformers'; +import { CommandParser } from '@redis/client/lib/client/parser'; +import { RedisArgument, ArrayReply, BlobStringReply, Command } from '@redis/client/lib/RESP/types'; +import { RedisVariadicArgument } from '@redis/client/lib/commands/generic-transformers'; export default { - FIRST_KEY_INDEX: 1, IS_READ_ONLY: false, - transformArguments(key: RedisArgument, items: RedisVariadicArgument) { - return pushVariadicArguments(['TOPK.ADD', key], items); + parseCommand(parser: CommandParser, key: RedisArgument, items: RedisVariadicArgument) { + parser.push('TOPK.ADD'); + parser.pushKey(key); + parser.pushVariadic(items); }, transformReply: undefined as unknown as () => ArrayReply } as const satisfies Command; diff --git a/packages/bloom/lib/commands/top-k/COUNT.spec.ts b/packages/bloom/lib/commands/top-k/COUNT.spec.ts index dce03f0e78c..a242edfef8a 100644 --- a/packages/bloom/lib/commands/top-k/COUNT.spec.ts +++ b/packages/bloom/lib/commands/top-k/COUNT.spec.ts @@ -1,11 +1,12 @@ import { strict as assert } from 'node:assert'; import testUtils, { GLOBAL } from '../../test-utils'; import COUNT from './COUNT'; +import { parseArgs } from '@redis/client/lib/commands/generic-transformers'; describe('TOPK.COUNT', () => { it('transformArguments', () => { assert.deepEqual( - COUNT.transformArguments('key', 'item'), + parseArgs(COUNT, 'key', 'item'), ['TOPK.COUNT', 'key', 'item'] ); }); diff --git a/packages/bloom/lib/commands/top-k/COUNT.ts b/packages/bloom/lib/commands/top-k/COUNT.ts index 7e3ccc6dc4c..7cca009c599 100644 --- a/packages/bloom/lib/commands/top-k/COUNT.ts +++ b/packages/bloom/lib/commands/top-k/COUNT.ts @@ -1,11 +1,13 @@ -import { RedisArgument, ArrayReply, NumberReply, Command } from '@redis/client/dist/lib/RESP/types'; -import { RedisVariadicArgument, pushVariadicArguments } from '@redis/client/dist/lib/commands/generic-transformers'; +import { CommandParser } from '@redis/client/lib/client/parser'; +import { RedisArgument, ArrayReply, NumberReply, Command } from '@redis/client/lib/RESP/types'; +import { RedisVariadicArgument } from '@redis/client/lib/commands/generic-transformers'; export default { - FIRST_KEY_INDEX: 1, IS_READ_ONLY: true, - transformArguments(key: RedisArgument, items: RedisVariadicArgument) { - return pushVariadicArguments(['TOPK.COUNT', key], items); + parseCommand(parser: CommandParser, key: RedisArgument, items: RedisVariadicArgument) { + parser.push('TOPK.COUNT'); + parser.pushKey(key); + parser.pushVariadic(items); }, transformReply: undefined as unknown as () => ArrayReply } as const satisfies Command; diff --git a/packages/bloom/lib/commands/top-k/INCRBY.spec.ts b/packages/bloom/lib/commands/top-k/INCRBY.spec.ts index aa7032a9a02..94e5b1d7058 100644 --- a/packages/bloom/lib/commands/top-k/INCRBY.spec.ts +++ b/packages/bloom/lib/commands/top-k/INCRBY.spec.ts @@ -1,12 +1,13 @@ import { strict as assert } from 'node:assert'; import testUtils, { GLOBAL } from '../../test-utils'; import INCRBY from './INCRBY'; +import { parseArgs } from '@redis/client/lib/commands/generic-transformers'; describe('TOPK.INCRBY', () => { describe('transformArguments', () => { it('single item', () => { assert.deepEqual( - INCRBY.transformArguments('key', { + parseArgs(INCRBY, 'key', { item: 'item', incrementBy: 1 }), @@ -16,7 +17,7 @@ describe('TOPK.INCRBY', () => { it('multiple items', () => { assert.deepEqual( - INCRBY.transformArguments('key', [{ + parseArgs(INCRBY, 'key', [{ item: 'a', incrementBy: 1 }, { diff --git a/packages/bloom/lib/commands/top-k/INCRBY.ts b/packages/bloom/lib/commands/top-k/INCRBY.ts index 16decf44dca..52ea0edc968 100644 --- a/packages/bloom/lib/commands/top-k/INCRBY.ts +++ b/packages/bloom/lib/commands/top-k/INCRBY.ts @@ -1,32 +1,32 @@ -import { RedisArgument, ArrayReply, SimpleStringReply, NullReply, Command } from '@redis/client/dist/lib/RESP/types'; +import { CommandParser } from '@redis/client/lib/client/parser'; +import { RedisArgument, ArrayReply, SimpleStringReply, NullReply, Command } from '@redis/client/lib/RESP/types'; export interface TopKIncrByItem { item: string; incrementBy: number; } -function pushIncrByItem(args: Array, { item, incrementBy }: TopKIncrByItem) { - args.push(item, incrementBy.toString()); +function pushIncrByItem(parser: CommandParser, { item, incrementBy }: TopKIncrByItem) { + parser.push(item, incrementBy.toString()); } export default { - FIRST_KEY_INDEX: 1, IS_READ_ONLY: false, - transformArguments( + parseCommand( + parser: CommandParser, key: RedisArgument, items: TopKIncrByItem | Array ) { - const args = ['TOPK.INCRBY', key]; + parser.push('TOPK.INCRBY'); + parser.pushKey(key); if (Array.isArray(items)) { for (const item of items) { - pushIncrByItem(args, item); + pushIncrByItem(parser, item); } } else { - pushIncrByItem(args, items); + pushIncrByItem(parser, items); } - - return args; }, transformReply: undefined as unknown as () => ArrayReply } as const satisfies Command; diff --git a/packages/bloom/lib/commands/top-k/INFO.spec.ts b/packages/bloom/lib/commands/top-k/INFO.spec.ts index 8e17829a2a6..2efbf0bdbef 100644 --- a/packages/bloom/lib/commands/top-k/INFO.spec.ts +++ b/packages/bloom/lib/commands/top-k/INFO.spec.ts @@ -1,11 +1,12 @@ import { strict as assert } from 'node:assert'; import testUtils, { GLOBAL } from '../../test-utils'; import INFO from './INFO'; +import { parseArgs } from '@redis/client/lib/commands/generic-transformers'; describe('TOPK INFO', () => { it('transformArguments', () => { assert.deepEqual( - INFO.transformArguments('key'), + parseArgs(INFO, 'key'), ['TOPK.INFO', 'key'] ); }); diff --git a/packages/bloom/lib/commands/top-k/INFO.ts b/packages/bloom/lib/commands/top-k/INFO.ts index e6f55ac2c1b..89ebeaa8ebe 100644 --- a/packages/bloom/lib/commands/top-k/INFO.ts +++ b/packages/bloom/lib/commands/top-k/INFO.ts @@ -1,5 +1,6 @@ -import { transformDoubleReply } from '@redis/client/dist/lib/commands/generic-transformers'; -import { RedisArgument, TuplesToMapReply, NumberReply, DoubleReply, UnwrapReply, Resp2Reply, Command, SimpleStringReply, TypeMapping } from '@redis/client/dist/lib/RESP/types'; +import { CommandParser } from '@redis/client/lib/client/parser'; +import { RedisArgument, TuplesToMapReply, NumberReply, DoubleReply, UnwrapReply, Resp2Reply, Command, SimpleStringReply, TypeMapping } from '@redis/client/lib/RESP/types'; +import { transformDoubleReply } from '@redis/client/lib/commands/generic-transformers'; import { transformInfoV2Reply } from '../bloom'; export type TopKInfoReplyMap = TuplesToMapReply<[ @@ -10,10 +11,10 @@ export type TopKInfoReplyMap = TuplesToMapReply<[ ]>; export default { - FIRST_KEY_INDEX: 1, IS_READ_ONLY: true, - transformArguments(key: RedisArgument) { - return ['TOPK.INFO', key]; + parseCommand(parser: CommandParser, key: RedisArgument) { + parser.push('TOPK.INFO'); + parser.pushKey(key); }, transformReply: { 2: (reply: UnwrapReply>, preserve?: any, typeMapping?: TypeMapping): TopKInfoReplyMap => { diff --git a/packages/bloom/lib/commands/top-k/LIST.spec.ts b/packages/bloom/lib/commands/top-k/LIST.spec.ts index 7ab96182bbe..8f5d0efa4db 100644 --- a/packages/bloom/lib/commands/top-k/LIST.spec.ts +++ b/packages/bloom/lib/commands/top-k/LIST.spec.ts @@ -1,11 +1,12 @@ import { strict as assert } from 'node:assert'; import testUtils, { GLOBAL } from '../../test-utils'; import LIST from './LIST'; +import { parseArgs } from '@redis/client/lib/commands/generic-transformers'; describe('TOPK.LIST', () => { it('transformArguments', () => { assert.deepEqual( - LIST.transformArguments('key'), + parseArgs(LIST, 'key'), ['TOPK.LIST', 'key'] ); }); diff --git a/packages/bloom/lib/commands/top-k/LIST.ts b/packages/bloom/lib/commands/top-k/LIST.ts index 26345b72462..74b85e0f71d 100644 --- a/packages/bloom/lib/commands/top-k/LIST.ts +++ b/packages/bloom/lib/commands/top-k/LIST.ts @@ -1,10 +1,11 @@ -import { RedisArgument, ArrayReply, BlobStringReply, Command } from '@redis/client/dist/lib/RESP/types'; +import { CommandParser } from '@redis/client/lib/client/parser'; +import { RedisArgument, ArrayReply, BlobStringReply, Command } from '@redis/client/lib/RESP/types'; export default { - FIRST_KEY_INDEX: 1, IS_READ_ONLY: true, - transformArguments(key: RedisArgument) { - return ['TOPK.LIST', key]; + parseCommand(parser: CommandParser, key: RedisArgument) { + parser.push('TOPK.LIST'); + parser.pushKey(key); }, transformReply: undefined as unknown as () => ArrayReply } as const satisfies Command; diff --git a/packages/bloom/lib/commands/top-k/LIST_WITHCOUNT.spec.ts b/packages/bloom/lib/commands/top-k/LIST_WITHCOUNT.spec.ts index 862d17eb3e3..852170e8cd3 100644 --- a/packages/bloom/lib/commands/top-k/LIST_WITHCOUNT.spec.ts +++ b/packages/bloom/lib/commands/top-k/LIST_WITHCOUNT.spec.ts @@ -1,13 +1,14 @@ import { strict as assert } from 'node:assert'; import testUtils, { GLOBAL } from '../../test-utils'; import LIST_WITHCOUNT from './LIST_WITHCOUNT'; +import { parseArgs } from '@redis/client/lib/commands/generic-transformers'; describe('TOPK.LIST WITHCOUNT', () => { testUtils.isVersionGreaterThanHook([2, 2, 9]); it('transformArguments', () => { assert.deepEqual( - LIST_WITHCOUNT.transformArguments('key'), + parseArgs(LIST_WITHCOUNT, 'key'), ['TOPK.LIST', 'key', 'WITHCOUNT'] ); }); diff --git a/packages/bloom/lib/commands/top-k/LIST_WITHCOUNT.ts b/packages/bloom/lib/commands/top-k/LIST_WITHCOUNT.ts index d26936fd3c7..a3a3d3f43f8 100644 --- a/packages/bloom/lib/commands/top-k/LIST_WITHCOUNT.ts +++ b/packages/bloom/lib/commands/top-k/LIST_WITHCOUNT.ts @@ -1,10 +1,12 @@ -import { RedisArgument, ArrayReply, BlobStringReply, NumberReply, UnwrapReply, Command } from '@redis/client/dist/lib/RESP/types'; +import { CommandParser } from '@redis/client/lib/client/parser'; +import { RedisArgument, ArrayReply, BlobStringReply, NumberReply, UnwrapReply, Command } from '@redis/client/lib/RESP/types'; export default { - FIRST_KEY_INDEX: 1, IS_READ_ONLY: true, - transformArguments(key: RedisArgument) { - return ['TOPK.LIST', key, 'WITHCOUNT']; + parseCommand(parser: CommandParser, key: RedisArgument) { + parser.push('TOPK.LIST'); + parser.pushKey(key); + parser.push('WITHCOUNT'); }, transformReply(rawReply: UnwrapReply>) { const reply: Array<{ diff --git a/packages/bloom/lib/commands/top-k/QUERY.spec.ts b/packages/bloom/lib/commands/top-k/QUERY.spec.ts index d5ecfebb6c6..3651ec5d37b 100644 --- a/packages/bloom/lib/commands/top-k/QUERY.spec.ts +++ b/packages/bloom/lib/commands/top-k/QUERY.spec.ts @@ -1,11 +1,12 @@ import { strict as assert } from 'node:assert'; import testUtils, { GLOBAL } from '../../test-utils'; import QUERY from './QUERY'; +import { parseArgs } from '@redis/client/lib/commands/generic-transformers'; describe('TOPK.QUERY', () => { it('transformArguments', () => { assert.deepEqual( - QUERY.transformArguments('key', 'item'), + parseArgs(QUERY, 'key', 'item'), ['TOPK.QUERY', 'key', 'item'] ); }); diff --git a/packages/bloom/lib/commands/top-k/QUERY.ts b/packages/bloom/lib/commands/top-k/QUERY.ts index 5529d4ab838..49b91714374 100644 --- a/packages/bloom/lib/commands/top-k/QUERY.ts +++ b/packages/bloom/lib/commands/top-k/QUERY.ts @@ -1,11 +1,13 @@ -import { RedisArgument, Command } from '@redis/client/dist/lib/RESP/types'; -import { RedisVariadicArgument, pushVariadicArguments, transformBooleanArrayReply } from '@redis/client/dist/lib/commands/generic-transformers'; +import { CommandParser } from '@redis/client/lib/client/parser'; +import { RedisArgument, Command } from '@redis/client/lib/RESP/types'; +import { RedisVariadicArgument, transformBooleanArrayReply } from '@redis/client/lib/commands/generic-transformers'; export default { - FIRST_KEY_INDEX: undefined, IS_READ_ONLY: false, - transformArguments(key: RedisArgument, items: RedisVariadicArgument) { - return pushVariadicArguments(['TOPK.QUERY', key], items); + parseCommand(parser: CommandParser, key: RedisArgument, items: RedisVariadicArgument) { + parser.push('TOPK.QUERY'); + parser.pushKey(key); + parser.pushVariadic(items); }, transformReply: transformBooleanArrayReply } as const satisfies Command; diff --git a/packages/bloom/lib/commands/top-k/RESERVE.spec.ts b/packages/bloom/lib/commands/top-k/RESERVE.spec.ts index 39d8fb7efc6..aa8d194f940 100644 --- a/packages/bloom/lib/commands/top-k/RESERVE.spec.ts +++ b/packages/bloom/lib/commands/top-k/RESERVE.spec.ts @@ -1,19 +1,20 @@ import { strict as assert } from 'node:assert'; import testUtils, { GLOBAL } from '../../test-utils'; import RESERVE from './RESERVE'; +import { parseArgs } from '@redis/client/lib/commands/generic-transformers'; describe('TOPK.RESERVE', () => { describe('transformArguments', () => { it('simple', () => { assert.deepEqual( - RESERVE.transformArguments('topK', 3), + parseArgs(RESERVE, 'topK', 3), ['TOPK.RESERVE', 'topK', '3'] ); }); it('with options', () => { assert.deepEqual( - RESERVE.transformArguments('topK', 3, { + parseArgs(RESERVE, 'topK', 3, { width: 8, depth: 7, decay: 0.9 diff --git a/packages/bloom/lib/commands/top-k/RESERVE.ts b/packages/bloom/lib/commands/top-k/RESERVE.ts index 12671728fea..f3ef3bf7200 100644 --- a/packages/bloom/lib/commands/top-k/RESERVE.ts +++ b/packages/bloom/lib/commands/top-k/RESERVE.ts @@ -1,4 +1,5 @@ -import { SimpleStringReply, Command, RedisArgument } from '@redis/client/dist/lib/RESP/types'; +import { CommandParser } from '@redis/client/lib/client/parser'; +import { SimpleStringReply, Command, RedisArgument } from '@redis/client/lib/RESP/types'; export interface TopKReserveOptions { width: number; @@ -7,20 +8,19 @@ export interface TopKReserveOptions { } export default { - FIRST_KEY_INDEX: 1, IS_READ_ONLY: false, - transformArguments(key: RedisArgument, topK: number, options?: TopKReserveOptions) { - const args = ['TOPK.RESERVE', key, topK.toString()]; + parseCommand(parser: CommandParser, key: RedisArgument, topK: number, options?: TopKReserveOptions) { + parser.push('TOPK.RESERVE'); + parser.pushKey(key); + parser.push(topK.toString()); if (options) { - args.push( + parser.push( options.width.toString(), options.depth.toString(), options.decay.toString() ); } - - return args; }, transformReply: undefined as unknown as () => SimpleStringReply<'OK'> } as const satisfies Command; diff --git a/packages/bloom/lib/commands/top-k/index.ts b/packages/bloom/lib/commands/top-k/index.ts index fb5de543cab..0352745fd07 100644 --- a/packages/bloom/lib/commands/top-k/index.ts +++ b/packages/bloom/lib/commands/top-k/index.ts @@ -1,4 +1,4 @@ -import type { RedisCommands } from '@redis/client/dist/lib/RESP/types'; +import type { RedisCommands } from '@redis/client/lib/RESP/types'; import ADD from './ADD'; import COUNT from './COUNT'; import INCRBY from './INCRBY'; diff --git a/packages/client/lib/RESP/encoder.ts b/packages/client/lib/RESP/encoder.ts index af857711dc3..995650627f1 100644 --- a/packages/client/lib/RESP/encoder.ts +++ b/packages/client/lib/RESP/encoder.ts @@ -2,7 +2,7 @@ import { RedisArgument } from './types'; const CRLF = '\r\n'; -export default function encodeCommand(args: Array): Array { +export default function encodeCommand(args: ReadonlyArray): ReadonlyArray { const toWrite: Array = []; let strings = '*' + args.length + CRLF; diff --git a/packages/client/lib/RESP/types.ts b/packages/client/lib/RESP/types.ts index 46fcd7ac8c1..692c433a49d 100644 --- a/packages/client/lib/RESP/types.ts +++ b/packages/client/lib/RESP/types.ts @@ -1,3 +1,5 @@ +import { CommandParser } from '../client/parser'; +import { Tail } from '../commands/generic-transformers'; import { BlobError, SimpleError } from '../errors'; import { RedisScriptConfig, SHA1 } from '../lua-script'; import { RESP_TYPES } from './decoder'; @@ -272,15 +274,16 @@ export type CommandArguments = Array & { preserve?: unknown }; // }; export type Command = { - FIRST_KEY_INDEX?: number | ((this: void, ...args: Array) => RedisArgument | undefined); + CACHEABLE?: boolean; IS_READ_ONLY?: boolean; /** * @internal * TODO: remove once `POLICIES` is implemented */ IS_FORWARD_COMMAND?: boolean; + NOT_KEYED_COMMAND?: true; // POLICIES?: CommandPolicies; - transformArguments(this: void, ...args: Array): CommandArguments; + parseCommand(this: void, parser: CommandParser, ...args: Array): void; TRANSFORM_LEGACY_REPLY?: boolean; transformReply: TransformReply | Record; unstableResp3?: boolean; @@ -365,7 +368,7 @@ export type CommandSignature< COMMAND extends Command, RESP extends RespVersions, TYPE_MAPPING extends TypeMapping -> = (...args: Parameters) => Promise, TYPE_MAPPING>>; +> = (...args: Tail>) => Promise, TYPE_MAPPING>>; // export type CommandWithPoliciesSignature< // COMMAND extends Command, diff --git a/packages/client/lib/client/commands-queue.ts b/packages/client/lib/client/commands-queue.ts index a4029779fc8..15e8a747b98 100644 --- a/packages/client/lib/client/commands-queue.ts +++ b/packages/client/lib/client/commands-queue.ts @@ -1,7 +1,7 @@ import { SinglyLinkedList, DoublyLinkedNode, DoublyLinkedList } from './linked-list'; import encodeCommand from '../RESP/encoder'; import { Decoder, PUSH_TYPE_MAPPING, RESP_TYPES } from '../RESP/decoder'; -import { CommandArguments, TypeMapping, ReplyUnion, RespVersions } from '../RESP/types'; +import { TypeMapping, ReplyUnion, RespVersions, RedisArgument } from '../RESP/types'; import { ChannelListeners, PubSub, PubSubCommand, PubSubListener, PubSubType, PubSubTypeListeners } from './pub-sub'; import { AbortError, ErrorReply } from '../errors'; import { MonitorCallback } from '.'; @@ -17,7 +17,7 @@ export interface CommandOptions { } export interface CommandToWrite extends CommandWaitingForReply { - args: CommandArguments; + args: ReadonlyArray; chainId: symbol | undefined; abort: { signal: AbortSignal; @@ -117,7 +117,7 @@ export default class RedisCommandsQueue { } addCommand( - args: CommandArguments, + args: ReadonlyArray, options?: CommandOptions ): Promise { if (this.#maxLength && this.#toWrite.length + this.#waitingForReply.length >= this.#maxLength) { @@ -346,7 +346,7 @@ export default class RedisCommandsQueue { *commandsToWrite() { let toSend = this.#toWrite.shift(); while (toSend) { - let encoded: CommandArguments; + let encoded: ReadonlyArray try { encoded = encodeCommand(toSend.args); } catch (err) { diff --git a/packages/client/lib/client/index.spec.ts b/packages/client/lib/client/index.spec.ts index 50ed3d39da1..cd2040ec97f 100644 --- a/packages/client/lib/client/index.spec.ts +++ b/packages/client/lib/client/index.spec.ts @@ -9,6 +9,7 @@ import { MATH_FUNCTION, loadMathFunction } from '../commands/FUNCTION_LOAD.spec' import { RESP_TYPES } from '../RESP/decoder'; import { BlobStringReply, NumberReply } from '../RESP/types'; import { SortedSetMember } from '../commands/generic-transformers'; +import { CommandParser } from './parser'; export const SQUARE_SCRIPT = defineScript({ SCRIPT: @@ -16,8 +17,8 @@ export const SQUARE_SCRIPT = defineScript({ return number * number`, NUMBER_OF_KEYS: 1, FIRST_KEY_INDEX: 0, - transformArguments(key: string) { - return [key]; + parseCommand(parser: CommandParser, key: string) { + parser.pushKey(key); }, transformReply: undefined as unknown as () => NumberReply }); @@ -318,8 +319,8 @@ describe('Client', () => { const module = { echo: { - transformArguments(message: string) { - return ['ECHO', message]; + parseCommand(parser: CommandParser, message: string) { + parser.push('ECHO', message); }, transformReply: undefined as unknown as () => BlobStringReply } diff --git a/packages/client/lib/client/index.ts b/packages/client/lib/client/index.ts index 64a3b578815..55355a133dd 100644 --- a/packages/client/lib/client/index.ts +++ b/packages/client/lib/client/index.ts @@ -7,14 +7,15 @@ import { ClientClosedError, ClientOfflineError, DisconnectsClientError, WatchErr import { URL } from 'node:url'; import { TcpSocketConnectOpts } from 'node:net'; import { PUBSUB_TYPE, PubSubType, PubSubListener, PubSubTypeListeners, ChannelListeners } from './pub-sub'; -import { Command, CommandSignature, TypeMapping, CommanderConfig, RedisFunction, RedisFunctions, RedisModules, RedisScript, RedisScripts, ReplyUnion, RespVersions, RedisArgument, ReplyWithTypeMapping, SimpleStringReply } from '../RESP/types'; +import { Command, CommandSignature, TypeMapping, CommanderConfig, RedisFunction, RedisFunctions, RedisModules, RedisScript, RedisScripts, ReplyUnion, RespVersions, RedisArgument, ReplyWithTypeMapping, SimpleStringReply, TransformReply } from '../RESP/types'; import RedisClientMultiCommand, { RedisClientMultiCommandType } from './multi-command'; import { RedisMultiQueuedCommand } from '../multi-command'; import HELLO, { HelloOptions } from '../commands/HELLO'; import { ScanOptions, ScanCommonOptions } from '../commands/SCAN'; import { RedisLegacyClient, RedisLegacyClientType } from './legacy-mode'; import { RedisPoolOptions, RedisClientPool } from './pool'; -import { RedisVariadicArgument, pushVariadicArguments } from '../commands/generic-transformers'; +import { RedisVariadicArgument, parseArgs, pushVariadicArguments } from '../commands/generic-transformers'; +import { BasicCommandParser, CommandParser } from './parser'; export interface RedisClientOptions< M extends RedisModules = RedisModules, @@ -151,64 +152,50 @@ export default class RedisClient< > extends EventEmitter { static #createCommand(command: Command, resp: RespVersions) { const transformReply = getTransformReply(command, resp); - return async function (this: ProxyClient, ...args: Array) { - const redisArgs = command.transformArguments(...args); - const typeMapping = this._commandOptions?.typeMapping; - const reply = await this.sendCommand(redisArgs, this._commandOptions); + return async function (this: ProxyClient, ...args: Array) { + const parser = new BasicCommandParser(); + command.parseCommand(parser, ...args); - return transformReply ? - transformReply(reply, redisArgs.preserve, typeMapping) : - reply; - }; + return this._self._executeCommand(command, parser, this._commandOptions, transformReply); + } } static #createModuleCommand(command: Command, resp: RespVersions) { const transformReply = getTransformReply(command, resp); - return async function (this: NamespaceProxyClient, ...args: Array) { - const redisArgs = command.transformArguments(...args); - const typeMapping = this._self._commandOptions?.typeMapping - const reply = await this._self.sendCommand(redisArgs, this._self._commandOptions); + return async function (this: NamespaceProxyClient, ...args: Array) { + const parser = new BasicCommandParser(); + command.parseCommand(parser, ...args); - return transformReply ? - transformReply(reply, redisArgs.preserve, typeMapping) : - reply; + return this._self._executeCommand(command, parser, this._self._commandOptions, transformReply); }; } static #createFunctionCommand(name: string, fn: RedisFunction, resp: RespVersions) { - const prefix = functionArgumentsPrefix(name, fn), - transformReply = getTransformReply(fn, resp); - return async function (this: NamespaceProxyClient, ...args: Array) { - const fnArgs = fn.transformArguments(...args); - const typeMapping = this._self._commandOptions?.typeMapping; + const prefix = functionArgumentsPrefix(name, fn); + const transformReply = getTransformReply(fn, resp); - const reply = await this._self.sendCommand( - prefix.concat(fnArgs), - this._self._commandOptions - ); + return async function (this: NamespaceProxyClient, ...args: Array) { + const parser = new BasicCommandParser(); + parser.push(...prefix); + fn.parseCommand(parser, ...args); - return transformReply ? - transformReply(reply, fnArgs.preserve, typeMapping) : - reply; + return this._self._executeCommand(fn, parser, this._self._commandOptions, transformReply); }; } static #createScriptCommand(script: RedisScript, resp: RespVersions) { - const prefix = scriptArgumentsPrefix(script), - transformReply = getTransformReply(script, resp); + const prefix = scriptArgumentsPrefix(script); + const transformReply = getTransformReply(script, resp); + return async function (this: ProxyClient, ...args: Array) { - const scriptArgs = script.transformArguments(...args); - const redisArgs = prefix.concat(scriptArgs); - const typeMapping = this._commandOptions?.typeMapping; - - const reply = await this.executeScript(script, redisArgs, this._commandOptions); - - return transformReply ? - transformReply(reply, scriptArgs.preserve, typeMapping) : - reply; - }; + const parser = new BasicCommandParser(); + parser.push(...prefix); + script.parseCommand(parser, ...args) + + return this._executeScript(script, parser, this._commandOptions, transformReply); + } } static factory< @@ -376,12 +363,12 @@ export default class RedisClient< } commands.push( - HELLO.transformArguments(this.#options.RESP, hello) + parseArgs(HELLO, this.#options.RESP, hello) ); } else { if (this.#options?.username || this.#options?.password) { commands.push( - COMMANDS.AUTH.transformArguments({ + parseArgs(COMMANDS.AUTH, { username: this.#options.username, password: this.#options.password ?? '' }) @@ -390,7 +377,7 @@ export default class RedisClient< if (this.#options?.name) { commands.push( - COMMANDS.CLIENT_SETNAME.transformArguments(this.#options.name) + parseArgs(COMMANDS.CLIENT_SETNAME, this.#options.name) ); } } @@ -401,7 +388,7 @@ export default class RedisClient< if (this.#options?.readonly) { commands.push( - COMMANDS.READONLY.transformArguments() + parseArgs(COMMANDS.READONLY) ); } @@ -585,35 +572,64 @@ export default class RedisClient< return this as unknown as RedisClientType; } - sendCommand( - args: Array, - options?: CommandOptions - ): Promise { - if (!this._self.#socket.isOpen) { - return Promise.reject(new ClientClosedError()); - } else if (!this._self.#socket.isReady && this._self.#options?.disableOfflineQueue) { - return Promise.reject(new ClientOfflineError()); + /** + * @internal + */ + async _executeCommand( + command: Command, + parser: CommandParser, + commandOptions: CommandOptions | undefined, + transformReply: TransformReply | undefined, + ) { + const reply = await this.sendCommand(parser.redisArgs, commandOptions); + + if (transformReply) { + return transformReply(reply, parser.preserve, commandOptions?.typeMapping); } - const promise = this._self.#queue.addCommand(args, options); - this._self.#scheduleWrite(); - return promise; + return reply; } - async executeScript( + /** + * @internal + */ + async _executeScript( script: RedisScript, - args: Array, - options?: CommandOptions + parser: CommandParser, + options: CommandOptions | undefined, + transformReply: TransformReply | undefined, ) { + const args = parser.redisArgs as Array; + + let reply: ReplyUnion; try { - return await this.sendCommand(args, options); + reply = await this.sendCommand(args, options); } catch (err) { if (!(err as Error)?.message?.startsWith?.('NOSCRIPT')) throw err; args[0] = 'EVAL'; args[1] = script.SCRIPT; - return await this.sendCommand(args, options); + reply = await this.sendCommand(args, options); + } + + return transformReply ? + transformReply(reply, parser.preserve, options?.typeMapping) : + reply; + } + + sendCommand( + args: ReadonlyArray, + options?: CommandOptions + ): Promise { + if (!this._self.#socket.isOpen) { + return Promise.reject(new ClientClosedError()); + } else if (!this._self.#socket.isReady && this._self.#options?.disableOfflineQueue) { + return Promise.reject(new ClientOfflineError()); } + + const promise = this._self.#queue.addCommand(args, options); + this._self.#scheduleWrite(); + return promise; } async SELECT(db: number): Promise { diff --git a/packages/client/lib/client/multi-command.ts b/packages/client/lib/client/multi-command.ts index b6579fcf9bf..a687655b60a 100644 --- a/packages/client/lib/client/multi-command.ts +++ b/packages/client/lib/client/multi-command.ts @@ -2,6 +2,8 @@ import COMMANDS from '../commands'; import RedisMultiCommand, { MULTI_REPLY, MultiReply, MultiReplyType, RedisMultiQueuedCommand } from '../multi-command'; import { ReplyWithTypeMapping, CommandReply, Command, CommandArguments, CommanderConfig, RedisFunctions, RedisModules, RedisScripts, RespVersions, TransformReply, RedisScript, RedisFunction, TypeMapping } from '../RESP/types'; import { attachConfig, functionArgumentsPrefix, getTransformReply } from '../commander'; +import { BasicCommandParser } from './parser'; +import { Tail } from '../commands/generic-transformers'; type CommandSignature< REPLIES extends Array, @@ -11,7 +13,7 @@ type CommandSignature< S extends RedisScripts, RESP extends RespVersions, TYPE_MAPPING extends TypeMapping -> = (...args: Parameters) => RedisClientMultiCommandType< +> = (...args: Tail>) => RedisClientMultiCommandType< [...REPLIES, ReplyWithTypeMapping, TYPE_MAPPING>], M, F, @@ -88,9 +90,16 @@ type ExecuteMulti = (commands: Array, selectedDB?: numb export default class RedisClientMultiCommand { static #createCommand(command: Command, resp: RespVersions) { const transformReply = getTransformReply(command, resp); + return function (this: RedisClientMultiCommand, ...args: Array) { + const parser = new BasicCommandParser(); + command.parseCommand(parser, ...args); + + const redisArgs: CommandArguments = parser.redisArgs; + redisArgs.preserve = parser.preserve; + return this.addCommand( - command.transformArguments(...args), + redisArgs, transformReply ); }; @@ -98,21 +107,33 @@ export default class RedisClientMultiCommand { static #createModuleCommand(command: Command, resp: RespVersions) { const transformReply = getTransformReply(command, resp); + return function (this: { _self: RedisClientMultiCommand }, ...args: Array) { + const parser = new BasicCommandParser(); + command.parseCommand(parser, ...args); + + const redisArgs: CommandArguments = parser.redisArgs; + redisArgs.preserve = parser.preserve; + return this._self.addCommand( - command.transformArguments(...args), + redisArgs, transformReply ); }; } static #createFunctionCommand(name: string, fn: RedisFunction, resp: RespVersions) { - const prefix = functionArgumentsPrefix(name, fn), - transformReply = getTransformReply(fn, resp); + const prefix = functionArgumentsPrefix(name, fn); + const transformReply = getTransformReply(fn, resp); + return function (this: { _self: RedisClientMultiCommand }, ...args: Array) { - const fnArgs = fn.transformArguments(...args), - redisArgs: CommandArguments = prefix.concat(fnArgs); - redisArgs.preserve = fnArgs.preserve; + const parser = new BasicCommandParser(); + parser.push(...prefix); + fn.parseCommand(parser, ...args); + + const redisArgs: CommandArguments = parser.redisArgs; + redisArgs.preserve = parser.preserve; + return this._self.addCommand( redisArgs, transformReply @@ -122,13 +143,19 @@ export default class RedisClientMultiCommand { static #createScriptCommand(script: RedisScript, resp: RespVersions) { const transformReply = getTransformReply(script, resp); + return function (this: RedisClientMultiCommand, ...args: Array) { - this.#multi.addScript( + const parser = new BasicCommandParser(); + script.parseCommand(parser, ...args); + + const redisArgs: CommandArguments = parser.redisArgs; + redisArgs.preserve = parser.preserve; + + return this.#addScript( script, - script.transformArguments(...args), + redisArgs, transformReply ); - return this; }; } @@ -149,17 +176,16 @@ export default class RedisClientMultiCommand { }); } - readonly #multi = new RedisMultiCommand(); + readonly #multi: RedisMultiCommand readonly #executeMulti: ExecuteMulti; readonly #executePipeline: ExecuteMulti; - readonly #typeMapping?: TypeMapping; #selectedDB?: number; constructor(executeMulti: ExecuteMulti, executePipeline: ExecuteMulti, typeMapping?: TypeMapping) { + this.#multi = new RedisMultiCommand(typeMapping); this.#executeMulti = executeMulti; this.#executePipeline = executePipeline; - this.#typeMapping = typeMapping; } SELECT(db: number, transformReply?: TransformReply): this { @@ -175,12 +201,21 @@ export default class RedisClientMultiCommand { return this; } + #addScript( + script: RedisScript, + args: CommandArguments, + transformReply?: TransformReply + ) { + this.#multi.addScript(script, args, transformReply); + + return this; + } + async exec(execAsPipeline = false): Promise> { if (execAsPipeline) return this.execAsPipeline(); return this.#multi.transformReplies( - await this.#executeMulti(this.#multi.queue, this.#selectedDB), - this.#typeMapping + await this.#executeMulti(this.#multi.queue, this.#selectedDB) ) as MultiReplyType; } @@ -194,8 +229,7 @@ export default class RedisClientMultiCommand { if (this.#multi.queue.length === 0) return [] as MultiReplyType; return this.#multi.transformReplies( - await this.#executePipeline(this.#multi.queue, this.#selectedDB), - this.#typeMapping + await this.#executePipeline(this.#multi.queue, this.#selectedDB) ) as MultiReplyType; } diff --git a/packages/client/lib/client/parser.ts b/packages/client/lib/client/parser.ts new file mode 100644 index 00000000000..12eec457739 --- /dev/null +++ b/packages/client/lib/client/parser.ts @@ -0,0 +1,92 @@ +import { RedisArgument } from '../RESP/types'; +import { RedisVariadicArgument } from '../commands/generic-transformers'; + +export interface CommandParser { + redisArgs: ReadonlyArray; + keys: ReadonlyArray; + firstKey: RedisArgument | undefined; + preserve: unknown; + + push: (...arg: Array) => unknown; + pushVariadic: (vals: RedisVariadicArgument) => unknown; + pushVariadicWithLength: (vals: RedisVariadicArgument) => unknown; + pushVariadicNumber: (vals: number | Array) => unknown; + pushKey: (key: RedisArgument) => unknown; // normal push of keys + pushKeys: (keys: RedisVariadicArgument) => unknown; // push multiple keys at a time + pushKeysLength: (keys: RedisVariadicArgument) => unknown; // push multiple keys at a time +} + +export class BasicCommandParser implements CommandParser { + #redisArgs: Array = []; + #keys: Array = []; + preserve: unknown; + + get redisArgs() { + return this.#redisArgs; + } + + get keys() { + return this.#keys; + } + + get firstKey() { + return this.#keys[0]; + } + + push(...arg: Array) { + this.#redisArgs.push(...arg); + }; + + pushVariadic(vals: RedisVariadicArgument) { + if (Array.isArray(vals)) { + for (const val of vals) { + this.push(val); + } + } else { + this.push(vals); + } + } + + pushVariadicWithLength(vals: RedisVariadicArgument) { + if (Array.isArray(vals)) { + this.#redisArgs.push(vals.length.toString()); + } else { + this.#redisArgs.push('1'); + } + this.pushVariadic(vals); + } + + pushVariadicNumber(vals: number | number[]) { + if (Array.isArray(vals)) { + for (const val of vals) { + this.push(val.toString()); + } + } else { + this.push(vals.toString()); + } + } + + pushKey(key: RedisArgument) { + this.#keys.push(key); + this.#redisArgs.push(key); + } + + pushKeysLength(keys: RedisVariadicArgument) { + if (Array.isArray(keys)) { + this.#redisArgs.push(keys.length.toString()); + } else { + this.#redisArgs.push('1'); + } + this.pushKeys(keys); + } + + pushKeys(keys: RedisVariadicArgument) { + if (Array.isArray(keys)) { + this.#keys.push(...keys); + this.#redisArgs.push(...keys); + } else { + this.#keys.push(keys); + this.#redisArgs.push(keys); + } + } +} diff --git a/packages/client/lib/client/pool.ts b/packages/client/lib/client/pool.ts index 4bd99ece8b6..a08377e3d38 100644 --- a/packages/client/lib/client/pool.ts +++ b/packages/client/lib/client/pool.ts @@ -7,6 +7,7 @@ import { TimeoutError } from '../errors'; import { attachConfig, functionArgumentsPrefix, getTransformReply, scriptArgumentsPrefix } from '../commander'; import { CommandOptions } from './commands-queue'; import RedisClientMultiCommand, { RedisClientMultiCommandType } from './multi-command'; +import { BasicCommandParser } from './parser'; export interface RedisPoolOptions { /** @@ -64,63 +65,48 @@ export class RedisClientPool< > extends EventEmitter { static #createCommand(command: Command, resp: RespVersions) { const transformReply = getTransformReply(command, resp); - return async function (this: ProxyPool, ...args: Array) { - const redisArgs = command.transformArguments(...args); - const typeMapping = this._commandOptions?.typeMapping; - const reply = await this.sendCommand(redisArgs, this._commandOptions); + return async function (this: ProxyPool, ...args: Array) { + const parser = new BasicCommandParser(); + command.parseCommand(parser, ...args); - return transformReply ? - transformReply(reply, redisArgs.preserve, typeMapping) : - reply; + return this.execute(client => client._executeCommand(command, parser, this._commandOptions, transformReply)) }; } static #createModuleCommand(command: Command, resp: RespVersions) { const transformReply = getTransformReply(command, resp); - return async function (this: NamespaceProxyPool, ...args: Array) { - const redisArgs = command.transformArguments(...args); - const typeMapping = this._self._commandOptions?.typeMapping; - const reply = await this._self.sendCommand(redisArgs, this._self._commandOptions); + return async function (this: NamespaceProxyPool, ...args: Array) { + const parser = new BasicCommandParser(); + command.parseCommand(parser, ...args); - return transformReply ? - transformReply(reply, redisArgs.preserve, typeMapping) : - reply; + return this._self.execute(client => client._executeCommand(command, parser, this._self._commandOptions, transformReply)) }; } static #createFunctionCommand(name: string, fn: RedisFunction, resp: RespVersions) { - const prefix = functionArgumentsPrefix(name, fn), - transformReply = getTransformReply(fn, resp); - return async function (this: NamespaceProxyPool, ...args: Array) { - const fnArgs = fn.transformArguments(...args); - const typeMapping = this._self._commandOptions?.typeMapping; + const prefix = functionArgumentsPrefix(name, fn); + const transformReply = getTransformReply(fn, resp); - const reply = await this._self.sendCommand( - prefix.concat(fnArgs), - this._self._commandOptions - ); + return async function (this: NamespaceProxyPool, ...args: Array) { + const parser = new BasicCommandParser(); + parser.push(...prefix); + fn.parseCommand(parser, ...args); - return transformReply ? - transformReply(reply, fnArgs.preserve, typeMapping) : - reply; - }; + return this._self.execute(client => client._executeCommand(fn, parser, this._self._commandOptions, transformReply)) }; } static #createScriptCommand(script: RedisScript, resp: RespVersions) { - const prefix = scriptArgumentsPrefix(script), - transformReply = getTransformReply(script, resp); + const prefix = scriptArgumentsPrefix(script); + const transformReply = getTransformReply(script, resp); + return async function (this: ProxyPool, ...args: Array) { - const scriptArgs = script.transformArguments(...args); - const redisArgs = prefix.concat(scriptArgs); - const typeMapping = this._commandOptions?.typeMapping; + const parser = new BasicCommandParser(); + parser.pushVariadic(prefix); + script.parseCommand(parser, ...args); - const reply = await this.executeScript(script, redisArgs, this._commandOptions); - - return transformReply ? - transformReply(reply, scriptArgs.preserve, typeMapping) : - reply; + return this.execute(client => client._executeScript(script, parser, this._commandOptions, transformReply)) }; } @@ -426,14 +412,6 @@ export class RedisClientPool< return this.execute(client => client.sendCommand(args, options)); } - executeScript( - script: RedisScript, - args: Array, - options?: CommandOptions - ) { - return this.execute(client => client.executeScript(script, args, options)); - } - MULTI() { type Multi = new (...args: ConstructorParameters) => RedisClientMultiCommandType<[], M, F, S, RESP, TYPE_MAPPING>; return new ((this as any).Multi as Multi)( diff --git a/packages/client/lib/client/socket.ts b/packages/client/lib/client/socket.ts index 3c2666e1067..36afa36c04a 100644 --- a/packages/client/lib/client/socket.ts +++ b/packages/client/lib/client/socket.ts @@ -271,7 +271,7 @@ export default class RedisSocket extends EventEmitter { }); } - write(iterable: Iterable>) { + write(iterable: Iterable>) { if (!this.#socket) return; this.#socket.cork(); diff --git a/packages/client/lib/cluster/index.ts b/packages/client/lib/cluster/index.ts index 7d01b1a20fe..12928e71f12 100644 --- a/packages/client/lib/cluster/index.ts +++ b/packages/client/lib/cluster/index.ts @@ -9,6 +9,9 @@ import RedisClusterMultiCommand, { RedisClusterMultiCommandType } from './multi- import { PubSubListener } from '../client/pub-sub'; import { ErrorReply } from '../errors'; import { RedisTcpSocketOptions } from '../client/socket'; +import ASKING from '../commands/ASKING'; +import { BasicCommandParser } from '../client/parser'; +import { parseArgs } from '../commands/generic-transformers'; interface ClusterCommander< M extends RedisModules, @@ -69,7 +72,7 @@ export interface RedisClusterOptions< type ClusterCommand< NAME extends PropertyKey, COMMAND extends Command -> = COMMAND['FIRST_KEY_INDEX'] extends undefined ? ( +> = COMMAND['NOT_KEYED_COMMAND'] extends true ? ( COMMAND['IS_FORWARD_COMMAND'] extends true ? NAME : never ) : NAME; @@ -143,131 +146,70 @@ export default class RedisCluster< TYPE_MAPPING extends TypeMapping, // POLICIES extends CommandPolicies > extends EventEmitter { - static extractFirstKey( - command: C, - args: Parameters, - redisArgs: Array - ) { - let key: RedisArgument | undefined; - switch (typeof command.FIRST_KEY_INDEX) { - case 'number': - key = redisArgs[command.FIRST_KEY_INDEX]; - break; - - case 'function': - key = command.FIRST_KEY_INDEX(...args); - break; - } - - return key; - } - static #createCommand(command: Command, resp: RespVersions) { const transformReply = getTransformReply(command, resp); return async function (this: ProxyCluster, ...args: Array) { - const redisArgs = command.transformArguments(...args); - const typeMapping = this._commandOptions?.typeMapping; - - const firstKey = RedisCluster.extractFirstKey( - command, - args, - redisArgs - ); + const parser = new BasicCommandParser(); + command.parseCommand(parser, ...args); - const reply = await this.sendCommand( - firstKey, + return this._self.#execute( + parser.firstKey, command.IS_READ_ONLY, - redisArgs, this._commandOptions, - // command.POLICIES + (client, opts) => client._executeCommand(command, parser, opts, transformReply) ); - - return transformReply ? - transformReply(reply, redisArgs.preserve, typeMapping) : - reply; }; } static #createModuleCommand(command: Command, resp: RespVersions) { const transformReply = getTransformReply(command, resp); - return async function (this: NamespaceProxyCluster, ...args: Array) { - const redisArgs = command.transformArguments(...args); - const typeMapping = this._self._commandOptions?.typeMapping; - const firstKey = RedisCluster.extractFirstKey( - command, - args, - redisArgs - ); + return async function (this: NamespaceProxyCluster, ...args: Array) { + const parser = new BasicCommandParser(); + command.parseCommand(parser, ...args); - const reply = await this._self.sendCommand( - firstKey, + return this._self.#execute( + parser.firstKey, command.IS_READ_ONLY, - redisArgs, this._self._commandOptions, - // command.POLICIES + (client, opts) => client._executeCommand(command, parser, opts, transformReply) ); - - return transformReply ? - transformReply(reply, redisArgs.preserve, typeMapping) : - reply; }; } static #createFunctionCommand(name: string, fn: RedisFunction, resp: RespVersions) { - const prefix = functionArgumentsPrefix(name, fn), - transformReply = getTransformReply(fn, resp); + const prefix = functionArgumentsPrefix(name, fn); + const transformReply = getTransformReply(fn, resp); + return async function (this: NamespaceProxyCluster, ...args: Array) { - const fnArgs = fn.transformArguments(...args); - const redisArgs = prefix.concat(fnArgs); - const typeMapping = this._self._commandOptions?.typeMapping; - - const firstKey = RedisCluster.extractFirstKey( - fn, - args, - fnArgs - ); + const parser = new BasicCommandParser(); + parser.push(...prefix); + fn.parseCommand(parser, ...args); - const reply = await this._self.sendCommand( - firstKey, + return this._self.#execute( + parser.firstKey, fn.IS_READ_ONLY, - redisArgs, this._self._commandOptions, - // fn.POLICIES + (client, opts) => client._executeCommand(fn, parser, opts, transformReply) ); - - return transformReply ? - transformReply(reply, fnArgs.preserve, typeMapping) : - reply; }; } static #createScriptCommand(script: RedisScript, resp: RespVersions) { - const prefix = scriptArgumentsPrefix(script), - transformReply = getTransformReply(script, resp); + const prefix = scriptArgumentsPrefix(script); + const transformReply = getTransformReply(script, resp); + return async function (this: ProxyCluster, ...args: Array) { - const scriptArgs = script.transformArguments(...args); - const redisArgs = prefix.concat(scriptArgs); - const typeMapping = this._commandOptions?.typeMapping; - - const firstKey = RedisCluster.extractFirstKey( - script, - args, - scriptArgs - ); + const parser = new BasicCommandParser(); + parser.push(...prefix); + script.parseCommand(parser, ...args); - const reply = await this.executeScript( - script, - firstKey, + return this._self.#execute( + parser.firstKey, script.IS_READ_ONLY, - redisArgs, this._commandOptions, - // script.POLICIES + (client, opts) => client._executeScript(script, parser, opts, transformReply) ); - - return transformReply ? - transformReply(reply, scriptArgs.preserve, typeMapping) : - reply; }; } @@ -443,15 +385,20 @@ export default class RedisCluster< async #execute( firstKey: RedisArgument | undefined, isReadonly: boolean | undefined, - fn: (client: RedisClientType) => Promise + options: ClusterCommandOptions | undefined, + fn: (client: RedisClientType, opts?: ClusterCommandOptions) => Promise ): Promise { const maxCommandRedirections = this.#options.maxCommandRedirections ?? 16; - let client = await this.#slots.getClient(firstKey, isReadonly), - i = 0; + let client = await this.#slots.getClient(firstKey, isReadonly); + let i = 0; + let myOpts = options; + while (true) { try { - return await fn(client); + return await fn(client, myOpts); } catch (err) { + // reset to passed in options, if changed by an ask request + myOpts = options; // TODO: error class if (++i > maxCommandRedirections || !(err instanceof Error)) { throw err; @@ -469,8 +416,14 @@ export default class RedisCluster< throw new Error(`Cannot find node ${address}`); } - await redirectTo.asking(); client = redirectTo; + + const chainId = Symbol('Asking Chain'); + myOpts = options ? {...options} : {}; + myOpts.chainId = chainId; + + client.sendCommand(parseArgs(ASKING), {chainId: chainId}).catch(err => { console.log(`Asking Failed: ${err}`) } ); + continue; } @@ -495,21 +448,8 @@ export default class RedisCluster< return this._self.#execute( firstKey, isReadonly, - client => client.sendCommand(args, options) - ); - } - - executeScript( - script: RedisScript, - firstKey: RedisArgument | undefined, - isReadonly: boolean | undefined, - args: Array, - options?: CommandOptions - ) { - return this._self.#execute( - firstKey, - isReadonly, - client => client.executeScript(script, args, options) + options, + (client, opts) => client.sendCommand(args, opts) ); } diff --git a/packages/client/lib/cluster/multi-command.ts b/packages/client/lib/cluster/multi-command.ts index 2b02e8d7df2..f370618ff30 100644 --- a/packages/client/lib/cluster/multi-command.ts +++ b/packages/client/lib/cluster/multi-command.ts @@ -2,7 +2,8 @@ import COMMANDS from '../commands'; import RedisMultiCommand, { MULTI_REPLY, MultiReply, MultiReplyType, RedisMultiQueuedCommand } from '../multi-command'; import { ReplyWithTypeMapping, CommandReply, Command, CommandArguments, CommanderConfig, RedisFunctions, RedisModules, RedisScripts, RespVersions, TransformReply, RedisScript, RedisFunction, TypeMapping, RedisArgument } from '../RESP/types'; import { attachConfig, functionArgumentsPrefix, getTransformReply } from '../commander'; -import RedisCluster from '.'; +import { BasicCommandParser } from '../client/parser'; +import { Tail } from '../commands/generic-transformers'; type CommandSignature< REPLIES extends Array, @@ -12,7 +13,7 @@ type CommandSignature< S extends RedisScripts, RESP extends RespVersions, TYPE_MAPPING extends TypeMapping -> = (...args: Parameters) => RedisClusterMultiCommandType< +> = (...args: Tail>) => RedisClusterMultiCommandType< [...REPLIES, ReplyWithTypeMapping, TYPE_MAPPING>], M, F, @@ -93,13 +94,15 @@ export type ClusterMultiExecute = ( export default class RedisClusterMultiCommand { static #createCommand(command: Command, resp: RespVersions) { const transformReply = getTransformReply(command, resp); + return function (this: RedisClusterMultiCommand, ...args: Array) { - const redisArgs = command.transformArguments(...args); - const firstKey = RedisCluster.extractFirstKey( - command, - args, - redisArgs - ); + const parser = new BasicCommandParser(); + command.parseCommand(parser, ...args); + + const redisArgs: CommandArguments = parser.redisArgs; + redisArgs.preserve = parser.preserve; + const firstKey = parser.firstKey; + return this.addCommand( firstKey, command.IS_READ_ONLY, @@ -111,13 +114,15 @@ export default class RedisClusterMultiCommand { static #createModuleCommand(command: Command, resp: RespVersions) { const transformReply = getTransformReply(command, resp); + return function (this: { _self: RedisClusterMultiCommand }, ...args: Array) { - const redisArgs = command.transformArguments(...args), - firstKey = RedisCluster.extractFirstKey( - command, - args, - redisArgs - ); + const parser = new BasicCommandParser(); + command.parseCommand(parser, ...args); + + const redisArgs: CommandArguments = parser.redisArgs; + redisArgs.preserve = parser.preserve; + const firstKey = parser.firstKey; + return this._self.addCommand( firstKey, command.IS_READ_ONLY, @@ -128,17 +133,18 @@ export default class RedisClusterMultiCommand { } static #createFunctionCommand(name: string, fn: RedisFunction, resp: RespVersions) { - const prefix = functionArgumentsPrefix(name, fn), - transformReply = getTransformReply(fn, resp); + const prefix = functionArgumentsPrefix(name, fn); + const transformReply = getTransformReply(fn, resp); + return function (this: { _self: RedisClusterMultiCommand }, ...args: Array) { - const fnArgs = fn.transformArguments(...args); - const redisArgs: CommandArguments = prefix.concat(fnArgs); - const firstKey = RedisCluster.extractFirstKey( - fn, - args, - fnArgs - ); - redisArgs.preserve = fnArgs.preserve; + const parser = new BasicCommandParser(); + parser.push(...prefix); + fn.parseCommand(parser, ...args); + + const redisArgs: CommandArguments = parser.redisArgs; + redisArgs.preserve = parser.preserve; + const firstKey = parser.firstKey; + return this._self.addCommand( firstKey, fn.IS_READ_ONLY, @@ -150,22 +156,22 @@ export default class RedisClusterMultiCommand { static #createScriptCommand(script: RedisScript, resp: RespVersions) { const transformReply = getTransformReply(script, resp); + return function (this: RedisClusterMultiCommand, ...args: Array) { - const scriptArgs = script.transformArguments(...args); - this.#setState( - RedisCluster.extractFirstKey( - script, - args, - scriptArgs - ), - script.IS_READ_ONLY - ); - this.#multi.addScript( + const parser = new BasicCommandParser(); + script.parseCommand(parser, ...args); + + const scriptArgs: CommandArguments = parser.redisArgs; + scriptArgs.preserve = parser.preserve; + const firstKey = parser.firstKey; + + return this.#addScript( + firstKey, + script.IS_READ_ONLY, script, scriptArgs, transformReply ); - return this; }; } @@ -186,12 +192,12 @@ export default class RedisClusterMultiCommand { }); } - readonly #multi = new RedisMultiCommand(); + readonly #multi: RedisMultiCommand + readonly #executeMulti: ClusterMultiExecute; readonly #executePipeline: ClusterMultiExecute; #firstKey: RedisArgument | undefined; #isReadonly: boolean | undefined = true; - readonly #typeMapping?: TypeMapping; constructor( executeMulti: ClusterMultiExecute, @@ -199,10 +205,10 @@ export default class RedisClusterMultiCommand { routing: RedisArgument | undefined, typeMapping?: TypeMapping ) { + this.#multi = new RedisMultiCommand(typeMapping); this.#executeMulti = executeMulti; this.#executePipeline = executePipeline; this.#firstKey = routing; - this.#typeMapping = typeMapping; } #setState( @@ -224,6 +230,19 @@ export default class RedisClusterMultiCommand { return this; } + #addScript( + firstKey: RedisArgument | undefined, + isReadonly: boolean | undefined, + script: RedisScript, + args: CommandArguments, + transformReply?: TransformReply + ) { + this.#setState(firstKey, isReadonly); + this.#multi.addScript(script, args, transformReply); + + return this; + } + async exec(execAsPipeline = false) { if (execAsPipeline) return this.execAsPipeline(); @@ -232,8 +251,7 @@ export default class RedisClusterMultiCommand { this.#firstKey, this.#isReadonly, this.#multi.queue - ), - this.#typeMapping + ) ) as MultiReplyType; } @@ -251,8 +269,7 @@ export default class RedisClusterMultiCommand { this.#firstKey, this.#isReadonly, this.#multi.queue - ), - this.#typeMapping + ) ) as MultiReplyType; } diff --git a/packages/client/lib/commander.ts b/packages/client/lib/commander.ts index 4434317d267..6e5a2687cb1 100644 --- a/packages/client/lib/commander.ts +++ b/packages/client/lib/commander.ts @@ -1,4 +1,4 @@ -import { Command, CommanderConfig, RedisCommands, RedisFunction, RedisFunctions, RedisModules, RedisScript, RedisScripts, RespVersions } from './RESP/types'; +import { Command, CommanderConfig, RedisArgument, RedisCommands, RedisFunction, RedisFunctions, RedisModules, RedisScript, RedisScripts, RespVersions, TransformReply } from './RESP/types'; interface AttachConfigOptions< M extends RedisModules, @@ -87,7 +87,7 @@ function attachNamespace(prototype: any, name: PropertyKey, fns: any) { }); } -export function getTransformReply(command: Command, resp: RespVersions) { +export function getTransformReply(command: Command, resp: RespVersions): TransformReply | undefined { switch (typeof command.transformReply) { case 'function': return command.transformReply; @@ -98,7 +98,7 @@ export function getTransformReply(command: Command, resp: RespVersions) { } export function functionArgumentsPrefix(name: string, fn: RedisFunction) { - const prefix: Array = [ + const prefix: Array = [ fn.IS_READ_ONLY ? 'FCALL_RO' : 'FCALL', name ]; diff --git a/packages/client/lib/commands/ACL_CAT.spec.ts b/packages/client/lib/commands/ACL_CAT.spec.ts index 2ce9d7db922..09d5ecade5a 100644 --- a/packages/client/lib/commands/ACL_CAT.spec.ts +++ b/packages/client/lib/commands/ACL_CAT.spec.ts @@ -1,5 +1,6 @@ import { strict as assert } from 'node:assert'; import testUtils, { GLOBAL } from '../test-utils'; +import { parseArgs } from './generic-transformers'; import ACL_CAT from './ACL_CAT'; describe('ACL CAT', () => { @@ -8,14 +9,14 @@ describe('ACL CAT', () => { describe('transformArguments', () => { it('simple', () => { assert.deepEqual( - ACL_CAT.transformArguments(), + parseArgs(ACL_CAT), ['ACL', 'CAT'] ); }); it('with categoryName', () => { assert.deepEqual( - ACL_CAT.transformArguments('dangerous'), + parseArgs(ACL_CAT, 'dangerous'), ['ACL', 'CAT', 'dangerous'] ); }); diff --git a/packages/client/lib/commands/ACL_CAT.ts b/packages/client/lib/commands/ACL_CAT.ts index dd4762239a7..ae094b732b8 100644 --- a/packages/client/lib/commands/ACL_CAT.ts +++ b/packages/client/lib/commands/ACL_CAT.ts @@ -1,16 +1,14 @@ +import { CommandParser } from '../client/parser'; import { RedisArgument, ArrayReply, BlobStringReply, Command } from '../RESP/types'; export default { - FIRST_KEY_INDEX: undefined, + NOT_KEYED_COMMAND: true, IS_READ_ONLY: true, - transformArguments(categoryName?: RedisArgument) { - const args: Array = ['ACL', 'CAT']; - + parseCommand(parser: CommandParser, categoryName?: RedisArgument) { + parser.push('ACL', 'CAT'); if (categoryName) { - args.push(categoryName); + parser.push(categoryName); } - - return args; }, transformReply: undefined as unknown as () => ArrayReply } as const satisfies Command; diff --git a/packages/client/lib/commands/ACL_DELUSER.spec.ts b/packages/client/lib/commands/ACL_DELUSER.spec.ts index d6acbb22230..45fa3af9fc7 100644 --- a/packages/client/lib/commands/ACL_DELUSER.spec.ts +++ b/packages/client/lib/commands/ACL_DELUSER.spec.ts @@ -1,6 +1,7 @@ import { strict as assert } from 'node:assert'; import testUtils, { GLOBAL } from '../test-utils'; import ACL_DELUSER from './ACL_DELUSER'; +import { parseArgs } from './generic-transformers'; describe('ACL DELUSER', () => { testUtils.isVersionGreaterThanHook([6]); @@ -8,14 +9,14 @@ describe('ACL DELUSER', () => { describe('transformArguments', () => { it('string', () => { assert.deepEqual( - ACL_DELUSER.transformArguments('username'), + parseArgs(ACL_DELUSER, 'username'), ['ACL', 'DELUSER', 'username'] ); }); it('array', () => { assert.deepEqual( - ACL_DELUSER.transformArguments(['1', '2']), + parseArgs(ACL_DELUSER, ['1', '2']), ['ACL', 'DELUSER', '1', '2'] ); }); diff --git a/packages/client/lib/commands/ACL_DELUSER.ts b/packages/client/lib/commands/ACL_DELUSER.ts index c0f8e15d672..5aa66becf75 100644 --- a/packages/client/lib/commands/ACL_DELUSER.ts +++ b/packages/client/lib/commands/ACL_DELUSER.ts @@ -1,11 +1,13 @@ +import { CommandParser } from '../client/parser'; import { NumberReply, Command } from '../RESP/types'; -import { RedisVariadicArgument, pushVariadicArguments } from './generic-transformers'; +import { RedisVariadicArgument } from './generic-transformers'; export default { - FIRST_KEY_INDEX: undefined, + NOT_KEYED_COMMAND: true, IS_READ_ONLY: true, - transformArguments(username: RedisVariadicArgument) { - return pushVariadicArguments(['ACL', 'DELUSER'], username); + parseCommand(parser: CommandParser, username: RedisVariadicArgument) { + parser.push('ACL', 'DELUSER'); + parser.pushVariadic(username); }, transformReply: undefined as unknown as () => NumberReply } as const satisfies Command; diff --git a/packages/client/lib/commands/ACL_DRYRUN.spec.ts b/packages/client/lib/commands/ACL_DRYRUN.spec.ts index 519092e0114..38a4def8361 100644 --- a/packages/client/lib/commands/ACL_DRYRUN.spec.ts +++ b/packages/client/lib/commands/ACL_DRYRUN.spec.ts @@ -1,13 +1,14 @@ import { strict as assert } from 'node:assert'; import testUtils, { GLOBAL } from '../test-utils'; import ACL_DRYRUN from './ACL_DRYRUN'; +import { parseArgs } from './generic-transformers'; describe('ACL DRYRUN', () => { testUtils.isVersionGreaterThanHook([7]); it('transformArguments', () => { assert.deepEqual( - ACL_DRYRUN.transformArguments('default', ['GET', 'key']), + parseArgs(ACL_DRYRUN, 'default', ['GET', 'key']), ['ACL', 'DRYRUN', 'default', 'GET', 'key'] ); }); diff --git a/packages/client/lib/commands/ACL_DRYRUN.ts b/packages/client/lib/commands/ACL_DRYRUN.ts index 257f0fe61e2..09a51bc36f8 100644 --- a/packages/client/lib/commands/ACL_DRYRUN.ts +++ b/packages/client/lib/commands/ACL_DRYRUN.ts @@ -1,15 +1,11 @@ +import { CommandParser } from '../client/parser'; import { RedisArgument, SimpleStringReply, BlobStringReply, Command } from '../RESP/types'; export default { - FIRST_KEY_INDEX: undefined, + NOT_KEYED_COMMAND: true, IS_READ_ONLY: true, - transformArguments(username: RedisArgument, command: Array) { - return [ - 'ACL', - 'DRYRUN', - username, - ...command - ]; + parseCommand(parser: CommandParser, username: RedisArgument, command: Array) { + parser.push('ACL', 'DRYRUN', username, ...command); }, transformReply: undefined as unknown as () => SimpleStringReply<'OK'> | BlobStringReply } as const satisfies Command; diff --git a/packages/client/lib/commands/ACL_GENPASS.spec.ts b/packages/client/lib/commands/ACL_GENPASS.spec.ts index 44c1e167eb7..35e161f424f 100644 --- a/packages/client/lib/commands/ACL_GENPASS.spec.ts +++ b/packages/client/lib/commands/ACL_GENPASS.spec.ts @@ -1,6 +1,7 @@ import { strict as assert } from 'node:assert'; import testUtils, { GLOBAL } from '../test-utils'; import ACL_GENPASS from './ACL_GENPASS'; +import { parseArgs } from './generic-transformers'; describe('ACL GENPASS', () => { testUtils.isVersionGreaterThanHook([6]); @@ -8,14 +9,14 @@ describe('ACL GENPASS', () => { describe('transformArguments', () => { it('simple', () => { assert.deepEqual( - ACL_GENPASS.transformArguments(), + parseArgs(ACL_GENPASS), ['ACL', 'GENPASS'] ); }); it('with bits', () => { assert.deepEqual( - ACL_GENPASS.transformArguments(128), + parseArgs(ACL_GENPASS, 128), ['ACL', 'GENPASS', '128'] ); }); diff --git a/packages/client/lib/commands/ACL_GENPASS.ts b/packages/client/lib/commands/ACL_GENPASS.ts index be89ff90a9a..b5caa29b9b2 100644 --- a/packages/client/lib/commands/ACL_GENPASS.ts +++ b/packages/client/lib/commands/ACL_GENPASS.ts @@ -1,16 +1,14 @@ +import { CommandParser } from '../client/parser'; import { BlobStringReply, Command } from '../RESP/types'; export default { - FIRST_KEY_INDEX: undefined, + NOT_KEYED_COMMAND: true, IS_READ_ONLY: true, - transformArguments(bits?: number) { - const args = ['ACL', 'GENPASS']; - + parseCommand(parser: CommandParser, bits?: number) { + parser.push('ACL', 'GENPASS'); if (bits) { - args.push(bits.toString()); + parser.push(bits.toString()); } - - return args; }, transformReply: undefined as unknown as () => BlobStringReply } as const satisfies Command; diff --git a/packages/client/lib/commands/ACL_GETUSER.spec.ts b/packages/client/lib/commands/ACL_GETUSER.spec.ts index 47351571127..83776a3473a 100644 --- a/packages/client/lib/commands/ACL_GETUSER.spec.ts +++ b/packages/client/lib/commands/ACL_GETUSER.spec.ts @@ -1,13 +1,14 @@ import { strict as assert } from 'node:assert'; import testUtils, { GLOBAL } from '../test-utils'; import ACL_GETUSER from './ACL_GETUSER'; +import { parseArgs } from './generic-transformers'; describe('ACL GETUSER', () => { testUtils.isVersionGreaterThanHook([6]); it('transformArguments', () => { assert.deepEqual( - ACL_GETUSER.transformArguments('username'), + parseArgs(ACL_GETUSER, 'username'), ['ACL', 'GETUSER', 'username'] ); }); diff --git a/packages/client/lib/commands/ACL_GETUSER.ts b/packages/client/lib/commands/ACL_GETUSER.ts index cbbf48a4c69..b4764ad744e 100644 --- a/packages/client/lib/commands/ACL_GETUSER.ts +++ b/packages/client/lib/commands/ACL_GETUSER.ts @@ -1,3 +1,4 @@ +import { CommandParser } from '../client/parser'; import { RedisArgument, TuplesToMapReply, BlobStringReply, ArrayReply, UnwrapReply, Resp2Reply, Command } from '../RESP/types'; type AclUser = TuplesToMapReply<[ @@ -17,10 +18,10 @@ type AclUser = TuplesToMapReply<[ ]>; export default { - FIRST_KEY_INDEX: undefined, + NOT_KEYED_COMMAND: true, IS_READ_ONLY: true, - transformArguments(username: RedisArgument) { - return ['ACL', 'GETUSER', username]; + parseCommand(parser: CommandParser, username: RedisArgument) { + parser.push('ACL', 'GETUSER', username); }, transformReply: { 2: (reply: UnwrapReply>) => ({ diff --git a/packages/client/lib/commands/ACL_LIST.spec.ts b/packages/client/lib/commands/ACL_LIST.spec.ts index b188cae30b0..0f67aaa53e9 100644 --- a/packages/client/lib/commands/ACL_LIST.spec.ts +++ b/packages/client/lib/commands/ACL_LIST.spec.ts @@ -1,13 +1,14 @@ import { strict as assert } from 'node:assert'; import testUtils, { GLOBAL } from '../test-utils'; import ACL_LIST from './ACL_LIST'; +import { parseArgs } from './generic-transformers'; describe('ACL LIST', () => { testUtils.isVersionGreaterThanHook([6]); it('transformArguments', () => { assert.deepEqual( - ACL_LIST.transformArguments(), + parseArgs(ACL_LIST), ['ACL', 'LIST'] ); }); diff --git a/packages/client/lib/commands/ACL_LIST.ts b/packages/client/lib/commands/ACL_LIST.ts index 1a831a4987c..b5f82cf272c 100644 --- a/packages/client/lib/commands/ACL_LIST.ts +++ b/packages/client/lib/commands/ACL_LIST.ts @@ -1,10 +1,11 @@ +import { CommandParser } from '../client/parser'; import { ArrayReply, BlobStringReply, Command } from '../RESP/types'; export default { - FIRST_KEY_INDEX: undefined, + NOT_KEYED_COMMAND: true, IS_READ_ONLY: true, - transformArguments() { - return ['ACL', 'LIST']; + parseCommand(parser: CommandParser) { + parser.push('ACL', 'LIST'); }, transformReply: undefined as unknown as () => ArrayReply } as const satisfies Command; diff --git a/packages/client/lib/commands/ACL_LOAD.spec.ts b/packages/client/lib/commands/ACL_LOAD.spec.ts index 68552164ce0..a41ce45e8a6 100644 --- a/packages/client/lib/commands/ACL_LOAD.spec.ts +++ b/packages/client/lib/commands/ACL_LOAD.spec.ts @@ -1,13 +1,14 @@ import { strict as assert } from 'node:assert'; import testUtils from '../test-utils'; import ACL_LOAD from './ACL_LOAD'; +import { parseArgs } from './generic-transformers'; describe('ACL LOAD', () => { testUtils.isVersionGreaterThanHook([6]); it('transformArguments', () => { assert.deepEqual( - ACL_LOAD.transformArguments(), + parseArgs(ACL_LOAD), ['ACL', 'LOAD'] ); }); diff --git a/packages/client/lib/commands/ACL_LOAD.ts b/packages/client/lib/commands/ACL_LOAD.ts index 39587829b17..dc4320b99fc 100644 --- a/packages/client/lib/commands/ACL_LOAD.ts +++ b/packages/client/lib/commands/ACL_LOAD.ts @@ -1,10 +1,11 @@ +import { CommandParser } from '../client/parser'; import { SimpleStringReply, Command } from '../RESP/types'; export default { - FIRST_KEY_INDEX: undefined, + NOT_KEYED_COMMAND: true, IS_READ_ONLY: true, - transformArguments() { - return ['ACL', 'LOAD']; + parseCommand(parser: CommandParser) { + parser.push('ACL', 'LOAD'); }, transformReply: undefined as unknown as () => SimpleStringReply<'OK'> } as const satisfies Command; diff --git a/packages/client/lib/commands/ACL_LOG.spec.ts b/packages/client/lib/commands/ACL_LOG.spec.ts index b85a7076f4a..7da61faca37 100644 --- a/packages/client/lib/commands/ACL_LOG.spec.ts +++ b/packages/client/lib/commands/ACL_LOG.spec.ts @@ -1,6 +1,7 @@ import { strict as assert } from 'node:assert'; import testUtils, { GLOBAL } from '../test-utils'; import ACL_LOG from './ACL_LOG'; +import { parseArgs } from './generic-transformers'; describe('ACL LOG', () => { testUtils.isVersionGreaterThanHook([6]); @@ -8,14 +9,14 @@ describe('ACL LOG', () => { describe('transformArguments', () => { it('simple', () => { assert.deepEqual( - ACL_LOG.transformArguments(), + parseArgs(ACL_LOG), ['ACL', 'LOG'] ); }); it('with count', () => { assert.deepEqual( - ACL_LOG.transformArguments(10), + parseArgs(ACL_LOG, 10), ['ACL', 'LOG', '10'] ); }); diff --git a/packages/client/lib/commands/ACL_LOG.ts b/packages/client/lib/commands/ACL_LOG.ts index 0f0a976e093..4cf2722ec86 100644 --- a/packages/client/lib/commands/ACL_LOG.ts +++ b/packages/client/lib/commands/ACL_LOG.ts @@ -1,3 +1,4 @@ +import { CommandParser } from '../client/parser'; import { ArrayReply, TuplesToMapReply, BlobStringReply, NumberReply, DoubleReply, UnwrapReply, Resp2Reply, Command, TypeMapping } from '../RESP/types'; import { transformDoubleReply } from './generic-transformers'; @@ -18,16 +19,13 @@ export type AclLogReply = ArrayReply>; export default { - FIRST_KEY_INDEX: undefined, + NOT_KEYED_COMMAND: true, IS_READ_ONLY: true, - transformArguments(count?: number) { - const args = ['ACL', 'LOG']; - - if (count !== undefined) { - args.push(count.toString()); + parseCommand(parser: CommandParser, count?: number) { + parser.push('ACL', 'LOG'); + if (count != undefined) { + parser.push(count.toString()); } - - return args; }, transformReply: { 2: (reply: UnwrapReply>, preserve?: any, typeMapping?: TypeMapping) => { diff --git a/packages/client/lib/commands/ACL_LOG_RESET.spec.ts b/packages/client/lib/commands/ACL_LOG_RESET.spec.ts index 8849440c1a6..62d193a132d 100644 --- a/packages/client/lib/commands/ACL_LOG_RESET.spec.ts +++ b/packages/client/lib/commands/ACL_LOG_RESET.spec.ts @@ -1,13 +1,14 @@ import { strict as assert } from 'node:assert'; import testUtils, { GLOBAL } from '../test-utils'; import ACL_LOG_RESET from './ACL_LOG_RESET'; +import { parseArgs } from './generic-transformers'; describe('ACL LOG RESET', () => { testUtils.isVersionGreaterThanHook([6]); it('transformArguments', () => { assert.deepEqual( - ACL_LOG_RESET.transformArguments(), + parseArgs(ACL_LOG_RESET), ['ACL', 'LOG', 'RESET'] ); }); diff --git a/packages/client/lib/commands/ACL_LOG_RESET.ts b/packages/client/lib/commands/ACL_LOG_RESET.ts index 91d58d538e9..9a692129bd2 100644 --- a/packages/client/lib/commands/ACL_LOG_RESET.ts +++ b/packages/client/lib/commands/ACL_LOG_RESET.ts @@ -1,11 +1,12 @@ +import { CommandParser } from '../client/parser'; import { SimpleStringReply, Command } from '../RESP/types'; import ACL_LOG from './ACL_LOG'; export default { - FIRST_KEY_INDEX: undefined, + NOT_KEYED_COMMAND: true, IS_READ_ONLY: ACL_LOG.IS_READ_ONLY, - transformArguments() { - return ['ACL', 'LOG', 'RESET']; + parseCommand(parser: CommandParser) { + parser.push('ACL', 'LOG', 'RESET'); }, transformReply: undefined as unknown as () => SimpleStringReply<'OK'> } as const satisfies Command; diff --git a/packages/client/lib/commands/ACL_SAVE.spec.ts b/packages/client/lib/commands/ACL_SAVE.spec.ts index 1fe402867e7..98f7c9f183d 100644 --- a/packages/client/lib/commands/ACL_SAVE.spec.ts +++ b/packages/client/lib/commands/ACL_SAVE.spec.ts @@ -1,13 +1,14 @@ import { strict as assert } from 'node:assert'; import testUtils from '../test-utils'; import ACL_SAVE from './ACL_SAVE'; +import { parseArgs } from './generic-transformers'; describe('ACL SAVE', () => { testUtils.isVersionGreaterThanHook([6]); it('transformArguments', () => { assert.deepEqual( - ACL_SAVE.transformArguments(), + parseArgs(ACL_SAVE), ['ACL', 'SAVE'] ); }); diff --git a/packages/client/lib/commands/ACL_SAVE.ts b/packages/client/lib/commands/ACL_SAVE.ts index 8c2e2dab115..ec24522724a 100644 --- a/packages/client/lib/commands/ACL_SAVE.ts +++ b/packages/client/lib/commands/ACL_SAVE.ts @@ -1,10 +1,11 @@ +import { CommandParser } from '../client/parser'; import { SimpleStringReply, Command } from '../RESP/types'; export default { - FIRST_KEY_INDEX: undefined, + NOT_KEYED_COMMAND: true, IS_READ_ONLY: true, - transformArguments() { - return ['ACL', 'SAVE']; + parseCommand(parser: CommandParser) { + parser.push('ACL', 'SAVE'); }, transformReply: undefined as unknown as () => SimpleStringReply<'OK'> } as const satisfies Command; diff --git a/packages/client/lib/commands/ACL_SETUSER.spec.ts b/packages/client/lib/commands/ACL_SETUSER.spec.ts index 10aea62ed02..9f39868e809 100644 --- a/packages/client/lib/commands/ACL_SETUSER.spec.ts +++ b/packages/client/lib/commands/ACL_SETUSER.spec.ts @@ -1,6 +1,7 @@ import { strict as assert } from 'node:assert'; import testUtils from '../test-utils'; import ACL_SETUSER from './ACL_SETUSER'; +import { parseArgs } from './generic-transformers'; describe('ACL SETUSER', () => { testUtils.isVersionGreaterThanHook([6]); @@ -8,14 +9,14 @@ describe('ACL SETUSER', () => { describe('transformArguments', () => { it('string', () => { assert.deepEqual( - ACL_SETUSER.transformArguments('username', 'allkeys'), + parseArgs(ACL_SETUSER, 'username', 'allkeys'), ['ACL', 'SETUSER', 'username', 'allkeys'] ); }); it('array', () => { assert.deepEqual( - ACL_SETUSER.transformArguments('username', ['allkeys', 'allchannels']), + parseArgs(ACL_SETUSER, 'username', ['allkeys', 'allchannels']), ['ACL', 'SETUSER', 'username', 'allkeys', 'allchannels'] ); }); diff --git a/packages/client/lib/commands/ACL_SETUSER.ts b/packages/client/lib/commands/ACL_SETUSER.ts index c99fec3d9ba..cad013f4d15 100644 --- a/packages/client/lib/commands/ACL_SETUSER.ts +++ b/packages/client/lib/commands/ACL_SETUSER.ts @@ -1,11 +1,13 @@ +import { CommandParser } from '../client/parser'; import { RedisArgument, SimpleStringReply, Command } from '../RESP/types'; -import { RedisVariadicArgument, pushVariadicArguments } from './generic-transformers'; +import { RedisVariadicArgument } from './generic-transformers'; export default { - FIRST_KEY_INDEX: undefined, + NOT_KEYED_COMMAND: true, IS_READ_ONLY: true, - transformArguments(username: RedisArgument, rule: RedisVariadicArgument) { - return pushVariadicArguments(['ACL', 'SETUSER', username], rule); + parseCommand(parser: CommandParser, username: RedisArgument, rule: RedisVariadicArgument) { + parser.push('ACL', 'SETUSER', username); + parser.pushVariadic(rule); }, transformReply: undefined as unknown as () => SimpleStringReply<'OK'> } as const satisfies Command; diff --git a/packages/client/lib/commands/ACL_USERS.spec.ts b/packages/client/lib/commands/ACL_USERS.spec.ts index 2d433d4ec1e..d897b61e4f3 100644 --- a/packages/client/lib/commands/ACL_USERS.spec.ts +++ b/packages/client/lib/commands/ACL_USERS.spec.ts @@ -1,13 +1,14 @@ import { strict as assert } from 'node:assert'; import testUtils from '../test-utils'; import ACL_USERS from './ACL_USERS'; +import { parseArgs } from './generic-transformers'; describe('ACL USERS', () => { testUtils.isVersionGreaterThanHook([6]); it('transformArguments', () => { assert.deepEqual( - ACL_USERS.transformArguments(), + parseArgs(ACL_USERS), ['ACL', 'USERS'] ); }); diff --git a/packages/client/lib/commands/ACL_USERS.ts b/packages/client/lib/commands/ACL_USERS.ts index ee8c619f25f..6ce4c6d84ef 100644 --- a/packages/client/lib/commands/ACL_USERS.ts +++ b/packages/client/lib/commands/ACL_USERS.ts @@ -1,10 +1,11 @@ +import { CommandParser } from '../client/parser'; import { ArrayReply, BlobStringReply, Command } from '../RESP/types'; export default { - FIRST_KEY_INDEX: undefined, + NOT_KEYED_COMMAND: true, IS_READ_ONLY: true, - transformArguments() { - return ['ACL', 'USERS']; + parseCommand(parser: CommandParser) { + parser.push('ACL', 'USERS'); }, transformReply: undefined as unknown as () => ArrayReply } as const satisfies Command; diff --git a/packages/client/lib/commands/ACL_WHOAMI.spec.ts b/packages/client/lib/commands/ACL_WHOAMI.spec.ts index 24a5cbd1d63..f939c657a7a 100644 --- a/packages/client/lib/commands/ACL_WHOAMI.spec.ts +++ b/packages/client/lib/commands/ACL_WHOAMI.spec.ts @@ -1,13 +1,14 @@ import { strict as assert } from 'node:assert'; import testUtils from '../test-utils'; import ACL_WHOAMI from './ACL_WHOAMI'; +import { parseArgs } from './generic-transformers'; describe('ACL WHOAMI', () => { testUtils.isVersionGreaterThanHook([6]); it('transformArguments', () => { assert.deepEqual( - ACL_WHOAMI.transformArguments(), + parseArgs(ACL_WHOAMI), ['ACL', 'WHOAMI'] ); }); diff --git a/packages/client/lib/commands/ACL_WHOAMI.ts b/packages/client/lib/commands/ACL_WHOAMI.ts index 81a1c84a3c6..eb21a75af5f 100644 --- a/packages/client/lib/commands/ACL_WHOAMI.ts +++ b/packages/client/lib/commands/ACL_WHOAMI.ts @@ -1,10 +1,11 @@ +import { CommandParser } from '../client/parser'; import { BlobStringReply, Command } from '../RESP/types'; export default { - FIRST_KEY_INDEX: undefined, + NOT_KEYED_COMMAND: true, IS_READ_ONLY: true, - transformArguments() { - return ['ACL', 'WHOAMI']; + parseCommand(parser: CommandParser) { + parser.push('ACL', 'WHOAMI'); }, transformReply: undefined as unknown as () => BlobStringReply } as const satisfies Command; diff --git a/packages/client/lib/commands/APPEND.spec.ts b/packages/client/lib/commands/APPEND.spec.ts index ca18a00ac42..925c16917b9 100644 --- a/packages/client/lib/commands/APPEND.spec.ts +++ b/packages/client/lib/commands/APPEND.spec.ts @@ -1,11 +1,12 @@ import { strict as assert } from 'node:assert'; import testUtils, { GLOBAL } from '../test-utils'; import APPEND from './APPEND'; +import { parseArgs } from './generic-transformers'; describe('APPEND', () => { it('transformArguments', () => { assert.deepEqual( - APPEND.transformArguments('key', 'value'), + parseArgs(APPEND, 'key', 'value'), ['APPEND', 'key', 'value'] ); }); diff --git a/packages/client/lib/commands/APPEND.ts b/packages/client/lib/commands/APPEND.ts index 1bc01024998..18fc5c7b3aa 100644 --- a/packages/client/lib/commands/APPEND.ts +++ b/packages/client/lib/commands/APPEND.ts @@ -1,10 +1,11 @@ +import { CommandParser } from '../client/parser'; import { RedisArgument, NumberReply, Command } from '../RESP/types'; export default { - FIRST_KEY_INDEX: 1, IS_READ_ONLY: false, - transformArguments(key: RedisArgument, value: RedisArgument) { - return ['APPEND', key, value]; + parseCommand(parser: CommandParser, key: RedisArgument, value: RedisArgument) { + parser.push('APPEND', key, value); }, + transformReply: undefined as unknown as () => NumberReply } as const satisfies Command; diff --git a/packages/client/lib/commands/ASKING.spec.ts b/packages/client/lib/commands/ASKING.spec.ts index bd83bec599f..7be4d25d449 100644 --- a/packages/client/lib/commands/ASKING.spec.ts +++ b/packages/client/lib/commands/ASKING.spec.ts @@ -1,10 +1,11 @@ import { strict as assert } from 'node:assert'; import ASKING from './ASKING'; +import { parseArgs } from './generic-transformers'; describe('ASKING', () => { it('transformArguments', () => { assert.deepEqual( - ASKING.transformArguments(), + parseArgs(ASKING), ['ASKING'] ); }); diff --git a/packages/client/lib/commands/ASKING.ts b/packages/client/lib/commands/ASKING.ts index c6ada477ee4..92ce8f72390 100644 --- a/packages/client/lib/commands/ASKING.ts +++ b/packages/client/lib/commands/ASKING.ts @@ -1,10 +1,13 @@ +import { CommandParser } from '../client/parser'; import { SimpleStringReply, Command } from '../RESP/types'; +export const ASKING_CMD = 'ASKING'; + export default { - FIRST_KEY_INDEX: undefined, + NOT_KEYED_COMMAND: true, IS_READ_ONLY: true, - transformArguments() { - return ['ASKING']; + parseCommand(parser: CommandParser) { + parser.push(ASKING_CMD); }, transformReply: undefined as unknown as () => SimpleStringReply<'OK'> } as const satisfies Command; diff --git a/packages/client/lib/commands/AUTH.spec.ts b/packages/client/lib/commands/AUTH.spec.ts index 2da016ba873..762dd24f16a 100644 --- a/packages/client/lib/commands/AUTH.spec.ts +++ b/packages/client/lib/commands/AUTH.spec.ts @@ -1,11 +1,12 @@ import { strict as assert } from 'node:assert'; import AUTH from './AUTH'; +import { parseArgs } from './generic-transformers'; describe('AUTH', () => { describe('transformArguments', () => { it('password only', () => { assert.deepEqual( - AUTH.transformArguments({ + parseArgs(AUTH, { password: 'password' }), ['AUTH', 'password'] @@ -14,7 +15,7 @@ describe('AUTH', () => { it('username & password', () => { assert.deepEqual( - AUTH.transformArguments({ + parseArgs(AUTH, { username: 'username', password: 'password' }), diff --git a/packages/client/lib/commands/AUTH.ts b/packages/client/lib/commands/AUTH.ts index 4c7a0b0c765..85b48b0026e 100644 --- a/packages/client/lib/commands/AUTH.ts +++ b/packages/client/lib/commands/AUTH.ts @@ -1,3 +1,4 @@ +import { CommandParser } from '../client/parser'; import { RedisArgument, SimpleStringReply, Command } from '../RESP/types'; export interface AuthOptions { @@ -6,18 +7,14 @@ export interface AuthOptions { } export default { - FIRST_KEY_INDEX: undefined, + NOT_KEYED_COMMAND: true, IS_READ_ONLY: true, - transformArguments({ username, password }: AuthOptions) { - const args: Array = ['AUTH']; - + parseCommand(parser: CommandParser, { username, password }: AuthOptions) { + parser.push('AUTH'); if (username !== undefined) { - args.push(username); + parser.push(username); } - - args.push(password); - - return args; + parser.push(password); }, transformReply: undefined as unknown as () => SimpleStringReply<'OK'> } as const satisfies Command; diff --git a/packages/client/lib/commands/BGREWRITEAOF.spec.ts b/packages/client/lib/commands/BGREWRITEAOF.spec.ts index 5447fc70a79..f58ec9a5762 100644 --- a/packages/client/lib/commands/BGREWRITEAOF.spec.ts +++ b/packages/client/lib/commands/BGREWRITEAOF.spec.ts @@ -1,11 +1,12 @@ import { strict as assert } from 'node:assert'; import testUtils, { GLOBAL } from '../test-utils'; import BGREWRITEAOF from './BGREWRITEAOF'; +import { parseArgs } from './generic-transformers'; describe('BGREWRITEAOF', () => { it('transformArguments', () => { assert.deepEqual( - BGREWRITEAOF.transformArguments(), + parseArgs(BGREWRITEAOF), ['BGREWRITEAOF'] ); }); diff --git a/packages/client/lib/commands/BGREWRITEAOF.ts b/packages/client/lib/commands/BGREWRITEAOF.ts index 5f9a46e318f..c658f3e8529 100644 --- a/packages/client/lib/commands/BGREWRITEAOF.ts +++ b/packages/client/lib/commands/BGREWRITEAOF.ts @@ -1,10 +1,11 @@ +import { CommandParser } from '../client/parser'; import { SimpleStringReply, Command } from '../RESP/types'; export default { - FIRST_KEY_INDEX: undefined, + NOT_KEYED_COMMAND: true, IS_READ_ONLY: true, - transformArguments() { - return ['BGREWRITEAOF']; + parseCommand(parser: CommandParser) { + parser.push('BGREWRITEAOF'); }, transformReply: undefined as unknown as () => SimpleStringReply } as const satisfies Command; diff --git a/packages/client/lib/commands/BGSAVE.spec.ts b/packages/client/lib/commands/BGSAVE.spec.ts index 7944722dd56..dcf7b815119 100644 --- a/packages/client/lib/commands/BGSAVE.spec.ts +++ b/packages/client/lib/commands/BGSAVE.spec.ts @@ -1,19 +1,20 @@ import { strict as assert } from 'node:assert'; import testUtils, { GLOBAL } from '../test-utils'; import BGSAVE from './BGSAVE'; +import { parseArgs } from './generic-transformers'; describe('BGSAVE', () => { describe('transformArguments', () => { it('simple', () => { assert.deepEqual( - BGSAVE.transformArguments(), + parseArgs(BGSAVE), ['BGSAVE'] ); }); it('with SCHEDULE', () => { assert.deepEqual( - BGSAVE.transformArguments({ + parseArgs(BGSAVE, { SCHEDULE: true }), ['BGSAVE', 'SCHEDULE'] diff --git a/packages/client/lib/commands/BGSAVE.ts b/packages/client/lib/commands/BGSAVE.ts index dc0f5056708..1fd6c6b5bdb 100644 --- a/packages/client/lib/commands/BGSAVE.ts +++ b/packages/client/lib/commands/BGSAVE.ts @@ -1,3 +1,4 @@ +import { CommandParser } from '../client/parser'; import { SimpleStringReply, Command } from '../RESP/types'; export interface BgSaveOptions { @@ -5,16 +6,13 @@ export interface BgSaveOptions { } export default { - FIRST_KEY_INDEX: undefined, + NOT_KEYED_COMMAND: true, IS_READ_ONLY: true, - transformArguments(options?: BgSaveOptions) { - const args = ['BGSAVE']; - + parseCommand(parser: CommandParser, options?: BgSaveOptions) { + parser.push('BGSAVE'); if (options?.SCHEDULE) { - args.push('SCHEDULE'); + parser.push('SCHEDULE'); } - - return args; }, transformReply: undefined as unknown as () => SimpleStringReply } as const satisfies Command; diff --git a/packages/client/lib/commands/BITCOUNT.spec.ts b/packages/client/lib/commands/BITCOUNT.spec.ts index ceb6476a31c..e2990472948 100644 --- a/packages/client/lib/commands/BITCOUNT.spec.ts +++ b/packages/client/lib/commands/BITCOUNT.spec.ts @@ -1,12 +1,13 @@ import { strict as assert } from 'node:assert'; import testUtils, { GLOBAL } from '../test-utils'; import BITCOUNT from './BITCOUNT'; +import { parseArgs } from './generic-transformers'; describe('BITCOUNT', () => { - describe('transformArguments', () => { + describe('parseCommand', () => { it('simple', () => { assert.deepEqual( - BITCOUNT.transformArguments('key'), + parseArgs(BITCOUNT, 'key'), ['BITCOUNT', 'key'] ); }); @@ -14,7 +15,7 @@ describe('BITCOUNT', () => { describe('with range', () => { it('simple', () => { assert.deepEqual( - BITCOUNT.transformArguments('key', { + parseArgs(BITCOUNT, 'key', { start: 0, end: 1 }), @@ -24,7 +25,7 @@ describe('BITCOUNT', () => { it('with mode', () => { assert.deepEqual( - BITCOUNT.transformArguments('key', { + parseArgs(BITCOUNT, 'key', { start: 0, end: 1, mode: 'BIT' diff --git a/packages/client/lib/commands/BITCOUNT.ts b/packages/client/lib/commands/BITCOUNT.ts index 6ec6b89be8b..decfb754db5 100644 --- a/packages/client/lib/commands/BITCOUNT.ts +++ b/packages/client/lib/commands/BITCOUNT.ts @@ -1,3 +1,4 @@ +import { CommandParser } from '../client/parser'; import { RedisArgument, NumberReply, Command } from '../RESP/types'; export interface BitCountRange { @@ -7,23 +8,19 @@ export interface BitCountRange { } export default { - FIRST_KEY_INDEX: 1, + CACHEABLE: true, IS_READ_ONLY: true, - transformArguments(key: RedisArgument, range?: BitCountRange) { - const args = ['BITCOUNT', key]; - + parseCommand(parser: CommandParser, key: RedisArgument, range?: BitCountRange) { + parser.push('BITCOUNT'); + parser.pushKey(key); if (range) { - args.push( - range.start.toString(), - range.end.toString() - ); + parser.push(range.start.toString()); + parser.push(range.end.toString()); if (range.mode) { - args.push(range.mode); + parser.push(range.mode); } } - - return args; }, transformReply: undefined as unknown as () => NumberReply } as const satisfies Command; diff --git a/packages/client/lib/commands/BITFIELD.spec.ts b/packages/client/lib/commands/BITFIELD.spec.ts index 7f805755493..5fcc112466b 100644 --- a/packages/client/lib/commands/BITFIELD.spec.ts +++ b/packages/client/lib/commands/BITFIELD.spec.ts @@ -1,11 +1,12 @@ import { strict as assert } from 'node:assert'; import testUtils, { GLOBAL } from '../test-utils'; import BITFIELD from './BITFIELD'; +import { parseArgs } from './generic-transformers'; describe('BITFIELD', () => { it('transformArguments', () => { assert.deepEqual( - BITFIELD.transformArguments('key', [{ + parseArgs(BITFIELD, 'key', [{ operation: 'OVERFLOW', behavior: 'WRAP' }, { diff --git a/packages/client/lib/commands/BITFIELD.ts b/packages/client/lib/commands/BITFIELD.ts index 5d7d4bf7282..f095b4cf7a6 100644 --- a/packages/client/lib/commands/BITFIELD.ts +++ b/packages/client/lib/commands/BITFIELD.ts @@ -1,3 +1,4 @@ +import { CommandParser } from '../client/parser'; import { RedisArgument, ArrayReply, NumberReply, NullReply, Command } from '../RESP/types'; export type BitFieldEncoding = `${'i' | 'u'}${number}`; @@ -39,15 +40,15 @@ export type BitFieldRoOperations = Array< >; export default { - FIRST_KEY_INDEX: 1, IS_READ_ONLY: false, - transformArguments(key: RedisArgument, operations: BitFieldOperations) { - const args = ['BITFIELD', key]; + parseCommand(parser: CommandParser, key: RedisArgument, operations: BitFieldOperations) { + parser.push('BITFIELD'); + parser.pushKey(key); for (const options of operations) { switch (options.operation) { case 'GET': - args.push( + parser.push( 'GET', options.encoding, options.offset.toString() @@ -55,7 +56,7 @@ export default { break; case 'SET': - args.push( + parser.push( 'SET', options.encoding, options.offset.toString(), @@ -64,7 +65,7 @@ export default { break; case 'INCRBY': - args.push( + parser.push( 'INCRBY', options.encoding, options.offset.toString(), @@ -73,15 +74,13 @@ export default { break; case 'OVERFLOW': - args.push( + parser.push( 'OVERFLOW', options.behavior ); break; } } - - return args; }, transformReply: undefined as unknown as () => ArrayReply } as const satisfies Command; diff --git a/packages/client/lib/commands/BITFIELD_RO.spec.ts b/packages/client/lib/commands/BITFIELD_RO.spec.ts index 0793100193f..f2c1797412f 100644 --- a/packages/client/lib/commands/BITFIELD_RO.spec.ts +++ b/packages/client/lib/commands/BITFIELD_RO.spec.ts @@ -1,13 +1,14 @@ import { strict as assert } from 'node:assert'; import testUtils, { GLOBAL } from '../test-utils'; import BITFIELD_RO from './BITFIELD_RO'; +import { parseArgs } from './generic-transformers'; describe('BITFIELD_RO', () => { testUtils.isVersionGreaterThanHook([6, 2]); - it('transformArguments', () => { + it('parseCommand', () => { assert.deepEqual( - BITFIELD_RO.transformArguments('key', [{ + parseArgs(BITFIELD_RO, 'key', [{ encoding: 'i8', offset: 0 }]), diff --git a/packages/client/lib/commands/BITFIELD_RO.ts b/packages/client/lib/commands/BITFIELD_RO.ts index 99e500abc04..66001718b80 100644 --- a/packages/client/lib/commands/BITFIELD_RO.ts +++ b/packages/client/lib/commands/BITFIELD_RO.ts @@ -1,3 +1,4 @@ +import { CommandParser } from '../client/parser'; import { RedisArgument, ArrayReply, NumberReply, Command } from '../RESP/types'; import { BitFieldGetOperation } from './BITFIELD'; @@ -6,20 +7,17 @@ export type BitFieldRoOperations = Array< >; export default { - FIRST_KEY_INDEX: 1, + CACHEABLE: true, IS_READ_ONLY: true, - transformArguments(key: RedisArgument, operations: BitFieldRoOperations) { - const args = ['BITFIELD_RO', key]; + parseCommand(parser: CommandParser, key: RedisArgument, operations: BitFieldRoOperations) { + parser.push('BITFIELD_RO'); + parser.pushKey(key); for (const operation of operations) { - args.push( - 'GET', - operation.encoding, - operation.offset.toString() - ); + parser.push('GET'); + parser.push(operation.encoding); + parser.push(operation.offset.toString()) } - - return args; }, transformReply: undefined as unknown as () => ArrayReply } as const satisfies Command; diff --git a/packages/client/lib/commands/BITOP.spec.ts b/packages/client/lib/commands/BITOP.spec.ts index 4df1782467f..25fe48fc13c 100644 --- a/packages/client/lib/commands/BITOP.spec.ts +++ b/packages/client/lib/commands/BITOP.spec.ts @@ -1,19 +1,20 @@ import { strict as assert } from 'node:assert'; import testUtils, { GLOBAL } from '../test-utils'; import BITOP from './BITOP'; +import { parseArgs } from './generic-transformers'; describe('BITOP', () => { describe('transformArguments', () => { it('single key', () => { assert.deepEqual( - BITOP.transformArguments('AND', 'destKey', 'key'), + parseArgs(BITOP, 'AND', 'destKey', 'key'), ['BITOP', 'AND', 'destKey', 'key'] ); }); it('multiple keys', () => { assert.deepEqual( - BITOP.transformArguments('AND', 'destKey', ['1', '2']), + parseArgs(BITOP, 'AND', 'destKey', ['1', '2']), ['BITOP', 'AND', 'destKey', '1', '2'] ); }); diff --git a/packages/client/lib/commands/BITOP.ts b/packages/client/lib/commands/BITOP.ts index 4c34845699e..bb770148114 100644 --- a/packages/client/lib/commands/BITOP.ts +++ b/packages/client/lib/commands/BITOP.ts @@ -1,17 +1,20 @@ +import { CommandParser } from '../client/parser'; import { NumberReply, Command, RedisArgument } from '../RESP/types'; -import { RedisVariadicArgument, pushVariadicArguments } from './generic-transformers'; +import { RedisVariadicArgument } from './generic-transformers'; export type BitOperations = 'AND' | 'OR' | 'XOR' | 'NOT'; export default { - FIRST_KEY_INDEX: 2, IS_READ_ONLY: false, - transformArguments( + parseCommand( + parser: CommandParser, operation: BitOperations, destKey: RedisArgument, key: RedisVariadicArgument ) { - return pushVariadicArguments(['BITOP', operation, destKey], key); + parser.push('BITOP', operation); + parser.pushKey(destKey); + parser.pushKeys(key); }, transformReply: undefined as unknown as () => NumberReply } as const satisfies Command; diff --git a/packages/client/lib/commands/BITPOS.spec.ts b/packages/client/lib/commands/BITPOS.spec.ts index 61940560057..c699deab83c 100644 --- a/packages/client/lib/commands/BITPOS.spec.ts +++ b/packages/client/lib/commands/BITPOS.spec.ts @@ -1,33 +1,34 @@ import { strict as assert } from 'node:assert'; import testUtils, { GLOBAL } from '../test-utils'; import BITPOS from './BITPOS'; +import { parseArgs } from './generic-transformers'; describe('BITPOS', () => { - describe('transformArguments', () => { + describe('parseCommand', () => { it('simple', () => { assert.deepEqual( - BITPOS.transformArguments('key', 1), + parseArgs(BITPOS, 'key', 1), ['BITPOS', 'key', '1'] ); }); it('with start', () => { assert.deepEqual( - BITPOS.transformArguments('key', 1, 1), + parseArgs(BITPOS, 'key', 1, 1), ['BITPOS', 'key', '1', '1'] ); }); it('with start and end', () => { assert.deepEqual( - BITPOS.transformArguments('key', 1, 1, -1), + parseArgs(BITPOS, 'key', 1, 1, -1), ['BITPOS', 'key', '1', '1', '-1'] ); }); it('with start, end and mode', () => { assert.deepEqual( - BITPOS.transformArguments('key', 1, 1, -1, 'BIT'), + parseArgs(BITPOS, 'key', 1, 1, -1, 'BIT'), ['BITPOS', 'key', '1', '1', '-1', 'BIT'] ); }); diff --git a/packages/client/lib/commands/BITPOS.ts b/packages/client/lib/commands/BITPOS.ts index 5d6276dffc0..57e3a63b681 100644 --- a/packages/client/lib/commands/BITPOS.ts +++ b/packages/client/lib/commands/BITPOS.ts @@ -1,31 +1,32 @@ +import { CommandParser } from '../client/parser'; import { RedisArgument, NumberReply, Command } from '../RESP/types'; import { BitValue } from './generic-transformers'; export default { - FIRST_KEY_INDEX: 1, + CACHEABLE: true, IS_READ_ONLY: true, - transformArguments( + parseCommand(parser: CommandParser, key: RedisArgument, bit: BitValue, start?: number, end?: number, mode?: 'BYTE' | 'BIT' ) { - const args = ['BITPOS', key, bit.toString()]; + parser.push('BITPOS'); + parser.pushKey(key); + parser.push(bit.toString()); if (start !== undefined) { - args.push(start.toString()); + parser.push(start.toString()); } if (end !== undefined) { - args.push(end.toString()); + parser.push(end.toString()); } if (mode) { - args.push(mode); + parser.push(mode); } - - return args; }, transformReply: undefined as unknown as () => NumberReply } as const satisfies Command; diff --git a/packages/client/lib/commands/BLMOVE.spec.ts b/packages/client/lib/commands/BLMOVE.spec.ts index 0eca8c61005..d4e9e024a8c 100644 --- a/packages/client/lib/commands/BLMOVE.spec.ts +++ b/packages/client/lib/commands/BLMOVE.spec.ts @@ -1,13 +1,14 @@ import { strict as assert } from 'node:assert'; import testUtils, { GLOBAL, BLOCKING_MIN_VALUE } from '../test-utils'; import BLMOVE from './BLMOVE'; +import { parseArgs } from './generic-transformers'; describe('BLMOVE', () => { testUtils.isVersionGreaterThanHook([6, 2]); it('transformArguments', () => { assert.deepEqual( - BLMOVE.transformArguments('source', 'destination', 'LEFT', 'RIGHT', 0), + parseArgs(BLMOVE, 'source', 'destination', 'LEFT', 'RIGHT', 0), ['BLMOVE', 'source', 'destination', 'LEFT', 'RIGHT', '0'] ); }); diff --git a/packages/client/lib/commands/BLMOVE.ts b/packages/client/lib/commands/BLMOVE.ts index c7e4844375f..b0ada7cdb20 100644 --- a/packages/client/lib/commands/BLMOVE.ts +++ b/packages/client/lib/commands/BLMOVE.ts @@ -1,24 +1,20 @@ +import { CommandParser } from '../client/parser'; import { RedisArgument, BlobStringReply, NullReply, Command } from '../RESP/types'; import { ListSide } from './generic-transformers'; export default { - FIRST_KEY_INDEX: 1, IS_READ_ONLY: false, - transformArguments( + parseCommand( + parser: CommandParser, source: RedisArgument, destination: RedisArgument, sourceSide: ListSide, destinationSide: ListSide, timeout: number ) { - return [ - 'BLMOVE', - source, - destination, - sourceSide, - destinationSide, - timeout.toString() - ]; + parser.push('BLMOVE'); + parser.pushKeys([source, destination]); + parser.push(sourceSide, destinationSide, timeout.toString()) }, transformReply: undefined as unknown as () => BlobStringReply | NullReply } as const satisfies Command; diff --git a/packages/client/lib/commands/BLMPOP.spec.ts b/packages/client/lib/commands/BLMPOP.spec.ts index b40556b1e46..6cda524b50f 100644 --- a/packages/client/lib/commands/BLMPOP.spec.ts +++ b/packages/client/lib/commands/BLMPOP.spec.ts @@ -1,6 +1,7 @@ import { strict as assert } from 'node:assert'; import testUtils, { GLOBAL, BLOCKING_MIN_VALUE } from '../test-utils'; import BLMPOP from './BLMPOP'; +import { parseArgs } from './generic-transformers'; describe('BLMPOP', () => { testUtils.isVersionGreaterThanHook([7]); @@ -8,14 +9,14 @@ describe('BLMPOP', () => { describe('transformArguments', () => { it('simple', () => { assert.deepEqual( - BLMPOP.transformArguments(0, 'key', 'LEFT'), + parseArgs(BLMPOP, 0, 'key', 'LEFT'), ['BLMPOP', '0', '1', 'key', 'LEFT'] ); }); it('with COUNT', () => { assert.deepEqual( - BLMPOP.transformArguments(0, 'key', 'LEFT', { + parseArgs(BLMPOP, 0, 'key', 'LEFT', { COUNT: 1 }), ['BLMPOP', '0', '1', 'key', 'LEFT', 'COUNT', '1'] diff --git a/packages/client/lib/commands/BLMPOP.ts b/packages/client/lib/commands/BLMPOP.ts index 3122e908600..15d03f8d822 100644 --- a/packages/client/lib/commands/BLMPOP.ts +++ b/packages/client/lib/commands/BLMPOP.ts @@ -1,17 +1,12 @@ +import { CommandParser } from '../client/parser'; import { Command } from '../RESP/types'; -import LMPOP, { LMPopArguments, transformLMPopArguments } from './LMPOP'; +import LMPOP, { LMPopArguments, parseLMPopArguments } from './LMPOP'; export default { - FIRST_KEY_INDEX: 3, IS_READ_ONLY: false, - transformArguments( - timeout: number, - ...args: LMPopArguments - ) { - return transformLMPopArguments( - ['BLMPOP', timeout.toString()], - ...args - ); - }, + parseCommand(parser: CommandParser, timeout: number, ...args: LMPopArguments) { + parser.push('BLMPOP', timeout.toString()); + parseLMPopArguments(parser, ...args); + }, transformReply: LMPOP.transformReply } as const satisfies Command; diff --git a/packages/client/lib/commands/BLPOP.spec.ts b/packages/client/lib/commands/BLPOP.spec.ts index 4bcc08d0fc9..1bb53a774b7 100644 --- a/packages/client/lib/commands/BLPOP.spec.ts +++ b/packages/client/lib/commands/BLPOP.spec.ts @@ -1,19 +1,20 @@ import { strict as assert } from 'node:assert'; import testUtils, { GLOBAL, BLOCKING_MIN_VALUE } from '../test-utils'; import BLPOP from './BLPOP'; +import { parseArgs } from './generic-transformers'; describe('BLPOP', () => { describe('transformArguments', () => { it('single', () => { assert.deepEqual( - BLPOP.transformArguments('key', 0), + parseArgs(BLPOP, 'key', 0), ['BLPOP', 'key', '0'] ); }); it('multiple', () => { assert.deepEqual( - BLPOP.transformArguments(['1', '2'], 0), + parseArgs(BLPOP, ['1', '2'], 0), ['BLPOP', '1', '2', '0'] ); }); diff --git a/packages/client/lib/commands/BLPOP.ts b/packages/client/lib/commands/BLPOP.ts index c9f8b4775eb..aa0b30e768e 100644 --- a/packages/client/lib/commands/BLPOP.ts +++ b/packages/client/lib/commands/BLPOP.ts @@ -1,16 +1,13 @@ +import { CommandParser } from '../client/parser'; import { UnwrapReply, NullReply, TuplesReply, BlobStringReply, Command } from '../RESP/types'; -import { RedisVariadicArgument, pushVariadicArguments } from './generic-transformers'; +import { RedisVariadicArgument } from './generic-transformers'; export default { - FIRST_KEY_INDEX: 1, IS_READ_ONLY: true, - transformArguments( - key: RedisVariadicArgument, - timeout: number - ) { - const args = pushVariadicArguments(['BLPOP'], key); - args.push(timeout.toString()); - return args; + parseCommand(parser: CommandParser, key: RedisVariadicArgument, timeout: number) { + parser.push('BLPOP'); + parser.pushKeys(key); + parser.push(timeout.toString()); }, transformReply(reply: UnwrapReply>) { if (reply === null) return null; diff --git a/packages/client/lib/commands/BRPOP.spec.ts b/packages/client/lib/commands/BRPOP.spec.ts index 21631d763f4..de23bb34a92 100644 --- a/packages/client/lib/commands/BRPOP.spec.ts +++ b/packages/client/lib/commands/BRPOP.spec.ts @@ -1,19 +1,20 @@ import { strict as assert } from 'node:assert'; import testUtils, { GLOBAL, BLOCKING_MIN_VALUE } from '../test-utils'; import BRPOP from './BRPOP'; +import { parseArgs } from './generic-transformers'; describe('BRPOP', () => { describe('transformArguments', () => { it('single', () => { assert.deepEqual( - BRPOP.transformArguments('key', 0), + parseArgs(BRPOP, 'key', 0), ['BRPOP', 'key', '0'] ); }); it('multiple', () => { assert.deepEqual( - BRPOP.transformArguments(['1', '2'], 0), + parseArgs(BRPOP, ['1', '2'], 0), ['BRPOP', '1', '2', '0'] ); }); diff --git a/packages/client/lib/commands/BRPOP.ts b/packages/client/lib/commands/BRPOP.ts index f9c8aaa5037..401a951556d 100644 --- a/packages/client/lib/commands/BRPOP.ts +++ b/packages/client/lib/commands/BRPOP.ts @@ -1,17 +1,14 @@ +import { CommandParser } from '../client/parser'; import { Command } from '../RESP/types'; -import { RedisVariadicArgument, pushVariadicArguments } from './generic-transformers'; +import { RedisVariadicArgument } from './generic-transformers'; import BLPOP from './BLPOP'; export default { - FIRST_KEY_INDEX: 1, IS_READ_ONLY: true, - transformArguments( - key: RedisVariadicArgument, - timeout: number - ) { - const args = pushVariadicArguments(['BRPOP'], key); - args.push(timeout.toString()); - return args; + parseCommand(parser: CommandParser, key: RedisVariadicArgument, timeout: number) { + parser.push('BRPOP'); + parser.pushKeys(key); + parser.push(timeout.toString()); }, transformReply: BLPOP.transformReply } as const satisfies Command; diff --git a/packages/client/lib/commands/BRPOPLPUSH.spec.ts b/packages/client/lib/commands/BRPOPLPUSH.spec.ts index 1f6dc48bfea..6c2a2a2c900 100644 --- a/packages/client/lib/commands/BRPOPLPUSH.spec.ts +++ b/packages/client/lib/commands/BRPOPLPUSH.spec.ts @@ -1,11 +1,12 @@ import { strict as assert } from 'node:assert'; import testUtils, { GLOBAL, BLOCKING_MIN_VALUE } from '../test-utils'; import BRPOPLPUSH from './BRPOPLPUSH'; +import { parseArgs } from './generic-transformers'; describe('BRPOPLPUSH', () => { it('transformArguments', () => { assert.deepEqual( - BRPOPLPUSH.transformArguments('source', 'destination', 0), + parseArgs(BRPOPLPUSH, 'source', 'destination', 0), ['BRPOPLPUSH', 'source', 'destination', '0'] ); }); diff --git a/packages/client/lib/commands/BRPOPLPUSH.ts b/packages/client/lib/commands/BRPOPLPUSH.ts index d342ea75725..72f63a1c1e5 100644 --- a/packages/client/lib/commands/BRPOPLPUSH.ts +++ b/packages/client/lib/commands/BRPOPLPUSH.ts @@ -1,14 +1,12 @@ +import { CommandParser } from '../client/parser'; import { RedisArgument, BlobStringReply, NullReply, Command } from '../RESP/types'; export default { - FIRST_KEY_INDEX: 1, IS_READ_ONLY: false, - transformArguments( - source: RedisArgument, - destination: RedisArgument, - timeout: number - ) { - return ['BRPOPLPUSH', source, destination, timeout.toString()]; + parseCommand(parser: CommandParser, source: RedisArgument, destination: RedisArgument, timeout: number) { + parser.push('BRPOPLPUSH'); + parser.pushKeys([source, destination]); + parser.push(timeout.toString()); }, transformReply: undefined as unknown as () => BlobStringReply | NullReply } as const satisfies Command; diff --git a/packages/client/lib/commands/BZMPOP.spec.ts b/packages/client/lib/commands/BZMPOP.spec.ts index 554e6898d62..8b082a214ee 100644 --- a/packages/client/lib/commands/BZMPOP.spec.ts +++ b/packages/client/lib/commands/BZMPOP.spec.ts @@ -1,6 +1,7 @@ import { strict as assert } from 'node:assert'; import testUtils, { GLOBAL, BLOCKING_MIN_VALUE } from '../test-utils'; import BZMPOP from './BZMPOP'; +import { parseArgs } from './generic-transformers'; describe('BZMPOP', () => { testUtils.isVersionGreaterThanHook([7]); @@ -8,14 +9,14 @@ describe('BZMPOP', () => { describe('transformArguments', () => { it('simple', () => { assert.deepEqual( - BZMPOP.transformArguments(0, 'key', 'MIN'), + parseArgs(BZMPOP, 0, 'key', 'MIN'), ['BZMPOP', '0', '1', 'key', 'MIN'] ); }); it('with COUNT', () => { assert.deepEqual( - BZMPOP.transformArguments(0, 'key', 'MIN', { + parseArgs(BZMPOP, 0, 'key', 'MIN', { COUNT: 2 }), ['BZMPOP', '0', '1', 'key', 'MIN', 'COUNT', '2'] diff --git a/packages/client/lib/commands/BZMPOP.ts b/packages/client/lib/commands/BZMPOP.ts index 030aa20c66b..98079b7a20d 100644 --- a/packages/client/lib/commands/BZMPOP.ts +++ b/packages/client/lib/commands/BZMPOP.ts @@ -1,11 +1,12 @@ +import { CommandParser } from '../client/parser'; import { Command } from '../RESP/types'; -import ZMPOP, { ZMPopArguments, transformZMPopArguments } from './ZMPOP'; +import ZMPOP, { parseZMPopArguments, ZMPopArguments } from './ZMPOP'; export default { - FIRST_KEY_INDEX: 3, IS_READ_ONLY: false, - transformArguments(timeout: number, ...args: ZMPopArguments) { - return transformZMPopArguments(['BZMPOP', timeout.toString()], ...args); + parseCommand(parser: CommandParser, timeout: number, ...args: ZMPopArguments) { + parser.push('BZMPOP', timeout.toString()); + parseZMPopArguments(parser, ...args); }, transformReply: ZMPOP.transformReply } as const satisfies Command; diff --git a/packages/client/lib/commands/BZPOPMAX.spec.ts b/packages/client/lib/commands/BZPOPMAX.spec.ts index 1f0a4d44f07..fbf60862327 100644 --- a/packages/client/lib/commands/BZPOPMAX.spec.ts +++ b/packages/client/lib/commands/BZPOPMAX.spec.ts @@ -1,19 +1,20 @@ import { strict as assert } from 'node:assert'; import testUtils, { GLOBAL, BLOCKING_MIN_VALUE } from '../test-utils'; import BZPOPMAX from './BZPOPMAX'; +import { parseArgs } from './generic-transformers'; describe('BZPOPMAX', () => { describe('transformArguments', () => { it('single', () => { assert.deepEqual( - BZPOPMAX.transformArguments('key', 0), + parseArgs(BZPOPMAX, 'key', 0), ['BZPOPMAX', 'key', '0'] ); }); it('multiple', () => { assert.deepEqual( - BZPOPMAX.transformArguments(['1', '2'], 0), + parseArgs(BZPOPMAX, ['1', '2'], 0), ['BZPOPMAX', '1', '2', '0'] ); }); diff --git a/packages/client/lib/commands/BZPOPMAX.ts b/packages/client/lib/commands/BZPOPMAX.ts index 792a5592574..1a5159269e9 100644 --- a/packages/client/lib/commands/BZPOPMAX.ts +++ b/packages/client/lib/commands/BZPOPMAX.ts @@ -1,23 +1,13 @@ -import { RedisArgument, NullReply, TuplesReply, BlobStringReply, DoubleReply, UnwrapReply, Command, TypeMapping } from '../RESP/types'; -import { RedisVariadicArgument, pushVariadicArguments, transformDoubleReply } from './generic-transformers'; - -export function transformBZPopArguments( - command: RedisArgument, - key: RedisVariadicArgument, - timeout: number -) { - const args = pushVariadicArguments([command], key); - args.push(timeout.toString()); - return args; -} - -export type BZPopArguments = typeof transformBZPopArguments extends (_: any, ...args: infer T) => any ? T : never; +import { CommandParser } from '../client/parser'; +import { NullReply, TuplesReply, BlobStringReply, DoubleReply, UnwrapReply, Command, TypeMapping } from '../RESP/types'; +import { RedisVariadicArgument, transformDoubleReply } from './generic-transformers'; export default { - FIRST_KEY_INDEX: 1, IS_READ_ONLY: false, - transformArguments(...args: BZPopArguments) { - return transformBZPopArguments('BZPOPMAX', ...args); + parseCommand(parser: CommandParser, keys: RedisVariadicArgument, timeout: number) { + parser.push('BZPOPMAX'); + parser.pushKeys(keys); + parser.push(timeout.toString()); }, transformReply: { 2( diff --git a/packages/client/lib/commands/BZPOPMIN.spec.ts b/packages/client/lib/commands/BZPOPMIN.spec.ts index 7f39f7d1896..2f8cab8dedf 100644 --- a/packages/client/lib/commands/BZPOPMIN.spec.ts +++ b/packages/client/lib/commands/BZPOPMIN.spec.ts @@ -1,19 +1,20 @@ import { strict as assert } from 'node:assert'; import testUtils, { GLOBAL, BLOCKING_MIN_VALUE } from '../test-utils'; import BZPOPMIN from './BZPOPMIN'; +import { parseArgs } from './generic-transformers'; describe('BZPOPMIN', () => { describe('transformArguments', () => { it('single', () => { assert.deepEqual( - BZPOPMIN.transformArguments('key', 0), + parseArgs(BZPOPMIN, 'key', 0), ['BZPOPMIN', 'key', '0'] ); }); it('multiple', () => { assert.deepEqual( - BZPOPMIN.transformArguments(['1', '2'], 0), + parseArgs(BZPOPMIN, ['1', '2'], 0), ['BZPOPMIN', '1', '2', '0'] ); }); diff --git a/packages/client/lib/commands/BZPOPMIN.ts b/packages/client/lib/commands/BZPOPMIN.ts index f27e623528e..9dc4c47e13d 100644 --- a/packages/client/lib/commands/BZPOPMIN.ts +++ b/packages/client/lib/commands/BZPOPMIN.ts @@ -1,11 +1,14 @@ +import { CommandParser } from '../client/parser'; import { Command } from '../RESP/types'; -import BZPOPMAX, { BZPopArguments, transformBZPopArguments } from './BZPOPMAX'; +import { RedisVariadicArgument } from './generic-transformers'; +import BZPOPMAX from './BZPOPMAX'; export default { - FIRST_KEY_INDEX: BZPOPMAX.FIRST_KEY_INDEX, IS_READ_ONLY: BZPOPMAX.IS_READ_ONLY, - transformArguments(...args: BZPopArguments) { - return transformBZPopArguments('BZPOPMIN', ...args); + parseCommand(parser: CommandParser, keys: RedisVariadicArgument, timeout: number) { + parser.push('BZPOPMIN'); + parser.pushKeys(keys); + parser.push(timeout.toString()); }, transformReply: BZPOPMAX.transformReply } as const satisfies Command; diff --git a/packages/client/lib/commands/CLIENT_CACHING.spec.ts b/packages/client/lib/commands/CLIENT_CACHING.spec.ts index 34023f98922..ad3511b3e97 100644 --- a/packages/client/lib/commands/CLIENT_CACHING.spec.ts +++ b/packages/client/lib/commands/CLIENT_CACHING.spec.ts @@ -1,18 +1,19 @@ import { strict as assert } from 'node:assert'; import CLIENT_CACHING from './CLIENT_CACHING'; +import { parseArgs } from './generic-transformers'; describe('CLIENT CACHING', () => { describe('transformArguments', () => { it('true', () => { assert.deepEqual( - CLIENT_CACHING.transformArguments(true), + parseArgs(CLIENT_CACHING, true), ['CLIENT', 'CACHING', 'YES'] ); }); it('false', () => { assert.deepEqual( - CLIENT_CACHING.transformArguments(false), + parseArgs(CLIENT_CACHING, false), ['CLIENT', 'CACHING', 'NO'] ); }); diff --git a/packages/client/lib/commands/CLIENT_CACHING.ts b/packages/client/lib/commands/CLIENT_CACHING.ts index 505ae152f85..9987e49c99b 100644 --- a/packages/client/lib/commands/CLIENT_CACHING.ts +++ b/packages/client/lib/commands/CLIENT_CACHING.ts @@ -1,14 +1,15 @@ +import { CommandParser } from '../client/parser'; import { SimpleStringReply, Command } from '../RESP/types'; export default { - FIRST_KEY_INDEX: undefined, + NOT_KEYED_COMMAND: true, IS_READ_ONLY: true, - transformArguments(value: boolean) { - return [ + parseCommand(parser: CommandParser, value: boolean) { + parser.push( 'CLIENT', 'CACHING', value ? 'YES' : 'NO' - ]; + ); }, transformReply: undefined as unknown as () => SimpleStringReply<'OK'> } as const satisfies Command; diff --git a/packages/client/lib/commands/CLIENT_GETNAME.spec.ts b/packages/client/lib/commands/CLIENT_GETNAME.spec.ts index 8975f1fee9c..5b0dfdb8437 100644 --- a/packages/client/lib/commands/CLIENT_GETNAME.spec.ts +++ b/packages/client/lib/commands/CLIENT_GETNAME.spec.ts @@ -1,11 +1,12 @@ import { strict as assert } from 'node:assert'; import testUtils, { GLOBAL } from '../test-utils'; import CLIENT_GETNAME from './CLIENT_GETNAME'; +import { parseArgs } from './generic-transformers'; describe('CLIENT GETNAME', () => { it('transformArguments', () => { assert.deepEqual( - CLIENT_GETNAME.transformArguments(), + parseArgs(CLIENT_GETNAME), ['CLIENT', 'GETNAME'] ); }); diff --git a/packages/client/lib/commands/CLIENT_GETNAME.ts b/packages/client/lib/commands/CLIENT_GETNAME.ts index c46b576407b..2e18c43cd5f 100644 --- a/packages/client/lib/commands/CLIENT_GETNAME.ts +++ b/packages/client/lib/commands/CLIENT_GETNAME.ts @@ -1,13 +1,11 @@ +import { CommandParser } from '../client/parser'; import { BlobStringReply, NullReply, Command } from '../RESP/types'; export default { - FIRST_KEY_INDEX: undefined, + NOT_KEYED_COMMAND: true, IS_READ_ONLY: true, - transformArguments() { - return [ - 'CLIENT', - 'GETNAME' - ]; + parseCommand(parser: CommandParser) { + parser.push('CLIENT', 'GETNAME'); }, transformReply: undefined as unknown as () => BlobStringReply | NullReply } as const satisfies Command; diff --git a/packages/client/lib/commands/CLIENT_GETREDIR.spec.ts b/packages/client/lib/commands/CLIENT_GETREDIR.spec.ts index 5cfedf2a4e7..a7c375fec26 100644 --- a/packages/client/lib/commands/CLIENT_GETREDIR.spec.ts +++ b/packages/client/lib/commands/CLIENT_GETREDIR.spec.ts @@ -1,10 +1,11 @@ import { strict as assert } from 'node:assert'; import CLIENT_GETREDIR from './CLIENT_GETREDIR'; +import { parseArgs } from './generic-transformers'; describe('CLIENT GETREDIR', () => { it('transformArguments', () => { assert.deepEqual( - CLIENT_GETREDIR.transformArguments(), + parseArgs(CLIENT_GETREDIR), ['CLIENT', 'GETREDIR'] ); }); diff --git a/packages/client/lib/commands/CLIENT_GETREDIR.ts b/packages/client/lib/commands/CLIENT_GETREDIR.ts index ae0b601b4e8..80cc6418dab 100644 --- a/packages/client/lib/commands/CLIENT_GETREDIR.ts +++ b/packages/client/lib/commands/CLIENT_GETREDIR.ts @@ -1,10 +1,11 @@ +import { CommandParser } from '../client/parser'; import { NumberReply, Command } from '../RESP/types'; export default { - FIRST_KEY_INDEX: undefined, + NOT_KEYED_COMMAND: true, IS_READ_ONLY: true, - transformArguments() { - return ['CLIENT', 'GETREDIR'] + parseCommand(parser: CommandParser) { + parser.push('CLIENT', 'GETREDIR'); }, transformReply: undefined as unknown as () => NumberReply } as const satisfies Command; diff --git a/packages/client/lib/commands/CLIENT_ID.spec.ts b/packages/client/lib/commands/CLIENT_ID.spec.ts index 7b51e6bd930..51b308adf2c 100644 --- a/packages/client/lib/commands/CLIENT_ID.spec.ts +++ b/packages/client/lib/commands/CLIENT_ID.spec.ts @@ -1,11 +1,12 @@ import { strict as assert } from 'node:assert'; import testUtils, { GLOBAL } from '../test-utils'; import CLIENT_ID from './CLIENT_ID'; +import { parseArgs } from './generic-transformers'; describe('CLIENT ID', () => { it('transformArguments', () => { assert.deepEqual( - CLIENT_ID.transformArguments(), + parseArgs(CLIENT_ID), ['CLIENT', 'ID'] ); }); diff --git a/packages/client/lib/commands/CLIENT_ID.ts b/packages/client/lib/commands/CLIENT_ID.ts index 165ab1931eb..da58786ec3c 100644 --- a/packages/client/lib/commands/CLIENT_ID.ts +++ b/packages/client/lib/commands/CLIENT_ID.ts @@ -1,10 +1,11 @@ +import { CommandParser } from '../client/parser'; import { NumberReply, Command } from '../RESP/types'; export default { - FIRST_KEY_INDEX: undefined, + NOT_KEYED_COMMAND: true, IS_READ_ONLY: true, - transformArguments() { - return ['CLIENT', 'ID']; + parseCommand(parser: CommandParser) { + parser.push('CLIENT', 'ID'); }, transformReply: undefined as unknown as () => NumberReply } as const satisfies Command; diff --git a/packages/client/lib/commands/CLIENT_INFO.spec.ts b/packages/client/lib/commands/CLIENT_INFO.spec.ts index 0aba384aa3a..50345a46ce3 100644 --- a/packages/client/lib/commands/CLIENT_INFO.spec.ts +++ b/packages/client/lib/commands/CLIENT_INFO.spec.ts @@ -1,13 +1,14 @@ import { strict as assert } from 'node:assert'; import CLIENT_INFO from './CLIENT_INFO'; import testUtils, { GLOBAL } from '../test-utils'; +import { parseArgs } from './generic-transformers'; describe('CLIENT INFO', () => { testUtils.isVersionGreaterThanHook([6, 2]); it('transformArguments', () => { assert.deepEqual( - CLIENT_INFO.transformArguments(), + parseArgs(CLIENT_INFO), ['CLIENT', 'INFO'] ); }); diff --git a/packages/client/lib/commands/CLIENT_INFO.ts b/packages/client/lib/commands/CLIENT_INFO.ts index 88721e2f8b9..36dac175443 100644 --- a/packages/client/lib/commands/CLIENT_INFO.ts +++ b/packages/client/lib/commands/CLIENT_INFO.ts @@ -1,3 +1,4 @@ +import { CommandParser } from '../client/parser'; import { Command, VerbatimStringReply } from '../RESP/types'; export interface ClientInfoReply { @@ -56,10 +57,10 @@ export interface ClientInfoReply { const CLIENT_INFO_REGEX = /([^\s=]+)=([^\s]*)/g; export default { - FIRST_KEY_INDEX: undefined, + NOT_KEYED_COMMAND: true, IS_READ_ONLY: true, - transformArguments() { - return ['CLIENT', 'INFO'] + parseCommand(parser: CommandParser) { + parser.push('CLIENT', 'INFO'); }, transformReply(rawReply: VerbatimStringReply) { const map: Record = {}; diff --git a/packages/client/lib/commands/CLIENT_KILL.spec.ts b/packages/client/lib/commands/CLIENT_KILL.spec.ts index 79254af41f9..5078a267516 100644 --- a/packages/client/lib/commands/CLIENT_KILL.spec.ts +++ b/packages/client/lib/commands/CLIENT_KILL.spec.ts @@ -1,11 +1,12 @@ import { strict as assert } from 'node:assert'; import CLIENT_KILL, { CLIENT_KILL_FILTERS } from './CLIENT_KILL'; +import { parseArgs } from './generic-transformers'; describe('CLIENT KILL', () => { describe('transformArguments', () => { it('ADDRESS', () => { assert.deepEqual( - CLIENT_KILL.transformArguments({ + parseArgs(CLIENT_KILL, { filter: CLIENT_KILL_FILTERS.ADDRESS, address: 'ip:6379' }), @@ -15,7 +16,7 @@ describe('CLIENT KILL', () => { it('LOCAL_ADDRESS', () => { assert.deepEqual( - CLIENT_KILL.transformArguments({ + parseArgs(CLIENT_KILL, { filter: CLIENT_KILL_FILTERS.LOCAL_ADDRESS, localAddress: 'ip:6379' }), @@ -26,7 +27,7 @@ describe('CLIENT KILL', () => { describe('ID', () => { it('string', () => { assert.deepEqual( - CLIENT_KILL.transformArguments({ + parseArgs(CLIENT_KILL, { filter: CLIENT_KILL_FILTERS.ID, id: '1' }), @@ -36,7 +37,7 @@ describe('CLIENT KILL', () => { it('number', () => { assert.deepEqual( - CLIENT_KILL.transformArguments({ + parseArgs(CLIENT_KILL, { filter: CLIENT_KILL_FILTERS.ID, id: 1 }), @@ -47,7 +48,7 @@ describe('CLIENT KILL', () => { it('TYPE', () => { assert.deepEqual( - CLIENT_KILL.transformArguments({ + parseArgs(CLIENT_KILL, { filter: CLIENT_KILL_FILTERS.TYPE, type: 'master' }), @@ -57,7 +58,7 @@ describe('CLIENT KILL', () => { it('USER', () => { assert.deepEqual( - CLIENT_KILL.transformArguments({ + parseArgs(CLIENT_KILL, { filter: CLIENT_KILL_FILTERS.USER, username: 'username' }), @@ -67,7 +68,7 @@ describe('CLIENT KILL', () => { it('MAXAGE', () => { assert.deepEqual( - CLIENT_KILL.transformArguments({ + parseArgs(CLIENT_KILL, { filter: CLIENT_KILL_FILTERS.MAXAGE, maxAge: 10 }), @@ -78,14 +79,14 @@ describe('CLIENT KILL', () => { describe('SKIP_ME', () => { it('undefined', () => { assert.deepEqual( - CLIENT_KILL.transformArguments(CLIENT_KILL_FILTERS.SKIP_ME), + parseArgs(CLIENT_KILL, CLIENT_KILL_FILTERS.SKIP_ME), ['CLIENT', 'KILL', 'SKIPME'] ); }); it('true', () => { assert.deepEqual( - CLIENT_KILL.transformArguments({ + parseArgs(CLIENT_KILL, { filter: CLIENT_KILL_FILTERS.SKIP_ME, skipMe: true }), @@ -95,7 +96,7 @@ describe('CLIENT KILL', () => { it('false', () => { assert.deepEqual( - CLIENT_KILL.transformArguments({ + parseArgs(CLIENT_KILL, { filter: CLIENT_KILL_FILTERS.SKIP_ME, skipMe: false }), @@ -106,7 +107,7 @@ describe('CLIENT KILL', () => { it('TYPE & SKIP_ME', () => { assert.deepEqual( - CLIENT_KILL.transformArguments([ + parseArgs(CLIENT_KILL, [ { filter: CLIENT_KILL_FILTERS.TYPE, type: 'master' diff --git a/packages/client/lib/commands/CLIENT_KILL.ts b/packages/client/lib/commands/CLIENT_KILL.ts index c5eb5304c57..24f8f0873f1 100644 --- a/packages/client/lib/commands/CLIENT_KILL.ts +++ b/packages/client/lib/commands/CLIENT_KILL.ts @@ -1,4 +1,5 @@ -import { RedisArgument, NumberReply, Command } from '../RESP/types'; +import { CommandParser } from '../client/parser'; +import { NumberReply, Command } from '../RESP/types'; export const CLIENT_KILL_FILTERS = { ADDRESS: 'ADDR', @@ -47,43 +48,42 @@ export interface ClientKillMaxAge extends ClientKillFilterCommon) { - const args = ['CLIENT', 'KILL']; + parseCommand(parser: CommandParser, filters: ClientKillFilter | Array) { + parser.push('CLIENT', 'KILL'); if (Array.isArray(filters)) { for (const filter of filters) { - pushFilter(args, filter); + pushFilter(parser, filter); } } else { - pushFilter(args, filters); + pushFilter(parser, filters); } - return args; }, transformReply: undefined as unknown as () => NumberReply } as const satisfies Command; -function pushFilter(args: Array, filter: ClientKillFilter): void { +function pushFilter(parser: CommandParser, filter: ClientKillFilter): void { if (filter === CLIENT_KILL_FILTERS.SKIP_ME) { - args.push('SKIPME'); + parser.push('SKIPME'); return; } - args.push(filter.filter); + parser.push(filter.filter); switch (filter.filter) { case CLIENT_KILL_FILTERS.ADDRESS: - args.push(filter.address); + parser.push(filter.address); break; case CLIENT_KILL_FILTERS.LOCAL_ADDRESS: - args.push(filter.localAddress); + parser.push(filter.localAddress); break; case CLIENT_KILL_FILTERS.ID: - args.push( + parser.push( typeof filter.id === 'number' ? filter.id.toString() : filter.id @@ -91,19 +91,19 @@ function pushFilter(args: Array, filter: ClientKillFilter): void break; case CLIENT_KILL_FILTERS.TYPE: - args.push(filter.type); + parser.push(filter.type); break; case CLIENT_KILL_FILTERS.USER: - args.push(filter.username); + parser.push(filter.username); break; case CLIENT_KILL_FILTERS.SKIP_ME: - args.push(filter.skipMe ? 'yes' : 'no'); + parser.push(filter.skipMe ? 'yes' : 'no'); break; case CLIENT_KILL_FILTERS.MAXAGE: - args.push(filter.maxAge.toString()); + parser.push(filter.maxAge.toString()); break; } } diff --git a/packages/client/lib/commands/CLIENT_LIST.spec.ts b/packages/client/lib/commands/CLIENT_LIST.spec.ts index e967a8dc0ff..34709c5f14f 100644 --- a/packages/client/lib/commands/CLIENT_LIST.spec.ts +++ b/packages/client/lib/commands/CLIENT_LIST.spec.ts @@ -1,19 +1,20 @@ import { strict as assert } from 'node:assert'; import CLIENT_LIST from './CLIENT_LIST'; import testUtils, { GLOBAL } from '../test-utils'; +import { parseArgs } from './generic-transformers'; describe('CLIENT LIST', () => { describe('transformArguments', () => { it('simple', () => { assert.deepEqual( - CLIENT_LIST.transformArguments(), + parseArgs(CLIENT_LIST), ['CLIENT', 'LIST'] ); }); it('with TYPE', () => { assert.deepEqual( - CLIENT_LIST.transformArguments({ + parseArgs(CLIENT_LIST, { TYPE: 'NORMAL' }), ['CLIENT', 'LIST', 'TYPE', 'NORMAL'] @@ -22,7 +23,7 @@ describe('CLIENT LIST', () => { it('with ID', () => { assert.deepEqual( - CLIENT_LIST.transformArguments({ + parseArgs(CLIENT_LIST, { ID: ['1', '2'] }), ['CLIENT', 'LIST', 'ID', '1', '2'] diff --git a/packages/client/lib/commands/CLIENT_LIST.ts b/packages/client/lib/commands/CLIENT_LIST.ts index dc43fb8855d..1e7f3d9ab40 100644 --- a/packages/client/lib/commands/CLIENT_LIST.ts +++ b/packages/client/lib/commands/CLIENT_LIST.ts @@ -1,5 +1,5 @@ +import { CommandParser } from '../client/parser'; import { RedisArgument, VerbatimStringReply, Command } from '../RESP/types'; -import { pushVariadicArguments } from './generic-transformers'; import CLIENT_INFO, { ClientInfoReply } from './CLIENT_INFO'; export interface ListFilterType { @@ -15,21 +15,18 @@ export interface ListFilterId { export type ListFilter = ListFilterType | ListFilterId; export default { - FIRST_KEY_INDEX: undefined, + NOT_KEYED_COMMAND: true, IS_READ_ONLY: true, - transformArguments(filter?: ListFilter) { - let args: Array = ['CLIENT', 'LIST']; - + parseCommand(parser: CommandParser, filter?: ListFilter) { + parser.push('CLIENT', 'LIST'); if (filter) { if (filter.TYPE !== undefined) { - args.push('TYPE', filter.TYPE); + parser.push('TYPE', filter.TYPE); } else { - args.push('ID'); - args = pushVariadicArguments(args, filter.ID); + parser.push('ID'); + parser.pushVariadic(filter.ID); } } - - return args; }, transformReply(rawReply: VerbatimStringReply): Array { const split = rawReply.toString().split('\n'), diff --git a/packages/client/lib/commands/CLIENT_NO-EVICT.spec.ts b/packages/client/lib/commands/CLIENT_NO-EVICT.spec.ts index 5de4dfd7604..50afd413492 100644 --- a/packages/client/lib/commands/CLIENT_NO-EVICT.spec.ts +++ b/packages/client/lib/commands/CLIENT_NO-EVICT.spec.ts @@ -1,6 +1,7 @@ import { strict as assert } from 'node:assert'; import testUtils, { GLOBAL } from '../test-utils'; import CLIENT_NO_EVICT from './CLIENT_NO-EVICT'; +import { parseArgs } from './generic-transformers'; describe('CLIENT NO-EVICT', () => { testUtils.isVersionGreaterThanHook([7]); @@ -8,14 +9,14 @@ describe('CLIENT NO-EVICT', () => { describe('transformArguments', () => { it('true', () => { assert.deepEqual( - CLIENT_NO_EVICT.transformArguments(true), + parseArgs(CLIENT_NO_EVICT, true), ['CLIENT', 'NO-EVICT', 'ON'] ); }); it('false', () => { assert.deepEqual( - CLIENT_NO_EVICT.transformArguments(false), + parseArgs(CLIENT_NO_EVICT, false), ['CLIENT', 'NO-EVICT', 'OFF'] ); }); diff --git a/packages/client/lib/commands/CLIENT_NO-EVICT.ts b/packages/client/lib/commands/CLIENT_NO-EVICT.ts index 82aa50074ba..de2f65270e2 100644 --- a/packages/client/lib/commands/CLIENT_NO-EVICT.ts +++ b/packages/client/lib/commands/CLIENT_NO-EVICT.ts @@ -1,14 +1,15 @@ +import { CommandParser } from '../client/parser'; import { SimpleStringReply, Command } from '../RESP/types'; export default { - FIRST_KEY_INDEX: undefined, + NOT_KEYED_COMMAND: true, IS_READ_ONLY: true, - transformArguments(value: boolean) { - return [ + parseCommand(parser: CommandParser, value: boolean) { + parser.push( 'CLIENT', 'NO-EVICT', value ? 'ON' : 'OFF' - ]; + ); }, transformReply: undefined as unknown as () => SimpleStringReply<'OK'> } as const satisfies Command; diff --git a/packages/client/lib/commands/CLIENT_NO-TOUCH.spec.ts b/packages/client/lib/commands/CLIENT_NO-TOUCH.spec.ts index e58c22d9c6e..ec5c9f18ae9 100644 --- a/packages/client/lib/commands/CLIENT_NO-TOUCH.spec.ts +++ b/packages/client/lib/commands/CLIENT_NO-TOUCH.spec.ts @@ -1,6 +1,7 @@ import { strict as assert } from 'assert'; import testUtils, { GLOBAL } from '../test-utils'; import CLIENT_NO_TOUCH from './CLIENT_NO-TOUCH'; +import { parseArgs } from './generic-transformers'; describe('CLIENT NO-TOUCH', () => { testUtils.isVersionGreaterThanHook([7, 2]); @@ -8,14 +9,14 @@ describe('CLIENT NO-TOUCH', () => { describe('transformArguments', () => { it('true', () => { assert.deepEqual( - CLIENT_NO_TOUCH.transformArguments(true), + parseArgs(CLIENT_NO_TOUCH, true), ['CLIENT', 'NO-TOUCH', 'ON'] ); }); it('false', () => { assert.deepEqual( - CLIENT_NO_TOUCH.transformArguments(false), + parseArgs(CLIENT_NO_TOUCH, false), ['CLIENT', 'NO-TOUCH', 'OFF'] ); }); diff --git a/packages/client/lib/commands/CLIENT_NO-TOUCH.ts b/packages/client/lib/commands/CLIENT_NO-TOUCH.ts index a6fc5eb1765..8c6deff4af5 100644 --- a/packages/client/lib/commands/CLIENT_NO-TOUCH.ts +++ b/packages/client/lib/commands/CLIENT_NO-TOUCH.ts @@ -1,14 +1,15 @@ +import { CommandParser } from '../client/parser'; import { SimpleStringReply, Command } from '../RESP/types'; export default { - FIRST_KEY_INDEX: undefined, + NOT_KEYED_COMMAND: true, IS_READ_ONLY: true, - transformArguments(value: boolean) { - return [ + parseCommand(parser: CommandParser, value: boolean) { + parser.push( 'CLIENT', 'NO-TOUCH', value ? 'ON' : 'OFF' - ]; + ); }, transformReply: undefined as unknown as () => SimpleStringReply<'OK'> } as const satisfies Command; diff --git a/packages/client/lib/commands/CLIENT_PAUSE.spec.ts b/packages/client/lib/commands/CLIENT_PAUSE.spec.ts index a30f9075072..e213433afbe 100644 --- a/packages/client/lib/commands/CLIENT_PAUSE.spec.ts +++ b/packages/client/lib/commands/CLIENT_PAUSE.spec.ts @@ -1,19 +1,20 @@ import { strict as assert } from 'node:assert'; import testUtils, { GLOBAL } from '../test-utils'; import CLIENT_PAUSE from './CLIENT_PAUSE'; +import { parseArgs } from './generic-transformers'; describe('CLIENT PAUSE', () => { describe('transformArguments', () => { it('simple', () => { assert.deepEqual( - CLIENT_PAUSE.transformArguments(0), + parseArgs(CLIENT_PAUSE, 0), ['CLIENT', 'PAUSE', '0'] ); }); it('with mode', () => { assert.deepEqual( - CLIENT_PAUSE.transformArguments(0, 'ALL'), + parseArgs(CLIENT_PAUSE, 0, 'ALL'), ['CLIENT', 'PAUSE', '0', 'ALL'] ); }); diff --git a/packages/client/lib/commands/CLIENT_PAUSE.ts b/packages/client/lib/commands/CLIENT_PAUSE.ts index 87b4177ed8c..ae6e4376364 100644 --- a/packages/client/lib/commands/CLIENT_PAUSE.ts +++ b/packages/client/lib/commands/CLIENT_PAUSE.ts @@ -1,20 +1,14 @@ +import { CommandParser } from '../client/parser'; import { SimpleStringReply, Command } from '../RESP/types'; export default { - FIRST_KEY_INDEX: undefined, + NOT_KEYED_COMMAND: true, IS_READ_ONLY: true, - transformArguments(timeout: number, mode?: 'WRITE' | 'ALL') { - const args = [ - 'CLIENT', - 'PAUSE', - timeout.toString() - ]; - + parseCommand(parser: CommandParser, timeout: number, mode?: 'WRITE' | 'ALL') { + parser.push('CLIENT', 'PAUSE', timeout.toString()); if (mode) { - args.push(mode); + parser.push(mode); } - - return args; }, transformReply: undefined as unknown as () => SimpleStringReply<'OK'> } as const satisfies Command; diff --git a/packages/client/lib/commands/CLIENT_SETNAME.spec.ts b/packages/client/lib/commands/CLIENT_SETNAME.spec.ts index 8e6b914791d..b2b339c3d19 100644 --- a/packages/client/lib/commands/CLIENT_SETNAME.spec.ts +++ b/packages/client/lib/commands/CLIENT_SETNAME.spec.ts @@ -2,11 +2,12 @@ import { strict as assert } from 'node:assert'; import testUtils, { GLOBAL } from '../test-utils'; import CLIENT_SETNAME from './CLIENT_SETNAME'; +import { parseArgs } from './generic-transformers'; describe('CLIENT SETNAME', () => { it('transformArguments', () => { assert.deepEqual( - CLIENT_SETNAME.transformArguments('name'), + parseArgs(CLIENT_SETNAME, 'name'), ['CLIENT', 'SETNAME', 'name'] ); }); diff --git a/packages/client/lib/commands/CLIENT_SETNAME.ts b/packages/client/lib/commands/CLIENT_SETNAME.ts index e2e2a921958..335891e8308 100644 --- a/packages/client/lib/commands/CLIENT_SETNAME.ts +++ b/packages/client/lib/commands/CLIENT_SETNAME.ts @@ -1,10 +1,11 @@ +import { CommandParser } from '../client/parser'; import { RedisArgument, SimpleStringReply, Command } from '../RESP/types'; export default { - FIRST_KEY_INDEX: undefined, + NOT_KEYED_COMMAND: true, IS_READ_ONLY: true, - transformArguments(name: RedisArgument) { - return ['CLIENT', 'SETNAME', name]; + parseCommand(parser: CommandParser, name: RedisArgument) { + parser.push('CLIENT', 'SETNAME', name); }, transformReply: undefined as unknown as () => SimpleStringReply<'OK'> } as const satisfies Command; diff --git a/packages/client/lib/commands/CLIENT_TRACKING.spec.ts b/packages/client/lib/commands/CLIENT_TRACKING.spec.ts index 98fe091fb1b..032725635ee 100644 --- a/packages/client/lib/commands/CLIENT_TRACKING.spec.ts +++ b/packages/client/lib/commands/CLIENT_TRACKING.spec.ts @@ -1,6 +1,7 @@ import { strict as assert } from 'node:assert'; import testUtils, { GLOBAL } from '../test-utils'; import CLIENT_TRACKING from './CLIENT_TRACKING'; +import { parseArgs } from './generic-transformers'; describe('CLIENT TRACKING', () => { testUtils.isVersionGreaterThanHook([6]); @@ -9,14 +10,14 @@ describe('CLIENT TRACKING', () => { describe('true', () => { it('simple', () => { assert.deepEqual( - CLIENT_TRACKING.transformArguments(true), + parseArgs(CLIENT_TRACKING, true), ['CLIENT', 'TRACKING', 'ON'] ); }); it('with REDIRECT', () => { assert.deepEqual( - CLIENT_TRACKING.transformArguments(true, { + parseArgs(CLIENT_TRACKING, true, { REDIRECT: 1 }), ['CLIENT', 'TRACKING', 'ON', 'REDIRECT', '1'] @@ -26,7 +27,7 @@ describe('CLIENT TRACKING', () => { describe('with BCAST', () => { it('simple', () => { assert.deepEqual( - CLIENT_TRACKING.transformArguments(true, { + parseArgs(CLIENT_TRACKING, true, { BCAST: true }), ['CLIENT', 'TRACKING', 'ON', 'BCAST'] @@ -36,7 +37,7 @@ describe('CLIENT TRACKING', () => { describe('with PREFIX', () => { it('string', () => { assert.deepEqual( - CLIENT_TRACKING.transformArguments(true, { + parseArgs(CLIENT_TRACKING, true, { BCAST: true, PREFIX: 'prefix' }), @@ -46,7 +47,7 @@ describe('CLIENT TRACKING', () => { it('array', () => { assert.deepEqual( - CLIENT_TRACKING.transformArguments(true, { + parseArgs(CLIENT_TRACKING, true, { BCAST: true, PREFIX: ['1', '2'] }), @@ -58,7 +59,7 @@ describe('CLIENT TRACKING', () => { it('with OPTIN', () => { assert.deepEqual( - CLIENT_TRACKING.transformArguments(true, { + parseArgs(CLIENT_TRACKING, true, { OPTIN: true }), ['CLIENT', 'TRACKING', 'ON', 'OPTIN'] @@ -67,7 +68,7 @@ describe('CLIENT TRACKING', () => { it('with OPTOUT', () => { assert.deepEqual( - CLIENT_TRACKING.transformArguments(true, { + parseArgs(CLIENT_TRACKING, true, { OPTOUT: true }), ['CLIENT', 'TRACKING', 'ON', 'OPTOUT'] @@ -76,7 +77,7 @@ describe('CLIENT TRACKING', () => { it('with NOLOOP', () => { assert.deepEqual( - CLIENT_TRACKING.transformArguments(true, { + parseArgs(CLIENT_TRACKING, true, { NOLOOP: true }), ['CLIENT', 'TRACKING', 'ON', 'NOLOOP'] @@ -86,7 +87,7 @@ describe('CLIENT TRACKING', () => { it('false', () => { assert.deepEqual( - CLIENT_TRACKING.transformArguments(false), + parseArgs(CLIENT_TRACKING, false), ['CLIENT', 'TRACKING', 'OFF'] ); }); diff --git a/packages/client/lib/commands/CLIENT_TRACKING.ts b/packages/client/lib/commands/CLIENT_TRACKING.ts index a783ce35894..df70a3705f9 100644 --- a/packages/client/lib/commands/CLIENT_TRACKING.ts +++ b/packages/client/lib/commands/CLIENT_TRACKING.ts @@ -1,4 +1,5 @@ -import { RedisArgument, SimpleStringReply, Command } from '../RESP/types'; +import { CommandParser } from '../client/parser'; +import { SimpleStringReply, Command } from '../RESP/types'; import { RedisVariadicArgument } from './generic-transformers'; interface CommonOptions { @@ -26,50 +27,49 @@ export type ClientTrackingOptions = CommonOptions & ( ); export default { - FIRST_KEY_INDEX: undefined, + NOT_KEYED_COMMAND: true, IS_READ_ONLY: true, - transformArguments( + parseCommand( + parser: CommandParser, mode: M, options?: M extends true ? ClientTrackingOptions : never ) { - const args: Array = [ + parser.push( 'CLIENT', 'TRACKING', mode ? 'ON' : 'OFF' - ]; + ); if (mode) { if (options?.REDIRECT) { - args.push( + parser.push( 'REDIRECT', options.REDIRECT.toString() ); } if (isBroadcast(options)) { - args.push('BCAST'); + parser.push('BCAST'); if (options?.PREFIX) { if (Array.isArray(options.PREFIX)) { for (const prefix of options.PREFIX) { - args.push('PREFIX', prefix); + parser.push('PREFIX', prefix); } } else { - args.push('PREFIX', options.PREFIX); + parser.push('PREFIX', options.PREFIX); } } } else if (isOptIn(options)) { - args.push('OPTIN'); + parser.push('OPTIN'); } else if (isOptOut(options)) { - args.push('OPTOUT'); + parser.push('OPTOUT'); } if (options?.NOLOOP) { - args.push('NOLOOP'); + parser.push('NOLOOP'); } } - - return args; }, transformReply: undefined as unknown as () => SimpleStringReply<'OK'> } as const satisfies Command; diff --git a/packages/client/lib/commands/CLIENT_TRACKINGINFO.spec.ts b/packages/client/lib/commands/CLIENT_TRACKINGINFO.spec.ts index 1cefbd27d53..d776519df22 100644 --- a/packages/client/lib/commands/CLIENT_TRACKINGINFO.spec.ts +++ b/packages/client/lib/commands/CLIENT_TRACKINGINFO.spec.ts @@ -1,13 +1,14 @@ import { strict as assert } from 'node:assert'; import testUtils, { GLOBAL } from '../test-utils'; import CLIENT_TRACKINGINFO from './CLIENT_TRACKINGINFO'; +import { parseArgs } from './generic-transformers'; describe('CLIENT TRACKINGINFO', () => { testUtils.isVersionGreaterThanHook([6, 2]); it('transformArguments', () => { assert.deepEqual( - CLIENT_TRACKINGINFO.transformArguments(), + parseArgs(CLIENT_TRACKINGINFO), ['CLIENT', 'TRACKINGINFO'] ); }); diff --git a/packages/client/lib/commands/CLIENT_TRACKINGINFO.ts b/packages/client/lib/commands/CLIENT_TRACKINGINFO.ts index d969ba0219e..fe6e090455c 100644 --- a/packages/client/lib/commands/CLIENT_TRACKINGINFO.ts +++ b/packages/client/lib/commands/CLIENT_TRACKINGINFO.ts @@ -1,3 +1,4 @@ +import { CommandParser } from '../client/parser'; import { TuplesToMapReply, BlobStringReply, SetReply, NumberReply, ArrayReply, UnwrapReply, Resp2Reply, Command } from '../RESP/types'; type TrackingInfo = TuplesToMapReply<[ @@ -7,10 +8,10 @@ type TrackingInfo = TuplesToMapReply<[ ]>; export default { - FIRST_KEY_INDEX: undefined, + NOT_KEYED_COMMAND: true, IS_READ_ONLY: true, - transformArguments() { - return ['CLIENT', 'TRACKINGINFO']; + parseCommand(parser: CommandParser) { + parser.push('CLIENT', 'TRACKINGINFO'); }, transformReply: { 2: (reply: UnwrapReply>) => ({ diff --git a/packages/client/lib/commands/CLIENT_UNPAUSE.spec.ts b/packages/client/lib/commands/CLIENT_UNPAUSE.spec.ts index bddf3ca0f02..0b58cf6517e 100644 --- a/packages/client/lib/commands/CLIENT_UNPAUSE.spec.ts +++ b/packages/client/lib/commands/CLIENT_UNPAUSE.spec.ts @@ -1,13 +1,14 @@ import { strict as assert } from 'node:assert'; import testUtils, { GLOBAL } from '../test-utils'; import CLIENT_UNPAUSE from './CLIENT_UNPAUSE'; +import { parseArgs } from './generic-transformers'; describe('CLIENT UNPAUSE', () => { testUtils.isVersionGreaterThanHook([6, 2]); it('transformArguments', () => { assert.deepEqual( - CLIENT_UNPAUSE.transformArguments(), + parseArgs(CLIENT_UNPAUSE), ['CLIENT', 'UNPAUSE'] ); }); diff --git a/packages/client/lib/commands/CLIENT_UNPAUSE.ts b/packages/client/lib/commands/CLIENT_UNPAUSE.ts index 9da0a9a8bbe..c202e50a5df 100644 --- a/packages/client/lib/commands/CLIENT_UNPAUSE.ts +++ b/packages/client/lib/commands/CLIENT_UNPAUSE.ts @@ -1,10 +1,11 @@ +import { CommandParser } from '../client/parser'; import { SimpleStringReply, Command } from '../RESP/types'; export default { - FIRST_KEY_INDEX: undefined, + NOT_KEYED_COMMAND: true, IS_READ_ONLY: true, - transformArguments() { - return ['CLIENT', 'UNPAUSE']; + parseCommand(parser: CommandParser) { + parser.push('CLIENT', 'UNPAUSE'); }, transformReply: undefined as unknown as () => SimpleStringReply<'OK'> } as const satisfies Command; diff --git a/packages/client/lib/commands/CLUSTER_ADDSLOTS.spec.ts b/packages/client/lib/commands/CLUSTER_ADDSLOTS.spec.ts index 56f7b2a85e7..4a9b1839bb4 100644 --- a/packages/client/lib/commands/CLUSTER_ADDSLOTS.spec.ts +++ b/packages/client/lib/commands/CLUSTER_ADDSLOTS.spec.ts @@ -1,18 +1,19 @@ import { strict as assert } from 'node:assert'; import CLUSTER_ADDSLOTS from './CLUSTER_ADDSLOTS'; +import { parseArgs } from './generic-transformers'; describe('CLUSTER ADDSLOTS', () => { describe('transformArguments', () => { it('single', () => { assert.deepEqual( - CLUSTER_ADDSLOTS.transformArguments(0), + parseArgs(CLUSTER_ADDSLOTS, 0), ['CLUSTER', 'ADDSLOTS', '0'] ); }); it('multiple', () => { assert.deepEqual( - CLUSTER_ADDSLOTS.transformArguments([0, 1]), + parseArgs(CLUSTER_ADDSLOTS, [0, 1]), ['CLUSTER', 'ADDSLOTS', '0', '1'] ); }); diff --git a/packages/client/lib/commands/CLUSTER_ADDSLOTS.ts b/packages/client/lib/commands/CLUSTER_ADDSLOTS.ts index dc42c2f13e8..0f5c4513d1d 100644 --- a/packages/client/lib/commands/CLUSTER_ADDSLOTS.ts +++ b/packages/client/lib/commands/CLUSTER_ADDSLOTS.ts @@ -1,14 +1,12 @@ +import { CommandParser } from '../client/parser'; import { SimpleStringReply, Command } from '../RESP/types'; -import { pushVariadicNumberArguments } from './generic-transformers'; export default { - FIRST_KEY_INDEX: undefined, + NOT_KEYED_COMMAND: true, IS_READ_ONLY: true, - transformArguments(slots: number | Array) { - return pushVariadicNumberArguments( - ['CLUSTER', 'ADDSLOTS'], - slots - ); + parseCommand(parser: CommandParser, slots: number | Array) { + parser.push('CLUSTER', 'ADDSLOTS'); + parser.pushVariadicNumber(slots); }, transformReply: undefined as unknown as () => SimpleStringReply<'OK'> } as const satisfies Command; diff --git a/packages/client/lib/commands/CLUSTER_ADDSLOTSRANGE.spec.ts b/packages/client/lib/commands/CLUSTER_ADDSLOTSRANGE.spec.ts index 6af6f586e99..40706968f93 100644 --- a/packages/client/lib/commands/CLUSTER_ADDSLOTSRANGE.spec.ts +++ b/packages/client/lib/commands/CLUSTER_ADDSLOTSRANGE.spec.ts @@ -1,6 +1,7 @@ import { strict as assert } from 'node:assert'; import testUtils from '../test-utils'; import CLUSTER_ADDSLOTSRANGE from './CLUSTER_ADDSLOTSRANGE'; +import { parseArgs } from './generic-transformers'; describe('CLUSTER ADDSLOTSRANGE', () => { testUtils.isVersionGreaterThanHook([7, 0]); @@ -8,7 +9,7 @@ describe('CLUSTER ADDSLOTSRANGE', () => { describe('transformArguments', () => { it('single', () => { assert.deepEqual( - CLUSTER_ADDSLOTSRANGE.transformArguments({ + parseArgs(CLUSTER_ADDSLOTSRANGE, { start: 0, end: 1 }), @@ -18,7 +19,7 @@ describe('CLUSTER ADDSLOTSRANGE', () => { it('multiple', () => { assert.deepEqual( - CLUSTER_ADDSLOTSRANGE.transformArguments([{ + parseArgs(CLUSTER_ADDSLOTSRANGE, [{ start: 0, end: 1 }, { diff --git a/packages/client/lib/commands/CLUSTER_ADDSLOTSRANGE.ts b/packages/client/lib/commands/CLUSTER_ADDSLOTSRANGE.ts index 5cf649a30da..40780731981 100644 --- a/packages/client/lib/commands/CLUSTER_ADDSLOTSRANGE.ts +++ b/packages/client/lib/commands/CLUSTER_ADDSLOTSRANGE.ts @@ -1,14 +1,13 @@ +import { CommandParser } from '../client/parser'; import { SimpleStringReply, Command } from '../RESP/types'; -import { pushSlotRangesArguments, SlotRange } from './generic-transformers'; +import { parseSlotRangesArguments, SlotRange } from './generic-transformers'; export default { - FIRST_KEY_INDEX: undefined, + NOT_KEYED_COMMAND: true, IS_READ_ONLY: true, - transformArguments(ranges: SlotRange | Array) { - return pushSlotRangesArguments( - ['CLUSTER', 'ADDSLOTSRANGE'], - ranges - ); + parseCommand(parser: CommandParser, ranges: SlotRange | Array) { + parser.push('CLUSTER', 'ADDSLOTSRANGE'); + parseSlotRangesArguments(parser, ranges); }, transformReply: undefined as unknown as () => SimpleStringReply<'OK'> } as const satisfies Command; diff --git a/packages/client/lib/commands/CLUSTER_BUMPEPOCH.spec.ts b/packages/client/lib/commands/CLUSTER_BUMPEPOCH.spec.ts index d21bc47c5d0..f3ecde9f6a8 100644 --- a/packages/client/lib/commands/CLUSTER_BUMPEPOCH.spec.ts +++ b/packages/client/lib/commands/CLUSTER_BUMPEPOCH.spec.ts @@ -1,11 +1,12 @@ import { strict as assert } from 'node:assert'; import testUtils, { GLOBAL } from '../test-utils'; import CLUSTER_BUMPEPOCH from './CLUSTER_BUMPEPOCH'; +import { parseArgs } from './generic-transformers'; describe('CLUSTER BUMPEPOCH', () => { it('transformArguments', () => { assert.deepEqual( - CLUSTER_BUMPEPOCH.transformArguments(), + parseArgs(CLUSTER_BUMPEPOCH), ['CLUSTER', 'BUMPEPOCH'] ); }); diff --git a/packages/client/lib/commands/CLUSTER_BUMPEPOCH.ts b/packages/client/lib/commands/CLUSTER_BUMPEPOCH.ts index 94f7e3b56f9..04b62f85424 100644 --- a/packages/client/lib/commands/CLUSTER_BUMPEPOCH.ts +++ b/packages/client/lib/commands/CLUSTER_BUMPEPOCH.ts @@ -1,10 +1,11 @@ +import { CommandParser } from '../client/parser'; import { SimpleStringReply, Command } from '../RESP/types'; export default { - FIRST_KEY_INDEX: undefined, + NOT_KEYED_COMMAND: true, IS_READ_ONLY: true, - transformArguments() { - return ['CLUSTER', 'BUMPEPOCH']; + parseCommand(parser: CommandParser) { + parser.push('CLUSTER', 'BUMPEPOCH'); }, transformReply: undefined as unknown as () => SimpleStringReply<'BUMPED' | 'STILL'> } as const satisfies Command; diff --git a/packages/client/lib/commands/CLUSTER_COUNT-FAILURE-REPORTS.spec.ts b/packages/client/lib/commands/CLUSTER_COUNT-FAILURE-REPORTS.spec.ts index 93c2aca7804..06a901ef301 100644 --- a/packages/client/lib/commands/CLUSTER_COUNT-FAILURE-REPORTS.spec.ts +++ b/packages/client/lib/commands/CLUSTER_COUNT-FAILURE-REPORTS.spec.ts @@ -1,11 +1,12 @@ import { strict as assert } from 'node:assert'; import testUtils, { GLOBAL } from '../test-utils'; import CLUSTER_COUNT_FAILURE_REPORTS from './CLUSTER_COUNT-FAILURE-REPORTS'; +import { parseArgs } from './generic-transformers'; describe('CLUSTER COUNT-FAILURE-REPORTS', () => { it('transformArguments', () => { assert.deepEqual( - CLUSTER_COUNT_FAILURE_REPORTS.transformArguments('0'), + parseArgs(CLUSTER_COUNT_FAILURE_REPORTS, '0'), ['CLUSTER', 'COUNT-FAILURE-REPORTS', '0'] ); }); diff --git a/packages/client/lib/commands/CLUSTER_COUNT-FAILURE-REPORTS.ts b/packages/client/lib/commands/CLUSTER_COUNT-FAILURE-REPORTS.ts index a005694713d..0ac311f7ecd 100644 --- a/packages/client/lib/commands/CLUSTER_COUNT-FAILURE-REPORTS.ts +++ b/packages/client/lib/commands/CLUSTER_COUNT-FAILURE-REPORTS.ts @@ -1,10 +1,11 @@ +import { CommandParser } from '../client/parser'; import { RedisArgument, NumberReply, Command } from '../RESP/types'; export default { - FIRST_KEY_INDEX: undefined, + NOT_KEYED_COMMAND: true, IS_READ_ONLY: true, - transformArguments(nodeId: RedisArgument) { - return ['CLUSTER', 'COUNT-FAILURE-REPORTS', nodeId]; + parseCommand(parser: CommandParser, nodeId: RedisArgument) { + parser.push('CLUSTER', 'COUNT-FAILURE-REPORTS', nodeId); }, transformReply: undefined as unknown as () => NumberReply } as const satisfies Command; diff --git a/packages/client/lib/commands/CLUSTER_COUNTKEYSINSLOT.spec.ts b/packages/client/lib/commands/CLUSTER_COUNTKEYSINSLOT.spec.ts index 180a120e153..52848409465 100644 --- a/packages/client/lib/commands/CLUSTER_COUNTKEYSINSLOT.spec.ts +++ b/packages/client/lib/commands/CLUSTER_COUNTKEYSINSLOT.spec.ts @@ -1,11 +1,12 @@ import { strict as assert } from 'node:assert'; import testUtils, { GLOBAL } from '../test-utils'; import CLUSTER_COUNTKEYSINSLOT from './CLUSTER_COUNTKEYSINSLOT'; +import { parseArgs } from './generic-transformers'; describe('CLUSTER COUNTKEYSINSLOT', () => { it('transformArguments', () => { assert.deepEqual( - CLUSTER_COUNTKEYSINSLOT.transformArguments(0), + parseArgs(CLUSTER_COUNTKEYSINSLOT, 0), ['CLUSTER', 'COUNTKEYSINSLOT', '0'] ); }); diff --git a/packages/client/lib/commands/CLUSTER_COUNTKEYSINSLOT.ts b/packages/client/lib/commands/CLUSTER_COUNTKEYSINSLOT.ts index 61f46230e89..63b4a8e02e2 100644 --- a/packages/client/lib/commands/CLUSTER_COUNTKEYSINSLOT.ts +++ b/packages/client/lib/commands/CLUSTER_COUNTKEYSINSLOT.ts @@ -1,10 +1,11 @@ +import { CommandParser } from '../client/parser'; import { NumberReply, Command } from '../RESP/types'; export default { - FIRST_KEY_INDEX: undefined, + NOT_KEYED_COMMAND: true, IS_READ_ONLY: true, - transformArguments(slot: number) { - return ['CLUSTER', 'COUNTKEYSINSLOT', slot.toString()]; + parseCommand(parser: CommandParser, slot: number) { + parser.push('CLUSTER', 'COUNTKEYSINSLOT', slot.toString()); }, transformReply: undefined as unknown as () => NumberReply } as const satisfies Command; diff --git a/packages/client/lib/commands/CLUSTER_DELSLOTS.spec.ts b/packages/client/lib/commands/CLUSTER_DELSLOTS.spec.ts index 59e40217b9c..2937fdd4d79 100644 --- a/packages/client/lib/commands/CLUSTER_DELSLOTS.spec.ts +++ b/packages/client/lib/commands/CLUSTER_DELSLOTS.spec.ts @@ -1,18 +1,19 @@ import { strict as assert } from 'node:assert'; import CLUSTER_DELSLOTS from './CLUSTER_DELSLOTS'; +import { parseArgs } from './generic-transformers'; describe('CLUSTER DELSLOTS', () => { describe('transformArguments', () => { it('single', () => { assert.deepEqual( - CLUSTER_DELSLOTS.transformArguments(0), + parseArgs(CLUSTER_DELSLOTS, 0), ['CLUSTER', 'DELSLOTS', '0'] ); }); it('multiple', () => { assert.deepEqual( - CLUSTER_DELSLOTS.transformArguments([0, 1]), + parseArgs(CLUSTER_DELSLOTS, [0, 1]), ['CLUSTER', 'DELSLOTS', '0', '1'] ); }); diff --git a/packages/client/lib/commands/CLUSTER_DELSLOTS.ts b/packages/client/lib/commands/CLUSTER_DELSLOTS.ts index 6a6bbb76085..9be6e962a18 100644 --- a/packages/client/lib/commands/CLUSTER_DELSLOTS.ts +++ b/packages/client/lib/commands/CLUSTER_DELSLOTS.ts @@ -1,14 +1,12 @@ +import { CommandParser } from '../client/parser'; import { SimpleStringReply, Command } from '../RESP/types'; -import { pushVariadicNumberArguments } from './generic-transformers'; export default { - FIRST_KEY_INDEX: undefined, + NOT_KEYED_COMMAND: true, IS_READ_ONLY: true, - transformArguments(slots: number | Array) { - return pushVariadicNumberArguments( - ['CLUSTER', 'DELSLOTS'], - slots - ); + parseCommand(parser: CommandParser, slots: number | Array) { + parser.push('CLUSTER', 'DELSLOTS'); + parser.pushVariadicNumber(slots); }, transformReply: undefined as unknown as () => SimpleStringReply<'OK'> } as const satisfies Command; diff --git a/packages/client/lib/commands/CLUSTER_DELSLOTSRANGE.spec.ts b/packages/client/lib/commands/CLUSTER_DELSLOTSRANGE.spec.ts index 2615f394b87..6007421d11b 100644 --- a/packages/client/lib/commands/CLUSTER_DELSLOTSRANGE.spec.ts +++ b/packages/client/lib/commands/CLUSTER_DELSLOTSRANGE.spec.ts @@ -1,11 +1,12 @@ import { strict as assert } from 'node:assert'; import CLUSTER_DELSLOTSRANGE from './CLUSTER_DELSLOTSRANGE'; +import { parseArgs } from './generic-transformers'; describe('CLUSTER DELSLOTSRANGE', () => { describe('transformArguments', () => { it('single', () => { assert.deepEqual( - CLUSTER_DELSLOTSRANGE.transformArguments({ + parseArgs(CLUSTER_DELSLOTSRANGE, { start: 0, end: 1 }), @@ -15,7 +16,7 @@ describe('CLUSTER DELSLOTSRANGE', () => { it('multiple', () => { assert.deepEqual( - CLUSTER_DELSLOTSRANGE.transformArguments([{ + parseArgs(CLUSTER_DELSLOTSRANGE, [{ start: 0, end: 1 }, { diff --git a/packages/client/lib/commands/CLUSTER_DELSLOTSRANGE.ts b/packages/client/lib/commands/CLUSTER_DELSLOTSRANGE.ts index e28ca9c8405..64c04021ba1 100644 --- a/packages/client/lib/commands/CLUSTER_DELSLOTSRANGE.ts +++ b/packages/client/lib/commands/CLUSTER_DELSLOTSRANGE.ts @@ -1,14 +1,13 @@ +import { CommandParser } from '../client/parser'; import { SimpleStringReply, Command } from '../RESP/types'; -import { pushSlotRangesArguments, SlotRange } from './generic-transformers'; +import { parseSlotRangesArguments, SlotRange } from './generic-transformers'; export default { - FIRST_KEY_INDEX: undefined, + NOT_KEYED_COMMAND: true, IS_READ_ONLY: true, - transformArguments(ranges: SlotRange | Array) { - return pushSlotRangesArguments( - ['CLUSTER', 'DELSLOTSRANGE'], - ranges - ); + parseCommand(parser:CommandParser, ranges: SlotRange | Array) { + parser.push('CLUSTER', 'DELSLOTSRANGE'); + parseSlotRangesArguments(parser, ranges); }, transformReply: undefined as unknown as () => SimpleStringReply<'OK'> } as const satisfies Command; diff --git a/packages/client/lib/commands/CLUSTER_FAILOVER.spec.ts b/packages/client/lib/commands/CLUSTER_FAILOVER.spec.ts index ac18a9a7f8f..f8e4b986048 100644 --- a/packages/client/lib/commands/CLUSTER_FAILOVER.spec.ts +++ b/packages/client/lib/commands/CLUSTER_FAILOVER.spec.ts @@ -1,18 +1,19 @@ import { strict as assert } from 'node:assert'; import CLUSTER_FAILOVER, { FAILOVER_MODES } from './CLUSTER_FAILOVER'; +import { parseArgs } from './generic-transformers'; describe('CLUSTER FAILOVER', () => { describe('transformArguments', () => { it('simple', () => { assert.deepEqual( - CLUSTER_FAILOVER.transformArguments(), + parseArgs(CLUSTER_FAILOVER), ['CLUSTER', 'FAILOVER'] ); }); it('with mode', () => { assert.deepEqual( - CLUSTER_FAILOVER.transformArguments({ + parseArgs(CLUSTER_FAILOVER, { mode: FAILOVER_MODES.FORCE }), ['CLUSTER', 'FAILOVER', 'FORCE'] diff --git a/packages/client/lib/commands/CLUSTER_FAILOVER.ts b/packages/client/lib/commands/CLUSTER_FAILOVER.ts index 63f79a246ba..f74d65bd691 100644 --- a/packages/client/lib/commands/CLUSTER_FAILOVER.ts +++ b/packages/client/lib/commands/CLUSTER_FAILOVER.ts @@ -1,3 +1,4 @@ +import { CommandParser } from '../client/parser'; import { SimpleStringReply, Command } from '../RESP/types'; export const FAILOVER_MODES = { @@ -12,16 +13,14 @@ export interface ClusterFailoverOptions { } export default { - FIRST_KEY_INDEX: undefined, + NOT_KEYED_COMMAND: true, IS_READ_ONLY: true, - transformArguments(options?: ClusterFailoverOptions) { - const args = ['CLUSTER', 'FAILOVER']; + parseCommand(parser:CommandParser, options?: ClusterFailoverOptions) { + parser.push('CLUSTER', 'FAILOVER'); if (options?.mode) { - args.push(options.mode); + parser.push(options.mode); } - - return args; }, transformReply: undefined as unknown as () => SimpleStringReply<'OK'> } as const satisfies Command; diff --git a/packages/client/lib/commands/CLUSTER_FLUSHSLOTS.spec.ts b/packages/client/lib/commands/CLUSTER_FLUSHSLOTS.spec.ts index fbc4346136d..43701adfe6a 100644 --- a/packages/client/lib/commands/CLUSTER_FLUSHSLOTS.spec.ts +++ b/packages/client/lib/commands/CLUSTER_FLUSHSLOTS.spec.ts @@ -1,10 +1,11 @@ import { strict as assert } from 'node:assert'; import CLUSTER_FLUSHSLOTS from './CLUSTER_FLUSHSLOTS'; +import { parseArgs } from './generic-transformers'; describe('CLUSTER FLUSHSLOTS', () => { it('transformArguments', () => { assert.deepEqual( - CLUSTER_FLUSHSLOTS.transformArguments(), + parseArgs(CLUSTER_FLUSHSLOTS), ['CLUSTER', 'FLUSHSLOTS'] ); }); diff --git a/packages/client/lib/commands/CLUSTER_FLUSHSLOTS.ts b/packages/client/lib/commands/CLUSTER_FLUSHSLOTS.ts index 327ed7b7d17..dab22b2e740 100644 --- a/packages/client/lib/commands/CLUSTER_FLUSHSLOTS.ts +++ b/packages/client/lib/commands/CLUSTER_FLUSHSLOTS.ts @@ -1,10 +1,11 @@ +import { CommandParser } from '../client/parser'; import { SimpleStringReply, Command } from '../RESP/types'; export default { - FIRST_KEY_INDEX: undefined, + NOT_KEYED_COMMAND: true, IS_READ_ONLY: true, - transformArguments() { - return ['CLUSTER', 'FLUSHSLOTS']; + parseCommand(parser: CommandParser) { + parser.push('CLUSTER', 'FLUSHSLOTS'); }, transformReply: undefined as unknown as () => SimpleStringReply<'OK'> } as const satisfies Command; diff --git a/packages/client/lib/commands/CLUSTER_FORGET.spec.ts b/packages/client/lib/commands/CLUSTER_FORGET.spec.ts index a9a923b01ee..8d02374cf87 100644 --- a/packages/client/lib/commands/CLUSTER_FORGET.spec.ts +++ b/packages/client/lib/commands/CLUSTER_FORGET.spec.ts @@ -1,10 +1,11 @@ import { strict as assert } from 'node:assert'; import CLUSTER_FORGET from './CLUSTER_FORGET'; +import { parseArgs } from './generic-transformers'; describe('CLUSTER FORGET', () => { it('transformArguments', () => { assert.deepEqual( - CLUSTER_FORGET.transformArguments('0'), + parseArgs(CLUSTER_FORGET, '0'), ['CLUSTER', 'FORGET', '0'] ); }); diff --git a/packages/client/lib/commands/CLUSTER_FORGET.ts b/packages/client/lib/commands/CLUSTER_FORGET.ts index a51c039563b..2928c3e9075 100644 --- a/packages/client/lib/commands/CLUSTER_FORGET.ts +++ b/packages/client/lib/commands/CLUSTER_FORGET.ts @@ -1,10 +1,11 @@ +import { CommandParser } from '../client/parser'; import { RedisArgument, SimpleStringReply, Command } from '../RESP/types'; export default { - FIRST_KEY_INDEX: undefined, + NOT_KEYED_COMMAND: true, IS_READ_ONLY: true, - transformArguments(nodeId: RedisArgument) { - return ['CLUSTER', 'FORGET', nodeId]; + parseCommand(parser: CommandParser, nodeId: RedisArgument) { + parser.push('CLUSTER', 'FORGET', nodeId); }, transformReply: undefined as unknown as () => SimpleStringReply<'OK'> } as const satisfies Command; diff --git a/packages/client/lib/commands/CLUSTER_GETKEYSINSLOT.spec.ts b/packages/client/lib/commands/CLUSTER_GETKEYSINSLOT.spec.ts index f1a4e2c3bcc..468eecc74a9 100644 --- a/packages/client/lib/commands/CLUSTER_GETKEYSINSLOT.spec.ts +++ b/packages/client/lib/commands/CLUSTER_GETKEYSINSLOT.spec.ts @@ -1,11 +1,12 @@ import { strict as assert } from 'node:assert'; import testUtils, { GLOBAL } from '../test-utils'; import CLUSTER_GETKEYSINSLOT from './CLUSTER_GETKEYSINSLOT'; +import { parseArgs } from './generic-transformers'; describe('CLUSTER GETKEYSINSLOT', () => { it('transformArguments', () => { assert.deepEqual( - CLUSTER_GETKEYSINSLOT.transformArguments(0, 10), + parseArgs(CLUSTER_GETKEYSINSLOT, 0, 10), ['CLUSTER', 'GETKEYSINSLOT', '0', '10'] ); }); diff --git a/packages/client/lib/commands/CLUSTER_GETKEYSINSLOT.ts b/packages/client/lib/commands/CLUSTER_GETKEYSINSLOT.ts index c19cd225e04..2fd630ea1af 100644 --- a/packages/client/lib/commands/CLUSTER_GETKEYSINSLOT.ts +++ b/packages/client/lib/commands/CLUSTER_GETKEYSINSLOT.ts @@ -1,10 +1,11 @@ +import { CommandParser } from '../client/parser'; import { ArrayReply, BlobStringReply, Command } from '../RESP/types'; export default { - FIRST_KEY_INDEX: undefined, + NOT_KEYED_COMMAND: true, IS_READ_ONLY: true, - transformArguments(slot: number, count: number) { - return ['CLUSTER', 'GETKEYSINSLOT', slot.toString(), count.toString()]; + parseCommand(parser: CommandParser, slot: number, count: number) { + parser.push('CLUSTER', 'GETKEYSINSLOT', slot.toString(), count.toString()); }, transformReply: undefined as unknown as () => ArrayReply } as const satisfies Command; diff --git a/packages/client/lib/commands/CLUSTER_INFO.spec.ts b/packages/client/lib/commands/CLUSTER_INFO.spec.ts index f7c708663fc..01dafce8d53 100644 --- a/packages/client/lib/commands/CLUSTER_INFO.spec.ts +++ b/packages/client/lib/commands/CLUSTER_INFO.spec.ts @@ -1,11 +1,12 @@ import { strict as assert } from 'node:assert'; import testUtils, { GLOBAL } from '../test-utils'; import CLUSTER_INFO from './CLUSTER_INFO'; +import { parseArgs } from './generic-transformers'; describe('CLUSTER INFO', () => { it('transformArguments', () => { assert.deepEqual( - CLUSTER_INFO.transformArguments(), + parseArgs(CLUSTER_INFO), ['CLUSTER', 'INFO'] ); }); diff --git a/packages/client/lib/commands/CLUSTER_INFO.ts b/packages/client/lib/commands/CLUSTER_INFO.ts index 4605efbe81a..53140b38819 100644 --- a/packages/client/lib/commands/CLUSTER_INFO.ts +++ b/packages/client/lib/commands/CLUSTER_INFO.ts @@ -1,10 +1,11 @@ +import { CommandParser } from '../client/parser'; import { VerbatimStringReply, Command } from '../RESP/types'; export default { - FIRST_KEY_INDEX: undefined, + NOT_KEYED_COMMAND: true, IS_READ_ONLY: true, - transformArguments() { - return ['CLUSTER', 'INFO']; + parseCommand(parser: CommandParser) { + parser.push('CLUSTER', 'INFO'); }, transformReply: undefined as unknown as () => VerbatimStringReply } as const satisfies Command; diff --git a/packages/client/lib/commands/CLUSTER_KEYSLOT.spec.ts b/packages/client/lib/commands/CLUSTER_KEYSLOT.spec.ts index d582c616cd1..188c403abb5 100644 --- a/packages/client/lib/commands/CLUSTER_KEYSLOT.spec.ts +++ b/packages/client/lib/commands/CLUSTER_KEYSLOT.spec.ts @@ -1,11 +1,12 @@ import { strict as assert } from 'node:assert'; import testUtils, { GLOBAL } from '../test-utils'; import CLUSTER_KEYSLOT from './CLUSTER_KEYSLOT'; +import { parseArgs } from './generic-transformers'; describe('CLUSTER KEYSLOT', () => { it('transformArguments', () => { assert.deepEqual( - CLUSTER_KEYSLOT.transformArguments('key'), + parseArgs(CLUSTER_KEYSLOT, 'key'), ['CLUSTER', 'KEYSLOT', 'key'] ); }); diff --git a/packages/client/lib/commands/CLUSTER_KEYSLOT.ts b/packages/client/lib/commands/CLUSTER_KEYSLOT.ts index 81e84430116..d81a14e1a96 100644 --- a/packages/client/lib/commands/CLUSTER_KEYSLOT.ts +++ b/packages/client/lib/commands/CLUSTER_KEYSLOT.ts @@ -1,10 +1,11 @@ +import { CommandParser } from '../client/parser'; import { Command, NumberReply, RedisArgument } from '../RESP/types'; export default { - FIRST_KEY_INDEX: undefined, + NOT_KEYED_COMMAND: true, IS_READ_ONLY: true, - transformArguments(key: RedisArgument) { - return ['CLUSTER', 'KEYSLOT', key]; + parseCommand(parser: CommandParser, key: RedisArgument) { + parser.push('CLUSTER', 'KEYSLOT', key); }, transformReply: undefined as unknown as () => NumberReply } as const satisfies Command; diff --git a/packages/client/lib/commands/CLUSTER_LINKS.spec.ts b/packages/client/lib/commands/CLUSTER_LINKS.spec.ts index d94231634e0..609ecfd3da9 100644 --- a/packages/client/lib/commands/CLUSTER_LINKS.spec.ts +++ b/packages/client/lib/commands/CLUSTER_LINKS.spec.ts @@ -1,13 +1,14 @@ import { strict as assert } from 'node:assert'; import testUtils, { GLOBAL } from '../test-utils'; import CLUSTER_LINKS from './CLUSTER_LINKS'; +import { parseArgs } from './generic-transformers'; describe('CLUSTER LINKS', () => { testUtils.isVersionGreaterThanHook([7]); it('transformArguments', () => { assert.deepEqual( - CLUSTER_LINKS.transformArguments(), + parseArgs(CLUSTER_LINKS), ['CLUSTER', 'LINKS'] ); }); diff --git a/packages/client/lib/commands/CLUSTER_LINKS.ts b/packages/client/lib/commands/CLUSTER_LINKS.ts index df83f3f7a11..e98f61e762b 100644 --- a/packages/client/lib/commands/CLUSTER_LINKS.ts +++ b/packages/client/lib/commands/CLUSTER_LINKS.ts @@ -1,3 +1,4 @@ +import { CommandParser } from '../client/parser'; import { ArrayReply, TuplesToMapReply, BlobStringReply, NumberReply, UnwrapReply, Resp2Reply, Command } from '../RESP/types'; type ClusterLinksReply = ArrayReply>; export default { - FIRST_KEY_INDEX: undefined, + NOT_KEYED_COMMAND: true, IS_READ_ONLY: true, - transformArguments() { - return ['CLUSTER', 'LINKS']; + parseCommand(parser: CommandParser) { + parser.push('CLUSTER', 'LINKS'); }, transformReply: { 2: (reply: UnwrapReply>) => reply.map(link => { diff --git a/packages/client/lib/commands/CLUSTER_MEET.spec.ts b/packages/client/lib/commands/CLUSTER_MEET.spec.ts index 0b678f009f7..6c063f34e45 100644 --- a/packages/client/lib/commands/CLUSTER_MEET.spec.ts +++ b/packages/client/lib/commands/CLUSTER_MEET.spec.ts @@ -1,10 +1,11 @@ import { strict as assert } from 'node:assert'; import CLUSTER_MEET from './CLUSTER_MEET'; +import { parseArgs } from './generic-transformers'; describe('CLUSTER MEET', () => { it('transformArguments', () => { assert.deepEqual( - CLUSTER_MEET.transformArguments('127.0.0.1', 6379), + parseArgs(CLUSTER_MEET, '127.0.0.1', 6379), ['CLUSTER', 'MEET', '127.0.0.1', '6379'] ); }); diff --git a/packages/client/lib/commands/CLUSTER_MEET.ts b/packages/client/lib/commands/CLUSTER_MEET.ts index df72599d40b..804e5963d19 100644 --- a/packages/client/lib/commands/CLUSTER_MEET.ts +++ b/packages/client/lib/commands/CLUSTER_MEET.ts @@ -1,10 +1,11 @@ +import { CommandParser } from '../client/parser'; import { SimpleStringReply, Command } from '../RESP/types'; export default { - FIRST_KEY_INDEX: undefined, + NOT_KEYED_COMMAND: true, IS_READ_ONLY: true, - transformArguments(host: string, port: number) { - return ['CLUSTER', 'MEET', host, port.toString()]; + parseCommand(parser: CommandParser, host: string, port: number) { + parser.push('CLUSTER', 'MEET', host, port.toString()); }, transformReply: undefined as unknown as () => SimpleStringReply<'OK'> } as const satisfies Command; diff --git a/packages/client/lib/commands/CLUSTER_MYID.spec.ts b/packages/client/lib/commands/CLUSTER_MYID.spec.ts index 74540e98ab7..78bb4495e3c 100644 --- a/packages/client/lib/commands/CLUSTER_MYID.spec.ts +++ b/packages/client/lib/commands/CLUSTER_MYID.spec.ts @@ -1,11 +1,12 @@ import { strict as assert } from 'node:assert'; import testUtils, { GLOBAL } from '../test-utils'; import CLUSTER_MYID from './CLUSTER_MYID'; +import { parseArgs } from './generic-transformers'; describe('CLUSTER MYID', () => { it('transformArguments', () => { assert.deepEqual( - CLUSTER_MYID.transformArguments(), + parseArgs(CLUSTER_MYID), ['CLUSTER', 'MYID'] ); }); diff --git a/packages/client/lib/commands/CLUSTER_MYID.ts b/packages/client/lib/commands/CLUSTER_MYID.ts index 73711b47ebb..2aae7cdd8e0 100644 --- a/packages/client/lib/commands/CLUSTER_MYID.ts +++ b/packages/client/lib/commands/CLUSTER_MYID.ts @@ -1,10 +1,11 @@ +import { CommandParser } from '../client/parser'; import { BlobStringReply, Command } from '../RESP/types'; export default { - FIRST_KEY_INDEX: undefined, + NOT_KEYED_COMMAND: true, IS_READ_ONLY: true, - transformArguments() { - return ['CLUSTER', 'MYID']; + parseCommand(parser: CommandParser) { + parser.push('CLUSTER', 'MYID'); }, transformReply: undefined as unknown as () => BlobStringReply } as const satisfies Command; diff --git a/packages/client/lib/commands/CLUSTER_MYSHARDID.spec.ts b/packages/client/lib/commands/CLUSTER_MYSHARDID.spec.ts index e64f2e3777a..6c2a61801bc 100644 --- a/packages/client/lib/commands/CLUSTER_MYSHARDID.spec.ts +++ b/packages/client/lib/commands/CLUSTER_MYSHARDID.spec.ts @@ -1,13 +1,14 @@ import { strict as assert } from 'assert'; import testUtils, { GLOBAL } from '../test-utils'; import CLUSTER_MYSHARDID from './CLUSTER_MYSHARDID'; +import { parseArgs } from './generic-transformers'; describe('CLUSTER MYSHARDID', () => { testUtils.isVersionGreaterThanHook([7, 2]); it('transformArguments', () => { assert.deepEqual( - CLUSTER_MYSHARDID.transformArguments(), + parseArgs(CLUSTER_MYSHARDID), ['CLUSTER', 'MYSHARDID'] ); }); diff --git a/packages/client/lib/commands/CLUSTER_MYSHARDID.ts b/packages/client/lib/commands/CLUSTER_MYSHARDID.ts index 0c38b61634f..ccde3ee249b 100644 --- a/packages/client/lib/commands/CLUSTER_MYSHARDID.ts +++ b/packages/client/lib/commands/CLUSTER_MYSHARDID.ts @@ -1,10 +1,11 @@ +import { CommandParser } from '../client/parser'; import { BlobStringReply, Command } from '../RESP/types'; export default { - FIRST_KEY_INDEX: undefined, + NOT_KEYED_COMMAND: true, IS_READ_ONLY: true, - transformArguments() { - return ['CLUSTER', 'MYSHARDID']; + parseCommand(parser: CommandParser) { + parser.push('CLUSTER', 'MYSHARDID'); }, transformReply: undefined as unknown as () => BlobStringReply } as const satisfies Command; diff --git a/packages/client/lib/commands/CLUSTER_NODES.spec.ts b/packages/client/lib/commands/CLUSTER_NODES.spec.ts index 99db17a23e6..a49996586b7 100644 --- a/packages/client/lib/commands/CLUSTER_NODES.spec.ts +++ b/packages/client/lib/commands/CLUSTER_NODES.spec.ts @@ -1,11 +1,12 @@ import { strict as assert } from 'node:assert'; import testUtils, { GLOBAL } from '../test-utils'; import CLUSTER_NODES from './CLUSTER_NODES'; +import { parseArgs } from './generic-transformers'; describe('CLUSTER NODES', () => { it('transformArguments', () => { assert.deepEqual( - CLUSTER_NODES.transformArguments(), + parseArgs(CLUSTER_NODES), ['CLUSTER', 'NODES'] ); }); diff --git a/packages/client/lib/commands/CLUSTER_NODES.ts b/packages/client/lib/commands/CLUSTER_NODES.ts index 64dd5056232..c8b59f88224 100644 --- a/packages/client/lib/commands/CLUSTER_NODES.ts +++ b/packages/client/lib/commands/CLUSTER_NODES.ts @@ -1,10 +1,11 @@ +import { CommandParser } from '../client/parser'; import { VerbatimStringReply, Command } from '../RESP/types'; export default { - FIRST_KEY_INDEX: undefined, + NOT_KEYED_COMMAND: true, IS_READ_ONLY: true, - transformArguments() { - return ['CLUSTER', 'NODES']; + parseCommand(parser: CommandParser) { + parser.push('CLUSTER', 'NODES'); }, transformReply: undefined as unknown as () => VerbatimStringReply } as const satisfies Command; diff --git a/packages/client/lib/commands/CLUSTER_REPLICAS.spec.ts b/packages/client/lib/commands/CLUSTER_REPLICAS.spec.ts index 1a48f360885..11bf086bb66 100644 --- a/packages/client/lib/commands/CLUSTER_REPLICAS.spec.ts +++ b/packages/client/lib/commands/CLUSTER_REPLICAS.spec.ts @@ -1,11 +1,12 @@ import { strict as assert } from 'node:assert'; import testUtils, { GLOBAL } from '../test-utils'; import CLUSTER_REPLICAS from './CLUSTER_REPLICAS'; +import { parseArgs } from './generic-transformers'; describe('CLUSTER REPLICAS', () => { it('transformArguments', () => { assert.deepEqual( - CLUSTER_REPLICAS.transformArguments('0'), + parseArgs(CLUSTER_REPLICAS, '0'), ['CLUSTER', 'REPLICAS', '0'] ); }); diff --git a/packages/client/lib/commands/CLUSTER_REPLICAS.ts b/packages/client/lib/commands/CLUSTER_REPLICAS.ts index 8e0fe2cdfd9..eb60e560b45 100644 --- a/packages/client/lib/commands/CLUSTER_REPLICAS.ts +++ b/packages/client/lib/commands/CLUSTER_REPLICAS.ts @@ -1,10 +1,11 @@ +import { CommandParser } from '../client/parser'; import { RedisArgument, ArrayReply, BlobStringReply, Command } from '../RESP/types'; export default { - FIRST_KEY_INDEX: undefined, + NOT_KEYED_COMMAND: true, IS_READ_ONLY: true, - transformArguments(nodeId: RedisArgument) { - return ['CLUSTER', 'REPLICAS', nodeId]; + parseCommand(parser: CommandParser, nodeId: RedisArgument) { + parser.push('CLUSTER', 'REPLICAS', nodeId); }, transformReply: undefined as unknown as () => ArrayReply } as const satisfies Command; diff --git a/packages/client/lib/commands/CLUSTER_REPLICATE.spec.ts b/packages/client/lib/commands/CLUSTER_REPLICATE.spec.ts index 80935385a88..3f130d360bf 100644 --- a/packages/client/lib/commands/CLUSTER_REPLICATE.spec.ts +++ b/packages/client/lib/commands/CLUSTER_REPLICATE.spec.ts @@ -1,10 +1,11 @@ import { strict as assert } from 'node:assert'; import CLUSTER_REPLICATE from './CLUSTER_REPLICATE'; +import { parseArgs } from './generic-transformers'; describe('CLUSTER REPLICATE', () => { it('transformArguments', () => { assert.deepEqual( - CLUSTER_REPLICATE.transformArguments('0'), + parseArgs(CLUSTER_REPLICATE, '0'), ['CLUSTER', 'REPLICATE', '0'] ); }); diff --git a/packages/client/lib/commands/CLUSTER_REPLICATE.ts b/packages/client/lib/commands/CLUSTER_REPLICATE.ts index 7431142024c..d7312ae108e 100644 --- a/packages/client/lib/commands/CLUSTER_REPLICATE.ts +++ b/packages/client/lib/commands/CLUSTER_REPLICATE.ts @@ -1,10 +1,11 @@ +import { CommandParser } from '../client/parser'; import { RedisArgument, SimpleStringReply, Command } from '../RESP/types'; export default { - FIRST_KEY_INDEX: undefined, + NOT_KEYED_COMMAND: true, IS_READ_ONLY: true, - transformArguments(nodeId: RedisArgument) { - return ['CLUSTER', 'REPLICATE', nodeId]; + parseCommand(parser: CommandParser, nodeId: RedisArgument) { + parser.push('CLUSTER', 'REPLICATE', nodeId); }, transformReply: undefined as unknown as () => SimpleStringReply } as const satisfies Command; diff --git a/packages/client/lib/commands/CLUSTER_RESET.spec.ts b/packages/client/lib/commands/CLUSTER_RESET.spec.ts index 190bdaf69e1..1ef55e3f572 100644 --- a/packages/client/lib/commands/CLUSTER_RESET.spec.ts +++ b/packages/client/lib/commands/CLUSTER_RESET.spec.ts @@ -1,18 +1,19 @@ import { strict as assert } from 'node:assert'; import CLUSTER_RESET from './CLUSTER_RESET'; +import { parseArgs } from './generic-transformers'; describe('CLUSTER RESET', () => { describe('transformArguments', () => { it('simple', () => { assert.deepEqual( - CLUSTER_RESET.transformArguments(), + parseArgs(CLUSTER_RESET), ['CLUSTER', 'RESET'] ); }); it('with mode', () => { assert.deepEqual( - CLUSTER_RESET.transformArguments({ + parseArgs(CLUSTER_RESET, { mode: 'HARD' }), ['CLUSTER', 'RESET', 'HARD'] diff --git a/packages/client/lib/commands/CLUSTER_RESET.ts b/packages/client/lib/commands/CLUSTER_RESET.ts index 7aaac9d3b0d..2ba1a6eaf20 100644 --- a/packages/client/lib/commands/CLUSTER_RESET.ts +++ b/packages/client/lib/commands/CLUSTER_RESET.ts @@ -1,3 +1,4 @@ +import { CommandParser } from '../client/parser'; import { SimpleStringReply, Command } from '../RESP/types'; export interface ClusterResetOptions { @@ -5,16 +6,14 @@ export interface ClusterResetOptions { } export default { - FIRST_KEY_INDEX: undefined, + NOT_KEYED_COMMAND: true, IS_READ_ONLY: true, - transformArguments(options?: ClusterResetOptions) { - const args = ['CLUSTER', 'RESET']; + parseCommand(parser: CommandParser, options?: ClusterResetOptions) { + parser.push('CLUSTER', 'RESET'); if (options?.mode) { - args.push(options.mode); + parser.push(options.mode); } - - return args; }, transformReply: undefined as unknown as () => SimpleStringReply<'OK'> } as const satisfies Command; diff --git a/packages/client/lib/commands/CLUSTER_SAVECONFIG.spec.ts b/packages/client/lib/commands/CLUSTER_SAVECONFIG.spec.ts index ece8087e8e4..a0d317ffae4 100644 --- a/packages/client/lib/commands/CLUSTER_SAVECONFIG.spec.ts +++ b/packages/client/lib/commands/CLUSTER_SAVECONFIG.spec.ts @@ -1,11 +1,12 @@ import { strict as assert } from 'node:assert'; import testUtils, { GLOBAL } from '../test-utils'; import CLUSTER_SAVECONFIG from './CLUSTER_SAVECONFIG'; +import { parseArgs } from './generic-transformers'; describe('CLUSTER SAVECONFIG', () => { it('transformArguments', () => { assert.deepEqual( - CLUSTER_SAVECONFIG.transformArguments(), + parseArgs(CLUSTER_SAVECONFIG), ['CLUSTER', 'SAVECONFIG'] ); }); diff --git a/packages/client/lib/commands/CLUSTER_SAVECONFIG.ts b/packages/client/lib/commands/CLUSTER_SAVECONFIG.ts index 489ffd27e48..08da2a45b89 100644 --- a/packages/client/lib/commands/CLUSTER_SAVECONFIG.ts +++ b/packages/client/lib/commands/CLUSTER_SAVECONFIG.ts @@ -1,10 +1,11 @@ +import { CommandParser } from '../client/parser'; import { SimpleStringReply, Command } from '../RESP/types'; export default { - FIRST_KEY_INDEX: undefined, + NOT_KEYED_COMMAND: true, IS_READ_ONLY: true, - transformArguments() { - return ['CLUSTER', 'SAVECONFIG']; + parseCommand(parser: CommandParser) { + parser.push('CLUSTER', 'SAVECONFIG'); }, transformReply: undefined as unknown as () => SimpleStringReply<'OK'> } as const satisfies Command; diff --git a/packages/client/lib/commands/CLUSTER_SET-CONFIG-EPOCH.spec.ts b/packages/client/lib/commands/CLUSTER_SET-CONFIG-EPOCH.spec.ts index 39cf026d0ef..fb02ee2fe65 100644 --- a/packages/client/lib/commands/CLUSTER_SET-CONFIG-EPOCH.spec.ts +++ b/packages/client/lib/commands/CLUSTER_SET-CONFIG-EPOCH.spec.ts @@ -1,10 +1,11 @@ import { strict as assert } from 'node:assert'; import CLUSTER_SET_CONFIG_EPOCH from './CLUSTER_SET-CONFIG-EPOCH'; +import { parseArgs } from './generic-transformers'; describe('CLUSTER SET-CONFIG-EPOCH', () => { it('transformArguments', () => { assert.deepEqual( - CLUSTER_SET_CONFIG_EPOCH.transformArguments(0), + parseArgs(CLUSTER_SET_CONFIG_EPOCH, 0), ['CLUSTER', 'SET-CONFIG-EPOCH', '0'] ); }); diff --git a/packages/client/lib/commands/CLUSTER_SET-CONFIG-EPOCH.ts b/packages/client/lib/commands/CLUSTER_SET-CONFIG-EPOCH.ts index 2a650840c44..ba423df7fb7 100644 --- a/packages/client/lib/commands/CLUSTER_SET-CONFIG-EPOCH.ts +++ b/packages/client/lib/commands/CLUSTER_SET-CONFIG-EPOCH.ts @@ -1,10 +1,11 @@ +import { CommandParser } from '../client/parser'; import { SimpleStringReply, Command } from '../RESP/types'; export default { - FIRST_KEY_INDEX: undefined, + NOT_KEYED_COMMAND: true, IS_READ_ONLY: true, - transformArguments(configEpoch: number) { - return ['CLUSTER', 'SET-CONFIG-EPOCH', configEpoch.toString() ]; + parseCommand(parser: CommandParser, configEpoch: number) { + parser.push('CLUSTER', 'SET-CONFIG-EPOCH', configEpoch.toString()); }, transformReply: undefined as unknown as () => SimpleStringReply<'OK'> } as const satisfies Command; diff --git a/packages/client/lib/commands/CLUSTER_SETSLOT.spec.ts b/packages/client/lib/commands/CLUSTER_SETSLOT.spec.ts index 7bce6d74b4a..fac496c3afb 100644 --- a/packages/client/lib/commands/CLUSTER_SETSLOT.spec.ts +++ b/packages/client/lib/commands/CLUSTER_SETSLOT.spec.ts @@ -1,18 +1,19 @@ import { strict as assert } from 'node:assert'; import CLUSTER_SETSLOT, { CLUSTER_SLOT_STATES } from './CLUSTER_SETSLOT'; +import { parseArgs } from './generic-transformers'; describe('CLUSTER SETSLOT', () => { describe('transformArguments', () => { it('simple', () => { assert.deepEqual( - CLUSTER_SETSLOT.transformArguments(0, CLUSTER_SLOT_STATES.IMPORTING), + parseArgs(CLUSTER_SETSLOT, 0, CLUSTER_SLOT_STATES.IMPORTING), ['CLUSTER', 'SETSLOT', '0', 'IMPORTING'] ); }); it('with nodeId', () => { assert.deepEqual( - CLUSTER_SETSLOT.transformArguments(0, CLUSTER_SLOT_STATES.IMPORTING, 'nodeId'), + parseArgs(CLUSTER_SETSLOT, 0, CLUSTER_SLOT_STATES.IMPORTING, 'nodeId'), ['CLUSTER', 'SETSLOT', '0', 'IMPORTING', 'nodeId'] ); }); diff --git a/packages/client/lib/commands/CLUSTER_SETSLOT.ts b/packages/client/lib/commands/CLUSTER_SETSLOT.ts index ad04513688e..1f74316a3f3 100644 --- a/packages/client/lib/commands/CLUSTER_SETSLOT.ts +++ b/packages/client/lib/commands/CLUSTER_SETSLOT.ts @@ -1,3 +1,4 @@ +import { CommandParser } from '../client/parser'; import { RedisArgument, SimpleStringReply, Command } from '../RESP/types'; export const CLUSTER_SLOT_STATES = { @@ -10,16 +11,14 @@ export const CLUSTER_SLOT_STATES = { export type ClusterSlotState = typeof CLUSTER_SLOT_STATES[keyof typeof CLUSTER_SLOT_STATES]; export default { - FIRST_KEY_INDEX: undefined, + NOT_KEYED_COMMAND: true, IS_READ_ONLY: true, - transformArguments(slot: number, state: ClusterSlotState, nodeId?: RedisArgument) { - const args: Array = ['CLUSTER', 'SETSLOT', slot.toString(), state]; + parseCommand(parser: CommandParser, slot: number, state: ClusterSlotState, nodeId?: RedisArgument) { + parser.push('CLUSTER', 'SETSLOT', slot.toString(), state); if (nodeId) { - args.push(nodeId); + parser.push(nodeId); } - - return args; }, transformReply: undefined as unknown as () => SimpleStringReply<'OK'> } as const satisfies Command; diff --git a/packages/client/lib/commands/CLUSTER_SLOTS.spec.ts b/packages/client/lib/commands/CLUSTER_SLOTS.spec.ts index 198dfdc6c1b..28879b036ae 100644 --- a/packages/client/lib/commands/CLUSTER_SLOTS.spec.ts +++ b/packages/client/lib/commands/CLUSTER_SLOTS.spec.ts @@ -1,11 +1,12 @@ import { strict as assert } from 'node:assert'; import testUtils, { GLOBAL } from '../test-utils'; import CLUSTER_SLOTS from './CLUSTER_SLOTS'; +import { parseArgs } from './generic-transformers'; describe('CLUSTER SLOTS', () => { it('transformArguments', () => { assert.deepEqual( - CLUSTER_SLOTS.transformArguments(), + parseArgs(CLUSTER_SLOTS), ['CLUSTER', 'SLOTS'] ); }); diff --git a/packages/client/lib/commands/CLUSTER_SLOTS.ts b/packages/client/lib/commands/CLUSTER_SLOTS.ts index 1b523328bbb..f6f967abe28 100644 --- a/packages/client/lib/commands/CLUSTER_SLOTS.ts +++ b/packages/client/lib/commands/CLUSTER_SLOTS.ts @@ -1,3 +1,4 @@ +import { CommandParser } from '../client/parser'; import { TuplesReply, BlobStringReply, NumberReply, ArrayReply, UnwrapReply, Command } from '../RESP/types'; type RawNode = TuplesReply<[ @@ -16,10 +17,10 @@ type ClusterSlotsRawReply = ArrayReply<[ export type ClusterSlotsNode = ReturnType; export default { - FIRST_KEY_INDEX: undefined, + NOT_KEYED_COMMAND: true, IS_READ_ONLY: true, - transformArguments() { - return ['CLUSTER', 'SLOTS']; + parseCommand(parser: CommandParser) { + parser.push('CLUSTER', 'SLOTS'); }, transformReply(reply: UnwrapReply) { return reply.map(([from, to, master, ...replicas]) => ({ diff --git a/packages/client/lib/commands/COMMAND.ts b/packages/client/lib/commands/COMMAND.ts index d9a960107a2..52eb7eb2fea 100644 --- a/packages/client/lib/commands/COMMAND.ts +++ b/packages/client/lib/commands/COMMAND.ts @@ -1,10 +1,12 @@ +import { CommandParser } from '../client/parser'; import { ArrayReply, Command, UnwrapReply } from '../RESP/types'; import { CommandRawReply, CommandReply, transformCommandReply } from './generic-transformers'; export default { + NOT_KEYED_COMMAND: true, IS_READ_ONLY: true, - transformArguments() { - return ['COMMAND']; + parseCommand(parser: CommandParser) { + parser.push('COMMAND'); }, // TODO: This works, as we don't currently handle any of the items returned as a map transformReply(reply: UnwrapReply>): Array { diff --git a/packages/client/lib/commands/COMMAND_COUNT.spec.ts b/packages/client/lib/commands/COMMAND_COUNT.spec.ts index 05bd29f223c..a36091df482 100644 --- a/packages/client/lib/commands/COMMAND_COUNT.spec.ts +++ b/packages/client/lib/commands/COMMAND_COUNT.spec.ts @@ -1,11 +1,12 @@ import { strict as assert } from 'node:assert'; import testUtils, { GLOBAL } from '../test-utils'; import COMMAND_COUNT from './COMMAND_COUNT'; +import { parseArgs } from './generic-transformers'; describe('COMMAND COUNT', () => { it('transformArguments', () => { assert.deepEqual( - COMMAND_COUNT.transformArguments(), + parseArgs(COMMAND_COUNT), ['COMMAND', 'COUNT'] ); }); diff --git a/packages/client/lib/commands/COMMAND_COUNT.ts b/packages/client/lib/commands/COMMAND_COUNT.ts index 10b0fdefe09..ef561920b0b 100644 --- a/packages/client/lib/commands/COMMAND_COUNT.ts +++ b/packages/client/lib/commands/COMMAND_COUNT.ts @@ -1,10 +1,11 @@ +import { CommandParser } from '../client/parser'; import { NumberReply, Command } from '../RESP/types'; export default { - FIRST_KEY_INDEX: undefined, + NOT_KEYED_COMMAND: true, IS_READ_ONLY: true, - transformArguments() { - return ['COMMAND', 'COUNT']; + parseCommand(parser: CommandParser) { + parser.push('COMMAND', 'COUNT'); }, transformReply: undefined as unknown as () => NumberReply } as const satisfies Command; diff --git a/packages/client/lib/commands/COMMAND_GETKEYS.spec.ts b/packages/client/lib/commands/COMMAND_GETKEYS.spec.ts index d5b9f60790d..332e2d51fbd 100644 --- a/packages/client/lib/commands/COMMAND_GETKEYS.spec.ts +++ b/packages/client/lib/commands/COMMAND_GETKEYS.spec.ts @@ -1,11 +1,12 @@ import { strict as assert } from 'node:assert'; import testUtils, { GLOBAL } from '../test-utils'; import COMMAND_GETKEYS from './COMMAND_GETKEYS'; +import { parseArgs } from './generic-transformers'; describe('COMMAND GETKEYS', () => { it('transformArguments', () => { assert.deepEqual( - COMMAND_GETKEYS.transformArguments(['GET', 'key']), + parseArgs(COMMAND_GETKEYS, ['GET', 'key']), ['COMMAND', 'GETKEYS', 'GET', 'key'] ); }); diff --git a/packages/client/lib/commands/COMMAND_GETKEYS.ts b/packages/client/lib/commands/COMMAND_GETKEYS.ts index 55cca415b8d..97c5cb69ce2 100644 --- a/packages/client/lib/commands/COMMAND_GETKEYS.ts +++ b/packages/client/lib/commands/COMMAND_GETKEYS.ts @@ -1,10 +1,12 @@ +import { CommandParser } from '../client/parser'; import { RedisArgument, ArrayReply, BlobStringReply, Command } from '../RESP/types'; export default { - FIRST_KEY_INDEX: undefined, + NOT_KEYED_COMMAND: true, IS_READ_ONLY: true, - transformArguments(args: Array) { - return ['COMMAND', 'GETKEYS', ...args]; + parseCommand(parser: CommandParser, args: Array) { + parser.push('COMMAND', 'GETKEYS'); + parser.push(...args); }, transformReply: undefined as unknown as () => ArrayReply } as const satisfies Command; diff --git a/packages/client/lib/commands/COMMAND_GETKEYSANDFLAGS.ts b/packages/client/lib/commands/COMMAND_GETKEYSANDFLAGS.ts index a032190c16e..72c1e16a2d1 100644 --- a/packages/client/lib/commands/COMMAND_GETKEYSANDFLAGS.ts +++ b/packages/client/lib/commands/COMMAND_GETKEYSANDFLAGS.ts @@ -1,3 +1,4 @@ +import { CommandParser } from '../client/parser'; import { RedisArgument, ArrayReply, TuplesReply, BlobStringReply, SetReply, UnwrapReply, Command } from '../RESP/types'; export type CommandGetKeysAndFlagsRawReply = ArrayReply>; export default { - FIRST_KEY_INDEX: undefined, + NOT_KEYED_COMMAND: true, IS_READ_ONLY: true, - transformArguments(args: Array) { - return ['COMMAND', 'GETKEYSANDFLAGS', ...args]; + parseCommand(parser: CommandParser, args: Array) { + parser.push('COMMAND', 'GETKEYSANDFLAGS'); + parser.push(...args); }, transformReply(reply: UnwrapReply) { return reply.map(entry => { diff --git a/packages/client/lib/commands/COMMAND_INFO.ts b/packages/client/lib/commands/COMMAND_INFO.ts index 5dbd00e8056..fdf03780652 100644 --- a/packages/client/lib/commands/COMMAND_INFO.ts +++ b/packages/client/lib/commands/COMMAND_INFO.ts @@ -1,11 +1,12 @@ +import { CommandParser } from '../client/parser'; import { ArrayReply, Command, UnwrapReply } from '../RESP/types'; import { CommandRawReply, CommandReply, transformCommandReply } from './generic-transformers'; export default { - FIRST_KEY_INDEX: undefined, + NOT_KEYED_COMMAND: true, IS_READ_ONLY: true, - transformArguments(commands: Array) { - return ['COMMAND', 'INFO', ...commands]; + parseCommand(parser: CommandParser, commands: Array) { + parser.push('COMMAND', 'INFO', ...commands); }, // TODO: This works, as we don't currently handle any of the items returned as a map transformReply(reply: UnwrapReply>): Array { diff --git a/packages/client/lib/commands/COMMAND_LIST.spec.ts b/packages/client/lib/commands/COMMAND_LIST.spec.ts index 28a9a203bc8..d2ee9e66161 100644 --- a/packages/client/lib/commands/COMMAND_LIST.spec.ts +++ b/packages/client/lib/commands/COMMAND_LIST.spec.ts @@ -1,6 +1,7 @@ import { strict as assert } from 'node:assert'; import testUtils, { GLOBAL } from '../test-utils'; import COMMAND_LIST from './COMMAND_LIST'; +import { parseArgs } from './generic-transformers'; describe('COMMAND LIST', () => { testUtils.isVersionGreaterThanHook([7]); @@ -8,7 +9,7 @@ describe('COMMAND LIST', () => { describe('transformArguments', () => { it('simple', () => { assert.deepEqual( - COMMAND_LIST.transformArguments(), + parseArgs(COMMAND_LIST), ['COMMAND', 'LIST'] ); }); @@ -16,7 +17,7 @@ describe('COMMAND LIST', () => { describe('with FILTERBY', () => { it('MODULE', () => { assert.deepEqual( - COMMAND_LIST.transformArguments({ + parseArgs(COMMAND_LIST, { FILTERBY: { type: 'MODULE', value: 'JSON' @@ -28,7 +29,7 @@ describe('COMMAND LIST', () => { it('ACLCAT', () => { assert.deepEqual( - COMMAND_LIST.transformArguments({ + parseArgs(COMMAND_LIST, { FILTERBY: { type: 'ACLCAT', value: 'admin' @@ -40,7 +41,7 @@ describe('COMMAND LIST', () => { it('PATTERN', () => { assert.deepEqual( - COMMAND_LIST.transformArguments({ + parseArgs(COMMAND_LIST, { FILTERBY: { type: 'PATTERN', value: 'a*' diff --git a/packages/client/lib/commands/COMMAND_LIST.ts b/packages/client/lib/commands/COMMAND_LIST.ts index e73cfdc1a0b..ba518b70eca 100644 --- a/packages/client/lib/commands/COMMAND_LIST.ts +++ b/packages/client/lib/commands/COMMAND_LIST.ts @@ -1,3 +1,4 @@ +import { CommandParser } from '../client/parser'; import { RedisArgument, ArrayReply, BlobStringReply, Command } from '../RESP/types'; export const COMMAND_LIST_FILTER_BY = { @@ -16,20 +17,18 @@ export interface CommandListOptions { } export default { - FIRST_KEY_INDEX: undefined, + NOT_KEYED_COMMAND: true, IS_READ_ONLY: true, - transformArguments(options?: CommandListOptions) { - const args: Array = ['COMMAND', 'LIST']; + parseCommand(parser: CommandParser, options?: CommandListOptions) { + parser.push('COMMAND', 'LIST'); if (options?.FILTERBY) { - args.push( + parser.push( 'FILTERBY', options.FILTERBY.type, options.FILTERBY.value ); } - - return args; }, transformReply: undefined as unknown as () => ArrayReply } as const satisfies Command; diff --git a/packages/client/lib/commands/CONFIG_GET.spec.ts b/packages/client/lib/commands/CONFIG_GET.spec.ts index 94bb2fadcb9..411b2ddf472 100644 --- a/packages/client/lib/commands/CONFIG_GET.spec.ts +++ b/packages/client/lib/commands/CONFIG_GET.spec.ts @@ -1,19 +1,20 @@ import { strict as assert } from 'node:assert'; import testUtils, { GLOBAL } from '../test-utils'; import CONFIG_GET from './CONFIG_GET'; +import { parseArgs } from './generic-transformers'; describe('CONFIG GET', () => { describe('transformArguments', () => { it('string', () => { assert.deepEqual( - CONFIG_GET.transformArguments('*'), + parseArgs(CONFIG_GET, '*'), ['CONFIG', 'GET', '*'] ); }); it('Array', () => { assert.deepEqual( - CONFIG_GET.transformArguments(['1', '2']), + parseArgs(CONFIG_GET, ['1', '2']), ['CONFIG', 'GET', '1', '2'] ); }); diff --git a/packages/client/lib/commands/CONFIG_GET.ts b/packages/client/lib/commands/CONFIG_GET.ts index 72fb6e56f5f..54fa997bf61 100644 --- a/packages/client/lib/commands/CONFIG_GET.ts +++ b/packages/client/lib/commands/CONFIG_GET.ts @@ -1,11 +1,13 @@ +import { CommandParser } from '../client/parser'; import { MapReply, BlobStringReply, Command } from '../RESP/types'; -import { RedisVariadicArgument, pushVariadicArguments, transformTuplesReply } from './generic-transformers'; +import { RedisVariadicArgument, transformTuplesReply } from './generic-transformers'; export default { - FIRST_KEY_INDEX: undefined, + NOT_KEYED_COMMAND: true, IS_READ_ONLY: true, - transformArguments(parameters: RedisVariadicArgument) { - return pushVariadicArguments(['CONFIG', 'GET'], parameters); + parseCommand(parser: CommandParser, parameters: RedisVariadicArgument) { + parser.push('CONFIG', 'GET'); + parser.pushVariadic(parameters); }, transformReply: { 2: transformTuplesReply, diff --git a/packages/client/lib/commands/CONFIG_RESETSTAT.spec.ts b/packages/client/lib/commands/CONFIG_RESETSTAT.spec.ts index c0699e182fc..f2f573df0dc 100644 --- a/packages/client/lib/commands/CONFIG_RESETSTAT.spec.ts +++ b/packages/client/lib/commands/CONFIG_RESETSTAT.spec.ts @@ -1,10 +1,11 @@ import { strict as assert } from 'node:assert'; import CONFIG_RESETSTAT from './CONFIG_RESETSTAT'; +import { parseArgs } from './generic-transformers'; describe('CONFIG RESETSTAT', () => { it('transformArguments', () => { assert.deepEqual( - CONFIG_RESETSTAT.transformArguments(), + parseArgs(CONFIG_RESETSTAT), ['CONFIG', 'RESETSTAT'] ); }); diff --git a/packages/client/lib/commands/CONFIG_RESETSTAT.ts b/packages/client/lib/commands/CONFIG_RESETSTAT.ts index 4d5deb18b47..15de5ba7808 100644 --- a/packages/client/lib/commands/CONFIG_RESETSTAT.ts +++ b/packages/client/lib/commands/CONFIG_RESETSTAT.ts @@ -1,10 +1,11 @@ +import { CommandParser } from '../client/parser'; import { SimpleStringReply, Command } from '../RESP/types'; export default { - FIRST_KEY_INDEX: undefined, + NOT_KEYED_COMMAND: true, IS_READ_ONLY: true, - transformArguments() { - return ['CONFIG', 'RESETSTAT']; + parseCommand(parser: CommandParser) { + parser.push('CONFIG', 'RESETSTAT'); }, transformReply: undefined as unknown as () => SimpleStringReply } as const satisfies Command; diff --git a/packages/client/lib/commands/CONFIG_REWRITE.spec.ts b/packages/client/lib/commands/CONFIG_REWRITE.spec.ts index d612ae216bc..bc006e84c80 100644 --- a/packages/client/lib/commands/CONFIG_REWRITE.spec.ts +++ b/packages/client/lib/commands/CONFIG_REWRITE.spec.ts @@ -1,10 +1,11 @@ import { strict as assert } from 'node:assert'; import CONFIG_REWRITE from './CONFIG_REWRITE'; +import { parseArgs } from './generic-transformers'; describe('CONFIG REWRITE', () => { it('transformArguments', () => { assert.deepEqual( - CONFIG_REWRITE.transformArguments(), + parseArgs(CONFIG_REWRITE), ['CONFIG', 'REWRITE'] ); }); diff --git a/packages/client/lib/commands/CONFIG_REWRITE.ts b/packages/client/lib/commands/CONFIG_REWRITE.ts index 6fbc4b1fa27..ae6712ffb57 100644 --- a/packages/client/lib/commands/CONFIG_REWRITE.ts +++ b/packages/client/lib/commands/CONFIG_REWRITE.ts @@ -1,10 +1,11 @@ +import { CommandParser } from '../client/parser'; import { SimpleStringReply, Command } from '../RESP/types'; export default { - FIRST_KEY_INDEX: undefined, + NOT_KEYED_COMMAND: true, IS_READ_ONLY: true, - transformArguments() { - return ['CONFIG', 'REWRITE']; + parseCommand(parser: CommandParser) { + parser.push('CONFIG', 'REWRITE'); }, transformReply: undefined as unknown as () => SimpleStringReply } as const satisfies Command; diff --git a/packages/client/lib/commands/CONFIG_SET.spec.ts b/packages/client/lib/commands/CONFIG_SET.spec.ts index 060183f58d1..56bed2ac46a 100644 --- a/packages/client/lib/commands/CONFIG_SET.spec.ts +++ b/packages/client/lib/commands/CONFIG_SET.spec.ts @@ -1,19 +1,20 @@ import { strict as assert } from 'node:assert'; import testUtils, { GLOBAL } from '../test-utils'; import CONFIG_SET from './CONFIG_SET'; +import { parseArgs } from './generic-transformers'; describe('CONFIG SET', () => { describe('transformArguments', () => { it('set one parameter (old version)', () => { assert.deepEqual( - CONFIG_SET.transformArguments('parameter', 'value'), + parseArgs(CONFIG_SET, 'parameter', 'value'), ['CONFIG', 'SET', 'parameter', 'value'] ); }); it('set muiltiple parameters', () => { assert.deepEqual( - CONFIG_SET.transformArguments({ + parseArgs(CONFIG_SET, { 1: 'a', 2: 'b', 3: 'c' diff --git a/packages/client/lib/commands/CONFIG_SET.ts b/packages/client/lib/commands/CONFIG_SET.ts index c7072245e22..dd1bbc29ef2 100644 --- a/packages/client/lib/commands/CONFIG_SET.ts +++ b/packages/client/lib/commands/CONFIG_SET.ts @@ -1,3 +1,4 @@ +import { CommandParser } from '../client/parser'; import { SimpleStringReply, Command, RedisArgument } from '../RESP/types'; type SingleParameter = [parameter: RedisArgument, value: RedisArgument]; @@ -5,22 +6,21 @@ type SingleParameter = [parameter: RedisArgument, value: RedisArgument]; type MultipleParameters = [config: Record]; export default { - FIRST_KEY_INDEX: undefined, + NOT_KEYED_COMMAND: true, IS_READ_ONLY: true, - transformArguments( + parseCommand( + parser: CommandParser, ...[parameterOrConfig, value]: SingleParameter | MultipleParameters ) { - const args: Array = ['CONFIG', 'SET']; + parser.push('CONFIG', 'SET'); if (typeof parameterOrConfig === 'string' || parameterOrConfig instanceof Buffer) { - args.push(parameterOrConfig, value!); + parser.push(parameterOrConfig, value!); } else { for (const [key, value] of Object.entries(parameterOrConfig)) { - args.push(key, value); + parser.push(key, value); } } - - return args; }, transformReply: undefined as unknown as () => SimpleStringReply } as const satisfies Command; diff --git a/packages/client/lib/commands/COPY.spec.ts b/packages/client/lib/commands/COPY.spec.ts index c4c26c30dc2..cd0c6ec9fbe 100644 --- a/packages/client/lib/commands/COPY.spec.ts +++ b/packages/client/lib/commands/COPY.spec.ts @@ -1,6 +1,7 @@ import { strict as assert } from 'node:assert'; import testUtils, { GLOBAL } from '../test-utils'; import COPY from './COPY'; +import { parseArgs } from './generic-transformers'; describe('COPY', () => { testUtils.isVersionGreaterThanHook([6, 2]); @@ -8,14 +9,14 @@ describe('COPY', () => { describe('transformArguments', () => { it('simple', () => { assert.deepEqual( - COPY.transformArguments('source', 'destination'), + parseArgs(COPY, 'source', 'destination'), ['COPY', 'source', 'destination'] ); }); it('with destination DB flag', () => { assert.deepEqual( - COPY.transformArguments('source', 'destination', { + parseArgs(COPY, 'source', 'destination', { DB: 1 }), ['COPY', 'source', 'destination', 'DB', '1'] @@ -24,7 +25,7 @@ describe('COPY', () => { it('with replace flag', () => { assert.deepEqual( - COPY.transformArguments('source', 'destination', { + parseArgs(COPY, 'source', 'destination', { REPLACE: true }), ['COPY', 'source', 'destination', 'REPLACE'] @@ -33,7 +34,7 @@ describe('COPY', () => { it('with both flags', () => { assert.deepEqual( - COPY.transformArguments('source', 'destination', { + parseArgs(COPY, 'source', 'destination', { DB: 1, REPLACE: true }), diff --git a/packages/client/lib/commands/COPY.ts b/packages/client/lib/commands/COPY.ts index a65948cf944..f26a930264c 100644 --- a/packages/client/lib/commands/COPY.ts +++ b/packages/client/lib/commands/COPY.ts @@ -1,3 +1,4 @@ +import { CommandParser } from '../client/parser'; import { RedisArgument, NumberReply, Command } from '../RESP/types'; export interface CopyCommandOptions { @@ -6,20 +7,18 @@ export interface CopyCommandOptions { } export default { - FIRST_KEY_INDEX: 1, IS_READ_ONLY: false, - transformArguments(source: RedisArgument, destination: RedisArgument, options?: CopyCommandOptions) { - const args = ['COPY', source, destination]; + parseCommand(parser: CommandParser, source: RedisArgument, destination: RedisArgument, options?: CopyCommandOptions) { + parser.push('COPY'); + parser.pushKeys([source, destination]); if (options?.DB) { - args.push('DB', options.DB.toString()); + parser.push('DB', options.DB.toString()); } if (options?.REPLACE) { - args.push('REPLACE'); + parser.push('REPLACE'); } - - return args; }, transformReply: undefined as unknown as () => NumberReply } as const satisfies Command; diff --git a/packages/client/lib/commands/DBSIZE.spec.ts b/packages/client/lib/commands/DBSIZE.spec.ts index bd668d166e7..5778e30de3e 100644 --- a/packages/client/lib/commands/DBSIZE.spec.ts +++ b/packages/client/lib/commands/DBSIZE.spec.ts @@ -1,11 +1,12 @@ import { strict as assert } from 'node:assert'; import testUtils, { GLOBAL } from '../test-utils'; import DBSIZE from './DBSIZE'; +import { parseArgs } from './generic-transformers'; describe('DBSIZE', () => { it('transformArguments', () => { assert.deepEqual( - DBSIZE.transformArguments(), + parseArgs(DBSIZE), ['DBSIZE'] ); }); diff --git a/packages/client/lib/commands/DBSIZE.ts b/packages/client/lib/commands/DBSIZE.ts index 54770831ab0..1ba1f060476 100644 --- a/packages/client/lib/commands/DBSIZE.ts +++ b/packages/client/lib/commands/DBSIZE.ts @@ -1,10 +1,11 @@ +import { CommandParser } from '../client/parser'; import { NumberReply, Command } from '../RESP/types'; export default { - FIRST_KEY_INDEX: undefined, + NOT_KEYED_COMMAND: true, IS_READ_ONLY: true, - transformArguments() { - return ['DBSIZE']; + parseCommand(parser: CommandParser) { + parser.push('DBSIZE'); }, transformReply: undefined as unknown as () => NumberReply } as const satisfies Command; diff --git a/packages/client/lib/commands/DECR.spec.ts b/packages/client/lib/commands/DECR.spec.ts index 80d6c8eb55e..69ff5a5391f 100644 --- a/packages/client/lib/commands/DECR.spec.ts +++ b/packages/client/lib/commands/DECR.spec.ts @@ -1,11 +1,12 @@ import { strict as assert } from 'node:assert'; import testUtils, { GLOBAL } from '../test-utils'; import DECR from './DECR'; +import { parseArgs } from './generic-transformers'; describe('DECR', () => { it('transformArguments', () => { assert.deepEqual( - DECR.transformArguments('key'), + parseArgs(DECR, 'key'), ['DECR', 'key'] ); }); diff --git a/packages/client/lib/commands/DECR.ts b/packages/client/lib/commands/DECR.ts index 540148b7eed..b9a6200d81b 100644 --- a/packages/client/lib/commands/DECR.ts +++ b/packages/client/lib/commands/DECR.ts @@ -1,9 +1,10 @@ +import { CommandParser } from '../client/parser'; import { RedisArgument, NumberReply, Command } from '../RESP/types'; export default { - FIRST_KEY_INDEX: 1, - transformArguments(key: RedisArgument) { - return ['DECR', key]; + parseCommand(parser: CommandParser, key: RedisArgument) { + parser.push('DECR'); + parser.pushKey(key); }, transformReply: undefined as unknown as () => NumberReply } as const satisfies Command; diff --git a/packages/client/lib/commands/DECRBY.spec.ts b/packages/client/lib/commands/DECRBY.spec.ts index fc0c1033187..ae80fd714e0 100644 --- a/packages/client/lib/commands/DECRBY.spec.ts +++ b/packages/client/lib/commands/DECRBY.spec.ts @@ -1,11 +1,12 @@ import { strict as assert } from 'node:assert'; import testUtils, { GLOBAL } from '../test-utils'; import DECRBY from './DECRBY'; +import { parseArgs } from './generic-transformers'; describe('DECRBY', () => { it('transformArguments', () => { assert.deepEqual( - DECRBY.transformArguments('key', 2), + parseArgs(DECRBY, 'key', 2), ['DECRBY', 'key', '2'] ); }); diff --git a/packages/client/lib/commands/DECRBY.ts b/packages/client/lib/commands/DECRBY.ts index 77d56939dd5..53a8315130d 100644 --- a/packages/client/lib/commands/DECRBY.ts +++ b/packages/client/lib/commands/DECRBY.ts @@ -1,9 +1,11 @@ +import { CommandParser } from '../client/parser'; import { RedisArgument, NumberReply, Command } from '../RESP/types'; export default { - FIRST_KEY_INDEX: 1, - transformArguments(key: RedisArgument, decrement: number) { - return ['DECRBY', key, decrement.toString()]; + parseCommand(parser: CommandParser, key: RedisArgument, decrement: number) { + parser.push('DECRBY'); + parser.pushKey(key); + parser.push(decrement.toString()); }, transformReply: undefined as unknown as () => NumberReply } as const satisfies Command; diff --git a/packages/client/lib/commands/DEL.spec.ts b/packages/client/lib/commands/DEL.spec.ts index caac8ac13b5..3d0364e7523 100644 --- a/packages/client/lib/commands/DEL.spec.ts +++ b/packages/client/lib/commands/DEL.spec.ts @@ -1,19 +1,20 @@ import { strict as assert } from 'node:assert'; import testUtils, { GLOBAL } from '../test-utils'; import DEL from './DEL'; +import { parseArgs } from './generic-transformers'; describe('DEL', () => { describe('transformArguments', () => { it('string', () => { assert.deepEqual( - DEL.transformArguments('key'), + parseArgs(DEL, 'key'), ['DEL', 'key'] ); }); it('array', () => { assert.deepEqual( - DEL.transformArguments(['key1', 'key2']), + parseArgs(DEL, ['key1', 'key2']), ['DEL', 'key1', 'key2'] ); }); diff --git a/packages/client/lib/commands/DEL.ts b/packages/client/lib/commands/DEL.ts index f59a5ba2e89..da0803f4d1b 100644 --- a/packages/client/lib/commands/DEL.ts +++ b/packages/client/lib/commands/DEL.ts @@ -1,11 +1,12 @@ +import { CommandParser } from '../client/parser'; import { NumberReply, Command } from '../RESP/types'; -import { RedisVariadicArgument, pushVariadicArguments } from './generic-transformers'; +import { RedisVariadicArgument } from './generic-transformers'; export default { - FIRST_KEY_INDEX: 1, IS_READ_ONLY: false, - transformArguments(keys: RedisVariadicArgument) { - return pushVariadicArguments(['DEL'], keys); + parseCommand(parser: CommandParser, keys: RedisVariadicArgument) { + parser.push('DEL'); + parser.pushKeys(keys); }, transformReply: undefined as unknown as () => NumberReply } as const satisfies Command; diff --git a/packages/client/lib/commands/DISCARD.spec.ts b/packages/client/lib/commands/DISCARD.spec.ts index 76e0abd57af..7aa769fc2ee 100644 --- a/packages/client/lib/commands/DISCARD.spec.ts +++ b/packages/client/lib/commands/DISCARD.spec.ts @@ -1,10 +1,11 @@ import { strict as assert } from 'node:assert'; import DISCARD from './DISCARD'; +import { parseArgs } from './generic-transformers'; describe('DISCARD', () => { it('transformArguments', () => { assert.deepEqual( - DISCARD.transformArguments(), + parseArgs(DISCARD), ['DISCARD'] ); }); diff --git a/packages/client/lib/commands/DISCARD.ts b/packages/client/lib/commands/DISCARD.ts index e153070c989..1d30191c13d 100644 --- a/packages/client/lib/commands/DISCARD.ts +++ b/packages/client/lib/commands/DISCARD.ts @@ -1,8 +1,9 @@ +import { CommandParser } from '../client/parser'; import { SimpleStringReply, Command } from '../RESP/types'; export default { - transformArguments() { - return ['DISCARD']; + parseCommand(parser: CommandParser) { + parser.push('DISCARD'); }, transformReply: undefined as unknown as () => SimpleStringReply } as const satisfies Command; diff --git a/packages/client/lib/commands/DUMP.spec.ts b/packages/client/lib/commands/DUMP.spec.ts index 15be3fae086..76fb2ec7c18 100644 --- a/packages/client/lib/commands/DUMP.spec.ts +++ b/packages/client/lib/commands/DUMP.spec.ts @@ -1,7 +1,16 @@ import { strict as assert } from 'node:assert'; import testUtils, { GLOBAL } from '../test-utils'; +import DUMP from './DUMP'; +import { parseArgs } from './generic-transformers'; describe('DUMP', () => { + it('transformArguments', () => { + assert.deepEqual( + parseArgs(DUMP, 'key'), + ['DUMP', 'key'] + ); + }); + testUtils.testAll('client.dump', async client => { assert.equal( await client.dump('key'), diff --git a/packages/client/lib/commands/DUMP.ts b/packages/client/lib/commands/DUMP.ts index 06b12f06d09..e442c1cdb2f 100644 --- a/packages/client/lib/commands/DUMP.ts +++ b/packages/client/lib/commands/DUMP.ts @@ -1,10 +1,11 @@ +import { CommandParser } from '../client/parser'; import { RedisArgument, BlobStringReply, Command } from '../RESP/types'; export default { - FIRST_KEY_INDEX: 1, IS_READ_ONLY: true, - transformArguments(key: RedisArgument) { - return ['DUMP', key]; + parseCommand(parser: CommandParser, key: RedisArgument) { + parser.push('DUMP'); + parser.pushKey(key); }, transformReply: undefined as unknown as () => BlobStringReply } as const satisfies Command; diff --git a/packages/client/lib/commands/ECHO.spec.ts b/packages/client/lib/commands/ECHO.spec.ts index c0d0725282c..38fd1d4270e 100644 --- a/packages/client/lib/commands/ECHO.spec.ts +++ b/packages/client/lib/commands/ECHO.spec.ts @@ -1,11 +1,12 @@ import { strict as assert } from 'node:assert'; import testUtils, { GLOBAL } from '../test-utils'; import ECHO from './ECHO'; +import { parseArgs } from './generic-transformers'; describe('ECHO', () => { it('transformArguments', () => { assert.deepEqual( - ECHO.transformArguments('message'), + parseArgs(ECHO, 'message'), ['ECHO', 'message'] ); }); diff --git a/packages/client/lib/commands/ECHO.ts b/packages/client/lib/commands/ECHO.ts index dfe5ec13000..7935bdc0101 100644 --- a/packages/client/lib/commands/ECHO.ts +++ b/packages/client/lib/commands/ECHO.ts @@ -1,10 +1,11 @@ +import { CommandParser } from '../client/parser'; import { RedisArgument, BlobStringReply, Command } from '../RESP/types'; export default { - FIRST_KEY_INDEX: undefined, + NOT_KEYED_COMMAND: true, IS_READ_ONLY: true, - transformArguments(message: RedisArgument) { - return ['ECHO', message]; + parseCommand(parser: CommandParser, message: RedisArgument) { + parser.push('ECHO', message); }, transformReply: undefined as unknown as () => BlobStringReply } as const satisfies Command; diff --git a/packages/client/lib/commands/EVAL.spec.ts b/packages/client/lib/commands/EVAL.spec.ts index 2aea64e0991..8ef16eed835 100644 --- a/packages/client/lib/commands/EVAL.spec.ts +++ b/packages/client/lib/commands/EVAL.spec.ts @@ -1,11 +1,12 @@ import { strict as assert } from 'node:assert'; import testUtils, { GLOBAL } from '../test-utils'; import EVAL from './EVAL'; +import { parseArgs } from './generic-transformers'; describe('EVAL', () => { it('transformArguments', () => { assert.deepEqual( - EVAL.transformArguments('return KEYS[1] + ARGV[1]', { + parseArgs(EVAL, 'return KEYS[1] + ARGV[1]', { keys: ['key'], arguments: ['argument'] }), diff --git a/packages/client/lib/commands/EVAL.ts b/packages/client/lib/commands/EVAL.ts index 21684e7a313..cdb8025b0be 100644 --- a/packages/client/lib/commands/EVAL.ts +++ b/packages/client/lib/commands/EVAL.ts @@ -1,3 +1,4 @@ +import { CommandParser } from '../client/parser'; import { RedisArgument, ReplyUnion, Command } from '../RESP/types'; export interface EvalOptions { @@ -5,29 +6,28 @@ export interface EvalOptions { arguments?: Array; } -export function transformEvalArguments( - command: RedisArgument, +export function parseEvalArguments( + parser: CommandParser, script: RedisArgument, options?: EvalOptions ) { - const args = [command, script]; - + parser.push(script); if (options?.keys) { - args.push(options.keys.length.toString(), ...options.keys); + parser.pushKeysLength(options.keys); } else { - args.push('0'); + parser.push('0'); } if (options?.arguments) { - args.push(...options.arguments); + parser.push(...options.arguments) } - - return args; } export default { - FIRST_KEY_INDEX: (_, options?: EvalOptions) => options?.keys?.[0], IS_READ_ONLY: false, - transformArguments: transformEvalArguments.bind(undefined, 'EVAL'), + parseCommand(...args: Parameters) { + args[0].push('EVAL'); + parseEvalArguments(...args); + }, transformReply: undefined as unknown as () => ReplyUnion } as const satisfies Command; diff --git a/packages/client/lib/commands/EVALSHA.spec.ts b/packages/client/lib/commands/EVALSHA.spec.ts index 81d3a0ec2b0..c491d6e2308 100644 --- a/packages/client/lib/commands/EVALSHA.spec.ts +++ b/packages/client/lib/commands/EVALSHA.spec.ts @@ -1,10 +1,11 @@ import { strict as assert } from 'node:assert'; import EVALSHA from './EVALSHA'; +import { parseArgs } from './generic-transformers'; describe('EVALSHA', () => { it('transformArguments', () => { assert.deepEqual( - EVALSHA.transformArguments('sha1', { + parseArgs(EVALSHA, 'sha1', { keys: ['key'], arguments: ['argument'] }), diff --git a/packages/client/lib/commands/EVALSHA.ts b/packages/client/lib/commands/EVALSHA.ts index dc4127f90da..5a9cc771358 100644 --- a/packages/client/lib/commands/EVALSHA.ts +++ b/packages/client/lib/commands/EVALSHA.ts @@ -1,9 +1,11 @@ import { Command } from '../RESP/types'; -import EVAL, { transformEvalArguments } from './EVAL'; +import EVAL, { parseEvalArguments } from './EVAL'; export default { - FIRST_KEY_INDEX: EVAL.FIRST_KEY_INDEX, IS_READ_ONLY: false, - transformArguments: transformEvalArguments.bind(undefined, 'EVALSHA'), + parseCommand(...args: Parameters) { + args[0].push('EVALSHA'); + parseEvalArguments(...args); + }, transformReply: EVAL.transformReply } as const satisfies Command; diff --git a/packages/client/lib/commands/EVALSHA_RO.spec.ts b/packages/client/lib/commands/EVALSHA_RO.spec.ts index 20b4a27e0d1..d3debe933fe 100644 --- a/packages/client/lib/commands/EVALSHA_RO.spec.ts +++ b/packages/client/lib/commands/EVALSHA_RO.spec.ts @@ -1,13 +1,14 @@ import { strict as assert } from 'node:assert'; import testUtils from '../test-utils'; import EVALSHA_RO from './EVALSHA_RO'; +import { parseArgs } from './generic-transformers'; describe('EVALSHA_RO', () => { testUtils.isVersionGreaterThanHook([7]); it('transformArguments', () => { assert.deepEqual( - EVALSHA_RO.transformArguments('sha1', { + parseArgs(EVALSHA_RO, 'sha1', { keys: ['key'], arguments: ['argument'] }), diff --git a/packages/client/lib/commands/EVALSHA_RO.ts b/packages/client/lib/commands/EVALSHA_RO.ts index fe9042bd5fd..24fadb3f486 100644 --- a/packages/client/lib/commands/EVALSHA_RO.ts +++ b/packages/client/lib/commands/EVALSHA_RO.ts @@ -1,9 +1,11 @@ import { Command } from '../RESP/types'; -import EVAL, { transformEvalArguments } from './EVAL'; +import EVAL, { parseEvalArguments } from './EVAL'; export default { - FIRST_KEY_INDEX: EVAL.FIRST_KEY_INDEX, IS_READ_ONLY: true, - transformArguments: transformEvalArguments.bind(undefined, 'EVALSHA_RO'), + parseCommand(...args: Parameters) { + args[0].push('EVALSHA_RO'); + parseEvalArguments(...args); + }, transformReply: EVAL.transformReply } as const satisfies Command; diff --git a/packages/client/lib/commands/EVAL_RO.spec.ts b/packages/client/lib/commands/EVAL_RO.spec.ts index 3f071e80681..b5cf1e4e926 100644 --- a/packages/client/lib/commands/EVAL_RO.spec.ts +++ b/packages/client/lib/commands/EVAL_RO.spec.ts @@ -1,13 +1,14 @@ import { strict as assert } from 'node:assert'; import testUtils, { GLOBAL } from '../test-utils'; import EVAL_RO from './EVAL_RO'; +import { parseArgs } from './generic-transformers'; describe('EVAL_RO', () => { testUtils.isVersionGreaterThanHook([7]); it('transformArguments', () => { assert.deepEqual( - EVAL_RO.transformArguments('return KEYS[1] + ARGV[1]', { + parseArgs(EVAL_RO, 'return KEYS[1] + ARGV[1]', { keys: ['key'], arguments: ['argument'] }), diff --git a/packages/client/lib/commands/EVAL_RO.ts b/packages/client/lib/commands/EVAL_RO.ts index 2608e28f789..2438fd9d1dd 100644 --- a/packages/client/lib/commands/EVAL_RO.ts +++ b/packages/client/lib/commands/EVAL_RO.ts @@ -1,9 +1,11 @@ import { Command } from '../RESP/types'; -import EVAL, { transformEvalArguments } from './EVAL'; +import EVAL, { parseEvalArguments } from './EVAL'; export default { - FIRST_KEY_INDEX: EVAL.FIRST_KEY_INDEX, IS_READ_ONLY: true, - transformArguments: transformEvalArguments.bind(undefined, 'EVAL_RO'), + parseCommand(...args: Parameters) { + args[0].push('EVAL_RO'); + parseEvalArguments(...args); + }, transformReply: EVAL.transformReply } as const satisfies Command; diff --git a/packages/client/lib/commands/EXISTS.spec.ts b/packages/client/lib/commands/EXISTS.spec.ts index 695795697f1..d2802dd49b3 100644 --- a/packages/client/lib/commands/EXISTS.spec.ts +++ b/packages/client/lib/commands/EXISTS.spec.ts @@ -1,19 +1,20 @@ import { strict as assert } from 'node:assert'; import testUtils, { GLOBAL } from '../test-utils'; import EXISTS from './EXISTS'; +import { parseArgs } from './generic-transformers'; describe('EXISTS', () => { - describe('transformArguments', () => { + describe('parseCommand', () => { it('string', () => { assert.deepEqual( - EXISTS.transformArguments('key'), + parseArgs(EXISTS, 'key'), ['EXISTS', 'key'] ); }); it('array', () => { assert.deepEqual( - EXISTS.transformArguments(['1', '2']), + parseArgs(EXISTS, ['1', '2']), ['EXISTS', '1', '2'] ); }); diff --git a/packages/client/lib/commands/EXISTS.ts b/packages/client/lib/commands/EXISTS.ts index a077943b8d2..8ebb28269fe 100644 --- a/packages/client/lib/commands/EXISTS.ts +++ b/packages/client/lib/commands/EXISTS.ts @@ -1,11 +1,13 @@ +import { CommandParser } from '../client/parser'; import { NumberReply, Command } from '../RESP/types'; -import { RedisVariadicArgument, pushVariadicArguments } from './generic-transformers'; +import { RedisVariadicArgument } from './generic-transformers'; export default { - FIRST_KEY_INDEX: 1, + CACHEABLE: true, IS_READ_ONLY: true, - transformArguments(keys: RedisVariadicArgument) { - return pushVariadicArguments(['EXISTS'], keys); + parseCommand(parser: CommandParser, keys: RedisVariadicArgument) { + parser.push('EXISTS'); + parser.pushKeys(keys); }, transformReply: undefined as unknown as () => NumberReply } as const satisfies Command; diff --git a/packages/client/lib/commands/EXPIRE.spec.ts b/packages/client/lib/commands/EXPIRE.spec.ts index 817e37cca45..f3d197b5c69 100644 --- a/packages/client/lib/commands/EXPIRE.spec.ts +++ b/packages/client/lib/commands/EXPIRE.spec.ts @@ -1,19 +1,20 @@ import { strict as assert } from 'node:assert'; import testUtils, { GLOBAL } from '../test-utils'; import EXPIRE from './EXPIRE'; +import { parseArgs } from './generic-transformers'; describe('EXPIRE', () => { describe('transformArguments', () => { it('simple', () => { assert.deepEqual( - EXPIRE.transformArguments('key', 1), + parseArgs(EXPIRE, 'key', 1), ['EXPIRE', 'key', '1'] ); }); it('with set option', () => { assert.deepEqual( - EXPIRE.transformArguments('key', 1, 'NX'), + parseArgs(EXPIRE, 'key', 1, 'NX'), ['EXPIRE', 'key', '1', 'NX'] ); }); diff --git a/packages/client/lib/commands/EXPIRE.ts b/packages/client/lib/commands/EXPIRE.ts index 3e57769bd6e..1e73b629492 100644 --- a/packages/client/lib/commands/EXPIRE.ts +++ b/packages/client/lib/commands/EXPIRE.ts @@ -1,20 +1,20 @@ +import { CommandParser } from '../client/parser'; import { RedisArgument, NumberReply, Command } from '../RESP/types'; export default { - FIRST_KEY_INDEX: 1, IS_READ_ONLY: true, - transformArguments( + parseCommand( + parser: CommandParser, key: RedisArgument, seconds: number, mode?: 'NX' | 'XX' | 'GT' | 'LT' ) { - const args = ['EXPIRE', key, seconds.toString()]; - + parser.push('EXPIRE'); + parser.pushKey(key); + parser.push(seconds.toString()); if (mode) { - args.push(mode); + parser.push(mode); } - - return args; }, transformReply: undefined as unknown as () => NumberReply } as const satisfies Command; diff --git a/packages/client/lib/commands/EXPIREAT.spec.ts b/packages/client/lib/commands/EXPIREAT.spec.ts index 31efdcea398..1949fb051bb 100644 --- a/packages/client/lib/commands/EXPIREAT.spec.ts +++ b/packages/client/lib/commands/EXPIREAT.spec.ts @@ -1,12 +1,13 @@ import { strict as assert } from 'node:assert'; import testUtils, { GLOBAL } from '../test-utils'; import EXPIREAT from './EXPIREAT'; +import { parseArgs } from './generic-transformers'; describe('EXPIREAT', () => { describe('transformArguments', () => { it('number', () => { assert.deepEqual( - EXPIREAT.transformArguments('key', 1), + parseArgs(EXPIREAT, 'key', 1), ['EXPIREAT', 'key', '1'] ); }); @@ -14,14 +15,14 @@ describe('EXPIREAT', () => { it('date', () => { const d = new Date(); assert.deepEqual( - EXPIREAT.transformArguments('key', d), + parseArgs(EXPIREAT, 'key', d), ['EXPIREAT', 'key', Math.floor(d.getTime() / 1000).toString()] ); }); it('with set option', () => { assert.deepEqual( - EXPIREAT.transformArguments('key', 1, 'GT'), + parseArgs(EXPIREAT, 'key', 1, 'GT'), ['EXPIREAT', 'key', '1', 'GT'] ); }); diff --git a/packages/client/lib/commands/EXPIREAT.ts b/packages/client/lib/commands/EXPIREAT.ts index 9a959a87f99..5bcb94ea321 100644 --- a/packages/client/lib/commands/EXPIREAT.ts +++ b/packages/client/lib/commands/EXPIREAT.ts @@ -1,21 +1,21 @@ +import { CommandParser } from '../client/parser'; import { RedisArgument, NumberReply, Command } from '../RESP/types'; import { transformEXAT } from './generic-transformers'; export default { - FIRST_KEY_INDEX: 1, IS_READ_ONLY: true, - transformArguments( + parseCommand( + parser: CommandParser, key: RedisArgument, timestamp: number | Date, mode?: 'NX' | 'XX' | 'GT' | 'LT' ) { - const args = ['EXPIREAT', key, transformEXAT(timestamp)]; - + parser.push('EXPIREAT'); + parser.pushKey(key); + parser.push(transformEXAT(timestamp)); if (mode) { - args.push(mode); + parser.push(mode); } - - return args; }, transformReply: undefined as unknown as () => NumberReply } as const satisfies Command; diff --git a/packages/client/lib/commands/EXPIRETIME.spec.ts b/packages/client/lib/commands/EXPIRETIME.spec.ts index 3c202d2427f..f2c8d3d4521 100644 --- a/packages/client/lib/commands/EXPIRETIME.spec.ts +++ b/packages/client/lib/commands/EXPIRETIME.spec.ts @@ -1,13 +1,14 @@ import { strict as assert } from 'node:assert'; import testUtils, { GLOBAL } from '../test-utils'; import EXPIRETIME from './EXPIRETIME'; +import { parseArgs } from './generic-transformers'; describe('EXPIRETIME', () => { testUtils.isVersionGreaterThanHook([7]); it('transformArguments', () => { assert.deepEqual( - EXPIRETIME.transformArguments('key'), + parseArgs(EXPIRETIME, 'key'), ['EXPIRETIME', 'key'] ); }); diff --git a/packages/client/lib/commands/EXPIRETIME.ts b/packages/client/lib/commands/EXPIRETIME.ts index d6ac35aeb3d..2bb97fb737b 100644 --- a/packages/client/lib/commands/EXPIRETIME.ts +++ b/packages/client/lib/commands/EXPIRETIME.ts @@ -1,10 +1,11 @@ +import { CommandParser } from '../client/parser'; import { RedisArgument, NumberReply, Command } from '../RESP/types'; export default { - FIRST_KEY_INDEX: 1, IS_READ_ONLY: true, - transformArguments(key: RedisArgument) { - return ['EXPIRETIME', key]; + parseCommand(parser: CommandParser, key: RedisArgument) { + parser.push('EXPIRETIME'); + parser.pushKey(key); }, transformReply: undefined as unknown as () => NumberReply } as const satisfies Command; diff --git a/packages/client/lib/commands/FAILOVER.spec.ts b/packages/client/lib/commands/FAILOVER.spec.ts index 96602caff91..b23c3516f03 100644 --- a/packages/client/lib/commands/FAILOVER.spec.ts +++ b/packages/client/lib/commands/FAILOVER.spec.ts @@ -1,11 +1,12 @@ import { strict as assert } from 'node:assert'; import FAILOVER from './FAILOVER'; +import { parseArgs } from './generic-transformers'; describe('FAILOVER', () => { describe('transformArguments', () => { it('simple', () => { assert.deepEqual( - FAILOVER.transformArguments(), + parseArgs(FAILOVER), ['FAILOVER'] ); }); @@ -13,7 +14,7 @@ describe('FAILOVER', () => { describe('with TO', () => { it('simple', () => { assert.deepEqual( - FAILOVER.transformArguments({ + parseArgs(FAILOVER, { TO: { host: 'host', port: 6379 @@ -25,7 +26,7 @@ describe('FAILOVER', () => { it('with FORCE', () => { assert.deepEqual( - FAILOVER.transformArguments({ + parseArgs(FAILOVER, { TO: { host: 'host', port: 6379, @@ -39,7 +40,7 @@ describe('FAILOVER', () => { it('with ABORT', () => { assert.deepEqual( - FAILOVER.transformArguments({ + parseArgs(FAILOVER, { ABORT: true }), ['FAILOVER', 'ABORT'] @@ -48,7 +49,7 @@ describe('FAILOVER', () => { it('with TIMEOUT', () => { assert.deepEqual( - FAILOVER.transformArguments({ + parseArgs(FAILOVER, { TIMEOUT: 1 }), ['FAILOVER', 'TIMEOUT', '1'] @@ -57,7 +58,7 @@ describe('FAILOVER', () => { it('with TO, ABORT, TIMEOUT', () => { assert.deepEqual( - FAILOVER.transformArguments({ + parseArgs(FAILOVER, { TO: { host: 'host', port: 6379 diff --git a/packages/client/lib/commands/FAILOVER.ts b/packages/client/lib/commands/FAILOVER.ts index 0c1e710d321..1e98b983f96 100644 --- a/packages/client/lib/commands/FAILOVER.ts +++ b/packages/client/lib/commands/FAILOVER.ts @@ -1,3 +1,4 @@ +import { CommandParser } from '../client/parser'; import { SimpleStringReply, Command } from '../RESP/types'; interface FailoverOptions { @@ -11,26 +12,24 @@ interface FailoverOptions { } export default { - transformArguments(options?: FailoverOptions) { - const args = ['FAILOVER']; + parseCommand(parser: CommandParser, options?: FailoverOptions) { + parser.push('FAILOVER'); if (options?.TO) { - args.push('TO', options.TO.host, options.TO.port.toString()); + parser.push('TO', options.TO.host, options.TO.port.toString()); if (options.TO.FORCE) { - args.push('FORCE'); + parser.push('FORCE'); } } if (options?.ABORT) { - args.push('ABORT'); + parser.push('ABORT'); } if (options?.TIMEOUT) { - args.push('TIMEOUT', options.TIMEOUT.toString()); + parser.push('TIMEOUT', options.TIMEOUT.toString()); } - - return args; }, transformReply: undefined as unknown as () => SimpleStringReply } as const satisfies Command; diff --git a/packages/client/lib/commands/FCALL.spec.ts b/packages/client/lib/commands/FCALL.spec.ts index 286f2a371bf..6c3a65c1448 100644 --- a/packages/client/lib/commands/FCALL.spec.ts +++ b/packages/client/lib/commands/FCALL.spec.ts @@ -2,13 +2,14 @@ import { strict as assert } from 'node:assert'; import testUtils, { GLOBAL } from '../test-utils'; import { MATH_FUNCTION, loadMathFunction } from './FUNCTION_LOAD.spec'; import FCALL from './FCALL'; +import { parseArgs } from './generic-transformers'; describe('FCALL', () => { testUtils.isVersionGreaterThanHook([7]); it('transformArguments', () => { assert.deepEqual( - FCALL.transformArguments('function', { + parseArgs(FCALL, 'function', { keys: ['key'], arguments: ['argument'] }), diff --git a/packages/client/lib/commands/FCALL.ts b/packages/client/lib/commands/FCALL.ts index ba371a81b13..622060f693c 100644 --- a/packages/client/lib/commands/FCALL.ts +++ b/packages/client/lib/commands/FCALL.ts @@ -1,9 +1,11 @@ import { Command } from '../RESP/types'; -import EVAL, { transformEvalArguments } from './EVAL'; +import EVAL, { parseEvalArguments } from './EVAL'; export default { - FIRST_KEY_INDEX: EVAL.FIRST_KEY_INDEX, IS_READ_ONLY: false, - transformArguments: transformEvalArguments.bind(undefined, 'FCALL'), + parseCommand(...args: Parameters) { + args[0].push('FCALL'); + parseEvalArguments(...args); + }, transformReply: EVAL.transformReply } as const satisfies Command; diff --git a/packages/client/lib/commands/FCALL_RO.spec.ts b/packages/client/lib/commands/FCALL_RO.spec.ts index 57edcebebef..447e00072be 100644 --- a/packages/client/lib/commands/FCALL_RO.spec.ts +++ b/packages/client/lib/commands/FCALL_RO.spec.ts @@ -2,13 +2,14 @@ import { strict as assert } from 'node:assert'; import testUtils, { GLOBAL } from '../test-utils'; import { MATH_FUNCTION, loadMathFunction } from './FUNCTION_LOAD.spec'; import FCALL_RO from './FCALL_RO'; +import { parseArgs } from './generic-transformers'; describe('FCALL_RO', () => { testUtils.isVersionGreaterThanHook([7]); it('transformArguments', () => { assert.deepEqual( - FCALL_RO.transformArguments('function', { + parseArgs(FCALL_RO, 'function', { keys: ['key'], arguments: ['argument'] }), diff --git a/packages/client/lib/commands/FCALL_RO.ts b/packages/client/lib/commands/FCALL_RO.ts index ec002a79f82..95effb0e698 100644 --- a/packages/client/lib/commands/FCALL_RO.ts +++ b/packages/client/lib/commands/FCALL_RO.ts @@ -1,9 +1,11 @@ import { Command } from '../RESP/types'; -import EVAL, { transformEvalArguments } from './EVAL'; +import EVAL, { parseEvalArguments } from './EVAL'; export default { - FIRST_KEY_INDEX: EVAL.FIRST_KEY_INDEX, IS_READ_ONLY: false, - transformArguments: transformEvalArguments.bind(undefined, 'FCALL_RO'), + parseCommand(...args: Parameters) { + args[0].push('FCALL_RO'); + parseEvalArguments(...args); + }, transformReply: EVAL.transformReply } as const satisfies Command; diff --git a/packages/client/lib/commands/FLUSHALL.spec.ts b/packages/client/lib/commands/FLUSHALL.spec.ts index 63ad38dd7de..86daff1973a 100644 --- a/packages/client/lib/commands/FLUSHALL.spec.ts +++ b/packages/client/lib/commands/FLUSHALL.spec.ts @@ -1,26 +1,27 @@ import { strict as assert } from 'node:assert'; import testUtils, { GLOBAL } from '../test-utils'; import FLUSHALL, { REDIS_FLUSH_MODES } from './FLUSHALL'; +import { parseArgs } from './generic-transformers'; describe('FLUSHALL', () => { describe('transformArguments', () => { it('default', () => { assert.deepEqual( - FLUSHALL.transformArguments(), + parseArgs(FLUSHALL), ['FLUSHALL'] ); }); it('ASYNC', () => { assert.deepEqual( - FLUSHALL.transformArguments(REDIS_FLUSH_MODES.ASYNC), + parseArgs(FLUSHALL,REDIS_FLUSH_MODES.ASYNC), ['FLUSHALL', 'ASYNC'] ); }); it('SYNC', () => { assert.deepEqual( - FLUSHALL.transformArguments(REDIS_FLUSH_MODES.SYNC), + parseArgs(FLUSHALL, REDIS_FLUSH_MODES.SYNC), ['FLUSHALL', 'SYNC'] ); }); diff --git a/packages/client/lib/commands/FLUSHALL.ts b/packages/client/lib/commands/FLUSHALL.ts index 5e6484a991e..c39535e8864 100644 --- a/packages/client/lib/commands/FLUSHALL.ts +++ b/packages/client/lib/commands/FLUSHALL.ts @@ -1,3 +1,4 @@ +import { CommandParser } from '../client/parser'; import { SimpleStringReply, Command } from '../RESP/types'; export const REDIS_FLUSH_MODES = { @@ -8,16 +9,13 @@ export const REDIS_FLUSH_MODES = { export type RedisFlushMode = typeof REDIS_FLUSH_MODES[keyof typeof REDIS_FLUSH_MODES]; export default { - FIRST_KEY_INDEX: undefined, + NOT_KEYED_COMMAND: true, IS_READ_ONLY: false, - transformArguments(mode?: RedisFlushMode) { - const args = ['FLUSHALL']; - + parseCommand(parser: CommandParser, mode?: RedisFlushMode) { + parser.push('FLUSHALL'); if (mode) { - args.push(mode); + parser.push(mode); } - - return args; }, transformReply: undefined as unknown as () => SimpleStringReply } as const satisfies Command; diff --git a/packages/client/lib/commands/FLUSHDB.spec.ts b/packages/client/lib/commands/FLUSHDB.spec.ts index ad09ecfc945..795df637cb4 100644 --- a/packages/client/lib/commands/FLUSHDB.spec.ts +++ b/packages/client/lib/commands/FLUSHDB.spec.ts @@ -2,26 +2,27 @@ import { strict as assert } from 'node:assert'; import testUtils, { GLOBAL } from '../test-utils'; import FLUSHDB from './FLUSHDB'; import { REDIS_FLUSH_MODES } from './FLUSHALL'; +import { parseArgs } from './generic-transformers'; describe('FLUSHDB', () => { describe('transformArguments', () => { it('default', () => { assert.deepEqual( - FLUSHDB.transformArguments(), + parseArgs(FLUSHDB), ['FLUSHDB'] ); }); it('ASYNC', () => { assert.deepEqual( - FLUSHDB.transformArguments(REDIS_FLUSH_MODES.ASYNC), + parseArgs(FLUSHDB, REDIS_FLUSH_MODES.ASYNC), ['FLUSHDB', 'ASYNC'] ); }); it('SYNC', () => { assert.deepEqual( - FLUSHDB.transformArguments(REDIS_FLUSH_MODES.SYNC), + parseArgs(FLUSHDB, REDIS_FLUSH_MODES.SYNC), ['FLUSHDB', 'SYNC'] ); }); diff --git a/packages/client/lib/commands/FLUSHDB.ts b/packages/client/lib/commands/FLUSHDB.ts index 75c7a66f190..5639f69a611 100644 --- a/packages/client/lib/commands/FLUSHDB.ts +++ b/packages/client/lib/commands/FLUSHDB.ts @@ -1,17 +1,15 @@ +import { CommandParser } from '../client/parser'; import { SimpleStringReply, Command } from '../RESP/types'; import { RedisFlushMode } from './FLUSHALL'; export default { - FIRST_KEY_INDEX: undefined, + NOT_KEYED_COMMAND: true, IS_READ_ONLY: false, - transformArguments(mode?: RedisFlushMode) { - const args = ['FLUSHDB']; - + parseCommand(parser: CommandParser, mode?: RedisFlushMode) { + parser.push('FLUSHDB'); if (mode) { - args.push(mode); + parser.push(mode); } - - return args; }, transformReply: undefined as unknown as () => SimpleStringReply } as const satisfies Command; diff --git a/packages/client/lib/commands/FUNCTION_DELETE.spec.ts b/packages/client/lib/commands/FUNCTION_DELETE.spec.ts index 1172e84b956..b33ea25916b 100644 --- a/packages/client/lib/commands/FUNCTION_DELETE.spec.ts +++ b/packages/client/lib/commands/FUNCTION_DELETE.spec.ts @@ -2,13 +2,14 @@ import { strict as assert } from 'node:assert'; import testUtils, { GLOBAL } from '../test-utils'; import FUNCTION_DELETE from './FUNCTION_DELETE'; import { MATH_FUNCTION, loadMathFunction } from './FUNCTION_LOAD.spec'; +import { parseArgs } from './generic-transformers'; describe('FUNCTION DELETE', () => { testUtils.isVersionGreaterThanHook([7]); it('transformArguments', () => { assert.deepEqual( - FUNCTION_DELETE.transformArguments('library'), + parseArgs(FUNCTION_DELETE, 'library'), ['FUNCTION', 'DELETE', 'library'] ); }); diff --git a/packages/client/lib/commands/FUNCTION_DELETE.ts b/packages/client/lib/commands/FUNCTION_DELETE.ts index 1061cded17c..dbfb044928e 100644 --- a/packages/client/lib/commands/FUNCTION_DELETE.ts +++ b/packages/client/lib/commands/FUNCTION_DELETE.ts @@ -1,10 +1,11 @@ +import { CommandParser } from '../client/parser'; import { RedisArgument, SimpleStringReply, Command } from '../RESP/types'; export default { - FIRST_KEY_INDEX: undefined, + NOT_KEYED_COMMAND: true, IS_READ_ONLY: false, - transformArguments(library: RedisArgument) { - return ['FUNCTION', 'DELETE', library]; + parseCommand(parser: CommandParser, library: RedisArgument) { + parser.push('FUNCTION', 'DELETE', library); }, transformReply: undefined as unknown as () => SimpleStringReply } as const satisfies Command; diff --git a/packages/client/lib/commands/FUNCTION_DUMP.spec.ts b/packages/client/lib/commands/FUNCTION_DUMP.spec.ts index 4d4e885e4f2..bbd6302bb6a 100644 --- a/packages/client/lib/commands/FUNCTION_DUMP.spec.ts +++ b/packages/client/lib/commands/FUNCTION_DUMP.spec.ts @@ -1,13 +1,14 @@ import { strict as assert } from 'node:assert'; import testUtils, { GLOBAL } from '../test-utils'; import FUNCTION_DUMP from './FUNCTION_DUMP'; +import { parseArgs } from './generic-transformers'; describe('FUNCTION DUMP', () => { testUtils.isVersionGreaterThanHook([7]); it('transformArguments', () => { assert.deepEqual( - FUNCTION_DUMP.transformArguments(), + parseArgs(FUNCTION_DUMP), ['FUNCTION', 'DUMP'] ); }); diff --git a/packages/client/lib/commands/FUNCTION_DUMP.ts b/packages/client/lib/commands/FUNCTION_DUMP.ts index 8f6ff047fa7..2d0dbdd4455 100644 --- a/packages/client/lib/commands/FUNCTION_DUMP.ts +++ b/packages/client/lib/commands/FUNCTION_DUMP.ts @@ -1,10 +1,11 @@ +import { CommandParser } from '../client/parser'; import { BlobStringReply, Command } from '../RESP/types'; export default { - FIRST_KEY_INDEX: undefined, + NOT_KEYED_COMMAND: true, IS_READ_ONLY: true, - transformArguments() { - return ['FUNCTION', 'DUMP']; + parseCommand(parser: CommandParser) { + parser.push('FUNCTION', 'DUMP') }, transformReply: undefined as unknown as () => BlobStringReply } as const satisfies Command; diff --git a/packages/client/lib/commands/FUNCTION_FLUSH.spec.ts b/packages/client/lib/commands/FUNCTION_FLUSH.spec.ts index 5601784ed6a..4fe90bdb607 100644 --- a/packages/client/lib/commands/FUNCTION_FLUSH.spec.ts +++ b/packages/client/lib/commands/FUNCTION_FLUSH.spec.ts @@ -1,6 +1,7 @@ import { strict as assert } from 'node:assert'; import testUtils, { GLOBAL } from '../test-utils'; import FUNCTION_FLUSH from './FUNCTION_FLUSH'; +import { parseArgs } from './generic-transformers'; describe('FUNCTION FLUSH', () => { testUtils.isVersionGreaterThanHook([7]); @@ -8,14 +9,14 @@ describe('FUNCTION FLUSH', () => { describe('transformArguments', () => { it('simple', () => { assert.deepEqual( - FUNCTION_FLUSH.transformArguments(), + parseArgs(FUNCTION_FLUSH), ['FUNCTION', 'FLUSH'] ); }); it('with mode', () => { assert.deepEqual( - FUNCTION_FLUSH.transformArguments('SYNC'), + parseArgs(FUNCTION_FLUSH, 'SYNC'), ['FUNCTION', 'FLUSH', 'SYNC'] ); }); diff --git a/packages/client/lib/commands/FUNCTION_FLUSH.ts b/packages/client/lib/commands/FUNCTION_FLUSH.ts index 844d3586d90..4ca59e4464e 100644 --- a/packages/client/lib/commands/FUNCTION_FLUSH.ts +++ b/packages/client/lib/commands/FUNCTION_FLUSH.ts @@ -1,17 +1,16 @@ +import { CommandParser } from '../client/parser'; import { SimpleStringReply, Command } from '../RESP/types'; import { RedisFlushMode } from './FLUSHALL'; export default { - FIRST_KEY_INDEX: undefined, + NOT_KEYED_COMMAND: true, IS_READ_ONLY: false, - transformArguments(mode?: RedisFlushMode) { - const args = ['FUNCTION', 'FLUSH']; - + parseCommand(parser: CommandParser, mode?: RedisFlushMode) { + parser.push('FUNCTION', 'FLUSH'); + if (mode) { - args.push(mode); + parser.push(mode); } - - return args; }, transformReply: undefined as unknown as () => SimpleStringReply<'OK'> } as const satisfies Command; diff --git a/packages/client/lib/commands/FUNCTION_KILL.spec.ts b/packages/client/lib/commands/FUNCTION_KILL.spec.ts index be231e41180..c4dbd124d30 100644 --- a/packages/client/lib/commands/FUNCTION_KILL.spec.ts +++ b/packages/client/lib/commands/FUNCTION_KILL.spec.ts @@ -1,13 +1,14 @@ import { strict as assert } from 'node:assert'; import testUtils from '../test-utils'; import FUNCTION_KILL from './FUNCTION_KILL'; +import { parseArgs } from './generic-transformers'; describe('FUNCTION KILL', () => { testUtils.isVersionGreaterThanHook([7]); it('transformArguments', () => { assert.deepEqual( - FUNCTION_KILL.transformArguments(), + parseArgs(FUNCTION_KILL), ['FUNCTION', 'KILL'] ); }); diff --git a/packages/client/lib/commands/FUNCTION_KILL.ts b/packages/client/lib/commands/FUNCTION_KILL.ts index f452b0b80d9..8b5351e93ab 100644 --- a/packages/client/lib/commands/FUNCTION_KILL.ts +++ b/packages/client/lib/commands/FUNCTION_KILL.ts @@ -1,10 +1,11 @@ +import { CommandParser } from '../client/parser'; import { SimpleStringReply, Command } from '../RESP/types'; export default { - FIRST_KEY_INDEX: undefined, + NOT_KEYED_COMMAND: true, IS_READ_ONLY: true, - transformArguments() { - return ['FUNCTION', 'KILL']; + parseCommand(parser: CommandParser) { + parser.push('FUNCTION', 'KILL'); }, transformReply: undefined as unknown as () => SimpleStringReply } as const satisfies Command; diff --git a/packages/client/lib/commands/FUNCTION_LIST.spec.ts b/packages/client/lib/commands/FUNCTION_LIST.spec.ts index e269d3150b6..6d9b28acf90 100644 --- a/packages/client/lib/commands/FUNCTION_LIST.spec.ts +++ b/packages/client/lib/commands/FUNCTION_LIST.spec.ts @@ -2,6 +2,7 @@ import { strict as assert } from 'node:assert'; import testUtils, { GLOBAL } from '../test-utils'; import FUNCTION_LIST from './FUNCTION_LIST'; import { MATH_FUNCTION, loadMathFunction } from './FUNCTION_LOAD.spec'; +import { parseArgs } from './generic-transformers'; describe('FUNCTION LIST', () => { testUtils.isVersionGreaterThanHook([7]); @@ -9,14 +10,14 @@ describe('FUNCTION LIST', () => { describe('transformArguments', () => { it('simple', () => { assert.deepEqual( - FUNCTION_LIST.transformArguments(), + parseArgs(FUNCTION_LIST), ['FUNCTION', 'LIST'] ); }); it('with LIBRARYNAME', () => { assert.deepEqual( - FUNCTION_LIST.transformArguments({ + parseArgs(FUNCTION_LIST, { LIBRARYNAME: 'patter*' }), ['FUNCTION', 'LIST', 'LIBRARYNAME', 'patter*'] diff --git a/packages/client/lib/commands/FUNCTION_LIST.ts b/packages/client/lib/commands/FUNCTION_LIST.ts index 07c1ff2a000..82e3697eadc 100644 --- a/packages/client/lib/commands/FUNCTION_LIST.ts +++ b/packages/client/lib/commands/FUNCTION_LIST.ts @@ -1,4 +1,5 @@ -import { RedisArgument, TuplesToMapReply, BlobStringReply, ArrayReply, NullReply, SetReply, UnwrapReply, Resp2Reply, CommandArguments, Command } from '../RESP/types'; +import { CommandParser } from '../client/parser'; +import { RedisArgument, TuplesToMapReply, BlobStringReply, ArrayReply, NullReply, SetReply, UnwrapReply, Resp2Reply, Command } from '../RESP/types'; export interface FunctionListOptions { LIBRARYNAME?: RedisArgument; @@ -17,16 +18,14 @@ export type FunctionListReplyItem = [ export type FunctionListReply = ArrayReply>; export default { - FIRST_KEY_INDEX: undefined, + NOT_KEYED_COMMAND: true, IS_READ_ONLY: false, - transformArguments(options?: FunctionListOptions) { - const args: CommandArguments = ['FUNCTION', 'LIST']; + parseCommand(parser: CommandParser, options?: FunctionListOptions) { + parser.push('FUNCTION', 'LIST'); if (options?.LIBRARYNAME) { - args.push('LIBRARYNAME', options.LIBRARYNAME); + parser.push('LIBRARYNAME', options.LIBRARYNAME); } - - return args; }, transformReply: { 2: (reply: UnwrapReply>) => { diff --git a/packages/client/lib/commands/FUNCTION_LIST_WITHCODE.spec.ts b/packages/client/lib/commands/FUNCTION_LIST_WITHCODE.spec.ts index 8ff40582460..f44db9ba037 100644 --- a/packages/client/lib/commands/FUNCTION_LIST_WITHCODE.spec.ts +++ b/packages/client/lib/commands/FUNCTION_LIST_WITHCODE.spec.ts @@ -2,6 +2,7 @@ import { strict as assert } from 'node:assert'; import testUtils, { GLOBAL } from '../test-utils'; import FUNCTION_LIST_WITHCODE from './FUNCTION_LIST_WITHCODE'; import { MATH_FUNCTION, loadMathFunction } from './FUNCTION_LOAD.spec'; +import { parseArgs } from './generic-transformers'; describe('FUNCTION LIST WITHCODE', () => { testUtils.isVersionGreaterThanHook([7]); @@ -9,14 +10,14 @@ describe('FUNCTION LIST WITHCODE', () => { describe('transformArguments', () => { it('simple', () => { assert.deepEqual( - FUNCTION_LIST_WITHCODE.transformArguments(), + parseArgs(FUNCTION_LIST_WITHCODE), ['FUNCTION', 'LIST', 'WITHCODE'] ); }); it('with LIBRARYNAME', () => { assert.deepEqual( - FUNCTION_LIST_WITHCODE.transformArguments({ + parseArgs(FUNCTION_LIST_WITHCODE, { LIBRARYNAME: 'patter*' }), ['FUNCTION', 'LIST', 'LIBRARYNAME', 'patter*', 'WITHCODE'] diff --git a/packages/client/lib/commands/FUNCTION_LIST_WITHCODE.ts b/packages/client/lib/commands/FUNCTION_LIST_WITHCODE.ts index 47a02a3da8a..208bc5fd303 100644 --- a/packages/client/lib/commands/FUNCTION_LIST_WITHCODE.ts +++ b/packages/client/lib/commands/FUNCTION_LIST_WITHCODE.ts @@ -7,12 +7,11 @@ export type FunctionListWithCodeReply = ArrayReply>; export default { - FIRST_KEY_INDEX: FUNCTION_LIST.FIRST_KEY_INDEX, + NOT_KEYED_COMMAND: FUNCTION_LIST.NOT_KEYED_COMMAND, IS_READ_ONLY: FUNCTION_LIST.IS_READ_ONLY, - transformArguments(...args: Parameters) { - const redisArgs = FUNCTION_LIST.transformArguments(...args); - redisArgs.push('WITHCODE'); - return redisArgs; + parseCommand(...args: Parameters) { + FUNCTION_LIST.parseCommand(...args); + args[0].push('WITHCODE'); }, transformReply: { 2: (reply: UnwrapReply>) => { diff --git a/packages/client/lib/commands/FUNCTION_LOAD.spec.ts b/packages/client/lib/commands/FUNCTION_LOAD.spec.ts index fe896bdf8c8..c0a511bffc9 100644 --- a/packages/client/lib/commands/FUNCTION_LOAD.spec.ts +++ b/packages/client/lib/commands/FUNCTION_LOAD.spec.ts @@ -3,6 +3,8 @@ import testUtils, { GLOBAL } from '../test-utils'; import FUNCTION_LOAD from './FUNCTION_LOAD'; import { RedisClientType } from '../client'; import { NumberReply, RedisFunctions, RedisModules, RedisScripts, RespVersions } from '../RESP/types'; +import { parseArgs } from './generic-transformers'; +import { CommandParser } from '../client/parser'; @@ -25,8 +27,8 @@ export const MATH_FUNCTION = { IS_READ_ONLY: true, NUMBER_OF_KEYS: 1, FIRST_KEY_INDEX: 0, - transformArguments(key: string) { - return [key]; + parseCommand(parser: CommandParser, key: string) { + parser.pushKey(key); }, transformReply: undefined as unknown as () => NumberReply } @@ -53,14 +55,14 @@ describe('FUNCTION LOAD', () => { describe('transformArguments', () => { it('simple', () => { assert.deepEqual( - FUNCTION_LOAD.transformArguments('code'), + parseArgs(FUNCTION_LOAD, 'code'), ['FUNCTION', 'LOAD', 'code'] ); }); it('with REPLACE', () => { assert.deepEqual( - FUNCTION_LOAD.transformArguments('code', { + parseArgs(FUNCTION_LOAD, 'code', { REPLACE: true }), ['FUNCTION', 'LOAD', 'REPLACE', 'code'] diff --git a/packages/client/lib/commands/FUNCTION_LOAD.ts b/packages/client/lib/commands/FUNCTION_LOAD.ts index 32b030909ad..40b8ea8c0f4 100644 --- a/packages/client/lib/commands/FUNCTION_LOAD.ts +++ b/packages/client/lib/commands/FUNCTION_LOAD.ts @@ -1,22 +1,21 @@ -import { RedisArgument, CommandArguments, BlobStringReply, Command } from '../RESP/types'; +import { CommandParser } from '../client/parser'; +import { RedisArgument, BlobStringReply, Command } from '../RESP/types'; export interface FunctionLoadOptions { REPLACE?: boolean; } export default { - FIRST_KEY_INDEX: undefined, + NOT_KEYED_COMMAND: true, IS_READ_ONLY: false, - transformArguments(code: RedisArgument, options?: FunctionLoadOptions) { - const args: CommandArguments = ['FUNCTION', 'LOAD']; + parseCommand(parser: CommandParser, code: RedisArgument, options?: FunctionLoadOptions) { + parser.push('FUNCTION', 'LOAD'); if (options?.REPLACE) { - args.push('REPLACE'); + parser.push('REPLACE'); } - args.push(code); - - return args; + parser.push(code); }, transformReply: undefined as unknown as () => BlobStringReply } as const satisfies Command; diff --git a/packages/client/lib/commands/FUNCTION_RESTORE.spec.ts b/packages/client/lib/commands/FUNCTION_RESTORE.spec.ts index 465e99b6104..72d7d1d6204 100644 --- a/packages/client/lib/commands/FUNCTION_RESTORE.spec.ts +++ b/packages/client/lib/commands/FUNCTION_RESTORE.spec.ts @@ -2,6 +2,7 @@ import { strict as assert } from 'node:assert'; import testUtils, { GLOBAL } from '../test-utils'; import FUNCTION_RESTORE from './FUNCTION_RESTORE'; import { RESP_TYPES } from '../RESP/decoder'; +import { parseArgs } from './generic-transformers'; describe('FUNCTION RESTORE', () => { testUtils.isVersionGreaterThanHook([7]); @@ -9,14 +10,14 @@ describe('FUNCTION RESTORE', () => { describe('transformArguments', () => { it('simple', () => { assert.deepEqual( - FUNCTION_RESTORE.transformArguments('dump'), + parseArgs(FUNCTION_RESTORE, 'dump'), ['FUNCTION', 'RESTORE', 'dump'] ); }); it('with mode', () => { assert.deepEqual( - FUNCTION_RESTORE.transformArguments('dump', { + parseArgs(FUNCTION_RESTORE, 'dump', { mode: 'APPEND' }), ['FUNCTION', 'RESTORE', 'dump', 'APPEND'] diff --git a/packages/client/lib/commands/FUNCTION_RESTORE.ts b/packages/client/lib/commands/FUNCTION_RESTORE.ts index 8c875530562..944813f25e5 100644 --- a/packages/client/lib/commands/FUNCTION_RESTORE.ts +++ b/packages/client/lib/commands/FUNCTION_RESTORE.ts @@ -1,3 +1,4 @@ +import { CommandParser } from '../client/parser'; import { SimpleStringReply, Command, RedisArgument } from '../RESP/types'; export interface FunctionRestoreOptions { @@ -5,16 +6,14 @@ export interface FunctionRestoreOptions { } export default { - FIRST_KEY_INDEX: undefined, + NOT_KEYED_COMMAND: true, IS_READ_ONLY: false, - transformArguments(dump: RedisArgument, options?: FunctionRestoreOptions) { - const args = ['FUNCTION', 'RESTORE', dump]; + parseCommand(parser: CommandParser, dump: RedisArgument, options?: FunctionRestoreOptions) { + parser.push('FUNCTION', 'RESTORE', dump); if (options?.mode) { - args.push(options.mode); + parser.push(options.mode); } - - return args; }, transformReply: undefined as unknown as () => SimpleStringReply<'OK'> } as const satisfies Command; diff --git a/packages/client/lib/commands/FUNCTION_STATS.spec.ts b/packages/client/lib/commands/FUNCTION_STATS.spec.ts index b48b012614f..a3c5e00fe72 100644 --- a/packages/client/lib/commands/FUNCTION_STATS.spec.ts +++ b/packages/client/lib/commands/FUNCTION_STATS.spec.ts @@ -1,13 +1,14 @@ import { strict as assert } from 'node:assert'; import testUtils, { GLOBAL } from '../test-utils'; import FUNCTION_STATS from './FUNCTION_STATS'; +import { parseArgs } from './generic-transformers'; describe('FUNCTION STATS', () => { testUtils.isVersionGreaterThanHook([7]); it('transformArguments', () => { assert.deepEqual( - FUNCTION_STATS.transformArguments(), + parseArgs(FUNCTION_STATS), ['FUNCTION', 'STATS'] ); }); diff --git a/packages/client/lib/commands/FUNCTION_STATS.ts b/packages/client/lib/commands/FUNCTION_STATS.ts index 138d1fb82d5..908be5476e0 100644 --- a/packages/client/lib/commands/FUNCTION_STATS.ts +++ b/packages/client/lib/commands/FUNCTION_STATS.ts @@ -1,3 +1,4 @@ +import { CommandParser } from '../client/parser'; import { Command, TuplesToMapReply, BlobStringReply, NullReply, NumberReply, MapReply, Resp2Reply, UnwrapReply } from '../RESP/types'; import { isNullReply } from './generic-transformers'; @@ -20,10 +21,10 @@ type FunctionStatsReply = TuplesToMapReply<[ ]>; export default { + NOT_KEYED_COMMAND: true, IS_READ_ONLY: true, - FIRST_KEY_INDEX: undefined, - transformArguments() { - return ['FUNCTION', 'STATS']; + parseCommand(parser: CommandParser) { + parser.push('FUNCTION', 'STATS'); }, transformReply: { 2: (reply: UnwrapReply>) => { diff --git a/packages/client/lib/commands/GEOADD.spec.ts b/packages/client/lib/commands/GEOADD.spec.ts index 14195ed289c..d947141a318 100644 --- a/packages/client/lib/commands/GEOADD.spec.ts +++ b/packages/client/lib/commands/GEOADD.spec.ts @@ -1,12 +1,13 @@ import { strict as assert } from 'node:assert'; import testUtils, { GLOBAL } from '../test-utils'; import GEOADD from './GEOADD'; +import { parseArgs } from './generic-transformers'; describe('GEOADD', () => { describe('transformArguments', () => { it('one member', () => { assert.deepEqual( - GEOADD.transformArguments('key', { + parseArgs(GEOADD, 'key', { member: 'member', longitude: 1, latitude: 2 @@ -17,7 +18,7 @@ describe('GEOADD', () => { it('multiple members', () => { assert.deepEqual( - GEOADD.transformArguments('key', [{ + parseArgs(GEOADD, 'key', [{ longitude: 1, latitude: 2, member: '3', @@ -32,7 +33,7 @@ describe('GEOADD', () => { it('with condition', () => { assert.deepEqual( - GEOADD.transformArguments('key', { + parseArgs(GEOADD, 'key', { longitude: 1, latitude: 2, member: 'member' @@ -45,7 +46,7 @@ describe('GEOADD', () => { it('with NX (backwards compatibility)', () => { assert.deepEqual( - GEOADD.transformArguments('key', { + parseArgs(GEOADD, 'key', { longitude: 1, latitude: 2, member: 'member' @@ -58,7 +59,7 @@ describe('GEOADD', () => { it('with CH', () => { assert.deepEqual( - GEOADD.transformArguments('key', { + parseArgs(GEOADD, 'key', { longitude: 1, latitude: 2, member: 'member' @@ -71,7 +72,7 @@ describe('GEOADD', () => { it('with condition, CH', () => { assert.deepEqual( - GEOADD.transformArguments('key', { + parseArgs(GEOADD, 'key', { longitude: 1, latitude: 2, member: 'member' diff --git a/packages/client/lib/commands/GEOADD.ts b/packages/client/lib/commands/GEOADD.ts index f89f6b80e82..31bf457e158 100644 --- a/packages/client/lib/commands/GEOADD.ts +++ b/packages/client/lib/commands/GEOADD.ts @@ -1,4 +1,5 @@ -import { RedisArgument, CommandArguments, NumberReply, Command } from '../RESP/types'; +import { CommandParser } from '../client/parser'; +import { RedisArgument, NumberReply, Command } from '../RESP/types'; import { GeoCoordinates } from './GEOSEARCH'; export interface GeoMember extends GeoCoordinates { @@ -19,45 +20,45 @@ export interface GeoAddOptions { } export default { - FIRST_KEY_INDEX: 1, IS_READ_ONLY: false, - transformArguments( + parseCommand( + parser: CommandParser, key: RedisArgument, toAdd: GeoMember | Array, options?: GeoAddOptions ) { - const args = ['GEOADD', key]; + parser.push('GEOADD') + parser.pushKey(key); if (options?.condition) { - args.push(options.condition); + parser.push(options.condition); } else if (options?.NX) { - args.push('NX'); + parser.push('NX'); } else if (options?.XX) { - args.push('XX'); + parser.push('XX'); } if (options?.CH) { - args.push('CH'); + parser.push('CH'); } if (Array.isArray(toAdd)) { for (const member of toAdd) { - pushMember(args, member); + pushMember(parser, member); } } else { - pushMember(args, toAdd); + pushMember(parser, toAdd); } - return args; }, transformReply: undefined as unknown as () => NumberReply } as const satisfies Command; function pushMember( - args: CommandArguments, + parser: CommandParser, { longitude, latitude, member }: GeoMember ) { - args.push( + parser.push( longitude.toString(), latitude.toString(), member diff --git a/packages/client/lib/commands/GEODIST.spec.ts b/packages/client/lib/commands/GEODIST.spec.ts index eb5a1ef801e..a23df405d1d 100644 --- a/packages/client/lib/commands/GEODIST.spec.ts +++ b/packages/client/lib/commands/GEODIST.spec.ts @@ -1,19 +1,20 @@ import { strict as assert } from 'node:assert'; import testUtils, { GLOBAL } from '../test-utils'; import GEODIST from './GEODIST'; +import { parseArgs } from './generic-transformers'; describe('GEODIST', () => { describe('transformArguments', () => { it('simple', () => { assert.deepEqual( - GEODIST.transformArguments('key', '1', '2'), + parseArgs(GEODIST, 'key', '1', '2'), ['GEODIST', 'key', '1', '2'] ); }); it('with unit', () => { assert.deepEqual( - GEODIST.transformArguments('key', '1', '2', 'm'), + parseArgs(GEODIST, 'key', '1', '2', 'm'), ['GEODIST', 'key', '1', '2', 'm'] ); }); diff --git a/packages/client/lib/commands/GEODIST.ts b/packages/client/lib/commands/GEODIST.ts index 3e684d67579..ba4d3080a71 100644 --- a/packages/client/lib/commands/GEODIST.ts +++ b/packages/client/lib/commands/GEODIST.ts @@ -1,22 +1,23 @@ +import { CommandParser } from '../client/parser'; import { RedisArgument, BlobStringReply, NullReply, Command } from '../RESP/types'; import { GeoUnits } from './GEOSEARCH'; export default { - FIRST_KEY_INDEX: 1, + CACHEABLE: true, IS_READ_ONLY: true, - transformArguments( + parseCommand(parser: CommandParser, key: RedisArgument, member1: RedisArgument, member2: RedisArgument, unit?: GeoUnits ) { - const args = ['GEODIST', key, member1, member2]; + parser.push('GEODIST'); + parser.pushKey(key); + parser.push(member1, member2); if (unit) { - args.push(unit); + parser.push(unit); } - - return args; }, transformReply(reply: BlobStringReply | NullReply) { return reply === null ? null : Number(reply); diff --git a/packages/client/lib/commands/GEOHASH.spec.ts b/packages/client/lib/commands/GEOHASH.spec.ts index 8efe55d89b6..ad26dff8434 100644 --- a/packages/client/lib/commands/GEOHASH.spec.ts +++ b/packages/client/lib/commands/GEOHASH.spec.ts @@ -1,19 +1,20 @@ import { strict as assert } from 'node:assert'; import testUtils, { GLOBAL } from '../test-utils'; import GEOHASH from './GEOHASH'; +import { parseArgs } from './generic-transformers'; describe('GEOHASH', () => { describe('transformArguments', () => { it('single member', () => { assert.deepEqual( - GEOHASH.transformArguments('key', 'member'), + parseArgs(GEOHASH, 'key', 'member'), ['GEOHASH', 'key', 'member'] ); }); it('multiple members', () => { assert.deepEqual( - GEOHASH.transformArguments('key', ['1', '2']), + parseArgs(GEOHASH, 'key', ['1', '2']), ['GEOHASH', 'key', '1', '2'] ); }); diff --git a/packages/client/lib/commands/GEOHASH.ts b/packages/client/lib/commands/GEOHASH.ts index d8d2732e512..c3265d13157 100644 --- a/packages/client/lib/commands/GEOHASH.ts +++ b/packages/client/lib/commands/GEOHASH.ts @@ -1,14 +1,14 @@ +import { CommandParser } from '../client/parser'; import { RedisArgument, ArrayReply, BlobStringReply, Command } from '../RESP/types'; -import { RedisVariadicArgument, pushVariadicArguments } from './generic-transformers'; +import { RedisVariadicArgument } from './generic-transformers'; export default { - FIRST_KEY_INDEX: 1, + CACHEABLE: true, IS_READ_ONLY: true, - transformArguments( - key: RedisArgument, - member: RedisVariadicArgument - ) { - return pushVariadicArguments(['GEOHASH', key], member); + parseCommand(parser: CommandParser, key: RedisArgument, member: RedisVariadicArgument) { + parser.push('GEOHASH'); + parser.pushKey(key); + parser.pushVariadic(member); }, transformReply: undefined as unknown as () => ArrayReply } as const satisfies Command; diff --git a/packages/client/lib/commands/GEOPOS.spec.ts b/packages/client/lib/commands/GEOPOS.spec.ts index 20ad5c5c942..247dd91d222 100644 --- a/packages/client/lib/commands/GEOPOS.spec.ts +++ b/packages/client/lib/commands/GEOPOS.spec.ts @@ -1,19 +1,20 @@ import { strict as assert } from 'node:assert'; import testUtils, { GLOBAL } from '../test-utils'; import GEOPOS from './GEOPOS'; +import { parseArgs } from './generic-transformers'; describe('GEOPOS', () => { describe('transformArguments', () => { it('single member', () => { assert.deepEqual( - GEOPOS.transformArguments('key', 'member'), + parseArgs(GEOPOS, 'key', 'member'), ['GEOPOS', 'key', 'member'] ); }); it('multiple members', () => { assert.deepEqual( - GEOPOS.transformArguments('key', ['1', '2']), + parseArgs(GEOPOS, 'key', ['1', '2']), ['GEOPOS', 'key', '1', '2'] ); }); diff --git a/packages/client/lib/commands/GEOPOS.ts b/packages/client/lib/commands/GEOPOS.ts index 30273c64c18..6bdbb65ac46 100644 --- a/packages/client/lib/commands/GEOPOS.ts +++ b/packages/client/lib/commands/GEOPOS.ts @@ -1,14 +1,14 @@ +import { CommandParser } from '../client/parser'; import { RedisArgument, ArrayReply, TuplesReply, BlobStringReply, NullReply, UnwrapReply, Command } from '../RESP/types'; -import { RedisVariadicArgument, pushVariadicArguments } from './generic-transformers'; +import { RedisVariadicArgument } from './generic-transformers'; export default { - FIRST_KEY_INDEX: 1, + CACHEABLE: true, IS_READ_ONLY: true, - transformArguments( - key: RedisArgument, - member: RedisVariadicArgument - ) { - return pushVariadicArguments(['GEOPOS', key], member); + parseCommand(parser: CommandParser, key: RedisArgument, member: RedisVariadicArgument) { + parser.push('GEOPOS'); + parser.pushKey(key); + parser.pushVariadic(member); }, transformReply(reply: UnwrapReply | NullReply>>) { return reply.map(item => { diff --git a/packages/client/lib/commands/GEORADIUS.spec.ts b/packages/client/lib/commands/GEORADIUS.spec.ts index 533e48f6898..3c33395c5f6 100644 --- a/packages/client/lib/commands/GEORADIUS.spec.ts +++ b/packages/client/lib/commands/GEORADIUS.spec.ts @@ -1,11 +1,12 @@ import { strict as assert } from 'node:assert'; import testUtils, { GLOBAL } from '../test-utils'; import GEORADIUS from './GEORADIUS'; +import { parseArgs } from './generic-transformers'; describe('GEORADIUS', () => { it('transformArguments', () => { assert.deepEqual( - GEORADIUS.transformArguments('key', { + parseArgs(GEORADIUS, 'key', { longitude: 1, latitude: 2 }, 3, 'm'), diff --git a/packages/client/lib/commands/GEORADIUS.ts b/packages/client/lib/commands/GEORADIUS.ts index e777432f090..5e8d880ab5e 100644 --- a/packages/client/lib/commands/GEORADIUS.ts +++ b/packages/client/lib/commands/GEORADIUS.ts @@ -1,32 +1,27 @@ +import { CommandParser } from '../client/parser'; import { RedisArgument, ArrayReply, BlobStringReply, Command } from '../RESP/types'; -import { GeoCoordinates, GeoUnits, GeoSearchOptions, pushGeoSearchOptions } from './GEOSEARCH'; +import { GeoCoordinates, GeoUnits, GeoSearchOptions, parseGeoSearchOptions } from './GEOSEARCH'; -export function transformGeoRadiusArguments( - command: RedisArgument, +export function parseGeoRadiusArguments( + parser: CommandParser, key: RedisArgument, from: GeoCoordinates, radius: number, unit: GeoUnits, options?: GeoSearchOptions ) { - const args = [ - command, - key, - from.longitude.toString(), - from.latitude.toString(), - radius.toString(), - unit - ]; + parser.pushKey(key); + parser.push(from.longitude.toString(), from.latitude.toString(), radius.toString(), unit); - pushGeoSearchOptions(args, options); - - return args; + parseGeoSearchOptions(parser, options) } export default { - FIRST_KEY_INDEX: 1, IS_READ_ONLY: false, - transformArguments: transformGeoRadiusArguments.bind(undefined, 'GEORADIUS'), + parseCommand(...args: Parameters) { + args[0].push('GEORADIUS'); + return parseGeoRadiusArguments(...args); + }, transformReply: undefined as unknown as () => ArrayReply } as const satisfies Command; diff --git a/packages/client/lib/commands/GEORADIUSBYMEMBER.spec.ts b/packages/client/lib/commands/GEORADIUSBYMEMBER.spec.ts index 57349a79acb..c81c3d75815 100644 --- a/packages/client/lib/commands/GEORADIUSBYMEMBER.spec.ts +++ b/packages/client/lib/commands/GEORADIUSBYMEMBER.spec.ts @@ -1,11 +1,12 @@ import { strict as assert } from 'node:assert'; import testUtils, { GLOBAL } from '../test-utils'; import GEORADIUSBYMEMBER from './GEORADIUSBYMEMBER'; +import { parseArgs } from './generic-transformers'; describe('GEORADIUSBYMEMBER', () => { it('transformArguments', () => { assert.deepEqual( - GEORADIUSBYMEMBER.transformArguments('key', 'member', 3, 'm'), + parseArgs(GEORADIUSBYMEMBER, 'key', 'member', 3, 'm'), ['GEORADIUSBYMEMBER', 'key', 'member', '3', 'm'] ); }); diff --git a/packages/client/lib/commands/GEORADIUSBYMEMBER.ts b/packages/client/lib/commands/GEORADIUSBYMEMBER.ts index 13b52a30630..be4ca54650c 100644 --- a/packages/client/lib/commands/GEORADIUSBYMEMBER.ts +++ b/packages/client/lib/commands/GEORADIUSBYMEMBER.ts @@ -1,30 +1,33 @@ +import { CommandParser } from '../client/parser'; import { RedisArgument, ArrayReply, BlobStringReply, Command } from '../RESP/types'; -import { GeoUnits, GeoSearchOptions, pushGeoSearchOptions } from './GEOSEARCH'; +import { GeoUnits, GeoSearchOptions, parseGeoSearchOptions } from './GEOSEARCH'; -export function transformGeoRadiusByMemberArguments( - command: RedisArgument, +export function parseGeoRadiusByMemberArguments( + parser: CommandParser, key: RedisArgument, from: RedisArgument, radius: number, unit: GeoUnits, options?: GeoSearchOptions ) { - const args = [ - command, - key, - from, - radius.toString(), - unit - ]; + parser.pushKey(key); + parser.push(from, radius.toString(), unit); - pushGeoSearchOptions(args, options); - - return args; + parseGeoSearchOptions(parser, options); } export default { - FIRST_KEY_INDEX: 1, IS_READ_ONLY: false, - transformArguments: transformGeoRadiusByMemberArguments.bind(undefined, 'GEORADIUSBYMEMBER'), + parseCommand( + parser: CommandParser, + key: RedisArgument, + from: RedisArgument, + radius: number, + unit: GeoUnits, + options?: GeoSearchOptions + ) { + parser.push('GEORADIUSBYMEMBER'); + parseGeoRadiusByMemberArguments(parser, key, from, radius, unit, options); + }, transformReply: undefined as unknown as () => ArrayReply } as const satisfies Command; diff --git a/packages/client/lib/commands/GEORADIUSBYMEMBER_RO.spec.ts b/packages/client/lib/commands/GEORADIUSBYMEMBER_RO.spec.ts index abf10013973..bd4aa86dec1 100644 --- a/packages/client/lib/commands/GEORADIUSBYMEMBER_RO.spec.ts +++ b/packages/client/lib/commands/GEORADIUSBYMEMBER_RO.spec.ts @@ -1,11 +1,12 @@ import { strict as assert } from 'node:assert'; import testUtils, { GLOBAL } from '../test-utils'; import GEORADIUSBYMEMBER_RO from './GEORADIUSBYMEMBER_RO'; +import { parseArgs } from './generic-transformers'; describe('GEORADIUSBYMEMBER_RO', () => { it('transformArguments', () => { assert.deepEqual( - GEORADIUSBYMEMBER_RO.transformArguments('key', 'member', 3, 'm'), + parseArgs(GEORADIUSBYMEMBER_RO, 'key', 'member', 3, 'm'), ['GEORADIUSBYMEMBER_RO', 'key', 'member', '3', 'm'] ); }); diff --git a/packages/client/lib/commands/GEORADIUSBYMEMBER_RO.ts b/packages/client/lib/commands/GEORADIUSBYMEMBER_RO.ts index 7f85ed15df7..335eea08133 100644 --- a/packages/client/lib/commands/GEORADIUSBYMEMBER_RO.ts +++ b/packages/client/lib/commands/GEORADIUSBYMEMBER_RO.ts @@ -1,9 +1,13 @@ import { Command } from '../RESP/types'; -import GEORADIUSBYMEMBER, { transformGeoRadiusByMemberArguments } from './GEORADIUSBYMEMBER'; +import GEORADIUSBYMEMBER, { parseGeoRadiusByMemberArguments } from './GEORADIUSBYMEMBER'; export default { - FIRST_KEY_INDEX: GEORADIUSBYMEMBER.FIRST_KEY_INDEX, + CACHEABLE: true, IS_READ_ONLY: true, - transformArguments: transformGeoRadiusByMemberArguments.bind(undefined, 'GEORADIUSBYMEMBER_RO'), + parseCommand(...args: Parameters) { + const parser = args[0]; + parser.push('GEORADIUSBYMEMBER_RO'); + parseGeoRadiusByMemberArguments(...args); + }, transformReply: GEORADIUSBYMEMBER.transformReply } as const satisfies Command; diff --git a/packages/client/lib/commands/GEORADIUSBYMEMBER_RO_WITH.spec.ts b/packages/client/lib/commands/GEORADIUSBYMEMBER_RO_WITH.spec.ts index bcf91266365..52b31b03594 100644 --- a/packages/client/lib/commands/GEORADIUSBYMEMBER_RO_WITH.spec.ts +++ b/packages/client/lib/commands/GEORADIUSBYMEMBER_RO_WITH.spec.ts @@ -3,6 +3,7 @@ import testUtils, { GLOBAL } from '../test-utils'; import GEORADIUSBYMEMBER_RO_WITH from './GEORADIUSBYMEMBER_RO_WITH'; import { CommandArguments } from '../RESP/types'; import { GEO_REPLY_WITH } from './GEOSEARCH_WITH'; +import { parseArgs } from './generic-transformers'; describe('GEORADIUSBYMEMBER_RO WITH', () => { it('transformArguments', () => { @@ -10,7 +11,7 @@ describe('GEORADIUSBYMEMBER_RO WITH', () => { expectedReply.preserve = ['WITHDIST']; assert.deepEqual( - GEORADIUSBYMEMBER_RO_WITH.transformArguments('key', 'member', 3, 'm', [ + parseArgs(GEORADIUSBYMEMBER_RO_WITH, 'key', 'member', 3, 'm', [ GEO_REPLY_WITH.DISTANCE ]), expectedReply diff --git a/packages/client/lib/commands/GEORADIUSBYMEMBER_RO_WITH.ts b/packages/client/lib/commands/GEORADIUSBYMEMBER_RO_WITH.ts index 5fb945a1f9c..06835438016 100644 --- a/packages/client/lib/commands/GEORADIUSBYMEMBER_RO_WITH.ts +++ b/packages/client/lib/commands/GEORADIUSBYMEMBER_RO_WITH.ts @@ -1,9 +1,13 @@ import { Command } from '../RESP/types'; -import GEORADIUSBYMEMBER_WITH, { transformGeoRadiusByMemberWithArguments } from './GEORADIUSBYMEMBER_WITH'; +import GEORADIUSBYMEMBER_WITH, { parseGeoRadiusByMemberWithArguments } from './GEORADIUSBYMEMBER_WITH'; export default { - FIRST_KEY_INDEX: GEORADIUSBYMEMBER_WITH.FIRST_KEY_INDEX, + CACHEABLE: true, IS_READ_ONLY: true, - transformArguments: transformGeoRadiusByMemberWithArguments.bind(undefined, 'GEORADIUSBYMEMBER_RO'), + parseCommand(...args: Parameters) { + const parser = args[0]; + parser.push('GEORADIUSBYMEMBER_RO'); + parseGeoRadiusByMemberWithArguments(...args); + }, transformReply: GEORADIUSBYMEMBER_WITH.transformReply } as const satisfies Command; diff --git a/packages/client/lib/commands/GEORADIUSBYMEMBER_STORE.spec.ts b/packages/client/lib/commands/GEORADIUSBYMEMBER_STORE.spec.ts index 3d44060f205..9edb08d1eae 100644 --- a/packages/client/lib/commands/GEORADIUSBYMEMBER_STORE.spec.ts +++ b/packages/client/lib/commands/GEORADIUSBYMEMBER_STORE.spec.ts @@ -1,19 +1,20 @@ import { strict as assert } from 'node:assert'; import testUtils, { GLOBAL } from '../test-utils'; import GEORADIUSBYMEMBER_STORE from './GEORADIUSBYMEMBER_STORE'; +import { parseArgs } from './generic-transformers'; describe('GEORADIUSBYMEMBER STORE', () => { describe('transformArguments', () => { it('STORE', () => { assert.deepEqual( - GEORADIUSBYMEMBER_STORE.transformArguments('key', 'member', 3, 'm', 'destination'), + parseArgs(GEORADIUSBYMEMBER_STORE, 'key', 'member', 3, 'm', 'destination'), ['GEORADIUSBYMEMBER', 'key', 'member', '3', 'm', 'STORE', 'destination'] ); }); it('STOREDIST', () => { assert.deepEqual( - GEORADIUSBYMEMBER_STORE.transformArguments('key', 'member', 3, 'm', 'destination', { + parseArgs(GEORADIUSBYMEMBER_STORE, 'key', 'member', 3, 'm', 'destination', { STOREDIST: true }), ['GEORADIUSBYMEMBER', 'key', 'member', '3', 'm', 'STOREDIST', 'destination'] diff --git a/packages/client/lib/commands/GEORADIUSBYMEMBER_STORE.ts b/packages/client/lib/commands/GEORADIUSBYMEMBER_STORE.ts index 90419963110..676df34dd5a 100644 --- a/packages/client/lib/commands/GEORADIUSBYMEMBER_STORE.ts +++ b/packages/client/lib/commands/GEORADIUSBYMEMBER_STORE.ts @@ -1,5 +1,6 @@ +import { CommandParser } from '../client/parser'; import { RedisArgument, NumberReply, Command } from '../RESP/types'; -import GEORADIUSBYMEMBER, { transformGeoRadiusByMemberArguments } from './GEORADIUSBYMEMBER'; +import GEORADIUSBYMEMBER, { parseGeoRadiusByMemberArguments } from './GEORADIUSBYMEMBER'; import { GeoSearchOptions, GeoUnits } from './GEOSEARCH'; export interface GeoRadiusStoreOptions extends GeoSearchOptions { @@ -7,9 +8,9 @@ export interface GeoRadiusStoreOptions extends GeoSearchOptions { } export default { - FIRST_KEY_INDEX: GEORADIUSBYMEMBER.FIRST_KEY_INDEX, IS_READ_ONLY: GEORADIUSBYMEMBER.IS_READ_ONLY, - transformArguments( + parseCommand( + parser: CommandParser, key: RedisArgument, from: RedisArgument, radius: number, @@ -17,15 +18,16 @@ export default { destination: RedisArgument, options?: GeoRadiusStoreOptions ) { - const args = transformGeoRadiusByMemberArguments('GEORADIUSBYMEMBER', key, from, radius, unit, options); + parser.push('GEORADIUSBYMEMBER') + parseGeoRadiusByMemberArguments(parser, key, from, radius, unit, options); if (options?.STOREDIST) { - args.push('STOREDIST', destination); + parser.push('STOREDIST'); + parser.pushKey(destination); } else { - args.push('STORE', destination); + parser.push('STORE'); + parser.pushKey(destination); } - - return args; }, transformReply: undefined as unknown as () => NumberReply } as const satisfies Command; diff --git a/packages/client/lib/commands/GEORADIUSBYMEMBER_WITH.spec.ts b/packages/client/lib/commands/GEORADIUSBYMEMBER_WITH.spec.ts index ffe3b2efff3..9d634d60656 100644 --- a/packages/client/lib/commands/GEORADIUSBYMEMBER_WITH.spec.ts +++ b/packages/client/lib/commands/GEORADIUSBYMEMBER_WITH.spec.ts @@ -3,6 +3,7 @@ import testUtils, { GLOBAL } from '../test-utils'; import GEORADIUSBYMEMBER_WITH from './GEORADIUSBYMEMBER_WITH'; import { CommandArguments } from '../RESP/types'; import { GEO_REPLY_WITH } from './GEOSEARCH_WITH'; +import { parseArgs } from './generic-transformers'; describe('GEORADIUSBYMEMBER WITH', () => { it('transformArguments', () => { @@ -10,7 +11,7 @@ describe('GEORADIUSBYMEMBER WITH', () => { expectedReply.preserve = ['WITHDIST']; assert.deepEqual( - GEORADIUSBYMEMBER_WITH.transformArguments('key', 'member', 3, 'm', [ + parseArgs(GEORADIUSBYMEMBER_WITH, 'key', 'member', 3, 'm', [ GEO_REPLY_WITH.DISTANCE ]), expectedReply diff --git a/packages/client/lib/commands/GEORADIUSBYMEMBER_WITH.ts b/packages/client/lib/commands/GEORADIUSBYMEMBER_WITH.ts index be9472a438f..eefae0b27a9 100644 --- a/packages/client/lib/commands/GEORADIUSBYMEMBER_WITH.ts +++ b/packages/client/lib/commands/GEORADIUSBYMEMBER_WITH.ts @@ -1,10 +1,11 @@ -import { RedisArgument, CommandArguments, Command } from '../RESP/types'; +import { CommandParser } from '../client/parser'; +import { RedisArgument, Command } from '../RESP/types'; import GEORADIUSBYMEMBER from './GEORADIUSBYMEMBER'; -import { GeoSearchOptions, GeoUnits, pushGeoSearchOptions } from './GEOSEARCH'; +import { GeoSearchOptions, GeoUnits, parseGeoSearchOptions } from './GEOSEARCH'; import GEOSEARCH_WITH, { GeoReplyWith } from './GEOSEARCH_WITH'; -export function transformGeoRadiusByMemberWithArguments( - command: RedisArgument, +export function parseGeoRadiusByMemberWithArguments( + parser: CommandParser, key: RedisArgument, from: RedisArgument, radius: number, @@ -12,25 +13,27 @@ export function transformGeoRadiusByMemberWithArguments( replyWith: Array, options?: GeoSearchOptions ) { - const args: CommandArguments = [ - command, - key, - from, - radius.toString(), - unit - ]; + parser.pushKey(key); + parser.push(from, radius.toString(), unit); + parseGeoSearchOptions(parser, options); - pushGeoSearchOptions(args, options); - - args.push(...replyWith); - args.preserve = replyWith; - - return args; + parser.push(...replyWith); + parser.preserve = replyWith; } export default { - FIRST_KEY_INDEX: GEORADIUSBYMEMBER.FIRST_KEY_INDEX, IS_READ_ONLY: GEORADIUSBYMEMBER.IS_READ_ONLY, - transformArguments: transformGeoRadiusByMemberWithArguments.bind(undefined, 'GEORADIUSBYMEMBER'), + parseCommand( + parser: CommandParser, + key: RedisArgument, + from: RedisArgument, + radius: number, + unit: GeoUnits, + replyWith: Array, + options?: GeoSearchOptions + ) { + parser.push('GEORADIUSBYMEMBER'); + parseGeoRadiusByMemberWithArguments(parser, key, from, radius, unit, replyWith, options); + }, transformReply: GEOSEARCH_WITH.transformReply } as const satisfies Command; \ No newline at end of file diff --git a/packages/client/lib/commands/GEORADIUS_RO.spec.ts b/packages/client/lib/commands/GEORADIUS_RO.spec.ts index 43a2ef1d583..917eba3ab8e 100644 --- a/packages/client/lib/commands/GEORADIUS_RO.spec.ts +++ b/packages/client/lib/commands/GEORADIUS_RO.spec.ts @@ -1,11 +1,12 @@ import { strict as assert } from 'node:assert'; import testUtils, { GLOBAL } from '../test-utils'; import GEORADIUS_RO from './GEORADIUS_RO'; +import { parseArgs } from './generic-transformers'; describe('GEORADIUS_RO', () => { it('transformArguments', () => { assert.deepEqual( - GEORADIUS_RO.transformArguments('key', { + parseArgs(GEORADIUS_RO, 'key', { longitude: 1, latitude: 2 }, 3, 'm'), diff --git a/packages/client/lib/commands/GEORADIUS_RO.ts b/packages/client/lib/commands/GEORADIUS_RO.ts index be8bb4b530d..5db65d9dc9b 100644 --- a/packages/client/lib/commands/GEORADIUS_RO.ts +++ b/packages/client/lib/commands/GEORADIUS_RO.ts @@ -1,9 +1,12 @@ import { Command } from '../RESP/types'; -import GEORADIUS, { transformGeoRadiusArguments } from './GEORADIUS'; +import GEORADIUS, { parseGeoRadiusArguments } from './GEORADIUS'; export default { - FIRST_KEY_INDEX: GEORADIUS.FIRST_KEY_INDEX, + CACHEABLE: true, IS_READ_ONLY: true, - transformArguments: transformGeoRadiusArguments.bind(undefined, 'GEORADIUS_RO'), + parseCommand(...args: Parameters) { + args[0].push('GEORADIUS_RO'); + parseGeoRadiusArguments(...args); + }, transformReply: GEORADIUS.transformReply } as const satisfies Command; diff --git a/packages/client/lib/commands/GEORADIUS_RO_WITH.spec.ts b/packages/client/lib/commands/GEORADIUS_RO_WITH.spec.ts index cb0540d8a18..01d79954b64 100644 --- a/packages/client/lib/commands/GEORADIUS_RO_WITH.spec.ts +++ b/packages/client/lib/commands/GEORADIUS_RO_WITH.spec.ts @@ -3,6 +3,7 @@ import testUtils, { GLOBAL } from '../test-utils'; import GEORADIUS_RO_WITH from './GEORADIUS_RO_WITH'; import { GEO_REPLY_WITH } from './GEOSEARCH_WITH'; import { CommandArguments } from '../RESP/types'; +import { parseArgs } from './generic-transformers'; describe('GEORADIUS_RO WITH', () => { it('transformArguments', () => { @@ -10,7 +11,7 @@ describe('GEORADIUS_RO WITH', () => { expectedReply.preserve = ['WITHDIST']; assert.deepEqual( - GEORADIUS_RO_WITH.transformArguments('key', { + parseArgs(GEORADIUS_RO_WITH, 'key', { longitude: 1, latitude: 2 }, 3, 'm', [GEO_REPLY_WITH.DISTANCE]), diff --git a/packages/client/lib/commands/GEORADIUS_RO_WITH.ts b/packages/client/lib/commands/GEORADIUS_RO_WITH.ts index 37cf594ce9d..cee1679382b 100644 --- a/packages/client/lib/commands/GEORADIUS_RO_WITH.ts +++ b/packages/client/lib/commands/GEORADIUS_RO_WITH.ts @@ -1,9 +1,13 @@ import { Command } from '../RESP/types'; -import GEORADIUS_WITH, { transformGeoRadiusWithArguments } from './GEORADIUS_WITH'; +import { parseGeoRadiusWithArguments } from './GEORADIUS_WITH'; +import GEORADIUS_WITH from './GEORADIUS_WITH'; export default { - FIRST_KEY_INDEX: GEORADIUS_WITH.FIRST_KEY_INDEX, + CACHEABLE: true, IS_READ_ONLY: true, - transformArguments: transformGeoRadiusWithArguments.bind(undefined, 'GEORADIUS_RO'), + parseCommand(...args: Parameters) { + args[0].push('GEORADIUS_RO'); + parseGeoRadiusWithArguments(...args); + }, transformReply: GEORADIUS_WITH.transformReply } as const satisfies Command; diff --git a/packages/client/lib/commands/GEORADIUS_STORE.spec.ts b/packages/client/lib/commands/GEORADIUS_STORE.spec.ts index 04a7d28aa95..9a9bcf37bcf 100644 --- a/packages/client/lib/commands/GEORADIUS_STORE.spec.ts +++ b/packages/client/lib/commands/GEORADIUS_STORE.spec.ts @@ -1,12 +1,13 @@ import { strict as assert } from 'node:assert'; import testUtils, { GLOBAL } from '../test-utils'; import GEORADIUS_STORE from './GEORADIUS_STORE'; +import { parseArgs } from './generic-transformers'; describe('GEORADIUS STORE', () => { describe('transformArguments', () => { it('STORE', () => { assert.deepEqual( - GEORADIUS_STORE.transformArguments('key', { + parseArgs(GEORADIUS_STORE, 'key', { longitude: 1, latitude: 2 }, 3, 'm', 'destination'), @@ -16,7 +17,7 @@ describe('GEORADIUS STORE', () => { it('STOREDIST', () => { assert.deepEqual( - GEORADIUS_STORE.transformArguments('key', { + parseArgs(GEORADIUS_STORE, 'key', { longitude: 1, latitude: 2 }, 3, 'm', 'destination', { diff --git a/packages/client/lib/commands/GEORADIUS_STORE.ts b/packages/client/lib/commands/GEORADIUS_STORE.ts index 3a553ebf8be..18459d44217 100644 --- a/packages/client/lib/commands/GEORADIUS_STORE.ts +++ b/packages/client/lib/commands/GEORADIUS_STORE.ts @@ -1,5 +1,6 @@ +import { CommandParser } from '../client/parser'; import { RedisArgument, NumberReply, Command } from '../RESP/types'; -import GEORADIUS, { transformGeoRadiusArguments } from './GEORADIUS'; +import GEORADIUS, { parseGeoRadiusArguments } from './GEORADIUS'; import { GeoCoordinates, GeoSearchOptions, GeoUnits } from './GEOSEARCH'; export interface GeoRadiusStoreOptions extends GeoSearchOptions { @@ -7,9 +8,9 @@ export interface GeoRadiusStoreOptions extends GeoSearchOptions { } export default { - FIRST_KEY_INDEX: GEORADIUS.FIRST_KEY_INDEX, IS_READ_ONLY: GEORADIUS.IS_READ_ONLY, - transformArguments( + parseCommand( + parser: CommandParser, key: RedisArgument, from: GeoCoordinates, radius: number, @@ -17,15 +18,15 @@ export default { destination: RedisArgument, options?: GeoRadiusStoreOptions ) { - const args = transformGeoRadiusArguments('GEORADIUS', key, from, radius, unit, options); - + parser.push('GEORADIUS'); + parseGeoRadiusArguments(parser, key, from, radius, unit, options); if (options?.STOREDIST) { - args.push('STOREDIST', destination); + parser.push('STOREDIST'); + parser.pushKey(destination); } else { - args.push('STORE', destination); + parser.push('STORE'); + parser.pushKey(destination); } - - return args; }, transformReply: undefined as unknown as () => NumberReply } as const satisfies Command; diff --git a/packages/client/lib/commands/GEORADIUS_WITH.spec.ts b/packages/client/lib/commands/GEORADIUS_WITH.spec.ts index bdbfc9c1f3a..f514c9be96f 100644 --- a/packages/client/lib/commands/GEORADIUS_WITH.spec.ts +++ b/packages/client/lib/commands/GEORADIUS_WITH.spec.ts @@ -3,6 +3,7 @@ import testUtils, { GLOBAL } from '../test-utils'; import GEORADIUS_WITH from './GEORADIUS_WITH'; import { GEO_REPLY_WITH } from './GEOSEARCH_WITH'; import { CommandArguments } from '../RESP/types'; +import { parseArgs } from './generic-transformers'; describe('GEORADIUS WITH', () => { it('transformArguments', () => { @@ -10,7 +11,7 @@ describe('GEORADIUS WITH', () => { expectedReply.preserve = ['WITHDIST']; assert.deepEqual( - GEORADIUS_WITH.transformArguments('key', { + parseArgs(GEORADIUS_WITH, 'key', { longitude: 1, latitude: 2 }, 3, 'm', [GEO_REPLY_WITH.DISTANCE]), diff --git a/packages/client/lib/commands/GEORADIUS_WITH.ts b/packages/client/lib/commands/GEORADIUS_WITH.ts index d72d8d49322..ac4c8b7bb1b 100644 --- a/packages/client/lib/commands/GEORADIUS_WITH.ts +++ b/packages/client/lib/commands/GEORADIUS_WITH.ts @@ -1,33 +1,36 @@ -import { CommandArguments, Command, RedisArgument } from '../RESP/types'; -import GEORADIUS, { transformGeoRadiusArguments } from './GEORADIUS'; +import { CommandParser } from '../client/parser'; +import { Command, RedisArgument } from '../RESP/types'; +import GEORADIUS, { parseGeoRadiusArguments } from './GEORADIUS'; import { GeoCoordinates, GeoSearchOptions, GeoUnits } from './GEOSEARCH'; import GEOSEARCH_WITH, { GeoReplyWith } from './GEOSEARCH_WITH'; -export function transformGeoRadiusWithArguments( - command: RedisArgument, +export function parseGeoRadiusWithArguments( + parser: CommandParser, key: RedisArgument, from: GeoCoordinates, radius: number, unit: GeoUnits, replyWith: Array, - options?: GeoSearchOptions + options?: GeoSearchOptions, ) { - const args: CommandArguments = transformGeoRadiusArguments( - command, - key, - from, - radius, - unit, - options - ); - args.push(...replyWith); - args.preserve = replyWith; - return args; + parseGeoRadiusArguments(parser, key, from, radius, unit, options) + parser.pushVariadic(replyWith); + parser.preserve = replyWith; } export default { - FIRST_KEY_INDEX: GEORADIUS.FIRST_KEY_INDEX, IS_READ_ONLY: GEORADIUS.IS_READ_ONLY, - transformArguments: transformGeoRadiusWithArguments.bind(undefined, 'GEORADIUS'), + parseCommand( + parser: CommandParser, + key: RedisArgument, + from: GeoCoordinates, + radius: number, + unit: GeoUnits, + replyWith: Array, + options?: GeoSearchOptions + ) { + parser.push('GEORADIUS'); + parseGeoRadiusWithArguments(parser, key, from, radius, unit, replyWith, options); + }, transformReply: GEOSEARCH_WITH.transformReply } as const satisfies Command; diff --git a/packages/client/lib/commands/GEOSEARCH.spec.ts b/packages/client/lib/commands/GEOSEARCH.spec.ts index 49f076880a6..4cd7e61a0ac 100644 --- a/packages/client/lib/commands/GEOSEARCH.spec.ts +++ b/packages/client/lib/commands/GEOSEARCH.spec.ts @@ -1,6 +1,7 @@ import { strict as assert } from 'node:assert'; import testUtils, { GLOBAL } from '../test-utils'; import GEOSEARCH from './GEOSEARCH'; +import { parseArgs } from './generic-transformers'; describe('GEOSEARCH', () => { testUtils.isVersionGreaterThanHook([6, 2]); @@ -8,7 +9,7 @@ describe('GEOSEARCH', () => { describe('transformArguments', () => { it('FROMMEMBER, BYRADIUS, without options', () => { assert.deepEqual( - GEOSEARCH.transformArguments('key', 'member', { + parseArgs(GEOSEARCH, 'key', 'member', { radius: 1, unit: 'm' }), @@ -18,7 +19,7 @@ describe('GEOSEARCH', () => { it('FROMLONLAT, BYBOX, without options', () => { assert.deepEqual( - GEOSEARCH.transformArguments('key', { + parseArgs(GEOSEARCH, 'key', { longitude: 1, latitude: 2 }, { @@ -32,7 +33,7 @@ describe('GEOSEARCH', () => { it('with SORT', () => { assert.deepEqual( - GEOSEARCH.transformArguments('key', 'member', { + parseArgs(GEOSEARCH, 'key', 'member', { radius: 1, unit: 'm' }, { @@ -45,7 +46,7 @@ describe('GEOSEARCH', () => { describe('with COUNT', () => { it('number', () => { assert.deepEqual( - GEOSEARCH.transformArguments('key', 'member', { + parseArgs(GEOSEARCH, 'key', 'member', { radius: 1, unit: 'm' }, { @@ -57,7 +58,7 @@ describe('GEOSEARCH', () => { it('with ANY', () => { assert.deepEqual( - GEOSEARCH.transformArguments('key', 'member', { + parseArgs(GEOSEARCH, 'key', 'member', { radius: 1, unit: 'm' }, { diff --git a/packages/client/lib/commands/GEOSEARCH.ts b/packages/client/lib/commands/GEOSEARCH.ts index c4deaa37e6c..8c77fd89239 100644 --- a/packages/client/lib/commands/GEOSEARCH.ts +++ b/packages/client/lib/commands/GEOSEARCH.ts @@ -1,4 +1,5 @@ -import { RedisArgument, CommandArguments, ArrayReply, BlobStringReply, Command } from '../RESP/types'; +import { CommandParser } from '../client/parser'; +import { RedisArgument, ArrayReply, BlobStringReply, Command } from '../RESP/types'; export type GeoUnits = 'm' | 'km' | 'mi' | 'ft'; @@ -22,30 +23,33 @@ export interface GeoSearchByBox { export type GeoSearchBy = GeoSearchByRadius | GeoSearchByBox; -export function pushGeoSearchArguments( - args: CommandArguments, +export function parseGeoSearchArguments( + parser: CommandParser, key: RedisArgument, from: GeoSearchFrom, by: GeoSearchBy, - options?: GeoSearchOptions + options?: GeoSearchOptions, + store?: RedisArgument ) { - args.push(key); + if (store !== undefined) { + parser.pushKey(store); + } + + parser.pushKey(key); if (typeof from === 'string' || from instanceof Buffer) { - args.push('FROMMEMBER', from); + parser.push('FROMMEMBER', from); } else { - args.push('FROMLONLAT', from.longitude.toString(), from.latitude.toString()); + parser.push('FROMLONLAT', from.longitude.toString(), from.latitude.toString()); } if ('radius' in by) { - args.push('BYRADIUS', by.radius.toString(), by.unit); + parser.push('BYRADIUS', by.radius.toString(), by.unit); } else { - args.push('BYBOX', by.width.toString(), by.height.toString(), by.unit); + parser.push('BYBOX', by.width.toString(), by.height.toString(), by.unit); } - pushGeoSearchOptions(args, options); - - return args; + parseGeoSearchOptions(parser, options); } export type GeoCountArgument = number | { @@ -58,37 +62,38 @@ export interface GeoSearchOptions { COUNT?: GeoCountArgument; } -export function pushGeoSearchOptions( - args: CommandArguments, +export function parseGeoSearchOptions( + parser: CommandParser, options?: GeoSearchOptions ) { if (options?.SORT) { - args.push(options.SORT); + parser.push(options.SORT); } if (options?.COUNT) { if (typeof options.COUNT === 'number') { - args.push('COUNT', options.COUNT.toString()); + parser.push('COUNT', options.COUNT.toString()); } else { - args.push('COUNT', options.COUNT.value.toString()); + parser.push('COUNT', options.COUNT.value.toString()); if (options.COUNT.ANY) { - args.push('ANY'); + parser.push('ANY'); } } } } export default { - FIRST_KEY_INDEX: 1, IS_READ_ONLY: true, - transformArguments( + parseCommand( + parser: CommandParser, key: RedisArgument, from: GeoSearchFrom, by: GeoSearchBy, options?: GeoSearchOptions ) { - return pushGeoSearchArguments(['GEOSEARCH'], key, from, by, options); + parser.push('GEOSEARCH'); + parseGeoSearchArguments(parser, key, from, by, options); }, transformReply: undefined as unknown as () => ArrayReply } as const satisfies Command; diff --git a/packages/client/lib/commands/GEOSEARCHSTORE.spec.ts b/packages/client/lib/commands/GEOSEARCHSTORE.spec.ts index c66d3e8e45e..b8427ae0412 100644 --- a/packages/client/lib/commands/GEOSEARCHSTORE.spec.ts +++ b/packages/client/lib/commands/GEOSEARCHSTORE.spec.ts @@ -1,6 +1,7 @@ import { strict as assert } from 'node:assert'; import testUtils, { GLOBAL } from '../test-utils'; import GEOSEARCHSTORE from './GEOSEARCHSTORE'; +import { parseArgs } from './generic-transformers'; describe('GEOSEARCHSTORE', () => { testUtils.isVersionGreaterThanHook([6, 2]); @@ -8,7 +9,7 @@ describe('GEOSEARCHSTORE', () => { describe('transformArguments', () => { it('simple', () => { assert.deepEqual( - GEOSEARCHSTORE.transformArguments('source', 'destination', 'member', { + parseArgs(GEOSEARCHSTORE, 'source', 'destination', 'member', { radius: 1, unit: 'm' }), @@ -18,7 +19,7 @@ describe('GEOSEARCHSTORE', () => { it('with STOREDIST', () => { assert.deepEqual( - GEOSEARCHSTORE.transformArguments('destination', 'source', 'member', { + parseArgs(GEOSEARCHSTORE, 'destination', 'source', 'member', { radius: 1, unit: 'm' }, { diff --git a/packages/client/lib/commands/GEOSEARCHSTORE.ts b/packages/client/lib/commands/GEOSEARCHSTORE.ts index 15635560217..eb8e12abe6d 100644 --- a/packages/client/lib/commands/GEOSEARCHSTORE.ts +++ b/packages/client/lib/commands/GEOSEARCHSTORE.ts @@ -1,27 +1,27 @@ +import { CommandParser } from '../client/parser'; import { RedisArgument, NumberReply, Command } from '../RESP/types'; -import { GeoSearchFrom, GeoSearchBy, GeoSearchOptions, pushGeoSearchArguments } from './GEOSEARCH'; +import { GeoSearchFrom, GeoSearchBy, GeoSearchOptions, parseGeoSearchArguments } from './GEOSEARCH'; export interface GeoSearchStoreOptions extends GeoSearchOptions { STOREDIST?: boolean; } export default { - FIRST_KEY_INDEX: 1, IS_READ_ONLY: false, - transformArguments( + parseCommand( + parser: CommandParser, destination: RedisArgument, source: RedisArgument, from: GeoSearchFrom, by: GeoSearchBy, options?: GeoSearchStoreOptions ) { - const args = pushGeoSearchArguments(['GEOSEARCHSTORE', destination], source, from, by, options); + parser.push('GEOSEARCHSTORE'); + parseGeoSearchArguments(parser, source, from, by, options, destination); if (options?.STOREDIST) { - args.push('STOREDIST'); + parser.push('STOREDIST'); } - - return args; }, transformReply: undefined as unknown as () => NumberReply } as const satisfies Command; diff --git a/packages/client/lib/commands/GEOSEARCH_WITH.spec.ts b/packages/client/lib/commands/GEOSEARCH_WITH.spec.ts index e27fb295aaf..973e5d5827f 100644 --- a/packages/client/lib/commands/GEOSEARCH_WITH.spec.ts +++ b/packages/client/lib/commands/GEOSEARCH_WITH.spec.ts @@ -2,6 +2,7 @@ import { strict as assert } from 'node:assert'; import testUtils, { GLOBAL } from '../test-utils'; import GEOSEARCH_WITH, { GEO_REPLY_WITH } from './GEOSEARCH_WITH'; import { CommandArguments } from '../RESP/types'; +import { parseArgs } from './generic-transformers'; describe('GEOSEARCH WITH', () => { testUtils.isVersionGreaterThanHook([6, 2]); @@ -11,7 +12,7 @@ describe('GEOSEARCH WITH', () => { expectedReply.preserve = ['WITHDIST']; assert.deepEqual( - GEOSEARCH_WITH.transformArguments('key', 'member', { + parseArgs(GEOSEARCH_WITH, 'key', 'member', { radius: 1, unit: 'm' }, [GEO_REPLY_WITH.DISTANCE]), diff --git a/packages/client/lib/commands/GEOSEARCH_WITH.ts b/packages/client/lib/commands/GEOSEARCH_WITH.ts index 19088230f0f..65e3975b72f 100644 --- a/packages/client/lib/commands/GEOSEARCH_WITH.ts +++ b/packages/client/lib/commands/GEOSEARCH_WITH.ts @@ -1,3 +1,4 @@ +import { CommandParser } from '../client/parser'; import { RedisArgument, ArrayReply, TuplesReply, BlobStringReply, NumberReply, DoubleReply, UnwrapReply, Command } from '../RESP/types'; import GEOSEARCH, { GeoSearchBy, GeoSearchFrom, GeoSearchOptions } from './GEOSEARCH'; @@ -20,19 +21,18 @@ export interface GeoReplyWithMember { } export default { - FIRST_KEY_INDEX: GEOSEARCH.FIRST_KEY_INDEX, IS_READ_ONLY: GEOSEARCH.IS_READ_ONLY, - transformArguments( + parseCommand( + parser: CommandParser, key: RedisArgument, from: GeoSearchFrom, by: GeoSearchBy, replyWith: Array, options?: GeoSearchOptions ) { - const args = GEOSEARCH.transformArguments(key, from, by, options); - args.push(...replyWith); - args.preserve = replyWith; - return args; + GEOSEARCH.parseCommand(parser, key, from, by, options); + parser.push(...replyWith); + parser.preserve = replyWith; }, transformReply( reply: UnwrapReply]>>>, diff --git a/packages/client/lib/commands/GET.spec.ts b/packages/client/lib/commands/GET.spec.ts index 4bd74183222..3e630d03e0b 100644 --- a/packages/client/lib/commands/GET.spec.ts +++ b/packages/client/lib/commands/GET.spec.ts @@ -1,11 +1,12 @@ import { strict as assert } from 'node:assert'; import testUtils, { GLOBAL } from '../test-utils'; +import { parseArgs } from './generic-transformers'; import GET from './GET'; describe('GET', () => { it('transformArguments', () => { assert.deepEqual( - GET.transformArguments('key'), + parseArgs(GET, 'key'), ['GET', 'key'] ); }); diff --git a/packages/client/lib/commands/GET.ts b/packages/client/lib/commands/GET.ts index bb3db4f76d9..ca013752ae5 100644 --- a/packages/client/lib/commands/GET.ts +++ b/packages/client/lib/commands/GET.ts @@ -1,10 +1,12 @@ +import { CommandParser } from '../client/parser'; import { RedisArgument, BlobStringReply, NullReply, Command } from '../RESP/types'; export default { - FIRST_KEY_INDEX: 1, + CACHEABLE: true, IS_READ_ONLY: true, - transformArguments(key: RedisArgument) { - return ['GET', key]; + parseCommand(parser: CommandParser, key: RedisArgument) { + parser.push('GET'); + parser.pushKey(key); }, transformReply: undefined as unknown as () => BlobStringReply | NullReply } as const satisfies Command; diff --git a/packages/client/lib/commands/GETBIT.spec.ts b/packages/client/lib/commands/GETBIT.spec.ts index ac39222b918..66d2798313c 100644 --- a/packages/client/lib/commands/GETBIT.spec.ts +++ b/packages/client/lib/commands/GETBIT.spec.ts @@ -1,11 +1,12 @@ import { strict as assert } from 'node:assert'; import testUtils, { GLOBAL } from '../test-utils'; import GETBIT from './GETBIT'; +import { parseArgs } from './generic-transformers'; describe('GETBIT', () => { - it('transformArguments', () => { + it('processCommand', () => { assert.deepEqual( - GETBIT.transformArguments('key', 0), + parseArgs(GETBIT, 'key', 0), ['GETBIT', 'key', '0'] ); }); diff --git a/packages/client/lib/commands/GETBIT.ts b/packages/client/lib/commands/GETBIT.ts index d8ece8f523a..023ba0fb607 100644 --- a/packages/client/lib/commands/GETBIT.ts +++ b/packages/client/lib/commands/GETBIT.ts @@ -1,11 +1,14 @@ +import { CommandParser } from '../client/parser'; import { NumberReply, Command, RedisArgument } from '../RESP/types'; import { BitValue } from './generic-transformers'; export default { - FIRST_KEY_INDEX: 1, + CACHEABLE: true, IS_READ_ONLY: true, - transformArguments(key: RedisArgument, offset: number) { - return ['GETBIT', key, offset.toString()]; + parseCommand(parser: CommandParser, key: RedisArgument, offset: number) { + parser.push('GETBIT'); + parser.pushKey(key); + parser.push(offset.toString()); }, transformReply: undefined as unknown as () => NumberReply } as const satisfies Command; diff --git a/packages/client/lib/commands/GETDEL.spec.ts b/packages/client/lib/commands/GETDEL.spec.ts index 311f15e554d..15ad5918008 100644 --- a/packages/client/lib/commands/GETDEL.spec.ts +++ b/packages/client/lib/commands/GETDEL.spec.ts @@ -1,13 +1,14 @@ import { strict as assert } from 'node:assert'; import testUtils, { GLOBAL } from '../test-utils'; import GETDEL from './GETDEL'; +import { parseArgs } from './generic-transformers'; describe('GETDEL', () => { testUtils.isVersionGreaterThanHook([6, 2]); it('transformArguments', () => { assert.deepEqual( - GETDEL.transformArguments('key'), + parseArgs(GETDEL, 'key'), ['GETDEL', 'key'] ); }); diff --git a/packages/client/lib/commands/GETDEL.ts b/packages/client/lib/commands/GETDEL.ts index c11fd047df4..a39014109f1 100644 --- a/packages/client/lib/commands/GETDEL.ts +++ b/packages/client/lib/commands/GETDEL.ts @@ -1,10 +1,11 @@ +import { CommandParser } from '../client/parser'; import { RedisArgument, BlobStringReply, NullReply, Command } from '../RESP/types'; export default { - FIRST_KEY_INDEX: 1, IS_READ_ONLY: true, - transformArguments(key: RedisArgument) { - return ['GETDEL', key]; + parseCommand(parser: CommandParser, key: RedisArgument) { + parser.push('GETDEL'); + parser.pushKey(key); }, transformReply: undefined as unknown as () => BlobStringReply | NullReply } as const satisfies Command; diff --git a/packages/client/lib/commands/GETEX.spec.ts b/packages/client/lib/commands/GETEX.spec.ts index 302d034b961..5965d8f196f 100644 --- a/packages/client/lib/commands/GETEX.spec.ts +++ b/packages/client/lib/commands/GETEX.spec.ts @@ -1,6 +1,7 @@ import { strict as assert } from 'node:assert'; import testUtils, { GLOBAL } from '../test-utils'; import GETEX from './GETEX'; +import { parseArgs } from './generic-transformers'; describe('GETEX', () => { testUtils.isVersionGreaterThanHook([6, 2]); @@ -8,7 +9,7 @@ describe('GETEX', () => { describe('transformArguments', () => { it('EX | PX', () => { assert.deepEqual( - GETEX.transformArguments('key', { + parseArgs(GETEX, 'key', { type: 'EX', value: 1 }), @@ -18,7 +19,7 @@ describe('GETEX', () => { it('EX (backwards compatibility)', () => { assert.deepEqual( - GETEX.transformArguments('key', { + parseArgs(GETEX, 'key', { EX: 1 }), ['GETEX', 'key', 'EX', '1'] @@ -27,7 +28,7 @@ describe('GETEX', () => { it('PX (backwards compatibility)', () => { assert.deepEqual( - GETEX.transformArguments('key', { + parseArgs(GETEX, 'key', { PX: 1 }), ['GETEX', 'key', 'PX', '1'] @@ -37,7 +38,7 @@ describe('GETEX', () => { describe('EXAT | PXAT', () => { it('number', () => { assert.deepEqual( - GETEX.transformArguments('key', { + parseArgs(GETEX, 'key', { type: 'EXAT', value: 1 }), @@ -48,7 +49,7 @@ describe('GETEX', () => { it('date', () => { const d = new Date(); assert.deepEqual( - GETEX.transformArguments('key', { + parseArgs(GETEX, 'key', { EXAT: d }), ['GETEX', 'key', 'EXAT', Math.floor(d.getTime() / 1000).toString()] @@ -59,7 +60,7 @@ describe('GETEX', () => { describe('EXAT (backwards compatibility)', () => { it('number', () => { assert.deepEqual( - GETEX.transformArguments('key', { + parseArgs(GETEX, 'key', { EXAT: 1 }), ['GETEX', 'key', 'EXAT', '1'] @@ -69,7 +70,7 @@ describe('GETEX', () => { it('date', () => { const d = new Date(); assert.deepEqual( - GETEX.transformArguments('key', { + parseArgs(GETEX, 'key', { EXAT: d }), ['GETEX', 'key', 'EXAT', Math.floor(d.getTime() / 1000).toString()] @@ -80,7 +81,7 @@ describe('GETEX', () => { describe('PXAT (backwards compatibility)', () => { it('number', () => { assert.deepEqual( - GETEX.transformArguments('key', { + parseArgs(GETEX, 'key', { PXAT: 1 }), ['GETEX', 'key', 'PXAT', '1'] @@ -90,7 +91,7 @@ describe('GETEX', () => { it('date', () => { const d = new Date(); assert.deepEqual( - GETEX.transformArguments('key', { + parseArgs(GETEX, 'key', { PXAT: d }), ['GETEX', 'key', 'PXAT', d.getTime().toString()] @@ -100,7 +101,7 @@ describe('GETEX', () => { it('PERSIST (backwards compatibility)', () => { assert.deepEqual( - GETEX.transformArguments('key', { + parseArgs(GETEX, 'key', { PERSIST: true }), ['GETEX', 'key', 'PERSIST'] diff --git a/packages/client/lib/commands/GETEX.ts b/packages/client/lib/commands/GETEX.ts index 8244350eddb..e5ae0b691a7 100644 --- a/packages/client/lib/commands/GETEX.ts +++ b/packages/client/lib/commands/GETEX.ts @@ -1,3 +1,4 @@ +import { CommandParser } from '../client/parser'; import { RedisArgument, BlobStringReply, NullReply, Command } from '../RESP/types'; import { transformEXAT, transformPXAT } from './generic-transformers'; @@ -37,42 +38,40 @@ export type GetExOptions = { }; export default { - FIRST_KEY_INDEX: 1, IS_READ_ONLY: true, - transformArguments(key: RedisArgument, options: GetExOptions) { - const args = ['GETEX', key]; + parseCommand(parser: CommandParser, key: RedisArgument, options: GetExOptions) { + parser.push('GETEX'); + parser.pushKey(key); if ('type' in options) { switch (options.type) { case 'EX': case 'PX': - args.push(options.type, options.value.toString()); + parser.push(options.type, options.value.toString()); break; case 'EXAT': case 'PXAT': - args.push(options.type, transformEXAT(options.value)); + parser.push(options.type, transformEXAT(options.value)); break; case 'PERSIST': - args.push('PERSIST'); + parser.push('PERSIST'); break; } } else { if ('EX' in options) { - args.push('EX', options.EX.toString()); + parser.push('EX', options.EX.toString()); } else if ('PX' in options) { - args.push('PX', options.PX.toString()); + parser.push('PX', options.PX.toString()); } else if ('EXAT' in options) { - args.push('EXAT', transformEXAT(options.EXAT)); + parser.push('EXAT', transformEXAT(options.EXAT)); } else if ('PXAT' in options) { - args.push('PXAT', transformPXAT(options.PXAT)); + parser.push('PXAT', transformPXAT(options.PXAT)); } else { // PERSIST - args.push('PERSIST'); + parser.push('PERSIST'); } } - - return args; }, transformReply: undefined as unknown as () => BlobStringReply | NullReply } as const satisfies Command; diff --git a/packages/client/lib/commands/GETRANGE.spec.ts b/packages/client/lib/commands/GETRANGE.spec.ts index 2aac1ca16d9..8a8e7dde038 100644 --- a/packages/client/lib/commands/GETRANGE.spec.ts +++ b/packages/client/lib/commands/GETRANGE.spec.ts @@ -1,11 +1,12 @@ import { strict as assert } from 'node:assert'; import testUtils, { GLOBAL } from '../test-utils'; import GETRANGE from './GETRANGE'; +import { parseArgs } from './generic-transformers'; describe('GETRANGE', () => { - it('transformArguments', () => { + it('processCommand', () => { assert.deepEqual( - GETRANGE.transformArguments('key', 0, -1), + parseArgs(GETRANGE, 'key', 0, -1), ['GETRANGE', 'key', '0', '-1'] ); }); diff --git a/packages/client/lib/commands/GETRANGE.ts b/packages/client/lib/commands/GETRANGE.ts index e5357cd120b..ce0db6e3c03 100644 --- a/packages/client/lib/commands/GETRANGE.ts +++ b/packages/client/lib/commands/GETRANGE.ts @@ -1,10 +1,13 @@ +import { CommandParser } from '../client/parser'; import { RedisArgument, BlobStringReply, NullReply, Command } from '../RESP/types'; export default { - FIRST_KEY_INDEX: 1, + CACHEABLE: true, IS_READ_ONLY: true, - transformArguments(key: RedisArgument, start: number, end: number) { - return ['GETRANGE', key, start.toString(), end.toString()]; + parseCommand(parser: CommandParser, key: RedisArgument, start: number, end: number) { + parser.push('GETRANGE'); + parser.pushKey(key); + parser.push(start.toString(), end.toString()); }, transformReply: undefined as unknown as () => BlobStringReply | NullReply } as const satisfies Command; diff --git a/packages/client/lib/commands/GETSET.spec.ts b/packages/client/lib/commands/GETSET.spec.ts index 6583ec34f75..5b162c16cc4 100644 --- a/packages/client/lib/commands/GETSET.spec.ts +++ b/packages/client/lib/commands/GETSET.spec.ts @@ -1,11 +1,12 @@ import { strict as assert } from 'node:assert'; import testUtils, { GLOBAL } from '../test-utils'; import GETSET from './GETSET'; +import { parseArgs } from './generic-transformers'; describe('GETSET', () => { it('transformArguments', () => { assert.deepEqual( - GETSET.transformArguments('key', 'value'), + parseArgs(GETSET, 'key', 'value'), ['GETSET', 'key', 'value'] ); }); diff --git a/packages/client/lib/commands/GETSET.ts b/packages/client/lib/commands/GETSET.ts index bbe920181b2..1b3312548e4 100644 --- a/packages/client/lib/commands/GETSET.ts +++ b/packages/client/lib/commands/GETSET.ts @@ -1,10 +1,12 @@ +import { CommandParser } from '../client/parser'; import { RedisArgument, BlobStringReply, NullReply, Command } from '../RESP/types'; export default { - FIRST_KEY_INDEX: 1, IS_READ_ONLY: true, - transformArguments(key: RedisArgument, value: RedisArgument) { - return ['GETSET', key, value]; + parseCommand(parser: CommandParser, key: RedisArgument, value: RedisArgument) { + parser.push('GETSET'); + parser.pushKey(key); + parser.push(value); }, transformReply: undefined as unknown as () => BlobStringReply | NullReply } as const satisfies Command; diff --git a/packages/client/lib/commands/HDEL.spec.ts b/packages/client/lib/commands/HDEL.spec.ts index 9f69485d9fe..767d916e147 100644 --- a/packages/client/lib/commands/HDEL.spec.ts +++ b/packages/client/lib/commands/HDEL.spec.ts @@ -1,19 +1,20 @@ import { strict as assert } from 'node:assert'; import testUtils, { GLOBAL } from '../test-utils'; import HDEL from './HDEL'; +import { parseArgs } from './generic-transformers'; describe('HDEL', () => { describe('transformArguments', () => { it('string', () => { assert.deepEqual( - HDEL.transformArguments('key', 'field'), + parseArgs(HDEL, 'key', 'field'), ['HDEL', 'key', 'field'] ); }); it('array', () => { assert.deepEqual( - HDEL.transformArguments('key', ['1', '2']), + parseArgs(HDEL, 'key', ['1', '2']), ['HDEL', 'key', '1', '2'] ); }); diff --git a/packages/client/lib/commands/HDEL.ts b/packages/client/lib/commands/HDEL.ts index 64aa55edda6..713d19a9b2a 100644 --- a/packages/client/lib/commands/HDEL.ts +++ b/packages/client/lib/commands/HDEL.ts @@ -1,10 +1,12 @@ +import { CommandParser } from '../client/parser'; import { RedisArgument, NumberReply, Command } from '../RESP/types'; -import { RedisVariadicArgument, pushVariadicArguments } from './generic-transformers'; +import { RedisVariadicArgument } from './generic-transformers'; export default { - FIRST_KEY_INDEX: 1, - transformArguments(key: RedisArgument, field: RedisVariadicArgument) { - return pushVariadicArguments(['HDEL', key], field); + parseCommand(parser: CommandParser, key: RedisArgument, field: RedisVariadicArgument) { + parser.push('HDEL'); + parser.pushKey(key); + parser.pushVariadic(field); }, transformReply: undefined as unknown as () => NumberReply } as const satisfies Command; diff --git a/packages/client/lib/commands/HELLO.spec.ts b/packages/client/lib/commands/HELLO.spec.ts index f7f117f18c7..5d11be344c1 100644 --- a/packages/client/lib/commands/HELLO.spec.ts +++ b/packages/client/lib/commands/HELLO.spec.ts @@ -1,6 +1,7 @@ import { strict as assert } from 'node:assert'; import testUtils, { GLOBAL } from '../test-utils'; import HELLO from './HELLO'; +import { parseArgs } from './generic-transformers'; describe('HELLO', () => { testUtils.isVersionGreaterThanHook([6]); @@ -8,21 +9,21 @@ describe('HELLO', () => { describe('transformArguments', () => { it('simple', () => { assert.deepEqual( - HELLO.transformArguments(), + parseArgs(HELLO), ['HELLO'] ); }); it('with protover', () => { assert.deepEqual( - HELLO.transformArguments(3), + parseArgs(HELLO, 3), ['HELLO', '3'] ); }); it('with protover, AUTH', () => { assert.deepEqual( - HELLO.transformArguments(3, { + parseArgs(HELLO, 3, { AUTH: { username: 'username', password: 'password' @@ -34,7 +35,7 @@ describe('HELLO', () => { it('with protover, SETNAME', () => { assert.deepEqual( - HELLO.transformArguments(3, { + parseArgs(HELLO, 3, { SETNAME: 'name' }), ['HELLO', '3', 'SETNAME', 'name'] @@ -43,7 +44,7 @@ describe('HELLO', () => { it('with protover, AUTH, SETNAME', () => { assert.deepEqual( - HELLO.transformArguments(3, { + parseArgs(HELLO, 3, { AUTH: { username: 'username', password: 'password' diff --git a/packages/client/lib/commands/HELLO.ts b/packages/client/lib/commands/HELLO.ts index 0fb2960d028..5d25998f987 100644 --- a/packages/client/lib/commands/HELLO.ts +++ b/packages/client/lib/commands/HELLO.ts @@ -1,3 +1,4 @@ +import { CommandParser } from '../client/parser'; import { RedisArgument, RespVersions, TuplesToMapReply, BlobStringReply, NumberReply, ArrayReply, UnwrapReply, Resp2Reply, Command } from '../RESP/types'; export interface HelloOptions { @@ -20,14 +21,14 @@ export type HelloReply = TuplesToMapReply<[ ]>; export default { - transformArguments(protover?: RespVersions, options?: HelloOptions) { - const args: Array = ['HELLO']; + parseCommand(parser: CommandParser, protover?: RespVersions, options?: HelloOptions) { + parser.push('HELLO'); if (protover) { - args.push(protover.toString()); + parser.push(protover.toString()); if (options?.AUTH) { - args.push( + parser.push( 'AUTH', options.AUTH.username, options.AUTH.password @@ -35,14 +36,12 @@ export default { } if (options?.SETNAME) { - args.push( + parser.push( 'SETNAME', options.SETNAME ); } } - - return args; }, transformReply: { 2: (reply: UnwrapReply>) => ({ diff --git a/packages/client/lib/commands/HEXISTS.spec.ts b/packages/client/lib/commands/HEXISTS.spec.ts index 69ca6fa765f..acd462ab7e2 100644 --- a/packages/client/lib/commands/HEXISTS.spec.ts +++ b/packages/client/lib/commands/HEXISTS.spec.ts @@ -1,11 +1,12 @@ import { strict as assert } from 'node:assert'; import testUtils, { GLOBAL } from '../test-utils'; import HEXISTS from './HEXISTS'; +import { parseArgs } from './generic-transformers'; describe('HEXISTS', () => { - it('transformArguments', () => { + it('processCommand', () => { assert.deepEqual( - HEXISTS.transformArguments('key', 'field'), + parseArgs(HEXISTS, 'key', 'field'), ['HEXISTS', 'key', 'field'] ); }); diff --git a/packages/client/lib/commands/HEXISTS.ts b/packages/client/lib/commands/HEXISTS.ts index dc7e937ee78..9bb517b7df4 100644 --- a/packages/client/lib/commands/HEXISTS.ts +++ b/packages/client/lib/commands/HEXISTS.ts @@ -1,10 +1,13 @@ +import { CommandParser } from '../client/parser'; import { RedisArgument, NumberReply, Command } from '../RESP/types'; export default { - FIRST_KEY_INDEX: 1, + CACHEABLE: true, IS_READ_ONLY: true, - transformArguments(key: RedisArgument, field: RedisArgument) { - return ['HEXISTS', key, field]; + parseCommand(parser: CommandParser, key: RedisArgument, field: RedisArgument) { + parser.push('HEXISTS'); + parser.pushKey(key); + parser.push(field); }, transformReply: undefined as unknown as () => NumberReply<0 | 1> } as const satisfies Command; diff --git a/packages/client/lib/commands/HEXPIRE.spec.ts b/packages/client/lib/commands/HEXPIRE.spec.ts index 71c48b7e884..d28cc065ec9 100644 --- a/packages/client/lib/commands/HEXPIRE.spec.ts +++ b/packages/client/lib/commands/HEXPIRE.spec.ts @@ -1,6 +1,7 @@ import { strict as assert } from 'node:assert'; import testUtils, { GLOBAL } from '../test-utils'; import HEXPIRE from './HEXPIRE'; +import { parseArgs } from './generic-transformers'; import { HASH_EXPIRATION_TIME } from './HEXPIRETIME'; describe('HEXPIRE', () => { @@ -9,21 +10,21 @@ describe('HEXPIRE', () => { describe('transformArguments', () => { it('string', () => { assert.deepEqual( - HEXPIRE.transformArguments('key', 'field', 1), + parseArgs(HEXPIRE, 'key', 'field', 1), ['HEXPIRE', 'key', '1', 'FIELDS', '1', 'field'] ); }); it('array', () => { assert.deepEqual( - HEXPIRE.transformArguments('key', ['field1', 'field2'], 1), + parseArgs(HEXPIRE, 'key', ['field1', 'field2'], 1), ['HEXPIRE', 'key', '1', 'FIELDS', '2', 'field1', 'field2'] ); }); it('with set option', () => { assert.deepEqual( - HEXPIRE.transformArguments('key', ['field1'], 1, 'NX'), + parseArgs(HEXPIRE, 'key', ['field1'], 1, 'NX'), ['HEXPIRE', 'key', '1', 'NX', 'FIELDS', '1', 'field1'] ); }); diff --git a/packages/client/lib/commands/HEXPIRE.ts b/packages/client/lib/commands/HEXPIRE.ts index 34b52c1db68..55e2f5a9be1 100644 --- a/packages/client/lib/commands/HEXPIRE.ts +++ b/packages/client/lib/commands/HEXPIRE.ts @@ -1,5 +1,6 @@ -import { Command, RedisArgument } from '../RESP/types'; -import { pushVariadicArgument } from './generic-transformers'; +import { CommandParser } from '../client/parser'; +import { ArrayReply, Command, RedisArgument } from '../RESP/types'; +import { RedisVariadicArgument } from './generic-transformers'; export const HASH_EXPIRATION = { /** The field does not exist */ @@ -11,25 +12,28 @@ export const HASH_EXPIRATION = { /** Field deleted because the specified expiration time is in the past */ DELETED: 2 } as const; - + export type HashExpiration = typeof HASH_EXPIRATION[keyof typeof HASH_EXPIRATION]; export default { - FIRST_KEY_INDEX: 1, - transformArguments(key: RedisArgument, - fields: RedisArgument | Array, + parseCommand( + parser: CommandParser, + key: RedisArgument, + fields: RedisVariadicArgument, seconds: number, - mode?: 'NX' | 'XX' | 'GT' | 'LT', + mode?: 'NX' | 'XX' | 'GT' | 'LT' ) { - const args = ['HEXPIRE', key, seconds.toString()]; - + parser.push('HEXPIRE'); + parser.pushKey(key); + parser.push(seconds.toString()); + if (mode) { - args.push(mode); + parser.push(mode); } - args.push('FIELDS'); + parser.push('FIELDS'); - return pushVariadicArgument(args, fields); + parser.pushVariadicWithLength(fields); }, - transformReply: undefined as unknown as () => Array + transformReply: undefined as unknown as () => ArrayReply } as const satisfies Command; diff --git a/packages/client/lib/commands/HEXPIREAT.spec.ts b/packages/client/lib/commands/HEXPIREAT.spec.ts index 1f87300214c..c7cc9fe749b 100644 --- a/packages/client/lib/commands/HEXPIREAT.spec.ts +++ b/packages/client/lib/commands/HEXPIREAT.spec.ts @@ -2,6 +2,7 @@ import { strict as assert } from 'node:assert'; import testUtils, { GLOBAL } from '../test-utils'; import HEXPIREAT from './HEXPIREAT'; import { HASH_EXPIRATION_TIME } from './HEXPIRETIME'; +import { parseArgs } from './generic-transformers'; describe('HEXPIREAT', () => { testUtils.isVersionGreaterThanHook([7, 4]); @@ -9,14 +10,14 @@ describe('HEXPIREAT', () => { describe('transformArguments', () => { it('string + number', () => { assert.deepEqual( - HEXPIREAT.transformArguments('key', 'field', 1), + parseArgs(HEXPIREAT, 'key', 'field', 1), ['HEXPIREAT', 'key', '1', 'FIELDS', '1', 'field'] ); }); it('array + number', () => { assert.deepEqual( - HEXPIREAT.transformArguments('key', ['field1', 'field2'], 1), + parseArgs(HEXPIREAT, 'key', ['field1', 'field2'], 1), ['HEXPIREAT', 'key', '1', 'FIELDS', '2', 'field1', 'field2'] ); }); @@ -25,14 +26,14 @@ describe('HEXPIREAT', () => { const d = new Date(); assert.deepEqual( - HEXPIREAT.transformArguments('key', ['field1'], d), + parseArgs(HEXPIREAT, 'key', ['field1'], d), ['HEXPIREAT', 'key', Math.floor(d.getTime() / 1000).toString(), 'FIELDS', '1', 'field1'] ); }); it('with set option', () => { assert.deepEqual( - HEXPIREAT.transformArguments('key', 'field1', 1, 'GT'), + parseArgs(HEXPIREAT, 'key', 'field1', 1, 'GT'), ['HEXPIREAT', 'key', '1', 'GT', 'FIELDS', '1', 'field1'] ); }); diff --git a/packages/client/lib/commands/HEXPIREAT.ts b/packages/client/lib/commands/HEXPIREAT.ts index 5a49951f1cd..1370f2ecd65 100644 --- a/packages/client/lib/commands/HEXPIREAT.ts +++ b/packages/client/lib/commands/HEXPIREAT.ts @@ -1,28 +1,26 @@ -import { Command, RedisArgument } from '../RESP/types'; -import { pushVariadicArgument, RedisVariadicArgument, transformEXAT } from './generic-transformers'; -import { HashExpiration } from './HEXPIRE'; +import { CommandParser } from '../client/parser'; +import { RedisVariadicArgument, transformEXAT } from './generic-transformers'; +import { ArrayReply, Command, NumberReply, RedisArgument } from '../RESP/types'; export default { - FIRST_KEY_INDEX: 1, - transformArguments( + parseCommand( + parser: CommandParser, key: RedisArgument, fields: RedisVariadicArgument, timestamp: number | Date, mode?: 'NX' | 'XX' | 'GT' | 'LT' ) { - const args = [ - 'HEXPIREAT', - key, - transformEXAT(timestamp) - ]; - + parser.push('HEXPIREAT'); + parser.pushKey(key); + parser.push(transformEXAT(timestamp)); + if (mode) { - args.push(mode); + parser.push(mode); } - - args.push('FIELDS') - - return pushVariadicArgument(args, fields); + + parser.push('FIELDS') + + parser.pushVariadicWithLength(fields); }, - transformReply: undefined as unknown as () => Array + transformReply: undefined as unknown as () => ArrayReply } as const satisfies Command; diff --git a/packages/client/lib/commands/HEXPIRETIME.spec.ts b/packages/client/lib/commands/HEXPIRETIME.spec.ts index 2335ec91726..32a8730e8a9 100644 --- a/packages/client/lib/commands/HEXPIRETIME.spec.ts +++ b/packages/client/lib/commands/HEXPIRETIME.spec.ts @@ -1,6 +1,7 @@ import { strict as assert } from 'node:assert'; import testUtils, { GLOBAL } from '../test-utils'; import HEXPIRETIME, { HASH_EXPIRATION_TIME } from './HEXPIRETIME'; +import { parseArgs } from './generic-transformers'; describe('HEXPIRETIME', () => { testUtils.isVersionGreaterThanHook([7, 4]); @@ -8,14 +9,14 @@ describe('HEXPIRETIME', () => { describe('transformArguments', () => { it('string', () => { assert.deepEqual( - HEXPIRETIME.transformArguments('key', 'field'), + parseArgs(HEXPIRETIME, 'key', 'field'), ['HEXPIRETIME', 'key', 'FIELDS', '1', 'field'] ); }); it('array', () => { assert.deepEqual( - HEXPIRETIME.transformArguments('key', ['field1', 'field2']), + parseArgs(HEXPIRETIME, 'key', ['field1', 'field2']), ['HEXPIRETIME', 'key', 'FIELDS', '2', 'field1', 'field2'] ); }); diff --git a/packages/client/lib/commands/HEXPIRETIME.ts b/packages/client/lib/commands/HEXPIRETIME.ts index 7edf1309002..697d327db16 100644 --- a/packages/client/lib/commands/HEXPIRETIME.ts +++ b/packages/client/lib/commands/HEXPIRETIME.ts @@ -1,5 +1,6 @@ +import { CommandParser } from '../client/parser'; import { ArrayReply, Command, NumberReply, RedisArgument } from '../RESP/types'; -import { pushVariadicArgument, RedisVariadicArgument } from './generic-transformers'; +import { RedisVariadicArgument } from './generic-transformers'; export const HASH_EXPIRATION_TIME = { /** The field does not exist */ @@ -9,10 +10,16 @@ export const HASH_EXPIRATION_TIME = { } as const; export default { - FIRST_KEY_INDEX: 1, IS_READ_ONLY: true, - transformArguments(key: RedisArgument, fields: RedisVariadicArgument) { - return pushVariadicArgument(['HEXPIRETIME', key, 'FIELDS'], fields); + parseCommand( + parser: CommandParser, + key: RedisArgument, + fields: RedisVariadicArgument + ) { + parser.push('HEXPIRETIME'); + parser.pushKey(key); + parser.push('FIELDS'); + parser.pushVariadicWithLength(fields); }, transformReply: undefined as unknown as () => ArrayReply } as const satisfies Command; diff --git a/packages/client/lib/commands/HGET.spec.ts b/packages/client/lib/commands/HGET.spec.ts index 397f22b5604..47061876aea 100644 --- a/packages/client/lib/commands/HGET.spec.ts +++ b/packages/client/lib/commands/HGET.spec.ts @@ -1,11 +1,12 @@ import { strict as assert } from 'node:assert'; import testUtils, { GLOBAL } from '../test-utils'; import HGET from './HGET'; +import { parseArgs } from './generic-transformers'; describe('HGET', () => { it('transformArguments', () => { assert.deepEqual( - HGET.transformArguments('key', 'field'), + parseArgs(HGET, 'key', 'field'), ['HGET', 'key', 'field'] ); }); diff --git a/packages/client/lib/commands/HGET.ts b/packages/client/lib/commands/HGET.ts index d83f84e24fa..fcd9334eb0a 100644 --- a/packages/client/lib/commands/HGET.ts +++ b/packages/client/lib/commands/HGET.ts @@ -1,10 +1,13 @@ +import { CommandParser } from '../client/parser'; import { RedisArgument, BlobStringReply, NullReply, Command } from '../RESP/types'; export default { - FIRST_KEY_INDEX: 1, + CACHEABLE: true, IS_READ_ONLY: true, - transformArguments(key: RedisArgument, field: RedisArgument) { - return ['HGET', key, field]; + parseCommand(parser: CommandParser, key: RedisArgument, field: RedisArgument) { + parser.push('HGET'); + parser.pushKey(key); + parser.push(field); }, transformReply: undefined as unknown as () => BlobStringReply | NullReply } as const satisfies Command; diff --git a/packages/client/lib/commands/HGETALL.ts b/packages/client/lib/commands/HGETALL.ts index f1f0ac50bcb..a2c3011c4c2 100644 --- a/packages/client/lib/commands/HGETALL.ts +++ b/packages/client/lib/commands/HGETALL.ts @@ -1,11 +1,13 @@ +import { CommandParser } from '../client/parser'; import { RedisArgument, MapReply, BlobStringReply, Command } from '../RESP/types'; import { transformTuplesReply } from './generic-transformers'; export default { - FIRST_KEY_INDEX: 1, + CACHEABLE: true, IS_READ_ONLY: true, - transformArguments(key: RedisArgument) { - return ['HGETALL', key]; + parseCommand(parser: CommandParser, key: RedisArgument) { + parser.push('HGETALL'); + parser.pushKey(key); }, TRANSFORM_LEGACY_REPLY: true, transformReply: { diff --git a/packages/client/lib/commands/HINCRBY.spec.ts b/packages/client/lib/commands/HINCRBY.spec.ts index 7718fe955eb..ad382d97a99 100644 --- a/packages/client/lib/commands/HINCRBY.spec.ts +++ b/packages/client/lib/commands/HINCRBY.spec.ts @@ -1,11 +1,12 @@ import { strict as assert } from 'node:assert'; import testUtils, { GLOBAL } from '../test-utils'; import HINCRBY from './HINCRBY'; +import { parseArgs } from './generic-transformers'; describe('HINCRBY', () => { it('transformArguments', () => { assert.deepEqual( - HINCRBY.transformArguments('key', 'field', 1), + parseArgs(HINCRBY, 'key', 'field', 1), ['HINCRBY', 'key', 'field', '1'] ); }); diff --git a/packages/client/lib/commands/HINCRBY.ts b/packages/client/lib/commands/HINCRBY.ts index cb7f62ebef5..3638e408f7d 100644 --- a/packages/client/lib/commands/HINCRBY.ts +++ b/packages/client/lib/commands/HINCRBY.ts @@ -1,18 +1,16 @@ +import { CommandParser } from '../client/parser'; import { RedisArgument, NumberReply, Command } from '../RESP/types'; export default { - FIRST_KEY_INDEX: 1, - transformArguments( + parseCommand( + parser: CommandParser, key: RedisArgument, field: RedisArgument, increment: number ) { - return [ - 'HINCRBY', - key, - field, - increment.toString() - ]; + parser.push('HINCRBY'); + parser.pushKey(key); + parser.push(field, increment.toString()); }, transformReply: undefined as unknown as () => NumberReply } as const satisfies Command; diff --git a/packages/client/lib/commands/HINCRBYFLOAT.spec.ts b/packages/client/lib/commands/HINCRBYFLOAT.spec.ts index 6c265dc6d10..2edbd6f9477 100644 --- a/packages/client/lib/commands/HINCRBYFLOAT.spec.ts +++ b/packages/client/lib/commands/HINCRBYFLOAT.spec.ts @@ -1,11 +1,12 @@ import { strict as assert } from 'node:assert'; import testUtils, { GLOBAL } from '../test-utils'; import HINCRBYFLOAT from './HINCRBYFLOAT'; +import { parseArgs } from './generic-transformers'; describe('HINCRBYFLOAT', () => { it('transformArguments', () => { assert.deepEqual( - HINCRBYFLOAT.transformArguments('key', 'field', 1.5), + parseArgs(HINCRBYFLOAT, 'key', 'field', 1.5), ['HINCRBYFLOAT', 'key', 'field', '1.5'] ); }); diff --git a/packages/client/lib/commands/HINCRBYFLOAT.ts b/packages/client/lib/commands/HINCRBYFLOAT.ts index a4eea75c827..6d527583c71 100644 --- a/packages/client/lib/commands/HINCRBYFLOAT.ts +++ b/packages/client/lib/commands/HINCRBYFLOAT.ts @@ -1,18 +1,16 @@ +import { CommandParser } from '../client/parser'; import { RedisArgument, BlobStringReply, Command } from '../RESP/types'; export default { - FIRST_KEY_INDEX: 1, - transformArguments( + parseCommand( + parser: CommandParser, key: RedisArgument, field: RedisArgument, increment: number ) { - return [ - 'HINCRBYFLOAT', - key, - field, - increment.toString() - ]; + parser.push('HINCRBYFLOAT'); + parser.pushKey(key); + parser.push(field, increment.toString()); }, transformReply: undefined as unknown as () => BlobStringReply } as const satisfies Command; diff --git a/packages/client/lib/commands/HKEYS.spec.ts b/packages/client/lib/commands/HKEYS.spec.ts index dada7b4d6fd..58445696d20 100644 --- a/packages/client/lib/commands/HKEYS.spec.ts +++ b/packages/client/lib/commands/HKEYS.spec.ts @@ -1,11 +1,12 @@ import { strict as assert } from 'node:assert'; import testUtils, { GLOBAL } from '../test-utils'; import HKEYS from './HKEYS'; +import { parseArgs } from './generic-transformers'; describe('HKEYS', () => { it('transformArguments', () => { assert.deepEqual( - HKEYS.transformArguments('key'), + parseArgs(HKEYS, 'key'), ['HKEYS', 'key'] ); }); diff --git a/packages/client/lib/commands/HKEYS.ts b/packages/client/lib/commands/HKEYS.ts index 00af43f7a40..f07a1ac127f 100644 --- a/packages/client/lib/commands/HKEYS.ts +++ b/packages/client/lib/commands/HKEYS.ts @@ -1,10 +1,12 @@ +import { CommandParser } from '../client/parser'; import { RedisArgument, ArrayReply, BlobStringReply, Command } from '../RESP/types'; export default { - FIRST_KEY_INDEX: 1, + CACHEABLE: true, IS_READ_ONLY: true, - transformArguments(key: RedisArgument) { - return ['HKEYS', key]; + parseCommand(parser: CommandParser, key: RedisArgument) { + parser.push('HKEYS') + parser.pushKey(key); }, transformReply: undefined as unknown as () => ArrayReply } as const satisfies Command; diff --git a/packages/client/lib/commands/HLEN.spec.ts b/packages/client/lib/commands/HLEN.spec.ts index 2457a261299..640e461ad07 100644 --- a/packages/client/lib/commands/HLEN.spec.ts +++ b/packages/client/lib/commands/HLEN.spec.ts @@ -1,11 +1,12 @@ import { strict as assert } from 'node:assert'; import testUtils, { GLOBAL } from '../test-utils'; import HLEN from './HLEN'; +import { parseArgs } from './generic-transformers'; describe('HLEN', () => { it('transformArguments', () => { assert.deepEqual( - HLEN.transformArguments('key'), + parseArgs(HLEN, 'key'), ['HLEN', 'key'] ); }); diff --git a/packages/client/lib/commands/HLEN.ts b/packages/client/lib/commands/HLEN.ts index 8f156d303e2..e3b89da3e7d 100644 --- a/packages/client/lib/commands/HLEN.ts +++ b/packages/client/lib/commands/HLEN.ts @@ -1,10 +1,12 @@ +import { CommandParser } from '../client/parser'; import { RedisArgument, NumberReply, Command } from '../RESP/types'; export default { - FIRST_KEY_INDEX: 1, + CACHEABLE: true, IS_READ_ONLY: true, - transformArguments(key: RedisArgument) { - return ['HLEN', key]; + parseCommand(parser: CommandParser, key: RedisArgument) { + parser.push('HLEN'); + parser.pushKey(key); }, transformReply: undefined as unknown as () => NumberReply } as const satisfies Command; diff --git a/packages/client/lib/commands/HMGET.spec.ts b/packages/client/lib/commands/HMGET.spec.ts index 99d94a6d375..8cc90e4abd5 100644 --- a/packages/client/lib/commands/HMGET.spec.ts +++ b/packages/client/lib/commands/HMGET.spec.ts @@ -1,19 +1,20 @@ import { strict as assert } from 'node:assert'; import testUtils, { GLOBAL } from '../test-utils'; import HMGET from './HMGET'; +import { parseArgs } from './generic-transformers'; describe('HMGET', () => { - describe('transformArguments', () => { + describe('parseCommand', () => { it('string', () => { assert.deepEqual( - HMGET.transformArguments('key', 'field'), + parseArgs(HMGET, 'key', 'field'), ['HMGET', 'key', 'field'] ); }); it('array', () => { assert.deepEqual( - HMGET.transformArguments('key', ['field1', 'field2']), + parseArgs(HMGET, 'key', ['field1', 'field2']), ['HMGET', 'key', 'field1', 'field2'] ); }); diff --git a/packages/client/lib/commands/HMGET.ts b/packages/client/lib/commands/HMGET.ts index df28a64be09..51ba937339f 100644 --- a/packages/client/lib/commands/HMGET.ts +++ b/packages/client/lib/commands/HMGET.ts @@ -1,14 +1,14 @@ +import { CommandParser } from '../client/parser'; import { RedisArgument, ArrayReply, BlobStringReply, NullReply, Command } from '../RESP/types'; -import { RedisVariadicArgument, pushVariadicArguments } from './generic-transformers'; +import { RedisVariadicArgument } from './generic-transformers'; export default { - FIRST_KEY_INDEX: 1, + CACHEABLE: true, IS_READ_ONLY: true, - transformArguments( - key: RedisArgument, - fields: RedisVariadicArgument - ) { - return pushVariadicArguments(['HMGET', key], fields); + parseCommand(parser: CommandParser, key: RedisArgument, fields: RedisVariadicArgument) { + parser.push('HMGET'); + parser.pushKey(key); + parser.pushVariadic(fields); }, transformReply: undefined as unknown as () => ArrayReply } as const satisfies Command; diff --git a/packages/client/lib/commands/HPERSIST.spec.ts b/packages/client/lib/commands/HPERSIST.spec.ts index 05e225e8ead..0b317977cbf 100644 --- a/packages/client/lib/commands/HPERSIST.spec.ts +++ b/packages/client/lib/commands/HPERSIST.spec.ts @@ -2,6 +2,7 @@ import { strict as assert } from 'node:assert'; import testUtils, { GLOBAL } from '../test-utils'; import HPERSIST from './HPERSIST'; import { HASH_EXPIRATION_TIME } from './HEXPIRETIME'; +import { parseArgs } from './generic-transformers'; describe('HPERSIST', () => { testUtils.isVersionGreaterThanHook([7, 4]); @@ -9,14 +10,14 @@ describe('HPERSIST', () => { describe('transformArguments', () => { it('string', () => { assert.deepEqual( - HPERSIST.transformArguments('key', 'field'), + parseArgs(HPERSIST, 'key', 'field'), ['HPERSIST', 'key', 'FIELDS', '1', 'field'] ); }); it('array', () => { assert.deepEqual( - HPERSIST.transformArguments('key', ['field1', 'field2']), + parseArgs(HPERSIST, 'key', ['field1', 'field2']), ['HPERSIST', 'key', 'FIELDS', '2', 'field1', 'field2'] ); }); diff --git a/packages/client/lib/commands/HPERSIST.ts b/packages/client/lib/commands/HPERSIST.ts index 3843fd80a5e..fd0f320e65a 100644 --- a/packages/client/lib/commands/HPERSIST.ts +++ b/packages/client/lib/commands/HPERSIST.ts @@ -1,11 +1,17 @@ +import { CommandParser } from '../client/parser'; import { ArrayReply, Command, NullReply, NumberReply, RedisArgument } from '../RESP/types'; -import { pushVariadicArgument, RedisVariadicArgument } from './generic-transformers'; +import { RedisVariadicArgument } from './generic-transformers'; export default { - FIRST_KEY_INDEX: 1, - IS_READ_ONLY: true, - transformArguments(key: RedisArgument, fields: RedisVariadicArgument) { - return pushVariadicArgument(['HPERSIST', key, 'FIELDS'], fields); + parseCommand( + parser: CommandParser, + key: RedisArgument, + fields: RedisVariadicArgument + ) { + parser.push('HPERSIST'); + parser.pushKey(key); + parser.push('FIELDS'); + parser.pushVariadicWithLength(fields); }, transformReply: undefined as unknown as () => ArrayReply | NullReply } as const satisfies Command; diff --git a/packages/client/lib/commands/HPEXPIRE.spec.ts b/packages/client/lib/commands/HPEXPIRE.spec.ts index febcb0bc96b..2f68fb9b7f3 100644 --- a/packages/client/lib/commands/HPEXPIRE.spec.ts +++ b/packages/client/lib/commands/HPEXPIRE.spec.ts @@ -2,6 +2,7 @@ import { strict as assert } from 'node:assert'; import testUtils, { GLOBAL } from '../test-utils'; import HPEXPIRE from './HPEXPIRE'; import { HASH_EXPIRATION_TIME } from './HEXPIRETIME'; +import { parseArgs } from './generic-transformers'; describe('HEXPIRE', () => { testUtils.isVersionGreaterThanHook([7, 4]); @@ -9,21 +10,21 @@ describe('HEXPIRE', () => { describe('transformArguments', () => { it('string', () => { assert.deepEqual( - HPEXPIRE.transformArguments('key', 'field', 1), + parseArgs(HPEXPIRE, 'key', 'field', 1), ['HPEXPIRE', 'key', '1', 'FIELDS', '1', 'field'] ); }); it('array', () => { assert.deepEqual( - HPEXPIRE.transformArguments('key', ['field1', 'field2'], 1), + parseArgs(HPEXPIRE, 'key', ['field1', 'field2'], 1), ['HPEXPIRE', 'key', '1', 'FIELDS', '2', 'field1', 'field2'] ); }); it('with set option', () => { assert.deepEqual( - HPEXPIRE.transformArguments('key', ['field1'], 1, 'NX'), + parseArgs(HPEXPIRE, 'key', ['field1'], 1, 'NX'), ['HPEXPIRE', 'key', '1', 'NX', 'FIELDS', '1', 'field1'] ); }); diff --git a/packages/client/lib/commands/HPEXPIRE.ts b/packages/client/lib/commands/HPEXPIRE.ts index 58624f9163a..34513c34e3b 100644 --- a/packages/client/lib/commands/HPEXPIRE.ts +++ b/packages/client/lib/commands/HPEXPIRE.ts @@ -1,24 +1,27 @@ +import { CommandParser } from '../client/parser'; import { ArrayReply, Command, NullReply, RedisArgument } from '../RESP/types'; -import { pushVariadicArgument, RedisVariadicArgument } from './generic-transformers'; -import { HashExpiration } from "./HEXPIRE"; +import { RedisVariadicArgument } from './generic-transformers'; +import { HashExpiration } from './HEXPIRE'; export default { - FIRST_KEY_INDEX: 1, - transformArguments( + parseCommand( + parser: CommandParser, key: RedisArgument, fields: RedisVariadicArgument, ms: number, mode?: 'NX' | 'XX' | 'GT' | 'LT', ) { - const args = ['HPEXPIRE', key, ms.toString()]; - + parser.push('HPEXPIRE'); + parser.pushKey(key); + parser.push(ms.toString()); + if (mode) { - args.push(mode); + parser.push(mode); } - - args.push('FIELDS') - - return pushVariadicArgument(args, fields); + + parser.push('FIELDS') + + parser.pushVariadicWithLength(fields); }, transformReply: undefined as unknown as () => ArrayReply | NullReply } as const satisfies Command; diff --git a/packages/client/lib/commands/HPEXPIREAT.spec.ts b/packages/client/lib/commands/HPEXPIREAT.spec.ts index f91bf967cf8..7c369980bf4 100644 --- a/packages/client/lib/commands/HPEXPIREAT.spec.ts +++ b/packages/client/lib/commands/HPEXPIREAT.spec.ts @@ -2,6 +2,7 @@ import { strict as assert } from 'node:assert'; import testUtils, { GLOBAL } from '../test-utils'; import HPEXPIREAT from './HPEXPIREAT'; import { HASH_EXPIRATION_TIME } from './HEXPIRETIME'; +import { parseArgs } from './generic-transformers'; describe('HPEXPIREAT', () => { testUtils.isVersionGreaterThanHook([7, 4]); @@ -9,14 +10,14 @@ describe('HPEXPIREAT', () => { describe('transformArguments', () => { it('string + number', () => { assert.deepEqual( - HPEXPIREAT.transformArguments('key', 'field', 1), + parseArgs(HPEXPIREAT, 'key', 'field', 1), ['HPEXPIREAT', 'key', '1', 'FIELDS', '1', 'field'] ); }); it('array + number', () => { assert.deepEqual( - HPEXPIREAT.transformArguments('key', ['field1', 'field2'], 1), + parseArgs(HPEXPIREAT, 'key', ['field1', 'field2'], 1), ['HPEXPIREAT', 'key', '1', 'FIELDS', '2', 'field1', 'field2'] ); }); @@ -24,14 +25,14 @@ describe('HPEXPIREAT', () => { it('date', () => { const d = new Date(); assert.deepEqual( - HPEXPIREAT.transformArguments('key', ['field1'], d), + parseArgs(HPEXPIREAT, 'key', ['field1'], d), ['HPEXPIREAT', 'key', d.getTime().toString(), 'FIELDS', '1', 'field1'] ); }); it('with set option', () => { assert.deepEqual( - HPEXPIREAT.transformArguments('key', ['field1'], 1, 'XX'), + parseArgs(HPEXPIREAT, 'key', ['field1'], 1, 'XX'), ['HPEXPIREAT', 'key', '1', 'XX', 'FIELDS', '1', 'field1'] ); }); diff --git a/packages/client/lib/commands/HPEXPIREAT.ts b/packages/client/lib/commands/HPEXPIREAT.ts index a6250d99432..14288d7ae90 100644 --- a/packages/client/lib/commands/HPEXPIREAT.ts +++ b/packages/client/lib/commands/HPEXPIREAT.ts @@ -1,24 +1,28 @@ +import { CommandParser } from '../client/parser'; import { ArrayReply, Command, NullReply, RedisArgument } from '../RESP/types'; -import { pushVariadicArgument, RedisVariadicArgument, transformPXAT } from './generic-transformers'; +import { RedisVariadicArgument, transformPXAT } from './generic-transformers'; import { HashExpiration } from './HEXPIRE'; export default { - FIRST_KEY_INDEX: 1, - transformArguments( + IS_READ_ONLY: true, + parseCommand( + parser: CommandParser, key: RedisArgument, fields: RedisVariadicArgument, timestamp: number | Date, mode?: 'NX' | 'XX' | 'GT' | 'LT' ) { - const args = ['HPEXPIREAT', key, transformPXAT(timestamp)]; + parser.push('HPEXPIREAT'); + parser.pushKey(key); + parser.push(transformPXAT(timestamp)); if (mode) { - args.push(mode); + parser.push(mode); } - args.push('FIELDS') + parser.push('FIELDS') - return pushVariadicArgument(args, fields); + parser.pushVariadicWithLength(fields); }, transformReply: undefined as unknown as () => ArrayReply | NullReply } as const satisfies Command; diff --git a/packages/client/lib/commands/HPEXPIRETIME.spec.ts b/packages/client/lib/commands/HPEXPIRETIME.spec.ts index a66988c428c..5673a725afc 100644 --- a/packages/client/lib/commands/HPEXPIRETIME.spec.ts +++ b/packages/client/lib/commands/HPEXPIRETIME.spec.ts @@ -2,6 +2,7 @@ import { strict as assert } from 'node:assert'; import testUtils, { GLOBAL } from '../test-utils'; import HPEXPIRETIME from './HPEXPIRETIME'; import { HASH_EXPIRATION_TIME } from './HEXPIRETIME'; +import { parseArgs } from './generic-transformers'; describe('HPEXPIRETIME', () => { testUtils.isVersionGreaterThanHook([7, 4]); @@ -9,14 +10,14 @@ describe('HPEXPIRETIME', () => { describe('transformArguments', () => { it('string', () => { assert.deepEqual( - HPEXPIRETIME.transformArguments('key', 'field'), + parseArgs(HPEXPIRETIME, 'key', 'field'), ['HPEXPIRETIME', 'key', 'FIELDS', '1', 'field'] ); }); it('array', () => { assert.deepEqual( - HPEXPIRETIME.transformArguments('key', ['field1', 'field2']), + parseArgs(HPEXPIRETIME, 'key', ['field1', 'field2']), ['HPEXPIRETIME', 'key', 'FIELDS', '2', 'field1', 'field2'] ); }); diff --git a/packages/client/lib/commands/HPEXPIRETIME.ts b/packages/client/lib/commands/HPEXPIRETIME.ts index acdccf25119..cacce25a85f 100644 --- a/packages/client/lib/commands/HPEXPIRETIME.ts +++ b/packages/client/lib/commands/HPEXPIRETIME.ts @@ -1,11 +1,18 @@ +import { CommandParser } from '../client/parser'; import { ArrayReply, Command, NullReply, NumberReply, RedisArgument } from '../RESP/types'; -import { pushVariadicArgument, RedisVariadicArgument } from './generic-transformers'; +import { RedisVariadicArgument } from './generic-transformers'; export default { - FIRST_KEY_INDEX: 1, IS_READ_ONLY: true, - transformArguments(key: RedisArgument, fields: RedisVariadicArgument) { - return pushVariadicArgument(['HPEXPIRETIME', key, 'FIELDS'], fields); + parseCommand( + parser: CommandParser, + key: RedisArgument, + fields: RedisVariadicArgument, + ) { + parser.push('HPEXPIRETIME'); + parser.pushKey(key); + parser.push('FIELDS'); + parser.pushVariadicWithLength(fields); }, transformReply: undefined as unknown as () => ArrayReply | NullReply } as const satisfies Command; diff --git a/packages/client/lib/commands/HPTTL.spec.ts b/packages/client/lib/commands/HPTTL.spec.ts index 7280ef841ca..baaa11b19c8 100644 --- a/packages/client/lib/commands/HPTTL.spec.ts +++ b/packages/client/lib/commands/HPTTL.spec.ts @@ -2,6 +2,7 @@ import { strict as assert } from 'node:assert'; import testUtils, { GLOBAL } from '../test-utils'; import HPTTL from './HPTTL'; import { HASH_EXPIRATION_TIME } from './HEXPIRETIME'; +import { parseArgs } from './generic-transformers'; describe('HPTTL', () => { testUtils.isVersionGreaterThanHook([7, 4]); @@ -9,14 +10,14 @@ describe('HPTTL', () => { describe('transformArguments', () => { it('string', () => { assert.deepEqual( - HPTTL.transformArguments('key', 'field'), + parseArgs(HPTTL, 'key', 'field'), ['HPTTL', 'key', 'FIELDS', '1', 'field'] ); }); it('array', () => { assert.deepEqual( - HPTTL.transformArguments('key', ['field1', 'field2']), + parseArgs(HPTTL, 'key', ['field1', 'field2']), ['HPTTL', 'key', 'FIELDS', '2', 'field1', 'field2'] ); }); diff --git a/packages/client/lib/commands/HPTTL.ts b/packages/client/lib/commands/HPTTL.ts index 4ab069db74e..b9cd54a850d 100644 --- a/packages/client/lib/commands/HPTTL.ts +++ b/packages/client/lib/commands/HPTTL.ts @@ -1,11 +1,18 @@ +import { CommandParser } from '../client/parser'; import { ArrayReply, Command, NullReply, NumberReply, RedisArgument } from '../RESP/types'; -import { pushVariadicArgument, RedisVariadicArgument } from './generic-transformers'; +import { RedisVariadicArgument } from './generic-transformers'; export default { - FIRST_KEY_INDEX: 1, IS_READ_ONLY: true, - transformArguments(key: RedisArgument, fields: RedisVariadicArgument) { - return pushVariadicArgument(['HPTTL', key, 'FIELDS'], fields); + parseCommand( + parser: CommandParser, + key: RedisArgument, + fields: RedisVariadicArgument + ) { + parser.push('HPTTL'); + parser.pushKey(key); + parser.push('FIELDS'); + parser.pushVariadicWithLength(fields); }, transformReply: undefined as unknown as () => ArrayReply | NullReply } as const satisfies Command; diff --git a/packages/client/lib/commands/HRANDFIELD.spec.ts b/packages/client/lib/commands/HRANDFIELD.spec.ts index 33f2d281803..151636057a0 100644 --- a/packages/client/lib/commands/HRANDFIELD.spec.ts +++ b/packages/client/lib/commands/HRANDFIELD.spec.ts @@ -1,13 +1,14 @@ import { strict as assert } from 'node:assert'; import testUtils, { GLOBAL } from '../test-utils'; import HRANDFIELD from './HRANDFIELD'; +import { parseArgs } from './generic-transformers'; describe('HRANDFIELD', () => { testUtils.isVersionGreaterThanHook([6, 2]); it('transformArguments', () => { assert.deepEqual( - HRANDFIELD.transformArguments('key'), + parseArgs(HRANDFIELD, 'key'), ['HRANDFIELD', 'key'] ); }); diff --git a/packages/client/lib/commands/HRANDFIELD.ts b/packages/client/lib/commands/HRANDFIELD.ts index be878e244a0..3383b94dcb2 100644 --- a/packages/client/lib/commands/HRANDFIELD.ts +++ b/packages/client/lib/commands/HRANDFIELD.ts @@ -1,10 +1,11 @@ +import { CommandParser } from '../client/parser'; import { RedisArgument, BlobStringReply, NullReply, Command } from '../RESP/types'; export default { - FIRST_KEY_INDEX: 1, IS_READ_ONLY: true, - transformArguments(key: RedisArgument) { - return ['HRANDFIELD', key]; + parseCommand(parser: CommandParser, key: RedisArgument) { + parser.push('HRANDFIELD'); + parser.pushKey(key); }, transformReply: undefined as unknown as () => BlobStringReply | NullReply } as const satisfies Command; diff --git a/packages/client/lib/commands/HRANDFIELD_COUNT.spec.ts b/packages/client/lib/commands/HRANDFIELD_COUNT.spec.ts index 99788dc4962..ee3fc984d55 100644 --- a/packages/client/lib/commands/HRANDFIELD_COUNT.spec.ts +++ b/packages/client/lib/commands/HRANDFIELD_COUNT.spec.ts @@ -1,13 +1,14 @@ import { strict as assert } from 'node:assert'; import testUtils, { GLOBAL } from '../test-utils'; import HRANDFIELD_COUNT from './HRANDFIELD_COUNT'; +import { parseArgs } from './generic-transformers'; describe('HRANDFIELD COUNT', () => { testUtils.isVersionGreaterThanHook([6, 2, 5]); it('transformArguments', () => { assert.deepEqual( - HRANDFIELD_COUNT.transformArguments('key', 1), + parseArgs(HRANDFIELD_COUNT, 'key', 1), ['HRANDFIELD', 'key', '1'] ); }); diff --git a/packages/client/lib/commands/HRANDFIELD_COUNT.ts b/packages/client/lib/commands/HRANDFIELD_COUNT.ts index 4b6f42a115b..62abe97e350 100644 --- a/packages/client/lib/commands/HRANDFIELD_COUNT.ts +++ b/packages/client/lib/commands/HRANDFIELD_COUNT.ts @@ -1,10 +1,12 @@ +import { CommandParser } from '../client/parser'; import { RedisArgument, ArrayReply, BlobStringReply, Command } from '../RESP/types'; export default { - FIRST_KEY_INDEX: 1, IS_READ_ONLY: true, - transformArguments(key: RedisArgument, count: number) { - return ['HRANDFIELD', key, count.toString()]; + parseCommand(parser: CommandParser, key: RedisArgument, count: number) { + parser.push('HRANDFIELD'); + parser.pushKey(key); + parser.push(count.toString()); }, transformReply: undefined as unknown as () => ArrayReply } as const satisfies Command; diff --git a/packages/client/lib/commands/HRANDFIELD_COUNT_WITHVALUES.ts b/packages/client/lib/commands/HRANDFIELD_COUNT_WITHVALUES.ts index ab36183c4ad..aa8ebad1b93 100644 --- a/packages/client/lib/commands/HRANDFIELD_COUNT_WITHVALUES.ts +++ b/packages/client/lib/commands/HRANDFIELD_COUNT_WITHVALUES.ts @@ -1,3 +1,4 @@ +import { CommandParser } from '../client/parser'; import { RedisArgument, ArrayReply, TuplesReply, BlobStringReply, UnwrapReply, Command } from '../RESP/types'; export type HRandFieldCountWithValuesReply = Array<{ @@ -6,10 +7,11 @@ export type HRandFieldCountWithValuesReply = Array<{ }>; export default { - FIRST_KEY_INDEX: 1, IS_READ_ONLY: true, - transformArguments(key: RedisArgument, count: number) { - return ['HRANDFIELD', key, count.toString(), 'WITHVALUES']; + parseCommand(parser: CommandParser, key: RedisArgument, count: number) { + parser.push('HRANDFIELD'); + parser.pushKey(key); + parser.push(count.toString(), 'WITHVALUES'); }, transformReply: { 2: (rawReply: UnwrapReply>) => { diff --git a/packages/client/lib/commands/HSCAN.spec.ts b/packages/client/lib/commands/HSCAN.spec.ts index a5f3cdca16c..9e489f6190d 100644 --- a/packages/client/lib/commands/HSCAN.spec.ts +++ b/packages/client/lib/commands/HSCAN.spec.ts @@ -1,19 +1,20 @@ import { strict as assert } from 'node:assert'; import testUtils, { GLOBAL } from '../test-utils'; +import { parseArgs } from './generic-transformers'; import HSCAN from './HSCAN'; describe('HSCAN', () => { describe('transformArguments', () => { it('cusror only', () => { assert.deepEqual( - HSCAN.transformArguments('key', '0'), + parseArgs(HSCAN, 'key', '0'), ['HSCAN', 'key', '0'] ); }); it('with MATCH', () => { assert.deepEqual( - HSCAN.transformArguments('key', '0', { + parseArgs(HSCAN, 'key', '0', { MATCH: 'pattern' }), ['HSCAN', 'key', '0', 'MATCH', 'pattern'] @@ -22,7 +23,7 @@ describe('HSCAN', () => { it('with COUNT', () => { assert.deepEqual( - HSCAN.transformArguments('key', '0', { + parseArgs(HSCAN, 'key', '0', { COUNT: 1 }), ['HSCAN', 'key', '0', 'COUNT', '1'] @@ -31,7 +32,7 @@ describe('HSCAN', () => { it('with MATCH & COUNT', () => { assert.deepEqual( - HSCAN.transformArguments('key', '0', { + parseArgs(HSCAN, 'key', '0', { MATCH: 'pattern', COUNT: 1 }), diff --git a/packages/client/lib/commands/HSCAN.ts b/packages/client/lib/commands/HSCAN.ts index db52db99fef..e1e40663a07 100644 --- a/packages/client/lib/commands/HSCAN.ts +++ b/packages/client/lib/commands/HSCAN.ts @@ -1,5 +1,6 @@ +import { CommandParser } from '../client/parser'; import { RedisArgument, BlobStringReply, Command } from '../RESP/types'; -import { ScanCommonOptions, pushScanArguments } from './SCAN'; +import { ScanCommonOptions, parseScanArguments } from './SCAN'; export interface HScanEntry { field: BlobStringReply; @@ -7,14 +8,16 @@ export interface HScanEntry { } export default { - FIRST_KEY_INDEX: 1, IS_READ_ONLY: true, - transformArguments( + parseCommand( + parser: CommandParser, key: RedisArgument, cursor: RedisArgument, options?: ScanCommonOptions ) { - return pushScanArguments(['HSCAN', key], cursor, options); + parser.push('HSCAN'); + parser.pushKey(key); + parseScanArguments(parser, cursor, options); }, transformReply([cursor, rawEntries]: [BlobStringReply, Array]) { const entries = []; diff --git a/packages/client/lib/commands/HSCAN_NOVALUES.spec.ts b/packages/client/lib/commands/HSCAN_NOVALUES.spec.ts index 1283a116dc5..83a452a6897 100644 --- a/packages/client/lib/commands/HSCAN_NOVALUES.spec.ts +++ b/packages/client/lib/commands/HSCAN_NOVALUES.spec.ts @@ -1,7 +1,7 @@ import { strict as assert } from 'node:assert'; import testUtils, { GLOBAL } from '../test-utils'; import HSCAN_NOVALUES from './HSCAN_NOVALUES'; -import { BlobStringReply } from '../RESP/types'; +import { parseArgs } from './generic-transformers'; describe('HSCAN_NOVALUES', () => { testUtils.isVersionGreaterThanHook([7,4]); @@ -9,14 +9,14 @@ describe('HSCAN_NOVALUES', () => { describe('transformArguments', () => { it('cusror only', () => { assert.deepEqual( - HSCAN_NOVALUES.transformArguments('key', '0'), + parseArgs(HSCAN_NOVALUES, 'key', '0'), ['HSCAN', 'key', '0', 'NOVALUES'] ); }); it('with MATCH', () => { assert.deepEqual( - HSCAN_NOVALUES.transformArguments('key', '0', { + parseArgs(HSCAN_NOVALUES, 'key', '0', { MATCH: 'pattern' }), ['HSCAN', 'key', '0', 'MATCH', 'pattern', 'NOVALUES'] @@ -25,7 +25,7 @@ describe('HSCAN_NOVALUES', () => { it('with COUNT', () => { assert.deepEqual( - HSCAN_NOVALUES.transformArguments('key', '0', { + parseArgs(HSCAN_NOVALUES, 'key', '0', { COUNT: 1 }), ['HSCAN', 'key', '0', 'COUNT', '1', 'NOVALUES'] @@ -34,7 +34,7 @@ describe('HSCAN_NOVALUES', () => { it('with MATCH & COUNT', () => { assert.deepEqual( - HSCAN_NOVALUES.transformArguments('key', '0', { + parseArgs(HSCAN_NOVALUES, 'key', '0', { MATCH: 'pattern', COUNT: 1 }), diff --git a/packages/client/lib/commands/HSCAN_NOVALUES.ts b/packages/client/lib/commands/HSCAN_NOVALUES.ts index 35ff861338c..eff61a7aab0 100644 --- a/packages/client/lib/commands/HSCAN_NOVALUES.ts +++ b/packages/client/lib/commands/HSCAN_NOVALUES.ts @@ -1,17 +1,13 @@ -import { RedisArgument, BlobStringReply, Command } from '../RESP/types'; -import { ScanCommonOptions, pushScanArguments } from './SCAN'; +import { BlobStringReply, Command } from '../RESP/types'; +import HSCAN from './HSCAN'; export default { - FIRST_KEY_INDEX: 1, IS_READ_ONLY: true, - transformArguments( - key: RedisArgument, - cursor: RedisArgument, - options?: ScanCommonOptions - ) { - const args = pushScanArguments(['HSCAN', key], cursor, options); - args.push('NOVALUES'); - return args; + parseCommand(...args: Parameters) { + const parser = args[0]; + + HSCAN.parseCommand(...args); + parser.push('NOVALUES'); }, transformReply([cursor, fields]: [BlobStringReply, Array]) { return { diff --git a/packages/client/lib/commands/HSET.spec.ts b/packages/client/lib/commands/HSET.spec.ts index eb5fdcd9b32..2cb53e6485a 100644 --- a/packages/client/lib/commands/HSET.spec.ts +++ b/packages/client/lib/commands/HSET.spec.ts @@ -1,27 +1,28 @@ import { strict as assert } from 'node:assert'; import testUtils, { GLOBAL } from '../test-utils'; import HSET from './HSET'; +import { parseArgs } from './generic-transformers'; describe('HSET', () => { describe('transformArguments', () => { describe('field, value', () => { it('string', () => { assert.deepEqual( - HSET.transformArguments('key', 'field', 'value'), + parseArgs(HSET, 'key', 'field', 'value'), ['HSET', 'key', 'field', 'value'] ); }); it('number', () => { assert.deepEqual( - HSET.transformArguments('key', 1, 2), + parseArgs(HSET, 'key', 1, 2), ['HSET', 'key', '1', '2'] ); }); it('Buffer', () => { assert.deepEqual( - HSET.transformArguments(Buffer.from('key'), Buffer.from('field'), Buffer.from('value')), + parseArgs(HSET, Buffer.from('key'), Buffer.from('field'), Buffer.from('value')), ['HSET', Buffer.from('key'), Buffer.from('field'), Buffer.from('value')] ); }); @@ -29,14 +30,14 @@ describe('HSET', () => { it('Map', () => { assert.deepEqual( - HSET.transformArguments('key', new Map([['field', 'value']])), + parseArgs(HSET, 'key', new Map([['field', 'value']])), ['HSET', 'key', 'field', 'value'] ); }); it('Array', () => { assert.deepEqual( - HSET.transformArguments('key', [['field', 'value']]), + parseArgs(HSET, 'key', [['field', 'value']]), ['HSET', 'key', 'field', 'value'] ); }); @@ -44,14 +45,14 @@ describe('HSET', () => { describe('Object', () => { it('string', () => { assert.deepEqual( - HSET.transformArguments('key', { field: 'value' }), + parseArgs(HSET, 'key', { field: 'value' }), ['HSET', 'key', 'field', 'value'] ); }); it('Buffer', () => { assert.deepEqual( - HSET.transformArguments('key', { field: Buffer.from('value') }), + parseArgs(HSET, 'key', { field: Buffer.from('value') }), ['HSET', 'key', 'field', Buffer.from('value')] ); }); diff --git a/packages/client/lib/commands/HSET.ts b/packages/client/lib/commands/HSET.ts index e14aa9d06f6..1f50aeacf03 100644 --- a/packages/client/lib/commands/HSET.ts +++ b/packages/client/lib/commands/HSET.ts @@ -1,3 +1,4 @@ +import { CommandParser } from '../client/parser'; import { RedisArgument, NumberReply, Command } from '../RESP/types'; export type HashTypes = RedisArgument | number; @@ -17,51 +18,49 @@ type MultipleFieldsArguments = [...generic: GenericArguments, value: HSETObject export type HSETArguments = SingleFieldArguments | MultipleFieldsArguments; export default { - FIRST_KEY_INDEX: 1, - transformArguments(...[key, value, fieldValue]: SingleFieldArguments | MultipleFieldsArguments) { - const args: Array = ['HSET', key]; + parseCommand(parser: CommandParser, ...[key, value, fieldValue]: SingleFieldArguments | MultipleFieldsArguments) { + parser.push('HSET'); + parser.pushKey(key); if (typeof value === 'string' || typeof value === 'number' || value instanceof Buffer) { - args.push( + parser.push( convertValue(value), convertValue(fieldValue!) ); } else if (value instanceof Map) { - pushMap(args, value); + pushMap(parser, value); } else if (Array.isArray(value)) { - pushTuples(args, value); + pushTuples(parser, value); } else { - pushObject(args, value); + pushObject(parser, value); } - - return args; }, transformReply: undefined as unknown as () => NumberReply } as const satisfies Command; -function pushMap(args: Array, map: HSETMap): void { +function pushMap(parser: CommandParser, map: HSETMap): void { for (const [key, value] of map.entries()) { - args.push( + parser.push( convertValue(key), convertValue(value) ); } } -function pushTuples(args: Array, tuples: HSETTuples): void { +function pushTuples(parser: CommandParser, tuples: HSETTuples): void { for (const tuple of tuples) { if (Array.isArray(tuple)) { - pushTuples(args, tuple); + pushTuples(parser, tuple); continue; } - args.push(convertValue(tuple)); + parser.push(convertValue(tuple)); } } -function pushObject(args: Array, object: HSETObject): void { +function pushObject(parser: CommandParser, object: HSETObject): void { for (const key of Object.keys(object)) { - args.push( + parser.push( convertValue(key), convertValue(object[key]) ); diff --git a/packages/client/lib/commands/HSETNX.spec.ts b/packages/client/lib/commands/HSETNX.spec.ts index 522732624e1..e65f9fb219c 100644 --- a/packages/client/lib/commands/HSETNX.spec.ts +++ b/packages/client/lib/commands/HSETNX.spec.ts @@ -1,11 +1,12 @@ import { strict as assert } from 'node:assert'; import testUtils, { GLOBAL } from '../test-utils'; import HSETNX from './HSETNX'; +import { parseArgs } from './generic-transformers'; describe('HSETNX', () => { it('transformArguments', () => { assert.deepEqual( - HSETNX.transformArguments('key', 'field', 'value'), + parseArgs(HSETNX, 'key', 'field', 'value'), ['HSETNX', 'key', 'field', 'value'] ); }); diff --git a/packages/client/lib/commands/HSETNX.ts b/packages/client/lib/commands/HSETNX.ts index d26c42a76b4..130d7cd81d3 100644 --- a/packages/client/lib/commands/HSETNX.ts +++ b/packages/client/lib/commands/HSETNX.ts @@ -1,14 +1,17 @@ +import { CommandParser } from '../client/parser'; import { RedisArgument, Command, NumberReply } from '../RESP/types'; export default { - FIRST_KEY_INDEX: 1, IS_READ_ONLY: true, - transformArguments( + parseCommand( + parser: CommandParser, key: RedisArgument, field: RedisArgument, value: RedisArgument ) { - return ['HSETNX', key, field, value]; + parser.push('HSETNX'); + parser.pushKey(key); + parser.push(field, value); }, transformReply: undefined as unknown as () => NumberReply<0 | 1> } as const satisfies Command; diff --git a/packages/client/lib/commands/HSTRLEN.spec.ts b/packages/client/lib/commands/HSTRLEN.spec.ts index 59b737b692b..47dd0eaf795 100644 --- a/packages/client/lib/commands/HSTRLEN.spec.ts +++ b/packages/client/lib/commands/HSTRLEN.spec.ts @@ -1,11 +1,12 @@ import { strict as assert } from 'node:assert'; import testUtils, { GLOBAL } from '../test-utils'; import HSTRLEN from './HSTRLEN'; +import { parseArgs } from './generic-transformers'; describe('HSTRLEN', () => { it('transformArguments', () => { assert.deepEqual( - HSTRLEN.transformArguments('key', 'field'), + parseArgs(HSTRLEN, 'key', 'field'), ['HSTRLEN', 'key', 'field'] ); }); diff --git a/packages/client/lib/commands/HSTRLEN.ts b/packages/client/lib/commands/HSTRLEN.ts index 843c4baf7ef..2468747d4c9 100644 --- a/packages/client/lib/commands/HSTRLEN.ts +++ b/packages/client/lib/commands/HSTRLEN.ts @@ -1,10 +1,13 @@ +import { CommandParser } from '../client/parser'; import { RedisArgument, ArrayReply, BlobStringReply, Command } from '../RESP/types'; export default { - FIRST_KEY_INDEX: 1, + CACHEABLE: true, IS_READ_ONLY: true, - transformArguments(key: RedisArgument, field: RedisArgument) { - return ['HSTRLEN', key, field]; + parseCommand(parser: CommandParser, key: RedisArgument, field: RedisArgument) { + parser.push('HSTRLEN'); + parser.pushKey(key); + parser.push(field); }, transformReply: undefined as unknown as () => ArrayReply } as const satisfies Command; diff --git a/packages/client/lib/commands/HTTL.spec.ts b/packages/client/lib/commands/HTTL.spec.ts index df74c8a728e..a79500e4d06 100644 --- a/packages/client/lib/commands/HTTL.spec.ts +++ b/packages/client/lib/commands/HTTL.spec.ts @@ -2,6 +2,7 @@ import { strict as assert } from 'node:assert'; import testUtils, { GLOBAL } from '../test-utils'; import HTTL from './HTTL'; import { HASH_EXPIRATION_TIME } from './HEXPIRETIME'; +import { parseArgs } from './generic-transformers'; describe('HTTL', () => { testUtils.isVersionGreaterThanHook([7, 4]); @@ -9,18 +10,17 @@ describe('HTTL', () => { describe('transformArguments', () => { it('string', () => { assert.deepEqual( - HTTL.transformArguments('key', 'field'), + parseArgs(HTTL, 'key', 'field'), ['HTTL', 'key', 'FIELDS', '1', 'field'] ); }); it('array', () => { assert.deepEqual( - HTTL.transformArguments('key', ['field1', 'field2']), + parseArgs(HTTL, 'key', ['field1', 'field2']), ['HTTL', 'key', 'FIELDS', '2', 'field1', 'field2'] ); }); - }); testUtils.testWithClient('hTTL', async client => { diff --git a/packages/client/lib/commands/HTTL.ts b/packages/client/lib/commands/HTTL.ts index 66b50ff3e68..4b8fe5d7e85 100644 --- a/packages/client/lib/commands/HTTL.ts +++ b/packages/client/lib/commands/HTTL.ts @@ -1,11 +1,18 @@ +import { CommandParser } from '../client/parser'; import { ArrayReply, Command, NullReply, NumberReply, RedisArgument } from '../RESP/types'; -import { pushVariadicArgument, RedisVariadicArgument } from './generic-transformers'; +import { RedisVariadicArgument } from './generic-transformers'; export default { - FIRST_KEY_INDEX: 1, IS_READ_ONLY: true, - transformArguments(key: RedisArgument, fields: RedisVariadicArgument) { - return pushVariadicArgument(['HTTL', key, 'FIELDS'], fields); + parseCommand( + parser: CommandParser, + key: RedisArgument, + fields: RedisVariadicArgument + ) { + parser.push('HTTL'); + parser.pushKey(key); + parser.push('FIELDS'); + parser.pushVariadicWithLength(fields); }, transformReply: undefined as unknown as () => ArrayReply | NullReply } as const satisfies Command; diff --git a/packages/client/lib/commands/HVALS.spec.ts b/packages/client/lib/commands/HVALS.spec.ts index 922aa588137..89cbb52861c 100644 --- a/packages/client/lib/commands/HVALS.spec.ts +++ b/packages/client/lib/commands/HVALS.spec.ts @@ -1,11 +1,12 @@ import { strict as assert } from 'node:assert'; import testUtils, { GLOBAL } from '../test-utils'; import HVALS from './HVALS'; +import { parseArgs } from './generic-transformers'; describe('HVALS', () => { - it('transformArguments', () => { + it('processCommand', () => { assert.deepEqual( - HVALS.transformArguments('key'), + parseArgs(HVALS, 'key'), ['HVALS', 'key'] ); }); diff --git a/packages/client/lib/commands/HVALS.ts b/packages/client/lib/commands/HVALS.ts index ee4193f5cec..ab17e47f533 100644 --- a/packages/client/lib/commands/HVALS.ts +++ b/packages/client/lib/commands/HVALS.ts @@ -1,10 +1,12 @@ +import { CommandParser } from '../client/parser'; import { RedisArgument, ArrayReply, BlobStringReply, Command } from '../RESP/types'; export default { - FIRST_KEY_INDEX: 1, + CACHEABLE: true, IS_READ_ONLY: true, - transformArguments(key: RedisArgument) { - return ['HVALS', key]; + parseCommand(parser: CommandParser, key: RedisArgument) { + parser.push('HVALS'); + parser.pushKey(key); }, transformReply: undefined as unknown as () => ArrayReply } as const satisfies Command; diff --git a/packages/client/lib/commands/INCR.spec.ts b/packages/client/lib/commands/INCR.spec.ts index 67129760245..0fe7ed7f8e6 100644 --- a/packages/client/lib/commands/INCR.spec.ts +++ b/packages/client/lib/commands/INCR.spec.ts @@ -1,11 +1,12 @@ import { strict as assert } from 'node:assert'; import testUtils, { GLOBAL } from '../test-utils'; import INCR from './INCR'; +import { parseArgs } from './generic-transformers'; describe('INCR', () => { it('transformArguments', () => { assert.deepEqual( - INCR.transformArguments('key'), + parseArgs(INCR, 'key'), ['INCR', 'key'] ); }); diff --git a/packages/client/lib/commands/INCR.ts b/packages/client/lib/commands/INCR.ts index eb38256c9dc..e719f06bc19 100644 --- a/packages/client/lib/commands/INCR.ts +++ b/packages/client/lib/commands/INCR.ts @@ -1,9 +1,10 @@ +import { CommandParser } from '../client/parser'; import { RedisArgument, NumberReply, Command } from '../RESP/types'; export default { - FIRST_KEY_INDEX: 1, - transformArguments(key: RedisArgument) { - return ['INCR', key]; + parseCommand(parser: CommandParser, key: RedisArgument) { + parser.push('INCR'); + parser.pushKey(key); }, transformReply: undefined as unknown as () => NumberReply } as const satisfies Command; diff --git a/packages/client/lib/commands/INCRBY.spec.ts b/packages/client/lib/commands/INCRBY.spec.ts index d66c01acce5..e2a5842f20a 100644 --- a/packages/client/lib/commands/INCRBY.spec.ts +++ b/packages/client/lib/commands/INCRBY.spec.ts @@ -1,11 +1,12 @@ import { strict as assert } from 'node:assert'; import testUtils, { GLOBAL } from '../test-utils'; import INCRBY from './INCRBY'; +import { parseArgs } from './generic-transformers'; describe('INCRBY', () => { it('transformArguments', () => { assert.deepEqual( - INCRBY.transformArguments('key', 1), + parseArgs(INCRBY, 'key', 1), ['INCRBY', 'key', '1'] ); }); diff --git a/packages/client/lib/commands/INCRBY.ts b/packages/client/lib/commands/INCRBY.ts index 5e94348fe63..bf463185044 100644 --- a/packages/client/lib/commands/INCRBY.ts +++ b/packages/client/lib/commands/INCRBY.ts @@ -1,9 +1,11 @@ +import { CommandParser } from '../client/parser'; import { RedisArgument, NumberReply, Command } from '../RESP/types'; export default { - FIRST_KEY_INDEX: 1, - transformArguments(key: RedisArgument, increment: number) { - return ['INCRBY', key, increment.toString()]; + parseCommand(parser: CommandParser, key: RedisArgument, increment: number) { + parser.push('INCRBY'); + parser.pushKey(key); + parser.push(increment.toString()); }, transformReply: undefined as unknown as () => NumberReply } as const satisfies Command; diff --git a/packages/client/lib/commands/INCRBYFLOAT.spec.ts b/packages/client/lib/commands/INCRBYFLOAT.spec.ts index 8bdd9c332de..57596970708 100644 --- a/packages/client/lib/commands/INCRBYFLOAT.spec.ts +++ b/packages/client/lib/commands/INCRBYFLOAT.spec.ts @@ -1,11 +1,12 @@ import { strict as assert } from 'node:assert'; import testUtils, { GLOBAL } from '../test-utils'; import INCRBYFLOAT from './INCRBYFLOAT'; +import { parseArgs } from './generic-transformers'; describe('INCRBYFLOAT', () => { it('transformArguments', () => { assert.deepEqual( - INCRBYFLOAT.transformArguments('key', 1.5), + parseArgs(INCRBYFLOAT, 'key', 1.5), ['INCRBYFLOAT', 'key', '1.5'] ); }); diff --git a/packages/client/lib/commands/INCRBYFLOAT.ts b/packages/client/lib/commands/INCRBYFLOAT.ts index 16f59e68c17..9a2dba42a6e 100644 --- a/packages/client/lib/commands/INCRBYFLOAT.ts +++ b/packages/client/lib/commands/INCRBYFLOAT.ts @@ -1,9 +1,11 @@ +import { CommandParser } from '../client/parser'; import { RedisArgument, BlobStringReply, Command } from '../RESP/types'; export default { - FIRST_KEY_INDEX: 1, - transformArguments(key: RedisArgument, increment: number) { - return ['INCRBYFLOAT', key, increment.toString()]; + parseCommand(parser: CommandParser, key: RedisArgument, increment: number) { + parser.push('INCRBYFLOAT'); + parser.pushKey(key); + parser.push(increment.toString()); }, transformReply: undefined as unknown as () => BlobStringReply } as const satisfies Command; diff --git a/packages/client/lib/commands/INFO.spec.ts b/packages/client/lib/commands/INFO.spec.ts index c4555778729..7ee8a95c137 100644 --- a/packages/client/lib/commands/INFO.spec.ts +++ b/packages/client/lib/commands/INFO.spec.ts @@ -1,19 +1,20 @@ import { strict as assert } from 'node:assert'; import testUtils, { GLOBAL } from '../test-utils'; import INFO from './INFO'; +import { parseArgs } from './generic-transformers'; describe('INFO', () => { describe('transformArguments', () => { it('simple', () => { assert.deepEqual( - INFO.transformArguments(), + parseArgs(INFO), ['INFO'] ); }); it('server section', () => { assert.deepEqual( - INFO.transformArguments('server'), + parseArgs(INFO, 'server'), ['INFO', 'server'] ); }); diff --git a/packages/client/lib/commands/INFO.ts b/packages/client/lib/commands/INFO.ts index 9877c0cf66b..82cbd497a5b 100644 --- a/packages/client/lib/commands/INFO.ts +++ b/packages/client/lib/commands/INFO.ts @@ -1,16 +1,15 @@ +import { CommandParser } from '../client/parser'; import { RedisArgument, VerbatimStringReply, Command } from '../RESP/types'; export default { - FIRST_KEY_INDEX: undefined, + NOT_KEYED_COMMAND: true, IS_READ_ONLY: true, - transformArguments(section?: RedisArgument) { - const args: Array = ['INFO']; + parseCommand(parser: CommandParser, section?: RedisArgument) { + parser.push('INFO'); if (section) { - args.push(section); + parser.push(section); } - - return args; }, transformReply: undefined as unknown as () => VerbatimStringReply } as const satisfies Command; diff --git a/packages/client/lib/commands/KEYS.ts b/packages/client/lib/commands/KEYS.ts index 488ba1154c9..e516245d2ee 100644 --- a/packages/client/lib/commands/KEYS.ts +++ b/packages/client/lib/commands/KEYS.ts @@ -1,10 +1,11 @@ +import { CommandParser } from '../client/parser'; import { RedisArgument, ArrayReply, BlobStringReply, Command } from '../RESP/types'; export default { - FIRST_KEY_INDEX: undefined, + NOT_KEYED_COMMAND: true, IS_READ_ONLY: true, - transformArguments(pattern: RedisArgument) { - return ['KEYS', pattern]; + parseCommand(parser: CommandParser, pattern: RedisArgument) { + parser.push('KEYS', pattern); }, transformReply: undefined as unknown as () => ArrayReply } as const satisfies Command; diff --git a/packages/client/lib/commands/LASTSAVE.spec.ts b/packages/client/lib/commands/LASTSAVE.spec.ts index 74cf30705fa..fba26811170 100644 --- a/packages/client/lib/commands/LASTSAVE.spec.ts +++ b/packages/client/lib/commands/LASTSAVE.spec.ts @@ -1,11 +1,12 @@ import { strict as assert } from 'node:assert'; import testUtils, { GLOBAL } from '../test-utils'; import LASTSAVE from './LASTSAVE'; +import { parseArgs } from './generic-transformers'; describe('LASTSAVE', () => { it('transformArguments', () => { assert.deepEqual( - LASTSAVE.transformArguments(), + parseArgs(LASTSAVE), ['LASTSAVE'] ); }); diff --git a/packages/client/lib/commands/LASTSAVE.ts b/packages/client/lib/commands/LASTSAVE.ts index a65161f78b9..447cb95ab6d 100644 --- a/packages/client/lib/commands/LASTSAVE.ts +++ b/packages/client/lib/commands/LASTSAVE.ts @@ -1,10 +1,11 @@ +import { CommandParser } from '../client/parser'; import { NumberReply, Command } from '../RESP/types'; export default { - FIRST_KEY_INDEX: undefined, + NOT_KEYED_COMMAND: true, IS_READ_ONLY: true, - transformArguments() { - return ['LASTSAVE']; + parseCommand(parser: CommandParser) { + parser.push('LASTSAVE'); }, transformReply: undefined as unknown as () => NumberReply } as const satisfies Command; diff --git a/packages/client/lib/commands/LATENCY_DOCTOR.spec.ts b/packages/client/lib/commands/LATENCY_DOCTOR.spec.ts index 00eabfb4cb3..654751b5b57 100644 --- a/packages/client/lib/commands/LATENCY_DOCTOR.spec.ts +++ b/packages/client/lib/commands/LATENCY_DOCTOR.spec.ts @@ -1,11 +1,12 @@ import { strict as assert } from 'node:assert'; import testUtils, { GLOBAL } from '../test-utils'; import LATENCY_DOCTOR from './LATENCY_DOCTOR'; +import { parseArgs } from './generic-transformers'; describe('LATENCY DOCTOR', () => { it('transformArguments', () => { assert.deepEqual( - LATENCY_DOCTOR.transformArguments(), + parseArgs(LATENCY_DOCTOR), ['LATENCY', 'DOCTOR'] ); }); diff --git a/packages/client/lib/commands/LATENCY_DOCTOR.ts b/packages/client/lib/commands/LATENCY_DOCTOR.ts index 96dc6b65702..49c830b3065 100644 --- a/packages/client/lib/commands/LATENCY_DOCTOR.ts +++ b/packages/client/lib/commands/LATENCY_DOCTOR.ts @@ -1,10 +1,11 @@ +import { CommandParser } from '../client/parser'; import { BlobStringReply, Command } from '../RESP/types'; export default { - FIRST_KEY_INDEX: undefined, + NOT_KEYED_COMMAND: true, IS_READ_ONLY: true, - transformArguments() { - return ['LATENCY', 'DOCTOR']; + parseCommand(parser: CommandParser) { + parser.push('LATENCY', 'DOCTOR'); }, transformReply: undefined as unknown as () => BlobStringReply } as const satisfies Command; diff --git a/packages/client/lib/commands/LATENCY_GRAPH.spec.ts b/packages/client/lib/commands/LATENCY_GRAPH.spec.ts index 03ad8e2c886..7135dc1c420 100644 --- a/packages/client/lib/commands/LATENCY_GRAPH.spec.ts +++ b/packages/client/lib/commands/LATENCY_GRAPH.spec.ts @@ -1,11 +1,12 @@ import { strict as assert } from 'node:assert'; import testUtils, { GLOBAL } from '../test-utils'; import LATENCY_GRAPH from './LATENCY_GRAPH'; +import { parseArgs } from './generic-transformers'; describe('LATENCY GRAPH', () => { it('transformArguments', () => { assert.deepEqual( - LATENCY_GRAPH.transformArguments('command'), + parseArgs(LATENCY_GRAPH, 'command'), [ 'LATENCY', 'GRAPH', diff --git a/packages/client/lib/commands/LATENCY_GRAPH.ts b/packages/client/lib/commands/LATENCY_GRAPH.ts index 7d5f54288b6..20251c3cded 100644 --- a/packages/client/lib/commands/LATENCY_GRAPH.ts +++ b/packages/client/lib/commands/LATENCY_GRAPH.ts @@ -1,3 +1,4 @@ +import { CommandParser } from '../client/parser'; import { BlobStringReply, Command } from '../RESP/types'; export const LATENCY_EVENTS = { @@ -22,10 +23,10 @@ export const LATENCY_EVENTS = { export type LatencyEvent = typeof LATENCY_EVENTS[keyof typeof LATENCY_EVENTS]; export default { - FIRST_KEY_INDEX: undefined, + NOT_KEYED_COMMAND: true, IS_READ_ONLY: true, - transformArguments(event: LatencyEvent) { - return ['LATENCY', 'GRAPH', event]; + parseCommand(parser: CommandParser, event: LatencyEvent) { + parser.push('LATENCY', 'GRAPH', event); }, transformReply: undefined as unknown as () => BlobStringReply } as const satisfies Command; diff --git a/packages/client/lib/commands/LATENCY_HISTORY.spec.ts b/packages/client/lib/commands/LATENCY_HISTORY.spec.ts index 509c856e28f..64f94d0d1a3 100644 --- a/packages/client/lib/commands/LATENCY_HISTORY.spec.ts +++ b/packages/client/lib/commands/LATENCY_HISTORY.spec.ts @@ -1,11 +1,12 @@ import { strict as assert } from 'assert'; import testUtils, { GLOBAL } from '../test-utils'; import LATENCY_HISTORY from './LATENCY_HISTORY'; +import { parseArgs } from './generic-transformers'; describe('LATENCY HISTORY', () => { it('transformArguments', () => { assert.deepEqual( - LATENCY_HISTORY.transformArguments('command'), + parseArgs(LATENCY_HISTORY, 'command'), ['LATENCY', 'HISTORY', 'command'] ); }); diff --git a/packages/client/lib/commands/LATENCY_HISTORY.ts b/packages/client/lib/commands/LATENCY_HISTORY.ts index df5b1772b2d..6e0e4d5c560 100644 --- a/packages/client/lib/commands/LATENCY_HISTORY.ts +++ b/packages/client/lib/commands/LATENCY_HISTORY.ts @@ -1,3 +1,4 @@ +import { CommandParser } from '../client/parser'; import { ArrayReply, TuplesReply, NumberReply, Command } from '../RESP/types'; export type LatencyEventType = ( @@ -20,10 +21,10 @@ export type LatencyEventType = ( ); export default { - FIRST_KEY_INDEX: undefined, + NOT_KEYED_COMMAND: true, IS_READ_ONLY: true, - transformArguments(event: LatencyEventType) { - return ['LATENCY', 'HISTORY', event]; + parseCommand(parser: CommandParser, event: LatencyEventType) { + parser.push('LATENCY', 'HISTORY', event); }, transformReply: undefined as unknown as () => ArrayReply { it('transformArguments', () => { assert.deepEqual( - LATENCY_LATEST.transformArguments(), + parseArgs(LATENCY_LATEST), ['LATENCY', 'LATEST'] ); }); diff --git a/packages/client/lib/commands/LATENCY_LATEST.ts b/packages/client/lib/commands/LATENCY_LATEST.ts index 29548af30dc..2ce3efd291c 100644 --- a/packages/client/lib/commands/LATENCY_LATEST.ts +++ b/packages/client/lib/commands/LATENCY_LATEST.ts @@ -1,10 +1,11 @@ +import { CommandParser } from '../client/parser'; import { ArrayReply, BlobStringReply, NumberReply, Command } from '../RESP/types'; export default { - FIRST_KEY_INDEX: undefined, + NOT_KEYED_COMMAND: true, IS_READ_ONLY: true, - transformArguments() { - return ['LATENCY', 'LATEST']; + parseCommand(parser: CommandParser) { + parser.push('LATENCY', 'LATEST'); }, transformReply: undefined as unknown as () => ArrayReply<[ name: BlobStringReply, diff --git a/packages/client/lib/commands/LCS.spec.ts b/packages/client/lib/commands/LCS.spec.ts index ff9d63db1b1..aedbb1b34e3 100644 --- a/packages/client/lib/commands/LCS.spec.ts +++ b/packages/client/lib/commands/LCS.spec.ts @@ -1,13 +1,14 @@ import { strict as assert } from 'node:assert'; import testUtils, { GLOBAL } from '../test-utils'; import LCS from './LCS'; +import { parseArgs } from './generic-transformers'; describe('LCS', () => { testUtils.isVersionGreaterThanHook([7]); it('transformArguments', () => { assert.deepEqual( - LCS.transformArguments('1', '2'), + parseArgs(LCS, '1', '2'), ['LCS', '1', '2'] ); }); diff --git a/packages/client/lib/commands/LCS.ts b/packages/client/lib/commands/LCS.ts index b798f12aa37..ed4f11ad990 100644 --- a/packages/client/lib/commands/LCS.ts +++ b/packages/client/lib/commands/LCS.ts @@ -1,13 +1,15 @@ +import { CommandParser } from '../client/parser'; import { RedisArgument, BlobStringReply, Command } from '../RESP/types'; export default { - FIRST_KEY_INDEX: 1, IS_READ_ONLY: true, - transformArguments( + parseCommand( + parser: CommandParser, key1: RedisArgument, key2: RedisArgument ) { - return ['LCS', key1, key2]; + parser.push('LCS'); + parser.pushKeys([key1, key2]); }, transformReply: undefined as unknown as () => BlobStringReply } as const satisfies Command; diff --git a/packages/client/lib/commands/LCS_IDX.spec.ts b/packages/client/lib/commands/LCS_IDX.spec.ts index 2f60a205faa..c4cc6681d85 100644 --- a/packages/client/lib/commands/LCS_IDX.spec.ts +++ b/packages/client/lib/commands/LCS_IDX.spec.ts @@ -1,13 +1,14 @@ import { strict as assert } from 'node:assert'; import testUtils, { GLOBAL } from '../test-utils'; import LCS_IDX from './LCS_IDX'; +import { parseArgs } from './generic-transformers'; describe('LCS IDX', () => { testUtils.isVersionGreaterThanHook([7]); it('transformArguments', () => { assert.deepEqual( - LCS_IDX.transformArguments('1', '2'), + parseArgs(LCS_IDX, '1', '2'), ['LCS', '1', '2', 'IDX'] ); }); diff --git a/packages/client/lib/commands/LCS_IDX.ts b/packages/client/lib/commands/LCS_IDX.ts index 0c266fffe1c..cb0a6b07657 100644 --- a/packages/client/lib/commands/LCS_IDX.ts +++ b/packages/client/lib/commands/LCS_IDX.ts @@ -1,3 +1,4 @@ +import { CommandParser } from '../client/parser'; import { RedisArgument, TuplesToMapReply, BlobStringReply, ArrayReply, NumberReply, UnwrapReply, Resp2Reply, Command, TuplesReply } from '../RESP/types'; import LCS from './LCS'; @@ -23,22 +24,20 @@ export type LcsIdxReply = TuplesToMapReply<[ ]>; export default { - FIRST_KEY_INDEX: LCS.FIRST_KEY_INDEX, IS_READ_ONLY: LCS.IS_READ_ONLY, - transformArguments( + parseCommand( + parser: CommandParser, key1: RedisArgument, key2: RedisArgument, options?: LcsIdxOptions ) { - const args = LCS.transformArguments(key1, key2); + LCS.parseCommand(parser, key1, key2); - args.push('IDX'); + parser.push('IDX'); if (options?.MINMATCHLEN) { - args.push('MINMATCHLEN', options.MINMATCHLEN.toString()); + parser.push('MINMATCHLEN', options.MINMATCHLEN.toString()); } - - return args; }, transformReply: { 2: (reply: UnwrapReply>) => ({ diff --git a/packages/client/lib/commands/LCS_IDX_WITHMATCHLEN.spec.ts b/packages/client/lib/commands/LCS_IDX_WITHMATCHLEN.spec.ts index 39ba17e8f2c..92ecad4761c 100644 --- a/packages/client/lib/commands/LCS_IDX_WITHMATCHLEN.spec.ts +++ b/packages/client/lib/commands/LCS_IDX_WITHMATCHLEN.spec.ts @@ -1,13 +1,14 @@ import { strict as assert } from 'node:assert'; import testUtils, { GLOBAL } from '../test-utils'; import LCS_IDX_WITHMATCHLEN from './LCS_IDX_WITHMATCHLEN'; +import { parseArgs } from './generic-transformers'; describe('LCS IDX WITHMATCHLEN', () => { testUtils.isVersionGreaterThanHook([7]); it('transformArguments', () => { assert.deepEqual( - LCS_IDX_WITHMATCHLEN.transformArguments('1', '2'), + parseArgs(LCS_IDX_WITHMATCHLEN, '1', '2'), ['LCS', '1', '2', 'IDX', 'WITHMATCHLEN'] ); }); diff --git a/packages/client/lib/commands/LCS_IDX_WITHMATCHLEN.ts b/packages/client/lib/commands/LCS_IDX_WITHMATCHLEN.ts index 4e645852035..d2a743983e1 100644 --- a/packages/client/lib/commands/LCS_IDX_WITHMATCHLEN.ts +++ b/packages/client/lib/commands/LCS_IDX_WITHMATCHLEN.ts @@ -1,5 +1,5 @@ -import { RedisArgument, TuplesToMapReply, BlobStringReply, ArrayReply, TuplesReply, NumberReply, UnwrapReply, Resp2Reply, Command } from '../RESP/types'; -import LCS_IDX, { LcsIdxOptions, LcsIdxRange } from './LCS_IDX'; +import { TuplesToMapReply, BlobStringReply, ArrayReply, TuplesReply, NumberReply, UnwrapReply, Resp2Reply, Command } from '../RESP/types'; +import LCS_IDX, { LcsIdxRange } from './LCS_IDX'; export type LcsIdxWithMatchLenMatches = ArrayReply< TuplesReply<[ @@ -15,16 +15,11 @@ export type LcsIdxWithMatchLenReply = TuplesToMapReply<[ ]>; export default { - FIRST_KEY_INDEX: LCS_IDX.FIRST_KEY_INDEX, IS_READ_ONLY: LCS_IDX.IS_READ_ONLY, - transformArguments( - key1: RedisArgument, - key2: RedisArgument, - options?: LcsIdxOptions - ) { - const args = LCS_IDX.transformArguments(key1, key2); - args.push('WITHMATCHLEN'); - return args; + parseCommand(...args: Parameters) { + const parser = args[0]; + LCS_IDX.parseCommand(...args); + parser.push('WITHMATCHLEN'); }, transformReply: { 2: (reply: UnwrapReply>) => ({ diff --git a/packages/client/lib/commands/LCS_LEN.spec.ts b/packages/client/lib/commands/LCS_LEN.spec.ts index 9dc163a68a9..53a2e83c326 100644 --- a/packages/client/lib/commands/LCS_LEN.spec.ts +++ b/packages/client/lib/commands/LCS_LEN.spec.ts @@ -1,13 +1,14 @@ import { strict as assert } from 'node:assert'; import testUtils, { GLOBAL } from '../test-utils'; import LCS_LEN from './LCS_LEN'; +import { parseArgs } from './generic-transformers'; describe('LCS_LEN', () => { testUtils.isVersionGreaterThanHook([7]); it('transformArguments', () => { assert.deepEqual( - LCS_LEN.transformArguments('1', '2'), + parseArgs(LCS_LEN, '1', '2'), ['LCS', '1', '2', 'LEN'] ); }); diff --git a/packages/client/lib/commands/LCS_LEN.ts b/packages/client/lib/commands/LCS_LEN.ts index d5d0e77e4d6..a1f92d914a4 100644 --- a/packages/client/lib/commands/LCS_LEN.ts +++ b/packages/client/lib/commands/LCS_LEN.ts @@ -1,16 +1,13 @@ -import { RedisArgument, NumberReply, Command } from '../RESP/types'; +import { NumberReply, Command } from '../RESP/types'; import LCS from './LCS'; export default { - FIRST_KEY_INDEX: LCS.FIRST_KEY_INDEX, IS_READ_ONLY: LCS.IS_READ_ONLY, - transformArguments( - key1: RedisArgument, - key2: RedisArgument - ) { - const args = LCS.transformArguments(key1, key2); - args.push('LEN'); - return args; + parseCommand(...args: Parameters) { + const parser = args[0]; + + LCS.parseCommand(...args); + parser.push('LEN'); }, transformReply: undefined as unknown as () => NumberReply } as const satisfies Command; diff --git a/packages/client/lib/commands/LINDEX.spec.ts b/packages/client/lib/commands/LINDEX.spec.ts index 60346b85f21..41eff474a1a 100644 --- a/packages/client/lib/commands/LINDEX.spec.ts +++ b/packages/client/lib/commands/LINDEX.spec.ts @@ -1,11 +1,12 @@ import { strict as assert } from 'node:assert'; import testUtils, { GLOBAL } from '../test-utils'; import LINDEX from './LINDEX'; +import { parseArgs } from './generic-transformers'; describe('LINDEX', () => { it('transformArguments', () => { assert.deepEqual( - LINDEX.transformArguments('key', 0), + parseArgs(LINDEX, 'key', 0), ['LINDEX', 'key', '0'] ); }); diff --git a/packages/client/lib/commands/LINDEX.ts b/packages/client/lib/commands/LINDEX.ts index 0478bf9dc41..6335fc40c2c 100644 --- a/packages/client/lib/commands/LINDEX.ts +++ b/packages/client/lib/commands/LINDEX.ts @@ -1,10 +1,13 @@ +import { CommandParser } from '../client/parser'; import { RedisArgument, BlobStringReply, NullReply, Command } from '../RESP/types'; export default { - FIRST_KEY_INDEX: 1, + CACHEABLE: true, IS_READ_ONLY: true, - transformArguments(key: RedisArgument, index: number) { - return ['LINDEX', key, index.toString()]; + parseCommand(parser: CommandParser, key: RedisArgument, index: number) { + parser.push('LINDEX'); + parser.pushKey(key); + parser.push(index.toString()); }, transformReply: undefined as unknown as () => BlobStringReply | NullReply } as const satisfies Command; diff --git a/packages/client/lib/commands/LINSERT.spec.ts b/packages/client/lib/commands/LINSERT.spec.ts index 6cafa3f5a84..c3c89d56c12 100644 --- a/packages/client/lib/commands/LINSERT.spec.ts +++ b/packages/client/lib/commands/LINSERT.spec.ts @@ -1,11 +1,12 @@ import { strict as assert } from 'node:assert'; import testUtils, { GLOBAL } from '../test-utils'; import LINSERT from './LINSERT'; +import { parseArgs } from './generic-transformers'; describe('LINSERT', () => { it('transformArguments', () => { assert.deepEqual( - LINSERT.transformArguments('key', 'BEFORE', 'pivot', 'element'), + parseArgs(LINSERT, 'key', 'BEFORE', 'pivot', 'element'), ['LINSERT', 'key', 'BEFORE', 'pivot', 'element'] ); }); diff --git a/packages/client/lib/commands/LINSERT.ts b/packages/client/lib/commands/LINSERT.ts index 4bdc77de5a4..8a40ac66630 100644 --- a/packages/client/lib/commands/LINSERT.ts +++ b/packages/client/lib/commands/LINSERT.ts @@ -1,23 +1,20 @@ +import { CommandParser } from '../client/parser'; import { RedisArgument, NumberReply, Command } from '../RESP/types'; type LInsertPosition = 'BEFORE' | 'AFTER'; export default { - FIRST_KEY_INDEX: 1, IS_READ_ONLY: true, - transformArguments( + parseCommand( + parser: CommandParser, key: RedisArgument, position: LInsertPosition, pivot: RedisArgument, element: RedisArgument ) { - return [ - 'LINSERT', - key, - position, - pivot, - element - ]; + parser.push('LINSERT'); + parser.pushKey(key); + parser.push(position, pivot, element); }, transformReply: undefined as unknown as () => NumberReply } as const satisfies Command; diff --git a/packages/client/lib/commands/LLEN.spec.ts b/packages/client/lib/commands/LLEN.spec.ts index f6ac9a73cc3..d86078d0b48 100644 --- a/packages/client/lib/commands/LLEN.spec.ts +++ b/packages/client/lib/commands/LLEN.spec.ts @@ -1,11 +1,12 @@ import { strict as assert } from 'node:assert'; import testUtils, { GLOBAL } from '../test-utils'; import LLEN from './LLEN'; +import { parseArgs } from './generic-transformers'; describe('LLEN', () => { it('transformArguments', () => { assert.deepEqual( - LLEN.transformArguments('key'), + parseArgs(LLEN, 'key'), ['LLEN', 'key'] ); }); diff --git a/packages/client/lib/commands/LLEN.ts b/packages/client/lib/commands/LLEN.ts index dda59ddf291..674e022e60d 100644 --- a/packages/client/lib/commands/LLEN.ts +++ b/packages/client/lib/commands/LLEN.ts @@ -1,10 +1,12 @@ +import { CommandParser } from '../client/parser'; import { RedisArgument, NumberReply, Command } from '../RESP/types'; export default { - FIRST_KEY_INDEX: 1, + CACHEABLE: true, IS_READ_ONLY: true, - transformArguments(key: RedisArgument) { - return ['LLEN', key]; + parseCommand(parser: CommandParser, key: RedisArgument) { + parser.push('LLEN'); + parser.pushKey(key); }, transformReply: undefined as unknown as () => NumberReply } as const satisfies Command; diff --git a/packages/client/lib/commands/LMOVE.spec.ts b/packages/client/lib/commands/LMOVE.spec.ts index 86740aa7b77..bed3ff8eab0 100644 --- a/packages/client/lib/commands/LMOVE.spec.ts +++ b/packages/client/lib/commands/LMOVE.spec.ts @@ -1,13 +1,14 @@ import { strict as assert } from 'node:assert'; import testUtils, { GLOBAL } from '../test-utils'; import LMOVE from './LMOVE'; +import { parseArgs } from './generic-transformers'; describe('LMOVE', () => { testUtils.isVersionGreaterThanHook([6, 2]); it('transformArguments', () => { assert.deepEqual( - LMOVE.transformArguments('source', 'destination', 'LEFT', 'RIGHT'), + parseArgs(LMOVE, 'source', 'destination', 'LEFT', 'RIGHT'), ['LMOVE', 'source', 'destination', 'LEFT', 'RIGHT'] ); }); diff --git a/packages/client/lib/commands/LMOVE.ts b/packages/client/lib/commands/LMOVE.ts index 95adc71a0ff..f3ac847e900 100644 --- a/packages/client/lib/commands/LMOVE.ts +++ b/packages/client/lib/commands/LMOVE.ts @@ -1,22 +1,19 @@ +import { CommandParser } from '../client/parser'; import { RedisArgument, BlobStringReply, NullReply, Command } from '../RESP/types'; import { ListSide } from './generic-transformers'; export default { - FIRST_KEY_INDEX: 1, IS_READ_ONLY: false, - transformArguments( + parseCommand( + parser: CommandParser, source: RedisArgument, destination: RedisArgument, sourceSide: ListSide, destinationSide: ListSide ) { - return [ - 'LMOVE', - source, - destination, - sourceSide, - destinationSide, - ]; + parser.push('LMOVE'); + parser.pushKeys([source, destination]); + parser.push(sourceSide, destinationSide); }, transformReply: undefined as unknown as () => BlobStringReply | NullReply } as const satisfies Command; diff --git a/packages/client/lib/commands/LMPOP.spec.ts b/packages/client/lib/commands/LMPOP.spec.ts index faf39e053ef..bd2cf869e74 100644 --- a/packages/client/lib/commands/LMPOP.spec.ts +++ b/packages/client/lib/commands/LMPOP.spec.ts @@ -1,6 +1,7 @@ import { strict as assert } from 'node:assert'; import testUtils, { GLOBAL } from '../test-utils'; import LMPOP from './LMPOP'; +import { parseArgs } from './generic-transformers'; describe('LMPOP', () => { testUtils.isVersionGreaterThanHook([7]); @@ -8,14 +9,14 @@ describe('LMPOP', () => { describe('transformArguments', () => { it('simple', () => { assert.deepEqual( - LMPOP.transformArguments('key', 'LEFT'), + parseArgs(LMPOP, 'key', 'LEFT'), ['LMPOP', '1', 'key', 'LEFT'] ); }); it('with COUNT', () => { assert.deepEqual( - LMPOP.transformArguments('key', 'LEFT', { + parseArgs(LMPOP, 'key', 'LEFT', { COUNT: 2 }), ['LMPOP', '1', 'key', 'LEFT', 'COUNT', '2'] diff --git a/packages/client/lib/commands/LMPOP.ts b/packages/client/lib/commands/LMPOP.ts index 49f7272ec46..c8095e42e75 100644 --- a/packages/client/lib/commands/LMPOP.ts +++ b/packages/client/lib/commands/LMPOP.ts @@ -1,34 +1,32 @@ -import { CommandArguments, NullReply, TuplesReply, BlobStringReply, Command } from '../RESP/types'; -import { ListSide, RedisVariadicArgument, pushVariadicArgument } from './generic-transformers'; +import { CommandParser } from '../client/parser'; +import { NullReply, TuplesReply, BlobStringReply, Command } from '../RESP/types'; +import { ListSide, RedisVariadicArgument, Tail } from './generic-transformers'; export interface LMPopOptions { COUNT?: number; } -export function transformLMPopArguments( - args: CommandArguments, +export function parseLMPopArguments( + parser: CommandParser, keys: RedisVariadicArgument, side: ListSide, options?: LMPopOptions -): CommandArguments { - args = pushVariadicArgument(args, keys); - - args.push(side); +) { + parser.pushKeysLength(keys); + parser.push(side); if (options?.COUNT !== undefined) { - args.push('COUNT', options.COUNT.toString()); + parser.push('COUNT', options.COUNT.toString()); } - - return args; } -export type LMPopArguments = typeof transformLMPopArguments extends (_: any, ...args: infer T) => any ? T : never; +export type LMPopArguments = Tail>; export default { - FIRST_KEY_INDEX: 2, - IS_READ_ONLY: false, - transformArguments(...args: LMPopArguments) { - return transformLMPopArguments(['LMPOP'], ...args); + IS_READ_ONLY: false, + parseCommand(parser: CommandParser, ...args: LMPopArguments) { + parser.push('LMPOP'); + parseLMPopArguments(parser, ...args); }, transformReply: undefined as unknown as () => NullReply | TuplesReply<[ key: BlobStringReply, diff --git a/packages/client/lib/commands/LOLWUT.spec.ts b/packages/client/lib/commands/LOLWUT.spec.ts index b05c4168f6f..b06030b0d0e 100644 --- a/packages/client/lib/commands/LOLWUT.spec.ts +++ b/packages/client/lib/commands/LOLWUT.spec.ts @@ -1,26 +1,27 @@ import { strict as assert } from 'node:assert'; import testUtils, { GLOBAL } from '../test-utils'; import LOLWUT from './LOLWUT'; +import { parseArgs } from './generic-transformers'; describe('LOLWUT', () => { describe('transformArguments', () => { it('simple', () => { assert.deepEqual( - LOLWUT.transformArguments(), + parseArgs(LOLWUT), ['LOLWUT'] ); }); it('with version', () => { assert.deepEqual( - LOLWUT.transformArguments(5), + parseArgs(LOLWUT, 5), ['LOLWUT', 'VERSION', '5'] ); }); it('with version and optional arguments', () => { assert.deepEqual( - LOLWUT.transformArguments(5, 1, 2, 3), + parseArgs(LOLWUT, 5, 1, 2, 3), ['LOLWUT', 'VERSION', '5', '1', '2', '3'] ); }); diff --git a/packages/client/lib/commands/LOLWUT.ts b/packages/client/lib/commands/LOLWUT.ts index 7a6c8329d69..372bf536967 100644 --- a/packages/client/lib/commands/LOLWUT.ts +++ b/packages/client/lib/commands/LOLWUT.ts @@ -1,20 +1,18 @@ +import { CommandParser } from '../client/parser'; import { BlobStringReply, Command } from '../RESP/types'; export default { - FIRST_KEY_INDEX: undefined, + NOT_KEYED_COMMAND: true, IS_READ_ONLY: true, - transformArguments(version?: number, ...optionalArguments: Array) { - const args = ['LOLWUT']; - + parseCommand(parser: CommandParser, version?: number, ...optionalArguments: Array) { + parser.push('LOLWUT'); if (version) { - args.push( + parser.push( 'VERSION', - version.toString(), - ...optionalArguments.map(String), + version.toString() ); + parser.pushVariadic(optionalArguments.map(String)); } - - return args; }, transformReply: undefined as unknown as () => BlobStringReply } as const satisfies Command; diff --git a/packages/client/lib/commands/LPOP.spec.ts b/packages/client/lib/commands/LPOP.spec.ts index 944e559b15f..93449bdbf5f 100644 --- a/packages/client/lib/commands/LPOP.spec.ts +++ b/packages/client/lib/commands/LPOP.spec.ts @@ -1,11 +1,12 @@ import { strict as assert } from 'node:assert'; import testUtils, { GLOBAL } from '../test-utils'; import LPOP from './LPOP'; +import { parseArgs } from './generic-transformers'; describe('LPOP', () => { it('transformArguments', () => { assert.deepEqual( - LPOP.transformArguments('key'), + parseArgs(LPOP, 'key'), ['LPOP', 'key'] ); }); diff --git a/packages/client/lib/commands/LPOP.ts b/packages/client/lib/commands/LPOP.ts index 7c85c30f9a1..3125236bfa0 100644 --- a/packages/client/lib/commands/LPOP.ts +++ b/packages/client/lib/commands/LPOP.ts @@ -1,9 +1,10 @@ +import { CommandParser } from '../client/parser'; import { RedisArgument, BlobStringReply, NullReply, Command } from '../RESP/types'; export default { - FIRST_KEY_INDEX: 1, - transformArguments(key: RedisArgument) { - return ['LPOP', key]; + parseCommand(parser: CommandParser, key: RedisArgument) { + parser.push('LPOP'); + parser.pushKey(key); }, transformReply: undefined as unknown as () => BlobStringReply | NullReply } as const satisfies Command; diff --git a/packages/client/lib/commands/LPOP_COUNT.spec.ts b/packages/client/lib/commands/LPOP_COUNT.spec.ts index 8286a504428..04bb3648d0a 100644 --- a/packages/client/lib/commands/LPOP_COUNT.spec.ts +++ b/packages/client/lib/commands/LPOP_COUNT.spec.ts @@ -1,13 +1,14 @@ import { strict as assert } from 'node:assert'; import testUtils, { GLOBAL } from '../test-utils'; import LPOP_COUNT from './LPOP_COUNT'; +import { parseArgs } from './generic-transformers'; describe('LPOP COUNT', () => { testUtils.isVersionGreaterThanHook([6, 2]); it('transformArguments', () => { assert.deepEqual( - LPOP_COUNT.transformArguments('key', 1), + parseArgs(LPOP_COUNT, 'key', 1), ['LPOP', 'key', '1'] ); }); diff --git a/packages/client/lib/commands/LPOP_COUNT.ts b/packages/client/lib/commands/LPOP_COUNT.ts index a1536e78dcb..6d9aba42c21 100644 --- a/packages/client/lib/commands/LPOP_COUNT.ts +++ b/packages/client/lib/commands/LPOP_COUNT.ts @@ -1,10 +1,12 @@ +import { CommandParser } from '../client/parser'; import { RedisArgument, NullReply, ArrayReply, BlobStringReply, Command } from '../RESP/types'; +import LPOP from './LPOP'; export default { - FIRST_KEY_INDEX: 2, IS_READ_ONLY: false, - transformArguments(key: RedisArgument, count: number) { - return ['LPOP', key, count.toString()]; + parseCommand(parser: CommandParser, key: RedisArgument, count: number) { + LPOP.parseCommand(parser, key); + parser.push(count.toString()) }, transformReply: undefined as unknown as () => NullReply | ArrayReply } as const satisfies Command; diff --git a/packages/client/lib/commands/LPOS.spec.ts b/packages/client/lib/commands/LPOS.spec.ts index 94c0bb3c034..f26af3f540f 100644 --- a/packages/client/lib/commands/LPOS.spec.ts +++ b/packages/client/lib/commands/LPOS.spec.ts @@ -1,21 +1,22 @@ import { strict as assert } from 'node:assert'; import testUtils, { GLOBAL } from '../test-utils'; import LPOS from './LPOS'; +import { parseArgs } from './generic-transformers'; describe('LPOS', () => { testUtils.isVersionGreaterThanHook([6, 0, 6]); - describe('transformArguments', () => { + describe('processCommand', () => { it('simple', () => { assert.deepEqual( - LPOS.transformArguments('key', 'element'), + parseArgs(LPOS, 'key', 'element'), ['LPOS', 'key', 'element'] ); }); it('with RANK', () => { assert.deepEqual( - LPOS.transformArguments('key', 'element', { + parseArgs(LPOS, 'key', 'element', { RANK: 0 }), ['LPOS', 'key', 'element', 'RANK', '0'] @@ -24,7 +25,7 @@ describe('LPOS', () => { it('with MAXLEN', () => { assert.deepEqual( - LPOS.transformArguments('key', 'element', { + parseArgs(LPOS, 'key', 'element', { MAXLEN: 10 }), ['LPOS', 'key', 'element', 'MAXLEN', '10'] @@ -33,7 +34,7 @@ describe('LPOS', () => { it('with RANK, MAXLEN', () => { assert.deepEqual( - LPOS.transformArguments('key', 'element', { + parseArgs(LPOS, 'key', 'element', { RANK: 0, MAXLEN: 10 }), diff --git a/packages/client/lib/commands/LPOS.ts b/packages/client/lib/commands/LPOS.ts index 047d80d7c29..bb05ba6555d 100644 --- a/packages/client/lib/commands/LPOS.ts +++ b/packages/client/lib/commands/LPOS.ts @@ -1,3 +1,4 @@ +import { CommandParser } from '../client/parser'; import { RedisArgument, NumberReply, NullReply, Command } from '../RESP/types'; export interface LPosOptions { @@ -6,26 +7,25 @@ export interface LPosOptions { } export default { - FIRST_KEY_INDEX: 1, + CACHEABLE: true, IS_READ_ONLY: true, - transformArguments( + parseCommand( + parser: CommandParser, key: RedisArgument, element: RedisArgument, options?: LPosOptions ) { - const args = ['LPOS', key, element]; + parser.push('LPOS'); + parser.pushKey(key); + parser.push(element); - if (options) { - if (typeof options.RANK === 'number') { - args.push('RANK', options.RANK.toString()); - } - - if (typeof options.MAXLEN === 'number') { - args.push('MAXLEN', options.MAXLEN.toString()); - } + if (options?.RANK !== undefined) { + parser.push('RANK', options.RANK.toString()); } - return args; + if (options?.MAXLEN !== undefined) { + parser.push('MAXLEN', options.MAXLEN.toString()); + } }, transformReply: undefined as unknown as () => NumberReply | NullReply } as const satisfies Command; diff --git a/packages/client/lib/commands/LPOS_COUNT.spec.ts b/packages/client/lib/commands/LPOS_COUNT.spec.ts index 747ffbc01cf..702ef5a746b 100644 --- a/packages/client/lib/commands/LPOS_COUNT.spec.ts +++ b/packages/client/lib/commands/LPOS_COUNT.spec.ts @@ -1,21 +1,22 @@ import { strict as assert } from 'node:assert'; import testUtils, { GLOBAL } from '../test-utils'; import LPOS_COUNT from './LPOS_COUNT'; +import { parseArgs } from './generic-transformers'; describe('LPOS COUNT', () => { testUtils.isVersionGreaterThanHook([6, 0, 6]); - describe('transformArguments', () => { + describe('processCommand', () => { it('simple', () => { assert.deepEqual( - LPOS_COUNT.transformArguments('key', 'element', 0), + parseArgs(LPOS_COUNT, 'key', 'element', 0), ['LPOS', 'key', 'element', 'COUNT', '0'] ); }); it('with RANK', () => { assert.deepEqual( - LPOS_COUNT.transformArguments('key', 'element', 0, { + parseArgs(LPOS_COUNT, 'key', 'element', 0, { RANK: 0 }), ['LPOS', 'key', 'element', 'RANK', '0', 'COUNT', '0'] @@ -24,20 +25,20 @@ describe('LPOS COUNT', () => { it('with MAXLEN', () => { assert.deepEqual( - LPOS_COUNT.transformArguments('key', 'element', 0, { + parseArgs(LPOS_COUNT, 'key', 'element', 0, { MAXLEN: 10 }), - ['LPOS', 'key', 'element', 'COUNT', '0', 'MAXLEN', '10'] + ['LPOS', 'key', 'element', 'MAXLEN', '10', 'COUNT', '0'] ); }); it('with RANK, MAXLEN', () => { assert.deepEqual( - LPOS_COUNT.transformArguments('key', 'element', 0, { + parseArgs(LPOS_COUNT, 'key', 'element', 0, { RANK: 0, MAXLEN: 10 }), - ['LPOS', 'key', 'element', 'RANK', '0', 'COUNT', '0', 'MAXLEN', '10'] + ['LPOS', 'key', 'element', 'RANK', '0', 'MAXLEN', '10', 'COUNT', '0'] ); }); }); diff --git a/packages/client/lib/commands/LPOS_COUNT.ts b/packages/client/lib/commands/LPOS_COUNT.ts index 1b057cff1f6..e782a2d26ee 100644 --- a/packages/client/lib/commands/LPOS_COUNT.ts +++ b/packages/client/lib/commands/LPOS_COUNT.ts @@ -1,28 +1,20 @@ +import { CommandParser } from '../client/parser'; import { RedisArgument, ArrayReply, NumberReply, Command } from '../RESP/types'; import LPOS, { LPosOptions } from './LPOS'; export default { - FIRST_KEY_INDEX: LPOS.FIRST_KEY_INDEX, + CACHEABLE: LPOS.CACHEABLE, IS_READ_ONLY: LPOS.IS_READ_ONLY, - transformArguments( + parseCommand( + parser: CommandParser, key: RedisArgument, element: RedisArgument, count: number, options?: LPosOptions ) { - const args = ['LPOS', key, element]; + LPOS.parseCommand(parser, key, element, options); - if (options?.RANK !== undefined) { - args.push('RANK', options.RANK.toString()); - } - - args.push('COUNT', count.toString()); - - if (options?.MAXLEN !== undefined) { - args.push('MAXLEN', options.MAXLEN.toString()); - } - - return args; + parser.push('COUNT', count.toString()); }, transformReply: undefined as unknown as () => ArrayReply } as const satisfies Command; diff --git a/packages/client/lib/commands/LPUSH.spec.ts b/packages/client/lib/commands/LPUSH.spec.ts index 8d2ddb5ccc2..09c7d9da772 100644 --- a/packages/client/lib/commands/LPUSH.spec.ts +++ b/packages/client/lib/commands/LPUSH.spec.ts @@ -1,19 +1,20 @@ import { strict as assert } from 'node:assert'; import testUtils, { GLOBAL } from '../test-utils'; import LPUSH from './LPUSH'; +import { parseArgs } from './generic-transformers'; describe('LPUSH', () => { describe('transformArguments', () => { it('string', () => { assert.deepEqual( - LPUSH.transformArguments('key', 'field'), + parseArgs(LPUSH, 'key', 'field'), ['LPUSH', 'key', 'field'] ); }); it('array', () => { assert.deepEqual( - LPUSH.transformArguments('key', ['1', '2']), + parseArgs(LPUSH, 'key', ['1', '2']), ['LPUSH', 'key', '1', '2'] ); }); diff --git a/packages/client/lib/commands/LPUSH.ts b/packages/client/lib/commands/LPUSH.ts index 714db2ae9a9..293029034ee 100644 --- a/packages/client/lib/commands/LPUSH.ts +++ b/packages/client/lib/commands/LPUSH.ts @@ -1,10 +1,12 @@ +import { CommandParser } from '../client/parser'; import { RedisArgument, NumberReply, Command } from '../RESP/types'; -import { RedisVariadicArgument, pushVariadicArguments } from './generic-transformers'; +import { RedisVariadicArgument } from './generic-transformers'; export default { - FIRST_KEY_INDEX: 1, - transformArguments(key: RedisArgument, elements: RedisVariadicArgument) { - return pushVariadicArguments(['LPUSH', key], elements); + parseCommand(parser: CommandParser, key: RedisArgument, elements: RedisVariadicArgument) { + parser.push('LPUSH'); + parser.pushKey(key); + parser.pushVariadic(elements); }, transformReply: undefined as unknown as () => NumberReply } as const satisfies Command; diff --git a/packages/client/lib/commands/LPUSHX.spec.ts b/packages/client/lib/commands/LPUSHX.spec.ts index f7dafe025c7..179a0ddb29e 100644 --- a/packages/client/lib/commands/LPUSHX.spec.ts +++ b/packages/client/lib/commands/LPUSHX.spec.ts @@ -1,19 +1,20 @@ import { strict as assert } from 'node:assert'; import testUtils, { GLOBAL } from '../test-utils'; import LPUSHX from './LPUSHX'; +import { parseArgs } from './generic-transformers'; describe('LPUSHX', () => { describe('transformArguments', () => { it('string', () => { assert.deepEqual( - LPUSHX.transformArguments('key', 'element'), + parseArgs(LPUSHX, 'key', 'element'), ['LPUSHX', 'key', 'element'] ); }); it('array', () => { assert.deepEqual( - LPUSHX.transformArguments('key', ['1', '2']), + parseArgs(LPUSHX, 'key', ['1', '2']), ['LPUSHX', 'key', '1', '2'] ); }); diff --git a/packages/client/lib/commands/LPUSHX.ts b/packages/client/lib/commands/LPUSHX.ts index d6dceda6d02..98dd51a7ac2 100644 --- a/packages/client/lib/commands/LPUSHX.ts +++ b/packages/client/lib/commands/LPUSHX.ts @@ -1,10 +1,12 @@ +import { CommandParser } from '../client/parser'; import { RedisArgument, NumberReply, Command } from '../RESP/types'; -import { RedisVariadicArgument, pushVariadicArguments } from './generic-transformers'; +import { RedisVariadicArgument } from './generic-transformers'; export default { - FIRST_KEY_INDEX: 1, - transformArguments(key: RedisArgument, elements: RedisVariadicArgument) { - return pushVariadicArguments(['LPUSHX', key], elements); + parseCommand(parser: CommandParser, key: RedisArgument, elements: RedisVariadicArgument) { + parser.push('LPUSHX'); + parser.pushKey(key); + parser.pushVariadic(elements); }, transformReply: undefined as unknown as () => NumberReply } as const satisfies Command; diff --git a/packages/client/lib/commands/LRANGE.spec.ts b/packages/client/lib/commands/LRANGE.spec.ts index 3e768cc0731..c0bb046d898 100644 --- a/packages/client/lib/commands/LRANGE.spec.ts +++ b/packages/client/lib/commands/LRANGE.spec.ts @@ -1,11 +1,12 @@ import { strict as assert } from 'node:assert'; import testUtils, { GLOBAL } from '../test-utils'; import LRANGE from './LRANGE'; +import { parseArgs } from './generic-transformers'; describe('LRANGE', () => { - it('transformArguments', () => { + it('processCommand', () => { assert.deepEqual( - LRANGE.transformArguments('key', 0, -1), + parseArgs(LRANGE, 'key', 0, -1), ['LRANGE', 'key', '0', '-1'] ); }); diff --git a/packages/client/lib/commands/LRANGE.ts b/packages/client/lib/commands/LRANGE.ts index 4b4b3488baa..ab033dd88a4 100644 --- a/packages/client/lib/commands/LRANGE.ts +++ b/packages/client/lib/commands/LRANGE.ts @@ -1,19 +1,13 @@ +import { CommandParser } from '../client/parser'; import { RedisArgument, ArrayReply, BlobStringReply, Command } from '../RESP/types'; export default { - FIRST_KEY_INDEX: 1, + CACHEABLE: true, IS_READ_ONLY: true, - transformArguments( - key: RedisArgument, - start: number, - stop: number -) { - return [ - 'LRANGE', - key, - start.toString(), - stop.toString() - ]; + parseCommand(parser: CommandParser, key: RedisArgument, start: number, stop: number) { + parser.push('LRANGE'); + parser.pushKey(key); + parser.push(start.toString(), stop.toString()) }, transformReply: undefined as unknown as () => ArrayReply } as const satisfies Command; diff --git a/packages/client/lib/commands/LREM.spec.ts b/packages/client/lib/commands/LREM.spec.ts index 1a35dab7c54..2a36d8ee2f1 100644 --- a/packages/client/lib/commands/LREM.spec.ts +++ b/packages/client/lib/commands/LREM.spec.ts @@ -1,11 +1,12 @@ import { strict as assert } from 'node:assert'; import testUtils, { GLOBAL } from '../test-utils'; import LREM from './LREM'; +import { parseArgs } from './generic-transformers'; describe('LREM', () => { it('transformArguments', () => { assert.deepEqual( - LREM.transformArguments('key', 0, 'element'), + parseArgs(LREM, 'key', 0, 'element'), ['LREM', 'key', '0', 'element'] ); }); diff --git a/packages/client/lib/commands/LREM.ts b/packages/client/lib/commands/LREM.ts index dbd2002be8b..bb97e3882e7 100644 --- a/packages/client/lib/commands/LREM.ts +++ b/packages/client/lib/commands/LREM.ts @@ -1,19 +1,13 @@ +import { CommandParser } from '../client/parser'; import { RedisArgument, NumberReply, Command } from '../RESP/types'; export default { - FIRST_KEY_INDEX: 1, IS_READ_ONLY: true, - transformArguments( - key: RedisArgument, - count: number, - element: RedisArgument - ) { - return [ - 'LREM', - key, - count.toString(), - element - ]; + parseCommand(parser: CommandParser, key: RedisArgument, count: number, element: RedisArgument) { + parser.push('LREM'); + parser.pushKey(key); + parser.push(count.toString()); + parser.push(element); }, transformReply: undefined as unknown as () => NumberReply } as const satisfies Command; diff --git a/packages/client/lib/commands/LSET.spec.ts b/packages/client/lib/commands/LSET.spec.ts index ae4b1bf9f2f..c7522942402 100644 --- a/packages/client/lib/commands/LSET.spec.ts +++ b/packages/client/lib/commands/LSET.spec.ts @@ -1,11 +1,12 @@ import { strict as assert } from 'node:assert'; import testUtils, { GLOBAL } from '../test-utils'; import LSET from './LSET'; +import { parseArgs } from './generic-transformers'; describe('LSET', () => { it('transformArguments', () => { assert.deepEqual( - LSET.transformArguments('key', 0, 'element'), + parseArgs(LSET, 'key', 0, 'element'), ['LSET', 'key', '0', 'element'] ); }); diff --git a/packages/client/lib/commands/LSET.ts b/packages/client/lib/commands/LSET.ts index edb86baae0f..0fe646fbb73 100644 --- a/packages/client/lib/commands/LSET.ts +++ b/packages/client/lib/commands/LSET.ts @@ -1,19 +1,12 @@ +import { CommandParser } from '../client/parser'; import { RedisArgument, SimpleStringReply, Command } from '../RESP/types'; export default { - FIRST_KEY_INDEX: 1, IS_READ_ONLY: true, - transformArguments( - key: RedisArgument, - index: number, - element: RedisArgument - ) { - return [ - 'LSET', - key, - index.toString(), - element - ]; + parseCommand(parser: CommandParser, key: RedisArgument, index: number, element: RedisArgument) { + parser.push('LSET'); + parser.pushKey(key); + parser.push(index.toString(), element); }, transformReply: undefined as unknown as () => SimpleStringReply<'OK'> } as const satisfies Command; diff --git a/packages/client/lib/commands/LTRIM.spec.ts b/packages/client/lib/commands/LTRIM.spec.ts index 3edc3669737..5b6d77c91de 100644 --- a/packages/client/lib/commands/LTRIM.spec.ts +++ b/packages/client/lib/commands/LTRIM.spec.ts @@ -1,11 +1,12 @@ import { strict as assert } from 'node:assert'; import testUtils, { GLOBAL } from '../test-utils'; import LTRIM from './LTRIM'; +import { parseArgs } from './generic-transformers'; describe('LTRIM', () => { it('transformArguments', () => { assert.deepEqual( - LTRIM.transformArguments('key', 0, -1), + parseArgs(LTRIM, 'key', 0, -1), ['LTRIM', 'key', '0', '-1'] ); }); diff --git a/packages/client/lib/commands/LTRIM.ts b/packages/client/lib/commands/LTRIM.ts index b80aa6264e9..acc7e767d0d 100644 --- a/packages/client/lib/commands/LTRIM.ts +++ b/packages/client/lib/commands/LTRIM.ts @@ -1,18 +1,11 @@ +import { CommandParser } from '../client/parser'; import { RedisArgument, SimpleStringReply, Command } from '../RESP/types'; export default { - FIRST_KEY_INDEX: 1, - transformArguments( - key: RedisArgument, - start: number, - stop: number - ) { - return [ - 'LTRIM', - key, - start.toString(), - stop.toString() - ]; + parseCommand(parser: CommandParser, key: RedisArgument, start: number, stop: number) { + parser.push('LTRIM'); + parser.pushKey(key); + parser.push(start.toString(), stop.toString()); }, transformReply: undefined as unknown as () => SimpleStringReply<'OK'> } as const satisfies Command; diff --git a/packages/client/lib/commands/MEMORY_DOCTOR.spec.ts b/packages/client/lib/commands/MEMORY_DOCTOR.spec.ts index e1d718d7aab..9d822f8e07e 100644 --- a/packages/client/lib/commands/MEMORY_DOCTOR.spec.ts +++ b/packages/client/lib/commands/MEMORY_DOCTOR.spec.ts @@ -1,11 +1,12 @@ import { strict as assert } from 'node:assert'; import testUtils, { GLOBAL } from '../test-utils'; import MEMORY_DOCTOR from './MEMORY_DOCTOR'; +import { parseArgs } from './generic-transformers'; describe('MEMORY DOCTOR', () => { it('transformArguments', () => { assert.deepEqual( - MEMORY_DOCTOR.transformArguments(), + parseArgs(MEMORY_DOCTOR), ['MEMORY', 'DOCTOR'] ); }); diff --git a/packages/client/lib/commands/MEMORY_DOCTOR.ts b/packages/client/lib/commands/MEMORY_DOCTOR.ts index 6f8eb29ef2b..3a2d808db10 100644 --- a/packages/client/lib/commands/MEMORY_DOCTOR.ts +++ b/packages/client/lib/commands/MEMORY_DOCTOR.ts @@ -1,10 +1,11 @@ +import { CommandParser } from '../client/parser'; import { BlobStringReply, Command } from '../RESP/types'; export default { - FIRST_KEY_INDEX: undefined, + NOT_KEYED_COMMAND: true, IS_READ_ONLY: true, - transformArguments() { - return ['MEMORY', 'DOCTOR']; + parseCommand(parser: CommandParser) { + parser.push('MEMORY', 'DOCTOR'); }, transformReply: undefined as unknown as () => BlobStringReply } as const satisfies Command; diff --git a/packages/client/lib/commands/MEMORY_MALLOC-STATS.spec.ts b/packages/client/lib/commands/MEMORY_MALLOC-STATS.spec.ts index 8484fcbf0b7..a4a85f5b994 100644 --- a/packages/client/lib/commands/MEMORY_MALLOC-STATS.spec.ts +++ b/packages/client/lib/commands/MEMORY_MALLOC-STATS.spec.ts @@ -1,11 +1,12 @@ import { strict as assert } from 'node:assert'; import testUtils, { GLOBAL } from '../test-utils'; import MEMORY_MALLOC_STATS from './MEMORY_MALLOC-STATS'; +import { parseArgs } from './generic-transformers'; describe('MEMORY MALLOC-STATS', () => { it('transformArguments', () => { assert.deepEqual( - MEMORY_MALLOC_STATS.transformArguments(), + parseArgs(MEMORY_MALLOC_STATS), ['MEMORY', 'MALLOC-STATS'] ); }); diff --git a/packages/client/lib/commands/MEMORY_MALLOC-STATS.ts b/packages/client/lib/commands/MEMORY_MALLOC-STATS.ts index 31b01a49b5c..af6b5db3347 100644 --- a/packages/client/lib/commands/MEMORY_MALLOC-STATS.ts +++ b/packages/client/lib/commands/MEMORY_MALLOC-STATS.ts @@ -1,10 +1,11 @@ +import { CommandParser } from '../client/parser'; import { BlobStringReply, Command } from '../RESP/types'; export default { - FIRST_KEY_INDEX: undefined, + NOT_KEYED_COMMAND: true, IS_READ_ONLY: true, - transformArguments() { - return ['MEMORY', 'MALLOC-STATS']; + parseCommand(parser: CommandParser) { + parser.push('MEMORY', 'MALLOC-STATS'); }, transformReply: undefined as unknown as () => BlobStringReply } as const satisfies Command; diff --git a/packages/client/lib/commands/MEMORY_PURGE.spec.ts b/packages/client/lib/commands/MEMORY_PURGE.spec.ts index 41b01f87c4b..be5fb738b0a 100644 --- a/packages/client/lib/commands/MEMORY_PURGE.spec.ts +++ b/packages/client/lib/commands/MEMORY_PURGE.spec.ts @@ -1,11 +1,12 @@ import { strict as assert } from 'node:assert'; import testUtils, { GLOBAL } from '../test-utils'; import MEMORY_PURGE from './MEMORY_PURGE'; +import { parseArgs } from './generic-transformers'; describe('MEMORY PURGE', () => { it('transformArguments', () => { assert.deepEqual( - MEMORY_PURGE.transformArguments(), + parseArgs(MEMORY_PURGE), ['MEMORY', 'PURGE'] ); }); diff --git a/packages/client/lib/commands/MEMORY_PURGE.ts b/packages/client/lib/commands/MEMORY_PURGE.ts index b4c48d40102..bbd02890786 100644 --- a/packages/client/lib/commands/MEMORY_PURGE.ts +++ b/packages/client/lib/commands/MEMORY_PURGE.ts @@ -1,10 +1,11 @@ +import { CommandParser } from '../client/parser'; import { SimpleStringReply, Command } from '../RESP/types'; export default { - FIRST_KEY_INDEX: undefined, + NOT_KEYED_COMMAND: true, IS_READ_ONLY: false, - transformArguments() { - return ['MEMORY', 'PURGE']; + parseCommand(parser: CommandParser) { + parser.push('MEMORY', 'PURGE'); }, transformReply: undefined as unknown as () => SimpleStringReply<'OK'> } as const satisfies Command; diff --git a/packages/client/lib/commands/MEMORY_STATS.spec.ts b/packages/client/lib/commands/MEMORY_STATS.spec.ts index 6d5f5b8690b..6aad05116af 100644 --- a/packages/client/lib/commands/MEMORY_STATS.spec.ts +++ b/packages/client/lib/commands/MEMORY_STATS.spec.ts @@ -1,11 +1,12 @@ import { strict as assert } from 'node:assert'; import testUtils, { GLOBAL } from '../test-utils'; import MEMORY_STATS from './MEMORY_STATS'; +import { parseArgs } from './generic-transformers'; describe('MEMORY STATS', () => { it('transformArguments', () => { assert.deepEqual( - MEMORY_STATS.transformArguments(), + parseArgs(MEMORY_STATS), ['MEMORY', 'STATS'] ); }); diff --git a/packages/client/lib/commands/MEMORY_STATS.ts b/packages/client/lib/commands/MEMORY_STATS.ts index f38a0e5f29b..33410535aa9 100644 --- a/packages/client/lib/commands/MEMORY_STATS.ts +++ b/packages/client/lib/commands/MEMORY_STATS.ts @@ -1,3 +1,4 @@ +import { CommandParser } from '../client/parser'; import { TuplesToMapReply, BlobStringReply, NumberReply, DoubleReply, ArrayReply, UnwrapReply, Command, TypeMapping } from '../RESP/types'; import { transformDoubleReply } from './generic-transformers'; @@ -35,10 +36,10 @@ export type MemoryStatsReply = TuplesToMapReply<[ ]>; export default { - FIRST_KEY_INDEX: undefined, + NOT_KEYED_COMMAND: true, IS_READ_ONLY: true, - transformArguments() { - return ['MEMORY', 'STATS']; + parseCommand(parser: CommandParser) { + parser.push('MEMORY', 'STATS'); }, transformReply: { 2: (rawReply: UnwrapReply>, preserve?: any, typeMapping?: TypeMapping) => { diff --git a/packages/client/lib/commands/MEMORY_USAGE.spec.ts b/packages/client/lib/commands/MEMORY_USAGE.spec.ts index 250a6884259..edf673564ee 100644 --- a/packages/client/lib/commands/MEMORY_USAGE.spec.ts +++ b/packages/client/lib/commands/MEMORY_USAGE.spec.ts @@ -1,19 +1,20 @@ import { strict as assert } from 'node:assert'; import testUtils, { GLOBAL } from '../test-utils'; import MEMORY_USAGE from './MEMORY_USAGE'; +import { parseArgs } from './generic-transformers'; describe('MEMORY USAGE', () => { describe('transformArguments', () => { it('simple', () => { assert.deepEqual( - MEMORY_USAGE.transformArguments('key'), + parseArgs(MEMORY_USAGE, 'key'), ['MEMORY', 'USAGE', 'key'] ); }); it('with SAMPLES', () => { assert.deepEqual( - MEMORY_USAGE.transformArguments('key', { + parseArgs(MEMORY_USAGE, 'key', { SAMPLES: 1 }), ['MEMORY', 'USAGE', 'key', 'SAMPLES', '1'] diff --git a/packages/client/lib/commands/MEMORY_USAGE.ts b/packages/client/lib/commands/MEMORY_USAGE.ts index 78b079c2c13..6e85438dbed 100644 --- a/packages/client/lib/commands/MEMORY_USAGE.ts +++ b/packages/client/lib/commands/MEMORY_USAGE.ts @@ -1,3 +1,4 @@ +import { CommandParser } from '../client/parser'; import { NumberReply, NullReply, Command, RedisArgument } from '../RESP/types'; export interface MemoryUsageOptions { @@ -5,16 +6,14 @@ export interface MemoryUsageOptions { } export default { - FIRST_KEY_INDEX: 1, IS_READ_ONLY: true, - transformArguments(key: RedisArgument, options?: MemoryUsageOptions) { - const args = ['MEMORY', 'USAGE', key]; + parseCommand(parser: CommandParser, key: RedisArgument, options?: MemoryUsageOptions) { + parser.push('MEMORY', 'USAGE'); + parser.pushKey(key); if (options?.SAMPLES) { - args.push('SAMPLES', options.SAMPLES.toString()); + parser.push('SAMPLES', options.SAMPLES.toString()); } - - return args; }, transformReply: undefined as unknown as () => NumberReply | NullReply } as const satisfies Command; diff --git a/packages/client/lib/commands/MGET.spec.ts b/packages/client/lib/commands/MGET.spec.ts index 296702a1a03..048fa6f0a58 100644 --- a/packages/client/lib/commands/MGET.spec.ts +++ b/packages/client/lib/commands/MGET.spec.ts @@ -1,11 +1,12 @@ import { strict as assert } from 'node:assert'; import testUtils, { GLOBAL } from '../test-utils'; import MGET from './MGET'; +import { parseArgs } from './generic-transformers'; describe('MGET', () => { - it('transformArguments', () => { + it('processCommand', () => { assert.deepEqual( - MGET.transformArguments(['1', '2']), + parseArgs(MGET, ['1', '2']), ['MGET', '1', '2'] ); }); diff --git a/packages/client/lib/commands/MGET.ts b/packages/client/lib/commands/MGET.ts index 0f0f9e52ffb..ce1e9ba7781 100644 --- a/packages/client/lib/commands/MGET.ts +++ b/packages/client/lib/commands/MGET.ts @@ -1,10 +1,12 @@ +import { CommandParser } from '../client/parser'; import { RedisArgument, BlobStringReply, NullReply, Command } from '../RESP/types'; export default { - FIRST_KEY_INDEX: 1, + CACHEABLE: true, IS_READ_ONLY: true, - transformArguments(keys: Array) { - return ['MGET', ...keys]; + parseCommand(parser: CommandParser, keys: Array) { + parser.push('MGET'); + parser.pushKeys(keys); }, transformReply: undefined as unknown as () => Array } as const satisfies Command; diff --git a/packages/client/lib/commands/MIGRATE.spec.ts b/packages/client/lib/commands/MIGRATE.spec.ts index 880d59a09c7..dd2fbdc82ff 100644 --- a/packages/client/lib/commands/MIGRATE.spec.ts +++ b/packages/client/lib/commands/MIGRATE.spec.ts @@ -1,25 +1,26 @@ import { strict as assert } from 'node:assert'; import MIGRATE from './MIGRATE'; +import { parseArgs } from './generic-transformers'; describe('MIGRATE', () => { describe('transformArguments', () => { it('single key', () => { assert.deepEqual( - MIGRATE.transformArguments('127.0.0.1', 6379, 'key', 0, 10), + parseArgs(MIGRATE, '127.0.0.1', 6379, 'key', 0, 10), ['MIGRATE', '127.0.0.1', '6379', 'key', '0', '10'] ); }); it('multiple keys', () => { assert.deepEqual( - MIGRATE.transformArguments('127.0.0.1', 6379, ['1', '2'], 0, 10), + parseArgs(MIGRATE, '127.0.0.1', 6379, ['1', '2'], 0, 10), ['MIGRATE', '127.0.0.1', '6379', '', '0', '10', 'KEYS', '1', '2'] ); }); it('with COPY', () => { assert.deepEqual( - MIGRATE.transformArguments('127.0.0.1', 6379, 'key', 0, 10, { + parseArgs(MIGRATE, '127.0.0.1', 6379, 'key', 0, 10, { COPY: true }), ['MIGRATE', '127.0.0.1', '6379', 'key', '0', '10', 'COPY'] @@ -28,7 +29,7 @@ describe('MIGRATE', () => { it('with REPLACE', () => { assert.deepEqual( - MIGRATE.transformArguments('127.0.0.1', 6379, 'key', 0, 10, { + parseArgs(MIGRATE, '127.0.0.1', 6379, 'key', 0, 10, { REPLACE: true }), ['MIGRATE', '127.0.0.1', '6379', 'key', '0', '10', 'REPLACE'] @@ -38,7 +39,7 @@ describe('MIGRATE', () => { describe('with AUTH', () => { it('password only', () => { assert.deepEqual( - MIGRATE.transformArguments('127.0.0.1', 6379, 'key', 0, 10, { + parseArgs(MIGRATE, '127.0.0.1', 6379, 'key', 0, 10, { AUTH: { password: 'password' } @@ -49,7 +50,7 @@ describe('MIGRATE', () => { it('username & password', () => { assert.deepEqual( - MIGRATE.transformArguments('127.0.0.1', 6379, 'key', 0, 10, { + parseArgs(MIGRATE, '127.0.0.1', 6379, 'key', 0, 10, { AUTH: { username: 'username', password: 'password' @@ -62,7 +63,7 @@ describe('MIGRATE', () => { it('with COPY, REPLACE, AUTH', () => { assert.deepEqual( - MIGRATE.transformArguments('127.0.0.1', 6379, 'key', 0, 10, { + parseArgs(MIGRATE, '127.0.0.1', 6379, 'key', 0, 10, { COPY: true, REPLACE: true, AUTH: { diff --git a/packages/client/lib/commands/MIGRATE.ts b/packages/client/lib/commands/MIGRATE.ts index 4821f93fcfb..15345060aa7 100644 --- a/packages/client/lib/commands/MIGRATE.ts +++ b/packages/client/lib/commands/MIGRATE.ts @@ -1,3 +1,4 @@ +import { CommandParser } from '../client/parser'; import { RedisArgument, SimpleStringReply, Command } from '../RESP/types'; import { AuthOptions } from './AUTH'; @@ -9,7 +10,8 @@ export interface MigrateOptions { export default { IS_READ_ONLY: false, - transformArguments( + parseCommand( + parser: CommandParser, host: RedisArgument, port: number, key: RedisArgument | Array, @@ -17,37 +19,37 @@ export default { timeout: number, options?: MigrateOptions ) { - const args = ['MIGRATE', host, port.toString()], - isKeyArray = Array.isArray(key); + parser.push('MIGRATE', host, port.toString()); + const isKeyArray = Array.isArray(key); if (isKeyArray) { - args.push(''); + parser.push(''); } else { - args.push(key); + parser.push(key); } - args.push( + parser.push( destinationDb.toString(), timeout.toString() ); if (options?.COPY) { - args.push('COPY'); + parser.push('COPY'); } if (options?.REPLACE) { - args.push('REPLACE'); + parser.push('REPLACE'); } if (options?.AUTH) { if (options.AUTH.username) { - args.push( + parser.push( 'AUTH2', options.AUTH.username, options.AUTH.password ); } else { - args.push( + parser.push( 'AUTH', options.AUTH.password ); @@ -55,13 +57,9 @@ export default { } if (isKeyArray) { - args.push( - 'KEYS', - ...key - ); + parser.push('KEYS'); + parser.pushVariadic(key); } - - return args; }, transformReply: undefined as unknown as () => SimpleStringReply<'OK'> } as const satisfies Command; diff --git a/packages/client/lib/commands/MODULE_LIST.spec.ts b/packages/client/lib/commands/MODULE_LIST.spec.ts index 9c1d3874381..0aab973cf21 100644 --- a/packages/client/lib/commands/MODULE_LIST.spec.ts +++ b/packages/client/lib/commands/MODULE_LIST.spec.ts @@ -1,10 +1,11 @@ import { strict as assert } from 'node:assert'; import MODULE_LIST from './MODULE_LIST'; +import { parseArgs } from './generic-transformers'; describe('MODULE LIST', () => { it('transformArguments', () => { assert.deepEqual( - MODULE_LIST.transformArguments(), + parseArgs(MODULE_LIST), ['MODULE', 'LIST'] ); }); diff --git a/packages/client/lib/commands/MODULE_LIST.ts b/packages/client/lib/commands/MODULE_LIST.ts index 5ddd4e91ff6..85203138f57 100644 --- a/packages/client/lib/commands/MODULE_LIST.ts +++ b/packages/client/lib/commands/MODULE_LIST.ts @@ -1,3 +1,4 @@ +import { CommandParser } from '../client/parser'; import { ArrayReply, TuplesToMapReply, BlobStringReply, NumberReply, UnwrapReply, Resp2Reply, Command } from '../RESP/types'; export type ModuleListReply = ArrayReply>; export default { - FIRST_KEY_INDEX: undefined, + NOT_KEYED_COMMAND: true, IS_READ_ONLY: true, - transformArguments() { - return ['MODULE', 'LIST']; + parseCommand(parser: CommandParser) { + parser.push('MODULE', 'LIST'); }, transformReply: { 2: (reply: UnwrapReply>) => { diff --git a/packages/client/lib/commands/MODULE_LOAD.spec.ts b/packages/client/lib/commands/MODULE_LOAD.spec.ts index 3b7b9dd00a3..418dd9b5daf 100644 --- a/packages/client/lib/commands/MODULE_LOAD.spec.ts +++ b/packages/client/lib/commands/MODULE_LOAD.spec.ts @@ -1,18 +1,19 @@ import { strict as assert } from 'node:assert'; import MODULE_LOAD from './MODULE_LOAD'; +import { parseArgs } from './generic-transformers'; describe('MODULE LOAD', () => { describe('transformArguments', () => { it('simple', () => { assert.deepEqual( - MODULE_LOAD.transformArguments('path'), + parseArgs(MODULE_LOAD, 'path'), ['MODULE', 'LOAD', 'path'] ); }); it('with module args', () => { assert.deepEqual( - MODULE_LOAD.transformArguments('path', ['1', '2']), + parseArgs(MODULE_LOAD, 'path', ['1', '2']), ['MODULE', 'LOAD', 'path', '1', '2'] ); }); diff --git a/packages/client/lib/commands/MODULE_LOAD.ts b/packages/client/lib/commands/MODULE_LOAD.ts index 82c5b81a905..ceb90c1c353 100644 --- a/packages/client/lib/commands/MODULE_LOAD.ts +++ b/packages/client/lib/commands/MODULE_LOAD.ts @@ -1,16 +1,15 @@ +import { CommandParser } from '../client/parser'; import { RedisArgument, SimpleStringReply, Command } from '../RESP/types'; export default { - FIRST_KEY_INDEX: undefined, + NOT_KEYED_COMMAND: true, IS_READ_ONLY: true, - transformArguments(path: RedisArgument, moduleArguments?: Array) { - const args = ['MODULE', 'LOAD', path]; + parseCommand(parser: CommandParser, path: RedisArgument, moduleArguments?: Array) { + parser.push('MODULE', 'LOAD', path); if (moduleArguments) { - return args.concat(moduleArguments); + parser.push(...moduleArguments); } - - return args; }, transformReply: undefined as unknown as () => SimpleStringReply<'OK'> } as const satisfies Command; diff --git a/packages/client/lib/commands/MODULE_UNLOAD.spec.ts b/packages/client/lib/commands/MODULE_UNLOAD.spec.ts index e33fb3a5f4f..581f41e03c8 100644 --- a/packages/client/lib/commands/MODULE_UNLOAD.spec.ts +++ b/packages/client/lib/commands/MODULE_UNLOAD.spec.ts @@ -1,10 +1,11 @@ import { strict as assert } from 'node:assert'; import MODULE_UNLOAD from './MODULE_UNLOAD'; +import { parseArgs } from './generic-transformers'; describe('MODULE UNLOAD', () => { it('transformArguments', () => { assert.deepEqual( - MODULE_UNLOAD.transformArguments('name'), + parseArgs(MODULE_UNLOAD, 'name'), ['MODULE', 'UNLOAD', 'name'] ); }); diff --git a/packages/client/lib/commands/MODULE_UNLOAD.ts b/packages/client/lib/commands/MODULE_UNLOAD.ts index 3a2bc05c0e7..1acc359d0d4 100644 --- a/packages/client/lib/commands/MODULE_UNLOAD.ts +++ b/packages/client/lib/commands/MODULE_UNLOAD.ts @@ -1,10 +1,11 @@ +import { CommandParser } from '../client/parser'; import { RedisArgument, SimpleStringReply, Command } from '../RESP/types'; export default { - FIRST_KEY_INDEX: undefined, + NOT_KEYED_COMMAND: true, IS_READ_ONLY: true, - transformArguments(name: RedisArgument) { - return ['MODULE', 'UNLOAD', name]; + parseCommand(parser: CommandParser, name: RedisArgument) { + parser.push('MODULE', 'UNLOAD', name); }, transformReply: undefined as unknown as () => SimpleStringReply<'OK'> } as const satisfies Command; diff --git a/packages/client/lib/commands/MOVE.spec.ts b/packages/client/lib/commands/MOVE.spec.ts index e767568e72b..91a01378b22 100644 --- a/packages/client/lib/commands/MOVE.spec.ts +++ b/packages/client/lib/commands/MOVE.spec.ts @@ -1,11 +1,12 @@ import { strict as assert } from 'node:assert'; import testUtils, { GLOBAL } from '../test-utils'; import MOVE from './MOVE'; +import { parseArgs } from './generic-transformers'; describe('MOVE', () => { it('transformArguments', () => { assert.deepEqual( - MOVE.transformArguments('key', 1), + parseArgs(MOVE, 'key', 1), ['MOVE', 'key', '1'] ); }); diff --git a/packages/client/lib/commands/MOVE.ts b/packages/client/lib/commands/MOVE.ts index 60aac4b1457..8a6c5427fbc 100644 --- a/packages/client/lib/commands/MOVE.ts +++ b/packages/client/lib/commands/MOVE.ts @@ -1,9 +1,11 @@ +import { CommandParser } from '../client/parser'; import { RedisArgument, NumberReply, Command } from '../RESP/types'; export default { - FIRST_KEY_INDEX: 1, - transformArguments(key: RedisArgument, db: number) { - return ['MOVE', key, db.toString()]; + parseCommand(parser: CommandParser, key: RedisArgument, db: number) { + parser.push('MOVE'); + parser.pushKey(key); + parser.push(db.toString()); }, transformReply: undefined as unknown as () => NumberReply } as const satisfies Command; diff --git a/packages/client/lib/commands/MSET.spec.ts b/packages/client/lib/commands/MSET.spec.ts index 5435e283108..cfb14eceb05 100644 --- a/packages/client/lib/commands/MSET.spec.ts +++ b/packages/client/lib/commands/MSET.spec.ts @@ -1,26 +1,27 @@ import { strict as assert } from 'node:assert'; import testUtils, { GLOBAL } from '../test-utils'; import MSET from './MSET'; +import { parseArgs } from './generic-transformers'; describe('MSET', () => { describe('transformArguments', () => { it("['key1', 'value1', 'key2', 'value2']", () => { assert.deepEqual( - MSET.transformArguments(['key1', 'value1', 'key2', 'value2']), + parseArgs(MSET, ['key1', 'value1', 'key2', 'value2']), ['MSET', 'key1', 'value1', 'key2', 'value2'] ); }); it("[['key1', 'value1'], ['key2', 'value2']]", () => { assert.deepEqual( - MSET.transformArguments([['key1', 'value1'], ['key2', 'value2']]), + parseArgs(MSET, [['key1', 'value1'], ['key2', 'value2']]), ['MSET', 'key1', 'value1', 'key2', 'value2'] ); }); it("{key1: 'value1'. key2: 'value2'}", () => { assert.deepEqual( - MSET.transformArguments({ key1: 'value1', key2: 'value2' }), + parseArgs(MSET, { key1: 'value1', key2: 'value2' }), ['MSET', 'key1', 'value1', 'key2', 'value2'] ); }); diff --git a/packages/client/lib/commands/MSET.ts b/packages/client/lib/commands/MSET.ts index 136fde3e7f7..f761854f09c 100644 --- a/packages/client/lib/commands/MSET.ts +++ b/packages/client/lib/commands/MSET.ts @@ -1,3 +1,4 @@ +import { CommandParser } from '../client/parser'; import { RedisArgument, SimpleStringReply, Command } from '../RESP/types'; export type MSetArguments = @@ -5,23 +6,36 @@ export type MSetArguments = Array | Record; -export function mSetArguments(command: string, toSet: MSetArguments) { - const args: Array = [command]; - +export function parseMSetArguments(parser: CommandParser, toSet: MSetArguments) { if (Array.isArray(toSet)) { - args.push(...toSet.flat()); + if (toSet.length == 0) { + throw new Error("empty toSet Argument") + } + if (Array.isArray(toSet[0])) { + for (const tuple of (toSet as Array<[RedisArgument, RedisArgument]>)) { + parser.pushKey(tuple[0]); + parser.push(tuple[1]); + } + } else { + const arr = toSet as Array; + for (let i=0; i < arr.length; i += 2) { + parser.pushKey(arr[i]); + parser.push(arr[i+1]); + } + } } else { for (const tuple of Object.entries(toSet)) { - args.push(...tuple); + parser.pushKey(tuple[0]); + parser.push(tuple[1]); } } - - return args; } export default { - FIRST_KEY_INDEX: 1, IS_READ_ONLY: true, - transformArguments: mSetArguments.bind(undefined, 'MSET'), + parseCommand(parser: CommandParser, toSet: MSetArguments) { + parser.push('MSET'); + return parseMSetArguments(parser, toSet); + }, transformReply: undefined as unknown as () => SimpleStringReply<'OK'> } as const satisfies Command; diff --git a/packages/client/lib/commands/MSETNX.spec.ts b/packages/client/lib/commands/MSETNX.spec.ts index 1583d04a94e..0a9f636abc7 100644 --- a/packages/client/lib/commands/MSETNX.spec.ts +++ b/packages/client/lib/commands/MSETNX.spec.ts @@ -1,26 +1,27 @@ import { strict as assert } from 'node:assert'; import testUtils, { GLOBAL } from '../test-utils'; import MSETNX from './MSETNX'; +import { parseArgs } from './generic-transformers'; describe('MSETNX', () => { describe('transformArguments', () => { it("['key1', 'value1', 'key2', 'value2']", () => { assert.deepEqual( - MSETNX.transformArguments(['key1', 'value1', 'key2', 'value2']), + parseArgs(MSETNX, ['key1', 'value1', 'key2', 'value2']), ['MSETNX', 'key1', 'value1', 'key2', 'value2'] ); }); it("[['key1', 'value1'], ['key2', 'value2']]", () => { assert.deepEqual( - MSETNX.transformArguments([['key1', 'value1'], ['key2', 'value2']]), + parseArgs(MSETNX, [['key1', 'value1'], ['key2', 'value2']]), ['MSETNX', 'key1', 'value1', 'key2', 'value2'] ); }); it("{key1: 'value1'. key2: 'value2'}", () => { assert.deepEqual( - MSETNX.transformArguments({ key1: 'value1', key2: 'value2' }), + parseArgs(MSETNX, { key1: 'value1', key2: 'value2' }), ['MSETNX', 'key1', 'value1', 'key2', 'value2'] ); }); diff --git a/packages/client/lib/commands/MSETNX.ts b/packages/client/lib/commands/MSETNX.ts index 4867b3ea092..3ecce9525de 100644 --- a/packages/client/lib/commands/MSETNX.ts +++ b/packages/client/lib/commands/MSETNX.ts @@ -1,9 +1,12 @@ +import { CommandParser } from '../client/parser'; import { SimpleStringReply, Command } from '../RESP/types'; -import { mSetArguments } from './MSET'; +import { MSetArguments, parseMSetArguments } from './MSET'; export default { - FIRST_KEY_INDEX: 1, IS_READ_ONLY: true, - transformArguments: mSetArguments.bind(undefined, 'MSETNX'), + parseCommand(parser: CommandParser, toSet: MSetArguments) { + parser.push('MSETNX'); + return parseMSetArguments(parser, toSet); + }, transformReply: undefined as unknown as () => SimpleStringReply<'OK'> } as const satisfies Command; diff --git a/packages/client/lib/commands/OBJECT_ENCODING.spec.ts b/packages/client/lib/commands/OBJECT_ENCODING.spec.ts index 48146f12924..34f82be9b8d 100644 --- a/packages/client/lib/commands/OBJECT_ENCODING.spec.ts +++ b/packages/client/lib/commands/OBJECT_ENCODING.spec.ts @@ -1,11 +1,12 @@ import { strict as assert } from 'node:assert'; import testUtils, { GLOBAL } from '../test-utils'; import OBJECT_ENCODING from './OBJECT_ENCODING'; +import { parseArgs } from './generic-transformers'; describe('OBJECT ENCODING', () => { it('transformArguments', () => { assert.deepEqual( - OBJECT_ENCODING.transformArguments('key'), + parseArgs(OBJECT_ENCODING, 'key'), ['OBJECT', 'ENCODING', 'key'] ); }); diff --git a/packages/client/lib/commands/OBJECT_ENCODING.ts b/packages/client/lib/commands/OBJECT_ENCODING.ts index e74c3f99ebd..3a795f6fb64 100644 --- a/packages/client/lib/commands/OBJECT_ENCODING.ts +++ b/packages/client/lib/commands/OBJECT_ENCODING.ts @@ -1,10 +1,11 @@ +import { CommandParser } from '../client/parser'; import { RedisArgument, BlobStringReply, NullReply, Command } from '../RESP/types'; export default { - FIRST_KEY_INDEX: 2, IS_READ_ONLY: true, - transformArguments(key: RedisArgument) { - return ['OBJECT', 'ENCODING', key]; + parseCommand(parser: CommandParser, key: RedisArgument) { + parser.push('OBJECT', 'ENCODING'); + parser.pushKey(key); }, transformReply: undefined as unknown as () => BlobStringReply | NullReply } as const satisfies Command; diff --git a/packages/client/lib/commands/OBJECT_FREQ.spec.ts b/packages/client/lib/commands/OBJECT_FREQ.spec.ts index cbad72c308d..081501b12e6 100644 --- a/packages/client/lib/commands/OBJECT_FREQ.spec.ts +++ b/packages/client/lib/commands/OBJECT_FREQ.spec.ts @@ -1,11 +1,12 @@ import { strict as assert } from 'node:assert'; import testUtils, { GLOBAL } from '../test-utils'; import OBJECT_FREQ from './OBJECT_FREQ'; +import { parseArgs } from './generic-transformers'; describe('OBJECT FREQ', () => { it('transformArguments', () => { assert.deepEqual( - OBJECT_FREQ.transformArguments('key'), + parseArgs(OBJECT_FREQ, 'key'), ['OBJECT', 'FREQ', 'key'] ); }); diff --git a/packages/client/lib/commands/OBJECT_FREQ.ts b/packages/client/lib/commands/OBJECT_FREQ.ts index b6757c6c57b..dad1124b101 100644 --- a/packages/client/lib/commands/OBJECT_FREQ.ts +++ b/packages/client/lib/commands/OBJECT_FREQ.ts @@ -1,10 +1,11 @@ +import { CommandParser } from '../client/parser'; import { RedisArgument, NumberReply, NullReply, Command } from '../RESP/types'; export default { - FIRST_KEY_INDEX: 2, IS_READ_ONLY: true, - transformArguments(key: RedisArgument) { - return ['OBJECT', 'FREQ', key]; + parseCommand(parser: CommandParser, key: RedisArgument) { + parser.push('OBJECT', 'FREQ'); + parser.pushKey(key); }, transformReply: undefined as unknown as () => NumberReply | NullReply } as const satisfies Command; diff --git a/packages/client/lib/commands/OBJECT_IDLETIME.spec.ts b/packages/client/lib/commands/OBJECT_IDLETIME.spec.ts index 51866427c81..30d47b8133f 100644 --- a/packages/client/lib/commands/OBJECT_IDLETIME.spec.ts +++ b/packages/client/lib/commands/OBJECT_IDLETIME.spec.ts @@ -1,11 +1,12 @@ import { strict as assert } from 'node:assert'; import testUtils, { GLOBAL } from '../test-utils'; import OBJECT_IDLETIME from './OBJECT_IDLETIME'; +import { parseArgs } from './generic-transformers'; describe('OBJECT IDLETIME', () => { it('transformArguments', () => { assert.deepEqual( - OBJECT_IDLETIME.transformArguments('key'), + parseArgs(OBJECT_IDLETIME, 'key'), ['OBJECT', 'IDLETIME', 'key'] ); }); diff --git a/packages/client/lib/commands/OBJECT_IDLETIME.ts b/packages/client/lib/commands/OBJECT_IDLETIME.ts index f0fef24e2d8..2bd32f4e65d 100644 --- a/packages/client/lib/commands/OBJECT_IDLETIME.ts +++ b/packages/client/lib/commands/OBJECT_IDLETIME.ts @@ -1,10 +1,11 @@ +import { CommandParser } from '../client/parser'; import { RedisArgument, NumberReply, NullReply, Command } from '../RESP/types'; export default { - FIRST_KEY_INDEX: 2, IS_READ_ONLY: true, - transformArguments(key: RedisArgument) { - return ['OBJECT', 'IDLETIME', key]; + parseCommand(parser: CommandParser, key: RedisArgument) { + parser.push('OBJECT', 'IDLETIME'); + parser.pushKey(key); }, transformReply: undefined as unknown as () => NumberReply | NullReply } as const satisfies Command; diff --git a/packages/client/lib/commands/OBJECT_REFCOUNT.spec.ts b/packages/client/lib/commands/OBJECT_REFCOUNT.spec.ts index f4309786eb1..8bac08a2e5b 100644 --- a/packages/client/lib/commands/OBJECT_REFCOUNT.spec.ts +++ b/packages/client/lib/commands/OBJECT_REFCOUNT.spec.ts @@ -1,11 +1,12 @@ import { strict as assert } from 'node:assert'; import testUtils, { GLOBAL } from '../test-utils'; import OBJECT_REFCOUNT from './OBJECT_REFCOUNT'; +import { parseArgs } from './generic-transformers'; describe('OBJECT REFCOUNT', () => { it('transformArguments', () => { assert.deepEqual( - OBJECT_REFCOUNT.transformArguments('key'), + parseArgs(OBJECT_REFCOUNT, 'key'), ['OBJECT', 'REFCOUNT', 'key'] ); }); diff --git a/packages/client/lib/commands/OBJECT_REFCOUNT.ts b/packages/client/lib/commands/OBJECT_REFCOUNT.ts index c8381a76bf7..4bee4dea60c 100644 --- a/packages/client/lib/commands/OBJECT_REFCOUNT.ts +++ b/packages/client/lib/commands/OBJECT_REFCOUNT.ts @@ -1,10 +1,11 @@ +import { CommandParser } from '../client/parser'; import { RedisArgument, NumberReply, NullReply, Command } from '../RESP/types'; export default { - FIRST_KEY_INDEX: 2, IS_READ_ONLY: true, - transformArguments(key: RedisArgument) { - return ['OBJECT', 'REFCOUNT', key]; + parseCommand(parser: CommandParser, key: RedisArgument) { + parser.push('OBJECT', 'REFCOUNT'); + parser.pushKey(key); }, transformReply: undefined as unknown as () => NumberReply | NullReply } as const satisfies Command; diff --git a/packages/client/lib/commands/PERSIST.spec.ts b/packages/client/lib/commands/PERSIST.spec.ts index d778a1c0a5f..fff6d7b3a76 100644 --- a/packages/client/lib/commands/PERSIST.spec.ts +++ b/packages/client/lib/commands/PERSIST.spec.ts @@ -1,11 +1,12 @@ import { strict as assert } from 'node:assert'; import testUtils, { GLOBAL } from '../test-utils'; import PERSIST from './PERSIST'; +import { parseArgs } from './generic-transformers'; describe('PERSIST', () => { it('transformArguments', () => { assert.deepEqual( - PERSIST.transformArguments('key'), + parseArgs(PERSIST, 'key'), ['PERSIST', 'key'] ); }); diff --git a/packages/client/lib/commands/PERSIST.ts b/packages/client/lib/commands/PERSIST.ts index 64637fabc14..a1d31523664 100644 --- a/packages/client/lib/commands/PERSIST.ts +++ b/packages/client/lib/commands/PERSIST.ts @@ -1,9 +1,10 @@ +import { CommandParser } from '../client/parser'; import { RedisArgument, NumberReply, Command } from '../RESP/types'; export default { - FIRST_KEY_INDEX: 1, - transformArguments(key: RedisArgument) { - return ['PERSIST', key]; + parseCommand(parser: CommandParser, key: RedisArgument) { + parser.push('PERSIST'); + parser.pushKey(key); }, transformReply: undefined as unknown as () => NumberReply } as const satisfies Command; diff --git a/packages/client/lib/commands/PEXPIRE.spec.ts b/packages/client/lib/commands/PEXPIRE.spec.ts index 61bfe69e9a1..368bc9b4907 100644 --- a/packages/client/lib/commands/PEXPIRE.spec.ts +++ b/packages/client/lib/commands/PEXPIRE.spec.ts @@ -1,19 +1,20 @@ import { strict as assert } from 'node:assert'; import testUtils, { GLOBAL } from '../test-utils'; import PEXPIRE from './PEXPIRE'; +import { parseArgs } from './generic-transformers'; describe('PEXPIRE', () => { describe('transformArguments', () => { it('simple', () => { assert.deepEqual( - PEXPIRE.transformArguments('key', 1), + parseArgs(PEXPIRE, 'key', 1), ['PEXPIRE', 'key', '1'] ); }); it('with set option', () => { assert.deepEqual( - PEXPIRE.transformArguments('key', 1, 'GT'), + parseArgs(PEXPIRE, 'key', 1, 'GT'), ['PEXPIRE', 'key', '1', 'GT'] ); }); diff --git a/packages/client/lib/commands/PEXPIRE.ts b/packages/client/lib/commands/PEXPIRE.ts index 4d64281e922..4053f46c8e2 100644 --- a/packages/client/lib/commands/PEXPIRE.ts +++ b/packages/client/lib/commands/PEXPIRE.ts @@ -1,20 +1,21 @@ +import { CommandParser } from '../client/parser'; import { RedisArgument, NumberReply, Command } from '../RESP/types'; export default { - FIRST_KEY_INDEX: 1, IS_READ_ONLY: true, - transformArguments( + parseCommand( + parser: CommandParser, key: RedisArgument, ms: number, mode?: 'NX' | 'XX' | 'GT' | 'LT' ) { - const args = ['PEXPIRE', key, ms.toString()]; + parser.push('PEXPIRE'); + parser.pushKey(key); + parser.push(ms.toString()); if (mode) { - args.push(mode); + parser.push(mode); } - - return args; }, transformReply: undefined as unknown as () => NumberReply } as const satisfies Command; diff --git a/packages/client/lib/commands/PEXPIREAT.spec.ts b/packages/client/lib/commands/PEXPIREAT.spec.ts index 117c5b512e7..f1053920403 100644 --- a/packages/client/lib/commands/PEXPIREAT.spec.ts +++ b/packages/client/lib/commands/PEXPIREAT.spec.ts @@ -1,12 +1,13 @@ import { strict as assert } from 'node:assert'; import testUtils, { GLOBAL } from '../test-utils'; import PEXPIREAT from './PEXPIREAT'; +import { parseArgs } from './generic-transformers'; describe('PEXPIREAT', () => { describe('transformArguments', () => { it('number', () => { assert.deepEqual( - PEXPIREAT.transformArguments('key', 1), + parseArgs(PEXPIREAT, 'key', 1), ['PEXPIREAT', 'key', '1'] ); }); @@ -14,14 +15,14 @@ describe('PEXPIREAT', () => { it('date', () => { const d = new Date(); assert.deepEqual( - PEXPIREAT.transformArguments('key', d), + parseArgs(PEXPIREAT, 'key', d), ['PEXPIREAT', 'key', d.getTime().toString()] ); }); it('with set option', () => { assert.deepEqual( - PEXPIREAT.transformArguments('key', 1, 'XX'), + parseArgs(PEXPIREAT, 'key', 1, 'XX'), ['PEXPIREAT', 'key', '1', 'XX'] ); }); diff --git a/packages/client/lib/commands/PEXPIREAT.ts b/packages/client/lib/commands/PEXPIREAT.ts index cbae589fba6..e454447c970 100644 --- a/packages/client/lib/commands/PEXPIREAT.ts +++ b/packages/client/lib/commands/PEXPIREAT.ts @@ -1,21 +1,22 @@ +import { CommandParser } from '../client/parser'; import { RedisArgument, NumberReply, Command } from '../RESP/types'; import { transformPXAT } from './generic-transformers'; export default { - FIRST_KEY_INDEX: 1, IS_READ_ONLY: true, - transformArguments( + parseCommand( + parser: CommandParser, key: RedisArgument, msTimestamp: number | Date, mode?: 'NX' | 'XX' | 'GT' | 'LT' ) { - const args = ['PEXPIREAT', key, transformPXAT(msTimestamp)]; + parser.push('PEXPIREAT'); + parser.pushKey(key); + parser.push(transformPXAT(msTimestamp)); if (mode) { - args.push(mode); + parser.push(mode); } - - return args; }, transformReply: undefined as unknown as () => NumberReply } as const satisfies Command; diff --git a/packages/client/lib/commands/PEXPIRETIME.spec.ts b/packages/client/lib/commands/PEXPIRETIME.spec.ts index 25a8293ec59..dbfc69e80dc 100644 --- a/packages/client/lib/commands/PEXPIRETIME.spec.ts +++ b/packages/client/lib/commands/PEXPIRETIME.spec.ts @@ -1,13 +1,14 @@ import { strict as assert } from 'node:assert'; import testUtils, { GLOBAL } from '../test-utils'; import PEXPIRETIME from './PEXPIRETIME'; +import { parseArgs } from './generic-transformers'; describe('PEXPIRETIME', () => { testUtils.isVersionGreaterThanHook([7]); it('transformArguments', () => { assert.deepEqual( - PEXPIRETIME.transformArguments('key'), + parseArgs(PEXPIRETIME, 'key'), ['PEXPIRETIME', 'key'] ); }); diff --git a/packages/client/lib/commands/PEXPIRETIME.ts b/packages/client/lib/commands/PEXPIRETIME.ts index e228351fa0e..b5d04eae230 100644 --- a/packages/client/lib/commands/PEXPIRETIME.ts +++ b/packages/client/lib/commands/PEXPIRETIME.ts @@ -1,10 +1,11 @@ +import { CommandParser } from '../client/parser'; import { RedisArgument, NumberReply, Command } from '../RESP/types'; export default { - FIRST_KEY_INDEX: 1, IS_READ_ONLY: true, - transformArguments(key: RedisArgument) { - return ['PEXPIRETIME', key]; + parseCommand(parser: CommandParser, key: RedisArgument) { + parser.push('PEXPIRETIME'); + parser.pushKey(key); }, transformReply: undefined as unknown as () => NumberReply } as const satisfies Command; diff --git a/packages/client/lib/commands/PFADD.spec.ts b/packages/client/lib/commands/PFADD.spec.ts index 04ba44164df..55c4311e638 100644 --- a/packages/client/lib/commands/PFADD.spec.ts +++ b/packages/client/lib/commands/PFADD.spec.ts @@ -1,19 +1,20 @@ import { strict as assert } from 'node:assert'; import testUtils, { GLOBAL } from '../test-utils'; import PFADD from './PFADD'; +import { parseArgs } from './generic-transformers'; describe('PFADD', () => { describe('transformArguments', () => { it('string', () => { assert.deepEqual( - PFADD.transformArguments('key', 'element'), + parseArgs(PFADD, 'key', 'element'), ['PFADD', 'key', 'element'] ); }); it('array', () => { assert.deepEqual( - PFADD.transformArguments('key', ['1', '2']), + parseArgs(PFADD, 'key', ['1', '2']), ['PFADD', 'key', '1', '2'] ); }); diff --git a/packages/client/lib/commands/PFADD.ts b/packages/client/lib/commands/PFADD.ts index d7f198fbe55..94c2d1d5ae6 100644 --- a/packages/client/lib/commands/PFADD.ts +++ b/packages/client/lib/commands/PFADD.ts @@ -1,14 +1,15 @@ +import { CommandParser } from '../client/parser'; import { RedisArgument, NumberReply, Command } from '../RESP/types'; -import { RedisVariadicArgument, pushVariadicArguments } from './generic-transformers'; +import { RedisVariadicArgument } from './generic-transformers'; export default { - FIRST_KEY_INDEX: 1, IS_READ_ONLY: true, - transformArguments(key: RedisArgument, element?: RedisVariadicArgument) { - const args = ['PFADD', key]; - if (!element) return args; - - return pushVariadicArguments(args, element); + parseCommand(parser: CommandParser, key: RedisArgument, element?: RedisVariadicArgument) { + parser.push('PFADD') + parser.pushKey(key); + if (element) { + parser.pushVariadic(element); + } }, transformReply: undefined as unknown as () => NumberReply } as const satisfies Command; diff --git a/packages/client/lib/commands/PFCOUNT.spec.ts b/packages/client/lib/commands/PFCOUNT.spec.ts index ebb54ea1d72..aec2ebecf0b 100644 --- a/packages/client/lib/commands/PFCOUNT.spec.ts +++ b/packages/client/lib/commands/PFCOUNT.spec.ts @@ -1,19 +1,20 @@ import { strict as assert } from 'node:assert'; import testUtils, { GLOBAL } from '../test-utils'; import PFCOUNT from './PFCOUNT'; +import { parseArgs } from './generic-transformers'; describe('PFCOUNT', () => { describe('transformArguments', () => { it('string', () => { assert.deepEqual( - PFCOUNT.transformArguments('key'), + parseArgs(PFCOUNT, 'key'), ['PFCOUNT', 'key'] ); }); it('array', () => { assert.deepEqual( - PFCOUNT.transformArguments(['1', '2']), + parseArgs(PFCOUNT, ['1', '2']), ['PFCOUNT', '1', '2'] ); }); diff --git a/packages/client/lib/commands/PFCOUNT.ts b/packages/client/lib/commands/PFCOUNT.ts index 5b46eb00d92..46d2e2ed71f 100644 --- a/packages/client/lib/commands/PFCOUNT.ts +++ b/packages/client/lib/commands/PFCOUNT.ts @@ -1,11 +1,12 @@ +import { CommandParser } from '../client/parser'; import { NumberReply, Command } from '../RESP/types'; -import { RedisVariadicArgument, pushVariadicArguments } from './generic-transformers'; +import { RedisVariadicArgument } from './generic-transformers'; export default { - FIRST_KEY_INDEX: 1, IS_READ_ONLY: true, - transformArguments(key: RedisVariadicArgument) { - return pushVariadicArguments(['PFCOUNT'], key); + parseCommand(parser: CommandParser, keys: RedisVariadicArgument) { + parser.push('PFCOUNT'); + parser.pushKeys(keys); }, transformReply: undefined as unknown as () => NumberReply } as const satisfies Command; diff --git a/packages/client/lib/commands/PFMERGE.spec.ts b/packages/client/lib/commands/PFMERGE.spec.ts index bb2444b3ef1..a286e932913 100644 --- a/packages/client/lib/commands/PFMERGE.spec.ts +++ b/packages/client/lib/commands/PFMERGE.spec.ts @@ -1,19 +1,20 @@ import { strict as assert } from 'node:assert'; import testUtils, { GLOBAL } from '../test-utils'; import PFMERGE from './PFMERGE'; +import { parseArgs } from './generic-transformers'; describe('PFMERGE', () => { describe('transformArguments', () => { it('string', () => { assert.deepEqual( - PFMERGE.transformArguments('destination', 'source'), + parseArgs(PFMERGE, 'destination', 'source'), ['PFMERGE', 'destination', 'source'] ); }); it('array', () => { assert.deepEqual( - PFMERGE.transformArguments('destination', ['1', '2']), + parseArgs(PFMERGE, 'destination', ['1', '2']), ['PFMERGE', 'destination', '1', '2'] ); }); diff --git a/packages/client/lib/commands/PFMERGE.ts b/packages/client/lib/commands/PFMERGE.ts index eeeeb5173db..e8eccf1afff 100644 --- a/packages/client/lib/commands/PFMERGE.ts +++ b/packages/client/lib/commands/PFMERGE.ts @@ -1,16 +1,18 @@ +import { CommandParser } from '../client/parser'; import { RedisArgument, SimpleStringReply, Command } from '../RESP/types'; -import { RedisVariadicArgument, pushVariadicArguments } from './generic-transformers'; +import { RedisVariadicArgument } from './generic-transformers'; export default { - FIRST_KEY_INDEX: 1, - transformArguments( + parseCommand( + parser: CommandParser, destination: RedisArgument, - source?: RedisVariadicArgument + sources?: RedisVariadicArgument ) { - const args = ['PFMERGE', destination]; - if (!source) return args; - - return pushVariadicArguments(args, source); + parser.push('PFMERGE'); + parser.pushKey(destination); + if (sources) { + parser.pushKeys(sources); + } }, transformReply: undefined as unknown as () => SimpleStringReply } as const satisfies Command; diff --git a/packages/client/lib/commands/PING.spec.ts b/packages/client/lib/commands/PING.spec.ts index 0cd75a6a8de..56f513685f4 100644 --- a/packages/client/lib/commands/PING.spec.ts +++ b/packages/client/lib/commands/PING.spec.ts @@ -1,19 +1,20 @@ import { strict as assert } from 'node:assert'; import testUtils, { GLOBAL } from '../test-utils'; import PING from './PING'; +import { parseArgs } from './generic-transformers'; describe('PING', () => { describe('transformArguments', () => { it('default', () => { assert.deepEqual( - PING.transformArguments(), + parseArgs(PING), ['PING'] ); }); it('with message', () => { assert.deepEqual( - PING.transformArguments('message'), + parseArgs(PING, 'message'), ['PING', 'message'] ); }); diff --git a/packages/client/lib/commands/PING.ts b/packages/client/lib/commands/PING.ts index 7f6fd31047e..26807eeeba4 100644 --- a/packages/client/lib/commands/PING.ts +++ b/packages/client/lib/commands/PING.ts @@ -1,15 +1,14 @@ +import { CommandParser } from '../client/parser'; import { RedisArgument, SimpleStringReply, BlobStringReply, Command } from '../RESP/types'; export default { - FIRST_KEY_INDEX: undefined, + NOT_KEYED_COMMAND: true, IS_READ_ONLY: true, - transformArguments(message?: RedisArgument) { - const args: Array = ['PING']; + parseCommand(parser: CommandParser, message?: RedisArgument) { + parser.push('PING'); if (message) { - args.push(message); + parser.push(message); } - - return args; }, transformReply: undefined as unknown as () => SimpleStringReply | BlobStringReply } as const satisfies Command; diff --git a/packages/client/lib/commands/PSETEX.spec.ts b/packages/client/lib/commands/PSETEX.spec.ts index fd7bcd2dff2..8580e2f8e9d 100644 --- a/packages/client/lib/commands/PSETEX.spec.ts +++ b/packages/client/lib/commands/PSETEX.spec.ts @@ -1,11 +1,12 @@ import { strict as assert } from 'node:assert'; import testUtils, { GLOBAL } from '../test-utils'; import PSETEX from './PSETEX'; +import { parseArgs } from './generic-transformers'; describe('PSETEX', () => { it('transformArguments', () => { assert.deepEqual( - PSETEX.transformArguments('key', 1, 'value'), + parseArgs(PSETEX, 'key', 1, 'value'), ['PSETEX', 'key', '1', 'value'] ); }); diff --git a/packages/client/lib/commands/PSETEX.ts b/packages/client/lib/commands/PSETEX.ts index 4e345a1a1c9..03a58546d67 100644 --- a/packages/client/lib/commands/PSETEX.ts +++ b/packages/client/lib/commands/PSETEX.ts @@ -1,18 +1,11 @@ +import { CommandParser } from '../client/parser'; import { RedisArgument, SimpleStringReply, Command } from '../RESP/types'; export default { - FIRST_KEY_INDEX: 1, - transformArguments( - key: RedisArgument, - ms: number, - value: RedisArgument - ) { - return [ - 'PSETEX', - key, - ms.toString(), - value - ]; + parseCommand(parser: CommandParser, key: RedisArgument, ms: number, value: RedisArgument) { + parser.push('PSETEX'); + parser.pushKey(key); + parser.push(ms.toString(), value); }, transformReply: undefined as unknown as () => SimpleStringReply<'OK'> } as const satisfies Command; diff --git a/packages/client/lib/commands/PTTL.spec.ts b/packages/client/lib/commands/PTTL.spec.ts index 65a9f5dd0ff..deb04bad97e 100644 --- a/packages/client/lib/commands/PTTL.spec.ts +++ b/packages/client/lib/commands/PTTL.spec.ts @@ -1,11 +1,12 @@ import { strict as assert } from 'node:assert'; import testUtils, { GLOBAL } from '../test-utils'; import PTTL from './PTTL'; +import { parseArgs } from './generic-transformers'; describe('PTTL', () => { it('transformArguments', () => { assert.deepEqual( - PTTL.transformArguments('key'), + parseArgs(PTTL, 'key'), ['PTTL', 'key'] ); }); diff --git a/packages/client/lib/commands/PTTL.ts b/packages/client/lib/commands/PTTL.ts index 35854337877..5717c51179f 100644 --- a/packages/client/lib/commands/PTTL.ts +++ b/packages/client/lib/commands/PTTL.ts @@ -1,10 +1,11 @@ +import { CommandParser } from '../client/parser'; import { RedisArgument, NumberReply, Command } from '../RESP/types'; export default { - FIRST_KEY_INDEX: 1, IS_READ_ONLY: true, - transformArguments(key: RedisArgument) { - return ['PTTL', key]; + parseCommand(parser: CommandParser, key: RedisArgument) { + parser.push('PTTL'); + parser.pushKey(key); }, transformReply: undefined as unknown as () => NumberReply } as const satisfies Command; diff --git a/packages/client/lib/commands/PUBLISH.spec.ts b/packages/client/lib/commands/PUBLISH.spec.ts index ec1f108e5ee..930adc8c4d7 100644 --- a/packages/client/lib/commands/PUBLISH.spec.ts +++ b/packages/client/lib/commands/PUBLISH.spec.ts @@ -1,11 +1,12 @@ import { strict as assert } from 'node:assert'; import testUtils, { GLOBAL } from '../test-utils'; import PUBLISH from './PUBLISH'; +import { parseArgs } from './generic-transformers'; describe('PUBLISH', () => { it('transformArguments', () => { assert.deepEqual( - PUBLISH.transformArguments('channel', 'message'), + parseArgs(PUBLISH, 'channel', 'message'), ['PUBLISH', 'channel', 'message'] ); }); diff --git a/packages/client/lib/commands/PUBLISH.ts b/packages/client/lib/commands/PUBLISH.ts index e790ff16c4d..557efd18834 100644 --- a/packages/client/lib/commands/PUBLISH.ts +++ b/packages/client/lib/commands/PUBLISH.ts @@ -1,11 +1,12 @@ +import { CommandParser } from '../client/parser'; import { RedisArgument, NumberReply, Command } from '../RESP/types'; export default { - FIRST_KEY_INDEX: undefined, + NOT_KEYED_COMMAND: true, IS_READ_ONLY: true, IS_FORWARD_COMMAND: true, - transformArguments(channel: RedisArgument, message: RedisArgument) { - return ['PUBLISH', channel, message]; + parseCommand(parser: CommandParser, channel: RedisArgument, message: RedisArgument) { + parser.push('PUBLISH', channel, message); }, transformReply: undefined as unknown as () => NumberReply } as const satisfies Command; diff --git a/packages/client/lib/commands/PUBSUB_CHANNELS.spec.ts b/packages/client/lib/commands/PUBSUB_CHANNELS.spec.ts index 2fe02523ed1..369e339a497 100644 --- a/packages/client/lib/commands/PUBSUB_CHANNELS.spec.ts +++ b/packages/client/lib/commands/PUBSUB_CHANNELS.spec.ts @@ -1,19 +1,20 @@ import { strict as assert } from 'node:assert'; import testUtils, { GLOBAL } from '../test-utils'; import PUBSUB_CHANNELS from './PUBSUB_CHANNELS'; +import { parseArgs } from './generic-transformers'; describe('PUBSUB CHANNELS', () => { describe('transformArguments', () => { it('simple', () => { assert.deepEqual( - PUBSUB_CHANNELS.transformArguments(), + parseArgs(PUBSUB_CHANNELS), ['PUBSUB', 'CHANNELS'] ); }); it('with pattern', () => { assert.deepEqual( - PUBSUB_CHANNELS.transformArguments('patter*'), + parseArgs(PUBSUB_CHANNELS, 'patter*'), ['PUBSUB', 'CHANNELS', 'patter*'] ); }); diff --git a/packages/client/lib/commands/PUBSUB_CHANNELS.ts b/packages/client/lib/commands/PUBSUB_CHANNELS.ts index 4bf7abd75dc..0f53c79a78a 100644 --- a/packages/client/lib/commands/PUBSUB_CHANNELS.ts +++ b/packages/client/lib/commands/PUBSUB_CHANNELS.ts @@ -1,16 +1,15 @@ +import { CommandParser } from '../client/parser'; import { RedisArgument, ArrayReply, BlobStringReply, Command } from '../RESP/types'; export default { - FIRST_KEY_INDEX: undefined, + NOT_KEYED_COMMAND: true, IS_READ_ONLY: true, - transformArguments(pattern?: RedisArgument) { - const args: Array = ['PUBSUB', 'CHANNELS']; + parseCommand(parser: CommandParser, pattern?: RedisArgument) { + parser.push('PUBSUB', 'CHANNELS'); if (pattern) { - args.push(pattern); + parser.push(pattern); } - - return args; }, transformReply: undefined as unknown as () => ArrayReply } as const satisfies Command; diff --git a/packages/client/lib/commands/PUBSUB_NUMPAT.spec.ts b/packages/client/lib/commands/PUBSUB_NUMPAT.spec.ts index 43a2b4b5c0e..d75256bb43c 100644 --- a/packages/client/lib/commands/PUBSUB_NUMPAT.spec.ts +++ b/packages/client/lib/commands/PUBSUB_NUMPAT.spec.ts @@ -1,11 +1,12 @@ import { strict as assert } from 'node:assert'; import testUtils, { GLOBAL } from '../test-utils'; import PUBSUB_NUMPAT from './PUBSUB_NUMPAT'; +import { parseArgs } from './generic-transformers'; describe('PUBSUB NUMPAT', () => { it('transformArguments', () => { assert.deepEqual( - PUBSUB_NUMPAT.transformArguments(), + parseArgs(PUBSUB_NUMPAT), ['PUBSUB', 'NUMPAT'] ); }); diff --git a/packages/client/lib/commands/PUBSUB_NUMPAT.ts b/packages/client/lib/commands/PUBSUB_NUMPAT.ts index e8a0738dc72..173446e023b 100644 --- a/packages/client/lib/commands/PUBSUB_NUMPAT.ts +++ b/packages/client/lib/commands/PUBSUB_NUMPAT.ts @@ -1,10 +1,11 @@ +import { CommandParser } from '../client/parser'; import { NumberReply, Command } from '../RESP/types'; export default { - FIRST_KEY_INDEX: undefined, + NOT_KEYED_COMMAND: true, IS_READ_ONLY: true, - transformArguments() { - return ['PUBSUB', 'NUMPAT']; + parseCommand(parser: CommandParser) { + parser.push('PUBSUB', 'NUMPAT'); }, transformReply: undefined as unknown as () => NumberReply } as const satisfies Command; diff --git a/packages/client/lib/commands/PUBSUB_NUMSUB.spec.ts b/packages/client/lib/commands/PUBSUB_NUMSUB.spec.ts index 151dc219288..11339ae2bb5 100644 --- a/packages/client/lib/commands/PUBSUB_NUMSUB.spec.ts +++ b/packages/client/lib/commands/PUBSUB_NUMSUB.spec.ts @@ -1,26 +1,27 @@ import { strict as assert } from 'node:assert'; import testUtils, { GLOBAL } from '../test-utils'; import PUBSUB_NUMSUB from './PUBSUB_NUMSUB'; +import { parseArgs } from './generic-transformers'; describe('PUBSUB NUMSUB', () => { describe('transformArguments', () => { it('simple', () => { assert.deepEqual( - PUBSUB_NUMSUB.transformArguments(), + parseArgs(PUBSUB_NUMSUB), ['PUBSUB', 'NUMSUB'] ); }); it('string', () => { assert.deepEqual( - PUBSUB_NUMSUB.transformArguments('channel'), + parseArgs(PUBSUB_NUMSUB, 'channel'), ['PUBSUB', 'NUMSUB', 'channel'] ); }); it('array', () => { assert.deepEqual( - PUBSUB_NUMSUB.transformArguments(['1', '2']), + parseArgs(PUBSUB_NUMSUB, ['1', '2']), ['PUBSUB', 'NUMSUB', '1', '2'] ); }); diff --git a/packages/client/lib/commands/PUBSUB_NUMSUB.ts b/packages/client/lib/commands/PUBSUB_NUMSUB.ts index 1f7c41f5bdd..cc74d5d8a73 100644 --- a/packages/client/lib/commands/PUBSUB_NUMSUB.ts +++ b/packages/client/lib/commands/PUBSUB_NUMSUB.ts @@ -1,15 +1,16 @@ +import { CommandParser } from '../client/parser'; import { ArrayReply, BlobStringReply, NumberReply, UnwrapReply, Command } from '../RESP/types'; -import { RedisVariadicArgument, pushVariadicArguments } from './generic-transformers'; +import { RedisVariadicArgument } from './generic-transformers'; export default { - FIRST_KEY_INDEX: undefined, + NOT_KEYED_COMMAND: true, IS_READ_ONLY: true, - transformArguments(channels?: RedisVariadicArgument) { - const args = ['PUBSUB', 'NUMSUB']; + parseCommand(parser: CommandParser, channels?: RedisVariadicArgument) { + parser.push('PUBSUB', 'NUMSUB'); - if (channels) return pushVariadicArguments(args, channels); - - return args; + if (channels) { + parser.pushVariadic(channels); + } }, transformReply(rawReply: UnwrapReply>) { const reply = Object.create(null); diff --git a/packages/client/lib/commands/PUBSUB_SHARDCHANNELS.spec.ts b/packages/client/lib/commands/PUBSUB_SHARDCHANNELS.spec.ts index 77982b34670..36597a9cfd8 100644 --- a/packages/client/lib/commands/PUBSUB_SHARDCHANNELS.spec.ts +++ b/packages/client/lib/commands/PUBSUB_SHARDCHANNELS.spec.ts @@ -1,6 +1,7 @@ import { strict as assert } from 'node:assert'; import testUtils, { GLOBAL } from '../test-utils'; import PUBSUB_SHARDCHANNELS from './PUBSUB_SHARDCHANNELS'; +import { parseArgs } from './generic-transformers'; describe('PUBSUB SHARDCHANNELS', () => { testUtils.isVersionGreaterThanHook([7]); @@ -8,14 +9,14 @@ describe('PUBSUB SHARDCHANNELS', () => { describe('transformArguments', () => { it('without pattern', () => { assert.deepEqual( - PUBSUB_SHARDCHANNELS.transformArguments(), + parseArgs(PUBSUB_SHARDCHANNELS), ['PUBSUB', 'SHARDCHANNELS'] ); }); it('with pattern', () => { assert.deepEqual( - PUBSUB_SHARDCHANNELS.transformArguments('patter*'), + parseArgs(PUBSUB_SHARDCHANNELS, 'patter*'), ['PUBSUB', 'SHARDCHANNELS', 'patter*'] ); }); diff --git a/packages/client/lib/commands/PUBSUB_SHARDCHANNELS.ts b/packages/client/lib/commands/PUBSUB_SHARDCHANNELS.ts index 74d78c02614..46ac2005fc3 100644 --- a/packages/client/lib/commands/PUBSUB_SHARDCHANNELS.ts +++ b/packages/client/lib/commands/PUBSUB_SHARDCHANNELS.ts @@ -1,16 +1,15 @@ +import { CommandParser } from '../client/parser'; import { RedisArgument, ArrayReply, BlobStringReply, Command } from '../RESP/types'; export default { - FIRST_KEY_INDEX: undefined, + NOT_KEYED_COMMAND: true, IS_READ_ONLY: true, - transformArguments(pattern?: RedisArgument) { - const args: Array = ['PUBSUB', 'SHARDCHANNELS']; + parseCommand(parser: CommandParser, pattern?: RedisArgument) { + parser.push('PUBSUB', 'SHARDCHANNELS'); if (pattern) { - args.push(pattern); + parser.push(pattern); } - - return args; }, transformReply: undefined as unknown as () => ArrayReply } as const satisfies Command; diff --git a/packages/client/lib/commands/PUBSUB_SHARDNUMSUB.spec.ts b/packages/client/lib/commands/PUBSUB_SHARDNUMSUB.spec.ts index e036a6eae5b..e335941897d 100644 --- a/packages/client/lib/commands/PUBSUB_SHARDNUMSUB.spec.ts +++ b/packages/client/lib/commands/PUBSUB_SHARDNUMSUB.spec.ts @@ -1,6 +1,7 @@ import { strict as assert } from 'assert'; import testUtils, { GLOBAL } from '../test-utils'; import PUBSUB_SHARDNUMSUB from './PUBSUB_SHARDNUMSUB'; +import { parseArgs } from './generic-transformers'; describe('PUBSUB SHARDNUMSUB', () => { testUtils.isVersionGreaterThanHook([7]); @@ -8,21 +9,21 @@ describe('PUBSUB SHARDNUMSUB', () => { describe('transformArguments', () => { it('simple', () => { assert.deepEqual( - PUBSUB_SHARDNUMSUB.transformArguments(), + parseArgs(PUBSUB_SHARDNUMSUB), ['PUBSUB', 'SHARDNUMSUB'] ); }); it('string', () => { assert.deepEqual( - PUBSUB_SHARDNUMSUB.transformArguments('channel'), + parseArgs(PUBSUB_SHARDNUMSUB, 'channel'), ['PUBSUB', 'SHARDNUMSUB', 'channel'] ); }); it('array', () => { assert.deepEqual( - PUBSUB_SHARDNUMSUB.transformArguments(['1', '2']), + parseArgs(PUBSUB_SHARDNUMSUB, ['1', '2']), ['PUBSUB', 'SHARDNUMSUB', '1', '2'] ); }); diff --git a/packages/client/lib/commands/PUBSUB_SHARDNUMSUB.ts b/packages/client/lib/commands/PUBSUB_SHARDNUMSUB.ts index 0ef82477006..220eadeabe3 100644 --- a/packages/client/lib/commands/PUBSUB_SHARDNUMSUB.ts +++ b/packages/client/lib/commands/PUBSUB_SHARDNUMSUB.ts @@ -1,15 +1,15 @@ +import { CommandParser } from '../client/parser'; import { ArrayReply, BlobStringReply, NumberReply, UnwrapReply, Command } from '../RESP/types'; -import { RedisVariadicArgument, pushVariadicArguments } from './generic-transformers'; +import { RedisVariadicArgument } from './generic-transformers'; export default { - FIRST_KEY_INDEX: undefined, IS_READ_ONLY: true, - transformArguments(channels?: RedisVariadicArgument) { - const args = ['PUBSUB', 'SHARDNUMSUB']; + parseCommand(parser: CommandParser, channels?: RedisVariadicArgument) { + parser.push('PUBSUB', 'SHARDNUMSUB'); - if (channels) return pushVariadicArguments(args, channels); - - return args; + if (channels) { + parser.pushVariadic(channels); + } }, transformReply(reply: UnwrapReply>) { const transformedReply: Record = Object.create(null); diff --git a/packages/client/lib/commands/RANDOMKEY.spec.ts b/packages/client/lib/commands/RANDOMKEY.spec.ts index 31de60d7a99..f86617a3b75 100644 --- a/packages/client/lib/commands/RANDOMKEY.spec.ts +++ b/packages/client/lib/commands/RANDOMKEY.spec.ts @@ -1,11 +1,12 @@ import { strict as assert } from 'node:assert'; import testUtils, { GLOBAL } from '../test-utils'; import RANDOMKEY from './RANDOMKEY'; +import { parseArgs } from './generic-transformers'; describe('RANDOMKEY', () => { it('transformArguments', () => { assert.deepEqual( - RANDOMKEY.transformArguments(), + parseArgs(RANDOMKEY), ['RANDOMKEY'] ); }); diff --git a/packages/client/lib/commands/RANDOMKEY.ts b/packages/client/lib/commands/RANDOMKEY.ts index 42028ebbe38..97d040a0d1d 100644 --- a/packages/client/lib/commands/RANDOMKEY.ts +++ b/packages/client/lib/commands/RANDOMKEY.ts @@ -1,10 +1,11 @@ +import { CommandParser } from '../client/parser'; import { NumberReply, Command } from '../RESP/types'; export default { - FIRST_KEY_INDEX: undefined, + NOT_KEYED_COMMAND: true, IS_READ_ONLY: true, - transformArguments() { - return ['RANDOMKEY']; + parseCommand(parser: CommandParser) { + parser.push('RANDOMKEY'); }, transformReply: undefined as unknown as () => NumberReply } as const satisfies Command; diff --git a/packages/client/lib/commands/READONLY.spec.ts b/packages/client/lib/commands/READONLY.spec.ts index 14bb047349a..ac303322330 100644 --- a/packages/client/lib/commands/READONLY.spec.ts +++ b/packages/client/lib/commands/READONLY.spec.ts @@ -1,10 +1,11 @@ import { strict as assert } from 'node:assert'; import READONLY from './READONLY'; +import { parseArgs } from './generic-transformers'; describe('READONLY', () => { it('transformArguments', () => { assert.deepEqual( - READONLY.transformArguments(), + parseArgs(READONLY), ['READONLY'] ); }); diff --git a/packages/client/lib/commands/READONLY.ts b/packages/client/lib/commands/READONLY.ts index bb15834550e..ce3300c5321 100644 --- a/packages/client/lib/commands/READONLY.ts +++ b/packages/client/lib/commands/READONLY.ts @@ -1,10 +1,11 @@ +import { CommandParser } from '../client/parser'; import { SimpleStringReply, Command } from '../RESP/types'; export default { - FIRST_KEY_INDEX: undefined, + NOT_KEYED_COMMAND: true, IS_READ_ONLY: true, - transformArguments() { - return ['READONLY']; + parseCommand(parser: CommandParser) { + parser.push('READONLY'); }, transformReply: undefined as unknown as () => SimpleStringReply } as const satisfies Command; diff --git a/packages/client/lib/commands/READWRITE.spec.ts b/packages/client/lib/commands/READWRITE.spec.ts index 94a88a0dcc7..cc3f99a5d16 100644 --- a/packages/client/lib/commands/READWRITE.spec.ts +++ b/packages/client/lib/commands/READWRITE.spec.ts @@ -1,10 +1,11 @@ import { strict as assert } from 'node:assert'; import READWRITE from './READWRITE'; +import { parseArgs } from './generic-transformers'; describe('READWRITE', () => { it('transformArguments', () => { assert.deepEqual( - READWRITE.transformArguments(), + parseArgs(READWRITE), ['READWRITE'] ); }); diff --git a/packages/client/lib/commands/READWRITE.ts b/packages/client/lib/commands/READWRITE.ts index fe70e15d4c8..7d9d8c7e00a 100644 --- a/packages/client/lib/commands/READWRITE.ts +++ b/packages/client/lib/commands/READWRITE.ts @@ -1,10 +1,11 @@ +import { CommandParser } from '../client/parser'; import { SimpleStringReply, Command } from '../RESP/types'; export default { - FIRST_KEY_INDEX: undefined, + NOT_KEYED_COMMAND: true, IS_READ_ONLY: true, - transformArguments() { - return ['READWRITE']; + parseCommand(parser: CommandParser) { + parser.push('READWRITE'); }, transformReply: undefined as unknown as () => SimpleStringReply } as const satisfies Command; diff --git a/packages/client/lib/commands/RENAME.spec.ts b/packages/client/lib/commands/RENAME.spec.ts index cf3dccbf3e7..05dd9417b96 100644 --- a/packages/client/lib/commands/RENAME.spec.ts +++ b/packages/client/lib/commands/RENAME.spec.ts @@ -1,11 +1,12 @@ import { strict as assert } from 'node:assert'; import testUtils, { GLOBAL } from '../test-utils'; import RENAME from './RENAME'; +import { parseArgs } from './generic-transformers'; describe('RENAME', () => { it('transformArguments', () => { assert.deepEqual( - RENAME.transformArguments('source', 'destination'), + parseArgs(RENAME, 'source', 'destination'), ['RENAME', 'source', 'destination'] ); }); diff --git a/packages/client/lib/commands/RENAME.ts b/packages/client/lib/commands/RENAME.ts index 16e883d0532..245851ca31a 100644 --- a/packages/client/lib/commands/RENAME.ts +++ b/packages/client/lib/commands/RENAME.ts @@ -1,10 +1,11 @@ +import { CommandParser } from '../client/parser'; import { RedisArgument, SimpleStringReply, Command } from '../RESP/types'; export default { - FIRST_KEY_INDEX: 1, IS_READ_ONLY: true, - transformArguments(key: RedisArgument, newKey: RedisArgument) { - return ['RENAME', key, newKey]; + parseCommand(parser: CommandParser, key: RedisArgument, newKey: RedisArgument) { + parser.push('RENAME'); + parser.pushKeys([key, newKey]); }, transformReply: undefined as unknown as () => SimpleStringReply } as const satisfies Command; diff --git a/packages/client/lib/commands/RENAMENX.spec.ts b/packages/client/lib/commands/RENAMENX.spec.ts index 5f83933e957..2367b453322 100644 --- a/packages/client/lib/commands/RENAMENX.spec.ts +++ b/packages/client/lib/commands/RENAMENX.spec.ts @@ -1,11 +1,12 @@ import { strict as assert } from 'node:assert'; import testUtils, { GLOBAL } from '../test-utils'; import RENAMENX from './RENAMENX'; +import { parseArgs } from './generic-transformers'; describe('RENAMENX', () => { it('transformArguments', () => { assert.deepEqual( - RENAMENX.transformArguments('source', 'destination'), + parseArgs(RENAMENX, 'source', 'destination'), ['RENAMENX', 'source', 'destination'] ); }); diff --git a/packages/client/lib/commands/RENAMENX.ts b/packages/client/lib/commands/RENAMENX.ts index 3a4f155d5a7..0e8d4f73cf3 100644 --- a/packages/client/lib/commands/RENAMENX.ts +++ b/packages/client/lib/commands/RENAMENX.ts @@ -1,10 +1,11 @@ +import { CommandParser } from '../client/parser'; import { RedisArgument, NumberReply, Command } from '../RESP/types'; export default { - FIRST_KEY_INDEX: 1, IS_READ_ONLY: true, - transformArguments(key: RedisArgument, newKey: RedisArgument) { - return ['RENAMENX', key, newKey]; + parseCommand(parser: CommandParser, key: RedisArgument, newKey: RedisArgument) { + parser.push('RENAMENX'); + parser.pushKeys([key, newKey]); }, transformReply: undefined as unknown as () => NumberReply } as const satisfies Command; diff --git a/packages/client/lib/commands/REPLICAOF.spec.ts b/packages/client/lib/commands/REPLICAOF.spec.ts index 77dbe060450..13668639494 100644 --- a/packages/client/lib/commands/REPLICAOF.spec.ts +++ b/packages/client/lib/commands/REPLICAOF.spec.ts @@ -1,10 +1,11 @@ import { strict as assert } from 'node:assert'; import REPLICAOF from './REPLICAOF'; +import { parseArgs } from './generic-transformers'; describe('REPLICAOF', () => { it('transformArguments', () => { assert.deepEqual( - REPLICAOF.transformArguments('host', 1), + parseArgs(REPLICAOF, 'host', 1), ['REPLICAOF', 'host', '1'] ); }); diff --git a/packages/client/lib/commands/REPLICAOF.ts b/packages/client/lib/commands/REPLICAOF.ts index 4e2f69f7265..c4b09bc4fb8 100644 --- a/packages/client/lib/commands/REPLICAOF.ts +++ b/packages/client/lib/commands/REPLICAOF.ts @@ -1,10 +1,11 @@ +import { CommandParser } from '../client/parser'; import { SimpleStringReply, Command } from '../RESP/types'; export default { - FIRST_KEY_INDEX: undefined, + NOT_KEYED_COMMAND: true, IS_READ_ONLY: true, - transformArguments(host: string, port: number) { - return ['REPLICAOF', host, port.toString()]; + parseCommand(parser: CommandParser, host: string, port: number) { + parser.push('REPLICAOF', host, port.toString()); }, transformReply: undefined as unknown as () => SimpleStringReply } as const satisfies Command; diff --git a/packages/client/lib/commands/RESTORE-ASKING.spec.ts b/packages/client/lib/commands/RESTORE-ASKING.spec.ts index 855196b9776..1258cf68e2d 100644 --- a/packages/client/lib/commands/RESTORE-ASKING.spec.ts +++ b/packages/client/lib/commands/RESTORE-ASKING.spec.ts @@ -1,10 +1,11 @@ import { strict as assert } from 'node:assert'; import RESTORE_ASKING from './RESTORE-ASKING'; +import { parseArgs } from './generic-transformers'; describe('RESTORE-ASKING', () => { it('transformArguments', () => { assert.deepEqual( - RESTORE_ASKING.transformArguments(), + parseArgs(RESTORE_ASKING), ['RESTORE-ASKING'] ); }); diff --git a/packages/client/lib/commands/RESTORE-ASKING.ts b/packages/client/lib/commands/RESTORE-ASKING.ts index 14f6dcbeab3..e8de532b6a4 100644 --- a/packages/client/lib/commands/RESTORE-ASKING.ts +++ b/packages/client/lib/commands/RESTORE-ASKING.ts @@ -1,10 +1,11 @@ +import { CommandParser } from '../client/parser'; import { SimpleStringReply, Command } from '../RESP/types'; export default { - FIRST_KEY_INDEX: undefined, + NOT_KEYED_COMMAND: true, IS_READ_ONLY: true, - transformArguments() { - return ['RESTORE-ASKING']; + parseCommand(parser: CommandParser) { + parser.push('RESTORE-ASKING'); }, transformReply: undefined as unknown as () => SimpleStringReply } as const satisfies Command; diff --git a/packages/client/lib/commands/RESTORE.spec.ts b/packages/client/lib/commands/RESTORE.spec.ts index 6b814e7325a..6083b2eb1a5 100644 --- a/packages/client/lib/commands/RESTORE.spec.ts +++ b/packages/client/lib/commands/RESTORE.spec.ts @@ -2,19 +2,20 @@ import { strict as assert } from 'assert'; import testUtils, { GLOBAL } from '../test-utils'; import RESTORE from './RESTORE'; import { RESP_TYPES } from '../RESP/decoder'; +import { parseArgs } from './generic-transformers'; describe('RESTORE', () => { describe('transformArguments', () => { it('simple', () => { assert.deepEqual( - RESTORE.transformArguments('key', 0, 'value'), + parseArgs(RESTORE, 'key', 0, 'value'), ['RESTORE', 'key', '0', 'value'] ); }); it('with REPLACE', () => { assert.deepEqual( - RESTORE.transformArguments('key', 0, 'value', { + parseArgs(RESTORE, 'key', 0, 'value', { REPLACE: true }), ['RESTORE', 'key', '0', 'value', 'REPLACE'] @@ -23,7 +24,7 @@ describe('RESTORE', () => { it('with ABSTTL', () => { assert.deepEqual( - RESTORE.transformArguments('key', 0, 'value', { + parseArgs(RESTORE, 'key', 0, 'value', { ABSTTL: true }), ['RESTORE', 'key', '0', 'value', 'ABSTTL'] @@ -32,7 +33,7 @@ describe('RESTORE', () => { it('with IDLETIME', () => { assert.deepEqual( - RESTORE.transformArguments('key', 0, 'value', { + parseArgs(RESTORE, 'key', 0, 'value', { IDLETIME: 1 }), ['RESTORE', 'key', '0', 'value', 'IDLETIME', '1'] @@ -41,7 +42,7 @@ describe('RESTORE', () => { it('with FREQ', () => { assert.deepEqual( - RESTORE.transformArguments('key', 0, 'value', { + parseArgs(RESTORE, 'key', 0, 'value', { FREQ: 1 }), ['RESTORE', 'key', '0', 'value', 'FREQ', '1'] @@ -50,7 +51,7 @@ describe('RESTORE', () => { it('with REPLACE, ABSTTL, IDLETIME and FREQ', () => { assert.deepEqual( - RESTORE.transformArguments('key', 0, 'value', { + parseArgs(RESTORE, 'key', 0, 'value', { REPLACE: true, ABSTTL: true, IDLETIME: 1, diff --git a/packages/client/lib/commands/RESTORE.ts b/packages/client/lib/commands/RESTORE.ts index b24c5b569f9..49016c525bd 100644 --- a/packages/client/lib/commands/RESTORE.ts +++ b/packages/client/lib/commands/RESTORE.ts @@ -1,3 +1,4 @@ +import { CommandParser } from '../client/parser'; import { RedisArgument, SimpleStringReply, Command } from '../RESP/types'; export interface RestoreOptions { @@ -8,33 +9,33 @@ export interface RestoreOptions { } export default { - FIRST_KEY_INDEX: 1, IS_READ_ONLY: false, - transformArguments( + parseCommand( + parser: CommandParser, key: RedisArgument, ttl: number, serializedValue: RedisArgument, options?: RestoreOptions ) { - const args = ['RESTORE', key, ttl.toString(), serializedValue]; + parser.push('RESTORE'); + parser.pushKey(key); + parser.push(ttl.toString(), serializedValue); if (options?.REPLACE) { - args.push('REPLACE'); + parser.push('REPLACE'); } if (options?.ABSTTL) { - args.push('ABSTTL'); + parser.push('ABSTTL'); } if (options?.IDLETIME) { - args.push('IDLETIME', options.IDLETIME.toString()); + parser.push('IDLETIME', options.IDLETIME.toString()); } if (options?.FREQ) { - args.push('FREQ', options.FREQ.toString()); + parser.push('FREQ', options.FREQ.toString()); } - - return args; }, transformReply: undefined as unknown as () => SimpleStringReply<'OK'> } as const satisfies Command; diff --git a/packages/client/lib/commands/ROLE.spec.ts b/packages/client/lib/commands/ROLE.spec.ts index c57ba5ba1f0..09ce6ed3427 100644 --- a/packages/client/lib/commands/ROLE.spec.ts +++ b/packages/client/lib/commands/ROLE.spec.ts @@ -1,11 +1,12 @@ import { strict as assert } from 'node:assert'; import testUtils, { GLOBAL } from '../test-utils'; import ROLE from './ROLE'; +import { parseArgs } from './generic-transformers'; describe('ROLE', () => { it('transformArguments', () => { assert.deepEqual( - ROLE.transformArguments(), + parseArgs(ROLE), ['ROLE'] ); }); diff --git a/packages/client/lib/commands/ROLE.ts b/packages/client/lib/commands/ROLE.ts index 7828e53fb61..f45bbad5c01 100644 --- a/packages/client/lib/commands/ROLE.ts +++ b/packages/client/lib/commands/ROLE.ts @@ -1,3 +1,4 @@ +import { CommandParser } from '../client/parser'; import { BlobStringReply, NumberReply, ArrayReply, TuplesReply, UnwrapReply, Command } from '../RESP/types'; type MasterRole = [ @@ -22,10 +23,10 @@ type SentinelRole = [ type Role = TuplesReply; export default { - FIRST_KEY_INDEX: undefined, + NOT_KEYED_COMMAND: true, IS_READ_ONLY: true, - transformArguments() { - return ['ROLE']; + parseCommand(parser: CommandParser) { + parser.push('ROLE'); }, transformReply(reply: UnwrapReply) { switch (reply[0] as unknown as UnwrapReply) { diff --git a/packages/client/lib/commands/RPOP.spec.ts b/packages/client/lib/commands/RPOP.spec.ts index 8ac5cb290f4..844965eae1a 100644 --- a/packages/client/lib/commands/RPOP.spec.ts +++ b/packages/client/lib/commands/RPOP.spec.ts @@ -1,11 +1,12 @@ import { strict as assert } from 'node:assert'; import testUtils, { GLOBAL } from '../test-utils'; import RPOP from './RPOP'; +import { parseArgs } from './generic-transformers'; describe('RPOP', () => { it('transformArguments', () => { assert.deepEqual( - RPOP.transformArguments('key'), + parseArgs(RPOP, 'key'), ['RPOP', 'key'] ); }); diff --git a/packages/client/lib/commands/RPOP.ts b/packages/client/lib/commands/RPOP.ts index f7d0b33d3af..4cc105c3704 100644 --- a/packages/client/lib/commands/RPOP.ts +++ b/packages/client/lib/commands/RPOP.ts @@ -1,9 +1,10 @@ +import { CommandParser } from '../client/parser'; import { RedisArgument, BlobStringReply, NullReply, Command } from '../RESP/types'; export default { - FIRST_KEY_INDEX: 1, - transformArguments(key: RedisArgument) { - return ['RPOP', key]; + parseCommand(parser: CommandParser, key: RedisArgument) { + parser.push('RPOP'); + parser.pushKey(key); }, transformReply: undefined as unknown as () => BlobStringReply | NullReply } as const satisfies Command; diff --git a/packages/client/lib/commands/RPOPLPUSH.spec.ts b/packages/client/lib/commands/RPOPLPUSH.spec.ts index 59458fc0aa8..728d600bc9d 100644 --- a/packages/client/lib/commands/RPOPLPUSH.spec.ts +++ b/packages/client/lib/commands/RPOPLPUSH.spec.ts @@ -1,11 +1,12 @@ import { strict as assert } from 'node:assert'; import testUtils, { GLOBAL } from '../test-utils'; import RPOPLPUSH from './RPOPLPUSH'; +import { parseArgs } from './generic-transformers'; describe('RPOPLPUSH', () => { it('transformArguments', () => { assert.deepEqual( - RPOPLPUSH.transformArguments('source', 'destination'), + parseArgs(RPOPLPUSH, 'source', 'destination'), ['RPOPLPUSH', 'source', 'destination'] ); }); diff --git a/packages/client/lib/commands/RPOPLPUSH.ts b/packages/client/lib/commands/RPOPLPUSH.ts index 1a5e1cc59bc..dcac0472235 100644 --- a/packages/client/lib/commands/RPOPLPUSH.ts +++ b/packages/client/lib/commands/RPOPLPUSH.ts @@ -1,12 +1,10 @@ +import { CommandParser } from '../client/parser'; import { RedisArgument, BlobStringReply, NullReply, Command } from '../RESP/types'; export default { - FIRST_KEY_INDEX: 1, - transformArguments( - source: RedisArgument, - destination: RedisArgument - ) { - return ['RPOPLPUSH', source, destination]; + parseCommand(parser: CommandParser, source: RedisArgument, destination: RedisArgument) { + parser.push('RPOPLPUSH'); + parser.pushKeys([source, destination]); }, transformReply: undefined as unknown as () => BlobStringReply | NullReply } as const satisfies Command; diff --git a/packages/client/lib/commands/RPOP_COUNT.spec.ts b/packages/client/lib/commands/RPOP_COUNT.spec.ts index 14f1854b8bc..e055d8655b5 100644 --- a/packages/client/lib/commands/RPOP_COUNT.spec.ts +++ b/packages/client/lib/commands/RPOP_COUNT.spec.ts @@ -1,13 +1,14 @@ import { strict as assert } from 'node:assert'; import testUtils, { GLOBAL } from '../test-utils'; import RPOP_COUNT from './RPOP_COUNT'; +import { parseArgs } from './generic-transformers'; describe('RPOP COUNT', () => { testUtils.isVersionGreaterThanHook([6, 2]); it('transformArguments', () => { assert.deepEqual( - RPOP_COUNT.transformArguments('key', 1), + parseArgs(RPOP_COUNT, 'key', 1), ['RPOP', 'key', '1'] ); }); diff --git a/packages/client/lib/commands/RPOP_COUNT.ts b/packages/client/lib/commands/RPOP_COUNT.ts index b60dec6ab9d..aff91c6a6f7 100644 --- a/packages/client/lib/commands/RPOP_COUNT.ts +++ b/packages/client/lib/commands/RPOP_COUNT.ts @@ -1,9 +1,11 @@ +import { CommandParser } from '../client/parser'; import { RedisArgument, ArrayReply, BlobStringReply, NullReply, Command } from '../RESP/types'; export default { - FIRST_KEY_INDEX: 1, - transformArguments(key: RedisArgument, count: number) { - return ['RPOP', key, count.toString()]; + parseCommand(parser: CommandParser, key: RedisArgument, count: number) { + parser.push('RPOP'); + parser.pushKey(key); + parser.push(count.toString()); }, transformReply: undefined as unknown as () => ArrayReply | NullReply } as const satisfies Command; diff --git a/packages/client/lib/commands/RPUSH.spec.ts b/packages/client/lib/commands/RPUSH.spec.ts index 06078d75951..559fb7a2746 100644 --- a/packages/client/lib/commands/RPUSH.spec.ts +++ b/packages/client/lib/commands/RPUSH.spec.ts @@ -1,19 +1,20 @@ import { strict as assert } from 'node:assert'; import testUtils, { GLOBAL } from '../test-utils'; import RPUSH from './RPUSH'; +import { parseArgs } from './generic-transformers'; describe('RPUSH', () => { describe('transformArguments', () => { it('string', () => { assert.deepEqual( - RPUSH.transformArguments('key', 'element'), + parseArgs(RPUSH, 'key', 'element'), ['RPUSH', 'key', 'element'] ); }); it('array', () => { assert.deepEqual( - RPUSH.transformArguments('key', ['1', '2']), + parseArgs(RPUSH, 'key', ['1', '2']), ['RPUSH', 'key', '1', '2'] ); }); diff --git a/packages/client/lib/commands/RPUSH.ts b/packages/client/lib/commands/RPUSH.ts index 4b048777389..b820aae6906 100644 --- a/packages/client/lib/commands/RPUSH.ts +++ b/packages/client/lib/commands/RPUSH.ts @@ -1,13 +1,12 @@ +import { CommandParser } from '../client/parser'; import { RedisArgument, NumberReply, Command } from '../RESP/types'; -import { RedisVariadicArgument, pushVariadicArguments } from './generic-transformers'; +import { RedisVariadicArgument } from './generic-transformers'; export default { - FIRST_KEY_INDEX: 1, - transformArguments( - key: RedisArgument, - element: RedisVariadicArgument - ) { - return pushVariadicArguments(['RPUSH', key], element); + parseCommand(parser: CommandParser, key: RedisArgument, element: RedisVariadicArgument) { + parser.push('RPUSH'); + parser.pushKey(key); + parser.pushVariadic(element); }, transformReply: undefined as unknown as () => NumberReply } as const satisfies Command; diff --git a/packages/client/lib/commands/RPUSHX.spec.ts b/packages/client/lib/commands/RPUSHX.spec.ts index 5adaa8b263a..b9fb660c5bc 100644 --- a/packages/client/lib/commands/RPUSHX.spec.ts +++ b/packages/client/lib/commands/RPUSHX.spec.ts @@ -1,19 +1,20 @@ import { strict as assert } from 'node:assert'; import testUtils, { GLOBAL } from '../test-utils'; import RPUSHX from './RPUSHX'; +import { parseArgs } from './generic-transformers'; describe('RPUSHX', () => { describe('transformArguments', () => { it('string', () => { assert.deepEqual( - RPUSHX.transformArguments('key', 'element'), + parseArgs(RPUSHX, 'key', 'element'), ['RPUSHX', 'key', 'element'] ); }); it('array', () => { assert.deepEqual( - RPUSHX.transformArguments('key', ['1', '2']), + parseArgs(RPUSHX, 'key', ['1', '2']), ['RPUSHX', 'key', '1', '2'] ); }); diff --git a/packages/client/lib/commands/RPUSHX.ts b/packages/client/lib/commands/RPUSHX.ts index 00b29624b0d..243f717bb78 100644 --- a/packages/client/lib/commands/RPUSHX.ts +++ b/packages/client/lib/commands/RPUSHX.ts @@ -1,13 +1,12 @@ +import { CommandParser } from '../client/parser'; import { RedisArgument, NumberReply, Command } from '../RESP/types'; -import { RedisVariadicArgument, pushVariadicArguments } from './generic-transformers'; +import { RedisVariadicArgument } from './generic-transformers'; export default { - FIRST_KEY_INDEX: 1, - transformArguments( - key: RedisArgument, - element: RedisVariadicArgument - ) { - return pushVariadicArguments(['RPUSHX', key], element); + parseCommand(parser: CommandParser, key: RedisArgument, element: RedisVariadicArgument) { + parser.push('RPUSHX'); + parser.pushKey(key); + parser.pushVariadic(element); }, transformReply: undefined as unknown as () => NumberReply } as const satisfies Command; diff --git a/packages/client/lib/commands/SADD.spec.ts b/packages/client/lib/commands/SADD.spec.ts index 77adc1c18ce..179e8602efc 100644 --- a/packages/client/lib/commands/SADD.spec.ts +++ b/packages/client/lib/commands/SADD.spec.ts @@ -1,19 +1,20 @@ import { strict as assert } from 'node:assert'; import testUtils, { GLOBAL } from '../test-utils'; import SADD from './SADD'; +import { parseArgs } from './generic-transformers'; describe('SADD', () => { describe('transformArguments', () => { it('string', () => { assert.deepEqual( - SADD.transformArguments('key', 'member'), + parseArgs(SADD, 'key', 'member'), ['SADD', 'key', 'member'] ); }); it('array', () => { assert.deepEqual( - SADD.transformArguments('key', ['1', '2']), + parseArgs(SADD, 'key', ['1', '2']), ['SADD', 'key', '1', '2'] ); }); diff --git a/packages/client/lib/commands/SADD.ts b/packages/client/lib/commands/SADD.ts index 2ff5e9263c3..1fb0171d8d4 100644 --- a/packages/client/lib/commands/SADD.ts +++ b/packages/client/lib/commands/SADD.ts @@ -1,10 +1,12 @@ +import { CommandParser } from '../client/parser'; import { RedisArgument, NumberReply, Command } from '../RESP/types'; -import { RedisVariadicArgument, pushVariadicArguments } from './generic-transformers'; +import { RedisVariadicArgument } from './generic-transformers'; export default { - FIRST_KEY_INDEX: 1, - transformArguments(key: RedisArgument, members: RedisVariadicArgument) { - return pushVariadicArguments(['SADD', key], members); + parseCommand(parser: CommandParser, key: RedisArgument, members: RedisVariadicArgument) { + parser.push('SADD'); + parser.pushKey(key); + parser.pushVariadic(members); }, transformReply: undefined as unknown as () => NumberReply } as const satisfies Command; diff --git a/packages/client/lib/commands/SAVE.spec.ts b/packages/client/lib/commands/SAVE.spec.ts index 5c014da7edb..5f0074f7492 100644 --- a/packages/client/lib/commands/SAVE.spec.ts +++ b/packages/client/lib/commands/SAVE.spec.ts @@ -1,10 +1,11 @@ import { strict as assert } from 'node:assert'; import SAVE from './SAVE'; +import { parseArgs } from './generic-transformers'; describe('SAVE', () => { it('transformArguments', () => { assert.deepEqual( - SAVE.transformArguments(), + parseArgs(SAVE), ['SAVE'] ); }); diff --git a/packages/client/lib/commands/SAVE.ts b/packages/client/lib/commands/SAVE.ts index ee6cccd35a0..ee78884083c 100644 --- a/packages/client/lib/commands/SAVE.ts +++ b/packages/client/lib/commands/SAVE.ts @@ -1,10 +1,11 @@ +import { CommandParser } from '../client/parser'; import { SimpleStringReply, Command } from '../RESP/types'; export default { - FIRST_KEY_INDEX: undefined, + NOT_KEYED_COMMAND: true, IS_READ_ONLY: true, - transformArguments() { - return ['SAVE']; + parseCommand(parser: CommandParser) { + parser.push('SAVE'); }, transformReply: undefined as unknown as () => SimpleStringReply } as const satisfies Command; diff --git a/packages/client/lib/commands/SCAN.spec.ts b/packages/client/lib/commands/SCAN.spec.ts index f4dd865d113..2a32cbebf4f 100644 --- a/packages/client/lib/commands/SCAN.spec.ts +++ b/packages/client/lib/commands/SCAN.spec.ts @@ -1,19 +1,20 @@ import { strict as assert } from 'node:assert'; import testUtils, { GLOBAL } from '../test-utils'; +import { parseArgs } from './generic-transformers'; import SCAN from './SCAN'; describe('SCAN', () => { describe('transformArguments', () => { it('cusror only', () => { assert.deepEqual( - SCAN.transformArguments('0'), + parseArgs(SCAN, '0'), ['SCAN', '0'] ); }); it('with MATCH', () => { assert.deepEqual( - SCAN.transformArguments('0', { + parseArgs(SCAN, '0', { MATCH: 'pattern' }), ['SCAN', '0', 'MATCH', 'pattern'] @@ -22,7 +23,7 @@ describe('SCAN', () => { it('with COUNT', () => { assert.deepEqual( - SCAN.transformArguments('0', { + parseArgs(SCAN, '0', { COUNT: 1 }), ['SCAN', '0', 'COUNT', '1'] @@ -31,7 +32,7 @@ describe('SCAN', () => { it('with TYPE', () => { assert.deepEqual( - SCAN.transformArguments('0', { + parseArgs(SCAN, '0', { TYPE: 'stream' }), ['SCAN', '0', 'TYPE', 'stream'] @@ -40,7 +41,7 @@ describe('SCAN', () => { it('with MATCH & COUNT & TYPE', () => { assert.deepEqual( - SCAN.transformArguments('0', { + parseArgs(SCAN, '0', { MATCH: 'pattern', COUNT: 1, TYPE: 'stream' diff --git a/packages/client/lib/commands/SCAN.ts b/packages/client/lib/commands/SCAN.ts index 13f54440443..2d6e4c35258 100644 --- a/packages/client/lib/commands/SCAN.ts +++ b/packages/client/lib/commands/SCAN.ts @@ -1,3 +1,4 @@ +import { CommandParser } from '../client/parser'; import { RedisArgument, CommandArguments, BlobStringReply, ArrayReply, Command } from '../RESP/types'; export interface ScanCommonOptions { @@ -5,6 +6,21 @@ export interface ScanCommonOptions { COUNT?: number; } +export function parseScanArguments( + parser: CommandParser, + cursor: RedisArgument, + options?: ScanOptions +) { + parser.push(cursor); + if (options?.MATCH) { + parser.push('MATCH', options.MATCH); + } + + if (options?.COUNT) { + parser.push('COUNT', options.COUNT.toString()); + } +} + export function pushScanArguments( args: CommandArguments, cursor: RedisArgument, @@ -28,16 +44,15 @@ export interface ScanOptions extends ScanCommonOptions { } export default { - FIRST_KEY_INDEX: undefined, + NOT_KEYED_COMMAND: true, IS_READ_ONLY: true, - transformArguments(cursor: RedisArgument, options?: ScanOptions) { - const args = pushScanArguments(['SCAN'], cursor, options); + parseCommand(parser: CommandParser, cursor: RedisArgument, options?: ScanOptions) { + parser.push('SCAN'); + parseScanArguments(parser, cursor, options); if (options?.TYPE) { - args.push('TYPE', options.TYPE); + parser.push('TYPE', options.TYPE); } - - return args; }, transformReply([cursor, keys]: [BlobStringReply, ArrayReply]) { return { diff --git a/packages/client/lib/commands/SCARD.spec.ts b/packages/client/lib/commands/SCARD.spec.ts index 5029f340d96..53434583832 100644 --- a/packages/client/lib/commands/SCARD.spec.ts +++ b/packages/client/lib/commands/SCARD.spec.ts @@ -1,11 +1,12 @@ import { strict as assert } from 'node:assert'; import testUtils, { GLOBAL } from '../test-utils'; +import { parseArgs } from './generic-transformers'; import SCARD from './SCARD'; describe('SCARD', () => { it('transformArguments', () => { assert.deepEqual( - SCARD.transformArguments('key'), + parseArgs(SCARD, 'key'), ['SCARD', 'key'] ); }); diff --git a/packages/client/lib/commands/SCARD.ts b/packages/client/lib/commands/SCARD.ts index c13d042ba60..61d4792d996 100644 --- a/packages/client/lib/commands/SCARD.ts +++ b/packages/client/lib/commands/SCARD.ts @@ -1,10 +1,12 @@ +import { CommandParser } from '../client/parser'; import { RedisArgument, NumberReply, Command } from '../RESP/types'; export default { - FIRST_KEY_INDEX: 1, + CACHEABLE: true, IS_READ_ONLY: true, - transformArguments(key: RedisArgument) { - return ['SCARD', key]; + parseCommand(parser: CommandParser, key: RedisArgument) { + parser.push('SCARD'); + parser.pushKey(key); }, transformReply: undefined as unknown as () => NumberReply } as const satisfies Command; diff --git a/packages/client/lib/commands/SCRIPT_DEBUG.spec.ts b/packages/client/lib/commands/SCRIPT_DEBUG.spec.ts index 4e07f2c250c..c98143a3415 100644 --- a/packages/client/lib/commands/SCRIPT_DEBUG.spec.ts +++ b/packages/client/lib/commands/SCRIPT_DEBUG.spec.ts @@ -1,11 +1,12 @@ import { strict as assert } from 'node:assert'; import testUtils, { GLOBAL } from '../test-utils'; import SCRIPT_DEBUG from './SCRIPT_DEBUG'; +import { parseArgs } from './generic-transformers'; describe('SCRIPT DEBUG', () => { it('transformArguments', () => { assert.deepEqual( - SCRIPT_DEBUG.transformArguments('NO'), + parseArgs(SCRIPT_DEBUG, 'NO'), ['SCRIPT', 'DEBUG', 'NO'] ); }); diff --git a/packages/client/lib/commands/SCRIPT_DEBUG.ts b/packages/client/lib/commands/SCRIPT_DEBUG.ts index 3c49ff709d5..b0d3079068f 100644 --- a/packages/client/lib/commands/SCRIPT_DEBUG.ts +++ b/packages/client/lib/commands/SCRIPT_DEBUG.ts @@ -1,10 +1,11 @@ +import { CommandParser } from '../client/parser'; import { SimpleStringReply, Command } from '../RESP/types'; export default { - FIRST_KEY_INDEX: undefined, + NOT_KEYED_COMMAND: true, IS_READ_ONLY: true, - transformArguments(mode: 'YES' | 'SYNC' | 'NO') { - return ['SCRIPT', 'DEBUG', mode]; + parseCommand(parser: CommandParser, mode: 'YES' | 'SYNC' | 'NO') { + parser.push('SCRIPT', 'DEBUG', mode); }, transformReply: undefined as unknown as () => SimpleStringReply<'OK'> } as const satisfies Command; diff --git a/packages/client/lib/commands/SCRIPT_EXISTS.spec.ts b/packages/client/lib/commands/SCRIPT_EXISTS.spec.ts index 8afdbb5f581..cf65156c72d 100644 --- a/packages/client/lib/commands/SCRIPT_EXISTS.spec.ts +++ b/packages/client/lib/commands/SCRIPT_EXISTS.spec.ts @@ -1,19 +1,20 @@ import { strict as assert } from 'node:assert'; import testUtils, { GLOBAL } from '../test-utils'; import SCRIPT_EXISTS from './SCRIPT_EXISTS'; +import { parseArgs } from './generic-transformers'; describe('SCRIPT EXISTS', () => { describe('transformArguments', () => { it('string', () => { assert.deepEqual( - SCRIPT_EXISTS.transformArguments('sha1'), + parseArgs(SCRIPT_EXISTS, 'sha1'), ['SCRIPT', 'EXISTS', 'sha1'] ); }); it('array', () => { assert.deepEqual( - SCRIPT_EXISTS.transformArguments(['1', '2']), + parseArgs(SCRIPT_EXISTS, ['1', '2']), ['SCRIPT', 'EXISTS', '1', '2'] ); }); diff --git a/packages/client/lib/commands/SCRIPT_EXISTS.ts b/packages/client/lib/commands/SCRIPT_EXISTS.ts index ab0a293d8de..b0f6cbe2275 100644 --- a/packages/client/lib/commands/SCRIPT_EXISTS.ts +++ b/packages/client/lib/commands/SCRIPT_EXISTS.ts @@ -1,11 +1,13 @@ +import { CommandParser } from '../client/parser'; import { ArrayReply, NumberReply, Command } from '../RESP/types'; -import { RedisVariadicArgument, pushVariadicArguments } from './generic-transformers'; +import { RedisVariadicArgument } from './generic-transformers'; export default { - FIRST_KEY_INDEX: undefined, + NOT_KEYED_COMMAND: true, IS_READ_ONLY: true, - transformArguments(sha1: RedisVariadicArgument) { - return pushVariadicArguments(['SCRIPT', 'EXISTS'], sha1); + parseCommand(parser: CommandParser, sha1: RedisVariadicArgument) { + parser.push('SCRIPT', 'EXISTS'); + parser.pushVariadic(sha1); }, transformReply: undefined as unknown as () => ArrayReply } as const satisfies Command; diff --git a/packages/client/lib/commands/SCRIPT_FLUSH.spec.ts b/packages/client/lib/commands/SCRIPT_FLUSH.spec.ts index ccc14ecc285..c51efd1a36c 100644 --- a/packages/client/lib/commands/SCRIPT_FLUSH.spec.ts +++ b/packages/client/lib/commands/SCRIPT_FLUSH.spec.ts @@ -1,19 +1,20 @@ import { strict as assert } from 'node:assert'; import testUtils, { GLOBAL } from '../test-utils'; import SCRIPT_FLUSH from './SCRIPT_FLUSH'; +import { parseArgs } from './generic-transformers'; describe('SCRIPT FLUSH', () => { describe('transformArguments', () => { it('simple', () => { assert.deepEqual( - SCRIPT_FLUSH.transformArguments(), + parseArgs(SCRIPT_FLUSH), ['SCRIPT', 'FLUSH'] ); }); it('with mode', () => { assert.deepEqual( - SCRIPT_FLUSH.transformArguments('SYNC'), + parseArgs(SCRIPT_FLUSH, 'SYNC'), ['SCRIPT', 'FLUSH', 'SYNC'] ); }); diff --git a/packages/client/lib/commands/SCRIPT_FLUSH.ts b/packages/client/lib/commands/SCRIPT_FLUSH.ts index f5426395628..1e05a619bad 100644 --- a/packages/client/lib/commands/SCRIPT_FLUSH.ts +++ b/packages/client/lib/commands/SCRIPT_FLUSH.ts @@ -1,16 +1,15 @@ +import { CommandParser } from '../client/parser'; import { SimpleStringReply, Command } from '../RESP/types'; export default { - FIRST_KEY_INDEX: undefined, + NOT_KEYED_COMMAND: true, IS_READ_ONLY: true, - transformArguments(mode?: 'ASYNC' | 'SYNC') { - const args = ['SCRIPT', 'FLUSH']; + parseCommand(parser: CommandParser, mode?: 'ASYNC' | 'SYNC') { + parser.push('SCRIPT', 'FLUSH'); if (mode) { - args.push(mode); + parser.push(mode); } - - return args; }, transformReply: undefined as unknown as () => SimpleStringReply<'OK'> } as const satisfies Command; diff --git a/packages/client/lib/commands/SCRIPT_KILL.spec.ts b/packages/client/lib/commands/SCRIPT_KILL.spec.ts index 1499c97ac07..7186efd54cf 100644 --- a/packages/client/lib/commands/SCRIPT_KILL.spec.ts +++ b/packages/client/lib/commands/SCRIPT_KILL.spec.ts @@ -1,10 +1,11 @@ import { strict as assert } from 'node:assert'; import SCRIPT_KILL from './SCRIPT_KILL'; +import { parseArgs } from './generic-transformers'; describe('SCRIPT KILL', () => { it('transformArguments', () => { assert.deepEqual( - SCRIPT_KILL.transformArguments(), + parseArgs(SCRIPT_KILL), ['SCRIPT', 'KILL'] ); }); diff --git a/packages/client/lib/commands/SCRIPT_KILL.ts b/packages/client/lib/commands/SCRIPT_KILL.ts index ac025b788bb..26953506235 100644 --- a/packages/client/lib/commands/SCRIPT_KILL.ts +++ b/packages/client/lib/commands/SCRIPT_KILL.ts @@ -1,10 +1,11 @@ +import { CommandParser } from '../client/parser'; import { SimpleStringReply, Command } from '../RESP/types'; export default { - FIRST_KEY_INDEX: undefined, + NOT_KEYED_COMMAND: true, IS_READ_ONLY: true, - transformArguments() { - return ['SCRIPT', 'KILL']; + parseCommand(parser: CommandParser) { + parser.push('SCRIPT', 'KILL'); }, transformReply: undefined as unknown as () => SimpleStringReply<'OK'> } as const satisfies Command; diff --git a/packages/client/lib/commands/SCRIPT_LOAD.spec.ts b/packages/client/lib/commands/SCRIPT_LOAD.spec.ts index d964859d2ff..b0df9887e11 100644 --- a/packages/client/lib/commands/SCRIPT_LOAD.spec.ts +++ b/packages/client/lib/commands/SCRIPT_LOAD.spec.ts @@ -2,6 +2,7 @@ import { strict as assert } from 'node:assert'; import testUtils, { GLOBAL } from '../test-utils'; import SCRIPT_LOAD from './SCRIPT_LOAD'; import { scriptSha1 } from '../lua-script'; +import { parseArgs } from './generic-transformers'; describe('SCRIPT LOAD', () => { const SCRIPT = 'return 1;', @@ -9,7 +10,7 @@ describe('SCRIPT LOAD', () => { it('transformArguments', () => { assert.deepEqual( - SCRIPT_LOAD.transformArguments(SCRIPT), + parseArgs(SCRIPT_LOAD, SCRIPT), ['SCRIPT', 'LOAD', SCRIPT] ); }); diff --git a/packages/client/lib/commands/SCRIPT_LOAD.ts b/packages/client/lib/commands/SCRIPT_LOAD.ts index 90028b13a5f..58f7c00dfcd 100644 --- a/packages/client/lib/commands/SCRIPT_LOAD.ts +++ b/packages/client/lib/commands/SCRIPT_LOAD.ts @@ -1,10 +1,11 @@ +import { CommandParser } from '../client/parser'; import { BlobStringReply, Command, RedisArgument } from '../RESP/types'; export default { - FIRST_KEY_INDEX: undefined, + NOT_KEYED_COMMAND: true, IS_READ_ONLY: true, - transformArguments(script: RedisArgument) { - return ['SCRIPT', 'LOAD', script]; + parseCommand(parser: CommandParser, script: RedisArgument) { + parser.push('SCRIPT', 'LOAD', script); }, transformReply: undefined as unknown as () => BlobStringReply } as const satisfies Command; diff --git a/packages/client/lib/commands/SDIFF.spec.ts b/packages/client/lib/commands/SDIFF.spec.ts index 83ac6dc1da1..a943a80688d 100644 --- a/packages/client/lib/commands/SDIFF.spec.ts +++ b/packages/client/lib/commands/SDIFF.spec.ts @@ -1,19 +1,20 @@ import { strict as assert } from 'node:assert'; import testUtils, { GLOBAL } from '../test-utils'; import SDIFF from './SDIFF'; +import { parseArgs } from './generic-transformers'; describe('SDIFF', () => { - describe('transformArguments', () => { + describe('processCommand', () => { it('string', () => { assert.deepEqual( - SDIFF.transformArguments('key'), + parseArgs(SDIFF, 'key'), ['SDIFF', 'key'] ); }); it('array', () => { assert.deepEqual( - SDIFF.transformArguments(['1', '2']), + parseArgs(SDIFF, ['1', '2']), ['SDIFF', '1', '2'] ); }); diff --git a/packages/client/lib/commands/SDIFF.ts b/packages/client/lib/commands/SDIFF.ts index 918cbf7fa15..bd78edc93db 100644 --- a/packages/client/lib/commands/SDIFF.ts +++ b/packages/client/lib/commands/SDIFF.ts @@ -1,11 +1,13 @@ +import { CommandParser } from '../client/parser'; import { ArrayReply, BlobStringReply, Command } from '../RESP/types'; -import { RedisVariadicArgument, pushVariadicArguments } from './generic-transformers'; +import { RedisVariadicArgument } from './generic-transformers'; export default { - FIRST_KEY_INDEX: 1, + CACHEABLE: true, IS_READ_ONLY: true, - transformArguments(keys: RedisVariadicArgument) { - return pushVariadicArguments(['SDIFF'], keys); + parseCommand(parser: CommandParser, keys: RedisVariadicArgument) { + parser.push('SDIFF'); + parser.pushKeys(keys); }, transformReply: undefined as unknown as () => ArrayReply } as const satisfies Command; diff --git a/packages/client/lib/commands/SDIFFSTORE.spec.ts b/packages/client/lib/commands/SDIFFSTORE.spec.ts index 613a9eb590b..43213adfbb0 100644 --- a/packages/client/lib/commands/SDIFFSTORE.spec.ts +++ b/packages/client/lib/commands/SDIFFSTORE.spec.ts @@ -1,19 +1,20 @@ import { strict as assert } from 'node:assert'; import testUtils, { GLOBAL } from '../test-utils'; import SDIFFSTORE from './SDIFFSTORE'; +import { parseArgs } from './generic-transformers'; describe('SDIFFSTORE', () => { describe('transformArguments', () => { it('string', () => { assert.deepEqual( - SDIFFSTORE.transformArguments('destination', 'key'), + parseArgs(SDIFFSTORE, 'destination', 'key'), ['SDIFFSTORE', 'destination', 'key'] ); }); it('array', () => { assert.deepEqual( - SDIFFSTORE.transformArguments('destination', ['1', '2']), + parseArgs(SDIFFSTORE, 'destination', ['1', '2']), ['SDIFFSTORE', 'destination', '1', '2'] ); }); diff --git a/packages/client/lib/commands/SDIFFSTORE.ts b/packages/client/lib/commands/SDIFFSTORE.ts index 15f0ccb499a..6da2795d8ff 100644 --- a/packages/client/lib/commands/SDIFFSTORE.ts +++ b/packages/client/lib/commands/SDIFFSTORE.ts @@ -1,10 +1,12 @@ +import { CommandParser } from '../client/parser'; import { RedisArgument, NumberReply, Command } from '../RESP/types'; -import { RedisVariadicArgument, pushVariadicArguments } from './generic-transformers'; +import { RedisVariadicArgument } from './generic-transformers'; export default { - FIRST_KEY_INDEX: 1, - transformArguments(destination: RedisArgument, keys: RedisVariadicArgument) { - return pushVariadicArguments(['SDIFFSTORE', destination], keys); + parseCommand(parser: CommandParser, destination: RedisArgument, keys: RedisVariadicArgument) { + parser.push('SDIFFSTORE'); + parser.pushKey(destination); + parser.pushKeys(keys); }, transformReply: undefined as unknown as () => NumberReply } as const satisfies Command; diff --git a/packages/client/lib/commands/SET.spec.ts b/packages/client/lib/commands/SET.spec.ts index 4364eb7391a..b8aa57fe77b 100644 --- a/packages/client/lib/commands/SET.spec.ts +++ b/packages/client/lib/commands/SET.spec.ts @@ -1,20 +1,21 @@ import { strict as assert } from 'node:assert'; import testUtils, { GLOBAL } from '../test-utils'; import SET from './SET'; +import { parseArgs } from './generic-transformers'; describe('SET', () => { describe('transformArguments', () => { describe('value', () => { it('string', () => { assert.deepEqual( - SET.transformArguments('key', 'value'), + parseArgs(SET, 'key', 'value'), ['SET', 'key', 'value'] ); }); it('number', () => { assert.deepEqual( - SET.transformArguments('key', 0), + parseArgs(SET, 'key', 0), ['SET', 'key', '0'] ); }); @@ -23,7 +24,7 @@ describe('SET', () => { describe('expiration', () => { it('\'KEEPTTL\'', () => { assert.deepEqual( - SET.transformArguments('key', 'value', { + parseArgs(SET, 'key', 'value', { expiration: 'KEEPTTL' }), ['SET', 'key', 'value', 'KEEPTTL'] @@ -32,7 +33,7 @@ describe('SET', () => { it('{ type: \'KEEPTTL\' }', () => { assert.deepEqual( - SET.transformArguments('key', 'value', { + parseArgs(SET, 'key', 'value', { expiration: { type: 'KEEPTTL' } @@ -43,7 +44,7 @@ describe('SET', () => { it('{ type: \'EX\' }', () => { assert.deepEqual( - SET.transformArguments('key', 'value', { + parseArgs(SET, 'key', 'value', { expiration: { type: 'EX', value: 0 @@ -55,7 +56,7 @@ describe('SET', () => { it('with EX (backwards compatibility)', () => { assert.deepEqual( - SET.transformArguments('key', 'value', { + parseArgs(SET, 'key', 'value', { EX: 0 }), ['SET', 'key', 'value', 'EX', '0'] @@ -64,7 +65,7 @@ describe('SET', () => { it('with PX (backwards compatibility)', () => { assert.deepEqual( - SET.transformArguments('key', 'value', { + parseArgs(SET, 'key', 'value', { PX: 0 }), ['SET', 'key', 'value', 'PX', '0'] @@ -73,7 +74,7 @@ describe('SET', () => { it('with EXAT (backwards compatibility)', () => { assert.deepEqual( - SET.transformArguments('key', 'value', { + parseArgs(SET, 'key', 'value', { EXAT: 0 }), ['SET', 'key', 'value', 'EXAT', '0'] @@ -82,7 +83,7 @@ describe('SET', () => { it('with PXAT (backwards compatibility)', () => { assert.deepEqual( - SET.transformArguments('key', 'value', { + parseArgs(SET, 'key', 'value', { PXAT: 0 }), ['SET', 'key', 'value', 'PXAT', '0'] @@ -91,7 +92,7 @@ describe('SET', () => { it('with KEEPTTL (backwards compatibility)', () => { assert.deepEqual( - SET.transformArguments('key', 'value', { + parseArgs(SET, 'key', 'value', { KEEPTTL: true }), ['SET', 'key', 'value', 'KEEPTTL'] @@ -102,7 +103,7 @@ describe('SET', () => { describe('condition', () => { it('with condition', () => { assert.deepEqual( - SET.transformArguments('key', 'value', { + parseArgs(SET, 'key', 'value', { condition: 'NX' }), ['SET', 'key', 'value', 'NX'] @@ -111,7 +112,7 @@ describe('SET', () => { it('with NX (backwards compatibility)', () => { assert.deepEqual( - SET.transformArguments('key', 'value', { + parseArgs(SET, 'key', 'value', { NX: true }), ['SET', 'key', 'value', 'NX'] @@ -120,7 +121,7 @@ describe('SET', () => { it('with XX (backwards compatibility)', () => { assert.deepEqual( - SET.transformArguments('key', 'value', { + parseArgs(SET, 'key', 'value', { XX: true }), ['SET', 'key', 'value', 'XX'] @@ -130,7 +131,7 @@ describe('SET', () => { it('with GET', () => { assert.deepEqual( - SET.transformArguments('key', 'value', { + parseArgs(SET, 'key', 'value', { GET: true }), ['SET', 'key', 'value', 'GET'] @@ -139,7 +140,7 @@ describe('SET', () => { it('with expiration, condition, GET', () => { assert.deepEqual( - SET.transformArguments('key', 'value', { + parseArgs(SET, 'key', 'value', { expiration: { type: 'EX', value: 0 diff --git a/packages/client/lib/commands/SET.ts b/packages/client/lib/commands/SET.ts index cede62e7055..d2d13c874c4 100644 --- a/packages/client/lib/commands/SET.ts +++ b/packages/client/lib/commands/SET.ts @@ -1,3 +1,4 @@ +import { CommandParser } from '../client/parser'; import { RedisArgument, SimpleStringReply, BlobStringReply, NullReply, Command } from '../RESP/types'; export interface SetOptions { @@ -42,50 +43,45 @@ export interface SetOptions { } export default { - FIRST_KEY_INDEX: 1, - transformArguments(key: RedisArgument, value: RedisArgument | number, options?: SetOptions) { - const args = [ - 'SET', - key, - typeof value === 'number' ? value.toString() : value - ]; + parseCommand(parser: CommandParser, key: RedisArgument, value: RedisArgument | number, options?: SetOptions) { + parser.push('SET'); + parser.pushKey(key); + parser.push(typeof value === 'number' ? value.toString() : value); if (options?.expiration) { if (typeof options.expiration === 'string') { - args.push(options.expiration); + parser.push(options.expiration); } else if (options.expiration.type === 'KEEPTTL') { - args.push('KEEPTTL'); + parser.push('KEEPTTL'); } else { - args.push( + parser.push( options.expiration.type, options.expiration.value.toString() ); } } else if (options?.EX !== undefined) { - args.push('EX', options.EX.toString()); + parser.push('EX', options.EX.toString()); } else if (options?.PX !== undefined) { - args.push('PX', options.PX.toString()); + parser.push('PX', options.PX.toString()); } else if (options?.EXAT !== undefined) { - args.push('EXAT', options.EXAT.toString()); + parser.push('EXAT', options.EXAT.toString()); } else if (options?.PXAT !== undefined) { - args.push('PXAT', options.PXAT.toString()); + parser.push('PXAT', options.PXAT.toString()); } else if (options?.KEEPTTL) { - args.push('KEEPTTL'); + parser.push('KEEPTTL'); } if (options?.condition) { - args.push(options.condition); + parser.push(options.condition); } else if (options?.NX) { - args.push('NX'); + parser.push('NX'); } else if (options?.XX) { - args.push('XX'); + parser.push('XX'); } if (options?.GET) { - args.push('GET'); + parser.push('GET'); } - - return args; }, transformReply: undefined as unknown as () => SimpleStringReply<'OK'> | BlobStringReply | NullReply } as const satisfies Command; diff --git a/packages/client/lib/commands/SETBIT.spec.ts b/packages/client/lib/commands/SETBIT.spec.ts index e4470bb1528..1eedcc69959 100644 --- a/packages/client/lib/commands/SETBIT.spec.ts +++ b/packages/client/lib/commands/SETBIT.spec.ts @@ -1,11 +1,12 @@ import { strict as assert } from 'node:assert'; import testUtils, { GLOBAL } from '../test-utils'; import SETBIT from './SETBIT'; +import { parseArgs } from './generic-transformers'; describe('SETBIT', () => { it('transformArguments', () => { assert.deepEqual( - SETBIT.transformArguments('key', 0, 1), + parseArgs(SETBIT, 'key', 0, 1), ['SETBIT', 'key', '0', '1'] ); }); diff --git a/packages/client/lib/commands/SETBIT.ts b/packages/client/lib/commands/SETBIT.ts index 5b3ec6173dc..5cd29260071 100644 --- a/packages/client/lib/commands/SETBIT.ts +++ b/packages/client/lib/commands/SETBIT.ts @@ -1,15 +1,13 @@ +import { CommandParser } from '../client/parser'; import { RedisArgument, NumberReply, Command } from '../RESP/types'; import { BitValue } from './generic-transformers'; export default { - FIRST_KEY_INDEX: 1, IS_READ_ONLY: false, - transformArguments( - key: RedisArgument, - offset: number, - value: BitValue - ) { - return ['SETBIT', key, offset.toString(), value.toString()]; + parseCommand(parser: CommandParser, key: RedisArgument, offset: number, value: BitValue) { + parser.push('SETBIT'); + parser.pushKey(key); + parser.push(offset.toString(), value.toString()); }, transformReply: undefined as unknown as () => NumberReply } as const satisfies Command; diff --git a/packages/client/lib/commands/SETEX.spec.ts b/packages/client/lib/commands/SETEX.spec.ts index 00f204cc713..7bc934ccd68 100644 --- a/packages/client/lib/commands/SETEX.spec.ts +++ b/packages/client/lib/commands/SETEX.spec.ts @@ -1,11 +1,12 @@ import { strict as assert } from 'node:assert'; import testUtils, { GLOBAL } from '../test-utils'; import SETEX from './SETEX'; +import { parseArgs } from './generic-transformers'; describe('SETEX', () => { it('transformArguments', () => { assert.deepEqual( - SETEX.transformArguments('key', 1, 'value'), + parseArgs(SETEX, 'key', 1, 'value'), ['SETEX', 'key', '1', 'value'] ); }); diff --git a/packages/client/lib/commands/SETEX.ts b/packages/client/lib/commands/SETEX.ts index bbd77e5d990..5e58b589975 100644 --- a/packages/client/lib/commands/SETEX.ts +++ b/packages/client/lib/commands/SETEX.ts @@ -1,18 +1,11 @@ +import { CommandParser } from '../client/parser'; import { RedisArgument, SimpleStringReply, Command } from '../RESP/types'; export default { - FIRST_KEY_INDEX: 1, - transformArguments( - key: RedisArgument, - seconds: number, - value: RedisArgument - ) { - return [ - 'SETEX', - key, - seconds.toString(), - value - ]; + parseCommand(parser: CommandParser, key: RedisArgument, seconds: number, value: RedisArgument) { + parser.push('SETEX'); + parser.pushKey(key); + parser.push(seconds.toString(), value); }, transformReply: undefined as unknown as () => SimpleStringReply<'OK'> } as const satisfies Command; diff --git a/packages/client/lib/commands/SETNX .spec.ts b/packages/client/lib/commands/SETNX .spec.ts index 5cfca29ba62..81a5af3d411 100644 --- a/packages/client/lib/commands/SETNX .spec.ts +++ b/packages/client/lib/commands/SETNX .spec.ts @@ -1,11 +1,12 @@ import { strict as assert } from 'node:assert'; import testUtils, { GLOBAL } from '../test-utils'; import SETNX from './SETNX'; +import { parseArgs } from './generic-transformers'; describe('SETNX', () => { it('transformArguments', () => { assert.deepEqual( - SETNX.transformArguments('key', 'value'), + parseArgs(SETNX, 'key', 'value'), ['SETNX', 'key', 'value'] ); }); diff --git a/packages/client/lib/commands/SETNX.ts b/packages/client/lib/commands/SETNX.ts index 0940efad693..ae60067c28f 100644 --- a/packages/client/lib/commands/SETNX.ts +++ b/packages/client/lib/commands/SETNX.ts @@ -1,9 +1,11 @@ +import { CommandParser } from '../client/parser'; import { RedisArgument, NumberReply, Command } from '../RESP/types'; export default { - FIRST_KEY_INDEX: 1, - transformArguments(key: RedisArgument, value: RedisArgument) { - return ['SETNX', key, value]; + parseCommand(parser: CommandParser, key: RedisArgument, value: RedisArgument) { + parser.push('SETNX'); + parser.pushKey(key); + parser.push(value); }, transformReply: undefined as unknown as () => NumberReply } as const satisfies Command; diff --git a/packages/client/lib/commands/SETRANGE.spec.ts b/packages/client/lib/commands/SETRANGE.spec.ts index 01d3545a359..acdab5bcd3b 100644 --- a/packages/client/lib/commands/SETRANGE.spec.ts +++ b/packages/client/lib/commands/SETRANGE.spec.ts @@ -1,11 +1,12 @@ import { strict as assert } from 'node:assert'; import testUtils, { GLOBAL } from '../test-utils'; import SETRANGE from './SETRANGE'; +import { parseArgs } from './generic-transformers'; describe('SETRANGE', () => { it('transformArguments', () => { assert.deepEqual( - SETRANGE.transformArguments('key', 0, 'value'), + parseArgs(SETRANGE, 'key', 0, 'value'), ['SETRANGE', 'key', '0', 'value'] ); }); diff --git a/packages/client/lib/commands/SETRANGE.ts b/packages/client/lib/commands/SETRANGE.ts index 1951a82c07d..42f4ca01117 100644 --- a/packages/client/lib/commands/SETRANGE.ts +++ b/packages/client/lib/commands/SETRANGE.ts @@ -1,18 +1,11 @@ +import { CommandParser } from '../client/parser'; import { RedisArgument, NumberReply, Command } from '../RESP/types'; export default { - FIRST_KEY_INDEX: 1, - transformArguments( - key: RedisArgument, - offset: number, - value: RedisArgument - ) { - return [ - 'SETRANGE', - key, - offset.toString(), - value - ]; + parseCommand(parser: CommandParser, key: RedisArgument, offset: number, value: RedisArgument) { + parser.push('SETRANGE'); + parser.pushKey(key); + parser.push(offset.toString(), value); }, transformReply: undefined as unknown as () => NumberReply } as const satisfies Command; diff --git a/packages/client/lib/commands/SHUTDOWN.spec.ts b/packages/client/lib/commands/SHUTDOWN.spec.ts index 7dd46a5d534..9c4ca852ad3 100644 --- a/packages/client/lib/commands/SHUTDOWN.spec.ts +++ b/packages/client/lib/commands/SHUTDOWN.spec.ts @@ -1,18 +1,19 @@ import { strict as assert } from 'node:assert'; import SHUTDOWN from './SHUTDOWN'; +import { parseArgs } from './generic-transformers'; describe('SHUTDOWN', () => { describe('transformArguments', () => { it('simple', () => { assert.deepEqual( - SHUTDOWN.transformArguments(), + parseArgs(SHUTDOWN), ['SHUTDOWN'] ); }); it('with mode', () => { assert.deepEqual( - SHUTDOWN.transformArguments({ + parseArgs(SHUTDOWN, { mode: 'NOSAVE' }), ['SHUTDOWN', 'NOSAVE'] @@ -21,7 +22,7 @@ describe('SHUTDOWN', () => { it('with NOW', () => { assert.deepEqual( - SHUTDOWN.transformArguments({ + parseArgs(SHUTDOWN, { NOW: true }), ['SHUTDOWN', 'NOW'] @@ -30,7 +31,7 @@ describe('SHUTDOWN', () => { it('with FORCE', () => { assert.deepEqual( - SHUTDOWN.transformArguments({ + parseArgs(SHUTDOWN, { FORCE: true }), ['SHUTDOWN', 'FORCE'] @@ -39,7 +40,7 @@ describe('SHUTDOWN', () => { it('with ABORT', () => { assert.deepEqual( - SHUTDOWN.transformArguments({ + parseArgs(SHUTDOWN, { ABORT: true }), ['SHUTDOWN', 'ABORT'] diff --git a/packages/client/lib/commands/SHUTDOWN.ts b/packages/client/lib/commands/SHUTDOWN.ts index e0f3d08ce81..33fb3e77301 100644 --- a/packages/client/lib/commands/SHUTDOWN.ts +++ b/packages/client/lib/commands/SHUTDOWN.ts @@ -1,3 +1,4 @@ +import { CommandParser } from '../client/parser'; import { SimpleStringReply, Command } from '../RESP/types'; export interface ShutdownOptions { @@ -8,28 +9,26 @@ export interface ShutdownOptions { } export default { - FIRST_KEY_INDEX: undefined, + NOT_KEYED_COMMAND: true, IS_READ_ONLY: false, - transformArguments(options?: ShutdownOptions) { - const args = ['SHUTDOWN'] + parseCommand(parser: CommandParser, options?: ShutdownOptions) { + parser.push('SHUTDOWN'); if (options?.mode) { - args.push(options.mode); + parser.push(options.mode); } if (options?.NOW) { - args.push('NOW'); + parser.push('NOW'); } if (options?.FORCE) { - args.push('FORCE'); + parser.push('FORCE'); } if (options?.ABORT) { - args.push('ABORT'); + parser.push('ABORT'); } - - return args; }, transformReply: undefined as unknown as () => void | SimpleStringReply } as const satisfies Command; diff --git a/packages/client/lib/commands/SINTER.spec.ts b/packages/client/lib/commands/SINTER.spec.ts index 5b66fdd3f89..6ca7b959ca7 100644 --- a/packages/client/lib/commands/SINTER.spec.ts +++ b/packages/client/lib/commands/SINTER.spec.ts @@ -1,19 +1,20 @@ import { strict as assert } from 'node:assert'; import testUtils, { GLOBAL } from '../test-utils'; import SINTER from './SINTER'; +import { parseArgs } from './generic-transformers'; describe('SINTER', () => { - describe('transformArguments', () => { + describe('processCommand', () => { it('string', () => { assert.deepEqual( - SINTER.transformArguments('key'), + parseArgs(SINTER, 'key'), ['SINTER', 'key'] ); }); it('array', () => { assert.deepEqual( - SINTER.transformArguments(['1', '2']), + parseArgs(SINTER, ['1', '2']), ['SINTER', '1', '2'] ); }); diff --git a/packages/client/lib/commands/SINTER.ts b/packages/client/lib/commands/SINTER.ts index f3f27de2e38..19ecdbb41ca 100644 --- a/packages/client/lib/commands/SINTER.ts +++ b/packages/client/lib/commands/SINTER.ts @@ -1,11 +1,13 @@ +import { CommandParser } from '../client/parser'; import { ArrayReply, BlobStringReply, Command } from '../RESP/types'; -import { RedisVariadicArgument, pushVariadicArguments } from './generic-transformers'; +import { RedisVariadicArgument } from './generic-transformers'; export default { - FIRST_KEY_INDEX: 1, + CACHEABLE: true, IS_READ_ONLY: true, - transformArguments(keys: RedisVariadicArgument) { - return pushVariadicArguments(['SINTER'], keys); + parseCommand(parser: CommandParser, keys: RedisVariadicArgument) { + parser.push('SINTER'); + parser.pushKeys(keys); }, transformReply: undefined as unknown as () => ArrayReply } as const satisfies Command; diff --git a/packages/client/lib/commands/SINTERCARD.spec.ts b/packages/client/lib/commands/SINTERCARD.spec.ts index cddb886088a..51aed13415d 100644 --- a/packages/client/lib/commands/SINTERCARD.spec.ts +++ b/packages/client/lib/commands/SINTERCARD.spec.ts @@ -1,6 +1,7 @@ import { strict as assert } from 'node:assert'; import testUtils, { GLOBAL } from '../test-utils'; import SINTERCARD from './SINTERCARD'; +import { parseArgs } from './generic-transformers'; describe('SINTERCARD', () => { testUtils.isVersionGreaterThanHook([7]); @@ -8,21 +9,21 @@ describe('SINTERCARD', () => { describe('transformArguments', () => { it('simple', () => { assert.deepEqual( - SINTERCARD.transformArguments(['1', '2']), + parseArgs(SINTERCARD, ['1', '2']), ['SINTERCARD', '2', '1', '2'] ); }); it('with limit (backwards compatibility)', () => { assert.deepEqual( - SINTERCARD.transformArguments(['1', '2'], 1), + parseArgs(SINTERCARD, ['1', '2'], 1), ['SINTERCARD', '2', '1', '2', 'LIMIT', '1'] ); }); it('with LIMIT', () => { assert.deepEqual( - SINTERCARD.transformArguments(['1', '2'], { + parseArgs(SINTERCARD, ['1', '2'], { LIMIT: 1 }), ['SINTERCARD', '2', '1', '2', 'LIMIT', '1'] diff --git a/packages/client/lib/commands/SINTERCARD.ts b/packages/client/lib/commands/SINTERCARD.ts index 626bc1048c3..cb9e7d3be3d 100644 --- a/packages/client/lib/commands/SINTERCARD.ts +++ b/packages/client/lib/commands/SINTERCARD.ts @@ -1,26 +1,23 @@ +import { CommandParser } from '../client/parser'; import { NumberReply, Command } from '../RESP/types'; -import { RedisVariadicArgument, pushVariadicArgument } from './generic-transformers'; +import { RedisVariadicArgument } from './generic-transformers'; export interface SInterCardOptions { LIMIT?: number; } export default { - FIRST_KEY_INDEX: 2, IS_READ_ONLY: true, - transformArguments( - keys: RedisVariadicArgument, - options?: SInterCardOptions | number // `number` for backwards compatibility - ) { - const args = pushVariadicArgument(['SINTERCARD'], keys); + // option `number` for backwards compatibility + parseCommand(parser: CommandParser, keys: RedisVariadicArgument, options?: SInterCardOptions | number) { + parser.push('SINTERCARD'); + parser.pushKeysLength(keys); if (typeof options === 'number') { // backwards compatibility - args.push('LIMIT', options.toString()); + parser.push('LIMIT', options.toString()); } else if (options?.LIMIT !== undefined) { - args.push('LIMIT', options.LIMIT.toString()); + parser.push('LIMIT', options.LIMIT.toString()); } - - return args; }, transformReply: undefined as unknown as () => NumberReply } as const satisfies Command; diff --git a/packages/client/lib/commands/SINTERSTORE.spec.ts b/packages/client/lib/commands/SINTERSTORE.spec.ts index 05416742ee9..83302a5c829 100644 --- a/packages/client/lib/commands/SINTERSTORE.spec.ts +++ b/packages/client/lib/commands/SINTERSTORE.spec.ts @@ -1,19 +1,20 @@ import { strict as assert } from 'node:assert'; import testUtils, { GLOBAL } from '../test-utils'; import SINTERSTORE from './SINTERSTORE'; +import { parseArgs } from './generic-transformers'; describe('SINTERSTORE', () => { describe('transformArguments', () => { it('string', () => { assert.deepEqual( - SINTERSTORE.transformArguments('destination', 'key'), + parseArgs(SINTERSTORE, 'destination', 'key'), ['SINTERSTORE', 'destination', 'key'] ); }); it('array', () => { assert.deepEqual( - SINTERSTORE.transformArguments('destination', ['1', '2']), + parseArgs(SINTERSTORE, 'destination', ['1', '2']), ['SINTERSTORE', 'destination', '1', '2'] ); }); diff --git a/packages/client/lib/commands/SINTERSTORE.ts b/packages/client/lib/commands/SINTERSTORE.ts index 744e0b18456..06db0af9cb0 100644 --- a/packages/client/lib/commands/SINTERSTORE.ts +++ b/packages/client/lib/commands/SINTERSTORE.ts @@ -1,14 +1,13 @@ +import { CommandParser } from '../client/parser'; import { RedisArgument, NumberReply, Command } from '../RESP/types'; -import { RedisVariadicArgument, pushVariadicArguments } from './generic-transformers'; +import { RedisVariadicArgument } from './generic-transformers'; export default { - FIRST_KEY_INDEX: 1, IS_READ_ONLY: false, - transformArguments( - destination: RedisArgument, - keys: RedisVariadicArgument - ) { - return pushVariadicArguments(['SINTERSTORE', destination], keys); + parseCommand(parser: CommandParser, destination: RedisArgument, keys: RedisVariadicArgument) { + parser.push('SINTERSTORE'); + parser.pushKey(destination) + parser.pushKeys(keys); }, transformReply: undefined as unknown as () => NumberReply } as const satisfies Command; diff --git a/packages/client/lib/commands/SISMEMBER.spec.ts b/packages/client/lib/commands/SISMEMBER.spec.ts index 0c1b92614cb..4796475c52c 100644 --- a/packages/client/lib/commands/SISMEMBER.spec.ts +++ b/packages/client/lib/commands/SISMEMBER.spec.ts @@ -1,11 +1,12 @@ import { strict as assert } from 'node:assert'; import testUtils, { GLOBAL } from '../test-utils'; import SISMEMBER from './SISMEMBER'; +import { parseArgs } from './generic-transformers'; describe('SISMEMBER', () => { - it('transformArguments', () => { + it('processCommand', () => { assert.deepEqual( - SISMEMBER.transformArguments('key', 'member'), + parseArgs(SISMEMBER, 'key', 'member'), ['SISMEMBER', 'key', 'member'] ); }); diff --git a/packages/client/lib/commands/SISMEMBER.ts b/packages/client/lib/commands/SISMEMBER.ts index 0687d19de30..6192ca2605f 100644 --- a/packages/client/lib/commands/SISMEMBER.ts +++ b/packages/client/lib/commands/SISMEMBER.ts @@ -1,10 +1,13 @@ +import { CommandParser } from '../client/parser'; import { NumberReply, Command, RedisArgument } from '../RESP/types'; export default { - FIRST_KEY_INDEX: 1, + CACHEABLE: true, IS_READ_ONLY: true, - transformArguments(key: RedisArgument, member: RedisArgument) { - return ['SISMEMBER', key, member]; + parseCommand(parser: CommandParser, key: RedisArgument, member: RedisArgument) { + parser.push('SISMEMBER'); + parser.pushKey(key); + parser.push(member); }, transformReply: undefined as unknown as () => NumberReply } as const satisfies Command; diff --git a/packages/client/lib/commands/SMEMBERS.spec.ts b/packages/client/lib/commands/SMEMBERS.spec.ts index 016b01ff747..6e2582e5abc 100644 --- a/packages/client/lib/commands/SMEMBERS.spec.ts +++ b/packages/client/lib/commands/SMEMBERS.spec.ts @@ -1,11 +1,12 @@ import { strict as assert } from 'node:assert'; import testUtils, { GLOBAL } from '../test-utils'; import SMEMBERS from './SMEMBERS'; +import { parseArgs } from './generic-transformers'; describe('SMEMBERS', () => { - it('transformArguments', () => { + it('processCommand', () => { assert.deepEqual( - SMEMBERS.transformArguments('key'), + parseArgs(SMEMBERS, 'key'), ['SMEMBERS', 'key'] ); }); diff --git a/packages/client/lib/commands/SMEMBERS.ts b/packages/client/lib/commands/SMEMBERS.ts index 391c83af6c6..6d018e999f4 100644 --- a/packages/client/lib/commands/SMEMBERS.ts +++ b/packages/client/lib/commands/SMEMBERS.ts @@ -1,10 +1,12 @@ +import { CommandParser } from '../client/parser'; import { RedisArgument, ArrayReply, BlobStringReply, SetReply, Command } from '../RESP/types'; export default { - FIRST_KEY_INDEX: 1, + CACHEABLE: true, IS_READ_ONLY: true, - transformArguments(key: RedisArgument) { - return ['SMEMBERS', key]; + parseCommand(parser: CommandParser, key: RedisArgument) { + parser.push('SMEMBERS'); + parser.pushKey(key); }, transformReply: { 2: undefined as unknown as () => ArrayReply, diff --git a/packages/client/lib/commands/SMISMEMBER.spec.ts b/packages/client/lib/commands/SMISMEMBER.spec.ts index 273ab05dd75..deff6912360 100644 --- a/packages/client/lib/commands/SMISMEMBER.spec.ts +++ b/packages/client/lib/commands/SMISMEMBER.spec.ts @@ -1,13 +1,14 @@ import { strict as assert } from 'node:assert'; import testUtils, { GLOBAL } from '../test-utils'; import SMISMEMBER from './SMISMEMBER'; +import { parseArgs } from './generic-transformers'; describe('SMISMEMBER', () => { testUtils.isVersionGreaterThanHook([6, 2]); - it('transformArguments', () => { + it('processCommand', () => { assert.deepEqual( - SMISMEMBER.transformArguments('key', ['1', '2']), + parseArgs(SMISMEMBER, 'key', ['1', '2']), ['SMISMEMBER', 'key', '1', '2'] ); }); diff --git a/packages/client/lib/commands/SMISMEMBER.ts b/packages/client/lib/commands/SMISMEMBER.ts index bdf48d45ab4..f0f3a143c7f 100644 --- a/packages/client/lib/commands/SMISMEMBER.ts +++ b/packages/client/lib/commands/SMISMEMBER.ts @@ -1,10 +1,13 @@ +import { CommandParser } from '../client/parser'; import { RedisArgument, ArrayReply, NumberReply, Command } from '../RESP/types'; export default { - FIRST_KEY_INDEX: 1, + CACHEABLE: true, IS_READ_ONLY: true, - transformArguments(key: RedisArgument, members: Array) { - return ['SMISMEMBER', key, ...members]; + parseCommand(parser: CommandParser, key: RedisArgument, members: Array) { + parser.push('SMISMEMBER'); + parser.pushKey(key); + parser.pushVariadic(members); }, transformReply: undefined as unknown as () => ArrayReply } as const satisfies Command; diff --git a/packages/client/lib/commands/SMOVE.spec.ts b/packages/client/lib/commands/SMOVE.spec.ts index 7ff2f773a7b..c68a6e41914 100644 --- a/packages/client/lib/commands/SMOVE.spec.ts +++ b/packages/client/lib/commands/SMOVE.spec.ts @@ -1,11 +1,12 @@ import { strict as assert } from 'node:assert'; import testUtils, { GLOBAL } from '../test-utils'; import SMOVE from './SMOVE'; +import { parseArgs } from './generic-transformers'; describe('SMOVE', () => { it('transformArguments', () => { assert.deepEqual( - SMOVE.transformArguments('source', 'destination', 'member'), + parseArgs(SMOVE, 'source', 'destination', 'member'), ['SMOVE', 'source', 'destination', 'member'] ); }); diff --git a/packages/client/lib/commands/SMOVE.ts b/packages/client/lib/commands/SMOVE.ts index 183b363fb90..d87eeefdfbf 100644 --- a/packages/client/lib/commands/SMOVE.ts +++ b/packages/client/lib/commands/SMOVE.ts @@ -1,14 +1,12 @@ +import { CommandParser } from '../client/parser'; import { RedisArgument, NumberReply, Command } from '../RESP/types'; export default { - FIRST_KEY_INDEX: 1, IS_READ_ONLY: false, - transformArguments( - source: RedisArgument, - destination: RedisArgument, - member: RedisArgument - ) { - return ['SMOVE', source, destination, member]; + parseCommand(parser: CommandParser, source: RedisArgument, destination: RedisArgument, member: RedisArgument) { + parser.push('SMOVE'); + parser.pushKeys([source, destination]); + parser.push(member); }, transformReply: undefined as unknown as () => NumberReply } as const satisfies Command; diff --git a/packages/client/lib/commands/SORT.spec.ts b/packages/client/lib/commands/SORT.spec.ts index 4fce8113755..330b321a1b8 100644 --- a/packages/client/lib/commands/SORT.spec.ts +++ b/packages/client/lib/commands/SORT.spec.ts @@ -1,19 +1,20 @@ import { strict as assert } from 'node:assert'; import testUtils, { GLOBAL } from '../test-utils'; import SORT from './SORT'; +import { parseArgs } from './generic-transformers'; describe('SORT', () => { describe('transformArguments', () => { it('simple', () => { assert.deepEqual( - SORT.transformArguments('key'), + parseArgs(SORT, 'key'), ['SORT', 'key'] ); }); it('with BY', () => { assert.deepEqual( - SORT.transformArguments('key', { + parseArgs(SORT, 'key', { BY: 'pattern' }), ['SORT', 'key', 'BY', 'pattern'] @@ -22,7 +23,7 @@ describe('SORT', () => { it('with LIMIT', () => { assert.deepEqual( - SORT.transformArguments('key', { + parseArgs(SORT, 'key', { LIMIT: { offset: 0, count: 1 @@ -35,7 +36,7 @@ describe('SORT', () => { describe('with GET', () => { it('string', () => { assert.deepEqual( - SORT.transformArguments('key', { + parseArgs(SORT, 'key', { GET: 'pattern' }), ['SORT', 'key', 'GET', 'pattern'] @@ -44,7 +45,7 @@ describe('SORT', () => { it('array', () => { assert.deepEqual( - SORT.transformArguments('key', { + parseArgs(SORT, 'key', { GET: ['1', '2'] }), ['SORT', 'key', 'GET', '1', 'GET', '2'] @@ -54,7 +55,7 @@ describe('SORT', () => { it('with DIRECTION', () => { assert.deepEqual( - SORT.transformArguments('key', { + parseArgs(SORT, 'key', { DIRECTION: 'ASC' }), ['SORT', 'key', 'ASC'] @@ -63,7 +64,7 @@ describe('SORT', () => { it('with ALPHA', () => { assert.deepEqual( - SORT.transformArguments('key', { + parseArgs(SORT, 'key', { ALPHA: true }), ['SORT', 'key', 'ALPHA'] @@ -72,7 +73,7 @@ describe('SORT', () => { it('with BY, LIMIT, GET, DIRECTION, ALPHA', () => { assert.deepEqual( - SORT.transformArguments('key', { + parseArgs(SORT, 'key', { BY: 'pattern', LIMIT: { offset: 0, diff --git a/packages/client/lib/commands/SORT.ts b/packages/client/lib/commands/SORT.ts index b71383943e9..3738d327d91 100644 --- a/packages/client/lib/commands/SORT.ts +++ b/packages/client/lib/commands/SORT.ts @@ -1,3 +1,4 @@ +import { CommandParser } from '../client/parser'; import { RedisArgument, ArrayReply, BlobStringReply, Command } from '../RESP/types'; export interface SortOptions { @@ -11,19 +12,19 @@ export interface SortOptions { ALPHA?: boolean; } -export function transformSortArguments( - command: RedisArgument, +export function parseSortArguments( + parser: CommandParser, key: RedisArgument, options?: SortOptions ) { - const args: Array = [command, key]; + parser.pushKey(key); if (options?.BY) { - args.push('BY', options.BY); + parser.push('BY', options.BY); } if (options?.LIMIT) { - args.push( + parser.push( 'LIMIT', options.LIMIT.offset.toString(), options.LIMIT.count.toString() @@ -33,27 +34,27 @@ export function transformSortArguments( if (options?.GET) { if (Array.isArray(options.GET)) { for (const pattern of options.GET) { - args.push('GET', pattern); + parser.push('GET', pattern); } } else { - args.push('GET', options.GET); + parser.push('GET', options.GET); } } if (options?.DIRECTION) { - args.push(options.DIRECTION); + parser.push(options.DIRECTION); } if (options?.ALPHA) { - args.push('ALPHA'); + parser.push('ALPHA'); } - - return args; } export default { - FIRST_KEY_INDEX: 1, IS_READ_ONLY: true, - transformArguments: transformSortArguments.bind(undefined, 'SORT'), + parseCommand(parser: CommandParser, key: RedisArgument, options?: SortOptions) { + parser.push('SORT'); + parseSortArguments(parser, key, options); + }, transformReply: undefined as unknown as () => ArrayReply } as const satisfies Command; diff --git a/packages/client/lib/commands/SORT_RO.spec.ts b/packages/client/lib/commands/SORT_RO.spec.ts index 963416ae639..86f8e507033 100644 --- a/packages/client/lib/commands/SORT_RO.spec.ts +++ b/packages/client/lib/commands/SORT_RO.spec.ts @@ -1,6 +1,7 @@ import { strict as assert } from 'node:assert'; import testUtils, { GLOBAL } from '../test-utils'; import SORT_RO from './SORT_RO'; +import { parseArgs } from './generic-transformers'; describe('SORT_RO', () => { testUtils.isVersionGreaterThanHook([7]); @@ -8,14 +9,14 @@ describe('SORT_RO', () => { describe('transformArguments', () => { it('simple', () => { assert.deepEqual( - SORT_RO.transformArguments('key'), + parseArgs(SORT_RO, 'key'), ['SORT_RO', 'key'] ); }); it('with BY', () => { assert.deepEqual( - SORT_RO.transformArguments('key', { + parseArgs(SORT_RO, 'key', { BY: 'pattern' }), ['SORT_RO', 'key', 'BY', 'pattern'] @@ -24,7 +25,7 @@ describe('SORT_RO', () => { it('with LIMIT', () => { assert.deepEqual( - SORT_RO.transformArguments('key', { + parseArgs(SORT_RO, 'key', { LIMIT: { offset: 0, count: 1 @@ -37,7 +38,7 @@ describe('SORT_RO', () => { describe('with GET', () => { it('string', () => { assert.deepEqual( - SORT_RO.transformArguments('key', { + parseArgs(SORT_RO, 'key', { GET: 'pattern' }), ['SORT_RO', 'key', 'GET', 'pattern'] @@ -46,7 +47,7 @@ describe('SORT_RO', () => { it('array', () => { assert.deepEqual( - SORT_RO.transformArguments('key', { + parseArgs(SORT_RO, 'key', { GET: ['1', '2'] }), ['SORT_RO', 'key', 'GET', '1', 'GET', '2'] @@ -56,7 +57,7 @@ describe('SORT_RO', () => { it('with DIRECTION', () => { assert.deepEqual( - SORT_RO.transformArguments('key', { + parseArgs(SORT_RO, 'key', { DIRECTION: 'ASC' }), ['SORT_RO', 'key', 'ASC'] @@ -65,7 +66,7 @@ describe('SORT_RO', () => { it('with ALPHA', () => { assert.deepEqual( - SORT_RO.transformArguments('key', { + parseArgs(SORT_RO, 'key', { ALPHA: true }), ['SORT_RO', 'key', 'ALPHA'] @@ -74,7 +75,7 @@ describe('SORT_RO', () => { it('with BY, LIMIT, GET, DIRECTION, ALPHA', () => { assert.deepEqual( - SORT_RO.transformArguments('key', { + parseArgs(SORT_RO, 'key', { BY: 'pattern', LIMIT: { offset: 0, diff --git a/packages/client/lib/commands/SORT_RO.ts b/packages/client/lib/commands/SORT_RO.ts index 459a0bbc03d..9901907c223 100644 --- a/packages/client/lib/commands/SORT_RO.ts +++ b/packages/client/lib/commands/SORT_RO.ts @@ -1,9 +1,13 @@ import { Command } from '../RESP/types'; -import SORT, { transformSortArguments } from './SORT'; +import SORT, { parseSortArguments } from './SORT'; export default { - FIRST_KEY_INDEX: SORT.FIRST_KEY_INDEX, IS_READ_ONLY: true, - transformArguments: transformSortArguments.bind(undefined, 'SORT_RO'), + parseCommand(...args: Parameters) { + const parser = args[0]; + + parser.push('SORT_RO'); + parseSortArguments(...args); + }, transformReply: SORT.transformReply } as const satisfies Command; diff --git a/packages/client/lib/commands/SORT_STORE.spec.ts b/packages/client/lib/commands/SORT_STORE.spec.ts index 49efd4c6ad8..a812cec52c5 100644 --- a/packages/client/lib/commands/SORT_STORE.spec.ts +++ b/packages/client/lib/commands/SORT_STORE.spec.ts @@ -1,19 +1,20 @@ import { strict as assert } from 'node:assert'; import testUtils, { GLOBAL } from '../test-utils'; import SORT_STORE from './SORT_STORE'; +import { parseArgs } from './generic-transformers'; describe('SORT STORE', () => { describe('transformArguments', () => { it('simple', () => { assert.deepEqual( - SORT_STORE.transformArguments('source', 'destination'), + parseArgs(SORT_STORE, 'source', 'destination'), ['SORT', 'source', 'STORE', 'destination'] ); }); it('with BY', () => { assert.deepEqual( - SORT_STORE.transformArguments('source', 'destination', { + parseArgs(SORT_STORE, 'source', 'destination', { BY: 'pattern' }), ['SORT', 'source', 'BY', 'pattern', 'STORE', 'destination'] @@ -22,7 +23,7 @@ describe('SORT STORE', () => { it('with LIMIT', () => { assert.deepEqual( - SORT_STORE.transformArguments('source', 'destination', { + parseArgs(SORT_STORE, 'source', 'destination', { LIMIT: { offset: 0, count: 1 @@ -35,7 +36,7 @@ describe('SORT STORE', () => { describe('with GET', () => { it('string', () => { assert.deepEqual( - SORT_STORE.transformArguments('source', 'destination', { + parseArgs(SORT_STORE, 'source', 'destination', { GET: 'pattern' }), ['SORT', 'source', 'GET', 'pattern', 'STORE', 'destination'] @@ -44,7 +45,7 @@ describe('SORT STORE', () => { it('array', () => { assert.deepEqual( - SORT_STORE.transformArguments('source', 'destination', { + parseArgs(SORT_STORE, 'source', 'destination', { GET: ['1', '2'] }), ['SORT', 'source', 'GET', '1', 'GET', '2', 'STORE', 'destination'] @@ -54,7 +55,7 @@ describe('SORT STORE', () => { it('with DIRECTION', () => { assert.deepEqual( - SORT_STORE.transformArguments('source', 'destination', { + parseArgs(SORT_STORE, 'source', 'destination', { DIRECTION: 'ASC' }), ['SORT', 'source', 'ASC', 'STORE', 'destination'] @@ -63,7 +64,7 @@ describe('SORT STORE', () => { it('with ALPHA', () => { assert.deepEqual( - SORT_STORE.transformArguments('source', 'destination', { + parseArgs(SORT_STORE, 'source', 'destination', { ALPHA: true }), ['SORT', 'source', 'ALPHA', 'STORE', 'destination'] @@ -72,7 +73,7 @@ describe('SORT STORE', () => { it('with BY, LIMIT, GET, DIRECTION, ALPHA', () => { assert.deepEqual( - SORT_STORE.transformArguments('source', 'destination', { + parseArgs(SORT_STORE, 'source', 'destination', { BY: 'pattern', LIMIT: { offset: 0, diff --git a/packages/client/lib/commands/SORT_STORE.ts b/packages/client/lib/commands/SORT_STORE.ts index b6ad709fb60..15c94732e41 100644 --- a/packages/client/lib/commands/SORT_STORE.ts +++ b/packages/client/lib/commands/SORT_STORE.ts @@ -1,17 +1,12 @@ +import { CommandParser } from '../client/parser'; import { RedisArgument, NumberReply, Command } from '../RESP/types'; import SORT, { SortOptions } from './SORT'; export default { - FIRST_KEY_INDEX: 1, IS_READ_ONLY: false, - transformArguments( - source: RedisArgument, - destination: RedisArgument, - options?: SortOptions - ) { - const args = SORT.transformArguments(source, options); - args.push('STORE', destination); - return args; + parseCommand(parser: CommandParser, source: RedisArgument, destination: RedisArgument, options?: SortOptions) { + SORT.parseCommand(parser, source, options); + parser.push('STORE', destination); }, transformReply: undefined as unknown as () => NumberReply } as const satisfies Command; diff --git a/packages/client/lib/commands/SPOP.spec.ts b/packages/client/lib/commands/SPOP.spec.ts index 896c1c820ac..f435134416b 100644 --- a/packages/client/lib/commands/SPOP.spec.ts +++ b/packages/client/lib/commands/SPOP.spec.ts @@ -1,11 +1,12 @@ import { strict as assert } from 'node:assert'; import testUtils, { GLOBAL } from '../test-utils'; import SPOP from './SPOP'; +import { parseArgs } from './generic-transformers'; describe('SPOP', () => { it('transformArguments', () => { assert.deepEqual( - SPOP.transformArguments('key'), + parseArgs(SPOP, 'key'), ['SPOP', 'key'] ); }); diff --git a/packages/client/lib/commands/SPOP.ts b/packages/client/lib/commands/SPOP.ts index 4b061a86306..38f40989e63 100644 --- a/packages/client/lib/commands/SPOP.ts +++ b/packages/client/lib/commands/SPOP.ts @@ -1,10 +1,11 @@ +import { CommandParser } from '../client/parser'; import { RedisArgument, BlobStringReply, NullReply, Command } from '../RESP/types'; export default { - FIRST_KEY_INDEX: 1, IS_READ_ONLY: false, - transformArguments(key: RedisArgument) { - return ['SPOP', key]; + parseCommand(parser: CommandParser, key: RedisArgument) { + parser.push('SPOP'); + parser.pushKey(key); }, transformReply: undefined as unknown as () => BlobStringReply | NullReply } as const satisfies Command; diff --git a/packages/client/lib/commands/SPOP_COUNT.spec.ts b/packages/client/lib/commands/SPOP_COUNT.spec.ts index ddad816b420..935ff437800 100644 --- a/packages/client/lib/commands/SPOP_COUNT.spec.ts +++ b/packages/client/lib/commands/SPOP_COUNT.spec.ts @@ -1,11 +1,12 @@ import { strict as assert } from 'node:assert'; import testUtils, { GLOBAL } from '../test-utils'; import SPOP_COUNT from './SPOP_COUNT'; +import { parseArgs } from './generic-transformers'; describe('SPOP_COUNT', () => { it('transformArguments', () => { assert.deepEqual( - SPOP_COUNT.transformArguments('key', 1), + parseArgs(SPOP_COUNT, 'key', 1), ['SPOP', 'key', '1'] ); }); diff --git a/packages/client/lib/commands/SPOP_COUNT.ts b/packages/client/lib/commands/SPOP_COUNT.ts index 4c68ae8d08e..0536203be97 100644 --- a/packages/client/lib/commands/SPOP_COUNT.ts +++ b/packages/client/lib/commands/SPOP_COUNT.ts @@ -1,10 +1,12 @@ +import { CommandParser } from '../client/parser'; import { RedisArgument, BlobStringReply, NullReply, Command } from '../RESP/types'; export default { - FIRST_KEY_INDEX: 1, IS_READ_ONLY: false, - transformArguments(key: RedisArgument, count: number) { - return ['SPOP', key, count.toString()]; + parseCommand(parser: CommandParser, key: RedisArgument, count: number) { + parser.push('SPOP'); + parser.pushKey(key); + parser.push(count.toString()); }, transformReply: undefined as unknown as () => BlobStringReply | NullReply } as const satisfies Command; diff --git a/packages/client/lib/commands/SPUBLISH.spec.ts b/packages/client/lib/commands/SPUBLISH.spec.ts index 741372d0154..5a53bc40b7d 100644 --- a/packages/client/lib/commands/SPUBLISH.spec.ts +++ b/packages/client/lib/commands/SPUBLISH.spec.ts @@ -1,13 +1,14 @@ import { strict as assert } from 'node:assert'; import testUtils, { GLOBAL } from '../test-utils'; import SPUBLISH from './SPUBLISH'; +import { parseArgs } from './generic-transformers'; describe('SPUBLISH', () => { testUtils.isVersionGreaterThanHook([7]); it('transformArguments', () => { assert.deepEqual( - SPUBLISH.transformArguments('channel', 'message'), + parseArgs(SPUBLISH, 'channel', 'message'), ['SPUBLISH', 'channel', 'message'] ); }); diff --git a/packages/client/lib/commands/SPUBLISH.ts b/packages/client/lib/commands/SPUBLISH.ts index 19d84b03c6f..77d93e617de 100644 --- a/packages/client/lib/commands/SPUBLISH.ts +++ b/packages/client/lib/commands/SPUBLISH.ts @@ -1,10 +1,12 @@ +import { CommandParser } from '../client/parser'; import { RedisArgument, NumberReply, Command } from '../RESP/types'; export default { - FIRST_KEY_INDEX: 1, IS_READ_ONLY: true, - transformArguments(channel: RedisArgument, message: RedisArgument) { - return ['SPUBLISH', channel, message]; + parseCommand(parser: CommandParser, channel: RedisArgument, message: RedisArgument) { + parser.push('SPUBLISH'); + parser.pushKey(channel); + parser.push(message); }, transformReply: undefined as unknown as () => NumberReply } as const satisfies Command; diff --git a/packages/client/lib/commands/SRANDMEMBER.spec.ts b/packages/client/lib/commands/SRANDMEMBER.spec.ts index a7df01f0eb9..637aac27b29 100644 --- a/packages/client/lib/commands/SRANDMEMBER.spec.ts +++ b/packages/client/lib/commands/SRANDMEMBER.spec.ts @@ -1,11 +1,12 @@ import { strict as assert } from 'node:assert'; import testUtils, { GLOBAL } from '../test-utils'; import SRANDMEMBER from './SRANDMEMBER'; +import { parseArgs } from './generic-transformers'; describe('SRANDMEMBER', () => { it('transformArguments', () => { assert.deepEqual( - SRANDMEMBER.transformArguments('key'), + parseArgs(SRANDMEMBER, 'key'), ['SRANDMEMBER', 'key'] ); }); diff --git a/packages/client/lib/commands/SRANDMEMBER.ts b/packages/client/lib/commands/SRANDMEMBER.ts index 6a2373ae927..4285f7aa17c 100644 --- a/packages/client/lib/commands/SRANDMEMBER.ts +++ b/packages/client/lib/commands/SRANDMEMBER.ts @@ -1,10 +1,11 @@ +import { CommandParser } from '../client/parser'; import { RedisArgument, BlobStringReply, NullReply, Command } from '../RESP/types'; export default { - FIRST_KEY_INDEX: 1, IS_READ_ONLY: true, - transformArguments(key: RedisArgument) { - return ['SRANDMEMBER', key]; + parseCommand(parser: CommandParser, key: RedisArgument) { + parser.push('SRANDMEMBER') + parser.pushKey(key); }, transformReply: undefined as unknown as () => BlobStringReply | NullReply } as const satisfies Command; diff --git a/packages/client/lib/commands/SRANDMEMBER_COUNT.spec.ts b/packages/client/lib/commands/SRANDMEMBER_COUNT.spec.ts index 364eb640341..13bb0d52d96 100644 --- a/packages/client/lib/commands/SRANDMEMBER_COUNT.spec.ts +++ b/packages/client/lib/commands/SRANDMEMBER_COUNT.spec.ts @@ -1,11 +1,12 @@ import { strict as assert } from 'node:assert'; import testUtils, { GLOBAL } from '../test-utils'; import SRANDMEMBER_COUNT from './SRANDMEMBER_COUNT'; +import { parseArgs } from './generic-transformers'; describe('SRANDMEMBER COUNT', () => { it('transformArguments', () => { assert.deepEqual( - SRANDMEMBER_COUNT.transformArguments('key', 1), + parseArgs(SRANDMEMBER_COUNT, 'key', 1), ['SRANDMEMBER', 'key', '1'] ); }); diff --git a/packages/client/lib/commands/SRANDMEMBER_COUNT.ts b/packages/client/lib/commands/SRANDMEMBER_COUNT.ts index 778f3d8f629..dd72245c3b3 100644 --- a/packages/client/lib/commands/SRANDMEMBER_COUNT.ts +++ b/packages/client/lib/commands/SRANDMEMBER_COUNT.ts @@ -1,13 +1,12 @@ +import { CommandParser } from '../client/parser'; import { RedisArgument, ArrayReply, BlobStringReply, Command } from '../RESP/types'; import SRANDMEMBER from './SRANDMEMBER'; export default { - FIRST_KEY_INDEX: SRANDMEMBER.FIRST_KEY_INDEX, IS_READ_ONLY: SRANDMEMBER.IS_READ_ONLY, - transformArguments(key: RedisArgument, count: number) { - const args = SRANDMEMBER.transformArguments(key); - args.push(count.toString()); - return args; + parseCommand(parser: CommandParser, key: RedisArgument, count: number) { + SRANDMEMBER.parseCommand(parser, key); + parser.push(count.toString()); }, transformReply: undefined as unknown as () => ArrayReply } as const satisfies Command; diff --git a/packages/client/lib/commands/SREM.spec.ts b/packages/client/lib/commands/SREM.spec.ts index f17c6fb118e..6def4178fc8 100644 --- a/packages/client/lib/commands/SREM.spec.ts +++ b/packages/client/lib/commands/SREM.spec.ts @@ -1,19 +1,20 @@ import { strict as assert } from 'node:assert'; import testUtils, { GLOBAL } from '../test-utils'; import SREM from './SREM'; +import { parseArgs } from './generic-transformers'; describe('SREM', () => { describe('transformArguments', () => { it('string', () => { assert.deepEqual( - SREM.transformArguments('key', 'member'), + parseArgs(SREM, 'key', 'member'), ['SREM', 'key', 'member'] ); }); it('array', () => { assert.deepEqual( - SREM.transformArguments('key', ['1', '2']), + parseArgs(SREM, 'key', ['1', '2']), ['SREM', 'key', '1', '2'] ); }); diff --git a/packages/client/lib/commands/SREM.ts b/packages/client/lib/commands/SREM.ts index daa95493d02..75053474cce 100644 --- a/packages/client/lib/commands/SREM.ts +++ b/packages/client/lib/commands/SREM.ts @@ -1,11 +1,13 @@ +import { CommandParser } from '../client/parser'; import { NumberReply, Command, RedisArgument } from '../RESP/types'; -import { RedisVariadicArgument, pushVariadicArguments } from './generic-transformers'; +import { RedisVariadicArgument } from './generic-transformers'; export default { - FIRST_KEY_INDEX: 1, IS_READ_ONLY: false, - transformArguments(key: RedisArgument, members: RedisVariadicArgument) { - return pushVariadicArguments(['SREM', key], members); + parseCommand(parser: CommandParser, key: RedisArgument, members: RedisVariadicArgument) { + parser.push('SREM'); + parser.pushKey(key); + parser.pushVariadic(members); }, transformReply: undefined as unknown as () => NumberReply } as const satisfies Command; diff --git a/packages/client/lib/commands/SSCAN.spec.ts b/packages/client/lib/commands/SSCAN.spec.ts index 29a13306fde..e5d689c6e98 100644 --- a/packages/client/lib/commands/SSCAN.spec.ts +++ b/packages/client/lib/commands/SSCAN.spec.ts @@ -1,19 +1,20 @@ import { strict as assert } from 'node:assert'; import testUtils, { GLOBAL } from '../test-utils'; import SSCAN from './SSCAN'; +import { parseArgs } from './generic-transformers'; describe('SSCAN', () => { describe('transformArguments', () => { it('cusror only', () => { assert.deepEqual( - SSCAN.transformArguments('key', '0'), + parseArgs(SSCAN, 'key', '0'), ['SSCAN', 'key', '0'] ); }); it('with MATCH', () => { assert.deepEqual( - SSCAN.transformArguments('key', '0', { + parseArgs(SSCAN, 'key', '0', { MATCH: 'pattern' }), ['SSCAN', 'key', '0', 'MATCH', 'pattern'] @@ -22,7 +23,7 @@ describe('SSCAN', () => { it('with COUNT', () => { assert.deepEqual( - SSCAN.transformArguments('key', '0', { + parseArgs(SSCAN, 'key', '0', { COUNT: 1 }), ['SSCAN', 'key', '0', 'COUNT', '1'] @@ -31,7 +32,7 @@ describe('SSCAN', () => { it('with MATCH & COUNT', () => { assert.deepEqual( - SSCAN.transformArguments('key', '0', { + parseArgs(SSCAN, 'key', '0', { MATCH: 'pattern', COUNT: 1 }), diff --git a/packages/client/lib/commands/SSCAN.ts b/packages/client/lib/commands/SSCAN.ts index f47144d834c..22634d56242 100644 --- a/packages/client/lib/commands/SSCAN.ts +++ b/packages/client/lib/commands/SSCAN.ts @@ -1,15 +1,18 @@ +import { CommandParser } from '../client/parser'; import { RedisArgument, BlobStringReply, Command } from '../RESP/types'; -import { ScanCommonOptions, pushScanArguments } from './SCAN'; +import { ScanCommonOptions, parseScanArguments} from './SCAN'; export default { - FIRST_KEY_INDEX: 1, IS_READ_ONLY: true, - transformArguments( + parseCommand( + parser: CommandParser, key: RedisArgument, cursor: RedisArgument, options?: ScanCommonOptions ) { - return pushScanArguments(['SSCAN', key], cursor, options); + parser.push('SSCAN'); + parser.pushKey(key); + parseScanArguments(parser, cursor, options); }, transformReply([cursor, members]: [BlobStringReply, Array]) { return { diff --git a/packages/client/lib/commands/STRLEN.spec.ts b/packages/client/lib/commands/STRLEN.spec.ts index b07c07b909a..dbb7a08541b 100644 --- a/packages/client/lib/commands/STRLEN.spec.ts +++ b/packages/client/lib/commands/STRLEN.spec.ts @@ -1,11 +1,12 @@ import { strict as assert } from 'node:assert'; import testUtils, { GLOBAL } from '../test-utils'; import STRLEN from './STRLEN'; +import { parseArgs } from './generic-transformers'; describe('STRLEN', () => { it('transformArguments', () => { assert.deepEqual( - STRLEN.transformArguments('key'), + parseArgs(STRLEN, 'key'), ['STRLEN', 'key'] ); }); diff --git a/packages/client/lib/commands/STRLEN.ts b/packages/client/lib/commands/STRLEN.ts index 594530ff6bf..34e0430fc9e 100644 --- a/packages/client/lib/commands/STRLEN.ts +++ b/packages/client/lib/commands/STRLEN.ts @@ -1,10 +1,12 @@ +import { CommandParser } from '../client/parser'; import { RedisArgument, NumberReply, Command } from '../RESP/types'; export default { - FIRST_KEY_INDEX: 1, + CACHEABLE: true, IS_READ_ONLY: true, - transformArguments(key: RedisArgument) { - return ['STRLEN', key]; + parseCommand(parser: CommandParser, key: RedisArgument) { + parser.push('STRLEN'); + parser.pushKey(key); }, transformReply: undefined as unknown as () => NumberReply } as const satisfies Command; diff --git a/packages/client/lib/commands/SUNION.spec.ts b/packages/client/lib/commands/SUNION.spec.ts index ff00c44a1b1..a4389d4236e 100644 --- a/packages/client/lib/commands/SUNION.spec.ts +++ b/packages/client/lib/commands/SUNION.spec.ts @@ -1,19 +1,20 @@ import { strict as assert } from 'node:assert'; import testUtils, { GLOBAL } from '../test-utils'; import SUNION from './SUNION'; +import { parseArgs } from './generic-transformers'; describe('SUNION', () => { describe('transformArguments', () => { it('string', () => { assert.deepEqual( - SUNION.transformArguments('key'), + parseArgs(SUNION, 'key'), ['SUNION', 'key'] ); }); it('array', () => { assert.deepEqual( - SUNION.transformArguments(['1', '2']), + parseArgs(SUNION, ['1', '2']), ['SUNION', '1', '2'] ); }); diff --git a/packages/client/lib/commands/SUNION.ts b/packages/client/lib/commands/SUNION.ts index 42042217e2b..3d9a5954a7c 100644 --- a/packages/client/lib/commands/SUNION.ts +++ b/packages/client/lib/commands/SUNION.ts @@ -1,11 +1,13 @@ +import { CommandParser } from '../client/parser'; import { ArrayReply, BlobStringReply, Command } from '../RESP/types'; -import { RedisVariadicArgument, pushVariadicArguments } from './generic-transformers'; +import { RedisVariadicArgument } from './generic-transformers'; export default { - FIRST_KEY_INDEX: 1, + CACHEABLE: true, IS_READ_ONLY: true, - transformArguments(keys: RedisVariadicArgument) { - return pushVariadicArguments(['SUNION'], keys); + parseCommand(parser: CommandParser, keys: RedisVariadicArgument) { + parser.push('SUNION'); + parser.pushKeys(keys); }, transformReply: undefined as unknown as () => ArrayReply } as const satisfies Command; diff --git a/packages/client/lib/commands/SUNIONSTORE.spec.ts b/packages/client/lib/commands/SUNIONSTORE.spec.ts index 790fd78060a..8f3db2cacd7 100644 --- a/packages/client/lib/commands/SUNIONSTORE.spec.ts +++ b/packages/client/lib/commands/SUNIONSTORE.spec.ts @@ -1,19 +1,20 @@ import { strict as assert } from 'node:assert'; import testUtils, { GLOBAL } from '../test-utils'; import SUNIONSTORE from './SUNIONSTORE'; +import { parseArgs } from './generic-transformers'; describe('SUNIONSTORE', () => { describe('transformArguments', () => { it('string', () => { assert.deepEqual( - SUNIONSTORE.transformArguments('destination', 'key'), + parseArgs(SUNIONSTORE, 'destination', 'key'), ['SUNIONSTORE', 'destination', 'key'] ); }); it('array', () => { assert.deepEqual( - SUNIONSTORE.transformArguments('destination', ['1', '2']), + parseArgs(SUNIONSTORE, 'destination', ['1', '2']), ['SUNIONSTORE', 'destination', '1', '2'] ); }); diff --git a/packages/client/lib/commands/SUNIONSTORE.ts b/packages/client/lib/commands/SUNIONSTORE.ts index 9adaa9288f3..e2f43ecb1c8 100644 --- a/packages/client/lib/commands/SUNIONSTORE.ts +++ b/packages/client/lib/commands/SUNIONSTORE.ts @@ -1,14 +1,13 @@ +import { CommandParser } from '../client/parser'; import { RedisArgument, NumberReply, Command } from '../RESP/types'; -import { RedisVariadicArgument, pushVariadicArguments } from './generic-transformers'; +import { RedisVariadicArgument } from './generic-transformers'; export default { - FIRST_KEY_INDEX: 1, IS_READ_ONLY: false, - transformArguments( - destination: RedisArgument, - keys: RedisVariadicArgument - ) { - return pushVariadicArguments(['SUNIONSTORE', destination], keys); + parseCommand(parser: CommandParser, destination: RedisArgument, keys: RedisVariadicArgument) { + parser.push('SUNIONSTORE'); + parser.pushKey(destination); + parser.pushKeys(keys); }, transformReply: undefined as unknown as () => NumberReply } as const satisfies Command; diff --git a/packages/client/lib/commands/SWAPDB.spec.ts b/packages/client/lib/commands/SWAPDB.spec.ts index 9331854c13b..a3b53b27218 100644 --- a/packages/client/lib/commands/SWAPDB.spec.ts +++ b/packages/client/lib/commands/SWAPDB.spec.ts @@ -1,11 +1,12 @@ import { strict as assert } from 'node:assert'; import testUtils, { GLOBAL } from '../test-utils'; import SWAPDB from './SWAPDB'; +import { parseArgs } from './generic-transformers'; describe('SWAPDB', () => { it('transformArguments', () => { assert.deepEqual( - SWAPDB.transformArguments(0, 1), + parseArgs(SWAPDB, 0, 1), ['SWAPDB', '0', '1'] ); }); diff --git a/packages/client/lib/commands/SWAPDB.ts b/packages/client/lib/commands/SWAPDB.ts index f3b768e95f4..e59c75715cd 100644 --- a/packages/client/lib/commands/SWAPDB.ts +++ b/packages/client/lib/commands/SWAPDB.ts @@ -1,10 +1,11 @@ +import { CommandParser } from '../client/parser'; import { SimpleStringReply, Command } from '../RESP/types'; export default { - FIRST_KEY_INDEX: undefined, + NOT_KEYED_COMMAND: true, IS_READ_ONLY: false, - transformArguments(index1: number, index2: number) { - return ['SWAPDB', index1.toString(), index2.toString()]; + parseCommand(parser: CommandParser, index1: number, index2: number) { + parser.push('SWAPDB', index1.toString(), index2.toString()); }, transformReply: undefined as unknown as () => SimpleStringReply<'OK'> } as const satisfies Command; diff --git a/packages/client/lib/commands/TIME.spec.ts b/packages/client/lib/commands/TIME.spec.ts index d9dd9667ea4..4ee704f0dd0 100644 --- a/packages/client/lib/commands/TIME.spec.ts +++ b/packages/client/lib/commands/TIME.spec.ts @@ -1,11 +1,12 @@ import { strict as assert } from 'node:assert'; import testUtils, { GLOBAL } from '../test-utils'; import TIME from './TIME'; +import { parseArgs } from './generic-transformers'; describe('TIME', () => { it('transformArguments', () => { assert.deepEqual( - TIME.transformArguments(), + parseArgs(TIME), ['TIME'] ); }); diff --git a/packages/client/lib/commands/TIME.ts b/packages/client/lib/commands/TIME.ts index d4dc67ae483..b25af710e1c 100644 --- a/packages/client/lib/commands/TIME.ts +++ b/packages/client/lib/commands/TIME.ts @@ -1,10 +1,11 @@ +import { CommandParser } from '../client/parser'; import { BlobStringReply, Command } from '../RESP/types'; export default { - FIRST_KEY_INDEX: undefined, + NOT_KEYED_COMMAND: true, IS_READ_ONLY: true, - transformArguments() { - return ['TIME']; + parseCommand(parser: CommandParser) { + parser.push('TIME'); }, transformReply: undefined as unknown as () => [ unixTimestamp: BlobStringReply<`${number}`>, diff --git a/packages/client/lib/commands/TOUCH.spec.ts b/packages/client/lib/commands/TOUCH.spec.ts index 48e77900ee3..69a3498346b 100644 --- a/packages/client/lib/commands/TOUCH.spec.ts +++ b/packages/client/lib/commands/TOUCH.spec.ts @@ -1,19 +1,20 @@ import { strict as assert } from 'node:assert'; import testUtils, { GLOBAL } from '../test-utils'; import TOUCH from './TOUCH'; +import { parseArgs } from './generic-transformers'; describe('TOUCH', () => { describe('transformArguments', () => { it('string', () => { assert.deepEqual( - TOUCH.transformArguments('key'), + parseArgs(TOUCH, 'key'), ['TOUCH', 'key'] ); }); it('array', () => { assert.deepEqual( - TOUCH.transformArguments(['1', '2']), + parseArgs(TOUCH, ['1', '2']), ['TOUCH', '1', '2'] ); }); diff --git a/packages/client/lib/commands/TOUCH.ts b/packages/client/lib/commands/TOUCH.ts index c1c19402f8b..c765c9f8347 100644 --- a/packages/client/lib/commands/TOUCH.ts +++ b/packages/client/lib/commands/TOUCH.ts @@ -1,11 +1,12 @@ +import { CommandParser } from '../client/parser'; import { NumberReply, Command } from '../RESP/types'; -import { RedisVariadicArgument, pushVariadicArguments } from './generic-transformers'; +import { RedisVariadicArgument } from './generic-transformers'; export default { - FIRST_KEY_INDEX: 1, IS_READ_ONLY: false, - transformArguments(key: RedisVariadicArgument) { - return pushVariadicArguments(['TOUCH'], key); + parseCommand(parser: CommandParser, key: RedisVariadicArgument) { + parser.push('TOUCH'); + parser.pushKeys(key); }, transformReply: undefined as unknown as () => NumberReply } as const satisfies Command; diff --git a/packages/client/lib/commands/TTL.spec.ts b/packages/client/lib/commands/TTL.spec.ts index 6b709226a2b..4d36053c02e 100644 --- a/packages/client/lib/commands/TTL.spec.ts +++ b/packages/client/lib/commands/TTL.spec.ts @@ -1,11 +1,12 @@ import { strict as assert } from 'node:assert'; import testUtils, { GLOBAL } from '../test-utils'; import TTL from './TTL'; +import { parseArgs } from './generic-transformers'; describe('TTL', () => { it('transformArguments', () => { assert.deepEqual( - TTL.transformArguments('key'), + parseArgs(TTL, 'key'), ['TTL', 'key'] ); }); diff --git a/packages/client/lib/commands/TTL.ts b/packages/client/lib/commands/TTL.ts index 65c3b7b026f..8420089fcb9 100644 --- a/packages/client/lib/commands/TTL.ts +++ b/packages/client/lib/commands/TTL.ts @@ -1,10 +1,11 @@ +import { CommandParser } from '../client/parser'; import { RedisArgument, NumberReply, Command } from '../RESP/types'; export default { - FIRST_KEY_INDEX: 1, IS_READ_ONLY: true, - transformArguments(key: RedisArgument) { - return ['TTL', key]; + parseCommand(parser: CommandParser, key: RedisArgument) { + parser.push('TTL'); + parser.pushKey(key); }, transformReply: undefined as unknown as () => NumberReply } as const satisfies Command; diff --git a/packages/client/lib/commands/TYPE.spec.ts b/packages/client/lib/commands/TYPE.spec.ts index 45cf1cfc1c9..ae7392cdce9 100644 --- a/packages/client/lib/commands/TYPE.spec.ts +++ b/packages/client/lib/commands/TYPE.spec.ts @@ -1,11 +1,12 @@ import { strict as assert } from 'node:assert'; import testUtils, { GLOBAL } from '../test-utils'; import TYPE from './TYPE'; +import { parseArgs } from './generic-transformers'; describe('TYPE', () => { - it('transformArguments', () => { + it('processCommand', () => { assert.deepEqual( - TYPE.transformArguments('key'), + parseArgs(TYPE, 'key'), ['TYPE', 'key'] ); }); diff --git a/packages/client/lib/commands/TYPE.ts b/packages/client/lib/commands/TYPE.ts index 09f6887492c..ffc592994db 100644 --- a/packages/client/lib/commands/TYPE.ts +++ b/packages/client/lib/commands/TYPE.ts @@ -1,10 +1,12 @@ +import { CommandParser } from '../client/parser'; import { RedisArgument, SimpleStringReply, Command } from '../RESP/types'; export default { - FIRST_KEY_INDEX: 1, + CACHEABLE: true, IS_READ_ONLY: true, - transformArguments(key: RedisArgument) { - return ['TYPE', key]; + parseCommand(parser: CommandParser, key: RedisArgument) { + parser.push('TYPE'); + parser.pushKey(key); }, transformReply: undefined as unknown as () => SimpleStringReply } as const satisfies Command; diff --git a/packages/client/lib/commands/UNLINK.spec.ts b/packages/client/lib/commands/UNLINK.spec.ts index 1e374783007..2c32bee8e33 100644 --- a/packages/client/lib/commands/UNLINK.spec.ts +++ b/packages/client/lib/commands/UNLINK.spec.ts @@ -1,19 +1,20 @@ import { strict as assert } from 'node:assert'; import testUtils, { GLOBAL } from '../test-utils'; import UNLINK from './UNLINK'; +import { parseArgs } from './generic-transformers'; describe('UNLINK', () => { describe('transformArguments', () => { it('string', () => { assert.deepEqual( - UNLINK.transformArguments('key'), + parseArgs(UNLINK, 'key'), ['UNLINK', 'key'] ); }); it('array', () => { assert.deepEqual( - UNLINK.transformArguments(['1', '2']), + parseArgs(UNLINK, ['1', '2']), ['UNLINK', '1', '2'] ); }); diff --git a/packages/client/lib/commands/UNLINK.ts b/packages/client/lib/commands/UNLINK.ts index 2346573f397..14d1e700277 100644 --- a/packages/client/lib/commands/UNLINK.ts +++ b/packages/client/lib/commands/UNLINK.ts @@ -1,11 +1,12 @@ +import { CommandParser } from '../client/parser'; import { NumberReply, Command } from '../RESP/types'; -import { RedisVariadicArgument, pushVariadicArguments } from './generic-transformers'; +import { RedisVariadicArgument } from './generic-transformers'; export default { - FIRST_KEY_INDEX: 1, IS_READ_ONLY: false, - transformArguments(key: RedisVariadicArgument) { - return pushVariadicArguments(['UNLINK'], key); + parseCommand(parser: CommandParser, keys: RedisVariadicArgument) { + parser.push('UNLINK'); + parser.pushKeys(keys); }, transformReply: undefined as unknown as () => NumberReply } as const satisfies Command; diff --git a/packages/client/lib/commands/WAIT.spec.ts b/packages/client/lib/commands/WAIT.spec.ts index 61b197c90ba..d2778e7967b 100644 --- a/packages/client/lib/commands/WAIT.spec.ts +++ b/packages/client/lib/commands/WAIT.spec.ts @@ -1,11 +1,12 @@ import { strict as assert } from 'node:assert'; import testUtils, { GLOBAL } from '../test-utils'; import WAIT from './WAIT'; +import { parseArgs } from './generic-transformers'; describe('WAIT', () => { it('transformArguments', () => { assert.deepEqual( - WAIT.transformArguments(0, 1), + parseArgs(WAIT, 0, 1), ['WAIT', '0', '1'] ); }); diff --git a/packages/client/lib/commands/WAIT.ts b/packages/client/lib/commands/WAIT.ts index 21c39a643e5..df45a12373d 100644 --- a/packages/client/lib/commands/WAIT.ts +++ b/packages/client/lib/commands/WAIT.ts @@ -1,10 +1,11 @@ +import { CommandParser } from '../client/parser'; import { NumberReply, Command } from '../RESP/types'; export default { - FIRST_KEY_INDEX: undefined, + NOT_KEYED_COMMAND: true, IS_READ_ONLY: true, - transformArguments(numberOfReplicas: number, timeout: number) { - return ['WAIT', numberOfReplicas.toString(), timeout.toString()]; + parseCommand(parser: CommandParser, numberOfReplicas: number, timeout: number) { + parser.push('WAIT', numberOfReplicas.toString(), timeout.toString()); }, transformReply: undefined as unknown as () => NumberReply } as const satisfies Command; diff --git a/packages/client/lib/commands/XACK.spec.ts b/packages/client/lib/commands/XACK.spec.ts index 81a7954ffd6..4ad60b256d0 100644 --- a/packages/client/lib/commands/XACK.spec.ts +++ b/packages/client/lib/commands/XACK.spec.ts @@ -1,19 +1,20 @@ import { strict as assert } from 'node:assert'; import testUtils, { GLOBAL } from '../test-utils'; import XACK from './XACK'; +import { parseArgs } from './generic-transformers'; describe('XACK', () => { describe('transformArguments', () => { it('string', () => { assert.deepEqual( - XACK.transformArguments('key', 'group', '0-0'), + parseArgs(XACK, 'key', 'group', '0-0'), ['XACK', 'key', 'group', '0-0'] ); }); it('array', () => { assert.deepEqual( - XACK.transformArguments('key', 'group', ['0-0', '1-0']), + parseArgs(XACK, 'key', 'group', ['0-0', '1-0']), ['XACK', 'key', 'group', '0-0', '1-0'] ); }); diff --git a/packages/client/lib/commands/XACK.ts b/packages/client/lib/commands/XACK.ts index 89b2d581201..2500134f1c8 100644 --- a/packages/client/lib/commands/XACK.ts +++ b/packages/client/lib/commands/XACK.ts @@ -1,15 +1,15 @@ +import { CommandParser } from '../client/parser'; import { NumberReply, Command, RedisArgument } from '../RESP/types'; -import { RedisVariadicArgument, pushVariadicArguments } from './generic-transformers'; +import { RedisVariadicArgument } from './generic-transformers'; export default { - FIRST_KEY_INDEX: 1, IS_READ_ONLY: false, - transformArguments( - key: RedisArgument, - group: RedisArgument, - id: RedisVariadicArgument - ) { - return pushVariadicArguments(['XACK', key, group], id); + parseCommand(parser: CommandParser, key: RedisArgument, group: RedisArgument, id: RedisVariadicArgument) { + parser.push('XACK'); + parser.pushKey(key); + parser.push(group) + parser.pushVariadic(id); }, transformReply: undefined as unknown as () => NumberReply } as const satisfies Command; + \ No newline at end of file diff --git a/packages/client/lib/commands/XADD.spec.ts b/packages/client/lib/commands/XADD.spec.ts index 10c6f4daa56..321581d0865 100644 --- a/packages/client/lib/commands/XADD.spec.ts +++ b/packages/client/lib/commands/XADD.spec.ts @@ -1,12 +1,13 @@ import { strict as assert } from 'node:assert'; import testUtils, { GLOBAL } from '../test-utils'; import XADD from './XADD'; +import { parseArgs } from './generic-transformers'; describe('XADD', () => { describe('transformArguments', () => { it('single field', () => { assert.deepEqual( - XADD.transformArguments('key', '*', { + parseArgs(XADD, 'key', '*', { field: 'value' }), ['XADD', 'key', '*', 'field', 'value'] @@ -15,7 +16,7 @@ describe('XADD', () => { it('multiple fields', () => { assert.deepEqual( - XADD.transformArguments('key', '*', { + parseArgs(XADD, 'key', '*', { '1': 'I', '2': 'II' }), @@ -25,7 +26,7 @@ describe('XADD', () => { it('with TRIM', () => { assert.deepEqual( - XADD.transformArguments('key', '*', { + parseArgs(XADD, 'key', '*', { field: 'value' }, { TRIM: { @@ -38,7 +39,7 @@ describe('XADD', () => { it('with TRIM.strategy', () => { assert.deepEqual( - XADD.transformArguments('key', '*', { + parseArgs(XADD, 'key', '*', { field: 'value' }, { TRIM: { @@ -52,7 +53,7 @@ describe('XADD', () => { it('with TRIM.strategyModifier', () => { assert.deepEqual( - XADD.transformArguments('key', '*', { + parseArgs(XADD, 'key', '*', { field: 'value' }, { TRIM: { @@ -66,7 +67,7 @@ describe('XADD', () => { it('with TRIM.limit', () => { assert.deepEqual( - XADD.transformArguments('key', '*', { + parseArgs(XADD, 'key', '*', { field: 'value' }, { TRIM: { diff --git a/packages/client/lib/commands/XADD.ts b/packages/client/lib/commands/XADD.ts index b681069c729..cb9d0f5fad8 100644 --- a/packages/client/lib/commands/XADD.ts +++ b/packages/client/lib/commands/XADD.ts @@ -1,4 +1,6 @@ -import { RedisArgument, BlobStringReply, Command, CommandArguments } from '../RESP/types'; +import { CommandParser } from '../client/parser'; +import { RedisArgument, BlobStringReply, Command } from '../RESP/types'; +import { Tail } from './generic-transformers'; export interface XAddOptions { TRIM?: { @@ -9,47 +11,47 @@ export interface XAddOptions { }; } -export function pushXAddArguments( - args: CommandArguments, +export function parseXAddArguments( + optional: RedisArgument | undefined, + parser: CommandParser, + key: RedisArgument, id: RedisArgument, message: Record, options?: XAddOptions ) { + parser.push('XADD'); + parser.pushKey(key); + if (optional) { + parser.push(optional); + } + if (options?.TRIM) { if (options.TRIM.strategy) { - args.push(options.TRIM.strategy); + parser.push(options.TRIM.strategy); } if (options.TRIM.strategyModifier) { - args.push(options.TRIM.strategyModifier); + parser.push(options.TRIM.strategyModifier); } - args.push(options.TRIM.threshold.toString()); + parser.push(options.TRIM.threshold.toString()); if (options.TRIM.limit) { - args.push('LIMIT', options.TRIM.limit.toString()); + parser.push('LIMIT', options.TRIM.limit.toString()); } } - args.push(id); + parser.push(id); for (const [key, value] of Object.entries(message)) { - args.push(key, value); + parser.push(key, value); } - - return args; } export default { - FIRST_KEY_INDEX: 1, IS_READ_ONLY: false, - transformArguments( - key: RedisArgument, - id: RedisArgument, - message: Record, - options?: XAddOptions - ) { - return pushXAddArguments(['XADD', key], id, message, options); + parseCommand(...args: Tail>) { + return parseXAddArguments(undefined, ...args); }, transformReply: undefined as unknown as () => BlobStringReply } as const satisfies Command; diff --git a/packages/client/lib/commands/XADD_NOMKSTREAM.spec.ts b/packages/client/lib/commands/XADD_NOMKSTREAM.spec.ts index a3dd5602a3b..97927f212ff 100644 --- a/packages/client/lib/commands/XADD_NOMKSTREAM.spec.ts +++ b/packages/client/lib/commands/XADD_NOMKSTREAM.spec.ts @@ -1,6 +1,7 @@ import { strict as assert } from 'node:assert'; import testUtils, { GLOBAL } from '../test-utils'; import XADD_NOMKSTREAM from './XADD_NOMKSTREAM'; +import { parseArgs } from './generic-transformers'; describe('XADD NOMKSTREAM', () => { testUtils.isVersionGreaterThanHook([6, 2]); @@ -8,7 +9,7 @@ describe('XADD NOMKSTREAM', () => { describe('transformArguments', () => { it('single field', () => { assert.deepEqual( - XADD_NOMKSTREAM.transformArguments('key', '*', { + parseArgs(XADD_NOMKSTREAM, 'key', '*', { field: 'value' }), ['XADD', 'key', 'NOMKSTREAM', '*', 'field', 'value'] @@ -17,7 +18,7 @@ describe('XADD NOMKSTREAM', () => { it('multiple fields', () => { assert.deepEqual( - XADD_NOMKSTREAM.transformArguments('key', '*', { + parseArgs(XADD_NOMKSTREAM, 'key', '*', { '1': 'I', '2': 'II' }), @@ -27,7 +28,7 @@ describe('XADD NOMKSTREAM', () => { it('with TRIM', () => { assert.deepEqual( - XADD_NOMKSTREAM.transformArguments('key', '*', { + parseArgs(XADD_NOMKSTREAM, 'key', '*', { field: 'value' }, { TRIM: { @@ -40,7 +41,7 @@ describe('XADD NOMKSTREAM', () => { it('with TRIM.strategy', () => { assert.deepEqual( - XADD_NOMKSTREAM.transformArguments('key', '*', { + parseArgs(XADD_NOMKSTREAM, 'key', '*', { field: 'value' }, { TRIM: { @@ -54,7 +55,7 @@ describe('XADD NOMKSTREAM', () => { it('with TRIM.strategyModifier', () => { assert.deepEqual( - XADD_NOMKSTREAM.transformArguments('key', '*', { + parseArgs(XADD_NOMKSTREAM, 'key', '*', { field: 'value' }, { TRIM: { @@ -68,7 +69,7 @@ describe('XADD NOMKSTREAM', () => { it('with TRIM.limit', () => { assert.deepEqual( - XADD_NOMKSTREAM.transformArguments('key', '*', { + parseArgs(XADD_NOMKSTREAM, 'key', '*', { field: 'value' }, { TRIM: { diff --git a/packages/client/lib/commands/XADD_NOMKSTREAM.ts b/packages/client/lib/commands/XADD_NOMKSTREAM.ts index 65e7dd566e3..9d33374be4a 100644 --- a/packages/client/lib/commands/XADD_NOMKSTREAM.ts +++ b/packages/client/lib/commands/XADD_NOMKSTREAM.ts @@ -1,16 +1,11 @@ -import { RedisArgument, BlobStringReply, NullReply, Command } from '../RESP/types'; -import { XAddOptions, pushXAddArguments } from './XADD'; +import { BlobStringReply, NullReply, Command } from '../RESP/types'; +import { Tail } from './generic-transformers'; +import { parseXAddArguments } from './XADD'; export default { - FIRST_KEY_INDEX: 1, IS_READ_ONLY: false, - transformArguments( - key: RedisArgument, - id: RedisArgument, - message: Record, - options?: XAddOptions - ) { - return pushXAddArguments(['XADD', key, 'NOMKSTREAM'], id, message, options); + parseCommand(...args: Tail>) { + return parseXAddArguments('NOMKSTREAM', ...args); }, transformReply: undefined as unknown as () => BlobStringReply | NullReply } as const satisfies Command; diff --git a/packages/client/lib/commands/XAUTOCLAIM.spec.ts b/packages/client/lib/commands/XAUTOCLAIM.spec.ts index 256c58cc4d6..58b09a63e78 100644 --- a/packages/client/lib/commands/XAUTOCLAIM.spec.ts +++ b/packages/client/lib/commands/XAUTOCLAIM.spec.ts @@ -1,6 +1,7 @@ import { strict as assert } from 'node:assert'; import testUtils, { GLOBAL } from '../test-utils'; import XAUTOCLAIM from './XAUTOCLAIM'; +import { parseArgs } from './generic-transformers'; describe('XAUTOCLAIM', () => { testUtils.isVersionGreaterThanHook([6, 2]); @@ -8,14 +9,14 @@ describe('XAUTOCLAIM', () => { describe('transformArguments', () => { it('simple', () => { assert.deepEqual( - XAUTOCLAIM.transformArguments('key', 'group', 'consumer', 1, '0-0'), + parseArgs(XAUTOCLAIM, 'key', 'group', 'consumer', 1, '0-0'), ['XAUTOCLAIM', 'key', 'group', 'consumer', '1', '0-0'] ); }); it('with COUNT', () => { assert.deepEqual( - XAUTOCLAIM.transformArguments('key', 'group', 'consumer', 1, '0-0', { + parseArgs(XAUTOCLAIM, 'key', 'group', 'consumer', 1, '0-0', { COUNT: 1 }), ['XAUTOCLAIM', 'key', 'group', 'consumer', '1', '0-0', 'COUNT', '1'] diff --git a/packages/client/lib/commands/XAUTOCLAIM.ts b/packages/client/lib/commands/XAUTOCLAIM.ts index 7d33142de32..19b4f63a2df 100644 --- a/packages/client/lib/commands/XAUTOCLAIM.ts +++ b/packages/client/lib/commands/XAUTOCLAIM.ts @@ -1,3 +1,4 @@ +import { CommandParser } from '../client/parser'; import { RedisArgument, TuplesReply, BlobStringReply, ArrayReply, NullReply, UnwrapReply, Command, TypeMapping } from '../RESP/types'; import { StreamMessageRawReply, transformStreamMessageNullReply } from './generic-transformers'; @@ -12,9 +13,9 @@ export type XAutoClaimRawReply = TuplesReply<[ ]>; export default { - FIRST_KEY_INDEX: 1, IS_READ_ONLY: false, - transformArguments( + parseCommand( + parser: CommandParser, key: RedisArgument, group: RedisArgument, consumer: RedisArgument, @@ -22,20 +23,13 @@ export default { start: RedisArgument, options?: XAutoClaimOptions ) { - const args = [ - 'XAUTOCLAIM', - key, - group, - consumer, - minIdleTime.toString(), - start - ]; + parser.push('XAUTOCLAIM'); + parser.pushKey(key); + parser.push(group, consumer, minIdleTime.toString(), start); if (options?.COUNT) { - args.push('COUNT', options.COUNT.toString()); + parser.push('COUNT', options.COUNT.toString()); } - - return args; }, transformReply(reply: UnwrapReply, preserve?: any, typeMapping?: TypeMapping) { return { diff --git a/packages/client/lib/commands/XAUTOCLAIM_JUSTID.spec.ts b/packages/client/lib/commands/XAUTOCLAIM_JUSTID.spec.ts index 96ceb1d8116..78911657086 100644 --- a/packages/client/lib/commands/XAUTOCLAIM_JUSTID.spec.ts +++ b/packages/client/lib/commands/XAUTOCLAIM_JUSTID.spec.ts @@ -1,13 +1,14 @@ import { strict as assert } from 'node:assert'; import testUtils, { GLOBAL } from '../test-utils'; import XAUTOCLAIM_JUSTID from './XAUTOCLAIM_JUSTID'; +import { parseArgs } from './generic-transformers'; describe('XAUTOCLAIM JUSTID', () => { testUtils.isVersionGreaterThanHook([6, 2]); it('transformArguments', () => { assert.deepEqual( - XAUTOCLAIM_JUSTID.transformArguments('key', 'group', 'consumer', 1, '0-0'), + parseArgs(XAUTOCLAIM_JUSTID, 'key', 'group', 'consumer', 1, '0-0'), ['XAUTOCLAIM', 'key', 'group', 'consumer', '1', '0-0', 'JUSTID'] ); }); diff --git a/packages/client/lib/commands/XAUTOCLAIM_JUSTID.ts b/packages/client/lib/commands/XAUTOCLAIM_JUSTID.ts index e2832f23536..c0ebe83748e 100644 --- a/packages/client/lib/commands/XAUTOCLAIM_JUSTID.ts +++ b/packages/client/lib/commands/XAUTOCLAIM_JUSTID.ts @@ -8,12 +8,11 @@ type XAutoClaimJustIdRawReply = TuplesReply<[ ]>; export default { - FIRST_KEY_INDEX: XAUTOCLAIM.FIRST_KEY_INDEX, IS_READ_ONLY: XAUTOCLAIM.IS_READ_ONLY, - transformArguments(...args: Parameters) { - const redisArgs = XAUTOCLAIM.transformArguments(...args); - redisArgs.push('JUSTID'); - return redisArgs; + parseCommand(...args: Parameters) { + const parser = args[0]; + XAUTOCLAIM.parseCommand(...args); + parser.push('JUSTID'); }, transformReply(reply: UnwrapReply) { return { diff --git a/packages/client/lib/commands/XCLAIM.spec.ts b/packages/client/lib/commands/XCLAIM.spec.ts index e8fde2e1a1a..90768509225 100644 --- a/packages/client/lib/commands/XCLAIM.spec.ts +++ b/packages/client/lib/commands/XCLAIM.spec.ts @@ -1,26 +1,27 @@ import { strict as assert } from 'node:assert'; import testUtils, { GLOBAL } from '../test-utils'; import XCLAIM from './XCLAIM'; +import { parseArgs } from './generic-transformers'; describe('XCLAIM', () => { describe('transformArguments', () => { it('single id (string)', () => { assert.deepEqual( - XCLAIM.transformArguments('key', 'group', 'consumer', 1, '0-0'), + parseArgs(XCLAIM, 'key', 'group', 'consumer', 1, '0-0'), ['XCLAIM', 'key', 'group', 'consumer', '1', '0-0'] ); }); it('multiple ids (array)', () => { assert.deepEqual( - XCLAIM.transformArguments('key', 'group', 'consumer', 1, ['0-0', '1-0']), + parseArgs(XCLAIM, 'key', 'group', 'consumer', 1, ['0-0', '1-0']), ['XCLAIM', 'key', 'group', 'consumer', '1', '0-0', '1-0'] ); }); it('with IDLE', () => { assert.deepEqual( - XCLAIM.transformArguments('key', 'group', 'consumer', 1, '0-0', { + parseArgs(XCLAIM, 'key', 'group', 'consumer', 1, '0-0', { IDLE: 1 }), ['XCLAIM', 'key', 'group', 'consumer', '1', '0-0', 'IDLE', '1'] @@ -30,7 +31,7 @@ describe('XCLAIM', () => { describe('with TIME', () => { it('number', () => { assert.deepEqual( - XCLAIM.transformArguments('key', 'group', 'consumer', 1, '0-0', { + parseArgs(XCLAIM, 'key', 'group', 'consumer', 1, '0-0', { TIME: 1 }), ['XCLAIM', 'key', 'group', 'consumer', '1', '0-0', 'TIME', '1'] @@ -40,7 +41,7 @@ describe('XCLAIM', () => { it('Date', () => { const d = new Date(); assert.deepEqual( - XCLAIM.transformArguments('key', 'group', 'consumer', 1, '0-0', { + parseArgs(XCLAIM, 'key', 'group', 'consumer', 1, '0-0', { TIME: d }), ['XCLAIM', 'key', 'group', 'consumer', '1', '0-0', 'TIME', d.getTime().toString()] @@ -50,7 +51,7 @@ describe('XCLAIM', () => { it('with RETRYCOUNT', () => { assert.deepEqual( - XCLAIM.transformArguments('key', 'group', 'consumer', 1, '0-0', { + parseArgs(XCLAIM, 'key', 'group', 'consumer', 1, '0-0', { RETRYCOUNT: 1 }), ['XCLAIM', 'key', 'group', 'consumer', '1', '0-0', 'RETRYCOUNT', '1'] @@ -59,7 +60,7 @@ describe('XCLAIM', () => { it('with FORCE', () => { assert.deepEqual( - XCLAIM.transformArguments('key', 'group', 'consumer', 1, '0-0', { + parseArgs(XCLAIM, 'key', 'group', 'consumer', 1, '0-0', { FORCE: true }), ['XCLAIM', 'key', 'group', 'consumer', '1', '0-0', 'FORCE'] @@ -68,7 +69,7 @@ describe('XCLAIM', () => { it('with LASTID', () => { assert.deepEqual( - XCLAIM.transformArguments('key', 'group', 'consumer', 1, '0-0', { + parseArgs(XCLAIM, 'key', 'group', 'consumer', 1, '0-0', { LASTID: '0-0' }), ['XCLAIM', 'key', 'group', 'consumer', '1', '0-0', 'LASTID', '0-0'] @@ -77,7 +78,7 @@ describe('XCLAIM', () => { it('with IDLE, TIME, RETRYCOUNT, FORCE, LASTID', () => { assert.deepEqual( - XCLAIM.transformArguments('key', 'group', 'consumer', 1, '0-0', { + parseArgs(XCLAIM, 'key', 'group', 'consumer', 1, '0-0', { IDLE: 1, TIME: 1, RETRYCOUNT: 1, diff --git a/packages/client/lib/commands/XCLAIM.ts b/packages/client/lib/commands/XCLAIM.ts index eb9c0b325e1..598b1b17ba4 100644 --- a/packages/client/lib/commands/XCLAIM.ts +++ b/packages/client/lib/commands/XCLAIM.ts @@ -1,5 +1,6 @@ +import { CommandParser } from '../client/parser'; import { RedisArgument, ArrayReply, NullReply, UnwrapReply, Command, TypeMapping } from '../RESP/types'; -import { RedisVariadicArgument, pushVariadicArguments, StreamMessageRawReply, transformStreamMessageNullReply } from './generic-transformers'; +import { RedisVariadicArgument, StreamMessageRawReply, transformStreamMessageNullReply } from './generic-transformers'; export interface XClaimOptions { IDLE?: number; @@ -10,9 +11,9 @@ export interface XClaimOptions { } export default { - FIRST_KEY_INDEX: 1, IS_READ_ONLY: false, - transformArguments( + parseCommand( + parser: CommandParser, key: RedisArgument, group: RedisArgument, consumer: RedisArgument, @@ -20,35 +21,33 @@ export default { id: RedisVariadicArgument, options?: XClaimOptions ) { - const args = pushVariadicArguments( - ['XCLAIM', key, group, consumer, minIdleTime.toString()], - id - ); + parser.push('XCLAIM'); + parser.pushKey(key); + parser.push(group, consumer, minIdleTime.toString()); + parser.pushVariadic(id); if (options?.IDLE !== undefined) { - args.push('IDLE', options.IDLE.toString()); + parser.push('IDLE', options.IDLE.toString()); } if (options?.TIME !== undefined) { - args.push( + parser.push( 'TIME', (options.TIME instanceof Date ? options.TIME.getTime() : options.TIME).toString() ); } if (options?.RETRYCOUNT !== undefined) { - args.push('RETRYCOUNT', options.RETRYCOUNT.toString()); + parser.push('RETRYCOUNT', options.RETRYCOUNT.toString()); } if (options?.FORCE) { - args.push('FORCE'); + parser.push('FORCE'); } if (options?.LASTID !== undefined) { - args.push('LASTID', options.LASTID); + parser.push('LASTID', options.LASTID); } - - return args; }, transformReply( reply: UnwrapReply>, diff --git a/packages/client/lib/commands/XCLAIM_JUSTID.spec.ts b/packages/client/lib/commands/XCLAIM_JUSTID.spec.ts index 6b580ac3c1b..d7bf9fdc70c 100644 --- a/packages/client/lib/commands/XCLAIM_JUSTID.spec.ts +++ b/packages/client/lib/commands/XCLAIM_JUSTID.spec.ts @@ -1,11 +1,12 @@ import { strict as assert } from 'node:assert'; import testUtils, { GLOBAL } from '../test-utils'; import XCLAIM_JUSTID from './XCLAIM_JUSTID'; +import { parseArgs } from './generic-transformers'; describe('XCLAIM JUSTID', () => { it('transformArguments', () => { assert.deepEqual( - XCLAIM_JUSTID.transformArguments('key', 'group', 'consumer', 1, '0-0'), + parseArgs(XCLAIM_JUSTID, 'key', 'group', 'consumer', 1, '0-0'), ['XCLAIM', 'key', 'group', 'consumer', '1', '0-0', 'JUSTID'] ); }); diff --git a/packages/client/lib/commands/XCLAIM_JUSTID.ts b/packages/client/lib/commands/XCLAIM_JUSTID.ts index 6200c9106e1..91be5aafbb4 100644 --- a/packages/client/lib/commands/XCLAIM_JUSTID.ts +++ b/packages/client/lib/commands/XCLAIM_JUSTID.ts @@ -2,12 +2,11 @@ import { ArrayReply, BlobStringReply, Command } from '../RESP/types'; import XCLAIM from './XCLAIM'; export default { - FIRST_KEY_INDEX: XCLAIM.FIRST_KEY_INDEX, IS_READ_ONLY: XCLAIM.IS_READ_ONLY, - transformArguments(...args: Parameters) { - const redisArgs = XCLAIM.transformArguments(...args); - redisArgs.push('JUSTID'); - return redisArgs; + parseCommand(...args: Parameters) { + const parser = args[0]; + XCLAIM.parseCommand(...args); + parser.push('JUSTID'); }, transformReply: undefined as unknown as () => ArrayReply } as const satisfies Command; diff --git a/packages/client/lib/commands/XDEL.spec.ts b/packages/client/lib/commands/XDEL.spec.ts index 15875d3b7b3..510168bb765 100644 --- a/packages/client/lib/commands/XDEL.spec.ts +++ b/packages/client/lib/commands/XDEL.spec.ts @@ -1,19 +1,20 @@ import { strict as assert } from 'node:assert'; import testUtils, { GLOBAL } from '../test-utils'; import XDEL from './XDEL'; +import { parseArgs } from './generic-transformers'; describe('XDEL', () => { describe('transformArguments', () => { it('string', () => { assert.deepEqual( - XDEL.transformArguments('key', '0-0'), + parseArgs(XDEL, 'key', '0-0'), ['XDEL', 'key', '0-0'] ); }); it('array', () => { assert.deepEqual( - XDEL.transformArguments('key', ['0-0', '1-0']), + parseArgs(XDEL, 'key', ['0-0', '1-0']), ['XDEL', 'key', '0-0', '1-0'] ); }); diff --git a/packages/client/lib/commands/XDEL.ts b/packages/client/lib/commands/XDEL.ts index acc9198c2b8..ee385203ce5 100644 --- a/packages/client/lib/commands/XDEL.ts +++ b/packages/client/lib/commands/XDEL.ts @@ -1,11 +1,13 @@ +import { CommandParser } from '../client/parser'; import { RedisArgument, NumberReply, Command } from '../RESP/types'; -import { RedisVariadicArgument, pushVariadicArguments } from './generic-transformers'; +import { RedisVariadicArgument } from './generic-transformers'; export default { - FIRST_KEY_INDEX: 1, IS_READ_ONLY: false, - transformArguments(key: RedisArgument, id: RedisVariadicArgument) { - return pushVariadicArguments(['XDEL', key], id); + parseCommand(parser: CommandParser, key: RedisArgument, id: RedisVariadicArgument) { + parser.push('XDEL'); + parser.pushKey(key); + parser.pushVariadic(id); }, transformReply: undefined as unknown as () => NumberReply } as const satisfies Command; diff --git a/packages/client/lib/commands/XGROUP_CREATE.spec.ts b/packages/client/lib/commands/XGROUP_CREATE.spec.ts index 5c9071289c9..7c9d6298c6b 100644 --- a/packages/client/lib/commands/XGROUP_CREATE.spec.ts +++ b/packages/client/lib/commands/XGROUP_CREATE.spec.ts @@ -1,19 +1,20 @@ import { strict as assert } from 'node:assert'; import testUtils, { GLOBAL } from '../test-utils'; import XGROUP_CREATE from './XGROUP_CREATE'; +import { parseArgs } from './generic-transformers'; describe('XGROUP CREATE', () => { describe('transformArguments', () => { it('simple', () => { assert.deepEqual( - XGROUP_CREATE.transformArguments('key', 'group', '$'), + parseArgs(XGROUP_CREATE, 'key', 'group', '$'), ['XGROUP', 'CREATE', 'key', 'group', '$'] ); }); it('with MKSTREAM', () => { assert.deepEqual( - XGROUP_CREATE.transformArguments('key', 'group', '$', { + parseArgs(XGROUP_CREATE, 'key', 'group', '$', { MKSTREAM: true }), ['XGROUP', 'CREATE', 'key', 'group', '$', 'MKSTREAM'] @@ -22,7 +23,7 @@ describe('XGROUP CREATE', () => { it('with ENTRIESREAD', () => { assert.deepEqual( - XGROUP_CREATE.transformArguments('key', 'group', '$', { + parseArgs(XGROUP_CREATE, 'key', 'group', '$', { ENTRIESREAD: 1 }), ['XGROUP', 'CREATE', 'key', 'group', '$', 'ENTRIESREAD', '1'] diff --git a/packages/client/lib/commands/XGROUP_CREATE.ts b/packages/client/lib/commands/XGROUP_CREATE.ts index a04fcbeb044..e91186efe29 100644 --- a/packages/client/lib/commands/XGROUP_CREATE.ts +++ b/packages/client/lib/commands/XGROUP_CREATE.ts @@ -1,3 +1,4 @@ +import { CommandParser } from '../client/parser'; import { RedisArgument, SimpleStringReply, Command } from '../RESP/types'; export interface XGroupCreateOptions { @@ -9,25 +10,25 @@ export interface XGroupCreateOptions { } export default { - FIRST_KEY_INDEX: 2, IS_READ_ONLY: false, - transformArguments( + parseCommand( + parser: CommandParser, key: RedisArgument, group: RedisArgument, id: RedisArgument, options?: XGroupCreateOptions ) { - const args = ['XGROUP', 'CREATE', key, group, id]; + parser.push('XGROUP', 'CREATE'); + parser.pushKey(key); + parser.push(group, id); if (options?.MKSTREAM) { - args.push('MKSTREAM'); + parser.push('MKSTREAM'); } if (options?.ENTRIESREAD) { - args.push('ENTRIESREAD', options.ENTRIESREAD.toString()); + parser.push('ENTRIESREAD', options.ENTRIESREAD.toString()); } - - return args; }, transformReply: undefined as unknown as () => SimpleStringReply<'OK'> } as const satisfies Command; diff --git a/packages/client/lib/commands/XGROUP_CREATECONSUMER.spec.ts b/packages/client/lib/commands/XGROUP_CREATECONSUMER.spec.ts index 3c3ecbda0a7..eb749073d35 100644 --- a/packages/client/lib/commands/XGROUP_CREATECONSUMER.spec.ts +++ b/packages/client/lib/commands/XGROUP_CREATECONSUMER.spec.ts @@ -1,13 +1,14 @@ import { strict as assert } from 'node:assert'; import testUtils, { GLOBAL } from '../test-utils'; import XGROUP_CREATECONSUMER from './XGROUP_CREATECONSUMER'; +import { parseArgs } from './generic-transformers'; describe('XGROUP CREATECONSUMER', () => { testUtils.isVersionGreaterThanHook([6, 2]); it('transformArguments', () => { assert.deepEqual( - XGROUP_CREATECONSUMER.transformArguments('key', 'group', 'consumer'), + parseArgs(XGROUP_CREATECONSUMER, 'key', 'group', 'consumer'), ['XGROUP', 'CREATECONSUMER', 'key', 'group', 'consumer'] ); }); diff --git a/packages/client/lib/commands/XGROUP_CREATECONSUMER.ts b/packages/client/lib/commands/XGROUP_CREATECONSUMER.ts index 8fd21ca60de..906bc4c683e 100644 --- a/packages/client/lib/commands/XGROUP_CREATECONSUMER.ts +++ b/packages/client/lib/commands/XGROUP_CREATECONSUMER.ts @@ -1,14 +1,17 @@ +import { CommandParser } from '../client/parser'; import { RedisArgument, Command, NumberReply } from '../RESP/types'; export default { - FIRST_KEY_INDEX: 2, IS_READ_ONLY: false, - transformArguments( + parseCommand( + parser: CommandParser, key: RedisArgument, group: RedisArgument, consumer: RedisArgument ) { - return ['XGROUP', 'CREATECONSUMER', key, group, consumer]; + parser.push('XGROUP', 'CREATECONSUMER'); + parser.pushKey(key); + parser.push(group, consumer); }, transformReply: undefined as unknown as () => NumberReply } as const satisfies Command; diff --git a/packages/client/lib/commands/XGROUP_DELCONSUMER.spec.ts b/packages/client/lib/commands/XGROUP_DELCONSUMER.spec.ts index afc524eef86..fabef789d78 100644 --- a/packages/client/lib/commands/XGROUP_DELCONSUMER.spec.ts +++ b/packages/client/lib/commands/XGROUP_DELCONSUMER.spec.ts @@ -1,11 +1,12 @@ import { strict as assert } from 'node:assert'; import testUtils, { GLOBAL } from '../test-utils'; import XGROUP_DELCONSUMER from './XGROUP_DELCONSUMER'; +import { parseArgs } from './generic-transformers'; describe('XGROUP DELCONSUMER', () => { it('transformArguments', () => { assert.deepEqual( - XGROUP_DELCONSUMER.transformArguments('key', 'group', 'consumer'), + parseArgs(XGROUP_DELCONSUMER, 'key', 'group', 'consumer'), ['XGROUP', 'DELCONSUMER', 'key', 'group', 'consumer'] ); }); diff --git a/packages/client/lib/commands/XGROUP_DELCONSUMER.ts b/packages/client/lib/commands/XGROUP_DELCONSUMER.ts index 53007270e05..360d7e06cae 100644 --- a/packages/client/lib/commands/XGROUP_DELCONSUMER.ts +++ b/packages/client/lib/commands/XGROUP_DELCONSUMER.ts @@ -1,14 +1,17 @@ +import { CommandParser } from '../client/parser'; import { RedisArgument, NumberReply, Command } from '../RESP/types'; export default { - FIRST_KEY_INDEX: 2, IS_READ_ONLY: false, - transformArguments( + parseCommand( + parser: CommandParser, key: RedisArgument, group: RedisArgument, consumer: RedisArgument ) { - return ['XGROUP', 'DELCONSUMER', key, group, consumer]; + parser.push('XGROUP', 'DELCONSUMER'); + parser.pushKey(key); + parser.push(group, consumer); }, transformReply: undefined as unknown as () => NumberReply } as const satisfies Command; diff --git a/packages/client/lib/commands/XGROUP_DESTROY.spec.ts b/packages/client/lib/commands/XGROUP_DESTROY.spec.ts index 6ec90834518..8277c66d3f6 100644 --- a/packages/client/lib/commands/XGROUP_DESTROY.spec.ts +++ b/packages/client/lib/commands/XGROUP_DESTROY.spec.ts @@ -1,11 +1,12 @@ import { strict as assert } from 'node:assert'; import testUtils, { GLOBAL } from '../test-utils'; import XGROUP_DESTROY from './XGROUP_DESTROY'; +import { parseArgs } from './generic-transformers'; describe('XGROUP DESTROY', () => { it('transformArguments', () => { assert.deepEqual( - XGROUP_DESTROY.transformArguments('key', 'group'), + parseArgs(XGROUP_DESTROY, 'key', 'group'), ['XGROUP', 'DESTROY', 'key', 'group'] ); }); diff --git a/packages/client/lib/commands/XGROUP_DESTROY.ts b/packages/client/lib/commands/XGROUP_DESTROY.ts index 6c14d9ae2bd..9112f1bcd79 100644 --- a/packages/client/lib/commands/XGROUP_DESTROY.ts +++ b/packages/client/lib/commands/XGROUP_DESTROY.ts @@ -1,13 +1,12 @@ +import { CommandParser } from '../client/parser'; import { RedisArgument, NumberReply, Command } from '../RESP/types'; export default { - FIRST_KEY_INDEX: 2, IS_READ_ONLY: false, - transformArguments( - key: RedisArgument, - group: RedisArgument - ) { - return ['XGROUP', 'DESTROY', key, group]; + parseCommand(parser: CommandParser, key: RedisArgument, group: RedisArgument) { + parser.push('XGROUP', 'DESTROY'); + parser.pushKey(key); + parser.push(group); }, transformReply: undefined as unknown as () => NumberReply } as const satisfies Command; diff --git a/packages/client/lib/commands/XGROUP_SETID.spec.ts b/packages/client/lib/commands/XGROUP_SETID.spec.ts index 891a796d14d..6ea0dd79c37 100644 --- a/packages/client/lib/commands/XGROUP_SETID.spec.ts +++ b/packages/client/lib/commands/XGROUP_SETID.spec.ts @@ -1,11 +1,12 @@ import { strict as assert } from 'node:assert'; import testUtils, { GLOBAL } from '../test-utils'; import XGROUP_SETID from './XGROUP_SETID'; +import { parseArgs } from './generic-transformers'; describe('XGROUP SETID', () => { it('transformArguments', () => { assert.deepEqual( - XGROUP_SETID.transformArguments('key', 'group', '0'), + parseArgs(XGROUP_SETID, 'key', 'group', '0'), ['XGROUP', 'SETID', 'key', 'group', '0'] ); }); diff --git a/packages/client/lib/commands/XGROUP_SETID.ts b/packages/client/lib/commands/XGROUP_SETID.ts index a23b4144335..5b0ddcc32ad 100644 --- a/packages/client/lib/commands/XGROUP_SETID.ts +++ b/packages/client/lib/commands/XGROUP_SETID.ts @@ -1,3 +1,4 @@ +import { CommandParser } from '../client/parser'; import { RedisArgument, SimpleStringReply, Command } from '../RESP/types'; export interface XGroupSetIdOptions { @@ -6,21 +7,21 @@ export interface XGroupSetIdOptions { } export default { - FIRST_KEY_INDEX: 2, IS_READ_ONLY: false, - transformArguments( + parseCommand( + parser: CommandParser, key: RedisArgument, group: RedisArgument, id: RedisArgument, options?: XGroupSetIdOptions ) { - const args = ['XGROUP', 'SETID', key, group, id]; + parser.push('XGROUP', 'SETID'); + parser.pushKey(key); + parser.push(group, id); if (options?.ENTRIESREAD) { - args.push('ENTRIESREAD', options.ENTRIESREAD.toString()); + parser.push('ENTRIESREAD', options.ENTRIESREAD.toString()); } - - return args; }, transformReply: undefined as unknown as () => SimpleStringReply<'OK'> } as const satisfies Command; diff --git a/packages/client/lib/commands/XINFO_CONSUMERS.spec.ts b/packages/client/lib/commands/XINFO_CONSUMERS.spec.ts index 86abdbb1493..b1f245dbf18 100644 --- a/packages/client/lib/commands/XINFO_CONSUMERS.spec.ts +++ b/packages/client/lib/commands/XINFO_CONSUMERS.spec.ts @@ -1,11 +1,12 @@ import { strict as assert } from 'node:assert'; import testUtils, { GLOBAL } from '../test-utils'; import XINFO_CONSUMERS from './XINFO_CONSUMERS'; +import { parseArgs } from './generic-transformers'; describe('XINFO CONSUMERS', () => { it('transformArguments', () => { assert.deepEqual( - XINFO_CONSUMERS.transformArguments('key', 'group'), + parseArgs(XINFO_CONSUMERS, 'key', 'group'), ['XINFO', 'CONSUMERS', 'key', 'group'] ); }); diff --git a/packages/client/lib/commands/XINFO_CONSUMERS.ts b/packages/client/lib/commands/XINFO_CONSUMERS.ts index ca0076d6335..310a40d17f3 100644 --- a/packages/client/lib/commands/XINFO_CONSUMERS.ts +++ b/packages/client/lib/commands/XINFO_CONSUMERS.ts @@ -1,3 +1,4 @@ +import { CommandParser } from '../client/parser'; import { RedisArgument, ArrayReply, TuplesToMapReply, BlobStringReply, NumberReply, UnwrapReply, Resp2Reply, Command } from '../RESP/types'; export type XInfoConsumersReply = ArrayReply>; export default { - FIRST_KEY_INDEX: 2, IS_READ_ONLY: true, - transformArguments( - key: RedisArgument, - group: RedisArgument - ) { - return ['XINFO', 'CONSUMERS', key, group]; + parseCommand(parser: CommandParser, key: RedisArgument, group: RedisArgument) { + parser.push('XINFO', 'CONSUMERS'); + parser.pushKey(key); + parser.push(group); }, transformReply: { 2: (reply: UnwrapReply>) => { diff --git a/packages/client/lib/commands/XINFO_GROUPS.spec.ts b/packages/client/lib/commands/XINFO_GROUPS.spec.ts index 1bee02a0e6d..a1196f4957a 100644 --- a/packages/client/lib/commands/XINFO_GROUPS.spec.ts +++ b/packages/client/lib/commands/XINFO_GROUPS.spec.ts @@ -1,11 +1,12 @@ import { strict as assert } from 'node:assert'; import testUtils, { GLOBAL } from '../test-utils'; import XINFO_GROUPS from './XINFO_GROUPS'; +import { parseArgs } from './generic-transformers'; describe('XINFO GROUPS', () => { it('transformArguments', () => { assert.deepEqual( - XINFO_GROUPS.transformArguments('key'), + parseArgs(XINFO_GROUPS, 'key'), ['XINFO', 'GROUPS', 'key'] ); }); diff --git a/packages/client/lib/commands/XINFO_GROUPS.ts b/packages/client/lib/commands/XINFO_GROUPS.ts index 24661ecde84..e7f8874125a 100644 --- a/packages/client/lib/commands/XINFO_GROUPS.ts +++ b/packages/client/lib/commands/XINFO_GROUPS.ts @@ -1,3 +1,4 @@ +import { CommandParser } from '../client/parser'; import { RedisArgument, ArrayReply, TuplesToMapReply, BlobStringReply, NumberReply, NullReply, UnwrapReply, Resp2Reply, Command } from '../RESP/types'; export type XInfoGroupsReply = ArrayReply>; export default { - FIRST_KEY_INDEX: 2, IS_READ_ONLY: true, - transformArguments(key: RedisArgument) { - return ['XINFO', 'GROUPS', key]; + parseCommand(parser: CommandParser, key: RedisArgument) { + parser.push('XINFO', 'GROUPS'); + parser.pushKey(key); }, transformReply: { 2: (reply: UnwrapReply>) => { diff --git a/packages/client/lib/commands/XINFO_STREAM.spec.ts b/packages/client/lib/commands/XINFO_STREAM.spec.ts index 9e6939092e2..7e1829f3059 100644 --- a/packages/client/lib/commands/XINFO_STREAM.spec.ts +++ b/packages/client/lib/commands/XINFO_STREAM.spec.ts @@ -1,11 +1,12 @@ import { strict as assert } from 'node:assert'; import testUtils, { GLOBAL } from '../test-utils'; import XINFO_STREAM from './XINFO_STREAM'; +import { parseArgs } from './generic-transformers'; describe('XINFO STREAM', () => { it('transformArguments', () => { assert.deepEqual( - XINFO_STREAM.transformArguments('key'), + parseArgs(XINFO_STREAM, 'key'), ['XINFO', 'STREAM', 'key'] ); }); diff --git a/packages/client/lib/commands/XINFO_STREAM.ts b/packages/client/lib/commands/XINFO_STREAM.ts index 04721d0ad32..bb102c591bb 100644 --- a/packages/client/lib/commands/XINFO_STREAM.ts +++ b/packages/client/lib/commands/XINFO_STREAM.ts @@ -1,3 +1,4 @@ +import { CommandParser } from '../client/parser'; import { RedisArgument, TuplesToMapReply, BlobStringReply, NumberReply, NullReply, TuplesReply, ArrayReply, UnwrapReply, Command } from '../RESP/types'; import { isNullReply, transformTuplesReply } from './generic-transformers'; @@ -18,10 +19,10 @@ export type XInfoStreamReply = TuplesToMapReply<[ ]>; export default { - FIRST_KEY_INDEX: 2, IS_READ_ONLY: true, - transformArguments(key: RedisArgument) { - return ['XINFO', 'STREAM', key]; + parseCommand(parser: CommandParser, key: RedisArgument) { + parser.push('XINFO', 'STREAM'); + parser.pushKey(key); }, transformReply: { // TODO: is there a "type safe" way to do it? diff --git a/packages/client/lib/commands/XLEN.spec.ts b/packages/client/lib/commands/XLEN.spec.ts index 164a6d6f094..3e22b9aebfa 100644 --- a/packages/client/lib/commands/XLEN.spec.ts +++ b/packages/client/lib/commands/XLEN.spec.ts @@ -1,11 +1,12 @@ import { strict as assert } from 'node:assert'; import testUtils, { GLOBAL } from '../test-utils'; import XLEN from './XLEN'; +import { parseArgs } from './generic-transformers'; describe('XLEN', () => { - it('transformArguments', () => { + it('processCommand', () => { assert.deepEqual( - XLEN.transformArguments('key'), + parseArgs(XLEN, 'key'), ['XLEN', 'key'] ); }); diff --git a/packages/client/lib/commands/XLEN.ts b/packages/client/lib/commands/XLEN.ts index d2ed566a190..39d47187b28 100644 --- a/packages/client/lib/commands/XLEN.ts +++ b/packages/client/lib/commands/XLEN.ts @@ -1,10 +1,12 @@ +import { CommandParser } from '../client/parser'; import { RedisArgument, NumberReply, Command } from '../RESP/types'; export default { - FIRST_KEY_INDEX: 1, + CACHEABLE: true, IS_READ_ONLY: true, - transformArguments(key: RedisArgument) { - return ['XLEN', key]; + parseCommand(parser: CommandParser, key: RedisArgument) { + parser.push('XLEN'); + parser.pushKey(key); }, transformReply: undefined as unknown as () => NumberReply } as const satisfies Command; diff --git a/packages/client/lib/commands/XPENDING.spec.ts b/packages/client/lib/commands/XPENDING.spec.ts index e6600cce383..55cb957fc62 100644 --- a/packages/client/lib/commands/XPENDING.spec.ts +++ b/packages/client/lib/commands/XPENDING.spec.ts @@ -1,12 +1,13 @@ import { strict as assert } from 'node:assert'; import testUtils, { GLOBAL } from '../test-utils'; import XPENDING from './XPENDING'; +import { parseArgs } from './generic-transformers'; describe('XPENDING', () => { describe('transformArguments', () => { it('transformArguments', () => { assert.deepEqual( - XPENDING.transformArguments('key', 'group'), + parseArgs(XPENDING, 'key', 'group'), ['XPENDING', 'key', 'group'] ); }); diff --git a/packages/client/lib/commands/XPENDING.ts b/packages/client/lib/commands/XPENDING.ts index a6ca4f5a774..11c944c61e7 100644 --- a/packages/client/lib/commands/XPENDING.ts +++ b/packages/client/lib/commands/XPENDING.ts @@ -1,3 +1,4 @@ +import { CommandParser } from '../client/parser'; import { RedisArgument, BlobStringReply, NullReply, ArrayReply, TuplesReply, NumberReply, UnwrapReply, Command } from '../RESP/types'; type XPendingRawReply = TuplesReply<[ @@ -11,10 +12,12 @@ type XPendingRawReply = TuplesReply<[ ]>; export default { - FIRST_KEY_INDEX: 1, + CACHEABLE: true, IS_READ_ONLY: true, - transformArguments(key: RedisArgument, group: RedisArgument) { - return ['XPENDING', key, group]; + parseCommand(parser: CommandParser, key: RedisArgument, group: RedisArgument) { + parser.push('XPENDING'); + parser.pushKey(key); + parser.push(group); }, transformReply(reply: UnwrapReply) { const consumers = reply[3] as unknown as UnwrapReply; diff --git a/packages/client/lib/commands/XPENDING_RANGE.spec.ts b/packages/client/lib/commands/XPENDING_RANGE.spec.ts index a66484fd2e6..33cd836f2a9 100644 --- a/packages/client/lib/commands/XPENDING_RANGE.spec.ts +++ b/packages/client/lib/commands/XPENDING_RANGE.spec.ts @@ -1,19 +1,20 @@ import { strict as assert } from 'node:assert'; import testUtils, { GLOBAL } from '../test-utils'; import XPENDING_RANGE from './XPENDING_RANGE'; +import { parseArgs } from './generic-transformers'; describe('XPENDING RANGE', () => { describe('transformArguments', () => { it('simple', () => { assert.deepEqual( - XPENDING_RANGE.transformArguments('key', 'group', '-', '+', 1), + parseArgs(XPENDING_RANGE, 'key', 'group', '-', '+', 1), ['XPENDING', 'key', 'group', '-', '+', '1'] ); }); it('with IDLE', () => { assert.deepEqual( - XPENDING_RANGE.transformArguments('key', 'group', '-', '+', 1, { + parseArgs(XPENDING_RANGE, 'key', 'group', '-', '+', 1, { IDLE: 1, }), ['XPENDING', 'key', 'group', 'IDLE', '1', '-', '+', '1'] @@ -22,7 +23,7 @@ describe('XPENDING RANGE', () => { it('with consumer', () => { assert.deepEqual( - XPENDING_RANGE.transformArguments('key', 'group', '-', '+', 1, { + parseArgs(XPENDING_RANGE, 'key', 'group', '-', '+', 1, { consumer: 'consumer' }), ['XPENDING', 'key', 'group', '-', '+', '1', 'consumer'] @@ -31,7 +32,7 @@ describe('XPENDING RANGE', () => { it('with IDLE, consumer', () => { assert.deepEqual( - XPENDING_RANGE.transformArguments('key', 'group', '-', '+', 1, { + parseArgs(XPENDING_RANGE, 'key', 'group', '-', '+', 1, { IDLE: 1, consumer: 'consumer' }), diff --git a/packages/client/lib/commands/XPENDING_RANGE.ts b/packages/client/lib/commands/XPENDING_RANGE.ts index 60a28e5172d..8d98ffe7f1e 100644 --- a/packages/client/lib/commands/XPENDING_RANGE.ts +++ b/packages/client/lib/commands/XPENDING_RANGE.ts @@ -1,3 +1,4 @@ +import { CommandParser } from '../client/parser'; import { RedisArgument, ArrayReply, TuplesReply, BlobStringReply, NumberReply, UnwrapReply, Command } from '../RESP/types'; export interface XPendingRangeOptions { @@ -13,33 +14,30 @@ type XPendingRangeRawReply = ArrayReply>; export default { - FIRST_KEY_INDEX: 1, + CACHEABLE: true, IS_READ_ONLY: true, - transformArguments( + parseCommand( + parser: CommandParser, key: RedisArgument, group: RedisArgument, start: RedisArgument, end: RedisArgument, count: number, options?: XPendingRangeOptions - ) { - const args = ['XPENDING', key, group]; + ) { + parser.push('XPENDING'); + parser.pushKey(key); + parser.push(group); if (options?.IDLE !== undefined) { - args.push('IDLE', options.IDLE.toString()); + parser.push('IDLE', options.IDLE.toString()); } - args.push( - start, - end, - count.toString() - ); + parser.push(start, end, count.toString()); if (options?.consumer) { - args.push(options.consumer); + parser.push(options.consumer); } - - return args; }, transformReply(reply: UnwrapReply) { return reply.map(pending => { diff --git a/packages/client/lib/commands/XRANGE.spec.ts b/packages/client/lib/commands/XRANGE.spec.ts index ebfe271db86..b111a97aff1 100644 --- a/packages/client/lib/commands/XRANGE.spec.ts +++ b/packages/client/lib/commands/XRANGE.spec.ts @@ -1,19 +1,20 @@ import { strict as assert } from 'node:assert'; import testUtils, { GLOBAL } from '../test-utils'; import XRANGE from './XRANGE'; +import { parseArgs } from './generic-transformers'; describe('XRANGE', () => { describe('transformArguments', () => { it('simple', () => { assert.deepEqual( - XRANGE.transformArguments('key', '-', '+'), + parseArgs(XRANGE, 'key', '-', '+'), ['XRANGE', 'key', '-', '+'] ); }); it('with COUNT', () => { assert.deepEqual( - XRANGE.transformArguments('key', '-', '+', { + parseArgs(XRANGE, 'key', '-', '+', { COUNT: 1 }), ['XRANGE', 'key', '-', '+', 'COUNT', '1'] diff --git a/packages/client/lib/commands/XRANGE.ts b/packages/client/lib/commands/XRANGE.ts index fb65160d810..de6bb6c9b1b 100644 --- a/packages/client/lib/commands/XRANGE.ts +++ b/packages/client/lib/commands/XRANGE.ts @@ -1,3 +1,4 @@ +import { CommandParser } from '../client/parser'; import { RedisArgument, ArrayReply, UnwrapReply, Command, TypeMapping } from '../RESP/types'; import { StreamMessageRawReply, transformStreamMessageReply } from './generic-transformers'; @@ -5,14 +6,12 @@ export interface XRangeOptions { COUNT?: number; } -export function transformXRangeArguments( - command: RedisArgument, - key: RedisArgument, +export function xRangeArguments( start: RedisArgument, end: RedisArgument, options?: XRangeOptions ) { - const args = [command, key, start, end]; + const args = [start, end]; if (options?.COUNT) { args.push('COUNT', options.COUNT.toString()); @@ -22,9 +21,13 @@ export function transformXRangeArguments( } export default { - FIRST_KEY_INDEX: 1, + CACHEABLE: true, IS_READ_ONLY: true, - transformArguments: transformXRangeArguments.bind(undefined, 'XRANGE'), + parseCommand(parser: CommandParser, key: RedisArgument, ...args: Parameters) { + parser.push('XRANGE'); + parser.pushKey(key); + parser.pushVariadic(xRangeArguments(args[0], args[1], args[2])); + }, transformReply( reply: UnwrapReply>, preserve?: any, diff --git a/packages/client/lib/commands/XREAD.spec.ts b/packages/client/lib/commands/XREAD.spec.ts index 09784a56e6d..bb72c96497e 100644 --- a/packages/client/lib/commands/XREAD.spec.ts +++ b/packages/client/lib/commands/XREAD.spec.ts @@ -1,12 +1,13 @@ import { strict as assert } from 'node:assert'; -import testUtils, { GLOBAL } from '../test-utils'; +import testUtils, { GLOBAL, parseFirstKey } from '../test-utils'; import XREAD from './XREAD'; +import { parseArgs } from './generic-transformers'; describe('XREAD', () => { describe('FIRST_KEY_INDEX', () => { it('single stream', () => { assert.equal( - XREAD.FIRST_KEY_INDEX({ + parseFirstKey(XREAD, { key: 'key', id: '' }), @@ -16,7 +17,7 @@ describe('XREAD', () => { it('multiple streams', () => { assert.equal( - XREAD.FIRST_KEY_INDEX([{ + parseFirstKey(XREAD, [{ key: '1', id: '' }, { @@ -31,7 +32,7 @@ describe('XREAD', () => { describe('transformArguments', () => { it('single stream', () => { assert.deepEqual( - XREAD.transformArguments({ + parseArgs(XREAD, { key: 'key', id: '0-0' }), @@ -41,7 +42,7 @@ describe('XREAD', () => { it('multiple streams', () => { assert.deepEqual( - XREAD.transformArguments([{ + parseArgs(XREAD, [{ key: '1', id: '0-0' }, { @@ -54,7 +55,7 @@ describe('XREAD', () => { it('with COUNT', () => { assert.deepEqual( - XREAD.transformArguments({ + parseArgs(XREAD, { key: 'key', id: '0-0' }, { @@ -66,7 +67,7 @@ describe('XREAD', () => { it('with BLOCK', () => { assert.deepEqual( - XREAD.transformArguments({ + parseArgs(XREAD, { key: 'key', id: '0-0' }, { @@ -78,7 +79,7 @@ describe('XREAD', () => { it('with COUNT, BLOCK', () => { assert.deepEqual( - XREAD.transformArguments({ + parseArgs(XREAD, { key: 'key', id: '0-0' }, { @@ -90,7 +91,6 @@ describe('XREAD', () => { }); }); - testUtils.testAll('client.xRead', async client => { const message = { field: 'value' }, [id, reply] = await Promise.all([ diff --git a/packages/client/lib/commands/XREAD.ts b/packages/client/lib/commands/XREAD.ts index 97679376c19..b57fb8f3983 100644 --- a/packages/client/lib/commands/XREAD.ts +++ b/packages/client/lib/commands/XREAD.ts @@ -1,3 +1,4 @@ +import { CommandParser } from '../client/parser'; import { Command, RedisArgument, ReplyUnion } from '../RESP/types'; import { transformStreamsMessagesReplyResp2 } from './generic-transformers'; @@ -8,19 +9,19 @@ export interface XReadStream { export type XReadStreams = Array | XReadStream; -export function pushXReadStreams(args: Array, streams: XReadStreams) { - args.push('STREAMS'); +export function pushXReadStreams(parser: CommandParser, streams: XReadStreams) { + parser.push('STREAMS'); if (Array.isArray(streams)) { - const keysStart = args.length, - idsStart = keysStart + streams.length; for (let i = 0; i < streams.length; i++) { - const stream = streams[i]; - args[keysStart + i] = stream.key; - args[idsStart + i] = stream.id; + parser.pushKey(streams[i].key); + } + for (let i = 0; i < streams.length; i++) { + parser.push(streams[i].id); } } else { - args.push(streams.key, streams.id); + parser.pushKey(streams.key); + parser.push(streams.id); } } @@ -30,24 +31,19 @@ export interface XReadOptions { } export default { - FIRST_KEY_INDEX(streams: XReadStreams) { - return Array.isArray(streams) ? streams[0].key : streams.key; - }, IS_READ_ONLY: true, - transformArguments(streams: XReadStreams, options?: XReadOptions) { - const args: Array = ['XREAD']; + parseCommand(parser: CommandParser, streams: XReadStreams, options?: XReadOptions) { + parser.push('XREAD'); if (options?.COUNT) { - args.push('COUNT', options.COUNT.toString()); + parser.push('COUNT', options.COUNT.toString()); } if (options?.BLOCK !== undefined) { - args.push('BLOCK', options.BLOCK.toString()); + parser.push('BLOCK', options.BLOCK.toString()); } - pushXReadStreams(args, streams); - - return args; + pushXReadStreams(parser, streams); }, transformReply: { 2: transformStreamsMessagesReplyResp2, @@ -55,4 +51,3 @@ export default { }, unstableResp3: true } as const satisfies Command; - diff --git a/packages/client/lib/commands/XREADGROUP.spec.ts b/packages/client/lib/commands/XREADGROUP.spec.ts index 004a48ddbe3..085a67bc9b3 100644 --- a/packages/client/lib/commands/XREADGROUP.spec.ts +++ b/packages/client/lib/commands/XREADGROUP.spec.ts @@ -1,19 +1,20 @@ import { strict as assert } from 'node:assert'; -import testUtils, { GLOBAL } from '../test-utils'; +import testUtils, { GLOBAL, parseFirstKey } from '../test-utils'; import XREADGROUP from './XREADGROUP'; +import { parseArgs } from './generic-transformers'; describe('XREADGROUP', () => { describe('FIRST_KEY_INDEX', () => { it('single stream', () => { assert.equal( - XREADGROUP.FIRST_KEY_INDEX('', '', { key: 'key', id: '' }), + parseFirstKey(XREADGROUP, '', '', { key: 'key', id: '' }), 'key' ); }); it('multiple streams', () => { assert.equal( - XREADGROUP.FIRST_KEY_INDEX('', '', [{ key: '1', id: '' }, { key: '2', id: '' }]), + parseFirstKey(XREADGROUP, '', '', [{ key: '1', id: '' }, { key: '2', id: '' }]), '1' ); }); @@ -22,7 +23,7 @@ describe('XREADGROUP', () => { describe('transformArguments', () => { it('single stream', () => { assert.deepEqual( - XREADGROUP.transformArguments('group', 'consumer', { + parseArgs(XREADGROUP, 'group', 'consumer', { key: 'key', id: '0-0' }), @@ -32,7 +33,7 @@ describe('XREADGROUP', () => { it('multiple streams', () => { assert.deepEqual( - XREADGROUP.transformArguments('group', 'consumer', [{ + parseArgs(XREADGROUP, 'group', 'consumer', [{ key: '1', id: '0-0' }, { @@ -45,7 +46,7 @@ describe('XREADGROUP', () => { it('with COUNT', () => { assert.deepEqual( - XREADGROUP.transformArguments('group', 'consumer', { + parseArgs(XREADGROUP, 'group', 'consumer', { key: 'key', id: '0-0' }, { @@ -57,7 +58,7 @@ describe('XREADGROUP', () => { it('with BLOCK', () => { assert.deepEqual( - XREADGROUP.transformArguments('group', 'consumer', { + parseArgs(XREADGROUP, 'group', 'consumer', { key: 'key', id: '0-0' }, { @@ -69,7 +70,7 @@ describe('XREADGROUP', () => { it('with NOACK', () => { assert.deepEqual( - XREADGROUP.transformArguments('group', 'consumer', { + parseArgs(XREADGROUP, 'group', 'consumer', { key: 'key', id: '0-0' }, { @@ -81,7 +82,7 @@ describe('XREADGROUP', () => { it('with COUNT, BLOCK, NOACK', () => { assert.deepEqual( - XREADGROUP.transformArguments('group', 'consumer', { + parseArgs(XREADGROUP, 'group', 'consumer', { key: 'key', id: '0-0' }, { diff --git a/packages/client/lib/commands/XREADGROUP.ts b/packages/client/lib/commands/XREADGROUP.ts index 296480f9e3a..d0947e19a08 100644 --- a/packages/client/lib/commands/XREADGROUP.ts +++ b/packages/client/lib/commands/XREADGROUP.ts @@ -1,6 +1,7 @@ +import { CommandParser } from '../client/parser'; import { Command, RedisArgument, ReplyUnion } from '../RESP/types'; +import { XReadStreams, pushXReadStreams } from './XREAD'; import { transformStreamsMessagesReplyResp2 } from './generic-transformers'; -import XREAD, { XReadStreams, pushXReadStreams } from './XREAD'; export interface XReadGroupOptions { COUNT?: number; @@ -9,37 +10,29 @@ export interface XReadGroupOptions { } export default { - FIRST_KEY_INDEX( - _group: RedisArgument, - _consumer: RedisArgument, - streams: XReadStreams - ) { - return XREAD.FIRST_KEY_INDEX(streams); - }, IS_READ_ONLY: true, - transformArguments( + parseCommand( + parser: CommandParser, group: RedisArgument, consumer: RedisArgument, streams: XReadStreams, options?: XReadGroupOptions ) { - const args = ['XREADGROUP', 'GROUP', group, consumer]; + parser.push('XREADGROUP', 'GROUP', group, consumer); if (options?.COUNT !== undefined) { - args.push('COUNT', options.COUNT.toString()); + parser.push('COUNT', options.COUNT.toString()); } if (options?.BLOCK !== undefined) { - args.push('BLOCK', options.BLOCK.toString()); + parser.push('BLOCK', options.BLOCK.toString()); } if (options?.NOACK) { - args.push('NOACK'); + parser.push('NOACK'); } - pushXReadStreams(args, streams); - - return args; + pushXReadStreams(parser, streams); }, transformReply: { 2: transformStreamsMessagesReplyResp2, diff --git a/packages/client/lib/commands/XREVRANGE.spec.ts b/packages/client/lib/commands/XREVRANGE.spec.ts index c9f3043af38..9872dc5e9e0 100644 --- a/packages/client/lib/commands/XREVRANGE.spec.ts +++ b/packages/client/lib/commands/XREVRANGE.spec.ts @@ -1,19 +1,20 @@ import { strict as assert } from 'node:assert'; import testUtils, { GLOBAL } from '../test-utils'; import XREVRANGE from './XREVRANGE'; +import { parseArgs } from './generic-transformers'; describe('XREVRANGE', () => { describe('transformArguments', () => { it('simple', () => { assert.deepEqual( - XREVRANGE.transformArguments('key', '-', '+'), + parseArgs(XREVRANGE, 'key', '-', '+'), ['XREVRANGE', 'key', '-', '+'] ); }); it('with COUNT', () => { assert.deepEqual( - XREVRANGE.transformArguments('key', '-', '+', { + parseArgs(XREVRANGE, 'key', '-', '+', { COUNT: 1 }), ['XREVRANGE', 'key', '-', '+', 'COUNT', '1'] diff --git a/packages/client/lib/commands/XREVRANGE.ts b/packages/client/lib/commands/XREVRANGE.ts index 86e4d8abc9e..ddc51082a1e 100644 --- a/packages/client/lib/commands/XREVRANGE.ts +++ b/packages/client/lib/commands/XREVRANGE.ts @@ -1,13 +1,18 @@ -import { Command } from '../RESP/types'; -import XRANGE, { transformXRangeArguments } from './XRANGE'; +import { CommandParser } from '../client/parser'; +import { Command, RedisArgument } from '../RESP/types'; +import XRANGE, { xRangeArguments } from './XRANGE'; export interface XRevRangeOptions { COUNT?: number; } export default { - FIRST_KEY_INDEX: XRANGE.FIRST_KEY_INDEX, + CACHEABLE: XRANGE.CACHEABLE, IS_READ_ONLY: XRANGE.IS_READ_ONLY, - transformArguments: transformXRangeArguments.bind(undefined, 'XREVRANGE'), + parseCommand(parser: CommandParser, key: RedisArgument, ...args: Parameters) { + parser.push('XREVRANGE'); + parser.pushKey(key); + parser.pushVariadic(xRangeArguments(args[0], args[1], args[2])); + }, transformReply: XRANGE.transformReply } as const satisfies Command; diff --git a/packages/client/lib/commands/XSETID.spec.ts b/packages/client/lib/commands/XSETID.spec.ts index 5ebbc943816..b3609695345 100644 --- a/packages/client/lib/commands/XSETID.spec.ts +++ b/packages/client/lib/commands/XSETID.spec.ts @@ -1,19 +1,20 @@ import { strict as assert } from 'node:assert'; import testUtils, { GLOBAL } from '../test-utils'; import XSETID from './XSETID'; +import { parseArgs } from './generic-transformers'; describe('XSETID', () => { describe('transformArguments', () => { it('simple', () => { assert.deepEqual( - XSETID.transformArguments('key', '0-0'), + parseArgs(XSETID, 'key', '0-0'), ['XSETID', 'key', '0-0'] ); }); it('with ENTRIESADDED', () => { assert.deepEqual( - XSETID.transformArguments('key', '0-0', { + parseArgs(XSETID, 'key', '0-0', { ENTRIESADDED: 1 }), ['XSETID', 'key', '0-0', 'ENTRIESADDED', '1'] @@ -22,7 +23,7 @@ describe('XSETID', () => { it('with MAXDELETEDID', () => { assert.deepEqual( - XSETID.transformArguments('key', '0-0', { + parseArgs(XSETID, 'key', '0-0', { MAXDELETEDID: '1-1' }), ['XSETID', 'key', '0-0', 'MAXDELETEDID', '1-1'] diff --git a/packages/client/lib/commands/XSETID.ts b/packages/client/lib/commands/XSETID.ts index a2ac8af0011..c76ac0b23a4 100644 --- a/packages/client/lib/commands/XSETID.ts +++ b/packages/client/lib/commands/XSETID.ts @@ -1,3 +1,4 @@ +import { CommandParser } from '../client/parser'; import { RedisArgument, SimpleStringReply, Command } from '../RESP/types'; export interface XSetIdOptions { /** added in 7.0 */ @@ -7,24 +8,24 @@ export interface XSetIdOptions { } export default { - FIRST_KEY_INDEX: 1, IS_READ_ONLY: false, - transformArguments( + parseCommand( + parser: CommandParser, key: RedisArgument, lastId: RedisArgument, options?: XSetIdOptions ) { - const args = ['XSETID', key, lastId]; + parser.push('XSETID'); + parser.pushKey(key); + parser.push(lastId); if (options?.ENTRIESADDED) { - args.push('ENTRIESADDED', options.ENTRIESADDED.toString()); + parser.push('ENTRIESADDED', options.ENTRIESADDED.toString()); } if (options?.MAXDELETEDID) { - args.push('MAXDELETEDID', options.MAXDELETEDID); + parser.push('MAXDELETEDID', options.MAXDELETEDID); } - - return args; }, transformReply: undefined as unknown as () => SimpleStringReply<'OK'> } as const satisfies Command; diff --git a/packages/client/lib/commands/XTRIM.spec.ts b/packages/client/lib/commands/XTRIM.spec.ts index a5a2fdf23c5..2c31f0fef92 100644 --- a/packages/client/lib/commands/XTRIM.spec.ts +++ b/packages/client/lib/commands/XTRIM.spec.ts @@ -1,19 +1,20 @@ import { strict as assert } from 'node:assert'; import testUtils, { GLOBAL } from '../test-utils'; import XTRIM from './XTRIM'; +import { parseArgs } from './generic-transformers'; describe('XTRIM', () => { describe('transformArguments', () => { it('simple', () => { assert.deepEqual( - XTRIM.transformArguments('key', 'MAXLEN', 1), + parseArgs(XTRIM, 'key', 'MAXLEN', 1), ['XTRIM', 'key', 'MAXLEN', '1'] ); }); it('with strategyModifier', () => { assert.deepEqual( - XTRIM.transformArguments('key', 'MAXLEN', 1, { + parseArgs(XTRIM, 'key', 'MAXLEN', 1, { strategyModifier: '=' }), ['XTRIM', 'key', 'MAXLEN', '=', '1'] @@ -22,7 +23,7 @@ describe('XTRIM', () => { it('with LIMIT', () => { assert.deepEqual( - XTRIM.transformArguments('key', 'MAXLEN', 1, { + parseArgs(XTRIM, 'key', 'MAXLEN', 1, { LIMIT: 1 }), ['XTRIM', 'key', 'MAXLEN', '1', 'LIMIT', '1'] @@ -31,7 +32,7 @@ describe('XTRIM', () => { it('with strategyModifier, LIMIT', () => { assert.deepEqual( - XTRIM.transformArguments('key', 'MAXLEN', 1, { + parseArgs(XTRIM, 'key', 'MAXLEN', 1, { strategyModifier: '=', LIMIT: 1 }), diff --git a/packages/client/lib/commands/XTRIM.ts b/packages/client/lib/commands/XTRIM.ts index 0512323a32a..fb617d8d35a 100644 --- a/packages/client/lib/commands/XTRIM.ts +++ b/packages/client/lib/commands/XTRIM.ts @@ -1,3 +1,4 @@ +import { CommandParser } from '../client/parser'; import { NumberReply, Command, RedisArgument } from '../RESP/types'; export interface XTrimOptions { @@ -7,27 +8,27 @@ export interface XTrimOptions { } export default { - FIRST_KEY_INDEX: 1, IS_READ_ONLY: false, - transformArguments( + parseCommand( + parser: CommandParser, key: RedisArgument, strategy: 'MAXLEN' | 'MINID', threshold: number, options?: XTrimOptions ) { - const args = ['XTRIM', key, strategy]; + parser.push('XTRIM') + parser.pushKey(key); + parser.push(strategy); if (options?.strategyModifier) { - args.push(options.strategyModifier); + parser.push(options.strategyModifier); } - args.push(threshold.toString()); + parser.push(threshold.toString()); if (options?.LIMIT) { - args.push('LIMIT', options.LIMIT.toString()); + parser.push('LIMIT', options.LIMIT.toString()); } - - return args; }, transformReply: undefined as unknown as () => NumberReply } as const satisfies Command; diff --git a/packages/client/lib/commands/ZADD.spec.ts b/packages/client/lib/commands/ZADD.spec.ts index 5f64cb13b92..0e770693e3a 100644 --- a/packages/client/lib/commands/ZADD.spec.ts +++ b/packages/client/lib/commands/ZADD.spec.ts @@ -1,12 +1,13 @@ import { strict as assert } from 'node:assert'; import testUtils, { GLOBAL } from '../test-utils'; import ZADD from './ZADD'; +import { parseArgs } from './generic-transformers'; describe('ZADD', () => { describe('transformArguments', () => { it('single member', () => { assert.deepEqual( - ZADD.transformArguments('key', { + parseArgs(ZADD, 'key', { value: '1', score: 1 }), @@ -16,7 +17,7 @@ describe('ZADD', () => { it('multiple members', () => { assert.deepEqual( - ZADD.transformArguments('key', [{ + parseArgs(ZADD, 'key', [{ value: '1', score: 1 }, { @@ -30,7 +31,7 @@ describe('ZADD', () => { describe('with condition', () => { it('condition property', () => { assert.deepEqual( - ZADD.transformArguments('key', { + parseArgs(ZADD, 'key', { value: '1', score: 1 }, { @@ -42,7 +43,7 @@ describe('ZADD', () => { it('with NX (backwards compatibility)', () => { assert.deepEqual( - ZADD.transformArguments('key', { + parseArgs(ZADD, 'key', { value: '1', score: 1 }, { @@ -54,7 +55,7 @@ describe('ZADD', () => { it('with XX (backwards compatibility)', () => { assert.deepEqual( - ZADD.transformArguments('key', { + parseArgs(ZADD, 'key', { value: '1', score: 1 }, { @@ -68,7 +69,7 @@ describe('ZADD', () => { describe('with comparison', () => { it('with LT', () => { assert.deepEqual( - ZADD.transformArguments('key', { + parseArgs(ZADD, 'key', { value: '1', score: 1 }, { @@ -80,7 +81,7 @@ describe('ZADD', () => { it('with LT (backwards compatibility)', () => { assert.deepEqual( - ZADD.transformArguments('key', { + parseArgs(ZADD, 'key', { value: '1', score: 1 }, { @@ -92,7 +93,7 @@ describe('ZADD', () => { it('with GT (backwards compatibility)', () => { assert.deepEqual( - ZADD.transformArguments('key', { + parseArgs(ZADD, 'key', { value: '1', score: 1 }, { @@ -105,7 +106,7 @@ describe('ZADD', () => { it('with CH', () => { assert.deepEqual( - ZADD.transformArguments('key', { + parseArgs(ZADD, 'key', { value: '1', score: 1 }, { @@ -117,7 +118,7 @@ describe('ZADD', () => { it('with condition, comparison, CH', () => { assert.deepEqual( - ZADD.transformArguments('key', { + parseArgs(ZADD, 'key', { value: '1', score: 1 }, { diff --git a/packages/client/lib/commands/ZADD.ts b/packages/client/lib/commands/ZADD.ts index 0c5602bf988..5ae71a151ba 100644 --- a/packages/client/lib/commands/ZADD.ts +++ b/packages/client/lib/commands/ZADD.ts @@ -1,3 +1,4 @@ +import { CommandParser } from '../client/parser'; import { RedisArgument, Command } from '../RESP/types'; import { SortedSetMember, transformDoubleArgument, transformDoubleReply } from './generic-transformers'; @@ -24,58 +25,57 @@ export interface ZAddOptions { } export default { - FIRST_KEY_INDEX: 1, - transformArguments( + parseCommand( + parser: CommandParser, key: RedisArgument, members: SortedSetMember | Array, options?: ZAddOptions ) { - const args = ['ZADD', key]; + parser.push('ZADD'); + parser.pushKey(key); if (options?.condition) { - args.push(options.condition); + parser.push(options.condition); } else if (options?.NX) { - args.push('NX'); + parser.push('NX'); } else if (options?.XX) { - args.push('XX'); + parser.push('XX'); } if (options?.comparison) { - args.push(options.comparison); + parser.push(options.comparison); } else if (options?.LT) { - args.push('LT'); + parser.push('LT'); } else if (options?.GT) { - args.push('GT'); + parser.push('GT'); } if (options?.CH) { - args.push('CH'); + parser.push('CH'); } - pushMembers(args, members); - - return args; + pushMembers(parser, members); }, transformReply: transformDoubleReply } as const satisfies Command; export function pushMembers( - args: Array, + parser: CommandParser, members: SortedSetMember | Array) { if (Array.isArray(members)) { for (const member of members) { - pushMember(args, member); + pushMember(parser, member); } } else { - pushMember(args, members); + pushMember(parser, members); } } function pushMember( - args: Array, + parser: CommandParser, member: SortedSetMember ) { - args.push( + parser.push( transformDoubleArgument(member.score), member.value ); diff --git a/packages/client/lib/commands/ZADD_INCR.spec.ts b/packages/client/lib/commands/ZADD_INCR.spec.ts index c6ffcb796f3..df9ac87f449 100644 --- a/packages/client/lib/commands/ZADD_INCR.spec.ts +++ b/packages/client/lib/commands/ZADD_INCR.spec.ts @@ -1,12 +1,13 @@ import { strict as assert } from 'node:assert'; import testUtils, { GLOBAL } from '../test-utils'; import ZADD_INCR from './ZADD_INCR'; +import { parseArgs } from './generic-transformers'; describe('ZADD INCR', () => { describe('transformArguments', () => { it('single member', () => { assert.deepEqual( - ZADD_INCR.transformArguments('key', { + parseArgs(ZADD_INCR, 'key', { value: '1', score: 1 }), @@ -16,7 +17,7 @@ describe('ZADD INCR', () => { it('multiple members', () => { assert.deepEqual( - ZADD_INCR.transformArguments('key', [{ + parseArgs(ZADD_INCR, 'key', [{ value: '1', score: 1 }, { @@ -29,7 +30,7 @@ describe('ZADD INCR', () => { it('with condition', () => { assert.deepEqual( - ZADD_INCR.transformArguments('key', { + parseArgs(ZADD_INCR, 'key', { value: '1', score: 1 }, { @@ -41,7 +42,7 @@ describe('ZADD INCR', () => { it('with comparison', () => { assert.deepEqual( - ZADD_INCR.transformArguments('key', { + parseArgs(ZADD_INCR, 'key', { value: '1', score: 1 }, { @@ -53,7 +54,7 @@ describe('ZADD INCR', () => { it('with CH', () => { assert.deepEqual( - ZADD_INCR.transformArguments('key', { + parseArgs(ZADD_INCR, 'key', { value: '1', score: 1 }, { @@ -65,7 +66,7 @@ describe('ZADD INCR', () => { it('with condition, comparison, CH', () => { assert.deepEqual( - ZADD_INCR.transformArguments('key', { + parseArgs(ZADD_INCR, 'key', { value: '1', score: 1 }, { diff --git a/packages/client/lib/commands/ZADD_INCR.ts b/packages/client/lib/commands/ZADD_INCR.ts index 8fb10721674..f37554b1681 100644 --- a/packages/client/lib/commands/ZADD_INCR.ts +++ b/packages/client/lib/commands/ZADD_INCR.ts @@ -1,3 +1,4 @@ +import { CommandParser } from '../client/parser'; import { RedisArgument, Command } from '../RESP/types'; import { pushMembers } from './ZADD'; import { SortedSetMember, transformNullableDoubleReply } from './generic-transformers'; @@ -9,31 +10,30 @@ export interface ZAddOptions { } export default { - FIRST_KEY_INDEX: 1, - transformArguments( + parseCommand( + parser: CommandParser, key: RedisArgument, members: SortedSetMember | Array, options?: ZAddOptions ) { - const args = ['ZADD', key]; + parser.push('ZADD'); + parser.pushKey(key); if (options?.condition) { - args.push(options.condition); + parser.push(options.condition); } if (options?.comparison) { - args.push(options.comparison); + parser.push(options.comparison); } if (options?.CH) { - args.push('CH'); + parser.push('CH'); } - args.push('INCR'); + parser.push('INCR'); - pushMembers(args, members); - - return args; + pushMembers(parser, members); }, transformReply: transformNullableDoubleReply } as const satisfies Command; diff --git a/packages/client/lib/commands/ZCARD.spec.ts b/packages/client/lib/commands/ZCARD.spec.ts index ff4bb881fb7..44adec0833a 100644 --- a/packages/client/lib/commands/ZCARD.spec.ts +++ b/packages/client/lib/commands/ZCARD.spec.ts @@ -1,11 +1,12 @@ import { strict as assert } from 'node:assert'; import testUtils, { GLOBAL } from '../test-utils'; import ZCARD from './ZCARD'; +import { parseArgs } from './generic-transformers'; describe('ZCARD', () => { it('transformArguments', () => { assert.deepEqual( - ZCARD.transformArguments('key'), + parseArgs(ZCARD, 'key'), ['ZCARD', 'key'] ); }); diff --git a/packages/client/lib/commands/ZCARD.ts b/packages/client/lib/commands/ZCARD.ts index c11cb69db3c..57b9e7f1d47 100644 --- a/packages/client/lib/commands/ZCARD.ts +++ b/packages/client/lib/commands/ZCARD.ts @@ -1,10 +1,12 @@ +import { CommandParser } from '../client/parser'; import { RedisArgument, NumberReply, Command } from '../RESP/types'; export default { - FIRST_KEY_INDEX: 1, + CACHEABLE: true, IS_READ_ONLY: true, - transformArguments(key: RedisArgument) { - return ['ZCARD', key]; + parseCommand(parser: CommandParser, key: RedisArgument) { + parser.push('ZCARD'); + parser.pushKey(key); }, transformReply: undefined as unknown as () => NumberReply } as const satisfies Command; diff --git a/packages/client/lib/commands/ZCOUNT.spec.ts b/packages/client/lib/commands/ZCOUNT.spec.ts index 357f24bd796..5d279d7a4ca 100644 --- a/packages/client/lib/commands/ZCOUNT.spec.ts +++ b/packages/client/lib/commands/ZCOUNT.spec.ts @@ -1,11 +1,12 @@ import { strict as assert } from 'node:assert'; import testUtils, { GLOBAL } from '../test-utils'; import ZCOUNT from './ZCOUNT'; +import { parseArgs } from './generic-transformers'; describe('ZCOUNT', () => { it('transformArguments', () => { assert.deepEqual( - ZCOUNT.transformArguments('key', 0, 1), + parseArgs(ZCOUNT, 'key', 0, 1), ['ZCOUNT', 'key', '0', '1'] ); }); diff --git a/packages/client/lib/commands/ZCOUNT.ts b/packages/client/lib/commands/ZCOUNT.ts index 187a316b15a..ccbc3d13d9b 100644 --- a/packages/client/lib/commands/ZCOUNT.ts +++ b/packages/client/lib/commands/ZCOUNT.ts @@ -1,20 +1,22 @@ +import { CommandParser } from '../client/parser'; import { RedisArgument, NumberReply, Command } from '../RESP/types'; import { transformStringDoubleArgument } from './generic-transformers'; export default { - FIRST_KEY_INDEX: 1, + CACHEABLE: true, IS_READ_ONLY: true, - transformArguments( + parseCommand( + parser: CommandParser, key: RedisArgument, min: number | RedisArgument, max: number | RedisArgument ) { - return [ - 'ZCOUNT', - key, - transformStringDoubleArgument(min), + parser.push('ZCOUNT'); + parser.pushKey(key); + parser.push( + transformStringDoubleArgument(min), transformStringDoubleArgument(max) - ]; + ); }, transformReply: undefined as unknown as () => NumberReply } as const satisfies Command; diff --git a/packages/client/lib/commands/ZDIFF.spec.ts b/packages/client/lib/commands/ZDIFF.spec.ts index a285190398c..4914df3e978 100644 --- a/packages/client/lib/commands/ZDIFF.spec.ts +++ b/packages/client/lib/commands/ZDIFF.spec.ts @@ -1,6 +1,7 @@ import { strict as assert } from 'node:assert'; import testUtils, { GLOBAL } from '../test-utils'; import ZDIFF from './ZDIFF'; +import { parseArgs } from './generic-transformers'; describe('ZDIFF', () => { testUtils.isVersionGreaterThanHook([6, 2]); @@ -8,14 +9,14 @@ describe('ZDIFF', () => { describe('transformArguments', () => { it('string', () => { assert.deepEqual( - ZDIFF.transformArguments('key'), + parseArgs(ZDIFF, 'key'), ['ZDIFF', '1', 'key'] ); }); it('array', () => { assert.deepEqual( - ZDIFF.transformArguments(['1', '2']), + parseArgs(ZDIFF, ['1', '2']), ['ZDIFF', '2', '1', '2'] ); }); diff --git a/packages/client/lib/commands/ZDIFF.ts b/packages/client/lib/commands/ZDIFF.ts index f16c8717cdb..28135dc9c13 100644 --- a/packages/client/lib/commands/ZDIFF.ts +++ b/packages/client/lib/commands/ZDIFF.ts @@ -1,11 +1,12 @@ +import { CommandParser } from '../client/parser'; import { ArrayReply, BlobStringReply, Command } from '../RESP/types'; -import { RedisVariadicArgument, pushVariadicArgument } from './generic-transformers'; +import { RedisVariadicArgument } from './generic-transformers'; export default { - FIRST_KEY_INDEX: 2, IS_READ_ONLY: true, - transformArguments(keys: RedisVariadicArgument) { - return pushVariadicArgument(['ZDIFF'], keys); + parseCommand(parser: CommandParser, keys: RedisVariadicArgument) { + parser.push('ZDIFF'); + parser.pushKeysLength(keys); }, transformReply: undefined as unknown as () => ArrayReply } as const satisfies Command; diff --git a/packages/client/lib/commands/ZDIFFSTORE.spec.ts b/packages/client/lib/commands/ZDIFFSTORE.spec.ts index 1bd779302bf..7f380cfc532 100644 --- a/packages/client/lib/commands/ZDIFFSTORE.spec.ts +++ b/packages/client/lib/commands/ZDIFFSTORE.spec.ts @@ -1,6 +1,7 @@ import { strict as assert } from 'node:assert'; import testUtils, { GLOBAL } from '../test-utils'; import ZDIFFSTORE from './ZDIFFSTORE'; +import { parseArgs } from './generic-transformers'; describe('ZDIFFSTORE', () => { testUtils.isVersionGreaterThanHook([6, 2]); @@ -8,14 +9,14 @@ describe('ZDIFFSTORE', () => { describe('transformArguments', () => { it('string', () => { assert.deepEqual( - ZDIFFSTORE.transformArguments('destination', 'key'), + parseArgs(ZDIFFSTORE, 'destination', 'key'), ['ZDIFFSTORE', 'destination', '1', 'key'] ); }); it('array', () => { assert.deepEqual( - ZDIFFSTORE.transformArguments('destination', ['1', '2']), + parseArgs(ZDIFFSTORE, 'destination', ['1', '2']), ['ZDIFFSTORE', 'destination', '2', '1', '2'] ); }); diff --git a/packages/client/lib/commands/ZDIFFSTORE.ts b/packages/client/lib/commands/ZDIFFSTORE.ts index e4614a1cb14..d83a4bdc851 100644 --- a/packages/client/lib/commands/ZDIFFSTORE.ts +++ b/packages/client/lib/commands/ZDIFFSTORE.ts @@ -1,14 +1,13 @@ +import { CommandParser } from '../client/parser'; import { RedisArgument, NumberReply, Command } from '../RESP/types'; -import { RedisVariadicArgument, pushVariadicArgument } from './generic-transformers'; +import { RedisVariadicArgument } from './generic-transformers'; export default { - FIRST_KEY_INDEX: 2, IS_READ_ONLY: true, - transformArguments( - destination: RedisArgument, - inputKeys: RedisVariadicArgument - ) { - return pushVariadicArgument(['ZDIFFSTORE', destination], inputKeys); + parseCommand(parser: CommandParser, destination: RedisArgument, inputKeys: RedisVariadicArgument) { + parser.push('ZDIFFSTORE'); + parser.pushKey(destination); + parser.pushKeysLength(inputKeys); }, transformReply: undefined as unknown as () => NumberReply } as const satisfies Command; diff --git a/packages/client/lib/commands/ZDIFF_WITHSCORES.spec.ts b/packages/client/lib/commands/ZDIFF_WITHSCORES.spec.ts index 4fcd1f978a3..bea639f223e 100644 --- a/packages/client/lib/commands/ZDIFF_WITHSCORES.spec.ts +++ b/packages/client/lib/commands/ZDIFF_WITHSCORES.spec.ts @@ -1,6 +1,7 @@ import { strict as assert } from 'node:assert'; import testUtils, { GLOBAL } from '../test-utils'; import ZDIFF_WITHSCORES from './ZDIFF_WITHSCORES'; +import { parseArgs } from './generic-transformers'; describe('ZDIFF WITHSCORES', () => { testUtils.isVersionGreaterThanHook([6, 2]); @@ -8,14 +9,14 @@ describe('ZDIFF WITHSCORES', () => { describe('transformArguments', () => { it('string', () => { assert.deepEqual( - ZDIFF_WITHSCORES.transformArguments('key'), + parseArgs(ZDIFF_WITHSCORES, 'key'), ['ZDIFF', '1', 'key', 'WITHSCORES'] ); }); it('array', () => { assert.deepEqual( - ZDIFF_WITHSCORES.transformArguments(['1', '2']), + parseArgs(ZDIFF_WITHSCORES, ['1', '2']), ['ZDIFF', '2', '1', '2', 'WITHSCORES'] ); }); diff --git a/packages/client/lib/commands/ZDIFF_WITHSCORES.ts b/packages/client/lib/commands/ZDIFF_WITHSCORES.ts index f971e828574..4088f106dc6 100644 --- a/packages/client/lib/commands/ZDIFF_WITHSCORES.ts +++ b/packages/client/lib/commands/ZDIFF_WITHSCORES.ts @@ -1,14 +1,14 @@ +import { CommandParser } from '../client/parser'; import { Command } from '../RESP/types'; +import { RedisVariadicArgument, transformSortedSetReply } from './generic-transformers'; import ZDIFF from './ZDIFF'; -import { transformSortedSetReply } from './generic-transformers'; + export default { - FIRST_KEY_INDEX: ZDIFF.FIRST_KEY_INDEX, IS_READ_ONLY: ZDIFF.IS_READ_ONLY, - transformArguments(...args: Parameters) { - const redisArgs = ZDIFF.transformArguments(...args); - redisArgs.push('WITHSCORES'); - return redisArgs; + parseCommand(parser: CommandParser, keys: RedisVariadicArgument) { + ZDIFF.parseCommand(parser, keys); + parser.push('WITHSCORES'); }, transformReply: transformSortedSetReply } as const satisfies Command; diff --git a/packages/client/lib/commands/ZINCRBY.spec.ts b/packages/client/lib/commands/ZINCRBY.spec.ts index fea3c7a2f71..8f6c5141252 100644 --- a/packages/client/lib/commands/ZINCRBY.spec.ts +++ b/packages/client/lib/commands/ZINCRBY.spec.ts @@ -1,11 +1,12 @@ import { strict as assert } from 'node:assert'; import testUtils, { GLOBAL } from '../test-utils'; import ZINCRBY from './ZINCRBY'; +import { parseArgs } from './generic-transformers'; describe('ZINCRBY', () => { it('transformArguments', () => { assert.deepEqual( - ZINCRBY.transformArguments('key', 1, 'member'), + parseArgs(ZINCRBY, 'key', 1, 'member'), ['ZINCRBY', 'key', '1', 'member'] ); }); diff --git a/packages/client/lib/commands/ZINCRBY.ts b/packages/client/lib/commands/ZINCRBY.ts index d9e43845016..5e461891e9c 100644 --- a/packages/client/lib/commands/ZINCRBY.ts +++ b/packages/client/lib/commands/ZINCRBY.ts @@ -1,19 +1,17 @@ +import { CommandParser } from '../client/parser'; import { RedisArgument, Command } from '../RESP/types'; import { transformDoubleArgument, transformDoubleReply } from './generic-transformers'; export default { - FIRST_KEY_INDEX: 1, - transformArguments( + parseCommand( + parser: CommandParser, key: RedisArgument, increment: number, member: RedisArgument ) { - return [ - 'ZINCRBY', - key, - transformDoubleArgument(increment), - member - ]; + parser.push('ZINCRBY'); + parser.pushKey(key); + parser.push(transformDoubleArgument(increment), member); }, transformReply: transformDoubleReply } as const satisfies Command; diff --git a/packages/client/lib/commands/ZINTER.spec.ts b/packages/client/lib/commands/ZINTER.spec.ts index 0d678ce1151..73df0935de9 100644 --- a/packages/client/lib/commands/ZINTER.spec.ts +++ b/packages/client/lib/commands/ZINTER.spec.ts @@ -1,6 +1,7 @@ import { strict as assert } from 'node:assert'; import testUtils, { GLOBAL } from '../test-utils'; import ZINTER from './ZINTER'; +import { parseArgs } from './generic-transformers'; describe('ZINTER', () => { testUtils.isVersionGreaterThanHook([6, 2]); @@ -8,21 +9,21 @@ describe('ZINTER', () => { describe('transformArguments', () => { it('key (string)', () => { assert.deepEqual( - ZINTER.transformArguments('key'), + parseArgs(ZINTER, 'key'), ['ZINTER', '1', 'key'] ); }); it('keys (Array)', () => { assert.deepEqual( - ZINTER.transformArguments(['1', '2']), + parseArgs(ZINTER, ['1', '2']), ['ZINTER', '2', '1', '2'] ); }); it('key & weight', () => { assert.deepEqual( - ZINTER.transformArguments({ + parseArgs(ZINTER, { key: 'key', weight: 1 }), @@ -32,7 +33,7 @@ describe('ZINTER', () => { it('keys & weights', () => { assert.deepEqual( - ZINTER.transformArguments([{ + parseArgs(ZINTER, [{ key: 'a', weight: 1 }, { @@ -45,7 +46,7 @@ describe('ZINTER', () => { it('with AGGREGATE', () => { assert.deepEqual( - ZINTER.transformArguments('key', { + parseArgs(ZINTER, 'key', { AGGREGATE: 'SUM' }), ['ZINTER', '1', 'key', 'AGGREGATE', 'SUM'] diff --git a/packages/client/lib/commands/ZINTER.ts b/packages/client/lib/commands/ZINTER.ts index 392c3a96c65..740d3c295ec 100644 --- a/packages/client/lib/commands/ZINTER.ts +++ b/packages/client/lib/commands/ZINTER.ts @@ -1,5 +1,6 @@ +import { CommandParser } from '../client/parser'; import { RedisArgument, ArrayReply, BlobStringReply, Command } from '../RESP/types'; -import { ZKeys, pushZKeysArguments } from './generic-transformers'; +import { ZKeys, parseZKeysArguments } from './generic-transformers'; export type ZInterKeyAndWeight = { key: RedisArgument; @@ -8,32 +9,29 @@ export type ZInterKeyAndWeight = { export type ZInterKeys = T | [T, ...Array]; +export type ZInterKeysType = ZInterKeys | ZInterKeys; + export interface ZInterOptions { AGGREGATE?: 'SUM' | 'MIN' | 'MAX'; } -export function pushZInterArguments( - args: Array, +export function parseZInterArguments( + parser: CommandParser, keys: ZKeys, options?: ZInterOptions ) { - args = pushZKeysArguments(args, keys); + parseZKeysArguments(parser, keys); if (options?.AGGREGATE) { - args.push('AGGREGATE', options.AGGREGATE); + parser.push('AGGREGATE', options.AGGREGATE); } - - return args; } export default { - FIRST_KEY_INDEX: 2, IS_READ_ONLY: true, - transformArguments( - keys: ZInterKeys | ZInterKeys, - options?: ZInterOptions - ) { - return pushZInterArguments(['ZINTER'], keys, options); + parseCommand(parser: CommandParser, keys: ZInterKeysType, options?: ZInterOptions) { + parser.push('ZINTER'); + parseZInterArguments(parser, keys, options); }, transformReply: undefined as unknown as () => ArrayReply } as const satisfies Command; diff --git a/packages/client/lib/commands/ZINTERCARD.spec.ts b/packages/client/lib/commands/ZINTERCARD.spec.ts index 312e2825ff4..5204872a2d0 100644 --- a/packages/client/lib/commands/ZINTERCARD.spec.ts +++ b/packages/client/lib/commands/ZINTERCARD.spec.ts @@ -1,6 +1,7 @@ import { strict as assert } from 'node:assert'; import testUtils, { GLOBAL } from '../test-utils'; import ZINTERCARD from './ZINTERCARD'; +import { parseArgs } from './generic-transformers'; describe('ZINTERCARD', () => { testUtils.isVersionGreaterThanHook([7]); @@ -8,7 +9,7 @@ describe('ZINTERCARD', () => { describe('transformArguments', () => { it('simple', () => { assert.deepEqual( - ZINTERCARD.transformArguments(['1', '2']), + parseArgs(ZINTERCARD, ['1', '2']), ['ZINTERCARD', '2', '1', '2'] ); }); @@ -16,14 +17,14 @@ describe('ZINTERCARD', () => { describe('with LIMIT', () => { it('plain number (backwards compatibility)', () => { assert.deepEqual( - ZINTERCARD.transformArguments(['1', '2'], 1), + parseArgs(ZINTERCARD, ['1', '2'], 1), ['ZINTERCARD', '2', '1', '2', 'LIMIT', '1'] ); }); it('{ LIMIT: number }', () => { assert.deepEqual( - ZINTERCARD.transformArguments(['1', '2'], { + parseArgs(ZINTERCARD, ['1', '2'], { LIMIT: 1 }), ['ZINTERCARD', '2', '1', '2', 'LIMIT', '1'] diff --git a/packages/client/lib/commands/ZINTERCARD.ts b/packages/client/lib/commands/ZINTERCARD.ts index 9953d88eecc..8c2e98d12cb 100644 --- a/packages/client/lib/commands/ZINTERCARD.ts +++ b/packages/client/lib/commands/ZINTERCARD.ts @@ -1,27 +1,27 @@ +import { CommandParser } from '../client/parser'; import { NumberReply, Command } from '../RESP/types'; -import { RedisVariadicArgument, pushVariadicArgument } from './generic-transformers'; +import { RedisVariadicArgument } from './generic-transformers'; export interface ZInterCardOptions { LIMIT?: number; } export default { - FIRST_KEY_INDEX: 2, IS_READ_ONLY: true, - transformArguments( + parseCommand( + parser: CommandParser, keys: RedisVariadicArgument, options?: ZInterCardOptions['LIMIT'] | ZInterCardOptions ) { - const args = pushVariadicArgument(['ZINTERCARD'], keys); + parser.push('ZINTERCARD'); + parser.pushKeysLength(keys); // backwards compatibility if (typeof options === 'number') { - args.push('LIMIT', options.toString()); + parser.push('LIMIT', options.toString()); } else if (options?.LIMIT) { - args.push('LIMIT', options.LIMIT.toString()); + parser.push('LIMIT', options.LIMIT.toString()); } - - return args; }, transformReply: undefined as unknown as () => NumberReply } as const satisfies Command; diff --git a/packages/client/lib/commands/ZINTERSTORE.spec.ts b/packages/client/lib/commands/ZINTERSTORE.spec.ts index 27914ca0327..c6b448ab908 100644 --- a/packages/client/lib/commands/ZINTERSTORE.spec.ts +++ b/packages/client/lib/commands/ZINTERSTORE.spec.ts @@ -1,26 +1,27 @@ import { strict as assert } from 'node:assert'; import testUtils, { GLOBAL } from '../test-utils'; import ZINTERSTORE from './ZINTERSTORE'; +import { parseArgs } from './generic-transformers'; describe('ZINTERSTORE', () => { describe('transformArguments', () => { it('key (string)', () => { assert.deepEqual( - ZINTERSTORE.transformArguments('destination', 'source'), + parseArgs(ZINTERSTORE, 'destination', 'source'), ['ZINTERSTORE', 'destination', '1', 'source'] ); }); it('keys (Array)', () => { assert.deepEqual( - ZINTERSTORE.transformArguments('destination', ['1', '2']), + parseArgs(ZINTERSTORE, 'destination', ['1', '2']), ['ZINTERSTORE', 'destination', '2', '1', '2'] ); }); it('key & weight', () => { assert.deepEqual( - ZINTERSTORE.transformArguments('destination', { + parseArgs(ZINTERSTORE, 'destination', { key: 'source', weight: 1 }), @@ -30,7 +31,7 @@ describe('ZINTERSTORE', () => { it('keys & weights', () => { assert.deepEqual( - ZINTERSTORE.transformArguments('destination', [{ + parseArgs(ZINTERSTORE, 'destination', [{ key: 'a', weight: 1 }, { @@ -43,7 +44,7 @@ describe('ZINTERSTORE', () => { it('with AGGREGATE', () => { assert.deepEqual( - ZINTERSTORE.transformArguments('destination', 'source', { + parseArgs(ZINTERSTORE, 'destination', 'source', { AGGREGATE: 'SUM' }), ['ZINTERSTORE', 'destination', '1', 'source', 'AGGREGATE', 'SUM'] diff --git a/packages/client/lib/commands/ZINTERSTORE.ts b/packages/client/lib/commands/ZINTERSTORE.ts index a5334566d73..dcbe153cfc7 100644 --- a/packages/client/lib/commands/ZINTERSTORE.ts +++ b/packages/client/lib/commands/ZINTERSTORE.ts @@ -1,17 +1,20 @@ +import { CommandParser } from '../client/parser'; import { RedisArgument, NumberReply, Command } from '../RESP/types'; -import { pushZInterArguments, ZInterOptions } from './ZINTER'; import { ZKeys } from './generic-transformers'; +import { parseZInterArguments, ZInterOptions } from './ZINTER'; export default { - FIRST_KEY_INDEX: 1, IS_READ_ONLY: false, - transformArguments( + parseCommand( + parser: CommandParser, destination: RedisArgument, keys: ZKeys, options?: ZInterOptions ) { - return pushZInterArguments(['ZINTERSTORE', destination], keys, options); + parser.push('ZINTERSTORE'); + parser.pushKey(destination); + parseZInterArguments(parser, keys, options); }, transformReply: undefined as unknown as () => NumberReply } as const satisfies Command; diff --git a/packages/client/lib/commands/ZINTER_WITHSCORES.spec.ts b/packages/client/lib/commands/ZINTER_WITHSCORES.spec.ts index 05790510e49..234b250b143 100644 --- a/packages/client/lib/commands/ZINTER_WITHSCORES.spec.ts +++ b/packages/client/lib/commands/ZINTER_WITHSCORES.spec.ts @@ -1,6 +1,7 @@ import { strict as assert } from 'node:assert'; import testUtils, { GLOBAL } from '../test-utils'; import ZINTER_WITHSCORES from './ZINTER_WITHSCORES'; +import { parseArgs } from './generic-transformers'; describe('ZINTER WITHSCORES', () => { testUtils.isVersionGreaterThanHook([6, 2]); @@ -8,21 +9,21 @@ describe('ZINTER WITHSCORES', () => { describe('transformArguments', () => { it('key (string)', () => { assert.deepEqual( - ZINTER_WITHSCORES.transformArguments('key'), + parseArgs(ZINTER_WITHSCORES, 'key'), ['ZINTER', '1', 'key', 'WITHSCORES'] ); }); it('keys (Array)', () => { assert.deepEqual( - ZINTER_WITHSCORES.transformArguments(['1', '2']), + parseArgs(ZINTER_WITHSCORES, ['1', '2']), ['ZINTER', '2', '1', '2', 'WITHSCORES'] ); }); it('key & weight', () => { assert.deepEqual( - ZINTER_WITHSCORES.transformArguments({ + parseArgs(ZINTER_WITHSCORES, { key: 'key', weight: 1 }), @@ -32,7 +33,7 @@ describe('ZINTER WITHSCORES', () => { it('keys & weights', () => { assert.deepEqual( - ZINTER_WITHSCORES.transformArguments([{ + parseArgs(ZINTER_WITHSCORES, [{ key: 'a', weight: 1 }, { @@ -45,7 +46,7 @@ describe('ZINTER WITHSCORES', () => { it('with AGGREGATE', () => { assert.deepEqual( - ZINTER_WITHSCORES.transformArguments('key', { + parseArgs(ZINTER_WITHSCORES, 'key', { AGGREGATE: 'SUM' }), ['ZINTER', '1', 'key', 'AGGREGATE', 'SUM', 'WITHSCORES'] diff --git a/packages/client/lib/commands/ZINTER_WITHSCORES.ts b/packages/client/lib/commands/ZINTER_WITHSCORES.ts index b287649fbc0..d3a6614b3c2 100644 --- a/packages/client/lib/commands/ZINTER_WITHSCORES.ts +++ b/packages/client/lib/commands/ZINTER_WITHSCORES.ts @@ -1,14 +1,13 @@ import { Command } from '../RESP/types'; -import ZINTER from './ZINTER'; import { transformSortedSetReply } from './generic-transformers'; +import ZINTER from './ZINTER'; + export default { - FIRST_KEY_INDEX: ZINTER.FIRST_KEY_INDEX, IS_READ_ONLY: ZINTER.IS_READ_ONLY, - transformArguments(...args: Parameters) { - const redisArgs = ZINTER.transformArguments(...args); - redisArgs.push('WITHSCORES'); - return redisArgs; + parseCommand(...args: Parameters) { + ZINTER.parseCommand(...args); + args[0].push('WITHSCORES'); }, transformReply: transformSortedSetReply } as const satisfies Command; diff --git a/packages/client/lib/commands/ZLEXCOUNT.spec.ts b/packages/client/lib/commands/ZLEXCOUNT.spec.ts index d0a76075579..78c7411affd 100644 --- a/packages/client/lib/commands/ZLEXCOUNT.spec.ts +++ b/packages/client/lib/commands/ZLEXCOUNT.spec.ts @@ -1,11 +1,12 @@ import { strict as assert } from 'node:assert'; import testUtils, { GLOBAL } from '../test-utils'; import ZLEXCOUNT from './ZLEXCOUNT'; +import { parseArgs } from './generic-transformers'; describe('ZLEXCOUNT', () => { it('transformArguments', () => { assert.deepEqual( - ZLEXCOUNT.transformArguments('key', '[a', '[b'), + parseArgs(ZLEXCOUNT, 'key', '[a', '[b'), ['ZLEXCOUNT', 'key', '[a', '[b'] ); }); diff --git a/packages/client/lib/commands/ZLEXCOUNT.ts b/packages/client/lib/commands/ZLEXCOUNT.ts index 26c9e0d70ac..7536590c168 100644 --- a/packages/client/lib/commands/ZLEXCOUNT.ts +++ b/packages/client/lib/commands/ZLEXCOUNT.ts @@ -1,19 +1,19 @@ +import { CommandParser } from '../client/parser'; import { RedisArgument, NumberReply, Command } from '../RESP/types'; export default { - FIRST_KEY_INDEX: 1, + CACHEABLE: true, IS_READ_ONLY: true, - transformArguments( + parseCommand( + parser: CommandParser, key: RedisArgument, min: RedisArgument, max: RedisArgument ) { - return [ - 'ZLEXCOUNT', - key, - min, - max - ]; + parser.push('ZLEXCOUNT'); + parser.pushKey(key); + parser.push(min); + parser.push(max); }, transformReply: undefined as unknown as () => NumberReply } as const satisfies Command; diff --git a/packages/client/lib/commands/ZMPOP.spec.ts b/packages/client/lib/commands/ZMPOP.spec.ts index 6335fca81cb..c15a53b7313 100644 --- a/packages/client/lib/commands/ZMPOP.spec.ts +++ b/packages/client/lib/commands/ZMPOP.spec.ts @@ -1,6 +1,7 @@ import { strict as assert } from 'node:assert'; import testUtils, { GLOBAL } from '../test-utils'; import ZMPOP from './ZMPOP'; +import { parseArgs } from './generic-transformers'; describe('ZMPOP', () => { testUtils.isVersionGreaterThanHook([7]); @@ -8,14 +9,14 @@ describe('ZMPOP', () => { describe('transformArguments', () => { it('simple', () => { assert.deepEqual( - ZMPOP.transformArguments('key', 'MIN'), + parseArgs(ZMPOP, 'key', 'MIN'), ['ZMPOP', '1', 'key', 'MIN'] ); }); it('with count', () => { assert.deepEqual( - ZMPOP.transformArguments('key', 'MIN', { + parseArgs(ZMPOP, 'key', 'MIN', { COUNT: 2 }), ['ZMPOP', '1', 'key', 'MIN', 'COUNT', '2'] diff --git a/packages/client/lib/commands/ZMPOP.ts b/packages/client/lib/commands/ZMPOP.ts index 57d2cccdaca..0e47108e25f 100644 --- a/packages/client/lib/commands/ZMPOP.ts +++ b/packages/client/lib/commands/ZMPOP.ts @@ -1,5 +1,6 @@ -import { RedisArgument, NullReply, TuplesReply, BlobStringReply, DoubleReply, ArrayReply, UnwrapReply, Resp2Reply, Command, TypeMapping } from '../RESP/types'; -import { pushVariadicArgument, RedisVariadicArgument, SortedSetSide, transformSortedSetReply, transformDoubleReply } from './generic-transformers'; +import { CommandParser } from '../client/parser'; +import { NullReply, TuplesReply, BlobStringReply, DoubleReply, ArrayReply, UnwrapReply, Resp2Reply, Command, TypeMapping } from '../RESP/types'; +import { RedisVariadicArgument, SortedSetSide, transformSortedSetReply, transformDoubleReply, Tail } from './generic-transformers'; export interface ZMPopOptions { COUNT?: number; @@ -13,30 +14,33 @@ export type ZMPopRawReply = NullReply | TuplesReply<[ ]>> ]>; -export function transformZMPopArguments( - args: Array, +export function parseZMPopArguments( + parser: CommandParser, keys: RedisVariadicArgument, side: SortedSetSide, options?: ZMPopOptions ) { - args = pushVariadicArgument(args, keys); + parser.pushKeysLength(keys); - args.push(side); + parser.push(side); if (options?.COUNT) { - args.push('COUNT', options.COUNT.toString()); + parser.push('COUNT', options.COUNT.toString()); } - - return args; } -export type ZMPopArguments = typeof transformZMPopArguments extends (_: any, ...args: infer T) => any ? T : never; +export type ZMPopArguments = Tail>; export default { - FIRST_KEY_INDEX: 2, IS_READ_ONLY: false, - transformArguments(...args: ZMPopArguments) { - return transformZMPopArguments(['ZMPOP'], ...args); + parseCommand( + parser: CommandParser, + keys: RedisVariadicArgument, + side: SortedSetSide, + options?: ZMPopOptions + ) { + parser.push('ZMPOP'); + parseZMPopArguments(parser, keys, side, options) }, transformReply: { 2(reply: UnwrapReply>, preserve?: any, typeMapping?: TypeMapping) { diff --git a/packages/client/lib/commands/ZMSCORE.spec.ts b/packages/client/lib/commands/ZMSCORE.spec.ts index 5035724b117..6c6d2946e00 100644 --- a/packages/client/lib/commands/ZMSCORE.spec.ts +++ b/packages/client/lib/commands/ZMSCORE.spec.ts @@ -1,6 +1,7 @@ import { strict as assert } from 'node:assert'; import testUtils, { GLOBAL } from '../test-utils'; import ZMSCORE from './ZMSCORE'; +import { parseArgs } from './generic-transformers'; describe('ZMSCORE', () => { testUtils.isVersionGreaterThanHook([6, 2]); @@ -8,14 +9,14 @@ describe('ZMSCORE', () => { describe('transformArguments', () => { it('string', () => { assert.deepEqual( - ZMSCORE.transformArguments('key', 'member'), + parseArgs(ZMSCORE, 'key', 'member'), ['ZMSCORE', 'key', 'member'] ); }); it('array', () => { assert.deepEqual( - ZMSCORE.transformArguments('key', ['1', '2']), + parseArgs(ZMSCORE, 'key', ['1', '2']), ['ZMSCORE', 'key', '1', '2'] ); }); diff --git a/packages/client/lib/commands/ZMSCORE.ts b/packages/client/lib/commands/ZMSCORE.ts index 00ade13b011..b225b35dfd3 100644 --- a/packages/client/lib/commands/ZMSCORE.ts +++ b/packages/client/lib/commands/ZMSCORE.ts @@ -1,14 +1,14 @@ +import { CommandParser } from '../client/parser'; import { RedisArgument, ArrayReply, NullReply, BlobStringReply, DoubleReply, UnwrapReply, Command, TypeMapping } from '../RESP/types'; -import { createTransformNullableDoubleReplyResp2Func, pushVariadicArguments, RedisVariadicArgument } from './generic-transformers'; +import { createTransformNullableDoubleReplyResp2Func, RedisVariadicArgument } from './generic-transformers'; export default { - FIRST_KEY_INDEX: 1, + CACHEABLE: true, IS_READ_ONLY: true, - transformArguments( - key: RedisArgument, - member: RedisVariadicArgument - ) { - return pushVariadicArguments(['ZMSCORE', key], member); + parseCommand(parser: CommandParser, key: RedisArgument, member: RedisVariadicArgument) { + parser.push('ZMSCORE'); + parser.pushKey(key); + parser.pushVariadic(member); }, transformReply: { 2: (reply: UnwrapReply>, preserve?: any, typeMapping?: TypeMapping) => { diff --git a/packages/client/lib/commands/ZPOPMAX.spec.ts b/packages/client/lib/commands/ZPOPMAX.spec.ts index 609ccb826b5..1796647df86 100644 --- a/packages/client/lib/commands/ZPOPMAX.spec.ts +++ b/packages/client/lib/commands/ZPOPMAX.spec.ts @@ -1,11 +1,12 @@ import { strict as assert } from 'node:assert'; import testUtils, { GLOBAL } from '../test-utils'; import ZPOPMAX from './ZPOPMAX'; +import { parseArgs } from './generic-transformers'; describe('ZPOPMAX', () => { it('transformArguments', () => { assert.deepEqual( - ZPOPMAX.transformArguments('key'), + parseArgs(ZPOPMAX, 'key'), ['ZPOPMAX', 'key'] ); }); diff --git a/packages/client/lib/commands/ZPOPMAX.ts b/packages/client/lib/commands/ZPOPMAX.ts index 130309347a6..05c7f35e052 100644 --- a/packages/client/lib/commands/ZPOPMAX.ts +++ b/packages/client/lib/commands/ZPOPMAX.ts @@ -1,11 +1,12 @@ +import { CommandParser } from '../client/parser'; import { RedisArgument, TuplesReply, BlobStringReply, DoubleReply, UnwrapReply, Command, TypeMapping } from '../RESP/types'; import { transformDoubleReply } from './generic-transformers'; export default { - FIRST_KEY_INDEX: 1, IS_READ_ONLY: false, - transformArguments(key: RedisArgument) { - return ['ZPOPMAX', key]; + parseCommand(parser: CommandParser, key: RedisArgument) { + parser.push('ZPOPMAX'); + parser.pushKey(key); }, transformReply: { 2: (reply: UnwrapReply>, preserve?: any, typeMapping?: TypeMapping) => { diff --git a/packages/client/lib/commands/ZPOPMAX_COUNT.spec.ts b/packages/client/lib/commands/ZPOPMAX_COUNT.spec.ts index b653b1f3f1a..dd9d85dbd36 100644 --- a/packages/client/lib/commands/ZPOPMAX_COUNT.spec.ts +++ b/packages/client/lib/commands/ZPOPMAX_COUNT.spec.ts @@ -1,11 +1,12 @@ import { strict as assert } from 'node:assert'; import testUtils, { GLOBAL } from '../test-utils'; import ZPOPMAX_COUNT from './ZPOPMAX_COUNT'; +import { parseArgs } from './generic-transformers'; describe('ZPOPMAX COUNT', () => { it('transformArguments', () => { assert.deepEqual( - ZPOPMAX_COUNT.transformArguments('key', 1), + parseArgs(ZPOPMAX_COUNT, 'key', 1), ['ZPOPMAX', 'key', '1'] ); }); diff --git a/packages/client/lib/commands/ZPOPMAX_COUNT.ts b/packages/client/lib/commands/ZPOPMAX_COUNT.ts index 00d39536ae1..888ce039fbe 100644 --- a/packages/client/lib/commands/ZPOPMAX_COUNT.ts +++ b/packages/client/lib/commands/ZPOPMAX_COUNT.ts @@ -1,11 +1,13 @@ +import { CommandParser } from '../client/parser'; import { RedisArgument, Command } from '../RESP/types'; import { transformSortedSetReply } from './generic-transformers'; export default { - FIRST_KEY_INDEX: 1, IS_READ_ONLY: false, - transformArguments(key: RedisArgument, count: number) { - return ['ZPOPMAX', key, count.toString()]; + parseCommand(parser: CommandParser, key: RedisArgument, count: number) { + parser.push('ZPOPMAX'); + parser.pushKey(key); + parser.push(count.toString()); }, transformReply: transformSortedSetReply } as const satisfies Command; diff --git a/packages/client/lib/commands/ZPOPMIN.spec.ts b/packages/client/lib/commands/ZPOPMIN.spec.ts index 0b2c57d28b9..653a4e70a92 100644 --- a/packages/client/lib/commands/ZPOPMIN.spec.ts +++ b/packages/client/lib/commands/ZPOPMIN.spec.ts @@ -1,11 +1,12 @@ import { strict as assert } from 'node:assert'; import testUtils, { GLOBAL } from '../test-utils'; import ZPOPMIN from './ZPOPMIN'; +import { parseArgs } from './generic-transformers'; describe('ZPOPMIN', () => { it('transformArguments', () => { assert.deepEqual( - ZPOPMIN.transformArguments('key'), + parseArgs(ZPOPMIN, 'key'), ['ZPOPMIN', 'key'] ); }); diff --git a/packages/client/lib/commands/ZPOPMIN.ts b/packages/client/lib/commands/ZPOPMIN.ts index b9da85cc974..6295925aef1 100644 --- a/packages/client/lib/commands/ZPOPMIN.ts +++ b/packages/client/lib/commands/ZPOPMIN.ts @@ -1,11 +1,12 @@ +import { CommandParser } from '../client/parser'; import { RedisArgument, Command } from '../RESP/types'; import ZPOPMAX from './ZPOPMAX'; export default { - FIRST_KEY_INDEX: 1, IS_READ_ONLY: false, - transformArguments(key: RedisArgument) { - return ['ZPOPMIN', key]; + parseCommand(parser: CommandParser, key: RedisArgument) { + parser.push('ZPOPMIN'); + parser.pushKey(key); }, transformReply: ZPOPMAX.transformReply } as const satisfies Command; diff --git a/packages/client/lib/commands/ZPOPMIN_COUNT.spec.ts b/packages/client/lib/commands/ZPOPMIN_COUNT.spec.ts index fa3d9e2a975..126a3cc1e9a 100644 --- a/packages/client/lib/commands/ZPOPMIN_COUNT.spec.ts +++ b/packages/client/lib/commands/ZPOPMIN_COUNT.spec.ts @@ -1,11 +1,12 @@ import { strict as assert } from 'node:assert'; import testUtils, { GLOBAL } from '../test-utils'; import ZPOPMIN_COUNT from './ZPOPMIN_COUNT'; +import { parseArgs } from './generic-transformers'; describe('ZPOPMIN COUNT', () => { it('transformArguments', () => { assert.deepEqual( - ZPOPMIN_COUNT.transformArguments('key', 1), + parseArgs(ZPOPMIN_COUNT, 'key', 1), ['ZPOPMIN', 'key', '1'] ); }); diff --git a/packages/client/lib/commands/ZPOPMIN_COUNT.ts b/packages/client/lib/commands/ZPOPMIN_COUNT.ts index 2433686da56..2b6abf580b9 100644 --- a/packages/client/lib/commands/ZPOPMIN_COUNT.ts +++ b/packages/client/lib/commands/ZPOPMIN_COUNT.ts @@ -1,11 +1,13 @@ +import { CommandParser } from '../client/parser'; import { RedisArgument, Command } from '../RESP/types'; import { transformSortedSetReply } from './generic-transformers'; export default { - FIRST_KEY_INDEX: 1, IS_READ_ONLY: false, - transformArguments(key: RedisArgument, count: number) { - return ['ZPOPMIN', key, count.toString()]; + parseCommand(parser: CommandParser, key: RedisArgument, count: number) { + parser.push('ZPOPMIN'); + parser.pushKey(key); + parser.push(count.toString()); }, transformReply: transformSortedSetReply } as const satisfies Command; diff --git a/packages/client/lib/commands/ZRANDMEMBER.spec.ts b/packages/client/lib/commands/ZRANDMEMBER.spec.ts index 519850f5eff..a25ea79f8e1 100644 --- a/packages/client/lib/commands/ZRANDMEMBER.spec.ts +++ b/packages/client/lib/commands/ZRANDMEMBER.spec.ts @@ -1,13 +1,14 @@ import { strict as assert } from 'node:assert'; import testUtils, { GLOBAL } from '../test-utils'; import ZRANDMEMBER from './ZRANDMEMBER'; +import { parseArgs } from './generic-transformers'; describe('ZRANDMEMBER', () => { testUtils.isVersionGreaterThanHook([6, 2]); it('transformArguments', () => { assert.deepEqual( - ZRANDMEMBER.transformArguments('key'), + parseArgs(ZRANDMEMBER, 'key'), ['ZRANDMEMBER', 'key'] ); }); diff --git a/packages/client/lib/commands/ZRANDMEMBER.ts b/packages/client/lib/commands/ZRANDMEMBER.ts index 449eb281c66..2abd9d3684c 100644 --- a/packages/client/lib/commands/ZRANDMEMBER.ts +++ b/packages/client/lib/commands/ZRANDMEMBER.ts @@ -1,10 +1,11 @@ +import { CommandParser } from '../client/parser'; import { RedisArgument, BlobStringReply, NullReply, Command } from '../RESP/types'; export default { - FIRST_KEY_INDEX: 1, IS_READ_ONLY: true, - transformArguments(key: RedisArgument) { - return ['ZRANDMEMBER', key]; + parseCommand(parser: CommandParser, key: RedisArgument) { + parser.push('ZRANDMEMBER'); + parser.pushKey(key); }, transformReply: undefined as unknown as () => BlobStringReply | NullReply } as const satisfies Command; diff --git a/packages/client/lib/commands/ZRANDMEMBER_COUNT.spec.ts b/packages/client/lib/commands/ZRANDMEMBER_COUNT.spec.ts index 2d0f4b9ced8..eee0d454975 100644 --- a/packages/client/lib/commands/ZRANDMEMBER_COUNT.spec.ts +++ b/packages/client/lib/commands/ZRANDMEMBER_COUNT.spec.ts @@ -1,13 +1,14 @@ import { strict as assert } from 'node:assert'; import testUtils, { GLOBAL } from '../test-utils'; import ZRANDMEMBER_COUNT from './ZRANDMEMBER_COUNT'; +import { parseArgs } from './generic-transformers'; describe('ZRANDMEMBER COUNT', () => { testUtils.isVersionGreaterThanHook([6, 2, 5]); it('transformArguments', () => { assert.deepEqual( - ZRANDMEMBER_COUNT.transformArguments('key', 1), + parseArgs(ZRANDMEMBER_COUNT, 'key', 1), ['ZRANDMEMBER', 'key', '1'] ); }); diff --git a/packages/client/lib/commands/ZRANDMEMBER_COUNT.ts b/packages/client/lib/commands/ZRANDMEMBER_COUNT.ts index 89b921f007a..42ef8110639 100644 --- a/packages/client/lib/commands/ZRANDMEMBER_COUNT.ts +++ b/packages/client/lib/commands/ZRANDMEMBER_COUNT.ts @@ -1,13 +1,12 @@ +import { CommandParser } from '../client/parser'; import { RedisArgument, ArrayReply, BlobStringReply, Command } from '../RESP/types'; import ZRANDMEMBER from './ZRANDMEMBER'; export default { - FIRST_KEY_INDEX: ZRANDMEMBER.FIRST_KEY_INDEX, IS_READ_ONLY: ZRANDMEMBER.IS_READ_ONLY, - transformArguments(key: RedisArgument, count: number) { - const args = ZRANDMEMBER.transformArguments(key); - args.push(count.toString()); - return args; + parseCommand(parser: CommandParser, key: RedisArgument, count: number) { + ZRANDMEMBER.parseCommand(parser, key); + parser.push(count.toString()); }, transformReply: undefined as unknown as () => ArrayReply } as const satisfies Command; diff --git a/packages/client/lib/commands/ZRANDMEMBER_COUNT_WITHSCORES.spec.ts b/packages/client/lib/commands/ZRANDMEMBER_COUNT_WITHSCORES.spec.ts index aeeea3f6e71..3be3b92aeef 100644 --- a/packages/client/lib/commands/ZRANDMEMBER_COUNT_WITHSCORES.spec.ts +++ b/packages/client/lib/commands/ZRANDMEMBER_COUNT_WITHSCORES.spec.ts @@ -1,13 +1,14 @@ import { strict as assert } from 'node:assert'; import testUtils, { GLOBAL } from '../test-utils'; import ZRANDMEMBER_COUNT_WITHSCORES from './ZRANDMEMBER_COUNT_WITHSCORES'; +import { parseArgs } from './generic-transformers'; describe('ZRANDMEMBER COUNT WITHSCORES', () => { testUtils.isVersionGreaterThanHook([6, 2, 5]); it('transformArguments', () => { assert.deepEqual( - ZRANDMEMBER_COUNT_WITHSCORES.transformArguments('key', 1), + parseArgs(ZRANDMEMBER_COUNT_WITHSCORES, 'key', 1), ['ZRANDMEMBER', 'key', '1', 'WITHSCORES'] ); }); diff --git a/packages/client/lib/commands/ZRANDMEMBER_COUNT_WITHSCORES.ts b/packages/client/lib/commands/ZRANDMEMBER_COUNT_WITHSCORES.ts index 14c28d4b6c6..f096e9d807d 100644 --- a/packages/client/lib/commands/ZRANDMEMBER_COUNT_WITHSCORES.ts +++ b/packages/client/lib/commands/ZRANDMEMBER_COUNT_WITHSCORES.ts @@ -1,14 +1,13 @@ +import { CommandParser } from '../client/parser'; import { Command, RedisArgument } from '../RESP/types'; -import ZRANDMEMBER_COUNT from './ZRANDMEMBER_COUNT'; import { transformSortedSetReply } from './generic-transformers'; +import ZRANDMEMBER_COUNT from './ZRANDMEMBER_COUNT'; export default { - FIRST_KEY_INDEX: ZRANDMEMBER_COUNT.FIRST_KEY_INDEX, IS_READ_ONLY: ZRANDMEMBER_COUNT.IS_READ_ONLY, - transformArguments(key: RedisArgument, count: number) { - const args = ZRANDMEMBER_COUNT.transformArguments(key, count); - args.push('WITHSCORES'); - return args; + parseCommand(parser: CommandParser, key: RedisArgument, count: number) { + ZRANDMEMBER_COUNT.parseCommand(parser, key, count); + parser.push('WITHSCORES'); }, transformReply: transformSortedSetReply } as const satisfies Command; diff --git a/packages/client/lib/commands/ZRANGE.spec.ts b/packages/client/lib/commands/ZRANGE.spec.ts index db940062b2f..a780e4ef613 100644 --- a/packages/client/lib/commands/ZRANGE.spec.ts +++ b/packages/client/lib/commands/ZRANGE.spec.ts @@ -1,19 +1,20 @@ import { strict as assert } from 'node:assert'; import testUtils, { GLOBAL } from '../test-utils'; import ZRANGE from './ZRANGE'; +import { parseArgs } from './generic-transformers'; describe('ZRANGE', () => { describe('transformArguments', () => { it('simple', () => { assert.deepEqual( - ZRANGE.transformArguments('src', 0, 1), + parseArgs(ZRANGE, 'src', 0, 1), ['ZRANGE', 'src', '0', '1'] ); }); it('with BYSCORE', () => { assert.deepEqual( - ZRANGE.transformArguments('src', 0, 1, { + parseArgs(ZRANGE, 'src', 0, 1, { BY: 'SCORE' }), ['ZRANGE', 'src', '0', '1', 'BYSCORE'] @@ -22,7 +23,7 @@ describe('ZRANGE', () => { it('with BYLEX', () => { assert.deepEqual( - ZRANGE.transformArguments('src', 0, 1, { + parseArgs(ZRANGE, 'src', 0, 1, { BY: 'LEX' }), ['ZRANGE', 'src', '0', '1', 'BYLEX'] @@ -31,7 +32,7 @@ describe('ZRANGE', () => { it('with REV', () => { assert.deepEqual( - ZRANGE.transformArguments('src', 0, 1, { + parseArgs(ZRANGE, 'src', 0, 1, { REV: true }), ['ZRANGE', 'src', '0', '1', 'REV'] @@ -40,7 +41,7 @@ describe('ZRANGE', () => { it('with LIMIT', () => { assert.deepEqual( - ZRANGE.transformArguments('src', 0, 1, { + parseArgs(ZRANGE, 'src', 0, 1, { LIMIT: { offset: 0, count: 1 @@ -52,7 +53,7 @@ describe('ZRANGE', () => { it('with BY & REV & LIMIT', () => { assert.deepEqual( - ZRANGE.transformArguments('src', 0, 1, { + parseArgs(ZRANGE, 'src', 0, 1, { BY: 'SCORE', REV: true, LIMIT: { diff --git a/packages/client/lib/commands/ZRANGE.ts b/packages/client/lib/commands/ZRANGE.ts index 557044b67da..d1bc3433a50 100644 --- a/packages/client/lib/commands/ZRANGE.ts +++ b/packages/client/lib/commands/ZRANGE.ts @@ -1,3 +1,4 @@ +import { CommandParser } from '../client/parser'; import { RedisArgument, ArrayReply, BlobStringReply, Command } from '../RESP/types'; import { transformStringDoubleArgument } from './generic-transformers'; @@ -10,45 +11,54 @@ export interface ZRangeOptions { }; } +export function zRangeArgument( + min: RedisArgument | number, + max: RedisArgument | number, + options?: ZRangeOptions +) { + const args = [ + transformStringDoubleArgument(min), + transformStringDoubleArgument(max) + ] + + switch (options?.BY) { + case 'SCORE': + args.push('BYSCORE'); + break; + + case 'LEX': + args.push('BYLEX'); + break; + } + + if (options?.REV) { + args.push('REV'); + } + + if (options?.LIMIT) { + args.push( + 'LIMIT', + options.LIMIT.offset.toString(), + options.LIMIT.count.toString() + ); + } + + return args; +} + export default { - FIRST_KEY_INDEX: 1, + CACHEABLE: true, IS_READ_ONLY: true, - transformArguments( + parseCommand( + parser: CommandParser, key: RedisArgument, min: RedisArgument | number, max: RedisArgument | number, options?: ZRangeOptions ) { - const args = [ - 'ZRANGE', - key, - transformStringDoubleArgument(min), - transformStringDoubleArgument(max) - ]; - - switch (options?.BY) { - case 'SCORE': - args.push('BYSCORE'); - break; - - case 'LEX': - args.push('BYLEX'); - break; - } - - if (options?.REV) { - args.push('REV'); - } - - if (options?.LIMIT) { - args.push( - 'LIMIT', - options.LIMIT.offset.toString(), - options.LIMIT.count.toString() - ); - } - - return args; + parser.push('ZRANGE'); + parser.pushKey(key); + parser.pushVariadic(zRangeArgument(min, max, options)) }, transformReply: undefined as unknown as () => ArrayReply } as const satisfies Command; diff --git a/packages/client/lib/commands/ZRANGEBYLEX.spec.ts b/packages/client/lib/commands/ZRANGEBYLEX.spec.ts index f3f6f4bc0e5..942e184661a 100644 --- a/packages/client/lib/commands/ZRANGEBYLEX.spec.ts +++ b/packages/client/lib/commands/ZRANGEBYLEX.spec.ts @@ -1,19 +1,20 @@ import { strict as assert } from 'node:assert'; import testUtils, { GLOBAL } from '../test-utils'; import ZRANGEBYLEX from './ZRANGEBYLEX'; +import { parseArgs } from './generic-transformers'; describe('ZRANGEBYLEX', () => { describe('transformArguments', () => { it('simple', () => { assert.deepEqual( - ZRANGEBYLEX.transformArguments('src', '-', '+'), + parseArgs(ZRANGEBYLEX, 'src', '-', '+'), ['ZRANGEBYLEX', 'src', '-', '+'] ); }); it('with LIMIT', () => { assert.deepEqual( - ZRANGEBYLEX.transformArguments('src', '-', '+', { + parseArgs(ZRANGEBYLEX, 'src', '-', '+', { LIMIT: { offset: 0, count: 1 diff --git a/packages/client/lib/commands/ZRANGEBYLEX.ts b/packages/client/lib/commands/ZRANGEBYLEX.ts index afe7718f3c3..316d9745c7e 100644 --- a/packages/client/lib/commands/ZRANGEBYLEX.ts +++ b/packages/client/lib/commands/ZRANGEBYLEX.ts @@ -1,3 +1,4 @@ +import { CommandParser } from '../client/parser'; import { RedisArgument, ArrayReply, BlobStringReply, Command } from '../RESP/types'; import { transformStringDoubleArgument } from './generic-transformers'; @@ -9,26 +10,25 @@ export interface ZRangeByLexOptions { } export default { - FIRST_KEY_INDEX: 1, + CACHEABLE: true, IS_READ_ONLY: true, - transformArguments( + parseCommand( + parser: CommandParser, key: RedisArgument, min: RedisArgument, max: RedisArgument, options?: ZRangeByLexOptions ) { - const args = [ - 'ZRANGEBYLEX', - key, + parser.push('ZRANGEBYLEX'); + parser.pushKey(key); + parser.push( transformStringDoubleArgument(min), transformStringDoubleArgument(max) - ]; + ); if (options?.LIMIT) { - args.push('LIMIT', options.LIMIT.offset.toString(), options.LIMIT.count.toString()); + parser.push('LIMIT', options.LIMIT.offset.toString(), options.LIMIT.count.toString()); } - - return args; }, transformReply: undefined as unknown as () => ArrayReply } as const satisfies Command; diff --git a/packages/client/lib/commands/ZRANGEBYSCORE.spec.ts b/packages/client/lib/commands/ZRANGEBYSCORE.spec.ts index 61267ea7f2f..364882f21a9 100644 --- a/packages/client/lib/commands/ZRANGEBYSCORE.spec.ts +++ b/packages/client/lib/commands/ZRANGEBYSCORE.spec.ts @@ -1,19 +1,20 @@ import { strict as assert } from 'node:assert'; import testUtils, { GLOBAL } from '../test-utils'; import ZRANGEBYSCORE from './ZRANGEBYSCORE'; +import { parseArgs } from './generic-transformers'; describe('ZRANGEBYSCORE', () => { describe('transformArguments', () => { it('simple', () => { assert.deepEqual( - ZRANGEBYSCORE.transformArguments('src', 0, 1), + parseArgs(ZRANGEBYSCORE, 'src', 0, 1), ['ZRANGEBYSCORE', 'src', '0', '1'] ); }); it('with LIMIT', () => { assert.deepEqual( - ZRANGEBYSCORE.transformArguments('src', 0, 1, { + parseArgs(ZRANGEBYSCORE, 'src', 0, 1, { LIMIT: { offset: 0, count: 1 diff --git a/packages/client/lib/commands/ZRANGEBYSCORE.ts b/packages/client/lib/commands/ZRANGEBYSCORE.ts index e54c96380de..4d5471fdc0b 100644 --- a/packages/client/lib/commands/ZRANGEBYSCORE.ts +++ b/packages/client/lib/commands/ZRANGEBYSCORE.ts @@ -1,3 +1,4 @@ +import { CommandParser } from '../client/parser'; import { RedisArgument, ArrayReply, BlobStringReply, Command } from '../RESP/types'; import { transformStringDoubleArgument } from './generic-transformers'; @@ -11,26 +12,25 @@ export interface ZRangeByScoreOptions { export declare function transformReply(): Array; export default { - FIRST_KEY_INDEX: 1, + CACHEABLE: true, IS_READ_ONLY: true, - transformArguments( + parseCommand( + parser: CommandParser, key: RedisArgument, min: string | number, max: string | number, options?: ZRangeByScoreOptions ) { - const args = [ - 'ZRANGEBYSCORE', - key, + parser.push('ZRANGEBYSCORE'); + parser.pushKey(key); + parser.push( transformStringDoubleArgument(min), transformStringDoubleArgument(max) - ]; + ); if (options?.LIMIT) { - args.push('LIMIT', options.LIMIT.offset.toString(), options.LIMIT.count.toString()); + parser.push('LIMIT', options.LIMIT.offset.toString(), options.LIMIT.count.toString()); } - - return args; }, transformReply: undefined as unknown as () => ArrayReply } as const satisfies Command; diff --git a/packages/client/lib/commands/ZRANGEBYSCORE_WITHSCORES.spec.ts b/packages/client/lib/commands/ZRANGEBYSCORE_WITHSCORES.spec.ts index e70a97b0372..191eaa4e34f 100644 --- a/packages/client/lib/commands/ZRANGEBYSCORE_WITHSCORES.spec.ts +++ b/packages/client/lib/commands/ZRANGEBYSCORE_WITHSCORES.spec.ts @@ -1,19 +1,20 @@ import { strict as assert } from 'node:assert'; import testUtils, { GLOBAL } from '../test-utils'; import ZRANGEBYSCORE_WITHSCORES from './ZRANGEBYSCORE_WITHSCORES'; +import { parseArgs } from './generic-transformers'; describe('ZRANGEBYSCORE WITHSCORES', () => { describe('transformArguments', () => { it('simple', () => { assert.deepEqual( - ZRANGEBYSCORE_WITHSCORES.transformArguments('src', 0, 1), + parseArgs(ZRANGEBYSCORE_WITHSCORES, 'src', 0, 1), ['ZRANGEBYSCORE', 'src', '0', '1', 'WITHSCORES'] ); }); it('with LIMIT', () => { assert.deepEqual( - ZRANGEBYSCORE_WITHSCORES.transformArguments('src', 0, 1, { + parseArgs(ZRANGEBYSCORE_WITHSCORES, 'src', 0, 1, { LIMIT: { offset: 0, count: 1 diff --git a/packages/client/lib/commands/ZRANGEBYSCORE_WITHSCORES.ts b/packages/client/lib/commands/ZRANGEBYSCORE_WITHSCORES.ts index bfbe09c6e26..1a759b23dce 100644 --- a/packages/client/lib/commands/ZRANGEBYSCORE_WITHSCORES.ts +++ b/packages/client/lib/commands/ZRANGEBYSCORE_WITHSCORES.ts @@ -1,14 +1,15 @@ import { Command } from '../RESP/types'; -import ZRANGEBYSCORE from './ZRANGEBYSCORE'; import { transformSortedSetReply } from './generic-transformers'; +import ZRANGEBYSCORE from './ZRANGEBYSCORE'; export default { - FIRST_KEY_INDEX: ZRANGEBYSCORE.FIRST_KEY_INDEX, + CACHEABLE: ZRANGEBYSCORE.CACHEABLE, IS_READ_ONLY: ZRANGEBYSCORE.IS_READ_ONLY, - transformArguments(...args: Parameters) { - const redisArgs = ZRANGEBYSCORE.transformArguments(...args); - redisArgs.push('WITHSCORES'); - return redisArgs; + parseCommand(...args: Parameters) { + const parser = args[0]; + + ZRANGEBYSCORE.parseCommand(...args); + parser.push('WITHSCORES'); }, transformReply: transformSortedSetReply } as const satisfies Command; diff --git a/packages/client/lib/commands/ZRANGESTORE.spec.ts b/packages/client/lib/commands/ZRANGESTORE.spec.ts index 51315d3463b..c9708efd6fd 100644 --- a/packages/client/lib/commands/ZRANGESTORE.spec.ts +++ b/packages/client/lib/commands/ZRANGESTORE.spec.ts @@ -1,6 +1,7 @@ import { strict as assert } from 'node:assert'; import testUtils, { GLOBAL } from '../test-utils'; import ZRANGESTORE from './ZRANGESTORE'; +import { parseArgs } from './generic-transformers'; describe('ZRANGESTORE', () => { testUtils.isVersionGreaterThanHook([6, 2]); @@ -8,14 +9,14 @@ describe('ZRANGESTORE', () => { describe('transformArguments', () => { it('simple', () => { assert.deepEqual( - ZRANGESTORE.transformArguments('destination', 'source', 0, 1), + parseArgs(ZRANGESTORE, 'destination', 'source', 0, 1), ['ZRANGESTORE', 'destination', 'source', '0', '1'] ); }); it('with BYSCORE', () => { assert.deepEqual( - ZRANGESTORE.transformArguments('destination', 'source', 0, 1, { + parseArgs(ZRANGESTORE, 'destination', 'source', 0, 1, { BY: 'SCORE' }), ['ZRANGESTORE', 'destination', 'source', '0', '1', 'BYSCORE'] @@ -24,7 +25,7 @@ describe('ZRANGESTORE', () => { it('with BYLEX', () => { assert.deepEqual( - ZRANGESTORE.transformArguments('destination', 'source', 0, 1, { + parseArgs(ZRANGESTORE, 'destination', 'source', 0, 1, { BY: 'LEX' }), ['ZRANGESTORE', 'destination', 'source', '0', '1', 'BYLEX'] @@ -33,7 +34,7 @@ describe('ZRANGESTORE', () => { it('with REV', () => { assert.deepEqual( - ZRANGESTORE.transformArguments('destination', 'source', 0, 1, { + parseArgs(ZRANGESTORE, 'destination', 'source', 0, 1, { REV: true }), ['ZRANGESTORE', 'destination', 'source', '0', '1', 'REV'] @@ -42,7 +43,7 @@ describe('ZRANGESTORE', () => { it('with LIMIT', () => { assert.deepEqual( - ZRANGESTORE.transformArguments('destination', 'source', 0, 1, { + parseArgs(ZRANGESTORE, 'destination', 'source', 0, 1, { LIMIT: { offset: 0, count: 1 @@ -54,7 +55,7 @@ describe('ZRANGESTORE', () => { it('with BY & REV & LIMIT', () => { assert.deepEqual( - ZRANGESTORE.transformArguments('destination', 'source', 0, 1, { + parseArgs(ZRANGESTORE, 'destination', 'source', 0, 1, { BY: 'SCORE', REV: true, LIMIT: { diff --git a/packages/client/lib/commands/ZRANGESTORE.ts b/packages/client/lib/commands/ZRANGESTORE.ts index 96f10120b87..f73e93a506f 100644 --- a/packages/client/lib/commands/ZRANGESTORE.ts +++ b/packages/client/lib/commands/ZRANGESTORE.ts @@ -1,3 +1,4 @@ +import { CommandParser } from '../client/parser'; import { RedisArgument, NumberReply, Command } from '../RESP/types'; import { transformStringDoubleArgument } from './generic-transformers'; @@ -11,42 +12,40 @@ export interface ZRangeStoreOptions { } export default { - FIRST_KEY_INDEX: 1, IS_READ_ONLY: false, - transformArguments( + parseCommand( + parser: CommandParser, destination: RedisArgument, source: RedisArgument, min: RedisArgument | number, max: RedisArgument | number, options?: ZRangeStoreOptions ) { - const args = [ - 'ZRANGESTORE', - destination, - source, - transformStringDoubleArgument(min), + parser.push('ZRANGESTORE'); + parser.pushKey(destination); + parser.pushKey(source); + parser.push( + transformStringDoubleArgument(min), transformStringDoubleArgument(max) - ]; + ); switch (options?.BY) { case 'SCORE': - args.push('BYSCORE'); + parser.push('BYSCORE'); break; case 'LEX': - args.push('BYLEX'); + parser.push('BYLEX'); break; } if (options?.REV) { - args.push('REV'); + parser.push('REV'); } if (options?.LIMIT) { - args.push('LIMIT', options.LIMIT.offset.toString(), options.LIMIT.count.toString()); + parser.push('LIMIT', options.LIMIT.offset.toString(), options.LIMIT.count.toString()); } - - return args; }, transformReply: undefined as unknown as () => NumberReply } as const satisfies Command; diff --git a/packages/client/lib/commands/ZRANGE_WITHSCORES.spec.ts b/packages/client/lib/commands/ZRANGE_WITHSCORES.spec.ts index 038c150a675..e3009a6eadb 100644 --- a/packages/client/lib/commands/ZRANGE_WITHSCORES.spec.ts +++ b/packages/client/lib/commands/ZRANGE_WITHSCORES.spec.ts @@ -1,19 +1,20 @@ import { strict as assert } from 'node:assert'; import testUtils, { GLOBAL } from '../test-utils'; import ZRANGE_WITHSCORES from './ZRANGE_WITHSCORES'; +import { parseArgs } from './generic-transformers'; describe('ZRANGE WITHSCORES', () => { describe('transformArguments', () => { it('simple', () => { assert.deepEqual( - ZRANGE_WITHSCORES.transformArguments('src', 0, 1), + parseArgs(ZRANGE_WITHSCORES, 'src', 0, 1), ['ZRANGE', 'src', '0', '1', 'WITHSCORES'] ); }); it('with BY', () => { assert.deepEqual( - ZRANGE_WITHSCORES.transformArguments('src', 0, 1, { + parseArgs(ZRANGE_WITHSCORES, 'src', 0, 1, { BY: 'SCORE' }), ['ZRANGE', 'src', '0', '1', 'BYSCORE', 'WITHSCORES'] @@ -22,7 +23,7 @@ describe('ZRANGE WITHSCORES', () => { it('with REV', () => { assert.deepEqual( - ZRANGE_WITHSCORES.transformArguments('src', 0, 1, { + parseArgs(ZRANGE_WITHSCORES, 'src', 0, 1, { REV: true }), ['ZRANGE', 'src', '0', '1', 'REV', 'WITHSCORES'] @@ -31,7 +32,7 @@ describe('ZRANGE WITHSCORES', () => { it('with LIMIT', () => { assert.deepEqual( - ZRANGE_WITHSCORES.transformArguments('src', 0, 1, { + parseArgs(ZRANGE_WITHSCORES, 'src', 0, 1, { LIMIT: { offset: 0, count: 1 @@ -43,7 +44,7 @@ describe('ZRANGE WITHSCORES', () => { it('with BY & REV & LIMIT', () => { assert.deepEqual( - ZRANGE_WITHSCORES.transformArguments('src', 0, 1, { + parseArgs(ZRANGE_WITHSCORES, 'src', 0, 1, { BY: 'SCORE', REV: true, LIMIT: { diff --git a/packages/client/lib/commands/ZRANGE_WITHSCORES.ts b/packages/client/lib/commands/ZRANGE_WITHSCORES.ts index cfa90e99ea4..7e6cf00cf2e 100644 --- a/packages/client/lib/commands/ZRANGE_WITHSCORES.ts +++ b/packages/client/lib/commands/ZRANGE_WITHSCORES.ts @@ -1,14 +1,15 @@ import { Command } from '../RESP/types'; -import ZRANGE from './ZRANGE'; import { transformSortedSetReply } from './generic-transformers'; +import ZRANGE from './ZRANGE'; export default { - FIRST_KEY_INDEX: ZRANGE.FIRST_KEY_INDEX, + CACHEABLE: ZRANGE.CACHEABLE, IS_READ_ONLY: ZRANGE.IS_READ_ONLY, - transformArguments(...args: Parameters) { - const redisArgs = ZRANGE.transformArguments(...args); - redisArgs.push('WITHSCORES'); - return redisArgs; + parseCommand(...args: Parameters) { + const parser = args[0]; + + ZRANGE.parseCommand(...args); + parser.push('WITHSCORES'); }, transformReply: transformSortedSetReply } as const satisfies Command; diff --git a/packages/client/lib/commands/ZRANK.spec.ts b/packages/client/lib/commands/ZRANK.spec.ts index 9341709bda3..480f75f66e1 100644 --- a/packages/client/lib/commands/ZRANK.spec.ts +++ b/packages/client/lib/commands/ZRANK.spec.ts @@ -1,11 +1,12 @@ import { strict as assert } from 'node:assert'; import testUtils, { GLOBAL } from '../test-utils'; import ZRANK from './ZRANK'; +import { parseArgs } from './generic-transformers'; describe('ZRANK', () => { it('transformArguments', () => { assert.deepEqual( - ZRANK.transformArguments('key', 'member'), + parseArgs(ZRANK, 'key', 'member'), ['ZRANK', 'key', 'member'] ); }); diff --git a/packages/client/lib/commands/ZRANK.ts b/packages/client/lib/commands/ZRANK.ts index 11184c0a28f..045e9ef8c25 100644 --- a/packages/client/lib/commands/ZRANK.ts +++ b/packages/client/lib/commands/ZRANK.ts @@ -1,10 +1,13 @@ +import { CommandParser } from '../client/parser'; import { RedisArgument, NumberReply, NullReply, Command } from '../RESP/types'; export default { - FIRST_KEY_INDEX: 1, + CACHEABLE: true, IS_READ_ONLY: true, - transformArguments(key: RedisArgument, member: RedisArgument) { - return ['ZRANK', key, member]; + parseCommand(parser: CommandParser, key: RedisArgument, member: RedisArgument) { + parser.push('ZRANK'); + parser.pushKey(key); + parser.push(member); }, transformReply: undefined as unknown as () => NumberReply | NullReply } as const satisfies Command; diff --git a/packages/client/lib/commands/ZRANK_WITHSCORE.spec.ts b/packages/client/lib/commands/ZRANK_WITHSCORE.spec.ts index b571e0f7071..9fa7cb1f6fd 100644 --- a/packages/client/lib/commands/ZRANK_WITHSCORE.spec.ts +++ b/packages/client/lib/commands/ZRANK_WITHSCORE.spec.ts @@ -1,13 +1,14 @@ import { strict as assert } from 'node:assert'; import testUtils, { GLOBAL } from '../test-utils'; import ZRANK_WITHSCORE from './ZRANK_WITHSCORE'; +import { parseArgs } from './generic-transformers'; describe('ZRANK WITHSCORE', () => { testUtils.isVersionGreaterThanHook([7, 2]); it('transformArguments', () => { assert.deepEqual( - ZRANK_WITHSCORE.transformArguments('key', 'member'), + parseArgs(ZRANK_WITHSCORE, 'key', 'member'), ['ZRANK', 'key', 'member', 'WITHSCORE'] ); }); diff --git a/packages/client/lib/commands/ZRANK_WITHSCORE.ts b/packages/client/lib/commands/ZRANK_WITHSCORE.ts index 39c788535e3..dc2e48b362d 100644 --- a/packages/client/lib/commands/ZRANK_WITHSCORE.ts +++ b/packages/client/lib/commands/ZRANK_WITHSCORE.ts @@ -2,12 +2,13 @@ import { NullReply, TuplesReply, NumberReply, BlobStringReply, DoubleReply, Unwr import ZRANK from './ZRANK'; export default { - FIRST_KEY_INDEX: ZRANK.FIRST_KEY_INDEX, + CACHEABLE: ZRANK.CACHEABLE, IS_READ_ONLY: ZRANK.IS_READ_ONLY, - transformArguments(...args: Parameters) { - const redisArgs = ZRANK.transformArguments(...args); - redisArgs.push('WITHSCORE'); - return redisArgs; + parseCommand(...args: Parameters) { + const parser = args[0]; + + ZRANK.parseCommand(...args); + parser.push('WITHSCORE'); }, transformReply: { 2: (reply: UnwrapReply>) => { diff --git a/packages/client/lib/commands/ZREM.spec.ts b/packages/client/lib/commands/ZREM.spec.ts index 4b203c9f4eb..ac65b3d0139 100644 --- a/packages/client/lib/commands/ZREM.spec.ts +++ b/packages/client/lib/commands/ZREM.spec.ts @@ -1,19 +1,20 @@ import { strict as assert } from 'node:assert'; import testUtils, { GLOBAL } from '../test-utils'; import ZREM from './ZREM'; +import { parseArgs } from './generic-transformers'; describe('ZREM', () => { describe('transformArguments', () => { it('string', () => { assert.deepEqual( - ZREM.transformArguments('key', 'member'), + parseArgs(ZREM, 'key', 'member'), ['ZREM', 'key', 'member'] ); }); it('array', () => { assert.deepEqual( - ZREM.transformArguments('key', ['1', '2']), + parseArgs(ZREM, 'key', ['1', '2']), ['ZREM', 'key', '1', '2'] ); }); diff --git a/packages/client/lib/commands/ZREM.ts b/packages/client/lib/commands/ZREM.ts index 54f55841fce..c8ba0ec02a6 100644 --- a/packages/client/lib/commands/ZREM.ts +++ b/packages/client/lib/commands/ZREM.ts @@ -1,14 +1,17 @@ +import { CommandParser } from '../client/parser'; import { RedisArgument, NumberReply, Command } from '../RESP/types'; -import { RedisVariadicArgument, pushVariadicArguments } from './generic-transformers'; +import { RedisVariadicArgument } from './generic-transformers'; export default { - FIRST_KEY_INDEX: 1, IS_READ_ONLY: false, - transformArguments( + parseCommand( + parser: CommandParser, key: RedisArgument, member: RedisVariadicArgument ) { - return pushVariadicArguments(['ZREM', key], member); + parser.push('ZREM'); + parser.pushKey(key); + parser.pushVariadic(member); }, transformReply: undefined as unknown as () => NumberReply } as const satisfies Command; diff --git a/packages/client/lib/commands/ZREMRANGEBYLEX.spec.ts b/packages/client/lib/commands/ZREMRANGEBYLEX.spec.ts index 9f29c3cdcf7..b141b7679ee 100644 --- a/packages/client/lib/commands/ZREMRANGEBYLEX.spec.ts +++ b/packages/client/lib/commands/ZREMRANGEBYLEX.spec.ts @@ -1,11 +1,12 @@ import { strict as assert } from 'node:assert'; import testUtils, { GLOBAL } from '../test-utils'; import ZREMRANGEBYLEX from './ZREMRANGEBYLEX'; +import { parseArgs } from './generic-transformers'; describe('ZREMRANGEBYLEX', () => { it('transformArguments', () => { assert.deepEqual( - ZREMRANGEBYLEX.transformArguments('key', '[a', '[b'), + parseArgs(ZREMRANGEBYLEX, 'key', '[a', '[b'), ['ZREMRANGEBYLEX', 'key', '[a', '[b'] ); }); diff --git a/packages/client/lib/commands/ZREMRANGEBYLEX.ts b/packages/client/lib/commands/ZREMRANGEBYLEX.ts index e3cd7013ac2..5d7e1a21bb0 100644 --- a/packages/client/lib/commands/ZREMRANGEBYLEX.ts +++ b/packages/client/lib/commands/ZREMRANGEBYLEX.ts @@ -1,20 +1,21 @@ +import { CommandParser } from '../client/parser'; import { NumberReply, Command, RedisArgument } from '../RESP/types'; import { transformStringDoubleArgument } from './generic-transformers'; export default { - FIRST_KEY_INDEX: 1, IS_READ_ONLY: false, - transformArguments( + parseCommand( + parser: CommandParser, key: RedisArgument, min: RedisArgument | number, max: RedisArgument | number ) { - return [ - 'ZREMRANGEBYLEX', - key, + parser.push('ZREMRANGEBYLEX'); + parser.pushKey(key); + parser.push( transformStringDoubleArgument(min), transformStringDoubleArgument(max) - ]; + ); }, transformReply: undefined as unknown as () => NumberReply } as const satisfies Command; diff --git a/packages/client/lib/commands/ZREMRANGEBYRANK.spec.ts b/packages/client/lib/commands/ZREMRANGEBYRANK.spec.ts index 12627083e1a..19f54466c20 100644 --- a/packages/client/lib/commands/ZREMRANGEBYRANK.spec.ts +++ b/packages/client/lib/commands/ZREMRANGEBYRANK.spec.ts @@ -1,11 +1,12 @@ import { strict as assert } from 'node:assert'; import testUtils, { GLOBAL } from '../test-utils'; import ZREMRANGEBYRANK from './ZREMRANGEBYRANK'; +import { parseArgs } from './generic-transformers'; describe('ZREMRANGEBYRANK', () => { it('transformArguments', () => { assert.deepEqual( - ZREMRANGEBYRANK.transformArguments('key', 0, 1), + parseArgs(ZREMRANGEBYRANK, 'key', 0, 1), ['ZREMRANGEBYRANK', 'key', '0', '1'] ); }); diff --git a/packages/client/lib/commands/ZREMRANGEBYRANK.ts b/packages/client/lib/commands/ZREMRANGEBYRANK.ts index 986de33060e..0a2eb3fadf3 100644 --- a/packages/client/lib/commands/ZREMRANGEBYRANK.ts +++ b/packages/client/lib/commands/ZREMRANGEBYRANK.ts @@ -1,13 +1,20 @@ +import { CommandParser } from '../client/parser'; import { RedisArgument, NumberReply, Command } from '../RESP/types'; export default { - FIRST_KEY_INDEX: 1, IS_READ_ONLY: false, - transformArguments( + parseCommand( + parser: CommandParser, key: RedisArgument, start: number, - stop: number) { - return ['ZREMRANGEBYRANK', key, start.toString(), stop.toString()]; + stop: number + ) { + parser.push('ZREMRANGEBYRANK'); + parser.pushKey(key); + parser.push( + start.toString(), + stop.toString() + ); }, transformReply: undefined as unknown as () => NumberReply } as const satisfies Command; diff --git a/packages/client/lib/commands/ZREMRANGEBYSCORE.spec.ts b/packages/client/lib/commands/ZREMRANGEBYSCORE.spec.ts index fb3ba4e718c..856692ef8f5 100644 --- a/packages/client/lib/commands/ZREMRANGEBYSCORE.spec.ts +++ b/packages/client/lib/commands/ZREMRANGEBYSCORE.spec.ts @@ -1,11 +1,12 @@ import { strict as assert } from 'node:assert'; import testUtils, { GLOBAL } from '../test-utils'; +import { parseArgs } from './generic-transformers'; import ZREMRANGEBYSCORE from './ZREMRANGEBYSCORE'; describe('ZREMRANGEBYSCORE', () => { it('transformArguments', () => { assert.deepEqual( - ZREMRANGEBYSCORE.transformArguments('key', 0, 1), + parseArgs(ZREMRANGEBYSCORE, 'key', 0, 1), ['ZREMRANGEBYSCORE', 'key', '0', '1'] ); }); diff --git a/packages/client/lib/commands/ZREMRANGEBYSCORE.ts b/packages/client/lib/commands/ZREMRANGEBYSCORE.ts index 7050f2627a7..3d23d875948 100644 --- a/packages/client/lib/commands/ZREMRANGEBYSCORE.ts +++ b/packages/client/lib/commands/ZREMRANGEBYSCORE.ts @@ -1,20 +1,21 @@ +import { CommandParser } from '../client/parser'; import { RedisArgument, NumberReply, Command } from '../RESP/types'; import { transformStringDoubleArgument } from './generic-transformers'; export default { - FIRST_KEY_INDEX: 1, IS_READ_ONLY: false, - transformArguments( + parseCommand( + parser: CommandParser, key: RedisArgument, min: RedisArgument | number, max: RedisArgument | number, ) { - return [ - 'ZREMRANGEBYSCORE', - key, + parser.push('ZREMRANGEBYSCORE'); + parser.pushKey(key); + parser.push( transformStringDoubleArgument(min), transformStringDoubleArgument(max) - ]; + ); }, transformReply: undefined as unknown as () => NumberReply } as const satisfies Command; diff --git a/packages/client/lib/commands/ZREVRANK.spec.ts b/packages/client/lib/commands/ZREVRANK.spec.ts index 418773b6003..c89f528eb1c 100644 --- a/packages/client/lib/commands/ZREVRANK.spec.ts +++ b/packages/client/lib/commands/ZREVRANK.spec.ts @@ -1,11 +1,12 @@ import { strict as assert } from 'node:assert'; import testUtils, { GLOBAL } from '../test-utils'; +import { parseArgs } from './generic-transformers'; import ZREVRANK from './ZREVRANK'; describe('ZREVRANK', () => { it('transformArguments', () => { assert.deepEqual( - ZREVRANK.transformArguments('key', 'member'), + parseArgs(ZREVRANK, 'key', 'member'), ['ZREVRANK', 'key', 'member'] ); }); diff --git a/packages/client/lib/commands/ZREVRANK.ts b/packages/client/lib/commands/ZREVRANK.ts index 3bf52d21de5..d48dc68adc2 100644 --- a/packages/client/lib/commands/ZREVRANK.ts +++ b/packages/client/lib/commands/ZREVRANK.ts @@ -1,10 +1,13 @@ +import { CommandParser } from '../client/parser'; import { NumberReply, NullReply, Command, RedisArgument } from '../RESP/types'; export default { - FIRST_KEY_INDEX: 1, + CACHEABLE: true, IS_READ_ONLY: true, - transformArguments(key: RedisArgument, member: RedisArgument) { - return ['ZREVRANK', key, member]; + parseCommand(parser: CommandParser, key: RedisArgument, member: RedisArgument) { + parser.push('ZREVRANK'); + parser.pushKey(key); + parser.push(member); }, transformReply: undefined as unknown as () => NumberReply | NullReply } as const satisfies Command; diff --git a/packages/client/lib/commands/ZSCAN.spec.ts b/packages/client/lib/commands/ZSCAN.spec.ts index ebeaad2a4d0..f8064aea41e 100644 --- a/packages/client/lib/commands/ZSCAN.spec.ts +++ b/packages/client/lib/commands/ZSCAN.spec.ts @@ -1,19 +1,20 @@ import { strict as assert } from 'node:assert'; import testUtils, { GLOBAL } from '../test-utils'; +import { parseArgs } from './generic-transformers'; import ZSCAN from './ZSCAN'; describe('ZSCAN', () => { describe('transformArguments', () => { it('cusror only', () => { assert.deepEqual( - ZSCAN.transformArguments('key', '0'), + parseArgs(ZSCAN, 'key', '0'), ['ZSCAN', 'key', '0'] ); }); it('with MATCH', () => { assert.deepEqual( - ZSCAN.transformArguments('key', '0', { + parseArgs(ZSCAN, 'key', '0', { MATCH: 'pattern' }), ['ZSCAN', 'key', '0', 'MATCH', 'pattern'] @@ -22,7 +23,7 @@ describe('ZSCAN', () => { it('with COUNT', () => { assert.deepEqual( - ZSCAN.transformArguments('key', '0', { + parseArgs(ZSCAN, 'key', '0', { COUNT: 1 }), ['ZSCAN', 'key', '0', 'COUNT', '1'] @@ -31,7 +32,7 @@ describe('ZSCAN', () => { it('with MATCH & COUNT', () => { assert.deepEqual( - ZSCAN.transformArguments('key', '0', { + parseArgs(ZSCAN, 'key', '0', { MATCH: 'pattern', COUNT: 1 }), diff --git a/packages/client/lib/commands/ZSCAN.ts b/packages/client/lib/commands/ZSCAN.ts index 853cdf098f6..051235033eb 100644 --- a/packages/client/lib/commands/ZSCAN.ts +++ b/packages/client/lib/commands/ZSCAN.ts @@ -1,5 +1,6 @@ +import { CommandParser } from '../client/parser'; import { RedisArgument, ArrayReply, BlobStringReply, Command } from '../RESP/types'; -import { ScanCommonOptions, pushScanArguments } from './SCAN'; +import { ScanCommonOptions, parseScanArguments } from './SCAN'; import { transformSortedSetReply } from './generic-transformers'; export interface HScanEntry { @@ -8,14 +9,16 @@ export interface HScanEntry { } export default { - FIRST_KEY_INDEX: 1, IS_READ_ONLY: true, - transformArguments( + parseCommand( + parser: CommandParser, key: RedisArgument, cursor: RedisArgument, options?: ScanCommonOptions ) { - return pushScanArguments(['ZSCAN', key], cursor, options); + parser.push('ZSCAN'); + parser.pushKey(key); + parseScanArguments(parser, cursor, options); }, transformReply([cursor, rawMembers]: [BlobStringReply, ArrayReply]) { return { diff --git a/packages/client/lib/commands/ZSCORE.spec.ts b/packages/client/lib/commands/ZSCORE.spec.ts index 3d8df6640c8..4229ab7aac0 100644 --- a/packages/client/lib/commands/ZSCORE.spec.ts +++ b/packages/client/lib/commands/ZSCORE.spec.ts @@ -1,11 +1,12 @@ import { strict as assert } from 'node:assert'; import testUtils, { GLOBAL } from '../test-utils'; import ZSCORE from './ZSCORE'; +import { parseArgs } from './generic-transformers'; describe('ZSCORE', () => { it('transformArguments', () => { assert.deepEqual( - ZSCORE.transformArguments('key', 'member'), + parseArgs(ZSCORE, 'key', 'member'), ['ZSCORE', 'key', 'member'] ); }); diff --git a/packages/client/lib/commands/ZSCORE.ts b/packages/client/lib/commands/ZSCORE.ts index 0d3db752e1c..23b52901078 100644 --- a/packages/client/lib/commands/ZSCORE.ts +++ b/packages/client/lib/commands/ZSCORE.ts @@ -1,12 +1,15 @@ +import { CommandParser } from '../client/parser'; import { RedisArgument, Command } from '../RESP/types'; import { transformNullableDoubleReply } from './generic-transformers'; export default { - FIRST_KEY_INDEX: 1, + CACHEABLE: true, IS_READ_ONLY: true, - transformArguments(key: RedisArgument, member: RedisArgument) { - return ['ZSCORE', key, member]; + parseCommand(parser: CommandParser, key: RedisArgument, member: RedisArgument) { + parser.push('ZSCORE'); + parser.pushKey(key); + parser.push(member); }, transformReply: transformNullableDoubleReply } as const satisfies Command; diff --git a/packages/client/lib/commands/ZUNION.spec.ts b/packages/client/lib/commands/ZUNION.spec.ts index f66bb7abc63..b4dbb4de603 100644 --- a/packages/client/lib/commands/ZUNION.spec.ts +++ b/packages/client/lib/commands/ZUNION.spec.ts @@ -1,6 +1,7 @@ import { strict as assert } from 'node:assert'; import testUtils, { GLOBAL } from '../test-utils'; import ZUNION from './ZUNION'; +import { parseArgs } from './generic-transformers'; describe('ZUNION', () => { testUtils.isVersionGreaterThanHook([6, 2]); @@ -8,21 +9,21 @@ describe('ZUNION', () => { describe('transformArguments', () => { it('key (string)', () => { assert.deepEqual( - ZUNION.transformArguments('key'), + parseArgs(ZUNION, 'key'), ['ZUNION', '1', 'key'] ); }); it('keys (Array)', () => { assert.deepEqual( - ZUNION.transformArguments(['1', '2']), + parseArgs(ZUNION, ['1', '2']), ['ZUNION', '2', '1', '2'] ); }); it('key & weight', () => { assert.deepEqual( - ZUNION.transformArguments({ + parseArgs(ZUNION, { key: 'key', weight: 1 }), @@ -32,7 +33,7 @@ describe('ZUNION', () => { it('keys & weights', () => { assert.deepEqual( - ZUNION.transformArguments([{ + parseArgs(ZUNION, [{ key: 'a', weight: 1 }, { @@ -45,7 +46,7 @@ describe('ZUNION', () => { it('with AGGREGATE', () => { assert.deepEqual( - ZUNION.transformArguments('key', { + parseArgs(ZUNION, 'key', { AGGREGATE: 'SUM' }), ['ZUNION', '1', 'key', 'AGGREGATE', 'SUM'] diff --git a/packages/client/lib/commands/ZUNION.ts b/packages/client/lib/commands/ZUNION.ts index 09614b9dc01..a91dc68bc09 100644 --- a/packages/client/lib/commands/ZUNION.ts +++ b/packages/client/lib/commands/ZUNION.ts @@ -1,24 +1,20 @@ +import { CommandParser } from '../client/parser'; import { ArrayReply, BlobStringReply, Command } from '../RESP/types'; -import { ZKeys, pushZKeysArguments } from './generic-transformers'; +import { ZKeys, parseZKeysArguments } from './generic-transformers'; export interface ZUnionOptions { AGGREGATE?: 'SUM' | 'MIN' | 'MAX'; } export default { - FIRST_KEY_INDEX: 2, IS_READ_ONLY: true, - transformArguments( - keys: ZKeys, - options?: ZUnionOptions - ) { - const args = pushZKeysArguments(['ZUNION'], keys); + parseCommand(parser: CommandParser, keys: ZKeys, options?: ZUnionOptions) { + parser.push('ZUNION'); + parseZKeysArguments(parser, keys); if (options?.AGGREGATE) { - args.push('AGGREGATE', options.AGGREGATE); + parser.push('AGGREGATE', options.AGGREGATE); } - - return args; }, transformReply: undefined as unknown as () => ArrayReply } as const satisfies Command; diff --git a/packages/client/lib/commands/ZUNIONSTORE.spec.ts b/packages/client/lib/commands/ZUNIONSTORE.spec.ts index 7a01e80f0c0..a369a649311 100644 --- a/packages/client/lib/commands/ZUNIONSTORE.spec.ts +++ b/packages/client/lib/commands/ZUNIONSTORE.spec.ts @@ -1,26 +1,27 @@ import { strict as assert } from 'node:assert'; import testUtils, { GLOBAL } from '../test-utils'; import ZUNIONSTORE from './ZUNIONSTORE'; +import { parseArgs } from './generic-transformers'; describe('ZUNIONSTORE', () => { describe('transformArguments', () => { it('key (string)', () => { assert.deepEqual( - ZUNIONSTORE.transformArguments('destination', 'source'), + parseArgs(ZUNIONSTORE, 'destination', 'source'), ['ZUNIONSTORE', 'destination', '1', 'source'] ); }); it('keys (Array)', () => { assert.deepEqual( - ZUNIONSTORE.transformArguments('destination', ['1', '2']), + parseArgs(ZUNIONSTORE, 'destination', ['1', '2']), ['ZUNIONSTORE', 'destination', '2', '1', '2'] ); }); it('key & weight', () => { assert.deepEqual( - ZUNIONSTORE.transformArguments('destination', { + parseArgs(ZUNIONSTORE, 'destination', { key: 'source', weight: 1 }), @@ -30,7 +31,7 @@ describe('ZUNIONSTORE', () => { it('keys & weights', () => { assert.deepEqual( - ZUNIONSTORE.transformArguments('destination', [{ + parseArgs(ZUNIONSTORE, 'destination', [{ key: 'a', weight: 1 }, { @@ -43,7 +44,7 @@ describe('ZUNIONSTORE', () => { it('with AGGREGATE', () => { assert.deepEqual( - ZUNIONSTORE.transformArguments('destination', 'source', { + parseArgs(ZUNIONSTORE, 'destination', 'source', { AGGREGATE: 'SUM' }), ['ZUNIONSTORE', 'destination', '1', 'source', 'AGGREGATE', 'SUM'] diff --git a/packages/client/lib/commands/ZUNIONSTORE.ts b/packages/client/lib/commands/ZUNIONSTORE.ts index a14d3ba31c9..c88f5a5a6f9 100644 --- a/packages/client/lib/commands/ZUNIONSTORE.ts +++ b/packages/client/lib/commands/ZUNIONSTORE.ts @@ -1,25 +1,26 @@ +import { CommandParser } from '../client/parser'; import { RedisArgument, NumberReply, Command, } from '../RESP/types'; -import { ZKeys, pushZKeysArguments } from './generic-transformers'; +import { ZKeys, parseZKeysArguments } from './generic-transformers'; export interface ZUnionOptions { AGGREGATE?: 'SUM' | 'MIN' | 'MAX'; } export default { - FIRST_KEY_INDEX: 1, IS_READ_ONLY: false, - transformArguments( + parseCommand( + parser: CommandParser, destination: RedisArgument, keys: ZKeys, options?: ZUnionOptions - ) { - const args = pushZKeysArguments(['ZUNIONSTORE', destination], keys); - + ): any { + parser.push('ZUNIONSTORE'); + parser.pushKey(destination); + parseZKeysArguments(parser, keys); + if (options?.AGGREGATE) { - args.push('AGGREGATE', options.AGGREGATE); + parser.push('AGGREGATE', options.AGGREGATE); } - - return args; }, transformReply: undefined as unknown as () => NumberReply } as const satisfies Command; diff --git a/packages/client/lib/commands/ZUNION_WITHSCORES.spec.ts b/packages/client/lib/commands/ZUNION_WITHSCORES.spec.ts index bbf3ec676e8..dee735fc99f 100644 --- a/packages/client/lib/commands/ZUNION_WITHSCORES.spec.ts +++ b/packages/client/lib/commands/ZUNION_WITHSCORES.spec.ts @@ -1,6 +1,7 @@ import { strict as assert } from 'node:assert'; import testUtils, { GLOBAL } from '../test-utils'; import ZUNION_WITHSCORES from './ZUNION_WITHSCORES'; +import { parseArgs } from './generic-transformers'; describe('ZUNION WITHSCORES', () => { testUtils.isVersionGreaterThanHook([6, 2]); @@ -8,21 +9,21 @@ describe('ZUNION WITHSCORES', () => { describe('transformArguments', () => { it('key (string)', () => { assert.deepEqual( - ZUNION_WITHSCORES.transformArguments('key'), + parseArgs(ZUNION_WITHSCORES, 'key'), ['ZUNION', '1', 'key', 'WITHSCORES'] ); }); it('keys (Array)', () => { assert.deepEqual( - ZUNION_WITHSCORES.transformArguments(['1', '2']), + parseArgs(ZUNION_WITHSCORES, ['1', '2']), ['ZUNION', '2', '1', '2', 'WITHSCORES'] ); }); it('key & weight', () => { assert.deepEqual( - ZUNION_WITHSCORES.transformArguments({ + parseArgs(ZUNION_WITHSCORES, { key: 'key', weight: 1 }), @@ -32,7 +33,7 @@ describe('ZUNION WITHSCORES', () => { it('keys & weights', () => { assert.deepEqual( - ZUNION_WITHSCORES.transformArguments([{ + parseArgs(ZUNION_WITHSCORES, [{ key: 'a', weight: 1 }, { @@ -45,7 +46,7 @@ describe('ZUNION WITHSCORES', () => { it('with AGGREGATE', () => { assert.deepEqual( - ZUNION_WITHSCORES.transformArguments('key', { + parseArgs(ZUNION_WITHSCORES, 'key', { AGGREGATE: 'SUM' }), ['ZUNION', '1', 'key', 'AGGREGATE', 'SUM', 'WITHSCORES'] diff --git a/packages/client/lib/commands/ZUNION_WITHSCORES.ts b/packages/client/lib/commands/ZUNION_WITHSCORES.ts index d0895a3de76..c62df55518f 100644 --- a/packages/client/lib/commands/ZUNION_WITHSCORES.ts +++ b/packages/client/lib/commands/ZUNION_WITHSCORES.ts @@ -1,14 +1,15 @@ import { Command } from '../RESP/types'; -import ZUNION from './ZUNION'; import { transformSortedSetReply } from './generic-transformers'; +import ZUNION from './ZUNION'; + export default { - FIRST_KEY_INDEX: ZUNION.FIRST_KEY_INDEX, IS_READ_ONLY: ZUNION.IS_READ_ONLY, - transformArguments(...args: Parameters) { - const redisArgs = ZUNION.transformArguments(...args); - redisArgs.push('WITHSCORES'); - return redisArgs; + parseCommand(...args: Parameters) { + const parser = args[0]; + + ZUNION.parseCommand(...args); + parser.push('WITHSCORES'); }, transformReply: transformSortedSetReply } as const satisfies Command; diff --git a/packages/client/lib/commands/generic-transformers.ts b/packages/client/lib/commands/generic-transformers.ts index cc7100d90e6..fc139a948e0 100644 --- a/packages/client/lib/commands/generic-transformers.ts +++ b/packages/client/lib/commands/generic-transformers.ts @@ -1,5 +1,6 @@ +import { BasicCommandParser, CommandParser } from '../client/parser'; import { RESP_TYPES } from '../RESP/decoder'; -import { UnwrapReply, ArrayReply, BlobStringReply, BooleanReply, CommandArguments, DoubleReply, NullReply, NumberReply, RedisArgument, TuplesReply, MapReply, TypeMapping } from '../RESP/types'; +import { UnwrapReply, ArrayReply, BlobStringReply, BooleanReply, CommandArguments, DoubleReply, NullReply, NumberReply, RedisArgument, TuplesReply, MapReply, TypeMapping, Command } from '../RESP/types'; export function isNullReply(reply: unknown): reply is NullReply { return reply === null; @@ -107,6 +108,19 @@ export interface Stringable { toString(): string; } +export function transformTuplesToMap( + reply: UnwrapReply>, + func: (elem: any) => T, +) { + const message = Object.create(null); + + for (let i = 0; i < reply.length; i+= 2) { + message[reply[i].toString()] = func(reply[i + 1]); + } + + return message; +} + export function createTransformTuplesReplyFunc(preserve?: any, typeMapping?: TypeMapping) { return (reply: ArrayReply) => { return transformTuplesReply(reply, preserve, typeMapping); @@ -255,16 +269,16 @@ export function pushVariadicArgument( return args; } -export function pushOptionalVariadicArgument( - args: CommandArguments, +export function parseOptionalVariadicArgument( + parser: CommandParser, name: RedisArgument, value?: RedisVariadicArgument -): CommandArguments { - if (value === undefined) return args; +) { + if (value === undefined) return; - args.push(name); + parser.push(name); - return pushVariadicArgument(args, value); + parser.pushVariadicWithLength(value); } export enum CommandFlags { @@ -393,29 +407,27 @@ export interface SlotRange { end: number; } -function pushSlotRangeArguments( - args: CommandArguments, +function parseSlotRangeArguments( + parser: CommandParser, range: SlotRange ): void { - args.push( + parser.push( range.start.toString(), range.end.toString() ); } -export function pushSlotRangesArguments( - args: CommandArguments, +export function parseSlotRangesArguments( + parser: CommandParser, ranges: SlotRange | Array -): CommandArguments { +) { if (Array.isArray(ranges)) { for (const range of ranges) { - pushSlotRangeArguments(args, range); + parseSlotRangeArguments(parser, range); } } else { - pushSlotRangeArguments(args, ranges); + parseSlotRangeArguments(parser, ranges); } - - return args; } export type RawRangeReply = [ @@ -444,41 +456,36 @@ export type ZVariadicKeys = T | [T, ...Array]; export type ZKeys = ZVariadicKeys | ZVariadicKeys; -export function pushZKeysArguments( - args: CommandArguments, +export function parseZKeysArguments( + parser: CommandParser, keys: ZKeys ) { if (Array.isArray(keys)) { - args.push(keys.length.toString()); + parser.push(keys.length.toString()); if (keys.length) { if (isPlainKeys(keys)) { - args = args.concat(keys); + parser.pushKeys(keys); } else { - const start = args.length; - args[start + keys.length] = 'WEIGHTS'; for (let i = 0; i < keys.length; i++) { - const index = start + i; - args[index] = keys[i].key; - args[index + 1 + keys.length] = transformDoubleArgument(keys[i].weight); + parser.pushKey(keys[i].key) + } + parser.push('WEIGHTS'); + for (let i = 0; i < keys.length; i++) { + parser.push(transformDoubleArgument(keys[i].weight)); } } } } else { - args.push('1'); + parser.push('1'); if (isPlainKey(keys)) { - args.push(keys); + parser.pushKey(keys); } else { - args.push( - keys.key, - 'WEIGHTS', - transformDoubleArgument(keys.weight) - ); + parser.pushKey(keys.key); + parser.push('WEIGHTS', transformDoubleArgument(keys.weight)); } } - - return args; } function isPlainKey(key: RedisArgument | ZKeyAndWeight): key is RedisArgument { @@ -489,6 +496,22 @@ function isPlainKeys(keys: Array | Array): keys is return isPlainKey(keys[0]); } +export type Tail = T extends [infer Head, ...infer Tail] ? Tail : never; + +/** + * @deprecated + */ +export function parseArgs(command: Command, ...args: Array): CommandArguments { + const parser = new BasicCommandParser(); + command.parseCommand!(parser, ...args); + + const redisArgs: CommandArguments = parser.redisArgs; + if (parser.preserve) { + redisArgs.preserve = parser.preserve; + } + return redisArgs; +} + export type StreamMessageRawReply = TuplesReply<[ id: BlobStringReply, message: ArrayReply diff --git a/packages/client/lib/multi-command.ts b/packages/client/lib/multi-command.ts index a3ff4c99407..3d45a02fb4d 100644 --- a/packages/client/lib/multi-command.ts +++ b/packages/client/lib/multi-command.ts @@ -16,6 +16,12 @@ export interface RedisMultiQueuedCommand { } export default class RedisMultiCommand { + private readonly typeMapping?: TypeMapping; + + constructor(typeMapping?: TypeMapping) { + this.typeMapping = typeMapping; + } + readonly queue: Array = []; readonly scriptsInUse = new Set(); @@ -46,7 +52,7 @@ export default class RedisMultiCommand { this.addCommand(redisArgs, transformReply); } - transformReplies(rawReplies: Array, typeMapping?: TypeMapping): Array { + transformReplies(rawReplies: Array): Array { const errorIndexes: Array = [], replies = rawReplies.map((reply, i) => { if (reply instanceof ErrorReply) { @@ -55,7 +61,7 @@ export default class RedisMultiCommand { } const { transformReply, args } = this.queue[i]; - return transformReply ? transformReply(reply, args.preserve, typeMapping) : reply; + return transformReply ? transformReply(reply, args.preserve, this.typeMapping) : reply; }); if (errorIndexes.length) throw new MultiErrorReply(replies, errorIndexes); diff --git a/packages/client/lib/sentinel/commands/SENTINEL_MASTER.ts b/packages/client/lib/sentinel/commands/SENTINEL_MASTER.ts index b260dcfba7d..84997ac7d8f 100644 --- a/packages/client/lib/sentinel/commands/SENTINEL_MASTER.ts +++ b/packages/client/lib/sentinel/commands/SENTINEL_MASTER.ts @@ -1,9 +1,10 @@ import { RedisArgument, MapReply, BlobStringReply, Command } from '../../RESP/types'; +import { CommandParser } from '../../client/parser'; import { transformTuplesReply } from '../../commands/generic-transformers'; export default { - transformArguments(dbname: RedisArgument) { - return ['SENTINEL', 'MASTER', dbname]; + parseCommand(parser: CommandParser, dbname: RedisArgument) { + parser.push('SENTINEL', 'MASTER', dbname); }, transformReply: { 2: transformTuplesReply, diff --git a/packages/client/lib/sentinel/commands/SENTINEL_MONITOR.ts b/packages/client/lib/sentinel/commands/SENTINEL_MONITOR.ts index 14caecd924a..65f438de132 100644 --- a/packages/client/lib/sentinel/commands/SENTINEL_MONITOR.ts +++ b/packages/client/lib/sentinel/commands/SENTINEL_MONITOR.ts @@ -1,8 +1,9 @@ +import { CommandParser } from '../../client/parser'; import { RedisArgument, SimpleStringReply, Command } from '../../RESP/types'; export default { - transformArguments(dbname: RedisArgument, host: RedisArgument, port: RedisArgument, quorum: RedisArgument) { - return ['SENTINEL', 'MONITOR', dbname, host, port, quorum]; + parseCommand(parser: CommandParser, dbname: RedisArgument, host: RedisArgument, port: RedisArgument, quorum: RedisArgument) { + parser.push('SENTINEL', 'MONITOR', dbname, host, port, quorum); }, transformReply: undefined as unknown as () => SimpleStringReply<'OK'> } as const satisfies Command; diff --git a/packages/client/lib/sentinel/commands/SENTINEL_REPLICAS.ts b/packages/client/lib/sentinel/commands/SENTINEL_REPLICAS.ts index 3d002896355..127449264d8 100644 --- a/packages/client/lib/sentinel/commands/SENTINEL_REPLICAS.ts +++ b/packages/client/lib/sentinel/commands/SENTINEL_REPLICAS.ts @@ -1,9 +1,10 @@ +import { CommandParser } from '../../client/parser'; import { RedisArgument, ArrayReply, BlobStringReply, MapReply, Command, TypeMapping, UnwrapReply } from '../../RESP/types'; import { transformTuplesReply } from '../../commands/generic-transformers'; export default { - transformArguments(dbname: RedisArgument) { - return ['SENTINEL', 'REPLICAS', dbname]; + parseCommand(parser: CommandParser, dbname: RedisArgument) { + parser.push('SENTINEL', 'REPLICAS', dbname); }, transformReply: { 2: (reply: ArrayReply>, preserve?: any, typeMapping?: TypeMapping) => { diff --git a/packages/client/lib/sentinel/commands/SENTINEL_SENTINELS.ts b/packages/client/lib/sentinel/commands/SENTINEL_SENTINELS.ts index 22c1e0123fc..4550b9498b3 100644 --- a/packages/client/lib/sentinel/commands/SENTINEL_SENTINELS.ts +++ b/packages/client/lib/sentinel/commands/SENTINEL_SENTINELS.ts @@ -1,9 +1,10 @@ +import { CommandParser } from '../../client/parser'; import { RedisArgument, ArrayReply, MapReply, BlobStringReply, Command, TypeMapping, UnwrapReply } from '../../RESP/types'; import { transformTuplesReply } from '../../commands/generic-transformers'; export default { - transformArguments(dbname: RedisArgument) { - return ['SENTINEL', 'SENTINELS', dbname]; + parseCommand(parser: CommandParser, dbname: RedisArgument) { + parser.push('SENTINEL', 'SENTINELS', dbname); }, transformReply: { 2: (reply: ArrayReply>, preserve?: any, typeMapping?: TypeMapping) => { diff --git a/packages/client/lib/sentinel/commands/SENTINEL_SET.ts b/packages/client/lib/sentinel/commands/SENTINEL_SET.ts index 41037819869..b4e8f843ea6 100644 --- a/packages/client/lib/sentinel/commands/SENTINEL_SET.ts +++ b/packages/client/lib/sentinel/commands/SENTINEL_SET.ts @@ -1,3 +1,4 @@ +import { CommandParser } from '../../client/parser'; import { RedisArgument, SimpleStringReply, Command } from '../../RESP/types'; export type SentinelSetOptions = Array<{ @@ -6,14 +7,12 @@ export type SentinelSetOptions = Array<{ }>; export default { - transformArguments(dbname: RedisArgument, options: SentinelSetOptions) { - const args = ['SENTINEL', 'SET', dbname]; + parseCommand(parser: CommandParser, dbname: RedisArgument, options: SentinelSetOptions) { + parser.push('SENTINEL', 'SET', dbname); for (const option of options) { - args.push(option.option, option.value); + parser.push(option.option, option.value); } - - return args; }, transformReply: undefined as unknown as () => SimpleStringReply<'OK'> } as const satisfies Command; diff --git a/packages/client/lib/sentinel/index.spec.ts b/packages/client/lib/sentinel/index.spec.ts index 1fba8d6b42f..be5522bdd8d 100644 --- a/packages/client/lib/sentinel/index.spec.ts +++ b/packages/client/lib/sentinel/index.spec.ts @@ -14,21 +14,10 @@ import { defineScript } from '../lua-script'; import { MATH_FUNCTION } from '../commands/FUNCTION_LOAD.spec'; import RedisBloomModules from '@redis/bloom'; import { RedisTcpSocketOptions } from '../client/socket'; +import { SQUARE_SCRIPT } from '../client/index.spec'; const execAsync = promisify(exec); -const SQUARE_SCRIPT = defineScript({ - SCRIPT: - `local number = redis.call('GET', KEYS[1]) - return number * number`, - NUMBER_OF_KEYS: 1, - FIRST_KEY_INDEX: 0, - transformArguments(key: string) { - return [key]; - }, - transformReply: undefined as unknown as () => NumberReply -}); - /* used to ensure test environment resets to normal state i.e. - all redis nodes are active and are part of the topology diff --git a/packages/client/lib/sentinel/index.ts b/packages/client/lib/sentinel/index.ts index b71514e9358..d25fa03e559 100644 --- a/packages/client/lib/sentinel/index.ts +++ b/packages/client/lib/sentinel/index.ts @@ -1,5 +1,5 @@ import { EventEmitter } from 'node:events'; -import { CommandArguments, RedisArgument, RedisFunctions, RedisModules, RedisScript, RedisScripts, ReplyUnion, RespVersions, TypeMapping } from '../RESP/types'; +import { CommandArguments, RedisFunctions, RedisModules, RedisScripts, ReplyUnion, RespVersions, TypeMapping } from '../RESP/types'; import RedisClient, { RedisClientOptions, RedisClientType } from '../client'; import { CommandOptions } from '../client/commands-queue'; import { attachConfig } from '../commander'; @@ -164,18 +164,6 @@ export class RedisSentinelClient< ); } - executeScript( - script: RedisScript, - isReadonly: boolean | undefined, - args: Array, - options?: CommandOptions - ) { - return this._execute( - isReadonly, - client => client.executeScript(script, args, options) - ); - } - /** * @internal */ @@ -440,18 +428,6 @@ export default class RedisSentinel< ); } - executeScript( - script: RedisScript, - isReadonly: boolean | undefined, - args: Array, - options?: CommandOptions - ) { - return this._execute( - isReadonly, - client => client.executeScript(script, args, options) - ); - } - /** * @internal */ diff --git a/packages/client/lib/sentinel/multi-commands.ts b/packages/client/lib/sentinel/multi-commands.ts index bf616370bf3..e70dc45c790 100644 --- a/packages/client/lib/sentinel/multi-commands.ts +++ b/packages/client/lib/sentinel/multi-commands.ts @@ -3,6 +3,8 @@ import RedisMultiCommand, { MULTI_REPLY, MultiReply, MultiReplyType } from '../m import { ReplyWithTypeMapping, CommandReply, Command, CommandArguments, CommanderConfig, RedisFunctions, RedisModules, RedisScripts, RespVersions, TransformReply, RedisScript, RedisFunction, TypeMapping } from '../RESP/types'; import { attachConfig, functionArgumentsPrefix, getTransformReply } from '../commander'; import { RedisSentinelType } from './types'; +import { BasicCommandParser } from '../client/parser'; +import { Tail } from '../commands/generic-transformers'; type CommandSignature< REPLIES extends Array, @@ -12,7 +14,7 @@ type CommandSignature< S extends RedisScripts, RESP extends RespVersions, TYPE_MAPPING extends TypeMapping -> = (...args: Parameters) => RedisSentinelMultiCommandType< +> = (...args: Tail>) => RedisSentinelMultiCommandType< [...REPLIES, ReplyWithTypeMapping, TYPE_MAPPING>], M, F, @@ -87,8 +89,14 @@ export type RedisSentinelMultiCommandType< export default class RedisSentinelMultiCommand { private static _createCommand(command: Command, resp: RespVersions) { const transformReply = getTransformReply(command, resp); + return function (this: RedisSentinelMultiCommand, ...args: Array) { - const redisArgs = command.transformArguments(...args); + const parser = new BasicCommandParser(); + command.parseCommand(parser, ...args); + + const redisArgs: CommandArguments = parser.redisArgs; + redisArgs.preserve = parser.preserve; + return this.addCommand( command.IS_READ_ONLY, redisArgs, @@ -99,8 +107,14 @@ export default class RedisSentinelMultiCommand { private static _createModuleCommand(command: Command, resp: RespVersions) { const transformReply = getTransformReply(command, resp); + return function (this: { _self: RedisSentinelMultiCommand }, ...args: Array) { - const redisArgs = command.transformArguments(...args); + const parser = new BasicCommandParser(); + command.parseCommand(parser, ...args); + + const redisArgs: CommandArguments = parser.redisArgs; + redisArgs.preserve = parser.preserve; + return this._self.addCommand( command.IS_READ_ONLY, redisArgs, @@ -110,12 +124,17 @@ export default class RedisSentinelMultiCommand { } private static _createFunctionCommand(name: string, fn: RedisFunction, resp: RespVersions) { - const prefix = functionArgumentsPrefix(name, fn), - transformReply = getTransformReply(fn, resp); + const prefix = functionArgumentsPrefix(name, fn); + const transformReply = getTransformReply(fn, resp); + return function (this: { _self: RedisSentinelMultiCommand }, ...args: Array) { - const fnArgs = fn.transformArguments(...args); - const redisArgs: CommandArguments = prefix.concat(fnArgs); - redisArgs.preserve = fnArgs.preserve; + const parser = new BasicCommandParser(); + parser.push(...prefix); + fn.parseCommand(parser, ...args); + + const redisArgs: CommandArguments = parser.redisArgs; + redisArgs.preserve = parser.preserve; + return this._self.addCommand( fn.IS_READ_ONLY, redisArgs, @@ -126,17 +145,20 @@ export default class RedisSentinelMultiCommand { private static _createScriptCommand(script: RedisScript, resp: RespVersions) { const transformReply = getTransformReply(script, resp); + return function (this: RedisSentinelMultiCommand, ...args: Array) { - const scriptArgs = script.transformArguments(...args); - this._setState( - script.IS_READ_ONLY - ); - this._multi.addScript( + const parser = new BasicCommandParser(); + script.parseCommand(parser, ...args); + + const scriptArgs: CommandArguments = parser.redisArgs; + scriptArgs.preserve = parser.preserve; + + return this.#addScript( + script.IS_READ_ONLY, script, scriptArgs, transformReply ); - return this; }; } @@ -157,20 +179,19 @@ export default class RedisSentinelMultiCommand { }); } - private readonly _multi = new RedisMultiCommand(); - private readonly _sentinel: RedisSentinelType - private _isReadonly: boolean | undefined = true; - private readonly _typeMapping?: TypeMapping; + readonly #multi = new RedisMultiCommand(); + readonly #sentinel: RedisSentinelType + #isReadonly: boolean | undefined = true; constructor(sentinel: RedisSentinelType, typeMapping: TypeMapping) { - this._sentinel = sentinel; - this._typeMapping = typeMapping; + this.#multi = new RedisMultiCommand(typeMapping); + this.#sentinel = sentinel; } - private _setState( + #setState( isReadonly: boolean | undefined, ) { - this._isReadonly &&= isReadonly; + this.#isReadonly &&= isReadonly; } addCommand( @@ -178,20 +199,31 @@ export default class RedisSentinelMultiCommand { args: CommandArguments, transformReply?: TransformReply ) { - this._setState(isReadonly); - this._multi.addCommand(args, transformReply); + this.#setState(isReadonly); + this.#multi.addCommand(args, transformReply); + return this; + } + + #addScript( + isReadonly: boolean | undefined, + script: RedisScript, + args: CommandArguments, + transformReply?: TransformReply + ) { + this.#setState(isReadonly); + this.#multi.addScript(script, args, transformReply); + return this; } async exec(execAsPipeline = false) { if (execAsPipeline) return this.execAsPipeline(); - return this._multi.transformReplies( - await this._sentinel._executeMulti( - this._isReadonly, - this._multi.queue - ), - this._typeMapping + return this.#multi.transformReplies( + await this.#sentinel._executeMulti( + this.#isReadonly, + this.#multi.queue + ) ) as MultiReplyType; } @@ -202,14 +234,13 @@ export default class RedisSentinelMultiCommand { } async execAsPipeline() { - if (this._multi.queue.length === 0) return [] as MultiReplyType; - - return this._multi.transformReplies( - await this._sentinel._executePipeline( - this._isReadonly, - this._multi.queue - ), - this._typeMapping + if (this.#multi.queue.length === 0) return [] as MultiReplyType; + + return this.#multi.transformReplies( + await this.#sentinel._executePipeline( + this.#isReadonly, + this.#multi.queue + ) ) as MultiReplyType; } diff --git a/packages/client/lib/sentinel/utils.ts b/packages/client/lib/sentinel/utils.ts index b4d430b1b44..90b789ddca9 100644 --- a/packages/client/lib/sentinel/utils.ts +++ b/packages/client/lib/sentinel/utils.ts @@ -1,3 +1,4 @@ +import { BasicCommandParser } from '../client/parser'; import { ArrayReply, Command, RedisFunction, RedisScript, RespVersions, UnwrapReply } from '../RESP/types'; import { RedisSocketOptions, RedisTcpSocketOptions } from '../client/socket'; import { functionArgumentsPrefix, getTransformReply, scriptArgumentsPrefix } from '../commander'; @@ -38,77 +39,60 @@ export function clientSocketToNode(socket: RedisSocketOptions): RedisNode { export function createCommand(command: Command, resp: RespVersions) { const transformReply = getTransformReply(command, resp); + return async function (this: T, ...args: Array) { - const redisArgs = command.transformArguments(...args); - const typeMapping = this._self.commandOptions?.typeMapping; + const parser = new BasicCommandParser(); + command.parseCommand(parser, ...args); - const reply = await this._self.sendCommand( + return this._self._execute( command.IS_READ_ONLY, - redisArgs, - this._self.commandOptions + client => client._executeCommand(command, parser, this.commandOptions, transformReply) ); - - return transformReply ? - transformReply(reply, redisArgs.preserve, typeMapping) : - reply; }; } export function createFunctionCommand(name: string, fn: RedisFunction, resp: RespVersions) { - const prefix = functionArgumentsPrefix(name, fn), - transformReply = getTransformReply(fn, resp); + const prefix = functionArgumentsPrefix(name, fn); + const transformReply = getTransformReply(fn, resp); + return async function (this: T, ...args: Array) { - const fnArgs = fn.transformArguments(...args); - const redisArgs = prefix.concat(fnArgs); - const typeMapping = this._self._self.commandOptions?.typeMapping; + const parser = new BasicCommandParser(); + parser.push(...prefix); + fn.parseCommand(parser, ...args); - const reply = await this._self._self.sendCommand( + return this._self._execute( fn.IS_READ_ONLY, - redisArgs, - this._self._self.commandOptions + client => client._executeCommand(fn, parser, this._self.commandOptions, transformReply) ); - - return transformReply ? - transformReply(reply, fnArgs.preserve, typeMapping) : - reply; } }; export function createModuleCommand(command: Command, resp: RespVersions) { const transformReply = getTransformReply(command, resp); + return async function (this: T, ...args: Array) { - const redisArgs = command.transformArguments(...args); - const typeMapping = this._self._self.commandOptions?.typeMapping; + const parser = new BasicCommandParser(); + command.parseCommand(parser, ...args); - const reply = await this._self._self.sendCommand( + return this._self._execute( command.IS_READ_ONLY, - redisArgs, - this._self._self.commandOptions + client => client._executeCommand(command, parser, this._self.commandOptions, transformReply) ); - - return transformReply ? - transformReply(reply, redisArgs.preserve, typeMapping) : - reply; } }; export function createScriptCommand(script: RedisScript, resp: RespVersions) { - const prefix = scriptArgumentsPrefix(script), - transformReply = getTransformReply(script, resp); + const prefix = scriptArgumentsPrefix(script); + const transformReply = getTransformReply(script, resp); + return async function (this: T, ...args: Array) { - const scriptArgs = script.transformArguments(...args); - const redisArgs = prefix.concat(scriptArgs); - const typeMapping = this._self.commandOptions?.typeMapping; + const parser = new BasicCommandParser(); + parser.push(...prefix); + script.parseCommand(parser, ...args); - const reply = await this._self.executeScript( - script, + return this._self._execute( script.IS_READ_ONLY, - redisArgs, - this._self.commandOptions + client => client._executeScript(script, parser, this.commandOptions, transformReply) ); - - return transformReply ? - transformReply(reply, scriptArgs.preserve, typeMapping) : - reply; }; } diff --git a/packages/client/lib/test-utils.ts b/packages/client/lib/test-utils.ts index 29eb03cb73d..083c9127e5b 100644 --- a/packages/client/lib/test-utils.ts +++ b/packages/client/lib/test-utils.ts @@ -1,6 +1,8 @@ import TestUtils from '@redis/test-utils'; import { SinonSpy } from 'sinon'; import { setTimeout } from 'node:timers/promises'; +import { Command } from './RESP/types'; +import { BasicCommandParser } from './client/parser'; const utils = new TestUtils({ dockerImageName: 'redis/redis-stack', @@ -67,3 +69,9 @@ export const BLOCKING_MIN_VALUE = ( utils.isVersionGreaterThan([6]) ? 0.01 : 1 ); + +export function parseFirstKey(command: Command, ...args: Array) { + const parser = new BasicCommandParser(); + command.parseCommand!(parser, ...args); + return parser.firstKey; +} diff --git a/packages/graph/lib/commands/CONFIG_GET.spec.ts b/packages/graph/lib/commands/CONFIG_GET.spec.ts index 42c7739f5d7..9a427867c63 100644 --- a/packages/graph/lib/commands/CONFIG_GET.spec.ts +++ b/packages/graph/lib/commands/CONFIG_GET.spec.ts @@ -1,11 +1,12 @@ import { strict as assert } from 'node:assert'; import testUtils, { GLOBAL } from '../test-utils'; import CONFIG_GET from './CONFIG_GET'; +import { parseArgs } from '@redis/client/lib/commands/generic-transformers'; describe('GRAPH.CONFIG GET', () => { it('transformArguments', () => { assert.deepEqual( - CONFIG_GET.transformArguments('TIMEOUT'), + parseArgs(CONFIG_GET, 'TIMEOUT'), ['GRAPH.CONFIG', 'GET', 'TIMEOUT'] ); }); diff --git a/packages/graph/lib/commands/CONFIG_GET.ts b/packages/graph/lib/commands/CONFIG_GET.ts index c7ed037e1a1..8ff289876d3 100644 --- a/packages/graph/lib/commands/CONFIG_GET.ts +++ b/packages/graph/lib/commands/CONFIG_GET.ts @@ -1,4 +1,5 @@ -import { RedisArgument, TuplesReply, ArrayReply, BlobStringReply, NumberReply, Command } from '@redis/client/dist/lib/RESP/types'; +import { RedisArgument, TuplesReply, ArrayReply, BlobStringReply, NumberReply, Command } from '@redis/client/lib/RESP/types'; +import { CommandParser } from '@redis/client/lib/client/parser'; type ConfigItemReply = TuplesReply<[ configKey: BlobStringReply, @@ -6,10 +7,10 @@ type ConfigItemReply = TuplesReply<[ ]>; export default { - FIRST_KEY_INDEX: undefined, + NOT_KEYED_COMMAND: true, IS_READ_ONLY: true, - transformArguments(configKey: RedisArgument) { - return ['GRAPH.CONFIG', 'GET', configKey]; + parseCommand(parser: CommandParser, configKey: RedisArgument) { + parser.push('GRAPH.CONFIG', 'GET', configKey); }, transformReply: undefined as unknown as () => ConfigItemReply | ArrayReply } as const satisfies Command; diff --git a/packages/graph/lib/commands/CONFIG_SET.spec.ts b/packages/graph/lib/commands/CONFIG_SET.spec.ts index 5ed51e78a29..ae6e296699c 100644 --- a/packages/graph/lib/commands/CONFIG_SET.spec.ts +++ b/packages/graph/lib/commands/CONFIG_SET.spec.ts @@ -1,11 +1,12 @@ import { strict as assert } from 'node:assert'; import testUtils, { GLOBAL } from '../test-utils'; import CONFIG_SET from './CONFIG_SET'; +import { parseArgs } from '@redis/client/lib/commands/generic-transformers'; describe('GRAPH.CONFIG SET', () => { it('transformArguments', () => { assert.deepEqual( - CONFIG_SET.transformArguments('TIMEOUT', 0), + parseArgs(CONFIG_SET, 'TIMEOUT', 0), ['GRAPH.CONFIG', 'SET', 'TIMEOUT', '0'] ); }); diff --git a/packages/graph/lib/commands/CONFIG_SET.ts b/packages/graph/lib/commands/CONFIG_SET.ts index ba23ac2f1a7..b37d8690bfa 100644 --- a/packages/graph/lib/commands/CONFIG_SET.ts +++ b/packages/graph/lib/commands/CONFIG_SET.ts @@ -1,15 +1,11 @@ -import { RedisArgument, SimpleStringReply, Command } from '@redis/client/dist/lib/RESP/types'; +import { RedisArgument, SimpleStringReply, Command } from '@redis/client/lib/RESP/types'; +import { CommandParser } from '@redis/client/lib/client/parser'; export default { - FIRST_KEY_INDEX: undefined, + NOT_KEYED_COMMAND: true, IS_READ_ONLY: false, - transformArguments(configKey: RedisArgument, value: number) { - return [ - 'GRAPH.CONFIG', - 'SET', - configKey, - value.toString() - ]; + parseCommand(parser: CommandParser, configKey: RedisArgument, value: number) { + parser.push('GRAPH.CONFIG', 'SET', configKey, value.toString()); }, transformReply: undefined as unknown as () => SimpleStringReply<'OK'> } as const satisfies Command; diff --git a/packages/graph/lib/commands/DELETE.spec.ts b/packages/graph/lib/commands/DELETE.spec.ts index 6fe24fd827a..5977c646307 100644 --- a/packages/graph/lib/commands/DELETE.spec.ts +++ b/packages/graph/lib/commands/DELETE.spec.ts @@ -1,11 +1,12 @@ import { strict as assert } from 'node:assert'; import testUtils, { GLOBAL } from '../test-utils'; import DELETE from './DELETE'; +import { parseArgs } from '@redis/client/lib/commands/generic-transformers'; describe('GRAPH.DELETE', () => { it('transformArguments', () => { assert.deepEqual( - DELETE.transformArguments('key'), + parseArgs(DELETE, 'key'), ['GRAPH.DELETE', 'key'] ); }); diff --git a/packages/graph/lib/commands/DELETE.ts b/packages/graph/lib/commands/DELETE.ts index f5f99fb92cc..2e1f9ff003c 100644 --- a/packages/graph/lib/commands/DELETE.ts +++ b/packages/graph/lib/commands/DELETE.ts @@ -1,10 +1,11 @@ -import { RedisArgument, BlobStringReply, Command } from '@redis/client/dist/lib/RESP/types'; +import { RedisArgument, BlobStringReply, Command } from '@redis/client/lib/RESP/types'; +import { CommandParser } from '@redis/client/lib/client/parser'; export default { - FIRST_KEY_INDEX: 1, IS_READ_ONLY: false, - transformArguments(key: RedisArgument) { - return ['GRAPH.DELETE', key]; + parseCommand(parser: CommandParser, key: RedisArgument) { + parser.push('GRAPH.DELETE'); + parser.pushKey(key); }, transformReply: undefined as unknown as () => BlobStringReply } as const satisfies Command; diff --git a/packages/graph/lib/commands/EXPLAIN.spec.ts b/packages/graph/lib/commands/EXPLAIN.spec.ts index 04bf838a4de..28f30cd17b3 100644 --- a/packages/graph/lib/commands/EXPLAIN.spec.ts +++ b/packages/graph/lib/commands/EXPLAIN.spec.ts @@ -1,11 +1,12 @@ import { strict as assert } from 'node:assert'; import testUtils, { GLOBAL } from '../test-utils'; import EXPLAIN from './EXPLAIN'; +import { parseArgs } from '@redis/client/lib/commands/generic-transformers'; describe('GRAPH.EXPLAIN', () => { it('transformArguments', () => { assert.deepEqual( - EXPLAIN.transformArguments('key', 'RETURN 0'), + parseArgs(EXPLAIN, 'key', 'RETURN 0'), ['GRAPH.EXPLAIN', 'key', 'RETURN 0'] ); }); diff --git a/packages/graph/lib/commands/EXPLAIN.ts b/packages/graph/lib/commands/EXPLAIN.ts index 99a73bf04bf..c690450a10f 100644 --- a/packages/graph/lib/commands/EXPLAIN.ts +++ b/packages/graph/lib/commands/EXPLAIN.ts @@ -1,10 +1,12 @@ -import { RedisArgument, ArrayReply, BlobStringReply, Command } from '@redis/client/dist/lib/RESP/types'; +import { RedisArgument, ArrayReply, BlobStringReply, Command } from '@redis/client/lib/RESP/types'; +import { CommandParser } from '@redis/client/lib/client/parser'; export default { - FIRST_KEY_INDEX: 1, IS_READ_ONLY: true, - transformArguments(key: RedisArgument, query: RedisArgument) { - return ['GRAPH.EXPLAIN', key, query]; + parseCommand(parser: CommandParser, key: RedisArgument, query: RedisArgument) { + parser.push('GRAPH.EXPLAIN'); + parser.pushKey(key); + parser.push(query); }, transformReply: undefined as unknown as () => ArrayReply } as const satisfies Command; diff --git a/packages/graph/lib/commands/LIST.spec.ts b/packages/graph/lib/commands/LIST.spec.ts index 36745efc470..19f18a0e30b 100644 --- a/packages/graph/lib/commands/LIST.spec.ts +++ b/packages/graph/lib/commands/LIST.spec.ts @@ -1,11 +1,12 @@ import { strict as assert } from 'node:assert'; import testUtils, { GLOBAL } from '../test-utils'; import LIST from './LIST'; +import { parseArgs } from '@redis/client/lib/commands/generic-transformers'; describe('GRAPH.LIST', () => { it('transformArguments', () => { assert.deepEqual( - LIST.transformArguments(), + parseArgs(LIST), ['GRAPH.LIST'] ); }); diff --git a/packages/graph/lib/commands/LIST.ts b/packages/graph/lib/commands/LIST.ts index 01a868854be..4fe66d748fa 100644 --- a/packages/graph/lib/commands/LIST.ts +++ b/packages/graph/lib/commands/LIST.ts @@ -1,10 +1,11 @@ -import { ArrayReply, BlobStringReply, Command } from '@redis/client/dist/lib/RESP/types'; +import { ArrayReply, BlobStringReply, Command } from '@redis/client/lib/RESP/types'; +import { CommandParser } from '@redis/client/lib/client/parser'; export default { - FIRST_KEY_INDEX: undefined, + NOT_KEYED_COMMAND: true, IS_READ_ONLY: true, - transformArguments() { - return ['GRAPH.LIST']; + parseCommand(parser: CommandParser) { + parser.push('GRAPH.LIST'); }, transformReply: undefined as unknown as () => ArrayReply } as const satisfies Command; diff --git a/packages/graph/lib/commands/PROFILE.spec.ts b/packages/graph/lib/commands/PROFILE.spec.ts index a758365d56e..7f16fd3ba58 100644 --- a/packages/graph/lib/commands/PROFILE.spec.ts +++ b/packages/graph/lib/commands/PROFILE.spec.ts @@ -1,11 +1,12 @@ import { strict as assert } from 'node:assert'; import testUtils, { GLOBAL } from '../test-utils'; import PROFILE from './PROFILE'; +import { parseArgs } from '@redis/client/lib/commands/generic-transformers'; describe('GRAPH.PROFILE', () => { it('transformArguments', () => { assert.deepEqual( - PROFILE.transformArguments('key', 'RETURN 0'), + parseArgs(PROFILE, 'key', 'RETURN 0'), ['GRAPH.PROFILE', 'key', 'RETURN 0'] ); }); diff --git a/packages/graph/lib/commands/PROFILE.ts b/packages/graph/lib/commands/PROFILE.ts index 2aa1e83dfb0..fba0973baaf 100644 --- a/packages/graph/lib/commands/PROFILE.ts +++ b/packages/graph/lib/commands/PROFILE.ts @@ -1,10 +1,12 @@ -import { RedisArgument, ArrayReply, BlobStringReply, Command } from '@redis/client/dist/lib/RESP/types'; +import { RedisArgument, ArrayReply, BlobStringReply, Command } from '@redis/client/lib/RESP/types'; +import { CommandParser } from '@redis/client/lib/client/parser'; export default { - FIRST_KEY_INDEX: 1, IS_READ_ONLY: true, - transformArguments(key: RedisArgument, query: RedisArgument) { - return ['GRAPH.PROFILE', key, query]; + parseCommand(parser: CommandParser, key: RedisArgument, query: RedisArgument) { + parser.push('GRAPH.PROFILE'); + parser.pushKey(key); + parser.push(query); }, transformReply: undefined as unknown as () => ArrayReply } as const satisfies Command; diff --git a/packages/graph/lib/commands/QUERY.spec.ts b/packages/graph/lib/commands/QUERY.spec.ts index 62c9bcaaefe..28c2189645b 100644 --- a/packages/graph/lib/commands/QUERY.spec.ts +++ b/packages/graph/lib/commands/QUERY.spec.ts @@ -1,12 +1,13 @@ import { strict as assert } from 'node:assert'; import testUtils, { GLOBAL } from '../test-utils'; import QUERY from './QUERY'; +import { parseArgs } from '@redis/client/lib/commands/generic-transformers'; describe('GRAPH.QUERY', () => { describe('transformArguments', () => { it('simple', () => { assert.deepEqual( - QUERY.transformArguments('key', 'query'), + parseArgs(QUERY, 'key', 'query'), ['GRAPH.QUERY', 'key', 'query'] ); }); @@ -14,7 +15,7 @@ describe('GRAPH.QUERY', () => { describe('params', () => { it('all types', () => { assert.deepEqual( - QUERY.transformArguments('key', 'query', { + parseArgs(QUERY, 'key', 'query', { params: { null: null, string: '"\\', @@ -30,7 +31,7 @@ describe('GRAPH.QUERY', () => { it('TypeError', () => { assert.throws(() => { - QUERY.transformArguments('key', 'query', { + parseArgs(QUERY, 'key', 'query', { params: { a: Symbol() } @@ -41,7 +42,7 @@ describe('GRAPH.QUERY', () => { it('TIMEOUT', () => { assert.deepEqual( - QUERY.transformArguments('key', 'query', { + parseArgs(QUERY, 'key', 'query', { TIMEOUT: 1 }), ['GRAPH.QUERY', 'key', 'query', 'TIMEOUT', '1'] @@ -50,7 +51,7 @@ describe('GRAPH.QUERY', () => { it('compact', () => { assert.deepEqual( - QUERY.transformArguments('key', 'query', undefined, true), + parseArgs(QUERY, 'key', 'query', undefined, true), ['GRAPH.QUERY', 'key', 'query', '--compact'] ); }); diff --git a/packages/graph/lib/commands/QUERY.ts b/packages/graph/lib/commands/QUERY.ts index 8a052354610..c96c00ff325 100644 --- a/packages/graph/lib/commands/QUERY.ts +++ b/packages/graph/lib/commands/QUERY.ts @@ -1,4 +1,5 @@ -import { RedisArgument, ArrayReply, BlobStringReply, NumberReply, NullReply, TuplesReply, UnwrapReply, Command } from '@redis/client/dist/lib/RESP/types'; +import { RedisArgument, ArrayReply, BlobStringReply, NumberReply, NullReply, TuplesReply, UnwrapReply, Command } from '@redis/client/lib/RESP/types'; +import { CommandParser } from '@redis/client/lib/client/parser'; type Headers = ArrayReply; @@ -25,30 +26,28 @@ export interface QueryOptions { TIMEOUT?: number; } -export function transformQueryArguments( +export function parseQueryArguments( command: RedisArgument, + parser: CommandParser, graph: RedisArgument, query: RedisArgument, options?: QueryOptions, compact?: boolean ) { - const args = [ - command, - graph, - options?.params ? - `CYPHER ${queryParamsToString(options.params)} ${query}` : - query - ]; + parser.push(command); + parser.pushKey(graph); + const param = options?.params ? + `CYPHER ${queryParamsToString(options.params)} ${query}` : + query; + parser.push(param); if (options?.TIMEOUT !== undefined) { - args.push('TIMEOUT', options.TIMEOUT.toString()); + parser.push('TIMEOUT', options.TIMEOUT.toString()); } if (compact) { - args.push('--compact'); + parser.push('--compact'); } - - return args; } function queryParamsToString(params: QueryParams) { @@ -85,9 +84,8 @@ function queryParamToString(param: QueryParam): string { } export default { - FIRST_KEY_INDEX: 1, IS_READ_ONLY: false, - transformArguments: transformQueryArguments.bind(undefined, 'GRAPH.QUERY'), + parseCommand: parseQueryArguments.bind(undefined, 'GRAPH.QUERY'), transformReply(reply: UnwrapReply) { return reply.length === 1 ? { headers: undefined, diff --git a/packages/graph/lib/commands/RO_QUERY.spec.ts b/packages/graph/lib/commands/RO_QUERY.spec.ts index 13829543552..fa9459cf642 100644 --- a/packages/graph/lib/commands/RO_QUERY.spec.ts +++ b/packages/graph/lib/commands/RO_QUERY.spec.ts @@ -1,11 +1,12 @@ import { strict as assert } from 'node:assert'; import testUtils, { GLOBAL } from '../test-utils'; import RO_QUERY from './RO_QUERY'; +import { parseArgs } from '@redis/client/lib/commands/generic-transformers'; describe('GRAPH.RO_QUERY', () => { it('transformArguments', () => { assert.deepEqual( - RO_QUERY.transformArguments('key', 'query'), + parseArgs(RO_QUERY, 'key', 'query'), ['GRAPH.RO_QUERY', 'key', 'query'] ); }); diff --git a/packages/graph/lib/commands/RO_QUERY.ts b/packages/graph/lib/commands/RO_QUERY.ts index 5987f511b7d..98668675d21 100644 --- a/packages/graph/lib/commands/RO_QUERY.ts +++ b/packages/graph/lib/commands/RO_QUERY.ts @@ -1,9 +1,8 @@ -import { Command } from '@redis/client/dist/lib/RESP/types'; -import QUERY, { transformQueryArguments } from './QUERY'; +import { Command } from '@redis/client/lib/RESP/types'; +import QUERY, { parseQueryArguments } from './QUERY'; export default { - FIRST_KEY_INDEX: QUERY.FIRST_KEY_INDEX, IS_READ_ONLY: true, - transformArguments: transformQueryArguments.bind(undefined, 'GRAPH.RO_QUERY'), + parseCommand: parseQueryArguments.bind(undefined, 'GRAPH.RO_QUERY'), transformReply: QUERY.transformReply } as const satisfies Command; diff --git a/packages/graph/lib/commands/SLOWLOG.spec.ts b/packages/graph/lib/commands/SLOWLOG.spec.ts index c1c77286a26..b991d6e7b96 100644 --- a/packages/graph/lib/commands/SLOWLOG.spec.ts +++ b/packages/graph/lib/commands/SLOWLOG.spec.ts @@ -1,11 +1,12 @@ import { strict as assert } from 'node:assert'; import testUtils, { GLOBAL } from '../test-utils'; import SLOWLOG from './SLOWLOG'; +import { parseArgs } from '@redis/client/lib/commands/generic-transformers'; describe('GRAPH.SLOWLOG', () => { it('transformArguments', () => { assert.deepEqual( - SLOWLOG.transformArguments('key'), + parseArgs(SLOWLOG, 'key'), ['GRAPH.SLOWLOG', 'key'] ); }); diff --git a/packages/graph/lib/commands/SLOWLOG.ts b/packages/graph/lib/commands/SLOWLOG.ts index 52927f6040b..bba615efb21 100644 --- a/packages/graph/lib/commands/SLOWLOG.ts +++ b/packages/graph/lib/commands/SLOWLOG.ts @@ -1,4 +1,5 @@ -import { RedisArgument, ArrayReply, TuplesReply, BlobStringReply, UnwrapReply, Command } from '@redis/client/dist/lib/RESP/types'; +import { RedisArgument, ArrayReply, TuplesReply, BlobStringReply, UnwrapReply, Command } from '@redis/client/lib/RESP/types'; +import { CommandParser } from '@redis/client/lib/client/parser'; type SlowLogRawReply = ArrayReply>; export default { - FIRST_KEY_INDEX: 1, IS_READ_ONLY: true, - transformArguments(key: RedisArgument) { - return ['GRAPH.SLOWLOG', key]; + parseCommand(parser: CommandParser, key: RedisArgument) { + parser.push('GRAPH.SLOWLOG'); + parser.pushKey(key); }, transformReply(reply: UnwrapReply) { return reply.map(log => { diff --git a/packages/graph/lib/commands/index.ts b/packages/graph/lib/commands/index.ts index e93356aa951..362d98f970d 100644 --- a/packages/graph/lib/commands/index.ts +++ b/packages/graph/lib/commands/index.ts @@ -1,4 +1,4 @@ -import type { RedisCommands } from '@redis/client/dist/lib/RESP/types'; +import type { RedisCommands } from '@redis/client/lib/RESP/types'; import CONFIG_GET from './CONFIG_GET'; import CONFIG_SET from './CONFIG_SET';; import DELETE from './DELETE'; diff --git a/packages/graph/lib/graph.ts b/packages/graph/lib/graph.ts index 348c8b7155f..cacdcd5aec0 100644 --- a/packages/graph/lib/graph.ts +++ b/packages/graph/lib/graph.ts @@ -1,5 +1,5 @@ import { RedisClientType } from '@redis/client'; -import { RedisArgument, RedisFunctions, RedisScripts } from '@redis/client/dist/lib/RESP/types'; +import { RedisArgument, RedisFunctions, RedisScripts } from '@redis/client/lib/RESP/types'; import QUERY, { QueryOptions } from './commands/QUERY'; interface GraphMetadata { diff --git a/packages/json/lib/commands/ARRAPPEND.spec.ts b/packages/json/lib/commands/ARRAPPEND.spec.ts index 3bdd967e237..b2c22e0b9c0 100644 --- a/packages/json/lib/commands/ARRAPPEND.spec.ts +++ b/packages/json/lib/commands/ARRAPPEND.spec.ts @@ -1,19 +1,20 @@ import { strict as assert } from 'node:assert'; import testUtils, { GLOBAL } from '../test-utils'; import ARRAPPEND from './ARRAPPEND'; +import { parseArgs } from '@redis/client/lib/commands/generic-transformers'; describe('JSON.ARRAPPEND', () => { describe('transformArguments', () => { it('single element', () => { assert.deepEqual( - ARRAPPEND.transformArguments('key', '$', 'value'), + parseArgs(ARRAPPEND, 'key', '$', 'value'), ['JSON.ARRAPPEND', 'key', '$', '"value"'] ); }); it('multiple elements', () => { assert.deepEqual( - ARRAPPEND.transformArguments('key', '$', 1, 2), + parseArgs(ARRAPPEND, 'key', '$', 1, 2), ['JSON.ARRAPPEND', 'key', '$', '1', '2'] ); }); diff --git a/packages/json/lib/commands/ARRAPPEND.ts b/packages/json/lib/commands/ARRAPPEND.ts index 6f486a301d7..ee79119b9f7 100644 --- a/packages/json/lib/commands/ARRAPPEND.ts +++ b/packages/json/lib/commands/ARRAPPEND.ts @@ -1,27 +1,23 @@ +import { CommandParser } from '@redis/client/lib/client/parser'; import { RedisJSON, transformRedisJsonArgument } from '.'; -import { RedisArgument, NumberReply, ArrayReply, NullReply, Command } from '@redis/client/dist/lib/RESP/types'; +import { RedisArgument, NumberReply, ArrayReply, NullReply, Command } from '@redis/client/lib/RESP/types'; export default { - FIRST_KEY_INDEX: 1, IS_READ_ONLY: false, - transformArguments( + parseCommand( + parser: CommandParser, key: RedisArgument, path: RedisArgument, json: RedisJSON, ...jsons: Array ) { - const args = new Array(4 + jsons.length); - args[0] = 'JSON.ARRAPPEND'; - args[1] = key; - args[2] = path; - args[3] = transformRedisJsonArgument(json); + parser.push('JSON.ARRAPPEND'); + parser.pushKey(key); + parser.push(path, transformRedisJsonArgument(json)); - let argsIndex = 4; for (let i = 0; i < jsons.length; i++) { - args[argsIndex++] = transformRedisJsonArgument(jsons[i]); + parser.push(transformRedisJsonArgument(jsons[i])); } - - return args; }, transformReply: undefined as unknown as () => NumberReply | ArrayReply } as const satisfies Command; diff --git a/packages/json/lib/commands/ARRINDEX.spec.ts b/packages/json/lib/commands/ARRINDEX.spec.ts index cb946b62515..3c1377354f1 100644 --- a/packages/json/lib/commands/ARRINDEX.spec.ts +++ b/packages/json/lib/commands/ARRINDEX.spec.ts @@ -1,12 +1,13 @@ import { strict as assert } from 'node:assert'; import testUtils, { GLOBAL } from '../test-utils'; import ARRINDEX from './ARRINDEX'; +import { parseArgs } from '@redis/client/lib/commands/generic-transformers'; describe('JSON.ARRINDEX', () => { describe('transformArguments', () => { it('simple', () => { assert.deepEqual( - ARRINDEX.transformArguments('key', '$', 'value'), + parseArgs(ARRINDEX, 'key', '$', 'value'), ['JSON.ARRINDEX', 'key', '$', '"value"'] ); }); @@ -14,7 +15,7 @@ describe('JSON.ARRINDEX', () => { describe('with range', () => { it('start only', () => { assert.deepEqual( - ARRINDEX.transformArguments('key', '$', 'value', { + parseArgs(ARRINDEX, 'key', '$', 'value', { range: { start: 0 } @@ -25,7 +26,7 @@ describe('JSON.ARRINDEX', () => { it('with start and stop', () => { assert.deepEqual( - ARRINDEX.transformArguments('key', '$', 'value', { + parseArgs(ARRINDEX, 'key', '$', 'value', { range: { start: 0, stop: 1 diff --git a/packages/json/lib/commands/ARRINDEX.ts b/packages/json/lib/commands/ARRINDEX.ts index 77c54b92522..d0533862c60 100644 --- a/packages/json/lib/commands/ARRINDEX.ts +++ b/packages/json/lib/commands/ARRINDEX.ts @@ -1,4 +1,5 @@ -import { RedisArgument, NumberReply, ArrayReply, NullReply, Command } from '@redis/client/dist/lib/RESP/types'; +import { CommandParser } from '@redis/client/lib/client/parser'; +import { RedisArgument, NumberReply, ArrayReply, NullReply, Command } from '@redis/client/lib/RESP/types'; import { RedisJSON, transformRedisJsonArgument } from '.'; export interface JsonArrIndexOptions { @@ -9,25 +10,25 @@ export interface JsonArrIndexOptions { } export default { - FIRST_KEY_INDEX: 1, IS_READ_ONLY: true, - transformArguments( + parseCommand( + parser: CommandParser, key: RedisArgument, path: RedisArgument, json: RedisJSON, options?: JsonArrIndexOptions ) { - const args = ['JSON.ARRINDEX', key, path, transformRedisJsonArgument(json)]; + parser.push('JSON.ARRINDEX'); + parser.pushKey(key); + parser.push(path, transformRedisJsonArgument(json)); if (options?.range) { - args.push(options.range.start.toString()); + parser.push(options.range.start.toString()); if (options.range.stop !== undefined) { - args.push(options.range.stop.toString()); + parser.push(options.range.stop.toString()); } } - - return args; }, transformReply: undefined as unknown as () => NumberReply | ArrayReply } as const satisfies Command; diff --git a/packages/json/lib/commands/ARRINSERT.spec.ts b/packages/json/lib/commands/ARRINSERT.spec.ts index efa824b3738..bf9c8a2a051 100644 --- a/packages/json/lib/commands/ARRINSERT.spec.ts +++ b/packages/json/lib/commands/ARRINSERT.spec.ts @@ -1,19 +1,20 @@ import { strict as assert } from 'node:assert'; import testUtils, { GLOBAL } from '../test-utils'; import ARRINSERT from './ARRINSERT'; +import { parseArgs } from '@redis/client/lib/commands/generic-transformers'; describe('JSON.ARRINSERT', () => { describe('transformArguments', () => { it('single element', () => { assert.deepEqual( - ARRINSERT.transformArguments('key', '$', 0, 'value'), + parseArgs(ARRINSERT, 'key', '$', 0, 'value'), ['JSON.ARRINSERT', 'key', '$', '0', '"value"'] ); }); it('multiple elements', () => { assert.deepEqual( - ARRINSERT.transformArguments('key', '$', 0, '1', '2'), + parseArgs(ARRINSERT, 'key', '$', 0, '1', '2'), ['JSON.ARRINSERT', 'key', '$', '0', '"1"', '"2"'] ); }); diff --git a/packages/json/lib/commands/ARRINSERT.ts b/packages/json/lib/commands/ARRINSERT.ts index c0891884725..7a55577c9d6 100644 --- a/packages/json/lib/commands/ARRINSERT.ts +++ b/packages/json/lib/commands/ARRINSERT.ts @@ -1,29 +1,24 @@ -import { RedisArgument, NumberReply, ArrayReply, NullReply, Command } from '@redis/client/dist/lib/RESP/types'; +import { CommandParser } from '@redis/client/lib/client/parser'; +import { RedisArgument, NumberReply, ArrayReply, NullReply, Command } from '@redis/client/lib/RESP/types'; import { RedisJSON, transformRedisJsonArgument } from '.'; export default { - FIRST_KEY_INDEX: 1, IS_READ_ONLY: false, - transformArguments( + parseCommand( + parser: CommandParser, key: RedisArgument, path: RedisArgument, index: number, json: RedisJSON, ...jsons: Array ) { - const args = new Array(4 + jsons.length); - args[0] = 'JSON.ARRINSERT'; - args[1] = key; - args[2] = path; - args[3] = index.toString(); - args[4] = transformRedisJsonArgument(json); + parser.push('JSON.ARRINSERT'); + parser.pushKey(key); + parser.push(path, index.toString(), transformRedisJsonArgument(json)); - let argsIndex = 5; for (let i = 0; i < jsons.length; i++) { - args[argsIndex++] = transformRedisJsonArgument(jsons[i]); + parser.push(transformRedisJsonArgument(jsons[i])); } - - return args; }, transformReply: undefined as unknown as () => NumberReply | ArrayReply } as const satisfies Command; diff --git a/packages/json/lib/commands/ARRLEN.spec.ts b/packages/json/lib/commands/ARRLEN.spec.ts index 5ecb01b2ce5..dcf7d35acb0 100644 --- a/packages/json/lib/commands/ARRLEN.spec.ts +++ b/packages/json/lib/commands/ARRLEN.spec.ts @@ -1,19 +1,20 @@ import { strict as assert } from 'node:assert'; import testUtils, { GLOBAL } from '../test-utils'; import ARRLEN from './ARRLEN'; +import { parseArgs } from '@redis/client/lib/commands/generic-transformers'; describe('JSON.ARRLEN', () => { describe('transformArguments', () => { it('simple', () => { assert.deepEqual( - ARRLEN.transformArguments('key'), + parseArgs(ARRLEN, 'key'), ['JSON.ARRLEN', 'key'] ); }); it('with path', () => { assert.deepEqual( - ARRLEN.transformArguments('key', { + parseArgs(ARRLEN, 'key', { path: '$' }), ['JSON.ARRLEN', 'key', '$'] diff --git a/packages/json/lib/commands/ARRLEN.ts b/packages/json/lib/commands/ARRLEN.ts index d30032c7d86..26accf8df99 100644 --- a/packages/json/lib/commands/ARRLEN.ts +++ b/packages/json/lib/commands/ARRLEN.ts @@ -1,20 +1,18 @@ -import { RedisArgument, ArrayReply, NumberReply, NullReply, Command } from '@redis/client/dist/lib/RESP/types'; +import { CommandParser } from '@redis/client/lib/client/parser'; +import { RedisArgument, ArrayReply, NumberReply, NullReply, Command } from '@redis/client/lib/RESP/types'; export interface JsonArrLenOptions { path?: RedisArgument; } export default { - FIRST_KEY_INDEX: 1, IS_READ_ONLY: true, - transformArguments(key: RedisArgument, options?: JsonArrLenOptions) { - const args = ['JSON.ARRLEN', key]; - + parseCommand(parser: CommandParser, key: RedisArgument, options?: JsonArrLenOptions) { + parser.push('JSON.ARRLEN'); + parser.pushKey(key); if (options?.path !== undefined) { - args.push(options.path); + parser.push(options.path); } - - return args; }, transformReply: undefined as unknown as () => NumberReply | ArrayReply } as const satisfies Command; diff --git a/packages/json/lib/commands/ARRPOP.spec.ts b/packages/json/lib/commands/ARRPOP.spec.ts index 1b069ba3928..f823e7fc08a 100644 --- a/packages/json/lib/commands/ARRPOP.spec.ts +++ b/packages/json/lib/commands/ARRPOP.spec.ts @@ -1,19 +1,20 @@ import { strict as assert } from 'node:assert'; import testUtils, { GLOBAL } from '../test-utils'; import ARRPOP from './ARRPOP'; +import { parseArgs } from '@redis/client/lib/commands/generic-transformers'; describe('JSON.ARRPOP', () => { describe('transformArguments', () => { it('simple', () => { assert.deepEqual( - ARRPOP.transformArguments('key'), + parseArgs(ARRPOP, 'key'), ['JSON.ARRPOP', 'key'] ); }); it('with path', () => { assert.deepEqual( - ARRPOP.transformArguments('key', { + parseArgs(ARRPOP, 'key', { path: '$' }), ['JSON.ARRPOP', 'key', '$'] @@ -22,7 +23,7 @@ describe('JSON.ARRPOP', () => { it('with path and index', () => { assert.deepEqual( - ARRPOP.transformArguments('key', { + parseArgs(ARRPOP, 'key', { path: '$', index: 0 }), diff --git a/packages/json/lib/commands/ARRPOP.ts b/packages/json/lib/commands/ARRPOP.ts index 4eafe9fdde4..375668471d1 100644 --- a/packages/json/lib/commands/ARRPOP.ts +++ b/packages/json/lib/commands/ARRPOP.ts @@ -1,5 +1,6 @@ -import { RedisArgument, ArrayReply, NullReply, BlobStringReply, Command, UnwrapReply } from '@redis/client/dist/lib/RESP/types'; -import { isArrayReply } from '@redis/client/dist/lib/commands/generic-transformers'; +import { CommandParser } from '@redis/client/lib/client/parser'; +import { RedisArgument, ArrayReply, NullReply, BlobStringReply, Command, UnwrapReply } from '@redis/client/lib/RESP/types'; +import { isArrayReply } from '@redis/client/lib/commands/generic-transformers'; import { transformRedisJsonNullReply } from '.'; export interface RedisArrPopOptions { @@ -8,20 +9,18 @@ export interface RedisArrPopOptions { } export default { - FIRST_KEY_INDEX: 1, IS_READ_ONLY: false, - transformArguments(key: RedisArgument, options?: RedisArrPopOptions) { - const args = ['JSON.ARRPOP', key]; + parseCommand(parser: CommandParser, key: RedisArgument, options?: RedisArrPopOptions) { + parser.push('JSON.ARRPOP'); + parser.pushKey(key); if (options) { - args.push(options.path); + parser.push(options.path); if (options.index !== undefined) { - args.push(options.index.toString()); + parser.push(options.index.toString()); } } - - return args; }, transformReply(reply: NullReply | BlobStringReply | ArrayReply) { return isArrayReply(reply) ? diff --git a/packages/json/lib/commands/ARRTRIM.spec.ts b/packages/json/lib/commands/ARRTRIM.spec.ts index 4c2f72aaa50..e346716e8df 100644 --- a/packages/json/lib/commands/ARRTRIM.spec.ts +++ b/packages/json/lib/commands/ARRTRIM.spec.ts @@ -1,11 +1,12 @@ import { strict as assert } from 'node:assert'; import testUtils, { GLOBAL } from '../test-utils'; import ARRTRIM from './ARRTRIM'; +import { parseArgs } from '@redis/client/lib/commands/generic-transformers'; describe('JSON.ARRTRIM', () => { it('transformArguments', () => { assert.deepEqual( - ARRTRIM.transformArguments('key', '$', 0, 1), + parseArgs(ARRTRIM, 'key', '$', 0, 1), ['JSON.ARRTRIM', 'key', '$', '0', '1'] ); }); diff --git a/packages/json/lib/commands/ARRTRIM.ts b/packages/json/lib/commands/ARRTRIM.ts index ab31f159491..e7cde0dc172 100644 --- a/packages/json/lib/commands/ARRTRIM.ts +++ b/packages/json/lib/commands/ARRTRIM.ts @@ -1,10 +1,12 @@ -import { RedisArgument, ArrayReply, NumberReply, NullReply, Command } from '@redis/client/dist/lib/RESP/types'; +import { CommandParser } from '@redis/client/lib/client/parser'; +import { RedisArgument, ArrayReply, NumberReply, NullReply, Command } from '@redis/client/lib/RESP/types'; export default { - FIRST_KEY_INDEX: 1, IS_READ_ONLY: false, - transformArguments(key: RedisArgument, path: RedisArgument, start: number, stop: number) { - return ['JSON.ARRTRIM', key, path, start.toString(), stop.toString()]; + parseCommand(parser: CommandParser, key: RedisArgument, path: RedisArgument, start: number, stop: number) { + parser.push('JSON.ARRTRIM'); + parser.pushKey(key); + parser.push(path, start.toString(), stop.toString()); }, transformReply: undefined as unknown as () => NumberReply | ArrayReply } as const satisfies Command; diff --git a/packages/json/lib/commands/CLEAR.spec.ts b/packages/json/lib/commands/CLEAR.spec.ts index 983e6bec2d9..c1786cc1dde 100644 --- a/packages/json/lib/commands/CLEAR.spec.ts +++ b/packages/json/lib/commands/CLEAR.spec.ts @@ -1,19 +1,20 @@ import { strict as assert } from 'node:assert'; import testUtils, { GLOBAL } from '../test-utils'; import CLEAR from './CLEAR'; +import { parseArgs } from '@redis/client/lib/commands/generic-transformers'; describe('JSON.CLEAR', () => { describe('transformArguments', () => { it('simple', () => { assert.deepEqual( - CLEAR.transformArguments('key'), + parseArgs(CLEAR, 'key'), ['JSON.CLEAR', 'key'] ); }); it('with path', () => { assert.deepEqual( - CLEAR.transformArguments('key', { + parseArgs(CLEAR, 'key', { path: '$' }), ['JSON.CLEAR', 'key', '$'] diff --git a/packages/json/lib/commands/CLEAR.ts b/packages/json/lib/commands/CLEAR.ts index 23e86d900e7..65d69ef18e5 100644 --- a/packages/json/lib/commands/CLEAR.ts +++ b/packages/json/lib/commands/CLEAR.ts @@ -1,20 +1,19 @@ -import { RedisArgument, NumberReply, Command } from '@redis/client/dist/lib/RESP/types'; +import { CommandParser } from '@redis/client/lib/client/parser'; +import { RedisArgument, NumberReply, Command } from '@redis/client/lib/RESP/types'; export interface JsonClearOptions { path?: RedisArgument; } export default { - FIRST_KEY_INDEX: 1, IS_READ_ONLY: false, - transformArguments(key: RedisArgument, options?: JsonClearOptions) { - const args = ['JSON.CLEAR', key]; + parseCommand(parser: CommandParser, key: RedisArgument, options?: JsonClearOptions) { + parser.push('JSON.CLEAR'); + parser.pushKey(key); if (options?.path !== undefined) { - args.push(options.path); + parser.push(options.path); } - - return args; }, transformReply: undefined as unknown as () => NumberReply } as const satisfies Command; diff --git a/packages/json/lib/commands/DEBUG_MEMORY.spec.ts b/packages/json/lib/commands/DEBUG_MEMORY.spec.ts index c41d07cb27e..09c29328d8e 100644 --- a/packages/json/lib/commands/DEBUG_MEMORY.spec.ts +++ b/packages/json/lib/commands/DEBUG_MEMORY.spec.ts @@ -1,19 +1,20 @@ import { strict as assert } from 'node:assert'; import testUtils, { GLOBAL } from '../test-utils'; import DEBUG_MEMORY from './DEBUG_MEMORY'; +import { parseArgs } from '@redis/client/lib/commands/generic-transformers'; describe('JSON.DEBUG MEMORY', () => { describe('transformArguments', () => { it('simple', () => { assert.deepEqual( - DEBUG_MEMORY.transformArguments('key'), + parseArgs(DEBUG_MEMORY, 'key'), ['JSON.DEBUG', 'MEMORY', 'key'] ); }); it('with path', () => { assert.deepEqual( - DEBUG_MEMORY.transformArguments('key', { + parseArgs(DEBUG_MEMORY, 'key', { path: '$' }), ['JSON.DEBUG', 'MEMORY', 'key', '$'] diff --git a/packages/json/lib/commands/DEBUG_MEMORY.ts b/packages/json/lib/commands/DEBUG_MEMORY.ts index c2e730b9dc2..5c4641c3270 100644 --- a/packages/json/lib/commands/DEBUG_MEMORY.ts +++ b/packages/json/lib/commands/DEBUG_MEMORY.ts @@ -1,20 +1,19 @@ -import { RedisArgument, NumberReply, Command } from '@redis/client/dist/lib/RESP/types'; +import { CommandParser } from '@redis/client/lib/client/parser'; +import { RedisArgument, NumberReply, Command } from '@redis/client/lib/RESP/types'; export interface JsonDebugMemoryOptions { path?: RedisArgument; } export default { - FIRST_KEY_INDEX: 2, IS_READ_ONLY: false, - transformArguments(key: RedisArgument, options?: JsonDebugMemoryOptions) { - const args = ['JSON.DEBUG', 'MEMORY', key]; + parseCommand(parser: CommandParser, key: RedisArgument, options?: JsonDebugMemoryOptions) { + parser.push('JSON.DEBUG', 'MEMORY'); + parser.pushKey(key); if (options?.path !== undefined) { - args.push(options.path); + parser.push(options.path); } - - return args; }, transformReply: undefined as unknown as () => NumberReply } as const satisfies Command; diff --git a/packages/json/lib/commands/DEL.spec.ts b/packages/json/lib/commands/DEL.spec.ts index 18f6a8f2db6..a008c3b9b2b 100644 --- a/packages/json/lib/commands/DEL.spec.ts +++ b/packages/json/lib/commands/DEL.spec.ts @@ -1,19 +1,20 @@ import { strict as assert } from 'node:assert'; import testUtils, { GLOBAL } from '../test-utils'; import DEL from './DEL'; +import { parseArgs } from '@redis/client/lib/commands/generic-transformers'; describe('JSON.DEL', () => { describe('transformArguments', () => { it('simple', () => { assert.deepEqual( - DEL.transformArguments('key'), + parseArgs(DEL, 'key'), ['JSON.DEL', 'key'] ); }); it('with path', () => { assert.deepEqual( - DEL.transformArguments('key', { + parseArgs(DEL, 'key', { path: '$.path' }), ['JSON.DEL', 'key', '$.path'] diff --git a/packages/json/lib/commands/DEL.ts b/packages/json/lib/commands/DEL.ts index f6952a8dc63..d4d8ed4620b 100644 --- a/packages/json/lib/commands/DEL.ts +++ b/packages/json/lib/commands/DEL.ts @@ -1,20 +1,19 @@ -import { RedisArgument, NumberReply, Command } from '@redis/client/dist/lib/RESP/types'; +import { CommandParser } from '@redis/client/lib/client/parser'; +import { RedisArgument, NumberReply, Command } from '@redis/client/lib/RESP/types'; export interface JsonDelOptions { path?: RedisArgument } export default { - FIRST_KEY_INDEX: 1, IS_READ_ONLY: false, - transformArguments(key: RedisArgument, options?: JsonDelOptions) { - const args = ['JSON.DEL', key]; + parseCommand(parser: CommandParser, key: RedisArgument, options?: JsonDelOptions) { + parser.push('JSON.DEL'); + parser.pushKey(key); if (options?.path !== undefined) { - args.push(options.path); + parser.push(options.path); } - - return args; }, transformReply: undefined as unknown as () => NumberReply } as const satisfies Command; diff --git a/packages/json/lib/commands/FORGET.spec.ts b/packages/json/lib/commands/FORGET.spec.ts index 04066ec43a9..888fff5659b 100644 --- a/packages/json/lib/commands/FORGET.spec.ts +++ b/packages/json/lib/commands/FORGET.spec.ts @@ -1,19 +1,20 @@ import { strict as assert } from 'node:assert'; import testUtils, { GLOBAL } from '../test-utils'; import FORGET from './FORGET'; +import { parseArgs } from '@redis/client/lib/commands/generic-transformers'; describe('JSON.FORGET', () => { describe('transformArguments', () => { it('key', () => { assert.deepEqual( - FORGET.transformArguments('key'), + parseArgs(FORGET, 'key'), ['JSON.FORGET', 'key'] ); }); it('key, path', () => { assert.deepEqual( - FORGET.transformArguments('key', { + parseArgs(FORGET, 'key', { path: '$.path' }), ['JSON.FORGET', 'key', '$.path'] diff --git a/packages/json/lib/commands/FORGET.ts b/packages/json/lib/commands/FORGET.ts index 68335ee92e9..1b5b97038e0 100644 --- a/packages/json/lib/commands/FORGET.ts +++ b/packages/json/lib/commands/FORGET.ts @@ -1,20 +1,19 @@ -import { RedisArgument, NumberReply, Command } from '@redis/client/dist/lib/RESP/types'; +import { CommandParser } from '@redis/client/lib/client/parser'; +import { RedisArgument, NumberReply, Command } from '@redis/client/lib/RESP/types'; export interface JsonForgetOptions { path?: RedisArgument; } export default { - FIRST_KEY_INDEX: 1, IS_READ_ONLY: false, - transformArguments(key: RedisArgument, options?: JsonForgetOptions) { - const args = ['JSON.FORGET', key]; + parseCommand(parser: CommandParser, key: RedisArgument, options?: JsonForgetOptions) { + parser.push('JSON.FORGET'); + parser.pushKey(key); if (options?.path !== undefined) { - args.push(options.path); + parser.push(options.path); } - - return args; }, transformReply: undefined as unknown as () => NumberReply } as const satisfies Command; diff --git a/packages/json/lib/commands/GET.spec.ts b/packages/json/lib/commands/GET.spec.ts index 6d6ff14f67f..0741de316e1 100644 --- a/packages/json/lib/commands/GET.spec.ts +++ b/packages/json/lib/commands/GET.spec.ts @@ -1,12 +1,13 @@ import { strict as assert } from 'node:assert'; import testUtils, { GLOBAL } from '../test-utils'; import GET from './GET'; +import { parseArgs } from '@redis/client/lib/commands/generic-transformers'; describe('JSON.GET', () => { describe('transformArguments', () => { it('simple', () => { assert.deepEqual( - GET.transformArguments('key'), + parseArgs(GET, 'key'), ['JSON.GET', 'key'] ); }); @@ -14,14 +15,14 @@ describe('JSON.GET', () => { describe('with path', () => { it('string', () => { assert.deepEqual( - GET.transformArguments('key', { path: '$' }), + parseArgs(GET, 'key', { path: '$' }), ['JSON.GET', 'key', '$'] ); }); it('array', () => { assert.deepEqual( - GET.transformArguments('key', { path: ['$.1', '$.2'] }), + parseArgs(GET, 'key', { path: ['$.1', '$.2'] }), ['JSON.GET', 'key', '$.1', '$.2'] ); }); diff --git a/packages/json/lib/commands/GET.ts b/packages/json/lib/commands/GET.ts index b7bcc52e3cb..3d623483d20 100644 --- a/packages/json/lib/commands/GET.ts +++ b/packages/json/lib/commands/GET.ts @@ -1,5 +1,6 @@ -import { RedisArgument, Command } from '@redis/client/dist/lib/RESP/types'; -import { RedisVariadicArgument, pushVariadicArguments } from '@redis/client/dist/lib/commands/generic-transformers'; +import { CommandParser } from '@redis/client/lib/client/parser'; +import { RedisArgument, Command } from '@redis/client/lib/RESP/types'; +import { RedisVariadicArgument } from '@redis/client/lib/commands/generic-transformers'; import { transformRedisJsonNullReply } from '.'; export interface JsonGetOptions { @@ -7,16 +8,13 @@ export interface JsonGetOptions { } export default { - FIRST_KEY_INDEX: 1, IS_READ_ONLY: false, - transformArguments(key: RedisArgument, options?: JsonGetOptions) { - let args = ['JSON.GET', key]; - + parseCommand(parser: CommandParser, key: RedisArgument, options?: JsonGetOptions) { + parser.push('JSON.GET'); + parser.pushKey(key); if (options?.path !== undefined) { - args = pushVariadicArguments(args, options.path); + parser.pushVariadic(options.path) } - - return args; }, transformReply: transformRedisJsonNullReply } as const satisfies Command; diff --git a/packages/json/lib/commands/MERGE.spec.ts b/packages/json/lib/commands/MERGE.spec.ts index 56f5d25e7d5..30a092035c5 100644 --- a/packages/json/lib/commands/MERGE.spec.ts +++ b/packages/json/lib/commands/MERGE.spec.ts @@ -1,11 +1,12 @@ import { strict as assert } from 'node:assert'; import testUtils, { GLOBAL } from '../test-utils'; import MERGE from './MERGE'; +import { parseArgs } from '@redis/client/lib/commands/generic-transformers'; describe('JSON.MERGE', () => { it('transformArguments', () => { assert.deepEqual( - MERGE.transformArguments('key', '$', 'value'), + parseArgs(MERGE, 'key', '$', 'value'), ['JSON.MERGE', 'key', '$', '"value"'] ); }); diff --git a/packages/json/lib/commands/MERGE.ts b/packages/json/lib/commands/MERGE.ts index 90cd080a06e..25ae84c6ed9 100644 --- a/packages/json/lib/commands/MERGE.ts +++ b/packages/json/lib/commands/MERGE.ts @@ -1,16 +1,13 @@ -import { SimpleStringReply, Command, RedisArgument } from '@redis/client/dist/lib/RESP/types'; +import { CommandParser } from '@redis/client/lib/client/parser'; +import { SimpleStringReply, Command, RedisArgument } from '@redis/client/lib/RESP/types'; import { RedisJSON, transformRedisJsonArgument } from '.'; export default { - FIRST_KEY_INDEX: 1, IS_READ_ONLY: false, - transformArguments(key: RedisArgument, path: RedisArgument, value: RedisJSON) { - return [ - 'JSON.MERGE', - key, - path, - transformRedisJsonArgument(value) - ]; + parseCommand(parser: CommandParser, key: RedisArgument, path: RedisArgument, value: RedisJSON) { + parser.push('JSON.MERGE'); + parser.pushKey(key); + parser.push(path, transformRedisJsonArgument(value)); }, transformReply: undefined as unknown as () => SimpleStringReply<'OK'> } as const satisfies Command; diff --git a/packages/json/lib/commands/MGET.spec.ts b/packages/json/lib/commands/MGET.spec.ts index 1bfaecd6da9..2d8efafde71 100644 --- a/packages/json/lib/commands/MGET.spec.ts +++ b/packages/json/lib/commands/MGET.spec.ts @@ -1,11 +1,12 @@ import { strict as assert } from 'node:assert'; import testUtils, { GLOBAL } from '../test-utils'; import MGET from './MGET'; +import { parseArgs } from '@redis/client/lib/commands/generic-transformers'; describe('JSON.MGET', () => { it('transformArguments', () => { assert.deepEqual( - MGET.transformArguments(['1', '2'], '$'), + parseArgs(MGET, ['1', '2'], '$'), ['JSON.MGET', '1', '2', '$'] ); }); diff --git a/packages/json/lib/commands/MGET.ts b/packages/json/lib/commands/MGET.ts index a7aea82ac2e..79241bee7c4 100644 --- a/packages/json/lib/commands/MGET.ts +++ b/packages/json/lib/commands/MGET.ts @@ -1,15 +1,13 @@ -import { RedisArgument, UnwrapReply, ArrayReply, NullReply, BlobStringReply, Command } from '@redis/client/dist/lib/RESP/types'; +import { CommandParser } from '@redis/client/lib/client/parser'; +import { RedisArgument, UnwrapReply, ArrayReply, NullReply, BlobStringReply, Command } from '@redis/client/lib/RESP/types'; import { transformRedisJsonNullReply } from '.'; export default { - FIRST_KEY_INDEX: 1, IS_READ_ONLY: true, - transformArguments(keys: Array, path: RedisArgument) { - return [ - 'JSON.MGET', - ...keys, - path - ]; + parseCommand(parser: CommandParser, keys: Array, path: RedisArgument) { + parser.push('JSON.MGET'); + parser.pushKeys(keys); + parser.push(path); }, transformReply(reply: UnwrapReply>) { return reply.map(json => transformRedisJsonNullReply(json)) diff --git a/packages/json/lib/commands/MSET.spec.ts b/packages/json/lib/commands/MSET.spec.ts index 360890234c8..38e8b077e81 100644 --- a/packages/json/lib/commands/MSET.spec.ts +++ b/packages/json/lib/commands/MSET.spec.ts @@ -1,11 +1,12 @@ import { strict as assert } from 'node:assert'; import testUtils, { GLOBAL } from '../test-utils'; import MSET from './MSET'; +import { parseArgs } from '@redis/client/lib/commands/generic-transformers'; describe('JSON.MSET', () => { it('transformArguments', () => { assert.deepEqual( - MSET.transformArguments([{ + parseArgs(MSET, [{ key: '1', path: '$', value: 1 diff --git a/packages/json/lib/commands/MSET.ts b/packages/json/lib/commands/MSET.ts index a081bfd543a..b6b42563c70 100644 --- a/packages/json/lib/commands/MSET.ts +++ b/packages/json/lib/commands/MSET.ts @@ -1,4 +1,5 @@ -import { RedisArgument, SimpleStringReply, Command } from '@redis/client/dist/lib/RESP/types'; +import { CommandParser } from '@redis/client/lib/client/parser'; +import { RedisArgument, SimpleStringReply, Command } from '@redis/client/lib/RESP/types'; import { RedisJSON, transformRedisJsonArgument } from '.'; export interface JsonMSetItem { @@ -8,21 +9,14 @@ export interface JsonMSetItem { } export default { - FIRST_KEY_INDEX: 1, IS_READ_ONLY: false, - transformArguments(items: Array) { - const args = new Array(1 + items.length * 3); - args[0] = 'JSON.MSET'; + parseCommand(parser: CommandParser, items: Array) { + parser.push('JSON.MSET'); - let argsIndex = 1; for (let i = 0; i < items.length; i++) { - const item = items[i]; - args[argsIndex++] = item.key; - args[argsIndex++] = item.path; - args[argsIndex++] = transformRedisJsonArgument(item.value); + parser.pushKey(items[i].key); + parser.push(items[i].path, transformRedisJsonArgument(items[i].value)); } - - return args; }, transformReply: undefined as unknown as () => SimpleStringReply<'OK'> } as const satisfies Command; diff --git a/packages/json/lib/commands/NUMINCRBY.spec.ts b/packages/json/lib/commands/NUMINCRBY.spec.ts index d0bffd2bd2a..b438069e80f 100644 --- a/packages/json/lib/commands/NUMINCRBY.spec.ts +++ b/packages/json/lib/commands/NUMINCRBY.spec.ts @@ -1,11 +1,12 @@ import { strict as assert } from 'node:assert'; import testUtils, { GLOBAL } from '../test-utils'; import NUMINCRBY from './NUMINCRBY'; +import { parseArgs } from '@redis/client/lib/commands/generic-transformers'; describe('JSON.NUMINCRBY', () => { it('transformArguments', () => { assert.deepEqual( - NUMINCRBY.transformArguments('key', '$', 1), + parseArgs(NUMINCRBY, 'key', '$', 1), ['JSON.NUMINCRBY', 'key', '$', '1'] ); }); diff --git a/packages/json/lib/commands/NUMINCRBY.ts b/packages/json/lib/commands/NUMINCRBY.ts index 65cc7db68a9..8c41194a9b9 100644 --- a/packages/json/lib/commands/NUMINCRBY.ts +++ b/packages/json/lib/commands/NUMINCRBY.ts @@ -1,10 +1,12 @@ -import { RedisArgument, ArrayReply, NumberReply, DoubleReply, NullReply, BlobStringReply, UnwrapReply, Command } from '@redis/client/dist/lib/RESP/types'; +import { CommandParser } from '@redis/client/lib/client/parser'; +import { RedisArgument, ArrayReply, NumberReply, DoubleReply, NullReply, BlobStringReply, UnwrapReply, Command } from '@redis/client/lib/RESP/types'; export default { - FIRST_KEY_INDEX: 1, IS_READ_ONLY: false, - transformArguments(key: RedisArgument, path: RedisArgument, by: number) { - return ['JSON.NUMINCRBY', key, path, by.toString()]; + parseCommand(parser: CommandParser, key: RedisArgument, path: RedisArgument, by: number) { + parser.push('JSON.NUMINCRBY'); + parser.pushKey(key); + parser.push(path, by.toString()); }, transformReply: { 2: (reply: UnwrapReply) => { diff --git a/packages/json/lib/commands/NUMMULTBY.spec.ts b/packages/json/lib/commands/NUMMULTBY.spec.ts index 9767c2b0979..24ee932e952 100644 --- a/packages/json/lib/commands/NUMMULTBY.spec.ts +++ b/packages/json/lib/commands/NUMMULTBY.spec.ts @@ -1,11 +1,12 @@ import { strict as assert } from 'node:assert'; import testUtils, { GLOBAL } from '../test-utils'; import NUMMULTBY from './NUMMULTBY'; +import { parseArgs } from '@redis/client/lib/commands/generic-transformers'; describe('JSON.NUMMULTBY', () => { it('transformArguments', () => { assert.deepEqual( - NUMMULTBY.transformArguments('key', '$', 2), + parseArgs(NUMMULTBY, 'key', '$', 2), ['JSON.NUMMULTBY', 'key', '$', '2'] ); }); diff --git a/packages/json/lib/commands/NUMMULTBY.ts b/packages/json/lib/commands/NUMMULTBY.ts index 255685a9a50..5eeb4b50e88 100644 --- a/packages/json/lib/commands/NUMMULTBY.ts +++ b/packages/json/lib/commands/NUMMULTBY.ts @@ -1,11 +1,13 @@ -import { RedisArgument, Command } from '@redis/client/dist/lib/RESP/types'; +import { CommandParser } from '@redis/client/lib/client/parser'; +import { RedisArgument, Command } from '@redis/client/lib/RESP/types'; import NUMINCRBY from './NUMINCRBY'; export default { - FIRST_KEY_INDEX: 1, IS_READ_ONLY: false, - transformArguments(key: RedisArgument, path: RedisArgument, by: number) { - return ['JSON.NUMMULTBY', key, path, by.toString()]; + parseCommand(parser: CommandParser, key: RedisArgument, path: RedisArgument, by: number) { + parser.push('JSON.NUMMULTBY'); + parser.pushKey(key); + parser.push(path, by.toString()); }, transformReply: NUMINCRBY.transformReply } as const satisfies Command; diff --git a/packages/json/lib/commands/OBJKEYS.spec.ts b/packages/json/lib/commands/OBJKEYS.spec.ts index dc984cb2ce9..0d2176248e4 100644 --- a/packages/json/lib/commands/OBJKEYS.spec.ts +++ b/packages/json/lib/commands/OBJKEYS.spec.ts @@ -1,19 +1,20 @@ import { strict as assert } from 'node:assert'; import testUtils, { GLOBAL } from '../test-utils'; import OBJKEYS from './OBJKEYS'; +import { parseArgs } from '@redis/client/lib/commands/generic-transformers'; describe('JSON.OBJKEYS', () => { describe('transformArguments', () => { it('simple', () => { assert.deepEqual( - OBJKEYS.transformArguments('key'), + parseArgs(OBJKEYS, 'key'), ['JSON.OBJKEYS', 'key'] ); }); it('with path', () => { assert.deepEqual( - OBJKEYS.transformArguments('key', { + parseArgs(OBJKEYS, 'key', { path: '$' }), ['JSON.OBJKEYS', 'key', '$'] diff --git a/packages/json/lib/commands/OBJKEYS.ts b/packages/json/lib/commands/OBJKEYS.ts index fb8ae6bb034..6d23da20534 100644 --- a/packages/json/lib/commands/OBJKEYS.ts +++ b/packages/json/lib/commands/OBJKEYS.ts @@ -1,20 +1,18 @@ -import { RedisArgument, ArrayReply, BlobStringReply, NullReply, Command } from '@redis/client/dist/lib/RESP/types'; +import { CommandParser } from '@redis/client/lib/client/parser'; +import { RedisArgument, ArrayReply, BlobStringReply, NullReply, Command } from '@redis/client/lib/RESP/types'; export interface JsonObjKeysOptions { path?: RedisArgument; } export default { - FIRST_KEY_INDEX: 1, IS_READ_ONLY: false, - transformArguments(key: RedisArgument, options?: JsonObjKeysOptions) { - const args = ['JSON.OBJKEYS', key]; - + parseCommand(parser: CommandParser, key: RedisArgument, options?: JsonObjKeysOptions) { + parser.push('JSON.OBJKEYS'); + parser.pushKey(key); if (options?.path !== undefined) { - args.push(options.path); + parser.push(options.path); } - - return args; }, transformReply: undefined as unknown as () => ArrayReply | ArrayReply | NullReply> } as const satisfies Command; diff --git a/packages/json/lib/commands/OBJLEN.spec.ts b/packages/json/lib/commands/OBJLEN.spec.ts index 8f616107fd5..a5664a4d6bc 100644 --- a/packages/json/lib/commands/OBJLEN.spec.ts +++ b/packages/json/lib/commands/OBJLEN.spec.ts @@ -1,19 +1,20 @@ import { strict as assert } from 'node:assert'; import testUtils, { GLOBAL } from '../test-utils'; import OBJLEN from './OBJLEN'; +import { parseArgs } from '@redis/client/lib/commands/generic-transformers'; describe('JSON.OBJLEN', () => { describe('transformArguments', () => { it('simple', () => { assert.deepEqual( - OBJLEN.transformArguments('key'), + parseArgs(OBJLEN, 'key'), ['JSON.OBJLEN', 'key'] ); }); it('with path', () => { assert.deepEqual( - OBJLEN.transformArguments('key', { + parseArgs(OBJLEN, 'key', { path: '$' }), ['JSON.OBJLEN', 'key', '$'] diff --git a/packages/json/lib/commands/OBJLEN.ts b/packages/json/lib/commands/OBJLEN.ts index f9c45e336ad..e15a72e2165 100644 --- a/packages/json/lib/commands/OBJLEN.ts +++ b/packages/json/lib/commands/OBJLEN.ts @@ -1,20 +1,18 @@ -import { RedisArgument, NumberReply, ArrayReply, NullReply, Command } from '@redis/client/dist/lib/RESP/types'; +import { CommandParser } from '@redis/client/lib/client/parser'; +import { RedisArgument, NumberReply, ArrayReply, NullReply, Command } from '@redis/client/lib/RESP/types'; export interface JsonObjLenOptions { path?: RedisArgument; } export default { - FIRST_KEY_INDEX: 1, IS_READ_ONLY: true, - transformArguments(key: RedisArgument, options?: JsonObjLenOptions) { - const args = ['JSON.OBJLEN', key]; - + parseCommand(parser: CommandParser, key: RedisArgument, options?: JsonObjLenOptions) { + parser.push('JSON.OBJLEN'); + parser.pushKey(key); if (options?.path !== undefined) { - args.push(options.path); + parser.push(options.path); } - - return args; }, transformReply: undefined as unknown as () => NumberReply | ArrayReply } as const satisfies Command; diff --git a/packages/json/lib/commands/RESP.spec.ts b/packages/json/lib/commands/RESP.spec.ts index 6dfc3360326..2cb3e9e15c3 100644 --- a/packages/json/lib/commands/RESP.spec.ts +++ b/packages/json/lib/commands/RESP.spec.ts @@ -1,19 +1,20 @@ import { strict as assert } from 'node:assert'; import testUtils, { GLOBAL } from '../test-utils'; -import { transformArguments } from './RESP'; +import RESP from './RESP'; +import { parseArgs } from '@redis/client/lib/commands/generic-transformers'; describe('RESP', () => { describe('transformArguments', () => { it('without path', () => { assert.deepEqual( - transformArguments('key'), + parseArgs(RESP, 'key'), ['JSON.RESP', 'key'] ); }); it('with path', () => { assert.deepEqual( - transformArguments('key', '$'), + parseArgs(RESP, 'key', '$'), ['JSON.RESP', 'key', '$'] ); }); diff --git a/packages/json/lib/commands/RESP.ts b/packages/json/lib/commands/RESP.ts index fcf54cd3537..c9713833958 100644 --- a/packages/json/lib/commands/RESP.ts +++ b/packages/json/lib/commands/RESP.ts @@ -1,15 +1,16 @@ -export const FIRST_KEY_INDEX = 1; - -export function transformArguments(key: string, path?: string): Array { - const args = ['JSON.RESP', key]; - - if (path) { - args.push(path); - } - - return args; -} +import { CommandParser } from "@redis/client/lib/client/parser"; +import { Command, RedisArgument } from "@redis/client/lib/RESP/types"; type RESPReply = Array; -export declare function transformReply(): RESPReply; +export default { + IS_READ_ONLY: true, + parseCommand(parser: CommandParser, key: RedisArgument, path?: string) { + parser.push('JSON.RESP'); + parser.pushKey(key); + if (path !== undefined) { + parser.push(path); + } + }, + transformReply: undefined as unknown as () => RESPReply + } as const satisfies Command; \ No newline at end of file diff --git a/packages/json/lib/commands/SET.spec.ts b/packages/json/lib/commands/SET.spec.ts index 15e27073281..7bd927f08e4 100644 --- a/packages/json/lib/commands/SET.spec.ts +++ b/packages/json/lib/commands/SET.spec.ts @@ -1,26 +1,27 @@ import { strict as assert } from 'node:assert'; import testUtils, { GLOBAL } from '../test-utils'; import SET from './SET'; +import { parseArgs } from '@redis/client/lib/commands/generic-transformers'; describe('JSON.SET', () => { describe('transformArguments', () => { it('transformArguments', () => { assert.deepEqual( - SET.transformArguments('key', '$', 'json'), + parseArgs(SET, 'key', '$', 'json'), ['JSON.SET', 'key', '$', '"json"'] ); }); it('NX', () => { assert.deepEqual( - SET.transformArguments('key', '$', 'json', { NX: true }), + parseArgs(SET, 'key', '$', 'json', { NX: true }), ['JSON.SET', 'key', '$', '"json"', 'NX'] ); }); it('XX', () => { assert.deepEqual( - SET.transformArguments('key', '$', 'json', { XX: true }), + parseArgs(SET, 'key', '$', 'json', { XX: true }), ['JSON.SET', 'key', '$', '"json"', 'XX'] ); }); diff --git a/packages/json/lib/commands/SET.ts b/packages/json/lib/commands/SET.ts index 78aea4b3545..6bd89803ded 100644 --- a/packages/json/lib/commands/SET.ts +++ b/packages/json/lib/commands/SET.ts @@ -1,4 +1,5 @@ -import { RedisArgument, SimpleStringReply, NullReply, Command } from '@redis/client/dist/lib/RESP/types'; +import { CommandParser } from '@redis/client/lib/client/parser'; +import { RedisArgument, SimpleStringReply, NullReply, Command } from '@redis/client/lib/RESP/types'; import { RedisJSON, transformRedisJsonArgument } from '.'; export interface JsonSetOptions { @@ -14,25 +15,25 @@ export interface JsonSetOptions { } export default { - FIRST_KEY_INDEX: 1, IS_READ_ONLY: false, - transformArguments( + parseCommand( + parser: CommandParser, key: RedisArgument, path: RedisArgument, json: RedisJSON, options?: JsonSetOptions ) { - const args = ['JSON.SET', key, path, transformRedisJsonArgument(json)]; + parser.push('JSON.SET'); + parser.pushKey(key); + parser.push(path, transformRedisJsonArgument(json)); if (options?.condition) { - args.push(options?.condition); + parser.push(options?.condition); } else if (options?.NX) { - args.push('NX'); + parser.push('NX'); } else if (options?.XX) { - args.push('XX'); + parser.push('XX'); } - - return args; }, transformReply: undefined as unknown as () => SimpleStringReply<'OK'> | NullReply } as const satisfies Command; diff --git a/packages/json/lib/commands/STRAPPEND.spec.ts b/packages/json/lib/commands/STRAPPEND.spec.ts index 0d8bdb57185..ebd539130e1 100644 --- a/packages/json/lib/commands/STRAPPEND.spec.ts +++ b/packages/json/lib/commands/STRAPPEND.spec.ts @@ -1,19 +1,20 @@ import { strict as assert } from 'node:assert'; import testUtils, { GLOBAL } from '../test-utils'; import STRAPPEND from './STRAPPEND'; +import { parseArgs } from '@redis/client/lib/commands/generic-transformers'; describe('JSON.STRAPPEND', () => { describe('transformArguments', () => { it('simple', () => { assert.deepEqual( - STRAPPEND.transformArguments('key', 'append'), + parseArgs(STRAPPEND, 'key', 'append'), ['JSON.STRAPPEND', 'key', '"append"'] ); }); it('with path', () => { assert.deepEqual( - STRAPPEND.transformArguments('key', 'append', { + parseArgs(STRAPPEND, 'key', 'append', { path: '$' }), ['JSON.STRAPPEND', 'key', '$', '"append"'] diff --git a/packages/json/lib/commands/STRAPPEND.ts b/packages/json/lib/commands/STRAPPEND.ts index 12ee7cc394c..97bbebf931b 100644 --- a/packages/json/lib/commands/STRAPPEND.ts +++ b/packages/json/lib/commands/STRAPPEND.ts @@ -1,4 +1,5 @@ -import { RedisArgument, Command, NullReply, NumberReply, ArrayReply } from '@redis/client/dist/lib/RESP/types'; +import { CommandParser } from '@redis/client/lib/client/parser'; +import { RedisArgument, Command, NullReply, NumberReply, ArrayReply } from '@redis/client/lib/RESP/types'; import { transformRedisJsonArgument } from '.'; export interface JsonStrAppendOptions { @@ -6,17 +7,16 @@ export interface JsonStrAppendOptions { } export default { - FIRST_KEY_INDEX: 1, IS_READ_ONLY: false, - transformArguments(key: RedisArgument, append: string, options?: JsonStrAppendOptions) { - const args = ['JSON.STRAPPEND', key]; + parseCommand(parser: CommandParser, key: RedisArgument, append: string, options?: JsonStrAppendOptions) { + parser.push('JSON.STRAPPEND'); + parser.pushKey(key); if (options?.path !== undefined) { - args.push(options.path); + parser.push(options.path); } - args.push(transformRedisJsonArgument(append)); - return args; + parser.push(transformRedisJsonArgument(append)); }, transformReply: undefined as unknown as () => NumberReply | ArrayReply } as const satisfies Command; diff --git a/packages/json/lib/commands/STRLEN.spec.ts b/packages/json/lib/commands/STRLEN.spec.ts index e058e48a635..b6881b5bd52 100644 --- a/packages/json/lib/commands/STRLEN.spec.ts +++ b/packages/json/lib/commands/STRLEN.spec.ts @@ -1,19 +1,20 @@ import { strict as assert } from 'node:assert'; import testUtils, { GLOBAL } from '../test-utils'; import STRLEN from './STRLEN'; +import { parseArgs } from '@redis/client/lib/commands/generic-transformers'; describe('JSON.STRLEN', () => { describe('transformArguments', () => { it('simple', () => { assert.deepEqual( - STRLEN.transformArguments('key'), + parseArgs(STRLEN, 'key'), ['JSON.STRLEN', 'key'] ); }); it('with path', () => { assert.deepEqual( - STRLEN.transformArguments('key', { + parseArgs(STRLEN, 'key', { path: '$' }), ['JSON.STRLEN', 'key', '$'] diff --git a/packages/json/lib/commands/STRLEN.ts b/packages/json/lib/commands/STRLEN.ts index 3b514d9caba..b72f30cd6da 100644 --- a/packages/json/lib/commands/STRLEN.ts +++ b/packages/json/lib/commands/STRLEN.ts @@ -1,20 +1,19 @@ -import { RedisArgument, ArrayReply, NumberReply, NullReply, Command } from '@redis/client/dist/lib/RESP/types'; +import { CommandParser } from '@redis/client/lib/client/parser'; +import { RedisArgument, ArrayReply, NumberReply, NullReply, Command } from '@redis/client/lib/RESP/types'; export interface JsonStrLenOptions { path?: RedisArgument; } export default { - FIRST_KEY_INDEX: 1, IS_READ_ONLY: true, - transformArguments(key: RedisArgument, options?: JsonStrLenOptions) { - const args = ['JSON.STRLEN', key]; + parseCommand(parser: CommandParser, key: RedisArgument, options?: JsonStrLenOptions) { + parser.push('JSON.STRLEN'); + parser.pushKey(key); if (options?.path) { - args.push(options.path); + parser.push(options.path); } - - return args; }, transformReply: undefined as unknown as () => NumberReply | ArrayReply } as const satisfies Command; diff --git a/packages/json/lib/commands/TOGGLE.spec.ts b/packages/json/lib/commands/TOGGLE.spec.ts index c8a78877906..173c7708f4a 100644 --- a/packages/json/lib/commands/TOGGLE.spec.ts +++ b/packages/json/lib/commands/TOGGLE.spec.ts @@ -1,11 +1,12 @@ import { strict as assert } from 'node:assert'; import testUtils, { GLOBAL } from '../test-utils'; import TOGGLE from './TOGGLE'; +import { parseArgs } from '@redis/client/lib/commands/generic-transformers'; describe('JSON.TOGGLE', () => { it('transformArguments', () => { assert.deepEqual( - TOGGLE.transformArguments('key', '$'), + parseArgs(TOGGLE, 'key', '$'), ['JSON.TOGGLE', 'key', '$'] ); }); diff --git a/packages/json/lib/commands/TOGGLE.ts b/packages/json/lib/commands/TOGGLE.ts index 2a8df3eba36..2707d54b6fd 100644 --- a/packages/json/lib/commands/TOGGLE.ts +++ b/packages/json/lib/commands/TOGGLE.ts @@ -1,10 +1,12 @@ -import { RedisArgument, ArrayReply, NumberReply, NullReply, Command, } from '@redis/client/dist/lib/RESP/types'; +import { CommandParser } from '@redis/client/lib/client/parser'; +import { RedisArgument, ArrayReply, NumberReply, NullReply, Command, } from '@redis/client/lib/RESP/types'; export default { - FIRST_KEY_INDEX: 1, IS_READ_ONLY: false, - transformArguments(key: RedisArgument, path: RedisArgument) { - return ['JSON.TOGGLE', key, path]; + parseCommand(parser: CommandParser, key: RedisArgument, path: RedisArgument) { + parser.push('JSON.TOGGLE'); + parser.pushKey(key); + parser.push(path); }, transformReply: undefined as unknown as () => NumberReply | NullReply | ArrayReply } as const satisfies Command; diff --git a/packages/json/lib/commands/TYPE.spec.ts b/packages/json/lib/commands/TYPE.spec.ts index 103ce59de6e..1b6ad109816 100644 --- a/packages/json/lib/commands/TYPE.spec.ts +++ b/packages/json/lib/commands/TYPE.spec.ts @@ -1,19 +1,20 @@ import { strict as assert } from 'node:assert'; import testUtils, { GLOBAL } from '../test-utils'; import TYPE from './TYPE'; +import { parseArgs } from '@redis/client/lib/commands/generic-transformers'; describe('JSON.TYPE', () => { describe('transformArguments', () => { it('simple', () => { assert.deepEqual( - TYPE.transformArguments('key'), + parseArgs(TYPE, 'key'), ['JSON.TYPE', 'key'] ); }); it('with path', () => { assert.deepEqual( - TYPE.transformArguments('key', { + parseArgs(TYPE, 'key', { path: '$' }), ['JSON.TYPE', 'key', '$'] diff --git a/packages/json/lib/commands/TYPE.ts b/packages/json/lib/commands/TYPE.ts index c2eea9856e1..d19de31df68 100644 --- a/packages/json/lib/commands/TYPE.ts +++ b/packages/json/lib/commands/TYPE.ts @@ -1,20 +1,19 @@ -import { NullReply, BlobStringReply, ArrayReply, Command, RedisArgument, UnwrapReply } from '@redis/client/dist/lib/RESP/types'; +import { CommandParser } from '@redis/client/lib/client/parser'; +import { NullReply, BlobStringReply, ArrayReply, Command, RedisArgument, UnwrapReply } from '@redis/client/lib/RESP/types'; export interface JsonTypeOptions { path?: RedisArgument; } export default { - FIRST_KEY_INDEX: 1, IS_READ_ONLY: true, - transformArguments(key: RedisArgument, options?: JsonTypeOptions) { - const args = ['JSON.TYPE', key]; + parseCommand(parser: CommandParser, key: RedisArgument, options?: JsonTypeOptions) { + parser.push('JSON.TYPE'); + parser.pushKey(key); if (options?.path) { - args.push(options.path); + parser.push(options.path); } - - return args; }, transformReply: { 2: undefined as unknown as () => NullReply | BlobStringReply | ArrayReply, @@ -24,4 +23,3 @@ export default { } }, } as const satisfies Command; - diff --git a/packages/json/lib/commands/index.ts b/packages/json/lib/commands/index.ts index 2724ff2565c..8ea44ce8049 100644 --- a/packages/json/lib/commands/index.ts +++ b/packages/json/lib/commands/index.ts @@ -1,4 +1,4 @@ -import { BlobStringReply, NullReply, UnwrapReply } from '@redis/client/dist/lib/RESP/types'; +import { BlobStringReply, NullReply, UnwrapReply } from '@redis/client/lib/RESP/types'; import ARRAPPEND from './ARRAPPEND'; import ARRINDEX from './ARRINDEX'; import ARRINSERT from './ARRINSERT'; @@ -23,7 +23,7 @@ import STRAPPEND from './STRAPPEND'; import STRLEN from './STRLEN'; import TOGGLE from './TOGGLE'; import TYPE from './TYPE'; -import { isNullReply } from '@redis/client/dist/lib/commands/generic-transformers'; +import { isNullReply } from '@redis/client/lib/commands/generic-transformers'; export default { ARRAPPEND, diff --git a/packages/search/lib/commands/AGGREGATE.spec.ts b/packages/search/lib/commands/AGGREGATE.spec.ts index 50ef44f2bd6..787fbd1472f 100644 --- a/packages/search/lib/commands/AGGREGATE.spec.ts +++ b/packages/search/lib/commands/AGGREGATE.spec.ts @@ -1,19 +1,20 @@ import { strict as assert } from 'node:assert'; import testUtils, { GLOBAL } from '../test-utils'; import AGGREGATE from './AGGREGATE'; +import { parseArgs } from '@redis/client/lib/commands/generic-transformers'; describe('AGGREGATE', () => { describe('transformArguments', () => { it('without options', () => { assert.deepEqual( - AGGREGATE.transformArguments('index', '*'), + parseArgs(AGGREGATE, 'index', '*'), ['FT.AGGREGATE', 'index', '*'] ); }); it('with VERBATIM', () => { assert.deepEqual( - AGGREGATE.transformArguments('index', '*', { + parseArgs(AGGREGATE, 'index', '*', { VERBATIM: true }), ['FT.AGGREGATE', 'index', '*', 'VERBATIM'] @@ -22,7 +23,7 @@ describe('AGGREGATE', () => { it('with ADDSCORES', () => { assert.deepEqual( - AGGREGATE.transformArguments('index', '*', { ADDSCORES: true }), + parseArgs(AGGREGATE, 'index', '*', { ADDSCORES: true }), ['FT.AGGREGATE', 'index', '*', 'ADDSCORES'] ); }); @@ -32,7 +33,7 @@ describe('AGGREGATE', () => { describe('without alias', () => { it('string', () => { assert.deepEqual( - AGGREGATE.transformArguments('index', '*', { + parseArgs(AGGREGATE, 'index', '*', { LOAD: '@property' }), ['FT.AGGREGATE', 'index', '*', 'LOAD', '1', '@property'] @@ -41,7 +42,7 @@ describe('AGGREGATE', () => { it('{ identifier: string }', () => { assert.deepEqual( - AGGREGATE.transformArguments('index', '*', { + parseArgs(AGGREGATE, 'index', '*', { LOAD: { identifier: '@property' } @@ -53,7 +54,7 @@ describe('AGGREGATE', () => { it('with alias', () => { assert.deepEqual( - AGGREGATE.transformArguments('index', '*', { + parseArgs(AGGREGATE, 'index', '*', { LOAD: { identifier: '@property', AS: 'alias' @@ -66,7 +67,7 @@ describe('AGGREGATE', () => { it('multiple', () => { assert.deepEqual( - AGGREGATE.transformArguments('index', '*', { + parseArgs(AGGREGATE, 'index', '*', { LOAD: ['@1', '@2'] }), ['FT.AGGREGATE', 'index', '*', 'LOAD', '2', '@1', '@2'] @@ -80,7 +81,7 @@ describe('AGGREGATE', () => { describe('without properties', () => { it('without alias', () => { assert.deepEqual( - AGGREGATE.transformArguments('index', '*', { + parseArgs(AGGREGATE, 'index', '*', { STEPS: [{ type: 'GROUPBY', REDUCE: { @@ -94,7 +95,7 @@ describe('AGGREGATE', () => { it('with alias', () => { assert.deepEqual( - AGGREGATE.transformArguments('index', '*', { + parseArgs(AGGREGATE, 'index', '*', { STEPS: [{ type: 'GROUPBY', REDUCE: { @@ -111,7 +112,7 @@ describe('AGGREGATE', () => { describe('with properties', () => { it('single', () => { assert.deepEqual( - AGGREGATE.transformArguments('index', '*', { + parseArgs(AGGREGATE, 'index', '*', { STEPS: [{ type: 'GROUPBY', properties: '@property', @@ -126,7 +127,7 @@ describe('AGGREGATE', () => { it('multiple', () => { assert.deepEqual( - AGGREGATE.transformArguments('index', '*', { + parseArgs(AGGREGATE, 'index', '*', { STEPS: [{ type: 'GROUPBY', properties: ['@1', '@2'], @@ -143,7 +144,7 @@ describe('AGGREGATE', () => { it('COUNT_DISTINCT', () => { assert.deepEqual( - AGGREGATE.transformArguments('index', '*', { + parseArgs(AGGREGATE, 'index', '*', { STEPS: [{ type: 'GROUPBY', REDUCE: { @@ -158,7 +159,7 @@ describe('AGGREGATE', () => { it('COUNT_DISTINCTISH', () => { assert.deepEqual( - AGGREGATE.transformArguments('index', '*', { + parseArgs(AGGREGATE, 'index', '*', { STEPS: [{ type: 'GROUPBY', REDUCE: { @@ -173,7 +174,7 @@ describe('AGGREGATE', () => { it('SUM', () => { assert.deepEqual( - AGGREGATE.transformArguments('index', '*', { + parseArgs(AGGREGATE, 'index', '*', { STEPS: [{ type: 'GROUPBY', REDUCE: { @@ -188,7 +189,7 @@ describe('AGGREGATE', () => { it('MIN', () => { assert.deepEqual( - AGGREGATE.transformArguments('index', '*', { + parseArgs(AGGREGATE, 'index', '*', { STEPS: [{ type: 'GROUPBY', REDUCE: { @@ -203,7 +204,7 @@ describe('AGGREGATE', () => { it('MAX', () => { assert.deepEqual( - AGGREGATE.transformArguments('index', '*', { + parseArgs(AGGREGATE, 'index', '*', { STEPS: [{ type: 'GROUPBY', REDUCE: { @@ -218,7 +219,7 @@ describe('AGGREGATE', () => { it('AVG', () => { assert.deepEqual( - AGGREGATE.transformArguments('index', '*', { + parseArgs(AGGREGATE, 'index', '*', { STEPS: [{ type: 'GROUPBY', REDUCE: { @@ -230,10 +231,9 @@ describe('AGGREGATE', () => { ['FT.AGGREGATE', 'index', '*', 'GROUPBY', '0', 'REDUCE', 'AVG', '1', '@property'] ); }); - it('STDDEV', () => { assert.deepEqual( - AGGREGATE.transformArguments('index', '*', { + parseArgs(AGGREGATE, 'index', '*', { STEPS: [{ type: 'GROUPBY', REDUCE: { @@ -248,7 +248,7 @@ describe('AGGREGATE', () => { it('QUANTILE', () => { assert.deepEqual( - AGGREGATE.transformArguments('index', '*', { + parseArgs(AGGREGATE, 'index', '*', { STEPS: [{ type: 'GROUPBY', REDUCE: { @@ -264,7 +264,7 @@ describe('AGGREGATE', () => { it('TOLIST', () => { assert.deepEqual( - AGGREGATE.transformArguments('index', '*', { + parseArgs(AGGREGATE, 'index', '*', { STEPS: [{ type: 'GROUPBY', REDUCE: { @@ -280,7 +280,7 @@ describe('AGGREGATE', () => { describe('FIRST_VALUE', () => { it('simple', () => { assert.deepEqual( - AGGREGATE.transformArguments('index', '*', { + parseArgs(AGGREGATE, 'index', '*', { STEPS: [{ type: 'GROUPBY', REDUCE: { @@ -297,7 +297,7 @@ describe('AGGREGATE', () => { describe('without direction', () => { it('string', () => { assert.deepEqual( - AGGREGATE.transformArguments('index', '*', { + parseArgs(AGGREGATE, 'index', '*', { STEPS: [{ type: 'GROUPBY', REDUCE: { @@ -314,7 +314,7 @@ describe('AGGREGATE', () => { it('{ property: string }', () => { assert.deepEqual( - AGGREGATE.transformArguments('index', '*', { + parseArgs(AGGREGATE, 'index', '*', { STEPS: [{ type: 'GROUPBY', REDUCE: { @@ -333,7 +333,7 @@ describe('AGGREGATE', () => { it('with direction', () => { assert.deepEqual( - AGGREGATE.transformArguments('index', '*', { + parseArgs(AGGREGATE, 'index', '*', { STEPS: [{ type: 'GROUPBY', REDUCE: { @@ -354,7 +354,7 @@ describe('AGGREGATE', () => { it('RANDOM_SAMPLE', () => { assert.deepEqual( - AGGREGATE.transformArguments('index', '*', { + parseArgs(AGGREGATE, 'index', '*', { STEPS: [{ type: 'GROUPBY', REDUCE: { @@ -372,7 +372,7 @@ describe('AGGREGATE', () => { describe('SORTBY', () => { it('string', () => { assert.deepEqual( - AGGREGATE.transformArguments('index', '*', { + parseArgs(AGGREGATE, 'index', '*', { STEPS: [{ type: 'SORTBY', BY: '@by' @@ -384,7 +384,7 @@ describe('AGGREGATE', () => { it('Array', () => { assert.deepEqual( - AGGREGATE.transformArguments('index', '*', { + parseArgs(AGGREGATE, 'index', '*', { STEPS: [{ type: 'SORTBY', BY: ['@1', '@2'] @@ -396,7 +396,7 @@ describe('AGGREGATE', () => { it('with MAX', () => { assert.deepEqual( - AGGREGATE.transformArguments('index', '*', { + parseArgs(AGGREGATE, 'index', '*', { STEPS: [{ type: 'SORTBY', BY: '@by', @@ -410,7 +410,7 @@ describe('AGGREGATE', () => { describe('APPLY', () => { assert.deepEqual( - AGGREGATE.transformArguments('index', '*', { + parseArgs(AGGREGATE, 'index', '*', { STEPS: [{ type: 'APPLY', expression: '@field + 1', @@ -423,7 +423,7 @@ describe('AGGREGATE', () => { describe('LIMIT', () => { assert.deepEqual( - AGGREGATE.transformArguments('index', '*', { + parseArgs(AGGREGATE, 'index', '*', { STEPS: [{ type: 'LIMIT', from: 0, @@ -436,7 +436,7 @@ describe('AGGREGATE', () => { describe('FILTER', () => { assert.deepEqual( - AGGREGATE.transformArguments('index', '*', { + parseArgs(AGGREGATE, 'index', '*', { STEPS: [{ type: 'FILTER', expression: '@field != ""' @@ -449,7 +449,7 @@ describe('AGGREGATE', () => { it('with PARAMS', () => { assert.deepEqual( - AGGREGATE.transformArguments('index', '*', { + parseArgs(AGGREGATE, 'index', '*', { PARAMS: { param: 'value' } @@ -460,7 +460,7 @@ describe('AGGREGATE', () => { it('with DIALECT', () => { assert.deepEqual( - AGGREGATE.transformArguments('index', '*', { + parseArgs(AGGREGATE, 'index', '*', { DIALECT: 1 }), ['FT.AGGREGATE', 'index', '*', 'DIALECT', '1'] @@ -469,7 +469,7 @@ describe('AGGREGATE', () => { it('with TIMEOUT', () => { assert.deepEqual( - AGGREGATE.transformArguments('index', '*', { TIMEOUT: 10 }), + parseArgs(AGGREGATE, 'index', '*', { TIMEOUT: 10 }), ['FT.AGGREGATE', 'index', '*', 'TIMEOUT', '10'] ); }); diff --git a/packages/search/lib/commands/AGGREGATE.ts b/packages/search/lib/commands/AGGREGATE.ts index cb9652622ad..0105b082682 100644 --- a/packages/search/lib/commands/AGGREGATE.ts +++ b/packages/search/lib/commands/AGGREGATE.ts @@ -1,7 +1,8 @@ -import { ArrayReply, BlobStringReply, Command, MapReply, NumberReply, RedisArgument, ReplyUnion, TypeMapping, UnwrapReply } from '@redis/client/dist/lib/RESP/types'; +import { CommandParser } from '@redis/client/lib/client/parser'; +import { ArrayReply, BlobStringReply, Command, MapReply, NumberReply, RedisArgument, ReplyUnion, TypeMapping, UnwrapReply } from '@redis/client/lib/RESP/types'; import { RediSearchProperty } from './CREATE'; -import { FtSearchParams, pushParamsArgument } from './SEARCH'; -import { pushVariadicArgument, transformTuplesReply } from '@redis/client/dist/lib/commands/generic-transformers'; +import { FtSearchParams, parseParamsArgument } from './SEARCH'; +import { transformTuplesReply } from '@redis/client/lib/commands/generic-transformers'; type LoadField = RediSearchProperty | { identifier: RediSearchProperty; @@ -137,12 +138,12 @@ export interface AggregateReply { }; export default { - FIRST_KEY_INDEX: undefined, + NOT_KEYED_COMMAND: true, IS_READ_ONLY: false, - transformArguments(index: RedisArgument, query: RedisArgument, options?: FtAggregateOptions) { - const args = ['FT.AGGREGATE', index, query]; + parseCommand(parser: CommandParser, index: RedisArgument, query: RedisArgument, options?: FtAggregateOptions) { + parser.push('FT.AGGREGATE', index, query); - return pushAggregateOptions(args, options); + return parseAggregateOptions(parser, options); }, transformReply: { 2: (rawReply: AggregateRawReply, preserve?: any, typeMapping?: TypeMapping): AggregateReply => { @@ -163,17 +164,17 @@ export default { unstableResp3: true } as const satisfies Command; -export function pushAggregateOptions(args: Array, options?: FtAggregateOptions) { +export function parseAggregateOptions(parser: CommandParser , options?: FtAggregateOptions) { if (options?.VERBATIM) { - args.push('VERBATIM'); + parser.push('VERBATIM'); } if (options?.ADDSCORES) { - args.push('ADDSCORES'); + parser.push('ADDSCORES'); } if (options?.LOAD) { - const length = args.push('LOAD', ''); + const args: Array = []; if (Array.isArray(options.LOAD)) { for (const load of options.LOAD) { @@ -183,36 +184,37 @@ export function pushAggregateOptions(args: Array, options?: FtAgg pushLoadField(args, options.LOAD); } - args[length - 1] = (args.length - length).toString(); + parser.push('LOAD'); + parser.pushVariadicWithLength(args); } if (options?.TIMEOUT !== undefined) { - args.push('TIMEOUT', options.TIMEOUT.toString()); + parser.push('TIMEOUT', options.TIMEOUT.toString()); } if (options?.STEPS) { for (const step of options.STEPS) { - args.push(step.type); + parser.push(step.type); switch (step.type) { case FT_AGGREGATE_STEPS.GROUPBY: if (!step.properties) { - args.push('0'); + parser.push('0'); } else { - pushVariadicArgument(args, step.properties); + parser.pushVariadicWithLength(step.properties); } if (Array.isArray(step.REDUCE)) { for (const reducer of step.REDUCE) { - pushGroupByReducer(args, reducer); + parseGroupByReducer(parser, reducer); } } else { - pushGroupByReducer(args, step.REDUCE); + parseGroupByReducer(parser, step.REDUCE); } break; case FT_AGGREGATE_STEPS.SORTBY: - const length = args.push(''); + const args: Array = []; if (Array.isArray(step.BY)) { for (const by of step.BY) { @@ -226,32 +228,30 @@ export function pushAggregateOptions(args: Array, options?: FtAgg args.push('MAX', step.MAX.toString()); } - args[length - 1] = (args.length - length).toString(); + parser.pushVariadicWithLength(args); break; case FT_AGGREGATE_STEPS.APPLY: - args.push(step.expression, 'AS', step.AS); + parser.push(step.expression, 'AS', step.AS); break; case FT_AGGREGATE_STEPS.LIMIT: - args.push(step.from.toString(), step.size.toString()); + parser.push(step.from.toString(), step.size.toString()); break; case FT_AGGREGATE_STEPS.FILTER: - args.push(step.expression); + parser.push(step.expression); break; } } } - pushParamsArgument(args, options?.PARAMS); + parseParamsArgument(parser, options?.PARAMS); if (options?.DIALECT !== undefined) { - args.push('DIALECT', options.DIALECT.toString()); + parser.push('DIALECT', options.DIALECT.toString()); } - - return args; } function pushLoadField(args: Array, toLoad: LoadField) { @@ -266,12 +266,12 @@ function pushLoadField(args: Array, toLoad: LoadField) { } } -function pushGroupByReducer(args: Array, reducer: GroupByReducers) { - args.push('REDUCE', reducer.type); +function parseGroupByReducer(parser: CommandParser, reducer: GroupByReducers) { + parser.push('REDUCE', reducer.type); switch (reducer.type) { case FT_AGGREGATE_GROUP_BY_REDUCERS.COUNT: - args.push('0'); + parser.push('0'); break; case FT_AGGREGATE_GROUP_BY_REDUCERS.COUNT_DISTINCT: @@ -282,15 +282,16 @@ function pushGroupByReducer(args: Array, reducer: GroupByReducers case FT_AGGREGATE_GROUP_BY_REDUCERS.AVG: case FT_AGGREGATE_GROUP_BY_REDUCERS.STDDEV: case FT_AGGREGATE_GROUP_BY_REDUCERS.TOLIST: - args.push('1', reducer.property); + parser.push('1', reducer.property); break; case FT_AGGREGATE_GROUP_BY_REDUCERS.QUANTILE: - args.push('2', reducer.property, reducer.quantile.toString()); + parser.push('2', reducer.property, reducer.quantile.toString()); break; case FT_AGGREGATE_GROUP_BY_REDUCERS.FIRST_VALUE: { - const length = args.push('', reducer.property) - 1; + const args: Array = [reducer.property]; + if (reducer.BY) { args.push('BY'); if (typeof reducer.BY === 'string' || reducer.BY instanceof Buffer) { @@ -303,17 +304,17 @@ function pushGroupByReducer(args: Array, reducer: GroupByReducers } } - args[length - 1] = (args.length - length).toString(); + parser.pushVariadicWithLength(args); break; } case FT_AGGREGATE_GROUP_BY_REDUCERS.RANDOM_SAMPLE: - args.push('2', reducer.property, reducer.sampleSize.toString()); + parser.push('2', reducer.property, reducer.sampleSize.toString()); break; } if (reducer.AS) { - args.push('AS', reducer.AS); + parser.push('AS', reducer.AS); } } diff --git a/packages/search/lib/commands/AGGREGATE_WITHCURSOR.spec.ts b/packages/search/lib/commands/AGGREGATE_WITHCURSOR.spec.ts index 9db3d945f97..57f46d1e32c 100644 --- a/packages/search/lib/commands/AGGREGATE_WITHCURSOR.spec.ts +++ b/packages/search/lib/commands/AGGREGATE_WITHCURSOR.spec.ts @@ -1,19 +1,20 @@ import { strict as assert } from 'node:assert'; import testUtils, { GLOBAL } from '../test-utils'; import AGGREGATE_WITHCURSOR from './AGGREGATE_WITHCURSOR'; +import { parseArgs } from '@redis/client/lib/commands/generic-transformers'; describe('AGGREGATE WITHCURSOR', () => { describe('transformArguments', () => { it('without options', () => { assert.deepEqual( - AGGREGATE_WITHCURSOR.transformArguments('index', '*'), + parseArgs(AGGREGATE_WITHCURSOR, 'index', '*'), ['FT.AGGREGATE', 'index', '*', 'WITHCURSOR'] ); }); it('with COUNT', () => { assert.deepEqual( - AGGREGATE_WITHCURSOR.transformArguments('index', '*', { + parseArgs(AGGREGATE_WITHCURSOR, 'index', '*', { COUNT: 1 }), ['FT.AGGREGATE', 'index', '*', 'WITHCURSOR', 'COUNT', '1'] @@ -22,7 +23,7 @@ describe('AGGREGATE WITHCURSOR', () => { it('with MAXIDLE', () => { assert.deepEqual( - AGGREGATE_WITHCURSOR.transformArguments('index', '*', { + parseArgs(AGGREGATE_WITHCURSOR, 'index', '*', { MAXIDLE: 1 }), ['FT.AGGREGATE', 'index', '*', 'WITHCURSOR', 'MAXIDLE', '1'] diff --git a/packages/search/lib/commands/AGGREGATE_WITHCURSOR.ts b/packages/search/lib/commands/AGGREGATE_WITHCURSOR.ts index cffb86b8b44..f9a7e75942f 100644 --- a/packages/search/lib/commands/AGGREGATE_WITHCURSOR.ts +++ b/packages/search/lib/commands/AGGREGATE_WITHCURSOR.ts @@ -1,4 +1,5 @@ -import { RedisArgument, Command, ReplyUnion, NumberReply } from '@redis/client/dist/lib/RESP/types'; +import { CommandParser } from '@redis/client/lib/client/parser'; +import { RedisArgument, Command, ReplyUnion, NumberReply } from '@redis/client/lib/RESP/types'; import AGGREGATE, { AggregateRawReply, AggregateReply, FtAggregateOptions } from './AGGREGATE'; export interface FtAggregateWithCursorOptions extends FtAggregateOptions { @@ -17,21 +18,18 @@ export interface AggregateWithCursorReply extends AggregateReply { } export default { - FIRST_KEY_INDEX: AGGREGATE.FIRST_KEY_INDEX, IS_READ_ONLY: AGGREGATE.IS_READ_ONLY, - transformArguments(index: RedisArgument, query: RedisArgument, options?: FtAggregateWithCursorOptions) { - const args = AGGREGATE.transformArguments(index, query, options); - args.push('WITHCURSOR'); + parseCommand(parser: CommandParser, index: RedisArgument, query: RedisArgument, options?: FtAggregateWithCursorOptions) { + AGGREGATE.parseCommand(parser, index, query, options); + parser.push('WITHCURSOR'); if (options?.COUNT !== undefined) { - args.push('COUNT', options.COUNT.toString()); + parser.push('COUNT', options.COUNT.toString()); } if(options?.MAXIDLE !== undefined) { - args.push('MAXIDLE', options.MAXIDLE.toString()); + parser.push('MAXIDLE', options.MAXIDLE.toString()); } - - return args; }, transformReply: { 2: (reply: AggregateWithCursorRawReply): AggregateWithCursorReply => { @@ -44,4 +42,3 @@ export default { }, unstableResp3: true } as const satisfies Command; - diff --git a/packages/search/lib/commands/ALIASADD.spec.ts b/packages/search/lib/commands/ALIASADD.spec.ts index 3a5d02175f9..b8332aed6a6 100644 --- a/packages/search/lib/commands/ALIASADD.spec.ts +++ b/packages/search/lib/commands/ALIASADD.spec.ts @@ -2,11 +2,12 @@ import { strict as assert } from 'node:assert'; import testUtils, { GLOBAL } from '../test-utils'; import ALIASADD from './ALIASADD'; import { SCHEMA_FIELD_TYPE } from './CREATE'; +import { parseArgs } from '@redis/client/lib/commands/generic-transformers'; describe('FT.ALIASADD', () => { it('transformArguments', () => { assert.deepEqual( - ALIASADD.transformArguments('alias', 'index'), + parseArgs(ALIASADD, 'alias', 'index'), ['FT.ALIASADD', 'alias', 'index'] ); }); diff --git a/packages/search/lib/commands/ALIASADD.ts b/packages/search/lib/commands/ALIASADD.ts index 648e1fef97e..db8eb54326e 100644 --- a/packages/search/lib/commands/ALIASADD.ts +++ b/packages/search/lib/commands/ALIASADD.ts @@ -1,10 +1,11 @@ -import { RedisArgument, SimpleStringReply, Command } from '@redis/client/dist/lib/RESP/types'; +import { CommandParser } from '@redis/client/lib/client/parser'; +import { RedisArgument, SimpleStringReply, Command } from '@redis/client/lib/RESP/types'; export default { - FIRST_KEY_INDEX: undefined, + NOT_KEYED_COMMAND: true, IS_READ_ONLY: true, - transformArguments(alias: RedisArgument, index: RedisArgument) { - return ['FT.ALIASADD', alias, index]; + parseCommand(parser: CommandParser, alias: RedisArgument, index: RedisArgument) { + parser.push('FT.ALIASADD', alias, index); }, transformReply: undefined as unknown as () => SimpleStringReply<'OK'> } as const satisfies Command; diff --git a/packages/search/lib/commands/ALIASDEL.spec.ts b/packages/search/lib/commands/ALIASDEL.spec.ts index 3842d01b145..19c2473f8cd 100644 --- a/packages/search/lib/commands/ALIASDEL.spec.ts +++ b/packages/search/lib/commands/ALIASDEL.spec.ts @@ -2,11 +2,12 @@ import { strict as assert } from 'node:assert'; import testUtils, { GLOBAL } from '../test-utils'; import ALIASDEL from './ALIASDEL'; import { SCHEMA_FIELD_TYPE } from './CREATE'; +import { parseArgs } from '@redis/client/lib/commands/generic-transformers'; describe('FT.ALIASDEL', () => { it('transformArguments', () => { assert.deepEqual( - ALIASDEL.transformArguments('alias'), + parseArgs(ALIASDEL, 'alias'), ['FT.ALIASDEL', 'alias'] ); }); diff --git a/packages/search/lib/commands/ALIASDEL.ts b/packages/search/lib/commands/ALIASDEL.ts index 40cc45a19de..3e4b70a1943 100644 --- a/packages/search/lib/commands/ALIASDEL.ts +++ b/packages/search/lib/commands/ALIASDEL.ts @@ -1,10 +1,11 @@ -import { RedisArgument, SimpleStringReply, Command } from '@redis/client/dist/lib/RESP/types'; +import { CommandParser } from '@redis/client/lib/client/parser'; +import { RedisArgument, SimpleStringReply, Command } from '@redis/client/lib/RESP/types'; export default { - FIRST_KEY_INDEX: undefined, + NOT_KEYED_COMMAND: true, IS_READ_ONLY: true, - transformArguments(alias: RedisArgument) { - return ['FT.ALIASDEL', alias]; + parseCommand(parser: CommandParser, alias: RedisArgument) { + parser.push('FT.ALIASDEL', alias); }, transformReply: undefined as unknown as () => SimpleStringReply<'OK'> } as const satisfies Command; diff --git a/packages/search/lib/commands/ALIASUPDATE.spec.ts b/packages/search/lib/commands/ALIASUPDATE.spec.ts index a0e7431af6b..f23af30229c 100644 --- a/packages/search/lib/commands/ALIASUPDATE.spec.ts +++ b/packages/search/lib/commands/ALIASUPDATE.spec.ts @@ -2,11 +2,12 @@ import { strict as assert } from 'node:assert'; import testUtils, { GLOBAL } from '../test-utils'; import ALIASUPDATE from './ALIASUPDATE'; import { SCHEMA_FIELD_TYPE } from './CREATE'; +import { parseArgs } from '@redis/client/lib/commands/generic-transformers'; describe('FT.ALIASUPDATE', () => { it('transformArguments', () => { assert.deepEqual( - ALIASUPDATE.transformArguments('alias', 'index'), + parseArgs(ALIASUPDATE, 'alias', 'index'), ['FT.ALIASUPDATE', 'alias', 'index'] ); }); diff --git a/packages/search/lib/commands/ALIASUPDATE.ts b/packages/search/lib/commands/ALIASUPDATE.ts index e2b72cfe649..46f0aa3e020 100644 --- a/packages/search/lib/commands/ALIASUPDATE.ts +++ b/packages/search/lib/commands/ALIASUPDATE.ts @@ -1,10 +1,11 @@ -import { SimpleStringReply, Command, RedisArgument } from '@redis/client/dist/lib/RESP/types'; +import { CommandParser } from '@redis/client/lib/client/parser'; +import { SimpleStringReply, Command, RedisArgument } from '@redis/client/lib/RESP/types'; export default { - FIRST_KEY_INDEX: undefined, + NOT_KEYED_COMMAND: true, IS_READ_ONLY: true, - transformArguments(alias: RedisArgument, index: RedisArgument) { - return ['FT.ALIASUPDATE', alias, index]; + parseCommand(parser: CommandParser, alias: RedisArgument, index: RedisArgument) { + parser.push('FT.ALIASUPDATE', alias, index); }, transformReply: undefined as unknown as () => SimpleStringReply<'OK'> } as const satisfies Command; diff --git a/packages/search/lib/commands/ALTER.spec.ts b/packages/search/lib/commands/ALTER.spec.ts index 6cac0be40c8..c34f7e045d5 100644 --- a/packages/search/lib/commands/ALTER.spec.ts +++ b/packages/search/lib/commands/ALTER.spec.ts @@ -2,12 +2,13 @@ import { strict as assert } from 'node:assert'; import testUtils, { GLOBAL } from '../test-utils'; import ALTER from './ALTER'; import { SCHEMA_FIELD_TYPE } from './CREATE'; +import { parseArgs } from '@redis/client/lib/commands/generic-transformers'; describe('FT.ALTER', () => { describe('transformArguments', () => { it('with NOINDEX', () => { assert.deepEqual( - ALTER.transformArguments('index', { + parseArgs(ALTER, 'index', { field: { type: SCHEMA_FIELD_TYPE.TEXT, NOINDEX: true, diff --git a/packages/search/lib/commands/ALTER.ts b/packages/search/lib/commands/ALTER.ts index d5587b2397c..4cde32e6ad4 100644 --- a/packages/search/lib/commands/ALTER.ts +++ b/packages/search/lib/commands/ALTER.ts @@ -1,13 +1,13 @@ -import { RedisArgument, SimpleStringReply, Command } from '@redis/client/dist/lib/RESP/types'; -import { RediSearchSchema, pushSchema } from './CREATE'; +import { CommandParser } from '@redis/client/lib/client/parser'; +import { RedisArgument, SimpleStringReply, Command } from '@redis/client/lib/RESP/types'; +import { RediSearchSchema, parseSchema } from './CREATE'; export default { - FIRST_KEY_INDEX: undefined, + NOT_KEYED_COMMAND: true, IS_READ_ONLY: true, - transformArguments(index: RedisArgument, schema: RediSearchSchema) { - const args = ['FT.ALTER', index, 'SCHEMA', 'ADD']; - pushSchema(args, schema); - return args; + parseCommand(parser: CommandParser, index: RedisArgument, schema: RediSearchSchema) { + parser.push('FT.ALTER', index, 'SCHEMA', 'ADD'); + parseSchema(parser, schema); }, transformReply: undefined as unknown as () => SimpleStringReply<'OK'> } as const satisfies Command; diff --git a/packages/search/lib/commands/CONFIG_GET.spec.ts b/packages/search/lib/commands/CONFIG_GET.spec.ts index 7ef2a3536b9..598a2a9ac41 100644 --- a/packages/search/lib/commands/CONFIG_GET.spec.ts +++ b/packages/search/lib/commands/CONFIG_GET.spec.ts @@ -1,11 +1,12 @@ import { strict as assert } from 'node:assert'; import testUtils, { GLOBAL } from '../test-utils'; import CONFIG_GET from './CONFIG_GET'; +import { parseArgs } from '@redis/client/lib/commands/generic-transformers'; describe('FT.CONFIG GET', () => { it('transformArguments', () => { assert.deepEqual( - CONFIG_GET.transformArguments('TIMEOUT'), + parseArgs(CONFIG_GET, 'TIMEOUT'), ['FT.CONFIG', 'GET', 'TIMEOUT'] ); }); diff --git a/packages/search/lib/commands/CONFIG_GET.ts b/packages/search/lib/commands/CONFIG_GET.ts index f96461e8694..9c0be73e2e3 100644 --- a/packages/search/lib/commands/CONFIG_GET.ts +++ b/packages/search/lib/commands/CONFIG_GET.ts @@ -1,10 +1,11 @@ -import { ArrayReply, TuplesReply, BlobStringReply, NullReply, UnwrapReply, Command } from '@redis/client/dist/lib/RESP/types'; +import { CommandParser } from '@redis/client/lib/client/parser'; +import { ArrayReply, TuplesReply, BlobStringReply, NullReply, UnwrapReply, Command } from '@redis/client/lib/RESP/types'; export default { - FIRST_KEY_INDEX: undefined, + NOT_KEYED_COMMAND: true, IS_READ_ONLY: true, - transformArguments(option: string) { - return ['FT.CONFIG', 'GET', option]; + parseCommand(parser: CommandParser, option: string) { + parser.push('FT.CONFIG', 'GET', option); }, transformReply(reply: UnwrapReply>>) { const transformedReply: Record = Object.create(null); diff --git a/packages/search/lib/commands/CONFIG_SET.spec.ts b/packages/search/lib/commands/CONFIG_SET.spec.ts index 3b20f2eac5d..71a4e69f26f 100644 --- a/packages/search/lib/commands/CONFIG_SET.spec.ts +++ b/packages/search/lib/commands/CONFIG_SET.spec.ts @@ -1,11 +1,12 @@ import { strict as assert } from 'node:assert'; import testUtils, { GLOBAL } from '../test-utils'; import CONFIG_SET from './CONFIG_SET'; +import { parseArgs } from '@redis/client/lib/commands/generic-transformers'; describe('FT.CONFIG SET', () => { it('transformArguments', () => { assert.deepEqual( - CONFIG_SET.transformArguments('TIMEOUT', '500'), + parseArgs(CONFIG_SET, 'TIMEOUT', '500'), ['FT.CONFIG', 'SET', 'TIMEOUT', '500'] ); }); diff --git a/packages/search/lib/commands/CONFIG_SET.ts b/packages/search/lib/commands/CONFIG_SET.ts index ac001bf68a6..ae57dd7d8f6 100644 --- a/packages/search/lib/commands/CONFIG_SET.ts +++ b/packages/search/lib/commands/CONFIG_SET.ts @@ -1,14 +1,15 @@ -import { RedisArgument, SimpleStringReply, Command } from '@redis/client/dist/lib/RESP/types'; +import { CommandParser } from '@redis/client/lib/client/parser'; +import { RedisArgument, SimpleStringReply, Command } from '@redis/client/lib/RESP/types'; // using `string & {}` to avoid TS widening the type to `string` // TODO type FtConfigProperties = 'a' | 'b' | (string & {}) | Buffer; export default { - FIRST_KEY_INDEX: undefined, + NOT_KEYED_COMMAND: true, IS_READ_ONLY: true, - transformArguments(property: FtConfigProperties, value: RedisArgument) { - return ['FT.CONFIG', 'SET', property, value]; + parseCommand(parser: CommandParser, property: FtConfigProperties, value: RedisArgument) { + parser.push('FT.CONFIG', 'SET', property, value); }, transformReply: undefined as unknown as () => SimpleStringReply<'OK'> } as const satisfies Command; diff --git a/packages/search/lib/commands/CREATE.spec.ts b/packages/search/lib/commands/CREATE.spec.ts index bc48691bd58..58888fb7ce4 100644 --- a/packages/search/lib/commands/CREATE.spec.ts +++ b/packages/search/lib/commands/CREATE.spec.ts @@ -1,12 +1,13 @@ import { strict as assert } from 'node:assert'; import testUtils, { GLOBAL } from '../test-utils'; import CREATE, { SCHEMA_FIELD_TYPE, SCHEMA_TEXT_FIELD_PHONETIC, SCHEMA_VECTOR_FIELD_ALGORITHM, REDISEARCH_LANGUAGE } from './CREATE'; +import { parseArgs } from '@redis/client/lib/commands/generic-transformers'; describe('FT.CREATE', () => { describe('transformArguments', () => { it('simple', () => { assert.deepEqual( - CREATE.transformArguments('index', {}), + parseArgs(CREATE, 'index', {}), ['FT.CREATE', 'index', 'SCHEMA'] ); }); @@ -15,7 +16,7 @@ describe('FT.CREATE', () => { describe('TEXT', () => { it('without options', () => { assert.deepEqual( - CREATE.transformArguments('index', { + parseArgs(CREATE, 'index', { field: SCHEMA_FIELD_TYPE.TEXT }), ['FT.CREATE', 'index', 'SCHEMA', 'field', 'TEXT'] @@ -24,7 +25,7 @@ describe('FT.CREATE', () => { it('with NOSTEM', () => { assert.deepEqual( - CREATE.transformArguments('index', { + parseArgs(CREATE, 'index', { field: { type: SCHEMA_FIELD_TYPE.TEXT, NOSTEM: true @@ -36,7 +37,7 @@ describe('FT.CREATE', () => { it('with WEIGHT', () => { assert.deepEqual( - CREATE.transformArguments('index', { + parseArgs(CREATE, 'index', { field: { type: SCHEMA_FIELD_TYPE.TEXT, WEIGHT: 1 @@ -48,7 +49,7 @@ describe('FT.CREATE', () => { it('with PHONETIC', () => { assert.deepEqual( - CREATE.transformArguments('index', { + parseArgs(CREATE, 'index', { field: { type: SCHEMA_FIELD_TYPE.TEXT, PHONETIC: SCHEMA_TEXT_FIELD_PHONETIC.DM_EN @@ -60,7 +61,7 @@ describe('FT.CREATE', () => { it('with WITHSUFFIXTRIE', () => { assert.deepEqual( - CREATE.transformArguments('index', { + parseArgs(CREATE, 'index', { field: { type: SCHEMA_FIELD_TYPE.TEXT, WITHSUFFIXTRIE: true @@ -73,7 +74,7 @@ describe('FT.CREATE', () => { it('NUMERIC', () => { assert.deepEqual( - CREATE.transformArguments('index', { + parseArgs(CREATE, 'index', { field: SCHEMA_FIELD_TYPE.NUMERIC }), ['FT.CREATE', 'index', 'SCHEMA', 'field', 'NUMERIC'] @@ -82,7 +83,7 @@ describe('FT.CREATE', () => { it('GEO', () => { assert.deepEqual( - CREATE.transformArguments('index', { + parseArgs(CREATE, 'index', { field: SCHEMA_FIELD_TYPE.GEO }), ['FT.CREATE', 'index', 'SCHEMA', 'field', 'GEO'] @@ -93,7 +94,7 @@ describe('FT.CREATE', () => { describe('without options', () => { it('SCHEMA_FIELD_TYPE.TAG', () => { assert.deepEqual( - CREATE.transformArguments('index', { + parseArgs(CREATE, 'index', { field: SCHEMA_FIELD_TYPE.TAG }), ['FT.CREATE', 'index', 'SCHEMA', 'field', 'TAG'] @@ -102,7 +103,7 @@ describe('FT.CREATE', () => { it('{ type: SCHEMA_FIELD_TYPE.TAG }', () => { assert.deepEqual( - CREATE.transformArguments('index', { + parseArgs(CREATE, 'index', { field: { type: SCHEMA_FIELD_TYPE.TAG } @@ -114,7 +115,7 @@ describe('FT.CREATE', () => { it('with SEPARATOR', () => { assert.deepEqual( - CREATE.transformArguments('index', { + parseArgs(CREATE, 'index', { field: { type: SCHEMA_FIELD_TYPE.TAG, SEPARATOR: 'separator' @@ -126,7 +127,7 @@ describe('FT.CREATE', () => { it('with CASESENSITIVE', () => { assert.deepEqual( - CREATE.transformArguments('index', { + parseArgs(CREATE, 'index', { field: { type: SCHEMA_FIELD_TYPE.TAG, CASESENSITIVE: true @@ -138,7 +139,7 @@ describe('FT.CREATE', () => { it('with WITHSUFFIXTRIE', () => { assert.deepEqual( - CREATE.transformArguments('index', { + parseArgs(CREATE, 'index', { field: { type: SCHEMA_FIELD_TYPE.TAG, WITHSUFFIXTRIE: true @@ -150,7 +151,7 @@ describe('FT.CREATE', () => { it('with INDEXEMPTY', () => { assert.deepEqual( - CREATE.transformArguments('index', { + parseArgs(CREATE, 'index', { field: { type: SCHEMA_FIELD_TYPE.TAG, INDEXEMPTY: true @@ -164,7 +165,7 @@ describe('FT.CREATE', () => { describe('VECTOR', () => { it('Flat algorithm', () => { assert.deepEqual( - CREATE.transformArguments('index', { + parseArgs(CREATE, 'index', { field: { type: SCHEMA_FIELD_TYPE.VECTOR, ALGORITHM: SCHEMA_VECTOR_FIELD_ALGORITHM.FLAT, @@ -185,7 +186,7 @@ describe('FT.CREATE', () => { it('HNSW algorithm', () => { assert.deepEqual( - CREATE.transformArguments('index', { + parseArgs(CREATE, 'index', { field: { type: SCHEMA_FIELD_TYPE.VECTOR, ALGORITHM: SCHEMA_VECTOR_FIELD_ALGORITHM.HNSW, @@ -211,7 +212,7 @@ describe('FT.CREATE', () => { describe('without options', () => { it('SCHEMA_FIELD_TYPE.GEOSHAPE', () => { assert.deepEqual( - CREATE.transformArguments('index', { + parseArgs(CREATE, 'index', { field: SCHEMA_FIELD_TYPE.GEOSHAPE }), ['FT.CREATE', 'index', 'SCHEMA', 'field', 'GEOSHAPE'] @@ -220,7 +221,7 @@ describe('FT.CREATE', () => { it('{ type: SCHEMA_FIELD_TYPE.GEOSHAPE }', () => { assert.deepEqual( - CREATE.transformArguments('index', { + parseArgs(CREATE, 'index', { field: { type: SCHEMA_FIELD_TYPE.GEOSHAPE } @@ -232,7 +233,7 @@ describe('FT.CREATE', () => { it('with COORD_SYSTEM', () => { assert.deepEqual( - CREATE.transformArguments('index', { + parseArgs(CREATE, 'index', { field: { type: SCHEMA_FIELD_TYPE.GEOSHAPE, COORD_SYSTEM: 'SPHERICAL' @@ -245,7 +246,7 @@ describe('FT.CREATE', () => { it('with AS', () => { assert.deepEqual( - CREATE.transformArguments('index', { + parseArgs(CREATE, 'index', { field: { type: SCHEMA_FIELD_TYPE.TEXT, AS: 'as' @@ -258,7 +259,7 @@ describe('FT.CREATE', () => { describe('with SORTABLE', () => { it('true', () => { assert.deepEqual( - CREATE.transformArguments('index', { + parseArgs(CREATE, 'index', { field: { type: SCHEMA_FIELD_TYPE.TEXT, SORTABLE: true @@ -270,7 +271,7 @@ describe('FT.CREATE', () => { it('UNF', () => { assert.deepEqual( - CREATE.transformArguments('index', { + parseArgs(CREATE, 'index', { field: { type: SCHEMA_FIELD_TYPE.TEXT, SORTABLE: 'UNF' @@ -283,7 +284,7 @@ describe('FT.CREATE', () => { it('with NOINDEX', () => { assert.deepEqual( - CREATE.transformArguments('index', { + parseArgs(CREATE, 'index', { field: { type: SCHEMA_FIELD_TYPE.TEXT, NOINDEX: true @@ -295,7 +296,7 @@ describe('FT.CREATE', () => { it('with INDEXMISSING', () => { assert.deepEqual( - CREATE.transformArguments('index', { + parseArgs(CREATE, 'index', { field: { type: SCHEMA_FIELD_TYPE.TEXT, INDEXMISSING: true @@ -308,7 +309,7 @@ describe('FT.CREATE', () => { it('with ON', () => { assert.deepEqual( - CREATE.transformArguments('index', {}, { + parseArgs(CREATE, 'index', {}, { ON: 'HASH' }), ['FT.CREATE', 'index', 'ON', 'HASH', 'SCHEMA'] @@ -318,7 +319,7 @@ describe('FT.CREATE', () => { describe('with PREFIX', () => { it('string', () => { assert.deepEqual( - CREATE.transformArguments('index', {}, { + parseArgs(CREATE, 'index', {}, { PREFIX: 'prefix' }), ['FT.CREATE', 'index', 'PREFIX', '1', 'prefix', 'SCHEMA'] @@ -327,7 +328,7 @@ describe('FT.CREATE', () => { it('Array', () => { assert.deepEqual( - CREATE.transformArguments('index', {}, { + parseArgs(CREATE, 'index', {}, { PREFIX: ['1', '2'] }), ['FT.CREATE', 'index', 'PREFIX', '2', '1', '2', 'SCHEMA'] @@ -337,7 +338,7 @@ describe('FT.CREATE', () => { it('with FILTER', () => { assert.deepEqual( - CREATE.transformArguments('index', {}, { + parseArgs(CREATE, 'index', {}, { FILTER: '@field != ""' }), ['FT.CREATE', 'index', 'FILTER', '@field != ""', 'SCHEMA'] @@ -346,7 +347,7 @@ describe('FT.CREATE', () => { it('with LANGUAGE', () => { assert.deepEqual( - CREATE.transformArguments('index', {}, { + parseArgs(CREATE, 'index', {}, { LANGUAGE: REDISEARCH_LANGUAGE.ARABIC }), ['FT.CREATE', 'index', 'LANGUAGE', REDISEARCH_LANGUAGE.ARABIC, 'SCHEMA'] @@ -355,7 +356,7 @@ describe('FT.CREATE', () => { it('with LANGUAGE_FIELD', () => { assert.deepEqual( - CREATE.transformArguments('index', {}, { + parseArgs(CREATE, 'index', {}, { LANGUAGE_FIELD: '@field' }), ['FT.CREATE', 'index', 'LANGUAGE_FIELD', '@field', 'SCHEMA'] @@ -364,7 +365,7 @@ describe('FT.CREATE', () => { it('with SCORE', () => { assert.deepEqual( - CREATE.transformArguments('index', {}, { + parseArgs(CREATE, 'index', {}, { SCORE: 1 }), ['FT.CREATE', 'index', 'SCORE', '1', 'SCHEMA'] @@ -373,7 +374,7 @@ describe('FT.CREATE', () => { it('with SCORE_FIELD', () => { assert.deepEqual( - CREATE.transformArguments('index', {}, { + parseArgs(CREATE, 'index', {}, { SCORE_FIELD: '@field' }), ['FT.CREATE', 'index', 'SCORE_FIELD', '@field', 'SCHEMA'] @@ -382,7 +383,7 @@ describe('FT.CREATE', () => { it('with MAXTEXTFIELDS', () => { assert.deepEqual( - CREATE.transformArguments('index', {}, { + parseArgs(CREATE, 'index', {}, { MAXTEXTFIELDS: true }), ['FT.CREATE', 'index', 'MAXTEXTFIELDS', 'SCHEMA'] @@ -391,7 +392,7 @@ describe('FT.CREATE', () => { it('with TEMPORARY', () => { assert.deepEqual( - CREATE.transformArguments('index', {}, { + parseArgs(CREATE, 'index', {}, { TEMPORARY: 1 }), ['FT.CREATE', 'index', 'TEMPORARY', '1', 'SCHEMA'] @@ -400,7 +401,7 @@ describe('FT.CREATE', () => { it('with NOOFFSETS', () => { assert.deepEqual( - CREATE.transformArguments('index', {}, { + parseArgs(CREATE, 'index', {}, { NOOFFSETS: true }), ['FT.CREATE', 'index', 'NOOFFSETS', 'SCHEMA'] @@ -409,7 +410,7 @@ describe('FT.CREATE', () => { it('with NOHL', () => { assert.deepEqual( - CREATE.transformArguments('index', {}, { + parseArgs(CREATE, 'index', {}, { NOHL: true }), ['FT.CREATE', 'index', 'NOHL', 'SCHEMA'] @@ -418,7 +419,7 @@ describe('FT.CREATE', () => { it('with NOFIELDS', () => { assert.deepEqual( - CREATE.transformArguments('index', {}, { + parseArgs(CREATE, 'index', {}, { NOFIELDS: true }), ['FT.CREATE', 'index', 'NOFIELDS', 'SCHEMA'] @@ -427,7 +428,7 @@ describe('FT.CREATE', () => { it('with NOFREQS', () => { assert.deepEqual( - CREATE.transformArguments('index', {}, { + parseArgs(CREATE, 'index', {}, { NOFREQS: true }), ['FT.CREATE', 'index', 'NOFREQS', 'SCHEMA'] @@ -436,7 +437,7 @@ describe('FT.CREATE', () => { it('with SKIPINITIALSCAN', () => { assert.deepEqual( - CREATE.transformArguments('index', {}, { + parseArgs(CREATE, 'index', {}, { SKIPINITIALSCAN: true }), ['FT.CREATE', 'index', 'SKIPINITIALSCAN', 'SCHEMA'] @@ -446,7 +447,7 @@ describe('FT.CREATE', () => { describe('with STOPWORDS', () => { it('string', () => { assert.deepEqual( - CREATE.transformArguments('index', {}, { + parseArgs(CREATE, 'index', {}, { STOPWORDS: 'stopword' }), ['FT.CREATE', 'index', 'STOPWORDS', '1', 'stopword', 'SCHEMA'] @@ -455,7 +456,7 @@ describe('FT.CREATE', () => { it('Array', () => { assert.deepEqual( - CREATE.transformArguments('index', {}, { + parseArgs(CREATE, 'index', {}, { STOPWORDS: ['1', '2'] }), ['FT.CREATE', 'index', 'STOPWORDS', '2', '1', '2', 'SCHEMA'] diff --git a/packages/search/lib/commands/CREATE.ts b/packages/search/lib/commands/CREATE.ts index 2951e56f090..d0096282f37 100644 --- a/packages/search/lib/commands/CREATE.ts +++ b/packages/search/lib/commands/CREATE.ts @@ -1,5 +1,6 @@ -import { RedisArgument, SimpleStringReply, Command, CommandArguments } from '@redis/client/lib/RESP/types'; -import { RedisVariadicArgument, pushOptionalVariadicArgument } from '@redis/client/lib/commands/generic-transformers'; +import { CommandParser } from '@redis/client/lib/client/parser'; +import { RedisArgument, SimpleStringReply, Command } from '@redis/client/lib/RESP/types'; +import { RedisVariadicArgument, parseOptionalVariadicArgument } from '@redis/client/lib/commands/generic-transformers'; export const SCHEMA_FIELD_TYPE = { TEXT: 'TEXT', @@ -102,93 +103,93 @@ export interface RediSearchSchema { ); } -function pushCommonSchemaFieldOptions(args: CommandArguments, fieldOptions: SchemaCommonField) { +function parseCommonSchemaFieldOptions(parser: CommandParser, fieldOptions: SchemaCommonField) { if (fieldOptions.SORTABLE) { - args.push('SORTABLE'); + parser.push('SORTABLE'); if (fieldOptions.SORTABLE === 'UNF') { - args.push('UNF'); + parser.push('UNF'); } } if (fieldOptions.NOINDEX) { - args.push('NOINDEX'); + parser.push('NOINDEX'); } } -export function pushSchema(args: CommandArguments, schema: RediSearchSchema) { +export function parseSchema(parser: CommandParser, schema: RediSearchSchema) { for (const [field, fieldOptions] of Object.entries(schema)) { - args.push(field); + parser.push(field); if (typeof fieldOptions === 'string') { - args.push(fieldOptions); + parser.push(fieldOptions); continue; } if (fieldOptions.AS) { - args.push('AS', fieldOptions.AS); + parser.push('AS', fieldOptions.AS); } - args.push(fieldOptions.type); + parser.push(fieldOptions.type); if (fieldOptions.INDEXMISSING) { - args.push('INDEXMISSING'); + parser.push('INDEXMISSING'); } switch (fieldOptions.type) { case SCHEMA_FIELD_TYPE.TEXT: if (fieldOptions.NOSTEM) { - args.push('NOSTEM'); + parser.push('NOSTEM'); } if (fieldOptions.WEIGHT) { - args.push('WEIGHT', fieldOptions.WEIGHT.toString()); + parser.push('WEIGHT', fieldOptions.WEIGHT.toString()); } if (fieldOptions.PHONETIC) { - args.push('PHONETIC', fieldOptions.PHONETIC); + parser.push('PHONETIC', fieldOptions.PHONETIC); } if (fieldOptions.WITHSUFFIXTRIE) { - args.push('WITHSUFFIXTRIE'); + parser.push('WITHSUFFIXTRIE'); } if (fieldOptions.INDEXEMPTY) { - args.push('INDEXEMPTY'); + parser.push('INDEXEMPTY'); } - pushCommonSchemaFieldOptions(args, fieldOptions) + parseCommonSchemaFieldOptions(parser, fieldOptions) break; case SCHEMA_FIELD_TYPE.NUMERIC: case SCHEMA_FIELD_TYPE.GEO: - pushCommonSchemaFieldOptions(args, fieldOptions) + parseCommonSchemaFieldOptions(parser, fieldOptions) break; case SCHEMA_FIELD_TYPE.TAG: if (fieldOptions.SEPARATOR) { - args.push('SEPARATOR', fieldOptions.SEPARATOR); + parser.push('SEPARATOR', fieldOptions.SEPARATOR); } if (fieldOptions.CASESENSITIVE) { - args.push('CASESENSITIVE'); + parser.push('CASESENSITIVE'); } if (fieldOptions.WITHSUFFIXTRIE) { - args.push('WITHSUFFIXTRIE'); + parser.push('WITHSUFFIXTRIE'); } if (fieldOptions.INDEXEMPTY) { - args.push('INDEXEMPTY'); + parser.push('INDEXEMPTY'); } - pushCommonSchemaFieldOptions(args, fieldOptions) + parseCommonSchemaFieldOptions(parser, fieldOptions) break; case SCHEMA_FIELD_TYPE.VECTOR: - args.push(fieldOptions.ALGORITHM); + parser.push(fieldOptions.ALGORITHM); - const lengthIndex = args.push('') - 1; + const args: Array = []; args.push( 'TYPE', fieldOptions.TYPE, @@ -200,7 +201,7 @@ export function pushSchema(args: CommandArguments, schema: RediSearchSchema) { args.push('INITIAL_CAP', fieldOptions.INITIAL_CAP.toString()); } - switch (fieldOptions.ALGORITHM) { + switch (fieldOptions.ALGORITHM) { case SCHEMA_VECTOR_FIELD_ALGORITHM.FLAT: if (fieldOptions.BLOCK_SIZE) { args.push('BLOCK_SIZE', fieldOptions.BLOCK_SIZE.toString()); @@ -223,13 +224,13 @@ export function pushSchema(args: CommandArguments, schema: RediSearchSchema) { break; } - args[lengthIndex] = (args.length - lengthIndex - 1).toString(); + parser.pushVariadicWithLength(args); break; case SCHEMA_FIELD_TYPE.GEOSHAPE: if (fieldOptions.COORD_SYSTEM !== undefined) { - args.push('COORD_SYSTEM', fieldOptions.COORD_SYSTEM); + parser.push('COORD_SYSTEM', fieldOptions.COORD_SYSTEM); } break; @@ -289,74 +290,72 @@ export interface CreateOptions { } export default { - FIRST_KEY_INDEX: undefined, + NOT_KEYED_COMMAND: true, IS_READ_ONLY: true, - transformArguments(index: RedisArgument, schema: RediSearchSchema, options?: CreateOptions) { - const args = ['FT.CREATE', index]; + parseCommand(parser: CommandParser, index: RedisArgument, schema: RediSearchSchema, options?: CreateOptions) { + parser.push('FT.CREATE', index); if (options?.ON) { - args.push('ON', options.ON); + parser.push('ON', options.ON); } - pushOptionalVariadicArgument(args, 'PREFIX', options?.PREFIX); + parseOptionalVariadicArgument(parser, 'PREFIX', options?.PREFIX); if (options?.FILTER) { - args.push('FILTER', options.FILTER); + parser.push('FILTER', options.FILTER); } if (options?.LANGUAGE) { - args.push('LANGUAGE', options.LANGUAGE); + parser.push('LANGUAGE', options.LANGUAGE); } if (options?.LANGUAGE_FIELD) { - args.push('LANGUAGE_FIELD', options.LANGUAGE_FIELD); + parser.push('LANGUAGE_FIELD', options.LANGUAGE_FIELD); } if (options?.SCORE) { - args.push('SCORE', options.SCORE.toString()); + parser.push('SCORE', options.SCORE.toString()); } if (options?.SCORE_FIELD) { - args.push('SCORE_FIELD', options.SCORE_FIELD); + parser.push('SCORE_FIELD', options.SCORE_FIELD); } // if (options?.PAYLOAD_FIELD) { - // args.push('PAYLOAD_FIELD', options.PAYLOAD_FIELD); + // parser.push('PAYLOAD_FIELD', options.PAYLOAD_FIELD); // } if (options?.MAXTEXTFIELDS) { - args.push('MAXTEXTFIELDS'); + parser.push('MAXTEXTFIELDS'); } if (options?.TEMPORARY) { - args.push('TEMPORARY', options.TEMPORARY.toString()); + parser.push('TEMPORARY', options.TEMPORARY.toString()); } if (options?.NOOFFSETS) { - args.push('NOOFFSETS'); + parser.push('NOOFFSETS'); } if (options?.NOHL) { - args.push('NOHL'); + parser.push('NOHL'); } if (options?.NOFIELDS) { - args.push('NOFIELDS'); + parser.push('NOFIELDS'); } if (options?.NOFREQS) { - args.push('NOFREQS'); + parser.push('NOFREQS'); } if (options?.SKIPINITIALSCAN) { - args.push('SKIPINITIALSCAN'); + parser.push('SKIPINITIALSCAN'); } - pushOptionalVariadicArgument(args, 'STOPWORDS', options?.STOPWORDS); - args.push('SCHEMA'); - pushSchema(args, schema); - - return args; + parseOptionalVariadicArgument(parser, 'STOPWORDS', options?.STOPWORDS); + parser.push('SCHEMA'); + parseSchema(parser, schema); }, transformReply: undefined as unknown as () => SimpleStringReply<'OK'> } as const satisfies Command; diff --git a/packages/search/lib/commands/CURSOR_DEL.spec.ts b/packages/search/lib/commands/CURSOR_DEL.spec.ts index 8e9a7cf9aec..230a5fd0feb 100644 --- a/packages/search/lib/commands/CURSOR_DEL.spec.ts +++ b/packages/search/lib/commands/CURSOR_DEL.spec.ts @@ -2,11 +2,12 @@ import { strict as assert } from 'node:assert'; import testUtils, { GLOBAL } from '../test-utils'; import CURSOR_DEL from './CURSOR_DEL'; import { SCHEMA_FIELD_TYPE } from './CREATE'; +import { parseArgs } from '@redis/client/lib/commands/generic-transformers'; describe('FT.CURSOR DEL', () => { it('transformArguments', () => { assert.deepEqual( - CURSOR_DEL.transformArguments('index', 0), + parseArgs(CURSOR_DEL, 'index', 0), ['FT.CURSOR', 'DEL', 'index', '0'] ); }); diff --git a/packages/search/lib/commands/CURSOR_DEL.ts b/packages/search/lib/commands/CURSOR_DEL.ts index afccd695ff3..1ea4b46c8bd 100644 --- a/packages/search/lib/commands/CURSOR_DEL.ts +++ b/packages/search/lib/commands/CURSOR_DEL.ts @@ -1,10 +1,11 @@ -import { SimpleStringReply, Command, RedisArgument, NumberReply, UnwrapReply } from '@redis/client/dist/lib/RESP/types'; +import { CommandParser } from '@redis/client/lib/client/parser'; +import { SimpleStringReply, Command, RedisArgument, NumberReply, UnwrapReply } from '@redis/client/lib/RESP/types'; export default { - FIRST_KEY_INDEX: undefined, + NOT_KEYED_COMMAND: true, IS_READ_ONLY: true, - transformArguments(index: RedisArgument, cursorId: UnwrapReply) { - return ['FT.CURSOR', 'DEL', index, cursorId.toString()]; + parseCommand(parser: CommandParser, index: RedisArgument, cursorId: UnwrapReply) { + parser.push('FT.CURSOR', 'DEL', index, cursorId.toString()); }, transformReply: undefined as unknown as () => SimpleStringReply<'OK'> } as const satisfies Command; diff --git a/packages/search/lib/commands/CURSOR_READ.spec.ts b/packages/search/lib/commands/CURSOR_READ.spec.ts index 5999d4a7c18..42dca0c5756 100644 --- a/packages/search/lib/commands/CURSOR_READ.spec.ts +++ b/packages/search/lib/commands/CURSOR_READ.spec.ts @@ -1,19 +1,20 @@ import { strict as assert } from 'node:assert'; import testUtils, { GLOBAL } from '../test-utils'; import CURSOR_READ from './CURSOR_READ'; +import { parseArgs } from '@redis/client/lib/commands/generic-transformers'; describe('FT.CURSOR READ', () => { describe('transformArguments', () => { it('without options', () => { assert.deepEqual( - CURSOR_READ.transformArguments('index', 0), + parseArgs(CURSOR_READ, 'index', '0'), ['FT.CURSOR', 'READ', 'index', '0'] ); }); it('with COUNT', () => { assert.deepEqual( - CURSOR_READ.transformArguments('index', 0, { + parseArgs(CURSOR_READ, 'index', '0', { COUNT: 1 }), ['FT.CURSOR', 'READ', 'index', '0', 'COUNT', '1'] diff --git a/packages/search/lib/commands/CURSOR_READ.ts b/packages/search/lib/commands/CURSOR_READ.ts index d08b22ba90d..d23a0f0bd15 100644 --- a/packages/search/lib/commands/CURSOR_READ.ts +++ b/packages/search/lib/commands/CURSOR_READ.ts @@ -1,4 +1,5 @@ -import { RedisArgument, Command, UnwrapReply, NumberReply } from '@redis/client/dist/lib/RESP/types'; +import { CommandParser } from '@redis/client/lib/client/parser'; +import { RedisArgument, Command, NumberReply, UnwrapReply } from '@redis/client/lib/RESP/types'; import AGGREGATE_WITHCURSOR from './AGGREGATE_WITHCURSOR'; export interface FtCursorReadOptions { @@ -6,16 +7,14 @@ export interface FtCursorReadOptions { } export default { - FIRST_KEY_INDEX: undefined, + NOT_KEYED_COMMAND: true, IS_READ_ONLY: true, - transformArguments(index: RedisArgument, cursor: UnwrapReply, options?: FtCursorReadOptions) { - const args = ['FT.CURSOR', 'READ', index, cursor.toString()]; + parseCommand(parser: CommandParser, index: RedisArgument, cursor: UnwrapReply, options?: FtCursorReadOptions) { + parser.push('FT.CURSOR', 'READ', index, cursor.toString()); if (options?.COUNT !== undefined) { - args.push('COUNT', options.COUNT.toString()); + parser.push('COUNT', options.COUNT.toString()); } - - return args; }, transformReply: AGGREGATE_WITHCURSOR.transformReply, unstableResp3: true diff --git a/packages/search/lib/commands/DICTADD.spec.ts b/packages/search/lib/commands/DICTADD.spec.ts index c18502ea4d0..4707db02dcf 100644 --- a/packages/search/lib/commands/DICTADD.spec.ts +++ b/packages/search/lib/commands/DICTADD.spec.ts @@ -1,19 +1,20 @@ import { strict as assert } from 'node:assert'; import testUtils, { GLOBAL } from '../test-utils'; import DICTADD from './DICTADD'; +import { parseArgs } from '@redis/client/lib/commands/generic-transformers'; describe('FT.DICTADD', () => { describe('transformArguments', () => { it('string', () => { assert.deepEqual( - DICTADD.transformArguments('dictionary', 'term'), + parseArgs(DICTADD, 'dictionary', 'term'), ['FT.DICTADD', 'dictionary', 'term'] ); }); it('Array', () => { assert.deepEqual( - DICTADD.transformArguments('dictionary', ['1', '2']), + parseArgs(DICTADD, 'dictionary', ['1', '2']), ['FT.DICTADD', 'dictionary', '1', '2'] ); }); diff --git a/packages/search/lib/commands/DICTADD.ts b/packages/search/lib/commands/DICTADD.ts index f633d58b1f3..67f94a82c13 100644 --- a/packages/search/lib/commands/DICTADD.ts +++ b/packages/search/lib/commands/DICTADD.ts @@ -1,11 +1,13 @@ -import { RedisArgument, NumberReply, Command } from '@redis/client/dist/lib/RESP/types'; -import { pushVariadicArguments, RedisVariadicArgument } from '@redis/client/dist/lib/commands/generic-transformers'; +import { CommandParser } from '@redis/client/lib/client/parser'; +import { RedisArgument, NumberReply, Command } from '@redis/client/lib/RESP/types'; +import { RedisVariadicArgument } from '@redis/client/lib/commands/generic-transformers'; export default { - FIRST_KEY_INDEX: undefined, + NOT_KEYED_COMMAND: true, IS_READ_ONLY: true, - transformArguments(dictionary: RedisArgument, term: RedisVariadicArgument) { - return pushVariadicArguments(['FT.DICTADD', dictionary], term); + parseCommand(parser: CommandParser, dictionary: RedisArgument, term: RedisVariadicArgument) { + parser.push('FT.DICTADD', dictionary); + parser.pushVariadic(term); }, transformReply: undefined as unknown as () => NumberReply } as const satisfies Command; diff --git a/packages/search/lib/commands/DICTDEL.spec.ts b/packages/search/lib/commands/DICTDEL.spec.ts index a7ca1b35cd3..a9f997bdf38 100644 --- a/packages/search/lib/commands/DICTDEL.spec.ts +++ b/packages/search/lib/commands/DICTDEL.spec.ts @@ -1,19 +1,20 @@ import { strict as assert } from 'node:assert'; import testUtils, { GLOBAL } from '../test-utils'; import DICTDEL from './DICTDEL'; +import { parseArgs } from '@redis/client/lib/commands/generic-transformers'; describe('FT.DICTDEL', () => { describe('transformArguments', () => { it('string', () => { assert.deepEqual( - DICTDEL.transformArguments('dictionary', 'term'), + parseArgs(DICTDEL, 'dictionary', 'term'), ['FT.DICTDEL', 'dictionary', 'term'] ); }); it('Array', () => { assert.deepEqual( - DICTDEL.transformArguments('dictionary', ['1', '2']), + parseArgs(DICTDEL, 'dictionary', ['1', '2']), ['FT.DICTDEL', 'dictionary', '1', '2'] ); }); diff --git a/packages/search/lib/commands/DICTDEL.ts b/packages/search/lib/commands/DICTDEL.ts index 087211751ee..9b0bda3a7a6 100644 --- a/packages/search/lib/commands/DICTDEL.ts +++ b/packages/search/lib/commands/DICTDEL.ts @@ -1,11 +1,13 @@ -import { RedisArgument, NumberReply, Command } from '@redis/client/dist/lib/RESP/types'; -import { pushVariadicArguments, RedisVariadicArgument } from '@redis/client/dist/lib/commands/generic-transformers'; +import { CommandParser } from '@redis/client/lib/client/parser'; +import { RedisArgument, NumberReply, Command } from '@redis/client/lib/RESP/types'; +import { RedisVariadicArgument } from '@redis/client/lib/commands/generic-transformers'; export default { - FIRST_KEY_INDEX: undefined, + NOT_KEYED_COMMAND: true, IS_READ_ONLY: true, - transformArguments(dictionary: RedisArgument, term: RedisVariadicArgument) { - return pushVariadicArguments(['FT.DICTDEL', dictionary], term); + parseCommand(parser: CommandParser, dictionary: RedisArgument, term: RedisVariadicArgument) { + parser.push('FT.DICTDEL', dictionary); + parser.pushVariadic(term); }, transformReply: undefined as unknown as () => NumberReply } as const satisfies Command; diff --git a/packages/search/lib/commands/DICTDUMP.spec.ts b/packages/search/lib/commands/DICTDUMP.spec.ts index fe8e9441189..1a3faa9dc9d 100644 --- a/packages/search/lib/commands/DICTDUMP.spec.ts +++ b/packages/search/lib/commands/DICTDUMP.spec.ts @@ -1,11 +1,12 @@ import { strict as assert } from 'node:assert'; import testUtils, { GLOBAL } from '../test-utils'; import DICTDUMP from './DICTDUMP'; +import { parseArgs } from '@redis/client/lib/commands/generic-transformers'; describe('FT.DICTDUMP', () => { it('transformArguments', () => { assert.deepEqual( - DICTDUMP.transformArguments('dictionary'), + parseArgs(DICTDUMP, 'dictionary'), ['FT.DICTDUMP', 'dictionary'] ); }); diff --git a/packages/search/lib/commands/DICTDUMP.ts b/packages/search/lib/commands/DICTDUMP.ts index f542403cc57..00dd5aba4eb 100644 --- a/packages/search/lib/commands/DICTDUMP.ts +++ b/packages/search/lib/commands/DICTDUMP.ts @@ -1,10 +1,11 @@ -import { RedisArgument, ArrayReply, SetReply, BlobStringReply, Command } from '@redis/client/dist/lib/RESP/types'; +import { CommandParser } from '@redis/client/lib/client/parser'; +import { RedisArgument, ArrayReply, SetReply, BlobStringReply, Command } from '@redis/client/lib/RESP/types'; export default { - FIRST_KEY_INDEX: undefined, + NOT_KEYED_COMMAND: true, IS_READ_ONLY: true, - transformArguments(dictionary: RedisArgument) { - return ['FT.DICTDUMP', dictionary]; + parseCommand(parser: CommandParser, dictionary: RedisArgument) { + parser.push('FT.DICTDUMP', dictionary); }, transformReply: { 2: undefined as unknown as () => ArrayReply, diff --git a/packages/search/lib/commands/DROPINDEX.spec.ts b/packages/search/lib/commands/DROPINDEX.spec.ts index 5fcbaca08ce..f1f0b0efddb 100644 --- a/packages/search/lib/commands/DROPINDEX.spec.ts +++ b/packages/search/lib/commands/DROPINDEX.spec.ts @@ -2,19 +2,20 @@ import { strict as assert } from 'node:assert'; import testUtils, { GLOBAL } from '../test-utils'; import DROPINDEX from './DROPINDEX'; import { SCHEMA_FIELD_TYPE } from './CREATE'; +import { parseArgs } from '@redis/client/lib/commands/generic-transformers'; describe('FT.DROPINDEX', () => { describe('transformArguments', () => { it('without options', () => { assert.deepEqual( - DROPINDEX.transformArguments('index'), + parseArgs(DROPINDEX, 'index'), ['FT.DROPINDEX', 'index'] ); }); it('with DD', () => { assert.deepEqual( - DROPINDEX.transformArguments('index', { DD: true }), + parseArgs(DROPINDEX, 'index', { DD: true }), ['FT.DROPINDEX', 'index', 'DD'] ); }); diff --git a/packages/search/lib/commands/DROPINDEX.ts b/packages/search/lib/commands/DROPINDEX.ts index 64fe9711e7f..e7be806ac14 100644 --- a/packages/search/lib/commands/DROPINDEX.ts +++ b/packages/search/lib/commands/DROPINDEX.ts @@ -1,20 +1,19 @@ -import { RedisArgument, SimpleStringReply, NumberReply, Command } from '@redis/client/dist/lib/RESP/types'; +import { CommandParser } from '@redis/client/lib/client/parser'; +import { RedisArgument, SimpleStringReply, NumberReply, Command } from '@redis/client/lib/RESP/types'; export interface FtDropIndexOptions { DD?: true; } export default { - FIRST_KEY_INDEX: undefined, + NOT_KEYED_COMMAND: true, IS_READ_ONLY: true, - transformArguments(index: RedisArgument, options?: FtDropIndexOptions) { - const args = ['FT.DROPINDEX', index]; + parseCommand(parser: CommandParser, index: RedisArgument, options?: FtDropIndexOptions) { + parser.push('FT.DROPINDEX', index); if (options?.DD) { - args.push('DD'); + parser.push('DD'); } - - return args; }, transformReply: { 2: undefined as unknown as () => SimpleStringReply<'OK'>, diff --git a/packages/search/lib/commands/EXPLAIN.spec.ts b/packages/search/lib/commands/EXPLAIN.spec.ts index e8b3555957f..ddc551fbd71 100644 --- a/packages/search/lib/commands/EXPLAIN.spec.ts +++ b/packages/search/lib/commands/EXPLAIN.spec.ts @@ -1,5 +1,6 @@ import { strict as assert } from 'node:assert'; import EXPLAIN from './EXPLAIN'; +import { parseArgs } from '@redis/client/lib/commands/generic-transformers'; import testUtils, { GLOBAL } from '../test-utils'; import { SCHEMA_FIELD_TYPE } from './CREATE'; @@ -7,14 +8,14 @@ describe('EXPLAIN', () => { describe('transformArguments', () => { it('simple', () => { assert.deepEqual( - EXPLAIN.transformArguments('index', '*'), + parseArgs(EXPLAIN, 'index', '*'), ['FT.EXPLAIN', 'index', '*'] ); }); it('with PARAMS', () => { assert.deepEqual( - EXPLAIN.transformArguments('index', '*', { + parseArgs(EXPLAIN, 'index', '*', { PARAMS: { param: 'value' } @@ -25,7 +26,7 @@ describe('EXPLAIN', () => { it('with DIALECT', () => { assert.deepEqual( - EXPLAIN.transformArguments('index', '*', { + parseArgs(EXPLAIN, 'index', '*', { DIALECT: 1 }), ['FT.EXPLAIN', 'index', '*', 'DIALECT', '1'] diff --git a/packages/search/lib/commands/EXPLAIN.ts b/packages/search/lib/commands/EXPLAIN.ts index 0ad84feb68d..deb75229b5d 100644 --- a/packages/search/lib/commands/EXPLAIN.ts +++ b/packages/search/lib/commands/EXPLAIN.ts @@ -1,5 +1,6 @@ -import { RedisArgument, SimpleStringReply, Command } from '@redis/client/dist/lib/RESP/types'; -import { FtSearchParams, pushParamsArgument } from './SEARCH'; +import { CommandParser } from '@redis/client/lib/client/parser'; +import { RedisArgument, SimpleStringReply, Command } from '@redis/client/lib/RESP/types'; +import { FtSearchParams, parseParamsArgument } from './SEARCH'; export interface FtExplainOptions { PARAMS?: FtSearchParams; @@ -7,22 +8,21 @@ export interface FtExplainOptions { } export default { - FIRST_KEY_INDEX: undefined, + NOT_KEYED_COMMAND: true, IS_READ_ONLY: true, - transformArguments( + parseCommand( + parser: CommandParser, index: RedisArgument, query: RedisArgument, options?: FtExplainOptions ) { - const args = ['FT.EXPLAIN', index, query]; + parser.push('FT.EXPLAIN', index, query); - pushParamsArgument(args, options?.PARAMS); + parseParamsArgument(parser, options?.PARAMS); if (options?.DIALECT) { - args.push('DIALECT', options.DIALECT.toString()); + parser.push('DIALECT', options.DIALECT.toString()); } - - return args; }, transformReply: undefined as unknown as () => SimpleStringReply } as const satisfies Command; diff --git a/packages/search/lib/commands/EXPLAINCLI.spec.ts b/packages/search/lib/commands/EXPLAINCLI.spec.ts index 3bffcf5fe5b..cf46a1740c5 100644 --- a/packages/search/lib/commands/EXPLAINCLI.spec.ts +++ b/packages/search/lib/commands/EXPLAINCLI.spec.ts @@ -1,10 +1,11 @@ import { strict as assert } from 'node:assert'; import EXPLAINCLI from './EXPLAINCLI'; +import { parseArgs } from '@redis/client/lib/commands/generic-transformers'; describe('EXPLAINCLI', () => { it('transformArguments', () => { assert.deepEqual( - EXPLAINCLI.transformArguments('index', '*'), + parseArgs(EXPLAINCLI, 'index', '*'), ['FT.EXPLAINCLI', 'index', '*'] ); }); diff --git a/packages/search/lib/commands/EXPLAINCLI.ts b/packages/search/lib/commands/EXPLAINCLI.ts index e16866991b9..7a4ae3a4b25 100644 --- a/packages/search/lib/commands/EXPLAINCLI.ts +++ b/packages/search/lib/commands/EXPLAINCLI.ts @@ -1,10 +1,11 @@ -import { RedisArgument, ArrayReply, BlobStringReply, Command } from '@redis/client/dist/lib/RESP/types'; +import { CommandParser } from '@redis/client/lib/client/parser'; +import { RedisArgument, ArrayReply, BlobStringReply, Command } from '@redis/client/lib/RESP/types'; export default { - FIRST_KEY_INDEX: undefined, + NOT_KEYED_COMMAND: true, IS_READ_ONLY: true, - transformArguments(index: RedisArgument, query: RedisArgument) { - return ['FT.EXPLAINCLI', index, query]; + parseCommand(parser: CommandParser, index: RedisArgument, query: RedisArgument) { + parser.push('FT.EXPLAINCLI', index, query); }, transformReply: undefined as unknown as () => ArrayReply } as const satisfies Command; diff --git a/packages/search/lib/commands/INFO.spec.ts b/packages/search/lib/commands/INFO.spec.ts index e7c7c897a84..cbb4ea91677 100644 --- a/packages/search/lib/commands/INFO.spec.ts +++ b/packages/search/lib/commands/INFO.spec.ts @@ -2,11 +2,12 @@ import { strict as assert } from 'node:assert'; import testUtils, { GLOBAL } from '../test-utils'; import INFO, { InfoReply } from './INFO'; import { SCHEMA_FIELD_TYPE } from './CREATE'; +import { parseArgs } from '@redis/client/lib/commands/generic-transformers'; describe('INFO', () => { it('transformArguments', () => { assert.deepEqual( - INFO.transformArguments('index'), + parseArgs(INFO, 'index'), ['FT.INFO', 'index'] ); }); diff --git a/packages/search/lib/commands/INFO.ts b/packages/search/lib/commands/INFO.ts index 52b87769cef..6792645fe3e 100644 --- a/packages/search/lib/commands/INFO.ts +++ b/packages/search/lib/commands/INFO.ts @@ -1,13 +1,14 @@ +import { CommandParser } from '@redis/client/lib/client/parser'; import { RedisArgument } from "@redis/client"; -import { ArrayReply, BlobStringReply, Command, DoubleReply, MapReply, NullReply, NumberReply, ReplyUnion, SimpleStringReply, TypeMapping } from "@redis/client/dist/lib/RESP/types"; -import { createTransformTuplesReplyFunc, transformDoubleReply } from "@redis/client/dist/lib/commands/generic-transformers"; +import { ArrayReply, BlobStringReply, Command, DoubleReply, MapReply, NullReply, NumberReply, ReplyUnion, SimpleStringReply, TypeMapping } from "@redis/client/lib/RESP/types"; +import { createTransformTuplesReplyFunc, transformDoubleReply } from "@redis/client/lib/commands/generic-transformers"; import { TuplesReply } from '@redis/client/lib/RESP/types'; export default { - FIRST_KEY_INDEX: undefined, + NOT_KEYED_COMMAND: true, IS_READ_ONLY: true, - transformArguments(index: RedisArgument) { - return ['FT.INFO', index]; + parseCommand(parser: CommandParser, index: RedisArgument) { + parser.push('FT.INFO', index); }, transformReply: { 2: transformV2Reply, diff --git a/packages/search/lib/commands/PROFILE_AGGREGATE.spec.ts b/packages/search/lib/commands/PROFILE_AGGREGATE.spec.ts index 8644ca5201e..ee112118c95 100644 --- a/packages/search/lib/commands/PROFILE_AGGREGATE.spec.ts +++ b/packages/search/lib/commands/PROFILE_AGGREGATE.spec.ts @@ -3,19 +3,20 @@ import testUtils, { GLOBAL } from '../test-utils'; import { FT_AGGREGATE_STEPS } from './AGGREGATE'; import PROFILE_AGGREGATE from './PROFILE_AGGREGATE'; import { SCHEMA_FIELD_TYPE } from './CREATE'; +import { parseArgs } from '@redis/client/lib/commands/generic-transformers'; describe('PROFILE AGGREGATE', () => { describe('transformArguments', () => { it('without options', () => { assert.deepEqual( - PROFILE_AGGREGATE.transformArguments('index', 'query'), + parseArgs(PROFILE_AGGREGATE, 'index', 'query'), ['FT.PROFILE', 'index', 'AGGREGATE', 'QUERY', 'query'] ); }); it('with options', () => { assert.deepEqual( - PROFILE_AGGREGATE.transformArguments('index', 'query', { + parseArgs(PROFILE_AGGREGATE, 'index', 'query', { LIMITED: true, VERBATIM: true, STEPS: [{ diff --git a/packages/search/lib/commands/PROFILE_AGGREGATE.ts b/packages/search/lib/commands/PROFILE_AGGREGATE.ts index b6a8db38665..703bfcacc72 100644 --- a/packages/search/lib/commands/PROFILE_AGGREGATE.ts +++ b/packages/search/lib/commands/PROFILE_AGGREGATE.ts @@ -1,27 +1,26 @@ -// import { pushAggregatehOptions, AggregateOptions, transformReply as transformAggregateReply, AggregateRawReply } from './AGGREGATE'; -// import { ProfileOptions, ProfileRawReply, ProfileReply, transformProfile } from '.'; - -import { Command, ReplyUnion } from "@redis/client/dist/lib/RESP/types"; -import AGGREGATE, { AggregateRawReply, FtAggregateOptions, pushAggregateOptions } from "./AGGREGATE"; +import { CommandParser } from '@redis/client/lib/client/parser'; +import { Command, ReplyUnion } from "@redis/client/lib/RESP/types"; +import AGGREGATE, { AggregateRawReply, FtAggregateOptions, parseAggregateOptions } from "./AGGREGATE"; import { ProfileOptions, ProfileRawReply, ProfileReply, transformProfile } from "./PROFILE_SEARCH"; export default { - FIRST_KEY_INDEX: undefined, + NOT_KEYED_COMMAND: true, IS_READ_ONLY: true, - transformArguments( + parseCommand( + parser: CommandParser, index: string, query: string, options?: ProfileOptions & FtAggregateOptions ) { - const args = ['FT.PROFILE', index, 'AGGREGATE']; + parser.push('FT.PROFILE', index, 'AGGREGATE'); if (options?.LIMITED) { - args.push('LIMITED'); + parser.push('LIMITED'); } - args.push('QUERY', query); + parser.push('QUERY', query); - return pushAggregateOptions(args, options) + parseAggregateOptions(parser, options) }, transformReply: { 2: (reply: ProfileAggeregateRawReply): ProfileReply => { diff --git a/packages/search/lib/commands/PROFILE_SEARCH.spec.ts b/packages/search/lib/commands/PROFILE_SEARCH.spec.ts index a6e2a968d43..524ff1a5228 100644 --- a/packages/search/lib/commands/PROFILE_SEARCH.spec.ts +++ b/packages/search/lib/commands/PROFILE_SEARCH.spec.ts @@ -2,20 +2,21 @@ import { strict as assert } from 'node:assert'; import testUtils, { GLOBAL } from '../test-utils'; import PROFILE_SEARCH from './PROFILE_SEARCH'; import { SCHEMA_FIELD_TYPE } from './CREATE'; +import { parseArgs } from '@redis/client/lib/commands/generic-transformers'; describe('PROFILE SEARCH', () => { describe('transformArguments', () => { it('without options', () => { assert.deepEqual( - PROFILE_SEARCH.transformArguments('index', 'query'), + parseArgs(PROFILE_SEARCH, 'index', 'query'), ['FT.PROFILE', 'index', 'SEARCH', 'QUERY', 'query'] ); }); it('with options', () => { assert.deepEqual( - PROFILE_SEARCH.transformArguments('index', 'query', { + parseArgs(PROFILE_SEARCH, 'index', 'query', { LIMITED: true, VERBATIM: true, INKEYS: 'key' diff --git a/packages/search/lib/commands/PROFILE_SEARCH.ts b/packages/search/lib/commands/PROFILE_SEARCH.ts index 5b9e918083b..c345e70dd78 100644 --- a/packages/search/lib/commands/PROFILE_SEARCH.ts +++ b/packages/search/lib/commands/PROFILE_SEARCH.ts @@ -1,10 +1,7 @@ -// import { SearchOptions, SearchRawReply, transformReply as transformSearchReply } from './SEARCH'; -// import { pushSearchOptions, ProfileOptions, ProfileRawReply, ProfileReply, transformProfile } from '.'; -// import { RedisCommandArguments } from '@redis/client/dist/lib/commands'; - -import { Command, RedisArgument, ReplyUnion } from "@redis/client/dist/lib/RESP/types"; -import SEARCH, { FtSearchOptions, SearchRawReply, SearchReply, pushSearchOptions } from "./SEARCH"; +import { CommandParser } from '@redis/client/lib/client/parser'; +import { Command, RedisArgument, ReplyUnion } from "@redis/client/lib/RESP/types"; import { AggregateReply } from "./AGGREGATE"; +import SEARCH, { FtSearchOptions, SearchRawReply, SearchReply, parseSearchOptions } from "./SEARCH"; export type ProfileRawReply = [ results: T, @@ -27,22 +24,23 @@ export interface ProfileOptions { } export default { - FIRST_KEY_INDEX: undefined, + NOT_KEYED_COMMAND: true, IS_READ_ONLY: true, - transformArguments( + parseCommand( + parser: CommandParser, index: RedisArgument, query: RedisArgument, options?: ProfileOptions & FtSearchOptions ) { - let args: Array = ['FT.PROFILE', index, 'SEARCH']; + parser.push('FT.PROFILE', index, 'SEARCH'); if (options?.LIMITED) { - args.push('LIMITED'); + parser.push('LIMITED'); } - args.push('QUERY', query); + parser.push('QUERY', query); - return pushSearchOptions(args, options); + parseSearchOptions(parser, options); }, transformReply: { 2: (reply: ProfileSearchRawReply, withoutDocuments: boolean): ProfileReply => { diff --git a/packages/search/lib/commands/SEARCH.spec.ts b/packages/search/lib/commands/SEARCH.spec.ts index 257dbb79515..24248b4cf15 100644 --- a/packages/search/lib/commands/SEARCH.spec.ts +++ b/packages/search/lib/commands/SEARCH.spec.ts @@ -1,19 +1,20 @@ import { strict as assert } from 'node:assert'; import testUtils, { GLOBAL } from '../test-utils'; import SEARCH from './SEARCH'; +import { parseArgs } from '@redis/client/lib/commands/generic-transformers'; describe('FT.SEARCH', () => { describe('transformArguments', () => { it('without options', () => { assert.deepEqual( - SEARCH.transformArguments('index', 'query'), + parseArgs(SEARCH, 'index', 'query'), ['FT.SEARCH', 'index', 'query'] ); }); it('with VERBATIM', () => { assert.deepEqual( - SEARCH.transformArguments('index', 'query', { + parseArgs(SEARCH, 'index', 'query', { VERBATIM: true }), ['FT.SEARCH', 'index', 'query', 'VERBATIM'] @@ -22,7 +23,7 @@ describe('FT.SEARCH', () => { it('with NOSTOPWORDS', () => { assert.deepEqual( - SEARCH.transformArguments('index', 'query', { + parseArgs(SEARCH, 'index', 'query', { NOSTOPWORDS: true }), ['FT.SEARCH', 'index', 'query', 'NOSTOPWORDS'] @@ -31,7 +32,7 @@ describe('FT.SEARCH', () => { it('with INKEYS', () => { assert.deepEqual( - SEARCH.transformArguments('index', 'query', { + parseArgs(SEARCH, 'index', 'query', { INKEYS: 'key' }), ['FT.SEARCH', 'index', 'query', 'INKEYS', '1', 'key'] @@ -40,7 +41,7 @@ describe('FT.SEARCH', () => { it('with INFIELDS', () => { assert.deepEqual( - SEARCH.transformArguments('index', 'query', { + parseArgs(SEARCH, 'index', 'query', { INFIELDS: 'field' }), ['FT.SEARCH', 'index', 'query', 'INFIELDS', '1', 'field'] @@ -49,7 +50,7 @@ describe('FT.SEARCH', () => { it('with RETURN', () => { assert.deepEqual( - SEARCH.transformArguments('index', 'query', { + parseArgs(SEARCH, 'index', 'query', { RETURN: 'return' }), ['FT.SEARCH', 'index', 'query', 'RETURN', '1', 'return'] @@ -59,7 +60,7 @@ describe('FT.SEARCH', () => { describe('with SUMMARIZE', () => { it('true', () => { assert.deepEqual( - SEARCH.transformArguments('index', 'query', { + parseArgs(SEARCH, 'index', 'query', { SUMMARIZE: true }), ['FT.SEARCH', 'index', 'query', 'SUMMARIZE'] @@ -69,7 +70,7 @@ describe('FT.SEARCH', () => { describe('with FIELDS', () => { it('string', () => { assert.deepEqual( - SEARCH.transformArguments('index', 'query', { + parseArgs(SEARCH, 'index', 'query', { SUMMARIZE: { FIELDS: '@field' } @@ -80,7 +81,7 @@ describe('FT.SEARCH', () => { it('Array', () => { assert.deepEqual( - SEARCH.transformArguments('index', 'query', { + parseArgs(SEARCH, 'index', 'query', { SUMMARIZE: { FIELDS: ['@1', '@2'] } @@ -92,7 +93,7 @@ describe('FT.SEARCH', () => { it('with FRAGS', () => { assert.deepEqual( - SEARCH.transformArguments('index', 'query', { + parseArgs(SEARCH, 'index', 'query', { SUMMARIZE: { FRAGS: 1 } @@ -103,7 +104,7 @@ describe('FT.SEARCH', () => { it('with LEN', () => { assert.deepEqual( - SEARCH.transformArguments('index', 'query', { + parseArgs(SEARCH, 'index', 'query', { SUMMARIZE: { LEN: 1 } @@ -114,7 +115,7 @@ describe('FT.SEARCH', () => { it('with SEPARATOR', () => { assert.deepEqual( - SEARCH.transformArguments('index', 'query', { + parseArgs(SEARCH, 'index', 'query', { SUMMARIZE: { SEPARATOR: 'separator' } @@ -127,7 +128,7 @@ describe('FT.SEARCH', () => { describe('with HIGHLIGHT', () => { it('true', () => { assert.deepEqual( - SEARCH.transformArguments('index', 'query', { + parseArgs(SEARCH, 'index', 'query', { HIGHLIGHT: true }), ['FT.SEARCH', 'index', 'query', 'HIGHLIGHT'] @@ -137,7 +138,7 @@ describe('FT.SEARCH', () => { describe('with FIELDS', () => { it('string', () => { assert.deepEqual( - SEARCH.transformArguments('index', 'query', { + parseArgs(SEARCH, 'index', 'query', { HIGHLIGHT: { FIELDS: ['@field'] } @@ -148,7 +149,7 @@ describe('FT.SEARCH', () => { it('Array', () => { assert.deepEqual( - SEARCH.transformArguments('index', 'query', { + parseArgs(SEARCH, 'index', 'query', { HIGHLIGHT: { FIELDS: ['@1', '@2'] } @@ -160,7 +161,7 @@ describe('FT.SEARCH', () => { it('with TAGS', () => { assert.deepEqual( - SEARCH.transformArguments('index', 'query', { + parseArgs(SEARCH, 'index', 'query', { HIGHLIGHT: { TAGS: { open: 'open', @@ -175,7 +176,7 @@ describe('FT.SEARCH', () => { it('with SLOP', () => { assert.deepEqual( - SEARCH.transformArguments('index', 'query', { + parseArgs(SEARCH, 'index', 'query', { SLOP: 1 }), ['FT.SEARCH', 'index', 'query', 'SLOP', '1'] @@ -184,7 +185,7 @@ describe('FT.SEARCH', () => { it('with TIMEOUT', () => { assert.deepEqual( - SEARCH.transformArguments('index', 'query', { + parseArgs(SEARCH, 'index', 'query', { TIMEOUT: 1 }), ['FT.SEARCH', 'index', 'query', 'TIMEOUT', '1'] @@ -193,7 +194,7 @@ describe('FT.SEARCH', () => { it('with INORDER', () => { assert.deepEqual( - SEARCH.transformArguments('index', 'query', { + parseArgs(SEARCH, 'index', 'query', { INORDER: true }), ['FT.SEARCH', 'index', 'query', 'INORDER'] @@ -202,7 +203,7 @@ describe('FT.SEARCH', () => { it('with LANGUAGE', () => { assert.deepEqual( - SEARCH.transformArguments('index', 'query', { + parseArgs(SEARCH, 'index', 'query', { LANGUAGE: 'Arabic' }), ['FT.SEARCH', 'index', 'query', 'LANGUAGE', 'Arabic'] @@ -211,7 +212,7 @@ describe('FT.SEARCH', () => { it('with EXPANDER', () => { assert.deepEqual( - SEARCH.transformArguments('index', 'query', { + parseArgs(SEARCH, 'index', 'query', { EXPANDER: 'expender' }), ['FT.SEARCH', 'index', 'query', 'EXPANDER', 'expender'] @@ -220,7 +221,7 @@ describe('FT.SEARCH', () => { it('with SCORER', () => { assert.deepEqual( - SEARCH.transformArguments('index', 'query', { + parseArgs(SEARCH, 'index', 'query', { SCORER: 'scorer' }), ['FT.SEARCH', 'index', 'query', 'SCORER', 'scorer'] @@ -229,7 +230,7 @@ describe('FT.SEARCH', () => { it('with SORTBY', () => { assert.deepEqual( - SEARCH.transformArguments('index', 'query', { + parseArgs(SEARCH, 'index', 'query', { SORTBY: '@by' }), ['FT.SEARCH', 'index', 'query', 'SORTBY', '@by'] @@ -238,7 +239,7 @@ describe('FT.SEARCH', () => { it('with LIMIT', () => { assert.deepEqual( - SEARCH.transformArguments('index', 'query', { + parseArgs(SEARCH, 'index', 'query', { LIMIT: { from: 0, size: 1 @@ -250,7 +251,7 @@ describe('FT.SEARCH', () => { it('with PARAMS', () => { assert.deepEqual( - SEARCH.transformArguments('index', 'query', { + parseArgs(SEARCH, 'index', 'query', { PARAMS: { string: 'string', buffer: Buffer.from('buffer'), @@ -263,7 +264,7 @@ describe('FT.SEARCH', () => { it('with DIALECT', () => { assert.deepEqual( - SEARCH.transformArguments('index', 'query', { + parseArgs(SEARCH, 'index', 'query', { DIALECT: 1 }), ['FT.SEARCH', 'index', 'query', 'DIALECT', '1'] diff --git a/packages/search/lib/commands/SEARCH.ts b/packages/search/lib/commands/SEARCH.ts index 1e5e8ec91f5..de03ac0070e 100644 --- a/packages/search/lib/commands/SEARCH.ts +++ b/packages/search/lib/commands/SEARCH.ts @@ -1,12 +1,15 @@ -import { RedisArgument, Command, ReplyUnion } from '@redis/client/dist/lib/RESP/types'; -import { RedisVariadicArgument, pushOptionalVariadicArgument } from '@redis/client/dist/lib/commands/generic-transformers'; +import { CommandParser } from '@redis/client/lib/client/parser'; +import { RedisArgument, Command, ReplyUnion } from '@redis/client/lib/RESP/types'; +import { RedisVariadicArgument, parseOptionalVariadicArgument } from '@redis/client/lib/commands/generic-transformers'; import { RediSearchProperty, RediSearchLanguage } from './CREATE'; export type FtSearchParams = Record; -export function pushParamsArgument(args: Array, params?: FtSearchParams) { +export function parseParamsArgument(parser: CommandParser, params?: FtSearchParams) { if (params) { - const length = args.push('PARAMS', ''); + parser.push('PARAMS'); + + const args: Array = []; for (const key in params) { if (!Object.hasOwn(params, key)) continue; @@ -17,7 +20,7 @@ export function pushParamsArgument(args: Array, params?: FtSearch ); } - args[length - 1] = (args.length - length).toString(); + parser.pushVariadicWithLength(args); } } @@ -58,109 +61,107 @@ export interface FtSearchOptions { DIALECT?: number; } -export function pushSearchOptions(args: Array, options?: FtSearchOptions) { +export function parseSearchOptions(parser: CommandParser, options?: FtSearchOptions) { if (options?.VERBATIM) { - args.push('VERBATIM'); + parser.push('VERBATIM'); } if (options?.NOSTOPWORDS) { - args.push('NOSTOPWORDS'); + parser.push('NOSTOPWORDS'); } - pushOptionalVariadicArgument(args, 'INKEYS', options?.INKEYS); - pushOptionalVariadicArgument(args, 'INFIELDS', options?.INFIELDS); - pushOptionalVariadicArgument(args, 'RETURN', options?.RETURN); + parseOptionalVariadicArgument(parser, 'INKEYS', options?.INKEYS); + parseOptionalVariadicArgument(parser, 'INFIELDS', options?.INFIELDS); + parseOptionalVariadicArgument(parser, 'RETURN', options?.RETURN); if (options?.SUMMARIZE) { - args.push('SUMMARIZE'); + parser.push('SUMMARIZE'); if (typeof options.SUMMARIZE === 'object') { - pushOptionalVariadicArgument(args, 'FIELDS', options.SUMMARIZE.FIELDS); + parseOptionalVariadicArgument(parser, 'FIELDS', options.SUMMARIZE.FIELDS); if (options.SUMMARIZE.FRAGS !== undefined) { - args.push('FRAGS', options.SUMMARIZE.FRAGS.toString()); + parser.push('FRAGS', options.SUMMARIZE.FRAGS.toString()); } if (options.SUMMARIZE.LEN !== undefined) { - args.push('LEN', options.SUMMARIZE.LEN.toString()); + parser.push('LEN', options.SUMMARIZE.LEN.toString()); } if (options.SUMMARIZE.SEPARATOR !== undefined) { - args.push('SEPARATOR', options.SUMMARIZE.SEPARATOR); + parser.push('SEPARATOR', options.SUMMARIZE.SEPARATOR); } } } if (options?.HIGHLIGHT) { - args.push('HIGHLIGHT'); + parser.push('HIGHLIGHT'); if (typeof options.HIGHLIGHT === 'object') { - pushOptionalVariadicArgument(args, 'FIELDS', options.HIGHLIGHT.FIELDS); + parseOptionalVariadicArgument(parser, 'FIELDS', options.HIGHLIGHT.FIELDS); if (options.HIGHLIGHT.TAGS) { - args.push('TAGS', options.HIGHLIGHT.TAGS.open, options.HIGHLIGHT.TAGS.close); + parser.push('TAGS', options.HIGHLIGHT.TAGS.open, options.HIGHLIGHT.TAGS.close); } } } if (options?.SLOP !== undefined) { - args.push('SLOP', options.SLOP.toString()); + parser.push('SLOP', options.SLOP.toString()); } if (options?.TIMEOUT !== undefined) { - args.push('TIMEOUT', options.TIMEOUT.toString()); + parser.push('TIMEOUT', options.TIMEOUT.toString()); } if (options?.INORDER) { - args.push('INORDER'); + parser.push('INORDER'); } if (options?.LANGUAGE) { - args.push('LANGUAGE', options.LANGUAGE); + parser.push('LANGUAGE', options.LANGUAGE); } if (options?.EXPANDER) { - args.push('EXPANDER', options.EXPANDER); + parser.push('EXPANDER', options.EXPANDER); } if (options?.SCORER) { - args.push('SCORER', options.SCORER); + parser.push('SCORER', options.SCORER); } if (options?.SORTBY) { - args.push('SORTBY'); + parser.push('SORTBY'); if (typeof options.SORTBY === 'string' || options.SORTBY instanceof Buffer) { - args.push(options.SORTBY); + parser.push(options.SORTBY); } else { - args.push(options.SORTBY.BY); + parser.push(options.SORTBY.BY); if (options.SORTBY.DIRECTION) { - args.push(options.SORTBY.DIRECTION); + parser.push(options.SORTBY.DIRECTION); } } } if (options?.LIMIT) { - args.push('LIMIT', options.LIMIT.from.toString(), options.LIMIT.size.toString()); + parser.push('LIMIT', options.LIMIT.from.toString(), options.LIMIT.size.toString()); } - pushParamsArgument(args, options?.PARAMS); + parseParamsArgument(parser, options?.PARAMS); if (options?.DIALECT !== undefined) { - args.push('DIALECT', options.DIALECT.toString()); + parser.push('DIALECT', options.DIALECT.toString()); } - - return args; } export default { - FIRST_KEY_INDEX: undefined, + NOT_KEYED_COMMAND: true, IS_READ_ONLY: true, - transformArguments(index: RedisArgument, query: RedisArgument, options?: FtSearchOptions) { - const args = ['FT.SEARCH', index, query]; + parseCommand(parser: CommandParser, index: RedisArgument, query: RedisArgument, options?: FtSearchOptions) { + parser.push('FT.SEARCH', index, query); - return pushSearchOptions(args, options); + parseSearchOptions(parser, options); }, transformReply: { 2: (reply: SearchRawReply): SearchReply => { diff --git a/packages/search/lib/commands/SEARCH_NOCONTENT.spec.ts b/packages/search/lib/commands/SEARCH_NOCONTENT.spec.ts index be998b9e63d..bfcca8b4bda 100644 --- a/packages/search/lib/commands/SEARCH_NOCONTENT.spec.ts +++ b/packages/search/lib/commands/SEARCH_NOCONTENT.spec.ts @@ -1,12 +1,13 @@ import { strict as assert } from 'assert'; import testUtils, { GLOBAL } from '../test-utils'; import SEARCH_NOCONTENT from './SEARCH_NOCONTENT'; +import { parseArgs } from '@redis/client/lib/commands/generic-transformers'; describe('FT.SEARCH NOCONTENT', () => { describe('transformArguments', () => { it('without options', () => { assert.deepEqual( - SEARCH_NOCONTENT.transformArguments('index', 'query'), + parseArgs(SEARCH_NOCONTENT, 'index', 'query'), ['FT.SEARCH', 'index', 'query', 'NOCONTENT'] ); }); diff --git a/packages/search/lib/commands/SEARCH_NOCONTENT.ts b/packages/search/lib/commands/SEARCH_NOCONTENT.ts index 4ee959b9d71..a4c6f6a27aa 100644 --- a/packages/search/lib/commands/SEARCH_NOCONTENT.ts +++ b/packages/search/lib/commands/SEARCH_NOCONTENT.ts @@ -1,13 +1,12 @@ -import { Command, ReplyUnion } from '@redis/client/dist/lib/RESP/types'; +import { Command, ReplyUnion } from '@redis/client/lib/RESP/types'; import SEARCH, { SearchRawReply } from './SEARCH'; export default { - FIRST_KEY_INDEX: SEARCH.FIRST_KEY_INDEX, + NOT_KEYED_COMMAND: SEARCH.NOT_KEYED_COMMAND, IS_READ_ONLY: SEARCH.IS_READ_ONLY, - transformArguments(...args: Parameters) { - const redisArgs = SEARCH.transformArguments(...args); - redisArgs.push('NOCONTENT'); - return redisArgs; + parseCommand(...args: Parameters) { + SEARCH.parseCommand(...args); + args[0].push('NOCONTENT'); }, transformReply: { 2: (reply: SearchRawReply): SearchNoContentReply => { diff --git a/packages/search/lib/commands/SPELLCHECK.spec.ts b/packages/search/lib/commands/SPELLCHECK.spec.ts index a70ee964920..4f5a3628f4d 100644 --- a/packages/search/lib/commands/SPELLCHECK.spec.ts +++ b/packages/search/lib/commands/SPELLCHECK.spec.ts @@ -1,19 +1,20 @@ import { strict as assert } from 'node:assert'; import testUtils, { GLOBAL } from '../test-utils'; import SPELLCHECK from './SPELLCHECK'; +import { parseArgs } from '@redis/client/lib/commands/generic-transformers'; describe('FT.SPELLCHECK', () => { describe('transformArguments', () => { it('without options', () => { assert.deepEqual( - SPELLCHECK.transformArguments('index', 'query'), + parseArgs(SPELLCHECK, 'index', 'query'), ['FT.SPELLCHECK', 'index', 'query'] ); }); it('with DISTANCE', () => { assert.deepEqual( - SPELLCHECK.transformArguments('index', 'query', { + parseArgs(SPELLCHECK, 'index', 'query', { DISTANCE: 2 }), ['FT.SPELLCHECK', 'index', 'query', 'DISTANCE', '2'] @@ -23,7 +24,7 @@ describe('FT.SPELLCHECK', () => { describe('with TERMS', () => { it('single', () => { assert.deepEqual( - SPELLCHECK.transformArguments('index', 'query', { + parseArgs(SPELLCHECK, 'index', 'query', { TERMS: { mode: 'INCLUDE', dictionary: 'dictionary' @@ -35,7 +36,7 @@ describe('FT.SPELLCHECK', () => { it('multiple', () => { assert.deepEqual( - SPELLCHECK.transformArguments('index', 'query', { + parseArgs(SPELLCHECK, 'index', 'query', { TERMS: [{ mode: 'INCLUDE', dictionary: 'include' @@ -51,7 +52,7 @@ describe('FT.SPELLCHECK', () => { it('with DIALECT', () => { assert.deepEqual( - SPELLCHECK.transformArguments('index', 'query', { + parseArgs(SPELLCHECK, 'index', 'query', { DIALECT: 1 }), ['FT.SPELLCHECK', 'index', 'query', 'DIALECT', '1'] diff --git a/packages/search/lib/commands/SPELLCHECK.ts b/packages/search/lib/commands/SPELLCHECK.ts index f52e74ba0f6..1e6981d01ff 100644 --- a/packages/search/lib/commands/SPELLCHECK.ts +++ b/packages/search/lib/commands/SPELLCHECK.ts @@ -1,4 +1,5 @@ -import { RedisArgument, CommandArguments, Command, ReplyUnion } from '@redis/client/dist/lib/RESP/types'; +import { CommandParser } from '@redis/client/lib/client/parser'; +import { RedisArgument, Command, ReplyUnion } from '@redis/client/lib/RESP/types'; export interface Terms { mode: 'INCLUDE' | 'EXCLUDE'; @@ -12,30 +13,28 @@ export interface FtSpellCheckOptions { } export default { - FIRST_KEY_INDEX: undefined, + NOT_KEYED_COMMAND: true, IS_READ_ONLY: true, - transformArguments(index: RedisArgument, query: RedisArgument, options?: FtSpellCheckOptions) { - const args = ['FT.SPELLCHECK', index, query]; + parseCommand(parser: CommandParser, index: RedisArgument, query: RedisArgument, options?: FtSpellCheckOptions) { + parser.push('FT.SPELLCHECK', index, query); if (options?.DISTANCE) { - args.push('DISTANCE', options.DISTANCE.toString()); + parser.push('DISTANCE', options.DISTANCE.toString()); } if (options?.TERMS) { if (Array.isArray(options.TERMS)) { for (const term of options.TERMS) { - pushTerms(args, term); + parseTerms(parser, term); } } else { - pushTerms(args, options.TERMS); + parseTerms(parser, options.TERMS); } } if (options?.DIALECT) { - args.push('DIALECT', options.DIALECT.toString()); + parser.push('DIALECT', options.DIALECT.toString()); } - - return args; }, transformReply: { 2: (rawReply: SpellCheckRawReply): SpellCheckReply => { @@ -52,6 +51,10 @@ export default { unstableResp3: true } as const satisfies Command; +function parseTerms(parser: CommandParser, { mode, dictionary }: Terms) { + parser.push('TERMS', mode, dictionary); +} + type SpellCheckRawReply = Array<[ _: string, term: string, @@ -65,7 +68,3 @@ type SpellCheckReply = Array<{ suggestion: string }> }>; - -function pushTerms(args: CommandArguments, { mode, dictionary }: Terms) { - args.push('TERMS', mode, dictionary); -} diff --git a/packages/search/lib/commands/SUGADD.spec.ts b/packages/search/lib/commands/SUGADD.spec.ts index 24e03d37796..2e0ce92edbc 100644 --- a/packages/search/lib/commands/SUGADD.spec.ts +++ b/packages/search/lib/commands/SUGADD.spec.ts @@ -1,26 +1,27 @@ import { strict as assert } from 'node:assert'; import testUtils, { GLOBAL } from '../test-utils'; import SUGADD from './SUGADD'; +import { parseArgs } from '@redis/client/lib/commands/generic-transformers'; describe('FT.SUGADD', () => { describe('transformArguments', () => { it('without options', () => { assert.deepEqual( - SUGADD.transformArguments('key', 'string', 1), + parseArgs(SUGADD, 'key', 'string', 1), ['FT.SUGADD', 'key', 'string', '1'] ); }); it('with INCR', () => { assert.deepEqual( - SUGADD.transformArguments('key', 'string', 1, { INCR: true }), + parseArgs(SUGADD, 'key', 'string', 1, { INCR: true }), ['FT.SUGADD', 'key', 'string', '1', 'INCR'] ); }); it('with PAYLOAD', () => { assert.deepEqual( - SUGADD.transformArguments('key', 'string', 1, { PAYLOAD: 'payload' }), + parseArgs(SUGADD, 'key', 'string', 1, { PAYLOAD: 'payload' }), ['FT.SUGADD', 'key', 'string', '1', 'PAYLOAD', 'payload'] ); }); diff --git a/packages/search/lib/commands/SUGADD.ts b/packages/search/lib/commands/SUGADD.ts index c18cd7846ed..a82f03ffa1b 100644 --- a/packages/search/lib/commands/SUGADD.ts +++ b/packages/search/lib/commands/SUGADD.ts @@ -1,4 +1,5 @@ -import { RedisArgument, NumberReply, Command } from '@redis/client/dist/lib/RESP/types'; +import { CommandParser } from '@redis/client/lib/client/parser'; +import { RedisArgument, NumberReply, Command } from '@redis/client/lib/RESP/types'; export interface FtSugAddOptions { INCR?: boolean; @@ -6,20 +7,19 @@ export interface FtSugAddOptions { } export default { - FIRST_KEY_INDEX: 1, IS_READ_ONLY: true, - transformArguments(key: RedisArgument, string: RedisArgument, score: number, options?: FtSugAddOptions) { - const args = ['FT.SUGADD', key, string, score.toString()]; + parseCommand(parser: CommandParser, key: RedisArgument, string: RedisArgument, score: number, options?: FtSugAddOptions) { + parser.push('FT.SUGADD'); + parser.pushKey(key); + parser.push(string, score.toString()); if (options?.INCR) { - args.push('INCR'); + parser.push('INCR'); } if (options?.PAYLOAD) { - args.push('PAYLOAD', options.PAYLOAD); + parser.push('PAYLOAD', options.PAYLOAD); } - - return args; }, transformReply: undefined as unknown as () => NumberReply } as const satisfies Command; diff --git a/packages/search/lib/commands/SUGDEL.spec.ts b/packages/search/lib/commands/SUGDEL.spec.ts index ea92c2a1a49..21677f14213 100644 --- a/packages/search/lib/commands/SUGDEL.spec.ts +++ b/packages/search/lib/commands/SUGDEL.spec.ts @@ -1,11 +1,12 @@ import { strict as assert } from 'node:assert'; import testUtils, { GLOBAL } from '../test-utils'; import SUGDEL from './SUGDEL'; +import { parseArgs } from '@redis/client/lib/commands/generic-transformers'; describe('FT.SUGDEL', () => { it('transformArguments', () => { assert.deepEqual( - SUGDEL.transformArguments('key', 'string'), + parseArgs(SUGDEL, 'key', 'string'), ['FT.SUGDEL', 'key', 'string'] ); }); diff --git a/packages/search/lib/commands/SUGDEL.ts b/packages/search/lib/commands/SUGDEL.ts index 5829ec40a2c..1cdf56d2025 100644 --- a/packages/search/lib/commands/SUGDEL.ts +++ b/packages/search/lib/commands/SUGDEL.ts @@ -1,10 +1,12 @@ -import { RedisArgument, NumberReply, Command } from '@redis/client/dist/lib/RESP/types'; +import { CommandParser } from '@redis/client/lib/client/parser'; +import { RedisArgument, NumberReply, Command } from '@redis/client/lib/RESP/types'; export default { - FIRST_KEY_INDEX: 1, IS_READ_ONLY: true, - transformArguments(key: RedisArgument, string: RedisArgument) { - return ['FT.SUGDEL', key, string]; + parseCommand(parser: CommandParser, key: RedisArgument, string: RedisArgument) { + parser.push('FT.SUGDEL'); + parser.pushKey(key); + parser.push(string); }, transformReply: undefined as unknown as () => NumberReply<0 | 1> } as const satisfies Command; diff --git a/packages/search/lib/commands/SUGGET.spec.ts b/packages/search/lib/commands/SUGGET.spec.ts index 6ea4c03f325..e30c62afd67 100644 --- a/packages/search/lib/commands/SUGGET.spec.ts +++ b/packages/search/lib/commands/SUGGET.spec.ts @@ -1,26 +1,27 @@ import { strict as assert } from 'node:assert'; import testUtils, { GLOBAL } from '../test-utils'; import SUGGET from './SUGGET'; +import { parseArgs } from '@redis/client/lib/commands/generic-transformers'; describe('FT.SUGGET', () => { describe('transformArguments', () => { it('without options', () => { assert.deepEqual( - SUGGET.transformArguments('key', 'prefix'), + parseArgs(SUGGET, 'key', 'prefix'), ['FT.SUGGET', 'key', 'prefix'] ); }); it('with FUZZY', () => { assert.deepEqual( - SUGGET.transformArguments('key', 'prefix', { FUZZY: true }), + parseArgs(SUGGET, 'key', 'prefix', { FUZZY: true }), ['FT.SUGGET', 'key', 'prefix', 'FUZZY'] ); }); it('with MAX', () => { assert.deepEqual( - SUGGET.transformArguments('key', 'prefix', { MAX: 10 }), + parseArgs(SUGGET, 'key', 'prefix', { MAX: 10 }), ['FT.SUGGET', 'key', 'prefix', 'MAX', '10'] ); }); diff --git a/packages/search/lib/commands/SUGGET.ts b/packages/search/lib/commands/SUGGET.ts index 53dc57a86aa..607e26df94e 100644 --- a/packages/search/lib/commands/SUGGET.ts +++ b/packages/search/lib/commands/SUGGET.ts @@ -1,4 +1,5 @@ -import { NullReply, ArrayReply, BlobStringReply, Command, RedisArgument } from '@redis/client/dist/lib/RESP/types'; +import { CommandParser } from '@redis/client/lib/client/parser'; +import { NullReply, ArrayReply, BlobStringReply, Command, RedisArgument } from '@redis/client/lib/RESP/types'; export interface FtSugGetOptions { FUZZY?: boolean; @@ -6,20 +7,19 @@ export interface FtSugGetOptions { } export default { - FIRST_KEY_INDEX: 1, IS_READ_ONLY: true, - transformArguments(key: RedisArgument, prefix: RedisArgument, options?: FtSugGetOptions) { - const args = ['FT.SUGGET', key, prefix]; + parseCommand(parser: CommandParser, key: RedisArgument, prefix: RedisArgument, options?: FtSugGetOptions) { + parser.push('FT.SUGGET'); + parser.pushKey(key); + parser.push(prefix); if (options?.FUZZY) { - args.push('FUZZY'); + parser.push('FUZZY'); } if (options?.MAX !== undefined) { - args.push('MAX', options.MAX.toString()); + parser.push('MAX', options.MAX.toString()); } - - return args; }, transformReply: undefined as unknown as () => NullReply | ArrayReply } as const satisfies Command; diff --git a/packages/search/lib/commands/SUGGET_WITHPAYLOADS.spec.ts b/packages/search/lib/commands/SUGGET_WITHPAYLOADS.spec.ts index 42a427ce1f4..160d7e3eb7c 100644 --- a/packages/search/lib/commands/SUGGET_WITHPAYLOADS.spec.ts +++ b/packages/search/lib/commands/SUGGET_WITHPAYLOADS.spec.ts @@ -1,11 +1,12 @@ import { strict as assert } from 'node:assert'; import testUtils, { GLOBAL } from '../test-utils'; import SUGGET_WITHPAYLOADS from './SUGGET_WITHPAYLOADS'; +import { parseArgs } from '@redis/client/lib/commands/generic-transformers'; describe('FT.SUGGET WITHPAYLOADS', () => { it('transformArguments', () => { assert.deepEqual( - SUGGET_WITHPAYLOADS.transformArguments('key', 'prefix'), + parseArgs(SUGGET_WITHPAYLOADS, 'key', 'prefix'), ['FT.SUGGET', 'key', 'prefix', 'WITHPAYLOADS'] ); }); diff --git a/packages/search/lib/commands/SUGGET_WITHPAYLOADS.ts b/packages/search/lib/commands/SUGGET_WITHPAYLOADS.ts index d8b097f3dbc..b2112bb4b34 100644 --- a/packages/search/lib/commands/SUGGET_WITHPAYLOADS.ts +++ b/packages/search/lib/commands/SUGGET_WITHPAYLOADS.ts @@ -1,14 +1,12 @@ -import { NullReply, ArrayReply, BlobStringReply, UnwrapReply, Command } from '@redis/client/dist/lib/RESP/types'; -import { isNullReply } from '@redis/client/dist/lib/commands/generic-transformers'; +import { NullReply, ArrayReply, BlobStringReply, UnwrapReply, Command } from '@redis/client/lib/RESP/types'; +import { isNullReply } from '@redis/client/lib/commands/generic-transformers'; import SUGGET from './SUGGET'; export default { - FIRST_KEY_INDEX: SUGGET.FIRST_KEY_INDEX, IS_READ_ONLY: SUGGET.IS_READ_ONLY, - transformArguments(...args: Parameters) { - const transformedArguments = SUGGET.transformArguments(...args); - transformedArguments.push('WITHPAYLOADS'); - return transformedArguments; + parseCommand(...args: Parameters) { + SUGGET.parseCommand(...args); + args[0].push('WITHPAYLOADS'); }, transformReply(reply: NullReply | UnwrapReply>) { if (isNullReply(reply)) return null; diff --git a/packages/search/lib/commands/SUGGET_WITHSCORES.spec.ts b/packages/search/lib/commands/SUGGET_WITHSCORES.spec.ts index 6969be7729d..262defb7933 100644 --- a/packages/search/lib/commands/SUGGET_WITHSCORES.spec.ts +++ b/packages/search/lib/commands/SUGGET_WITHSCORES.spec.ts @@ -1,11 +1,12 @@ import { strict as assert } from 'node:assert'; import testUtils, { GLOBAL } from '../test-utils'; import SUGGET_WITHSCORES from './SUGGET_WITHSCORES'; +import { parseArgs } from '@redis/client/lib/commands/generic-transformers'; describe('FT.SUGGET WITHSCORES', () => { it('transformArguments', () => { assert.deepEqual( - SUGGET_WITHSCORES.transformArguments('key', 'prefix'), + parseArgs(SUGGET_WITHSCORES, 'key', 'prefix'), ['FT.SUGGET', 'key', 'prefix', 'WITHSCORES'] ); }); diff --git a/packages/search/lib/commands/SUGGET_WITHSCORES.ts b/packages/search/lib/commands/SUGGET_WITHSCORES.ts index 9d24d95cbb0..088153c31f5 100644 --- a/packages/search/lib/commands/SUGGET_WITHSCORES.ts +++ b/packages/search/lib/commands/SUGGET_WITHSCORES.ts @@ -1,5 +1,5 @@ -import { NullReply, ArrayReply, BlobStringReply, DoubleReply, UnwrapReply, Command, TypeMapping } from '@redis/client/dist/lib/RESP/types'; -import { isNullReply, transformDoubleReply } from '@redis/client/dist/lib/commands/generic-transformers'; +import { NullReply, ArrayReply, BlobStringReply, DoubleReply, UnwrapReply, Command, TypeMapping } from '@redis/client/lib/RESP/types'; +import { isNullReply, transformDoubleReply } from '@redis/client/lib/commands/generic-transformers'; import SUGGET from './SUGGET'; type SuggestScore = { @@ -8,12 +8,10 @@ type SuggestScore = { } export default { - FIRST_KEY_INDEX: SUGGET.FIRST_KEY_INDEX, IS_READ_ONLY: SUGGET.IS_READ_ONLY, - transformArguments(...args: Parameters) { - const transformedArguments = SUGGET.transformArguments(...args); - transformedArguments.push('WITHSCORES'); - return transformedArguments; + parseCommand(...args: Parameters) { + SUGGET.parseCommand(...args); + args[0].push('WITHSCORES'); }, transformReply: { 2: (reply: NullReply | UnwrapReply>, preserve?: any, typeMapping?: TypeMapping) => { diff --git a/packages/search/lib/commands/SUGGET_WITHSCORES_WITHPAYLOADS.spec.ts b/packages/search/lib/commands/SUGGET_WITHSCORES_WITHPAYLOADS.spec.ts index 98aad1c8028..573708f689e 100644 --- a/packages/search/lib/commands/SUGGET_WITHSCORES_WITHPAYLOADS.spec.ts +++ b/packages/search/lib/commands/SUGGET_WITHSCORES_WITHPAYLOADS.spec.ts @@ -1,11 +1,12 @@ import { strict as assert } from 'node:assert'; import testUtils, { GLOBAL } from '../test-utils'; import SUGGET_WITHSCORES_WITHPAYLOADS from './SUGGET_WITHSCORES_WITHPAYLOADS'; +import { parseArgs } from '@redis/client/lib/commands/generic-transformers'; describe('FT.SUGGET WITHSCORES WITHPAYLOADS', () => { it('transformArguments', () => { assert.deepEqual( - SUGGET_WITHSCORES_WITHPAYLOADS.transformArguments('key', 'prefix'), + parseArgs(SUGGET_WITHSCORES_WITHPAYLOADS, 'key', 'prefix'), ['FT.SUGGET', 'key', 'prefix', 'WITHSCORES', 'WITHPAYLOADS'] ); }); diff --git a/packages/search/lib/commands/SUGGET_WITHSCORES_WITHPAYLOADS.ts b/packages/search/lib/commands/SUGGET_WITHSCORES_WITHPAYLOADS.ts index 1e125eb15fa..6f032a15899 100644 --- a/packages/search/lib/commands/SUGGET_WITHSCORES_WITHPAYLOADS.ts +++ b/packages/search/lib/commands/SUGGET_WITHSCORES_WITHPAYLOADS.ts @@ -1,5 +1,5 @@ -import { NullReply, ArrayReply, BlobStringReply, DoubleReply, UnwrapReply, Command, TypeMapping } from '@redis/client/dist/lib/RESP/types'; -import { isNullReply, transformDoubleReply } from '@redis/client/dist/lib/commands/generic-transformers'; +import { NullReply, ArrayReply, BlobStringReply, DoubleReply, UnwrapReply, Command, TypeMapping } from '@redis/client/lib/RESP/types'; +import { isNullReply, transformDoubleReply } from '@redis/client/lib/commands/generic-transformers'; import SUGGET from './SUGGET'; type SuggestScoreWithPayload = { @@ -9,15 +9,13 @@ type SuggestScoreWithPayload = { } export default { - FIRST_KEY_INDEX: SUGGET.FIRST_KEY_INDEX, IS_READ_ONLY: SUGGET.IS_READ_ONLY, - transformArguments(...args: Parameters) { - const transformedArguments = SUGGET.transformArguments(...args); - transformedArguments.push( + parseCommand(...args: Parameters) { + SUGGET.parseCommand(...args); + args[0].push( 'WITHSCORES', 'WITHPAYLOADS' ); - return transformedArguments; }, transformReply: { 2: (reply: NullReply | UnwrapReply>, preserve?: any, typeMapping?: TypeMapping) => { diff --git a/packages/search/lib/commands/SUGLEN.spec.ts b/packages/search/lib/commands/SUGLEN.spec.ts index 6e6d5e1fc5c..d738f09042e 100644 --- a/packages/search/lib/commands/SUGLEN.spec.ts +++ b/packages/search/lib/commands/SUGLEN.spec.ts @@ -1,11 +1,12 @@ import { strict as assert } from 'node:assert'; import testUtils, { GLOBAL } from '../test-utils'; import SUGLEN from './SUGLEN'; +import { parseArgs } from '@redis/client/lib/commands/generic-transformers'; describe('FT.SUGLEN', () => { it('transformArguments', () => { assert.deepEqual( - SUGLEN.transformArguments('key'), + parseArgs(SUGLEN, 'key'), ['FT.SUGLEN', 'key'] ); }); diff --git a/packages/search/lib/commands/SUGLEN.ts b/packages/search/lib/commands/SUGLEN.ts index 85dde8cfb70..7437559843f 100644 --- a/packages/search/lib/commands/SUGLEN.ts +++ b/packages/search/lib/commands/SUGLEN.ts @@ -1,10 +1,10 @@ -import { RedisArgument, NumberReply, Command } from '@redis/client/dist/lib/RESP/types'; +import { CommandParser } from '@redis/client/lib/client/parser'; +import { RedisArgument, NumberReply, Command } from '@redis/client/lib/RESP/types'; export default { - FIRST_KEY_INDEX: 1, IS_READ_ONLY: true, - transformArguments(key: RedisArgument) { - return ['FT.SUGLEN', key]; + parseCommand(parser: CommandParser, key: RedisArgument) { + parser.push('FT.SUGLEN', key); }, transformReply: undefined as unknown as () => NumberReply } as const satisfies Command; diff --git a/packages/search/lib/commands/SYNDUMP.spec.ts b/packages/search/lib/commands/SYNDUMP.spec.ts index 59c010a8d6d..88bf50cfb54 100644 --- a/packages/search/lib/commands/SYNDUMP.spec.ts +++ b/packages/search/lib/commands/SYNDUMP.spec.ts @@ -2,11 +2,12 @@ import { strict as assert } from 'node:assert'; import testUtils, { GLOBAL } from '../test-utils'; import SYNDUMP from './SYNDUMP'; import { SCHEMA_FIELD_TYPE } from './CREATE'; +import { parseArgs } from '@redis/client/lib/commands/generic-transformers'; describe('FT.SYNDUMP', () => { it('transformArguments', () => { assert.deepEqual( - SYNDUMP.transformArguments('index'), + parseArgs(SYNDUMP, 'index'), ['FT.SYNDUMP', 'index'] ); }); diff --git a/packages/search/lib/commands/SYNDUMP.ts b/packages/search/lib/commands/SYNDUMP.ts index 2fe7540fda5..0c3b68bf2e7 100644 --- a/packages/search/lib/commands/SYNDUMP.ts +++ b/packages/search/lib/commands/SYNDUMP.ts @@ -1,10 +1,11 @@ -import { RedisArgument, MapReply, BlobStringReply, ArrayReply, UnwrapReply, Command } from '@redis/client/dist/lib/RESP/types'; +import { CommandParser } from '@redis/client/lib/client/parser'; +import { RedisArgument, MapReply, BlobStringReply, ArrayReply, UnwrapReply, Command } from '@redis/client/lib/RESP/types'; export default { - FIRST_KEY_INDEX: undefined, + NOT_KEYED_COMMAND: true, IS_READ_ONLY: true, - transformArguments(index: RedisArgument) { - return ['FT.SYNDUMP', index]; + parseCommand(parser: CommandParser, index: RedisArgument) { + parser.push('FT.SYNDUMP', index); }, transformReply: { 2: (reply: UnwrapReply>>) => { diff --git a/packages/search/lib/commands/SYNUPDATE.spec.ts b/packages/search/lib/commands/SYNUPDATE.spec.ts index e901ae9fe3f..f93e0599151 100644 --- a/packages/search/lib/commands/SYNUPDATE.spec.ts +++ b/packages/search/lib/commands/SYNUPDATE.spec.ts @@ -2,26 +2,27 @@ import { strict as assert } from 'node:assert'; import testUtils, { GLOBAL } from '../test-utils'; import SYNUPDATE from './SYNUPDATE'; import { SCHEMA_FIELD_TYPE } from './CREATE'; +import { parseArgs } from '@redis/client/lib/commands/generic-transformers'; describe('FT.SYNUPDATE', () => { describe('transformArguments', () => { it('single term', () => { assert.deepEqual( - SYNUPDATE.transformArguments('index', 'groupId', 'term'), + parseArgs(SYNUPDATE, 'index', 'groupId', 'term'), ['FT.SYNUPDATE', 'index', 'groupId', 'term'] ); }); it('multiple terms', () => { assert.deepEqual( - SYNUPDATE.transformArguments('index', 'groupId', ['1', '2']), + parseArgs(SYNUPDATE, 'index', 'groupId', ['1', '2']), ['FT.SYNUPDATE', 'index', 'groupId', '1', '2'] ); }); it('with SKIPINITIALSCAN', () => { assert.deepEqual( - SYNUPDATE.transformArguments('index', 'groupId', 'term', { + parseArgs(SYNUPDATE, 'index', 'groupId', 'term', { SKIPINITIALSCAN: true }), ['FT.SYNUPDATE', 'index', 'groupId', 'SKIPINITIALSCAN', 'term'] diff --git a/packages/search/lib/commands/SYNUPDATE.ts b/packages/search/lib/commands/SYNUPDATE.ts index 926d8e58e1c..2baf2ded18c 100644 --- a/packages/search/lib/commands/SYNUPDATE.ts +++ b/packages/search/lib/commands/SYNUPDATE.ts @@ -1,26 +1,28 @@ -import { SimpleStringReply, Command, RedisArgument } from '@redis/client/dist/lib/RESP/types'; -import { RedisVariadicArgument, pushVariadicArguments } from '@redis/client/dist/lib/commands/generic-transformers'; +import { CommandParser } from '@redis/client/lib/client/parser'; +import { SimpleStringReply, Command, RedisArgument } from '@redis/client/lib/RESP/types'; +import { RedisVariadicArgument } from '@redis/client/lib/commands/generic-transformers'; export interface FtSynUpdateOptions { SKIPINITIALSCAN?: boolean; } export default { - FIRST_KEY_INDEX: undefined, + NOT_KEYED_COMMAND: true, IS_READ_ONLY: true, - transformArguments( + parseCommand( + parser: CommandParser, index: RedisArgument, groupId: RedisArgument, terms: RedisVariadicArgument, options?: FtSynUpdateOptions ) { - const args = ['FT.SYNUPDATE', index, groupId]; + parser.push('FT.SYNUPDATE', index, groupId); if (options?.SKIPINITIALSCAN) { - args.push('SKIPINITIALSCAN'); + parser.push('SKIPINITIALSCAN'); } - return pushVariadicArguments(args, terms); + parser.pushVariadic(terms); }, transformReply: undefined as unknown as () => SimpleStringReply<'OK'> } as const satisfies Command; diff --git a/packages/search/lib/commands/TAGVALS.spec.ts b/packages/search/lib/commands/TAGVALS.spec.ts index dbc6203f93e..f0d83c9f7ad 100644 --- a/packages/search/lib/commands/TAGVALS.spec.ts +++ b/packages/search/lib/commands/TAGVALS.spec.ts @@ -2,11 +2,12 @@ import { strict as assert } from 'node:assert'; import testUtils, { GLOBAL } from '../test-utils'; import TAGVALS from './TAGVALS'; import { SCHEMA_FIELD_TYPE } from './CREATE'; +import { parseArgs } from '@redis/client/lib/commands/generic-transformers'; describe('FT.TAGVALS', () => { it('transformArguments', () => { assert.deepEqual( - TAGVALS.transformArguments('index', '@field'), + parseArgs(TAGVALS, 'index', '@field'), ['FT.TAGVALS', 'index', '@field'] ); }); diff --git a/packages/search/lib/commands/TAGVALS.ts b/packages/search/lib/commands/TAGVALS.ts index 8a6e73c97b8..d00d657f3ab 100644 --- a/packages/search/lib/commands/TAGVALS.ts +++ b/packages/search/lib/commands/TAGVALS.ts @@ -1,10 +1,11 @@ -import { RedisArgument, ArrayReply, SetReply, BlobStringReply, Command } from '@redis/client/dist/lib/RESP/types'; +import { CommandParser } from '@redis/client/lib/client/parser'; +import { RedisArgument, ArrayReply, SetReply, BlobStringReply, Command } from '@redis/client/lib/RESP/types'; export default { - FIRST_KEY_INDEX: undefined, + NOT_KEYED_COMMAND: true, IS_READ_ONLY: true, - transformArguments(index: RedisArgument, fieldName: RedisArgument) { - return ['FT.TAGVALS', index, fieldName]; + parseCommand(parser: CommandParser, index: RedisArgument, fieldName: RedisArgument) { + parser.push('FT.TAGVALS', index, fieldName); }, transformReply: { 2: undefined as unknown as () => ArrayReply, diff --git a/packages/search/lib/commands/_LIST.spec.ts b/packages/search/lib/commands/_LIST.spec.ts index a7f13b011ac..dfe32f2e29d 100644 --- a/packages/search/lib/commands/_LIST.spec.ts +++ b/packages/search/lib/commands/_LIST.spec.ts @@ -1,11 +1,12 @@ import { strict as assert } from 'node:assert'; import testUtils, { GLOBAL } from '../test-utils'; import _LIST from './_LIST'; +import { parseArgs } from '@redis/client/lib/commands/generic-transformers'; describe('_LIST', () => { it('transformArguments', () => { assert.deepEqual( - _LIST.transformArguments(), + parseArgs(_LIST), ['FT._LIST'] ); }); diff --git a/packages/search/lib/commands/_LIST.ts b/packages/search/lib/commands/_LIST.ts index efb6c31acce..432eea64797 100644 --- a/packages/search/lib/commands/_LIST.ts +++ b/packages/search/lib/commands/_LIST.ts @@ -1,10 +1,11 @@ -import { ArrayReply, SetReply, BlobStringReply, Command } from '@redis/client/dist/lib/RESP/types'; +import { CommandParser } from '@redis/client/lib/client/parser'; +import { ArrayReply, SetReply, BlobStringReply, Command } from '@redis/client/lib/RESP/types'; export default { - FIRST_KEY_INDEX: undefined, + NOT_KEYED_COMMAND: true, IS_READ_ONLY: true, - transformArguments() { - return ['FT._LIST']; + parseCommand(parser: CommandParser) { + parser.push('FT._LIST'); }, transformReply: { 2: undefined as unknown as () => ArrayReply, diff --git a/packages/test-utils/lib/dockers.ts b/packages/test-utils/lib/dockers.ts index a1cb63eb7bf..e8d7ad45ca6 100644 --- a/packages/test-utils/lib/dockers.ts +++ b/packages/test-utils/lib/dockers.ts @@ -2,7 +2,7 @@ import { createConnection } from 'node:net'; import { once } from 'node:events'; import { createClient } from '@redis/client/index'; import { setTimeout } from 'node:timers/promises'; -// import { ClusterSlotsReply } from '@redis/client/dist/lib/commands/CLUSTER_SLOTS'; +// import { ClusterSlotsReply } from '@redis/client/lib/commands/CLUSTER_SLOTS'; import { promisify } from 'node:util'; import { exec } from 'node:child_process'; const execAsync = promisify(exec); diff --git a/packages/time-series/lib/commands/ADD.spec.ts b/packages/time-series/lib/commands/ADD.spec.ts index 7dcf031c2b2..055d2246d8b 100644 --- a/packages/time-series/lib/commands/ADD.spec.ts +++ b/packages/time-series/lib/commands/ADD.spec.ts @@ -2,19 +2,20 @@ import { strict as assert } from 'node:assert'; import testUtils, { GLOBAL } from '../test-utils'; import ADD from './ADD'; import { TIME_SERIES_ENCODING, TIME_SERIES_DUPLICATE_POLICIES } from '.'; +import { parseArgs } from '@redis/client/lib/commands/generic-transformers'; describe('TS.ADD', () => { describe('transformArguments', () => { it('without options', () => { assert.deepEqual( - ADD.transformArguments('key', '*', 1), + parseArgs(ADD, 'key', '*', 1), ['TS.ADD', 'key', '*', '1'] ); }); it('with RETENTION', () => { assert.deepEqual( - ADD.transformArguments('key', '*', 1, { + parseArgs(ADD, 'key', '*', 1, { RETENTION: 1 }), ['TS.ADD', 'key', '*', '1', 'RETENTION', '1'] @@ -23,7 +24,7 @@ describe('TS.ADD', () => { it('with ENCODING', () => { assert.deepEqual( - ADD.transformArguments('key', '*', 1, { + parseArgs(ADD, 'key', '*', 1, { ENCODING: TIME_SERIES_ENCODING.UNCOMPRESSED }), ['TS.ADD', 'key', '*', '1', 'ENCODING', 'UNCOMPRESSED'] @@ -32,7 +33,7 @@ describe('TS.ADD', () => { it('with CHUNK_SIZE', () => { assert.deepEqual( - ADD.transformArguments('key', '*', 1, { + parseArgs(ADD, 'key', '*', 1, { CHUNK_SIZE: 1 }), ['TS.ADD', 'key', '*', '1', 'CHUNK_SIZE', '1'] @@ -41,7 +42,7 @@ describe('TS.ADD', () => { it('with ON_DUPLICATE', () => { assert.deepEqual( - ADD.transformArguments('key', '*', 1, { + parseArgs(ADD, 'key', '*', 1, { ON_DUPLICATE: TIME_SERIES_DUPLICATE_POLICIES.BLOCK }), ['TS.ADD', 'key', '*', '1', 'ON_DUPLICATE', 'BLOCK'] @@ -50,7 +51,7 @@ describe('TS.ADD', () => { it('with LABELS', () => { assert.deepEqual( - ADD.transformArguments('key', '*', 1, { + parseArgs(ADD, 'key', '*', 1, { LABELS: { label: 'value' } }), ['TS.ADD', 'key', '*', '1', 'LABELS', 'label', 'value'] @@ -59,7 +60,7 @@ describe('TS.ADD', () => { it ('with IGNORE', () => { assert.deepEqual( - ADD.transformArguments('key', '*', 1, { + parseArgs(ADD, 'key', '*', 1, { IGNORE: { maxTimeDiff: 1, maxValDiff: 1 @@ -71,7 +72,7 @@ describe('TS.ADD', () => { it('with RETENTION, ENCODING, CHUNK_SIZE, ON_DUPLICATE, LABELS, IGNORE', () => { assert.deepEqual( - ADD.transformArguments('key', '*', 1, { + parseArgs(ADD, 'key', '*', 1, { RETENTION: 1, ENCODING: TIME_SERIES_ENCODING.UNCOMPRESSED, CHUNK_SIZE: 1, diff --git a/packages/time-series/lib/commands/ADD.ts b/packages/time-series/lib/commands/ADD.ts index 1842dcfc346..f79a27c5db7 100644 --- a/packages/time-series/lib/commands/ADD.ts +++ b/packages/time-series/lib/commands/ADD.ts @@ -1,15 +1,16 @@ -import { RedisArgument, NumberReply, Command } from '@redis/client/dist/lib/RESP/types'; +import { CommandParser } from '@redis/client/lib/client/parser'; +import { RedisArgument, NumberReply, Command } from '@redis/client/lib/RESP/types'; import { transformTimestampArgument, - pushRetentionArgument, + parseRetentionArgument, TimeSeriesEncoding, - pushEncodingArgument, - pushChunkSizeArgument, + parseEncodingArgument, + parseChunkSizeArgument, TimeSeriesDuplicatePolicies, Labels, - pushLabelsArgument, + parseLabelsArgument, Timestamp, - pushIgnoreArgument + parseIgnoreArgument } from '.'; export interface TsIgnoreOptions { @@ -27,36 +28,31 @@ export interface TsAddOptions { } export default { - FIRST_KEY_INDEX: 1, IS_READ_ONLY: false, - transformArguments( + parseCommand( + parser: CommandParser, key: RedisArgument, timestamp: Timestamp, value: number, options?: TsAddOptions ) { - const args = [ - 'TS.ADD', - key, - transformTimestampArgument(timestamp), - value.toString() - ]; + parser.push('TS.ADD'); + parser.pushKey(key); + parser.push(transformTimestampArgument(timestamp), value.toString()); - pushRetentionArgument(args, options?.RETENTION); + parseRetentionArgument(parser, options?.RETENTION); - pushEncodingArgument(args, options?.ENCODING); + parseEncodingArgument(parser, options?.ENCODING); - pushChunkSizeArgument(args, options?.CHUNK_SIZE); + parseChunkSizeArgument(parser, options?.CHUNK_SIZE); if (options?.ON_DUPLICATE) { - args.push('ON_DUPLICATE', options.ON_DUPLICATE); + parser.push('ON_DUPLICATE', options.ON_DUPLICATE); } - pushLabelsArgument(args, options?.LABELS); + parseLabelsArgument(parser, options?.LABELS); - pushIgnoreArgument(args, options?.IGNORE); - - return args; + parseIgnoreArgument(parser, options?.IGNORE); }, transformReply: undefined as unknown as () => NumberReply } as const satisfies Command; diff --git a/packages/time-series/lib/commands/ALTER.spec.ts b/packages/time-series/lib/commands/ALTER.spec.ts index 1b24111156b..560d9ffde2c 100644 --- a/packages/time-series/lib/commands/ALTER.spec.ts +++ b/packages/time-series/lib/commands/ALTER.spec.ts @@ -2,19 +2,20 @@ import { strict as assert } from 'node:assert'; import testUtils, { GLOBAL } from '../test-utils'; import ALTER from './ALTER'; import { TIME_SERIES_DUPLICATE_POLICIES } from '.'; +import { parseArgs } from '@redis/client/lib/commands/generic-transformers'; describe('TS.ALTER', () => { describe('transformArguments', () => { it('without options', () => { assert.deepEqual( - ALTER.transformArguments('key'), + parseArgs(ALTER, 'key'), ['TS.ALTER', 'key'] ); }); it('with RETENTION', () => { assert.deepEqual( - ALTER.transformArguments('key', { + parseArgs(ALTER, 'key', { RETENTION: 1 }), ['TS.ALTER', 'key', 'RETENTION', '1'] @@ -23,7 +24,7 @@ describe('TS.ALTER', () => { it('with CHUNK_SIZE', () => { assert.deepEqual( - ALTER.transformArguments('key', { + parseArgs(ALTER, 'key', { CHUNK_SIZE: 1 }), ['TS.ALTER', 'key', 'CHUNK_SIZE', '1'] @@ -32,7 +33,7 @@ describe('TS.ALTER', () => { it('with DUPLICATE_POLICY', () => { assert.deepEqual( - ALTER.transformArguments('key', { + parseArgs(ALTER, 'key', { DUPLICATE_POLICY: TIME_SERIES_DUPLICATE_POLICIES.BLOCK }), ['TS.ALTER', 'key', 'DUPLICATE_POLICY', 'BLOCK'] @@ -41,7 +42,7 @@ describe('TS.ALTER', () => { it('with LABELS', () => { assert.deepEqual( - ALTER.transformArguments('key', { + parseArgs(ALTER, 'key', { LABELS: { label: 'value' } }), ['TS.ALTER', 'key', 'LABELS', 'label', 'value'] @@ -50,7 +51,7 @@ describe('TS.ALTER', () => { it('with IGNORE with MAX_TIME_DIFF', () => { assert.deepEqual( - ALTER.transformArguments('key', { + parseArgs(ALTER, 'key', { IGNORE: { maxTimeDiff: 1, maxValDiff: 1 @@ -62,7 +63,7 @@ describe('TS.ALTER', () => { it('with RETENTION, CHUNK_SIZE, DUPLICATE_POLICY, LABELS, IGNORE', () => { assert.deepEqual( - ALTER.transformArguments('key', { + parseArgs(ALTER, 'key', { RETENTION: 1, CHUNK_SIZE: 1, DUPLICATE_POLICY: TIME_SERIES_DUPLICATE_POLICIES.BLOCK, diff --git a/packages/time-series/lib/commands/ALTER.ts b/packages/time-series/lib/commands/ALTER.ts index f77edb5c43f..8217e81c218 100644 --- a/packages/time-series/lib/commands/ALTER.ts +++ b/packages/time-series/lib/commands/ALTER.ts @@ -1,26 +1,25 @@ -import { RedisArgument, SimpleStringReply, Command } from '@redis/client/dist/lib/RESP/types'; +import { CommandParser } from '@redis/client/lib/client/parser'; +import { RedisArgument, SimpleStringReply, Command } from '@redis/client/lib/RESP/types'; import { TsCreateOptions } from './CREATE'; -import { pushRetentionArgument, pushChunkSizeArgument, pushDuplicatePolicy, pushLabelsArgument, pushIgnoreArgument } from '.'; +import { parseRetentionArgument, parseChunkSizeArgument, parseDuplicatePolicy, parseLabelsArgument, parseIgnoreArgument } from '.'; export type TsAlterOptions = Pick; export default { - FIRST_KEY_INDEX: 1, IS_READ_ONLY: false, - transformArguments(key: RedisArgument, options?: TsAlterOptions) { - const args = ['TS.ALTER', key]; + parseCommand(parser: CommandParser, key: RedisArgument, options?: TsAlterOptions) { + parser.push('TS.ALTER'); + parser.pushKey(key); - pushRetentionArgument(args, options?.RETENTION); + parseRetentionArgument(parser, options?.RETENTION); - pushChunkSizeArgument(args, options?.CHUNK_SIZE); + parseChunkSizeArgument(parser, options?.CHUNK_SIZE); - pushDuplicatePolicy(args, options?.DUPLICATE_POLICY); + parseDuplicatePolicy(parser, options?.DUPLICATE_POLICY); - pushLabelsArgument(args, options?.LABELS); + parseLabelsArgument(parser, options?.LABELS); - pushIgnoreArgument(args, options?.IGNORE); - - return args; + parseIgnoreArgument(parser, options?.IGNORE); }, -transformReply: undefined as unknown as () => SimpleStringReply<'OK'> + transformReply: undefined as unknown as () => SimpleStringReply<'OK'> } as const satisfies Command; diff --git a/packages/time-series/lib/commands/CREATE.spec.ts b/packages/time-series/lib/commands/CREATE.spec.ts index feff9cbdd7b..795b59b880d 100644 --- a/packages/time-series/lib/commands/CREATE.spec.ts +++ b/packages/time-series/lib/commands/CREATE.spec.ts @@ -2,19 +2,20 @@ import { strict as assert } from 'node:assert'; import testUtils, { GLOBAL } from '../test-utils'; import CREATE from './CREATE'; import { TIME_SERIES_ENCODING, TIME_SERIES_DUPLICATE_POLICIES } from '.'; +import { parseArgs } from '@redis/client/lib/commands/generic-transformers'; describe('TS.CREATE', () => { describe('transformArguments', () => { it('without options', () => { assert.deepEqual( - CREATE.transformArguments('key'), + parseArgs(CREATE, 'key'), ['TS.CREATE', 'key'] ); }); it('with RETENTION', () => { assert.deepEqual( - CREATE.transformArguments('key', { + parseArgs(CREATE, 'key', { RETENTION: 1 }), ['TS.CREATE', 'key', 'RETENTION', '1'] @@ -23,7 +24,7 @@ describe('TS.CREATE', () => { it('with ENCODING', () => { assert.deepEqual( - CREATE.transformArguments('key', { + parseArgs(CREATE, 'key', { ENCODING: TIME_SERIES_ENCODING.UNCOMPRESSED }), ['TS.CREATE', 'key', 'ENCODING', 'UNCOMPRESSED'] @@ -32,7 +33,7 @@ describe('TS.CREATE', () => { it('with CHUNK_SIZE', () => { assert.deepEqual( - CREATE.transformArguments('key', { + parseArgs(CREATE, 'key', { CHUNK_SIZE: 1 }), ['TS.CREATE', 'key', 'CHUNK_SIZE', '1'] @@ -41,7 +42,7 @@ describe('TS.CREATE', () => { it('with DUPLICATE_POLICY', () => { assert.deepEqual( - CREATE.transformArguments('key', { + parseArgs(CREATE, 'key', { DUPLICATE_POLICY: TIME_SERIES_DUPLICATE_POLICIES.BLOCK }), ['TS.CREATE', 'key', 'DUPLICATE_POLICY', 'BLOCK'] @@ -50,7 +51,7 @@ describe('TS.CREATE', () => { it('with LABELS', () => { assert.deepEqual( - CREATE.transformArguments('key', { + parseArgs(CREATE, 'key', { LABELS: { label: 'value' } }), ['TS.CREATE', 'key', 'LABELS', 'label', 'value'] @@ -59,7 +60,7 @@ describe('TS.CREATE', () => { it('with IGNORE with MAX_TIME_DIFF', () => { assert.deepEqual( - CREATE.transformArguments('key', { + parseArgs(CREATE, 'key', { IGNORE: { maxTimeDiff: 1, maxValDiff: 1 @@ -71,7 +72,7 @@ describe('TS.CREATE', () => { it('with RETENTION, ENCODING, CHUNK_SIZE, DUPLICATE_POLICY, LABELS, IGNORE', () => { assert.deepEqual( - CREATE.transformArguments('key', { + parseArgs(CREATE, 'key', { RETENTION: 1, ENCODING: TIME_SERIES_ENCODING.UNCOMPRESSED, CHUNK_SIZE: 1, diff --git a/packages/time-series/lib/commands/CREATE.ts b/packages/time-series/lib/commands/CREATE.ts index abb84de12a2..86defd1e0a4 100644 --- a/packages/time-series/lib/commands/CREATE.ts +++ b/packages/time-series/lib/commands/CREATE.ts @@ -1,14 +1,15 @@ -import { RedisArgument, SimpleStringReply, Command } from '@redis/client/dist/lib/RESP/types'; +import { CommandParser } from '@redis/client/lib/client/parser'; +import { RedisArgument, SimpleStringReply, Command } from '@redis/client/lib/RESP/types'; import { - pushRetentionArgument, + parseRetentionArgument, TimeSeriesEncoding, - pushEncodingArgument, - pushChunkSizeArgument, + parseEncodingArgument, + parseChunkSizeArgument, TimeSeriesDuplicatePolicies, - pushDuplicatePolicy, + parseDuplicatePolicy, Labels, - pushLabelsArgument, - pushIgnoreArgument + parseLabelsArgument, + parseIgnoreArgument } from '.'; import { TsIgnoreOptions } from './ADD'; @@ -22,24 +23,22 @@ export interface TsCreateOptions { } export default { - FIRST_KEY_INDEX: 1, IS_READ_ONLY: false, - transformArguments(key: RedisArgument, options?: TsCreateOptions) { - const args = ['TS.CREATE', key]; + parseCommand(parser: CommandParser, key: RedisArgument, options?: TsCreateOptions) { + parser.push('TS.CREATE'); + parser.pushKey(key); - pushRetentionArgument(args, options?.RETENTION); + parseRetentionArgument(parser, options?.RETENTION); - pushEncodingArgument(args, options?.ENCODING); + parseEncodingArgument(parser, options?.ENCODING); - pushChunkSizeArgument(args, options?.CHUNK_SIZE); + parseChunkSizeArgument(parser, options?.CHUNK_SIZE); - pushDuplicatePolicy(args, options?.DUPLICATE_POLICY); + parseDuplicatePolicy(parser, options?.DUPLICATE_POLICY); - pushLabelsArgument(args, options?.LABELS); + parseLabelsArgument(parser, options?.LABELS); - pushIgnoreArgument(args, options?.IGNORE); - - return args; + parseIgnoreArgument(parser, options?.IGNORE); }, transformReply: undefined as unknown as () => SimpleStringReply<'OK'> } as const satisfies Command; diff --git a/packages/time-series/lib/commands/CREATERULE.spec.ts b/packages/time-series/lib/commands/CREATERULE.spec.ts index f1e5b934506..da26bf458e2 100644 --- a/packages/time-series/lib/commands/CREATERULE.spec.ts +++ b/packages/time-series/lib/commands/CREATERULE.spec.ts @@ -1,19 +1,20 @@ import { strict as assert } from 'node:assert'; import testUtils, { GLOBAL } from '../test-utils'; import CREATERULE, { TIME_SERIES_AGGREGATION_TYPE } from './CREATERULE'; +import { parseArgs } from '@redis/client/lib/commands/generic-transformers'; describe('TS.CREATERULE', () => { describe('transformArguments', () => { it('without options', () => { assert.deepEqual( - CREATERULE.transformArguments('source', 'destination', TIME_SERIES_AGGREGATION_TYPE.AVG, 1), + parseArgs(CREATERULE, 'source', 'destination', TIME_SERIES_AGGREGATION_TYPE.AVG, 1), ['TS.CREATERULE', 'source', 'destination', 'AGGREGATION', 'AVG', '1'] ); }); it('with alignTimestamp', () => { assert.deepEqual( - CREATERULE.transformArguments('source', 'destination', TIME_SERIES_AGGREGATION_TYPE.AVG, 1, 1), + parseArgs(CREATERULE, 'source', 'destination', TIME_SERIES_AGGREGATION_TYPE.AVG, 1, 1), ['TS.CREATERULE', 'source', 'destination', 'AGGREGATION', 'AVG', '1', '1'] ); }); diff --git a/packages/time-series/lib/commands/CREATERULE.ts b/packages/time-series/lib/commands/CREATERULE.ts index bd074d7107c..99a8a4c9d57 100644 --- a/packages/time-series/lib/commands/CREATERULE.ts +++ b/packages/time-series/lib/commands/CREATERULE.ts @@ -1,4 +1,5 @@ -import { RedisArgument, SimpleStringReply, Command } from '@redis/client/dist/lib/RESP/types'; +import { CommandParser } from '@redis/client/lib/client/parser'; +import { RedisArgument, SimpleStringReply, Command } from '@redis/client/lib/RESP/types'; export const TIME_SERIES_AGGREGATION_TYPE = { AVG: 'AVG', @@ -19,29 +20,22 @@ export const TIME_SERIES_AGGREGATION_TYPE = { export type TimeSeriesAggregationType = typeof TIME_SERIES_AGGREGATION_TYPE[keyof typeof TIME_SERIES_AGGREGATION_TYPE]; export default { - FIRST_KEY_INDEX: 1, IS_READ_ONLY: false, - transformArguments( + parseCommand( + parser: CommandParser, sourceKey: RedisArgument, destinationKey: RedisArgument, aggregationType: TimeSeriesAggregationType, bucketDuration: number, alignTimestamp?: number ) { - const args = [ - 'TS.CREATERULE', - sourceKey, - destinationKey, - 'AGGREGATION', - aggregationType, - bucketDuration.toString() - ]; + parser.push('TS.CREATERULE'); + parser.pushKeys([sourceKey, destinationKey]); + parser.push('AGGREGATION', aggregationType, bucketDuration.toString()); if (alignTimestamp !== undefined) { - args.push(alignTimestamp.toString()); + parser.push(alignTimestamp.toString()); } - - return args; }, transformReply: undefined as unknown as () => SimpleStringReply<'OK'> } as const satisfies Command; diff --git a/packages/time-series/lib/commands/DECRBY.spec.ts b/packages/time-series/lib/commands/DECRBY.spec.ts index dbce98b2acd..b272ed1614d 100644 --- a/packages/time-series/lib/commands/DECRBY.spec.ts +++ b/packages/time-series/lib/commands/DECRBY.spec.ts @@ -1,19 +1,20 @@ import { strict as assert } from 'node:assert'; import testUtils, { GLOBAL } from '../test-utils'; import DECRBY from './DECRBY'; +import { parseArgs } from '@redis/client/lib/commands/generic-transformers'; describe('TS.DECRBY', () => { describe('transformArguments', () => { it('without options', () => { assert.deepEqual( - DECRBY.transformArguments('key', 1), + parseArgs(DECRBY, 'key', 1), ['TS.DECRBY', 'key', '1'] ); }); it('with TIMESTAMP', () => { assert.deepEqual( - DECRBY.transformArguments('key', 1, { + parseArgs(DECRBY, 'key', 1, { TIMESTAMP: '*' }), ['TS.DECRBY', 'key', '1', 'TIMESTAMP', '*'] @@ -22,7 +23,7 @@ describe('TS.DECRBY', () => { it('with RETENTION', () => { assert.deepEqual( - DECRBY.transformArguments('key', 1, { + parseArgs(DECRBY, 'key', 1, { RETENTION: 1 }), ['TS.DECRBY', 'key', '1', 'RETENTION', '1'] @@ -31,7 +32,7 @@ describe('TS.DECRBY', () => { it('with UNCOMPRESSED', () => { assert.deepEqual( - DECRBY.transformArguments('key', 1, { + parseArgs(DECRBY, 'key', 1, { UNCOMPRESSED: true }), ['TS.DECRBY', 'key', '1', 'UNCOMPRESSED'] @@ -40,7 +41,7 @@ describe('TS.DECRBY', () => { it('with CHUNK_SIZE', () => { assert.deepEqual( - DECRBY.transformArguments('key', 1, { + parseArgs(DECRBY, 'key', 1, { CHUNK_SIZE: 100 }), ['TS.DECRBY', 'key', '1', 'CHUNK_SIZE', '100'] @@ -49,7 +50,7 @@ describe('TS.DECRBY', () => { it('with LABELS', () => { assert.deepEqual( - DECRBY.transformArguments('key', 1, { + parseArgs(DECRBY, 'key', 1, { LABELS: { label: 'value' } }), ['TS.DECRBY', 'key', '1', 'LABELS', 'label', 'value'] @@ -58,7 +59,7 @@ describe('TS.DECRBY', () => { it ('with IGNORE', () => { assert.deepEqual( - DECRBY.transformArguments('key', 1, { + parseArgs(DECRBY, 'key', 1, { IGNORE: { maxTimeDiff: 1, maxValDiff: 1 @@ -70,7 +71,7 @@ describe('TS.DECRBY', () => { it('with TIMESTAMP, RETENTION, UNCOMPRESSED, CHUNK_SIZE and LABELS', () => { assert.deepEqual( - DECRBY.transformArguments('key', 1, { + parseArgs(DECRBY, 'key', 1, { TIMESTAMP: '*', RETENTION: 1, UNCOMPRESSED: true, diff --git a/packages/time-series/lib/commands/DECRBY.ts b/packages/time-series/lib/commands/DECRBY.ts index a5ee01efb06..c2a7e6abd97 100644 --- a/packages/time-series/lib/commands/DECRBY.ts +++ b/packages/time-series/lib/commands/DECRBY.ts @@ -1,9 +1,13 @@ -import { Command } from '@redis/client/dist/lib/RESP/types'; -import INCRBY, { transformIncrByArguments } from './INCRBY'; +import { Command } from '@redis/client/lib/RESP/types'; +import INCRBY, { parseIncrByArguments } from './INCRBY'; export default { - FIRST_KEY_INDEX: INCRBY.FIRST_KEY_INDEX, IS_READ_ONLY: INCRBY.IS_READ_ONLY, - transformArguments: transformIncrByArguments.bind(undefined, 'TS.DECRBY'), + parseCommand(...args: Parameters) { + const parser = args[0]; + + parser.push('TS.DECRBY'); + parseIncrByArguments(...args); + }, transformReply: INCRBY.transformReply } as const satisfies Command; diff --git a/packages/time-series/lib/commands/DEL.spec.ts b/packages/time-series/lib/commands/DEL.spec.ts index afe6be77c4b..07d29ca095e 100644 --- a/packages/time-series/lib/commands/DEL.spec.ts +++ b/packages/time-series/lib/commands/DEL.spec.ts @@ -1,11 +1,12 @@ import { strict as assert } from 'node:assert'; import testUtils, { GLOBAL } from '../test-utils'; import DEL from './DEL'; +import { parseArgs } from '@redis/client/lib/commands/generic-transformers'; describe('TS.DEL', () => { it('transformArguments', () => { assert.deepEqual( - DEL.transformArguments('key', '-', '+'), + parseArgs(DEL, 'key', '-', '+'), ['TS.DEL', 'key', '-', '+'] ); }); diff --git a/packages/time-series/lib/commands/DEL.ts b/packages/time-series/lib/commands/DEL.ts index 26c3e610f17..ad957e6c402 100644 --- a/packages/time-series/lib/commands/DEL.ts +++ b/packages/time-series/lib/commands/DEL.ts @@ -1,16 +1,13 @@ +import { CommandParser } from '@redis/client/lib/client/parser'; import { Timestamp, transformTimestampArgument } from '.'; -import { RedisArgument, NumberReply, Command, } from '@redis/client/dist/lib/RESP/types'; +import { RedisArgument, NumberReply, Command, } from '@redis/client/lib/RESP/types'; export default { - FIRST_KEY_INDEX: 1, IS_READ_ONLY: false, - transformArguments(key: RedisArgument, fromTimestamp: Timestamp, toTimestamp: Timestamp) { - return [ - 'TS.DEL', - key, - transformTimestampArgument(fromTimestamp), - transformTimestampArgument(toTimestamp) - ]; + parseCommand(parser: CommandParser, key: RedisArgument, fromTimestamp: Timestamp, toTimestamp: Timestamp) { + parser.push('TS.DEL'); + parser.pushKey(key); + parser.push(transformTimestampArgument(fromTimestamp), transformTimestampArgument(toTimestamp)); }, transformReply: undefined as unknown as () => NumberReply } as const satisfies Command; diff --git a/packages/time-series/lib/commands/DELETERULE.spec.ts b/packages/time-series/lib/commands/DELETERULE.spec.ts index 8c8568c8567..d7a19a8eaa1 100644 --- a/packages/time-series/lib/commands/DELETERULE.spec.ts +++ b/packages/time-series/lib/commands/DELETERULE.spec.ts @@ -2,11 +2,12 @@ import { strict as assert } from 'node:assert'; import testUtils, { GLOBAL } from '../test-utils'; import DELETERULE from './DELETERULE'; import { TIME_SERIES_AGGREGATION_TYPE } from './CREATERULE'; +import { parseArgs } from '@redis/client/lib/commands/generic-transformers'; describe('TS.DELETERULE', () => { it('transformArguments', () => { assert.deepEqual( - DELETERULE.transformArguments('source', 'destination'), + parseArgs(DELETERULE, 'source', 'destination'), ['TS.DELETERULE', 'source', 'destination'] ); }); diff --git a/packages/time-series/lib/commands/DELETERULE.ts b/packages/time-series/lib/commands/DELETERULE.ts index 5cf88897f7d..8a1aa41385d 100644 --- a/packages/time-series/lib/commands/DELETERULE.ts +++ b/packages/time-series/lib/commands/DELETERULE.ts @@ -1,14 +1,11 @@ -import { RedisArgument, SimpleStringReply, Command } from '@redis/client/dist/lib/RESP/types'; +import { CommandParser } from '@redis/client/lib/client/parser'; +import { RedisArgument, SimpleStringReply, Command } from '@redis/client/lib/RESP/types'; export default { - FIRST_KEY_INDEX: 1, IS_READ_ONLY: false, - transformArguments(sourceKey: RedisArgument, destinationKey: RedisArgument) { - return [ - 'TS.DELETERULE', - sourceKey, - destinationKey - ]; + parseCommand(parser: CommandParser, sourceKey: RedisArgument, destinationKey: RedisArgument) { + parser.push('TS.DELETERULE'); + parser.pushKeys([sourceKey, destinationKey]); }, transformReply: undefined as unknown as () => SimpleStringReply<'OK'> } as const satisfies Command; diff --git a/packages/time-series/lib/commands/GET.spec.ts b/packages/time-series/lib/commands/GET.spec.ts index a1f47346bc2..836a1b638af 100644 --- a/packages/time-series/lib/commands/GET.spec.ts +++ b/packages/time-series/lib/commands/GET.spec.ts @@ -1,19 +1,20 @@ import { strict as assert } from 'node:assert'; import testUtils, { GLOBAL } from '../test-utils'; import GET from './GET'; +import { parseArgs } from '@redis/client/lib/commands/generic-transformers'; describe('TS.GET', () => { describe('transformArguments', () => { it('without options', () => { assert.deepEqual( - GET.transformArguments('key'), + parseArgs(GET, 'key'), ['TS.GET', 'key'] ); }); it('with LATEST', () => { assert.deepEqual( - GET.transformArguments('key', { + parseArgs(GET, 'key', { LATEST: true }), ['TS.GET', 'key', 'LATEST'] diff --git a/packages/time-series/lib/commands/GET.ts b/packages/time-series/lib/commands/GET.ts index 78e5e3bced0..9f165bed6eb 100644 --- a/packages/time-series/lib/commands/GET.ts +++ b/packages/time-series/lib/commands/GET.ts @@ -1,4 +1,5 @@ -import { RedisArgument, TuplesReply, NumberReply, DoubleReply, UnwrapReply, Resp2Reply, Command } from '@redis/client/dist/lib/RESP/types'; +import { CommandParser } from '@redis/client/lib/client/parser'; +import { RedisArgument, TuplesReply, NumberReply, DoubleReply, UnwrapReply, Resp2Reply, Command } from '@redis/client/lib/RESP/types'; export interface TsGetOptions { LATEST?: boolean; @@ -7,16 +8,14 @@ export interface TsGetOptions { export type TsGetReply = TuplesReply<[]> | TuplesReply<[NumberReply, DoubleReply]>; export default { - FIRST_KEY_INDEX: 1, IS_READ_ONLY: true, - transformArguments(key: RedisArgument, options?: TsGetOptions) { - const args = ['TS.GET', key]; + parseCommand(parser: CommandParser, key: RedisArgument, options?: TsGetOptions) { + parser.push('TS.GET'); + parser.pushKey(key); if (options?.LATEST) { - args.push('LATEST'); + parser.push('LATEST'); } - - return args; }, transformReply: { 2(reply: UnwrapReply>) { diff --git a/packages/time-series/lib/commands/INCRBY.spec.ts b/packages/time-series/lib/commands/INCRBY.spec.ts index 33163a72c82..5d005952b30 100644 --- a/packages/time-series/lib/commands/INCRBY.spec.ts +++ b/packages/time-series/lib/commands/INCRBY.spec.ts @@ -1,19 +1,20 @@ import { strict as assert } from 'node:assert'; import testUtils, { GLOBAL } from '../test-utils'; import INCRBY from './INCRBY'; +import { parseArgs } from '@redis/client/lib/commands/generic-transformers'; describe('TS.INCRBY', () => { describe('transformArguments', () => { it('without options', () => { assert.deepEqual( - INCRBY.transformArguments('key', 1), + parseArgs(INCRBY, 'key', 1), ['TS.INCRBY', 'key', '1'] ); }); it('with TIMESTAMP', () => { assert.deepEqual( - INCRBY.transformArguments('key', 1, { + parseArgs(INCRBY, 'key', 1, { TIMESTAMP: '*' }), ['TS.INCRBY', 'key', '1', 'TIMESTAMP', '*'] @@ -22,7 +23,7 @@ describe('TS.INCRBY', () => { it('with RETENTION', () => { assert.deepEqual( - INCRBY.transformArguments('key', 1, { + parseArgs(INCRBY, 'key', 1, { RETENTION: 1 }), ['TS.INCRBY', 'key', '1', 'RETENTION', '1'] @@ -31,7 +32,7 @@ describe('TS.INCRBY', () => { it('with UNCOMPRESSED', () => { assert.deepEqual( - INCRBY.transformArguments('key', 1, { + parseArgs(INCRBY, 'key', 1, { UNCOMPRESSED: true }), ['TS.INCRBY', 'key', '1', 'UNCOMPRESSED'] @@ -40,7 +41,7 @@ describe('TS.INCRBY', () => { it('without UNCOMPRESSED', () => { assert.deepEqual( - INCRBY.transformArguments('key', 1, { + parseArgs(INCRBY, 'key', 1, { UNCOMPRESSED: false }), ['TS.INCRBY', 'key', '1'] @@ -49,7 +50,7 @@ describe('TS.INCRBY', () => { it('with CHUNK_SIZE', () => { assert.deepEqual( - INCRBY.transformArguments('key', 1, { + parseArgs(INCRBY, 'key', 1, { CHUNK_SIZE: 1 }), ['TS.INCRBY', 'key', '1', 'CHUNK_SIZE', '1'] @@ -58,7 +59,7 @@ describe('TS.INCRBY', () => { it('with LABELS', () => { assert.deepEqual( - INCRBY.transformArguments('key', 1, { + parseArgs(INCRBY, 'key', 1, { LABELS: { label: 'value' } }), ['TS.INCRBY', 'key', '1', 'LABELS', 'label', 'value'] @@ -67,7 +68,7 @@ describe('TS.INCRBY', () => { it ('with IGNORE', () => { assert.deepEqual( - INCRBY.transformArguments('key', 1, { + parseArgs(INCRBY, 'key', 1, { IGNORE: { maxTimeDiff: 1, maxValDiff: 1 @@ -79,7 +80,7 @@ describe('TS.INCRBY', () => { it('with TIMESTAMP, RETENTION, UNCOMPRESSED, CHUNK_SIZE and LABELS', () => { assert.deepEqual( - INCRBY.transformArguments('key', 1, { + parseArgs(INCRBY, 'key', 1, { TIMESTAMP: '*', RETENTION: 1, UNCOMPRESSED: true, diff --git a/packages/time-series/lib/commands/INCRBY.ts b/packages/time-series/lib/commands/INCRBY.ts index 3160d3906d3..7dbdb6b9ead 100644 --- a/packages/time-series/lib/commands/INCRBY.ts +++ b/packages/time-series/lib/commands/INCRBY.ts @@ -1,5 +1,6 @@ -import { RedisArgument, NumberReply, Command } from '@redis/client/dist/lib/RESP/types'; -import { Timestamp, transformTimestampArgument, pushRetentionArgument, pushChunkSizeArgument, Labels, pushLabelsArgument, pushIgnoreArgument } from '.'; +import { CommandParser } from '@redis/client/lib/client/parser'; +import { RedisArgument, NumberReply, Command } from '@redis/client/lib/RESP/types'; +import { Timestamp, transformTimestampArgument, parseRetentionArgument, parseChunkSizeArgument, Labels, parseLabelsArgument, parseIgnoreArgument } from '.'; import { TsIgnoreOptions } from './ADD'; export interface TsIncrByOptions { @@ -11,40 +12,39 @@ export interface TsIncrByOptions { IGNORE?: TsIgnoreOptions; } -export function transformIncrByArguments( - command: RedisArgument, +export function parseIncrByArguments( + parser: CommandParser, key: RedisArgument, value: number, options?: TsIncrByOptions ) { - const args = [ - command, - key, - value.toString() - ]; + parser.pushKey(key); + parser.push(value.toString()); if (options?.TIMESTAMP !== undefined && options?.TIMESTAMP !== null) { - args.push('TIMESTAMP', transformTimestampArgument(options.TIMESTAMP)); + parser.push('TIMESTAMP', transformTimestampArgument(options.TIMESTAMP)); } - pushRetentionArgument(args, options?.RETENTION); + parseRetentionArgument(parser, options?.RETENTION); if (options?.UNCOMPRESSED) { - args.push('UNCOMPRESSED'); + parser.push('UNCOMPRESSED'); } - pushChunkSizeArgument(args, options?.CHUNK_SIZE); + parseChunkSizeArgument(parser, options?.CHUNK_SIZE); - pushLabelsArgument(args, options?.LABELS); + parseLabelsArgument(parser, options?.LABELS); - pushIgnoreArgument(args, options?.IGNORE); - - return args; + parseIgnoreArgument(parser, options?.IGNORE); } export default { - FIRST_KEY_INDEX: 1, IS_READ_ONLY: false, - transformArguments: transformIncrByArguments.bind(undefined, 'TS.INCRBY'), + parseCommand(...args: Parameters) { + const parser = args[0]; + + parser.push('TS.INCRBY'); + parseIncrByArguments(...args); + }, transformReply: undefined as unknown as () => NumberReply } as const satisfies Command; diff --git a/packages/time-series/lib/commands/INFO.spec.ts b/packages/time-series/lib/commands/INFO.spec.ts index e4295b80fa4..73b9d8dc930 100644 --- a/packages/time-series/lib/commands/INFO.spec.ts +++ b/packages/time-series/lib/commands/INFO.spec.ts @@ -3,11 +3,12 @@ import { TIME_SERIES_DUPLICATE_POLICIES } from '.'; import testUtils, { GLOBAL } from '../test-utils'; import INFO, { InfoReply } from './INFO'; import { TIME_SERIES_AGGREGATION_TYPE } from './CREATERULE'; +import { parseArgs } from '@redis/client/lib/commands/generic-transformers'; describe('TS.INFO', () => { it('transformArguments', () => { assert.deepEqual( - INFO.transformArguments('key'), + parseArgs(INFO, 'key'), ['TS.INFO', 'key'] ); }); diff --git a/packages/time-series/lib/commands/INFO.ts b/packages/time-series/lib/commands/INFO.ts index 91d4e4bbad3..fbd66875b1f 100644 --- a/packages/time-series/lib/commands/INFO.ts +++ b/packages/time-series/lib/commands/INFO.ts @@ -1,3 +1,4 @@ +import { CommandParser } from '@redis/client/lib/client/parser'; import { ArrayReply, BlobStringReply, Command, DoubleReply, NumberReply, ReplyUnion, SimpleStringReply, TypeMapping } from "@redis/client/lib/RESP/types"; import { TimeSeriesDuplicatePolicies } from "."; import { TimeSeriesAggregationType } from "./CREATERULE"; @@ -71,10 +72,10 @@ export interface InfoReply { } export default { - FIRST_KEY_INDEX: 1, IS_READ_ONLY: true, - transformArguments(key: string) { - return ['TS.INFO', key]; + parseCommand(parser: CommandParser, key: string) { + parser.push('TS.INFO'); + parser.pushKey(key); }, transformReply: { 2: (reply: InfoRawReply, _, typeMapping?: TypeMapping): InfoReply => { @@ -125,4 +126,4 @@ export default { 3: undefined as unknown as () => ReplyUnion }, unstableResp3: true - } as const satisfies Command; \ No newline at end of file + } as const satisfies Command; diff --git a/packages/time-series/lib/commands/INFO_DEBUG.spec.ts b/packages/time-series/lib/commands/INFO_DEBUG.spec.ts index 674f91c60a7..063b9126550 100644 --- a/packages/time-series/lib/commands/INFO_DEBUG.spec.ts +++ b/packages/time-series/lib/commands/INFO_DEBUG.spec.ts @@ -4,11 +4,12 @@ import testUtils, { GLOBAL } from '../test-utils'; import { assertInfo } from './INFO.spec'; import INFO_DEBUG from './INFO_DEBUG'; import { TIME_SERIES_AGGREGATION_TYPE } from './CREATERULE'; +import { parseArgs } from '@redis/client/lib/commands/generic-transformers'; describe('TS.INFO_DEBUG', () => { it('transformArguments', () => { assert.deepEqual( - INFO_DEBUG.transformArguments('key'), + parseArgs(INFO_DEBUG, 'key'), ['TS.INFO', 'key', 'DEBUG'] ); }); diff --git a/packages/time-series/lib/commands/INFO_DEBUG.ts b/packages/time-series/lib/commands/INFO_DEBUG.ts index fb2b28b8072..bee1147f2bb 100644 --- a/packages/time-series/lib/commands/INFO_DEBUG.ts +++ b/packages/time-series/lib/commands/INFO_DEBUG.ts @@ -1,6 +1,6 @@ -import { BlobStringReply, Command, NumberReply, SimpleStringReply, TypeMapping } from "@redis/client/lib/RESP/types"; +import { CommandParser } from '@redis/client/lib/client/parser'; +import { BlobStringReply, Command, NumberReply, SimpleStringReply, TypeMapping, ReplyUnion } from "@redis/client/lib/RESP/types"; import INFO, { InfoRawReply, InfoRawReplyTypes, InfoReply } from "./INFO"; -import { ReplyUnion } from '@redis/client/lib/RESP/types'; type chunkType = Array<[ 'startTimestamp', @@ -37,12 +37,10 @@ export interface InfoDebugReply extends InfoReply { } export default { - FIRST_KEY_INDEX: INFO.FIRST_KEY_INDEX, IS_READ_ONLY: INFO.IS_READ_ONLY, - transformArguments(key: string) { - const args = INFO.transformArguments(key); - args.push('DEBUG'); - return args; + parseCommand(parser: CommandParser, key: string) { + INFO.parseCommand(parser, key); + parser.push('DEBUG'); }, transformReply: { 2: (reply: InfoDebugRawReply, _, typeMapping?: TypeMapping): InfoDebugReply => { @@ -76,4 +74,4 @@ export default { 3: undefined as unknown as () => ReplyUnion }, unstableResp3: true -} as const satisfies Command; \ No newline at end of file +} as const satisfies Command; diff --git a/packages/time-series/lib/commands/MADD.spec.ts b/packages/time-series/lib/commands/MADD.spec.ts index bbe358e5438..8bf8e27fdb3 100644 --- a/packages/time-series/lib/commands/MADD.spec.ts +++ b/packages/time-series/lib/commands/MADD.spec.ts @@ -2,11 +2,12 @@ import { strict as assert } from 'node:assert'; import testUtils, { GLOBAL } from '../test-utils'; import MADD from './MADD'; import { SimpleError } from '@redis/client/lib/errors'; +import { parseArgs } from '@redis/client/lib/commands/generic-transformers'; describe('TS.MADD', () => { it('transformArguments', () => { assert.deepEqual( - MADD.transformArguments([{ + parseArgs(MADD, [{ key: '1', timestamp: 0, value: 0 diff --git a/packages/time-series/lib/commands/MADD.ts b/packages/time-series/lib/commands/MADD.ts index 59c1ed59bdb..cb1f077055a 100644 --- a/packages/time-series/lib/commands/MADD.ts +++ b/packages/time-series/lib/commands/MADD.ts @@ -1,5 +1,6 @@ +import { CommandParser } from '@redis/client/lib/client/parser'; import { Timestamp, transformTimestampArgument } from '.'; -import { ArrayReply, NumberReply, SimpleErrorReply, Command } from '@redis/client/dist/lib/RESP/types'; +import { ArrayReply, NumberReply, SimpleErrorReply, Command } from '@redis/client/lib/RESP/types'; export interface TsMAddSample { key: string; @@ -8,20 +9,14 @@ export interface TsMAddSample { } export default { - FIRST_KEY_INDEX: 1, IS_READ_ONLY: false, - transformArguments(toAdd: Array) { - const args = ['TS.MADD']; + parseCommand(parser: CommandParser, toAdd: Array) { + parser.push('TS.MADD'); for (const { key, timestamp, value } of toAdd) { - args.push( - key, - transformTimestampArgument(timestamp), - value.toString() - ); + parser.pushKey(key); + parser.push(transformTimestampArgument(timestamp), value.toString()); } - - return args; }, transformReply: undefined as unknown as () => ArrayReply } as const satisfies Command; diff --git a/packages/time-series/lib/commands/MGET.spec.ts b/packages/time-series/lib/commands/MGET.spec.ts index b2de0486cfe..ba2e571be49 100644 --- a/packages/time-series/lib/commands/MGET.spec.ts +++ b/packages/time-series/lib/commands/MGET.spec.ts @@ -1,19 +1,20 @@ import { strict as assert } from 'node:assert'; import testUtils, { GLOBAL } from '../test-utils'; import MGET from './MGET'; +import { parseArgs } from '@redis/client/lib/commands/generic-transformers'; describe('TS.MGET', () => { describe('transformArguments', () => { it('without options', () => { assert.deepEqual( - MGET.transformArguments('label=value'), + parseArgs(MGET, 'label=value'), ['TS.MGET', 'FILTER', 'label=value'] ); }); it('with LATEST', () => { assert.deepEqual( - MGET.transformArguments('label=value', { + parseArgs(MGET, 'label=value', { LATEST: true }), ['TS.MGET', 'LATEST', 'FILTER', 'label=value'] diff --git a/packages/time-series/lib/commands/MGET.ts b/packages/time-series/lib/commands/MGET.ts index 2b04b29589b..add742a70d5 100644 --- a/packages/time-series/lib/commands/MGET.ts +++ b/packages/time-series/lib/commands/MGET.ts @@ -1,22 +1,21 @@ -import { CommandArguments, Command, BlobStringReply, ArrayReply, Resp2Reply, MapReply, TuplesReply, TypeMapping } from '@redis/client/dist/lib/RESP/types'; -import { RedisVariadicArgument, pushVariadicArguments } from '@redis/client/dist/lib/commands/generic-transformers'; +import { CommandParser } from '@redis/client/lib/client/parser'; +import { Command, BlobStringReply, ArrayReply, Resp2Reply, MapReply, TuplesReply, TypeMapping } from '@redis/client/lib/RESP/types'; import { resp2MapToValue, resp3MapToValue, SampleRawReply, transformSampleReply } from '.'; +import { RedisVariadicArgument } from '@redis/client/lib/commands/generic-transformers'; export interface TsMGetOptions { LATEST?: boolean; } -export function pushLatestArgument(args: CommandArguments, latest?: boolean) { +export function parseLatestArgument(parser: CommandParser, latest?: boolean) { if (latest) { - args.push('LATEST'); + parser.push('LATEST'); } - - return args; } -export function pushFilterArgument(args: CommandArguments, filter: RedisVariadicArgument) { - args.push('FILTER'); - return pushVariadicArguments(args, filter); +export function parseFilterArgument(parser: CommandParser, filter: RedisVariadicArgument) { + parser.push('FILTER'); + parser.pushVariadic(filter); } export type MGetRawReply2 = ArrayReply< @@ -36,11 +35,12 @@ export type MGetRawReply3 = MapReply< >; export default { - FIRST_KEY_INDEX: undefined, + NOT_KEYED_COMMAND: true, IS_READ_ONLY: true, - transformArguments(filter: RedisVariadicArgument, options?: TsMGetOptions) { - const args = pushLatestArgument(['TS.MGET'], options?.LATEST); - return pushFilterArgument(args, filter); + parseCommand(parser: CommandParser, filter: RedisVariadicArgument, options?: TsMGetOptions) { + parser.push('TS.MGET'); + parseLatestArgument(parser, options?.LATEST); + parseFilterArgument(parser, filter); }, transformReply: { 2(reply: MGetRawReply2, _, typeMapping?: TypeMapping) { diff --git a/packages/time-series/lib/commands/MGET_SELECTED_LABELS.spec.ts b/packages/time-series/lib/commands/MGET_SELECTED_LABELS.spec.ts index d9820027bb9..d79c463fc7d 100644 --- a/packages/time-series/lib/commands/MGET_SELECTED_LABELS.spec.ts +++ b/packages/time-series/lib/commands/MGET_SELECTED_LABELS.spec.ts @@ -1,11 +1,12 @@ import { strict as assert } from 'node:assert'; import testUtils, { GLOBAL } from '../test-utils'; import MGET_SELECTED_LABELS from './MGET_SELECTED_LABELS'; +import { parseArgs } from '@redis/client/lib/commands/generic-transformers'; describe('TS.MGET_SELECTED_LABELS', () => { it('transformArguments', () => { assert.deepEqual( - MGET_SELECTED_LABELS.transformArguments('label=value', 'label'), + parseArgs(MGET_SELECTED_LABELS, 'label=value', 'label'), ['TS.MGET', 'SELECTED_LABELS', 'label', 'FILTER', 'label=value'] ); }); diff --git a/packages/time-series/lib/commands/MGET_SELECTED_LABELS.ts b/packages/time-series/lib/commands/MGET_SELECTED_LABELS.ts index d132972d879..67c7dc79600 100644 --- a/packages/time-series/lib/commands/MGET_SELECTED_LABELS.ts +++ b/packages/time-series/lib/commands/MGET_SELECTED_LABELS.ts @@ -1,16 +1,17 @@ -import { Command, BlobStringReply, NullReply } from '@redis/client/dist/lib/RESP/types'; -import { RedisVariadicArgument } from '@redis/client/dist/lib/commands/generic-transformers'; -import { TsMGetOptions, pushLatestArgument, pushFilterArgument } from './MGET'; -import { pushSelectedLabelsArguments } from '.'; +import { CommandParser } from '@redis/client/lib/client/parser'; +import { Command, BlobStringReply, NullReply } from '@redis/client/lib/RESP/types'; +import { RedisVariadicArgument } from '@redis/client/lib/commands/generic-transformers'; +import { TsMGetOptions, parseLatestArgument, parseFilterArgument } from './MGET'; +import { parseSelectedLabelsArguments } from '.'; import { createTransformMGetLabelsReply } from './MGET_WITHLABELS'; export default { - FIRST_KEY_INDEX: undefined, IS_READ_ONLY: true, - transformArguments(filter: RedisVariadicArgument, selectedLabels: RedisVariadicArgument, options?: TsMGetOptions) { - let args = pushLatestArgument(['TS.MGET'], options?.LATEST); - args = pushSelectedLabelsArguments(args, selectedLabels); - return pushFilterArgument(args, filter); + parseCommand(parser: CommandParser, filter: RedisVariadicArgument, selectedLabels: RedisVariadicArgument, options?: TsMGetOptions) { + parser.push('TS.MGET'); + parseLatestArgument(parser, options?.LATEST); + parseSelectedLabelsArguments(parser, selectedLabels); + parseFilterArgument(parser, filter); }, transformReply: createTransformMGetLabelsReply(), } as const satisfies Command; diff --git a/packages/time-series/lib/commands/MGET_WITHLABELS.spec.ts b/packages/time-series/lib/commands/MGET_WITHLABELS.spec.ts index d3e51d2cab6..33fc5308444 100644 --- a/packages/time-series/lib/commands/MGET_WITHLABELS.spec.ts +++ b/packages/time-series/lib/commands/MGET_WITHLABELS.spec.ts @@ -1,11 +1,12 @@ import { strict as assert } from 'node:assert'; import testUtils, { GLOBAL } from '../test-utils'; import MGET_WITHLABELS from './MGET_WITHLABELS'; +import { parseArgs } from '@redis/client/lib/commands/generic-transformers'; describe('TS.MGET_WITHLABELS', () => { it('transformArguments', () => { assert.deepEqual( - MGET_WITHLABELS.transformArguments('label=value'), + parseArgs(MGET_WITHLABELS, 'label=value'), ['TS.MGET', 'WITHLABELS', 'FILTER', 'label=value'] ); }); diff --git a/packages/time-series/lib/commands/MGET_WITHLABELS.ts b/packages/time-series/lib/commands/MGET_WITHLABELS.ts index 679a536f2ab..f6d50c91b14 100644 --- a/packages/time-series/lib/commands/MGET_WITHLABELS.ts +++ b/packages/time-series/lib/commands/MGET_WITHLABELS.ts @@ -1,6 +1,7 @@ -import { Command, BlobStringReply, ArrayReply, Resp2Reply, MapReply, TuplesReply, TypeMapping } from '@redis/client/dist/lib/RESP/types'; -import { RedisVariadicArgument } from '@redis/client/dist/lib/commands/generic-transformers'; -import { TsMGetOptions, pushLatestArgument, pushFilterArgument } from './MGET'; +import { CommandParser } from '@redis/client/lib/client/parser'; +import { Command, BlobStringReply, ArrayReply, Resp2Reply, MapReply, TuplesReply, TypeMapping } from '@redis/client/lib/RESP/types'; +import { RedisVariadicArgument } from '@redis/client/lib/commands/generic-transformers'; +import { TsMGetOptions, parseLatestArgument, parseFilterArgument } from './MGET'; import { RawLabelValue, resp2MapToValue, resp3MapToValue, SampleRawReply, transformRESP2Labels, transformSampleReply } from '.'; export interface TsMGetWithLabelsOptions extends TsMGetOptions { @@ -50,12 +51,12 @@ export function createTransformMGetLabelsReply() { } export default { - FIRST_KEY_INDEX: undefined, IS_READ_ONLY: true, - transformArguments(filter: RedisVariadicArgument, options?: TsMGetOptions) { - const args = pushLatestArgument(['TS.MGET'], options?.LATEST); - args.push('WITHLABELS'); - return pushFilterArgument(args, filter); + parseCommand(parser: CommandParser, filter: RedisVariadicArgument, options?: TsMGetWithLabelsOptions) { + parser.push('TS.MGET'); + parseLatestArgument(parser, options?.LATEST); + parser.push('WITHLABELS'); + parseFilterArgument(parser, filter); }, transformReply: createTransformMGetLabelsReply(), } as const satisfies Command; diff --git a/packages/time-series/lib/commands/MRANGE.spec.ts b/packages/time-series/lib/commands/MRANGE.spec.ts index 9d41763eb02..94c8e72983a 100644 --- a/packages/time-series/lib/commands/MRANGE.spec.ts +++ b/packages/time-series/lib/commands/MRANGE.spec.ts @@ -2,11 +2,12 @@ import { strict as assert } from 'node:assert'; import testUtils, { GLOBAL } from '../test-utils'; import MRANGE from './MRANGE'; import { TIME_SERIES_AGGREGATION_TYPE } from './CREATERULE'; +import { parseArgs } from '@redis/client/lib/commands/generic-transformers'; describe('TS.MRANGE', () => { it('transformArguments', () => { assert.deepEqual( - MRANGE.transformArguments('-', '+', 'label=value', { + parseArgs(MRANGE, '-', '+', 'label=value', { LATEST: true, FILTER_BY_TS: [0], FILTER_BY_VALUE: { diff --git a/packages/time-series/lib/commands/MRANGE.ts b/packages/time-series/lib/commands/MRANGE.ts index bbc93a70dad..dbe48d6f542 100644 --- a/packages/time-series/lib/commands/MRANGE.ts +++ b/packages/time-series/lib/commands/MRANGE.ts @@ -1,8 +1,9 @@ -import { Command, ArrayReply, BlobStringReply, Resp2Reply, MapReply, TuplesReply, TypeMapping, RedisArgument } from '@redis/client/dist/lib/RESP/types'; -import { RedisVariadicArgument } from '@redis/client/dist/lib/commands/generic-transformers'; +import { CommandParser } from '@redis/client/lib/client/parser'; +import { Command, ArrayReply, BlobStringReply, Resp2Reply, MapReply, TuplesReply, TypeMapping, RedisArgument } from '@redis/client/lib/RESP/types'; +import { RedisVariadicArgument } from '@redis/client/lib/commands/generic-transformers'; import { resp2MapToValue, resp3MapToValue, SampleRawReply, Timestamp, transformSamplesReply } from '.'; -import { TsRangeOptions, pushRangeArguments } from './RANGE'; -import { pushFilterArgument } from './MGET'; +import { TsRangeOptions, parseRangeArguments } from './RANGE'; +import { parseFilterArgument } from './MGET'; export type TsMRangeRawReply2 = ArrayReply< TuplesReply<[ @@ -23,26 +24,28 @@ export type TsMRangeRawReply3 = MapReply< export function createTransformMRangeArguments(command: RedisArgument) { return ( + parser: CommandParser, fromTimestamp: Timestamp, toTimestamp: Timestamp, filter: RedisVariadicArgument, options?: TsRangeOptions ) => { - const args = pushRangeArguments( - [command], + parser.push(command); + parseRangeArguments( + parser, fromTimestamp, toTimestamp, options ); - return pushFilterArgument(args, filter); + parseFilterArgument(parser, filter); }; } export default { - FIRST_KEY_INDEX: undefined, + NOT_KEYED_COMMAND: true, IS_READ_ONLY: true, - transformArguments: createTransformMRangeArguments('TS.MRANGE'), + parseCommand: createTransformMRangeArguments('TS.MRANGE'), transformReply: { 2(reply: TsMRangeRawReply2, _?: any, typeMapping?: TypeMapping) { return resp2MapToValue(reply, ([_key, _labels, samples]) => { diff --git a/packages/time-series/lib/commands/MRANGE_GROUPBY.spec.ts b/packages/time-series/lib/commands/MRANGE_GROUPBY.spec.ts index c0d05425ff4..bcdde20fe98 100644 --- a/packages/time-series/lib/commands/MRANGE_GROUPBY.spec.ts +++ b/packages/time-series/lib/commands/MRANGE_GROUPBY.spec.ts @@ -2,11 +2,12 @@ import { strict as assert } from 'node:assert'; import testUtils, { GLOBAL } from '../test-utils'; import MRANGE_GROUPBY, { TIME_SERIES_REDUCERS } from './MRANGE_GROUPBY'; import { TIME_SERIES_AGGREGATION_TYPE } from './CREATERULE'; +import { parseArgs } from '@redis/client/lib/commands/generic-transformers'; describe('TS.MRANGE_GROUPBY', () => { it('transformArguments', () => { assert.deepEqual( - MRANGE_GROUPBY.transformArguments('-', '+', 'label=value', { + parseArgs(MRANGE_GROUPBY, '-', '+', 'label=value', { REDUCE: TIME_SERIES_REDUCERS.AVG, label: 'label' }, { diff --git a/packages/time-series/lib/commands/MRANGE_GROUPBY.ts b/packages/time-series/lib/commands/MRANGE_GROUPBY.ts index 3b4e94eac20..0d996521d1c 100644 --- a/packages/time-series/lib/commands/MRANGE_GROUPBY.ts +++ b/packages/time-series/lib/commands/MRANGE_GROUPBY.ts @@ -1,8 +1,9 @@ -import { Command, ArrayReply, BlobStringReply, Resp2Reply, MapReply, TuplesReply, TypeMapping, RedisArgument, TuplesToMapReply, UnwrapReply } from '@redis/client/dist/lib/RESP/types'; -import { RedisVariadicArgument } from '@redis/client/dist/lib/commands/generic-transformers'; +import { CommandParser } from '@redis/client/lib/client/parser'; +import { Command, ArrayReply, BlobStringReply, Resp2Reply, MapReply, TuplesReply, TypeMapping, RedisArgument, TuplesToMapReply, UnwrapReply } from '@redis/client/lib/RESP/types'; +import { RedisVariadicArgument } from '@redis/client/lib/commands/generic-transformers'; import { resp2MapToValue, resp3MapToValue, SampleRawReply, Timestamp, transformSamplesReply } from '.'; -import { TsRangeOptions, pushRangeArguments } from './RANGE'; -import { pushFilterArgument } from './MGET'; +import { TsRangeOptions, parseRangeArguments } from './RANGE'; +import { parseFilterArgument } from './MGET'; export const TIME_SERIES_REDUCERS = { AVG: 'AVG', @@ -24,8 +25,8 @@ export interface TsMRangeGroupBy { REDUCE: TimeSeriesReducer; } -export function pushGroupByArguments(args: Array, groupBy: TsMRangeGroupBy) { - args.push('GROUPBY', groupBy.label, 'REDUCE', groupBy.REDUCE); +export function parseGroupByArguments(parser: CommandParser, groupBy: TsMRangeGroupBy) { + parser.push('GROUPBY', groupBy.label, 'REDUCE', groupBy.REDUCE); } export type TsMRangeGroupByRawReply2 = ArrayReply< @@ -52,24 +53,19 @@ export type TsMRangeGroupByRawReply3 = MapReply< export function createTransformMRangeGroupByArguments(command: RedisArgument) { return ( + parser: CommandParser, fromTimestamp: Timestamp, toTimestamp: Timestamp, filter: RedisVariadicArgument, groupBy: TsMRangeGroupBy, options?: TsRangeOptions ) => { - let args = pushRangeArguments( - [command], - fromTimestamp, - toTimestamp, - options - ); - - args = pushFilterArgument(args, filter); - - pushGroupByArguments(args, groupBy); + parser.push(command); + parseRangeArguments(parser, fromTimestamp, toTimestamp, options) - return args; + parseFilterArgument(parser, filter); + + parseGroupByArguments(parser, groupBy); }; } @@ -85,9 +81,8 @@ export function extractResp3MRangeSources(raw: TsMRangeGroupByRawMetadataReply3) } export default { - FIRST_KEY_INDEX: undefined, IS_READ_ONLY: true, - transformArguments: createTransformMRangeGroupByArguments('TS.MRANGE'), + parseCommand: createTransformMRangeGroupByArguments('TS.MRANGE'), transformReply: { 2(reply: TsMRangeGroupByRawReply2, _?: any, typeMapping?: TypeMapping) { return resp2MapToValue(reply, ([_key, _labels, samples]) => { diff --git a/packages/time-series/lib/commands/MRANGE_SELECTED_LABELS.spec.ts b/packages/time-series/lib/commands/MRANGE_SELECTED_LABELS.spec.ts index 5c15bad89e8..92680dea375 100644 --- a/packages/time-series/lib/commands/MRANGE_SELECTED_LABELS.spec.ts +++ b/packages/time-series/lib/commands/MRANGE_SELECTED_LABELS.spec.ts @@ -2,11 +2,12 @@ import { strict as assert } from 'node:assert'; import testUtils, { GLOBAL } from '../test-utils'; import MRANGE_SELECTED_LABELS from './MRANGE_SELECTED_LABELS'; import { TIME_SERIES_AGGREGATION_TYPE } from './CREATERULE'; +import { parseArgs } from '@redis/client/lib/commands/generic-transformers'; describe('TS.MRANGE_SELECTED_LABELS', () => { it('transformArguments', () => { assert.deepEqual( - MRANGE_SELECTED_LABELS.transformArguments('-', '+', 'label', 'label=value', { + parseArgs(MRANGE_SELECTED_LABELS, '-', '+', 'label', 'label=value', { FILTER_BY_TS: [0], FILTER_BY_VALUE: { min: 0, diff --git a/packages/time-series/lib/commands/MRANGE_SELECTED_LABELS.ts b/packages/time-series/lib/commands/MRANGE_SELECTED_LABELS.ts index f91f9583330..115944a2f40 100644 --- a/packages/time-series/lib/commands/MRANGE_SELECTED_LABELS.ts +++ b/packages/time-series/lib/commands/MRANGE_SELECTED_LABELS.ts @@ -1,8 +1,9 @@ -import { Command, ArrayReply, BlobStringReply, Resp2Reply, MapReply, TuplesReply, TypeMapping, NullReply, RedisArgument } from '@redis/client/dist/lib/RESP/types'; -import { RedisVariadicArgument } from '@redis/client/dist/lib/commands/generic-transformers'; -import { pushSelectedLabelsArguments, resp2MapToValue, resp3MapToValue, SampleRawReply, Timestamp, transformRESP2Labels, transformSamplesReply } from '.'; -import { TsRangeOptions, pushRangeArguments } from './RANGE'; -import { pushFilterArgument } from './MGET'; +import { CommandParser } from '@redis/client/lib/client/parser'; +import { Command, ArrayReply, BlobStringReply, Resp2Reply, MapReply, TuplesReply, TypeMapping, NullReply, RedisArgument } from '@redis/client/lib/RESP/types'; +import { RedisVariadicArgument } from '@redis/client/lib/commands/generic-transformers'; +import { parseSelectedLabelsArguments, resp2MapToValue, resp3MapToValue, SampleRawReply, Timestamp, transformRESP2Labels, transformSamplesReply } from '.'; +import { TsRangeOptions, parseRangeArguments } from './RANGE'; +import { parseFilterArgument } from './MGET'; export type TsMRangeSelectedLabelsRawReply2 = ArrayReply< TuplesReply<[ @@ -26,29 +27,30 @@ export type TsMRangeSelectedLabelsRawReply3 = MapReply< export function createTransformMRangeSelectedLabelsArguments(command: RedisArgument) { return ( + parser: CommandParser, fromTimestamp: Timestamp, toTimestamp: Timestamp, selectedLabels: RedisVariadicArgument, filter: RedisVariadicArgument, options?: TsRangeOptions ) => { - let args = pushRangeArguments( - [command], + parser.push(command); + parseRangeArguments( + parser, fromTimestamp, toTimestamp, options ); - args = pushSelectedLabelsArguments(args, selectedLabels); + parseSelectedLabelsArguments(parser, selectedLabels); - return pushFilterArgument(args, filter); + parseFilterArgument(parser, filter); }; } export default { - FIRST_KEY_INDEX: undefined, IS_READ_ONLY: true, - transformArguments: createTransformMRangeSelectedLabelsArguments('TS.MRANGE'), + parseCommand: createTransformMRangeSelectedLabelsArguments('TS.MRANGE'), transformReply: { 2(reply: TsMRangeSelectedLabelsRawReply2, _?: any, typeMapping?: TypeMapping) { return resp2MapToValue(reply, ([_key, labels, samples]) => { diff --git a/packages/time-series/lib/commands/MRANGE_SELECTED_LABELS_GROUPBY.spec.ts b/packages/time-series/lib/commands/MRANGE_SELECTED_LABELS_GROUPBY.spec.ts index 90090a851aa..4e5b2b47094 100644 --- a/packages/time-series/lib/commands/MRANGE_SELECTED_LABELS_GROUPBY.spec.ts +++ b/packages/time-series/lib/commands/MRANGE_SELECTED_LABELS_GROUPBY.spec.ts @@ -3,11 +3,12 @@ import testUtils, { GLOBAL } from '../test-utils'; import MRANGE_SELECTED_LABELS_GROUPBY from './MRANGE_SELECTED_LABELS_GROUPBY'; import { TIME_SERIES_REDUCERS } from './MRANGE_GROUPBY'; import { TIME_SERIES_AGGREGATION_TYPE } from './CREATERULE'; +import { parseArgs } from '@redis/client/lib/commands/generic-transformers'; describe('TS.MRANGE_SELECTED_LABELS_GROUPBY', () => { it('transformArguments', () => { assert.deepEqual( - MRANGE_SELECTED_LABELS_GROUPBY.transformArguments('-', '+', 'label', 'label=value', { + parseArgs(MRANGE_SELECTED_LABELS_GROUPBY, '-', '+', 'label', 'label=value', { REDUCE: TIME_SERIES_REDUCERS.AVG, label: 'label' }, { diff --git a/packages/time-series/lib/commands/MRANGE_SELECTED_LABELS_GROUPBY.ts b/packages/time-series/lib/commands/MRANGE_SELECTED_LABELS_GROUPBY.ts index 7a798c41137..b4e8006a84e 100644 --- a/packages/time-series/lib/commands/MRANGE_SELECTED_LABELS_GROUPBY.ts +++ b/packages/time-series/lib/commands/MRANGE_SELECTED_LABELS_GROUPBY.ts @@ -1,9 +1,10 @@ -import { Command, ArrayReply, BlobStringReply, MapReply, TuplesReply, RedisArgument, NullReply } from '@redis/client/dist/lib/RESP/types'; -import { RedisVariadicArgument } from '@redis/client/dist/lib/commands/generic-transformers'; -import { pushSelectedLabelsArguments, resp3MapToValue, SampleRawReply, Timestamp, transformSamplesReply } from '.'; -import { TsRangeOptions, pushRangeArguments } from './RANGE'; -import { extractResp3MRangeSources, pushGroupByArguments, TsMRangeGroupBy, TsMRangeGroupByRawMetadataReply3 } from './MRANGE_GROUPBY'; -import { pushFilterArgument } from './MGET'; +import { CommandParser } from '@redis/client/lib/client/parser'; +import { Command, ArrayReply, BlobStringReply, MapReply, TuplesReply, RedisArgument, NullReply } from '@redis/client/lib/RESP/types'; +import { RedisVariadicArgument } from '@redis/client/lib/commands/generic-transformers'; +import { parseSelectedLabelsArguments, resp3MapToValue, SampleRawReply, Timestamp, transformSamplesReply } from '.'; +import { TsRangeOptions, parseRangeArguments } from './RANGE'; +import { extractResp3MRangeSources, parseGroupByArguments, TsMRangeGroupBy, TsMRangeGroupByRawMetadataReply3 } from './MRANGE_GROUPBY'; +import { parseFilterArgument } from './MGET'; import MRANGE_SELECTED_LABELS from './MRANGE_SELECTED_LABELS'; export type TsMRangeWithLabelsGroupByRawReply3 = MapReply< @@ -20,6 +21,7 @@ export function createMRangeSelectedLabelsGroupByTransformArguments( command: RedisArgument ) { return ( + parser: CommandParser, fromTimestamp: Timestamp, toTimestamp: Timestamp, selectedLabels: RedisVariadicArgument, @@ -27,27 +29,25 @@ export function createMRangeSelectedLabelsGroupByTransformArguments( groupBy: TsMRangeGroupBy, options?: TsRangeOptions ) => { - let args = pushRangeArguments( - [command], + parser.push(command); + parseRangeArguments( + parser, fromTimestamp, toTimestamp, options ); - args = pushSelectedLabelsArguments(args, selectedLabels); + parseSelectedLabelsArguments(parser, selectedLabels); - args = pushFilterArgument(args, filter); + parseFilterArgument(parser, filter); - pushGroupByArguments(args, groupBy); - - return args; + parseGroupByArguments(parser, groupBy); }; } export default { - FIRST_KEY_INDEX: undefined, IS_READ_ONLY: true, - transformArguments: createMRangeSelectedLabelsGroupByTransformArguments('TS.MRANGE'), + parseCommand: createMRangeSelectedLabelsGroupByTransformArguments('TS.MRANGE'), transformReply: { 2: MRANGE_SELECTED_LABELS.transformReply[2], 3(reply: TsMRangeWithLabelsGroupByRawReply3) { diff --git a/packages/time-series/lib/commands/MRANGE_WITHLABELS.spec.ts b/packages/time-series/lib/commands/MRANGE_WITHLABELS.spec.ts index fabf04b60dc..eab2e1fadbe 100644 --- a/packages/time-series/lib/commands/MRANGE_WITHLABELS.spec.ts +++ b/packages/time-series/lib/commands/MRANGE_WITHLABELS.spec.ts @@ -2,11 +2,12 @@ import { strict as assert } from 'node:assert'; import testUtils, { GLOBAL } from '../test-utils'; import MRANGE_WITHLABELS from './MRANGE_WITHLABELS'; import { TIME_SERIES_AGGREGATION_TYPE } from './CREATERULE'; +import { parseArgs } from '@redis/client/lib/commands/generic-transformers'; describe('TS.MRANGE_WITHLABELS', () => { it('transformArguments', () => { assert.deepEqual( - MRANGE_WITHLABELS.transformArguments('-', '+', 'label=value', { + parseArgs(MRANGE_WITHLABELS, '-', '+', 'label=value', { LATEST: true, FILTER_BY_TS: [0], FILTER_BY_VALUE: { diff --git a/packages/time-series/lib/commands/MRANGE_WITHLABELS.ts b/packages/time-series/lib/commands/MRANGE_WITHLABELS.ts index ab7a4ec8f6a..04d72411b7e 100644 --- a/packages/time-series/lib/commands/MRANGE_WITHLABELS.ts +++ b/packages/time-series/lib/commands/MRANGE_WITHLABELS.ts @@ -1,8 +1,9 @@ -import { Command, UnwrapReply, ArrayReply, BlobStringReply, Resp2Reply, MapReply, TuplesReply, TypeMapping, RedisArgument } from '@redis/client/dist/lib/RESP/types'; -import { RedisVariadicArgument } from '@redis/client/dist/lib/commands/generic-transformers'; +import { CommandParser } from '@redis/client/lib/client/parser'; +import { Command, UnwrapReply, ArrayReply, BlobStringReply, Resp2Reply, MapReply, TuplesReply, TypeMapping, RedisArgument } from '@redis/client/lib/RESP/types'; +import { RedisVariadicArgument } from '@redis/client/lib/commands/generic-transformers'; import { resp2MapToValue, resp3MapToValue, SampleRawReply, Timestamp, transformSamplesReply } from '.'; -import { TsRangeOptions, pushRangeArguments } from './RANGE'; -import { pushFilterArgument } from './MGET'; +import { TsRangeOptions, parseRangeArguments } from './RANGE'; +import { parseFilterArgument } from './MGET'; export type TsMRangeWithLabelsRawReply2 = ArrayReply< TuplesReply<[ @@ -26,28 +27,30 @@ export type TsMRangeWithLabelsRawReply3 = MapReply< export function createTransformMRangeWithLabelsArguments(command: RedisArgument) { return ( + parser: CommandParser, fromTimestamp: Timestamp, toTimestamp: Timestamp, filter: RedisVariadicArgument, options?: TsRangeOptions ) => { - const args = pushRangeArguments( - [command], + parser.push(command); + parseRangeArguments( + parser, fromTimestamp, toTimestamp, options ); - args.push('WITHLABELS'); + parser.push('WITHLABELS'); - return pushFilterArgument(args, filter); + parseFilterArgument(parser, filter); }; } export default { - FIRST_KEY_INDEX: undefined, + NOT_KEYED_COMMAND: true, IS_READ_ONLY: true, - transformArguments: createTransformMRangeWithLabelsArguments('TS.MRANGE'), + parseCommand: createTransformMRangeWithLabelsArguments('TS.MRANGE'), transformReply: { 2(reply: TsMRangeWithLabelsRawReply2, _?: any, typeMapping?: TypeMapping) { return resp2MapToValue(reply, ([_key, labels, samples]) => { diff --git a/packages/time-series/lib/commands/MRANGE_WITHLABELS_GROUPBY.spec.ts b/packages/time-series/lib/commands/MRANGE_WITHLABELS_GROUPBY.spec.ts index 755c3aca320..4a8b8fe707f 100644 --- a/packages/time-series/lib/commands/MRANGE_WITHLABELS_GROUPBY.spec.ts +++ b/packages/time-series/lib/commands/MRANGE_WITHLABELS_GROUPBY.spec.ts @@ -3,11 +3,12 @@ import testUtils, { GLOBAL } from '../test-utils'; import MRANGE_WITHLABELS_GROUPBY from './MRANGE_WITHLABELS_GROUPBY'; import { TIME_SERIES_REDUCERS } from './MRANGE_GROUPBY'; import { TIME_SERIES_AGGREGATION_TYPE } from './CREATERULE'; +import { parseArgs } from '@redis/client/lib/commands/generic-transformers'; describe('TS.MRANGE_WITHLABELS_GROUPBY', () => { it('transformArguments', () => { assert.deepEqual( - MRANGE_WITHLABELS_GROUPBY.transformArguments('-', '+', 'label=value', { + parseArgs(MRANGE_WITHLABELS_GROUPBY, '-', '+', 'label=value', { label: 'label', REDUCE: TIME_SERIES_REDUCERS.AVG }, { diff --git a/packages/time-series/lib/commands/MRANGE_WITHLABELS_GROUPBY.ts b/packages/time-series/lib/commands/MRANGE_WITHLABELS_GROUPBY.ts index 7c5e0af368b..f09d630dcd4 100644 --- a/packages/time-series/lib/commands/MRANGE_WITHLABELS_GROUPBY.ts +++ b/packages/time-series/lib/commands/MRANGE_WITHLABELS_GROUPBY.ts @@ -1,9 +1,10 @@ -import { Command, ArrayReply, BlobStringReply, Resp2Reply, MapReply, TuplesReply, TypeMapping, RedisArgument } from '@redis/client/dist/lib/RESP/types'; -import { RedisVariadicArgument } from '@redis/client/dist/lib/commands/generic-transformers'; +import { CommandParser } from '@redis/client/lib/client/parser'; +import { Command, ArrayReply, BlobStringReply, Resp2Reply, MapReply, TuplesReply, TypeMapping, RedisArgument } from '@redis/client/lib/RESP/types'; +import { RedisVariadicArgument } from '@redis/client/lib/commands/generic-transformers'; import { resp2MapToValue, resp3MapToValue, SampleRawReply, Timestamp, transformRESP2LabelsWithSources, transformSamplesReply } from '.'; -import { TsRangeOptions, pushRangeArguments } from './RANGE'; -import { extractResp3MRangeSources, pushGroupByArguments, TsMRangeGroupBy, TsMRangeGroupByRawMetadataReply3 } from './MRANGE_GROUPBY'; -import { pushFilterArgument } from './MGET'; +import { TsRangeOptions, parseRangeArguments } from './RANGE'; +import { extractResp3MRangeSources, parseGroupByArguments, TsMRangeGroupBy, TsMRangeGroupByRawMetadataReply3 } from './MRANGE_GROUPBY'; +import { parseFilterArgument } from './MGET'; export type TsMRangeWithLabelsGroupByRawReply2 = ArrayReply< TuplesReply<[ @@ -28,33 +29,32 @@ export type TsMRangeWithLabelsGroupByRawReply3 = MapReply< export function createMRangeWithLabelsGroupByTransformArguments(command: RedisArgument) { return ( + parser: CommandParser, fromTimestamp: Timestamp, toTimestamp: Timestamp, filter: RedisVariadicArgument, groupBy: TsMRangeGroupBy, options?: TsRangeOptions ) => { - let args = pushRangeArguments( - [command], + parser.push(command); + parseRangeArguments( + parser, fromTimestamp, toTimestamp, options ); - args.push('WITHLABELS'); + parser.push('WITHLABELS'); - args = pushFilterArgument(args, filter); + parseFilterArgument(parser, filter); - pushGroupByArguments(args, groupBy); - - return args; - }; + parseGroupByArguments(parser, groupBy); + }; } export default { - FIRST_KEY_INDEX: undefined, IS_READ_ONLY: true, - transformArguments: createMRangeWithLabelsGroupByTransformArguments('TS.MRANGE'), + parseCommand: createMRangeWithLabelsGroupByTransformArguments('TS.MRANGE'), transformReply: { 2(reply: TsMRangeWithLabelsGroupByRawReply2, _?: any, typeMapping?: TypeMapping) { return resp2MapToValue(reply, ([_key, labels, samples]) => { diff --git a/packages/time-series/lib/commands/MREVRANGE.spec.ts b/packages/time-series/lib/commands/MREVRANGE.spec.ts index 8d6b8d3c148..09051103f8b 100644 --- a/packages/time-series/lib/commands/MREVRANGE.spec.ts +++ b/packages/time-series/lib/commands/MREVRANGE.spec.ts @@ -2,11 +2,12 @@ import { strict as assert } from 'node:assert'; import testUtils, { GLOBAL } from '../test-utils'; import MREVRANGE from './MREVRANGE'; import { TIME_SERIES_AGGREGATION_TYPE } from './CREATERULE'; +import { parseArgs } from '@redis/client/lib/commands/generic-transformers'; describe('TS.MREVRANGE', () => { it('transformArguments', () => { assert.deepEqual( - MREVRANGE.transformArguments('-', '+', 'label=value', { + parseArgs(MREVRANGE, '-', '+', 'label=value', { LATEST: true, FILTER_BY_TS: [0], FILTER_BY_VALUE: { diff --git a/packages/time-series/lib/commands/MREVRANGE.ts b/packages/time-series/lib/commands/MREVRANGE.ts index 097176e6832..e2ed6d9cc91 100644 --- a/packages/time-series/lib/commands/MREVRANGE.ts +++ b/packages/time-series/lib/commands/MREVRANGE.ts @@ -1,9 +1,9 @@ -import { Command } from '@redis/client/dist/lib/RESP/types'; +import { Command } from '@redis/client/lib/RESP/types'; import MRANGE, { createTransformMRangeArguments } from './MRANGE'; export default { - FIRST_KEY_INDEX: MRANGE.FIRST_KEY_INDEX, + NOT_KEYED_COMMAND: MRANGE.NOT_KEYED_COMMAND, IS_READ_ONLY: MRANGE.IS_READ_ONLY, - transformArguments: createTransformMRangeArguments('TS.MREVRANGE'), + parseCommand: createTransformMRangeArguments('TS.MREVRANGE'), transformReply: MRANGE.transformReply, } as const satisfies Command; diff --git a/packages/time-series/lib/commands/MREVRANGE_GROUPBY.spec.ts b/packages/time-series/lib/commands/MREVRANGE_GROUPBY.spec.ts index 9ccebc6c517..d32d675ad0a 100644 --- a/packages/time-series/lib/commands/MREVRANGE_GROUPBY.spec.ts +++ b/packages/time-series/lib/commands/MREVRANGE_GROUPBY.spec.ts @@ -3,11 +3,12 @@ import testUtils, { GLOBAL } from '../test-utils'; import MREVRANGE_GROUPBY from './MREVRANGE_GROUPBY'; import { TIME_SERIES_REDUCERS } from './MRANGE_GROUPBY'; import { TIME_SERIES_AGGREGATION_TYPE } from './CREATERULE'; +import { parseArgs } from '@redis/client/lib/commands/generic-transformers'; describe('TS.MREVRANGE_GROUPBY', () => { it('transformArguments', () => { assert.deepEqual( - MREVRANGE_GROUPBY.transformArguments('-', '+', 'label=value', { + parseArgs(MREVRANGE_GROUPBY, '-', '+', 'label=value', { REDUCE: TIME_SERIES_REDUCERS.AVG, label: 'label' }, { diff --git a/packages/time-series/lib/commands/MREVRANGE_GROUPBY.ts b/packages/time-series/lib/commands/MREVRANGE_GROUPBY.ts index 24b2e6142f6..efdd1acdcfe 100644 --- a/packages/time-series/lib/commands/MREVRANGE_GROUPBY.ts +++ b/packages/time-series/lib/commands/MREVRANGE_GROUPBY.ts @@ -1,9 +1,8 @@ -import { Command } from '@redis/client/dist/lib/RESP/types'; +import { Command } from '@redis/client/lib/RESP/types'; import MRANGE_GROUPBY, { createTransformMRangeGroupByArguments } from './MRANGE_GROUPBY'; export default { - FIRST_KEY_INDEX: MRANGE_GROUPBY.FIRST_KEY_INDEX, IS_READ_ONLY: MRANGE_GROUPBY.IS_READ_ONLY, - transformArguments: createTransformMRangeGroupByArguments('TS.MREVRANGE'), + parseCommand: createTransformMRangeGroupByArguments('TS.MREVRANGE'), transformReply: MRANGE_GROUPBY.transformReply, } as const satisfies Command; diff --git a/packages/time-series/lib/commands/MREVRANGE_SELECTED_LABELS.spec.ts b/packages/time-series/lib/commands/MREVRANGE_SELECTED_LABELS.spec.ts index f0533010b84..f68e34727c2 100644 --- a/packages/time-series/lib/commands/MREVRANGE_SELECTED_LABELS.spec.ts +++ b/packages/time-series/lib/commands/MREVRANGE_SELECTED_LABELS.spec.ts @@ -2,11 +2,12 @@ import { strict as assert } from 'node:assert'; import testUtils, { GLOBAL } from '../test-utils'; import MREVRANGE_SELECTED_LABELS from './MREVRANGE_SELECTED_LABELS'; import { TIME_SERIES_AGGREGATION_TYPE } from './CREATERULE'; +import { parseArgs } from '@redis/client/lib/commands/generic-transformers'; describe('TS.MREVRANGE_SELECTED_LABELS', () => { it('transformArguments', () => { assert.deepEqual( - MREVRANGE_SELECTED_LABELS.transformArguments('-', '+', 'label', 'label=value', { + parseArgs(MREVRANGE_SELECTED_LABELS, '-', '+', 'label', 'label=value', { FILTER_BY_TS: [0], FILTER_BY_VALUE: { min: 0, diff --git a/packages/time-series/lib/commands/MREVRANGE_SELECTED_LABELS.ts b/packages/time-series/lib/commands/MREVRANGE_SELECTED_LABELS.ts index 8656b768c28..8b679e65b6a 100644 --- a/packages/time-series/lib/commands/MREVRANGE_SELECTED_LABELS.ts +++ b/packages/time-series/lib/commands/MREVRANGE_SELECTED_LABELS.ts @@ -1,9 +1,8 @@ -import { Command } from '@redis/client/dist/lib/RESP/types'; +import { Command } from '@redis/client/lib/RESP/types'; import MRANGE_SELECTED_LABELS, { createTransformMRangeSelectedLabelsArguments } from './MRANGE_SELECTED_LABELS'; export default { - FIRST_KEY_INDEX: MRANGE_SELECTED_LABELS.FIRST_KEY_INDEX, IS_READ_ONLY: MRANGE_SELECTED_LABELS.IS_READ_ONLY, - transformArguments: createTransformMRangeSelectedLabelsArguments('TS.MREVRANGE'), + parseCommand: createTransformMRangeSelectedLabelsArguments('TS.MREVRANGE'), transformReply: MRANGE_SELECTED_LABELS.transformReply, } as const satisfies Command; diff --git a/packages/time-series/lib/commands/MREVRANGE_SELECTED_LABELS_GROUPBY.spec.ts b/packages/time-series/lib/commands/MREVRANGE_SELECTED_LABELS_GROUPBY.spec.ts index 34ef4ff79a0..444bb2f3d24 100644 --- a/packages/time-series/lib/commands/MREVRANGE_SELECTED_LABELS_GROUPBY.spec.ts +++ b/packages/time-series/lib/commands/MREVRANGE_SELECTED_LABELS_GROUPBY.spec.ts @@ -3,11 +3,12 @@ import testUtils, { GLOBAL } from '../test-utils'; import MREVRANGE_SELECTED_LABELS_GROUPBY from './MREVRANGE_SELECTED_LABELS_GROUPBY'; import { TIME_SERIES_REDUCERS } from './MRANGE_GROUPBY'; import { TIME_SERIES_AGGREGATION_TYPE } from './CREATERULE'; +import { parseArgs } from '@redis/client/lib/commands/generic-transformers'; describe('TS.MREVRANGE_SELECTED_LABELS_GROUPBY', () => { it('transformArguments', () => { assert.deepEqual( - MREVRANGE_SELECTED_LABELS_GROUPBY.transformArguments('-', '+', 'label', 'label=value', { + parseArgs(MREVRANGE_SELECTED_LABELS_GROUPBY, '-', '+', 'label', 'label=value', { REDUCE: TIME_SERIES_REDUCERS.AVG, label: 'label' }, { diff --git a/packages/time-series/lib/commands/MREVRANGE_SELECTED_LABELS_GROUPBY.ts b/packages/time-series/lib/commands/MREVRANGE_SELECTED_LABELS_GROUPBY.ts index f47330367b7..d01ebe1033a 100644 --- a/packages/time-series/lib/commands/MREVRANGE_SELECTED_LABELS_GROUPBY.ts +++ b/packages/time-series/lib/commands/MREVRANGE_SELECTED_LABELS_GROUPBY.ts @@ -1,9 +1,8 @@ -import { Command } from '@redis/client/dist/lib/RESP/types'; +import { Command } from '@redis/client/lib/RESP/types'; import MRANGE_SELECTED_LABELS_GROUPBY, { createMRangeSelectedLabelsGroupByTransformArguments } from './MRANGE_SELECTED_LABELS_GROUPBY'; export default { - FIRST_KEY_INDEX: MRANGE_SELECTED_LABELS_GROUPBY.FIRST_KEY_INDEX, IS_READ_ONLY: MRANGE_SELECTED_LABELS_GROUPBY.IS_READ_ONLY, - transformArguments: createMRangeSelectedLabelsGroupByTransformArguments('TS.MREVRANGE'), + parseCommand: createMRangeSelectedLabelsGroupByTransformArguments('TS.MREVRANGE'), transformReply: MRANGE_SELECTED_LABELS_GROUPBY.transformReply, } as const satisfies Command; diff --git a/packages/time-series/lib/commands/MREVRANGE_WITHLABELS.spec.ts b/packages/time-series/lib/commands/MREVRANGE_WITHLABELS.spec.ts index eb88f233e43..da43a715f2e 100644 --- a/packages/time-series/lib/commands/MREVRANGE_WITHLABELS.spec.ts +++ b/packages/time-series/lib/commands/MREVRANGE_WITHLABELS.spec.ts @@ -2,11 +2,12 @@ import { strict as assert } from 'node:assert'; import testUtils, { GLOBAL } from '../test-utils'; import MREVRANGE_WITHLABELS from './MREVRANGE_WITHLABELS'; import { TIME_SERIES_AGGREGATION_TYPE } from './CREATERULE'; +import { parseArgs } from '@redis/client/lib/commands/generic-transformers'; describe('TS.MREVRANGE_WITHLABELS', () => { it('transformArguments', () => { assert.deepEqual( - MREVRANGE_WITHLABELS.transformArguments('-', '+', 'label=value', { + parseArgs(MREVRANGE_WITHLABELS, '-', '+', 'label=value', { LATEST: true, FILTER_BY_TS: [0], FILTER_BY_VALUE: { diff --git a/packages/time-series/lib/commands/MREVRANGE_WITHLABELS.ts b/packages/time-series/lib/commands/MREVRANGE_WITHLABELS.ts index 81356d845fd..d4f6255592e 100644 --- a/packages/time-series/lib/commands/MREVRANGE_WITHLABELS.ts +++ b/packages/time-series/lib/commands/MREVRANGE_WITHLABELS.ts @@ -1,9 +1,9 @@ -import { Command } from '@redis/client/dist/lib/RESP/types'; +import { Command } from '@redis/client/lib/RESP/types'; import MRANGE_WITHLABELS, { createTransformMRangeWithLabelsArguments } from './MRANGE_WITHLABELS'; export default { - FIRST_KEY_INDEX: MRANGE_WITHLABELS.FIRST_KEY_INDEX, + NOT_KEYED_COMMAND: MRANGE_WITHLABELS.NOT_KEYED_COMMAND, IS_READ_ONLY: MRANGE_WITHLABELS.IS_READ_ONLY, - transformArguments: createTransformMRangeWithLabelsArguments('TS.MREVRANGE'), + parseCommand: createTransformMRangeWithLabelsArguments('TS.MREVRANGE'), transformReply: MRANGE_WITHLABELS.transformReply, } as const satisfies Command; diff --git a/packages/time-series/lib/commands/MREVRANGE_WITHLABELS_GROUPBY.spec.ts b/packages/time-series/lib/commands/MREVRANGE_WITHLABELS_GROUPBY.spec.ts index da2c358b330..f4e6df9f0c6 100644 --- a/packages/time-series/lib/commands/MREVRANGE_WITHLABELS_GROUPBY.spec.ts +++ b/packages/time-series/lib/commands/MREVRANGE_WITHLABELS_GROUPBY.spec.ts @@ -3,11 +3,12 @@ import testUtils, { GLOBAL } from '../test-utils'; import MREVRANGE_WITHLABELS_GROUPBY from './MREVRANGE_WITHLABELS_GROUPBY'; import { TIME_SERIES_REDUCERS } from './MRANGE_GROUPBY'; import { TIME_SERIES_AGGREGATION_TYPE } from './CREATERULE'; +import { parseArgs } from '@redis/client/lib/commands/generic-transformers'; describe('TS.MREVRANGE_WITHLABELS_GROUPBY', () => { it('transformArguments', () => { assert.deepEqual( - MREVRANGE_WITHLABELS_GROUPBY.transformArguments('-', '+', 'label=value', { + parseArgs(MREVRANGE_WITHLABELS_GROUPBY, '-', '+', 'label=value', { label: 'label', REDUCE: TIME_SERIES_REDUCERS.AVG }, { diff --git a/packages/time-series/lib/commands/MREVRANGE_WITHLABELS_GROUPBY.ts b/packages/time-series/lib/commands/MREVRANGE_WITHLABELS_GROUPBY.ts index b3d49643fd0..ed43d0eae6b 100644 --- a/packages/time-series/lib/commands/MREVRANGE_WITHLABELS_GROUPBY.ts +++ b/packages/time-series/lib/commands/MREVRANGE_WITHLABELS_GROUPBY.ts @@ -1,9 +1,8 @@ -import { Command } from '@redis/client/dist/lib/RESP/types'; +import { Command } from '@redis/client/lib/RESP/types'; import MRANGE_WITHLABELS_GROUPBY, { createMRangeWithLabelsGroupByTransformArguments } from './MRANGE_WITHLABELS_GROUPBY'; export default { - FIRST_KEY_INDEX: MRANGE_WITHLABELS_GROUPBY.FIRST_KEY_INDEX, IS_READ_ONLY: MRANGE_WITHLABELS_GROUPBY.IS_READ_ONLY, - transformArguments: createMRangeWithLabelsGroupByTransformArguments('TS.MREVRANGE'), + parseCommand: createMRangeWithLabelsGroupByTransformArguments('TS.MREVRANGE'), transformReply: MRANGE_WITHLABELS_GROUPBY.transformReply, } as const satisfies Command; diff --git a/packages/time-series/lib/commands/QUERYINDEX.spec.ts b/packages/time-series/lib/commands/QUERYINDEX.spec.ts index 74f201bb74b..2f3f5617fb3 100644 --- a/packages/time-series/lib/commands/QUERYINDEX.spec.ts +++ b/packages/time-series/lib/commands/QUERYINDEX.spec.ts @@ -1,19 +1,20 @@ import { strict as assert } from 'node:assert'; import testUtils, { GLOBAL } from '../test-utils'; import QUERYINDEX from './QUERYINDEX'; +import { parseArgs } from '@redis/client/lib/commands/generic-transformers'; describe('TS.QUERYINDEX', () => { describe('transformArguments', () => { it('single filter', () => { assert.deepEqual( - QUERYINDEX.transformArguments('*'), + parseArgs(QUERYINDEX, '*'), ['TS.QUERYINDEX', '*'] ); }); it('multiple filters', () => { assert.deepEqual( - QUERYINDEX.transformArguments(['a=1', 'b=2']), + parseArgs(QUERYINDEX, ['a=1', 'b=2']), ['TS.QUERYINDEX', 'a=1', 'b=2'] ); }); diff --git a/packages/time-series/lib/commands/QUERYINDEX.ts b/packages/time-series/lib/commands/QUERYINDEX.ts index 86c2a3c5a7e..69625801974 100644 --- a/packages/time-series/lib/commands/QUERYINDEX.ts +++ b/packages/time-series/lib/commands/QUERYINDEX.ts @@ -1,11 +1,13 @@ -import { ArrayReply, BlobStringReply, SetReply, Command } from '@redis/client/dist/lib/RESP/types'; -import { RedisVariadicArgument, pushVariadicArguments } from '@redis/client/dist/lib/commands/generic-transformers'; +import { CommandParser } from '@redis/client/lib/client/parser'; +import { ArrayReply, BlobStringReply, SetReply, Command } from '@redis/client/lib/RESP/types'; +import { RedisVariadicArgument } from '@redis/client/lib/commands/generic-transformers'; export default { - FIRST_KEY_INDEX: undefined, + NOT_KEYED_COMMAND: true, IS_READ_ONLY: true, - transformArguments(filter: RedisVariadicArgument) { - return pushVariadicArguments(['TS.QUERYINDEX'], filter); + parseCommand(parser: CommandParser, filter: RedisVariadicArgument) { + parser.push('TS.QUERYINDEX'); + parser.pushVariadic(filter); }, transformReply: { 2: undefined as unknown as () => ArrayReply, diff --git a/packages/time-series/lib/commands/RANGE.spec.ts b/packages/time-series/lib/commands/RANGE.spec.ts index bc5d38d740b..2d20b455fc1 100644 --- a/packages/time-series/lib/commands/RANGE.spec.ts +++ b/packages/time-series/lib/commands/RANGE.spec.ts @@ -2,11 +2,12 @@ import { strict as assert } from 'node:assert'; import testUtils, { GLOBAL } from '../test-utils'; import RANGE from './RANGE'; import { TIME_SERIES_AGGREGATION_TYPE } from './CREATERULE'; +import { parseArgs } from '@redis/client/lib/commands/generic-transformers'; describe('TS.RANGE', () => { it('transformArguments', () => { assert.deepEqual( - RANGE.transformArguments('key', '-', '+', { + parseArgs(RANGE, 'key', '-', '+', { FILTER_BY_TS: [0], FILTER_BY_VALUE: { min: 1, diff --git a/packages/time-series/lib/commands/RANGE.ts b/packages/time-series/lib/commands/RANGE.ts index 084073fefe6..ef4c79fe603 100644 --- a/packages/time-series/lib/commands/RANGE.ts +++ b/packages/time-series/lib/commands/RANGE.ts @@ -1,7 +1,8 @@ -import { CommandArguments, RedisArgument, Command } from '@redis/client/dist/lib/RESP/types'; +import { CommandParser } from '@redis/client/lib/client/parser'; +import { RedisArgument, Command } from '@redis/client/lib/RESP/types'; import { Timestamp, transformTimestampArgument, SamplesRawReply, transformSamplesReply } from '.'; import { TimeSeriesAggregationType } from './CREATERULE'; -import { Resp2Reply } from '@redis/client/dist/lib/RESP/types'; +import { Resp2Reply } from '@redis/client/lib/RESP/types'; export const TIME_SERIES_BUCKET_TIMESTAMP = { LOW: '-', @@ -29,30 +30,30 @@ export interface TsRangeOptions { }; } -export function pushRangeArguments( - args: CommandArguments, +export function parseRangeArguments( + parser: CommandParser, fromTimestamp: Timestamp, toTimestamp: Timestamp, options?: TsRangeOptions ) { - args.push( + parser.push( transformTimestampArgument(fromTimestamp), transformTimestampArgument(toTimestamp) ); if (options?.LATEST) { - args.push('LATEST'); + parser.push('LATEST'); } if (options?.FILTER_BY_TS) { - args.push('FILTER_BY_TS'); + parser.push('FILTER_BY_TS'); for (const timestamp of options.FILTER_BY_TS) { - args.push(transformTimestampArgument(timestamp)); + parser.push(transformTimestampArgument(timestamp)); } } if (options?.FILTER_BY_VALUE) { - args.push( + parser.push( 'FILTER_BY_VALUE', options.FILTER_BY_VALUE.min.toString(), options.FILTER_BY_VALUE.max.toString() @@ -60,54 +61,52 @@ export function pushRangeArguments( } if (options?.COUNT !== undefined) { - args.push('COUNT', options.COUNT.toString()); + parser.push('COUNT', options.COUNT.toString()); } if (options?.AGGREGATION) { if (options?.ALIGN !== undefined) { - args.push('ALIGN', transformTimestampArgument(options.ALIGN)); + parser.push('ALIGN', transformTimestampArgument(options.ALIGN)); } - args.push( + parser.push( 'AGGREGATION', options.AGGREGATION.type, transformTimestampArgument(options.AGGREGATION.timeBucket) ); if (options.AGGREGATION.BUCKETTIMESTAMP) { - args.push( + parser.push( 'BUCKETTIMESTAMP', options.AGGREGATION.BUCKETTIMESTAMP ); } if (options.AGGREGATION.EMPTY) { - args.push('EMPTY'); + parser.push('EMPTY'); } } - - return args; } export function transformRangeArguments( - command: RedisArgument, + parser: CommandParser, key: RedisArgument, fromTimestamp: Timestamp, toTimestamp: Timestamp, options?: TsRangeOptions ) { - return pushRangeArguments( - [command, key], - fromTimestamp, - toTimestamp, - options - ); + parser.pushKey(key); + parseRangeArguments(parser, fromTimestamp, toTimestamp, options); } export default { - FIRST_KEY_INDEX: 1, IS_READ_ONLY: true, - transformArguments: transformRangeArguments.bind(undefined, 'TS.RANGE'), + parseCommand(...args: Parameters) { + const parser = args[0]; + + parser.push('TS.RANGE'); + transformRangeArguments(...args); + }, transformReply: { 2(reply: Resp2Reply) { return transformSamplesReply[2](reply); diff --git a/packages/time-series/lib/commands/REVRANGE.spec.ts b/packages/time-series/lib/commands/REVRANGE.spec.ts index c371e8306b0..a4c6aa2c0db 100644 --- a/packages/time-series/lib/commands/REVRANGE.spec.ts +++ b/packages/time-series/lib/commands/REVRANGE.spec.ts @@ -2,11 +2,12 @@ import { strict as assert } from 'node:assert'; import testUtils, { GLOBAL } from '../test-utils'; import REVRANGE from './REVRANGE'; import { TIME_SERIES_AGGREGATION_TYPE } from '../index'; +import { parseArgs } from '@redis/client/lib/commands/generic-transformers'; describe('TS.REVRANGE', () => { it('transformArguments', () => { assert.deepEqual( - REVRANGE.transformArguments('key', '-', '+', { + parseArgs(REVRANGE, 'key', '-', '+', { FILTER_BY_TS: [0], FILTER_BY_VALUE: { min: 1, diff --git a/packages/time-series/lib/commands/REVRANGE.ts b/packages/time-series/lib/commands/REVRANGE.ts index 1097223080b..24a31a785a2 100644 --- a/packages/time-series/lib/commands/REVRANGE.ts +++ b/packages/time-series/lib/commands/REVRANGE.ts @@ -1,9 +1,13 @@ -import { Command } from '@redis/client/dist/lib/RESP/types'; +import { Command } from '@redis/client/lib/RESP/types'; import RANGE, { transformRangeArguments } from './RANGE'; export default { - FIRST_KEY_INDEX: RANGE.FIRST_KEY_INDEX, IS_READ_ONLY: RANGE.IS_READ_ONLY, - transformArguments: transformRangeArguments.bind(undefined, 'TS.REVRANGE'), + parseCommand(...args: Parameters) { + const parser = args[0]; + + parser.push('TS.REVRANGE'); + transformRangeArguments(...args); + }, transformReply: RANGE.transformReply } as const satisfies Command; diff --git a/packages/time-series/lib/commands/index.spec.ts b/packages/time-series/lib/commands/index.spec.ts index ff7f4afad68..5b28708152f 100644 --- a/packages/time-series/lib/commands/index.spec.ts +++ b/packages/time-series/lib/commands/index.spec.ts @@ -1,4 +1,4 @@ -// import { RedisCommandArguments } from '@redis/client/dist/lib/commands'; +// import { RedisCommandArguments } from '@redis/client/lib/commands'; // import { strict as assert } from 'node:assert'; // import { // transformTimestampArgument, diff --git a/packages/time-series/lib/commands/index.ts b/packages/time-series/lib/commands/index.ts index 5b9d97b6566..e0389a60a2e 100644 --- a/packages/time-series/lib/commands/index.ts +++ b/packages/time-series/lib/commands/index.ts @@ -1,4 +1,4 @@ -import type { DoubleReply, NumberReply, RedisArgument, RedisCommands, TuplesReply, UnwrapReply, Resp2Reply, ArrayReply, BlobStringReply, MapReply, NullReply, TypeMapping, ReplyUnion, RespType } from '@redis/client/lib/RESP/types'; +import type { DoubleReply, NumberReply, RedisCommands, TuplesReply, UnwrapReply, Resp2Reply, ArrayReply, BlobStringReply, MapReply, NullReply, TypeMapping, ReplyUnion, RespType } from '@redis/client/lib/RESP/types'; import ADD, { TsIgnoreOptions } from './ADD'; import ALTER from './ALTER'; import CREATE from './CREATE'; @@ -29,7 +29,8 @@ import MREVRANGE from './MREVRANGE'; import QUERYINDEX from './QUERYINDEX'; import RANGE from './RANGE'; import REVRANGE from './REVRANGE'; -import { RedisVariadicArgument, pushVariadicArguments } from '@redis/client/lib/commands/generic-transformers'; +import { RedisVariadicArgument } from '@redis/client/lib/commands/generic-transformers'; +import { CommandParser } from '@redis/client/lib/client/parser'; import { RESP_TYPES } from '@redis/client/lib/RESP/decoder'; export default { @@ -95,15 +96,15 @@ export default { revRange: REVRANGE } as const satisfies RedisCommands; -export function pushIgnoreArgument(args: Array, ignore?: TsIgnoreOptions) { +export function parseIgnoreArgument(parser: CommandParser, ignore?: TsIgnoreOptions) { if (ignore !== undefined) { - args.push('IGNORE', ignore.maxTimeDiff.toString(), ignore.maxValDiff.toString()); + parser.push('IGNORE', ignore.maxTimeDiff.toString(), ignore.maxValDiff.toString()); } } -export function pushRetentionArgument(args: Array, retention?: number) { +export function parseRetentionArgument(parser: CommandParser, retention?: number) { if (retention !== undefined) { - args.push('RETENTION', retention.toString()); + parser.push('RETENTION', retention.toString()); } } @@ -114,15 +115,15 @@ export const TIME_SERIES_ENCODING = { export type TimeSeriesEncoding = typeof TIME_SERIES_ENCODING[keyof typeof TIME_SERIES_ENCODING]; -export function pushEncodingArgument(args: Array, encoding?: TimeSeriesEncoding) { +export function parseEncodingArgument(parser: CommandParser, encoding?: TimeSeriesEncoding) { if (encoding !== undefined) { - args.push('ENCODING', encoding); + parser.push('ENCODING', encoding); } } -export function pushChunkSizeArgument(args: Array, chunkSize?: number) { +export function parseChunkSizeArgument(parser: CommandParser, chunkSize?: number) { if (chunkSize !== undefined) { - args.push('CHUNK_SIZE', chunkSize.toString()); + parser.push('CHUNK_SIZE', chunkSize.toString()); } } @@ -137,9 +138,9 @@ export const TIME_SERIES_DUPLICATE_POLICIES = { export type TimeSeriesDuplicatePolicies = typeof TIME_SERIES_DUPLICATE_POLICIES[keyof typeof TIME_SERIES_DUPLICATE_POLICIES]; -export function pushDuplicatePolicy(args: Array, duplicatePolicy?: TimeSeriesDuplicatePolicies) { +export function parseDuplicatePolicy(parser: CommandParser, duplicatePolicy?: TimeSeriesDuplicatePolicies) { if (duplicatePolicy !== undefined) { - args.push('DUPLICATE_POLICY', duplicatePolicy); + parser.push('DUPLICATE_POLICY', duplicatePolicy); } } @@ -159,16 +160,14 @@ export type Labels = { [label: string]: string; }; -export function pushLabelsArgument(args: Array, labels?: Labels) { +export function parseLabelsArgument(parser: CommandParser, labels?: Labels) { if (labels) { - args.push('LABELS'); + parser.push('LABELS'); for (const [label, value] of Object.entries(labels)) { - args.push(label, value); + parser.push(label, value); } } - - return args; } export type SampleRawReply = TuplesReply<[timestamp: NumberReply, value: DoubleReply]>; @@ -269,12 +268,12 @@ export function resp3MapToValue< return reply as never; } -export function pushSelectedLabelsArguments( - args: Array, +export function parseSelectedLabelsArguments( + parser: CommandParser, selectedLabels: RedisVariadicArgument ) { - args.push('SELECTED_LABELS'); - return pushVariadicArguments(args, selectedLabels); + parser.push('SELECTED_LABELS'); + parser.pushVariadic(selectedLabels); } export type RawLabelValue = BlobStringReply | NullReply; From 8dab27ed02cc4b52c60ed3e069f4a3df1a1d0a2e Mon Sep 17 00:00:00 2001 From: Shaya Potter Date: Mon, 4 Nov 2024 17:23:34 +0200 Subject: [PATCH 1480/1748] fix sentinel generics (#2859) * fix sentinel generics * comment nit --- packages/client/lib/sentinel/index.ts | 12 ++++++------ packages/client/lib/sentinel/types.ts | 6 ++++-- 2 files changed, 10 insertions(+), 8 deletions(-) diff --git a/packages/client/lib/sentinel/index.ts b/packages/client/lib/sentinel/index.ts index d25fa03e559..92a87fbb145 100644 --- a/packages/client/lib/sentinel/index.ts +++ b/packages/client/lib/sentinel/index.ts @@ -93,10 +93,10 @@ export class RedisSentinelClient< RESP extends RespVersions = 2, TYPE_MAPPING extends TypeMapping = {} >( + options: RedisSentinelOptions, internal: RedisSentinelInternal, clientInfo: ClientInfo, commandOptions?: CommandOptions, - options?: RedisSentinelOptions ) { return RedisSentinelClient.factory(options)(internal, clientInfo, commandOptions); } @@ -272,7 +272,7 @@ export default class RedisSentinel< this.#options = options; - if (options?.commandOptions) { + if (options.commandOptions) { this.#commandOptions = options.commandOptions; } @@ -307,7 +307,7 @@ export default class RedisSentinel< Sentinel.prototype.Multi = RedisSentinelMultiCommand.extend(config); - return (options?: Omit>) => { + return (options: Omit>) => { // returning a "proxy" to prevent the namespaces.self to leak between "proxies" return Object.create(new Sentinel(options)) as RedisSentinelType; }; @@ -319,7 +319,7 @@ export default class RedisSentinel< S extends RedisScripts = {}, RESP extends RespVersions = 2, TYPE_MAPPING extends TypeMapping = {} - >(options?: RedisSentinelOptions) { + >(options: RedisSentinelOptions) { return RedisSentinel.factory(options)(options); } @@ -409,7 +409,7 @@ export default class RedisSentinel< try { return await fn( - RedisSentinelClient.create(this._self.#internal, clientInfo, this._self.#commandOptions, this._self.#options) + RedisSentinelClient.create(this._self.#options, this._self.#internal, clientInfo, this._self.#commandOptions) ); } finally { const promise = this._self.#internal.releaseClientLease(clientInfo); @@ -510,7 +510,7 @@ export default class RedisSentinel< async aquire(): Promise> { const clientInfo = await this._self.#internal.getClientLease(); - return RedisSentinelClient.create(this._self.#internal, clientInfo, this._self.#commandOptions, this._self.#options); + return RedisSentinelClient.create(this._self.#options, this._self.#internal, clientInfo, this._self.#commandOptions); } getSentinelNode(): RedisNode | undefined { diff --git a/packages/client/lib/sentinel/types.ts b/packages/client/lib/sentinel/types.ts index 1f868ec5177..428e7bccd66 100644 --- a/packages/client/lib/sentinel/types.ts +++ b/packages/client/lib/sentinel/types.ts @@ -29,14 +29,16 @@ export interface RedisSentinelOptions< * The maximum number of times a command will retry due to topology changes. */ maxCommandRediscovers?: number; + // TODO: omit properties that users shouldn't be able to specify for sentinel at this level /** * The configuration values for every node in the cluster. Use this for example when specifying an ACL user to connect with */ - nodeClientOptions?: RedisClientOptions; + nodeClientOptions?: RedisClientOptions; + // TODO: omit properties that users shouldn't be able to specify for sentinel at this level /** * The configuration values for every sentinel in the cluster. Use this for example when specifying an ACL user to connect with */ - sentinelClientOptions?: RedisClientOptions; + sentinelClientOptions?: RedisClientOptions; /** * The number of clients connected to the master node */ From b835309cf8f2cc27c3543956a45bb170e0672124 Mon Sep 17 00:00:00 2001 From: Leibale Eidelman Date: Mon, 4 Nov 2024 12:21:17 -0500 Subject: [PATCH 1481/1748] fix cross packages imports --- packages/bloom/lib/commands/bloom/ADD.ts | 6 +++--- packages/bloom/lib/commands/bloom/CARD.ts | 4 ++-- packages/bloom/lib/commands/bloom/EXISTS.ts | 6 +++--- packages/bloom/lib/commands/bloom/INFO.ts | 4 ++-- packages/bloom/lib/commands/bloom/INSERT.ts | 8 ++++---- packages/bloom/lib/commands/bloom/LOADCHUNK.ts | 4 ++-- packages/bloom/lib/commands/bloom/MADD.ts | 8 ++++---- packages/bloom/lib/commands/bloom/MEXISTS.ts | 8 ++++---- packages/bloom/lib/commands/bloom/RESERVE.ts | 4 ++-- packages/bloom/lib/commands/bloom/SCANDUMP.ts | 4 ++-- packages/bloom/lib/commands/bloom/index.ts | 2 +- packages/bloom/lib/commands/count-min-sketch/INCRBY.ts | 4 ++-- packages/bloom/lib/commands/count-min-sketch/INFO.ts | 4 ++-- packages/bloom/lib/commands/count-min-sketch/INITBYDIM.ts | 4 ++-- .../bloom/lib/commands/count-min-sketch/INITBYPROB.ts | 4 ++-- packages/bloom/lib/commands/count-min-sketch/MERGE.ts | 4 ++-- packages/bloom/lib/commands/count-min-sketch/QUERY.ts | 6 +++--- packages/bloom/lib/commands/count-min-sketch/index.ts | 2 +- packages/bloom/lib/commands/cuckoo/ADD.ts | 6 +++--- packages/bloom/lib/commands/cuckoo/ADDNX.ts | 6 +++--- packages/bloom/lib/commands/cuckoo/COUNT.ts | 4 ++-- packages/bloom/lib/commands/cuckoo/DEL.ts | 6 +++--- packages/bloom/lib/commands/cuckoo/EXISTS.ts | 6 +++--- packages/bloom/lib/commands/cuckoo/INFO.ts | 4 ++-- packages/bloom/lib/commands/cuckoo/INSERT.ts | 6 +++--- packages/bloom/lib/commands/cuckoo/INSERTNX.ts | 2 +- packages/bloom/lib/commands/cuckoo/LOADCHUNK.ts | 4 ++-- packages/bloom/lib/commands/cuckoo/RESERVE.ts | 4 ++-- packages/bloom/lib/commands/cuckoo/SCANDUMP.ts | 4 ++-- packages/bloom/lib/commands/cuckoo/index.ts | 2 +- packages/bloom/lib/commands/t-digest/ADD.ts | 4 ++-- packages/bloom/lib/commands/t-digest/BYRANK.ts | 6 +++--- packages/bloom/lib/commands/t-digest/BYREVRANK.ts | 2 +- packages/bloom/lib/commands/t-digest/CDF.ts | 6 +++--- packages/bloom/lib/commands/t-digest/CREATE.ts | 4 ++-- packages/bloom/lib/commands/t-digest/INFO.ts | 4 ++-- packages/bloom/lib/commands/t-digest/MAX.ts | 6 +++--- packages/bloom/lib/commands/t-digest/MERGE.ts | 6 +++--- packages/bloom/lib/commands/t-digest/MIN.ts | 6 +++--- packages/bloom/lib/commands/t-digest/QUANTILE.ts | 6 +++--- packages/bloom/lib/commands/t-digest/RANK.ts | 4 ++-- packages/bloom/lib/commands/t-digest/RESET.ts | 4 ++-- packages/bloom/lib/commands/t-digest/REVRANK.ts | 2 +- packages/bloom/lib/commands/t-digest/TRIMMED_MEAN.ts | 6 +++--- packages/bloom/lib/commands/t-digest/index.ts | 2 +- packages/bloom/lib/commands/top-k/ADD.ts | 6 +++--- packages/bloom/lib/commands/top-k/COUNT.ts | 6 +++--- packages/bloom/lib/commands/top-k/INCRBY.ts | 4 ++-- packages/bloom/lib/commands/top-k/INFO.ts | 6 +++--- packages/bloom/lib/commands/top-k/LIST.ts | 4 ++-- packages/bloom/lib/commands/top-k/LIST_WITHCOUNT.ts | 4 ++-- packages/bloom/lib/commands/top-k/QUERY.ts | 6 +++--- packages/bloom/lib/commands/top-k/RESERVE.ts | 4 ++-- packages/bloom/lib/commands/top-k/index.ts | 2 +- packages/graph/lib/commands/CONFIG_GET.ts | 4 ++-- packages/graph/lib/commands/CONFIG_SET.ts | 4 ++-- packages/graph/lib/commands/DELETE.ts | 4 ++-- packages/graph/lib/commands/EXPLAIN.ts | 4 ++-- packages/graph/lib/commands/LIST.ts | 4 ++-- packages/graph/lib/commands/PROFILE.ts | 4 ++-- packages/graph/lib/commands/QUERY.ts | 4 ++-- packages/graph/lib/commands/RO_QUERY.ts | 2 +- packages/graph/lib/commands/SLOWLOG.ts | 4 ++-- packages/graph/lib/commands/index.ts | 2 +- packages/graph/lib/graph.ts | 2 +- packages/json/lib/commands/ARRAPPEND.ts | 4 ++-- packages/json/lib/commands/ARRINDEX.ts | 4 ++-- packages/json/lib/commands/ARRINSERT.ts | 4 ++-- packages/json/lib/commands/ARRLEN.ts | 4 ++-- packages/json/lib/commands/ARRPOP.ts | 6 +++--- packages/json/lib/commands/ARRTRIM.ts | 4 ++-- packages/json/lib/commands/CLEAR.ts | 4 ++-- packages/json/lib/commands/DEBUG_MEMORY.ts | 4 ++-- packages/json/lib/commands/DEL.ts | 4 ++-- packages/json/lib/commands/FORGET.ts | 4 ++-- packages/json/lib/commands/GET.ts | 6 +++--- packages/json/lib/commands/MERGE.ts | 4 ++-- packages/json/lib/commands/MGET.ts | 4 ++-- packages/json/lib/commands/MSET.ts | 4 ++-- packages/json/lib/commands/NUMINCRBY.ts | 4 ++-- packages/json/lib/commands/NUMMULTBY.ts | 4 ++-- packages/json/lib/commands/OBJKEYS.ts | 4 ++-- packages/json/lib/commands/OBJLEN.ts | 4 ++-- packages/json/lib/commands/RESP.ts | 4 ++-- packages/json/lib/commands/SET.ts | 4 ++-- packages/json/lib/commands/STRAPPEND.ts | 4 ++-- packages/json/lib/commands/STRLEN.ts | 4 ++-- packages/json/lib/commands/TOGGLE.ts | 4 ++-- packages/json/lib/commands/TYPE.ts | 4 ++-- packages/json/lib/commands/index.ts | 4 ++-- packages/search/lib/commands/AGGREGATE.ts | 6 +++--- packages/search/lib/commands/AGGREGATE_WITHCURSOR.ts | 4 ++-- packages/search/lib/commands/ALIASADD.ts | 4 ++-- packages/search/lib/commands/ALIASDEL.ts | 4 ++-- packages/search/lib/commands/ALIASUPDATE.ts | 4 ++-- packages/search/lib/commands/ALTER.ts | 4 ++-- packages/search/lib/commands/CONFIG_GET.ts | 4 ++-- packages/search/lib/commands/CONFIG_SET.ts | 4 ++-- packages/search/lib/commands/CREATE.ts | 6 +++--- packages/search/lib/commands/CURSOR_DEL.ts | 4 ++-- packages/search/lib/commands/CURSOR_READ.ts | 4 ++-- packages/search/lib/commands/DICTADD.ts | 6 +++--- packages/search/lib/commands/DICTDEL.ts | 6 +++--- packages/search/lib/commands/DICTDUMP.ts | 4 ++-- packages/search/lib/commands/DROPINDEX.ts | 4 ++-- packages/search/lib/commands/EXPLAIN.ts | 4 ++-- packages/search/lib/commands/EXPLAINCLI.ts | 4 ++-- packages/search/lib/commands/INFO.ts | 8 ++++---- packages/search/lib/commands/PROFILE_AGGREGATE.ts | 4 ++-- packages/search/lib/commands/PROFILE_SEARCH.ts | 4 ++-- packages/search/lib/commands/SEARCH.ts | 6 +++--- packages/search/lib/commands/SEARCH_NOCONTENT.ts | 2 +- packages/search/lib/commands/SPELLCHECK.ts | 4 ++-- packages/search/lib/commands/SUGADD.ts | 4 ++-- packages/search/lib/commands/SUGDEL.ts | 4 ++-- packages/search/lib/commands/SUGGET.ts | 4 ++-- packages/search/lib/commands/SUGGET_WITHPAYLOADS.ts | 4 ++-- packages/search/lib/commands/SUGGET_WITHSCORES.ts | 4 ++-- .../search/lib/commands/SUGGET_WITHSCORES_WITHPAYLOADS.ts | 4 ++-- packages/search/lib/commands/SUGLEN.ts | 4 ++-- packages/search/lib/commands/SYNDUMP.ts | 4 ++-- packages/search/lib/commands/SYNUPDATE.ts | 6 +++--- packages/search/lib/commands/TAGVALS.ts | 4 ++-- packages/search/lib/commands/_LIST.ts | 4 ++-- packages/test-utils/lib/dockers.ts | 2 +- packages/time-series/lib/commands/ADD.ts | 4 ++-- packages/time-series/lib/commands/ALTER.ts | 4 ++-- packages/time-series/lib/commands/CREATE.ts | 4 ++-- packages/time-series/lib/commands/CREATERULE.ts | 4 ++-- packages/time-series/lib/commands/DECRBY.ts | 2 +- packages/time-series/lib/commands/DEL.ts | 4 ++-- packages/time-series/lib/commands/DELETERULE.ts | 4 ++-- packages/time-series/lib/commands/GET.ts | 4 ++-- packages/time-series/lib/commands/INCRBY.ts | 4 ++-- packages/time-series/lib/commands/INFO.ts | 6 +++--- packages/time-series/lib/commands/INFO_DEBUG.ts | 4 ++-- packages/time-series/lib/commands/MADD.ts | 4 ++-- packages/time-series/lib/commands/MGET.ts | 6 +++--- packages/time-series/lib/commands/MGET_SELECTED_LABELS.ts | 6 +++--- packages/time-series/lib/commands/MGET_WITHLABELS.ts | 6 +++--- packages/time-series/lib/commands/MRANGE.ts | 6 +++--- packages/time-series/lib/commands/MRANGE_GROUPBY.ts | 6 +++--- .../time-series/lib/commands/MRANGE_SELECTED_LABELS.ts | 6 +++--- .../lib/commands/MRANGE_SELECTED_LABELS_GROUPBY.ts | 6 +++--- packages/time-series/lib/commands/MRANGE_WITHLABELS.ts | 6 +++--- .../time-series/lib/commands/MRANGE_WITHLABELS_GROUPBY.ts | 6 +++--- packages/time-series/lib/commands/MREVRANGE.ts | 2 +- packages/time-series/lib/commands/MREVRANGE_GROUPBY.ts | 2 +- .../time-series/lib/commands/MREVRANGE_SELECTED_LABELS.ts | 2 +- .../lib/commands/MREVRANGE_SELECTED_LABELS_GROUPBY.ts | 2 +- packages/time-series/lib/commands/MREVRANGE_WITHLABELS.ts | 2 +- .../lib/commands/MREVRANGE_WITHLABELS_GROUPBY.ts | 2 +- packages/time-series/lib/commands/QUERYINDEX.ts | 6 +++--- packages/time-series/lib/commands/RANGE.ts | 6 +++--- packages/time-series/lib/commands/REVRANGE.ts | 2 +- packages/time-series/lib/commands/index.ts | 8 ++++---- 156 files changed, 340 insertions(+), 340 deletions(-) diff --git a/packages/bloom/lib/commands/bloom/ADD.ts b/packages/bloom/lib/commands/bloom/ADD.ts index f394755acc4..e12d9cfa1d2 100644 --- a/packages/bloom/lib/commands/bloom/ADD.ts +++ b/packages/bloom/lib/commands/bloom/ADD.ts @@ -1,6 +1,6 @@ -import { CommandParser } from '@redis/client/lib/client/parser'; -import { RedisArgument, Command } from '@redis/client/lib/RESP/types'; -import { transformBooleanReply } from '@redis/client/lib/commands/generic-transformers'; +import { CommandParser } from '@redis/client/dist/lib/client/parser'; +import { RedisArgument, Command } from '@redis/client/dist/lib/RESP/types'; +import { transformBooleanReply } from '@redis/client/dist/lib/commands/generic-transformers'; export default { IS_READ_ONLY: false, diff --git a/packages/bloom/lib/commands/bloom/CARD.ts b/packages/bloom/lib/commands/bloom/CARD.ts index 1d206b30af9..c2f9aeb00fc 100644 --- a/packages/bloom/lib/commands/bloom/CARD.ts +++ b/packages/bloom/lib/commands/bloom/CARD.ts @@ -1,5 +1,5 @@ -import { CommandParser } from '@redis/client/lib/client/parser'; -import { RedisArgument, NumberReply, Command } from '@redis/client/lib/RESP/types'; +import { CommandParser } from '@redis/client/dist/lib/client/parser'; +import { RedisArgument, NumberReply, Command } from '@redis/client/dist/lib/RESP/types'; export default { IS_READ_ONLY: true, diff --git a/packages/bloom/lib/commands/bloom/EXISTS.ts b/packages/bloom/lib/commands/bloom/EXISTS.ts index 3de18dd07cd..b3f19af9516 100644 --- a/packages/bloom/lib/commands/bloom/EXISTS.ts +++ b/packages/bloom/lib/commands/bloom/EXISTS.ts @@ -1,6 +1,6 @@ -import { CommandParser } from '@redis/client/lib/client/parser'; -import { RedisArgument, Command } from '@redis/client/lib/RESP/types'; -import { transformBooleanReply } from '@redis/client/lib/commands/generic-transformers'; +import { CommandParser } from '@redis/client/dist/lib/client/parser'; +import { RedisArgument, Command } from '@redis/client/dist/lib/RESP/types'; +import { transformBooleanReply } from '@redis/client/dist/lib/commands/generic-transformers'; export default { IS_READ_ONLY: true, diff --git a/packages/bloom/lib/commands/bloom/INFO.ts b/packages/bloom/lib/commands/bloom/INFO.ts index b1e3f137c8c..b715bf57738 100644 --- a/packages/bloom/lib/commands/bloom/INFO.ts +++ b/packages/bloom/lib/commands/bloom/INFO.ts @@ -1,5 +1,5 @@ -import { CommandParser } from '@redis/client/lib/client/parser'; -import { RedisArgument, Command, UnwrapReply, NullReply, NumberReply, TuplesToMapReply, Resp2Reply, SimpleStringReply, TypeMapping } from '@redis/client/lib/RESP/types'; +import { CommandParser } from '@redis/client/dist/lib/client/parser'; +import { RedisArgument, Command, UnwrapReply, NullReply, NumberReply, TuplesToMapReply, Resp2Reply, SimpleStringReply, TypeMapping } from '@redis/client/dist/lib/RESP/types'; import { transformInfoV2Reply } from '.'; export type BfInfoReplyMap = TuplesToMapReply<[ diff --git a/packages/bloom/lib/commands/bloom/INSERT.ts b/packages/bloom/lib/commands/bloom/INSERT.ts index 14831f2f11a..b8dcef325f8 100644 --- a/packages/bloom/lib/commands/bloom/INSERT.ts +++ b/packages/bloom/lib/commands/bloom/INSERT.ts @@ -1,7 +1,7 @@ -import { CommandParser } from '@redis/client/lib/client/parser'; -import { RedisArgument, Command } from '@redis/client/lib/RESP/types'; -import { RedisVariadicArgument } from '@redis/client/lib/commands/generic-transformers'; -import { transformBooleanArrayReply } from '@redis/client/lib/commands/generic-transformers'; +import { CommandParser } from '@redis/client/dist/lib/client/parser'; +import { RedisArgument, Command } from '@redis/client/dist/lib/RESP/types'; +import { RedisVariadicArgument } from '@redis/client/dist/lib/commands/generic-transformers'; +import { transformBooleanArrayReply } from '@redis/client/dist/lib/commands/generic-transformers'; export interface BfInsertOptions { CAPACITY?: number; diff --git a/packages/bloom/lib/commands/bloom/LOADCHUNK.ts b/packages/bloom/lib/commands/bloom/LOADCHUNK.ts index 47036e042af..ef3cc4a3e12 100644 --- a/packages/bloom/lib/commands/bloom/LOADCHUNK.ts +++ b/packages/bloom/lib/commands/bloom/LOADCHUNK.ts @@ -1,5 +1,5 @@ -import { CommandParser } from '@redis/client/lib/client/parser'; -import { RedisArgument, SimpleStringReply, Command } from '@redis/client/lib/RESP/types'; +import { CommandParser } from '@redis/client/dist/lib/client/parser'; +import { RedisArgument, SimpleStringReply, Command } from '@redis/client/dist/lib/RESP/types'; export default { IS_READ_ONLY: false, diff --git a/packages/bloom/lib/commands/bloom/MADD.ts b/packages/bloom/lib/commands/bloom/MADD.ts index fda7419bf19..efbd932b403 100644 --- a/packages/bloom/lib/commands/bloom/MADD.ts +++ b/packages/bloom/lib/commands/bloom/MADD.ts @@ -1,7 +1,7 @@ -import { CommandParser } from '@redis/client/lib/client/parser'; -import { RedisArgument, Command } from '@redis/client/lib/RESP/types'; -import { RedisVariadicArgument } from '@redis/client/lib/commands/generic-transformers'; -import { transformBooleanArrayReply } from '@redis/client/lib/commands/generic-transformers'; +import { CommandParser } from '@redis/client/dist/lib/client/parser'; +import { RedisArgument, Command } from '@redis/client/dist/lib/RESP/types'; +import { RedisVariadicArgument } from '@redis/client/dist/lib/commands/generic-transformers'; +import { transformBooleanArrayReply } from '@redis/client/dist/lib/commands/generic-transformers'; export default { IS_READ_ONLY: false, diff --git a/packages/bloom/lib/commands/bloom/MEXISTS.ts b/packages/bloom/lib/commands/bloom/MEXISTS.ts index acd85786f97..a5a311a8e4c 100644 --- a/packages/bloom/lib/commands/bloom/MEXISTS.ts +++ b/packages/bloom/lib/commands/bloom/MEXISTS.ts @@ -1,7 +1,7 @@ -import { CommandParser } from '@redis/client/lib/client/parser'; -import { RedisArgument, Command } from '@redis/client/lib/RESP/types'; -import { RedisVariadicArgument } from '@redis/client/lib/commands/generic-transformers'; -import { transformBooleanArrayReply } from '@redis/client/lib/commands/generic-transformers'; +import { CommandParser } from '@redis/client/dist/lib/client/parser'; +import { RedisArgument, Command } from '@redis/client/dist/lib/RESP/types'; +import { RedisVariadicArgument } from '@redis/client/dist/lib/commands/generic-transformers'; +import { transformBooleanArrayReply } from '@redis/client/dist/lib/commands/generic-transformers'; export default { IS_READ_ONLY: true, diff --git a/packages/bloom/lib/commands/bloom/RESERVE.ts b/packages/bloom/lib/commands/bloom/RESERVE.ts index aff155ab769..00f17c1889f 100644 --- a/packages/bloom/lib/commands/bloom/RESERVE.ts +++ b/packages/bloom/lib/commands/bloom/RESERVE.ts @@ -1,5 +1,5 @@ -import { CommandParser } from '@redis/client/lib/client/parser'; -import { RedisArgument, SimpleStringReply, Command } from '@redis/client/lib/RESP/types'; +import { CommandParser } from '@redis/client/dist/lib/client/parser'; +import { RedisArgument, SimpleStringReply, Command } from '@redis/client/dist/lib/RESP/types'; export interface BfReserveOptions { EXPANSION?: number; diff --git a/packages/bloom/lib/commands/bloom/SCANDUMP.ts b/packages/bloom/lib/commands/bloom/SCANDUMP.ts index 37b1f57045c..d0472b649c5 100644 --- a/packages/bloom/lib/commands/bloom/SCANDUMP.ts +++ b/packages/bloom/lib/commands/bloom/SCANDUMP.ts @@ -1,5 +1,5 @@ -import { CommandParser } from '@redis/client/lib/client/parser'; -import { RedisArgument, TuplesReply, NumberReply, BlobStringReply, UnwrapReply, Command } from '@redis/client/lib/RESP/types'; +import { CommandParser } from '@redis/client/dist/lib/client/parser'; +import { RedisArgument, TuplesReply, NumberReply, BlobStringReply, UnwrapReply, Command } from '@redis/client/dist/lib/RESP/types'; export default { IS_READ_ONLY: true, diff --git a/packages/bloom/lib/commands/bloom/index.ts b/packages/bloom/lib/commands/bloom/index.ts index e87f57220dd..a93f79c9c56 100644 --- a/packages/bloom/lib/commands/bloom/index.ts +++ b/packages/bloom/lib/commands/bloom/index.ts @@ -1,4 +1,4 @@ -import type { RedisCommands, TypeMapping } from '@redis/client/lib/RESP/types'; +import type { RedisCommands, TypeMapping } from '@redis/client/dist/lib/RESP/types'; import ADD from './ADD'; import CARD from './CARD'; diff --git a/packages/bloom/lib/commands/count-min-sketch/INCRBY.ts b/packages/bloom/lib/commands/count-min-sketch/INCRBY.ts index 39cc52d31de..a011957ece6 100644 --- a/packages/bloom/lib/commands/count-min-sketch/INCRBY.ts +++ b/packages/bloom/lib/commands/count-min-sketch/INCRBY.ts @@ -1,5 +1,5 @@ -import { CommandParser } from '@redis/client/lib/client/parser'; -import { RedisArgument, ArrayReply, NumberReply, Command } from '@redis/client/lib/RESP/types'; +import { CommandParser } from '@redis/client/dist/lib/client/parser'; +import { RedisArgument, ArrayReply, NumberReply, Command } from '@redis/client/dist/lib/RESP/types'; export interface BfIncrByItem { item: RedisArgument; diff --git a/packages/bloom/lib/commands/count-min-sketch/INFO.ts b/packages/bloom/lib/commands/count-min-sketch/INFO.ts index 9b77409f2d1..fef1cac97e7 100644 --- a/packages/bloom/lib/commands/count-min-sketch/INFO.ts +++ b/packages/bloom/lib/commands/count-min-sketch/INFO.ts @@ -1,5 +1,5 @@ -import { CommandParser } from '@redis/client/lib/client/parser'; -import { RedisArgument, TuplesToMapReply, NumberReply, UnwrapReply, Resp2Reply, Command, SimpleStringReply, TypeMapping } from '@redis/client/lib/RESP/types'; +import { CommandParser } from '@redis/client/dist/lib/client/parser'; +import { RedisArgument, TuplesToMapReply, NumberReply, UnwrapReply, Resp2Reply, Command, SimpleStringReply, TypeMapping } from '@redis/client/dist/lib/RESP/types'; import { transformInfoV2Reply } from '../bloom'; export type CmsInfoReplyMap = TuplesToMapReply<[ diff --git a/packages/bloom/lib/commands/count-min-sketch/INITBYDIM.ts b/packages/bloom/lib/commands/count-min-sketch/INITBYDIM.ts index cd295d0696e..44e6a75952f 100644 --- a/packages/bloom/lib/commands/count-min-sketch/INITBYDIM.ts +++ b/packages/bloom/lib/commands/count-min-sketch/INITBYDIM.ts @@ -1,5 +1,5 @@ -import { CommandParser } from '@redis/client/lib/client/parser'; -import { RedisArgument, SimpleStringReply, Command } from '@redis/client/lib/RESP/types'; +import { CommandParser } from '@redis/client/dist/lib/client/parser'; +import { RedisArgument, SimpleStringReply, Command } from '@redis/client/dist/lib/RESP/types'; export default { IS_READ_ONLY: false, diff --git a/packages/bloom/lib/commands/count-min-sketch/INITBYPROB.ts b/packages/bloom/lib/commands/count-min-sketch/INITBYPROB.ts index e7e85d4100b..3b96120bd04 100644 --- a/packages/bloom/lib/commands/count-min-sketch/INITBYPROB.ts +++ b/packages/bloom/lib/commands/count-min-sketch/INITBYPROB.ts @@ -1,5 +1,5 @@ -import { CommandParser } from '@redis/client/lib/client/parser'; -import { RedisArgument, SimpleStringReply, Command } from '@redis/client/lib/RESP/types'; +import { CommandParser } from '@redis/client/dist/lib/client/parser'; +import { RedisArgument, SimpleStringReply, Command } from '@redis/client/dist/lib/RESP/types'; export default { IS_READ_ONLY: false, diff --git a/packages/bloom/lib/commands/count-min-sketch/MERGE.ts b/packages/bloom/lib/commands/count-min-sketch/MERGE.ts index cc0fc929070..4d959bd619d 100644 --- a/packages/bloom/lib/commands/count-min-sketch/MERGE.ts +++ b/packages/bloom/lib/commands/count-min-sketch/MERGE.ts @@ -1,5 +1,5 @@ -import { CommandParser } from '@redis/client/lib/client/parser'; -import { RedisArgument, SimpleStringReply, Command } from '@redis/client/lib/RESP/types'; +import { CommandParser } from '@redis/client/dist/lib/client/parser'; +import { RedisArgument, SimpleStringReply, Command } from '@redis/client/dist/lib/RESP/types'; interface BfMergeSketch { name: RedisArgument; diff --git a/packages/bloom/lib/commands/count-min-sketch/QUERY.ts b/packages/bloom/lib/commands/count-min-sketch/QUERY.ts index 54d838c1935..b55b51d1bbd 100644 --- a/packages/bloom/lib/commands/count-min-sketch/QUERY.ts +++ b/packages/bloom/lib/commands/count-min-sketch/QUERY.ts @@ -1,6 +1,6 @@ -import { CommandParser } from '@redis/client/lib/client/parser'; -import { ArrayReply, NumberReply, Command, RedisArgument } from '@redis/client/lib/RESP/types'; -import { RedisVariadicArgument } from '@redis/client/lib/commands/generic-transformers'; +import { CommandParser } from '@redis/client/dist/lib/client/parser'; +import { ArrayReply, NumberReply, Command, RedisArgument } from '@redis/client/dist/lib/RESP/types'; +import { RedisVariadicArgument } from '@redis/client/dist/lib/commands/generic-transformers'; export default { IS_READ_ONLY: true, diff --git a/packages/bloom/lib/commands/count-min-sketch/index.ts b/packages/bloom/lib/commands/count-min-sketch/index.ts index 1132a7524e1..4f0f395ca3d 100644 --- a/packages/bloom/lib/commands/count-min-sketch/index.ts +++ b/packages/bloom/lib/commands/count-min-sketch/index.ts @@ -1,4 +1,4 @@ -import type { RedisCommands } from '@redis/client/lib/RESP/types'; +import type { RedisCommands } from '@redis/client/dist/lib/RESP/types'; import INCRBY from './INCRBY'; import INFO from './INFO'; import INITBYDIM from './INITBYDIM'; diff --git a/packages/bloom/lib/commands/cuckoo/ADD.ts b/packages/bloom/lib/commands/cuckoo/ADD.ts index 3d0dc77be2d..37a5d1b5b86 100644 --- a/packages/bloom/lib/commands/cuckoo/ADD.ts +++ b/packages/bloom/lib/commands/cuckoo/ADD.ts @@ -1,6 +1,6 @@ -import { CommandParser } from '@redis/client/lib/client/parser'; -import { RedisArgument, Command } from '@redis/client/lib/RESP/types'; -import { transformBooleanReply } from '@redis/client/lib/commands/generic-transformers'; +import { CommandParser } from '@redis/client/dist/lib/client/parser'; +import { RedisArgument, Command } from '@redis/client/dist/lib/RESP/types'; +import { transformBooleanReply } from '@redis/client/dist/lib/commands/generic-transformers'; export default { IS_READ_ONLY: false, diff --git a/packages/bloom/lib/commands/cuckoo/ADDNX.ts b/packages/bloom/lib/commands/cuckoo/ADDNX.ts index f358f1581ec..ceaf62be21c 100644 --- a/packages/bloom/lib/commands/cuckoo/ADDNX.ts +++ b/packages/bloom/lib/commands/cuckoo/ADDNX.ts @@ -1,6 +1,6 @@ -import { CommandParser } from '@redis/client/lib/client/parser'; -import { RedisArgument, Command } from '@redis/client/lib/RESP/types'; -import { transformBooleanReply } from '@redis/client/lib/commands/generic-transformers'; +import { CommandParser } from '@redis/client/dist/lib/client/parser'; +import { RedisArgument, Command } from '@redis/client/dist/lib/RESP/types'; +import { transformBooleanReply } from '@redis/client/dist/lib/commands/generic-transformers'; export default { IS_READ_ONLY: false, diff --git a/packages/bloom/lib/commands/cuckoo/COUNT.ts b/packages/bloom/lib/commands/cuckoo/COUNT.ts index 512b0143272..f0cd5a72105 100644 --- a/packages/bloom/lib/commands/cuckoo/COUNT.ts +++ b/packages/bloom/lib/commands/cuckoo/COUNT.ts @@ -1,5 +1,5 @@ -import { CommandParser } from '@redis/client/lib/client/parser'; -import { RedisArgument, NumberReply, Command } from '@redis/client/lib/RESP/types'; +import { CommandParser } from '@redis/client/dist/lib/client/parser'; +import { RedisArgument, NumberReply, Command } from '@redis/client/dist/lib/RESP/types'; export default { IS_READ_ONLY: true, diff --git a/packages/bloom/lib/commands/cuckoo/DEL.ts b/packages/bloom/lib/commands/cuckoo/DEL.ts index 0b2bdaea990..c97b7c2d9fc 100644 --- a/packages/bloom/lib/commands/cuckoo/DEL.ts +++ b/packages/bloom/lib/commands/cuckoo/DEL.ts @@ -1,6 +1,6 @@ -import { CommandParser } from '@redis/client/lib/client/parser'; -import { RedisArgument, Command } from '@redis/client/lib/RESP/types'; -import { transformBooleanReply } from '@redis/client/lib/commands/generic-transformers'; +import { CommandParser } from '@redis/client/dist/lib/client/parser'; +import { RedisArgument, Command } from '@redis/client/dist/lib/RESP/types'; +import { transformBooleanReply } from '@redis/client/dist/lib/commands/generic-transformers'; export default { IS_READ_ONLY: false, diff --git a/packages/bloom/lib/commands/cuckoo/EXISTS.ts b/packages/bloom/lib/commands/cuckoo/EXISTS.ts index ef93462990b..2299cb3de99 100644 --- a/packages/bloom/lib/commands/cuckoo/EXISTS.ts +++ b/packages/bloom/lib/commands/cuckoo/EXISTS.ts @@ -1,6 +1,6 @@ -import { CommandParser } from '@redis/client/lib/client/parser'; -import { RedisArgument, Command } from '@redis/client/lib/RESP/types'; -import { transformBooleanReply } from '@redis/client/lib/commands/generic-transformers'; +import { CommandParser } from '@redis/client/dist/lib/client/parser'; +import { RedisArgument, Command } from '@redis/client/dist/lib/RESP/types'; +import { transformBooleanReply } from '@redis/client/dist/lib/commands/generic-transformers'; export default { IS_READ_ONLY: false, diff --git a/packages/bloom/lib/commands/cuckoo/INFO.ts b/packages/bloom/lib/commands/cuckoo/INFO.ts index 15972b206ff..6a8f06f1e77 100644 --- a/packages/bloom/lib/commands/cuckoo/INFO.ts +++ b/packages/bloom/lib/commands/cuckoo/INFO.ts @@ -1,5 +1,5 @@ -import { CommandParser } from '@redis/client/lib/client/parser'; -import { RedisArgument, Command, NumberReply, TuplesToMapReply, UnwrapReply, Resp2Reply, SimpleStringReply, TypeMapping } from '@redis/client/lib/RESP/types'; +import { CommandParser } from '@redis/client/dist/lib/client/parser'; +import { RedisArgument, Command, NumberReply, TuplesToMapReply, UnwrapReply, Resp2Reply, SimpleStringReply, TypeMapping } from '@redis/client/dist/lib/RESP/types'; import { transformInfoV2Reply } from '../bloom'; export type CfInfoReplyMap = TuplesToMapReply<[ diff --git a/packages/bloom/lib/commands/cuckoo/INSERT.ts b/packages/bloom/lib/commands/cuckoo/INSERT.ts index 75534e0a7fa..3ad3feee16d 100644 --- a/packages/bloom/lib/commands/cuckoo/INSERT.ts +++ b/packages/bloom/lib/commands/cuckoo/INSERT.ts @@ -1,6 +1,6 @@ -import { CommandParser } from '@redis/client/lib/client/parser'; -import { Command, RedisArgument } from '@redis/client/lib/RESP/types'; -import { RedisVariadicArgument, transformBooleanArrayReply } from '@redis/client/lib/commands/generic-transformers'; +import { CommandParser } from '@redis/client/dist/lib/client/parser'; +import { Command, RedisArgument } from '@redis/client/dist/lib/RESP/types'; +import { RedisVariadicArgument, transformBooleanArrayReply } from '@redis/client/dist/lib/commands/generic-transformers'; export interface CfInsertOptions { CAPACITY?: number; diff --git a/packages/bloom/lib/commands/cuckoo/INSERTNX.ts b/packages/bloom/lib/commands/cuckoo/INSERTNX.ts index 581cfcd9e60..7ddc952e2fa 100644 --- a/packages/bloom/lib/commands/cuckoo/INSERTNX.ts +++ b/packages/bloom/lib/commands/cuckoo/INSERTNX.ts @@ -1,4 +1,4 @@ -import { Command } from '@redis/client/lib/RESP/types'; +import { Command } from '@redis/client/dist/lib/RESP/types'; import INSERT, { parseCfInsertArguments } from './INSERT'; export default { diff --git a/packages/bloom/lib/commands/cuckoo/LOADCHUNK.ts b/packages/bloom/lib/commands/cuckoo/LOADCHUNK.ts index 420774a6504..8fb21be8e0d 100644 --- a/packages/bloom/lib/commands/cuckoo/LOADCHUNK.ts +++ b/packages/bloom/lib/commands/cuckoo/LOADCHUNK.ts @@ -1,5 +1,5 @@ -import { CommandParser } from '@redis/client/lib/client/parser'; -import { SimpleStringReply, Command, RedisArgument } from '@redis/client/lib/RESP/types'; +import { CommandParser } from '@redis/client/dist/lib/client/parser'; +import { SimpleStringReply, Command, RedisArgument } from '@redis/client/dist/lib/RESP/types'; export default { IS_READ_ONLY: false, diff --git a/packages/bloom/lib/commands/cuckoo/RESERVE.ts b/packages/bloom/lib/commands/cuckoo/RESERVE.ts index ba401dcdee9..2685b0db06d 100644 --- a/packages/bloom/lib/commands/cuckoo/RESERVE.ts +++ b/packages/bloom/lib/commands/cuckoo/RESERVE.ts @@ -1,5 +1,5 @@ -import { CommandParser } from '@redis/client/lib/client/parser'; -import { RedisArgument, SimpleStringReply, Command } from '@redis/client/lib/RESP/types'; +import { CommandParser } from '@redis/client/dist/lib/client/parser'; +import { RedisArgument, SimpleStringReply, Command } from '@redis/client/dist/lib/RESP/types'; export interface CfReserveOptions { BUCKETSIZE?: number; diff --git a/packages/bloom/lib/commands/cuckoo/SCANDUMP.ts b/packages/bloom/lib/commands/cuckoo/SCANDUMP.ts index aab7a5eecc2..25ef2c3f6da 100644 --- a/packages/bloom/lib/commands/cuckoo/SCANDUMP.ts +++ b/packages/bloom/lib/commands/cuckoo/SCANDUMP.ts @@ -1,5 +1,5 @@ -import { CommandParser } from '@redis/client/lib/client/parser'; -import { RedisArgument, TuplesReply, NumberReply, BlobStringReply, NullReply, UnwrapReply, Command } from '@redis/client/lib/RESP/types'; +import { CommandParser } from '@redis/client/dist/lib/client/parser'; +import { RedisArgument, TuplesReply, NumberReply, BlobStringReply, NullReply, UnwrapReply, Command } from '@redis/client/dist/lib/RESP/types'; export default { IS_READ_ONLY: true, diff --git a/packages/bloom/lib/commands/cuckoo/index.ts b/packages/bloom/lib/commands/cuckoo/index.ts index 7ee99b41cd3..62c63fe8d19 100644 --- a/packages/bloom/lib/commands/cuckoo/index.ts +++ b/packages/bloom/lib/commands/cuckoo/index.ts @@ -1,4 +1,4 @@ -import type { RedisCommands } from '@redis/client/lib/RESP/types'; +import type { RedisCommands } from '@redis/client/dist/lib/RESP/types'; import ADD from './ADD'; import ADDNX from './ADDNX'; import COUNT from './COUNT'; diff --git a/packages/bloom/lib/commands/t-digest/ADD.ts b/packages/bloom/lib/commands/t-digest/ADD.ts index d9d67144a95..5534d58065b 100644 --- a/packages/bloom/lib/commands/t-digest/ADD.ts +++ b/packages/bloom/lib/commands/t-digest/ADD.ts @@ -1,5 +1,5 @@ -import { CommandParser } from '@redis/client/lib/client/parser'; -import { SimpleStringReply, Command, RedisArgument } from '@redis/client/lib/RESP/types'; +import { CommandParser } from '@redis/client/dist/lib/client/parser'; +import { SimpleStringReply, Command, RedisArgument } from '@redis/client/dist/lib/RESP/types'; export default { IS_READ_ONLY: false, diff --git a/packages/bloom/lib/commands/t-digest/BYRANK.ts b/packages/bloom/lib/commands/t-digest/BYRANK.ts index 126c9963e71..9c1ab0059f3 100644 --- a/packages/bloom/lib/commands/t-digest/BYRANK.ts +++ b/packages/bloom/lib/commands/t-digest/BYRANK.ts @@ -1,6 +1,6 @@ -import { CommandParser } from '@redis/client/lib/client/parser'; -import { RedisArgument, Command } from '@redis/client/lib/RESP/types'; -import { transformDoubleArrayReply } from '@redis/client/lib/commands/generic-transformers'; +import { CommandParser } from '@redis/client/dist/lib/client/parser'; +import { RedisArgument, Command } from '@redis/client/dist/lib/RESP/types'; +import { transformDoubleArrayReply } from '@redis/client/dist/lib/commands/generic-transformers'; export function transformByRankArguments( parser: CommandParser, diff --git a/packages/bloom/lib/commands/t-digest/BYREVRANK.ts b/packages/bloom/lib/commands/t-digest/BYREVRANK.ts index 4995bf86cc0..8721c081e7a 100644 --- a/packages/bloom/lib/commands/t-digest/BYREVRANK.ts +++ b/packages/bloom/lib/commands/t-digest/BYREVRANK.ts @@ -1,4 +1,4 @@ -import { Command } from '@redis/client/lib/RESP/types'; +import { Command } from '@redis/client/dist/lib/RESP/types'; import BYRANK, { transformByRankArguments } from './BYRANK'; export default { diff --git a/packages/bloom/lib/commands/t-digest/CDF.ts b/packages/bloom/lib/commands/t-digest/CDF.ts index e786dc4c8a8..4d1d8ea2786 100644 --- a/packages/bloom/lib/commands/t-digest/CDF.ts +++ b/packages/bloom/lib/commands/t-digest/CDF.ts @@ -1,6 +1,6 @@ -import { CommandParser } from '@redis/client/lib/client/parser'; -import { RedisArgument, Command } from '@redis/client/lib/RESP/types'; -import { transformDoubleArrayReply } from '@redis/client/lib/commands/generic-transformers'; +import { CommandParser } from '@redis/client/dist/lib/client/parser'; +import { RedisArgument, Command } from '@redis/client/dist/lib/RESP/types'; +import { transformDoubleArrayReply } from '@redis/client/dist/lib/commands/generic-transformers'; export default { IS_READ_ONLY: true, diff --git a/packages/bloom/lib/commands/t-digest/CREATE.ts b/packages/bloom/lib/commands/t-digest/CREATE.ts index fd160cce842..58b1e008284 100644 --- a/packages/bloom/lib/commands/t-digest/CREATE.ts +++ b/packages/bloom/lib/commands/t-digest/CREATE.ts @@ -1,5 +1,5 @@ -import { CommandParser } from '@redis/client/lib/client/parser'; -import { RedisArgument, SimpleStringReply, Command } from '@redis/client/lib/RESP/types'; +import { CommandParser } from '@redis/client/dist/lib/client/parser'; +import { RedisArgument, SimpleStringReply, Command } from '@redis/client/dist/lib/RESP/types'; export interface TDigestCreateOptions { COMPRESSION?: number; diff --git a/packages/bloom/lib/commands/t-digest/INFO.ts b/packages/bloom/lib/commands/t-digest/INFO.ts index 43624e8f9b9..2cb9e93443c 100644 --- a/packages/bloom/lib/commands/t-digest/INFO.ts +++ b/packages/bloom/lib/commands/t-digest/INFO.ts @@ -1,5 +1,5 @@ -import { CommandParser } from '@redis/client/lib/client/parser'; -import { RedisArgument, Command, NumberReply, TuplesToMapReply, UnwrapReply, Resp2Reply, SimpleStringReply, TypeMapping } from '@redis/client/lib/RESP/types'; +import { CommandParser } from '@redis/client/dist/lib/client/parser'; +import { RedisArgument, Command, NumberReply, TuplesToMapReply, UnwrapReply, Resp2Reply, SimpleStringReply, TypeMapping } from '@redis/client/dist/lib/RESP/types'; import { transformInfoV2Reply } from '../bloom'; export type TdInfoReplyMap = TuplesToMapReply<[ diff --git a/packages/bloom/lib/commands/t-digest/MAX.ts b/packages/bloom/lib/commands/t-digest/MAX.ts index 64852d8e6dc..140db6a3e48 100644 --- a/packages/bloom/lib/commands/t-digest/MAX.ts +++ b/packages/bloom/lib/commands/t-digest/MAX.ts @@ -1,6 +1,6 @@ -import { CommandParser } from '@redis/client/lib/client/parser'; -import { RedisArgument, Command } from '@redis/client/lib/RESP/types'; -import { transformDoubleReply } from '@redis/client/lib/commands/generic-transformers'; +import { CommandParser } from '@redis/client/dist/lib/client/parser'; +import { RedisArgument, Command } from '@redis/client/dist/lib/RESP/types'; +import { transformDoubleReply } from '@redis/client/dist/lib/commands/generic-transformers'; export default { IS_READ_ONLY: true, diff --git a/packages/bloom/lib/commands/t-digest/MERGE.ts b/packages/bloom/lib/commands/t-digest/MERGE.ts index 1031f0e9170..80049d1e540 100644 --- a/packages/bloom/lib/commands/t-digest/MERGE.ts +++ b/packages/bloom/lib/commands/t-digest/MERGE.ts @@ -1,6 +1,6 @@ -import { CommandParser } from '@redis/client/lib/client/parser'; -import { RedisArgument, SimpleStringReply, Command } from '@redis/client/lib/RESP/types'; -import { RedisVariadicArgument } from '@redis/client/lib/commands/generic-transformers'; +import { CommandParser } from '@redis/client/dist/lib/client/parser'; +import { RedisArgument, SimpleStringReply, Command } from '@redis/client/dist/lib/RESP/types'; +import { RedisVariadicArgument } from '@redis/client/dist/lib/commands/generic-transformers'; export interface TDigestMergeOptions { COMPRESSION?: number; diff --git a/packages/bloom/lib/commands/t-digest/MIN.ts b/packages/bloom/lib/commands/t-digest/MIN.ts index cc44dbbea4e..d6e56fb672e 100644 --- a/packages/bloom/lib/commands/t-digest/MIN.ts +++ b/packages/bloom/lib/commands/t-digest/MIN.ts @@ -1,6 +1,6 @@ -import { CommandParser } from '@redis/client/lib/client/parser'; -import { RedisArgument, Command } from '@redis/client/lib/RESP/types'; -import { transformDoubleReply } from '@redis/client/lib/commands/generic-transformers'; +import { CommandParser } from '@redis/client/dist/lib/client/parser'; +import { RedisArgument, Command } from '@redis/client/dist/lib/RESP/types'; +import { transformDoubleReply } from '@redis/client/dist/lib/commands/generic-transformers'; export default { IS_READ_ONLY: true, diff --git a/packages/bloom/lib/commands/t-digest/QUANTILE.ts b/packages/bloom/lib/commands/t-digest/QUANTILE.ts index 962c3a9b4ca..1c27b5f6ec6 100644 --- a/packages/bloom/lib/commands/t-digest/QUANTILE.ts +++ b/packages/bloom/lib/commands/t-digest/QUANTILE.ts @@ -1,6 +1,6 @@ -import { CommandParser } from '@redis/client/lib/client/parser'; -import { RedisArgument, Command } from '@redis/client/lib/RESP/types'; -import { transformDoubleArrayReply } from '@redis/client/lib/commands/generic-transformers'; +import { CommandParser } from '@redis/client/dist/lib/client/parser'; +import { RedisArgument, Command } from '@redis/client/dist/lib/RESP/types'; +import { transformDoubleArrayReply } from '@redis/client/dist/lib/commands/generic-transformers'; export default { IS_READ_ONLY: true, diff --git a/packages/bloom/lib/commands/t-digest/RANK.ts b/packages/bloom/lib/commands/t-digest/RANK.ts index 316ca74b542..053c0c544e9 100644 --- a/packages/bloom/lib/commands/t-digest/RANK.ts +++ b/packages/bloom/lib/commands/t-digest/RANK.ts @@ -1,5 +1,5 @@ -import { CommandParser } from '@redis/client/lib/client/parser'; -import { RedisArgument, ArrayReply, NumberReply, Command } from '@redis/client/lib/RESP/types'; +import { CommandParser } from '@redis/client/dist/lib/client/parser'; +import { RedisArgument, ArrayReply, NumberReply, Command } from '@redis/client/dist/lib/RESP/types'; export function transformRankArguments( parser: CommandParser, diff --git a/packages/bloom/lib/commands/t-digest/RESET.ts b/packages/bloom/lib/commands/t-digest/RESET.ts index 571102bfc20..c2bda72d6d4 100644 --- a/packages/bloom/lib/commands/t-digest/RESET.ts +++ b/packages/bloom/lib/commands/t-digest/RESET.ts @@ -1,5 +1,5 @@ -import { CommandParser } from '@redis/client/lib/client/parser'; -import { RedisArgument, SimpleStringReply, Command } from '@redis/client/lib/RESP/types'; +import { CommandParser } from '@redis/client/dist/lib/client/parser'; +import { RedisArgument, SimpleStringReply, Command } from '@redis/client/dist/lib/RESP/types'; export default { IS_READ_ONLY: false, diff --git a/packages/bloom/lib/commands/t-digest/REVRANK.ts b/packages/bloom/lib/commands/t-digest/REVRANK.ts index ca9301bb423..e7f2357a2f8 100644 --- a/packages/bloom/lib/commands/t-digest/REVRANK.ts +++ b/packages/bloom/lib/commands/t-digest/REVRANK.ts @@ -1,4 +1,4 @@ -import { Command } from '@redis/client/lib/RESP/types'; +import { Command } from '@redis/client/dist/lib/RESP/types'; import RANK, { transformRankArguments } from './RANK'; export default { diff --git a/packages/bloom/lib/commands/t-digest/TRIMMED_MEAN.ts b/packages/bloom/lib/commands/t-digest/TRIMMED_MEAN.ts index f411198260a..1fd6360ab65 100644 --- a/packages/bloom/lib/commands/t-digest/TRIMMED_MEAN.ts +++ b/packages/bloom/lib/commands/t-digest/TRIMMED_MEAN.ts @@ -1,6 +1,6 @@ -import { CommandParser } from '@redis/client/lib/client/parser'; -import { RedisArgument, Command } from '@redis/client/lib/RESP/types'; -import { transformDoubleReply } from '@redis/client/lib/commands/generic-transformers'; +import { CommandParser } from '@redis/client/dist/lib/client/parser'; +import { RedisArgument, Command } from '@redis/client/dist/lib/RESP/types'; +import { transformDoubleReply } from '@redis/client/dist/lib/commands/generic-transformers'; export default { IS_READ_ONLY: true, diff --git a/packages/bloom/lib/commands/t-digest/index.ts b/packages/bloom/lib/commands/t-digest/index.ts index fb80b35d0a1..d180911dbf9 100644 --- a/packages/bloom/lib/commands/t-digest/index.ts +++ b/packages/bloom/lib/commands/t-digest/index.ts @@ -1,4 +1,4 @@ -import type { RedisCommands } from '@redis/client/lib/RESP/types'; +import type { RedisCommands } from '@redis/client/dist/lib/RESP/types'; import ADD from './ADD'; import BYRANK from './BYRANK'; import BYREVRANK from './BYREVRANK'; diff --git a/packages/bloom/lib/commands/top-k/ADD.ts b/packages/bloom/lib/commands/top-k/ADD.ts index 42b162165c7..244e6209c91 100644 --- a/packages/bloom/lib/commands/top-k/ADD.ts +++ b/packages/bloom/lib/commands/top-k/ADD.ts @@ -1,6 +1,6 @@ -import { CommandParser } from '@redis/client/lib/client/parser'; -import { RedisArgument, ArrayReply, BlobStringReply, Command } from '@redis/client/lib/RESP/types'; -import { RedisVariadicArgument } from '@redis/client/lib/commands/generic-transformers'; +import { CommandParser } from '@redis/client/dist/lib/client/parser'; +import { RedisArgument, ArrayReply, BlobStringReply, Command } from '@redis/client/dist/lib/RESP/types'; +import { RedisVariadicArgument } from '@redis/client/dist/lib/commands/generic-transformers'; export default { IS_READ_ONLY: false, diff --git a/packages/bloom/lib/commands/top-k/COUNT.ts b/packages/bloom/lib/commands/top-k/COUNT.ts index 7cca009c599..7e75a3b68aa 100644 --- a/packages/bloom/lib/commands/top-k/COUNT.ts +++ b/packages/bloom/lib/commands/top-k/COUNT.ts @@ -1,6 +1,6 @@ -import { CommandParser } from '@redis/client/lib/client/parser'; -import { RedisArgument, ArrayReply, NumberReply, Command } from '@redis/client/lib/RESP/types'; -import { RedisVariadicArgument } from '@redis/client/lib/commands/generic-transformers'; +import { CommandParser } from '@redis/client/dist/lib/client/parser'; +import { RedisArgument, ArrayReply, NumberReply, Command } from '@redis/client/dist/lib/RESP/types'; +import { RedisVariadicArgument } from '@redis/client/dist/lib/commands/generic-transformers'; export default { IS_READ_ONLY: true, diff --git a/packages/bloom/lib/commands/top-k/INCRBY.ts b/packages/bloom/lib/commands/top-k/INCRBY.ts index 52ea0edc968..9e9a49e18f9 100644 --- a/packages/bloom/lib/commands/top-k/INCRBY.ts +++ b/packages/bloom/lib/commands/top-k/INCRBY.ts @@ -1,5 +1,5 @@ -import { CommandParser } from '@redis/client/lib/client/parser'; -import { RedisArgument, ArrayReply, SimpleStringReply, NullReply, Command } from '@redis/client/lib/RESP/types'; +import { CommandParser } from '@redis/client/dist/lib/client/parser'; +import { RedisArgument, ArrayReply, SimpleStringReply, NullReply, Command } from '@redis/client/dist/lib/RESP/types'; export interface TopKIncrByItem { item: string; diff --git a/packages/bloom/lib/commands/top-k/INFO.ts b/packages/bloom/lib/commands/top-k/INFO.ts index 89ebeaa8ebe..53da265c1f2 100644 --- a/packages/bloom/lib/commands/top-k/INFO.ts +++ b/packages/bloom/lib/commands/top-k/INFO.ts @@ -1,6 +1,6 @@ -import { CommandParser } from '@redis/client/lib/client/parser'; -import { RedisArgument, TuplesToMapReply, NumberReply, DoubleReply, UnwrapReply, Resp2Reply, Command, SimpleStringReply, TypeMapping } from '@redis/client/lib/RESP/types'; -import { transformDoubleReply } from '@redis/client/lib/commands/generic-transformers'; +import { CommandParser } from '@redis/client/dist/lib/client/parser'; +import { RedisArgument, TuplesToMapReply, NumberReply, DoubleReply, UnwrapReply, Resp2Reply, Command, SimpleStringReply, TypeMapping } from '@redis/client/dist/lib/RESP/types'; +import { transformDoubleReply } from '@redis/client/dist/lib/commands/generic-transformers'; import { transformInfoV2Reply } from '../bloom'; export type TopKInfoReplyMap = TuplesToMapReply<[ diff --git a/packages/bloom/lib/commands/top-k/LIST.ts b/packages/bloom/lib/commands/top-k/LIST.ts index 74b85e0f71d..d7adeaa193c 100644 --- a/packages/bloom/lib/commands/top-k/LIST.ts +++ b/packages/bloom/lib/commands/top-k/LIST.ts @@ -1,5 +1,5 @@ -import { CommandParser } from '@redis/client/lib/client/parser'; -import { RedisArgument, ArrayReply, BlobStringReply, Command } from '@redis/client/lib/RESP/types'; +import { CommandParser } from '@redis/client/dist/lib/client/parser'; +import { RedisArgument, ArrayReply, BlobStringReply, Command } from '@redis/client/dist/lib/RESP/types'; export default { IS_READ_ONLY: true, diff --git a/packages/bloom/lib/commands/top-k/LIST_WITHCOUNT.ts b/packages/bloom/lib/commands/top-k/LIST_WITHCOUNT.ts index a3a3d3f43f8..2c0f10e785b 100644 --- a/packages/bloom/lib/commands/top-k/LIST_WITHCOUNT.ts +++ b/packages/bloom/lib/commands/top-k/LIST_WITHCOUNT.ts @@ -1,5 +1,5 @@ -import { CommandParser } from '@redis/client/lib/client/parser'; -import { RedisArgument, ArrayReply, BlobStringReply, NumberReply, UnwrapReply, Command } from '@redis/client/lib/RESP/types'; +import { CommandParser } from '@redis/client/dist/lib/client/parser'; +import { RedisArgument, ArrayReply, BlobStringReply, NumberReply, UnwrapReply, Command } from '@redis/client/dist/lib/RESP/types'; export default { IS_READ_ONLY: true, diff --git a/packages/bloom/lib/commands/top-k/QUERY.ts b/packages/bloom/lib/commands/top-k/QUERY.ts index 49b91714374..a6fb4bae69e 100644 --- a/packages/bloom/lib/commands/top-k/QUERY.ts +++ b/packages/bloom/lib/commands/top-k/QUERY.ts @@ -1,6 +1,6 @@ -import { CommandParser } from '@redis/client/lib/client/parser'; -import { RedisArgument, Command } from '@redis/client/lib/RESP/types'; -import { RedisVariadicArgument, transformBooleanArrayReply } from '@redis/client/lib/commands/generic-transformers'; +import { CommandParser } from '@redis/client/dist/lib/client/parser'; +import { RedisArgument, Command } from '@redis/client/dist/lib/RESP/types'; +import { RedisVariadicArgument, transformBooleanArrayReply } from '@redis/client/dist/lib/commands/generic-transformers'; export default { IS_READ_ONLY: false, diff --git a/packages/bloom/lib/commands/top-k/RESERVE.ts b/packages/bloom/lib/commands/top-k/RESERVE.ts index f3ef3bf7200..ee3ee9a8cf4 100644 --- a/packages/bloom/lib/commands/top-k/RESERVE.ts +++ b/packages/bloom/lib/commands/top-k/RESERVE.ts @@ -1,5 +1,5 @@ -import { CommandParser } from '@redis/client/lib/client/parser'; -import { SimpleStringReply, Command, RedisArgument } from '@redis/client/lib/RESP/types'; +import { CommandParser } from '@redis/client/dist/lib/client/parser'; +import { SimpleStringReply, Command, RedisArgument } from '@redis/client/dist/lib/RESP/types'; export interface TopKReserveOptions { width: number; diff --git a/packages/bloom/lib/commands/top-k/index.ts b/packages/bloom/lib/commands/top-k/index.ts index 0352745fd07..fb5de543cab 100644 --- a/packages/bloom/lib/commands/top-k/index.ts +++ b/packages/bloom/lib/commands/top-k/index.ts @@ -1,4 +1,4 @@ -import type { RedisCommands } from '@redis/client/lib/RESP/types'; +import type { RedisCommands } from '@redis/client/dist/lib/RESP/types'; import ADD from './ADD'; import COUNT from './COUNT'; import INCRBY from './INCRBY'; diff --git a/packages/graph/lib/commands/CONFIG_GET.ts b/packages/graph/lib/commands/CONFIG_GET.ts index 8ff289876d3..4986dfc1816 100644 --- a/packages/graph/lib/commands/CONFIG_GET.ts +++ b/packages/graph/lib/commands/CONFIG_GET.ts @@ -1,5 +1,5 @@ -import { RedisArgument, TuplesReply, ArrayReply, BlobStringReply, NumberReply, Command } from '@redis/client/lib/RESP/types'; -import { CommandParser } from '@redis/client/lib/client/parser'; +import { RedisArgument, TuplesReply, ArrayReply, BlobStringReply, NumberReply, Command } from '@redis/client/dist/lib/RESP/types'; +import { CommandParser } from '@redis/client/dist/lib/client/parser'; type ConfigItemReply = TuplesReply<[ configKey: BlobStringReply, diff --git a/packages/graph/lib/commands/CONFIG_SET.ts b/packages/graph/lib/commands/CONFIG_SET.ts index b37d8690bfa..63f604402ec 100644 --- a/packages/graph/lib/commands/CONFIG_SET.ts +++ b/packages/graph/lib/commands/CONFIG_SET.ts @@ -1,5 +1,5 @@ -import { RedisArgument, SimpleStringReply, Command } from '@redis/client/lib/RESP/types'; -import { CommandParser } from '@redis/client/lib/client/parser'; +import { RedisArgument, SimpleStringReply, Command } from '@redis/client/dist/lib/RESP/types'; +import { CommandParser } from '@redis/client/dist/lib/client/parser'; export default { NOT_KEYED_COMMAND: true, diff --git a/packages/graph/lib/commands/DELETE.ts b/packages/graph/lib/commands/DELETE.ts index 2e1f9ff003c..43af38d6fb0 100644 --- a/packages/graph/lib/commands/DELETE.ts +++ b/packages/graph/lib/commands/DELETE.ts @@ -1,5 +1,5 @@ -import { RedisArgument, BlobStringReply, Command } from '@redis/client/lib/RESP/types'; -import { CommandParser } from '@redis/client/lib/client/parser'; +import { RedisArgument, BlobStringReply, Command } from '@redis/client/dist/lib/RESP/types'; +import { CommandParser } from '@redis/client/dist/lib/client/parser'; export default { IS_READ_ONLY: false, diff --git a/packages/graph/lib/commands/EXPLAIN.ts b/packages/graph/lib/commands/EXPLAIN.ts index c690450a10f..a9af7e73a20 100644 --- a/packages/graph/lib/commands/EXPLAIN.ts +++ b/packages/graph/lib/commands/EXPLAIN.ts @@ -1,5 +1,5 @@ -import { RedisArgument, ArrayReply, BlobStringReply, Command } from '@redis/client/lib/RESP/types'; -import { CommandParser } from '@redis/client/lib/client/parser'; +import { RedisArgument, ArrayReply, BlobStringReply, Command } from '@redis/client/dist/lib/RESP/types'; +import { CommandParser } from '@redis/client/dist/lib/client/parser'; export default { IS_READ_ONLY: true, diff --git a/packages/graph/lib/commands/LIST.ts b/packages/graph/lib/commands/LIST.ts index 4fe66d748fa..70fa8bda812 100644 --- a/packages/graph/lib/commands/LIST.ts +++ b/packages/graph/lib/commands/LIST.ts @@ -1,5 +1,5 @@ -import { ArrayReply, BlobStringReply, Command } from '@redis/client/lib/RESP/types'; -import { CommandParser } from '@redis/client/lib/client/parser'; +import { ArrayReply, BlobStringReply, Command } from '@redis/client/dist/lib/RESP/types'; +import { CommandParser } from '@redis/client/dist/lib/client/parser'; export default { NOT_KEYED_COMMAND: true, diff --git a/packages/graph/lib/commands/PROFILE.ts b/packages/graph/lib/commands/PROFILE.ts index fba0973baaf..c3229fb9a04 100644 --- a/packages/graph/lib/commands/PROFILE.ts +++ b/packages/graph/lib/commands/PROFILE.ts @@ -1,5 +1,5 @@ -import { RedisArgument, ArrayReply, BlobStringReply, Command } from '@redis/client/lib/RESP/types'; -import { CommandParser } from '@redis/client/lib/client/parser'; +import { RedisArgument, ArrayReply, BlobStringReply, Command } from '@redis/client/dist/lib/RESP/types'; +import { CommandParser } from '@redis/client/dist/lib/client/parser'; export default { IS_READ_ONLY: true, diff --git a/packages/graph/lib/commands/QUERY.ts b/packages/graph/lib/commands/QUERY.ts index c96c00ff325..23ea24c5768 100644 --- a/packages/graph/lib/commands/QUERY.ts +++ b/packages/graph/lib/commands/QUERY.ts @@ -1,5 +1,5 @@ -import { RedisArgument, ArrayReply, BlobStringReply, NumberReply, NullReply, TuplesReply, UnwrapReply, Command } from '@redis/client/lib/RESP/types'; -import { CommandParser } from '@redis/client/lib/client/parser'; +import { RedisArgument, ArrayReply, BlobStringReply, NumberReply, NullReply, TuplesReply, UnwrapReply, Command } from '@redis/client/dist/lib/RESP/types'; +import { CommandParser } from '@redis/client/dist/lib/client/parser'; type Headers = ArrayReply; diff --git a/packages/graph/lib/commands/RO_QUERY.ts b/packages/graph/lib/commands/RO_QUERY.ts index 98668675d21..f052877b99d 100644 --- a/packages/graph/lib/commands/RO_QUERY.ts +++ b/packages/graph/lib/commands/RO_QUERY.ts @@ -1,4 +1,4 @@ -import { Command } from '@redis/client/lib/RESP/types'; +import { Command } from '@redis/client/dist/lib/RESP/types'; import QUERY, { parseQueryArguments } from './QUERY'; export default { diff --git a/packages/graph/lib/commands/SLOWLOG.ts b/packages/graph/lib/commands/SLOWLOG.ts index bba615efb21..4110335f922 100644 --- a/packages/graph/lib/commands/SLOWLOG.ts +++ b/packages/graph/lib/commands/SLOWLOG.ts @@ -1,5 +1,5 @@ -import { RedisArgument, ArrayReply, TuplesReply, BlobStringReply, UnwrapReply, Command } from '@redis/client/lib/RESP/types'; -import { CommandParser } from '@redis/client/lib/client/parser'; +import { RedisArgument, ArrayReply, TuplesReply, BlobStringReply, UnwrapReply, Command } from '@redis/client/dist/lib/RESP/types'; +import { CommandParser } from '@redis/client/dist/lib/client/parser'; type SlowLogRawReply = ArrayReply; diff --git a/packages/json/lib/commands/SET.ts b/packages/json/lib/commands/SET.ts index 6bd89803ded..75d7099acfb 100644 --- a/packages/json/lib/commands/SET.ts +++ b/packages/json/lib/commands/SET.ts @@ -1,5 +1,5 @@ -import { CommandParser } from '@redis/client/lib/client/parser'; -import { RedisArgument, SimpleStringReply, NullReply, Command } from '@redis/client/lib/RESP/types'; +import { CommandParser } from '@redis/client/dist/lib/client/parser'; +import { RedisArgument, SimpleStringReply, NullReply, Command } from '@redis/client/dist/lib/RESP/types'; import { RedisJSON, transformRedisJsonArgument } from '.'; export interface JsonSetOptions { diff --git a/packages/json/lib/commands/STRAPPEND.ts b/packages/json/lib/commands/STRAPPEND.ts index 97bbebf931b..45d503856ac 100644 --- a/packages/json/lib/commands/STRAPPEND.ts +++ b/packages/json/lib/commands/STRAPPEND.ts @@ -1,5 +1,5 @@ -import { CommandParser } from '@redis/client/lib/client/parser'; -import { RedisArgument, Command, NullReply, NumberReply, ArrayReply } from '@redis/client/lib/RESP/types'; +import { CommandParser } from '@redis/client/dist/lib/client/parser'; +import { RedisArgument, Command, NullReply, NumberReply, ArrayReply } from '@redis/client/dist/lib/RESP/types'; import { transformRedisJsonArgument } from '.'; export interface JsonStrAppendOptions { diff --git a/packages/json/lib/commands/STRLEN.ts b/packages/json/lib/commands/STRLEN.ts index b72f30cd6da..644cdf27ef7 100644 --- a/packages/json/lib/commands/STRLEN.ts +++ b/packages/json/lib/commands/STRLEN.ts @@ -1,5 +1,5 @@ -import { CommandParser } from '@redis/client/lib/client/parser'; -import { RedisArgument, ArrayReply, NumberReply, NullReply, Command } from '@redis/client/lib/RESP/types'; +import { CommandParser } from '@redis/client/dist/lib/client/parser'; +import { RedisArgument, ArrayReply, NumberReply, NullReply, Command } from '@redis/client/dist/lib/RESP/types'; export interface JsonStrLenOptions { path?: RedisArgument; diff --git a/packages/json/lib/commands/TOGGLE.ts b/packages/json/lib/commands/TOGGLE.ts index 2707d54b6fd..85c769729c7 100644 --- a/packages/json/lib/commands/TOGGLE.ts +++ b/packages/json/lib/commands/TOGGLE.ts @@ -1,5 +1,5 @@ -import { CommandParser } from '@redis/client/lib/client/parser'; -import { RedisArgument, ArrayReply, NumberReply, NullReply, Command, } from '@redis/client/lib/RESP/types'; +import { CommandParser } from '@redis/client/dist/lib/client/parser'; +import { RedisArgument, ArrayReply, NumberReply, NullReply, Command, } from '@redis/client/dist/lib/RESP/types'; export default { IS_READ_ONLY: false, diff --git a/packages/json/lib/commands/TYPE.ts b/packages/json/lib/commands/TYPE.ts index d19de31df68..1146043b2c2 100644 --- a/packages/json/lib/commands/TYPE.ts +++ b/packages/json/lib/commands/TYPE.ts @@ -1,5 +1,5 @@ -import { CommandParser } from '@redis/client/lib/client/parser'; -import { NullReply, BlobStringReply, ArrayReply, Command, RedisArgument, UnwrapReply } from '@redis/client/lib/RESP/types'; +import { CommandParser } from '@redis/client/dist/lib/client/parser'; +import { NullReply, BlobStringReply, ArrayReply, Command, RedisArgument, UnwrapReply } from '@redis/client/dist/lib/RESP/types'; export interface JsonTypeOptions { path?: RedisArgument; diff --git a/packages/json/lib/commands/index.ts b/packages/json/lib/commands/index.ts index 8ea44ce8049..2724ff2565c 100644 --- a/packages/json/lib/commands/index.ts +++ b/packages/json/lib/commands/index.ts @@ -1,4 +1,4 @@ -import { BlobStringReply, NullReply, UnwrapReply } from '@redis/client/lib/RESP/types'; +import { BlobStringReply, NullReply, UnwrapReply } from '@redis/client/dist/lib/RESP/types'; import ARRAPPEND from './ARRAPPEND'; import ARRINDEX from './ARRINDEX'; import ARRINSERT from './ARRINSERT'; @@ -23,7 +23,7 @@ import STRAPPEND from './STRAPPEND'; import STRLEN from './STRLEN'; import TOGGLE from './TOGGLE'; import TYPE from './TYPE'; -import { isNullReply } from '@redis/client/lib/commands/generic-transformers'; +import { isNullReply } from '@redis/client/dist/lib/commands/generic-transformers'; export default { ARRAPPEND, diff --git a/packages/search/lib/commands/AGGREGATE.ts b/packages/search/lib/commands/AGGREGATE.ts index 0105b082682..925a91da008 100644 --- a/packages/search/lib/commands/AGGREGATE.ts +++ b/packages/search/lib/commands/AGGREGATE.ts @@ -1,8 +1,8 @@ -import { CommandParser } from '@redis/client/lib/client/parser'; -import { ArrayReply, BlobStringReply, Command, MapReply, NumberReply, RedisArgument, ReplyUnion, TypeMapping, UnwrapReply } from '@redis/client/lib/RESP/types'; +import { CommandParser } from '@redis/client/dist/lib/client/parser'; +import { ArrayReply, BlobStringReply, Command, MapReply, NumberReply, RedisArgument, ReplyUnion, TypeMapping, UnwrapReply } from '@redis/client/dist/lib/RESP/types'; import { RediSearchProperty } from './CREATE'; import { FtSearchParams, parseParamsArgument } from './SEARCH'; -import { transformTuplesReply } from '@redis/client/lib/commands/generic-transformers'; +import { transformTuplesReply } from '@redis/client/dist/lib/commands/generic-transformers'; type LoadField = RediSearchProperty | { identifier: RediSearchProperty; diff --git a/packages/search/lib/commands/AGGREGATE_WITHCURSOR.ts b/packages/search/lib/commands/AGGREGATE_WITHCURSOR.ts index f9a7e75942f..8dfca7169ef 100644 --- a/packages/search/lib/commands/AGGREGATE_WITHCURSOR.ts +++ b/packages/search/lib/commands/AGGREGATE_WITHCURSOR.ts @@ -1,5 +1,5 @@ -import { CommandParser } from '@redis/client/lib/client/parser'; -import { RedisArgument, Command, ReplyUnion, NumberReply } from '@redis/client/lib/RESP/types'; +import { CommandParser } from '@redis/client/dist/lib/client/parser'; +import { RedisArgument, Command, ReplyUnion, NumberReply } from '@redis/client/dist/lib/RESP/types'; import AGGREGATE, { AggregateRawReply, AggregateReply, FtAggregateOptions } from './AGGREGATE'; export interface FtAggregateWithCursorOptions extends FtAggregateOptions { diff --git a/packages/search/lib/commands/ALIASADD.ts b/packages/search/lib/commands/ALIASADD.ts index db8eb54326e..c35e60bed4f 100644 --- a/packages/search/lib/commands/ALIASADD.ts +++ b/packages/search/lib/commands/ALIASADD.ts @@ -1,5 +1,5 @@ -import { CommandParser } from '@redis/client/lib/client/parser'; -import { RedisArgument, SimpleStringReply, Command } from '@redis/client/lib/RESP/types'; +import { CommandParser } from '@redis/client/dist/lib/client/parser'; +import { RedisArgument, SimpleStringReply, Command } from '@redis/client/dist/lib/RESP/types'; export default { NOT_KEYED_COMMAND: true, diff --git a/packages/search/lib/commands/ALIASDEL.ts b/packages/search/lib/commands/ALIASDEL.ts index 3e4b70a1943..9a2dbda4b9e 100644 --- a/packages/search/lib/commands/ALIASDEL.ts +++ b/packages/search/lib/commands/ALIASDEL.ts @@ -1,5 +1,5 @@ -import { CommandParser } from '@redis/client/lib/client/parser'; -import { RedisArgument, SimpleStringReply, Command } from '@redis/client/lib/RESP/types'; +import { CommandParser } from '@redis/client/dist/lib/client/parser'; +import { RedisArgument, SimpleStringReply, Command } from '@redis/client/dist/lib/RESP/types'; export default { NOT_KEYED_COMMAND: true, diff --git a/packages/search/lib/commands/ALIASUPDATE.ts b/packages/search/lib/commands/ALIASUPDATE.ts index 46f0aa3e020..3bd5ea92ba3 100644 --- a/packages/search/lib/commands/ALIASUPDATE.ts +++ b/packages/search/lib/commands/ALIASUPDATE.ts @@ -1,5 +1,5 @@ -import { CommandParser } from '@redis/client/lib/client/parser'; -import { SimpleStringReply, Command, RedisArgument } from '@redis/client/lib/RESP/types'; +import { CommandParser } from '@redis/client/dist/lib/client/parser'; +import { SimpleStringReply, Command, RedisArgument } from '@redis/client/dist/lib/RESP/types'; export default { NOT_KEYED_COMMAND: true, diff --git a/packages/search/lib/commands/ALTER.ts b/packages/search/lib/commands/ALTER.ts index 4cde32e6ad4..4a68817bd2c 100644 --- a/packages/search/lib/commands/ALTER.ts +++ b/packages/search/lib/commands/ALTER.ts @@ -1,5 +1,5 @@ -import { CommandParser } from '@redis/client/lib/client/parser'; -import { RedisArgument, SimpleStringReply, Command } from '@redis/client/lib/RESP/types'; +import { CommandParser } from '@redis/client/dist/lib/client/parser'; +import { RedisArgument, SimpleStringReply, Command } from '@redis/client/dist/lib/RESP/types'; import { RediSearchSchema, parseSchema } from './CREATE'; export default { diff --git a/packages/search/lib/commands/CONFIG_GET.ts b/packages/search/lib/commands/CONFIG_GET.ts index 9c0be73e2e3..ae7a9e0c78d 100644 --- a/packages/search/lib/commands/CONFIG_GET.ts +++ b/packages/search/lib/commands/CONFIG_GET.ts @@ -1,5 +1,5 @@ -import { CommandParser } from '@redis/client/lib/client/parser'; -import { ArrayReply, TuplesReply, BlobStringReply, NullReply, UnwrapReply, Command } from '@redis/client/lib/RESP/types'; +import { CommandParser } from '@redis/client/dist/lib/client/parser'; +import { ArrayReply, TuplesReply, BlobStringReply, NullReply, UnwrapReply, Command } from '@redis/client/dist/lib/RESP/types'; export default { NOT_KEYED_COMMAND: true, diff --git a/packages/search/lib/commands/CONFIG_SET.ts b/packages/search/lib/commands/CONFIG_SET.ts index ae57dd7d8f6..499b9525aa3 100644 --- a/packages/search/lib/commands/CONFIG_SET.ts +++ b/packages/search/lib/commands/CONFIG_SET.ts @@ -1,5 +1,5 @@ -import { CommandParser } from '@redis/client/lib/client/parser'; -import { RedisArgument, SimpleStringReply, Command } from '@redis/client/lib/RESP/types'; +import { CommandParser } from '@redis/client/dist/lib/client/parser'; +import { RedisArgument, SimpleStringReply, Command } from '@redis/client/dist/lib/RESP/types'; // using `string & {}` to avoid TS widening the type to `string` // TODO diff --git a/packages/search/lib/commands/CREATE.ts b/packages/search/lib/commands/CREATE.ts index d0096282f37..5eec9799264 100644 --- a/packages/search/lib/commands/CREATE.ts +++ b/packages/search/lib/commands/CREATE.ts @@ -1,6 +1,6 @@ -import { CommandParser } from '@redis/client/lib/client/parser'; -import { RedisArgument, SimpleStringReply, Command } from '@redis/client/lib/RESP/types'; -import { RedisVariadicArgument, parseOptionalVariadicArgument } from '@redis/client/lib/commands/generic-transformers'; +import { CommandParser } from '@redis/client/dist/lib/client/parser'; +import { RedisArgument, SimpleStringReply, Command } from '@redis/client/dist/lib/RESP/types'; +import { RedisVariadicArgument, parseOptionalVariadicArgument } from '@redis/client/dist/lib/commands/generic-transformers'; export const SCHEMA_FIELD_TYPE = { TEXT: 'TEXT', diff --git a/packages/search/lib/commands/CURSOR_DEL.ts b/packages/search/lib/commands/CURSOR_DEL.ts index 1ea4b46c8bd..5f638ebb0ee 100644 --- a/packages/search/lib/commands/CURSOR_DEL.ts +++ b/packages/search/lib/commands/CURSOR_DEL.ts @@ -1,5 +1,5 @@ -import { CommandParser } from '@redis/client/lib/client/parser'; -import { SimpleStringReply, Command, RedisArgument, NumberReply, UnwrapReply } from '@redis/client/lib/RESP/types'; +import { CommandParser } from '@redis/client/dist/lib/client/parser'; +import { SimpleStringReply, Command, RedisArgument, NumberReply, UnwrapReply } from '@redis/client/dist/lib/RESP/types'; export default { NOT_KEYED_COMMAND: true, diff --git a/packages/search/lib/commands/CURSOR_READ.ts b/packages/search/lib/commands/CURSOR_READ.ts index d23a0f0bd15..e64070122d1 100644 --- a/packages/search/lib/commands/CURSOR_READ.ts +++ b/packages/search/lib/commands/CURSOR_READ.ts @@ -1,5 +1,5 @@ -import { CommandParser } from '@redis/client/lib/client/parser'; -import { RedisArgument, Command, NumberReply, UnwrapReply } from '@redis/client/lib/RESP/types'; +import { CommandParser } from '@redis/client/dist/lib/client/parser'; +import { RedisArgument, Command, NumberReply, UnwrapReply } from '@redis/client/dist/lib/RESP/types'; import AGGREGATE_WITHCURSOR from './AGGREGATE_WITHCURSOR'; export interface FtCursorReadOptions { diff --git a/packages/search/lib/commands/DICTADD.ts b/packages/search/lib/commands/DICTADD.ts index 67f94a82c13..2106775f854 100644 --- a/packages/search/lib/commands/DICTADD.ts +++ b/packages/search/lib/commands/DICTADD.ts @@ -1,6 +1,6 @@ -import { CommandParser } from '@redis/client/lib/client/parser'; -import { RedisArgument, NumberReply, Command } from '@redis/client/lib/RESP/types'; -import { RedisVariadicArgument } from '@redis/client/lib/commands/generic-transformers'; +import { CommandParser } from '@redis/client/dist/lib/client/parser'; +import { RedisArgument, NumberReply, Command } from '@redis/client/dist/lib/RESP/types'; +import { RedisVariadicArgument } from '@redis/client/dist/lib/commands/generic-transformers'; export default { NOT_KEYED_COMMAND: true, diff --git a/packages/search/lib/commands/DICTDEL.ts b/packages/search/lib/commands/DICTDEL.ts index 9b0bda3a7a6..988af1139e9 100644 --- a/packages/search/lib/commands/DICTDEL.ts +++ b/packages/search/lib/commands/DICTDEL.ts @@ -1,6 +1,6 @@ -import { CommandParser } from '@redis/client/lib/client/parser'; -import { RedisArgument, NumberReply, Command } from '@redis/client/lib/RESP/types'; -import { RedisVariadicArgument } from '@redis/client/lib/commands/generic-transformers'; +import { CommandParser } from '@redis/client/dist/lib/client/parser'; +import { RedisArgument, NumberReply, Command } from '@redis/client/dist/lib/RESP/types'; +import { RedisVariadicArgument } from '@redis/client/dist/lib/commands/generic-transformers'; export default { NOT_KEYED_COMMAND: true, diff --git a/packages/search/lib/commands/DICTDUMP.ts b/packages/search/lib/commands/DICTDUMP.ts index 00dd5aba4eb..3c223442ecb 100644 --- a/packages/search/lib/commands/DICTDUMP.ts +++ b/packages/search/lib/commands/DICTDUMP.ts @@ -1,5 +1,5 @@ -import { CommandParser } from '@redis/client/lib/client/parser'; -import { RedisArgument, ArrayReply, SetReply, BlobStringReply, Command } from '@redis/client/lib/RESP/types'; +import { CommandParser } from '@redis/client/dist/lib/client/parser'; +import { RedisArgument, ArrayReply, SetReply, BlobStringReply, Command } from '@redis/client/dist/lib/RESP/types'; export default { NOT_KEYED_COMMAND: true, diff --git a/packages/search/lib/commands/DROPINDEX.ts b/packages/search/lib/commands/DROPINDEX.ts index e7be806ac14..407bdd031aa 100644 --- a/packages/search/lib/commands/DROPINDEX.ts +++ b/packages/search/lib/commands/DROPINDEX.ts @@ -1,5 +1,5 @@ -import { CommandParser } from '@redis/client/lib/client/parser'; -import { RedisArgument, SimpleStringReply, NumberReply, Command } from '@redis/client/lib/RESP/types'; +import { CommandParser } from '@redis/client/dist/lib/client/parser'; +import { RedisArgument, SimpleStringReply, NumberReply, Command } from '@redis/client/dist/lib/RESP/types'; export interface FtDropIndexOptions { DD?: true; diff --git a/packages/search/lib/commands/EXPLAIN.ts b/packages/search/lib/commands/EXPLAIN.ts index deb75229b5d..dcd7c3c0d2d 100644 --- a/packages/search/lib/commands/EXPLAIN.ts +++ b/packages/search/lib/commands/EXPLAIN.ts @@ -1,5 +1,5 @@ -import { CommandParser } from '@redis/client/lib/client/parser'; -import { RedisArgument, SimpleStringReply, Command } from '@redis/client/lib/RESP/types'; +import { CommandParser } from '@redis/client/dist/lib/client/parser'; +import { RedisArgument, SimpleStringReply, Command } from '@redis/client/dist/lib/RESP/types'; import { FtSearchParams, parseParamsArgument } from './SEARCH'; export interface FtExplainOptions { diff --git a/packages/search/lib/commands/EXPLAINCLI.ts b/packages/search/lib/commands/EXPLAINCLI.ts index 7a4ae3a4b25..53711067058 100644 --- a/packages/search/lib/commands/EXPLAINCLI.ts +++ b/packages/search/lib/commands/EXPLAINCLI.ts @@ -1,5 +1,5 @@ -import { CommandParser } from '@redis/client/lib/client/parser'; -import { RedisArgument, ArrayReply, BlobStringReply, Command } from '@redis/client/lib/RESP/types'; +import { CommandParser } from '@redis/client/dist/lib/client/parser'; +import { RedisArgument, ArrayReply, BlobStringReply, Command } from '@redis/client/dist/lib/RESP/types'; export default { NOT_KEYED_COMMAND: true, diff --git a/packages/search/lib/commands/INFO.ts b/packages/search/lib/commands/INFO.ts index 6792645fe3e..cee6ae683cd 100644 --- a/packages/search/lib/commands/INFO.ts +++ b/packages/search/lib/commands/INFO.ts @@ -1,8 +1,8 @@ -import { CommandParser } from '@redis/client/lib/client/parser'; +import { CommandParser } from '@redis/client/dist/lib/client/parser'; import { RedisArgument } from "@redis/client"; -import { ArrayReply, BlobStringReply, Command, DoubleReply, MapReply, NullReply, NumberReply, ReplyUnion, SimpleStringReply, TypeMapping } from "@redis/client/lib/RESP/types"; -import { createTransformTuplesReplyFunc, transformDoubleReply } from "@redis/client/lib/commands/generic-transformers"; -import { TuplesReply } from '@redis/client/lib/RESP/types'; +import { ArrayReply, BlobStringReply, Command, DoubleReply, MapReply, NullReply, NumberReply, ReplyUnion, SimpleStringReply, TypeMapping } from "@redis/client/dist/lib/RESP/types"; +import { createTransformTuplesReplyFunc, transformDoubleReply } from "@redis/client/dist/lib/commands/generic-transformers"; +import { TuplesReply } from '@redis/client/dist/lib/RESP/types'; export default { NOT_KEYED_COMMAND: true, diff --git a/packages/search/lib/commands/PROFILE_AGGREGATE.ts b/packages/search/lib/commands/PROFILE_AGGREGATE.ts index 703bfcacc72..ad671c9eb45 100644 --- a/packages/search/lib/commands/PROFILE_AGGREGATE.ts +++ b/packages/search/lib/commands/PROFILE_AGGREGATE.ts @@ -1,5 +1,5 @@ -import { CommandParser } from '@redis/client/lib/client/parser'; -import { Command, ReplyUnion } from "@redis/client/lib/RESP/types"; +import { CommandParser } from '@redis/client/dist/lib/client/parser'; +import { Command, ReplyUnion } from "@redis/client/dist/lib/RESP/types"; import AGGREGATE, { AggregateRawReply, FtAggregateOptions, parseAggregateOptions } from "./AGGREGATE"; import { ProfileOptions, ProfileRawReply, ProfileReply, transformProfile } from "./PROFILE_SEARCH"; diff --git a/packages/search/lib/commands/PROFILE_SEARCH.ts b/packages/search/lib/commands/PROFILE_SEARCH.ts index c345e70dd78..86ce85ba7f1 100644 --- a/packages/search/lib/commands/PROFILE_SEARCH.ts +++ b/packages/search/lib/commands/PROFILE_SEARCH.ts @@ -1,5 +1,5 @@ -import { CommandParser } from '@redis/client/lib/client/parser'; -import { Command, RedisArgument, ReplyUnion } from "@redis/client/lib/RESP/types"; +import { CommandParser } from '@redis/client/dist/lib/client/parser'; +import { Command, RedisArgument, ReplyUnion } from "@redis/client/dist/lib/RESP/types"; import { AggregateReply } from "./AGGREGATE"; import SEARCH, { FtSearchOptions, SearchRawReply, SearchReply, parseSearchOptions } from "./SEARCH"; diff --git a/packages/search/lib/commands/SEARCH.ts b/packages/search/lib/commands/SEARCH.ts index de03ac0070e..c2d2a9d2696 100644 --- a/packages/search/lib/commands/SEARCH.ts +++ b/packages/search/lib/commands/SEARCH.ts @@ -1,6 +1,6 @@ -import { CommandParser } from '@redis/client/lib/client/parser'; -import { RedisArgument, Command, ReplyUnion } from '@redis/client/lib/RESP/types'; -import { RedisVariadicArgument, parseOptionalVariadicArgument } from '@redis/client/lib/commands/generic-transformers'; +import { CommandParser } from '@redis/client/dist/lib/client/parser'; +import { RedisArgument, Command, ReplyUnion } from '@redis/client/dist/lib/RESP/types'; +import { RedisVariadicArgument, parseOptionalVariadicArgument } from '@redis/client/dist/lib/commands/generic-transformers'; import { RediSearchProperty, RediSearchLanguage } from './CREATE'; export type FtSearchParams = Record; diff --git a/packages/search/lib/commands/SEARCH_NOCONTENT.ts b/packages/search/lib/commands/SEARCH_NOCONTENT.ts index a4c6f6a27aa..c0a152375d9 100644 --- a/packages/search/lib/commands/SEARCH_NOCONTENT.ts +++ b/packages/search/lib/commands/SEARCH_NOCONTENT.ts @@ -1,4 +1,4 @@ -import { Command, ReplyUnion } from '@redis/client/lib/RESP/types'; +import { Command, ReplyUnion } from '@redis/client/dist/lib/RESP/types'; import SEARCH, { SearchRawReply } from './SEARCH'; export default { diff --git a/packages/search/lib/commands/SPELLCHECK.ts b/packages/search/lib/commands/SPELLCHECK.ts index 1e6981d01ff..ae95b72c249 100644 --- a/packages/search/lib/commands/SPELLCHECK.ts +++ b/packages/search/lib/commands/SPELLCHECK.ts @@ -1,5 +1,5 @@ -import { CommandParser } from '@redis/client/lib/client/parser'; -import { RedisArgument, Command, ReplyUnion } from '@redis/client/lib/RESP/types'; +import { CommandParser } from '@redis/client/dist/lib/client/parser'; +import { RedisArgument, Command, ReplyUnion } from '@redis/client/dist/lib/RESP/types'; export interface Terms { mode: 'INCLUDE' | 'EXCLUDE'; diff --git a/packages/search/lib/commands/SUGADD.ts b/packages/search/lib/commands/SUGADD.ts index a82f03ffa1b..34e5bccb7f1 100644 --- a/packages/search/lib/commands/SUGADD.ts +++ b/packages/search/lib/commands/SUGADD.ts @@ -1,5 +1,5 @@ -import { CommandParser } from '@redis/client/lib/client/parser'; -import { RedisArgument, NumberReply, Command } from '@redis/client/lib/RESP/types'; +import { CommandParser } from '@redis/client/dist/lib/client/parser'; +import { RedisArgument, NumberReply, Command } from '@redis/client/dist/lib/RESP/types'; export interface FtSugAddOptions { INCR?: boolean; diff --git a/packages/search/lib/commands/SUGDEL.ts b/packages/search/lib/commands/SUGDEL.ts index 1cdf56d2025..6bc99456d2e 100644 --- a/packages/search/lib/commands/SUGDEL.ts +++ b/packages/search/lib/commands/SUGDEL.ts @@ -1,5 +1,5 @@ -import { CommandParser } from '@redis/client/lib/client/parser'; -import { RedisArgument, NumberReply, Command } from '@redis/client/lib/RESP/types'; +import { CommandParser } from '@redis/client/dist/lib/client/parser'; +import { RedisArgument, NumberReply, Command } from '@redis/client/dist/lib/RESP/types'; export default { IS_READ_ONLY: true, diff --git a/packages/search/lib/commands/SUGGET.ts b/packages/search/lib/commands/SUGGET.ts index 607e26df94e..e8a3aecdab0 100644 --- a/packages/search/lib/commands/SUGGET.ts +++ b/packages/search/lib/commands/SUGGET.ts @@ -1,5 +1,5 @@ -import { CommandParser } from '@redis/client/lib/client/parser'; -import { NullReply, ArrayReply, BlobStringReply, Command, RedisArgument } from '@redis/client/lib/RESP/types'; +import { CommandParser } from '@redis/client/dist/lib/client/parser'; +import { NullReply, ArrayReply, BlobStringReply, Command, RedisArgument } from '@redis/client/dist/lib/RESP/types'; export interface FtSugGetOptions { FUZZY?: boolean; diff --git a/packages/search/lib/commands/SUGGET_WITHPAYLOADS.ts b/packages/search/lib/commands/SUGGET_WITHPAYLOADS.ts index b2112bb4b34..60bf5ee86d9 100644 --- a/packages/search/lib/commands/SUGGET_WITHPAYLOADS.ts +++ b/packages/search/lib/commands/SUGGET_WITHPAYLOADS.ts @@ -1,5 +1,5 @@ -import { NullReply, ArrayReply, BlobStringReply, UnwrapReply, Command } from '@redis/client/lib/RESP/types'; -import { isNullReply } from '@redis/client/lib/commands/generic-transformers'; +import { NullReply, ArrayReply, BlobStringReply, UnwrapReply, Command } from '@redis/client/dist/lib/RESP/types'; +import { isNullReply } from '@redis/client/dist/lib/commands/generic-transformers'; import SUGGET from './SUGGET'; export default { diff --git a/packages/search/lib/commands/SUGGET_WITHSCORES.ts b/packages/search/lib/commands/SUGGET_WITHSCORES.ts index 088153c31f5..060e59132db 100644 --- a/packages/search/lib/commands/SUGGET_WITHSCORES.ts +++ b/packages/search/lib/commands/SUGGET_WITHSCORES.ts @@ -1,5 +1,5 @@ -import { NullReply, ArrayReply, BlobStringReply, DoubleReply, UnwrapReply, Command, TypeMapping } from '@redis/client/lib/RESP/types'; -import { isNullReply, transformDoubleReply } from '@redis/client/lib/commands/generic-transformers'; +import { NullReply, ArrayReply, BlobStringReply, DoubleReply, UnwrapReply, Command, TypeMapping } from '@redis/client/dist/lib/RESP/types'; +import { isNullReply, transformDoubleReply } from '@redis/client/dist/lib/commands/generic-transformers'; import SUGGET from './SUGGET'; type SuggestScore = { diff --git a/packages/search/lib/commands/SUGGET_WITHSCORES_WITHPAYLOADS.ts b/packages/search/lib/commands/SUGGET_WITHSCORES_WITHPAYLOADS.ts index 6f032a15899..07277420338 100644 --- a/packages/search/lib/commands/SUGGET_WITHSCORES_WITHPAYLOADS.ts +++ b/packages/search/lib/commands/SUGGET_WITHSCORES_WITHPAYLOADS.ts @@ -1,5 +1,5 @@ -import { NullReply, ArrayReply, BlobStringReply, DoubleReply, UnwrapReply, Command, TypeMapping } from '@redis/client/lib/RESP/types'; -import { isNullReply, transformDoubleReply } from '@redis/client/lib/commands/generic-transformers'; +import { NullReply, ArrayReply, BlobStringReply, DoubleReply, UnwrapReply, Command, TypeMapping } from '@redis/client/dist/lib/RESP/types'; +import { isNullReply, transformDoubleReply } from '@redis/client/dist/lib/commands/generic-transformers'; import SUGGET from './SUGGET'; type SuggestScoreWithPayload = { diff --git a/packages/search/lib/commands/SUGLEN.ts b/packages/search/lib/commands/SUGLEN.ts index 7437559843f..a3f0fbe45ed 100644 --- a/packages/search/lib/commands/SUGLEN.ts +++ b/packages/search/lib/commands/SUGLEN.ts @@ -1,5 +1,5 @@ -import { CommandParser } from '@redis/client/lib/client/parser'; -import { RedisArgument, NumberReply, Command } from '@redis/client/lib/RESP/types'; +import { CommandParser } from '@redis/client/dist/lib/client/parser'; +import { RedisArgument, NumberReply, Command } from '@redis/client/dist/lib/RESP/types'; export default { IS_READ_ONLY: true, diff --git a/packages/search/lib/commands/SYNDUMP.ts b/packages/search/lib/commands/SYNDUMP.ts index 0c3b68bf2e7..5f454f96fe0 100644 --- a/packages/search/lib/commands/SYNDUMP.ts +++ b/packages/search/lib/commands/SYNDUMP.ts @@ -1,5 +1,5 @@ -import { CommandParser } from '@redis/client/lib/client/parser'; -import { RedisArgument, MapReply, BlobStringReply, ArrayReply, UnwrapReply, Command } from '@redis/client/lib/RESP/types'; +import { CommandParser } from '@redis/client/dist/lib/client/parser'; +import { RedisArgument, MapReply, BlobStringReply, ArrayReply, UnwrapReply, Command } from '@redis/client/dist/lib/RESP/types'; export default { NOT_KEYED_COMMAND: true, diff --git a/packages/search/lib/commands/SYNUPDATE.ts b/packages/search/lib/commands/SYNUPDATE.ts index 2baf2ded18c..3af735412ae 100644 --- a/packages/search/lib/commands/SYNUPDATE.ts +++ b/packages/search/lib/commands/SYNUPDATE.ts @@ -1,6 +1,6 @@ -import { CommandParser } from '@redis/client/lib/client/parser'; -import { SimpleStringReply, Command, RedisArgument } from '@redis/client/lib/RESP/types'; -import { RedisVariadicArgument } from '@redis/client/lib/commands/generic-transformers'; +import { CommandParser } from '@redis/client/dist/lib/client/parser'; +import { SimpleStringReply, Command, RedisArgument } from '@redis/client/dist/lib/RESP/types'; +import { RedisVariadicArgument } from '@redis/client/dist/lib/commands/generic-transformers'; export interface FtSynUpdateOptions { SKIPINITIALSCAN?: boolean; diff --git a/packages/search/lib/commands/TAGVALS.ts b/packages/search/lib/commands/TAGVALS.ts index d00d657f3ab..0afddb247fd 100644 --- a/packages/search/lib/commands/TAGVALS.ts +++ b/packages/search/lib/commands/TAGVALS.ts @@ -1,5 +1,5 @@ -import { CommandParser } from '@redis/client/lib/client/parser'; -import { RedisArgument, ArrayReply, SetReply, BlobStringReply, Command } from '@redis/client/lib/RESP/types'; +import { CommandParser } from '@redis/client/dist/lib/client/parser'; +import { RedisArgument, ArrayReply, SetReply, BlobStringReply, Command } from '@redis/client/dist/lib/RESP/types'; export default { NOT_KEYED_COMMAND: true, diff --git a/packages/search/lib/commands/_LIST.ts b/packages/search/lib/commands/_LIST.ts index 432eea64797..c1ca8cc2ee5 100644 --- a/packages/search/lib/commands/_LIST.ts +++ b/packages/search/lib/commands/_LIST.ts @@ -1,5 +1,5 @@ -import { CommandParser } from '@redis/client/lib/client/parser'; -import { ArrayReply, SetReply, BlobStringReply, Command } from '@redis/client/lib/RESP/types'; +import { CommandParser } from '@redis/client/dist/lib/client/parser'; +import { ArrayReply, SetReply, BlobStringReply, Command } from '@redis/client/dist/lib/RESP/types'; export default { NOT_KEYED_COMMAND: true, diff --git a/packages/test-utils/lib/dockers.ts b/packages/test-utils/lib/dockers.ts index e8d7ad45ca6..a1cb63eb7bf 100644 --- a/packages/test-utils/lib/dockers.ts +++ b/packages/test-utils/lib/dockers.ts @@ -2,7 +2,7 @@ import { createConnection } from 'node:net'; import { once } from 'node:events'; import { createClient } from '@redis/client/index'; import { setTimeout } from 'node:timers/promises'; -// import { ClusterSlotsReply } from '@redis/client/lib/commands/CLUSTER_SLOTS'; +// import { ClusterSlotsReply } from '@redis/client/dist/lib/commands/CLUSTER_SLOTS'; import { promisify } from 'node:util'; import { exec } from 'node:child_process'; const execAsync = promisify(exec); diff --git a/packages/time-series/lib/commands/ADD.ts b/packages/time-series/lib/commands/ADD.ts index f79a27c5db7..e7626d227da 100644 --- a/packages/time-series/lib/commands/ADD.ts +++ b/packages/time-series/lib/commands/ADD.ts @@ -1,5 +1,5 @@ -import { CommandParser } from '@redis/client/lib/client/parser'; -import { RedisArgument, NumberReply, Command } from '@redis/client/lib/RESP/types'; +import { CommandParser } from '@redis/client/dist/lib/client/parser'; +import { RedisArgument, NumberReply, Command } from '@redis/client/dist/lib/RESP/types'; import { transformTimestampArgument, parseRetentionArgument, diff --git a/packages/time-series/lib/commands/ALTER.ts b/packages/time-series/lib/commands/ALTER.ts index 8217e81c218..f7f6948da71 100644 --- a/packages/time-series/lib/commands/ALTER.ts +++ b/packages/time-series/lib/commands/ALTER.ts @@ -1,5 +1,5 @@ -import { CommandParser } from '@redis/client/lib/client/parser'; -import { RedisArgument, SimpleStringReply, Command } from '@redis/client/lib/RESP/types'; +import { CommandParser } from '@redis/client/dist/lib/client/parser'; +import { RedisArgument, SimpleStringReply, Command } from '@redis/client/dist/lib/RESP/types'; import { TsCreateOptions } from './CREATE'; import { parseRetentionArgument, parseChunkSizeArgument, parseDuplicatePolicy, parseLabelsArgument, parseIgnoreArgument } from '.'; diff --git a/packages/time-series/lib/commands/CREATE.ts b/packages/time-series/lib/commands/CREATE.ts index 86defd1e0a4..39f35c06ed7 100644 --- a/packages/time-series/lib/commands/CREATE.ts +++ b/packages/time-series/lib/commands/CREATE.ts @@ -1,5 +1,5 @@ -import { CommandParser } from '@redis/client/lib/client/parser'; -import { RedisArgument, SimpleStringReply, Command } from '@redis/client/lib/RESP/types'; +import { CommandParser } from '@redis/client/dist/lib/client/parser'; +import { RedisArgument, SimpleStringReply, Command } from '@redis/client/dist/lib/RESP/types'; import { parseRetentionArgument, TimeSeriesEncoding, diff --git a/packages/time-series/lib/commands/CREATERULE.ts b/packages/time-series/lib/commands/CREATERULE.ts index 99a8a4c9d57..1e4f38c6ee6 100644 --- a/packages/time-series/lib/commands/CREATERULE.ts +++ b/packages/time-series/lib/commands/CREATERULE.ts @@ -1,5 +1,5 @@ -import { CommandParser } from '@redis/client/lib/client/parser'; -import { RedisArgument, SimpleStringReply, Command } from '@redis/client/lib/RESP/types'; +import { CommandParser } from '@redis/client/dist/lib/client/parser'; +import { RedisArgument, SimpleStringReply, Command } from '@redis/client/dist/lib/RESP/types'; export const TIME_SERIES_AGGREGATION_TYPE = { AVG: 'AVG', diff --git a/packages/time-series/lib/commands/DECRBY.ts b/packages/time-series/lib/commands/DECRBY.ts index c2a7e6abd97..8ff09d926c0 100644 --- a/packages/time-series/lib/commands/DECRBY.ts +++ b/packages/time-series/lib/commands/DECRBY.ts @@ -1,4 +1,4 @@ -import { Command } from '@redis/client/lib/RESP/types'; +import { Command } from '@redis/client/dist/lib/RESP/types'; import INCRBY, { parseIncrByArguments } from './INCRBY'; export default { diff --git a/packages/time-series/lib/commands/DEL.ts b/packages/time-series/lib/commands/DEL.ts index ad957e6c402..fc96c989b18 100644 --- a/packages/time-series/lib/commands/DEL.ts +++ b/packages/time-series/lib/commands/DEL.ts @@ -1,6 +1,6 @@ -import { CommandParser } from '@redis/client/lib/client/parser'; +import { CommandParser } from '@redis/client/dist/lib/client/parser'; import { Timestamp, transformTimestampArgument } from '.'; -import { RedisArgument, NumberReply, Command, } from '@redis/client/lib/RESP/types'; +import { RedisArgument, NumberReply, Command, } from '@redis/client/dist/lib/RESP/types'; export default { IS_READ_ONLY: false, diff --git a/packages/time-series/lib/commands/DELETERULE.ts b/packages/time-series/lib/commands/DELETERULE.ts index 8a1aa41385d..b4e47a0fba6 100644 --- a/packages/time-series/lib/commands/DELETERULE.ts +++ b/packages/time-series/lib/commands/DELETERULE.ts @@ -1,5 +1,5 @@ -import { CommandParser } from '@redis/client/lib/client/parser'; -import { RedisArgument, SimpleStringReply, Command } from '@redis/client/lib/RESP/types'; +import { CommandParser } from '@redis/client/dist/lib/client/parser'; +import { RedisArgument, SimpleStringReply, Command } from '@redis/client/dist/lib/RESP/types'; export default { IS_READ_ONLY: false, diff --git a/packages/time-series/lib/commands/GET.ts b/packages/time-series/lib/commands/GET.ts index 9f165bed6eb..c1bb2c1c749 100644 --- a/packages/time-series/lib/commands/GET.ts +++ b/packages/time-series/lib/commands/GET.ts @@ -1,5 +1,5 @@ -import { CommandParser } from '@redis/client/lib/client/parser'; -import { RedisArgument, TuplesReply, NumberReply, DoubleReply, UnwrapReply, Resp2Reply, Command } from '@redis/client/lib/RESP/types'; +import { CommandParser } from '@redis/client/dist/lib/client/parser'; +import { RedisArgument, TuplesReply, NumberReply, DoubleReply, UnwrapReply, Resp2Reply, Command } from '@redis/client/dist/lib/RESP/types'; export interface TsGetOptions { LATEST?: boolean; diff --git a/packages/time-series/lib/commands/INCRBY.ts b/packages/time-series/lib/commands/INCRBY.ts index 7dbdb6b9ead..e62ec42690a 100644 --- a/packages/time-series/lib/commands/INCRBY.ts +++ b/packages/time-series/lib/commands/INCRBY.ts @@ -1,5 +1,5 @@ -import { CommandParser } from '@redis/client/lib/client/parser'; -import { RedisArgument, NumberReply, Command } from '@redis/client/lib/RESP/types'; +import { CommandParser } from '@redis/client/dist/lib/client/parser'; +import { RedisArgument, NumberReply, Command } from '@redis/client/dist/lib/RESP/types'; import { Timestamp, transformTimestampArgument, parseRetentionArgument, parseChunkSizeArgument, Labels, parseLabelsArgument, parseIgnoreArgument } from '.'; import { TsIgnoreOptions } from './ADD'; diff --git a/packages/time-series/lib/commands/INFO.ts b/packages/time-series/lib/commands/INFO.ts index fbd66875b1f..fe0e49e095a 100644 --- a/packages/time-series/lib/commands/INFO.ts +++ b/packages/time-series/lib/commands/INFO.ts @@ -1,8 +1,8 @@ -import { CommandParser } from '@redis/client/lib/client/parser'; -import { ArrayReply, BlobStringReply, Command, DoubleReply, NumberReply, ReplyUnion, SimpleStringReply, TypeMapping } from "@redis/client/lib/RESP/types"; +import { CommandParser } from '@redis/client/dist/lib/client/parser'; +import { ArrayReply, BlobStringReply, Command, DoubleReply, NumberReply, ReplyUnion, SimpleStringReply, TypeMapping } from "@redis/client/dist/lib/RESP/types"; import { TimeSeriesDuplicatePolicies } from "."; import { TimeSeriesAggregationType } from "./CREATERULE"; -import { transformDoubleReply } from '@redis/client/lib/commands/generic-transformers'; +import { transformDoubleReply } from '@redis/client/dist/lib/commands/generic-transformers'; export type InfoRawReplyTypes = SimpleStringReply | NumberReply | diff --git a/packages/time-series/lib/commands/INFO_DEBUG.ts b/packages/time-series/lib/commands/INFO_DEBUG.ts index bee1147f2bb..89d66a36ef8 100644 --- a/packages/time-series/lib/commands/INFO_DEBUG.ts +++ b/packages/time-series/lib/commands/INFO_DEBUG.ts @@ -1,5 +1,5 @@ -import { CommandParser } from '@redis/client/lib/client/parser'; -import { BlobStringReply, Command, NumberReply, SimpleStringReply, TypeMapping, ReplyUnion } from "@redis/client/lib/RESP/types"; +import { CommandParser } from '@redis/client/dist/lib/client/parser'; +import { BlobStringReply, Command, NumberReply, SimpleStringReply, TypeMapping, ReplyUnion } from "@redis/client/dist/lib/RESP/types"; import INFO, { InfoRawReply, InfoRawReplyTypes, InfoReply } from "./INFO"; type chunkType = Array<[ diff --git a/packages/time-series/lib/commands/MADD.ts b/packages/time-series/lib/commands/MADD.ts index cb1f077055a..5af94d6d497 100644 --- a/packages/time-series/lib/commands/MADD.ts +++ b/packages/time-series/lib/commands/MADD.ts @@ -1,6 +1,6 @@ -import { CommandParser } from '@redis/client/lib/client/parser'; +import { CommandParser } from '@redis/client/dist/lib/client/parser'; import { Timestamp, transformTimestampArgument } from '.'; -import { ArrayReply, NumberReply, SimpleErrorReply, Command } from '@redis/client/lib/RESP/types'; +import { ArrayReply, NumberReply, SimpleErrorReply, Command } from '@redis/client/dist/lib/RESP/types'; export interface TsMAddSample { key: string; diff --git a/packages/time-series/lib/commands/MGET.ts b/packages/time-series/lib/commands/MGET.ts index add742a70d5..fa4e3fc63d6 100644 --- a/packages/time-series/lib/commands/MGET.ts +++ b/packages/time-series/lib/commands/MGET.ts @@ -1,7 +1,7 @@ -import { CommandParser } from '@redis/client/lib/client/parser'; -import { Command, BlobStringReply, ArrayReply, Resp2Reply, MapReply, TuplesReply, TypeMapping } from '@redis/client/lib/RESP/types'; +import { CommandParser } from '@redis/client/dist/lib/client/parser'; +import { Command, BlobStringReply, ArrayReply, Resp2Reply, MapReply, TuplesReply, TypeMapping } from '@redis/client/dist/lib/RESP/types'; import { resp2MapToValue, resp3MapToValue, SampleRawReply, transformSampleReply } from '.'; -import { RedisVariadicArgument } from '@redis/client/lib/commands/generic-transformers'; +import { RedisVariadicArgument } from '@redis/client/dist/lib/commands/generic-transformers'; export interface TsMGetOptions { LATEST?: boolean; diff --git a/packages/time-series/lib/commands/MGET_SELECTED_LABELS.ts b/packages/time-series/lib/commands/MGET_SELECTED_LABELS.ts index 67c7dc79600..e93b517f80a 100644 --- a/packages/time-series/lib/commands/MGET_SELECTED_LABELS.ts +++ b/packages/time-series/lib/commands/MGET_SELECTED_LABELS.ts @@ -1,6 +1,6 @@ -import { CommandParser } from '@redis/client/lib/client/parser'; -import { Command, BlobStringReply, NullReply } from '@redis/client/lib/RESP/types'; -import { RedisVariadicArgument } from '@redis/client/lib/commands/generic-transformers'; +import { CommandParser } from '@redis/client/dist/lib/client/parser'; +import { Command, BlobStringReply, NullReply } from '@redis/client/dist/lib/RESP/types'; +import { RedisVariadicArgument } from '@redis/client/dist/lib/commands/generic-transformers'; import { TsMGetOptions, parseLatestArgument, parseFilterArgument } from './MGET'; import { parseSelectedLabelsArguments } from '.'; import { createTransformMGetLabelsReply } from './MGET_WITHLABELS'; diff --git a/packages/time-series/lib/commands/MGET_WITHLABELS.ts b/packages/time-series/lib/commands/MGET_WITHLABELS.ts index f6d50c91b14..38b8442db31 100644 --- a/packages/time-series/lib/commands/MGET_WITHLABELS.ts +++ b/packages/time-series/lib/commands/MGET_WITHLABELS.ts @@ -1,6 +1,6 @@ -import { CommandParser } from '@redis/client/lib/client/parser'; -import { Command, BlobStringReply, ArrayReply, Resp2Reply, MapReply, TuplesReply, TypeMapping } from '@redis/client/lib/RESP/types'; -import { RedisVariadicArgument } from '@redis/client/lib/commands/generic-transformers'; +import { CommandParser } from '@redis/client/dist/lib/client/parser'; +import { Command, BlobStringReply, ArrayReply, Resp2Reply, MapReply, TuplesReply, TypeMapping } from '@redis/client/dist/lib/RESP/types'; +import { RedisVariadicArgument } from '@redis/client/dist/lib/commands/generic-transformers'; import { TsMGetOptions, parseLatestArgument, parseFilterArgument } from './MGET'; import { RawLabelValue, resp2MapToValue, resp3MapToValue, SampleRawReply, transformRESP2Labels, transformSampleReply } from '.'; diff --git a/packages/time-series/lib/commands/MRANGE.ts b/packages/time-series/lib/commands/MRANGE.ts index dbe48d6f542..95fa5297bdd 100644 --- a/packages/time-series/lib/commands/MRANGE.ts +++ b/packages/time-series/lib/commands/MRANGE.ts @@ -1,6 +1,6 @@ -import { CommandParser } from '@redis/client/lib/client/parser'; -import { Command, ArrayReply, BlobStringReply, Resp2Reply, MapReply, TuplesReply, TypeMapping, RedisArgument } from '@redis/client/lib/RESP/types'; -import { RedisVariadicArgument } from '@redis/client/lib/commands/generic-transformers'; +import { CommandParser } from '@redis/client/dist/lib/client/parser'; +import { Command, ArrayReply, BlobStringReply, Resp2Reply, MapReply, TuplesReply, TypeMapping, RedisArgument } from '@redis/client/dist/lib/RESP/types'; +import { RedisVariadicArgument } from '@redis/client/dist/lib/commands/generic-transformers'; import { resp2MapToValue, resp3MapToValue, SampleRawReply, Timestamp, transformSamplesReply } from '.'; import { TsRangeOptions, parseRangeArguments } from './RANGE'; import { parseFilterArgument } from './MGET'; diff --git a/packages/time-series/lib/commands/MRANGE_GROUPBY.ts b/packages/time-series/lib/commands/MRANGE_GROUPBY.ts index 0d996521d1c..5ccd61b2a26 100644 --- a/packages/time-series/lib/commands/MRANGE_GROUPBY.ts +++ b/packages/time-series/lib/commands/MRANGE_GROUPBY.ts @@ -1,6 +1,6 @@ -import { CommandParser } from '@redis/client/lib/client/parser'; -import { Command, ArrayReply, BlobStringReply, Resp2Reply, MapReply, TuplesReply, TypeMapping, RedisArgument, TuplesToMapReply, UnwrapReply } from '@redis/client/lib/RESP/types'; -import { RedisVariadicArgument } from '@redis/client/lib/commands/generic-transformers'; +import { CommandParser } from '@redis/client/dist/lib/client/parser'; +import { Command, ArrayReply, BlobStringReply, Resp2Reply, MapReply, TuplesReply, TypeMapping, RedisArgument, TuplesToMapReply, UnwrapReply } from '@redis/client/dist/lib/RESP/types'; +import { RedisVariadicArgument } from '@redis/client/dist/lib/commands/generic-transformers'; import { resp2MapToValue, resp3MapToValue, SampleRawReply, Timestamp, transformSamplesReply } from '.'; import { TsRangeOptions, parseRangeArguments } from './RANGE'; import { parseFilterArgument } from './MGET'; diff --git a/packages/time-series/lib/commands/MRANGE_SELECTED_LABELS.ts b/packages/time-series/lib/commands/MRANGE_SELECTED_LABELS.ts index 115944a2f40..643b57a67e7 100644 --- a/packages/time-series/lib/commands/MRANGE_SELECTED_LABELS.ts +++ b/packages/time-series/lib/commands/MRANGE_SELECTED_LABELS.ts @@ -1,6 +1,6 @@ -import { CommandParser } from '@redis/client/lib/client/parser'; -import { Command, ArrayReply, BlobStringReply, Resp2Reply, MapReply, TuplesReply, TypeMapping, NullReply, RedisArgument } from '@redis/client/lib/RESP/types'; -import { RedisVariadicArgument } from '@redis/client/lib/commands/generic-transformers'; +import { CommandParser } from '@redis/client/dist/lib/client/parser'; +import { Command, ArrayReply, BlobStringReply, Resp2Reply, MapReply, TuplesReply, TypeMapping, NullReply, RedisArgument } from '@redis/client/dist/lib/RESP/types'; +import { RedisVariadicArgument } from '@redis/client/dist/lib/commands/generic-transformers'; import { parseSelectedLabelsArguments, resp2MapToValue, resp3MapToValue, SampleRawReply, Timestamp, transformRESP2Labels, transformSamplesReply } from '.'; import { TsRangeOptions, parseRangeArguments } from './RANGE'; import { parseFilterArgument } from './MGET'; diff --git a/packages/time-series/lib/commands/MRANGE_SELECTED_LABELS_GROUPBY.ts b/packages/time-series/lib/commands/MRANGE_SELECTED_LABELS_GROUPBY.ts index b4e8006a84e..c5cf1ef56c5 100644 --- a/packages/time-series/lib/commands/MRANGE_SELECTED_LABELS_GROUPBY.ts +++ b/packages/time-series/lib/commands/MRANGE_SELECTED_LABELS_GROUPBY.ts @@ -1,6 +1,6 @@ -import { CommandParser } from '@redis/client/lib/client/parser'; -import { Command, ArrayReply, BlobStringReply, MapReply, TuplesReply, RedisArgument, NullReply } from '@redis/client/lib/RESP/types'; -import { RedisVariadicArgument } from '@redis/client/lib/commands/generic-transformers'; +import { CommandParser } from '@redis/client/dist/lib/client/parser'; +import { Command, ArrayReply, BlobStringReply, MapReply, TuplesReply, RedisArgument, NullReply } from '@redis/client/dist/lib/RESP/types'; +import { RedisVariadicArgument } from '@redis/client/dist/lib/commands/generic-transformers'; import { parseSelectedLabelsArguments, resp3MapToValue, SampleRawReply, Timestamp, transformSamplesReply } from '.'; import { TsRangeOptions, parseRangeArguments } from './RANGE'; import { extractResp3MRangeSources, parseGroupByArguments, TsMRangeGroupBy, TsMRangeGroupByRawMetadataReply3 } from './MRANGE_GROUPBY'; diff --git a/packages/time-series/lib/commands/MRANGE_WITHLABELS.ts b/packages/time-series/lib/commands/MRANGE_WITHLABELS.ts index 04d72411b7e..19641596a67 100644 --- a/packages/time-series/lib/commands/MRANGE_WITHLABELS.ts +++ b/packages/time-series/lib/commands/MRANGE_WITHLABELS.ts @@ -1,6 +1,6 @@ -import { CommandParser } from '@redis/client/lib/client/parser'; -import { Command, UnwrapReply, ArrayReply, BlobStringReply, Resp2Reply, MapReply, TuplesReply, TypeMapping, RedisArgument } from '@redis/client/lib/RESP/types'; -import { RedisVariadicArgument } from '@redis/client/lib/commands/generic-transformers'; +import { CommandParser } from '@redis/client/dist/lib/client/parser'; +import { Command, UnwrapReply, ArrayReply, BlobStringReply, Resp2Reply, MapReply, TuplesReply, TypeMapping, RedisArgument } from '@redis/client/dist/lib/RESP/types'; +import { RedisVariadicArgument } from '@redis/client/dist/lib/commands/generic-transformers'; import { resp2MapToValue, resp3MapToValue, SampleRawReply, Timestamp, transformSamplesReply } from '.'; import { TsRangeOptions, parseRangeArguments } from './RANGE'; import { parseFilterArgument } from './MGET'; diff --git a/packages/time-series/lib/commands/MRANGE_WITHLABELS_GROUPBY.ts b/packages/time-series/lib/commands/MRANGE_WITHLABELS_GROUPBY.ts index f09d630dcd4..ff0065e22b7 100644 --- a/packages/time-series/lib/commands/MRANGE_WITHLABELS_GROUPBY.ts +++ b/packages/time-series/lib/commands/MRANGE_WITHLABELS_GROUPBY.ts @@ -1,6 +1,6 @@ -import { CommandParser } from '@redis/client/lib/client/parser'; -import { Command, ArrayReply, BlobStringReply, Resp2Reply, MapReply, TuplesReply, TypeMapping, RedisArgument } from '@redis/client/lib/RESP/types'; -import { RedisVariadicArgument } from '@redis/client/lib/commands/generic-transformers'; +import { CommandParser } from '@redis/client/dist/lib/client/parser'; +import { Command, ArrayReply, BlobStringReply, Resp2Reply, MapReply, TuplesReply, TypeMapping, RedisArgument } from '@redis/client/dist/lib/RESP/types'; +import { RedisVariadicArgument } from '@redis/client/dist/lib/commands/generic-transformers'; import { resp2MapToValue, resp3MapToValue, SampleRawReply, Timestamp, transformRESP2LabelsWithSources, transformSamplesReply } from '.'; import { TsRangeOptions, parseRangeArguments } from './RANGE'; import { extractResp3MRangeSources, parseGroupByArguments, TsMRangeGroupBy, TsMRangeGroupByRawMetadataReply3 } from './MRANGE_GROUPBY'; diff --git a/packages/time-series/lib/commands/MREVRANGE.ts b/packages/time-series/lib/commands/MREVRANGE.ts index e2ed6d9cc91..99d3123dd27 100644 --- a/packages/time-series/lib/commands/MREVRANGE.ts +++ b/packages/time-series/lib/commands/MREVRANGE.ts @@ -1,4 +1,4 @@ -import { Command } from '@redis/client/lib/RESP/types'; +import { Command } from '@redis/client/dist/lib/RESP/types'; import MRANGE, { createTransformMRangeArguments } from './MRANGE'; export default { diff --git a/packages/time-series/lib/commands/MREVRANGE_GROUPBY.ts b/packages/time-series/lib/commands/MREVRANGE_GROUPBY.ts index efdd1acdcfe..4afcd113505 100644 --- a/packages/time-series/lib/commands/MREVRANGE_GROUPBY.ts +++ b/packages/time-series/lib/commands/MREVRANGE_GROUPBY.ts @@ -1,4 +1,4 @@ -import { Command } from '@redis/client/lib/RESP/types'; +import { Command } from '@redis/client/dist/lib/RESP/types'; import MRANGE_GROUPBY, { createTransformMRangeGroupByArguments } from './MRANGE_GROUPBY'; export default { diff --git a/packages/time-series/lib/commands/MREVRANGE_SELECTED_LABELS.ts b/packages/time-series/lib/commands/MREVRANGE_SELECTED_LABELS.ts index 8b679e65b6a..10e00fc7a29 100644 --- a/packages/time-series/lib/commands/MREVRANGE_SELECTED_LABELS.ts +++ b/packages/time-series/lib/commands/MREVRANGE_SELECTED_LABELS.ts @@ -1,4 +1,4 @@ -import { Command } from '@redis/client/lib/RESP/types'; +import { Command } from '@redis/client/dist/lib/RESP/types'; import MRANGE_SELECTED_LABELS, { createTransformMRangeSelectedLabelsArguments } from './MRANGE_SELECTED_LABELS'; export default { diff --git a/packages/time-series/lib/commands/MREVRANGE_SELECTED_LABELS_GROUPBY.ts b/packages/time-series/lib/commands/MREVRANGE_SELECTED_LABELS_GROUPBY.ts index d01ebe1033a..b000c04c183 100644 --- a/packages/time-series/lib/commands/MREVRANGE_SELECTED_LABELS_GROUPBY.ts +++ b/packages/time-series/lib/commands/MREVRANGE_SELECTED_LABELS_GROUPBY.ts @@ -1,4 +1,4 @@ -import { Command } from '@redis/client/lib/RESP/types'; +import { Command } from '@redis/client/dist/lib/RESP/types'; import MRANGE_SELECTED_LABELS_GROUPBY, { createMRangeSelectedLabelsGroupByTransformArguments } from './MRANGE_SELECTED_LABELS_GROUPBY'; export default { diff --git a/packages/time-series/lib/commands/MREVRANGE_WITHLABELS.ts b/packages/time-series/lib/commands/MREVRANGE_WITHLABELS.ts index d4f6255592e..6cde143c422 100644 --- a/packages/time-series/lib/commands/MREVRANGE_WITHLABELS.ts +++ b/packages/time-series/lib/commands/MREVRANGE_WITHLABELS.ts @@ -1,4 +1,4 @@ -import { Command } from '@redis/client/lib/RESP/types'; +import { Command } from '@redis/client/dist/lib/RESP/types'; import MRANGE_WITHLABELS, { createTransformMRangeWithLabelsArguments } from './MRANGE_WITHLABELS'; export default { diff --git a/packages/time-series/lib/commands/MREVRANGE_WITHLABELS_GROUPBY.ts b/packages/time-series/lib/commands/MREVRANGE_WITHLABELS_GROUPBY.ts index ed43d0eae6b..4727112b974 100644 --- a/packages/time-series/lib/commands/MREVRANGE_WITHLABELS_GROUPBY.ts +++ b/packages/time-series/lib/commands/MREVRANGE_WITHLABELS_GROUPBY.ts @@ -1,4 +1,4 @@ -import { Command } from '@redis/client/lib/RESP/types'; +import { Command } from '@redis/client/dist/lib/RESP/types'; import MRANGE_WITHLABELS_GROUPBY, { createMRangeWithLabelsGroupByTransformArguments } from './MRANGE_WITHLABELS_GROUPBY'; export default { diff --git a/packages/time-series/lib/commands/QUERYINDEX.ts b/packages/time-series/lib/commands/QUERYINDEX.ts index 69625801974..1b53e84b7a3 100644 --- a/packages/time-series/lib/commands/QUERYINDEX.ts +++ b/packages/time-series/lib/commands/QUERYINDEX.ts @@ -1,6 +1,6 @@ -import { CommandParser } from '@redis/client/lib/client/parser'; -import { ArrayReply, BlobStringReply, SetReply, Command } from '@redis/client/lib/RESP/types'; -import { RedisVariadicArgument } from '@redis/client/lib/commands/generic-transformers'; +import { CommandParser } from '@redis/client/dist/lib/client/parser'; +import { ArrayReply, BlobStringReply, SetReply, Command } from '@redis/client/dist/lib/RESP/types'; +import { RedisVariadicArgument } from '@redis/client/dist/lib/commands/generic-transformers'; export default { NOT_KEYED_COMMAND: true, diff --git a/packages/time-series/lib/commands/RANGE.ts b/packages/time-series/lib/commands/RANGE.ts index ef4c79fe603..f7f808cecdb 100644 --- a/packages/time-series/lib/commands/RANGE.ts +++ b/packages/time-series/lib/commands/RANGE.ts @@ -1,8 +1,8 @@ -import { CommandParser } from '@redis/client/lib/client/parser'; -import { RedisArgument, Command } from '@redis/client/lib/RESP/types'; +import { CommandParser } from '@redis/client/dist/lib/client/parser'; +import { RedisArgument, Command } from '@redis/client/dist/lib/RESP/types'; import { Timestamp, transformTimestampArgument, SamplesRawReply, transformSamplesReply } from '.'; import { TimeSeriesAggregationType } from './CREATERULE'; -import { Resp2Reply } from '@redis/client/lib/RESP/types'; +import { Resp2Reply } from '@redis/client/dist/lib/RESP/types'; export const TIME_SERIES_BUCKET_TIMESTAMP = { LOW: '-', diff --git a/packages/time-series/lib/commands/REVRANGE.ts b/packages/time-series/lib/commands/REVRANGE.ts index 24a31a785a2..238b2ce9fe7 100644 --- a/packages/time-series/lib/commands/REVRANGE.ts +++ b/packages/time-series/lib/commands/REVRANGE.ts @@ -1,4 +1,4 @@ -import { Command } from '@redis/client/lib/RESP/types'; +import { Command } from '@redis/client/dist/lib/RESP/types'; import RANGE, { transformRangeArguments } from './RANGE'; export default { diff --git a/packages/time-series/lib/commands/index.ts b/packages/time-series/lib/commands/index.ts index e0389a60a2e..f340861cb96 100644 --- a/packages/time-series/lib/commands/index.ts +++ b/packages/time-series/lib/commands/index.ts @@ -1,4 +1,4 @@ -import type { DoubleReply, NumberReply, RedisCommands, TuplesReply, UnwrapReply, Resp2Reply, ArrayReply, BlobStringReply, MapReply, NullReply, TypeMapping, ReplyUnion, RespType } from '@redis/client/lib/RESP/types'; +import type { DoubleReply, NumberReply, RedisCommands, TuplesReply, UnwrapReply, Resp2Reply, ArrayReply, BlobStringReply, MapReply, NullReply, TypeMapping, ReplyUnion, RespType } from '@redis/client/dist/lib/RESP/types'; import ADD, { TsIgnoreOptions } from './ADD'; import ALTER from './ALTER'; import CREATE from './CREATE'; @@ -29,9 +29,9 @@ import MREVRANGE from './MREVRANGE'; import QUERYINDEX from './QUERYINDEX'; import RANGE from './RANGE'; import REVRANGE from './REVRANGE'; -import { RedisVariadicArgument } from '@redis/client/lib/commands/generic-transformers'; -import { CommandParser } from '@redis/client/lib/client/parser'; -import { RESP_TYPES } from '@redis/client/lib/RESP/decoder'; +import { RedisVariadicArgument } from '@redis/client/dist/lib/commands/generic-transformers'; +import { CommandParser } from '@redis/client/dist/lib/client/parser'; +import { RESP_TYPES } from '@redis/client/dist/lib/RESP/decoder'; export default { ADD, From b87b8b113430eb5e860ab435dc84e141a2e8ae98 Mon Sep 17 00:00:00 2001 From: Leibale Eidelman Date: Mon, 4 Nov 2024 12:21:41 -0500 Subject: [PATCH 1482/1748] add createSentinel to the "redis" package --- packages/redis/index.ts | 47 +++++++++++++++++++++++++++++++++-------- 1 file changed, 38 insertions(+), 9 deletions(-) diff --git a/packages/redis/index.ts b/packages/redis/index.ts index 7586846d12c..572b45b707b 100644 --- a/packages/redis/index.ts +++ b/packages/redis/index.ts @@ -4,12 +4,15 @@ import { RedisScripts, RespVersions, TypeMapping, - createClient as _createClient, + createClient as genericCreateClient, RedisClientOptions, - RedisClientType as _RedisClientType, - createCluster as _createCluster, + RedisClientType as GenericRedisClientType, + createCluster as genericCreateCluster, RedisClusterOptions, - RedisClusterType as _RedisClusterType, + RedisClusterType as genericRedisClusterType, + RedisSentinelOptions, + RedisSentinelType as genericRedisSentinelType, + createSentinel as genericCreateSentinel } from '@redis/client'; import RedisBloomModules from '@redis/bloom'; import RedisGraph from '@redis/graph'; @@ -40,7 +43,7 @@ export type RedisClientType< S extends RedisScripts = {}, RESP extends RespVersions = 2, TYPE_MAPPING extends TypeMapping = {} -> = _RedisClientType; +> = GenericRedisClientType; export function createClient< M extends RedisModules, @@ -50,8 +53,8 @@ export function createClient< TYPE_MAPPING extends TypeMapping >( options?: RedisClientOptions -): _RedisClientType { - return _createClient({ +): GenericRedisClientType { + return genericCreateClient({ ...options, modules: { ...modules, @@ -66,7 +69,7 @@ export type RedisClusterType< S extends RedisScripts = {}, RESP extends RespVersions = 2, TYPE_MAPPING extends TypeMapping = {} -> = _RedisClusterType; +> = genericRedisClusterType; export function createCluster< M extends RedisModules, @@ -77,7 +80,33 @@ export function createCluster< >( options: RedisClusterOptions ): RedisClusterType { - return _createCluster({ + return genericCreateCluster({ + ...options, + modules: { + ...modules, + ...(options?.modules as M) + } + }); +} + +export type RedisSentinelType< + M extends RedisModules = RedisDefaultModules, + F extends RedisFunctions = {}, + S extends RedisScripts = {}, + RESP extends RespVersions = 2, + TYPE_MAPPING extends TypeMapping = {} +> = genericRedisSentinelType; + +export function createSentinel< + M extends RedisModules, + F extends RedisFunctions, + S extends RedisScripts, + RESP extends RespVersions, + TYPE_MAPPING extends TypeMapping +>( + options: RedisSentinelOptions +): RedisSentinelType { + return genericCreateSentinel({ ...options, modules: { ...modules, From 9a3e1c5e0327799283053acaf2aa3799cf49cde9 Mon Sep 17 00:00:00 2001 From: Max Gruenfelder Date: Mon, 25 Nov 2024 21:28:44 +0100 Subject: [PATCH 1483/1748] Fix creation of cluster client again (#2870) * shallow copy of this.#options.defaults.socket * shallow copy of this.#options.defaults.socket * nit * fix redis create cluster client again --------- Co-authored-by: Max Gruenfelder Co-authored-by: Leibale Eidelman --- packages/client/lib/cluster/cluster-slots.ts | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/packages/client/lib/cluster/cluster-slots.ts b/packages/client/lib/cluster/cluster-slots.ts index 824cf2ae813..10c2c9d306b 100644 --- a/packages/client/lib/cluster/cluster-slots.ts +++ b/packages/client/lib/cluster/cluster-slots.ts @@ -261,10 +261,10 @@ export default class RedisClusterSlots< let socket; if (this.#options.defaults.socket) { - socket = options?.socket ? { + socket = { ...this.#options.defaults.socket, - ...options.socket - } : this.#options.defaults.socket; + ...options?.socket + }; } else { socket = options?.socket; } From ffa7d2525c6d8bdd24faaad527a3ad0aec2cd8e9 Mon Sep 17 00:00:00 2001 From: Jeremy <98552694+jjsimps@users.noreply.github.com> Date: Mon, 25 Nov 2024 14:36:33 -0600 Subject: [PATCH 1484/1748] Fix cluster-slots discover race condition again (#2867) --- packages/client/lib/cluster/cluster-slots.ts | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/packages/client/lib/cluster/cluster-slots.ts b/packages/client/lib/cluster/cluster-slots.ts index 10c2c9d306b..3a4adff73c4 100644 --- a/packages/client/lib/cluster/cluster-slots.ts +++ b/packages/client/lib/cluster/cluster-slots.ts @@ -164,13 +164,14 @@ export default class RedisClusterSlots< } async #discover(rootNode: RedisClusterClientOptions) { - this.#resetSlots(); try { const addressesInUse = new Set(), promises: Array> = [], eagerConnect = this.#options.minimizeConnections !== true; - for (const { from, to, master, replicas } of await this.#getShards(rootNode)) { + const shards = await this.#getShards(rootNode); + this.#resetSlots(); // Reset slots AFTER shards have been fetched to prevent a race condition + for (const { from, to, master, replicas } of shards) { const shard: Shard = { master: this.#initiateSlotNode(master, false, eagerConnect, addressesInUse, promises) }; From ae89341780b529639e47d20c7ce457a213e27f93 Mon Sep 17 00:00:00 2001 From: mohamed amine ozennou <102986762+ozennou@users.noreply.github.com> Date: Wed, 29 Jan 2025 11:40:58 +0100 Subject: [PATCH 1485/1748] chore: Update tests.yml (#2887) - Add node 22 - Update actions/setup-node from v3 to v4 - Ignore .md files from triggering the workflow --- .github/workflows/tests.yml | 8 ++++++-- 1 file changed, 6 insertions(+), 2 deletions(-) diff --git a/.github/workflows/tests.yml b/.github/workflows/tests.yml index 68ea09c6e4f..9decd26898a 100644 --- a/.github/workflows/tests.yml +++ b/.github/workflows/tests.yml @@ -6,25 +6,29 @@ on: - master - v4.0 - v5 + paths-ignore: + - '**/*.md' pull_request: branches: - master - v4.0 - v5 + paths-ignore: + - '**/*.md' jobs: tests: runs-on: ubuntu-latest strategy: fail-fast: false matrix: - node-version: ['18', '20'] + node-version: ['18', '20', '22'] redis-version: ['6.2.6-v17', '7.2.0-v13', '7.4.0-v1'] steps: - uses: actions/checkout@v4 with: fetch-depth: 1 - name: Use Node.js ${{ matrix.node-version }} - uses: actions/setup-node@v3 + uses: actions/setup-node@v4 with: node-version: ${{ matrix.node-version }} - name: Update npm From 6d21de3f311876452c938a9c020c4f8a871c0e05 Mon Sep 17 00:00:00 2001 From: "Bobby I." Date: Thu, 30 Jan 2025 10:29:19 +0200 Subject: [PATCH 1486/1748] feat(auth): add Entra ID identity provider integration for Redis client authentication (#2877) * feat(auth): refactor authentication mechanism to use CredentialsProvider - Introduce new credential providers: AsyncCredentialsProvider, StreamingCredentialsProvider - Update client handshake process to use the new CredentialsProviders and to support async credentials fetch / credentials refresh - Internal conversion of username/password to a CredentialsProvider - Modify URL parsing to accommodate the new authentication structure - Tests * feat(auth): auth extensions Introduces TokenManager and supporting classes to handle token acquisition, automatic refresh, and updates via identity providers. This foundation enables consistent authentication token management across different identity provider implementations. Key additions: - Add TokenManager to obtain and maintain auth tokens from identity providers with automated refresh scheduling based on TTL and configurable thresholds - Add IdentityProvider interface for token acquisition from auth providers - Implement Token class for managing token state and TTL tracking - Include configurable retry mechanism with exponential backoff and jitter - Add comprehensive test suite covering refresh cycles and error handling This change establishes the core infrastructure needed for reliable token lifecycle management across different authentication providers. * feat(auth): add Entra ID identity provider integration Introduces Entra ID (former Azure AD) authentication support with multiple authentication flows and automated token lifecycle management. Key additions: - Add EntraIdCredentialsProvider for handling Entra ID authentication flows - Implement MSALIdentityProvider to integrate with MSAL/EntraID authentication library - Add support for multiple authentication methods: - Managed identities (system and user-assigned) - Client credentials with certificate - Client credentials with secret - Authorization Code flow with PKCE - Add factory class with builder methods for each authentication flow - Include sample Express server implementation for Authorization Code flow - Add comprehensive configuration options for authority and token management * feat(test-utils): improve cluster testing - Add support for configuring replica authentication with 'masterauth' - Allow default client configuration during test cluster creation This improves the testing framework's flexibility by automatically configuring replica authentication when '--requirepass' is used and enabling custom client configurations across cluster nodes. * feat(auth): add EntraId integration tests - Add integration tests for token renewal and re-authentication flows - Update credentials provider to use uniqueId as username instead of account username - Add test utilities for loading Redis endpoint configurations - Split TypeScript configs into separate files for samples and integration tests - Remove `@redis/authx` package and nest it under `@` --- .github/release-drafter/entraid-config.yml | 50 + .github/workflows/release-drafter-entraid.yml | 24 + package-lock.json | 1150 ++++++++++++++++- .../client/lib/authx/credentials-provider.ts | 102 ++ packages/client/lib/authx/disposable.ts | 6 + .../client/lib/authx/identity-provider.ts | 22 + packages/client/lib/authx/index.ts | 15 + .../client/lib/authx/token-manager.spec.ts | 588 +++++++++ packages/client/lib/authx/token-manager.ts | 318 +++++ packages/client/lib/authx/token.ts | 23 + packages/client/lib/client/index.spec.ts | 121 +- packages/client/lib/client/index.ts | 143 +- packages/client/lib/test-utils.ts | 38 + packages/entraid/.nycrc.json | 10 + packages/entraid/.release-it.json | 11 + packages/entraid/README.md | 137 ++ .../entraid-integration.spec.ts | 217 ++++ .../entra-id-credentials-provider-factory.ts | 371 ++++++ .../lib/entraid-credentials-provider.spec.ts | 199 +++ .../lib/entraid-credentials-provider.ts | 140 ++ packages/entraid/lib/index.ts | 3 + .../entraid/lib/msal-identity-provider.ts | 31 + packages/entraid/lib/test-utils.ts | 46 + packages/entraid/package.json | 47 + .../entraid/samples/auth-code-pkce/index.ts | 153 +++ .../entraid/tsconfig.integration-tests.json | 10 + packages/entraid/tsconfig.json | 20 + packages/entraid/tsconfig.samples.json | 10 + packages/test-utils/lib/cae-client-testing.ts | 30 + packages/test-utils/lib/dockers.ts | 36 +- packages/test-utils/lib/index.ts | 3 +- tsconfig.json | 46 +- 32 files changed, 4004 insertions(+), 116 deletions(-) create mode 100644 .github/release-drafter/entraid-config.yml create mode 100644 .github/workflows/release-drafter-entraid.yml create mode 100644 packages/client/lib/authx/credentials-provider.ts create mode 100644 packages/client/lib/authx/disposable.ts create mode 100644 packages/client/lib/authx/identity-provider.ts create mode 100644 packages/client/lib/authx/index.ts create mode 100644 packages/client/lib/authx/token-manager.spec.ts create mode 100644 packages/client/lib/authx/token-manager.ts create mode 100644 packages/client/lib/authx/token.ts create mode 100644 packages/entraid/.nycrc.json create mode 100644 packages/entraid/.release-it.json create mode 100644 packages/entraid/README.md create mode 100644 packages/entraid/integration-tests/entraid-integration.spec.ts create mode 100644 packages/entraid/lib/entra-id-credentials-provider-factory.ts create mode 100644 packages/entraid/lib/entraid-credentials-provider.spec.ts create mode 100644 packages/entraid/lib/entraid-credentials-provider.ts create mode 100644 packages/entraid/lib/index.ts create mode 100644 packages/entraid/lib/msal-identity-provider.ts create mode 100644 packages/entraid/lib/test-utils.ts create mode 100644 packages/entraid/package.json create mode 100644 packages/entraid/samples/auth-code-pkce/index.ts create mode 100644 packages/entraid/tsconfig.integration-tests.json create mode 100644 packages/entraid/tsconfig.json create mode 100644 packages/entraid/tsconfig.samples.json create mode 100644 packages/test-utils/lib/cae-client-testing.ts diff --git a/.github/release-drafter/entraid-config.yml b/.github/release-drafter/entraid-config.yml new file mode 100644 index 00000000000..d0ddd00773a --- /dev/null +++ b/.github/release-drafter/entraid-config.yml @@ -0,0 +1,50 @@ +name-template: 'entraid@$NEXT_PATCH_VERSION' +tag-template: 'entraid@$NEXT_PATCH_VERSION' +autolabeler: + - label: 'chore' + files: + - '*.md' + - '.github/*' + - label: 'bug' + branch: + - '/bug-.+' + - label: 'chore' + branch: + - '/chore-.+' + - label: 'feature' + branch: + - '/feature-.+' +categories: + - title: 'Breaking Changes' + labels: + - 'breakingchange' + - title: '🚀 New Features' + labels: + - 'feature' + - 'enhancement' + - title: '🐛 Bug Fixes' + labels: + - 'fix' + - 'bugfix' + - 'bug' + - title: '🧰 Maintenance' + label: + - 'chore' + - 'maintenance' + - 'documentation' + - 'docs' + +change-template: '- $TITLE (#$NUMBER)' +include-paths: + - 'packages/entraid' +exclude-labels: + - 'skip-changelog' +template: | + ## Changes + + $CHANGES + + ## Contributors + We'd like to thank all the contributors who worked on this release! + + $CONTRIBUTORS diff --git a/.github/workflows/release-drafter-entraid.yml b/.github/workflows/release-drafter-entraid.yml new file mode 100644 index 00000000000..d522c6cef6f --- /dev/null +++ b/.github/workflows/release-drafter-entraid.yml @@ -0,0 +1,24 @@ +name: Release Drafter + +on: + push: + # branches to consider in the event; optional, defaults to all + branches: + - master + +jobs: + + update_release_draft: + + permissions: + contents: write + pull-requests: write + runs-on: ubuntu-latest + steps: + # Drafts your next Release notes as Pull Requests are merged into "master" + - uses: release-drafter/release-drafter@v5 + with: + # (Optional) specify config name to use, relative to .github/. Default: release-drafter.yml + config-name: release-drafter/entraid-config.yml + env: + GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }} diff --git a/package-lock.json b/package-lock.json index ba18a98b6a5..8fdd049a5b2 100644 --- a/package-lock.json +++ b/package-lock.json @@ -33,6 +33,29 @@ "node": ">=6.0.0" } }, + "node_modules/@azure/msal-common": { + "version": "14.16.0", + "resolved": "https://registry.npmjs.org/@azure/msal-common/-/msal-common-14.16.0.tgz", + "integrity": "sha512-1KOZj9IpcDSwpNiQNjt0jDYZpQvNZay7QAEi/5DLubay40iGYtLzya/jbjRPLyOTZhEKyL1MzPuw2HqBCjceYA==", + "license": "MIT", + "engines": { + "node": ">=0.8.0" + } + }, + "node_modules/@azure/msal-node": { + "version": "2.16.2", + "resolved": "https://registry.npmjs.org/@azure/msal-node/-/msal-node-2.16.2.tgz", + "integrity": "sha512-An7l1hEr0w1HMMh1LU+rtDtqL7/jw74ORlc9Wnh06v7TU/xpG39/Zdr1ZJu3QpjUfKJ+E0/OXMW8DRSWTlh7qQ==", + "license": "MIT", + "dependencies": { + "@azure/msal-common": "14.16.0", + "jsonwebtoken": "^9.0.0", + "uuid": "^8.3.0" + }, + "engines": { + "node": ">=16" + } + }, "node_modules/@babel/code-frame": { "version": "7.23.5", "dev": true, @@ -824,6 +847,10 @@ "node": ">=12" } }, + "node_modules/@redis/authx": { + "resolved": "packages/authx", + "link": true + }, "node_modules/@redis/bloom": { "resolved": "packages/bloom", "link": true @@ -832,6 +859,10 @@ "resolved": "packages/client", "link": true }, + "node_modules/@redis/entraid": { + "resolved": "packages/entraid", + "link": true + }, "node_modules/@redis/graph": { "resolved": "packages/graph", "link": true @@ -929,11 +960,82 @@ "dev": true, "license": "MIT" }, + "node_modules/@types/body-parser": { + "version": "1.19.5", + "resolved": "https://registry.npmjs.org/@types/body-parser/-/body-parser-1.19.5.tgz", + "integrity": "sha512-fB3Zu92ucau0iQ0JMCFQE7b/dv8Ot07NI3KaZIkIUNXq82k4eBAqUaneXfleGY9JWskeS9y+u0nXMyspcuQrCg==", + "dev": true, + "license": "MIT", + "dependencies": { + "@types/connect": "*", + "@types/node": "*" + } + }, + "node_modules/@types/connect": { + "version": "3.4.38", + "resolved": "https://registry.npmjs.org/@types/connect/-/connect-3.4.38.tgz", + "integrity": "sha512-K6uROf1LD88uDQqJCktA4yzL1YYAK6NgfsI0v/mTgyPKWsX1CnJ0XPSDhViejru1GcRkLWb8RlzFYJRqGUbaug==", + "dev": true, + "license": "MIT", + "dependencies": { + "@types/node": "*" + } + }, + "node_modules/@types/express": { + "version": "4.17.21", + "resolved": "https://registry.npmjs.org/@types/express/-/express-4.17.21.tgz", + "integrity": "sha512-ejlPM315qwLpaQlQDTjPdsUFSc6ZsP4AN6AlWnogPjQ7CVi7PYF3YVz+CY3jE2pwYf7E/7HlDAN0rV2GxTG0HQ==", + "dev": true, + "license": "MIT", + "dependencies": { + "@types/body-parser": "*", + "@types/express-serve-static-core": "^4.17.33", + "@types/qs": "*", + "@types/serve-static": "*" + } + }, + "node_modules/@types/express-serve-static-core": { + "version": "4.19.6", + "resolved": "https://registry.npmjs.org/@types/express-serve-static-core/-/express-serve-static-core-4.19.6.tgz", + "integrity": "sha512-N4LZ2xG7DatVqhCZzOGb1Yi5lMbXSZcmdLDe9EzSndPV2HpWYWzRbaerl2n27irrm94EPpprqa8KpskPT085+A==", + "dev": true, + "license": "MIT", + "dependencies": { + "@types/node": "*", + "@types/qs": "*", + "@types/range-parser": "*", + "@types/send": "*" + } + }, + "node_modules/@types/express-session": { + "version": "1.18.1", + "resolved": "https://registry.npmjs.org/@types/express-session/-/express-session-1.18.1.tgz", + "integrity": "sha512-S6TkD/lljxDlQ2u/4A70luD8/ZxZcrU5pQwI1rVXCiaVIywoFgbA+PIUNDjPhQpPdK0dGleLtYc/y7XWBfclBg==", + "dev": true, + "license": "MIT", + "dependencies": { + "@types/express": "*" + } + }, "node_modules/@types/http-cache-semantics": { "version": "4.0.4", "dev": true, "license": "MIT" }, + "node_modules/@types/http-errors": { + "version": "2.0.4", + "resolved": "https://registry.npmjs.org/@types/http-errors/-/http-errors-2.0.4.tgz", + "integrity": "sha512-D0CFMMtydbJAegzOyHjtiKPLlvnm3iTZyZRSZoLq2mRhDdmLfIWOCYPfQJ4cu2erKghU++QvjcUjp/5h7hESpA==", + "dev": true, + "license": "MIT" + }, + "node_modules/@types/mime": { + "version": "1.3.5", + "resolved": "https://registry.npmjs.org/@types/mime/-/mime-1.3.5.tgz", + "integrity": "sha512-/pyBZWSLD2n0dcHE3hq8s8ZvcETHtEuF+3E7XVt0Ig2nvsVQXdghHVcEkIWjy9A0wKfTn97a/PSDYohKIlnP/w==", + "dev": true, + "license": "MIT" + }, "node_modules/@types/mocha": { "version": "10.0.6", "dev": true, @@ -947,6 +1049,43 @@ "undici-types": "~5.26.4" } }, + "node_modules/@types/qs": { + "version": "6.9.17", + "resolved": "https://registry.npmjs.org/@types/qs/-/qs-6.9.17.tgz", + "integrity": "sha512-rX4/bPcfmvxHDv0XjfJELTTr+iB+tn032nPILqHm5wbthUUUuVtNGGqzhya9XUxjTP8Fpr0qYgSZZKxGY++svQ==", + "dev": true, + "license": "MIT" + }, + "node_modules/@types/range-parser": { + "version": "1.2.7", + "resolved": "https://registry.npmjs.org/@types/range-parser/-/range-parser-1.2.7.tgz", + "integrity": "sha512-hKormJbkJqzQGhziax5PItDUTMAM9uE2XXQmM37dyd4hVM+5aVl7oVxMVUiVQn2oCQFN/LKCZdvSM0pFRqbSmQ==", + "dev": true, + "license": "MIT" + }, + "node_modules/@types/send": { + "version": "0.17.4", + "resolved": "https://registry.npmjs.org/@types/send/-/send-0.17.4.tgz", + "integrity": "sha512-x2EM6TJOybec7c52BX0ZspPodMsQUd5L6PRwOunVyVUhXiBSKf3AezDL8Dgvgt5o0UfKNfuA0eMLr2wLT4AiBA==", + "dev": true, + "license": "MIT", + "dependencies": { + "@types/mime": "^1", + "@types/node": "*" + } + }, + "node_modules/@types/serve-static": { + "version": "1.15.7", + "resolved": "https://registry.npmjs.org/@types/serve-static/-/serve-static-1.15.7.tgz", + "integrity": "sha512-W8Ym+h8nhuRwaKPaDw34QUkwsGi6Rc4yYqvKFo5rm2FUEhCFbzVWrxXUxuKK8TASjWsysJY0nsmNCGhCOIsrOw==", + "dev": true, + "license": "MIT", + "dependencies": { + "@types/http-errors": "*", + "@types/node": "*", + "@types/send": "*" + } + }, "node_modules/@types/sinon": { "version": "17.0.3", "dev": true, @@ -973,6 +1112,20 @@ "dev": true, "license": "MIT" }, + "node_modules/accepts": { + "version": "1.3.8", + "resolved": "https://registry.npmjs.org/accepts/-/accepts-1.3.8.tgz", + "integrity": "sha512-PYAthTa2m2VKxuvSD3DPC/Gy+U+sOA1LAuT8mkmRuvw+NACSaeXEQ+NHcVF7rONl6qcaxV3Uuemwawk+7+SJLw==", + "dev": true, + "license": "MIT", + "dependencies": { + "mime-types": "~2.1.34", + "negotiator": "0.6.3" + }, + "engines": { + "node": ">= 0.6" + } + }, "node_modules/agent-base": { "version": "7.1.0", "dev": true, @@ -1112,6 +1265,13 @@ "url": "https://github.com/sponsors/ljharb" } }, + "node_modules/array-flatten": { + "version": "1.1.1", + "resolved": "https://registry.npmjs.org/array-flatten/-/array-flatten-1.1.1.tgz", + "integrity": "sha512-PCVAQswWemu6UdxsDFFX/+gVeYqKAod3D3UVm91jHwynguOwAvYPhx8nNlM++NqRcK6CxxpUafjmhIdKiHibqg==", + "dev": true, + "license": "MIT" + }, "node_modules/array-union": { "version": "1.0.2", "dev": true, @@ -1260,6 +1420,48 @@ "readable-stream": "^3.4.0" } }, + "node_modules/body-parser": { + "version": "1.20.3", + "resolved": "https://registry.npmjs.org/body-parser/-/body-parser-1.20.3.tgz", + "integrity": "sha512-7rAxByjUMqQ3/bHJy7D6OGXvx/MMc4IqBn/X0fcM1QUcAItpZrBEYhWGem+tzXH90c+G01ypMcYJBO9Y30203g==", + "dev": true, + "license": "MIT", + "dependencies": { + "bytes": "3.1.2", + "content-type": "~1.0.5", + "debug": "2.6.9", + "depd": "2.0.0", + "destroy": "1.2.0", + "http-errors": "2.0.0", + "iconv-lite": "0.4.24", + "on-finished": "2.4.1", + "qs": "6.13.0", + "raw-body": "2.5.2", + "type-is": "~1.6.18", + "unpipe": "1.0.0" + }, + "engines": { + "node": ">= 0.8", + "npm": "1.2.8000 || >= 1.4.16" + } + }, + "node_modules/body-parser/node_modules/debug": { + "version": "2.6.9", + "resolved": "https://registry.npmjs.org/debug/-/debug-2.6.9.tgz", + "integrity": "sha512-bC7ElrdJaJnPbAP+1EotYvqZsb3ecl5wi6Bfi6BJTUcNowp6cvspg0jXznRTKDjm/E7AdgFBVeAPVMNcKGsHMA==", + "dev": true, + "license": "MIT", + "dependencies": { + "ms": "2.0.0" + } + }, + "node_modules/body-parser/node_modules/ms": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/ms/-/ms-2.0.0.tgz", + "integrity": "sha512-Tpp60P6IUJDTuOq/5Z8cdskzJujfwqfOTkrwIwj7IRISpnkJnT6SyJ4PCPnGMoFjC9ddhal5KVIYtAt97ix05A==", + "dev": true, + "license": "MIT" + }, "node_modules/boxen": { "version": "7.1.1", "dev": true, @@ -1466,6 +1668,12 @@ "ieee754": "^1.1.13" } }, + "node_modules/buffer-equal-constant-time": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/buffer-equal-constant-time/-/buffer-equal-constant-time-1.0.1.tgz", + "integrity": "sha512-zRpUiDwd/xk6ADqPMATG8vc9VPrkck7T07OIx0gnjmJAnHnTVXNQG3vfvWNuiZIkwu9KrKdA1iJKfsfTVxE6NA==", + "license": "BSD-3-Clause" + }, "node_modules/bundle-name": { "version": "4.1.0", "dev": true, @@ -1480,6 +1688,16 @@ "url": "https://github.com/sponsors/sindresorhus" } }, + "node_modules/bytes": { + "version": "3.1.2", + "resolved": "https://registry.npmjs.org/bytes/-/bytes-3.1.2.tgz", + "integrity": "sha512-/Nf7TyzTx6S3yRJObOAV7956r8cr2+Oj8AC5dt8wSP3BQAoeX58NoHyCU8P8zGkNXStjTSi6fzO6F0pBdcYbEg==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">= 0.8" + } + }, "node_modules/cacheable-lookup": { "version": "7.0.0", "dev": true, @@ -1531,18 +1749,38 @@ } }, "node_modules/call-bind": { - "version": "1.0.5", + "version": "1.0.8", + "resolved": "https://registry.npmjs.org/call-bind/-/call-bind-1.0.8.tgz", + "integrity": "sha512-oKlSFMcMwpUg2ednkhQ454wfWiU/ul3CkJe/PEHcTKuiX6RpbehUiFMXu13HalGZxfUwCQzZG747YXBn1im9ww==", "dev": true, "license": "MIT", "dependencies": { - "function-bind": "^1.1.2", - "get-intrinsic": "^1.2.1", - "set-function-length": "^1.1.1" + "call-bind-apply-helpers": "^1.0.0", + "es-define-property": "^1.0.0", + "get-intrinsic": "^1.2.4", + "set-function-length": "^1.2.2" + }, + "engines": { + "node": ">= 0.4" }, "funding": { "url": "https://github.com/sponsors/ljharb" } }, + "node_modules/call-bind-apply-helpers": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/call-bind-apply-helpers/-/call-bind-apply-helpers-1.0.1.tgz", + "integrity": "sha512-BhYE+WDaywFg2TBWYNXAE+8B1ATnThNBqXHP5nQu0jWJdVvY2hvkpyB3qOmtmDePiS5/BDQ8wASEWGMWRG148g==", + "dev": true, + "license": "MIT", + "dependencies": { + "es-errors": "^1.3.0", + "function-bind": "^1.1.2" + }, + "engines": { + "node": ">= 0.4" + } + }, "node_modules/callsites": { "version": "3.1.0", "dev": true, @@ -1805,11 +2043,51 @@ "url": "https://github.com/yeoman/configstore?sponsor=1" } }, + "node_modules/content-disposition": { + "version": "0.5.4", + "resolved": "https://registry.npmjs.org/content-disposition/-/content-disposition-0.5.4.tgz", + "integrity": "sha512-FveZTNuGw04cxlAiWbzi6zTAL/lhehaWbTtgluJh4/E95DqMwTmha3KZN1aAWA8cFIhHzMZUvLevkw5Rqk+tSQ==", + "dev": true, + "license": "MIT", + "dependencies": { + "safe-buffer": "5.2.1" + }, + "engines": { + "node": ">= 0.6" + } + }, + "node_modules/content-type": { + "version": "1.0.5", + "resolved": "https://registry.npmjs.org/content-type/-/content-type-1.0.5.tgz", + "integrity": "sha512-nTjqfcBFEipKdXCv4YDQWCfmcLZKm81ldF0pAopTvyrFGVbcR6P/VAAd5G7N+0tTr8QqiU0tFadD6FK4NtJwOA==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">= 0.6" + } + }, "node_modules/convert-source-map": { "version": "1.9.0", "dev": true, "license": "MIT" }, + "node_modules/cookie": { + "version": "0.7.1", + "resolved": "https://registry.npmjs.org/cookie/-/cookie-0.7.1.tgz", + "integrity": "sha512-6DnInpx7SJ2AK3+CTUE/ZM0vWTUboZCegxhC2xiIydHR9jNuTAASBrfEpHhiGOZw/nX51bHt6YQl8jsGo4y/0w==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">= 0.6" + } + }, + "node_modules/cookie-signature": { + "version": "1.0.6", + "resolved": "https://registry.npmjs.org/cookie-signature/-/cookie-signature-1.0.6.tgz", + "integrity": "sha512-QADzlaHc8icV8I7vbaJXJwod9HWYp8uCqf1xa4OfNu1T7JVxQIrUgOWtHdNDtPiywmFbiS12VjotIXLrKM3orQ==", + "dev": true, + "license": "MIT" + }, "node_modules/cosmiconfig": { "version": "9.0.0", "dev": true, @@ -2003,16 +2281,21 @@ } }, "node_modules/define-data-property": { - "version": "1.1.1", + "version": "1.1.4", + "resolved": "https://registry.npmjs.org/define-data-property/-/define-data-property-1.1.4.tgz", + "integrity": "sha512-rBMvIzlpA8v6E+SJZoo++HAYqsLrkg7MSfIinMPFhmkorw7X+dOXVJQs+QT69zGkzMyfDnIMN2Wid1+NbL3T+A==", "dev": true, "license": "MIT", "dependencies": { - "get-intrinsic": "^1.2.1", - "gopd": "^1.0.1", - "has-property-descriptors": "^1.0.0" + "es-define-property": "^1.0.0", + "es-errors": "^1.3.0", + "gopd": "^1.0.1" }, "engines": { "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" } }, "node_modules/define-lazy-prop": { @@ -2055,11 +2338,32 @@ "node": ">= 14" } }, + "node_modules/depd": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/depd/-/depd-2.0.0.tgz", + "integrity": "sha512-g7nH6P6dyDioJogAAGprGpCtVImJhpPk/roCzdb3fIh61/s/nPsfR6onyMwkCAR/OlC3yBC0lESvUoQEAssIrw==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">= 0.8" + } + }, "node_modules/deprecation": { "version": "2.3.1", "dev": true, "license": "ISC" }, + "node_modules/destroy": { + "version": "1.2.0", + "resolved": "https://registry.npmjs.org/destroy/-/destroy-1.2.0.tgz", + "integrity": "sha512-2sJGJTaXIIaR1w4iJSNoN0hnMY7Gpc/n8D4qSCJw8QqFWXf7cuAgnEHxBpweaVcPevC2l3KpjYCx3NypQQgaJg==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">= 0.8", + "npm": "1.2.8000 || >= 1.4.16" + } + }, "node_modules/diff": { "version": "5.0.0", "dev": true, @@ -2082,11 +2386,55 @@ "url": "https://github.com/sponsors/sindresorhus" } }, + "node_modules/dotenv": { + "version": "16.4.7", + "resolved": "https://registry.npmjs.org/dotenv/-/dotenv-16.4.7.tgz", + "integrity": "sha512-47qPchRCykZC03FhkYAhrvwU4xDBFIj1QPqaarj6mdM/hgUzfPHcpkHJOn3mJAufFeeAxAzeGsr5X0M4k6fLZQ==", + "dev": true, + "license": "BSD-2-Clause", + "engines": { + "node": ">=12" + }, + "funding": { + "url": "https://dotenvx.com" + } + }, + "node_modules/dunder-proto": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/dunder-proto/-/dunder-proto-1.0.0.tgz", + "integrity": "sha512-9+Sj30DIu+4KvHqMfLUGLFYL2PkURSYMVXJyXe92nFRvlYq5hBjLEhblKB+vkd/WVlUYMWigiY07T91Fkk0+4A==", + "dev": true, + "license": "MIT", + "dependencies": { + "call-bind-apply-helpers": "^1.0.0", + "es-errors": "^1.3.0", + "gopd": "^1.2.0" + }, + "engines": { + "node": ">= 0.4" + } + }, "node_modules/eastasianwidth": { "version": "0.2.0", "dev": true, "license": "MIT" }, + "node_modules/ecdsa-sig-formatter": { + "version": "1.0.11", + "resolved": "https://registry.npmjs.org/ecdsa-sig-formatter/-/ecdsa-sig-formatter-1.0.11.tgz", + "integrity": "sha512-nagl3RYrbNv6kQkeJIpt6NJZy8twLB/2vtz6yN9Z4vRKHN4/QZJIEbqohALSgwKdnksuY3k5Addp5lg8sVoVcQ==", + "license": "Apache-2.0", + "dependencies": { + "safe-buffer": "^5.0.1" + } + }, + "node_modules/ee-first": { + "version": "1.1.1", + "resolved": "https://registry.npmjs.org/ee-first/-/ee-first-1.1.1.tgz", + "integrity": "sha512-WMwm9LhRUo+WUaRN+vRuETqG89IgZphVSNkdFgeb6sS/E4OrDIN7t48CAewSHXc6C8lefD8KKfr5vY61brQlow==", + "dev": true, + "license": "MIT" + }, "node_modules/electron-to-chromium": { "version": "1.4.656", "dev": true, @@ -2102,6 +2450,16 @@ "dev": true, "license": "MIT" }, + "node_modules/encodeurl": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/encodeurl/-/encodeurl-2.0.0.tgz", + "integrity": "sha512-Q0n9HRi4m6JuGIV1eFlmvJB7ZEVxu93IrMyiMsGC0lrMJMWzRgx6WGquyfQgZVb31vhGgXnfmPNNXmxnOkRBrg==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">= 0.8" + } + }, "node_modules/env-paths": { "version": "2.2.1", "dev": true, @@ -2175,6 +2533,16 @@ "dev": true, "license": "MIT" }, + "node_modules/es-define-property": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/es-define-property/-/es-define-property-1.0.1.tgz", + "integrity": "sha512-e3nRfgfUZ4rNGL232gUgX06QNyyez04KdjFrF+LTRoOXmrOgFKDg4BCdsjW8EnT69eqdYGmRpJwiPVYNrCaW3g==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">= 0.4" + } + }, "node_modules/es-errors": { "version": "1.3.0", "dev": true, @@ -2292,6 +2660,13 @@ "url": "https://github.com/sponsors/sindresorhus" } }, + "node_modules/escape-html": { + "version": "1.0.3", + "resolved": "https://registry.npmjs.org/escape-html/-/escape-html-1.0.3.tgz", + "integrity": "sha512-NiSupZ4OeuGwr68lGIeym/ksIZMJodUGOSCZ/FSnTxcrekbvqrgdUxlJOMpijaKZVjAJrWrGs/6Jy8OMuyj9ow==", + "dev": true, + "license": "MIT" + }, "node_modules/escape-string-regexp": { "version": "4.0.0", "dev": true, @@ -2351,6 +2726,16 @@ "node": ">=0.10.0" } }, + "node_modules/etag": { + "version": "1.8.1", + "resolved": "https://registry.npmjs.org/etag/-/etag-1.8.1.tgz", + "integrity": "sha512-aIL5Fx7mawVa300al2BnEE4iNvo1qETxLrPI/o05L7z6go7fCw1J6EQmbK4FmJ2AS7kgVF/KEZWufBfdClMcPg==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">= 0.6" + } + }, "node_modules/execa": { "version": "8.0.1", "dev": true, @@ -2395,6 +2780,131 @@ "url": "https://github.com/sponsors/isaacs" } }, + "node_modules/express": { + "version": "4.21.2", + "resolved": "https://registry.npmjs.org/express/-/express-4.21.2.tgz", + "integrity": "sha512-28HqgMZAmih1Czt9ny7qr6ek2qddF4FclbMzwhCREB6OFfH+rXAnuNCwo1/wFvrtbgsQDb4kSbX9de9lFbrXnA==", + "dev": true, + "license": "MIT", + "dependencies": { + "accepts": "~1.3.8", + "array-flatten": "1.1.1", + "body-parser": "1.20.3", + "content-disposition": "0.5.4", + "content-type": "~1.0.4", + "cookie": "0.7.1", + "cookie-signature": "1.0.6", + "debug": "2.6.9", + "depd": "2.0.0", + "encodeurl": "~2.0.0", + "escape-html": "~1.0.3", + "etag": "~1.8.1", + "finalhandler": "1.3.1", + "fresh": "0.5.2", + "http-errors": "2.0.0", + "merge-descriptors": "1.0.3", + "methods": "~1.1.2", + "on-finished": "2.4.1", + "parseurl": "~1.3.3", + "path-to-regexp": "0.1.12", + "proxy-addr": "~2.0.7", + "qs": "6.13.0", + "range-parser": "~1.2.1", + "safe-buffer": "5.2.1", + "send": "0.19.0", + "serve-static": "1.16.2", + "setprototypeof": "1.2.0", + "statuses": "2.0.1", + "type-is": "~1.6.18", + "utils-merge": "1.0.1", + "vary": "~1.1.2" + }, + "engines": { + "node": ">= 0.10.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/express" + } + }, + "node_modules/express-session": { + "version": "1.18.1", + "resolved": "https://registry.npmjs.org/express-session/-/express-session-1.18.1.tgz", + "integrity": "sha512-a5mtTqEaZvBCL9A9aqkrtfz+3SMDhOVUnjafjo+s7A9Txkq+SVX2DLvSp1Zrv4uCXa3lMSK3viWnh9Gg07PBUA==", + "dev": true, + "license": "MIT", + "dependencies": { + "cookie": "0.7.2", + "cookie-signature": "1.0.7", + "debug": "2.6.9", + "depd": "~2.0.0", + "on-headers": "~1.0.2", + "parseurl": "~1.3.3", + "safe-buffer": "5.2.1", + "uid-safe": "~2.1.5" + }, + "engines": { + "node": ">= 0.8.0" + } + }, + "node_modules/express-session/node_modules/cookie": { + "version": "0.7.2", + "resolved": "https://registry.npmjs.org/cookie/-/cookie-0.7.2.tgz", + "integrity": "sha512-yki5XnKuf750l50uGTllt6kKILY4nQ1eNIQatoXEByZ5dWgnKqbnqmTrBE5B4N7lrMJKQ2ytWMiTO2o0v6Ew/w==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">= 0.6" + } + }, + "node_modules/express-session/node_modules/cookie-signature": { + "version": "1.0.7", + "resolved": "https://registry.npmjs.org/cookie-signature/-/cookie-signature-1.0.7.tgz", + "integrity": "sha512-NXdYc3dLr47pBkpUCHtKSwIOQXLVn8dZEuywboCOJY/osA0wFSLlSawr3KN8qXJEyX66FcONTH8EIlVuK0yyFA==", + "dev": true, + "license": "MIT" + }, + "node_modules/express-session/node_modules/debug": { + "version": "2.6.9", + "resolved": "https://registry.npmjs.org/debug/-/debug-2.6.9.tgz", + "integrity": "sha512-bC7ElrdJaJnPbAP+1EotYvqZsb3ecl5wi6Bfi6BJTUcNowp6cvspg0jXznRTKDjm/E7AdgFBVeAPVMNcKGsHMA==", + "dev": true, + "license": "MIT", + "dependencies": { + "ms": "2.0.0" + } + }, + "node_modules/express-session/node_modules/ms": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/ms/-/ms-2.0.0.tgz", + "integrity": "sha512-Tpp60P6IUJDTuOq/5Z8cdskzJujfwqfOTkrwIwj7IRISpnkJnT6SyJ4PCPnGMoFjC9ddhal5KVIYtAt97ix05A==", + "dev": true, + "license": "MIT" + }, + "node_modules/express/node_modules/debug": { + "version": "2.6.9", + "resolved": "https://registry.npmjs.org/debug/-/debug-2.6.9.tgz", + "integrity": "sha512-bC7ElrdJaJnPbAP+1EotYvqZsb3ecl5wi6Bfi6BJTUcNowp6cvspg0jXznRTKDjm/E7AdgFBVeAPVMNcKGsHMA==", + "dev": true, + "license": "MIT", + "dependencies": { + "ms": "2.0.0" + } + }, + "node_modules/express/node_modules/ms": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/ms/-/ms-2.0.0.tgz", + "integrity": "sha512-Tpp60P6IUJDTuOq/5Z8cdskzJujfwqfOTkrwIwj7IRISpnkJnT6SyJ4PCPnGMoFjC9ddhal5KVIYtAt97ix05A==", + "dev": true, + "license": "MIT" + }, + "node_modules/express/node_modules/path-to-regexp": { + "version": "0.1.12", + "resolved": "https://registry.npmjs.org/path-to-regexp/-/path-to-regexp-0.1.12.tgz", + "integrity": "sha512-RA1GjUVMnvYFxuqovrEqZoxxW5NUZqbwKtYz/Tt7nXerk0LbLblQmrsgdeOxV5SFHf0UDggjS/bSeOZwt1pmEQ==", + "dev": true, + "license": "MIT" + }, "node_modules/external-editor": { "version": "3.1.0", "dev": true, @@ -2525,6 +3035,42 @@ "node": ">=8" } }, + "node_modules/finalhandler": { + "version": "1.3.1", + "resolved": "https://registry.npmjs.org/finalhandler/-/finalhandler-1.3.1.tgz", + "integrity": "sha512-6BN9trH7bp3qvnrRyzsBz+g3lZxTNZTbVO2EV1CS0WIcDbawYVdYvGflME/9QP0h0pYlCDBCTjYa9nZzMDpyxQ==", + "dev": true, + "license": "MIT", + "dependencies": { + "debug": "2.6.9", + "encodeurl": "~2.0.0", + "escape-html": "~1.0.3", + "on-finished": "2.4.1", + "parseurl": "~1.3.3", + "statuses": "2.0.1", + "unpipe": "~1.0.0" + }, + "engines": { + "node": ">= 0.8" + } + }, + "node_modules/finalhandler/node_modules/debug": { + "version": "2.6.9", + "resolved": "https://registry.npmjs.org/debug/-/debug-2.6.9.tgz", + "integrity": "sha512-bC7ElrdJaJnPbAP+1EotYvqZsb3ecl5wi6Bfi6BJTUcNowp6cvspg0jXznRTKDjm/E7AdgFBVeAPVMNcKGsHMA==", + "dev": true, + "license": "MIT", + "dependencies": { + "ms": "2.0.0" + } + }, + "node_modules/finalhandler/node_modules/ms": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/ms/-/ms-2.0.0.tgz", + "integrity": "sha512-Tpp60P6IUJDTuOq/5Z8cdskzJujfwqfOTkrwIwj7IRISpnkJnT6SyJ4PCPnGMoFjC9ddhal5KVIYtAt97ix05A==", + "dev": true, + "license": "MIT" + }, "node_modules/find-cache-dir": { "version": "3.3.2", "dev": true, @@ -2603,6 +3149,26 @@ "node": ">=12.20.0" } }, + "node_modules/forwarded": { + "version": "0.2.0", + "resolved": "https://registry.npmjs.org/forwarded/-/forwarded-0.2.0.tgz", + "integrity": "sha512-buRG0fpBtRHSTCOASe6hD258tEubFoRLb4ZNA6NxMVHNw2gOcwHo9wyablzMzOA5z9xA9L1KNjk/Nt6MT9aYow==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">= 0.6" + } + }, + "node_modules/fresh": { + "version": "0.5.2", + "resolved": "https://registry.npmjs.org/fresh/-/fresh-0.5.2.tgz", + "integrity": "sha512-zJ2mQYM18rEFOudeV4GShTGIQ7RbzA7ozbU9I/XBpm7kqgMywgmylMwXHxZJmkVoYkna9d2pVXVXPdYTP9ej8Q==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">= 0.6" + } + }, "node_modules/fromentries": { "version": "1.3.2", "dev": true, @@ -2701,15 +3267,20 @@ } }, "node_modules/get-intrinsic": { - "version": "1.2.3", + "version": "1.2.5", + "resolved": "https://registry.npmjs.org/get-intrinsic/-/get-intrinsic-1.2.5.tgz", + "integrity": "sha512-Y4+pKa7XeRUPWFNvOOYHkRYrfzW07oraURSvjDmRVOJ748OrVmeXtpE4+GCEHncjCjkTxPNRt8kEbxDhsn6VTg==", "dev": true, "license": "MIT", "dependencies": { - "es-errors": "^1.0.0", + "call-bind-apply-helpers": "^1.0.0", + "dunder-proto": "^1.0.0", + "es-define-property": "^1.0.1", + "es-errors": "^1.3.0", "function-bind": "^1.1.2", - "has-proto": "^1.0.1", - "has-symbols": "^1.0.3", - "hasown": "^2.0.0" + "gopd": "^1.2.0", + "has-symbols": "^1.1.0", + "hasown": "^2.0.2" }, "engines": { "node": ">= 0.4" @@ -2934,11 +3505,13 @@ } }, "node_modules/gopd": { - "version": "1.0.1", + "version": "1.2.0", + "resolved": "https://registry.npmjs.org/gopd/-/gopd-1.2.0.tgz", + "integrity": "sha512-ZUKRh6/kUFoAiTAtTYPZJ3hw9wNxx+BIBOijnlG9PnrJsCcSjs1wyyD6vJpaYtgnzDrKYRSqf3OO6Rfa93xsRg==", "dev": true, "license": "MIT", - "dependencies": { - "get-intrinsic": "^1.1.3" + "engines": { + "node": ">= 0.4" }, "funding": { "url": "https://github.com/sponsors/ljharb" @@ -3001,11 +3574,13 @@ } }, "node_modules/has-property-descriptors": { - "version": "1.0.1", + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/has-property-descriptors/-/has-property-descriptors-1.0.2.tgz", + "integrity": "sha512-55JNKuIW+vq4Ke1BjOTjM2YctQIvCT7GFzHwmfZPGo5wnrgkid0YQtnAleFSqumZm4az3n2BS+erby5ipJdgrg==", "dev": true, "license": "MIT", "dependencies": { - "get-intrinsic": "^1.2.2" + "es-define-property": "^1.0.0" }, "funding": { "url": "https://github.com/sponsors/ljharb" @@ -3023,7 +3598,9 @@ } }, "node_modules/has-symbols": { - "version": "1.0.3", + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/has-symbols/-/has-symbols-1.1.0.tgz", + "integrity": "sha512-1cDNdwJ2Jaohmb3sg4OmKaMBwuC48sYni5HUw2DvsC8LjGTLK9h+eb1X6RyuOHe4hT0ULCW68iomhjUoKUqlPQ==", "dev": true, "license": "MIT", "engines": { @@ -3063,7 +3640,9 @@ } }, "node_modules/hasown": { - "version": "2.0.0", + "version": "2.0.2", + "resolved": "https://registry.npmjs.org/hasown/-/hasown-2.0.2.tgz", + "integrity": "sha512-0hJU9SCPvmMzIBdZFqNPXWa6dqh7WdH0cII9y+CyS8rG3nL48Bclra9HmKhVVUHyPWNH5Y7xDwAB7bfgSjkUMQ==", "dev": true, "license": "MIT", "dependencies": { @@ -3091,6 +3670,23 @@ "dev": true, "license": "BSD-2-Clause" }, + "node_modules/http-errors": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/http-errors/-/http-errors-2.0.0.tgz", + "integrity": "sha512-FtwrG/euBzaEjYeRqOgly7G0qviiXoJWnvEH2Z1plBdXgbyjv34pHTSb9zoeHMyDy33+DWy5Wt9Wo+TURtOYSQ==", + "dev": true, + "license": "MIT", + "dependencies": { + "depd": "2.0.0", + "inherits": "2.0.4", + "setprototypeof": "1.2.0", + "statuses": "2.0.1", + "toidentifier": "1.0.1" + }, + "engines": { + "node": ">= 0.8" + } + }, "node_modules/http-proxy-agent": { "version": "7.0.0", "dev": true, @@ -3360,6 +3956,16 @@ "dev": true, "license": "MIT" }, + "node_modules/ipaddr.js": { + "version": "1.9.1", + "resolved": "https://registry.npmjs.org/ipaddr.js/-/ipaddr.js-1.9.1.tgz", + "integrity": "sha512-0KI/607xoxSToH7GjN1FfSbLoU0+btTicjsQSWQlh/hZykN8KpmMf7uYwPW3R+akZ6R/w18ZlXSHBYXiYUPO3g==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">= 0.10" + } + }, "node_modules/is-arguments": { "version": "1.1.1", "dev": true, @@ -4032,26 +4638,81 @@ "node": ">=6" } }, - "node_modules/jsonc-parser": { - "version": "3.2.1", + "node_modules/jsonc-parser": { + "version": "3.2.1", + "dev": true, + "license": "MIT" + }, + "node_modules/jsonfile": { + "version": "6.1.0", + "dev": true, + "license": "MIT", + "dependencies": { + "universalify": "^2.0.0" + }, + "optionalDependencies": { + "graceful-fs": "^4.1.6" + } + }, + "node_modules/jsonwebtoken": { + "version": "9.0.2", + "resolved": "https://registry.npmjs.org/jsonwebtoken/-/jsonwebtoken-9.0.2.tgz", + "integrity": "sha512-PRp66vJ865SSqOlgqS8hujT5U4AOgMfhrwYIuIhfKaoSCZcirrmASQr8CX7cUg+RMih+hgznrjp99o+W4pJLHQ==", + "license": "MIT", + "dependencies": { + "jws": "^3.2.2", + "lodash.includes": "^4.3.0", + "lodash.isboolean": "^3.0.3", + "lodash.isinteger": "^4.0.4", + "lodash.isnumber": "^3.0.3", + "lodash.isplainobject": "^4.0.6", + "lodash.isstring": "^4.0.1", + "lodash.once": "^4.0.0", + "ms": "^2.1.1", + "semver": "^7.5.4" + }, + "engines": { + "node": ">=12", + "npm": ">=6" + } + }, + "node_modules/jsonwebtoken/node_modules/semver": { + "version": "7.6.3", + "resolved": "https://registry.npmjs.org/semver/-/semver-7.6.3.tgz", + "integrity": "sha512-oVekP1cKtI+CTDvHWYFUcMtsK/00wmAEfyqKfNdARm8u1wNVhSgaX7A8d4UuIlUI5e84iEwOhs7ZPYRmzU9U6A==", + "license": "ISC", + "bin": { + "semver": "bin/semver.js" + }, + "engines": { + "node": ">=10" + } + }, + "node_modules/just-extend": { + "version": "6.2.0", "dev": true, "license": "MIT" }, - "node_modules/jsonfile": { - "version": "6.1.0", - "dev": true, + "node_modules/jwa": { + "version": "1.4.1", + "resolved": "https://registry.npmjs.org/jwa/-/jwa-1.4.1.tgz", + "integrity": "sha512-qiLX/xhEEFKUAJ6FiBMbes3w9ATzyk5W7Hvzpa/SLYdxNtng+gcurvrI7TbACjIXlsJyr05/S1oUhZrc63evQA==", "license": "MIT", "dependencies": { - "universalify": "^2.0.0" - }, - "optionalDependencies": { - "graceful-fs": "^4.1.6" + "buffer-equal-constant-time": "1.0.1", + "ecdsa-sig-formatter": "1.0.11", + "safe-buffer": "^5.0.1" } }, - "node_modules/just-extend": { - "version": "6.2.0", - "dev": true, - "license": "MIT" + "node_modules/jws": { + "version": "3.2.2", + "resolved": "https://registry.npmjs.org/jws/-/jws-3.2.2.tgz", + "integrity": "sha512-YHlZCB6lMTllWDtSPHz/ZXTsi8S00usEV6v1tjq8tOUZzw7DpSDWVXjXDre6ed1w/pd495ODpHZYSdkRTsa0HA==", + "license": "MIT", + "dependencies": { + "jwa": "^1.4.1", + "safe-buffer": "^5.0.1" + } }, "node_modules/keyv": { "version": "4.5.4", @@ -4119,14 +4780,42 @@ "dev": true, "license": "MIT" }, + "node_modules/lodash.includes": { + "version": "4.3.0", + "resolved": "https://registry.npmjs.org/lodash.includes/-/lodash.includes-4.3.0.tgz", + "integrity": "sha512-W3Bx6mdkRTGtlJISOvVD/lbqjTlPPUDTMnlXZFnVwi9NKJ6tiAk6LVdlhZMm17VZisqhKcgzpO5Wz91PCt5b0w==", + "license": "MIT" + }, + "node_modules/lodash.isboolean": { + "version": "3.0.3", + "resolved": "https://registry.npmjs.org/lodash.isboolean/-/lodash.isboolean-3.0.3.tgz", + "integrity": "sha512-Bz5mupy2SVbPHURB98VAcw+aHh4vRV5IPNhILUCsOzRmsTmSQ17jIuqopAentWoehktxGd9e/hbIXq980/1QJg==", + "license": "MIT" + }, + "node_modules/lodash.isinteger": { + "version": "4.0.4", + "resolved": "https://registry.npmjs.org/lodash.isinteger/-/lodash.isinteger-4.0.4.tgz", + "integrity": "sha512-DBwtEWN2caHQ9/imiNeEA5ys1JoRtRfY3d7V9wkqtbycnAmTvRRmbHKDV4a0EYc678/dia0jrte4tjYwVBaZUA==", + "license": "MIT" + }, + "node_modules/lodash.isnumber": { + "version": "3.0.3", + "resolved": "https://registry.npmjs.org/lodash.isnumber/-/lodash.isnumber-3.0.3.tgz", + "integrity": "sha512-QYqzpfwO3/CWf3XP+Z+tkQsfaLL/EnUlXWVkIk5FUPc4sBdTehEqZONuyRt2P67PXAk+NXmTBcc97zw9t1FQrw==", + "license": "MIT" + }, "node_modules/lodash.isplainobject": { "version": "4.0.6", - "dev": true, "license": "MIT" }, "node_modules/lodash.isstring": { "version": "4.0.1", - "dev": true, + "license": "MIT" + }, + "node_modules/lodash.once": { + "version": "4.1.1", + "resolved": "https://registry.npmjs.org/lodash.once/-/lodash.once-4.1.1.tgz", + "integrity": "sha512-Sb487aTOCr9drQVL8pIxOzVhafOjZN9UU54hiN8PU3uAiSV7lx1yYNpbNmex2PK6dSJoNTSJUUswT651yww3Mg==", "license": "MIT" }, "node_modules/lodash.uniqby": { @@ -4209,6 +4898,26 @@ "node": ">= 12" } }, + "node_modules/media-typer": { + "version": "0.3.0", + "resolved": "https://registry.npmjs.org/media-typer/-/media-typer-0.3.0.tgz", + "integrity": "sha512-dq+qelQ9akHpcOl/gUVRTxVIOkAJ1wR3QAvb4RsVjS8oVoFjDGTc679wJYmUmknUF5HwMLOgb5O+a3KxfWapPQ==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">= 0.6" + } + }, + "node_modules/merge-descriptors": { + "version": "1.0.3", + "resolved": "https://registry.npmjs.org/merge-descriptors/-/merge-descriptors-1.0.3.tgz", + "integrity": "sha512-gaNvAS7TZ897/rVaZ0nMtAyxNyi/pdbjbAwUpFQpN70GqnVfOiXpeUUMKRBmzXaSQ8DdTX4/0ms62r2K+hE6mQ==", + "dev": true, + "license": "MIT", + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, "node_modules/merge-stream": { "version": "2.0.0", "dev": true, @@ -4222,6 +4931,16 @@ "node": ">= 8" } }, + "node_modules/methods": { + "version": "1.1.2", + "resolved": "https://registry.npmjs.org/methods/-/methods-1.1.2.tgz", + "integrity": "sha512-iclAHeNqNm68zFtnZ0e+1L2yUIdvzNoauKU4WBA3VvH/vPFieF7qfRlwUZU+DA9P9bPXIS90ulxoUoCH23sV2w==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">= 0.6" + } + }, "node_modules/micromatch": { "version": "4.0.5", "dev": true, @@ -4234,6 +4953,19 @@ "node": ">=8.6" } }, + "node_modules/mime": { + "version": "1.6.0", + "resolved": "https://registry.npmjs.org/mime/-/mime-1.6.0.tgz", + "integrity": "sha512-x0Vn8spI+wuJ1O6S7gnbaQg8Pxh4NNHb7KSINmEWKiPE4RKOplvijn+NkmYmmRgP68mc70j2EbeTFRsrswaQeg==", + "dev": true, + "license": "MIT", + "bin": { + "mime": "cli.js" + }, + "engines": { + "node": ">=4" + } + }, "node_modules/mime-db": { "version": "1.52.0", "dev": true, @@ -4384,7 +5116,6 @@ }, "node_modules/ms": { "version": "2.1.3", - "dev": true, "license": "MIT" }, "node_modules/mute-stream": { @@ -4406,6 +5137,16 @@ "node": "^10 || ^12 || ^13.7 || ^14 || >=15.0.1" } }, + "node_modules/negotiator": { + "version": "0.6.3", + "resolved": "https://registry.npmjs.org/negotiator/-/negotiator-0.6.3.tgz", + "integrity": "sha512-+EUsqGPLsM+j/zdChZjsnX51g4XrHFOIXwfnCVPGlQk/k5giakcKsuxCObBRu6DSm9opw/O6slWbJdghQM4bBg==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">= 0.6" + } + }, "node_modules/netmask": { "version": "2.0.2", "dev": true, @@ -4723,6 +5464,29 @@ "url": "https://github.com/sponsors/ljharb" } }, + "node_modules/on-finished": { + "version": "2.4.1", + "resolved": "https://registry.npmjs.org/on-finished/-/on-finished-2.4.1.tgz", + "integrity": "sha512-oVlzkg3ENAhCk2zdv7IJwd/QUD4z2RxRwpkcGY8psCVcCYZNq4wYnVWALHM+brtuJjePWiYF/ClmuDr8Ch5+kg==", + "dev": true, + "license": "MIT", + "dependencies": { + "ee-first": "1.1.1" + }, + "engines": { + "node": ">= 0.8" + } + }, + "node_modules/on-headers": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/on-headers/-/on-headers-1.0.2.tgz", + "integrity": "sha512-pZAE+FJLoyITytdqK0U5s+FIpjN0JP3OzFi/u8Rx+EV5/W+JTWGXG8xFzevE7AjBfDqHv/8vL8qQsIhHnqRkrA==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">= 0.8" + } + }, "node_modules/once": { "version": "1.4.0", "dev": true, @@ -5178,6 +5942,16 @@ "parse-path": "^7.0.0" } }, + "node_modules/parseurl": { + "version": "1.3.3", + "resolved": "https://registry.npmjs.org/parseurl/-/parseurl-1.3.3.tgz", + "integrity": "sha512-CiyeOxFT/JZyN5m0z9PfXw4SCBJ6Sygz1Dpl0wqjlhDEGGBP1GnsUVEL0p63hoG1fcj3fHynXi9NYO4nWOL+qQ==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">= 0.8" + } + }, "node_modules/path-exists": { "version": "4.0.0", "dev": true, @@ -5365,6 +6139,20 @@ "dev": true, "license": "MIT" }, + "node_modules/proxy-addr": { + "version": "2.0.7", + "resolved": "https://registry.npmjs.org/proxy-addr/-/proxy-addr-2.0.7.tgz", + "integrity": "sha512-llQsMLSUDUPT44jdrU/O37qlnifitDP+ZwrmmZcoSKyLKvtZxpyV0n2/bD/N4tBAAZ/gJEdZU7KMraoK1+XYAg==", + "dev": true, + "license": "MIT", + "dependencies": { + "forwarded": "0.2.0", + "ipaddr.js": "1.9.1" + }, + "engines": { + "node": ">= 0.10" + } + }, "node_modules/proxy-agent": { "version": "6.3.1", "dev": true, @@ -5410,6 +6198,22 @@ "url": "https://github.com/sponsors/sindresorhus" } }, + "node_modules/qs": { + "version": "6.13.0", + "resolved": "https://registry.npmjs.org/qs/-/qs-6.13.0.tgz", + "integrity": "sha512-+38qI9SOr8tfZ4QmJNplMUxqjbe7LKvvZgWdExBOmd+egZTtjLB67Gu0HRX3u/XOq7UU2Nx6nsjvS16Z9uwfpg==", + "dev": true, + "license": "BSD-3-Clause", + "dependencies": { + "side-channel": "^1.0.6" + }, + "engines": { + "node": ">=0.6" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, "node_modules/queue-microtask": { "version": "1.2.3", "dev": true, @@ -5440,6 +6244,16 @@ "url": "https://github.com/sponsors/sindresorhus" } }, + "node_modules/random-bytes": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/random-bytes/-/random-bytes-1.0.0.tgz", + "integrity": "sha512-iv7LhNVO047HzYR3InF6pUcUsPQiHTM1Qal51DcGSuZFBil1aBBWG5eHPNek7bvILMaYJ/8RU1e8w1AMdHmLQQ==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">= 0.8" + } + }, "node_modules/randombytes": { "version": "2.1.0", "dev": true, @@ -5448,6 +6262,32 @@ "safe-buffer": "^5.1.0" } }, + "node_modules/range-parser": { + "version": "1.2.1", + "resolved": "https://registry.npmjs.org/range-parser/-/range-parser-1.2.1.tgz", + "integrity": "sha512-Hrgsx+orqoygnmhFbKaHE6c296J+HTAQXoxEF6gNupROmmGJRoyzfG3ccAveqCBrwr/2yxQ5BVd/GTl5agOwSg==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">= 0.6" + } + }, + "node_modules/raw-body": { + "version": "2.5.2", + "resolved": "https://registry.npmjs.org/raw-body/-/raw-body-2.5.2.tgz", + "integrity": "sha512-8zGqypfENjCIqGhgXToC8aB2r7YrBX+AQAfIPs/Mlk+BtPTztOvTS01NRW/3Eh60J+a48lt8qsCzirQ6loCVfA==", + "dev": true, + "license": "MIT", + "dependencies": { + "bytes": "3.1.2", + "http-errors": "2.0.0", + "iconv-lite": "0.4.24", + "unpipe": "1.0.0" + }, + "engines": { + "node": ">= 0.8" + } + }, "node_modules/rc": { "version": "1.2.8", "dev": true, @@ -5880,7 +6720,6 @@ }, "node_modules/safe-buffer": { "version": "5.2.1", - "dev": true, "funding": [ { "type": "github", @@ -5970,6 +6809,58 @@ "dev": true, "license": "ISC" }, + "node_modules/send": { + "version": "0.19.0", + "resolved": "https://registry.npmjs.org/send/-/send-0.19.0.tgz", + "integrity": "sha512-dW41u5VfLXu8SJh5bwRmyYUbAoSB3c9uQh6L8h/KtsFREPWpbX1lrljJo186Jc4nmci/sGUZ9a0a0J2zgfq2hw==", + "dev": true, + "license": "MIT", + "dependencies": { + "debug": "2.6.9", + "depd": "2.0.0", + "destroy": "1.2.0", + "encodeurl": "~1.0.2", + "escape-html": "~1.0.3", + "etag": "~1.8.1", + "fresh": "0.5.2", + "http-errors": "2.0.0", + "mime": "1.6.0", + "ms": "2.1.3", + "on-finished": "2.4.1", + "range-parser": "~1.2.1", + "statuses": "2.0.1" + }, + "engines": { + "node": ">= 0.8.0" + } + }, + "node_modules/send/node_modules/debug": { + "version": "2.6.9", + "resolved": "https://registry.npmjs.org/debug/-/debug-2.6.9.tgz", + "integrity": "sha512-bC7ElrdJaJnPbAP+1EotYvqZsb3ecl5wi6Bfi6BJTUcNowp6cvspg0jXznRTKDjm/E7AdgFBVeAPVMNcKGsHMA==", + "dev": true, + "license": "MIT", + "dependencies": { + "ms": "2.0.0" + } + }, + "node_modules/send/node_modules/debug/node_modules/ms": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/ms/-/ms-2.0.0.tgz", + "integrity": "sha512-Tpp60P6IUJDTuOq/5Z8cdskzJujfwqfOTkrwIwj7IRISpnkJnT6SyJ4PCPnGMoFjC9ddhal5KVIYtAt97ix05A==", + "dev": true, + "license": "MIT" + }, + "node_modules/send/node_modules/encodeurl": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/encodeurl/-/encodeurl-1.0.2.tgz", + "integrity": "sha512-TPJXq8JqFaVYm2CWmPvnP2Iyo4ZSM7/QKcSmuMLDObfpH5fi7RUGmd/rTDf+rut/saiDiQEeVTNgAmJEdAOx0w==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">= 0.8" + } + }, "node_modules/serialize-javascript": { "version": "6.0.0", "dev": true, @@ -5978,21 +6869,40 @@ "randombytes": "^2.1.0" } }, + "node_modules/serve-static": { + "version": "1.16.2", + "resolved": "https://registry.npmjs.org/serve-static/-/serve-static-1.16.2.tgz", + "integrity": "sha512-VqpjJZKadQB/PEbEwvFdO43Ax5dFBZ2UECszz8bQ7pi7wt//PWe1P6MN7eCnjsatYtBT6EuiClbjSWP2WrIoTw==", + "dev": true, + "license": "MIT", + "dependencies": { + "encodeurl": "~2.0.0", + "escape-html": "~1.0.3", + "parseurl": "~1.3.3", + "send": "0.19.0" + }, + "engines": { + "node": ">= 0.8.0" + } + }, "node_modules/set-blocking": { "version": "2.0.0", "dev": true, "license": "ISC" }, "node_modules/set-function-length": { - "version": "1.2.0", + "version": "1.2.2", + "resolved": "https://registry.npmjs.org/set-function-length/-/set-function-length-1.2.2.tgz", + "integrity": "sha512-pgRc4hJ4/sNjWCSS9AmnS40x3bNMDTknHgL5UaMBTMyJnU90EgWh1Rz+MC9eFu4BuN/UwZjKQuY/1v3rM7HMfg==", "dev": true, "license": "MIT", "dependencies": { - "define-data-property": "^1.1.1", + "define-data-property": "^1.1.4", + "es-errors": "^1.3.0", "function-bind": "^1.1.2", - "get-intrinsic": "^1.2.2", + "get-intrinsic": "^1.2.4", "gopd": "^1.0.1", - "has-property-descriptors": "^1.0.1" + "has-property-descriptors": "^1.0.2" }, "engines": { "node": ">= 0.4" @@ -6011,6 +6921,13 @@ "node": ">= 0.4" } }, + "node_modules/setprototypeof": { + "version": "1.2.0", + "resolved": "https://registry.npmjs.org/setprototypeof/-/setprototypeof-1.2.0.tgz", + "integrity": "sha512-E5LDX7Wrp85Kil5bhZv46j8jOeboKq5JMmYM3gVGdGH8xFpPWXUMsNrlODCrkoxMEeNi/XZIwuRvY4XNwYMJpw==", + "dev": true, + "license": "ISC" + }, "node_modules/shebang-command": { "version": "2.0.0", "dev": true, @@ -6058,13 +6975,19 @@ } }, "node_modules/side-channel": { - "version": "1.0.4", + "version": "1.0.6", + "resolved": "https://registry.npmjs.org/side-channel/-/side-channel-1.0.6.tgz", + "integrity": "sha512-fDW/EZ6Q9RiO8eFG8Hj+7u/oW+XrPTIChwCOM2+th2A6OblDtYYIpve9m+KvI9Z4C9qSEXlaGR6bTEYHReuglA==", "dev": true, "license": "MIT", "dependencies": { - "call-bind": "^1.0.0", - "get-intrinsic": "^1.0.2", - "object-inspect": "^1.9.0" + "call-bind": "^1.0.7", + "es-errors": "^1.3.0", + "get-intrinsic": "^1.2.4", + "object-inspect": "^1.13.1" + }, + "engines": { + "node": ">= 0.4" }, "funding": { "url": "https://github.com/sponsors/ljharb" @@ -6191,6 +7114,16 @@ "dev": true, "license": "BSD-3-Clause" }, + "node_modules/statuses": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/statuses/-/statuses-2.0.1.tgz", + "integrity": "sha512-RwNA9Z/7PrK06rYLIzFMlaF+l73iwpzsqRIFgbMLbTcLD6cOao82TaWefPXQvB2fOC4AjuYSEndS7N/mTCbkdQ==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">= 0.8" + } + }, "node_modules/stdin-discarder": { "version": "0.2.2", "dev": true, @@ -6404,6 +7337,16 @@ "node": ">=8.0" } }, + "node_modules/toidentifier": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/toidentifier/-/toidentifier-1.0.1.tgz", + "integrity": "sha512-o5sSPKEkg/DIQNmH43V0/uerLrpzVedkUh8tGNvaeXpfpuwjKenlSox/2O/BTlZUtEe+JG7s5YhEz608PlAHRA==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=0.6" + } + }, "node_modules/trim-repeated": { "version": "1.0.0", "dev": true, @@ -6462,6 +7405,20 @@ "node": ">=8" } }, + "node_modules/type-is": { + "version": "1.6.18", + "resolved": "https://registry.npmjs.org/type-is/-/type-is-1.6.18.tgz", + "integrity": "sha512-TkRKr9sUTxEH8MdfuCSP7VizJyzRNMjj2J2do2Jr3Kym598JVdEksuzPQCnlFPW4ky9Q+iA+ma9BGm06XQBy8g==", + "dev": true, + "license": "MIT", + "dependencies": { + "media-typer": "0.3.0", + "mime-types": "~2.1.24" + }, + "engines": { + "node": ">= 0.6" + } + }, "node_modules/typed-array-buffer": { "version": "1.0.0", "dev": true, @@ -6585,6 +7542,19 @@ "node": ">=14.17" } }, + "node_modules/uid-safe": { + "version": "2.1.5", + "resolved": "https://registry.npmjs.org/uid-safe/-/uid-safe-2.1.5.tgz", + "integrity": "sha512-KPHm4VL5dDXKz01UuEd88Df+KzynaohSL9fBh096KWAxSKZQDI2uBrVqtvRM4rwrIrRRKsdLNML/lnaaVSRioA==", + "dev": true, + "license": "MIT", + "dependencies": { + "random-bytes": "~1.0.0" + }, + "engines": { + "node": ">= 0.8" + } + }, "node_modules/unbox-primitive": { "version": "1.0.2", "dev": true, @@ -6642,6 +7612,16 @@ "node": ">= 10.0.0" } }, + "node_modules/unpipe": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/unpipe/-/unpipe-1.0.0.tgz", + "integrity": "sha512-pjy2bYhSsufwWlKwPc+l3cN7+wuJlK6uz0YdJEOlQDbl6jo/YlPi4mb8agUkVC8BF7V8NuzeyPNqRksA3hztKQ==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">= 0.8" + } + }, "node_modules/update-browserslist-db": { "version": "1.0.13", "dev": true, @@ -6750,14 +7730,33 @@ "dev": true, "license": "MIT" }, + "node_modules/utils-merge": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/utils-merge/-/utils-merge-1.0.1.tgz", + "integrity": "sha512-pMZTvIkT1d+TFGvDOqodOclx0QWkkgi6Tdoa8gC8ffGAAqz9pzPTZWAybbsHHoED/ztMtkv/VoYTYyShUn81hA==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">= 0.4.0" + } + }, "node_modules/uuid": { "version": "8.3.2", - "dev": true, "license": "MIT", "bin": { "uuid": "dist/bin/uuid" } }, + "node_modules/vary": { + "version": "1.1.2", + "resolved": "https://registry.npmjs.org/vary/-/vary-1.1.2.tgz", + "integrity": "sha512-BNGbWLfd0eUPabhkXUVm0j8uuvREyTh5ovRa/dyow/BqAbZJyC+5fU+IzQOzmAKzYqYRAISoRhdQr3eIZ/PXqg==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">= 0.8" + } + }, "node_modules/vscode-oniguruma": { "version": "1.7.0", "dev": true, @@ -7127,6 +8126,21 @@ "url": "https://github.com/sponsors/sindresorhus" } }, + "packages/authx": { + "name": "@redis/authx", + "version": "5.0.0-next.5", + "license": "MIT", + "dependencies": { + "@azure/msal-node": "^2.16.1" + }, + "devDependencies": {}, + "engines": { + "node": ">= 18" + }, + "peerDependencies": { + "@redis/client": "^5.0.0-next.5" + } + }, "packages/bloom": { "name": "@redis/bloom", "version": "5.0.0-next.5", @@ -7155,8 +8169,52 @@ }, "engines": { "node": ">= 18" + }, + "peerDependencies": { + "@redis/authx": "^5.0.0-next.5" + } + }, + "packages/entraid": { + "name": "@redis/entraid", + "version": "5.0.0-next.5", + "license": "MIT", + "dependencies": { + "@azure/msal-node": "^2.16.1" + }, + "devDependencies": { + "@redis/test-utils": "*", + "@types/express": "^4.17.21", + "@types/express-session": "^1.18.0", + "@types/node": "^22.9.0", + "dotenv": "^16.3.1", + "express": "^4.21.1", + "express-session": "^1.18.1" + }, + "engines": { + "node": ">= 18" + }, + "peerDependencies": { + "@redis/authx": "^5.0.0-next.5", + "@redis/client": "^5.0.0-next.5" } }, + "packages/entraid/node_modules/@types/node": { + "version": "22.10.2", + "resolved": "https://registry.npmjs.org/@types/node/-/node-22.10.2.tgz", + "integrity": "sha512-Xxr6BBRCAOQixvonOye19wnzyDiUtTeqldOOmj3CkeblonbccA12PFwlufvRdrpjXxqnmUaeiU5EOA+7s5diUQ==", + "dev": true, + "license": "MIT", + "dependencies": { + "undici-types": "~6.20.0" + } + }, + "packages/entraid/node_modules/undici-types": { + "version": "6.20.0", + "resolved": "https://registry.npmjs.org/undici-types/-/undici-types-6.20.0.tgz", + "integrity": "sha512-Ny6QZ2Nju20vw1SRHe3d9jVu6gJ+4e3+MMpqu7pqE5HT6WsTSlce++GQmK5UXS8mzV8DSYHrQH+Xrf2jVcuKNg==", + "dev": true, + "license": "MIT" + }, "packages/graph": { "name": "@redis/graph", "version": "5.0.0-next.5", diff --git a/packages/client/lib/authx/credentials-provider.ts b/packages/client/lib/authx/credentials-provider.ts new file mode 100644 index 00000000000..667795be9b3 --- /dev/null +++ b/packages/client/lib/authx/credentials-provider.ts @@ -0,0 +1,102 @@ +import { Disposable } from './disposable'; +/** + * Provides credentials asynchronously. + */ +export interface AsyncCredentialsProvider { + readonly type: 'async-credentials-provider'; + credentials: () => Promise +} + +/** + * Provides credentials asynchronously with support for continuous updates via a subscription model. + * This is useful for environments where credentials are frequently rotated or updated or can be revoked. + */ +export interface StreamingCredentialsProvider { + readonly type: 'streaming-credentials-provider'; + + /** + * Provides initial credentials and subscribes to subsequent updates. This is used internally by the node-redis client + * to handle credential rotation and re-authentication. + * + * Note: The node-redis client manages the subscription lifecycle automatically. Users only need to implement + * onReAuthenticationError if they want to be notified about authentication failures. + * + * Error handling: + * - Errors received via onError indicate a fatal issue with the credentials stream + * - The stream is automatically closed(disposed) when onError occurs + * - onError typically mean the provider failed to fetch new credentials after retrying + * + * @example + * ```ts + * const provider = getStreamingProvider(); + * const [initialCredentials, disposable] = await provider.subscribe({ + * onNext: (newCredentials) => { + * // Handle credential update + * }, + * onError: (error) => { + * // Handle fatal stream error + * } + * }); + * + * @param listener - Callbacks to handle credential updates and errors + * @returns A Promise resolving to [initial credentials, cleanup function] + */ + subscribe: (listener: StreamingCredentialsListener) => Promise<[BasicAuth, Disposable]> + + /** + * Called when authentication fails or credentials cannot be renewed in time. + * Implement this to handle authentication errors in your application. + * + * @param error - Either a CredentialsError (invalid/expired credentials) or + * UnableToObtainNewCredentialsError (failed to fetch new credentials on time) + */ + onReAuthenticationError: (error: ReAuthenticationError) => void; + +} + +/** + * Type representing basic authentication credentials. + */ +export type BasicAuth = { username?: string, password?: string } + +/** + * Callback to handle credential updates and errors. + */ +export type StreamingCredentialsListener = { + onNext: (credentials: T) => void; + onError: (e: Error) => void; +} + + +/** + * Providers that can supply authentication credentials + */ +export type CredentialsProvider = AsyncCredentialsProvider | StreamingCredentialsProvider + +/** + * Errors that can occur during re-authentication. + */ +export type ReAuthenticationError = CredentialsError | UnableToObtainNewCredentialsError + +/** + * Thrown when re-authentication fails with provided credentials . + * e.g. when the credentials are invalid, expired or revoked. + * + */ +export class CredentialsError extends Error { + constructor(message: string) { + super(`Re-authentication with latest credentials failed: ${message}`); + this.name = 'CredentialsError'; + } + +} + +/** + * Thrown when new credentials cannot be obtained before current ones expire + */ +export class UnableToObtainNewCredentialsError extends Error { + constructor(message: string) { + super(`Unable to obtain new credentials : ${message}`); + this.name = 'UnableToObtainNewCredentialsError'; + } +} \ No newline at end of file diff --git a/packages/client/lib/authx/disposable.ts b/packages/client/lib/authx/disposable.ts new file mode 100644 index 00000000000..ee4526a37bd --- /dev/null +++ b/packages/client/lib/authx/disposable.ts @@ -0,0 +1,6 @@ +/** + * Represents a resource that can be disposed. + */ +export interface Disposable { + dispose(): void; +} \ No newline at end of file diff --git a/packages/client/lib/authx/identity-provider.ts b/packages/client/lib/authx/identity-provider.ts new file mode 100644 index 00000000000..a2d25c8f9db --- /dev/null +++ b/packages/client/lib/authx/identity-provider.ts @@ -0,0 +1,22 @@ +/** + * An identity provider is responsible for providing a token that can be used to authenticate with a service. + */ + +/** + * The response from an identity provider when requesting a token. + * + * note: "native" refers to the type of the token that the actual identity provider library is using. + * + * @type T The type of the native idp token. + * @property token The token. + * @property ttlMs The time-to-live of the token in epoch milliseconds extracted from the native token in local time. + */ +export type TokenResponse = { token: T, ttlMs: number }; + +export interface IdentityProvider { + /** + * Request a token from the identity provider. + * @returns A promise that resolves to an object containing the token and the time-to-live in epoch milliseconds. + */ + requestToken(): Promise>; +} \ No newline at end of file diff --git a/packages/client/lib/authx/index.ts b/packages/client/lib/authx/index.ts new file mode 100644 index 00000000000..ce611e1497f --- /dev/null +++ b/packages/client/lib/authx/index.ts @@ -0,0 +1,15 @@ +export { TokenManager, TokenManagerConfig, TokenStreamListener, RetryPolicy, IDPError } from './token-manager'; +export { + CredentialsProvider, + StreamingCredentialsProvider, + UnableToObtainNewCredentialsError, + CredentialsError, + StreamingCredentialsListener, + AsyncCredentialsProvider, + ReAuthenticationError, + BasicAuth +} from './credentials-provider'; +export { Token } from './token'; +export { IdentityProvider, TokenResponse } from './identity-provider'; + +export { Disposable } from './disposable' \ No newline at end of file diff --git a/packages/client/lib/authx/token-manager.spec.ts b/packages/client/lib/authx/token-manager.spec.ts new file mode 100644 index 00000000000..1cc2a207edc --- /dev/null +++ b/packages/client/lib/authx/token-manager.spec.ts @@ -0,0 +1,588 @@ +import { strict as assert } from 'node:assert'; +import { Token } from './token'; +import { IDPError, RetryPolicy, TokenManager, TokenManagerConfig, TokenStreamListener } from './token-manager'; +import { IdentityProvider, TokenResponse } from './identity-provider'; +import { setTimeout } from 'timers/promises'; + +describe('TokenManager', () => { + + /** + * Helper function to delay execution for a given number of milliseconds. + * @param ms + */ + const delay = (ms: number) => { + return setTimeout(ms); + } + + /** + * IdentityProvider that returns a fixed test token for testing and doesn't handle TTL. + */ + class TestIdentityProvider implements IdentityProvider { + requestToken(): Promise> { + return Promise.resolve({ token: 'test-token 1', ttlMs: 1000 }); + } + } + + /** + * Helper function to create a test token with a given TTL . + * @param ttlMs Time-to-live in milliseconds + */ + const createToken = (ttlMs: number): Token => { + return new Token('test-token', ttlMs, 0); + }; + + /** + * Listener that records received tokens and errors for testing. + */ + class TestListener implements TokenStreamListener { + + public readonly receivedTokens: Token[] = []; + public readonly errors: IDPError[] = []; + + onNext(token: Token): void { + this.receivedTokens.push(token); + } + + onError(error: IDPError): void { + this.errors.push(error); + } + } + + /** + * IdentityProvider that returns a sequence of tokens with a fixed delay simulating network latency. + * Used for testing token refresh scenarios. + */ + class ControlledIdentityProvider implements IdentityProvider { + private tokenIndex = 0; + private readonly delayMs: number; + private readonly ttlMs: number; + + constructor( + private readonly tokens: string[], + delayMs: number = 0, + tokenTTlMs: number = 100 + ) { + this.delayMs = delayMs; + this.ttlMs = tokenTTlMs; + } + + async requestToken(): Promise> { + + if (this.tokenIndex >= this.tokens.length) { + throw new Error('No more test tokens available'); + } + + if (this.delayMs > 0) { + await setTimeout(this.delayMs); + } + + return { token: this.tokens[this.tokenIndex++], ttlMs: this.ttlMs }; + } + + } + + /** + * IdentityProvider that simulates various error scenarios with configurable behavior + */ + class ErrorSimulatingProvider implements IdentityProvider { + private requestCount = 0; + + constructor( + private readonly errorSequence: Array, + private readonly delayMs: number = 0, + private readonly ttlMs: number = 100 + ) {} + + async requestToken(): Promise> { + + if (this.delayMs > 0) { + await delay(this.delayMs); + } + + const result = this.errorSequence[this.requestCount]; + this.requestCount++; + + if (result instanceof Error) { + throw result; + } else if (typeof result === 'string') { + return { token: result, ttlMs: this.ttlMs }; + } else { + throw new Error('No more responses configured'); + } + } + + getRequestCount(): number { + return this.requestCount; + } + } + + describe('constructor validation', () => { + it('should throw error if ratio is greater than 1', () => { + const config: TokenManagerConfig = { + expirationRefreshRatio: 1.1 + }; + + assert.throws( + () => new TokenManager(new TestIdentityProvider(), config), + /expirationRefreshRatio must be less than or equal to 1/ + ); + }); + + it('should throw error if ratio is negative', () => { + const config: TokenManagerConfig = { + expirationRefreshRatio: -0.1 + }; + + assert.throws( + () => new TokenManager(new TestIdentityProvider(), config), + /expirationRefreshRatio must be greater or equal to 0/ + ); + }); + + it('should accept ratio of 1', () => { + const config: TokenManagerConfig = { + expirationRefreshRatio: 1 + }; + + assert.doesNotThrow( + () => new TokenManager(new TestIdentityProvider(), config) + ); + }); + + it('should accept ratio of 0', () => { + const config: TokenManagerConfig = { + expirationRefreshRatio: 0 + }; + + assert.doesNotThrow( + () => new TokenManager(new TestIdentityProvider(), config) + ); + }); + }); + + describe('calculateRefreshTime', () => { + it('should calculate correct refresh time with 0.8 ratio', () => { + const config: TokenManagerConfig = { + expirationRefreshRatio: 0.8 + }; + + const manager = new TokenManager(new TestIdentityProvider(), config); + const token = createToken(1000); + const refreshTime = manager.calculateRefreshTime(token, 0); + + // With 1000s TTL and 0.8 ratio, should refresh at 800s + assert.equal(refreshTime, 800); + }); + + it('should return 0 for ratio of 0', () => { + const config: TokenManagerConfig = { + expirationRefreshRatio: 0 + }; + + const manager = new TokenManager(new TestIdentityProvider(), config); + const token = createToken(1000); + const refreshTime = manager.calculateRefreshTime(token, 0); + + assert.equal(refreshTime, 0); + }); + + it('should refresh at expiration time with ratio of 1', () => { + const config: TokenManagerConfig = { + expirationRefreshRatio: 1 + }; + + const manager = new TokenManager(new TestIdentityProvider(), config); + const token = createToken(1000); + const refreshTime = manager.calculateRefreshTime(token, 0); + + assert.equal(refreshTime, 1000); + }); + + it('should handle short TTL tokens', () => { + const config: TokenManagerConfig = { + expirationRefreshRatio: 0.8 + }; + + const manager = new TokenManager(new TestIdentityProvider(), config); + const token = createToken(5); + const refreshTime = manager.calculateRefreshTime(token, 0); + + assert.equal(refreshTime, 4); + }); + + it('should handle expired tokens', () => { + const config: TokenManagerConfig = { + expirationRefreshRatio: 0.8 + }; + + const manager = new TokenManager(new TestIdentityProvider(), config); + // Create token that expired 100s ago + const token = createToken(-100); + const refreshTime = manager.calculateRefreshTime(token, 0); + + // Should return refresh time of 0 for expired tokens + assert.equal(refreshTime, 0); + }); + describe('token refresh scenarios', () => { + + describe('token refresh', () => { + it('should handle token refresh', async () => { + const networkDelay = 20; + const tokenTtl = 100; + + const config: TokenManagerConfig = { + expirationRefreshRatio: 0.8 + }; + + const identityProvider = new ControlledIdentityProvider(['token1', 'token2', 'token3'], networkDelay, tokenTtl); + const manager = new TokenManager(identityProvider, config); + const listener = new TestListener(); + const disposable = manager.start(listener); + + assert.equal(manager.getCurrentToken(), null, 'Should not have token yet'); + // Wait for the first token request to complete ( it should be immediate, and we should wait only for the network delay) + await delay(networkDelay) + + assert.equal(listener.receivedTokens.length, 1, 'Should receive initial token'); + assert.equal(listener.receivedTokens[0].value, 'token1', 'Should have correct token value'); + assert.equal(listener.receivedTokens[0].expiresAtMs - listener.receivedTokens[0].receivedAtMs, + tokenTtl, 'Should have correct TTL'); + assert.equal(listener.errors.length, 0, 'Should not have any errors: ' + listener.errors); + assert.equal(manager.getCurrentToken().value, 'token1', 'Should have current token'); + + await delay(80); + + assert.equal(listener.receivedTokens.length, 1, 'Should not receive new token yet'); + assert.equal(listener.errors.length, 0, 'Should not have any errors'); + + await delay(networkDelay); + + assert.equal(listener.receivedTokens.length, 2, 'Should receive second token'); + assert.equal(listener.receivedTokens[1].value, 'token2', 'Should have correct token value'); + assert.equal(listener.receivedTokens[1].expiresAtMs - listener.receivedTokens[1].receivedAtMs, + tokenTtl, 'Should have correct TTL'); + assert.equal(listener.errors.length, 0, 'Should not have any errors'); + assert.equal(manager.getCurrentToken().value, 'token2', 'Should have current token'); + + await delay(80); + + assert.equal(listener.receivedTokens.length, 2, 'Should not receive new token yet'); + assert.equal(listener.errors.length, 0, 'Should not have any errors'); + + await delay(networkDelay); + + assert.equal(listener.receivedTokens.length, 3, 'Should receive third token'); + assert.equal(listener.receivedTokens[2].value, 'token3', 'Should have correct token value'); + assert.equal(listener.receivedTokens[2].expiresAtMs - listener.receivedTokens[2].receivedAtMs, + tokenTtl, 'Should have correct TTL'); + assert.equal(listener.errors.length, 0, 'Should not have any errors'); + assert.equal(manager.getCurrentToken().value, 'token3', 'Should have current token'); + + disposable?.dispose(); + }); + }); + }); + }); + + describe('TokenManager error handling', () => { + + describe('error scenarios', () => { + it('should not recover if retries are not enabled', async () => { + + const networkDelay = 20; + const tokenTtl = 100; + + const config: TokenManagerConfig = { + expirationRefreshRatio: 0.8 + }; + + const identityProvider = new ErrorSimulatingProvider( + [ + 'token1', + new Error('Fatal error'), + 'token3' + ], + networkDelay, + tokenTtl + ); + + const manager = new TokenManager(identityProvider, config); + const listener = new TestListener(); + const disposable = manager.start(listener); + + await delay(networkDelay); + + assert.equal(listener.receivedTokens.length, 1, 'Should receive initial token'); + assert.equal(listener.receivedTokens[0].value, 'token1', 'Should have correct initial token'); + assert.equal(listener.receivedTokens[0].expiresAtMs - listener.receivedTokens[0].receivedAtMs, + tokenTtl, 'Should have correct TTL'); + assert.equal(listener.errors.length, 0, 'Should not have errors yet'); + + await delay(80); + + assert.equal(listener.receivedTokens.length, 1, 'Should not receive new token yet'); + assert.equal(listener.errors.length, 0, 'Should not have any errors'); + + await delay(networkDelay); + + assert.equal(listener.receivedTokens.length, 1, 'Should not receive new token after failure'); + assert.equal(listener.errors.length, 1, 'Should receive error'); + assert.equal(listener.errors[0].message, 'Fatal error', 'Should have correct error message'); + assert.equal(listener.errors[0].isRetryable, false, 'Should be a fatal error'); + + // verify that the token manager is stopped and no more requests are made after the error and expected refresh time + await delay(80); + + assert.equal(identityProvider.getRequestCount(), 2, 'Should not make more requests after error'); + assert.equal(listener.receivedTokens.length, 1, 'Should not receive new token after error'); + assert.equal(listener.errors.length, 1, 'Should not receive more errors after error'); + assert.equal(manager.isRunning(), false, 'Should stop token manager after error'); + + disposable?.dispose(); + }); + + it('should handle retries with exponential backoff', async () => { + const networkDelay = 20; + const tokenTtl = 100; + + const config: TokenManagerConfig = { + expirationRefreshRatio: 0.8, + retry: { + maxAttempts: 3, + initialDelayMs: 100, + maxDelayMs: 1000, + backoffMultiplier: 2, + isRetryable: (error: unknown) => error instanceof Error && error.message === 'Temporary failure' + } + }; + + const identityProvider = new ErrorSimulatingProvider( + [ + 'initial-token', + new Error('Temporary failure'), // First attempt fails + new Error('Temporary failure'), // First retry fails + 'recovery-token' // Second retry succeeds + ], + networkDelay, + tokenTtl + ); + + const manager = new TokenManager(identityProvider, config); + const listener = new TestListener(); + const disposable = manager.start(listener); + + // Wait for initial token + await delay(networkDelay); + assert.equal(listener.receivedTokens.length, 1, 'Should receive initial token'); + assert.equal(listener.receivedTokens[0].value, 'initial-token', 'Should have correct initial token'); + assert.equal(listener.receivedTokens[0].expiresAtMs - listener.receivedTokens[0].receivedAtMs, + tokenTtl, 'Should have correct TTL'); + assert.equal(listener.errors.length, 0, 'Should not have errors yet'); + + await delay(80); + + assert.equal(listener.receivedTokens.length, 1, 'Should not receive new token yet'); + assert.equal(listener.errors.length, 0, 'Should not have any errors'); + + await delay(networkDelay); + + // Should have first error but not stop due to retry config + assert.equal(listener.errors.length, 1, 'Should have first error'); + assert.ok(listener.errors[0].message.includes('attempt 1'), 'Error should indicate first attempt'); + assert.equal(listener.errors[0].isRetryable, true, 'Should not be a fatal error'); + assert.equal(manager.isRunning(), true, 'Should continue running during retries'); + + // Advance past first retry (delay: 100ms due to backoff) + await delay(100); + + assert.equal(listener.errors.length, 1, 'Should not have the second error yet'); + + await delay(networkDelay); + + assert.equal(listener.errors.length, 2, 'Should have second error'); + assert.ok(listener.errors[1].message.includes('attempt 2'), 'Error should indicate second attempt'); + assert.equal(listener.errors[0].isRetryable, true, 'Should not be a fatal error'); + assert.equal(manager.isRunning(), true, 'Should continue running during retries'); + + // Advance past second retry (delay: 200ms due to backoff) + await delay(200); + + assert.equal(listener.errors.length, 2, 'Should not have another error'); + assert.equal(listener.receivedTokens.length, 1, 'Should not receive new token yet'); + + await delay(networkDelay); + + // Should have recovered + assert.equal(listener.receivedTokens.length, 2, 'Should receive recovery token'); + assert.equal(listener.receivedTokens[1].value, 'recovery-token', 'Should have correct recovery token'); + assert.equal(listener.receivedTokens[1].expiresAtMs - listener.receivedTokens[1].receivedAtMs, + tokenTtl, 'Should have correct TTL'); + assert.equal(manager.isRunning(), true, 'Should continue running after recovery'); + assert.equal(identityProvider.getRequestCount(), 4, 'Should have made exactly 4 requests'); + + disposable?.dispose(); + }); + + it('should stop after max retries exceeded', async () => { + const networkDelay = 20; + const tokenTtl = 100; + + const config: TokenManagerConfig = { + expirationRefreshRatio: 0.8, + retry: { + maxAttempts: 2, // Only allow 2 retries + initialDelayMs: 100, + maxDelayMs: 1000, + backoffMultiplier: 2, + jitterPercentage: 0, + isRetryable: (error: unknown) => error instanceof Error && error.message === 'Temporary failure' + } + }; + + // All attempts must fail + const identityProvider = new ErrorSimulatingProvider( + [ + 'initial-token', + new Error('Temporary failure'), + new Error('Temporary failure'), + new Error('Temporary failure') + ], + networkDelay, + tokenTtl + ); + + const manager = new TokenManager(identityProvider, config); + const listener = new TestListener(); + const disposable = manager.start(listener); + + // Wait for initial token + await delay(networkDelay); + assert.equal(listener.receivedTokens.length, 1, 'Should receive initial token'); + + await delay(80); + + assert.equal(listener.receivedTokens.length, 1, 'Should not receive new token yet'); + assert.equal(listener.errors.length, 0, 'Should not have any errors'); + + //wait for the "network call" to complete + await delay(networkDelay); + + // First error + assert.equal(listener.errors.length, 1, 'Should have first error'); + assert.equal(manager.isRunning(), true, 'Should continue running after first error'); + assert.equal(listener.errors[0].isRetryable, true, 'Should not be a fatal error'); + + // Advance past first retry + await delay(100); + + assert.equal(listener.errors.length, 1, 'Should not have second error yet'); + + //wait for the "network call" to complete + await delay(networkDelay); + + // Second error + assert.equal(listener.errors.length, 2, 'Should have second error'); + assert.equal(manager.isRunning(), true, 'Should continue running after second error'); + assert.equal(listener.errors[1].isRetryable, true, 'Should not be a fatal error'); + + // Advance past second retry + await delay(200); + + assert.equal(listener.errors.length, 2, 'Should not have third error yet'); + + //wait for the "network call" to complete + await delay(networkDelay); + + // Should stop after max retries + assert.equal(listener.errors.length, 3, 'Should have final error'); + assert.equal(listener.errors[2].isRetryable, false, 'Should be a fatal error'); + assert.equal(manager.isRunning(), false, 'Should stop after max retries exceeded'); + assert.equal(identityProvider.getRequestCount(), 4, 'Should have made exactly 4 requests'); + + disposable?.dispose(); + + }); + }); + }); + + describe('TokenManager retry delay calculations', () => { + const createManager = (retryConfig: Partial) => { + const config: TokenManagerConfig = { + expirationRefreshRatio: 0.8, + retry: { + maxAttempts: 3, + initialDelayMs: 100, + maxDelayMs: 1000, + backoffMultiplier: 2, + ...retryConfig + } + }; + return new TokenManager(new TestIdentityProvider(), config); + }; + + describe('calculateRetryDelay', () => { + + it('should apply exponential backoff', () => { + const manager = createManager({ + initialDelayMs: 100, + backoffMultiplier: 2, + jitterPercentage: 0 + }); + + // Test multiple retry attempts + const expectedDelays = [ + [1, 100], // First attempt: initialDelay * (2^0) = 100 + [2, 200], // Second attempt: initialDelay * (2^1) = 200 + [3, 400], // Third attempt: initialDelay * (2^2) = 400 + [4, 800], // Fourth attempt: initialDelay * (2^3) = 800 + [5, 1000] // Fifth attempt: would be 1600, but capped at maxDelay (1000) + ]; + + for (const [attempt, expectedDelay] of expectedDelays) { + manager['retryAttempt'] = attempt; + assert.equal( + manager.calculateRetryDelay(), + expectedDelay, + `Incorrect delay for attempt ${attempt}` + ); + } + }); + + it('should respect maxDelayMs', () => { + const manager = createManager({ + initialDelayMs: 100, + maxDelayMs: 300, + backoffMultiplier: 2, + jitterPercentage: 0 + }); + + // Test that delays are capped at maxDelayMs + const expectedDelays = [ + [1, 100], // First attempt: 100 + [2, 200], // Second attempt: 200 + [3, 300], // Third attempt: would be 400, capped at 300 + [4, 300], // Fourth attempt: would be 800, capped at 300 + [5, 300] // Fifth attempt: would be 1600, capped at 300 + ]; + + for (const [attempt, expectedDelay] of expectedDelays) { + manager['retryAttempt'] = attempt; + assert.equal( + manager.calculateRetryDelay(), + expectedDelay, + `Incorrect delay for attempt ${attempt}` + ); + } + }); + + it('should return 0 when no retry config is present', () => { + const manager = new TokenManager(new TestIdentityProvider(), { + expirationRefreshRatio: 0.8 + }); + manager['retryAttempt'] = 1; + assert.equal(manager.calculateRetryDelay(), 0); + }); + }); + }); +}); + diff --git a/packages/client/lib/authx/token-manager.ts b/packages/client/lib/authx/token-manager.ts new file mode 100644 index 00000000000..6532d88317b --- /dev/null +++ b/packages/client/lib/authx/token-manager.ts @@ -0,0 +1,318 @@ +import { IdentityProvider, TokenResponse } from './identity-provider'; +import { Token } from './token'; +import {Disposable} from './disposable'; + +/** + * The configuration for retrying token refreshes. + */ +export interface RetryPolicy { + /** + * The maximum number of attempts to retry token refreshes. + */ + maxAttempts: number; + + /** + * The initial delay in milliseconds before the first retry. + */ + initialDelayMs: number; + + /** + * The maximum delay in milliseconds between retries. + * The calculated delay will be capped at this value. + */ + maxDelayMs: number; + + /** + * The multiplier for exponential backoff between retries. + * @example + * A value of 2 will double the delay each time: + * - 1st retry: initialDelayMs + * - 2nd retry: initialDelayMs * 2 + * - 3rd retry: initialDelayMs * 4 + */ + backoffMultiplier: number; + + /** + * The percentage of jitter to apply to the delay. + * @example + * A value of 0.1 will add or subtract up to 10% of the delay. + */ + jitterPercentage?: number; + + /** + * Function to classify errors from the identity provider as retryable or non-retryable. + * Used to determine if a token refresh failure should be retried based on the type of error. + * + * The default behavior is to retry all types of errors if no function is provided. + * + * Common use cases: + * - Network errors that may be transient (should retry) + * - Invalid credentials (should not retry) + * - Rate limiting responses (should retry) + * + * @param error - The error from the identity provider3 + * @param attempt - Current retry attempt (0-based) + * @returns `true` if the error is considered transient and the operation should be retried + * + * @example + * ```typescript + * const retryPolicy: RetryPolicy = { + * maxAttempts: 3, + * initialDelayMs: 1000, + * maxDelayMs: 5000, + * backoffMultiplier: 2, + * isRetryable: (error) => { + * // Retry on network errors or rate limiting + * return error instanceof NetworkError || + * error instanceof RateLimitError; + * } + * }; + * ``` + */ + isRetryable?: (error: unknown, attempt: number) => boolean; +} + +/** + * the configuration for the TokenManager. + */ +export interface TokenManagerConfig { + + /** + * Represents the ratio of a token's lifetime at which a refresh should be triggered. + * For example, a value of 0.75 means the token should be refreshed when 75% of its lifetime has elapsed (or when + * 25% of its lifetime remains). + */ + expirationRefreshRatio: number; + + // The retry policy for token refreshes. If not provided, no retries will be attempted. + retry?: RetryPolicy; +} + +/** + * IDPError indicates a failure from the identity provider. + * + * The `isRetryable` flag is determined by the RetryPolicy's error classification function - if an error is + * classified as retryable, it will be marked as transient and the token manager will attempt to recover. + */ +export class IDPError extends Error { + constructor(public readonly message: string, public readonly isRetryable: boolean) { + super(message); + this.name = 'IDPError'; + } +} + +/** + * TokenStreamListener is an interface for objects that listen to token changes. + */ +export type TokenStreamListener = { + /** + * Called each time a new token is received. + * @param token + */ + onNext: (token: Token) => void; + + /** + * Called when an error occurs while calling the underlying IdentityProvider. The error can be + * transient and the token manager will attempt to obtain a token again if retry policy is configured. + * + * Only fatal errors will terminate the stream and stop the token manager. + * + * @param error + */ + onError: (error: IDPError) => void; + +} + +/** + * TokenManager is responsible for obtaining/refreshing tokens and notifying listeners about token changes. + * It uses an IdentityProvider to request tokens. The token refresh is scheduled based on the token's TTL and + * the expirationRefreshRatio configuration. + * + * The TokenManager should be disposed when it is no longer needed by calling the dispose method on the Disposable + * returned by start. + */ +export class TokenManager { + private currentToken: Token | null = null; + private refreshTimeout: NodeJS.Timeout | null = null; + private listener: TokenStreamListener | null = null; + private retryAttempt: number = 0; + + constructor( + private readonly identityProvider: IdentityProvider, + private readonly config: TokenManagerConfig + ) { + if (this.config.expirationRefreshRatio > 1) { + throw new Error('expirationRefreshRatio must be less than or equal to 1'); + } + if (this.config.expirationRefreshRatio < 0) { + throw new Error('expirationRefreshRatio must be greater or equal to 0'); + } + } + + /** + * Starts the token manager and returns a Disposable that can be used to stop the token manager. + * + * @param listener The listener that will receive token updates. + * @param initialDelayMs The initial delay in milliseconds before the first token refresh. + */ + public start(listener: TokenStreamListener, initialDelayMs: number = 0): Disposable { + if (this.listener) { + this.stop(); + } + + this.listener = listener; + this.retryAttempt = 0; + + this.scheduleNextRefresh(initialDelayMs); + + return { + dispose: () => this.stop() + }; + } + + public calculateRetryDelay(): number { + if (!this.config.retry) return 0; + + const { initialDelayMs, maxDelayMs, backoffMultiplier, jitterPercentage } = this.config.retry; + + let delay = initialDelayMs * Math.pow(backoffMultiplier, this.retryAttempt - 1); + + delay = Math.min(delay, maxDelayMs); + + if (jitterPercentage) { + const jitterRange = delay * (jitterPercentage / 100); + const jitterAmount = Math.random() * jitterRange - (jitterRange / 2); + delay += jitterAmount; + } + + let result = Math.max(0, Math.floor(delay)); + + return result; + } + + private shouldRetry(error: unknown): boolean { + if (!this.config.retry) return false; + + const { maxAttempts, isRetryable } = this.config.retry; + + if (this.retryAttempt >= maxAttempts) { + return false; + } + + if (isRetryable) { + return isRetryable(error, this.retryAttempt); + } + + return false; + } + + public isRunning(): boolean { + return this.listener !== null; + } + + private async refresh(): Promise { + if (!this.listener) { + throw new Error('TokenManager is not running, but refresh was called'); + } + + try { + await this.identityProvider.requestToken().then(this.handleNewToken); + this.retryAttempt = 0; + } catch (error) { + + if (this.shouldRetry(error)) { + this.retryAttempt++; + const retryDelay = this.calculateRetryDelay(); + this.notifyError(`Token refresh failed (attempt ${this.retryAttempt}), retrying in ${retryDelay}ms: ${error}`, true) + this.scheduleNextRefresh(retryDelay); + } else { + this.notifyError(error, false); + this.stop(); + } + } + } + + private handleNewToken = async ({ token: nativeToken, ttlMs }: TokenResponse): Promise => { + if (!this.listener) { + throw new Error('TokenManager is not running, but a new token was received'); + } + const token = this.wrapAndSetCurrentToken(nativeToken, ttlMs); + this.listener.onNext(token); + + this.scheduleNextRefresh(this.calculateRefreshTime(token)); + } + + /** + * Creates a Token object from a native token and sets it as the current token. + * + * @param nativeToken - The raw token received from the identity provider + * @param ttlMs - Time-to-live in milliseconds for the token + * + * @returns A new Token instance containing the wrapped native token and expiration details + * + */ + public wrapAndSetCurrentToken(nativeToken: T, ttlMs: number): Token { + const now = Date.now(); + const token = new Token( + nativeToken, + now + ttlMs, + now + ); + this.currentToken = token; + return token; + } + + private scheduleNextRefresh(delayMs: number): void { + if (this.refreshTimeout) { + clearTimeout(this.refreshTimeout); + this.refreshTimeout = null; + } + if (delayMs === 0) { + this.refresh(); + } else { + this.refreshTimeout = setTimeout(() => this.refresh(), delayMs); + } + + } + + /** + * Calculates the time in milliseconds when the token should be refreshed + * based on the token's TTL and the expirationRefreshRatio configuration. + * + * @param token The token to calculate the refresh time for. + * @param now The current time in milliseconds. Defaults to Date.now(). + */ + public calculateRefreshTime(token: Token, now: number = Date.now()): number { + const ttlMs = token.getTtlMs(now); + return Math.floor(ttlMs * this.config.expirationRefreshRatio); + } + + private stop(): void { + + if (this.refreshTimeout) { + clearTimeout(this.refreshTimeout); + this.refreshTimeout = null; + } + + this.listener = null; + this.currentToken = null; + this.retryAttempt = 0; + } + + /** + * Returns the current token or null if no token is available. + */ + public getCurrentToken(): Token | null { + return this.currentToken; + } + + private notifyError(error: unknown, isRetryable: boolean): void { + const errorMessage = error instanceof Error ? error.message : String(error); + + if (!this.listener) { + throw new Error(`TokenManager is not running but received an error: ${errorMessage}`); + } + + this.listener.onError(new IDPError(errorMessage, isRetryable)); + } +} \ No newline at end of file diff --git a/packages/client/lib/authx/token.ts b/packages/client/lib/authx/token.ts new file mode 100644 index 00000000000..3d6e6867d84 --- /dev/null +++ b/packages/client/lib/authx/token.ts @@ -0,0 +1,23 @@ +/** + * A token that can be used to authenticate with a service. + */ +export class Token { + constructor( + public readonly value: T, + //represents the token deadline - the time in milliseconds since the Unix epoch at which the token expires + public readonly expiresAtMs: number, + //represents the time in milliseconds since the Unix epoch at which the token was received + public readonly receivedAtMs: number + ) {} + + /** + * Returns the time-to-live of the token in milliseconds. + * @param now The current time in milliseconds since the Unix epoch. + */ + getTtlMs(now: number): number { + if (this.expiresAtMs < now) { + return 0; + } + return this.expiresAtMs - now; + } +} \ No newline at end of file diff --git a/packages/client/lib/client/index.spec.ts b/packages/client/lib/client/index.spec.ts index cd2040ec97f..c71cf1a1fad 100644 --- a/packages/client/lib/client/index.spec.ts +++ b/packages/client/lib/client/index.spec.ts @@ -1,6 +1,6 @@ import { strict as assert } from 'node:assert'; import testUtils, { GLOBAL, waitTillBeenCalled } from '../test-utils'; -import RedisClient, { RedisClientType } from '.'; +import RedisClient, { RedisClientOptions, RedisClientType } from '.'; import { AbortError, ClientClosedError, ClientOfflineError, ConnectionTimeoutError, DisconnectsClientError, ErrorReply, MultiErrorReply, SocketClosedUnexpectedlyError, WatchError } from '../errors'; import { defineScript } from '../lua-script'; import { spy } from 'sinon'; @@ -25,36 +25,87 @@ export const SQUARE_SCRIPT = defineScript({ describe('Client', () => { describe('parseURL', () => { - it('redis://user:secret@localhost:6379/0', () => { - assert.deepEqual( - RedisClient.parseURL('redis://user:secret@localhost:6379/0'), - { - socket: { - host: 'localhost', - port: 6379 - }, - username: 'user', - password: 'secret', - database: 0 + it('redis://user:secret@localhost:6379/0', async () => { + const result = RedisClient.parseURL('redis://user:secret@localhost:6379/0'); + const expected : RedisClientOptions = { + socket: { + host: 'localhost', + port: 6379 + }, + username: 'user', + password: 'secret', + database: 0, + credentialsProvider: { + type: 'async-credentials-provider', + credentials: async () => ({ + password: 'secret', + username: 'user' + }) } - ); + }; + + // Compare everything except the credentials function + const { credentialsProvider: resultCredProvider, ...resultRest } = result; + const { credentialsProvider: expectedCredProvider, ...expectedRest } = expected; + + // Compare non-function properties + assert.deepEqual(resultRest, expectedRest); + + if(result.credentialsProvider.type === 'async-credentials-provider' + && expected.credentialsProvider.type === 'async-credentials-provider') { + + // Compare the actual output of the credentials functions + const resultCreds = await result.credentialsProvider.credentials(); + const expectedCreds = await expected.credentialsProvider.credentials(); + assert.deepEqual(resultCreds, expectedCreds); + } else { + assert.fail('Credentials provider type mismatch'); + } + + }); - it('rediss://user:secret@localhost:6379/0', () => { - assert.deepEqual( - RedisClient.parseURL('rediss://user:secret@localhost:6379/0'), - { - socket: { - host: 'localhost', - port: 6379, - tls: true - }, - username: 'user', - password: 'secret', - database: 0 + it('rediss://user:secret@localhost:6379/0', async () => { + const result = RedisClient.parseURL('rediss://user:secret@localhost:6379/0'); + const expected: RedisClientOptions = { + socket: { + host: 'localhost', + port: 6379, + tls: true + }, + username: 'user', + password: 'secret', + database: 0, + credentialsProvider: { + credentials: async () => ({ + password: 'secret', + username: 'user' + }), + type: 'async-credentials-provider' } - ); - }); + }; + + // Compare everything except the credentials function + const { credentialsProvider: resultCredProvider, ...resultRest } = result; + const { credentialsProvider: expectedCredProvider, ...expectedRest } = expected; + + // Compare non-function properties + assert.deepEqual(resultRest, expectedRest); + assert.equal(resultCredProvider.type, expectedCredProvider.type); + + if (result.credentialsProvider.type === 'async-credentials-provider' && + expected.credentialsProvider.type === 'async-credentials-provider') { + + // Compare the actual output of the credentials functions + const resultCreds = await result.credentialsProvider.credentials(); + const expectedCreds = await expected.credentialsProvider.credentials(); + assert.deepEqual(resultCreds, expectedCreds); + + } else { + assert.fail('Credentials provider type mismatch'); + } + + }) it('Invalid protocol', () => { assert.throws( @@ -90,6 +141,21 @@ describe('Client', () => { ); }, GLOBAL.SERVERS.PASSWORD); + testUtils.testWithClient('Client can authenticate asynchronously ', async client => { + assert.equal( + await client.ping(), + 'PONG' + ); + }, GLOBAL.SERVERS.ASYNC_BASIC_AUTH); + + testUtils.testWithClient('Client can authenticate using the streaming credentials provider for initial token acquisition', + async client => { + assert.equal( + await client.ping(), + 'PONG' + ); + }, GLOBAL.SERVERS.STREAMING_AUTH); + testUtils.testWithClient('should execute AUTH before SELECT', async client => { assert.equal( (await client.clientInfo()).db, @@ -294,6 +360,7 @@ describe('Client', () => { assert.equal(err.replies.length, 2); assert.deepEqual(err.errorIndexes, [1]); assert.ok(err.replies[1] instanceof ErrorReply); + // @ts-ignore TS2802 assert.deepEqual([...err.errors()], [err.replies[1]]); return true; } diff --git a/packages/client/lib/client/index.ts b/packages/client/lib/client/index.ts index 55355a133dd..5dae1271ecb 100644 --- a/packages/client/lib/client/index.ts +++ b/packages/client/lib/client/index.ts @@ -1,5 +1,6 @@ import COMMANDS from '../commands'; import RedisSocket, { RedisSocketOptions } from './socket'; +import { BasicAuth, CredentialsError, CredentialsProvider, StreamingCredentialsProvider, UnableToObtainNewCredentialsError, Disposable } from '../authx'; import RedisCommandsQueue, { CommandOptions } from './commands-queue'; import { EventEmitter } from 'node:events'; import { attachConfig, functionArgumentsPrefix, getTransformReply, scriptArgumentsPrefix } from '../commander'; @@ -42,6 +43,13 @@ export interface RedisClientOptions< * ACL password or the old "--requirepass" password */ password?: string; + + /** + * Provides credentials for authentication. Can be set directly or will be created internally + * if username/password are provided instead. If both are supplied, this credentialsProvider + * takes precedence over username/password. + */ + credentialsProvider?: CredentialsProvider; /** * Client name ([see `CLIENT SETNAME`](https://redis.io/commands/client-setname)) */ @@ -261,6 +269,17 @@ export default class RedisClient< parsed.password = decodeURIComponent(password); } + if (username || password) { + parsed.credentialsProvider = { + type: 'async-credentials-provider', + credentials: async () => ( + { + username: username ? decodeURIComponent(username) : undefined, + password: password ? decodeURIComponent(password) : undefined + }) + }; + } + if (pathname.length > 1) { const database = Number(pathname.substring(1)); if (isNaN(database)) { @@ -284,6 +303,8 @@ export default class RedisClient< #epoch: number; #watchEpoch?: number; + #credentialsSubscription: Disposable | null = null; + get options(): RedisClientOptions | undefined { return this._self.#options; } @@ -317,6 +338,19 @@ export default class RedisClient< } #initiateOptions(options?: RedisClientOptions): RedisClientOptions | undefined { + + // Convert username/password to credentialsProvider if no credentialsProvider is already in place + if (!options?.credentialsProvider && (options?.username || options?.password)) { + + options.credentialsProvider = { + type: 'async-credentials-provider', + credentials: async () => ({ + username: options.username, + password: options.password + }) + }; + } + if (options?.url) { const parsed = RedisClient.parseURL(options.url); if (options.socket) { @@ -345,17 +379,65 @@ export default class RedisClient< ); } - #handshake(selectedDB: number) { + /** + * @param credentials + */ + private reAuthenticate = async (credentials: BasicAuth) => { + // Re-authentication is not supported on RESP2 with PubSub active + if (!(this.isPubSubActive && !this.#options?.RESP)) { + await this.sendCommand( + parseArgs(COMMANDS.AUTH, { + username: credentials.username, + password: credentials.password ?? '' + }) + ); + } + } + + #subscribeForStreamingCredentials(cp: StreamingCredentialsProvider): Promise<[BasicAuth, Disposable]> { + return cp.subscribe({ + onNext: credentials => { + this.reAuthenticate(credentials).catch(error => { + const errorMessage = error instanceof Error ? error.message : String(error); + cp.onReAuthenticationError(new CredentialsError(errorMessage)); + }); + + }, + onError: (e: Error) => { + const errorMessage = `Error from streaming credentials provider: ${e.message}`; + cp.onReAuthenticationError(new UnableToObtainNewCredentialsError(errorMessage)); + } + }); + } + + async #handshake(selectedDB: number) { const commands = []; + const cp = this.#options?.credentialsProvider; if (this.#options?.RESP) { const hello: HelloOptions = {}; - if (this.#options.password) { - hello.AUTH = { - username: this.#options.username ?? 'default', - password: this.#options.password - }; + if (cp && cp.type === 'async-credentials-provider') { + const credentials = await cp.credentials(); + if (credentials.password) { + hello.AUTH = { + username: credentials.username ?? 'default', + password: credentials.password + }; + } + } + + if (cp && cp.type === 'streaming-credentials-provider') { + + const [credentials, disposable] = await this.#subscribeForStreamingCredentials(cp) + this.#credentialsSubscription = disposable; + + if (credentials.password) { + hello.AUTH = { + username: credentials.username ?? 'default', + password: credentials.password + }; + } } if (this.#options.name) { @@ -366,13 +448,34 @@ export default class RedisClient< parseArgs(HELLO, this.#options.RESP, hello) ); } else { - if (this.#options?.username || this.#options?.password) { - commands.push( - parseArgs(COMMANDS.AUTH, { - username: this.#options.username, - password: this.#options.password ?? '' - }) - ); + + if (cp && cp.type === 'async-credentials-provider') { + + const credentials = await cp.credentials(); + + if (credentials.username || credentials.password) { + commands.push( + parseArgs(COMMANDS.AUTH, { + username: credentials.username, + password: credentials.password ?? '' + }) + ); + } + } + + if (cp && cp.type === 'streaming-credentials-provider') { + + const [credentials, disposable] = await this.#subscribeForStreamingCredentials(cp) + this.#credentialsSubscription = disposable; + + if (credentials.username || credentials.password) { + commands.push( + parseArgs(COMMANDS.AUTH, { + username: credentials.username, + password: credentials.password ?? '' + }) + ); + } } if (this.#options?.name) { @@ -396,7 +499,7 @@ export default class RedisClient< } #initiateSocket(): RedisSocket { - const socketInitiator = () => { + const socketInitiator = async () => { const promises = [], chainId = Symbol('Socket Initiator'); @@ -418,7 +521,7 @@ export default class RedisClient< ); } - const commands = this.#handshake(this.#selectedDB); + const commands = await this.#handshake(this.#selectedDB); for (let i = commands.length - 1; i >= 0; --i) { promises.push( this.#queue.addCommand(commands[i], { @@ -1000,7 +1103,9 @@ export default class RedisClient< const chainId = Symbol('Reset Chain'), promises = [this._self.#queue.reset(chainId)], selectedDB = this._self.#options?.database ?? 0; - for (const command of this._self.#handshake(selectedDB)) { + this._self.#credentialsSubscription?.dispose(); + this._self.#credentialsSubscription = null; + for (const command of (await this._self.#handshake(selectedDB))) { promises.push( this._self.#queue.addCommand(command, { chainId @@ -1051,6 +1156,8 @@ export default class RedisClient< * @deprecated use .close instead */ QUIT(): Promise { + this._self.#credentialsSubscription?.dispose(); + this._self.#credentialsSubscription = null; return this._self.#socket.quit(async () => { clearTimeout(this._self.#pingTimer); const quitPromise = this._self.#queue.addCommand(['QUIT']); @@ -1089,6 +1196,8 @@ export default class RedisClient< resolve(); }; this._self.#socket.on('data', maybeClose); + this._self.#credentialsSubscription?.dispose(); + this._self.#credentialsSubscription = null; }); } @@ -1099,6 +1208,8 @@ export default class RedisClient< clearTimeout(this._self.#pingTimer); this._self.#queue.flushAll(new DisconnectsClientError()); this._self.#socket.destroy(); + this._self.#credentialsSubscription?.dispose(); + this._self.#credentialsSubscription = null; } ref() { diff --git a/packages/client/lib/test-utils.ts b/packages/client/lib/test-utils.ts index 083c9127e5b..2d561dd2e20 100644 --- a/packages/client/lib/test-utils.ts +++ b/packages/client/lib/test-utils.ts @@ -1,6 +1,7 @@ import TestUtils from '@redis/test-utils'; import { SinonSpy } from 'sinon'; import { setTimeout } from 'node:timers/promises'; +import { CredentialsProvider } from './authx'; import { Command } from './RESP/types'; import { BasicCommandParser } from './client/parser'; @@ -16,6 +17,31 @@ const DEBUG_MODE_ARGS = utils.isVersionGreaterThan([7]) ? ['--enable-debug-command', 'yes'] : []; +const asyncBasicAuthCredentialsProvider: CredentialsProvider = + { + type: 'async-credentials-provider', + credentials: async () => ({ password: 'password' }) + } as const; + +const streamingCredentialsProvider: CredentialsProvider = + { + type: 'streaming-credentials-provider', + + subscribe : (observable) => ( Promise.resolve([ + { password: 'password' }, + { + dispose: () => { + console.log('disposing credentials provider subscription'); + } + } + ])), + + onReAuthenticationError: (error) => { + console.error('re-authentication error', error); + } + + } as const; + export const GLOBAL = { SERVERS: { OPEN: { @@ -26,6 +52,18 @@ export const GLOBAL = { clientOptions: { password: 'password' } + }, + ASYNC_BASIC_AUTH: { + serverArguments: ['--requirepass', 'password', ...DEBUG_MODE_ARGS], + clientOptions: { + credentialsProvider: asyncBasicAuthCredentialsProvider + } + }, + STREAMING_AUTH: { + serverArguments: ['--requirepass', 'password', ...DEBUG_MODE_ARGS], + clientOptions: { + credentialsProvider: streamingCredentialsProvider + } } }, CLUSTERS: { diff --git a/packages/entraid/.nycrc.json b/packages/entraid/.nycrc.json new file mode 100644 index 00000000000..848af2b5a27 --- /dev/null +++ b/packages/entraid/.nycrc.json @@ -0,0 +1,10 @@ +{ + "extends": "@istanbuljs/nyc-config-typescript", + "exclude": [ + "integration-tests", + "samples", + "dist", + "**/*.spec.ts", + "lib/test-utils.ts" + ] +} diff --git a/packages/entraid/.release-it.json b/packages/entraid/.release-it.json new file mode 100644 index 00000000000..a5f3a31062e --- /dev/null +++ b/packages/entraid/.release-it.json @@ -0,0 +1,11 @@ +{ + "git": { + "tagName": "entraid@${version}", + "commitMessage": "Release ${tagName}", + "tagAnnotation": "Release ${tagName}" + }, + "npm": { + "versionArgs": ["--workspaces-update=false"], + "publishArgs": ["--access", "public"] + } +} diff --git a/packages/entraid/README.md b/packages/entraid/README.md new file mode 100644 index 00000000000..e9c7956022e --- /dev/null +++ b/packages/entraid/README.md @@ -0,0 +1,137 @@ +# @redis/entraid + +Secure token-based authentication for Redis clients using Microsoft Entra ID (formerly Azure Active Directory). + +## Features + +- Token-based authentication using Microsoft Entra ID +- Automatic token refresh before expiration +- Automatic re-authentication of all connections after token refresh +- Support for multiple authentication flows: + - Managed identities (system-assigned and user-assigned) + - Service principals (with or without certificates) + - Authorization Code with PKCE flow +- Built-in retry mechanisms for transient failures + +## Installation + +```bash +npm install @redis/client +npm install @redis/entraid +``` + +## Getting Started + +The first step to using @redis/entraid is choosing the right credentials provider for your authentication needs. The `EntraIdCredentialsProviderFactory` class provides several factory methods to create the appropriate provider: + +- `createForSystemAssignedManagedIdentity`: Use when your application runs in Azure with a system-assigned managed identity +- `createForUserAssignedManagedIdentity`: Use when your application runs in Azure with a user-assigned managed identity +- `createForClientCredentials`: Use when authenticating with a service principal using client secret +- `createForClientCredentialsWithCertificate`: Use when authenticating with a service principal using a certificate +- `createForAuthorizationCodeWithPKCE`: Use for interactive authentication flows in user applications + +## Usage Examples + +### Service Principal Authentication + +```typescript +import { createClient } from '@redis/client'; +import { EntraIdCredentialsProviderFactory } from '@redis/entraid'; + +const provider = EntraIdCredentialsProviderFactory.createForClientCredentials({ + clientId: 'your-client-id', + clientSecret: 'your-client-secret', + authorityConfig: { + type: 'multi-tenant', + tenantId: 'your-tenant-id' + }, + tokenManagerConfig: { + expirationRefreshRatio: 0.8 // Refresh token after 80% of its lifetime + } +}); + +const client = createClient({ + url: 'redis://your-host', + credentialsProvider: provider +}); + +await client.connect(); +``` + +### System-Assigned Managed Identity + +```typescript +const provider = EntraIdCredentialsProviderFactory.createForSystemAssignedManagedIdentity({ + clientId: 'your-client-id', + tokenManagerConfig: { + expirationRefreshRatio: 0.8 + } +}); +``` + +### User-Assigned Managed Identity + +```typescript +const provider = EntraIdCredentialsProviderFactory.createForUserAssignedManagedIdentity({ + clientId: 'your-client-id', + userAssignedClientId: 'your-user-assigned-client-id', + tokenManagerConfig: { + expirationRefreshRatio: 0.8 + } +}); +``` + +## Important Limitations + +### RESP2 PUB/SUB Limitations + +When using RESP2 (Redis Serialization Protocol 2), there are important limitations with PUB/SUB: + +- **No Re-Authentication in PUB/SUB Mode**: In RESP2, once a connection enters PUB/SUB mode, the socket is blocked and cannot process out-of-band commands like AUTH. This means that connections in PUB/SUB mode cannot be re-authenticated when tokens are refreshed. +- **Connection Eviction**: As a result, PUB/SUB connections will be evicted by the Redis proxy when their tokens expire. The client will need to establish new connections with fresh tokens. + +### Transaction Safety + +When using token-based authentication, special care must be taken with Redis transactions. The token manager runs in the background and may attempt to re-authenticate connections at any time by sending AUTH commands. This can interfere with manually constructed transactions. + +#### ✅ Recommended: Use the Official Transaction API + +Always use the official transaction API provided by the client: + +```typescript +// Correct way to handle transactions +const multi = client.multi(); +multi.set('key1', 'value1'); +multi.set('key2', 'value2'); +await multi.exec(); +``` + +#### ❌ Avoid: Manual Transaction Construction + +Do not manually construct transactions by sending individual MULTI/EXEC commands: + +```typescript +// Incorrect and potentially dangerous +await client.sendCommand(['MULTI']); +await client.sendCommand(['SET', 'key1', 'value1']); +await client.sendCommand(['SET', 'key2', 'value2']); +await client.sendCommand(['EXEC']); // Risk of AUTH command being injected before EXEC +``` + +## Error Handling + +The provider includes built-in retry mechanisms for transient errors: + +```typescript +const provider = EntraIdCredentialsProviderFactory.createForClientCredentials({ + // ... other config ... + tokenManagerConfig: { + retry: { + maxAttempts: 3, + initialDelayMs: 100, + maxDelayMs: 1000, + backoffMultiplier: 2 + } + } +}); +``` diff --git a/packages/entraid/integration-tests/entraid-integration.spec.ts b/packages/entraid/integration-tests/entraid-integration.spec.ts new file mode 100644 index 00000000000..deb1d47dec1 --- /dev/null +++ b/packages/entraid/integration-tests/entraid-integration.spec.ts @@ -0,0 +1,217 @@ +import { BasicAuth } from '@redis/client/dist/lib/authx'; +import { createClient } from '@redis/client'; +import { EntraIdCredentialsProviderFactory } from '../lib/entra-id-credentials-provider-factory'; +import { strict as assert } from 'node:assert'; +import { spy, SinonSpy } from 'sinon'; +import { randomUUID } from 'crypto'; +import { loadFromFile, RedisEndpointsConfig } from '@redis/test-utils/lib/cae-client-testing'; +import { EntraidCredentialsProvider } from '../lib/entraid-credentials-provider'; +import * as crypto from 'node:crypto'; + +describe('EntraID Integration Tests', () => { + + it('client configured with client secret should be able to authenticate/re-authenticate', async () => { + const config = await readConfigFromEnv(); + await runAuthenticationTest(() => + EntraIdCredentialsProviderFactory.createForClientCredentials({ + clientId: config.clientId, + clientSecret: config.clientSecret, + authorityConfig: { type: 'multi-tenant', tenantId: config.tenantId }, + tokenManagerConfig: { + expirationRefreshRatio: 0.0001 + } + }) + ); + }); + + it('client configured with client certificate should be able to authenticate/re-authenticate', async () => { + const config = await readConfigFromEnv(); + await runAuthenticationTest(() => + EntraIdCredentialsProviderFactory.createForClientCredentialsWithCertificate({ + clientId: config.clientId, + certificate: convertCertsForMSAL(config.cert, config.privateKey), + authorityConfig: { type: 'multi-tenant', tenantId: config.tenantId }, + tokenManagerConfig: { + expirationRefreshRatio: 0.0001 + } + }) + ); + }); + + it('client with system managed identity should be able to authenticate/re-authenticate', async () => { + const config = await readConfigFromEnv(); + await runAuthenticationTest(() => + EntraIdCredentialsProviderFactory.createForSystemAssignedManagedIdentity({ + clientId: config.clientId, + authorityConfig: { type: 'multi-tenant', tenantId: config.tenantId }, + tokenManagerConfig: { + expirationRefreshRatio: 0.00001 + } + }) + ); + }); + + interface TestConfig { + clientId: string; + clientSecret: string; + authority: string; + tenantId: string; + redisScopes: string; + cert: string; + privateKey: string; + userAssignedManagedId: string; + endpoints: RedisEndpointsConfig; + } + + const readConfigFromEnv = async (): Promise => { + const requiredEnvVars = { + AZURE_CLIENT_ID: process.env.AZURE_CLIENT_ID, + AZURE_CLIENT_SECRET: process.env.AZURE_CLIENT_SECRET, + AZURE_AUTHORITY: process.env.AZURE_AUTHORITY, + AZURE_TENANT_ID: process.env.AZURE_TENANT_ID, + AZURE_REDIS_SCOPES: process.env.AZURE_REDIS_SCOPES, + AZURE_CERT: process.env.AZURE_CERT, + AZURE_PRIVATE_KEY: process.env.AZURE_PRIVATE_KEY, + AZURE_USER_ASSIGNED_MANAGED_ID: process.env.AZURE_USER_ASSIGNED_MANAGED_ID, + REDIS_ENDPOINTS_CONFIG_PATH: process.env.REDIS_ENDPOINTS_CONFIG_PATH + }; + + Object.entries(requiredEnvVars).forEach(([key, value]) => { + if (value == undefined) { + throw new Error(`${key} environment variable must be set`); + } + }); + + return { + endpoints: await loadFromFile(requiredEnvVars.REDIS_ENDPOINTS_CONFIG_PATH), + clientId: requiredEnvVars.AZURE_CLIENT_ID, + clientSecret: requiredEnvVars.AZURE_CLIENT_SECRET, + authority: requiredEnvVars.AZURE_AUTHORITY, + tenantId: requiredEnvVars.AZURE_TENANT_ID, + redisScopes: requiredEnvVars.AZURE_REDIS_SCOPES, + cert: requiredEnvVars.AZURE_CERT, + privateKey: requiredEnvVars.AZURE_PRIVATE_KEY, + userAssignedManagedId: requiredEnvVars.AZURE_USER_ASSIGNED_MANAGED_ID + }; + }; + + interface TokenDetail { + token: string; + exp: number; + iat: number; + lifetime: number; + uti: string; + } + + const setupTestClient = async (credentialsProvider: EntraidCredentialsProvider) => { + const config = await readConfigFromEnv(); + const client = createClient({ + url: config.endpoints['standalone-entraid-acl'].endpoints[0], + credentialsProvider + }); + + const clientInstance = (client as any)._self; + const reAuthSpy: SinonSpy = spy(clientInstance, 'reAuthenticate'); + + return { client, reAuthSpy }; + }; + + const runClientOperations = async (client: any) => { + const startTime = Date.now(); + while (Date.now() - startTime < 1000) { + const key = randomUUID(); + await client.set(key, 'value'); + const value = await client.get(key); + assert.equal(value, 'value'); + await client.del(key); + } + }; + + const validateTokens = (reAuthSpy: SinonSpy) => { + assert(reAuthSpy.callCount >= 1, + `reAuthenticate should have been called at least once, but was called ${reAuthSpy.callCount} times`); + + const tokenDetails: TokenDetail[] = reAuthSpy.getCalls().map(call => { + const creds = call.args[0] as BasicAuth; + const tokenPayload = JSON.parse( + Buffer.from(creds.password.split('.')[1], 'base64').toString() + ); + + return { + token: creds.password, + exp: tokenPayload.exp, + iat: tokenPayload.iat, + lifetime: tokenPayload.exp - tokenPayload.iat, + uti: tokenPayload.uti + }; + }); + + // Verify unique tokens + const uniqueTokens = new Set(tokenDetails.map(detail => detail.token)); + assert.equal( + uniqueTokens.size, + reAuthSpy.callCount, + `Expected ${reAuthSpy.callCount} different tokens, but got ${uniqueTokens.size} unique tokens` + ); + + // Verify all tokens are not cached (i.e. have the same lifetime) + const uniqueLifetimes = new Set(tokenDetails.map(detail => detail.lifetime)); + assert.equal( + uniqueLifetimes.size, + 1, + `Expected all tokens to have the same lifetime, but found ${uniqueLifetimes.size} different lifetimes: ${[uniqueLifetimes].join(', ')} seconds` + ); + + // Verify that all tokens have different uti (unique token identifier) + const uniqueUti = new Set(tokenDetails.map(detail => detail.uti)); + assert.equal( + uniqueUti.size, + reAuthSpy.callCount, + `Expected all tokens to have different uti, but found ${uniqueUti.size} different uti in: ${[uniqueUti].join(', ')}` + ); + }; + + const runAuthenticationTest = async (setupCredentialsProvider: () => any) => { + const { client, reAuthSpy } = await setupTestClient(setupCredentialsProvider()); + + try { + await client.connect(); + await runClientOperations(client); + validateTokens(reAuthSpy); + } finally { + await client.destroy(); + } + }; + +}); + +function getCertificate(certBase64) { + try { + const decodedCert = Buffer.from(certBase64, 'base64'); + const cert = new crypto.X509Certificate(decodedCert); + return cert; + } catch (error) { + console.error('Error parsing certificate:', error); + throw error; + } +} + +function getCertificateThumbprint(certBase64) { + const cert = getCertificate(certBase64); + return cert.fingerprint.replace(/:/g, ''); +} + +function convertCertsForMSAL(certBase64, privateKeyBase64) { + const thumbprint = getCertificateThumbprint(certBase64); + + const privateKeyPEM = `-----BEGIN PRIVATE KEY-----\n${privateKeyBase64}\n-----END PRIVATE KEY-----`; + + return { + thumbprint: thumbprint, + privateKey: privateKeyPEM, + x5c: certBase64 + } + +} + + diff --git a/packages/entraid/lib/entra-id-credentials-provider-factory.ts b/packages/entraid/lib/entra-id-credentials-provider-factory.ts new file mode 100644 index 00000000000..0f89be8039b --- /dev/null +++ b/packages/entraid/lib/entra-id-credentials-provider-factory.ts @@ -0,0 +1,371 @@ +import { NetworkError } from '@azure/msal-common'; +import { + LogLevel, + ManagedIdentityApplication, + ManagedIdentityConfiguration, + AuthenticationResult, + PublicClientApplication, + ConfidentialClientApplication, AuthorizationUrlRequest, AuthorizationCodeRequest, CryptoProvider, Configuration, NodeAuthOptions, AccountInfo +} from '@azure/msal-node'; +import { RetryPolicy, TokenManager, TokenManagerConfig, ReAuthenticationError } from '@redis/client/dist/lib/authx'; +import { EntraidCredentialsProvider } from './entraid-credentials-provider'; +import { MSALIdentityProvider } from './msal-identity-provider'; + +/** + * This class is used to create credentials providers for different types of authentication flows. + */ +export class EntraIdCredentialsProviderFactory { + + /** + * This method is used to create a ManagedIdentityProvider for both system-assigned and user-assigned managed identities. + * + * @param params + * @param userAssignedClientId For user-assigned managed identities, the developer needs to pass either the client ID, + * full resource identifier, or the object ID of the managed identity when creating ManagedIdentityApplication. + * + */ + public static createManagedIdentityProvider( + params: CredentialParams, userAssignedClientId?: string + ): EntraidCredentialsProvider { + const config: ManagedIdentityConfiguration = { + // For user-assigned identity, include the client ID + ...(userAssignedClientId && { + managedIdentityIdParams: { + userAssignedClientId + } + }), + system: { + loggerOptions + } + }; + + const client = new ManagedIdentityApplication(config); + + const idp = new MSALIdentityProvider( + () => client.acquireToken({ + resource: params.scopes?.[0] ?? REDIS_SCOPE, + forceRefresh: true + }).then(x => x === null ? Promise.reject('Token is null') : x) + ); + + return new EntraidCredentialsProvider( + new TokenManager(idp, params.tokenManagerConfig), + idp, + { onReAuthenticationError: params.onReAuthenticationError, credentialsMapper: OID_CREDENTIALS_MAPPER } + ); + } + + /** + * This method is used to create a credentials provider for system-assigned managed identities. + * @param params + */ + static createForSystemAssignedManagedIdentity( + params: CredentialParams + ): EntraidCredentialsProvider { + return this.createManagedIdentityProvider(params); + } + + /** + * This method is used to create a credentials provider for user-assigned managed identities. + * It will include the client ID as the userAssignedClientId in the ManagedIdentityConfiguration. + * @param params + */ + static createForUserAssignedManagedIdentity( + params: CredentialParams & { userAssignedClientId: string } + ): EntraidCredentialsProvider { + return this.createManagedIdentityProvider(params, params.userAssignedClientId); + } + + static #createForClientCredentials( + authConfig: NodeAuthOptions, + params: CredentialParams + ): EntraidCredentialsProvider { + const config: Configuration = { + auth: { + ...authConfig, + authority: this.getAuthority(params.authorityConfig ?? { type: 'default' }) + }, + system: { + loggerOptions + } + }; + + const client = new ConfidentialClientApplication(config); + + const idp = new MSALIdentityProvider( + () => client.acquireTokenByClientCredential({ + skipCache: true, + scopes: params.scopes ?? [REDIS_SCOPE_DEFAULT] + }).then(x => x === null ? Promise.reject('Token is null') : x) + ); + + return new EntraidCredentialsProvider(new TokenManager(idp, params.tokenManagerConfig), idp, + { + onReAuthenticationError: params.onReAuthenticationError, + credentialsMapper: OID_CREDENTIALS_MAPPER + }); + } + + /** + * This method is used to create a credentials provider for service principals using certificate. + * @param params + */ + static createForClientCredentialsWithCertificate( + params: ClientCredentialsWithCertificateParams + ): EntraidCredentialsProvider { + return this.#createForClientCredentials( + { + clientId: params.clientId, + clientCertificate: params.certificate + }, + params + ); + } + + /** + * This method is used to create a credentials provider for service principals using client secret. + * @param params + */ + static createForClientCredentials( + params: ClientSecretCredentialsParams + ): EntraidCredentialsProvider { + return this.#createForClientCredentials( + { + clientId: params.clientId, + clientSecret: params.clientSecret + }, + params + ); + } + + /** + * This method is used to create a credentials provider for the Authorization Code Flow with PKCE. + * @param params + */ + static createForAuthorizationCodeWithPKCE( + params: AuthCodePKCEParams + ): { + getPKCECodes: () => Promise<{ + verifier: string; + challenge: string; + challengeMethod: string; + }>; + getAuthCodeUrl: ( + pkceCodes: { challenge: string; challengeMethod: string } + ) => Promise; + createCredentialsProvider: ( + params: PKCEParams + ) => EntraidCredentialsProvider; + } { + + const requiredScopes = ['user.read', 'offline_access']; + const scopes = [...new Set([...(params.scopes || []), ...requiredScopes])]; + + const authFlow = AuthCodeFlowHelper.create({ + clientId: params.clientId, + redirectUri: params.redirectUri, + scopes: scopes, + authorityConfig: params.authorityConfig + }); + + return { + getPKCECodes: AuthCodeFlowHelper.generatePKCE, + getAuthCodeUrl: (pkceCodes) => authFlow.getAuthCodeUrl(pkceCodes), + createCredentialsProvider: (pkceParams) => { + + // This is used to store the initial credentials account to be used + // for silent token acquisition after the initial token acquisition. + let initialCredentialsAccount: AccountInfo | null = null; + + const idp = new MSALIdentityProvider( + async () => { + if (!initialCredentialsAccount) { + let authResult = await authFlow.acquireTokenByCode(pkceParams); + initialCredentialsAccount = authResult.account; + return authResult; + } else { + return authFlow.client.acquireTokenSilent({ + forceRefresh: true, + account: initialCredentialsAccount, + scopes + }); + } + + } + ); + const tm = new TokenManager(idp, params.tokenManagerConfig); + return new EntraidCredentialsProvider(tm, idp, { onReAuthenticationError: params.onReAuthenticationError }); + } + }; + } + + static getAuthority(config: AuthorityConfig): string { + switch (config.type) { + case 'multi-tenant': + return `https://login.microsoftonline.com/${config.tenantId}`; + case 'custom': + return config.authorityUrl; + case 'default': + return 'https://login.microsoftonline.com/common'; + default: + throw new Error('Invalid authority configuration'); + } + } + +} + +const REDIS_SCOPE_DEFAULT = 'https://redis.azure.com/.default'; +const REDIS_SCOPE = 'https://redis.azure.com' + +export type AuthorityConfig = + | { type: 'multi-tenant'; tenantId: string } + | { type: 'custom'; authorityUrl: string } + | { type: 'default' }; + +export type PKCEParams = { + code: string; + verifier: string; + clientInfo?: string; +} + +export type CredentialParams = { + clientId: string; + scopes?: string[]; + authorityConfig?: AuthorityConfig; + + tokenManagerConfig: TokenManagerConfig + onReAuthenticationError?: (error: ReAuthenticationError) => void; +} + +export type AuthCodePKCEParams = CredentialParams & { + redirectUri: string; +}; + +export type ClientSecretCredentialsParams = CredentialParams & { + clientSecret: string; +}; + +export type ClientCredentialsWithCertificateParams = CredentialParams & { + certificate: { + thumbprint: string; + privateKey: string; + x5c?: string; + }; +}; + +const loggerOptions = { + loggerCallback(loglevel: LogLevel, message: string, containsPii: boolean) { + if (!containsPii) console.log(message); + }, + piiLoggingEnabled: false, + logLevel: LogLevel.Error +} + +/** + * The most important part of the RetryPolicy is the `isRetryable` function. This function is used to determine if a request should be retried based + * on the error returned from the identity provider. The default for is to retry on network errors only. + */ +export const DEFAULT_RETRY_POLICY: RetryPolicy = { + // currently only retry on network errors + isRetryable: (error: unknown) => error instanceof NetworkError, + maxAttempts: 10, + initialDelayMs: 100, + maxDelayMs: 100000, + backoffMultiplier: 2, + jitterPercentage: 0.1 + +}; + +export const DEFAULT_TOKEN_MANAGER_CONFIG: TokenManagerConfig = { + retry: DEFAULT_RETRY_POLICY, + expirationRefreshRatio: 0.7 // Refresh token when 70% of the token has expired +} + +/** + * This class is used to help with the Authorization Code Flow with PKCE. + * It provides methods to generate PKCE codes, get the authorization URL, and create the credential provider. + */ +export class AuthCodeFlowHelper { + private constructor( + readonly client: PublicClientApplication, + readonly scopes: string[], + readonly redirectUri: string + ) {} + + async getAuthCodeUrl(pkceCodes: { + challenge: string; + challengeMethod: string; + }): Promise { + const authCodeUrlParameters: AuthorizationUrlRequest = { + scopes: this.scopes, + redirectUri: this.redirectUri, + codeChallenge: pkceCodes.challenge, + codeChallengeMethod: pkceCodes.challengeMethod + }; + + return this.client.getAuthCodeUrl(authCodeUrlParameters); + } + + async acquireTokenByCode(params: PKCEParams): Promise { + const tokenRequest: AuthorizationCodeRequest = { + code: params.code, + scopes: this.scopes, + redirectUri: this.redirectUri, + codeVerifier: params.verifier, + clientInfo: params.clientInfo + }; + + return this.client.acquireTokenByCode(tokenRequest); + } + + static async generatePKCE(): Promise<{ + verifier: string; + challenge: string; + challengeMethod: string; + }> { + const cryptoProvider = new CryptoProvider(); + const { verifier, challenge } = await cryptoProvider.generatePkceCodes(); + return { + verifier, + challenge, + challengeMethod: 'S256' + }; + } + + static create(params: { + clientId: string; + redirectUri: string; + scopes?: string[]; + authorityConfig?: AuthorityConfig; + }): AuthCodeFlowHelper { + const config = { + auth: { + clientId: params.clientId, + authority: EntraIdCredentialsProviderFactory.getAuthority(params.authorityConfig ?? { type: 'default' }) + }, + system: { + loggerOptions + } + }; + + return new AuthCodeFlowHelper( + new PublicClientApplication(config), + params.scopes ?? ['user.read'], + params.redirectUri + ); + } +} + +const OID_CREDENTIALS_MAPPER = (token: AuthenticationResult) => { + + // Client credentials flow is app-only authentication (no user context), + // so only access token is provided without user-specific claims (uniqueId, idToken, ...) + // this means that we need to extract the oid from the access token manually + const accessToken = JSON.parse(Buffer.from(token.accessToken.split('.')[1], 'base64').toString()); + + return ({ + username: accessToken.oid, + password: token.accessToken + }) + +} diff --git a/packages/entraid/lib/entraid-credentials-provider.spec.ts b/packages/entraid/lib/entraid-credentials-provider.spec.ts new file mode 100644 index 00000000000..1bdf4e9b65f --- /dev/null +++ b/packages/entraid/lib/entraid-credentials-provider.spec.ts @@ -0,0 +1,199 @@ +import { AuthenticationResult } from '@azure/msal-node'; +import { IdentityProvider, TokenManager, TokenResponse, BasicAuth } from '@redis/client/dist/lib/authx'; +import { EntraidCredentialsProvider } from './entraid-credentials-provider'; +import { setTimeout } from 'timers/promises'; +import { strict as assert } from 'node:assert'; +import { GLOBAL, testUtils } from './test-utils' + + +describe('EntraID authentication in cluster mode', () => { + + testUtils.testWithCluster('sendCommand', async cluster => { + assert.equal( + await cluster.sendCommand(undefined, true, ['PING']), + 'PONG' + ); + }, GLOBAL.CLUSTERS.PASSWORD_WITH_REPLICAS); +}) + +describe('EntraID CredentialsProvider Subscription Behavior', () => { + + it('should properly handle token refresh sequence for multiple subscribers', async () => { + const networkDelay = 20; + const tokenTTL = 100; + const refreshRatio = 0.5; // Refresh at 50% of TTL + + const idp = new SequenceEntraIDProvider(tokenTTL, networkDelay); + const tokenManager = new TokenManager(idp, { + expirationRefreshRatio: refreshRatio + }); + const entraid = new EntraidCredentialsProvider(tokenManager, idp); + + // Create two initial subscribers + const subscriber1 = new TestSubscriber('subscriber1'); + const subscriber2 = new TestSubscriber('subscriber2'); + + assert.equal(entraid.hasActiveSubscriptions(), false, 'There should be no active subscriptions'); + assert.equal(entraid.getSubscriptionsCount(), 0, 'There should be 0 subscriptions'); + + // Start the first two subscriptions almost simultaneously + const [sub1Initial, sub2Initial] = await Promise.all([ + entraid.subscribe(subscriber1), + entraid.subscribe(subscriber2)] + ); + + assertCredentials(sub1Initial[0], 'initial-token', 'Subscriber 1 should receive initial token'); + assertCredentials(sub2Initial[0], 'initial-token', 'Subscriber 2 should receive initial token'); + + assert.equal(entraid.hasActiveSubscriptions(), true, 'There should be active subscriptions'); + assert.equal(entraid.getSubscriptionsCount(), 2, 'There should be 2 subscriptions'); + + // add a third subscriber after a very short delay + const subscriber3 = new TestSubscriber('subscriber3'); + await setTimeout(1); + const sub3Initial = await entraid.subscribe(subscriber3) + + assert.equal(entraid.hasActiveSubscriptions(), true, 'There should be active subscriptions'); + assert.equal(entraid.getSubscriptionsCount(), 3, 'There should be 3 subscriptions'); + + // make sure the third subscriber gets the initial token as well + assertCredentials(sub3Initial[0], 'initial-token', 'Subscriber 3 should receive initial token'); + + // Wait for first refresh (50% of TTL + network delay + small buffer) + await setTimeout((tokenTTL * refreshRatio) + networkDelay + 15); + + // All 3 subscribers should receive refresh-token-1 + assertCredentials(subscriber1.credentials[0], 'refresh-token-1', 'Subscriber 1 should receive first refresh token'); + assertCredentials(subscriber2.credentials[0], 'refresh-token-1', 'Subscriber 2 should receive first refresh token'); + assertCredentials(subscriber3.credentials[0], 'refresh-token-1', 'Subscriber 3 should receive first refresh token'); + + // Add a late subscriber - should immediately get refresh-token-1 + const subscriber4 = new TestSubscriber('subscriber4'); + const sub4Initial = await entraid.subscribe(subscriber4); + + assert.equal(entraid.hasActiveSubscriptions(), true, 'There should be active subscriptions'); + assert.equal(entraid.getSubscriptionsCount(), 4, 'There should be 4 subscriptions'); + + assertCredentials(sub4Initial[0], 'refresh-token-1', 'Late subscriber should receive refresh-token-1'); + + // Wait for second refresh + await setTimeout((tokenTTL * refreshRatio) + networkDelay + 15); + + assertCredentials(subscriber1.credentials[1], 'refresh-token-2', 'Subscriber 1 should receive second refresh token'); + assertCredentials(subscriber2.credentials[1], 'refresh-token-2', 'Subscriber 2 should receive second refresh token'); + assertCredentials(subscriber3.credentials[1], 'refresh-token-2', 'Subscriber 3 should receive second refresh token'); + + assertCredentials(subscriber4.credentials[0], 'refresh-token-2', 'Subscriber 4 should receive second refresh token'); + + // Verify refreshes happen after minimum expected time + const minimumRefreshInterval = tokenTTL * 0.4; // 40% of TTL as safety margin + + verifyRefreshTiming(subscriber1, minimumRefreshInterval); + verifyRefreshTiming(subscriber2, minimumRefreshInterval); + verifyRefreshTiming(subscriber3, minimumRefreshInterval); + verifyRefreshTiming(subscriber4, minimumRefreshInterval); + + // Cleanup + + assert.equal(tokenManager.isRunning(), true); + sub1Initial[1].dispose(); + sub2Initial[1].dispose(); + sub3Initial[1].dispose(); + assert.equal(entraid.hasActiveSubscriptions(), true, 'There should be active subscriptions'); + assert.equal(entraid.getSubscriptionsCount(), 1, 'There should be 1 subscriptions'); + sub4Initial[1].dispose(); + assert.equal(entraid.hasActiveSubscriptions(), false, 'There should be no active subscriptions'); + assert.equal(entraid.getSubscriptionsCount(), 0, 'There should be 0 subscriptions'); + assert.equal(tokenManager.isRunning(), false) + }); + + const verifyRefreshTiming = ( + subscriber: TestSubscriber, + expectedMinimumInterval: number, + message?: string + ) => { + const intervals = []; + for (let i = 1; i < subscriber.timestamps.length; i++) { + intervals.push(subscriber.timestamps[i] - subscriber.timestamps[i - 1]); + } + + intervals.forEach((interval, index) => { + assert.ok( + interval > expectedMinimumInterval, + message || `Refresh ${index + 1} for ${subscriber.name} should happen after minimum interval of ${expectedMinimumInterval}ms` + ); + }); + }; + + class SequenceEntraIDProvider implements IdentityProvider { + private currentIndex = 0; + + constructor( + private readonly tokenTTL: number = 100, + private tokenDeliveryDelayMs: number = 0, + private readonly tokenSequence: AuthenticationResult[] = [ + { + accessToken: 'initial-token', + uniqueId: 'test-user' + } as AuthenticationResult, + { + accessToken: 'refresh-token-1', + uniqueId: 'test-user' + } as AuthenticationResult, + { + accessToken: 'refresh-token-2', + uniqueId: 'test-user' + } as AuthenticationResult + ] + ) {} + + setTokenDeliveryDelay(delayMs: number): void { + this.tokenDeliveryDelayMs = delayMs; + } + + async requestToken(): Promise> { + if (this.tokenDeliveryDelayMs > 0) { + await setTimeout(this.tokenDeliveryDelayMs); + } + + if (this.currentIndex >= this.tokenSequence.length) { + throw new Error('No more tokens in sequence'); + } + + return { + token: this.tokenSequence[this.currentIndex++], + ttlMs: this.tokenTTL + }; + } + } + + class TestSubscriber { + public readonly credentials: Array = []; + public readonly errors: Error[] = []; + public readonly timestamps: number[] = []; + + constructor(public readonly name: string = 'unnamed') {} + + onNext = (creds: BasicAuth) => { + this.credentials.push(creds); + this.timestamps.push(Date.now()); + } + + onError = (error: Error) => { + this.errors.push(error); + } + } + + /** + * Assert that the actual credentials match the expected token + * @param actual + * @param expectedToken + * @param message + */ + const assertCredentials = (actual: BasicAuth, expectedToken: string, message: string) => { + assert.deepEqual(actual, { + username: 'test-user', + password: expectedToken + }, message); + }; +}); \ No newline at end of file diff --git a/packages/entraid/lib/entraid-credentials-provider.ts b/packages/entraid/lib/entraid-credentials-provider.ts new file mode 100644 index 00000000000..115d6dbff3a --- /dev/null +++ b/packages/entraid/lib/entraid-credentials-provider.ts @@ -0,0 +1,140 @@ +import { AuthenticationResult } from '@azure/msal-common/node'; +import { + BasicAuth, StreamingCredentialsProvider, IdentityProvider, TokenManager, + ReAuthenticationError, StreamingCredentialsListener, IDPError, Token, Disposable +} from '@redis/client/dist/lib/authx'; + +/** + * A streaming credentials provider that uses the Entraid identity provider to provide credentials. + * Please use one of the factory functions in `entraid-credetfactories.ts` to create an instance of this class for the different + * type of authentication flows. + */ +export class EntraidCredentialsProvider implements StreamingCredentialsProvider { + readonly type = 'streaming-credentials-provider'; + + readonly #listeners: Set> = new Set(); + + #tokenManagerDisposable: Disposable | null = null; + #isStarting: boolean = false; + + #pendingSubscribers: Array<{ + resolve: (value: [BasicAuth, Disposable]) => void; + reject: (error: Error) => void; + pendingListener: StreamingCredentialsListener; + }> = []; + + constructor( + public readonly tokenManager: TokenManager, + public readonly idp: IdentityProvider, + private readonly options: { + onReAuthenticationError?: (error: ReAuthenticationError) => void; + credentialsMapper?: (token: AuthenticationResult) => BasicAuth; + onRetryableError?: (error: string) => void; + } = {} + ) { + this.onReAuthenticationError = options.onReAuthenticationError ?? DEFAULT_ERROR_HANDLER; + this.#credentialsMapper = options.credentialsMapper ?? DEFAULT_CREDENTIALS_MAPPER; + } + + async subscribe( + listener: StreamingCredentialsListener + ): Promise<[BasicAuth, Disposable]> { + + const currentToken = this.tokenManager.getCurrentToken(); + + if (currentToken) { + return [this.#credentialsMapper(currentToken.value), this.#createDisposable(listener)]; + } + + if (this.#isStarting) { + return new Promise((resolve, reject) => { + this.#pendingSubscribers.push({ resolve, reject, pendingListener: listener }); + }); + } + + this.#isStarting = true; + try { + const initialToken = await this.#startTokenManagerAndObtainInitialToken(); + + this.#pendingSubscribers.forEach(({ resolve, pendingListener }) => { + resolve([this.#credentialsMapper(initialToken.value), this.#createDisposable(pendingListener)]); + }); + this.#pendingSubscribers = []; + + return [this.#credentialsMapper(initialToken.value), this.#createDisposable(listener)]; + } finally { + this.#isStarting = false; + } + } + + onReAuthenticationError: (error: ReAuthenticationError) => void; + + #credentialsMapper: (token: AuthenticationResult) => BasicAuth; + + #createTokenManagerListener(subscribers: Set>) { + return { + onError: (error: IDPError): void => { + if (!error.isRetryable) { + subscribers.forEach(listener => listener.onError(error)); + } else { + this.options.onRetryableError?.(error.message); + } + }, + onNext: (token: { value: AuthenticationResult }): void => { + const credentials = this.#credentialsMapper(token.value); + subscribers.forEach(listener => listener.onNext(credentials)); + } + }; + } + + #createDisposable(listener: StreamingCredentialsListener): Disposable { + this.#listeners.add(listener); + + return { + dispose: () => { + this.#listeners.delete(listener); + if (this.#listeners.size === 0 && this.#tokenManagerDisposable) { + this.#tokenManagerDisposable.dispose(); + this.#tokenManagerDisposable = null; + } + } + }; + } + + async #startTokenManagerAndObtainInitialToken(): Promise> { + const initialResponse = await this.idp.requestToken(); + const token = this.tokenManager.wrapAndSetCurrentToken(initialResponse.token, initialResponse.ttlMs); + + this.#tokenManagerDisposable = this.tokenManager.start( + this.#createTokenManagerListener(this.#listeners), + this.tokenManager.calculateRefreshTime(token) + ); + return token; + } + + public hasActiveSubscriptions(): boolean { + return this.#tokenManagerDisposable !== null && this.#listeners.size > 0; + } + + public getSubscriptionsCount(): number { + return this.#listeners.size; + } + + public getTokenManager() { + return this.tokenManager; + } + + public getCurrentCredentials(): BasicAuth | null { + const currentToken = this.tokenManager.getCurrentToken(); + return currentToken ? this.#credentialsMapper(currentToken.value) : null; + } + +} + +const DEFAULT_CREDENTIALS_MAPPER = (token: AuthenticationResult): BasicAuth => ({ + username: token.uniqueId, + password: token.accessToken +}); + +const DEFAULT_ERROR_HANDLER = (error: ReAuthenticationError) => + console.error('ReAuthenticationError', error); \ No newline at end of file diff --git a/packages/entraid/lib/index.ts b/packages/entraid/lib/index.ts new file mode 100644 index 00000000000..4873c9935c5 --- /dev/null +++ b/packages/entraid/lib/index.ts @@ -0,0 +1,3 @@ +export * from './entra-id-credentials-provider-factory'; +export * from './entraid-credentials-provider'; +export * from './msal-identity-provider'; \ No newline at end of file diff --git a/packages/entraid/lib/msal-identity-provider.ts b/packages/entraid/lib/msal-identity-provider.ts new file mode 100644 index 00000000000..59b38d18ec6 --- /dev/null +++ b/packages/entraid/lib/msal-identity-provider.ts @@ -0,0 +1,31 @@ +import { + AuthenticationResult +} from '@azure/msal-node'; +import { IdentityProvider, TokenResponse } from '@redis/client/dist/lib/authx'; + +export class MSALIdentityProvider implements IdentityProvider { + private readonly getToken: () => Promise; + + constructor(getToken: () => Promise) { + this.getToken = getToken; + } + + async requestToken(): Promise> { + try { + const result = await this.getToken(); + + if (!result?.accessToken || !result?.expiresOn) { + throw new Error('Invalid token response'); + } + return { + token: result, + ttlMs: result.expiresOn.getTime() - Date.now() + }; + } catch (error) { + throw error; + } + } + +} + + diff --git a/packages/entraid/lib/test-utils.ts b/packages/entraid/lib/test-utils.ts new file mode 100644 index 00000000000..eecec2d4d6d --- /dev/null +++ b/packages/entraid/lib/test-utils.ts @@ -0,0 +1,46 @@ +import { AuthenticationResult } from '@azure/msal-node'; +import { IdentityProvider, StreamingCredentialsProvider, TokenManager, TokenResponse } from '@redis/client/dist/lib/authx'; +import TestUtils from '@redis/test-utils'; +import { EntraidCredentialsProvider } from './entraid-credentials-provider'; + +export const testUtils = new TestUtils({ + dockerImageName: 'redis/redis-stack', + dockerImageVersionArgument: 'redis-version', + defaultDockerVersion: '7.4.0-v1' +}); + +const DEBUG_MODE_ARGS = testUtils.isVersionGreaterThan([7]) ? + ['--enable-debug-command', 'yes'] : + []; + +const idp: IdentityProvider = { + requestToken(): Promise> { + // @ts-ignore + return Promise.resolve({ + ttlMs: 100000, + token: { + accessToken: 'password' + } + }) + } +} + +const tokenManager = new TokenManager(idp, { expirationRefreshRatio: 0.8 }); +const entraIdCredentialsProvider: StreamingCredentialsProvider = new EntraidCredentialsProvider(tokenManager, idp) + +const PASSWORD_WITH_REPLICAS = { + serverArguments: ['--requirepass', 'password', ...DEBUG_MODE_ARGS], + numberOfMasters: 2, + numberOfReplicas: 1, + clusterConfiguration: { + defaults: { + credentialsProvider: entraIdCredentialsProvider + } + } +} + +export const GLOBAL = { + CLUSTERS: { + PASSWORD_WITH_REPLICAS + } +} diff --git a/packages/entraid/package.json b/packages/entraid/package.json new file mode 100644 index 00000000000..571d78c0417 --- /dev/null +++ b/packages/entraid/package.json @@ -0,0 +1,47 @@ +{ + "name": "@redis/entraid", + "version": "5.0.0-next.5", + "license": "MIT", + "main": "./dist/index.js", + "types": "./dist/index.d.ts", + "files": [ + "dist/", + "!dist/tsconfig.tsbuildinfo" + ], + "scripts": { + "clean": "rimraf dist", + "build": "npm run clean && tsc", + "start:auth-pkce": "tsx --tsconfig tsconfig.samples.json ./samples/auth-code-pkce/index.ts", + "test-integration": "mocha -r tsx --tsconfig tsconfig.integration-tests.json './integration-tests/**/*.spec.ts'", + "test": "nyc -r text-summary -r lcov mocha -r tsx './lib/**/*.spec.ts'" + }, + "dependencies": { + "@azure/msal-node": "^2.16.1" + }, + "peerDependencies": { + "@redis/client": "^5.0.0-next.5" + }, + "devDependencies": { + "@types/express": "^4.17.21", + "@types/express-session": "^1.18.0", + "@types/node": "^22.9.0", + "dotenv": "^16.3.1", + "express": "^4.21.1", + "express-session": "^1.18.1", + "@redis/test-utils": "*" + }, + "engines": { + "node": ">= 18" + }, + "repository": { + "type": "git", + "url": "git://github.com/redis/node-redis.git" + }, + "bugs": { + "url": "https://github.com/redis/node-redis/issues" + }, + "homepage": "https://github.com/redis/node-redis/tree/master/packages/entraid", + "keywords": [ + "redis" + ] +} diff --git a/packages/entraid/samples/auth-code-pkce/index.ts b/packages/entraid/samples/auth-code-pkce/index.ts new file mode 100644 index 00000000000..25429269c44 --- /dev/null +++ b/packages/entraid/samples/auth-code-pkce/index.ts @@ -0,0 +1,153 @@ +import express, { Request, Response } from 'express'; +import session from 'express-session'; +import dotenv from 'dotenv'; +import { DEFAULT_TOKEN_MANAGER_CONFIG, EntraIdCredentialsProviderFactory } from '../../lib/entra-id-credentials-provider-factory'; + +dotenv.config(); + +if (!process.env.SESSION_SECRET) { + throw new Error('SESSION_SECRET environment variable must be set'); +} + +interface PKCESession extends session.Session { + pkceCodes?: { + verifier: string; + challenge: string; + challengeMethod: string; + }; +} + +interface AuthRequest extends Request { + session: PKCESession; +} + +const app = express(); + +const sessionConfig = { + secret: process.env.SESSION_SECRET, + resave: false, + saveUninitialized: false, + cookie: { + secure: process.env.NODE_ENV === 'production', // Only use secure in production + httpOnly: true, + sameSite: 'lax', + maxAge: 3600000 // 1 hour + } +} as const; + +app.use(session(sessionConfig)); + +if (!process.env.MSAL_CLIENT_ID || !process.env.MSAL_TENANT_ID) { + throw new Error('MSAL_CLIENT_ID and MSAL_TENANT_ID environment variables must be set'); +} + +// Initialize MSAL provider with authorization code PKCE flow +const { + getPKCECodes, + createCredentialsProvider, + getAuthCodeUrl +} = EntraIdCredentialsProviderFactory.createForAuthorizationCodeWithPKCE({ + clientId: process.env.MSAL_CLIENT_ID, + redirectUri: process.env.REDIRECT_URI || 'http://localhost:3000/redirect', + authorityConfig: { type: 'multi-tenant', tenantId: process.env.MSAL_TENANT_ID }, + tokenManagerConfig: DEFAULT_TOKEN_MANAGER_CONFIG +}); + +app.get('/login', async (req: AuthRequest, res: Response) => { + try { + // Generate PKCE Codes before starting the authorization flow + const pkceCodes = await getPKCECodes(); + + // Store PKCE codes in session + req.session.pkceCodes = pkceCodes + + await new Promise((resolve, reject) => { + req.session.save((err) => { + if (err) reject(err); + else resolve(); + }); + }); + + const authUrl = await getAuthCodeUrl({ + challenge: pkceCodes.challenge, + challengeMethod: pkceCodes.challengeMethod + }); + + res.redirect(authUrl); + } catch (error) { + console.error('Login flow failed:', error); + res.status(500).send('Authentication failed'); + } +}); + +app.get('/redirect', async (req: AuthRequest, res: Response) => { + try { + + // The authorization code is in req.query.code + const { code, client_info } = req.query; + const { pkceCodes } = req.session; + + if (!pkceCodes) { + console.error('Session state:', { + hasSession: !!req.session, + sessionID: req.sessionID, + pkceCodes: req.session.pkceCodes + }); + return res.status(400).send('PKCE codes not found in session'); + } + + // Check both possible error scenarios + if (req.query.error) { + console.error('OAuth error:', req.query.error, req.query.error_description); + return res.status(400).send(`OAuth error: ${req.query.error_description || req.query.error}`); + } + + if (!code) { + console.error('Missing authorization code. Query parameters received:', req.query); + return res.status(400).send('Authorization code not found in request. Query params: ' + JSON.stringify(req.query)); + } + + // Configure with the received code + const entraidCredentialsProvider = createCredentialsProvider( + { + code: code as string, + verifier: pkceCodes.verifier, + clientInfo: client_info as string | undefined + }, + ); + + const initialCredentials = entraidCredentialsProvider.subscribe({ + onNext: (token) => { + console.log('Token acquired:', token); + }, + onError: (error) => { + console.error('Token acquisition failed:', error); + } + }); + + const [credentials] = await initialCredentials; + + console.log('Credentials acquired:', credentials) + + // Clear sensitive data + delete req.session.pkceCodes; + + await new Promise((resolve, reject) => { + req.session.save((err) => { + if (err) reject(err); + else resolve(); + }); + }); + + res.json({ message: 'Authentication successful' }); + } catch (error) { + console.error('Token acquisition failed:', error); + res.status(500).send('Failed to acquire token'); + } +}); + +const PORT = process.env.PORT || 3000; +app.listen(PORT, () => { + console.log(`Server running on port ${PORT}`); + console.log(`Login URL: http://localhost:${PORT}/login`); +}); \ No newline at end of file diff --git a/packages/entraid/tsconfig.integration-tests.json b/packages/entraid/tsconfig.integration-tests.json new file mode 100644 index 00000000000..5d15f4f2753 --- /dev/null +++ b/packages/entraid/tsconfig.integration-tests.json @@ -0,0 +1,10 @@ +{ + "extends": "./tsconfig.json", + "include": [ + "./integration-tests/**/*.ts", + "./lib/**/*.ts" + ], + "compilerOptions": { + "noEmit": true + }, +} \ No newline at end of file diff --git a/packages/entraid/tsconfig.json b/packages/entraid/tsconfig.json new file mode 100644 index 00000000000..414dc1fe755 --- /dev/null +++ b/packages/entraid/tsconfig.json @@ -0,0 +1,20 @@ +{ + "extends": "../../tsconfig.base.json", + "compilerOptions": { + "outDir": "./dist" + }, + "include": [ + "./lib/**/*.ts" + ], + "exclude": [ + "./lib/**/*.spec.ts", + "./lib/test-util.ts", + ], + "typedocOptions": { + "entryPoints": [ + "./lib" + ], + "entryPointStrategy": "expand", + "out": "../../documentation/entraid" + } +} diff --git a/packages/entraid/tsconfig.samples.json b/packages/entraid/tsconfig.samples.json new file mode 100644 index 00000000000..0eb936369ff --- /dev/null +++ b/packages/entraid/tsconfig.samples.json @@ -0,0 +1,10 @@ +{ + "extends": "./tsconfig.json", + "include": [ + "./samples/**/*.ts", + "./lib/**/*.ts" + ], + "compilerOptions": { + "noEmit": true + } +} \ No newline at end of file diff --git a/packages/test-utils/lib/cae-client-testing.ts b/packages/test-utils/lib/cae-client-testing.ts new file mode 100644 index 00000000000..92b846dd37e --- /dev/null +++ b/packages/test-utils/lib/cae-client-testing.ts @@ -0,0 +1,30 @@ +import { readFile } from 'node:fs/promises'; + +interface RawRedisEndpoint { + username?: string; + password?: string; + tls: boolean; + endpoints: string[]; +} + +export type RedisEndpointsConfig = Record; + +export function loadFromJson(jsonString: string): RedisEndpointsConfig { + try { + return JSON.parse(jsonString) as RedisEndpointsConfig; + } catch (error) { + throw new Error(`Invalid JSON configuration: ${error}`); + } +} + +export async function loadFromFile(path: string): Promise { + try { + const configFile = await readFile(path, 'utf-8'); + return loadFromJson(configFile); + } catch (error) { + if (error instanceof Error && 'code' in error && error.code === 'ENOENT') { + throw new Error(`Config file not found at path: ${path}`); + } + throw error; + } +} \ No newline at end of file diff --git a/packages/test-utils/lib/dockers.ts b/packages/test-utils/lib/dockers.ts index a1cb63eb7bf..bfb66603750 100644 --- a/packages/test-utils/lib/dockers.ts +++ b/packages/test-utils/lib/dockers.ts @@ -1,3 +1,4 @@ +import { RedisClusterClientOptions } from '@redis/client/dist/lib/cluster'; import { createConnection } from 'node:net'; import { once } from 'node:events'; import { createClient } from '@redis/client/index'; @@ -102,7 +103,8 @@ async function spawnRedisClusterNodeDockers( dockersConfig: RedisClusterDockersConfig, serverArguments: Array, fromSlot: number, - toSlot: number + toSlot: number, + clientConfig?: Partial ) { const range: Array = []; for (let i = fromSlot; i < toSlot; i++) { @@ -111,7 +113,8 @@ async function spawnRedisClusterNodeDockers( const master = await spawnRedisClusterNodeDocker( dockersConfig, - serverArguments + serverArguments, + clientConfig ); await master.client.clusterAddSlots(range); @@ -127,7 +130,13 @@ async function spawnRedisClusterNodeDockers( 'yes', '--cluster-node-timeout', '5000' - ]).then(async replica => { + ], clientConfig).then(async replica => { + + const requirePassIndex = serverArguments.findIndex((x)=>x==='--requirepass'); + if(requirePassIndex!==-1) { + const password = serverArguments[requirePassIndex+1]; + await replica.client.configSet({'masterauth': password}) + } await replica.client.clusterMeet('127.0.0.1', master.docker.port); while ((await replica.client.clusterSlots()).length === 0) { @@ -151,7 +160,8 @@ async function spawnRedisClusterNodeDockers( async function spawnRedisClusterNodeDocker( dockersConfig: RedisClusterDockersConfig, - serverArguments: Array + serverArguments: Array, + clientConfig?: Partial ) { const docker = await spawnRedisServerDocker(dockersConfig, [ ...serverArguments, @@ -163,7 +173,8 @@ async function spawnRedisClusterNodeDocker( client = createClient({ socket: { port: docker.port - } + }, + ...clientConfig }); await client.connect(); @@ -178,7 +189,8 @@ const SLOTS = 16384; async function spawnRedisClusterDockers( dockersConfig: RedisClusterDockersConfig, - serverArguments: Array + serverArguments: Array, + clientConfig?: Partial ): Promise> { const numberOfMasters = dockersConfig.numberOfMasters ?? 2, slotsPerNode = Math.floor(SLOTS / numberOfMasters), @@ -191,7 +203,8 @@ async function spawnRedisClusterDockers( dockersConfig, serverArguments, fromSlot, - toSlot + toSlot, + clientConfig ) ); } @@ -234,13 +247,18 @@ function totalNodes(slots: any) { const RUNNING_CLUSTERS = new Map, ReturnType>(); -export function spawnRedisCluster(dockersConfig: RedisClusterDockersConfig, serverArguments: Array): Promise> { +export function spawnRedisCluster( + dockersConfig: RedisClusterDockersConfig, + serverArguments: Array, + clientConfig?: Partial): Promise> { + const runningCluster = RUNNING_CLUSTERS.get(serverArguments); if (runningCluster) { return runningCluster; } - const dockersPromise = spawnRedisClusterDockers(dockersConfig, serverArguments); + const dockersPromise = spawnRedisClusterDockers(dockersConfig, serverArguments,clientConfig); + RUNNING_CLUSTERS.set(serverArguments, dockersPromise); return dockersPromise; } diff --git a/packages/test-utils/lib/index.ts b/packages/test-utils/lib/index.ts index 87ba34db7ef..9dee350e31e 100644 --- a/packages/test-utils/lib/index.ts +++ b/packages/test-utils/lib/index.ts @@ -290,7 +290,8 @@ export default class TestUtils { ...dockerImage, numberOfMasters: options.numberOfMasters, numberOfReplicas: options.numberOfReplicas - }, options.serverArguments); + }, options.serverArguments, + options.clusterConfiguration?.defaults); return dockersPromise; }); } diff --git a/tsconfig.json b/tsconfig.json index a578fefa54f..8f43ab41d22 100644 --- a/tsconfig.json +++ b/tsconfig.json @@ -1,20 +1,32 @@ { "files": [], - "references": [{ - "path": "./packages/client" - }, { - "path": "./packages/test-utils" - }, { - "path": "./packages/bloom" - }, { - "path": "./packages/graph" - }, { - "path": "./packages/json" - }, { - "path": "./packages/search" - }, { - "path": "./packages/time-series" - }, { - "path": "./packages/redis" - }] + "references": [ + { + "path": "./packages/client" + }, + { + "path": "./packages/test-utils" + }, + { + "path": "./packages/bloom" + }, + { + "path": "./packages/graph" + }, + { + "path": "./packages/json" + }, + { + "path": "./packages/search" + }, + { + "path": "./packages/time-series" + }, + { + "path": "./packages/entraid" + }, + { + "path": "./packages/redis" + } + ] } From 6066692cb936039ae0dfc19c28ee22a1e2ebfbed Mon Sep 17 00:00:00 2001 From: dmaier-redislabs Date: Thu, 30 Jan 2025 11:02:54 +0200 Subject: [PATCH 1487/1748] Release client@5.0.0-next.6 --- packages/client/package.json | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/packages/client/package.json b/packages/client/package.json index 9d028aa2bb2..cc892a12a49 100644 --- a/packages/client/package.json +++ b/packages/client/package.json @@ -1,6 +1,6 @@ { "name": "@redis/client", - "version": "5.0.0-next.5", + "version": "5.0.0-next.6", "license": "MIT", "main": "./dist/index.js", "types": "./dist/index.d.ts", From c9215d18bf857f9ebaa0b2dc3d52d5591b4e52ea Mon Sep 17 00:00:00 2001 From: dmaier-redislabs Date: Thu, 30 Jan 2025 11:07:40 +0200 Subject: [PATCH 1488/1748] Updated the EntraID package to use client@5.0.0-next.6 --- packages/entraid/package.json | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/packages/entraid/package.json b/packages/entraid/package.json index 571d78c0417..8041e9bbe29 100644 --- a/packages/entraid/package.json +++ b/packages/entraid/package.json @@ -19,7 +19,7 @@ "@azure/msal-node": "^2.16.1" }, "peerDependencies": { - "@redis/client": "^5.0.0-next.5" + "@redis/client": "^5.0.0-next.6" }, "devDependencies": { "@types/express": "^4.17.21", From 998b7f0ebd04496963ca844344b0a7f3320dc3d4 Mon Sep 17 00:00:00 2001 From: dmaier-redislabs Date: Thu, 30 Jan 2025 11:09:31 +0200 Subject: [PATCH 1489/1748] Release entraid@5.0.0-next.6 --- packages/entraid/package.json | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/packages/entraid/package.json b/packages/entraid/package.json index 8041e9bbe29..da0d7df9935 100644 --- a/packages/entraid/package.json +++ b/packages/entraid/package.json @@ -1,6 +1,6 @@ { "name": "@redis/entraid", - "version": "5.0.0-next.5", + "version": "5.0.0-next.6", "license": "MIT", "main": "./dist/index.js", "types": "./dist/index.d.ts", From 68e8973fd02cd55d6c537415fbe401dc49783ad9 Mon Sep 17 00:00:00 2001 From: dmaier-redislabs Date: Thu, 30 Jan 2025 11:11:21 +0200 Subject: [PATCH 1490/1748] Updated the JSON package to use client@5.0.0-next.6 --- packages/json/package.json | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/packages/json/package.json b/packages/json/package.json index 6cc43916497..b59f65ebc80 100644 --- a/packages/json/package.json +++ b/packages/json/package.json @@ -12,7 +12,7 @@ "test": "nyc -r text-summary -r lcov mocha -r tsx './lib/**/*.spec.ts'" }, "peerDependencies": { - "@redis/client": "^5.0.0-next.5" + "@redis/client": "^5.0.0-next.6" }, "devDependencies": { "@redis/test-utils": "*" From c0da3db8cfcc0cfb847974e1c3e22244fee54431 Mon Sep 17 00:00:00 2001 From: dmaier-redislabs Date: Thu, 30 Jan 2025 11:11:58 +0200 Subject: [PATCH 1491/1748] Release json@5.0.0-next.6 --- packages/json/package.json | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/packages/json/package.json b/packages/json/package.json index b59f65ebc80..b7a972c8c7a 100644 --- a/packages/json/package.json +++ b/packages/json/package.json @@ -1,6 +1,6 @@ { "name": "@redis/json", - "version": "5.0.0-next.5", + "version": "5.0.0-next.6", "license": "MIT", "main": "./dist/lib/index.js", "types": "./dist/lib/index.d.ts", From 153346b9aa3497e07be3848b1462c418ad6e8108 Mon Sep 17 00:00:00 2001 From: dmaier-redislabs Date: Thu, 30 Jan 2025 11:12:56 +0200 Subject: [PATCH 1492/1748] Updated the search package to use client@5.0.0-next.6 --- packages/search/package.json | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/packages/search/package.json b/packages/search/package.json index c5b75110f0f..f139c4ea8ba 100644 --- a/packages/search/package.json +++ b/packages/search/package.json @@ -12,7 +12,7 @@ "test": "nyc -r text-summary -r lcov mocha -r tsx './lib/**/*.spec.ts'" }, "peerDependencies": { - "@redis/client": "^5.0.0-next.5" + "@redis/client": "^5.0.0-next.6" }, "devDependencies": { "@redis/test-utils": "*" From f30926c91a3b08dc9f5a11fc08f2437794f0ba4f Mon Sep 17 00:00:00 2001 From: dmaier-redislabs Date: Thu, 30 Jan 2025 11:13:34 +0200 Subject: [PATCH 1493/1748] Release search@5.0.0-next.6 --- packages/search/package.json | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/packages/search/package.json b/packages/search/package.json index f139c4ea8ba..26cbbaf5ad9 100644 --- a/packages/search/package.json +++ b/packages/search/package.json @@ -1,6 +1,6 @@ { "name": "@redis/search", - "version": "5.0.0-next.5", + "version": "5.0.0-next.6", "license": "MIT", "main": "./dist/lib/index.js", "types": "./dist/lib/index.d.ts", From 81a40a7020e7ba5829b32d8f122f03f19315fe59 Mon Sep 17 00:00:00 2001 From: dmaier-redislabs Date: Thu, 30 Jan 2025 11:14:30 +0200 Subject: [PATCH 1494/1748] Updated the timeseries package to use client@5.0.0-next.6 --- packages/time-series/package.json | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/packages/time-series/package.json b/packages/time-series/package.json index 1e41ee237c3..819b6fdb6cd 100644 --- a/packages/time-series/package.json +++ b/packages/time-series/package.json @@ -12,7 +12,7 @@ "test": "nyc -r text-summary -r lcov mocha -r tsx './lib/**/*.spec.ts'" }, "peerDependencies": { - "@redis/client": "^5.0.0-next.5" + "@redis/client": "^5.0.0-next.6" }, "devDependencies": { "@redis/test-utils": "*" From a424058d374bf77ccc1807a246e2f40c77fac00b Mon Sep 17 00:00:00 2001 From: dmaier-redislabs Date: Thu, 30 Jan 2025 11:15:11 +0200 Subject: [PATCH 1495/1748] Release time-series@5.0.0-next.6 --- packages/time-series/package.json | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/packages/time-series/package.json b/packages/time-series/package.json index 819b6fdb6cd..22cfe8cefba 100644 --- a/packages/time-series/package.json +++ b/packages/time-series/package.json @@ -1,6 +1,6 @@ { "name": "@redis/time-series", - "version": "5.0.0-next.5", + "version": "5.0.0-next.6", "license": "MIT", "main": "./dist/lib/index.js", "types": "./dist/lib/index.d.ts", From aecd625901a8691401d07d548629e574f958f9a9 Mon Sep 17 00:00:00 2001 From: dmaier-redislabs Date: Thu, 30 Jan 2025 11:16:57 +0200 Subject: [PATCH 1496/1748] Updated the bloom package to use client@5.0.0-next.6 --- packages/bloom/package.json | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/packages/bloom/package.json b/packages/bloom/package.json index cbcbc8ce2bd..a571451b8e3 100644 --- a/packages/bloom/package.json +++ b/packages/bloom/package.json @@ -12,7 +12,7 @@ "test": "nyc -r text-summary -r lcov mocha -r tsx './lib/**/*.spec.ts'" }, "peerDependencies": { - "@redis/client": "^5.0.0-next.5" + "@redis/client": "^5.0.0-next.6" }, "devDependencies": { "@redis/test-utils": "*" From 0a748590a0abd0888da8a88064d85773586fbae1 Mon Sep 17 00:00:00 2001 From: dmaier-redislabs Date: Thu, 30 Jan 2025 11:17:29 +0200 Subject: [PATCH 1497/1748] Release bloom@5.0.0-next.6 --- packages/bloom/package.json | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/packages/bloom/package.json b/packages/bloom/package.json index a571451b8e3..434ba809f7c 100644 --- a/packages/bloom/package.json +++ b/packages/bloom/package.json @@ -1,6 +1,6 @@ { "name": "@redis/bloom", - "version": "5.0.0-next.5", + "version": "5.0.0-next.6", "license": "MIT", "main": "./dist/lib/index.js", "types": "./dist/lib/index.d.ts", From 81f5fc43b8ebdb79cfa49dd717bc00cd1f5d91ef Mon Sep 17 00:00:00 2001 From: dmaier-redislabs Date: Thu, 30 Jan 2025 11:18:54 +0200 Subject: [PATCH 1498/1748] Updated the graph package to use client@5.0.0-next.6 --- packages/graph/package.json | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/packages/graph/package.json b/packages/graph/package.json index c62abb23892..1604826b054 100644 --- a/packages/graph/package.json +++ b/packages/graph/package.json @@ -12,7 +12,7 @@ "test-disable": "nyc -r text-summary -r lcov mocha -r tsx './lib/**/*.spec.ts'" }, "peerDependencies": { - "@redis/client": "^5.0.0-next.5" + "@redis/client": "^5.0.0-next.6" }, "devDependencies": { "@redis/test-utils": "*" From 0f7e0f45ae4ae0fdc2477321e46bd9398ac28308 Mon Sep 17 00:00:00 2001 From: dmaier-redislabs Date: Thu, 30 Jan 2025 11:19:25 +0200 Subject: [PATCH 1499/1748] Release graph@5.0.0-next.6 --- packages/graph/package.json | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/packages/graph/package.json b/packages/graph/package.json index 1604826b054..1ea08401f1b 100644 --- a/packages/graph/package.json +++ b/packages/graph/package.json @@ -1,6 +1,6 @@ { "name": "@redis/graph", - "version": "5.0.0-next.5", + "version": "5.0.0-next.6", "license": "MIT", "main": "./dist/lib/index.js", "types": "./dist/lib/index.d.ts", From 70f3698134daf71e431b1fc2a4c25c692c11f1e7 Mon Sep 17 00:00:00 2001 From: dmaier-redislabs Date: Thu, 30 Jan 2025 11:21:24 +0200 Subject: [PATCH 1500/1748] Updated the Redis package to use client@5.0.0-next.6 --- packages/redis/package.json | 12 ++++++------ 1 file changed, 6 insertions(+), 6 deletions(-) diff --git a/packages/redis/package.json b/packages/redis/package.json index 617a5bff357..e99a229b11c 100644 --- a/packages/redis/package.json +++ b/packages/redis/package.json @@ -10,12 +10,12 @@ "!dist/tsconfig.tsbuildinfo" ], "dependencies": { - "@redis/bloom": "5.0.0-next.5", - "@redis/client": "5.0.0-next.5", - "@redis/graph": "5.0.0-next.5", - "@redis/json": "5.0.0-next.5", - "@redis/search": "5.0.0-next.5", - "@redis/time-series": "5.0.0-next.5" + "@redis/bloom": "5.0.0-next.6", + "@redis/client": "5.0.0-next.6", + "@redis/graph": "5.0.0-next.6", + "@redis/json": "5.0.0-next.6", + "@redis/search": "5.0.0-next.6", + "@redis/time-series": "5.0.0-next.6" }, "engines": { "node": ">= 18" From aa4ea3317f6b6eb77e9f7733931a9a3cab39b3bd Mon Sep 17 00:00:00 2001 From: dmaier-redislabs Date: Thu, 30 Jan 2025 11:26:52 +0200 Subject: [PATCH 1501/1748] Release redis@5.0.0-next.6 --- packages/redis/package.json | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/packages/redis/package.json b/packages/redis/package.json index e99a229b11c..5069d936ba8 100644 --- a/packages/redis/package.json +++ b/packages/redis/package.json @@ -1,7 +1,7 @@ { "name": "redis", "description": "A modern, high performance Redis client", - "version": "5.0.0-next.5", + "version": "5.0.0-next.6", "license": "MIT", "main": "./dist/index.js", "types": "./dist/index.d.ts", From 558ebb4d25f2cbeb707cb601d4bd11014a6a6992 Mon Sep 17 00:00:00 2001 From: "Bobby I." Date: Thu, 30 Jan 2025 12:46:41 +0200 Subject: [PATCH 1502/1748] Update README.md (#2890) --- packages/entraid/README.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/packages/entraid/README.md b/packages/entraid/README.md index e9c7956022e..131935fe619 100644 --- a/packages/entraid/README.md +++ b/packages/entraid/README.md @@ -36,7 +36,7 @@ The first step to using @redis/entraid is choosing the right credentials provide ```typescript import { createClient } from '@redis/client'; -import { EntraIdCredentialsProviderFactory } from '@redis/entraid'; +import { EntraIdCredentialsProviderFactory } from '@redis/entraid/dist/lib/entra-id-credentials-provider-factory'; const provider = EntraIdCredentialsProviderFactory.createForClientCredentials({ clientId: 'your-client-id', From 1af01373dbeac4bca700158a13a22627f6ef05f2 Mon Sep 17 00:00:00 2001 From: Hristo Temelski Date: Mon, 17 Feb 2025 13:47:12 +0200 Subject: [PATCH 1503/1748] feat(search): Set default dialect to 2 for Redis Search commands (#2895) - The default dialect `DEFAULT_DIALECT` is now set to '2' - Automatically append DIALECT parameter to search commands when not explicitly specified --- .../search/lib/commands/AGGREGATE.spec.ts | 69 ++++++++++--------- packages/search/lib/commands/AGGREGATE.ts | 9 ++- .../lib/commands/AGGREGATE_WITHCURSOR.spec.ts | 7 +- packages/search/lib/commands/EXPLAIN.spec.ts | 5 +- packages/search/lib/commands/EXPLAIN.ts | 3 + .../search/lib/commands/EXPLAINCLI.spec.ts | 10 ++- packages/search/lib/commands/EXPLAINCLI.ts | 18 ++++- .../lib/commands/PROFILE_AGGREGATE.spec.ts | 5 +- .../lib/commands/PROFILE_SEARCH.spec.ts | 6 +- packages/search/lib/commands/SEARCH.spec.ts | 52 +++++++------- packages/search/lib/commands/SEARCH.ts | 5 +- .../lib/commands/SEARCH_NOCONTENT.spec.ts | 3 +- .../search/lib/commands/SEARCH_NOCONTENT.ts | 2 +- .../search/lib/commands/SPELLCHECK.spec.ts | 9 +-- packages/search/lib/commands/SPELLCHECK.ts | 3 + packages/search/lib/dialect/default.ts | 1 + 16 files changed, 126 insertions(+), 81 deletions(-) create mode 100644 packages/search/lib/dialect/default.ts diff --git a/packages/search/lib/commands/AGGREGATE.spec.ts b/packages/search/lib/commands/AGGREGATE.spec.ts index 787fbd1472f..420911c5600 100644 --- a/packages/search/lib/commands/AGGREGATE.spec.ts +++ b/packages/search/lib/commands/AGGREGATE.spec.ts @@ -2,13 +2,14 @@ import { strict as assert } from 'node:assert'; import testUtils, { GLOBAL } from '../test-utils'; import AGGREGATE from './AGGREGATE'; import { parseArgs } from '@redis/client/lib/commands/generic-transformers'; +import { DEFAULT_DIALECT } from '../dialect/default'; -describe('AGGREGATE', () => { +describe('AGGREGATE', () => { describe('transformArguments', () => { it('without options', () => { assert.deepEqual( parseArgs(AGGREGATE, 'index', '*'), - ['FT.AGGREGATE', 'index', '*'] + ['FT.AGGREGATE', 'index', '*', 'DIALECT', DEFAULT_DIALECT] ); }); @@ -17,14 +18,14 @@ describe('AGGREGATE', () => { parseArgs(AGGREGATE, 'index', '*', { VERBATIM: true }), - ['FT.AGGREGATE', 'index', '*', 'VERBATIM'] + ['FT.AGGREGATE', 'index', '*', 'VERBATIM', 'DIALECT', DEFAULT_DIALECT] ); }); it('with ADDSCORES', () => { assert.deepEqual( parseArgs(AGGREGATE, 'index', '*', { ADDSCORES: true }), - ['FT.AGGREGATE', 'index', '*', 'ADDSCORES'] + ['FT.AGGREGATE', 'index', '*', 'ADDSCORES', 'DIALECT', DEFAULT_DIALECT] ); }); @@ -36,7 +37,7 @@ describe('AGGREGATE', () => { parseArgs(AGGREGATE, 'index', '*', { LOAD: '@property' }), - ['FT.AGGREGATE', 'index', '*', 'LOAD', '1', '@property'] + ['FT.AGGREGATE', 'index', '*', 'LOAD', '1', '@property', 'DIALECT', DEFAULT_DIALECT] ); }); @@ -47,7 +48,7 @@ describe('AGGREGATE', () => { identifier: '@property' } }), - ['FT.AGGREGATE', 'index', '*', 'LOAD', '1', '@property'] + ['FT.AGGREGATE', 'index', '*', 'LOAD', '1', '@property', 'DIALECT', DEFAULT_DIALECT] ); }); }); @@ -60,7 +61,7 @@ describe('AGGREGATE', () => { AS: 'alias' } }), - ['FT.AGGREGATE', 'index', '*', 'LOAD', '3', '@property', 'AS', 'alias'] + ['FT.AGGREGATE', 'index', '*', 'LOAD', '3', '@property', 'AS', 'alias', 'DIALECT', DEFAULT_DIALECT] ); }); }); @@ -70,7 +71,7 @@ describe('AGGREGATE', () => { parseArgs(AGGREGATE, 'index', '*', { LOAD: ['@1', '@2'] }), - ['FT.AGGREGATE', 'index', '*', 'LOAD', '2', '@1', '@2'] + ['FT.AGGREGATE', 'index', '*', 'LOAD', '2', '@1', '@2', 'DIALECT', DEFAULT_DIALECT] ); }); }); @@ -89,7 +90,7 @@ describe('AGGREGATE', () => { } }] }), - ['FT.AGGREGATE', 'index', '*', 'GROUPBY', '0', 'REDUCE', 'COUNT', '0'] + ['FT.AGGREGATE', 'index', '*', 'GROUPBY', '0', 'REDUCE', 'COUNT', '0', 'DIALECT', DEFAULT_DIALECT] ); }); @@ -104,7 +105,7 @@ describe('AGGREGATE', () => { } }] }), - ['FT.AGGREGATE', 'index', '*', 'GROUPBY', '0', 'REDUCE', 'COUNT', '0', 'AS', 'count'] + ['FT.AGGREGATE', 'index', '*', 'GROUPBY', '0', 'REDUCE', 'COUNT', '0', 'AS', 'count', 'DIALECT', DEFAULT_DIALECT] ); }); }); @@ -121,7 +122,7 @@ describe('AGGREGATE', () => { } }] }), - ['FT.AGGREGATE', 'index', '*', 'GROUPBY', '1', '@property', 'REDUCE', 'COUNT', '0'] + ['FT.AGGREGATE', 'index', '*', 'GROUPBY', '1', '@property', 'REDUCE', 'COUNT', '0', 'DIALECT', DEFAULT_DIALECT] ); }); @@ -136,7 +137,7 @@ describe('AGGREGATE', () => { } }] }), - ['FT.AGGREGATE', 'index', '*', 'GROUPBY', '2', '@1', '@2', 'REDUCE', 'COUNT', '0'] + ['FT.AGGREGATE', 'index', '*', 'GROUPBY', '2', '@1', '@2', 'REDUCE', 'COUNT', '0', 'DIALECT', DEFAULT_DIALECT] ); }); }); @@ -153,7 +154,7 @@ describe('AGGREGATE', () => { } }] }), - ['FT.AGGREGATE', 'index', '*', 'GROUPBY', '0', 'REDUCE', 'COUNT_DISTINCT', '1', '@property'] + ['FT.AGGREGATE', 'index', '*', 'GROUPBY', '0', 'REDUCE', 'COUNT_DISTINCT', '1', '@property', 'DIALECT', DEFAULT_DIALECT] ); }); @@ -168,7 +169,7 @@ describe('AGGREGATE', () => { } }] }), - ['FT.AGGREGATE', 'index', '*', 'GROUPBY', '0', 'REDUCE', 'COUNT_DISTINCTISH', '1', '@property'] + ['FT.AGGREGATE', 'index', '*', 'GROUPBY', '0', 'REDUCE', 'COUNT_DISTINCTISH', '1', '@property', 'DIALECT', DEFAULT_DIALECT] ); }); @@ -183,7 +184,7 @@ describe('AGGREGATE', () => { } }] }), - ['FT.AGGREGATE', 'index', '*', 'GROUPBY', '0', 'REDUCE', 'SUM', '1', '@property'] + ['FT.AGGREGATE', 'index', '*', 'GROUPBY', '0', 'REDUCE', 'SUM', '1', '@property', 'DIALECT', DEFAULT_DIALECT] ); }); @@ -198,7 +199,7 @@ describe('AGGREGATE', () => { } }] }), - ['FT.AGGREGATE', 'index', '*', 'GROUPBY', '0', 'REDUCE', 'MIN', '1', '@property'] + ['FT.AGGREGATE', 'index', '*', 'GROUPBY', '0', 'REDUCE', 'MIN', '1', '@property', 'DIALECT', DEFAULT_DIALECT] ); }); @@ -213,7 +214,7 @@ describe('AGGREGATE', () => { } }] }), - ['FT.AGGREGATE', 'index', '*', 'GROUPBY', '0', 'REDUCE', 'MAX', '1', '@property'] + ['FT.AGGREGATE', 'index', '*', 'GROUPBY', '0', 'REDUCE', 'MAX', '1', '@property', 'DIALECT', DEFAULT_DIALECT] ); }); @@ -228,7 +229,7 @@ describe('AGGREGATE', () => { } }] }), - ['FT.AGGREGATE', 'index', '*', 'GROUPBY', '0', 'REDUCE', 'AVG', '1', '@property'] + ['FT.AGGREGATE', 'index', '*', 'GROUPBY', '0', 'REDUCE', 'AVG', '1', '@property', 'DIALECT', DEFAULT_DIALECT] ); }); it('STDDEV', () => { @@ -242,7 +243,7 @@ describe('AGGREGATE', () => { } }] }), - ['FT.AGGREGATE', 'index', '*', 'GROUPBY', '0', 'REDUCE', 'STDDEV', '1', '@property'] + ['FT.AGGREGATE', 'index', '*', 'GROUPBY', '0', 'REDUCE', 'STDDEV', '1', '@property', 'DIALECT', DEFAULT_DIALECT] ); }); @@ -258,7 +259,7 @@ describe('AGGREGATE', () => { } }] }), - ['FT.AGGREGATE', 'index', '*', 'GROUPBY', '0', 'REDUCE', 'QUANTILE', '2', '@property', '0.5'] + ['FT.AGGREGATE', 'index', '*', 'GROUPBY', '0', 'REDUCE', 'QUANTILE', '2', '@property', '0.5', 'DIALECT', DEFAULT_DIALECT] ); }); @@ -273,7 +274,7 @@ describe('AGGREGATE', () => { } }] }), - ['FT.AGGREGATE', 'index', '*', 'GROUPBY', '0', 'REDUCE', 'TOLIST', '1', '@property'] + ['FT.AGGREGATE', 'index', '*', 'GROUPBY', '0', 'REDUCE', 'TOLIST', '1', '@property', 'DIALECT', DEFAULT_DIALECT] ); }); @@ -289,7 +290,7 @@ describe('AGGREGATE', () => { } }] }), - ['FT.AGGREGATE', 'index', '*', 'GROUPBY', '0', 'REDUCE', 'FIRST_VALUE', '1', '@property'] + ['FT.AGGREGATE', 'index', '*', 'GROUPBY', '0', 'REDUCE', 'FIRST_VALUE', '1', '@property', 'DIALECT', DEFAULT_DIALECT] ); }); @@ -307,7 +308,7 @@ describe('AGGREGATE', () => { } }] }), - ['FT.AGGREGATE', 'index', '*', 'GROUPBY', '0', 'REDUCE', 'FIRST_VALUE', '3', '@property', 'BY', '@by'] + ['FT.AGGREGATE', 'index', '*', 'GROUPBY', '0', 'REDUCE', 'FIRST_VALUE', '3', '@property', 'BY', '@by', 'DIALECT', DEFAULT_DIALECT] ); }); @@ -326,7 +327,7 @@ describe('AGGREGATE', () => { } }] }), - ['FT.AGGREGATE', 'index', '*', 'GROUPBY', '0', 'REDUCE', 'FIRST_VALUE', '3', '@property', 'BY', '@by'] + ['FT.AGGREGATE', 'index', '*', 'GROUPBY', '0', 'REDUCE', 'FIRST_VALUE', '3', '@property', 'BY', '@by', 'DIALECT', DEFAULT_DIALECT] ); }); }); @@ -346,7 +347,7 @@ describe('AGGREGATE', () => { } }] }), - ['FT.AGGREGATE', 'index', '*', 'GROUPBY', '0', 'REDUCE', 'FIRST_VALUE', '4', '@property', 'BY', '@by', 'ASC'] + ['FT.AGGREGATE', 'index', '*', 'GROUPBY', '0', 'REDUCE', 'FIRST_VALUE', '4', '@property', 'BY', '@by', 'ASC', 'DIALECT', DEFAULT_DIALECT] ); }); }); @@ -364,7 +365,7 @@ describe('AGGREGATE', () => { } }] }), - ['FT.AGGREGATE', 'index', '*', 'GROUPBY', '0', 'REDUCE', 'RANDOM_SAMPLE', '2', '@property', '1'] + ['FT.AGGREGATE', 'index', '*', 'GROUPBY', '0', 'REDUCE', 'RANDOM_SAMPLE', '2', '@property', '1', 'DIALECT', DEFAULT_DIALECT] ); }); }); @@ -378,7 +379,7 @@ describe('AGGREGATE', () => { BY: '@by' }] }), - ['FT.AGGREGATE', 'index', '*', 'SORTBY', '1', '@by'] + ['FT.AGGREGATE', 'index', '*', 'SORTBY', '1', '@by', 'DIALECT', DEFAULT_DIALECT] ); }); @@ -390,7 +391,7 @@ describe('AGGREGATE', () => { BY: ['@1', '@2'] }] }), - ['FT.AGGREGATE', 'index', '*', 'SORTBY', '2', '@1', '@2'] + ['FT.AGGREGATE', 'index', '*', 'SORTBY', '2', '@1', '@2', 'DIALECT', DEFAULT_DIALECT] ); }); @@ -403,7 +404,7 @@ describe('AGGREGATE', () => { MAX: 1 }] }), - ['FT.AGGREGATE', 'index', '*', 'SORTBY', '3', '@by', 'MAX', '1'] + ['FT.AGGREGATE', 'index', '*', 'SORTBY', '3', '@by', 'MAX', '1', 'DIALECT', DEFAULT_DIALECT] ); }); }); @@ -417,7 +418,7 @@ describe('AGGREGATE', () => { AS: 'as' }] }), - ['FT.AGGREGATE', 'index', '*', 'APPLY', '@field + 1', 'AS', 'as'] + ['FT.AGGREGATE', 'index', '*', 'APPLY', '@field + 1', 'AS', 'as', 'DIALECT', DEFAULT_DIALECT] ); }); @@ -430,7 +431,7 @@ describe('AGGREGATE', () => { size: 1 }] }), - ['FT.AGGREGATE', 'index', '*', 'LIMIT', '0', '1'] + ['FT.AGGREGATE', 'index', '*', 'LIMIT', '0', '1', 'DIALECT', DEFAULT_DIALECT] ); }); @@ -442,7 +443,7 @@ describe('AGGREGATE', () => { expression: '@field != ""' }] }), - ['FT.AGGREGATE', 'index', '*', 'FILTER', '@field != ""'] + ['FT.AGGREGATE', 'index', '*', 'FILTER', '@field != ""', 'DIALECT', DEFAULT_DIALECT] ); }); }); @@ -454,7 +455,7 @@ describe('AGGREGATE', () => { param: 'value' } }), - ['FT.AGGREGATE', 'index', '*', 'PARAMS', '2', 'param', 'value'] + ['FT.AGGREGATE', 'index', '*', 'PARAMS', '2', 'param', 'value', 'DIALECT', DEFAULT_DIALECT] ); }); @@ -470,7 +471,7 @@ describe('AGGREGATE', () => { it('with TIMEOUT', () => { assert.deepEqual( parseArgs(AGGREGATE, 'index', '*', { TIMEOUT: 10 }), - ['FT.AGGREGATE', 'index', '*', 'TIMEOUT', '10'] + ['FT.AGGREGATE', 'index', '*', 'TIMEOUT', '10', 'DIALECT', DEFAULT_DIALECT] ); }); }); diff --git a/packages/search/lib/commands/AGGREGATE.ts b/packages/search/lib/commands/AGGREGATE.ts index 925a91da008..b2589a52a54 100644 --- a/packages/search/lib/commands/AGGREGATE.ts +++ b/packages/search/lib/commands/AGGREGATE.ts @@ -3,6 +3,7 @@ import { ArrayReply, BlobStringReply, Command, MapReply, NumberReply, RedisArgum import { RediSearchProperty } from './CREATE'; import { FtSearchParams, parseParamsArgument } from './SEARCH'; import { transformTuplesReply } from '@redis/client/dist/lib/commands/generic-transformers'; +import { DEFAULT_DIALECT } from '../dialect/default'; type LoadField = RediSearchProperty | { identifier: RediSearchProperty; @@ -12,12 +13,12 @@ type LoadField = RediSearchProperty | { export const FT_AGGREGATE_STEPS = { GROUPBY: 'GROUPBY', SORTBY: 'SORTBY', - APPLY: 'APPLY', + APPLY: 'APPLY', LIMIT: 'LIMIT', FILTER: 'FILTER' } as const; -type FT_AGGREGATE_STEPS = typeof FT_AGGREGATE_STEPS; +type FT_AGGREGATE_STEPS = typeof FT_AGGREGATE_STEPS; export type FtAggregateStep = FT_AGGREGATE_STEPS[keyof FT_AGGREGATE_STEPS]; @@ -249,8 +250,10 @@ export function parseAggregateOptions(parser: CommandParser , options?: FtAggreg parseParamsArgument(parser, options?.PARAMS); - if (options?.DIALECT !== undefined) { + if (options?.DIALECT) { parser.push('DIALECT', options.DIALECT.toString()); + } else { + parser.push('DIALECT', DEFAULT_DIALECT); } } diff --git a/packages/search/lib/commands/AGGREGATE_WITHCURSOR.spec.ts b/packages/search/lib/commands/AGGREGATE_WITHCURSOR.spec.ts index 57f46d1e32c..0e89346c49f 100644 --- a/packages/search/lib/commands/AGGREGATE_WITHCURSOR.spec.ts +++ b/packages/search/lib/commands/AGGREGATE_WITHCURSOR.spec.ts @@ -2,13 +2,14 @@ import { strict as assert } from 'node:assert'; import testUtils, { GLOBAL } from '../test-utils'; import AGGREGATE_WITHCURSOR from './AGGREGATE_WITHCURSOR'; import { parseArgs } from '@redis/client/lib/commands/generic-transformers'; +import { DEFAULT_DIALECT } from '../dialect/default'; describe('AGGREGATE WITHCURSOR', () => { describe('transformArguments', () => { it('without options', () => { assert.deepEqual( parseArgs(AGGREGATE_WITHCURSOR, 'index', '*'), - ['FT.AGGREGATE', 'index', '*', 'WITHCURSOR'] + ['FT.AGGREGATE', 'index', '*', 'DIALECT', DEFAULT_DIALECT, 'WITHCURSOR'] ); }); @@ -17,7 +18,7 @@ describe('AGGREGATE WITHCURSOR', () => { parseArgs(AGGREGATE_WITHCURSOR, 'index', '*', { COUNT: 1 }), - ['FT.AGGREGATE', 'index', '*', 'WITHCURSOR', 'COUNT', '1'] + ['FT.AGGREGATE', 'index', '*', 'DIALECT', DEFAULT_DIALECT, 'WITHCURSOR', 'COUNT', '1'] ); }); @@ -26,7 +27,7 @@ describe('AGGREGATE WITHCURSOR', () => { parseArgs(AGGREGATE_WITHCURSOR, 'index', '*', { MAXIDLE: 1 }), - ['FT.AGGREGATE', 'index', '*', 'WITHCURSOR', 'MAXIDLE', '1'] + ['FT.AGGREGATE', 'index', '*', 'DIALECT', DEFAULT_DIALECT, 'WITHCURSOR', 'MAXIDLE', '1'] ); }); }); diff --git a/packages/search/lib/commands/EXPLAIN.spec.ts b/packages/search/lib/commands/EXPLAIN.spec.ts index ddc551fbd71..d1691bc7c25 100644 --- a/packages/search/lib/commands/EXPLAIN.spec.ts +++ b/packages/search/lib/commands/EXPLAIN.spec.ts @@ -3,13 +3,14 @@ import EXPLAIN from './EXPLAIN'; import { parseArgs } from '@redis/client/lib/commands/generic-transformers'; import testUtils, { GLOBAL } from '../test-utils'; import { SCHEMA_FIELD_TYPE } from './CREATE'; +import { DEFAULT_DIALECT } from '../dialect/default'; describe('EXPLAIN', () => { describe('transformArguments', () => { it('simple', () => { assert.deepEqual( parseArgs(EXPLAIN, 'index', '*'), - ['FT.EXPLAIN', 'index', '*'] + ['FT.EXPLAIN', 'index', '*', 'DIALECT', DEFAULT_DIALECT] ); }); @@ -20,7 +21,7 @@ describe('EXPLAIN', () => { param: 'value' } }), - ['FT.EXPLAIN', 'index', '*', 'PARAMS', '2', 'param', 'value'] + ['FT.EXPLAIN', 'index', '*', 'PARAMS', '2', 'param', 'value', 'DIALECT', DEFAULT_DIALECT] ); }); diff --git a/packages/search/lib/commands/EXPLAIN.ts b/packages/search/lib/commands/EXPLAIN.ts index dcd7c3c0d2d..39a430f4371 100644 --- a/packages/search/lib/commands/EXPLAIN.ts +++ b/packages/search/lib/commands/EXPLAIN.ts @@ -1,6 +1,7 @@ import { CommandParser } from '@redis/client/dist/lib/client/parser'; import { RedisArgument, SimpleStringReply, Command } from '@redis/client/dist/lib/RESP/types'; import { FtSearchParams, parseParamsArgument } from './SEARCH'; +import { DEFAULT_DIALECT } from '../dialect/default'; export interface FtExplainOptions { PARAMS?: FtSearchParams; @@ -22,6 +23,8 @@ export default { if (options?.DIALECT) { parser.push('DIALECT', options.DIALECT.toString()); + } else { + parser.push('DIALECT', DEFAULT_DIALECT); } }, transformReply: undefined as unknown as () => SimpleStringReply diff --git a/packages/search/lib/commands/EXPLAINCLI.spec.ts b/packages/search/lib/commands/EXPLAINCLI.spec.ts index cf46a1740c5..1812b674094 100644 --- a/packages/search/lib/commands/EXPLAINCLI.spec.ts +++ b/packages/search/lib/commands/EXPLAINCLI.spec.ts @@ -1,12 +1,20 @@ import { strict as assert } from 'node:assert'; import EXPLAINCLI from './EXPLAINCLI'; import { parseArgs } from '@redis/client/lib/commands/generic-transformers'; +import { DEFAULT_DIALECT } from '../dialect/default'; describe('EXPLAINCLI', () => { it('transformArguments', () => { assert.deepEqual( parseArgs(EXPLAINCLI, 'index', '*'), - ['FT.EXPLAINCLI', 'index', '*'] + ['FT.EXPLAINCLI', 'index', '*', 'DIALECT', DEFAULT_DIALECT] + ); + }); + + it('with dialect', () => { + assert.deepEqual( + parseArgs(EXPLAINCLI, 'index', '*', {DIALECT: 1}), + ['FT.EXPLAINCLI', 'index', '*', 'DIALECT', '1'] ); }); }); diff --git a/packages/search/lib/commands/EXPLAINCLI.ts b/packages/search/lib/commands/EXPLAINCLI.ts index 53711067058..4ef5fba88d6 100644 --- a/packages/search/lib/commands/EXPLAINCLI.ts +++ b/packages/search/lib/commands/EXPLAINCLI.ts @@ -1,11 +1,27 @@ import { CommandParser } from '@redis/client/dist/lib/client/parser'; import { RedisArgument, ArrayReply, BlobStringReply, Command } from '@redis/client/dist/lib/RESP/types'; +import { DEFAULT_DIALECT } from '../dialect/default'; + +export interface FtExplainCLIOptions { + DIALECT?: number; +} export default { NOT_KEYED_COMMAND: true, IS_READ_ONLY: true, - parseCommand(parser: CommandParser, index: RedisArgument, query: RedisArgument) { + parseCommand( + parser: CommandParser, + index: RedisArgument, + query: RedisArgument, + options?: FtExplainCLIOptions + ) { parser.push('FT.EXPLAINCLI', index, query); + + if (options?.DIALECT) { + parser.push('DIALECT', options.DIALECT.toString()); + } else { + parser.push('DIALECT', DEFAULT_DIALECT); + } }, transformReply: undefined as unknown as () => ArrayReply } as const satisfies Command; diff --git a/packages/search/lib/commands/PROFILE_AGGREGATE.spec.ts b/packages/search/lib/commands/PROFILE_AGGREGATE.spec.ts index ee112118c95..5cfa0500a0a 100644 --- a/packages/search/lib/commands/PROFILE_AGGREGATE.spec.ts +++ b/packages/search/lib/commands/PROFILE_AGGREGATE.spec.ts @@ -4,13 +4,14 @@ import { FT_AGGREGATE_STEPS } from './AGGREGATE'; import PROFILE_AGGREGATE from './PROFILE_AGGREGATE'; import { SCHEMA_FIELD_TYPE } from './CREATE'; import { parseArgs } from '@redis/client/lib/commands/generic-transformers'; +import { DEFAULT_DIALECT } from '../dialect/default'; describe('PROFILE AGGREGATE', () => { describe('transformArguments', () => { it('without options', () => { assert.deepEqual( parseArgs(PROFILE_AGGREGATE, 'index', 'query'), - ['FT.PROFILE', 'index', 'AGGREGATE', 'QUERY', 'query'] + ['FT.PROFILE', 'index', 'AGGREGATE', 'QUERY', 'query', 'DIALECT', DEFAULT_DIALECT] ); }); @@ -25,7 +26,7 @@ describe('PROFILE AGGREGATE', () => { }] }), ['FT.PROFILE', 'index', 'AGGREGATE', 'LIMITED', 'QUERY', 'query', - 'VERBATIM', 'SORTBY', '1', '@by'] + 'VERBATIM', 'SORTBY', '1', '@by', 'DIALECT', DEFAULT_DIALECT] ); }); }); diff --git a/packages/search/lib/commands/PROFILE_SEARCH.spec.ts b/packages/search/lib/commands/PROFILE_SEARCH.spec.ts index 524ff1a5228..60f1e8b7474 100644 --- a/packages/search/lib/commands/PROFILE_SEARCH.spec.ts +++ b/packages/search/lib/commands/PROFILE_SEARCH.spec.ts @@ -3,14 +3,14 @@ import testUtils, { GLOBAL } from '../test-utils'; import PROFILE_SEARCH from './PROFILE_SEARCH'; import { SCHEMA_FIELD_TYPE } from './CREATE'; import { parseArgs } from '@redis/client/lib/commands/generic-transformers'; - +import { DEFAULT_DIALECT } from '../dialect/default'; describe('PROFILE SEARCH', () => { describe('transformArguments', () => { it('without options', () => { assert.deepEqual( parseArgs(PROFILE_SEARCH, 'index', 'query'), - ['FT.PROFILE', 'index', 'SEARCH', 'QUERY', 'query'] + ['FT.PROFILE', 'index', 'SEARCH', 'QUERY', 'query', 'DIALECT', DEFAULT_DIALECT] ); }); @@ -22,7 +22,7 @@ describe('PROFILE SEARCH', () => { INKEYS: 'key' }), ['FT.PROFILE', 'index', 'SEARCH', 'LIMITED', 'QUERY', 'query', - 'VERBATIM', 'INKEYS', '1', 'key'] + 'VERBATIM', 'INKEYS', '1', 'key', 'DIALECT', DEFAULT_DIALECT] ); }); }); diff --git a/packages/search/lib/commands/SEARCH.spec.ts b/packages/search/lib/commands/SEARCH.spec.ts index 24248b4cf15..ab480808ffa 100644 --- a/packages/search/lib/commands/SEARCH.spec.ts +++ b/packages/search/lib/commands/SEARCH.spec.ts @@ -2,13 +2,15 @@ import { strict as assert } from 'node:assert'; import testUtils, { GLOBAL } from '../test-utils'; import SEARCH from './SEARCH'; import { parseArgs } from '@redis/client/lib/commands/generic-transformers'; +import { DEFAULT_DIALECT } from '../dialect/default'; + describe('FT.SEARCH', () => { describe('transformArguments', () => { it('without options', () => { assert.deepEqual( parseArgs(SEARCH, 'index', 'query'), - ['FT.SEARCH', 'index', 'query'] + ['FT.SEARCH', 'index', 'query', 'DIALECT', DEFAULT_DIALECT] ); }); @@ -17,7 +19,7 @@ describe('FT.SEARCH', () => { parseArgs(SEARCH, 'index', 'query', { VERBATIM: true }), - ['FT.SEARCH', 'index', 'query', 'VERBATIM'] + ['FT.SEARCH', 'index', 'query', 'VERBATIM', 'DIALECT', DEFAULT_DIALECT] ); }); @@ -26,7 +28,7 @@ describe('FT.SEARCH', () => { parseArgs(SEARCH, 'index', 'query', { NOSTOPWORDS: true }), - ['FT.SEARCH', 'index', 'query', 'NOSTOPWORDS'] + ['FT.SEARCH', 'index', 'query', 'NOSTOPWORDS', 'DIALECT', DEFAULT_DIALECT] ); }); @@ -35,7 +37,7 @@ describe('FT.SEARCH', () => { parseArgs(SEARCH, 'index', 'query', { INKEYS: 'key' }), - ['FT.SEARCH', 'index', 'query', 'INKEYS', '1', 'key'] + ['FT.SEARCH', 'index', 'query', 'INKEYS', '1', 'key', 'DIALECT', DEFAULT_DIALECT] ); }); @@ -44,7 +46,7 @@ describe('FT.SEARCH', () => { parseArgs(SEARCH, 'index', 'query', { INFIELDS: 'field' }), - ['FT.SEARCH', 'index', 'query', 'INFIELDS', '1', 'field'] + ['FT.SEARCH', 'index', 'query', 'INFIELDS', '1', 'field', 'DIALECT', DEFAULT_DIALECT] ); }); @@ -53,7 +55,7 @@ describe('FT.SEARCH', () => { parseArgs(SEARCH, 'index', 'query', { RETURN: 'return' }), - ['FT.SEARCH', 'index', 'query', 'RETURN', '1', 'return'] + ['FT.SEARCH', 'index', 'query', 'RETURN', '1', 'return', 'DIALECT', DEFAULT_DIALECT] ); }); @@ -63,7 +65,7 @@ describe('FT.SEARCH', () => { parseArgs(SEARCH, 'index', 'query', { SUMMARIZE: true }), - ['FT.SEARCH', 'index', 'query', 'SUMMARIZE'] + ['FT.SEARCH', 'index', 'query', 'SUMMARIZE', 'DIALECT', DEFAULT_DIALECT] ); }); @@ -75,7 +77,7 @@ describe('FT.SEARCH', () => { FIELDS: '@field' } }), - ['FT.SEARCH', 'index', 'query', 'SUMMARIZE', 'FIELDS', '1', '@field'] + ['FT.SEARCH', 'index', 'query', 'SUMMARIZE', 'FIELDS', '1', '@field', 'DIALECT', DEFAULT_DIALECT] ); }); @@ -86,7 +88,7 @@ describe('FT.SEARCH', () => { FIELDS: ['@1', '@2'] } }), - ['FT.SEARCH', 'index', 'query', 'SUMMARIZE', 'FIELDS', '2', '@1', '@2'] + ['FT.SEARCH', 'index', 'query', 'SUMMARIZE', 'FIELDS', '2', '@1', '@2', 'DIALECT', DEFAULT_DIALECT] ); }); }); @@ -98,7 +100,7 @@ describe('FT.SEARCH', () => { FRAGS: 1 } }), - ['FT.SEARCH', 'index', 'query', 'SUMMARIZE', 'FRAGS', '1'] + ['FT.SEARCH', 'index', 'query', 'SUMMARIZE', 'FRAGS', '1', 'DIALECT', DEFAULT_DIALECT] ); }); @@ -109,7 +111,7 @@ describe('FT.SEARCH', () => { LEN: 1 } }), - ['FT.SEARCH', 'index', 'query', 'SUMMARIZE', 'LEN', '1'] + ['FT.SEARCH', 'index', 'query', 'SUMMARIZE', 'LEN', '1', 'DIALECT', DEFAULT_DIALECT] ); }); @@ -120,7 +122,7 @@ describe('FT.SEARCH', () => { SEPARATOR: 'separator' } }), - ['FT.SEARCH', 'index', 'query', 'SUMMARIZE', 'SEPARATOR', 'separator'] + ['FT.SEARCH', 'index', 'query', 'SUMMARIZE', 'SEPARATOR', 'separator', 'DIALECT', DEFAULT_DIALECT] ); }); }); @@ -131,7 +133,7 @@ describe('FT.SEARCH', () => { parseArgs(SEARCH, 'index', 'query', { HIGHLIGHT: true }), - ['FT.SEARCH', 'index', 'query', 'HIGHLIGHT'] + ['FT.SEARCH', 'index', 'query', 'HIGHLIGHT', 'DIALECT', DEFAULT_DIALECT] ); }); @@ -143,7 +145,7 @@ describe('FT.SEARCH', () => { FIELDS: ['@field'] } }), - ['FT.SEARCH', 'index', 'query', 'HIGHLIGHT', 'FIELDS', '1', '@field'] + ['FT.SEARCH', 'index', 'query', 'HIGHLIGHT', 'FIELDS', '1', '@field', 'DIALECT', DEFAULT_DIALECT] ); }); @@ -154,7 +156,7 @@ describe('FT.SEARCH', () => { FIELDS: ['@1', '@2'] } }), - ['FT.SEARCH', 'index', 'query', 'HIGHLIGHT', 'FIELDS', '2', '@1', '@2'] + ['FT.SEARCH', 'index', 'query', 'HIGHLIGHT', 'FIELDS', '2', '@1', '@2', 'DIALECT', DEFAULT_DIALECT] ); }); }); @@ -169,7 +171,7 @@ describe('FT.SEARCH', () => { } } }), - ['FT.SEARCH', 'index', 'query', 'HIGHLIGHT', 'TAGS', 'open', 'close'] + ['FT.SEARCH', 'index', 'query', 'HIGHLIGHT', 'TAGS', 'open', 'close', 'DIALECT', DEFAULT_DIALECT] ); }); }); @@ -179,7 +181,7 @@ describe('FT.SEARCH', () => { parseArgs(SEARCH, 'index', 'query', { SLOP: 1 }), - ['FT.SEARCH', 'index', 'query', 'SLOP', '1'] + ['FT.SEARCH', 'index', 'query', 'SLOP', '1', 'DIALECT', DEFAULT_DIALECT] ); }); @@ -188,7 +190,7 @@ describe('FT.SEARCH', () => { parseArgs(SEARCH, 'index', 'query', { TIMEOUT: 1 }), - ['FT.SEARCH', 'index', 'query', 'TIMEOUT', '1'] + ['FT.SEARCH', 'index', 'query', 'TIMEOUT', '1', 'DIALECT', DEFAULT_DIALECT] ); }); @@ -197,7 +199,7 @@ describe('FT.SEARCH', () => { parseArgs(SEARCH, 'index', 'query', { INORDER: true }), - ['FT.SEARCH', 'index', 'query', 'INORDER'] + ['FT.SEARCH', 'index', 'query', 'INORDER', 'DIALECT', DEFAULT_DIALECT] ); }); @@ -206,7 +208,7 @@ describe('FT.SEARCH', () => { parseArgs(SEARCH, 'index', 'query', { LANGUAGE: 'Arabic' }), - ['FT.SEARCH', 'index', 'query', 'LANGUAGE', 'Arabic'] + ['FT.SEARCH', 'index', 'query', 'LANGUAGE', 'Arabic', 'DIALECT', DEFAULT_DIALECT] ); }); @@ -215,7 +217,7 @@ describe('FT.SEARCH', () => { parseArgs(SEARCH, 'index', 'query', { EXPANDER: 'expender' }), - ['FT.SEARCH', 'index', 'query', 'EXPANDER', 'expender'] + ['FT.SEARCH', 'index', 'query', 'EXPANDER', 'expender', 'DIALECT', DEFAULT_DIALECT] ); }); @@ -224,7 +226,7 @@ describe('FT.SEARCH', () => { parseArgs(SEARCH, 'index', 'query', { SCORER: 'scorer' }), - ['FT.SEARCH', 'index', 'query', 'SCORER', 'scorer'] + ['FT.SEARCH', 'index', 'query', 'SCORER', 'scorer', 'DIALECT', DEFAULT_DIALECT] ); }); @@ -233,7 +235,7 @@ describe('FT.SEARCH', () => { parseArgs(SEARCH, 'index', 'query', { SORTBY: '@by' }), - ['FT.SEARCH', 'index', 'query', 'SORTBY', '@by'] + ['FT.SEARCH', 'index', 'query', 'SORTBY', '@by', 'DIALECT', DEFAULT_DIALECT] ); }); @@ -245,7 +247,7 @@ describe('FT.SEARCH', () => { size: 1 } }), - ['FT.SEARCH', 'index', 'query', 'LIMIT', '0', '1'] + ['FT.SEARCH', 'index', 'query', 'LIMIT', '0', '1', 'DIALECT', DEFAULT_DIALECT] ); }); @@ -258,7 +260,7 @@ describe('FT.SEARCH', () => { number: 1 } }), - ['FT.SEARCH', 'index', 'query', 'PARAMS', '6', 'string', 'string', 'buffer', Buffer.from('buffer'), 'number', '1'] + ['FT.SEARCH', 'index', 'query', 'PARAMS', '6', 'string', 'string', 'buffer', Buffer.from('buffer'), 'number', '1', 'DIALECT', DEFAULT_DIALECT] ); }); diff --git a/packages/search/lib/commands/SEARCH.ts b/packages/search/lib/commands/SEARCH.ts index c2d2a9d2696..f48ac056784 100644 --- a/packages/search/lib/commands/SEARCH.ts +++ b/packages/search/lib/commands/SEARCH.ts @@ -2,6 +2,7 @@ import { CommandParser } from '@redis/client/dist/lib/client/parser'; import { RedisArgument, Command, ReplyUnion } from '@redis/client/dist/lib/RESP/types'; import { RedisVariadicArgument, parseOptionalVariadicArgument } from '@redis/client/dist/lib/commands/generic-transformers'; import { RediSearchProperty, RediSearchLanguage } from './CREATE'; +import { DEFAULT_DIALECT } from '../dialect/default'; export type FtSearchParams = Record; @@ -150,8 +151,10 @@ export function parseSearchOptions(parser: CommandParser, options?: FtSearchOpti parseParamsArgument(parser, options?.PARAMS); - if (options?.DIALECT !== undefined) { + if (options?.DIALECT) { parser.push('DIALECT', options.DIALECT.toString()); + } else { + parser.push('DIALECT', DEFAULT_DIALECT); } } diff --git a/packages/search/lib/commands/SEARCH_NOCONTENT.spec.ts b/packages/search/lib/commands/SEARCH_NOCONTENT.spec.ts index bfcca8b4bda..cd37409b5bb 100644 --- a/packages/search/lib/commands/SEARCH_NOCONTENT.spec.ts +++ b/packages/search/lib/commands/SEARCH_NOCONTENT.spec.ts @@ -2,13 +2,14 @@ import { strict as assert } from 'assert'; import testUtils, { GLOBAL } from '../test-utils'; import SEARCH_NOCONTENT from './SEARCH_NOCONTENT'; import { parseArgs } from '@redis/client/lib/commands/generic-transformers'; +import { DEFAULT_DIALECT } from '../dialect/default'; describe('FT.SEARCH NOCONTENT', () => { describe('transformArguments', () => { it('without options', () => { assert.deepEqual( parseArgs(SEARCH_NOCONTENT, 'index', 'query'), - ['FT.SEARCH', 'index', 'query', 'NOCONTENT'] + ['FT.SEARCH', 'index', 'query', 'DIALECT', DEFAULT_DIALECT, 'NOCONTENT'] ); }); }); diff --git a/packages/search/lib/commands/SEARCH_NOCONTENT.ts b/packages/search/lib/commands/SEARCH_NOCONTENT.ts index c0a152375d9..a6968851acd 100644 --- a/packages/search/lib/commands/SEARCH_NOCONTENT.ts +++ b/packages/search/lib/commands/SEARCH_NOCONTENT.ts @@ -5,7 +5,7 @@ export default { NOT_KEYED_COMMAND: SEARCH.NOT_KEYED_COMMAND, IS_READ_ONLY: SEARCH.IS_READ_ONLY, parseCommand(...args: Parameters) { - SEARCH.parseCommand(...args); + SEARCH.parseCommand(...args); args[0].push('NOCONTENT'); }, transformReply: { diff --git a/packages/search/lib/commands/SPELLCHECK.spec.ts b/packages/search/lib/commands/SPELLCHECK.spec.ts index 4f5a3628f4d..482deed6a45 100644 --- a/packages/search/lib/commands/SPELLCHECK.spec.ts +++ b/packages/search/lib/commands/SPELLCHECK.spec.ts @@ -2,13 +2,14 @@ import { strict as assert } from 'node:assert'; import testUtils, { GLOBAL } from '../test-utils'; import SPELLCHECK from './SPELLCHECK'; import { parseArgs } from '@redis/client/lib/commands/generic-transformers'; +import { DEFAULT_DIALECT } from '../dialect/default'; describe('FT.SPELLCHECK', () => { describe('transformArguments', () => { it('without options', () => { assert.deepEqual( parseArgs(SPELLCHECK, 'index', 'query'), - ['FT.SPELLCHECK', 'index', 'query'] + ['FT.SPELLCHECK', 'index', 'query', 'DIALECT', DEFAULT_DIALECT] ); }); @@ -17,7 +18,7 @@ describe('FT.SPELLCHECK', () => { parseArgs(SPELLCHECK, 'index', 'query', { DISTANCE: 2 }), - ['FT.SPELLCHECK', 'index', 'query', 'DISTANCE', '2'] + ['FT.SPELLCHECK', 'index', 'query', 'DISTANCE', '2', 'DIALECT', DEFAULT_DIALECT] ); }); @@ -30,7 +31,7 @@ describe('FT.SPELLCHECK', () => { dictionary: 'dictionary' } }), - ['FT.SPELLCHECK', 'index', 'query', 'TERMS', 'INCLUDE', 'dictionary'] + ['FT.SPELLCHECK', 'index', 'query', 'TERMS', 'INCLUDE', 'dictionary', 'DIALECT', DEFAULT_DIALECT] ); }); @@ -45,7 +46,7 @@ describe('FT.SPELLCHECK', () => { dictionary: 'exclude' }] }), - ['FT.SPELLCHECK', 'index', 'query', 'TERMS', 'INCLUDE', 'include', 'TERMS', 'EXCLUDE', 'exclude'] + ['FT.SPELLCHECK', 'index', 'query', 'TERMS', 'INCLUDE', 'include', 'TERMS', 'EXCLUDE', 'exclude', 'DIALECT', DEFAULT_DIALECT] ); }); }); diff --git a/packages/search/lib/commands/SPELLCHECK.ts b/packages/search/lib/commands/SPELLCHECK.ts index ae95b72c249..3b909cdca32 100644 --- a/packages/search/lib/commands/SPELLCHECK.ts +++ b/packages/search/lib/commands/SPELLCHECK.ts @@ -1,5 +1,6 @@ import { CommandParser } from '@redis/client/dist/lib/client/parser'; import { RedisArgument, Command, ReplyUnion } from '@redis/client/dist/lib/RESP/types'; +import { DEFAULT_DIALECT } from '../dialect/default'; export interface Terms { mode: 'INCLUDE' | 'EXCLUDE'; @@ -34,6 +35,8 @@ export default { if (options?.DIALECT) { parser.push('DIALECT', options.DIALECT.toString()); + } else { + parser.push('DIALECT', DEFAULT_DIALECT); } }, transformReply: { diff --git a/packages/search/lib/dialect/default.ts b/packages/search/lib/dialect/default.ts new file mode 100644 index 00000000000..54cde05d119 --- /dev/null +++ b/packages/search/lib/dialect/default.ts @@ -0,0 +1 @@ +export const DEFAULT_DIALECT = '2'; From 33cdc007466b336f7f7c82669e8312d06df83a31 Mon Sep 17 00:00:00 2001 From: "Bobby I." Date: Tue, 18 Feb 2025 15:57:40 +0200 Subject: [PATCH 1504/1748] churn(docs) update entraid documentation (#2898) --- README.md | 1 + packages/entraid/README.md | 5 +++-- 2 files changed, 4 insertions(+), 2 deletions(-) diff --git a/README.md b/README.md index 990204eb3df..457f3d2c2cf 100644 --- a/README.md +++ b/README.md @@ -52,6 +52,7 @@ npm install redis | [`@redis/json`](./packages/json) | [Redis JSON](https://redis.io/docs/data-types/json/) commands | | [`@redis/search`](./packages/search) | [RediSearch](https://redis.io/docs/interact/search-and-query/) commands | | [`@redis/time-series`](./packages/time-series) | [Redis Time-Series](https://redis.io/docs/data-types/timeseries/) commands | +| [`@redis/entraid`](./packages/entraid) | Secure token-based authentication for Redis clients using Microsoft Entra ID | > Looking for a high-level library to handle object mapping? See [redis-om-node](https://github.com/redis/redis-om-node)! diff --git a/packages/entraid/README.md b/packages/entraid/README.md index 131935fe619..eec88d71360 100644 --- a/packages/entraid/README.md +++ b/packages/entraid/README.md @@ -15,9 +15,10 @@ Secure token-based authentication for Redis clients using Microsoft Entra ID (fo ## Installation + ```bash -npm install @redis/client -npm install @redis/entraid +npm install "@redis/client@5.0.0-next.6" +npm install "@redis/entraid@5.0.0-next.6" ``` ## Getting Started From 69d507a572a984e233090467fd960a7d6c47bb64 Mon Sep 17 00:00:00 2001 From: "Bobby I." Date: Thu, 27 Feb 2025 10:56:58 +0200 Subject: [PATCH 1505/1748] refactor!: redis 8 compatibility improvements and test infrastructure updates (#2893) * churn(test): use redislabs/client-libs-test for testing This switches our testing infrastructure from redis/redis-stack to redislabs/client-libs-test Docker image across all packages. This change also updates the default Docker version from 7.4.0-v1 to 8.0-M04-pre. * churn(test): verify CONFIG SET / GET compatibility with Redis 8 - Add tests for Redis 8 search configuration settings - Deprecate Redis Search CONFIG commands in favor of standard CONFIG - Test read-only config restrictions for Redis 8 * churn(test): handle Redis 8 coordinate precision in GEOPOS - Update GEOPOS tests to handle increased precision in Redis 8 (17 decimal places vs 14) - Add precision-aware coordinate comparison helper - Add comprehensive test suite for coordinate comparison function * test(search): adapt SUGGET tests for Redis 8 empty results - Update tests to expect empty array ([]) instead of null for SUGGET variants - Affects sugGet, sugGetWithPayloads, sugGetWithScores, and sugGetWithScoresWithPayloads * test(search): support Redis 8 INFO indexes_all field - Add indexes_all field introduced in Redis 8 to index definition test * refactor!(search): simplify PROFILE commands to return raw response - BREAKING CHANGE: FT.PROFILE now returns raw response, letting users implement their own parsing * test: improve version-specific test coverage - Add `testWithClientIfVersionWithinRange` method to run tests for specific Redis versions - Refactor TestUtils to handle version comparisons more accurately - Update test utilities across Redis modules to run tests against multiple versions, and not against latest only --- .github/workflows/tests.yml | 4 +- package.json | 1 + packages/bloom/lib/test-utils.ts | 8 +- .../client/lib/commands/CONFIG_GET.spec.ts | 30 +- .../client/lib/commands/CONFIG_SET.spec.ts | 9 + packages/client/lib/commands/GEOPOS.spec.ts | 124 +++++- packages/client/lib/test-utils.ts | 6 +- packages/entraid/lib/test-utils.ts | 6 +- packages/graph/lib/test-utils.ts | 9 +- packages/json/lib/test-utils.ts | 8 +- .../search/lib/commands/CONFIG_SET.spec.ts | 36 ++ packages/search/lib/commands/INFO.spec.ts | 396 +++++++++++++----- .../lib/commands/PROFILE_AGGREGATE.spec.ts | 137 ++++-- .../search/lib/commands/PROFILE_AGGREGATE.ts | 60 ++- .../lib/commands/PROFILE_SEARCH.spec.ts | 117 ++++-- .../search/lib/commands/PROFILE_SEARCH.ts | 133 +----- packages/search/lib/commands/SUGGET.spec.ts | 16 +- .../lib/commands/SUGGET_WITHPAYLOADS.spec.ts | 21 +- .../lib/commands/SUGGET_WITHSCORES.spec.ts | 9 +- .../SUGGET_WITHSCORES_WITHPAYLOADS.spec.ts | 8 +- packages/search/lib/commands/index.ts | 12 + packages/search/lib/test-utils.ts | 35 +- packages/search/package.json | 3 +- packages/test-utils/lib/dockers.ts | 51 ++- packages/test-utils/lib/index.spec.ts | 106 +++++ packages/test-utils/lib/index.ts | 149 +++++-- packages/test-utils/package.json | 3 + packages/time-series/lib/test-utils.ts | 8 +- 28 files changed, 1083 insertions(+), 422 deletions(-) create mode 100644 packages/test-utils/lib/index.spec.ts diff --git a/.github/workflows/tests.yml b/.github/workflows/tests.yml index 9decd26898a..7bcc72e5408 100644 --- a/.github/workflows/tests.yml +++ b/.github/workflows/tests.yml @@ -21,8 +21,8 @@ jobs: strategy: fail-fast: false matrix: - node-version: ['18', '20', '22'] - redis-version: ['6.2.6-v17', '7.2.0-v13', '7.4.0-v1'] + node-version: [ '18', '20', '22' ] + redis-version: [ 'rs-7.2.0-v13', 'rs-7.4.0-v1', '8.0-M04-pre' ] steps: - uses: actions/checkout@v4 with: diff --git a/package.json b/package.json index 7ab2a557ff2..0a29c71f831 100644 --- a/package.json +++ b/package.json @@ -5,6 +5,7 @@ "./packages/*" ], "scripts": { + "test-single": "TS_NODE_PROJECT='./packages/test-utils/tsconfig.json' mocha --require ts-node/register/transpile-only ", "test": "npm run test -ws --if-present", "build": "tsc --build", "documentation": "typedoc --out ./documentation", diff --git a/packages/bloom/lib/test-utils.ts b/packages/bloom/lib/test-utils.ts index 1291054e802..7996d61e44e 100644 --- a/packages/bloom/lib/test-utils.ts +++ b/packages/bloom/lib/test-utils.ts @@ -1,10 +1,10 @@ import TestUtils from '@redis/test-utils'; import RedisBloomModules from '.'; -export default new TestUtils({ - dockerImageName: 'redis/redis-stack', - dockerImageVersionArgument: 'redisbloom-version', - defaultDockerVersion: '7.4.0-v1' +export default TestUtils.createFromConfig({ + dockerImageName: 'redislabs/client-libs-test', + dockerImageVersionArgument: 'redis-version', + defaultDockerVersion: '8.0-M04-pre' }); export const GLOBAL = { diff --git a/packages/client/lib/commands/CONFIG_GET.spec.ts b/packages/client/lib/commands/CONFIG_GET.spec.ts index 411b2ddf472..c3f0eac76dd 100644 --- a/packages/client/lib/commands/CONFIG_GET.spec.ts +++ b/packages/client/lib/commands/CONFIG_GET.spec.ts @@ -19,7 +19,6 @@ describe('CONFIG GET', () => { ); }); }); - testUtils.testWithClient('client.configGet', async client => { const config = await client.configGet('*'); @@ -29,4 +28,33 @@ describe('CONFIG GET', () => { assert.equal(typeof value, 'string'); } }, GLOBAL.SERVERS.OPEN); + + testUtils.testWithClient('client.configSet.getSearchConfigSettingTest | Redis >= 8', async client => { + assert.ok( + await client.configGet('search-timeout'), + 'OK' + ); + }, GLOBAL.SERVERS.OPEN); + + testUtils.testWithClient('client.configSet.getTSConfigSettingTest | Redis >= 8', async client => { + assert.ok( + await client.configGet('ts-retention-policy'), + 'OK' + ); + }, GLOBAL.SERVERS.OPEN); + + testUtils.testWithClient('client.configSet.getBFConfigSettingTest | Redis >= 8', async client => { + assert.ok( + await client.configGet('bf-error-rate'), + 'OK' + ); + }, GLOBAL.SERVERS.OPEN); + + testUtils.testWithClient('client.configSet.getCFConfigSettingTest | Redis >= 8', async client => { + assert.ok( + await client.configGet('cf-initial-size'), + 'OK' + ); + }, GLOBAL.SERVERS.OPEN); + }); diff --git a/packages/client/lib/commands/CONFIG_SET.spec.ts b/packages/client/lib/commands/CONFIG_SET.spec.ts index 56bed2ac46a..f9f34dec937 100644 --- a/packages/client/lib/commands/CONFIG_SET.spec.ts +++ b/packages/client/lib/commands/CONFIG_SET.spec.ts @@ -30,4 +30,13 @@ describe('CONFIG SET', () => { 'OK' ); }, GLOBAL.SERVERS.OPEN); + + testUtils.testWithClient('client.configSet.setReadOnlySearchConfigTest | Redis >= 8', + async client => { + assert.rejects( + client.configSet('search-max-doctablesize', '0'), + new Error('ERR CONFIG SET failed (possibly related to argument \'search-max-doctablesize\') - can\'t set immutable config') + ); + }, GLOBAL.SERVERS.OPEN); + }); diff --git a/packages/client/lib/commands/GEOPOS.spec.ts b/packages/client/lib/commands/GEOPOS.spec.ts index 247dd91d222..002d16d0256 100644 --- a/packages/client/lib/commands/GEOPOS.spec.ts +++ b/packages/client/lib/commands/GEOPOS.spec.ts @@ -1,4 +1,4 @@ -import { strict as assert } from 'node:assert'; +import { strict as assert, fail } from 'node:assert'; import testUtils, { GLOBAL } from '../test-utils'; import GEOPOS from './GEOPOS'; import { parseArgs } from './generic-transformers'; @@ -41,12 +41,126 @@ describe('GEOPOS', () => { ...coordinates }); - assert.deepEqual( - await client.geoPos('key', 'member'), - [coordinates] - ); + const result = await client.geoPos('key', 'member'); + + /** + * - Redis < 8: Returns coordinates with 14 decimal places (e.g., "-122.06429868936539") + * - Redis 8+: Returns coordinates with 17 decimal places (e.g., "-122.06429868936538696") + * + */ + const PRECISION = 13; // Number of decimal places to compare + + if (result && result.length === 1 && result[0] != null) { + const { longitude, latitude } = result[0]; + + assert.ok( + compareWithPrecision(longitude, coordinates.longitude, PRECISION), + `Longitude mismatch: ${longitude} vs ${coordinates.longitude}` + ); + assert.ok( + compareWithPrecision(latitude, coordinates.latitude, PRECISION), + `Latitude mismatch: ${latitude} vs ${coordinates.latitude}` + ); + + } else { + assert.fail('Expected a valid result'); + } + + + }, { client: GLOBAL.SERVERS.OPEN, cluster: GLOBAL.CLUSTERS.OPEN }); }); + +describe('compareWithPrecision', () => { + it('should match exact same numbers', () => { + assert.strictEqual( + compareWithPrecision('123.456789', '123.456789', 6), + true + ); + }); + + it('should match when actual has more precision than needed', () => { + assert.strictEqual( + compareWithPrecision('123.456789123456', '123.456789', 6), + true + ); + }); + + it('should match when expected has more precision than needed', () => { + assert.strictEqual( + compareWithPrecision('123.456789', '123.456789123456', 6), + true + ); + }); + + it('should fail when decimals differ within precision', () => { + assert.strictEqual( + compareWithPrecision('123.456689', '123.456789', 6), + false + ); + }); + + it('should handle negative numbers', () => { + assert.strictEqual( + compareWithPrecision('-122.06429868936538', '-122.06429868936539', 13), + true + ); + }); + + it('should fail when integer parts differ', () => { + assert.strictEqual( + compareWithPrecision('124.456789', '123.456789', 6), + false + ); + }); + + it('should handle zero decimal places', () => { + assert.strictEqual( + compareWithPrecision('123.456789', '123.456789', 0), + true + ); + }); + + it('should handle numbers without decimal points', () => { + assert.strictEqual( + compareWithPrecision('123', '123', 6), + true + ); + }); + + it('should handle one number without decimal point', () => { + assert.strictEqual( + compareWithPrecision('123', '123.000', 3), + true + ); + }); + + it('should match Redis coordinates with different precision', () => { + assert.strictEqual( + compareWithPrecision( + '-122.06429868936538696', + '-122.06429868936539', + 13 + ), + true + ); + }); + + it('should match Redis latitude with different precision', () => { + assert.strictEqual( + compareWithPrecision( + '37.37749628831998194', + '37.37749628831998', + 14 + ), + true + ); + }); +}); + +export const compareWithPrecision = (actual: string, expected: string, decimals: number): boolean => { + return Math.abs(Number(actual) - Number(expected)) < Math.pow(10, -decimals); +}; diff --git a/packages/client/lib/test-utils.ts b/packages/client/lib/test-utils.ts index 2d561dd2e20..dce1f97d88a 100644 --- a/packages/client/lib/test-utils.ts +++ b/packages/client/lib/test-utils.ts @@ -5,10 +5,10 @@ import { CredentialsProvider } from './authx'; import { Command } from './RESP/types'; import { BasicCommandParser } from './client/parser'; -const utils = new TestUtils({ - dockerImageName: 'redis/redis-stack', +const utils = TestUtils.createFromConfig({ + dockerImageName: 'redislabs/client-libs-test', dockerImageVersionArgument: 'redis-version', - defaultDockerVersion: '7.4.0-v1' + defaultDockerVersion: '8.0-M04-pre' }); export default utils; diff --git a/packages/entraid/lib/test-utils.ts b/packages/entraid/lib/test-utils.ts index eecec2d4d6d..970637a1225 100644 --- a/packages/entraid/lib/test-utils.ts +++ b/packages/entraid/lib/test-utils.ts @@ -3,10 +3,10 @@ import { IdentityProvider, StreamingCredentialsProvider, TokenManager, TokenResp import TestUtils from '@redis/test-utils'; import { EntraidCredentialsProvider } from './entraid-credentials-provider'; -export const testUtils = new TestUtils({ - dockerImageName: 'redis/redis-stack', +export const testUtils = TestUtils.createFromConfig({ + dockerImageName: 'redislabs/client-libs-test', dockerImageVersionArgument: 'redis-version', - defaultDockerVersion: '7.4.0-v1' + defaultDockerVersion: '8.0-M04-pre' }); const DEBUG_MODE_ARGS = testUtils.isVersionGreaterThan([7]) ? diff --git a/packages/graph/lib/test-utils.ts b/packages/graph/lib/test-utils.ts index 2aa9384dbe6..16c44582061 100644 --- a/packages/graph/lib/test-utils.ts +++ b/packages/graph/lib/test-utils.ts @@ -1,10 +1,11 @@ import TestUtils from '@redis/test-utils'; import RedisGraph from '.'; -export default new TestUtils({ - dockerImageName: 'redis/redis-stack', - dockerImageVersionArgument: 'redisgraph-version', - defaultDockerVersion: '7.4.0-v1' + +export default TestUtils.createFromConfig({ + dockerImageName: 'redislabs/client-libs-test', + dockerImageVersionArgument: 'redis-version', + defaultDockerVersion: '8.0-M04-pre' }); export const GLOBAL = { diff --git a/packages/json/lib/test-utils.ts b/packages/json/lib/test-utils.ts index 0ac30c521b2..caa1c3049af 100644 --- a/packages/json/lib/test-utils.ts +++ b/packages/json/lib/test-utils.ts @@ -1,10 +1,10 @@ import TestUtils from '@redis/test-utils'; import RedisJSON from '.'; -export default new TestUtils({ - dockerImageName: 'redis/redis-stack', - dockerImageVersionArgument: 'redisgraph-version', - defaultDockerVersion: '7.4.0-v1' +export default TestUtils.createFromConfig({ + dockerImageName: 'redislabs/client-libs-test', + dockerImageVersionArgument: 'redis-version', + defaultDockerVersion: '8.0-M04-pre' }); export const GLOBAL = { diff --git a/packages/search/lib/commands/CONFIG_SET.spec.ts b/packages/search/lib/commands/CONFIG_SET.spec.ts index 71a4e69f26f..c5922a28756 100644 --- a/packages/search/lib/commands/CONFIG_SET.spec.ts +++ b/packages/search/lib/commands/CONFIG_SET.spec.ts @@ -17,4 +17,40 @@ describe('FT.CONFIG SET', () => { 'OK' ); }, GLOBAL.SERVERS.OPEN); + + testUtils.testWithClientIfVersionWithinRange([[8], 'LATEST'], 'setSearchConfigGloballyTest', async client => { + + const normalizeObject = obj => JSON.parse(JSON.stringify(obj)); + assert.equal(await client.configSet('search-default-dialect', '3'), + 'OK', 'CONFIG SET should return OK'); + + assert.deepEqual( + normalizeObject(await client.configGet('search-default-dialect')), + { 'search-default-dialect': '3' }, + 'CONFIG GET should return 3' + ); + + assert.deepEqual( + normalizeObject(await client.ft.configGet('DEFAULT_DIALECT')), + { 'DEFAULT_DIALECT': '3' }, + 'FT.CONFIG GET should return 3' + ); + + const ftConfigSetResult = await client.ft.configSet('DEFAULT_DIALECT', '2'); + assert.equal(normalizeObject(ftConfigSetResult), 'OK', 'FT.CONFIG SET should return OK'); + + assert.deepEqual( + normalizeObject(await client.ft.configGet('DEFAULT_DIALECT')), + { 'DEFAULT_DIALECT': '2' }, + 'FT.CONFIG GET should return 2' + ); + + assert.deepEqual( + normalizeObject(await client.configGet('search-default-dialect')), + { 'search-default-dialect': '2' }, + 'CONFIG GET should return 22' + ); + + }, GLOBAL.SERVERS.OPEN); + }); diff --git a/packages/search/lib/commands/INFO.spec.ts b/packages/search/lib/commands/INFO.spec.ts index cbb4ea91677..caf311e07e9 100644 --- a/packages/search/lib/commands/INFO.spec.ts +++ b/packages/search/lib/commands/INFO.spec.ts @@ -5,105 +5,305 @@ import { SCHEMA_FIELD_TYPE } from './CREATE'; import { parseArgs } from '@redis/client/lib/commands/generic-transformers'; describe('INFO', () => { - it('transformArguments', () => { - assert.deepEqual( - parseArgs(INFO, 'index'), - ['FT.INFO', 'index'] - ); + it('transformArguments', () => { + assert.deepEqual( + parseArgs(INFO, 'index'), + ['FT.INFO', 'index'] + ); + }); + + testUtils.testWithClientIfVersionWithinRange([[8], 'LATEST'], 'client.ft.info', async client => { + + await client.ft.create('index', { + field: SCHEMA_FIELD_TYPE.TEXT }); + const ret = await client.ft.info('index'); + // effectively testing that stopwords_list is not in ret + assert.deepEqual( + ret, + { + index_name: 'index', + index_options: [], + index_definition: Object.create(null, { + + indexes_all: { + value: 'false', + configurable: true, + enumerable: true + }, + + default_score: { + value: '1', + configurable: true, + enumerable: true + }, + key_type: { + value: 'HASH', + configurable: true, + enumerable: true + }, + prefixes: { + value: [''], + configurable: true, + enumerable: true + } + }), + attributes: [Object.create(null, { + identifier: { + value: 'field', + configurable: true, + enumerable: true + }, + attribute: { + value: 'field', + configurable: true, + enumerable: true + }, + type: { + value: 'TEXT', + configurable: true, + enumerable: true + }, + WEIGHT: { + value: '1', + configurable: true, + enumerable: true + } + })], + num_docs: 0, + max_doc_id: 0, + num_terms: 0, + num_records: 0, + inverted_sz_mb: 0, + vector_index_sz_mb: 0, + total_inverted_index_blocks: 0, + offset_vectors_sz_mb: 0, + doc_table_size_mb: 0, + sortable_values_size_mb: 0, + key_table_size_mb: 0, + records_per_doc_avg: NaN, + bytes_per_record_avg: NaN, + cleaning: 0, + offsets_per_term_avg: NaN, + offset_bits_per_record_avg: NaN, + geoshapes_sz_mb: 0, + hash_indexing_failures: 0, + indexing: 0, + percent_indexed: 1, + number_of_uses: 1, + tag_overhead_sz_mb: 0, + text_overhead_sz_mb: 0, + total_index_memory_sz_mb: 0, + total_indexing_time: 0, + gc_stats: { + bytes_collected: 0, + total_ms_run: 0, + total_cycles: 0, + average_cycle_time_ms: NaN, + last_run_time_ms: 0, + gc_numeric_trees_missed: 0, + gc_blocks_denied: 0 + }, + cursor_stats: { + global_idle: 0, + global_total: 0, + index_capacity: 128, + index_total: 0 + }, + } + ); + + }, GLOBAL.SERVERS.OPEN); + + testUtils.testWithClientIfVersionWithinRange([[7, 4, 2], [7, 4, 2]], 'client.ft.info', async client => { - testUtils.testWithClient('client.ft.info', async client => { - await client.ft.create('index', { - field: SCHEMA_FIELD_TYPE.TEXT - }); - const ret = await client.ft.info('index'); - // effectively testing that stopwords_list is not in ret - assert.deepEqual( - ret, - { - index_name: 'index', - index_options: [], - index_definition: Object.create(null, { - default_score: { - value: '1', - configurable: true, - enumerable: true - }, - key_type: { - value: 'HASH', - configurable: true, - enumerable: true - }, - prefixes: { - value: [''], - configurable: true, - enumerable: true - } - }), - attributes: [Object.create(null, { - identifier: { - value: 'field', - configurable: true, - enumerable: true - }, - attribute: { - value: 'field', - configurable: true, - enumerable: true - }, - type: { - value: 'TEXT', - configurable: true, - enumerable: true - }, - WEIGHT: { - value: '1', - configurable: true, - enumerable: true - } - })], - num_docs: 0, - max_doc_id: 0, - num_terms: 0, - num_records: 0, - inverted_sz_mb: 0, - vector_index_sz_mb: 0, - total_inverted_index_blocks: 0, - offset_vectors_sz_mb: 0, - doc_table_size_mb: 0, - sortable_values_size_mb: 0, - key_table_size_mb: 0, - records_per_doc_avg: NaN, - bytes_per_record_avg: NaN, - cleaning: 0, - offsets_per_term_avg: NaN, - offset_bits_per_record_avg: NaN, - geoshapes_sz_mb: 0, - hash_indexing_failures: 0, - indexing: 0, - percent_indexed: 1, - number_of_uses: 1, - tag_overhead_sz_mb: 0, - text_overhead_sz_mb: 0, - total_index_memory_sz_mb: 0, - total_indexing_time: 0, - gc_stats: { - bytes_collected: 0, - total_ms_run: 0, - total_cycles: 0, - average_cycle_time_ms: NaN, - last_run_time_ms: 0, - gc_numeric_trees_missed: 0, - gc_blocks_denied: 0 - }, - cursor_stats: { - global_idle: 0, - global_total: 0, - index_capacity: 128, - index_total: 0 - }, - } - ); + await client.ft.create('index', { + field: SCHEMA_FIELD_TYPE.TEXT + }); + const ret = await client.ft.info('index'); + // effectively testing that stopwords_list is not in ret + assert.deepEqual( + ret, + { + index_name: 'index', + index_options: [], + index_definition: Object.create(null, { + default_score: { + value: '1', + configurable: true, + enumerable: true + }, + key_type: { + value: 'HASH', + configurable: true, + enumerable: true + }, + prefixes: { + value: [''], + configurable: true, + enumerable: true + } + }), + attributes: [Object.create(null, { + identifier: { + value: 'field', + configurable: true, + enumerable: true + }, + attribute: { + value: 'field', + configurable: true, + enumerable: true + }, + type: { + value: 'TEXT', + configurable: true, + enumerable: true + }, + WEIGHT: { + value: '1', + configurable: true, + enumerable: true + } + })], + num_docs: 0, + max_doc_id: 0, + num_terms: 0, + num_records: 0, + inverted_sz_mb: 0, + vector_index_sz_mb: 0, + total_inverted_index_blocks: 0, + offset_vectors_sz_mb: 0, + doc_table_size_mb: 0, + sortable_values_size_mb: 0, + key_table_size_mb: 0, + records_per_doc_avg: NaN, + bytes_per_record_avg: NaN, + cleaning: 0, + offsets_per_term_avg: NaN, + offset_bits_per_record_avg: NaN, + geoshapes_sz_mb: 0, + hash_indexing_failures: 0, + indexing: 0, + percent_indexed: 1, + number_of_uses: 1, + tag_overhead_sz_mb: 0, + text_overhead_sz_mb: 0, + total_index_memory_sz_mb: 0, + total_indexing_time: 0, + gc_stats: { + bytes_collected: 0, + total_ms_run: 0, + total_cycles: 0, + average_cycle_time_ms: NaN, + last_run_time_ms: 0, + gc_numeric_trees_missed: 0, + gc_blocks_denied: 0 + }, + cursor_stats: { + global_idle: 0, + global_total: 0, + index_capacity: 128, + index_total: 0 + }, + } + ); + + }, GLOBAL.SERVERS.OPEN); + + testUtils.testWithClientIfVersionWithinRange([[7, 2, 0], [7, 2, 0]], 'client.ft.info', async client => { + + await client.ft.create('index', { + field: SCHEMA_FIELD_TYPE.TEXT + }); + const ret = await client.ft.info('index'); + // effectively testing that stopwords_list is not in ret + assert.deepEqual( + ret, + { + index_name: 'index', + index_options: [], + index_definition: Object.create(null, { + default_score: { + value: '1', + configurable: true, + enumerable: true + }, + key_type: { + value: 'HASH', + configurable: true, + enumerable: true + }, + prefixes: { + value: [''], + configurable: true, + enumerable: true + } + }), + attributes: [Object.create(null, { + identifier: { + value: 'field', + configurable: true, + enumerable: true + }, + attribute: { + value: 'field', + configurable: true, + enumerable: true + }, + type: { + value: 'TEXT', + configurable: true, + enumerable: true + }, + WEIGHT: { + value: '1', + configurable: true, + enumerable: true + } + })], + num_docs: "0", + max_doc_id: "0", + num_terms: "0", + num_records: "0", + inverted_sz_mb: 0, + vector_index_sz_mb: 0, + total_inverted_index_blocks: "0", + offset_vectors_sz_mb: 0, + doc_table_size_mb: 0, + sortable_values_size_mb: 0, + key_table_size_mb: 0, + records_per_doc_avg: NaN, + bytes_per_record_avg: NaN, + cleaning: 0, + offsets_per_term_avg: NaN, + offset_bits_per_record_avg: NaN, + geoshapes_sz_mb: 0, + hash_indexing_failures: "0", + indexing: "0", + percent_indexed: 1, + number_of_uses: 1, + tag_overhead_sz_mb: 0, + text_overhead_sz_mb: 0, + total_index_memory_sz_mb: 0, + total_indexing_time: 0, + gc_stats: { + bytes_collected: 0, + total_ms_run: 0, + total_cycles: 0, + average_cycle_time_ms: NaN, + last_run_time_ms: 0, + gc_numeric_trees_missed: 0, + gc_blocks_denied: 0 + }, + cursor_stats: { + global_idle: 0, + global_total: 0, + index_capacity: 128, + index_total: 0 + }, + } + ); - }, GLOBAL.SERVERS.OPEN); + }, GLOBAL.SERVERS.OPEN); }); diff --git a/packages/search/lib/commands/PROFILE_AGGREGATE.spec.ts b/packages/search/lib/commands/PROFILE_AGGREGATE.spec.ts index 5cfa0500a0a..bdf452c16ea 100644 --- a/packages/search/lib/commands/PROFILE_AGGREGATE.spec.ts +++ b/packages/search/lib/commands/PROFILE_AGGREGATE.spec.ts @@ -7,43 +7,106 @@ import { parseArgs } from '@redis/client/lib/commands/generic-transformers'; import { DEFAULT_DIALECT } from '../dialect/default'; describe('PROFILE AGGREGATE', () => { - describe('transformArguments', () => { - it('without options', () => { - assert.deepEqual( - parseArgs(PROFILE_AGGREGATE, 'index', 'query'), - ['FT.PROFILE', 'index', 'AGGREGATE', 'QUERY', 'query', 'DIALECT', DEFAULT_DIALECT] - ); - }); - - it('with options', () => { - assert.deepEqual( - parseArgs(PROFILE_AGGREGATE, 'index', 'query', { - LIMITED: true, - VERBATIM: true, - STEPS: [{ - type: FT_AGGREGATE_STEPS.SORTBY, - BY: '@by' - }] - }), - ['FT.PROFILE', 'index', 'AGGREGATE', 'LIMITED', 'QUERY', 'query', - 'VERBATIM', 'SORTBY', '1', '@by', 'DIALECT', DEFAULT_DIALECT] - ); - }); + describe('transformArguments', () => { + it('without options', () => { + assert.deepEqual( + parseArgs(PROFILE_AGGREGATE, 'index', 'query'), + ['FT.PROFILE', 'index', 'AGGREGATE', 'QUERY', 'query', 'DIALECT', DEFAULT_DIALECT] + ); }); - testUtils.testWithClient('client.ft.search', async client => { - await Promise.all([ - client.ft.create('index', { - field: SCHEMA_FIELD_TYPE.NUMERIC - }), - client.hSet('1', 'field', '1'), - client.hSet('2', 'field', '2') - ]); - - const res = await client.ft.profileAggregate('index', '*'); - assert.deepEqual('None', res.profile.warning); - assert.ok(typeof res.profile.iteratorsProfile.counter === 'number'); - assert.ok(typeof res.profile.parsingTime === 'string'); - assert.ok(res.results.total == 1); - }, GLOBAL.SERVERS.OPEN); + it('with options', () => { + assert.deepEqual( + parseArgs(PROFILE_AGGREGATE, 'index', 'query', { + LIMITED: true, + VERBATIM: true, + STEPS: [{ + type: FT_AGGREGATE_STEPS.SORTBY, + BY: '@by' + }] + }), + ['FT.PROFILE', 'index', 'AGGREGATE', 'LIMITED', 'QUERY', 'query', + 'VERBATIM', 'SORTBY', '1', '@by', 'DIALECT', DEFAULT_DIALECT] + ); + }); + }); + + testUtils.testWithClientIfVersionWithinRange([[8], 'LATEST'], 'client.ft.search', async client => { + await Promise.all([ + client.ft.create('index', { + field: SCHEMA_FIELD_TYPE.NUMERIC + }), + client.hSet('1', 'field', '1'), + client.hSet('2', 'field', '2') + ]); + + + const normalizeObject = obj => JSON.parse(JSON.stringify(obj)); + const res = await client.ft.profileAggregate('index', '*'); + + const normalizedRes = normalizeObject(res); + assert.equal(normalizedRes.results.total, 1); + + assert.ok(normalizedRes.profile[0] === 'Shards'); + assert.ok(Array.isArray(normalizedRes.profile[1])); + assert.ok(normalizedRes.profile[2] === 'Coordinator'); + assert.ok(Array.isArray(normalizedRes.profile[3])); + + const shardProfile = normalizedRes.profile[1][0]; + assert.ok(shardProfile.includes('Total profile time')); + assert.ok(shardProfile.includes('Parsing time')); + assert.ok(shardProfile.includes('Pipeline creation time')); + assert.ok(shardProfile.includes('Warning')); + assert.ok(shardProfile.includes('Iterators profile')); + + }, GLOBAL.SERVERS.OPEN); + + testUtils.testWithClientIfVersionWithinRange([[7, 2, 0], [7, 4, 0]], 'client.ft.search', async client => { + await Promise.all([ + client.ft.create('index', { + field: SCHEMA_FIELD_TYPE.NUMERIC + }), + client.hSet('1', 'field', '1'), + client.hSet('2', 'field', '2') + ]); + + const normalizeObject = obj => JSON.parse(JSON.stringify(obj)); + const res = await client.ft.profileAggregate('index', '*'); + const normalizedRes = normalizeObject(res); + assert.equal(normalizedRes.results.total, 1); + + assert.ok(Array.isArray(normalizedRes.profile)); + assert.equal(normalizedRes.profile[0][0], 'Total profile time'); + assert.equal(normalizedRes.profile[1][0], 'Parsing time'); + assert.equal(normalizedRes.profile[2][0], 'Pipeline creation time'); + assert.equal(normalizedRes.profile[3][0], 'Warning'); + assert.equal(normalizedRes.profile[4][0], 'Iterators profile'); + assert.equal(normalizedRes.profile[5][0], 'Result processors profile'); + + const iteratorsProfile = normalizedRes.profile[4][1]; + assert.equal(iteratorsProfile[0], 'Type'); + assert.equal(iteratorsProfile[1], 'WILDCARD'); + assert.equal(iteratorsProfile[2], 'Time'); + assert.equal(iteratorsProfile[4], 'Counter'); + }, GLOBAL.SERVERS.OPEN); + + testUtils.testWithClientIfVersionWithinRange([[8], 'LATEST'], '[RESP3] client.ft.search', async client => { + await Promise.all([ + client.ft.create('index', { + field: SCHEMA_FIELD_TYPE.NUMERIC + }), + client.hSet('1', 'field', '1'), + client.hSet('2', 'field', '2') + ]); + + + const normalizeObject = obj => JSON.parse(JSON.stringify(obj)); + const res = await client.ft.profileAggregate('index', '*'); + + const normalizedRes = normalizeObject(res); + assert.equal(normalizedRes.Results.total_results, 1); + assert.ok(normalizedRes.Profile.Shards); + + }, GLOBAL.SERVERS.OPEN_3) + }); diff --git a/packages/search/lib/commands/PROFILE_AGGREGATE.ts b/packages/search/lib/commands/PROFILE_AGGREGATE.ts index ad671c9eb45..94bb6984afa 100644 --- a/packages/search/lib/commands/PROFILE_AGGREGATE.ts +++ b/packages/search/lib/commands/PROFILE_AGGREGATE.ts @@ -1,37 +1,35 @@ import { CommandParser } from '@redis/client/dist/lib/client/parser'; -import { Command, ReplyUnion } from "@redis/client/dist/lib/RESP/types"; -import AGGREGATE, { AggregateRawReply, FtAggregateOptions, parseAggregateOptions } from "./AGGREGATE"; -import { ProfileOptions, ProfileRawReply, ProfileReply, transformProfile } from "./PROFILE_SEARCH"; +import { Command, ReplyUnion, UnwrapReply } from '@redis/client/dist/lib/RESP/types'; +import AGGREGATE, { AggregateRawReply, FtAggregateOptions, parseAggregateOptions } from './AGGREGATE'; +import { ProfileOptions, ProfileRawReplyResp2, ProfileReplyResp2, } from './PROFILE_SEARCH'; export default { NOT_KEYED_COMMAND: true, - IS_READ_ONLY: true, - parseCommand( - parser: CommandParser, - index: string, - query: string, - options?: ProfileOptions & FtAggregateOptions - ) { - parser.push('FT.PROFILE', index, 'AGGREGATE'); - - if (options?.LIMITED) { - parser.push('LIMITED'); - } - - parser.push('QUERY', query); + IS_READ_ONLY: true, + parseCommand( + parser: CommandParser, + index: string, + query: string, + options?: ProfileOptions & FtAggregateOptions + ) { + parser.push('FT.PROFILE', index, 'AGGREGATE'); - parseAggregateOptions(parser, options) - }, - transformReply: { - 2: (reply: ProfileAggeregateRawReply): ProfileReply => { - return { - results: AGGREGATE.transformReply[2](reply[0]), - profile: transformProfile(reply[1]) - } - }, - 3: undefined as unknown as () => ReplyUnion - }, - unstableResp3: true - } as const satisfies Command; + if (options?.LIMITED) { + parser.push('LIMITED'); + } - type ProfileAggeregateRawReply = ProfileRawReply; \ No newline at end of file + parser.push('QUERY', query); + + parseAggregateOptions(parser, options) + }, + transformReply: { + 2: (reply: UnwrapReply>): ProfileReplyResp2 => { + return { + results: AGGREGATE.transformReply[2](reply[0]), + profile: reply[1] + } + }, + 3: (reply: ReplyUnion): ReplyUnion => reply + }, + unstableResp3: true +} as const satisfies Command; diff --git a/packages/search/lib/commands/PROFILE_SEARCH.spec.ts b/packages/search/lib/commands/PROFILE_SEARCH.spec.ts index 60f1e8b7474..419b879d00a 100644 --- a/packages/search/lib/commands/PROFILE_SEARCH.spec.ts +++ b/packages/search/lib/commands/PROFILE_SEARCH.spec.ts @@ -6,39 +6,90 @@ import { parseArgs } from '@redis/client/lib/commands/generic-transformers'; import { DEFAULT_DIALECT } from '../dialect/default'; describe('PROFILE SEARCH', () => { - describe('transformArguments', () => { - it('without options', () => { - assert.deepEqual( - parseArgs(PROFILE_SEARCH, 'index', 'query'), - ['FT.PROFILE', 'index', 'SEARCH', 'QUERY', 'query', 'DIALECT', DEFAULT_DIALECT] - ); - }); - - it('with options', () => { - assert.deepEqual( - parseArgs(PROFILE_SEARCH, 'index', 'query', { - LIMITED: true, - VERBATIM: true, - INKEYS: 'key' - }), - ['FT.PROFILE', 'index', 'SEARCH', 'LIMITED', 'QUERY', 'query', - 'VERBATIM', 'INKEYS', '1', 'key', 'DIALECT', DEFAULT_DIALECT] - ); - }); + describe('transformArguments', () => { + it('without options', () => { + assert.deepEqual( + parseArgs(PROFILE_SEARCH, 'index', 'query'), + ['FT.PROFILE', 'index', 'SEARCH', 'QUERY', 'query', 'DIALECT', DEFAULT_DIALECT] + ); }); - testUtils.testWithClient('client.ft.search', async client => { - await Promise.all([ - client.ft.create('index', { - field: SCHEMA_FIELD_TYPE.NUMERIC - }), - client.hSet('1', 'field', '1') - ]); - - const res = await client.ft.profileSearch('index', '*'); - assert.strictEqual('None', res.profile.warning); - assert.ok(typeof res.profile.iteratorsProfile.counter === 'number'); - assert.ok(typeof res.profile.parsingTime === 'string'); - assert.ok(res.results.total == 1); - }, GLOBAL.SERVERS.OPEN); + it('with options', () => { + assert.deepEqual( + parseArgs(PROFILE_SEARCH, 'index', 'query', { + LIMITED: true, + VERBATIM: true, + INKEYS: 'key' + }), + ['FT.PROFILE', 'index', 'SEARCH', 'LIMITED', 'QUERY', 'query', + 'VERBATIM', 'INKEYS', '1', 'key', 'DIALECT', DEFAULT_DIALECT] + ); + }); + }); + + testUtils.testWithClientIfVersionWithinRange([[8], 'LATEST'], 'client.ft.search', async client => { + await Promise.all([ + client.ft.create('index', { + field: SCHEMA_FIELD_TYPE.NUMERIC + }), + client.hSet('1', 'field', '1') + ]); + + const normalizeObject = obj => JSON.parse(JSON.stringify(obj)); + + const res = await client.ft.profileSearch('index', '*'); + + const normalizedRes = normalizeObject(res); + assert.equal(normalizedRes.results.total, 1); + + assert.ok(normalizedRes.profile[0] === 'Shards'); + assert.ok(Array.isArray(normalizedRes.profile[1])); + assert.ok(normalizedRes.profile[2] === 'Coordinator'); + assert.ok(Array.isArray(normalizedRes.profile[3])); + + const shardProfile = normalizedRes.profile[1][0]; + assert.ok(shardProfile.includes('Total profile time')); + assert.ok(shardProfile.includes('Parsing time')); + assert.ok(shardProfile.includes('Pipeline creation time')); + assert.ok(shardProfile.includes('Warning')); + assert.ok(shardProfile.includes('Iterators profile')); + ; + + }, GLOBAL.SERVERS.OPEN); + + + + + + testUtils.testWithClientIfVersionWithinRange([[7, 2, 0], [7, 4, 0]], 'client.ft.search', async client => { + await Promise.all([ + client.ft.create('index', { + field: SCHEMA_FIELD_TYPE.NUMERIC + }), + client.hSet('1', 'field', '1') + ]); + + const normalizeObject = obj => JSON.parse(JSON.stringify(obj)); + + const res = await client.ft.profileSearch('index', '*'); + + const normalizedRes = normalizeObject(res); + assert.equal(normalizedRes.results.total, 1); + + assert.ok(Array.isArray(normalizedRes.profile)); + assert.equal(normalizedRes.profile[0][0], 'Total profile time'); + assert.equal(normalizedRes.profile[1][0], 'Parsing time'); + assert.equal(normalizedRes.profile[2][0], 'Pipeline creation time'); + assert.equal(normalizedRes.profile[3][0], 'Warning'); + assert.equal(normalizedRes.profile[4][0], 'Iterators profile'); + assert.equal(normalizedRes.profile[5][0], 'Result processors profile'); + + const iteratorsProfile = normalizedRes.profile[4][1]; + assert.equal(iteratorsProfile[0], 'Type'); + assert.equal(iteratorsProfile[1], 'WILDCARD'); + assert.equal(iteratorsProfile[2], 'Time'); + assert.equal(iteratorsProfile[4], 'Counter'); + + }, GLOBAL.SERVERS.OPEN); + }); diff --git a/packages/search/lib/commands/PROFILE_SEARCH.ts b/packages/search/lib/commands/PROFILE_SEARCH.ts index 86ce85ba7f1..b13dbebe996 100644 --- a/packages/search/lib/commands/PROFILE_SEARCH.ts +++ b/packages/search/lib/commands/PROFILE_SEARCH.ts @@ -1,23 +1,19 @@ import { CommandParser } from '@redis/client/dist/lib/client/parser'; -import { Command, RedisArgument, ReplyUnion } from "@redis/client/dist/lib/RESP/types"; -import { AggregateReply } from "./AGGREGATE"; -import SEARCH, { FtSearchOptions, SearchRawReply, SearchReply, parseSearchOptions } from "./SEARCH"; +import { ArrayReply, Command, RedisArgument, ReplyUnion, TuplesReply, UnwrapReply } from '@redis/client/dist/lib/RESP/types'; +import { AggregateReply } from './AGGREGATE'; +import SEARCH, { FtSearchOptions, SearchRawReply, SearchReply, parseSearchOptions } from './SEARCH'; -export type ProfileRawReply = [ - results: T, - profile: [ - _: string, - TotalProfileTime: string, - _: string, - ParsingTime: string, - _: string, - PipelineCreationTime: string, - _: string, - IteratorsProfile: Array - ] -]; +export type ProfileRawReplyResp2 = TuplesReply<[ + T, + ArrayReply +]>; -type ProfileSearchRawReply = ProfileRawReply; +type ProfileSearchResponseResp2 = ProfileRawReplyResp2; + +export interface ProfileReplyResp2 { + results: SearchReply | AggregateReply; + profile: ReplyUnion; +} export interface ProfileOptions { LIMITED?: true; @@ -43,108 +39,13 @@ export default { parseSearchOptions(parser, options); }, transformReply: { - 2: (reply: ProfileSearchRawReply, withoutDocuments: boolean): ProfileReply => { + 2: (reply: UnwrapReply): ProfileReplyResp2 => { return { results: SEARCH.transformReply[2](reply[0]), - profile: transformProfile(reply[1]) - } + profile: reply[1] + }; }, - 3: undefined as unknown as () => ReplyUnion + 3: (reply: ReplyUnion): ReplyUnion => reply }, unstableResp3: true } as const satisfies Command; - -export interface ProfileReply { - results: SearchReply | AggregateReply; - profile: ProfileData; -} - -interface ChildIterator { - type?: string, - counter?: number, - term?: string, - size?: number, - time?: string, - childIterators?: Array -} - -interface IteratorsProfile { - type?: string, - counter?: number, - queryType?: string, - time?: string, - childIterators?: Array -} - -interface ProfileData { - totalProfileTime: string, - parsingTime: string, - pipelineCreationTime: string, - warning: string, - iteratorsProfile: IteratorsProfile -} - -export function transformProfile(reply: Array): ProfileData{ - return { - totalProfileTime: reply[0][1], - parsingTime: reply[1][1], - pipelineCreationTime: reply[2][1], - warning: reply[3][1] ? reply[3][1] : 'None', - iteratorsProfile: transformIterators(reply[4][1]) - }; -} - -function transformIterators(IteratorsProfile: Array): IteratorsProfile { - var res: IteratorsProfile = {}; - for (let i = 0; i < IteratorsProfile.length; i += 2) { - const value = IteratorsProfile[i+1]; - switch (IteratorsProfile[i]) { - case 'Type': - res.type = value; - break; - case 'Counter': - res.counter = value; - break; - case 'Time': - res.time = value; - break; - case 'Query type': - res.queryType = value; - break; - case 'Child iterators': - res.childIterators = value.map(transformChildIterators); - break; - } - } - - return res; -} - -function transformChildIterators(IteratorsProfile: Array): ChildIterator { - var res: ChildIterator = {}; - for (let i = 1; i < IteratorsProfile.length; i += 2) { - const value = IteratorsProfile[i+1]; - switch (IteratorsProfile[i]) { - case 'Type': - res.type = value; - break; - case 'Counter': - res.counter = value; - break; - case 'Time': - res.time = value; - break; - case 'Size': - res.size = value; - break; - case 'Term': - res.term = value; - break; - case 'Child iterators': - res.childIterators = value.map(transformChildIterators); - break; - } - } - - return res; -} \ No newline at end of file diff --git a/packages/search/lib/commands/SUGGET.spec.ts b/packages/search/lib/commands/SUGGET.spec.ts index e30c62afd67..b82ea547782 100644 --- a/packages/search/lib/commands/SUGGET.spec.ts +++ b/packages/search/lib/commands/SUGGET.spec.ts @@ -28,13 +28,23 @@ describe('FT.SUGGET', () => { }); describe('client.ft.sugGet', () => { - testUtils.testWithClient('null', async client => { - assert.equal( + + testUtils.testWithClientIfVersionWithinRange([[8], 'LATEST'], 'null', async client => { + assert.deepStrictEqual( await client.ft.sugGet('key', 'prefix'), - null + [] ); }, GLOBAL.SERVERS.OPEN); + + + testUtils.testWithClientIfVersionWithinRange([[6, 2, 0], [7, 4, 0]], 'null', async client => { + assert.deepStrictEqual( + await client.ft.sugGet('key', 'prefix'), + null + ); + }, GLOBAL.SERVERS.OPEN) + testUtils.testWithClient('with suggestions', async client => { const [, reply] = await Promise.all([ client.ft.sugAdd('key', 'string', 1), diff --git a/packages/search/lib/commands/SUGGET_WITHPAYLOADS.spec.ts b/packages/search/lib/commands/SUGGET_WITHPAYLOADS.spec.ts index 160d7e3eb7c..c01b87e2892 100644 --- a/packages/search/lib/commands/SUGGET_WITHPAYLOADS.spec.ts +++ b/packages/search/lib/commands/SUGGET_WITHPAYLOADS.spec.ts @@ -11,14 +11,21 @@ describe('FT.SUGGET WITHPAYLOADS', () => { ); }); - describe('client.ft.sugGetWithPayloads', () => { - testUtils.testWithClient('null', async client => { - assert.equal( - await client.ft.sugGetWithPayloads('key', 'prefix'), - null - ); - }, GLOBAL.SERVERS.OPEN); + testUtils.testWithClientIfVersionWithinRange([[8], 'LATEST'], 'null', async client => { + assert.deepStrictEqual( + await client.ft.sugGetWithPayloads('key', 'prefix'), + [] + ); + }, GLOBAL.SERVERS.OPEN); + + testUtils.testWithClientIfVersionWithinRange([[6], [7, 4, 0]], 'null', async client => { + assert.deepStrictEqual( + await client.ft.sugGetWithPayloads('key', 'prefix'), + null + ); + }, GLOBAL.SERVERS.OPEN); + describe('with suggestions', () => { testUtils.testWithClient('with suggestions', async client => { const [, reply] = await Promise.all([ client.ft.sugAdd('key', 'string', 1, { diff --git a/packages/search/lib/commands/SUGGET_WITHSCORES.spec.ts b/packages/search/lib/commands/SUGGET_WITHSCORES.spec.ts index 262defb7933..50db89ffe99 100644 --- a/packages/search/lib/commands/SUGGET_WITHSCORES.spec.ts +++ b/packages/search/lib/commands/SUGGET_WITHSCORES.spec.ts @@ -12,14 +12,15 @@ describe('FT.SUGGET WITHSCORES', () => { }); describe('client.ft.sugGetWithScores', () => { - testUtils.testWithClient('null', async client => { - assert.equal( + + testUtils.testWithClientIfVersionWithinRange([[8],'LATEST'], 'null', async client => { + assert.deepStrictEqual( await client.ft.sugGetWithScores('key', 'prefix'), - null + [] ); }, GLOBAL.SERVERS.OPEN); - testUtils.testWithClient('with suggestions', async client => { + testUtils.testWithClientIfVersionWithinRange([[8],'LATEST'],'with suggestions', async client => { const [, reply] = await Promise.all([ client.ft.sugAdd('key', 'string', 1), client.ft.sugGetWithScores('key', 's') diff --git a/packages/search/lib/commands/SUGGET_WITHSCORES_WITHPAYLOADS.spec.ts b/packages/search/lib/commands/SUGGET_WITHSCORES_WITHPAYLOADS.spec.ts index 573708f689e..96eb473159f 100644 --- a/packages/search/lib/commands/SUGGET_WITHSCORES_WITHPAYLOADS.spec.ts +++ b/packages/search/lib/commands/SUGGET_WITHSCORES_WITHPAYLOADS.spec.ts @@ -12,14 +12,14 @@ describe('FT.SUGGET WITHSCORES WITHPAYLOADS', () => { }); describe('client.ft.sugGetWithScoresWithPayloads', () => { - testUtils.testWithClient('null', async client => { - assert.equal( + testUtils.testWithClientIfVersionWithinRange([[8], 'LATEST'], 'null', async client => { + assert.deepStrictEqual( await client.ft.sugGetWithScoresWithPayloads('key', 'prefix'), - null + [] ); }, GLOBAL.SERVERS.OPEN); - testUtils.testWithClient('with suggestions', async client => { + testUtils.testWithClientIfVersionWithinRange([[8], 'LATEST'], 'with suggestions', async client => { const [, reply] = await Promise.all([ client.ft.sugAdd('key', 'string', 1, { PAYLOAD: 'payload' diff --git a/packages/search/lib/commands/index.ts b/packages/search/lib/commands/index.ts index 00706a70c2e..7aa3f061bf7 100644 --- a/packages/search/lib/commands/index.ts +++ b/packages/search/lib/commands/index.ts @@ -48,9 +48,21 @@ export default { aliasDel: ALIASDEL, ALIASUPDATE, aliasUpdate: ALIASUPDATE, + /** + * @deprecated Redis >=8 uses the standard CONFIG command + */ CONFIG_GET, + /** + * @deprecated Redis >=8 uses the standard CONFIG command + */ configGet: CONFIG_GET, + /** + * @deprecated Redis >=8 uses the standard CONFIG command + */ CONFIG_SET, + /** + * @deprecated Redis >=8 uses the standard CONFIG command + */ configSet: CONFIG_SET, CREATE, create: CREATE, diff --git a/packages/search/lib/test-utils.ts b/packages/search/lib/test-utils.ts index ce43a37bc21..1318676042e 100644 --- a/packages/search/lib/test-utils.ts +++ b/packages/search/lib/test-utils.ts @@ -1,21 +1,32 @@ import TestUtils from '@redis/test-utils'; import RediSearch from '.'; +import { RespVersions } from '@redis/client'; -export default new TestUtils({ - dockerImageName: 'redis/redis-stack', - dockerImageVersionArgument: 'redisearch-version', - defaultDockerVersion: '7.4.0-v1' +export default TestUtils.createFromConfig({ + dockerImageName: 'redislabs/client-libs-test', + dockerImageVersionArgument: 'redis-version', + defaultDockerVersion: '8.0-M04-pre' }); export const GLOBAL = { - SERVERS: { - OPEN: { - serverArguments: [], - clientOptions: { - modules: { - ft: RediSearch - } - } + SERVERS: { + OPEN: { + serverArguments: [], + clientOptions: { + modules: { + ft: RediSearch } + } + }, + OPEN_3: { + serverArguments: [], + clientOptions: { + RESP: 3 as RespVersions, + unstableResp3:true, + modules: { + ft: RediSearch + } + } } + } }; diff --git a/packages/search/package.json b/packages/search/package.json index 26cbbaf5ad9..985356cd230 100644 --- a/packages/search/package.json +++ b/packages/search/package.json @@ -9,7 +9,8 @@ "!dist/tsconfig.tsbuildinfo" ], "scripts": { - "test": "nyc -r text-summary -r lcov mocha -r tsx './lib/**/*.spec.ts'" + "test": "nyc -r text-summary -r lcov mocha -r tsx './lib/**/*.spec.ts'", + "test-sourcemap": "mocha -r ts-node/register/transpile-only './lib/**/*.spec.ts'" }, "peerDependencies": { "@redis/client": "^5.0.0-next.6" diff --git a/packages/test-utils/lib/dockers.ts b/packages/test-utils/lib/dockers.ts index bfb66603750..e3ff5edc38b 100644 --- a/packages/test-utils/lib/dockers.ts +++ b/packages/test-utils/lib/dockers.ts @@ -4,9 +4,11 @@ import { once } from 'node:events'; import { createClient } from '@redis/client/index'; import { setTimeout } from 'node:timers/promises'; // import { ClusterSlotsReply } from '@redis/client/dist/lib/commands/CLUSTER_SLOTS'; + +import { execFile as execFileCallback } from 'node:child_process'; import { promisify } from 'node:util'; -import { exec } from 'node:child_process'; -const execAsync = promisify(exec); + +const execAsync = promisify(execFileCallback); interface ErrorWithCode extends Error { code: string; @@ -46,11 +48,29 @@ export interface RedisServerDocker { dockerId: string; } -async function spawnRedisServerDocker({ image, version }: RedisServerDockerConfig, serverArguments: Array): Promise { - const port = (await portIterator.next()).value, - { stdout, stderr } = await execAsync( - `docker run -e REDIS_ARGS="--port ${port.toString()} ${serverArguments.join(' ')}" -d --network host ${image}:${version}` - ); +async function spawnRedisServerDocker({ + image, + version +}: RedisServerDockerConfig, serverArguments: Array): Promise { + const port = (await portIterator.next()).value; + const portStr = port.toString(); + + const dockerArgs = [ + 'run', + '-e', `PORT=${portStr}`, + '-d', + '--network', 'host', + `${image}:${version}`, + '--port', portStr + ]; + + if (serverArguments.length > 0) { + dockerArgs.push(...serverArguments); + } + + console.log(`[Docker] Spawning Redis container - Image: ${image}:${version}, Port: ${port}`); + + const { stdout, stderr } = await execAsync('docker', dockerArgs); if (!stdout) { throw new Error(`docker run error - ${stderr}`); @@ -65,7 +85,6 @@ async function spawnRedisServerDocker({ image, version }: RedisServerDockerConfi dockerId: stdout.trim() }; } - const RUNNING_SERVERS = new Map, ReturnType>(); export function spawnRedisServer(dockerConfig: RedisServerDockerConfig, serverArguments: Array): Promise { @@ -80,7 +99,7 @@ export function spawnRedisServer(dockerConfig: RedisServerDockerConfig, serverAr } async function dockerRemove(dockerId: string): Promise { - const { stderr } = await execAsync(`docker rm -f ${dockerId}`); + const { stderr } = await execAsync('docker', ['rm', '-f', dockerId]); if (stderr) { throw new Error(`docker rm error - ${stderr}`); } @@ -132,15 +151,15 @@ async function spawnRedisClusterNodeDockers( '5000' ], clientConfig).then(async replica => { - const requirePassIndex = serverArguments.findIndex((x)=>x==='--requirepass'); - if(requirePassIndex!==-1) { - const password = serverArguments[requirePassIndex+1]; - await replica.client.configSet({'masterauth': password}) + const requirePassIndex = serverArguments.findIndex((x) => x === '--requirepass'); + if (requirePassIndex !== -1) { + const password = serverArguments[requirePassIndex + 1]; + await replica.client.configSet({ 'masterauth': password }) } await replica.client.clusterMeet('127.0.0.1', master.docker.port); while ((await replica.client.clusterSlots()).length === 0) { - await setTimeout(50); + await setTimeout(25); } await replica.client.clusterReplicate( @@ -224,7 +243,7 @@ async function spawnRedisClusterDockers( while ( totalNodes(await client.clusterSlots()) !== nodes.length || !(await client.sendCommand(['CLUSTER', 'INFO'])).startsWith('cluster_state:ok') // TODO - ) { + ) { await setTimeout(50); } @@ -257,7 +276,7 @@ export function spawnRedisCluster( return runningCluster; } - const dockersPromise = spawnRedisClusterDockers(dockersConfig, serverArguments,clientConfig); + const dockersPromise = spawnRedisClusterDockers(dockersConfig, serverArguments, clientConfig); RUNNING_CLUSTERS.set(serverArguments, dockersPromise); return dockersPromise; diff --git a/packages/test-utils/lib/index.spec.ts b/packages/test-utils/lib/index.spec.ts new file mode 100644 index 00000000000..0f1e7552284 --- /dev/null +++ b/packages/test-utils/lib/index.spec.ts @@ -0,0 +1,106 @@ +import { strict as assert } from 'node:assert'; +import TestUtils from './index'; + +describe('TestUtils', () => { + describe('parseVersionNumber', () => { + it('should handle special versions', () => { + assert.deepStrictEqual(TestUtils.parseVersionNumber('latest'), [Infinity]); + assert.deepStrictEqual(TestUtils.parseVersionNumber('edge'), [Infinity]); + }); + + it('should parse simple version numbers', () => { + assert.deepStrictEqual(TestUtils.parseVersionNumber('7.4.0'), [7, 4, 0]); + }); + + it('should handle versions with multiple dashes and prefixes', () => { + assert.deepStrictEqual(TestUtils.parseVersionNumber('rs-7.4.0-v2'), [7, 4, 0]); + assert.deepStrictEqual(TestUtils.parseVersionNumber('rs-7.4.0'), [7, 4, 0]); + assert.deepStrictEqual(TestUtils.parseVersionNumber('7.4.0-v2'), [7, 4, 0]); + }); + + it('should handle various version number formats', () => { + assert.deepStrictEqual(TestUtils.parseVersionNumber('10.5'), [10, 5]); + assert.deepStrictEqual(TestUtils.parseVersionNumber('8.0.0'), [8, 0, 0]); + assert.deepStrictEqual(TestUtils.parseVersionNumber('rs-6.2.4-v1'), [6, 2, 4]); + }); + + it('should throw TypeError for invalid version strings', () => { + ['', 'invalid', 'rs-', 'v2', 'rs-invalid-v2'].forEach(version => { + assert.throws( + () => TestUtils.parseVersionNumber(version), + TypeError, + `Expected TypeError for version string: ${version}` + ); + }); + }); + }); +}); + + + +describe('Version Comparison', () => { + it('should correctly compare versions', () => { + const tests: [Array, Array, -1 | 0 | 1][] = [ + [[1, 0, 0], [1, 0, 0], 0], + [[2, 0, 0], [1, 9, 9], 1], + [[1, 9, 9], [2, 0, 0], -1], + [[1, 2, 3], [1, 2], 1], + [[1, 2], [1, 2, 3], -1], + [[1, 2, 0], [1, 2, 1], -1], + [[1], [1, 0, 0], 0], + [[2], [1, 9, 9], 1], + ]; + + tests.forEach(([a, b, expected]) => { + + assert.equal( + TestUtils.compareVersions(a, b), + expected, + `Failed comparing ${a.join('.')} with ${b.join('.')}: expected ${expected}` + ); + }); + }); + + it('should correctly compare versions', () => { + const tests: [Array, Array, -1 | 0 | 1][] = [ + [[1, 0, 0], [1, 0, 0], 0], + [[2, 0, 0], [1, 9, 9], 1], + [[1, 9, 9], [2, 0, 0], -1], + [[1, 2, 3], [1, 2], 1], + [[1, 2], [1, 2, 3], -1], + [[1, 2, 0], [1, 2, 1], -1], + [[1], [1, 0, 0], 0], + [[2], [1, 9, 9], 1], + ]; + + tests.forEach(([a, b, expected]) => { + + assert.equal( + TestUtils.compareVersions(a, b), + expected, + `Failed comparing ${a.join('.')} with ${b.join('.')}: expected ${expected}` + ); + }); + }) + it('isVersionInRange should work correctly', () => { + const tests: [Array, Array, Array, boolean][] = [ + [[7, 0, 0], [7, 0, 0], [7, 0, 0], true], + [[7, 0, 1], [7, 0, 0], [7, 0, 2], true], + [[7, 0, 0], [7, 0, 1], [7, 0, 2], false], + [[7, 0, 3], [7, 0, 1], [7, 0, 2], false], + [[7], [6, 0, 0], [8, 0, 0], true], + [[7, 1, 1], [7, 1, 0], [7, 1, 2], true], + [[6, 0, 0], [7, 0, 0], [8, 0, 0], false], + [[9, 0, 0], [7, 0, 0], [8, 0, 0], false] + ]; + + tests.forEach(([version, min, max, expected]) => { + const testUtils = new TestUtils({ string: version.join('.'), numbers: version }, "test") + assert.equal( + testUtils.isVersionInRange(min, max), + expected, + `Failed checking if ${version.join('.')} is between ${min.join('.')} and ${max.join('.')}: expected ${expected}` + ); + }); + }) +}); diff --git a/packages/test-utils/lib/index.ts b/packages/test-utils/lib/index.ts index 9dee350e31e..b48f11b02c7 100644 --- a/packages/test-utils/lib/index.ts +++ b/packages/test-utils/lib/index.ts @@ -19,12 +19,38 @@ import { RedisServerDockerConfig, spawnRedisServer, spawnRedisCluster } from './ import yargs from 'yargs'; import { hideBin } from 'yargs/helpers'; + interface TestUtilsConfig { + /** + * The name of the Docker image to use for spawning Redis test instances. + * This should be a valid Docker image name that contains a Redis server. + * + * @example 'redislabs/client-libs-test' + */ dockerImageName: string; + + /** + * The command-line argument name used to specify the Redis version. + * This argument can be passed when running tests / GH actions. + * + * @example + * If set to 'redis-version', you can run tests with: + * ```bash + * npm test -- --redis-version="6.2" + * ``` + */ dockerImageVersionArgument: string; + + /** + * The default Redis version to use if no version is specified via command-line arguments. + * Can be a specific version number (e.g., '6.2'), 'latest', or 'edge'. + * If not provided, defaults to 'latest'. + * + * @optional + * @default 'latest' + */ defaultDockerVersion?: string; } - interface CommonTestOptions { serverArguments: Array; minimumDockerVersion?: Array; @@ -83,22 +109,27 @@ interface Version { } export default class TestUtils { - static #parseVersionNumber(version: string): Array { + static parseVersionNumber(version: string): Array { if (version === 'latest' || version === 'edge') return [Infinity]; - const dashIndex = version.indexOf('-'); - return (dashIndex === -1 ? version : version.substring(0, dashIndex)) - .split('.') - .map(x => { - const value = Number(x); - if (Number.isNaN(value)) { - throw new TypeError(`${version} is not a valid redis version`); - } - return value; - }); - } + // Match complete version number patterns + const versionMatch = version.match(/(^|\-)\d+(\.\d+)*($|\-)/); + if (!versionMatch) { + throw new TypeError(`${version} is not a valid redis version`); + } + // Extract just the numbers and dots between first and last dash (or start/end) + const versionNumbers = versionMatch[0].replace(/^\-|\-$/g, ''); + + return versionNumbers.split('.').map(x => { + const value = Number(x); + if (Number.isNaN(value)) { + throw new TypeError(`${version} is not a valid redis version`); + } + return value; + }); + } static #getVersion(argumentName: string, defaultVersion = 'latest'): Version { return yargs(hideBin(process.argv)) .option(argumentName, { @@ -108,7 +139,7 @@ export default class TestUtils { .coerce(argumentName, (version: string) => { return { string: version, - numbers: TestUtils.#parseVersionNumber(version) + numbers: TestUtils.parseVersionNumber(version) }; }) .demandOption(argumentName) @@ -118,39 +149,76 @@ export default class TestUtils { readonly #VERSION_NUMBERS: Array; readonly #DOCKER_IMAGE: RedisServerDockerConfig; - constructor(config: TestUtilsConfig) { - const { string, numbers } = TestUtils.#getVersion(config.dockerImageVersionArgument, config.defaultDockerVersion); + constructor({ string, numbers }: Version, dockerImageName: string) { this.#VERSION_NUMBERS = numbers; this.#DOCKER_IMAGE = { - image: config.dockerImageName, + image: dockerImageName, version: string }; } + /** + * Creates a new TestUtils instance from a configuration object. + * + * @param config - Configuration object containing Docker image and version settings + * @param config.dockerImageName - The name of the Docker image to use for tests + * @param config.dockerImageVersionArgument - The command-line argument name for specifying Redis version + * @param config.defaultDockerVersion - Optional default Redis version if not specified via arguments + * @returns A new TestUtils instance configured with the provided settings + */ + public static createFromConfig(config: TestUtilsConfig) { + return new TestUtils( + TestUtils.#getVersion(config.dockerImageVersionArgument, + config.defaultDockerVersion), config.dockerImageName); + } + isVersionGreaterThan(minimumVersion: Array | undefined): boolean { if (minimumVersion === undefined) return true; - - const lastIndex = Math.min(this.#VERSION_NUMBERS.length, minimumVersion.length) - 1; - for (let i = 0; i < lastIndex; i++) { - if (this.#VERSION_NUMBERS[i] > minimumVersion[i]) { - return true; - } else if (minimumVersion[i] > this.#VERSION_NUMBERS[i]) { - return false; - } - } - - return this.#VERSION_NUMBERS[lastIndex] >= minimumVersion[lastIndex]; + return TestUtils.compareVersions(this.#VERSION_NUMBERS, minimumVersion) >= 0; } isVersionGreaterThanHook(minimumVersion: Array | undefined): void { - const isVersionGreaterThan = this.isVersionGreaterThan.bind(this); + + const isVersionGreaterThanHook = this.isVersionGreaterThan.bind(this); + const versionNumber = this.#VERSION_NUMBERS.join('.'); + const minimumVersionString = minimumVersion?.join('.'); before(function () { - if (!isVersionGreaterThan(minimumVersion)) { + if (!isVersionGreaterThanHook(minimumVersion)) { + console.warn(`TestUtils: Version ${versionNumber} is less than minimum version ${minimumVersionString}, skipping test`); return this.skip(); } }); } + isVersionInRange(minVersion: Array, maxVersion: Array): boolean { + return TestUtils.compareVersions(this.#VERSION_NUMBERS, minVersion) >= 0 && + TestUtils.compareVersions(this.#VERSION_NUMBERS, maxVersion) <= 0 + } + + /** + * Compares two semantic version arrays and returns: + * -1 if version a is less than version b + * 0 if version a equals version b + * 1 if version a is greater than version b + * + * @param a First version array + * @param b Second version array + * @returns -1 | 0 | 1 + */ + static compareVersions(a: Array, b: Array): -1 | 0 | 1 { + const maxLength = Math.max(a.length, b.length); + + const paddedA = [...a, ...Array(maxLength - a.length).fill(0)]; + const paddedB = [...b, ...Array(maxLength - b.length).fill(0)]; + + for (let i = 0; i < maxLength; i++) { + if (paddedA[i] > paddedB[i]) return 1; + if (paddedA[i] < paddedB[i]) return -1; + } + + return 0; + } + testWithClient< M extends RedisModules = {}, F extends RedisFunctions = {}, @@ -204,6 +272,27 @@ export default class TestUtils { }); } + testWithClientIfVersionWithinRange< + M extends RedisModules = {}, + F extends RedisFunctions = {}, + S extends RedisScripts = {}, + RESP extends RespVersions = 2, + TYPE_MAPPING extends TypeMapping = {} + >( + range: ([minVersion: Array, maxVersion: Array] | [minVersion: Array, 'LATEST']), + title: string, + fn: (client: RedisClientType) => unknown, + options: ClientTestOptions + ): void { + + if (this.isVersionInRange(range[0], range[1] === 'LATEST' ? [Infinity, Infinity, Infinity] : range[1])) { + return this.testWithClient(`${title} [${range[0].join('.')}] - [${(range[1] === 'LATEST') ? range[1] : range[1].join(".")}] `, fn, options) + } else { + console.warn(`Skipping test ${title} because server version ${this.#VERSION_NUMBERS.join('.')} is not within range ${range[0].join(".")} - ${range[1] !== 'LATEST' ? range[1].join(".") : 'LATEST'}`) + } + + } + testWithClientPool< M extends RedisModules = {}, F extends RedisFunctions = {}, diff --git a/packages/test-utils/package.json b/packages/test-utils/package.json index 5e291211b6c..f7373f6add1 100644 --- a/packages/test-utils/package.json +++ b/packages/test-utils/package.json @@ -3,6 +3,9 @@ "private": true, "main": "./dist/lib/index.js", "types": "./dist/lib/index.d.ts", + "scripts": { + "test": "nyc -r text-summary -r lcov mocha -r tsx './lib/**/*.spec.ts'" + }, "peerDependencies": { "@redis/client": "*" }, diff --git a/packages/time-series/lib/test-utils.ts b/packages/time-series/lib/test-utils.ts index 1cb5c8ed97b..0b7e940788f 100644 --- a/packages/time-series/lib/test-utils.ts +++ b/packages/time-series/lib/test-utils.ts @@ -1,10 +1,10 @@ import TestUtils from '@redis/test-utils'; import TimeSeries from '.'; -export default new TestUtils({ - dockerImageName: 'redis/redis-stack', - dockerImageVersionArgument: 'timeseries-version', - defaultDockerVersion: '7.4.0-v1' +export default TestUtils.createFromConfig({ + dockerImageName: 'redislabs/client-libs-test', + dockerImageVersionArgument: 'redis-version', + defaultDockerVersion: '8.0-M04-pre' }); export const GLOBAL = { From 8b4ed0059aabc26bde8cc228459be18836cd6f65 Mon Sep 17 00:00:00 2001 From: "Bobby I." Date: Wed, 5 Mar 2025 14:47:18 +0200 Subject: [PATCH 1506/1748] feat(entraid): add support for azure identity (#2901) This PR adds support for using Azure Identity's credential classes with Redis Enterprise Entra ID authentication. The main changes include: - Add a new factory method createForDefaultAzureCredential to enable using Azure Identity credentials - Add @azure/identity as a dependency to support the new authentication flow - Add support for DefaultAzureCredential, EnvironmentCredential, and any other TokenCredential implementation - Create a new AzureIdentityProvider to support DefaultAzureCredential - Update documentation and README with usage examples for DefaultAzureCredential - Add integration tests for the new authentication methods - Include a sample application demonstrating interactive browser authentication - Export constants for Redis scopes / credential mappers to simplify authentication configuration --- package-lock.json | 291 +++++++++++++++--- packages/entraid/README.md | 50 +++ .../entraid-integration.spec.ts | 113 +++++-- .../entraid/lib/azure-identity-provider.ts | 22 ++ .../entra-id-credentials-provider-factory.ts | 88 ++++-- .../lib/entraid-credentials-provider.ts | 81 ++++- .../entraid/lib/msal-identity-provider.ts | 20 +- packages/entraid/package.json | 2 + .../samples/interactive-browser/index.ts | 111 +++++++ 9 files changed, 655 insertions(+), 123 deletions(-) create mode 100644 packages/entraid/lib/azure-identity-provider.ts create mode 100644 packages/entraid/samples/interactive-browser/index.ts diff --git a/package-lock.json b/package-lock.json index 8fdd049a5b2..25a1dc9d51c 100644 --- a/package-lock.json +++ b/package-lock.json @@ -33,6 +33,214 @@ "node": ">=6.0.0" } }, + "node_modules/@azure/abort-controller": { + "version": "2.1.2", + "resolved": "https://registry.npmjs.org/@azure/abort-controller/-/abort-controller-2.1.2.tgz", + "integrity": "sha512-nBrLsEWm4J2u5LpAPjxADTlq3trDgVZZXHNKabeXZtpq3d3AbN/KGO82R87rdDz5/lYB024rtEf10/q0urNgsA==", + "license": "MIT", + "dependencies": { + "tslib": "^2.6.2" + }, + "engines": { + "node": ">=18.0.0" + } + }, + "node_modules/@azure/core-auth": { + "version": "1.9.0", + "resolved": "https://registry.npmjs.org/@azure/core-auth/-/core-auth-1.9.0.tgz", + "integrity": "sha512-FPwHpZywuyasDSLMqJ6fhbOK3TqUdviZNF8OqRGA4W5Ewib2lEEZ+pBsYcBa88B2NGO/SEnYPGhyBqNlE8ilSw==", + "license": "MIT", + "dependencies": { + "@azure/abort-controller": "^2.0.0", + "@azure/core-util": "^1.11.0", + "tslib": "^2.6.2" + }, + "engines": { + "node": ">=18.0.0" + } + }, + "node_modules/@azure/core-client": { + "version": "1.9.2", + "resolved": "https://registry.npmjs.org/@azure/core-client/-/core-client-1.9.2.tgz", + "integrity": "sha512-kRdry/rav3fUKHl/aDLd/pDLcB+4pOFwPPTVEExuMyaI5r+JBbMWqRbCY1pn5BniDaU3lRxO9eaQ1AmSMehl/w==", + "license": "MIT", + "dependencies": { + "@azure/abort-controller": "^2.0.0", + "@azure/core-auth": "^1.4.0", + "@azure/core-rest-pipeline": "^1.9.1", + "@azure/core-tracing": "^1.0.0", + "@azure/core-util": "^1.6.1", + "@azure/logger": "^1.0.0", + "tslib": "^2.6.2" + }, + "engines": { + "node": ">=18.0.0" + } + }, + "node_modules/@azure/core-rest-pipeline": { + "version": "1.19.0", + "resolved": "https://registry.npmjs.org/@azure/core-rest-pipeline/-/core-rest-pipeline-1.19.0.tgz", + "integrity": "sha512-bM3308LRyg5g7r3Twprtqww0R/r7+GyVxj4BafcmVPo4WQoGt5JXuaqxHEFjw2o3rvFZcUPiqJMg6WuvEEeVUA==", + "license": "MIT", + "dependencies": { + "@azure/abort-controller": "^2.0.0", + "@azure/core-auth": "^1.8.0", + "@azure/core-tracing": "^1.0.1", + "@azure/core-util": "^1.11.0", + "@azure/logger": "^1.0.0", + "http-proxy-agent": "^7.0.0", + "https-proxy-agent": "^7.0.0", + "tslib": "^2.6.2" + }, + "engines": { + "node": ">=18.0.0" + } + }, + "node_modules/@azure/core-tracing": { + "version": "1.2.0", + "resolved": "https://registry.npmjs.org/@azure/core-tracing/-/core-tracing-1.2.0.tgz", + "integrity": "sha512-UKTiEJPkWcESPYJz3X5uKRYyOcJD+4nYph+KpfdPRnQJVrZfk0KJgdnaAWKfhsBBtAf/D58Az4AvCJEmWgIBAg==", + "license": "MIT", + "dependencies": { + "tslib": "^2.6.2" + }, + "engines": { + "node": ">=18.0.0" + } + }, + "node_modules/@azure/core-util": { + "version": "1.11.0", + "resolved": "https://registry.npmjs.org/@azure/core-util/-/core-util-1.11.0.tgz", + "integrity": "sha512-DxOSLua+NdpWoSqULhjDyAZTXFdP/LKkqtYuxxz1SCN289zk3OG8UOpnCQAz/tygyACBtWp/BoO72ptK7msY8g==", + "license": "MIT", + "dependencies": { + "@azure/abort-controller": "^2.0.0", + "tslib": "^2.6.2" + }, + "engines": { + "node": ">=18.0.0" + } + }, + "node_modules/@azure/identity": { + "version": "4.7.0", + "resolved": "https://registry.npmjs.org/@azure/identity/-/identity-4.7.0.tgz", + "integrity": "sha512-6z/S2KorkbKaZ0DgZFVRdu7RCuATmMSTjKpuhj7YpjxkJ0vnJ7kTM3cpNgzFgk9OPYfZ31wrBEtC/iwAS4jQDA==", + "license": "MIT", + "dependencies": { + "@azure/abort-controller": "^2.0.0", + "@azure/core-auth": "^1.9.0", + "@azure/core-client": "^1.9.2", + "@azure/core-rest-pipeline": "^1.17.0", + "@azure/core-tracing": "^1.0.0", + "@azure/core-util": "^1.11.0", + "@azure/logger": "^1.0.0", + "@azure/msal-browser": "^4.2.0", + "@azure/msal-node": "^3.2.1", + "events": "^3.0.0", + "jws": "^4.0.0", + "open": "^10.1.0", + "stoppable": "^1.1.0", + "tslib": "^2.2.0" + }, + "engines": { + "node": ">=18.0.0" + } + }, + "node_modules/@azure/identity/node_modules/@azure/msal-common": { + "version": "15.2.0", + "resolved": "https://registry.npmjs.org/@azure/msal-common/-/msal-common-15.2.0.tgz", + "integrity": "sha512-HiYfGAKthisUYqHG1nImCf/uzcyS31wng3o+CycWLIM9chnYJ9Lk6jZ30Y6YiYYpTQ9+z/FGUpiKKekd3Arc0A==", + "license": "MIT", + "engines": { + "node": ">=0.8.0" + } + }, + "node_modules/@azure/identity/node_modules/@azure/msal-node": { + "version": "3.2.3", + "resolved": "https://registry.npmjs.org/@azure/msal-node/-/msal-node-3.2.3.tgz", + "integrity": "sha512-0eaPqBIWEAizeYiXdeHb09Iq0tvHJ17ztvNEaLdr/KcJJhJxbpkkEQf09DB+vKlFE0tzYi7j4rYLTXtES/InEQ==", + "license": "MIT", + "dependencies": { + "@azure/msal-common": "15.2.0", + "jsonwebtoken": "^9.0.0", + "uuid": "^8.3.0" + }, + "engines": { + "node": ">=16" + } + }, + "node_modules/@azure/identity/node_modules/jwa": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/jwa/-/jwa-2.0.0.tgz", + "integrity": "sha512-jrZ2Qx916EA+fq9cEAeCROWPTfCwi1IVHqT2tapuqLEVVDKFDENFw1oL+MwrTvH6msKxsd1YTDVw6uKEcsrLEA==", + "license": "MIT", + "dependencies": { + "buffer-equal-constant-time": "1.0.1", + "ecdsa-sig-formatter": "1.0.11", + "safe-buffer": "^5.0.1" + } + }, + "node_modules/@azure/identity/node_modules/jws": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/jws/-/jws-4.0.0.tgz", + "integrity": "sha512-KDncfTmOZoOMTFG4mBlG0qUIOlc03fmzH+ru6RgYVZhPkyiy/92Owlt/8UEN+a4TXR1FQetfIpJE8ApdvdVxTg==", + "license": "MIT", + "dependencies": { + "jwa": "^2.0.0", + "safe-buffer": "^5.0.1" + } + }, + "node_modules/@azure/identity/node_modules/open": { + "version": "10.1.0", + "resolved": "https://registry.npmjs.org/open/-/open-10.1.0.tgz", + "integrity": "sha512-mnkeQ1qP5Ue2wd+aivTD3NHd/lZ96Lu0jgf0pwktLPtx6cTZiH7tyeGRRHs0zX0rbrahXPnXlUnbeXyaBBuIaw==", + "license": "MIT", + "dependencies": { + "default-browser": "^5.2.1", + "define-lazy-prop": "^3.0.0", + "is-inside-container": "^1.0.0", + "is-wsl": "^3.1.0" + }, + "engines": { + "node": ">=18" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/@azure/logger": { + "version": "1.1.4", + "resolved": "https://registry.npmjs.org/@azure/logger/-/logger-1.1.4.tgz", + "integrity": "sha512-4IXXzcCdLdlXuCG+8UKEwLA1T1NHqUfanhXYHiQTn+6sfWCZXduqbtXDGceg3Ce5QxTGo7EqmbV6Bi+aqKuClQ==", + "license": "MIT", + "dependencies": { + "tslib": "^2.6.2" + }, + "engines": { + "node": ">=18.0.0" + } + }, + "node_modules/@azure/msal-browser": { + "version": "4.4.0", + "resolved": "https://registry.npmjs.org/@azure/msal-browser/-/msal-browser-4.4.0.tgz", + "integrity": "sha512-rU6juYXk67CKQmpgi6fDgZoPQ9InZ1760z1BSAH7RbeIc4lHZM/Tu+H0CyRk7cnrfvTkexyYE4pjYhMghpzheA==", + "license": "MIT", + "dependencies": { + "@azure/msal-common": "15.2.0" + }, + "engines": { + "node": ">=0.8.0" + } + }, + "node_modules/@azure/msal-browser/node_modules/@azure/msal-common": { + "version": "15.2.0", + "resolved": "https://registry.npmjs.org/@azure/msal-common/-/msal-common-15.2.0.tgz", + "integrity": "sha512-HiYfGAKthisUYqHG1nImCf/uzcyS31wng3o+CycWLIM9chnYJ9Lk6jZ30Y6YiYYpTQ9+z/FGUpiKKekd3Arc0A==", + "license": "MIT", + "engines": { + "node": ">=0.8.0" + } + }, "node_modules/@azure/msal-common": { "version": "14.16.0", "resolved": "https://registry.npmjs.org/@azure/msal-common/-/msal-common-14.16.0.tgz", @@ -847,10 +1055,6 @@ "node": ">=12" } }, - "node_modules/@redis/authx": { - "resolved": "packages/authx", - "link": true - }, "node_modules/@redis/bloom": { "resolved": "packages/bloom", "link": true @@ -1128,7 +1332,6 @@ }, "node_modules/agent-base": { "version": "7.1.0", - "dev": true, "license": "MIT", "dependencies": { "debug": "^4.3.4" @@ -1676,7 +1879,6 @@ }, "node_modules/bundle-name": { "version": "4.1.0", - "dev": true, "license": "MIT", "dependencies": { "run-applescript": "^7.0.0" @@ -2161,7 +2363,6 @@ }, "node_modules/debug": { "version": "4.3.4", - "dev": true, "license": "MIT", "dependencies": { "ms": "2.1.2" @@ -2177,7 +2378,6 @@ }, "node_modules/debug/node_modules/ms": { "version": "2.1.2", - "dev": true, "license": "MIT" }, "node_modules/decamelize": { @@ -2223,7 +2423,6 @@ }, "node_modules/default-browser": { "version": "5.2.1", - "dev": true, "license": "MIT", "dependencies": { "bundle-name": "^4.1.0", @@ -2238,7 +2437,6 @@ }, "node_modules/default-browser-id": { "version": "5.0.0", - "dev": true, "license": "MIT", "engines": { "node": ">=18" @@ -2300,7 +2498,6 @@ }, "node_modules/define-lazy-prop": { "version": "3.0.0", - "dev": true, "license": "MIT", "engines": { "node": ">=12" @@ -2736,6 +2933,15 @@ "node": ">= 0.6" } }, + "node_modules/events": { + "version": "3.3.0", + "resolved": "https://registry.npmjs.org/events/-/events-3.3.0.tgz", + "integrity": "sha512-mQw+2fkQbALzQ7V0MY0IqdnXNOeTtP4r0lN9z7AAawCXgqea7bDii20AYrIBrFd/Hx0M2Ocz6S111CaFkUcb0Q==", + "license": "MIT", + "engines": { + "node": ">=0.8.x" + } + }, "node_modules/execa": { "version": "8.0.1", "dev": true, @@ -3689,7 +3895,6 @@ }, "node_modules/http-proxy-agent": { "version": "7.0.0", - "dev": true, "license": "MIT", "dependencies": { "agent-base": "^7.1.0", @@ -3713,7 +3918,6 @@ }, "node_modules/https-proxy-agent": { "version": "7.0.2", - "dev": true, "license": "MIT", "dependencies": { "agent-base": "^7.0.2", @@ -4087,7 +4291,6 @@ }, "node_modules/is-docker": { "version": "3.0.0", - "dev": true, "license": "MIT", "bin": { "is-docker": "cli.js" @@ -4142,7 +4345,6 @@ }, "node_modules/is-inside-container": { "version": "1.0.0", - "dev": true, "license": "MIT", "dependencies": { "is-docker": "^3.0.0" @@ -4391,7 +4593,6 @@ }, "node_modules/is-wsl": { "version": "3.1.0", - "dev": true, "license": "MIT", "dependencies": { "is-inside-container": "^1.0.0" @@ -6654,7 +6855,6 @@ }, "node_modules/run-applescript": { "version": "7.0.0", - "dev": true, "license": "MIT", "engines": { "node": ">=18" @@ -7146,6 +7346,16 @@ "node": ">= 0.4" } }, + "node_modules/stoppable": { + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/stoppable/-/stoppable-1.1.0.tgz", + "integrity": "sha512-KXDYZ9dszj6bzvnEMRYvxgeTHU74QBFL54XKtP3nyMuJ81CFYtABZ3bAzL2EdFUaEwJOBOgENyFj3R7oTzDyyw==", + "license": "MIT", + "engines": { + "node": ">=4", + "npm": ">=6" + } + }, "node_modules/string_decoder": { "version": "1.3.0", "dev": true, @@ -7368,7 +7578,6 @@ }, "node_modules/tslib": { "version": "2.6.2", - "dev": true, "license": "0BSD" }, "node_modules/tsx": { @@ -8129,6 +8338,7 @@ "packages/authx": { "name": "@redis/authx", "version": "5.0.0-next.5", + "extraneous": true, "license": "MIT", "dependencies": { "@azure/msal-node": "^2.16.1" @@ -8143,7 +8353,7 @@ }, "packages/bloom": { "name": "@redis/bloom", - "version": "5.0.0-next.5", + "version": "5.0.0-next.6", "license": "MIT", "devDependencies": { "@redis/test-utils": "*" @@ -8152,12 +8362,12 @@ "node": ">= 18" }, "peerDependencies": { - "@redis/client": "^5.0.0-next.5" + "@redis/client": "^5.0.0-next.6" } }, "packages/client": { "name": "@redis/client", - "version": "5.0.0-next.5", + "version": "5.0.0-next.6", "license": "MIT", "dependencies": { "cluster-key-slot": "1.1.2" @@ -8169,16 +8379,14 @@ }, "engines": { "node": ">= 18" - }, - "peerDependencies": { - "@redis/authx": "^5.0.0-next.5" } }, "packages/entraid": { "name": "@redis/entraid", - "version": "5.0.0-next.5", + "version": "5.0.0-next.6", "license": "MIT", "dependencies": { + "@azure/identity": "4.7.0", "@azure/msal-node": "^2.16.1" }, "devDependencies": { @@ -8194,8 +8402,7 @@ "node": ">= 18" }, "peerDependencies": { - "@redis/authx": "^5.0.0-next.5", - "@redis/client": "^5.0.0-next.5" + "@redis/client": "^5.0.0-next.6" } }, "packages/entraid/node_modules/@types/node": { @@ -8217,7 +8424,7 @@ }, "packages/graph": { "name": "@redis/graph", - "version": "5.0.0-next.5", + "version": "5.0.0-next.6", "license": "MIT", "devDependencies": { "@redis/test-utils": "*" @@ -8226,12 +8433,12 @@ "node": ">= 18" }, "peerDependencies": { - "@redis/client": "^5.0.0-next.5" + "@redis/client": "^5.0.0-next.6" } }, "packages/json": { "name": "@redis/json", - "version": "5.0.0-next.5", + "version": "5.0.0-next.6", "license": "MIT", "devDependencies": { "@redis/test-utils": "*" @@ -8240,19 +8447,19 @@ "node": ">= 18" }, "peerDependencies": { - "@redis/client": "^5.0.0-next.5" + "@redis/client": "^5.0.0-next.6" } }, "packages/redis": { - "version": "5.0.0-next.5", + "version": "5.0.0-next.6", "license": "MIT", "dependencies": { - "@redis/bloom": "5.0.0-next.5", - "@redis/client": "5.0.0-next.5", - "@redis/graph": "5.0.0-next.5", - "@redis/json": "5.0.0-next.5", - "@redis/search": "5.0.0-next.5", - "@redis/time-series": "5.0.0-next.5" + "@redis/bloom": "5.0.0-next.6", + "@redis/client": "5.0.0-next.6", + "@redis/graph": "5.0.0-next.6", + "@redis/json": "5.0.0-next.6", + "@redis/search": "5.0.0-next.6", + "@redis/time-series": "5.0.0-next.6" }, "engines": { "node": ">= 18" @@ -8260,7 +8467,7 @@ }, "packages/search": { "name": "@redis/search", - "version": "5.0.0-next.5", + "version": "5.0.0-next.6", "license": "MIT", "devDependencies": { "@redis/test-utils": "*" @@ -8269,7 +8476,7 @@ "node": ">= 18" }, "peerDependencies": { - "@redis/client": "^5.0.0-next.5" + "@redis/client": "^5.0.0-next.6" } }, "packages/test-utils": { @@ -8338,7 +8545,7 @@ }, "packages/time-series": { "name": "@redis/time-series", - "version": "5.0.0-next.5", + "version": "5.0.0-next.6", "license": "MIT", "devDependencies": { "@redis/test-utils": "*" @@ -8347,7 +8554,7 @@ "node": ">= 18" }, "peerDependencies": { - "@redis/client": "^5.0.0-next.5" + "@redis/client": "^5.0.0-next.6" } } } diff --git a/packages/entraid/README.md b/packages/entraid/README.md index eec88d71360..f2212848455 100644 --- a/packages/entraid/README.md +++ b/packages/entraid/README.md @@ -11,6 +11,7 @@ Secure token-based authentication for Redis clients using Microsoft Entra ID (fo - Managed identities (system-assigned and user-assigned) - Service principals (with or without certificates) - Authorization Code with PKCE flow + - DefaultAzureCredential from @azure/identity - Built-in retry mechanisms for transient failures ## Installation @@ -30,6 +31,7 @@ The first step to using @redis/entraid is choosing the right credentials provide - `createForClientCredentials`: Use when authenticating with a service principal using client secret - `createForClientCredentialsWithCertificate`: Use when authenticating with a service principal using a certificate - `createForAuthorizationCodeWithPKCE`: Use for interactive authentication flows in user applications +- `createForDefaultAzureCredential`: Use when you want to leverage Azure Identity's DefaultAzureCredential ## Usage Examples @@ -82,6 +84,54 @@ const provider = EntraIdCredentialsProviderFactory.createForUserAssignedManagedI }); ``` +### DefaultAzureCredential Authentication + +tip: see a real sample here: [samples/interactive-browser/index.ts](./samples/interactive-browser/index.ts) + +The DefaultAzureCredential from @azure/identity provides a simplified authentication experience that automatically tries different authentication methods based on the environment. This is especially useful for applications that need to work in different environments (local development, CI/CD, and production). + +```typescript +import { createClient } from '@redis/client'; +import { DefaultAzureCredential } from '@azure/identity'; +import { EntraIdCredentialsProviderFactory, REDIS_SCOPE_DEFAULT } from '@redis/entraid/dist/lib/entra-id-credentials-provider-factory'; + +// Create a DefaultAzureCredential instance +const credential = new DefaultAzureCredential(); + +// Create a provider using DefaultAzureCredential +const provider = EntraIdCredentialsProviderFactory.createForDefaultAzureCredential({ + // Use the same parameters you would pass to credential.getToken() + credential, + scopes: REDIS_SCOPE_DEFAULT, // The Redis scope + // Optional additional parameters for getToken + options: { + // Any options you would normally pass to credential.getToken() + }, + tokenManagerConfig: { + expirationRefreshRatio: 0.8 + } +}); + +const client = createClient({ + url: 'redis://your-host', + credentialsProvider: provider +}); + +await client.connect(); +``` + +#### Important Notes on Using DefaultAzureCredential + +When using the `createForDefaultAzureCredential` method, you need to: + +1. Create your own instance of `DefaultAzureCredential` +2. Pass the same parameters to the factory method that you would use with the `getToken()` method: + - `scopes`: The Redis scope (use the exported `REDIS_SCOPE_DEFAULT` constant) + - `options`: Any additional options for the getToken method + +This factory method creates a wrapper around DefaultAzureCredential that adapts it to the Redis client's +authentication system, while maintaining all the flexibility of the original Azure Identity authentication. + ## Important Limitations ### RESP2 PUB/SUB Limitations diff --git a/packages/entraid/integration-tests/entraid-integration.spec.ts b/packages/entraid/integration-tests/entraid-integration.spec.ts index deb1d47dec1..4d078a01ede 100644 --- a/packages/entraid/integration-tests/entraid-integration.spec.ts +++ b/packages/entraid/integration-tests/entraid-integration.spec.ts @@ -1,6 +1,7 @@ +import { DefaultAzureCredential, EnvironmentCredential } from '@azure/identity'; import { BasicAuth } from '@redis/client/dist/lib/authx'; import { createClient } from '@redis/client'; -import { EntraIdCredentialsProviderFactory } from '../lib/entra-id-credentials-provider-factory'; +import { EntraIdCredentialsProviderFactory, REDIS_SCOPE_DEFAULT } from '../lib/entra-id-credentials-provider-factory'; import { strict as assert } from 'node:assert'; import { spy, SinonSpy } from 'sinon'; import { randomUUID } from 'crypto'; @@ -51,6 +52,35 @@ describe('EntraID Integration Tests', () => { ); }); + it('client with DefaultAzureCredential should be able to authenticate/re-authenticate', async () => { + + const azureCredential = new DefaultAzureCredential(); + + await runAuthenticationTest(() => + EntraIdCredentialsProviderFactory.createForDefaultAzureCredential({ + credential: azureCredential, + scopes: REDIS_SCOPE_DEFAULT, + tokenManagerConfig: { + expirationRefreshRatio: 0.00001 + } + }) + , { testingDefaultAzureCredential: true }); + }); + + it('client with EnvironmentCredential should be able to authenticate/re-authenticate', async () => { + const envCredential = new EnvironmentCredential(); + + await runAuthenticationTest(() => + EntraIdCredentialsProviderFactory.createForDefaultAzureCredential({ + credential: envCredential, + scopes: REDIS_SCOPE_DEFAULT, + tokenManagerConfig: { + expirationRefreshRatio: 0.00001 + } + }) + , { testingDefaultAzureCredential: true }); + }); + interface TestConfig { clientId: string; clientSecret: string; @@ -83,15 +113,15 @@ describe('EntraID Integration Tests', () => { }); return { - endpoints: await loadFromFile(requiredEnvVars.REDIS_ENDPOINTS_CONFIG_PATH), - clientId: requiredEnvVars.AZURE_CLIENT_ID, - clientSecret: requiredEnvVars.AZURE_CLIENT_SECRET, - authority: requiredEnvVars.AZURE_AUTHORITY, - tenantId: requiredEnvVars.AZURE_TENANT_ID, - redisScopes: requiredEnvVars.AZURE_REDIS_SCOPES, - cert: requiredEnvVars.AZURE_CERT, - privateKey: requiredEnvVars.AZURE_PRIVATE_KEY, - userAssignedManagedId: requiredEnvVars.AZURE_USER_ASSIGNED_MANAGED_ID + endpoints: await loadFromFile(requiredEnvVars.REDIS_ENDPOINTS_CONFIG_PATH as string), + clientId: requiredEnvVars.AZURE_CLIENT_ID as string, + clientSecret: requiredEnvVars.AZURE_CLIENT_SECRET as string, + authority: requiredEnvVars.AZURE_AUTHORITY as string, + tenantId: requiredEnvVars.AZURE_TENANT_ID as string, + redisScopes: requiredEnvVars.AZURE_REDIS_SCOPES as string, + cert: requiredEnvVars.AZURE_CERT as string, + privateKey: requiredEnvVars.AZURE_PRIVATE_KEY as string, + userAssignedManagedId: requiredEnvVars.AZURE_USER_ASSIGNED_MANAGED_ID as string }; }; @@ -127,12 +157,22 @@ describe('EntraID Integration Tests', () => { } }; - const validateTokens = (reAuthSpy: SinonSpy) => { + /** + * Validates authentication tokens generated during re-authentication + * + * @param reAuthSpy - The Sinon spy on the reAuthenticate method + * @param skipUniqueCheckForDefaultAzureCredential - Skip the unique check for DefaultAzureCredential as there are no guarantees that the tokens will be unique + * if the test is using default azure credential + */ + const validateTokens = (reAuthSpy: SinonSpy, skipUniqueCheckForDefaultAzureCredential: boolean) => { assert(reAuthSpy.callCount >= 1, `reAuthenticate should have been called at least once, but was called ${reAuthSpy.callCount} times`); const tokenDetails: TokenDetail[] = reAuthSpy.getCalls().map(call => { const creds = call.args[0] as BasicAuth; + if (!creds.password) { + throw new Error('Expected password to be set in BasicAuth credentials'); + } const tokenPayload = JSON.parse( Buffer.from(creds.password.split('.')[1], 'base64').toString() ); @@ -146,38 +186,43 @@ describe('EntraID Integration Tests', () => { }; }); - // Verify unique tokens - const uniqueTokens = new Set(tokenDetails.map(detail => detail.token)); - assert.equal( - uniqueTokens.size, - reAuthSpy.callCount, - `Expected ${reAuthSpy.callCount} different tokens, but got ${uniqueTokens.size} unique tokens` - ); + // we can't guarantee that the tokens will be unique when using DefaultAzureCredential + if (!skipUniqueCheckForDefaultAzureCredential) { + // Verify unique tokens + const uniqueTokens = new Set(tokenDetails.map(detail => detail.token)); + assert.equal( + uniqueTokens.size, + reAuthSpy.callCount, + `Expected ${reAuthSpy.callCount} different tokens, but got ${uniqueTokens.size} unique tokens` + ); - // Verify all tokens are not cached (i.e. have the same lifetime) - const uniqueLifetimes = new Set(tokenDetails.map(detail => detail.lifetime)); - assert.equal( - uniqueLifetimes.size, - 1, - `Expected all tokens to have the same lifetime, but found ${uniqueLifetimes.size} different lifetimes: ${[uniqueLifetimes].join(', ')} seconds` - ); + // Verify all tokens are not cached (i.e. have the same lifetime) + const uniqueLifetimes = new Set(tokenDetails.map(detail => detail.lifetime)); + assert.equal( + uniqueLifetimes.size, + 1, + `Expected all tokens to have the same lifetime, but found ${uniqueLifetimes.size} different lifetimes: ${(Array.from(uniqueLifetimes).join(','))} seconds` + ); - // Verify that all tokens have different uti (unique token identifier) - const uniqueUti = new Set(tokenDetails.map(detail => detail.uti)); - assert.equal( - uniqueUti.size, - reAuthSpy.callCount, - `Expected all tokens to have different uti, but found ${uniqueUti.size} different uti in: ${[uniqueUti].join(', ')}` - ); + // Verify that all tokens have different uti (unique token identifier) + const uniqueUti = new Set(tokenDetails.map(detail => detail.uti)); + assert.equal( + uniqueUti.size, + reAuthSpy.callCount, + `Expected all tokens to have different uti, but found ${uniqueUti.size} different uti in: ${(Array.from(uniqueUti).join(','))}` + ); + } }; - const runAuthenticationTest = async (setupCredentialsProvider: () => any) => { + const runAuthenticationTest = async (setupCredentialsProvider: () => any, options: { + testingDefaultAzureCredential: boolean + } = { testingDefaultAzureCredential: false }) => { const { client, reAuthSpy } = await setupTestClient(setupCredentialsProvider()); try { await client.connect(); await runClientOperations(client); - validateTokens(reAuthSpy); + validateTokens(reAuthSpy, options.testingDefaultAzureCredential); } finally { await client.destroy(); } diff --git a/packages/entraid/lib/azure-identity-provider.ts b/packages/entraid/lib/azure-identity-provider.ts new file mode 100644 index 00000000000..d522c9d4b89 --- /dev/null +++ b/packages/entraid/lib/azure-identity-provider.ts @@ -0,0 +1,22 @@ +import type { AccessToken } from '@azure/core-auth'; + +import { IdentityProvider, TokenResponse } from '@redis/client/dist/lib/authx'; + +export class AzureIdentityProvider implements IdentityProvider { + private readonly getToken: () => Promise; + + constructor(getToken: () => Promise) { + this.getToken = getToken; + } + + async requestToken(): Promise> { + const result = await this.getToken(); + return { + token: result, + ttlMs: result.expiresOnTimestamp - Date.now() + }; + } + +} + + diff --git a/packages/entraid/lib/entra-id-credentials-provider-factory.ts b/packages/entraid/lib/entra-id-credentials-provider-factory.ts index 0f89be8039b..98a3a11078a 100644 --- a/packages/entraid/lib/entra-id-credentials-provider-factory.ts +++ b/packages/entraid/lib/entra-id-credentials-provider-factory.ts @@ -1,3 +1,4 @@ +import type { GetTokenOptions, TokenCredential } from '@azure/core-auth'; import { NetworkError } from '@azure/msal-common'; import { LogLevel, @@ -7,8 +8,9 @@ import { PublicClientApplication, ConfidentialClientApplication, AuthorizationUrlRequest, AuthorizationCodeRequest, CryptoProvider, Configuration, NodeAuthOptions, AccountInfo } from '@azure/msal-node'; -import { RetryPolicy, TokenManager, TokenManagerConfig, ReAuthenticationError } from '@redis/client/dist/lib/authx'; -import { EntraidCredentialsProvider } from './entraid-credentials-provider'; +import { RetryPolicy, TokenManager, TokenManagerConfig, ReAuthenticationError, BasicAuth } from '@redis/client/dist/lib/authx'; +import { AzureIdentityProvider } from './azure-identity-provider'; +import { AuthenticationResponse, DEFAULT_CREDENTIALS_MAPPER, EntraidCredentialsProvider, OID_CREDENTIALS_MAPPER } from './entraid-credentials-provider'; import { MSALIdentityProvider } from './msal-identity-provider'; /** @@ -51,7 +53,11 @@ export class EntraIdCredentialsProviderFactory { return new EntraidCredentialsProvider( new TokenManager(idp, params.tokenManagerConfig), idp, - { onReAuthenticationError: params.onReAuthenticationError, credentialsMapper: OID_CREDENTIALS_MAPPER } + { + onReAuthenticationError: params.onReAuthenticationError, + credentialsMapper: params.credentialsMapper ?? OID_CREDENTIALS_MAPPER, + onRetryableError: params.onRetryableError + } ); } @@ -102,7 +108,8 @@ export class EntraIdCredentialsProviderFactory { return new EntraidCredentialsProvider(new TokenManager(idp, params.tokenManagerConfig), idp, { onReAuthenticationError: params.onReAuthenticationError, - credentialsMapper: OID_CREDENTIALS_MAPPER + credentialsMapper: params.credentialsMapper ?? OID_CREDENTIALS_MAPPER, + onRetryableError: params.onRetryableError }); } @@ -138,6 +145,42 @@ export class EntraIdCredentialsProviderFactory { ); } + /** + * This method is used to create a credentials provider using DefaultAzureCredential. + * + * The user needs to create a configured instance of DefaultAzureCredential ( or any other class that implements TokenCredential )and pass it to this method. + * + * The default credentials mapper for this method is OID_CREDENTIALS_MAPPER which extracts the object ID from JWT + * encoded token. + * + * Depending on the actual flow that DefaultAzureCredential uses, the user may need to provide different + * credential mapper via the credentialsMapper parameter. + * + */ + static createForDefaultAzureCredential( + { + credential, + scopes, + options, + tokenManagerConfig, + onReAuthenticationError, + credentialsMapper, + onRetryableError + }: DefaultAzureCredentialsParams + ): EntraidCredentialsProvider { + + const idp = new AzureIdentityProvider( + () => credential.getToken(scopes, options).then(x => x === null ? Promise.reject('Token is null') : x) + ); + + return new EntraidCredentialsProvider(new TokenManager(idp, tokenManagerConfig), idp, + { + onReAuthenticationError: onReAuthenticationError, + credentialsMapper: credentialsMapper ?? OID_CREDENTIALS_MAPPER, + onRetryableError: onRetryableError + }); + } + /** * This method is used to create a credentials provider for the Authorization Code Flow with PKCE. * @param params @@ -194,7 +237,11 @@ export class EntraIdCredentialsProviderFactory { } ); const tm = new TokenManager(idp, params.tokenManagerConfig); - return new EntraidCredentialsProvider(tm, idp, { onReAuthenticationError: params.onReAuthenticationError }); + return new EntraidCredentialsProvider(tm, idp, { + onReAuthenticationError: params.onReAuthenticationError, + credentialsMapper: params.credentialsMapper ?? DEFAULT_CREDENTIALS_MAPPER, + onRetryableError: params.onRetryableError + }); } }; } @@ -214,8 +261,8 @@ export class EntraIdCredentialsProviderFactory { } -const REDIS_SCOPE_DEFAULT = 'https://redis.azure.com/.default'; -const REDIS_SCOPE = 'https://redis.azure.com' +export const REDIS_SCOPE_DEFAULT = 'https://redis.azure.com/.default'; +export const REDIS_SCOPE = 'https://redis.azure.com' export type AuthorityConfig = | { type: 'multi-tenant'; tenantId: string } @@ -234,7 +281,19 @@ export type CredentialParams = { authorityConfig?: AuthorityConfig; tokenManagerConfig: TokenManagerConfig - onReAuthenticationError?: (error: ReAuthenticationError) => void; + onReAuthenticationError?: (error: ReAuthenticationError) => void + credentialsMapper?: (token: AuthenticationResponse) => BasicAuth + onRetryableError?: (error: string) => void +} + +export type DefaultAzureCredentialsParams = { + scopes: string | string[], + options?: GetTokenOptions, + credential: TokenCredential + tokenManagerConfig: TokenManagerConfig + onReAuthenticationError?: (error: ReAuthenticationError) => void + credentialsMapper?: (token: AuthenticationResponse) => BasicAuth + onRetryableError?: (error: string) => void } export type AuthCodePKCEParams = CredentialParams & { @@ -356,16 +415,3 @@ export class AuthCodeFlowHelper { } } -const OID_CREDENTIALS_MAPPER = (token: AuthenticationResult) => { - - // Client credentials flow is app-only authentication (no user context), - // so only access token is provided without user-specific claims (uniqueId, idToken, ...) - // this means that we need to extract the oid from the access token manually - const accessToken = JSON.parse(Buffer.from(token.accessToken.split('.')[1], 'base64').toString()); - - return ({ - username: accessToken.oid, - password: token.accessToken - }) - -} diff --git a/packages/entraid/lib/entraid-credentials-provider.ts b/packages/entraid/lib/entraid-credentials-provider.ts index 115d6dbff3a..465c9e8a975 100644 --- a/packages/entraid/lib/entraid-credentials-provider.ts +++ b/packages/entraid/lib/entraid-credentials-provider.ts @@ -1,4 +1,5 @@ import { AuthenticationResult } from '@azure/msal-common/node'; +import { AccessToken } from '@azure/core-auth'; import { BasicAuth, StreamingCredentialsProvider, IdentityProvider, TokenManager, ReAuthenticationError, StreamingCredentialsListener, IDPError, Token, Disposable @@ -9,6 +10,9 @@ import { * Please use one of the factory functions in `entraid-credetfactories.ts` to create an instance of this class for the different * type of authentication flows. */ + +export type AuthenticationResponse = AuthenticationResult | AccessToken + export class EntraidCredentialsProvider implements StreamingCredentialsProvider { readonly type = 'streaming-credentials-provider'; @@ -24,11 +28,11 @@ export class EntraidCredentialsProvider implements StreamingCredentialsProvider }> = []; constructor( - public readonly tokenManager: TokenManager, - public readonly idp: IdentityProvider, + public readonly tokenManager: TokenManager, + public readonly idp: IdentityProvider, private readonly options: { onReAuthenticationError?: (error: ReAuthenticationError) => void; - credentialsMapper?: (token: AuthenticationResult) => BasicAuth; + credentialsMapper?: (token: AuthenticationResponse) => BasicAuth; onRetryableError?: (error: string) => void; } = {} ) { @@ -69,7 +73,7 @@ export class EntraidCredentialsProvider implements StreamingCredentialsProvider onReAuthenticationError: (error: ReAuthenticationError) => void; - #credentialsMapper: (token: AuthenticationResult) => BasicAuth; + #credentialsMapper: (token: AuthenticationResponse) => BasicAuth; #createTokenManagerListener(subscribers: Set>) { return { @@ -80,7 +84,7 @@ export class EntraidCredentialsProvider implements StreamingCredentialsProvider this.options.onRetryableError?.(error.message); } }, - onNext: (token: { value: AuthenticationResult }): void => { + onNext: (token: { value: AuthenticationResult | AccessToken }): void => { const credentials = this.#credentialsMapper(token.value); subscribers.forEach(listener => listener.onNext(credentials)); } @@ -101,10 +105,10 @@ export class EntraidCredentialsProvider implements StreamingCredentialsProvider }; } - async #startTokenManagerAndObtainInitialToken(): Promise> { - const initialResponse = await this.idp.requestToken(); - const token = this.tokenManager.wrapAndSetCurrentToken(initialResponse.token, initialResponse.ttlMs); + async #startTokenManagerAndObtainInitialToken(): Promise> { + const { ttlMs, token: initialToken } = await this.idp.requestToken(); + const token = this.tokenManager.wrapAndSetCurrentToken(initialToken, ttlMs); this.#tokenManagerDisposable = this.tokenManager.start( this.#createTokenManagerListener(this.#listeners), this.tokenManager.calculateRefreshTime(token) @@ -131,10 +135,61 @@ export class EntraidCredentialsProvider implements StreamingCredentialsProvider } -const DEFAULT_CREDENTIALS_MAPPER = (token: AuthenticationResult): BasicAuth => ({ - username: token.uniqueId, - password: token.accessToken -}); +export const DEFAULT_CREDENTIALS_MAPPER = (token: AuthenticationResponse): BasicAuth => { + if (isAuthenticationResult(token)) { + return { + username: token.uniqueId, + password: token.accessToken + } + } else { + return OID_CREDENTIALS_MAPPER(token) + } +}; const DEFAULT_ERROR_HANDLER = (error: ReAuthenticationError) => - console.error('ReAuthenticationError', error); \ No newline at end of file + console.error('ReAuthenticationError', error); + +export const OID_CREDENTIALS_MAPPER = (token: (AuthenticationResult | AccessToken)) => { + + if (isAuthenticationResult(token)) { + // Client credentials flow is app-only authentication (no user context), + // so only access token is provided without user-specific claims (uniqueId, idToken, ...) + // this means that we need to extract the oid from the access token manually + const accessToken = JSON.parse(Buffer.from(token.accessToken.split('.')[1], 'base64').toString()); + + return ({ + username: accessToken.oid, + password: token.accessToken + }) + } else { + const accessToken = JSON.parse(Buffer.from(token.token.split('.')[1], 'base64').toString()); + + return ({ + username: accessToken.oid, + password: token.token + }) + } + +} + +/** + * Type guard to check if a token is an MSAL AuthenticationResult + * + * @param auth - The token to check + * @returns true if the token is an AuthenticationResult + */ +export function isAuthenticationResult(auth: AuthenticationResult | AccessToken): auth is AuthenticationResult { + return typeof (auth as AuthenticationResult).accessToken === 'string' && + !('token' in auth) +} + +/** + * Type guard to check if a token is an Azure Identity AccessToken + * + * @param auth - The token to check + * @returns true if the token is an AccessToken + */ +export function isAccessToken(auth: AuthenticationResult | AccessToken): auth is AccessToken { + return typeof (auth as AccessToken).token === 'string' && + !('accessToken' in auth); +} \ No newline at end of file diff --git a/packages/entraid/lib/msal-identity-provider.ts b/packages/entraid/lib/msal-identity-provider.ts index 59b38d18ec6..0f15e01fcdc 100644 --- a/packages/entraid/lib/msal-identity-provider.ts +++ b/packages/entraid/lib/msal-identity-provider.ts @@ -11,21 +11,15 @@ export class MSALIdentityProvider implements IdentityProvider> { - try { - const result = await this.getToken(); + const result = await this.getToken(); - if (!result?.accessToken || !result?.expiresOn) { - throw new Error('Invalid token response'); - } - return { - token: result, - ttlMs: result.expiresOn.getTime() - Date.now() - }; - } catch (error) { - throw error; + if (!result?.accessToken || !result?.expiresOn) { + throw new Error('Invalid token response'); } + return { + token: result, + ttlMs: result.expiresOn.getTime() - Date.now() + }; } } - - diff --git a/packages/entraid/package.json b/packages/entraid/package.json index da0d7df9935..fd504cd44dc 100644 --- a/packages/entraid/package.json +++ b/packages/entraid/package.json @@ -12,10 +12,12 @@ "clean": "rimraf dist", "build": "npm run clean && tsc", "start:auth-pkce": "tsx --tsconfig tsconfig.samples.json ./samples/auth-code-pkce/index.ts", + "start:interactive-browser": "tsx --tsconfig tsconfig.samples.json ./samples/interactive-browser/index.ts", "test-integration": "mocha -r tsx --tsconfig tsconfig.integration-tests.json './integration-tests/**/*.spec.ts'", "test": "nyc -r text-summary -r lcov mocha -r tsx './lib/**/*.spec.ts'" }, "dependencies": { + "@azure/identity": "4.7.0", "@azure/msal-node": "^2.16.1" }, "peerDependencies": { diff --git a/packages/entraid/samples/interactive-browser/index.ts b/packages/entraid/samples/interactive-browser/index.ts new file mode 100644 index 00000000000..f458ad9e190 --- /dev/null +++ b/packages/entraid/samples/interactive-browser/index.ts @@ -0,0 +1,111 @@ +import express, { Request, Response } from 'express'; +import session from 'express-session'; +import dotenv from 'dotenv'; +import { DEFAULT_TOKEN_MANAGER_CONFIG, EntraIdCredentialsProviderFactory } from '../../lib/entra-id-credentials-provider-factory'; +import { InteractiveBrowserCredential } from '@azure/identity'; + +dotenv.config(); + +if (!process.env.SESSION_SECRET) { + throw new Error('SESSION_SECRET environment variable must be set'); +} + +const app = express(); + +const sessionConfig = { + secret: process.env.SESSION_SECRET, + resave: false, + saveUninitialized: false, + cookie: { + secure: process.env.NODE_ENV === 'production', // Only use secure in production + httpOnly: true, + sameSite: 'lax', + maxAge: 3600000 // 1 hour + } +} as const; + +app.use(session(sessionConfig)); + +if (!process.env.MSAL_CLIENT_ID || !process.env.MSAL_TENANT_ID) { + throw new Error('MSAL_CLIENT_ID and MSAL_TENANT_ID environment variables must be set'); +} + + +app.get('/login', async (req: Request, res: Response) => { + try { + // Create an instance of InteractiveBrowserCredential + const credential = new InteractiveBrowserCredential({ + clientId: process.env.MSAL_CLIENT_ID!, + tenantId: process.env.MSAL_TENANT_ID!, + loginStyle: 'popup', + redirectUri: 'http://localhost:3000/redirect' + }); + + // Create Redis client using the EntraID credentials provider + const entraidCredentialsProvider = EntraIdCredentialsProviderFactory.createForDefaultAzureCredential({ + credential, + scopes: ['user.read'], + tokenManagerConfig: DEFAULT_TOKEN_MANAGER_CONFIG + }); + + // Subscribe to credentials updates + const initialCredentials = entraidCredentialsProvider.subscribe({ + onNext: (token) => { + // Never log the full token in production + console.log('Token acquired successfully'); + console.log('Username:', token.username); + + }, + onError: (error) => { + console.error('Token acquisition failed:', error); + } + }); + + // Wait for the initial credentials + const [credentials] = await initialCredentials; + + // Return success response + res.json({ + status: 'success', + message: 'Authentication successful', + credentials: { + username: credentials.username, + password: credentials.password + } + }); + } catch (error) { + console.error('Authentication failed:', error); + res.status(500).json({ + status: 'error', + message: 'Authentication failed', + error: error instanceof Error ? error.message : String(error) + }); + } +}); + +// Create a simple status page +app.get('/', (req: Request, res: Response) => { + res.send(` + + + Interactive Browser Credential Demo + + + +

Interactive Browser Credential Demo

+

This example demonstrates using the InteractiveBrowserCredential from @azure/identity to authenticate with Microsoft Entra ID.

+

When you click the button below, you'll be redirected to the Microsoft login page.

+ Login with Microsoft + + + `); +}); + +const PORT = process.env.PORT || 3000; +app.listen(PORT, () => { + console.log(`Server running on port ${PORT}`); + console.log(`Open http://localhost:${PORT} in your browser to start`); +}); From ca85f8268ded7909b03300b156b43cf91bbf7182 Mon Sep 17 00:00:00 2001 From: "Bobby I." Date: Tue, 18 Mar 2025 14:27:37 +0200 Subject: [PATCH 1507/1748] refactor!: Remove graph module (#2897) https://redis.io/blog/redisgraph-eol/ --- .github/release-drafter/graph-config.yml | 49 --- .github/workflows/release-drafter-graph.yml | 24 -- README.md | 1 - docs/v4-to-v5.md | 4 - packages/graph/.nycrc.json | 4 - packages/graph/.release-it.json | 11 - packages/graph/README.md | 34 -- .../graph/lib/commands/CONFIG_GET.spec.ts | 23 -- packages/graph/lib/commands/CONFIG_GET.ts | 16 - .../graph/lib/commands/CONFIG_SET.spec.ts | 20 - packages/graph/lib/commands/CONFIG_SET.ts | 11 - packages/graph/lib/commands/DELETE.spec.ts | 22 -- packages/graph/lib/commands/DELETE.ts | 11 - packages/graph/lib/commands/EXPLAIN.spec.ts | 24 -- packages/graph/lib/commands/EXPLAIN.ts | 12 - packages/graph/lib/commands/LIST.spec.ts | 20 - packages/graph/lib/commands/LIST.ts | 11 - packages/graph/lib/commands/PROFILE.spec.ts | 21 - packages/graph/lib/commands/PROFILE.ts | 12 - packages/graph/lib/commands/QUERY.spec.ts | 64 ---- packages/graph/lib/commands/QUERY.ts | 100 ----- packages/graph/lib/commands/RO_QUERY.spec.ts | 21 - packages/graph/lib/commands/RO_QUERY.ts | 8 - packages/graph/lib/commands/SLOWLOG.spec.ts | 21 - packages/graph/lib/commands/SLOWLOG.ts | 28 -- packages/graph/lib/commands/index.ts | 31 -- packages/graph/lib/graph.spec.ts | 148 -------- packages/graph/lib/graph.ts | 359 ------------------ packages/graph/lib/index.ts | 2 - packages/graph/lib/test-utils.ts | 22 -- packages/graph/package.json | 35 -- packages/graph/tsconfig.json | 20 - packages/redis/index.ts | 3 - packages/redis/package.json | 1 - tsconfig.json | 3 - 35 files changed, 1196 deletions(-) delete mode 100644 .github/release-drafter/graph-config.yml delete mode 100644 .github/workflows/release-drafter-graph.yml delete mode 100644 packages/graph/.nycrc.json delete mode 100644 packages/graph/.release-it.json delete mode 100644 packages/graph/README.md delete mode 100644 packages/graph/lib/commands/CONFIG_GET.spec.ts delete mode 100644 packages/graph/lib/commands/CONFIG_GET.ts delete mode 100644 packages/graph/lib/commands/CONFIG_SET.spec.ts delete mode 100644 packages/graph/lib/commands/CONFIG_SET.ts delete mode 100644 packages/graph/lib/commands/DELETE.spec.ts delete mode 100644 packages/graph/lib/commands/DELETE.ts delete mode 100644 packages/graph/lib/commands/EXPLAIN.spec.ts delete mode 100644 packages/graph/lib/commands/EXPLAIN.ts delete mode 100644 packages/graph/lib/commands/LIST.spec.ts delete mode 100644 packages/graph/lib/commands/LIST.ts delete mode 100644 packages/graph/lib/commands/PROFILE.spec.ts delete mode 100644 packages/graph/lib/commands/PROFILE.ts delete mode 100644 packages/graph/lib/commands/QUERY.spec.ts delete mode 100644 packages/graph/lib/commands/QUERY.ts delete mode 100644 packages/graph/lib/commands/RO_QUERY.spec.ts delete mode 100644 packages/graph/lib/commands/RO_QUERY.ts delete mode 100644 packages/graph/lib/commands/SLOWLOG.spec.ts delete mode 100644 packages/graph/lib/commands/SLOWLOG.ts delete mode 100644 packages/graph/lib/commands/index.ts delete mode 100644 packages/graph/lib/graph.spec.ts delete mode 100644 packages/graph/lib/graph.ts delete mode 100644 packages/graph/lib/index.ts delete mode 100644 packages/graph/lib/test-utils.ts delete mode 100644 packages/graph/package.json delete mode 100644 packages/graph/tsconfig.json diff --git a/.github/release-drafter/graph-config.yml b/.github/release-drafter/graph-config.yml deleted file mode 100644 index 88d76b78b55..00000000000 --- a/.github/release-drafter/graph-config.yml +++ /dev/null @@ -1,49 +0,0 @@ -name-template: 'graph@$NEXT_PATCH_VERSION' -tag-template: 'graph@$NEXT_PATCH_VERSION' -autolabeler: - - label: 'chore' - files: - - '*.md' - - '.github/*' - - label: 'bug' - branch: - - '/bug-.+' - - label: 'chore' - branch: - - '/chore-.+' - - label: 'feature' - branch: - - '/feature-.+' -categories: - - title: 'Breaking Changes' - labels: - - 'breakingchange' - - title: '🚀 New Features' - labels: - - 'feature' - - 'enhancement' - - title: '🐛 Bug Fixes' - labels: - - 'fix' - - 'bugfix' - - 'bug' - - title: '🧰 Maintenance' - label: - - 'chore' - - 'maintenance' - - 'documentation' - - 'docs' -change-template: '- $TITLE (#$NUMBER)' -include-paths: - - 'packages/graph' -exclude-labels: - - 'skip-changelog' -template: | - ## Changes - - $CHANGES - - ## Contributors - We'd like to thank all the contributors who worked on this release! - - $CONTRIBUTORS diff --git a/.github/workflows/release-drafter-graph.yml b/.github/workflows/release-drafter-graph.yml deleted file mode 100644 index 4d664e5f19e..00000000000 --- a/.github/workflows/release-drafter-graph.yml +++ /dev/null @@ -1,24 +0,0 @@ -name: Release Drafter - -on: - push: - # branches to consider in the event; optional, defaults to all - branches: - - master - -jobs: - - update_release_draft: - - permissions: - contents: write - pull-requests: write - runs-on: ubuntu-latest - steps: - # Drafts your next Release notes as Pull Requests are merged into "master" - - uses: release-drafter/release-drafter@v5 - with: - # (Optional) specify config name to use, relative to .github/. Default: release-drafter.yml - config-name: release-drafter/graph-config.yml - env: - GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }} diff --git a/README.md b/README.md index 457f3d2c2cf..8e7e3845256 100644 --- a/README.md +++ b/README.md @@ -48,7 +48,6 @@ npm install redis | [`redis`](./packages/redis) | The client with all the ["redis-stack"](https://github.com/redis-stack/redis-stack) modules | | [`@redis/client`](./packages/client) | The base clients (i.e `RedisClient`, `RedisCluster`, etc.) | | [`@redis/bloom`](./packages/bloom) | [Redis Bloom](https://redis.io/docs/data-types/probabilistic/) commands | -| [`@redis/graph`](./packages/graph) | [Redis Graph](https://redis.io/docs/data-types/probabilistic/) commands | | [`@redis/json`](./packages/json) | [Redis JSON](https://redis.io/docs/data-types/json/) commands | | [`@redis/search`](./packages/search) | [RediSearch](https://redis.io/docs/interact/search-and-query/) commands | | [`@redis/time-series`](./packages/time-series) | [Redis Time-Series](https://redis.io/docs/data-types/timeseries/) commands | diff --git a/docs/v4-to-v5.md b/docs/v4-to-v5.md index 95c2230ce23..57ad3f9bbf6 100644 --- a/docs/v4-to-v5.md +++ b/docs/v4-to-v5.md @@ -211,10 +211,6 @@ In v5, any unwritten commands (in the same pipeline) will be discarded. - `TOPK.QUERY`: `Array` -> `Array` -### Graph - -- `GRAPH.SLOWLOG`: `timestamp` has been changed from `Date` to `number` - ### JSON - `JSON.ARRINDEX`: `start` and `end` arguments moved to `{ range: { start: number; end: number; }; }` [^future-proofing] diff --git a/packages/graph/.nycrc.json b/packages/graph/.nycrc.json deleted file mode 100644 index 367a89ad32c..00000000000 --- a/packages/graph/.nycrc.json +++ /dev/null @@ -1,4 +0,0 @@ -{ - "extends": "@istanbuljs/nyc-config-typescript", - "exclude": ["dist", "**/*.spec.ts", "lib/test-utils.ts"] -} diff --git a/packages/graph/.release-it.json b/packages/graph/.release-it.json deleted file mode 100644 index 7797dd0b4dd..00000000000 --- a/packages/graph/.release-it.json +++ /dev/null @@ -1,11 +0,0 @@ -{ - "git": { - "tagName": "graph@${version}", - "commitMessage": "Release ${tagName}", - "tagAnnotation": "Release ${tagName}" - }, - "npm": { - "versionArgs": ["--workspaces-update=false"], - "publishArgs": ["--access", "public"] - } -} diff --git a/packages/graph/README.md b/packages/graph/README.md deleted file mode 100644 index 4c712bfd820..00000000000 --- a/packages/graph/README.md +++ /dev/null @@ -1,34 +0,0 @@ -# @redis/graph - -Example usage: -```javascript -import { createClient, Graph } from 'redis'; - -const client = createClient(); -client.on('error', (err) => console.log('Redis Client Error', err)); - -await client.connect(); - -const graph = new Graph(client, 'graph'); - -await graph.query( - 'CREATE (:Rider { name: $riderName })-[:rides]->(:Team { name: $teamName })', - { - params: { - riderName: 'Buzz Aldrin', - teamName: 'Apollo' - } - } -); - -const result = await graph.roQuery( - 'MATCH (r:Rider)-[:rides]->(t:Team { name: $name }) RETURN r.name AS name', - { - params: { - name: 'Apollo' - } - } -); - -console.log(result.data); // [{ name: 'Buzz Aldrin' }] -``` diff --git a/packages/graph/lib/commands/CONFIG_GET.spec.ts b/packages/graph/lib/commands/CONFIG_GET.spec.ts deleted file mode 100644 index 9a427867c63..00000000000 --- a/packages/graph/lib/commands/CONFIG_GET.spec.ts +++ /dev/null @@ -1,23 +0,0 @@ -import { strict as assert } from 'node:assert'; -import testUtils, { GLOBAL } from '../test-utils'; -import CONFIG_GET from './CONFIG_GET'; -import { parseArgs } from '@redis/client/lib/commands/generic-transformers'; - -describe('GRAPH.CONFIG GET', () => { - it('transformArguments', () => { - assert.deepEqual( - parseArgs(CONFIG_GET, 'TIMEOUT'), - ['GRAPH.CONFIG', 'GET', 'TIMEOUT'] - ); - }); - - testUtils.testWithClient('client.graph.configGet', async client => { - assert.deepEqual( - await client.graph.configGet('TIMEOUT'), - [ - 'TIMEOUT', - 0 - ] - ); - }, GLOBAL.SERVERS.OPEN); -}); diff --git a/packages/graph/lib/commands/CONFIG_GET.ts b/packages/graph/lib/commands/CONFIG_GET.ts deleted file mode 100644 index 4986dfc1816..00000000000 --- a/packages/graph/lib/commands/CONFIG_GET.ts +++ /dev/null @@ -1,16 +0,0 @@ -import { RedisArgument, TuplesReply, ArrayReply, BlobStringReply, NumberReply, Command } from '@redis/client/dist/lib/RESP/types'; -import { CommandParser } from '@redis/client/dist/lib/client/parser'; - -type ConfigItemReply = TuplesReply<[ - configKey: BlobStringReply, - value: NumberReply -]>; - -export default { - NOT_KEYED_COMMAND: true, - IS_READ_ONLY: true, - parseCommand(parser: CommandParser, configKey: RedisArgument) { - parser.push('GRAPH.CONFIG', 'GET', configKey); - }, - transformReply: undefined as unknown as () => ConfigItemReply | ArrayReply -} as const satisfies Command; diff --git a/packages/graph/lib/commands/CONFIG_SET.spec.ts b/packages/graph/lib/commands/CONFIG_SET.spec.ts deleted file mode 100644 index ae6e296699c..00000000000 --- a/packages/graph/lib/commands/CONFIG_SET.spec.ts +++ /dev/null @@ -1,20 +0,0 @@ -import { strict as assert } from 'node:assert'; -import testUtils, { GLOBAL } from '../test-utils'; -import CONFIG_SET from './CONFIG_SET'; -import { parseArgs } from '@redis/client/lib/commands/generic-transformers'; - -describe('GRAPH.CONFIG SET', () => { - it('transformArguments', () => { - assert.deepEqual( - parseArgs(CONFIG_SET, 'TIMEOUT', 0), - ['GRAPH.CONFIG', 'SET', 'TIMEOUT', '0'] - ); - }); - - testUtils.testWithClient('client.graph.configSet', async client => { - assert.equal( - await client.graph.configSet('TIMEOUT', 0), - 'OK' - ); - }, GLOBAL.SERVERS.OPEN); -}); diff --git a/packages/graph/lib/commands/CONFIG_SET.ts b/packages/graph/lib/commands/CONFIG_SET.ts deleted file mode 100644 index 63f604402ec..00000000000 --- a/packages/graph/lib/commands/CONFIG_SET.ts +++ /dev/null @@ -1,11 +0,0 @@ -import { RedisArgument, SimpleStringReply, Command } from '@redis/client/dist/lib/RESP/types'; -import { CommandParser } from '@redis/client/dist/lib/client/parser'; - -export default { - NOT_KEYED_COMMAND: true, - IS_READ_ONLY: false, - parseCommand(parser: CommandParser, configKey: RedisArgument, value: number) { - parser.push('GRAPH.CONFIG', 'SET', configKey, value.toString()); - }, - transformReply: undefined as unknown as () => SimpleStringReply<'OK'> -} as const satisfies Command; diff --git a/packages/graph/lib/commands/DELETE.spec.ts b/packages/graph/lib/commands/DELETE.spec.ts deleted file mode 100644 index 5977c646307..00000000000 --- a/packages/graph/lib/commands/DELETE.spec.ts +++ /dev/null @@ -1,22 +0,0 @@ -import { strict as assert } from 'node:assert'; -import testUtils, { GLOBAL } from '../test-utils'; -import DELETE from './DELETE'; -import { parseArgs } from '@redis/client/lib/commands/generic-transformers'; - -describe('GRAPH.DELETE', () => { - it('transformArguments', () => { - assert.deepEqual( - parseArgs(DELETE, 'key'), - ['GRAPH.DELETE', 'key'] - ); - }); - - testUtils.testWithClient('client.graph.delete', async client => { - const [, reply] = await Promise.all([ - client.graph.query('key', 'RETURN 1'), - client.graph.delete('key') - ]); - - assert.equal(typeof reply, 'string'); - }, GLOBAL.SERVERS.OPEN); -}); diff --git a/packages/graph/lib/commands/DELETE.ts b/packages/graph/lib/commands/DELETE.ts deleted file mode 100644 index 43af38d6fb0..00000000000 --- a/packages/graph/lib/commands/DELETE.ts +++ /dev/null @@ -1,11 +0,0 @@ -import { RedisArgument, BlobStringReply, Command } from '@redis/client/dist/lib/RESP/types'; -import { CommandParser } from '@redis/client/dist/lib/client/parser'; - -export default { - IS_READ_ONLY: false, - parseCommand(parser: CommandParser, key: RedisArgument) { - parser.push('GRAPH.DELETE'); - parser.pushKey(key); - }, - transformReply: undefined as unknown as () => BlobStringReply -} as const satisfies Command; diff --git a/packages/graph/lib/commands/EXPLAIN.spec.ts b/packages/graph/lib/commands/EXPLAIN.spec.ts deleted file mode 100644 index 28f30cd17b3..00000000000 --- a/packages/graph/lib/commands/EXPLAIN.spec.ts +++ /dev/null @@ -1,24 +0,0 @@ -import { strict as assert } from 'node:assert'; -import testUtils, { GLOBAL } from '../test-utils'; -import EXPLAIN from './EXPLAIN'; -import { parseArgs } from '@redis/client/lib/commands/generic-transformers'; - -describe('GRAPH.EXPLAIN', () => { - it('transformArguments', () => { - assert.deepEqual( - parseArgs(EXPLAIN, 'key', 'RETURN 0'), - ['GRAPH.EXPLAIN', 'key', 'RETURN 0'] - ); - }); - - testUtils.testWithClient('client.graph.explain', async client => { - const [, reply] = await Promise.all([ - client.graph.query('key', 'RETURN 0'), // make sure to create a graph first - client.graph.explain('key', 'RETURN 0') - ]); - assert.ok(Array.isArray(reply)); - for (const item of reply) { - assert.equal(typeof item, 'string'); - } - }, GLOBAL.SERVERS.OPEN); -}); diff --git a/packages/graph/lib/commands/EXPLAIN.ts b/packages/graph/lib/commands/EXPLAIN.ts deleted file mode 100644 index a9af7e73a20..00000000000 --- a/packages/graph/lib/commands/EXPLAIN.ts +++ /dev/null @@ -1,12 +0,0 @@ -import { RedisArgument, ArrayReply, BlobStringReply, Command } from '@redis/client/dist/lib/RESP/types'; -import { CommandParser } from '@redis/client/dist/lib/client/parser'; - -export default { - IS_READ_ONLY: true, - parseCommand(parser: CommandParser, key: RedisArgument, query: RedisArgument) { - parser.push('GRAPH.EXPLAIN'); - parser.pushKey(key); - parser.push(query); - }, - transformReply: undefined as unknown as () => ArrayReply -} as const satisfies Command; diff --git a/packages/graph/lib/commands/LIST.spec.ts b/packages/graph/lib/commands/LIST.spec.ts deleted file mode 100644 index 19f18a0e30b..00000000000 --- a/packages/graph/lib/commands/LIST.spec.ts +++ /dev/null @@ -1,20 +0,0 @@ -import { strict as assert } from 'node:assert'; -import testUtils, { GLOBAL } from '../test-utils'; -import LIST from './LIST'; -import { parseArgs } from '@redis/client/lib/commands/generic-transformers'; - -describe('GRAPH.LIST', () => { - it('transformArguments', () => { - assert.deepEqual( - parseArgs(LIST), - ['GRAPH.LIST'] - ); - }); - - testUtils.testWithClient('client.graph.list', async client => { - assert.deepEqual( - await client.graph.list(), - [] - ); - }, GLOBAL.SERVERS.OPEN); -}); diff --git a/packages/graph/lib/commands/LIST.ts b/packages/graph/lib/commands/LIST.ts deleted file mode 100644 index 70fa8bda812..00000000000 --- a/packages/graph/lib/commands/LIST.ts +++ /dev/null @@ -1,11 +0,0 @@ -import { ArrayReply, BlobStringReply, Command } from '@redis/client/dist/lib/RESP/types'; -import { CommandParser } from '@redis/client/dist/lib/client/parser'; - -export default { - NOT_KEYED_COMMAND: true, - IS_READ_ONLY: true, - parseCommand(parser: CommandParser) { - parser.push('GRAPH.LIST'); - }, - transformReply: undefined as unknown as () => ArrayReply -} as const satisfies Command; diff --git a/packages/graph/lib/commands/PROFILE.spec.ts b/packages/graph/lib/commands/PROFILE.spec.ts deleted file mode 100644 index 7f16fd3ba58..00000000000 --- a/packages/graph/lib/commands/PROFILE.spec.ts +++ /dev/null @@ -1,21 +0,0 @@ -import { strict as assert } from 'node:assert'; -import testUtils, { GLOBAL } from '../test-utils'; -import PROFILE from './PROFILE'; -import { parseArgs } from '@redis/client/lib/commands/generic-transformers'; - -describe('GRAPH.PROFILE', () => { - it('transformArguments', () => { - assert.deepEqual( - parseArgs(PROFILE, 'key', 'RETURN 0'), - ['GRAPH.PROFILE', 'key', 'RETURN 0'] - ); - }); - - testUtils.testWithClient('client.graph.profile', async client => { - const reply = await client.graph.profile('key', 'RETURN 0'); - assert.ok(Array.isArray(reply)); - for (const item of reply) { - assert.equal(typeof item, 'string'); - } - }, GLOBAL.SERVERS.OPEN); -}); diff --git a/packages/graph/lib/commands/PROFILE.ts b/packages/graph/lib/commands/PROFILE.ts deleted file mode 100644 index c3229fb9a04..00000000000 --- a/packages/graph/lib/commands/PROFILE.ts +++ /dev/null @@ -1,12 +0,0 @@ -import { RedisArgument, ArrayReply, BlobStringReply, Command } from '@redis/client/dist/lib/RESP/types'; -import { CommandParser } from '@redis/client/dist/lib/client/parser'; - -export default { - IS_READ_ONLY: true, - parseCommand(parser: CommandParser, key: RedisArgument, query: RedisArgument) { - parser.push('GRAPH.PROFILE'); - parser.pushKey(key); - parser.push(query); - }, - transformReply: undefined as unknown as () => ArrayReply -} as const satisfies Command; diff --git a/packages/graph/lib/commands/QUERY.spec.ts b/packages/graph/lib/commands/QUERY.spec.ts deleted file mode 100644 index 28c2189645b..00000000000 --- a/packages/graph/lib/commands/QUERY.spec.ts +++ /dev/null @@ -1,64 +0,0 @@ -import { strict as assert } from 'node:assert'; -import testUtils, { GLOBAL } from '../test-utils'; -import QUERY from './QUERY'; -import { parseArgs } from '@redis/client/lib/commands/generic-transformers'; - -describe('GRAPH.QUERY', () => { - describe('transformArguments', () => { - it('simple', () => { - assert.deepEqual( - parseArgs(QUERY, 'key', 'query'), - ['GRAPH.QUERY', 'key', 'query'] - ); - }); - - describe('params', () => { - it('all types', () => { - assert.deepEqual( - parseArgs(QUERY, 'key', 'query', { - params: { - null: null, - string: '"\\', - number: 0, - boolean: false, - array: [0], - object: {a: 0} - } - }), - ['GRAPH.QUERY', 'key', 'CYPHER null=null string="\\"\\\\" number=0 boolean=false array=[0] object={a:0} query'] - ); - }); - - it('TypeError', () => { - assert.throws(() => { - parseArgs(QUERY, 'key', 'query', { - params: { - a: Symbol() - } - }) - }, TypeError); - }); - }); - - it('TIMEOUT', () => { - assert.deepEqual( - parseArgs(QUERY, 'key', 'query', { - TIMEOUT: 1 - }), - ['GRAPH.QUERY', 'key', 'query', 'TIMEOUT', '1'] - ); - }); - - it('compact', () => { - assert.deepEqual( - parseArgs(QUERY, 'key', 'query', undefined, true), - ['GRAPH.QUERY', 'key', 'query', '--compact'] - ); - }); - }); - - testUtils.testWithClient('client.graph.query', async client => { - const { data } = await client.graph.query('key', 'RETURN 0'); - assert.deepEqual(data, [[0]]); - }, GLOBAL.SERVERS.OPEN); -}); diff --git a/packages/graph/lib/commands/QUERY.ts b/packages/graph/lib/commands/QUERY.ts deleted file mode 100644 index 23ea24c5768..00000000000 --- a/packages/graph/lib/commands/QUERY.ts +++ /dev/null @@ -1,100 +0,0 @@ -import { RedisArgument, ArrayReply, BlobStringReply, NumberReply, NullReply, TuplesReply, UnwrapReply, Command } from '@redis/client/dist/lib/RESP/types'; -import { CommandParser } from '@redis/client/dist/lib/client/parser'; - -type Headers = ArrayReply; - -type Data = ArrayReply; - -type Metadata = ArrayReply; - -type QueryRawReply = TuplesReply<[ - headers: Headers, - data: Data, - metadata: Metadata -] | [ - metadata: Metadata -]>; - -type QueryParam = null | string | number | boolean | QueryParams | Array; - -type QueryParams = { - [key: string]: QueryParam; -}; - -export interface QueryOptions { - params?: QueryParams; - TIMEOUT?: number; -} - -export function parseQueryArguments( - command: RedisArgument, - parser: CommandParser, - graph: RedisArgument, - query: RedisArgument, - options?: QueryOptions, - compact?: boolean -) { - parser.push(command); - parser.pushKey(graph); - const param = options?.params ? - `CYPHER ${queryParamsToString(options.params)} ${query}` : - query; - parser.push(param); - - if (options?.TIMEOUT !== undefined) { - parser.push('TIMEOUT', options.TIMEOUT.toString()); - } - - if (compact) { - parser.push('--compact'); - } -} - -function queryParamsToString(params: QueryParams) { - return Object.entries(params) - .map(([key, value]) => `${key}=${queryParamToString(value)}`) - .join(' '); -} - -function queryParamToString(param: QueryParam): string { - if (param === null) { - return 'null'; - } - - switch (typeof param) { - case 'string': - return `"${param.replace(/["\\]/g, '\\$&')}"`; - - case 'number': - case 'boolean': - return param.toString(); - } - - if (Array.isArray(param)) { - return `[${param.map(queryParamToString).join(',')}]`; - } else if (typeof param === 'object') { - const body = []; - for (const [key, value] of Object.entries(param)) { - body.push(`${key}:${queryParamToString(value)}`); - } - return `{${body.join(',')}}`; - } else { - throw new TypeError(`Unexpected param type ${typeof param} ${param}`) - } -} - -export default { - IS_READ_ONLY: false, - parseCommand: parseQueryArguments.bind(undefined, 'GRAPH.QUERY'), - transformReply(reply: UnwrapReply) { - return reply.length === 1 ? { - headers: undefined, - data: undefined, - metadata: reply[0] - } : { - headers: reply[0], - data: reply[1], - metadata: reply[2] - }; - } -} as const satisfies Command; diff --git a/packages/graph/lib/commands/RO_QUERY.spec.ts b/packages/graph/lib/commands/RO_QUERY.spec.ts deleted file mode 100644 index fa9459cf642..00000000000 --- a/packages/graph/lib/commands/RO_QUERY.spec.ts +++ /dev/null @@ -1,21 +0,0 @@ -import { strict as assert } from 'node:assert'; -import testUtils, { GLOBAL } from '../test-utils'; -import RO_QUERY from './RO_QUERY'; -import { parseArgs } from '@redis/client/lib/commands/generic-transformers'; - -describe('GRAPH.RO_QUERY', () => { - it('transformArguments', () => { - assert.deepEqual( - parseArgs(RO_QUERY, 'key', 'query'), - ['GRAPH.RO_QUERY', 'key', 'query'] - ); - }); - - testUtils.testWithClient('client.graph.roQuery', async client => { - const [, { data }] = await Promise.all([ - client.graph.query('key', 'RETURN 0'), // make sure to create a graph first - client.graph.roQuery('key', 'RETURN 0') - ]); - assert.deepEqual(data, [[0]]); - }, GLOBAL.SERVERS.OPEN); -}); \ No newline at end of file diff --git a/packages/graph/lib/commands/RO_QUERY.ts b/packages/graph/lib/commands/RO_QUERY.ts deleted file mode 100644 index f052877b99d..00000000000 --- a/packages/graph/lib/commands/RO_QUERY.ts +++ /dev/null @@ -1,8 +0,0 @@ -import { Command } from '@redis/client/dist/lib/RESP/types'; -import QUERY, { parseQueryArguments } from './QUERY'; - -export default { - IS_READ_ONLY: true, - parseCommand: parseQueryArguments.bind(undefined, 'GRAPH.RO_QUERY'), - transformReply: QUERY.transformReply -} as const satisfies Command; diff --git a/packages/graph/lib/commands/SLOWLOG.spec.ts b/packages/graph/lib/commands/SLOWLOG.spec.ts deleted file mode 100644 index b991d6e7b96..00000000000 --- a/packages/graph/lib/commands/SLOWLOG.spec.ts +++ /dev/null @@ -1,21 +0,0 @@ -import { strict as assert } from 'node:assert'; -import testUtils, { GLOBAL } from '../test-utils'; -import SLOWLOG from './SLOWLOG'; -import { parseArgs } from '@redis/client/lib/commands/generic-transformers'; - -describe('GRAPH.SLOWLOG', () => { - it('transformArguments', () => { - assert.deepEqual( - parseArgs(SLOWLOG, 'key'), - ['GRAPH.SLOWLOG', 'key'] - ); - }); - - testUtils.testWithClient('client.graph.slowLog', async client => { - const [, reply] = await Promise.all([ - client.graph.query('key', 'RETURN 1'), - client.graph.slowLog('key') - ]); - assert.equal(reply.length, 1); - }, GLOBAL.SERVERS.OPEN); -}); diff --git a/packages/graph/lib/commands/SLOWLOG.ts b/packages/graph/lib/commands/SLOWLOG.ts deleted file mode 100644 index 4110335f922..00000000000 --- a/packages/graph/lib/commands/SLOWLOG.ts +++ /dev/null @@ -1,28 +0,0 @@ -import { RedisArgument, ArrayReply, TuplesReply, BlobStringReply, UnwrapReply, Command } from '@redis/client/dist/lib/RESP/types'; -import { CommandParser } from '@redis/client/dist/lib/client/parser'; - -type SlowLogRawReply = ArrayReply>; - -export default { - IS_READ_ONLY: true, - parseCommand(parser: CommandParser, key: RedisArgument) { - parser.push('GRAPH.SLOWLOG'); - parser.pushKey(key); - }, - transformReply(reply: UnwrapReply) { - return reply.map(log => { - const [timestamp, command, query, took] = log as unknown as UnwrapReply; - return { - timestamp: Number(timestamp), - command, - query, - took: Number(took) - }; - }); - } -} as const satisfies Command; diff --git a/packages/graph/lib/commands/index.ts b/packages/graph/lib/commands/index.ts deleted file mode 100644 index e93356aa951..00000000000 --- a/packages/graph/lib/commands/index.ts +++ /dev/null @@ -1,31 +0,0 @@ -import type { RedisCommands } from '@redis/client/dist/lib/RESP/types'; -import CONFIG_GET from './CONFIG_GET'; -import CONFIG_SET from './CONFIG_SET';; -import DELETE from './DELETE'; -import EXPLAIN from './EXPLAIN'; -import LIST from './LIST'; -import PROFILE from './PROFILE'; -import QUERY from './QUERY'; -import RO_QUERY from './RO_QUERY'; -import SLOWLOG from './SLOWLOG'; - -export default { - CONFIG_GET, - configGet: CONFIG_GET, - CONFIG_SET, - configSet: CONFIG_SET, - DELETE, - delete: DELETE, - EXPLAIN, - explain: EXPLAIN, - LIST, - list: LIST, - PROFILE, - profile: PROFILE, - QUERY, - query: QUERY, - RO_QUERY, - roQuery: RO_QUERY, - SLOWLOG, - slowLog: SLOWLOG -} as const satisfies RedisCommands; diff --git a/packages/graph/lib/graph.spec.ts b/packages/graph/lib/graph.spec.ts deleted file mode 100644 index ab506c43a4b..00000000000 --- a/packages/graph/lib/graph.spec.ts +++ /dev/null @@ -1,148 +0,0 @@ -import { strict as assert } from 'node:assert'; -import testUtils, { GLOBAL } from './test-utils'; -import Graph from './graph'; - -describe('Graph', () => { - testUtils.testWithClient('null', async client => { - const graph = new Graph(client as any, 'graph'), - { data } = await graph.query('RETURN null AS key'); - - assert.deepEqual( - data, - [{ key: null }] - ); - }, GLOBAL.SERVERS.OPEN); - - testUtils.testWithClient('string', async client => { - const graph = new Graph(client as any, 'graph'), - { data } = await graph.query('RETURN "string" AS key'); - - assert.deepEqual( - data, - [{ key: 'string' }] - ); - }, GLOBAL.SERVERS.OPEN); - - testUtils.testWithClient('integer', async client => { - const graph = new Graph(client as any, 'graph'), - { data } = await graph.query('RETURN 0 AS key'); - - assert.deepEqual( - data, - [{ key: 0 }] - ); - }, GLOBAL.SERVERS.OPEN); - - testUtils.testWithClient('boolean', async client => { - const graph = new Graph(client as any, 'graph'), - { data } = await graph.query('RETURN false AS key'); - - assert.deepEqual( - data, - [{ key: false }] - ); - }, GLOBAL.SERVERS.OPEN); - - testUtils.testWithClient('double', async client => { - const graph = new Graph(client as any, 'graph'), - { data } = await graph.query('RETURN 0.1 AS key'); - - assert.deepEqual( - data, - [{ key: 0.1 }] - ); - }, GLOBAL.SERVERS.OPEN); - - testUtils.testWithClient('array', async client => { - const graph = new Graph(client as any, 'graph'), - { data } = await graph.query('RETURN [null] AS key'); - - assert.deepEqual( - data, - [{ key: [null] }] - ); - }, GLOBAL.SERVERS.OPEN); - - testUtils.testWithClient('edge', async client => { - const graph = new Graph(client as any, 'graph'); - - // check with and without metadata cache - for (let i = 0; i < 2; i++) { - const { data } = await graph.query('CREATE ()-[edge :edge]->() RETURN edge'); - assert.ok(Array.isArray(data)); - assert.equal(data.length, 1); - assert.equal(typeof data[0].edge.id, 'number'); - assert.equal(data[0].edge.relationshipType, 'edge'); - assert.equal(typeof data[0].edge.sourceId, 'number'); - assert.equal(typeof data[0].edge.destinationId, 'number'); - assert.deepEqual(data[0].edge.properties, {}); - } - - }, GLOBAL.SERVERS.OPEN); - - testUtils.testWithClient('node', async client => { - const graph = new Graph(client as any, 'graph'); - - // check with and without metadata cache - for (let i = 0; i < 2; i++) { - const { data } = await graph.query('CREATE (node :node { p: 0 }) RETURN node'); - assert.ok(Array.isArray(data)); - assert.equal(data.length, 1); - assert.equal(typeof data[0].node.id, 'number'); - assert.deepEqual(data[0].node.labels, ['node']); - assert.deepEqual(data[0].node.properties, { p: 0 }); - } - }, GLOBAL.SERVERS.OPEN); - - testUtils.testWithClient('path', async client => { - const graph = new Graph(client as any, 'graph'), - [, { data }] = await Promise.all([ - await graph.query('CREATE ()-[:edge]->()'), - await graph.roQuery('MATCH path = ()-[:edge]->() RETURN path') - ]); - - assert.ok(Array.isArray(data)); - assert.equal(data.length, 1); - - assert.ok(Array.isArray(data[0].path.nodes)); - assert.equal(data[0].path.nodes.length, 2); - for (const node of data[0].path.nodes) { - assert.equal(typeof node.id, 'number'); - assert.deepEqual(node.labels, []); - assert.deepEqual(node.properties, {}); - } - - assert.ok(Array.isArray(data[0].path.edges)); - assert.equal(data[0].path.edges.length, 1); - for (const edge of data[0].path.edges) { - assert.equal(typeof edge.id, 'number'); - assert.equal(edge.relationshipType, 'edge'); - assert.equal(typeof edge.sourceId, 'number'); - assert.equal(typeof edge.destinationId, 'number'); - assert.deepEqual(edge.properties, {}); - } - }, GLOBAL.SERVERS.OPEN); - - testUtils.testWithClient('map', async client => { - const graph = new Graph(client as any, 'graph'), - { data } = await graph.query('RETURN { key: "value" } AS map'); - - assert.deepEqual(data, [{ - map: { - key: 'value' - } - }]); - }, GLOBAL.SERVERS.OPEN); - - testUtils.testWithClient('point', async client => { - const graph = new Graph(client as any, 'graph'), - { data } = await graph.query('RETURN point({ latitude: 1, longitude: 2 }) AS point'); - - assert.deepEqual(data, [{ - point: { - latitude: 1, - longitude: 2 - } - }]); - }, GLOBAL.SERVERS.OPEN); -}); diff --git a/packages/graph/lib/graph.ts b/packages/graph/lib/graph.ts deleted file mode 100644 index 348c8b7155f..00000000000 --- a/packages/graph/lib/graph.ts +++ /dev/null @@ -1,359 +0,0 @@ -import { RedisClientType } from '@redis/client'; -import { RedisArgument, RedisFunctions, RedisScripts } from '@redis/client/dist/lib/RESP/types'; -import QUERY, { QueryOptions } from './commands/QUERY'; - -interface GraphMetadata { - labels: Array; - relationshipTypes: Array; - propertyKeys: Array; -} - -// https://github.com/RedisGraph/RedisGraph/blob/master/src/resultset/formatters/resultset_formatter.h#L20 -enum GraphValueTypes { - UNKNOWN = 0, - NULL = 1, - STRING = 2, - INTEGER = 3, - BOOLEAN = 4, - DOUBLE = 5, - ARRAY = 6, - EDGE = 7, - NODE = 8, - PATH = 9, - MAP = 10, - POINT = 11 -} - -type GraphEntityRawProperties = Array<[ - id: number, - ...value: GraphRawValue -]>; - -type GraphEdgeRawValue = [ - GraphValueTypes.EDGE, - [ - id: number, - relationshipTypeId: number, - sourceId: number, - destinationId: number, - properties: GraphEntityRawProperties - ] -]; - -type GraphNodeRawValue = [ - GraphValueTypes.NODE, - [ - id: number, - labelIds: Array, - properties: GraphEntityRawProperties - ] -]; - -type GraphPathRawValue = [ - GraphValueTypes.PATH, - [ - nodes: [ - GraphValueTypes.ARRAY, - Array - ], - edges: [ - GraphValueTypes.ARRAY, - Array - ] - ] -]; - -type GraphMapRawValue = [ - GraphValueTypes.MAP, - Array -]; - -type GraphRawValue = [ - GraphValueTypes.NULL, - null -] | [ - GraphValueTypes.STRING, - string -] | [ - GraphValueTypes.INTEGER, - number -] | [ - GraphValueTypes.BOOLEAN, - string -] | [ - GraphValueTypes.DOUBLE, - string -] | [ - GraphValueTypes.ARRAY, - Array -] | GraphEdgeRawValue | GraphNodeRawValue | GraphPathRawValue | GraphMapRawValue | [ - GraphValueTypes.POINT, - [ - latitude: string, - longitude: string - ] -]; - -type GraphEntityProperties = Record; - -interface GraphEdge { - id: number; - relationshipType: string; - sourceId: number; - destinationId: number; - properties: GraphEntityProperties; -} - -interface GraphNode { - id: number; - labels: Array; - properties: GraphEntityProperties; -} - -interface GraphPath { - nodes: Array; - edges: Array; -} - -type GraphMap = { - [key: string]: GraphValue; -}; - -type GraphValue = null | string | number | boolean | Array | { -} | GraphEdge | GraphNode | GraphPath | GraphMap | { - latitude: string; - longitude: string; -}; - -export type GraphReply = { - data?: Array; -}; - -export type GraphClientType = RedisClientType<{ - graph: { - query: typeof QUERY, - roQuery: typeof import('./commands/RO_QUERY.js').default - } -}, RedisFunctions, RedisScripts>; - -export default class Graph { - #client: GraphClientType; - #name: string; - #metadata?: GraphMetadata; - - constructor( - client: GraphClientType, - name: string - ) { - this.#client = client; - this.#name = name; - } - - async query( - query: RedisArgument, - options?: QueryOptions - ) { - return this.#parseReply( - await this.#client.graph.query( - this.#name, - query, - options, - true - ) - ); - } - - async roQuery( - query: RedisArgument, - options?: QueryOptions - ) { - return this.#parseReply( - await this.#client.graph.roQuery( - this.#name, - query, - options, - true - ) - ); - } - - #setMetadataPromise?: Promise; - - #updateMetadata(): Promise { - this.#setMetadataPromise ??= this.#setMetadata() - .finally(() => this.#setMetadataPromise = undefined); - return this.#setMetadataPromise; - } - - // DO NOT use directly, use #updateMetadata instead - async #setMetadata(): Promise { - const [labels, relationshipTypes, propertyKeys] = await Promise.all([ - this.#client.graph.roQuery(this.#name, 'CALL db.labels()'), - this.#client.graph.roQuery(this.#name, 'CALL db.relationshipTypes()'), - this.#client.graph.roQuery(this.#name, 'CALL db.propertyKeys()') - ]); - - this.#metadata = { - labels: this.#cleanMetadataArray(labels.data as Array<[string]>), - relationshipTypes: this.#cleanMetadataArray(relationshipTypes.data as Array<[string]>), - propertyKeys: this.#cleanMetadataArray(propertyKeys.data as Array<[string]>) - }; - - return this.#metadata; - } - - #cleanMetadataArray(arr: Array<[string]>): Array { - return arr.map(([value]) => value); - } - - #getMetadata( - key: T, - id: number - ): GraphMetadata[T][number] | Promise { - return this.#metadata?.[key][id] ?? this.#getMetadataAsync(key, id); - } - - // DO NOT use directly, use #getMetadata instead - async #getMetadataAsync( - key: T, - id: number - ): Promise { - const value = (await this.#updateMetadata())[key][id]; - if (value === undefined) throw new Error(`Cannot find value from ${key}[${id}]`); - return value; - } - - // TODO: reply type - async #parseReply(reply: any): Promise> { - if (!reply.data) return reply; - - const promises: Array> = [], - parsed = { - metadata: reply.metadata, - data: reply.data!.map((row: any) => { - const data: Record = {}; - for (let i = 0; i < row.length; i++) { - data[reply.headers[i][1]] = this.#parseValue(row[i], promises); - } - - return data as unknown as T; - }) - }; - - if (promises.length) await Promise.all(promises); - - return parsed; - } - - #parseValue([valueType, value]: GraphRawValue, promises: Array>): GraphValue { - switch (valueType) { - case GraphValueTypes.NULL: - return null; - - case GraphValueTypes.STRING: - case GraphValueTypes.INTEGER: - return value; - - case GraphValueTypes.BOOLEAN: - return value === 'true'; - - case GraphValueTypes.DOUBLE: - return parseFloat(value); - - case GraphValueTypes.ARRAY: - return value.map(x => this.#parseValue(x, promises)); - - case GraphValueTypes.EDGE: - return this.#parseEdge(value, promises); - - case GraphValueTypes.NODE: - return this.#parseNode(value, promises); - - case GraphValueTypes.PATH: - return { - nodes: value[0][1].map(([, node]) => this.#parseNode(node, promises)), - edges: value[1][1].map(([, edge]) => this.#parseEdge(edge, promises)) - }; - - case GraphValueTypes.MAP: - const map: GraphMap = {}; - for (let i = 0; i < value.length; i++) { - map[value[i++] as string] = this.#parseValue(value[i] as GraphRawValue, promises); - } - - return map; - - case GraphValueTypes.POINT: - return { - latitude: parseFloat(value[0]), - longitude: parseFloat(value[1]) - }; - - default: - throw new Error(`unknown scalar type: ${valueType}`); - } - } - - #parseEdge([ - id, - relationshipTypeId, - sourceId, - destinationId, - properties - ]: GraphEdgeRawValue[1], promises: Array>): GraphEdge { - const edge = { - id, - sourceId, - destinationId, - properties: this.#parseProperties(properties, promises) - } as GraphEdge; - - const relationshipType = this.#getMetadata('relationshipTypes', relationshipTypeId); - if (relationshipType instanceof Promise) { - promises.push( - relationshipType.then(value => edge.relationshipType = value) - ); - } else { - edge.relationshipType = relationshipType; - } - - return edge; - } - - #parseNode([ - id, - labelIds, - properties - ]: GraphNodeRawValue[1], promises: Array>): GraphNode { - const labels = new Array(labelIds.length); - for (let i = 0; i < labelIds.length; i++) { - const value = this.#getMetadata('labels', labelIds[i]); - if (value instanceof Promise) { - promises.push(value.then(value => labels[i] = value)); - } else { - labels[i] = value; - } - } - - return { - id, - labels, - properties: this.#parseProperties(properties, promises) - }; - } - - #parseProperties(raw: GraphEntityRawProperties, promises: Array>): GraphEntityProperties { - const parsed: GraphEntityProperties = {}; - for (const [id, type, value] of raw) { - const parsedValue = this.#parseValue([type, value] as GraphRawValue, promises), - key = this.#getMetadata('propertyKeys', id); - if (key instanceof Promise) { - promises.push(key.then(key => parsed[key] = parsedValue)); - } else { - parsed[key] = parsedValue; - } - } - - return parsed; - } -} diff --git a/packages/graph/lib/index.ts b/packages/graph/lib/index.ts deleted file mode 100644 index e9f15ab1fd9..00000000000 --- a/packages/graph/lib/index.ts +++ /dev/null @@ -1,2 +0,0 @@ -export { default } from './commands'; -export { default as Graph } from './graph'; diff --git a/packages/graph/lib/test-utils.ts b/packages/graph/lib/test-utils.ts deleted file mode 100644 index 16c44582061..00000000000 --- a/packages/graph/lib/test-utils.ts +++ /dev/null @@ -1,22 +0,0 @@ -import TestUtils from '@redis/test-utils'; -import RedisGraph from '.'; - - -export default TestUtils.createFromConfig({ - dockerImageName: 'redislabs/client-libs-test', - dockerImageVersionArgument: 'redis-version', - defaultDockerVersion: '8.0-M04-pre' -}); - -export const GLOBAL = { - SERVERS: { - OPEN: { - serverArguments: [], - clientOptions: { - modules: { - graph: RedisGraph - } - } - } - } -}; diff --git a/packages/graph/package.json b/packages/graph/package.json deleted file mode 100644 index 1ea08401f1b..00000000000 --- a/packages/graph/package.json +++ /dev/null @@ -1,35 +0,0 @@ -{ - "name": "@redis/graph", - "version": "5.0.0-next.6", - "license": "MIT", - "main": "./dist/lib/index.js", - "types": "./dist/lib/index.d.ts", - "files": [ - "dist/", - "!dist/tsconfig.tsbuildinfo" - ], - "scripts": { - "test-disable": "nyc -r text-summary -r lcov mocha -r tsx './lib/**/*.spec.ts'" - }, - "peerDependencies": { - "@redis/client": "^5.0.0-next.6" - }, - "devDependencies": { - "@redis/test-utils": "*" - }, - "engines": { - "node": ">= 18" - }, - "repository": { - "type": "git", - "url": "git://github.com/redis/node-redis.git" - }, - "bugs": { - "url": "https://github.com/redis/node-redis/issues" - }, - "homepage": "https://github.com/redis/node-redis/tree/master/packages/graph", - "keywords": [ - "redis", - "RedisGraph" - ] -} diff --git a/packages/graph/tsconfig.json b/packages/graph/tsconfig.json deleted file mode 100644 index 9d17cb63371..00000000000 --- a/packages/graph/tsconfig.json +++ /dev/null @@ -1,20 +0,0 @@ -{ - "extends": "../../tsconfig.base.json", - "compilerOptions": { - "outDir": "./dist" - }, - "include": [ - "./lib/**/*.ts" - ], - "exclude": [ - "./lib/test-utils.ts", - "./lib/**/*.spec.ts" - ], - "typedocOptions": { - "entryPoints": [ - "./lib" - ], - "entryPointStrategy": "expand", - "out": "../../documentation/graph" - } -} diff --git a/packages/redis/index.ts b/packages/redis/index.ts index 572b45b707b..73477363d3b 100644 --- a/packages/redis/index.ts +++ b/packages/redis/index.ts @@ -15,21 +15,18 @@ import { createSentinel as genericCreateSentinel } from '@redis/client'; import RedisBloomModules from '@redis/bloom'; -import RedisGraph from '@redis/graph'; import RedisJSON from '@redis/json'; import RediSearch from '@redis/search'; import RedisTimeSeries from '@redis/time-series'; // export * from '@redis/client'; // export * from '@redis/bloom'; -// export * from '@redis/graph'; // export * from '@redis/json'; // export * from '@redis/search'; // export * from '@redis/time-series'; const modules = { ...RedisBloomModules, - graph: RedisGraph, json: RedisJSON, ft: RediSearch, ts: RedisTimeSeries diff --git a/packages/redis/package.json b/packages/redis/package.json index 5069d936ba8..c9719370e88 100644 --- a/packages/redis/package.json +++ b/packages/redis/package.json @@ -12,7 +12,6 @@ "dependencies": { "@redis/bloom": "5.0.0-next.6", "@redis/client": "5.0.0-next.6", - "@redis/graph": "5.0.0-next.6", "@redis/json": "5.0.0-next.6", "@redis/search": "5.0.0-next.6", "@redis/time-series": "5.0.0-next.6" diff --git a/tsconfig.json b/tsconfig.json index 8f43ab41d22..180b3fc2ba7 100644 --- a/tsconfig.json +++ b/tsconfig.json @@ -10,9 +10,6 @@ { "path": "./packages/bloom" }, - { - "path": "./packages/graph" - }, { "path": "./packages/json" }, From a7feb60e0af755b706a09208fcacbc553d6327c3 Mon Sep 17 00:00:00 2001 From: Hristo Temelski Date: Tue, 18 Mar 2025 15:24:11 +0200 Subject: [PATCH 1508/1748] tests: bumped the version of the 8 docker test image to '8.0-M05-pre' (#2909) --- .github/workflows/tests.yml | 2 +- packages/bloom/lib/test-utils.ts | 2 +- packages/client/lib/test-utils.ts | 2 +- packages/entraid/lib/test-utils.ts | 2 +- packages/json/lib/test-utils.ts | 2 +- packages/search/lib/commands/PROFILE_AGGREGATE.spec.ts | 6 ++---- packages/search/lib/test-utils.ts | 2 +- packages/test-utils/lib/index.ts | 3 +++ packages/time-series/lib/test-utils.ts | 2 +- 9 files changed, 12 insertions(+), 11 deletions(-) diff --git a/.github/workflows/tests.yml b/.github/workflows/tests.yml index 7bcc72e5408..366a24b4dc6 100644 --- a/.github/workflows/tests.yml +++ b/.github/workflows/tests.yml @@ -22,7 +22,7 @@ jobs: fail-fast: false matrix: node-version: [ '18', '20', '22' ] - redis-version: [ 'rs-7.2.0-v13', 'rs-7.4.0-v1', '8.0-M04-pre' ] + redis-version: [ 'rs-7.2.0-v13', 'rs-7.4.0-v1', '8.0-M05-pre' ] steps: - uses: actions/checkout@v4 with: diff --git a/packages/bloom/lib/test-utils.ts b/packages/bloom/lib/test-utils.ts index 7996d61e44e..71b423b41ea 100644 --- a/packages/bloom/lib/test-utils.ts +++ b/packages/bloom/lib/test-utils.ts @@ -4,7 +4,7 @@ import RedisBloomModules from '.'; export default TestUtils.createFromConfig({ dockerImageName: 'redislabs/client-libs-test', dockerImageVersionArgument: 'redis-version', - defaultDockerVersion: '8.0-M04-pre' + defaultDockerVersion: '8.0-M05-pre' }); export const GLOBAL = { diff --git a/packages/client/lib/test-utils.ts b/packages/client/lib/test-utils.ts index dce1f97d88a..f7862a9d685 100644 --- a/packages/client/lib/test-utils.ts +++ b/packages/client/lib/test-utils.ts @@ -8,7 +8,7 @@ import { BasicCommandParser } from './client/parser'; const utils = TestUtils.createFromConfig({ dockerImageName: 'redislabs/client-libs-test', dockerImageVersionArgument: 'redis-version', - defaultDockerVersion: '8.0-M04-pre' + defaultDockerVersion: '8.0-M05-pre' }); export default utils; diff --git a/packages/entraid/lib/test-utils.ts b/packages/entraid/lib/test-utils.ts index 970637a1225..11ad498f0b3 100644 --- a/packages/entraid/lib/test-utils.ts +++ b/packages/entraid/lib/test-utils.ts @@ -6,7 +6,7 @@ import { EntraidCredentialsProvider } from './entraid-credentials-provider'; export const testUtils = TestUtils.createFromConfig({ dockerImageName: 'redislabs/client-libs-test', dockerImageVersionArgument: 'redis-version', - defaultDockerVersion: '8.0-M04-pre' + defaultDockerVersion: '8.0-M05-pre' }); const DEBUG_MODE_ARGS = testUtils.isVersionGreaterThan([7]) ? diff --git a/packages/json/lib/test-utils.ts b/packages/json/lib/test-utils.ts index caa1c3049af..9894b2d0399 100644 --- a/packages/json/lib/test-utils.ts +++ b/packages/json/lib/test-utils.ts @@ -4,7 +4,7 @@ import RedisJSON from '.'; export default TestUtils.createFromConfig({ dockerImageName: 'redislabs/client-libs-test', dockerImageVersionArgument: 'redis-version', - defaultDockerVersion: '8.0-M04-pre' + defaultDockerVersion: '8.0-M05-pre' }); export const GLOBAL = { diff --git a/packages/search/lib/commands/PROFILE_AGGREGATE.spec.ts b/packages/search/lib/commands/PROFILE_AGGREGATE.spec.ts index bdf452c16ea..ca2302f08ea 100644 --- a/packages/search/lib/commands/PROFILE_AGGREGATE.spec.ts +++ b/packages/search/lib/commands/PROFILE_AGGREGATE.spec.ts @@ -59,7 +59,7 @@ describe('PROFILE AGGREGATE', () => { assert.ok(shardProfile.includes('Warning')); assert.ok(shardProfile.includes('Iterators profile')); - }, GLOBAL.SERVERS.OPEN); + }, Object.assign(GLOBAL.SERVERS.OPEN, {skipTest: true})); testUtils.testWithClientIfVersionWithinRange([[7, 2, 0], [7, 4, 0]], 'client.ft.search', async client => { await Promise.all([ @@ -106,7 +106,5 @@ describe('PROFILE AGGREGATE', () => { const normalizedRes = normalizeObject(res); assert.equal(normalizedRes.Results.total_results, 1); assert.ok(normalizedRes.Profile.Shards); - - }, GLOBAL.SERVERS.OPEN_3) - + }, Object.assign(GLOBAL.SERVERS.OPEN_3, {skipTest: true})); }); diff --git a/packages/search/lib/test-utils.ts b/packages/search/lib/test-utils.ts index 1318676042e..7264b1b6b12 100644 --- a/packages/search/lib/test-utils.ts +++ b/packages/search/lib/test-utils.ts @@ -5,7 +5,7 @@ import { RespVersions } from '@redis/client'; export default TestUtils.createFromConfig({ dockerImageName: 'redislabs/client-libs-test', dockerImageVersionArgument: 'redis-version', - defaultDockerVersion: '8.0-M04-pre' + defaultDockerVersion: '8.0-M05-pre' }); export const GLOBAL = { diff --git a/packages/test-utils/lib/index.ts b/packages/test-utils/lib/index.ts index b48f11b02c7..1c564749ff2 100644 --- a/packages/test-utils/lib/index.ts +++ b/packages/test-utils/lib/index.ts @@ -54,6 +54,7 @@ interface TestUtilsConfig { interface CommonTestOptions { serverArguments: Array; minimumDockerVersion?: Array; + skipTest?: boolean; } interface ClientTestOptions< @@ -242,6 +243,7 @@ export default class TestUtils { } it(title, async function () { + if (options.skipTest) return this.skip(); if (!dockerPromise) return this.skip(); const client = createClient({ @@ -316,6 +318,7 @@ export default class TestUtils { } it(title, async function () { + if (options.skipTest) return this.skip(); if (!dockerPromise) return this.skip(); const pool = createClientPool({ diff --git a/packages/time-series/lib/test-utils.ts b/packages/time-series/lib/test-utils.ts index 0b7e940788f..0f25341e34d 100644 --- a/packages/time-series/lib/test-utils.ts +++ b/packages/time-series/lib/test-utils.ts @@ -4,7 +4,7 @@ import TimeSeries from '.'; export default TestUtils.createFromConfig({ dockerImageName: 'redislabs/client-libs-test', dockerImageVersionArgument: 'redis-version', - defaultDockerVersion: '8.0-M04-pre' + defaultDockerVersion: '8.0-M05-pre' }); export const GLOBAL = { From 2ff5cb88d4ab1792d6ca0b613f69dfb424a92d8e Mon Sep 17 00:00:00 2001 From: Hristo Temelski Date: Wed, 19 Mar 2025 12:18:30 +0200 Subject: [PATCH 1509/1748] tests: Reenable and fix profile aggregate tests (#2910) --- packages/search/lib/commands/PROFILE_AGGREGATE.spec.ts | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/packages/search/lib/commands/PROFILE_AGGREGATE.spec.ts b/packages/search/lib/commands/PROFILE_AGGREGATE.spec.ts index ca2302f08ea..dbb834ed7c2 100644 --- a/packages/search/lib/commands/PROFILE_AGGREGATE.spec.ts +++ b/packages/search/lib/commands/PROFILE_AGGREGATE.spec.ts @@ -45,7 +45,7 @@ describe('PROFILE AGGREGATE', () => { const res = await client.ft.profileAggregate('index', '*'); const normalizedRes = normalizeObject(res); - assert.equal(normalizedRes.results.total, 1); + assert.equal(normalizedRes.results.total, 2); assert.ok(normalizedRes.profile[0] === 'Shards'); assert.ok(Array.isArray(normalizedRes.profile[1])); @@ -59,7 +59,7 @@ describe('PROFILE AGGREGATE', () => { assert.ok(shardProfile.includes('Warning')); assert.ok(shardProfile.includes('Iterators profile')); - }, Object.assign(GLOBAL.SERVERS.OPEN, {skipTest: true})); + }, GLOBAL.SERVERS.OPEN); testUtils.testWithClientIfVersionWithinRange([[7, 2, 0], [7, 4, 0]], 'client.ft.search', async client => { await Promise.all([ @@ -104,7 +104,7 @@ describe('PROFILE AGGREGATE', () => { const res = await client.ft.profileAggregate('index', '*'); const normalizedRes = normalizeObject(res); - assert.equal(normalizedRes.Results.total_results, 1); + assert.equal(normalizedRes.Results.total_results, 2); assert.ok(normalizedRes.Profile.Shards); - }, Object.assign(GLOBAL.SERVERS.OPEN_3, {skipTest: true})); + }, GLOBAL.SERVERS.OPEN_3); }); From 4cbecf6a0911d7f3b3d3d7864105d8912739ee99 Mon Sep 17 00:00:00 2001 From: Hristo Temelski Date: Wed, 19 Mar 2025 12:26:23 +0200 Subject: [PATCH 1510/1748] feat(hash field expiration): Added hash field expiration commands (#2907) * [CAE-686] Added hash field expiration commands * [CAE-686] Improve HSETEX return type * [CAE-686] Minor pushTuples change, renamed HSETEX test * [CAE-686] Changed hsetex function signature for better consistency with other commands * [CAE-686] Fixed hsetex test * [CAE-686] Bumped docker version to 8.0-M05-pre, enabled and fixed tests --- packages/client/lib/commands/HGETDEL.spec.ts | 48 ++++++++ packages/client/lib/commands/HGETDEL.ts | 13 +++ packages/client/lib/commands/HGETEX.spec.ts | 78 +++++++++++++ packages/client/lib/commands/HGETEX.ts | 42 +++++++ packages/client/lib/commands/HSETEX.spec.ts | 98 ++++++++++++++++ packages/client/lib/commands/HSETEX.ts | 110 ++++++++++++++++++ .../lib/commands/generic-transformers.ts | 2 +- packages/client/lib/commands/index.ts | 9 ++ 8 files changed, 399 insertions(+), 1 deletion(-) create mode 100644 packages/client/lib/commands/HGETDEL.spec.ts create mode 100644 packages/client/lib/commands/HGETDEL.ts create mode 100644 packages/client/lib/commands/HGETEX.spec.ts create mode 100644 packages/client/lib/commands/HGETEX.ts create mode 100644 packages/client/lib/commands/HSETEX.spec.ts create mode 100644 packages/client/lib/commands/HSETEX.ts diff --git a/packages/client/lib/commands/HGETDEL.spec.ts b/packages/client/lib/commands/HGETDEL.spec.ts new file mode 100644 index 00000000000..b2e19967f1d --- /dev/null +++ b/packages/client/lib/commands/HGETDEL.spec.ts @@ -0,0 +1,48 @@ +import { strict as assert } from 'node:assert'; +import testUtils, { GLOBAL } from '../test-utils'; +import { BasicCommandParser } from '../client/parser'; +import HGETDEL from './HGETDEL'; + +describe('HGETDEL parseCommand', () => { + it('hGetDel parseCommand base', () => { + const parser = new BasicCommandParser; + HGETDEL.parseCommand(parser, 'key', 'field'); + assert.deepEqual(parser.redisArgs, ['HGETDEL', 'key', 'FIELDS', '1', 'field']); + }); + + it('hGetDel parseCommand variadic', () => { + const parser = new BasicCommandParser; + HGETDEL.parseCommand(parser, 'key', ['field1', 'field2']); + assert.deepEqual(parser.redisArgs, ['HGETDEL', 'key', 'FIELDS', '2', 'field1', 'field2']); + }); +}); + + +describe('HGETDEL call', () => { + testUtils.testWithClientIfVersionWithinRange([[8], 'LATEST'], 'hGetDel empty single field', async client => { + assert.deepEqual( + await client.hGetDel('key', 'filed1'), + [null] + ); + }, GLOBAL.SERVERS.OPEN); + + testUtils.testWithClientIfVersionWithinRange([[8], 'LATEST'], 'hGetDel empty multiple fields', async client => { + assert.deepEqual( + await client.hGetDel('key', ['filed1', 'field2']), + [null, null] + ); + }, GLOBAL.SERVERS.OPEN); + + testUtils.testWithClientIfVersionWithinRange([[8], 'LATEST'], 'hGetDel partially populated multiple fields', async client => { + await client.hSet('key', 'field1', 'value1') + assert.deepEqual( + await client.hGetDel('key', ['field1', 'field2']), + ['value1', null] + ); + + assert.deepEqual( + await client.hGetDel('key', 'field1'), + [null] + ); + }, GLOBAL.SERVERS.OPEN); +}); diff --git a/packages/client/lib/commands/HGETDEL.ts b/packages/client/lib/commands/HGETDEL.ts new file mode 100644 index 00000000000..a0326c425ea --- /dev/null +++ b/packages/client/lib/commands/HGETDEL.ts @@ -0,0 +1,13 @@ +import { CommandParser } from '../client/parser'; +import { RedisVariadicArgument } from './generic-transformers'; +import { RedisArgument, ArrayReply, BlobStringReply, NullReply, Command } from '../RESP/types'; + +export default { + parseCommand(parser: CommandParser, key: RedisArgument, fields: RedisVariadicArgument) { + parser.push('HGETDEL'); + parser.pushKey(key); + parser.push('FIELDS') + parser.pushVariadicWithLength(fields); + }, + transformReply: undefined as unknown as () => ArrayReply +} as const satisfies Command; diff --git a/packages/client/lib/commands/HGETEX.spec.ts b/packages/client/lib/commands/HGETEX.spec.ts new file mode 100644 index 00000000000..2625a0ac023 --- /dev/null +++ b/packages/client/lib/commands/HGETEX.spec.ts @@ -0,0 +1,78 @@ +import { strict as assert } from 'node:assert'; +import testUtils,{ GLOBAL } from '../test-utils'; +import { BasicCommandParser } from '../client/parser'; +import HGETEX from './HGETEX'; +import { setTimeout } from 'timers/promises'; + +describe('HGETEX parseCommand', () => { + it('hGetEx parseCommand base', () => { + const parser = new BasicCommandParser; + HGETEX.parseCommand(parser, 'key', 'field'); + assert.deepEqual(parser.redisArgs, ['HGETEX', 'key', 'FIELDS', '1', 'field']); + }); + + it('hGetEx parseCommand expiration PERSIST string', () => { + const parser = new BasicCommandParser; + HGETEX.parseCommand(parser, 'key', 'field', {expiration: 'PERSIST'}); + assert.deepEqual(parser.redisArgs, ['HGETEX', 'key', 'PERSIST', 'FIELDS', '1', 'field']); + }); + + it('hGetEx parseCommand expiration PERSIST obj', () => { + const parser = new BasicCommandParser; + HGETEX.parseCommand(parser, 'key', 'field', {expiration: {type: 'PERSIST'}}); + assert.deepEqual(parser.redisArgs, ['HGETEX', 'key', 'PERSIST', 'FIELDS', '1', 'field']); + }); + + it('hGetEx parseCommand expiration EX obj', () => { + const parser = new BasicCommandParser; + HGETEX.parseCommand(parser, 'key', 'field', {expiration: {type: 'EX', value: 1000}}); + assert.deepEqual(parser.redisArgs, ['HGETEX', 'key', 'EX', '1000', 'FIELDS', '1', 'field']); + }); + + it('hGetEx parseCommand expiration EXAT obj variadic', () => { + const parser = new BasicCommandParser; + HGETEX.parseCommand(parser, 'key', ['field1', 'field2'], {expiration: {type: 'EXAT', value: 1000}}); + assert.deepEqual(parser.redisArgs, ['HGETEX', 'key', 'EXAT', '1000', 'FIELDS', '2', 'field1', 'field2']); + }); +}); + + +describe('HGETEX call', () => { + testUtils.testWithClientIfVersionWithinRange([[8], 'LATEST'], 'hGetEx empty single field', async client => { + assert.deepEqual( + await client.hGetEx('key', 'field1', {expiration: 'PERSIST'}), + [null] + ); + }, GLOBAL.SERVERS.OPEN); + + testUtils.testWithClientIfVersionWithinRange([[8], 'LATEST'], 'hGetEx empty multiple fields', async client => { + assert.deepEqual( + await client.hGetEx('key', ['field1', 'field2'], {expiration: 'PERSIST'}), + [null, null] + ); + }, GLOBAL.SERVERS.OPEN); + + testUtils.testWithClientIfVersionWithinRange([[8], 'LATEST'], 'hGetEx set expiry', async client => { + await client.hSet('key', 'field', 'value') + assert.deepEqual( + await client.hGetEx('key', 'field', {expiration: {type: 'PX', value: 50}}), + ['value'] + ); + await setTimeout(100) + assert.deepEqual( + await client.hGet('key', 'field'), + null + ); + }, GLOBAL.SERVERS.OPEN); + + testUtils.testWithClientIfVersionWithinRange([[8], 'LATEST'], 'gGetEx set expiry PERSIST', async client => { + await client.hSet('key', 'field', 'value') + await client.hGetEx('key', 'field', {expiration: {type: 'PX', value: 50}}) + await client.hGetEx('key', 'field', {expiration: 'PERSIST'}) + await setTimeout(100) + assert.deepEqual( + await client.hGet('key', 'field'), + 'value' + ) + }, GLOBAL.SERVERS.OPEN); +}); \ No newline at end of file diff --git a/packages/client/lib/commands/HGETEX.ts b/packages/client/lib/commands/HGETEX.ts new file mode 100644 index 00000000000..ce265e15bd6 --- /dev/null +++ b/packages/client/lib/commands/HGETEX.ts @@ -0,0 +1,42 @@ +import { CommandParser } from '../client/parser'; +import { RedisVariadicArgument } from './generic-transformers'; +import { ArrayReply, Command, BlobStringReply, NullReply, RedisArgument } from '../RESP/types'; + +export interface HGetExOptions { + expiration?: { + type: 'EX' | 'PX' | 'EXAT' | 'PXAT'; + value: number; + } | { + type: 'PERSIST'; + } | 'PERSIST'; +} + +export default { + parseCommand( + parser: CommandParser, + key: RedisArgument, + fields: RedisVariadicArgument, + options?: HGetExOptions + ) { + parser.push('HGETEX'); + parser.pushKey(key); + + if (options?.expiration) { + if (typeof options.expiration === 'string') { + parser.push(options.expiration); + } else if (options.expiration.type === 'PERSIST') { + parser.push('PERSIST'); + } else { + parser.push( + options.expiration.type, + options.expiration.value.toString() + ); + } + } + + parser.push('FIELDS') + + parser.pushVariadicWithLength(fields); + }, + transformReply: undefined as unknown as () => ArrayReply +} as const satisfies Command; diff --git a/packages/client/lib/commands/HSETEX.spec.ts b/packages/client/lib/commands/HSETEX.spec.ts new file mode 100644 index 00000000000..fc38e0f0f45 --- /dev/null +++ b/packages/client/lib/commands/HSETEX.spec.ts @@ -0,0 +1,98 @@ +import { strict as assert } from 'node:assert'; +import testUtils,{ GLOBAL } from '../test-utils'; +import { BasicCommandParser } from '../client/parser'; +import HSETEX from './HSETEX'; + +describe('HSETEX parseCommand', () => { + it('hSetEx parseCommand base', () => { + const parser = new BasicCommandParser; + HSETEX.parseCommand(parser, 'key', ['field', 'value']); + assert.deepEqual(parser.redisArgs, ['HSETEX', 'key', 'FIELDS', '1', 'field', 'value']); + }); + + it('hSetEx parseCommand base empty obj', () => { + const parser = new BasicCommandParser; + assert.throws(() => {HSETEX.parseCommand(parser, 'key', {})}); + }); + + it('hSetEx parseCommand base one key obj', () => { + const parser = new BasicCommandParser; + HSETEX.parseCommand(parser, 'key', {'k': 'v'}); + assert.deepEqual(parser.redisArgs, ['HSETEX', 'key', 'FIELDS', '1', 'k', 'v']); + }); + + it('hSetEx parseCommand array', () => { + const parser = new BasicCommandParser; + HSETEX.parseCommand(parser, 'key', ['field1', 'value1', 'field2', 'value2']); + assert.deepEqual(parser.redisArgs, ['HSETEX', 'key', 'FIELDS', '2', 'field1', 'value1', 'field2', 'value2']); + }); + + it('hSetEx parseCommand array invalid args, throws an error', () => { + const parser = new BasicCommandParser; + assert.throws(() => {HSETEX.parseCommand(parser, 'key', ['field1', 'value1', 'field2'])}); + }); + + it('hSetEx parseCommand array in array', () => { + const parser1 = new BasicCommandParser; + HSETEX.parseCommand(parser1, 'key', [['field1', 'value1'], ['field2', 'value2']]); + assert.deepEqual(parser1.redisArgs, ['HSETEX', 'key', 'FIELDS', '2', 'field1', 'value1', 'field2', 'value2']); + + const parser2 = new BasicCommandParser; + HSETEX.parseCommand(parser2, 'key', [['field1', 'value1'], ['field2', 'value2'], ['field3', 'value3']]); + assert.deepEqual(parser2.redisArgs, ['HSETEX', 'key', 'FIELDS', '3', 'field1', 'value1', 'field2', 'value2', 'field3', 'value3']); + }); + + it('hSetEx parseCommand map', () => { + const parser1 = new BasicCommandParser; + HSETEX.parseCommand(parser1, 'key', new Map([['field1', 'value1'], ['field2', 'value2']])); + assert.deepEqual(parser1.redisArgs, ['HSETEX', 'key', 'FIELDS', '2', 'field1', 'value1', 'field2', 'value2']); + }); + + it('hSetEx parseCommand obj', () => { + const parser1 = new BasicCommandParser; + HSETEX.parseCommand(parser1, 'key', {field1: "value1", field2: "value2"}); + assert.deepEqual(parser1.redisArgs, ['HSETEX', 'key', 'FIELDS', '2', 'field1', 'value1', 'field2', 'value2']); + }); + + it('hSetEx parseCommand options FNX KEEPTTL', () => { + const parser = new BasicCommandParser; + HSETEX.parseCommand(parser, 'key', ['field', 'value'], {mode: 'FNX', expiration: 'KEEPTTL'}); + assert.deepEqual(parser.redisArgs, ['HSETEX', 'key', 'FNX', 'KEEPTTL', 'FIELDS', '1', 'field', 'value']); + }); + + it('hSetEx parseCommand options FXX EX 500', () => { + const parser = new BasicCommandParser; + HSETEX.parseCommand(parser, 'key', ['field', 'value'], {mode: 'FXX', expiration: {type: 'EX', value: 500}}); + assert.deepEqual(parser.redisArgs, ['HSETEX', 'key', 'FXX', 'EX', '500', 'FIELDS', '1', 'field', 'value']); + }); +}); + + +describe('HSETEX call', () => { + testUtils.testWithClientIfVersionWithinRange([[8], 'LATEST'], 'hSetEx calls', async client => { + assert.deepEqual( + await client.hSetEx('key_hsetex_call', ['field1', 'value1'], {expiration: {type: "EX", value: 500}, mode: "FNX"}), + 1 + ); + + assert.deepEqual( + await client.hSetEx('key_hsetex_call', ['field1', 'value1', 'field2', 'value2'], {expiration: {type: "EX", value: 500}, mode: "FXX"}), + 0 + ); + + assert.deepEqual( + await client.hSetEx('key_hsetex_call', ['field1', 'value1', 'field2', 'value2'], {expiration: {type: "EX", value: 500}, mode: "FNX"}), + 0 + ); + + assert.deepEqual( + await client.hSetEx('key_hsetex_call', ['field2', 'value2'], {expiration: {type: "EX", value: 500}, mode: "FNX"}), + 1 + ); + + assert.deepEqual( + await client.hSetEx('key_hsetex_call', ['field1', 'value1', 'field2', 'value2'], {expiration: {type: "EX", value: 500}, mode: "FXX"}), + 1 + ); + }, GLOBAL.SERVERS.OPEN); +}); \ No newline at end of file diff --git a/packages/client/lib/commands/HSETEX.ts b/packages/client/lib/commands/HSETEX.ts new file mode 100644 index 00000000000..3827538934c --- /dev/null +++ b/packages/client/lib/commands/HSETEX.ts @@ -0,0 +1,110 @@ +import { BasicCommandParser, CommandParser } from '../client/parser'; +import { Command, NumberReply, RedisArgument } from '../RESP/types'; + +export interface HSetExOptions { + expiration?: { + type: 'EX' | 'PX' | 'EXAT' | 'PXAT'; + value: number; + } | { + type: 'KEEPTTL'; + } | 'KEEPTTL'; + mode?: 'FNX' | 'FXX' + } + +export type HashTypes = RedisArgument | number; + +type HSETEXObject = Record; + +type HSETEXMap = Map; + +type HSETEXTuples = Array<[HashTypes, HashTypes]> | Array; + +export default { + parseCommand( + parser: CommandParser, + key: RedisArgument, + fields: HSETEXObject | HSETEXMap | HSETEXTuples, + options?: HSetExOptions + ) { + parser.push('HSETEX'); + parser.pushKey(key); + + if (options?.mode) { + parser.push(options.mode) + } + if (options?.expiration) { + if (typeof options.expiration === 'string') { + parser.push(options.expiration); + } else if (options.expiration.type === 'KEEPTTL') { + parser.push('KEEPTTL'); + } else { + parser.push( + options.expiration.type, + options.expiration.value.toString() + ); + } + } + + parser.push('FIELDS') + if (fields instanceof Map) { + pushMap(parser, fields); + } else if (Array.isArray(fields)) { + pushTuples(parser, fields); + } else { + pushObject(parser, fields); + } + }, + transformReply: undefined as unknown as () => NumberReply<0 | 1> +} as const satisfies Command; + + +function pushMap(parser: CommandParser, map: HSETEXMap): void { + parser.push(map.size.toString()) + for (const [key, value] of map.entries()) { + parser.push( + convertValue(key), + convertValue(value) + ); + } +} + +function pushTuples(parser: CommandParser, tuples: HSETEXTuples): void { + const tmpParser = new BasicCommandParser + _pushTuples(tmpParser, tuples) + + if (tmpParser.redisArgs.length%2 != 0) { + throw Error('invalid number of arguments, expected key value ....[key value] pairs, got key without value') + } + + parser.push((tmpParser.redisArgs.length/2).toString()) + parser.push(...tmpParser.redisArgs) +} + +function _pushTuples(parser: CommandParser, tuples: HSETEXTuples): void { + for (const tuple of tuples) { + if (Array.isArray(tuple)) { + _pushTuples(parser, tuple); + continue; + } + parser.push(convertValue(tuple)); + } +} + +function pushObject(parser: CommandParser, object: HSETEXObject): void { + const len = Object.keys(object).length + if (len == 0) { + throw Error('object without keys') + } + + parser.push(len.toString()) + for (const key of Object.keys(object)) { + parser.push( + convertValue(key), + convertValue(object[key]) + ); + } +} + +function convertValue(value: HashTypes): RedisArgument { + return typeof value === 'number' ? value.toString() : value; +} \ No newline at end of file diff --git a/packages/client/lib/commands/generic-transformers.ts b/packages/client/lib/commands/generic-transformers.ts index fc139a948e0..91eab7107a1 100644 --- a/packages/client/lib/commands/generic-transformers.ts +++ b/packages/client/lib/commands/generic-transformers.ts @@ -269,7 +269,7 @@ export function pushVariadicArgument( return args; } -export function parseOptionalVariadicArgument( +export function parseOptionalVariadicArgument( parser: CommandParser, name: RedisArgument, value?: RedisVariadicArgument diff --git a/packages/client/lib/commands/index.ts b/packages/client/lib/commands/index.ts index 024ee2191b8..5cd81331a4e 100644 --- a/packages/client/lib/commands/index.ts +++ b/packages/client/lib/commands/index.ts @@ -138,6 +138,8 @@ import HEXPIREAT from './HEXPIREAT'; import HEXPIRETIME from './HEXPIRETIME'; import HGET from './HGET'; import HGETALL from './HGETALL'; +import HGETDEL from './HGETDEL'; +import HGETEX from './HGETEX'; import HINCRBY from './HINCRBY'; import HINCRBYFLOAT from './HINCRBYFLOAT'; import HKEYS from './HKEYS'; @@ -154,6 +156,7 @@ import HRANDFIELD from './HRANDFIELD'; import HSCAN from './HSCAN'; import HSCAN_NOVALUES from './HSCAN_NOVALUES'; import HSET from './HSET'; +import HSETEX from './HSETEX'; import HSETNX from './HSETNX'; import HSTRLEN from './HSTRLEN'; import HTTL from './HTTL'; @@ -621,6 +624,10 @@ export default { hGet: HGET, HGETALL, hGetAll: HGETALL, + HGETDEL, + hGetDel: HGETDEL, + HGETEX, + hGetEx: HGETEX, HINCRBY, hIncrBy: HINCRBY, HINCRBYFLOAT, @@ -653,6 +660,8 @@ export default { hScanNoValues: HSCAN_NOVALUES, HSET, hSet: HSET, + HSETEX, + hSetEx: HSETEX, HSETNX, hSetNX: HSETNX, HSTRLEN, From 6c5a3fd0c0d51313652771389e650cb28f3e8444 Mon Sep 17 00:00:00 2001 From: "Bobby I." Date: Thu, 20 Mar 2025 12:31:25 +0200 Subject: [PATCH 1511/1748] fix(entraid): correct package entry point structure (#2891) - Add /index.ts that re-exports all from /lib/index.ts - Preserve existing /lib/index.ts exports --- packages/entraid/README.md | 2 +- packages/entraid/index.ts | 1 + 2 files changed, 2 insertions(+), 1 deletion(-) create mode 100644 packages/entraid/index.ts diff --git a/packages/entraid/README.md b/packages/entraid/README.md index f2212848455..7a1ab1a94a2 100644 --- a/packages/entraid/README.md +++ b/packages/entraid/README.md @@ -39,7 +39,7 @@ The first step to using @redis/entraid is choosing the right credentials provide ```typescript import { createClient } from '@redis/client'; -import { EntraIdCredentialsProviderFactory } from '@redis/entraid/dist/lib/entra-id-credentials-provider-factory'; +import { EntraIdCredentialsProviderFactory } from '@redis/entraid'; const provider = EntraIdCredentialsProviderFactory.createForClientCredentials({ clientId: 'your-client-id', diff --git a/packages/entraid/index.ts b/packages/entraid/index.ts new file mode 100644 index 00000000000..303b5dc6e14 --- /dev/null +++ b/packages/entraid/index.ts @@ -0,0 +1 @@ +export * from './lib/index' \ No newline at end of file From d64072da95a46331a9bd3857c221c4f889d79259 Mon Sep 17 00:00:00 2001 From: Hristo Temelski Date: Fri, 21 Mar 2025 11:43:10 +0200 Subject: [PATCH 1512/1748] feat(integer 8 vector support): Changed ft create vector types to union, added support for int8/uint8 (#2911) * [CAE-827] Changed ft create vector types to union, added support for int8/uint8 * [CAE-827] Moved test cases --- packages/search/lib/commands/CREATE.spec.ts | 83 +++++++++++++++++++++ packages/search/lib/commands/CREATE.ts | 2 +- 2 files changed, 84 insertions(+), 1 deletion(-) diff --git a/packages/search/lib/commands/CREATE.spec.ts b/packages/search/lib/commands/CREATE.spec.ts index 58888fb7ce4..2c54d3d0235 100644 --- a/packages/search/lib/commands/CREATE.spec.ts +++ b/packages/search/lib/commands/CREATE.spec.ts @@ -473,4 +473,87 @@ describe('FT.CREATE', () => { 'OK' ); }, GLOBAL.SERVERS.OPEN); + + testUtils.testWithClientIfVersionWithinRange([[7], 'LATEST'], 'client.ft.create vector types big floats', async client => { + assert.equal( + await client.ft.create("index_float32", { + field: { + ALGORITHM: "FLAT", + TYPE: "FLOAT32", + DIM: 1, + DISTANCE_METRIC: 'COSINE', + type: 'VECTOR' + }, + }), + "OK" + ); + + assert.equal( + await client.ft.create("index_float64", { + field: { + ALGORITHM: "FLAT", + TYPE: "FLOAT64", + DIM: 1, + DISTANCE_METRIC: 'COSINE', + type: 'VECTOR' + }, + }), + "OK" + ); + }, GLOBAL.SERVERS.OPEN); + + + testUtils.testWithClientIfVersionWithinRange([[8], 'LATEST'], 'client.ft.create vector types small floats and ints', async client => { + assert.equal( + await client.ft.create("index_float16", { + field: { + ALGORITHM: "FLAT", + TYPE: "FLOAT16", + DIM: 1, + DISTANCE_METRIC: 'COSINE', + type: 'VECTOR' + }, + }), + "OK" + ); + + assert.equal( + await client.ft.create("index_bloat16", { + field: { + ALGORITHM: "FLAT", + TYPE: "BFLOAT16", + DIM: 1, + DISTANCE_METRIC: 'COSINE', + type: 'VECTOR' + }, + }), + "OK" + ); + + assert.equal( + await client.ft.create("index_int8", { + field: { + ALGORITHM: "FLAT", + TYPE: "INT8", + DIM: 1, + DISTANCE_METRIC: 'COSINE', + type: 'VECTOR' + }, + }), + "OK" + ); + + assert.equal( + await client.ft.create("index_uint8", { + field: { + ALGORITHM: "FLAT", + TYPE: "UINT8", + DIM: 1, + DISTANCE_METRIC: 'COSINE', + type: 'VECTOR' + }, + }), + "OK" + ); + }, GLOBAL.SERVERS.OPEN); }); diff --git a/packages/search/lib/commands/CREATE.ts b/packages/search/lib/commands/CREATE.ts index 5eec9799264..5645a2b2dce 100644 --- a/packages/search/lib/commands/CREATE.ts +++ b/packages/search/lib/commands/CREATE.ts @@ -61,7 +61,7 @@ export type SchemaVectorFieldAlgorithm = typeof SCHEMA_VECTOR_FIELD_ALGORITHM[ke interface SchemaVectorField extends SchemaField { ALGORITHM: SchemaVectorFieldAlgorithm; - TYPE: string; + TYPE: 'FLOAT32' | 'FLOAT64' | 'BFLOAT16' | 'FLOAT16' | 'INT8' | 'UINT8'; DIM: number; DISTANCE_METRIC: 'L2' | 'IP' | 'COSINE'; INITIAL_CAP?: number; From bf06a3b703050b46c033b4b0663bf8636b06a833 Mon Sep 17 00:00:00 2001 From: "H. Temelski" Date: Tue, 25 Mar 2025 10:19:53 +0200 Subject: [PATCH 1513/1748] Release client@5.0.0-next.7 --- packages/client/package.json | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/packages/client/package.json b/packages/client/package.json index cc892a12a49..3372f3a5758 100644 --- a/packages/client/package.json +++ b/packages/client/package.json @@ -1,6 +1,6 @@ { "name": "@redis/client", - "version": "5.0.0-next.6", + "version": "5.0.0-next.7", "license": "MIT", "main": "./dist/index.js", "types": "./dist/index.d.ts", From 4f88442f6fe55a2bc3a0f40828f7cef31242dc29 Mon Sep 17 00:00:00 2001 From: "H. Temelski" Date: Tue, 25 Mar 2025 10:25:18 +0200 Subject: [PATCH 1514/1748] Updated the EntraID package to use client@5.0.0-next.7 --- packages/entraid/package.json | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/packages/entraid/package.json b/packages/entraid/package.json index fd504cd44dc..bbf6cc363a6 100644 --- a/packages/entraid/package.json +++ b/packages/entraid/package.json @@ -21,7 +21,7 @@ "@azure/msal-node": "^2.16.1" }, "peerDependencies": { - "@redis/client": "^5.0.0-next.6" + "@redis/client": "^5.0.0-next.7" }, "devDependencies": { "@types/express": "^4.17.21", From 8ab820c0db6a46126e537c96ea8c004e7dac363a Mon Sep 17 00:00:00 2001 From: "H. Temelski" Date: Tue, 25 Mar 2025 10:27:10 +0200 Subject: [PATCH 1515/1748] Release entraid@5.0.0-next.7 --- packages/entraid/package.json | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/packages/entraid/package.json b/packages/entraid/package.json index bbf6cc363a6..ad9cef61f4e 100644 --- a/packages/entraid/package.json +++ b/packages/entraid/package.json @@ -1,6 +1,6 @@ { "name": "@redis/entraid", - "version": "5.0.0-next.6", + "version": "5.0.0-next.7", "license": "MIT", "main": "./dist/index.js", "types": "./dist/index.d.ts", From 60b81ff7f061b51f8e2d38f7fad59e6cd9357fe7 Mon Sep 17 00:00:00 2001 From: "H. Temelski" Date: Tue, 25 Mar 2025 10:28:15 +0200 Subject: [PATCH 1516/1748] Updated the JSON package to use client@5.0.0-next.7 --- packages/json/package.json | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/packages/json/package.json b/packages/json/package.json index b7a972c8c7a..6cdd78f6564 100644 --- a/packages/json/package.json +++ b/packages/json/package.json @@ -12,7 +12,7 @@ "test": "nyc -r text-summary -r lcov mocha -r tsx './lib/**/*.spec.ts'" }, "peerDependencies": { - "@redis/client": "^5.0.0-next.6" + "@redis/client": "^5.0.0-next.7" }, "devDependencies": { "@redis/test-utils": "*" From 5a2a61496eeb578ad39ce2e9821802a771addd71 Mon Sep 17 00:00:00 2001 From: "H. Temelski" Date: Tue, 25 Mar 2025 10:29:09 +0200 Subject: [PATCH 1517/1748] Release json@5.0.0-next.7 --- packages/json/package.json | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/packages/json/package.json b/packages/json/package.json index 6cdd78f6564..0217aba33c6 100644 --- a/packages/json/package.json +++ b/packages/json/package.json @@ -1,6 +1,6 @@ { "name": "@redis/json", - "version": "5.0.0-next.6", + "version": "5.0.0-next.7", "license": "MIT", "main": "./dist/lib/index.js", "types": "./dist/lib/index.d.ts", From c50892a7f792a3bf4cc53d6c52f834a7ea31f8f8 Mon Sep 17 00:00:00 2001 From: "H. Temelski" Date: Tue, 25 Mar 2025 10:30:08 +0200 Subject: [PATCH 1518/1748] Updated the search package to use client@5.0.0-next.7 --- packages/search/package.json | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/packages/search/package.json b/packages/search/package.json index 985356cd230..1ed118daceb 100644 --- a/packages/search/package.json +++ b/packages/search/package.json @@ -13,7 +13,7 @@ "test-sourcemap": "mocha -r ts-node/register/transpile-only './lib/**/*.spec.ts'" }, "peerDependencies": { - "@redis/client": "^5.0.0-next.6" + "@redis/client": "^5.0.0-next.7" }, "devDependencies": { "@redis/test-utils": "*" From 17b3304323653350706e713c831010e81de5e5b8 Mon Sep 17 00:00:00 2001 From: "H. Temelski" Date: Tue, 25 Mar 2025 10:31:02 +0200 Subject: [PATCH 1519/1748] Release search@5.0.0-next.7 --- packages/search/package.json | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/packages/search/package.json b/packages/search/package.json index 1ed118daceb..605ab0a7ffe 100644 --- a/packages/search/package.json +++ b/packages/search/package.json @@ -1,6 +1,6 @@ { "name": "@redis/search", - "version": "5.0.0-next.6", + "version": "5.0.0-next.7", "license": "MIT", "main": "./dist/lib/index.js", "types": "./dist/lib/index.d.ts", From abba2f8493f23fa1d13feaefe6fb132513a3edb9 Mon Sep 17 00:00:00 2001 From: "H. Temelski" Date: Tue, 25 Mar 2025 10:31:40 +0200 Subject: [PATCH 1520/1748] Updated the timeseries package to use client@5.0.0-next.7 --- packages/time-series/package.json | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/packages/time-series/package.json b/packages/time-series/package.json index 22cfe8cefba..665ba6222a3 100644 --- a/packages/time-series/package.json +++ b/packages/time-series/package.json @@ -12,7 +12,7 @@ "test": "nyc -r text-summary -r lcov mocha -r tsx './lib/**/*.spec.ts'" }, "peerDependencies": { - "@redis/client": "^5.0.0-next.6" + "@redis/client": "^5.0.0-next.7" }, "devDependencies": { "@redis/test-utils": "*" From 1b2eba2b76c2866b56e2c2501cc1a12f17f6153f Mon Sep 17 00:00:00 2001 From: "H. Temelski" Date: Tue, 25 Mar 2025 10:32:21 +0200 Subject: [PATCH 1521/1748] Release time-series@5.0.0-next.7 --- packages/time-series/package.json | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/packages/time-series/package.json b/packages/time-series/package.json index 665ba6222a3..e0c138b0dd2 100644 --- a/packages/time-series/package.json +++ b/packages/time-series/package.json @@ -1,6 +1,6 @@ { "name": "@redis/time-series", - "version": "5.0.0-next.6", + "version": "5.0.0-next.7", "license": "MIT", "main": "./dist/lib/index.js", "types": "./dist/lib/index.d.ts", From ffa00cfbe8015e3aaf86eedfd972011a44977d29 Mon Sep 17 00:00:00 2001 From: "H. Temelski" Date: Tue, 25 Mar 2025 10:33:03 +0200 Subject: [PATCH 1522/1748] Updated the bloom package to use client@5.0.0-next.7 --- packages/bloom/package.json | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/packages/bloom/package.json b/packages/bloom/package.json index 434ba809f7c..a3e83f09b60 100644 --- a/packages/bloom/package.json +++ b/packages/bloom/package.json @@ -12,7 +12,7 @@ "test": "nyc -r text-summary -r lcov mocha -r tsx './lib/**/*.spec.ts'" }, "peerDependencies": { - "@redis/client": "^5.0.0-next.6" + "@redis/client": "^5.0.0-next.7" }, "devDependencies": { "@redis/test-utils": "*" From 57e323ae16d6fca75359bd2ec85716ebff1b3aad Mon Sep 17 00:00:00 2001 From: "H. Temelski" Date: Tue, 25 Mar 2025 10:33:38 +0200 Subject: [PATCH 1523/1748] Release bloom@5.0.0-next.7 --- packages/bloom/package.json | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/packages/bloom/package.json b/packages/bloom/package.json index a3e83f09b60..d913d434a15 100644 --- a/packages/bloom/package.json +++ b/packages/bloom/package.json @@ -1,6 +1,6 @@ { "name": "@redis/bloom", - "version": "5.0.0-next.6", + "version": "5.0.0-next.7", "license": "MIT", "main": "./dist/lib/index.js", "types": "./dist/lib/index.d.ts", From a3debec021f59e633407fae69fa13f3118c6d154 Mon Sep 17 00:00:00 2001 From: "H. Temelski" Date: Tue, 25 Mar 2025 10:34:55 +0200 Subject: [PATCH 1524/1748] Updated the Redis package to use client@5.0.0-next.7 --- packages/redis/package.json | 10 +++++----- 1 file changed, 5 insertions(+), 5 deletions(-) diff --git a/packages/redis/package.json b/packages/redis/package.json index c9719370e88..b974a59ebf0 100644 --- a/packages/redis/package.json +++ b/packages/redis/package.json @@ -10,11 +10,11 @@ "!dist/tsconfig.tsbuildinfo" ], "dependencies": { - "@redis/bloom": "5.0.0-next.6", - "@redis/client": "5.0.0-next.6", - "@redis/json": "5.0.0-next.6", - "@redis/search": "5.0.0-next.6", - "@redis/time-series": "5.0.0-next.6" + "@redis/bloom": "5.0.0-next.7", + "@redis/client": "5.0.0-next.7", + "@redis/json": "5.0.0-next.7", + "@redis/search": "5.0.0-next.7", + "@redis/time-series": "5.0.0-next.7" }, "engines": { "node": ">= 18" From 73de2cecdac382e5a643f971235e5934afb16600 Mon Sep 17 00:00:00 2001 From: "H. Temelski" Date: Tue, 25 Mar 2025 10:38:18 +0200 Subject: [PATCH 1525/1748] Release redis@5.0.0-next.7 --- packages/redis/package.json | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/packages/redis/package.json b/packages/redis/package.json index b974a59ebf0..8b1567afb1d 100644 --- a/packages/redis/package.json +++ b/packages/redis/package.json @@ -1,7 +1,7 @@ { "name": "redis", "description": "A modern, high performance Redis client", - "version": "5.0.0-next.6", + "version": "5.0.0-next.7", "license": "MIT", "main": "./dist/index.js", "types": "./dist/index.d.ts", From 0f24c7fc2127ac1fa442ac4d88d25ddab11f6ed6 Mon Sep 17 00:00:00 2001 From: "Bobby I." Date: Tue, 25 Mar 2025 11:28:06 +0200 Subject: [PATCH 1526/1748] entraid: update readme.md (#2916) --- packages/entraid/README.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/packages/entraid/README.md b/packages/entraid/README.md index 7a1ab1a94a2..83ddd1a3914 100644 --- a/packages/entraid/README.md +++ b/packages/entraid/README.md @@ -39,7 +39,7 @@ The first step to using @redis/entraid is choosing the right credentials provide ```typescript import { createClient } from '@redis/client'; -import { EntraIdCredentialsProviderFactory } from '@redis/entraid'; +import { EntraIdCredentialsProviderFactory } from '@redis/entraid/dist/lib'; const provider = EntraIdCredentialsProviderFactory.createForClientCredentials({ clientId: 'your-client-id', From a7c96a01f8fc9000870e82587932f9480b63a4df Mon Sep 17 00:00:00 2001 From: "Bobby I." Date: Tue, 25 Mar 2025 13:50:16 +0200 Subject: [PATCH 1527/1748] fix (entraid): correct package entry point structure (#2917) - last time we forgot to include `index.ts` --- packages/entraid/README.md | 6 +++--- packages/entraid/tsconfig.json | 3 ++- 2 files changed, 5 insertions(+), 4 deletions(-) diff --git a/packages/entraid/README.md b/packages/entraid/README.md index 83ddd1a3914..10ecfec9973 100644 --- a/packages/entraid/README.md +++ b/packages/entraid/README.md @@ -39,7 +39,7 @@ The first step to using @redis/entraid is choosing the right credentials provide ```typescript import { createClient } from '@redis/client'; -import { EntraIdCredentialsProviderFactory } from '@redis/entraid/dist/lib'; +import { EntraIdCredentialsProviderFactory } from '@redis/entraid'; const provider = EntraIdCredentialsProviderFactory.createForClientCredentials({ clientId: 'your-client-id', @@ -86,7 +86,7 @@ const provider = EntraIdCredentialsProviderFactory.createForUserAssignedManagedI ### DefaultAzureCredential Authentication -tip: see a real sample here: [samples/interactive-browser/index.ts](./samples/interactive-browser/index.ts) +tip: see a real sample here: [samples/interactive-browser/index.ts](./samples/interactive-browser/index.ts) The DefaultAzureCredential from @azure/identity provides a simplified authentication experience that automatically tries different authentication methods based on the environment. This is especially useful for applications that need to work in different environments (local development, CI/CD, and production). @@ -128,7 +128,7 @@ When using the `createForDefaultAzureCredential` method, you need to: 2. Pass the same parameters to the factory method that you would use with the `getToken()` method: - `scopes`: The Redis scope (use the exported `REDIS_SCOPE_DEFAULT` constant) - `options`: Any additional options for the getToken method - + This factory method creates a wrapper around DefaultAzureCredential that adapts it to the Redis client's authentication system, while maintaining all the flexibility of the original Azure Identity authentication. diff --git a/packages/entraid/tsconfig.json b/packages/entraid/tsconfig.json index 414dc1fe755..47100f5b87d 100644 --- a/packages/entraid/tsconfig.json +++ b/packages/entraid/tsconfig.json @@ -4,7 +4,8 @@ "outDir": "./dist" }, "include": [ - "./lib/**/*.ts" + "./lib/**/*.ts", + "./index.ts" ], "exclude": [ "./lib/**/*.spec.ts", From 924dafabc3c177cb1d3be4df0d685f679b5bbc19 Mon Sep 17 00:00:00 2001 From: Svetlin Pavlov <58084028+spavlov6@users.noreply.github.com> Date: Wed, 26 Mar 2025 15:17:35 +0200 Subject: [PATCH 1528/1748] refactor(test-utils): remove TODO comments and TypeScript ignore directives for socket port (#2915) --- packages/test-utils/lib/index.ts | 4 ---- 1 file changed, 4 deletions(-) diff --git a/packages/test-utils/lib/index.ts b/packages/test-utils/lib/index.ts index 1c564749ff2..117d089bcbe 100644 --- a/packages/test-utils/lib/index.ts +++ b/packages/test-utils/lib/index.ts @@ -250,8 +250,6 @@ export default class TestUtils { ...options.clientOptions, socket: { ...options.clientOptions?.socket, - // TODO - // @ts-ignore port: (await dockerPromise).port } }); @@ -325,8 +323,6 @@ export default class TestUtils { ...options.clientOptions, socket: { ...options.clientOptions?.socket, - // TODO - // @ts-ignore port: (await dockerPromise).port } }, options.poolOptions); From bdf95fdfca16408adc078726282ce8a43211c077 Mon Sep 17 00:00:00 2001 From: Daniel Hensby Date: Wed, 2 Apr 2025 14:48:21 +0100 Subject: [PATCH 1529/1748] fix: loosen @azure/identity constraint for @redis/entraid (#2920) --- packages/entraid/package.json | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/packages/entraid/package.json b/packages/entraid/package.json index ad9cef61f4e..4eba7e0a788 100644 --- a/packages/entraid/package.json +++ b/packages/entraid/package.json @@ -17,7 +17,7 @@ "test": "nyc -r text-summary -r lcov mocha -r tsx './lib/**/*.spec.ts'" }, "dependencies": { - "@azure/identity": "4.7.0", + "@azure/identity": "^4.7.0", "@azure/msal-node": "^2.16.1" }, "peerDependencies": { From 4d659f0b446d19b409f53eafbf7317f5fbb917a9 Mon Sep 17 00:00:00 2001 From: Daniel Hensby Date: Mon, 7 Apr 2025 13:29:13 +0100 Subject: [PATCH 1530/1748] docs: update the default credential provider example (#2919) --- packages/entraid/README.md | 10 +++++----- 1 file changed, 5 insertions(+), 5 deletions(-) diff --git a/packages/entraid/README.md b/packages/entraid/README.md index 10ecfec9973..733cf895a7e 100644 --- a/packages/entraid/README.md +++ b/packages/entraid/README.md @@ -18,8 +18,8 @@ Secure token-based authentication for Redis clients using Microsoft Entra ID (fo ```bash -npm install "@redis/client@5.0.0-next.6" -npm install "@redis/entraid@5.0.0-next.6" +npm install "@redis/client@5.0.0-next.7" +npm install "@redis/entraid@5.0.0-next.7" ``` ## Getting Started @@ -92,11 +92,11 @@ The DefaultAzureCredential from @azure/identity provides a simplified authentica ```typescript import { createClient } from '@redis/client'; -import { DefaultAzureCredential } from '@azure/identity'; -import { EntraIdCredentialsProviderFactory, REDIS_SCOPE_DEFAULT } from '@redis/entraid/dist/lib/entra-id-credentials-provider-factory'; +import { getDefaultAzureCredential } from '@azure/identity'; +import { EntraIdCredentialsProviderFactory, REDIS_SCOPE_DEFAULT } from '@redis/entraid'; // Create a DefaultAzureCredential instance -const credential = new DefaultAzureCredential(); +const credential = getDefaultAzureCredential(); // Create a provider using DefaultAzureCredential const provider = EntraIdCredentialsProviderFactory.createForDefaultAzureCredential({ From 5295926cc0604ca7185566f05fa9fb28848253db Mon Sep 17 00:00:00 2001 From: "Bobby I." Date: Tue, 29 Apr 2025 10:56:38 +0300 Subject: [PATCH 1531/1748] bump test container to 8.0-RC2-pre (#2927) --- .github/workflows/tests.yml | 2 +- packages/search/lib/commands/INFO.spec.ts | 95 +---------------------- 2 files changed, 2 insertions(+), 95 deletions(-) diff --git a/.github/workflows/tests.yml b/.github/workflows/tests.yml index 366a24b4dc6..4ad7883a262 100644 --- a/.github/workflows/tests.yml +++ b/.github/workflows/tests.yml @@ -22,7 +22,7 @@ jobs: fail-fast: false matrix: node-version: [ '18', '20', '22' ] - redis-version: [ 'rs-7.2.0-v13', 'rs-7.4.0-v1', '8.0-M05-pre' ] + redis-version: [ 'rs-7.2.0-v13', 'rs-7.4.0-v1', '8.0-RC2-pre' ] steps: - uses: actions/checkout@v4 with: diff --git a/packages/search/lib/commands/INFO.spec.ts b/packages/search/lib/commands/INFO.spec.ts index caf311e07e9..b52e99ab9b0 100644 --- a/packages/search/lib/commands/INFO.spec.ts +++ b/packages/search/lib/commands/INFO.spec.ts @@ -18,100 +18,7 @@ describe('INFO', () => { field: SCHEMA_FIELD_TYPE.TEXT }); const ret = await client.ft.info('index'); - // effectively testing that stopwords_list is not in ret - assert.deepEqual( - ret, - { - index_name: 'index', - index_options: [], - index_definition: Object.create(null, { - - indexes_all: { - value: 'false', - configurable: true, - enumerable: true - }, - - default_score: { - value: '1', - configurable: true, - enumerable: true - }, - key_type: { - value: 'HASH', - configurable: true, - enumerable: true - }, - prefixes: { - value: [''], - configurable: true, - enumerable: true - } - }), - attributes: [Object.create(null, { - identifier: { - value: 'field', - configurable: true, - enumerable: true - }, - attribute: { - value: 'field', - configurable: true, - enumerable: true - }, - type: { - value: 'TEXT', - configurable: true, - enumerable: true - }, - WEIGHT: { - value: '1', - configurable: true, - enumerable: true - } - })], - num_docs: 0, - max_doc_id: 0, - num_terms: 0, - num_records: 0, - inverted_sz_mb: 0, - vector_index_sz_mb: 0, - total_inverted_index_blocks: 0, - offset_vectors_sz_mb: 0, - doc_table_size_mb: 0, - sortable_values_size_mb: 0, - key_table_size_mb: 0, - records_per_doc_avg: NaN, - bytes_per_record_avg: NaN, - cleaning: 0, - offsets_per_term_avg: NaN, - offset_bits_per_record_avg: NaN, - geoshapes_sz_mb: 0, - hash_indexing_failures: 0, - indexing: 0, - percent_indexed: 1, - number_of_uses: 1, - tag_overhead_sz_mb: 0, - text_overhead_sz_mb: 0, - total_index_memory_sz_mb: 0, - total_indexing_time: 0, - gc_stats: { - bytes_collected: 0, - total_ms_run: 0, - total_cycles: 0, - average_cycle_time_ms: NaN, - last_run_time_ms: 0, - gc_numeric_trees_missed: 0, - gc_blocks_denied: 0 - }, - cursor_stats: { - global_idle: 0, - global_total: 0, - index_capacity: 128, - index_total: 0 - }, - } - ); + assert.equal(ret.index_name, 'index'); }, GLOBAL.SERVERS.OPEN); From 048df302e40a9ced034b9a40d7dcfced511e15bb Mon Sep 17 00:00:00 2001 From: Nikolay Karadzhov Date: Wed, 30 Apr 2025 14:38:32 +0300 Subject: [PATCH 1532/1748] Fix imports (#2929) * fix: exports align exports with v4 as much as possible * document breaking changes * export type return SetOptions export --- docs/v4-to-v5.md | 9 +++++++++ packages/client/index.ts | 8 ++++---- packages/redis/index.ts | 10 +++++----- packages/search/lib/index.ts | 26 ++++++++++++++++++++------ packages/time-series/lib/index.ts | 1 + 5 files changed, 39 insertions(+), 15 deletions(-) diff --git a/docs/v4-to-v5.md b/docs/v4-to-v5.md index 57ad3f9bbf6..3b09658b66f 100644 --- a/docs/v4-to-v5.md +++ b/docs/v4-to-v5.md @@ -141,6 +141,8 @@ In older versions, if the socket disconnects during the pipeline execution, i.e. In v5, any unwritten commands (in the same pipeline) will be discarded. +- `RedisFlushModes` -> `REDIS_FLUSH_MODES` [^enum-to-constants] + ## Commands ### Redis @@ -221,6 +223,13 @@ In v5, any unwritten commands (in the same pipeline) will be discarded. - `FT.SUGDEL`: [^boolean-to-number] - `FT.CURSOR READ`: `cursor` type changed from `number` to `string` (in and out) to avoid issues when the number is bigger than `Number.MAX_SAFE_INTEGER`. See [here](https://github.com/redis/node-redis/issues/2561). +- `AggregateGroupByReducers` -> `FT_AGGREGATE_GROUP_BY_REDUCERS` [^enum-to-constants] +- `AggregateSteps` -> `FT_AGGREGATE_STEPS` [^enum-to-constants] +- `RedisSearchLanguages` -> `REDISEARCH_LANGUAGE` [^enum-to-constants] +- `SchemaFieldTypes` -> `SCHEMA_FIELD_TYPE` [^enum-to-constants] +- `SchemaTextFieldPhonetics` -> `SCHEMA_TEXT_FIELD_PHONETIC` [^enum-to-constants] +- `SearchOptions` -> `FtSearchOptions` +- `VectorAlgorithms` -> `SCHEMA_VECTOR_FIELD_ALGORITHM` [^enum-to-constants] ### Time Series diff --git a/packages/client/index.ts b/packages/client/index.ts index 56cdf703ca3..e426badf126 100644 --- a/packages/client/index.ts +++ b/packages/client/index.ts @@ -2,7 +2,7 @@ export { RedisModules, RedisFunctions, RedisScripts, RespVersions, TypeMapping/* export { RESP_TYPES } from './lib/RESP/decoder'; export { VerbatimString } from './lib/RESP/verbatim-string'; export { defineScript } from './lib/lua-script'; -// export * from './lib/errors'; +export * from './lib/errors'; import RedisClient, { RedisClientOptions, RedisClientType } from './lib/client'; export { RedisClientOptions, RedisClientType }; @@ -20,8 +20,8 @@ import RedisSentinel from './lib/sentinel'; export { RedisSentinelOptions, RedisSentinelType } from './lib/sentinel/types'; export const createSentinel = RedisSentinel.create; -// export { GeoReplyWith } from './lib/commands/generic-transformers'; +export { GEO_REPLY_WITH, GeoReplyWith } from './lib/commands/GEOSEARCH_WITH'; -// export { SetOptions } from './lib/commands/SET'; +export { SetOptions } from './lib/commands/SET'; -// export { RedisFlushModes } from './lib/commands/FLUSHALL'; +export { REDIS_FLUSH_MODES } from './lib/commands/FLUSHALL'; diff --git a/packages/redis/index.ts b/packages/redis/index.ts index 73477363d3b..61da052ea20 100644 --- a/packages/redis/index.ts +++ b/packages/redis/index.ts @@ -19,11 +19,11 @@ import RedisJSON from '@redis/json'; import RediSearch from '@redis/search'; import RedisTimeSeries from '@redis/time-series'; -// export * from '@redis/client'; -// export * from '@redis/bloom'; -// export * from '@redis/json'; -// export * from '@redis/search'; -// export * from '@redis/time-series'; +export * from '@redis/client'; +export * from '@redis/bloom'; +export * from '@redis/json'; +export * from '@redis/search'; +export * from '@redis/time-series'; const modules = { ...RedisBloomModules, diff --git a/packages/search/lib/index.ts b/packages/search/lib/index.ts index 34d57e8ae5e..9bcfb91b956 100644 --- a/packages/search/lib/index.ts +++ b/packages/search/lib/index.ts @@ -1,7 +1,21 @@ -export { default } from './commands'; +export { default } from './commands' -export { SCHEMA_FIELD_TYPE, SchemaFieldType } from './commands/CREATE'; - -// export { RediSearchSchema, RedisSearchLanguages, SchemaFieldTypes, SchemaTextFieldPhonetics, SearchReply, VectorAlgorithms } from './commands'; -// export { AggregateGroupByReducers, AggregateSteps } from './commands/AGGREGATE'; -// export { SearchOptions } from './commands/SEARCH'; +export { SearchReply } from './commands/SEARCH' +export { RediSearchSchema } from './commands/CREATE' +export { + REDISEARCH_LANGUAGE, + RediSearchLanguage, + SCHEMA_FIELD_TYPE, + SchemaFieldType, + SCHEMA_TEXT_FIELD_PHONETIC, + SchemaTextFieldPhonetic, + SCHEMA_VECTOR_FIELD_ALGORITHM, + SchemaVectorFieldAlgorithm +} from './commands/CREATE' +export { + FT_AGGREGATE_GROUP_BY_REDUCERS, + FtAggregateGroupByReducer, + FT_AGGREGATE_STEPS, + FtAggregateStep +} from './commands/AGGREGATE' +export { FtSearchOptions } from './commands/SEARCH' diff --git a/packages/time-series/lib/index.ts b/packages/time-series/lib/index.ts index bd0be1e9cea..52422bf1b5a 100644 --- a/packages/time-series/lib/index.ts +++ b/packages/time-series/lib/index.ts @@ -5,3 +5,4 @@ export { } from './commands'; export { TIME_SERIES_AGGREGATION_TYPE, TimeSeriesAggregationType } from './commands/CREATERULE'; export { TIME_SERIES_BUCKET_TIMESTAMP, TimeSeriesBucketTimestamp } from './commands/RANGE'; +export { TIME_SERIES_REDUCERS, TimeSeriesReducer } from './commands/MRANGE_GROUPBY'; From 10ff6debab44591f1104bc5e1500e79d64fac505 Mon Sep 17 00:00:00 2001 From: Hristo Temelski Date: Wed, 30 Apr 2025 15:56:29 +0300 Subject: [PATCH 1533/1748] fix(sentinel): Migrated to the new testing framework, fixed issues that were discovered during transition * [CAE-342] Fix a couple of bugs * Fixed issue with nodes masterauth persistency, changed docker container * [CAE-342] Fixed a couple of sentinel issues, enabled most tests * [CAE-342] Added comment * [CAE-342] Migrate majority of tests to testUtils * [CAE-342] Minor refactor * . * [CAE-342] Using cae containers for sentinel * [CAE-342] Improved resiliency of the legacy tests, added TSdoc comment * [CAE-342] Some extra logging, removed unneeded changes * [CAE-342] Moved docker env as optional part of redisserverdockerconfig * [CAE-342] Move password to serverArguments * [CAE-342] Moved ts-node to devDependencies * [CAE-342] Reverted legacy testing framework improvements --- package-lock.json | 203 +- package.json | 3 +- packages/client/lib/client/index.ts | 17 + packages/client/lib/sentinel/index.spec.ts | 2023 +++++++++----------- packages/client/lib/sentinel/index.ts | 16 +- packages/client/lib/sentinel/test-util.ts | 2 +- packages/client/lib/test-utils.ts | 83 +- packages/test-utils/lib/dockers.ts | 165 +- packages/test-utils/lib/index.ts | 124 +- 9 files changed, 1421 insertions(+), 1215 deletions(-) diff --git a/package-lock.json b/package-lock.json index 25a1dc9d51c..064b1158f50 100644 --- a/package-lock.json +++ b/package-lock.json @@ -8,6 +8,9 @@ "workspaces": [ "./packages/*" ], + "dependencies": { + "ts-node": "^10.9.2" + }, "devDependencies": { "@istanbuljs/nyc-config-typescript": "^1.0.2", "@types/mocha": "^10.0.6", @@ -664,6 +667,28 @@ "node": ">=6.9.0" } }, + "node_modules/@cspotcode/source-map-support": { + "version": "0.8.1", + "resolved": "https://registry.npmjs.org/@cspotcode/source-map-support/-/source-map-support-0.8.1.tgz", + "integrity": "sha512-IchNf6dN4tHoMFIn/7OE8LWZ19Y6q/67Bmf6vnGREv8RSbBVb9LPJxEcnwrcwX6ixSvaiGoomAUvu4YSxXrVgw==", + "license": "MIT", + "dependencies": { + "@jridgewell/trace-mapping": "0.3.9" + }, + "engines": { + "node": ">=12" + } + }, + "node_modules/@cspotcode/source-map-support/node_modules/@jridgewell/trace-mapping": { + "version": "0.3.9", + "resolved": "https://registry.npmjs.org/@jridgewell/trace-mapping/-/trace-mapping-0.3.9.tgz", + "integrity": "sha512-3Belt6tdc8bPgAtbcmdtNJlirVoTmEb5e2gC94PnkwEW9jI6CAHUeoG85tjWP5WquqfavoMtMwiG4P926ZKKuQ==", + "license": "MIT", + "dependencies": { + "@jridgewell/resolve-uri": "^3.0.3", + "@jridgewell/sourcemap-codec": "^1.4.10" + } + }, "node_modules/@esbuild/linux-x64": { "version": "0.19.12", "cpu": [ @@ -804,7 +829,6 @@ }, "node_modules/@jridgewell/resolve-uri": { "version": "3.1.1", - "dev": true, "license": "MIT", "engines": { "node": ">=6.0.0" @@ -820,7 +844,6 @@ }, "node_modules/@jridgewell/sourcemap-codec": { "version": "1.4.15", - "dev": true, "license": "MIT" }, "node_modules/@jridgewell/trace-mapping": { @@ -1067,10 +1090,6 @@ "resolved": "packages/entraid", "link": true }, - "node_modules/@redis/graph": { - "resolved": "packages/graph", - "link": true - }, "node_modules/@redis/json": { "resolved": "packages/json", "link": true @@ -1164,6 +1183,30 @@ "dev": true, "license": "MIT" }, + "node_modules/@tsconfig/node10": { + "version": "1.0.11", + "resolved": "https://registry.npmjs.org/@tsconfig/node10/-/node10-1.0.11.tgz", + "integrity": "sha512-DcRjDCujK/kCk/cUe8Xz8ZSpm8mS3mNNpta+jGCA6USEDfktlNvm1+IuZ9eTcDbNk41BHwpHHeW+N1lKCz4zOw==", + "license": "MIT" + }, + "node_modules/@tsconfig/node12": { + "version": "1.0.11", + "resolved": "https://registry.npmjs.org/@tsconfig/node12/-/node12-1.0.11.tgz", + "integrity": "sha512-cqefuRsh12pWyGsIoBKJA9luFu3mRxCA+ORZvA4ktLSzIuCUtWVxGIuXigEwO5/ywWFMZ2QEGKWvkZG1zDMTag==", + "license": "MIT" + }, + "node_modules/@tsconfig/node14": { + "version": "1.0.3", + "resolved": "https://registry.npmjs.org/@tsconfig/node14/-/node14-1.0.3.tgz", + "integrity": "sha512-ysT8mhdixWK6Hw3i1V2AeRqZ5WfXg1G43mqoYlM2nc6388Fq5jcXyr5mRsqViLx/GJYdoL0bfXD8nmF+Zn/Iow==", + "license": "MIT" + }, + "node_modules/@tsconfig/node16": { + "version": "1.0.4", + "resolved": "https://registry.npmjs.org/@tsconfig/node16/-/node16-1.0.4.tgz", + "integrity": "sha512-vxhUy4J8lyeyinH7Azl1pdd43GJhZH/tP2weN8TntQblOY+A0XbT8DJk1/oCPuOOyg/Ja757rG0CgHcWC8OfMA==", + "license": "MIT" + }, "node_modules/@types/body-parser": { "version": "1.19.5", "resolved": "https://registry.npmjs.org/@types/body-parser/-/body-parser-1.19.5.tgz", @@ -1247,7 +1290,6 @@ }, "node_modules/@types/node": { "version": "20.11.16", - "dev": true, "license": "MIT", "dependencies": { "undici-types": "~5.26.4" @@ -1330,6 +1372,30 @@ "node": ">= 0.6" } }, + "node_modules/acorn": { + "version": "8.14.1", + "resolved": "https://registry.npmjs.org/acorn/-/acorn-8.14.1.tgz", + "integrity": "sha512-OvQ/2pUDKmgfCg++xsTX1wGxfTaszcHVcTctW4UJB4hibJx2HXxxO5UmVgyjMa+ZDsiaf5wWLXYpRWMmBI0QHg==", + "license": "MIT", + "bin": { + "acorn": "bin/acorn" + }, + "engines": { + "node": ">=0.4.0" + } + }, + "node_modules/acorn-walk": { + "version": "8.3.4", + "resolved": "https://registry.npmjs.org/acorn-walk/-/acorn-walk-8.3.4.tgz", + "integrity": "sha512-ueEepnujpqee2o5aIYnvHU6C0A42MNdsIDeqy5BydrkuC5R1ZuUFnm27EeFJGoEHJQgn3uleRvmTXaJgfXbt4g==", + "license": "MIT", + "dependencies": { + "acorn": "^8.11.0" + }, + "engines": { + "node": ">=0.4.0" + } + }, "node_modules/agent-base": { "version": "7.1.0", "license": "MIT", @@ -1448,6 +1514,12 @@ "dev": true, "license": "MIT" }, + "node_modules/arg": { + "version": "4.1.3", + "resolved": "https://registry.npmjs.org/arg/-/arg-4.1.3.tgz", + "integrity": "sha512-58S9QDqG0Xx27YwPSt9fJxivjYl432YCwfDMfZ+71RAqUrZef7LrKQZ3LHLOwCS4FLNBplP533Zx895SeOCHvA==", + "license": "MIT" + }, "node_modules/argparse": { "version": "2.0.1", "dev": true, @@ -2315,6 +2387,12 @@ } } }, + "node_modules/create-require": { + "version": "1.1.1", + "resolved": "https://registry.npmjs.org/create-require/-/create-require-1.1.1.tgz", + "integrity": "sha512-dcKFX3jn0MpIaXjisoRvexIJVEKzaq7z2rZKxf+MSr9TkdmHmsU4m2lcLojrj/FHl8mk5VxMmYA+ftRkP/3oKQ==", + "license": "MIT" + }, "node_modules/cross-spawn": { "version": "7.0.3", "dev": true, @@ -5088,6 +5166,12 @@ "url": "https://github.com/sponsors/sindresorhus" } }, + "node_modules/make-error": { + "version": "1.3.6", + "resolved": "https://registry.npmjs.org/make-error/-/make-error-1.3.6.tgz", + "integrity": "sha512-s8UhlNe7vPKomQhC1qFelMokr/Sc3AgNbso3n74mVPA5LTZwkB9NlXf4XPamLxJE8h0gh73rM94xvwRT2CVInw==", + "license": "ISC" + }, "node_modules/marked": { "version": "4.3.0", "dev": true, @@ -7576,6 +7660,58 @@ "node": ">=0.8.0" } }, + "node_modules/ts-node": { + "version": "10.9.2", + "resolved": "https://registry.npmjs.org/ts-node/-/ts-node-10.9.2.tgz", + "integrity": "sha512-f0FFpIdcHgn8zcPSbf1dRevwt047YMnaiJM3u2w2RewrB+fob/zePZcrOyQoLMMO7aBIddLcQIEK5dYjkLnGrQ==", + "license": "MIT", + "dependencies": { + "@cspotcode/source-map-support": "^0.8.0", + "@tsconfig/node10": "^1.0.7", + "@tsconfig/node12": "^1.0.7", + "@tsconfig/node14": "^1.0.0", + "@tsconfig/node16": "^1.0.2", + "acorn": "^8.4.1", + "acorn-walk": "^8.1.1", + "arg": "^4.1.0", + "create-require": "^1.1.0", + "diff": "^4.0.1", + "make-error": "^1.1.1", + "v8-compile-cache-lib": "^3.0.1", + "yn": "3.1.1" + }, + "bin": { + "ts-node": "dist/bin.js", + "ts-node-cwd": "dist/bin-cwd.js", + "ts-node-esm": "dist/bin-esm.js", + "ts-node-script": "dist/bin-script.js", + "ts-node-transpile-only": "dist/bin-transpile.js", + "ts-script": "dist/bin-script-deprecated.js" + }, + "peerDependencies": { + "@swc/core": ">=1.2.50", + "@swc/wasm": ">=1.2.50", + "@types/node": "*", + "typescript": ">=2.7" + }, + "peerDependenciesMeta": { + "@swc/core": { + "optional": true + }, + "@swc/wasm": { + "optional": true + } + } + }, + "node_modules/ts-node/node_modules/diff": { + "version": "4.0.2", + "resolved": "https://registry.npmjs.org/diff/-/diff-4.0.2.tgz", + "integrity": "sha512-58lmxKSA4BNyLz+HHMUzlOEpg09FV+ev6ZMe3vJihgdxzgcwZ8VoEEPmALCZG9LmqfVoNMMKpttIYTVG6uDY7A==", + "license": "BSD-3-Clause", + "engines": { + "node": ">=0.3.1" + } + }, "node_modules/tslib": { "version": "2.6.2", "license": "0BSD" @@ -7741,7 +7877,6 @@ }, "node_modules/typescript": { "version": "5.3.3", - "dev": true, "license": "Apache-2.0", "bin": { "tsc": "bin/tsc", @@ -7780,7 +7915,6 @@ }, "node_modules/undici-types": { "version": "5.26.5", - "dev": true, "license": "MIT" }, "node_modules/unicorn-magic": { @@ -7956,6 +8090,12 @@ "uuid": "dist/bin/uuid" } }, + "node_modules/v8-compile-cache-lib": { + "version": "3.0.1", + "resolved": "https://registry.npmjs.org/v8-compile-cache-lib/-/v8-compile-cache-lib-3.0.1.tgz", + "integrity": "sha512-wa7YjyUGfNZngI/vtK0UHAN+lgDCxBPCylVXGp0zu59Fz5aiGtNXaq3DhIov063MorB+VfufLh3JlF2KdTK3xg==", + "license": "MIT" + }, "node_modules/vary": { "version": "1.1.2", "resolved": "https://registry.npmjs.org/vary/-/vary-1.1.2.tgz", @@ -8324,6 +8464,15 @@ "url": "https://github.com/sponsors/sindresorhus" } }, + "node_modules/yn": { + "version": "3.1.1", + "resolved": "https://registry.npmjs.org/yn/-/yn-3.1.1.tgz", + "integrity": "sha512-Ux4ygGWsu2c7isFWe8Yu1YluJmqVhxqK2cLXNQA5AcC3QfbGNpM7fu0Y8b/z16pXLnFxZYvWhd3fhBY9DLmC6Q==", + "license": "MIT", + "engines": { + "node": ">=6" + } + }, "node_modules/yocto-queue": { "version": "0.1.0", "dev": true, @@ -8353,7 +8502,7 @@ }, "packages/bloom": { "name": "@redis/bloom", - "version": "5.0.0-next.6", + "version": "5.0.0-next.7", "license": "MIT", "devDependencies": { "@redis/test-utils": "*" @@ -8362,12 +8511,12 @@ "node": ">= 18" }, "peerDependencies": { - "@redis/client": "^5.0.0-next.6" + "@redis/client": "^5.0.0-next.7" } }, "packages/client": { "name": "@redis/client", - "version": "5.0.0-next.6", + "version": "5.0.0-next.7", "license": "MIT", "dependencies": { "cluster-key-slot": "1.1.2" @@ -8383,7 +8532,7 @@ }, "packages/entraid": { "name": "@redis/entraid", - "version": "5.0.0-next.6", + "version": "5.0.0-next.7", "license": "MIT", "dependencies": { "@azure/identity": "4.7.0", @@ -8402,7 +8551,7 @@ "node": ">= 18" }, "peerDependencies": { - "@redis/client": "^5.0.0-next.6" + "@redis/client": "^5.0.0-next.7" } }, "packages/entraid/node_modules/@types/node": { @@ -8425,6 +8574,7 @@ "packages/graph": { "name": "@redis/graph", "version": "5.0.0-next.6", + "extraneous": true, "license": "MIT", "devDependencies": { "@redis/test-utils": "*" @@ -8438,7 +8588,7 @@ }, "packages/json": { "name": "@redis/json", - "version": "5.0.0-next.6", + "version": "5.0.0-next.7", "license": "MIT", "devDependencies": { "@redis/test-utils": "*" @@ -8447,19 +8597,18 @@ "node": ">= 18" }, "peerDependencies": { - "@redis/client": "^5.0.0-next.6" + "@redis/client": "^5.0.0-next.7" } }, "packages/redis": { - "version": "5.0.0-next.6", + "version": "5.0.0-next.7", "license": "MIT", "dependencies": { - "@redis/bloom": "5.0.0-next.6", - "@redis/client": "5.0.0-next.6", - "@redis/graph": "5.0.0-next.6", - "@redis/json": "5.0.0-next.6", - "@redis/search": "5.0.0-next.6", - "@redis/time-series": "5.0.0-next.6" + "@redis/bloom": "5.0.0-next.7", + "@redis/client": "5.0.0-next.7", + "@redis/json": "5.0.0-next.7", + "@redis/search": "5.0.0-next.7", + "@redis/time-series": "5.0.0-next.7" }, "engines": { "node": ">= 18" @@ -8467,7 +8616,7 @@ }, "packages/search": { "name": "@redis/search", - "version": "5.0.0-next.6", + "version": "5.0.0-next.7", "license": "MIT", "devDependencies": { "@redis/test-utils": "*" @@ -8476,7 +8625,7 @@ "node": ">= 18" }, "peerDependencies": { - "@redis/client": "^5.0.0-next.6" + "@redis/client": "^5.0.0-next.7" } }, "packages/test-utils": { @@ -8545,7 +8694,7 @@ }, "packages/time-series": { "name": "@redis/time-series", - "version": "5.0.0-next.6", + "version": "5.0.0-next.7", "license": "MIT", "devDependencies": { "@redis/test-utils": "*" @@ -8554,7 +8703,7 @@ "node": ">= 18" }, "peerDependencies": { - "@redis/client": "^5.0.0-next.6" + "@redis/client": "^5.0.0-next.7" } } } diff --git a/package.json b/package.json index 0a29c71f831..2ff2d4825ae 100644 --- a/package.json +++ b/package.json @@ -21,6 +21,7 @@ "release-it": "^17.0.3", "tsx": "^4.7.0", "typedoc": "^0.25.7", - "typescript": "^5.3.3" + "typescript": "^5.3.3", + "ts-node": "^10.9.2" } } diff --git a/packages/client/lib/client/index.ts b/packages/client/lib/client/index.ts index 5dae1271ecb..f48e03d0c19 100644 --- a/packages/client/lib/client/index.ts +++ b/packages/client/lib/client/index.ts @@ -299,6 +299,9 @@ export default class RedisClient< #monitorCallback?: MonitorCallback; private _self = this; private _commandOptions?: CommandOptions; + // flag used to annotate that the client + // was in a watch transaction when + // a topology change occured #dirtyWatch?: string; #epoch: number; #watchEpoch?: number; @@ -325,6 +328,20 @@ export default class RedisClient< return this._self.#watchEpoch !== undefined; } + /** + * Indicates whether the client's WATCH command has been invalidated by a topology change. + * When this returns true, any transaction using WATCH will fail with a WatchError. + * @returns true if the watched keys have been modified, false otherwise + */ + get isDirtyWatch(): boolean { + return this._self.#dirtyWatch !== undefined + } + + /** + * Marks the client's WATCH command as invalidated due to a topology change. + * This will cause any subsequent EXEC in a transaction to fail with a WatchError. + * @param msg - The error message explaining why the WATCH is dirty + */ setDirtyWatch(msg: string) { this._self.#dirtyWatch = msg; } diff --git a/packages/client/lib/sentinel/index.spec.ts b/packages/client/lib/sentinel/index.spec.ts index be5522bdd8d..567da4b1a50 100644 --- a/packages/client/lib/sentinel/index.spec.ts +++ b/packages/client/lib/sentinel/index.spec.ts @@ -1,29 +1,339 @@ import { strict as assert } from 'node:assert'; import { setTimeout } from 'node:timers/promises'; +import testUtils, { GLOBAL, MATH_FUNCTION } from '../test-utils'; +import { RESP_TYPES } from '../RESP/decoder'; import { WatchError } from "../errors"; import { RedisSentinelConfig, SentinelFramework } from "./test-util"; -import { RedisNode, RedisSentinelClientType, RedisSentinelEvent, RedisSentinelType } from "./types"; -import { RedisSentinelFactory } from '.'; -import { RedisClientType } from '../client'; +import { RedisSentinelEvent, RedisSentinelType, RedisSentinelClientType, RedisNode } from "./types"; import { RedisModules, RedisFunctions, RedisScripts, RespVersions, TypeMapping, NumberReply } from '../RESP/types'; - import { promisify } from 'node:util'; import { exec } from 'node:child_process'; -import { RESP_TYPES } from '../RESP/decoder'; -import { defineScript } from '../lua-script'; -import { MATH_FUNCTION } from '../commands/FUNCTION_LOAD.spec'; -import RedisBloomModules from '@redis/bloom'; -import { RedisTcpSocketOptions } from '../client/socket'; -import { SQUARE_SCRIPT } from '../client/index.spec'; - const execAsync = promisify(exec); -/* used to ensure test environment resets to normal state - i.e. - - all redis nodes are active and are part of the topology - before allowing things to continue. -*/ +[GLOBAL.SENTINEL.OPEN, GLOBAL.SENTINEL.PASSWORD].forEach(testOptions => { + const passIndex = testOptions.serverArguments.indexOf('--requirepass')+1; + let password: string | undefined = undefined; + if (passIndex != 0) { + password = testOptions.serverArguments[passIndex]; + } + + describe(`test with password - ${password}`, () => { + testUtils.testWithClientSentinel('client should be authenticated', async sentinel => { + await assert.doesNotReject(sentinel.set('x', 1)); + }, testOptions); + + testUtils.testWithClientSentinel('try to connect multiple times', async sentinel => { + await assert.rejects(sentinel.connect()); + }, testOptions); + + + testUtils.testWithClientSentinel('should respect type mapping', async sentinel => { + const typeMapped = sentinel.withTypeMapping({ + [RESP_TYPES.SIMPLE_STRING]: Buffer + }); + + const resp = await typeMapped.ping(); + assert.deepEqual(resp, Buffer.from('PONG')); + }, testOptions); + + testUtils.testWithClientSentinel('many readers', async sentinel => { + await sentinel.set("x", 1); + for (let i = 0; i < 10; i++) { + if (await sentinel.get("x") == "1") { + break; + } + await setTimeout(1000); + } + + const promises: Array> = []; + for (let i = 0; i < 500; i++) { + promises.push(sentinel.get("x")); + } + + const resp = await Promise.all(promises); + assert.equal(resp.length, 500); + for (let i = 0; i < 500; i++) { + assert.equal(resp[i], "1", `failed on match at ${i}`); + } + }, testOptions); + + testUtils.testWithClientSentinel('use', async sentinel => { + await sentinel.use( + async (client: any ) => { + await assert.doesNotReject(client.get('x')); + } + ); + }, testOptions); + + testUtils.testWithClientSentinel('watch does not carry over leases', async sentinel => { + assert.equal(await sentinel.use(client => client.watch("x")), 'OK') + assert.equal(await sentinel.use(client => client.set('x', 1)), 'OK'); + assert.deepEqual(await sentinel.use(client => client.multi().get('x').exec()), ['1']); + }, testOptions); + + testUtils.testWithClientSentinel('plain pubsub - channel', async sentinel => { + let pubSubResolve; + const pubSubPromise = new Promise((res) => { + pubSubResolve = res; + }); + + let tester = false; + await sentinel.subscribe('test', () => { + tester = true; + pubSubResolve(1); + }) + + await sentinel.publish('test', 'hello world'); + await pubSubPromise; + assert.equal(tester, true); + + // now unsubscribe + tester = false; + await sentinel.unsubscribe('test') + await sentinel.publish('test', 'hello world'); + await setTimeout(1000); + + assert.equal(tester, false); + }, testOptions); + + testUtils.testWithClientSentinel('plain pubsub - pattern', async sentinel => { + let pubSubResolve; + const pubSubPromise = new Promise((res) => { + pubSubResolve = res; + }); + + let tester = false; + await sentinel.pSubscribe('test*', () => { + tester = true; + pubSubResolve(1); + }) + + await sentinel.publish('testy', 'hello world'); + await pubSubPromise; + assert.equal(tester, true); + + // now unsubscribe + tester = false; + await sentinel.pUnsubscribe('test*'); + await sentinel.publish('testy', 'hello world'); + await setTimeout(1000); + + assert.equal(tester, false); + }, testOptions) + }); +}); + +describe(`test with scripts`, () => { + testUtils.testWithClientSentinel('with script', async sentinel => { + const [, reply] = await Promise.all([ + sentinel.set('key', '2'), + sentinel.square('key') + ]); + + assert.equal(reply, 4); + }, GLOBAL.SENTINEL.WITH_SCRIPT); + + testUtils.testWithClientSentinel('with script multi', async sentinel => { + const reply = await sentinel.multi().set('key', 2).square('key').exec(); + assert.deepEqual(reply, ['OK', 4]); + }, GLOBAL.SENTINEL.WITH_SCRIPT); + + testUtils.testWithClientSentinel('use with script', async sentinel => { + const reply = await sentinel.use( + async (client: any) => { + assert.equal(await client.set('key', '2'), 'OK'); + assert.equal(await client.get('key'), '2'); + return client.square('key') + } + ); + }, GLOBAL.SENTINEL.WITH_SCRIPT) +}); + + +describe(`test with functions`, () => { + testUtils.testWithClientSentinel('with function', async sentinel => { + await sentinel.functionLoad( + MATH_FUNCTION.code, + { REPLACE: true } + ); + + await sentinel.set('key', '2'); + const resp = await sentinel.math.square('key'); + + assert.equal(resp, 4); + }, GLOBAL.SENTINEL.WITH_FUNCTION); + + testUtils.testWithClientSentinel('with function multi', async sentinel => { + await sentinel.functionLoad( + MATH_FUNCTION.code, + { REPLACE: true } + ); + + const reply = await sentinel.multi().set('key', 2).math.square('key').exec(); + assert.deepEqual(reply, ['OK', 4]); + }, GLOBAL.SENTINEL.WITH_FUNCTION); + + testUtils.testWithClientSentinel('use with function', async sentinel => { + await sentinel.functionLoad( + MATH_FUNCTION.code, + { REPLACE: true } + ); + + const reply = await sentinel.use( + async (client: any) => { + await client.set('key', '2'); + return client.math.square('key'); + } + ); + + assert.equal(reply, 4); + }, GLOBAL.SENTINEL.WITH_FUNCTION); +}); + +describe(`test with modules`, () => { + testUtils.testWithClientSentinel('with module', async sentinel => { + const resp = await sentinel.bf.add('key', 'item') + assert.equal(resp, true); + }, GLOBAL.SENTINEL.WITH_MODULE); + + testUtils.testWithClientSentinel('with module multi', async sentinel => { + const resp = await sentinel.multi().bf.add('key', 'item').exec(); + assert.deepEqual(resp, [true]); + }, GLOBAL.SENTINEL.WITH_MODULE); + + testUtils.testWithClientSentinel('use with module', async sentinel => { + const reply = await sentinel.use( + async (client: any) => { + return client.bf.add('key', 'item'); + } + ); + + assert.equal(reply, true); + }, GLOBAL.SENTINEL.WITH_MODULE); +}); + +describe(`test with replica pool size 1`, () => { + testUtils.testWithClientSentinel('client lease', async sentinel => { + sentinel.on("error", () => { }); + + const clientLease = await sentinel.aquire(); + clientLease.set('x', 456); + + let matched = false; + /* waits for replication */ + for (let i = 0; i < 15; i++) { + try { + assert.equal(await sentinel.get("x"), '456'); + matched = true; + break; + } catch (err) { + await setTimeout(1000); + } + } + + clientLease.release(); + + assert.equal(matched, true); + }, GLOBAL.SENTINEL.WITH_REPLICA_POOL_SIZE_1); + + testUtils.testWithClientSentinel('block on pool', async sentinel => { + const promise = sentinel.use( + async client => { + await setTimeout(1000); + return await client.get("x"); + } + ) + + await sentinel.set("x", 1); + assert.equal(await promise, null); + }, GLOBAL.SENTINEL.WITH_REPLICA_POOL_SIZE_1); + + testUtils.testWithClientSentinel('pipeline', async sentinel => { + const resp = await sentinel.multi().set('x', 1).get('x').execAsPipeline(); + assert.deepEqual(resp, ['OK', '1']); + }, GLOBAL.SENTINEL.WITH_REPLICA_POOL_SIZE_1); +}); + +describe(`test with masterPoolSize 2, reserve client true`, () => { + // TODO: flaky test, sometimes fails with `promise1 === null` + testUtils.testWithClientSentinel('reserve client, takes a client out of pool', async sentinel => { + const promise1 = sentinel.use( + async client => { + const val = await client.get("x"); + await client.set("x", 2); + return val; + } + ) + + const promise2 = sentinel.use( + async client => { + return client.get("x"); + } + ) + + await sentinel.set("x", 1); + assert.equal(await promise1, "1"); + assert.equal(await promise2, "2"); + }, Object.assign(GLOBAL.SENTINEL.WITH_RESERVE_CLIENT_MASTER_POOL_SIZE_2, {skipTest: true})); +}); + +describe(`test with masterPoolSize 2`, () => { + testUtils.testWithClientSentinel('multple clients', async sentinel => { + sentinel.on("error", () => { }); + + const promise = sentinel.use( + async client => { + await sentinel!.set("x", 1); + await client.get("x"); + } + ) + + await assert.doesNotReject(promise); + }, GLOBAL.SENTINEL.WITH_MASTER_POOL_SIZE_2); + + testUtils.testWithClientSentinel('use - watch - clean', async sentinel => { + let promise = sentinel.use(async (client) => { + await client.set("x", 1); + await client.watch("x"); + return client.multi().get("x").exec(); + }); + + assert.deepEqual(await promise, ['1']); + }, GLOBAL.SENTINEL.WITH_MASTER_POOL_SIZE_2); + + testUtils.testWithClientSentinel('use - watch - dirty', async sentinel => { + let promise = sentinel.use(async (client) => { + await client.set('x', 1); + await client.watch('x'); + await sentinel!.set('x', 2); + return client.multi().get('x').exec(); + }); + + await assert.rejects(promise, new WatchError()); + }, GLOBAL.SENTINEL.WITH_MASTER_POOL_SIZE_2); + + testUtils.testWithClientSentinel('lease - watch - clean', async sentinel => { + const leasedClient = await sentinel.aquire(); + await leasedClient.set('x', 1); + await leasedClient.watch('x'); + assert.deepEqual(await leasedClient.multi().get('x').exec(), ['1']) + }, GLOBAL.SENTINEL.WITH_MASTER_POOL_SIZE_2); + + testUtils.testWithClientSentinel('lease - watch - dirty', async sentinel => { + const leasedClient = await sentinel.aquire(); + await leasedClient.set('x', 1); + await leasedClient.watch('x'); + await leasedClient.set('x', 2); + + await assert.rejects(leasedClient.multi().get('x').exec(), new WatchError()); + }, GLOBAL.SENTINEL.WITH_MASTER_POOL_SIZE_2); +}); + +// TODO: Figure out how to modify the test utils +// so it would have fine grained controll over +// sentinel +// it should somehow replicate the `SentinelFramework` object functionallities async function steadyState(frame: SentinelFramework) { let checkedMaster = false; let checkedReplicas = false; @@ -34,7 +344,6 @@ async function steadyState(frame: SentinelFramework) { checkedMaster = true; } } - if (!checkedReplicas) { const replicas = (await frame.sentinelReplicas()); checkedReplicas = true; @@ -43,17 +352,14 @@ async function steadyState(frame: SentinelFramework) { } } } - let nodeResolve, nodeReject; const nodePromise = new Promise((res, rej) => { nodeResolve = res; nodeReject = rej; }) - const seenNodes = new Set(); let sentinel: RedisSentinelType | undefined; const tracer = []; - try { sentinel = frame.getSentinelClient({ replicaPoolSize: 1, scanInterval: 2000 }, false) .on('topology-change', (event: RedisSentinelEvent) => { @@ -66,7 +372,6 @@ async function steadyState(frame: SentinelFramework) { }).on('error', err => { }); sentinel.setTracer(tracer); await sentinel.connect(); - await nodePromise; await sentinel.flushAll(); @@ -77,1189 +382,593 @@ async function steadyState(frame: SentinelFramework) { } } -["redis-sentinel-test-password", undefined].forEach(function (password) { - describe.skip(`Sentinel - password = ${password}`, () => { - const config: RedisSentinelConfig = { sentinelName: "test", numberOfNodes: 3, password: password }; - const frame = new SentinelFramework(config); - let tracer = new Array(); - let stopMeasuringBlocking = false; - let longestDelta = 0; - let longestTestDelta = 0; - let last: number; - - before(async function () { - this.timeout(15000); - - last = Date.now(); - - function deltaMeasurer() { - const delta = Date.now() - last; - if (delta > longestDelta) { - longestDelta = delta; - } - if (delta > longestTestDelta) { - longestTestDelta = delta; +describe.skip('legacy tests', () => { + const config: RedisSentinelConfig = { sentinelName: "test", numberOfNodes: 3, password: undefined }; + const frame = new SentinelFramework(config); + let tracer = new Array(); + let stopMeasuringBlocking = false; + let longestDelta = 0; + let longestTestDelta = 0; + let last: number; + + before(async function () { + this.timeout(15000); + + last = Date.now(); + + function deltaMeasurer() { + const delta = Date.now() - last; + if (delta > longestDelta) { + longestDelta = delta; + } + if (delta > longestTestDelta) { + longestTestDelta = delta; + } + if (!stopMeasuringBlocking) { + last = Date.now(); + setImmediate(deltaMeasurer); + } + } + setImmediate(deltaMeasurer); + await frame.spawnRedisSentinel(); + }); + + after(async function () { + this.timeout(15000); + + stopMeasuringBlocking = true; + + await frame.cleanup(); + }) + + describe('Sentinel Client', function () { + let sentinel: RedisSentinelType | undefined; + + beforeEach(async function () { + this.timeout(0); + + await frame.getAllRunning(); + await steadyState(frame); + longestTestDelta = 0; + }) + + afterEach(async function () { + this.timeout(60000); + // avoid errors in afterEach that end testing + if (sentinel !== undefined) { + sentinel.on('error', () => { }); + } + + if (this!.currentTest!.state === 'failed') { + console.log(`longest event loop blocked delta: ${longestDelta}`); + console.log(`longest event loop blocked in failing test: ${longestTestDelta}`); + console.log("trace:"); + for (const line of tracer) { + console.log(line); } - if (!stopMeasuringBlocking) { - last = Date.now(); - setImmediate(deltaMeasurer); + console.log(`sentinel object state:`) + console.log(`master: ${JSON.stringify(sentinel?.getMasterNode())}`) + console.log(`replicas: ${JSON.stringify(sentinel?.getReplicaNodes().entries)}`) + const results = await Promise.all([ + frame.sentinelSentinels(), + frame.sentinelMaster(), + frame.sentinelReplicas() + ]) + + console.log(`sentinel sentinels:\n${JSON.stringify(results[0], undefined, '\t')}`); + console.log(`sentinel master:\n${JSON.stringify(results[1], undefined, '\t')}`); + console.log(`sentinel replicas:\n${JSON.stringify(results[2], undefined, '\t')}`); + const { stdout, stderr } = await execAsync("docker ps -a"); + console.log(`docker stdout:\n${stdout}`); + const ids = frame.getAllDockerIds(); + console.log("docker logs"); + for (const [id, port] of ids) { + console.log(`${id}/${port}\n`); + const { stdout, stderr } = await execAsync(`docker logs ${id}`, {maxBuffer: 8192 * 8192 * 4}); + console.log(stdout); } } + tracer.length = 0; + + if (sentinel !== undefined) { + await sentinel.destroy(); + sentinel = undefined; + } + }) + + it('use', async function () { + this.timeout(60000); - setImmediate(deltaMeasurer); + sentinel = frame.getSentinelClient({ replicaPoolSize: 1 }); + sentinel.on("error", () => { }); + await sentinel.connect(); - await frame.spawnRedisSentinel(); + await sentinel.use( + async (client: RedisSentinelClientType, ) => { + const masterNode = sentinel!.getMasterNode(); + await frame.stopNode(masterNode!.port.toString()); + await assert.doesNotReject(client.get('x')); + } + ); }); - - after(async function () { - this.timeout(15000); - - stopMeasuringBlocking = true; - - await frame.cleanup(); - }) - - describe('Sentinel Client', function () { - let sentinel: RedisSentinelType< RedisModules, RedisFunctions, RedisScripts, RespVersions, TypeMapping> | undefined; - - beforeEach(async function () { - this.timeout(0); - - await frame.getAllRunning(); - await steadyState(frame); - longestTestDelta = 0; + // stops master to force sentinel to update + it('stop master', async function () { + this.timeout(60000); + + sentinel = frame.getSentinelClient(); + sentinel.setTracer(tracer); + sentinel.on("error", () => { }); + await sentinel.connect(); + + tracer.push(`connected`); + + let masterChangeResolve; + const masterChangePromise = new Promise((res) => { + masterChangeResolve = res; }) - - afterEach(async function () { - this.timeout(30000); - // avoid errors in afterEach that end testing - if (sentinel !== undefined) { - sentinel.on('error', () => { }); - } - - if (this!.currentTest!.state === 'failed') { - console.log(`longest event loop blocked delta: ${longestDelta}`); - console.log(`longest event loop blocked in failing test: ${longestTestDelta}`); - console.log("trace:"); - for (const line of tracer) { - console.log(line); - } - console.log(`sentinel object state:`) - console.log(`master: ${JSON.stringify(sentinel?.getMasterNode())}`) - console.log(`replicas: ${JSON.stringify(sentinel?.getReplicaNodes().entries)}`) - const results = await Promise.all([ - frame.sentinelSentinels(), - frame.sentinelMaster(), - frame.sentinelReplicas() - ]) - console.log(`sentinel sentinels:\n${JSON.stringify(results[0], undefined, '\t')}`); - console.log(`sentinel master:\n${JSON.stringify(results[1], undefined, '\t')}`); - console.log(`sentinel replicas:\n${JSON.stringify(results[2], undefined, '\t')}`); - const { stdout, stderr } = await execAsync("docker ps -a"); - console.log(`docker stdout:\n${stdout}`); - - const ids = frame.getAllDockerIds(); - console.log("docker logs"); - - for (const [id, port] of ids) { - console.log(`${id}/${port}\n`); - const { stdout, stderr } = await execAsync(`docker logs ${id}`, {maxBuffer: 8192 * 8192 * 4}); - console.log(stdout); - } - } - tracer.length = 0; - - if (sentinel !== undefined) { - await sentinel.destroy(); - sentinel = undefined; + const masterNode = await sentinel.getMasterNode(); + sentinel.on('topology-change', (event: RedisSentinelEvent) => { + tracer.push(`got topology-change event: ${JSON.stringify(event)}`); + if (event.type === "MASTER_CHANGE" && event.node.port != masterNode!.port) { + tracer.push(`got expected master change event`); + masterChangeResolve(event.node); } + }); + + tracer.push(`stopping master node`); + await frame.stopNode(masterNode!.port.toString()); + tracer.push(`stopped master node`); + + tracer.push(`waiting on master change promise`); + const newMaster = await masterChangePromise as RedisNode; + tracer.push(`got new master node of ${newMaster.port}`); + assert.notEqual(masterNode!.port, newMaster.port); + }); + + // if master changes, client should make sure user knows watches are invalid + it('watch across master change', async function () { + this.timeout(60000); + + sentinel = frame.getSentinelClient({ masterPoolSize: 2 }); + sentinel.setTracer(tracer); + sentinel.on("error", () => { }); + await sentinel.connect(); + + tracer.push("connected"); + + const client = await sentinel.aquire(); + tracer.push("aquired lease"); + + await client.set("x", 1); + await client.watch("x"); + + tracer.push("did a watch on lease"); + + let resolve; + const promise = new Promise((res) => { + resolve = res; }) - - it('basic bootstrap', async function () { - sentinel = frame.getSentinelClient(); - await sentinel.connect(); - await assert.doesNotReject(sentinel.set('x', 1)); + const masterNode = sentinel.getMasterNode(); + tracer.push(`got masterPort as ${masterNode!.port}`); - }); - - it('basic teardown worked', async function () { - const nodePorts = frame.getAllNodesPort(); - const sentinelPorts = frame.getAllSentinelsPort(); - - assert.notEqual(nodePorts.length, 0); - assert.notEqual(sentinelPorts.length, 0); - - sentinel = frame.getSentinelClient(); - await sentinel.connect(); - - await assert.doesNotReject(sentinel.get('x')); - }); - - it('try to connect multiple times', async function () { - sentinel = frame.getSentinelClient(); - const connectPromise = sentinel.connect(); - await assert.rejects(sentinel.connect()); - await connectPromise; - }); - - it('with type mapping', async function () { - const commandOptions = { - typeMapping: { - [RESP_TYPES.SIMPLE_STRING]: Buffer - } + sentinel.on('topology-change', (event: RedisSentinelEvent) => { + tracer.push(`got topology-change event: ${JSON.stringify(event)}`); + if (event.type === "MASTER_CHANGE" && event.node.port != masterNode!.port) { + tracer.push("resolving promise"); + resolve(event.node); } - sentinel = frame.getSentinelClient({ commandOptions: commandOptions }); - await sentinel.connect(); - - const resp = await sentinel.ping(); - assert.deepEqual(resp, Buffer.from('PONG')) + }); + + tracer.push("stopping master node"); + await frame.stopNode(masterNode!.port.toString()); + tracer.push("stopped master node and waiting on promise"); + + const newMaster = await promise as RedisNode; + tracer.push(`promise returned, newMaster = ${JSON.stringify(newMaster)}`); + assert.notEqual(masterNode!.port, newMaster.port); + tracer.push(`newMaster does not equal old master`); + + tracer.push(`waiting to assert that a multi/exec now fails`); + await assert.rejects(async () => { await client.multi().get("x").exec() }, new Error("sentinel config changed in middle of a WATCH Transaction")); + tracer.push(`asserted that a multi/exec now fails`); + }); + + // same as above, but set a watch before and after master change, shouldn't change the fact that watches are invalid + it('watch before and after master change', async function () { + this.timeout(60000); + + sentinel = frame.getSentinelClient({ masterPoolSize: 2 }); + sentinel.setTracer(tracer); + sentinel.on("error", () => { }); + await sentinel.connect(); + tracer.push("connected"); + + const client = await sentinel.aquire(); + tracer.push("got leased client"); + await client.set("x", 1); + await client.watch("x"); + + tracer.push("set and watched x"); + + let resolve; + const promise = new Promise((res) => { + resolve = res; }) - - it('with a script', async function () { - const options = { - scripts: { - square: SQUARE_SCRIPT - } + + const masterNode = sentinel.getMasterNode(); + tracer.push(`initial masterPort = ${masterNode!.port} `); + + sentinel.on('topology-change', (event: RedisSentinelEvent) => { + tracer.push(`got topology-change event: ${JSON.stringify(event)}`); + if (event.type === "MASTER_CHANGE" && event.node.port != masterNode!.port) { + tracer.push("got a master change event that is not the same as before"); + resolve(event.node); } - - sentinel = frame.getSentinelClient(options); - await sentinel.connect(); - - const [, reply] = await Promise.all([ - sentinel.set('key', '2'), - sentinel.square('key') - ]); - - assert.equal(reply, 4); + }); + + tracer.push("stopping master"); + await frame.stopNode(masterNode!.port.toString()); + tracer.push("stopped master"); + + tracer.push("waiting on master change promise"); + const newMaster = await promise as RedisNode; + tracer.push(`got master change port as ${newMaster.port}`); + assert.notEqual(masterNode!.port, newMaster.port); + + tracer.push("watching again, shouldn't matter"); + await client.watch("y"); + + tracer.push("expecting multi to be rejected"); + await assert.rejects(async () => { await client.multi().get("x").exec() }, new Error("sentinel config changed in middle of a WATCH Transaction")); + tracer.push("multi was rejected"); + }); + + + // pubsub continues to work, even with a master change + it('pubsub - channel - with master change', async function () { + this.timeout(60000); + + sentinel = frame.getSentinelClient(); + sentinel.setTracer(tracer); + sentinel.on("error", () => { }); + await sentinel.connect(); + tracer.push(`connected`); + + let pubSubResolve; + const pubSubPromise = new Promise((res) => { + pubSubResolve = res; }) - it('multi with a script', async function () { - const options = { - scripts: { - square: SQUARE_SCRIPT - } + let tester = false; + await sentinel.subscribe('test', () => { + tracer.push(`got pubsub message`); + tester = true; + pubSubResolve(1); + }) + + let masterChangeResolve; + const masterChangePromise = new Promise((res) => { + masterChangeResolve = res; + }) + + const masterNode = sentinel.getMasterNode(); + tracer.push(`got masterPort as ${masterNode!.port}`); + + sentinel.on('topology-change', (event: RedisSentinelEvent) => { + tracer.push(`got topology-change event: ${JSON.stringify(event)}`); + if (event.type === "MASTER_CHANGE" && event.node.port != masterNode!.port) { + tracer.push("got a master change event that is not the same as before"); + masterChangeResolve(event.node); } - - sentinel = frame.getSentinelClient(options); - await sentinel.connect(); + }); + + tracer.push("stopping master"); + await frame.stopNode(masterNode!.port.toString()); + tracer.push("stopped master and waiting on change promise"); + + const newMaster = await masterChangePromise as RedisNode; + tracer.push(`got master change port as ${newMaster.port}`); + assert.notEqual(masterNode!.port, newMaster.port); + + tracer.push(`publishing pubsub message`); + await sentinel.publish('test', 'hello world'); + tracer.push(`published pubsub message and waiting pn pubsub promise`); + await pubSubPromise; + tracer.push(`got pubsub promise`); + + assert.equal(tester, true); + + // now unsubscribe + tester = false + await sentinel.unsubscribe('test') + await sentinel.publish('test', 'hello world'); + await setTimeout(1000); + + assert.equal(tester, false); + }); - const reply = await sentinel.multi().set('key', 2).square('key').exec(); + it('pubsub - pattern - with master change', async function () { + this.timeout(60000); - assert.deepEqual(reply, ['OK', 4]); + sentinel = frame.getSentinelClient(); + sentinel.setTracer(tracer); + sentinel.on("error", () => { }); + await sentinel.connect(); + tracer.push(`connected`); + + let pubSubResolve; + const pubSubPromise = new Promise((res) => { + pubSubResolve = res; }) - - it('with a function', async function () { - const options = { - functions: { - math: MATH_FUNCTION.library - } - } - sentinel = frame.getSentinelClient(options); - await sentinel.connect(); - - await sentinel.functionLoad( - MATH_FUNCTION.code, - { REPLACE: true } - ); - - await sentinel.set('key', '2'); - const resp = await sentinel.math.square('key'); - - assert.equal(resp, 4); + + let tester = false; + await sentinel.pSubscribe('test*', () => { + tracer.push(`got pubsub message`); + tester = true; + pubSubResolve(1); }) - it('multi with a function', async function () { - const options = { - functions: { - math: MATH_FUNCTION.library - } + let masterChangeResolve; + const masterChangePromise = new Promise((res) => { + masterChangeResolve = res; + }) + + const masterNode = sentinel.getMasterNode(); + tracer.push(`got masterPort as ${masterNode!.port}`); + + sentinel.on('topology-change', (event: RedisSentinelEvent) => { + tracer.push(`got topology-change event: ${JSON.stringify(event)}`); + if (event.type === "MASTER_CHANGE" && event.node.port != masterNode!.port) { + tracer.push("got a master change event that is not the same as before"); + masterChangeResolve(event.node); } - sentinel = frame.getSentinelClient(options); - await sentinel.connect(); - - await sentinel.functionLoad( - MATH_FUNCTION.code, - { REPLACE: true } - ); + }); + + tracer.push("stopping master"); + await frame.stopNode(masterNode!.port.toString()); + tracer.push("stopped master and waiting on master change promise"); + + const newMaster = await masterChangePromise as RedisNode; + tracer.push(`got master change port as ${newMaster.port}`); + assert.notEqual(masterNode!.port, newMaster.port); - const reply = await sentinel.multi().set('key', 2).math.square('key').exec(); - assert.deepEqual(reply, ['OK', 4]); + tracer.push(`publishing pubsub message`); + await sentinel.publish('testy', 'hello world'); + tracer.push(`published pubsub message and waiting on pubsub promise`); + await pubSubPromise; + tracer.push(`got pubsub promise`); + assert.equal(tester, true); + + // now unsubscribe + tester = false + await sentinel.pUnsubscribe('test*'); + await sentinel.publish('testy', 'hello world'); + await setTimeout(1000); + + assert.equal(tester, false); + }); + + // if we stop a node, the comand should "retry" until we reconfigure topology and execute on new topology + it('command immeaditely after stopping master', async function () { + this.timeout(60000); + + sentinel = frame.getSentinelClient(); + sentinel.setTracer(tracer); + sentinel.on("error", () => { }); + await sentinel.connect(); + + tracer.push("connected"); + + let masterChangeResolve; + const masterChangePromise = new Promise((res) => { + masterChangeResolve = res; }) - - it('with a module', async function () { - const options = { - modules: RedisBloomModules + + const masterNode = sentinel.getMasterNode(); + tracer.push(`original master port = ${masterNode!.port}`); + + let changeCount = 0; + sentinel.on('topology-change', (event: RedisSentinelEvent) => { + tracer.push(`got topology-change event: ${JSON.stringify(event)}`); + if (event.type === "MASTER_CHANGE" && event.node.port != masterNode!.port) { + changeCount++; + tracer.push(`got topology-change event we expected`); + masterChangeResolve(event.node); } - sentinel = frame.getSentinelClient(options); - await sentinel.connect(); - - const resp = await sentinel.bf.add('key', 'item') - assert.equal(resp, true); + }); + + tracer.push(`stopping masterNode`); + await frame.stopNode(masterNode!.port.toString()); + tracer.push(`stopped masterNode`); + assert.equal(await sentinel.set('x', 123), 'OK'); + tracer.push(`did the set operation`); + const presumamblyNewMaster = sentinel.getMasterNode(); + tracer.push(`new master node seems to be ${presumamblyNewMaster?.port} and waiting on master change promise`); + + const newMaster = await masterChangePromise as RedisNode; + tracer.push(`got new masternode event saying master is at ${newMaster.port}`); + assert.notEqual(masterNode!.port, newMaster.port); + + tracer.push(`doing the get`); + const val = await sentinel.get('x'); + tracer.push(`did the get and got ${val}`); + const newestMaster = sentinel.getMasterNode() + tracer.push(`after get, we see master as ${newestMaster?.port}`); + + switch (changeCount) { + case 1: + // if we only changed masters once, we should have the proper value + assert.equal(val, '123'); + break; + case 2: + // we changed masters twice quickly, so probably didn't replicate + // therefore, this is soewhat flakey, but the above is the common case + assert(val == '123' || val == null); + break; + default: + assert(false, "unexpected case"); + } + }); + + it('shutdown sentinel node', async function () { + this.timeout(60000); + + sentinel = frame.getSentinelClient(); + sentinel.setTracer(tracer); + sentinel.on("error", () => { }); + await sentinel.connect(); + tracer.push("connected"); + + let sentinelChangeResolve; + const sentinelChangePromise = new Promise((res) => { + sentinelChangeResolve = res; }) - it('multi with a module', async function () { - const options = { - modules: RedisBloomModules + const sentinelNode = sentinel.getSentinelNode(); + tracer.push(`sentinelNode = ${sentinelNode?.port}`) + + sentinel.on('topology-change', (event: RedisSentinelEvent) => { + tracer.push(`got topology-change event: ${JSON.stringify(event)}`); + if (event.type === "SENTINEL_CHANGE") { + tracer.push("got sentinel change event"); + sentinelChangeResolve(event.node); } - sentinel = frame.getSentinelClient(options); - await sentinel.connect(); - - const resp = await sentinel.multi().bf.add('key', 'item').exec(); - assert.deepEqual(resp, [true]); + }); + + tracer.push("Stopping sentinel node"); + await frame.stopSentinel(sentinelNode!.port.toString()); + tracer.push("Stopped sentinel node and waiting on sentinel change promise"); + const newSentinel = await sentinelChangePromise as RedisNode; + tracer.push("got sentinel change promise"); + assert.notEqual(sentinelNode!.port, newSentinel.port); + }); + + it('timer works, and updates sentinel list', async function () { + this.timeout(60000); + + sentinel = frame.getSentinelClient({ scanInterval: 1000 }); + sentinel.setTracer(tracer); + await sentinel.connect(); + tracer.push("connected"); + + let sentinelChangeResolve; + const sentinelChangePromise = new Promise((res) => { + sentinelChangeResolve = res; }) - - it('many readers', async function () { - this.timeout(10000); - - sentinel = frame.getSentinelClient({ replicaPoolSize: 8 }); - await sentinel.connect(); - - await sentinel.set("x", 1); - for (let i = 0; i < 10; i++) { - if (await sentinel.get("x") == "1") { - break; - } - await setTimeout(1000); - } - - const promises: Array> = []; - for (let i = 0; i < 500; i++) { - promises.push(sentinel.get("x")); - } - - const resp = await Promise.all(promises); - assert.equal(resp.length, 500); - for (let i = 0; i < 500; i++) { - assert.equal(resp[i], "1", `failed on match at ${i}`); + + sentinel.on('topology-change', (event: RedisSentinelEvent) => { + tracer.push(`got topology-change event: ${JSON.stringify(event)}`); + if (event.type === "SENTINE_LIST_CHANGE" && event.size == 4) { + tracer.push(`got sentinel list change event with right size`); + sentinelChangeResolve(event.size); } }); - - it('use', async function () { - this.timeout(30000); - - sentinel = frame.getSentinelClient({ replicaPoolSize: 1 }); - sentinel.on("error", () => { }); - await sentinel.connect(); - - await sentinel.use( - async (client: RedisSentinelClientType, ) => { - const masterNode = sentinel!.getMasterNode(); - await frame.stopNode(masterNode!.port.toString()); - await assert.doesNotReject(client.get('x')); - } - ); - }); - - it('use with script', async function () { - this.timeout(10000); - - const options = { - scripts: { - square: SQUARE_SCRIPT - } - } - - sentinel = frame.getSentinelClient(options); - await sentinel.connect(); - - const reply = await sentinel.use( - async (client: RedisSentinelClientType) => { - assert.equal(await client.set('key', '2'), 'OK'); - assert.equal(await client.get('key'), '2'); - return client.square('key') - } - ); - - assert.equal(reply, 4); - }) - - it('use with a function', async function () { - this.timeout(10000); - - const options = { - functions: { - math: MATH_FUNCTION.library - } - } - sentinel = frame.getSentinelClient(options); - await sentinel.connect(); - - await sentinel.functionLoad( - MATH_FUNCTION.code, - { REPLACE: true } - ); - - const reply = await sentinel.use( - async (client: RedisSentinelClientType) => { - await client.set('key', '2'); - return client.math.square('key'); - } - ); - - assert.equal(reply, 4); - }) - - it('use with a module', async function () { - const options = { - modules: RedisBloomModules - } - sentinel = frame.getSentinelClient(options); - await sentinel.connect(); - - const reply = await sentinel.use( - async (client: RedisSentinelClientType) => { - return client.bf.add('key', 'item'); - } - ); - - assert.equal(reply, true); - }) - - it('block on pool', async function () { - this.timeout(30000); - - sentinel = frame.getSentinelClient({ replicaPoolSize: 1 }); - sentinel.on("error", () => { }); - await sentinel.connect(); - - const promise = sentinel.use( - async client => { - await setTimeout(1000); - return await client.get("x"); - } - ) - - await sentinel.set("x", 1); - assert.equal(await promise, null); - }); - it('reserve client, takes a client out of pool', async function () { - this.timeout(30000); - - sentinel = frame.getSentinelClient({ masterPoolSize: 2, reserveClient: true }); - await sentinel.connect(); - - const promise1 = sentinel.use( - async client => { - const val = await client.get("x"); - await client.set("x", 2); - return val; - } - ) + tracer.push(`adding sentinel`); + await frame.addSentinel(); + tracer.push(`added sentinel and waiting on sentinel change promise`); + const newSentinelSize = await sentinelChangePromise as number; - const promise2 = sentinel.use( - async client => { - return client.get("x"); - } - ) + assert.equal(newSentinelSize, 4); + }); - await sentinel.set("x", 1); - assert.equal(await promise1, "1"); - assert.equal(await promise2, "2"); + it('stop replica, bring back replica', async function () { + this.timeout(60000); + + sentinel = frame.getSentinelClient({ replicaPoolSize: 1 }); + sentinel.setTracer(tracer); + sentinel.on('error', err => { }); + await sentinel.connect(); + tracer.push("connected"); + + let sentinelRemoveResolve; + const sentinelRemovePromise = new Promise((res) => { + sentinelRemoveResolve = res; }) - - it('multiple clients', async function () { - this.timeout(30000); - - sentinel = frame.getSentinelClient({ masterPoolSize: 2 }); - sentinel.on("error", () => { }); - await sentinel.connect(); - - let set = false; - - const promise = sentinel.use( - async client => { - await sentinel!.set("x", 1); - await client.get("x"); - } - ) - - await assert.doesNotReject(promise); - }); - - // by taking a lease, we know we will block on master as no clients are available, but as read occuring, means replica read occurs - it('replica reads', async function () { - this.timeout(30000); - - sentinel = frame.getSentinelClient({ replicaPoolSize: 1 }); - sentinel.on("error", () => { }); - await sentinel.connect(); - - const clientLease = await sentinel.aquire(); - clientLease.set('x', 456); - - let matched = false; - /* waits for replication */ - for (let i = 0; i < 15; i++) { - try { - assert.equal(await sentinel.get("x"), '456'); - matched = true; - break; - } catch (err) { - await setTimeout(1000); + + const replicaPort = await frame.getRandonNonMasterNode(); + + sentinel.on('topology-change', (event: RedisSentinelEvent) => { + tracer.push(`got topology-change event: ${JSON.stringify(event)}`); + if (event.type === "REPLICA_REMOVE") { + if (event.node.port.toString() == replicaPort) { + tracer.push("got expected replica removed event"); + sentinelRemoveResolve(event.node); + } else { + tracer.push(`got replica removed event for a different node: ${event.node.port}`); } } - - clientLease.release(); - - assert.equal(matched, true); }); - - it('pipeline', async function () { - this.timeout(30000); - - sentinel = frame.getSentinelClient({ replicaPoolSize: 1 }); - await sentinel.connect(); - - const resp = await sentinel.multi().set('x', 1).get('x').execAsPipeline(); - - assert.deepEqual(resp, ['OK', '1']); + + tracer.push(`replicaPort = ${replicaPort} and stopping it`); + await frame.stopNode(replicaPort); + tracer.push("stopped replica and waiting on sentinel removed promise"); + const stoppedNode = await sentinelRemovePromise as RedisNode; + tracer.push("got removed promise"); + assert.equal(stoppedNode.port, Number(replicaPort)); + + let sentinelRestartedResolve; + const sentinelRestartedPromise = new Promise((res) => { + sentinelRestartedResolve = res; }) - - it('use - watch - clean', async function () { - this.timeout(30000); - - sentinel = frame.getSentinelClient({ masterPoolSize: 2 }); - await sentinel.connect(); - - let promise = sentinel.use(async (client) => { - await client.set("x", 1); - await client.watch("x"); - return client.multi().get("x").exec(); - }); - - assert.deepEqual(await promise, ['1']); - }); - - it('use - watch - dirty', async function () { - this.timeout(30000); - - sentinel = frame.getSentinelClient({ masterPoolSize: 2 }); - await sentinel.connect(); - - let promise = sentinel.use(async (client) => { - await client.set('x', 1); - await client.watch('x'); - await sentinel!.set('x', 2); - return client.multi().get('x').exec(); - }); - - await assert.rejects(promise, new WatchError()); - }); - - it('lease - watch - clean', async function () { - sentinel = frame.getSentinelClient({ masterPoolSize: 2 }); - await sentinel.connect(); - - const leasedClient = await sentinel.aquire(); - await leasedClient.set('x', 1); - await leasedClient.watch('x'); - assert.deepEqual(await leasedClient.multi().get('x').exec(), ['1']) - }); - - it('lease - watch - dirty', async function () { - sentinel = frame.getSentinelClient({ masterPoolSize: 2 }); - await sentinel.connect(); - - const leasedClient = await sentinel.aquire(); - await leasedClient.set('x', 1); - await leasedClient.watch('x'); - await leasedClient.set('x', 2); - - await assert.rejects(leasedClient.multi().get('x').exec(), new WatchError()); - }); - - - it('watch does not carry through leases', async function () { - this.timeout(10000); - sentinel = frame.getSentinelClient(); - await sentinel.connect(); - - // each of these commands is an independent lease - assert.equal(await sentinel.use(client => client.watch("x")), 'OK') - assert.equal(await sentinel.use(client => client.set('x', 1)), 'OK'); - assert.deepEqual(await sentinel.use(client => client.multi().get('x').exec()), ['1']); - }); - - // stops master to force sentinel to update - it('stop master', async function () { - this.timeout(30000); - - sentinel = frame.getSentinelClient(); - sentinel.setTracer(tracer); - sentinel.on("error", () => { }); - await sentinel.connect(); - - tracer.push(`connected`); - - let masterChangeResolve; - const masterChangePromise = new Promise((res) => { - masterChangeResolve = res; - }) - - const masterNode = await sentinel.getMasterNode(); - sentinel.on('topology-change', (event: RedisSentinelEvent) => { - tracer.push(`got topology-change event: ${JSON.stringify(event)}`); - if (event.type === "MASTER_CHANGE" && event.node.port != masterNode!.port) { - tracer.push(`got expected master change event`); - masterChangeResolve(event.node); - } - }); - - tracer.push(`stopping master node`); - await frame.stopNode(masterNode!.port.toString()); - tracer.push(`stopped master node`); - - tracer.push(`waiting on master change promise`); - const newMaster = await masterChangePromise as RedisNode; - tracer.push(`got new master node of ${newMaster.port}`); - assert.notEqual(masterNode!.port, newMaster.port); - }); - - // if master changes, client should make sure user knows watches are invalid - it('watch across master change', async function () { - this.timeout(30000); - - sentinel = frame.getSentinelClient({ masterPoolSize: 2 }); - sentinel.setTracer(tracer); - sentinel.on("error", () => { }); - await sentinel.connect(); - - tracer.push("connected"); - - const client = await sentinel.aquire(); - tracer.push("aquired lease"); - - await client.set("x", 1); - await client.watch("x"); - - tracer.push("did a watch on lease"); - - let resolve; - const promise = new Promise((res) => { - resolve = res; - }) - - const masterNode = sentinel.getMasterNode(); - tracer.push(`got masterPort as ${masterNode!.port}`); - - sentinel.on('topology-change', (event: RedisSentinelEvent) => { - tracer.push(`got topology-change event: ${JSON.stringify(event)}`); - if (event.type === "MASTER_CHANGE" && event.node.port != masterNode!.port) { - tracer.push("resolving promise"); - resolve(event.node); - } - }); - - tracer.push("stopping master node"); - await frame.stopNode(masterNode!.port.toString()); - tracer.push("stopped master node and waiting on promise"); - - const newMaster = await promise as RedisNode; - tracer.push(`promise returned, newMaster = ${JSON.stringify(newMaster)}`); - assert.notEqual(masterNode!.port, newMaster.port); - tracer.push(`newMaster does not equal old master`); - - tracer.push(`waiting to assert that a multi/exec now fails`); - await assert.rejects(async () => { await client.multi().get("x").exec() }, new Error("sentinel config changed in middle of a WATCH Transaction")); - tracer.push(`asserted that a multi/exec now fails`); - }); - - // same as above, but set a watch before and after master change, shouldn't change the fact that watches are invalid - it('watch before and after master change', async function () { - this.timeout(30000); - - sentinel = frame.getSentinelClient({ masterPoolSize: 2 }); - sentinel.setTracer(tracer); - sentinel.on("error", () => { }); - await sentinel.connect(); - tracer.push("connected"); - - const client = await sentinel.aquire(); - tracer.push("got leased client"); - await client.set("x", 1); - await client.watch("x"); - - tracer.push("set and watched x"); - - let resolve; - const promise = new Promise((res) => { - resolve = res; - }) - - const masterNode = sentinel.getMasterNode(); - tracer.push(`initial masterPort = ${masterNode!.port} `); - - sentinel.on('topology-change', (event: RedisSentinelEvent) => { - tracer.push(`got topology-change event: ${JSON.stringify(event)}`); - if (event.type === "MASTER_CHANGE" && event.node.port != masterNode!.port) { - tracer.push("got a master change event that is not the same as before"); - resolve(event.node); - } - }); - - tracer.push("stopping master"); - await frame.stopNode(masterNode!.port.toString()); - tracer.push("stopped master"); - - tracer.push("waiting on master change promise"); - const newMaster = await promise as RedisNode; - tracer.push(`got master change port as ${newMaster.port}`); - assert.notEqual(masterNode!.port, newMaster.port); - - tracer.push("watching again, shouldn't matter"); - await client.watch("y"); - - tracer.push("expecting multi to be rejected"); - await assert.rejects(async () => { await client.multi().get("x").exec() }, new Error("sentinel config changed in middle of a WATCH Transaction")); - tracer.push("multi was rejected"); - }); - - it('plain pubsub - channel', async function () { - this.timeout(30000); - - sentinel = frame.getSentinelClient(); - sentinel.setTracer(tracer); - await sentinel.connect(); - tracer.push(`connected`); - - let pubSubResolve; - const pubSubPromise = new Promise((res) => { - pubSubResolve = res; - }); - - let tester = false; - await sentinel.subscribe('test', () => { - tracer.push(`got pubsub message`); - tester = true; - pubSubResolve(1); - }) - - tracer.push(`publishing pubsub message`); - await sentinel.publish('test', 'hello world'); - tracer.push(`waiting on pubsub promise`); - await pubSubPromise; - tracer.push(`got pubsub promise`); - assert.equal(tester, true); - - // now unsubscribe - tester = false - tracer.push(`unsubscribing pubsub listener`); - await sentinel.unsubscribe('test') - tracer.push(`pubishing pubsub message`); - await sentinel.publish('test', 'hello world'); - await setTimeout(1000); - - tracer.push(`ensuring pubsub was unsubscribed via an assert`); - assert.equal(tester, false); - }); - - it('plain pubsub - pattern', async function () { - this.timeout(30000); - - sentinel = frame.getSentinelClient(); - sentinel.setTracer(tracer); - await sentinel.connect(); - tracer.push(`connected`); - - let pubSubResolve; - const pubSubPromise = new Promise((res) => { - pubSubResolve = res; - }); - - let tester = false; - await sentinel.pSubscribe('test*', () => { - tracer.push(`got pubsub message`); - tester = true; - pubSubResolve(1); - }) - - tracer.push(`publishing pubsub message`); - await sentinel.publish('testy', 'hello world'); - tracer.push(`waiting on pubsub promise`); - await pubSubPromise; - tracer.push(`got pubsub promise`); - assert.equal(tester, true); - - // now unsubscribe - tester = false - tracer.push(`unsubscribing pubsub listener`); - await sentinel.pUnsubscribe('test*'); - tracer.push(`pubishing pubsub message`); - await sentinel.publish('testy', 'hello world'); - await setTimeout(1000); - - tracer.push(`ensuring pubsub was unsubscribed via an assert`); - assert.equal(tester, false); - }); - - // pubsub continues to work, even with a master change - it('pubsub - channel - with master change', async function () { - this.timeout(30000); - - sentinel = frame.getSentinelClient(); - sentinel.setTracer(tracer); - sentinel.on("error", () => { }); - await sentinel.connect(); - tracer.push(`connected`); - - let pubSubResolve; - const pubSubPromise = new Promise((res) => { - pubSubResolve = res; - }) - - let tester = false; - await sentinel.subscribe('test', () => { - tracer.push(`got pubsub message`); - tester = true; - pubSubResolve(1); - }) - - let masterChangeResolve; - const masterChangePromise = new Promise((res) => { - masterChangeResolve = res; - }) - - const masterNode = sentinel.getMasterNode(); - tracer.push(`got masterPort as ${masterNode!.port}`); - - sentinel.on('topology-change', (event: RedisSentinelEvent) => { - tracer.push(`got topology-change event: ${JSON.stringify(event)}`); - if (event.type === "MASTER_CHANGE" && event.node.port != masterNode!.port) { - tracer.push("got a master change event that is not the same as before"); - masterChangeResolve(event.node); - } - }); - - tracer.push("stopping master"); - await frame.stopNode(masterNode!.port.toString()); - tracer.push("stopped master and waiting on change promise"); - - const newMaster = await masterChangePromise as RedisNode; - tracer.push(`got master change port as ${newMaster.port}`); - assert.notEqual(masterNode!.port, newMaster.port); - - tracer.push(`publishing pubsub message`); - await sentinel.publish('test', 'hello world'); - tracer.push(`published pubsub message and waiting pn pubsub promise`); - await pubSubPromise; - tracer.push(`got pubsub promise`); - - assert.equal(tester, true); - - // now unsubscribe - tester = false - await sentinel.unsubscribe('test') - await sentinel.publish('test', 'hello world'); - await setTimeout(1000); - - assert.equal(tester, false); - }); - - it('pubsub - pattern - with master change', async function () { - this.timeout(30000); - - sentinel = frame.getSentinelClient(); - sentinel.setTracer(tracer); - sentinel.on("error", () => { }); - await sentinel.connect(); - tracer.push(`connected`); - - let pubSubResolve; - const pubSubPromise = new Promise((res) => { - pubSubResolve = res; - }) - - let tester = false; - await sentinel.pSubscribe('test*', () => { - tracer.push(`got pubsub message`); - tester = true; - pubSubResolve(1); - }) - - let masterChangeResolve; - const masterChangePromise = new Promise((res) => { - masterChangeResolve = res; - }) - - const masterNode = sentinel.getMasterNode(); - tracer.push(`got masterPort as ${masterNode!.port}`); - - sentinel.on('topology-change', (event: RedisSentinelEvent) => { - tracer.push(`got topology-change event: ${JSON.stringify(event)}`); - if (event.type === "MASTER_CHANGE" && event.node.port != masterNode!.port) { - tracer.push("got a master change event that is not the same as before"); - masterChangeResolve(event.node); - } - }); - - tracer.push("stopping master"); - await frame.stopNode(masterNode!.port.toString()); - tracer.push("stopped master and waiting on master change promise"); - - const newMaster = await masterChangePromise as RedisNode; - tracer.push(`got master change port as ${newMaster.port}`); - assert.notEqual(masterNode!.port, newMaster.port); - - tracer.push(`publishing pubsub message`); - await sentinel.publish('testy', 'hello world'); - tracer.push(`published pubsub message and waiting on pubsub promise`); - await pubSubPromise; - tracer.push(`got pubsub promise`); - assert.equal(tester, true); - - // now unsubscribe - tester = false - await sentinel.pUnsubscribe('test*'); - await sentinel.publish('testy', 'hello world'); - await setTimeout(1000); - - assert.equal(tester, false); - }); - - // if we stop a node, the comand should "retry" until we reconfigure topology and execute on new topology - it('command immeaditely after stopping master', async function () { - this.timeout(30000); - - sentinel = frame.getSentinelClient(); - sentinel.setTracer(tracer); - sentinel.on("error", () => { }); - await sentinel.connect(); - - tracer.push("connected"); - - let masterChangeResolve; - const masterChangePromise = new Promise((res) => { - masterChangeResolve = res; - }) - - const masterNode = sentinel.getMasterNode(); - tracer.push(`original master port = ${masterNode!.port}`); - - let changeCount = 0; - sentinel.on('topology-change', (event: RedisSentinelEvent) => { - tracer.push(`got topology-change event: ${JSON.stringify(event)}`); - if (event.type === "MASTER_CHANGE" && event.node.port != masterNode!.port) { - changeCount++; - tracer.push(`got topology-change event we expected`); - masterChangeResolve(event.node); - } - }); - - tracer.push(`stopping masterNode`); - await frame.stopNode(masterNode!.port.toString()); - tracer.push(`stopped masterNode`); - assert.equal(await sentinel.set('x', 123), 'OK'); - tracer.push(`did the set operation`); - const presumamblyNewMaster = sentinel.getMasterNode(); - tracer.push(`new master node seems to be ${presumamblyNewMaster?.port} and waiting on master change promise`); - - const newMaster = await masterChangePromise as RedisNode; - tracer.push(`got new masternode event saying master is at ${newMaster.port}`); - assert.notEqual(masterNode!.port, newMaster.port); - - tracer.push(`doing the get`); - const val = await sentinel.get('x'); - tracer.push(`did the get and got ${val}`); - const newestMaster = sentinel.getMasterNode() - tracer.push(`after get, we see master as ${newestMaster?.port}`); - - switch (changeCount) { - case 1: - // if we only changed masters once, we should have the proper value - assert.equal(val, '123'); - break; - case 2: - // we changed masters twice quickly, so probably didn't replicate - // therefore, this is soewhat flakey, but the above is the common case - assert(val == '123' || val == null); - break; - default: - assert(false, "unexpected case"); + + sentinel.on('topology-change', (event: RedisSentinelEvent) => { + tracer.push(`got topology-change event: ${JSON.stringify(event)}`); + if (event.type === "REPLICA_ADD") { + tracer.push("got replica added event"); + sentinelRestartedResolve(event.node); } }); - - it('shutdown sentinel node', async function () { - this.timeout(30000); - - sentinel = frame.getSentinelClient(); - sentinel.setTracer(tracer); - sentinel.on("error", () => { }); - await sentinel.connect(); - tracer.push("connected"); - - let sentinelChangeResolve; - const sentinelChangePromise = new Promise((res) => { - sentinelChangeResolve = res; - }) - - const sentinelNode = sentinel.getSentinelNode(); - tracer.push(`sentinelNode = ${sentinelNode?.port}`) - - sentinel.on('topology-change', (event: RedisSentinelEvent) => { - tracer.push(`got topology-change event: ${JSON.stringify(event)}`); - if (event.type === "SENTINEL_CHANGE") { - tracer.push("got sentinel change event"); - sentinelChangeResolve(event.node); - } - }); - - tracer.push("Stopping sentinel node"); - await frame.stopSentinel(sentinelNode!.port.toString()); - tracer.push("Stopped sentinel node and waiting on sentinel change promise"); - const newSentinel = await sentinelChangePromise as RedisNode; - tracer.push("got sentinel change promise"); - assert.notEqual(sentinelNode!.port, newSentinel.port); - }); - - it('timer works, and updates sentinel list', async function () { - this.timeout(30000); - - sentinel = frame.getSentinelClient({ scanInterval: 1000 }); - sentinel.setTracer(tracer); - await sentinel.connect(); - tracer.push("connected"); - - let sentinelChangeResolve; - const sentinelChangePromise = new Promise((res) => { - sentinelChangeResolve = res; - }) - - sentinel.on('topology-change', (event: RedisSentinelEvent) => { - tracer.push(`got topology-change event: ${JSON.stringify(event)}`); - if (event.type === "SENTINE_LIST_CHANGE" && event.size == 4) { - tracer.push(`got sentinel list change event with right size`); - sentinelChangeResolve(event.size); - } - }); - - tracer.push(`adding sentinel`); - await frame.addSentinel(); - tracer.push(`added sentinel and waiting on sentinel change promise`); - const newSentinelSize = await sentinelChangePromise as number; - - assert.equal(newSentinelSize, 4); - }); - - it('stop replica, bring back replica', async function () { - this.timeout(30000); - - sentinel = frame.getSentinelClient({ replicaPoolSize: 1 }); - sentinel.setTracer(tracer); - sentinel.on('error', err => { }); - await sentinel.connect(); - tracer.push("connected"); - - let sentinelRemoveResolve; - const sentinelRemovePromise = new Promise((res) => { - sentinelRemoveResolve = res; - }) - - const replicaPort = await frame.getRandonNonMasterNode(); - - sentinel.on('topology-change', (event: RedisSentinelEvent) => { - tracer.push(`got topology-change event: ${JSON.stringify(event)}`); - if (event.type === "REPLICA_REMOVE") { - if (event.node.port.toString() == replicaPort) { - tracer.push("got expected replica removed event"); - sentinelRemoveResolve(event.node); - } else { - tracer.push(`got replica removed event for a different node: ${event.node.port}`); - } - } - }); - - tracer.push(`replicaPort = ${replicaPort} and stopping it`); - await frame.stopNode(replicaPort); - tracer.push("stopped replica and waiting on sentinel removed promise"); - const stoppedNode = await sentinelRemovePromise as RedisNode; - tracer.push("got removed promise"); - assert.equal(stoppedNode.port, Number(replicaPort)); - - let sentinelRestartedResolve; - const sentinelRestartedPromise = new Promise((res) => { - sentinelRestartedResolve = res; - }) - - sentinel.on('topology-change', (event: RedisSentinelEvent) => { - tracer.push(`got topology-change event: ${JSON.stringify(event)}`); - if (event.type === "REPLICA_ADD") { - tracer.push("got replica added event"); - sentinelRestartedResolve(event.node); - } - }); - - tracer.push("restarting replica"); - await frame.restartNode(replicaPort); - tracer.push("restarted replica and waiting on restart promise"); - const restartedNode = await sentinelRestartedPromise as RedisNode; - tracer.push("got restarted promise"); - assert.equal(restartedNode.port, Number(replicaPort)); - }) - - it('add a node / new replica', async function () { - this.timeout(30000); - - sentinel = frame.getSentinelClient({ scanInterval: 2000, replicaPoolSize: 1 }); - sentinel.setTracer(tracer); - // need to handle errors, as the spawning a new docker node can cause existing connections to time out - sentinel.on('error', err => { }); - await sentinel.connect(); - tracer.push("connected"); - - let nodeAddedResolve: (value: RedisNode) => void; - const nodeAddedPromise = new Promise((res) => { - nodeAddedResolve = res as (value: RedisNode) => void; - }); - - const portSet = new Set(); - for (const port of frame.getAllNodesPort()) { - portSet.add(port); - } - - // "on" and not "once" as due to connection timeouts, can happen multiple times, and want right one - sentinel.on('topology-change', (event: RedisSentinelEvent) => { - tracer.push(`got topology-change event: ${JSON.stringify(event)}`); - if (event.type === "REPLICA_ADD") { - if (!portSet.has(event.node.port)) { - tracer.push("got expected replica added event"); - nodeAddedResolve(event.node); - } - } - }); - - tracer.push("adding node"); - await frame.addNode(); - tracer.push("added node and waiting on added promise"); - await nodeAddedPromise; - }) + + tracer.push("restarting replica"); + await frame.restartNode(replicaPort); + tracer.push("restarted replica and waiting on restart promise"); + const restartedNode = await sentinelRestartedPromise as RedisNode; + tracer.push("got restarted promise"); + assert.equal(restartedNode.port, Number(replicaPort)); }) - - describe('Sentinel Factory', function () { - let master: RedisClientType | undefined; - let replica: RedisClientType | undefined; - - beforeEach(async function () { - this.timeout(0); - await frame.getAllRunning(); - - await steadyState(frame); - longestTestDelta = 0; - }) - - afterEach(async function () { - if (this!.currentTest!.state === 'failed') { - console.log(`longest event loop blocked delta: ${longestDelta}`); - console.log(`longest event loop blocked in failing test: ${longestTestDelta}`); - console.log("trace:"); - for (const line of tracer) { - console.log(line); - } - const results = await Promise.all([ - frame.sentinelSentinels(), - frame.sentinelMaster(), - frame.sentinelReplicas() - ]) - console.log(`sentinel sentinels:\n${JSON.stringify(results[0], undefined, '\t')}`); - console.log(`sentinel master:\n${JSON.stringify(results[1], undefined, '\t')}`); - console.log(`sentinel replicas:\n${JSON.stringify(results[2], undefined, '\t')}`); - const { stdout, stderr } = await execAsync("docker ps -a"); - console.log(`docker stdout:\n${stdout}`); - console.log(`docker stderr:\n${stderr}`); - } - tracer.length = 0; - - if (master !== undefined) { - if (master.isOpen) { - master.destroy(); - } - master = undefined; - } - - if (replica !== undefined) { - if (replica.isOpen) { - replica.destroy(); + it('add a node / new replica', async function () { + this.timeout(60000); + + sentinel = frame.getSentinelClient({ scanInterval: 2000, replicaPoolSize: 1 }); + sentinel.setTracer(tracer); + // need to handle errors, as the spawning a new docker node can cause existing connections to time out + sentinel.on('error', err => { }); + await sentinel.connect(); + tracer.push("connected"); + + let nodeAddedResolve: (value: RedisNode) => void; + const nodeAddedPromise = new Promise((res) => { + nodeAddedResolve = res as (value: RedisNode) => void; + }); + + const portSet = new Set(); + for (const port of frame.getAllNodesPort()) { + portSet.add(port); + } + + // "on" and not "once" as due to connection timeouts, can happen multiple times, and want right one + sentinel.on('topology-change', (event: RedisSentinelEvent) => { + tracer.push(`got topology-change event: ${JSON.stringify(event)}`); + if (event.type === "REPLICA_ADD") { + if (!portSet.has(event.node.port)) { + tracer.push("got expected replica added event"); + nodeAddedResolve(event.node); } - replica = undefined; } - }) - - it('sentinel factory - master', async function () { - const sentinelPorts = frame.getAllSentinelsPort(); - const sentinels: Array = []; - for (const port of sentinelPorts) { - sentinels.push({ host: "localhost", port: port }); - } - - const factory = new RedisSentinelFactory({ name: frame.config.sentinelName, sentinelRootNodes: sentinels, sentinelClientOptions: {password: password}, nodeClientOptions: {password: password} }) - await factory.updateSentinelRootNodes(); - - master = await factory.getMasterClient(); - await master.connect(); - - assert.equal(await master.set("x", 1), 'OK'); - }) - - it('sentinel factory - replica', async function () { - const sentinelPorts = frame.getAllSentinelsPort(); - const sentinels: Array = []; - - for (const port of sentinelPorts) { - sentinels.push({ host: "localhost", port: port }); - } - - const factory = new RedisSentinelFactory({ name: frame.config.sentinelName, sentinelRootNodes: sentinels, sentinelClientOptions: {password: password}, nodeClientOptions: {password: password} }) - await factory.updateSentinelRootNodes(); - - const masterNode = await factory.getMasterNode(); - replica = await factory.getReplicaClient(); - const replicaSocketOptions = replica.options?.socket as unknown as RedisTcpSocketOptions | undefined; - assert.notEqual(masterNode.port, replicaSocketOptions?.port) - }) - - it('sentinel factory - bad node', async function () { - const factory = new RedisSentinelFactory({ name: frame.config.sentinelName, sentinelRootNodes: [{ host: "locahost", port: 1 }] }); - await assert.rejects(factory.updateSentinelRootNodes(), new Error("Couldn't connect to any sentinel node")); - }) - - it('sentinel factory - invalid db name', async function () { - this.timeout(15000); - - const sentinelPorts = frame.getAllSentinelsPort(); - const sentinels: Array = []; - - for (const port of sentinelPorts) { - sentinels.push({ host: "localhost", port: port }); - } - - const factory = new RedisSentinelFactory({ name: "invalid-name", sentinelRootNodes: sentinels, sentinelClientOptions: {password: password}, nodeClientOptions: {password: password} }) - await assert.rejects(factory.updateSentinelRootNodes(), new Error("ERR No such master with that name")); - }) - - it('sentinel factory - no available nodes', async function () { - this.timeout(15000); - - const sentinelPorts = frame.getAllSentinelsPort(); - const sentinels: Array = []; - - for (const port of sentinelPorts) { - sentinels.push({ host: "localhost", port: port }); - } - - const factory = new RedisSentinelFactory({ name: frame.config.sentinelName, sentinelRootNodes: sentinels, sentinelClientOptions: {password: password}, nodeClientOptions: {password: password} }) - - for (const node of frame.getAllNodesPort()) { - await frame.stopNode(node.toString()); - } - - await setTimeout(1000); - - await assert.rejects(factory.getMasterNode(), new Error("Master Node Not Enumerated")); - }) + }); + + tracer.push("adding node"); + await frame.addNode(); + tracer.push("added node and waiting on added promise"); + await nodeAddedPromise; }) - }) + }); }); + + + diff --git a/packages/client/lib/sentinel/index.ts b/packages/client/lib/sentinel/index.ts index 92a87fbb145..73c4fffd070 100644 --- a/packages/client/lib/sentinel/index.ts +++ b/packages/client/lib/sentinel/index.ts @@ -345,9 +345,12 @@ export default class RedisSentinel< key: K, value: V ) { - const proxy = Object.create(this._self); - proxy._commandOptions = Object.create(this._self.#commandOptions ?? null); - proxy._commandOptions[key] = value; + const proxy = Object.create(this); + // Create new commandOptions object with the inherited properties + proxy._self.#commandOptions = { + ...(this._self.#commandOptions || {}), + [key]: value + }; return proxy as RedisSentinelType< M, F, @@ -682,9 +685,10 @@ class RedisSentinelInternal< async #connect() { let count = 0; - while (true) { + while (true) { this.#trace("starting connect loop"); + count+=1; if (this.#destroy) { this.#trace("in #connect and want to destroy") return; @@ -1109,7 +1113,7 @@ class RedisSentinelInternal< this.#trace(`transform: destroying old masters if open`); for (const client of this.#masterClients) { - masterWatches.push(client.isWatching); + masterWatches.push(client.isWatching || client.isDirtyWatch); if (client.isOpen) { client.destroy() @@ -1460,4 +1464,4 @@ export class RedisSentinelFactory extends EventEmitter { } }); } -} +} \ No newline at end of file diff --git a/packages/client/lib/sentinel/test-util.ts b/packages/client/lib/sentinel/test-util.ts index 25dd4c4371a..60696bc3437 100644 --- a/packages/client/lib/sentinel/test-util.ts +++ b/packages/client/lib/sentinel/test-util.ts @@ -602,4 +602,4 @@ export class SentinelFramework extends DockerBase { } } } -} +} \ No newline at end of file diff --git a/packages/client/lib/test-utils.ts b/packages/client/lib/test-utils.ts index f7862a9d685..63f43ba5e6a 100644 --- a/packages/client/lib/test-utils.ts +++ b/packages/client/lib/test-utils.ts @@ -2,9 +2,10 @@ import TestUtils from '@redis/test-utils'; import { SinonSpy } from 'sinon'; import { setTimeout } from 'node:timers/promises'; import { CredentialsProvider } from './authx'; -import { Command } from './RESP/types'; -import { BasicCommandParser } from './client/parser'; - +import { Command, NumberReply } from './RESP/types'; +import { BasicCommandParser, CommandParser } from './client/parser'; +import { defineScript } from './lua-script'; +import RedisBloomModules from '@redis/bloom'; const utils = TestUtils.createFromConfig({ dockerImageName: 'redislabs/client-libs-test', dockerImageVersionArgument: 'redis-version', @@ -42,6 +43,45 @@ const streamingCredentialsProvider: CredentialsProvider = } as const; +const SQUARE_SCRIPT = defineScript({ + SCRIPT: + `local number = redis.call('GET', KEYS[1]) + return number * number`, + NUMBER_OF_KEYS: 1, + FIRST_KEY_INDEX: 0, + parseCommand(parser: CommandParser, key: string) { + parser.pushKey(key); + }, + transformReply: undefined as unknown as () => NumberReply +}); + +export const MATH_FUNCTION = { + name: 'math', + engine: 'LUA', + code: + `#!LUA name=math + redis.register_function { + function_name = "square", + callback = function(keys, args) + local number = redis.call('GET', keys[1]) + return number * number + end, + flags = { "no-writes" } + }`, + library: { + square: { + NAME: 'square', + IS_READ_ONLY: true, + NUMBER_OF_KEYS: 1, + FIRST_KEY_INDEX: 0, + parseCommand(parser: CommandParser, key: string) { + parser.pushKey(key); + }, + transformReply: undefined as unknown as () => NumberReply + } + } +}; + export const GLOBAL = { SERVERS: { OPEN: { @@ -86,6 +126,43 @@ export const GLOBAL = { useReplicas: true } } + }, + SENTINEL: { + OPEN: { + serverArguments: [...DEBUG_MODE_ARGS], + }, + PASSWORD: { + serverArguments: ['--requirepass', 'test_password', ...DEBUG_MODE_ARGS], + }, + WITH_SCRIPT: { + serverArguments: [...DEBUG_MODE_ARGS], + scripts: { + square: SQUARE_SCRIPT, + }, + }, + WITH_FUNCTION: { + serverArguments: [...DEBUG_MODE_ARGS], + functions: { + math: MATH_FUNCTION.library, + }, + }, + WITH_MODULE: { + serverArguments: [...DEBUG_MODE_ARGS], + modules: RedisBloomModules, + }, + WITH_REPLICA_POOL_SIZE_1: { + serverArguments: [...DEBUG_MODE_ARGS], + replicaPoolSize: 1, + }, + WITH_RESERVE_CLIENT_MASTER_POOL_SIZE_2: { + serverArguments: [...DEBUG_MODE_ARGS], + masterPoolSize: 2, + reserveClient: true, + }, + WITH_MASTER_POOL_SIZE_2: { + serverArguments: [...DEBUG_MODE_ARGS], + masterPoolSize: 2, + } } }; diff --git a/packages/test-utils/lib/dockers.ts b/packages/test-utils/lib/dockers.ts index e3ff5edc38b..3814a80923b 100644 --- a/packages/test-utils/lib/dockers.ts +++ b/packages/test-utils/lib/dockers.ts @@ -4,9 +4,11 @@ import { once } from 'node:events'; import { createClient } from '@redis/client/index'; import { setTimeout } from 'node:timers/promises'; // import { ClusterSlotsReply } from '@redis/client/dist/lib/commands/CLUSTER_SLOTS'; - import { execFile as execFileCallback } from 'node:child_process'; import { promisify } from 'node:util'; +import * as fs from 'node:fs'; +import * as os from 'node:os'; +import * as path from 'node:path'; const execAsync = promisify(execFileCallback); @@ -38,37 +40,64 @@ const portIterator = (async function* (): AsyncIterableIterator { throw new Error('All ports are in use'); })(); -export interface RedisServerDockerConfig { +interface RedisServerDockerConfig { image: string; version: string; } +interface SentinelConfig { + mode: "sentinel"; + mounts: Array; + port: number; +} + +interface ServerConfig { + mode: "server"; +} + +export type RedisServerDockerOptions = RedisServerDockerConfig & (SentinelConfig | ServerConfig) + export interface RedisServerDocker { port: number; dockerId: string; } -async function spawnRedisServerDocker({ - image, - version -}: RedisServerDockerConfig, serverArguments: Array): Promise { - const port = (await portIterator.next()).value; +async function spawnRedisServerDocker( +options: RedisServerDockerOptions, serverArguments: Array): Promise { + let port; + if (options.mode == "sentinel") { + port = options.port; + } else { + port = (await portIterator.next()).value; + } + const portStr = port.toString(); const dockerArgs = [ 'run', - '-e', `PORT=${portStr}`, + '--init', + '-e', `PORT=${portStr}` + ]; + + if (options.mode == "sentinel") { + options.mounts.forEach(mount => { + dockerArgs.push('-v', mount); + }); + } + + dockerArgs.push( '-d', '--network', 'host', - `${image}:${version}`, - '--port', portStr - ]; + `${options.image}:${options.version}` + ); if (serverArguments.length > 0) { - dockerArgs.push(...serverArguments); + for (let i = 0; i < serverArguments.length; i++) { + dockerArgs.push(serverArguments[i]) + } } - console.log(`[Docker] Spawning Redis container - Image: ${image}:${version}, Port: ${port}`); + console.log(`[Docker] Spawning Redis container - Image: ${options.image}:${options.version}, Port: ${port}, Mode: ${options.mode}`); const { stdout, stderr } = await execAsync('docker', dockerArgs); @@ -87,7 +116,7 @@ async function spawnRedisServerDocker({ } const RUNNING_SERVERS = new Map, ReturnType>(); -export function spawnRedisServer(dockerConfig: RedisServerDockerConfig, serverArguments: Array): Promise { +export function spawnRedisServer(dockerConfig: RedisServerDockerOptions, serverArguments: Array): Promise { const runningServer = RUNNING_SERVERS.get(serverArguments); if (runningServer) { return runningServer; @@ -113,7 +142,7 @@ after(() => { ); }); -export interface RedisClusterDockersConfig extends RedisServerDockerConfig { +export type RedisClusterDockersConfig = RedisServerDockerOptions & { numberOfMasters?: number; numberOfReplicas?: number; } @@ -178,7 +207,7 @@ async function spawnRedisClusterNodeDockers( } async function spawnRedisClusterNodeDocker( - dockersConfig: RedisClusterDockersConfig, + dockersConfig: RedisServerDockerOptions, serverArguments: Array, clientConfig?: Partial ) { @@ -291,3 +320,107 @@ after(() => { }) ); }); + + +const RUNNING_NODES = new Map, Array>(); +const RUNNING_SENTINELS = new Map, Array>(); + +export async function spawnRedisSentinel( + dockerConfigs: RedisServerDockerOptions, + serverArguments: Array, +): Promise> { + const runningNodes = RUNNING_SENTINELS.get(serverArguments); + if (runningNodes) { + return runningNodes; + } + + const passIndex = serverArguments.indexOf('--requirepass')+1; + let password: string | undefined = undefined; + if (passIndex != 0) { + password = serverArguments[passIndex]; + } + + const master = await spawnRedisServerDocker(dockerConfigs, serverArguments); + const redisNodes: Array = [master]; + const replicaPromises: Array> = []; + + const replicasCount = 2; + for (let i = 0; i < replicasCount; i++) { + replicaPromises.push((async () => { + const replica = await spawnRedisServerDocker(dockerConfigs, serverArguments); + const client = createClient({ + socket: { + port: replica.port + }, + password: password + }); + + await client.connect(); + await client.replicaOf("127.0.0.1", master.port); + await client.close(); + + return replica; + })()); + } + + const replicas = await Promise.all(replicaPromises); + redisNodes.push(...replicas); + RUNNING_NODES.set(serverArguments, redisNodes); + + const sentinelPromises: Array> = []; + const sentinelCount = 3; + + const appPrefix = 'sentinel-config-dir'; + const tmpDir = fs.mkdtempSync(path.join(os.tmpdir(), appPrefix)); + + for (let i = 0; i < sentinelCount; i++) { + sentinelPromises.push((async () => { + const port = (await portIterator.next()).value; + + let sentinelConfig = `port ${port} +sentinel monitor mymaster 127.0.0.1 ${master.port} 2 +sentinel down-after-milliseconds mymaster 5000 +sentinel failover-timeout mymaster 6000 +`; + if (password !== undefined) { + sentinelConfig += `requirepass ${password}\n`; + sentinelConfig += `sentinel auth-pass mymaster ${password}\n`; + } + + const dir = fs.mkdtempSync(path.join(tmpDir, i.toString())); + fs.writeFile(`${dir}/redis.conf`, sentinelConfig, err => { + if (err) { + console.error("failed to create temporary config file", err); + } + }); + + return await spawnRedisServerDocker( + { + image: dockerConfigs.image, + version: dockerConfigs.version, + mode: "sentinel", + mounts: [`${dir}/redis.conf:/redis/config/node-sentinel-1/redis.conf`], + port: port, + }, serverArguments); + })()); + } + + const sentinelNodes = await Promise.all(sentinelPromises); + RUNNING_SENTINELS.set(serverArguments, sentinelNodes); + + if (tmpDir) { + fs.rmSync(tmpDir, { recursive: true }); + } + + return sentinelNodes; +} + +after(() => { + return Promise.all( + [...RUNNING_NODES.values(), ...RUNNING_SENTINELS.values()].map(async dockersPromise => { + return Promise.all( + dockersPromise.map(({ dockerId }) => dockerRemove(dockerId)) + ); + }) + ); +}); diff --git a/packages/test-utils/lib/index.ts b/packages/test-utils/lib/index.ts index 117d089bcbe..8ed85bf6e3e 100644 --- a/packages/test-utils/lib/index.ts +++ b/packages/test-utils/lib/index.ts @@ -6,8 +6,11 @@ import { TypeMapping, // CommandPolicies, createClient, + createSentinel, RedisClientOptions, RedisClientType, + RedisSentinelOptions, + RedisSentinelType, RedisPoolOptions, RedisClientPoolType, createClientPool, @@ -15,7 +18,8 @@ import { RedisClusterOptions, RedisClusterType } from '@redis/client/index'; -import { RedisServerDockerConfig, spawnRedisServer, spawnRedisCluster } from './dockers'; +import { RedisNode } from '@redis/client/lib/sentinel/types' +import { spawnRedisServer, spawnRedisCluster, spawnRedisSentinel, RedisServerDockerOptions } from './dockers'; import yargs from 'yargs'; import { hideBin } from 'yargs/helpers'; @@ -68,6 +72,24 @@ interface ClientTestOptions< disableClientSetup?: boolean; } +interface SentinelTestOptions< + M extends RedisModules, + F extends RedisFunctions, + S extends RedisScripts, + RESP extends RespVersions, + TYPE_MAPPING extends TypeMapping +> extends CommonTestOptions { + sentinelOptions?: Partial>; + clientOptions?: Partial>; + scripts?: S; + functions?: F; + modules?: M; + disableClientSetup?: boolean; + replicaPoolSize?: number; + masterPoolSize?: number; + reserveClient?: boolean; +} + interface ClientPoolTestOptions< M extends RedisModules, F extends RedisFunctions, @@ -148,13 +170,14 @@ export default class TestUtils { } readonly #VERSION_NUMBERS: Array; - readonly #DOCKER_IMAGE: RedisServerDockerConfig; + readonly #DOCKER_IMAGE: RedisServerDockerOptions; constructor({ string, numbers }: Version, dockerImageName: string) { this.#VERSION_NUMBERS = numbers; this.#DOCKER_IMAGE = { image: dockerImageName, - version: string + version: string, + mode: "server" }; } @@ -179,7 +202,6 @@ export default class TestUtils { } isVersionGreaterThanHook(minimumVersion: Array | undefined): void { - const isVersionGreaterThanHook = this.isVersionGreaterThan.bind(this); const versionNumber = this.#VERSION_NUMBERS.join('.'); const minimumVersionString = minimumVersion?.join('.'); @@ -272,6 +294,81 @@ export default class TestUtils { }); } + testWithClientSentinel< + M extends RedisModules = {}, + F extends RedisFunctions = {}, + S extends RedisScripts = {}, + RESP extends RespVersions = 2, + TYPE_MAPPING extends TypeMapping = {} + >( + title: string, + fn: (sentinel: RedisSentinelType) => unknown, + options: SentinelTestOptions + ): void { + let dockerPromises: ReturnType; + + const passIndex = options.serverArguments.indexOf('--requirepass')+1; + let password: string | undefined = undefined; + if (passIndex != 0) { + password = options.serverArguments[passIndex]; + } + + if (this.isVersionGreaterThan(options.minimumDockerVersion)) { + const dockerImage = this.#DOCKER_IMAGE; + before(function () { + this.timeout(30000); + dockerPromises = spawnRedisSentinel(dockerImage, options.serverArguments); + return dockerPromises; + }); + } + + it(title, async function () { + this.timeout(30000); + if (options.skipTest) return this.skip(); + if (!dockerPromises) return this.skip(); + + + const promises = await dockerPromises; + const rootNodes: Array = promises.map(promise => ({ + host: "127.0.0.1", + port: promise.port + })); + + const sentinel = createSentinel({ + name: 'mymaster', + sentinelRootNodes: rootNodes, + nodeClientOptions: { + password: password || undefined, + }, + sentinelClientOptions: { + password: password || undefined, + }, + replicaPoolSize: options?.replicaPoolSize || 0, + scripts: options?.scripts || {}, + modules: options?.modules || {}, + functions: options?.functions || {}, + masterPoolSize: options?.masterPoolSize || undefined, + reserveClient: options?.reserveClient || false, + }) as RedisSentinelType; + + if (options.disableClientSetup) { + return fn(sentinel); + } + + await sentinel.connect(); + + try { + await sentinel.flushAll(); + await fn(sentinel); + } finally { + if (sentinel.isOpen) { + await sentinel.flushAll(); + sentinel.destroy(); + } + } + }); + } + testWithClientIfVersionWithinRange< M extends RedisModules = {}, F extends RedisFunctions = {}, @@ -290,8 +387,27 @@ export default class TestUtils { } else { console.warn(`Skipping test ${title} because server version ${this.#VERSION_NUMBERS.join('.')} is not within range ${range[0].join(".")} - ${range[1] !== 'LATEST' ? range[1].join(".") : 'LATEST'}`) } + } + testWithClienSentineltIfVersionWithinRange< + M extends RedisModules = {}, + F extends RedisFunctions = {}, + S extends RedisScripts = {}, + RESP extends RespVersions = 2, + TYPE_MAPPING extends TypeMapping = {} +>( + range: ([minVersion: Array, maxVersion: Array] | [minVersion: Array, 'LATEST']), + title: string, + fn: (sentinel: RedisSentinelType) => unknown, + options: SentinelTestOptions +): void { + + if (this.isVersionInRange(range[0], range[1] === 'LATEST' ? [Infinity, Infinity, Infinity] : range[1])) { + return this.testWithClientSentinel(`${title} [${range[0].join('.')}] - [${(range[1] === 'LATEST') ? range[1] : range[1].join(".")}] `, fn, options) + } else { + console.warn(`Skipping test ${title} because server version ${this.#VERSION_NUMBERS.join('.')} is not within range ${range[0].join(".")} - ${range[1] !== 'LATEST' ? range[1].join(".") : 'LATEST'}`) } +} testWithClientPool< M extends RedisModules = {}, From 9459660d960bb015b67d4bdf80f20672d85cb1ed Mon Sep 17 00:00:00 2001 From: Hristo Temelski Date: Wed, 30 Apr 2025 15:57:01 +0300 Subject: [PATCH 1534/1748] fix(pubsub): Fixed cluster client pubsub logic * Infer the cluster pubsub client read only mode from the node type * Modify flag logic --- packages/client/lib/cluster/cluster-slots.ts | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/packages/client/lib/cluster/cluster-slots.ts b/packages/client/lib/cluster/cluster-slots.ts index 3a4adff73c4..c2fde197f4f 100644 --- a/packages/client/lib/cluster/cluster-slots.ts +++ b/packages/client/lib/cluster/cluster-slots.ts @@ -518,7 +518,7 @@ export default class RedisClusterSlots< node = index < this.masters.length ? this.masters[index] : this.replicas[index - this.masters.length], - client = this.#createClient(node, true); + client = this.#createClient(node, index >= this.masters.length); this.pubSubNode = { address: node.address, From 49d6b2d465cf1e2aa5792bcd2bec5ff058c4545b Mon Sep 17 00:00:00 2001 From: "Bobby I." Date: Wed, 30 Apr 2025 16:28:22 +0300 Subject: [PATCH 1535/1748] Update README.MD (#2924) * Update README.MD * docs: update programmability.md examples + add Programmability section to README and * fix imports according to the new v5 exports * more v5 docs updates --------- Co-authored-by: Nikolay Karadzhov --- README.md | 259 +++++++++++++++++++++++++++++++++++++++- docs/RESP.md | 2 +- docs/programmability.md | 33 +++-- docs/v4-to-v5.md | 12 +- docs/v5.md | 65 +++++++++- 5 files changed, 341 insertions(+), 30 deletions(-) diff --git a/README.md b/README.md index 8e7e3845256..b9fe524c677 100644 --- a/README.md +++ b/README.md @@ -27,10 +27,10 @@ node-redis is a modern, high performance [Redis](https://redis.io) client for No ## Installation -Start a redis-server via docker (or any other method you prefer): +Start a redis via docker: ```bash -docker run -p 6379:6379 -it redis/redis-stack-server:latest +docker run -p 6379:6379 -d redis:8.0-rc1 ``` To install node-redis, simply: @@ -38,13 +38,13 @@ To install node-redis, simply: ```bash npm install redis ``` - -> "redis" is the "whole in one" package that includes all the other packages. If you only need a subset of the commands, you can install the individual packages. See the list below. +> "redis" is the "whole in one" package that includes all the other packages. If you only need a subset of the commands, +> you can install the individual packages. See the list below. ## Packages | Name | Description | -|------------------------------------------------|---------------------------------------------------------------------------------------------| +| ---------------------------------------------- | ------------------------------------------------------------------------------------------- | | [`redis`](./packages/redis) | The client with all the ["redis-stack"](https://github.com/redis-stack/redis-stack) modules | | [`@redis/client`](./packages/client) | The base clients (i.e `RedisClient`, `RedisCluster`, etc.) | | [`@redis/bloom`](./packages/bloom) | [Redis Bloom](https://redis.io/docs/data-types/probabilistic/) commands | @@ -53,7 +53,254 @@ npm install redis | [`@redis/time-series`](./packages/time-series) | [Redis Time-Series](https://redis.io/docs/data-types/timeseries/) commands | | [`@redis/entraid`](./packages/entraid) | Secure token-based authentication for Redis clients using Microsoft Entra ID | -> Looking for a high-level library to handle object mapping? See [redis-om-node](https://github.com/redis/redis-om-node)! +> Looking for a high-level library to handle object mapping? +> See [redis-om-node](https://github.com/redis/redis-om-node)! + + +## Usage + +### Basic Example + +```typescript +import { createClient } from "redis"; + +const client = await createClient() + .on("error", (err) => console.log("Redis Client Error", err)) + .connect(); + +await client.set("key", "value"); +const value = await client.get("key"); +client.destroy(); +``` + +The above code connects to localhost on port 6379. To connect to a different host or port, use a connection string in +the format `redis[s]://[[username][:password]@][host][:port][/db-number]`: + +```typescript +createClient({ + url: "redis://alice:foobared@awesome.redis.server:6380", +}); +``` + +You can also use discrete parameters, UNIX sockets, and even TLS to connect. Details can be found in +the [client configuration guide](./docs/client-configuration.md). + +To check if the the client is connected and ready to send commands, use `client.isReady` which returns a boolean. +`client.isOpen` is also available. This returns `true` when the client's underlying socket is open, and `false` when it +isn't (for example when the client is still connecting or reconnecting after a network error). + +### Redis Commands + +There is built-in support for all of the [out-of-the-box Redis commands](https://redis.io/commands). They are exposed +using the raw Redis command names (`HSET`, `HGETALL`, etc.) and a friendlier camel-cased version (`hSet`, `hGetAll`, +etc.): + +```typescript +// raw Redis commands +await client.HSET("key", "field", "value"); +await client.HGETALL("key"); + +// friendly JavaScript commands +await client.hSet("key", "field", "value"); +await client.hGetAll("key"); +``` + +Modifiers to commands are specified using a JavaScript object: + +```typescript +await client.set("key", "value", { + EX: 10, + NX: true, +}); +``` + +Replies will be transformed into useful data structures: + +```typescript +await client.hGetAll("key"); // { field1: 'value1', field2: 'value2' } +await client.hVals("key"); // ['value1', 'value2'] +``` + +`Buffer`s are supported as well: + +```typescript +const client = createClient().withTypeMapping({ + [RESP_TYPES.BLOB_STRING]: Buffer +}); + +await client.hSet("key", "field", Buffer.from("value")); // 'OK' +await client.hGet("key", "field"); // { field: } + +``` + +### Unsupported Redis Commands + +If you want to run commands and/or use arguments that Node Redis doesn't know about (yet!) use `.sendCommand()`: + +```typescript +await client.sendCommand(["SET", "key", "value", "NX"]); // 'OK' + +await client.sendCommand(["HGETALL", "key"]); // ['key1', 'field1', 'key2', 'field2'] +``` + +### Transactions (Multi/Exec) + +Start a [transaction](https://redis.io/topics/transactions) by calling `.multi()`, then chaining your commands. When +you're done, call `.exec()` and you'll get an array back with your results: + +```typescript +await client.set("another-key", "another-value"); + +const [setKeyReply, otherKeyValue] = await client + .multi() + .set("key", "value") + .get("another-key") + .exec(); // ['OK', 'another-value'] +``` + +You can also [watch](https://redis.io/topics/transactions#optimistic-locking-using-check-and-set) keys by calling +`.watch()`. Your transaction will abort if any of the watched keys change. + + +### Blocking Commands + +In v4, `RedisClient` had the ability to create a pool of connections using an "Isolation Pool" on top of the "main" +connection. However, there was no way to use the pool without a "main" connection: + +```javascript +const client = await createClient() + .on("error", (err) => console.error(err)) + .connect(); + +await client.ping(client.commandOptions({ isolated: true })); +``` + +In v5 we've extracted this pool logic into its own class—`RedisClientPool`: + +```javascript +const pool = await createClientPool() + .on("error", (err) => console.error(err)) + .connect(); + +await pool.ping(); +``` + + +### Pub/Sub + +See the [Pub/Sub overview](./docs/pub-sub.md). + +### Scan Iterator + +[`SCAN`](https://redis.io/commands/scan) results can be looped over +using [async iterators](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Symbol/asyncIterator): + +```typescript +for await (const key of client.scanIterator()) { + // use the key! + await client.get(key); +} +``` + +This works with `HSCAN`, `SSCAN`, and `ZSCAN` too: + +```typescript +for await (const { field, value } of client.hScanIterator("hash")) { +} +for await (const member of client.sScanIterator("set")) { +} +for await (const { score, value } of client.zScanIterator("sorted-set")) { +} +``` + +You can override the default options by providing a configuration object: + +```typescript +client.scanIterator({ + TYPE: "string", // `SCAN` only + MATCH: "patter*", + COUNT: 100, +}); +``` + +### Disconnecting + +The `QUIT` command has been deprecated in Redis 7.2 and should now also be considered deprecated in Node-Redis. Instead +of sending a `QUIT` command to the server, the client can simply close the network connection. + +`client.QUIT/quit()` is replaced by `client.close()`. and, to avoid confusion, `client.disconnect()` has been renamed to +`client.destroy()`. + +```typescript +client.destroy(); +``` + +### 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=="), +]); +``` + +### Programmability + +See the [Programmability overview](./docs/programmability.md). + +### Clustering + +Check out the [Clustering Guide](./docs/clustering.md) when using Node Redis to connect to a Redis Cluster. + +### Events + +The Node Redis client class is an Nodejs EventEmitter and it emits an event each time the network status changes: + +| Name | When | Listener arguments | +| ----------------------- | ---------------------------------------------------------------------------------- | --------------------------------------------------------- | +| `connect` | Initiating a connection to the server | _No arguments_ | +| `ready` | Client is ready to use | _No arguments_ | +| `end` | Connection has been closed (via `.disconnect()`) | _No arguments_ | +| `error` | An error has occurred—usually a network issue such as "Socket closed unexpectedly" | `(error: Error)` | +| `reconnecting` | Client is trying to reconnect to the server | _No arguments_ | +| `sharded-channel-moved` | See [here](./docs/pub-sub.md#sharded-channel-moved-event) | See [here](./docs/pub-sub.md#sharded-channel-moved-event) | + +> :warning: You **MUST** listen to `error` events. If a client doesn't have at least one `error` listener registered and +> an `error` occurs, that error will be thrown and the Node.js process will exit. See the [ > `EventEmitter` docs](https://nodejs.org/api/events.html#events_error_events) for more details. + +> The client will not emit [any other events](./docs/v3-to-v4.md#all-the-removed-events) beyond those listed above. + +## Supported Redis versions + +Node Redis is supported with the following versions of Redis: + +| Version | Supported | +| ------- | ------------------ | +| 8.0.z | :heavy_check_mark: | +| 7.0.z | :heavy_check_mark: | +| 6.2.z | :heavy_check_mark: | +| 6.0.z | :heavy_check_mark: | +| 5.0.z | :heavy_check_mark: | +| < 5.0 | :x: | + +> Node Redis should work with older versions of Redis, but it is not fully tested and we cannot offer support. + +## Migration + +- [From V3 to V4](docs/v3-to-v4.md) +- [From V4 to V5](docs/v4-to-v5.md) +- [V5](docs/v5.md) ## Contributing diff --git a/docs/RESP.md b/docs/RESP.md index 5d634831e17..f8c2388226b 100644 --- a/docs/RESP.md +++ b/docs/RESP.md @@ -23,7 +23,7 @@ By default, each type is mapped to the first option in the lists below. To chang - Double (`,`) => `number | string` - Simple String (`+`) => `string | Buffer` - Blob String (`$`) => `string | Buffer` -- Verbatim String (`=`) => `string | Buffer | VerbatimString` (TODO: `VerbatimString` typedoc link) +- Verbatim String (`=`) => `string | Buffer | VerbatimString` - Simple Error (`-`) => `ErrorReply` - Blob Error (`!`) => `ErrorReply` - Array (`*`) => `Array` diff --git a/docs/programmability.md b/docs/programmability.md index f6ae42033c6..56eb048ca0c 100644 --- a/docs/programmability.md +++ b/docs/programmability.md @@ -25,16 +25,22 @@ FUNCTION LOAD "#!lua name=library\nredis.register_function{function_name='add', Load the prior redis function on the _redis server_ before running the example below. ```typescript -import { createClient } from 'redis'; +import { CommandParser } from '@redis/client/lib/client/parser'; +import { NumberReply } from '@redis/client/lib/RESP/types'; +import { createClient, RedisArgument } from 'redis'; const client = createClient({ functions: { library: { add: { NUMBER_OF_KEYS: 1, - FIRST_KEY_INDEX: 1, - transformArguments(key: string, toAdd: number): Array { - return [key, toAdd.toString()]; + parseCommand( + parser: CommandParser, + key: RedisArgument, + toAdd: RedisArgument + ) { + parser.pushKey(key) + parser.push(toAdd) }, transformReply: undefined as unknown as () => NumberReply } @@ -43,9 +49,8 @@ const client = createClient({ }); await client.connect(); - await client.set('key', '1'); -await client.library.add('key', 2); // 3 +await client.library.add('key', '2'); // 3 ``` ## [Lua Scripts](https://redis.io/docs/manual/programmability/eval-intro/) @@ -53,7 +58,9 @@ await client.library.add('key', 2); // 3 The following is an end-to-end example of the prior concept. ```typescript -import { createClient, defineScript, NumberReply } from 'redis'; +import { CommandParser } from '@redis/client/lib/client/parser'; +import { NumberReply } from '@redis/client/lib/RESP/types'; +import { createClient, defineScript, RedisArgument } from 'redis'; const client = createClient({ scripts: { @@ -61,8 +68,13 @@ const client = createClient({ SCRIPT: 'return redis.call("GET", KEYS[1]) + ARGV[1];', NUMBER_OF_KEYS: 1, FIRST_KEY_INDEX: 1, - transformArguments(key: string, toAdd: number): Array { - return [key, toAdd.toString()]; + parseCommand( + parser: CommandParser, + key: RedisArgument, + toAdd: RedisArgument + ) { + parser.pushKey(key) + parser.push(toAdd) }, transformReply: undefined as unknown as () => NumberReply }) @@ -70,7 +82,6 @@ const client = createClient({ }); await client.connect(); - await client.set('key', '1'); -await client.add('key', 2); // 3 +await client.add('key', '2'); // 3 ``` diff --git a/docs/v4-to-v5.md b/docs/v4-to-v5.md index 3b09658b66f..fbe02e7c4d6 100644 --- a/docs/v4-to-v5.md +++ b/docs/v4-to-v5.md @@ -234,12 +234,12 @@ In v5, any unwritten commands (in the same pipeline) will be discarded. ### Time Series - `TS.ADD`: `boolean` -> `number` [^boolean-to-number] -- `TS.[M][REV]RANGE`: `enum TimeSeriesBucketTimestamp` -> `const TIME_SERIES_BUCKET_TIMESTAMP` [^enum-to-constants], `enum TimeSeriesReducers` -> `const TIME_SERIES_REDUCERS` [^enum-to-constants], the `ALIGN` argument has been moved into `AGGREGRATION` +- `TS.[M][REV]RANGE`: the `ALIGN` argument has been moved into `AGGREGATION` - `TS.SYNUPDATE`: `Array>` -> `Record>` -- `TS.M[REV]RANGE[_WITHLABELS]`, `TS.MGET[_WITHLABELS]`: TODO - -[^enum-to-constants]: TODO +- `TimeSeriesDuplicatePolicies` -> `TIME_SERIES_DUPLICATE_POLICIES` [^enum-to-constants] +- `TimeSeriesEncoding` -> `TIME_SERIES_ENCODING` [^enum-to-constants] +- `TimeSeriesAggregationType` -> `TIME_SERIES_AGGREGATION_TYPE` [^enum-to-constants] +- `TimeSeriesReducers` -> `TIME_SERIES_REDUCERS` [^enum-to-constants] +- `TimeSeriesBucketTimestamp` -> `TIME_SERIES_BUCKET_TIMESTAMP` [^enum-to-constants] [^map-keys]: To avoid unnecessary transformations and confusion, map keys will not be transformed to "js friendly" names (i.e. `number-of-keys` will not be renamed to `numberOfKeys`). See [here](https://github.com/redis/node-redis/discussions/2506). - -[^future-proofing]: TODO diff --git a/docs/v5.md b/docs/v5.md index 4a1bd817b9b..a3d0ab68389 100644 --- a/docs/v5.md +++ b/docs/v5.md @@ -1,30 +1,83 @@ # RESP3 Support -TODO +Node Redis v5 adds support for [RESP3](https://github.com/redis/redis-specifications/blob/master/protocol/RESP3.md), the new Redis serialization protocol. RESP3 offers richer data types and improved type handling compared to RESP2. + +To use RESP3, specify it when creating your client: ```javascript +import { createClient } from 'redis'; + const client = createClient({ RESP: 3 }); ``` +## Type Mapping + +With RESP3, you can leverage the protocol's richer type system. You can customize how different Redis types are represented in JavaScript using type mapping: + ```javascript -// by default +import { createClient, RESP_TYPES } from 'redis'; + +// By default await client.hGetAll('key'); // Record +// Use Map instead of plain object await client.withTypeMapping({ - [TYPES.MAP]: Map + [RESP_TYPES.MAP]: Map }).hGetAll('key'); // Map +// Use both Map and Buffer await client.withTypeMapping({ - [TYPES.MAP]: Map, - [TYPES.BLOB_STRING]: Buffer + [RESP_TYPES.MAP]: Map, + [RESP_TYPES.BLOB_STRING]: Buffer }).hGetAll('key'); // Map ``` +This replaces the previous approach of using `commandOptions({ returnBuffers: true })` in v4. + +## PubSub in RESP3 + +RESP3 uses a different mechanism for handling Pub/Sub messages. Instead of modifying the `onReply` handler as in RESP2, RESP3 provides a dedicated `onPush` handler. When using RESP3, the client automatically uses this more efficient push notification system. + +## Known Limitations + +### Unstable Module Commands + +Some Redis module commands have unstable RESP3 transformations. These commands will throw an error when used with RESP3 unless you explicitly opt in to using them by setting `unstableResp3: true` in your client configuration: + +```javascript +const client = createClient({ + RESP: 3, + unstableResp3: true +}); +``` + +The following commands have unstable RESP3 implementations: + +1. **Stream Commands**: + - `XREAD` and `XREADGROUP` - The response format differs between RESP2 and RESP3 + +2. **Search Commands (RediSearch)**: + - `FT.AGGREGATE` + - `FT.AGGREGATE_WITHCURSOR` + - `FT.CURSOR_READ` + - `FT.INFO` + - `FT.PROFILE_AGGREGATE` + - `FT.PROFILE_SEARCH` + - `FT.SEARCH` + - `FT.SEARCH_NOCONTENT` + - `FT.SPELLCHECK` + +3. **Time Series Commands**: + - `TS.INFO` + - `TS.INFO_DEBUG` + +If you need to use these commands with RESP3, be aware that the response format might change in future versions. + # Sentinel Support -[TODO](./sentinel.md) +[Sentinel](./sentinel.md) # `multi.exec<'typed'>` / `multi.execTyped` From 46bfeaa94e8987216fcfd554c8ef7c5159480429 Mon Sep 17 00:00:00 2001 From: "Bobby I." Date: Wed, 30 Apr 2025 16:30:16 +0300 Subject: [PATCH 1536/1748] Fix typo and improve Sentinel docs (#2931) --- docs/sentinel.md | 31 +++++---- packages/client/lib/sentinel/index.spec.ts | 38 +++++------ packages/client/lib/sentinel/index.ts | 75 +++++++++++++++++++++- packages/client/lib/sentinel/types.ts | 10 ++- 4 files changed, 118 insertions(+), 36 deletions(-) diff --git a/docs/sentinel.md b/docs/sentinel.md index 80e79c3f88c..f10b2953df5 100644 --- a/docs/sentinel.md +++ b/docs/sentinel.md @@ -14,7 +14,7 @@ const sentinel = await createSentinel({ port: 1234 }] }) - .on('error', err => console.error('Redis Sentinel Error', err)); + .on('error', err => console.error('Redis Sentinel Error', err)) .connect(); await sentinel.set('key', 'value'); @@ -26,16 +26,19 @@ In the above example, we configure the sentinel object to fetch the configuratio ## `createSentinel` configuration -| Property | Default | Description | -|-----------------------|---------|-----------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------| -| name | | The sentinel identifier for a particular database cluster | -| sentinelRootNodes | | An array of root nodes that are part of the sentinel cluster, which will be used to get the topology. Each element in the array is a client configuration object. There is no need to specify every node in the cluster: 3 should be enough to reliably connect and obtain the sentinel configuration from the server | -| maxCommandRediscovers | `16` | The maximum number of times a command will retry due to topology changes. | -| nodeClientOptions | | The configuration values for every node in the cluster. Use this for example when specifying an ACL user to connect with | -| sentinelClientOptions | | The configuration values for every sentinel in the cluster. Use this for example when specifying an ACL user to connect with | -| masterPoolSize | `1` | The number of clients connected to the master node | -| replicaPoolSize | `0` | The number of clients connected to each replica node. When greater than 0, the client will distribute the load by executing read-only commands (such as `GET`, `GEOSEARCH`, etc.) across all the cluster nodes. | -| reserveClient | `false` | When `true`, one client will be reserved for the sentinel object. When `false`, the sentinel object will wait for the first available client from the pool. | +| Property | Default | Description | +|----------------------------|-----------|-----------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------| +| name | | The sentinel identifier for a particular database cluster | +| sentinelRootNodes | | An array of root nodes that are part of the sentinel cluster, which will be used to get the topology. Each element in the array is a client configuration object. There is no need to specify every node in the cluster: 3 should be enough to reliably connect and obtain the sentinel configuration from the server | +| maxCommandRediscovers | `16` | The maximum number of times a command will retry due to topology changes. | +| nodeClientOptions | | The configuration values for every node in the cluster. Use this for example when specifying an ACL user to connect with | +| sentinelClientOptions | | The configuration values for every sentinel in the cluster. Use this for example when specifying an ACL user to connect with | +| masterPoolSize | `1` | The number of clients connected to the master node | +| replicaPoolSize | `0` | The number of clients connected to each replica node. When greater than 0, the client will distribute the load by executing read-only commands (such as `GET`, `GEOSEARCH`, etc.) across all the cluster nodes. | +| scanInterval | `10000` | Interval in milliseconds to periodically scan for changes in the sentinel topology. The client will query the sentinel for changes at this interval. | +| passthroughClientErrorEvents | `false` | When `true`, error events from client instances inside the sentinel will be propagated to the sentinel instance. This allows handling all client errors through a single error handler on the sentinel instance. | +| reserveClient | `false` | When `true`, one client will be reserved for the sentinel object. When `false`, the sentinel object will wait for the first available client from the pool. | + ## PubSub It supports PubSub via the normal mechanisms, including migrating the listeners if the node they are connected to goes down. @@ -60,7 +63,7 @@ createSentinel({ }); ``` -In addition, it also provides the ability have a pool of clients connected to the replica nodes, and to direct all read-only commands to them: +In addition, it also provides the ability have a pool of clients connected to the replica nodes, and to direct all read-only commands to them: ```javascript createSentinel({ @@ -85,9 +88,9 @@ const result = await sentinel.use(async client => { }); ``` -`.getMasterClientLease()` +`.acquire()` ```javascript -const clientLease = await sentinel.getMasterClientLease(); +const clientLease = await sentinel.acquire(); try { await clientLease.watch('key'); diff --git a/packages/client/lib/sentinel/index.spec.ts b/packages/client/lib/sentinel/index.spec.ts index 567da4b1a50..cf9228c261f 100644 --- a/packages/client/lib/sentinel/index.spec.ts +++ b/packages/client/lib/sentinel/index.spec.ts @@ -134,7 +134,7 @@ describe(`test with scripts`, () => { }, GLOBAL.SENTINEL.WITH_SCRIPT); testUtils.testWithClientSentinel('with script multi', async sentinel => { - const reply = await sentinel.multi().set('key', 2).square('key').exec(); + const reply = await sentinel.multi().set('key', 2).square('key').exec(); assert.deepEqual(reply, ['OK', 4]); }, GLOBAL.SENTINEL.WITH_SCRIPT); @@ -148,7 +148,7 @@ describe(`test with scripts`, () => { ); }, GLOBAL.SENTINEL.WITH_SCRIPT) }); - + describe(`test with functions`, () => { testUtils.testWithClientSentinel('with function', async sentinel => { @@ -178,14 +178,14 @@ describe(`test with functions`, () => { MATH_FUNCTION.code, { REPLACE: true } ); - + const reply = await sentinel.use( async (client: any) => { await client.set('key', '2'); return client.math.square('key'); } ); - + assert.equal(reply, 4); }, GLOBAL.SENTINEL.WITH_FUNCTION); }); @@ -216,7 +216,7 @@ describe(`test with replica pool size 1`, () => { testUtils.testWithClientSentinel('client lease', async sentinel => { sentinel.on("error", () => { }); - const clientLease = await sentinel.aquire(); + const clientLease = await sentinel.acquire(); clientLease.set('x', 456); let matched = false; @@ -243,7 +243,7 @@ describe(`test with replica pool size 1`, () => { return await client.get("x"); } ) - + await sentinel.set("x", 1); assert.equal(await promise, null); }, GLOBAL.SENTINEL.WITH_REPLICA_POOL_SIZE_1); @@ -276,7 +276,7 @@ describe(`test with masterPoolSize 2, reserve client true`, () => { assert.equal(await promise2, "2"); }, Object.assign(GLOBAL.SENTINEL.WITH_RESERVE_CLIENT_MASTER_POOL_SIZE_2, {skipTest: true})); }); - + describe(`test with masterPoolSize 2`, () => { testUtils.testWithClientSentinel('multple clients', async sentinel => { sentinel.on("error", () => { }); @@ -313,14 +313,14 @@ describe(`test with masterPoolSize 2`, () => { }, GLOBAL.SENTINEL.WITH_MASTER_POOL_SIZE_2); testUtils.testWithClientSentinel('lease - watch - clean', async sentinel => { - const leasedClient = await sentinel.aquire(); + const leasedClient = await sentinel.acquire(); await leasedClient.set('x', 1); await leasedClient.watch('x'); assert.deepEqual(await leasedClient.multi().get('x').exec(), ['1']) }, GLOBAL.SENTINEL.WITH_MASTER_POOL_SIZE_2); testUtils.testWithClientSentinel('lease - watch - dirty', async sentinel => { - const leasedClient = await sentinel.aquire(); + const leasedClient = await sentinel.acquire(); await leasedClient.set('x', 1); await leasedClient.watch('x'); await leasedClient.set('x', 2); @@ -328,11 +328,11 @@ describe(`test with masterPoolSize 2`, () => { await assert.rejects(leasedClient.multi().get('x').exec(), new WatchError()); }, GLOBAL.SENTINEL.WITH_MASTER_POOL_SIZE_2); }); - + // TODO: Figure out how to modify the test utils // so it would have fine grained controll over -// sentinel +// sentinel // it should somehow replicate the `SentinelFramework` object functionallities async function steadyState(frame: SentinelFramework) { let checkedMaster = false; @@ -439,7 +439,7 @@ describe.skip('legacy tests', () => { sentinel.on('error', () => { }); } - if (this!.currentTest!.state === 'failed') { + if (this!.currentTest!.state === 'failed') { console.log(`longest event loop blocked delta: ${longestDelta}`); console.log(`longest event loop blocked in failing test: ${longestTestDelta}`); console.log("trace:"); @@ -454,7 +454,7 @@ describe.skip('legacy tests', () => { frame.sentinelMaster(), frame.sentinelReplicas() ]) - + console.log(`sentinel sentinels:\n${JSON.stringify(results[0], undefined, '\t')}`); console.log(`sentinel master:\n${JSON.stringify(results[1], undefined, '\t')}`); console.log(`sentinel replicas:\n${JSON.stringify(results[2], undefined, '\t')}`); @@ -492,7 +492,7 @@ describe.skip('legacy tests', () => { ); }); - // stops master to force sentinel to update + // stops master to force sentinel to update it('stop master', async function () { this.timeout(60000); @@ -538,8 +538,8 @@ describe.skip('legacy tests', () => { tracer.push("connected"); - const client = await sentinel.aquire(); - tracer.push("aquired lease"); + const client = await sentinel.acquire(); + tracer.push("acquired lease"); await client.set("x", 1); await client.watch("x"); @@ -586,7 +586,7 @@ describe.skip('legacy tests', () => { await sentinel.connect(); tracer.push("connected"); - const client = await sentinel.aquire(); + const client = await sentinel.acquire(); tracer.push("got leased client"); await client.set("x", 1); await client.watch("x"); @@ -965,10 +965,10 @@ describe.skip('legacy tests', () => { tracer.push("adding node"); await frame.addNode(); tracer.push("added node and waiting on added promise"); - await nodeAddedPromise; + await nodeAddedPromise; }) }); }); - + diff --git a/packages/client/lib/sentinel/index.ts b/packages/client/lib/sentinel/index.ts index 73c4fffd070..3bf94abd819 100644 --- a/packages/client/lib/sentinel/index.ts +++ b/packages/client/lib/sentinel/index.ts @@ -32,14 +32,30 @@ export class RedisSentinelClient< #internal: RedisSentinelInternal; readonly _self: RedisSentinelClient; + /** + * Indicates if the client connection is open + * + * @returns `true` if the client connection is open, `false` otherwise + */ + get isOpen() { return this._self.#internal.isOpen; } + /** + * Indicates if the client connection is ready to accept commands + * + * @returns `true` if the client connection is ready, `false` otherwise + */ get isReady() { return this._self.#internal.isReady; } + /** + * Gets the command options configured for this client + * + * @returns The command options for this client or `undefined` if none were set + */ get commandOptions() { return this._self.#commandOptions; } @@ -222,6 +238,16 @@ export class RedisSentinelClient< unwatch = this.UNWATCH; + /** + * Releases the client lease back to the pool + * + * After calling this method, the client instance should no longer be used as it + * will be returned to the client pool and may be given to other operations. + * + * @returns A promise that resolves when the client is ready to be reused, or undefined + * if the client was immediately ready + * @throws Error if the lease has already been released + */ release() { if (this._self.#clientInfo === undefined) { throw new Error('RedisSentinelClient lease already released'); @@ -245,10 +271,20 @@ export default class RedisSentinel< #internal: RedisSentinelInternal; #options: RedisSentinelOptions; + /** + * Indicates if the sentinel connection is open + * + * @returns `true` if the sentinel connection is open, `false` otherwise + */ get isOpen() { return this._self.#internal.isOpen; } + /** + * Indicates if the sentinel connection is ready to accept commands + * + * @returns `true` if the sentinel connection is ready, `false` otherwise + */ get isReady() { return this._self.#internal.isReady; } @@ -511,7 +547,28 @@ export default class RedisSentinel< pUnsubscribe = this.PUNSUBSCRIBE; - async aquire(): Promise> { + /** + * Acquires a master client lease for exclusive operations + * + * Used when multiple commands need to run on an exclusive client (for example, using `WATCH/MULTI/EXEC`). + * The returned client must be released after use with the `release()` method. + * + * @returns A promise that resolves to a Redis client connected to the master node + * @example + * ```javascript + * const clientLease = await sentinel.acquire(); + * + * try { + * await clientLease.watch('key'); + * const resp = await clientLease.multi() + * .get('key') + * .exec(); + * } finally { + * clientLease.release(); + * } + * ``` + */ + async acquire(): Promise> { const clientInfo = await this._self.#internal.getClientLease(); return RedisSentinelClient.create(this._self.#options, this._self.#internal, clientInfo, this._self.#commandOptions); } @@ -641,6 +698,12 @@ class RedisSentinelInternal< }); } + /** + * Gets a client lease from the master client pool + * + * @returns A client info object or a promise that resolves to a client info object + * when a client becomes available + */ getClientLease(): ClientInfo | Promise { const id = this.#masterClientQueue.shift(); if (id !== undefined) { @@ -650,6 +713,16 @@ class RedisSentinelInternal< return this.#masterClientQueue.wait().then(id => ({ id })); } + /** + * Releases a client lease back to the pool + * + * If the client was used for a transaction that might have left it in a dirty state, + * it will be reset before being returned to the pool. + * + * @param clientInfo The client info object representing the client to release + * @returns A promise that resolves when the client is ready to be reused, or undefined + * if the client was immediately ready or no longer exists + */ releaseClientLease(clientInfo: ClientInfo) { const client = this.#masterClients[clientInfo.id]; // client can be undefined if releasing in middle of a reconfigure diff --git a/packages/client/lib/sentinel/types.ts b/packages/client/lib/sentinel/types.ts index 428e7bccd66..28a5a91ddd3 100644 --- a/packages/client/lib/sentinel/types.ts +++ b/packages/client/lib/sentinel/types.ts @@ -49,11 +49,17 @@ export interface RedisSentinelOptions< */ replicaPoolSize?: number; /** - * TODO + * Interval in milliseconds to periodically scan for changes in the sentinel topology. + * The client will query the sentinel for changes at this interval. + * + * Default: 10000 (10 seconds) */ scanInterval?: number; /** - * TODO + * When `true`, error events from client instances inside the sentinel will be propagated to the sentinel instance. + * This allows handling all client errors through a single error handler on the sentinel instance. + * + * Default: false */ passthroughClientErrorEvents?: boolean; /** From 4022e6947d23c3938c62578eae3b3d5b71cc5c0e Mon Sep 17 00:00:00 2001 From: Nikolay Karadzhov Date: Wed, 30 Apr 2025 16:50:35 +0300 Subject: [PATCH 1537/1748] update package-lock.json (#2932) --- package-lock.json | 26 ++++++++++++++++++++++---- 1 file changed, 22 insertions(+), 4 deletions(-) diff --git a/package-lock.json b/package-lock.json index 064b1158f50..dee35a0fcec 100644 --- a/package-lock.json +++ b/package-lock.json @@ -8,9 +8,6 @@ "workspaces": [ "./packages/*" ], - "dependencies": { - "ts-node": "^10.9.2" - }, "devDependencies": { "@istanbuljs/nyc-config-typescript": "^1.0.2", "@types/mocha": "^10.0.6", @@ -19,6 +16,7 @@ "mocha": "^10.2.0", "nyc": "^15.1.0", "release-it": "^17.0.3", + "ts-node": "^10.9.2", "tsx": "^4.7.0", "typedoc": "^0.25.7", "typescript": "^5.3.3" @@ -671,6 +669,7 @@ "version": "0.8.1", "resolved": "https://registry.npmjs.org/@cspotcode/source-map-support/-/source-map-support-0.8.1.tgz", "integrity": "sha512-IchNf6dN4tHoMFIn/7OE8LWZ19Y6q/67Bmf6vnGREv8RSbBVb9LPJxEcnwrcwX6ixSvaiGoomAUvu4YSxXrVgw==", + "dev": true, "license": "MIT", "dependencies": { "@jridgewell/trace-mapping": "0.3.9" @@ -683,6 +682,7 @@ "version": "0.3.9", "resolved": "https://registry.npmjs.org/@jridgewell/trace-mapping/-/trace-mapping-0.3.9.tgz", "integrity": "sha512-3Belt6tdc8bPgAtbcmdtNJlirVoTmEb5e2gC94PnkwEW9jI6CAHUeoG85tjWP5WquqfavoMtMwiG4P926ZKKuQ==", + "dev": true, "license": "MIT", "dependencies": { "@jridgewell/resolve-uri": "^3.0.3", @@ -829,6 +829,7 @@ }, "node_modules/@jridgewell/resolve-uri": { "version": "3.1.1", + "dev": true, "license": "MIT", "engines": { "node": ">=6.0.0" @@ -844,6 +845,7 @@ }, "node_modules/@jridgewell/sourcemap-codec": { "version": "1.4.15", + "dev": true, "license": "MIT" }, "node_modules/@jridgewell/trace-mapping": { @@ -1187,24 +1189,28 @@ "version": "1.0.11", "resolved": "https://registry.npmjs.org/@tsconfig/node10/-/node10-1.0.11.tgz", "integrity": "sha512-DcRjDCujK/kCk/cUe8Xz8ZSpm8mS3mNNpta+jGCA6USEDfktlNvm1+IuZ9eTcDbNk41BHwpHHeW+N1lKCz4zOw==", + "dev": true, "license": "MIT" }, "node_modules/@tsconfig/node12": { "version": "1.0.11", "resolved": "https://registry.npmjs.org/@tsconfig/node12/-/node12-1.0.11.tgz", "integrity": "sha512-cqefuRsh12pWyGsIoBKJA9luFu3mRxCA+ORZvA4ktLSzIuCUtWVxGIuXigEwO5/ywWFMZ2QEGKWvkZG1zDMTag==", + "dev": true, "license": "MIT" }, "node_modules/@tsconfig/node14": { "version": "1.0.3", "resolved": "https://registry.npmjs.org/@tsconfig/node14/-/node14-1.0.3.tgz", "integrity": "sha512-ysT8mhdixWK6Hw3i1V2AeRqZ5WfXg1G43mqoYlM2nc6388Fq5jcXyr5mRsqViLx/GJYdoL0bfXD8nmF+Zn/Iow==", + "dev": true, "license": "MIT" }, "node_modules/@tsconfig/node16": { "version": "1.0.4", "resolved": "https://registry.npmjs.org/@tsconfig/node16/-/node16-1.0.4.tgz", "integrity": "sha512-vxhUy4J8lyeyinH7Azl1pdd43GJhZH/tP2weN8TntQblOY+A0XbT8DJk1/oCPuOOyg/Ja757rG0CgHcWC8OfMA==", + "dev": true, "license": "MIT" }, "node_modules/@types/body-parser": { @@ -1290,6 +1296,7 @@ }, "node_modules/@types/node": { "version": "20.11.16", + "dev": true, "license": "MIT", "dependencies": { "undici-types": "~5.26.4" @@ -1376,6 +1383,7 @@ "version": "8.14.1", "resolved": "https://registry.npmjs.org/acorn/-/acorn-8.14.1.tgz", "integrity": "sha512-OvQ/2pUDKmgfCg++xsTX1wGxfTaszcHVcTctW4UJB4hibJx2HXxxO5UmVgyjMa+ZDsiaf5wWLXYpRWMmBI0QHg==", + "dev": true, "license": "MIT", "bin": { "acorn": "bin/acorn" @@ -1388,6 +1396,7 @@ "version": "8.3.4", "resolved": "https://registry.npmjs.org/acorn-walk/-/acorn-walk-8.3.4.tgz", "integrity": "sha512-ueEepnujpqee2o5aIYnvHU6C0A42MNdsIDeqy5BydrkuC5R1ZuUFnm27EeFJGoEHJQgn3uleRvmTXaJgfXbt4g==", + "dev": true, "license": "MIT", "dependencies": { "acorn": "^8.11.0" @@ -1518,6 +1527,7 @@ "version": "4.1.3", "resolved": "https://registry.npmjs.org/arg/-/arg-4.1.3.tgz", "integrity": "sha512-58S9QDqG0Xx27YwPSt9fJxivjYl432YCwfDMfZ+71RAqUrZef7LrKQZ3LHLOwCS4FLNBplP533Zx895SeOCHvA==", + "dev": true, "license": "MIT" }, "node_modules/argparse": { @@ -2391,6 +2401,7 @@ "version": "1.1.1", "resolved": "https://registry.npmjs.org/create-require/-/create-require-1.1.1.tgz", "integrity": "sha512-dcKFX3jn0MpIaXjisoRvexIJVEKzaq7z2rZKxf+MSr9TkdmHmsU4m2lcLojrj/FHl8mk5VxMmYA+ftRkP/3oKQ==", + "dev": true, "license": "MIT" }, "node_modules/cross-spawn": { @@ -5170,6 +5181,7 @@ "version": "1.3.6", "resolved": "https://registry.npmjs.org/make-error/-/make-error-1.3.6.tgz", "integrity": "sha512-s8UhlNe7vPKomQhC1qFelMokr/Sc3AgNbso3n74mVPA5LTZwkB9NlXf4XPamLxJE8h0gh73rM94xvwRT2CVInw==", + "dev": true, "license": "ISC" }, "node_modules/marked": { @@ -7664,6 +7676,7 @@ "version": "10.9.2", "resolved": "https://registry.npmjs.org/ts-node/-/ts-node-10.9.2.tgz", "integrity": "sha512-f0FFpIdcHgn8zcPSbf1dRevwt047YMnaiJM3u2w2RewrB+fob/zePZcrOyQoLMMO7aBIddLcQIEK5dYjkLnGrQ==", + "dev": true, "license": "MIT", "dependencies": { "@cspotcode/source-map-support": "^0.8.0", @@ -7707,6 +7720,7 @@ "version": "4.0.2", "resolved": "https://registry.npmjs.org/diff/-/diff-4.0.2.tgz", "integrity": "sha512-58lmxKSA4BNyLz+HHMUzlOEpg09FV+ev6ZMe3vJihgdxzgcwZ8VoEEPmALCZG9LmqfVoNMMKpttIYTVG6uDY7A==", + "dev": true, "license": "BSD-3-Clause", "engines": { "node": ">=0.3.1" @@ -7877,6 +7891,7 @@ }, "node_modules/typescript": { "version": "5.3.3", + "dev": true, "license": "Apache-2.0", "bin": { "tsc": "bin/tsc", @@ -7915,6 +7930,7 @@ }, "node_modules/undici-types": { "version": "5.26.5", + "dev": true, "license": "MIT" }, "node_modules/unicorn-magic": { @@ -8094,6 +8110,7 @@ "version": "3.0.1", "resolved": "https://registry.npmjs.org/v8-compile-cache-lib/-/v8-compile-cache-lib-3.0.1.tgz", "integrity": "sha512-wa7YjyUGfNZngI/vtK0UHAN+lgDCxBPCylVXGp0zu59Fz5aiGtNXaq3DhIov063MorB+VfufLh3JlF2KdTK3xg==", + "dev": true, "license": "MIT" }, "node_modules/vary": { @@ -8468,6 +8485,7 @@ "version": "3.1.1", "resolved": "https://registry.npmjs.org/yn/-/yn-3.1.1.tgz", "integrity": "sha512-Ux4ygGWsu2c7isFWe8Yu1YluJmqVhxqK2cLXNQA5AcC3QfbGNpM7fu0Y8b/z16pXLnFxZYvWhd3fhBY9DLmC6Q==", + "dev": true, "license": "MIT", "engines": { "node": ">=6" @@ -8535,7 +8553,7 @@ "version": "5.0.0-next.7", "license": "MIT", "dependencies": { - "@azure/identity": "4.7.0", + "@azure/identity": "^4.7.0", "@azure/msal-node": "^2.16.1" }, "devDependencies": { From bf2b3752d6697f8d2152d2162be8d86c8890d7eb Mon Sep 17 00:00:00 2001 From: Nikolay Karadzhov Date: Wed, 30 Apr 2025 16:56:53 +0300 Subject: [PATCH 1538/1748] Release client@5.0.0 --- packages/client/package.json | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/packages/client/package.json b/packages/client/package.json index 3372f3a5758..5b88c185da9 100644 --- a/packages/client/package.json +++ b/packages/client/package.json @@ -1,6 +1,6 @@ { "name": "@redis/client", - "version": "5.0.0-next.7", + "version": "5.0.0", "license": "MIT", "main": "./dist/index.js", "types": "./dist/index.d.ts", From 091244a32a8f983b6678699f409687563c4e3796 Mon Sep 17 00:00:00 2001 From: Nikolay Karadzhov Date: Wed, 30 Apr 2025 17:00:26 +0300 Subject: [PATCH 1539/1748] Updated the Bloom package to use client@5.0.0 --- packages/bloom/package.json | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/packages/bloom/package.json b/packages/bloom/package.json index d913d434a15..a1916660749 100644 --- a/packages/bloom/package.json +++ b/packages/bloom/package.json @@ -12,7 +12,7 @@ "test": "nyc -r text-summary -r lcov mocha -r tsx './lib/**/*.spec.ts'" }, "peerDependencies": { - "@redis/client": "^5.0.0-next.7" + "@redis/client": "^5.0.0" }, "devDependencies": { "@redis/test-utils": "*" From bd10c92348e8b5592ea64ca7e11ea741834f7b04 Mon Sep 17 00:00:00 2001 From: Nikolay Karadzhov Date: Wed, 30 Apr 2025 17:04:03 +0300 Subject: [PATCH 1540/1748] Release bloom@5.0.0 --- packages/bloom/package.json | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/packages/bloom/package.json b/packages/bloom/package.json index a1916660749..595e581ad8e 100644 --- a/packages/bloom/package.json +++ b/packages/bloom/package.json @@ -1,6 +1,6 @@ { "name": "@redis/bloom", - "version": "5.0.0-next.7", + "version": "5.0.0", "license": "MIT", "main": "./dist/lib/index.js", "types": "./dist/lib/index.d.ts", From 42911295a4710634b357cc8244f837014b770707 Mon Sep 17 00:00:00 2001 From: Nikolay Karadzhov Date: Wed, 30 Apr 2025 17:05:10 +0300 Subject: [PATCH 1541/1748] Updated the Entraid package to use client@5.0.0 --- packages/entraid/package.json | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/packages/entraid/package.json b/packages/entraid/package.json index 4eba7e0a788..7c61a729ba9 100644 --- a/packages/entraid/package.json +++ b/packages/entraid/package.json @@ -21,7 +21,7 @@ "@azure/msal-node": "^2.16.1" }, "peerDependencies": { - "@redis/client": "^5.0.0-next.7" + "@redis/client": "^5.0.0" }, "devDependencies": { "@types/express": "^4.17.21", From 99003307f986bcc6b6e77149af61e62b4d02132d Mon Sep 17 00:00:00 2001 From: Nikolay Karadzhov Date: Wed, 30 Apr 2025 17:05:46 +0300 Subject: [PATCH 1542/1748] Release entraid@5.0.0 --- packages/entraid/package.json | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/packages/entraid/package.json b/packages/entraid/package.json index 7c61a729ba9..8e9ff16aa22 100644 --- a/packages/entraid/package.json +++ b/packages/entraid/package.json @@ -1,6 +1,6 @@ { "name": "@redis/entraid", - "version": "5.0.0-next.7", + "version": "5.0.0", "license": "MIT", "main": "./dist/index.js", "types": "./dist/index.d.ts", From 73596170471c6b588c5ee5fd0977cc6049b6b24d Mon Sep 17 00:00:00 2001 From: Nikolay Karadzhov Date: Wed, 30 Apr 2025 17:06:44 +0300 Subject: [PATCH 1543/1748] Updated the Json package to use client@5.0.0 --- packages/json/package.json | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/packages/json/package.json b/packages/json/package.json index 0217aba33c6..378fabb080c 100644 --- a/packages/json/package.json +++ b/packages/json/package.json @@ -12,7 +12,7 @@ "test": "nyc -r text-summary -r lcov mocha -r tsx './lib/**/*.spec.ts'" }, "peerDependencies": { - "@redis/client": "^5.0.0-next.7" + "@redis/client": "^5.0.0" }, "devDependencies": { "@redis/test-utils": "*" From b7147911de0ffa725df48dc859156b658a2d85f0 Mon Sep 17 00:00:00 2001 From: Nikolay Karadzhov Date: Wed, 30 Apr 2025 17:07:55 +0300 Subject: [PATCH 1544/1748] Release json@5.0.0 --- packages/json/package.json | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/packages/json/package.json b/packages/json/package.json index 378fabb080c..7d6163cc59e 100644 --- a/packages/json/package.json +++ b/packages/json/package.json @@ -1,6 +1,6 @@ { "name": "@redis/json", - "version": "5.0.0-next.7", + "version": "5.0.0", "license": "MIT", "main": "./dist/lib/index.js", "types": "./dist/lib/index.d.ts", From c942f0eb9f77e818d537dc49974cc7d316117b8e Mon Sep 17 00:00:00 2001 From: Nikolay Karadzhov Date: Wed, 30 Apr 2025 17:12:59 +0300 Subject: [PATCH 1545/1748] Updated the Search package to use client@5.0.0 --- packages/search/package.json | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/packages/search/package.json b/packages/search/package.json index 605ab0a7ffe..c670f6e6aea 100644 --- a/packages/search/package.json +++ b/packages/search/package.json @@ -13,7 +13,7 @@ "test-sourcemap": "mocha -r ts-node/register/transpile-only './lib/**/*.spec.ts'" }, "peerDependencies": { - "@redis/client": "^5.0.0-next.7" + "@redis/client": "^5.0.0" }, "devDependencies": { "@redis/test-utils": "*" From 30cecc4b48100d92fda8b18fd13828b179e5d8c7 Mon Sep 17 00:00:00 2001 From: Nikolay Karadzhov Date: Wed, 30 Apr 2025 17:13:28 +0300 Subject: [PATCH 1546/1748] Release search@5.0.0 --- packages/search/package.json | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/packages/search/package.json b/packages/search/package.json index c670f6e6aea..0097cce3480 100644 --- a/packages/search/package.json +++ b/packages/search/package.json @@ -1,6 +1,6 @@ { "name": "@redis/search", - "version": "5.0.0-next.7", + "version": "5.0.0", "license": "MIT", "main": "./dist/lib/index.js", "types": "./dist/lib/index.d.ts", From 912e0d8170a7ea7760d1bdf36d3e3f0012d97304 Mon Sep 17 00:00:00 2001 From: Nikolay Karadzhov Date: Wed, 30 Apr 2025 17:14:39 +0300 Subject: [PATCH 1547/1748] Updated the Timeseries package to use client@5.0.0 --- packages/time-series/package.json | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/packages/time-series/package.json b/packages/time-series/package.json index e0c138b0dd2..bc14593d756 100644 --- a/packages/time-series/package.json +++ b/packages/time-series/package.json @@ -12,7 +12,7 @@ "test": "nyc -r text-summary -r lcov mocha -r tsx './lib/**/*.spec.ts'" }, "peerDependencies": { - "@redis/client": "^5.0.0-next.7" + "@redis/client": "^5.0.0" }, "devDependencies": { "@redis/test-utils": "*" From 16fb7e02da469f06ba52cadc79ac5f07b27a7848 Mon Sep 17 00:00:00 2001 From: Nikolay Karadzhov Date: Wed, 30 Apr 2025 17:15:15 +0300 Subject: [PATCH 1548/1748] Release time-series@5.0.0 --- packages/time-series/package.json | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/packages/time-series/package.json b/packages/time-series/package.json index bc14593d756..5960350ff41 100644 --- a/packages/time-series/package.json +++ b/packages/time-series/package.json @@ -1,6 +1,6 @@ { "name": "@redis/time-series", - "version": "5.0.0-next.7", + "version": "5.0.0", "license": "MIT", "main": "./dist/lib/index.js", "types": "./dist/lib/index.d.ts", From 1daf0f02dacdc1f875bcb068e11141f0856c1d0e Mon Sep 17 00:00:00 2001 From: Nikolay Karadzhov Date: Wed, 30 Apr 2025 17:17:15 +0300 Subject: [PATCH 1549/1748] Updated the Redis package to use client@5.0.0 --- packages/redis/package.json | 10 +++++----- 1 file changed, 5 insertions(+), 5 deletions(-) diff --git a/packages/redis/package.json b/packages/redis/package.json index 8b1567afb1d..7e1c38fadbf 100644 --- a/packages/redis/package.json +++ b/packages/redis/package.json @@ -10,11 +10,11 @@ "!dist/tsconfig.tsbuildinfo" ], "dependencies": { - "@redis/bloom": "5.0.0-next.7", - "@redis/client": "5.0.0-next.7", - "@redis/json": "5.0.0-next.7", - "@redis/search": "5.0.0-next.7", - "@redis/time-series": "5.0.0-next.7" + "@redis/bloom": "5.0.0", + "@redis/client": "5.0.0", + "@redis/json": "5.0.0", + "@redis/search": "5.0.0", + "@redis/time-series": "5.0.0" }, "engines": { "node": ">= 18" From 47e297077a20f396a1b22fafa917cea284dc3427 Mon Sep 17 00:00:00 2001 From: "H. Temelski" Date: Wed, 30 Apr 2025 17:20:34 +0300 Subject: [PATCH 1550/1748] Release redis@5.0.0 --- packages/redis/package.json | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/packages/redis/package.json b/packages/redis/package.json index 7e1c38fadbf..ddb79ca2275 100644 --- a/packages/redis/package.json +++ b/packages/redis/package.json @@ -1,7 +1,7 @@ { "name": "redis", "description": "A modern, high performance Redis client", - "version": "5.0.0-next.7", + "version": "5.0.0", "license": "MIT", "main": "./dist/index.js", "types": "./dist/index.d.ts", From 964b8de06d983b44dfda861dc966b3fca00a01ea Mon Sep 17 00:00:00 2001 From: Aryan Pandey Date: Sat, 3 May 2025 19:02:56 +0530 Subject: [PATCH 1551/1748] docs: clarify connection pooling in createClient and fix broken link in isolationPoolOptions (#2896) - Added a Connection Pooling section in `createClient` documentation to clarify that a single connection is typically sufficient and to provide guidance on when to use a connection pool. - Updated `isolationPoolOptions` description with a more precise explanation and replaced the broken link with a reference to `createClientPool`. - Changes made based on issue #2845. --- docs/client-configuration.md | 8 +++++++- 1 file changed, 7 insertions(+), 1 deletion(-) diff --git a/docs/client-configuration.md b/docs/client-configuration.md index deb68437e16..0564794ac46 100644 --- a/docs/client-configuration.md +++ b/docs/client-configuration.md @@ -25,7 +25,7 @@ | disableOfflineQueue | `false` | Disables offline queuing, see [FAQ](./FAQ.md#what-happens-when-the-network-goes-down) | | 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)) | -| isolationPoolOptions | | See the [Isolated Execution Guide](./isolated-execution.md) | +| isolationPoolOptions | | An object that configures a pool of isolated connections, If you frequently need isolated connections, consider using [createClientPool](https://github.com/redis/node-redis/blob/master/docs/pool.md#creating-a-pool) instead | | pingInterval | | Send `PING` command at interval (in ms). Useful with ["Azure Cache for Redis"](https://learn.microsoft.com/en-us/azure/azure-cache-for-redis/cache-best-practices-connection#idle-timeout) | ## Reconnect Strategy @@ -81,3 +81,9 @@ createClient({ } }); ``` +## Connection Pooling + +In most cases, a single Redis connection is sufficient, as the node-redis client efficiently handles commands using an underlying socket. Unlike traditional databases, Redis does not require connection pooling for optimal performance. + +However, if your use case requires exclusive connections see [RedisClientPool](https://github.com/redis/node-redis/blob/master/docs/pool.md), which allows you to create and manage multiple dedicated connections. + From bab1211ad499e2a6bdd1b62b07481ccfcc156344 Mon Sep 17 00:00:00 2001 From: Nicholas Wilson Date: Sat, 3 May 2025 08:34:15 -0500 Subject: [PATCH 1552/1748] Updated CHANGELOG.md, fix typo(s) (#2861) --- CHANGELOG.md | 16 ++++++++-------- 1 file changed, 8 insertions(+), 8 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index 33cf69851c7..fbc3070381e 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -21,7 +21,7 @@ - Fix `NOAUTH` error when using authentication & database (#1681) - Allow to `.quit()` in PubSub mode (#1766) -- Add an option to configurate `name` on a client (#1758) +- Add an option to configure `name` on a client (#1758) - Lowercase commands (`client.hset`) in `legacyMode` - Fix PubSub resubscribe (#1764) - Fix `RedisSocketOptions` type (#1741) @@ -466,7 +466,7 @@ Features Bugfixes - Fixed a javascript parser regression introduced in 2.0 that could result in timeouts on high load. ([@BridgeAR](https://github.com/BridgeAR)) -- I was not able to write a regression test for this, since the error seems to only occur under heavy load with special conditions. So please have a look for timeouts with the js parser, if you use it and report all issues and switch to the hiredis parser in the meanwhile. If you're able to come up with a reproducable test case, this would be even better :) +- I was not able to write a regression test for this, since the error seems to only occur under heavy load with special conditions. So please have a look for timeouts with the js parser, if you use it and report all issues and switch to the hiredis parser in the meanwhile. If you're able to come up with a reproducible test case, this would be even better :) - Fixed should_buffer boolean for .exec, .select and .auth commands not being returned and fix a couple special conditions ([@BridgeAR](https://github.com/BridgeAR)) If you do not rely on transactions but want to reduce the RTT you can use .batch from now on. It'll behave just the same as .multi but it does not have any transaction and therefor won't roll back any failed commands.
@@ -518,7 +518,7 @@ Bugfixes: - Fix argument mutation while using the array notation with the multi constructor (@BridgeAR) - Fix multi.hmset key not being type converted if used with an object and key not being a string (@BridgeAR) -- Fix parser errors not being catched properly (@BridgeAR) +- Fix parser errors not being caught properly (@BridgeAR) - Fix a crash that could occur if a redis server does not return the info command as usual #541 (@BridgeAR) - Explicitly passing undefined as a callback statement will work again. E.g. client.publish('channel', 'message', undefined); (@BridgeAR) @@ -560,13 +560,13 @@ This is the biggest release that node_redis had since it was released in 2010. A - Increased coverage by 10% and add a lot of tests to make sure everything works as it should. We now reached 97% :-) (@BridgeAR) - Remove dead code, clean up and refactor very old chunks (@BridgeAR) - Don't flush the offline queue if reconnecting (@BridgeAR) -- Emit all errors insteaf of throwing sometimes and sometimes emitting them (@BridgeAR) +- Emit all errors instead of throwing sometimes and sometimes emitting them (@BridgeAR) - _auth_pass_ passwords are now checked to be a valid password (@jcppman & @BridgeAR) ## Bug fixes: - Don't kill the app anymore by randomly throwing errors sync instead of emitting them (@BridgeAR) -- Don't catch user errors anymore occuring in callbacks (no try callback anymore & more fixes for the parser) (@BridgeAR) +- Don't catch user errors anymore occurring in callbacks (no try callback anymore & more fixes for the parser) (@BridgeAR) - Early garbage collection of queued items (@dohse) - Fix js parser returning errors as strings (@BridgeAR) - Do not wrap errors into other errors (@BridgeAR) @@ -588,19 +588,19 @@ This is the biggest release that node_redis had since it was released in 2010. A ## Breaking changes: 1. redis.send_command commands have to be lower case from now on. This does only apply if you use `.send_command` directly instead of the convenient methods like `redis.command`. -2. Error messages have changed quite a bit. If you depend on a specific wording please check your application carfully. +2. Error messages have changed quite a bit. If you depend on a specific wording please check your application carefully. 3. Errors are from now on always either returned if a callback is present or emitted. They won't be thrown (neither sync, nor async). 4. The Multi error handling has changed a lot! - All errors are from now on errors instead of strings (this only applied to the js parser). - If an error occurs while queueing the commands an EXECABORT error will be returned including the failed commands as `.errors` property instead of an array with errors. - If an error occurs while executing the commands and that command has a callback it'll return the error as first parameter (`err, undefined` instead of `null, undefined`). -- All the errors occuring while executing the commands will stay in the result value as error instance (if you used the js parser before they would have been strings). Be aware that the transaction won't be aborted if those error occurr! +- All the errors occurring while executing the commands will stay in the result value as error instance (if you used the js parser before they would have been strings). Be aware that the transaction won't be aborted if those error occur! - If `multi.exec` does not have a callback and an EXECABORT error occurrs, it'll emit that error instead. 5. If redis can't connect to your redis server it'll give up after a certain point of failures (either max connection attempts or connection timeout exceeded). If that is the case it'll emit an CONNECTION_BROKEN error. You'll have to initiate a new client to try again afterwards. 6. The offline queue is not flushed anymore on a reconnect. It'll stay until node_redis gives up trying to reach the server or until you close the connection. -7. Before this release node_redis catched user errors and threw them async back. This is not the case anymore! No user behavior of what so ever will be tracked or catched. +7. Before this release node_redis caught user errors and threw them async back. This is not the case anymore! No user behavior of what so ever will be tracked or caught. 8. The keyspace of `redis.server_info` (db0...) is from now on an object instead of an string. NodeRedis also thanks @qdb, @tobek, @cvibhagool, @frewsxcv, @davidbanham, @serv, @vitaliylag, @chrishamant, @GamingCoder and all other contributors that I may have missed for their contributions! From bd5c230c629a629f87a676b5687abb5056202eb5 Mon Sep 17 00:00:00 2001 From: Nikolay Karadzhov Date: Mon, 5 May 2025 11:29:59 +0300 Subject: [PATCH 1553/1748] fix: update package-lock.json (#2939) --- package-lock.json | 34 +++++++++++++++++----------------- 1 file changed, 17 insertions(+), 17 deletions(-) diff --git a/package-lock.json b/package-lock.json index dee35a0fcec..443d0d9e025 100644 --- a/package-lock.json +++ b/package-lock.json @@ -8520,7 +8520,7 @@ }, "packages/bloom": { "name": "@redis/bloom", - "version": "5.0.0-next.7", + "version": "5.0.0", "license": "MIT", "devDependencies": { "@redis/test-utils": "*" @@ -8529,12 +8529,12 @@ "node": ">= 18" }, "peerDependencies": { - "@redis/client": "^5.0.0-next.7" + "@redis/client": "^5.0.0" } }, "packages/client": { "name": "@redis/client", - "version": "5.0.0-next.7", + "version": "5.0.0", "license": "MIT", "dependencies": { "cluster-key-slot": "1.1.2" @@ -8550,7 +8550,7 @@ }, "packages/entraid": { "name": "@redis/entraid", - "version": "5.0.0-next.7", + "version": "5.0.0", "license": "MIT", "dependencies": { "@azure/identity": "^4.7.0", @@ -8569,7 +8569,7 @@ "node": ">= 18" }, "peerDependencies": { - "@redis/client": "^5.0.0-next.7" + "@redis/client": "^5.0.0" } }, "packages/entraid/node_modules/@types/node": { @@ -8606,7 +8606,7 @@ }, "packages/json": { "name": "@redis/json", - "version": "5.0.0-next.7", + "version": "5.0.0", "license": "MIT", "devDependencies": { "@redis/test-utils": "*" @@ -8615,18 +8615,18 @@ "node": ">= 18" }, "peerDependencies": { - "@redis/client": "^5.0.0-next.7" + "@redis/client": "^5.0.0" } }, "packages/redis": { - "version": "5.0.0-next.7", + "version": "5.0.0", "license": "MIT", "dependencies": { - "@redis/bloom": "5.0.0-next.7", - "@redis/client": "5.0.0-next.7", - "@redis/json": "5.0.0-next.7", - "@redis/search": "5.0.0-next.7", - "@redis/time-series": "5.0.0-next.7" + "@redis/bloom": "5.0.0", + "@redis/client": "5.0.0", + "@redis/json": "5.0.0", + "@redis/search": "5.0.0", + "@redis/time-series": "5.0.0" }, "engines": { "node": ">= 18" @@ -8634,7 +8634,7 @@ }, "packages/search": { "name": "@redis/search", - "version": "5.0.0-next.7", + "version": "5.0.0", "license": "MIT", "devDependencies": { "@redis/test-utils": "*" @@ -8643,7 +8643,7 @@ "node": ">= 18" }, "peerDependencies": { - "@redis/client": "^5.0.0-next.7" + "@redis/client": "^5.0.0" } }, "packages/test-utils": { @@ -8712,7 +8712,7 @@ }, "packages/time-series": { "name": "@redis/time-series", - "version": "5.0.0-next.7", + "version": "5.0.0", "license": "MIT", "devDependencies": { "@redis/test-utils": "*" @@ -8721,7 +8721,7 @@ "node": ">= 18" }, "peerDependencies": { - "@redis/client": "^5.0.0-next.7" + "@redis/client": "^5.0.0" } } } From 2c9ad2e772fc444c2cbd0c4119d9bfbe399e2213 Mon Sep 17 00:00:00 2001 From: Nikolay Karadzhov Date: Mon, 5 May 2025 11:35:41 +0300 Subject: [PATCH 1554/1748] chore(examples): fix examples for v5 (#2938) --- examples/README.md | 14 +++++----- examples/blocking-list-pop.js | 5 ++-- examples/command-with-modifiers.js | 2 +- examples/connect-to-cluster.js | 5 ++-- examples/dump-and-restore.js | 6 ++++ examples/get-server-time.js | 10 +++++-- examples/hyperloglog.js | 4 +-- examples/lua-multi-incr.js | 8 ++++-- examples/managing-json.js | 11 ++++++-- examples/package.json | 4 +-- examples/search-hashes.js | 8 +++--- examples/search-json.js | 16 +++++------ examples/search-knn.js | 6 ++-- examples/set-scan.js | 12 ++++++-- examples/sorted-set.js | 28 +++++++++++++++++-- examples/stream-consumer-group.js | 16 +++++------ examples/time-series.js | 10 +++---- examples/topk.js | 12 ++++---- .../transaction-with-arbitrary-commands.js | 27 ++++++++++++------ examples/transaction-with-watch.js | 4 ++- 20 files changed, 133 insertions(+), 75 deletions(-) diff --git a/examples/README.md b/examples/README.md index 1080f424da1..47c9912fd31 100644 --- a/examples/README.md +++ b/examples/README.md @@ -40,12 +40,12 @@ We'd love to see more examples here. If you have an idea that you'd like to see To set up the examples folder so that you can run an example / develop one of your own: -``` -$ git clone https://github.com/redis/node-redis.git -$ cd node-redis -$ npm install -ws && npm run build-all -$ cd examples -$ npm install +```bash +git clone https://github.com/redis/node-redis.git +cd node-redis +npm install -ws && npm run build +cd examples +npm install ``` ### Coding Guidelines for Examples @@ -91,5 +91,5 @@ await client.connect(); // Add your example code here... -client.destroy(); +client.close(); ``` diff --git a/examples/blocking-list-pop.js b/examples/blocking-list-pop.js index ec9bec4d639..f29a409398c 100644 --- a/examples/blocking-list-pop.js +++ b/examples/blocking-list-pop.js @@ -4,16 +4,15 @@ // The script will be blocked until the LPUSH command is executed. // After which we log the list and quit the client. -import { createClient, commandOptions } from 'redis'; +import { createClientPool } from 'redis'; -const client = createClient(); +const client = createClientPool(); await client.connect(); const keyName = 'keyName'; const blpopPromise = client.blPop( - commandOptions({ isolated: true }), keyName, 0 ); diff --git a/examples/command-with-modifiers.js b/examples/command-with-modifiers.js index 31106b17e45..356304722c0 100644 --- a/examples/command-with-modifiers.js +++ b/examples/command-with-modifiers.js @@ -1,7 +1,7 @@ // Define a custom script that shows example of SET command // with several modifiers. -import { createClient } from '../packages/client'; +import { createClient } from 'redis'; const client = createClient(); diff --git a/examples/connect-to-cluster.js b/examples/connect-to-cluster.js index 98655497c9e..86e45b87968 100644 --- a/examples/connect-to-cluster.js +++ b/examples/connect-to-cluster.js @@ -1,7 +1,7 @@ // This is an example script to connect to a running cluster. // After connecting to the cluster the code sets and get a value. -// To setup this cluster you can follow the guide here: +// To setup this cluster you can follow the guide here: // https://redis.io/docs/manual/scaling/ // In this guide the ports which are being used are 7000 - 7005 @@ -29,5 +29,4 @@ await cluster.connect(); await cluster.set('hello', 'cluster'); const value = await cluster.get('hello'); console.log(value); - -await cluster.quit(); +await cluster.close(); diff --git a/examples/dump-and-restore.js b/examples/dump-and-restore.js index c2ee7f1e199..f464fd38be1 100644 --- a/examples/dump-and-restore.js +++ b/examples/dump-and-restore.js @@ -12,6 +12,12 @@ const client = await createClient({ console.log('Redis Client Error', err); }).connect(); +// Make sure the source key exists +await client.set('source', 'value'); + +// Make sure destination doesnt exist +await client.del('destination'); + // DUMP a specific key into a local variable const dump = await client.dump('source'); diff --git a/examples/get-server-time.js b/examples/get-server-time.js index 0e32c1296af..752264df349 100644 --- a/examples/get-server-time.js +++ b/examples/get-server-time.js @@ -6,7 +6,13 @@ const client = createClient(); await client.connect(); const serverTime = await client.time(); -// 2022-02-25T12:57:40.000Z { microseconds: 351346 } +// In v5, TIME returns [unixTimestamp: string, microseconds: string] instead of Date +// Example: ['1708956789', '123456'] console.log(serverTime); -client.destroy(); +// Convert to JavaScript Date if needed +const [seconds, microseconds] = serverTime; +const date = new Date(parseInt(seconds) * 1000 + parseInt(microseconds) / 1000); +console.log('Converted to Date:', date); + +client.close(); diff --git a/examples/hyperloglog.js b/examples/hyperloglog.js index 027112a08bf..1f8f04f2a6c 100644 --- a/examples/hyperloglog.js +++ b/examples/hyperloglog.js @@ -9,7 +9,7 @@ const client = createClient(); await client.connect(); // Use `pfAdd` to add an element to a Hyperloglog, creating the Hyperloglog if necessary. -// await client.pfAdd(key, value) +// await client.pfAdd(key, value) // returns 1 or 0 // To get a count, the `pfCount` method is used. // await client.pfCount(key) @@ -48,4 +48,4 @@ try { console.error(e); } -client.destroy(); +client.close(); diff --git a/examples/lua-multi-incr.js b/examples/lua-multi-incr.js index 5cf39142006..71b12bdab0f 100644 --- a/examples/lua-multi-incr.js +++ b/examples/lua-multi-incr.js @@ -12,9 +12,11 @@ const client = createClient({ 'redis.pcall("INCRBY", KEYS[1], ARGV[1]),' + 'redis.pcall("INCRBY", KEYS[2], ARGV[1])' + '}', - transformArguments(key1, key2, increment) { - return [key1, key2, increment.toString()]; - }, + parseCommand(parser, key1, key2, increment) { + parser.pushKey(key1); + parser.pushKey(key2); + parser.push(increment.toString()); + }, }), }, }); diff --git a/examples/managing-json.js b/examples/managing-json.js index a28a0ee5106..0f1eb3b3c21 100644 --- a/examples/managing-json.js +++ b/examples/managing-json.js @@ -57,7 +57,7 @@ results = await client.json.get('noderedis:jsondata', { }); // Goldie is 3 years old now. -console.log(`Goldie is ${JSON.stringify(results[0])} years old now.`); +console.log(`Goldie is ${JSON.parse(results)[0]} years old now.`); // Add a new pet... await client.json.arrAppend('noderedis:jsondata', '$.pets', { @@ -68,9 +68,14 @@ await client.json.arrAppend('noderedis:jsondata', '$.pets', { }); // How many pets do we have now? -const numPets = await client.json.arrLen('noderedis:jsondata', '$.pets'); +const numPets = await client.json.arrLen('noderedis:jsondata', { path: '$.pets' }); // We now have 4 pets. console.log(`We now have ${numPets} pets.`); -client.destroy(); +const rex = { name: 'Rex', species: 'dog', age: 3, isMammal: true } + +const index = await client.json.arrIndex( 'noderedis:jsondata', '$.pets', rex); +console.log(`Rex is at index ${index}`); + +client.close(); diff --git a/examples/package.json b/examples/package.json index 91120774d94..c350c0b248b 100644 --- a/examples/package.json +++ b/examples/package.json @@ -1,12 +1,12 @@ { "name": "node-redis-examples", "version": "1.0.0", - "description": "node-redis 4 example script", + "description": "node-redis 5 example script", "main": "index.js", "private": true, "type": "module", "dependencies": { - "redis": "../packages/client" + "redis": "../packages/redis" } } diff --git a/examples/search-hashes.js b/examples/search-hashes.js index f3aca6b8aed..a496fec823a 100644 --- a/examples/search-hashes.js +++ b/examples/search-hashes.js @@ -1,7 +1,7 @@ // This example demonstrates how to use RediSearch to index and query data // stored in Redis hashes. -import { createClient, SchemaFieldTypes } from 'redis'; +import { createClient, SCHEMA_FIELD_TYPE } from 'redis'; const client = createClient(); @@ -12,11 +12,11 @@ try { // Documentation: https://redis.io/commands/ft.create/ await client.ft.create('idx:animals', { name: { - type: SchemaFieldTypes.TEXT, + type: SCHEMA_FIELD_TYPE.TEXT, SORTABLE: true }, - species: SchemaFieldTypes.TAG, - age: SchemaFieldTypes.NUMERIC + species: SCHEMA_FIELD_TYPE.TAG, + age: SCHEMA_FIELD_TYPE.NUMERIC }, { ON: 'HASH', PREFIX: 'noderedis:animals' diff --git a/examples/search-json.js b/examples/search-json.js index bff5b2cb362..60f2ff095ed 100644 --- a/examples/search-json.js +++ b/examples/search-json.js @@ -3,7 +3,7 @@ // https://redis.io/docs/stack/search/ // https://redis.io/docs/stack/json/ -import { createClient, SchemaFieldTypes, AggregateGroupByReducers, AggregateSteps } from 'redis'; +import { createClient, SCHEMA_FIELD_TYPE, FT_AGGREGATE_GROUP_BY_REDUCERS, FT_AGGREGATE_STEPS } from 'redis'; const client = createClient(); @@ -14,19 +14,19 @@ await client.connect(); try { await client.ft.create('idx:users', { '$.name': { - type: SchemaFieldTypes.TEXT, + type: SCHEMA_FIELD_TYPE.TEXT, SORTABLE: true }, '$.age': { - type: SchemaFieldTypes.NUMERIC, + type: SCHEMA_FIELD_TYPE.NUMERIC, AS: 'age' }, '$.coins': { - type: SchemaFieldTypes.NUMERIC, + type: SCHEMA_FIELD_TYPE.NUMERIC, AS: 'coins' }, '$.email': { - type: SchemaFieldTypes.TAG, + type: SCHEMA_FIELD_TYPE.TAG, AS: 'email' } }, { @@ -119,13 +119,13 @@ console.log( JSON.stringify( await client.ft.aggregate('idx:users', '*', { STEPS: [{ - type: AggregateSteps.GROUPBY, + type: FT_AGGREGATE_STEPS.GROUPBY, REDUCE: [{ - type: AggregateGroupByReducers.AVG, + type: FT_AGGREGATE_GROUP_BY_REDUCERS.AVG, property: 'age', AS: 'averageAge' }, { - type: AggregateGroupByReducers.SUM, + type: FT_AGGREGATE_GROUP_BY_REDUCERS.SUM, property: 'coins', AS: 'totalCoins' }] diff --git a/examples/search-knn.js b/examples/search-knn.js index 49bd00d86df..abfce990189 100644 --- a/examples/search-knn.js +++ b/examples/search-knn.js @@ -4,7 +4,7 @@ // Inspired by RediSearch Python tests: // https://github.com/RediSearch/RediSearch/blob/06e36d48946ea08bd0d8b76394a4e82eeb919d78/tests/pytests/test_vecsim.py#L96 -import { createClient, SchemaFieldTypes, VectorAlgorithms } from 'redis'; +import { createClient, SCHEMA_FIELD_TYPE, SCHEMA_VECTOR_FIELD_ALGORITHM } from 'redis'; const client = createClient(); @@ -15,8 +15,8 @@ try { // Documentation: https://redis.io/docs/stack/search/reference/vectors/ await client.ft.create('idx:knn-example', { v: { - type: SchemaFieldTypes.VECTOR, - ALGORITHM: VectorAlgorithms.HNSW, + type: SCHEMA_FIELD_TYPE.VECTOR, + ALGORITHM: SCHEMA_VECTOR_FIELD_ALGORITHM.HNSW, TYPE: 'FLOAT32', DIM: 2, DISTANCE_METRIC: 'COSINE' diff --git a/examples/set-scan.js b/examples/set-scan.js index 0e379224d9d..698b05983b0 100644 --- a/examples/set-scan.js +++ b/examples/set-scan.js @@ -8,8 +8,14 @@ const client = createClient(); await client.connect(); const setName = 'setName'; -for await (const member of client.sScanIterator(setName)) { - console.log(member); + +for await (const members of client.sScanIterator(setName)) { + console.log('Batch of members:', members); + + // Process each member in the batch if needed + for (const member of members) { + console.log('Individual member:', member); + } } -client.destroy(); +client.close(); diff --git a/examples/sorted-set.js b/examples/sorted-set.js index 3fcc24b8442..830427bea9a 100644 --- a/examples/sorted-set.js +++ b/examples/sorted-set.js @@ -24,8 +24,30 @@ await client.zAdd('mysortedset', [ // Get all of the values/scores from the sorted set using // the scan approach: // https://redis.io/commands/zscan -for await (const memberWithScore of client.zScanIterator('mysortedset')) { - console.log(memberWithScore); +for await (const membersWithScores of client.zScanIterator('mysortedset')) { + console.log('Batch of members with scores:', membersWithScores); + + for (const memberWithScore of membersWithScores) { + console.log('Individual member with score:', memberWithScore); + } } -client.destroy(); +await client.zAdd('anothersortedset', [ + { + score: 99, + value: 'Ninety Nine' + }, + { + score: 102, + value: 'One Hundred and Two' + } +]); + +// Intersection of two sorted sets +const intersection = await client.zInter([ + { key: 'mysortedset', weight: 1 }, + { key: 'anothersortedset', weight: 1 } +]); +console.log('Intersection:', intersection); + +client.close(); diff --git a/examples/stream-consumer-group.js b/examples/stream-consumer-group.js index 0161b5b4d32..cf82b5e96af 100644 --- a/examples/stream-consumer-group.js +++ b/examples/stream-consumer-group.js @@ -20,7 +20,7 @@ // // $ node stream-consumer-group.js consumer2 -import { createClient, commandOptions } from 'redis'; +import { createClient } from 'redis'; const client = createClient(); @@ -46,14 +46,13 @@ try { console.log(`Starting consumer ${consumerName}.`); +const pool = client.createPool(); + while (true) { try { // https://redis.io/commands/xreadgroup/ - let response = await client.xReadGroup( - commandOptions({ - isolated: true - }), - 'myconsumergroup', + let response = await pool.xReadGroup( + 'myconsumergroup', consumerName, [ // XREADGROUP can read from multiple streams, starting at a // different ID for each... @@ -91,9 +90,10 @@ while (true) { // stream entry. // https://redis.io/commands/xack/ const entryId = response[0].messages[0].id; - await client.xAck('mystream', 'myconsumergroup', entryId); + const ackResult = await pool.xAck('mystream', 'myconsumergroup', entryId); - console.log(`Acknowledged processing of entry ${entryId}.`); + // ackResult will be 1 if the message was successfully acknowledged, 0 otherwise + console.log(`Acknowledged processing of entry ${entryId}. Result: ${ackResult}`); } else { // Response is null, we have read everything that is // in the stream right now... diff --git a/examples/time-series.js b/examples/time-series.js index 1d61ff94408..75df2736f81 100644 --- a/examples/time-series.js +++ b/examples/time-series.js @@ -2,7 +2,7 @@ // Requires the RedisTimeSeries module: https://redis.io/docs/stack/timeseries/ import { createClient } from 'redis'; -import { TimeSeriesDuplicatePolicies, TimeSeriesEncoding, TimeSeriesAggregationType } from '@redis/time-series'; +import { TIME_SERIES_DUPLICATE_POLICIES, TIME_SERIES_ENCODING, TIME_SERIES_AGGREGATION_TYPE } from '@redis/time-series'; const client = createClient(); @@ -14,8 +14,8 @@ try { // https://redis.io/commands/ts.create/ const created = await client.ts.create('mytimeseries', { RETENTION: 86400000, // 1 day in milliseconds - ENCODING: TimeSeriesEncoding.UNCOMPRESSED, // No compression - DUPLICATE_POLICY: TimeSeriesDuplicatePolicies.BLOCK // No duplicates + ENCODING: TIME_SERIES_ENCODING.UNCOMPRESSED, // No compression + DUPLICATE_POLICY: TIME_SERIES_DUPLICATE_POLICIES.BLOCK // No duplicates }); if (created === 'OK') { @@ -74,7 +74,7 @@ try { const rangeResponse = await client.ts.range('mytimeseries', fromTimestamp, toTimestamp, { // Group into 10 second averages. AGGREGATION: { - type: TimeSeriesAggregationType.AVERAGE, + type: TIME_SERIES_AGGREGATION_TYPE.AVG, timeBucket: 10000 } }); @@ -119,4 +119,4 @@ try { console.error(e); } -client.destroy(); +client.close(); diff --git a/examples/topk.js b/examples/topk.js index d09144c230c..10cc29950ed 100644 --- a/examples/topk.js +++ b/examples/topk.js @@ -1,4 +1,4 @@ -// This example demonstrates the use of the Top K +// This example demonstrates the use of the Top K // in the RedisBloom module (https://redis.io/docs/stack/bloom/) import { createClient } from 'redis'; @@ -95,10 +95,10 @@ const [ steve, suze, leibale, frederick ] = await client.topK.query('mytopk', [ 'frederick' ]); -console.log(`steve ${steve === 1 ? 'is': 'is not'} in the top 10.`); -console.log(`suze ${suze === 1 ? 'is': 'is not'} in the top 10.`); -console.log(`leibale ${leibale === 1 ? 'is': 'is not'} in the top 10.`); -console.log(`frederick ${frederick === 1 ? 'is': 'is not'} in the top 10.`); +console.log(`steve ${steve ? 'is': 'is not'} in the top 10.`); +console.log(`suze ${suze ? 'is': 'is not'} in the top 10.`); +console.log(`leibale ${leibale ? 'is': 'is not'} in the top 10.`); +console.log(`frederick ${frederick ? 'is': 'is not'} in the top 10.`); // Get count estimate for some team members with TOPK.COUNT: // https://redis.io/commands/topk.count/ @@ -110,4 +110,4 @@ const [ simonCount, lanceCount ] = await client.topK.count('mytopk', [ console.log(`Count estimate for simon: ${simonCount}.`); console.log(`Count estimate for lance: ${lanceCount}.`); -client.destroy(); +client.close(); diff --git a/examples/transaction-with-arbitrary-commands.js b/examples/transaction-with-arbitrary-commands.js index d68533205a1..cc22a659678 100644 --- a/examples/transaction-with-arbitrary-commands.js +++ b/examples/transaction-with-arbitrary-commands.js @@ -1,6 +1,6 @@ -// How to mix and match supported commands that have named functions with +// How to mix and match supported commands that have named functions with // commands sent as arbitrary strings in the same transaction context. -// Use this when working with new Redis commands that haven't been added to +// Use this when working with new Redis commands that haven't been added to // node-redis yet, or when working with commands that have been added to Redis // by modules other than those directly supported by node-redis. @@ -23,18 +23,29 @@ await client.sendCommand(['hset', 'hash2', 'number', '3']); // In a transaction context, use addCommand to send arbitrary commands. // addCommand can be mixed and matched with named command functions as // shown. -const responses = await client - .multi() +const multi = client.multi() .hGetAll('hash2') .addCommand(['hset', 'hash3', 'number', '4']) - .hGet('hash3', 'number') - .exec(); + .hGet('hash3', 'number'); + +// exec() returns Array +const responses = await multi.exec(); // responses will be: // [ [Object: null prototype] { name: 'hash2', number: '3' }, 0, '4' ] -console.log(responses); +console.log('Using exec():', responses); + +// This is equivalent to multi.exec<'typed'>() +const typedResponses = await multi + .hGetAll('hash2') + .addCommand(['hset', 'hash3', 'number', '4']) + .hGet('hash3', 'number') + .execTyped(); + +// typedResponses will have more specific types +console.log('Using execTyped():', typedResponses); // Clean up fixtures. await client.del(['hash1', 'hash2', 'hash3']); -client.destroy(); +client.close(); diff --git a/examples/transaction-with-watch.js b/examples/transaction-with-watch.js index d92b910dfa3..752d0b6a4e3 100644 --- a/examples/transaction-with-watch.js +++ b/examples/transaction-with-watch.js @@ -13,9 +13,11 @@ function restrictFunctionCalls(fn, maxCalls) { const fn = restrictFunctionCalls(transaction, 4); +const pool = await client.createPool(); + async function transaction() { try { - await client.executeIsolated(async (isolatedClient) => { + await pool.execute(async (isolatedClient) => { await isolatedClient.watch('paymentId:1259'); const multi = isolatedClient .multi() From f6912b03da8f8d8885714369d71df28d9a48ec56 Mon Sep 17 00:00:00 2001 From: Nikolay Karadzhov Date: Mon, 5 May 2025 11:38:51 +0300 Subject: [PATCH 1555/1748] Update packages/redis/README.md (#2935) * copy root readme into redis readme * fix links * update supported versions * update supported versions --- README.md | 8 +- packages/redis/README.md | 322 ++++++++++++++++++++++++++------------- 2 files changed, 219 insertions(+), 111 deletions(-) diff --git a/README.md b/README.md index b9fe524c677..ac6394461f1 100644 --- a/README.md +++ b/README.md @@ -288,11 +288,9 @@ Node Redis is supported with the following versions of Redis: | Version | Supported | | ------- | ------------------ | | 8.0.z | :heavy_check_mark: | -| 7.0.z | :heavy_check_mark: | -| 6.2.z | :heavy_check_mark: | -| 6.0.z | :heavy_check_mark: | -| 5.0.z | :heavy_check_mark: | -| < 5.0 | :x: | +| 7.4.z | :heavy_check_mark: | +| 7.2.z | :heavy_check_mark: | +| < 7.2 | :x: | > Node Redis should work with older versions of Redis, but it is not fully tested and we cannot offer support. diff --git a/packages/redis/README.md b/packages/redis/README.md index 76929ffa48b..f0b2a34905d 100644 --- a/packages/redis/README.md +++ b/packages/redis/README.md @@ -1,195 +1,305 @@ # Node-Redis +[![Tests](https://img.shields.io/github/actions/workflow/status/redis/node-redis/tests.yml?branch=master)](https://github.com/redis/node-redis/actions/workflows/tests.yml) +[![Coverage](https://codecov.io/gh/redis/node-redis/branch/master/graph/badge.svg?token=xcfqHhJC37)](https://codecov.io/gh/redis/node-redis) +[![License](https://img.shields.io/github/license/redis/node-redis.svg)](https://github.com/redis/node-redis/blob/master/LICENSE) + +[![Discord](https://img.shields.io/discord/697882427875393627.svg?style=social&logo=discord)](https://discord.gg/redis) +[![Twitch](https://img.shields.io/twitch/status/redisinc?style=social)](https://www.twitch.tv/redisinc) +[![YouTube](https://img.shields.io/youtube/channel/views/UCD78lHSwYqMlyetR0_P4Vig?style=social)](https://www.youtube.com/redisinc) +[![Twitter](https://img.shields.io/twitter/follow/redisinc?style=social)](https://twitter.com/redisinc) + +node-redis is a modern, high performance [Redis](https://redis.io) client for Node.js. + +## How do I Redis? + +[Learn for free at Redis University](https://university.redis.com/) + +[Build faster with the Redis Launchpad](https://launchpad.redis.com/) + +[Try the Redis Cloud](https://redis.com/try-free/) + +[Dive in developer tutorials](https://developer.redis.com/) + +[Join the Redis community](https://redis.com/community/) + +[Work at Redis](https://redis.com/company/careers/jobs/) + +## Installation + +Start a redis via docker: + +```bash +docker run -p 6379:6379 -d redis:8.0-rc1 +``` + +To install node-redis, simply: + +```bash +npm install redis +``` +> "redis" is the "whole in one" package that includes all the other packages. If you only need a subset of the commands, +> you can install the individual packages. See the list below. + +## Packages + +| Name | Description | +| ---------------------------------------------- | ------------------------------------------------------------------------------------------- | +| [`redis`](../redis) | The client with all the ["redis-stack"](https://github.com/redis-stack/redis-stack) modules | +| [`@redis/client`](../client) | The base clients (i.e `RedisClient`, `RedisCluster`, etc.) | +| [`@redis/bloom`](../bloom) | [Redis Bloom](https://redis.io/docs/data-types/probabilistic/) commands | +| [`@redis/json`](../json) | [Redis JSON](https://redis.io/docs/data-types/json/) commands | +| [`@redis/search`](../search) | [RediSearch](https://redis.io/docs/interact/search-and-query/) commands | +| [`@redis/time-series`](../time-series) | [Redis Time-Series](https://redis.io/docs/data-types/timeseries/) commands | +| [`@redis/entraid`](../entraid) | Secure token-based authentication for Redis clients using Microsoft Entra ID | + +> Looking for a high-level library to handle object mapping? +> See [redis-om-node](https://github.com/redis/redis-om-node)! + + ## Usage ### Basic Example -```javascript -import { createClient } from 'redis'; +```typescript +import { createClient } from "redis"; const client = await createClient() - .on('error', err => console.log('Redis Client Error', err)) + .on("error", (err) => console.log("Redis Client Error", err)) .connect(); -await client.set('key', 'value'); -const value = await client.get('key'); -await client.close(); +await client.set("key", "value"); +const value = await client.get("key"); +client.destroy(); ``` -> :warning: You **MUST** listen to `error` events. If a client doesn't have at least one `error` listener registered and an `error` occurs, that error will be thrown and the Node.js process will exit. See the [`EventEmitter` docs](https://nodejs.org/api/events.html#error-events) for more details. +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]`: -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]`: - -```javascript +```typescript createClient({ - url: 'redis://alice:foobared@awesome.redis.server:6380' + 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). +You can also use discrete parameters, UNIX sockets, and even TLS to connect. Details can be found in +the [client configuration guide](../../docs/client-configuration.md). + +To check if the the client is connected and ready to send commands, use `client.isReady` which returns a boolean. +`client.isOpen` is also available. This returns `true` when the client's underlying socket is open, and `false` when it +isn't (for example when the client is still connecting or reconnecting after a network error). ### Redis Commands -There is built-in support for all of the [out-of-the-box Redis commands](https://redis.io/commands). They are exposed using the raw Redis command names (`HSET`, `HGETALL`, etc.) and a friendlier camel-cased version (`hSet`, `hGetAll`, etc.): +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.): -```javascript +```typescript // raw Redis commands -await client.HSET('key', 'field', 'value'); -await client.HGETALL('key'); +await client.HSET("key", "field", "value"); +await client.HGETALL("key"); // friendly JavaScript commands -await client.hSet('key', 'field', 'value'); -await client.hGetAll('key'); +await client.hSet("key", "field", "value"); +await client.hGetAll("key"); ``` Modifiers to commands are specified using a JavaScript object: -```javascript -await client.set('key', 'value', { - expiration: { - type: 'EX', - value: 10 - }, - condition: 'NX' +```typescript +await client.set("key", "value", { + EX: 10, + NX: true, }); ``` -> NOTE: command modifiers that change the reply type (e.g. `WITHSCORES` for `ZDIFF`) are exposed as separate commands (e.g. `ZDIFF_WITHSCORES`/`zDiffWithScores`). - -Replies will be mapped to useful data structures: +Replies will be transformed into useful data structures: -```javascript -await client.hGetAll('key'); // { field1: 'value1', field2: 'value2' } -await client.hVals('key'); // ['value1', 'value2'] +```typescript +await client.hGetAll("key"); // { field1: 'value1', field2: 'value2' } +await client.hVals("key"); // ['value1', 'value2'] ``` -> NOTE: you can change the default type mapping. See the [Type Mapping](../../docs/command-options.md#type-mapping) documentation for more information. +`Buffer`s are supported as well: + +```typescript +const client = createClient().withTypeMapping({ + [RESP_TYPES.BLOB_STRING]: Buffer +}); + +await client.hSet("key", "field", Buffer.from("value")); // 'OK' +await client.hGet("key", "field"); // { field: } + +``` ### Unsupported Redis Commands If you want to run commands and/or use arguments that Node Redis doesn't know about (yet!) use `.sendCommand()`: -```javascript -await client.sendCommand(['SET', 'key', 'value', 'EX', '10', 'NX']); // 'OK' -await client.sendCommand(['HGETALL', 'key']); // ['key1', 'field1', 'key2', 'field2'] +```typescript +await client.sendCommand(["SET", "key", "value", "NX"]); // 'OK' + +await client.sendCommand(["HGETALL", "key"]); // ['key1', 'field1', 'key2', 'field2'] ``` -### Disconnecting +### Transactions (Multi/Exec) -#### `.close()` +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: -Gracefully close a client's connection to Redis. -Wait for commands in process, but reject any new commands. +```typescript +await client.set("another-key", "another-value"); -```javascript -const [ping, get] = await Promise.all([ - client.ping(), - client.get('key'), - client.close() -]); // ['PONG', null] - -try { - await client.get('key'); -} catch (err) { - // ClientClosedError -} +const [setKeyReply, otherKeyValue] = await client + .multi() + .set("key", "value") + .get("another-key") + .exec(); // ['OK', 'another-value'] ``` -> `.close()` is just like `.quit()` which was depreacted v5. See the [relevant section in the migration guide](../../docs/v4-to-v5.md#Quit-VS-Disconnect) for more information. +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. + -#### `.destroy()` +### Blocking Commands -Forcibly close a client's connection to Redis. +In v4, `RedisClient` had the ability to create a pool of connections using an "Isolation Pool" on top of the "main" +connection. However, there was no way to use the pool without a "main" connection: ```javascript -try { - const promise = Promise.all([ - client.ping(), - client.get('key') - ]); +const client = await createClient() + .on("error", (err) => console.error(err)) + .connect(); - client.destroy(); +await client.ping(client.commandOptions({ isolated: true })); +``` + +In v5 we've extracted this pool logic into its own class—`RedisClientPool`: + +```javascript +const pool = await createClientPool() + .on("error", (err) => console.error(err)) + .connect(); - await promise; -} catch (err) { - // DisconnectsClientError +await pool.ping(); +``` + + +### Pub/Sub + +See the [Pub/Sub overview](../../docs/pub-sub.md). + +### Scan Iterator + +[`SCAN`](https://redis.io/commands/scan) results can be looped over +using [async iterators](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Symbol/asyncIterator): + +```typescript +for await (const key of client.scanIterator()) { + // use the key! + await client.get(key); } +``` + +This works with `HSCAN`, `SSCAN`, and `ZSCAN` too: -try { - await client.get('key'); -} catch (err) { - // ClientClosedError +```typescript +for await (const { field, value } of client.hScanIterator("hash")) { +} +for await (const member of client.sScanIterator("set")) { +} +for await (const { score, value } of client.zScanIterator("sorted-set")) { } ``` -> `.destroy()` is just like `.disconnect()` which was depreated in v5. See the [relevant section in the migration guide](../../docs/v4-to-v5.md#Quit-VS-Disconnect) for more information. +You can override the default options by providing a configuration object: + +```typescript +client.scanIterator({ + TYPE: "string", // `SCAN` only + MATCH: "patter*", + COUNT: 100, +}); +``` + +### Disconnecting + +The `QUIT` command has been deprecated in Redis 7.2 and should now also be considered deprecated in Node-Redis. Instead +of sending a `QUIT` command to the server, the client can simply close the network connection. + +`client.QUIT/quit()` is replaced by `client.close()`. and, to avoid confusion, `client.disconnect()` has been renamed to +`client.destroy()`. + +```typescript +client.destroy(); +``` ### Auto-Pipelining Node Redis will automatically pipeline requests that are made during the same "tick". -```javascript -client.set('Tm9kZSBSZWRpcw==', 'users:1'); -client.sAdd('users:1:tokens', 'Tm9kZSBSZWRpcw=='); +```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()`. +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()`. -```javascript +```typescript await Promise.all([ - client.set('Tm9kZSBSZWRpcw==', 'users:1'), - client.sAdd('users:1:tokens', 'Tm9kZSBSZWRpcw==') + client.set("Tm9kZSBSZWRpcw==", "users:1"), + client.sAdd("users:1:tokens", "Tm9kZSBSZWRpcw=="), ]); ``` -### Connection State +### Programmability -To client exposes 2 `boolean`s that track the client state: -1. `isOpen` - the client is either connecting or connected. -2. `isReady` - the client is connected and ready to send +See the [Programmability overview](../../docs/programmability.md). -### Events +### Clustering -The client extends `EventEmitter` and emits the following events: +Check out the [Clustering Guide](../../docs/clustering.md) when using Node Redis to connect to a Redis Cluster. -| Name | When | Listener arguments | -|-------------------------|------------------------------------------------------------------------------------|------------------------------------------------------------| -| `connect` | Initiating a connection to the server | *No arguments* | -| `ready` | Client is ready to use | *No arguments* | -| `end` | Connection has been closed (via `.quit()` or `.disconnect()`) | *No arguments* | -| `error` | An error has occurred—usually a network issue such as "Socket closed unexpectedly" | `(error: Error)` | -| `reconnecting` | Client is trying to reconnect to the server | *No arguments* | -| `sharded-channel-moved` | See [here](../../docs/pub-sub.md#sharded-channel-moved-event) | See [here](../../docs/pub-sub.md#sharded-channel-moved-event) | +### Events + +The Node Redis client class is an Nodejs EventEmitter and it emits an event each time the network status changes: -> :warning: You **MUST** listen to `error` events. If a client doesn't have at least one `error` listener registered and an `error` occurs, that error will be thrown and the Node.js process will exit. See the [`EventEmitter` docs](https://nodejs.org/api/events.html#error-events) for more details. +| Name | When | Listener arguments | +| ----------------------- | ---------------------------------------------------------------------------------- | --------------------------------------------------------- | +| `connect` | Initiating a connection to the server | _No arguments_ | +| `ready` | Client is ready to use | _No arguments_ | +| `end` | Connection has been closed (via `.disconnect()`) | _No arguments_ | +| `error` | An error has occurred—usually a network issue such as "Socket closed unexpectedly" | `(error: Error)` | +| `reconnecting` | Client is trying to reconnect to the server | _No arguments_ | +| `sharded-channel-moved` | See [here](../../docs/pub-sub.md#sharded-channel-moved-event) | See [here](../../docs/pub-sub.md#sharded-channel-moved-event) | -### Read more +> :warning: You **MUST** listen to `error` events. If a client doesn't have at least one `error` listener registered and +> an `error` occurs, that error will be thrown and the Node.js process will exit. See the [ > `EventEmitter` docs](https://nodejs.org/api/events.html#events_error_events) for more details. -- [Transactions (`MULTI`/`EXEC`)](../../docs/transactions.md). -- [Pub/Sub](../../docs/pub-sub.md). -- [Scan Iterators](../../docs/scan-iterators.md). -- [Programmability](../../docs/programmability.md). -- [Command Options](../../docs/command-options.md). -- [Pool](../../docs/pool.md). -- [Clustering](../../docs/clustering.md). -- [Sentinel](../../docs/sentinel.md). -- [FAQ](../../docs/FAQ.md). +> The client will not emit [any other events](../../docs/v3-to-v4.md#all-the-removed-events) beyond those listed above. ## Supported Redis versions Node Redis is supported with the following versions of Redis: | Version | Supported | -|---------|--------------------| +| ------- | ------------------ | +| 8.0.z | :heavy_check_mark: | +| 7.4.z | :heavy_check_mark: | | 7.2.z | :heavy_check_mark: | -| 7.0.z | :heavy_check_mark: | -| 6.2.z | :heavy_check_mark: | -| 6.0.z | :heavy_check_mark: | -| 5.0.z | :heavy_check_mark: | -| < 5.0 | :x: | +| < 7.2 | :x: | > Node Redis should work with older versions of Redis, but it is not fully tested and we cannot offer support. +## Migration + +- [From V3 to V4](../../docs/v3-to-v4.md) +- [From V4 to V5](../../docs/v4-to-v5.md) +- [V5](../../docs/v5.md) + ## Contributing If you'd like to contribute, check out the [contributing guide](../../CONTRIBUTING.md). From 404db308374ed1bb61dca6f62826e4a7d6f87d84 Mon Sep 17 00:00:00 2001 From: Nikolay Karadzhov Date: Mon, 5 May 2025 11:51:15 +0300 Subject: [PATCH 1556/1748] Release client@5.0.1 --- packages/client/package.json | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/packages/client/package.json b/packages/client/package.json index 5b88c185da9..34d60154083 100644 --- a/packages/client/package.json +++ b/packages/client/package.json @@ -1,6 +1,6 @@ { "name": "@redis/client", - "version": "5.0.0", + "version": "5.0.1", "license": "MIT", "main": "./dist/index.js", "types": "./dist/index.d.ts", From 2c9faad2d9673941111018047dc420ecc1c842a0 Mon Sep 17 00:00:00 2001 From: Nikolay Karadzhov Date: Mon, 5 May 2025 11:55:40 +0300 Subject: [PATCH 1557/1748] Updated the Bloom package to use client@5.0.1 --- packages/bloom/package.json | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/packages/bloom/package.json b/packages/bloom/package.json index 595e581ad8e..41a366c01d9 100644 --- a/packages/bloom/package.json +++ b/packages/bloom/package.json @@ -12,7 +12,7 @@ "test": "nyc -r text-summary -r lcov mocha -r tsx './lib/**/*.spec.ts'" }, "peerDependencies": { - "@redis/client": "^5.0.0" + "@redis/client": "^5.0.1" }, "devDependencies": { "@redis/test-utils": "*" From 6714ad109ddd00b9a4be968e84e40de19c5e5df1 Mon Sep 17 00:00:00 2001 From: Nikolay Karadzhov Date: Mon, 5 May 2025 11:57:11 +0300 Subject: [PATCH 1558/1748] Release bloom@5.0.1 --- packages/bloom/package.json | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/packages/bloom/package.json b/packages/bloom/package.json index 41a366c01d9..0772fcd3bb8 100644 --- a/packages/bloom/package.json +++ b/packages/bloom/package.json @@ -1,6 +1,6 @@ { "name": "@redis/bloom", - "version": "5.0.0", + "version": "5.0.1", "license": "MIT", "main": "./dist/lib/index.js", "types": "./dist/lib/index.d.ts", From 57e5daab98f5adcc4d7d9eaa2d7de0e1bb6296b5 Mon Sep 17 00:00:00 2001 From: Nikolay Karadzhov Date: Mon, 5 May 2025 11:58:23 +0300 Subject: [PATCH 1559/1748] Updated the Entraid package to use client@5.0.1 --- packages/entraid/package.json | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/packages/entraid/package.json b/packages/entraid/package.json index 8e9ff16aa22..665d3cc852e 100644 --- a/packages/entraid/package.json +++ b/packages/entraid/package.json @@ -21,7 +21,7 @@ "@azure/msal-node": "^2.16.1" }, "peerDependencies": { - "@redis/client": "^5.0.0" + "@redis/client": "^5.0.1" }, "devDependencies": { "@types/express": "^4.17.21", From 67cde227ccc54ae1d606e3ee03aabdca5579cb66 Mon Sep 17 00:00:00 2001 From: Nikolay Karadzhov Date: Mon, 5 May 2025 11:59:07 +0300 Subject: [PATCH 1560/1748] Release entraid@5.0.1 --- packages/entraid/package.json | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/packages/entraid/package.json b/packages/entraid/package.json index 665d3cc852e..57af37d36cd 100644 --- a/packages/entraid/package.json +++ b/packages/entraid/package.json @@ -1,6 +1,6 @@ { "name": "@redis/entraid", - "version": "5.0.0", + "version": "5.0.1", "license": "MIT", "main": "./dist/index.js", "types": "./dist/index.d.ts", From 84680d6e5a6176a2babf8a1d718b117293c7d74f Mon Sep 17 00:00:00 2001 From: Nikolay Karadzhov Date: Mon, 5 May 2025 12:00:10 +0300 Subject: [PATCH 1561/1748] Updated the Json package to use client@5.0.1 --- packages/json/package.json | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/packages/json/package.json b/packages/json/package.json index 7d6163cc59e..ac946aa2381 100644 --- a/packages/json/package.json +++ b/packages/json/package.json @@ -12,7 +12,7 @@ "test": "nyc -r text-summary -r lcov mocha -r tsx './lib/**/*.spec.ts'" }, "peerDependencies": { - "@redis/client": "^5.0.0" + "@redis/client": "^5.0.1" }, "devDependencies": { "@redis/test-utils": "*" From e99cd073b66e4d2466c2bd2698ec7ae073635fac Mon Sep 17 00:00:00 2001 From: Nikolay Karadzhov Date: Mon, 5 May 2025 12:00:46 +0300 Subject: [PATCH 1562/1748] Release json@5.0.1 --- packages/json/package.json | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/packages/json/package.json b/packages/json/package.json index ac946aa2381..5c2dfc49a45 100644 --- a/packages/json/package.json +++ b/packages/json/package.json @@ -1,6 +1,6 @@ { "name": "@redis/json", - "version": "5.0.0", + "version": "5.0.1", "license": "MIT", "main": "./dist/lib/index.js", "types": "./dist/lib/index.d.ts", From cc13ae298e998830873b7a9db0268c018903250b Mon Sep 17 00:00:00 2001 From: Nikolay Karadzhov Date: Mon, 5 May 2025 12:01:39 +0300 Subject: [PATCH 1563/1748] Updated the Search package to use client@5.0.1 --- packages/search/package.json | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/packages/search/package.json b/packages/search/package.json index 0097cce3480..df907d98972 100644 --- a/packages/search/package.json +++ b/packages/search/package.json @@ -13,7 +13,7 @@ "test-sourcemap": "mocha -r ts-node/register/transpile-only './lib/**/*.spec.ts'" }, "peerDependencies": { - "@redis/client": "^5.0.0" + "@redis/client": "^5.0.1" }, "devDependencies": { "@redis/test-utils": "*" From 17179ddb351d5405aa47cd954303b52c0668345e Mon Sep 17 00:00:00 2001 From: Nikolay Karadzhov Date: Mon, 5 May 2025 12:02:20 +0300 Subject: [PATCH 1564/1748] Release search@5.0.1 --- packages/search/package.json | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/packages/search/package.json b/packages/search/package.json index df907d98972..ba1fa2a74be 100644 --- a/packages/search/package.json +++ b/packages/search/package.json @@ -1,6 +1,6 @@ { "name": "@redis/search", - "version": "5.0.0", + "version": "5.0.1", "license": "MIT", "main": "./dist/lib/index.js", "types": "./dist/lib/index.d.ts", From 1e976d24ccd0f6f8e5baf1eacf4cc871fc34dbef Mon Sep 17 00:00:00 2001 From: Nikolay Karadzhov Date: Mon, 5 May 2025 12:03:17 +0300 Subject: [PATCH 1565/1748] Updated the Timeseries package to use client@5.0.1 --- packages/time-series/package.json | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/packages/time-series/package.json b/packages/time-series/package.json index 5960350ff41..9d57f5558d2 100644 --- a/packages/time-series/package.json +++ b/packages/time-series/package.json @@ -12,7 +12,7 @@ "test": "nyc -r text-summary -r lcov mocha -r tsx './lib/**/*.spec.ts'" }, "peerDependencies": { - "@redis/client": "^5.0.0" + "@redis/client": "^5.0.1" }, "devDependencies": { "@redis/test-utils": "*" From 71ab009ef8d13a7ba33966d6bb0e5a79511f7ef4 Mon Sep 17 00:00:00 2001 From: Nikolay Karadzhov Date: Mon, 5 May 2025 12:03:48 +0300 Subject: [PATCH 1566/1748] Release time-series@5.0.1 --- packages/time-series/package.json | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/packages/time-series/package.json b/packages/time-series/package.json index 9d57f5558d2..1b436701d6b 100644 --- a/packages/time-series/package.json +++ b/packages/time-series/package.json @@ -1,6 +1,6 @@ { "name": "@redis/time-series", - "version": "5.0.0", + "version": "5.0.1", "license": "MIT", "main": "./dist/lib/index.js", "types": "./dist/lib/index.d.ts", From fd069641f30fb684516de2b478b6c5a3581058d0 Mon Sep 17 00:00:00 2001 From: Nikolay Karadzhov Date: Mon, 5 May 2025 12:21:02 +0300 Subject: [PATCH 1567/1748] Updated the Redis package to use client@5.0.1 --- package-lock.json | 32 ++++++++++++++++---------------- packages/redis/package.json | 10 +++++----- 2 files changed, 21 insertions(+), 21 deletions(-) diff --git a/package-lock.json b/package-lock.json index 443d0d9e025..0b8ca6ee37e 100644 --- a/package-lock.json +++ b/package-lock.json @@ -8520,7 +8520,7 @@ }, "packages/bloom": { "name": "@redis/bloom", - "version": "5.0.0", + "version": "5.0.1", "license": "MIT", "devDependencies": { "@redis/test-utils": "*" @@ -8529,12 +8529,12 @@ "node": ">= 18" }, "peerDependencies": { - "@redis/client": "^5.0.0" + "@redis/client": "^5.0.1" } }, "packages/client": { "name": "@redis/client", - "version": "5.0.0", + "version": "5.0.1", "license": "MIT", "dependencies": { "cluster-key-slot": "1.1.2" @@ -8550,7 +8550,7 @@ }, "packages/entraid": { "name": "@redis/entraid", - "version": "5.0.0", + "version": "5.0.1", "license": "MIT", "dependencies": { "@azure/identity": "^4.7.0", @@ -8569,7 +8569,7 @@ "node": ">= 18" }, "peerDependencies": { - "@redis/client": "^5.0.0" + "@redis/client": "^5.0.1" } }, "packages/entraid/node_modules/@types/node": { @@ -8606,7 +8606,7 @@ }, "packages/json": { "name": "@redis/json", - "version": "5.0.0", + "version": "5.0.1", "license": "MIT", "devDependencies": { "@redis/test-utils": "*" @@ -8615,18 +8615,18 @@ "node": ">= 18" }, "peerDependencies": { - "@redis/client": "^5.0.0" + "@redis/client": "^5.0.1" } }, "packages/redis": { "version": "5.0.0", "license": "MIT", "dependencies": { - "@redis/bloom": "5.0.0", - "@redis/client": "5.0.0", - "@redis/json": "5.0.0", - "@redis/search": "5.0.0", - "@redis/time-series": "5.0.0" + "@redis/bloom": "5.0.1", + "@redis/client": "5.0.1", + "@redis/json": "5.0.1", + "@redis/search": "5.0.1", + "@redis/time-series": "5.0.1" }, "engines": { "node": ">= 18" @@ -8634,7 +8634,7 @@ }, "packages/search": { "name": "@redis/search", - "version": "5.0.0", + "version": "5.0.1", "license": "MIT", "devDependencies": { "@redis/test-utils": "*" @@ -8643,7 +8643,7 @@ "node": ">= 18" }, "peerDependencies": { - "@redis/client": "^5.0.0" + "@redis/client": "^5.0.1" } }, "packages/test-utils": { @@ -8712,7 +8712,7 @@ }, "packages/time-series": { "name": "@redis/time-series", - "version": "5.0.0", + "version": "5.0.1", "license": "MIT", "devDependencies": { "@redis/test-utils": "*" @@ -8721,7 +8721,7 @@ "node": ">= 18" }, "peerDependencies": { - "@redis/client": "^5.0.0" + "@redis/client": "^5.0.1" } } } diff --git a/packages/redis/package.json b/packages/redis/package.json index ddb79ca2275..ab894d5b0fe 100644 --- a/packages/redis/package.json +++ b/packages/redis/package.json @@ -10,11 +10,11 @@ "!dist/tsconfig.tsbuildinfo" ], "dependencies": { - "@redis/bloom": "5.0.0", - "@redis/client": "5.0.0", - "@redis/json": "5.0.0", - "@redis/search": "5.0.0", - "@redis/time-series": "5.0.0" + "@redis/bloom": "5.0.1", + "@redis/client": "5.0.1", + "@redis/json": "5.0.1", + "@redis/search": "5.0.1", + "@redis/time-series": "5.0.1" }, "engines": { "node": ">= 18" From 52e55623568684be0d6d16545cb938808422c5ea Mon Sep 17 00:00:00 2001 From: Nikolay Karadzhov Date: Mon, 5 May 2025 12:28:40 +0300 Subject: [PATCH 1568/1748] Release redis@5.0.1 --- packages/redis/package.json | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/packages/redis/package.json b/packages/redis/package.json index ab894d5b0fe..e7c9da2660b 100644 --- a/packages/redis/package.json +++ b/packages/redis/package.json @@ -1,7 +1,7 @@ { "name": "redis", "description": "A modern, high performance Redis client", - "version": "5.0.0", + "version": "5.0.1", "license": "MIT", "main": "./dist/index.js", "types": "./dist/index.d.ts", From 87b77e3e5f763b1b51c0dbfa9e7bb026a0ee57af Mon Sep 17 00:00:00 2001 From: Nikolay Karadzhov Date: Wed, 7 May 2025 13:47:23 +0300 Subject: [PATCH 1569/1748] fix(client): add type annotations (#2949) Fix type parameter for transformTuplesReply in CONFIG_GET and HGETALL commands fixes #2933 --- packages/client/lib/commands/CONFIG_GET.ts | 2 +- packages/client/lib/commands/HGETALL.ts | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/packages/client/lib/commands/CONFIG_GET.ts b/packages/client/lib/commands/CONFIG_GET.ts index 54fa997bf61..e8339c4d9a0 100644 --- a/packages/client/lib/commands/CONFIG_GET.ts +++ b/packages/client/lib/commands/CONFIG_GET.ts @@ -10,7 +10,7 @@ export default { parser.pushVariadic(parameters); }, transformReply: { - 2: transformTuplesReply, + 2: transformTuplesReply, 3: undefined as unknown as () => MapReply } } as const satisfies Command; diff --git a/packages/client/lib/commands/HGETALL.ts b/packages/client/lib/commands/HGETALL.ts index a2c3011c4c2..8d53669cdd4 100644 --- a/packages/client/lib/commands/HGETALL.ts +++ b/packages/client/lib/commands/HGETALL.ts @@ -11,7 +11,7 @@ export default { }, TRANSFORM_LEGACY_REPLY: true, transformReply: { - 2: transformTuplesReply, + 2: transformTuplesReply, 3: undefined as unknown as () => MapReply } } as const satisfies Command; From bc4b2101ee20aa916883ecd85fbb24f1eb496df5 Mon Sep 17 00:00:00 2001 From: Orgad Shaneh Date: Wed, 7 May 2025 16:10:03 +0300 Subject: [PATCH 1570/1748] Export CommandParser from client index file and fix doc (#2945) * Export CommandParser from client index file * Tidy up long export line in client index file Wrap and sort entries. * Adapt and fix wrong examples in programmability doc --- docs/programmability.md | 10 ++++------ packages/client/index.ts | 11 ++++++++++- 2 files changed, 14 insertions(+), 7 deletions(-) diff --git a/docs/programmability.md b/docs/programmability.md index 56eb048ca0c..c5917c2387d 100644 --- a/docs/programmability.md +++ b/docs/programmability.md @@ -25,9 +25,8 @@ FUNCTION LOAD "#!lua name=library\nredis.register_function{function_name='add', Load the prior redis function on the _redis server_ before running the example below. ```typescript -import { CommandParser } from '@redis/client/lib/client/parser'; -import { NumberReply } from '@redis/client/lib/RESP/types'; -import { createClient, RedisArgument } from 'redis'; +import { CommandParser, createClient, RedisArgument } from '@redis/client'; +import { NumberReply } from '@redis/client/dist/lib/RESP/types.js'; const client = createClient({ functions: { @@ -58,9 +57,8 @@ await client.library.add('key', '2'); // 3 The following is an end-to-end example of the prior concept. ```typescript -import { CommandParser } from '@redis/client/lib/client/parser'; -import { NumberReply } from '@redis/client/lib/RESP/types'; -import { createClient, defineScript, RedisArgument } from 'redis'; +import { CommandParser, createClient, defineScript, RedisArgument } from '@redis/client'; +import { NumberReply } from '@redis/client/dist/lib/RESP/types.js'; const client = createClient({ scripts: { diff --git a/packages/client/index.ts b/packages/client/index.ts index e426badf126..1f05bc30341 100644 --- a/packages/client/index.ts +++ b/packages/client/index.ts @@ -1,4 +1,12 @@ -export { RedisModules, RedisFunctions, RedisScripts, RespVersions, TypeMapping/*, CommandPolicies*/, RedisArgument } from './lib/RESP/types'; +export { + /* CommandPolicies, */ + RedisArgument, + RedisFunctions, + RedisModules, + RedisScripts, + RespVersions, + TypeMapping, +} from './lib/RESP/types'; export { RESP_TYPES } from './lib/RESP/decoder'; export { VerbatimString } from './lib/RESP/verbatim-string'; export { defineScript } from './lib/lua-script'; @@ -7,6 +15,7 @@ export * from './lib/errors'; import RedisClient, { RedisClientOptions, RedisClientType } from './lib/client'; export { RedisClientOptions, RedisClientType }; export const createClient = RedisClient.create; +export { CommandParser } from './lib/client/parser'; import { RedisClientPool, RedisPoolOptions, RedisClientPoolType } from './lib/client/pool'; export { RedisClientPoolType, RedisPoolOptions }; From 7b737821b23a0180bfb5a3b010c2612472b15edc Mon Sep 17 00:00:00 2001 From: Nikolay Karadzhov Date: Wed, 7 May 2025 16:10:35 +0300 Subject: [PATCH 1571/1748] fix: fix various command import issues (#2944) * fix: fix various command import issues there was some sort of a circular dependency in /lib/commands/index.ts for various modules fixes #2937 fixes #2941 * remove redundant definition --- packages/bloom/lib/commands/bloom/INFO.ts | 2 +- packages/bloom/lib/commands/bloom/helpers.ts | 29 ++ packages/bloom/lib/commands/bloom/index.ts | 33 +- packages/client/lib/client/index.ts | 3 +- packages/json/lib/commands/ARRAPPEND.ts | 2 +- packages/json/lib/commands/ARRINDEX.ts | 2 +- packages/json/lib/commands/ARRINSERT.ts | 2 +- packages/json/lib/commands/ARRPOP.ts | 2 +- packages/json/lib/commands/GET.spec.ts | 6 + packages/json/lib/commands/GET.ts | 12 +- packages/json/lib/commands/MERGE.ts | 2 +- packages/json/lib/commands/MGET.ts | 2 +- packages/json/lib/commands/MSET.ts | 2 +- packages/json/lib/commands/SET.ts | 2 +- packages/json/lib/commands/STRAPPEND.ts | 2 +- packages/json/lib/commands/helpers.ts | 22 ++ packages/json/lib/commands/index.ts | 20 +- packages/time-series/lib/commands/ADD.spec.ts | 2 +- packages/time-series/lib/commands/ADD.ts | 2 +- .../time-series/lib/commands/ALTER.spec.ts | 2 +- packages/time-series/lib/commands/ALTER.ts | 3 +- .../time-series/lib/commands/CREATE.spec.ts | 2 +- packages/time-series/lib/commands/CREATE.ts | 2 +- packages/time-series/lib/commands/DEL.ts | 2 +- packages/time-series/lib/commands/INCRBY.ts | 2 +- .../time-series/lib/commands/INFO.spec.ts | 2 +- packages/time-series/lib/commands/INFO.ts | 2 +- .../lib/commands/INFO_DEBUG.spec.ts | 2 +- packages/time-series/lib/commands/MADD.ts | 2 +- packages/time-series/lib/commands/MGET.ts | 2 +- .../lib/commands/MGET_SELECTED_LABELS.ts | 2 +- .../lib/commands/MGET_WITHLABELS.ts | 2 +- packages/time-series/lib/commands/MRANGE.ts | 2 +- .../lib/commands/MRANGE_GROUPBY.ts | 2 +- .../lib/commands/MRANGE_SELECTED_LABELS.ts | 2 +- .../MRANGE_SELECTED_LABELS_GROUPBY.ts | 2 +- .../lib/commands/MRANGE_WITHLABELS.ts | 2 +- .../lib/commands/MRANGE_WITHLABELS_GROUPBY.ts | 2 +- packages/time-series/lib/commands/RANGE.ts | 2 +- packages/time-series/lib/commands/helpers.ts | 306 +++++++++++++++++ .../time-series/lib/commands/index.spec.ts | 2 +- packages/time-series/lib/commands/index.ts | 309 +----------------- 42 files changed, 416 insertions(+), 391 deletions(-) create mode 100644 packages/bloom/lib/commands/bloom/helpers.ts create mode 100644 packages/json/lib/commands/helpers.ts create mode 100644 packages/time-series/lib/commands/helpers.ts diff --git a/packages/bloom/lib/commands/bloom/INFO.ts b/packages/bloom/lib/commands/bloom/INFO.ts index b715bf57738..7074885a41e 100644 --- a/packages/bloom/lib/commands/bloom/INFO.ts +++ b/packages/bloom/lib/commands/bloom/INFO.ts @@ -1,6 +1,6 @@ import { CommandParser } from '@redis/client/dist/lib/client/parser'; import { RedisArgument, Command, UnwrapReply, NullReply, NumberReply, TuplesToMapReply, Resp2Reply, SimpleStringReply, TypeMapping } from '@redis/client/dist/lib/RESP/types'; -import { transformInfoV2Reply } from '.'; +import { transformInfoV2Reply } from './helpers'; export type BfInfoReplyMap = TuplesToMapReply<[ [SimpleStringReply<'Capacity'>, NumberReply], diff --git a/packages/bloom/lib/commands/bloom/helpers.ts b/packages/bloom/lib/commands/bloom/helpers.ts new file mode 100644 index 00000000000..f5b39c71aa8 --- /dev/null +++ b/packages/bloom/lib/commands/bloom/helpers.ts @@ -0,0 +1,29 @@ +import { RESP_TYPES, TypeMapping } from "@redis/client"; + +export function transformInfoV2Reply(reply: Array, typeMapping?: TypeMapping): T { + const mapType = typeMapping ? typeMapping[RESP_TYPES.MAP] : undefined; + + switch (mapType) { + case Array: { + return reply as unknown as T; + } + case Map: { + const ret = new Map(); + + for (let i = 0; i < reply.length; i += 2) { + ret.set(reply[i].toString(), reply[i + 1]); + } + + return ret as unknown as T; + } + default: { + const ret = Object.create(null); + + for (let i = 0; i < reply.length; i += 2) { + ret[reply[i].toString()] = reply[i + 1]; + } + + return ret as unknown as T; + } + } +} \ No newline at end of file diff --git a/packages/bloom/lib/commands/bloom/index.ts b/packages/bloom/lib/commands/bloom/index.ts index a93f79c9c56..d49ac63b2ea 100644 --- a/packages/bloom/lib/commands/bloom/index.ts +++ b/packages/bloom/lib/commands/bloom/index.ts @@ -1,4 +1,4 @@ -import type { RedisCommands, TypeMapping } from '@redis/client/dist/lib/RESP/types'; +import type { RedisCommands } from '@redis/client/dist/lib/RESP/types'; import ADD from './ADD'; import CARD from './CARD'; @@ -10,7 +10,8 @@ import MADD from './MADD'; import MEXISTS from './MEXISTS'; import RESERVE from './RESERVE'; import SCANDUMP from './SCANDUMP'; -import { RESP_TYPES } from '@redis/client'; + +export * from './helpers'; export default { ADD, @@ -34,31 +35,3 @@ export default { SCANDUMP, scanDump: SCANDUMP } as const satisfies RedisCommands; - -export function transformInfoV2Reply(reply: Array, typeMapping?: TypeMapping): T { - const mapType = typeMapping ? typeMapping[RESP_TYPES.MAP] : undefined; - - switch (mapType) { - case Array: { - return reply as unknown as T; - } - case Map: { - const ret = new Map(); - - for (let i = 0; i < reply.length; i += 2) { - ret.set(reply[i].toString(), reply[i + 1]); - } - - return ret as unknown as T; - } - default: { - const ret = Object.create(null); - - for (let i = 0; i < reply.length; i += 2) { - ret[reply[i].toString()] = reply[i + 1]; - } - - return ret as unknown as T; - } - } -} \ No newline at end of file diff --git a/packages/client/lib/client/index.ts b/packages/client/lib/client/index.ts index f48e03d0c19..f9e95025c6a 100644 --- a/packages/client/lib/client/index.ts +++ b/packages/client/lib/client/index.ts @@ -704,7 +704,8 @@ export default class RedisClient< const reply = await this.sendCommand(parser.redisArgs, commandOptions); if (transformReply) { - return transformReply(reply, parser.preserve, commandOptions?.typeMapping); + const res = transformReply(reply, parser.preserve, commandOptions?.typeMapping); + return res } return reply; diff --git a/packages/json/lib/commands/ARRAPPEND.ts b/packages/json/lib/commands/ARRAPPEND.ts index 539eb91a297..d2283b128e3 100644 --- a/packages/json/lib/commands/ARRAPPEND.ts +++ b/packages/json/lib/commands/ARRAPPEND.ts @@ -1,5 +1,5 @@ import { CommandParser } from '@redis/client/dist/lib/client/parser'; -import { RedisJSON, transformRedisJsonArgument } from '.'; +import { RedisJSON, transformRedisJsonArgument } from './helpers'; import { RedisArgument, NumberReply, ArrayReply, NullReply, Command } from '@redis/client/dist/lib/RESP/types'; export default { diff --git a/packages/json/lib/commands/ARRINDEX.ts b/packages/json/lib/commands/ARRINDEX.ts index 23f010b1f0b..6ffcf9f5f0e 100644 --- a/packages/json/lib/commands/ARRINDEX.ts +++ b/packages/json/lib/commands/ARRINDEX.ts @@ -1,6 +1,6 @@ import { CommandParser } from '@redis/client/dist/lib/client/parser'; import { RedisArgument, NumberReply, ArrayReply, NullReply, Command } from '@redis/client/dist/lib/RESP/types'; -import { RedisJSON, transformRedisJsonArgument } from '.'; +import { RedisJSON, transformRedisJsonArgument } from './helpers'; export interface JsonArrIndexOptions { range?: { diff --git a/packages/json/lib/commands/ARRINSERT.ts b/packages/json/lib/commands/ARRINSERT.ts index eb1d7c882f2..e64e0b18559 100644 --- a/packages/json/lib/commands/ARRINSERT.ts +++ b/packages/json/lib/commands/ARRINSERT.ts @@ -1,6 +1,6 @@ import { CommandParser } from '@redis/client/dist/lib/client/parser'; import { RedisArgument, NumberReply, ArrayReply, NullReply, Command } from '@redis/client/dist/lib/RESP/types'; -import { RedisJSON, transformRedisJsonArgument } from '.'; +import { RedisJSON, transformRedisJsonArgument } from './helpers'; export default { IS_READ_ONLY: false, diff --git a/packages/json/lib/commands/ARRPOP.ts b/packages/json/lib/commands/ARRPOP.ts index 5f1489c3bd4..30ed34c37be 100644 --- a/packages/json/lib/commands/ARRPOP.ts +++ b/packages/json/lib/commands/ARRPOP.ts @@ -1,7 +1,7 @@ import { CommandParser } from '@redis/client/dist/lib/client/parser'; import { RedisArgument, ArrayReply, NullReply, BlobStringReply, Command, UnwrapReply } from '@redis/client/dist/lib/RESP/types'; import { isArrayReply } from '@redis/client/dist/lib/commands/generic-transformers'; -import { transformRedisJsonNullReply } from '.'; +import { transformRedisJsonNullReply } from './helpers'; export interface RedisArrPopOptions { path: RedisArgument; diff --git a/packages/json/lib/commands/GET.spec.ts b/packages/json/lib/commands/GET.spec.ts index 0741de316e1..6b4f44871cb 100644 --- a/packages/json/lib/commands/GET.spec.ts +++ b/packages/json/lib/commands/GET.spec.ts @@ -34,5 +34,11 @@ describe('JSON.GET', () => { await client.json.get('key'), null ); + + await client.json.set('noderedis:users:1', '$', { name: 'Alice', age: 32, }) + const res = await client.json.get('noderedis:users:1'); + assert.equal(typeof res, 'object') + assert.deepEqual(res, { name: 'Alice', age: 32, }) + }, GLOBAL.SERVERS.OPEN); }); diff --git a/packages/json/lib/commands/GET.ts b/packages/json/lib/commands/GET.ts index d43d7464c50..6705ac534bc 100644 --- a/packages/json/lib/commands/GET.ts +++ b/packages/json/lib/commands/GET.ts @@ -1,7 +1,7 @@ import { CommandParser } from '@redis/client/dist/lib/client/parser'; import { RedisArgument, Command } from '@redis/client/dist/lib/RESP/types'; import { RedisVariadicArgument } from '@redis/client/dist/lib/commands/generic-transformers'; -import { transformRedisJsonNullReply } from '.'; +import { transformRedisJsonNullReply } from './helpers'; export interface JsonGetOptions { path?: RedisVariadicArgument; @@ -9,12 +9,16 @@ export interface JsonGetOptions { export default { IS_READ_ONLY: false, - parseCommand(parser: CommandParser, key: RedisArgument, options?: JsonGetOptions) { + parseCommand( + parser: CommandParser, + key: RedisArgument, + options?: JsonGetOptions + ) { parser.push('JSON.GET'); parser.pushKey(key); if (options?.path !== undefined) { - parser.pushVariadic(options.path) + parser.pushVariadic(options.path); } }, transformReply: transformRedisJsonNullReply -} as const satisfies Command; +} as const satisfies Command; \ No newline at end of file diff --git a/packages/json/lib/commands/MERGE.ts b/packages/json/lib/commands/MERGE.ts index 0cb8131a68c..3c93913f91a 100644 --- a/packages/json/lib/commands/MERGE.ts +++ b/packages/json/lib/commands/MERGE.ts @@ -1,6 +1,6 @@ import { CommandParser } from '@redis/client/dist/lib/client/parser'; import { SimpleStringReply, Command, RedisArgument } from '@redis/client/dist/lib/RESP/types'; -import { RedisJSON, transformRedisJsonArgument } from '.'; +import { RedisJSON, transformRedisJsonArgument } from './helpers'; export default { IS_READ_ONLY: false, diff --git a/packages/json/lib/commands/MGET.ts b/packages/json/lib/commands/MGET.ts index 447de064d2b..d0fc0a99089 100644 --- a/packages/json/lib/commands/MGET.ts +++ b/packages/json/lib/commands/MGET.ts @@ -1,6 +1,6 @@ import { CommandParser } from '@redis/client/dist/lib/client/parser'; import { RedisArgument, UnwrapReply, ArrayReply, NullReply, BlobStringReply, Command } from '@redis/client/dist/lib/RESP/types'; -import { transformRedisJsonNullReply } from '.'; +import { transformRedisJsonNullReply } from './helpers'; export default { IS_READ_ONLY: true, diff --git a/packages/json/lib/commands/MSET.ts b/packages/json/lib/commands/MSET.ts index cb0bea26ddd..2dfab142493 100644 --- a/packages/json/lib/commands/MSET.ts +++ b/packages/json/lib/commands/MSET.ts @@ -1,6 +1,6 @@ import { CommandParser } from '@redis/client/dist/lib/client/parser'; import { RedisArgument, SimpleStringReply, Command } from '@redis/client/dist/lib/RESP/types'; -import { RedisJSON, transformRedisJsonArgument } from '.'; +import { RedisJSON, transformRedisJsonArgument } from './helpers'; export interface JsonMSetItem { key: RedisArgument; diff --git a/packages/json/lib/commands/SET.ts b/packages/json/lib/commands/SET.ts index 75d7099acfb..27da2ec64ee 100644 --- a/packages/json/lib/commands/SET.ts +++ b/packages/json/lib/commands/SET.ts @@ -1,6 +1,6 @@ import { CommandParser } from '@redis/client/dist/lib/client/parser'; import { RedisArgument, SimpleStringReply, NullReply, Command } from '@redis/client/dist/lib/RESP/types'; -import { RedisJSON, transformRedisJsonArgument } from '.'; +import { RedisJSON, transformRedisJsonArgument } from './helpers'; export interface JsonSetOptions { condition?: 'NX' | 'XX'; diff --git a/packages/json/lib/commands/STRAPPEND.ts b/packages/json/lib/commands/STRAPPEND.ts index 45d503856ac..3c0e5767549 100644 --- a/packages/json/lib/commands/STRAPPEND.ts +++ b/packages/json/lib/commands/STRAPPEND.ts @@ -1,6 +1,6 @@ import { CommandParser } from '@redis/client/dist/lib/client/parser'; import { RedisArgument, Command, NullReply, NumberReply, ArrayReply } from '@redis/client/dist/lib/RESP/types'; -import { transformRedisJsonArgument } from '.'; +import { transformRedisJsonArgument } from './helpers'; export interface JsonStrAppendOptions { path?: RedisArgument; diff --git a/packages/json/lib/commands/helpers.ts b/packages/json/lib/commands/helpers.ts new file mode 100644 index 00000000000..26ff12f6834 --- /dev/null +++ b/packages/json/lib/commands/helpers.ts @@ -0,0 +1,22 @@ +import { isNullReply } from "@redis/client/dist/lib/commands/generic-transformers"; +import { BlobStringReply, NullReply, UnwrapReply } from "@redis/client/dist/lib/RESP/types"; + +export function transformRedisJsonNullReply(json: NullReply | BlobStringReply): NullReply | RedisJSON { + console.log('transformRedisJsonNullReply', json) + return isNullReply(json) ? json : transformRedisJsonReply(json); +} + +export type RedisJSON = null | boolean | number | string | Date | Array | { + [key: string]: RedisJSON; + [key: number]: RedisJSON; +}; + +export function transformRedisJsonArgument(json: RedisJSON): string { + return JSON.stringify(json); +} + +export function transformRedisJsonReply(json: BlobStringReply): RedisJSON { + const res = JSON.parse((json as unknown as UnwrapReply).toString()); + console.log('transformRedisJsonReply', json, res) + return res; +} diff --git a/packages/json/lib/commands/index.ts b/packages/json/lib/commands/index.ts index 2724ff2565c..a9e16bde757 100644 --- a/packages/json/lib/commands/index.ts +++ b/packages/json/lib/commands/index.ts @@ -1,4 +1,3 @@ -import { BlobStringReply, NullReply, UnwrapReply } from '@redis/client/dist/lib/RESP/types'; import ARRAPPEND from './ARRAPPEND'; import ARRINDEX from './ARRINDEX'; import ARRINSERT from './ARRINSERT'; @@ -23,7 +22,8 @@ import STRAPPEND from './STRAPPEND'; import STRLEN from './STRLEN'; import TOGGLE from './TOGGLE'; import TYPE from './TYPE'; -import { isNullReply } from '@redis/client/dist/lib/commands/generic-transformers'; + +export * from './helpers'; export default { ARRAPPEND, @@ -82,19 +82,3 @@ export default { type: TYPE }; -export type RedisJSON = null | boolean | number | string | Date | Array | { - [key: string]: RedisJSON; - [key: number]: RedisJSON; -}; - -export function transformRedisJsonArgument(json: RedisJSON): string { - return JSON.stringify(json); -} - -export function transformRedisJsonReply(json: BlobStringReply): RedisJSON { - return JSON.parse((json as unknown as UnwrapReply).toString()); -} - -export function transformRedisJsonNullReply(json: NullReply | BlobStringReply): NullReply | RedisJSON { - return isNullReply(json) ? json : transformRedisJsonReply(json); -} diff --git a/packages/time-series/lib/commands/ADD.spec.ts b/packages/time-series/lib/commands/ADD.spec.ts index 055d2246d8b..d66c85441a1 100644 --- a/packages/time-series/lib/commands/ADD.spec.ts +++ b/packages/time-series/lib/commands/ADD.spec.ts @@ -1,7 +1,7 @@ import { strict as assert } from 'node:assert'; import testUtils, { GLOBAL } from '../test-utils'; import ADD from './ADD'; -import { TIME_SERIES_ENCODING, TIME_SERIES_DUPLICATE_POLICIES } from '.'; +import { TIME_SERIES_ENCODING, TIME_SERIES_DUPLICATE_POLICIES } from './helpers'; import { parseArgs } from '@redis/client/lib/commands/generic-transformers'; describe('TS.ADD', () => { diff --git a/packages/time-series/lib/commands/ADD.ts b/packages/time-series/lib/commands/ADD.ts index e7626d227da..0f254339ff9 100644 --- a/packages/time-series/lib/commands/ADD.ts +++ b/packages/time-series/lib/commands/ADD.ts @@ -11,7 +11,7 @@ import { parseLabelsArgument, Timestamp, parseIgnoreArgument -} from '.'; +} from './helpers'; export interface TsIgnoreOptions { maxTimeDiff: number; diff --git a/packages/time-series/lib/commands/ALTER.spec.ts b/packages/time-series/lib/commands/ALTER.spec.ts index 560d9ffde2c..46b94c5863a 100644 --- a/packages/time-series/lib/commands/ALTER.spec.ts +++ b/packages/time-series/lib/commands/ALTER.spec.ts @@ -1,7 +1,7 @@ import { strict as assert } from 'node:assert'; import testUtils, { GLOBAL } from '../test-utils'; import ALTER from './ALTER'; -import { TIME_SERIES_DUPLICATE_POLICIES } from '.'; +import { TIME_SERIES_DUPLICATE_POLICIES } from './helpers'; import { parseArgs } from '@redis/client/lib/commands/generic-transformers'; describe('TS.ALTER', () => { diff --git a/packages/time-series/lib/commands/ALTER.ts b/packages/time-series/lib/commands/ALTER.ts index f7f6948da71..29f99290a52 100644 --- a/packages/time-series/lib/commands/ALTER.ts +++ b/packages/time-series/lib/commands/ALTER.ts @@ -1,7 +1,8 @@ import { CommandParser } from '@redis/client/dist/lib/client/parser'; import { RedisArgument, SimpleStringReply, Command } from '@redis/client/dist/lib/RESP/types'; import { TsCreateOptions } from './CREATE'; -import { parseRetentionArgument, parseChunkSizeArgument, parseDuplicatePolicy, parseLabelsArgument, parseIgnoreArgument } from '.'; +import { parseRetentionArgument, parseChunkSizeArgument, parseDuplicatePolicy, parseLabelsArgument, parseIgnoreArgument } from './helpers'; + export type TsAlterOptions = Pick; diff --git a/packages/time-series/lib/commands/CREATE.spec.ts b/packages/time-series/lib/commands/CREATE.spec.ts index 795b59b880d..4fbfabb6858 100644 --- a/packages/time-series/lib/commands/CREATE.spec.ts +++ b/packages/time-series/lib/commands/CREATE.spec.ts @@ -1,7 +1,7 @@ import { strict as assert } from 'node:assert'; import testUtils, { GLOBAL } from '../test-utils'; import CREATE from './CREATE'; -import { TIME_SERIES_ENCODING, TIME_SERIES_DUPLICATE_POLICIES } from '.'; +import { TIME_SERIES_ENCODING, TIME_SERIES_DUPLICATE_POLICIES } from './helpers'; import { parseArgs } from '@redis/client/lib/commands/generic-transformers'; describe('TS.CREATE', () => { diff --git a/packages/time-series/lib/commands/CREATE.ts b/packages/time-series/lib/commands/CREATE.ts index 39f35c06ed7..c499a752f23 100644 --- a/packages/time-series/lib/commands/CREATE.ts +++ b/packages/time-series/lib/commands/CREATE.ts @@ -10,7 +10,7 @@ import { Labels, parseLabelsArgument, parseIgnoreArgument -} from '.'; +} from './helpers'; import { TsIgnoreOptions } from './ADD'; export interface TsCreateOptions { diff --git a/packages/time-series/lib/commands/DEL.ts b/packages/time-series/lib/commands/DEL.ts index fc96c989b18..de9cadf88c9 100644 --- a/packages/time-series/lib/commands/DEL.ts +++ b/packages/time-series/lib/commands/DEL.ts @@ -1,5 +1,5 @@ import { CommandParser } from '@redis/client/dist/lib/client/parser'; -import { Timestamp, transformTimestampArgument } from '.'; +import { Timestamp, transformTimestampArgument } from './helpers'; import { RedisArgument, NumberReply, Command, } from '@redis/client/dist/lib/RESP/types'; export default { diff --git a/packages/time-series/lib/commands/INCRBY.ts b/packages/time-series/lib/commands/INCRBY.ts index e62ec42690a..2365f716a83 100644 --- a/packages/time-series/lib/commands/INCRBY.ts +++ b/packages/time-series/lib/commands/INCRBY.ts @@ -1,6 +1,6 @@ import { CommandParser } from '@redis/client/dist/lib/client/parser'; import { RedisArgument, NumberReply, Command } from '@redis/client/dist/lib/RESP/types'; -import { Timestamp, transformTimestampArgument, parseRetentionArgument, parseChunkSizeArgument, Labels, parseLabelsArgument, parseIgnoreArgument } from '.'; +import { Timestamp, transformTimestampArgument, parseRetentionArgument, parseChunkSizeArgument, Labels, parseLabelsArgument, parseIgnoreArgument } from './helpers'; import { TsIgnoreOptions } from './ADD'; export interface TsIncrByOptions { diff --git a/packages/time-series/lib/commands/INFO.spec.ts b/packages/time-series/lib/commands/INFO.spec.ts index 73b9d8dc930..994cb281915 100644 --- a/packages/time-series/lib/commands/INFO.spec.ts +++ b/packages/time-series/lib/commands/INFO.spec.ts @@ -1,5 +1,5 @@ import { strict as assert } from 'node:assert'; -import { TIME_SERIES_DUPLICATE_POLICIES } from '.'; +import { TIME_SERIES_DUPLICATE_POLICIES } from './helpers'; import testUtils, { GLOBAL } from '../test-utils'; import INFO, { InfoReply } from './INFO'; import { TIME_SERIES_AGGREGATION_TYPE } from './CREATERULE'; diff --git a/packages/time-series/lib/commands/INFO.ts b/packages/time-series/lib/commands/INFO.ts index fe0e49e095a..62cc1108a80 100644 --- a/packages/time-series/lib/commands/INFO.ts +++ b/packages/time-series/lib/commands/INFO.ts @@ -1,6 +1,6 @@ import { CommandParser } from '@redis/client/dist/lib/client/parser'; import { ArrayReply, BlobStringReply, Command, DoubleReply, NumberReply, ReplyUnion, SimpleStringReply, TypeMapping } from "@redis/client/dist/lib/RESP/types"; -import { TimeSeriesDuplicatePolicies } from "."; +import { TimeSeriesDuplicatePolicies } from "./helpers"; import { TimeSeriesAggregationType } from "./CREATERULE"; import { transformDoubleReply } from '@redis/client/dist/lib/commands/generic-transformers'; diff --git a/packages/time-series/lib/commands/INFO_DEBUG.spec.ts b/packages/time-series/lib/commands/INFO_DEBUG.spec.ts index 063b9126550..ff9d6aa3c72 100644 --- a/packages/time-series/lib/commands/INFO_DEBUG.spec.ts +++ b/packages/time-series/lib/commands/INFO_DEBUG.spec.ts @@ -1,5 +1,5 @@ import { strict as assert } from 'node:assert'; -import { TIME_SERIES_DUPLICATE_POLICIES } from '.'; +import { TIME_SERIES_DUPLICATE_POLICIES } from './helpers'; import testUtils, { GLOBAL } from '../test-utils'; import { assertInfo } from './INFO.spec'; import INFO_DEBUG from './INFO_DEBUG'; diff --git a/packages/time-series/lib/commands/MADD.ts b/packages/time-series/lib/commands/MADD.ts index 5af94d6d497..b4c91a98384 100644 --- a/packages/time-series/lib/commands/MADD.ts +++ b/packages/time-series/lib/commands/MADD.ts @@ -1,5 +1,5 @@ import { CommandParser } from '@redis/client/dist/lib/client/parser'; -import { Timestamp, transformTimestampArgument } from '.'; +import { Timestamp, transformTimestampArgument } from './helpers'; import { ArrayReply, NumberReply, SimpleErrorReply, Command } from '@redis/client/dist/lib/RESP/types'; export interface TsMAddSample { diff --git a/packages/time-series/lib/commands/MGET.ts b/packages/time-series/lib/commands/MGET.ts index fa4e3fc63d6..fd5e8c71b93 100644 --- a/packages/time-series/lib/commands/MGET.ts +++ b/packages/time-series/lib/commands/MGET.ts @@ -1,6 +1,6 @@ import { CommandParser } from '@redis/client/dist/lib/client/parser'; import { Command, BlobStringReply, ArrayReply, Resp2Reply, MapReply, TuplesReply, TypeMapping } from '@redis/client/dist/lib/RESP/types'; -import { resp2MapToValue, resp3MapToValue, SampleRawReply, transformSampleReply } from '.'; +import { resp2MapToValue, resp3MapToValue, SampleRawReply, transformSampleReply } from './helpers'; import { RedisVariadicArgument } from '@redis/client/dist/lib/commands/generic-transformers'; export interface TsMGetOptions { diff --git a/packages/time-series/lib/commands/MGET_SELECTED_LABELS.ts b/packages/time-series/lib/commands/MGET_SELECTED_LABELS.ts index e93b517f80a..d74d073c174 100644 --- a/packages/time-series/lib/commands/MGET_SELECTED_LABELS.ts +++ b/packages/time-series/lib/commands/MGET_SELECTED_LABELS.ts @@ -2,7 +2,7 @@ import { CommandParser } from '@redis/client/dist/lib/client/parser'; import { Command, BlobStringReply, NullReply } from '@redis/client/dist/lib/RESP/types'; import { RedisVariadicArgument } from '@redis/client/dist/lib/commands/generic-transformers'; import { TsMGetOptions, parseLatestArgument, parseFilterArgument } from './MGET'; -import { parseSelectedLabelsArguments } from '.'; +import { parseSelectedLabelsArguments } from './helpers'; import { createTransformMGetLabelsReply } from './MGET_WITHLABELS'; export default { diff --git a/packages/time-series/lib/commands/MGET_WITHLABELS.ts b/packages/time-series/lib/commands/MGET_WITHLABELS.ts index 38b8442db31..737e7236130 100644 --- a/packages/time-series/lib/commands/MGET_WITHLABELS.ts +++ b/packages/time-series/lib/commands/MGET_WITHLABELS.ts @@ -2,7 +2,7 @@ import { CommandParser } from '@redis/client/dist/lib/client/parser'; import { Command, BlobStringReply, ArrayReply, Resp2Reply, MapReply, TuplesReply, TypeMapping } from '@redis/client/dist/lib/RESP/types'; import { RedisVariadicArgument } from '@redis/client/dist/lib/commands/generic-transformers'; import { TsMGetOptions, parseLatestArgument, parseFilterArgument } from './MGET'; -import { RawLabelValue, resp2MapToValue, resp3MapToValue, SampleRawReply, transformRESP2Labels, transformSampleReply } from '.'; +import { RawLabelValue, resp2MapToValue, resp3MapToValue, SampleRawReply, transformRESP2Labels, transformSampleReply } from './helpers'; export interface TsMGetWithLabelsOptions extends TsMGetOptions { SELECTED_LABELS?: RedisVariadicArgument; diff --git a/packages/time-series/lib/commands/MRANGE.ts b/packages/time-series/lib/commands/MRANGE.ts index 95fa5297bdd..3351b755499 100644 --- a/packages/time-series/lib/commands/MRANGE.ts +++ b/packages/time-series/lib/commands/MRANGE.ts @@ -1,7 +1,7 @@ import { CommandParser } from '@redis/client/dist/lib/client/parser'; import { Command, ArrayReply, BlobStringReply, Resp2Reply, MapReply, TuplesReply, TypeMapping, RedisArgument } from '@redis/client/dist/lib/RESP/types'; import { RedisVariadicArgument } from '@redis/client/dist/lib/commands/generic-transformers'; -import { resp2MapToValue, resp3MapToValue, SampleRawReply, Timestamp, transformSamplesReply } from '.'; +import { resp2MapToValue, resp3MapToValue, SampleRawReply, Timestamp, transformSamplesReply } from './helpers'; import { TsRangeOptions, parseRangeArguments } from './RANGE'; import { parseFilterArgument } from './MGET'; diff --git a/packages/time-series/lib/commands/MRANGE_GROUPBY.ts b/packages/time-series/lib/commands/MRANGE_GROUPBY.ts index 5ccd61b2a26..74279d00b6d 100644 --- a/packages/time-series/lib/commands/MRANGE_GROUPBY.ts +++ b/packages/time-series/lib/commands/MRANGE_GROUPBY.ts @@ -1,7 +1,7 @@ import { CommandParser } from '@redis/client/dist/lib/client/parser'; import { Command, ArrayReply, BlobStringReply, Resp2Reply, MapReply, TuplesReply, TypeMapping, RedisArgument, TuplesToMapReply, UnwrapReply } from '@redis/client/dist/lib/RESP/types'; import { RedisVariadicArgument } from '@redis/client/dist/lib/commands/generic-transformers'; -import { resp2MapToValue, resp3MapToValue, SampleRawReply, Timestamp, transformSamplesReply } from '.'; +import { resp2MapToValue, resp3MapToValue, SampleRawReply, Timestamp, transformSamplesReply } from './helpers'; import { TsRangeOptions, parseRangeArguments } from './RANGE'; import { parseFilterArgument } from './MGET'; diff --git a/packages/time-series/lib/commands/MRANGE_SELECTED_LABELS.ts b/packages/time-series/lib/commands/MRANGE_SELECTED_LABELS.ts index 643b57a67e7..75affc54aeb 100644 --- a/packages/time-series/lib/commands/MRANGE_SELECTED_LABELS.ts +++ b/packages/time-series/lib/commands/MRANGE_SELECTED_LABELS.ts @@ -1,7 +1,7 @@ import { CommandParser } from '@redis/client/dist/lib/client/parser'; import { Command, ArrayReply, BlobStringReply, Resp2Reply, MapReply, TuplesReply, TypeMapping, NullReply, RedisArgument } from '@redis/client/dist/lib/RESP/types'; import { RedisVariadicArgument } from '@redis/client/dist/lib/commands/generic-transformers'; -import { parseSelectedLabelsArguments, resp2MapToValue, resp3MapToValue, SampleRawReply, Timestamp, transformRESP2Labels, transformSamplesReply } from '.'; +import { parseSelectedLabelsArguments, resp2MapToValue, resp3MapToValue, SampleRawReply, Timestamp, transformRESP2Labels, transformSamplesReply } from './helpers'; import { TsRangeOptions, parseRangeArguments } from './RANGE'; import { parseFilterArgument } from './MGET'; diff --git a/packages/time-series/lib/commands/MRANGE_SELECTED_LABELS_GROUPBY.ts b/packages/time-series/lib/commands/MRANGE_SELECTED_LABELS_GROUPBY.ts index c5cf1ef56c5..99429a9bb76 100644 --- a/packages/time-series/lib/commands/MRANGE_SELECTED_LABELS_GROUPBY.ts +++ b/packages/time-series/lib/commands/MRANGE_SELECTED_LABELS_GROUPBY.ts @@ -1,7 +1,7 @@ import { CommandParser } from '@redis/client/dist/lib/client/parser'; import { Command, ArrayReply, BlobStringReply, MapReply, TuplesReply, RedisArgument, NullReply } from '@redis/client/dist/lib/RESP/types'; import { RedisVariadicArgument } from '@redis/client/dist/lib/commands/generic-transformers'; -import { parseSelectedLabelsArguments, resp3MapToValue, SampleRawReply, Timestamp, transformSamplesReply } from '.'; +import { parseSelectedLabelsArguments, resp3MapToValue, SampleRawReply, Timestamp, transformSamplesReply } from './helpers'; import { TsRangeOptions, parseRangeArguments } from './RANGE'; import { extractResp3MRangeSources, parseGroupByArguments, TsMRangeGroupBy, TsMRangeGroupByRawMetadataReply3 } from './MRANGE_GROUPBY'; import { parseFilterArgument } from './MGET'; diff --git a/packages/time-series/lib/commands/MRANGE_WITHLABELS.ts b/packages/time-series/lib/commands/MRANGE_WITHLABELS.ts index 19641596a67..ef4864a0307 100644 --- a/packages/time-series/lib/commands/MRANGE_WITHLABELS.ts +++ b/packages/time-series/lib/commands/MRANGE_WITHLABELS.ts @@ -1,7 +1,7 @@ import { CommandParser } from '@redis/client/dist/lib/client/parser'; import { Command, UnwrapReply, ArrayReply, BlobStringReply, Resp2Reply, MapReply, TuplesReply, TypeMapping, RedisArgument } from '@redis/client/dist/lib/RESP/types'; import { RedisVariadicArgument } from '@redis/client/dist/lib/commands/generic-transformers'; -import { resp2MapToValue, resp3MapToValue, SampleRawReply, Timestamp, transformSamplesReply } from '.'; +import { resp2MapToValue, resp3MapToValue, SampleRawReply, Timestamp, transformSamplesReply } from './helpers'; import { TsRangeOptions, parseRangeArguments } from './RANGE'; import { parseFilterArgument } from './MGET'; diff --git a/packages/time-series/lib/commands/MRANGE_WITHLABELS_GROUPBY.ts b/packages/time-series/lib/commands/MRANGE_WITHLABELS_GROUPBY.ts index ff0065e22b7..6552f6328e8 100644 --- a/packages/time-series/lib/commands/MRANGE_WITHLABELS_GROUPBY.ts +++ b/packages/time-series/lib/commands/MRANGE_WITHLABELS_GROUPBY.ts @@ -1,7 +1,7 @@ import { CommandParser } from '@redis/client/dist/lib/client/parser'; import { Command, ArrayReply, BlobStringReply, Resp2Reply, MapReply, TuplesReply, TypeMapping, RedisArgument } from '@redis/client/dist/lib/RESP/types'; import { RedisVariadicArgument } from '@redis/client/dist/lib/commands/generic-transformers'; -import { resp2MapToValue, resp3MapToValue, SampleRawReply, Timestamp, transformRESP2LabelsWithSources, transformSamplesReply } from '.'; +import { resp2MapToValue, resp3MapToValue, SampleRawReply, Timestamp, transformRESP2LabelsWithSources, transformSamplesReply } from './helpers'; import { TsRangeOptions, parseRangeArguments } from './RANGE'; import { extractResp3MRangeSources, parseGroupByArguments, TsMRangeGroupBy, TsMRangeGroupByRawMetadataReply3 } from './MRANGE_GROUPBY'; import { parseFilterArgument } from './MGET'; diff --git a/packages/time-series/lib/commands/RANGE.ts b/packages/time-series/lib/commands/RANGE.ts index f7f808cecdb..44da30d81de 100644 --- a/packages/time-series/lib/commands/RANGE.ts +++ b/packages/time-series/lib/commands/RANGE.ts @@ -1,6 +1,6 @@ import { CommandParser } from '@redis/client/dist/lib/client/parser'; import { RedisArgument, Command } from '@redis/client/dist/lib/RESP/types'; -import { Timestamp, transformTimestampArgument, SamplesRawReply, transformSamplesReply } from '.'; +import { Timestamp, transformTimestampArgument, SamplesRawReply, transformSamplesReply } from './helpers'; import { TimeSeriesAggregationType } from './CREATERULE'; import { Resp2Reply } from '@redis/client/dist/lib/RESP/types'; diff --git a/packages/time-series/lib/commands/helpers.ts b/packages/time-series/lib/commands/helpers.ts new file mode 100644 index 00000000000..3e277d0747d --- /dev/null +++ b/packages/time-series/lib/commands/helpers.ts @@ -0,0 +1,306 @@ +import { CommandParser } from "@redis/client/dist/lib/client/parser"; +import { TsIgnoreOptions } from "./ADD"; +import { ArrayReply, BlobStringReply, DoubleReply, MapReply, NullReply, NumberReply, ReplyUnion, Resp2Reply, RespType, TuplesReply, TypeMapping, UnwrapReply } from "@redis/client/dist/lib/RESP/types"; +import { RESP_TYPES } from "@redis/client"; +import { RedisVariadicArgument } from "@redis/client/dist/lib/commands/generic-transformers"; + +export function parseIgnoreArgument(parser: CommandParser, ignore?: TsIgnoreOptions) { + if (ignore !== undefined) { + parser.push('IGNORE', ignore.maxTimeDiff.toString(), ignore.maxValDiff.toString()); + } +} + +export function parseRetentionArgument(parser: CommandParser, retention?: number) { + if (retention !== undefined) { + parser.push('RETENTION', retention.toString()); + } +} + +export const TIME_SERIES_ENCODING = { + COMPRESSED: 'COMPRESSED', + UNCOMPRESSED: 'UNCOMPRESSED' +} as const; + +export type TimeSeriesEncoding = typeof TIME_SERIES_ENCODING[keyof typeof TIME_SERIES_ENCODING]; + +export function parseEncodingArgument(parser: CommandParser, encoding?: TimeSeriesEncoding) { + if (encoding !== undefined) { + parser.push('ENCODING', encoding); + } +} + +export function parseChunkSizeArgument(parser: CommandParser, chunkSize?: number) { + if (chunkSize !== undefined) { + parser.push('CHUNK_SIZE', chunkSize.toString()); + } +} + +export const TIME_SERIES_DUPLICATE_POLICIES = { + BLOCK: 'BLOCK', + FIRST: 'FIRST', + LAST: 'LAST', + MIN: 'MIN', + MAX: 'MAX', + SUM: 'SUM' +} as const; + +export type TimeSeriesDuplicatePolicies = typeof TIME_SERIES_DUPLICATE_POLICIES[keyof typeof TIME_SERIES_DUPLICATE_POLICIES]; + +export function parseDuplicatePolicy(parser: CommandParser, duplicatePolicy?: TimeSeriesDuplicatePolicies) { + if (duplicatePolicy !== undefined) { + parser.push('DUPLICATE_POLICY', duplicatePolicy); + } +} + +export type Timestamp = number | Date | string; + +export function transformTimestampArgument(timestamp: Timestamp): string { + if (typeof timestamp === 'string') return timestamp; + + return ( + typeof timestamp === 'number' ? + timestamp : + timestamp.getTime() + ).toString(); +} + +export type Labels = { + [label: string]: string; +}; + +export function parseLabelsArgument(parser: CommandParser, labels?: Labels) { + if (labels) { + parser.push('LABELS'); + + for (const [label, value] of Object.entries(labels)) { + parser.push(label, value); + } + } +} + +export type SampleRawReply = TuplesReply<[timestamp: NumberReply, value: DoubleReply]>; + +export const transformSampleReply = { + 2(reply: Resp2Reply) { + const [ timestamp, value ] = reply as unknown as UnwrapReply; + return { + timestamp, + value: Number(value) // TODO: use double type mapping instead + }; + }, + 3(reply: SampleRawReply) { + const [ timestamp, value ] = reply as unknown as UnwrapReply; + return { + timestamp, + value + }; + } +}; + +export type SamplesRawReply = ArrayReply; + +export const transformSamplesReply = { + 2(reply: Resp2Reply) { + return (reply as unknown as UnwrapReply) + .map(sample => transformSampleReply[2](sample)); + }, + 3(reply: SamplesRawReply) { + return (reply as unknown as UnwrapReply) + .map(sample => transformSampleReply[3](sample)); + } +}; + +// TODO: move to @redis/client? +export function resp2MapToValue< + RAW_VALUE extends TuplesReply<[key: BlobStringReply, ...rest: Array]>, + TRANSFORMED +>( + wrappedReply: ArrayReply, + parseFunc: (rawValue: UnwrapReply) => TRANSFORMED, + typeMapping?: TypeMapping +): MapReply { + const reply = wrappedReply as unknown as UnwrapReply; + switch (typeMapping?.[RESP_TYPES.MAP]) { + case Map: { + const ret = new Map(); + for (const wrappedTuple of reply) { + const tuple = wrappedTuple as unknown as UnwrapReply; + const key = tuple[0] as unknown as UnwrapReply; + ret.set(key.toString(), parseFunc(tuple)); + } + return ret as never; + } + case Array: { + for (const wrappedTuple of reply) { + const tuple = wrappedTuple as unknown as UnwrapReply; + (tuple[1] as unknown as TRANSFORMED) = parseFunc(tuple); + } + return reply as never; + } + default: { + const ret: Record = Object.create(null); + for (const wrappedTuple of reply) { + const tuple = wrappedTuple as unknown as UnwrapReply; + const key = tuple[0] as unknown as UnwrapReply; + ret[key.toString()] = parseFunc(tuple); + } + return ret as never; + } + } +} + +export function resp3MapToValue< + RAW_VALUE extends RespType, // TODO: simplify types + TRANSFORMED +>( + wrappedReply: MapReply, + parseFunc: (rawValue: UnwrapReply) => TRANSFORMED +): MapReply { + const reply = wrappedReply as unknown as UnwrapReply; + if (reply instanceof Array) { + for (let i = 1; i < reply.length; i += 2) { + (reply[i] as unknown as TRANSFORMED) = parseFunc(reply[i] as unknown as UnwrapReply); + } + } else if (reply instanceof Map) { + for (const [key, value] of reply.entries()) { + (reply as unknown as Map).set( + key, + parseFunc(value as unknown as UnwrapReply) + ); + } + } else { + for (const [key, value] of Object.entries(reply)) { + (reply[key] as unknown as TRANSFORMED) = parseFunc(value as unknown as UnwrapReply); + } + } + return reply as never; +} + +export function parseSelectedLabelsArguments( + parser: CommandParser, + selectedLabels: RedisVariadicArgument +) { + parser.push('SELECTED_LABELS'); + parser.pushVariadic(selectedLabels); +} + +export type RawLabelValue = BlobStringReply | NullReply; + +export type RawLabels = ArrayReply>; + +export function transformRESP2Labels( + labels: RawLabels, + typeMapping?: TypeMapping +): MapReply { + const unwrappedLabels = labels as unknown as UnwrapReply; + switch (typeMapping?.[RESP_TYPES.MAP]) { + case Map: + const map = new Map(); + for (const tuple of unwrappedLabels) { + const [key, value] = tuple as unknown as UnwrapReply; + const unwrappedKey = key as unknown as UnwrapReply; + map.set(unwrappedKey.toString(), value); + } + return map as never; + + case Array: + return unwrappedLabels.flat() as never; + + case Object: + default: + const labelsObject: Record = Object.create(null); + for (const tuple of unwrappedLabels) { + const [key, value] = tuple as unknown as UnwrapReply; + const unwrappedKey = key as unknown as UnwrapReply; + labelsObject[unwrappedKey.toString()] = value; + } + return labelsObject as never; + } +} + +export function transformRESP2LabelsWithSources( + labels: RawLabels, + typeMapping?: TypeMapping +) { + const unwrappedLabels = labels as unknown as UnwrapReply; + const to = unwrappedLabels.length - 2; // ignore __reducer__ and __source__ + let transformedLabels: MapReply; + switch (typeMapping?.[RESP_TYPES.MAP]) { + case Map: + const map = new Map(); + for (let i = 0; i < to; i++) { + const [key, value] = unwrappedLabels[i] as unknown as UnwrapReply; + const unwrappedKey = key as unknown as UnwrapReply; + map.set(unwrappedKey.toString(), value); + } + transformedLabels = map as never; + break; + + case Array: + transformedLabels = unwrappedLabels.slice(0, to).flat() as never; + break; + + case Object: + default: + const labelsObject: Record = Object.create(null); + for (let i = 0; i < to; i++) { + const [key, value] = unwrappedLabels[i] as unknown as UnwrapReply; + const unwrappedKey = key as unknown as UnwrapReply; + labelsObject[unwrappedKey.toString()] = value; + } + transformedLabels = labelsObject as never; + break; + } + + const sourcesTuple = unwrappedLabels[unwrappedLabels.length - 1]; + const unwrappedSourcesTuple = sourcesTuple as unknown as UnwrapReply; + // the __source__ label will never be null + const transformedSources = transformRESP2Sources(unwrappedSourcesTuple[1] as BlobStringReply); + + return { + labels: transformedLabels, + sources: transformedSources + }; +} + +function transformRESP2Sources(sourcesRaw: BlobStringReply) { + // if a label contains "," this function will produce incorrcet results.. + // there is not much we can do about it, and we assume most users won't be using "," in their labels.. + + const unwrappedSources = sourcesRaw as unknown as UnwrapReply; + if (typeof unwrappedSources === 'string') { + return unwrappedSources.split(','); + } + + const indexOfComma = unwrappedSources.indexOf(','); + if (indexOfComma === -1) { + return [unwrappedSources]; + } + + const sourcesArray = [ + unwrappedSources.subarray(0, indexOfComma) + ]; + + let previousComma = indexOfComma + 1; + while (true) { + const indexOf = unwrappedSources.indexOf(',', previousComma); + if (indexOf === -1) { + sourcesArray.push( + unwrappedSources.subarray(previousComma) + ); + break; + } + + const source = unwrappedSources.subarray( + previousComma, + indexOf + ); + sourcesArray.push(source); + previousComma = indexOf + 1; + } + + return sourcesArray; +} \ No newline at end of file diff --git a/packages/time-series/lib/commands/index.spec.ts b/packages/time-series/lib/commands/index.spec.ts index 5b28708152f..b565abea476 100644 --- a/packages/time-series/lib/commands/index.spec.ts +++ b/packages/time-series/lib/commands/index.spec.ts @@ -24,7 +24,7 @@ // TimeSeriesDuplicatePolicies, // pushLatestArgument, // TimeSeriesBucketTimestamp -// } from '.'; +// } from './helpers'; // describe('transformTimestampArgument', () => { // it('number', () => { diff --git a/packages/time-series/lib/commands/index.ts b/packages/time-series/lib/commands/index.ts index f340861cb96..43bde4767bf 100644 --- a/packages/time-series/lib/commands/index.ts +++ b/packages/time-series/lib/commands/index.ts @@ -1,5 +1,4 @@ -import type { DoubleReply, NumberReply, RedisCommands, TuplesReply, UnwrapReply, Resp2Reply, ArrayReply, BlobStringReply, MapReply, NullReply, TypeMapping, ReplyUnion, RespType } from '@redis/client/dist/lib/RESP/types'; -import ADD, { TsIgnoreOptions } from './ADD'; +import ADD from './ADD'; import ALTER from './ALTER'; import CREATE from './CREATE'; import CREATERULE from './CREATERULE'; @@ -29,9 +28,9 @@ import MREVRANGE from './MREVRANGE'; import QUERYINDEX from './QUERYINDEX'; import RANGE from './RANGE'; import REVRANGE from './REVRANGE'; -import { RedisVariadicArgument } from '@redis/client/dist/lib/commands/generic-transformers'; -import { CommandParser } from '@redis/client/dist/lib/client/parser'; -import { RESP_TYPES } from '@redis/client/dist/lib/RESP/decoder'; +import { RedisCommands } from '@redis/client/dist/lib/RESP/types'; + +export * from './helpers'; export default { ADD, @@ -96,303 +95,3 @@ export default { revRange: REVRANGE } as const satisfies RedisCommands; -export function parseIgnoreArgument(parser: CommandParser, ignore?: TsIgnoreOptions) { - if (ignore !== undefined) { - parser.push('IGNORE', ignore.maxTimeDiff.toString(), ignore.maxValDiff.toString()); - } -} - -export function parseRetentionArgument(parser: CommandParser, retention?: number) { - if (retention !== undefined) { - parser.push('RETENTION', retention.toString()); - } -} - -export const TIME_SERIES_ENCODING = { - COMPRESSED: 'COMPRESSED', - UNCOMPRESSED: 'UNCOMPRESSED' -} as const; - -export type TimeSeriesEncoding = typeof TIME_SERIES_ENCODING[keyof typeof TIME_SERIES_ENCODING]; - -export function parseEncodingArgument(parser: CommandParser, encoding?: TimeSeriesEncoding) { - if (encoding !== undefined) { - parser.push('ENCODING', encoding); - } -} - -export function parseChunkSizeArgument(parser: CommandParser, chunkSize?: number) { - if (chunkSize !== undefined) { - parser.push('CHUNK_SIZE', chunkSize.toString()); - } -} - -export const TIME_SERIES_DUPLICATE_POLICIES = { - BLOCK: 'BLOCK', - FIRST: 'FIRST', - LAST: 'LAST', - MIN: 'MIN', - MAX: 'MAX', - SUM: 'SUM' -} as const; - -export type TimeSeriesDuplicatePolicies = typeof TIME_SERIES_DUPLICATE_POLICIES[keyof typeof TIME_SERIES_DUPLICATE_POLICIES]; - -export function parseDuplicatePolicy(parser: CommandParser, duplicatePolicy?: TimeSeriesDuplicatePolicies) { - if (duplicatePolicy !== undefined) { - parser.push('DUPLICATE_POLICY', duplicatePolicy); - } -} - -export type Timestamp = number | Date | string; - -export function transformTimestampArgument(timestamp: Timestamp): string { - if (typeof timestamp === 'string') return timestamp; - - return ( - typeof timestamp === 'number' ? - timestamp : - timestamp.getTime() - ).toString(); -} - -export type Labels = { - [label: string]: string; -}; - -export function parseLabelsArgument(parser: CommandParser, labels?: Labels) { - if (labels) { - parser.push('LABELS'); - - for (const [label, value] of Object.entries(labels)) { - parser.push(label, value); - } - } -} - -export type SampleRawReply = TuplesReply<[timestamp: NumberReply, value: DoubleReply]>; - -export const transformSampleReply = { - 2(reply: Resp2Reply) { - const [ timestamp, value ] = reply as unknown as UnwrapReply; - return { - timestamp, - value: Number(value) // TODO: use double type mapping instead - }; - }, - 3(reply: SampleRawReply) { - const [ timestamp, value ] = reply as unknown as UnwrapReply; - return { - timestamp, - value - }; - } -}; - -export type SamplesRawReply = ArrayReply; - -export const transformSamplesReply = { - 2(reply: Resp2Reply) { - return (reply as unknown as UnwrapReply) - .map(sample => transformSampleReply[2](sample)); - }, - 3(reply: SamplesRawReply) { - return (reply as unknown as UnwrapReply) - .map(sample => transformSampleReply[3](sample)); - } -}; - -// TODO: move to @redis/client? -export function resp2MapToValue< - RAW_VALUE extends TuplesReply<[key: BlobStringReply, ...rest: Array]>, - TRANSFORMED ->( - wrappedReply: ArrayReply, - parseFunc: (rawValue: UnwrapReply) => TRANSFORMED, - typeMapping?: TypeMapping -): MapReply { - const reply = wrappedReply as unknown as UnwrapReply; - switch (typeMapping?.[RESP_TYPES.MAP]) { - case Map: { - const ret = new Map(); - for (const wrappedTuple of reply) { - const tuple = wrappedTuple as unknown as UnwrapReply; - const key = tuple[0] as unknown as UnwrapReply; - ret.set(key.toString(), parseFunc(tuple)); - } - return ret as never; - } - case Array: { - for (const wrappedTuple of reply) { - const tuple = wrappedTuple as unknown as UnwrapReply; - (tuple[1] as unknown as TRANSFORMED) = parseFunc(tuple); - } - return reply as never; - } - default: { - const ret: Record = Object.create(null); - for (const wrappedTuple of reply) { - const tuple = wrappedTuple as unknown as UnwrapReply; - const key = tuple[0] as unknown as UnwrapReply; - ret[key.toString()] = parseFunc(tuple); - } - return ret as never; - } - } -} - -export function resp3MapToValue< - RAW_VALUE extends RespType, // TODO: simplify types - TRANSFORMED ->( - wrappedReply: MapReply, - parseFunc: (rawValue: UnwrapReply) => TRANSFORMED -): MapReply { - const reply = wrappedReply as unknown as UnwrapReply; - if (reply instanceof Array) { - for (let i = 1; i < reply.length; i += 2) { - (reply[i] as unknown as TRANSFORMED) = parseFunc(reply[i] as unknown as UnwrapReply); - } - } else if (reply instanceof Map) { - for (const [key, value] of reply.entries()) { - (reply as unknown as Map).set( - key, - parseFunc(value as unknown as UnwrapReply) - ); - } - } else { - for (const [key, value] of Object.entries(reply)) { - (reply[key] as unknown as TRANSFORMED) = parseFunc(value as unknown as UnwrapReply); - } - } - return reply as never; -} - -export function parseSelectedLabelsArguments( - parser: CommandParser, - selectedLabels: RedisVariadicArgument -) { - parser.push('SELECTED_LABELS'); - parser.pushVariadic(selectedLabels); -} - -export type RawLabelValue = BlobStringReply | NullReply; - -export type RawLabels = ArrayReply>; - -export function transformRESP2Labels( - labels: RawLabels, - typeMapping?: TypeMapping -): MapReply { - const unwrappedLabels = labels as unknown as UnwrapReply; - switch (typeMapping?.[RESP_TYPES.MAP]) { - case Map: - const map = new Map(); - for (const tuple of unwrappedLabels) { - const [key, value] = tuple as unknown as UnwrapReply; - const unwrappedKey = key as unknown as UnwrapReply; - map.set(unwrappedKey.toString(), value); - } - return map as never; - - case Array: - return unwrappedLabels.flat() as never; - - case Object: - default: - const labelsObject: Record = Object.create(null); - for (const tuple of unwrappedLabels) { - const [key, value] = tuple as unknown as UnwrapReply; - const unwrappedKey = key as unknown as UnwrapReply; - labelsObject[unwrappedKey.toString()] = value; - } - return labelsObject as never; - } -} - -export function transformRESP2LabelsWithSources( - labels: RawLabels, - typeMapping?: TypeMapping -) { - const unwrappedLabels = labels as unknown as UnwrapReply; - const to = unwrappedLabels.length - 2; // ignore __reducer__ and __source__ - let transformedLabels: MapReply; - switch (typeMapping?.[RESP_TYPES.MAP]) { - case Map: - const map = new Map(); - for (let i = 0; i < to; i++) { - const [key, value] = unwrappedLabels[i] as unknown as UnwrapReply; - const unwrappedKey = key as unknown as UnwrapReply; - map.set(unwrappedKey.toString(), value); - } - transformedLabels = map as never; - break; - - case Array: - transformedLabels = unwrappedLabels.slice(0, to).flat() as never; - break; - - case Object: - default: - const labelsObject: Record = Object.create(null); - for (let i = 0; i < to; i++) { - const [key, value] = unwrappedLabels[i] as unknown as UnwrapReply; - const unwrappedKey = key as unknown as UnwrapReply; - labelsObject[unwrappedKey.toString()] = value; - } - transformedLabels = labelsObject as never; - break; - } - - const sourcesTuple = unwrappedLabels[unwrappedLabels.length - 1]; - const unwrappedSourcesTuple = sourcesTuple as unknown as UnwrapReply; - // the __source__ label will never be null - const transformedSources = transformRESP2Sources(unwrappedSourcesTuple[1] as BlobStringReply); - - return { - labels: transformedLabels, - sources: transformedSources - }; -} - -function transformRESP2Sources(sourcesRaw: BlobStringReply) { - // if a label contains "," this function will produce incorrcet results.. - // there is not much we can do about it, and we assume most users won't be using "," in their labels.. - - const unwrappedSources = sourcesRaw as unknown as UnwrapReply; - if (typeof unwrappedSources === 'string') { - return unwrappedSources.split(','); - } - - const indexOfComma = unwrappedSources.indexOf(','); - if (indexOfComma === -1) { - return [unwrappedSources]; - } - - const sourcesArray = [ - unwrappedSources.subarray(0, indexOfComma) - ]; - - let previousComma = indexOfComma + 1; - while (true) { - const indexOf = unwrappedSources.indexOf(',', previousComma); - if (indexOf === -1) { - sourcesArray.push( - unwrappedSources.subarray(previousComma) - ); - break; - } - - const source = unwrappedSources.subarray( - previousComma, - indexOf - ); - sourcesArray.push(source); - previousComma = indexOf + 1; - } - - return sourcesArray; -} From 73d7ae71cc45c5563412ce39c0cac553935a3944 Mon Sep 17 00:00:00 2001 From: Nikolay Karadzhov Date: Wed, 7 May 2025 16:10:50 +0300 Subject: [PATCH 1572/1748] update package-lock.json (#2943) --- package-lock.json | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/package-lock.json b/package-lock.json index 0b8ca6ee37e..40d291d9201 100644 --- a/package-lock.json +++ b/package-lock.json @@ -8619,7 +8619,7 @@ } }, "packages/redis": { - "version": "5.0.0", + "version": "5.0.1", "license": "MIT", "dependencies": { "@redis/bloom": "5.0.1", From 86480aaa74cf557c5ad704ab5e8fb24bdc68c386 Mon Sep 17 00:00:00 2001 From: Clubsandwich Date: Wed, 7 May 2025 22:11:09 +0900 Subject: [PATCH 1573/1748] fix `cluster.sUnsubscribe` - make `listener` optional (#2946) --- packages/client/lib/cluster/index.ts | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/packages/client/lib/cluster/index.ts b/packages/client/lib/cluster/index.ts index 12928e71f12..622ac78f467 100644 --- a/packages/client/lib/cluster/index.ts +++ b/packages/client/lib/cluster/index.ts @@ -548,7 +548,7 @@ export default class RedisCluster< SUNSUBSCRIBE( channels: string | Array, - listener: PubSubListener, + listener?: PubSubListener, bufferMode?: T ) { return this._self.#slots.executeShardedUnsubscribeCommand( From bb7845dfe39e385d9e02966d54efb211addb79e0 Mon Sep 17 00:00:00 2001 From: Hristo Temelski Date: Thu, 8 May 2025 10:29:05 +0300 Subject: [PATCH 1574/1748] Disable readOnly for cluster s/pubsub client (#2950) --- packages/client/lib/cluster/cluster-slots.ts | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/packages/client/lib/cluster/cluster-slots.ts b/packages/client/lib/cluster/cluster-slots.ts index c2fde197f4f..0679b200349 100644 --- a/packages/client/lib/cluster/cluster-slots.ts +++ b/packages/client/lib/cluster/cluster-slots.ts @@ -518,7 +518,7 @@ export default class RedisClusterSlots< node = index < this.masters.length ? this.masters[index] : this.replicas[index - this.masters.length], - client = this.#createClient(node, index >= this.masters.length); + client = this.#createClient(node, false); this.pubSubNode = { address: node.address, @@ -563,7 +563,7 @@ export default class RedisClusterSlots< } async #initiateShardedPubSubClient(master: MasterNode) { - const client = this.#createClient(master, true) + const client = this.#createClient(master, false) .on('server-sunsubscribe', async (channel, listeners) => { try { await this.rediscover(client); From 5e9fea1fd3fec6becd376f09bd6af7069a842ce7 Mon Sep 17 00:00:00 2001 From: "Bobby I." Date: Thu, 8 May 2025 15:14:30 +0300 Subject: [PATCH 1575/1748] Update Redis version to 8.0.1-pre (#2952) > FT.AGGREGATE returns an array reply where each row is an array reply and represents a single aggregate result. > The integer reply at position 1 does not represent a valid value. We now calculate the result length bazed on the number of results instead of the integer reply at pos 1 --- .github/workflows/tests.yml | 8 ++++---- packages/search/lib/commands/AGGREGATE.ts | 2 +- packages/search/lib/commands/PROFILE_AGGREGATE.spec.ts | 2 +- 3 files changed, 6 insertions(+), 6 deletions(-) diff --git a/.github/workflows/tests.yml b/.github/workflows/tests.yml index 4ad7883a262..395e0251018 100644 --- a/.github/workflows/tests.yml +++ b/.github/workflows/tests.yml @@ -7,22 +7,22 @@ on: - v4.0 - v5 paths-ignore: - - '**/*.md' + - "**/*.md" pull_request: branches: - master - v4.0 - v5 paths-ignore: - - '**/*.md' + - "**/*.md" jobs: tests: runs-on: ubuntu-latest strategy: fail-fast: false matrix: - node-version: [ '18', '20', '22' ] - redis-version: [ 'rs-7.2.0-v13', 'rs-7.4.0-v1', '8.0-RC2-pre' ] + node-version: ["18", "20", "22"] + redis-version: ["rs-7.2.0-v13", "rs-7.4.0-v1", "8.0.1-pre"] steps: - uses: actions/checkout@v4 with: diff --git a/packages/search/lib/commands/AGGREGATE.ts b/packages/search/lib/commands/AGGREGATE.ts index b2589a52a54..9d318743504 100644 --- a/packages/search/lib/commands/AGGREGATE.ts +++ b/packages/search/lib/commands/AGGREGATE.ts @@ -156,7 +156,7 @@ export default { } return { - total: Number(rawReply[0]), + total: results.length, results }; }, diff --git a/packages/search/lib/commands/PROFILE_AGGREGATE.spec.ts b/packages/search/lib/commands/PROFILE_AGGREGATE.spec.ts index dbb834ed7c2..7ddec4b0484 100644 --- a/packages/search/lib/commands/PROFILE_AGGREGATE.spec.ts +++ b/packages/search/lib/commands/PROFILE_AGGREGATE.spec.ts @@ -73,7 +73,7 @@ describe('PROFILE AGGREGATE', () => { const normalizeObject = obj => JSON.parse(JSON.stringify(obj)); const res = await client.ft.profileAggregate('index', '*'); const normalizedRes = normalizeObject(res); - assert.equal(normalizedRes.results.total, 1); + assert.equal(normalizedRes.results.total, 2); assert.ok(Array.isArray(normalizedRes.profile)); assert.equal(normalizedRes.profile[0][0], 'Total profile time'); From ebd03036d69528d11693711b910222a2d1b61e9d Mon Sep 17 00:00:00 2001 From: "Bobby I." Date: Fri, 9 May 2025 10:13:32 +0300 Subject: [PATCH 1576/1748] revert the 'total' count logic in AGGREGATE response introduced in #2952 (#2955) > FT.AGGREGATE returns an array reply where each row is an array reply and represents a single aggregate result. The integer reply at position 1 does not represent a valid value. https://redis.io/docs/latest/commands/ft.aggregate/#return --- packages/search/lib/commands/AGGREGATE.ts | 5 ++++- .../search/lib/commands/PROFILE_AGGREGATE.spec.ts | 14 +++++++++++--- 2 files changed, 15 insertions(+), 4 deletions(-) diff --git a/packages/search/lib/commands/AGGREGATE.ts b/packages/search/lib/commands/AGGREGATE.ts index 9d318743504..0ac3d2e4ce0 100644 --- a/packages/search/lib/commands/AGGREGATE.ts +++ b/packages/search/lib/commands/AGGREGATE.ts @@ -156,7 +156,10 @@ export default { } return { - total: results.length, + // https://redis.io/docs/latest/commands/ft.aggregate/#return + // FT.AGGREGATE returns an array reply where each row is an array reply and represents a single aggregate result. + // The integer reply at position 1 does not represent a valid value. + total: Number(rawReply[0]), results }; }, diff --git a/packages/search/lib/commands/PROFILE_AGGREGATE.spec.ts b/packages/search/lib/commands/PROFILE_AGGREGATE.spec.ts index 7ddec4b0484..82783fbaba9 100644 --- a/packages/search/lib/commands/PROFILE_AGGREGATE.spec.ts +++ b/packages/search/lib/commands/PROFILE_AGGREGATE.spec.ts @@ -45,7 +45,9 @@ describe('PROFILE AGGREGATE', () => { const res = await client.ft.profileAggregate('index', '*'); const normalizedRes = normalizeObject(res); - assert.equal(normalizedRes.results.total, 2); + // TODO uncomment after https://redis.io/docs/latest/commands/ft.aggregate/#return + // starts returning valid values + // assert.equal(normalizedRes.results.total, 2); assert.ok(normalizedRes.profile[0] === 'Shards'); assert.ok(Array.isArray(normalizedRes.profile[1])); @@ -73,7 +75,10 @@ describe('PROFILE AGGREGATE', () => { const normalizeObject = obj => JSON.parse(JSON.stringify(obj)); const res = await client.ft.profileAggregate('index', '*'); const normalizedRes = normalizeObject(res); - assert.equal(normalizedRes.results.total, 2); + + // TODO uncomment after https://redis.io/docs/latest/commands/ft.aggregate/#return + // starts returning valid values + // assert.equal(normalizedRes.results.total, 2); assert.ok(Array.isArray(normalizedRes.profile)); assert.equal(normalizedRes.profile[0][0], 'Total profile time'); @@ -103,8 +108,11 @@ describe('PROFILE AGGREGATE', () => { const normalizeObject = obj => JSON.parse(JSON.stringify(obj)); const res = await client.ft.profileAggregate('index', '*'); + // TODO uncomment after https://redis.io/docs/latest/commands/ft.aggregate/#return + // starts returning valid values + // assert.equal(res.Results.total_results, 2); + const normalizedRes = normalizeObject(res); - assert.equal(normalizedRes.Results.total_results, 2); assert.ok(normalizedRes.Profile.Shards); }, GLOBAL.SERVERS.OPEN_3); }); From 6f961bd7154c899907cc6419af9ca362c58ec81c Mon Sep 17 00:00:00 2001 From: Nikolay Karadzhov Date: Wed, 14 May 2025 17:23:22 +0300 Subject: [PATCH 1577/1748] fix(client): cache subsequent clients (#2963) * fix(client): cache subsequent clients we dont need to recreate a client if its config hasnt changed fixes #2954 * handle circular structures * make cache generic --- packages/client/lib/client/index.ts | 30 ++++--- packages/client/lib/client/pool.ts | 27 +++--- packages/client/lib/cluster/index.ts | 30 ++++--- .../client/lib/single-entry-cache.spec.ts | 85 +++++++++++++++++++ packages/client/lib/single-entry-cache.ts | 37 ++++++++ 5 files changed, 178 insertions(+), 31 deletions(-) create mode 100644 packages/client/lib/single-entry-cache.spec.ts create mode 100644 packages/client/lib/single-entry-cache.ts diff --git a/packages/client/lib/client/index.ts b/packages/client/lib/client/index.ts index f9e95025c6a..f3e72a3a172 100644 --- a/packages/client/lib/client/index.ts +++ b/packages/client/lib/client/index.ts @@ -17,6 +17,7 @@ import { RedisLegacyClient, RedisLegacyClientType } from './legacy-mode'; import { RedisPoolOptions, RedisClientPool } from './pool'; import { RedisVariadicArgument, parseArgs, pushVariadicArguments } from '../commands/generic-transformers'; import { BasicCommandParser, CommandParser } from './parser'; +import SingleEntryCache from '../single-entry-cache'; export interface RedisClientOptions< M extends RedisModules = RedisModules, @@ -206,23 +207,32 @@ export default class RedisClient< } } + static #SingleEntryCache = new SingleEntryCache() + static factory< M extends RedisModules = {}, F extends RedisFunctions = {}, S extends RedisScripts = {}, RESP extends RespVersions = 2 >(config?: CommanderConfig) { - const Client = attachConfig({ - BaseClass: RedisClient, - commands: COMMANDS, - createCommand: RedisClient.#createCommand, - createModuleCommand: RedisClient.#createModuleCommand, - createFunctionCommand: RedisClient.#createFunctionCommand, - createScriptCommand: RedisClient.#createScriptCommand, - config - }); - Client.prototype.Multi = RedisClientMultiCommand.extend(config); + + let Client = RedisClient.#SingleEntryCache.get(config); + if (!Client) { + Client = attachConfig({ + BaseClass: RedisClient, + commands: COMMANDS, + createCommand: RedisClient.#createCommand, + createModuleCommand: RedisClient.#createModuleCommand, + createFunctionCommand: RedisClient.#createFunctionCommand, + createScriptCommand: RedisClient.#createScriptCommand, + config + }); + + Client.prototype.Multi = RedisClientMultiCommand.extend(config); + + RedisClient.#SingleEntryCache.set(config, Client); + } return ( options?: Omit, keyof Exclude> diff --git a/packages/client/lib/client/pool.ts b/packages/client/lib/client/pool.ts index a08377e3d38..ec89f0c39e3 100644 --- a/packages/client/lib/client/pool.ts +++ b/packages/client/lib/client/pool.ts @@ -8,6 +8,7 @@ import { attachConfig, functionArgumentsPrefix, getTransformReply, scriptArgumen import { CommandOptions } from './commands-queue'; import RedisClientMultiCommand, { RedisClientMultiCommandType } from './multi-command'; import { BasicCommandParser } from './parser'; +import SingleEntryCache from '../single-entry-cache'; export interface RedisPoolOptions { /** @@ -110,6 +111,8 @@ export class RedisClientPool< }; } + static #SingleEntryCache = new SingleEntryCache(); + static create< M extends RedisModules, F extends RedisFunctions, @@ -120,17 +123,21 @@ export class RedisClientPool< clientOptions?: RedisClientOptions, options?: Partial ) { - const Pool = attachConfig({ - BaseClass: RedisClientPool, - commands: COMMANDS, - createCommand: RedisClientPool.#createCommand, - createModuleCommand: RedisClientPool.#createModuleCommand, - createFunctionCommand: RedisClientPool.#createFunctionCommand, - createScriptCommand: RedisClientPool.#createScriptCommand, - config: clientOptions - }); - Pool.prototype.Multi = RedisClientMultiCommand.extend(clientOptions); + let Pool = RedisClientPool.#SingleEntryCache.get(clientOptions); + if(!Pool) { + Pool = attachConfig({ + BaseClass: RedisClientPool, + commands: COMMANDS, + createCommand: RedisClientPool.#createCommand, + createModuleCommand: RedisClientPool.#createModuleCommand, + createFunctionCommand: RedisClientPool.#createFunctionCommand, + createScriptCommand: RedisClientPool.#createScriptCommand, + config: clientOptions + }); + Pool.prototype.Multi = RedisClientMultiCommand.extend(clientOptions); + RedisClientPool.#SingleEntryCache.set(clientOptions, Pool); + } // returning a "proxy" to prevent the namespaces._self to leak between "proxies" return Object.create( diff --git a/packages/client/lib/cluster/index.ts b/packages/client/lib/cluster/index.ts index 622ac78f467..8b37f9c1aa7 100644 --- a/packages/client/lib/cluster/index.ts +++ b/packages/client/lib/cluster/index.ts @@ -12,6 +12,7 @@ import { RedisTcpSocketOptions } from '../client/socket'; import ASKING from '../commands/ASKING'; import { BasicCommandParser } from '../client/parser'; import { parseArgs } from '../commands/generic-transformers'; +import SingleEntryCache from '../single-entry-cache'; interface ClusterCommander< M extends RedisModules, @@ -213,6 +214,8 @@ export default class RedisCluster< }; } + static #SingleEntryCache = new SingleEntryCache(); + static factory< M extends RedisModules = {}, F extends RedisFunctions = {}, @@ -221,17 +224,22 @@ export default class RedisCluster< TYPE_MAPPING extends TypeMapping = {}, // POLICIES extends CommandPolicies = {} >(config?: ClusterCommander) { - const Cluster = attachConfig({ - BaseClass: RedisCluster, - commands: COMMANDS, - createCommand: RedisCluster.#createCommand, - createModuleCommand: RedisCluster.#createModuleCommand, - createFunctionCommand: RedisCluster.#createFunctionCommand, - createScriptCommand: RedisCluster.#createScriptCommand, - config - }); - - Cluster.prototype.Multi = RedisClusterMultiCommand.extend(config); + + let Cluster = RedisCluster.#SingleEntryCache.get(config); + if (!Cluster) { + Cluster = attachConfig({ + BaseClass: RedisCluster, + commands: COMMANDS, + createCommand: RedisCluster.#createCommand, + createModuleCommand: RedisCluster.#createModuleCommand, + createFunctionCommand: RedisCluster.#createFunctionCommand, + createScriptCommand: RedisCluster.#createScriptCommand, + config + }); + + Cluster.prototype.Multi = RedisClusterMultiCommand.extend(config); + RedisCluster.#SingleEntryCache.set(config, Cluster); + } return (options?: Omit>) => { // returning a "proxy" to prevent the namespaces._self to leak between "proxies" diff --git a/packages/client/lib/single-entry-cache.spec.ts b/packages/client/lib/single-entry-cache.spec.ts new file mode 100644 index 00000000000..ef535738eac --- /dev/null +++ b/packages/client/lib/single-entry-cache.spec.ts @@ -0,0 +1,85 @@ +import assert from 'node:assert'; +import SingleEntryCache from './single-entry-cache'; + +describe('SingleEntryCache', () => { + let cache: SingleEntryCache; + beforeEach(() => { + cache = new SingleEntryCache(); + }); + + it('should return undefined when getting from empty cache', () => { + assert.strictEqual(cache.get({ key: 'value' }), undefined); + }); + + it('should return the cached instance when getting with the same key object', () => { + const keyObj = { key: 'value' }; + const instance = { data: 'test data' }; + + cache.set(keyObj, instance); + assert.strictEqual(cache.get(keyObj), instance); + }); + + it('should return undefined when getting with a different key object', () => { + const keyObj1 = { key: 'value1' }; + const keyObj2 = { key: 'value2' }; + const instance = { data: 'test data' }; + + cache.set(keyObj1, instance); + assert.strictEqual(cache.get(keyObj2), undefined); + }); + + it('should update the cached instance when setting with the same key object', () => { + const keyObj = { key: 'value' }; + const instance1 = { data: 'test data 1' }; + const instance2 = { data: 'test data 2' }; + + cache.set(keyObj, instance1); + assert.strictEqual(cache.get(keyObj), instance1); + + cache.set(keyObj, instance2); + assert.strictEqual(cache.get(keyObj), instance2); + }); + + it('should handle undefined key object', () => { + const instance = { data: 'test data' }; + + cache.set(undefined, instance); + assert.strictEqual(cache.get(undefined), instance); + }); + + it('should handle complex objects as keys', () => { + const keyObj = { + id: 123, + nested: { + prop: 'value', + array: [1, 2, 3] + } + }; + const instance = { data: 'complex test data' }; + + cache.set(keyObj, instance); + assert.strictEqual(cache.get(keyObj), instance); + }); + + it('should consider objects with same properties but different order as different keys', () => { + const keyObj1 = { a: 1, b: 2 }; + const keyObj2 = { b: 2, a: 1 }; // Same properties but different order + const instance = { data: 'test data' }; + + cache.set(keyObj1, instance); + + assert.strictEqual(cache.get(keyObj2), undefined); + }); + + it('should handle circular structures', () => { + const keyObj: any = {}; + keyObj.self = keyObj; + + const instance = { data: 'test data' }; + + cache.set(keyObj, instance); + + assert.strictEqual(cache.get(keyObj), instance); + }); + +}); diff --git a/packages/client/lib/single-entry-cache.ts b/packages/client/lib/single-entry-cache.ts new file mode 100644 index 00000000000..5c65df96660 --- /dev/null +++ b/packages/client/lib/single-entry-cache.ts @@ -0,0 +1,37 @@ +export default class SingleEntryCache { + #cached?: V; + #serializedKey?: string; + + /** + * Retrieves an instance from the cache based on the provided key object. + * + * @param keyObj - The key object to look up in the cache. + * @returns The cached instance if found, undefined otherwise. + * + * @remarks + * This method uses JSON.stringify for comparison, which may not work correctly + * if the properties in the key object are rearranged or reordered. + */ + get(keyObj?: K): V | undefined { + return JSON.stringify(keyObj, makeCircularReplacer()) === this.#serializedKey ? this.#cached : undefined; + } + + set(keyObj: K | undefined, obj: V) { + this.#cached = obj; + this.#serializedKey = JSON.stringify(keyObj, makeCircularReplacer()); + } +} + +function makeCircularReplacer() { + const seen = new WeakSet(); + return function serialize(_: string, value: any) { + if (value && typeof value === 'object') { + if (seen.has(value)) { + return 'circular'; + } + seen.add(value); + return value; + } + return value; + } +} \ No newline at end of file From f01f1014cb472e88a5e19f3386341023a8e7a7c8 Mon Sep 17 00:00:00 2001 From: "Bobby I." Date: Mon, 19 May 2025 15:11:47 +0300 Subject: [PATCH 1578/1748] Client Side Caching (#2947) * CSC POC ontop of Parser * add csc file that weren't merged after patch * address review comments * nits to try and fix github * last change from review * Update client-side cache and improve documentation * Add client side caching RESP3 validation * Add documentation for RESP and unstableResp3 options * Add comprehensive cache statistics The `CacheStats` class provides detailed metrics like hit/miss counts, load success/failure counts, total load time, and eviction counts. It also offers derived metrics such as hit/miss rates, load failure rate, and average load penalty. The design is inspired by Caffeine. `BasicClientSideCache` now uses a `StatsCounter` to accumulate these statistics, exposed via a new `stats()` method. The previous `cacheHits()` and `cacheMisses()` methods have been removed. A `recordStats` option (default: true) in `ClientSideCacheConfig` allows disabling statistics collection. --------- Co-authored-by: Shaya Potter --- README.md | 18 + docs/v5.md | 97 ++ packages/client/index.ts | 3 + packages/client/lib/RESP/types.ts | 10 +- packages/client/lib/client/cache.spec.ts | 700 ++++++++++++++ packages/client/lib/client/cache.ts | 870 ++++++++++++++++++ packages/client/lib/client/commands-queue.ts | 23 +- packages/client/lib/client/index.spec.ts | 64 +- packages/client/lib/client/index.ts | 170 +++- packages/client/lib/client/linked-list.ts | 10 +- packages/client/lib/client/parser.ts | 11 + packages/client/lib/client/pool.ts | 93 +- packages/client/lib/client/socket.ts | 7 + .../client/lib/cluster/cluster-slots.spec.ts | 48 + packages/client/lib/cluster/cluster-slots.ts | 25 +- packages/client/lib/cluster/index.ts | 87 +- packages/client/lib/commands/GEOSEARCH.ts | 5 - .../client/lib/commands/GEOSEARCHSTORE.ts | 7 +- packages/client/lib/sentinel/index.spec.ts | 76 ++ packages/client/lib/sentinel/index.ts | 36 +- packages/client/lib/sentinel/test-util.ts | 14 +- packages/client/lib/sentinel/types.ts | 36 + packages/client/lib/sentinel/utils.ts | 2 +- packages/redis/README.md | 17 + packages/test-utils/lib/index.ts | 2 +- 25 files changed, 2330 insertions(+), 101 deletions(-) create mode 100644 packages/client/lib/client/cache.spec.ts create mode 100644 packages/client/lib/client/cache.ts create mode 100644 packages/client/lib/cluster/cluster-slots.spec.ts diff --git a/README.md b/README.md index ac6394461f1..38615ee519e 100644 --- a/README.md +++ b/README.md @@ -235,6 +235,24 @@ of sending a `QUIT` command to the server, the client can simply close the netwo client.destroy(); ``` +### Client Side Caching + +Node Redis v5 adds support for [Client Side Caching](https://redis.io/docs/manual/client-side-caching/), which enables clients to cache query results locally. The Redis server will notify the client when cached results are no longer valid. + +```typescript +// Enable client side caching with RESP3 +const client = createClient({ + RESP: 3, + clientSideCache: { + ttl: 0, // Time-to-live (0 = no expiration) + maxEntries: 0, // Maximum entries (0 = unlimited) + evictPolicy: "LRU" // Eviction policy: "LRU" or "FIFO" + } +}); +``` + +See the [V5 documentation](./docs/v5.md#client-side-caching) for more details and advanced usage. + ### Auto-Pipelining Node Redis will automatically pipeline requests that are made during the same "tick". diff --git a/docs/v5.md b/docs/v5.md index a3d0ab68389..1784ae5bd74 100644 --- a/docs/v5.md +++ b/docs/v5.md @@ -89,3 +89,100 @@ await multi.exec(); // Array await multi.exec<'typed'>(); // [string] await multi.execTyped(); // [string] ``` + +# Client Side Caching + +Node Redis v5 adds support for [Client Side Caching](https://redis.io/docs/manual/client-side-caching/), which enables clients to cache query results locally. The server will notify the client when cached results are no longer valid. + +Client Side Caching is only supported with RESP3. + +## Usage + +There are two ways to implement client side caching: + +### Anonymous Cache + +```javascript +const client = createClient({ + RESP: 3, + clientSideCache: { + ttl: 0, // Time-to-live in milliseconds (0 = no expiration) + maxEntries: 0, // Maximum entries to store (0 = unlimited) + evictPolicy: "LRU" // Eviction policy: "LRU" or "FIFO" + } +}); +``` + +In this instance, the cache is managed internally by the client. + +### Controllable Cache + +```javascript +import { BasicClientSideCache } from 'redis'; + +const cache = new BasicClientSideCache({ + ttl: 0, + maxEntries: 0, + evictPolicy: "LRU" +}); + +const client = createClient({ + RESP: 3, + clientSideCache: cache +}); +``` + +With this approach, you have direct access to the cache object for more control: + +```javascript +// Manually invalidate keys +cache.invalidate(key); + +// Clear the entire cache +cache.clear(); + +// Get cache metrics +// `cache.stats()` returns a `CacheStats` object with comprehensive statistics. +const statistics = cache.stats(); + +// Key metrics: +const hits = statistics.hitCount; // Number of cache hits +const misses = statistics.missCount; // Number of cache misses +const hitRate = statistics.hitRate(); // Cache hit rate (0.0 to 1.0) + +// Many other metrics are available on the `statistics` object, e.g.: +// statistics.missRate(), statistics.loadSuccessCount, +// statistics.averageLoadPenalty(), statistics.requestCount() +``` + +## Pooled Caching + +Client side caching also works with client pools. For pooled clients, the cache is shared across all clients in the pool: + +```javascript +const client = createClientPool({RESP: 3}, { + clientSideCache: { + ttl: 0, + maxEntries: 0, + evictPolicy: "LRU" + }, + minimum: 5 +}); +``` + +For a controllable pooled cache: + +```javascript +import { BasicPooledClientSideCache } from 'redis'; + +const cache = new BasicPooledClientSideCache({ + ttl: 0, + maxEntries: 0, + evictPolicy: "LRU" +}); + +const client = createClientPool({RESP: 3}, { + clientSideCache: cache, + minimum: 5 +}); +``` diff --git a/packages/client/index.ts b/packages/client/index.ts index 1f05bc30341..2deb0c39b47 100644 --- a/packages/client/index.ts +++ b/packages/client/index.ts @@ -34,3 +34,6 @@ export { GEO_REPLY_WITH, GeoReplyWith } from './lib/commands/GEOSEARCH_WITH'; export { SetOptions } from './lib/commands/SET'; export { REDIS_FLUSH_MODES } from './lib/commands/FLUSHALL'; + +export { BasicClientSideCache, BasicPooledClientSideCache } from './lib/client/cache'; + diff --git a/packages/client/lib/RESP/types.ts b/packages/client/lib/RESP/types.ts index 692c433a49d..8749bbdc7b0 100644 --- a/packages/client/lib/RESP/types.ts +++ b/packages/client/lib/RESP/types.ts @@ -314,11 +314,17 @@ export interface CommanderConfig< functions?: F; scripts?: S; /** - * TODO + * Specifies the Redis Serialization Protocol version to use. + * RESP2 is the default (value 2), while RESP3 (value 3) provides + * additional data types and features introduced in Redis 6.0. */ RESP?: RESP; /** - * TODO + * When set to true, enables commands that have unstable RESP3 implementations. + * When using RESP3 protocol, commands marked as having unstable RESP3 support + * will throw an error unless this flag is explicitly set to true. + * This primarily affects modules like Redis Search where response formats + * in RESP3 mode may change in future versions. */ unstableResp3?: boolean; } diff --git a/packages/client/lib/client/cache.spec.ts b/packages/client/lib/client/cache.spec.ts new file mode 100644 index 00000000000..55f2672c26c --- /dev/null +++ b/packages/client/lib/client/cache.spec.ts @@ -0,0 +1,700 @@ +import assert from "assert"; +import testUtils, { GLOBAL } from "../test-utils" +import { BasicClientSideCache, BasicPooledClientSideCache, CacheStats } from "./cache" +import { REDIS_FLUSH_MODES } from "../commands/FLUSHALL"; +import { once } from 'events'; + +describe("Client Side Cache", () => { + describe('Basic Cache', () => { + const csc = new BasicClientSideCache({ maxEntries: 10 }); + + testUtils.testWithClient('Basic Cache Miss', async client => { + csc.clear(); + + await client.set("x", 1); + await client.get("x"); + + assert.equal(csc.stats().missCount, 1, "Cache Misses"); + assert.equal(csc.stats().hitCount, 0, "Cache Hits"); + }, { + ...GLOBAL.SERVERS.OPEN, + clientOptions: { + RESP: 3, + clientSideCache: csc + } + }); + + testUtils.testWithClient('Basic Cache Hit', async client => { + csc.clear(); + + await client.set("x", 1); + assert.equal(await client.get("x"), '1'); + assert.equal(await client.get("x"), '1'); + + assert.equal(csc.stats().missCount, 1, "Cache Misses"); + assert.equal(csc.stats().hitCount, 1, "Cache Hits"); + }, { + ...GLOBAL.SERVERS.OPEN, + clientOptions: { + RESP: 3, + clientSideCache: csc + } + }); + + testUtils.testWithClient('Max Cache Entries', async client => { + csc.clear(); + + await client.set('1', 1); + assert.equal(await client.get('1'), '1'); + assert.equal(await client.get('2'), null); + assert.equal(await client.get('3'), null); + assert.equal(await client.get('4'), null); + assert.equal(await client.get('5'), null); + assert.equal(await client.get('6'), null); + assert.equal(await client.get('7'), null); + assert.equal(await client.get('8'), null); + assert.equal(await client.get('9'), null); + assert.equal(await client.get('10'), null); + assert.equal(await client.get('11'), null); + assert.equal(await client.get('1'), '1'); + + assert.equal(csc.stats().missCount, 12, "Cache Misses"); + assert.equal(csc.stats().hitCount, 0, "Cache Hits"); + }, { + ...GLOBAL.SERVERS.OPEN, + clientOptions: { + RESP: 3, + clientSideCache: csc + } + }); + + testUtils.testWithClient('LRU works correctly', async client => { + csc.clear(); + + await client.set('1', 1); + assert.equal(await client.get('1'), '1'); + assert.equal(await client.get('2'), null); + assert.equal(await client.get('3'), null); + assert.equal(await client.get('4'), null); + assert.equal(await client.get('5'), null); + assert.equal(await client.get('1'), '1'); + assert.equal(await client.get('6'), null); + assert.equal(await client.get('7'), null); + assert.equal(await client.get('8'), null); + assert.equal(await client.get('9'), null); + assert.equal(await client.get('10'), null); + assert.equal(await client.get('11'), null); + assert.equal(await client.get('1'), '1'); + + assert.equal(csc.stats().missCount, 11, "Cache Misses"); + assert.equal(csc.stats().hitCount, 2, "Cache Hits"); + }, { + ...GLOBAL.SERVERS.OPEN, + clientOptions: { + RESP: 3, + clientSideCache: csc + } + }); + + testUtils.testWithClient('Basic Cache Clear', async client => { + csc.clear(); + + await client.set("x", 1); + await client.get("x"); + csc.clear(); + await client.get("x"); + + assert.equal(csc.stats().missCount, 1, "Cache Misses"); + assert.equal(csc.stats().hitCount, 0, "Cache Hits"); + }, { + ...GLOBAL.SERVERS.OPEN, + clientOptions: { + RESP: 3, + clientSideCache: csc + } + }); + + testUtils.testWithClient('Null Invalidate acts as clear', async client => { + csc.clear(); + + await client.set("x", 1); + await client.get("x"); + csc.invalidate(null); + await client.get("x"); + + assert.equal(2, csc.stats().missCount, "Cache Misses"); + assert.equal(0, csc.stats().hitCount, "Cache Hits"); + }, { + ...GLOBAL.SERVERS.OPEN, + clientOptions: { + RESP: 3, + clientSideCache: csc + } + }); + + testUtils.testWithClient('flushdb causes an invalidate null', async client => { + csc.clear(); + + await client.set("x", 1); + assert.equal(await client.get("x"), '1'); + await client.flushDb(REDIS_FLUSH_MODES.SYNC); + assert.equal(await client.get("x"), null); + + assert.equal(csc.stats().missCount, 2, "Cache Misses"); + assert.equal(csc.stats().hitCount, 0, "Cache Hits"); + }, { + ...GLOBAL.SERVERS.OPEN, + clientOptions: { + RESP: 3, + clientSideCache: csc + } + }); + + testUtils.testWithClient('Basic Cache Invalidate', async client => { + csc.clear(); + + await client.set("x", 1); + assert.equal(await client.get("x"), '1', 'first get'); + await client.set("x", 2); + assert.equal(await client.get("x"), '2', 'second get'); + await client.set("x", 3); + assert.equal(await client.get("x"), '3', 'third get'); + + assert.equal(csc.stats().missCount, 3, "Cache Misses"); + assert.equal(csc.stats().hitCount, 0, "Cache Hits"); + }, { + ...GLOBAL.SERVERS.OPEN, + clientOptions: { + RESP: 3, + clientSideCache: csc + } + }); + + testUtils.testWithClient("Cached Replies Don't Mutate", async client => { + csc.clear(); + + await client.set("x", 1); + await client.set('y', 2); + const ret1 = await client.mGet(['x', 'y']); + assert.deepEqual(ret1, ['1', '2'], 'first mGet'); + ret1[0] = '4'; + const ret2 = await client.mGet(['x', 'y']); + assert.deepEqual(ret2, ['1', '2'], 'second mGet'); + ret2[0] = '8'; + const ret3 = await client.mGet(['x', 'y']); + assert.deepEqual(ret3, ['1', '2'], 'third mGet'); + + assert.equal(csc.stats().missCount, 1, "Cache Misses"); + assert.equal(csc.stats().hitCount, 2, "Cache Hits"); + }, { + ...GLOBAL.SERVERS.OPEN, + clientOptions: { + RESP: 3, + clientSideCache: csc + } + }); + + testUtils.testWithClient("Cached cleared on disconnect", async client => { + csc.clear(); + + await client.set("x", 1); + await client.set('y', 2); + const ret1 = await client.mGet(['x', 'y']); + assert.deepEqual(ret1, ['1', '2'], 'first mGet'); + + assert.equal(csc.stats().missCount, 1, "first Cache Misses"); + assert.equal(csc.stats().hitCount, 0, "first Cache Hits"); + + await client.close(); + + await client.connect(); + + const ret2 = await client.mGet(['x', 'y']); + assert.deepEqual(ret2, ['1', '2'], 'second mGet'); + + assert.equal(csc.stats().missCount, 1, "second Cache Misses"); + assert.equal(csc.stats().hitCount, 0, "second Cache Hits"); + }, { + ...GLOBAL.SERVERS.OPEN, + clientOptions: { + RESP: 3, + clientSideCache: csc + } + }); + }); + + describe("Pooled Cache", () => { + const csc = new BasicPooledClientSideCache(); + + testUtils.testWithClient('Virtual Pool Disconnect', async client1 => { + const client2 = client1.duplicate(); + await client2.connect() + + assert.equal(await client2.get("x"), null); + assert.equal(await client1.get("x"), null); + + assert.equal(1, csc.stats().missCount, "Cache Misses"); + assert.equal(1, csc.stats().hitCount, "Cache Hits"); + + await client2.close(); + + assert.equal(await client1.get("x"), null); + assert.equal(await client1.get("x"), null); + + assert.equal(2, csc.stats().missCount, "Cache Misses"); + assert.equal(2, csc.stats().hitCount, "Cache Hits"); + }, { + ...GLOBAL.SERVERS.OPEN, + clientOptions: { + RESP: 3, + clientSideCache: csc + } + }); + + testUtils.testWithClientPool('Basic Cache Miss and Clear', async client => { + csc.clear(); + + await client.set("x", 1); + assert.equal(await client.get("x"), '1'); + + assert.equal(1, csc.stats().missCount, "Cache Misses"); + assert.equal(0, csc.stats().hitCount, "Cache Hits"); + }, { + ...GLOBAL.SERVERS.OPEN, + clientOptions: { + RESP: 3, + }, + poolOptions: { + minimum: 5, + maximum: 5, + acquireTimeout: 0, + cleanupDelay: 1, + clientSideCache: csc + } + }) + + testUtils.testWithClientPool('Basic Cache Hit', async client => { + csc.clear(); + + await client.set("x", 1); + assert.equal(await client.get("x"), '1'); + assert.equal(await client.get("x"), '1'); + assert.equal(await client.get("x"), '1'); + + assert.equal(csc.stats().missCount, 1, "Cache Misses"); + assert.equal(csc.stats().hitCount, 2, "Cache Hits"); + }, { + ...GLOBAL.SERVERS.OPEN, + clientOptions: { + RESP: 3, + }, + poolOptions: { + minimum: 5, + maximum: 5, + acquireTimeout: 0, + cleanupDelay: 1, + clientSideCache: csc + } + }) + + testUtils.testWithClientPool('Basic Cache Manually Invalidate', async client => { + csc.clear(); + + await client.set("x", 1); + + assert.equal(await client.get("x"), '1', 'first get'); + + let p: Promise> = once(csc, 'invalidate'); + await client.set("x", 2); + let [i] = await p; + + assert.equal(await client.get("x"), '2', 'second get'); + + p = once(csc, 'invalidate'); + await client.set("x", 3); + [i] = await p; + + assert.equal(await client.get("x"), '3'); + + assert.equal(csc.stats().missCount, 3, "Cache Misses"); + assert.equal(csc.stats().hitCount, 0, "Cache Hits"); + }, { + ...GLOBAL.SERVERS.OPEN, + clientOptions: { + RESP: 3, + }, + poolOptions: { + minimum: 5, + maximum: 5, + acquireTimeout: 0, + cleanupDelay: 1, + clientSideCache: csc + } + }) + + testUtils.testWithClientPool('Basic Cache Invalidate via message', async client => { + csc.clear(); + + await client.set('x', 1); + await client.set('y', 2); + + assert.deepEqual(await client.mGet(['x', 'y']), ['1', '2'], 'first mGet'); + + assert.equal(csc.stats().missCount, 1, "Cache Misses"); + assert.equal(csc.stats().hitCount, 0, "Cache Hits"); + + let p: Promise> = once(csc, 'invalidate'); + await client.set("x", 3); + let [i] = await p; + + assert.equal(i, 'x'); + + assert.deepEqual(await client.mGet(['x', 'y']), ['3', '2'], 'second mGet'); + + assert.equal(csc.stats().missCount, 2, "Cache Misses"); + assert.equal(csc.stats().hitCount, 0, "Cache Hits"); + + p = once(csc, 'invalidate'); + await client.set("y", 4); + [i] = await p; + + assert.equal(i, 'y'); + + assert.deepEqual(await client.mGet(['x', 'y']), ['3', '4'], 'second mGet'); + + assert.equal(csc.stats().missCount, 3, "Cache Misses"); + assert.equal(csc.stats().hitCount, 0, "Cache Hits"); + }, { + ...GLOBAL.SERVERS.OPEN, + clientOptions: { + RESP: 3, + }, + poolOptions: { + minimum: 5, + maximum: 5, + acquireTimeout: 0, + cleanupDelay: 1, + clientSideCache: csc + } + }) + }); + + describe('Cluster Caching', () => { + const csc = new BasicPooledClientSideCache(); + + testUtils.testWithCluster('Basic Cache Miss and Clear', async client => { + csc.clear(); + + await client.set("x", 1); + await client.get("x"); + await client.set("y", 1); + await client.get("y"); + + assert.equal(2, csc.stats().missCount, "Cache Misses"); + assert.equal(0, csc.stats().hitCount, "Cache Hits"); + }, { + ...GLOBAL.CLUSTERS.OPEN, + clusterConfiguration: { + RESP: 3, + clientSideCache: csc + } + }) + + testUtils.testWithCluster('Basic Cache Hit', async client => { + csc.clear(); + + await client.set("x", 1); + assert.equal(await client.get("x"), '1'); + assert.equal(await client.get("x"), '1'); + assert.equal(await client.get("x"), '1'); + await client.set("y", 1); + assert.equal(await client.get("y"), '1'); + assert.equal(await client.get("y"), '1'); + assert.equal(await client.get("y"), '1'); + + assert.equal(2, csc.stats().missCount, "Cache Misses"); + assert.equal(4, csc.stats().hitCount, "Cache Hits"); + }, { + ...GLOBAL.CLUSTERS.OPEN, + clusterConfiguration: { + RESP: 3, + clientSideCache: csc + } + }) + + testUtils.testWithCluster('Basic Cache Invalidate', async client => { + csc.clear(); + + await client.set("x", 1); + assert.equal(await client.get("x"), '1'); + await client.set("x", 2); + assert.equal(await client.get("x"), '2'); + await client.set("x", 3); + assert.equal(await client.get("x"), '3'); + + await client.set("y", 1); + assert.equal(await client.get("y"), '1'); + await client.set("y", 2); + assert.equal(await client.get("y"), '2'); + await client.set("y", 3); + assert.equal(await client.get("y"), '3'); + + assert.equal(6, csc.stats().missCount, "Cache Misses"); + assert.equal(0, csc.stats().hitCount, "Cache Hits"); + }, { + ...GLOBAL.CLUSTERS.OPEN, + clusterConfiguration: { + RESP: 3, + clientSideCache: csc + } + }) + }); + describe("CacheStats", () => { + describe("CacheStats.of()", () => { + it("should correctly initialize stats and calculate derived values", () => { + const stats = CacheStats.of(10, 5, 8, 2, 100, 3); + assert.strictEqual(stats.hitCount, 10, "hitCount should be 10"); + assert.strictEqual(stats.missCount, 5, "missCount should be 5"); + assert.strictEqual(stats.loadSuccessCount, 8, "loadSuccessCount should be 8"); + assert.strictEqual(stats.loadFailureCount, 2, "loadFailureCount should be 2"); + assert.strictEqual(stats.totalLoadTime, 100, "totalLoadTime should be 100"); + assert.strictEqual(stats.evictionCount, 3, "evictionCount should be 3"); + + assert.strictEqual(stats.requestCount(), 15, "requestCount should be 15 (10 hits + 5 misses)"); + assert.strictEqual(stats.hitRate(), 10 / 15, "hitRate should be 10/15"); + assert.strictEqual(stats.missRate(), 5 / 15, "missRate should be 5/15"); + assert.strictEqual(stats.loadCount(), 10, "loadCount should be 10 (8 success + 2 failure)"); + assert.strictEqual(stats.loadFailureRate(), 2 / 10, "loadFailureRate should be 2/10"); + assert.strictEqual(stats.averageLoadPenalty(), 100 / 10, "averageLoadPenalty should be 10 (100 time / 10 loads)"); + }); + + it("should handle zero values and division by zero for derived values", () => { + const stats = CacheStats.of(0, 0, 0, 0, 0, 0); + assert.strictEqual(stats.hitCount, 0, "hitCount"); + assert.strictEqual(stats.missCount, 0, "missCount"); + assert.strictEqual(stats.loadSuccessCount, 0, "loadSuccessCount"); + assert.strictEqual(stats.loadFailureCount, 0, "loadFailureCount"); + assert.strictEqual(stats.totalLoadTime, 0, "totalLoadTime"); + assert.strictEqual(stats.evictionCount, 0, "evictionCount"); + + assert.strictEqual(stats.requestCount(), 0, "requestCount should be 0"); + assert.strictEqual(stats.hitRate(), 1, "hitRate should be 1 for 0 requests"); + assert.strictEqual(stats.missRate(), 0, "missRate should be 0 for 0 requests"); + assert.strictEqual(stats.loadCount(), 0, "loadCount should be 0"); + assert.strictEqual(stats.loadFailureRate(), 0, "loadFailureRate should be 0 for 0 loads"); + assert.strictEqual(stats.averageLoadPenalty(), 0, "averageLoadPenalty should be 0 for 0 loads"); + }); + }); + + describe("CacheStats.empty()", () => { + it("should return stats with all zero counts and 0 for rates/penalties", () => { + const stats = CacheStats.empty(); + assert.strictEqual(stats.hitCount, 0, "empty.hitCount"); + assert.strictEqual(stats.missCount, 0, "empty.missCount"); + assert.strictEqual(stats.loadSuccessCount, 0, "empty.loadSuccessCount"); + assert.strictEqual(stats.loadFailureCount, 0, "empty.loadFailureCount"); + assert.strictEqual(stats.totalLoadTime, 0, "empty.totalLoadTime"); + assert.strictEqual(stats.evictionCount, 0, "empty.evictionCount"); + + assert.strictEqual(stats.requestCount(), 0, "empty.requestCount"); + assert.strictEqual(stats.hitRate(), 1, "empty.hitRate should be 1"); + assert.strictEqual(stats.missRate(), 0, "empty.missRate should be 0"); + assert.strictEqual(stats.loadCount(), 0, "empty.loadCount"); + assert.strictEqual(stats.loadFailureRate(), 0, "empty.loadFailureRate should be 0"); + assert.strictEqual(stats.averageLoadPenalty(), 0, "empty.averageLoadPenalty should be 0"); + }); + }); + + describe("instance methods", () => { + const stats1 = CacheStats.of(10, 5, 8, 2, 100, 3); + const stats2 = CacheStats.of(20, 10, 12, 3, 200, 5); + + describe("plus()", () => { + it("should correctly add two CacheStats instances", () => { + const sum = stats1.plus(stats2); + assert.strictEqual(sum.hitCount, 30); + assert.strictEqual(sum.missCount, 15); + assert.strictEqual(sum.loadSuccessCount, 20); + assert.strictEqual(sum.loadFailureCount, 5); + assert.strictEqual(sum.totalLoadTime, 300); + assert.strictEqual(sum.evictionCount, 8); + }); + + it("should correctly sum large numbers", () => { + const statsC = CacheStats.of(Number.MAX_VALUE, 1, 1, 1, 1, 1); + const statsD = CacheStats.of(Number.MAX_VALUE, 1, 1, 1, 1, 1); + const sum = statsC.plus(statsD); + assert.strictEqual(sum.hitCount, Infinity, "Summing MAX_VALUE should result in Infinity"); + }); + }); + + describe("minus()", () => { + it("should correctly subtract one CacheStats instance from another, flooring at 0", () => { + const diff = stats2.minus(stats1); + assert.strictEqual(diff.hitCount, 10); + assert.strictEqual(diff.missCount, 5); + assert.strictEqual(diff.loadSuccessCount, 4); + assert.strictEqual(diff.loadFailureCount, 1); + assert.strictEqual(diff.totalLoadTime, 100); + assert.strictEqual(diff.evictionCount, 2); + }); + + it("should floor results at 0 if minuend is smaller than subtrahend", () => { + const sSmall = CacheStats.of(5, 2, 1, 0, 10, 1); + const sLarge = CacheStats.of(10, 5, 2, 1, 20, 2); + const diff = sSmall.minus(sLarge); + assert.strictEqual(diff.hitCount, 0, "hitCount should be floored at 0 (5 - 10)"); + assert.strictEqual(diff.missCount, 0, "missCount should be floored at 0 (2 - 5)"); + assert.strictEqual(diff.loadSuccessCount, 0, "loadSuccessCount should be floored at 0 (1 - 2)"); + assert.strictEqual(diff.loadFailureCount, 0, "loadFailureCount should be floored at 0 (0 - 1)"); + assert.strictEqual(diff.totalLoadTime, 0, "totalLoadTime should be floored at 0 (10 - 20)"); + assert.strictEqual(diff.evictionCount, 0, "evictionCount should be floored at 0 (1 - 2)"); + }); + }); + + describe("hitRate()", () => { + it("should return 0 if requestCount is 0", () => { + const stats = CacheStats.of(0, 0, 0, 0, 0, 0); + assert.strictEqual(stats.hitRate(), 1); + }); + it("should return 0 if hitCount is 0 but missCount > 0", () => { + const stats = CacheStats.of(0, 1, 0, 0, 0, 0); + assert.strictEqual(stats.hitRate(), 0); + }); + it("should return 1 if missCount is 0 but hitCount > 0", () => { + const stats = CacheStats.of(1, 0, 0, 0, 0, 0); + assert.strictEqual(stats.hitRate(), 1); + }); + }); + + describe("missRate()", () => { + it("should return 0 if requestCount is 0", () => { + const stats = CacheStats.of(0, 0, 0, 0, 0, 0); + assert.strictEqual(stats.missRate(), 0); + }); + it("should return 1 if hitCount is 0 but missCount > 0", () => { + const stats = CacheStats.of(0, 1, 0, 0, 0, 0); + assert.strictEqual(stats.missRate(), 1); + }); + it("should return 0 if missCount is 0 but hitCount > 0", () => { + const stats = CacheStats.of(1, 0, 0, 0, 0, 0); + assert.strictEqual(stats.missRate(), 0); + }); + }); + + describe("loadFailureRate()", () => { + it("should return 0 if loadCount is 0", () => { + const stats = CacheStats.of(0, 0, 0, 0, 0, 0); + assert.strictEqual(stats.loadFailureRate(), 0); + }); + it("should return 0 if loadFailureCount is 0 but loadSuccessCount > 0", () => { + const stats = CacheStats.of(0, 0, 1, 0, 10, 0); + assert.strictEqual(stats.loadFailureRate(), 0); + }); + it("should return 1 if loadSuccessCount is 0 but loadFailureCount > 0", () => { + const stats = CacheStats.of(0, 0, 0, 1, 10, 0); + assert.strictEqual(stats.loadFailureRate(), 1); + }); + }); + + describe("averageLoadPenalty()", () => { + it("should return 0 if loadCount is 0, even if totalLoadTime > 0", () => { + const stats = CacheStats.of(0, 0, 0, 0, 100, 0); + assert.strictEqual(stats.averageLoadPenalty(), 0); + }); + it("should return 0 if totalLoadTime is 0 and loadCount > 0", () => { + const stats = CacheStats.of(0, 0, 1, 1, 0, 0); + assert.strictEqual(stats.averageLoadPenalty(), 0); + }); + }); + }); + }); + it('should reflect comprehensive cache operations in stats via BasicClientSideCache', async function () { + + const csc = new BasicClientSideCache({ + maxEntries: 2, // Small size to easily trigger evictions + }); + + testUtils.testWithClient('comprehensive_stats_run', async client => { + + // --- Phase 1: Initial misses and loads --- + await client.set('keyA', 'valueA_1'); + assert.strictEqual(await client.get('keyA'), 'valueA_1', "Get keyA first time"); + assert.strictEqual(csc.stats().missCount, 1); + assert.strictEqual(csc.stats().loadSuccessCount, 1); + + await client.set('keyB', 'valueB_1'); + assert.strictEqual(await client.get('keyB'), 'valueB_1', "Get keyB first time"); + assert.strictEqual(csc.stats().missCount, 2); + assert.strictEqual(csc.stats().loadSuccessCount, 2); + + // --- Phase 2: Cache hits --- + assert.strictEqual(await client.get('keyA'), 'valueA_1', "Get keyA second time (hit)"); + assert.strictEqual(csc.stats().hitCount, 1); + + assert.strictEqual(await client.get('keyB'), 'valueB_1', "Get keyB second time (hit)"); + assert.strictEqual(csc.stats().hitCount, 2); + + + // --- Phase 3: Trigger evictions and more misses/loads --- + await client.set('keyC', 'valueC_1'); + assert.strictEqual(await client.get('keyC'), 'valueC_1', "Get keyC first time (evicts keyA)"); + assert.strictEqual(csc.stats().missCount, 3); + assert.strictEqual(csc.stats().loadSuccessCount, 3); + assert.strictEqual(csc.stats().evictionCount, 1); + + + assert.strictEqual(await client.get('keyA'), 'valueA_1', "Get keyA again (miss after eviction)"); + assert.strictEqual(csc.stats().missCount, 4); + assert.strictEqual(csc.stats().loadSuccessCount, 4); + assert.strictEqual(csc.stats().evictionCount, 2); + + + // --- Phase 4: More hits --- + assert.strictEqual(await client.get('keyC'), 'valueC_1', "Get keyC again (hit)"); + assert.strictEqual(csc.stats().hitCount, 3); + + // --- Phase 5: Update a key (results in invalidation, then miss/load on next GET) --- + // Note: A SET operation on an existing cached key should invalidate it. + // The invalidation itself isn't directly a "hit" or "miss" for stats, + // but the *next* GET will be a miss. + await client.set('keyA', 'valueA_2'); + assert.strictEqual(await client.get('keyA'), 'valueA_2', "Get keyA after SET (miss due to invalidation)"); + + assert.strictEqual(csc.stats().hitCount, 3); + assert.strictEqual(csc.stats().loadSuccessCount, 5); + + + + const stats = csc.stats() + + assert.strictEqual(stats.hitCount, 3, "Final hitCount"); + assert.strictEqual(stats.missCount, 5, "Final missCount"); + assert.strictEqual(stats.loadSuccessCount, 5, "Final loadSuccessCount"); + assert.strictEqual(stats.loadFailureCount, 0, "Final loadFailureCount (expected 0 for this test)"); + assert.strictEqual(stats.evictionCount, 2, "Final evictionCount"); + assert.ok(stats.totalLoadTime >= 0, "Final totalLoadTime should be non-negative"); + + assert.strictEqual(stats.requestCount(), 8, "Final requestCount (5 misses + 3 hits)"); + assert.strictEqual(stats.hitRate(), 3 / 8, "Final hitRate"); + assert.strictEqual(stats.missRate(), 5 / 8, "Final missRate"); + + assert.strictEqual(stats.loadCount(), 5, "Final loadCount (5 success + 0 failure)"); + assert.strictEqual(stats.loadFailureRate(), 0, "Final loadFailureRate (0 failures / 5 loads)"); + + if (stats.loadCount() > 0) { + assert.ok(stats.averageLoadPenalty() >= 0, "Final averageLoadPenalty should be non-negative"); + assert.strictEqual(stats.averageLoadPenalty(), stats.totalLoadTime / stats.loadCount(), "Average load penalty calculation"); + } else { + assert.strictEqual(stats.averageLoadPenalty(), 0, "Final averageLoadPenalty should be 0 if no loads"); + } + + }, { + ...GLOBAL.SERVERS.OPEN, + clientOptions: { + RESP: 3, + clientSideCache: csc + } + }); + }); +}); diff --git a/packages/client/lib/client/cache.ts b/packages/client/lib/client/cache.ts new file mode 100644 index 00000000000..7254352ee8f --- /dev/null +++ b/packages/client/lib/client/cache.ts @@ -0,0 +1,870 @@ +import { EventEmitter } from 'stream'; +import RedisClient from '.'; +import { RedisArgument, ReplyUnion, TransformReply, TypeMapping } from '../RESP/types'; +import { BasicCommandParser } from './parser'; + +/** + * A snapshot of cache statistics. + * + * This class provides an immutable view of the cache's operational statistics at a particular + * point in time. It is heavily inspired by the statistics reporting capabilities found in + * Ben Manes's Caffeine cache (https://github.com/ben-manes/caffeine). + * + * Instances of `CacheStats` are typically obtained from a {@link StatsCounter} and can be used + * for performance monitoring, debugging, or logging. It includes metrics such as hit rate, + * miss rate, load success/failure rates, average load penalty, and eviction counts. + * + * All statistics are non-negative. Rates and averages are typically in the range `[0.0, 1.0]`, + * or `0` if the an operation has not occurred (e.g. hit rate is 0 if there are no requests). + * + * Cache statistics are incremented according to specific rules: + * - When a cache lookup encounters an existing entry, hitCount is incremented. + * - When a cache lookup encounters a missing entry, missCount is incremented. + * - When a new entry is successfully loaded, loadSuccessCount is incremented and the + * loading time is added to totalLoadTime. + * - When an entry fails to load, loadFailureCount is incremented and the + * loading time is added to totalLoadTime. + * - When an entry is evicted due to size constraints or expiration, + * evictionCount is incremented. + */ +export class CacheStats { + /** + * Creates a new CacheStats instance with the specified statistics. + */ + private constructor( + public readonly hitCount: number, + public readonly missCount: number, + public readonly loadSuccessCount: number, + public readonly loadFailureCount: number, + public readonly totalLoadTime: number, + public readonly evictionCount: number + ) { + if ( + hitCount < 0 || + missCount < 0 || + loadSuccessCount < 0 || + loadFailureCount < 0 || + totalLoadTime < 0 || + evictionCount < 0 + ) { + throw new Error('All statistics values must be non-negative'); + } + } + + /** + * Creates a new CacheStats instance with the specified statistics. + * + * @param hitCount - Number of cache hits + * @param missCount - Number of cache misses + * @param loadSuccessCount - Number of successful cache loads + * @param loadFailureCount - Number of failed cache loads + * @param totalLoadTime - Total load time in milliseconds + * @param evictionCount - Number of cache evictions + */ + static of( + hitCount = 0, + missCount = 0, + loadSuccessCount = 0, + loadFailureCount = 0, + totalLoadTime = 0, + evictionCount = 0 + ): CacheStats { + return new CacheStats( + hitCount, + missCount, + loadSuccessCount, + loadFailureCount, + totalLoadTime, + evictionCount + ); + } + + /** + * Returns a statistics instance where no cache events have been recorded. + * + * @returns An empty statistics instance + */ + static empty(): CacheStats { + return CacheStats.EMPTY_STATS; + } + + /** + * An empty stats instance with all counters set to zero. + */ + private static readonly EMPTY_STATS = new CacheStats(0, 0, 0, 0, 0, 0); + + /** + * Returns the total number of times cache lookup methods have returned + * either a cached or uncached value. + * + * @returns Total number of requests (hits + misses) + */ + requestCount(): number { + return this.hitCount + this.missCount; + } + + /** + * Returns the hit rate of the cache. + * This is defined as hitCount / requestCount, or 1.0 when requestCount is 0. + * + * @returns The ratio of cache requests that were hits (between 0.0 and 1.0) + */ + hitRate(): number { + const requestCount = this.requestCount(); + return requestCount === 0 ? 1.0 : this.hitCount / requestCount; + } + + /** + * Returns the miss rate of the cache. + * This is defined as missCount / requestCount, or 0.0 when requestCount is 0. + * + * @returns The ratio of cache requests that were misses (between 0.0 and 1.0) + */ + missRate(): number { + const requestCount = this.requestCount(); + return requestCount === 0 ? 0.0 : this.missCount / requestCount; + } + + /** + * Returns the total number of load operations (successful + failed). + * + * @returns Total number of load operations + */ + loadCount(): number { + return this.loadSuccessCount + this.loadFailureCount; + } + + /** + * Returns the ratio of cache loading attempts that failed. + * This is defined as loadFailureCount / loadCount, or 0.0 when loadCount is 0. + * + * @returns Ratio of load operations that failed (between 0.0 and 1.0) + */ + loadFailureRate(): number { + const loadCount = this.loadCount(); + return loadCount === 0 ? 0.0 : this.loadFailureCount / loadCount; + } + + /** + * Returns the average time spent loading new values, in milliseconds. + * This is defined as totalLoadTime / loadCount, or 0.0 when loadCount is 0. + * + * @returns Average load time in milliseconds + */ + averageLoadPenalty(): number { + const loadCount = this.loadCount(); + return loadCount === 0 ? 0.0 : this.totalLoadTime / loadCount; + } + + /** + * Returns a new CacheStats representing the difference between this CacheStats + * and another. Negative values are rounded up to zero. + * + * @param other - The statistics to subtract from this instance + * @returns The difference between this instance and other + */ + minus(other: CacheStats): CacheStats { + return CacheStats.of( + Math.max(0, this.hitCount - other.hitCount), + Math.max(0, this.missCount - other.missCount), + Math.max(0, this.loadSuccessCount - other.loadSuccessCount), + Math.max(0, this.loadFailureCount - other.loadFailureCount), + Math.max(0, this.totalLoadTime - other.totalLoadTime), + Math.max(0, this.evictionCount - other.evictionCount) + ); + } + + /** + * Returns a new CacheStats representing the sum of this CacheStats and another. + * + * @param other - The statistics to add to this instance + * @returns The sum of this instance and other + */ + plus(other: CacheStats): CacheStats { + return CacheStats.of( + this.hitCount + other.hitCount, + this.missCount + other.missCount, + this.loadSuccessCount + other.loadSuccessCount, + this.loadFailureCount + other.loadFailureCount, + this.totalLoadTime + other.totalLoadTime, + this.evictionCount + other.evictionCount + ); + } +} + +/** + * An accumulator for cache statistics. + * + * This interface defines the contract for objects that record cache-related events + * such as hits, misses, loads (successes and failures), and evictions. The design + * is inspired by the statistics collection mechanisms in Ben Manes's Caffeine cache + * (https://github.com/ben-manes/caffeine). + * + * Implementations of this interface are responsible for aggregating these events. + * A snapshot of the current statistics can be obtained by calling the `snapshot()` + * method, which returns an immutable {@link CacheStats} object. + * + * Common implementations include `DefaultStatsCounter` for active statistics collection + * and `DisabledStatsCounter` for a no-op version when stats are not needed. + */ +export interface StatsCounter { + /** + * Records cache hits. This should be called when a cache request returns a cached value. + * + * @param count - The number of hits to record + */ + recordHits(count: number): void; + + /** + * Records cache misses. This should be called when a cache request returns a value that was not + * found in the cache. + * + * @param count - The number of misses to record + */ + recordMisses(count: number): void; + + /** + * Records the successful load of a new entry. This method should be called when a cache request + * causes an entry to be loaded and the loading completes successfully. + * + * @param loadTime - The number of milliseconds the cache spent computing or retrieving the new value + */ + recordLoadSuccess(loadTime: number): void; + + /** + * Records the failed load of a new entry. This method should be called when a cache request + * causes an entry to be loaded, but an exception is thrown while loading the entry. + * + * @param loadTime - The number of milliseconds the cache spent computing or retrieving the new value + * prior to the failure + */ + recordLoadFailure(loadTime: number): void; + + /** + * Records the eviction of an entry from the cache. This should only be called when an entry is + * evicted due to the cache's eviction strategy, and not as a result of manual invalidations. + * + * @param count - The number of evictions to record + */ + recordEvictions(count: number): void; + + /** + * Returns a snapshot of this counter's values. Note that this may be an inconsistent view, as it + * may be interleaved with update operations. + * + * @return A snapshot of this counter's values + */ + snapshot(): CacheStats; +} + +/** + * A StatsCounter implementation that does nothing and always returns empty stats. + */ +class DisabledStatsCounter implements StatsCounter { + static readonly INSTANCE = new DisabledStatsCounter(); + + private constructor() { } + + recordHits(count: number): void { } + recordMisses(count: number): void { } + recordLoadSuccess(loadTime: number): void { } + recordLoadFailure(loadTime: number): void { } + recordEvictions(count: number): void { } + snapshot(): CacheStats { return CacheStats.empty(); } +} + +/** + * Returns a StatsCounter that does not record any cache events. + * + * @return A StatsCounter that does not record metrics + */ +function disabledStatsCounter(): StatsCounter { + return DisabledStatsCounter.INSTANCE; +} + +/** + * A StatsCounter implementation that maintains cache statistics. + */ +class DefaultStatsCounter implements StatsCounter { + #hitCount = 0; + #missCount = 0; + #loadSuccessCount = 0; + #loadFailureCount = 0; + #totalLoadTime = 0; + #evictionCount = 0; + + /** + * Records cache hits. + * + * @param count - The number of hits to record + */ + recordHits(count: number): void { + this.#hitCount += count; + } + + /** + * Records cache misses. + * + * @param count - The number of misses to record + */ + recordMisses(count: number): void { + this.#missCount += count; + } + + /** + * Records the successful load of a new entry. + * + * @param loadTime - The number of milliseconds spent loading the entry + */ + recordLoadSuccess(loadTime: number): void { + this.#loadSuccessCount++; + this.#totalLoadTime += loadTime; + } + + /** + * Records the failed load of a new entry. + * + * @param loadTime - The number of milliseconds spent attempting to load the entry + */ + recordLoadFailure(loadTime: number): void { + this.#loadFailureCount++; + this.#totalLoadTime += loadTime; + } + + /** + * Records cache evictions. + * + * @param count - The number of evictions to record + */ + recordEvictions(count: number): void { + this.#evictionCount += count; + } + + /** + * Returns a snapshot of the current statistics. + * + * @returns A snapshot of the current statistics + */ + snapshot(): CacheStats { + return CacheStats.of( + this.#hitCount, + this.#missCount, + this.#loadSuccessCount, + this.#loadFailureCount, + this.#totalLoadTime, + this.#evictionCount + ); + } + + /** + * Creates a new DefaultStatsCounter. + * + * @returns A new DefaultStatsCounter instance + */ + static create(): DefaultStatsCounter { + return new DefaultStatsCounter(); + } +} + +type CachingClient = RedisClient; +type CmdFunc = () => Promise; + +type EvictionPolicy = "LRU" | "FIFO" + +/** + * Configuration options for Client Side Cache + */ +export interface ClientSideCacheConfig { + /** + * Time-to-live in milliseconds for cached entries. + * Use 0 for no expiration. + * @default 0 + */ + ttl?: number; + + /** + * Maximum number of entries to store in the cache. + * Use 0 for unlimited entries. + * @default 0 + */ + maxEntries?: number; + + /** + * Eviction policy to use when the cache reaches its capacity. + * - "LRU" (Least Recently Used): Evicts least recently accessed entries first + * - "FIFO" (First In First Out): Evicts oldest entries first + * @default "LRU" + */ + evictPolicy?: EvictionPolicy; + + /** + * Whether to collect statistics about cache operations. + * @default true + */ + recordStats?: boolean; +} + +interface CacheCreator { + epoch: number; + client: CachingClient; +} + +interface ClientSideCacheEntry { + invalidate(): void; + validate(): boolean; +} + +/** + * Generates a unique cache key from Redis command arguments + * + * @param redisArgs - Array of Redis command arguments + * @returns A unique string key for caching + */ +function generateCacheKey(redisArgs: ReadonlyArray): string { + const tmp = new Array(redisArgs.length * 2); + + for (let i = 0; i < redisArgs.length; i++) { + tmp[i] = redisArgs[i].length; + tmp[i + redisArgs.length] = redisArgs[i]; + } + + return tmp.join('_'); +} + +abstract class ClientSideCacheEntryBase implements ClientSideCacheEntry { + #invalidated = false; + readonly #expireTime: number; + + constructor(ttl: number) { + if (ttl == 0) { + this.#expireTime = 0; + } else { + this.#expireTime = Date.now() + ttl; + } + } + + invalidate(): void { + this.#invalidated = true; + } + + validate(): boolean { + return !this.#invalidated && (this.#expireTime == 0 || (Date.now() < this.#expireTime)) + } +} + +class ClientSideCacheEntryValue extends ClientSideCacheEntryBase { + readonly #value: any; + + get value() { + return this.#value; + } + + constructor(ttl: number, value: any) { + super(ttl); + this.#value = value; + } +} + +class ClientSideCacheEntryPromise extends ClientSideCacheEntryBase { + readonly #sendCommandPromise: Promise; + + get promise() { + return this.#sendCommandPromise; + } + + constructor(ttl: number, sendCommandPromise: Promise) { + super(ttl); + this.#sendCommandPromise = sendCommandPromise; + } +} + +export abstract class ClientSideCacheProvider extends EventEmitter { + abstract handleCache(client: CachingClient, parser: BasicCommandParser, fn: CmdFunc, transformReply: TransformReply | undefined, typeMapping: TypeMapping | undefined): Promise; + abstract trackingOn(): Array; + abstract invalidate(key: RedisArgument | null): void; + abstract clear(): void; + abstract stats(): CacheStats; + abstract onError(): void; + abstract onClose(): void; +} + +export class BasicClientSideCache extends ClientSideCacheProvider { + #cacheKeyToEntryMap: Map; + #keyToCacheKeySetMap: Map>; + readonly ttl: number; + readonly maxEntries: number; + readonly lru: boolean; + #statsCounter: StatsCounter; + + + recordEvictions(count: number): void { + this.#statsCounter.recordEvictions(count); + } + + recordHits(count: number): void { + this.#statsCounter.recordHits(count); + } + + recordMisses(count: number): void { + this.#statsCounter.recordMisses(count); + } + + constructor(config?: ClientSideCacheConfig) { + super(); + + this.#cacheKeyToEntryMap = new Map(); + this.#keyToCacheKeySetMap = new Map>(); + this.ttl = config?.ttl ?? 0; + this.maxEntries = config?.maxEntries ?? 0; + this.lru = config?.evictPolicy !== "FIFO"; + + const recordStats = config?.recordStats !== false; + this.#statsCounter = recordStats ? DefaultStatsCounter.create() : disabledStatsCounter(); + } + + /* logic of how caching works: + + 1. commands use a CommandParser + it enables us to define/retrieve + cacheKey - a unique key that corresponds to this command and its arguments + redisKeys - an array of redis keys as strings that if the key is modified, will cause redis to invalidate this result when cached + 2. check if cacheKey is in our cache + 2b1. if its a value cacheEntry - return it + 2b2. if it's a promise cache entry - wait on promise and then go to 3c. + 3. if cacheEntry is not in cache + 3a. send the command save the promise into a a cacheEntry and then wait on result + 3b. transform reply (if required) based on transformReply + 3b. check the cacheEntry is still valid - in cache and hasn't been deleted) + 3c. if valid - overwrite with value entry + 4. return previously non cached result + */ + override async handleCache( + client: CachingClient, + parser: BasicCommandParser, + fn: CmdFunc, + transformReply?: TransformReply, + typeMapping?: TypeMapping + ) { + let reply: ReplyUnion; + + const cacheKey = generateCacheKey(parser.redisArgs); + + // "2" + let cacheEntry = this.get(cacheKey); + if (cacheEntry) { + // If instanceof is "too slow", can add a "type" and then use an "as" cast to call proper getters. + if (cacheEntry instanceof ClientSideCacheEntryValue) { // "2b1" + this.#statsCounter.recordHits(1); + + return structuredClone(cacheEntry.value); + } else if (cacheEntry instanceof ClientSideCacheEntryPromise) { // 2b2 + // This counts as a miss since the value hasn't been fully loaded yet. + this.#statsCounter.recordMisses(1); + reply = await cacheEntry.promise; + } else { + throw new Error("unknown cache entry type"); + } + } else { // 3/3a + this.#statsCounter.recordMisses(1); + + const startTime = performance.now(); + const promise = fn(); + + cacheEntry = this.createPromiseEntry(client, promise); + this.set(cacheKey, cacheEntry, parser.keys); + + try { + reply = await promise; + const loadTime = performance.now() - startTime; + this.#statsCounter.recordLoadSuccess(loadTime); + } catch (err) { + const loadTime = performance.now() - startTime; + this.#statsCounter.recordLoadFailure(loadTime); + + if (cacheEntry.validate()) { + this.delete(cacheKey!); + } + + throw err; + } + } + + // 3b + let val; + if (transformReply) { + val = transformReply(reply, parser.preserve, typeMapping); + } else { + val = reply; + } + + // 3c + if (cacheEntry.validate()) { // revalidating promise entry (dont save value, if promise entry has been invalidated) + // 3d + cacheEntry = this.createValueEntry(client, val); + this.set(cacheKey, cacheEntry, parser.keys); + this.emit("cached-key", cacheKey); + } else { + // cache entry for key got invalidated between execution and saving, so not saving + } + + return structuredClone(val); + } + + override trackingOn() { + return ['CLIENT', 'TRACKING', 'ON']; + } + + override invalidate(key: RedisArgument | null) { + if (key === null) { + this.clear(false); + this.emit("invalidate", key); + + return; + } + + const keySet = this.#keyToCacheKeySetMap.get(key.toString()); + if (keySet) { + for (const cacheKey of keySet) { + const entry = this.#cacheKeyToEntryMap.get(cacheKey); + if (entry) { + entry.invalidate(); + } + this.#cacheKeyToEntryMap.delete(cacheKey); + } + this.#keyToCacheKeySetMap.delete(key.toString()); + } + + this.emit('invalidate', key); + } + + override clear(resetStats = true) { + const oldSize = this.#cacheKeyToEntryMap.size; + this.#cacheKeyToEntryMap.clear(); + this.#keyToCacheKeySetMap.clear(); + + if (resetStats) { + if (!(this.#statsCounter instanceof DisabledStatsCounter)) { + this.#statsCounter = DefaultStatsCounter.create(); + } + } else { + // If old entries were evicted due to clear, record them as evictions + if (oldSize > 0) { + this.#statsCounter.recordEvictions(oldSize); + } + } + } + + get(cacheKey: string) { + const val = this.#cacheKeyToEntryMap.get(cacheKey); + + if (val && !val.validate()) { + this.delete(cacheKey); + this.#statsCounter.recordEvictions(1); + this.emit("cache-evict", cacheKey); + + return undefined; + } + + if (val !== undefined && this.lru) { + this.#cacheKeyToEntryMap.delete(cacheKey); + this.#cacheKeyToEntryMap.set(cacheKey, val); + } + + return val; + } + + delete(cacheKey: string) { + const entry = this.#cacheKeyToEntryMap.get(cacheKey); + if (entry) { + entry.invalidate(); + this.#cacheKeyToEntryMap.delete(cacheKey); + } + } + + has(cacheKey: string) { + return this.#cacheKeyToEntryMap.has(cacheKey); + } + + set(cacheKey: string, cacheEntry: ClientSideCacheEntry, keys: Array) { + let count = this.#cacheKeyToEntryMap.size; + const oldEntry = this.#cacheKeyToEntryMap.get(cacheKey); + + if (oldEntry) { + count--; // overwriting, so not incrementig + oldEntry.invalidate(); + } + + if (this.maxEntries > 0 && count >= this.maxEntries) { + this.deleteOldest(); + this.#statsCounter.recordEvictions(1); + } + + this.#cacheKeyToEntryMap.set(cacheKey, cacheEntry); + + for (const key of keys) { + if (!this.#keyToCacheKeySetMap.has(key.toString())) { + this.#keyToCacheKeySetMap.set(key.toString(), new Set()); + } + + const cacheKeySet = this.#keyToCacheKeySetMap.get(key.toString()); + cacheKeySet!.add(cacheKey); + } + } + + size() { + return this.#cacheKeyToEntryMap.size; + } + + createValueEntry(client: CachingClient, value: any): ClientSideCacheEntryValue { + return new ClientSideCacheEntryValue(this.ttl, value); + } + + createPromiseEntry(client: CachingClient, sendCommandPromise: Promise): ClientSideCacheEntryPromise { + return new ClientSideCacheEntryPromise(this.ttl, sendCommandPromise); + } + + override stats(): CacheStats { + return this.#statsCounter.snapshot(); + } + + override onError(): void { + this.clear(); + } + + override onClose() { + this.clear(); + } + + /** + * @internal + */ + deleteOldest() { + const it = this.#cacheKeyToEntryMap[Symbol.iterator](); + const n = it.next(); + if (!n.done) { + const key = n.value[0]; + const entry = this.#cacheKeyToEntryMap.get(key); + if (entry) { + entry.invalidate(); + } + this.#cacheKeyToEntryMap.delete(key); + } + } + + /** + * Get cache entries for debugging + * @internal + */ + entryEntries(): IterableIterator<[string, ClientSideCacheEntry]> { + return this.#cacheKeyToEntryMap.entries(); + } + + /** + * Get key set entries for debugging + * @internal + */ + keySetEntries(): IterableIterator<[string, Set]> { + return this.#keyToCacheKeySetMap.entries(); + } +} + +export abstract class PooledClientSideCacheProvider extends BasicClientSideCache { + #disabled = false; + + disable(): void { + this.#disabled = true; + } + + enable(): void { + this.#disabled = false; + } + + override get(cacheKey: string): ClientSideCacheEntry | undefined { + if (this.#disabled) { + return undefined; + } + + return super.get(cacheKey); + } + + override has(cacheKey: string): boolean { + if (this.#disabled) { + return false; + } + + return super.has(cacheKey); + } + + onPoolClose(): void { + this.clear(); + } +} + +export class BasicPooledClientSideCache extends PooledClientSideCacheProvider { + override onError() { + this.clear(false); + } + + override onClose() { + this.clear(false); + } +} + +class PooledClientSideCacheEntryValue extends ClientSideCacheEntryValue { + #creator: CacheCreator; + + constructor(ttl: number, creator: CacheCreator, value: any) { + super(ttl, value); + + this.#creator = creator; + } + + override validate(): boolean { + let ret = super.validate(); + if (this.#creator) { + ret = ret && this.#creator.client.isReady && this.#creator.client.socketEpoch == this.#creator.epoch + } + + return ret; + } +} + +class PooledClientSideCacheEntryPromise extends ClientSideCacheEntryPromise { + #creator: CacheCreator; + + constructor(ttl: number, creator: CacheCreator, sendCommandPromise: Promise) { + super(ttl, sendCommandPromise); + + this.#creator = creator; + } + + override validate(): boolean { + let ret = super.validate(); + + return ret && this.#creator.client.isReady && this.#creator.client.socketEpoch == this.#creator.epoch + } +} + +export class PooledNoRedirectClientSideCache extends BasicPooledClientSideCache { + override createValueEntry(client: CachingClient, value: any): ClientSideCacheEntryValue { + const creator = { + epoch: client.socketEpoch, + client: client + }; + + return new PooledClientSideCacheEntryValue(this.ttl, creator, value); + } + + override createPromiseEntry(client: CachingClient, sendCommandPromise: Promise): ClientSideCacheEntryPromise { + const creator = { + epoch: client.socketEpoch, + client: client + }; + + return new PooledClientSideCacheEntryPromise(this.ttl, creator, sendCommandPromise); + } + + override onError() { } + + override onClose() { } +} diff --git a/packages/client/lib/client/commands-queue.ts b/packages/client/lib/client/commands-queue.ts index 15e8a747b98..78c0a01b203 100644 --- a/packages/client/lib/client/commands-queue.ts +++ b/packages/client/lib/client/commands-queue.ts @@ -56,6 +56,8 @@ export default class RedisCommandsQueue { return this.#pubSub.isActive; } + #invalidateCallback?: (key: RedisArgument | null) => unknown; + constructor( respVersion: RespVersions, maxLength: number | null | undefined, @@ -107,15 +109,34 @@ export default class RedisCommandsQueue { return new Decoder({ onReply: reply => this.#onReply(reply), onErrorReply: err => this.#onErrorReply(err), + //TODO: we can shave off a few cycles by not adding onPush handler at all if CSC is not used onPush: push => { if (!this.#onPush(push)) { - + // currently only supporting "invalidate" over RESP3 push messages + switch (push[0].toString()) { + case "invalidate": { + if (this.#invalidateCallback) { + if (push[1] !== null) { + for (const key of push[1]) { + this.#invalidateCallback(key); + } + } else { + this.#invalidateCallback(null); + } + } + break; + } + } } }, getTypeMapping: () => this.#getTypeMapping() }); } + setInvalidateCallback(callback?: (key: RedisArgument | null) => unknown) { + this.#invalidateCallback = callback; + } + addCommand( args: ReadonlyArray, options?: CommandOptions diff --git a/packages/client/lib/client/index.spec.ts b/packages/client/lib/client/index.spec.ts index c71cf1a1fad..cc052dd5b51 100644 --- a/packages/client/lib/client/index.spec.ts +++ b/packages/client/lib/client/index.spec.ts @@ -24,10 +24,44 @@ export const SQUARE_SCRIPT = defineScript({ }); describe('Client', () => { + describe('initialization', () => { + describe('clientSideCache validation', () => { + const clientSideCacheConfig = { ttl: 0, maxEntries: 0 }; + + it('should throw error when clientSideCache is enabled with RESP 2', () => { + assert.throws( + () => new RedisClient({ + clientSideCache: clientSideCacheConfig, + RESP: 2, + }), + new Error('Client Side Caching is only supported with RESP3') + ); + }); + + it('should throw error when clientSideCache is enabled with RESP undefined', () => { + assert.throws( + () => new RedisClient({ + clientSideCache: clientSideCacheConfig, + }), + new Error('Client Side Caching is only supported with RESP3') + ); + }); + + it('should not throw when clientSideCache is enabled with RESP 3', () => { + assert.doesNotThrow(() => + new RedisClient({ + clientSideCache: clientSideCacheConfig, + RESP: 3, + }) + ); + }); + }); + }); + describe('parseURL', () => { it('redis://user:secret@localhost:6379/0', async () => { const result = RedisClient.parseURL('redis://user:secret@localhost:6379/0'); - const expected : RedisClientOptions = { + const expected: RedisClientOptions = { socket: { host: 'localhost', port: 6379 @@ -51,8 +85,8 @@ describe('Client', () => { // Compare non-function properties assert.deepEqual(resultRest, expectedRest); - if(result.credentialsProvider.type === 'async-credentials-provider' - && expected.credentialsProvider.type === 'async-credentials-provider') { + if (result?.credentialsProvider?.type === 'async-credentials-provider' + && expected?.credentialsProvider?.type === 'async-credentials-provider') { // Compare the actual output of the credentials functions const resultCreds = await result.credentialsProvider.credentials(); @@ -91,10 +125,10 @@ describe('Client', () => { // Compare non-function properties assert.deepEqual(resultRest, expectedRest); - assert.equal(resultCredProvider.type, expectedCredProvider.type); + assert.equal(resultCredProvider?.type, expectedCredProvider?.type); - if (result.credentialsProvider.type === 'async-credentials-provider' && - expected.credentialsProvider.type === 'async-credentials-provider') { + if (result?.credentialsProvider?.type === 'async-credentials-provider' && + expected?.credentialsProvider?.type === 'async-credentials-provider') { // Compare the actual output of the credentials functions const resultCreds = await result.credentialsProvider.credentials(); @@ -150,11 +184,11 @@ describe('Client', () => { testUtils.testWithClient('Client can authenticate using the streaming credentials provider for initial token acquisition', async client => { - assert.equal( - await client.ping(), - 'PONG' - ); - }, GLOBAL.SERVERS.STREAMING_AUTH); + assert.equal( + await client.ping(), + 'PONG' + ); + }, GLOBAL.SERVERS.STREAMING_AUTH); testUtils.testWithClient('should execute AUTH before SELECT', async client => { assert.equal( @@ -408,7 +442,7 @@ describe('Client', () => { }); testUtils.testWithClient('functions', async client => { - const [,, reply] = await Promise.all([ + const [, , reply] = await Promise.all([ loadMathFunction(client), client.set('key', '2'), client.math.square('key') @@ -522,8 +556,8 @@ describe('Client', () => { const hash: Record = {}; const expectedFields: Array = []; for (let i = 0; i < 100; i++) { - hash[i.toString()] = i.toString(); - expectedFields.push(i.toString()); + hash[i.toString()] = i.toString(); + expectedFields.push(i.toString()); } await client.hSet('key', hash); @@ -842,7 +876,7 @@ describe('Client', () => { testUtils.testWithClient('should be able to go back to "normal mode"', async client => { await Promise.all([ - client.monitor(() => {}), + client.monitor(() => { }), client.reset() ]); await assert.doesNotReject(client.ping()); diff --git a/packages/client/lib/client/index.ts b/packages/client/lib/client/index.ts index f3e72a3a172..c7f94fe680a 100644 --- a/packages/client/lib/client/index.ts +++ b/packages/client/lib/client/index.ts @@ -16,6 +16,7 @@ import { ScanOptions, ScanCommonOptions } from '../commands/SCAN'; import { RedisLegacyClient, RedisLegacyClientType } from './legacy-mode'; import { RedisPoolOptions, RedisClientPool } from './pool'; import { RedisVariadicArgument, parseArgs, pushVariadicArguments } from '../commands/generic-transformers'; +import { BasicClientSideCache, ClientSideCacheConfig, ClientSideCacheProvider } from './cache'; import { BasicCommandParser, CommandParser } from './parser'; import SingleEntryCache from '../single-entry-cache'; @@ -78,45 +79,98 @@ export interface RedisClientOptions< */ pingInterval?: number; /** - * TODO + * Default command options to be applied to all commands executed through this client. + * + * These options can be overridden on a per-command basis when calling specific commands. + * + * @property {symbol} [chainId] - Identifier for chaining commands together + * @property {boolean} [asap] - When true, the command is executed as soon as possible + * @property {AbortSignal} [abortSignal] - AbortSignal to cancel the command + * @property {TypeMapping} [typeMapping] - Custom type mappings between RESP and JavaScript types + * + * @example Setting default command options + * ``` + * const client = createClient({ + * commandOptions: { + * asap: true, + * typeMapping: { + * // Custom type mapping configuration + * } + * } + * }); + * ``` */ commandOptions?: CommandOptions; + /** + * Client Side Caching configuration. + * + * Enables Redis Servers and Clients to work together to cache results from commands + * sent to a server. The server will notify the client when cached results are no longer valid. + * + * Note: Client Side Caching is only supported with RESP3. + * + * @example Anonymous cache configuration + * ``` + * const client = createClient({ + * RESP: 3, + * clientSideCache: { + * ttl: 0, + * maxEntries: 0, + * evictPolicy: "LRU" + * } + * }); + * ``` + * + * @example Using a controllable cache + * ``` + * const cache = new BasicClientSideCache({ + * ttl: 0, + * maxEntries: 0, + * evictPolicy: "LRU" + * }); + * const client = createClient({ + * RESP: 3, + * clientSideCache: cache + * }); + * ``` + */ + clientSideCache?: ClientSideCacheProvider | ClientSideCacheConfig; } type WithCommands< RESP extends RespVersions, TYPE_MAPPING extends TypeMapping > = { - [P in keyof typeof COMMANDS]: CommandSignature<(typeof COMMANDS)[P], RESP, TYPE_MAPPING>; -}; + [P in keyof typeof COMMANDS]: CommandSignature<(typeof COMMANDS)[P], RESP, TYPE_MAPPING>; + }; type WithModules< M extends RedisModules, RESP extends RespVersions, TYPE_MAPPING extends TypeMapping > = { - [P in keyof M]: { - [C in keyof M[P]]: CommandSignature; + [P in keyof M]: { + [C in keyof M[P]]: CommandSignature; + }; }; -}; type WithFunctions< F extends RedisFunctions, RESP extends RespVersions, TYPE_MAPPING extends TypeMapping > = { - [L in keyof F]: { - [C in keyof F[L]]: CommandSignature; + [L in keyof F]: { + [C in keyof F[L]]: CommandSignature; + }; }; -}; type WithScripts< S extends RedisScripts, RESP extends RespVersions, TYPE_MAPPING extends TypeMapping > = { - [P in keyof S]: CommandSignature; -}; + [P in keyof S]: CommandSignature; + }; export type RedisClientExtensions< M extends RedisModules = {}, @@ -125,11 +179,11 @@ export type RedisClientExtensions< RESP extends RespVersions = 2, TYPE_MAPPING extends TypeMapping = {} > = ( - WithCommands & - WithModules & - WithFunctions & - WithScripts -); + WithCommands & + WithModules & + WithFunctions & + WithScripts + ); export type RedisClientType< M extends RedisModules = {}, @@ -138,9 +192,9 @@ export type RedisClientType< RESP extends RespVersions = 2, TYPE_MAPPING extends TypeMapping = {} > = ( - RedisClient & - RedisClientExtensions -); + RedisClient & + RedisClientExtensions + ); type ProxyClient = RedisClient; @@ -309,14 +363,17 @@ export default class RedisClient< #monitorCallback?: MonitorCallback; private _self = this; private _commandOptions?: CommandOptions; - // flag used to annotate that the client - // was in a watch transaction when + // flag used to annotate that the client + // was in a watch transaction when // a topology change occured #dirtyWatch?: string; - #epoch: number; - #watchEpoch?: number; - + #watchEpoch?: number; + #clientSideCache?: ClientSideCacheProvider; #credentialsSubscription: Disposable | null = null; + get clientSideCache() { + return this._self.#clientSideCache; + } + get options(): RedisClientOptions | undefined { return this._self.#options; @@ -334,6 +391,10 @@ export default class RedisClient< return this._self.#queue.isPubSubActive; } + get socketEpoch() { + return this._self.#socket.socketEpoch; + } + get isWatching() { return this._self.#watchEpoch !== undefined; } @@ -358,12 +419,28 @@ export default class RedisClient< constructor(options?: RedisClientOptions) { super(); + this.#validateOptions(options) this.#options = this.#initiateOptions(options); this.#queue = this.#initiateQueue(); this.#socket = this.#initiateSocket(); - this.#epoch = 0; + + if (options?.clientSideCache) { + if (options.clientSideCache instanceof ClientSideCacheProvider) { + this.#clientSideCache = options.clientSideCache; + } else { + const cscConfig = options.clientSideCache; + this.#clientSideCache = new BasicClientSideCache(cscConfig); + } + this.#queue.setInvalidateCallback(this.#clientSideCache.invalidate.bind(this.#clientSideCache)); + } } + #validateOptions(options?: RedisClientOptions) { + if (options?.clientSideCache && options?.RESP !== 3) { + throw new Error('Client Side Caching is only supported with RESP3'); + } + + } #initiateOptions(options?: RedisClientOptions): RedisClientOptions | undefined { // Convert username/password to credentialsProvider if no credentialsProvider is already in place @@ -421,7 +498,7 @@ export default class RedisClient< } } - #subscribeForStreamingCredentials(cp: StreamingCredentialsProvider): Promise<[BasicAuth, Disposable]> { + #subscribeForStreamingCredentials(cp: StreamingCredentialsProvider): Promise<[BasicAuth, Disposable]> { return cp.subscribe({ onNext: credentials => { this.reAuthenticate(credentials).catch(error => { @@ -456,7 +533,7 @@ export default class RedisClient< if (cp && cp.type === 'streaming-credentials-provider') { - const [credentials, disposable] = await this.#subscribeForStreamingCredentials(cp) + const [credentials, disposable] = await this.#subscribeForStreamingCredentials(cp) this.#credentialsSubscription = disposable; if (credentials.password) { @@ -492,7 +569,7 @@ export default class RedisClient< if (cp && cp.type === 'streaming-credentials-provider') { - const [credentials, disposable] = await this.#subscribeForStreamingCredentials(cp) + const [credentials, disposable] = await this.#subscribeForStreamingCredentials(cp) this.#credentialsSubscription = disposable; if (credentials.username || credentials.password) { @@ -522,6 +599,10 @@ export default class RedisClient< ); } + if (this.#clientSideCache) { + commands.push(this.#clientSideCache.trackingOn()); + } + return commands; } @@ -575,6 +656,7 @@ export default class RedisClient< }) .on('error', err => { this.emit('error', err); + this.#clientSideCache?.onError(); if (this.#socket.isOpen && !this.#options?.disableOfflineQueue) { this.#queue.flushWaitingForReply(err); } else { @@ -583,7 +665,6 @@ export default class RedisClient< }) .on('connect', () => this.emit('connect')) .on('ready', () => { - this.#epoch++; this.emit('ready'); this.#setPingTimer(); this.#maybeScheduleWrite(); @@ -711,14 +792,21 @@ export default class RedisClient< commandOptions: CommandOptions | undefined, transformReply: TransformReply | undefined, ) { - const reply = await this.sendCommand(parser.redisArgs, commandOptions); + const csc = this._self.#clientSideCache; + const defaultTypeMapping = this._self.#options?.commandOptions === commandOptions; - if (transformReply) { - const res = transformReply(reply, parser.preserve, commandOptions?.typeMapping); - return res - } + const fn = () => { return this.sendCommand(parser.redisArgs, commandOptions) }; + + if (csc && command.CACHEABLE && defaultTypeMapping) { + return await csc.handleCache(this._self, parser as BasicCommandParser, fn, transformReply, commandOptions?.typeMapping); + } else { + const reply = await fn(); - return reply; + if (transformReply) { + return transformReply(reply, parser.preserve, commandOptions?.typeMapping); + } + return reply; + } } /** @@ -883,7 +971,7 @@ export default class RedisClient< const reply = await this._self.sendCommand( pushVariadicArguments(['WATCH'], key) ); - this._self.#watchEpoch ??= this._self.#epoch; + this._self.#watchEpoch ??= this._self.socketEpoch; return reply as unknown as ReplyWithTypeMapping, TYPE_MAPPING>; } @@ -942,7 +1030,7 @@ export default class RedisClient< * @internal */ async _executePipeline( - commands: Array, + commands: Array, selectedDB?: number ) { if (!this._self.#socket.isOpen) { @@ -986,15 +1074,15 @@ export default class RedisClient< throw new WatchError(dirtyWatch); } - if (watchEpoch && watchEpoch !== this._self.#epoch) { + if (watchEpoch && watchEpoch !== this._self.socketEpoch) { throw new WatchError('Client reconnected after WATCH'); } const typeMapping = this._commandOptions?.typeMapping; const chainId = Symbol('MULTI Chain'); const promises = [ - this._self.#queue.addCommand(['MULTI'], { chainId }), - ]; + this._self.#queue.addCommand(['MULTI'], { chainId }), + ]; for (const { args } of commands) { promises.push( @@ -1210,6 +1298,7 @@ export default class RedisClient< return new Promise(resolve => { clearTimeout(this._self.#pingTimer); this._self.#socket.close(); + this._self.#clientSideCache?.onClose(); if (this._self.#queue.isEmpty()) { this._self.#socket.destroySocket(); @@ -1236,6 +1325,7 @@ export default class RedisClient< clearTimeout(this._self.#pingTimer); this._self.#queue.flushAll(new DisconnectsClientError()); this._self.#socket.destroy(); + this._self.#clientSideCache?.onClose(); this._self.#credentialsSubscription?.dispose(); this._self.#credentialsSubscription = null; } diff --git a/packages/client/lib/client/linked-list.ts b/packages/client/lib/client/linked-list.ts index ac1d021be91..29678f027b5 100644 --- a/packages/client/lib/client/linked-list.ts +++ b/packages/client/lib/client/linked-list.ts @@ -114,6 +114,7 @@ export class DoublyLinkedList { export interface SinglyLinkedNode { value: T; next: SinglyLinkedNode | undefined; + removed: boolean; } export class SinglyLinkedList { @@ -140,7 +141,8 @@ export class SinglyLinkedList { const node = { value, - next: undefined + next: undefined, + removed: false }; if (this.#head === undefined) { @@ -151,6 +153,9 @@ export class SinglyLinkedList { } remove(node: SinglyLinkedNode, parent: SinglyLinkedNode | undefined) { + if (node.removed) { + throw new Error("node already removed"); + } --this.#length; if (this.#head === node) { @@ -165,6 +170,8 @@ export class SinglyLinkedList { } else { parent!.next = node.next; } + + node.removed = true; } shift() { @@ -177,6 +184,7 @@ export class SinglyLinkedList { this.#head = node.next; } + node.removed = true; return node.value; } diff --git a/packages/client/lib/client/parser.ts b/packages/client/lib/client/parser.ts index 12eec457739..3e820230429 100644 --- a/packages/client/lib/client/parser.ts +++ b/packages/client/lib/client/parser.ts @@ -33,6 +33,17 @@ export class BasicCommandParser implements CommandParser { return this.#keys[0]; } + get cacheKey() { + const tmp = new Array(this.#redisArgs.length*2); + + for (let i = 0; i < this.#redisArgs.length; i++) { + tmp[i] = this.#redisArgs[i].length; + tmp[i+this.#redisArgs.length] = this.#redisArgs[i]; + } + + return tmp.join('_'); + } + push(...arg: Array) { this.#redisArgs.push(...arg); }; diff --git a/packages/client/lib/client/pool.ts b/packages/client/lib/client/pool.ts index ec89f0c39e3..6f633c9caa7 100644 --- a/packages/client/lib/client/pool.ts +++ b/packages/client/lib/client/pool.ts @@ -7,6 +7,7 @@ import { TimeoutError } from '../errors'; import { attachConfig, functionArgumentsPrefix, getTransformReply, scriptArgumentsPrefix } from '../commander'; import { CommandOptions } from './commands-queue'; import RedisClientMultiCommand, { RedisClientMultiCommandType } from './multi-command'; +import { BasicPooledClientSideCache, ClientSideCacheConfig, PooledClientSideCacheProvider } from './cache'; import { BasicCommandParser } from './parser'; import SingleEntryCache from '../single-entry-cache'; @@ -24,11 +25,55 @@ export interface RedisPoolOptions { */ acquireTimeout: number; /** - * TODO + * The delay in milliseconds before a cleanup operation is performed on idle clients. + * + * After this delay, the pool will check if there are too many idle clients and destroy + * excess ones to maintain optimal pool size. */ cleanupDelay: number; /** - * TODO + * Client Side Caching configuration for the pool. + * + * Enables Redis Servers and Clients to work together to cache results from commands + * sent to a server. The server will notify the client when cached results are no longer valid. + * In pooled mode, the cache is shared across all clients in the pool. + * + * Note: Client Side Caching is only supported with RESP3. + * + * @example Anonymous cache configuration + * ``` + * const client = createClientPool({RESP: 3}, { + * clientSideCache: { + * ttl: 0, + * maxEntries: 0, + * evictPolicy: "LRU" + * }, + * minimum: 5 + * }); + * ``` + * + * @example Using a controllable cache + * ``` + * const cache = new BasicPooledClientSideCache({ + * ttl: 0, + * maxEntries: 0, + * evictPolicy: "LRU" + * }); + * const client = createClientPool({RESP: 3}, { + * clientSideCache: cache, + * minimum: 5 + * }); + * ``` + */ + clientSideCache?: PooledClientSideCacheProvider | ClientSideCacheConfig; + /** + * Enable experimental support for RESP3 module commands. + * + * When enabled, allows the use of module commands that have been adapted + * for the RESP3 protocol. This is an unstable feature and may change in + * future versions. + * + * @default false */ unstableResp3Modules?: boolean; } @@ -120,7 +165,7 @@ export class RedisClientPool< RESP extends RespVersions, TYPE_MAPPING extends TypeMapping = {} >( - clientOptions?: RedisClientOptions, + clientOptions?: Omit, "clientSideCache">, options?: Partial ) { @@ -142,7 +187,7 @@ export class RedisClientPool< // returning a "proxy" to prevent the namespaces._self to leak between "proxies" return Object.create( new Pool( - RedisClient.factory(clientOptions).bind(undefined, clientOptions), + clientOptions, options ) ) as RedisClientPoolType; @@ -216,22 +261,41 @@ export class RedisClientPool< return this._self.#isClosing; } + #clientSideCache?: PooledClientSideCacheProvider; + get clientSideCache() { + return this._self.#clientSideCache; + } + /** * You are probably looking for {@link RedisClient.createPool `RedisClient.createPool`}, * {@link RedisClientPool.fromClient `RedisClientPool.fromClient`}, * or {@link RedisClientPool.fromOptions `RedisClientPool.fromOptions`}... */ constructor( - clientFactory: () => RedisClientType, + clientOptions?: RedisClientOptions, options?: Partial ) { super(); - this.#clientFactory = clientFactory; this.#options = { ...RedisClientPool.#DEFAULTS, ...options }; + if (options?.clientSideCache) { + if (clientOptions === undefined) { + clientOptions = {}; + } + + if (options.clientSideCache instanceof PooledClientSideCacheProvider) { + this.#clientSideCache = clientOptions.clientSideCache = options.clientSideCache; + } else { + const cscConfig = options.clientSideCache; + this.#clientSideCache = clientOptions.clientSideCache = new BasicPooledClientSideCache(cscConfig); +// this.#clientSideCache = clientOptions.clientSideCache = new PooledNoRedirectClientSideCache(cscConfig); + } + } + + this.#clientFactory = RedisClient.factory(clientOptions).bind(undefined, clientOptions) as () => RedisClientType; } private _self = this; @@ -295,7 +359,6 @@ export class RedisClientPool< async connect() { if (this._self.#isOpen) return; // TODO: throw error? - this._self.#isOpen = true; const promises = []; @@ -305,11 +368,12 @@ export class RedisClientPool< try { await Promise.all(promises); - return this as unknown as RedisClientPoolType; } catch (err) { this.destroy(); throw err; } + + return this as unknown as RedisClientPoolType; } async #create() { @@ -319,7 +383,8 @@ export class RedisClientPool< ); try { - await node.value.connect(); + const client = node.value; + await client.connect(); } catch (err) { this._self.#clientsInUse.remove(node); throw err; @@ -408,7 +473,8 @@ export class RedisClientPool< const toDestroy = Math.min(this.#idleClients.length, this.totalClients - this.#options.minimum); for (let i = 0; i < toDestroy; i++) { // TODO: shift vs pop - this.#idleClients.shift()!.destroy(); + const client = this.#idleClients.shift()! + client.destroy(); } } @@ -446,8 +512,10 @@ export class RedisClientPool< for (const client of this._self.#clientsInUse) { promises.push(client.close()); } - + await Promise.all(promises); + + this.#clientSideCache?.onPoolClose(); this._self.#idleClients.reset(); this._self.#clientsInUse.reset(); @@ -467,6 +535,9 @@ export class RedisClientPool< for (const client of this._self.#clientsInUse) { client.destroy(); } + + this._self.#clientSideCache?.onPoolClose(); + this._self.#clientsInUse.reset(); this._self.#isOpen = false; diff --git a/packages/client/lib/client/socket.ts b/packages/client/lib/client/socket.ts index 36afa36c04a..603416cf9ed 100644 --- a/packages/client/lib/client/socket.ts +++ b/packages/client/lib/client/socket.ts @@ -72,6 +72,12 @@ export default class RedisSocket extends EventEmitter { #isSocketUnrefed = false; + #socketEpoch = 0; + + get socketEpoch() { + return this.#socketEpoch; + } + constructor(initiator: RedisSocketInitiator, options?: RedisSocketOptions) { super(); @@ -212,6 +218,7 @@ export default class RedisSocket extends EventEmitter { throw err; } this.#isReady = true; + this.#socketEpoch++; this.emit('ready'); } catch (err) { const retryIn = this.#shouldReconnect(retries++, err as Error); diff --git a/packages/client/lib/cluster/cluster-slots.spec.ts b/packages/client/lib/cluster/cluster-slots.spec.ts new file mode 100644 index 00000000000..bea1453037a --- /dev/null +++ b/packages/client/lib/cluster/cluster-slots.spec.ts @@ -0,0 +1,48 @@ +import { strict as assert } from 'node:assert'; +import { EventEmitter } from 'node:events'; +import { RedisClusterOptions, RedisClusterClientOptions } from './index'; +import RedisClusterSlots from './cluster-slots'; + +describe('RedisClusterSlots', () => { + describe('initialization', () => { + + describe('clientSideCache validation', () => { + const mockEmit = ((_event: string | symbol, ..._args: any[]): boolean => true) as EventEmitter['emit']; + const clientSideCacheConfig = { ttl: 0, maxEntries: 0 }; + const rootNodes: Array = [ + { socket: { host: 'localhost', port: 30001 } } + ]; + + it('should throw error when clientSideCache is enabled with RESP 2', () => { + assert.throws( + () => new RedisClusterSlots({ + rootNodes, + clientSideCache: clientSideCacheConfig, + RESP: 2 as const, + }, mockEmit), + new Error('Client Side Caching is only supported with RESP3') + ); + }); + + it('should throw error when clientSideCache is enabled with RESP undefined', () => { + assert.throws( + () => new RedisClusterSlots({ + rootNodes, + clientSideCache: clientSideCacheConfig, + }, mockEmit), + new Error('Client Side Caching is only supported with RESP3') + ); + }); + + it('should not throw when clientSideCache is enabled with RESP 3', () => { + assert.doesNotThrow(() => + new RedisClusterSlots({ + rootNodes, + clientSideCache: clientSideCacheConfig, + RESP: 3 as const, + }, mockEmit) + ); + }); + }); + }); +}); diff --git a/packages/client/lib/cluster/cluster-slots.ts b/packages/client/lib/cluster/cluster-slots.ts index 0679b200349..84486112320 100644 --- a/packages/client/lib/cluster/cluster-slots.ts +++ b/packages/client/lib/cluster/cluster-slots.ts @@ -6,6 +6,7 @@ import { ChannelListeners, PUBSUB_TYPE, PubSubTypeListeners } from '../client/pu import { RedisArgument, RedisFunctions, RedisModules, RedisScripts, RespVersions, TypeMapping } from '../RESP/types'; import calculateSlot from 'cluster-key-slot'; import { RedisSocketOptions } from '../client/socket'; +import { BasicPooledClientSideCache, PooledClientSideCacheProvider } from '../client/cache'; interface NodeAddress { host: string; @@ -111,6 +112,7 @@ export default class RedisClusterSlots< replicas = new Array>(); readonly nodeByAddress = new Map | ShardNode>(); pubSubNode?: PubSubNode; + clientSideCache?: PooledClientSideCacheProvider; #isOpen = false; @@ -118,12 +120,28 @@ export default class RedisClusterSlots< return this.#isOpen; } + #validateOptions(options?: RedisClusterOptions) { + if (options?.clientSideCache && options?.RESP !== 3) { + throw new Error('Client Side Caching is only supported with RESP3'); + } + } + constructor( options: RedisClusterOptions, emit: EventEmitter['emit'] ) { + this.#validateOptions(options); this.#options = options; - this.#clientFactory = RedisClient.factory(options); + + if (options?.clientSideCache) { + if (options.clientSideCache instanceof PooledClientSideCacheProvider) { + this.clientSideCache = options.clientSideCache; + } else { + this.clientSideCache = new BasicPooledClientSideCache(options.clientSideCache) + } + } + + this.#clientFactory = RedisClient.factory(this.#options); this.#emit = emit; } @@ -164,6 +182,8 @@ export default class RedisClusterSlots< } async #discover(rootNode: RedisClusterClientOptions) { + this.clientSideCache?.clear(); + this.clientSideCache?.disable(); try { const addressesInUse = new Set(), promises: Array> = [], @@ -219,6 +239,7 @@ export default class RedisClusterSlots< } await Promise.all(promises); + this.clientSideCache?.enable(); return true; } catch (err) { @@ -314,6 +335,8 @@ export default class RedisClusterSlots< #createClient(node: ShardNode, readonly = node.readonly) { return this.#clientFactory( this.#clientOptionsDefaults({ + clientSideCache: this.clientSideCache, + RESP: this.#options.RESP, socket: this.#getNodeAddress(node.address) ?? { host: node.host, port: node.port diff --git a/packages/client/lib/cluster/index.ts b/packages/client/lib/cluster/index.ts index 8b37f9c1aa7..f7385629262 100644 --- a/packages/client/lib/cluster/index.ts +++ b/packages/client/lib/cluster/index.ts @@ -9,11 +9,10 @@ import RedisClusterMultiCommand, { RedisClusterMultiCommandType } from './multi- import { PubSubListener } from '../client/pub-sub'; import { ErrorReply } from '../errors'; import { RedisTcpSocketOptions } from '../client/socket'; -import ASKING from '../commands/ASKING'; +import { ClientSideCacheConfig, PooledClientSideCacheProvider } from '../client/cache'; import { BasicCommandParser } from '../client/parser'; -import { parseArgs } from '../commands/generic-transformers'; -import SingleEntryCache from '../single-entry-cache'; - +import { ASKING_CMD } from '../commands/ASKING'; +import SingleEntryCache from '../single-entry-cache' interface ClusterCommander< M extends RedisModules, F extends RedisFunctions, @@ -67,6 +66,41 @@ export interface RedisClusterOptions< * Useful when the cluster is running on another network */ nodeAddressMap?: NodeAddressMap; + /** + * Client Side Caching configuration for the pool. + * + * Enables Redis Servers and Clients to work together to cache results from commands + * sent to a server. The server will notify the client when cached results are no longer valid. + * In pooled mode, the cache is shared across all clients in the pool. + * + * Note: Client Side Caching is only supported with RESP3. + * + * @example Anonymous cache configuration + * ``` + * const client = createCluster({ + * clientSideCache: { + * ttl: 0, + * maxEntries: 0, + * evictPolicy: "LRU" + * }, + * minimum: 5 + * }); + * ``` + * + * @example Using a controllable cache + * ``` + * const cache = new BasicPooledClientSideCache({ + * ttl: 0, + * maxEntries: 0, + * evictPolicy: "LRU" + * }); + * const client = createCluster({ + * clientSideCache: cache, + * minimum: 5 + * }); + * ``` + */ + clientSideCache?: PooledClientSideCacheProvider | ClientSideCacheConfig; } // remove once request & response policies are ready @@ -149,6 +183,7 @@ export default class RedisCluster< > extends EventEmitter { static #createCommand(command: Command, resp: RespVersions) { const transformReply = getTransformReply(command, resp); + return async function (this: ProxyCluster, ...args: Array) { const parser = new BasicCommandParser(); command.parseCommand(parser, ...args); @@ -273,6 +308,10 @@ export default class RedisCluster< return this._self.#slots.slots; } + get clientSideCache() { + return this._self.#slots.clientSideCache; + } + /** * An array of the cluster masters. * Use with {@link RedisCluster.prototype.nodeClient} to get the client for a specific master node. @@ -390,6 +429,27 @@ export default class RedisCluster< // return this._commandOptionsProxy('policies', policies); // } + #handleAsk( + fn: (client: RedisClientType, opts?: ClusterCommandOptions) => Promise + ) { + return async (client: RedisClientType, options?: ClusterCommandOptions) => { + const chainId = Symbol("asking chain"); + const opts = options ? {...options} : {}; + opts.chainId = chainId; + + + + const ret = await Promise.all( + [ + client.sendCommand([ASKING_CMD], {chainId: chainId}), + fn(client, opts) + ] + ); + + return ret[1]; + }; + } + async #execute( firstKey: RedisArgument | undefined, isReadonly: boolean | undefined, @@ -399,14 +459,15 @@ export default class RedisCluster< const maxCommandRedirections = this.#options.maxCommandRedirections ?? 16; let client = await this.#slots.getClient(firstKey, isReadonly); let i = 0; - let myOpts = options; + + let myFn = fn; while (true) { try { - return await fn(client, myOpts); + return await myFn(client, options); } catch (err) { - // reset to passed in options, if changed by an ask request - myOpts = options; + myFn = fn; + // TODO: error class if (++i > maxCommandRedirections || !(err instanceof Error)) { throw err; @@ -425,13 +486,7 @@ export default class RedisCluster< } client = redirectTo; - - const chainId = Symbol('Asking Chain'); - myOpts = options ? {...options} : {}; - myOpts.chainId = chainId; - - client.sendCommand(parseArgs(ASKING), {chainId: chainId}).catch(err => { console.log(`Asking Failed: ${err}`) } ); - + myFn = this.#handleAsk(fn); continue; } @@ -582,10 +637,12 @@ export default class RedisCluster< } close() { + this._self.#slots.clientSideCache?.onPoolClose(); return this._self.#slots.close(); } destroy() { + this._self.#slots.clientSideCache?.onPoolClose(); return this._self.#slots.destroy(); } diff --git a/packages/client/lib/commands/GEOSEARCH.ts b/packages/client/lib/commands/GEOSEARCH.ts index 8c77fd89239..869dc60bec9 100644 --- a/packages/client/lib/commands/GEOSEARCH.ts +++ b/packages/client/lib/commands/GEOSEARCH.ts @@ -29,12 +29,7 @@ export function parseGeoSearchArguments( from: GeoSearchFrom, by: GeoSearchBy, options?: GeoSearchOptions, - store?: RedisArgument ) { - if (store !== undefined) { - parser.pushKey(store); - } - parser.pushKey(key); if (typeof from === 'string' || from instanceof Buffer) { diff --git a/packages/client/lib/commands/GEOSEARCHSTORE.ts b/packages/client/lib/commands/GEOSEARCHSTORE.ts index eb8e12abe6d..34c6e0988e2 100644 --- a/packages/client/lib/commands/GEOSEARCHSTORE.ts +++ b/packages/client/lib/commands/GEOSEARCHSTORE.ts @@ -17,7 +17,12 @@ export default { options?: GeoSearchStoreOptions ) { parser.push('GEOSEARCHSTORE'); - parseGeoSearchArguments(parser, source, from, by, options, destination); + + if (destination !== undefined) { + parser.pushKey(destination); + } + + parseGeoSearchArguments(parser, source, from, by, options); if (options?.STOREDIST) { parser.push('STOREDIST'); diff --git a/packages/client/lib/sentinel/index.spec.ts b/packages/client/lib/sentinel/index.spec.ts index cf9228c261f..035cf09020d 100644 --- a/packages/client/lib/sentinel/index.spec.ts +++ b/packages/client/lib/sentinel/index.spec.ts @@ -5,11 +5,59 @@ import { RESP_TYPES } from '../RESP/decoder'; import { WatchError } from "../errors"; import { RedisSentinelConfig, SentinelFramework } from "./test-util"; import { RedisSentinelEvent, RedisSentinelType, RedisSentinelClientType, RedisNode } from "./types"; +import RedisSentinel from "./index"; import { RedisModules, RedisFunctions, RedisScripts, RespVersions, TypeMapping, NumberReply } from '../RESP/types'; import { promisify } from 'node:util'; import { exec } from 'node:child_process'; +import { BasicPooledClientSideCache } from '../client/cache' +import { once } from 'node:events' const execAsync = promisify(exec); +describe('RedisSentinel', () => { + describe('initialization', () => { + describe('clientSideCache validation', () => { + const clientSideCacheConfig = { ttl: 0, maxEntries: 0 }; + const options = { + name: 'mymaster', + sentinelRootNodes: [ + { host: 'localhost', port: 26379 } + ] + }; + + it('should throw error when clientSideCache is enabled with RESP 2', () => { + assert.throws( + () => RedisSentinel.create({ + ...options, + clientSideCache: clientSideCacheConfig, + RESP: 2 as const, + }), + new Error('Client Side Caching is only supported with RESP3') + ); + }); + + it('should throw error when clientSideCache is enabled with RESP undefined', () => { + assert.throws( + () => RedisSentinel.create({ + ...options, + clientSideCache: clientSideCacheConfig, + }), + new Error('Client Side Caching is only supported with RESP3') + ); + }); + + it('should not throw when clientSideCache is enabled with RESP 3', () => { + assert.doesNotThrow(() => + RedisSentinel.create({ + ...options, + clientSideCache: clientSideCacheConfig, + RESP: 3 as const, + }) + ); + }); + }); + }); +}); + [GLOBAL.SENTINEL.OPEN, GLOBAL.SENTINEL.PASSWORD].forEach(testOptions => { const passIndex = testOptions.serverArguments.indexOf('--requirepass')+1; let password: string | undefined = undefined; @@ -967,6 +1015,34 @@ describe.skip('legacy tests', () => { tracer.push("added node and waiting on added promise"); await nodeAddedPromise; }) + + it('with client side caching', async function() { + this.timeout(30000); + const csc = new BasicPooledClientSideCache(); + + sentinel = frame.getSentinelClient({nodeClientOptions: {RESP: 3}, clientSideCache: csc, masterPoolSize: 5}); + await sentinel.connect(); + + await sentinel.set('x', 1); + await sentinel.get('x'); + await sentinel.get('x'); + await sentinel.get('x'); + await sentinel.get('x'); + + assert.equal(1, csc.cacheMisses()); + assert.equal(3, csc.cacheHits()); + + const invalidatePromise = once(csc, 'invalidate'); + await sentinel.set('x', 2); + await invalidatePromise; + await sentinel.get('x'); + await sentinel.get('x'); + await sentinel.get('x'); + await sentinel.get('x'); + + assert.equal(csc.cacheMisses(), 2); + assert.equal(csc.cacheHits(), 6); + }) }); }); diff --git a/packages/client/lib/sentinel/index.ts b/packages/client/lib/sentinel/index.ts index 3bf94abd819..ec570e64bf2 100644 --- a/packages/client/lib/sentinel/index.ts +++ b/packages/client/lib/sentinel/index.ts @@ -16,6 +16,7 @@ import { RedisVariadicArgument } from '../commands/generic-transformers'; import { WaitQueue } from './wait-queue'; import { TcpNetConnectOpts } from 'node:net'; import { RedisTcpSocketOptions } from '../client/socket'; +import { BasicPooledClientSideCache, PooledClientSideCacheProvider } from '../client/cache'; interface ClientInfo { id: number; @@ -301,6 +302,10 @@ export default class RedisSentinel< #masterClientCount = 0; #masterClientInfo?: ClientInfo; + get clientSideCache() { + return this._self.#internal.clientSideCache; + } + constructor(options: RedisSentinelOptions) { super(); @@ -617,7 +622,7 @@ class RedisSentinelInternal< readonly #name: string; readonly #nodeClientOptions: RedisClientOptions; - readonly #sentinelClientOptions: RedisClientOptions; + readonly #sentinelClientOptions: RedisClientOptions; readonly #scanInterval: number; readonly #passthroughClientErrorEvents: boolean; @@ -650,9 +655,22 @@ class RedisSentinelInternal< #trace: (msg: string) => unknown = () => { }; + #clientSideCache?: PooledClientSideCacheProvider; + get clientSideCache() { + return this.#clientSideCache; + } + + #validateOptions(options?: RedisSentinelOptions) { + if (options?.clientSideCache && options?.RESP !== 3) { + throw new Error('Client Side Caching is only supported with RESP3'); + } + } + constructor(options: RedisSentinelOptions) { super(); + this.#validateOptions(options); + this.#name = options.name; this.#sentinelRootNodes = Array.from(options.sentinelRootNodes); @@ -662,11 +680,21 @@ class RedisSentinelInternal< this.#scanInterval = options.scanInterval ?? 0; this.#passthroughClientErrorEvents = options.passthroughClientErrorEvents ?? false; - this.#nodeClientOptions = options.nodeClientOptions ? Object.assign({} as RedisClientOptions, options.nodeClientOptions) : {}; + this.#nodeClientOptions = (options.nodeClientOptions ? {...options.nodeClientOptions} : {}) as RedisClientOptions; if (this.#nodeClientOptions.url !== undefined) { throw new Error("invalid nodeClientOptions for Sentinel"); } + if (options.clientSideCache) { + if (options.clientSideCache instanceof PooledClientSideCacheProvider) { + this.#clientSideCache = this.#nodeClientOptions.clientSideCache = options.clientSideCache; + } else { + const cscConfig = options.clientSideCache; + this.#clientSideCache = this.#nodeClientOptions.clientSideCache = new BasicPooledClientSideCache(cscConfig); +// this.#clientSideCache = this.#nodeClientOptions.clientSideCache = new PooledNoRedirectClientSideCache(cscConfig); + } + } + this.#sentinelClientOptions = options.sentinelClientOptions ? Object.assign({} as RedisClientOptions, options.sentinelClientOptions) : {}; this.#sentinelClientOptions.modules = RedisSentinelModule; @@ -904,6 +932,8 @@ class RedisSentinelInternal< this.#isReady = false; + this.#clientSideCache?.onPoolClose(); + if (this.#scanTimer) { clearInterval(this.#scanTimer); this.#scanTimer = undefined; @@ -952,6 +982,8 @@ class RedisSentinelInternal< this.#isReady = false; + this.#clientSideCache?.onPoolClose(); + if (this.#scanTimer) { clearInterval(this.#scanTimer); this.#scanTimer = undefined; diff --git a/packages/client/lib/sentinel/test-util.ts b/packages/client/lib/sentinel/test-util.ts index 60696bc3437..86bc5b31786 100644 --- a/packages/client/lib/sentinel/test-util.ts +++ b/packages/client/lib/sentinel/test-util.ts @@ -188,18 +188,22 @@ export class SentinelFramework extends DockerBase { } const options: RedisSentinelOptions = { + ...opts, name: this.config.sentinelName, sentinelRootNodes: this.#sentinelList.map((sentinel) => { return { host: '127.0.0.1', port: sentinel.docker.port } }), passthroughClientErrorEvents: errors } if (this.config.password !== undefined) { - options.nodeClientOptions = {password: this.config.password}; - options.sentinelClientOptions = {password: this.config.password}; - } + if (!options.nodeClientOptions) { + options.nodeClientOptions = {}; + } + options.nodeClientOptions.password = this.config.password; - if (opts) { - Object.assign(options, opts); + if (!options.sentinelClientOptions) { + options.sentinelClientOptions = {}; + } + options.sentinelClientOptions = {password: this.config.password}; } return RedisSentinel.create(options); diff --git a/packages/client/lib/sentinel/types.ts b/packages/client/lib/sentinel/types.ts index 28a5a91ddd3..e72f2eec2a0 100644 --- a/packages/client/lib/sentinel/types.ts +++ b/packages/client/lib/sentinel/types.ts @@ -4,6 +4,7 @@ import { CommandSignature, CommanderConfig, RedisFunctions, RedisModules, RedisS import COMMANDS from '../commands'; import RedisSentinel, { RedisSentinelClient } from '.'; import { RedisTcpSocketOptions } from '../client/socket'; +import { ClientSideCacheConfig, PooledClientSideCacheProvider } from '../client/cache'; export interface RedisNode { host: string; @@ -67,6 +68,41 @@ export interface RedisSentinelOptions< * When `false`, the sentinel object will wait for the first available client from the pool. */ reserveClient?: boolean; + /** + * Client Side Caching configuration for the pool. + * + * Enables Redis Servers and Clients to work together to cache results from commands + * sent to a server. The server will notify the client when cached results are no longer valid. + * In pooled mode, the cache is shared across all clients in the pool. + * + * Note: Client Side Caching is only supported with RESP3. + * + * @example Anonymous cache configuration + * ``` + * const client = createSentinel({ + * clientSideCache: { + * ttl: 0, + * maxEntries: 0, + * evictPolicy: "LRU" + * }, + * minimum: 5 + * }); + * ``` + * + * @example Using a controllable cache + * ``` + * const cache = new BasicPooledClientSideCache({ + * ttl: 0, + * maxEntries: 0, + * evictPolicy: "LRU" + * }); + * const client = createSentinel({ + * clientSideCache: cache, + * minimum: 5 + * }); + * ``` + */ + clientSideCache?: PooledClientSideCacheProvider | ClientSideCacheConfig; } export interface SentinelCommander< diff --git a/packages/client/lib/sentinel/utils.ts b/packages/client/lib/sentinel/utils.ts index 90b789ddca9..7e2404c2f7a 100644 --- a/packages/client/lib/sentinel/utils.ts +++ b/packages/client/lib/sentinel/utils.ts @@ -1,5 +1,5 @@ -import { BasicCommandParser } from '../client/parser'; import { ArrayReply, Command, RedisFunction, RedisScript, RespVersions, UnwrapReply } from '../RESP/types'; +import { BasicCommandParser } from '../client/parser'; import { RedisSocketOptions, RedisTcpSocketOptions } from '../client/socket'; import { functionArgumentsPrefix, getTransformReply, scriptArgumentsPrefix } from '../commander'; import { NamespaceProxySentinel, NamespaceProxySentinelClient, ProxySentinel, ProxySentinelClient, RedisNode } from './types'; diff --git a/packages/redis/README.md b/packages/redis/README.md index f0b2a34905d..d04a19b0d71 100644 --- a/packages/redis/README.md +++ b/packages/redis/README.md @@ -234,6 +234,23 @@ of sending a `QUIT` command to the server, the client can simply close the netwo ```typescript client.destroy(); ``` +### Client Side Caching + +Node Redis v5 adds support for [Client Side Caching](https://redis.io/docs/manual/client-side-caching/), which enables clients to cache query results locally. The Redis server will notify the client when cached results are no longer valid. + +```typescript +// Enable client side caching with RESP3 +const client = createClient({ + RESP: 3, + clientSideCache: { + ttl: 0, // Time-to-live (0 = no expiration) + maxEntries: 0, // Maximum entries (0 = unlimited) + evictPolicy: "LRU" // Eviction policy: "LRU" or "FIFO" + } +}); +``` + +See the [V5 documentation](../../docs/v5.md#client-side-caching) for more details and advanced usage. ### Auto-Pipelining diff --git a/packages/test-utils/lib/index.ts b/packages/test-utils/lib/index.ts index 8ed85bf6e3e..d92c5c9e3d8 100644 --- a/packages/test-utils/lib/index.ts +++ b/packages/test-utils/lib/index.ts @@ -450,7 +450,7 @@ export default class TestUtils { await fn(pool); } finally { await pool.flushAll(); - pool.destroy(); + pool.close(); } }); } From d0a5c4c94513023b785687064f2c43bf6cad8ef2 Mon Sep 17 00:00:00 2001 From: Hristo Temelski Date: Tue, 20 May 2025 11:04:11 +0300 Subject: [PATCH 1579/1748] Fix sentinel csc tests (#2966) Co-authored-by: H. Temelski --- packages/client/lib/sentinel/index.spec.ts | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/packages/client/lib/sentinel/index.spec.ts b/packages/client/lib/sentinel/index.spec.ts index 035cf09020d..a2e52b774bb 100644 --- a/packages/client/lib/sentinel/index.spec.ts +++ b/packages/client/lib/sentinel/index.spec.ts @@ -1029,8 +1029,8 @@ describe.skip('legacy tests', () => { await sentinel.get('x'); await sentinel.get('x'); - assert.equal(1, csc.cacheMisses()); - assert.equal(3, csc.cacheHits()); + assert.equal(1, csc.stats().missCount); + assert.equal(3, csc.stats().hitCount); const invalidatePromise = once(csc, 'invalidate'); await sentinel.set('x', 2); @@ -1040,8 +1040,8 @@ describe.skip('legacy tests', () => { await sentinel.get('x'); await sentinel.get('x'); - assert.equal(csc.cacheMisses(), 2); - assert.equal(csc.cacheHits(), 6); + assert.equal(csc.stats().missCount, 2); + assert.equal(csc.stats().hitCount, 6); }) }); }); From f3d1d3352e28a8fe6f535f9459c2278c153b0599 Mon Sep 17 00:00:00 2001 From: Nikolay Karadzhov Date: Tue, 20 May 2025 14:28:15 +0300 Subject: [PATCH 1580/1748] feat(client): expose socketTimeout option (#2965) The maximum duration (in milliseconds) that the socket can remain idle (i.e., with no data sent or received) before being automatically closed. Default reconnectionStrategy will ignore the new SocketTimeoutError, but users are allowed to have custom strategies wich handle those errors in different ways --- docs/client-configuration.md | 8 ++- packages/client/lib/client/socket.spec.ts | 69 +++++++++++++++++++++-- packages/client/lib/client/socket.ts | 24 +++++++- packages/client/lib/errors.ts | 6 ++ 4 files changed, 99 insertions(+), 8 deletions(-) diff --git a/docs/client-configuration.md b/docs/client-configuration.md index 0564794ac46..1c9ba51a11d 100644 --- a/docs/client-configuration.md +++ b/docs/client-configuration.md @@ -9,6 +9,7 @@ | socket.family | `0` | IP Stack version (one of `4 \| 6 \| 0`) | | socket.path | | Path to the UNIX Socket | | socket.connectTimeout | `5000` | Connection timeout (in milliseconds) | +| socket.socketTimeout | | The maximum duration (in milliseconds) that the socket can remain idle (i.e., with no data sent or received) before being automatically closed | | socket.noDelay | `true` | Toggle [`Nagle's algorithm`](https://nodejs.org/api/net.html#net_socket_setnodelay_nodelay) | | socket.keepAlive | `true` | Toggle [`keep-alive`](https://nodejs.org/api/net.html#socketsetkeepaliveenable-initialdelay) functionality | | socket.keepAliveInitialDelay | `5000` | If set to a positive number, it sets the initial delay before the first keepalive probe is sent on an idle socket | @@ -40,7 +41,12 @@ By default the strategy uses exponential backoff, but it can be overwritten like ```javascript createClient({ socket: { - reconnectStrategy: retries => { + reconnectStrategy: (retries, cause) => { + // By default, do not reconnect on socket timeout. + if (cause instanceof SocketTimeoutError) { + return false; + } + // Generate a random jitter between 0 – 200 ms: const jitter = Math.floor(Math.random() * 200); // Delay is an exponential back off, (times^2) * 50 ms, with a maximum value of 2000 ms: diff --git a/packages/client/lib/client/socket.spec.ts b/packages/client/lib/client/socket.spec.ts index 20b238a3a38..5117cc4f49d 100644 --- a/packages/client/lib/client/socket.spec.ts +++ b/packages/client/lib/client/socket.spec.ts @@ -2,13 +2,12 @@ import { strict as assert } from 'node:assert'; import { spy } from 'sinon'; import { once } from 'node:events'; import RedisSocket, { RedisSocketOptions } from './socket'; +import testUtils, { GLOBAL } from '../test-utils'; +import { setTimeout } from 'timers/promises'; describe('Socket', () => { function createSocket(options: RedisSocketOptions): RedisSocket { - const socket = new RedisSocket( - () => Promise.resolve(), - options - ); + const socket = new RedisSocket(() => Promise.resolve(), options); socket.on('error', () => { // ignore errors @@ -84,4 +83,66 @@ describe('Socket', () => { assert.equal(socket.isOpen, false); }); }); + + describe('socketTimeout', () => { + const timeout = 50; + testUtils.testWithClient( + 'should timeout with positive socketTimeout values', + async client => { + let timedOut = false; + + assert.equal(client.isReady, true, 'client.isReady'); + assert.equal(client.isOpen, true, 'client.isOpen'); + + client.on('error', err => { + assert.equal( + err.message, + `Socket timeout timeout. Expecting data, but didn't receive any in ${timeout}ms.` + ); + + assert.equal(client.isReady, false, 'client.isReady'); + + // This is actually a bug with the onSocketError implementation, + // the client should be closed before the error is emitted + process.nextTick(() => { + assert.equal(client.isOpen, false, 'client.isOpen'); + }); + + timedOut = true; + }); + await setTimeout(timeout * 2); + if (!timedOut) assert.fail('Should have timed out by now'); + }, + { + ...GLOBAL.SERVERS.OPEN, + clientOptions: { + socket: { + socketTimeout: timeout + } + } + } + ); + + testUtils.testWithClient( + 'should not timeout with undefined socketTimeout', + async client => { + + assert.equal(client.isReady, true, 'client.isReady'); + assert.equal(client.isOpen, true, 'client.isOpen'); + + client.on('error', err => { + assert.fail('Should not have timed out or errored in any way'); + }); + await setTimeout(100); + }, + { + ...GLOBAL.SERVERS.OPEN, + clientOptions: { + socket: { + socketTimeout: undefined + } + } + } + ); + }); }); diff --git a/packages/client/lib/client/socket.ts b/packages/client/lib/client/socket.ts index 603416cf9ed..58ccbe0b0c5 100644 --- a/packages/client/lib/client/socket.ts +++ b/packages/client/lib/client/socket.ts @@ -1,7 +1,7 @@ import { EventEmitter, once } from 'node:events'; import net from 'node:net'; import tls from 'node:tls'; -import { ConnectionTimeoutError, ClientClosedError, SocketClosedUnexpectedlyError, ReconnectStrategyError } from '../errors'; +import { ConnectionTimeoutError, ClientClosedError, SocketClosedUnexpectedlyError, ReconnectStrategyError, SocketTimeoutError } from '../errors'; import { setTimeout } from 'node:timers/promises'; import { RedisArgument } from '../RESP/types'; @@ -23,6 +23,10 @@ type RedisSocketOptionsCommon = { * 3. `(retries: number, cause: Error) => false | number | Error` -> `number` is the same as configuring a `number` directly, `Error` is the same as `false`, but with a custom error. */ reconnectStrategy?: false | number | ReconnectStrategyFunction; + /** + * The timeout (in milliseconds) after which the socket will be closed. `undefined` means no timeout. + */ + socketTimeout?: number; } type RedisTcpOptions = RedisSocketOptionsCommon & NetOptions & Omit< @@ -55,6 +59,7 @@ export default class RedisSocket extends EventEmitter { readonly #connectTimeout; readonly #reconnectStrategy; readonly #socketFactory; + readonly #socketTimeout; #socket?: net.Socket | tls.TLSSocket; @@ -85,6 +90,7 @@ export default class RedisSocket extends EventEmitter { this.#connectTimeout = options?.connectTimeout ?? 5000; this.#reconnectStrategy = this.#createReconnectStrategy(options); this.#socketFactory = this.#createSocketFactory(options); + this.#socketTimeout = options?.socketTimeout; } #createReconnectStrategy(options?: RedisSocketOptions): ReconnectStrategyFunction { @@ -103,7 +109,7 @@ export default class RedisSocket extends EventEmitter { return retryIn; } catch (err) { this.emit('error', err); - return this.defaultReconnectStrategy(retries); + return this.defaultReconnectStrategy(retries, err); } }; } @@ -253,6 +259,13 @@ export default class RedisSocket extends EventEmitter { socket.removeListener('timeout', onTimeout); } + if (this.#socketTimeout) { + socket.once('timeout', () => { + socket.destroy(new SocketTimeoutError(this.#socketTimeout!)); + }); + socket.setTimeout(this.#socketTimeout); + } + socket .once('error', err => this.#onSocketError(err)) .once('close', hadError => { @@ -341,7 +354,12 @@ export default class RedisSocket extends EventEmitter { this.#socket?.unref(); } - defaultReconnectStrategy(retries: number) { + defaultReconnectStrategy(retries: number, cause: unknown) { + // By default, do not reconnect on socket timeout. + if (cause instanceof SocketTimeoutError) { + return false; + } + // Generate a random jitter between 0 – 200 ms: const jitter = Math.floor(Math.random() * 200); // Delay is an exponential back off, (times^2) * 50 ms, with a maximum value of 2000 ms: diff --git a/packages/client/lib/errors.ts b/packages/client/lib/errors.ts index 8af4c5e5bed..db37ec1a9ba 100644 --- a/packages/client/lib/errors.ts +++ b/packages/client/lib/errors.ts @@ -16,6 +16,12 @@ export class ConnectionTimeoutError extends Error { } } +export class SocketTimeoutError extends Error { + constructor(timeout: number) { + super(`Socket timeout timeout. Expecting data, but didn't receive any in ${timeout}ms.`); + } +} + export class ClientClosedError extends Error { constructor() { super('The client is closed'); From 4a5f879ec9763d5522d3f0d9cf985a8418b50315 Mon Sep 17 00:00:00 2001 From: Nikolay Karadzhov Date: Tue, 20 May 2025 15:15:09 +0300 Subject: [PATCH 1581/1748] fix(client): bring disableClientInfo option back (#2959) * fix(client): bring disableClientInfo option back It disappeared in v5 fixes #2958 --- packages/client/lib/client/index.ts | 131 ++++++++++++------ .../client/lib/commands/CLIENT_INFO.spec.ts | 86 ++++++++++++ packages/client/lib/commands/CLIENT_INFO.ts | 13 +- packages/client/lib/errors.ts | 6 +- packages/client/tsconfig.json | 7 +- tsconfig.base.json | 3 +- 6 files changed, 196 insertions(+), 50 deletions(-) diff --git a/packages/client/lib/client/index.ts b/packages/client/lib/client/index.ts index c7f94fe680a..8d98aa8ed26 100644 --- a/packages/client/lib/client/index.ts +++ b/packages/client/lib/client/index.ts @@ -4,11 +4,11 @@ import { BasicAuth, CredentialsError, CredentialsProvider, StreamingCredentialsP import RedisCommandsQueue, { CommandOptions } from './commands-queue'; import { EventEmitter } from 'node:events'; import { attachConfig, functionArgumentsPrefix, getTransformReply, scriptArgumentsPrefix } from '../commander'; -import { ClientClosedError, ClientOfflineError, DisconnectsClientError, WatchError } from '../errors'; +import { ClientClosedError, ClientOfflineError, DisconnectsClientError, SimpleError, WatchError } from '../errors'; import { URL } from 'node:url'; import { TcpSocketConnectOpts } from 'node:net'; import { PUBSUB_TYPE, PubSubType, PubSubListener, PubSubTypeListeners, ChannelListeners } from './pub-sub'; -import { Command, CommandSignature, TypeMapping, CommanderConfig, RedisFunction, RedisFunctions, RedisModules, RedisScript, RedisScripts, ReplyUnion, RespVersions, RedisArgument, ReplyWithTypeMapping, SimpleStringReply, TransformReply } from '../RESP/types'; +import { Command, CommandSignature, TypeMapping, CommanderConfig, RedisFunction, RedisFunctions, RedisModules, RedisScript, RedisScripts, ReplyUnion, RespVersions, RedisArgument, ReplyWithTypeMapping, SimpleStringReply, TransformReply, CommandArguments } from '../RESP/types'; import RedisClientMultiCommand, { RedisClientMultiCommandType } from './multi-command'; import { RedisMultiQueuedCommand } from '../multi-command'; import HELLO, { HelloOptions } from '../commands/HELLO'; @@ -19,6 +19,7 @@ import { RedisVariadicArgument, parseArgs, pushVariadicArguments } from '../comm import { BasicClientSideCache, ClientSideCacheConfig, ClientSideCacheProvider } from './cache'; import { BasicCommandParser, CommandParser } from './parser'; import SingleEntryCache from '../single-entry-cache'; +import { version } from '../../package.json' export interface RedisClientOptions< M extends RedisModules = RedisModules, @@ -135,6 +136,14 @@ export interface RedisClientOptions< * ``` */ clientSideCache?: ClientSideCacheProvider | ClientSideCacheConfig; + /** + * If set to true, disables sending client identifier (user-agent like message) to the redis server + */ + disableClientInfo?: boolean; + /** + * Tag to append to library name that is sent to the Redis server + */ + clientInfoTag?: string; } type WithCommands< @@ -514,7 +523,28 @@ export default class RedisClient< }); } - async #handshake(selectedDB: number) { + async #handshake(chainId: symbol, asap: boolean) { + const promises = []; + const commandsWithErrorHandlers = await this.#getHandshakeCommands(); + + if (asap) commandsWithErrorHandlers.reverse() + + for (const { cmd, errorHandler } of commandsWithErrorHandlers) { + promises.push( + this.#queue + .addCommand(cmd, { + chainId, + asap + }) + .catch(errorHandler) + ); + } + return promises; + } + + async #getHandshakeCommands(): Promise< + Array<{ cmd: CommandArguments } & { errorHandler?: (err: Error) => void }> + > { const commands = []; const cp = this.#options?.credentialsProvider; @@ -532,8 +562,8 @@ export default class RedisClient< } if (cp && cp.type === 'streaming-credentials-provider') { - - const [credentials, disposable] = await this.#subscribeForStreamingCredentials(cp) + const [credentials, disposable] = + await this.#subscribeForStreamingCredentials(cp); this.#credentialsSubscription = disposable; if (credentials.password) { @@ -548,59 +578,88 @@ export default class RedisClient< hello.SETNAME = this.#options.name; } - commands.push( - parseArgs(HELLO, this.#options.RESP, hello) - ); + commands.push({ cmd: parseArgs(HELLO, this.#options.RESP, hello) }); } else { - if (cp && cp.type === 'async-credentials-provider') { - const credentials = await cp.credentials(); if (credentials.username || credentials.password) { - commands.push( - parseArgs(COMMANDS.AUTH, { + commands.push({ + cmd: parseArgs(COMMANDS.AUTH, { username: credentials.username, password: credentials.password ?? '' }) - ); + }); } } if (cp && cp.type === 'streaming-credentials-provider') { - - const [credentials, disposable] = await this.#subscribeForStreamingCredentials(cp) + const [credentials, disposable] = + await this.#subscribeForStreamingCredentials(cp); this.#credentialsSubscription = disposable; if (credentials.username || credentials.password) { - commands.push( - parseArgs(COMMANDS.AUTH, { + commands.push({ + cmd: parseArgs(COMMANDS.AUTH, { username: credentials.username, password: credentials.password ?? '' }) - ); + }); } } if (this.#options?.name) { - commands.push( - parseArgs(COMMANDS.CLIENT_SETNAME, this.#options.name) - ); + commands.push({ + cmd: parseArgs(COMMANDS.CLIENT_SETNAME, this.#options.name) + }); } } - if (selectedDB !== 0) { - commands.push(['SELECT', this.#selectedDB.toString()]); + if (this.#selectedDB !== 0) { + commands.push({ cmd: ['SELECT', this.#selectedDB.toString()] }); } if (this.#options?.readonly) { - commands.push( - parseArgs(COMMANDS.READONLY) - ); + commands.push({ cmd: parseArgs(COMMANDS.READONLY) }); + } + + if (!this.#options?.disableClientInfo) { + commands.push({ + cmd: ['CLIENT', 'SETINFO', 'LIB-VER', version], + errorHandler: (err: Error) => { + // Only throw if not a SimpleError - unknown subcommand + // Client libraries are expected to ignore failures + // of type SimpleError - unknown subcommand, which are + // expected from older servers ( < v7 ) + if (!(err instanceof SimpleError) || !err.isUnknownSubcommand()) { + throw err; + } + } + }); + + commands.push({ + cmd: [ + 'CLIENT', + 'SETINFO', + 'LIB-NAME', + this.#options?.clientInfoTag + ? `node-redis(${this.#options.clientInfoTag})` + : 'node-redis' + ], + errorHandler: (err: Error) => { + // Only throw if not a SimpleError - unknown subcommand + // Client libraries are expected to ignore failures + // of type SimpleError - unknown subcommand, which are + // expected from older servers ( < v7 ) + if (!(err instanceof SimpleError) || !err.isUnknownSubcommand()) { + throw err; + } + } + }); } if (this.#clientSideCache) { - commands.push(this.#clientSideCache.trackingOn()); + commands.push({cmd: this.#clientSideCache.trackingOn()}); } return commands; @@ -629,15 +688,7 @@ export default class RedisClient< ); } - const commands = await this.#handshake(this.#selectedDB); - for (let i = commands.length - 1; i >= 0; --i) { - promises.push( - this.#queue.addCommand(commands[i], { - chainId, - asap: true - }) - ); - } + promises.push(...(await this.#handshake(chainId, true))); if (promises.length) { this.#write(); @@ -1221,13 +1272,7 @@ export default class RedisClient< selectedDB = this._self.#options?.database ?? 0; this._self.#credentialsSubscription?.dispose(); this._self.#credentialsSubscription = null; - for (const command of (await this._self.#handshake(selectedDB))) { - promises.push( - this._self.#queue.addCommand(command, { - chainId - }) - ); - } + promises.push(...(await this._self.#handshake(chainId, false))); this._self.#scheduleWrite(); await Promise.all(promises); this._self.#selectedDB = selectedDB; diff --git a/packages/client/lib/commands/CLIENT_INFO.spec.ts b/packages/client/lib/commands/CLIENT_INFO.spec.ts index 50345a46ce3..96881e6c1aa 100644 --- a/packages/client/lib/commands/CLIENT_INFO.spec.ts +++ b/packages/client/lib/commands/CLIENT_INFO.spec.ts @@ -2,6 +2,7 @@ import { strict as assert } from 'node:assert'; import CLIENT_INFO from './CLIENT_INFO'; import testUtils, { GLOBAL } from '../test-utils'; import { parseArgs } from './generic-transformers'; +import { version } from '../../package.json'; describe('CLIENT INFO', () => { testUtils.isVersionGreaterThanHook([6, 2]); @@ -48,4 +49,89 @@ describe('CLIENT INFO', () => { } } }, GLOBAL.SERVERS.OPEN); + + testUtils.testWithClient('client.clientInfo Redis < 7', async client => { + const reply = await client.clientInfo(); + if (!testUtils.isVersionGreaterThan([7])) { + assert.strictEqual(reply.libName, undefined, 'LibName should be undefined for Redis < 7'); + assert.strictEqual(reply.libVer, undefined, 'LibVer should be undefined for Redis < 7'); + } + }, GLOBAL.SERVERS.OPEN); + + testUtils.testWithClientIfVersionWithinRange([[7], 'LATEST'], 'client.clientInfo Redis>=7 info disabled', async client => { + const reply = await client.clientInfo(); + assert.equal(reply.libName, ''); + assert.equal(reply.libVer, ''); + }, { + ...GLOBAL.SERVERS.OPEN, + clientOptions: { + disableClientInfo: true + } + }); + + testUtils.testWithClientIfVersionWithinRange([[7], 'LATEST'], 'client.clientInfo Redis>=7 resp unset, info enabled, tag set', async client => { + const reply = await client.clientInfo(); + assert.equal(reply.libName, 'node-redis(client1)'); + assert.equal(reply.libVer, version); + }, { + ...GLOBAL.SERVERS.OPEN, + clientOptions: { + clientInfoTag: 'client1' + } + }); + + testUtils.testWithClientIfVersionWithinRange([[7], 'LATEST'], 'client.clientInfo Redis>=7 resp unset, info enabled, tag unset', async client => { + const reply = await client.clientInfo(); + assert.equal(reply.libName, 'node-redis'); + assert.equal(reply.libVer, version); + }, GLOBAL.SERVERS.OPEN); + + testUtils.testWithClientIfVersionWithinRange([[7], 'LATEST'], 'client.clientInfo Redis>=7 resp2 info enabled', async client => { + const reply = await client.clientInfo(); + assert.equal(reply.libName, 'node-redis(client1)'); + assert.equal(reply.libVer, version); + }, { + ...GLOBAL.SERVERS.OPEN, + clientOptions: { + RESP: 2, + clientInfoTag: 'client1' + } + }); + + testUtils.testWithClientIfVersionWithinRange([[7], 'LATEST'], 'client.clientInfo Redis>=7 resp2 info disabled', async client => { + const reply = await client.clientInfo(); + assert.equal(reply.libName, ''); + assert.equal(reply.libVer, ''); + }, { + ...GLOBAL.SERVERS.OPEN, + clientOptions: { + disableClientInfo: true, + RESP: 2 + } + }); + + testUtils.testWithClientIfVersionWithinRange([[7], 'LATEST'], 'client.clientInfo Redis>=7 resp3 info enabled', async client => { + const reply = await client.clientInfo(); + assert.equal(reply.libName, 'node-redis(client1)'); + assert.equal(reply.libVer, version); + }, { + ...GLOBAL.SERVERS.OPEN, + clientOptions: { + RESP: 3, + clientInfoTag: 'client1' + } + }); + + testUtils.testWithClientIfVersionWithinRange([[7], 'LATEST'], 'client.clientInfo Redis>=7 resp3 info disabled', async client => { + const reply = await client.clientInfo(); + assert.equal(reply.libName, ''); + assert.equal(reply.libVer, ''); + }, { + ...GLOBAL.SERVERS.OPEN, + clientOptions: { + disableClientInfo: true, + RESP: 3 + } + }); + }); diff --git a/packages/client/lib/commands/CLIENT_INFO.ts b/packages/client/lib/commands/CLIENT_INFO.ts index 36dac175443..8908bdb2600 100644 --- a/packages/client/lib/commands/CLIENT_INFO.ts +++ b/packages/client/lib/commands/CLIENT_INFO.ts @@ -52,6 +52,14 @@ export interface ClientInfoReply { * available since 7.0 */ resp?: number; + /** + * available since 7.0 + */ + libName?: string; + /** + * available since 7.0 + */ + libVer?: string; } const CLIENT_INFO_REGEX = /([^\s=]+)=([^\s]*)/g; @@ -67,7 +75,6 @@ export default { for (const item of rawReply.toString().matchAll(CLIENT_INFO_REGEX)) { map[item[1]] = item[2]; } - const reply: ClientInfoReply = { id: Number(map.id), addr: map.addr, @@ -89,7 +96,9 @@ export default { totMem: Number(map['tot-mem']), events: map.events, cmd: map.cmd, - user: map.user + user: map.user, + libName: map['lib-name'], + libVer: map['lib-ver'] }; if (map.laddr !== undefined) { diff --git a/packages/client/lib/errors.ts b/packages/client/lib/errors.ts index db37ec1a9ba..4f05f62ac4b 100644 --- a/packages/client/lib/errors.ts +++ b/packages/client/lib/errors.ts @@ -70,7 +70,11 @@ export class ErrorReply extends Error { } } -export class SimpleError extends ErrorReply {} +export class SimpleError extends ErrorReply { + isUnknownSubcommand(): boolean { + return this.message.toLowerCase().indexOf('err unknown subcommand') !== -1; + } +} export class BlobError extends ErrorReply {} diff --git a/packages/client/tsconfig.json b/packages/client/tsconfig.json index 8caa47300d4..b1f7b44d915 100644 --- a/packages/client/tsconfig.json +++ b/packages/client/tsconfig.json @@ -1,11 +1,12 @@ { "extends": "../../tsconfig.base.json", "compilerOptions": { - "outDir": "./dist" + "outDir": "./dist", }, "include": [ "./index.ts", - "./lib/**/*.ts" + "./lib/**/*.ts", + "./package.json" ], "exclude": [ "./lib/test-utils.ts", @@ -18,6 +19,6 @@ "./lib" ], "entryPointStrategy": "expand", - "out": "../../documentation/client" + "out": "../../documentation/client", } } diff --git a/tsconfig.base.json b/tsconfig.base.json index bd2bcac0845..d4a631fc008 100644 --- a/tsconfig.base.json +++ b/tsconfig.base.json @@ -15,6 +15,7 @@ "sourceMap": true, "declaration": true, "declarationMap": true, - "allowJs": true + "allowJs": true, + "resolveJsonModule": true } } From 1294b4b8e02d4b079f5556717312650ae172dac4 Mon Sep 17 00:00:00 2001 From: Gary Burgmann Date: Tue, 20 May 2025 22:36:40 +1000 Subject: [PATCH 1582/1748] issue/2956 - document disableClientInfo (#2957) * issue/2956 - document disableClientInfo * issue/2956 - remove accidental disableOfflineQueue bold --- docs/client-configuration.md | 1 + 1 file changed, 1 insertion(+) diff --git a/docs/client-configuration.md b/docs/client-configuration.md index 1c9ba51a11d..57af626bf71 100644 --- a/docs/client-configuration.md +++ b/docs/client-configuration.md @@ -28,6 +28,7 @@ | legacyMode | `false` | Maintain some backwards compatibility (see the [Migration Guide](./v3-to-v4.md)) | | isolationPoolOptions | | An object that configures a pool of isolated connections, If you frequently need isolated connections, consider using [createClientPool](https://github.com/redis/node-redis/blob/master/docs/pool.md#creating-a-pool) instead | | pingInterval | | Send `PING` command at interval (in ms). Useful with ["Azure Cache for Redis"](https://learn.microsoft.com/en-us/azure/azure-cache-for-redis/cache-best-practices-connection#idle-timeout) | +| disableClientInfo | `false` | Disables `CLIENT SETINFO LIB-NAME node-redis` and `CLIENT SETINFO LIB-VER X.X.X` commands | ## Reconnect Strategy From 00845570ac0b728d8a196af5af3f1b436d0f523b Mon Sep 17 00:00:00 2001 From: Nikolay Karadzhov Date: Tue, 20 May 2025 15:53:24 +0300 Subject: [PATCH 1583/1748] Release client@5.1.0 --- packages/client/package.json | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/packages/client/package.json b/packages/client/package.json index 34d60154083..dd41dc53e0b 100644 --- a/packages/client/package.json +++ b/packages/client/package.json @@ -1,6 +1,6 @@ { "name": "@redis/client", - "version": "5.0.1", + "version": "5.1.0", "license": "MIT", "main": "./dist/index.js", "types": "./dist/index.d.ts", From 4f7f83478ba7d89a1131a3c49771608a24236eef Mon Sep 17 00:00:00 2001 From: Nikolay Karadzhov Date: Tue, 20 May 2025 15:57:45 +0300 Subject: [PATCH 1584/1748] Updated the Bloom package to use client@5.1.0 --- package-lock.json | 16 ++++++++++++++-- packages/bloom/package.json | 2 +- 2 files changed, 15 insertions(+), 3 deletions(-) diff --git a/package-lock.json b/package-lock.json index 40d291d9201..bb0da682490 100644 --- a/package-lock.json +++ b/package-lock.json @@ -8529,12 +8529,12 @@ "node": ">= 18" }, "peerDependencies": { - "@redis/client": "^5.0.1" + "@redis/client": "^5.1.0" } }, "packages/client": { "name": "@redis/client", - "version": "5.0.1", + "version": "5.1.0", "license": "MIT", "dependencies": { "cluster-key-slot": "1.1.2" @@ -8632,6 +8632,18 @@ "node": ">= 18" } }, + "packages/redis/node_modules/@redis/client": { + "version": "5.0.1", + "resolved": "https://registry.npmjs.org/@redis/client/-/client-5.0.1.tgz", + "integrity": "sha512-k0EJvlMGEyBqUD3orKe0UMZ66fPtfwqPIr+ZSd853sXj2EyhNtPXSx+J6sENXJNgAlEBhvD+57Dwt0qTisKB0A==", + "license": "MIT", + "dependencies": { + "cluster-key-slot": "1.1.2" + }, + "engines": { + "node": ">= 18" + } + }, "packages/search": { "name": "@redis/search", "version": "5.0.1", diff --git a/packages/bloom/package.json b/packages/bloom/package.json index 0772fcd3bb8..e7b226f17a6 100644 --- a/packages/bloom/package.json +++ b/packages/bloom/package.json @@ -12,7 +12,7 @@ "test": "nyc -r text-summary -r lcov mocha -r tsx './lib/**/*.spec.ts'" }, "peerDependencies": { - "@redis/client": "^5.0.1" + "@redis/client": "^5.1.0" }, "devDependencies": { "@redis/test-utils": "*" From 3d53e254d421adfdd9b3432adf4ceb6bb0195246 Mon Sep 17 00:00:00 2001 From: Nikolay Karadzhov Date: Tue, 20 May 2025 15:59:14 +0300 Subject: [PATCH 1585/1748] Release bloom@5.1.0 --- packages/bloom/package.json | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/packages/bloom/package.json b/packages/bloom/package.json index e7b226f17a6..3c8e37c5b74 100644 --- a/packages/bloom/package.json +++ b/packages/bloom/package.json @@ -1,6 +1,6 @@ { "name": "@redis/bloom", - "version": "5.0.1", + "version": "5.1.0", "license": "MIT", "main": "./dist/lib/index.js", "types": "./dist/lib/index.d.ts", From e938bda0d9f4682362949bd68df3f6df9e9ce5a1 Mon Sep 17 00:00:00 2001 From: Nikolay Karadzhov Date: Tue, 20 May 2025 16:01:04 +0300 Subject: [PATCH 1586/1748] Updated the Entraid package to use client@5.1.0 --- package-lock.json | 16 ++++++++++++++-- packages/entraid/package.json | 2 +- 2 files changed, 15 insertions(+), 3 deletions(-) diff --git a/package-lock.json b/package-lock.json index bb0da682490..0577b4fb49a 100644 --- a/package-lock.json +++ b/package-lock.json @@ -8520,7 +8520,7 @@ }, "packages/bloom": { "name": "@redis/bloom", - "version": "5.0.1", + "version": "5.1.0", "license": "MIT", "devDependencies": { "@redis/test-utils": "*" @@ -8569,7 +8569,7 @@ "node": ">= 18" }, "peerDependencies": { - "@redis/client": "^5.0.1" + "@redis/client": "^5.1.0" } }, "packages/entraid/node_modules/@types/node": { @@ -8632,6 +8632,18 @@ "node": ">= 18" } }, + "packages/redis/node_modules/@redis/bloom": { + "version": "5.0.1", + "resolved": "https://registry.npmjs.org/@redis/bloom/-/bloom-5.0.1.tgz", + "integrity": "sha512-F7L+rnuJvq/upKaVoEgsf8VT7g5pLQYWRqSUOV3uO4vpVtARzSKJ7CLyJjVsQS+wZVCGxsLMh8DwAIDcny1B+g==", + "license": "MIT", + "engines": { + "node": ">= 18" + }, + "peerDependencies": { + "@redis/client": "^5.0.1" + } + }, "packages/redis/node_modules/@redis/client": { "version": "5.0.1", "resolved": "https://registry.npmjs.org/@redis/client/-/client-5.0.1.tgz", diff --git a/packages/entraid/package.json b/packages/entraid/package.json index 57af37d36cd..43caebcc5bd 100644 --- a/packages/entraid/package.json +++ b/packages/entraid/package.json @@ -21,7 +21,7 @@ "@azure/msal-node": "^2.16.1" }, "peerDependencies": { - "@redis/client": "^5.0.1" + "@redis/client": "^5.1.0" }, "devDependencies": { "@types/express": "^4.17.21", From f2a3c1bf5cae6368e87e6b1bac773ecb5cde516a Mon Sep 17 00:00:00 2001 From: Nikolay Karadzhov Date: Tue, 20 May 2025 16:01:52 +0300 Subject: [PATCH 1587/1748] Release entraid@5.1.0 --- packages/entraid/package.json | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/packages/entraid/package.json b/packages/entraid/package.json index 43caebcc5bd..ba44351b3b9 100644 --- a/packages/entraid/package.json +++ b/packages/entraid/package.json @@ -1,6 +1,6 @@ { "name": "@redis/entraid", - "version": "5.0.1", + "version": "5.1.0", "license": "MIT", "main": "./dist/index.js", "types": "./dist/index.d.ts", From a1b41e2dbcfdaee19851e25a43106380fc613c72 Mon Sep 17 00:00:00 2001 From: Nikolay Karadzhov Date: Tue, 20 May 2025 16:03:07 +0300 Subject: [PATCH 1588/1748] Updated the Json package to use client@5.1.0 --- package-lock.json | 4 ++-- packages/json/package.json | 2 +- 2 files changed, 3 insertions(+), 3 deletions(-) diff --git a/package-lock.json b/package-lock.json index 0577b4fb49a..78feb73b2c4 100644 --- a/package-lock.json +++ b/package-lock.json @@ -8550,7 +8550,7 @@ }, "packages/entraid": { "name": "@redis/entraid", - "version": "5.0.1", + "version": "5.1.0", "license": "MIT", "dependencies": { "@azure/identity": "^4.7.0", @@ -8615,7 +8615,7 @@ "node": ">= 18" }, "peerDependencies": { - "@redis/client": "^5.0.1" + "@redis/client": "^5.1.0" } }, "packages/redis": { diff --git a/packages/json/package.json b/packages/json/package.json index 5c2dfc49a45..72cbe6cb2c3 100644 --- a/packages/json/package.json +++ b/packages/json/package.json @@ -12,7 +12,7 @@ "test": "nyc -r text-summary -r lcov mocha -r tsx './lib/**/*.spec.ts'" }, "peerDependencies": { - "@redis/client": "^5.0.1" + "@redis/client": "^5.1.0" }, "devDependencies": { "@redis/test-utils": "*" From a485936a9f88ff8363b9695788dc2457845186c2 Mon Sep 17 00:00:00 2001 From: Nikolay Karadzhov Date: Tue, 20 May 2025 16:03:59 +0300 Subject: [PATCH 1589/1748] Release json@5.1.0 --- packages/json/package.json | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/packages/json/package.json b/packages/json/package.json index 72cbe6cb2c3..fd0b3c768ff 100644 --- a/packages/json/package.json +++ b/packages/json/package.json @@ -1,6 +1,6 @@ { "name": "@redis/json", - "version": "5.0.1", + "version": "5.1.0", "license": "MIT", "main": "./dist/lib/index.js", "types": "./dist/lib/index.d.ts", From 4bb23283c36f00419b4be8fdd8473e6de42be3c6 Mon Sep 17 00:00:00 2001 From: Nikolay Karadzhov Date: Tue, 20 May 2025 16:05:08 +0300 Subject: [PATCH 1590/1748] Updated the Search package to use client@5.1.0 --- package-lock.json | 16 ++++++++++++++-- packages/search/package.json | 2 +- 2 files changed, 15 insertions(+), 3 deletions(-) diff --git a/package-lock.json b/package-lock.json index 78feb73b2c4..86c53b24b0b 100644 --- a/package-lock.json +++ b/package-lock.json @@ -8606,7 +8606,7 @@ }, "packages/json": { "name": "@redis/json", - "version": "5.0.1", + "version": "5.1.0", "license": "MIT", "devDependencies": { "@redis/test-utils": "*" @@ -8656,6 +8656,18 @@ "node": ">= 18" } }, + "packages/redis/node_modules/@redis/json": { + "version": "5.0.1", + "resolved": "https://registry.npmjs.org/@redis/json/-/json-5.0.1.tgz", + "integrity": "sha512-t94HOTk5myfhvaHZzlUzk2hoUvH2jsjftcnMgJWuHL/pzjAJQoZDCUJzjkoXIUjWXuyJixTguaaDyOZWwqH2Kg==", + "license": "MIT", + "engines": { + "node": ">= 18" + }, + "peerDependencies": { + "@redis/client": "^5.0.1" + } + }, "packages/search": { "name": "@redis/search", "version": "5.0.1", @@ -8667,7 +8679,7 @@ "node": ">= 18" }, "peerDependencies": { - "@redis/client": "^5.0.1" + "@redis/client": "^5.1.0" } }, "packages/test-utils": { diff --git a/packages/search/package.json b/packages/search/package.json index ba1fa2a74be..9ec7398d90b 100644 --- a/packages/search/package.json +++ b/packages/search/package.json @@ -13,7 +13,7 @@ "test-sourcemap": "mocha -r ts-node/register/transpile-only './lib/**/*.spec.ts'" }, "peerDependencies": { - "@redis/client": "^5.0.1" + "@redis/client": "^5.1.0" }, "devDependencies": { "@redis/test-utils": "*" From 2cc68647fe26b49789a86539763091e04f0e9736 Mon Sep 17 00:00:00 2001 From: Nikolay Karadzhov Date: Tue, 20 May 2025 16:06:45 +0300 Subject: [PATCH 1591/1748] Release search@5.1.0 --- packages/search/package.json | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/packages/search/package.json b/packages/search/package.json index 9ec7398d90b..ac56b5ff5db 100644 --- a/packages/search/package.json +++ b/packages/search/package.json @@ -1,6 +1,6 @@ { "name": "@redis/search", - "version": "5.0.1", + "version": "5.1.0", "license": "MIT", "main": "./dist/lib/index.js", "types": "./dist/lib/index.d.ts", From 7fca460305ae71d4058ce71009cd70fa034ac330 Mon Sep 17 00:00:00 2001 From: Nikolay Karadzhov Date: Tue, 20 May 2025 16:07:45 +0300 Subject: [PATCH 1592/1748] Updated the Timeseries package to use client@5.1.0 --- package-lock.json | 16 ++++++++++++++-- packages/time-series/package.json | 2 +- 2 files changed, 15 insertions(+), 3 deletions(-) diff --git a/package-lock.json b/package-lock.json index 86c53b24b0b..5f0c957a86d 100644 --- a/package-lock.json +++ b/package-lock.json @@ -8668,9 +8668,21 @@ "@redis/client": "^5.0.1" } }, + "packages/redis/node_modules/@redis/search": { + "version": "5.0.1", + "resolved": "https://registry.npmjs.org/@redis/search/-/search-5.0.1.tgz", + "integrity": "sha512-wipK6ZptY7K68B7YLVhP5I/wYCDUU+mDJMyJiUcQLuOs7/eKOBc8lTXKUSssor8QnzZSPy4A5ulcC5PZY22Zgw==", + "license": "MIT", + "engines": { + "node": ">= 18" + }, + "peerDependencies": { + "@redis/client": "^5.0.1" + } + }, "packages/search": { "name": "@redis/search", - "version": "5.0.1", + "version": "5.1.0", "license": "MIT", "devDependencies": { "@redis/test-utils": "*" @@ -8757,7 +8769,7 @@ "node": ">= 18" }, "peerDependencies": { - "@redis/client": "^5.0.1" + "@redis/client": "^5.1.0" } } } diff --git a/packages/time-series/package.json b/packages/time-series/package.json index 1b436701d6b..2fcec56b70b 100644 --- a/packages/time-series/package.json +++ b/packages/time-series/package.json @@ -12,7 +12,7 @@ "test": "nyc -r text-summary -r lcov mocha -r tsx './lib/**/*.spec.ts'" }, "peerDependencies": { - "@redis/client": "^5.0.1" + "@redis/client": "^5.1.0" }, "devDependencies": { "@redis/test-utils": "*" From 8d34ee207e7a3f6426c9d54a16ec00e50eb99876 Mon Sep 17 00:00:00 2001 From: Nikolay Karadzhov Date: Tue, 20 May 2025 16:12:49 +0300 Subject: [PATCH 1593/1748] Release time-series@5.1.0 --- packages/time-series/package.json | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/packages/time-series/package.json b/packages/time-series/package.json index 2fcec56b70b..466c53df220 100644 --- a/packages/time-series/package.json +++ b/packages/time-series/package.json @@ -1,6 +1,6 @@ { "name": "@redis/time-series", - "version": "5.0.1", + "version": "5.1.0", "license": "MIT", "main": "./dist/lib/index.js", "types": "./dist/lib/index.d.ts", From 78c6d603f24915ff39adaf485124532a0f4ffd93 Mon Sep 17 00:00:00 2001 From: Nikolay Karadzhov Date: Tue, 20 May 2025 16:17:43 +0300 Subject: [PATCH 1594/1748] Updated the Redis package to use client@5.1.0 --- package-lock.json | 60 ++++--------------------------------- packages/redis/package.json | 10 +++---- 2 files changed, 11 insertions(+), 59 deletions(-) diff --git a/package-lock.json b/package-lock.json index 5f0c957a86d..a9f846e7a88 100644 --- a/package-lock.json +++ b/package-lock.json @@ -8622,64 +8622,16 @@ "version": "5.0.1", "license": "MIT", "dependencies": { - "@redis/bloom": "5.0.1", - "@redis/client": "5.0.1", - "@redis/json": "5.0.1", - "@redis/search": "5.0.1", - "@redis/time-series": "5.0.1" + "@redis/bloom": "5.1.0", + "@redis/client": "5.1.0", + "@redis/json": "5.1.0", + "@redis/search": "5.1.0", + "@redis/time-series": "5.1.0" }, "engines": { "node": ">= 18" } }, - "packages/redis/node_modules/@redis/bloom": { - "version": "5.0.1", - "resolved": "https://registry.npmjs.org/@redis/bloom/-/bloom-5.0.1.tgz", - "integrity": "sha512-F7L+rnuJvq/upKaVoEgsf8VT7g5pLQYWRqSUOV3uO4vpVtARzSKJ7CLyJjVsQS+wZVCGxsLMh8DwAIDcny1B+g==", - "license": "MIT", - "engines": { - "node": ">= 18" - }, - "peerDependencies": { - "@redis/client": "^5.0.1" - } - }, - "packages/redis/node_modules/@redis/client": { - "version": "5.0.1", - "resolved": "https://registry.npmjs.org/@redis/client/-/client-5.0.1.tgz", - "integrity": "sha512-k0EJvlMGEyBqUD3orKe0UMZ66fPtfwqPIr+ZSd853sXj2EyhNtPXSx+J6sENXJNgAlEBhvD+57Dwt0qTisKB0A==", - "license": "MIT", - "dependencies": { - "cluster-key-slot": "1.1.2" - }, - "engines": { - "node": ">= 18" - } - }, - "packages/redis/node_modules/@redis/json": { - "version": "5.0.1", - "resolved": "https://registry.npmjs.org/@redis/json/-/json-5.0.1.tgz", - "integrity": "sha512-t94HOTk5myfhvaHZzlUzk2hoUvH2jsjftcnMgJWuHL/pzjAJQoZDCUJzjkoXIUjWXuyJixTguaaDyOZWwqH2Kg==", - "license": "MIT", - "engines": { - "node": ">= 18" - }, - "peerDependencies": { - "@redis/client": "^5.0.1" - } - }, - "packages/redis/node_modules/@redis/search": { - "version": "5.0.1", - "resolved": "https://registry.npmjs.org/@redis/search/-/search-5.0.1.tgz", - "integrity": "sha512-wipK6ZptY7K68B7YLVhP5I/wYCDUU+mDJMyJiUcQLuOs7/eKOBc8lTXKUSssor8QnzZSPy4A5ulcC5PZY22Zgw==", - "license": "MIT", - "engines": { - "node": ">= 18" - }, - "peerDependencies": { - "@redis/client": "^5.0.1" - } - }, "packages/search": { "name": "@redis/search", "version": "5.1.0", @@ -8760,7 +8712,7 @@ }, "packages/time-series": { "name": "@redis/time-series", - "version": "5.0.1", + "version": "5.1.0", "license": "MIT", "devDependencies": { "@redis/test-utils": "*" diff --git a/packages/redis/package.json b/packages/redis/package.json index e7c9da2660b..30c0fba90e6 100644 --- a/packages/redis/package.json +++ b/packages/redis/package.json @@ -10,11 +10,11 @@ "!dist/tsconfig.tsbuildinfo" ], "dependencies": { - "@redis/bloom": "5.0.1", - "@redis/client": "5.0.1", - "@redis/json": "5.0.1", - "@redis/search": "5.0.1", - "@redis/time-series": "5.0.1" + "@redis/bloom": "5.1.0", + "@redis/client": "5.1.0", + "@redis/json": "5.1.0", + "@redis/search": "5.1.0", + "@redis/time-series": "5.1.0" }, "engines": { "node": ">= 18" From ab399a8a22636bd4f4434fbdefa868b0ee581ab6 Mon Sep 17 00:00:00 2001 From: Nikolay Karadzhov Date: Tue, 20 May 2025 16:20:35 +0300 Subject: [PATCH 1595/1748] Release redis@5.1.0 --- packages/redis/package.json | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/packages/redis/package.json b/packages/redis/package.json index 30c0fba90e6..b6c3f409a69 100644 --- a/packages/redis/package.json +++ b/packages/redis/package.json @@ -1,7 +1,7 @@ { "name": "redis", "description": "A modern, high performance Redis client", - "version": "5.0.1", + "version": "5.1.0", "license": "MIT", "main": "./dist/index.js", "types": "./dist/index.d.ts", From b8acbce213c0953f7b5874a8d0d1738805864e04 Mon Sep 17 00:00:00 2001 From: Nikolay Karadzhov Date: Tue, 20 May 2025 16:22:20 +0300 Subject: [PATCH 1596/1748] udpate package-lock.json --- package-lock.json | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/package-lock.json b/package-lock.json index a9f846e7a88..ab4cbe138bc 100644 --- a/package-lock.json +++ b/package-lock.json @@ -8619,7 +8619,7 @@ } }, "packages/redis": { - "version": "5.0.1", + "version": "5.1.0", "license": "MIT", "dependencies": { "@redis/bloom": "5.1.0", From 9ea260f0f9a3aba90c0c62ade443c23d5c44ab31 Mon Sep 17 00:00:00 2001 From: Nikolay Karadzhov Date: Wed, 21 May 2025 11:38:29 +0300 Subject: [PATCH 1597/1748] fix(handshake): ignore errors on client.setinfo (#2969) As per the documentation (https://redis.io/docs/latest/commands/client-setinfo): Client libraries are expected to pipeline this command after authentication on all connections and ignore failures since they could be connected to an older version that doesn't support them. Turns out different versions of redis server return different errors, so its better to catch all. fixes #2968 --- packages/client/lib/client/index.ts | 26 +++++++++----------------- packages/client/lib/errors.ts | 6 +----- 2 files changed, 10 insertions(+), 22 deletions(-) diff --git a/packages/client/lib/client/index.ts b/packages/client/lib/client/index.ts index 8d98aa8ed26..a446ad8e755 100644 --- a/packages/client/lib/client/index.ts +++ b/packages/client/lib/client/index.ts @@ -4,7 +4,7 @@ import { BasicAuth, CredentialsError, CredentialsProvider, StreamingCredentialsP import RedisCommandsQueue, { CommandOptions } from './commands-queue'; import { EventEmitter } from 'node:events'; import { attachConfig, functionArgumentsPrefix, getTransformReply, scriptArgumentsPrefix } from '../commander'; -import { ClientClosedError, ClientOfflineError, DisconnectsClientError, SimpleError, WatchError } from '../errors'; +import { ClientClosedError, ClientOfflineError, DisconnectsClientError, WatchError } from '../errors'; import { URL } from 'node:url'; import { TcpSocketConnectOpts } from 'node:net'; import { PUBSUB_TYPE, PubSubType, PubSubListener, PubSubTypeListeners, ChannelListeners } from './pub-sub'; @@ -626,14 +626,10 @@ export default class RedisClient< if (!this.#options?.disableClientInfo) { commands.push({ cmd: ['CLIENT', 'SETINFO', 'LIB-VER', version], - errorHandler: (err: Error) => { - // Only throw if not a SimpleError - unknown subcommand - // Client libraries are expected to ignore failures - // of type SimpleError - unknown subcommand, which are - // expected from older servers ( < v7 ) - if (!(err instanceof SimpleError) || !err.isUnknownSubcommand()) { - throw err; - } + errorHandler: () => { + // Client libraries are expected to pipeline this command + // after authentication on all connections and ignore failures + // since they could be connected to an older version that doesn't support them. } }); @@ -646,14 +642,10 @@ export default class RedisClient< ? `node-redis(${this.#options.clientInfoTag})` : 'node-redis' ], - errorHandler: (err: Error) => { - // Only throw if not a SimpleError - unknown subcommand - // Client libraries are expected to ignore failures - // of type SimpleError - unknown subcommand, which are - // expected from older servers ( < v7 ) - if (!(err instanceof SimpleError) || !err.isUnknownSubcommand()) { - throw err; - } + errorHandler: () => { + // Client libraries are expected to pipeline this command + // after authentication on all connections and ignore failures + // since they could be connected to an older version that doesn't support them. } }); } diff --git a/packages/client/lib/errors.ts b/packages/client/lib/errors.ts index 4f05f62ac4b..db37ec1a9ba 100644 --- a/packages/client/lib/errors.ts +++ b/packages/client/lib/errors.ts @@ -70,11 +70,7 @@ export class ErrorReply extends Error { } } -export class SimpleError extends ErrorReply { - isUnknownSubcommand(): boolean { - return this.message.toLowerCase().indexOf('err unknown subcommand') !== -1; - } -} +export class SimpleError extends ErrorReply {} export class BlobError extends ErrorReply {} From 27537b0ab79b3a0dae4db1f03b5bb6eefacb32fb Mon Sep 17 00:00:00 2001 From: Nikolay Karadzhov Date: Thu, 22 May 2025 10:35:16 +0300 Subject: [PATCH 1598/1748] fix(cluster): replace native private with _ (#2971) MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Private class fields (e.g., #execute) cause runtime errors when accessed from contexts where `this` is not the exact instance, triggering “Receiver must be an instance of class RedisCluster”. fixes #2967 --- packages/client/lib/cluster/index.ts | 96 ++++++++++++++-------------- 1 file changed, 48 insertions(+), 48 deletions(-) diff --git a/packages/client/lib/cluster/index.ts b/packages/client/lib/cluster/index.ts index f7385629262..c2c251810e3 100644 --- a/packages/client/lib/cluster/index.ts +++ b/packages/client/lib/cluster/index.ts @@ -188,7 +188,7 @@ export default class RedisCluster< const parser = new BasicCommandParser(); command.parseCommand(parser, ...args); - return this._self.#execute( + return this._self._execute( parser.firstKey, command.IS_READ_ONLY, this._commandOptions, @@ -204,7 +204,7 @@ export default class RedisCluster< const parser = new BasicCommandParser(); command.parseCommand(parser, ...args); - return this._self.#execute( + return this._self._execute( parser.firstKey, command.IS_READ_ONLY, this._self._commandOptions, @@ -222,7 +222,7 @@ export default class RedisCluster< parser.push(...prefix); fn.parseCommand(parser, ...args); - return this._self.#execute( + return this._self._execute( parser.firstKey, fn.IS_READ_ONLY, this._self._commandOptions, @@ -240,7 +240,7 @@ export default class RedisCluster< parser.push(...prefix); script.parseCommand(parser, ...args); - return this._self.#execute( + return this._self._execute( parser.firstKey, script.IS_READ_ONLY, this._commandOptions, @@ -293,9 +293,9 @@ export default class RedisCluster< return RedisCluster.factory(options)(options); } - readonly #options: RedisClusterOptions; + readonly _options: RedisClusterOptions; - readonly #slots: RedisClusterSlots; + readonly _slots: RedisClusterSlots; private _self = this; private _commandOptions?: ClusterCommandOptions; @@ -305,11 +305,11 @@ export default class RedisCluster< * Use with {@link RedisCluster.prototype.nodeClient} to get the client for a specific node (master or replica). */ get slots() { - return this._self.#slots.slots; + return this._self._slots.slots; } get clientSideCache() { - return this._self.#slots.clientSideCache; + return this._self._slots.clientSideCache; } /** @@ -317,7 +317,7 @@ export default class RedisCluster< * Use with {@link RedisCluster.prototype.nodeClient} to get the client for a specific master node. */ get masters() { - return this._self.#slots.masters; + return this._self._slots.masters; } /** @@ -325,7 +325,7 @@ export default class RedisCluster< * Use with {@link RedisCluster.prototype.nodeClient} to get the client for a specific replica node. */ get replicas() { - return this._self.#slots.replicas; + return this._self._slots.replicas; } /** @@ -333,25 +333,25 @@ export default class RedisCluster< * Use with {@link RedisCluster.prototype.nodeClient} to get the client for a specific node (master or replica). */ get nodeByAddress() { - return this._self.#slots.nodeByAddress; + return this._self._slots.nodeByAddress; } /** * The current pub/sub node. */ get pubSubNode() { - return this._self.#slots.pubSubNode; + return this._self._slots.pubSubNode; } get isOpen() { - return this._self.#slots.isOpen; + return this._self._slots.isOpen; } constructor(options: RedisClusterOptions) { super(); - this.#options = options; - this.#slots = new RedisClusterSlots(options, this.emit.bind(this)); + this._options = options; + this._slots = new RedisClusterSlots(options, this.emit.bind(this)); if (options?.commandOptions) { this._commandOptions = options.commandOptions; @@ -366,14 +366,14 @@ export default class RedisCluster< _TYPE_MAPPING extends TypeMapping = TYPE_MAPPING >(overrides?: Partial>) { return new (Object.getPrototypeOf(this).constructor)({ - ...this._self.#options, + ...this._self._options, commandOptions: this._commandOptions, ...overrides }) as RedisClusterType<_M, _F, _S, _RESP, _TYPE_MAPPING>; } async connect() { - await this._self.#slots.connect(); + await this._self._slots.connect(); return this as unknown as RedisClusterType; } @@ -429,7 +429,7 @@ export default class RedisCluster< // return this._commandOptionsProxy('policies', policies); // } - #handleAsk( + _handleAsk( fn: (client: RedisClientType, opts?: ClusterCommandOptions) => Promise ) { return async (client: RedisClientType, options?: ClusterCommandOptions) => { @@ -450,14 +450,14 @@ export default class RedisCluster< }; } - async #execute( + async _execute( firstKey: RedisArgument | undefined, isReadonly: boolean | undefined, options: ClusterCommandOptions | undefined, fn: (client: RedisClientType, opts?: ClusterCommandOptions) => Promise ): Promise { - const maxCommandRedirections = this.#options.maxCommandRedirections ?? 16; - let client = await this.#slots.getClient(firstKey, isReadonly); + const maxCommandRedirections = this._options.maxCommandRedirections ?? 16; + let client = await this._slots.getClient(firstKey, isReadonly); let i = 0; let myFn = fn; @@ -475,10 +475,10 @@ export default class RedisCluster< if (err.message.startsWith('ASK')) { const address = err.message.substring(err.message.lastIndexOf(' ') + 1); - let redirectTo = await this.#slots.getMasterByAddress(address); + let redirectTo = await this._slots.getMasterByAddress(address); if (!redirectTo) { - await this.#slots.rediscover(client); - redirectTo = await this.#slots.getMasterByAddress(address); + await this._slots.rediscover(client); + redirectTo = await this._slots.getMasterByAddress(address); } if (!redirectTo) { @@ -486,13 +486,13 @@ export default class RedisCluster< } client = redirectTo; - myFn = this.#handleAsk(fn); + myFn = this._handleAsk(fn); continue; } if (err.message.startsWith('MOVED')) { - await this.#slots.rediscover(client); - client = await this.#slots.getClient(firstKey, isReadonly); + await this._slots.rediscover(client); + client = await this._slots.getClient(firstKey, isReadonly); continue; } @@ -508,7 +508,7 @@ export default class RedisCluster< options?: ClusterCommandOptions, // defaultPolicies?: CommandPolicies ): Promise { - return this._self.#execute( + return this._self._execute( firstKey, isReadonly, options, @@ -520,11 +520,11 @@ export default class RedisCluster< type Multi = new (...args: ConstructorParameters) => RedisClusterMultiCommandType<[], M, F, S, RESP, TYPE_MAPPING>; return new ((this as any).Multi as Multi)( async (firstKey, isReadonly, commands) => { - const client = await this._self.#slots.getClient(firstKey, isReadonly); + const client = await this._self._slots.getClient(firstKey, isReadonly); return client._executeMulti(commands); }, async (firstKey, isReadonly, commands) => { - const client = await this._self.#slots.getClient(firstKey, isReadonly); + const client = await this._self._slots.getClient(firstKey, isReadonly); return client._executePipeline(commands); }, routing, @@ -539,7 +539,7 @@ export default class RedisCluster< listener: PubSubListener, bufferMode?: T ) { - return (await this._self.#slots.getPubSubClient()) + return (await this._self._slots.getPubSubClient()) .SUBSCRIBE(channels, listener, bufferMode); } @@ -550,7 +550,7 @@ export default class RedisCluster< listener?: PubSubListener, bufferMode?: T ) { - return this._self.#slots.executeUnsubscribeCommand(client => + return this._self._slots.executeUnsubscribeCommand(client => client.UNSUBSCRIBE(channels, listener, bufferMode) ); } @@ -562,7 +562,7 @@ export default class RedisCluster< listener: PubSubListener, bufferMode?: T ) { - return (await this._self.#slots.getPubSubClient()) + return (await this._self._slots.getPubSubClient()) .PSUBSCRIBE(patterns, listener, bufferMode); } @@ -573,7 +573,7 @@ export default class RedisCluster< listener?: PubSubListener, bufferMode?: T ) { - return this._self.#slots.executeUnsubscribeCommand(client => + return this._self._slots.executeUnsubscribeCommand(client => client.PUNSUBSCRIBE(patterns, listener, bufferMode) ); } @@ -585,9 +585,9 @@ export default class RedisCluster< listener: PubSubListener, bufferMode?: T ) { - const maxCommandRedirections = this._self.#options.maxCommandRedirections ?? 16, + const maxCommandRedirections = this._self._options.maxCommandRedirections ?? 16, firstChannel = Array.isArray(channels) ? channels[0] : channels; - let client = await this._self.#slots.getShardedPubSubClient(firstChannel); + let client = await this._self._slots.getShardedPubSubClient(firstChannel); for (let i = 0; ; i++) { try { return await client.SSUBSCRIBE(channels, listener, bufferMode); @@ -597,8 +597,8 @@ export default class RedisCluster< } if (err.message.startsWith('MOVED')) { - await this._self.#slots.rediscover(client); - client = await this._self.#slots.getShardedPubSubClient(firstChannel); + await this._self._slots.rediscover(client); + client = await this._self._slots.getShardedPubSubClient(firstChannel); continue; } @@ -614,7 +614,7 @@ export default class RedisCluster< listener?: PubSubListener, bufferMode?: T ) { - return this._self.#slots.executeShardedUnsubscribeCommand( + return this._self._slots.executeShardedUnsubscribeCommand( Array.isArray(channels) ? channels[0] : channels, client => client.SUNSUBSCRIBE(channels, listener, bufferMode) ); @@ -626,28 +626,28 @@ export default class RedisCluster< * @deprecated Use `close` instead. */ quit() { - return this._self.#slots.quit(); + return this._self._slots.quit(); } /** * @deprecated Use `destroy` instead. */ disconnect() { - return this._self.#slots.disconnect(); + return this._self._slots.disconnect(); } close() { - this._self.#slots.clientSideCache?.onPoolClose(); - return this._self.#slots.close(); + this._self._slots.clientSideCache?.onPoolClose(); + return this._self._slots.close(); } destroy() { - this._self.#slots.clientSideCache?.onPoolClose(); - return this._self.#slots.destroy(); + this._self._slots.clientSideCache?.onPoolClose(); + return this._self._slots.destroy(); } nodeClient(node: ShardNode) { - return this._self.#slots.nodeClient(node); + return this._self._slots.nodeClient(node); } /** @@ -655,7 +655,7 @@ export default class RedisCluster< * Userful for running "forward" commands (like PUBLISH) on a random node. */ getRandomNode() { - return this._self.#slots.getRandomNode(); + return this._self._slots.getRandomNode(); } /** @@ -663,7 +663,7 @@ export default class RedisCluster< * Useful for running readonly commands on a slot. */ getSlotRandomNode(slot: number) { - return this._self.#slots.getSlotRandomNode(slot); + return this._self._slots.getSlotRandomNode(slot); } /** From 065eb5e9145db9152923a44b63a356c55f76cf3b Mon Sep 17 00:00:00 2001 From: Nikolay Karadzhov Date: Thu, 22 May 2025 10:36:52 +0300 Subject: [PATCH 1599/1748] fix(json): remove debug console.logs (#2970) --- packages/json/lib/commands/helpers.ts | 2 -- 1 file changed, 2 deletions(-) diff --git a/packages/json/lib/commands/helpers.ts b/packages/json/lib/commands/helpers.ts index 26ff12f6834..99579ce81cb 100644 --- a/packages/json/lib/commands/helpers.ts +++ b/packages/json/lib/commands/helpers.ts @@ -2,7 +2,6 @@ import { isNullReply } from "@redis/client/dist/lib/commands/generic-transformer import { BlobStringReply, NullReply, UnwrapReply } from "@redis/client/dist/lib/RESP/types"; export function transformRedisJsonNullReply(json: NullReply | BlobStringReply): NullReply | RedisJSON { - console.log('transformRedisJsonNullReply', json) return isNullReply(json) ? json : transformRedisJsonReply(json); } @@ -17,6 +16,5 @@ export function transformRedisJsonArgument(json: RedisJSON): string { export function transformRedisJsonReply(json: BlobStringReply): RedisJSON { const res = JSON.parse((json as unknown as UnwrapReply).toString()); - console.log('transformRedisJsonReply', json, res) return res; } From f346bad64e87730bfbe9a38fb639dbd759353332 Mon Sep 17 00:00:00 2001 From: Hristo Temelski Date: Tue, 27 May 2025 14:21:22 +0300 Subject: [PATCH 1600/1748] Adapt legacy sentinel tests to use the new test utils (#2976) * modified legacy sentinel tests * Adapt legacy sentinel tests to use the new test utils * modify tmpdir creation * reduced sentinel config timeouts, removed unneeded comment --------- Co-authored-by: H. Temelski --- packages/client/lib/client/index.spec.ts | 4 +- packages/client/lib/sentinel/index.spec.ts | 67 ++-- packages/client/lib/sentinel/test-util.ts | 347 ++++++++------------- packages/client/lib/test-utils.ts | 2 +- packages/test-utils/lib/dockers.ts | 81 +++-- packages/test-utils/lib/index.ts | 71 ++++- 6 files changed, 264 insertions(+), 308 deletions(-) diff --git a/packages/client/lib/client/index.spec.ts b/packages/client/lib/client/index.spec.ts index cc052dd5b51..4f752210dbe 100644 --- a/packages/client/lib/client/index.spec.ts +++ b/packages/client/lib/client/index.spec.ts @@ -89,8 +89,8 @@ describe('Client', () => { && expected?.credentialsProvider?.type === 'async-credentials-provider') { // Compare the actual output of the credentials functions - const resultCreds = await result.credentialsProvider.credentials(); - const expectedCreds = await expected.credentialsProvider.credentials(); + const resultCreds = await result.credentialsProvider?.credentials(); + const expectedCreds = await expected.credentialsProvider?.credentials(); assert.deepEqual(resultCreds, expectedCreds); } else { assert.fail('Credentials provider type mismatch'); diff --git a/packages/client/lib/sentinel/index.spec.ts b/packages/client/lib/sentinel/index.spec.ts index a2e52b774bb..a9bdc8cc95c 100644 --- a/packages/client/lib/sentinel/index.spec.ts +++ b/packages/client/lib/sentinel/index.spec.ts @@ -197,7 +197,6 @@ describe(`test with scripts`, () => { }, GLOBAL.SENTINEL.WITH_SCRIPT) }); - describe(`test with functions`, () => { testUtils.testWithClientSentinel('with function', async sentinel => { await sentinel.functionLoad( @@ -377,12 +376,9 @@ describe(`test with masterPoolSize 2`, () => { }, GLOBAL.SENTINEL.WITH_MASTER_POOL_SIZE_2); }); - -// TODO: Figure out how to modify the test utils -// so it would have fine grained controll over -// sentinel -// it should somehow replicate the `SentinelFramework` object functionallities async function steadyState(frame: SentinelFramework) { + // wait a bit to ensure that sentinels are seeing eachother + await setTimeout(2000) let checkedMaster = false; let checkedReplicas = false; while (!checkedMaster || !checkedReplicas) { @@ -430,7 +426,7 @@ async function steadyState(frame: SentinelFramework) { } } -describe.skip('legacy tests', () => { +describe('legacy tests', () => { const config: RedisSentinelConfig = { sentinelName: "test", numberOfNodes: 3, password: undefined }; const frame = new SentinelFramework(config); let tracer = new Array(); @@ -439,42 +435,30 @@ describe.skip('legacy tests', () => { let longestTestDelta = 0; let last: number; - before(async function () { - this.timeout(15000); - - last = Date.now(); - - function deltaMeasurer() { - const delta = Date.now() - last; - if (delta > longestDelta) { - longestDelta = delta; - } - if (delta > longestTestDelta) { - longestTestDelta = delta; - } - if (!stopMeasuringBlocking) { - last = Date.now(); - setImmediate(deltaMeasurer); - } - } - setImmediate(deltaMeasurer); - await frame.spawnRedisSentinel(); - }); - - after(async function () { - this.timeout(15000); - - stopMeasuringBlocking = true; - - await frame.cleanup(); - }) describe('Sentinel Client', function () { let sentinel: RedisSentinelType | undefined; beforeEach(async function () { - this.timeout(0); - + this.timeout(15000); + + last = Date.now(); + + function deltaMeasurer() { + const delta = Date.now() - last; + if (delta > longestDelta) { + longestDelta = delta; + } + if (delta > longestTestDelta) { + longestTestDelta = delta; + } + if (!stopMeasuringBlocking) { + last = Date.now(); + setImmediate(deltaMeasurer); + } + } + setImmediate(deltaMeasurer); + await frame.spawnRedisSentinel(); await frame.getAllRunning(); await steadyState(frame); longestTestDelta = 0; @@ -522,6 +506,10 @@ describe.skip('legacy tests', () => { await sentinel.destroy(); sentinel = undefined; } + + stopMeasuringBlocking = true; + + await frame.cleanup(); }) it('use', async function () { @@ -863,7 +851,6 @@ describe.skip('legacy tests', () => { it('shutdown sentinel node', async function () { this.timeout(60000); - sentinel = frame.getSentinelClient(); sentinel.setTracer(tracer); sentinel.on("error", () => { }); @@ -1020,7 +1007,7 @@ describe.skip('legacy tests', () => { this.timeout(30000); const csc = new BasicPooledClientSideCache(); - sentinel = frame.getSentinelClient({nodeClientOptions: {RESP: 3}, clientSideCache: csc, masterPoolSize: 5}); + sentinel = frame.getSentinelClient({nodeClientOptions: {RESP: 3 as const}, RESP: 3 as const, clientSideCache: csc, masterPoolSize: 5}); await sentinel.connect(); await sentinel.set('x', 1); diff --git a/packages/client/lib/sentinel/test-util.ts b/packages/client/lib/sentinel/test-util.ts index 86bc5b31786..60c1a59689a 100644 --- a/packages/client/lib/sentinel/test-util.ts +++ b/packages/client/lib/sentinel/test-util.ts @@ -4,12 +4,13 @@ import { once } from 'node:events'; import { promisify } from 'node:util'; import { exec } from 'node:child_process'; import { RedisSentinelOptions, RedisSentinelType } from './types'; -import RedisClient from '../client'; +import RedisClient, {RedisClientType} from '../client'; import RedisSentinel from '.'; import { RedisArgument, RedisFunctions, RedisModules, RedisScripts, RespVersions, TypeMapping } from '../RESP/types'; const execAsync = promisify(exec); import RedisSentinelModule from './module' - +import TestUtils from '@redis/test-utils'; +import { DEBUG_MODE_ARGS } from '../test-utils' interface ErrorWithCode extends Error { code: string; } @@ -125,7 +126,6 @@ export interface RedisSentinelConfig { sentinelServerArgument?: Array sentinelName: string; - sentinelQuorum?: number; password?: string; } @@ -151,6 +151,7 @@ export interface SentinelController { } export class SentinelFramework extends DockerBase { + #testUtils: TestUtils; #nodeList: Awaited> = []; /* port -> docker info/client */ #nodeMap: Map>>>; @@ -170,7 +171,11 @@ export class SentinelFramework extends DockerBase { super(); this.config = config; - + this.#testUtils = TestUtils.createFromConfig({ + dockerImageName: 'redislabs/client-libs-test', + dockerImageVersionArgument: 'redis-version', + defaultDockerVersion: '8.0-M05-pre' + }); this.#nodeMap = new Map>>>(); this.#sentinelMap = new Map>>>(); } @@ -190,7 +195,7 @@ export class SentinelFramework extends DockerBase { const options: RedisSentinelOptions = { ...opts, name: this.config.sentinelName, - sentinelRootNodes: this.#sentinelList.map((sentinel) => { return { host: '127.0.0.1', port: sentinel.docker.port } }), + sentinelRootNodes: this.#sentinelList.map((sentinel) => { return { host: '127.0.0.1', port: sentinel.port } }), passthroughClientErrorEvents: errors } @@ -218,11 +223,11 @@ export class SentinelFramework extends DockerBase { throw new Error("inconsistent state with partial setup"); } - this.#nodeList = await this.spawnRedisSentinelNodes(); - this.#nodeList.map((value) => this.#nodeMap.set(value.docker.port.toString(), value)); + this.#nodeList = await this.spawnRedisSentinelNodes(2); + this.#nodeList.map((value) => this.#nodeMap.set(value.port.toString(), value)); - this.#sentinelList = await this.spawnRedisSentinelSentinels(); - this.#sentinelList.map((value) => this.#sentinelMap.set(value.docker.port.toString(), value)); + this.#sentinelList = await this.spawnRedisSentinelSentinels(this.#nodeList[0].port, 3) + this.#sentinelList.map((value) => this.#sentinelMap.set(value.port.toString(), value)); this.#spawned = true; } @@ -234,11 +239,8 @@ export class SentinelFramework extends DockerBase { return Promise.all( [...this.#nodeMap!.values(), ...this.#sentinelMap!.values()].map( - async ({ docker, client }) => { - if (client.isOpen) { - client.destroy(); - } - this.dockerRemove(docker.dockerId); + async ({ dockerId }) => { + this.dockerRemove(dockerId); } ) ).finally(async () => { @@ -248,112 +250,33 @@ export class SentinelFramework extends DockerBase { }); } - protected async spawnRedisSentinelNodeDocker() { - const imageInfo: RedisServerDockerConfig = this.config.nodeDockerConfig ?? { image: "redis/redis-stack-server", version: "latest" }; - const serverArguments: Array = this.config.nodeServerArguments ?? []; - let environment; - if (this.config.password !== undefined) { - environment = `REDIS_ARGS="{port} --requirepass ${this.config.password}"`; - } else { - environment = 'REDIS_ARGS="{port}"'; - } - - const docker = await this.spawnRedisServerDocker(imageInfo, serverArguments, environment); - const client = await RedisClient.create({ - password: this.config.password, - socket: { - port: docker.port - } - }).on("error", () => { }).connect(); - - return { - docker, - client - }; - } - - protected async spawnRedisSentinelNodes() { - const master = await this.spawnRedisSentinelNodeDocker(); + protected async spawnRedisSentinelNodes(replicasCount: number) { + const master = await this.#testUtils.spawnRedisServer({serverArguments: DEBUG_MODE_ARGS}) + + const replicas: Array = [] + for (let i = 0; i < replicasCount; i++) { + const replica = await this.#testUtils.spawnRedisServer({serverArguments: DEBUG_MODE_ARGS}) + replicas.push(replica) - const promises: Array> = []; + const client = RedisClient.create({ + socket: { + port: replica.port + } + }) - for (let i = 0; i < (this.config.numberOfNodes ?? 0) - 1; i++) { - promises.push( - this.spawnRedisSentinelNodeDocker().then(async node => { - if (this.config.password !== undefined) { - await node.client.configSet({'masterauth': this.config.password}) - } - await node.client.replicaOf('127.0.0.1', master.docker.port); - return node; - }) - ); + await client.connect(); + await client.replicaOf("127.0.0.1", master.port); + await client.close(); } return [ master, - ...await Promise.all(promises) - ]; - } - - protected async spawnRedisSentinelSentinelDocker() { - const imageInfo: RedisServerDockerConfig = this.config.sentinelDockerConfig ?? { image: "redis", version: "latest" } - let serverArguments: Array; - if (this.config.password === undefined) { - serverArguments = this.config.sentinelServerArgument ?? - [ - "/bin/bash", - "-c", - "\"touch /tmp/sentinel.conf ; /usr/local/bin/redis-sentinel /tmp/sentinel.conf {port} \"" - ]; - } else { - serverArguments = this.config.sentinelServerArgument ?? - [ - "/bin/bash", - "-c", - `"touch /tmp/sentinel.conf ; /usr/local/bin/redis-sentinel /tmp/sentinel.conf {port} --requirepass ${this.config.password}"` - ]; - } - - const docker = await this.spawnRedisServerDocker(imageInfo, serverArguments); - const client = await RedisClient.create({ - modules: RedisSentinelModule, - password: this.config.password, - socket: { - port: docker.port - } - }).on("error", () => { }).connect(); - - return { - docker, - client - }; + ...replicas + ] } - protected async spawnRedisSentinelSentinels() { - const quorum = this.config.sentinelQuorum?.toString() ?? "2"; - const node = this.#nodeList[0]; - - const promises: Array> = []; - - for (let i = 0; i < (this.config.numberOfSentinels ?? 3); i++) { - promises.push( - this.spawnRedisSentinelSentinelDocker().then(async sentinel => { - await sentinel.client.sentinel.sentinelMonitor(this.config.sentinelName, '127.0.0.1', node.docker.port.toString(), quorum); - const options: Array<{option: RedisArgument, value: RedisArgument}> = []; - options.push({ option: "down-after-milliseconds", value: "100" }); - options.push({ option: "failover-timeout", value: "5000" }); - if (this.config.password !== undefined) { - options.push({ option: "auth-pass", value: this.config.password }); - } - await sentinel.client.sentinel.sentinelSet(this.config.sentinelName, options) - return sentinel; - }) - ); - } - - return [ - ...await Promise.all(promises) - ] + protected async spawnRedisSentinelSentinels(masterPort: number, sentinels: number) { + return this.#testUtils.spawnRedisSentinels({serverArguments: DEBUG_MODE_ARGS}, masterPort, this.config.sentinelName, sentinels) } async getAllRunning() { @@ -384,90 +307,71 @@ export class SentinelFramework extends DockerBase { } async addSentinel() { - const quorum = this.config.sentinelQuorum?.toString() ?? "2"; - const node = this.#nodeList[0]; - const sentinel = await this.spawnRedisSentinelSentinelDocker(); - - await sentinel.client.sentinel.sentinelMonitor(this.config.sentinelName, '127.0.0.1', node.docker.port.toString(), quorum); - const options: Array<{option: RedisArgument, value: RedisArgument}> = []; - options.push({ option: "down-after-milliseconds", value: "100" }); - options.push({ option: "failover-timeout", value: "5000" }); - if (this.config.password !== undefined) { - options.push({ option: "auth-pass", value: this.config.password }); - } - await sentinel.client.sentinel.sentinelSet(this.config.sentinelName, options); - - this.#sentinelList.push(sentinel); - this.#sentinelMap.set(sentinel.docker.port.toString(), sentinel); + const nodes = await this.#testUtils.spawnRedisSentinels({serverArguments: DEBUG_MODE_ARGS}, this.#nodeList[0].port, this.config.sentinelName, 1) + this.#sentinelList.push(nodes[0]); + this.#sentinelMap.set(nodes[0].port.toString(), nodes[0]); } async addNode() { const masterPort = await this.getMasterPort(); - const newNode = await this.spawnRedisSentinelNodeDocker(); + const replica = await this.#testUtils.spawnRedisServer({serverArguments: DEBUG_MODE_ARGS}) - if (this.config.password !== undefined) { - await newNode.client.configSet({'masterauth': this.config.password}) - } - await newNode.client.replicaOf('127.0.0.1', masterPort); + const client = RedisClient.create({ + socket: { + port: replica.port + } + }) + + await client.connect(); + await client.replicaOf("127.0.0.1", masterPort); + await client.close(); + - this.#nodeList.push(newNode); - this.#nodeMap.set(newNode.docker.port.toString(), newNode); + this.#nodeList.push(replica); + this.#nodeMap.set(replica.port.toString(), replica); } async getMaster(tracer?: Array): Promise { - for (const sentinel of this.#sentinelMap!.values()) { - let info; - - try { - if (!sentinel.client.isReady) { - continue; - } - - info = await sentinel.client.sentinel.sentinelMaster(this.config.sentinelName); - if (tracer) { - tracer.push('getMaster: master data returned from sentinel'); - tracer.push(JSON.stringify(info, undefined, '\t')) - } - } catch (err) { - console.log("getMaster: sentinelMaster call failed: " + err); - continue; - } - - const master = this.#nodeMap.get(info.port); - if (master === undefined) { - throw new Error(`couldn't find master node for ${info.port}`); - } - - if (tracer) { - tracer.push(`getMaster: master port is either ${info.port} or ${master.docker.port}`); - } + const client = RedisClient.create({ + name: this.config.sentinelName, + socket: { + host: "127.0.0.1", + port: this.#sentinelList[0].port, + }, + modules: RedisSentinelModule, + }); + await client.connect() + const info = await client.sentinel.sentinelMaster(this.config.sentinelName); + await client.close() - if (!master.client.isOpen) { - throw new Error(`Sentinel's expected master node (${info.port}) is now down`); - } + const master = this.#nodeMap.get(info.port); + if (master === undefined) { + throw new Error(`couldn't find master node for ${info.port}`); + } - return info.port; + if (tracer) { + tracer.push(`getMaster: master port is either ${info.port} or ${master.port}`); } - throw new Error("Couldn't get master"); + return info.port; } async getMasterPort(tracer?: Array): Promise { const data = await this.getMaster(tracer) - return this.#nodeMap.get(data!)!.docker.port; + return this.#nodeMap.get(data!)!.port; } getRandomNode() { - return this.#nodeList[Math.floor(Math.random() * this.#nodeList.length)].docker.port.toString(); + return this.#nodeList[Math.floor(Math.random() * this.#nodeList.length)].port.toString(); } async getRandonNonMasterNode(): Promise { const masterPort = await this.getMasterPort(); while (true) { const node = this.#nodeList[Math.floor(Math.random() * this.#nodeList.length)]; - if (node.docker.port != masterPort) { - return node.docker.port.toString(); + if (node.port != masterPort) { + return node.port.toString(); } } } @@ -479,11 +383,7 @@ export class SentinelFramework extends DockerBase { throw new Error("unknown node: " + id); } - if (node.client.isOpen) { - node.client.destroy(); - } - - return await this.dockerStop(node.docker.dockerId); + return await this.dockerStop(node.dockerId); } async restartNode(id: string) { @@ -492,15 +392,7 @@ export class SentinelFramework extends DockerBase { throw new Error("unknown node: " + id); } - await this.dockerStart(node.docker.dockerId); - if (!node.client.isOpen) { - node.client = await RedisClient.create({ - password: this.config.password, - socket: { - port: node.docker.port - } - }).on("error", () => { }).connect(); - } + await this.dockerStart(node.dockerId); } async stopSentinel(id: string) { @@ -509,11 +401,7 @@ export class SentinelFramework extends DockerBase { throw new Error("unknown sentinel: " + id); } - if (sentinel.client.isOpen) { - sentinel.client.destroy(); - } - - return await this.dockerStop(sentinel.docker.dockerId); + return await this.dockerStop(sentinel.dockerId); } async restartSentinel(id: string) { @@ -522,16 +410,7 @@ export class SentinelFramework extends DockerBase { throw new Error("unknown sentinel: " + id); } - await this.dockerStart(sentinel.docker.dockerId); - if (!sentinel.client.isOpen) { - sentinel.client = await RedisClient.create({ - modules: RedisSentinelModule, - password: this.config.password, - socket: { - port: sentinel.docker.port - } - }).on("error", () => { }).connect(); - } + await this.dockerStart(sentinel.dockerId); } getNodePort(id: string) { @@ -540,13 +419,13 @@ export class SentinelFramework extends DockerBase { throw new Error("unknown node: " + id); } - return node.docker.port; + return node.port; } getAllNodesPort() { let ports: Array = []; for (const node of this.#nodeList) { - ports.push(node.docker.port); + ports.push(node.port); } return ports @@ -555,7 +434,7 @@ export class SentinelFramework extends DockerBase { getAllDockerIds() { let ids = new Map(); for (const node of this.#nodeList) { - ids.set(node.docker.dockerId, node.docker.port); + ids.set(node.dockerId, node.port); } return ids; @@ -567,43 +446,67 @@ export class SentinelFramework extends DockerBase { throw new Error("unknown sentinel: " + id); } - return sentinel.docker.port; + return sentinel.port; } getAllSentinelsPort() { let ports: Array = []; for (const sentinel of this.#sentinelList) { - ports.push(sentinel.docker.port); + ports.push(sentinel.port); } return ports } getSetinel(i: number): string { - return this.#sentinelList[i].docker.port.toString(); + return this.#sentinelList[i].port.toString(); } - sentinelSentinels() { - for (const sentinel of this.#sentinelList) { - if (sentinel.client.isReady) { - return sentinel.client.sentinel.sentinelSentinels(this.config.sentinelName); - } - } + async sentinelSentinels() { + const client = RedisClient.create({ + name: this.config.sentinelName, + socket: { + host: "127.0.0.1", + port: this.#sentinelList[0].port, + }, + modules: RedisSentinelModule, + }); + await client.connect() + const sentinels = client.sentinel.sentinelSentinels(this.config.sentinelName) + await client.close() + + return sentinels } - sentinelMaster() { - for (const sentinel of this.#sentinelList) { - if (sentinel.client.isReady) { - return sentinel.client.sentinel.sentinelMaster(this.config.sentinelName); - } - } + async sentinelMaster() { + const client = RedisClient.create({ + name: this.config.sentinelName, + socket: { + host: "127.0.0.1", + port: this.#sentinelList[0].port, + }, + modules: RedisSentinelModule, + }); + await client.connect() + const master = client.sentinel.sentinelMaster(this.config.sentinelName) + await client.close() + + return master } - sentinelReplicas() { - for (const sentinel of this.#sentinelList) { - if (sentinel.client.isReady) { - return sentinel.client.sentinel.sentinelReplicas(this.config.sentinelName); - } - } + async sentinelReplicas() { + const client = RedisClient.create({ + name: this.config.sentinelName, + socket: { + host: "127.0.0.1", + port: this.#sentinelList[0].port, + }, + modules: RedisSentinelModule, + }); + await client.connect() + const replicas = client.sentinel.sentinelReplicas(this.config.sentinelName) + await client.close() + + return replicas } } \ No newline at end of file diff --git a/packages/client/lib/test-utils.ts b/packages/client/lib/test-utils.ts index 63f43ba5e6a..19bbafc66ea 100644 --- a/packages/client/lib/test-utils.ts +++ b/packages/client/lib/test-utils.ts @@ -14,7 +14,7 @@ const utils = TestUtils.createFromConfig({ export default utils; -const DEBUG_MODE_ARGS = utils.isVersionGreaterThan([7]) ? +export const DEBUG_MODE_ARGS = utils.isVersionGreaterThan([7]) ? ['--enable-debug-command', 'yes'] : []; diff --git a/packages/test-utils/lib/dockers.ts b/packages/test-utils/lib/dockers.ts index 3814a80923b..47257964f6a 100644 --- a/packages/test-utils/lib/dockers.ts +++ b/packages/test-utils/lib/dockers.ts @@ -62,7 +62,7 @@ export interface RedisServerDocker { dockerId: string; } -async function spawnRedisServerDocker( +export async function spawnRedisServerDocker( options: RedisServerDockerOptions, serverArguments: Array): Promise { let port; if (options.mode == "sentinel") { @@ -374,35 +374,16 @@ export async function spawnRedisSentinel( const tmpDir = fs.mkdtempSync(path.join(os.tmpdir(), appPrefix)); for (let i = 0; i < sentinelCount; i++) { - sentinelPromises.push((async () => { - const port = (await portIterator.next()).value; - - let sentinelConfig = `port ${port} -sentinel monitor mymaster 127.0.0.1 ${master.port} 2 -sentinel down-after-milliseconds mymaster 5000 -sentinel failover-timeout mymaster 6000 -`; - if (password !== undefined) { - sentinelConfig += `requirepass ${password}\n`; - sentinelConfig += `sentinel auth-pass mymaster ${password}\n`; - } - - const dir = fs.mkdtempSync(path.join(tmpDir, i.toString())); - fs.writeFile(`${dir}/redis.conf`, sentinelConfig, err => { - if (err) { - console.error("failed to create temporary config file", err); - } - }); - - return await spawnRedisServerDocker( - { - image: dockerConfigs.image, - version: dockerConfigs.version, - mode: "sentinel", - mounts: [`${dir}/redis.conf:/redis/config/node-sentinel-1/redis.conf`], - port: port, - }, serverArguments); - })()); + sentinelPromises.push( + spawnSentinelNode( + dockerConfigs, + serverArguments, + master.port, + "mymaster", + path.join(tmpDir, i.toString()), + password, + ), + ) } const sentinelNodes = await Promise.all(sentinelPromises); @@ -424,3 +405,43 @@ after(() => { }) ); }); + + +export async function spawnSentinelNode( + dockerConfigs: RedisServerDockerOptions, + serverArguments: Array, + masterPort: number, + sentinelName: string, + tmpDir: string, + password?: string, +) { + const port = (await portIterator.next()).value; + + let sentinelConfig = `port ${port} +sentinel monitor ${sentinelName} 127.0.0.1 ${masterPort} 2 +sentinel down-after-milliseconds ${sentinelName} 500 +sentinel failover-timeout ${sentinelName} 1000 +`; + if (password !== undefined) { + sentinelConfig += `requirepass ${password}\n`; + sentinelConfig += `sentinel auth-pass ${sentinelName} ${password}\n`; + } + + const dir = fs.mkdtempSync(tmpDir); + fs.writeFile(`${dir}/redis.conf`, sentinelConfig, err => { + if (err) { + console.error("failed to create temporary config file", err); + } + }); + + return await spawnRedisServerDocker( + { + image: dockerConfigs.image, + version: dockerConfigs.version, + mode: "sentinel", + mounts: [`${dir}/redis.conf:/redis/config/node-sentinel-1/redis.conf`], + port: port, + }, + serverArguments, + ); +} \ No newline at end of file diff --git a/packages/test-utils/lib/index.ts b/packages/test-utils/lib/index.ts index d92c5c9e3d8..a41f970e0c8 100644 --- a/packages/test-utils/lib/index.ts +++ b/packages/test-utils/lib/index.ts @@ -19,10 +19,13 @@ import { RedisClusterType } from '@redis/client/index'; import { RedisNode } from '@redis/client/lib/sentinel/types' -import { spawnRedisServer, spawnRedisCluster, spawnRedisSentinel, RedisServerDockerOptions } from './dockers'; +import { spawnRedisServer, spawnRedisCluster, spawnRedisSentinel, RedisServerDockerOptions, RedisServerDocker, spawnSentinelNode, spawnRedisServerDocker } from './dockers'; import yargs from 'yargs'; import { hideBin } from 'yargs/helpers'; +import * as fs from 'node:fs'; +import * as os from 'node:os'; +import * as path from 'node:path'; interface TestUtilsConfig { /** @@ -395,19 +398,19 @@ export default class TestUtils { S extends RedisScripts = {}, RESP extends RespVersions = 2, TYPE_MAPPING extends TypeMapping = {} ->( - range: ([minVersion: Array, maxVersion: Array] | [minVersion: Array, 'LATEST']), - title: string, - fn: (sentinel: RedisSentinelType) => unknown, - options: SentinelTestOptions -): void { - - if (this.isVersionInRange(range[0], range[1] === 'LATEST' ? [Infinity, Infinity, Infinity] : range[1])) { - return this.testWithClientSentinel(`${title} [${range[0].join('.')}] - [${(range[1] === 'LATEST') ? range[1] : range[1].join(".")}] `, fn, options) - } else { - console.warn(`Skipping test ${title} because server version ${this.#VERSION_NUMBERS.join('.')} is not within range ${range[0].join(".")} - ${range[1] !== 'LATEST' ? range[1].join(".") : 'LATEST'}`) + >( + range: ([minVersion: Array, maxVersion: Array] | [minVersion: Array, 'LATEST']), + title: string, + fn: (sentinel: RedisSentinelType) => unknown, + options: SentinelTestOptions + ): void { + + if (this.isVersionInRange(range[0], range[1] === 'LATEST' ? [Infinity, Infinity, Infinity] : range[1])) { + return this.testWithClientSentinel(`${title} [${range[0].join('.')}] - [${(range[1] === 'LATEST') ? range[1] : range[1].join(".")}] `, fn, options) + } else { + console.warn(`Skipping test ${title} because server version ${this.#VERSION_NUMBERS.join('.')} is not within range ${range[0].join(".")} - ${range[1] !== 'LATEST' ? range[1].join(".") : 'LATEST'}`) + } } -} testWithClientPool< M extends RedisModules = {}, @@ -541,4 +544,46 @@ export default class TestUtils { this.testWithClient(`client.${title}`, fn, options.client); this.testWithCluster(`cluster.${title}`, fn, options.cluster); } + + + spawnRedisServer< + M extends RedisModules = {}, + F extends RedisFunctions = {}, + S extends RedisScripts = {}, + RESP extends RespVersions = 2, + TYPE_MAPPING extends TypeMapping = {} + // POLICIES extends CommandPolicies = {} + >( + options: ClientPoolTestOptions + ): Promise { + return spawnRedisServerDocker(this.#DOCKER_IMAGE, options.serverArguments) + } + + async spawnRedisSentinels< + M extends RedisModules = {}, + F extends RedisFunctions = {}, + S extends RedisScripts = {}, + RESP extends RespVersions = 2, + TYPE_MAPPING extends TypeMapping = {} + // POLICIES extends CommandPolicies = {} + >( + options: ClientPoolTestOptions, + masterPort: number, + sentinelName: string, + count: number + ): Promise> { + const sentinels: Array = []; + for (let i = 0; i < count; i++) { + const appPrefix = 'sentinel-config-dir'; + const tmpDir = fs.mkdtempSync(path.join(os.tmpdir(), appPrefix)); + + sentinels.push(await spawnSentinelNode(this.#DOCKER_IMAGE, options.serverArguments, masterPort, sentinelName, tmpDir)) + + if (tmpDir) { + fs.rmSync(tmpDir, { recursive: true }); + } + } + + return sentinels + } } From 708b22f366d2825f5b2b58c49a764373e6814bfa Mon Sep 17 00:00:00 2001 From: Arya Date: Wed, 28 May 2025 13:15:37 +0530 Subject: [PATCH 1601/1748] docs(readme): replace relative GitHub links with absolute URLs (#2980) --- packages/redis/README.md | 38 +++++++++++++++++++------------------- 1 file changed, 19 insertions(+), 19 deletions(-) diff --git a/packages/redis/README.md b/packages/redis/README.md index d04a19b0d71..ab6b4707e6f 100644 --- a/packages/redis/README.md +++ b/packages/redis/README.md @@ -45,13 +45,13 @@ npm install redis | Name | Description | | ---------------------------------------------- | ------------------------------------------------------------------------------------------- | -| [`redis`](../redis) | The client with all the ["redis-stack"](https://github.com/redis-stack/redis-stack) modules | -| [`@redis/client`](../client) | The base clients (i.e `RedisClient`, `RedisCluster`, etc.) | -| [`@redis/bloom`](../bloom) | [Redis Bloom](https://redis.io/docs/data-types/probabilistic/) commands | -| [`@redis/json`](../json) | [Redis JSON](https://redis.io/docs/data-types/json/) commands | -| [`@redis/search`](../search) | [RediSearch](https://redis.io/docs/interact/search-and-query/) commands | -| [`@redis/time-series`](../time-series) | [Redis Time-Series](https://redis.io/docs/data-types/timeseries/) commands | -| [`@redis/entraid`](../entraid) | Secure token-based authentication for Redis clients using Microsoft Entra ID | +| [`redis`](https://github.com/redis/node-redis/tree/master/packages/redis) | The client with all the ["redis-stack"](https://github.com/redis-stack/redis-stack) modules | +| [`@redis/client`](https://github.com/redis/node-redis/tree/master/packages/client) | The base clients (i.e `RedisClient`, `RedisCluster`, etc.) | +| [`@redis/bloom`](https://github.com/redis/node-redis/tree/master/packages/bloom) | [Redis Bloom](https://redis.io/docs/data-types/probabilistic/) commands | +| [`@redis/json`](https://github.com/redis/node-redis/tree/master/packages/json) | [Redis JSON](https://redis.io/docs/data-types/json/) commands | +| [`@redis/search`](https://github.com/redis/node-redis/tree/master/packages/search) | [RediSearch](https://redis.io/docs/interact/search-and-query/) commands | +| [`@redis/time-series`](https://github.com/redis/node-redis/tree/master/packages/time-series) | [Redis Time-Series](https://redis.io/docs/data-types/timeseries/) commands | +| [`@redis/entraid`](https://github.com/redis/node-redis/tree/master/packages/entraid) | Secure token-based authentication for Redis clients using Microsoft Entra ID | > Looking for a high-level library to handle object mapping? > See [redis-om-node](https://github.com/redis/redis-om-node)! @@ -83,7 +83,7 @@ createClient({ ``` 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). +the [client configuration guide](https://github.com/redis/node-redis/blob/master/docs/client-configuration.md). To check if the the client is connected and ready to send commands, use `client.isReady` which returns a boolean. `client.isOpen` is also available. This returns `true` when the client's underlying socket is open, and `false` when it @@ -188,7 +188,7 @@ await pool.ping(); ### Pub/Sub -See the [Pub/Sub overview](../../docs/pub-sub.md). +See the [Pub/Sub overview](https://github.com/redis/node-redis/blob/master/docs/pub-sub.md). ### Scan Iterator @@ -250,7 +250,7 @@ const client = createClient({ }); ``` -See the [V5 documentation](../../docs/v5.md#client-side-caching) for more details and advanced usage. +See the [V5 documentation](https://github.com/redis/node-redis/blob/master/docs/v5.md#client-side-caching) for more details and advanced usage. ### Auto-Pipelining @@ -274,11 +274,11 @@ await Promise.all([ ### Programmability -See the [Programmability overview](../../docs/programmability.md). +See the [Programmability overview](https://github.com/redis/node-redis/blob/master/docs/programmability.md). ### Clustering -Check out the [Clustering Guide](../../docs/clustering.md) when using Node Redis to connect to a Redis Cluster. +Check out the [Clustering Guide](https://github.com/redis/node-redis/blob/master/docs/clustering.md) when using Node Redis to connect to a Redis Cluster. ### Events @@ -291,12 +291,12 @@ The Node Redis client class is an Nodejs EventEmitter and it emits an event each | `end` | Connection has been closed (via `.disconnect()`) | _No arguments_ | | `error` | An error has occurred—usually a network issue such as "Socket closed unexpectedly" | `(error: Error)` | | `reconnecting` | Client is trying to reconnect to the server | _No arguments_ | -| `sharded-channel-moved` | See [here](../../docs/pub-sub.md#sharded-channel-moved-event) | See [here](../../docs/pub-sub.md#sharded-channel-moved-event) | +| `sharded-channel-moved` | See [here](https://github.com/redis/node-redis/blob/master/docs/pub-sub.md#sharded-channel-moved-event) | See [here](https://github.com/redis/node-redis/blob/master/docs/pub-sub.md#sharded-channel-moved-event) | > :warning: You **MUST** listen to `error` events. If a client doesn't have at least one `error` listener registered and > an `error` occurs, that error will be thrown and the Node.js process will exit. See the [ > `EventEmitter` docs](https://nodejs.org/api/events.html#events_error_events) for more details. -> The client will not emit [any other events](../../docs/v3-to-v4.md#all-the-removed-events) beyond those listed above. +> The client will not emit [any other events](https://github.com/redis/node-redis/blob/master/docs/v3-to-v4.md#all-the-removed-events) beyond those listed above. ## Supported Redis versions @@ -313,13 +313,13 @@ Node Redis is supported with the following versions of Redis: ## Migration -- [From V3 to V4](../../docs/v3-to-v4.md) -- [From V4 to V5](../../docs/v4-to-v5.md) -- [V5](../../docs/v5.md) +- [From V3 to V4](https://github.com/redis/node-redis/blob/master/docs/v3-to-v4.md) +- [From V4 to V5](https://github.com/redis/node-redis/blob/master/docs/v4-to-v5.md) +- [V5](https://github.com/redis/node-redis/blob/master/docs/v5.md) ## Contributing -If you'd like to contribute, check out the [contributing guide](../../CONTRIBUTING.md). +If you'd like to contribute, check out the [contributing guide](https://github.com/redis/node-redis/blob/master/CONTRIBUTING.md). Thank you to all the people who already contributed to Node Redis! @@ -327,4 +327,4 @@ Thank you to all the people who already contributed to Node Redis! ## License -This repository is licensed under the "MIT" license. See [LICENSE](../../LICENSE). +This repository is licensed under the "MIT" license. See [LICENSE](https://github.com/redis/node-redis/blob/master/LICENSE). From 4b939a7bdbc5791e1b33212da1890994c36ff5e7 Mon Sep 17 00:00:00 2001 From: Nikolay Karadzhov Date: Wed, 28 May 2025 16:16:25 +0300 Subject: [PATCH 1602/1748] Release client@5.1.1 --- packages/client/package.json | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/packages/client/package.json b/packages/client/package.json index dd41dc53e0b..a6d44451a62 100644 --- a/packages/client/package.json +++ b/packages/client/package.json @@ -1,6 +1,6 @@ { "name": "@redis/client", - "version": "5.1.0", + "version": "5.1.1", "license": "MIT", "main": "./dist/index.js", "types": "./dist/index.d.ts", From bd6f764d91ee46d833a21c0671e8bc79693fcfa3 Mon Sep 17 00:00:00 2001 From: Nikolay Karadzhov Date: Wed, 28 May 2025 16:19:30 +0300 Subject: [PATCH 1603/1748] Updated the Bloom package to use client@5.1.1 --- package-lock.json | 16 ++++++++++++++-- packages/bloom/package.json | 2 +- 2 files changed, 15 insertions(+), 3 deletions(-) diff --git a/package-lock.json b/package-lock.json index ab4cbe138bc..9ffa7854a89 100644 --- a/package-lock.json +++ b/package-lock.json @@ -8529,12 +8529,12 @@ "node": ">= 18" }, "peerDependencies": { - "@redis/client": "^5.1.0" + "@redis/client": "^5.1.1" } }, "packages/client": { "name": "@redis/client", - "version": "5.1.0", + "version": "5.1.1", "license": "MIT", "dependencies": { "cluster-key-slot": "1.1.2" @@ -8632,6 +8632,18 @@ "node": ">= 18" } }, + "packages/redis/node_modules/@redis/client": { + "version": "5.1.0", + "resolved": "https://registry.npmjs.org/@redis/client/-/client-5.1.0.tgz", + "integrity": "sha512-FMD35y2KgCWTBLOfF0MhwDSaIVcu5mOUuTV9Kw3JOWHMgON3+ulht31cjTB/gph0BfD1vzUvCeROeRaf/d+35w==", + "license": "MIT", + "dependencies": { + "cluster-key-slot": "1.1.2" + }, + "engines": { + "node": ">= 18" + } + }, "packages/search": { "name": "@redis/search", "version": "5.1.0", diff --git a/packages/bloom/package.json b/packages/bloom/package.json index 3c8e37c5b74..e642650c283 100644 --- a/packages/bloom/package.json +++ b/packages/bloom/package.json @@ -12,7 +12,7 @@ "test": "nyc -r text-summary -r lcov mocha -r tsx './lib/**/*.spec.ts'" }, "peerDependencies": { - "@redis/client": "^5.1.0" + "@redis/client": "^5.1.1" }, "devDependencies": { "@redis/test-utils": "*" From 3b03ced4ea5e7e8c316a9d38e607c91aa044d664 Mon Sep 17 00:00:00 2001 From: Nikolay Karadzhov Date: Wed, 28 May 2025 16:20:11 +0300 Subject: [PATCH 1604/1748] Release bloom@5.1.1 --- packages/bloom/package.json | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/packages/bloom/package.json b/packages/bloom/package.json index e642650c283..547e5ee64ed 100644 --- a/packages/bloom/package.json +++ b/packages/bloom/package.json @@ -1,6 +1,6 @@ { "name": "@redis/bloom", - "version": "5.1.0", + "version": "5.1.1", "license": "MIT", "main": "./dist/lib/index.js", "types": "./dist/lib/index.d.ts", From 566b16eb53b8d362dd910228832bac1f01df931c Mon Sep 17 00:00:00 2001 From: Nikolay Karadzhov Date: Wed, 28 May 2025 16:21:25 +0300 Subject: [PATCH 1605/1748] Updated the Entraid package to use client@5.1.1 --- package-lock.json | 16 ++++++++++++++-- packages/entraid/package.json | 2 +- 2 files changed, 15 insertions(+), 3 deletions(-) diff --git a/package-lock.json b/package-lock.json index 9ffa7854a89..d08701f4df6 100644 --- a/package-lock.json +++ b/package-lock.json @@ -8520,7 +8520,7 @@ }, "packages/bloom": { "name": "@redis/bloom", - "version": "5.1.0", + "version": "5.1.1", "license": "MIT", "devDependencies": { "@redis/test-utils": "*" @@ -8569,7 +8569,7 @@ "node": ">= 18" }, "peerDependencies": { - "@redis/client": "^5.1.0" + "@redis/client": "^5.1.1" } }, "packages/entraid/node_modules/@types/node": { @@ -8632,6 +8632,18 @@ "node": ">= 18" } }, + "packages/redis/node_modules/@redis/bloom": { + "version": "5.1.0", + "resolved": "https://registry.npmjs.org/@redis/bloom/-/bloom-5.1.0.tgz", + "integrity": "sha512-Gp5RWvVKbvItMU2sd848yhY/BnigToz8H4PYcvlBBSP5cQ3lVP1LMh5Kx2CYBNzCdDabVicwBKNvaoLBqPNqIg==", + "license": "MIT", + "engines": { + "node": ">= 18" + }, + "peerDependencies": { + "@redis/client": "^5.1.0" + } + }, "packages/redis/node_modules/@redis/client": { "version": "5.1.0", "resolved": "https://registry.npmjs.org/@redis/client/-/client-5.1.0.tgz", diff --git a/packages/entraid/package.json b/packages/entraid/package.json index ba44351b3b9..bd20b01ac7d 100644 --- a/packages/entraid/package.json +++ b/packages/entraid/package.json @@ -21,7 +21,7 @@ "@azure/msal-node": "^2.16.1" }, "peerDependencies": { - "@redis/client": "^5.1.0" + "@redis/client": "^5.1.1" }, "devDependencies": { "@types/express": "^4.17.21", From e4d903ca0cbe17577a3a06f9f54ffddd993c0630 Mon Sep 17 00:00:00 2001 From: Nikolay Karadzhov Date: Wed, 28 May 2025 16:21:59 +0300 Subject: [PATCH 1606/1748] Release entraid@5.1.1 --- packages/entraid/package.json | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/packages/entraid/package.json b/packages/entraid/package.json index bd20b01ac7d..43f26d1e039 100644 --- a/packages/entraid/package.json +++ b/packages/entraid/package.json @@ -1,6 +1,6 @@ { "name": "@redis/entraid", - "version": "5.1.0", + "version": "5.1.1", "license": "MIT", "main": "./dist/index.js", "types": "./dist/index.d.ts", From e68b5deb190f9ab841ca2f987b2e5f5483096188 Mon Sep 17 00:00:00 2001 From: Nikolay Karadzhov Date: Wed, 28 May 2025 16:23:12 +0300 Subject: [PATCH 1607/1748] Updated the Json package to use client@5.1.1 --- package-lock.json | 4 ++-- packages/json/package.json | 2 +- 2 files changed, 3 insertions(+), 3 deletions(-) diff --git a/package-lock.json b/package-lock.json index d08701f4df6..21eeebac222 100644 --- a/package-lock.json +++ b/package-lock.json @@ -8550,7 +8550,7 @@ }, "packages/entraid": { "name": "@redis/entraid", - "version": "5.1.0", + "version": "5.1.1", "license": "MIT", "dependencies": { "@azure/identity": "^4.7.0", @@ -8615,7 +8615,7 @@ "node": ">= 18" }, "peerDependencies": { - "@redis/client": "^5.1.0" + "@redis/client": "^5.1.1" } }, "packages/redis": { diff --git a/packages/json/package.json b/packages/json/package.json index fd0b3c768ff..b3fe4a27ee7 100644 --- a/packages/json/package.json +++ b/packages/json/package.json @@ -12,7 +12,7 @@ "test": "nyc -r text-summary -r lcov mocha -r tsx './lib/**/*.spec.ts'" }, "peerDependencies": { - "@redis/client": "^5.1.0" + "@redis/client": "^5.1.1" }, "devDependencies": { "@redis/test-utils": "*" From 8c7b384e6b51e0b74d19267a0c94e6e68baca952 Mon Sep 17 00:00:00 2001 From: Nikolay Karadzhov Date: Wed, 28 May 2025 16:23:31 +0300 Subject: [PATCH 1608/1748] Release json@5.1.1 --- packages/json/package.json | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/packages/json/package.json b/packages/json/package.json index b3fe4a27ee7..3b473dfce0f 100644 --- a/packages/json/package.json +++ b/packages/json/package.json @@ -1,6 +1,6 @@ { "name": "@redis/json", - "version": "5.1.0", + "version": "5.1.1", "license": "MIT", "main": "./dist/lib/index.js", "types": "./dist/lib/index.d.ts", From b2213d813254b842f3b0f424b74a416fedd6178f Mon Sep 17 00:00:00 2001 From: Nikolay Karadzhov Date: Wed, 28 May 2025 16:24:32 +0300 Subject: [PATCH 1609/1748] Updated the Search package to use client@5.1.1 --- package-lock.json | 16 ++++++++++++++-- packages/search/package.json | 2 +- 2 files changed, 15 insertions(+), 3 deletions(-) diff --git a/package-lock.json b/package-lock.json index 21eeebac222..c33dc30b282 100644 --- a/package-lock.json +++ b/package-lock.json @@ -8606,7 +8606,7 @@ }, "packages/json": { "name": "@redis/json", - "version": "5.1.0", + "version": "5.1.1", "license": "MIT", "devDependencies": { "@redis/test-utils": "*" @@ -8656,6 +8656,18 @@ "node": ">= 18" } }, + "packages/redis/node_modules/@redis/json": { + "version": "5.1.0", + "resolved": "https://registry.npmjs.org/@redis/json/-/json-5.1.0.tgz", + "integrity": "sha512-laXZt1Rlimk3py5ZoABBnd4xn/8dWbLUWGvVS7avgMhdczS+eWtXpElilJbFpc+7ZpVlol4vaSGFuR8ThDcTFw==", + "license": "MIT", + "engines": { + "node": ">= 18" + }, + "peerDependencies": { + "@redis/client": "^5.1.0" + } + }, "packages/search": { "name": "@redis/search", "version": "5.1.0", @@ -8667,7 +8679,7 @@ "node": ">= 18" }, "peerDependencies": { - "@redis/client": "^5.1.0" + "@redis/client": "^5.1.1" } }, "packages/test-utils": { diff --git a/packages/search/package.json b/packages/search/package.json index ac56b5ff5db..a44f6322a7f 100644 --- a/packages/search/package.json +++ b/packages/search/package.json @@ -13,7 +13,7 @@ "test-sourcemap": "mocha -r ts-node/register/transpile-only './lib/**/*.spec.ts'" }, "peerDependencies": { - "@redis/client": "^5.1.0" + "@redis/client": "^5.1.1" }, "devDependencies": { "@redis/test-utils": "*" From 13566cf9d6a85ce076d8bd4df735353bb71a7686 Mon Sep 17 00:00:00 2001 From: Nikolay Karadzhov Date: Wed, 28 May 2025 16:24:51 +0300 Subject: [PATCH 1610/1748] Release search@5.1.1 --- packages/search/package.json | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/packages/search/package.json b/packages/search/package.json index a44f6322a7f..7cb73dfc0a5 100644 --- a/packages/search/package.json +++ b/packages/search/package.json @@ -1,6 +1,6 @@ { "name": "@redis/search", - "version": "5.1.0", + "version": "5.1.1", "license": "MIT", "main": "./dist/lib/index.js", "types": "./dist/lib/index.d.ts", From d178b7839a458bc7c3e20569a416e9d4c7a4d4cc Mon Sep 17 00:00:00 2001 From: Nikolay Karadzhov Date: Wed, 28 May 2025 16:25:37 +0300 Subject: [PATCH 1611/1748] Updated the Timeseries package to use client@5.1.1 --- package-lock.json | 16 ++++++++++++++-- packages/time-series/package.json | 2 +- 2 files changed, 15 insertions(+), 3 deletions(-) diff --git a/package-lock.json b/package-lock.json index c33dc30b282..325eefb08b3 100644 --- a/package-lock.json +++ b/package-lock.json @@ -8668,9 +8668,21 @@ "@redis/client": "^5.1.0" } }, + "packages/redis/node_modules/@redis/search": { + "version": "5.1.0", + "resolved": "https://registry.npmjs.org/@redis/search/-/search-5.1.0.tgz", + "integrity": "sha512-afQYMeIdWGNPjr84GxxgJVkolxMW3BBrlNOwhRHPdzbdGh0rTUPjOJpGHBig34ostHX6AhZ6QwqceU1zLluDEg==", + "license": "MIT", + "engines": { + "node": ">= 18" + }, + "peerDependencies": { + "@redis/client": "^5.1.0" + } + }, "packages/search": { "name": "@redis/search", - "version": "5.1.0", + "version": "5.1.1", "license": "MIT", "devDependencies": { "@redis/test-utils": "*" @@ -8757,7 +8769,7 @@ "node": ">= 18" }, "peerDependencies": { - "@redis/client": "^5.1.0" + "@redis/client": "^5.1.1" } } } diff --git a/packages/time-series/package.json b/packages/time-series/package.json index 466c53df220..3acef8b8668 100644 --- a/packages/time-series/package.json +++ b/packages/time-series/package.json @@ -12,7 +12,7 @@ "test": "nyc -r text-summary -r lcov mocha -r tsx './lib/**/*.spec.ts'" }, "peerDependencies": { - "@redis/client": "^5.1.0" + "@redis/client": "^5.1.1" }, "devDependencies": { "@redis/test-utils": "*" From d4380221c84a83734c2936ce3fc624c7b962b123 Mon Sep 17 00:00:00 2001 From: Nikolay Karadzhov Date: Wed, 28 May 2025 16:25:56 +0300 Subject: [PATCH 1612/1748] Release time-series@5.1.1 --- packages/time-series/package.json | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/packages/time-series/package.json b/packages/time-series/package.json index 3acef8b8668..888f0f317e2 100644 --- a/packages/time-series/package.json +++ b/packages/time-series/package.json @@ -1,6 +1,6 @@ { "name": "@redis/time-series", - "version": "5.1.0", + "version": "5.1.1", "license": "MIT", "main": "./dist/lib/index.js", "types": "./dist/lib/index.d.ts", From d374514309a4975367dfbf02d14538c721cd2f34 Mon Sep 17 00:00:00 2001 From: Nikolay Karadzhov Date: Wed, 28 May 2025 16:27:23 +0300 Subject: [PATCH 1613/1748] Updated the Redis package to use client@5.1.1 --- package-lock.json | 60 ++++--------------------------------- packages/redis/package.json | 10 +++---- 2 files changed, 11 insertions(+), 59 deletions(-) diff --git a/package-lock.json b/package-lock.json index 325eefb08b3..8fae6bef850 100644 --- a/package-lock.json +++ b/package-lock.json @@ -8622,64 +8622,16 @@ "version": "5.1.0", "license": "MIT", "dependencies": { - "@redis/bloom": "5.1.0", - "@redis/client": "5.1.0", - "@redis/json": "5.1.0", - "@redis/search": "5.1.0", - "@redis/time-series": "5.1.0" + "@redis/bloom": "5.1.1", + "@redis/client": "5.1.1", + "@redis/json": "5.1.1", + "@redis/search": "5.1.1", + "@redis/time-series": "5.1.1" }, "engines": { "node": ">= 18" } }, - "packages/redis/node_modules/@redis/bloom": { - "version": "5.1.0", - "resolved": "https://registry.npmjs.org/@redis/bloom/-/bloom-5.1.0.tgz", - "integrity": "sha512-Gp5RWvVKbvItMU2sd848yhY/BnigToz8H4PYcvlBBSP5cQ3lVP1LMh5Kx2CYBNzCdDabVicwBKNvaoLBqPNqIg==", - "license": "MIT", - "engines": { - "node": ">= 18" - }, - "peerDependencies": { - "@redis/client": "^5.1.0" - } - }, - "packages/redis/node_modules/@redis/client": { - "version": "5.1.0", - "resolved": "https://registry.npmjs.org/@redis/client/-/client-5.1.0.tgz", - "integrity": "sha512-FMD35y2KgCWTBLOfF0MhwDSaIVcu5mOUuTV9Kw3JOWHMgON3+ulht31cjTB/gph0BfD1vzUvCeROeRaf/d+35w==", - "license": "MIT", - "dependencies": { - "cluster-key-slot": "1.1.2" - }, - "engines": { - "node": ">= 18" - } - }, - "packages/redis/node_modules/@redis/json": { - "version": "5.1.0", - "resolved": "https://registry.npmjs.org/@redis/json/-/json-5.1.0.tgz", - "integrity": "sha512-laXZt1Rlimk3py5ZoABBnd4xn/8dWbLUWGvVS7avgMhdczS+eWtXpElilJbFpc+7ZpVlol4vaSGFuR8ThDcTFw==", - "license": "MIT", - "engines": { - "node": ">= 18" - }, - "peerDependencies": { - "@redis/client": "^5.1.0" - } - }, - "packages/redis/node_modules/@redis/search": { - "version": "5.1.0", - "resolved": "https://registry.npmjs.org/@redis/search/-/search-5.1.0.tgz", - "integrity": "sha512-afQYMeIdWGNPjr84GxxgJVkolxMW3BBrlNOwhRHPdzbdGh0rTUPjOJpGHBig34ostHX6AhZ6QwqceU1zLluDEg==", - "license": "MIT", - "engines": { - "node": ">= 18" - }, - "peerDependencies": { - "@redis/client": "^5.1.0" - } - }, "packages/search": { "name": "@redis/search", "version": "5.1.1", @@ -8760,7 +8712,7 @@ }, "packages/time-series": { "name": "@redis/time-series", - "version": "5.1.0", + "version": "5.1.1", "license": "MIT", "devDependencies": { "@redis/test-utils": "*" diff --git a/packages/redis/package.json b/packages/redis/package.json index b6c3f409a69..e9e863cc264 100644 --- a/packages/redis/package.json +++ b/packages/redis/package.json @@ -10,11 +10,11 @@ "!dist/tsconfig.tsbuildinfo" ], "dependencies": { - "@redis/bloom": "5.1.0", - "@redis/client": "5.1.0", - "@redis/json": "5.1.0", - "@redis/search": "5.1.0", - "@redis/time-series": "5.1.0" + "@redis/bloom": "5.1.1", + "@redis/client": "5.1.1", + "@redis/json": "5.1.1", + "@redis/search": "5.1.1", + "@redis/time-series": "5.1.1" }, "engines": { "node": ">= 18" From e4a1ca467fcbc1942fa6eb2f7b60b081110544e4 Mon Sep 17 00:00:00 2001 From: Nikolay Karadzhov Date: Wed, 28 May 2025 16:31:12 +0300 Subject: [PATCH 1614/1748] Release redis@5.1.1 --- package-lock.json | 2 +- packages/redis/package.json | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/package-lock.json b/package-lock.json index 8fae6bef850..6d2460ed19f 100644 --- a/package-lock.json +++ b/package-lock.json @@ -8619,7 +8619,7 @@ } }, "packages/redis": { - "version": "5.1.0", + "version": "5.1.1", "license": "MIT", "dependencies": { "@redis/bloom": "5.1.1", diff --git a/packages/redis/package.json b/packages/redis/package.json index e9e863cc264..05fb70cdd11 100644 --- a/packages/redis/package.json +++ b/packages/redis/package.json @@ -1,7 +1,7 @@ { "name": "redis", "description": "A modern, high performance Redis client", - "version": "5.1.0", + "version": "5.1.1", "license": "MIT", "main": "./dist/index.js", "types": "./dist/index.d.ts", From 20c16e0c2c82c6637c84c3f280e86ddad7fe0849 Mon Sep 17 00:00:00 2001 From: "Bobby I." Date: Tue, 3 Jun 2025 14:38:07 +0300 Subject: [PATCH 1615/1748] (docs) add jsdoc comments to command parsers (#2984) * (docs) bloom: add jsdocs for all commands * (docs) json: add jsdocs * (docs) search: add jsdocs for all commands * (docs) jsdocs for std commands * (docs) jsdoc comments to time series commands --- packages/bloom/lib/commands/bloom/ADD.ts | 6 +++ packages/bloom/lib/commands/bloom/CARD.ts | 5 +++ packages/bloom/lib/commands/bloom/EXISTS.ts | 6 +++ packages/bloom/lib/commands/bloom/INFO.ts | 5 +++ packages/bloom/lib/commands/bloom/INSERT.ts | 12 ++++++ .../bloom/lib/commands/bloom/LOADCHUNK.ts | 7 ++++ packages/bloom/lib/commands/bloom/MADD.ts | 6 +++ packages/bloom/lib/commands/bloom/MEXISTS.ts | 6 +++ packages/bloom/lib/commands/bloom/RESERVE.ts | 10 +++++ packages/bloom/lib/commands/bloom/SCANDUMP.ts | 6 +++ .../lib/commands/count-min-sketch/INCRBY.ts | 6 +++ .../lib/commands/count-min-sketch/INFO.ts | 5 +++ .../commands/count-min-sketch/INITBYDIM.ts | 7 ++++ .../commands/count-min-sketch/INITBYPROB.ts | 7 ++++ .../lib/commands/count-min-sketch/MERGE.ts | 6 +++ .../lib/commands/count-min-sketch/QUERY.ts | 6 +++ packages/bloom/lib/commands/cuckoo/ADD.ts | 6 +++ packages/bloom/lib/commands/cuckoo/ADDNX.ts | 6 +++ packages/bloom/lib/commands/cuckoo/COUNT.ts | 6 +++ packages/bloom/lib/commands/cuckoo/DEL.ts | 6 +++ packages/bloom/lib/commands/cuckoo/EXISTS.ts | 6 +++ packages/bloom/lib/commands/cuckoo/INFO.ts | 5 +++ packages/bloom/lib/commands/cuckoo/INSERT.ts | 9 +++++ .../bloom/lib/commands/cuckoo/INSERTNX.ts | 9 +++++ .../bloom/lib/commands/cuckoo/LOADCHUNK.ts | 7 ++++ packages/bloom/lib/commands/cuckoo/RESERVE.ts | 10 +++++ .../bloom/lib/commands/cuckoo/SCANDUMP.ts | 6 +++ packages/bloom/lib/commands/t-digest/ADD.ts | 6 +++ .../bloom/lib/commands/t-digest/BYRANK.ts | 6 +++ .../bloom/lib/commands/t-digest/BYREVRANK.ts | 6 +++ packages/bloom/lib/commands/t-digest/CDF.ts | 6 +++ .../bloom/lib/commands/t-digest/CREATE.ts | 7 ++++ packages/bloom/lib/commands/t-digest/INFO.ts | 5 +++ packages/bloom/lib/commands/t-digest/MAX.ts | 5 +++ packages/bloom/lib/commands/t-digest/MERGE.ts | 9 +++++ packages/bloom/lib/commands/t-digest/MIN.ts | 5 +++ .../bloom/lib/commands/t-digest/QUANTILE.ts | 6 +++ packages/bloom/lib/commands/t-digest/RANK.ts | 6 +++ packages/bloom/lib/commands/t-digest/RESET.ts | 5 +++ .../bloom/lib/commands/t-digest/REVRANK.ts | 6 +++ .../lib/commands/t-digest/TRIMMED_MEAN.ts | 7 ++++ packages/bloom/lib/commands/top-k/ADD.ts | 6 +++ packages/bloom/lib/commands/top-k/COUNT.ts | 6 +++ packages/bloom/lib/commands/top-k/INCRBY.ts | 6 +++ packages/bloom/lib/commands/top-k/INFO.ts | 5 +++ packages/bloom/lib/commands/top-k/LIST.ts | 5 +++ .../lib/commands/top-k/LIST_WITHCOUNT.ts | 5 +++ packages/bloom/lib/commands/top-k/QUERY.ts | 6 +++ packages/bloom/lib/commands/top-k/RESERVE.ts | 10 +++++ packages/client/lib/commands/ACL_CAT.ts | 5 +++ packages/client/lib/commands/ACL_DELUSER.ts | 5 +++ packages/client/lib/commands/ACL_DRYRUN.ts | 6 +++ packages/client/lib/commands/ACL_GENPASS.ts | 5 +++ packages/client/lib/commands/ACL_GETUSER.ts | 5 +++ packages/client/lib/commands/ACL_LIST.ts | 4 ++ packages/client/lib/commands/ACL_LOAD.ts | 4 ++ packages/client/lib/commands/ACL_LOG.ts | 5 +++ packages/client/lib/commands/ACL_LOG_RESET.ts | 4 ++ packages/client/lib/commands/ACL_SAVE.ts | 4 ++ packages/client/lib/commands/ACL_SETUSER.ts | 6 +++ packages/client/lib/commands/ACL_USERS.ts | 4 ++ packages/client/lib/commands/ACL_WHOAMI.ts | 4 ++ packages/client/lib/commands/APPEND.ts | 6 +++ packages/client/lib/commands/ASKING.ts | 4 ++ packages/client/lib/commands/AUTH.ts | 7 ++++ packages/client/lib/commands/BGREWRITEAOF.ts | 4 ++ packages/client/lib/commands/BGSAVE.ts | 6 +++ packages/client/lib/commands/BITCOUNT.ts | 9 +++++ packages/client/lib/commands/BITFIELD.ts | 6 +++ packages/client/lib/commands/BITFIELD_RO.ts | 6 +++ packages/client/lib/commands/BITOP.ts | 7 ++++ packages/client/lib/commands/BITPOS.ts | 9 +++++ packages/client/lib/commands/BLMOVE.ts | 9 +++++ packages/client/lib/commands/BLMPOP.ts | 6 +++ packages/client/lib/commands/BLPOP.ts | 6 +++ packages/client/lib/commands/BRPOP.ts | 6 +++ packages/client/lib/commands/BRPOPLPUSH.ts | 7 ++++ packages/client/lib/commands/BZMPOP.ts | 6 +++ packages/client/lib/commands/BZPOPMAX.ts | 6 +++ packages/client/lib/commands/BZPOPMIN.ts | 6 +++ .../client/lib/commands/CLIENT_CACHING.ts | 5 +++ .../client/lib/commands/CLIENT_GETNAME.ts | 4 ++ .../client/lib/commands/CLIENT_GETREDIR.ts | 4 ++ packages/client/lib/commands/CLIENT_ID.ts | 4 ++ packages/client/lib/commands/CLIENT_INFO.ts | 4 ++ packages/client/lib/commands/CLIENT_KILL.ts | 5 +++ packages/client/lib/commands/CLIENT_LIST.ts | 5 +++ .../client/lib/commands/CLIENT_NO-EVICT.ts | 5 +++ .../client/lib/commands/CLIENT_NO-TOUCH.ts | 5 +++ packages/client/lib/commands/CLIENT_PAUSE.ts | 6 +++ .../client/lib/commands/CLIENT_SETNAME.ts | 5 +++ .../client/lib/commands/CLIENT_TRACKING.ts | 6 +++ .../lib/commands/CLIENT_TRACKINGINFO.ts | 4 ++ .../client/lib/commands/CLIENT_UNPAUSE.ts | 4 ++ .../client/lib/commands/CLUSTER_ADDSLOTS.ts | 5 +++ .../lib/commands/CLUSTER_ADDSLOTSRANGE.ts | 5 +++ .../client/lib/commands/CLUSTER_BUMPEPOCH.ts | 4 ++ .../commands/CLUSTER_COUNT-FAILURE-REPORTS.ts | 5 +++ .../lib/commands/CLUSTER_COUNTKEYSINSLOT.ts | 5 +++ .../client/lib/commands/CLUSTER_DELSLOTS.ts | 5 +++ .../lib/commands/CLUSTER_DELSLOTSRANGE.ts | 5 +++ .../client/lib/commands/CLUSTER_FAILOVER.ts | 5 +++ .../client/lib/commands/CLUSTER_FLUSHSLOTS.ts | 4 ++ .../client/lib/commands/CLUSTER_FORGET.ts | 5 +++ .../lib/commands/CLUSTER_GETKEYSINSLOT.ts | 6 +++ packages/client/lib/commands/CLUSTER_INFO.ts | 4 ++ .../client/lib/commands/CLUSTER_KEYSLOT.ts | 5 +++ packages/client/lib/commands/CLUSTER_LINKS.ts | 4 ++ packages/client/lib/commands/CLUSTER_MEET.ts | 6 +++ packages/client/lib/commands/CLUSTER_MYID.ts | 4 ++ .../client/lib/commands/CLUSTER_MYSHARDID.ts | 4 ++ packages/client/lib/commands/CLUSTER_NODES.ts | 4 ++ .../client/lib/commands/CLUSTER_REPLICAS.ts | 5 +++ .../client/lib/commands/CLUSTER_REPLICATE.ts | 5 +++ packages/client/lib/commands/CLUSTER_RESET.ts | 5 +++ .../client/lib/commands/CLUSTER_SAVECONFIG.ts | 4 ++ .../lib/commands/CLUSTER_SET-CONFIG-EPOCH.ts | 5 +++ .../client/lib/commands/CLUSTER_SETSLOT.ts | 7 ++++ packages/client/lib/commands/CLUSTER_SLOTS.ts | 4 ++ packages/client/lib/commands/COMMAND.ts | 4 ++ packages/client/lib/commands/COMMAND_COUNT.ts | 4 ++ .../client/lib/commands/COMMAND_GETKEYS.ts | 5 +++ .../lib/commands/COMMAND_GETKEYSANDFLAGS.ts | 5 +++ packages/client/lib/commands/COMMAND_INFO.ts | 5 +++ packages/client/lib/commands/COMMAND_LIST.ts | 5 +++ packages/client/lib/commands/CONFIG_GET.ts | 5 +++ .../client/lib/commands/CONFIG_RESETSTAT.ts | 4 ++ .../client/lib/commands/CONFIG_REWRITE.ts | 4 ++ packages/client/lib/commands/CONFIG_SET.ts | 6 +++ packages/client/lib/commands/COPY.ts | 7 ++++ packages/client/lib/commands/DBSIZE.ts | 4 ++ packages/client/lib/commands/DECR.ts | 5 +++ packages/client/lib/commands/DECRBY.ts | 6 +++ packages/client/lib/commands/DEL.ts | 5 +++ packages/client/lib/commands/DISCARD.ts | 4 ++ packages/client/lib/commands/DUMP.ts | 5 +++ packages/client/lib/commands/ECHO.ts | 5 +++ packages/client/lib/commands/EVAL.ts | 6 +++ packages/client/lib/commands/EVALSHA.ts | 6 +++ packages/client/lib/commands/EVALSHA_RO.ts | 6 +++ packages/client/lib/commands/EVAL_RO.ts | 6 +++ packages/client/lib/commands/EXISTS.ts | 5 +++ packages/client/lib/commands/EXPIRE.ts | 7 ++++ packages/client/lib/commands/EXPIREAT.ts | 7 ++++ packages/client/lib/commands/EXPIRETIME.ts | 5 +++ packages/client/lib/commands/FAILOVER.ts | 5 +++ packages/client/lib/commands/FCALL.ts | 6 +++ packages/client/lib/commands/FCALL_RO.ts | 6 +++ packages/client/lib/commands/FLUSHALL.ts | 5 +++ packages/client/lib/commands/FLUSHDB.ts | 5 +++ .../client/lib/commands/FUNCTION_DELETE.ts | 5 +++ packages/client/lib/commands/FUNCTION_DUMP.ts | 4 ++ .../client/lib/commands/FUNCTION_FLUSH.ts | 5 +++ packages/client/lib/commands/FUNCTION_KILL.ts | 4 ++ packages/client/lib/commands/FUNCTION_LIST.ts | 5 +++ .../lib/commands/FUNCTION_LIST_WITHCODE.ts | 5 +++ packages/client/lib/commands/FUNCTION_LOAD.ts | 6 +++ .../client/lib/commands/FUNCTION_RESTORE.ts | 6 +++ .../client/lib/commands/FUNCTION_STATS.ts | 4 ++ packages/client/lib/commands/GEOADD.ts | 7 ++++ packages/client/lib/commands/GEODIST.ts | 8 ++++ packages/client/lib/commands/GEOHASH.ts | 6 +++ packages/client/lib/commands/GEOPOS.ts | 6 +++ packages/client/lib/commands/GEORADIUS.ts | 9 +++++ .../client/lib/commands/GEORADIUSBYMEMBER.ts | 9 +++++ .../lib/commands/GEORADIUSBYMEMBER_RO.ts | 9 +++++ .../lib/commands/GEORADIUSBYMEMBER_RO_WITH.ts | 9 +++++ .../lib/commands/GEORADIUSBYMEMBER_STORE.ts | 10 +++++ .../lib/commands/GEORADIUSBYMEMBER_WITH.ts | 10 +++++ packages/client/lib/commands/GEORADIUS_RO.ts | 9 +++++ .../client/lib/commands/GEORADIUS_RO_WITH.ts | 10 +++++ .../client/lib/commands/GEORADIUS_STORE.ts | 10 +++++ .../client/lib/commands/GEORADIUS_WITH.ts | 10 +++++ packages/client/lib/commands/GEOSEARCH.ts | 8 ++++ .../client/lib/commands/GEOSEARCHSTORE.ts | 9 +++++ .../client/lib/commands/GEOSEARCH_WITH.ts | 9 +++++ packages/client/lib/commands/GET.ts | 5 +++ packages/client/lib/commands/GETBIT.ts | 6 +++ packages/client/lib/commands/GETDEL.ts | 5 +++ packages/client/lib/commands/GETEX.ts | 6 +++ packages/client/lib/commands/GETRANGE.ts | 7 ++++ packages/client/lib/commands/GETSET.ts | 6 +++ packages/client/lib/commands/HDEL.ts | 6 +++ packages/client/lib/commands/HELLO.ts | 6 +++ packages/client/lib/commands/HEXISTS.ts | 6 +++ packages/client/lib/commands/HEXPIRE.ts | 8 ++++ packages/client/lib/commands/HEXPIREAT.ts | 8 ++++ packages/client/lib/commands/HEXPIRETIME.ts | 6 +++ packages/client/lib/commands/HGET.ts | 6 +++ packages/client/lib/commands/HGETALL.ts | 5 +++ packages/client/lib/commands/HGETDEL.ts | 6 +++ packages/client/lib/commands/HGETEX.ts | 7 ++++ packages/client/lib/commands/HINCRBY.ts | 7 ++++ packages/client/lib/commands/HINCRBYFLOAT.ts | 7 ++++ packages/client/lib/commands/HKEYS.ts | 5 +++ packages/client/lib/commands/HLEN.ts | 5 +++ packages/client/lib/commands/HMGET.ts | 6 +++ packages/client/lib/commands/HPERSIST.ts | 6 +++ packages/client/lib/commands/HPEXPIRE.ts | 9 +++++ packages/client/lib/commands/HPEXPIREAT.ts | 9 +++++ packages/client/lib/commands/HPEXPIRETIME.ts | 8 ++++ packages/client/lib/commands/HPTTL.ts | 8 ++++ packages/client/lib/commands/HRANDFIELD.ts | 7 ++++ .../client/lib/commands/HRANDFIELD_COUNT.ts | 8 ++++ .../commands/HRANDFIELD_COUNT_WITHVALUES.ts | 8 ++++ packages/client/lib/commands/HSCAN.ts | 9 +++++ .../client/lib/commands/HSCAN_NOVALUES.ts | 6 +++ packages/client/lib/commands/HSET.ts | 9 +++++ packages/client/lib/commands/HSETEX.ts | 9 +++++ packages/client/lib/commands/HSETNX.ts | 9 +++++ packages/client/lib/commands/HSTRLEN.ts | 8 ++++ packages/client/lib/commands/HTTL.ts | 6 +++ packages/client/lib/commands/HVALS.ts | 5 +++ packages/client/lib/commands/INCR.ts | 7 ++++ packages/client/lib/commands/INCRBY.ts | 8 ++++ packages/client/lib/commands/INCRBYFLOAT.ts | 8 ++++ packages/client/lib/commands/INFO.ts | 7 ++++ packages/client/lib/commands/KEYS.ts | 7 ++++ packages/client/lib/commands/LASTSAVE.ts | 6 +++ .../client/lib/commands/LATENCY_DOCTOR.ts | 6 +++ packages/client/lib/commands/LATENCY_GRAPH.ts | 7 ++++ .../client/lib/commands/LATENCY_HISTORY.ts | 7 ++++ .../client/lib/commands/LATENCY_LATEST.ts | 6 +++ packages/client/lib/commands/LCS.ts | 8 ++++ packages/client/lib/commands/LCS_IDX.ts | 9 +++++ .../lib/commands/LCS_IDX_WITHMATCHLEN.ts | 6 +++ packages/client/lib/commands/LCS_LEN.ts | 6 +++ packages/client/lib/commands/LINDEX.ts | 8 ++++ packages/client/lib/commands/LINSERT.ts | 10 +++++ packages/client/lib/commands/LLEN.ts | 7 ++++ packages/client/lib/commands/LMOVE.ts | 10 +++++ packages/client/lib/commands/LMPOP.ts | 7 ++++ packages/client/lib/commands/LOLWUT.ts | 8 ++++ packages/client/lib/commands/LPOP.ts | 7 ++++ packages/client/lib/commands/LPOP_COUNT.ts | 8 ++++ packages/client/lib/commands/LPOS.ts | 9 +++++ packages/client/lib/commands/LPOS_COUNT.ts | 10 +++++ packages/client/lib/commands/LPUSH.ts | 8 ++++ packages/client/lib/commands/LPUSHX.ts | 8 ++++ packages/client/lib/commands/LRANGE.ts | 9 +++++ packages/client/lib/commands/LREM.ts | 9 +++++ packages/client/lib/commands/LSET.ts | 9 +++++ packages/client/lib/commands/LTRIM.ts | 9 +++++ packages/client/lib/commands/MEMORY_DOCTOR.ts | 6 +++ .../lib/commands/MEMORY_MALLOC-STATS.ts | 6 +++ packages/client/lib/commands/MEMORY_PURGE.ts | 6 +++ packages/client/lib/commands/MEMORY_STATS.ts | 6 +++ packages/client/lib/commands/MEMORY_USAGE.ts | 8 ++++ packages/client/lib/commands/MGET.ts | 7 ++++ packages/client/lib/commands/MIGRATE.ts | 12 ++++++ packages/client/lib/commands/MODULE_LIST.ts | 6 +++ packages/client/lib/commands/MODULE_LOAD.ts | 8 ++++ packages/client/lib/commands/MODULE_UNLOAD.ts | 7 ++++ packages/client/lib/commands/MOVE.ts | 8 ++++ packages/client/lib/commands/MSET.ts | 7 ++++ packages/client/lib/commands/MSETNX.ts | 7 ++++ .../client/lib/commands/OBJECT_ENCODING.ts | 7 ++++ packages/client/lib/commands/OBJECT_FREQ.ts | 7 ++++ .../client/lib/commands/OBJECT_IDLETIME.ts | 7 ++++ .../client/lib/commands/OBJECT_REFCOUNT.ts | 7 ++++ packages/client/lib/commands/PERSIST.ts | 7 ++++ packages/client/lib/commands/PEXPIRE.ts | 9 +++++ packages/client/lib/commands/PEXPIREAT.ts | 9 +++++ packages/client/lib/commands/PEXPIRETIME.ts | 7 ++++ packages/client/lib/commands/PFADD.ts | 8 ++++ packages/client/lib/commands/PFCOUNT.ts | 7 ++++ packages/client/lib/commands/PFMERGE.ts | 8 ++++ packages/client/lib/commands/PING.ts | 7 ++++ packages/client/lib/commands/PSETEX.ts | 9 +++++ packages/client/lib/commands/PTTL.ts | 7 ++++ packages/client/lib/commands/PUBLISH.ts | 8 ++++ .../client/lib/commands/PUBSUB_CHANNELS.ts | 7 ++++ packages/client/lib/commands/PUBSUB_NUMPAT.ts | 6 +++ packages/client/lib/commands/PUBSUB_NUMSUB.ts | 13 ++++++ .../lib/commands/PUBSUB_SHARDCHANNELS.ts | 7 ++++ .../client/lib/commands/PUBSUB_SHARDNUMSUB.ts | 13 ++++++ packages/client/lib/commands/RANDOMKEY.ts | 6 +++ packages/client/lib/commands/READONLY.ts | 6 +++ packages/client/lib/commands/READWRITE.ts | 6 +++ packages/client/lib/commands/RENAME.ts | 8 ++++ packages/client/lib/commands/RENAMENX.ts | 8 ++++ packages/client/lib/commands/REPLICAOF.ts | 8 ++++ .../client/lib/commands/RESTORE-ASKING.ts | 6 +++ packages/client/lib/commands/RESTORE.ts | 18 +++++++++ packages/client/lib/commands/ROLE.ts | 24 +++++++++++ packages/client/lib/commands/RPOP.ts | 7 ++++ packages/client/lib/commands/RPOPLPUSH.ts | 8 ++++ packages/client/lib/commands/RPOP_COUNT.ts | 8 ++++ packages/client/lib/commands/RPUSH.ts | 8 ++++ packages/client/lib/commands/RPUSHX.ts | 8 ++++ packages/client/lib/commands/SADD.ts | 8 ++++ packages/client/lib/commands/SAVE.ts | 6 +++ packages/client/lib/commands/SCAN.ts | 40 +++++++++++++++++++ packages/client/lib/commands/SCARD.ts | 7 ++++ packages/client/lib/commands/SCRIPT_DEBUG.ts | 7 ++++ packages/client/lib/commands/SCRIPT_EXISTS.ts | 7 ++++ packages/client/lib/commands/SCRIPT_FLUSH.ts | 7 ++++ packages/client/lib/commands/SCRIPT_KILL.ts | 6 +++ packages/client/lib/commands/SCRIPT_LOAD.ts | 7 ++++ packages/client/lib/commands/SDIFF.ts | 7 ++++ packages/client/lib/commands/SDIFFSTORE.ts | 8 ++++ packages/client/lib/commands/SET.ts | 9 +++++ packages/client/lib/commands/SETBIT.ts | 9 +++++ packages/client/lib/commands/SETEX.ts | 9 +++++ packages/client/lib/commands/SETNX.ts | 8 ++++ packages/client/lib/commands/SETRANGE.ts | 9 +++++ packages/client/lib/commands/SHUTDOWN.ts | 15 +++++++ packages/client/lib/commands/SINTER.ts | 7 ++++ packages/client/lib/commands/SINTERCARD.ts | 14 ++++++- packages/client/lib/commands/SINTERSTORE.ts | 8 ++++ packages/client/lib/commands/SISMEMBER.ts | 8 ++++ packages/client/lib/commands/SMEMBERS.ts | 7 ++++ packages/client/lib/commands/SMISMEMBER.ts | 8 ++++ packages/client/lib/commands/SMOVE.ts | 9 +++++ packages/client/lib/commands/SORT.ts | 24 +++++++++++ packages/client/lib/commands/SORT_RO.ts | 4 ++ packages/client/lib/commands/SORT_STORE.ts | 7 ++++ packages/client/lib/commands/SPOP.ts | 7 ++++ packages/client/lib/commands/SPOP_COUNT.ts | 8 ++++ packages/client/lib/commands/SPUBLISH.ts | 8 ++++ packages/client/lib/commands/SRANDMEMBER.ts | 7 ++++ .../client/lib/commands/SRANDMEMBER_COUNT.ts | 8 ++++ packages/client/lib/commands/SREM.ts | 9 +++++ packages/client/lib/commands/SSCAN.ts | 17 ++++++++ packages/client/lib/commands/STRLEN.ts | 8 ++++ packages/client/lib/commands/SUNION.ts | 8 ++++ packages/client/lib/commands/SUNIONSTORE.ts | 9 +++++ packages/client/lib/commands/SWAPDB.ts | 6 +++ packages/client/lib/commands/TIME.ts | 7 ++++ packages/client/lib/commands/TOUCH.ts | 8 ++++ packages/client/lib/commands/TTL.ts | 8 ++++ packages/client/lib/commands/TYPE.ts | 8 ++++ packages/client/lib/commands/UNLINK.ts | 8 ++++ packages/client/lib/commands/WAIT.ts | 9 +++++ packages/client/lib/commands/XACK.ts | 10 +++++ packages/client/lib/commands/XADD.ts | 30 ++++++++++++++ .../client/lib/commands/XADD_NOMKSTREAM.ts | 10 +++++ packages/client/lib/commands/XAUTOCLAIM.ts | 33 +++++++++++++++ .../client/lib/commands/XAUTOCLAIM_JUSTID.ts | 20 ++++++++++ packages/client/lib/commands/XCLAIM.ts | 30 ++++++++++++++ packages/client/lib/commands/XCLAIM_JUSTID.ts | 15 +++++++ packages/client/lib/commands/XDEL.ts | 12 ++++++ packages/client/lib/commands/XGROUP_CREATE.ts | 17 ++++++++ .../lib/commands/XGROUP_CREATECONSUMER.ts | 13 ++++++ .../client/lib/commands/XGROUP_DELCONSUMER.ts | 13 ++++++ .../client/lib/commands/XGROUP_DESTROY.ts | 12 ++++++ packages/client/lib/commands/XGROUP_SETID.ts | 16 ++++++++ .../client/lib/commands/XINFO_CONSUMERS.ts | 23 +++++++++++ packages/client/lib/commands/XINFO_GROUPS.ts | 23 +++++++++++ packages/client/lib/commands/XINFO_STREAM.ts | 31 ++++++++++++++ packages/client/lib/commands/XLEN.ts | 11 +++++ packages/client/lib/commands/XPENDING.ts | 23 +++++++++++ .../client/lib/commands/XPENDING_RANGE.ts | 33 +++++++++++++++ packages/client/lib/commands/XRANGE.ts | 30 ++++++++++++++ packages/client/lib/commands/XREAD.ts | 30 ++++++++++++++ packages/client/lib/commands/XREADGROUP.ts | 21 ++++++++++ packages/client/lib/commands/XREVRANGE.ts | 17 ++++++++ packages/client/lib/commands/XTRIM.ts | 20 ++++++++++ packages/client/lib/commands/ZADD.ts | 28 +++++++++++++ packages/client/lib/commands/ZADD_INCR.ts | 20 ++++++++++ packages/client/lib/commands/ZCARD.ts | 11 +++++ packages/client/lib/commands/ZCOUNT.ts | 7 ++++ packages/client/lib/commands/ZDIFF.ts | 5 +++ packages/client/lib/commands/ZDIFFSTORE.ts | 6 +++ .../client/lib/commands/ZDIFF_WITHSCORES.ts | 5 +++ packages/client/lib/commands/ZINCRBY.ts | 7 ++++ packages/client/lib/commands/ZINTER.ts | 6 +++ packages/client/lib/commands/ZINTERCARD.ts | 6 +++ packages/client/lib/commands/ZINTERSTORE.ts | 7 ++++ .../client/lib/commands/ZINTER_WITHSCORES.ts | 4 ++ packages/client/lib/commands/ZLEXCOUNT.ts | 7 ++++ packages/client/lib/commands/ZMPOP.ts | 7 ++++ packages/client/lib/commands/ZMSCORE.ts | 6 +++ packages/client/lib/commands/ZPOPMAX.ts | 5 +++ packages/client/lib/commands/ZPOPMAX_COUNT.ts | 6 +++ packages/client/lib/commands/ZPOPMIN.ts | 5 +++ packages/client/lib/commands/ZPOPMIN_COUNT.ts | 6 +++ packages/client/lib/commands/ZRANDMEMBER.ts | 5 +++ .../client/lib/commands/ZRANDMEMBER_COUNT.ts | 6 +++ .../commands/ZRANDMEMBER_COUNT_WITHSCORES.ts | 6 +++ packages/client/lib/commands/ZRANGE.ts | 8 ++++ packages/client/lib/commands/ZRANGEBYLEX.ts | 8 ++++ packages/client/lib/commands/ZRANGEBYSCORE.ts | 8 ++++ .../lib/commands/ZRANGEBYSCORE_WITHSCORES.ts | 4 ++ packages/client/lib/commands/ZRANGESTORE.ts | 9 +++++ .../client/lib/commands/ZRANGE_WITHSCORES.ts | 4 ++ packages/client/lib/commands/ZRANK.ts | 6 +++ .../client/lib/commands/ZRANK_WITHSCORE.ts | 4 ++ packages/client/lib/commands/ZREM.ts | 6 +++ .../client/lib/commands/ZREMRANGEBYLEX.ts | 7 ++++ .../client/lib/commands/ZREMRANGEBYRANK.ts | 7 ++++ .../client/lib/commands/ZREMRANGEBYSCORE.ts | 7 ++++ packages/client/lib/commands/ZREVRANK.ts | 6 +++ packages/client/lib/commands/ZSCAN.ts | 7 ++++ packages/client/lib/commands/ZSCORE.ts | 6 +++ packages/client/lib/commands/ZUNION.ts | 6 +++ packages/client/lib/commands/ZUNIONSTORE.ts | 7 ++++ .../client/lib/commands/ZUNION_WITHSCORES.ts | 4 ++ .../lib/sentinel/commands/SENTINEL_MASTER.ts | 5 +++ .../lib/sentinel/commands/SENTINEL_MONITOR.ts | 8 ++++ .../sentinel/commands/SENTINEL_REPLICAS.ts | 5 +++ .../sentinel/commands/SENTINEL_SENTINELS.ts | 5 +++ .../lib/sentinel/commands/SENTINEL_SET.ts | 6 +++ packages/json/lib/commands/ARRAPPEND.ts | 10 +++++ packages/json/lib/commands/ARRINDEX.ts | 12 ++++++ packages/json/lib/commands/ARRINSERT.ts | 11 +++++ packages/json/lib/commands/ARRLEN.ts | 9 +++++ packages/json/lib/commands/ARRPOP.ts | 10 +++++ packages/json/lib/commands/ARRTRIM.ts | 10 +++++ packages/json/lib/commands/CLEAR.ts | 9 +++++ packages/json/lib/commands/DEBUG_MEMORY.ts | 9 +++++ packages/json/lib/commands/DEL.ts | 9 +++++ packages/json/lib/commands/FORGET.ts | 9 +++++ packages/json/lib/commands/GET.ts | 9 +++++ packages/json/lib/commands/MERGE.ts | 9 +++++ packages/json/lib/commands/MGET.ts | 8 ++++ packages/json/lib/commands/MSET.ts | 10 +++++ packages/json/lib/commands/NUMINCRBY.ts | 9 +++++ packages/json/lib/commands/NUMMULTBY.ts | 9 +++++ packages/json/lib/commands/OBJKEYS.ts | 9 +++++ packages/json/lib/commands/OBJLEN.ts | 9 +++++ packages/json/lib/commands/RESP.ts | 8 ++++ packages/json/lib/commands/SET.ts | 13 ++++++ packages/json/lib/commands/STRAPPEND.ts | 10 +++++ packages/json/lib/commands/STRLEN.ts | 9 +++++ packages/json/lib/commands/TOGGLE.ts | 8 ++++ packages/json/lib/commands/TYPE.ts | 9 +++++ packages/search/lib/commands/AGGREGATE.ts | 12 ++++++ .../lib/commands/AGGREGATE_WITHCURSOR.ts | 10 +++++ packages/search/lib/commands/ALIASADD.ts | 6 +++ packages/search/lib/commands/ALIASDEL.ts | 5 +++ packages/search/lib/commands/ALIASUPDATE.ts | 6 +++ packages/search/lib/commands/ALTER.ts | 6 +++ packages/search/lib/commands/CONFIG_GET.ts | 5 +++ packages/search/lib/commands/CONFIG_SET.ts | 6 +++ packages/search/lib/commands/CREATE.ts | 16 ++++++++ packages/search/lib/commands/CURSOR_DEL.ts | 6 +++ packages/search/lib/commands/CURSOR_READ.ts | 8 ++++ packages/search/lib/commands/DICTADD.ts | 6 +++ packages/search/lib/commands/DICTDEL.ts | 6 +++ packages/search/lib/commands/DICTDUMP.ts | 5 +++ packages/search/lib/commands/DROPINDEX.ts | 7 ++++ packages/search/lib/commands/EXPLAIN.ts | 9 +++++ packages/search/lib/commands/EXPLAINCLI.ts | 8 ++++ packages/search/lib/commands/INFO.ts | 5 +++ .../search/lib/commands/PROFILE_AGGREGATE.ts | 9 +++++ .../search/lib/commands/PROFILE_SEARCH.ts | 9 +++++ packages/search/lib/commands/SEARCH.ts | 15 +++++++ .../search/lib/commands/SEARCH_NOCONTENT.ts | 8 ++++ packages/search/lib/commands/SPELLCHECK.ts | 10 +++++ packages/search/lib/commands/SUGADD.ts | 10 +++++ packages/search/lib/commands/SUGDEL.ts | 6 +++ packages/search/lib/commands/SUGGET.ts | 9 +++++ .../lib/commands/SUGGET_WITHPAYLOADS.ts | 8 ++++ .../search/lib/commands/SUGGET_WITHSCORES.ts | 8 ++++ .../SUGGET_WITHSCORES_WITHPAYLOADS.ts | 8 ++++ packages/search/lib/commands/SUGLEN.ts | 5 +++ packages/search/lib/commands/SYNDUMP.ts | 5 +++ packages/search/lib/commands/SYNUPDATE.ts | 9 +++++ packages/search/lib/commands/TAGVALS.ts | 6 +++ packages/search/lib/commands/_LIST.ts | 4 ++ packages/time-series/lib/commands/ADD.ts | 8 ++++ packages/time-series/lib/commands/ALTER.ts | 6 +++ packages/time-series/lib/commands/CREATE.ts | 6 +++ .../time-series/lib/commands/CREATERULE.ts | 9 +++++ packages/time-series/lib/commands/DECRBY.ts | 4 ++ packages/time-series/lib/commands/DEL.ts | 7 ++++ .../time-series/lib/commands/DELETERULE.ts | 6 +++ packages/time-series/lib/commands/GET.ts | 6 +++ packages/time-series/lib/commands/INCRBY.ts | 11 +++++ packages/time-series/lib/commands/INFO.ts | 5 +++ .../time-series/lib/commands/INFO_DEBUG.ts | 5 +++ packages/time-series/lib/commands/MADD.ts | 5 +++ packages/time-series/lib/commands/MGET.ts | 16 ++++++++ .../lib/commands/MGET_SELECTED_LABELS.ts | 7 ++++ .../lib/commands/MGET_WITHLABELS.ts | 6 +++ packages/time-series/lib/commands/MRANGE.ts | 12 ++++++ .../lib/commands/MRANGE_GROUPBY.ts | 22 ++++++++++ .../lib/commands/MRANGE_SELECTED_LABELS.ts | 13 ++++++ .../MRANGE_SELECTED_LABELS_GROUPBY.ts | 14 +++++++ .../lib/commands/MRANGE_WITHLABELS.ts | 12 ++++++ .../lib/commands/MRANGE_WITHLABELS_GROUPBY.ts | 9 +++++ .../time-series/lib/commands/MREVRANGE.ts | 8 ++++ .../lib/commands/MREVRANGE_GROUPBY.ts | 9 +++++ .../lib/commands/MREVRANGE_SELECTED_LABELS.ts | 9 +++++ .../MREVRANGE_SELECTED_LABELS_GROUPBY.ts | 10 +++++ .../lib/commands/MREVRANGE_WITHLABELS.ts | 8 ++++ .../commands/MREVRANGE_WITHLABELS_GROUPBY.ts | 9 +++++ .../time-series/lib/commands/QUERYINDEX.ts | 5 +++ packages/time-series/lib/commands/RANGE.ts | 4 ++ packages/time-series/lib/commands/REVRANGE.ts | 4 ++ 491 files changed, 3861 insertions(+), 1 deletion(-) diff --git a/packages/bloom/lib/commands/bloom/ADD.ts b/packages/bloom/lib/commands/bloom/ADD.ts index e12d9cfa1d2..bf976606997 100644 --- a/packages/bloom/lib/commands/bloom/ADD.ts +++ b/packages/bloom/lib/commands/bloom/ADD.ts @@ -4,6 +4,12 @@ import { transformBooleanReply } from '@redis/client/dist/lib/commands/generic-t export default { IS_READ_ONLY: false, + /** + * Adds an item to a Bloom Filter + * @param parser - The command parser + * @param key - The name of the Bloom filter + * @param item - The item to add to the filter + */ parseCommand(parser: CommandParser, key: RedisArgument, item: RedisArgument) { parser.push('BF.ADD'); parser.pushKey(key); diff --git a/packages/bloom/lib/commands/bloom/CARD.ts b/packages/bloom/lib/commands/bloom/CARD.ts index c2f9aeb00fc..e1873e1faf1 100644 --- a/packages/bloom/lib/commands/bloom/CARD.ts +++ b/packages/bloom/lib/commands/bloom/CARD.ts @@ -3,6 +3,11 @@ import { RedisArgument, NumberReply, Command } from '@redis/client/dist/lib/RESP export default { IS_READ_ONLY: true, + /** + * Returns the cardinality (number of items) in a Bloom Filter + * @param parser - The command parser + * @param key - The name of the Bloom filter to query + */ parseCommand(parser: CommandParser, key: RedisArgument) { parser.push('BF.CARD'); parser.pushKey(key); diff --git a/packages/bloom/lib/commands/bloom/EXISTS.ts b/packages/bloom/lib/commands/bloom/EXISTS.ts index b3f19af9516..db16253e2c7 100644 --- a/packages/bloom/lib/commands/bloom/EXISTS.ts +++ b/packages/bloom/lib/commands/bloom/EXISTS.ts @@ -4,6 +4,12 @@ import { transformBooleanReply } from '@redis/client/dist/lib/commands/generic-t export default { IS_READ_ONLY: true, + /** + * Checks if an item exists in a Bloom Filter + * @param parser - The command parser + * @param key - The name of the Bloom filter + * @param item - The item to check for existence + */ parseCommand(parser: CommandParser, key: RedisArgument, item: RedisArgument) { parser.push('BF.EXISTS'); parser.pushKey(key); diff --git a/packages/bloom/lib/commands/bloom/INFO.ts b/packages/bloom/lib/commands/bloom/INFO.ts index 7074885a41e..bdf7d0fda9b 100644 --- a/packages/bloom/lib/commands/bloom/INFO.ts +++ b/packages/bloom/lib/commands/bloom/INFO.ts @@ -12,6 +12,11 @@ export type BfInfoReplyMap = TuplesToMapReply<[ export default { IS_READ_ONLY: true, + /** + * Returns information about a Bloom Filter, including capacity, size, number of filters, items inserted, and expansion rate + * @param parser - The command parser + * @param key - The name of the Bloom filter to get information about + */ parseCommand(parser: CommandParser, key: RedisArgument) { parser.push('BF.INFO'); parser.pushKey(key); diff --git a/packages/bloom/lib/commands/bloom/INSERT.ts b/packages/bloom/lib/commands/bloom/INSERT.ts index b8dcef325f8..c607e015694 100644 --- a/packages/bloom/lib/commands/bloom/INSERT.ts +++ b/packages/bloom/lib/commands/bloom/INSERT.ts @@ -13,6 +13,18 @@ export interface BfInsertOptions { export default { IS_READ_ONLY: false, + /** + * Adds one or more items to a Bloom Filter, creating it if it does not exist + * @param parser - The command parser + * @param key - The name of the Bloom filter + * @param items - One or more items to add to the filter + * @param options - Optional parameters for filter creation + * @param options.CAPACITY - Desired capacity for a new filter + * @param options.ERROR - Desired error rate for a new filter + * @param options.EXPANSION - Expansion rate for a new filter + * @param options.NOCREATE - If true, prevents automatic filter creation + * @param options.NONSCALING - Prevents the filter from creating additional sub-filters + */ parseCommand( parser: CommandParser, key: RedisArgument, diff --git a/packages/bloom/lib/commands/bloom/LOADCHUNK.ts b/packages/bloom/lib/commands/bloom/LOADCHUNK.ts index ef3cc4a3e12..d0af9d7a644 100644 --- a/packages/bloom/lib/commands/bloom/LOADCHUNK.ts +++ b/packages/bloom/lib/commands/bloom/LOADCHUNK.ts @@ -3,6 +3,13 @@ import { RedisArgument, SimpleStringReply, Command } from '@redis/client/dist/li export default { IS_READ_ONLY: false, + /** + * Restores a Bloom Filter chunk previously saved using SCANDUMP + * @param parser - The command parser + * @param key - The name of the Bloom filter to restore + * @param iterator - Iterator value from the SCANDUMP command + * @param chunk - Data chunk from the SCANDUMP command + */ parseCommand(parser: CommandParser, key: RedisArgument, iterator: number, chunk: RedisArgument) { parser.push('BF.LOADCHUNK'); parser.pushKey(key); diff --git a/packages/bloom/lib/commands/bloom/MADD.ts b/packages/bloom/lib/commands/bloom/MADD.ts index efbd932b403..adef0aee3e2 100644 --- a/packages/bloom/lib/commands/bloom/MADD.ts +++ b/packages/bloom/lib/commands/bloom/MADD.ts @@ -5,6 +5,12 @@ import { transformBooleanArrayReply } from '@redis/client/dist/lib/commands/gene export default { IS_READ_ONLY: false, + /** + * Adds multiple items to a Bloom Filter in a single call + * @param parser - The command parser + * @param key - The name of the Bloom filter + * @param items - One or more items to add to the filter + */ parseCommand(parser: CommandParser, key: RedisArgument, items: RedisVariadicArgument) { parser.push('BF.MADD'); parser.pushKey(key); diff --git a/packages/bloom/lib/commands/bloom/MEXISTS.ts b/packages/bloom/lib/commands/bloom/MEXISTS.ts index a5a311a8e4c..658f9b01229 100644 --- a/packages/bloom/lib/commands/bloom/MEXISTS.ts +++ b/packages/bloom/lib/commands/bloom/MEXISTS.ts @@ -5,6 +5,12 @@ import { transformBooleanArrayReply } from '@redis/client/dist/lib/commands/gene export default { IS_READ_ONLY: true, + /** + * Checks if multiple items exist in a Bloom Filter in a single call + * @param parser - The command parser + * @param key - The name of the Bloom filter + * @param items - One or more items to check for existence + */ parseCommand(parser: CommandParser, key: RedisArgument, items: RedisVariadicArgument) { parser.push('BF.MEXISTS'); parser.pushKey(key); diff --git a/packages/bloom/lib/commands/bloom/RESERVE.ts b/packages/bloom/lib/commands/bloom/RESERVE.ts index 00f17c1889f..d0b3fb906a9 100644 --- a/packages/bloom/lib/commands/bloom/RESERVE.ts +++ b/packages/bloom/lib/commands/bloom/RESERVE.ts @@ -8,6 +8,16 @@ export interface BfReserveOptions { export default { IS_READ_ONLY: true, + /** + * Creates an empty Bloom Filter with a given desired error ratio and initial capacity + * @param parser - The command parser + * @param key - The name of the Bloom filter to create + * @param errorRate - The desired probability for false positives (between 0 and 1) + * @param capacity - The number of entries intended to be added to the filter + * @param options - Optional parameters to tune the filter + * @param options.EXPANSION - Expansion rate for the filter + * @param options.NONSCALING - Prevents the filter from creating additional sub-filters + */ parseCommand( parser: CommandParser, key: RedisArgument, diff --git a/packages/bloom/lib/commands/bloom/SCANDUMP.ts b/packages/bloom/lib/commands/bloom/SCANDUMP.ts index d0472b649c5..4aebc5c9fc3 100644 --- a/packages/bloom/lib/commands/bloom/SCANDUMP.ts +++ b/packages/bloom/lib/commands/bloom/SCANDUMP.ts @@ -3,6 +3,12 @@ import { RedisArgument, TuplesReply, NumberReply, BlobStringReply, UnwrapReply, export default { IS_READ_ONLY: true, + /** + * Begins an incremental save of a Bloom Filter. This is useful for large filters that can't be saved at once + * @param parser - The command parser + * @param key - The name of the Bloom filter to save + * @param iterator - Iterator value; Start at 0, and use the iterator from the response for the next chunk + */ parseCommand(parser: CommandParser, key: RedisArgument, iterator: number) { parser.push('BF.SCANDUMP'); parser.pushKey(key); diff --git a/packages/bloom/lib/commands/count-min-sketch/INCRBY.ts b/packages/bloom/lib/commands/count-min-sketch/INCRBY.ts index a011957ece6..145047b207a 100644 --- a/packages/bloom/lib/commands/count-min-sketch/INCRBY.ts +++ b/packages/bloom/lib/commands/count-min-sketch/INCRBY.ts @@ -8,6 +8,12 @@ export interface BfIncrByItem { export default { IS_READ_ONLY: false, + /** + * Increases the count of one or more items in a Count-Min Sketch + * @param parser - The command parser + * @param key - The name of the sketch + * @param items - A single item or array of items to increment, each with an item and increment value + */ parseCommand( parser: CommandParser, key: RedisArgument, diff --git a/packages/bloom/lib/commands/count-min-sketch/INFO.ts b/packages/bloom/lib/commands/count-min-sketch/INFO.ts index fef1cac97e7..1f188bda013 100644 --- a/packages/bloom/lib/commands/count-min-sketch/INFO.ts +++ b/packages/bloom/lib/commands/count-min-sketch/INFO.ts @@ -16,6 +16,11 @@ export interface CmsInfoReply { export default { IS_READ_ONLY: true, + /** + * Returns width, depth, and total count of items in a Count-Min Sketch + * @param parser - The command parser + * @param key - The name of the sketch to get information about + */ parseCommand(parser: CommandParser, key: RedisArgument) { parser.push('CMS.INFO'); parser.pushKey(key); diff --git a/packages/bloom/lib/commands/count-min-sketch/INITBYDIM.ts b/packages/bloom/lib/commands/count-min-sketch/INITBYDIM.ts index 44e6a75952f..2bf9d97f208 100644 --- a/packages/bloom/lib/commands/count-min-sketch/INITBYDIM.ts +++ b/packages/bloom/lib/commands/count-min-sketch/INITBYDIM.ts @@ -3,6 +3,13 @@ import { RedisArgument, SimpleStringReply, Command } from '@redis/client/dist/li export default { IS_READ_ONLY: false, + /** + * Initialize a Count-Min Sketch using width and depth parameters + * @param parser - The command parser + * @param key - The name of the sketch + * @param width - Number of counters in each array (must be a multiple of 2) + * @param depth - Number of counter arrays (determines accuracy of estimates) + */ parseCommand(parser: CommandParser, key: RedisArgument, width: number, depth: number) { parser.push('CMS.INITBYDIM'); parser.pushKey(key); diff --git a/packages/bloom/lib/commands/count-min-sketch/INITBYPROB.ts b/packages/bloom/lib/commands/count-min-sketch/INITBYPROB.ts index 3b96120bd04..180781d91e8 100644 --- a/packages/bloom/lib/commands/count-min-sketch/INITBYPROB.ts +++ b/packages/bloom/lib/commands/count-min-sketch/INITBYPROB.ts @@ -3,6 +3,13 @@ import { RedisArgument, SimpleStringReply, Command } from '@redis/client/dist/li export default { IS_READ_ONLY: false, + /** + * Initialize a Count-Min Sketch using error rate and probability parameters + * @param parser - The command parser + * @param key - The name of the sketch + * @param error - Estimate error, as a decimal between 0 and 1 + * @param probability - The desired probability for inflated count, as a decimal between 0 and 1 + */ parseCommand(parser: CommandParser, key: RedisArgument, error: number, probability: number) { parser.push('CMS.INITBYPROB'); parser.pushKey(key); diff --git a/packages/bloom/lib/commands/count-min-sketch/MERGE.ts b/packages/bloom/lib/commands/count-min-sketch/MERGE.ts index 4d959bd619d..e4921c7975f 100644 --- a/packages/bloom/lib/commands/count-min-sketch/MERGE.ts +++ b/packages/bloom/lib/commands/count-min-sketch/MERGE.ts @@ -10,6 +10,12 @@ export type BfMergeSketches = Array | Array; export default { IS_READ_ONLY: false, + /** + * Merges multiple Count-Min Sketches into a single sketch, with optional weights + * @param parser - The command parser + * @param destination - The name of the destination sketch + * @param source - Array of sketch names or array of sketches with weights + */ parseCommand( parser: CommandParser, destination: RedisArgument, diff --git a/packages/bloom/lib/commands/count-min-sketch/QUERY.ts b/packages/bloom/lib/commands/count-min-sketch/QUERY.ts index b55b51d1bbd..4322b0470c0 100644 --- a/packages/bloom/lib/commands/count-min-sketch/QUERY.ts +++ b/packages/bloom/lib/commands/count-min-sketch/QUERY.ts @@ -4,6 +4,12 @@ import { RedisVariadicArgument } from '@redis/client/dist/lib/commands/generic-t export default { IS_READ_ONLY: true, + /** + * Returns the count for one or more items in a Count-Min Sketch + * @param parser - The command parser + * @param key - The name of the sketch + * @param items - One or more items to get counts for + */ parseCommand(parser: CommandParser, key: RedisArgument, items: RedisVariadicArgument) { parser.push('CMS.QUERY'); parser.pushKey(key); diff --git a/packages/bloom/lib/commands/cuckoo/ADD.ts b/packages/bloom/lib/commands/cuckoo/ADD.ts index 37a5d1b5b86..db16acdea5a 100644 --- a/packages/bloom/lib/commands/cuckoo/ADD.ts +++ b/packages/bloom/lib/commands/cuckoo/ADD.ts @@ -4,6 +4,12 @@ import { transformBooleanReply } from '@redis/client/dist/lib/commands/generic-t export default { IS_READ_ONLY: false, + /** + * Adds an item to a Cuckoo Filter, creating the filter if it does not exist + * @param parser - The command parser + * @param key - The name of the Cuckoo filter + * @param item - The item to add to the filter + */ parseCommand(parser: CommandParser, key: RedisArgument, item: RedisArgument) { parser.push('CF.ADD'); parser.pushKey(key); diff --git a/packages/bloom/lib/commands/cuckoo/ADDNX.ts b/packages/bloom/lib/commands/cuckoo/ADDNX.ts index ceaf62be21c..ef6e1222e7f 100644 --- a/packages/bloom/lib/commands/cuckoo/ADDNX.ts +++ b/packages/bloom/lib/commands/cuckoo/ADDNX.ts @@ -4,6 +4,12 @@ import { transformBooleanReply } from '@redis/client/dist/lib/commands/generic-t export default { IS_READ_ONLY: false, + /** + * Adds an item to a Cuckoo Filter only if it does not exist + * @param parser - The command parser + * @param key - The name of the Cuckoo filter + * @param item - The item to add to the filter if it doesn't exist + */ parseCommand(parser: CommandParser, key: RedisArgument, item: RedisArgument) { parser.push('CF.ADDNX'); parser.pushKey(key); diff --git a/packages/bloom/lib/commands/cuckoo/COUNT.ts b/packages/bloom/lib/commands/cuckoo/COUNT.ts index f0cd5a72105..e06d71f73e9 100644 --- a/packages/bloom/lib/commands/cuckoo/COUNT.ts +++ b/packages/bloom/lib/commands/cuckoo/COUNT.ts @@ -3,6 +3,12 @@ import { RedisArgument, NumberReply, Command } from '@redis/client/dist/lib/RESP export default { IS_READ_ONLY: true, + /** + * Returns the number of times an item appears in a Cuckoo Filter + * @param parser - The command parser + * @param key - The name of the Cuckoo filter + * @param item - The item to count occurrences of + */ parseCommand(parser: CommandParser, key: RedisArgument, item: RedisArgument) { parser.push('CF.COUNT'); parser.pushKey(key); diff --git a/packages/bloom/lib/commands/cuckoo/DEL.ts b/packages/bloom/lib/commands/cuckoo/DEL.ts index c97b7c2d9fc..651a5bd8a8a 100644 --- a/packages/bloom/lib/commands/cuckoo/DEL.ts +++ b/packages/bloom/lib/commands/cuckoo/DEL.ts @@ -4,6 +4,12 @@ import { transformBooleanReply } from '@redis/client/dist/lib/commands/generic-t export default { IS_READ_ONLY: false, + /** + * Removes an item from a Cuckoo Filter if it exists + * @param parser - The command parser + * @param key - The name of the Cuckoo filter + * @param item - The item to remove from the filter + */ parseCommand(parser: CommandParser, key: RedisArgument, item: RedisArgument) { parser.push('CF.DEL'); parser.pushKey(key); diff --git a/packages/bloom/lib/commands/cuckoo/EXISTS.ts b/packages/bloom/lib/commands/cuckoo/EXISTS.ts index 2299cb3de99..820143ae886 100644 --- a/packages/bloom/lib/commands/cuckoo/EXISTS.ts +++ b/packages/bloom/lib/commands/cuckoo/EXISTS.ts @@ -4,6 +4,12 @@ import { transformBooleanReply } from '@redis/client/dist/lib/commands/generic-t export default { IS_READ_ONLY: false, + /** + * Checks if an item exists in a Cuckoo Filter + * @param parser - The command parser + * @param key - The name of the Cuckoo filter + * @param item - The item to check for existence + */ parseCommand(parser: CommandParser, key: RedisArgument, item: RedisArgument) { parser.push('CF.EXISTS'); parser.pushKey(key); diff --git a/packages/bloom/lib/commands/cuckoo/INFO.ts b/packages/bloom/lib/commands/cuckoo/INFO.ts index 6a8f06f1e77..88622b5cb19 100644 --- a/packages/bloom/lib/commands/cuckoo/INFO.ts +++ b/packages/bloom/lib/commands/cuckoo/INFO.ts @@ -15,6 +15,11 @@ export type CfInfoReplyMap = TuplesToMapReply<[ export default { IS_READ_ONLY: true, + /** + * Returns detailed information about a Cuckoo Filter including size, buckets, filters count, items statistics and configuration + * @param parser - The command parser + * @param key - The name of the Cuckoo filter to get information about + */ parseCommand(parser: CommandParser, key: RedisArgument) { parser.push('CF.INFO'); parser.pushKey(key); diff --git a/packages/bloom/lib/commands/cuckoo/INSERT.ts b/packages/bloom/lib/commands/cuckoo/INSERT.ts index 3ad3feee16d..277c820cbcd 100644 --- a/packages/bloom/lib/commands/cuckoo/INSERT.ts +++ b/packages/bloom/lib/commands/cuckoo/INSERT.ts @@ -29,6 +29,15 @@ export function parseCfInsertArguments( export default { IS_READ_ONLY: false, + /** + * Adds one or more items to a Cuckoo Filter, creating it if it does not exist + * @param parser - The command parser + * @param key - The name of the Cuckoo filter + * @param items - One or more items to add to the filter + * @param options - Optional parameters for filter creation + * @param options.CAPACITY - The number of entries intended to be added to the filter + * @param options.NOCREATE - If true, prevents automatic filter creation + */ parseCommand(...args: Parameters) { args[0].push('CF.INSERT'); parseCfInsertArguments(...args); diff --git a/packages/bloom/lib/commands/cuckoo/INSERTNX.ts b/packages/bloom/lib/commands/cuckoo/INSERTNX.ts index 7ddc952e2fa..bf99db6c3f7 100644 --- a/packages/bloom/lib/commands/cuckoo/INSERTNX.ts +++ b/packages/bloom/lib/commands/cuckoo/INSERTNX.ts @@ -1,6 +1,15 @@ import { Command } from '@redis/client/dist/lib/RESP/types'; import INSERT, { parseCfInsertArguments } from './INSERT'; +/** + * Adds one or more items to a Cuckoo Filter only if they do not exist yet, creating the filter if needed + * @param parser - The command parser + * @param key - The name of the Cuckoo filter + * @param items - One or more items to add to the filter + * @param options - Optional parameters for filter creation + * @param options.CAPACITY - The number of entries intended to be added to the filter + * @param options.NOCREATE - If true, prevents automatic filter creation + */ export default { IS_READ_ONLY: INSERT.IS_READ_ONLY, parseCommand(...args: Parameters) { diff --git a/packages/bloom/lib/commands/cuckoo/LOADCHUNK.ts b/packages/bloom/lib/commands/cuckoo/LOADCHUNK.ts index 8fb21be8e0d..3a966e5145a 100644 --- a/packages/bloom/lib/commands/cuckoo/LOADCHUNK.ts +++ b/packages/bloom/lib/commands/cuckoo/LOADCHUNK.ts @@ -3,6 +3,13 @@ import { SimpleStringReply, Command, RedisArgument } from '@redis/client/dist/li export default { IS_READ_ONLY: false, + /** + * Restores a Cuckoo Filter chunk previously saved using SCANDUMP + * @param parser - The command parser + * @param key - The name of the Cuckoo filter to restore + * @param iterator - Iterator value from the SCANDUMP command + * @param chunk - Data chunk from the SCANDUMP command + */ parseCommand(parser: CommandParser, key: RedisArgument, iterator: number, chunk: RedisArgument) { parser.push('CF.LOADCHUNK'); parser.pushKey(key); diff --git a/packages/bloom/lib/commands/cuckoo/RESERVE.ts b/packages/bloom/lib/commands/cuckoo/RESERVE.ts index 2685b0db06d..26e31a1c645 100644 --- a/packages/bloom/lib/commands/cuckoo/RESERVE.ts +++ b/packages/bloom/lib/commands/cuckoo/RESERVE.ts @@ -9,6 +9,16 @@ export interface CfReserveOptions { export default { IS_READ_ONLY: false, + /** + * Creates an empty Cuckoo Filter with specified capacity and parameters + * @param parser - The command parser + * @param key - The name of the Cuckoo filter to create + * @param capacity - The number of entries intended to be added to the filter + * @param options - Optional parameters to tune the filter + * @param options.BUCKETSIZE - Number of items in each bucket + * @param options.MAXITERATIONS - Maximum number of iterations before declaring filter full + * @param options.EXPANSION - Number of additional buckets per expansion + */ parseCommand( parser: CommandParser, key: RedisArgument, diff --git a/packages/bloom/lib/commands/cuckoo/SCANDUMP.ts b/packages/bloom/lib/commands/cuckoo/SCANDUMP.ts index 25ef2c3f6da..96a036671f4 100644 --- a/packages/bloom/lib/commands/cuckoo/SCANDUMP.ts +++ b/packages/bloom/lib/commands/cuckoo/SCANDUMP.ts @@ -3,6 +3,12 @@ import { RedisArgument, TuplesReply, NumberReply, BlobStringReply, NullReply, Un export default { IS_READ_ONLY: true, + /** + * Begins an incremental save of a Cuckoo Filter. This is useful for large filters that can't be saved at once + * @param parser - The command parser + * @param key - The name of the Cuckoo filter to save + * @param iterator - Iterator value; Start at 0, and use the iterator from the response for the next chunk + */ parseCommand(parser: CommandParser, key: RedisArgument, iterator: number) { parser.push('CF.SCANDUMP'); parser.pushKey(key); diff --git a/packages/bloom/lib/commands/t-digest/ADD.ts b/packages/bloom/lib/commands/t-digest/ADD.ts index 5534d58065b..30e745f8f47 100644 --- a/packages/bloom/lib/commands/t-digest/ADD.ts +++ b/packages/bloom/lib/commands/t-digest/ADD.ts @@ -3,6 +3,12 @@ import { SimpleStringReply, Command, RedisArgument } from '@redis/client/dist/li export default { IS_READ_ONLY: false, + /** + * Adds one or more observations to a t-digest sketch + * @param parser - The command parser + * @param key - The name of the t-digest sketch + * @param values - Array of numeric values to add to the sketch + */ parseCommand(parser: CommandParser, key: RedisArgument, values: Array) { parser.push('TDIGEST.ADD'); parser.pushKey(key); diff --git a/packages/bloom/lib/commands/t-digest/BYRANK.ts b/packages/bloom/lib/commands/t-digest/BYRANK.ts index 9c1ab0059f3..9ac855bfeab 100644 --- a/packages/bloom/lib/commands/t-digest/BYRANK.ts +++ b/packages/bloom/lib/commands/t-digest/BYRANK.ts @@ -16,6 +16,12 @@ export function transformByRankArguments( export default { IS_READ_ONLY: true, + /** + * Returns value estimates for one or more ranks in a t-digest sketch + * @param parser - The command parser + * @param key - The name of the t-digest sketch + * @param ranks - Array of ranks to get value estimates for (ascending order) + */ parseCommand(...args: Parameters) { args[0].push('TDIGEST.BYRANK'); transformByRankArguments(...args); diff --git a/packages/bloom/lib/commands/t-digest/BYREVRANK.ts b/packages/bloom/lib/commands/t-digest/BYREVRANK.ts index 8721c081e7a..a94e5566bb1 100644 --- a/packages/bloom/lib/commands/t-digest/BYREVRANK.ts +++ b/packages/bloom/lib/commands/t-digest/BYREVRANK.ts @@ -1,6 +1,12 @@ import { Command } from '@redis/client/dist/lib/RESP/types'; import BYRANK, { transformByRankArguments } from './BYRANK'; +/** + * Returns value estimates for one or more ranks in a t-digest sketch, starting from highest rank + * @param parser - The command parser + * @param key - The name of the t-digest sketch + * @param ranks - Array of ranks to get value estimates for (descending order) + */ export default { IS_READ_ONLY: BYRANK.IS_READ_ONLY, parseCommand(...args: Parameters) { diff --git a/packages/bloom/lib/commands/t-digest/CDF.ts b/packages/bloom/lib/commands/t-digest/CDF.ts index 4d1d8ea2786..a3d3b884a34 100644 --- a/packages/bloom/lib/commands/t-digest/CDF.ts +++ b/packages/bloom/lib/commands/t-digest/CDF.ts @@ -4,6 +4,12 @@ import { transformDoubleArrayReply } from '@redis/client/dist/lib/commands/gener export default { IS_READ_ONLY: true, + /** + * Estimates the cumulative distribution function for values in a t-digest sketch + * @param parser - The command parser + * @param key - The name of the t-digest sketch + * @param values - Array of values to get CDF estimates for + */ parseCommand(parser: CommandParser, key: RedisArgument, values: Array) { parser.push('TDIGEST.CDF'); parser.pushKey(key); diff --git a/packages/bloom/lib/commands/t-digest/CREATE.ts b/packages/bloom/lib/commands/t-digest/CREATE.ts index 58b1e008284..25eb9c4f425 100644 --- a/packages/bloom/lib/commands/t-digest/CREATE.ts +++ b/packages/bloom/lib/commands/t-digest/CREATE.ts @@ -7,6 +7,13 @@ export interface TDigestCreateOptions { export default { IS_READ_ONLY: false, + /** + * Creates a new t-digest sketch for storing distributions + * @param parser - The command parser + * @param key - The name of the t-digest sketch + * @param options - Optional parameters for sketch creation + * @param options.COMPRESSION - Compression parameter that affects performance and accuracy + */ parseCommand(parser: CommandParser, key: RedisArgument, options?: TDigestCreateOptions) { parser.push('TDIGEST.CREATE'); parser.pushKey(key); diff --git a/packages/bloom/lib/commands/t-digest/INFO.ts b/packages/bloom/lib/commands/t-digest/INFO.ts index 2cb9e93443c..96ae420886c 100644 --- a/packages/bloom/lib/commands/t-digest/INFO.ts +++ b/packages/bloom/lib/commands/t-digest/INFO.ts @@ -16,6 +16,11 @@ export type TdInfoReplyMap = TuplesToMapReply<[ export default { IS_READ_ONLY: true, + /** + * Returns information about a t-digest sketch including compression, capacity, nodes, weights, observations and memory usage + * @param parser - The command parser + * @param key - The name of the t-digest sketch to get information about + */ parseCommand(parser: CommandParser, key: RedisArgument) { parser.push('TDIGEST.INFO'); parser.pushKey(key); diff --git a/packages/bloom/lib/commands/t-digest/MAX.ts b/packages/bloom/lib/commands/t-digest/MAX.ts index 140db6a3e48..2353c60cdc6 100644 --- a/packages/bloom/lib/commands/t-digest/MAX.ts +++ b/packages/bloom/lib/commands/t-digest/MAX.ts @@ -4,6 +4,11 @@ import { transformDoubleReply } from '@redis/client/dist/lib/commands/generic-tr export default { IS_READ_ONLY: true, + /** + * Returns the maximum value from a t-digest sketch + * @param parser - The command parser + * @param key - The name of the t-digest sketch + */ parseCommand(parser: CommandParser, key: RedisArgument) { parser.push('TDIGEST.MAX'); parser.pushKey(key); diff --git a/packages/bloom/lib/commands/t-digest/MERGE.ts b/packages/bloom/lib/commands/t-digest/MERGE.ts index 80049d1e540..f34d30a7cef 100644 --- a/packages/bloom/lib/commands/t-digest/MERGE.ts +++ b/packages/bloom/lib/commands/t-digest/MERGE.ts @@ -9,6 +9,15 @@ export interface TDigestMergeOptions { export default { IS_READ_ONLY: false, + /** + * Merges multiple t-digest sketches into one, with optional compression and override settings + * @param parser - The command parser + * @param destination - The name of the destination t-digest sketch + * @param source - One or more source sketch names to merge from + * @param options - Optional parameters for merge operation + * @param options.COMPRESSION - New compression value for merged sketch + * @param options.OVERRIDE - If true, override destination sketch if it exists + */ parseCommand( parser: CommandParser, destination: RedisArgument, diff --git a/packages/bloom/lib/commands/t-digest/MIN.ts b/packages/bloom/lib/commands/t-digest/MIN.ts index d6e56fb672e..9b8ea2a0cfc 100644 --- a/packages/bloom/lib/commands/t-digest/MIN.ts +++ b/packages/bloom/lib/commands/t-digest/MIN.ts @@ -4,6 +4,11 @@ import { transformDoubleReply } from '@redis/client/dist/lib/commands/generic-tr export default { IS_READ_ONLY: true, + /** + * Returns the minimum value from a t-digest sketch + * @param parser - The command parser + * @param key - The name of the t-digest sketch + */ parseCommand(parser: CommandParser, key: RedisArgument) { parser.push('TDIGEST.MIN'); parser.pushKey(key); diff --git a/packages/bloom/lib/commands/t-digest/QUANTILE.ts b/packages/bloom/lib/commands/t-digest/QUANTILE.ts index 1c27b5f6ec6..772172b84d5 100644 --- a/packages/bloom/lib/commands/t-digest/QUANTILE.ts +++ b/packages/bloom/lib/commands/t-digest/QUANTILE.ts @@ -4,6 +4,12 @@ import { transformDoubleArrayReply } from '@redis/client/dist/lib/commands/gener export default { IS_READ_ONLY: true, + /** + * Returns value estimates at requested quantiles from a t-digest sketch + * @param parser - The command parser + * @param key - The name of the t-digest sketch + * @param quantiles - Array of quantiles (between 0 and 1) to get value estimates for + */ parseCommand(parser: CommandParser, key: RedisArgument, quantiles: Array) { parser.push('TDIGEST.QUANTILE'); parser.pushKey(key); diff --git a/packages/bloom/lib/commands/t-digest/RANK.ts b/packages/bloom/lib/commands/t-digest/RANK.ts index 053c0c544e9..55dd3c636ce 100644 --- a/packages/bloom/lib/commands/t-digest/RANK.ts +++ b/packages/bloom/lib/commands/t-digest/RANK.ts @@ -15,6 +15,12 @@ export function transformRankArguments( export default { IS_READ_ONLY: true, + /** + * Returns the rank of one or more values in a t-digest sketch (number of values that are lower than each value) + * @param parser - The command parser + * @param key - The name of the t-digest sketch + * @param values - Array of values to get ranks for + */ parseCommand(...args: Parameters) { args[0].push('TDIGEST.RANK'); transformRankArguments(...args); diff --git a/packages/bloom/lib/commands/t-digest/RESET.ts b/packages/bloom/lib/commands/t-digest/RESET.ts index c2bda72d6d4..8e1cefe9ff8 100644 --- a/packages/bloom/lib/commands/t-digest/RESET.ts +++ b/packages/bloom/lib/commands/t-digest/RESET.ts @@ -3,6 +3,11 @@ import { RedisArgument, SimpleStringReply, Command } from '@redis/client/dist/li export default { IS_READ_ONLY: false, + /** + * Resets a t-digest sketch, clearing all previously added observations + * @param parser - The command parser + * @param key - The name of the t-digest sketch to reset + */ parseCommand(parser: CommandParser, key: RedisArgument) { parser.push('TDIGEST.RESET'); parser.pushKey(key); diff --git a/packages/bloom/lib/commands/t-digest/REVRANK.ts b/packages/bloom/lib/commands/t-digest/REVRANK.ts index e7f2357a2f8..e323e10190a 100644 --- a/packages/bloom/lib/commands/t-digest/REVRANK.ts +++ b/packages/bloom/lib/commands/t-digest/REVRANK.ts @@ -1,6 +1,12 @@ import { Command } from '@redis/client/dist/lib/RESP/types'; import RANK, { transformRankArguments } from './RANK'; +/** + * Returns the reverse rank of one or more values in a t-digest sketch (number of values that are higher than each value) + * @param parser - The command parser + * @param key - The name of the t-digest sketch + * @param values - Array of values to get reverse ranks for + */ export default { IS_READ_ONLY: RANK.IS_READ_ONLY, parseCommand(...args: Parameters) { diff --git a/packages/bloom/lib/commands/t-digest/TRIMMED_MEAN.ts b/packages/bloom/lib/commands/t-digest/TRIMMED_MEAN.ts index 1fd6360ab65..f65f37a95de 100644 --- a/packages/bloom/lib/commands/t-digest/TRIMMED_MEAN.ts +++ b/packages/bloom/lib/commands/t-digest/TRIMMED_MEAN.ts @@ -4,6 +4,13 @@ import { transformDoubleReply } from '@redis/client/dist/lib/commands/generic-tr export default { IS_READ_ONLY: true, + /** + * Returns the mean value from a t-digest sketch after trimming values at specified percentiles + * @param parser - The command parser + * @param key - The name of the t-digest sketch + * @param lowCutPercentile - Lower percentile cutoff (between 0 and 100) + * @param highCutPercentile - Higher percentile cutoff (between 0 and 100) + */ parseCommand( parser: CommandParser, key: RedisArgument, diff --git a/packages/bloom/lib/commands/top-k/ADD.ts b/packages/bloom/lib/commands/top-k/ADD.ts index 244e6209c91..8eab7dd5241 100644 --- a/packages/bloom/lib/commands/top-k/ADD.ts +++ b/packages/bloom/lib/commands/top-k/ADD.ts @@ -4,6 +4,12 @@ import { RedisVariadicArgument } from '@redis/client/dist/lib/commands/generic-t export default { IS_READ_ONLY: false, + /** + * Adds one or more items to a Top-K filter and returns items dropped from the top-K list + * @param parser - The command parser + * @param key - The name of the Top-K filter + * @param items - One or more items to add to the filter + */ parseCommand(parser: CommandParser, key: RedisArgument, items: RedisVariadicArgument) { parser.push('TOPK.ADD'); parser.pushKey(key); diff --git a/packages/bloom/lib/commands/top-k/COUNT.ts b/packages/bloom/lib/commands/top-k/COUNT.ts index 7e75a3b68aa..b72641471cd 100644 --- a/packages/bloom/lib/commands/top-k/COUNT.ts +++ b/packages/bloom/lib/commands/top-k/COUNT.ts @@ -4,6 +4,12 @@ import { RedisVariadicArgument } from '@redis/client/dist/lib/commands/generic-t export default { IS_READ_ONLY: true, + /** + * Returns the count of occurrences for one or more items in a Top-K filter + * @param parser - The command parser + * @param key - The name of the Top-K filter + * @param items - One or more items to get counts for + */ parseCommand(parser: CommandParser, key: RedisArgument, items: RedisVariadicArgument) { parser.push('TOPK.COUNT'); parser.pushKey(key); diff --git a/packages/bloom/lib/commands/top-k/INCRBY.ts b/packages/bloom/lib/commands/top-k/INCRBY.ts index 9e9a49e18f9..ef93653bd3c 100644 --- a/packages/bloom/lib/commands/top-k/INCRBY.ts +++ b/packages/bloom/lib/commands/top-k/INCRBY.ts @@ -12,6 +12,12 @@ function pushIncrByItem(parser: CommandParser, { item, incrementBy }: TopKIncrBy export default { IS_READ_ONLY: false, + /** + * Increases the score of one or more items in a Top-K filter by specified increments + * @param parser - The command parser + * @param key - The name of the Top-K filter + * @param items - A single item or array of items to increment, each with an item name and increment value + */ parseCommand( parser: CommandParser, key: RedisArgument, diff --git a/packages/bloom/lib/commands/top-k/INFO.ts b/packages/bloom/lib/commands/top-k/INFO.ts index 53da265c1f2..60ee3939324 100644 --- a/packages/bloom/lib/commands/top-k/INFO.ts +++ b/packages/bloom/lib/commands/top-k/INFO.ts @@ -12,6 +12,11 @@ export type TopKInfoReplyMap = TuplesToMapReply<[ export default { IS_READ_ONLY: true, + /** + * Returns configuration and statistics of a Top-K filter, including k, width, depth, and decay parameters + * @param parser - The command parser + * @param key - The name of the Top-K filter to get information about + */ parseCommand(parser: CommandParser, key: RedisArgument) { parser.push('TOPK.INFO'); parser.pushKey(key); diff --git a/packages/bloom/lib/commands/top-k/LIST.ts b/packages/bloom/lib/commands/top-k/LIST.ts index d7adeaa193c..e030ff02efa 100644 --- a/packages/bloom/lib/commands/top-k/LIST.ts +++ b/packages/bloom/lib/commands/top-k/LIST.ts @@ -3,6 +3,11 @@ import { RedisArgument, ArrayReply, BlobStringReply, Command } from '@redis/clie export default { IS_READ_ONLY: true, + /** + * Returns all items in a Top-K filter + * @param parser - The command parser + * @param key - The name of the Top-K filter + */ parseCommand(parser: CommandParser, key: RedisArgument) { parser.push('TOPK.LIST'); parser.pushKey(key); diff --git a/packages/bloom/lib/commands/top-k/LIST_WITHCOUNT.ts b/packages/bloom/lib/commands/top-k/LIST_WITHCOUNT.ts index 2c0f10e785b..bed58a36b70 100644 --- a/packages/bloom/lib/commands/top-k/LIST_WITHCOUNT.ts +++ b/packages/bloom/lib/commands/top-k/LIST_WITHCOUNT.ts @@ -3,6 +3,11 @@ import { RedisArgument, ArrayReply, BlobStringReply, NumberReply, UnwrapReply, C export default { IS_READ_ONLY: true, + /** + * Returns all items in a Top-K filter with their respective counts + * @param parser - The command parser + * @param key - The name of the Top-K filter + */ parseCommand(parser: CommandParser, key: RedisArgument) { parser.push('TOPK.LIST'); parser.pushKey(key); diff --git a/packages/bloom/lib/commands/top-k/QUERY.ts b/packages/bloom/lib/commands/top-k/QUERY.ts index a6fb4bae69e..8976e211979 100644 --- a/packages/bloom/lib/commands/top-k/QUERY.ts +++ b/packages/bloom/lib/commands/top-k/QUERY.ts @@ -4,6 +4,12 @@ import { RedisVariadicArgument, transformBooleanArrayReply } from '@redis/client export default { IS_READ_ONLY: false, + /** + * Checks if one or more items are in the Top-K list + * @param parser - The command parser + * @param key - The name of the Top-K filter + * @param items - One or more items to check in the filter + */ parseCommand(parser: CommandParser, key: RedisArgument, items: RedisVariadicArgument) { parser.push('TOPK.QUERY'); parser.pushKey(key); diff --git a/packages/bloom/lib/commands/top-k/RESERVE.ts b/packages/bloom/lib/commands/top-k/RESERVE.ts index ee3ee9a8cf4..e31c2ceb99c 100644 --- a/packages/bloom/lib/commands/top-k/RESERVE.ts +++ b/packages/bloom/lib/commands/top-k/RESERVE.ts @@ -9,6 +9,16 @@ export interface TopKReserveOptions { export default { IS_READ_ONLY: false, + /** + * Creates a new Top-K filter with specified parameters + * @param parser - The command parser + * @param key - The name of the Top-K filter + * @param topK - Number of top occurring items to keep + * @param options - Optional parameters for filter configuration + * @param options.width - Number of counters in each array + * @param options.depth - Number of counter-arrays + * @param options.decay - Counter decay factor + */ parseCommand(parser: CommandParser, key: RedisArgument, topK: number, options?: TopKReserveOptions) { parser.push('TOPK.RESERVE'); parser.pushKey(key); diff --git a/packages/client/lib/commands/ACL_CAT.ts b/packages/client/lib/commands/ACL_CAT.ts index ae094b732b8..f4ddfacc68d 100644 --- a/packages/client/lib/commands/ACL_CAT.ts +++ b/packages/client/lib/commands/ACL_CAT.ts @@ -4,6 +4,11 @@ import { RedisArgument, ArrayReply, BlobStringReply, Command } from '../RESP/typ export default { NOT_KEYED_COMMAND: true, IS_READ_ONLY: true, + /** + * Lists ACL categories or commands in a category + * @param parser - The Redis command parser + * @param categoryName - Optional category name to filter commands + */ parseCommand(parser: CommandParser, categoryName?: RedisArgument) { parser.push('ACL', 'CAT'); if (categoryName) { diff --git a/packages/client/lib/commands/ACL_DELUSER.ts b/packages/client/lib/commands/ACL_DELUSER.ts index 5aa66becf75..404641e0abb 100644 --- a/packages/client/lib/commands/ACL_DELUSER.ts +++ b/packages/client/lib/commands/ACL_DELUSER.ts @@ -5,6 +5,11 @@ import { RedisVariadicArgument } from './generic-transformers'; export default { NOT_KEYED_COMMAND: true, IS_READ_ONLY: true, + /** + * Deletes one or more users from the ACL + * @param parser - The Redis command parser + * @param username - Username(s) to delete + */ parseCommand(parser: CommandParser, username: RedisVariadicArgument) { parser.push('ACL', 'DELUSER'); parser.pushVariadic(username); diff --git a/packages/client/lib/commands/ACL_DRYRUN.ts b/packages/client/lib/commands/ACL_DRYRUN.ts index 09a51bc36f8..49ac41a859a 100644 --- a/packages/client/lib/commands/ACL_DRYRUN.ts +++ b/packages/client/lib/commands/ACL_DRYRUN.ts @@ -4,6 +4,12 @@ import { RedisArgument, SimpleStringReply, BlobStringReply, Command } from '../R export default { NOT_KEYED_COMMAND: true, IS_READ_ONLY: true, + /** + * Simulates ACL operations without executing them + * @param parser - The Redis command parser + * @param username - Username to simulate ACL operations for + * @param command - Command arguments to simulate + */ parseCommand(parser: CommandParser, username: RedisArgument, command: Array) { parser.push('ACL', 'DRYRUN', username, ...command); }, diff --git a/packages/client/lib/commands/ACL_GENPASS.ts b/packages/client/lib/commands/ACL_GENPASS.ts index b5caa29b9b2..d1785839a5c 100644 --- a/packages/client/lib/commands/ACL_GENPASS.ts +++ b/packages/client/lib/commands/ACL_GENPASS.ts @@ -4,6 +4,11 @@ import { BlobStringReply, Command } from '../RESP/types'; export default { NOT_KEYED_COMMAND: true, IS_READ_ONLY: true, + /** + * Generates a secure password for ACL users + * @param parser - The Redis command parser + * @param bits - Optional number of bits for password entropy + */ parseCommand(parser: CommandParser, bits?: number) { parser.push('ACL', 'GENPASS'); if (bits) { diff --git a/packages/client/lib/commands/ACL_GETUSER.ts b/packages/client/lib/commands/ACL_GETUSER.ts index b4764ad744e..a1505251c6e 100644 --- a/packages/client/lib/commands/ACL_GETUSER.ts +++ b/packages/client/lib/commands/ACL_GETUSER.ts @@ -20,6 +20,11 @@ type AclUser = TuplesToMapReply<[ export default { NOT_KEYED_COMMAND: true, IS_READ_ONLY: true, + /** + * Returns ACL information about a specific user + * @param parser - The Redis command parser + * @param username - Username to get information for + */ parseCommand(parser: CommandParser, username: RedisArgument) { parser.push('ACL', 'GETUSER', username); }, diff --git a/packages/client/lib/commands/ACL_LIST.ts b/packages/client/lib/commands/ACL_LIST.ts index b5f82cf272c..4d2ec995cd5 100644 --- a/packages/client/lib/commands/ACL_LIST.ts +++ b/packages/client/lib/commands/ACL_LIST.ts @@ -4,6 +4,10 @@ import { ArrayReply, BlobStringReply, Command } from '../RESP/types'; export default { NOT_KEYED_COMMAND: true, IS_READ_ONLY: true, + /** + * Returns all configured ACL users and their permissions + * @param parser - The Redis command parser + */ parseCommand(parser: CommandParser) { parser.push('ACL', 'LIST'); }, diff --git a/packages/client/lib/commands/ACL_LOAD.ts b/packages/client/lib/commands/ACL_LOAD.ts index dc4320b99fc..0367904a507 100644 --- a/packages/client/lib/commands/ACL_LOAD.ts +++ b/packages/client/lib/commands/ACL_LOAD.ts @@ -4,6 +4,10 @@ import { SimpleStringReply, Command } from '../RESP/types'; export default { NOT_KEYED_COMMAND: true, IS_READ_ONLY: true, + /** + * Reloads ACL configuration from the ACL file + * @param parser - The Redis command parser + */ parseCommand(parser: CommandParser) { parser.push('ACL', 'LOAD'); }, diff --git a/packages/client/lib/commands/ACL_LOG.ts b/packages/client/lib/commands/ACL_LOG.ts index 4cf2722ec86..a65f85039b1 100644 --- a/packages/client/lib/commands/ACL_LOG.ts +++ b/packages/client/lib/commands/ACL_LOG.ts @@ -21,6 +21,11 @@ export type AclLogReply = ArrayReply) { parser.push('CLIENT', 'KILL'); diff --git a/packages/client/lib/commands/CLIENT_LIST.ts b/packages/client/lib/commands/CLIENT_LIST.ts index 1e7f3d9ab40..1c1d9c3ec50 100644 --- a/packages/client/lib/commands/CLIENT_LIST.ts +++ b/packages/client/lib/commands/CLIENT_LIST.ts @@ -17,6 +17,11 @@ export type ListFilter = ListFilterType | ListFilterId; export default { NOT_KEYED_COMMAND: true, IS_READ_ONLY: true, + /** + * Returns information about all client connections. Can be filtered by type or ID + * @param parser - The Redis command parser + * @param filter - Optional filter to return only specific client types or IDs + */ parseCommand(parser: CommandParser, filter?: ListFilter) { parser.push('CLIENT', 'LIST'); if (filter) { diff --git a/packages/client/lib/commands/CLIENT_NO-EVICT.ts b/packages/client/lib/commands/CLIENT_NO-EVICT.ts index de2f65270e2..116d226d036 100644 --- a/packages/client/lib/commands/CLIENT_NO-EVICT.ts +++ b/packages/client/lib/commands/CLIENT_NO-EVICT.ts @@ -4,6 +4,11 @@ import { SimpleStringReply, Command } from '../RESP/types'; export default { NOT_KEYED_COMMAND: true, IS_READ_ONLY: true, + /** + * Controls whether to prevent the client's connections from being evicted + * @param parser - The Redis command parser + * @param value - Whether to enable (true) or disable (false) the no-evict mode + */ parseCommand(parser: CommandParser, value: boolean) { parser.push( 'CLIENT', diff --git a/packages/client/lib/commands/CLIENT_NO-TOUCH.ts b/packages/client/lib/commands/CLIENT_NO-TOUCH.ts index 8c6deff4af5..167b31f3600 100644 --- a/packages/client/lib/commands/CLIENT_NO-TOUCH.ts +++ b/packages/client/lib/commands/CLIENT_NO-TOUCH.ts @@ -4,6 +4,11 @@ import { SimpleStringReply, Command } from '../RESP/types'; export default { NOT_KEYED_COMMAND: true, IS_READ_ONLY: true, + /** + * Controls whether to prevent the client from touching the LRU/LFU of keys + * @param parser - The Redis command parser + * @param value - Whether to enable (true) or disable (false) the no-touch mode + */ parseCommand(parser: CommandParser, value: boolean) { parser.push( 'CLIENT', diff --git a/packages/client/lib/commands/CLIENT_PAUSE.ts b/packages/client/lib/commands/CLIENT_PAUSE.ts index ae6e4376364..4d0638df89d 100644 --- a/packages/client/lib/commands/CLIENT_PAUSE.ts +++ b/packages/client/lib/commands/CLIENT_PAUSE.ts @@ -4,6 +4,12 @@ import { SimpleStringReply, Command } from '../RESP/types'; export default { NOT_KEYED_COMMAND: true, IS_READ_ONLY: true, + /** + * Stops the server from processing client commands for the specified duration + * @param parser - The Redis command parser + * @param timeout - Time in milliseconds to pause command processing + * @param mode - Optional mode: 'WRITE' to pause only write commands, 'ALL' to pause all commands + */ parseCommand(parser: CommandParser, timeout: number, mode?: 'WRITE' | 'ALL') { parser.push('CLIENT', 'PAUSE', timeout.toString()); if (mode) { diff --git a/packages/client/lib/commands/CLIENT_SETNAME.ts b/packages/client/lib/commands/CLIENT_SETNAME.ts index 335891e8308..7e4fcc17d94 100644 --- a/packages/client/lib/commands/CLIENT_SETNAME.ts +++ b/packages/client/lib/commands/CLIENT_SETNAME.ts @@ -4,6 +4,11 @@ import { RedisArgument, SimpleStringReply, Command } from '../RESP/types'; export default { NOT_KEYED_COMMAND: true, IS_READ_ONLY: true, + /** + * Assigns a name to the current connection + * @param parser - The Redis command parser + * @param name - The name to assign to the connection + */ parseCommand(parser: CommandParser, name: RedisArgument) { parser.push('CLIENT', 'SETNAME', name); }, diff --git a/packages/client/lib/commands/CLIENT_TRACKING.ts b/packages/client/lib/commands/CLIENT_TRACKING.ts index df70a3705f9..08bed098c20 100644 --- a/packages/client/lib/commands/CLIENT_TRACKING.ts +++ b/packages/client/lib/commands/CLIENT_TRACKING.ts @@ -29,6 +29,12 @@ export type ClientTrackingOptions = CommonOptions & ( export default { NOT_KEYED_COMMAND: true, IS_READ_ONLY: true, + /** + * Controls server-assisted client side caching for the current connection + * @param parser - The Redis command parser + * @param mode - Whether to enable (true) or disable (false) tracking + * @param options - Optional configuration including REDIRECT, BCAST, PREFIX, OPTIN, OPTOUT, and NOLOOP options + */ parseCommand( parser: CommandParser, mode: M, diff --git a/packages/client/lib/commands/CLIENT_TRACKINGINFO.ts b/packages/client/lib/commands/CLIENT_TRACKINGINFO.ts index fe6e090455c..1fb19e9e60b 100644 --- a/packages/client/lib/commands/CLIENT_TRACKINGINFO.ts +++ b/packages/client/lib/commands/CLIENT_TRACKINGINFO.ts @@ -10,6 +10,10 @@ type TrackingInfo = TuplesToMapReply<[ export default { NOT_KEYED_COMMAND: true, IS_READ_ONLY: true, + /** + * Returns information about the current connection's key tracking state + * @param parser - The Redis command parser + */ parseCommand(parser: CommandParser) { parser.push('CLIENT', 'TRACKINGINFO'); }, diff --git a/packages/client/lib/commands/CLIENT_UNPAUSE.ts b/packages/client/lib/commands/CLIENT_UNPAUSE.ts index c202e50a5df..2ac4fde0112 100644 --- a/packages/client/lib/commands/CLIENT_UNPAUSE.ts +++ b/packages/client/lib/commands/CLIENT_UNPAUSE.ts @@ -4,6 +4,10 @@ import { SimpleStringReply, Command } from '../RESP/types'; export default { NOT_KEYED_COMMAND: true, IS_READ_ONLY: true, + /** + * Resumes processing of client commands after a CLIENT PAUSE + * @param parser - The Redis command parser + */ parseCommand(parser: CommandParser) { parser.push('CLIENT', 'UNPAUSE'); }, diff --git a/packages/client/lib/commands/CLUSTER_ADDSLOTS.ts b/packages/client/lib/commands/CLUSTER_ADDSLOTS.ts index 0f5c4513d1d..f833a42e5ad 100644 --- a/packages/client/lib/commands/CLUSTER_ADDSLOTS.ts +++ b/packages/client/lib/commands/CLUSTER_ADDSLOTS.ts @@ -4,6 +4,11 @@ import { SimpleStringReply, Command } from '../RESP/types'; export default { NOT_KEYED_COMMAND: true, IS_READ_ONLY: true, + /** + * Assigns hash slots to the current node in a Redis Cluster + * @param parser - The Redis command parser + * @param slots - One or more hash slots to be assigned + */ parseCommand(parser: CommandParser, slots: number | Array) { parser.push('CLUSTER', 'ADDSLOTS'); parser.pushVariadicNumber(slots); diff --git a/packages/client/lib/commands/CLUSTER_ADDSLOTSRANGE.ts b/packages/client/lib/commands/CLUSTER_ADDSLOTSRANGE.ts index 40780731981..e3e7c8b4988 100644 --- a/packages/client/lib/commands/CLUSTER_ADDSLOTSRANGE.ts +++ b/packages/client/lib/commands/CLUSTER_ADDSLOTSRANGE.ts @@ -5,6 +5,11 @@ import { parseSlotRangesArguments, SlotRange } from './generic-transformers'; export default { NOT_KEYED_COMMAND: true, IS_READ_ONLY: true, + /** + * Assigns hash slot ranges to the current node in a Redis Cluster + * @param parser - The Redis command parser + * @param ranges - One or more slot ranges to be assigned, each specified as [start, end] + */ parseCommand(parser: CommandParser, ranges: SlotRange | Array) { parser.push('CLUSTER', 'ADDSLOTSRANGE'); parseSlotRangesArguments(parser, ranges); diff --git a/packages/client/lib/commands/CLUSTER_BUMPEPOCH.ts b/packages/client/lib/commands/CLUSTER_BUMPEPOCH.ts index 04b62f85424..3cf9f3fb207 100644 --- a/packages/client/lib/commands/CLUSTER_BUMPEPOCH.ts +++ b/packages/client/lib/commands/CLUSTER_BUMPEPOCH.ts @@ -4,6 +4,10 @@ import { SimpleStringReply, Command } from '../RESP/types'; export default { NOT_KEYED_COMMAND: true, IS_READ_ONLY: true, + /** + * Advances the cluster config epoch + * @param parser - The Redis command parser + */ parseCommand(parser: CommandParser) { parser.push('CLUSTER', 'BUMPEPOCH'); }, diff --git a/packages/client/lib/commands/CLUSTER_COUNT-FAILURE-REPORTS.ts b/packages/client/lib/commands/CLUSTER_COUNT-FAILURE-REPORTS.ts index 0ac311f7ecd..b4421b1d8fa 100644 --- a/packages/client/lib/commands/CLUSTER_COUNT-FAILURE-REPORTS.ts +++ b/packages/client/lib/commands/CLUSTER_COUNT-FAILURE-REPORTS.ts @@ -4,6 +4,11 @@ import { RedisArgument, NumberReply, Command } from '../RESP/types'; export default { NOT_KEYED_COMMAND: true, IS_READ_ONLY: true, + /** + * Returns the number of failure reports for a given node + * @param parser - The Redis command parser + * @param nodeId - The ID of the node to check + */ parseCommand(parser: CommandParser, nodeId: RedisArgument) { parser.push('CLUSTER', 'COUNT-FAILURE-REPORTS', nodeId); }, diff --git a/packages/client/lib/commands/CLUSTER_COUNTKEYSINSLOT.ts b/packages/client/lib/commands/CLUSTER_COUNTKEYSINSLOT.ts index 63b4a8e02e2..df97c79161e 100644 --- a/packages/client/lib/commands/CLUSTER_COUNTKEYSINSLOT.ts +++ b/packages/client/lib/commands/CLUSTER_COUNTKEYSINSLOT.ts @@ -4,6 +4,11 @@ import { NumberReply, Command } from '../RESP/types'; export default { NOT_KEYED_COMMAND: true, IS_READ_ONLY: true, + /** + * Returns the number of keys in the specified hash slot + * @param parser - The Redis command parser + * @param slot - The hash slot to check + */ parseCommand(parser: CommandParser, slot: number) { parser.push('CLUSTER', 'COUNTKEYSINSLOT', slot.toString()); }, diff --git a/packages/client/lib/commands/CLUSTER_DELSLOTS.ts b/packages/client/lib/commands/CLUSTER_DELSLOTS.ts index 9be6e962a18..eea7bcb699e 100644 --- a/packages/client/lib/commands/CLUSTER_DELSLOTS.ts +++ b/packages/client/lib/commands/CLUSTER_DELSLOTS.ts @@ -4,6 +4,11 @@ import { SimpleStringReply, Command } from '../RESP/types'; export default { NOT_KEYED_COMMAND: true, IS_READ_ONLY: true, + /** + * Removes hash slots from the current node in a Redis Cluster + * @param parser - The Redis command parser + * @param slots - One or more hash slots to be removed + */ parseCommand(parser: CommandParser, slots: number | Array) { parser.push('CLUSTER', 'DELSLOTS'); parser.pushVariadicNumber(slots); diff --git a/packages/client/lib/commands/CLUSTER_DELSLOTSRANGE.ts b/packages/client/lib/commands/CLUSTER_DELSLOTSRANGE.ts index 64c04021ba1..32b27d8ea14 100644 --- a/packages/client/lib/commands/CLUSTER_DELSLOTSRANGE.ts +++ b/packages/client/lib/commands/CLUSTER_DELSLOTSRANGE.ts @@ -5,6 +5,11 @@ import { parseSlotRangesArguments, SlotRange } from './generic-transformers'; export default { NOT_KEYED_COMMAND: true, IS_READ_ONLY: true, + /** + * Removes hash slot ranges from the current node in a Redis Cluster + * @param parser - The Redis command parser + * @param ranges - One or more slot ranges to be removed, each specified as [start, end] + */ parseCommand(parser:CommandParser, ranges: SlotRange | Array) { parser.push('CLUSTER', 'DELSLOTSRANGE'); parseSlotRangesArguments(parser, ranges); diff --git a/packages/client/lib/commands/CLUSTER_FAILOVER.ts b/packages/client/lib/commands/CLUSTER_FAILOVER.ts index f74d65bd691..8a228c07349 100644 --- a/packages/client/lib/commands/CLUSTER_FAILOVER.ts +++ b/packages/client/lib/commands/CLUSTER_FAILOVER.ts @@ -15,6 +15,11 @@ export interface ClusterFailoverOptions { export default { NOT_KEYED_COMMAND: true, IS_READ_ONLY: true, + /** + * Forces a replica to perform a manual failover of its master + * @param parser - The Redis command parser + * @param options - Optional configuration with FORCE or TAKEOVER mode + */ parseCommand(parser:CommandParser, options?: ClusterFailoverOptions) { parser.push('CLUSTER', 'FAILOVER'); diff --git a/packages/client/lib/commands/CLUSTER_FLUSHSLOTS.ts b/packages/client/lib/commands/CLUSTER_FLUSHSLOTS.ts index dab22b2e740..dac1f24c697 100644 --- a/packages/client/lib/commands/CLUSTER_FLUSHSLOTS.ts +++ b/packages/client/lib/commands/CLUSTER_FLUSHSLOTS.ts @@ -4,6 +4,10 @@ import { SimpleStringReply, Command } from '../RESP/types'; export default { NOT_KEYED_COMMAND: true, IS_READ_ONLY: true, + /** + * Deletes all hash slots from the current node in a Redis Cluster + * @param parser - The Redis command parser + */ parseCommand(parser: CommandParser) { parser.push('CLUSTER', 'FLUSHSLOTS'); }, diff --git a/packages/client/lib/commands/CLUSTER_FORGET.ts b/packages/client/lib/commands/CLUSTER_FORGET.ts index 2928c3e9075..ff7cb952121 100644 --- a/packages/client/lib/commands/CLUSTER_FORGET.ts +++ b/packages/client/lib/commands/CLUSTER_FORGET.ts @@ -4,6 +4,11 @@ import { RedisArgument, SimpleStringReply, Command } from '../RESP/types'; export default { NOT_KEYED_COMMAND: true, IS_READ_ONLY: true, + /** + * Removes a node from the cluster + * @param parser - The Redis command parser + * @param nodeId - The ID of the node to remove + */ parseCommand(parser: CommandParser, nodeId: RedisArgument) { parser.push('CLUSTER', 'FORGET', nodeId); }, diff --git a/packages/client/lib/commands/CLUSTER_GETKEYSINSLOT.ts b/packages/client/lib/commands/CLUSTER_GETKEYSINSLOT.ts index 2fd630ea1af..90756cf6302 100644 --- a/packages/client/lib/commands/CLUSTER_GETKEYSINSLOT.ts +++ b/packages/client/lib/commands/CLUSTER_GETKEYSINSLOT.ts @@ -4,6 +4,12 @@ import { ArrayReply, BlobStringReply, Command } from '../RESP/types'; export default { NOT_KEYED_COMMAND: true, IS_READ_ONLY: true, + /** + * Returns a number of keys from the specified hash slot + * @param parser - The Redis command parser + * @param slot - The hash slot to get keys from + * @param count - Maximum number of keys to return + */ parseCommand(parser: CommandParser, slot: number, count: number) { parser.push('CLUSTER', 'GETKEYSINSLOT', slot.toString(), count.toString()); }, diff --git a/packages/client/lib/commands/CLUSTER_INFO.ts b/packages/client/lib/commands/CLUSTER_INFO.ts index 53140b38819..fa220b9f645 100644 --- a/packages/client/lib/commands/CLUSTER_INFO.ts +++ b/packages/client/lib/commands/CLUSTER_INFO.ts @@ -4,6 +4,10 @@ import { VerbatimStringReply, Command } from '../RESP/types'; export default { NOT_KEYED_COMMAND: true, IS_READ_ONLY: true, + /** + * Returns information about the state of a Redis Cluster + * @param parser - The Redis command parser + */ parseCommand(parser: CommandParser) { parser.push('CLUSTER', 'INFO'); }, diff --git a/packages/client/lib/commands/CLUSTER_KEYSLOT.ts b/packages/client/lib/commands/CLUSTER_KEYSLOT.ts index d81a14e1a96..1add4cb4f4a 100644 --- a/packages/client/lib/commands/CLUSTER_KEYSLOT.ts +++ b/packages/client/lib/commands/CLUSTER_KEYSLOT.ts @@ -4,6 +4,11 @@ import { Command, NumberReply, RedisArgument } from '../RESP/types'; export default { NOT_KEYED_COMMAND: true, IS_READ_ONLY: true, + /** + * Returns the hash slot number for a given key + * @param parser - The Redis command parser + * @param key - The key to get the hash slot for + */ parseCommand(parser: CommandParser, key: RedisArgument) { parser.push('CLUSTER', 'KEYSLOT', key); }, diff --git a/packages/client/lib/commands/CLUSTER_LINKS.ts b/packages/client/lib/commands/CLUSTER_LINKS.ts index e98f61e762b..d35f4712650 100644 --- a/packages/client/lib/commands/CLUSTER_LINKS.ts +++ b/packages/client/lib/commands/CLUSTER_LINKS.ts @@ -13,6 +13,10 @@ type ClusterLinksReply = ArrayReply; export default { NOT_KEYED_COMMAND: true, IS_READ_ONLY: true, + /** + * Returns information about which Redis Cluster node handles which hash slots + * @param parser - The Redis command parser + */ parseCommand(parser: CommandParser) { parser.push('CLUSTER', 'SLOTS'); }, diff --git a/packages/client/lib/commands/COMMAND.ts b/packages/client/lib/commands/COMMAND.ts index 52eb7eb2fea..3d24b716a0d 100644 --- a/packages/client/lib/commands/COMMAND.ts +++ b/packages/client/lib/commands/COMMAND.ts @@ -5,6 +5,10 @@ import { CommandRawReply, CommandReply, transformCommandReply } from './generic- export default { NOT_KEYED_COMMAND: true, IS_READ_ONLY: true, + /** + * Returns an array with details about all Redis commands + * @param parser - The Redis command parser + */ parseCommand(parser: CommandParser) { parser.push('COMMAND'); }, diff --git a/packages/client/lib/commands/COMMAND_COUNT.ts b/packages/client/lib/commands/COMMAND_COUNT.ts index ef561920b0b..36b35a58d7b 100644 --- a/packages/client/lib/commands/COMMAND_COUNT.ts +++ b/packages/client/lib/commands/COMMAND_COUNT.ts @@ -4,6 +4,10 @@ import { NumberReply, Command } from '../RESP/types'; export default { NOT_KEYED_COMMAND: true, IS_READ_ONLY: true, + /** + * Returns the total number of commands available in the Redis server + * @param parser - The Redis command parser + */ parseCommand(parser: CommandParser) { parser.push('COMMAND', 'COUNT'); }, diff --git a/packages/client/lib/commands/COMMAND_GETKEYS.ts b/packages/client/lib/commands/COMMAND_GETKEYS.ts index 97c5cb69ce2..6f447c4d4d6 100644 --- a/packages/client/lib/commands/COMMAND_GETKEYS.ts +++ b/packages/client/lib/commands/COMMAND_GETKEYS.ts @@ -4,6 +4,11 @@ import { RedisArgument, ArrayReply, BlobStringReply, Command } from '../RESP/typ export default { NOT_KEYED_COMMAND: true, IS_READ_ONLY: true, + /** + * Extracts the key names from a Redis command + * @param parser - The Redis command parser + * @param args - Command arguments to analyze + */ parseCommand(parser: CommandParser, args: Array) { parser.push('COMMAND', 'GETKEYS'); parser.push(...args); diff --git a/packages/client/lib/commands/COMMAND_GETKEYSANDFLAGS.ts b/packages/client/lib/commands/COMMAND_GETKEYSANDFLAGS.ts index 72c1e16a2d1..1677adb43ec 100644 --- a/packages/client/lib/commands/COMMAND_GETKEYSANDFLAGS.ts +++ b/packages/client/lib/commands/COMMAND_GETKEYSANDFLAGS.ts @@ -9,6 +9,11 @@ export type CommandGetKeysAndFlagsRawReply = ArrayReply) { parser.push('COMMAND', 'GETKEYSANDFLAGS'); parser.push(...args); diff --git a/packages/client/lib/commands/COMMAND_INFO.ts b/packages/client/lib/commands/COMMAND_INFO.ts index fdf03780652..19629f0ccae 100644 --- a/packages/client/lib/commands/COMMAND_INFO.ts +++ b/packages/client/lib/commands/COMMAND_INFO.ts @@ -5,6 +5,11 @@ import { CommandRawReply, CommandReply, transformCommandReply } from './generic- export default { NOT_KEYED_COMMAND: true, IS_READ_ONLY: true, + /** + * Returns details about specific Redis commands + * @param parser - The Redis command parser + * @param commands - Array of command names to get information about + */ parseCommand(parser: CommandParser, commands: Array) { parser.push('COMMAND', 'INFO', ...commands); }, diff --git a/packages/client/lib/commands/COMMAND_LIST.ts b/packages/client/lib/commands/COMMAND_LIST.ts index ba518b70eca..fa218d86aa7 100644 --- a/packages/client/lib/commands/COMMAND_LIST.ts +++ b/packages/client/lib/commands/COMMAND_LIST.ts @@ -19,6 +19,11 @@ export interface CommandListOptions { export default { NOT_KEYED_COMMAND: true, IS_READ_ONLY: true, + /** + * Returns a list of all commands supported by the Redis server + * @param parser - The Redis command parser + * @param options - Options for filtering the command list + */ parseCommand(parser: CommandParser, options?: CommandListOptions) { parser.push('COMMAND', 'LIST'); diff --git a/packages/client/lib/commands/CONFIG_GET.ts b/packages/client/lib/commands/CONFIG_GET.ts index e8339c4d9a0..d0c80297fc4 100644 --- a/packages/client/lib/commands/CONFIG_GET.ts +++ b/packages/client/lib/commands/CONFIG_GET.ts @@ -5,6 +5,11 @@ import { RedisVariadicArgument, transformTuplesReply } from './generic-transform export default { NOT_KEYED_COMMAND: true, IS_READ_ONLY: true, + /** + * Gets the values of configuration parameters + * @param parser - The Redis command parser + * @param parameters - Pattern or specific configuration parameter names + */ parseCommand(parser: CommandParser, parameters: RedisVariadicArgument) { parser.push('CONFIG', 'GET'); parser.pushVariadic(parameters); diff --git a/packages/client/lib/commands/CONFIG_RESETSTAT.ts b/packages/client/lib/commands/CONFIG_RESETSTAT.ts index 15de5ba7808..356a9b29a79 100644 --- a/packages/client/lib/commands/CONFIG_RESETSTAT.ts +++ b/packages/client/lib/commands/CONFIG_RESETSTAT.ts @@ -4,6 +4,10 @@ import { SimpleStringReply, Command } from '../RESP/types'; export default { NOT_KEYED_COMMAND: true, IS_READ_ONLY: true, + /** + * Resets the statistics reported by Redis using the INFO command + * @param parser - The Redis command parser + */ parseCommand(parser: CommandParser) { parser.push('CONFIG', 'RESETSTAT'); }, diff --git a/packages/client/lib/commands/CONFIG_REWRITE.ts b/packages/client/lib/commands/CONFIG_REWRITE.ts index ae6712ffb57..a9f2e0a41ba 100644 --- a/packages/client/lib/commands/CONFIG_REWRITE.ts +++ b/packages/client/lib/commands/CONFIG_REWRITE.ts @@ -4,6 +4,10 @@ import { SimpleStringReply, Command } from '../RESP/types'; export default { NOT_KEYED_COMMAND: true, IS_READ_ONLY: true, + /** + * Rewrites the Redis configuration file with the current configuration + * @param parser - The Redis command parser + */ parseCommand(parser: CommandParser) { parser.push('CONFIG', 'REWRITE'); }, diff --git a/packages/client/lib/commands/CONFIG_SET.ts b/packages/client/lib/commands/CONFIG_SET.ts index dd1bbc29ef2..81b4c65c1d9 100644 --- a/packages/client/lib/commands/CONFIG_SET.ts +++ b/packages/client/lib/commands/CONFIG_SET.ts @@ -8,6 +8,12 @@ type MultipleParameters = [config: Record]; export default { NOT_KEYED_COMMAND: true, IS_READ_ONLY: true, + /** + * Sets configuration parameters to the specified values + * @param parser - The Redis command parser + * @param parameterOrConfig - Either a single parameter name or a configuration object + * @param value - Value for the parameter (when using single parameter format) + */ parseCommand( parser: CommandParser, ...[parameterOrConfig, value]: SingleParameter | MultipleParameters diff --git a/packages/client/lib/commands/COPY.ts b/packages/client/lib/commands/COPY.ts index f26a930264c..0d8af5636df 100644 --- a/packages/client/lib/commands/COPY.ts +++ b/packages/client/lib/commands/COPY.ts @@ -8,6 +8,13 @@ export interface CopyCommandOptions { export default { IS_READ_ONLY: false, + /** + * Copies the value stored at the source key to the destination key + * @param parser - The Redis command parser + * @param source - Source key + * @param destination - Destination key + * @param options - Options for the copy operation + */ parseCommand(parser: CommandParser, source: RedisArgument, destination: RedisArgument, options?: CopyCommandOptions) { parser.push('COPY'); parser.pushKeys([source, destination]); diff --git a/packages/client/lib/commands/DBSIZE.ts b/packages/client/lib/commands/DBSIZE.ts index 1ba1f060476..b5777b63f7d 100644 --- a/packages/client/lib/commands/DBSIZE.ts +++ b/packages/client/lib/commands/DBSIZE.ts @@ -4,6 +4,10 @@ import { NumberReply, Command } from '../RESP/types'; export default { NOT_KEYED_COMMAND: true, IS_READ_ONLY: true, + /** + * Returns the number of keys in the current database + * @param parser - The Redis command parser + */ parseCommand(parser: CommandParser) { parser.push('DBSIZE'); }, diff --git a/packages/client/lib/commands/DECR.ts b/packages/client/lib/commands/DECR.ts index b9a6200d81b..5155fba81f5 100644 --- a/packages/client/lib/commands/DECR.ts +++ b/packages/client/lib/commands/DECR.ts @@ -2,6 +2,11 @@ import { CommandParser } from '../client/parser'; import { RedisArgument, NumberReply, Command } from '../RESP/types'; export default { + /** + * Decrements the integer value of a key by one + * @param parser - The Redis command parser + * @param key - Key to decrement + */ parseCommand(parser: CommandParser, key: RedisArgument) { parser.push('DECR'); parser.pushKey(key); diff --git a/packages/client/lib/commands/DECRBY.ts b/packages/client/lib/commands/DECRBY.ts index 53a8315130d..9f35ee15a26 100644 --- a/packages/client/lib/commands/DECRBY.ts +++ b/packages/client/lib/commands/DECRBY.ts @@ -2,6 +2,12 @@ import { CommandParser } from '../client/parser'; import { RedisArgument, NumberReply, Command } from '../RESP/types'; export default { + /** + * Decrements the integer value of a key by the given number + * @param parser - The Redis command parser + * @param key - Key to decrement + * @param decrement - Decrement amount + */ parseCommand(parser: CommandParser, key: RedisArgument, decrement: number) { parser.push('DECRBY'); parser.pushKey(key); diff --git a/packages/client/lib/commands/DEL.ts b/packages/client/lib/commands/DEL.ts index da0803f4d1b..7ad1b1160e7 100644 --- a/packages/client/lib/commands/DEL.ts +++ b/packages/client/lib/commands/DEL.ts @@ -4,6 +4,11 @@ import { RedisVariadicArgument } from './generic-transformers'; export default { IS_READ_ONLY: false, + /** + * Removes the specified keys. A key is ignored if it does not exist + * @param parser - The Redis command parser + * @param keys - One or more keys to delete + */ parseCommand(parser: CommandParser, keys: RedisVariadicArgument) { parser.push('DEL'); parser.pushKeys(keys); diff --git a/packages/client/lib/commands/DISCARD.ts b/packages/client/lib/commands/DISCARD.ts index 1d30191c13d..d8c8c83791e 100644 --- a/packages/client/lib/commands/DISCARD.ts +++ b/packages/client/lib/commands/DISCARD.ts @@ -2,6 +2,10 @@ import { CommandParser } from '../client/parser'; import { SimpleStringReply, Command } from '../RESP/types'; export default { + /** + * Discards a transaction, forgetting all queued commands + * @param parser - The Redis command parser + */ parseCommand(parser: CommandParser) { parser.push('DISCARD'); }, diff --git a/packages/client/lib/commands/DUMP.ts b/packages/client/lib/commands/DUMP.ts index e442c1cdb2f..c4905cc71c4 100644 --- a/packages/client/lib/commands/DUMP.ts +++ b/packages/client/lib/commands/DUMP.ts @@ -3,6 +3,11 @@ import { RedisArgument, BlobStringReply, Command } from '../RESP/types'; export default { IS_READ_ONLY: true, + /** + * Returns a serialized version of the value stored at the key + * @param parser - The Redis command parser + * @param key - Key to dump + */ parseCommand(parser: CommandParser, key: RedisArgument) { parser.push('DUMP'); parser.pushKey(key); diff --git a/packages/client/lib/commands/ECHO.ts b/packages/client/lib/commands/ECHO.ts index 7935bdc0101..b346ade50b1 100644 --- a/packages/client/lib/commands/ECHO.ts +++ b/packages/client/lib/commands/ECHO.ts @@ -4,6 +4,11 @@ import { RedisArgument, BlobStringReply, Command } from '../RESP/types'; export default { NOT_KEYED_COMMAND: true, IS_READ_ONLY: true, + /** + * Returns the given string + * @param parser - The Redis command parser + * @param message - Message to echo back + */ parseCommand(parser: CommandParser, message: RedisArgument) { parser.push('ECHO', message); }, diff --git a/packages/client/lib/commands/EVAL.ts b/packages/client/lib/commands/EVAL.ts index cdb8025b0be..ff244c82aaf 100644 --- a/packages/client/lib/commands/EVAL.ts +++ b/packages/client/lib/commands/EVAL.ts @@ -25,6 +25,12 @@ export function parseEvalArguments( export default { IS_READ_ONLY: false, + /** + * Executes a Lua script server side + * @param parser - The Redis command parser + * @param script - Lua script to execute + * @param options - Script execution options including keys and arguments + */ parseCommand(...args: Parameters) { args[0].push('EVAL'); parseEvalArguments(...args); diff --git a/packages/client/lib/commands/EVALSHA.ts b/packages/client/lib/commands/EVALSHA.ts index 5a9cc771358..29bb6ffdfcb 100644 --- a/packages/client/lib/commands/EVALSHA.ts +++ b/packages/client/lib/commands/EVALSHA.ts @@ -3,6 +3,12 @@ import EVAL, { parseEvalArguments } from './EVAL'; export default { IS_READ_ONLY: false, + /** + * Executes a Lua script server side using the script's SHA1 digest + * @param parser - The Redis command parser + * @param sha1 - SHA1 digest of the script + * @param options - Script execution options including keys and arguments + */ parseCommand(...args: Parameters) { args[0].push('EVALSHA'); parseEvalArguments(...args); diff --git a/packages/client/lib/commands/EVALSHA_RO.ts b/packages/client/lib/commands/EVALSHA_RO.ts index 24fadb3f486..628ca3dee53 100644 --- a/packages/client/lib/commands/EVALSHA_RO.ts +++ b/packages/client/lib/commands/EVALSHA_RO.ts @@ -3,6 +3,12 @@ import EVAL, { parseEvalArguments } from './EVAL'; export default { IS_READ_ONLY: true, + /** + * Executes a read-only Lua script server side using the script's SHA1 digest + * @param parser - The Redis command parser + * @param sha1 - SHA1 digest of the script + * @param options - Script execution options including keys and arguments + */ parseCommand(...args: Parameters) { args[0].push('EVALSHA_RO'); parseEvalArguments(...args); diff --git a/packages/client/lib/commands/EVAL_RO.ts b/packages/client/lib/commands/EVAL_RO.ts index 2438fd9d1dd..803c4f840c5 100644 --- a/packages/client/lib/commands/EVAL_RO.ts +++ b/packages/client/lib/commands/EVAL_RO.ts @@ -3,6 +3,12 @@ import EVAL, { parseEvalArguments } from './EVAL'; export default { IS_READ_ONLY: true, + /** + * Executes a read-only Lua script server side + * @param parser - The Redis command parser + * @param script - Lua script to execute + * @param options - Script execution options including keys and arguments + */ parseCommand(...args: Parameters) { args[0].push('EVAL_RO'); parseEvalArguments(...args); diff --git a/packages/client/lib/commands/EXISTS.ts b/packages/client/lib/commands/EXISTS.ts index 8ebb28269fe..ea6ea8cb0cd 100644 --- a/packages/client/lib/commands/EXISTS.ts +++ b/packages/client/lib/commands/EXISTS.ts @@ -5,6 +5,11 @@ import { RedisVariadicArgument } from './generic-transformers'; export default { CACHEABLE: true, IS_READ_ONLY: true, + /** + * Determines if the specified keys exist + * @param parser - The Redis command parser + * @param keys - One or more keys to check + */ parseCommand(parser: CommandParser, keys: RedisVariadicArgument) { parser.push('EXISTS'); parser.pushKeys(keys); diff --git a/packages/client/lib/commands/EXPIRE.ts b/packages/client/lib/commands/EXPIRE.ts index 1e73b629492..15855832c32 100644 --- a/packages/client/lib/commands/EXPIRE.ts +++ b/packages/client/lib/commands/EXPIRE.ts @@ -3,6 +3,13 @@ import { RedisArgument, NumberReply, Command } from '../RESP/types'; export default { IS_READ_ONLY: true, + /** + * Sets a timeout on key. After the timeout has expired, the key will be automatically deleted + * @param parser - The Redis command parser + * @param key - Key to set expiration on + * @param seconds - Number of seconds until key expiration + * @param mode - Expiration mode: NX (only if key has no expiry), XX (only if key has existing expiry), GT (only if new expiry is greater than current), LT (only if new expiry is less than current) + */ parseCommand( parser: CommandParser, key: RedisArgument, diff --git a/packages/client/lib/commands/EXPIREAT.ts b/packages/client/lib/commands/EXPIREAT.ts index 5bcb94ea321..4956b8aa23b 100644 --- a/packages/client/lib/commands/EXPIREAT.ts +++ b/packages/client/lib/commands/EXPIREAT.ts @@ -4,6 +4,13 @@ import { transformEXAT } from './generic-transformers'; export default { IS_READ_ONLY: true, + /** + * Sets the expiration for a key at a specific Unix timestamp + * @param parser - The Redis command parser + * @param key - Key to set expiration on + * @param timestamp - Unix timestamp (seconds since January 1, 1970) or Date object + * @param mode - Expiration mode: NX (only if key has no expiry), XX (only if key has existing expiry), GT (only if new expiry is greater than current), LT (only if new expiry is less than current) + */ parseCommand( parser: CommandParser, key: RedisArgument, diff --git a/packages/client/lib/commands/EXPIRETIME.ts b/packages/client/lib/commands/EXPIRETIME.ts index 2bb97fb737b..faa8571eca3 100644 --- a/packages/client/lib/commands/EXPIRETIME.ts +++ b/packages/client/lib/commands/EXPIRETIME.ts @@ -3,6 +3,11 @@ import { RedisArgument, NumberReply, Command } from '../RESP/types'; export default { IS_READ_ONLY: true, + /** + * Returns the absolute Unix timestamp (since January 1, 1970) at which the given key will expire + * @param parser - The Redis command parser + * @param key - Key to check expiration time + */ parseCommand(parser: CommandParser, key: RedisArgument) { parser.push('EXPIRETIME'); parser.pushKey(key); diff --git a/packages/client/lib/commands/FAILOVER.ts b/packages/client/lib/commands/FAILOVER.ts index 1e98b983f96..24fa7a0347b 100644 --- a/packages/client/lib/commands/FAILOVER.ts +++ b/packages/client/lib/commands/FAILOVER.ts @@ -12,6 +12,11 @@ interface FailoverOptions { } export default { + /** + * Starts a coordinated failover between the primary and a replica + * @param parser - The Redis command parser + * @param options - Failover options including target host, abort flag, and timeout + */ parseCommand(parser: CommandParser, options?: FailoverOptions) { parser.push('FAILOVER'); diff --git a/packages/client/lib/commands/FCALL.ts b/packages/client/lib/commands/FCALL.ts index 622060f693c..8fa56d4258e 100644 --- a/packages/client/lib/commands/FCALL.ts +++ b/packages/client/lib/commands/FCALL.ts @@ -3,6 +3,12 @@ import EVAL, { parseEvalArguments } from './EVAL'; export default { IS_READ_ONLY: false, + /** + * Invokes a Redis function + * @param parser - The Redis command parser + * @param functionName - Name of the function to call + * @param options - Function execution options including keys and arguments + */ parseCommand(...args: Parameters) { args[0].push('FCALL'); parseEvalArguments(...args); diff --git a/packages/client/lib/commands/FCALL_RO.ts b/packages/client/lib/commands/FCALL_RO.ts index 95effb0e698..5aac38aed0b 100644 --- a/packages/client/lib/commands/FCALL_RO.ts +++ b/packages/client/lib/commands/FCALL_RO.ts @@ -3,6 +3,12 @@ import EVAL, { parseEvalArguments } from './EVAL'; export default { IS_READ_ONLY: false, + /** + * Invokes a read-only Redis function + * @param parser - The Redis command parser + * @param functionName - Name of the function to call + * @param options - Function execution options including keys and arguments + */ parseCommand(...args: Parameters) { args[0].push('FCALL_RO'); parseEvalArguments(...args); diff --git a/packages/client/lib/commands/FLUSHALL.ts b/packages/client/lib/commands/FLUSHALL.ts index c39535e8864..de6852d57e0 100644 --- a/packages/client/lib/commands/FLUSHALL.ts +++ b/packages/client/lib/commands/FLUSHALL.ts @@ -11,6 +11,11 @@ export type RedisFlushMode = typeof REDIS_FLUSH_MODES[keyof typeof REDIS_FLUSH_M export default { NOT_KEYED_COMMAND: true, IS_READ_ONLY: false, + /** + * Removes all keys from all databases + * @param parser - The Redis command parser + * @param mode - Optional flush mode (ASYNC or SYNC) + */ parseCommand(parser: CommandParser, mode?: RedisFlushMode) { parser.push('FLUSHALL'); if (mode) { diff --git a/packages/client/lib/commands/FLUSHDB.ts b/packages/client/lib/commands/FLUSHDB.ts index 5639f69a611..cd1ac201fce 100644 --- a/packages/client/lib/commands/FLUSHDB.ts +++ b/packages/client/lib/commands/FLUSHDB.ts @@ -5,6 +5,11 @@ import { RedisFlushMode } from './FLUSHALL'; export default { NOT_KEYED_COMMAND: true, IS_READ_ONLY: false, + /** + * Removes all keys from the current database + * @param parser - The Redis command parser + * @param mode - Optional flush mode (ASYNC or SYNC) + */ parseCommand(parser: CommandParser, mode?: RedisFlushMode) { parser.push('FLUSHDB'); if (mode) { diff --git a/packages/client/lib/commands/FUNCTION_DELETE.ts b/packages/client/lib/commands/FUNCTION_DELETE.ts index dbfb044928e..e7b59ecb0cc 100644 --- a/packages/client/lib/commands/FUNCTION_DELETE.ts +++ b/packages/client/lib/commands/FUNCTION_DELETE.ts @@ -4,6 +4,11 @@ import { RedisArgument, SimpleStringReply, Command } from '../RESP/types'; export default { NOT_KEYED_COMMAND: true, IS_READ_ONLY: false, + /** + * Deletes a library and all its functions + * @param parser - The Redis command parser + * @param library - Name of the library to delete + */ parseCommand(parser: CommandParser, library: RedisArgument) { parser.push('FUNCTION', 'DELETE', library); }, diff --git a/packages/client/lib/commands/FUNCTION_DUMP.ts b/packages/client/lib/commands/FUNCTION_DUMP.ts index 2d0dbdd4455..73d6986b707 100644 --- a/packages/client/lib/commands/FUNCTION_DUMP.ts +++ b/packages/client/lib/commands/FUNCTION_DUMP.ts @@ -4,6 +4,10 @@ import { BlobStringReply, Command } from '../RESP/types'; export default { NOT_KEYED_COMMAND: true, IS_READ_ONLY: true, + /** + * Returns a serialized payload representing the current functions loaded in the server + * @param parser - The Redis command parser + */ parseCommand(parser: CommandParser) { parser.push('FUNCTION', 'DUMP') }, diff --git a/packages/client/lib/commands/FUNCTION_FLUSH.ts b/packages/client/lib/commands/FUNCTION_FLUSH.ts index 4ca59e4464e..8019fc0c215 100644 --- a/packages/client/lib/commands/FUNCTION_FLUSH.ts +++ b/packages/client/lib/commands/FUNCTION_FLUSH.ts @@ -5,6 +5,11 @@ import { RedisFlushMode } from './FLUSHALL'; export default { NOT_KEYED_COMMAND: true, IS_READ_ONLY: false, + /** + * Deletes all the libraries and functions from a Redis server + * @param parser - The Redis command parser + * @param mode - Optional flush mode (ASYNC or SYNC) + */ parseCommand(parser: CommandParser, mode?: RedisFlushMode) { parser.push('FUNCTION', 'FLUSH'); diff --git a/packages/client/lib/commands/FUNCTION_KILL.ts b/packages/client/lib/commands/FUNCTION_KILL.ts index 8b5351e93ab..b1626684b62 100644 --- a/packages/client/lib/commands/FUNCTION_KILL.ts +++ b/packages/client/lib/commands/FUNCTION_KILL.ts @@ -4,6 +4,10 @@ import { SimpleStringReply, Command } from '../RESP/types'; export default { NOT_KEYED_COMMAND: true, IS_READ_ONLY: true, + /** + * Kills a function that is currently executing + * @param parser - The Redis command parser + */ parseCommand(parser: CommandParser) { parser.push('FUNCTION', 'KILL'); }, diff --git a/packages/client/lib/commands/FUNCTION_LIST.ts b/packages/client/lib/commands/FUNCTION_LIST.ts index 82e3697eadc..64ebaea8f85 100644 --- a/packages/client/lib/commands/FUNCTION_LIST.ts +++ b/packages/client/lib/commands/FUNCTION_LIST.ts @@ -20,6 +20,11 @@ export type FunctionListReply = ArrayReply) { FUNCTION_LIST.parseCommand(...args); args[0].push('WITHCODE'); diff --git a/packages/client/lib/commands/FUNCTION_LOAD.ts b/packages/client/lib/commands/FUNCTION_LOAD.ts index 40b8ea8c0f4..0766a124afb 100644 --- a/packages/client/lib/commands/FUNCTION_LOAD.ts +++ b/packages/client/lib/commands/FUNCTION_LOAD.ts @@ -8,6 +8,12 @@ export interface FunctionLoadOptions { export default { NOT_KEYED_COMMAND: true, IS_READ_ONLY: false, + /** + * Loads a library to Redis + * @param parser - The Redis command parser + * @param code - Library code to load + * @param options - Function load options + */ parseCommand(parser: CommandParser, code: RedisArgument, options?: FunctionLoadOptions) { parser.push('FUNCTION', 'LOAD'); diff --git a/packages/client/lib/commands/FUNCTION_RESTORE.ts b/packages/client/lib/commands/FUNCTION_RESTORE.ts index 944813f25e5..f18541a614a 100644 --- a/packages/client/lib/commands/FUNCTION_RESTORE.ts +++ b/packages/client/lib/commands/FUNCTION_RESTORE.ts @@ -8,6 +8,12 @@ export interface FunctionRestoreOptions { export default { NOT_KEYED_COMMAND: true, IS_READ_ONLY: false, + /** + * Restores libraries from the dump payload + * @param parser - The Redis command parser + * @param dump - Serialized payload of functions to restore + * @param options - Options for the restore operation + */ parseCommand(parser: CommandParser, dump: RedisArgument, options?: FunctionRestoreOptions) { parser.push('FUNCTION', 'RESTORE', dump); diff --git a/packages/client/lib/commands/FUNCTION_STATS.ts b/packages/client/lib/commands/FUNCTION_STATS.ts index 908be5476e0..77eccf916bd 100644 --- a/packages/client/lib/commands/FUNCTION_STATS.ts +++ b/packages/client/lib/commands/FUNCTION_STATS.ts @@ -23,6 +23,10 @@ type FunctionStatsReply = TuplesToMapReply<[ export default { NOT_KEYED_COMMAND: true, IS_READ_ONLY: true, + /** + * Returns information about the function that is currently running and information about the available execution engines + * @param parser - The Redis command parser + */ parseCommand(parser: CommandParser) { parser.push('FUNCTION', 'STATS'); }, diff --git a/packages/client/lib/commands/GEOADD.ts b/packages/client/lib/commands/GEOADD.ts index 31bf457e158..3da7b0e74b6 100644 --- a/packages/client/lib/commands/GEOADD.ts +++ b/packages/client/lib/commands/GEOADD.ts @@ -21,6 +21,13 @@ export interface GeoAddOptions { export default { IS_READ_ONLY: false, + /** + * Adds geospatial items to the specified key + * @param parser - The Redis command parser + * @param key - Key to add the geospatial items to + * @param toAdd - Geospatial member(s) to add + * @param options - Options for the GEOADD command + */ parseCommand( parser: CommandParser, key: RedisArgument, diff --git a/packages/client/lib/commands/GEODIST.ts b/packages/client/lib/commands/GEODIST.ts index ba4d3080a71..f86d8156ebf 100644 --- a/packages/client/lib/commands/GEODIST.ts +++ b/packages/client/lib/commands/GEODIST.ts @@ -5,6 +5,14 @@ import { GeoUnits } from './GEOSEARCH'; export default { CACHEABLE: true, IS_READ_ONLY: true, + /** + * Returns the distance between two members in a geospatial index + * @param parser - The Redis command parser + * @param key - Key of the geospatial index + * @param member1 - First member in the geospatial index + * @param member2 - Second member in the geospatial index + * @param unit - Unit of distance (m, km, ft, mi) + */ parseCommand(parser: CommandParser, key: RedisArgument, member1: RedisArgument, diff --git a/packages/client/lib/commands/GEOHASH.ts b/packages/client/lib/commands/GEOHASH.ts index c3265d13157..bddc7a1fc0d 100644 --- a/packages/client/lib/commands/GEOHASH.ts +++ b/packages/client/lib/commands/GEOHASH.ts @@ -5,6 +5,12 @@ import { RedisVariadicArgument } from './generic-transformers'; export default { CACHEABLE: true, IS_READ_ONLY: true, + /** + * Returns the Geohash string representation of one or more position members + * @param parser - The Redis command parser + * @param key - Key of the geospatial index + * @param member - One or more members in the geospatial index + */ parseCommand(parser: CommandParser, key: RedisArgument, member: RedisVariadicArgument) { parser.push('GEOHASH'); parser.pushKey(key); diff --git a/packages/client/lib/commands/GEOPOS.ts b/packages/client/lib/commands/GEOPOS.ts index 6bdbb65ac46..6fed2a8abc8 100644 --- a/packages/client/lib/commands/GEOPOS.ts +++ b/packages/client/lib/commands/GEOPOS.ts @@ -5,6 +5,12 @@ import { RedisVariadicArgument } from './generic-transformers'; export default { CACHEABLE: true, IS_READ_ONLY: true, + /** + * Returns the longitude and latitude of one or more members in a geospatial index + * @param parser - The Redis command parser + * @param key - Key of the geospatial index + * @param member - One or more members in the geospatial index + */ parseCommand(parser: CommandParser, key: RedisArgument, member: RedisVariadicArgument) { parser.push('GEOPOS'); parser.pushKey(key); diff --git a/packages/client/lib/commands/GEORADIUS.ts b/packages/client/lib/commands/GEORADIUS.ts index 5e8d880ab5e..2f622415064 100644 --- a/packages/client/lib/commands/GEORADIUS.ts +++ b/packages/client/lib/commands/GEORADIUS.ts @@ -18,6 +18,15 @@ export function parseGeoRadiusArguments( export default { IS_READ_ONLY: false, + /** + * Queries members in a geospatial index based on a radius from a center point + * @param parser - The Redis command parser + * @param key - Key of the geospatial index + * @param from - Center coordinates for the search + * @param radius - Radius of the search area + * @param unit - Unit of distance (m, km, ft, mi) + * @param options - Additional search options + */ parseCommand(...args: Parameters) { args[0].push('GEORADIUS'); return parseGeoRadiusArguments(...args); diff --git a/packages/client/lib/commands/GEORADIUSBYMEMBER.ts b/packages/client/lib/commands/GEORADIUSBYMEMBER.ts index be4ca54650c..ee29ab84115 100644 --- a/packages/client/lib/commands/GEORADIUSBYMEMBER.ts +++ b/packages/client/lib/commands/GEORADIUSBYMEMBER.ts @@ -18,6 +18,15 @@ export function parseGeoRadiusByMemberArguments( export default { IS_READ_ONLY: false, + /** + * Queries members in a geospatial index based on a radius from a member + * @param parser - The Redis command parser + * @param key - Key of the geospatial index + * @param from - Member name to use as center point + * @param radius - Radius of the search area + * @param unit - Unit of distance (m, km, ft, mi) + * @param options - Additional search options + */ parseCommand( parser: CommandParser, key: RedisArgument, diff --git a/packages/client/lib/commands/GEORADIUSBYMEMBER_RO.ts b/packages/client/lib/commands/GEORADIUSBYMEMBER_RO.ts index 335eea08133..8629694588a 100644 --- a/packages/client/lib/commands/GEORADIUSBYMEMBER_RO.ts +++ b/packages/client/lib/commands/GEORADIUSBYMEMBER_RO.ts @@ -4,6 +4,15 @@ import GEORADIUSBYMEMBER, { parseGeoRadiusByMemberArguments } from './GEORADIUSB export default { CACHEABLE: true, IS_READ_ONLY: true, + /** + * Read-only variant that queries members in a geospatial index based on a radius from a member + * @param parser - The Redis command parser + * @param key - Key of the geospatial index + * @param from - Member name to use as center point + * @param radius - Radius of the search area + * @param unit - Unit of distance (m, km, ft, mi) + * @param options - Additional search options + */ parseCommand(...args: Parameters) { const parser = args[0]; parser.push('GEORADIUSBYMEMBER_RO'); diff --git a/packages/client/lib/commands/GEORADIUSBYMEMBER_RO_WITH.ts b/packages/client/lib/commands/GEORADIUSBYMEMBER_RO_WITH.ts index 06835438016..239c6cf2444 100644 --- a/packages/client/lib/commands/GEORADIUSBYMEMBER_RO_WITH.ts +++ b/packages/client/lib/commands/GEORADIUSBYMEMBER_RO_WITH.ts @@ -4,6 +4,15 @@ import GEORADIUSBYMEMBER_WITH, { parseGeoRadiusByMemberWithArguments } from './G export default { CACHEABLE: true, IS_READ_ONLY: true, + /** + * Read-only variant that queries members in a geospatial index based on a radius from a member with additional information + * @param parser - The Redis command parser + * @param key - Key of the geospatial index + * @param from - Member name to use as center point + * @param radius - Radius of the search area + * @param unit - Unit of distance (m, km, ft, mi) + * @param withValues - Information to include with each returned member + */ parseCommand(...args: Parameters) { const parser = args[0]; parser.push('GEORADIUSBYMEMBER_RO'); diff --git a/packages/client/lib/commands/GEORADIUSBYMEMBER_STORE.ts b/packages/client/lib/commands/GEORADIUSBYMEMBER_STORE.ts index 676df34dd5a..20a1e0b6699 100644 --- a/packages/client/lib/commands/GEORADIUSBYMEMBER_STORE.ts +++ b/packages/client/lib/commands/GEORADIUSBYMEMBER_STORE.ts @@ -9,6 +9,16 @@ export interface GeoRadiusStoreOptions extends GeoSearchOptions { export default { IS_READ_ONLY: GEORADIUSBYMEMBER.IS_READ_ONLY, + /** + * Queries members in a geospatial index based on a radius from a member and stores the results + * @param parser - The Redis command parser + * @param key - Key of the geospatial index + * @param from - Member name to use as center point + * @param radius - Radius of the search area + * @param unit - Unit of distance (m, km, ft, mi) + * @param destination - Key to store the results + * @param options - Additional search and storage options + */ parseCommand( parser: CommandParser, key: RedisArgument, diff --git a/packages/client/lib/commands/GEORADIUSBYMEMBER_WITH.ts b/packages/client/lib/commands/GEORADIUSBYMEMBER_WITH.ts index eefae0b27a9..9f7a01bb525 100644 --- a/packages/client/lib/commands/GEORADIUSBYMEMBER_WITH.ts +++ b/packages/client/lib/commands/GEORADIUSBYMEMBER_WITH.ts @@ -23,6 +23,16 @@ export function parseGeoRadiusByMemberWithArguments( export default { IS_READ_ONLY: GEORADIUSBYMEMBER.IS_READ_ONLY, + /** + * Queries members in a geospatial index based on a radius from a member with additional information + * @param parser - The Redis command parser + * @param key - Key of the geospatial index + * @param from - Member name to use as center point + * @param radius - Radius of the search area + * @param unit - Unit of distance (m, km, ft, mi) + * @param replyWith - Information to include with each returned member + * @param options - Additional search options + */ parseCommand( parser: CommandParser, key: RedisArgument, diff --git a/packages/client/lib/commands/GEORADIUS_RO.ts b/packages/client/lib/commands/GEORADIUS_RO.ts index 5db65d9dc9b..29cf6f8ccd7 100644 --- a/packages/client/lib/commands/GEORADIUS_RO.ts +++ b/packages/client/lib/commands/GEORADIUS_RO.ts @@ -4,6 +4,15 @@ import GEORADIUS, { parseGeoRadiusArguments } from './GEORADIUS'; export default { CACHEABLE: true, IS_READ_ONLY: true, + /** + * Read-only variant that queries members in a geospatial index based on a radius from a center point + * @param parser - The Redis command parser + * @param key - Key of the geospatial index + * @param from - Center coordinates for the search + * @param radius - Radius of the search area + * @param unit - Unit of distance (m, km, ft, mi) + * @param options - Additional search options + */ parseCommand(...args: Parameters) { args[0].push('GEORADIUS_RO'); parseGeoRadiusArguments(...args); diff --git a/packages/client/lib/commands/GEORADIUS_RO_WITH.ts b/packages/client/lib/commands/GEORADIUS_RO_WITH.ts index cee1679382b..aaaef482f05 100644 --- a/packages/client/lib/commands/GEORADIUS_RO_WITH.ts +++ b/packages/client/lib/commands/GEORADIUS_RO_WITH.ts @@ -5,6 +5,16 @@ import GEORADIUS_WITH from './GEORADIUS_WITH'; export default { CACHEABLE: true, IS_READ_ONLY: true, + /** + * Read-only variant that queries members in a geospatial index based on a radius from a center point with additional information + * @param parser - The Redis command parser + * @param key - Key of the geospatial index + * @param from - Center coordinates for the search + * @param radius - Radius of the search area + * @param unit - Unit of distance (m, km, ft, mi) + * @param replyWith - Information to include with each returned member + * @param options - Additional search options + */ parseCommand(...args: Parameters) { args[0].push('GEORADIUS_RO'); parseGeoRadiusWithArguments(...args); diff --git a/packages/client/lib/commands/GEORADIUS_STORE.ts b/packages/client/lib/commands/GEORADIUS_STORE.ts index 18459d44217..b2db8ca9882 100644 --- a/packages/client/lib/commands/GEORADIUS_STORE.ts +++ b/packages/client/lib/commands/GEORADIUS_STORE.ts @@ -9,6 +9,16 @@ export interface GeoRadiusStoreOptions extends GeoSearchOptions { export default { IS_READ_ONLY: GEORADIUS.IS_READ_ONLY, + /** + * Queries members in a geospatial index based on a radius from a center point and stores the results + * @param parser - The Redis command parser + * @param key - Key of the geospatial index + * @param from - Center coordinates for the search + * @param radius - Radius of the search area + * @param unit - Unit of distance (m, km, ft, mi) + * @param destination - Key to store the results + * @param options - Additional search and storage options + */ parseCommand( parser: CommandParser, key: RedisArgument, diff --git a/packages/client/lib/commands/GEORADIUS_WITH.ts b/packages/client/lib/commands/GEORADIUS_WITH.ts index ac4c8b7bb1b..5028a926145 100644 --- a/packages/client/lib/commands/GEORADIUS_WITH.ts +++ b/packages/client/lib/commands/GEORADIUS_WITH.ts @@ -20,6 +20,16 @@ export function parseGeoRadiusWithArguments( export default { IS_READ_ONLY: GEORADIUS.IS_READ_ONLY, + /** + * Queries members in a geospatial index based on a radius from a center point with additional information + * @param parser - The Redis command parser + * @param key - Key of the geospatial index + * @param from - Center coordinates for the search + * @param radius - Radius of the search area + * @param unit - Unit of distance (m, km, ft, mi) + * @param replyWith - Information to include with each returned member + * @param options - Additional search options + */ parseCommand( parser: CommandParser, key: RedisArgument, diff --git a/packages/client/lib/commands/GEOSEARCH.ts b/packages/client/lib/commands/GEOSEARCH.ts index 869dc60bec9..a26ccec23eb 100644 --- a/packages/client/lib/commands/GEOSEARCH.ts +++ b/packages/client/lib/commands/GEOSEARCH.ts @@ -80,6 +80,14 @@ export function parseGeoSearchOptions( export default { IS_READ_ONLY: true, + /** + * Queries members inside an area of a geospatial index + * @param parser - The Redis command parser + * @param key - Key of the geospatial index + * @param from - Center point of the search (member name or coordinates) + * @param by - Search area specification (radius or box dimensions) + * @param options - Additional search options + */ parseCommand( parser: CommandParser, key: RedisArgument, diff --git a/packages/client/lib/commands/GEOSEARCHSTORE.ts b/packages/client/lib/commands/GEOSEARCHSTORE.ts index 34c6e0988e2..194eafda818 100644 --- a/packages/client/lib/commands/GEOSEARCHSTORE.ts +++ b/packages/client/lib/commands/GEOSEARCHSTORE.ts @@ -8,6 +8,15 @@ export interface GeoSearchStoreOptions extends GeoSearchOptions { export default { IS_READ_ONLY: false, + /** + * Searches a geospatial index and stores the results in a new sorted set + * @param parser - The Redis command parser + * @param destination - Key to store the results + * @param source - Key of the geospatial index to search + * @param from - Center point of the search (member name or coordinates) + * @param by - Search area specification (radius or box dimensions) + * @param options - Additional search and storage options + */ parseCommand( parser: CommandParser, destination: RedisArgument, diff --git a/packages/client/lib/commands/GEOSEARCH_WITH.ts b/packages/client/lib/commands/GEOSEARCH_WITH.ts index 65e3975b72f..dca125a816e 100644 --- a/packages/client/lib/commands/GEOSEARCH_WITH.ts +++ b/packages/client/lib/commands/GEOSEARCH_WITH.ts @@ -22,6 +22,15 @@ export interface GeoReplyWithMember { export default { IS_READ_ONLY: GEOSEARCH.IS_READ_ONLY, + /** + * Queries members inside an area of a geospatial index with additional information + * @param parser - The Redis command parser + * @param key - Key of the geospatial index + * @param from - Center point of the search (member name or coordinates) + * @param by - Search area specification (radius or box dimensions) + * @param replyWith - Information to include with each returned member + * @param options - Additional search options + */ parseCommand( parser: CommandParser, key: RedisArgument, diff --git a/packages/client/lib/commands/GET.ts b/packages/client/lib/commands/GET.ts index ca013752ae5..e55c900eea2 100644 --- a/packages/client/lib/commands/GET.ts +++ b/packages/client/lib/commands/GET.ts @@ -4,6 +4,11 @@ import { RedisArgument, BlobStringReply, NullReply, Command } from '../RESP/type export default { CACHEABLE: true, IS_READ_ONLY: true, + /** + * Gets the value of a key + * @param parser - The Redis command parser + * @param key - Key to get the value of + */ parseCommand(parser: CommandParser, key: RedisArgument) { parser.push('GET'); parser.pushKey(key); diff --git a/packages/client/lib/commands/GETBIT.ts b/packages/client/lib/commands/GETBIT.ts index 023ba0fb607..7d4a240473f 100644 --- a/packages/client/lib/commands/GETBIT.ts +++ b/packages/client/lib/commands/GETBIT.ts @@ -5,6 +5,12 @@ import { BitValue } from './generic-transformers'; export default { CACHEABLE: true, IS_READ_ONLY: true, + /** + * Returns the bit value at a given offset in a string value + * @param parser - The Redis command parser + * @param key - Key to retrieve the bit from + * @param offset - Bit offset + */ parseCommand(parser: CommandParser, key: RedisArgument, offset: number) { parser.push('GETBIT'); parser.pushKey(key); diff --git a/packages/client/lib/commands/GETDEL.ts b/packages/client/lib/commands/GETDEL.ts index a39014109f1..7dbdc7d2535 100644 --- a/packages/client/lib/commands/GETDEL.ts +++ b/packages/client/lib/commands/GETDEL.ts @@ -3,6 +3,11 @@ import { RedisArgument, BlobStringReply, NullReply, Command } from '../RESP/type export default { IS_READ_ONLY: true, + /** + * Gets the value of a key and deletes the key + * @param parser - The Redis command parser + * @param key - Key to get and delete + */ parseCommand(parser: CommandParser, key: RedisArgument) { parser.push('GETDEL'); parser.pushKey(key); diff --git a/packages/client/lib/commands/GETEX.ts b/packages/client/lib/commands/GETEX.ts index e5ae0b691a7..836c2b5effe 100644 --- a/packages/client/lib/commands/GETEX.ts +++ b/packages/client/lib/commands/GETEX.ts @@ -39,6 +39,12 @@ export type GetExOptions = { export default { IS_READ_ONLY: true, + /** + * Gets the value of a key and optionally sets its expiration + * @param parser - The Redis command parser + * @param key - Key to get value from + * @param options - Options for setting expiration + */ parseCommand(parser: CommandParser, key: RedisArgument, options: GetExOptions) { parser.push('GETEX'); parser.pushKey(key); diff --git a/packages/client/lib/commands/GETRANGE.ts b/packages/client/lib/commands/GETRANGE.ts index ce0db6e3c03..f5f1586f0a9 100644 --- a/packages/client/lib/commands/GETRANGE.ts +++ b/packages/client/lib/commands/GETRANGE.ts @@ -4,6 +4,13 @@ import { RedisArgument, BlobStringReply, NullReply, Command } from '../RESP/type export default { CACHEABLE: true, IS_READ_ONLY: true, + /** + * Returns a substring of the string stored at a key + * @param parser - The Redis command parser + * @param key - Key to get substring from + * @param start - Start position of the substring + * @param end - End position of the substring + */ parseCommand(parser: CommandParser, key: RedisArgument, start: number, end: number) { parser.push('GETRANGE'); parser.pushKey(key); diff --git a/packages/client/lib/commands/GETSET.ts b/packages/client/lib/commands/GETSET.ts index 1b3312548e4..1b9d98f90d7 100644 --- a/packages/client/lib/commands/GETSET.ts +++ b/packages/client/lib/commands/GETSET.ts @@ -3,6 +3,12 @@ import { RedisArgument, BlobStringReply, NullReply, Command } from '../RESP/type export default { IS_READ_ONLY: true, + /** + * Sets a key to a new value and returns its old value + * @param parser - The Redis command parser + * @param key - Key to set + * @param value - Value to set + */ parseCommand(parser: CommandParser, key: RedisArgument, value: RedisArgument) { parser.push('GETSET'); parser.pushKey(key); diff --git a/packages/client/lib/commands/HDEL.ts b/packages/client/lib/commands/HDEL.ts index 713d19a9b2a..cc5c4dab1b9 100644 --- a/packages/client/lib/commands/HDEL.ts +++ b/packages/client/lib/commands/HDEL.ts @@ -3,6 +3,12 @@ import { RedisArgument, NumberReply, Command } from '../RESP/types'; import { RedisVariadicArgument } from './generic-transformers'; export default { + /** + * Removes one or more fields from a hash + * @param parser - The Redis command parser + * @param key - Key of the hash + * @param field - Field(s) to remove + */ parseCommand(parser: CommandParser, key: RedisArgument, field: RedisVariadicArgument) { parser.push('HDEL'); parser.pushKey(key); diff --git a/packages/client/lib/commands/HELLO.ts b/packages/client/lib/commands/HELLO.ts index 5d25998f987..23feaad554a 100644 --- a/packages/client/lib/commands/HELLO.ts +++ b/packages/client/lib/commands/HELLO.ts @@ -21,6 +21,12 @@ export type HelloReply = TuplesToMapReply<[ ]>; export default { + /** + * Handshakes with the Redis server and switches to the specified protocol version + * @param parser - The Redis command parser + * @param protover - Protocol version to use + * @param options - Additional options for authentication and connection naming + */ parseCommand(parser: CommandParser, protover?: RespVersions, options?: HelloOptions) { parser.push('HELLO'); diff --git a/packages/client/lib/commands/HEXISTS.ts b/packages/client/lib/commands/HEXISTS.ts index 9bb517b7df4..50b8f1ae8c2 100644 --- a/packages/client/lib/commands/HEXISTS.ts +++ b/packages/client/lib/commands/HEXISTS.ts @@ -4,6 +4,12 @@ import { RedisArgument, NumberReply, Command } from '../RESP/types'; export default { CACHEABLE: true, IS_READ_ONLY: true, + /** + * Determines whether a field exists in a hash + * @param parser - The Redis command parser + * @param key - Key of the hash + * @param field - Field to check + */ parseCommand(parser: CommandParser, key: RedisArgument, field: RedisArgument) { parser.push('HEXISTS'); parser.pushKey(key); diff --git a/packages/client/lib/commands/HEXPIRE.ts b/packages/client/lib/commands/HEXPIRE.ts index 55e2f5a9be1..95ee58eac1d 100644 --- a/packages/client/lib/commands/HEXPIRE.ts +++ b/packages/client/lib/commands/HEXPIRE.ts @@ -16,6 +16,14 @@ export const HASH_EXPIRATION = { export type HashExpiration = typeof HASH_EXPIRATION[keyof typeof HASH_EXPIRATION]; export default { + /** + * Sets a timeout on hash fields. After the timeout has expired, the fields will be automatically deleted + * @param parser - The Redis command parser + * @param key - Key of the hash + * @param fields - Fields to set expiration on + * @param seconds - Number of seconds until field expiration + * @param mode - Expiration mode: NX (only if field has no expiry), XX (only if field has existing expiry), GT (only if new expiry is greater than current), LT (only if new expiry is less than current) + */ parseCommand( parser: CommandParser, key: RedisArgument, diff --git a/packages/client/lib/commands/HEXPIREAT.ts b/packages/client/lib/commands/HEXPIREAT.ts index 1370f2ecd65..c09efd4aa34 100644 --- a/packages/client/lib/commands/HEXPIREAT.ts +++ b/packages/client/lib/commands/HEXPIREAT.ts @@ -3,6 +3,14 @@ import { RedisVariadicArgument, transformEXAT } from './generic-transformers'; import { ArrayReply, Command, NumberReply, RedisArgument } from '../RESP/types'; export default { + /** + * Sets the expiration for hash fields at a specific Unix timestamp + * @param parser - The Redis command parser + * @param key - Key of the hash + * @param fields - Fields to set expiration on + * @param timestamp - Unix timestamp (seconds since January 1, 1970) or Date object + * @param mode - Expiration mode: NX (only if field has no expiry), XX (only if field has existing expiry), GT (only if new expiry is greater than current), LT (only if new expiry is less than current) + */ parseCommand( parser: CommandParser, key: RedisArgument, diff --git a/packages/client/lib/commands/HEXPIRETIME.ts b/packages/client/lib/commands/HEXPIRETIME.ts index 697d327db16..94504935090 100644 --- a/packages/client/lib/commands/HEXPIRETIME.ts +++ b/packages/client/lib/commands/HEXPIRETIME.ts @@ -11,6 +11,12 @@ export const HASH_EXPIRATION_TIME = { export default { IS_READ_ONLY: true, + /** + * Returns the absolute Unix timestamp (since January 1, 1970) at which the given hash fields will expire + * @param parser - The Redis command parser + * @param key - Key of the hash + * @param fields - Fields to check expiration time + */ parseCommand( parser: CommandParser, key: RedisArgument, diff --git a/packages/client/lib/commands/HGET.ts b/packages/client/lib/commands/HGET.ts index fcd9334eb0a..8c4d690992b 100644 --- a/packages/client/lib/commands/HGET.ts +++ b/packages/client/lib/commands/HGET.ts @@ -4,6 +4,12 @@ import { RedisArgument, BlobStringReply, NullReply, Command } from '../RESP/type export default { CACHEABLE: true, IS_READ_ONLY: true, + /** + * Gets the value of a field in a hash + * @param parser - The Redis command parser + * @param key - Key of the hash + * @param field - Field to get the value of + */ parseCommand(parser: CommandParser, key: RedisArgument, field: RedisArgument) { parser.push('HGET'); parser.pushKey(key); diff --git a/packages/client/lib/commands/HGETALL.ts b/packages/client/lib/commands/HGETALL.ts index 8d53669cdd4..13238ab6ea3 100644 --- a/packages/client/lib/commands/HGETALL.ts +++ b/packages/client/lib/commands/HGETALL.ts @@ -5,6 +5,11 @@ import { transformTuplesReply } from './generic-transformers'; export default { CACHEABLE: true, IS_READ_ONLY: true, + /** + * Gets all fields and values in a hash + * @param parser - The Redis command parser + * @param key - Key of the hash + */ parseCommand(parser: CommandParser, key: RedisArgument) { parser.push('HGETALL'); parser.pushKey(key); diff --git a/packages/client/lib/commands/HGETDEL.ts b/packages/client/lib/commands/HGETDEL.ts index a0326c425ea..8b55cae3ed5 100644 --- a/packages/client/lib/commands/HGETDEL.ts +++ b/packages/client/lib/commands/HGETDEL.ts @@ -3,6 +3,12 @@ import { RedisVariadicArgument } from './generic-transformers'; import { RedisArgument, ArrayReply, BlobStringReply, NullReply, Command } from '../RESP/types'; export default { + /** + * Gets and deletes the specified fields from a hash + * @param parser - The Redis command parser + * @param key - Key of the hash + * @param fields - Fields to get and delete + */ parseCommand(parser: CommandParser, key: RedisArgument, fields: RedisVariadicArgument) { parser.push('HGETDEL'); parser.pushKey(key); diff --git a/packages/client/lib/commands/HGETEX.ts b/packages/client/lib/commands/HGETEX.ts index ce265e15bd6..6b039575a27 100644 --- a/packages/client/lib/commands/HGETEX.ts +++ b/packages/client/lib/commands/HGETEX.ts @@ -12,6 +12,13 @@ export interface HGetExOptions { } export default { + /** + * Gets the values of the specified fields in a hash and optionally sets their expiration + * @param parser - The Redis command parser + * @param key - Key of the hash + * @param fields - Fields to get values from + * @param options - Options for setting expiration + */ parseCommand( parser: CommandParser, key: RedisArgument, diff --git a/packages/client/lib/commands/HINCRBY.ts b/packages/client/lib/commands/HINCRBY.ts index 3638e408f7d..cb028315f4c 100644 --- a/packages/client/lib/commands/HINCRBY.ts +++ b/packages/client/lib/commands/HINCRBY.ts @@ -2,6 +2,13 @@ import { CommandParser } from '../client/parser'; import { RedisArgument, NumberReply, Command } from '../RESP/types'; export default { + /** + * Increments the integer value of a field in a hash by the given number + * @param parser - The Redis command parser + * @param key - Key of the hash + * @param field - Field to increment + * @param increment - Increment amount + */ parseCommand( parser: CommandParser, key: RedisArgument, diff --git a/packages/client/lib/commands/HINCRBYFLOAT.ts b/packages/client/lib/commands/HINCRBYFLOAT.ts index 6d527583c71..6d85fa50432 100644 --- a/packages/client/lib/commands/HINCRBYFLOAT.ts +++ b/packages/client/lib/commands/HINCRBYFLOAT.ts @@ -2,6 +2,13 @@ import { CommandParser } from '../client/parser'; import { RedisArgument, BlobStringReply, Command } from '../RESP/types'; export default { + /** + * Increments the float value of a field in a hash by the given amount + * @param parser - The Redis command parser + * @param key - Key of the hash + * @param field - Field to increment + * @param increment - Increment amount (float) + */ parseCommand( parser: CommandParser, key: RedisArgument, diff --git a/packages/client/lib/commands/HKEYS.ts b/packages/client/lib/commands/HKEYS.ts index f07a1ac127f..bf2783eb2dc 100644 --- a/packages/client/lib/commands/HKEYS.ts +++ b/packages/client/lib/commands/HKEYS.ts @@ -4,6 +4,11 @@ import { RedisArgument, ArrayReply, BlobStringReply, Command } from '../RESP/typ export default { CACHEABLE: true, IS_READ_ONLY: true, + /** + * Gets all field names in a hash + * @param parser - The Redis command parser + * @param key - Key of the hash + */ parseCommand(parser: CommandParser, key: RedisArgument) { parser.push('HKEYS') parser.pushKey(key); diff --git a/packages/client/lib/commands/HLEN.ts b/packages/client/lib/commands/HLEN.ts index e3b89da3e7d..7ffbdeee9d6 100644 --- a/packages/client/lib/commands/HLEN.ts +++ b/packages/client/lib/commands/HLEN.ts @@ -4,6 +4,11 @@ import { RedisArgument, NumberReply, Command } from '../RESP/types'; export default { CACHEABLE: true, IS_READ_ONLY: true, + /** + * Gets the number of fields in a hash. + * @param parser - The Redis command parser. + * @param key - Key of the hash. + */ parseCommand(parser: CommandParser, key: RedisArgument) { parser.push('HLEN'); parser.pushKey(key); diff --git a/packages/client/lib/commands/HMGET.ts b/packages/client/lib/commands/HMGET.ts index 51ba937339f..18a7baa219e 100644 --- a/packages/client/lib/commands/HMGET.ts +++ b/packages/client/lib/commands/HMGET.ts @@ -5,6 +5,12 @@ import { RedisVariadicArgument } from './generic-transformers'; export default { CACHEABLE: true, IS_READ_ONLY: true, + /** + * Gets the values of all the specified fields in a hash. + * @param parser - The Redis command parser. + * @param key - Key of the hash. + * @param fields - Fields to get from the hash. + */ parseCommand(parser: CommandParser, key: RedisArgument, fields: RedisVariadicArgument) { parser.push('HMGET'); parser.pushKey(key); diff --git a/packages/client/lib/commands/HPERSIST.ts b/packages/client/lib/commands/HPERSIST.ts index fd0f320e65a..00ab1f4b4b5 100644 --- a/packages/client/lib/commands/HPERSIST.ts +++ b/packages/client/lib/commands/HPERSIST.ts @@ -3,6 +3,12 @@ import { ArrayReply, Command, NullReply, NumberReply, RedisArgument } from '../R import { RedisVariadicArgument } from './generic-transformers'; export default { + /** + * Removes the expiration from the specified fields in a hash. + * @param parser - The Redis command parser. + * @param key - Key of the hash. + * @param fields - Fields to remove expiration from. + */ parseCommand( parser: CommandParser, key: RedisArgument, diff --git a/packages/client/lib/commands/HPEXPIRE.ts b/packages/client/lib/commands/HPEXPIRE.ts index 34513c34e3b..2e20c96bb13 100644 --- a/packages/client/lib/commands/HPEXPIRE.ts +++ b/packages/client/lib/commands/HPEXPIRE.ts @@ -4,6 +4,15 @@ import { RedisVariadicArgument } from './generic-transformers'; import { HashExpiration } from './HEXPIRE'; export default { + /** + * Parses the arguments for the `HPEXPIRE` command. + * + * @param parser - The command parser instance. + * @param key - The key of the hash. + * @param fields - The fields to set the expiration for. + * @param ms - The expiration time in milliseconds. + * @param mode - Optional mode for the command ('NX', 'XX', 'GT', 'LT'). + */ parseCommand( parser: CommandParser, key: RedisArgument, diff --git a/packages/client/lib/commands/HPEXPIREAT.ts b/packages/client/lib/commands/HPEXPIREAT.ts index 14288d7ae90..58fedc84765 100644 --- a/packages/client/lib/commands/HPEXPIREAT.ts +++ b/packages/client/lib/commands/HPEXPIREAT.ts @@ -5,6 +5,15 @@ import { HashExpiration } from './HEXPIRE'; export default { IS_READ_ONLY: true, + /** + * Parses the arguments for the `HPEXPIREAT` command. + * + * @param parser - The command parser instance. + * @param key - The key of the hash. + * @param fields - The fields to set the expiration for. + * @param timestamp - The expiration timestamp (Unix timestamp or Date object). + * @param mode - Optional mode for the command ('NX', 'XX', 'GT', 'LT'). + */ parseCommand( parser: CommandParser, key: RedisArgument, diff --git a/packages/client/lib/commands/HPEXPIRETIME.ts b/packages/client/lib/commands/HPEXPIRETIME.ts index cacce25a85f..d27a15749ae 100644 --- a/packages/client/lib/commands/HPEXPIRETIME.ts +++ b/packages/client/lib/commands/HPEXPIRETIME.ts @@ -4,6 +4,14 @@ import { RedisVariadicArgument } from './generic-transformers'; export default { IS_READ_ONLY: true, + /** + * Constructs the HPEXPIRETIME command + * + * @param parser - The command parser + * @param key - The key to retrieve expiration time for + * @param fields - The fields to retrieve expiration time for + * @see https://redis.io/commands/hpexpiretime/ + */ parseCommand( parser: CommandParser, key: RedisArgument, diff --git a/packages/client/lib/commands/HPTTL.ts b/packages/client/lib/commands/HPTTL.ts index b9cd54a850d..f177e6b5a02 100644 --- a/packages/client/lib/commands/HPTTL.ts +++ b/packages/client/lib/commands/HPTTL.ts @@ -4,6 +4,14 @@ import { RedisVariadicArgument } from './generic-transformers'; export default { IS_READ_ONLY: true, + /** + * Constructs the HPTTL command + * + * @param parser - The command parser + * @param key - The key to check time-to-live for + * @param fields - The fields to check time-to-live for + * @see https://redis.io/commands/hpttl/ + */ parseCommand( parser: CommandParser, key: RedisArgument, diff --git a/packages/client/lib/commands/HRANDFIELD.ts b/packages/client/lib/commands/HRANDFIELD.ts index 3383b94dcb2..38cc34dcee5 100644 --- a/packages/client/lib/commands/HRANDFIELD.ts +++ b/packages/client/lib/commands/HRANDFIELD.ts @@ -3,6 +3,13 @@ import { RedisArgument, BlobStringReply, NullReply, Command } from '../RESP/type export default { IS_READ_ONLY: true, + /** + * Constructs the HRANDFIELD command + * + * @param parser - The command parser + * @param key - The key of the hash to get a random field from + * @see https://redis.io/commands/hrandfield/ + */ parseCommand(parser: CommandParser, key: RedisArgument) { parser.push('HRANDFIELD'); parser.pushKey(key); diff --git a/packages/client/lib/commands/HRANDFIELD_COUNT.ts b/packages/client/lib/commands/HRANDFIELD_COUNT.ts index 62abe97e350..99c5cb0dada 100644 --- a/packages/client/lib/commands/HRANDFIELD_COUNT.ts +++ b/packages/client/lib/commands/HRANDFIELD_COUNT.ts @@ -3,6 +3,14 @@ import { RedisArgument, ArrayReply, BlobStringReply, Command } from '../RESP/typ export default { IS_READ_ONLY: true, + /** + * Constructs the HRANDFIELD command with count parameter + * + * @param parser - The command parser + * @param key - The key of the hash to get random fields from + * @param count - The number of fields to return (positive: unique fields, negative: may repeat fields) + * @see https://redis.io/commands/hrandfield/ + */ parseCommand(parser: CommandParser, key: RedisArgument, count: number) { parser.push('HRANDFIELD'); parser.pushKey(key); diff --git a/packages/client/lib/commands/HRANDFIELD_COUNT_WITHVALUES.ts b/packages/client/lib/commands/HRANDFIELD_COUNT_WITHVALUES.ts index aa8ebad1b93..e247006c6a7 100644 --- a/packages/client/lib/commands/HRANDFIELD_COUNT_WITHVALUES.ts +++ b/packages/client/lib/commands/HRANDFIELD_COUNT_WITHVALUES.ts @@ -8,6 +8,14 @@ export type HRandFieldCountWithValuesReply = Array<{ export default { IS_READ_ONLY: true, + /** + * Constructs the HRANDFIELD command with count parameter and WITHVALUES option + * + * @param parser - The command parser + * @param key - The key of the hash to get random fields from + * @param count - The number of fields to return (positive: unique fields, negative: may repeat fields) + * @see https://redis.io/commands/hrandfield/ + */ parseCommand(parser: CommandParser, key: RedisArgument, count: number) { parser.push('HRANDFIELD'); parser.pushKey(key); diff --git a/packages/client/lib/commands/HSCAN.ts b/packages/client/lib/commands/HSCAN.ts index e1e40663a07..78141814ff1 100644 --- a/packages/client/lib/commands/HSCAN.ts +++ b/packages/client/lib/commands/HSCAN.ts @@ -9,6 +9,15 @@ export interface HScanEntry { export default { IS_READ_ONLY: true, + /** + * Constructs the HSCAN command + * + * @param parser - The command parser + * @param key - The key of the hash to scan + * @param cursor - The cursor position to start scanning from + * @param options - Options for the scan (COUNT, MATCH, TYPE) + * @see https://redis.io/commands/hscan/ + */ parseCommand( parser: CommandParser, key: RedisArgument, diff --git a/packages/client/lib/commands/HSCAN_NOVALUES.ts b/packages/client/lib/commands/HSCAN_NOVALUES.ts index eff61a7aab0..8f7afe52b8e 100644 --- a/packages/client/lib/commands/HSCAN_NOVALUES.ts +++ b/packages/client/lib/commands/HSCAN_NOVALUES.ts @@ -3,6 +3,12 @@ import HSCAN from './HSCAN'; export default { IS_READ_ONLY: true, + /** + * Constructs the HSCAN command with NOVALUES option + * + * @param args - The same parameters as HSCAN command + * @see https://redis.io/commands/hscan/ + */ parseCommand(...args: Parameters) { const parser = args[0]; diff --git a/packages/client/lib/commands/HSET.ts b/packages/client/lib/commands/HSET.ts index 1f50aeacf03..7dc4da8d3cf 100644 --- a/packages/client/lib/commands/HSET.ts +++ b/packages/client/lib/commands/HSET.ts @@ -18,6 +18,15 @@ type MultipleFieldsArguments = [...generic: GenericArguments, value: HSETObject export type HSETArguments = SingleFieldArguments | MultipleFieldsArguments; export default { + /** + * Constructs the HSET command + * + * @param parser - The command parser + * @param key - The key of the hash + * @param value - Either the field name (when using single field) or an object/map/array of field-value pairs + * @param fieldValue - The value to set (only used with single field variant) + * @see https://redis.io/commands/hset/ + */ parseCommand(parser: CommandParser, ...[key, value, fieldValue]: SingleFieldArguments | MultipleFieldsArguments) { parser.push('HSET'); parser.pushKey(key); diff --git a/packages/client/lib/commands/HSETEX.ts b/packages/client/lib/commands/HSETEX.ts index 3827538934c..316b95a91c3 100644 --- a/packages/client/lib/commands/HSETEX.ts +++ b/packages/client/lib/commands/HSETEX.ts @@ -20,6 +20,15 @@ type HSETEXMap = Map; type HSETEXTuples = Array<[HashTypes, HashTypes]> | Array; export default { + /** + * Constructs the HSETEX command + * + * @param parser - The command parser + * @param key - The key of the hash + * @param fields - Object, Map, or Array of field-value pairs to set + * @param options - Optional configuration for expiration and mode settings + * @see https://redis.io/commands/hsetex/ + */ parseCommand( parser: CommandParser, key: RedisArgument, diff --git a/packages/client/lib/commands/HSETNX.ts b/packages/client/lib/commands/HSETNX.ts index 130d7cd81d3..dc10b6c5e00 100644 --- a/packages/client/lib/commands/HSETNX.ts +++ b/packages/client/lib/commands/HSETNX.ts @@ -3,6 +3,15 @@ import { RedisArgument, Command, NumberReply } from '../RESP/types'; export default { IS_READ_ONLY: true, + /** + * Constructs the HSETNX command + * + * @param parser - The command parser + * @param key - The key of the hash + * @param field - The field to set if it does not exist + * @param value - The value to set + * @see https://redis.io/commands/hsetnx/ + */ parseCommand( parser: CommandParser, key: RedisArgument, diff --git a/packages/client/lib/commands/HSTRLEN.ts b/packages/client/lib/commands/HSTRLEN.ts index 2468747d4c9..016c14e27a8 100644 --- a/packages/client/lib/commands/HSTRLEN.ts +++ b/packages/client/lib/commands/HSTRLEN.ts @@ -4,6 +4,14 @@ import { RedisArgument, ArrayReply, BlobStringReply, Command } from '../RESP/typ export default { CACHEABLE: true, IS_READ_ONLY: true, + /** + * Constructs the HSTRLEN command + * + * @param parser - The command parser + * @param key - The key of the hash + * @param field - The field to get the string length of + * @see https://redis.io/commands/hstrlen/ + */ parseCommand(parser: CommandParser, key: RedisArgument, field: RedisArgument) { parser.push('HSTRLEN'); parser.pushKey(key); diff --git a/packages/client/lib/commands/HTTL.ts b/packages/client/lib/commands/HTTL.ts index 4b8fe5d7e85..710b4c7c1ff 100644 --- a/packages/client/lib/commands/HTTL.ts +++ b/packages/client/lib/commands/HTTL.ts @@ -4,6 +4,12 @@ import { RedisVariadicArgument } from './generic-transformers'; export default { IS_READ_ONLY: true, + /** + * Returns the remaining time to live of field(s) in a hash. + * @param parser - The Redis command parser. + * @param key - Key of the hash. + * @param fields - Fields to check time to live. + */ parseCommand( parser: CommandParser, key: RedisArgument, diff --git a/packages/client/lib/commands/HVALS.ts b/packages/client/lib/commands/HVALS.ts index ab17e47f533..faa5fe43442 100644 --- a/packages/client/lib/commands/HVALS.ts +++ b/packages/client/lib/commands/HVALS.ts @@ -4,6 +4,11 @@ import { RedisArgument, ArrayReply, BlobStringReply, Command } from '../RESP/typ export default { CACHEABLE: true, IS_READ_ONLY: true, + /** + * Gets all values in a hash. + * @param parser - The Redis command parser. + * @param key - Key of the hash. + */ parseCommand(parser: CommandParser, key: RedisArgument) { parser.push('HVALS'); parser.pushKey(key); diff --git a/packages/client/lib/commands/INCR.ts b/packages/client/lib/commands/INCR.ts index e719f06bc19..0a294ccdc5a 100644 --- a/packages/client/lib/commands/INCR.ts +++ b/packages/client/lib/commands/INCR.ts @@ -2,6 +2,13 @@ import { CommandParser } from '../client/parser'; import { RedisArgument, NumberReply, Command } from '../RESP/types'; export default { + /** + * Constructs the INCR command + * + * @param parser - The command parser + * @param key - The key to increment + * @see https://redis.io/commands/incr/ + */ parseCommand(parser: CommandParser, key: RedisArgument) { parser.push('INCR'); parser.pushKey(key); diff --git a/packages/client/lib/commands/INCRBY.ts b/packages/client/lib/commands/INCRBY.ts index bf463185044..f23ec1a74a4 100644 --- a/packages/client/lib/commands/INCRBY.ts +++ b/packages/client/lib/commands/INCRBY.ts @@ -2,6 +2,14 @@ import { CommandParser } from '../client/parser'; import { RedisArgument, NumberReply, Command } from '../RESP/types'; export default { + /** + * Constructs the INCRBY command + * + * @param parser - The command parser + * @param key - The key to increment + * @param increment - The amount to increment by + * @see https://redis.io/commands/incrby/ + */ parseCommand(parser: CommandParser, key: RedisArgument, increment: number) { parser.push('INCRBY'); parser.pushKey(key); diff --git a/packages/client/lib/commands/INCRBYFLOAT.ts b/packages/client/lib/commands/INCRBYFLOAT.ts index 9a2dba42a6e..9effa756db5 100644 --- a/packages/client/lib/commands/INCRBYFLOAT.ts +++ b/packages/client/lib/commands/INCRBYFLOAT.ts @@ -2,6 +2,14 @@ import { CommandParser } from '../client/parser'; import { RedisArgument, BlobStringReply, Command } from '../RESP/types'; export default { + /** + * Constructs the INCRBYFLOAT command + * + * @param parser - The command parser + * @param key - The key to increment + * @param increment - The floating-point value to increment by + * @see https://redis.io/commands/incrbyfloat/ + */ parseCommand(parser: CommandParser, key: RedisArgument, increment: number) { parser.push('INCRBYFLOAT'); parser.pushKey(key); diff --git a/packages/client/lib/commands/INFO.ts b/packages/client/lib/commands/INFO.ts index 82cbd497a5b..799fcb1825a 100644 --- a/packages/client/lib/commands/INFO.ts +++ b/packages/client/lib/commands/INFO.ts @@ -4,6 +4,13 @@ import { RedisArgument, VerbatimStringReply, Command } from '../RESP/types'; export default { NOT_KEYED_COMMAND: true, IS_READ_ONLY: true, + /** + * Constructs the INFO command + * + * @param parser - The command parser + * @param section - Optional specific section of information to retrieve + * @see https://redis.io/commands/info/ + */ parseCommand(parser: CommandParser, section?: RedisArgument) { parser.push('INFO'); diff --git a/packages/client/lib/commands/KEYS.ts b/packages/client/lib/commands/KEYS.ts index e516245d2ee..eb240c26ceb 100644 --- a/packages/client/lib/commands/KEYS.ts +++ b/packages/client/lib/commands/KEYS.ts @@ -4,6 +4,13 @@ import { RedisArgument, ArrayReply, BlobStringReply, Command } from '../RESP/typ export default { NOT_KEYED_COMMAND: true, IS_READ_ONLY: true, + /** + * Constructs the KEYS command + * + * @param parser - The command parser + * @param pattern - The pattern to match keys against + * @see https://redis.io/commands/keys/ + */ parseCommand(parser: CommandParser, pattern: RedisArgument) { parser.push('KEYS', pattern); }, diff --git a/packages/client/lib/commands/LASTSAVE.ts b/packages/client/lib/commands/LASTSAVE.ts index 447cb95ab6d..fbbc6a0046a 100644 --- a/packages/client/lib/commands/LASTSAVE.ts +++ b/packages/client/lib/commands/LASTSAVE.ts @@ -4,6 +4,12 @@ import { NumberReply, Command } from '../RESP/types'; export default { NOT_KEYED_COMMAND: true, IS_READ_ONLY: true, + /** + * Constructs the LASTSAVE command + * + * @param parser - The command parser + * @see https://redis.io/commands/lastsave/ + */ parseCommand(parser: CommandParser) { parser.push('LASTSAVE'); }, diff --git a/packages/client/lib/commands/LATENCY_DOCTOR.ts b/packages/client/lib/commands/LATENCY_DOCTOR.ts index 49c830b3065..5ba7ee6a7bf 100644 --- a/packages/client/lib/commands/LATENCY_DOCTOR.ts +++ b/packages/client/lib/commands/LATENCY_DOCTOR.ts @@ -4,6 +4,12 @@ import { BlobStringReply, Command } from '../RESP/types'; export default { NOT_KEYED_COMMAND: true, IS_READ_ONLY: true, + /** + * Constructs the LATENCY DOCTOR command + * + * @param parser - The command parser + * @see https://redis.io/commands/latency-doctor/ + */ parseCommand(parser: CommandParser) { parser.push('LATENCY', 'DOCTOR'); }, diff --git a/packages/client/lib/commands/LATENCY_GRAPH.ts b/packages/client/lib/commands/LATENCY_GRAPH.ts index 20251c3cded..8c53624c741 100644 --- a/packages/client/lib/commands/LATENCY_GRAPH.ts +++ b/packages/client/lib/commands/LATENCY_GRAPH.ts @@ -25,6 +25,13 @@ export type LatencyEvent = typeof LATENCY_EVENTS[keyof typeof LATENCY_EVENTS]; export default { NOT_KEYED_COMMAND: true, IS_READ_ONLY: true, + /** + * Constructs the LATENCY GRAPH command + * + * @param parser - The command parser + * @param event - The latency event to get the graph for + * @see https://redis.io/commands/latency-graph/ + */ parseCommand(parser: CommandParser, event: LatencyEvent) { parser.push('LATENCY', 'GRAPH', event); }, diff --git a/packages/client/lib/commands/LATENCY_HISTORY.ts b/packages/client/lib/commands/LATENCY_HISTORY.ts index 6e0e4d5c560..dec7129befa 100644 --- a/packages/client/lib/commands/LATENCY_HISTORY.ts +++ b/packages/client/lib/commands/LATENCY_HISTORY.ts @@ -23,6 +23,13 @@ export type LatencyEventType = ( export default { NOT_KEYED_COMMAND: true, IS_READ_ONLY: true, + /** + * Constructs the LATENCY HISTORY command + * + * @param parser - The command parser + * @param event - The latency event to get the history for + * @see https://redis.io/commands/latency-history/ + */ parseCommand(parser: CommandParser, event: LatencyEventType) { parser.push('LATENCY', 'HISTORY', event); }, diff --git a/packages/client/lib/commands/LATENCY_LATEST.ts b/packages/client/lib/commands/LATENCY_LATEST.ts index 2ce3efd291c..8fbdd46a13a 100644 --- a/packages/client/lib/commands/LATENCY_LATEST.ts +++ b/packages/client/lib/commands/LATENCY_LATEST.ts @@ -4,6 +4,12 @@ import { ArrayReply, BlobStringReply, NumberReply, Command } from '../RESP/types export default { NOT_KEYED_COMMAND: true, IS_READ_ONLY: true, + /** + * Constructs the LATENCY LATEST command + * + * @param parser - The command parser + * @see https://redis.io/commands/latency-latest/ + */ parseCommand(parser: CommandParser) { parser.push('LATENCY', 'LATEST'); }, diff --git a/packages/client/lib/commands/LCS.ts b/packages/client/lib/commands/LCS.ts index ed4f11ad990..9b2317e147f 100644 --- a/packages/client/lib/commands/LCS.ts +++ b/packages/client/lib/commands/LCS.ts @@ -3,6 +3,14 @@ import { RedisArgument, BlobStringReply, Command } from '../RESP/types'; export default { IS_READ_ONLY: true, + /** + * Constructs the LCS command (Longest Common Substring) + * + * @param parser - The command parser + * @param key1 - First key containing the first string + * @param key2 - Second key containing the second string + * @see https://redis.io/commands/lcs/ + */ parseCommand( parser: CommandParser, key1: RedisArgument, diff --git a/packages/client/lib/commands/LCS_IDX.ts b/packages/client/lib/commands/LCS_IDX.ts index cb0a6b07657..684aa99efb0 100644 --- a/packages/client/lib/commands/LCS_IDX.ts +++ b/packages/client/lib/commands/LCS_IDX.ts @@ -25,6 +25,15 @@ export type LcsIdxReply = TuplesToMapReply<[ export default { IS_READ_ONLY: LCS.IS_READ_ONLY, + /** + * Constructs the LCS command with IDX option + * + * @param parser - The command parser + * @param key1 - First key containing the first string + * @param key2 - Second key containing the second string + * @param options - Additional options for the LCS IDX command + * @see https://redis.io/commands/lcs/ + */ parseCommand( parser: CommandParser, key1: RedisArgument, diff --git a/packages/client/lib/commands/LCS_IDX_WITHMATCHLEN.ts b/packages/client/lib/commands/LCS_IDX_WITHMATCHLEN.ts index d2a743983e1..f3578b789fc 100644 --- a/packages/client/lib/commands/LCS_IDX_WITHMATCHLEN.ts +++ b/packages/client/lib/commands/LCS_IDX_WITHMATCHLEN.ts @@ -16,6 +16,12 @@ export type LcsIdxWithMatchLenReply = TuplesToMapReply<[ export default { IS_READ_ONLY: LCS_IDX.IS_READ_ONLY, + /** + * Constructs the LCS command with IDX and WITHMATCHLEN options + * + * @param args - The same parameters as LCS_IDX command + * @see https://redis.io/commands/lcs/ + */ parseCommand(...args: Parameters) { const parser = args[0]; LCS_IDX.parseCommand(...args); diff --git a/packages/client/lib/commands/LCS_LEN.ts b/packages/client/lib/commands/LCS_LEN.ts index a1f92d914a4..bb35c3d9209 100644 --- a/packages/client/lib/commands/LCS_LEN.ts +++ b/packages/client/lib/commands/LCS_LEN.ts @@ -3,6 +3,12 @@ import LCS from './LCS'; export default { IS_READ_ONLY: LCS.IS_READ_ONLY, + /** + * Constructs the LCS command with LEN option + * + * @param args - The same parameters as LCS command + * @see https://redis.io/commands/lcs/ + */ parseCommand(...args: Parameters) { const parser = args[0]; diff --git a/packages/client/lib/commands/LINDEX.ts b/packages/client/lib/commands/LINDEX.ts index 6335fc40c2c..dd7671a41c6 100644 --- a/packages/client/lib/commands/LINDEX.ts +++ b/packages/client/lib/commands/LINDEX.ts @@ -4,6 +4,14 @@ import { RedisArgument, BlobStringReply, NullReply, Command } from '../RESP/type export default { CACHEABLE: true, IS_READ_ONLY: true, + /** + * Constructs the LINDEX command + * + * @param parser - The command parser + * @param key - The key of the list + * @param index - The index of the element to retrieve + * @see https://redis.io/commands/lindex/ + */ parseCommand(parser: CommandParser, key: RedisArgument, index: number) { parser.push('LINDEX'); parser.pushKey(key); diff --git a/packages/client/lib/commands/LINSERT.ts b/packages/client/lib/commands/LINSERT.ts index 8a40ac66630..ede230191ba 100644 --- a/packages/client/lib/commands/LINSERT.ts +++ b/packages/client/lib/commands/LINSERT.ts @@ -5,6 +5,16 @@ type LInsertPosition = 'BEFORE' | 'AFTER'; export default { IS_READ_ONLY: true, + /** + * Constructs the LINSERT command + * + * @param parser - The command parser + * @param key - The key of the list + * @param position - The position where to insert (BEFORE or AFTER) + * @param pivot - The element to find in the list + * @param element - The element to insert + * @see https://redis.io/commands/linsert/ + */ parseCommand( parser: CommandParser, key: RedisArgument, diff --git a/packages/client/lib/commands/LLEN.ts b/packages/client/lib/commands/LLEN.ts index 674e022e60d..7ece6823bb5 100644 --- a/packages/client/lib/commands/LLEN.ts +++ b/packages/client/lib/commands/LLEN.ts @@ -4,6 +4,13 @@ import { RedisArgument, NumberReply, Command } from '../RESP/types'; export default { CACHEABLE: true, IS_READ_ONLY: true, + /** + * Constructs the LLEN command + * + * @param parser - The command parser + * @param key - The key of the list to get the length of + * @see https://redis.io/commands/llen/ + */ parseCommand(parser: CommandParser, key: RedisArgument) { parser.push('LLEN'); parser.pushKey(key); diff --git a/packages/client/lib/commands/LMOVE.ts b/packages/client/lib/commands/LMOVE.ts index f3ac847e900..9ed0003b23d 100644 --- a/packages/client/lib/commands/LMOVE.ts +++ b/packages/client/lib/commands/LMOVE.ts @@ -4,6 +4,16 @@ import { ListSide } from './generic-transformers'; export default { IS_READ_ONLY: false, + /** + * Constructs the LMOVE command + * + * @param parser - The command parser + * @param source - The source list key + * @param destination - The destination list key + * @param sourceSide - The side to pop from (LEFT or RIGHT) + * @param destinationSide - The side to push to (LEFT or RIGHT) + * @see https://redis.io/commands/lmove/ + */ parseCommand( parser: CommandParser, source: RedisArgument, diff --git a/packages/client/lib/commands/LMPOP.ts b/packages/client/lib/commands/LMPOP.ts index c8095e42e75..54dc40c1c3d 100644 --- a/packages/client/lib/commands/LMPOP.ts +++ b/packages/client/lib/commands/LMPOP.ts @@ -24,6 +24,13 @@ export type LMPopArguments = Tail>; export default { IS_READ_ONLY: false, + /** + * Constructs the LMPOP command + * + * @param parser - The command parser + * @param args - Arguments including keys, side (LEFT or RIGHT), and options + * @see https://redis.io/commands/lmpop/ + */ parseCommand(parser: CommandParser, ...args: LMPopArguments) { parser.push('LMPOP'); parseLMPopArguments(parser, ...args); diff --git a/packages/client/lib/commands/LOLWUT.ts b/packages/client/lib/commands/LOLWUT.ts index 372bf536967..5e07a103720 100644 --- a/packages/client/lib/commands/LOLWUT.ts +++ b/packages/client/lib/commands/LOLWUT.ts @@ -4,6 +4,14 @@ import { BlobStringReply, Command } from '../RESP/types'; export default { NOT_KEYED_COMMAND: true, IS_READ_ONLY: true, + /** + * Constructs the LOLWUT command + * + * @param parser - The command parser + * @param version - Optional version parameter + * @param optionalArguments - Additional optional numeric arguments + * @see https://redis.io/commands/lolwut/ + */ parseCommand(parser: CommandParser, version?: number, ...optionalArguments: Array) { parser.push('LOLWUT'); if (version) { diff --git a/packages/client/lib/commands/LPOP.ts b/packages/client/lib/commands/LPOP.ts index 3125236bfa0..aaa83be465d 100644 --- a/packages/client/lib/commands/LPOP.ts +++ b/packages/client/lib/commands/LPOP.ts @@ -2,6 +2,13 @@ import { CommandParser } from '../client/parser'; import { RedisArgument, BlobStringReply, NullReply, Command } from '../RESP/types'; export default { + /** + * Constructs the LPOP command + * + * @param parser - The command parser + * @param key - The key of the list to pop from + * @see https://redis.io/commands/lpop/ + */ parseCommand(parser: CommandParser, key: RedisArgument) { parser.push('LPOP'); parser.pushKey(key); diff --git a/packages/client/lib/commands/LPOP_COUNT.ts b/packages/client/lib/commands/LPOP_COUNT.ts index 6d9aba42c21..cdc0dc41a22 100644 --- a/packages/client/lib/commands/LPOP_COUNT.ts +++ b/packages/client/lib/commands/LPOP_COUNT.ts @@ -4,6 +4,14 @@ import LPOP from './LPOP'; export default { IS_READ_ONLY: false, + /** + * Constructs the LPOP command with count parameter + * + * @param parser - The command parser + * @param key - The key of the list to pop from + * @param count - The number of elements to pop + * @see https://redis.io/commands/lpop/ + */ parseCommand(parser: CommandParser, key: RedisArgument, count: number) { LPOP.parseCommand(parser, key); parser.push(count.toString()) diff --git a/packages/client/lib/commands/LPOS.ts b/packages/client/lib/commands/LPOS.ts index bb05ba6555d..54078b8185f 100644 --- a/packages/client/lib/commands/LPOS.ts +++ b/packages/client/lib/commands/LPOS.ts @@ -9,6 +9,15 @@ export interface LPosOptions { export default { CACHEABLE: true, IS_READ_ONLY: true, + /** + * Constructs the LPOS command + * + * @param parser - The command parser + * @param key - The key of the list + * @param element - The element to search for + * @param options - Optional parameters for RANK and MAXLEN + * @see https://redis.io/commands/lpos/ + */ parseCommand( parser: CommandParser, key: RedisArgument, diff --git a/packages/client/lib/commands/LPOS_COUNT.ts b/packages/client/lib/commands/LPOS_COUNT.ts index e782a2d26ee..ace6e49c1e5 100644 --- a/packages/client/lib/commands/LPOS_COUNT.ts +++ b/packages/client/lib/commands/LPOS_COUNT.ts @@ -5,6 +5,16 @@ import LPOS, { LPosOptions } from './LPOS'; export default { CACHEABLE: LPOS.CACHEABLE, IS_READ_ONLY: LPOS.IS_READ_ONLY, + /** + * Constructs the LPOS command with COUNT option + * + * @param parser - The command parser + * @param key - The key of the list + * @param element - The element to search for + * @param count - The number of positions to return + * @param options - Optional parameters for RANK and MAXLEN + * @see https://redis.io/commands/lpos/ + */ parseCommand( parser: CommandParser, key: RedisArgument, diff --git a/packages/client/lib/commands/LPUSH.ts b/packages/client/lib/commands/LPUSH.ts index 293029034ee..89e1e094870 100644 --- a/packages/client/lib/commands/LPUSH.ts +++ b/packages/client/lib/commands/LPUSH.ts @@ -3,6 +3,14 @@ import { RedisArgument, NumberReply, Command } from '../RESP/types'; import { RedisVariadicArgument } from './generic-transformers'; export default { + /** + * Constructs the LPUSH command + * + * @param parser - The command parser + * @param key - The key of the list + * @param elements - One or more elements to push to the list + * @see https://redis.io/commands/lpush/ + */ parseCommand(parser: CommandParser, key: RedisArgument, elements: RedisVariadicArgument) { parser.push('LPUSH'); parser.pushKey(key); diff --git a/packages/client/lib/commands/LPUSHX.ts b/packages/client/lib/commands/LPUSHX.ts index 98dd51a7ac2..e87bd4ff0d5 100644 --- a/packages/client/lib/commands/LPUSHX.ts +++ b/packages/client/lib/commands/LPUSHX.ts @@ -3,6 +3,14 @@ import { RedisArgument, NumberReply, Command } from '../RESP/types'; import { RedisVariadicArgument } from './generic-transformers'; export default { + /** + * Constructs the LPUSHX command + * + * @param parser - The command parser + * @param key - The key of the list + * @param elements - One or more elements to push to the list if it exists + * @see https://redis.io/commands/lpushx/ + */ parseCommand(parser: CommandParser, key: RedisArgument, elements: RedisVariadicArgument) { parser.push('LPUSHX'); parser.pushKey(key); diff --git a/packages/client/lib/commands/LRANGE.ts b/packages/client/lib/commands/LRANGE.ts index ab033dd88a4..040bb6b4498 100644 --- a/packages/client/lib/commands/LRANGE.ts +++ b/packages/client/lib/commands/LRANGE.ts @@ -4,6 +4,15 @@ import { RedisArgument, ArrayReply, BlobStringReply, Command } from '../RESP/typ export default { CACHEABLE: true, IS_READ_ONLY: true, + /** + * Constructs the LRANGE command + * + * @param parser - The command parser + * @param key - The key of the list + * @param start - The starting index + * @param stop - The ending index + * @see https://redis.io/commands/lrange/ + */ parseCommand(parser: CommandParser, key: RedisArgument, start: number, stop: number) { parser.push('LRANGE'); parser.pushKey(key); diff --git a/packages/client/lib/commands/LREM.ts b/packages/client/lib/commands/LREM.ts index bb97e3882e7..4e5de0fa78c 100644 --- a/packages/client/lib/commands/LREM.ts +++ b/packages/client/lib/commands/LREM.ts @@ -3,6 +3,15 @@ import { RedisArgument, NumberReply, Command } from '../RESP/types'; export default { IS_READ_ONLY: true, + /** + * Constructs the LREM command + * + * @param parser - The command parser + * @param key - The key of the list + * @param count - The count of elements to remove (negative: from tail to head, 0: all occurrences, positive: from head to tail) + * @param element - The element to remove + * @see https://redis.io/commands/lrem/ + */ parseCommand(parser: CommandParser, key: RedisArgument, count: number, element: RedisArgument) { parser.push('LREM'); parser.pushKey(key); diff --git a/packages/client/lib/commands/LSET.ts b/packages/client/lib/commands/LSET.ts index 0fe646fbb73..052961a316e 100644 --- a/packages/client/lib/commands/LSET.ts +++ b/packages/client/lib/commands/LSET.ts @@ -3,6 +3,15 @@ import { RedisArgument, SimpleStringReply, Command } from '../RESP/types'; export default { IS_READ_ONLY: true, + /** + * Constructs the LSET command + * + * @param parser - The command parser + * @param key - The key of the list + * @param index - The index of the element to replace + * @param element - The new value to set + * @see https://redis.io/commands/lset/ + */ parseCommand(parser: CommandParser, key: RedisArgument, index: number, element: RedisArgument) { parser.push('LSET'); parser.pushKey(key); diff --git a/packages/client/lib/commands/LTRIM.ts b/packages/client/lib/commands/LTRIM.ts index acc7e767d0d..31c2b66b5a9 100644 --- a/packages/client/lib/commands/LTRIM.ts +++ b/packages/client/lib/commands/LTRIM.ts @@ -2,6 +2,15 @@ import { CommandParser } from '../client/parser'; import { RedisArgument, SimpleStringReply, Command } from '../RESP/types'; export default { + /** + * Constructs the LTRIM command + * + * @param parser - The command parser + * @param key - The key of the list + * @param start - The starting index + * @param stop - The ending index + * @see https://redis.io/commands/ltrim/ + */ parseCommand(parser: CommandParser, key: RedisArgument, start: number, stop: number) { parser.push('LTRIM'); parser.pushKey(key); diff --git a/packages/client/lib/commands/MEMORY_DOCTOR.ts b/packages/client/lib/commands/MEMORY_DOCTOR.ts index 3a2d808db10..21e42ccc7ea 100644 --- a/packages/client/lib/commands/MEMORY_DOCTOR.ts +++ b/packages/client/lib/commands/MEMORY_DOCTOR.ts @@ -4,6 +4,12 @@ import { BlobStringReply, Command } from '../RESP/types'; export default { NOT_KEYED_COMMAND: true, IS_READ_ONLY: true, + /** + * Constructs the MEMORY DOCTOR command + * + * @param parser - The command parser + * @see https://redis.io/commands/memory-doctor/ + */ parseCommand(parser: CommandParser) { parser.push('MEMORY', 'DOCTOR'); }, diff --git a/packages/client/lib/commands/MEMORY_MALLOC-STATS.ts b/packages/client/lib/commands/MEMORY_MALLOC-STATS.ts index af6b5db3347..69ad8c37a85 100644 --- a/packages/client/lib/commands/MEMORY_MALLOC-STATS.ts +++ b/packages/client/lib/commands/MEMORY_MALLOC-STATS.ts @@ -4,6 +4,12 @@ import { BlobStringReply, Command } from '../RESP/types'; export default { NOT_KEYED_COMMAND: true, IS_READ_ONLY: true, + /** + * Constructs the MEMORY MALLOC-STATS command + * + * @param parser - The command parser + * @see https://redis.io/commands/memory-malloc-stats/ + */ parseCommand(parser: CommandParser) { parser.push('MEMORY', 'MALLOC-STATS'); }, diff --git a/packages/client/lib/commands/MEMORY_PURGE.ts b/packages/client/lib/commands/MEMORY_PURGE.ts index bbd02890786..39f837016ad 100644 --- a/packages/client/lib/commands/MEMORY_PURGE.ts +++ b/packages/client/lib/commands/MEMORY_PURGE.ts @@ -4,6 +4,12 @@ import { SimpleStringReply, Command } from '../RESP/types'; export default { NOT_KEYED_COMMAND: true, IS_READ_ONLY: false, + /** + * Constructs the MEMORY PURGE command + * + * @param parser - The command parser + * @see https://redis.io/commands/memory-purge/ + */ parseCommand(parser: CommandParser) { parser.push('MEMORY', 'PURGE'); }, diff --git a/packages/client/lib/commands/MEMORY_STATS.ts b/packages/client/lib/commands/MEMORY_STATS.ts index 33410535aa9..9391a91613c 100644 --- a/packages/client/lib/commands/MEMORY_STATS.ts +++ b/packages/client/lib/commands/MEMORY_STATS.ts @@ -38,6 +38,12 @@ export type MemoryStatsReply = TuplesToMapReply<[ export default { NOT_KEYED_COMMAND: true, IS_READ_ONLY: true, + /** + * Constructs the MEMORY STATS command + * + * @param parser - The command parser + * @see https://redis.io/commands/memory-stats/ + */ parseCommand(parser: CommandParser) { parser.push('MEMORY', 'STATS'); }, diff --git a/packages/client/lib/commands/MEMORY_USAGE.ts b/packages/client/lib/commands/MEMORY_USAGE.ts index 6e85438dbed..a1fa79f6210 100644 --- a/packages/client/lib/commands/MEMORY_USAGE.ts +++ b/packages/client/lib/commands/MEMORY_USAGE.ts @@ -7,6 +7,14 @@ export interface MemoryUsageOptions { export default { IS_READ_ONLY: true, + /** + * Constructs the MEMORY USAGE command + * + * @param parser - The command parser + * @param key - The key to get memory usage for + * @param options - Optional parameters including SAMPLES + * @see https://redis.io/commands/memory-usage/ + */ parseCommand(parser: CommandParser, key: RedisArgument, options?: MemoryUsageOptions) { parser.push('MEMORY', 'USAGE'); parser.pushKey(key); diff --git a/packages/client/lib/commands/MGET.ts b/packages/client/lib/commands/MGET.ts index ce1e9ba7781..22145dd3485 100644 --- a/packages/client/lib/commands/MGET.ts +++ b/packages/client/lib/commands/MGET.ts @@ -4,6 +4,13 @@ import { RedisArgument, BlobStringReply, NullReply, Command } from '../RESP/type export default { CACHEABLE: true, IS_READ_ONLY: true, + /** + * Constructs the MGET command + * + * @param parser - The command parser + * @param keys - Array of keys to get + * @see https://redis.io/commands/mget/ + */ parseCommand(parser: CommandParser, keys: Array) { parser.push('MGET'); parser.pushKeys(keys); diff --git a/packages/client/lib/commands/MIGRATE.ts b/packages/client/lib/commands/MIGRATE.ts index 15345060aa7..ba798e331ab 100644 --- a/packages/client/lib/commands/MIGRATE.ts +++ b/packages/client/lib/commands/MIGRATE.ts @@ -10,6 +10,18 @@ export interface MigrateOptions { export default { IS_READ_ONLY: false, + /** + * Constructs the MIGRATE command + * + * @param parser - The command parser + * @param host - Target Redis instance host + * @param port - Target Redis instance port + * @param key - Key or keys to migrate + * @param destinationDb - Target database index + * @param timeout - Timeout in milliseconds + * @param options - Optional parameters including COPY, REPLACE, and AUTH + * @see https://redis.io/commands/migrate/ + */ parseCommand( parser: CommandParser, host: RedisArgument, diff --git a/packages/client/lib/commands/MODULE_LIST.ts b/packages/client/lib/commands/MODULE_LIST.ts index 85203138f57..8183c419a66 100644 --- a/packages/client/lib/commands/MODULE_LIST.ts +++ b/packages/client/lib/commands/MODULE_LIST.ts @@ -9,6 +9,12 @@ export type ModuleListReply = ArrayReply) { parser.push('MODULE', 'LOAD', path); diff --git a/packages/client/lib/commands/MODULE_UNLOAD.ts b/packages/client/lib/commands/MODULE_UNLOAD.ts index 1acc359d0d4..6d19b2b2a73 100644 --- a/packages/client/lib/commands/MODULE_UNLOAD.ts +++ b/packages/client/lib/commands/MODULE_UNLOAD.ts @@ -4,6 +4,13 @@ import { RedisArgument, SimpleStringReply, Command } from '../RESP/types'; export default { NOT_KEYED_COMMAND: true, IS_READ_ONLY: true, + /** + * Constructs the MODULE UNLOAD command + * + * @param parser - The command parser + * @param name - The name of the module to unload + * @see https://redis.io/commands/module-unload/ + */ parseCommand(parser: CommandParser, name: RedisArgument) { parser.push('MODULE', 'UNLOAD', name); }, diff --git a/packages/client/lib/commands/MOVE.ts b/packages/client/lib/commands/MOVE.ts index 8a6c5427fbc..0c08a6fa100 100644 --- a/packages/client/lib/commands/MOVE.ts +++ b/packages/client/lib/commands/MOVE.ts @@ -2,6 +2,14 @@ import { CommandParser } from '../client/parser'; import { RedisArgument, NumberReply, Command } from '../RESP/types'; export default { + /** + * Constructs the MOVE command + * + * @param parser - The command parser + * @param key - The key to move + * @param db - The destination database index + * @see https://redis.io/commands/move/ + */ parseCommand(parser: CommandParser, key: RedisArgument, db: number) { parser.push('MOVE'); parser.pushKey(key); diff --git a/packages/client/lib/commands/MSET.ts b/packages/client/lib/commands/MSET.ts index f761854f09c..ab734bae5c7 100644 --- a/packages/client/lib/commands/MSET.ts +++ b/packages/client/lib/commands/MSET.ts @@ -33,6 +33,13 @@ export function parseMSetArguments(parser: CommandParser, toSet: MSetArguments) export default { IS_READ_ONLY: true, + /** + * Constructs the MSET command + * + * @param parser - The command parser + * @param toSet - Key-value pairs to set (array of tuples, flat array, or object) + * @see https://redis.io/commands/mset/ + */ parseCommand(parser: CommandParser, toSet: MSetArguments) { parser.push('MSET'); return parseMSetArguments(parser, toSet); diff --git a/packages/client/lib/commands/MSETNX.ts b/packages/client/lib/commands/MSETNX.ts index 3ecce9525de..9a2186023f6 100644 --- a/packages/client/lib/commands/MSETNX.ts +++ b/packages/client/lib/commands/MSETNX.ts @@ -4,6 +4,13 @@ import { MSetArguments, parseMSetArguments } from './MSET'; export default { IS_READ_ONLY: true, + /** + * Constructs the MSETNX command + * + * @param parser - The command parser + * @param toSet - Key-value pairs to set if none of the keys exist (array of tuples, flat array, or object) + * @see https://redis.io/commands/msetnx/ + */ parseCommand(parser: CommandParser, toSet: MSetArguments) { parser.push('MSETNX'); return parseMSetArguments(parser, toSet); diff --git a/packages/client/lib/commands/OBJECT_ENCODING.ts b/packages/client/lib/commands/OBJECT_ENCODING.ts index 3a795f6fb64..2c0f6b41bbc 100644 --- a/packages/client/lib/commands/OBJECT_ENCODING.ts +++ b/packages/client/lib/commands/OBJECT_ENCODING.ts @@ -3,6 +3,13 @@ import { RedisArgument, BlobStringReply, NullReply, Command } from '../RESP/type export default { IS_READ_ONLY: true, + /** + * Constructs the OBJECT ENCODING command + * + * @param parser - The command parser + * @param key - The key to get the internal encoding for + * @see https://redis.io/commands/object-encoding/ + */ parseCommand(parser: CommandParser, key: RedisArgument) { parser.push('OBJECT', 'ENCODING'); parser.pushKey(key); diff --git a/packages/client/lib/commands/OBJECT_FREQ.ts b/packages/client/lib/commands/OBJECT_FREQ.ts index dad1124b101..42a310a97c5 100644 --- a/packages/client/lib/commands/OBJECT_FREQ.ts +++ b/packages/client/lib/commands/OBJECT_FREQ.ts @@ -3,6 +3,13 @@ import { RedisArgument, NumberReply, NullReply, Command } from '../RESP/types'; export default { IS_READ_ONLY: true, + /** + * Constructs the OBJECT FREQ command + * + * @param parser - The command parser + * @param key - The key to get the access frequency for + * @see https://redis.io/commands/object-freq/ + */ parseCommand(parser: CommandParser, key: RedisArgument) { parser.push('OBJECT', 'FREQ'); parser.pushKey(key); diff --git a/packages/client/lib/commands/OBJECT_IDLETIME.ts b/packages/client/lib/commands/OBJECT_IDLETIME.ts index 2bd32f4e65d..2d4afeda65a 100644 --- a/packages/client/lib/commands/OBJECT_IDLETIME.ts +++ b/packages/client/lib/commands/OBJECT_IDLETIME.ts @@ -3,6 +3,13 @@ import { RedisArgument, NumberReply, NullReply, Command } from '../RESP/types'; export default { IS_READ_ONLY: true, + /** + * Constructs the OBJECT IDLETIME command + * + * @param parser - The command parser + * @param key - The key to get the idle time for + * @see https://redis.io/commands/object-idletime/ + */ parseCommand(parser: CommandParser, key: RedisArgument) { parser.push('OBJECT', 'IDLETIME'); parser.pushKey(key); diff --git a/packages/client/lib/commands/OBJECT_REFCOUNT.ts b/packages/client/lib/commands/OBJECT_REFCOUNT.ts index 4bee4dea60c..7948a4941de 100644 --- a/packages/client/lib/commands/OBJECT_REFCOUNT.ts +++ b/packages/client/lib/commands/OBJECT_REFCOUNT.ts @@ -3,6 +3,13 @@ import { RedisArgument, NumberReply, NullReply, Command } from '../RESP/types'; export default { IS_READ_ONLY: true, + /** + * Constructs the OBJECT REFCOUNT command + * + * @param parser - The command parser + * @param key - The key to get the reference count for + * @see https://redis.io/commands/object-refcount/ + */ parseCommand(parser: CommandParser, key: RedisArgument) { parser.push('OBJECT', 'REFCOUNT'); parser.pushKey(key); diff --git a/packages/client/lib/commands/PERSIST.ts b/packages/client/lib/commands/PERSIST.ts index a1d31523664..3b1f4a7062c 100644 --- a/packages/client/lib/commands/PERSIST.ts +++ b/packages/client/lib/commands/PERSIST.ts @@ -2,6 +2,13 @@ import { CommandParser } from '../client/parser'; import { RedisArgument, NumberReply, Command } from '../RESP/types'; export default { + /** + * Constructs the PERSIST command + * + * @param parser - The command parser + * @param key - The key to remove the expiration from + * @see https://redis.io/commands/persist/ + */ parseCommand(parser: CommandParser, key: RedisArgument) { parser.push('PERSIST'); parser.pushKey(key); diff --git a/packages/client/lib/commands/PEXPIRE.ts b/packages/client/lib/commands/PEXPIRE.ts index 4053f46c8e2..f1d96076885 100644 --- a/packages/client/lib/commands/PEXPIRE.ts +++ b/packages/client/lib/commands/PEXPIRE.ts @@ -3,6 +3,15 @@ import { RedisArgument, NumberReply, Command } from '../RESP/types'; export default { IS_READ_ONLY: true, + /** + * Constructs the PEXPIRE command + * + * @param parser - The command parser + * @param key - The key to set the expiration for + * @param ms - The expiration time in milliseconds + * @param mode - Optional mode for the command ('NX', 'XX', 'GT', 'LT') + * @see https://redis.io/commands/pexpire/ + */ parseCommand( parser: CommandParser, key: RedisArgument, diff --git a/packages/client/lib/commands/PEXPIREAT.ts b/packages/client/lib/commands/PEXPIREAT.ts index e454447c970..072cc33bbb4 100644 --- a/packages/client/lib/commands/PEXPIREAT.ts +++ b/packages/client/lib/commands/PEXPIREAT.ts @@ -4,6 +4,15 @@ import { transformPXAT } from './generic-transformers'; export default { IS_READ_ONLY: true, + /** + * Constructs the PEXPIREAT command + * + * @param parser - The command parser + * @param key - The key to set the expiration for + * @param msTimestamp - The expiration timestamp in milliseconds (Unix timestamp or Date object) + * @param mode - Optional mode for the command ('NX', 'XX', 'GT', 'LT') + * @see https://redis.io/commands/pexpireat/ + */ parseCommand( parser: CommandParser, key: RedisArgument, diff --git a/packages/client/lib/commands/PEXPIRETIME.ts b/packages/client/lib/commands/PEXPIRETIME.ts index b5d04eae230..6b3488662c3 100644 --- a/packages/client/lib/commands/PEXPIRETIME.ts +++ b/packages/client/lib/commands/PEXPIRETIME.ts @@ -3,6 +3,13 @@ import { RedisArgument, NumberReply, Command } from '../RESP/types'; export default { IS_READ_ONLY: true, + /** + * Constructs the PEXPIRETIME command + * + * @param parser - The command parser + * @param key - The key to get the expiration time for in milliseconds + * @see https://redis.io/commands/pexpiretime/ + */ parseCommand(parser: CommandParser, key: RedisArgument) { parser.push('PEXPIRETIME'); parser.pushKey(key); diff --git a/packages/client/lib/commands/PFADD.ts b/packages/client/lib/commands/PFADD.ts index 94c2d1d5ae6..f5d2a280ca0 100644 --- a/packages/client/lib/commands/PFADD.ts +++ b/packages/client/lib/commands/PFADD.ts @@ -4,6 +4,14 @@ import { RedisVariadicArgument } from './generic-transformers'; export default { IS_READ_ONLY: true, + /** + * Constructs the PFADD command + * + * @param parser - The command parser + * @param key - The key of the HyperLogLog + * @param element - Optional elements to add + * @see https://redis.io/commands/pfadd/ + */ parseCommand(parser: CommandParser, key: RedisArgument, element?: RedisVariadicArgument) { parser.push('PFADD') parser.pushKey(key); diff --git a/packages/client/lib/commands/PFCOUNT.ts b/packages/client/lib/commands/PFCOUNT.ts index 46d2e2ed71f..1358fed7d67 100644 --- a/packages/client/lib/commands/PFCOUNT.ts +++ b/packages/client/lib/commands/PFCOUNT.ts @@ -4,6 +4,13 @@ import { RedisVariadicArgument } from './generic-transformers'; export default { IS_READ_ONLY: true, + /** + * Constructs the PFCOUNT command + * + * @param parser - The command parser + * @param keys - One or more keys of HyperLogLog structures to count + * @see https://redis.io/commands/pfcount/ + */ parseCommand(parser: CommandParser, keys: RedisVariadicArgument) { parser.push('PFCOUNT'); parser.pushKeys(keys); diff --git a/packages/client/lib/commands/PFMERGE.ts b/packages/client/lib/commands/PFMERGE.ts index e8eccf1afff..834a5dfbf55 100644 --- a/packages/client/lib/commands/PFMERGE.ts +++ b/packages/client/lib/commands/PFMERGE.ts @@ -3,6 +3,14 @@ import { RedisArgument, SimpleStringReply, Command } from '../RESP/types'; import { RedisVariadicArgument } from './generic-transformers'; export default { + /** + * Constructs the PFMERGE command + * + * @param parser - The command parser + * @param destination - The destination key to merge to + * @param sources - One or more source keys to merge from + * @see https://redis.io/commands/pfmerge/ + */ parseCommand( parser: CommandParser, destination: RedisArgument, diff --git a/packages/client/lib/commands/PING.ts b/packages/client/lib/commands/PING.ts index 26807eeeba4..1e8d21e1584 100644 --- a/packages/client/lib/commands/PING.ts +++ b/packages/client/lib/commands/PING.ts @@ -4,6 +4,13 @@ import { RedisArgument, SimpleStringReply, BlobStringReply, Command } from '../R export default { NOT_KEYED_COMMAND: true, IS_READ_ONLY: true, + /** + * Constructs the PING command + * + * @param parser - The command parser + * @param message - Optional message to be returned instead of PONG + * @see https://redis.io/commands/ping/ + */ parseCommand(parser: CommandParser, message?: RedisArgument) { parser.push('PING'); if (message) { diff --git a/packages/client/lib/commands/PSETEX.ts b/packages/client/lib/commands/PSETEX.ts index 03a58546d67..5b6d83bd694 100644 --- a/packages/client/lib/commands/PSETEX.ts +++ b/packages/client/lib/commands/PSETEX.ts @@ -2,6 +2,15 @@ import { CommandParser } from '../client/parser'; import { RedisArgument, SimpleStringReply, Command } from '../RESP/types'; export default { + /** + * Constructs the PSETEX command + * + * @param parser - The command parser + * @param key - The key to set + * @param ms - The expiration time in milliseconds + * @param value - The value to set + * @see https://redis.io/commands/psetex/ + */ parseCommand(parser: CommandParser, key: RedisArgument, ms: number, value: RedisArgument) { parser.push('PSETEX'); parser.pushKey(key); diff --git a/packages/client/lib/commands/PTTL.ts b/packages/client/lib/commands/PTTL.ts index 5717c51179f..9d408aeee17 100644 --- a/packages/client/lib/commands/PTTL.ts +++ b/packages/client/lib/commands/PTTL.ts @@ -3,6 +3,13 @@ import { RedisArgument, NumberReply, Command } from '../RESP/types'; export default { IS_READ_ONLY: true, + /** + * Constructs the PTTL command + * + * @param parser - The command parser + * @param key - The key to get the time to live in milliseconds + * @see https://redis.io/commands/pttl/ + */ parseCommand(parser: CommandParser, key: RedisArgument) { parser.push('PTTL'); parser.pushKey(key); diff --git a/packages/client/lib/commands/PUBLISH.ts b/packages/client/lib/commands/PUBLISH.ts index 557efd18834..197a2b069eb 100644 --- a/packages/client/lib/commands/PUBLISH.ts +++ b/packages/client/lib/commands/PUBLISH.ts @@ -5,6 +5,14 @@ export default { NOT_KEYED_COMMAND: true, IS_READ_ONLY: true, IS_FORWARD_COMMAND: true, + /** + * Constructs the PUBLISH command + * + * @param parser - The command parser + * @param channel - The channel to publish to + * @param message - The message to publish + * @see https://redis.io/commands/publish/ + */ parseCommand(parser: CommandParser, channel: RedisArgument, message: RedisArgument) { parser.push('PUBLISH', channel, message); }, diff --git a/packages/client/lib/commands/PUBSUB_CHANNELS.ts b/packages/client/lib/commands/PUBSUB_CHANNELS.ts index 0f53c79a78a..c9eb9bf7b4e 100644 --- a/packages/client/lib/commands/PUBSUB_CHANNELS.ts +++ b/packages/client/lib/commands/PUBSUB_CHANNELS.ts @@ -4,6 +4,13 @@ import { RedisArgument, ArrayReply, BlobStringReply, Command } from '../RESP/typ export default { NOT_KEYED_COMMAND: true, IS_READ_ONLY: true, + /** + * Constructs the PUBSUB CHANNELS command + * + * @param parser - The command parser + * @param pattern - Optional pattern to filter channels + * @see https://redis.io/commands/pubsub-channels/ + */ parseCommand(parser: CommandParser, pattern?: RedisArgument) { parser.push('PUBSUB', 'CHANNELS'); diff --git a/packages/client/lib/commands/PUBSUB_NUMPAT.ts b/packages/client/lib/commands/PUBSUB_NUMPAT.ts index 173446e023b..4b876db88f1 100644 --- a/packages/client/lib/commands/PUBSUB_NUMPAT.ts +++ b/packages/client/lib/commands/PUBSUB_NUMPAT.ts @@ -4,6 +4,12 @@ import { NumberReply, Command } from '../RESP/types'; export default { NOT_KEYED_COMMAND: true, IS_READ_ONLY: true, + /** + * Constructs the PUBSUB NUMPAT command + * + * @param parser - The command parser + * @see https://redis.io/commands/pubsub-numpat/ + */ parseCommand(parser: CommandParser) { parser.push('PUBSUB', 'NUMPAT'); }, diff --git a/packages/client/lib/commands/PUBSUB_NUMSUB.ts b/packages/client/lib/commands/PUBSUB_NUMSUB.ts index cc74d5d8a73..da6647dc553 100644 --- a/packages/client/lib/commands/PUBSUB_NUMSUB.ts +++ b/packages/client/lib/commands/PUBSUB_NUMSUB.ts @@ -5,6 +5,13 @@ import { RedisVariadicArgument } from './generic-transformers'; export default { NOT_KEYED_COMMAND: true, IS_READ_ONLY: true, + /** + * Constructs the PUBSUB NUMSUB command + * + * @param parser - The command parser + * @param channels - Optional channel names to get subscription count for + * @see https://redis.io/commands/pubsub-numsub/ + */ parseCommand(parser: CommandParser, channels?: RedisVariadicArgument) { parser.push('PUBSUB', 'NUMSUB'); @@ -12,6 +19,12 @@ export default { parser.pushVariadic(channels); } }, + /** + * Transforms the PUBSUB NUMSUB reply into a record of channel name to subscriber count + * + * @param rawReply - The raw reply from Redis + * @returns Record mapping channel names to their subscriber counts + */ transformReply(rawReply: UnwrapReply>) { const reply = Object.create(null); let i = 0; diff --git a/packages/client/lib/commands/PUBSUB_SHARDCHANNELS.ts b/packages/client/lib/commands/PUBSUB_SHARDCHANNELS.ts index 46ac2005fc3..30601de55df 100644 --- a/packages/client/lib/commands/PUBSUB_SHARDCHANNELS.ts +++ b/packages/client/lib/commands/PUBSUB_SHARDCHANNELS.ts @@ -4,6 +4,13 @@ import { RedisArgument, ArrayReply, BlobStringReply, Command } from '../RESP/typ export default { NOT_KEYED_COMMAND: true, IS_READ_ONLY: true, + /** + * Constructs the PUBSUB SHARDCHANNELS command + * + * @param parser - The command parser + * @param pattern - Optional pattern to filter shard channels + * @see https://redis.io/commands/pubsub-shardchannels/ + */ parseCommand(parser: CommandParser, pattern?: RedisArgument) { parser.push('PUBSUB', 'SHARDCHANNELS'); diff --git a/packages/client/lib/commands/PUBSUB_SHARDNUMSUB.ts b/packages/client/lib/commands/PUBSUB_SHARDNUMSUB.ts index 220eadeabe3..9d54a113d78 100644 --- a/packages/client/lib/commands/PUBSUB_SHARDNUMSUB.ts +++ b/packages/client/lib/commands/PUBSUB_SHARDNUMSUB.ts @@ -4,6 +4,13 @@ import { RedisVariadicArgument } from './generic-transformers'; export default { IS_READ_ONLY: true, + /** + * Constructs the PUBSUB SHARDNUMSUB command + * + * @param parser - The command parser + * @param channels - Optional shard channel names to get subscription count for + * @see https://redis.io/commands/pubsub-shardnumsub/ + */ parseCommand(parser: CommandParser, channels?: RedisVariadicArgument) { parser.push('PUBSUB', 'SHARDNUMSUB'); @@ -11,6 +18,12 @@ export default { parser.pushVariadic(channels); } }, + /** + * Transforms the PUBSUB SHARDNUMSUB reply into a record of shard channel name to subscriber count + * + * @param reply - The raw reply from Redis + * @returns Record mapping shard channel names to their subscriber counts + */ transformReply(reply: UnwrapReply>) { const transformedReply: Record = Object.create(null); diff --git a/packages/client/lib/commands/RANDOMKEY.ts b/packages/client/lib/commands/RANDOMKEY.ts index 97d040a0d1d..263f539113b 100644 --- a/packages/client/lib/commands/RANDOMKEY.ts +++ b/packages/client/lib/commands/RANDOMKEY.ts @@ -4,6 +4,12 @@ import { NumberReply, Command } from '../RESP/types'; export default { NOT_KEYED_COMMAND: true, IS_READ_ONLY: true, + /** + * Constructs the RANDOMKEY command + * + * @param parser - The command parser + * @see https://redis.io/commands/randomkey/ + */ parseCommand(parser: CommandParser) { parser.push('RANDOMKEY'); }, diff --git a/packages/client/lib/commands/READONLY.ts b/packages/client/lib/commands/READONLY.ts index ce3300c5321..16eef975818 100644 --- a/packages/client/lib/commands/READONLY.ts +++ b/packages/client/lib/commands/READONLY.ts @@ -4,6 +4,12 @@ import { SimpleStringReply, Command } from '../RESP/types'; export default { NOT_KEYED_COMMAND: true, IS_READ_ONLY: true, + /** + * Constructs the READONLY command + * + * @param parser - The command parser + * @see https://redis.io/commands/readonly/ + */ parseCommand(parser: CommandParser) { parser.push('READONLY'); }, diff --git a/packages/client/lib/commands/READWRITE.ts b/packages/client/lib/commands/READWRITE.ts index 7d9d8c7e00a..f747366448c 100644 --- a/packages/client/lib/commands/READWRITE.ts +++ b/packages/client/lib/commands/READWRITE.ts @@ -4,6 +4,12 @@ import { SimpleStringReply, Command } from '../RESP/types'; export default { NOT_KEYED_COMMAND: true, IS_READ_ONLY: true, + /** + * Constructs the READWRITE command + * + * @param parser - The command parser + * @see https://redis.io/commands/readwrite/ + */ parseCommand(parser: CommandParser) { parser.push('READWRITE'); }, diff --git a/packages/client/lib/commands/RENAME.ts b/packages/client/lib/commands/RENAME.ts index 245851ca31a..0033758d128 100644 --- a/packages/client/lib/commands/RENAME.ts +++ b/packages/client/lib/commands/RENAME.ts @@ -3,6 +3,14 @@ import { RedisArgument, SimpleStringReply, Command } from '../RESP/types'; export default { IS_READ_ONLY: true, + /** + * Constructs the RENAME command + * + * @param parser - The command parser + * @param key - The key to rename + * @param newKey - The new key name + * @see https://redis.io/commands/rename/ + */ parseCommand(parser: CommandParser, key: RedisArgument, newKey: RedisArgument) { parser.push('RENAME'); parser.pushKeys([key, newKey]); diff --git a/packages/client/lib/commands/RENAMENX.ts b/packages/client/lib/commands/RENAMENX.ts index 0e8d4f73cf3..38c12dee727 100644 --- a/packages/client/lib/commands/RENAMENX.ts +++ b/packages/client/lib/commands/RENAMENX.ts @@ -3,6 +3,14 @@ import { RedisArgument, NumberReply, Command } from '../RESP/types'; export default { IS_READ_ONLY: true, + /** + * Constructs the RENAMENX command + * + * @param parser - The command parser + * @param key - The key to rename + * @param newKey - The new key name, if it doesn't exist + * @see https://redis.io/commands/renamenx/ + */ parseCommand(parser: CommandParser, key: RedisArgument, newKey: RedisArgument) { parser.push('RENAMENX'); parser.pushKeys([key, newKey]); diff --git a/packages/client/lib/commands/REPLICAOF.ts b/packages/client/lib/commands/REPLICAOF.ts index c4b09bc4fb8..08d4167fff4 100644 --- a/packages/client/lib/commands/REPLICAOF.ts +++ b/packages/client/lib/commands/REPLICAOF.ts @@ -4,6 +4,14 @@ import { SimpleStringReply, Command } from '../RESP/types'; export default { NOT_KEYED_COMMAND: true, IS_READ_ONLY: true, + /** + * Constructs the REPLICAOF command + * + * @param parser - The command parser + * @param host - The host of the master to replicate from + * @param port - The port of the master to replicate from + * @see https://redis.io/commands/replicaof/ + */ parseCommand(parser: CommandParser, host: string, port: number) { parser.push('REPLICAOF', host, port.toString()); }, diff --git a/packages/client/lib/commands/RESTORE-ASKING.ts b/packages/client/lib/commands/RESTORE-ASKING.ts index e8de532b6a4..947ee9544d9 100644 --- a/packages/client/lib/commands/RESTORE-ASKING.ts +++ b/packages/client/lib/commands/RESTORE-ASKING.ts @@ -4,6 +4,12 @@ import { SimpleStringReply, Command } from '../RESP/types'; export default { NOT_KEYED_COMMAND: true, IS_READ_ONLY: true, + /** + * Constructs the RESTORE-ASKING command + * + * @param parser - The command parser + * @see https://redis.io/commands/restore-asking/ + */ parseCommand(parser: CommandParser) { parser.push('RESTORE-ASKING'); }, diff --git a/packages/client/lib/commands/RESTORE.ts b/packages/client/lib/commands/RESTORE.ts index 49016c525bd..5b07a773cc4 100644 --- a/packages/client/lib/commands/RESTORE.ts +++ b/packages/client/lib/commands/RESTORE.ts @@ -1,6 +1,14 @@ import { CommandParser } from '../client/parser'; import { RedisArgument, SimpleStringReply, Command } from '../RESP/types'; +/** + * Options for the RESTORE command + * + * @property REPLACE - Replace existing key + * @property ABSTTL - Use the TTL value as absolute timestamp + * @property IDLETIME - Set the idle time (seconds) for the key + * @property FREQ - Set the frequency counter for LFU policy + */ export interface RestoreOptions { REPLACE?: boolean; ABSTTL?: boolean; @@ -10,6 +18,16 @@ export interface RestoreOptions { export default { IS_READ_ONLY: false, + /** + * Constructs the RESTORE command + * + * @param parser - The command parser + * @param key - The key to restore + * @param ttl - Time to live in milliseconds, 0 for no expiry + * @param serializedValue - The serialized value from DUMP command + * @param options - Options for the RESTORE command + * @see https://redis.io/commands/restore/ + */ parseCommand( parser: CommandParser, key: RedisArgument, diff --git a/packages/client/lib/commands/ROLE.ts b/packages/client/lib/commands/ROLE.ts index f45bbad5c01..749ac4935fa 100644 --- a/packages/client/lib/commands/ROLE.ts +++ b/packages/client/lib/commands/ROLE.ts @@ -1,12 +1,18 @@ import { CommandParser } from '../client/parser'; import { BlobStringReply, NumberReply, ArrayReply, TuplesReply, UnwrapReply, Command } from '../RESP/types'; +/** + * Role information returned for a Redis master + */ type MasterRole = [ role: BlobStringReply<'master'>, replicationOffest: NumberReply, replicas: ArrayReply> ]; +/** + * Role information returned for a Redis slave + */ type SlaveRole = [ role: BlobStringReply<'slave'>, masterHost: BlobStringReply, @@ -15,19 +21,37 @@ type SlaveRole = [ dataReceived: NumberReply ]; +/** + * Role information returned for a Redis sentinel + */ type SentinelRole = [ role: BlobStringReply<'sentinel'>, masterNames: ArrayReply ]; +/** + * Combined role type for Redis instance role information + */ type Role = TuplesReply; export default { NOT_KEYED_COMMAND: true, IS_READ_ONLY: true, + /** + * Constructs the ROLE command + * + * @param parser - The command parser + * @see https://redis.io/commands/role/ + */ parseCommand(parser: CommandParser) { parser.push('ROLE'); }, + /** + * Transforms the ROLE reply into a structured object + * + * @param reply - The raw reply from Redis + * @returns Structured object representing role information + */ transformReply(reply: UnwrapReply) { switch (reply[0] as unknown as UnwrapReply) { case 'master': { diff --git a/packages/client/lib/commands/RPOP.ts b/packages/client/lib/commands/RPOP.ts index 4cc105c3704..4e284496579 100644 --- a/packages/client/lib/commands/RPOP.ts +++ b/packages/client/lib/commands/RPOP.ts @@ -2,6 +2,13 @@ import { CommandParser } from '../client/parser'; import { RedisArgument, BlobStringReply, NullReply, Command } from '../RESP/types'; export default { + /** + * Constructs the RPOP command + * + * @param parser - The command parser + * @param key - The list key to pop from + * @see https://redis.io/commands/rpop/ + */ parseCommand(parser: CommandParser, key: RedisArgument) { parser.push('RPOP'); parser.pushKey(key); diff --git a/packages/client/lib/commands/RPOPLPUSH.ts b/packages/client/lib/commands/RPOPLPUSH.ts index dcac0472235..936aeb01c8f 100644 --- a/packages/client/lib/commands/RPOPLPUSH.ts +++ b/packages/client/lib/commands/RPOPLPUSH.ts @@ -2,6 +2,14 @@ import { CommandParser } from '../client/parser'; import { RedisArgument, BlobStringReply, NullReply, Command } from '../RESP/types'; export default { + /** + * Constructs the RPOPLPUSH command + * + * @param parser - The command parser + * @param source - The source list key + * @param destination - The destination list key + * @see https://redis.io/commands/rpoplpush/ + */ parseCommand(parser: CommandParser, source: RedisArgument, destination: RedisArgument) { parser.push('RPOPLPUSH'); parser.pushKeys([source, destination]); diff --git a/packages/client/lib/commands/RPOP_COUNT.ts b/packages/client/lib/commands/RPOP_COUNT.ts index aff91c6a6f7..2a60335da94 100644 --- a/packages/client/lib/commands/RPOP_COUNT.ts +++ b/packages/client/lib/commands/RPOP_COUNT.ts @@ -2,6 +2,14 @@ import { CommandParser } from '../client/parser'; import { RedisArgument, ArrayReply, BlobStringReply, NullReply, Command } from '../RESP/types'; export default { + /** + * Constructs the RPOP command with count parameter + * + * @param parser - The command parser + * @param key - The list key to pop from + * @param count - The number of elements to pop + * @see https://redis.io/commands/rpop/ + */ parseCommand(parser: CommandParser, key: RedisArgument, count: number) { parser.push('RPOP'); parser.pushKey(key); diff --git a/packages/client/lib/commands/RPUSH.ts b/packages/client/lib/commands/RPUSH.ts index b820aae6906..452623e7f0d 100644 --- a/packages/client/lib/commands/RPUSH.ts +++ b/packages/client/lib/commands/RPUSH.ts @@ -3,6 +3,14 @@ import { RedisArgument, NumberReply, Command } from '../RESP/types'; import { RedisVariadicArgument } from './generic-transformers'; export default { + /** + * Constructs the RPUSH command + * + * @param parser - The command parser + * @param key - The list key to push to + * @param element - One or more elements to push + * @see https://redis.io/commands/rpush/ + */ parseCommand(parser: CommandParser, key: RedisArgument, element: RedisVariadicArgument) { parser.push('RPUSH'); parser.pushKey(key); diff --git a/packages/client/lib/commands/RPUSHX.ts b/packages/client/lib/commands/RPUSHX.ts index 243f717bb78..a9ec4bd1ef6 100644 --- a/packages/client/lib/commands/RPUSHX.ts +++ b/packages/client/lib/commands/RPUSHX.ts @@ -3,6 +3,14 @@ import { RedisArgument, NumberReply, Command } from '../RESP/types'; import { RedisVariadicArgument } from './generic-transformers'; export default { + /** + * Constructs the RPUSHX command + * + * @param parser - The command parser + * @param key - The list key to push to (only if it exists) + * @param element - One or more elements to push + * @see https://redis.io/commands/rpushx/ + */ parseCommand(parser: CommandParser, key: RedisArgument, element: RedisVariadicArgument) { parser.push('RPUSHX'); parser.pushKey(key); diff --git a/packages/client/lib/commands/SADD.ts b/packages/client/lib/commands/SADD.ts index 1fb0171d8d4..3ee55706b95 100644 --- a/packages/client/lib/commands/SADD.ts +++ b/packages/client/lib/commands/SADD.ts @@ -3,6 +3,14 @@ import { RedisArgument, NumberReply, Command } from '../RESP/types'; import { RedisVariadicArgument } from './generic-transformers'; export default { + /** + * Constructs the SADD command + * + * @param parser - The command parser + * @param key - The set key to add members to + * @param members - One or more members to add to the set + * @see https://redis.io/commands/sadd/ + */ parseCommand(parser: CommandParser, key: RedisArgument, members: RedisVariadicArgument) { parser.push('SADD'); parser.pushKey(key); diff --git a/packages/client/lib/commands/SAVE.ts b/packages/client/lib/commands/SAVE.ts index ee78884083c..078b14da7a3 100644 --- a/packages/client/lib/commands/SAVE.ts +++ b/packages/client/lib/commands/SAVE.ts @@ -4,6 +4,12 @@ import { SimpleStringReply, Command } from '../RESP/types'; export default { NOT_KEYED_COMMAND: true, IS_READ_ONLY: true, + /** + * Constructs the SAVE command + * + * @param parser - The command parser + * @see https://redis.io/commands/save/ + */ parseCommand(parser: CommandParser) { parser.push('SAVE'); }, diff --git a/packages/client/lib/commands/SCAN.ts b/packages/client/lib/commands/SCAN.ts index 2d6e4c35258..41991a24172 100644 --- a/packages/client/lib/commands/SCAN.ts +++ b/packages/client/lib/commands/SCAN.ts @@ -1,11 +1,24 @@ import { CommandParser } from '../client/parser'; import { RedisArgument, CommandArguments, BlobStringReply, ArrayReply, Command } from '../RESP/types'; +/** + * Common options for SCAN-type commands + * + * @property MATCH - Pattern to filter returned keys + * @property COUNT - Hint for how many elements to return per iteration + */ export interface ScanCommonOptions { MATCH?: string; COUNT?: number; } +/** + * Parses scan arguments for SCAN-type commands + * + * @param parser - The command parser + * @param cursor - The cursor position for iteration + * @param options - Scan options + */ export function parseScanArguments( parser: CommandParser, cursor: RedisArgument, @@ -21,6 +34,14 @@ export function parseScanArguments( } } +/** + * Pushes scan arguments to the command arguments array + * + * @param args - The command arguments array + * @param cursor - The cursor position for iteration + * @param options - Scan options + * @returns The updated command arguments array + */ export function pushScanArguments( args: CommandArguments, cursor: RedisArgument, @@ -39,6 +60,11 @@ export function pushScanArguments( return args; } +/** + * Options for the SCAN command + * + * @property TYPE - Filter by value type + */ export interface ScanOptions extends ScanCommonOptions { TYPE?: RedisArgument; } @@ -46,6 +72,14 @@ export interface ScanOptions extends ScanCommonOptions { export default { NOT_KEYED_COMMAND: true, IS_READ_ONLY: true, + /** + * Constructs the SCAN command + * + * @param parser - The command parser + * @param cursor - The cursor position to start scanning from + * @param options - Scan options + * @see https://redis.io/commands/scan/ + */ parseCommand(parser: CommandParser, cursor: RedisArgument, options?: ScanOptions) { parser.push('SCAN'); parseScanArguments(parser, cursor, options); @@ -54,6 +88,12 @@ export default { parser.push('TYPE', options.TYPE); } }, + /** + * Transforms the SCAN reply into a structured object + * + * @param reply - The raw reply containing cursor and keys + * @returns Object with cursor and keys properties + */ transformReply([cursor, keys]: [BlobStringReply, ArrayReply]) { return { cursor, diff --git a/packages/client/lib/commands/SCARD.ts b/packages/client/lib/commands/SCARD.ts index 61d4792d996..20a2aefae00 100644 --- a/packages/client/lib/commands/SCARD.ts +++ b/packages/client/lib/commands/SCARD.ts @@ -4,6 +4,13 @@ import { RedisArgument, NumberReply, Command } from '../RESP/types'; export default { CACHEABLE: true, IS_READ_ONLY: true, + /** + * Constructs the SCARD command + * + * @param parser - The command parser + * @param key - The set key to get the cardinality of + * @see https://redis.io/commands/scard/ + */ parseCommand(parser: CommandParser, key: RedisArgument) { parser.push('SCARD'); parser.pushKey(key); diff --git a/packages/client/lib/commands/SCRIPT_DEBUG.ts b/packages/client/lib/commands/SCRIPT_DEBUG.ts index b0d3079068f..3f09c550449 100644 --- a/packages/client/lib/commands/SCRIPT_DEBUG.ts +++ b/packages/client/lib/commands/SCRIPT_DEBUG.ts @@ -4,6 +4,13 @@ import { SimpleStringReply, Command } from '../RESP/types'; export default { NOT_KEYED_COMMAND: true, IS_READ_ONLY: true, + /** + * Constructs the SCRIPT DEBUG command + * + * @param parser - The command parser + * @param mode - Debug mode: YES, SYNC, or NO + * @see https://redis.io/commands/script-debug/ + */ parseCommand(parser: CommandParser, mode: 'YES' | 'SYNC' | 'NO') { parser.push('SCRIPT', 'DEBUG', mode); }, diff --git a/packages/client/lib/commands/SCRIPT_EXISTS.ts b/packages/client/lib/commands/SCRIPT_EXISTS.ts index b0f6cbe2275..66479654a0d 100644 --- a/packages/client/lib/commands/SCRIPT_EXISTS.ts +++ b/packages/client/lib/commands/SCRIPT_EXISTS.ts @@ -5,6 +5,13 @@ import { RedisVariadicArgument } from './generic-transformers'; export default { NOT_KEYED_COMMAND: true, IS_READ_ONLY: true, + /** + * Constructs the SCRIPT EXISTS command + * + * @param parser - The command parser + * @param sha1 - One or more SHA1 digests of scripts + * @see https://redis.io/commands/script-exists/ + */ parseCommand(parser: CommandParser, sha1: RedisVariadicArgument) { parser.push('SCRIPT', 'EXISTS'); parser.pushVariadic(sha1); diff --git a/packages/client/lib/commands/SCRIPT_FLUSH.ts b/packages/client/lib/commands/SCRIPT_FLUSH.ts index 1e05a619bad..91b61a4e59a 100644 --- a/packages/client/lib/commands/SCRIPT_FLUSH.ts +++ b/packages/client/lib/commands/SCRIPT_FLUSH.ts @@ -4,6 +4,13 @@ import { SimpleStringReply, Command } from '../RESP/types'; export default { NOT_KEYED_COMMAND: true, IS_READ_ONLY: true, + /** + * Constructs the SCRIPT FLUSH command + * + * @param parser - The command parser + * @param mode - Optional flush mode: ASYNC or SYNC + * @see https://redis.io/commands/script-flush/ + */ parseCommand(parser: CommandParser, mode?: 'ASYNC' | 'SYNC') { parser.push('SCRIPT', 'FLUSH'); diff --git a/packages/client/lib/commands/SCRIPT_KILL.ts b/packages/client/lib/commands/SCRIPT_KILL.ts index 26953506235..ee2b2835cc1 100644 --- a/packages/client/lib/commands/SCRIPT_KILL.ts +++ b/packages/client/lib/commands/SCRIPT_KILL.ts @@ -4,6 +4,12 @@ import { SimpleStringReply, Command } from '../RESP/types'; export default { NOT_KEYED_COMMAND: true, IS_READ_ONLY: true, + /** + * Constructs the SCRIPT KILL command + * + * @param parser - The command parser + * @see https://redis.io/commands/script-kill/ + */ parseCommand(parser: CommandParser) { parser.push('SCRIPT', 'KILL'); }, diff --git a/packages/client/lib/commands/SCRIPT_LOAD.ts b/packages/client/lib/commands/SCRIPT_LOAD.ts index 58f7c00dfcd..6e9acb388fc 100644 --- a/packages/client/lib/commands/SCRIPT_LOAD.ts +++ b/packages/client/lib/commands/SCRIPT_LOAD.ts @@ -4,6 +4,13 @@ import { BlobStringReply, Command, RedisArgument } from '../RESP/types'; export default { NOT_KEYED_COMMAND: true, IS_READ_ONLY: true, + /** + * Constructs the SCRIPT LOAD command + * + * @param parser - The command parser + * @param script - The Lua script to load + * @see https://redis.io/commands/script-load/ + */ parseCommand(parser: CommandParser, script: RedisArgument) { parser.push('SCRIPT', 'LOAD', script); }, diff --git a/packages/client/lib/commands/SDIFF.ts b/packages/client/lib/commands/SDIFF.ts index bd78edc93db..07d700adac6 100644 --- a/packages/client/lib/commands/SDIFF.ts +++ b/packages/client/lib/commands/SDIFF.ts @@ -5,6 +5,13 @@ import { RedisVariadicArgument } from './generic-transformers'; export default { CACHEABLE: true, IS_READ_ONLY: true, + /** + * Constructs the SDIFF command + * + * @param parser - The command parser + * @param keys - One or more set keys to compute the difference from + * @see https://redis.io/commands/sdiff/ + */ parseCommand(parser: CommandParser, keys: RedisVariadicArgument) { parser.push('SDIFF'); parser.pushKeys(keys); diff --git a/packages/client/lib/commands/SDIFFSTORE.ts b/packages/client/lib/commands/SDIFFSTORE.ts index 6da2795d8ff..478d015d8c0 100644 --- a/packages/client/lib/commands/SDIFFSTORE.ts +++ b/packages/client/lib/commands/SDIFFSTORE.ts @@ -3,6 +3,14 @@ import { RedisArgument, NumberReply, Command } from '../RESP/types'; import { RedisVariadicArgument } from './generic-transformers'; export default { + /** + * Constructs the SDIFFSTORE command + * + * @param parser - The command parser + * @param destination - The destination key to store the result + * @param keys - One or more set keys to compute the difference from + * @see https://redis.io/commands/sdiffstore/ + */ parseCommand(parser: CommandParser, destination: RedisArgument, keys: RedisVariadicArgument) { parser.push('SDIFFSTORE'); parser.pushKey(destination); diff --git a/packages/client/lib/commands/SET.ts b/packages/client/lib/commands/SET.ts index d2d13c874c4..d1384255679 100644 --- a/packages/client/lib/commands/SET.ts +++ b/packages/client/lib/commands/SET.ts @@ -43,6 +43,15 @@ export interface SetOptions { } export default { + /** + * Constructs the SET command + * + * @param parser - The command parser + * @param key - The key to set + * @param value - The value to set + * @param options - Additional options for the SET command + * @see https://redis.io/commands/set/ + */ parseCommand(parser: CommandParser, key: RedisArgument, value: RedisArgument | number, options?: SetOptions) { parser.push('SET'); parser.pushKey(key); diff --git a/packages/client/lib/commands/SETBIT.ts b/packages/client/lib/commands/SETBIT.ts index 5cd29260071..b9c29796db9 100644 --- a/packages/client/lib/commands/SETBIT.ts +++ b/packages/client/lib/commands/SETBIT.ts @@ -4,6 +4,15 @@ import { BitValue } from './generic-transformers'; export default { IS_READ_ONLY: false, + /** + * Constructs the SETBIT command + * + * @param parser - The command parser + * @param key - The key to set the bit on + * @param offset - The bit offset (zero-based) + * @param value - The bit value (0 or 1) + * @see https://redis.io/commands/setbit/ + */ parseCommand(parser: CommandParser, key: RedisArgument, offset: number, value: BitValue) { parser.push('SETBIT'); parser.pushKey(key); diff --git a/packages/client/lib/commands/SETEX.ts b/packages/client/lib/commands/SETEX.ts index 5e58b589975..39c7c60f53b 100644 --- a/packages/client/lib/commands/SETEX.ts +++ b/packages/client/lib/commands/SETEX.ts @@ -2,6 +2,15 @@ import { CommandParser } from '../client/parser'; import { RedisArgument, SimpleStringReply, Command } from '../RESP/types'; export default { + /** + * Constructs the SETEX command + * + * @param parser - The command parser + * @param key - The key to set + * @param seconds - The expiration time in seconds + * @param value - The value to set + * @see https://redis.io/commands/setex/ + */ parseCommand(parser: CommandParser, key: RedisArgument, seconds: number, value: RedisArgument) { parser.push('SETEX'); parser.pushKey(key); diff --git a/packages/client/lib/commands/SETNX.ts b/packages/client/lib/commands/SETNX.ts index ae60067c28f..b32b6c5ef34 100644 --- a/packages/client/lib/commands/SETNX.ts +++ b/packages/client/lib/commands/SETNX.ts @@ -2,6 +2,14 @@ import { CommandParser } from '../client/parser'; import { RedisArgument, NumberReply, Command } from '../RESP/types'; export default { + /** + * Constructs the SETNX command + * + * @param parser - The command parser + * @param key - The key to set if it doesn't exist + * @param value - The value to set + * @see https://redis.io/commands/setnx/ + */ parseCommand(parser: CommandParser, key: RedisArgument, value: RedisArgument) { parser.push('SETNX'); parser.pushKey(key); diff --git a/packages/client/lib/commands/SETRANGE.ts b/packages/client/lib/commands/SETRANGE.ts index 42f4ca01117..366e6c28a7d 100644 --- a/packages/client/lib/commands/SETRANGE.ts +++ b/packages/client/lib/commands/SETRANGE.ts @@ -2,6 +2,15 @@ import { CommandParser } from '../client/parser'; import { RedisArgument, NumberReply, Command } from '../RESP/types'; export default { + /** + * Constructs the SETRANGE command + * + * @param parser - The command parser + * @param key - The key to modify + * @param offset - The offset at which to start writing + * @param value - The value to write at the offset + * @see https://redis.io/commands/setrange/ + */ parseCommand(parser: CommandParser, key: RedisArgument, offset: number, value: RedisArgument) { parser.push('SETRANGE'); parser.pushKey(key); diff --git a/packages/client/lib/commands/SHUTDOWN.ts b/packages/client/lib/commands/SHUTDOWN.ts index 33fb3e77301..6a5416d430c 100644 --- a/packages/client/lib/commands/SHUTDOWN.ts +++ b/packages/client/lib/commands/SHUTDOWN.ts @@ -1,6 +1,14 @@ import { CommandParser } from '../client/parser'; import { SimpleStringReply, Command } from '../RESP/types'; +/** + * Options for the SHUTDOWN command + * + * @property mode - NOSAVE will not save DB, SAVE will force save DB + * @property NOW - Immediately terminate all clients + * @property FORCE - Force shutdown even in case of errors + * @property ABORT - Abort a shutdown in progress + */ export interface ShutdownOptions { mode?: 'NOSAVE' | 'SAVE'; NOW?: boolean; @@ -11,6 +19,13 @@ export interface ShutdownOptions { export default { NOT_KEYED_COMMAND: true, IS_READ_ONLY: false, + /** + * Constructs the SHUTDOWN command + * + * @param parser - The command parser + * @param options - Options for the shutdown process + * @see https://redis.io/commands/shutdown/ + */ parseCommand(parser: CommandParser, options?: ShutdownOptions) { parser.push('SHUTDOWN'); diff --git a/packages/client/lib/commands/SINTER.ts b/packages/client/lib/commands/SINTER.ts index 19ecdbb41ca..a129d71fd7a 100644 --- a/packages/client/lib/commands/SINTER.ts +++ b/packages/client/lib/commands/SINTER.ts @@ -5,6 +5,13 @@ import { RedisVariadicArgument } from './generic-transformers'; export default { CACHEABLE: true, IS_READ_ONLY: true, + /** + * Constructs the SINTER command + * + * @param parser - The command parser + * @param keys - One or more set keys to compute the intersection from + * @see https://redis.io/commands/sinter/ + */ parseCommand(parser: CommandParser, keys: RedisVariadicArgument) { parser.push('SINTER'); parser.pushKeys(keys); diff --git a/packages/client/lib/commands/SINTERCARD.ts b/packages/client/lib/commands/SINTERCARD.ts index cb9e7d3be3d..191c0881a8d 100644 --- a/packages/client/lib/commands/SINTERCARD.ts +++ b/packages/client/lib/commands/SINTERCARD.ts @@ -2,13 +2,25 @@ import { CommandParser } from '../client/parser'; import { NumberReply, Command } from '../RESP/types'; import { RedisVariadicArgument } from './generic-transformers'; +/** + * Options for the SINTERCARD command + * + * @property LIMIT - Maximum number of elements to return + */ export interface SInterCardOptions { LIMIT?: number; } export default { IS_READ_ONLY: true, - // option `number` for backwards compatibility + /** + * Constructs the SINTERCARD command + * + * @param parser - The command parser + * @param keys - One or more set keys to compute the intersection cardinality from + * @param options - Options for the SINTERCARD command or a number for LIMIT (backwards compatibility) + * @see https://redis.io/commands/sintercard/ + */ parseCommand(parser: CommandParser, keys: RedisVariadicArgument, options?: SInterCardOptions | number) { parser.push('SINTERCARD'); parser.pushKeysLength(keys); diff --git a/packages/client/lib/commands/SINTERSTORE.ts b/packages/client/lib/commands/SINTERSTORE.ts index 06db0af9cb0..377b63fbddc 100644 --- a/packages/client/lib/commands/SINTERSTORE.ts +++ b/packages/client/lib/commands/SINTERSTORE.ts @@ -4,6 +4,14 @@ import { RedisVariadicArgument } from './generic-transformers'; export default { IS_READ_ONLY: false, + /** + * Constructs the SINTERSTORE command + * + * @param parser - The command parser + * @param destination - The destination key to store the result + * @param keys - One or more set keys to compute the intersection from + * @see https://redis.io/commands/sinterstore/ + */ parseCommand(parser: CommandParser, destination: RedisArgument, keys: RedisVariadicArgument) { parser.push('SINTERSTORE'); parser.pushKey(destination) diff --git a/packages/client/lib/commands/SISMEMBER.ts b/packages/client/lib/commands/SISMEMBER.ts index 6192ca2605f..3310d43d97b 100644 --- a/packages/client/lib/commands/SISMEMBER.ts +++ b/packages/client/lib/commands/SISMEMBER.ts @@ -4,6 +4,14 @@ import { NumberReply, Command, RedisArgument } from '../RESP/types'; export default { CACHEABLE: true, IS_READ_ONLY: true, + /** + * Constructs the SISMEMBER command + * + * @param parser - The command parser + * @param key - The set key to check membership in + * @param member - The member to check for existence + * @see https://redis.io/commands/sismember/ + */ parseCommand(parser: CommandParser, key: RedisArgument, member: RedisArgument) { parser.push('SISMEMBER'); parser.pushKey(key); diff --git a/packages/client/lib/commands/SMEMBERS.ts b/packages/client/lib/commands/SMEMBERS.ts index 6d018e999f4..399ffd86147 100644 --- a/packages/client/lib/commands/SMEMBERS.ts +++ b/packages/client/lib/commands/SMEMBERS.ts @@ -4,6 +4,13 @@ import { RedisArgument, ArrayReply, BlobStringReply, SetReply, Command } from '. export default { CACHEABLE: true, IS_READ_ONLY: true, + /** + * Constructs the SMEMBERS command + * + * @param parser - The command parser + * @param key - The set key to get all members from + * @see https://redis.io/commands/smembers/ + */ parseCommand(parser: CommandParser, key: RedisArgument) { parser.push('SMEMBERS'); parser.pushKey(key); diff --git a/packages/client/lib/commands/SMISMEMBER.ts b/packages/client/lib/commands/SMISMEMBER.ts index f0f3a143c7f..b5950dcfd7f 100644 --- a/packages/client/lib/commands/SMISMEMBER.ts +++ b/packages/client/lib/commands/SMISMEMBER.ts @@ -4,6 +4,14 @@ import { RedisArgument, ArrayReply, NumberReply, Command } from '../RESP/types'; export default { CACHEABLE: true, IS_READ_ONLY: true, + /** + * Constructs the SMISMEMBER command + * + * @param parser - The command parser + * @param key - The set key to check membership in + * @param members - The members to check for existence + * @see https://redis.io/commands/smismember/ + */ parseCommand(parser: CommandParser, key: RedisArgument, members: Array) { parser.push('SMISMEMBER'); parser.pushKey(key); diff --git a/packages/client/lib/commands/SMOVE.ts b/packages/client/lib/commands/SMOVE.ts index d87eeefdfbf..d5f150b99f2 100644 --- a/packages/client/lib/commands/SMOVE.ts +++ b/packages/client/lib/commands/SMOVE.ts @@ -3,6 +3,15 @@ import { RedisArgument, NumberReply, Command } from '../RESP/types'; export default { IS_READ_ONLY: false, + /** + * Constructs the SMOVE command + * + * @param parser - The command parser + * @param source - The source set key + * @param destination - The destination set key + * @param member - The member to move + * @see https://redis.io/commands/smove/ + */ parseCommand(parser: CommandParser, source: RedisArgument, destination: RedisArgument, member: RedisArgument) { parser.push('SMOVE'); parser.pushKeys([source, destination]); diff --git a/packages/client/lib/commands/SORT.ts b/packages/client/lib/commands/SORT.ts index 3738d327d91..5ec889f3063 100644 --- a/packages/client/lib/commands/SORT.ts +++ b/packages/client/lib/commands/SORT.ts @@ -1,6 +1,15 @@ import { CommandParser } from '../client/parser'; import { RedisArgument, ArrayReply, BlobStringReply, Command } from '../RESP/types'; +/** + * Options for the SORT command + * + * @property BY - Pattern for external key to sort by + * @property LIMIT - Offset and count for results pagination + * @property GET - Pattern(s) for retrieving external keys + * @property DIRECTION - Sort direction: ASC (ascending) or DESC (descending) + * @property ALPHA - Sort lexicographically instead of numerically + */ export interface SortOptions { BY?: RedisArgument; LIMIT?: { @@ -12,6 +21,13 @@ export interface SortOptions { ALPHA?: boolean; } +/** + * Parses sort arguments for the SORT command + * + * @param parser - The command parser + * @param key - The key to sort + * @param options - Sort options + */ export function parseSortArguments( parser: CommandParser, key: RedisArgument, @@ -52,6 +68,14 @@ export function parseSortArguments( export default { IS_READ_ONLY: true, + /** + * Constructs the SORT command + * + * @param parser - The command parser + * @param key - The key to sort (list, set, or sorted set) + * @param options - Sort options + * @see https://redis.io/commands/sort/ + */ parseCommand(parser: CommandParser, key: RedisArgument, options?: SortOptions) { parser.push('SORT'); parseSortArguments(parser, key, options); diff --git a/packages/client/lib/commands/SORT_RO.ts b/packages/client/lib/commands/SORT_RO.ts index 9901907c223..5531f927d52 100644 --- a/packages/client/lib/commands/SORT_RO.ts +++ b/packages/client/lib/commands/SORT_RO.ts @@ -3,6 +3,10 @@ import SORT, { parseSortArguments } from './SORT'; export default { IS_READ_ONLY: true, + /** + * Read-only variant of SORT that sorts the elements in a list, set or sorted set. + * @param args - Same parameters as the SORT command. + */ parseCommand(...args: Parameters) { const parser = args[0]; diff --git a/packages/client/lib/commands/SORT_STORE.ts b/packages/client/lib/commands/SORT_STORE.ts index 15c94732e41..5fd52e076df 100644 --- a/packages/client/lib/commands/SORT_STORE.ts +++ b/packages/client/lib/commands/SORT_STORE.ts @@ -4,6 +4,13 @@ import SORT, { SortOptions } from './SORT'; export default { IS_READ_ONLY: false, + /** + * Sorts the elements in a list, set or sorted set and stores the result in a new list. + * @param parser - The Redis command parser. + * @param source - Key of the source list, set or sorted set. + * @param destination - Destination key where the result will be stored. + * @param options - Optional sorting parameters. + */ parseCommand(parser: CommandParser, source: RedisArgument, destination: RedisArgument, options?: SortOptions) { SORT.parseCommand(parser, source, options); parser.push('STORE', destination); diff --git a/packages/client/lib/commands/SPOP.ts b/packages/client/lib/commands/SPOP.ts index 38f40989e63..8e9450b2b01 100644 --- a/packages/client/lib/commands/SPOP.ts +++ b/packages/client/lib/commands/SPOP.ts @@ -3,6 +3,13 @@ import { RedisArgument, BlobStringReply, NullReply, Command } from '../RESP/type export default { IS_READ_ONLY: false, + /** + * Constructs the SPOP command to remove and return a random member from a set + * + * @param parser - The command parser + * @param key - The key of the set to pop from + * @see https://redis.io/commands/spop/ + */ parseCommand(parser: CommandParser, key: RedisArgument) { parser.push('SPOP'); parser.pushKey(key); diff --git a/packages/client/lib/commands/SPOP_COUNT.ts b/packages/client/lib/commands/SPOP_COUNT.ts index 0536203be97..1191f07cff2 100644 --- a/packages/client/lib/commands/SPOP_COUNT.ts +++ b/packages/client/lib/commands/SPOP_COUNT.ts @@ -3,6 +3,14 @@ import { RedisArgument, BlobStringReply, NullReply, Command } from '../RESP/type export default { IS_READ_ONLY: false, + /** + * Constructs the SPOP command to remove and return multiple random members from a set + * + * @param parser - The command parser + * @param key - The key of the set to pop from + * @param count - The number of members to pop + * @see https://redis.io/commands/spop/ + */ parseCommand(parser: CommandParser, key: RedisArgument, count: number) { parser.push('SPOP'); parser.pushKey(key); diff --git a/packages/client/lib/commands/SPUBLISH.ts b/packages/client/lib/commands/SPUBLISH.ts index 77d93e617de..6dd9f37e66b 100644 --- a/packages/client/lib/commands/SPUBLISH.ts +++ b/packages/client/lib/commands/SPUBLISH.ts @@ -3,6 +3,14 @@ import { RedisArgument, NumberReply, Command } from '../RESP/types'; export default { IS_READ_ONLY: true, + /** + * Constructs the SPUBLISH command to post a message to a Sharded Pub/Sub channel + * + * @param parser - The command parser + * @param channel - The channel to publish to + * @param message - The message to publish + * @see https://redis.io/commands/spublish/ + */ parseCommand(parser: CommandParser, channel: RedisArgument, message: RedisArgument) { parser.push('SPUBLISH'); parser.pushKey(channel); diff --git a/packages/client/lib/commands/SRANDMEMBER.ts b/packages/client/lib/commands/SRANDMEMBER.ts index 4285f7aa17c..9e04e45b52a 100644 --- a/packages/client/lib/commands/SRANDMEMBER.ts +++ b/packages/client/lib/commands/SRANDMEMBER.ts @@ -3,6 +3,13 @@ import { RedisArgument, BlobStringReply, NullReply, Command } from '../RESP/type export default { IS_READ_ONLY: true, + /** + * Constructs the SRANDMEMBER command to get a random member from a set + * + * @param parser - The command parser + * @param key - The key of the set to get random member from + * @see https://redis.io/commands/srandmember/ + */ parseCommand(parser: CommandParser, key: RedisArgument) { parser.push('SRANDMEMBER') parser.pushKey(key); diff --git a/packages/client/lib/commands/SRANDMEMBER_COUNT.ts b/packages/client/lib/commands/SRANDMEMBER_COUNT.ts index dd72245c3b3..c7dd434b710 100644 --- a/packages/client/lib/commands/SRANDMEMBER_COUNT.ts +++ b/packages/client/lib/commands/SRANDMEMBER_COUNT.ts @@ -4,6 +4,14 @@ import SRANDMEMBER from './SRANDMEMBER'; export default { IS_READ_ONLY: SRANDMEMBER.IS_READ_ONLY, + /** + * Constructs the SRANDMEMBER command to get multiple random members from a set + * + * @param parser - The command parser + * @param key - The key of the set to get random members from + * @param count - The number of members to return. If negative, may return the same member multiple times + * @see https://redis.io/commands/srandmember/ + */ parseCommand(parser: CommandParser, key: RedisArgument, count: number) { SRANDMEMBER.parseCommand(parser, key); parser.push(count.toString()); diff --git a/packages/client/lib/commands/SREM.ts b/packages/client/lib/commands/SREM.ts index 75053474cce..d97ed7774d8 100644 --- a/packages/client/lib/commands/SREM.ts +++ b/packages/client/lib/commands/SREM.ts @@ -4,6 +4,15 @@ import { RedisVariadicArgument } from './generic-transformers'; export default { IS_READ_ONLY: false, + /** + * Constructs the SREM command to remove one or more members from a set + * + * @param parser - The command parser + * @param key - The key of the set to remove members from + * @param members - One or more members to remove from the set + * @returns The number of members that were removed from the set + * @see https://redis.io/commands/srem/ + */ parseCommand(parser: CommandParser, key: RedisArgument, members: RedisVariadicArgument) { parser.push('SREM'); parser.pushKey(key); diff --git a/packages/client/lib/commands/SSCAN.ts b/packages/client/lib/commands/SSCAN.ts index 22634d56242..14e2c079ff0 100644 --- a/packages/client/lib/commands/SSCAN.ts +++ b/packages/client/lib/commands/SSCAN.ts @@ -4,6 +4,16 @@ import { ScanCommonOptions, parseScanArguments} from './SCAN'; export default { IS_READ_ONLY: true, + /** + * Constructs the SSCAN command to incrementally iterate over elements in a set + * + * @param parser - The command parser + * @param key - The key of the set to scan + * @param cursor - The cursor position to start scanning from + * @param options - Optional scanning parameters (COUNT and MATCH) + * @returns Iterator containing cursor position and matching members + * @see https://redis.io/commands/sscan/ + */ parseCommand( parser: CommandParser, key: RedisArgument, @@ -14,6 +24,13 @@ export default { parser.pushKey(key); parseScanArguments(parser, cursor, options); }, + /** + * Transforms the SSCAN reply into a cursor result object + * + * @param cursor - The next cursor position + * @param members - Array of matching set members + * @returns Object containing cursor and members array + */ transformReply([cursor, members]: [BlobStringReply, Array]) { return { cursor, diff --git a/packages/client/lib/commands/STRLEN.ts b/packages/client/lib/commands/STRLEN.ts index 34e0430fc9e..0f0e612422a 100644 --- a/packages/client/lib/commands/STRLEN.ts +++ b/packages/client/lib/commands/STRLEN.ts @@ -4,6 +4,14 @@ import { RedisArgument, NumberReply, Command } from '../RESP/types'; export default { CACHEABLE: true, IS_READ_ONLY: true, + /** + * Constructs the STRLEN command to get the length of a string value + * + * @param parser - The command parser + * @param key - The key holding the string value + * @returns The length of the string value, or 0 when key does not exist + * @see https://redis.io/commands/strlen/ + */ parseCommand(parser: CommandParser, key: RedisArgument) { parser.push('STRLEN'); parser.pushKey(key); diff --git a/packages/client/lib/commands/SUNION.ts b/packages/client/lib/commands/SUNION.ts index 3d9a5954a7c..7acecd1d12a 100644 --- a/packages/client/lib/commands/SUNION.ts +++ b/packages/client/lib/commands/SUNION.ts @@ -5,6 +5,14 @@ import { RedisVariadicArgument } from './generic-transformers'; export default { CACHEABLE: true, IS_READ_ONLY: true, + /** + * Constructs the SUNION command to return the members of the set resulting from the union of all the given sets + * + * @param parser - The command parser + * @param keys - One or more set keys to compute the union from + * @returns Array of all elements that are members of at least one of the given sets + * @see https://redis.io/commands/sunion/ + */ parseCommand(parser: CommandParser, keys: RedisVariadicArgument) { parser.push('SUNION'); parser.pushKeys(keys); diff --git a/packages/client/lib/commands/SUNIONSTORE.ts b/packages/client/lib/commands/SUNIONSTORE.ts index e2f43ecb1c8..0a877c9cb8d 100644 --- a/packages/client/lib/commands/SUNIONSTORE.ts +++ b/packages/client/lib/commands/SUNIONSTORE.ts @@ -4,6 +4,15 @@ import { RedisVariadicArgument } from './generic-transformers'; export default { IS_READ_ONLY: false, + /** + * Constructs the SUNIONSTORE command to store the union of multiple sets into a destination set + * + * @param parser - The command parser + * @param destination - The destination key to store the resulting set + * @param keys - One or more source set keys to compute the union from + * @returns The number of elements in the resulting set + * @see https://redis.io/commands/sunionstore/ + */ parseCommand(parser: CommandParser, destination: RedisArgument, keys: RedisVariadicArgument) { parser.push('SUNIONSTORE'); parser.pushKey(destination); diff --git a/packages/client/lib/commands/SWAPDB.ts b/packages/client/lib/commands/SWAPDB.ts index e59c75715cd..66b19409a2b 100644 --- a/packages/client/lib/commands/SWAPDB.ts +++ b/packages/client/lib/commands/SWAPDB.ts @@ -4,6 +4,12 @@ import { SimpleStringReply, Command } from '../RESP/types'; export default { NOT_KEYED_COMMAND: true, IS_READ_ONLY: false, + /** + * Swaps the data of two Redis databases. + * @param parser - The Redis command parser. + * @param index1 - First database index. + * @param index2 - Second database index. + */ parseCommand(parser: CommandParser, index1: number, index2: number) { parser.push('SWAPDB', index1.toString(), index2.toString()); }, diff --git a/packages/client/lib/commands/TIME.ts b/packages/client/lib/commands/TIME.ts index b25af710e1c..dc248d82069 100644 --- a/packages/client/lib/commands/TIME.ts +++ b/packages/client/lib/commands/TIME.ts @@ -4,6 +4,13 @@ import { BlobStringReply, Command } from '../RESP/types'; export default { NOT_KEYED_COMMAND: true, IS_READ_ONLY: true, + /** + * Constructs the TIME command to return the server's current time + * + * @param parser - The command parser + * @returns Array containing the Unix timestamp in seconds and microseconds + * @see https://redis.io/commands/time/ + */ parseCommand(parser: CommandParser) { parser.push('TIME'); }, diff --git a/packages/client/lib/commands/TOUCH.ts b/packages/client/lib/commands/TOUCH.ts index c765c9f8347..953a696111c 100644 --- a/packages/client/lib/commands/TOUCH.ts +++ b/packages/client/lib/commands/TOUCH.ts @@ -4,6 +4,14 @@ import { RedisVariadicArgument } from './generic-transformers'; export default { IS_READ_ONLY: false, + /** + * Constructs the TOUCH command to alter the last access time of keys + * + * @param parser - The command parser + * @param key - One or more keys to touch + * @returns The number of keys that were touched + * @see https://redis.io/commands/touch/ + */ parseCommand(parser: CommandParser, key: RedisVariadicArgument) { parser.push('TOUCH'); parser.pushKeys(key); diff --git a/packages/client/lib/commands/TTL.ts b/packages/client/lib/commands/TTL.ts index 8420089fcb9..c3340eda32e 100644 --- a/packages/client/lib/commands/TTL.ts +++ b/packages/client/lib/commands/TTL.ts @@ -3,6 +3,14 @@ import { RedisArgument, NumberReply, Command } from '../RESP/types'; export default { IS_READ_ONLY: true, + /** + * Constructs the TTL command to get the remaining time to live of a key + * + * @param parser - The command parser + * @param key - Key to check + * @returns Time to live in seconds, -2 if key does not exist, -1 if has no timeout + * @see https://redis.io/commands/ttl/ + */ parseCommand(parser: CommandParser, key: RedisArgument) { parser.push('TTL'); parser.pushKey(key); diff --git a/packages/client/lib/commands/TYPE.ts b/packages/client/lib/commands/TYPE.ts index ffc592994db..740aa08e94a 100644 --- a/packages/client/lib/commands/TYPE.ts +++ b/packages/client/lib/commands/TYPE.ts @@ -4,6 +4,14 @@ import { RedisArgument, SimpleStringReply, Command } from '../RESP/types'; export default { CACHEABLE: true, IS_READ_ONLY: true, + /** + * Constructs the TYPE command to determine the data type stored at key + * + * @param parser - The command parser + * @param key - Key to check + * @returns String reply: "none", "string", "list", "set", "zset", "hash", "stream" + * @see https://redis.io/commands/type/ + */ parseCommand(parser: CommandParser, key: RedisArgument) { parser.push('TYPE'); parser.pushKey(key); diff --git a/packages/client/lib/commands/UNLINK.ts b/packages/client/lib/commands/UNLINK.ts index 14d1e700277..4aa9cc315ab 100644 --- a/packages/client/lib/commands/UNLINK.ts +++ b/packages/client/lib/commands/UNLINK.ts @@ -4,6 +4,14 @@ import { RedisVariadicArgument } from './generic-transformers'; export default { IS_READ_ONLY: false, + /** + * Constructs the UNLINK command to asynchronously delete one or more keys + * + * @param parser - The command parser + * @param keys - One or more keys to unlink + * @returns The number of keys that were unlinked + * @see https://redis.io/commands/unlink/ + */ parseCommand(parser: CommandParser, keys: RedisVariadicArgument) { parser.push('UNLINK'); parser.pushKeys(keys); diff --git a/packages/client/lib/commands/WAIT.ts b/packages/client/lib/commands/WAIT.ts index df45a12373d..7ccebbc4ec9 100644 --- a/packages/client/lib/commands/WAIT.ts +++ b/packages/client/lib/commands/WAIT.ts @@ -4,6 +4,15 @@ import { NumberReply, Command } from '../RESP/types'; export default { NOT_KEYED_COMMAND: true, IS_READ_ONLY: true, + /** + * Constructs the WAIT command to synchronize with replicas + * + * @param parser - The command parser + * @param numberOfReplicas - Number of replicas that must acknowledge the write + * @param timeout - Maximum time to wait in milliseconds + * @returns The number of replicas that acknowledged the write + * @see https://redis.io/commands/wait/ + */ parseCommand(parser: CommandParser, numberOfReplicas: number, timeout: number) { parser.push('WAIT', numberOfReplicas.toString(), timeout.toString()); }, diff --git a/packages/client/lib/commands/XACK.ts b/packages/client/lib/commands/XACK.ts index 2500134f1c8..26e1c962baa 100644 --- a/packages/client/lib/commands/XACK.ts +++ b/packages/client/lib/commands/XACK.ts @@ -4,6 +4,16 @@ import { RedisVariadicArgument } from './generic-transformers'; export default { IS_READ_ONLY: false, + /** + * Constructs the XACK command to acknowledge the processing of stream messages in a consumer group + * + * @param parser - The command parser + * @param key - The stream key + * @param group - The consumer group name + * @param id - One or more message IDs to acknowledge + * @returns The number of messages successfully acknowledged + * @see https://redis.io/commands/xack/ + */ parseCommand(parser: CommandParser, key: RedisArgument, group: RedisArgument, id: RedisVariadicArgument) { parser.push('XACK'); parser.pushKey(key); diff --git a/packages/client/lib/commands/XADD.ts b/packages/client/lib/commands/XADD.ts index cb9d0f5fad8..b0c50b1bfdb 100644 --- a/packages/client/lib/commands/XADD.ts +++ b/packages/client/lib/commands/XADD.ts @@ -2,6 +2,15 @@ import { CommandParser } from '../client/parser'; import { RedisArgument, BlobStringReply, Command } from '../RESP/types'; import { Tail } from './generic-transformers'; +/** + * Options for the XADD command + * + * @property TRIM - Optional trimming configuration + * @property TRIM.strategy - Trim strategy: MAXLEN (by length) or MINID (by ID) + * @property TRIM.strategyModifier - Exact ('=') or approximate ('~') trimming + * @property TRIM.threshold - Maximum stream length or minimum ID to retain + * @property TRIM.limit - Maximum number of entries to trim in one call + */ export interface XAddOptions { TRIM?: { strategy?: 'MAXLEN' | 'MINID'; @@ -11,6 +20,16 @@ export interface XAddOptions { }; } +/** + * Parses arguments for the XADD command + * + * @param optional - Optional command modifier + * @param parser - The command parser + * @param key - The stream key + * @param id - Message ID (* for auto-generation) + * @param message - Key-value pairs representing the message fields + * @param options - Additional options for stream trimming + */ export function parseXAddArguments( optional: RedisArgument | undefined, parser: CommandParser, @@ -50,6 +69,17 @@ export function parseXAddArguments( export default { IS_READ_ONLY: false, + /** + * Constructs the XADD command to append a new entry to a stream + * + * @param parser - The command parser + * @param key - The stream key + * @param id - Message ID (* for auto-generation) + * @param message - Key-value pairs representing the message fields + * @param options - Additional options for stream trimming + * @returns The ID of the added entry + * @see https://redis.io/commands/xadd/ + */ parseCommand(...args: Tail>) { return parseXAddArguments(undefined, ...args); }, diff --git a/packages/client/lib/commands/XADD_NOMKSTREAM.ts b/packages/client/lib/commands/XADD_NOMKSTREAM.ts index 9d33374be4a..8b1861a065b 100644 --- a/packages/client/lib/commands/XADD_NOMKSTREAM.ts +++ b/packages/client/lib/commands/XADD_NOMKSTREAM.ts @@ -2,8 +2,18 @@ import { BlobStringReply, NullReply, Command } from '../RESP/types'; import { Tail } from './generic-transformers'; import { parseXAddArguments } from './XADD'; +/** + * Command for adding entries to an existing stream without creating it if it doesn't exist + */ export default { IS_READ_ONLY: false, + /** + * Constructs the XADD command with NOMKSTREAM option to append a new entry to an existing stream + * + * @param args - Arguments tuple containing parser, key, id, message, and options + * @returns The ID of the added entry, or null if the stream doesn't exist + * @see https://redis.io/commands/xadd/ + */ parseCommand(...args: Tail>) { return parseXAddArguments('NOMKSTREAM', ...args); }, diff --git a/packages/client/lib/commands/XAUTOCLAIM.ts b/packages/client/lib/commands/XAUTOCLAIM.ts index 19b4f63a2df..bd6f7b05346 100644 --- a/packages/client/lib/commands/XAUTOCLAIM.ts +++ b/packages/client/lib/commands/XAUTOCLAIM.ts @@ -2,10 +2,22 @@ import { CommandParser } from '../client/parser'; import { RedisArgument, TuplesReply, BlobStringReply, ArrayReply, NullReply, UnwrapReply, Command, TypeMapping } from '../RESP/types'; import { StreamMessageRawReply, transformStreamMessageNullReply } from './generic-transformers'; +/** + * Options for the XAUTOCLAIM command + * + * @property COUNT - Limit the number of messages to claim + */ export interface XAutoClaimOptions { COUNT?: number; } +/** + * Raw reply structure for XAUTOCLAIM command + * + * @property nextId - The ID to use for the next XAUTOCLAIM call + * @property messages - Array of claimed messages or null entries + * @property deletedMessages - Array of message IDs that no longer exist + */ export type XAutoClaimRawReply = TuplesReply<[ nextId: BlobStringReply, messages: ArrayReply, @@ -14,6 +26,19 @@ export type XAutoClaimRawReply = TuplesReply<[ export default { IS_READ_ONLY: false, + /** + * Constructs the XAUTOCLAIM command to automatically claim pending messages in a consumer group + * + * @param parser - The command parser + * @param key - The stream key + * @param group - The consumer group name + * @param consumer - The consumer name that will claim the messages + * @param minIdleTime - Minimum idle time in milliseconds for a message to be claimed + * @param start - Message ID to start scanning from + * @param options - Additional options for the claim operation + * @returns Object containing nextId, claimed messages, and list of deleted message IDs + * @see https://redis.io/commands/xautoclaim/ + */ parseCommand( parser: CommandParser, key: RedisArgument, @@ -31,6 +56,14 @@ export default { parser.push('COUNT', options.COUNT.toString()); } }, + /** + * Transforms the raw XAUTOCLAIM reply into a structured object + * + * @param reply - Raw reply from Redis + * @param preserve - Preserve options (unused) + * @param typeMapping - Type mapping for message fields + * @returns Structured object containing nextId, messages, and deletedMessages + */ transformReply(reply: UnwrapReply, preserve?: any, typeMapping?: TypeMapping) { return { nextId: reply[0], diff --git a/packages/client/lib/commands/XAUTOCLAIM_JUSTID.ts b/packages/client/lib/commands/XAUTOCLAIM_JUSTID.ts index c0ebe83748e..efa299c6f8f 100644 --- a/packages/client/lib/commands/XAUTOCLAIM_JUSTID.ts +++ b/packages/client/lib/commands/XAUTOCLAIM_JUSTID.ts @@ -1,6 +1,13 @@ import { TuplesReply, BlobStringReply, ArrayReply, UnwrapReply, Command } from '../RESP/types'; import XAUTOCLAIM from './XAUTOCLAIM'; +/** + * Raw reply structure for XAUTOCLAIM JUSTID command + * + * @property nextId - The ID to use for the next XAUTOCLAIM call + * @property messages - Array of message IDs that were claimed + * @property deletedMessages - Array of message IDs that no longer exist + */ type XAutoClaimJustIdRawReply = TuplesReply<[ nextId: BlobStringReply, messages: ArrayReply, @@ -9,11 +16,24 @@ type XAutoClaimJustIdRawReply = TuplesReply<[ export default { IS_READ_ONLY: XAUTOCLAIM.IS_READ_ONLY, + /** + * Constructs the XAUTOCLAIM command with JUSTID option to get only message IDs + * + * @param args - Same parameters as XAUTOCLAIM command + * @returns Object containing nextId and arrays of claimed and deleted message IDs + * @see https://redis.io/commands/xautoclaim/ + */ parseCommand(...args: Parameters) { const parser = args[0]; XAUTOCLAIM.parseCommand(...args); parser.push('JUSTID'); }, + /** + * Transforms the raw XAUTOCLAIM JUSTID reply into a structured object + * + * @param reply - Raw reply from Redis + * @returns Structured object containing nextId, message IDs, and deleted message IDs + */ transformReply(reply: UnwrapReply) { return { nextId: reply[0], diff --git a/packages/client/lib/commands/XCLAIM.ts b/packages/client/lib/commands/XCLAIM.ts index 598b1b17ba4..2bc771288ac 100644 --- a/packages/client/lib/commands/XCLAIM.ts +++ b/packages/client/lib/commands/XCLAIM.ts @@ -2,6 +2,15 @@ import { CommandParser } from '../client/parser'; import { RedisArgument, ArrayReply, NullReply, UnwrapReply, Command, TypeMapping } from '../RESP/types'; import { RedisVariadicArgument, StreamMessageRawReply, transformStreamMessageNullReply } from './generic-transformers'; +/** + * Options for the XCLAIM command + * + * @property IDLE - Set the idle time (in milliseconds) for the claimed messages + * @property TIME - Set the last delivery time (Unix timestamp or Date) + * @property RETRYCOUNT - Set the retry counter for the claimed messages + * @property FORCE - Create the pending message entry even if the message doesn't exist + * @property LASTID - Update the consumer group last ID + */ export interface XClaimOptions { IDLE?: number; TIME?: number | Date; @@ -12,6 +21,19 @@ export interface XClaimOptions { export default { IS_READ_ONLY: false, + /** + * Constructs the XCLAIM command to claim pending messages in a consumer group + * + * @param parser - The command parser + * @param key - The stream key + * @param group - The consumer group name + * @param consumer - The consumer name that will claim the messages + * @param minIdleTime - Minimum idle time in milliseconds for a message to be claimed + * @param id - One or more message IDs to claim + * @param options - Additional options for the claim operation + * @returns Array of claimed messages + * @see https://redis.io/commands/xclaim/ + */ parseCommand( parser: CommandParser, key: RedisArgument, @@ -49,6 +71,14 @@ export default { parser.push('LASTID', options.LASTID); } }, + /** + * Transforms the raw XCLAIM reply into an array of messages + * + * @param reply - Raw reply from Redis + * @param preserve - Preserve options (unused) + * @param typeMapping - Type mapping for message fields + * @returns Array of claimed messages with their fields + */ transformReply( reply: UnwrapReply>, preserve?: any, diff --git a/packages/client/lib/commands/XCLAIM_JUSTID.ts b/packages/client/lib/commands/XCLAIM_JUSTID.ts index 91be5aafbb4..56e1d576158 100644 --- a/packages/client/lib/commands/XCLAIM_JUSTID.ts +++ b/packages/client/lib/commands/XCLAIM_JUSTID.ts @@ -1,12 +1,27 @@ import { ArrayReply, BlobStringReply, Command } from '../RESP/types'; import XCLAIM from './XCLAIM'; +/** + * Command variant for XCLAIM that returns only message IDs + */ export default { IS_READ_ONLY: XCLAIM.IS_READ_ONLY, + /** + * Constructs the XCLAIM command with JUSTID option to get only message IDs + * + * @param args - Same parameters as XCLAIM command + * @returns Array of successfully claimed message IDs + * @see https://redis.io/commands/xclaim/ + */ parseCommand(...args: Parameters) { const parser = args[0]; XCLAIM.parseCommand(...args); parser.push('JUSTID'); }, + /** + * Transforms the XCLAIM JUSTID reply into an array of message IDs + * + * @returns Array of claimed message IDs + */ transformReply: undefined as unknown as () => ArrayReply } as const satisfies Command; diff --git a/packages/client/lib/commands/XDEL.ts b/packages/client/lib/commands/XDEL.ts index ee385203ce5..db8df7d4fd2 100644 --- a/packages/client/lib/commands/XDEL.ts +++ b/packages/client/lib/commands/XDEL.ts @@ -2,8 +2,20 @@ import { CommandParser } from '../client/parser'; import { RedisArgument, NumberReply, Command } from '../RESP/types'; import { RedisVariadicArgument } from './generic-transformers'; +/** + * Command for removing messages from a stream + */ export default { IS_READ_ONLY: false, + /** + * Constructs the XDEL command to remove one or more messages from a stream + * + * @param parser - The command parser + * @param key - The stream key + * @param id - One or more message IDs to delete + * @returns The number of messages actually deleted + * @see https://redis.io/commands/xdel/ + */ parseCommand(parser: CommandParser, key: RedisArgument, id: RedisVariadicArgument) { parser.push('XDEL'); parser.pushKey(key); diff --git a/packages/client/lib/commands/XGROUP_CREATE.ts b/packages/client/lib/commands/XGROUP_CREATE.ts index e91186efe29..db6df04fa0f 100644 --- a/packages/client/lib/commands/XGROUP_CREATE.ts +++ b/packages/client/lib/commands/XGROUP_CREATE.ts @@ -1,6 +1,12 @@ import { CommandParser } from '../client/parser'; import { RedisArgument, SimpleStringReply, Command } from '../RESP/types'; +/** + * Options for creating a consumer group + * + * @property MKSTREAM - Create the stream if it doesn't exist + * @property ENTRIESREAD - Set the number of entries that were read in this consumer group (Redis 7.0+) + */ export interface XGroupCreateOptions { MKSTREAM?: boolean; /** @@ -11,6 +17,17 @@ export interface XGroupCreateOptions { export default { IS_READ_ONLY: false, + /** + * Constructs the XGROUP CREATE command to create a consumer group for a stream + * + * @param parser - The command parser + * @param key - The stream key + * @param group - Name of the consumer group + * @param id - ID of the last delivered item in the stream ('$' for last item, '0' for all items) + * @param options - Additional options for group creation + * @returns 'OK' if successful + * @see https://redis.io/commands/xgroup-create/ + */ parseCommand( parser: CommandParser, key: RedisArgument, diff --git a/packages/client/lib/commands/XGROUP_CREATECONSUMER.ts b/packages/client/lib/commands/XGROUP_CREATECONSUMER.ts index 906bc4c683e..0b730c7f96b 100644 --- a/packages/client/lib/commands/XGROUP_CREATECONSUMER.ts +++ b/packages/client/lib/commands/XGROUP_CREATECONSUMER.ts @@ -1,8 +1,21 @@ import { CommandParser } from '../client/parser'; import { RedisArgument, Command, NumberReply } from '../RESP/types'; +/** + * Command for creating a new consumer in a consumer group + */ export default { IS_READ_ONLY: false, + /** + * Constructs the XGROUP CREATECONSUMER command to create a new consumer in a consumer group + * + * @param parser - The command parser + * @param key - The stream key + * @param group - Name of the consumer group + * @param consumer - Name of the consumer to create + * @returns 1 if the consumer was created, 0 if it already existed + * @see https://redis.io/commands/xgroup-createconsumer/ + */ parseCommand( parser: CommandParser, key: RedisArgument, diff --git a/packages/client/lib/commands/XGROUP_DELCONSUMER.ts b/packages/client/lib/commands/XGROUP_DELCONSUMER.ts index 360d7e06cae..5feffe74042 100644 --- a/packages/client/lib/commands/XGROUP_DELCONSUMER.ts +++ b/packages/client/lib/commands/XGROUP_DELCONSUMER.ts @@ -1,8 +1,21 @@ import { CommandParser } from '../client/parser'; import { RedisArgument, NumberReply, Command } from '../RESP/types'; +/** + * Command for removing a consumer from a consumer group + */ export default { IS_READ_ONLY: false, + /** + * Constructs the XGROUP DELCONSUMER command to remove a consumer from a consumer group + * + * @param parser - The command parser + * @param key - The stream key + * @param group - Name of the consumer group + * @param consumer - Name of the consumer to remove + * @returns The number of pending messages owned by the deleted consumer + * @see https://redis.io/commands/xgroup-delconsumer/ + */ parseCommand( parser: CommandParser, key: RedisArgument, diff --git a/packages/client/lib/commands/XGROUP_DESTROY.ts b/packages/client/lib/commands/XGROUP_DESTROY.ts index 9112f1bcd79..ed454abbb2b 100644 --- a/packages/client/lib/commands/XGROUP_DESTROY.ts +++ b/packages/client/lib/commands/XGROUP_DESTROY.ts @@ -1,8 +1,20 @@ import { CommandParser } from '../client/parser'; import { RedisArgument, NumberReply, Command } from '../RESP/types'; +/** + * Command for removing a consumer group + */ export default { IS_READ_ONLY: false, + /** + * Constructs the XGROUP DESTROY command to remove a consumer group + * + * @param parser - The command parser + * @param key - The stream key + * @param group - Name of the consumer group to destroy + * @returns 1 if the group was destroyed, 0 if it did not exist + * @see https://redis.io/commands/xgroup-destroy/ + */ parseCommand(parser: CommandParser, key: RedisArgument, group: RedisArgument) { parser.push('XGROUP', 'DESTROY'); parser.pushKey(key); diff --git a/packages/client/lib/commands/XGROUP_SETID.ts b/packages/client/lib/commands/XGROUP_SETID.ts index 5b0ddcc32ad..4f3076b6032 100644 --- a/packages/client/lib/commands/XGROUP_SETID.ts +++ b/packages/client/lib/commands/XGROUP_SETID.ts @@ -1,6 +1,11 @@ import { CommandParser } from '../client/parser'; import { RedisArgument, SimpleStringReply, Command } from '../RESP/types'; +/** + * Options for setting a consumer group's ID position + * + * @property ENTRIESREAD - Set the number of entries that were read in this consumer group (Redis 7.0+) + */ export interface XGroupSetIdOptions { /** added in 7.0 */ ENTRIESREAD?: number; @@ -8,6 +13,17 @@ export interface XGroupSetIdOptions { export default { IS_READ_ONLY: false, + /** + * Constructs the XGROUP SETID command to set the last delivered ID for a consumer group + * + * @param parser - The command parser + * @param key - The stream key + * @param group - Name of the consumer group + * @param id - ID to set as last delivered message ('$' for last item, '0' for all items) + * @param options - Additional options for setting the group ID + * @returns 'OK' if successful + * @see https://redis.io/commands/xgroup-setid/ + */ parseCommand( parser: CommandParser, key: RedisArgument, diff --git a/packages/client/lib/commands/XINFO_CONSUMERS.ts b/packages/client/lib/commands/XINFO_CONSUMERS.ts index 310a40d17f3..49267f13980 100644 --- a/packages/client/lib/commands/XINFO_CONSUMERS.ts +++ b/packages/client/lib/commands/XINFO_CONSUMERS.ts @@ -1,6 +1,14 @@ import { CommandParser } from '../client/parser'; import { RedisArgument, ArrayReply, TuplesToMapReply, BlobStringReply, NumberReply, UnwrapReply, Resp2Reply, Command } from '../RESP/types'; +/** + * Reply structure for XINFO CONSUMERS command + * + * @property name - Name of the consumer + * @property pending - Number of pending messages for this consumer + * @property idle - Idle time in milliseconds + * @property inactive - Time in milliseconds since last interaction (Redis 7.2+) + */ export type XInfoConsumersReply = ArrayReply, BlobStringReply], [BlobStringReply<'pending'>, NumberReply], @@ -11,12 +19,27 @@ export type XInfoConsumersReply = ArrayReply>) => { return reply.map(consumer => { const unwrapped = consumer as unknown as UnwrapReply; diff --git a/packages/client/lib/commands/XINFO_GROUPS.ts b/packages/client/lib/commands/XINFO_GROUPS.ts index e7f8874125a..1d8142bfaef 100644 --- a/packages/client/lib/commands/XINFO_GROUPS.ts +++ b/packages/client/lib/commands/XINFO_GROUPS.ts @@ -1,6 +1,9 @@ import { CommandParser } from '../client/parser'; import { RedisArgument, ArrayReply, TuplesToMapReply, BlobStringReply, NumberReply, NullReply, UnwrapReply, Resp2Reply, Command } from '../RESP/types'; +/** + * Reply structure for XINFO GROUPS command containing information about consumer groups + */ export type XInfoGroupsReply = ArrayReply, BlobStringReply], [BlobStringReply<'consumers'>, NumberReply], @@ -14,11 +17,31 @@ export type XInfoGroupsReply = ArrayReply>) => { return reply.map(group => { const unwrapped = group as unknown as UnwrapReply; diff --git a/packages/client/lib/commands/XINFO_STREAM.ts b/packages/client/lib/commands/XINFO_STREAM.ts index bb102c591bb..546dd70cab7 100644 --- a/packages/client/lib/commands/XINFO_STREAM.ts +++ b/packages/client/lib/commands/XINFO_STREAM.ts @@ -2,6 +2,20 @@ import { CommandParser } from '../client/parser'; import { RedisArgument, TuplesToMapReply, BlobStringReply, NumberReply, NullReply, TuplesReply, ArrayReply, UnwrapReply, Command } from '../RESP/types'; import { isNullReply, transformTuplesReply } from './generic-transformers'; +/** + * Reply structure for XINFO STREAM command containing detailed information about a stream + * + * @property length - Number of entries in the stream + * @property radix-tree-keys - Number of radix tree keys + * @property radix-tree-nodes - Number of radix tree nodes + * @property last-generated-id - Last generated message ID + * @property max-deleted-entry-id - Highest message ID deleted (Redis 7.2+) + * @property entries-added - Total number of entries added (Redis 7.2+) + * @property recorded-first-entry-id - ID of the first recorded entry (Redis 7.2+) + * @property groups - Number of consumer groups + * @property first-entry - First entry in the stream + * @property last-entry - Last entry in the stream + */ export type XInfoStreamReply = TuplesToMapReply<[ [BlobStringReply<'length'>, NumberReply], [BlobStringReply<'radix-tree-keys'>, NumberReply], @@ -20,6 +34,14 @@ export type XInfoStreamReply = TuplesToMapReply<[ export default { IS_READ_ONLY: true, + /** + * Constructs the XINFO STREAM command to get detailed information about a stream + * + * @param parser - The command parser + * @param key - The stream key + * @returns Detailed information about the stream including its length, structure, and entries + * @see https://redis.io/commands/xinfo-stream/ + */ parseCommand(parser: CommandParser, key: RedisArgument) { parser.push('XINFO', 'STREAM'); parser.pushKey(key); @@ -67,11 +89,20 @@ export default { } } as const satisfies Command; +/** + * Raw entry structure from Redis stream + */ type RawEntry = TuplesReply<[ id: BlobStringReply, message: ArrayReply ]> | NullReply; +/** + * Transforms a raw stream entry into a structured object + * + * @param entry - Raw entry from Redis + * @returns Structured object with id and message, or null if entry is null + */ function transformEntry(entry: RawEntry) { if (isNullReply(entry)) return entry; diff --git a/packages/client/lib/commands/XLEN.ts b/packages/client/lib/commands/XLEN.ts index 39d47187b28..f7718371cf2 100644 --- a/packages/client/lib/commands/XLEN.ts +++ b/packages/client/lib/commands/XLEN.ts @@ -1,9 +1,20 @@ import { CommandParser } from '../client/parser'; import { RedisArgument, NumberReply, Command } from '../RESP/types'; +/** + * Command for getting the length of a stream + */ export default { CACHEABLE: true, IS_READ_ONLY: true, + /** + * Constructs the XLEN command to get the number of entries in a stream + * + * @param parser - The command parser + * @param key - The stream key + * @returns The number of entries inside the stream + * @see https://redis.io/commands/xlen/ + */ parseCommand(parser: CommandParser, key: RedisArgument) { parser.push('XLEN'); parser.pushKey(key); diff --git a/packages/client/lib/commands/XPENDING.ts b/packages/client/lib/commands/XPENDING.ts index 11c944c61e7..cff9ef2f51b 100644 --- a/packages/client/lib/commands/XPENDING.ts +++ b/packages/client/lib/commands/XPENDING.ts @@ -1,6 +1,14 @@ import { CommandParser } from '../client/parser'; import { RedisArgument, BlobStringReply, NullReply, ArrayReply, TuplesReply, NumberReply, UnwrapReply, Command } from '../RESP/types'; +/** + * Raw reply structure for XPENDING command + * + * @property pending - Number of pending messages in the group + * @property firstId - ID of the first pending message + * @property lastId - ID of the last pending message + * @property consumers - Array of consumer info with delivery counts + */ type XPendingRawReply = TuplesReply<[ pending: NumberReply, firstId: BlobStringReply | NullReply, @@ -14,11 +22,26 @@ type XPendingRawReply = TuplesReply<[ export default { CACHEABLE: true, IS_READ_ONLY: true, + /** + * Constructs the XPENDING command to inspect pending messages of a consumer group + * + * @param parser - The command parser + * @param key - The stream key + * @param group - Name of the consumer group + * @returns Summary of pending messages including total count, ID range, and per-consumer stats + * @see https://redis.io/commands/xpending/ + */ parseCommand(parser: CommandParser, key: RedisArgument, group: RedisArgument) { parser.push('XPENDING'); parser.pushKey(key); parser.push(group); }, + /** + * Transforms the raw XPENDING reply into a structured object + * + * @param reply - Raw reply from Redis + * @returns Object containing pending count, ID range, and consumer statistics + */ transformReply(reply: UnwrapReply) { const consumers = reply[3] as unknown as UnwrapReply; return { diff --git a/packages/client/lib/commands/XPENDING_RANGE.ts b/packages/client/lib/commands/XPENDING_RANGE.ts index 8d98ffe7f1e..e136061fe9e 100644 --- a/packages/client/lib/commands/XPENDING_RANGE.ts +++ b/packages/client/lib/commands/XPENDING_RANGE.ts @@ -1,11 +1,25 @@ import { CommandParser } from '../client/parser'; import { RedisArgument, ArrayReply, TuplesReply, BlobStringReply, NumberReply, UnwrapReply, Command } from '../RESP/types'; +/** + * Options for the XPENDING RANGE command + * + * @property IDLE - Filter by message idle time in milliseconds + * @property consumer - Filter by specific consumer name + */ export interface XPendingRangeOptions { IDLE?: number; consumer?: RedisArgument; } +/** + * Raw reply structure for XPENDING RANGE command + * + * @property id - Message ID + * @property consumer - Name of the consumer that holds the message + * @property millisecondsSinceLastDelivery - Time since last delivery attempt + * @property deliveriesCounter - Number of times this message was delivered + */ type XPendingRangeRawReply = ArrayReply) { return reply.map(pending => { const unwrapped = pending as unknown as UnwrapReply; diff --git a/packages/client/lib/commands/XRANGE.ts b/packages/client/lib/commands/XRANGE.ts index de6bb6c9b1b..4b83a66e5e6 100644 --- a/packages/client/lib/commands/XRANGE.ts +++ b/packages/client/lib/commands/XRANGE.ts @@ -2,10 +2,23 @@ import { CommandParser } from '../client/parser'; import { RedisArgument, ArrayReply, UnwrapReply, Command, TypeMapping } from '../RESP/types'; import { StreamMessageRawReply, transformStreamMessageReply } from './generic-transformers'; +/** + * Options for the XRANGE command + * + * @property COUNT - Limit the number of entries returned + */ export interface XRangeOptions { COUNT?: number; } +/** + * Helper function to build XRANGE command arguments + * + * @param start - Start of ID range (use '-' for minimum ID) + * @param end - End of ID range (use '+' for maximum ID) + * @param options - Additional options for the range query + * @returns Array of arguments for the XRANGE command + */ export function xRangeArguments( start: RedisArgument, end: RedisArgument, @@ -23,11 +36,28 @@ export function xRangeArguments( export default { CACHEABLE: true, IS_READ_ONLY: true, + /** + * Constructs the XRANGE command to read stream entries in a specific range + * + * @param parser - The command parser + * @param key - The stream key + * @param args - Arguments tuple containing start ID, end ID, and options + * @returns Array of messages in the specified range + * @see https://redis.io/commands/xrange/ + */ parseCommand(parser: CommandParser, key: RedisArgument, ...args: Parameters) { parser.push('XRANGE'); parser.pushKey(key); parser.pushVariadic(xRangeArguments(args[0], args[1], args[2])); }, + /** + * Transforms the raw XRANGE reply into structured message objects + * + * @param reply - Raw reply from Redis + * @param preserve - Preserve options (unused) + * @param typeMapping - Type mapping for message fields + * @returns Array of structured message objects + */ transformReply( reply: UnwrapReply>, preserve?: any, diff --git a/packages/client/lib/commands/XREAD.ts b/packages/client/lib/commands/XREAD.ts index b57fb8f3983..110443ad3a5 100644 --- a/packages/client/lib/commands/XREAD.ts +++ b/packages/client/lib/commands/XREAD.ts @@ -2,6 +2,12 @@ import { CommandParser } from '../client/parser'; import { Command, RedisArgument, ReplyUnion } from '../RESP/types'; import { transformStreamsMessagesReplyResp2 } from './generic-transformers'; +/** + * Structure representing a stream to read from + * + * @property key - The stream key + * @property id - The message ID to start reading from + */ export interface XReadStream { key: RedisArgument; id: RedisArgument; @@ -9,6 +15,12 @@ export interface XReadStream { export type XReadStreams = Array | XReadStream; +/** + * Helper function to push stream keys and IDs to the command parser + * + * @param parser - The command parser + * @param streams - Single stream or array of streams to read from + */ export function pushXReadStreams(parser: CommandParser, streams: XReadStreams) { parser.push('STREAMS'); @@ -25,6 +37,12 @@ export function pushXReadStreams(parser: CommandParser, streams: XReadStreams) { } } +/** + * Options for the XREAD command + * + * @property COUNT - Limit the number of entries returned per stream + * @property BLOCK - Milliseconds to block waiting for new entries (0 for indefinite) + */ export interface XReadOptions { COUNT?: number; BLOCK?: number; @@ -32,6 +50,15 @@ export interface XReadOptions { export default { IS_READ_ONLY: true, + /** + * Constructs the XREAD command to read messages from one or more streams + * + * @param parser - The command parser + * @param streams - Single stream or array of streams to read from + * @param options - Additional options for reading streams + * @returns Array of stream entries, each containing the stream name and its messages + * @see https://redis.io/commands/xread/ + */ parseCommand(parser: CommandParser, streams: XReadStreams, options?: XReadOptions) { parser.push('XREAD'); @@ -45,6 +72,9 @@ export default { pushXReadStreams(parser, streams); }, + /** + * Transform functions for different RESP versions + */ transformReply: { 2: transformStreamsMessagesReplyResp2, 3: undefined as unknown as () => ReplyUnion diff --git a/packages/client/lib/commands/XREADGROUP.ts b/packages/client/lib/commands/XREADGROUP.ts index d0947e19a08..b274aab95fe 100644 --- a/packages/client/lib/commands/XREADGROUP.ts +++ b/packages/client/lib/commands/XREADGROUP.ts @@ -3,6 +3,13 @@ import { Command, RedisArgument, ReplyUnion } from '../RESP/types'; import { XReadStreams, pushXReadStreams } from './XREAD'; import { transformStreamsMessagesReplyResp2 } from './generic-transformers'; +/** + * Options for the XREADGROUP command + * + * @property COUNT - Limit the number of entries returned per stream + * @property BLOCK - Milliseconds to block waiting for new entries (0 for indefinite) + * @property NOACK - Skip adding the message to the PEL (Pending Entries List) + */ export interface XReadGroupOptions { COUNT?: number; BLOCK?: number; @@ -11,6 +18,17 @@ export interface XReadGroupOptions { export default { IS_READ_ONLY: true, + /** + * Constructs the XREADGROUP command to read messages from streams as a consumer group member + * + * @param parser - The command parser + * @param group - Name of the consumer group + * @param consumer - Name of the consumer in the group + * @param streams - Single stream or array of streams to read from + * @param options - Additional options for reading streams + * @returns Array of stream entries, each containing the stream name and its messages + * @see https://redis.io/commands/xreadgroup/ + */ parseCommand( parser: CommandParser, group: RedisArgument, @@ -34,6 +52,9 @@ export default { pushXReadStreams(parser, streams); }, + /** + * Transform functions for different RESP versions + */ transformReply: { 2: transformStreamsMessagesReplyResp2, 3: undefined as unknown as () => ReplyUnion diff --git a/packages/client/lib/commands/XREVRANGE.ts b/packages/client/lib/commands/XREVRANGE.ts index ddc51082a1e..452c2ab3807 100644 --- a/packages/client/lib/commands/XREVRANGE.ts +++ b/packages/client/lib/commands/XREVRANGE.ts @@ -2,13 +2,30 @@ import { CommandParser } from '../client/parser'; import { Command, RedisArgument } from '../RESP/types'; import XRANGE, { xRangeArguments } from './XRANGE'; +/** + * Options for the XREVRANGE command + * + * @property COUNT - Limit the number of entries returned + */ export interface XRevRangeOptions { COUNT?: number; } +/** + * Command for reading stream entries in reverse order + */ export default { CACHEABLE: XRANGE.CACHEABLE, IS_READ_ONLY: XRANGE.IS_READ_ONLY, + /** + * Constructs the XREVRANGE command to read stream entries in reverse order + * + * @param parser - The command parser + * @param key - The stream key + * @param args - Arguments tuple containing start ID, end ID, and options + * @returns Array of messages in the specified range in reverse order + * @see https://redis.io/commands/xrevrange/ + */ parseCommand(parser: CommandParser, key: RedisArgument, ...args: Parameters) { parser.push('XREVRANGE'); parser.pushKey(key); diff --git a/packages/client/lib/commands/XTRIM.ts b/packages/client/lib/commands/XTRIM.ts index fb617d8d35a..6125720111a 100644 --- a/packages/client/lib/commands/XTRIM.ts +++ b/packages/client/lib/commands/XTRIM.ts @@ -1,14 +1,34 @@ import { CommandParser } from '../client/parser'; import { NumberReply, Command, RedisArgument } from '../RESP/types'; +/** + * Options for the XTRIM command + * + * @property strategyModifier - Exact ('=') or approximate ('~') trimming + * @property LIMIT - Maximum number of entries to trim in one call (Redis 6.2+) + */ export interface XTrimOptions { strategyModifier?: '=' | '~'; /** added in 6.2 */ LIMIT?: number; } +/** + * Command for trimming a stream to a specified length or minimum ID + */ export default { IS_READ_ONLY: false, + /** + * Constructs the XTRIM command to trim a stream by length or minimum ID + * + * @param parser - The command parser + * @param key - The stream key + * @param strategy - Trim by maximum length (MAXLEN) or minimum ID (MINID) + * @param threshold - Maximum length or minimum ID threshold + * @param options - Additional options for trimming + * @returns Number of entries removed from the stream + * @see https://redis.io/commands/xtrim/ + */ parseCommand( parser: CommandParser, key: RedisArgument, diff --git a/packages/client/lib/commands/ZADD.ts b/packages/client/lib/commands/ZADD.ts index 5ae71a151ba..d53835d44d1 100644 --- a/packages/client/lib/commands/ZADD.ts +++ b/packages/client/lib/commands/ZADD.ts @@ -2,6 +2,9 @@ import { CommandParser } from '../client/parser'; import { RedisArgument, Command } from '../RESP/types'; import { SortedSetMember, transformDoubleArgument, transformDoubleReply } from './generic-transformers'; +/** + * Options for the ZADD command + */ export interface ZAddOptions { condition?: 'NX' | 'XX'; /** @@ -24,7 +27,20 @@ export interface ZAddOptions { CH?: boolean; } +/** + * Command for adding members to a sorted set + */ export default { + /** + * Constructs the ZADD command to add one or more members to a sorted set + * + * @param parser - The command parser + * @param key - The sorted set key + * @param members - One or more members to add with their scores + * @param options - Additional options for adding members + * @returns Number of new members added (or changed members if CH is set) + * @see https://redis.io/commands/zadd/ + */ parseCommand( parser: CommandParser, key: RedisArgument, @@ -59,6 +75,12 @@ export default { transformReply: transformDoubleReply } as const satisfies Command; +/** + * Helper function to push sorted set members to the command + * + * @param parser - The command parser + * @param members - One or more members with their scores + */ export function pushMembers( parser: CommandParser, members: SortedSetMember | Array) { @@ -71,6 +93,12 @@ export function pushMembers( } } +/** + * Helper function to push a single sorted set member to the command + * + * @param parser - The command parser + * @param member - Member with its score + */ function pushMember( parser: CommandParser, member: SortedSetMember diff --git a/packages/client/lib/commands/ZADD_INCR.ts b/packages/client/lib/commands/ZADD_INCR.ts index f37554b1681..73e40efe5c8 100644 --- a/packages/client/lib/commands/ZADD_INCR.ts +++ b/packages/client/lib/commands/ZADD_INCR.ts @@ -3,13 +3,33 @@ import { RedisArgument, Command } from '../RESP/types'; import { pushMembers } from './ZADD'; import { SortedSetMember, transformNullableDoubleReply } from './generic-transformers'; +/** + * Options for the ZADD INCR command + * + * @property condition - Add condition: NX (only if not exists) or XX (only if exists) + * @property comparison - Score comparison: LT (less than) or GT (greater than) + * @property CH - Return the number of changed elements instead of added elements + */ export interface ZAddOptions { condition?: 'NX' | 'XX'; comparison?: 'LT' | 'GT'; CH?: boolean; } +/** + * Command for incrementing the score of a member in a sorted set + */ export default { + /** + * Constructs the ZADD command with INCR option to increment the score of a member + * + * @param parser - The command parser + * @param key - The sorted set key + * @param members - Member(s) whose score to increment + * @param options - Additional options for the increment operation + * @returns The new score of the member after increment (null if member does not exist with XX option) + * @see https://redis.io/commands/zadd/ + */ parseCommand( parser: CommandParser, key: RedisArgument, diff --git a/packages/client/lib/commands/ZCARD.ts b/packages/client/lib/commands/ZCARD.ts index 57b9e7f1d47..d2e0f8df5e2 100644 --- a/packages/client/lib/commands/ZCARD.ts +++ b/packages/client/lib/commands/ZCARD.ts @@ -1,9 +1,20 @@ import { CommandParser } from '../client/parser'; import { RedisArgument, NumberReply, Command } from '../RESP/types'; +/** + * Command for getting the number of members in a sorted set + */ export default { CACHEABLE: true, IS_READ_ONLY: true, + /** + * Constructs the ZCARD command to get the cardinality (number of members) of a sorted set + * + * @param parser - The command parser + * @param key - The sorted set key + * @returns Number of members in the sorted set + * @see https://redis.io/commands/zcard/ + */ parseCommand(parser: CommandParser, key: RedisArgument) { parser.push('ZCARD'); parser.pushKey(key); diff --git a/packages/client/lib/commands/ZCOUNT.ts b/packages/client/lib/commands/ZCOUNT.ts index ccbc3d13d9b..0ac473eb710 100644 --- a/packages/client/lib/commands/ZCOUNT.ts +++ b/packages/client/lib/commands/ZCOUNT.ts @@ -5,6 +5,13 @@ import { transformStringDoubleArgument } from './generic-transformers'; export default { CACHEABLE: true, IS_READ_ONLY: true, + /** + * Returns the number of elements in the sorted set with a score between min and max. + * @param parser - The Redis command parser. + * @param key - Key of the sorted set. + * @param min - Minimum score to count from (inclusive). + * @param max - Maximum score to count to (inclusive). + */ parseCommand( parser: CommandParser, key: RedisArgument, diff --git a/packages/client/lib/commands/ZDIFF.ts b/packages/client/lib/commands/ZDIFF.ts index 28135dc9c13..f52492c2bca 100644 --- a/packages/client/lib/commands/ZDIFF.ts +++ b/packages/client/lib/commands/ZDIFF.ts @@ -4,6 +4,11 @@ import { RedisVariadicArgument } from './generic-transformers'; export default { IS_READ_ONLY: true, + /** + * Returns the difference between the first sorted set and all the successive sorted sets. + * @param parser - The Redis command parser. + * @param keys - Keys of the sorted sets. + */ parseCommand(parser: CommandParser, keys: RedisVariadicArgument) { parser.push('ZDIFF'); parser.pushKeysLength(keys); diff --git a/packages/client/lib/commands/ZDIFFSTORE.ts b/packages/client/lib/commands/ZDIFFSTORE.ts index d83a4bdc851..87407421fb0 100644 --- a/packages/client/lib/commands/ZDIFFSTORE.ts +++ b/packages/client/lib/commands/ZDIFFSTORE.ts @@ -4,6 +4,12 @@ import { RedisVariadicArgument } from './generic-transformers'; export default { IS_READ_ONLY: true, + /** + * Computes the difference between the first and all successive sorted sets and stores it in a new key. + * @param parser - The Redis command parser. + * @param destination - Destination key where the result will be stored. + * @param inputKeys - Keys of the sorted sets to find the difference between. + */ parseCommand(parser: CommandParser, destination: RedisArgument, inputKeys: RedisVariadicArgument) { parser.push('ZDIFFSTORE'); parser.pushKey(destination); diff --git a/packages/client/lib/commands/ZDIFF_WITHSCORES.ts b/packages/client/lib/commands/ZDIFF_WITHSCORES.ts index 4088f106dc6..6cb661b652a 100644 --- a/packages/client/lib/commands/ZDIFF_WITHSCORES.ts +++ b/packages/client/lib/commands/ZDIFF_WITHSCORES.ts @@ -6,6 +6,11 @@ import ZDIFF from './ZDIFF'; export default { IS_READ_ONLY: ZDIFF.IS_READ_ONLY, + /** + * Returns the difference between the first sorted set and all successive sorted sets with their scores. + * @param parser - The Redis command parser. + * @param keys - Keys of the sorted sets. + */ parseCommand(parser: CommandParser, keys: RedisVariadicArgument) { ZDIFF.parseCommand(parser, keys); parser.push('WITHSCORES'); diff --git a/packages/client/lib/commands/ZINCRBY.ts b/packages/client/lib/commands/ZINCRBY.ts index 5e461891e9c..30692fffb59 100644 --- a/packages/client/lib/commands/ZINCRBY.ts +++ b/packages/client/lib/commands/ZINCRBY.ts @@ -3,6 +3,13 @@ import { RedisArgument, Command } from '../RESP/types'; import { transformDoubleArgument, transformDoubleReply } from './generic-transformers'; export default { + /** + * Increments the score of a member in a sorted set by the specified increment. + * @param parser - The Redis command parser. + * @param key - Key of the sorted set. + * @param increment - Value to increment the score by. + * @param member - Member whose score should be incremented. + */ parseCommand( parser: CommandParser, key: RedisArgument, diff --git a/packages/client/lib/commands/ZINTER.ts b/packages/client/lib/commands/ZINTER.ts index 740d3c295ec..30d6716293e 100644 --- a/packages/client/lib/commands/ZINTER.ts +++ b/packages/client/lib/commands/ZINTER.ts @@ -29,6 +29,12 @@ export function parseZInterArguments( export default { IS_READ_ONLY: true, + /** + * Intersects multiple sorted sets and returns the result as a new sorted set. + * @param parser - The Redis command parser. + * @param keys - Keys of the sorted sets to intersect. + * @param options - Optional parameters for the intersection operation. + */ parseCommand(parser: CommandParser, keys: ZInterKeysType, options?: ZInterOptions) { parser.push('ZINTER'); parseZInterArguments(parser, keys, options); diff --git a/packages/client/lib/commands/ZINTERCARD.ts b/packages/client/lib/commands/ZINTERCARD.ts index 8c2e98d12cb..7673b0f0a69 100644 --- a/packages/client/lib/commands/ZINTERCARD.ts +++ b/packages/client/lib/commands/ZINTERCARD.ts @@ -8,6 +8,12 @@ export interface ZInterCardOptions { export default { IS_READ_ONLY: true, + /** + * Returns the cardinality of the intersection of multiple sorted sets. + * @param parser - The Redis command parser. + * @param keys - Keys of the sorted sets to intersect. + * @param options - Limit option or options object with limit. + */ parseCommand( parser: CommandParser, keys: RedisVariadicArgument, diff --git a/packages/client/lib/commands/ZINTERSTORE.ts b/packages/client/lib/commands/ZINTERSTORE.ts index dcbe153cfc7..1405b70287b 100644 --- a/packages/client/lib/commands/ZINTERSTORE.ts +++ b/packages/client/lib/commands/ZINTERSTORE.ts @@ -6,6 +6,13 @@ import { parseZInterArguments, ZInterOptions } from './ZINTER'; export default { IS_READ_ONLY: false, + /** + * Stores the result of intersection of multiple sorted sets in a new sorted set. + * @param parser - The Redis command parser. + * @param destination - Destination key where the result will be stored. + * @param keys - Keys of the sorted sets to intersect. + * @param options - Optional parameters for the intersection operation. + */ parseCommand( parser: CommandParser, destination: RedisArgument, diff --git a/packages/client/lib/commands/ZINTER_WITHSCORES.ts b/packages/client/lib/commands/ZINTER_WITHSCORES.ts index d3a6614b3c2..40ba3ce4287 100644 --- a/packages/client/lib/commands/ZINTER_WITHSCORES.ts +++ b/packages/client/lib/commands/ZINTER_WITHSCORES.ts @@ -5,6 +5,10 @@ import ZINTER from './ZINTER'; export default { IS_READ_ONLY: ZINTER.IS_READ_ONLY, + /** + * Intersects multiple sorted sets and returns the result with scores. + * @param args - Same parameters as ZINTER command. + */ parseCommand(...args: Parameters) { ZINTER.parseCommand(...args); args[0].push('WITHSCORES'); diff --git a/packages/client/lib/commands/ZLEXCOUNT.ts b/packages/client/lib/commands/ZLEXCOUNT.ts index 7536590c168..97bed9f6014 100644 --- a/packages/client/lib/commands/ZLEXCOUNT.ts +++ b/packages/client/lib/commands/ZLEXCOUNT.ts @@ -4,6 +4,13 @@ import { RedisArgument, NumberReply, Command } from '../RESP/types'; export default { CACHEABLE: true, IS_READ_ONLY: true, + /** + * Returns the number of elements in the sorted set between the lexicographical range specified by min and max. + * @param parser - The Redis command parser. + * @param key - Key of the sorted set. + * @param min - Minimum lexicographical value (inclusive). + * @param max - Maximum lexicographical value (inclusive). + */ parseCommand( parser: CommandParser, key: RedisArgument, diff --git a/packages/client/lib/commands/ZMPOP.ts b/packages/client/lib/commands/ZMPOP.ts index 0e47108e25f..fe766ddd13a 100644 --- a/packages/client/lib/commands/ZMPOP.ts +++ b/packages/client/lib/commands/ZMPOP.ts @@ -33,6 +33,13 @@ export type ZMPopArguments = Tail>; export default { IS_READ_ONLY: false, + /** + * Removes and returns up to count members with the highest/lowest scores from the first non-empty sorted set. + * @param parser - The Redis command parser. + * @param keys - Keys of the sorted sets to pop from. + * @param side - Side to pop from (MIN or MAX). + * @param options - Optional parameters including COUNT. + */ parseCommand( parser: CommandParser, keys: RedisVariadicArgument, diff --git a/packages/client/lib/commands/ZMSCORE.ts b/packages/client/lib/commands/ZMSCORE.ts index b225b35dfd3..0275e8d98db 100644 --- a/packages/client/lib/commands/ZMSCORE.ts +++ b/packages/client/lib/commands/ZMSCORE.ts @@ -5,6 +5,12 @@ import { createTransformNullableDoubleReplyResp2Func, RedisVariadicArgument } fr export default { CACHEABLE: true, IS_READ_ONLY: true, + /** + * Returns the scores associated with the specified members in the sorted set stored at key. + * @param parser - The Redis command parser. + * @param key - Key of the sorted set. + * @param member - One or more members to get scores for. + */ parseCommand(parser: CommandParser, key: RedisArgument, member: RedisVariadicArgument) { parser.push('ZMSCORE'); parser.pushKey(key); diff --git a/packages/client/lib/commands/ZPOPMAX.ts b/packages/client/lib/commands/ZPOPMAX.ts index 05c7f35e052..fd7b7cf9f94 100644 --- a/packages/client/lib/commands/ZPOPMAX.ts +++ b/packages/client/lib/commands/ZPOPMAX.ts @@ -4,6 +4,11 @@ import { transformDoubleReply } from './generic-transformers'; export default { IS_READ_ONLY: false, + /** + * Removes and returns the member with the highest score in the sorted set. + * @param parser - The Redis command parser. + * @param key - Key of the sorted set. + */ parseCommand(parser: CommandParser, key: RedisArgument) { parser.push('ZPOPMAX'); parser.pushKey(key); diff --git a/packages/client/lib/commands/ZPOPMAX_COUNT.ts b/packages/client/lib/commands/ZPOPMAX_COUNT.ts index 888ce039fbe..50f347acf3e 100644 --- a/packages/client/lib/commands/ZPOPMAX_COUNT.ts +++ b/packages/client/lib/commands/ZPOPMAX_COUNT.ts @@ -4,6 +4,12 @@ import { transformSortedSetReply } from './generic-transformers'; export default { IS_READ_ONLY: false, + /** + * Removes and returns up to count members with the highest scores in the sorted set. + * @param parser - The Redis command parser. + * @param key - Key of the sorted set. + * @param count - Number of members to pop. + */ parseCommand(parser: CommandParser, key: RedisArgument, count: number) { parser.push('ZPOPMAX'); parser.pushKey(key); diff --git a/packages/client/lib/commands/ZPOPMIN.ts b/packages/client/lib/commands/ZPOPMIN.ts index 6295925aef1..2de4977da7f 100644 --- a/packages/client/lib/commands/ZPOPMIN.ts +++ b/packages/client/lib/commands/ZPOPMIN.ts @@ -4,6 +4,11 @@ import ZPOPMAX from './ZPOPMAX'; export default { IS_READ_ONLY: false, + /** + * Removes and returns the member with the lowest score in the sorted set. + * @param parser - The Redis command parser. + * @param key - Key of the sorted set. + */ parseCommand(parser: CommandParser, key: RedisArgument) { parser.push('ZPOPMIN'); parser.pushKey(key); diff --git a/packages/client/lib/commands/ZPOPMIN_COUNT.ts b/packages/client/lib/commands/ZPOPMIN_COUNT.ts index 2b6abf580b9..24e084b2aef 100644 --- a/packages/client/lib/commands/ZPOPMIN_COUNT.ts +++ b/packages/client/lib/commands/ZPOPMIN_COUNT.ts @@ -4,6 +4,12 @@ import { transformSortedSetReply } from './generic-transformers'; export default { IS_READ_ONLY: false, + /** + * Removes and returns up to count members with the lowest scores in the sorted set. + * @param parser - The Redis command parser. + * @param key - Key of the sorted set. + * @param count - Number of members to pop. + */ parseCommand(parser: CommandParser, key: RedisArgument, count: number) { parser.push('ZPOPMIN'); parser.pushKey(key); diff --git a/packages/client/lib/commands/ZRANDMEMBER.ts b/packages/client/lib/commands/ZRANDMEMBER.ts index 2abd9d3684c..ed0a529da5e 100644 --- a/packages/client/lib/commands/ZRANDMEMBER.ts +++ b/packages/client/lib/commands/ZRANDMEMBER.ts @@ -3,6 +3,11 @@ import { RedisArgument, BlobStringReply, NullReply, Command } from '../RESP/type export default { IS_READ_ONLY: true, + /** + * Returns a random member from a sorted set. + * @param parser - The Redis command parser. + * @param key - Key of the sorted set. + */ parseCommand(parser: CommandParser, key: RedisArgument) { parser.push('ZRANDMEMBER'); parser.pushKey(key); diff --git a/packages/client/lib/commands/ZRANDMEMBER_COUNT.ts b/packages/client/lib/commands/ZRANDMEMBER_COUNT.ts index 42ef8110639..f201f9c236a 100644 --- a/packages/client/lib/commands/ZRANDMEMBER_COUNT.ts +++ b/packages/client/lib/commands/ZRANDMEMBER_COUNT.ts @@ -4,6 +4,12 @@ import ZRANDMEMBER from './ZRANDMEMBER'; export default { IS_READ_ONLY: ZRANDMEMBER.IS_READ_ONLY, + /** + * Returns one or more random members from a sorted set. + * @param parser - The Redis command parser. + * @param key - Key of the sorted set. + * @param count - Number of members to return. + */ parseCommand(parser: CommandParser, key: RedisArgument, count: number) { ZRANDMEMBER.parseCommand(parser, key); parser.push(count.toString()); diff --git a/packages/client/lib/commands/ZRANDMEMBER_COUNT_WITHSCORES.ts b/packages/client/lib/commands/ZRANDMEMBER_COUNT_WITHSCORES.ts index f096e9d807d..3792bce794f 100644 --- a/packages/client/lib/commands/ZRANDMEMBER_COUNT_WITHSCORES.ts +++ b/packages/client/lib/commands/ZRANDMEMBER_COUNT_WITHSCORES.ts @@ -5,6 +5,12 @@ import ZRANDMEMBER_COUNT from './ZRANDMEMBER_COUNT'; export default { IS_READ_ONLY: ZRANDMEMBER_COUNT.IS_READ_ONLY, + /** + * Returns one or more random members with their scores from a sorted set. + * @param parser - The Redis command parser. + * @param key - Key of the sorted set. + * @param count - Number of members to return. + */ parseCommand(parser: CommandParser, key: RedisArgument, count: number) { ZRANDMEMBER_COUNT.parseCommand(parser, key, count); parser.push('WITHSCORES'); diff --git a/packages/client/lib/commands/ZRANGE.ts b/packages/client/lib/commands/ZRANGE.ts index d1bc3433a50..43801289bde 100644 --- a/packages/client/lib/commands/ZRANGE.ts +++ b/packages/client/lib/commands/ZRANGE.ts @@ -49,6 +49,14 @@ export function zRangeArgument( export default { CACHEABLE: true, IS_READ_ONLY: true, + /** + * Returns the specified range of elements in the sorted set. + * @param parser - The Redis command parser. + * @param key - Key of the sorted set. + * @param min - Minimum index, score or lexicographical value. + * @param max - Maximum index, score or lexicographical value. + * @param options - Optional parameters for range retrieval (BY, REV, LIMIT). + */ parseCommand( parser: CommandParser, key: RedisArgument, diff --git a/packages/client/lib/commands/ZRANGEBYLEX.ts b/packages/client/lib/commands/ZRANGEBYLEX.ts index 316d9745c7e..e069fa55b4b 100644 --- a/packages/client/lib/commands/ZRANGEBYLEX.ts +++ b/packages/client/lib/commands/ZRANGEBYLEX.ts @@ -12,6 +12,14 @@ export interface ZRangeByLexOptions { export default { CACHEABLE: true, IS_READ_ONLY: true, + /** + * Returns all the elements in the sorted set at key with a lexicographical value between min and max. + * @param parser - The Redis command parser. + * @param key - Key of the sorted set. + * @param min - Minimum lexicographical value. + * @param max - Maximum lexicographical value. + * @param options - Optional parameters including LIMIT. + */ parseCommand( parser: CommandParser, key: RedisArgument, diff --git a/packages/client/lib/commands/ZRANGEBYSCORE.ts b/packages/client/lib/commands/ZRANGEBYSCORE.ts index 4d5471fdc0b..80bc8bc2b6c 100644 --- a/packages/client/lib/commands/ZRANGEBYSCORE.ts +++ b/packages/client/lib/commands/ZRANGEBYSCORE.ts @@ -14,6 +14,14 @@ export declare function transformReply(): Array; export default { CACHEABLE: true, IS_READ_ONLY: true, + /** + * Returns all the elements in the sorted set with a score between min and max. + * @param parser - The Redis command parser. + * @param key - Key of the sorted set. + * @param min - Minimum score. + * @param max - Maximum score. + * @param options - Optional parameters including LIMIT. + */ parseCommand( parser: CommandParser, key: RedisArgument, diff --git a/packages/client/lib/commands/ZRANGEBYSCORE_WITHSCORES.ts b/packages/client/lib/commands/ZRANGEBYSCORE_WITHSCORES.ts index 1a759b23dce..9cea5bd7b83 100644 --- a/packages/client/lib/commands/ZRANGEBYSCORE_WITHSCORES.ts +++ b/packages/client/lib/commands/ZRANGEBYSCORE_WITHSCORES.ts @@ -5,6 +5,10 @@ import ZRANGEBYSCORE from './ZRANGEBYSCORE'; export default { CACHEABLE: ZRANGEBYSCORE.CACHEABLE, IS_READ_ONLY: ZRANGEBYSCORE.IS_READ_ONLY, + /** + * Returns all the elements in the sorted set with a score between min and max, with their scores. + * @param args - Same parameters as the ZRANGEBYSCORE command. + */ parseCommand(...args: Parameters) { const parser = args[0]; diff --git a/packages/client/lib/commands/ZRANGESTORE.ts b/packages/client/lib/commands/ZRANGESTORE.ts index f73e93a506f..bd3e260e32c 100644 --- a/packages/client/lib/commands/ZRANGESTORE.ts +++ b/packages/client/lib/commands/ZRANGESTORE.ts @@ -13,6 +13,15 @@ export interface ZRangeStoreOptions { export default { IS_READ_ONLY: false, + /** + * Stores the result of a range operation on a sorted set into a new sorted set. + * @param parser - The Redis command parser. + * @param destination - Destination key where the result will be stored. + * @param source - Key of the source sorted set. + * @param min - Minimum index, score or lexicographical value. + * @param max - Maximum index, score or lexicographical value. + * @param options - Optional parameters for the range operation (BY, REV, LIMIT). + */ parseCommand( parser: CommandParser, destination: RedisArgument, diff --git a/packages/client/lib/commands/ZRANGE_WITHSCORES.ts b/packages/client/lib/commands/ZRANGE_WITHSCORES.ts index 7e6cf00cf2e..e85af4be08e 100644 --- a/packages/client/lib/commands/ZRANGE_WITHSCORES.ts +++ b/packages/client/lib/commands/ZRANGE_WITHSCORES.ts @@ -5,6 +5,10 @@ import ZRANGE from './ZRANGE'; export default { CACHEABLE: ZRANGE.CACHEABLE, IS_READ_ONLY: ZRANGE.IS_READ_ONLY, + /** + * Returns the specified range of elements in the sorted set with their scores. + * @param args - Same parameters as the ZRANGE command. + */ parseCommand(...args: Parameters) { const parser = args[0]; diff --git a/packages/client/lib/commands/ZRANK.ts b/packages/client/lib/commands/ZRANK.ts index 045e9ef8c25..73329aa2a59 100644 --- a/packages/client/lib/commands/ZRANK.ts +++ b/packages/client/lib/commands/ZRANK.ts @@ -4,6 +4,12 @@ import { RedisArgument, NumberReply, NullReply, Command } from '../RESP/types'; export default { CACHEABLE: true, IS_READ_ONLY: true, + /** + * Returns the rank of a member in the sorted set, with scores ordered from low to high. + * @param parser - The Redis command parser. + * @param key - Key of the sorted set. + * @param member - Member to get the rank for. + */ parseCommand(parser: CommandParser, key: RedisArgument, member: RedisArgument) { parser.push('ZRANK'); parser.pushKey(key); diff --git a/packages/client/lib/commands/ZRANK_WITHSCORE.ts b/packages/client/lib/commands/ZRANK_WITHSCORE.ts index dc2e48b362d..6f6537f4087 100644 --- a/packages/client/lib/commands/ZRANK_WITHSCORE.ts +++ b/packages/client/lib/commands/ZRANK_WITHSCORE.ts @@ -4,6 +4,10 @@ import ZRANK from './ZRANK'; export default { CACHEABLE: ZRANK.CACHEABLE, IS_READ_ONLY: ZRANK.IS_READ_ONLY, + /** + * Returns the rank of a member in the sorted set with its score. + * @param args - Same parameters as the ZRANK command. + */ parseCommand(...args: Parameters) { const parser = args[0]; diff --git a/packages/client/lib/commands/ZREM.ts b/packages/client/lib/commands/ZREM.ts index c8ba0ec02a6..960b47a36fd 100644 --- a/packages/client/lib/commands/ZREM.ts +++ b/packages/client/lib/commands/ZREM.ts @@ -4,6 +4,12 @@ import { RedisVariadicArgument } from './generic-transformers'; export default { IS_READ_ONLY: false, + /** + * Removes the specified members from the sorted set. + * @param parser - The Redis command parser. + * @param key - Key of the sorted set. + * @param member - One or more members to remove. + */ parseCommand( parser: CommandParser, key: RedisArgument, diff --git a/packages/client/lib/commands/ZREMRANGEBYLEX.ts b/packages/client/lib/commands/ZREMRANGEBYLEX.ts index 5d7e1a21bb0..434dcc6aac0 100644 --- a/packages/client/lib/commands/ZREMRANGEBYLEX.ts +++ b/packages/client/lib/commands/ZREMRANGEBYLEX.ts @@ -4,6 +4,13 @@ import { transformStringDoubleArgument } from './generic-transformers'; export default { IS_READ_ONLY: false, + /** + * Removes all elements in the sorted set with lexicographical values between min and max. + * @param parser - The Redis command parser. + * @param key - Key of the sorted set. + * @param min - Minimum lexicographical value. + * @param max - Maximum lexicographical value. + */ parseCommand( parser: CommandParser, key: RedisArgument, diff --git a/packages/client/lib/commands/ZREMRANGEBYRANK.ts b/packages/client/lib/commands/ZREMRANGEBYRANK.ts index 0a2eb3fadf3..90ab6b3aefe 100644 --- a/packages/client/lib/commands/ZREMRANGEBYRANK.ts +++ b/packages/client/lib/commands/ZREMRANGEBYRANK.ts @@ -3,6 +3,13 @@ import { RedisArgument, NumberReply, Command } from '../RESP/types'; export default { IS_READ_ONLY: false, + /** + * Removes all elements in the sorted set with rank between start and stop. + * @param parser - The Redis command parser. + * @param key - Key of the sorted set. + * @param start - Minimum rank (starting from 0). + * @param stop - Maximum rank. + */ parseCommand( parser: CommandParser, key: RedisArgument, diff --git a/packages/client/lib/commands/ZREMRANGEBYSCORE.ts b/packages/client/lib/commands/ZREMRANGEBYSCORE.ts index 3d23d875948..e78c57ea656 100644 --- a/packages/client/lib/commands/ZREMRANGEBYSCORE.ts +++ b/packages/client/lib/commands/ZREMRANGEBYSCORE.ts @@ -4,6 +4,13 @@ import { transformStringDoubleArgument } from './generic-transformers'; export default { IS_READ_ONLY: false, + /** + * Removes all elements in the sorted set with scores between min and max. + * @param parser - The Redis command parser. + * @param key - Key of the sorted set. + * @param min - Minimum score. + * @param max - Maximum score. + */ parseCommand( parser: CommandParser, key: RedisArgument, diff --git a/packages/client/lib/commands/ZREVRANK.ts b/packages/client/lib/commands/ZREVRANK.ts index d48dc68adc2..f2f79e570cc 100644 --- a/packages/client/lib/commands/ZREVRANK.ts +++ b/packages/client/lib/commands/ZREVRANK.ts @@ -4,6 +4,12 @@ import { NumberReply, NullReply, Command, RedisArgument } from '../RESP/types'; export default { CACHEABLE: true, IS_READ_ONLY: true, + /** + * Returns the rank of a member in the sorted set, with scores ordered from high to low. + * @param parser - The Redis command parser. + * @param key - Key of the sorted set. + * @param member - Member to get the rank for. + */ parseCommand(parser: CommandParser, key: RedisArgument, member: RedisArgument) { parser.push('ZREVRANK'); parser.pushKey(key); diff --git a/packages/client/lib/commands/ZSCAN.ts b/packages/client/lib/commands/ZSCAN.ts index 051235033eb..2790db5e023 100644 --- a/packages/client/lib/commands/ZSCAN.ts +++ b/packages/client/lib/commands/ZSCAN.ts @@ -10,6 +10,13 @@ export interface HScanEntry { export default { IS_READ_ONLY: true, + /** + * Incrementally iterates over a sorted set. + * @param parser - The Redis command parser. + * @param key - Key of the sorted set. + * @param cursor - Cursor position to start the scan from. + * @param options - Optional scan parameters (COUNT, MATCH, TYPE). + */ parseCommand( parser: CommandParser, key: RedisArgument, diff --git a/packages/client/lib/commands/ZSCORE.ts b/packages/client/lib/commands/ZSCORE.ts index 23b52901078..8b44154f44d 100644 --- a/packages/client/lib/commands/ZSCORE.ts +++ b/packages/client/lib/commands/ZSCORE.ts @@ -6,6 +6,12 @@ import { transformNullableDoubleReply } from './generic-transformers'; export default { CACHEABLE: true, IS_READ_ONLY: true, + /** + * Returns the score of a member in a sorted set. + * @param parser - The Redis command parser. + * @param key - Key of the sorted set. + * @param member - Member to get the score for. + */ parseCommand(parser: CommandParser, key: RedisArgument, member: RedisArgument) { parser.push('ZSCORE'); parser.pushKey(key); diff --git a/packages/client/lib/commands/ZUNION.ts b/packages/client/lib/commands/ZUNION.ts index a91dc68bc09..6497d0d8e8b 100644 --- a/packages/client/lib/commands/ZUNION.ts +++ b/packages/client/lib/commands/ZUNION.ts @@ -8,6 +8,12 @@ export interface ZUnionOptions { export default { IS_READ_ONLY: true, + /** + * Returns the union of multiple sorted sets. + * @param parser - The Redis command parser. + * @param keys - Keys of the sorted sets to combine. + * @param options - Optional parameters for the union operation. + */ parseCommand(parser: CommandParser, keys: ZKeys, options?: ZUnionOptions) { parser.push('ZUNION'); parseZKeysArguments(parser, keys); diff --git a/packages/client/lib/commands/ZUNIONSTORE.ts b/packages/client/lib/commands/ZUNIONSTORE.ts index c88f5a5a6f9..9de766e8b06 100644 --- a/packages/client/lib/commands/ZUNIONSTORE.ts +++ b/packages/client/lib/commands/ZUNIONSTORE.ts @@ -8,6 +8,13 @@ export interface ZUnionOptions { export default { IS_READ_ONLY: false, + /** + * Stores the union of multiple sorted sets in a new sorted set. + * @param parser - The Redis command parser. + * @param destination - Destination key where the result will be stored. + * @param keys - Keys of the sorted sets to combine. + * @param options - Optional parameters for the union operation. + */ parseCommand( parser: CommandParser, destination: RedisArgument, diff --git a/packages/client/lib/commands/ZUNION_WITHSCORES.ts b/packages/client/lib/commands/ZUNION_WITHSCORES.ts index c62df55518f..af93a4eb1c0 100644 --- a/packages/client/lib/commands/ZUNION_WITHSCORES.ts +++ b/packages/client/lib/commands/ZUNION_WITHSCORES.ts @@ -5,6 +5,10 @@ import ZUNION from './ZUNION'; export default { IS_READ_ONLY: ZUNION.IS_READ_ONLY, + /** + * Returns the union of multiple sorted sets with their scores. + * @param args - Same parameters as the ZUNION command. + */ parseCommand(...args: Parameters) { const parser = args[0]; diff --git a/packages/client/lib/sentinel/commands/SENTINEL_MASTER.ts b/packages/client/lib/sentinel/commands/SENTINEL_MASTER.ts index 84997ac7d8f..842b86a0596 100644 --- a/packages/client/lib/sentinel/commands/SENTINEL_MASTER.ts +++ b/packages/client/lib/sentinel/commands/SENTINEL_MASTER.ts @@ -3,6 +3,11 @@ import { CommandParser } from '../../client/parser'; import { transformTuplesReply } from '../../commands/generic-transformers'; export default { + /** + * Returns information about the specified master. + * @param parser - The Redis command parser. + * @param dbname - Name of the master. + */ parseCommand(parser: CommandParser, dbname: RedisArgument) { parser.push('SENTINEL', 'MASTER', dbname); }, diff --git a/packages/client/lib/sentinel/commands/SENTINEL_MONITOR.ts b/packages/client/lib/sentinel/commands/SENTINEL_MONITOR.ts index 65f438de132..eed4f7e7233 100644 --- a/packages/client/lib/sentinel/commands/SENTINEL_MONITOR.ts +++ b/packages/client/lib/sentinel/commands/SENTINEL_MONITOR.ts @@ -2,6 +2,14 @@ import { CommandParser } from '../../client/parser'; import { RedisArgument, SimpleStringReply, Command } from '../../RESP/types'; export default { + /** + * Instructs a Sentinel to monitor a new master with the specified parameters. + * @param parser - The Redis command parser. + * @param dbname - Name that identifies the master. + * @param host - Host of the master. + * @param port - Port of the master. + * @param quorum - Number of Sentinels that need to agree to trigger a failover. + */ parseCommand(parser: CommandParser, dbname: RedisArgument, host: RedisArgument, port: RedisArgument, quorum: RedisArgument) { parser.push('SENTINEL', 'MONITOR', dbname, host, port, quorum); }, diff --git a/packages/client/lib/sentinel/commands/SENTINEL_REPLICAS.ts b/packages/client/lib/sentinel/commands/SENTINEL_REPLICAS.ts index 127449264d8..4228a2123d9 100644 --- a/packages/client/lib/sentinel/commands/SENTINEL_REPLICAS.ts +++ b/packages/client/lib/sentinel/commands/SENTINEL_REPLICAS.ts @@ -3,6 +3,11 @@ import { RedisArgument, ArrayReply, BlobStringReply, MapReply, Command, TypeMapp import { transformTuplesReply } from '../../commands/generic-transformers'; export default { + /** + * Returns a list of replicas for the specified master. + * @param parser - The Redis command parser. + * @param dbname - Name of the master. + */ parseCommand(parser: CommandParser, dbname: RedisArgument) { parser.push('SENTINEL', 'REPLICAS', dbname); }, diff --git a/packages/client/lib/sentinel/commands/SENTINEL_SENTINELS.ts b/packages/client/lib/sentinel/commands/SENTINEL_SENTINELS.ts index 4550b9498b3..20cccbb76b6 100644 --- a/packages/client/lib/sentinel/commands/SENTINEL_SENTINELS.ts +++ b/packages/client/lib/sentinel/commands/SENTINEL_SENTINELS.ts @@ -3,6 +3,11 @@ import { RedisArgument, ArrayReply, MapReply, BlobStringReply, Command, TypeMapp import { transformTuplesReply } from '../../commands/generic-transformers'; export default { + /** + * Returns a list of Sentinel instances for the specified master. + * @param parser - The Redis command parser. + * @param dbname - Name of the master. + */ parseCommand(parser: CommandParser, dbname: RedisArgument) { parser.push('SENTINEL', 'SENTINELS', dbname); }, diff --git a/packages/client/lib/sentinel/commands/SENTINEL_SET.ts b/packages/client/lib/sentinel/commands/SENTINEL_SET.ts index b4e8f843ea6..b2881c14e5a 100644 --- a/packages/client/lib/sentinel/commands/SENTINEL_SET.ts +++ b/packages/client/lib/sentinel/commands/SENTINEL_SET.ts @@ -7,6 +7,12 @@ export type SentinelSetOptions = Array<{ }>; export default { + /** + * Sets configuration parameters for a specific master. + * @param parser - The Redis command parser. + * @param dbname - Name of the master. + * @param options - Configuration options to set as option-value pairs. + */ parseCommand(parser: CommandParser, dbname: RedisArgument, options: SentinelSetOptions) { parser.push('SENTINEL', 'SET', dbname); diff --git a/packages/json/lib/commands/ARRAPPEND.ts b/packages/json/lib/commands/ARRAPPEND.ts index d2283b128e3..d1082baf48e 100644 --- a/packages/json/lib/commands/ARRAPPEND.ts +++ b/packages/json/lib/commands/ARRAPPEND.ts @@ -4,6 +4,16 @@ import { RedisArgument, NumberReply, ArrayReply, NullReply, Command } from '@red export default { IS_READ_ONLY: false, + /** + * Appends one or more values to the end of an array in a JSON document. + * Returns the new array length after append, or null if the path does not exist. + * + * @param parser - The Redis command parser + * @param key - The key to append to + * @param path - Path to the array in the JSON document + * @param json - The first value to append + * @param jsons - Additional values to append + */ parseCommand( parser: CommandParser, key: RedisArgument, diff --git a/packages/json/lib/commands/ARRINDEX.ts b/packages/json/lib/commands/ARRINDEX.ts index 6ffcf9f5f0e..69485f55a6c 100644 --- a/packages/json/lib/commands/ARRINDEX.ts +++ b/packages/json/lib/commands/ARRINDEX.ts @@ -11,6 +11,18 @@ export interface JsonArrIndexOptions { export default { IS_READ_ONLY: true, + /** + * Returns the index of the first occurrence of a value in a JSON array. + * If the specified value is not found, it returns -1, or null if the path does not exist. + * + * @param parser - The Redis command parser + * @param key - The key containing the array + * @param path - Path to the array in the JSON document + * @param json - The value to search for + * @param options - Optional range parameters for the search + * @param options.range.start - Starting index for the search + * @param options.range.stop - Optional ending index for the search + */ parseCommand( parser: CommandParser, key: RedisArgument, diff --git a/packages/json/lib/commands/ARRINSERT.ts b/packages/json/lib/commands/ARRINSERT.ts index e64e0b18559..33fe30a99e8 100644 --- a/packages/json/lib/commands/ARRINSERT.ts +++ b/packages/json/lib/commands/ARRINSERT.ts @@ -4,6 +4,17 @@ import { RedisJSON, transformRedisJsonArgument } from './helpers'; export default { IS_READ_ONLY: false, + /** + * Inserts one or more values into an array at the specified index. + * Returns the new array length after insert, or null if the path does not exist. + * + * @param parser - The Redis command parser + * @param key - The key containing the array + * @param path - Path to the array in the JSON document + * @param index - The position where to insert the values + * @param json - The first value to insert + * @param jsons - Additional values to insert + */ parseCommand( parser: CommandParser, key: RedisArgument, diff --git a/packages/json/lib/commands/ARRLEN.ts b/packages/json/lib/commands/ARRLEN.ts index f49166c218d..f2111986c0e 100644 --- a/packages/json/lib/commands/ARRLEN.ts +++ b/packages/json/lib/commands/ARRLEN.ts @@ -7,6 +7,15 @@ export interface JsonArrLenOptions { export default { IS_READ_ONLY: true, + /** + * Returns the length of an array in a JSON document. + * Returns null if the path does not exist or the value is not an array. + * + * @param parser - The Redis command parser + * @param key - The key containing the array + * @param options - Optional parameters + * @param options.path - Path to the array in the JSON document + */ parseCommand(parser: CommandParser, key: RedisArgument, options?: JsonArrLenOptions) { parser.push('JSON.ARRLEN'); parser.pushKey(key); diff --git a/packages/json/lib/commands/ARRPOP.ts b/packages/json/lib/commands/ARRPOP.ts index 30ed34c37be..53d9ed2dc87 100644 --- a/packages/json/lib/commands/ARRPOP.ts +++ b/packages/json/lib/commands/ARRPOP.ts @@ -10,6 +10,16 @@ export interface RedisArrPopOptions { export default { IS_READ_ONLY: false, + /** + * Removes and returns an element from an array in a JSON document. + * Returns null if the path does not exist or the value is not an array. + * + * @param parser - The Redis command parser + * @param key - The key containing the array + * @param options - Optional parameters + * @param options.path - Path to the array in the JSON document + * @param options.index - Optional index to pop from. Default is -1 (last element) + */ parseCommand(parser: CommandParser, key: RedisArgument, options?: RedisArrPopOptions) { parser.push('JSON.ARRPOP'); parser.pushKey(key); diff --git a/packages/json/lib/commands/ARRTRIM.ts b/packages/json/lib/commands/ARRTRIM.ts index 573fa787507..bfcb1da14aa 100644 --- a/packages/json/lib/commands/ARRTRIM.ts +++ b/packages/json/lib/commands/ARRTRIM.ts @@ -3,6 +3,16 @@ import { RedisArgument, ArrayReply, NumberReply, NullReply, Command } from '@red export default { IS_READ_ONLY: false, + /** + * Trims an array in a JSON document to include only elements within the specified range. + * Returns the new array length after trimming, or null if the path does not exist. + * + * @param parser - The Redis command parser + * @param key - The key containing the array + * @param path - Path to the array in the JSON document + * @param start - Starting index (inclusive) + * @param stop - Ending index (inclusive) + */ parseCommand(parser: CommandParser, key: RedisArgument, path: RedisArgument, start: number, stop: number) { parser.push('JSON.ARRTRIM'); parser.pushKey(key); diff --git a/packages/json/lib/commands/CLEAR.ts b/packages/json/lib/commands/CLEAR.ts index b86513cc219..281c5e6abae 100644 --- a/packages/json/lib/commands/CLEAR.ts +++ b/packages/json/lib/commands/CLEAR.ts @@ -7,6 +7,15 @@ export interface JsonClearOptions { export default { IS_READ_ONLY: false, + /** + * Clears container values (arrays/objects) in a JSON document. + * Returns the number of values cleared (0 or 1), or null if the path does not exist. + * + * @param parser - The Redis command parser + * @param key - The key containing the JSON document + * @param options - Optional parameters + * @param options.path - Path to the container to clear + */ parseCommand(parser: CommandParser, key: RedisArgument, options?: JsonClearOptions) { parser.push('JSON.CLEAR'); parser.pushKey(key); diff --git a/packages/json/lib/commands/DEBUG_MEMORY.ts b/packages/json/lib/commands/DEBUG_MEMORY.ts index aa36d74c077..cf0f2c8f215 100644 --- a/packages/json/lib/commands/DEBUG_MEMORY.ts +++ b/packages/json/lib/commands/DEBUG_MEMORY.ts @@ -7,6 +7,15 @@ export interface JsonDebugMemoryOptions { export default { IS_READ_ONLY: false, + /** + * Reports memory usage details for a JSON document value. + * Returns size in bytes of the value, or null if the key or path does not exist. + * + * @param parser - The Redis command parser + * @param key - The key containing the JSON document + * @param options - Optional parameters + * @param options.path - Path to the value to examine + */ parseCommand(parser: CommandParser, key: RedisArgument, options?: JsonDebugMemoryOptions) { parser.push('JSON.DEBUG', 'MEMORY'); parser.pushKey(key); diff --git a/packages/json/lib/commands/DEL.ts b/packages/json/lib/commands/DEL.ts index e86366bebe6..4d768927088 100644 --- a/packages/json/lib/commands/DEL.ts +++ b/packages/json/lib/commands/DEL.ts @@ -7,6 +7,15 @@ export interface JsonDelOptions { export default { IS_READ_ONLY: false, + /** + * Deletes a value from a JSON document. + * Returns the number of paths deleted (0 or 1), or null if the key does not exist. + * + * @param parser - The Redis command parser + * @param key - The key containing the JSON document + * @param options - Optional parameters + * @param options.path - Path to the value to delete + */ parseCommand(parser: CommandParser, key: RedisArgument, options?: JsonDelOptions) { parser.push('JSON.DEL'); parser.pushKey(key); diff --git a/packages/json/lib/commands/FORGET.ts b/packages/json/lib/commands/FORGET.ts index 0a8ed3d91c4..ea924c74247 100644 --- a/packages/json/lib/commands/FORGET.ts +++ b/packages/json/lib/commands/FORGET.ts @@ -7,6 +7,15 @@ export interface JsonForgetOptions { export default { IS_READ_ONLY: false, + /** + * Alias for JSON.DEL - Deletes a value from a JSON document. + * Returns the number of paths deleted (0 or 1), or null if the key does not exist. + * + * @param parser - The Redis command parser + * @param key - The key containing the JSON document + * @param options - Optional parameters + * @param options.path - Path to the value to delete + */ parseCommand(parser: CommandParser, key: RedisArgument, options?: JsonForgetOptions) { parser.push('JSON.FORGET'); parser.pushKey(key); diff --git a/packages/json/lib/commands/GET.ts b/packages/json/lib/commands/GET.ts index 6705ac534bc..e514fefae39 100644 --- a/packages/json/lib/commands/GET.ts +++ b/packages/json/lib/commands/GET.ts @@ -9,6 +9,15 @@ export interface JsonGetOptions { export default { IS_READ_ONLY: false, + /** + * Gets values from a JSON document. + * Returns the value at the specified path, or null if the key or path does not exist. + * + * @param parser - The Redis command parser + * @param key - The key containing the JSON document + * @param options - Optional parameters + * @param options.path - Path(s) to the value(s) to retrieve + */ parseCommand( parser: CommandParser, key: RedisArgument, diff --git a/packages/json/lib/commands/MERGE.ts b/packages/json/lib/commands/MERGE.ts index 3c93913f91a..72baea1048a 100644 --- a/packages/json/lib/commands/MERGE.ts +++ b/packages/json/lib/commands/MERGE.ts @@ -4,6 +4,15 @@ import { RedisJSON, transformRedisJsonArgument } from './helpers'; export default { IS_READ_ONLY: false, + /** + * Merges a given JSON value into a JSON document. + * Returns OK on success, or null if the key does not exist. + * + * @param parser - The Redis command parser + * @param key - The key containing the JSON document + * @param path - Path to merge into + * @param value - JSON value to merge + */ parseCommand(parser: CommandParser, key: RedisArgument, path: RedisArgument, value: RedisJSON) { parser.push('JSON.MERGE'); parser.pushKey(key); diff --git a/packages/json/lib/commands/MGET.ts b/packages/json/lib/commands/MGET.ts index d0fc0a99089..7bb948bc667 100644 --- a/packages/json/lib/commands/MGET.ts +++ b/packages/json/lib/commands/MGET.ts @@ -4,6 +4,14 @@ import { transformRedisJsonNullReply } from './helpers'; export default { IS_READ_ONLY: true, + /** + * Gets values at a specific path from multiple JSON documents. + * Returns an array of values at the path from each key, null for missing keys/paths. + * + * @param parser - The Redis command parser + * @param keys - Array of keys containing JSON documents + * @param path - Path to retrieve from each document + */ parseCommand(parser: CommandParser, keys: Array, path: RedisArgument) { parser.push('JSON.MGET'); parser.pushKeys(keys); diff --git a/packages/json/lib/commands/MSET.ts b/packages/json/lib/commands/MSET.ts index 2dfab142493..9e5ec1799f1 100644 --- a/packages/json/lib/commands/MSET.ts +++ b/packages/json/lib/commands/MSET.ts @@ -10,6 +10,16 @@ export interface JsonMSetItem { export default { IS_READ_ONLY: false, + /** + * Sets multiple JSON values in multiple documents. + * Returns OK on success. + * + * @param parser - The Redis command parser + * @param items - Array of objects containing key, path, and value to set + * @param items[].key - The key containing the JSON document + * @param items[].path - Path in the document to set + * @param items[].value - JSON value to set at the path + */ parseCommand(parser: CommandParser, items: Array) { parser.push('JSON.MSET'); diff --git a/packages/json/lib/commands/NUMINCRBY.ts b/packages/json/lib/commands/NUMINCRBY.ts index 02c1c17dbc9..d8884385354 100644 --- a/packages/json/lib/commands/NUMINCRBY.ts +++ b/packages/json/lib/commands/NUMINCRBY.ts @@ -3,6 +3,15 @@ import { RedisArgument, ArrayReply, NumberReply, DoubleReply, NullReply, BlobStr export default { IS_READ_ONLY: false, + /** + * Increments a numeric value stored in a JSON document by a given number. + * Returns the value after increment, or null if the key/path doesn't exist or value is not numeric. + * + * @param parser - The Redis command parser + * @param key - The key containing the JSON document + * @param path - Path to the numeric value + * @param by - Amount to increment by + */ parseCommand(parser: CommandParser, key: RedisArgument, path: RedisArgument, by: number) { parser.push('JSON.NUMINCRBY'); parser.pushKey(key); diff --git a/packages/json/lib/commands/NUMMULTBY.ts b/packages/json/lib/commands/NUMMULTBY.ts index c3621908a4c..22b25640d4b 100644 --- a/packages/json/lib/commands/NUMMULTBY.ts +++ b/packages/json/lib/commands/NUMMULTBY.ts @@ -4,6 +4,15 @@ import NUMINCRBY from './NUMINCRBY'; export default { IS_READ_ONLY: false, + /** + * Multiplies a numeric value stored in a JSON document by a given number. + * Returns the value after multiplication, or null if the key/path doesn't exist or value is not numeric. + * + * @param parser - The Redis command parser + * @param key - The key containing the JSON document + * @param path - Path to the numeric value + * @param by - Amount to multiply by + */ parseCommand(parser: CommandParser, key: RedisArgument, path: RedisArgument, by: number) { parser.push('JSON.NUMMULTBY'); parser.pushKey(key); diff --git a/packages/json/lib/commands/OBJKEYS.ts b/packages/json/lib/commands/OBJKEYS.ts index f7e94dd4dfc..9f8abdc42c4 100644 --- a/packages/json/lib/commands/OBJKEYS.ts +++ b/packages/json/lib/commands/OBJKEYS.ts @@ -7,6 +7,15 @@ export interface JsonObjKeysOptions { export default { IS_READ_ONLY: false, + /** + * Returns the keys in the object stored in a JSON document. + * Returns array of keys, array of arrays for multiple paths, or null if path doesn't exist. + * + * @param parser - The Redis command parser + * @param key - The key containing the JSON document + * @param options - Optional parameters + * @param options.path - Path to the object to examine + */ parseCommand(parser: CommandParser, key: RedisArgument, options?: JsonObjKeysOptions) { parser.push('JSON.OBJKEYS'); parser.pushKey(key); diff --git a/packages/json/lib/commands/OBJLEN.ts b/packages/json/lib/commands/OBJLEN.ts index d1286a89b8c..0cee11770c1 100644 --- a/packages/json/lib/commands/OBJLEN.ts +++ b/packages/json/lib/commands/OBJLEN.ts @@ -7,6 +7,15 @@ export interface JsonObjLenOptions { export default { IS_READ_ONLY: true, + /** + * Returns the number of keys in the object stored in a JSON document. + * Returns length of object, array of lengths for multiple paths, or null if path doesn't exist. + * + * @param parser - The Redis command parser + * @param key - The key containing the JSON document + * @param options - Optional parameters + * @param options.path - Path to the object to examine + */ parseCommand(parser: CommandParser, key: RedisArgument, options?: JsonObjLenOptions) { parser.push('JSON.OBJLEN'); parser.pushKey(key); diff --git a/packages/json/lib/commands/RESP.ts b/packages/json/lib/commands/RESP.ts index 62084d73b0f..79cc2dac8d1 100644 --- a/packages/json/lib/commands/RESP.ts +++ b/packages/json/lib/commands/RESP.ts @@ -5,6 +5,14 @@ type RESPReply = Array; export default { IS_READ_ONLY: true, + /** + * Returns the JSON value at the specified path in RESP (Redis Serialization Protocol) format. + * Returns the value in RESP form, useful for language-independent processing. + * + * @param parser - The Redis command parser + * @param key - The key containing the JSON document + * @param path - Optional path to the value in the document + */ parseCommand(parser: CommandParser, key: RedisArgument, path?: string) { parser.push('JSON.RESP'); parser.pushKey(key); diff --git a/packages/json/lib/commands/SET.ts b/packages/json/lib/commands/SET.ts index 27da2ec64ee..a0df41fa89d 100644 --- a/packages/json/lib/commands/SET.ts +++ b/packages/json/lib/commands/SET.ts @@ -16,6 +16,19 @@ export interface JsonSetOptions { export default { IS_READ_ONLY: false, + /** + * Sets a JSON value at a specific path in a JSON document. + * Returns OK on success, or null if condition (NX/XX) is not met. + * + * @param parser - The Redis command parser + * @param key - The key containing the JSON document + * @param path - Path in the document to set + * @param json - JSON value to set at the path + * @param options - Optional parameters + * @param options.condition - Set condition: NX (only if doesn't exist) or XX (only if exists) + * @deprecated options.NX - Use options.condition instead + * @deprecated options.XX - Use options.condition instead + */ parseCommand( parser: CommandParser, key: RedisArgument, diff --git a/packages/json/lib/commands/STRAPPEND.ts b/packages/json/lib/commands/STRAPPEND.ts index 3c0e5767549..aa8f3772fb1 100644 --- a/packages/json/lib/commands/STRAPPEND.ts +++ b/packages/json/lib/commands/STRAPPEND.ts @@ -8,6 +8,16 @@ export interface JsonStrAppendOptions { export default { IS_READ_ONLY: false, + /** + * Appends a string to a string value stored in a JSON document. + * Returns new string length after append, or null if the path doesn't exist or value is not a string. + * + * @param parser - The Redis command parser + * @param key - The key containing the JSON document + * @param append - String to append + * @param options - Optional parameters + * @param options.path - Path to the string value + */ parseCommand(parser: CommandParser, key: RedisArgument, append: string, options?: JsonStrAppendOptions) { parser.push('JSON.STRAPPEND'); parser.pushKey(key); diff --git a/packages/json/lib/commands/STRLEN.ts b/packages/json/lib/commands/STRLEN.ts index 644cdf27ef7..ca1923d7c6a 100644 --- a/packages/json/lib/commands/STRLEN.ts +++ b/packages/json/lib/commands/STRLEN.ts @@ -7,6 +7,15 @@ export interface JsonStrLenOptions { export default { IS_READ_ONLY: true, + /** + * Returns the length of a string value stored in a JSON document. + * Returns string length, array of lengths for multiple paths, or null if path doesn't exist. + * + * @param parser - The Redis command parser + * @param key - The key containing the JSON document + * @param options - Optional parameters + * @param options.path - Path to the string value + */ parseCommand(parser: CommandParser, key: RedisArgument, options?: JsonStrLenOptions) { parser.push('JSON.STRLEN'); parser.pushKey(key); diff --git a/packages/json/lib/commands/TOGGLE.ts b/packages/json/lib/commands/TOGGLE.ts index 85c769729c7..2d93d391164 100644 --- a/packages/json/lib/commands/TOGGLE.ts +++ b/packages/json/lib/commands/TOGGLE.ts @@ -3,6 +3,14 @@ import { RedisArgument, ArrayReply, NumberReply, NullReply, Command, } from '@re export default { IS_READ_ONLY: false, + /** + * Toggles a boolean value stored in a JSON document. + * Returns 1 if value was toggled to true, 0 if toggled to false, or null if path doesn't exist. + * + * @param parser - The Redis command parser + * @param key - The key containing the JSON document + * @param path - Path to the boolean value + */ parseCommand(parser: CommandParser, key: RedisArgument, path: RedisArgument) { parser.push('JSON.TOGGLE'); parser.pushKey(key); diff --git a/packages/json/lib/commands/TYPE.ts b/packages/json/lib/commands/TYPE.ts index 1146043b2c2..758335a7361 100644 --- a/packages/json/lib/commands/TYPE.ts +++ b/packages/json/lib/commands/TYPE.ts @@ -7,6 +7,15 @@ export interface JsonTypeOptions { export default { IS_READ_ONLY: true, + /** + * Returns the type of JSON value at a specific path in a JSON document. + * Returns the type as a string, array of types for multiple paths, or null if path doesn't exist. + * + * @param parser - The Redis command parser + * @param key - The key containing the JSON document + * @param options - Optional parameters + * @param options.path - Path to examine + */ parseCommand(parser: CommandParser, key: RedisArgument, options?: JsonTypeOptions) { parser.push('JSON.TYPE'); parser.pushKey(key); diff --git a/packages/search/lib/commands/AGGREGATE.ts b/packages/search/lib/commands/AGGREGATE.ts index 0ac3d2e4ce0..9e8fb7810d6 100644 --- a/packages/search/lib/commands/AGGREGATE.ts +++ b/packages/search/lib/commands/AGGREGATE.ts @@ -141,6 +141,18 @@ export interface AggregateReply { export default { NOT_KEYED_COMMAND: true, IS_READ_ONLY: false, + /** + * Performs an aggregation query on a RediSearch index. + * @param parser - The command parser + * @param index - The index name to query + * @param query - The text query to use as filter, use * to indicate no filtering + * @param options - Optional parameters for aggregation: + * - VERBATIM: disable stemming in query evaluation + * - LOAD: specify fields to load from documents + * - STEPS: sequence of aggregation steps (GROUPBY, SORTBY, APPLY, LIMIT, FILTER) + * - PARAMS: bind parameters for query evaluation + * - TIMEOUT: maximum time to run the query + */ parseCommand(parser: CommandParser, index: RedisArgument, query: RedisArgument, options?: FtAggregateOptions) { parser.push('FT.AGGREGATE', index, query); diff --git a/packages/search/lib/commands/AGGREGATE_WITHCURSOR.ts b/packages/search/lib/commands/AGGREGATE_WITHCURSOR.ts index 8dfca7169ef..e1b0e42f9fe 100644 --- a/packages/search/lib/commands/AGGREGATE_WITHCURSOR.ts +++ b/packages/search/lib/commands/AGGREGATE_WITHCURSOR.ts @@ -19,6 +19,16 @@ export interface AggregateWithCursorReply extends AggregateReply { export default { IS_READ_ONLY: AGGREGATE.IS_READ_ONLY, + /** + * Performs an aggregation with a cursor for retrieving large result sets. + * @param parser - The command parser + * @param index - Name of the index to query + * @param query - The aggregation query + * @param options - Optional parameters: + * - All options supported by FT.AGGREGATE + * - COUNT: Number of results to return per cursor fetch + * - MAXIDLE: Maximum idle time for cursor in milliseconds + */ parseCommand(parser: CommandParser, index: RedisArgument, query: RedisArgument, options?: FtAggregateWithCursorOptions) { AGGREGATE.parseCommand(parser, index, query, options); parser.push('WITHCURSOR'); diff --git a/packages/search/lib/commands/ALIASADD.ts b/packages/search/lib/commands/ALIASADD.ts index c35e60bed4f..7d3a03498e6 100644 --- a/packages/search/lib/commands/ALIASADD.ts +++ b/packages/search/lib/commands/ALIASADD.ts @@ -4,6 +4,12 @@ import { RedisArgument, SimpleStringReply, Command } from '@redis/client/dist/li export default { NOT_KEYED_COMMAND: true, IS_READ_ONLY: true, + /** + * Adds an alias to a RediSearch index. + * @param parser - The command parser + * @param alias - The alias to add + * @param index - The index name to alias + */ parseCommand(parser: CommandParser, alias: RedisArgument, index: RedisArgument) { parser.push('FT.ALIASADD', alias, index); }, diff --git a/packages/search/lib/commands/ALIASDEL.ts b/packages/search/lib/commands/ALIASDEL.ts index 9a2dbda4b9e..3058be13997 100644 --- a/packages/search/lib/commands/ALIASDEL.ts +++ b/packages/search/lib/commands/ALIASDEL.ts @@ -4,6 +4,11 @@ import { RedisArgument, SimpleStringReply, Command } from '@redis/client/dist/li export default { NOT_KEYED_COMMAND: true, IS_READ_ONLY: true, + /** + * Removes an existing alias from a RediSearch index. + * @param parser - The command parser + * @param alias - The alias to remove + */ parseCommand(parser: CommandParser, alias: RedisArgument) { parser.push('FT.ALIASDEL', alias); }, diff --git a/packages/search/lib/commands/ALIASUPDATE.ts b/packages/search/lib/commands/ALIASUPDATE.ts index 3bd5ea92ba3..35879ea79cb 100644 --- a/packages/search/lib/commands/ALIASUPDATE.ts +++ b/packages/search/lib/commands/ALIASUPDATE.ts @@ -4,6 +4,12 @@ import { SimpleStringReply, Command, RedisArgument } from '@redis/client/dist/li export default { NOT_KEYED_COMMAND: true, IS_READ_ONLY: true, + /** + * Updates the index pointed to by an existing alias. + * @param parser - The command parser + * @param alias - The existing alias to update + * @param index - The new index name that the alias should point to + */ parseCommand(parser: CommandParser, alias: RedisArgument, index: RedisArgument) { parser.push('FT.ALIASUPDATE', alias, index); }, diff --git a/packages/search/lib/commands/ALTER.ts b/packages/search/lib/commands/ALTER.ts index 4a68817bd2c..05c1b799eb1 100644 --- a/packages/search/lib/commands/ALTER.ts +++ b/packages/search/lib/commands/ALTER.ts @@ -5,6 +5,12 @@ import { RediSearchSchema, parseSchema } from './CREATE'; export default { NOT_KEYED_COMMAND: true, IS_READ_ONLY: true, + /** + * Alters an existing RediSearch index schema by adding new fields. + * @param parser - The command parser + * @param index - The index to alter + * @param schema - The schema definition containing new fields to add + */ parseCommand(parser: CommandParser, index: RedisArgument, schema: RediSearchSchema) { parser.push('FT.ALTER', index, 'SCHEMA', 'ADD'); parseSchema(parser, schema); diff --git a/packages/search/lib/commands/CONFIG_GET.ts b/packages/search/lib/commands/CONFIG_GET.ts index ae7a9e0c78d..8073805c533 100644 --- a/packages/search/lib/commands/CONFIG_GET.ts +++ b/packages/search/lib/commands/CONFIG_GET.ts @@ -4,6 +4,11 @@ import { ArrayReply, TuplesReply, BlobStringReply, NullReply, UnwrapReply, Comma export default { NOT_KEYED_COMMAND: true, IS_READ_ONLY: true, + /** + * Gets a RediSearch configuration option value. + * @param parser - The command parser + * @param option - The name of the configuration option to retrieve + */ parseCommand(parser: CommandParser, option: string) { parser.push('FT.CONFIG', 'GET', option); }, diff --git a/packages/search/lib/commands/CONFIG_SET.ts b/packages/search/lib/commands/CONFIG_SET.ts index 499b9525aa3..c3c8cc7259b 100644 --- a/packages/search/lib/commands/CONFIG_SET.ts +++ b/packages/search/lib/commands/CONFIG_SET.ts @@ -8,6 +8,12 @@ type FtConfigProperties = 'a' | 'b' | (string & {}) | Buffer; export default { NOT_KEYED_COMMAND: true, IS_READ_ONLY: true, + /** + * Sets a RediSearch configuration option value. + * @param parser - The command parser + * @param property - The name of the configuration option to set + * @param value - The value to set for the configuration option + */ parseCommand(parser: CommandParser, property: FtConfigProperties, value: RedisArgument) { parser.push('FT.CONFIG', 'SET', property, value); }, diff --git a/packages/search/lib/commands/CREATE.ts b/packages/search/lib/commands/CREATE.ts index 5645a2b2dce..9f24a256fae 100644 --- a/packages/search/lib/commands/CREATE.ts +++ b/packages/search/lib/commands/CREATE.ts @@ -292,6 +292,22 @@ export interface CreateOptions { export default { NOT_KEYED_COMMAND: true, IS_READ_ONLY: true, + /** + * Creates a new search index with the given schema and options. + * @param parser - The command parser + * @param index - Name of the index to create + * @param schema - Index schema defining field names and types (TEXT, NUMERIC, GEO, TAG, VECTOR, GEOSHAPE) + * @param options - Optional parameters: + * - ON: Type of container to index (HASH or JSON) + * - PREFIX: Prefixes for document keys to index + * - FILTER: Expression that filters indexed documents + * - LANGUAGE/LANGUAGE_FIELD: Default language for indexing + * - SCORE/SCORE_FIELD: Document ranking parameters + * - MAXTEXTFIELDS: Index all text fields without specifying them + * - TEMPORARY: Create a temporary index + * - NOOFFSETS/NOHL/NOFIELDS/NOFREQS: Index optimization flags + * - STOPWORDS: Custom stopword list + */ parseCommand(parser: CommandParser, index: RedisArgument, schema: RediSearchSchema, options?: CreateOptions) { parser.push('FT.CREATE', index); diff --git a/packages/search/lib/commands/CURSOR_DEL.ts b/packages/search/lib/commands/CURSOR_DEL.ts index 5f638ebb0ee..39d0dc8af01 100644 --- a/packages/search/lib/commands/CURSOR_DEL.ts +++ b/packages/search/lib/commands/CURSOR_DEL.ts @@ -4,6 +4,12 @@ import { SimpleStringReply, Command, RedisArgument, NumberReply, UnwrapReply } f export default { NOT_KEYED_COMMAND: true, IS_READ_ONLY: true, + /** + * Deletes a cursor from an index. + * @param parser - The command parser + * @param index - The index name that contains the cursor + * @param cursorId - The cursor ID to delete + */ parseCommand(parser: CommandParser, index: RedisArgument, cursorId: UnwrapReply) { parser.push('FT.CURSOR', 'DEL', index, cursorId.toString()); }, diff --git a/packages/search/lib/commands/CURSOR_READ.ts b/packages/search/lib/commands/CURSOR_READ.ts index e64070122d1..50ee5eafbd6 100644 --- a/packages/search/lib/commands/CURSOR_READ.ts +++ b/packages/search/lib/commands/CURSOR_READ.ts @@ -9,6 +9,14 @@ export interface FtCursorReadOptions { export default { NOT_KEYED_COMMAND: true, IS_READ_ONLY: true, + /** + * Reads from an existing cursor to get more results from an index. + * @param parser - The command parser + * @param index - The index name that contains the cursor + * @param cursor - The cursor ID to read from + * @param options - Optional parameters: + * - COUNT: Maximum number of results to return + */ parseCommand(parser: CommandParser, index: RedisArgument, cursor: UnwrapReply, options?: FtCursorReadOptions) { parser.push('FT.CURSOR', 'READ', index, cursor.toString()); diff --git a/packages/search/lib/commands/DICTADD.ts b/packages/search/lib/commands/DICTADD.ts index 2106775f854..84936ff5f73 100644 --- a/packages/search/lib/commands/DICTADD.ts +++ b/packages/search/lib/commands/DICTADD.ts @@ -5,6 +5,12 @@ import { RedisVariadicArgument } from '@redis/client/dist/lib/commands/generic-t export default { NOT_KEYED_COMMAND: true, IS_READ_ONLY: true, + /** + * Adds terms to a dictionary. + * @param parser - The command parser + * @param dictionary - Name of the dictionary to add terms to + * @param term - One or more terms to add to the dictionary + */ parseCommand(parser: CommandParser, dictionary: RedisArgument, term: RedisVariadicArgument) { parser.push('FT.DICTADD', dictionary); parser.pushVariadic(term); diff --git a/packages/search/lib/commands/DICTDEL.ts b/packages/search/lib/commands/DICTDEL.ts index 988af1139e9..c39b03f45ef 100644 --- a/packages/search/lib/commands/DICTDEL.ts +++ b/packages/search/lib/commands/DICTDEL.ts @@ -5,6 +5,12 @@ import { RedisVariadicArgument } from '@redis/client/dist/lib/commands/generic-t export default { NOT_KEYED_COMMAND: true, IS_READ_ONLY: true, + /** + * Deletes terms from a dictionary. + * @param parser - The command parser + * @param dictionary - Name of the dictionary to remove terms from + * @param term - One or more terms to delete from the dictionary + */ parseCommand(parser: CommandParser, dictionary: RedisArgument, term: RedisVariadicArgument) { parser.push('FT.DICTDEL', dictionary); parser.pushVariadic(term); diff --git a/packages/search/lib/commands/DICTDUMP.ts b/packages/search/lib/commands/DICTDUMP.ts index 3c223442ecb..1ae40b4edb3 100644 --- a/packages/search/lib/commands/DICTDUMP.ts +++ b/packages/search/lib/commands/DICTDUMP.ts @@ -4,6 +4,11 @@ import { RedisArgument, ArrayReply, SetReply, BlobStringReply, Command } from '@ export default { NOT_KEYED_COMMAND: true, IS_READ_ONLY: true, + /** + * Returns all terms in a dictionary. + * @param parser - The command parser + * @param dictionary - Name of the dictionary to dump + */ parseCommand(parser: CommandParser, dictionary: RedisArgument) { parser.push('FT.DICTDUMP', dictionary); }, diff --git a/packages/search/lib/commands/DROPINDEX.ts b/packages/search/lib/commands/DROPINDEX.ts index 407bdd031aa..5b6e0dde786 100644 --- a/packages/search/lib/commands/DROPINDEX.ts +++ b/packages/search/lib/commands/DROPINDEX.ts @@ -8,6 +8,13 @@ export interface FtDropIndexOptions { export default { NOT_KEYED_COMMAND: true, IS_READ_ONLY: true, + /** + * Deletes an index and all associated documents. + * @param parser - The command parser + * @param index - Name of the index to delete + * @param options - Optional parameters: + * - DD: Also delete the indexed documents themselves + */ parseCommand(parser: CommandParser, index: RedisArgument, options?: FtDropIndexOptions) { parser.push('FT.DROPINDEX', index); diff --git a/packages/search/lib/commands/EXPLAIN.ts b/packages/search/lib/commands/EXPLAIN.ts index 39a430f4371..78d09ffeded 100644 --- a/packages/search/lib/commands/EXPLAIN.ts +++ b/packages/search/lib/commands/EXPLAIN.ts @@ -11,6 +11,15 @@ export interface FtExplainOptions { export default { NOT_KEYED_COMMAND: true, IS_READ_ONLY: true, + /** + * Returns the execution plan for a complex query. + * @param parser - The command parser + * @param index - Name of the index to explain query against + * @param query - The query string to explain + * @param options - Optional parameters: + * - PARAMS: Named parameters to use in the query + * - DIALECT: Version of query dialect to use (defaults to 1) + */ parseCommand( parser: CommandParser, index: RedisArgument, diff --git a/packages/search/lib/commands/EXPLAINCLI.ts b/packages/search/lib/commands/EXPLAINCLI.ts index 4ef5fba88d6..42e489ce10e 100644 --- a/packages/search/lib/commands/EXPLAINCLI.ts +++ b/packages/search/lib/commands/EXPLAINCLI.ts @@ -9,6 +9,14 @@ export interface FtExplainCLIOptions { export default { NOT_KEYED_COMMAND: true, IS_READ_ONLY: true, + /** + * Returns the execution plan for a complex query in a more verbose format than FT.EXPLAIN. + * @param parser - The command parser + * @param index - Name of the index to explain query against + * @param query - The query string to explain + * @param options - Optional parameters: + * - DIALECT: Version of query dialect to use (defaults to 1) + */ parseCommand( parser: CommandParser, index: RedisArgument, diff --git a/packages/search/lib/commands/INFO.ts b/packages/search/lib/commands/INFO.ts index cee6ae683cd..03cf21edfd8 100644 --- a/packages/search/lib/commands/INFO.ts +++ b/packages/search/lib/commands/INFO.ts @@ -7,6 +7,11 @@ import { TuplesReply } from '@redis/client/dist/lib/RESP/types'; export default { NOT_KEYED_COMMAND: true, IS_READ_ONLY: true, + /** + * Returns information and statistics about an index. + * @param parser - The command parser + * @param index - Name of the index to get information about + */ parseCommand(parser: CommandParser, index: RedisArgument) { parser.push('FT.INFO', index); }, diff --git a/packages/search/lib/commands/PROFILE_AGGREGATE.ts b/packages/search/lib/commands/PROFILE_AGGREGATE.ts index 94bb6984afa..99aca95a698 100644 --- a/packages/search/lib/commands/PROFILE_AGGREGATE.ts +++ b/packages/search/lib/commands/PROFILE_AGGREGATE.ts @@ -6,6 +6,15 @@ import { ProfileOptions, ProfileRawReplyResp2, ProfileReplyResp2, } from './PROF export default { NOT_KEYED_COMMAND: true, IS_READ_ONLY: true, + /** + * Profiles the execution of an aggregation query for performance analysis. + * @param parser - The command parser + * @param index - Name of the index to profile query against + * @param query - The aggregation query to profile + * @param options - Optional parameters: + * - LIMITED: Collect limited timing information only + * - All options supported by FT.AGGREGATE command + */ parseCommand( parser: CommandParser, index: string, diff --git a/packages/search/lib/commands/PROFILE_SEARCH.ts b/packages/search/lib/commands/PROFILE_SEARCH.ts index b13dbebe996..cdbb12fcdd8 100644 --- a/packages/search/lib/commands/PROFILE_SEARCH.ts +++ b/packages/search/lib/commands/PROFILE_SEARCH.ts @@ -22,6 +22,15 @@ export interface ProfileOptions { export default { NOT_KEYED_COMMAND: true, IS_READ_ONLY: true, + /** + * Profiles the execution of a search query for performance analysis. + * @param parser - The command parser + * @param index - Name of the index to profile query against + * @param query - The search query to profile + * @param options - Optional parameters: + * - LIMITED: Collect limited timing information only + * - All options supported by FT.SEARCH command + */ parseCommand( parser: CommandParser, index: RedisArgument, diff --git a/packages/search/lib/commands/SEARCH.ts b/packages/search/lib/commands/SEARCH.ts index f48ac056784..abc561dff4e 100644 --- a/packages/search/lib/commands/SEARCH.ts +++ b/packages/search/lib/commands/SEARCH.ts @@ -161,6 +161,21 @@ export function parseSearchOptions(parser: CommandParser, options?: FtSearchOpti export default { NOT_KEYED_COMMAND: true, IS_READ_ONLY: true, + /** + * Searches a RediSearch index with the given query. + * @param parser - The command parser + * @param index - The index name to search + * @param query - The text query to search. For syntax, see https://redis.io/docs/stack/search/reference/query_syntax + * @param options - Optional search parameters including: + * - VERBATIM: do not try to use stemming for query expansion + * - NOSTOPWORDS: do not filter stopwords from the query + * - INKEYS/INFIELDS: restrict the search to specific keys/fields + * - RETURN: limit which fields are returned + * - SUMMARIZE/HIGHLIGHT: create search result highlights + * - LIMIT: pagination control + * - SORTBY: sort results by a specific field + * - PARAMS: bind parameters to the query + */ parseCommand(parser: CommandParser, index: RedisArgument, query: RedisArgument, options?: FtSearchOptions) { parser.push('FT.SEARCH', index, query); diff --git a/packages/search/lib/commands/SEARCH_NOCONTENT.ts b/packages/search/lib/commands/SEARCH_NOCONTENT.ts index a6968851acd..2fcfd2b4166 100644 --- a/packages/search/lib/commands/SEARCH_NOCONTENT.ts +++ b/packages/search/lib/commands/SEARCH_NOCONTENT.ts @@ -4,6 +4,14 @@ import SEARCH, { SearchRawReply } from './SEARCH'; export default { NOT_KEYED_COMMAND: SEARCH.NOT_KEYED_COMMAND, IS_READ_ONLY: SEARCH.IS_READ_ONLY, + /** + * Performs a search query but returns only document ids without their contents. + * @param args - Same parameters as FT.SEARCH: + * - parser: The command parser + * - index: Name of the index to search + * - query: The text query to search + * - options: Optional search parameters + */ parseCommand(...args: Parameters) { SEARCH.parseCommand(...args); args[0].push('NOCONTENT'); diff --git a/packages/search/lib/commands/SPELLCHECK.ts b/packages/search/lib/commands/SPELLCHECK.ts index 3b909cdca32..d6d84b19543 100644 --- a/packages/search/lib/commands/SPELLCHECK.ts +++ b/packages/search/lib/commands/SPELLCHECK.ts @@ -16,6 +16,16 @@ export interface FtSpellCheckOptions { export default { NOT_KEYED_COMMAND: true, IS_READ_ONLY: true, + /** + * Performs spelling correction on a search query. + * @param parser - The command parser + * @param index - Name of the index to use for spelling corrections + * @param query - The search query to check for spelling + * @param options - Optional parameters: + * - DISTANCE: Maximum Levenshtein distance for spelling suggestions + * - TERMS: Custom dictionary terms to include/exclude + * - DIALECT: Version of query dialect to use (defaults to 1) + */ parseCommand(parser: CommandParser, index: RedisArgument, query: RedisArgument, options?: FtSpellCheckOptions) { parser.push('FT.SPELLCHECK', index, query); diff --git a/packages/search/lib/commands/SUGADD.ts b/packages/search/lib/commands/SUGADD.ts index 34e5bccb7f1..3fa592a2733 100644 --- a/packages/search/lib/commands/SUGADD.ts +++ b/packages/search/lib/commands/SUGADD.ts @@ -8,6 +8,16 @@ export interface FtSugAddOptions { export default { IS_READ_ONLY: true, + /** + * Adds a suggestion string to an auto-complete suggestion dictionary. + * @param parser - The command parser + * @param key - The suggestion dictionary key + * @param string - The suggestion string to add + * @param score - The suggestion score used for sorting + * @param options - Optional parameters: + * - INCR: If true, increment the existing entry's score + * - PAYLOAD: Optional payload to associate with the suggestion + */ parseCommand(parser: CommandParser, key: RedisArgument, string: RedisArgument, score: number, options?: FtSugAddOptions) { parser.push('FT.SUGADD'); parser.pushKey(key); diff --git a/packages/search/lib/commands/SUGDEL.ts b/packages/search/lib/commands/SUGDEL.ts index 6bc99456d2e..852b33f5c52 100644 --- a/packages/search/lib/commands/SUGDEL.ts +++ b/packages/search/lib/commands/SUGDEL.ts @@ -3,6 +3,12 @@ import { RedisArgument, NumberReply, Command } from '@redis/client/dist/lib/RESP export default { IS_READ_ONLY: true, + /** + * Deletes a string from a suggestion dictionary. + * @param parser - The command parser + * @param key - The suggestion dictionary key + * @param string - The suggestion string to delete + */ parseCommand(parser: CommandParser, key: RedisArgument, string: RedisArgument) { parser.push('FT.SUGDEL'); parser.pushKey(key); diff --git a/packages/search/lib/commands/SUGGET.ts b/packages/search/lib/commands/SUGGET.ts index e8a3aecdab0..6c463a020e2 100644 --- a/packages/search/lib/commands/SUGGET.ts +++ b/packages/search/lib/commands/SUGGET.ts @@ -8,6 +8,15 @@ export interface FtSugGetOptions { export default { IS_READ_ONLY: true, + /** + * Gets completion suggestions for a prefix from a suggestion dictionary. + * @param parser - The command parser + * @param key - The suggestion dictionary key + * @param prefix - The prefix to get completion suggestions for + * @param options - Optional parameters: + * - FUZZY: Enable fuzzy prefix matching + * - MAX: Maximum number of results to return + */ parseCommand(parser: CommandParser, key: RedisArgument, prefix: RedisArgument, options?: FtSugGetOptions) { parser.push('FT.SUGGET'); parser.pushKey(key); diff --git a/packages/search/lib/commands/SUGGET_WITHPAYLOADS.ts b/packages/search/lib/commands/SUGGET_WITHPAYLOADS.ts index 60bf5ee86d9..a83279be0ff 100644 --- a/packages/search/lib/commands/SUGGET_WITHPAYLOADS.ts +++ b/packages/search/lib/commands/SUGGET_WITHPAYLOADS.ts @@ -4,6 +4,14 @@ import SUGGET from './SUGGET'; export default { IS_READ_ONLY: SUGGET.IS_READ_ONLY, + /** + * Gets completion suggestions with their payloads from a suggestion dictionary. + * @param args - Same parameters as FT.SUGGET: + * - parser: The command parser + * - key: The suggestion dictionary key + * - prefix: The prefix to get completion suggestions for + * - options: Optional parameters for fuzzy matching and max results + */ parseCommand(...args: Parameters) { SUGGET.parseCommand(...args); args[0].push('WITHPAYLOADS'); diff --git a/packages/search/lib/commands/SUGGET_WITHSCORES.ts b/packages/search/lib/commands/SUGGET_WITHSCORES.ts index 060e59132db..5c0a3fba2a3 100644 --- a/packages/search/lib/commands/SUGGET_WITHSCORES.ts +++ b/packages/search/lib/commands/SUGGET_WITHSCORES.ts @@ -9,6 +9,14 @@ type SuggestScore = { export default { IS_READ_ONLY: SUGGET.IS_READ_ONLY, + /** + * Gets completion suggestions with their scores from a suggestion dictionary. + * @param args - Same parameters as FT.SUGGET: + * - parser: The command parser + * - key: The suggestion dictionary key + * - prefix: The prefix to get completion suggestions for + * - options: Optional parameters for fuzzy matching and max results + */ parseCommand(...args: Parameters) { SUGGET.parseCommand(...args); args[0].push('WITHSCORES'); diff --git a/packages/search/lib/commands/SUGGET_WITHSCORES_WITHPAYLOADS.ts b/packages/search/lib/commands/SUGGET_WITHSCORES_WITHPAYLOADS.ts index 07277420338..b7aa38df3fe 100644 --- a/packages/search/lib/commands/SUGGET_WITHSCORES_WITHPAYLOADS.ts +++ b/packages/search/lib/commands/SUGGET_WITHSCORES_WITHPAYLOADS.ts @@ -10,6 +10,14 @@ type SuggestScoreWithPayload = { export default { IS_READ_ONLY: SUGGET.IS_READ_ONLY, + /** + * Gets completion suggestions with their scores and payloads from a suggestion dictionary. + * @param args - Same parameters as FT.SUGGET: + * - parser: The command parser + * - key: The suggestion dictionary key + * - prefix: The prefix to get completion suggestions for + * - options: Optional parameters for fuzzy matching and max results + */ parseCommand(...args: Parameters) { SUGGET.parseCommand(...args); args[0].push( diff --git a/packages/search/lib/commands/SUGLEN.ts b/packages/search/lib/commands/SUGLEN.ts index a3f0fbe45ed..ecc4f4a6fc0 100644 --- a/packages/search/lib/commands/SUGLEN.ts +++ b/packages/search/lib/commands/SUGLEN.ts @@ -3,6 +3,11 @@ import { RedisArgument, NumberReply, Command } from '@redis/client/dist/lib/RESP export default { IS_READ_ONLY: true, + /** + * Gets the size of a suggestion dictionary. + * @param parser - The command parser + * @param key - The suggestion dictionary key + */ parseCommand(parser: CommandParser, key: RedisArgument) { parser.push('FT.SUGLEN', key); }, diff --git a/packages/search/lib/commands/SYNDUMP.ts b/packages/search/lib/commands/SYNDUMP.ts index 5f454f96fe0..da3e77b4223 100644 --- a/packages/search/lib/commands/SYNDUMP.ts +++ b/packages/search/lib/commands/SYNDUMP.ts @@ -4,6 +4,11 @@ import { RedisArgument, MapReply, BlobStringReply, ArrayReply, UnwrapReply, Comm export default { NOT_KEYED_COMMAND: true, IS_READ_ONLY: true, + /** + * Dumps the contents of a synonym group. + * @param parser - The command parser + * @param index - Name of the index that contains the synonym group + */ parseCommand(parser: CommandParser, index: RedisArgument) { parser.push('FT.SYNDUMP', index); }, diff --git a/packages/search/lib/commands/SYNUPDATE.ts b/packages/search/lib/commands/SYNUPDATE.ts index 3af735412ae..0fed14f894a 100644 --- a/packages/search/lib/commands/SYNUPDATE.ts +++ b/packages/search/lib/commands/SYNUPDATE.ts @@ -9,6 +9,15 @@ export interface FtSynUpdateOptions { export default { NOT_KEYED_COMMAND: true, IS_READ_ONLY: true, + /** + * Updates a synonym group with new terms. + * @param parser - The command parser + * @param index - Name of the index that contains the synonym group + * @param groupId - ID of the synonym group to update + * @param terms - One or more synonym terms to add to the group + * @param options - Optional parameters: + * - SKIPINITIALSCAN: Skip the initial scan for existing documents + */ parseCommand( parser: CommandParser, index: RedisArgument, diff --git a/packages/search/lib/commands/TAGVALS.ts b/packages/search/lib/commands/TAGVALS.ts index 0afddb247fd..1c307945f2b 100644 --- a/packages/search/lib/commands/TAGVALS.ts +++ b/packages/search/lib/commands/TAGVALS.ts @@ -4,6 +4,12 @@ import { RedisArgument, ArrayReply, SetReply, BlobStringReply, Command } from '@ export default { NOT_KEYED_COMMAND: true, IS_READ_ONLY: true, + /** + * Returns the distinct values in a TAG field. + * @param parser - The command parser + * @param index - Name of the index + * @param fieldName - Name of the TAG field to get values from + */ parseCommand(parser: CommandParser, index: RedisArgument, fieldName: RedisArgument) { parser.push('FT.TAGVALS', index, fieldName); }, diff --git a/packages/search/lib/commands/_LIST.ts b/packages/search/lib/commands/_LIST.ts index c1ca8cc2ee5..1b30e044e61 100644 --- a/packages/search/lib/commands/_LIST.ts +++ b/packages/search/lib/commands/_LIST.ts @@ -4,6 +4,10 @@ import { ArrayReply, SetReply, BlobStringReply, Command } from '@redis/client/di export default { NOT_KEYED_COMMAND: true, IS_READ_ONLY: true, + /** + * Lists all existing indexes in the database. + * @param parser - The command parser + */ parseCommand(parser: CommandParser) { parser.push('FT._LIST'); }, diff --git a/packages/time-series/lib/commands/ADD.ts b/packages/time-series/lib/commands/ADD.ts index 0f254339ff9..78a9247c41e 100644 --- a/packages/time-series/lib/commands/ADD.ts +++ b/packages/time-series/lib/commands/ADD.ts @@ -29,6 +29,14 @@ export interface TsAddOptions { export default { IS_READ_ONLY: false, + /** + * Creates or appends a sample to a time series + * @param parser - The command parser + * @param key - The key name for the time series + * @param timestamp - The timestamp of the sample + * @param value - The value of the sample + * @param options - Optional configuration parameters + */ parseCommand( parser: CommandParser, key: RedisArgument, diff --git a/packages/time-series/lib/commands/ALTER.ts b/packages/time-series/lib/commands/ALTER.ts index 29f99290a52..613539b4861 100644 --- a/packages/time-series/lib/commands/ALTER.ts +++ b/packages/time-series/lib/commands/ALTER.ts @@ -8,6 +8,12 @@ export type TsAlterOptions = Pick) { const parser = args[0]; diff --git a/packages/time-series/lib/commands/DEL.ts b/packages/time-series/lib/commands/DEL.ts index de9cadf88c9..1e0e01164fd 100644 --- a/packages/time-series/lib/commands/DEL.ts +++ b/packages/time-series/lib/commands/DEL.ts @@ -4,6 +4,13 @@ import { RedisArgument, NumberReply, Command, } from '@redis/client/dist/lib/RES export default { IS_READ_ONLY: false, + /** + * Deletes samples between two timestamps from a time series + * @param parser - The command parser + * @param key - The key name of the time series + * @param fromTimestamp - Start timestamp to delete from + * @param toTimestamp - End timestamp to delete until + */ parseCommand(parser: CommandParser, key: RedisArgument, fromTimestamp: Timestamp, toTimestamp: Timestamp) { parser.push('TS.DEL'); parser.pushKey(key); diff --git a/packages/time-series/lib/commands/DELETERULE.ts b/packages/time-series/lib/commands/DELETERULE.ts index b4e47a0fba6..8897f666b7b 100644 --- a/packages/time-series/lib/commands/DELETERULE.ts +++ b/packages/time-series/lib/commands/DELETERULE.ts @@ -3,6 +3,12 @@ import { RedisArgument, SimpleStringReply, Command } from '@redis/client/dist/li export default { IS_READ_ONLY: false, + /** + * Deletes a compaction rule between source and destination time series + * @param parser - The command parser + * @param sourceKey - The source time series key + * @param destinationKey - The destination time series key + */ parseCommand(parser: CommandParser, sourceKey: RedisArgument, destinationKey: RedisArgument) { parser.push('TS.DELETERULE'); parser.pushKeys([sourceKey, destinationKey]); diff --git a/packages/time-series/lib/commands/GET.ts b/packages/time-series/lib/commands/GET.ts index c1bb2c1c749..9462705c02c 100644 --- a/packages/time-series/lib/commands/GET.ts +++ b/packages/time-series/lib/commands/GET.ts @@ -9,6 +9,12 @@ export type TsGetReply = TuplesReply<[]> | TuplesReply<[NumberReply, DoubleReply export default { IS_READ_ONLY: true, + /** + * Gets the last sample of a time series + * @param parser - The command parser + * @param key - The key name of the time series + * @param options - Optional parameters for the command + */ parseCommand(parser: CommandParser, key: RedisArgument, options?: TsGetOptions) { parser.push('TS.GET'); parser.pushKey(key); diff --git a/packages/time-series/lib/commands/INCRBY.ts b/packages/time-series/lib/commands/INCRBY.ts index 2365f716a83..41f11b0d7f5 100644 --- a/packages/time-series/lib/commands/INCRBY.ts +++ b/packages/time-series/lib/commands/INCRBY.ts @@ -12,6 +12,13 @@ export interface TsIncrByOptions { IGNORE?: TsIgnoreOptions; } +/** + * Parses arguments for incrementing a time series value + * @param parser - The command parser + * @param key - The key name of the time series + * @param value - The value to increment by + * @param options - Optional parameters for the command + */ export function parseIncrByArguments( parser: CommandParser, key: RedisArgument, @@ -40,6 +47,10 @@ export function parseIncrByArguments( export default { IS_READ_ONLY: false, + /** + * Increases the value of a time series by a given amount + * @param args - Arguments passed to the {@link parseIncrByArguments} function + */ parseCommand(...args: Parameters) { const parser = args[0]; diff --git a/packages/time-series/lib/commands/INFO.ts b/packages/time-series/lib/commands/INFO.ts index 62cc1108a80..2e908a9d32d 100644 --- a/packages/time-series/lib/commands/INFO.ts +++ b/packages/time-series/lib/commands/INFO.ts @@ -73,6 +73,11 @@ export interface InfoReply { export default { IS_READ_ONLY: true, + /** + * Gets information about a time series + * @param parser - The command parser + * @param key - The key name of the time series + */ parseCommand(parser: CommandParser, key: string) { parser.push('TS.INFO'); parser.pushKey(key); diff --git a/packages/time-series/lib/commands/INFO_DEBUG.ts b/packages/time-series/lib/commands/INFO_DEBUG.ts index 89d66a36ef8..bbdee4924ff 100644 --- a/packages/time-series/lib/commands/INFO_DEBUG.ts +++ b/packages/time-series/lib/commands/INFO_DEBUG.ts @@ -38,6 +38,11 @@ export interface InfoDebugReply extends InfoReply { export default { IS_READ_ONLY: INFO.IS_READ_ONLY, + /** + * Gets debug information about a time series + * @param parser - The command parser + * @param key - The key name of the time series + */ parseCommand(parser: CommandParser, key: string) { INFO.parseCommand(parser, key); parser.push('DEBUG'); diff --git a/packages/time-series/lib/commands/MADD.ts b/packages/time-series/lib/commands/MADD.ts index b4c91a98384..d0b36ea373f 100644 --- a/packages/time-series/lib/commands/MADD.ts +++ b/packages/time-series/lib/commands/MADD.ts @@ -10,6 +10,11 @@ export interface TsMAddSample { export default { IS_READ_ONLY: false, + /** + * Adds multiple samples to multiple time series + * @param parser - The command parser + * @param toAdd - Array of samples to add to different time series + */ parseCommand(parser: CommandParser, toAdd: Array) { parser.push('TS.MADD'); diff --git a/packages/time-series/lib/commands/MGET.ts b/packages/time-series/lib/commands/MGET.ts index fd5e8c71b93..023c0bda2d4 100644 --- a/packages/time-series/lib/commands/MGET.ts +++ b/packages/time-series/lib/commands/MGET.ts @@ -7,12 +7,22 @@ export interface TsMGetOptions { LATEST?: boolean; } +/** + * Adds LATEST argument to command if specified + * @param parser - The command parser + * @param latest - Whether to include the LATEST argument + */ export function parseLatestArgument(parser: CommandParser, latest?: boolean) { if (latest) { parser.push('LATEST'); } } +/** + * Adds FILTER argument to command + * @param parser - The command parser + * @param filter - Filter to match time series keys + */ export function parseFilterArgument(parser: CommandParser, filter: RedisVariadicArgument) { parser.push('FILTER'); parser.pushVariadic(filter); @@ -37,6 +47,12 @@ export type MGetRawReply3 = MapReply< export default { NOT_KEYED_COMMAND: true, IS_READ_ONLY: true, + /** + * Gets the last samples matching a specific filter from multiple time series + * @param parser - The command parser + * @param filter - Filter to match time series keys + * @param options - Optional parameters for the command + */ parseCommand(parser: CommandParser, filter: RedisVariadicArgument, options?: TsMGetOptions) { parser.push('TS.MGET'); parseLatestArgument(parser, options?.LATEST); diff --git a/packages/time-series/lib/commands/MGET_SELECTED_LABELS.ts b/packages/time-series/lib/commands/MGET_SELECTED_LABELS.ts index d74d073c174..a13fcbeaa56 100644 --- a/packages/time-series/lib/commands/MGET_SELECTED_LABELS.ts +++ b/packages/time-series/lib/commands/MGET_SELECTED_LABELS.ts @@ -7,6 +7,13 @@ import { createTransformMGetLabelsReply } from './MGET_WITHLABELS'; export default { IS_READ_ONLY: true, + /** + * Gets the last samples matching a specific filter with selected labels + * @param parser - The command parser + * @param filter - Filter to match time series keys + * @param selectedLabels - Labels to include in the output + * @param options - Optional parameters for the command + */ parseCommand(parser: CommandParser, filter: RedisVariadicArgument, selectedLabels: RedisVariadicArgument, options?: TsMGetOptions) { parser.push('TS.MGET'); parseLatestArgument(parser, options?.LATEST); diff --git a/packages/time-series/lib/commands/MGET_WITHLABELS.ts b/packages/time-series/lib/commands/MGET_WITHLABELS.ts index 737e7236130..aa9b5687eec 100644 --- a/packages/time-series/lib/commands/MGET_WITHLABELS.ts +++ b/packages/time-series/lib/commands/MGET_WITHLABELS.ts @@ -52,6 +52,12 @@ export function createTransformMGetLabelsReply() { export default { IS_READ_ONLY: true, + /** + * Gets the last samples matching a specific filter with labels + * @param parser - The command parser + * @param filter - Filter to match time series keys + * @param options - Optional parameters for the command + */ parseCommand(parser: CommandParser, filter: RedisVariadicArgument, options?: TsMGetWithLabelsOptions) { parser.push('TS.MGET'); parseLatestArgument(parser, options?.LATEST); diff --git a/packages/time-series/lib/commands/MRANGE.ts b/packages/time-series/lib/commands/MRANGE.ts index 3351b755499..8b9ec66e6e3 100644 --- a/packages/time-series/lib/commands/MRANGE.ts +++ b/packages/time-series/lib/commands/MRANGE.ts @@ -22,6 +22,10 @@ export type TsMRangeRawReply3 = MapReply< ]> >; +/** + * Creates a function that parses arguments for multi-range commands + * @param command - The command name to use (TS.MRANGE or TS.MREVRANGE) + */ export function createTransformMRangeArguments(command: RedisArgument) { return ( parser: CommandParser, @@ -45,6 +49,14 @@ export function createTransformMRangeArguments(command: RedisArgument) { export default { NOT_KEYED_COMMAND: true, IS_READ_ONLY: true, + /** + * Gets samples for time series matching a specific filter within a time range + * @param parser - The command parser + * @param fromTimestamp - Start timestamp for range + * @param toTimestamp - End timestamp for range + * @param filter - Filter to match time series keys + * @param options - Optional parameters for the command + */ parseCommand: createTransformMRangeArguments('TS.MRANGE'), transformReply: { 2(reply: TsMRangeRawReply2, _?: any, typeMapping?: TypeMapping) { diff --git a/packages/time-series/lib/commands/MRANGE_GROUPBY.ts b/packages/time-series/lib/commands/MRANGE_GROUPBY.ts index 74279d00b6d..dc049276127 100644 --- a/packages/time-series/lib/commands/MRANGE_GROUPBY.ts +++ b/packages/time-series/lib/commands/MRANGE_GROUPBY.ts @@ -25,6 +25,11 @@ export interface TsMRangeGroupBy { REDUCE: TimeSeriesReducer; } +/** + * Adds GROUPBY arguments to command + * @param parser - The command parser + * @param groupBy - Group by parameters + */ export function parseGroupByArguments(parser: CommandParser, groupBy: TsMRangeGroupBy) { parser.push('GROUPBY', groupBy.label, 'REDUCE', groupBy.REDUCE); } @@ -51,6 +56,10 @@ export type TsMRangeGroupByRawReply3 = MapReply< ]> >; +/** + * Creates a function that parses arguments for multi-range commands with grouping + * @param command - The command name to use (TS.MRANGE or TS.MREVRANGE) + */ export function createTransformMRangeGroupByArguments(command: RedisArgument) { return ( parser: CommandParser, @@ -69,6 +78,10 @@ export function createTransformMRangeGroupByArguments(command: RedisArgument) { }; } +/** + * Extracts source keys from RESP3 metadata reply + * @param raw - Raw metadata from RESP3 reply + */ export function extractResp3MRangeSources(raw: TsMRangeGroupByRawMetadataReply3) { const unwrappedMetadata2 = raw as unknown as UnwrapReply; if (unwrappedMetadata2 instanceof Map) { @@ -82,6 +95,15 @@ export function extractResp3MRangeSources(raw: TsMRangeGroupByRawMetadataReply3) export default { IS_READ_ONLY: true, + /** + * Gets samples for time series matching a filter within a time range with grouping + * @param parser - The command parser + * @param fromTimestamp - Start timestamp for range + * @param toTimestamp - End timestamp for range + * @param filter - Filter to match time series keys + * @param groupBy - Group by parameters + * @param options - Optional parameters for the command + */ parseCommand: createTransformMRangeGroupByArguments('TS.MRANGE'), transformReply: { 2(reply: TsMRangeGroupByRawReply2, _?: any, typeMapping?: TypeMapping) { diff --git a/packages/time-series/lib/commands/MRANGE_SELECTED_LABELS.ts b/packages/time-series/lib/commands/MRANGE_SELECTED_LABELS.ts index 75affc54aeb..c9b737fd290 100644 --- a/packages/time-series/lib/commands/MRANGE_SELECTED_LABELS.ts +++ b/packages/time-series/lib/commands/MRANGE_SELECTED_LABELS.ts @@ -25,6 +25,10 @@ export type TsMRangeSelectedLabelsRawReply3 = MapReply< ]> >; +/** + * Creates a function that parses arguments for multi-range commands with selected labels + * @param command - The command name to use (TS.MRANGE or TS.MREVRANGE) + */ export function createTransformMRangeSelectedLabelsArguments(command: RedisArgument) { return ( parser: CommandParser, @@ -50,6 +54,15 @@ export function createTransformMRangeSelectedLabelsArguments(command: RedisArgum export default { IS_READ_ONLY: true, + /** + * Gets samples for time series matching a filter with selected labels + * @param parser - The command parser + * @param fromTimestamp - Start timestamp for range + * @param toTimestamp - End timestamp for range + * @param selectedLabels - Labels to include in the output + * @param filter - Filter to match time series keys + * @param options - Optional parameters for the command + */ parseCommand: createTransformMRangeSelectedLabelsArguments('TS.MRANGE'), transformReply: { 2(reply: TsMRangeSelectedLabelsRawReply2, _?: any, typeMapping?: TypeMapping) { diff --git a/packages/time-series/lib/commands/MRANGE_SELECTED_LABELS_GROUPBY.ts b/packages/time-series/lib/commands/MRANGE_SELECTED_LABELS_GROUPBY.ts index 99429a9bb76..d2f94b82bb3 100644 --- a/packages/time-series/lib/commands/MRANGE_SELECTED_LABELS_GROUPBY.ts +++ b/packages/time-series/lib/commands/MRANGE_SELECTED_LABELS_GROUPBY.ts @@ -17,6 +17,10 @@ export type TsMRangeWithLabelsGroupByRawReply3 = MapReply< ]> >; +/** + * Creates a function that parses arguments for multi-range commands with selected labels and grouping + * @param command - The command name to use (TS.MRANGE or TS.MREVRANGE) + */ export function createMRangeSelectedLabelsGroupByTransformArguments( command: RedisArgument ) { @@ -47,6 +51,16 @@ export function createMRangeSelectedLabelsGroupByTransformArguments( export default { IS_READ_ONLY: true, + /** + * Gets samples for time series matching a filter with selected labels and grouping + * @param parser - The command parser + * @param fromTimestamp - Start timestamp for range + * @param toTimestamp - End timestamp for range + * @param selectedLabels - Labels to include in the output + * @param filter - Filter to match time series keys + * @param groupBy - Group by parameters + * @param options - Optional parameters for the command + */ parseCommand: createMRangeSelectedLabelsGroupByTransformArguments('TS.MRANGE'), transformReply: { 2: MRANGE_SELECTED_LABELS.transformReply[2], diff --git a/packages/time-series/lib/commands/MRANGE_WITHLABELS.ts b/packages/time-series/lib/commands/MRANGE_WITHLABELS.ts index ef4864a0307..01a3634cf4c 100644 --- a/packages/time-series/lib/commands/MRANGE_WITHLABELS.ts +++ b/packages/time-series/lib/commands/MRANGE_WITHLABELS.ts @@ -25,6 +25,10 @@ export type TsMRangeWithLabelsRawReply3 = MapReply< ]> >; +/** + * Creates a function that parses arguments for multi-range commands with labels + * @param command - The command name to use (TS.MRANGE or TS.MREVRANGE) + */ export function createTransformMRangeWithLabelsArguments(command: RedisArgument) { return ( parser: CommandParser, @@ -50,6 +54,14 @@ export function createTransformMRangeWithLabelsArguments(command: RedisArgument) export default { NOT_KEYED_COMMAND: true, IS_READ_ONLY: true, + /** + * Gets samples for time series matching a filter with labels + * @param parser - The command parser + * @param fromTimestamp - Start timestamp for range + * @param toTimestamp - End timestamp for range + * @param filter - Filter to match time series keys + * @param options - Optional parameters for the command + */ parseCommand: createTransformMRangeWithLabelsArguments('TS.MRANGE'), transformReply: { 2(reply: TsMRangeWithLabelsRawReply2, _?: any, typeMapping?: TypeMapping) { diff --git a/packages/time-series/lib/commands/MRANGE_WITHLABELS_GROUPBY.ts b/packages/time-series/lib/commands/MRANGE_WITHLABELS_GROUPBY.ts index 6552f6328e8..08c70000f70 100644 --- a/packages/time-series/lib/commands/MRANGE_WITHLABELS_GROUPBY.ts +++ b/packages/time-series/lib/commands/MRANGE_WITHLABELS_GROUPBY.ts @@ -54,6 +54,15 @@ export function createMRangeWithLabelsGroupByTransformArguments(command: RedisAr export default { IS_READ_ONLY: true, + /** + * Gets samples for time series matching a filter with labels and grouping + * @param parser - The command parser + * @param fromTimestamp - Start timestamp for range + * @param toTimestamp - End timestamp for range + * @param filter - Filter to match time series keys + * @param groupBy - Group by parameters + * @param options - Optional parameters for the command + */ parseCommand: createMRangeWithLabelsGroupByTransformArguments('TS.MRANGE'), transformReply: { 2(reply: TsMRangeWithLabelsGroupByRawReply2, _?: any, typeMapping?: TypeMapping) { diff --git a/packages/time-series/lib/commands/MREVRANGE.ts b/packages/time-series/lib/commands/MREVRANGE.ts index 99d3123dd27..54bd5ca9cca 100644 --- a/packages/time-series/lib/commands/MREVRANGE.ts +++ b/packages/time-series/lib/commands/MREVRANGE.ts @@ -4,6 +4,14 @@ import MRANGE, { createTransformMRangeArguments } from './MRANGE'; export default { NOT_KEYED_COMMAND: MRANGE.NOT_KEYED_COMMAND, IS_READ_ONLY: MRANGE.IS_READ_ONLY, + /** + * Gets samples for time series matching a specific filter within a time range (in reverse order) + * @param parser - The command parser + * @param fromTimestamp - Start timestamp for range + * @param toTimestamp - End timestamp for range + * @param filter - Filter to match time series keys + * @param options - Optional parameters for the command + */ parseCommand: createTransformMRangeArguments('TS.MREVRANGE'), transformReply: MRANGE.transformReply, } as const satisfies Command; diff --git a/packages/time-series/lib/commands/MREVRANGE_GROUPBY.ts b/packages/time-series/lib/commands/MREVRANGE_GROUPBY.ts index 4afcd113505..329d9cceb8e 100644 --- a/packages/time-series/lib/commands/MREVRANGE_GROUPBY.ts +++ b/packages/time-series/lib/commands/MREVRANGE_GROUPBY.ts @@ -3,6 +3,15 @@ import MRANGE_GROUPBY, { createTransformMRangeGroupByArguments } from './MRANGE_ export default { IS_READ_ONLY: MRANGE_GROUPBY.IS_READ_ONLY, + /** + * Gets samples for time series matching a filter within a time range with grouping (in reverse order) + * @param parser - The command parser + * @param fromTimestamp - Start timestamp for range + * @param toTimestamp - End timestamp for range + * @param filter - Filter to match time series keys + * @param groupBy - Group by parameters + * @param options - Optional parameters for the command + */ parseCommand: createTransformMRangeGroupByArguments('TS.MREVRANGE'), transformReply: MRANGE_GROUPBY.transformReply, } as const satisfies Command; diff --git a/packages/time-series/lib/commands/MREVRANGE_SELECTED_LABELS.ts b/packages/time-series/lib/commands/MREVRANGE_SELECTED_LABELS.ts index 10e00fc7a29..15dc9d87daa 100644 --- a/packages/time-series/lib/commands/MREVRANGE_SELECTED_LABELS.ts +++ b/packages/time-series/lib/commands/MREVRANGE_SELECTED_LABELS.ts @@ -3,6 +3,15 @@ import MRANGE_SELECTED_LABELS, { createTransformMRangeSelectedLabelsArguments } export default { IS_READ_ONLY: MRANGE_SELECTED_LABELS.IS_READ_ONLY, + /** + * Gets samples for time series matching a filter with selected labels (in reverse order) + * @param parser - The command parser + * @param fromTimestamp - Start timestamp for range + * @param toTimestamp - End timestamp for range + * @param selectedLabels - Labels to include in the output + * @param filter - Filter to match time series keys + * @param options - Optional parameters for the command + */ parseCommand: createTransformMRangeSelectedLabelsArguments('TS.MREVRANGE'), transformReply: MRANGE_SELECTED_LABELS.transformReply, } as const satisfies Command; diff --git a/packages/time-series/lib/commands/MREVRANGE_SELECTED_LABELS_GROUPBY.ts b/packages/time-series/lib/commands/MREVRANGE_SELECTED_LABELS_GROUPBY.ts index b000c04c183..c044a9ca064 100644 --- a/packages/time-series/lib/commands/MREVRANGE_SELECTED_LABELS_GROUPBY.ts +++ b/packages/time-series/lib/commands/MREVRANGE_SELECTED_LABELS_GROUPBY.ts @@ -3,6 +3,16 @@ import MRANGE_SELECTED_LABELS_GROUPBY, { createMRangeSelectedLabelsGroupByTransf export default { IS_READ_ONLY: MRANGE_SELECTED_LABELS_GROUPBY.IS_READ_ONLY, + /** + * Gets samples for time series matching a filter with selected labels and grouping (in reverse order) + * @param parser - The command parser + * @param fromTimestamp - Start timestamp for range + * @param toTimestamp - End timestamp for range + * @param selectedLabels - Labels to include in the output + * @param filter - Filter to match time series keys + * @param groupBy - Group by parameters + * @param options - Optional parameters for the command + */ parseCommand: createMRangeSelectedLabelsGroupByTransformArguments('TS.MREVRANGE'), transformReply: MRANGE_SELECTED_LABELS_GROUPBY.transformReply, } as const satisfies Command; diff --git a/packages/time-series/lib/commands/MREVRANGE_WITHLABELS.ts b/packages/time-series/lib/commands/MREVRANGE_WITHLABELS.ts index 6cde143c422..0a05ab2c985 100644 --- a/packages/time-series/lib/commands/MREVRANGE_WITHLABELS.ts +++ b/packages/time-series/lib/commands/MREVRANGE_WITHLABELS.ts @@ -4,6 +4,14 @@ import MRANGE_WITHLABELS, { createTransformMRangeWithLabelsArguments } from './M export default { NOT_KEYED_COMMAND: MRANGE_WITHLABELS.NOT_KEYED_COMMAND, IS_READ_ONLY: MRANGE_WITHLABELS.IS_READ_ONLY, + /** + * Gets samples for time series matching a filter with labels (in reverse order) + * @param parser - The command parser + * @param fromTimestamp - Start timestamp for range + * @param toTimestamp - End timestamp for range + * @param filter - Filter to match time series keys + * @param options - Optional parameters for the command + */ parseCommand: createTransformMRangeWithLabelsArguments('TS.MREVRANGE'), transformReply: MRANGE_WITHLABELS.transformReply, } as const satisfies Command; diff --git a/packages/time-series/lib/commands/MREVRANGE_WITHLABELS_GROUPBY.ts b/packages/time-series/lib/commands/MREVRANGE_WITHLABELS_GROUPBY.ts index 4727112b974..e5c62898951 100644 --- a/packages/time-series/lib/commands/MREVRANGE_WITHLABELS_GROUPBY.ts +++ b/packages/time-series/lib/commands/MREVRANGE_WITHLABELS_GROUPBY.ts @@ -3,6 +3,15 @@ import MRANGE_WITHLABELS_GROUPBY, { createMRangeWithLabelsGroupByTransformArgume export default { IS_READ_ONLY: MRANGE_WITHLABELS_GROUPBY.IS_READ_ONLY, + /** + * Gets samples for time series matching a filter with labels and grouping (in reverse order) + * @param parser - The command parser + * @param fromTimestamp - Start timestamp for range + * @param toTimestamp - End timestamp for range + * @param filter - Filter to match time series keys + * @param groupBy - Group by parameters + * @param options - Optional parameters for the command + */ parseCommand: createMRangeWithLabelsGroupByTransformArguments('TS.MREVRANGE'), transformReply: MRANGE_WITHLABELS_GROUPBY.transformReply, } as const satisfies Command; diff --git a/packages/time-series/lib/commands/QUERYINDEX.ts b/packages/time-series/lib/commands/QUERYINDEX.ts index 1b53e84b7a3..158a7341c8a 100644 --- a/packages/time-series/lib/commands/QUERYINDEX.ts +++ b/packages/time-series/lib/commands/QUERYINDEX.ts @@ -5,6 +5,11 @@ import { RedisVariadicArgument } from '@redis/client/dist/lib/commands/generic-t export default { NOT_KEYED_COMMAND: true, IS_READ_ONLY: true, + /** + * Queries the index for time series matching a specific filter + * @param parser - The command parser + * @param filter - Filter to match time series labels + */ parseCommand(parser: CommandParser, filter: RedisVariadicArgument) { parser.push('TS.QUERYINDEX'); parser.pushVariadic(filter); diff --git a/packages/time-series/lib/commands/RANGE.ts b/packages/time-series/lib/commands/RANGE.ts index 44da30d81de..03d58d012ca 100644 --- a/packages/time-series/lib/commands/RANGE.ts +++ b/packages/time-series/lib/commands/RANGE.ts @@ -101,6 +101,10 @@ export function transformRangeArguments( export default { IS_READ_ONLY: true, + /** + * Gets samples from a time series within a time range + * @param args - Arguments passed to the {@link transformRangeArguments} function + */ parseCommand(...args: Parameters) { const parser = args[0]; diff --git a/packages/time-series/lib/commands/REVRANGE.ts b/packages/time-series/lib/commands/REVRANGE.ts index 238b2ce9fe7..27389b896c3 100644 --- a/packages/time-series/lib/commands/REVRANGE.ts +++ b/packages/time-series/lib/commands/REVRANGE.ts @@ -3,6 +3,10 @@ import RANGE, { transformRangeArguments } from './RANGE'; export default { IS_READ_ONLY: RANGE.IS_READ_ONLY, + /** + * Gets samples from a time series within a time range (in reverse order) + * @param args - Arguments passed to the {@link transformRangeArguments} function + */ parseCommand(...args: Parameters) { const parser = args[0]; From b33a662e501fcea56aa27d7e90ff604b9f965183 Mon Sep 17 00:00:00 2001 From: Nikolay Karadzhov Date: Wed, 4 Jun 2025 10:52:14 +0300 Subject: [PATCH 1616/1748] Automate release (#2977) * release-it/bumper * remove git:false * fix package ordering * adjust git add * fix git config * adjust git config for all packages * add noop release script for test-utils * no need to try to release root * better way to handle skipping * pass parameters down * better version hint * update node version * return git arguments from before * rename release workflow * rename workflow * set git.tagMatch * add link to docs * update description * update workspace order in package-lock * fix secondary releases release-it/bumper was removing the ^ before the peerDep to client npm is not happy with that. one potential fix would be to bump all packages together as a prestep and then proceed without bupming again. for now, this fix should bring us to the previous state ( what was used in the manual process ) * require clean working dir in root * remove root release-it config not needed --- .github/workflows/release.yml | 50 + package-lock.json | 4101 +++++++++---------------- package.json | 19 +- packages/bloom/.release-it.json | 19 +- packages/bloom/package.json | 3 +- packages/client/.release-it.json | 12 +- packages/client/package.json | 3 +- packages/entraid/.release-it.json | 19 +- packages/entraid/package.json | 3 +- packages/json/.release-it.json | 19 +- packages/json/package.json | 3 +- packages/redis/.release-it.json | 22 +- packages/redis/package.json | 3 + packages/search/.release-it.json | 19 +- packages/search/package.json | 3 +- packages/time-series/.release-it.json | 19 +- packages/time-series/package.json | 3 +- 17 files changed, 1641 insertions(+), 2679 deletions(-) create mode 100644 .github/workflows/release.yml diff --git a/.github/workflows/release.yml b/.github/workflows/release.yml new file mode 100644 index 00000000000..d5732f2eda0 --- /dev/null +++ b/.github/workflows/release.yml @@ -0,0 +1,50 @@ +name: Release + +on: + workflow_dispatch: + inputs: + version: + description: 'Version to release ("major", "minor", "patch", or "pre*" version; or specify version like "5.3.3")' + required: true + type: string + args: + description: 'Additional arguments to pass to release-it (e.g. "--dry-run"). See docs: https://github.com/release-it/release-it/blob/main/docs/git.md#configuration-options' + required: false + type: string + +jobs: + release: + runs-on: ubuntu-latest + permissions: + contents: write + packages: write + steps: + - name: Checkout code + uses: actions/checkout@v4 + with: + fetch-depth: 0 + + - name: Setup Node.js + uses: actions/setup-node@v4 + with: + node-version: '22' + registry-url: 'https://registry.npmjs.org' + + - name: Install dependencies + run: npm ci + + - name: Configure Git + run: | + git config --local user.email "action@github.com" + git config --local user.name "GitHub Action" + + # Build all packages + - name: Build packages + run: npm run build + + # Release using the monorepo approach + - name: Release packages + run: npm run release -- --ci -i ${{ github.event.inputs.version }} ${{ github.event.inputs.args }} + env: + NODE_AUTH_TOKEN: ${{ secrets.NPM_TOKEN }} + GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }} diff --git a/package-lock.json b/package-lock.json index 6d2460ed19f..dddb972c236 100644 --- a/package-lock.json +++ b/package-lock.json @@ -6,16 +6,24 @@ "": { "name": "redis-monorepo", "workspaces": [ - "./packages/*" + "./packages/client", + "./packages/test-utils", + "./packages/bloom", + "./packages/json", + "./packages/search", + "./packages/time-series", + "./packages/entraid", + "./packages/redis" ], "devDependencies": { "@istanbuljs/nyc-config-typescript": "^1.0.2", + "@release-it/bumper": "^7.0.5", "@types/mocha": "^10.0.6", "@types/node": "^20.11.16", "gh-pages": "^6.1.1", "mocha": "^10.2.0", "nyc": "^15.1.0", - "release-it": "^17.0.3", + "release-it": "^19.0.2", "ts-node": "^10.9.2", "tsx": "^4.7.0", "typedoc": "^0.25.7", @@ -191,24 +199,6 @@ "safe-buffer": "^5.0.1" } }, - "node_modules/@azure/identity/node_modules/open": { - "version": "10.1.0", - "resolved": "https://registry.npmjs.org/open/-/open-10.1.0.tgz", - "integrity": "sha512-mnkeQ1qP5Ue2wd+aivTD3NHd/lZ96Lu0jgf0pwktLPtx6cTZiH7tyeGRRHs0zX0rbrahXPnXlUnbeXyaBBuIaw==", - "license": "MIT", - "dependencies": { - "default-browser": "^5.2.1", - "define-lazy-prop": "^3.0.0", - "is-inside-container": "^1.0.0", - "is-wsl": "^3.1.0" - }, - "engines": { - "node": ">=18" - }, - "funding": { - "url": "https://github.com/sponsors/sindresorhus" - } - }, "node_modules/@azure/logger": { "version": "1.1.4", "resolved": "https://registry.npmjs.org/@azure/logger/-/logger-1.1.4.tgz", @@ -705,10 +695,343 @@ } }, "node_modules/@iarna/toml": { - "version": "2.2.5", + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/@iarna/toml/-/toml-3.0.0.tgz", + "integrity": "sha512-td6ZUkz2oS3VeleBcN+m//Q6HlCFCPrnI0FZhrt/h4XqLEdOyYp2u21nd8MdsR+WJy5r9PTDaHTDDfhf4H4l6Q==", "dev": true, "license": "ISC" }, + "node_modules/@inquirer/checkbox": { + "version": "4.1.7", + "resolved": "https://registry.npmjs.org/@inquirer/checkbox/-/checkbox-4.1.7.tgz", + "integrity": "sha512-VEr2vnI4TSM2Q50fAck98mzWJGAoxbF0rb48tcSEjkJ2kn3mM6c/YsJwnyu45PlXd6aNWObMGWmQVleL2BJy6w==", + "dev": true, + "license": "MIT", + "dependencies": { + "@inquirer/core": "^10.1.12", + "@inquirer/figures": "^1.0.12", + "@inquirer/type": "^3.0.7", + "ansi-escapes": "^4.3.2", + "yoctocolors-cjs": "^2.1.2" + }, + "engines": { + "node": ">=18" + }, + "peerDependencies": { + "@types/node": ">=18" + }, + "peerDependenciesMeta": { + "@types/node": { + "optional": true + } + } + }, + "node_modules/@inquirer/confirm": { + "version": "5.1.11", + "resolved": "https://registry.npmjs.org/@inquirer/confirm/-/confirm-5.1.11.tgz", + "integrity": "sha512-HgVha2B1lurfZ8u7cBWmu60HpkpnnIT/1IrreBx5g2oxQOVYU15WQDl6oZqjuXVbzteFKSpmMkLTMf2OmbUjaw==", + "dev": true, + "license": "MIT", + "dependencies": { + "@inquirer/core": "^10.1.12", + "@inquirer/type": "^3.0.7" + }, + "engines": { + "node": ">=18" + }, + "peerDependencies": { + "@types/node": ">=18" + }, + "peerDependenciesMeta": { + "@types/node": { + "optional": true + } + } + }, + "node_modules/@inquirer/core": { + "version": "10.1.12", + "resolved": "https://registry.npmjs.org/@inquirer/core/-/core-10.1.12.tgz", + "integrity": "sha512-uoaDadeJCYSVKYCMPwJi3AjCF9w+l9aWbHYA4iskKX84cVW/A2M6bJlWBoy3k81GpFp6EX3IElV1Z5xKw0g1QQ==", + "dev": true, + "license": "MIT", + "dependencies": { + "@inquirer/figures": "^1.0.12", + "@inquirer/type": "^3.0.7", + "ansi-escapes": "^4.3.2", + "cli-width": "^4.1.0", + "mute-stream": "^2.0.0", + "signal-exit": "^4.1.0", + "wrap-ansi": "^6.2.0", + "yoctocolors-cjs": "^2.1.2" + }, + "engines": { + "node": ">=18" + }, + "peerDependencies": { + "@types/node": ">=18" + }, + "peerDependenciesMeta": { + "@types/node": { + "optional": true + } + } + }, + "node_modules/@inquirer/core/node_modules/signal-exit": { + "version": "4.1.0", + "resolved": "https://registry.npmjs.org/signal-exit/-/signal-exit-4.1.0.tgz", + "integrity": "sha512-bzyZ1e88w9O1iNJbKnOlvYTrWPDl46O1bG0D3XInv+9tkPrxrN8jUUTiFlDkkmKWgn1M6CfIA13SuGqOa9Korw==", + "dev": true, + "license": "ISC", + "engines": { + "node": ">=14" + }, + "funding": { + "url": "https://github.com/sponsors/isaacs" + } + }, + "node_modules/@inquirer/editor": { + "version": "4.2.12", + "resolved": "https://registry.npmjs.org/@inquirer/editor/-/editor-4.2.12.tgz", + "integrity": "sha512-YNOCY79iqI/ksWohdudGtnO02N/a2j82b6akK/+hy1/C6xoU07dsKFUBfQ36nLCxE98ICS74Uyandq7nBS31Mw==", + "dev": true, + "license": "MIT", + "dependencies": { + "@inquirer/core": "^10.1.12", + "@inquirer/type": "^3.0.7", + "external-editor": "^3.1.0" + }, + "engines": { + "node": ">=18" + }, + "peerDependencies": { + "@types/node": ">=18" + }, + "peerDependenciesMeta": { + "@types/node": { + "optional": true + } + } + }, + "node_modules/@inquirer/expand": { + "version": "4.0.14", + "resolved": "https://registry.npmjs.org/@inquirer/expand/-/expand-4.0.14.tgz", + "integrity": "sha512-aon4yACMp4Qwc/2f6xafcC6jzAJ5vXBwL5+z4bS2y4YIOGF+QOe+Jzd5hLz1hOo+bhzVS7q07dNXTeBjaFAqRA==", + "dev": true, + "license": "MIT", + "dependencies": { + "@inquirer/core": "^10.1.12", + "@inquirer/type": "^3.0.7", + "yoctocolors-cjs": "^2.1.2" + }, + "engines": { + "node": ">=18" + }, + "peerDependencies": { + "@types/node": ">=18" + }, + "peerDependenciesMeta": { + "@types/node": { + "optional": true + } + } + }, + "node_modules/@inquirer/figures": { + "version": "1.0.12", + "resolved": "https://registry.npmjs.org/@inquirer/figures/-/figures-1.0.12.tgz", + "integrity": "sha512-MJttijd8rMFcKJC8NYmprWr6hD3r9Gd9qUC0XwPNwoEPWSMVJwA2MlXxF+nhZZNMY+HXsWa+o7KY2emWYIn0jQ==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=18" + } + }, + "node_modules/@inquirer/input": { + "version": "4.1.11", + "resolved": "https://registry.npmjs.org/@inquirer/input/-/input-4.1.11.tgz", + "integrity": "sha512-gzcBWLWMiBaY507HFg4B1NJ18InnHhLjj4DTLfyoz9Rv7dSPpJ9JSj7Of8ea5QE2D+ms3ESTl/4MdzrC1//B0Q==", + "dev": true, + "license": "MIT", + "dependencies": { + "@inquirer/core": "^10.1.12", + "@inquirer/type": "^3.0.7" + }, + "engines": { + "node": ">=18" + }, + "peerDependencies": { + "@types/node": ">=18" + }, + "peerDependenciesMeta": { + "@types/node": { + "optional": true + } + } + }, + "node_modules/@inquirer/number": { + "version": "3.0.14", + "resolved": "https://registry.npmjs.org/@inquirer/number/-/number-3.0.14.tgz", + "integrity": "sha512-8B4jX8ArK9zvb8/tB04jGLja4XoFfjvrTLJ5YeLlFnJh3jPa9VTQt2kxJZubGKc8YHX68e1XQxv4Nu/WZUnXIw==", + "dev": true, + "license": "MIT", + "dependencies": { + "@inquirer/core": "^10.1.12", + "@inquirer/type": "^3.0.7" + }, + "engines": { + "node": ">=18" + }, + "peerDependencies": { + "@types/node": ">=18" + }, + "peerDependenciesMeta": { + "@types/node": { + "optional": true + } + } + }, + "node_modules/@inquirer/password": { + "version": "4.0.14", + "resolved": "https://registry.npmjs.org/@inquirer/password/-/password-4.0.14.tgz", + "integrity": "sha512-/N/5PeI+QWE23dTn2D4erD9Y3yYeh0bUDkO9tt2d11mAVuCswiOKzoHrV9KYGQhoD6ae+Nff1G8TPqbfUUh8Ag==", + "dev": true, + "license": "MIT", + "dependencies": { + "@inquirer/core": "^10.1.12", + "@inquirer/type": "^3.0.7", + "ansi-escapes": "^4.3.2" + }, + "engines": { + "node": ">=18" + }, + "peerDependencies": { + "@types/node": ">=18" + }, + "peerDependenciesMeta": { + "@types/node": { + "optional": true + } + } + }, + "node_modules/@inquirer/prompts": { + "version": "7.5.2", + "resolved": "https://registry.npmjs.org/@inquirer/prompts/-/prompts-7.5.2.tgz", + "integrity": "sha512-+jsUm6G9X5PUD97HkcGojzwyPsz5oSB2FUbj+D+NOYFQUj0XqvhDcDfk9mhMxFG/RDIgT9Kq4x0rm5pC5zVHUg==", + "dev": true, + "license": "MIT", + "dependencies": { + "@inquirer/checkbox": "^4.1.7", + "@inquirer/confirm": "^5.1.11", + "@inquirer/editor": "^4.2.12", + "@inquirer/expand": "^4.0.14", + "@inquirer/input": "^4.1.11", + "@inquirer/number": "^3.0.14", + "@inquirer/password": "^4.0.14", + "@inquirer/rawlist": "^4.1.2", + "@inquirer/search": "^3.0.14", + "@inquirer/select": "^4.2.2" + }, + "engines": { + "node": ">=18" + }, + "peerDependencies": { + "@types/node": ">=18" + }, + "peerDependenciesMeta": { + "@types/node": { + "optional": true + } + } + }, + "node_modules/@inquirer/rawlist": { + "version": "4.1.2", + "resolved": "https://registry.npmjs.org/@inquirer/rawlist/-/rawlist-4.1.2.tgz", + "integrity": "sha512-VDuhV58w3FuKNl24GR9ygdbu3NkGfuaK7D2gyMWeY79Lr4GVbj7ySxw1isAnelSzU1ecZC/TwICa5rCy0za2OA==", + "dev": true, + "license": "MIT", + "dependencies": { + "@inquirer/core": "^10.1.12", + "@inquirer/type": "^3.0.7", + "yoctocolors-cjs": "^2.1.2" + }, + "engines": { + "node": ">=18" + }, + "peerDependencies": { + "@types/node": ">=18" + }, + "peerDependenciesMeta": { + "@types/node": { + "optional": true + } + } + }, + "node_modules/@inquirer/search": { + "version": "3.0.14", + "resolved": "https://registry.npmjs.org/@inquirer/search/-/search-3.0.14.tgz", + "integrity": "sha512-+VdtRD5nVR50K5fEMq/qbtHGH08vfqm69NJtojavlMXj6fsYymQZrNqjxEISPs2PDvtsemTJVFGs0uI6Zti6Dw==", + "dev": true, + "license": "MIT", + "dependencies": { + "@inquirer/core": "^10.1.12", + "@inquirer/figures": "^1.0.12", + "@inquirer/type": "^3.0.7", + "yoctocolors-cjs": "^2.1.2" + }, + "engines": { + "node": ">=18" + }, + "peerDependencies": { + "@types/node": ">=18" + }, + "peerDependenciesMeta": { + "@types/node": { + "optional": true + } + } + }, + "node_modules/@inquirer/select": { + "version": "4.2.2", + "resolved": "https://registry.npmjs.org/@inquirer/select/-/select-4.2.2.tgz", + "integrity": "sha512-3X8AAPE1WPUwY3IawT19BapD0kKpAUP7SVUu5mxmRjnl/f4q0MQz8CU8ToCC6Im0SzyOTWmSauE3GBgyOv1rBw==", + "dev": true, + "license": "MIT", + "dependencies": { + "@inquirer/core": "^10.1.12", + "@inquirer/figures": "^1.0.12", + "@inquirer/type": "^3.0.7", + "ansi-escapes": "^4.3.2", + "yoctocolors-cjs": "^2.1.2" + }, + "engines": { + "node": ">=18" + }, + "peerDependencies": { + "@types/node": ">=18" + }, + "peerDependenciesMeta": { + "@types/node": { + "optional": true + } + } + }, + "node_modules/@inquirer/type": { + "version": "3.0.7", + "resolved": "https://registry.npmjs.org/@inquirer/type/-/type-3.0.7.tgz", + "integrity": "sha512-PfunHQcjwnju84L+ycmcMKB/pTPIngjUJvfnRhKY6FKPuYXlM4aQCb/nIdTFR6BEhMjFvngzvng/vBAJMZpLSA==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=18" + }, + "peerDependencies": { + "@types/node": ">=18" + }, + "peerDependenciesMeta": { + "@types/node": { + "optional": true + } + } + }, "node_modules/@istanbuljs/load-nyc-config": { "version": "1.1.0", "dev": true, @@ -857,19 +1180,10 @@ "@jridgewell/sourcemap-codec": "^1.4.14" } }, - "node_modules/@ljharb/through": { - "version": "2.3.12", - "dev": true, - "license": "MIT", - "dependencies": { - "call-bind": "^1.0.5" - }, - "engines": { - "node": ">= 0.4" - } - }, "node_modules/@nodelib/fs.scandir": { "version": "2.1.5", + "resolved": "https://registry.npmjs.org/@nodelib/fs.scandir/-/fs.scandir-2.1.5.tgz", + "integrity": "sha512-vq24Bq3ym5HEQm2NKCr3yXDwjc7vTsEThRDnkp2DK9p1uqLR+DHurm/NOTo0KG7HYHU7eppKZj3MyqYuMBf62g==", "dev": true, "license": "MIT", "dependencies": { @@ -882,6 +1196,8 @@ }, "node_modules/@nodelib/fs.stat": { "version": "2.0.5", + "resolved": "https://registry.npmjs.org/@nodelib/fs.stat/-/fs.stat-2.0.5.tgz", + "integrity": "sha512-RkhPPp2zrqDAQA/2jNhnztcPAlv64XdhIp7a7454A5ovI7Bukxgt7MX7udwAu3zg1DcpPU0rz3VV1SeaqvY4+A==", "dev": true, "license": "MIT", "engines": { @@ -890,6 +1206,8 @@ }, "node_modules/@nodelib/fs.walk": { "version": "1.2.8", + "resolved": "https://registry.npmjs.org/@nodelib/fs.walk/-/fs.walk-1.2.8.tgz", + "integrity": "sha512-oGB+UxlgWcgQkgwo8GcEGwemoTFt3FIO9ababBmaGwXIoBKZ+GTy0pP185beGg7Llih/NSHSV2XAs1lnznocSg==", "dev": true, "license": "MIT", "dependencies": { @@ -900,8 +1218,20 @@ "node": ">= 8" } }, + "node_modules/@nodeutils/defaults-deep": { + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/@nodeutils/defaults-deep/-/defaults-deep-1.1.0.tgz", + "integrity": "sha512-gG44cwQovaOFdSR02jR9IhVRpnDP64VN6JdjYJTfNz4J4fWn7TQnmrf22nSjRqlwlxPcW8PL/L3KbJg3tdwvpg==", + "dev": true, + "license": "ISC", + "dependencies": { + "lodash": "^4.15.0" + } + }, "node_modules/@octokit/auth-token": { - "version": "4.0.0", + "version": "5.1.2", + "resolved": "https://registry.npmjs.org/@octokit/auth-token/-/auth-token-5.1.2.tgz", + "integrity": "sha512-JcQDsBdg49Yky2w2ld20IHAlwr8d/d8N6NiOXbtuoPCqzbsiJgF633mVUw3x4mo0H5ypataQIX7SFu3yy44Mpw==", "dev": true, "license": "MIT", "engines": { @@ -909,175 +1239,207 @@ } }, "node_modules/@octokit/core": { - "version": "5.1.0", + "version": "6.1.5", + "resolved": "https://registry.npmjs.org/@octokit/core/-/core-6.1.5.tgz", + "integrity": "sha512-vvmsN0r7rguA+FySiCsbaTTobSftpIDIpPW81trAmsv9TGxg3YCujAxRYp/Uy8xmDgYCzzgulG62H7KYUFmeIg==", "dev": true, "license": "MIT", "dependencies": { - "@octokit/auth-token": "^4.0.0", - "@octokit/graphql": "^7.0.0", - "@octokit/request": "^8.0.2", - "@octokit/request-error": "^5.0.0", - "@octokit/types": "^12.0.0", - "before-after-hook": "^2.2.0", - "universal-user-agent": "^6.0.0" + "@octokit/auth-token": "^5.0.0", + "@octokit/graphql": "^8.2.2", + "@octokit/request": "^9.2.3", + "@octokit/request-error": "^6.1.8", + "@octokit/types": "^14.0.0", + "before-after-hook": "^3.0.2", + "universal-user-agent": "^7.0.0" }, "engines": { "node": ">= 18" } }, "node_modules/@octokit/endpoint": { - "version": "9.0.4", + "version": "10.1.4", + "resolved": "https://registry.npmjs.org/@octokit/endpoint/-/endpoint-10.1.4.tgz", + "integrity": "sha512-OlYOlZIsfEVZm5HCSR8aSg02T2lbUWOsCQoPKfTXJwDzcHQBrVBGdGXb89dv2Kw2ToZaRtudp8O3ZIYoaOjKlA==", "dev": true, "license": "MIT", "dependencies": { - "@octokit/types": "^12.0.0", - "universal-user-agent": "^6.0.0" + "@octokit/types": "^14.0.0", + "universal-user-agent": "^7.0.2" }, "engines": { "node": ">= 18" } }, "node_modules/@octokit/graphql": { - "version": "7.0.2", + "version": "8.2.2", + "resolved": "https://registry.npmjs.org/@octokit/graphql/-/graphql-8.2.2.tgz", + "integrity": "sha512-Yi8hcoqsrXGdt0yObxbebHXFOiUA+2v3n53epuOg1QUgOB6c4XzvisBNVXJSl8RYA5KrDuSL2yq9Qmqe5N0ryA==", "dev": true, "license": "MIT", "dependencies": { - "@octokit/request": "^8.0.1", - "@octokit/types": "^12.0.0", - "universal-user-agent": "^6.0.0" + "@octokit/request": "^9.2.3", + "@octokit/types": "^14.0.0", + "universal-user-agent": "^7.0.0" }, "engines": { "node": ">= 18" } }, "node_modules/@octokit/openapi-types": { - "version": "19.1.0", + "version": "25.0.0", + "resolved": "https://registry.npmjs.org/@octokit/openapi-types/-/openapi-types-25.0.0.tgz", + "integrity": "sha512-FZvktFu7HfOIJf2BScLKIEYjDsw6RKc7rBJCdvCTfKsVnx2GEB/Nbzjr29DUdb7vQhlzS/j8qDzdditP0OC6aw==", "dev": true, "license": "MIT" }, "node_modules/@octokit/plugin-paginate-rest": { - "version": "9.1.5", + "version": "11.6.0", + "resolved": "https://registry.npmjs.org/@octokit/plugin-paginate-rest/-/plugin-paginate-rest-11.6.0.tgz", + "integrity": "sha512-n5KPteiF7pWKgBIBJSk8qzoZWcUkza2O6A0za97pMGVrGfPdltxrfmfF5GucHYvHGZD8BdaZmmHGz5cX/3gdpw==", "dev": true, "license": "MIT", "dependencies": { - "@octokit/types": "^12.4.0" + "@octokit/types": "^13.10.0" }, "engines": { "node": ">= 18" }, "peerDependencies": { - "@octokit/core": ">=5" + "@octokit/core": ">=6" + } + }, + "node_modules/@octokit/plugin-paginate-rest/node_modules/@octokit/openapi-types": { + "version": "24.2.0", + "resolved": "https://registry.npmjs.org/@octokit/openapi-types/-/openapi-types-24.2.0.tgz", + "integrity": "sha512-9sIH3nSUttelJSXUrmGzl7QUBFul0/mB8HRYl3fOlgHbIWG+WnYDXU3v/2zMtAvuzZ/ed00Ei6on975FhBfzrg==", + "dev": true, + "license": "MIT" + }, + "node_modules/@octokit/plugin-paginate-rest/node_modules/@octokit/types": { + "version": "13.10.0", + "resolved": "https://registry.npmjs.org/@octokit/types/-/types-13.10.0.tgz", + "integrity": "sha512-ifLaO34EbbPj0Xgro4G5lP5asESjwHracYJvVaPIyXMuiuXLlhic3S47cBdTb+jfODkTE5YtGCLt3Ay3+J97sA==", + "dev": true, + "license": "MIT", + "dependencies": { + "@octokit/openapi-types": "^24.2.0" } }, "node_modules/@octokit/plugin-request-log": { - "version": "4.0.0", + "version": "5.3.1", + "resolved": "https://registry.npmjs.org/@octokit/plugin-request-log/-/plugin-request-log-5.3.1.tgz", + "integrity": "sha512-n/lNeCtq+9ofhC15xzmJCNKP2BWTv8Ih2TTy+jatNCCq/gQP/V7rK3fjIfuz0pDWDALO/o/4QY4hyOF6TQQFUw==", "dev": true, "license": "MIT", "engines": { "node": ">= 18" }, "peerDependencies": { - "@octokit/core": ">=5" + "@octokit/core": ">=6" } }, "node_modules/@octokit/plugin-rest-endpoint-methods": { - "version": "10.2.0", + "version": "13.5.0", + "resolved": "https://registry.npmjs.org/@octokit/plugin-rest-endpoint-methods/-/plugin-rest-endpoint-methods-13.5.0.tgz", + "integrity": "sha512-9Pas60Iv9ejO3WlAX3maE1+38c5nqbJXV5GrncEfkndIpZrJ/WPMRd2xYDcPPEt5yzpxcjw9fWNoPhsSGzqKqw==", "dev": true, "license": "MIT", "dependencies": { - "@octokit/types": "^12.3.0" + "@octokit/types": "^13.10.0" }, "engines": { "node": ">= 18" }, "peerDependencies": { - "@octokit/core": ">=5" + "@octokit/core": ">=6" + } + }, + "node_modules/@octokit/plugin-rest-endpoint-methods/node_modules/@octokit/openapi-types": { + "version": "24.2.0", + "resolved": "https://registry.npmjs.org/@octokit/openapi-types/-/openapi-types-24.2.0.tgz", + "integrity": "sha512-9sIH3nSUttelJSXUrmGzl7QUBFul0/mB8HRYl3fOlgHbIWG+WnYDXU3v/2zMtAvuzZ/ed00Ei6on975FhBfzrg==", + "dev": true, + "license": "MIT" + }, + "node_modules/@octokit/plugin-rest-endpoint-methods/node_modules/@octokit/types": { + "version": "13.10.0", + "resolved": "https://registry.npmjs.org/@octokit/types/-/types-13.10.0.tgz", + "integrity": "sha512-ifLaO34EbbPj0Xgro4G5lP5asESjwHracYJvVaPIyXMuiuXLlhic3S47cBdTb+jfODkTE5YtGCLt3Ay3+J97sA==", + "dev": true, + "license": "MIT", + "dependencies": { + "@octokit/openapi-types": "^24.2.0" } }, "node_modules/@octokit/request": { - "version": "8.1.6", + "version": "9.2.3", + "resolved": "https://registry.npmjs.org/@octokit/request/-/request-9.2.3.tgz", + "integrity": "sha512-Ma+pZU8PXLOEYzsWf0cn/gY+ME57Wq8f49WTXA8FMHp2Ps9djKw//xYJ1je8Hm0pR2lU9FUGeJRWOtxq6olt4w==", "dev": true, "license": "MIT", "dependencies": { - "@octokit/endpoint": "^9.0.0", - "@octokit/request-error": "^5.0.0", - "@octokit/types": "^12.0.0", - "universal-user-agent": "^6.0.0" + "@octokit/endpoint": "^10.1.4", + "@octokit/request-error": "^6.1.8", + "@octokit/types": "^14.0.0", + "fast-content-type-parse": "^2.0.0", + "universal-user-agent": "^7.0.2" }, "engines": { "node": ">= 18" } }, "node_modules/@octokit/request-error": { - "version": "5.0.1", + "version": "6.1.8", + "resolved": "https://registry.npmjs.org/@octokit/request-error/-/request-error-6.1.8.tgz", + "integrity": "sha512-WEi/R0Jmq+IJKydWlKDmryPcmdYSVjL3ekaiEL1L9eo1sUnqMJ+grqmC9cjk7CA7+b2/T397tO5d8YLOH3qYpQ==", "dev": true, "license": "MIT", "dependencies": { - "@octokit/types": "^12.0.0", - "deprecation": "^2.0.0", - "once": "^1.4.0" + "@octokit/types": "^14.0.0" }, "engines": { "node": ">= 18" } }, "node_modules/@octokit/rest": { - "version": "20.0.2", + "version": "21.1.1", + "resolved": "https://registry.npmjs.org/@octokit/rest/-/rest-21.1.1.tgz", + "integrity": "sha512-sTQV7va0IUVZcntzy1q3QqPm/r8rWtDCqpRAmb8eXXnKkjoQEtFe3Nt5GTVsHft+R6jJoHeSiVLcgcvhtue/rg==", "dev": true, "license": "MIT", "dependencies": { - "@octokit/core": "^5.0.0", - "@octokit/plugin-paginate-rest": "^9.0.0", - "@octokit/plugin-request-log": "^4.0.0", - "@octokit/plugin-rest-endpoint-methods": "^10.0.0" + "@octokit/core": "^6.1.4", + "@octokit/plugin-paginate-rest": "^11.4.2", + "@octokit/plugin-request-log": "^5.3.1", + "@octokit/plugin-rest-endpoint-methods": "^13.3.0" }, "engines": { "node": ">= 18" } }, "node_modules/@octokit/types": { - "version": "12.4.0", + "version": "14.0.0", + "resolved": "https://registry.npmjs.org/@octokit/types/-/types-14.0.0.tgz", + "integrity": "sha512-VVmZP0lEhbo2O1pdq63gZFiGCKkm8PPp8AUOijlwPO6hojEVjspA0MWKP7E4hbvGxzFKNqKr6p0IYtOH/Wf/zA==", "dev": true, "license": "MIT", "dependencies": { - "@octokit/openapi-types": "^19.1.0" - } - }, - "node_modules/@pnpm/config.env-replace": { - "version": "1.1.0", - "dev": true, - "license": "MIT", - "engines": { - "node": ">=12.22.0" + "@octokit/openapi-types": "^25.0.0" } }, - "node_modules/@pnpm/network.ca-file": { - "version": "1.0.2", + "node_modules/@phun-ky/typeof": { + "version": "1.2.8", + "resolved": "https://registry.npmjs.org/@phun-ky/typeof/-/typeof-1.2.8.tgz", + "integrity": "sha512-7J6ca1tK0duM2BgVB+CuFMh3idlIVASOP2QvOCbNWDc6JnvjtKa9nufPoJQQ4xrwBonwgT1TIhRRcEtzdVgWsA==", "dev": true, "license": "MIT", - "dependencies": { - "graceful-fs": "4.2.10" - }, "engines": { - "node": ">=12.22.0" - } - }, - "node_modules/@pnpm/network.ca-file/node_modules/graceful-fs": { - "version": "4.2.10", - "dev": true, - "license": "ISC" - }, - "node_modules/@pnpm/npm-conf": { - "version": "2.2.2", - "dev": true, - "license": "MIT", - "dependencies": { - "@pnpm/config.env-replace": "^1.1.0", - "@pnpm/network.ca-file": "^1.0.1", - "config-chain": "^1.1.11" + "node": "^20.9.0 || >=22.0.0", + "npm": ">=10.8.2" }, - "engines": { - "node": ">=12" + "funding": { + "url": "https://github.com/phun-ky/typeof?sponsor=1" } }, "node_modules/@redis/bloom": { @@ -1108,26 +1470,40 @@ "resolved": "packages/time-series", "link": true }, - "node_modules/@sindresorhus/is": { - "version": "5.6.0", + "node_modules/@release-it/bumper": { + "version": "7.0.5", + "resolved": "https://registry.npmjs.org/@release-it/bumper/-/bumper-7.0.5.tgz", + "integrity": "sha512-HCFMqDHreLYg4jjTWL//pW1GzZZMn3p7HDbwS2y7y5m0L6p8hEaOEixC3tEzwyVV7VP1VGjqxMvxfa360q8+Tg==", "dev": true, "license": "MIT", + "dependencies": { + "@iarna/toml": "^3.0.0", + "cheerio": "^1.0.0", + "detect-indent": "7.0.1", + "fast-glob": "^3.3.3", + "ini": "^5.0.0", + "js-yaml": "^4.1.0", + "lodash-es": "^4.17.21", + "semver": "^7.7.1" + }, "engines": { - "node": ">=14.16" + "node": "^20.9.0 || >=22.0.0" }, - "funding": { - "url": "https://github.com/sindresorhus/is?sponsor=1" + "peerDependencies": { + "release-it": ">=18.0.0 || >=19.0.0" } }, - "node_modules/@sindresorhus/merge-streams": { - "version": "1.0.0", + "node_modules/@release-it/bumper/node_modules/semver": { + "version": "7.7.2", + "resolved": "https://registry.npmjs.org/semver/-/semver-7.7.2.tgz", + "integrity": "sha512-RF0Fw+rO5AMf9MAyaRXI4AV0Ulj5lMHqVxxdSgiVbixSCXoEmmX/jk0CuJw4+3SqroYO9VoUh+HcuJivvtJemA==", "dev": true, - "license": "MIT", - "engines": { - "node": ">=18" + "license": "ISC", + "bin": { + "semver": "bin/semver.js" }, - "funding": { - "url": "https://github.com/sponsors/sindresorhus" + "engines": { + "node": ">=10" } }, "node_modules/@sinonjs/commons": { @@ -1169,19 +1545,10 @@ "dev": true, "license": "(Unlicense OR Apache-2.0)" }, - "node_modules/@szmarczak/http-timer": { - "version": "5.0.1", - "dev": true, - "license": "MIT", - "dependencies": { - "defer-to-connect": "^2.0.1" - }, - "engines": { - "node": ">=14.16" - } - }, "node_modules/@tootallnate/quickjs-emscripten": { "version": "0.23.0", + "resolved": "https://registry.npmjs.org/@tootallnate/quickjs-emscripten/-/quickjs-emscripten-0.23.0.tgz", + "integrity": "sha512-C5Mc6rdnsaJDjO3UpGW/CQTHtCKaYlScZTly4JIu97Jxo/odCiH0ITnDXSJPTOrEKk/ycSZ0AOgTmkDtkOsvIA==", "dev": true, "license": "MIT" }, @@ -1270,11 +1637,6 @@ "@types/express": "*" } }, - "node_modules/@types/http-cache-semantics": { - "version": "4.0.4", - "dev": true, - "license": "MIT" - }, "node_modules/@types/http-errors": { "version": "2.0.4", "resolved": "https://registry.npmjs.org/@types/http-errors/-/http-errors-2.0.4.tgz", @@ -1302,6 +1664,13 @@ "undici-types": "~5.26.4" } }, + "node_modules/@types/parse-path": { + "version": "7.0.3", + "resolved": "https://registry.npmjs.org/@types/parse-path/-/parse-path-7.0.3.tgz", + "integrity": "sha512-LriObC2+KYZD3FzCrgWGv/qufdUy4eXrxcLgQMfYXgPbLIecKIsVBaQgUPmxSSLcjmYbDTQbMgr6qr6l/eb7Bg==", + "dev": true, + "license": "MIT" + }, "node_modules/@types/qs": { "version": "6.9.17", "resolved": "https://registry.npmjs.org/@types/qs/-/qs-6.9.17.tgz", @@ -1406,11 +1775,10 @@ } }, "node_modules/agent-base": { - "version": "7.1.0", + "version": "7.1.3", + "resolved": "https://registry.npmjs.org/agent-base/-/agent-base-7.1.3.tgz", + "integrity": "sha512-jRR5wdylq8CkOe6hei19GGZnxM6rBGwFl3Bg0YItGDimvjGtAvdZk4Pu6Cl4u4Igsws4a1fd1Vq3ezrhn4KmFw==", "license": "MIT", - "dependencies": { - "debug": "^4.3.4" - }, "engines": { "node": ">= 14" } @@ -1427,14 +1795,6 @@ "node": ">=8" } }, - "node_modules/ansi-align": { - "version": "3.0.1", - "dev": true, - "license": "ISC", - "dependencies": { - "string-width": "^4.1.0" - } - }, "node_modules/ansi-colors": { "version": "4.1.1", "dev": true, @@ -1445,6 +1805,8 @@ }, "node_modules/ansi-escapes": { "version": "4.3.2", + "resolved": "https://registry.npmjs.org/ansi-escapes/-/ansi-escapes-4.3.2.tgz", + "integrity": "sha512-gKXj5ALrKWQLsYG9jlTRmR/xKluxHV+Z9QEwNIgCfM1/uwPMCuzVVnh5mwTd+OuBZcwSIMbqssNWRm1lE51QaQ==", "dev": true, "license": "MIT", "dependencies": { @@ -1459,6 +1821,8 @@ }, "node_modules/ansi-escapes/node_modules/type-fest": { "version": "0.21.3", + "resolved": "https://registry.npmjs.org/type-fest/-/type-fest-0.21.3.tgz", + "integrity": "sha512-t0rzBq87m3fVcduHDUFhKmyyX+9eo6WQjZvf51Ea/M0Q7+T374Jp1aUiyUl0GKxp8M/OETVHSDvmkyPgvX+X2w==", "dev": true, "license": "(MIT OR CC0-1.0)", "engines": { @@ -1535,21 +1899,6 @@ "dev": true, "license": "Python-2.0" }, - "node_modules/array-buffer-byte-length": { - "version": "1.0.1", - "dev": true, - "license": "MIT", - "dependencies": { - "call-bind": "^1.0.5", - "is-array-buffer": "^3.0.4" - }, - "engines": { - "node": ">= 0.4" - }, - "funding": { - "url": "https://github.com/sponsors/ljharb" - } - }, "node_modules/array-flatten": { "version": "1.1.1", "resolved": "https://registry.npmjs.org/array-flatten/-/array-flatten-1.1.1.tgz", @@ -1576,47 +1925,10 @@ "node": ">=0.10.0" } }, - "node_modules/array.prototype.map": { - "version": "1.0.6", - "dev": true, - "license": "MIT", - "dependencies": { - "call-bind": "^1.0.2", - "define-properties": "^1.2.0", - "es-abstract": "^1.22.1", - "es-array-method-boxes-properly": "^1.0.0", - "is-string": "^1.0.7" - }, - "engines": { - "node": ">= 0.4" - }, - "funding": { - "url": "https://github.com/sponsors/ljharb" - } - }, - "node_modules/arraybuffer.prototype.slice": { - "version": "1.0.3", - "dev": true, - "license": "MIT", - "dependencies": { - "array-buffer-byte-length": "^1.0.1", - "call-bind": "^1.0.5", - "define-properties": "^1.2.1", - "es-abstract": "^1.22.3", - "es-errors": "^1.2.1", - "get-intrinsic": "^1.2.3", - "is-array-buffer": "^3.0.4", - "is-shared-array-buffer": "^1.0.2" - }, - "engines": { - "node": ">= 0.4" - }, - "funding": { - "url": "https://github.com/sponsors/ljharb" - } - }, "node_modules/ast-types": { "version": "0.13.4", + "resolved": "https://registry.npmjs.org/ast-types/-/ast-types-0.13.4.tgz", + "integrity": "sha512-x1FCFnFifvYDDzTaLII71vG5uvDwgtmDTEVWAxrgeiR8VjMONcCXJx7E+USjDtHlwFmt9MysbqgF9b9Vjr6w+w==", "dev": true, "license": "MIT", "dependencies": { @@ -1633,49 +1945,23 @@ }, "node_modules/async-retry": { "version": "1.3.3", + "resolved": "https://registry.npmjs.org/async-retry/-/async-retry-1.3.3.tgz", + "integrity": "sha512-wfr/jstw9xNi/0teMHrRW7dsz3Lt5ARhYNZ2ewpadnhaIp5mbALhOAP+EAdsC7t4Z6wqsDVv9+W6gm1Dk9mEyw==", "dev": true, "license": "MIT", "dependencies": { "retry": "0.13.1" } }, - "node_modules/available-typed-arrays": { - "version": "1.0.6", - "dev": true, - "license": "MIT", - "engines": { - "node": ">= 0.4" - }, - "funding": { - "url": "https://github.com/sponsors/ljharb" - } - }, "node_modules/balanced-match": { "version": "1.0.2", "dev": true, "license": "MIT" }, - "node_modules/base64-js": { - "version": "1.5.1", - "dev": true, - "funding": [ - { - "type": "github", - "url": "https://github.com/sponsors/feross" - }, - { - "type": "patreon", - "url": "https://www.patreon.com/feross" - }, - { - "type": "consulting", - "url": "https://feross.org/support" - } - ], - "license": "MIT" - }, "node_modules/basic-ftp": { - "version": "5.0.4", + "version": "5.0.5", + "resolved": "https://registry.npmjs.org/basic-ftp/-/basic-ftp-5.0.5.tgz", + "integrity": "sha512-4Bcg1P8xhUuqcii/S0Z9wiHIrQVPMermM1any+MX5GeGD7faD3/msQUDGLol9wOcz4/jbg/WJnGqoJF6LiBdtg==", "dev": true, "license": "MIT", "engines": { @@ -1683,7 +1969,9 @@ } }, "node_modules/before-after-hook": { - "version": "2.2.3", + "version": "3.0.2", + "resolved": "https://registry.npmjs.org/before-after-hook/-/before-after-hook-3.0.2.tgz", + "integrity": "sha512-Nik3Sc0ncrMK4UUdXQmAnRtzmNQTAAXmXIopizwZ1W1t8QmfJj+zL4OA2I7XPTPW5z5TDqv4hRo/JzouDJnX3A==", "dev": true, "license": "Apache-2.0" }, @@ -1695,16 +1983,6 @@ "node": ">=8" } }, - "node_modules/bl": { - "version": "4.1.0", - "dev": true, - "license": "MIT", - "dependencies": { - "buffer": "^5.5.0", - "inherits": "^2.0.4", - "readable-stream": "^3.4.0" - } - }, "node_modules/body-parser": { "version": "1.20.3", "resolved": "https://registry.npmjs.org/body-parser/-/body-parser-1.20.3.tgz", @@ -1747,132 +2025,12 @@ "dev": true, "license": "MIT" }, - "node_modules/boxen": { - "version": "7.1.1", - "dev": true, - "license": "MIT", - "dependencies": { - "ansi-align": "^3.0.1", - "camelcase": "^7.0.1", - "chalk": "^5.2.0", - "cli-boxes": "^3.0.0", - "string-width": "^5.1.2", - "type-fest": "^2.13.0", - "widest-line": "^4.0.1", - "wrap-ansi": "^8.1.0" - }, - "engines": { - "node": ">=14.16" - }, - "funding": { - "url": "https://github.com/sponsors/sindresorhus" - } - }, - "node_modules/boxen/node_modules/ansi-regex": { - "version": "6.0.1", - "dev": true, - "license": "MIT", - "engines": { - "node": ">=12" - }, - "funding": { - "url": "https://github.com/chalk/ansi-regex?sponsor=1" - } - }, - "node_modules/boxen/node_modules/ansi-styles": { - "version": "6.2.1", - "dev": true, - "license": "MIT", - "engines": { - "node": ">=12" - }, - "funding": { - "url": "https://github.com/chalk/ansi-styles?sponsor=1" - } - }, - "node_modules/boxen/node_modules/camelcase": { - "version": "7.0.1", - "dev": true, - "license": "MIT", - "engines": { - "node": ">=14.16" - }, - "funding": { - "url": "https://github.com/sponsors/sindresorhus" - } - }, - "node_modules/boxen/node_modules/chalk": { - "version": "5.3.0", - "dev": true, - "license": "MIT", - "engines": { - "node": "^12.17.0 || ^14.13 || >=16.0.0" - }, - "funding": { - "url": "https://github.com/chalk/chalk?sponsor=1" - } - }, - "node_modules/boxen/node_modules/emoji-regex": { - "version": "9.2.2", - "dev": true, - "license": "MIT" - }, - "node_modules/boxen/node_modules/string-width": { - "version": "5.1.2", - "dev": true, - "license": "MIT", - "dependencies": { - "eastasianwidth": "^0.2.0", - "emoji-regex": "^9.2.2", - "strip-ansi": "^7.0.1" - }, - "engines": { - "node": ">=12" - }, - "funding": { - "url": "https://github.com/sponsors/sindresorhus" - } - }, - "node_modules/boxen/node_modules/strip-ansi": { - "version": "7.1.0", - "dev": true, - "license": "MIT", - "dependencies": { - "ansi-regex": "^6.0.1" - }, - "engines": { - "node": ">=12" - }, - "funding": { - "url": "https://github.com/chalk/strip-ansi?sponsor=1" - } - }, - "node_modules/boxen/node_modules/type-fest": { - "version": "2.19.0", - "dev": true, - "license": "(MIT OR CC0-1.0)", - "engines": { - "node": ">=12.20" - }, - "funding": { - "url": "https://github.com/sponsors/sindresorhus" - } - }, - "node_modules/boxen/node_modules/wrap-ansi": { - "version": "8.1.0", + "node_modules/boolbase": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/boolbase/-/boolbase-1.0.0.tgz", + "integrity": "sha512-JZOSA7Mo9sNGB8+UjSgzdLtokWAky1zbztM3WRLCbZ70/3cTANmQmOdR7y2g+J0e2WXywy1yS468tY+IruqEww==", "dev": true, - "license": "MIT", - "dependencies": { - "ansi-styles": "^6.1.0", - "string-width": "^5.0.1", - "strip-ansi": "^7.0.1" - }, - "engines": { - "node": ">=12" - }, - "funding": { - "url": "https://github.com/chalk/wrap-ansi?sponsor=1" - } + "license": "ISC" }, "node_modules/brace-expansion": { "version": "1.1.11", @@ -1884,11 +2042,13 @@ } }, "node_modules/braces": { - "version": "3.0.2", + "version": "3.0.3", + "resolved": "https://registry.npmjs.org/braces/-/braces-3.0.3.tgz", + "integrity": "sha512-yQbXgO/OSZVD2IsiLlro+7Hf6Q18EJrKSEsdoMzKePKXct3gvD8oLcOQdIzGupr5Fj+EDe8gO/lxc1BzfMpxvA==", "dev": true, "license": "MIT", "dependencies": { - "fill-range": "^7.0.1" + "fill-range": "^7.1.1" }, "engines": { "node": ">=8" @@ -1930,29 +2090,6 @@ "node": "^6 || ^7 || ^8 || ^9 || ^10 || ^11 || ^12 || >=13.7" } }, - "node_modules/buffer": { - "version": "5.7.1", - "dev": true, - "funding": [ - { - "type": "github", - "url": "https://github.com/sponsors/feross" - }, - { - "type": "patreon", - "url": "https://www.patreon.com/feross" - }, - { - "type": "consulting", - "url": "https://feross.org/support" - } - ], - "license": "MIT", - "dependencies": { - "base64-js": "^1.3.1", - "ieee754": "^1.1.13" - } - }, "node_modules/buffer-equal-constant-time": { "version": "1.0.1", "resolved": "https://registry.npmjs.org/buffer-equal-constant-time/-/buffer-equal-constant-time-1.0.1.tgz", @@ -1982,40 +2119,63 @@ "node": ">= 0.8" } }, - "node_modules/cacheable-lookup": { - "version": "7.0.0", + "node_modules/c12": { + "version": "3.0.3", + "resolved": "https://registry.npmjs.org/c12/-/c12-3.0.3.tgz", + "integrity": "sha512-uC3MacKBb0Z15o5QWCHvHWj5Zv34pGQj9P+iXKSpTuSGFS0KKhUWf4t9AJ+gWjYOdmWCPEGpEzm8sS0iqbpo1w==", "dev": true, "license": "MIT", - "engines": { - "node": ">=14.16" + "dependencies": { + "chokidar": "^4.0.3", + "confbox": "^0.2.2", + "defu": "^6.1.4", + "dotenv": "^16.4.7", + "exsolve": "^1.0.4", + "giget": "^2.0.0", + "jiti": "^2.4.2", + "ohash": "^2.0.11", + "pathe": "^2.0.3", + "perfect-debounce": "^1.0.0", + "pkg-types": "^2.1.0", + "rc9": "^2.1.2" + }, + "peerDependencies": { + "magicast": "^0.3.5" + }, + "peerDependenciesMeta": { + "magicast": { + "optional": true + } } }, - "node_modules/cacheable-request": { - "version": "10.2.14", + "node_modules/c12/node_modules/chokidar": { + "version": "4.0.3", + "resolved": "https://registry.npmjs.org/chokidar/-/chokidar-4.0.3.tgz", + "integrity": "sha512-Qgzu8kfBvo+cA4962jnP1KkS6Dop5NS6g7R5LFYJr4b8Ub94PPQXUksCw9PvXoeXPRRddRNC5C1JQUR2SMGtnA==", "dev": true, "license": "MIT", "dependencies": { - "@types/http-cache-semantics": "^4.0.2", - "get-stream": "^6.0.1", - "http-cache-semantics": "^4.1.1", - "keyv": "^4.5.3", - "mimic-response": "^4.0.0", - "normalize-url": "^8.0.0", - "responselike": "^3.0.0" + "readdirp": "^4.0.1" }, "engines": { - "node": ">=14.16" + "node": ">= 14.16.0" + }, + "funding": { + "url": "https://paulmillr.com/funding/" } }, - "node_modules/cacheable-request/node_modules/get-stream": { - "version": "6.0.1", + "node_modules/c12/node_modules/readdirp": { + "version": "4.1.2", + "resolved": "https://registry.npmjs.org/readdirp/-/readdirp-4.1.2.tgz", + "integrity": "sha512-GDhwkLfywWL2s6vEjyhri+eXmfH6j1L7JE27WhqLeYzoh/A3DBaYGEj2H/HFZCn/kMfim73FXxEJTw06WtxQwg==", "dev": true, "license": "MIT", "engines": { - "node": ">=10" + "node": ">= 14.18.0" }, "funding": { - "url": "https://github.com/sponsors/sindresorhus" + "type": "individual", + "url": "https://paulmillr.com/funding/" } }, "node_modules/caching-transform": { @@ -2065,14 +2225,6 @@ "node": ">= 0.4" } }, - "node_modules/callsites": { - "version": "3.1.0", - "dev": true, - "license": "MIT", - "engines": { - "node": ">=6" - } - }, "node_modules/camelcase": { "version": "5.3.1", "dev": true, @@ -2128,9 +2280,55 @@ }, "node_modules/chardet": { "version": "0.7.0", + "resolved": "https://registry.npmjs.org/chardet/-/chardet-0.7.0.tgz", + "integrity": "sha512-mT8iDcrh03qDGRRmoA2hmBJnxpllMR+0/0qlzjqZES6NdiWDcZkCNAk4rPFZ9Q85r27unkiNNg8ZOiwZXBHwcA==", "dev": true, "license": "MIT" }, + "node_modules/cheerio": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/cheerio/-/cheerio-1.0.0.tgz", + "integrity": "sha512-quS9HgjQpdaXOvsZz82Oz7uxtXiy6UIsIQcpBj7HRw2M63Skasm9qlDocAM7jNuaxdhpPU7c4kJN+gA5MCu4ww==", + "dev": true, + "license": "MIT", + "dependencies": { + "cheerio-select": "^2.1.0", + "dom-serializer": "^2.0.0", + "domhandler": "^5.0.3", + "domutils": "^3.1.0", + "encoding-sniffer": "^0.2.0", + "htmlparser2": "^9.1.0", + "parse5": "^7.1.2", + "parse5-htmlparser2-tree-adapter": "^7.0.0", + "parse5-parser-stream": "^7.1.2", + "undici": "^6.19.5", + "whatwg-mimetype": "^4.0.0" + }, + "engines": { + "node": ">=18.17" + }, + "funding": { + "url": "https://github.com/cheeriojs/cheerio?sponsor=1" + } + }, + "node_modules/cheerio-select": { + "version": "2.1.0", + "resolved": "https://registry.npmjs.org/cheerio-select/-/cheerio-select-2.1.0.tgz", + "integrity": "sha512-9v9kG0LvzrlcungtnJtpGNxY+fzECQKhK4EGJX2vByejiMX84MFNQw4UxPJl3bFbTMw+Dfs37XaIkCwTZfLh4g==", + "dev": true, + "license": "BSD-2-Clause", + "dependencies": { + "boolbase": "^1.0.0", + "css-select": "^5.1.0", + "css-what": "^6.1.0", + "domelementtype": "^2.3.0", + "domhandler": "^5.0.3", + "domutils": "^3.0.1" + }, + "funding": { + "url": "https://github.com/sponsors/fb55" + } + }, "node_modules/chokidar": { "version": "3.5.3", "dev": true, @@ -2158,7 +2356,9 @@ } }, "node_modules/ci-info": { - "version": "3.9.0", + "version": "4.2.0", + "resolved": "https://registry.npmjs.org/ci-info/-/ci-info-4.2.0.tgz", + "integrity": "sha512-cYY9mypksY8NRqgDB1XD1RiJL338v/551niynFTGkZOO2LHuB2OmOYxDIe/ttN9AHwrqdum1360G3ald0W9kCg==", "dev": true, "funding": [ { @@ -2171,38 +2371,44 @@ "node": ">=8" } }, - "node_modules/clean-stack": { - "version": "2.2.0", + "node_modules/citty": { + "version": "0.1.6", + "resolved": "https://registry.npmjs.org/citty/-/citty-0.1.6.tgz", + "integrity": "sha512-tskPPKEs8D2KPafUypv2gxwJP8h/OaJmC82QQGGDQcHvXX43xF2VDACcJVmZ0EuSxkpO9Kc4MlrA3q0+FG58AQ==", "dev": true, "license": "MIT", - "engines": { - "node": ">=6" + "dependencies": { + "consola": "^3.2.3" } }, - "node_modules/cli-boxes": { - "version": "3.0.0", + "node_modules/clean-stack": { + "version": "2.2.0", "dev": true, "license": "MIT", "engines": { - "node": ">=10" - }, - "funding": { - "url": "https://github.com/sponsors/sindresorhus" + "node": ">=6" } }, "node_modules/cli-cursor": { - "version": "3.1.0", + "version": "5.0.0", + "resolved": "https://registry.npmjs.org/cli-cursor/-/cli-cursor-5.0.0.tgz", + "integrity": "sha512-aCj4O5wKyszjMmDT4tZj93kxyydN/K5zPWSCe6/0AV/AA1pqe5ZBIw0a2ZfPQV7lL5/yb5HsUreJ6UFAF1tEQw==", "dev": true, "license": "MIT", "dependencies": { - "restore-cursor": "^3.1.0" + "restore-cursor": "^5.0.0" }, "engines": { - "node": ">=8" + "node": ">=18" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" } }, "node_modules/cli-spinners": { "version": "2.9.2", + "resolved": "https://registry.npmjs.org/cli-spinners/-/cli-spinners-2.9.2.tgz", + "integrity": "sha512-ywqV+5MmyL4E7ybXgKys4DugZbX0FC6LnwrhjuykIjnK9k8OQacQ7axGKnjDXWNhns0xot3bZI5h55H8yo9cJg==", "dev": true, "license": "MIT", "engines": { @@ -2214,6 +2420,8 @@ }, "node_modules/cli-width": { "version": "4.1.0", + "resolved": "https://registry.npmjs.org/cli-width/-/cli-width-4.1.0.tgz", + "integrity": "sha512-ouuZd4/dm2Sw5Gmqy6bGyNNNe1qt9RpmxveLSO7KcgsTnU7RXfsw+/bukWGo1abgBiMAic068rclZsO4IWmmxQ==", "dev": true, "license": "ISC", "engines": { @@ -2246,14 +2454,6 @@ "url": "https://github.com/chalk/wrap-ansi?sponsor=1" } }, - "node_modules/clone": { - "version": "1.0.4", - "dev": true, - "license": "MIT", - "engines": { - "node": ">=0.8" - } - }, "node_modules/cluster-key-slot": { "version": "1.1.2", "license": "Apache-2.0", @@ -2295,36 +2495,21 @@ "dev": true, "license": "MIT" }, - "node_modules/config-chain": { - "version": "1.1.13", - "dev": true, - "license": "MIT", - "dependencies": { - "ini": "^1.3.4", - "proto-list": "~1.2.1" - } - }, - "node_modules/config-chain/node_modules/ini": { - "version": "1.3.8", + "node_modules/confbox": { + "version": "0.2.2", + "resolved": "https://registry.npmjs.org/confbox/-/confbox-0.2.2.tgz", + "integrity": "sha512-1NB+BKqhtNipMsov4xI/NnhCKp9XG9NamYp5PVm9klAT0fsrNPjaFICsCFhNhwZJKNh7zB/3q8qXz0E9oaMNtQ==", "dev": true, - "license": "ISC" + "license": "MIT" }, - "node_modules/configstore": { - "version": "6.0.0", + "node_modules/consola": { + "version": "3.4.2", + "resolved": "https://registry.npmjs.org/consola/-/consola-3.4.2.tgz", + "integrity": "sha512-5IKcdX0nnYavi6G7TtOhwkYzyjfJlatbjMjuLSfE2kYT5pMDOilZ4OvMhi637CcDICTmz3wARPoyhqyX1Y+XvA==", "dev": true, - "license": "BSD-2-Clause", - "dependencies": { - "dot-prop": "^6.0.1", - "graceful-fs": "^4.2.6", - "unique-string": "^3.0.0", - "write-file-atomic": "^3.0.3", - "xdg-basedir": "^5.0.1" - }, + "license": "MIT", "engines": { - "node": ">=12" - }, - "funding": { - "url": "https://github.com/yeoman/configstore?sponsor=1" + "node": "^14.18.0 || >=16.10.0" } }, "node_modules/content-disposition": { @@ -2372,31 +2557,6 @@ "dev": true, "license": "MIT" }, - "node_modules/cosmiconfig": { - "version": "9.0.0", - "dev": true, - "license": "MIT", - "dependencies": { - "env-paths": "^2.2.1", - "import-fresh": "^3.3.0", - "js-yaml": "^4.1.0", - "parse-json": "^5.2.0" - }, - "engines": { - "node": ">=14" - }, - "funding": { - "url": "https://github.com/sponsors/d-fischer" - }, - "peerDependencies": { - "typescript": ">=4.9.5" - }, - "peerDependenciesMeta": { - "typescript": { - "optional": true - } - } - }, "node_modules/create-require": { "version": "1.1.1", "resolved": "https://registry.npmjs.org/create-require/-/create-require-1.1.1.tgz", @@ -2417,37 +2577,44 @@ "node": ">= 8" } }, - "node_modules/crypto-random-string": { - "version": "4.0.0", + "node_modules/css-select": { + "version": "5.1.0", + "resolved": "https://registry.npmjs.org/css-select/-/css-select-5.1.0.tgz", + "integrity": "sha512-nwoRF1rvRRnnCqqY7updORDsuqKzqYJ28+oSMaJMMgOauh3fvwHqMS7EZpIPqK8GL+g9mKxF1vP/ZjSeNjEVHg==", "dev": true, - "license": "MIT", + "license": "BSD-2-Clause", "dependencies": { - "type-fest": "^1.0.1" - }, - "engines": { - "node": ">=12" + "boolbase": "^1.0.0", + "css-what": "^6.1.0", + "domhandler": "^5.0.2", + "domutils": "^3.0.1", + "nth-check": "^2.0.1" }, "funding": { - "url": "https://github.com/sponsors/sindresorhus" + "url": "https://github.com/sponsors/fb55" } }, - "node_modules/crypto-random-string/node_modules/type-fest": { - "version": "1.4.0", + "node_modules/css-what": { + "version": "6.1.0", + "resolved": "https://registry.npmjs.org/css-what/-/css-what-6.1.0.tgz", + "integrity": "sha512-HTUrgRJ7r4dsZKU6GjmpfRK1O76h97Z8MfS1G0FozR+oF2kG6Vfe8JE6zwrkbxigziPHinCJ+gCPjA9EaBDtRw==", "dev": true, - "license": "(MIT OR CC0-1.0)", + "license": "BSD-2-Clause", "engines": { - "node": ">=10" + "node": ">= 6" }, "funding": { - "url": "https://github.com/sponsors/sindresorhus" + "url": "https://github.com/sponsors/fb55" } }, "node_modules/data-uri-to-buffer": { - "version": "4.0.1", + "version": "6.0.2", + "resolved": "https://registry.npmjs.org/data-uri-to-buffer/-/data-uri-to-buffer-6.0.2.tgz", + "integrity": "sha512-7hvf7/GW8e86rW0ptuwS3OcBGDjIi6SZva7hCyWC0yYry2cOPmLIjXAUHI6DK2HsnwJd9ifmt57i8eV2n4YNpw==", "dev": true, "license": "MIT", "engines": { - "node": ">= 12" + "node": ">= 14" } }, "node_modules/debug": { @@ -2477,39 +2644,6 @@ "node": ">=0.10.0" } }, - "node_modules/decompress-response": { - "version": "6.0.0", - "dev": true, - "license": "MIT", - "dependencies": { - "mimic-response": "^3.1.0" - }, - "engines": { - "node": ">=10" - }, - "funding": { - "url": "https://github.com/sponsors/sindresorhus" - } - }, - "node_modules/decompress-response/node_modules/mimic-response": { - "version": "3.1.0", - "dev": true, - "license": "MIT", - "engines": { - "node": ">=10" - }, - "funding": { - "url": "https://github.com/sponsors/sindresorhus" - } - }, - "node_modules/deep-extend": { - "version": "0.6.0", - "dev": true, - "license": "MIT", - "engines": { - "node": ">=4.0.0" - } - }, "node_modules/default-browser": { "version": "5.2.1", "license": "MIT", @@ -2548,25 +2682,6 @@ "url": "https://github.com/sponsors/sindresorhus" } }, - "node_modules/defaults": { - "version": "1.0.4", - "dev": true, - "license": "MIT", - "dependencies": { - "clone": "^1.0.2" - }, - "funding": { - "url": "https://github.com/sponsors/sindresorhus" - } - }, - "node_modules/defer-to-connect": { - "version": "2.0.1", - "dev": true, - "license": "MIT", - "engines": { - "node": ">=10" - } - }, "node_modules/define-data-property": { "version": "1.1.4", "resolved": "https://registry.npmjs.org/define-data-property/-/define-data-property-1.1.4.tgz", @@ -2595,24 +2710,17 @@ "url": "https://github.com/sponsors/sindresorhus" } }, - "node_modules/define-properties": { - "version": "1.2.1", + "node_modules/defu": { + "version": "6.1.4", + "resolved": "https://registry.npmjs.org/defu/-/defu-6.1.4.tgz", + "integrity": "sha512-mEQCMmwJu317oSz8CwdIOdwf3xMif1ttiM8LTufzc3g6kR+9Pe236twL8j3IYT1F7GfRgGcW6MWxzZjLIkuHIg==", "dev": true, - "license": "MIT", - "dependencies": { - "define-data-property": "^1.0.1", - "has-property-descriptors": "^1.0.0", - "object-keys": "^1.1.1" - }, - "engines": { - "node": ">= 0.4" - }, - "funding": { - "url": "https://github.com/sponsors/ljharb" - } + "license": "MIT" }, "node_modules/degenerator": { "version": "5.0.1", + "resolved": "https://registry.npmjs.org/degenerator/-/degenerator-5.0.1.tgz", + "integrity": "sha512-TllpMR/t0M5sqCXfj85i4XaAzxmS5tVA16dqvdkMwGmzI+dXLXnw3J+3Vdv7VKw+ThlTMboK6i9rnZ6Nntj5CQ==", "dev": true, "license": "MIT", "dependencies": { @@ -2634,10 +2742,12 @@ "node": ">= 0.8" } }, - "node_modules/deprecation": { - "version": "2.3.1", + "node_modules/destr": { + "version": "2.0.5", + "resolved": "https://registry.npmjs.org/destr/-/destr-2.0.5.tgz", + "integrity": "sha512-ugFTXCtDZunbzasqBxrK93Ik/DRYsO6S/fedkWEMKqt04xZ4csmnmwGDBAb07QWNaGMAmnTIemsYZCksjATwsA==", "dev": true, - "license": "ISC" + "license": "MIT" }, "node_modules/destroy": { "version": "1.2.0", @@ -2650,6 +2760,16 @@ "npm": "1.2.8000 || >= 1.4.16" } }, + "node_modules/detect-indent": { + "version": "7.0.1", + "resolved": "https://registry.npmjs.org/detect-indent/-/detect-indent-7.0.1.tgz", + "integrity": "sha512-Mc7QhQ8s+cLrnUfU/Ji94vG/r8M26m8f++vyres4ZoojaRDpZ1eSIh/EpzLNwlWuvzSZ3UbDFspjFvTDXe6e/g==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=12.20" + } + }, "node_modules/diff": { "version": "5.0.0", "dev": true, @@ -2658,18 +2778,63 @@ "node": ">=0.3.1" } }, - "node_modules/dot-prop": { - "version": "6.0.1", + "node_modules/dom-serializer": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/dom-serializer/-/dom-serializer-2.0.0.tgz", + "integrity": "sha512-wIkAryiqt/nV5EQKqQpo3SToSOV9J0DnbJqwK7Wv/Trc92zIAYZ4FlMu+JPFW1DfGFt81ZTCGgDEabffXeLyJg==", "dev": true, "license": "MIT", "dependencies": { - "is-obj": "^2.0.0" + "domelementtype": "^2.3.0", + "domhandler": "^5.0.2", + "entities": "^4.2.0" + }, + "funding": { + "url": "https://github.com/cheeriojs/dom-serializer?sponsor=1" + } + }, + "node_modules/domelementtype": { + "version": "2.3.0", + "resolved": "https://registry.npmjs.org/domelementtype/-/domelementtype-2.3.0.tgz", + "integrity": "sha512-OLETBj6w0OsagBwdXnPdN0cnMfF9opN69co+7ZrbfPGrdpPVNBUj02spi6B1N7wChLQiPn4CSH/zJvXw56gmHw==", + "dev": true, + "funding": [ + { + "type": "github", + "url": "https://github.com/sponsors/fb55" + } + ], + "license": "BSD-2-Clause" + }, + "node_modules/domhandler": { + "version": "5.0.3", + "resolved": "https://registry.npmjs.org/domhandler/-/domhandler-5.0.3.tgz", + "integrity": "sha512-cgwlv/1iFQiFnU96XXgROh8xTeetsnJiDsTc7TYCLFd9+/WNkIqPTxiM/8pSd8VIrhXGTf1Ny1q1hquVqDJB5w==", + "dev": true, + "license": "BSD-2-Clause", + "dependencies": { + "domelementtype": "^2.3.0" }, "engines": { - "node": ">=10" + "node": ">= 4" }, "funding": { - "url": "https://github.com/sponsors/sindresorhus" + "url": "https://github.com/fb55/domhandler?sponsor=1" + } + }, + "node_modules/domutils": { + "version": "3.2.2", + "resolved": "https://registry.npmjs.org/domutils/-/domutils-3.2.2.tgz", + "integrity": "sha512-6kZKyUajlDuqlHKVX1w7gyslj9MPIXzIFiz/rGu35uC1wMi+kMhQwGhl4lt9unC9Vb9INnY9Z3/ZA3+FhASLaw==", + "dev": true, + "license": "BSD-2-Clause", + "dependencies": { + "dom-serializer": "^2.0.0", + "domelementtype": "^2.3.0", + "domhandler": "^5.0.3" + }, + "funding": { + "url": "https://github.com/fb55/domutils?sponsor=1" } }, "node_modules/dotenv": { @@ -2700,11 +2865,6 @@ "node": ">= 0.4" } }, - "node_modules/eastasianwidth": { - "version": "0.2.0", - "dev": true, - "license": "MIT" - }, "node_modules/ecdsa-sig-formatter": { "version": "1.0.11", "resolved": "https://registry.npmjs.org/ecdsa-sig-formatter/-/ecdsa-sig-formatter-1.0.11.tgz", @@ -2746,79 +2906,46 @@ "node": ">= 0.8" } }, - "node_modules/env-paths": { - "version": "2.2.1", + "node_modules/encoding-sniffer": { + "version": "0.2.0", + "resolved": "https://registry.npmjs.org/encoding-sniffer/-/encoding-sniffer-0.2.0.tgz", + "integrity": "sha512-ju7Wq1kg04I3HtiYIOrUrdfdDvkyO9s5XM8QAj/bN61Yo/Vb4vgJxy5vi4Yxk01gWHbrofpPtpxM8bKger9jhg==", "dev": true, "license": "MIT", - "engines": { - "node": ">=6" + "dependencies": { + "iconv-lite": "^0.6.3", + "whatwg-encoding": "^3.1.1" + }, + "funding": { + "url": "https://github.com/fb55/encoding-sniffer?sponsor=1" } }, - "node_modules/error-ex": { - "version": "1.3.2", + "node_modules/encoding-sniffer/node_modules/iconv-lite": { + "version": "0.6.3", + "resolved": "https://registry.npmjs.org/iconv-lite/-/iconv-lite-0.6.3.tgz", + "integrity": "sha512-4fCk79wshMdzMp2rH06qWrJE4iolqLhCUH+OiuIgU++RB0+94NlDL81atO7GX55uUKueo0txHNtvEyI6D7WdMw==", "dev": true, "license": "MIT", "dependencies": { - "is-arrayish": "^0.2.1" + "safer-buffer": ">= 2.1.2 < 3.0.0" + }, + "engines": { + "node": ">=0.10.0" } }, - "node_modules/es-abstract": { - "version": "1.22.3", + "node_modules/entities": { + "version": "4.5.0", + "resolved": "https://registry.npmjs.org/entities/-/entities-4.5.0.tgz", + "integrity": "sha512-V0hjH4dGPh9Ao5p0MoRY6BVqtwCjhz6vI5LT8AJ55H+4g9/4vbHx1I54fS0XuclLhDHArPQCiMjDxjaL8fPxhw==", "dev": true, - "license": "MIT", - "dependencies": { - "array-buffer-byte-length": "^1.0.0", - "arraybuffer.prototype.slice": "^1.0.2", - "available-typed-arrays": "^1.0.5", - "call-bind": "^1.0.5", - "es-set-tostringtag": "^2.0.1", - "es-to-primitive": "^1.2.1", - "function.prototype.name": "^1.1.6", - "get-intrinsic": "^1.2.2", - "get-symbol-description": "^1.0.0", - "globalthis": "^1.0.3", - "gopd": "^1.0.1", - "has-property-descriptors": "^1.0.0", - "has-proto": "^1.0.1", - "has-symbols": "^1.0.3", - "hasown": "^2.0.0", - "internal-slot": "^1.0.5", - "is-array-buffer": "^3.0.2", - "is-callable": "^1.2.7", - "is-negative-zero": "^2.0.2", - "is-regex": "^1.1.4", - "is-shared-array-buffer": "^1.0.2", - "is-string": "^1.0.7", - "is-typed-array": "^1.1.12", - "is-weakref": "^1.0.2", - "object-inspect": "^1.13.1", - "object-keys": "^1.1.1", - "object.assign": "^4.1.4", - "regexp.prototype.flags": "^1.5.1", - "safe-array-concat": "^1.0.1", - "safe-regex-test": "^1.0.0", - "string.prototype.trim": "^1.2.8", - "string.prototype.trimend": "^1.0.7", - "string.prototype.trimstart": "^1.0.7", - "typed-array-buffer": "^1.0.0", - "typed-array-byte-length": "^1.0.0", - "typed-array-byte-offset": "^1.0.0", - "typed-array-length": "^1.0.4", - "unbox-primitive": "^1.0.2", - "which-typed-array": "^1.1.13" - }, + "license": "BSD-2-Clause", "engines": { - "node": ">= 0.4" + "node": ">=0.12" }, "funding": { - "url": "https://github.com/sponsors/ljharb" + "url": "https://github.com/fb55/entities?sponsor=1" } }, - "node_modules/es-array-method-boxes-properly": { - "version": "1.0.0", - "dev": true, - "license": "MIT" - }, "node_modules/es-define-property": { "version": "1.0.1", "resolved": "https://registry.npmjs.org/es-define-property/-/es-define-property-1.0.1.tgz", @@ -2837,56 +2964,8 @@ "node": ">= 0.4" } }, - "node_modules/es-get-iterator": { - "version": "1.1.3", - "dev": true, - "license": "MIT", - "dependencies": { - "call-bind": "^1.0.2", - "get-intrinsic": "^1.1.3", - "has-symbols": "^1.0.3", - "is-arguments": "^1.1.1", - "is-map": "^2.0.2", - "is-set": "^2.0.2", - "is-string": "^1.0.7", - "isarray": "^2.0.5", - "stop-iteration-iterator": "^1.0.0" - }, - "funding": { - "url": "https://github.com/sponsors/ljharb" - } - }, - "node_modules/es-set-tostringtag": { - "version": "2.0.2", - "dev": true, - "license": "MIT", - "dependencies": { - "get-intrinsic": "^1.2.2", - "has-tostringtag": "^1.0.0", - "hasown": "^2.0.0" - }, - "engines": { - "node": ">= 0.4" - } - }, - "node_modules/es-to-primitive": { - "version": "1.2.1", - "dev": true, - "license": "MIT", - "dependencies": { - "is-callable": "^1.1.4", - "is-date-object": "^1.0.1", - "is-symbol": "^1.0.2" - }, - "engines": { - "node": ">= 0.4" - }, - "funding": { - "url": "https://github.com/sponsors/ljharb" - } - }, - "node_modules/es6-error": { - "version": "4.1.1", + "node_modules/es6-error": { + "version": "4.1.1", "dev": true, "license": "MIT" }, @@ -2935,17 +3014,6 @@ "node": ">=6" } }, - "node_modules/escape-goat": { - "version": "4.0.0", - "dev": true, - "license": "MIT", - "engines": { - "node": ">=12" - }, - "funding": { - "url": "https://github.com/sponsors/sindresorhus" - } - }, "node_modules/escape-html": { "version": "1.0.3", "resolved": "https://registry.npmjs.org/escape-html/-/escape-html-1.0.3.tgz", @@ -2966,6 +3034,8 @@ }, "node_modules/escodegen": { "version": "2.1.0", + "resolved": "https://registry.npmjs.org/escodegen/-/escodegen-2.1.0.tgz", + "integrity": "sha512-2NlIDTwUWJN0mRPQOdtQBzbUHvdGY2P1VXSyU83Q3xKxM7WHX2Ql8dKq782Q9TgQUNOLEzEYu9bzLNj1q88I5w==", "dev": true, "license": "BSD-2-Clause", "dependencies": { @@ -2998,6 +3068,8 @@ }, "node_modules/estraverse": { "version": "5.3.0", + "resolved": "https://registry.npmjs.org/estraverse/-/estraverse-5.3.0.tgz", + "integrity": "sha512-MMdARuVEQziNTeJD8DgMqmhwR11BRQ/cBP+pLtYdSTnf3MIO8fFeiINEbX36ZdNlfU/7A9f3gUw49B3oQsvwBA==", "dev": true, "license": "BSD-2-Clause", "engines": { @@ -3006,12 +3078,27 @@ }, "node_modules/esutils": { "version": "2.0.3", + "resolved": "https://registry.npmjs.org/esutils/-/esutils-2.0.3.tgz", + "integrity": "sha512-kVscqXk4OCp68SZ0dkgEKVi6/8ij300KBWTJq32P/dYeWTSwK41WyTxalN1eRmA5Z9UU/LX9D7FWSmV9SAYx6g==", "dev": true, "license": "BSD-2-Clause", "engines": { "node": ">=0.10.0" } }, + "node_modules/eta": { + "version": "3.5.0", + "resolved": "https://registry.npmjs.org/eta/-/eta-3.5.0.tgz", + "integrity": "sha512-e3x3FBvGzeCIHhF+zhK8FZA2vC5uFn6b4HJjegUbIWrDb4mJ7JjTGMJY9VGIbRVpmSwHopNiaJibhjIr+HfLug==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=6.0.0" + }, + "funding": { + "url": "https://github.com/eta-dev/eta?sponsor=1" + } + }, "node_modules/etag": { "version": "1.8.1", "resolved": "https://registry.npmjs.org/etag/-/etag-1.8.1.tgz", @@ -3033,6 +3120,8 @@ }, "node_modules/execa": { "version": "8.0.1", + "resolved": "https://registry.npmjs.org/execa/-/execa-8.0.1.tgz", + "integrity": "sha512-VyhnebXciFV2DESc+p6B+y0LjSm0krU4OgJN44qFAhBY0TJ+1V61tYD2+wHusZ6F9n5K+vl8k0sTy7PEfV4qpg==", "dev": true, "license": "MIT", "dependencies": { @@ -3055,6 +3144,8 @@ }, "node_modules/execa/node_modules/is-stream": { "version": "3.0.0", + "resolved": "https://registry.npmjs.org/is-stream/-/is-stream-3.0.0.tgz", + "integrity": "sha512-LnQR4bZ9IADDRSkvpqMGvt/tEJWclzklNgSw48V5EAaAeDd6qGvN8ei6k5p0tvxSR171VmGyHuTiAOfxAbr8kA==", "dev": true, "license": "MIT", "engines": { @@ -3064,8 +3155,26 @@ "url": "https://github.com/sponsors/sindresorhus" } }, + "node_modules/execa/node_modules/onetime": { + "version": "6.0.0", + "resolved": "https://registry.npmjs.org/onetime/-/onetime-6.0.0.tgz", + "integrity": "sha512-1FlR+gjXK7X+AsAHso35MnyN5KqGwJRi/31ft6x0M194ht7S+rWAvd7PHss9xSKMzE0asv1pyIHaJYq+BbacAQ==", + "dev": true, + "license": "MIT", + "dependencies": { + "mimic-fn": "^4.0.0" + }, + "engines": { + "node": ">=12" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, "node_modules/execa/node_modules/signal-exit": { "version": "4.1.0", + "resolved": "https://registry.npmjs.org/signal-exit/-/signal-exit-4.1.0.tgz", + "integrity": "sha512-bzyZ1e88w9O1iNJbKnOlvYTrWPDl46O1bG0D3XInv+9tkPrxrN8jUUTiFlDkkmKWgn1M6CfIA13SuGqOa9Korw==", "dev": true, "license": "ISC", "engines": { @@ -3200,8 +3309,17 @@ "dev": true, "license": "MIT" }, + "node_modules/exsolve": { + "version": "1.0.5", + "resolved": "https://registry.npmjs.org/exsolve/-/exsolve-1.0.5.tgz", + "integrity": "sha512-pz5dvkYYKQ1AHVrgOzBKWeP4u4FRb3a6DNK2ucr0OoNwYIU4QWsJ+NM36LLzORT+z845MzKHHhpXiUF5nvQoJg==", + "dev": true, + "license": "MIT" + }, "node_modules/external-editor": { "version": "3.1.0", + "resolved": "https://registry.npmjs.org/external-editor/-/external-editor-3.1.0.tgz", + "integrity": "sha512-hMQ4CX1p1izmuLYyZqLMO/qGNw10wSv9QDCPfzXfyFrOaCSSoRfqE1Kf1s5an66J5JZC62NewG+mK49jOCtQew==", "dev": true, "license": "MIT", "dependencies": { @@ -3213,8 +3331,27 @@ "node": ">=4" } }, + "node_modules/fast-content-type-parse": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/fast-content-type-parse/-/fast-content-type-parse-2.0.1.tgz", + "integrity": "sha512-nGqtvLrj5w0naR6tDPfB4cUmYCqouzyQiz6C5y/LtcDllJdrcc6WaWW6iXyIIOErTa/XRybj28aasdn4LkVk6Q==", + "dev": true, + "funding": [ + { + "type": "github", + "url": "https://github.com/sponsors/fastify" + }, + { + "type": "opencollective", + "url": "https://opencollective.com/fastify" + } + ], + "license": "MIT" + }, "node_modules/fast-glob": { - "version": "3.3.2", + "version": "3.3.3", + "resolved": "https://registry.npmjs.org/fast-glob/-/fast-glob-3.3.3.tgz", + "integrity": "sha512-7MptL8U0cqcFdzIzwOTHoilX9x5BrNqye7Z/LuC7kCMRio1EMSyqRK3BEAUD7sXRq4iT4AzTVuZdhgQ2TCvYLg==", "dev": true, "license": "MIT", "dependencies": { @@ -3222,79 +3359,22 @@ "@nodelib/fs.walk": "^1.2.3", "glob-parent": "^5.1.2", "merge2": "^1.3.0", - "micromatch": "^4.0.4" + "micromatch": "^4.0.8" }, "engines": { "node": ">=8.6.0" } }, "node_modules/fastq": { - "version": "1.17.1", + "version": "1.19.1", + "resolved": "https://registry.npmjs.org/fastq/-/fastq-1.19.1.tgz", + "integrity": "sha512-GwLTyxkCXjXbxqIhTsMI2Nui8huMPtnxg7krajPJAjnEG/iiOS7i+zCtWGZR9G0NBKbXKh6X9m9UIsYX/N6vvQ==", "dev": true, "license": "ISC", "dependencies": { "reusify": "^1.0.4" } }, - "node_modules/fetch-blob": { - "version": "3.2.0", - "dev": true, - "funding": [ - { - "type": "github", - "url": "https://github.com/sponsors/jimmywarting" - }, - { - "type": "paypal", - "url": "https://paypal.me/jimmywarting" - } - ], - "license": "MIT", - "dependencies": { - "node-domexception": "^1.0.0", - "web-streams-polyfill": "^3.0.3" - }, - "engines": { - "node": "^12.20 || >= 14.13" - } - }, - "node_modules/figures": { - "version": "5.0.0", - "dev": true, - "license": "MIT", - "dependencies": { - "escape-string-regexp": "^5.0.0", - "is-unicode-supported": "^1.2.0" - }, - "engines": { - "node": ">=14" - }, - "funding": { - "url": "https://github.com/sponsors/sindresorhus" - } - }, - "node_modules/figures/node_modules/escape-string-regexp": { - "version": "5.0.0", - "dev": true, - "license": "MIT", - "engines": { - "node": ">=12" - }, - "funding": { - "url": "https://github.com/sponsors/sindresorhus" - } - }, - "node_modules/figures/node_modules/is-unicode-supported": { - "version": "1.3.0", - "dev": true, - "license": "MIT", - "engines": { - "node": ">=12" - }, - "funding": { - "url": "https://github.com/sponsors/sindresorhus" - } - }, "node_modules/filename-reserved-regex": { "version": "2.0.0", "dev": true, @@ -3320,7 +3400,9 @@ } }, "node_modules/fill-range": { - "version": "7.0.1", + "version": "7.1.1", + "resolved": "https://registry.npmjs.org/fill-range/-/fill-range-7.1.1.tgz", + "integrity": "sha512-YsGpe3WHLK8ZYi4tWDg2Jy3ebRz2rXowDxnld4bkQB00cc/1Zw9AWnC0i9ztDJitivtQvaI9KaLyKrc+hBW0yg==", "dev": true, "license": "MIT", "dependencies": { @@ -3405,14 +3487,6 @@ "flat": "cli.js" } }, - "node_modules/for-each": { - "version": "0.3.3", - "dev": true, - "license": "MIT", - "dependencies": { - "is-callable": "^1.1.3" - } - }, "node_modules/foreground-child": { "version": "2.0.0", "dev": true, @@ -3425,25 +3499,6 @@ "node": ">=8.0.0" } }, - "node_modules/form-data-encoder": { - "version": "2.1.4", - "dev": true, - "license": "MIT", - "engines": { - "node": ">= 14.17" - } - }, - "node_modules/formdata-polyfill": { - "version": "4.0.10", - "dev": true, - "license": "MIT", - "dependencies": { - "fetch-blob": "^3.1.2" - }, - "engines": { - "node": ">=12.20.0" - } - }, "node_modules/forwarded": { "version": "0.2.0", "resolved": "https://registry.npmjs.org/forwarded/-/forwarded-0.2.0.tgz", @@ -3509,31 +3564,6 @@ "url": "https://github.com/sponsors/ljharb" } }, - "node_modules/function.prototype.name": { - "version": "1.1.6", - "dev": true, - "license": "MIT", - "dependencies": { - "call-bind": "^1.0.2", - "define-properties": "^1.2.0", - "es-abstract": "^1.22.1", - "functions-have-names": "^1.2.3" - }, - "engines": { - "node": ">= 0.4" - }, - "funding": { - "url": "https://github.com/sponsors/ljharb" - } - }, - "node_modules/functions-have-names": { - "version": "1.2.3", - "dev": true, - "license": "MIT", - "funding": { - "url": "https://github.com/sponsors/ljharb" - } - }, "node_modules/gensync": { "version": "1.0.0-beta.2", "dev": true, @@ -3551,7 +3581,9 @@ } }, "node_modules/get-east-asian-width": { - "version": "1.2.0", + "version": "1.3.0", + "resolved": "https://registry.npmjs.org/get-east-asian-width/-/get-east-asian-width-1.3.0.tgz", + "integrity": "sha512-vpeMIQKxczTD/0s2CdEWHcb0eeJe6TFjxb+J5xgX7hScxqrGuyjmv4c1D4A/gelKfyox0gJJwIHF+fLjeaM8kQ==", "dev": true, "license": "MIT", "engines": { @@ -3594,6 +3626,8 @@ }, "node_modules/get-stream": { "version": "8.0.1", + "resolved": "https://registry.npmjs.org/get-stream/-/get-stream-8.0.1.tgz", + "integrity": "sha512-VaUJspBffn/LMCJVoMvSAdmscJyS1auj5Zulnn5UoYcY531UWmdwhRWkcGKnGU93m5HSXP9LP2usOryrBtQowA==", "dev": true, "license": "MIT", "engines": { @@ -3603,21 +3637,6 @@ "url": "https://github.com/sponsors/sindresorhus" } }, - "node_modules/get-symbol-description": { - "version": "1.0.0", - "dev": true, - "license": "MIT", - "dependencies": { - "call-bind": "^1.0.2", - "get-intrinsic": "^1.1.1" - }, - "engines": { - "node": ">= 0.4" - }, - "funding": { - "url": "https://github.com/sponsors/ljharb" - } - }, "node_modules/get-tsconfig": { "version": "4.7.2", "dev": true, @@ -3630,56 +3649,20 @@ } }, "node_modules/get-uri": { - "version": "6.0.2", + "version": "6.0.4", + "resolved": "https://registry.npmjs.org/get-uri/-/get-uri-6.0.4.tgz", + "integrity": "sha512-E1b1lFFLvLgak2whF2xDBcOy6NLVGZBqqjJjsIhvopKfWWEi64pLVTWWehV8KlLerZkfNTA95sTe2OdJKm1OzQ==", "dev": true, "license": "MIT", "dependencies": { "basic-ftp": "^5.0.2", - "data-uri-to-buffer": "^6.0.0", - "debug": "^4.3.4", - "fs-extra": "^8.1.0" + "data-uri-to-buffer": "^6.0.2", + "debug": "^4.3.4" }, "engines": { "node": ">= 14" } }, - "node_modules/get-uri/node_modules/data-uri-to-buffer": { - "version": "6.0.1", - "dev": true, - "license": "MIT", - "engines": { - "node": ">= 14" - } - }, - "node_modules/get-uri/node_modules/fs-extra": { - "version": "8.1.0", - "dev": true, - "license": "MIT", - "dependencies": { - "graceful-fs": "^4.2.0", - "jsonfile": "^4.0.0", - "universalify": "^0.1.0" - }, - "engines": { - "node": ">=6 <7 || >=8" - } - }, - "node_modules/get-uri/node_modules/jsonfile": { - "version": "4.0.0", - "dev": true, - "license": "MIT", - "optionalDependencies": { - "graceful-fs": "^4.1.6" - } - }, - "node_modules/get-uri/node_modules/universalify": { - "version": "0.1.2", - "dev": true, - "license": "MIT", - "engines": { - "node": ">= 4.0.0" - } - }, "node_modules/gh-pages": { "version": "6.1.1", "dev": true, @@ -3701,21 +3684,43 @@ "node": ">=10" } }, + "node_modules/giget": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/giget/-/giget-2.0.0.tgz", + "integrity": "sha512-L5bGsVkxJbJgdnwyuheIunkGatUF/zssUoxxjACCseZYAVbaqdh9Tsmmlkl8vYan09H7sbvKt4pS8GqKLBrEzA==", + "dev": true, + "license": "MIT", + "dependencies": { + "citty": "^0.1.6", + "consola": "^3.4.0", + "defu": "^6.1.4", + "node-fetch-native": "^1.6.6", + "nypm": "^0.6.0", + "pathe": "^2.0.3" + }, + "bin": { + "giget": "dist/cli.mjs" + } + }, "node_modules/git-up": { - "version": "7.0.0", + "version": "8.1.1", + "resolved": "https://registry.npmjs.org/git-up/-/git-up-8.1.1.tgz", + "integrity": "sha512-FDenSF3fVqBYSaJoYy1KSc2wosx0gCvKP+c+PRBht7cAaiCeQlBtfBDX9vgnNOHmdePlSFITVcn4pFfcgNvx3g==", "dev": true, "license": "MIT", "dependencies": { "is-ssh": "^1.4.0", - "parse-url": "^8.1.0" + "parse-url": "^9.2.0" } }, "node_modules/git-url-parse": { - "version": "14.0.0", + "version": "16.1.0", + "resolved": "https://registry.npmjs.org/git-url-parse/-/git-url-parse-16.1.0.tgz", + "integrity": "sha512-cPLz4HuK86wClEW7iDdeAKcCVlWXmrLpb2L+G9goW0Z1dtpNS6BXXSOckUTlJT/LDQViE1QZKstNORzHsLnobw==", "dev": true, "license": "MIT", "dependencies": { - "git-up": "^7.0.0" + "git-up": "^8.1.0" } }, "node_modules/glob": { @@ -3748,20 +3753,6 @@ "node": ">= 6" } }, - "node_modules/global-dirs": { - "version": "3.0.1", - "dev": true, - "license": "MIT", - "dependencies": { - "ini": "2.0.0" - }, - "engines": { - "node": ">=10" - }, - "funding": { - "url": "https://github.com/sponsors/sindresorhus" - } - }, "node_modules/globals": { "version": "11.12.0", "dev": true, @@ -3770,20 +3761,6 @@ "node": ">=4" } }, - "node_modules/globalthis": { - "version": "1.0.3", - "dev": true, - "license": "MIT", - "dependencies": { - "define-properties": "^1.1.3" - }, - "engines": { - "node": ">= 0.4" - }, - "funding": { - "url": "https://github.com/sponsors/ljharb" - } - }, "node_modules/globby": { "version": "6.1.0", "dev": true, @@ -3812,56 +3789,13 @@ "url": "https://github.com/sponsors/ljharb" } }, - "node_modules/got": { - "version": "13.0.0", + "node_modules/graceful-fs": { + "version": "4.2.11", "dev": true, - "license": "MIT", - "dependencies": { - "@sindresorhus/is": "^5.2.0", - "@szmarczak/http-timer": "^5.0.1", - "cacheable-lookup": "^7.0.0", - "cacheable-request": "^10.2.8", - "decompress-response": "^6.0.0", - "form-data-encoder": "^2.1.2", - "get-stream": "^6.0.1", - "http2-wrapper": "^2.1.10", - "lowercase-keys": "^3.0.0", - "p-cancelable": "^3.0.0", - "responselike": "^3.0.0" - }, - "engines": { - "node": ">=16" - }, - "funding": { - "url": "https://github.com/sindresorhus/got?sponsor=1" - } + "license": "ISC" }, - "node_modules/got/node_modules/get-stream": { - "version": "6.0.1", - "dev": true, - "license": "MIT", - "engines": { - "node": ">=10" - }, - "funding": { - "url": "https://github.com/sponsors/sindresorhus" - } - }, - "node_modules/graceful-fs": { - "version": "4.2.11", - "dev": true, - "license": "ISC" - }, - "node_modules/has-bigints": { - "version": "1.0.2", - "dev": true, - "license": "MIT", - "funding": { - "url": "https://github.com/sponsors/ljharb" - } - }, - "node_modules/has-flag": { - "version": "4.0.0", + "node_modules/has-flag": { + "version": "4.0.0", "dev": true, "license": "MIT", "engines": { @@ -3881,17 +3815,6 @@ "url": "https://github.com/sponsors/ljharb" } }, - "node_modules/has-proto": { - "version": "1.0.1", - "dev": true, - "license": "MIT", - "engines": { - "node": ">= 0.4" - }, - "funding": { - "url": "https://github.com/sponsors/ljharb" - } - }, "node_modules/has-symbols": { "version": "1.1.0", "resolved": "https://registry.npmjs.org/has-symbols/-/has-symbols-1.1.0.tgz", @@ -3905,20 +3828,6 @@ "url": "https://github.com/sponsors/ljharb" } }, - "node_modules/has-tostringtag": { - "version": "1.0.2", - "dev": true, - "license": "MIT", - "dependencies": { - "has-symbols": "^1.0.3" - }, - "engines": { - "node": ">= 0.4" - }, - "funding": { - "url": "https://github.com/sponsors/ljharb" - } - }, "node_modules/hasha": { "version": "5.2.2", "dev": true, @@ -3960,10 +3869,25 @@ "dev": true, "license": "MIT" }, - "node_modules/http-cache-semantics": { - "version": "4.1.1", + "node_modules/htmlparser2": { + "version": "9.1.0", + "resolved": "https://registry.npmjs.org/htmlparser2/-/htmlparser2-9.1.0.tgz", + "integrity": "sha512-5zfg6mHUoaer/97TxnGpxmbR7zJtPwIYFMZ/H5ucTlPZhKvtum05yiPK3Mgai3a0DyVxv7qYqoweaEd2nrYQzQ==", "dev": true, - "license": "BSD-2-Clause" + "funding": [ + "https://github.com/fb55/htmlparser2?sponsor=1", + { + "type": "github", + "url": "https://github.com/sponsors/fb55" + } + ], + "license": "MIT", + "dependencies": { + "domelementtype": "^2.3.0", + "domhandler": "^5.0.3", + "domutils": "^3.1.0", + "entities": "^4.5.0" + } }, "node_modules/http-errors": { "version": "2.0.0", @@ -3983,7 +3907,9 @@ } }, "node_modules/http-proxy-agent": { - "version": "7.0.0", + "version": "7.0.2", + "resolved": "https://registry.npmjs.org/http-proxy-agent/-/http-proxy-agent-7.0.2.tgz", + "integrity": "sha512-T1gkAiYYDWYx3V5Bmyu7HcfcvL7mUrTWiM6yOfa3PIphViJ/gFPbvidQ+veqSOHci/PxBcDabeUNCzpOODJZig==", "license": "MIT", "dependencies": { "agent-base": "^7.1.0", @@ -3993,23 +3919,13 @@ "node": ">= 14" } }, - "node_modules/http2-wrapper": { - "version": "2.2.1", - "dev": true, - "license": "MIT", - "dependencies": { - "quick-lru": "^5.1.1", - "resolve-alpn": "^1.2.0" - }, - "engines": { - "node": ">=10.19.0" - } - }, "node_modules/https-proxy-agent": { - "version": "7.0.2", + "version": "7.0.6", + "resolved": "https://registry.npmjs.org/https-proxy-agent/-/https-proxy-agent-7.0.6.tgz", + "integrity": "sha512-vK9P5/iUfdl95AI+JVyUuIcVtd4ofvtrOr3HNtM2yxC9bnMbEdp3x01OhQNnjb8IJYi38VlTE3mBXwcfvywuSw==", "license": "MIT", "dependencies": { - "agent-base": "^7.0.2", + "agent-base": "^7.1.2", "debug": "4" }, "engines": { @@ -4018,6 +3934,8 @@ }, "node_modules/human-signals": { "version": "5.0.0", + "resolved": "https://registry.npmjs.org/human-signals/-/human-signals-5.0.0.tgz", + "integrity": "sha512-AXcZb6vzzrFAUE61HnN4mpLqd/cSIwNQjtNWR0euPm6y0iqx3G4gOXaIDdtdDwZmhwe82LA6+zinmW4UBWVePQ==", "dev": true, "license": "Apache-2.0", "engines": { @@ -4035,64 +3953,6 @@ "node": ">=0.10.0" } }, - "node_modules/ieee754": { - "version": "1.2.1", - "dev": true, - "funding": [ - { - "type": "github", - "url": "https://github.com/sponsors/feross" - }, - { - "type": "patreon", - "url": "https://www.patreon.com/feross" - }, - { - "type": "consulting", - "url": "https://feross.org/support" - } - ], - "license": "BSD-3-Clause" - }, - "node_modules/ignore": { - "version": "5.3.1", - "dev": true, - "license": "MIT", - "engines": { - "node": ">= 4" - } - }, - "node_modules/import-fresh": { - "version": "3.3.0", - "dev": true, - "license": "MIT", - "dependencies": { - "parent-module": "^1.0.0", - "resolve-from": "^4.0.0" - }, - "engines": { - "node": ">=6" - }, - "funding": { - "url": "https://github.com/sponsors/sindresorhus" - } - }, - "node_modules/import-fresh/node_modules/resolve-from": { - "version": "4.0.0", - "dev": true, - "license": "MIT", - "engines": { - "node": ">=4" - } - }, - "node_modules/import-lazy": { - "version": "4.0.0", - "dev": true, - "license": "MIT", - "engines": { - "node": ">=8" - } - }, "node_modules/imurmurhash": { "version": "0.1.4", "dev": true, @@ -4124,130 +3984,62 @@ "license": "ISC" }, "node_modules/ini": { - "version": "2.0.0", + "version": "5.0.0", + "resolved": "https://registry.npmjs.org/ini/-/ini-5.0.0.tgz", + "integrity": "sha512-+N0ngpO3e7cRUWOJAS7qw0IZIVc6XPrW4MlFBdD066F2L4k1L6ker3hLqSq7iXxU5tgS4WGkIUElWn5vogAEnw==", "dev": true, "license": "ISC", "engines": { - "node": ">=10" + "node": "^18.17.0 || >=20.5.0" } }, "node_modules/inquirer": { - "version": "9.2.12", + "version": "12.6.0", + "resolved": "https://registry.npmjs.org/inquirer/-/inquirer-12.6.0.tgz", + "integrity": "sha512-3zmmccQd/8o65nPOZJZ+2wqt76Ghw3+LaMrmc6JE/IzcvQhJ1st+QLCOo/iLS85/tILU0myG31a2TAZX0ysAvg==", "dev": true, "license": "MIT", "dependencies": { - "@ljharb/through": "^2.3.11", + "@inquirer/core": "^10.1.10", + "@inquirer/prompts": "^7.5.0", + "@inquirer/type": "^3.0.6", "ansi-escapes": "^4.3.2", - "chalk": "^5.3.0", - "cli-cursor": "^3.1.0", - "cli-width": "^4.1.0", - "external-editor": "^3.1.0", - "figures": "^5.0.0", - "lodash": "^4.17.21", - "mute-stream": "1.0.0", - "ora": "^5.4.1", + "mute-stream": "^2.0.0", "run-async": "^3.0.0", - "rxjs": "^7.8.1", - "string-width": "^4.2.3", - "strip-ansi": "^6.0.1", - "wrap-ansi": "^6.2.0" - }, - "engines": { - "node": ">=14.18.0" - } - }, - "node_modules/inquirer/node_modules/chalk": { - "version": "5.3.0", - "dev": true, - "license": "MIT", - "engines": { - "node": "^12.17.0 || ^14.13 || >=16.0.0" - }, - "funding": { - "url": "https://github.com/chalk/chalk?sponsor=1" - } - }, - "node_modules/inquirer/node_modules/is-interactive": { - "version": "1.0.0", - "dev": true, - "license": "MIT", - "engines": { - "node": ">=8" - } - }, - "node_modules/inquirer/node_modules/ora": { - "version": "5.4.1", - "dev": true, - "license": "MIT", - "dependencies": { - "bl": "^4.1.0", - "chalk": "^4.1.0", - "cli-cursor": "^3.1.0", - "cli-spinners": "^2.5.0", - "is-interactive": "^1.0.0", - "is-unicode-supported": "^0.1.0", - "log-symbols": "^4.1.0", - "strip-ansi": "^6.0.0", - "wcwidth": "^1.0.1" + "rxjs": "^7.8.2" }, "engines": { - "node": ">=10" - }, - "funding": { - "url": "https://github.com/sponsors/sindresorhus" - } - }, - "node_modules/inquirer/node_modules/ora/node_modules/chalk": { - "version": "4.1.2", - "dev": true, - "license": "MIT", - "dependencies": { - "ansi-styles": "^4.1.0", - "supports-color": "^7.1.0" - }, - "engines": { - "node": ">=10" + "node": ">=18" }, - "funding": { - "url": "https://github.com/chalk/chalk?sponsor=1" - } - }, - "node_modules/inquirer/node_modules/supports-color": { - "version": "7.2.0", - "dev": true, - "license": "MIT", - "dependencies": { - "has-flag": "^4.0.0" + "peerDependencies": { + "@types/node": ">=18" }, - "engines": { - "node": ">=8" + "peerDependenciesMeta": { + "@types/node": { + "optional": true + } } }, - "node_modules/internal-slot": { - "version": "1.0.6", + "node_modules/ip-address": { + "version": "9.0.5", + "resolved": "https://registry.npmjs.org/ip-address/-/ip-address-9.0.5.tgz", + "integrity": "sha512-zHtQzGojZXTwZTHQqra+ETKd4Sn3vgi7uBmlPoXVWZqYvuKmtI0l/VZTjqGmJY9x88GGOaZ9+G9ES8hC4T4X8g==", "dev": true, "license": "MIT", "dependencies": { - "get-intrinsic": "^1.2.2", - "hasown": "^2.0.0", - "side-channel": "^1.0.4" + "jsbn": "1.1.0", + "sprintf-js": "^1.1.3" }, "engines": { - "node": ">= 0.4" - } - }, - "node_modules/interpret": { - "version": "1.4.0", - "dev": true, - "license": "MIT", - "engines": { - "node": ">= 0.10" + "node": ">= 12" } }, - "node_modules/ip": { - "version": "1.1.8", + "node_modules/ip-address/node_modules/sprintf-js": { + "version": "1.1.3", + "resolved": "https://registry.npmjs.org/sprintf-js/-/sprintf-js-1.1.3.tgz", + "integrity": "sha512-Oo+0REFV59/rz3gfJNKQiBlwfHaSESl1pcGyABQsnnIfWOFt6JNj5gCog2U6MLZ//IGYD+nA8nI+mTShREReaA==", "dev": true, - "license": "MIT" + "license": "BSD-3-Clause" }, "node_modules/ipaddr.js": { "version": "1.9.1", @@ -4259,52 +4051,6 @@ "node": ">= 0.10" } }, - "node_modules/is-arguments": { - "version": "1.1.1", - "dev": true, - "license": "MIT", - "dependencies": { - "call-bind": "^1.0.2", - "has-tostringtag": "^1.0.0" - }, - "engines": { - "node": ">= 0.4" - }, - "funding": { - "url": "https://github.com/sponsors/ljharb" - } - }, - "node_modules/is-array-buffer": { - "version": "3.0.4", - "dev": true, - "license": "MIT", - "dependencies": { - "call-bind": "^1.0.2", - "get-intrinsic": "^1.2.1" - }, - "engines": { - "node": ">= 0.4" - }, - "funding": { - "url": "https://github.com/sponsors/ljharb" - } - }, - "node_modules/is-arrayish": { - "version": "0.2.1", - "dev": true, - "license": "MIT" - }, - "node_modules/is-bigint": { - "version": "1.0.4", - "dev": true, - "license": "MIT", - "dependencies": { - "has-bigints": "^1.0.1" - }, - "funding": { - "url": "https://github.com/sponsors/ljharb" - } - }, "node_modules/is-binary-path": { "version": "2.1.0", "dev": true, @@ -4316,68 +4062,6 @@ "node": ">=8" } }, - "node_modules/is-boolean-object": { - "version": "1.1.2", - "dev": true, - "license": "MIT", - "dependencies": { - "call-bind": "^1.0.2", - "has-tostringtag": "^1.0.0" - }, - "engines": { - "node": ">= 0.4" - }, - "funding": { - "url": "https://github.com/sponsors/ljharb" - } - }, - "node_modules/is-callable": { - "version": "1.2.7", - "dev": true, - "license": "MIT", - "engines": { - "node": ">= 0.4" - }, - "funding": { - "url": "https://github.com/sponsors/ljharb" - } - }, - "node_modules/is-ci": { - "version": "3.0.1", - "dev": true, - "license": "MIT", - "dependencies": { - "ci-info": "^3.2.0" - }, - "bin": { - "is-ci": "bin.js" - } - }, - "node_modules/is-core-module": { - "version": "2.13.1", - "dev": true, - "license": "MIT", - "dependencies": { - "hasown": "^2.0.0" - }, - "funding": { - "url": "https://github.com/sponsors/ljharb" - } - }, - "node_modules/is-date-object": { - "version": "1.0.5", - "dev": true, - "license": "MIT", - "dependencies": { - "has-tostringtag": "^1.0.0" - }, - "engines": { - "node": ">= 0.4" - }, - "funding": { - "url": "https://github.com/sponsors/ljharb" - } - }, "node_modules/is-docker": { "version": "3.0.0", "license": "MIT", @@ -4418,20 +4102,6 @@ "node": ">=0.10.0" } }, - "node_modules/is-in-ci": { - "version": "0.1.0", - "dev": true, - "license": "MIT", - "bin": { - "is-in-ci": "cli.js" - }, - "engines": { - "node": ">=18" - }, - "funding": { - "url": "https://github.com/sponsors/sindresorhus" - } - }, "node_modules/is-inside-container": { "version": "1.0.0", "license": "MIT", @@ -4448,23 +4118,10 @@ "url": "https://github.com/sponsors/sindresorhus" } }, - "node_modules/is-installed-globally": { - "version": "0.4.0", - "dev": true, - "license": "MIT", - "dependencies": { - "global-dirs": "^3.0.0", - "is-path-inside": "^3.0.2" - }, - "engines": { - "node": ">=10" - }, - "funding": { - "url": "https://github.com/sponsors/sindresorhus" - } - }, "node_modules/is-interactive": { "version": "2.0.0", + "resolved": "https://registry.npmjs.org/is-interactive/-/is-interactive-2.0.0.tgz", + "integrity": "sha512-qP1vozQRI+BMOPcjFzrjXuQvdak2pHNUMZoeG2eRbiSqyvbEf/wQtEOTOX1guk6E3t36RkaqiSt8A/6YElNxLQ==", "dev": true, "license": "MIT", "engines": { @@ -4474,74 +4131,16 @@ "url": "https://github.com/sponsors/sindresorhus" } }, - "node_modules/is-map": { - "version": "2.0.2", - "dev": true, - "license": "MIT", - "funding": { - "url": "https://github.com/sponsors/ljharb" - } - }, - "node_modules/is-negative-zero": { - "version": "2.0.2", - "dev": true, - "license": "MIT", - "engines": { - "node": ">= 0.4" - }, - "funding": { - "url": "https://github.com/sponsors/ljharb" - } - }, - "node_modules/is-npm": { - "version": "6.0.0", - "dev": true, - "license": "MIT", - "engines": { - "node": "^12.20.0 || ^14.13.1 || >=16.0.0" - }, - "funding": { - "url": "https://github.com/sponsors/sindresorhus" - } - }, "node_modules/is-number": { "version": "7.0.0", + "resolved": "https://registry.npmjs.org/is-number/-/is-number-7.0.0.tgz", + "integrity": "sha512-41Cifkg6e8TylSpdtTpeLVMqvSBEVzTttHvERD741+pnZ8ANv0004MRL43QKPDlK9cGvNp6NZWZUBlbGXYxxng==", "dev": true, "license": "MIT", "engines": { "node": ">=0.12.0" } }, - "node_modules/is-number-object": { - "version": "1.0.7", - "dev": true, - "license": "MIT", - "dependencies": { - "has-tostringtag": "^1.0.0" - }, - "engines": { - "node": ">= 0.4" - }, - "funding": { - "url": "https://github.com/sponsors/ljharb" - } - }, - "node_modules/is-obj": { - "version": "2.0.0", - "dev": true, - "license": "MIT", - "engines": { - "node": ">=8" - } - }, - "node_modules/is-path-inside": { - "version": "3.0.3", - "dev": true, - "license": "MIT", - "engines": { - "node": ">=8" - } - }, "node_modules/is-plain-obj": { "version": "2.1.0", "dev": true, @@ -4550,42 +4149,10 @@ "node": ">=8" } }, - "node_modules/is-regex": { - "version": "1.1.4", - "dev": true, - "license": "MIT", - "dependencies": { - "call-bind": "^1.0.2", - "has-tostringtag": "^1.0.0" - }, - "engines": { - "node": ">= 0.4" - }, - "funding": { - "url": "https://github.com/sponsors/ljharb" - } - }, - "node_modules/is-set": { - "version": "2.0.2", - "dev": true, - "license": "MIT", - "funding": { - "url": "https://github.com/sponsors/ljharb" - } - }, - "node_modules/is-shared-array-buffer": { - "version": "1.0.2", - "dev": true, - "license": "MIT", - "dependencies": { - "call-bind": "^1.0.2" - }, - "funding": { - "url": "https://github.com/sponsors/ljharb" - } - }, "node_modules/is-ssh": { - "version": "1.4.0", + "version": "1.4.1", + "resolved": "https://registry.npmjs.org/is-ssh/-/is-ssh-1.4.1.tgz", + "integrity": "sha512-JNeu1wQsHjyHgn9NcWTaXq6zWSR6hqE0++zhfZlkFBbScNkyvxCdeV8sRkSBaeLKxmbpR21brail63ACNxJ0Tg==", "dev": true, "license": "MIT", "dependencies": { @@ -4603,48 +4170,6 @@ "url": "https://github.com/sponsors/sindresorhus" } }, - "node_modules/is-string": { - "version": "1.0.7", - "dev": true, - "license": "MIT", - "dependencies": { - "has-tostringtag": "^1.0.0" - }, - "engines": { - "node": ">= 0.4" - }, - "funding": { - "url": "https://github.com/sponsors/ljharb" - } - }, - "node_modules/is-symbol": { - "version": "1.0.4", - "dev": true, - "license": "MIT", - "dependencies": { - "has-symbols": "^1.0.2" - }, - "engines": { - "node": ">= 0.4" - }, - "funding": { - "url": "https://github.com/sponsors/ljharb" - } - }, - "node_modules/is-typed-array": { - "version": "1.1.13", - "dev": true, - "license": "MIT", - "dependencies": { - "which-typed-array": "^1.1.14" - }, - "engines": { - "node": ">= 0.4" - }, - "funding": { - "url": "https://github.com/sponsors/ljharb" - } - }, "node_modules/is-typedarray": { "version": "1.0.0", "dev": true, @@ -4661,17 +4186,6 @@ "url": "https://github.com/sponsors/sindresorhus" } }, - "node_modules/is-weakref": { - "version": "1.0.2", - "dev": true, - "license": "MIT", - "dependencies": { - "call-bind": "^1.0.2" - }, - "funding": { - "url": "https://github.com/sponsors/ljharb" - } - }, "node_modules/is-windows": { "version": "1.0.2", "dev": true, @@ -4693,18 +4207,15 @@ "url": "https://github.com/sponsors/sindresorhus" } }, - "node_modules/isarray": { - "version": "2.0.5", - "dev": true, - "license": "MIT" - }, "node_modules/isexe": { "version": "2.0.0", "dev": true, "license": "ISC" }, "node_modules/issue-parser": { - "version": "6.0.0", + "version": "7.0.1", + "resolved": "https://registry.npmjs.org/issue-parser/-/issue-parser-7.0.1.tgz", + "integrity": "sha512-3YZcUUR2Wt1WsapF+S/WiA2WmlW0cWAoPccMqne7AxEBhCdFeTPjfv/Axb8V2gyCgY3nRw+ksZ3xSUX+R47iAg==", "dev": true, "license": "MIT", "dependencies": { @@ -4715,7 +4226,7 @@ "lodash.uniqby": "^4.7.0" }, "engines": { - "node": ">=10.13" + "node": "^18.17 || >=20.6.1" } }, "node_modules/istanbul-lib-coverage": { @@ -4860,24 +4371,14 @@ "node": ">=8" } }, - "node_modules/iterate-iterator": { - "version": "1.0.2", - "dev": true, - "license": "MIT", - "funding": { - "url": "https://github.com/sponsors/ljharb" - } - }, - "node_modules/iterate-value": { - "version": "1.0.2", + "node_modules/jiti": { + "version": "2.4.2", + "resolved": "https://registry.npmjs.org/jiti/-/jiti-2.4.2.tgz", + "integrity": "sha512-rg9zJN+G4n2nfJl5MW3BMygZX56zKPNVEYYqq7adpmMh4Jn2QNEwhvQlFy6jPVdcod7txZtKHWnyZiA3a0zP7A==", "dev": true, "license": "MIT", - "dependencies": { - "es-get-iterator": "^1.0.2", - "iterate-iterator": "^1.0.1" - }, - "funding": { - "url": "https://github.com/sponsors/ljharb" + "bin": { + "jiti": "lib/jiti-cli.mjs" } }, "node_modules/js-tokens": { @@ -4896,6 +4397,13 @@ "js-yaml": "bin/js-yaml.js" } }, + "node_modules/jsbn": { + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/jsbn/-/jsbn-1.1.0.tgz", + "integrity": "sha512-4bYVV3aAMtDTTu4+xsDYa6sy9GyJ69/amsu9sYF2zqjiEoZA5xJi3BrfX3uY+/IekIu7MwdObdbDWpoZdBv3/A==", + "dev": true, + "license": "MIT" + }, "node_modules/jsesc": { "version": "2.5.2", "dev": true, @@ -4907,16 +4415,6 @@ "node": ">=4" } }, - "node_modules/json-buffer": { - "version": "3.0.1", - "dev": true, - "license": "MIT" - }, - "node_modules/json-parse-even-better-errors": { - "version": "2.3.1", - "dev": true, - "license": "MIT" - }, "node_modules/json5": { "version": "2.2.3", "dev": true, @@ -4990,47 +4488,20 @@ "license": "MIT", "dependencies": { "buffer-equal-constant-time": "1.0.1", - "ecdsa-sig-formatter": "1.0.11", - "safe-buffer": "^5.0.1" - } - }, - "node_modules/jws": { - "version": "3.2.2", - "resolved": "https://registry.npmjs.org/jws/-/jws-3.2.2.tgz", - "integrity": "sha512-YHlZCB6lMTllWDtSPHz/ZXTsi8S00usEV6v1tjq8tOUZzw7DpSDWVXjXDre6ed1w/pd495ODpHZYSdkRTsa0HA==", - "license": "MIT", - "dependencies": { - "jwa": "^1.4.1", - "safe-buffer": "^5.0.1" - } - }, - "node_modules/keyv": { - "version": "4.5.4", - "dev": true, - "license": "MIT", - "dependencies": { - "json-buffer": "3.0.1" + "ecdsa-sig-formatter": "1.0.11", + "safe-buffer": "^5.0.1" } }, - "node_modules/latest-version": { - "version": "7.0.0", - "dev": true, + "node_modules/jws": { + "version": "3.2.2", + "resolved": "https://registry.npmjs.org/jws/-/jws-3.2.2.tgz", + "integrity": "sha512-YHlZCB6lMTllWDtSPHz/ZXTsi8S00usEV6v1tjq8tOUZzw7DpSDWVXjXDre6ed1w/pd495ODpHZYSdkRTsa0HA==", "license": "MIT", "dependencies": { - "package-json": "^8.1.0" - }, - "engines": { - "node": ">=14.16" - }, - "funding": { - "url": "https://github.com/sponsors/sindresorhus" + "jwa": "^1.4.1", + "safe-buffer": "^5.0.1" } }, - "node_modules/lines-and-columns": { - "version": "1.2.4", - "dev": true, - "license": "MIT" - }, "node_modules/locate-path": { "version": "6.0.0", "dev": true, @@ -5047,16 +4518,29 @@ }, "node_modules/lodash": { "version": "4.17.21", + "resolved": "https://registry.npmjs.org/lodash/-/lodash-4.17.21.tgz", + "integrity": "sha512-v2kDEe57lecTulaDIuNTPy3Ry4gLGJ6Z1O3vE1krgXZNrsQ+LFTGHVxVjcXPs17LhbZVGedAJv8XZ1tvj5FvSg==", + "dev": true, + "license": "MIT" + }, + "node_modules/lodash-es": { + "version": "4.17.21", + "resolved": "https://registry.npmjs.org/lodash-es/-/lodash-es-4.17.21.tgz", + "integrity": "sha512-mKnC+QJ9pWVzv+C4/U3rRsHapFfHvQFoFB92e52xeyGMcX6/OlIl78je1u8vePzYZSkkogMPJ2yjxxsb89cxyw==", "dev": true, "license": "MIT" }, "node_modules/lodash.capitalize": { "version": "4.2.1", + "resolved": "https://registry.npmjs.org/lodash.capitalize/-/lodash.capitalize-4.2.1.tgz", + "integrity": "sha512-kZzYOKspf8XVX5AvmQF94gQW0lejFVgb80G85bU4ZWzoJ6C03PQg3coYAUpSTpQWelrZELd3XWgHzw4Ck5kaIw==", "dev": true, "license": "MIT" }, "node_modules/lodash.escaperegexp": { "version": "4.1.2", + "resolved": "https://registry.npmjs.org/lodash.escaperegexp/-/lodash.escaperegexp-4.1.2.tgz", + "integrity": "sha512-TM9YBvyC84ZxE3rgfefxUWiQKLilstD6k7PTGt6wfbtXF8ixIJLOL3VYyV/z+ZiPLsVxAsKAFVwWlWeb2Y8Yyw==", "dev": true, "license": "MIT" }, @@ -5102,6 +4586,13 @@ "version": "4.0.1", "license": "MIT" }, + "node_modules/lodash.merge": { + "version": "4.6.2", + "resolved": "https://registry.npmjs.org/lodash.merge/-/lodash.merge-4.6.2.tgz", + "integrity": "sha512-0KpjqXRVvrYyCsX1swR/XTK0va6VQkQM6MNo7PqW77ByjAhoARA8EfrP1N4+KlKj8YS0ZUCtRT/YUuhyYDujIQ==", + "dev": true, + "license": "MIT" + }, "node_modules/lodash.once": { "version": "4.1.1", "resolved": "https://registry.npmjs.org/lodash.once/-/lodash.once-4.1.1.tgz", @@ -5110,6 +4601,8 @@ }, "node_modules/lodash.uniqby": { "version": "4.7.0", + "resolved": "https://registry.npmjs.org/lodash.uniqby/-/lodash.uniqby-4.7.0.tgz", + "integrity": "sha512-e/zcLx6CSbmaEgFHCA7BnoQKyCtKMxnuWrJygbwPs/AIn+IMKl66L8/s+wBUn5LRw2pZx3bUHibiV1b6aTWIww==", "dev": true, "license": "MIT" }, @@ -5128,17 +4621,6 @@ "url": "https://github.com/sponsors/sindresorhus" } }, - "node_modules/lowercase-keys": { - "version": "3.0.0", - "dev": true, - "license": "MIT", - "engines": { - "node": "^12.20.0 || ^14.13.1 || >=16.0.0" - }, - "funding": { - "url": "https://github.com/sponsors/sindresorhus" - } - }, "node_modules/lru-cache": { "version": "5.1.1", "dev": true, @@ -5153,7 +4635,9 @@ "license": "MIT" }, "node_modules/macos-release": { - "version": "3.2.0", + "version": "3.3.0", + "resolved": "https://registry.npmjs.org/macos-release/-/macos-release-3.3.0.tgz", + "integrity": "sha512-tPJQ1HeyiU2vRruNGhZ+VleWuMQRro8iFtJxYgnS4NQe+EukKF6aGiIT+7flZhISAt2iaXBCfFGvAyif7/f8nQ==", "dev": true, "license": "MIT", "engines": { @@ -5217,11 +4701,15 @@ }, "node_modules/merge-stream": { "version": "2.0.0", + "resolved": "https://registry.npmjs.org/merge-stream/-/merge-stream-2.0.0.tgz", + "integrity": "sha512-abv/qOcuPfk3URPfDzmZU1LKmuw8kT+0nIHvKrKgFrwifol/doWcdA4ZqsWQ8ENrFKkd67Mfpo/LovbIUsbt3w==", "dev": true, "license": "MIT" }, "node_modules/merge2": { "version": "1.4.1", + "resolved": "https://registry.npmjs.org/merge2/-/merge2-1.4.1.tgz", + "integrity": "sha512-8q7VEgMJW4J8tcfVPy8g09NcQwZdbwFEqhe/WZkoIzjn/3TGDwtOCYtXGxA3O8tPzpczCCDgv+P2P5y00ZJOOg==", "dev": true, "license": "MIT", "engines": { @@ -5239,11 +4727,13 @@ } }, "node_modules/micromatch": { - "version": "4.0.5", + "version": "4.0.8", + "resolved": "https://registry.npmjs.org/micromatch/-/micromatch-4.0.8.tgz", + "integrity": "sha512-PXwfBhYu0hBCPw8Dn0E+WDYb7af3dSLVWKi3HGv84IdF4TyFoC0ysxFd0Goxw7nSv4T/PzEJQxsYsEiFCKo2BA==", "dev": true, "license": "MIT", "dependencies": { - "braces": "^3.0.2", + "braces": "^3.0.3", "picomatch": "^2.3.1" }, "engines": { @@ -5284,6 +4774,8 @@ }, "node_modules/mimic-fn": { "version": "4.0.0", + "resolved": "https://registry.npmjs.org/mimic-fn/-/mimic-fn-4.0.0.tgz", + "integrity": "sha512-vqiC06CuhBTUdZH+RYl8sFrL096vA45Ok5ISO6sE/Mr1jRbGH4Csnhi8f3wKVl7x8mO4Au7Ir9D3Oyv1VYMFJw==", "dev": true, "license": "MIT", "engines": { @@ -5293,12 +4785,14 @@ "url": "https://github.com/sponsors/sindresorhus" } }, - "node_modules/mimic-response": { - "version": "4.0.0", + "node_modules/mimic-function": { + "version": "5.0.1", + "resolved": "https://registry.npmjs.org/mimic-function/-/mimic-function-5.0.1.tgz", + "integrity": "sha512-VP79XUPxV2CigYP3jWwAUFSku2aKqBH7uTAapFWCBqutsbmDo96KY5o8uh6U+/YSIn5OxJnXp73beVkpqMIGhA==", "dev": true, "license": "MIT", "engines": { - "node": "^12.20.0 || ^14.13.1 || >=16.0.0" + "node": ">=18" }, "funding": { "url": "https://github.com/sponsors/sindresorhus" @@ -5315,14 +4809,6 @@ "node": "*" } }, - "node_modules/minimist": { - "version": "1.2.8", - "dev": true, - "license": "MIT", - "funding": { - "url": "https://github.com/sponsors/ljharb" - } - }, "node_modules/mocha": { "version": "10.2.0", "dev": true, @@ -5416,11 +4902,13 @@ "license": "MIT" }, "node_modules/mute-stream": { - "version": "1.0.0", + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/mute-stream/-/mute-stream-2.0.0.tgz", + "integrity": "sha512-WWdIxpyjEn+FhQJQQv9aQAYlHoNVdzIzUySNV1gHUPDSdZJ3yZn7pAAbQcV7B56Mvu881q9FZV+0Vx2xC44VWA==", "dev": true, "license": "ISC", "engines": { - "node": "^14.17.0 || ^16.13.0 || >=18.0.0" + "node": "^18.17.0 || >=20.5.0" } }, "node_modules/nanoid": { @@ -5446,6 +4934,8 @@ }, "node_modules/netmask": { "version": "2.0.2", + "resolved": "https://registry.npmjs.org/netmask/-/netmask-2.0.2.tgz", + "integrity": "sha512-dBpDMdxv9Irdq66304OLfEmQ9tbNRFnFTuZiLo+bD+r332bBmMJ8GBLXklIXXgxd3+v9+KUnZaUR5PJMa75Gsg==", "dev": true, "license": "MIT", "engines": { @@ -5454,6 +4944,8 @@ }, "node_modules/new-github-release-url": { "version": "2.0.0", + "resolved": "https://registry.npmjs.org/new-github-release-url/-/new-github-release-url-2.0.0.tgz", + "integrity": "sha512-NHDDGYudnvRutt/VhKFlX26IotXe1w0cmkDm6JGquh5bz/bDTw0LufSmH/GxTjEdpHEO+bVKFTwdrcGa/9XlKQ==", "dev": true, "license": "MIT", "dependencies": { @@ -5468,6 +4960,8 @@ }, "node_modules/new-github-release-url/node_modules/type-fest": { "version": "2.19.0", + "resolved": "https://registry.npmjs.org/type-fest/-/type-fest-2.19.0.tgz", + "integrity": "sha512-RAH822pAdBgcNMAfWnCBU3CFZcfZ/i1eZjwFU/dsLKumyuuP3niueg2UAukXYF0E2AAoc82ZSSf9J0WQBinzHA==", "dev": true, "license": "(MIT OR CC0-1.0)", "engines": { @@ -5489,40 +4983,12 @@ "path-to-regexp": "^6.2.1" } }, - "node_modules/node-domexception": { - "version": "1.0.0", - "dev": true, - "funding": [ - { - "type": "github", - "url": "https://github.com/sponsors/jimmywarting" - }, - { - "type": "github", - "url": "https://paypal.me/jimmywarting" - } - ], - "license": "MIT", - "engines": { - "node": ">=10.5.0" - } - }, - "node_modules/node-fetch": { - "version": "3.3.2", + "node_modules/node-fetch-native": { + "version": "1.6.6", + "resolved": "https://registry.npmjs.org/node-fetch-native/-/node-fetch-native-1.6.6.tgz", + "integrity": "sha512-8Mc2HhqPdlIfedsuZoc3yioPuzp6b+L5jRCRY1QzuWZh2EGJVQrGppC6V6cF0bLdbW0+O2YpqCA25aF/1lvipQ==", "dev": true, - "license": "MIT", - "dependencies": { - "data-uri-to-buffer": "^4.0.0", - "fetch-blob": "^3.1.4", - "formdata-polyfill": "^4.0.10" - }, - "engines": { - "node": "^12.20.0 || ^14.13.1 || >=16.0.0" - }, - "funding": { - "type": "opencollective", - "url": "https://opencollective.com/node-fetch" - } + "license": "MIT" }, "node_modules/node-preload": { "version": "0.2.1", @@ -5548,19 +5014,10 @@ "node": ">=0.10.0" } }, - "node_modules/normalize-url": { - "version": "8.0.0", - "dev": true, - "license": "MIT", - "engines": { - "node": ">=14.16" - }, - "funding": { - "url": "https://github.com/sponsors/sindresorhus" - } - }, "node_modules/npm-run-path": { - "version": "5.2.0", + "version": "5.3.0", + "resolved": "https://registry.npmjs.org/npm-run-path/-/npm-run-path-5.3.0.tgz", + "integrity": "sha512-ppwTtiJZq0O/ai0z7yfudtBpWIoxM8yE6nHi1X47eFR2EWORqfbu6CnPlNsjeN683eT0qG6H/Pyf9fCcvjnnnQ==", "dev": true, "license": "MIT", "dependencies": { @@ -5575,6 +5032,8 @@ }, "node_modules/npm-run-path/node_modules/path-key": { "version": "4.0.0", + "resolved": "https://registry.npmjs.org/path-key/-/path-key-4.0.0.tgz", + "integrity": "sha512-haREypq7xkM7ErfgIyA0z+Bj4AGKlMSdlQE2jvJo6huWD1EdkKYV+G/T4nq0YEF2vgTT8kqMFKo1uHn950r4SQ==", "dev": true, "license": "MIT", "engines": { @@ -5584,6 +5043,19 @@ "url": "https://github.com/sponsors/sindresorhus" } }, + "node_modules/nth-check": { + "version": "2.1.1", + "resolved": "https://registry.npmjs.org/nth-check/-/nth-check-2.1.1.tgz", + "integrity": "sha512-lqjrjmaOoAnWfMmBPL+XNnynZh2+swxiX3WUE0s4yEHI6m+AwrK2UZOimIRl3X/4QctVqS8AiZjFqyOGrMXb/w==", + "dev": true, + "license": "BSD-2-Clause", + "dependencies": { + "boolbase": "^1.0.0" + }, + "funding": { + "url": "https://github.com/fb55/nth-check?sponsor=1" + } + }, "node_modules/nyc": { "version": "15.1.0", "dev": true, @@ -5720,47 +5192,56 @@ "node": ">=6" } }, - "node_modules/object-assign": { - "version": "4.1.1", + "node_modules/nypm": { + "version": "0.6.0", + "resolved": "https://registry.npmjs.org/nypm/-/nypm-0.6.0.tgz", + "integrity": "sha512-mn8wBFV9G9+UFHIrq+pZ2r2zL4aPau/by3kJb3cM7+5tQHMt6HGQB8FDIeKFYp8o0D2pnH6nVsO88N4AmUxIWg==", "dev": true, "license": "MIT", + "dependencies": { + "citty": "^0.1.6", + "consola": "^3.4.0", + "pathe": "^2.0.3", + "pkg-types": "^2.0.0", + "tinyexec": "^0.3.2" + }, + "bin": { + "nypm": "dist/cli.mjs" + }, "engines": { - "node": ">=0.10.0" + "node": "^14.16.0 || >=16.10.0" } }, - "node_modules/object-inspect": { - "version": "1.13.1", + "node_modules/nypm/node_modules/tinyexec": { + "version": "0.3.2", + "resolved": "https://registry.npmjs.org/tinyexec/-/tinyexec-0.3.2.tgz", + "integrity": "sha512-KQQR9yN7R5+OSwaK0XQoj22pwHoTlgYqmUscPYoknOoWCWfj/5/ABTMRi69FrKU5ffPVh5QcFikpWJI/P1ocHA==", "dev": true, - "license": "MIT", - "funding": { - "url": "https://github.com/sponsors/ljharb" - } + "license": "MIT" }, - "node_modules/object-keys": { - "version": "1.1.1", + "node_modules/object-assign": { + "version": "4.1.1", "dev": true, "license": "MIT", "engines": { - "node": ">= 0.4" + "node": ">=0.10.0" } }, - "node_modules/object.assign": { - "version": "4.1.5", + "node_modules/object-inspect": { + "version": "1.13.1", "dev": true, "license": "MIT", - "dependencies": { - "call-bind": "^1.0.5", - "define-properties": "^1.2.1", - "has-symbols": "^1.0.3", - "object-keys": "^1.1.1" - }, - "engines": { - "node": ">= 0.4" - }, "funding": { "url": "https://github.com/sponsors/ljharb" } }, + "node_modules/ohash": { + "version": "2.0.11", + "resolved": "https://registry.npmjs.org/ohash/-/ohash-2.0.11.tgz", + "integrity": "sha512-RdR9FQrFwNBNXAr4GixM8YaRZRJ5PUWbKYbE5eOsrwAjJW0q2REGcf79oYPsLyskQCZG1PLN+S/K1V00joZAoQ==", + "dev": true, + "license": "MIT" + }, "node_modules/on-finished": { "version": "2.4.1", "resolved": "https://registry.npmjs.org/on-finished/-/on-finished-2.4.1.tgz", @@ -5793,22 +5274,25 @@ } }, "node_modules/onetime": { - "version": "6.0.0", + "version": "7.0.0", + "resolved": "https://registry.npmjs.org/onetime/-/onetime-7.0.0.tgz", + "integrity": "sha512-VXJjc87FScF88uafS3JllDgvAm+c/Slfz06lorj2uAY34rlUu0Nt+v8wreiImcrgAjjIHp1rXpTDlLOGw29WwQ==", "dev": true, "license": "MIT", "dependencies": { - "mimic-fn": "^4.0.0" + "mimic-function": "^5.0.0" }, "engines": { - "node": ">=12" + "node": ">=18" }, "funding": { "url": "https://github.com/sponsors/sindresorhus" } }, "node_modules/open": { - "version": "10.0.3", - "dev": true, + "version": "10.1.2", + "resolved": "https://registry.npmjs.org/open/-/open-10.1.2.tgz", + "integrity": "sha512-cxN6aIDPz6rm8hbebcP7vrQNhvRcveZoJU72Y7vskh4oIm+BZwBECnx5nTmrlres1Qapvx27Qo1Auukpf8PKXw==", "license": "MIT", "dependencies": { "default-browser": "^5.2.1", @@ -5824,18 +5308,20 @@ } }, "node_modules/ora": { - "version": "8.0.1", + "version": "8.2.0", + "resolved": "https://registry.npmjs.org/ora/-/ora-8.2.0.tgz", + "integrity": "sha512-weP+BZ8MVNnlCm8c0Qdc1WSWq4Qn7I+9CJGm7Qali6g44e/PUzbjNqJX5NJ9ljlNMosfJvg1fKEGILklK9cwnw==", "dev": true, "license": "MIT", "dependencies": { "chalk": "^5.3.0", - "cli-cursor": "^4.0.0", + "cli-cursor": "^5.0.0", "cli-spinners": "^2.9.2", "is-interactive": "^2.0.0", "is-unicode-supported": "^2.0.0", "log-symbols": "^6.0.0", - "stdin-discarder": "^0.2.1", - "string-width": "^7.0.0", + "stdin-discarder": "^0.2.2", + "string-width": "^7.2.0", "strip-ansi": "^7.1.0" }, "engines": { @@ -5846,7 +5332,9 @@ } }, "node_modules/ora/node_modules/ansi-regex": { - "version": "6.0.1", + "version": "6.1.0", + "resolved": "https://registry.npmjs.org/ansi-regex/-/ansi-regex-6.1.0.tgz", + "integrity": "sha512-7HSX4QQb4CspciLpVFwyRe79O3xsIZDDLER21kERQ71oaPodF8jL725AgJMFAYbooIqolJoRLuM81SpeUkpkvA==", "dev": true, "license": "MIT", "engines": { @@ -5857,7 +5345,9 @@ } }, "node_modules/ora/node_modules/chalk": { - "version": "5.3.0", + "version": "5.4.1", + "resolved": "https://registry.npmjs.org/chalk/-/chalk-5.4.1.tgz", + "integrity": "sha512-zgVZuo2WcZgfUEmsn6eO3kINexW8RAE4maiQ8QNs8CtpPCSyMiYsULR3HQYkm3w8FIA3SberyMJMSldGsW+U3w==", "dev": true, "license": "MIT", "engines": { @@ -5867,27 +5357,17 @@ "url": "https://github.com/chalk/chalk?sponsor=1" } }, - "node_modules/ora/node_modules/cli-cursor": { - "version": "4.0.0", - "dev": true, - "license": "MIT", - "dependencies": { - "restore-cursor": "^4.0.0" - }, - "engines": { - "node": "^12.20.0 || ^14.13.1 || >=16.0.0" - }, - "funding": { - "url": "https://github.com/sponsors/sindresorhus" - } - }, "node_modules/ora/node_modules/emoji-regex": { - "version": "10.3.0", + "version": "10.4.0", + "resolved": "https://registry.npmjs.org/emoji-regex/-/emoji-regex-10.4.0.tgz", + "integrity": "sha512-EC+0oUMY1Rqm4O6LLrgjtYDvcVYTy7chDnM4Q7030tP4Kwj3u/pR6gP9ygnp2CJMK5Gq+9Q2oqmrFJAz01DXjw==", "dev": true, "license": "MIT" }, "node_modules/ora/node_modules/is-unicode-supported": { - "version": "2.0.0", + "version": "2.1.0", + "resolved": "https://registry.npmjs.org/is-unicode-supported/-/is-unicode-supported-2.1.0.tgz", + "integrity": "sha512-mE00Gnza5EEB3Ds0HfMyllZzbBrmLOX3vfWoj9A9PEnTfratQ/BcaJOuMhnkhjXvb2+FkY3VuHqtAGpTPmglFQ==", "dev": true, "license": "MIT", "engines": { @@ -5899,6 +5379,8 @@ }, "node_modules/ora/node_modules/log-symbols": { "version": "6.0.0", + "resolved": "https://registry.npmjs.org/log-symbols/-/log-symbols-6.0.0.tgz", + "integrity": "sha512-i24m8rpwhmPIS4zscNzK6MSEhk0DUWa/8iYQWxhffV8jkI4Phvs3F+quL5xvS0gdQR0FyTCMMH33Y78dDTzzIw==", "dev": true, "license": "MIT", "dependencies": { @@ -5914,6 +5396,8 @@ }, "node_modules/ora/node_modules/log-symbols/node_modules/is-unicode-supported": { "version": "1.3.0", + "resolved": "https://registry.npmjs.org/is-unicode-supported/-/is-unicode-supported-1.3.0.tgz", + "integrity": "sha512-43r2mRvz+8JRIKnWJ+3j8JtjRKZ6GmjzfaE/qiBJnikNnYv/6bagRJ1kUhNk8R5EX/GkobD+r+sfxCPJsiKBLQ==", "dev": true, "license": "MIT", "engines": { @@ -5923,45 +5407,10 @@ "url": "https://github.com/sponsors/sindresorhus" } }, - "node_modules/ora/node_modules/mimic-fn": { - "version": "2.1.0", - "dev": true, - "license": "MIT", - "engines": { - "node": ">=6" - } - }, - "node_modules/ora/node_modules/onetime": { - "version": "5.1.2", - "dev": true, - "license": "MIT", - "dependencies": { - "mimic-fn": "^2.1.0" - }, - "engines": { - "node": ">=6" - }, - "funding": { - "url": "https://github.com/sponsors/sindresorhus" - } - }, - "node_modules/ora/node_modules/restore-cursor": { - "version": "4.0.0", - "dev": true, - "license": "MIT", - "dependencies": { - "onetime": "^5.1.0", - "signal-exit": "^3.0.2" - }, - "engines": { - "node": "^12.20.0 || ^14.13.1 || >=16.0.0" - }, - "funding": { - "url": "https://github.com/sponsors/sindresorhus" - } - }, "node_modules/ora/node_modules/string-width": { - "version": "7.1.0", + "version": "7.2.0", + "resolved": "https://registry.npmjs.org/string-width/-/string-width-7.2.0.tgz", + "integrity": "sha512-tsaTIkKW9b4N+AEj+SVA+WhJzV7/zMhcSu78mLKWSk7cXMOSHsBKFWUs0fWwq8QyK3MgJBQRX6Gbi4kYbdvGkQ==", "dev": true, "license": "MIT", "dependencies": { @@ -5978,6 +5427,8 @@ }, "node_modules/ora/node_modules/strip-ansi": { "version": "7.1.0", + "resolved": "https://registry.npmjs.org/strip-ansi/-/strip-ansi-7.1.0.tgz", + "integrity": "sha512-iq6eVVI64nQQTRYq2KtEg2d2uU7LElhTJwsH4YzIHZshxlgZms/wIc4VoDQTlG/IvVIrBKG06CrZnp0qv7hkcQ==", "dev": true, "license": "MIT", "dependencies": { @@ -5991,15 +5442,17 @@ } }, "node_modules/os-name": { - "version": "5.1.0", + "version": "6.0.0", + "resolved": "https://registry.npmjs.org/os-name/-/os-name-6.0.0.tgz", + "integrity": "sha512-bv608E0UX86atYi2GMGjDe0vF/X1TJjemNS8oEW6z22YW1Rc3QykSYoGfkQbX0zZX9H0ZB6CQP/3GTf1I5hURg==", "dev": true, "license": "MIT", "dependencies": { - "macos-release": "^3.1.0", - "windows-release": "^5.0.1" + "macos-release": "^3.2.0", + "windows-release": "^6.0.0" }, "engines": { - "node": "^12.20.0 || ^14.13.1 || >=16.0.0" + "node": ">=18" }, "funding": { "url": "https://github.com/sponsors/sindresorhus" @@ -6007,20 +5460,14 @@ }, "node_modules/os-tmpdir": { "version": "1.0.2", + "resolved": "https://registry.npmjs.org/os-tmpdir/-/os-tmpdir-1.0.2.tgz", + "integrity": "sha512-D2FR03Vir7FIu45XBY20mTb+/ZSWB00sjU9jdQXt83gDrI4Ztz5Fs7/yy74g2N5SVQY4xY1qDr4rNddwYRVX0g==", "dev": true, "license": "MIT", "engines": { "node": ">=0.10.0" } }, - "node_modules/p-cancelable": { - "version": "3.0.0", - "dev": true, - "license": "MIT", - "engines": { - "node": ">=12.20" - } - }, "node_modules/p-limit": { "version": "3.1.0", "dev": true, @@ -6069,174 +5516,128 @@ } }, "node_modules/pac-proxy-agent": { - "version": "7.0.1", + "version": "7.2.0", + "resolved": "https://registry.npmjs.org/pac-proxy-agent/-/pac-proxy-agent-7.2.0.tgz", + "integrity": "sha512-TEB8ESquiLMc0lV8vcd5Ql/JAKAoyzHFXaStwjkzpOpC5Yv+pIzLfHvjTSdf3vpa2bMiUQrg9i6276yn8666aA==", "dev": true, "license": "MIT", "dependencies": { "@tootallnate/quickjs-emscripten": "^0.23.0", - "agent-base": "^7.0.2", + "agent-base": "^7.1.2", "debug": "^4.3.4", "get-uri": "^6.0.1", "http-proxy-agent": "^7.0.0", - "https-proxy-agent": "^7.0.2", - "pac-resolver": "^7.0.0", - "socks-proxy-agent": "^8.0.2" - }, - "engines": { - "node": ">= 14" - } - }, - "node_modules/pac-resolver": { - "version": "7.0.0", - "dev": true, - "license": "MIT", - "dependencies": { - "degenerator": "^5.0.0", - "ip": "^1.1.8", - "netmask": "^2.0.2" - }, - "engines": { - "node": ">= 14" - } - }, - "node_modules/package-hash": { - "version": "4.0.0", - "dev": true, - "license": "ISC", - "dependencies": { - "graceful-fs": "^4.1.15", - "hasha": "^5.0.0", - "lodash.flattendeep": "^4.4.0", - "release-zalgo": "^1.0.0" - }, - "engines": { - "node": ">=8" - } - }, - "node_modules/package-json": { - "version": "8.1.1", - "dev": true, - "license": "MIT", - "dependencies": { - "got": "^12.1.0", - "registry-auth-token": "^5.0.1", - "registry-url": "^6.0.0", - "semver": "^7.3.7" - }, - "engines": { - "node": ">=14.16" - }, - "funding": { - "url": "https://github.com/sponsors/sindresorhus" - } - }, - "node_modules/package-json/node_modules/get-stream": { - "version": "6.0.1", - "dev": true, - "license": "MIT", - "engines": { - "node": ">=10" - }, - "funding": { - "url": "https://github.com/sponsors/sindresorhus" - } - }, - "node_modules/package-json/node_modules/got": { - "version": "12.6.1", - "dev": true, - "license": "MIT", - "dependencies": { - "@sindresorhus/is": "^5.2.0", - "@szmarczak/http-timer": "^5.0.1", - "cacheable-lookup": "^7.0.0", - "cacheable-request": "^10.2.8", - "decompress-response": "^6.0.0", - "form-data-encoder": "^2.1.2", - "get-stream": "^6.0.1", - "http2-wrapper": "^2.1.10", - "lowercase-keys": "^3.0.0", - "p-cancelable": "^3.0.0", - "responselike": "^3.0.0" + "https-proxy-agent": "^7.0.6", + "pac-resolver": "^7.0.1", + "socks-proxy-agent": "^8.0.5" }, "engines": { - "node": ">=14.16" - }, - "funding": { - "url": "https://github.com/sindresorhus/got?sponsor=1" + "node": ">= 14" } }, - "node_modules/package-json/node_modules/lru-cache": { - "version": "6.0.0", + "node_modules/pac-resolver": { + "version": "7.0.1", + "resolved": "https://registry.npmjs.org/pac-resolver/-/pac-resolver-7.0.1.tgz", + "integrity": "sha512-5NPgf87AT2STgwa2ntRMr45jTKrYBGkVU36yT0ig/n/GMAa3oPqhZfIQ2kMEimReg0+t9kZViDVZ83qfVUlckg==", "dev": true, - "license": "ISC", + "license": "MIT", "dependencies": { - "yallist": "^4.0.0" + "degenerator": "^5.0.0", + "netmask": "^2.0.2" }, "engines": { - "node": ">=10" + "node": ">= 14" } }, - "node_modules/package-json/node_modules/semver": { - "version": "7.5.4", + "node_modules/package-hash": { + "version": "4.0.0", "dev": true, "license": "ISC", "dependencies": { - "lru-cache": "^6.0.0" - }, - "bin": { - "semver": "bin/semver.js" + "graceful-fs": "^4.1.15", + "hasha": "^5.0.0", + "lodash.flattendeep": "^4.4.0", + "release-zalgo": "^1.0.0" }, "engines": { - "node": ">=10" + "node": ">=8" } }, - "node_modules/package-json/node_modules/yallist": { - "version": "4.0.0", + "node_modules/parse-path": { + "version": "7.1.0", + "resolved": "https://registry.npmjs.org/parse-path/-/parse-path-7.1.0.tgz", + "integrity": "sha512-EuCycjZtfPcjWk7KTksnJ5xPMvWGA/6i4zrLYhRG0hGvC3GPU/jGUj3Cy+ZR0v30duV3e23R95T1lE2+lsndSw==", "dev": true, - "license": "ISC" + "license": "MIT", + "dependencies": { + "protocols": "^2.0.0" + } }, - "node_modules/parent-module": { - "version": "1.0.1", + "node_modules/parse-url": { + "version": "9.2.0", + "resolved": "https://registry.npmjs.org/parse-url/-/parse-url-9.2.0.tgz", + "integrity": "sha512-bCgsFI+GeGWPAvAiUv63ZorMeif3/U0zaXABGJbOWt5OH2KCaPHF6S+0ok4aqM9RuIPGyZdx9tR9l13PsW4AYQ==", "dev": true, "license": "MIT", "dependencies": { - "callsites": "^3.0.0" + "@types/parse-path": "^7.0.0", + "parse-path": "^7.0.0" }, "engines": { - "node": ">=6" + "node": ">=14.13.0" } }, - "node_modules/parse-json": { - "version": "5.2.0", + "node_modules/parse5": { + "version": "7.3.0", + "resolved": "https://registry.npmjs.org/parse5/-/parse5-7.3.0.tgz", + "integrity": "sha512-IInvU7fabl34qmi9gY8XOVxhYyMyuH2xUNpb2q8/Y+7552KlejkRvqvD19nMoUW/uQGGbqNpA6Tufu5FL5BZgw==", "dev": true, "license": "MIT", "dependencies": { - "@babel/code-frame": "^7.0.0", - "error-ex": "^1.3.1", - "json-parse-even-better-errors": "^2.3.0", - "lines-and-columns": "^1.1.6" - }, - "engines": { - "node": ">=8" + "entities": "^6.0.0" }, "funding": { - "url": "https://github.com/sponsors/sindresorhus" + "url": "https://github.com/inikulin/parse5?sponsor=1" } }, - "node_modules/parse-path": { - "version": "7.0.0", + "node_modules/parse5-htmlparser2-tree-adapter": { + "version": "7.1.0", + "resolved": "https://registry.npmjs.org/parse5-htmlparser2-tree-adapter/-/parse5-htmlparser2-tree-adapter-7.1.0.tgz", + "integrity": "sha512-ruw5xyKs6lrpo9x9rCZqZZnIUntICjQAd0Wsmp396Ul9lN/h+ifgVV1x1gZHi8euej6wTfpqX8j+BFQxF0NS/g==", "dev": true, "license": "MIT", "dependencies": { - "protocols": "^2.0.0" + "domhandler": "^5.0.3", + "parse5": "^7.0.0" + }, + "funding": { + "url": "https://github.com/inikulin/parse5?sponsor=1" } }, - "node_modules/parse-url": { - "version": "8.1.0", + "node_modules/parse5-parser-stream": { + "version": "7.1.2", + "resolved": "https://registry.npmjs.org/parse5-parser-stream/-/parse5-parser-stream-7.1.2.tgz", + "integrity": "sha512-JyeQc9iwFLn5TbvvqACIF/VXG6abODeB3Fwmv/TGdLk2LfbWkaySGY72at4+Ty7EkPZj854u4CrICqNk2qIbow==", "dev": true, "license": "MIT", "dependencies": { - "parse-path": "^7.0.0" + "parse5": "^7.0.0" + }, + "funding": { + "url": "https://github.com/inikulin/parse5?sponsor=1" + } + }, + "node_modules/parse5/node_modules/entities": { + "version": "6.0.0", + "resolved": "https://registry.npmjs.org/entities/-/entities-6.0.0.tgz", + "integrity": "sha512-aKstq2TDOndCn4diEyp9Uq/Flu2i1GlLkc6XIDQSDMuaFE3OPW5OphLCyQ5SpSJZTb4reN+kTcYru5yIfXoRPw==", + "dev": true, + "license": "BSD-2-Clause", + "engines": { + "node": ">=0.12" + }, + "funding": { + "url": "https://github.com/fb55/entities?sponsor=1" } }, "node_modules/parseurl": { @@ -6273,26 +5674,24 @@ "node": ">=8" } }, - "node_modules/path-parse": { - "version": "1.0.7", + "node_modules/path-to-regexp": { + "version": "6.2.1", "dev": true, "license": "MIT" }, - "node_modules/path-to-regexp": { - "version": "6.2.1", + "node_modules/pathe": { + "version": "2.0.3", + "resolved": "https://registry.npmjs.org/pathe/-/pathe-2.0.3.tgz", + "integrity": "sha512-WUjGcAqP1gQacoQe+OBJsFA7Ld4DyXuUIjZ5cc75cLHvJ7dtNsTugphxIADwspS+AraAUePCKrSVtPLFj/F88w==", "dev": true, "license": "MIT" }, - "node_modules/path-type": { - "version": "5.0.0", + "node_modules/perfect-debounce": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/perfect-debounce/-/perfect-debounce-1.0.0.tgz", + "integrity": "sha512-xCy9V055GLEqoFaHoC1SoLIaLmWctgCUaBaWxDZ7/Zx4CTyX7cJQLJOok/orfjZAh9kEYpjJa4d0KcJmCbctZA==", "dev": true, - "license": "MIT", - "engines": { - "node": ">=12" - }, - "funding": { - "url": "https://github.com/sponsors/sindresorhus" - } + "license": "MIT" }, "node_modules/picocolors": { "version": "1.0.0", @@ -6396,43 +5795,33 @@ "node": ">=8" } }, - "node_modules/process-on-spawn": { - "version": "1.0.0", + "node_modules/pkg-types": { + "version": "2.1.0", + "resolved": "https://registry.npmjs.org/pkg-types/-/pkg-types-2.1.0.tgz", + "integrity": "sha512-wmJwA+8ihJixSoHKxZJRBQG1oY8Yr9pGLzRmSsNms0iNWyHHAlZCa7mmKiFR10YPZuz/2k169JiS/inOjBCZ2A==", "dev": true, "license": "MIT", "dependencies": { - "fromentries": "^1.2.0" - }, - "engines": { - "node": ">=8" + "confbox": "^0.2.1", + "exsolve": "^1.0.1", + "pathe": "^2.0.3" } }, - "node_modules/promise.allsettled": { - "version": "1.0.7", + "node_modules/process-on-spawn": { + "version": "1.0.0", "dev": true, "license": "MIT", "dependencies": { - "array.prototype.map": "^1.0.5", - "call-bind": "^1.0.2", - "define-properties": "^1.2.0", - "es-abstract": "^1.22.1", - "get-intrinsic": "^1.2.1", - "iterate-value": "^1.0.2" + "fromentries": "^1.2.0" }, "engines": { - "node": ">= 0.4" - }, - "funding": { - "url": "https://github.com/sponsors/ljharb" + "node": ">=8" } }, - "node_modules/proto-list": { - "version": "1.2.4", - "dev": true, - "license": "ISC" - }, "node_modules/protocols": { - "version": "2.0.1", + "version": "2.0.2", + "resolved": "https://registry.npmjs.org/protocols/-/protocols-2.0.2.tgz", + "integrity": "sha512-hHVTzba3wboROl0/aWRRG9dMytgH6ow//STBZh43l/wQgmMhYhOFi0EHWAPtoCz9IAUymsyP0TSBHkhgMEGNnQ==", "dev": true, "license": "MIT" }, @@ -6451,18 +5840,20 @@ } }, "node_modules/proxy-agent": { - "version": "6.3.1", + "version": "6.5.0", + "resolved": "https://registry.npmjs.org/proxy-agent/-/proxy-agent-6.5.0.tgz", + "integrity": "sha512-TmatMXdr2KlRiA2CyDu8GqR8EjahTG3aY3nXjdzFyoZbmB8hrBsTyMezhULIXKnC0jpfjlmiZ3+EaCzoInSu/A==", "dev": true, "license": "MIT", "dependencies": { - "agent-base": "^7.0.2", + "agent-base": "^7.1.2", "debug": "^4.3.4", - "http-proxy-agent": "^7.0.0", - "https-proxy-agent": "^7.0.2", + "http-proxy-agent": "^7.0.1", + "https-proxy-agent": "^7.0.6", "lru-cache": "^7.14.1", - "pac-proxy-agent": "^7.0.1", + "pac-proxy-agent": "^7.1.0", "proxy-from-env": "^1.1.0", - "socks-proxy-agent": "^8.0.2" + "socks-proxy-agent": "^8.0.5" }, "engines": { "node": ">= 14" @@ -6470,6 +5861,8 @@ }, "node_modules/proxy-agent/node_modules/lru-cache": { "version": "7.18.3", + "resolved": "https://registry.npmjs.org/lru-cache/-/lru-cache-7.18.3.tgz", + "integrity": "sha512-jumlc0BIUrS3qJGgIkWZsyfAM7NCWiBcCDhnd+3NNM5KbBmLTgHVfWBcg6W+rLUsIpzpERPsvwUP7CckAQSOoA==", "dev": true, "license": "ISC", "engines": { @@ -6478,23 +5871,11 @@ }, "node_modules/proxy-from-env": { "version": "1.1.0", + "resolved": "https://registry.npmjs.org/proxy-from-env/-/proxy-from-env-1.1.0.tgz", + "integrity": "sha512-D+zkORCbA9f1tdWRK0RaCR3GPv50cMxcrz4X8k5LTSUD1Dkw47mKJEZQNunItRTkWwgtaUSo1RVFRIG9ZXiFYg==", "dev": true, "license": "MIT" }, - "node_modules/pupa": { - "version": "3.1.0", - "dev": true, - "license": "MIT", - "dependencies": { - "escape-goat": "^4.0.0" - }, - "engines": { - "node": ">=12.20" - }, - "funding": { - "url": "https://github.com/sponsors/sindresorhus" - } - }, "node_modules/qs": { "version": "6.13.0", "resolved": "https://registry.npmjs.org/qs/-/qs-6.13.0.tgz", @@ -6513,6 +5894,8 @@ }, "node_modules/queue-microtask": { "version": "1.2.3", + "resolved": "https://registry.npmjs.org/queue-microtask/-/queue-microtask-1.2.3.tgz", + "integrity": "sha512-NuaNSa6flKT5JaSYQzJok04JzTL1CA6aGhv5rfLW3PgqA+M2ChpZQnAC8h8i4ZFkBS8X5RqkDBHA7r4hej3K9A==", "dev": true, "funding": [ { @@ -6530,17 +5913,6 @@ ], "license": "MIT" }, - "node_modules/quick-lru": { - "version": "5.1.1", - "dev": true, - "license": "MIT", - "engines": { - "node": ">=10" - }, - "funding": { - "url": "https://github.com/sponsors/sindresorhus" - } - }, "node_modules/random-bytes": { "version": "1.0.0", "resolved": "https://registry.npmjs.org/random-bytes/-/random-bytes-1.0.0.tgz", @@ -6585,44 +5957,15 @@ "node": ">= 0.8" } }, - "node_modules/rc": { - "version": "1.2.8", - "dev": true, - "license": "(BSD-2-Clause OR MIT OR Apache-2.0)", - "dependencies": { - "deep-extend": "^0.6.0", - "ini": "~1.3.0", - "minimist": "^1.2.0", - "strip-json-comments": "~2.0.1" - }, - "bin": { - "rc": "cli.js" - } - }, - "node_modules/rc/node_modules/ini": { - "version": "1.3.8", - "dev": true, - "license": "ISC" - }, - "node_modules/rc/node_modules/strip-json-comments": { - "version": "2.0.1", - "dev": true, - "license": "MIT", - "engines": { - "node": ">=0.10.0" - } - }, - "node_modules/readable-stream": { - "version": "3.6.2", + "node_modules/rc9": { + "version": "2.1.2", + "resolved": "https://registry.npmjs.org/rc9/-/rc9-2.1.2.tgz", + "integrity": "sha512-btXCnMmRIBINM2LDZoEmOogIZU7Qe7zn4BpomSKZ/ykbLObuBdvG+mFq11DL6fjH1DRwHhrlgtYWG96bJiC7Cg==", "dev": true, "license": "MIT", "dependencies": { - "inherits": "^2.0.3", - "string_decoder": "^1.1.1", - "util-deprecate": "^1.0.1" - }, - "engines": { - "node": ">= 6" + "defu": "^6.1.4", + "destr": "^2.0.3" } }, "node_modules/readdirp": { @@ -6636,63 +5979,14 @@ "node": ">=8.10.0" } }, - "node_modules/rechoir": { - "version": "0.6.2", - "dev": true, - "dependencies": { - "resolve": "^1.1.6" - }, - "engines": { - "node": ">= 0.10" - } - }, "node_modules/redis": { "resolved": "packages/redis", "link": true }, - "node_modules/regexp.prototype.flags": { - "version": "1.5.1", - "dev": true, - "license": "MIT", - "dependencies": { - "call-bind": "^1.0.2", - "define-properties": "^1.2.0", - "set-function-name": "^2.0.0" - }, - "engines": { - "node": ">= 0.4" - }, - "funding": { - "url": "https://github.com/sponsors/ljharb" - } - }, - "node_modules/registry-auth-token": { - "version": "5.0.2", - "dev": true, - "license": "MIT", - "dependencies": { - "@pnpm/npm-conf": "^2.1.0" - }, - "engines": { - "node": ">=14" - } - }, - "node_modules/registry-url": { - "version": "6.0.1", - "dev": true, - "license": "MIT", - "dependencies": { - "rc": "1.2.8" - }, - "engines": { - "node": ">=12" - }, - "funding": { - "url": "https://github.com/sponsors/sindresorhus" - } - }, "node_modules/release-it": { - "version": "17.0.3", + "version": "19.0.2", + "resolved": "https://registry.npmjs.org/release-it/-/release-it-19.0.2.tgz", + "integrity": "sha512-tGRCcKeXNOMrK9Qe+ZIgQiMlQgjV8PLxZjTq1XGlCk5u1qPgx+Pps0i8HIt667FDt0wLjFtvn5o9ItpitKnVUA==", "dev": true, "funding": [ { @@ -6706,89 +6000,68 @@ ], "license": "MIT", "dependencies": { - "@iarna/toml": "2.2.5", - "@octokit/rest": "20.0.2", + "@nodeutils/defaults-deep": "1.1.0", + "@octokit/rest": "21.1.1", + "@phun-ky/typeof": "1.2.8", "async-retry": "1.3.3", - "chalk": "5.3.0", - "cosmiconfig": "9.0.0", - "execa": "8.0.1", - "git-url-parse": "14.0.0", - "globby": "14.0.0", - "got": "13.0.0", - "inquirer": "9.2.12", - "is-ci": "3.0.1", - "issue-parser": "6.0.0", - "lodash": "4.17.21", - "mime-types": "2.1.35", + "c12": "3.0.3", + "ci-info": "^4.2.0", + "eta": "3.5.0", + "git-url-parse": "16.1.0", + "inquirer": "12.6.0", + "issue-parser": "7.0.1", + "lodash.get": "4.4.2", + "lodash.merge": "4.6.2", + "mime-types": "3.0.1", "new-github-release-url": "2.0.0", - "node-fetch": "3.3.2", - "open": "10.0.3", - "ora": "8.0.1", - "os-name": "5.1.0", - "promise.allsettled": "1.0.7", - "proxy-agent": "6.3.1", - "semver": "7.5.4", - "shelljs": "0.8.5", - "update-notifier": "7.0.0", + "open": "10.1.2", + "ora": "8.2.0", + "os-name": "6.0.0", + "proxy-agent": "6.5.0", + "semver": "7.7.1", + "tinyexec": "1.0.1", + "tinyglobby": "0.2.13", + "undici": "6.21.2", "url-join": "5.0.0", - "wildcard-match": "5.1.2", + "wildcard-match": "5.1.4", "yargs-parser": "21.1.1" }, "bin": { "release-it": "bin/release-it.js" }, "engines": { - "node": ">=18" + "node": "^20.12.0 || >=22.0.0" } }, - "node_modules/release-it/node_modules/chalk": { - "version": "5.3.0", + "node_modules/release-it/node_modules/mime-db": { + "version": "1.54.0", + "resolved": "https://registry.npmjs.org/mime-db/-/mime-db-1.54.0.tgz", + "integrity": "sha512-aU5EJuIN2WDemCcAp2vFBfp/m4EAhWJnUNSSw0ixs7/kXbd6Pg64EmwJkNdFhB8aWt1sH2CTXrLxo/iAGV3oPQ==", "dev": true, "license": "MIT", "engines": { - "node": "^12.17.0 || ^14.13 || >=16.0.0" - }, - "funding": { - "url": "https://github.com/chalk/chalk?sponsor=1" + "node": ">= 0.6" } }, - "node_modules/release-it/node_modules/globby": { - "version": "14.0.0", + "node_modules/release-it/node_modules/mime-types": { + "version": "3.0.1", + "resolved": "https://registry.npmjs.org/mime-types/-/mime-types-3.0.1.tgz", + "integrity": "sha512-xRc4oEhT6eaBpU1XF7AjpOFD+xQmXNB5OVKwp4tqCuBpHLS/ZbBDrc07mYTDqVMg6PfxUjjNp85O6Cd2Z/5HWA==", "dev": true, "license": "MIT", "dependencies": { - "@sindresorhus/merge-streams": "^1.0.0", - "fast-glob": "^3.3.2", - "ignore": "^5.2.4", - "path-type": "^5.0.0", - "slash": "^5.1.0", - "unicorn-magic": "^0.1.0" - }, - "engines": { - "node": ">=18" - }, - "funding": { - "url": "https://github.com/sponsors/sindresorhus" - } - }, - "node_modules/release-it/node_modules/lru-cache": { - "version": "6.0.0", - "dev": true, - "license": "ISC", - "dependencies": { - "yallist": "^4.0.0" + "mime-db": "^1.54.0" }, "engines": { - "node": ">=10" + "node": ">= 0.6" } }, "node_modules/release-it/node_modules/semver": { - "version": "7.5.4", + "version": "7.7.1", + "resolved": "https://registry.npmjs.org/semver/-/semver-7.7.1.tgz", + "integrity": "sha512-hlq8tAfn0m/61p4BVRcPzIGr6LKiMwo4VM6dGi6pt4qcRkmNzTcWq6eCEjEh+qXjkMDvPlOFFSGwQjoEa6gyMA==", "dev": true, "license": "ISC", - "dependencies": { - "lru-cache": "^6.0.0" - }, "bin": { "semver": "bin/semver.js" }, @@ -6796,13 +6069,20 @@ "node": ">=10" } }, - "node_modules/release-it/node_modules/yallist": { - "version": "4.0.0", + "node_modules/release-it/node_modules/undici": { + "version": "6.21.2", + "resolved": "https://registry.npmjs.org/undici/-/undici-6.21.2.tgz", + "integrity": "sha512-uROZWze0R0itiAKVPsYhFov9LxrPMHLMEQFszeI2gCN6bnIIZ8twzBCJcN2LJrBBLfrP0t1FW0g+JmKVl8Vk1g==", "dev": true, - "license": "ISC" + "license": "MIT", + "engines": { + "node": ">=18.17" + } }, "node_modules/release-it/node_modules/yargs-parser": { "version": "21.1.1", + "resolved": "https://registry.npmjs.org/yargs-parser/-/yargs-parser-21.1.1.tgz", + "integrity": "sha512-tVpsJW7DdjecAiFpbIB1e3qxIQsE6NoPc5/eTdrbbIC4h0LVsWhnoa3g+m2HclBIujHzsxZ4VJVA+GUuc2/LBw==", "dev": true, "license": "ISC", "engines": { @@ -6833,27 +6113,6 @@ "dev": true, "license": "ISC" }, - "node_modules/resolve": { - "version": "1.22.8", - "dev": true, - "license": "MIT", - "dependencies": { - "is-core-module": "^2.13.0", - "path-parse": "^1.0.7", - "supports-preserve-symlinks-flag": "^1.0.0" - }, - "bin": { - "resolve": "bin/resolve" - }, - "funding": { - "url": "https://github.com/sponsors/ljharb" - } - }, - "node_modules/resolve-alpn": { - "version": "1.2.1", - "dev": true, - "license": "MIT" - }, "node_modules/resolve-from": { "version": "5.0.0", "dev": true, @@ -6870,56 +6129,40 @@ "url": "https://github.com/privatenumber/resolve-pkg-maps?sponsor=1" } }, - "node_modules/responselike": { - "version": "3.0.0", + "node_modules/restore-cursor": { + "version": "5.1.0", + "resolved": "https://registry.npmjs.org/restore-cursor/-/restore-cursor-5.1.0.tgz", + "integrity": "sha512-oMA2dcrw6u0YfxJQXm342bFKX/E4sG9rbTzO9ptUcR/e8A33cHuvStiYOwH7fszkZlZ1z/ta9AAoPk2F4qIOHA==", "dev": true, "license": "MIT", "dependencies": { - "lowercase-keys": "^3.0.0" + "onetime": "^7.0.0", + "signal-exit": "^4.1.0" }, "engines": { - "node": ">=14.16" + "node": ">=18" }, "funding": { "url": "https://github.com/sponsors/sindresorhus" } }, - "node_modules/restore-cursor": { - "version": "3.1.0", - "dev": true, - "license": "MIT", - "dependencies": { - "onetime": "^5.1.0", - "signal-exit": "^3.0.2" - }, - "engines": { - "node": ">=8" - } - }, - "node_modules/restore-cursor/node_modules/mimic-fn": { - "version": "2.1.0", - "dev": true, - "license": "MIT", - "engines": { - "node": ">=6" - } - }, - "node_modules/restore-cursor/node_modules/onetime": { - "version": "5.1.2", + "node_modules/restore-cursor/node_modules/signal-exit": { + "version": "4.1.0", + "resolved": "https://registry.npmjs.org/signal-exit/-/signal-exit-4.1.0.tgz", + "integrity": "sha512-bzyZ1e88w9O1iNJbKnOlvYTrWPDl46O1bG0D3XInv+9tkPrxrN8jUUTiFlDkkmKWgn1M6CfIA13SuGqOa9Korw==", "dev": true, - "license": "MIT", - "dependencies": { - "mimic-fn": "^2.1.0" - }, + "license": "ISC", "engines": { - "node": ">=6" + "node": ">=14" }, "funding": { - "url": "https://github.com/sponsors/sindresorhus" + "url": "https://github.com/sponsors/isaacs" } }, "node_modules/retry": { "version": "0.13.1", + "resolved": "https://registry.npmjs.org/retry/-/retry-0.13.1.tgz", + "integrity": "sha512-XQBQ3I8W1Cge0Seh+6gjj03LbmRFWuoszgK9ooCpwYIrhhoO80pfq4cUkU5DkknwfOfFteRwlZ56PYOGYyFWdg==", "dev": true, "license": "MIT", "engines": { @@ -6927,7 +6170,9 @@ } }, "node_modules/reusify": { - "version": "1.0.4", + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/reusify/-/reusify-1.1.0.tgz", + "integrity": "sha512-g6QUff04oZpHs0eG5p83rFLhHeV00ug/Yf9nZM6fLeUrPguBTkTQOdpAWWspMh55TZfVQDPaN3NQJfbVRAxdIw==", "dev": true, "license": "MIT", "engines": { @@ -6961,6 +6206,8 @@ }, "node_modules/run-async": { "version": "3.0.0", + "resolved": "https://registry.npmjs.org/run-async/-/run-async-3.0.0.tgz", + "integrity": "sha512-540WwVDOMxA6dN6We19EcT9sc3hkXPw5mzRNGM3FkdN/vtE9NFvj5lFAPNwUDmJjXidm3v7TC1cTE7t17Ulm1Q==", "dev": true, "license": "MIT", "engines": { @@ -6969,6 +6216,8 @@ }, "node_modules/run-parallel": { "version": "1.2.0", + "resolved": "https://registry.npmjs.org/run-parallel/-/run-parallel-1.2.0.tgz", + "integrity": "sha512-5l4VyZR86LZ/lDxZTR6jqL8AFE2S0IFLMP26AbjsLVADxHdhB/c0GUsH+y39UfCi3dzz8OlQuPmnaJOMoDHQBA==", "dev": true, "funding": [ { @@ -6990,30 +6239,15 @@ } }, "node_modules/rxjs": { - "version": "7.8.1", + "version": "7.8.2", + "resolved": "https://registry.npmjs.org/rxjs/-/rxjs-7.8.2.tgz", + "integrity": "sha512-dhKf903U/PQZY6boNNtAGdWbG85WAbjT/1xYoZIC7FAY0yWapOBQVsVrDl58W86//e1VpMNBtRV4MaXfdMySFA==", "dev": true, "license": "Apache-2.0", "dependencies": { "tslib": "^2.1.0" } }, - "node_modules/safe-array-concat": { - "version": "1.1.0", - "dev": true, - "license": "MIT", - "dependencies": { - "call-bind": "^1.0.5", - "get-intrinsic": "^1.2.2", - "has-symbols": "^1.0.3", - "isarray": "^2.0.5" - }, - "engines": { - "node": ">=0.4" - }, - "funding": { - "url": "https://github.com/sponsors/ljharb" - } - }, "node_modules/safe-buffer": { "version": "5.2.1", "funding": [ @@ -7032,22 +6266,6 @@ ], "license": "MIT" }, - "node_modules/safe-regex-test": { - "version": "1.0.2", - "dev": true, - "license": "MIT", - "dependencies": { - "call-bind": "^1.0.5", - "get-intrinsic": "^1.2.2", - "is-regex": "^1.1.4" - }, - "engines": { - "node": ">= 0.4" - }, - "funding": { - "url": "https://github.com/sponsors/ljharb" - } - }, "node_modules/safer-buffer": { "version": "2.1.2", "dev": true, @@ -7061,50 +6279,6 @@ "semver": "bin/semver.js" } }, - "node_modules/semver-diff": { - "version": "4.0.0", - "dev": true, - "license": "MIT", - "dependencies": { - "semver": "^7.3.5" - }, - "engines": { - "node": ">=12" - }, - "funding": { - "url": "https://github.com/sponsors/sindresorhus" - } - }, - "node_modules/semver-diff/node_modules/lru-cache": { - "version": "6.0.0", - "dev": true, - "license": "ISC", - "dependencies": { - "yallist": "^4.0.0" - }, - "engines": { - "node": ">=10" - } - }, - "node_modules/semver-diff/node_modules/semver": { - "version": "7.5.4", - "dev": true, - "license": "ISC", - "dependencies": { - "lru-cache": "^6.0.0" - }, - "bin": { - "semver": "bin/semver.js" - }, - "engines": { - "node": ">=10" - } - }, - "node_modules/semver-diff/node_modules/yallist": { - "version": "4.0.0", - "dev": true, - "license": "ISC" - }, "node_modules/send": { "version": "0.19.0", "resolved": "https://registry.npmjs.org/send/-/send-0.19.0.tgz", @@ -7204,19 +6378,6 @@ "node": ">= 0.4" } }, - "node_modules/set-function-name": { - "version": "2.0.1", - "dev": true, - "license": "MIT", - "dependencies": { - "define-data-property": "^1.0.1", - "functions-have-names": "^1.2.3", - "has-property-descriptors": "^1.0.0" - }, - "engines": { - "node": ">= 0.4" - } - }, "node_modules/setprototypeof": { "version": "1.2.0", "resolved": "https://registry.npmjs.org/setprototypeof/-/setprototypeof-1.2.0.tgz", @@ -7243,22 +6404,6 @@ "node": ">=8" } }, - "node_modules/shelljs": { - "version": "0.8.5", - "dev": true, - "license": "BSD-3-Clause", - "dependencies": { - "glob": "^7.0.0", - "interpret": "^1.0.0", - "rechoir": "^0.6.2" - }, - "bin": { - "shjs": "bin/shjs" - }, - "engines": { - "node": ">=4" - } - }, "node_modules/shiki": { "version": "0.14.7", "dev": true, @@ -7330,19 +6475,10 @@ "node": ">=8" } }, - "node_modules/slash": { - "version": "5.1.0", - "dev": true, - "license": "MIT", - "engines": { - "node": ">=14.16" - }, - "funding": { - "url": "https://github.com/sponsors/sindresorhus" - } - }, "node_modules/smart-buffer": { "version": "4.2.0", + "resolved": "https://registry.npmjs.org/smart-buffer/-/smart-buffer-4.2.0.tgz", + "integrity": "sha512-94hK0Hh8rPqQl2xXc3HsaBoOXKV20MToPkcXvwbISWLEs+64sBq5kFgn2kJDHb1Pry9yrP0dxrCI9RRci7RXKg==", "dev": true, "license": "MIT", "engines": { @@ -7351,36 +6487,35 @@ } }, "node_modules/socks": { - "version": "2.7.1", + "version": "2.8.4", + "resolved": "https://registry.npmjs.org/socks/-/socks-2.8.4.tgz", + "integrity": "sha512-D3YaD0aRxR3mEcqnidIs7ReYJFVzWdd6fXJYUM8ixcQcJRGTka/b3saV0KflYhyVJXKhb947GndU35SxYNResQ==", "dev": true, "license": "MIT", "dependencies": { - "ip": "^2.0.0", + "ip-address": "^9.0.5", "smart-buffer": "^4.2.0" }, "engines": { - "node": ">= 10.13.0", + "node": ">= 10.0.0", "npm": ">= 3.0.0" } }, "node_modules/socks-proxy-agent": { - "version": "8.0.2", + "version": "8.0.5", + "resolved": "https://registry.npmjs.org/socks-proxy-agent/-/socks-proxy-agent-8.0.5.tgz", + "integrity": "sha512-HehCEsotFqbPW9sJ8WVYB6UbmIMv7kUUORIF2Nncq4VQvBfNBLibW9YZR5dlYCSUhwcD628pRllm7n+E+YTzJw==", "dev": true, "license": "MIT", "dependencies": { - "agent-base": "^7.0.2", + "agent-base": "^7.1.2", "debug": "^4.3.4", - "socks": "^2.7.1" + "socks": "^2.8.3" }, "engines": { "node": ">= 14" } }, - "node_modules/socks/node_modules/ip": { - "version": "2.0.0", - "dev": true, - "license": "MIT" - }, "node_modules/source-map": { "version": "0.6.1", "dev": true, @@ -7422,6 +6557,8 @@ }, "node_modules/stdin-discarder": { "version": "0.2.2", + "resolved": "https://registry.npmjs.org/stdin-discarder/-/stdin-discarder-0.2.2.tgz", + "integrity": "sha512-UhDfHmA92YAlNnCfhmq0VeNL5bDbiZGg7sZ2IvPsXubGkiNa9EC+tUTsjBRsYUAz87btI6/1wf4XoVvQ3uRnmQ==", "dev": true, "license": "MIT", "engines": { @@ -7431,17 +6568,6 @@ "url": "https://github.com/sponsors/sindresorhus" } }, - "node_modules/stop-iteration-iterator": { - "version": "1.0.0", - "dev": true, - "license": "MIT", - "dependencies": { - "internal-slot": "^1.0.4" - }, - "engines": { - "node": ">= 0.4" - } - }, "node_modules/stoppable": { "version": "1.1.0", "resolved": "https://registry.npmjs.org/stoppable/-/stoppable-1.1.0.tgz", @@ -7452,14 +6578,6 @@ "npm": ">=6" } }, - "node_modules/string_decoder": { - "version": "1.3.0", - "dev": true, - "license": "MIT", - "dependencies": { - "safe-buffer": "~5.2.0" - } - }, "node_modules/string-width": { "version": "4.2.3", "dev": true, @@ -7473,48 +6591,6 @@ "node": ">=8" } }, - "node_modules/string.prototype.trim": { - "version": "1.2.8", - "dev": true, - "license": "MIT", - "dependencies": { - "call-bind": "^1.0.2", - "define-properties": "^1.2.0", - "es-abstract": "^1.22.1" - }, - "engines": { - "node": ">= 0.4" - }, - "funding": { - "url": "https://github.com/sponsors/ljharb" - } - }, - "node_modules/string.prototype.trimend": { - "version": "1.0.7", - "dev": true, - "license": "MIT", - "dependencies": { - "call-bind": "^1.0.2", - "define-properties": "^1.2.0", - "es-abstract": "^1.22.1" - }, - "funding": { - "url": "https://github.com/sponsors/ljharb" - } - }, - "node_modules/string.prototype.trimstart": { - "version": "1.0.7", - "dev": true, - "license": "MIT", - "dependencies": { - "call-bind": "^1.0.2", - "define-properties": "^1.2.0", - "es-abstract": "^1.22.1" - }, - "funding": { - "url": "https://github.com/sponsors/ljharb" - } - }, "node_modules/strip-ansi": { "version": "6.0.1", "dev": true, @@ -7536,6 +6612,8 @@ }, "node_modules/strip-final-newline": { "version": "3.0.0", + "resolved": "https://registry.npmjs.org/strip-final-newline/-/strip-final-newline-3.0.0.tgz", + "integrity": "sha512-dOESqjYr96iWYylGObzd39EuNTa5VJxyvVAEm5Jnh7KGo75V43Hk1odPQkNDyXNmUR6k+gEiDVXnjB8HJ3crXw==", "dev": true, "license": "MIT", "engines": { @@ -7589,17 +6667,6 @@ "url": "https://github.com/chalk/supports-color?sponsor=1" } }, - "node_modules/supports-preserve-symlinks-flag": { - "version": "1.0.0", - "dev": true, - "license": "MIT", - "engines": { - "node": ">= 0.4" - }, - "funding": { - "url": "https://github.com/sponsors/ljharb" - } - }, "node_modules/test-exclude": { "version": "6.0.0", "dev": true, @@ -7613,8 +6680,62 @@ "node": ">=8" } }, + "node_modules/tinyexec": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/tinyexec/-/tinyexec-1.0.1.tgz", + "integrity": "sha512-5uC6DDlmeqiOwCPmK9jMSdOuZTh8bU39Ys6yidB+UTt5hfZUPGAypSgFRiEp+jbi9qH40BLDvy85jIU88wKSqw==", + "dev": true, + "license": "MIT" + }, + "node_modules/tinyglobby": { + "version": "0.2.13", + "resolved": "https://registry.npmjs.org/tinyglobby/-/tinyglobby-0.2.13.tgz", + "integrity": "sha512-mEwzpUgrLySlveBwEVDMKk5B57bhLPYovRfPAXD5gA/98Opn0rCDj3GtLwFvCvH5RK9uPCExUROW5NjDwvqkxw==", + "dev": true, + "license": "MIT", + "dependencies": { + "fdir": "^6.4.4", + "picomatch": "^4.0.2" + }, + "engines": { + "node": ">=12.0.0" + }, + "funding": { + "url": "https://github.com/sponsors/SuperchupuDev" + } + }, + "node_modules/tinyglobby/node_modules/fdir": { + "version": "6.4.4", + "resolved": "https://registry.npmjs.org/fdir/-/fdir-6.4.4.tgz", + "integrity": "sha512-1NZP+GK4GfuAv3PqKvxQRDMjdSRZjnkq7KfhlNrCNNlZ0ygQFpebfrnfnq/W7fpUnAv9aGWmY1zKx7FYL3gwhg==", + "dev": true, + "license": "MIT", + "peerDependencies": { + "picomatch": "^3 || ^4" + }, + "peerDependenciesMeta": { + "picomatch": { + "optional": true + } + } + }, + "node_modules/tinyglobby/node_modules/picomatch": { + "version": "4.0.2", + "resolved": "https://registry.npmjs.org/picomatch/-/picomatch-4.0.2.tgz", + "integrity": "sha512-M7BAV6Rlcy5u+m6oPhAPFgJTzAioX/6B0DxyvDlo9l8+T3nLKbrczg2WLUyzd45L8RqfUMyGPzekbMvX2Ldkwg==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=12" + }, + "funding": { + "url": "https://github.com/sponsors/jonschlinkert" + } + }, "node_modules/tmp": { "version": "0.0.33", + "resolved": "https://registry.npmjs.org/tmp/-/tmp-0.0.33.tgz", + "integrity": "sha512-jRCJlojKnZ3addtTOjdIqoRuPEKBvNXcGYqzO6zWZX8KfKEpnGY5jfggJQ3EjKuu8D4bJRr0y+cYJFmYbImXGw==", "dev": true, "license": "MIT", "dependencies": { @@ -7634,6 +6755,8 @@ }, "node_modules/to-regex-range": { "version": "5.0.1", + "resolved": "https://registry.npmjs.org/to-regex-range/-/to-regex-range-5.0.1.tgz", + "integrity": "sha512-65P7iz6X5yEr1cwcgvQxbbIw7Uk3gOy5dIdtZ4rDveLqhrdJP+Li/Hx6tyK0NEb+2GCyneCMJiGqrADCSNk8sQ==", "dev": true, "license": "MIT", "dependencies": { @@ -7778,67 +6901,6 @@ "node": ">= 0.6" } }, - "node_modules/typed-array-buffer": { - "version": "1.0.0", - "dev": true, - "license": "MIT", - "dependencies": { - "call-bind": "^1.0.2", - "get-intrinsic": "^1.2.1", - "is-typed-array": "^1.1.10" - }, - "engines": { - "node": ">= 0.4" - } - }, - "node_modules/typed-array-byte-length": { - "version": "1.0.0", - "dev": true, - "license": "MIT", - "dependencies": { - "call-bind": "^1.0.2", - "for-each": "^0.3.3", - "has-proto": "^1.0.1", - "is-typed-array": "^1.1.10" - }, - "engines": { - "node": ">= 0.4" - }, - "funding": { - "url": "https://github.com/sponsors/ljharb" - } - }, - "node_modules/typed-array-byte-offset": { - "version": "1.0.0", - "dev": true, - "license": "MIT", - "dependencies": { - "available-typed-arrays": "^1.0.5", - "call-bind": "^1.0.2", - "for-each": "^0.3.3", - "has-proto": "^1.0.1", - "is-typed-array": "^1.1.10" - }, - "engines": { - "node": ">= 0.4" - }, - "funding": { - "url": "https://github.com/sponsors/ljharb" - } - }, - "node_modules/typed-array-length": { - "version": "1.0.4", - "dev": true, - "license": "MIT", - "dependencies": { - "call-bind": "^1.0.2", - "for-each": "^0.3.3", - "is-typed-array": "^1.1.9" - }, - "funding": { - "url": "https://github.com/sponsors/ljharb" - } - }, "node_modules/typedarray-to-buffer": { "version": "3.1.5", "dev": true, @@ -7904,62 +6966,35 @@ "node_modules/uid-safe": { "version": "2.1.5", "resolved": "https://registry.npmjs.org/uid-safe/-/uid-safe-2.1.5.tgz", - "integrity": "sha512-KPHm4VL5dDXKz01UuEd88Df+KzynaohSL9fBh096KWAxSKZQDI2uBrVqtvRM4rwrIrRRKsdLNML/lnaaVSRioA==", - "dev": true, - "license": "MIT", - "dependencies": { - "random-bytes": "~1.0.0" - }, - "engines": { - "node": ">= 0.8" - } - }, - "node_modules/unbox-primitive": { - "version": "1.0.2", - "dev": true, - "license": "MIT", - "dependencies": { - "call-bind": "^1.0.2", - "has-bigints": "^1.0.2", - "has-symbols": "^1.0.3", - "which-boxed-primitive": "^1.0.2" - }, - "funding": { - "url": "https://github.com/sponsors/ljharb" - } - }, - "node_modules/undici-types": { - "version": "5.26.5", - "dev": true, - "license": "MIT" - }, - "node_modules/unicorn-magic": { - "version": "0.1.0", + "integrity": "sha512-KPHm4VL5dDXKz01UuEd88Df+KzynaohSL9fBh096KWAxSKZQDI2uBrVqtvRM4rwrIrRRKsdLNML/lnaaVSRioA==", "dev": true, "license": "MIT", - "engines": { - "node": ">=18" + "dependencies": { + "random-bytes": "~1.0.0" }, - "funding": { - "url": "https://github.com/sponsors/sindresorhus" + "engines": { + "node": ">= 0.8" } }, - "node_modules/unique-string": { - "version": "3.0.0", + "node_modules/undici": { + "version": "6.21.3", + "resolved": "https://registry.npmjs.org/undici/-/undici-6.21.3.tgz", + "integrity": "sha512-gBLkYIlEnSp8pFbT64yFgGE6UIB9tAkhukC23PmMDCe5Nd+cRqKxSjw5y54MK2AZMgZfJWMaNE4nYUHgi1XEOw==", "dev": true, "license": "MIT", - "dependencies": { - "crypto-random-string": "^4.0.0" - }, "engines": { - "node": ">=12" - }, - "funding": { - "url": "https://github.com/sponsors/sindresorhus" + "node": ">=18.17" } }, + "node_modules/undici-types": { + "version": "5.26.5", + "dev": true, + "license": "MIT" + }, "node_modules/universal-user-agent": { - "version": "6.0.1", + "version": "7.0.3", + "resolved": "https://registry.npmjs.org/universal-user-agent/-/universal-user-agent-7.0.3.tgz", + "integrity": "sha512-TmnEAEAsBJVZM/AADELsK76llnwcf9vMKuPz8JflO1frO8Lchitr0fNaN9d+Ap0BjKtqWqd/J17qeDnXh8CL2A==", "dev": true, "license": "ISC" }, @@ -8010,85 +7045,16 @@ "browserslist": ">= 4.21.0" } }, - "node_modules/update-notifier": { - "version": "7.0.0", - "dev": true, - "license": "BSD-2-Clause", - "dependencies": { - "boxen": "^7.1.1", - "chalk": "^5.3.0", - "configstore": "^6.0.0", - "import-lazy": "^4.0.0", - "is-in-ci": "^0.1.0", - "is-installed-globally": "^0.4.0", - "is-npm": "^6.0.0", - "latest-version": "^7.0.0", - "pupa": "^3.1.0", - "semver": "^7.5.4", - "semver-diff": "^4.0.0", - "xdg-basedir": "^5.1.0" - }, - "engines": { - "node": ">=18" - }, - "funding": { - "url": "https://github.com/yeoman/update-notifier?sponsor=1" - } - }, - "node_modules/update-notifier/node_modules/chalk": { - "version": "5.3.0", - "dev": true, - "license": "MIT", - "engines": { - "node": "^12.17.0 || ^14.13 || >=16.0.0" - }, - "funding": { - "url": "https://github.com/chalk/chalk?sponsor=1" - } - }, - "node_modules/update-notifier/node_modules/lru-cache": { - "version": "6.0.0", - "dev": true, - "license": "ISC", - "dependencies": { - "yallist": "^4.0.0" - }, - "engines": { - "node": ">=10" - } - }, - "node_modules/update-notifier/node_modules/semver": { - "version": "7.5.4", - "dev": true, - "license": "ISC", - "dependencies": { - "lru-cache": "^6.0.0" - }, - "bin": { - "semver": "bin/semver.js" - }, - "engines": { - "node": ">=10" - } - }, - "node_modules/update-notifier/node_modules/yallist": { - "version": "4.0.0", - "dev": true, - "license": "ISC" - }, "node_modules/url-join": { "version": "5.0.0", + "resolved": "https://registry.npmjs.org/url-join/-/url-join-5.0.0.tgz", + "integrity": "sha512-n2huDr9h9yzd6exQVnH/jU5mr+Pfx08LRXXZhkLLetAMESRj+anQsTAh940iMrIetKAmry9coFuZQ2jY8/p3WA==", "dev": true, "license": "MIT", "engines": { "node": "^12.20.0 || ^14.13.1 || >=16.0.0" } }, - "node_modules/util-deprecate": { - "version": "1.0.2", - "dev": true, - "license": "MIT" - }, "node_modules/utils-merge": { "version": "1.0.1", "resolved": "https://registry.npmjs.org/utils-merge/-/utils-merge-1.0.1.tgz", @@ -8133,20 +7099,40 @@ "dev": true, "license": "MIT" }, - "node_modules/wcwidth": { - "version": "1.0.1", + "node_modules/whatwg-encoding": { + "version": "3.1.1", + "resolved": "https://registry.npmjs.org/whatwg-encoding/-/whatwg-encoding-3.1.1.tgz", + "integrity": "sha512-6qN4hJdMwfYBtE3YBTTHhoeuUrDBPZmbQaxWAqSALV/MeEnR5z1xd8UKud2RAkFoPkmB+hli1TZSnyi84xz1vQ==", + "dev": true, + "license": "MIT", + "dependencies": { + "iconv-lite": "0.6.3" + }, + "engines": { + "node": ">=18" + } + }, + "node_modules/whatwg-encoding/node_modules/iconv-lite": { + "version": "0.6.3", + "resolved": "https://registry.npmjs.org/iconv-lite/-/iconv-lite-0.6.3.tgz", + "integrity": "sha512-4fCk79wshMdzMp2rH06qWrJE4iolqLhCUH+OiuIgU++RB0+94NlDL81atO7GX55uUKueo0txHNtvEyI6D7WdMw==", "dev": true, "license": "MIT", "dependencies": { - "defaults": "^1.0.3" + "safer-buffer": ">= 2.1.2 < 3.0.0" + }, + "engines": { + "node": ">=0.10.0" } }, - "node_modules/web-streams-polyfill": { - "version": "3.3.2", + "node_modules/whatwg-mimetype": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/whatwg-mimetype/-/whatwg-mimetype-4.0.0.tgz", + "integrity": "sha512-QaKxh0eNIi2mE9p2vEdzfagOKHCcj1pJ56EEHGQOVxp8r9/iszLUUV7v89x9O1p/T+NlTM5W7jW6+cz4Fq1YVg==", "dev": true, "license": "MIT", "engines": { - "node": ">= 8" + "node": ">=18" } }, "node_modules/which": { @@ -8163,205 +7149,34 @@ "node": ">= 8" } }, - "node_modules/which-boxed-primitive": { - "version": "1.0.2", - "dev": true, - "license": "MIT", - "dependencies": { - "is-bigint": "^1.0.1", - "is-boolean-object": "^1.1.0", - "is-number-object": "^1.0.4", - "is-string": "^1.0.5", - "is-symbol": "^1.0.3" - }, - "funding": { - "url": "https://github.com/sponsors/ljharb" - } - }, "node_modules/which-module": { "version": "2.0.1", "dev": true, "license": "ISC" }, - "node_modules/which-typed-array": { - "version": "1.1.14", - "dev": true, - "license": "MIT", - "dependencies": { - "available-typed-arrays": "^1.0.6", - "call-bind": "^1.0.5", - "for-each": "^0.3.3", - "gopd": "^1.0.1", - "has-tostringtag": "^1.0.1" - }, - "engines": { - "node": ">= 0.4" - }, - "funding": { - "url": "https://github.com/sponsors/ljharb" - } - }, - "node_modules/widest-line": { - "version": "4.0.1", - "dev": true, - "license": "MIT", - "dependencies": { - "string-width": "^5.0.1" - }, - "engines": { - "node": ">=12" - }, - "funding": { - "url": "https://github.com/sponsors/sindresorhus" - } - }, - "node_modules/widest-line/node_modules/ansi-regex": { - "version": "6.0.1", - "dev": true, - "license": "MIT", - "engines": { - "node": ">=12" - }, - "funding": { - "url": "https://github.com/chalk/ansi-regex?sponsor=1" - } - }, - "node_modules/widest-line/node_modules/emoji-regex": { - "version": "9.2.2", - "dev": true, - "license": "MIT" - }, - "node_modules/widest-line/node_modules/string-width": { - "version": "5.1.2", - "dev": true, - "license": "MIT", - "dependencies": { - "eastasianwidth": "^0.2.0", - "emoji-regex": "^9.2.2", - "strip-ansi": "^7.0.1" - }, - "engines": { - "node": ">=12" - }, - "funding": { - "url": "https://github.com/sponsors/sindresorhus" - } - }, - "node_modules/widest-line/node_modules/strip-ansi": { - "version": "7.1.0", - "dev": true, - "license": "MIT", - "dependencies": { - "ansi-regex": "^6.0.1" - }, - "engines": { - "node": ">=12" - }, - "funding": { - "url": "https://github.com/chalk/strip-ansi?sponsor=1" - } - }, "node_modules/wildcard-match": { - "version": "5.1.2", + "version": "5.1.4", + "resolved": "https://registry.npmjs.org/wildcard-match/-/wildcard-match-5.1.4.tgz", + "integrity": "sha512-wldeCaczs8XXq7hj+5d/F38JE2r7EXgb6WQDM84RVwxy81T/sxB5e9+uZLK9Q9oNz1mlvjut+QtvgaOQFPVq/g==", "dev": true, "license": "ISC" }, "node_modules/windows-release": { - "version": "5.1.1", - "dev": true, - "license": "MIT", - "dependencies": { - "execa": "^5.1.1" - }, - "engines": { - "node": "^12.20.0 || ^14.13.1 || >=16.0.0" - }, - "funding": { - "url": "https://github.com/sponsors/sindresorhus" - } - }, - "node_modules/windows-release/node_modules/execa": { - "version": "5.1.1", - "dev": true, - "license": "MIT", - "dependencies": { - "cross-spawn": "^7.0.3", - "get-stream": "^6.0.0", - "human-signals": "^2.1.0", - "is-stream": "^2.0.0", - "merge-stream": "^2.0.0", - "npm-run-path": "^4.0.1", - "onetime": "^5.1.2", - "signal-exit": "^3.0.3", - "strip-final-newline": "^2.0.0" - }, - "engines": { - "node": ">=10" - }, - "funding": { - "url": "https://github.com/sindresorhus/execa?sponsor=1" - } - }, - "node_modules/windows-release/node_modules/get-stream": { - "version": "6.0.1", - "dev": true, - "license": "MIT", - "engines": { - "node": ">=10" - }, - "funding": { - "url": "https://github.com/sponsors/sindresorhus" - } - }, - "node_modules/windows-release/node_modules/human-signals": { - "version": "2.1.0", - "dev": true, - "license": "Apache-2.0", - "engines": { - "node": ">=10.17.0" - } - }, - "node_modules/windows-release/node_modules/mimic-fn": { - "version": "2.1.0", - "dev": true, - "license": "MIT", - "engines": { - "node": ">=6" - } - }, - "node_modules/windows-release/node_modules/npm-run-path": { - "version": "4.0.1", - "dev": true, - "license": "MIT", - "dependencies": { - "path-key": "^3.0.0" - }, - "engines": { - "node": ">=8" - } - }, - "node_modules/windows-release/node_modules/onetime": { - "version": "5.1.2", + "version": "6.1.0", + "resolved": "https://registry.npmjs.org/windows-release/-/windows-release-6.1.0.tgz", + "integrity": "sha512-1lOb3qdzw6OFmOzoY0nauhLG72TpWtb5qgYPiSh/62rjc1XidBSDio2qw0pwHh17VINF217ebIkZJdFLZFn9SA==", "dev": true, "license": "MIT", "dependencies": { - "mimic-fn": "^2.1.0" + "execa": "^8.0.1" }, "engines": { - "node": ">=6" + "node": ">=18" }, "funding": { "url": "https://github.com/sponsors/sindresorhus" } }, - "node_modules/windows-release/node_modules/strip-final-newline": { - "version": "2.0.0", - "dev": true, - "license": "MIT", - "engines": { - "node": ">=6" - } - }, "node_modules/workerpool": { "version": "6.2.1", "dev": true, @@ -8396,17 +7211,6 @@ "typedarray-to-buffer": "^3.1.5" } }, - "node_modules/xdg-basedir": { - "version": "5.1.0", - "dev": true, - "license": "MIT", - "engines": { - "node": ">=12" - }, - "funding": { - "url": "https://github.com/sponsors/sindresorhus" - } - }, "node_modules/y18n": { "version": "5.0.8", "dev": true, @@ -8502,6 +7306,19 @@ "url": "https://github.com/sponsors/sindresorhus" } }, + "node_modules/yoctocolors-cjs": { + "version": "2.1.2", + "resolved": "https://registry.npmjs.org/yoctocolors-cjs/-/yoctocolors-cjs-2.1.2.tgz", + "integrity": "sha512-cYVsTjKl8b+FrnidjibDWskAv7UKOfcwaVZdp/it9n1s9fU3IkgDbhdIRKCW4JDsAlECJY0ytoVPT3sK6kideA==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=18" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, "packages/authx": { "name": "@redis/authx", "version": "5.0.0-next.5", diff --git a/package.json b/package.json index 2ff2d4825ae..e192e69d55e 100644 --- a/package.json +++ b/package.json @@ -2,26 +2,35 @@ "name": "redis-monorepo", "private": true, "workspaces": [ - "./packages/*" + "./packages/client", + "./packages/test-utils", + "./packages/bloom", + "./packages/json", + "./packages/search", + "./packages/time-series", + "./packages/entraid", + "./packages/redis" ], "scripts": { "test-single": "TS_NODE_PROJECT='./packages/test-utils/tsconfig.json' mocha --require ts-node/register/transpile-only ", "test": "npm run test -ws --if-present", "build": "tsc --build", "documentation": "typedoc --out ./documentation", - "gh-pages": "gh-pages -d ./documentation -e ./documentation -u 'documentation-bot '" + "gh-pages": "gh-pages -d ./documentation -e ./documentation -u 'documentation-bot '", + "release": "npm run release --workspaces --if-present --" }, "devDependencies": { "@istanbuljs/nyc-config-typescript": "^1.0.2", + "@release-it/bumper": "^7.0.5", "@types/mocha": "^10.0.6", "@types/node": "^20.11.16", "gh-pages": "^6.1.1", "mocha": "^10.2.0", "nyc": "^15.1.0", - "release-it": "^17.0.3", + "release-it": "^19.0.2", + "ts-node": "^10.9.2", "tsx": "^4.7.0", "typedoc": "^0.25.7", - "typescript": "^5.3.3", - "ts-node": "^10.9.2" + "typescript": "^5.3.3" } } diff --git a/packages/bloom/.release-it.json b/packages/bloom/.release-it.json index 3a27a088058..23e1cf09078 100644 --- a/packages/bloom/.release-it.json +++ b/packages/bloom/.release-it.json @@ -1,11 +1,22 @@ { + "npm": { + "publish": true, + "publishArgs": ["--access", "public"] + }, "git": { "tagName": "bloom@${version}", + "tagMatch": "bloom@*", "commitMessage": "Release ${tagName}", - "tagAnnotation": "Release ${tagName}" + "tagAnnotation": "Release ${tagName}", + "commitArgs": "--all" }, - "npm": { - "versionArgs": ["--workspaces-update=false"], - "publishArgs": ["--access", "public"] + "plugins": { + "@release-it/bumper": { + "out": { + "file": "package.json", + "path": ["peerDependencies.@redis/client"], + "versionPrefix": "^" + } + } } } diff --git a/packages/bloom/package.json b/packages/bloom/package.json index 547e5ee64ed..8eb9dbf1e81 100644 --- a/packages/bloom/package.json +++ b/packages/bloom/package.json @@ -9,7 +9,8 @@ "!dist/tsconfig.tsbuildinfo" ], "scripts": { - "test": "nyc -r text-summary -r lcov mocha -r tsx './lib/**/*.spec.ts'" + "test": "nyc -r text-summary -r lcov mocha -r tsx './lib/**/*.spec.ts'", + "release": "release-it" }, "peerDependencies": { "@redis/client": "^5.1.1" diff --git a/packages/client/.release-it.json b/packages/client/.release-it.json index 3ae247ad371..fe1a6ad0d0d 100644 --- a/packages/client/.release-it.json +++ b/packages/client/.release-it.json @@ -1,11 +1,13 @@ { + "npm": { + "publish": true, + "publishArgs": ["--access", "public"] + }, "git": { "tagName": "client@${version}", + "tagMatch": "client@*", "commitMessage": "Release ${tagName}", - "tagAnnotation": "Release ${tagName}" - }, - "npm": { - "versionArgs": ["--workspaces-update=false"], - "publishArgs": ["--access", "public"] + "tagAnnotation": "Release ${tagName}", + "commitArgs": "--all" } } diff --git a/packages/client/package.json b/packages/client/package.json index a6d44451a62..9d0950eb4ea 100644 --- a/packages/client/package.json +++ b/packages/client/package.json @@ -9,7 +9,8 @@ "!dist/tsconfig.tsbuildinfo" ], "scripts": { - "test": "nyc -r text-summary -r lcov mocha -r tsx './lib/**/*.spec.ts'" + "test": "nyc -r text-summary -r lcov mocha -r tsx './lib/**/*.spec.ts'", + "release": "release-it" }, "dependencies": { "cluster-key-slot": "1.1.2" diff --git a/packages/entraid/.release-it.json b/packages/entraid/.release-it.json index a5f3a31062e..a0609a242f7 100644 --- a/packages/entraid/.release-it.json +++ b/packages/entraid/.release-it.json @@ -1,11 +1,22 @@ { + "npm": { + "publish": true, + "publishArgs": ["--access", "public"] + }, "git": { "tagName": "entraid@${version}", + "tagMatch": "entraid@*", "commitMessage": "Release ${tagName}", - "tagAnnotation": "Release ${tagName}" + "tagAnnotation": "Release ${tagName}", + "commitArgs": "--all" }, - "npm": { - "versionArgs": ["--workspaces-update=false"], - "publishArgs": ["--access", "public"] + "plugins": { + "@release-it/bumper": { + "out": { + "file": "package.json", + "path": ["peerDependencies.@redis/client"], + "versionPrefix": "^" + } + } } } diff --git a/packages/entraid/package.json b/packages/entraid/package.json index 43f26d1e039..1dc89f0c5c1 100644 --- a/packages/entraid/package.json +++ b/packages/entraid/package.json @@ -14,7 +14,8 @@ "start:auth-pkce": "tsx --tsconfig tsconfig.samples.json ./samples/auth-code-pkce/index.ts", "start:interactive-browser": "tsx --tsconfig tsconfig.samples.json ./samples/interactive-browser/index.ts", "test-integration": "mocha -r tsx --tsconfig tsconfig.integration-tests.json './integration-tests/**/*.spec.ts'", - "test": "nyc -r text-summary -r lcov mocha -r tsx './lib/**/*.spec.ts'" + "test": "nyc -r text-summary -r lcov mocha -r tsx './lib/**/*.spec.ts'", + "release": "release-it" }, "dependencies": { "@azure/identity": "^4.7.0", diff --git a/packages/json/.release-it.json b/packages/json/.release-it.json index 8de2f3696e3..a384e3e3d4d 100644 --- a/packages/json/.release-it.json +++ b/packages/json/.release-it.json @@ -1,11 +1,22 @@ { + "npm": { + "publish": true, + "publishArgs": ["--access", "public"] + }, "git": { "tagName": "json@${version}", + "tagMatch": "json@*", "commitMessage": "Release ${tagName}", - "tagAnnotation": "Release ${tagName}" + "tagAnnotation": "Release ${tagName}", + "commitArgs": "--all" }, - "npm": { - "versionArgs": ["--workspaces-update=false"], - "publishArgs": ["--access", "public"] + "plugins": { + "@release-it/bumper": { + "out": { + "file": "package.json", + "path": ["peerDependencies.@redis/client"], + "versionPrefix": "^" + } + } } } diff --git a/packages/json/package.json b/packages/json/package.json index 3b473dfce0f..09418cabf9f 100644 --- a/packages/json/package.json +++ b/packages/json/package.json @@ -9,7 +9,8 @@ "!dist/tsconfig.tsbuildinfo" ], "scripts": { - "test": "nyc -r text-summary -r lcov mocha -r tsx './lib/**/*.spec.ts'" + "test": "nyc -r text-summary -r lcov mocha -r tsx './lib/**/*.spec.ts'", + "release": "release-it" }, "peerDependencies": { "@redis/client": "^5.1.1" diff --git a/packages/redis/.release-it.json b/packages/redis/.release-it.json index 982b4ac6cbe..af127d79ae0 100644 --- a/packages/redis/.release-it.json +++ b/packages/redis/.release-it.json @@ -1,7 +1,27 @@ { + "npm": { + "publish": true, + "publishArgs": ["--access", "public"] + }, "git": { "tagName": "redis@${version}", + "tagMatch": "redis@*", "commitMessage": "Release ${tagName}", - "tagAnnotation": "Release ${tagName}" + "tagAnnotation": "Release ${tagName}", + "commitArgs": "--all" + }, + "plugins": { + "@release-it/bumper": { + "out": { + "file": "package.json", + "path": [ + "dependencies.@redis/client", + "dependencies.@redis/bloom", + "dependencies.@redis/json", + "dependencies.@redis/search", + "dependencies.@redis/time-series" + ] + } + } } } diff --git a/packages/redis/package.json b/packages/redis/package.json index 05fb70cdd11..889898b3c21 100644 --- a/packages/redis/package.json +++ b/packages/redis/package.json @@ -9,6 +9,9 @@ "dist/", "!dist/tsconfig.tsbuildinfo" ], + "scripts": { + "release": "release-it" + }, "dependencies": { "@redis/bloom": "5.1.1", "@redis/client": "5.1.1", diff --git a/packages/search/.release-it.json b/packages/search/.release-it.json index 3996a524e3b..85d55c087de 100644 --- a/packages/search/.release-it.json +++ b/packages/search/.release-it.json @@ -1,11 +1,22 @@ { + "npm": { + "publish": true, + "publishArgs": ["--access", "public"] + }, "git": { "tagName": "search@${version}", + "tagMatch": "search@*", "commitMessage": "Release ${tagName}", - "tagAnnotation": "Release ${tagName}" + "tagAnnotation": "Release ${tagName}", + "commitArgs": "--all" }, - "npm": { - "versionArgs": ["--workspaces-update=false"], - "publishArgs": ["--access", "public"] + "plugins": { + "@release-it/bumper": { + "out": { + "file": "package.json", + "path": ["peerDependencies.@redis/client"], + "versionPrefix": "^" + } + } } } diff --git a/packages/search/package.json b/packages/search/package.json index 7cb73dfc0a5..6f7af2e0a25 100644 --- a/packages/search/package.json +++ b/packages/search/package.json @@ -10,7 +10,8 @@ ], "scripts": { "test": "nyc -r text-summary -r lcov mocha -r tsx './lib/**/*.spec.ts'", - "test-sourcemap": "mocha -r ts-node/register/transpile-only './lib/**/*.spec.ts'" + "test-sourcemap": "mocha -r ts-node/register/transpile-only './lib/**/*.spec.ts'", + "release": "release-it" }, "peerDependencies": { "@redis/client": "^5.1.1" diff --git a/packages/time-series/.release-it.json b/packages/time-series/.release-it.json index 6c59e8955cf..0ffec5a0c70 100644 --- a/packages/time-series/.release-it.json +++ b/packages/time-series/.release-it.json @@ -1,11 +1,22 @@ { + "npm": { + "publish": true, + "publishArgs": ["--access", "public"] + }, "git": { "tagName": "time-series@${version}", + "tagMatch": "time-series@*", "commitMessage": "Release ${tagName}", - "tagAnnotation": "Release ${tagName}" + "tagAnnotation": "Release ${tagName}", + "commitArgs": "--all" }, - "npm": { - "versionArgs": ["--workspaces-update=false"], - "publishArgs": ["--access", "public"] + "plugins": { + "@release-it/bumper": { + "out": { + "file": "package.json", + "path": ["peerDependencies.@redis/client"], + "versionPrefix": "^" + } + } } } diff --git a/packages/time-series/package.json b/packages/time-series/package.json index 888f0f317e2..4d5d8f01be5 100644 --- a/packages/time-series/package.json +++ b/packages/time-series/package.json @@ -9,7 +9,8 @@ "!dist/tsconfig.tsbuildinfo" ], "scripts": { - "test": "nyc -r text-summary -r lcov mocha -r tsx './lib/**/*.spec.ts'" + "test": "nyc -r text-summary -r lcov mocha -r tsx './lib/**/*.spec.ts'", + "release": "release-it" }, "peerDependencies": { "@redis/client": "^5.1.1" From 2bb515e48917b35734b3d5c7a7b03fb872db7352 Mon Sep 17 00:00:00 2001 From: Nikolay Karadzhov Date: Thu, 5 Jun 2025 11:46:57 +0300 Subject: [PATCH 1617/1748] fix(search): align ft.search with server (#2988) as per the ft.search docs ( https://redis.io/docs/latest/commands/ft.search ): If a relevant key expires while a query is running, an attempt to load the key's value will return a null array. However, the key is still counted in the total number of results. So, instead of crashing when seeing a null as a value, we return empty object. fixes #2772 see https://github.com/redis/node-redis/pull/2814 --- packages/search/lib/commands/SEARCH.ts | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/packages/search/lib/commands/SEARCH.ts b/packages/search/lib/commands/SEARCH.ts index abc561dff4e..b8efda05777 100644 --- a/packages/search/lib/commands/SEARCH.ts +++ b/packages/search/lib/commands/SEARCH.ts @@ -221,6 +221,10 @@ export interface SearchReply { function documentValue(tuples: any) { const message = Object.create(null); + if(!tuples) { + return message; + } + let i = 0; while (i < tuples.length) { const key = tuples[i++], From 21d8f0e95799ddb8e431e58e6e9ff3f8603fba10 Mon Sep 17 00:00:00 2001 From: Navi sureka Date: Fri, 6 Jun 2025 12:00:07 +0530 Subject: [PATCH 1618/1748] docs(search): update SchemaFieldTypes to SCHEMA_FIELD_TYPE for redis@5.x (#2992) --- packages/search/README.md | 12 ++++++------ 1 file changed, 6 insertions(+), 6 deletions(-) diff --git a/packages/search/README.md b/packages/search/README.md index 70a91fdeb2f..37597e6580a 100644 --- a/packages/search/README.md +++ b/packages/search/README.md @@ -19,11 +19,11 @@ Before we can perform any searches, we need to tell RediSearch how to index our ```javascript await client.ft.create('idx:animals', { name: { - type: SchemaFieldTypes.TEXT, + type: SCHEMA_FIELD_TYPE.TEXT, SORTABLE: true }, - species: SchemaFieldTypes.TAG, - age: SchemaFieldTypes.NUMERIC + species: SCHEMA_FIELD_TYPE.TAG, + age: SCHEMA_FIELD_TYPE.NUMERIC }, { ON: 'HASH', PREFIX: 'noderedis:animals' @@ -91,15 +91,15 @@ One way we might choose to index these documents is as follows: ```javascript await client.ft.create('idx:users', { '$.name': { - type: SchemaFieldTypes.TEXT, + type: SCHEMA_FIELD_TYPE.TEXT, SORTABLE: 'UNF' }, '$.age': { - type: SchemaFieldTypes.NUMERIC, + type: SCHEMA_FIELD_TYPE.NUMERIC, AS: 'age' }, '$.coins': { - type: SchemaFieldTypes.NUMERIC, + type: SCHEMA_FIELD_TYPE.NUMERIC, AS: 'coins' } }, { From 0ebe55cbd79686b6935dbfff1bf199ec8e28b178 Mon Sep 17 00:00:00 2001 From: Nikolay Karadzhov Date: Fri, 6 Jun 2025 10:02:47 +0300 Subject: [PATCH 1619/1748] Release client@5.5.6 --- packages/client/package.json | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/packages/client/package.json b/packages/client/package.json index 9d0950eb4ea..b95d1087d07 100644 --- a/packages/client/package.json +++ b/packages/client/package.json @@ -1,6 +1,6 @@ { "name": "@redis/client", - "version": "5.1.1", + "version": "5.5.6", "license": "MIT", "main": "./dist/index.js", "types": "./dist/index.d.ts", From 44caf9fcbad156d39c31c45f3f6ffaf0aa65dd2f Mon Sep 17 00:00:00 2001 From: Nikolay Karadzhov Date: Fri, 6 Jun 2025 10:08:35 +0300 Subject: [PATCH 1620/1748] Updated the Bloom package to use client@5.5.6 --- package-lock.json | 16 ++++++++++++++-- packages/bloom/package.json | 2 +- 2 files changed, 15 insertions(+), 3 deletions(-) diff --git a/package-lock.json b/package-lock.json index dddb972c236..bd4fc1f5094 100644 --- a/package-lock.json +++ b/package-lock.json @@ -7346,12 +7346,12 @@ "node": ">= 18" }, "peerDependencies": { - "@redis/client": "^5.1.1" + "@redis/client": "^5.5.6" } }, "packages/client": { "name": "@redis/client", - "version": "5.1.1", + "version": "5.5.6", "license": "MIT", "dependencies": { "cluster-key-slot": "1.1.2" @@ -7449,6 +7449,18 @@ "node": ">= 18" } }, + "packages/redis/node_modules/@redis/client": { + "version": "5.1.1", + "resolved": "https://registry.npmjs.org/@redis/client/-/client-5.1.1.tgz", + "integrity": "sha512-vojbBqUdbkD+ylCy3+ZDXLzSmgiYH9pLrv87kF+nDgsRaHKrVVxPV9B4u6EfWRx7XGvQGZqsXVkKFhsEOsG3LA==", + "license": "MIT", + "dependencies": { + "cluster-key-slot": "1.1.2" + }, + "engines": { + "node": ">= 18" + } + }, "packages/search": { "name": "@redis/search", "version": "5.1.1", diff --git a/packages/bloom/package.json b/packages/bloom/package.json index 8eb9dbf1e81..af927fef0f2 100644 --- a/packages/bloom/package.json +++ b/packages/bloom/package.json @@ -13,7 +13,7 @@ "release": "release-it" }, "peerDependencies": { - "@redis/client": "^5.1.1" + "@redis/client": "^5.5.6" }, "devDependencies": { "@redis/test-utils": "*" From 2302df3abdcf53cf01447b14a47f037acc34d9ba Mon Sep 17 00:00:00 2001 From: Nikolay Karadzhov Date: Fri, 6 Jun 2025 10:09:56 +0300 Subject: [PATCH 1621/1748] Release bloom@5.5.6 --- package-lock.json | 14 +++++++++++++- packages/bloom/package.json | 2 +- 2 files changed, 14 insertions(+), 2 deletions(-) diff --git a/package-lock.json b/package-lock.json index bd4fc1f5094..c6c2cd3dd5c 100644 --- a/package-lock.json +++ b/package-lock.json @@ -7337,7 +7337,7 @@ }, "packages/bloom": { "name": "@redis/bloom", - "version": "5.1.1", + "version": "5.5.6", "license": "MIT", "devDependencies": { "@redis/test-utils": "*" @@ -7449,6 +7449,18 @@ "node": ">= 18" } }, + "packages/redis/node_modules/@redis/bloom": { + "version": "5.1.1", + "resolved": "https://registry.npmjs.org/@redis/bloom/-/bloom-5.1.1.tgz", + "integrity": "sha512-PnMcvpL7O2DHtnSL5JtyNmraNrdHuJXi3u2isGTUuPgkbAuWQKfZdknq471ySILL+qKtLfVJqzgDFMjYmZzK6Q==", + "license": "MIT", + "engines": { + "node": ">= 18" + }, + "peerDependencies": { + "@redis/client": "^5.1.1" + } + }, "packages/redis/node_modules/@redis/client": { "version": "5.1.1", "resolved": "https://registry.npmjs.org/@redis/client/-/client-5.1.1.tgz", diff --git a/packages/bloom/package.json b/packages/bloom/package.json index af927fef0f2..83dd6f893f9 100644 --- a/packages/bloom/package.json +++ b/packages/bloom/package.json @@ -1,6 +1,6 @@ { "name": "@redis/bloom", - "version": "5.1.1", + "version": "5.5.6", "license": "MIT", "main": "./dist/lib/index.js", "types": "./dist/lib/index.d.ts", From 91e5d306ca356624781a82a287902531237e4a2f Mon Sep 17 00:00:00 2001 From: Nikolay Karadzhov Date: Fri, 6 Jun 2025 10:11:18 +0300 Subject: [PATCH 1622/1748] Updated the Entraid package to use client@5.5.6 --- package-lock.json | 2 +- packages/entraid/package.json | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/package-lock.json b/package-lock.json index c6c2cd3dd5c..89e00511798 100644 --- a/package-lock.json +++ b/package-lock.json @@ -7386,7 +7386,7 @@ "node": ">= 18" }, "peerDependencies": { - "@redis/client": "^5.1.1" + "@redis/client": "^5.5.6" } }, "packages/entraid/node_modules/@types/node": { diff --git a/packages/entraid/package.json b/packages/entraid/package.json index 1dc89f0c5c1..f9447291b72 100644 --- a/packages/entraid/package.json +++ b/packages/entraid/package.json @@ -22,7 +22,7 @@ "@azure/msal-node": "^2.16.1" }, "peerDependencies": { - "@redis/client": "^5.1.1" + "@redis/client": "^5.5.6" }, "devDependencies": { "@types/express": "^4.17.21", From 7d12438300f7466ddaf73f79e9e15afe09ac25d1 Mon Sep 17 00:00:00 2001 From: Nikolay Karadzhov Date: Fri, 6 Jun 2025 10:12:16 +0300 Subject: [PATCH 1623/1748] Release entraid@5.5.6 --- package-lock.json | 2 +- packages/entraid/package.json | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/package-lock.json b/package-lock.json index 89e00511798..b9ce55faa13 100644 --- a/package-lock.json +++ b/package-lock.json @@ -7367,7 +7367,7 @@ }, "packages/entraid": { "name": "@redis/entraid", - "version": "5.1.1", + "version": "5.5.6", "license": "MIT", "dependencies": { "@azure/identity": "^4.7.0", diff --git a/packages/entraid/package.json b/packages/entraid/package.json index f9447291b72..79641cdf9a9 100644 --- a/packages/entraid/package.json +++ b/packages/entraid/package.json @@ -1,6 +1,6 @@ { "name": "@redis/entraid", - "version": "5.1.1", + "version": "5.5.6", "license": "MIT", "main": "./dist/index.js", "types": "./dist/index.d.ts", From b2519abe7b53110646c2cb4edd2ceaa96d91c9e0 Mon Sep 17 00:00:00 2001 From: Nikolay Karadzhov Date: Fri, 6 Jun 2025 10:13:34 +0300 Subject: [PATCH 1624/1748] Updated the Json package to use client@5.5.6 --- package-lock.json | 2 +- packages/json/package.json | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/package-lock.json b/package-lock.json index b9ce55faa13..03fa9822402 100644 --- a/package-lock.json +++ b/package-lock.json @@ -7432,7 +7432,7 @@ "node": ">= 18" }, "peerDependencies": { - "@redis/client": "^5.1.1" + "@redis/client": "^5.5.6" } }, "packages/redis": { diff --git a/packages/json/package.json b/packages/json/package.json index 09418cabf9f..971f1adcce7 100644 --- a/packages/json/package.json +++ b/packages/json/package.json @@ -13,7 +13,7 @@ "release": "release-it" }, "peerDependencies": { - "@redis/client": "^5.1.1" + "@redis/client": "^5.5.6" }, "devDependencies": { "@redis/test-utils": "*" From a5dc786b768549be76c2f4a0bbaef5b080ad7d86 Mon Sep 17 00:00:00 2001 From: Nikolay Karadzhov Date: Fri, 6 Jun 2025 10:14:12 +0300 Subject: [PATCH 1625/1748] Release json@5.5.6 --- package-lock.json | 14 +++++++++++++- packages/json/package.json | 2 +- 2 files changed, 14 insertions(+), 2 deletions(-) diff --git a/package-lock.json b/package-lock.json index 03fa9822402..22e3bb8c442 100644 --- a/package-lock.json +++ b/package-lock.json @@ -7423,7 +7423,7 @@ }, "packages/json": { "name": "@redis/json", - "version": "5.1.1", + "version": "5.5.6", "license": "MIT", "devDependencies": { "@redis/test-utils": "*" @@ -7473,6 +7473,18 @@ "node": ">= 18" } }, + "packages/redis/node_modules/@redis/json": { + "version": "5.1.1", + "resolved": "https://registry.npmjs.org/@redis/json/-/json-5.1.1.tgz", + "integrity": "sha512-A5M0dcgxGKq+oE6spIPBcGLDBiwoSPTs2wesVb4x30rXfG6rPtqt1Z7fCMtvTL2kHUNRKgZ78zhD+0+MENZt7g==", + "license": "MIT", + "engines": { + "node": ">= 18" + }, + "peerDependencies": { + "@redis/client": "^5.1.1" + } + }, "packages/search": { "name": "@redis/search", "version": "5.1.1", diff --git a/packages/json/package.json b/packages/json/package.json index 971f1adcce7..0d77a3bc6b1 100644 --- a/packages/json/package.json +++ b/packages/json/package.json @@ -1,6 +1,6 @@ { "name": "@redis/json", - "version": "5.1.1", + "version": "5.5.6", "license": "MIT", "main": "./dist/lib/index.js", "types": "./dist/lib/index.d.ts", From 8610a6c8369dfddd8814ca9d699ae56ec96a9426 Mon Sep 17 00:00:00 2001 From: Nikolay Karadzhov Date: Fri, 6 Jun 2025 10:15:45 +0300 Subject: [PATCH 1626/1748] Updated the Search package to use client@5.5.6 --- package-lock.json | 2 +- packages/search/package.json | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/package-lock.json b/package-lock.json index 22e3bb8c442..6aff9edf361 100644 --- a/package-lock.json +++ b/package-lock.json @@ -7496,7 +7496,7 @@ "node": ">= 18" }, "peerDependencies": { - "@redis/client": "^5.1.1" + "@redis/client": "^5.5.6" } }, "packages/test-utils": { diff --git a/packages/search/package.json b/packages/search/package.json index 6f7af2e0a25..998d785d410 100644 --- a/packages/search/package.json +++ b/packages/search/package.json @@ -14,7 +14,7 @@ "release": "release-it" }, "peerDependencies": { - "@redis/client": "^5.1.1" + "@redis/client": "^5.5.6" }, "devDependencies": { "@redis/test-utils": "*" From c9ecb3d65cf54aa735674f5c4790597eb751dc9e Mon Sep 17 00:00:00 2001 From: Nikolay Karadzhov Date: Fri, 6 Jun 2025 10:16:17 +0300 Subject: [PATCH 1627/1748] Release search@5.5.6 --- package-lock.json | 14 +++++++++++++- packages/search/package.json | 2 +- 2 files changed, 14 insertions(+), 2 deletions(-) diff --git a/package-lock.json b/package-lock.json index 6aff9edf361..95d93f347b1 100644 --- a/package-lock.json +++ b/package-lock.json @@ -7485,9 +7485,21 @@ "@redis/client": "^5.1.1" } }, + "packages/redis/node_modules/@redis/search": { + "version": "5.1.1", + "resolved": "https://registry.npmjs.org/@redis/search/-/search-5.1.1.tgz", + "integrity": "sha512-bChudQmcqfYUxEGMeXMkljXtwse4hzqcqRwbZDwRyYe+EEeW/lXVl3w/mS2tHnAb2yqGnfDghid8iHEtVNqjww==", + "license": "MIT", + "engines": { + "node": ">= 18" + }, + "peerDependencies": { + "@redis/client": "^5.1.1" + } + }, "packages/search": { "name": "@redis/search", - "version": "5.1.1", + "version": "5.5.6", "license": "MIT", "devDependencies": { "@redis/test-utils": "*" diff --git a/packages/search/package.json b/packages/search/package.json index 998d785d410..30edac8003e 100644 --- a/packages/search/package.json +++ b/packages/search/package.json @@ -1,6 +1,6 @@ { "name": "@redis/search", - "version": "5.1.1", + "version": "5.5.6", "license": "MIT", "main": "./dist/lib/index.js", "types": "./dist/lib/index.d.ts", From b07346e74a83c3c07c59126ddee3638ef7d7a359 Mon Sep 17 00:00:00 2001 From: Nikolay Karadzhov Date: Fri, 6 Jun 2025 10:18:25 +0300 Subject: [PATCH 1628/1748] Updated the Timeseries package to use client@5.5.6 --- package-lock.json | 2 +- packages/time-series/package.json | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/package-lock.json b/package-lock.json index 95d93f347b1..9feb22bc7b7 100644 --- a/package-lock.json +++ b/package-lock.json @@ -7586,7 +7586,7 @@ "node": ">= 18" }, "peerDependencies": { - "@redis/client": "^5.1.1" + "@redis/client": "^5.5.6" } } } diff --git a/packages/time-series/package.json b/packages/time-series/package.json index 4d5d8f01be5..e89bab86c8c 100644 --- a/packages/time-series/package.json +++ b/packages/time-series/package.json @@ -13,7 +13,7 @@ "release": "release-it" }, "peerDependencies": { - "@redis/client": "^5.1.1" + "@redis/client": "^5.5.6" }, "devDependencies": { "@redis/test-utils": "*" From 7deaf336e437dfd5cea99b4e45dd54769ae7e68e Mon Sep 17 00:00:00 2001 From: Nikolay Karadzhov Date: Fri, 6 Jun 2025 10:18:52 +0300 Subject: [PATCH 1629/1748] Release time-series@5.5.6 --- package-lock.json | 14 +++++++++++++- packages/time-series/package.json | 2 +- 2 files changed, 14 insertions(+), 2 deletions(-) diff --git a/package-lock.json b/package-lock.json index 9feb22bc7b7..fc697256e38 100644 --- a/package-lock.json +++ b/package-lock.json @@ -7497,6 +7497,18 @@ "@redis/client": "^5.1.1" } }, + "packages/redis/node_modules/@redis/time-series": { + "version": "5.1.1", + "resolved": "https://registry.npmjs.org/@redis/time-series/-/time-series-5.1.1.tgz", + "integrity": "sha512-HPjZLfcZxh5mBLqRgx7KCZG6JXxGnb7yJqo9qZ/KMTWK/k3SWyH47DHJbYbRNzKOEkbK/l/5kikDTm79uJuCbg==", + "license": "MIT", + "engines": { + "node": ">= 18" + }, + "peerDependencies": { + "@redis/client": "^5.1.1" + } + }, "packages/search": { "name": "@redis/search", "version": "5.5.6", @@ -7577,7 +7589,7 @@ }, "packages/time-series": { "name": "@redis/time-series", - "version": "5.1.1", + "version": "5.5.6", "license": "MIT", "devDependencies": { "@redis/test-utils": "*" diff --git a/packages/time-series/package.json b/packages/time-series/package.json index e89bab86c8c..f90bc8f5f9b 100644 --- a/packages/time-series/package.json +++ b/packages/time-series/package.json @@ -1,6 +1,6 @@ { "name": "@redis/time-series", - "version": "5.1.1", + "version": "5.5.6", "license": "MIT", "main": "./dist/lib/index.js", "types": "./dist/lib/index.d.ts", From ba0ba71aad841a8b7511e29deeb403260ffa8e71 Mon Sep 17 00:00:00 2001 From: Nikolay Karadzhov Date: Fri, 6 Jun 2025 10:20:13 +0300 Subject: [PATCH 1630/1748] Updated the Redis package to use client@5.5.6 --- package-lock.json | 70 +++---------------------------------- packages/redis/package.json | 10 +++--- 2 files changed, 10 insertions(+), 70 deletions(-) diff --git a/package-lock.json b/package-lock.json index fc697256e38..af8d1a88b7f 100644 --- a/package-lock.json +++ b/package-lock.json @@ -7439,76 +7439,16 @@ "version": "5.1.1", "license": "MIT", "dependencies": { - "@redis/bloom": "5.1.1", - "@redis/client": "5.1.1", - "@redis/json": "5.1.1", - "@redis/search": "5.1.1", - "@redis/time-series": "5.1.1" + "@redis/bloom": "5.5.6", + "@redis/client": "5.5.6", + "@redis/json": "5.5.6", + "@redis/search": "5.5.6", + "@redis/time-series": "5.5.6" }, "engines": { "node": ">= 18" } }, - "packages/redis/node_modules/@redis/bloom": { - "version": "5.1.1", - "resolved": "https://registry.npmjs.org/@redis/bloom/-/bloom-5.1.1.tgz", - "integrity": "sha512-PnMcvpL7O2DHtnSL5JtyNmraNrdHuJXi3u2isGTUuPgkbAuWQKfZdknq471ySILL+qKtLfVJqzgDFMjYmZzK6Q==", - "license": "MIT", - "engines": { - "node": ">= 18" - }, - "peerDependencies": { - "@redis/client": "^5.1.1" - } - }, - "packages/redis/node_modules/@redis/client": { - "version": "5.1.1", - "resolved": "https://registry.npmjs.org/@redis/client/-/client-5.1.1.tgz", - "integrity": "sha512-vojbBqUdbkD+ylCy3+ZDXLzSmgiYH9pLrv87kF+nDgsRaHKrVVxPV9B4u6EfWRx7XGvQGZqsXVkKFhsEOsG3LA==", - "license": "MIT", - "dependencies": { - "cluster-key-slot": "1.1.2" - }, - "engines": { - "node": ">= 18" - } - }, - "packages/redis/node_modules/@redis/json": { - "version": "5.1.1", - "resolved": "https://registry.npmjs.org/@redis/json/-/json-5.1.1.tgz", - "integrity": "sha512-A5M0dcgxGKq+oE6spIPBcGLDBiwoSPTs2wesVb4x30rXfG6rPtqt1Z7fCMtvTL2kHUNRKgZ78zhD+0+MENZt7g==", - "license": "MIT", - "engines": { - "node": ">= 18" - }, - "peerDependencies": { - "@redis/client": "^5.1.1" - } - }, - "packages/redis/node_modules/@redis/search": { - "version": "5.1.1", - "resolved": "https://registry.npmjs.org/@redis/search/-/search-5.1.1.tgz", - "integrity": "sha512-bChudQmcqfYUxEGMeXMkljXtwse4hzqcqRwbZDwRyYe+EEeW/lXVl3w/mS2tHnAb2yqGnfDghid8iHEtVNqjww==", - "license": "MIT", - "engines": { - "node": ">= 18" - }, - "peerDependencies": { - "@redis/client": "^5.1.1" - } - }, - "packages/redis/node_modules/@redis/time-series": { - "version": "5.1.1", - "resolved": "https://registry.npmjs.org/@redis/time-series/-/time-series-5.1.1.tgz", - "integrity": "sha512-HPjZLfcZxh5mBLqRgx7KCZG6JXxGnb7yJqo9qZ/KMTWK/k3SWyH47DHJbYbRNzKOEkbK/l/5kikDTm79uJuCbg==", - "license": "MIT", - "engines": { - "node": ">= 18" - }, - "peerDependencies": { - "@redis/client": "^5.1.1" - } - }, "packages/search": { "name": "@redis/search", "version": "5.5.6", diff --git a/packages/redis/package.json b/packages/redis/package.json index 889898b3c21..bdead544fde 100644 --- a/packages/redis/package.json +++ b/packages/redis/package.json @@ -13,11 +13,11 @@ "release": "release-it" }, "dependencies": { - "@redis/bloom": "5.1.1", - "@redis/client": "5.1.1", - "@redis/json": "5.1.1", - "@redis/search": "5.1.1", - "@redis/time-series": "5.1.1" + "@redis/bloom": "5.5.6", + "@redis/client": "5.5.6", + "@redis/json": "5.5.6", + "@redis/search": "5.5.6", + "@redis/time-series": "5.5.6" }, "engines": { "node": ">= 18" From ca91718b594aa624571639abfe5bb8195637bc53 Mon Sep 17 00:00:00 2001 From: Nikolay Karadzhov Date: Fri, 6 Jun 2025 10:21:11 +0300 Subject: [PATCH 1631/1748] Release redis@5.5.6 --- package-lock.json | 2 +- packages/redis/package.json | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/package-lock.json b/package-lock.json index af8d1a88b7f..d9fc9f93f92 100644 --- a/package-lock.json +++ b/package-lock.json @@ -7436,7 +7436,7 @@ } }, "packages/redis": { - "version": "5.1.1", + "version": "5.5.6", "license": "MIT", "dependencies": { "@redis/bloom": "5.5.6", diff --git a/packages/redis/package.json b/packages/redis/package.json index bdead544fde..bf5ea798e70 100644 --- a/packages/redis/package.json +++ b/packages/redis/package.json @@ -1,7 +1,7 @@ { "name": "redis", "description": "A modern, high performance Redis client", - "version": "5.1.1", + "version": "5.5.6", "license": "MIT", "main": "./dist/index.js", "types": "./dist/index.d.ts", From 62ac8b7c32473b9d0e45cbb628d05a910bc00a5f Mon Sep 17 00:00:00 2001 From: Nikolay Karadzhov Date: Fri, 6 Jun 2025 15:38:52 +0300 Subject: [PATCH 1632/1748] fix(client): make unstable cmds throw (#2990) As per the docs, unstableResp3 commands should throw when client is created with { RESP: 3, unstableResp3: false|undefined } fixes #2989 --- docs/v5.md | 4 +-- packages/client/lib/commander.ts | 6 +++- packages/client/lib/commands/XREAD.spec.ts | 33 +++++++++++++++++++ .../client/lib/commands/XREADGROUP.spec.ts | 32 ++++++++++++++++++ 4 files changed, 72 insertions(+), 3 deletions(-) diff --git a/docs/v5.md b/docs/v5.md index 1784ae5bd74..15ef67c14ee 100644 --- a/docs/v5.md +++ b/docs/v5.md @@ -42,9 +42,9 @@ RESP3 uses a different mechanism for handling Pub/Sub messages. Instead of modif ## Known Limitations -### Unstable Module Commands +### Unstable Commands -Some Redis module commands have unstable RESP3 transformations. These commands will throw an error when used with RESP3 unless you explicitly opt in to using them by setting `unstableResp3: true` in your client configuration: +Some Redis commands have unstable RESP3 transformations. These commands will throw an error when used with RESP3 unless you explicitly opt in to using them by setting `unstableResp3: true` in your client configuration: ```javascript const client = createClient({ diff --git a/packages/client/lib/commander.ts b/packages/client/lib/commander.ts index 6e5a2687cb1..cfdf39526cc 100644 --- a/packages/client/lib/commander.ts +++ b/packages/client/lib/commander.ts @@ -38,7 +38,11 @@ export function attachConfig< Class: any = class extends BaseClass {}; for (const [name, command] of Object.entries(commands)) { - Class.prototype[name] = createCommand(command, RESP); + if (config?.RESP == 3 && command.unstableResp3 && !config.unstableResp3) { + Class.prototype[name] = throwResp3SearchModuleUnstableError; + } else { + Class.prototype[name] = createCommand(command, RESP); + } } if (config?.modules) { diff --git a/packages/client/lib/commands/XREAD.spec.ts b/packages/client/lib/commands/XREAD.spec.ts index bb72c96497e..0edcfe43117 100644 --- a/packages/client/lib/commands/XREAD.spec.ts +++ b/packages/client/lib/commands/XREAD.spec.ts @@ -131,4 +131,37 @@ describe('XREAD', () => { client: GLOBAL.SERVERS.OPEN, cluster: GLOBAL.CLUSTERS.OPEN }); + + testUtils.testWithClient('client.xRead should throw with resp3 and unstableResp3: false', async client => { + assert.throws( + () => client.xRead({ + key: 'key', + id: '0-0' + }), + { + message: 'Some RESP3 results for Redis Query Engine responses may change. Refer to the readme for guidance' + } + ); + }, { + ...GLOBAL.SERVERS.OPEN, + clientOptions: { + RESP: 3 + } + }); + + testUtils.testWithClient('client.xRead should not throw with resp3 and unstableResp3: true', async client => { + assert.doesNotThrow( + () => client.xRead({ + key: 'key', + id: '0-0' + }) + ); + }, { + ...GLOBAL.SERVERS.OPEN, + clientOptions: { + RESP: 3, + unstableResp3: true + } + }); + }); diff --git a/packages/client/lib/commands/XREADGROUP.spec.ts b/packages/client/lib/commands/XREADGROUP.spec.ts index 085a67bc9b3..acc7cc2dea9 100644 --- a/packages/client/lib/commands/XREADGROUP.spec.ts +++ b/packages/client/lib/commands/XREADGROUP.spec.ts @@ -155,4 +155,36 @@ describe('XREADGROUP', () => { client: GLOBAL.SERVERS.OPEN, cluster: GLOBAL.CLUSTERS.OPEN }); + + testUtils.testWithClient('client.xReadGroup should throw with resp3 and unstableResp3: false', async client => { + assert.throws( + () => client.xReadGroup('group', 'consumer', { + key: 'key', + id: '>' + }), + { + message: 'Some RESP3 results for Redis Query Engine responses may change. Refer to the readme for guidance' + } + ); + }, { + ...GLOBAL.SERVERS.OPEN, + clientOptions: { + RESP: 3 + } + }); + + testUtils.testWithClient('client.xReadGroup should not throw with resp3 and unstableResp3: true', async client => { + assert.doesNotThrow( + () => client.xReadGroup('group', 'consumer', { + key: 'key', + id: '>' + }) + ); + }, { + ...GLOBAL.SERVERS.OPEN, + clientOptions: { + RESP: 3, + unstableResp3: true + } + }); }); From 5d205cf161acce0111d344ef625e3ac4a0488b76 Mon Sep 17 00:00:00 2001 From: Nikolay Karadzhov Date: Mon, 16 Jun 2025 10:20:49 +0300 Subject: [PATCH 1633/1748] chore(tests): bump test container version 8.0.2 (#2997) --- .github/workflows/tests.yml | 2 +- packages/bloom/lib/test-utils.ts | 2 +- packages/client/lib/sentinel/test-util.ts | 2 +- packages/client/lib/test-utils.ts | 2 +- packages/entraid/lib/test-utils.ts | 2 +- packages/json/lib/test-utils.ts | 2 +- packages/search/lib/test-utils.ts | 2 +- packages/time-series/lib/test-utils.ts | 2 +- 8 files changed, 8 insertions(+), 8 deletions(-) diff --git a/.github/workflows/tests.yml b/.github/workflows/tests.yml index 395e0251018..d7bd649900d 100644 --- a/.github/workflows/tests.yml +++ b/.github/workflows/tests.yml @@ -22,7 +22,7 @@ jobs: fail-fast: false matrix: node-version: ["18", "20", "22"] - redis-version: ["rs-7.2.0-v13", "rs-7.4.0-v1", "8.0.1-pre"] + redis-version: ["rs-7.2.0-v13", "rs-7.4.0-v1", "8.0.2"] steps: - uses: actions/checkout@v4 with: diff --git a/packages/bloom/lib/test-utils.ts b/packages/bloom/lib/test-utils.ts index 71b423b41ea..62a1f40e872 100644 --- a/packages/bloom/lib/test-utils.ts +++ b/packages/bloom/lib/test-utils.ts @@ -4,7 +4,7 @@ import RedisBloomModules from '.'; export default TestUtils.createFromConfig({ dockerImageName: 'redislabs/client-libs-test', dockerImageVersionArgument: 'redis-version', - defaultDockerVersion: '8.0-M05-pre' + defaultDockerVersion: '8.0.2' }); export const GLOBAL = { diff --git a/packages/client/lib/sentinel/test-util.ts b/packages/client/lib/sentinel/test-util.ts index 60c1a59689a..06f8f74093c 100644 --- a/packages/client/lib/sentinel/test-util.ts +++ b/packages/client/lib/sentinel/test-util.ts @@ -174,7 +174,7 @@ export class SentinelFramework extends DockerBase { this.#testUtils = TestUtils.createFromConfig({ dockerImageName: 'redislabs/client-libs-test', dockerImageVersionArgument: 'redis-version', - defaultDockerVersion: '8.0-M05-pre' + defaultDockerVersion: '8.0.2' }); this.#nodeMap = new Map>>>(); this.#sentinelMap = new Map>>>(); diff --git a/packages/client/lib/test-utils.ts b/packages/client/lib/test-utils.ts index 19bbafc66ea..48736b56cd0 100644 --- a/packages/client/lib/test-utils.ts +++ b/packages/client/lib/test-utils.ts @@ -9,7 +9,7 @@ import RedisBloomModules from '@redis/bloom'; const utils = TestUtils.createFromConfig({ dockerImageName: 'redislabs/client-libs-test', dockerImageVersionArgument: 'redis-version', - defaultDockerVersion: '8.0-M05-pre' + defaultDockerVersion: '8.0.2' }); export default utils; diff --git a/packages/entraid/lib/test-utils.ts b/packages/entraid/lib/test-utils.ts index 11ad498f0b3..1beac939138 100644 --- a/packages/entraid/lib/test-utils.ts +++ b/packages/entraid/lib/test-utils.ts @@ -6,7 +6,7 @@ import { EntraidCredentialsProvider } from './entraid-credentials-provider'; export const testUtils = TestUtils.createFromConfig({ dockerImageName: 'redislabs/client-libs-test', dockerImageVersionArgument: 'redis-version', - defaultDockerVersion: '8.0-M05-pre' + defaultDockerVersion: '8.0.2' }); const DEBUG_MODE_ARGS = testUtils.isVersionGreaterThan([7]) ? diff --git a/packages/json/lib/test-utils.ts b/packages/json/lib/test-utils.ts index 9894b2d0399..0d250449ac0 100644 --- a/packages/json/lib/test-utils.ts +++ b/packages/json/lib/test-utils.ts @@ -4,7 +4,7 @@ import RedisJSON from '.'; export default TestUtils.createFromConfig({ dockerImageName: 'redislabs/client-libs-test', dockerImageVersionArgument: 'redis-version', - defaultDockerVersion: '8.0-M05-pre' + defaultDockerVersion: '8.0.2' }); export const GLOBAL = { diff --git a/packages/search/lib/test-utils.ts b/packages/search/lib/test-utils.ts index 7264b1b6b12..354ca16a287 100644 --- a/packages/search/lib/test-utils.ts +++ b/packages/search/lib/test-utils.ts @@ -5,7 +5,7 @@ import { RespVersions } from '@redis/client'; export default TestUtils.createFromConfig({ dockerImageName: 'redislabs/client-libs-test', dockerImageVersionArgument: 'redis-version', - defaultDockerVersion: '8.0-M05-pre' + defaultDockerVersion: '8.0.2' }); export const GLOBAL = { diff --git a/packages/time-series/lib/test-utils.ts b/packages/time-series/lib/test-utils.ts index 0f25341e34d..ab3f1cebf7d 100644 --- a/packages/time-series/lib/test-utils.ts +++ b/packages/time-series/lib/test-utils.ts @@ -4,7 +4,7 @@ import TimeSeries from '.'; export default TestUtils.createFromConfig({ dockerImageName: 'redislabs/client-libs-test', dockerImageVersionArgument: 'redis-version', - defaultDockerVersion: '8.0-M05-pre' + defaultDockerVersion: '8.0.2' }); export const GLOBAL = { From 2b3140bb72657af328c8b0f814c4901b0dd2ed52 Mon Sep 17 00:00:00 2001 From: Nikolay Karadzhov Date: Thu, 19 Jun 2025 10:05:22 +0300 Subject: [PATCH 1634/1748] chore(actions): add action to close stale issues (#3000) --- .github/workflows/stale-issues.yml | 25 +++++++++++++++++++++++++ 1 file changed, 25 insertions(+) create mode 100644 .github/workflows/stale-issues.yml diff --git a/.github/workflows/stale-issues.yml b/.github/workflows/stale-issues.yml new file mode 100644 index 00000000000..445af1c818c --- /dev/null +++ b/.github/workflows/stale-issues.yml @@ -0,0 +1,25 @@ +name: "Close stale issues" +on: + schedule: + - cron: "0 0 * * *" + +permissions: {} +jobs: + stale: + permissions: + issues: write # to close stale issues (actions/stale) + pull-requests: write # to close stale PRs (actions/stale) + + runs-on: ubuntu-latest + steps: + - uses: actions/stale@v9 + with: + repo-token: ${{ secrets.GITHUB_TOKEN }} + stale-issue-message: 'This issue is marked stale. It will be closed in 30 days if it is not updated.' + stale-pr-message: 'This pull request is marked stale. It will be closed in 30 days if it is not updated.' + days-before-stale: 365 + days-before-close: 30 + stale-issue-label: "Stale" + stale-pr-label: "Stale" + operations-per-run: 10 + remove-stale-when-updated: true From b52177752e1120f9e587b0a0062019a472f59af1 Mon Sep 17 00:00:00 2001 From: Pavel Pashov <60297174+PavelPashov@users.noreply.github.com> Date: Thu, 19 Jun 2025 13:56:00 +0300 Subject: [PATCH 1635/1748] feat: added support for new bitop operations (#3001) refactored bitop tests, covered all operations updated default docker version on all packages --- .github/workflows/tests.yml | 2 +- packages/bloom/lib/test-utils.ts | 2 +- packages/client/lib/commands/BITOP.spec.ts | 70 +++++++++++++++++++++- packages/client/lib/commands/BITOP.ts | 4 +- packages/client/lib/sentinel/test-util.ts | 2 +- packages/client/lib/test-utils.ts | 2 +- packages/entraid/lib/test-utils.ts | 2 +- packages/json/lib/test-utils.ts | 2 +- packages/search/lib/test-utils.ts | 2 +- packages/time-series/lib/test-utils.ts | 2 +- 10 files changed, 77 insertions(+), 13 deletions(-) diff --git a/.github/workflows/tests.yml b/.github/workflows/tests.yml index d7bd649900d..89efdb61114 100644 --- a/.github/workflows/tests.yml +++ b/.github/workflows/tests.yml @@ -22,7 +22,7 @@ jobs: fail-fast: false matrix: node-version: ["18", "20", "22"] - redis-version: ["rs-7.2.0-v13", "rs-7.4.0-v1", "8.0.2"] + redis-version: ["rs-7.2.0-v13", "rs-7.4.0-v1", "8.0.2", "8.2-M01-pre"] steps: - uses: actions/checkout@v4 with: diff --git a/packages/bloom/lib/test-utils.ts b/packages/bloom/lib/test-utils.ts index 62a1f40e872..4396c94f726 100644 --- a/packages/bloom/lib/test-utils.ts +++ b/packages/bloom/lib/test-utils.ts @@ -4,7 +4,7 @@ import RedisBloomModules from '.'; export default TestUtils.createFromConfig({ dockerImageName: 'redislabs/client-libs-test', dockerImageVersionArgument: 'redis-version', - defaultDockerVersion: '8.0.2' + defaultDockerVersion: '8.2-M01-pre' }); export const GLOBAL = { diff --git a/packages/client/lib/commands/BITOP.spec.ts b/packages/client/lib/commands/BITOP.spec.ts index 25fe48fc13c..65fe6f86338 100644 --- a/packages/client/lib/commands/BITOP.spec.ts +++ b/packages/client/lib/commands/BITOP.spec.ts @@ -1,6 +1,6 @@ import { strict as assert } from 'node:assert'; import testUtils, { GLOBAL } from '../test-utils'; -import BITOP from './BITOP'; +import BITOP, { BitOperations } from './BITOP'; import { parseArgs } from './generic-transformers'; describe('BITOP', () => { @@ -20,13 +20,77 @@ describe('BITOP', () => { }); }); - testUtils.testAll('bitOp', async client => { + for (const op of ['AND', 'OR', 'XOR'] as BitOperations[]) { + testUtils.testAll(`bitOp ${op} with non-existing keys`, async client => { + assert.equal( + await client.bitOp(op, '{tag}destKey', ['{tag}key1', '{tag}key2']), + 0 + ); + }, { + client: GLOBAL.SERVERS.OPEN, + cluster: GLOBAL.CLUSTERS.OPEN + }); + + testUtils.testAll(`bitOp ${op} with existing keys`, async client => { + await client.set('{tag}key1', 'value1'); + await client.set('{tag}key2', 'value2'); + + assert.equal( + await client.bitOp(op, '{tag}destKey', ['{tag}key1', '{tag}key2']), + 6 + ); + }, { + client: GLOBAL.SERVERS.OPEN, + cluster: GLOBAL.CLUSTERS.OPEN + }); + } + + // NOT operation requires only one key + testUtils.testAll('bitOp NOT with non-existing keys', async client => { assert.equal( - await client.bitOp('AND', '{tag}destKey', '{tag}key'), + await client.bitOp('NOT', '{tag}destKey', '{tag}key'), 0 ); }, { client: GLOBAL.SERVERS.OPEN, cluster: GLOBAL.CLUSTERS.OPEN }); + + testUtils.testAll('bitOp NOT with existing keys', async client => { + await client.set('{tag}key', 'value'); + + assert.equal( + await client.bitOp('NOT', '{tag}destKey', '{tag}key'), + 5 + ); + }, { + client: GLOBAL.SERVERS.OPEN, + cluster: GLOBAL.CLUSTERS.OPEN + }); + + // newer operations supported since Redis 8.2 + for (const op of ['DIFF', 'DIFF1', 'ANDOR', 'ONE'] as BitOperations[]) { + testUtils.testAll(`bitOp ${op} with non-existing keys`, async client => { + assert.equal( + await client.bitOp(op, '{tag}destKey', ['{tag}key1', '{tag}key2']), + 0 + ); + }, { + client: { ...GLOBAL.SERVERS.OPEN, minimumDockerVersion: [8, 2] }, + cluster: { ...GLOBAL.CLUSTERS.OPEN, minimumDockerVersion: [8, 2] }, + }); + + testUtils.testAll(`bitOp ${op} with existing keys`, async client => { + await client.set('{tag}key1', 'value1'); + await client.set('{tag}key2', 'value2'); + + assert.equal( + await client.bitOp(op, '{tag}destKey', ['{tag}key1', '{tag}key2']), + 6 + ); + }, { + client: { ...GLOBAL.SERVERS.OPEN, minimumDockerVersion: [8, 2] }, + cluster: { ...GLOBAL.CLUSTERS.OPEN, minimumDockerVersion: [8, 2] }, + }); + } }); diff --git a/packages/client/lib/commands/BITOP.ts b/packages/client/lib/commands/BITOP.ts index cfce482fcb5..da8b97ceda6 100644 --- a/packages/client/lib/commands/BITOP.ts +++ b/packages/client/lib/commands/BITOP.ts @@ -2,14 +2,14 @@ import { CommandParser } from '../client/parser'; import { NumberReply, Command, RedisArgument } from '../RESP/types'; import { RedisVariadicArgument } from './generic-transformers'; -export type BitOperations = 'AND' | 'OR' | 'XOR' | 'NOT'; +export type BitOperations = 'AND' | 'OR' | 'XOR' | 'NOT' | 'DIFF' | 'DIFF1' | 'ANDOR' | 'ONE'; export default { IS_READ_ONLY: false, /** * Performs bitwise operations between strings * @param parser - The Redis command parser - * @param operation - Bitwise operation to perform: AND, OR, XOR, NOT + * @param operation - Bitwise operation to perform: AND, OR, XOR, NOT, DIFF, DIFF1, ANDOR, ONE * @param destKey - Destination key to store the result * @param key - Source key(s) to perform operation on */ diff --git a/packages/client/lib/sentinel/test-util.ts b/packages/client/lib/sentinel/test-util.ts index 06f8f74093c..c8efa47f41d 100644 --- a/packages/client/lib/sentinel/test-util.ts +++ b/packages/client/lib/sentinel/test-util.ts @@ -174,7 +174,7 @@ export class SentinelFramework extends DockerBase { this.#testUtils = TestUtils.createFromConfig({ dockerImageName: 'redislabs/client-libs-test', dockerImageVersionArgument: 'redis-version', - defaultDockerVersion: '8.0.2' + defaultDockerVersion: '8.2-M01-pre' }); this.#nodeMap = new Map>>>(); this.#sentinelMap = new Map>>>(); diff --git a/packages/client/lib/test-utils.ts b/packages/client/lib/test-utils.ts index 48736b56cd0..809ee788e92 100644 --- a/packages/client/lib/test-utils.ts +++ b/packages/client/lib/test-utils.ts @@ -9,7 +9,7 @@ import RedisBloomModules from '@redis/bloom'; const utils = TestUtils.createFromConfig({ dockerImageName: 'redislabs/client-libs-test', dockerImageVersionArgument: 'redis-version', - defaultDockerVersion: '8.0.2' + defaultDockerVersion: '8.2-M01-pre' }); export default utils; diff --git a/packages/entraid/lib/test-utils.ts b/packages/entraid/lib/test-utils.ts index 1beac939138..3c561d4ba44 100644 --- a/packages/entraid/lib/test-utils.ts +++ b/packages/entraid/lib/test-utils.ts @@ -6,7 +6,7 @@ import { EntraidCredentialsProvider } from './entraid-credentials-provider'; export const testUtils = TestUtils.createFromConfig({ dockerImageName: 'redislabs/client-libs-test', dockerImageVersionArgument: 'redis-version', - defaultDockerVersion: '8.0.2' + defaultDockerVersion: '8.2-M01-pre' }); const DEBUG_MODE_ARGS = testUtils.isVersionGreaterThan([7]) ? diff --git a/packages/json/lib/test-utils.ts b/packages/json/lib/test-utils.ts index 0d250449ac0..6b6859d61bf 100644 --- a/packages/json/lib/test-utils.ts +++ b/packages/json/lib/test-utils.ts @@ -4,7 +4,7 @@ import RedisJSON from '.'; export default TestUtils.createFromConfig({ dockerImageName: 'redislabs/client-libs-test', dockerImageVersionArgument: 'redis-version', - defaultDockerVersion: '8.0.2' + defaultDockerVersion: '8.2-M01-pre' }); export const GLOBAL = { diff --git a/packages/search/lib/test-utils.ts b/packages/search/lib/test-utils.ts index 354ca16a287..a2b9c816da1 100644 --- a/packages/search/lib/test-utils.ts +++ b/packages/search/lib/test-utils.ts @@ -5,7 +5,7 @@ import { RespVersions } from '@redis/client'; export default TestUtils.createFromConfig({ dockerImageName: 'redislabs/client-libs-test', dockerImageVersionArgument: 'redis-version', - defaultDockerVersion: '8.0.2' + defaultDockerVersion: '8.2-M01-pre' }); export const GLOBAL = { diff --git a/packages/time-series/lib/test-utils.ts b/packages/time-series/lib/test-utils.ts index ab3f1cebf7d..8a664ee8df2 100644 --- a/packages/time-series/lib/test-utils.ts +++ b/packages/time-series/lib/test-utils.ts @@ -4,7 +4,7 @@ import TimeSeries from '.'; export default TestUtils.createFromConfig({ dockerImageName: 'redislabs/client-libs-test', dockerImageVersionArgument: 'redis-version', - defaultDockerVersion: '8.0.2' + defaultDockerVersion: '8.2-M01-pre' }); export const GLOBAL = { From c5b4f47975efea2347d923a32326808ce15a19be Mon Sep 17 00:00:00 2001 From: Nikolay Karadzhov Date: Tue, 24 Jun 2025 13:35:29 +0300 Subject: [PATCH 1636/1748] feat: add support for vector sets (#2998) * wip * improve the vadd api * resp3 tests * fix some tests * extract json helper functions in client package * use transformJsonReply * remove the CACHEABLE flag for all vector set commands currently, client side caching is not supported for vector set commands by the server * properly transform vinfo result * add resp3 test for vlinks * add more tests for vrandmember * fix vrem return types * fix vsetattr return type * fix vsim_withscores * implement vlinks_withscores * set minimum docker image version to 8 * align return types * add RAW variant for VEMB -> VEMB_RAW * use the new parseCommand api --- packages/client/lib/commands/VADD.spec.ts | 121 +++++++++++ packages/client/lib/commands/VADD.ts | 65 ++++++ packages/client/lib/commands/VCARD.spec.ts | 60 ++++++ packages/client/lib/commands/VCARD.ts | 18 ++ packages/client/lib/commands/VDIM.spec.ts | 43 ++++ packages/client/lib/commands/VDIM.ts | 18 ++ packages/client/lib/commands/VEMB.spec.ts | 42 ++++ packages/client/lib/commands/VEMB.ts | 21 ++ packages/client/lib/commands/VEMB_RAW.spec.ts | 68 ++++++ packages/client/lib/commands/VEMB_RAW.ts | 57 +++++ packages/client/lib/commands/VGETATTR.spec.ts | 77 +++++++ packages/client/lib/commands/VGETATTR.ts | 21 ++ packages/client/lib/commands/VINFO.spec.ts | 58 +++++ packages/client/lib/commands/VINFO.ts | 38 ++++ packages/client/lib/commands/VLINKS.spec.ts | 42 ++++ packages/client/lib/commands/VLINKS.ts | 20 ++ .../lib/commands/VLINKS_WITHSCORES.spec.ts | 75 +++++++ .../client/lib/commands/VLINKS_WITHSCORES.ts | 42 ++++ .../client/lib/commands/VRANDMEMBER.spec.ts | 201 ++++++++++++++++++ packages/client/lib/commands/VRANDMEMBER.ts | 23 ++ packages/client/lib/commands/VREM.spec.ts | 63 ++++++ packages/client/lib/commands/VREM.ts | 20 ++ packages/client/lib/commands/VSETATTR.spec.ts | 58 +++++ packages/client/lib/commands/VSETATTR.ts | 32 +++ packages/client/lib/commands/VSIM.spec.ts | 85 ++++++++ packages/client/lib/commands/VSIM.ts | 68 ++++++ .../lib/commands/VSIM_WITHSCORES.spec.ts | 62 ++++++ .../client/lib/commands/VSIM_WITHSCORES.ts | 36 ++++ .../lib/commands/generic-transformers.ts | 18 ++ packages/client/lib/commands/index.ts | 44 +++- packages/json/lib/commands/ARRAPPEND.ts | 2 +- packages/json/lib/commands/ARRINDEX.ts | 2 +- packages/json/lib/commands/ARRINSERT.ts | 2 +- packages/json/lib/commands/ARRPOP.ts | 3 +- packages/json/lib/commands/GET.ts | 3 +- packages/json/lib/commands/MERGE.ts | 2 +- packages/json/lib/commands/MGET.ts | 2 +- packages/json/lib/commands/MSET.ts | 2 +- packages/json/lib/commands/SET.ts | 2 +- packages/json/lib/commands/STRAPPEND.ts | 2 +- packages/json/lib/commands/helpers.ts | 20 -- packages/json/lib/commands/index.ts | 4 +- 42 files changed, 1608 insertions(+), 34 deletions(-) create mode 100644 packages/client/lib/commands/VADD.spec.ts create mode 100644 packages/client/lib/commands/VADD.ts create mode 100644 packages/client/lib/commands/VCARD.spec.ts create mode 100644 packages/client/lib/commands/VCARD.ts create mode 100644 packages/client/lib/commands/VDIM.spec.ts create mode 100644 packages/client/lib/commands/VDIM.ts create mode 100644 packages/client/lib/commands/VEMB.spec.ts create mode 100644 packages/client/lib/commands/VEMB.ts create mode 100644 packages/client/lib/commands/VEMB_RAW.spec.ts create mode 100644 packages/client/lib/commands/VEMB_RAW.ts create mode 100644 packages/client/lib/commands/VGETATTR.spec.ts create mode 100644 packages/client/lib/commands/VGETATTR.ts create mode 100644 packages/client/lib/commands/VINFO.spec.ts create mode 100644 packages/client/lib/commands/VINFO.ts create mode 100644 packages/client/lib/commands/VLINKS.spec.ts create mode 100644 packages/client/lib/commands/VLINKS.ts create mode 100644 packages/client/lib/commands/VLINKS_WITHSCORES.spec.ts create mode 100644 packages/client/lib/commands/VLINKS_WITHSCORES.ts create mode 100644 packages/client/lib/commands/VRANDMEMBER.spec.ts create mode 100644 packages/client/lib/commands/VRANDMEMBER.ts create mode 100644 packages/client/lib/commands/VREM.spec.ts create mode 100644 packages/client/lib/commands/VREM.ts create mode 100644 packages/client/lib/commands/VSETATTR.spec.ts create mode 100644 packages/client/lib/commands/VSETATTR.ts create mode 100644 packages/client/lib/commands/VSIM.spec.ts create mode 100644 packages/client/lib/commands/VSIM.ts create mode 100644 packages/client/lib/commands/VSIM_WITHSCORES.spec.ts create mode 100644 packages/client/lib/commands/VSIM_WITHSCORES.ts delete mode 100644 packages/json/lib/commands/helpers.ts diff --git a/packages/client/lib/commands/VADD.spec.ts b/packages/client/lib/commands/VADD.spec.ts new file mode 100644 index 00000000000..e064beab498 --- /dev/null +++ b/packages/client/lib/commands/VADD.spec.ts @@ -0,0 +1,121 @@ +import { strict as assert } from 'node:assert'; +import testUtils, { GLOBAL } from '../test-utils'; +import VADD from './VADD'; +import { BasicCommandParser } from '../client/parser'; + +describe('VADD', () => { + describe('parseCommand', () => { + it('basic usage', () => { + const parser = new BasicCommandParser(); + VADD.parseCommand(parser, 'key', [1.0, 2.0, 3.0], 'element'); + assert.deepEqual( + parser.redisArgs, + ['VADD', 'key', 'VALUES', '3', '1', '2', '3', 'element'] + ); + }); + + it('with REDUCE option', () => { + const parser = new BasicCommandParser(); + VADD.parseCommand(parser, 'key', [1.0, 2], 'element', { REDUCE: 50 }); + assert.deepEqual( + parser.redisArgs, + ['VADD', 'key', 'REDUCE', '50', 'VALUES', '2', '1', '2', 'element'] + ); + }); + + it('with quantization options', () => { + let parser = new BasicCommandParser(); + VADD.parseCommand(parser, 'key', [1.0, 2.0], 'element', { QUANT: 'Q8' }); + assert.deepEqual( + parser.redisArgs, + ['VADD', 'key', 'VALUES', '2', '1', '2', 'element', 'Q8'] + ); + + parser = new BasicCommandParser(); + VADD.parseCommand(parser, 'key', [1.0, 2.0], 'element', { QUANT: 'BIN' }); + assert.deepEqual( + parser.redisArgs, + ['VADD', 'key', 'VALUES', '2', '1', '2', 'element', 'BIN'] + ); + + parser = new BasicCommandParser(); + VADD.parseCommand(parser, 'key', [1.0, 2.0], 'element', { QUANT: 'NOQUANT' }); + assert.deepEqual( + parser.redisArgs, + ['VADD', 'key', 'VALUES', '2', '1', '2', 'element', 'NOQUANT'] + ); + }); + + it('with all options', () => { + const parser = new BasicCommandParser(); + VADD.parseCommand(parser, 'key', [1.0, 2.0], 'element', { + REDUCE: 50, + CAS: true, + QUANT: 'Q8', + EF: 200, + SETATTR: { name: 'test', value: 42 }, + M: 16 + }); + assert.deepEqual( + parser.redisArgs, + [ + 'VADD', 'key', 'REDUCE', '50', 'VALUES', '2', '1', '2', 'element', + 'CAS', 'Q8', 'EF', '200', 'SETATTR', '{"name":"test","value":42}', 'M', '16' + ] + ); + }); + }); + + testUtils.testAll('vAdd', async client => { + assert.equal( + await client.vAdd('key', [1.0, 2.0, 3.0], 'element'), + true + ); + + // same element should not be added again + assert.equal( + await client.vAdd('key', [1, 2 , 3], 'element'), + false + ); + + }, { + client: { ...GLOBAL.SERVERS.OPEN, minimumDockerVersion: [8, 0] }, + cluster: { ...GLOBAL.CLUSTERS.OPEN, minimumDockerVersion: [8, 0] }, + }); + + testUtils.testWithClient('vAdd with RESP3', async client => { + // Test basic functionality with RESP3 + assert.equal( + await client.vAdd('resp3-key', [1.5, 2.5, 3.5], 'resp3-element'), + true + ); + + // same element should not be added again + assert.equal( + await client.vAdd('resp3-key', [1, 2 , 3], 'resp3-element'), + false + ); + + // Test with options to ensure complex parameters work with RESP3 + assert.equal( + await client.vAdd('resp3-key', [4.0, 5.0, 6.0], 'resp3-element2', { + QUANT: 'Q8', + CAS: true, + SETATTR: { type: 'test', value: 123 } + }), + true + ); + + // Verify the vector set was created correctly + assert.equal( + await client.vCard('resp3-key'), + 2 + ); + }, { + ...GLOBAL.SERVERS.OPEN, + clientOptions: { + RESP: 3 + }, + minimumDockerVersion: [8, 0] + }); +}); diff --git a/packages/client/lib/commands/VADD.ts b/packages/client/lib/commands/VADD.ts new file mode 100644 index 00000000000..0406bd58d03 --- /dev/null +++ b/packages/client/lib/commands/VADD.ts @@ -0,0 +1,65 @@ +import { CommandParser } from '../client/parser'; +import { RedisArgument, Command } from '../RESP/types'; +import { transformBooleanReply, transformDoubleArgument } from './generic-transformers'; + +export interface VAddOptions { + REDUCE?: number; + CAS?: boolean; + QUANT?: 'NOQUANT' | 'BIN' | 'Q8', + EF?: number; + SETATTR?: Record; + M?: number; +} + +export default { + /** + * Add a new element into the vector set specified by key + * + * @param parser - The command parser + * @param key - The name of the key that will hold the vector set data + * @param vector - The vector data as array of numbers + * @param element - The name of the element being added to the vector set + * @param options - Optional parameters for vector addition + * @see https://redis.io/commands/vadd/ + */ + parseCommand( + parser: CommandParser, + key: RedisArgument, + vector: Array, + element: RedisArgument, + options?: VAddOptions + ) { + parser.push('VADD'); + parser.pushKey(key); + + if (options?.REDUCE !== undefined) { + parser.push('REDUCE', options.REDUCE.toString()); + } + + parser.push('VALUES', vector.length.toString()); + for (const value of vector) { + parser.push(transformDoubleArgument(value)); + } + + parser.push(element); + + if (options?.CAS) { + parser.push('CAS'); + } + + options?.QUANT && parser.push(options.QUANT); + + if (options?.EF !== undefined) { + parser.push('EF', options.EF.toString()); + } + + if (options?.SETATTR) { + parser.push('SETATTR', JSON.stringify(options.SETATTR)); + } + + if (options?.M !== undefined) { + parser.push('M', options.M.toString()); + } + }, + transformReply: transformBooleanReply +} as const satisfies Command; diff --git a/packages/client/lib/commands/VCARD.spec.ts b/packages/client/lib/commands/VCARD.spec.ts new file mode 100644 index 00000000000..feb9040fcb7 --- /dev/null +++ b/packages/client/lib/commands/VCARD.spec.ts @@ -0,0 +1,60 @@ +import { strict as assert } from 'node:assert'; +import testUtils, { GLOBAL } from '../test-utils'; +import VCARD from './VCARD'; +import { BasicCommandParser } from '../client/parser'; + +describe('VCARD', () => { + it('parseCommand', () => { + const parser = new BasicCommandParser(); + VCARD.parseCommand(parser, 'key') + assert.deepEqual( + parser.redisArgs, + ['VCARD', 'key'] + ); + }); + + testUtils.testAll('vCard', async client => { + await client.vAdd('key', [1.0, 2.0, 3.0], 'element1'); + await client.vAdd('key', [4.0, 5.0, 6.0], 'element2'); + + assert.equal( + await client.vCard('key'), + 2 + ); + + assert.equal(await client.vCard('unknown'), 0); + }, { + client: { ...GLOBAL.SERVERS.OPEN, minimumDockerVersion: [8, 0] }, + cluster: { ...GLOBAL.CLUSTERS.OPEN, minimumDockerVersion: [8, 0] } + }); + + testUtils.testWithClient('vCard with RESP3', async client => { + // Test empty vector set + assert.equal( + await client.vCard('resp3-empty-key'), + 0 + ); + + // Add elements and test cardinality + await client.vAdd('resp3-key', [1.0, 2.0], 'elem1'); + assert.equal( + await client.vCard('resp3-key'), + 1 + ); + + await client.vAdd('resp3-key', [3.0, 4.0], 'elem2'); + await client.vAdd('resp3-key', [5.0, 6.0], 'elem3'); + assert.equal( + await client.vCard('resp3-key'), + 3 + ); + + assert.equal(await client.vCard('unknown'), 0); + }, { + ...GLOBAL.SERVERS.OPEN, + clientOptions: { + RESP: 3 + }, + minimumDockerVersion: [8, 0] + }); +}); diff --git a/packages/client/lib/commands/VCARD.ts b/packages/client/lib/commands/VCARD.ts new file mode 100644 index 00000000000..575abf9b710 --- /dev/null +++ b/packages/client/lib/commands/VCARD.ts @@ -0,0 +1,18 @@ +import { CommandParser } from '../client/parser'; +import { RedisArgument, NumberReply, Command } from '../RESP/types'; + +export default { + IS_READ_ONLY: true, + /** + * Retrieve the number of elements in a vector set + * + * @param parser - The command parser + * @param key - The key of the vector set + * @see https://redis.io/commands/vcard/ + */ + parseCommand(parser: CommandParser, key: RedisArgument) { + parser.push('VCARD'); + parser.pushKey(key); + }, + transformReply: undefined as unknown as () => NumberReply +} as const satisfies Command; diff --git a/packages/client/lib/commands/VDIM.spec.ts b/packages/client/lib/commands/VDIM.spec.ts new file mode 100644 index 00000000000..db3f5f3bd8f --- /dev/null +++ b/packages/client/lib/commands/VDIM.spec.ts @@ -0,0 +1,43 @@ +import { strict as assert } from 'node:assert'; +import testUtils, { GLOBAL } from '../test-utils'; +import VDIM from './VDIM'; +import { BasicCommandParser } from '../client/parser'; + +describe('VDIM', () => { + it('parseCommand', () => { + const parser = new BasicCommandParser(); + VDIM.parseCommand(parser, 'key'); + assert.deepEqual( + parser.redisArgs, + ['VDIM', 'key'] + ); + }); + + testUtils.testAll('vDim', async client => { + await client.vAdd('key', [1.0, 2.0, 3.0], 'element'); + + assert.equal( + await client.vDim('key'), + 3 + ); + }, { + client: { ...GLOBAL.SERVERS.OPEN, minimumDockerVersion: [8, 0] }, + cluster: { ...GLOBAL.CLUSTERS.OPEN, minimumDockerVersion: [8, 0] } + }); + + testUtils.testWithClient('vDim with RESP3', async client => { + await client.vAdd('resp3-5d', [1.0, 2.0, 3.0, 4.0, 5.0], 'elem5d'); + + assert.equal( + await client.vDim('resp3-5d'), + 5 + ); + + }, { + ...GLOBAL.SERVERS.OPEN, + clientOptions: { + RESP: 3 + }, + minimumDockerVersion: [8, 0] + }); +}); diff --git a/packages/client/lib/commands/VDIM.ts b/packages/client/lib/commands/VDIM.ts new file mode 100644 index 00000000000..f7933e77eac --- /dev/null +++ b/packages/client/lib/commands/VDIM.ts @@ -0,0 +1,18 @@ +import { CommandParser } from '../client/parser'; +import { RedisArgument, NumberReply, Command } from '../RESP/types'; + +export default { + IS_READ_ONLY: true, + /** + * Retrieve the dimension of the vectors in a vector set + * + * @param parser - The command parser + * @param key - The key of the vector set + * @see https://redis.io/commands/vdim/ + */ + parseCommand(parser: CommandParser, key: RedisArgument) { + parser.push('VDIM'); + parser.pushKey(key); + }, + transformReply: undefined as unknown as () => NumberReply +} as const satisfies Command; diff --git a/packages/client/lib/commands/VEMB.spec.ts b/packages/client/lib/commands/VEMB.spec.ts new file mode 100644 index 00000000000..ed9515ebddf --- /dev/null +++ b/packages/client/lib/commands/VEMB.spec.ts @@ -0,0 +1,42 @@ +import { strict as assert } from 'node:assert'; +import testUtils, { GLOBAL } from '../test-utils'; +import VEMB from './VEMB'; +import { BasicCommandParser } from '../client/parser'; + +describe('VEMB', () => { + it('parseCommand', () => { + const parser = new BasicCommandParser(); + VEMB.parseCommand(parser, 'key', 'element'); + assert.deepEqual( + parser.redisArgs, + ['VEMB', 'key', 'element'] + ); + }); + + testUtils.testAll('vEmb', async client => { + await client.vAdd('key', [1.0, 2.0, 3.0], 'element'); + + const result = await client.vEmb('key', 'element'); + assert.ok(Array.isArray(result)); + assert.equal(result.length, 3); + assert.equal(typeof result[0], 'number'); + }, { + client: { ...GLOBAL.SERVERS.OPEN, minimumDockerVersion: [8, 0] }, + cluster: { ...GLOBAL.CLUSTERS.OPEN, minimumDockerVersion: [8, 0] } + }); + + testUtils.testWithClient('vEmb with RESP3', async client => { + await client.vAdd('resp3-key', [1.5, 2.5, 3.5, 4.5], 'resp3-element'); + + const result = await client.vEmb('resp3-key', 'resp3-element'); + assert.ok(Array.isArray(result)); + assert.equal(result.length, 4); + assert.equal(typeof result[0], 'number'); + }, { + ...GLOBAL.SERVERS.OPEN, + clientOptions: { + RESP: 3 + }, + minimumDockerVersion: [8, 0] + }); +}); diff --git a/packages/client/lib/commands/VEMB.ts b/packages/client/lib/commands/VEMB.ts new file mode 100644 index 00000000000..d534c27d65d --- /dev/null +++ b/packages/client/lib/commands/VEMB.ts @@ -0,0 +1,21 @@ +import { CommandParser } from '../client/parser'; +import { RedisArgument, Command } from '../RESP/types'; +import { transformDoubleArrayReply } from './generic-transformers'; + +export default { + IS_READ_ONLY: true, + /** + * Retrieve the approximate vector associated with a vector set element + * + * @param parser - The command parser + * @param key - The key of the vector set + * @param element - The name of the element to retrieve the vector for + * @see https://redis.io/commands/vemb/ + */ + parseCommand(parser: CommandParser, key: RedisArgument, element: RedisArgument) { + parser.push('VEMB'); + parser.pushKey(key); + parser.push(element); + }, + transformReply: transformDoubleArrayReply +} as const satisfies Command; diff --git a/packages/client/lib/commands/VEMB_RAW.spec.ts b/packages/client/lib/commands/VEMB_RAW.spec.ts new file mode 100644 index 00000000000..33d3af8540d --- /dev/null +++ b/packages/client/lib/commands/VEMB_RAW.spec.ts @@ -0,0 +1,68 @@ +import { strict as assert } from 'node:assert'; +import testUtils, { GLOBAL } from '../test-utils'; +import VEMB_RAW from './VEMB_RAW'; +import { BasicCommandParser } from '../client/parser'; + +describe('VEMB_RAW', () => { + it('parseCommand', () => { + const parser = new BasicCommandParser(); + VEMB_RAW.parseCommand(parser, 'key', 'element'); + assert.deepEqual( + parser.redisArgs, + ['VEMB', 'key', 'element', 'RAW'] + ); + }); + + testUtils.testAll('vEmbRaw', async client => { + await client.vAdd('key1', [1.0, 2.0, 3.0], 'element'); + const result1 = await client.vEmbRaw('key1', 'element'); + assert.equal(result1.quantization, 'int8'); + assert.ok(result1.quantizationRange !== undefined); + + await client.vAdd('key2', [1.0, 2.0, 3.0], 'element', { QUANT: 'Q8' }); + const result2 = await client.vEmbRaw('key2', 'element'); + assert.equal(result2.quantization, 'int8'); + assert.ok(result2.quantizationRange !== undefined); + + await client.vAdd('key3', [1.0, 2.0, 3.0], 'element', { QUANT: 'NOQUANT' }); + const result3 = await client.vEmbRaw('key3', 'element'); + assert.equal(result3.quantization, 'f32'); + assert.equal(result3.quantizationRange, undefined); + + await client.vAdd('key4', [1.0, 2.0, 3.0], 'element', { QUANT: 'BIN' }); + const result4 = await client.vEmbRaw('key4', 'element'); + assert.equal(result4.quantization, 'bin'); + assert.equal(result4.quantizationRange, undefined); + }, { + client: { ...GLOBAL.SERVERS.OPEN, minimumDockerVersion: [8, 0] }, + cluster: { ...GLOBAL.CLUSTERS.OPEN, minimumDockerVersion: [8, 0] } + }); + + testUtils.testWithClient('vEmbRaw with RESP3', async client => { + await client.vAdd('key1', [1.0, 2.0, 3.0], 'element'); + const result1 = await client.vEmbRaw('key1', 'element'); + assert.equal(result1.quantization, 'int8'); + assert.ok(result1.quantizationRange !== undefined); + + await client.vAdd('key2', [1.0, 2.0, 3.0], 'element', { QUANT: 'Q8' }); + const result2 = await client.vEmbRaw('key2', 'element'); + assert.equal(result2.quantization, 'int8'); + assert.ok(result2.quantizationRange !== undefined); + + await client.vAdd('key3', [1.0, 2.0, 3.0], 'element', { QUANT: 'NOQUANT' }); + const result3 = await client.vEmbRaw('key3', 'element'); + assert.equal(result3.quantization, 'f32'); + assert.equal(result3.quantizationRange, undefined); + + await client.vAdd('key4', [1.0, 2.0, 3.0], 'element', { QUANT: 'BIN' }); + const result4 = await client.vEmbRaw('key4', 'element'); + assert.equal(result4.quantization, 'bin'); + assert.equal(result4.quantizationRange, undefined); + }, { + ...GLOBAL.SERVERS.OPEN, + clientOptions: { + RESP: 3 + }, + minimumDockerVersion: [8, 0] + }); +}); diff --git a/packages/client/lib/commands/VEMB_RAW.ts b/packages/client/lib/commands/VEMB_RAW.ts new file mode 100644 index 00000000000..b6881d321c9 --- /dev/null +++ b/packages/client/lib/commands/VEMB_RAW.ts @@ -0,0 +1,57 @@ +import { CommandParser } from '../client/parser'; +import { + RedisArgument, + Command, + BlobStringReply, + SimpleStringReply, + DoubleReply +} from '../RESP/types'; +import { transformDoubleReply } from './generic-transformers'; +import VEMB from './VEMB'; + +type RawVembReply = { + quantization: SimpleStringReply; + raw: BlobStringReply; + l2Norm: DoubleReply; + quantizationRange?: DoubleReply; +}; + +const transformRawVembReply = { + 2: (reply: any[]): RawVembReply => { + return { + quantization: reply[0], + raw: reply[1], + l2Norm: transformDoubleReply[2](reply[2]), + ...(reply[3] !== undefined && { quantizationRange: transformDoubleReply[2](reply[3]) }) + }; + }, + 3: (reply: any[]): RawVembReply => { + return { + quantization: reply[0], + raw: reply[1], + l2Norm: reply[2], + quantizationRange: reply[3] + }; + }, +}; + +export default { + IS_READ_ONLY: true, + /** + * Retrieve the RAW approximate vector associated with a vector set element + * + * @param parser - The command parser + * @param key - The key of the vector set + * @param element - The name of the element to retrieve the vector for + * @see https://redis.io/commands/vemb/ + */ + parseCommand( + parser: CommandParser, + key: RedisArgument, + element: RedisArgument + ) { + VEMB.parseCommand(parser, key, element); + parser.push('RAW'); + }, + transformReply: transformRawVembReply +} as const satisfies Command; diff --git a/packages/client/lib/commands/VGETATTR.spec.ts b/packages/client/lib/commands/VGETATTR.spec.ts new file mode 100644 index 00000000000..d904146c670 --- /dev/null +++ b/packages/client/lib/commands/VGETATTR.spec.ts @@ -0,0 +1,77 @@ +import { strict as assert } from 'node:assert'; +import testUtils, { GLOBAL } from '../test-utils'; +import VGETATTR from './VGETATTR'; +import { BasicCommandParser } from '../client/parser'; + +describe('VGETATTR', () => { + it('parseCommand', () => { + const parser = new BasicCommandParser(); + VGETATTR.parseCommand(parser, 'key', 'element'); + assert.deepEqual( + parser.redisArgs, + ['VGETATTR', 'key', 'element'] + ); + }); + + testUtils.testAll('vGetAttr', async client => { + await client.vAdd('key', [1.0, 2.0, 3.0], 'element'); + + const nullResult = await client.vGetAttr('key', 'element'); + assert.equal(nullResult, null); + + await client.vSetAttr('key', 'element', { name: 'test' }); + + const result = await client.vGetAttr('key', 'element'); + + assert.ok(result !== null); + assert.equal(typeof result, 'object') + + assert.deepEqual(result, { + name: 'test' + }) + + + }, { + client: { ...GLOBAL.SERVERS.OPEN, minimumDockerVersion: [8, 0] }, + cluster: { ...GLOBAL.CLUSTERS.OPEN, minimumDockerVersion: [8, 0] } + }); + + testUtils.testWithClient('vGetAttr with RESP3', async client => { + await client.vAdd('resp3-key', [1.0, 2.0], 'resp3-element'); + + // Test null case (no attributes set) + const nullResult = await client.vGetAttr('resp3-key', 'resp3-element'); + + assert.equal(nullResult, null); + + // Set complex attributes and retrieve them + const complexAttrs = { + name: 'test-item', + category: 'electronics', + price: 99.99, + inStock: true, + tags: ['new', 'featured'] + }; + await client.vSetAttr('resp3-key', 'resp3-element', complexAttrs); + + const result = await client.vGetAttr('resp3-key', 'resp3-element'); + + assert.ok(result !== null); + assert.equal(typeof result, 'object') + + assert.deepEqual(result, { + name: 'test-item', + category: 'electronics', + price: 99.99, + inStock: true, + tags: ['new', 'featured'] + }) + + }, { + ...GLOBAL.SERVERS.OPEN, + clientOptions: { + RESP: 3 + }, + minimumDockerVersion: [8, 0] + }); +}); diff --git a/packages/client/lib/commands/VGETATTR.ts b/packages/client/lib/commands/VGETATTR.ts new file mode 100644 index 00000000000..05ec8706fb1 --- /dev/null +++ b/packages/client/lib/commands/VGETATTR.ts @@ -0,0 +1,21 @@ +import { CommandParser } from '../client/parser'; +import { RedisArgument, Command } from '../RESP/types'; +import { transformRedisJsonNullReply } from './generic-transformers'; + +export default { + IS_READ_ONLY: true, + /** + * Retrieve the attributes of a vector set element + * + * @param parser - The command parser + * @param key - The key of the vector set + * @param element - The name of the element to retrieve attributes for + * @see https://redis.io/commands/vgetattr/ + */ + parseCommand(parser: CommandParser, key: RedisArgument, element: RedisArgument) { + parser.push('VGETATTR'); + parser.pushKey(key); + parser.push(element); + }, + transformReply: transformRedisJsonNullReply +} as const satisfies Command; diff --git a/packages/client/lib/commands/VINFO.spec.ts b/packages/client/lib/commands/VINFO.spec.ts new file mode 100644 index 00000000000..074598644ff --- /dev/null +++ b/packages/client/lib/commands/VINFO.spec.ts @@ -0,0 +1,58 @@ +import { strict as assert } from 'node:assert'; +import testUtils, { GLOBAL } from '../test-utils'; +import VINFO from './VINFO'; +import { BasicCommandParser } from '../client/parser'; + +describe('VINFO', () => { + it('parseCommand', () => { + const parser = new BasicCommandParser(); + VINFO.parseCommand(parser, 'key'); + assert.deepEqual( + parser.redisArgs, + ['VINFO', 'key'] + ); + }); + + testUtils.testAll('vInfo', async client => { + await client.vAdd('key', [1.0, 2.0, 3.0], 'element'); + + const result = await client.vInfo('key'); + assert.ok(typeof result === 'object' && result !== null); + + assert.equal(result['vector-dim'], 3); + assert.equal(result['size'], 1); + assert.ok('quant-type' in result); + assert.ok('hnsw-m' in result); + assert.ok('projection-input-dim' in result); + assert.ok('max-level' in result); + assert.ok('attributes-count' in result); + assert.ok('vset-uid' in result); + assert.ok('hnsw-max-node-uid' in result); + }, { + client: { ...GLOBAL.SERVERS.OPEN, minimumDockerVersion: [8, 0] }, + cluster: { ...GLOBAL.CLUSTERS.OPEN, minimumDockerVersion: [8, 0] } + }); + + testUtils.testWithClient('vInfo with RESP3', async client => { + await client.vAdd('resp3-key', [1.0, 2.0, 3.0], 'resp3-element'); + + const result = await client.vInfo('resp3-key'); + assert.ok(typeof result === 'object' && result !== null); + + assert.equal(result['vector-dim'], 3); + assert.equal(result['size'], 1); + assert.ok('quant-type' in result); + assert.ok('hnsw-m' in result); + assert.ok('projection-input-dim' in result); + assert.ok('max-level' in result); + assert.ok('attributes-count' in result); + assert.ok('vset-uid' in result); + assert.ok('hnsw-max-node-uid' in result); + }, { + ...GLOBAL.SERVERS.OPEN, + clientOptions: { + RESP: 3 + }, + minimumDockerVersion: [8, 0] + }); +}); diff --git a/packages/client/lib/commands/VINFO.ts b/packages/client/lib/commands/VINFO.ts new file mode 100644 index 00000000000..4e0d68d7cb0 --- /dev/null +++ b/packages/client/lib/commands/VINFO.ts @@ -0,0 +1,38 @@ +import { CommandParser } from '../client/parser'; +import { RedisArgument, Command, UnwrapReply, Resp2Reply, TuplesToMapReply, SimpleStringReply, NumberReply } from '../RESP/types'; + +export type VInfoReplyMap = TuplesToMapReply<[ + [SimpleStringReply<'quant-type'>, SimpleStringReply], + [SimpleStringReply<'vector-dim'>, NumberReply], + [SimpleStringReply<'size'>, NumberReply], + [SimpleStringReply<'max-level'>, NumberReply], + [SimpleStringReply<'vset-uid'>, NumberReply], + [SimpleStringReply<'hnsw-max-node-uid'>, NumberReply], +]>; + +export default { + IS_READ_ONLY: true, + /** + * Retrieve metadata and internal details about a vector set, including size, dimensions, quantization type, and graph structure + * + * @param parser - The command parser + * @param key - The key of the vector set + * @see https://redis.io/commands/vinfo/ + */ + parseCommand(parser: CommandParser, key: RedisArgument) { + parser.push('VINFO'); + parser.pushKey(key); + }, + transformReply: { + 2: (reply: UnwrapReply>): VInfoReplyMap => { + const ret = Object.create(null); + + for (let i = 0; i < reply.length; i += 2) { + ret[reply[i].toString()] = reply[i + 1]; + } + + return ret as unknown as VInfoReplyMap; + }, + 3: undefined as unknown as () => VInfoReplyMap + } +} as const satisfies Command; diff --git a/packages/client/lib/commands/VLINKS.spec.ts b/packages/client/lib/commands/VLINKS.spec.ts new file mode 100644 index 00000000000..e788f9f9a98 --- /dev/null +++ b/packages/client/lib/commands/VLINKS.spec.ts @@ -0,0 +1,42 @@ +import { strict as assert } from 'node:assert'; +import testUtils, { GLOBAL } from '../test-utils'; +import VLINKS from './VLINKS'; +import { BasicCommandParser } from '../client/parser'; + +describe('VLINKS', () => { + it('parseCommand', () => { + const parser = new BasicCommandParser(); + VLINKS.parseCommand(parser, 'key', 'element'); + assert.deepEqual( + parser.redisArgs, + ['VLINKS', 'key', 'element'] + ); + }); + + testUtils.testAll('vLinks', async client => { + await client.vAdd('key', [1.0, 2.0, 3.0], 'element1'); + await client.vAdd('key', [1.1, 2.1, 3.1], 'element2'); + + const result = await client.vLinks('key', 'element1'); + assert.ok(Array.isArray(result)); + assert.ok(result.length) + }, { + client: { ...GLOBAL.SERVERS.OPEN, minimumDockerVersion: [8, 0] }, + cluster: { ...GLOBAL.CLUSTERS.OPEN, minimumDockerVersion: [8, 0] } + }); + + testUtils.testWithClient('vLinks with RESP3', async client => { + await client.vAdd('resp3-key', [1.0, 2.0, 3.0], 'element1'); + await client.vAdd('resp3-key', [1.1, 2.1, 3.1], 'element2'); + + const result = await client.vLinks('resp3-key', 'element1'); + assert.ok(Array.isArray(result)); + assert.ok(result.length) + }, { + ...GLOBAL.SERVERS.OPEN, + clientOptions: { + RESP: 3 + }, + minimumDockerVersion: [8, 0] + }); +}); diff --git a/packages/client/lib/commands/VLINKS.ts b/packages/client/lib/commands/VLINKS.ts new file mode 100644 index 00000000000..9e97fc7de9b --- /dev/null +++ b/packages/client/lib/commands/VLINKS.ts @@ -0,0 +1,20 @@ +import { CommandParser } from '../client/parser'; +import { RedisArgument, ArrayReply, BlobStringReply, Command } from '../RESP/types'; + +export default { + IS_READ_ONLY: true, + /** + * Retrieve the neighbors of a specified element in a vector set; the connections for each layer of the HNSW graph + * + * @param parser - The command parser + * @param key - The key of the vector set + * @param element - The name of the element to retrieve neighbors for + * @see https://redis.io/commands/vlinks/ + */ + parseCommand(parser: CommandParser, key: RedisArgument, element: RedisArgument) { + parser.push('VLINKS'); + parser.pushKey(key); + parser.push(element); + }, + transformReply: undefined as unknown as () => ArrayReply> +} as const satisfies Command; diff --git a/packages/client/lib/commands/VLINKS_WITHSCORES.spec.ts b/packages/client/lib/commands/VLINKS_WITHSCORES.spec.ts new file mode 100644 index 00000000000..db96bd1a8af --- /dev/null +++ b/packages/client/lib/commands/VLINKS_WITHSCORES.spec.ts @@ -0,0 +1,75 @@ +import { strict as assert } from 'node:assert'; +import testUtils, { GLOBAL } from '../test-utils'; +import VLINKS_WITHSCORES from './VLINKS_WITHSCORES'; +import { BasicCommandParser } from '../client/parser'; + +describe('VLINKS WITHSCORES', () => { + it('parseCommand', () => { + const parser = new BasicCommandParser(); + VLINKS_WITHSCORES.parseCommand(parser, 'key', 'element'); + assert.deepEqual(parser.redisArgs, [ + 'VLINKS', + 'key', + 'element', + 'WITHSCORES' + ]); + }); + + testUtils.testAll( + 'vLinksWithScores', + async client => { + // Create a vector set with multiple elements to build HNSW graph layers + await client.vAdd('key', [1.0, 2.0, 3.0], 'element1'); + await client.vAdd('key', [1.1, 2.1, 3.1], 'element2'); + await client.vAdd('key', [1.2, 2.2, 3.2], 'element3'); + await client.vAdd('key', [2.0, 3.0, 4.0], 'element4'); + + const result = await client.vLinksWithScores('key', 'element1'); + + assert.ok(Array.isArray(result)); + + for (const layer of result) { + assert.equal( + typeof layer, + 'object' + ); + } + + assert.ok(result.length >= 1, 'Should have at least layer 0'); + }, + { + client: { ...GLOBAL.SERVERS.OPEN, minimumDockerVersion: [8, 0] }, + cluster: { ...GLOBAL.CLUSTERS.OPEN, minimumDockerVersion: [8, 0] } + } + ); + + testUtils.testWithClient( + 'vLinksWithScores with RESP3', + async client => { + await client.vAdd('resp3-key', [1.0, 2.0, 3.0], 'element1'); + await client.vAdd('resp3-key', [1.1, 2.1, 3.1], 'element2'); + await client.vAdd('resp3-key', [1.2, 2.2, 3.2], 'element3'); + await client.vAdd('resp3-key', [2.0, 3.0, 4.0], 'element4'); + + const result = await client.vLinksWithScores('resp3-key', 'element1'); + + assert.ok(Array.isArray(result)); + + for (const layer of result) { + assert.equal( + typeof layer, + 'object' + ); + } + + assert.ok(result.length >= 1, 'Should have at least layer 0'); + }, + { + ...GLOBAL.SERVERS.OPEN, + clientOptions: { + RESP: 3 + }, + minimumDockerVersion: [8, 0] + } + ); +}); diff --git a/packages/client/lib/commands/VLINKS_WITHSCORES.ts b/packages/client/lib/commands/VLINKS_WITHSCORES.ts new file mode 100644 index 00000000000..10ebe160fcd --- /dev/null +++ b/packages/client/lib/commands/VLINKS_WITHSCORES.ts @@ -0,0 +1,42 @@ +import { BlobStringReply, Command, DoubleReply, MapReply } from '../RESP/types'; +import { transformDoubleReply } from './generic-transformers'; +import VLINKS from './VLINKS'; + + +function transformVLinksWithScoresReply(reply: any): Array> { + const layers: Array> = []; + + for (const layer of reply) { + const obj: Record = Object.create(null); + + // Each layer contains alternating element names and scores + for (let i = 0; i < layer.length; i += 2) { + const element = layer[i]; + const score = transformDoubleReply[2](layer[i + 1]); + obj[element.toString()] = score; + } + + layers.push(obj); + } + + return layers; +} + +export default { + IS_READ_ONLY: VLINKS.IS_READ_ONLY, + /** + * Get the connections for each layer of the HNSW graph with similarity scores + * @param args - Same parameters as the VLINKS command + * @see https://redis.io/commands/vlinks/ + */ + parseCommand(...args: Parameters) { + const parser = args[0]; + + VLINKS.parseCommand(...args); + parser.push('WITHSCORES'); + }, + transformReply: { + 2: transformVLinksWithScoresReply, + 3: undefined as unknown as () => Array> + } +} as const satisfies Command; diff --git a/packages/client/lib/commands/VRANDMEMBER.spec.ts b/packages/client/lib/commands/VRANDMEMBER.spec.ts new file mode 100644 index 00000000000..28c020e3563 --- /dev/null +++ b/packages/client/lib/commands/VRANDMEMBER.spec.ts @@ -0,0 +1,201 @@ +import { strict as assert } from 'node:assert'; +import testUtils, { GLOBAL } from '../test-utils'; +import VRANDMEMBER from './VRANDMEMBER'; +import { BasicCommandParser } from '../client/parser'; + +describe('VRANDMEMBER', () => { + describe('parseCommand', () => { + it('without count', () => { + const parser = new BasicCommandParser(); + VRANDMEMBER.parseCommand(parser, 'key'); + assert.deepEqual( + parser.redisArgs, + ['VRANDMEMBER', 'key'] + ); + }); + + it('with count', () => { + const parser = new BasicCommandParser(); + VRANDMEMBER.parseCommand(parser, 'key', 2); + assert.deepEqual( + parser.redisArgs, + ['VRANDMEMBER', 'key', '2'] + ); + }); + }); + + describe('RESP2 tests', () => { + testUtils.testAll('vRandMember without count - returns single element as string', async client => { + await client.vAdd('key', [1.0, 2.0, 3.0], 'element1'); + await client.vAdd('key', [4.0, 5.0, 6.0], 'element2'); + await client.vAdd('key', [7.0, 8.0, 9.0], 'element3'); + + const result = await client.vRandMember('key'); + assert.equal(typeof result, 'string'); + assert.ok(['element1', 'element2', 'element3'].includes(result as string)); + }, { + client: { ...GLOBAL.SERVERS.OPEN, minimumDockerVersion: [8, 0] }, + cluster: { ...GLOBAL.CLUSTERS.OPEN, minimumDockerVersion: [8, 0] } + }); + + testUtils.testAll('vRandMember with positive count - returns distinct elements', async client => { + await client.vAdd('key', [1.0, 2.0, 3.0], 'element1'); + await client.vAdd('key', [4.0, 5.0, 6.0], 'element2'); + await client.vAdd('key', [7.0, 8.0, 9.0], 'element3'); + + const result = await client.vRandMember('key', 2); + assert.ok(Array.isArray(result)); + assert.equal(result.length, 2); + + }, { + client: { ...GLOBAL.SERVERS.OPEN, minimumDockerVersion: [8, 0] }, + cluster: { ...GLOBAL.CLUSTERS.OPEN, minimumDockerVersion: [8, 0] } + }); + + testUtils.testAll('vRandMember with negative count - allows duplicates', async client => { + await client.vAdd('key', [1.0, 2.0, 3.0], 'element1'); + await client.vAdd('key', [4.0, 5.0, 6.0], 'element2'); + + const result = await client.vRandMember('key', -5); + assert.ok(Array.isArray(result)); + assert.equal(result.length, 5); + + // All elements should be from our set (duplicates allowed) + result.forEach(element => { + assert.ok(['element1', 'element2'].includes(element)); + }); + }, { + client: { ...GLOBAL.SERVERS.OPEN, minimumDockerVersion: [8, 0] }, + cluster: { ...GLOBAL.CLUSTERS.OPEN, minimumDockerVersion: [8, 0] } + }); + + testUtils.testAll('vRandMember count exceeds set size - returns entire set', async client => { + await client.vAdd('key', [1.0, 2.0, 3.0], 'element1'); + await client.vAdd('key', [4.0, 5.0, 6.0], 'element2'); + + const result = await client.vRandMember('key', 10); + assert.ok(Array.isArray(result)); + assert.equal(result.length, 2); // Only 2 elements exist + + // Should contain both elements + assert.ok(result.includes('element1')); + assert.ok(result.includes('element2')); + }, { + client: { ...GLOBAL.SERVERS.OPEN, minimumDockerVersion: [8, 0] }, + cluster: { ...GLOBAL.CLUSTERS.OPEN, minimumDockerVersion: [8, 0] } + }); + + testUtils.testAll('vRandMember on non-existent key', async client => { + // Without count - should return null + const resultNoCount = await client.vRandMember('nonexistent'); + assert.equal(resultNoCount, null); + + // With count - should return empty array + const resultWithCount = await client.vRandMember('nonexistent', 5); + assert.ok(Array.isArray(resultWithCount)); + assert.equal(resultWithCount.length, 0); + }, { + client: { ...GLOBAL.SERVERS.OPEN, minimumDockerVersion: [8, 0] }, + cluster: { ...GLOBAL.CLUSTERS.OPEN, minimumDockerVersion: [8, 0] } + }); + }); + + describe('RESP3 tests', () => { + testUtils.testWithClient('vRandMember without count - returns single element as string', async client => { + await client.vAdd('resp3-key', [1.0, 2.0, 3.0], 'element1'); + await client.vAdd('resp3-key', [4.0, 5.0, 6.0], 'element2'); + await client.vAdd('resp3-key', [7.0, 8.0, 9.0], 'element3'); + + const result = await client.vRandMember('resp3-key'); + assert.equal(typeof result, 'string'); + assert.ok(['element1', 'element2', 'element3'].includes(result as string)); + }, { + ...GLOBAL.SERVERS.OPEN, + clientOptions: { + RESP: 3 + }, + minimumDockerVersion: [8, 0] + }); + + testUtils.testWithClient('vRandMember with positive count - returns distinct elements', async client => { + await client.vAdd('resp3-key', [1.0, 2.0, 3.0], 'element1'); + await client.vAdd('resp3-key', [4.0, 5.0, 6.0], 'element2'); + await client.vAdd('resp3-key', [7.0, 8.0, 9.0], 'element3'); + + const result = await client.vRandMember('resp3-key', 2); + assert.ok(Array.isArray(result)); + assert.equal(result.length, 2); + + // Should be distinct elements (no duplicates) + const uniqueElements = new Set(result); + assert.equal(uniqueElements.size, 2); + + // All elements should be from our set + result.forEach(element => { + assert.ok(['element1', 'element2', 'element3'].includes(element)); + }); + }, { + ...GLOBAL.SERVERS.OPEN, + clientOptions: { + RESP: 3 + }, + minimumDockerVersion: [8, 0] + }); + + testUtils.testWithClient('vRandMember with negative count - allows duplicates', async client => { + await client.vAdd('resp3-key', [1.0, 2.0, 3.0], 'element1'); + await client.vAdd('resp3-key', [4.0, 5.0, 6.0], 'element2'); + + const result = await client.vRandMember('resp3-key', -5); + assert.ok(Array.isArray(result)); + assert.equal(result.length, 5); + + // All elements should be from our set (duplicates allowed) + result.forEach(element => { + assert.ok(['element1', 'element2'].includes(element)); + }); + }, { + ...GLOBAL.SERVERS.OPEN, + clientOptions: { + RESP: 3 + }, + minimumDockerVersion: [8, 0] + }); + + testUtils.testWithClient('vRandMember count exceeds set size - returns entire set', async client => { + await client.vAdd('resp3-key', [1.0, 2.0, 3.0], 'element1'); + await client.vAdd('resp3-key', [4.0, 5.0, 6.0], 'element2'); + + const result = await client.vRandMember('resp3-key', 10); + assert.ok(Array.isArray(result)); + assert.equal(result.length, 2); // Only 2 elements exist + + // Should contain both elements + assert.ok(result.includes('element1')); + assert.ok(result.includes('element2')); + }, { + ...GLOBAL.SERVERS.OPEN, + clientOptions: { + RESP: 3 + }, + minimumDockerVersion: [8, 0] + }); + + testUtils.testWithClient('vRandMember on non-existent key', async client => { + // Without count - should return null + const resultNoCount = await client.vRandMember('resp3-nonexistent'); + assert.equal(resultNoCount, null); + + // With count - should return empty array + const resultWithCount = await client.vRandMember('resp3-nonexistent', 5); + assert.ok(Array.isArray(resultWithCount)); + assert.equal(resultWithCount.length, 0); + }, { + ...GLOBAL.SERVERS.OPEN, + clientOptions: { + RESP: 3 + }, + minimumDockerVersion: [8, 0] + }); + }); +}); diff --git a/packages/client/lib/commands/VRANDMEMBER.ts b/packages/client/lib/commands/VRANDMEMBER.ts new file mode 100644 index 00000000000..299af33b9fa --- /dev/null +++ b/packages/client/lib/commands/VRANDMEMBER.ts @@ -0,0 +1,23 @@ +import { CommandParser } from '../client/parser'; +import { RedisArgument, BlobStringReply, ArrayReply, Command, NullReply } from '../RESP/types'; + +export default { + IS_READ_ONLY: true, + /** + * Retrieve random elements of a vector set + * + * @param parser - The command parser + * @param key - The key of the vector set + * @param count - Optional number of elements to return + * @see https://redis.io/commands/vrandmember/ + */ + parseCommand(parser: CommandParser, key: RedisArgument, count?: number) { + parser.push('VRANDMEMBER'); + parser.pushKey(key); + + if (count !== undefined) { + parser.push(count.toString()); + } + }, + transformReply: undefined as unknown as () => BlobStringReply | ArrayReply | NullReply +} as const satisfies Command; diff --git a/packages/client/lib/commands/VREM.spec.ts b/packages/client/lib/commands/VREM.spec.ts new file mode 100644 index 00000000000..9e558c991c1 --- /dev/null +++ b/packages/client/lib/commands/VREM.spec.ts @@ -0,0 +1,63 @@ +import { strict as assert } from 'node:assert'; +import testUtils, { GLOBAL } from '../test-utils'; +import VREM from './VREM'; +import { BasicCommandParser } from '../client/parser'; + +describe('VREM', () => { + const parser = new BasicCommandParser(); + VREM.parseCommand(parser, 'key', 'element'); + it('parseCommand', () => { + assert.deepEqual( + parser.redisArgs, + ['VREM', 'key', 'element'] + ); + }); + + testUtils.testAll('vRem', async client => { + await client.vAdd('key', [1.0, 2.0, 3.0], 'element'); + + assert.equal( + await client.vRem('key', 'element'), + true + ); + + assert.equal( + await client.vRem('key', 'element'), + false + ); + + assert.equal( + await client.vCard('key'), + 0 + ); + }, { + client: { ...GLOBAL.SERVERS.OPEN, minimumDockerVersion: [8, 0] }, + cluster: { ...GLOBAL.CLUSTERS.OPEN, minimumDockerVersion: [8, 0] } + }); + + testUtils.testWithClient('vRem with RESP3', async client => { + await client.vAdd('resp3-key', [1.0, 2.0, 3.0], 'resp3-element'); + + assert.equal( + await client.vRem('resp3-key', 'resp3-element'), + true + ); + + assert.equal( + await client.vRem('resp3-key', 'resp3-element'), + false + ); + + + assert.equal( + await client.vCard('resp3-key'), + 0 + ); + }, { + ...GLOBAL.SERVERS.OPEN, + clientOptions: { + RESP: 3 + }, + minimumDockerVersion: [8, 0] + }); +}); diff --git a/packages/client/lib/commands/VREM.ts b/packages/client/lib/commands/VREM.ts new file mode 100644 index 00000000000..7eb22b2e2ec --- /dev/null +++ b/packages/client/lib/commands/VREM.ts @@ -0,0 +1,20 @@ +import { CommandParser } from '../client/parser'; +import { RedisArgument, Command } from '../RESP/types'; +import { transformBooleanReply } from './generic-transformers'; + +export default { + /** + * Remove an element from a vector set + * + * @param parser - The command parser + * @param key - The key of the vector set + * @param element - The name of the element to remove from the vector set + * @see https://redis.io/commands/vrem/ + */ + parseCommand(parser: CommandParser, key: RedisArgument, element: RedisArgument) { + parser.push('VREM'); + parser.pushKey(key); + parser.push(element); + }, + transformReply: transformBooleanReply +} as const satisfies Command; diff --git a/packages/client/lib/commands/VSETATTR.spec.ts b/packages/client/lib/commands/VSETATTR.spec.ts new file mode 100644 index 00000000000..303006d4081 --- /dev/null +++ b/packages/client/lib/commands/VSETATTR.spec.ts @@ -0,0 +1,58 @@ +import { strict as assert } from 'node:assert'; +import testUtils, { GLOBAL } from '../test-utils'; +import VSETATTR from './VSETATTR'; +import { BasicCommandParser } from '../client/parser'; + +describe('VSETATTR', () => { + describe('parseCommand', () => { + it('with object', () => { + const parser = new BasicCommandParser(); + VSETATTR.parseCommand(parser, 'key', 'element', { name: 'test', value: 42 }), + assert.deepEqual( + parser.redisArgs, + ['VSETATTR', 'key', 'element', '{"name":"test","value":42}'] + ); + }); + + it('with string', () => { + const parser = new BasicCommandParser(); + VSETATTR.parseCommand(parser, 'key', 'element', '{"name":"test"}'), + assert.deepEqual( + parser.redisArgs, + ['VSETATTR', 'key', 'element', '{"name":"test"}'] + ); + }); + }); + + testUtils.testAll('vSetAttr', async client => { + await client.vAdd('key', [1.0, 2.0, 3.0], 'element'); + + assert.equal( + await client.vSetAttr('key', 'element', { name: 'test', value: 42 }), + true + ); + }, { + client: { ...GLOBAL.SERVERS.OPEN, minimumDockerVersion: [8, 0] }, + cluster: { ...GLOBAL.CLUSTERS.OPEN, minimumDockerVersion: [8, 0] } + }); + + testUtils.testWithClient('vSetAttr with RESP3 - returns boolean', async client => { + await client.vAdd('resp3-key', [1.0, 2.0, 3.0], 'resp3-element'); + + const result = await client.vSetAttr('resp3-key', 'resp3-element', { + name: 'test-item', + category: 'electronics', + price: 99.99 + }); + + // RESP3 returns boolean instead of number + assert.equal(typeof result, 'boolean'); + assert.equal(result, true); + }, { + ...GLOBAL.SERVERS.OPEN, + clientOptions: { + RESP: 3 + }, + minimumDockerVersion: [8, 0] + }); +}); diff --git a/packages/client/lib/commands/VSETATTR.ts b/packages/client/lib/commands/VSETATTR.ts new file mode 100644 index 00000000000..084b8f8008e --- /dev/null +++ b/packages/client/lib/commands/VSETATTR.ts @@ -0,0 +1,32 @@ +import { CommandParser } from '../client/parser'; +import { RedisArgument, Command } from '../RESP/types'; +import { transformBooleanReply } from './generic-transformers'; + +export default { + /** + * Set or replace attributes on a vector set element + * + * @param parser - The command parser + * @param key - The key of the vector set + * @param element - The name of the element to set attributes for + * @param attributes - The attributes to set (as JSON string or object) + * @see https://redis.io/commands/vsetattr/ + */ + parseCommand( + parser: CommandParser, + key: RedisArgument, + element: RedisArgument, + attributes: RedisArgument | Record + ) { + parser.push('VSETATTR'); + parser.pushKey(key); + parser.push(element); + + if (typeof attributes === 'object' && attributes !== null) { + parser.push(JSON.stringify(attributes)); + } else { + parser.push(attributes); + } + }, + transformReply: transformBooleanReply +} as const satisfies Command; diff --git a/packages/client/lib/commands/VSIM.spec.ts b/packages/client/lib/commands/VSIM.spec.ts new file mode 100644 index 00000000000..b7e10eb6c49 --- /dev/null +++ b/packages/client/lib/commands/VSIM.spec.ts @@ -0,0 +1,85 @@ +import { strict as assert } from 'node:assert'; +import testUtils, { GLOBAL } from '../test-utils'; +import VSIM from './VSIM'; +import { BasicCommandParser } from '../client/parser'; + +describe('VSIM', () => { + describe('parseCommand', () => { + it('with vector', () => { + const parser = new BasicCommandParser(); + VSIM.parseCommand(parser, 'key', [1.0, 2.0, 3.0]), + assert.deepEqual( + parser.redisArgs, + ['VSIM', 'key', 'VALUES', '3', '1', '2', '3'] + ); + }); + + it('with element', () => { + const parser = new BasicCommandParser(); + VSIM.parseCommand(parser, 'key', 'element'); + assert.deepEqual( + parser.redisArgs, + ['VSIM', 'key', 'ELE', 'element'] + ); + }); + + it('with options', () => { + const parser = new BasicCommandParser(); + VSIM.parseCommand(parser, 'key', 'element', { + COUNT: 5, + EF: 100, + FILTER: '.price > 20', + 'FILTER-EF': 50, + TRUTH: true, + NOTHREAD: true + }); + assert.deepEqual( + parser.redisArgs, + [ + 'VSIM', 'key', 'ELE', 'element', + 'COUNT', '5', 'EF', '100', 'FILTER', '.price > 20', + 'FILTER-EF', '50', 'TRUTH', 'NOTHREAD' + ] + ); + }); + }); + + testUtils.testAll('vSim', async client => { + await client.vAdd('key', [1.0, 2.0, 3.0], 'element1'); + await client.vAdd('key', [1.1, 2.1, 3.1], 'element2'); + + const result = await client.vSim('key', 'element1'); + assert.ok(Array.isArray(result)); + assert.ok(result.includes('element1')); + }, { + client: { ...GLOBAL.SERVERS.OPEN, minimumDockerVersion: [8, 0] }, + cluster: { ...GLOBAL.CLUSTERS.OPEN, minimumDockerVersion: [8, 0] } + }); + + testUtils.testWithClient('vSim with RESP3', async client => { + await client.vAdd('resp3-key', [1.0, 2.0, 3.0], 'element1'); + await client.vAdd('resp3-key', [1.1, 2.1, 3.1], 'element2'); + await client.vAdd('resp3-key', [2.0, 3.0, 4.0], 'element3'); + + // Test similarity search with vector + const resultWithVector = await client.vSim('resp3-key', [1.05, 2.05, 3.05]); + assert.ok(Array.isArray(resultWithVector)); + assert.ok(resultWithVector.length > 0); + + // Test similarity search with element + const resultWithElement = await client.vSim('resp3-key', 'element1'); + assert.ok(Array.isArray(resultWithElement)); + assert.ok(resultWithElement.includes('element1')); + + // Test with options + const resultWithOptions = await client.vSim('resp3-key', 'element1', { COUNT: 2 }); + assert.ok(Array.isArray(resultWithOptions)); + assert.ok(resultWithOptions.length <= 2); + }, { + ...GLOBAL.SERVERS.OPEN, + clientOptions: { + RESP: 3 + }, + minimumDockerVersion: [8, 0] + }); +}); diff --git a/packages/client/lib/commands/VSIM.ts b/packages/client/lib/commands/VSIM.ts new file mode 100644 index 00000000000..dc41a54caf1 --- /dev/null +++ b/packages/client/lib/commands/VSIM.ts @@ -0,0 +1,68 @@ +import { CommandParser } from '../client/parser'; +import { RedisArgument, ArrayReply, BlobStringReply, Command } from '../RESP/types'; +import { transformDoubleArgument } from './generic-transformers'; + +export interface VSimOptions { + COUNT?: number; + EF?: number; + FILTER?: string; + 'FILTER-EF'?: number; + TRUTH?: boolean; + NOTHREAD?: boolean; +} + +export default { + IS_READ_ONLY: true, + /** + * Retrieve elements similar to a given vector or element with optional filtering + * + * @param parser - The command parser + * @param key - The key of the vector set + * @param query - The query vector (array of numbers) or element name (string) + * @param options - Optional parameters for similarity search + * @see https://redis.io/commands/vsim/ + */ + parseCommand( + parser: CommandParser, + key: RedisArgument, + query: RedisArgument | Array, + options?: VSimOptions + ) { + parser.push('VSIM'); + parser.pushKey(key); + + if (Array.isArray(query)) { + parser.push('VALUES', query.length.toString()); + for (const value of query) { + parser.push(transformDoubleArgument(value)); + } + } else { + parser.push('ELE', query); + } + + if (options?.COUNT !== undefined) { + parser.push('COUNT', options.COUNT.toString()); + } + + if (options?.EF !== undefined) { + parser.push('EF', options.EF.toString()); + } + + if (options?.FILTER) { + parser.push('FILTER', options.FILTER); + } + + if (options?.['FILTER-EF'] !== undefined) { + parser.push('FILTER-EF', options['FILTER-EF'].toString()); + } + + if (options?.TRUTH) { + parser.push('TRUTH'); + } + + if (options?.NOTHREAD) { + parser.push('NOTHREAD'); + } + }, + transformReply: undefined as unknown as () => ArrayReply +} as const satisfies Command; diff --git a/packages/client/lib/commands/VSIM_WITHSCORES.spec.ts b/packages/client/lib/commands/VSIM_WITHSCORES.spec.ts new file mode 100644 index 00000000000..ff9bc41376f --- /dev/null +++ b/packages/client/lib/commands/VSIM_WITHSCORES.spec.ts @@ -0,0 +1,62 @@ +import { strict as assert } from 'node:assert'; +import testUtils, { GLOBAL } from '../test-utils'; +import VSIM_WITHSCORES from './VSIM_WITHSCORES'; +import { BasicCommandParser } from '../client/parser'; + +describe('VSIM WITHSCORES', () => { + it('parseCommand', () => { + const parser = new BasicCommandParser(); + VSIM_WITHSCORES.parseCommand(parser, 'key', 'element') + assert.deepEqual(parser.redisArgs, [ + 'VSIM', + 'key', + 'ELE', + 'element', + 'WITHSCORES' + ]); + }); + + testUtils.testAll( + 'vSimWithScores', + async client => { + await client.vAdd('key', [1.0, 2.0, 3.0], 'element1'); + await client.vAdd('key', [1.1, 2.1, 3.1], 'element2'); + + const result = await client.vSimWithScores('key', 'element1'); + + assert.ok(typeof result === 'object'); + assert.ok('element1' in result); + assert.ok('element2' in result); + assert.equal(typeof result['element1'], 'number'); + assert.equal(typeof result['element2'], 'number'); + }, + { + client: { ...GLOBAL.SERVERS.OPEN, minimumDockerVersion: [8, 0] }, + cluster: { ...GLOBAL.CLUSTERS.OPEN, minimumDockerVersion: [8, 0] } + } + ); + + testUtils.testWithClient( + 'vSimWithScores with RESP3 - returns Map with scores', + async client => { + await client.vAdd('resp3-key', [1.0, 2.0, 3.0], 'element1'); + await client.vAdd('resp3-key', [1.1, 2.1, 3.1], 'element2'); + await client.vAdd('resp3-key', [2.0, 3.0, 4.0], 'element3'); + + const result = await client.vSimWithScores('resp3-key', 'element1'); + + assert.ok(typeof result === 'object'); + assert.ok('element1' in result); + assert.ok('element2' in result); + assert.equal(typeof result['element1'], 'number'); + assert.equal(typeof result['element2'], 'number'); + }, + { + ...GLOBAL.SERVERS.OPEN, + clientOptions: { + RESP: 3 + }, + minimumDockerVersion: [8, 0] + } + ); +}); diff --git a/packages/client/lib/commands/VSIM_WITHSCORES.ts b/packages/client/lib/commands/VSIM_WITHSCORES.ts new file mode 100644 index 00000000000..fda05be6642 --- /dev/null +++ b/packages/client/lib/commands/VSIM_WITHSCORES.ts @@ -0,0 +1,36 @@ +import { + ArrayReply, + BlobStringReply, + Command, + DoubleReply, + MapReply, + UnwrapReply +} from '../RESP/types'; +import { transformDoubleReply } from './generic-transformers'; +import VSIM from './VSIM'; + +export default { + IS_READ_ONLY: VSIM.IS_READ_ONLY, + /** + * Retrieve elements similar to a given vector or element with similarity scores + * @param args - Same parameters as the VSIM command + * @see https://redis.io/commands/vsim/ + */ + parseCommand(...args: Parameters) { + const parser = args[0]; + + VSIM.parseCommand(...args); + parser.push('WITHSCORES'); + }, + transformReply: { + 2: (reply: ArrayReply) => { + const inferred = reply as unknown as UnwrapReply; + const members: Record = {}; + for (let i = 0; i < inferred.length; i += 2) { + members[inferred[i].toString()] = transformDoubleReply[2](inferred[i + 1]); + } + return members; + }, + 3: undefined as unknown as () => MapReply + } +} as const satisfies Command; diff --git a/packages/client/lib/commands/generic-transformers.ts b/packages/client/lib/commands/generic-transformers.ts index 91eab7107a1..022339e4bb7 100644 --- a/packages/client/lib/commands/generic-transformers.ts +++ b/packages/client/lib/commands/generic-transformers.ts @@ -662,3 +662,21 @@ export function transformStreamsMessagesReplyResp3(reply: UnwrapReply } } + +export type RedisJSON = null | boolean | number | string | Date | Array | { + [key: string]: RedisJSON; + [key: number]: RedisJSON; +}; + +export function transformRedisJsonArgument(json: RedisJSON): string { + return JSON.stringify(json); +} + +export function transformRedisJsonReply(json: BlobStringReply): RedisJSON { + const res = JSON.parse((json as unknown as UnwrapReply).toString()); + return res; +} + +export function transformRedisJsonNullReply(json: NullReply | BlobStringReply): NullReply | RedisJSON { + return isNullReply(json) ? json : transformRedisJsonReply(json); +} diff --git a/packages/client/lib/commands/index.ts b/packages/client/lib/commands/index.ts index 5cd81331a4e..87ab8d10b8f 100644 --- a/packages/client/lib/commands/index.ts +++ b/packages/client/lib/commands/index.ts @@ -344,6 +344,20 @@ import ZSCORE from './ZSCORE'; import ZUNION_WITHSCORES from './ZUNION_WITHSCORES'; import ZUNION from './ZUNION'; import ZUNIONSTORE from './ZUNIONSTORE'; +import VADD from './VADD'; +import VCARD from './VCARD'; +import VDIM from './VDIM'; +import VEMB from './VEMB'; +import VEMB_RAW from './VEMB_RAW'; +import VGETATTR from './VGETATTR'; +import VINFO from './VINFO'; +import VLINKS from './VLINKS'; +import VLINKS_WITHSCORES from './VLINKS_WITHSCORES'; +import VRANDMEMBER from './VRANDMEMBER'; +import VREM from './VREM'; +import VSETATTR from './VSETATTR'; +import VSIM from './VSIM'; +import VSIM_WITHSCORES from './VSIM_WITHSCORES'; export default { ACL_CAT, @@ -1037,5 +1051,33 @@ export default { ZUNION, zUnion: ZUNION, ZUNIONSTORE, - zUnionStore: ZUNIONSTORE + zUnionStore: ZUNIONSTORE, + VADD, + vAdd: VADD, + VCARD, + vCard: VCARD, + VDIM, + vDim: VDIM, + VEMB, + vEmb: VEMB, + VEMB_RAW, + vEmbRaw: VEMB_RAW, + VGETATTR, + vGetAttr: VGETATTR, + VINFO, + vInfo: VINFO, + VLINKS, + vLinks: VLINKS, + VLINKS_WITHSCORES, + vLinksWithScores: VLINKS_WITHSCORES, + VRANDMEMBER, + vRandMember: VRANDMEMBER, + VREM, + vRem: VREM, + VSETATTR, + vSetAttr: VSETATTR, + VSIM, + vSim: VSIM, + VSIM_WITHSCORES, + vSimWithScores: VSIM_WITHSCORES } as const satisfies RedisCommands; diff --git a/packages/json/lib/commands/ARRAPPEND.ts b/packages/json/lib/commands/ARRAPPEND.ts index d1082baf48e..b98d1532b4e 100644 --- a/packages/json/lib/commands/ARRAPPEND.ts +++ b/packages/json/lib/commands/ARRAPPEND.ts @@ -1,5 +1,5 @@ import { CommandParser } from '@redis/client/dist/lib/client/parser'; -import { RedisJSON, transformRedisJsonArgument } from './helpers'; +import { RedisJSON, transformRedisJsonArgument } from '@redis/client/dist/lib/commands/generic-transformers'; import { RedisArgument, NumberReply, ArrayReply, NullReply, Command } from '@redis/client/dist/lib/RESP/types'; export default { diff --git a/packages/json/lib/commands/ARRINDEX.ts b/packages/json/lib/commands/ARRINDEX.ts index 69485f55a6c..1437fab4d56 100644 --- a/packages/json/lib/commands/ARRINDEX.ts +++ b/packages/json/lib/commands/ARRINDEX.ts @@ -1,6 +1,6 @@ import { CommandParser } from '@redis/client/dist/lib/client/parser'; import { RedisArgument, NumberReply, ArrayReply, NullReply, Command } from '@redis/client/dist/lib/RESP/types'; -import { RedisJSON, transformRedisJsonArgument } from './helpers'; +import { RedisJSON, transformRedisJsonArgument } from '@redis/client/dist/lib/commands/generic-transformers'; export interface JsonArrIndexOptions { range?: { diff --git a/packages/json/lib/commands/ARRINSERT.ts b/packages/json/lib/commands/ARRINSERT.ts index 33fe30a99e8..7a5ab945892 100644 --- a/packages/json/lib/commands/ARRINSERT.ts +++ b/packages/json/lib/commands/ARRINSERT.ts @@ -1,6 +1,6 @@ import { CommandParser } from '@redis/client/dist/lib/client/parser'; import { RedisArgument, NumberReply, ArrayReply, NullReply, Command } from '@redis/client/dist/lib/RESP/types'; -import { RedisJSON, transformRedisJsonArgument } from './helpers'; +import { RedisJSON, transformRedisJsonArgument } from '@redis/client/dist/lib/commands/generic-transformers'; export default { IS_READ_ONLY: false, diff --git a/packages/json/lib/commands/ARRPOP.ts b/packages/json/lib/commands/ARRPOP.ts index 53d9ed2dc87..88e4da96980 100644 --- a/packages/json/lib/commands/ARRPOP.ts +++ b/packages/json/lib/commands/ARRPOP.ts @@ -1,7 +1,6 @@ import { CommandParser } from '@redis/client/dist/lib/client/parser'; import { RedisArgument, ArrayReply, NullReply, BlobStringReply, Command, UnwrapReply } from '@redis/client/dist/lib/RESP/types'; -import { isArrayReply } from '@redis/client/dist/lib/commands/generic-transformers'; -import { transformRedisJsonNullReply } from './helpers'; +import { isArrayReply, transformRedisJsonNullReply } from '@redis/client/dist/lib/commands/generic-transformers'; export interface RedisArrPopOptions { path: RedisArgument; diff --git a/packages/json/lib/commands/GET.ts b/packages/json/lib/commands/GET.ts index e514fefae39..14ec46a53af 100644 --- a/packages/json/lib/commands/GET.ts +++ b/packages/json/lib/commands/GET.ts @@ -1,7 +1,6 @@ import { CommandParser } from '@redis/client/dist/lib/client/parser'; import { RedisArgument, Command } from '@redis/client/dist/lib/RESP/types'; -import { RedisVariadicArgument } from '@redis/client/dist/lib/commands/generic-transformers'; -import { transformRedisJsonNullReply } from './helpers'; +import { RedisVariadicArgument, transformRedisJsonNullReply } from '@redis/client/dist/lib/commands/generic-transformers'; export interface JsonGetOptions { path?: RedisVariadicArgument; diff --git a/packages/json/lib/commands/MERGE.ts b/packages/json/lib/commands/MERGE.ts index 72baea1048a..1a4b54fc4ba 100644 --- a/packages/json/lib/commands/MERGE.ts +++ b/packages/json/lib/commands/MERGE.ts @@ -1,6 +1,6 @@ import { CommandParser } from '@redis/client/dist/lib/client/parser'; import { SimpleStringReply, Command, RedisArgument } from '@redis/client/dist/lib/RESP/types'; -import { RedisJSON, transformRedisJsonArgument } from './helpers'; +import { RedisJSON, transformRedisJsonArgument } from '@redis/client/dist/lib/commands/generic-transformers'; export default { IS_READ_ONLY: false, diff --git a/packages/json/lib/commands/MGET.ts b/packages/json/lib/commands/MGET.ts index 7bb948bc667..01a7783b922 100644 --- a/packages/json/lib/commands/MGET.ts +++ b/packages/json/lib/commands/MGET.ts @@ -1,6 +1,6 @@ import { CommandParser } from '@redis/client/dist/lib/client/parser'; import { RedisArgument, UnwrapReply, ArrayReply, NullReply, BlobStringReply, Command } from '@redis/client/dist/lib/RESP/types'; -import { transformRedisJsonNullReply } from './helpers'; +import { transformRedisJsonNullReply } from '@redis/client/dist/lib/commands/generic-transformers'; export default { IS_READ_ONLY: true, diff --git a/packages/json/lib/commands/MSET.ts b/packages/json/lib/commands/MSET.ts index 9e5ec1799f1..81e8d4c6bdf 100644 --- a/packages/json/lib/commands/MSET.ts +++ b/packages/json/lib/commands/MSET.ts @@ -1,6 +1,6 @@ import { CommandParser } from '@redis/client/dist/lib/client/parser'; import { RedisArgument, SimpleStringReply, Command } from '@redis/client/dist/lib/RESP/types'; -import { RedisJSON, transformRedisJsonArgument } from './helpers'; +import { RedisJSON, transformRedisJsonArgument } from '@redis/client/dist/lib/commands/generic-transformers'; export interface JsonMSetItem { key: RedisArgument; diff --git a/packages/json/lib/commands/SET.ts b/packages/json/lib/commands/SET.ts index a0df41fa89d..9ab680b4898 100644 --- a/packages/json/lib/commands/SET.ts +++ b/packages/json/lib/commands/SET.ts @@ -1,6 +1,6 @@ import { CommandParser } from '@redis/client/dist/lib/client/parser'; import { RedisArgument, SimpleStringReply, NullReply, Command } from '@redis/client/dist/lib/RESP/types'; -import { RedisJSON, transformRedisJsonArgument } from './helpers'; +import { RedisJSON, transformRedisJsonArgument } from '@redis/client/dist/lib/commands/generic-transformers'; export interface JsonSetOptions { condition?: 'NX' | 'XX'; diff --git a/packages/json/lib/commands/STRAPPEND.ts b/packages/json/lib/commands/STRAPPEND.ts index aa8f3772fb1..b3115f684c3 100644 --- a/packages/json/lib/commands/STRAPPEND.ts +++ b/packages/json/lib/commands/STRAPPEND.ts @@ -1,6 +1,6 @@ import { CommandParser } from '@redis/client/dist/lib/client/parser'; import { RedisArgument, Command, NullReply, NumberReply, ArrayReply } from '@redis/client/dist/lib/RESP/types'; -import { transformRedisJsonArgument } from './helpers'; +import { transformRedisJsonArgument } from '@redis/client/dist/lib/commands/generic-transformers'; export interface JsonStrAppendOptions { path?: RedisArgument; diff --git a/packages/json/lib/commands/helpers.ts b/packages/json/lib/commands/helpers.ts deleted file mode 100644 index 99579ce81cb..00000000000 --- a/packages/json/lib/commands/helpers.ts +++ /dev/null @@ -1,20 +0,0 @@ -import { isNullReply } from "@redis/client/dist/lib/commands/generic-transformers"; -import { BlobStringReply, NullReply, UnwrapReply } from "@redis/client/dist/lib/RESP/types"; - -export function transformRedisJsonNullReply(json: NullReply | BlobStringReply): NullReply | RedisJSON { - return isNullReply(json) ? json : transformRedisJsonReply(json); -} - -export type RedisJSON = null | boolean | number | string | Date | Array | { - [key: string]: RedisJSON; - [key: number]: RedisJSON; -}; - -export function transformRedisJsonArgument(json: RedisJSON): string { - return JSON.stringify(json); -} - -export function transformRedisJsonReply(json: BlobStringReply): RedisJSON { - const res = JSON.parse((json as unknown as UnwrapReply).toString()); - return res; -} diff --git a/packages/json/lib/commands/index.ts b/packages/json/lib/commands/index.ts index a9e16bde757..0e29bdd648d 100644 --- a/packages/json/lib/commands/index.ts +++ b/packages/json/lib/commands/index.ts @@ -23,7 +23,9 @@ import STRLEN from './STRLEN'; import TOGGLE from './TOGGLE'; import TYPE from './TYPE'; -export * from './helpers'; +// Re-export helper types and functions from client package +export type { RedisJSON } from '@redis/client/dist/lib/commands/generic-transformers'; +export { transformRedisJsonArgument, transformRedisJsonReply, transformRedisJsonNullReply } from '@redis/client/dist/lib/commands/generic-transformers'; export default { ARRAPPEND, From 742d5713e8938be0f3b93adb2ddc858edf196ff4 Mon Sep 17 00:00:00 2001 From: Nikolay Karadzhov Date: Wed, 25 Jun 2025 13:15:44 +0300 Subject: [PATCH 1637/1748] fix(commands): sPopCount return Array (#3006) Also, touch the tests for spop and spopcount to use the new parseCommand API fixes #3004 --- packages/client/lib/commands/SPOP.spec.ts | 19 +++++++++++++++++-- .../client/lib/commands/SPOP_COUNT.spec.ts | 19 +++++++++++++++++-- packages/client/lib/commands/SPOP_COUNT.ts | 4 ++-- 3 files changed, 36 insertions(+), 6 deletions(-) diff --git a/packages/client/lib/commands/SPOP.spec.ts b/packages/client/lib/commands/SPOP.spec.ts index f435134416b..542e1ba3fcb 100644 --- a/packages/client/lib/commands/SPOP.spec.ts +++ b/packages/client/lib/commands/SPOP.spec.ts @@ -1,12 +1,14 @@ import { strict as assert } from 'node:assert'; import testUtils, { GLOBAL } from '../test-utils'; import SPOP from './SPOP'; -import { parseArgs } from './generic-transformers'; +import { BasicCommandParser } from '../client/parser'; describe('SPOP', () => { it('transformArguments', () => { + const parser = new BasicCommandParser(); + SPOP.parseCommand(parser, 'key'); assert.deepEqual( - parseArgs(SPOP, 'key'), + parser.redisArgs, ['SPOP', 'key'] ); }); @@ -16,6 +18,19 @@ describe('SPOP', () => { await client.sPop('key'), null ); + + await client.sAdd('key', 'member'); + + assert.equal( + await client.sPop('key'), + 'member' + ); + + assert.equal( + await client.sPop('key'), + null + ); + }, { client: GLOBAL.SERVERS.OPEN, cluster: GLOBAL.CLUSTERS.OPEN diff --git a/packages/client/lib/commands/SPOP_COUNT.spec.ts b/packages/client/lib/commands/SPOP_COUNT.spec.ts index 935ff437800..9720101f31f 100644 --- a/packages/client/lib/commands/SPOP_COUNT.spec.ts +++ b/packages/client/lib/commands/SPOP_COUNT.spec.ts @@ -1,21 +1,36 @@ import { strict as assert } from 'node:assert'; import testUtils, { GLOBAL } from '../test-utils'; import SPOP_COUNT from './SPOP_COUNT'; -import { parseArgs } from './generic-transformers'; +import { BasicCommandParser } from '../client/parser'; describe('SPOP_COUNT', () => { it('transformArguments', () => { + const parser = new BasicCommandParser(); + SPOP_COUNT.parseCommand(parser, 'key', 1); assert.deepEqual( - parseArgs(SPOP_COUNT, 'key', 1), + parser.redisArgs, ['SPOP', 'key', '1'] ); }); testUtils.testAll('sPopCount', async client => { + assert.deepEqual( await client.sPopCount('key', 1), [] ); + + await Promise.all([ + client.sAdd('key', 'member'), + client.sAdd('key', 'member2'), + client.sAdd('key', 'member3') + ]) + + assert.deepEqual( + (await client.sPopCount('key', 3)).length, + 3 + ); + }, { client: GLOBAL.SERVERS.OPEN, cluster: GLOBAL.CLUSTERS.OPEN diff --git a/packages/client/lib/commands/SPOP_COUNT.ts b/packages/client/lib/commands/SPOP_COUNT.ts index 1191f07cff2..a285e6f5c48 100644 --- a/packages/client/lib/commands/SPOP_COUNT.ts +++ b/packages/client/lib/commands/SPOP_COUNT.ts @@ -1,5 +1,5 @@ import { CommandParser } from '../client/parser'; -import { RedisArgument, BlobStringReply, NullReply, Command } from '../RESP/types'; +import { RedisArgument, Command, ArrayReply } from '../RESP/types'; export default { IS_READ_ONLY: false, @@ -16,5 +16,5 @@ export default { parser.pushKey(key); parser.push(count.toString()); }, - transformReply: undefined as unknown as () => BlobStringReply | NullReply + transformReply: undefined as unknown as () => ArrayReply } as const satisfies Command; From 6f3380ba68786a3d5a9825f9a29c2053e43d32e3 Mon Sep 17 00:00:00 2001 From: Ricardo Ferreira Date: Wed, 25 Jun 2025 13:41:54 +0100 Subject: [PATCH 1638/1748] fix: ensure the repo links in the README are functional on the website (#3005) --- README.md | 39 +++++++++++++++++++-------------------- 1 file changed, 19 insertions(+), 20 deletions(-) diff --git a/README.md b/README.md index 38615ee519e..ab6b4707e6f 100644 --- a/README.md +++ b/README.md @@ -45,13 +45,13 @@ npm install redis | Name | Description | | ---------------------------------------------- | ------------------------------------------------------------------------------------------- | -| [`redis`](./packages/redis) | The client with all the ["redis-stack"](https://github.com/redis-stack/redis-stack) modules | -| [`@redis/client`](./packages/client) | The base clients (i.e `RedisClient`, `RedisCluster`, etc.) | -| [`@redis/bloom`](./packages/bloom) | [Redis Bloom](https://redis.io/docs/data-types/probabilistic/) commands | -| [`@redis/json`](./packages/json) | [Redis JSON](https://redis.io/docs/data-types/json/) commands | -| [`@redis/search`](./packages/search) | [RediSearch](https://redis.io/docs/interact/search-and-query/) commands | -| [`@redis/time-series`](./packages/time-series) | [Redis Time-Series](https://redis.io/docs/data-types/timeseries/) commands | -| [`@redis/entraid`](./packages/entraid) | Secure token-based authentication for Redis clients using Microsoft Entra ID | +| [`redis`](https://github.com/redis/node-redis/tree/master/packages/redis) | The client with all the ["redis-stack"](https://github.com/redis-stack/redis-stack) modules | +| [`@redis/client`](https://github.com/redis/node-redis/tree/master/packages/client) | The base clients (i.e `RedisClient`, `RedisCluster`, etc.) | +| [`@redis/bloom`](https://github.com/redis/node-redis/tree/master/packages/bloom) | [Redis Bloom](https://redis.io/docs/data-types/probabilistic/) commands | +| [`@redis/json`](https://github.com/redis/node-redis/tree/master/packages/json) | [Redis JSON](https://redis.io/docs/data-types/json/) commands | +| [`@redis/search`](https://github.com/redis/node-redis/tree/master/packages/search) | [RediSearch](https://redis.io/docs/interact/search-and-query/) commands | +| [`@redis/time-series`](https://github.com/redis/node-redis/tree/master/packages/time-series) | [Redis Time-Series](https://redis.io/docs/data-types/timeseries/) commands | +| [`@redis/entraid`](https://github.com/redis/node-redis/tree/master/packages/entraid) | Secure token-based authentication for Redis clients using Microsoft Entra ID | > Looking for a high-level library to handle object mapping? > See [redis-om-node](https://github.com/redis/redis-om-node)! @@ -83,7 +83,7 @@ createClient({ ``` 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). +the [client configuration guide](https://github.com/redis/node-redis/blob/master/docs/client-configuration.md). To check if the the client is connected and ready to send commands, use `client.isReady` which returns a boolean. `client.isOpen` is also available. This returns `true` when the client's underlying socket is open, and `false` when it @@ -188,7 +188,7 @@ await pool.ping(); ### Pub/Sub -See the [Pub/Sub overview](./docs/pub-sub.md). +See the [Pub/Sub overview](https://github.com/redis/node-redis/blob/master/docs/pub-sub.md). ### Scan Iterator @@ -234,7 +234,6 @@ of sending a `QUIT` command to the server, the client can simply close the netwo ```typescript client.destroy(); ``` - ### Client Side Caching Node Redis v5 adds support for [Client Side Caching](https://redis.io/docs/manual/client-side-caching/), which enables clients to cache query results locally. The Redis server will notify the client when cached results are no longer valid. @@ -251,7 +250,7 @@ const client = createClient({ }); ``` -See the [V5 documentation](./docs/v5.md#client-side-caching) for more details and advanced usage. +See the [V5 documentation](https://github.com/redis/node-redis/blob/master/docs/v5.md#client-side-caching) for more details and advanced usage. ### Auto-Pipelining @@ -275,11 +274,11 @@ await Promise.all([ ### Programmability -See the [Programmability overview](./docs/programmability.md). +See the [Programmability overview](https://github.com/redis/node-redis/blob/master/docs/programmability.md). ### Clustering -Check out the [Clustering Guide](./docs/clustering.md) when using Node Redis to connect to a Redis Cluster. +Check out the [Clustering Guide](https://github.com/redis/node-redis/blob/master/docs/clustering.md) when using Node Redis to connect to a Redis Cluster. ### Events @@ -292,12 +291,12 @@ The Node Redis client class is an Nodejs EventEmitter and it emits an event each | `end` | Connection has been closed (via `.disconnect()`) | _No arguments_ | | `error` | An error has occurred—usually a network issue such as "Socket closed unexpectedly" | `(error: Error)` | | `reconnecting` | Client is trying to reconnect to the server | _No arguments_ | -| `sharded-channel-moved` | See [here](./docs/pub-sub.md#sharded-channel-moved-event) | See [here](./docs/pub-sub.md#sharded-channel-moved-event) | +| `sharded-channel-moved` | See [here](https://github.com/redis/node-redis/blob/master/docs/pub-sub.md#sharded-channel-moved-event) | See [here](https://github.com/redis/node-redis/blob/master/docs/pub-sub.md#sharded-channel-moved-event) | > :warning: You **MUST** listen to `error` events. If a client doesn't have at least one `error` listener registered and > an `error` occurs, that error will be thrown and the Node.js process will exit. See the [ > `EventEmitter` docs](https://nodejs.org/api/events.html#events_error_events) for more details. -> The client will not emit [any other events](./docs/v3-to-v4.md#all-the-removed-events) beyond those listed above. +> The client will not emit [any other events](https://github.com/redis/node-redis/blob/master/docs/v3-to-v4.md#all-the-removed-events) beyond those listed above. ## Supported Redis versions @@ -314,13 +313,13 @@ Node Redis is supported with the following versions of Redis: ## Migration -- [From V3 to V4](docs/v3-to-v4.md) -- [From V4 to V5](docs/v4-to-v5.md) -- [V5](docs/v5.md) +- [From V3 to V4](https://github.com/redis/node-redis/blob/master/docs/v3-to-v4.md) +- [From V4 to V5](https://github.com/redis/node-redis/blob/master/docs/v4-to-v5.md) +- [V5](https://github.com/redis/node-redis/blob/master/docs/v5.md) ## Contributing -If you'd like to contribute, check out the [contributing guide](CONTRIBUTING.md). +If you'd like to contribute, check out the [contributing guide](https://github.com/redis/node-redis/blob/master/CONTRIBUTING.md). Thank you to all the people who already contributed to Node Redis! @@ -328,4 +327,4 @@ Thank you to all the people who already contributed to Node Redis! ## License -This repository is licensed under the "MIT" license. See [LICENSE](LICENSE). +This repository is licensed under the "MIT" license. See [LICENSE](https://github.com/redis/node-redis/blob/master/LICENSE). From 79749f24618baa7d977470372cafaa4b747f0837 Mon Sep 17 00:00:00 2001 From: Nikolay Karadzhov Date: Mon, 7 Jul 2025 11:06:34 +0300 Subject: [PATCH 1639/1748] fix(sentinel): propagate RESP option to clients (#3011) `createSentinel` takes RESP as an option, but does not propagate down to the actual clients. This creates confusion for the users as they expect the option to be set to all clients, which is reasonable. In case of clientSideCaching, this problem manifests as validation failure because clientSideCaching requires RESP3, but if we dont propagate, clients start with the default RESP2 fixes #3010 --- packages/client/lib/sentinel/index.spec.ts | 23 ++++++++++++++-------- packages/client/lib/sentinel/index.ts | 5 +++++ packages/test-utils/lib/index.ts | 2 ++ 3 files changed, 22 insertions(+), 8 deletions(-) diff --git a/packages/client/lib/sentinel/index.spec.ts b/packages/client/lib/sentinel/index.spec.ts index a9bdc8cc95c..ef1702eab13 100644 --- a/packages/client/lib/sentinel/index.spec.ts +++ b/packages/client/lib/sentinel/index.spec.ts @@ -23,7 +23,7 @@ describe('RedisSentinel', () => { { host: 'localhost', port: 26379 } ] }; - + it('should throw error when clientSideCache is enabled with RESP 2', () => { assert.throws( () => RedisSentinel.create({ @@ -46,7 +46,7 @@ describe('RedisSentinel', () => { }); it('should not throw when clientSideCache is enabled with RESP 3', () => { - assert.doesNotThrow(() => + assert.doesNotThrow(() => RedisSentinel.create({ ...options, clientSideCache: clientSideCacheConfig, @@ -54,6 +54,16 @@ describe('RedisSentinel', () => { }) ); }); + + testUtils.testWithClientSentinel('should successfully connect to sentinel', async () => { + }, { + ...GLOBAL.SENTINEL.OPEN, + sentinelOptions: { + RESP: 3, + clientSideCache: { ttl: 0, maxEntries: 0, evictPolicy: 'LRU'}, + }, + }) + }); }); }); @@ -417,7 +427,7 @@ async function steadyState(frame: SentinelFramework) { sentinel.setTracer(tracer); await sentinel.connect(); await nodePromise; - + await sentinel.flushAll(); } finally { if (sentinel !== undefined) { @@ -443,7 +453,7 @@ describe('legacy tests', () => { this.timeout(15000); last = Date.now(); - + function deltaMeasurer() { const delta = Date.now() - last; if (delta > longestDelta) { @@ -508,7 +518,7 @@ describe('legacy tests', () => { } stopMeasuringBlocking = true; - + await frame.cleanup(); }) @@ -1032,6 +1042,3 @@ describe('legacy tests', () => { }) }); }); - - - diff --git a/packages/client/lib/sentinel/index.ts b/packages/client/lib/sentinel/index.ts index ec570e64bf2..b4a794b871a 100644 --- a/packages/client/lib/sentinel/index.ts +++ b/packages/client/lib/sentinel/index.ts @@ -625,6 +625,7 @@ class RedisSentinelInternal< readonly #sentinelClientOptions: RedisClientOptions; readonly #scanInterval: number; readonly #passthroughClientErrorEvents: boolean; + readonly #RESP?: RespVersions; #anotherReset = false; @@ -673,6 +674,7 @@ class RedisSentinelInternal< this.#name = options.name; + this.#RESP = options.RESP; this.#sentinelRootNodes = Array.from(options.sentinelRootNodes); this.#maxCommandRediscovers = options.maxCommandRediscovers ?? 16; this.#masterPoolSize = options.masterPoolSize ?? 1; @@ -716,6 +718,9 @@ class RedisSentinelInternal< #createClient(node: RedisNode, clientOptions: RedisClientOptions, reconnectStrategy?: undefined | false) { return RedisClient.create({ + //first take the globally set RESP + RESP: this.#RESP, + //then take the client options, which can in theory overwrite it ...clientOptions, socket: { ...clientOptions.socket, diff --git a/packages/test-utils/lib/index.ts b/packages/test-utils/lib/index.ts index a41f970e0c8..43dd4debfdf 100644 --- a/packages/test-utils/lib/index.ts +++ b/packages/test-utils/lib/index.ts @@ -337,6 +337,7 @@ export default class TestUtils { port: promise.port })); + const sentinel = createSentinel({ name: 'mymaster', sentinelRootNodes: rootNodes, @@ -352,6 +353,7 @@ export default class TestUtils { functions: options?.functions || {}, masterPoolSize: options?.masterPoolSize || undefined, reserveClient: options?.reserveClient || false, + ...options?.sentinelOptions }) as RedisSentinelType; if (options.disableClientSetup) { From 65a12d50e711d86c6172f586b6a82c33efaf4bb2 Mon Sep 17 00:00:00 2001 From: Nikolay Karadzhov Date: Mon, 7 Jul 2025 11:37:08 +0300 Subject: [PATCH 1640/1748] feat(client): add command timeout option (#3008) Co-authored-by: Florian Schunk <149071178+florian-schunk@users.noreply.github.com> --- docs/command-options.md | 13 ++ packages/client/lib/client/commands-queue.ts | 56 +++++++-- packages/client/lib/client/index.spec.ts | 118 +++++++++++++++---- packages/client/lib/client/index.ts | 12 +- packages/client/lib/cluster/index.ts | 28 +++-- packages/client/lib/sentinel/index.ts | 38 +++--- packages/client/lib/sentinel/utils.ts | 2 +- packages/test-utils/lib/index.ts | 21 ++-- 8 files changed, 212 insertions(+), 76 deletions(-) diff --git a/docs/command-options.md b/docs/command-options.md index b246445ad74..8583eae135b 100644 --- a/docs/command-options.md +++ b/docs/command-options.md @@ -37,6 +37,19 @@ try { } ``` + +## Timeout + +This option is similar to the Abort Signal one, but provides an easier way to set timeout for commands. Again, this applies to commands that haven't been written to the socket yet. + +```javascript +const client = createClient({ + commandOptions: { + timeout: 1000 + } +}) +``` + ## ASAP Commands that are executed in the "asap" mode are added to the beginning of the "to sent" queue. diff --git a/packages/client/lib/client/commands-queue.ts b/packages/client/lib/client/commands-queue.ts index 78c0a01b203..52a07a7e3b5 100644 --- a/packages/client/lib/client/commands-queue.ts +++ b/packages/client/lib/client/commands-queue.ts @@ -3,7 +3,7 @@ import encodeCommand from '../RESP/encoder'; import { Decoder, PUSH_TYPE_MAPPING, RESP_TYPES } from '../RESP/decoder'; import { TypeMapping, ReplyUnion, RespVersions, RedisArgument } from '../RESP/types'; import { ChannelListeners, PubSub, PubSubCommand, PubSubListener, PubSubType, PubSubTypeListeners } from './pub-sub'; -import { AbortError, ErrorReply } from '../errors'; +import { AbortError, ErrorReply, TimeoutError } from '../errors'; import { MonitorCallback } from '.'; export interface CommandOptions { @@ -14,6 +14,10 @@ export interface CommandOptions { * Maps between RESP and JavaScript types */ typeMapping?: T; + /** + * Timeout for the command in milliseconds + */ + timeout?: number; } export interface CommandToWrite extends CommandWaitingForReply { @@ -23,6 +27,10 @@ export interface CommandToWrite extends CommandWaitingForReply { signal: AbortSignal; listener: () => unknown; } | undefined; + timeout: { + signal: AbortSignal; + listener: () => unknown; + } | undefined; } interface CommandWaitingForReply { @@ -80,7 +88,7 @@ export default class RedisCommandsQueue { #onPush(push: Array) { // TODO: type if (this.#pubSub.handleMessageReply(push)) return true; - + const isShardedUnsubscribe = PubSub.isShardedUnsubscribe(push); if (isShardedUnsubscribe && !this.#waitingForReply.length) { const channel = push[1].toString(); @@ -153,12 +161,26 @@ export default class RedisCommandsQueue { args, chainId: options?.chainId, abort: undefined, + timeout: undefined, resolve, reject, channelsCounter: undefined, typeMapping: options?.typeMapping }; + const timeout = options?.timeout; + if (timeout) { + const signal = AbortSignal.timeout(timeout); + value.timeout = { + signal, + listener: () => { + this.#toWrite.remove(node); + value.reject(new TimeoutError()); + } + }; + signal.addEventListener('abort', value.timeout.listener, { once: true }); + } + const signal = options?.abortSignal; if (signal) { value.abort = { @@ -181,6 +203,7 @@ export default class RedisCommandsQueue { args: command.args, chainId, abort: undefined, + timeout: undefined, resolve() { command.resolve(); resolve(); @@ -202,7 +225,7 @@ export default class RedisCommandsQueue { this.decoder.onReply = (reply => { if (Array.isArray(reply)) { if (this.#onPush(reply)) return; - + if (PONG.equals(reply[0] as Buffer)) { const { resolve, typeMapping } = this.#waitingForReply.shift()!, buffer = ((reply[1] as Buffer).length === 0 ? reply[0] : reply[1]) as Buffer; @@ -250,7 +273,7 @@ export default class RedisCommandsQueue { if (!this.#pubSub.isActive) { this.#resetDecoderCallbacks(); } - + resolve(); }; } @@ -299,6 +322,7 @@ export default class RedisCommandsQueue { args: ['MONITOR'], chainId: options?.chainId, abort: undefined, + timeout: undefined, // using `resolve` instead of using `.then`/`await` to make sure it'll be called before processing the next reply resolve: () => { // after running `MONITOR` only `MONITOR` and `RESET` replies are expected @@ -317,7 +341,7 @@ export default class RedisCommandsQueue { reject, channelsCounter: undefined, typeMapping - }, options?.asap); + }, options?.asap); }); } @@ -340,11 +364,11 @@ export default class RedisCommandsQueue { this.#resetDecoderCallbacks(); this.#resetFallbackOnReply = undefined; this.#pubSub.reset(); - + this.#waitingForReply.shift()!.resolve(reply); return; } - + this.#resetFallbackOnReply!(reply); }) as Decoder['onReply']; @@ -352,6 +376,7 @@ export default class RedisCommandsQueue { args: ['RESET'], chainId, abort: undefined, + timeout: undefined, resolve, reject, channelsCounter: undefined, @@ -376,16 +401,20 @@ export default class RedisCommandsQueue { continue; } - // TODO reuse `toSend` or create new object? + // TODO reuse `toSend` or create new object? (toSend as any).args = undefined; if (toSend.abort) { RedisCommandsQueue.#removeAbortListener(toSend); toSend.abort = undefined; } + if (toSend.timeout) { + RedisCommandsQueue.#removeTimeoutListener(toSend); + toSend.timeout = undefined; + } this.#chainInExecution = toSend.chainId; toSend.chainId = undefined; this.#waitingForReply.push(toSend); - + yield encoded; toSend = this.#toWrite.shift(); } @@ -402,11 +431,18 @@ export default class RedisCommandsQueue { command.abort!.signal.removeEventListener('abort', command.abort!.listener); } + static #removeTimeoutListener(command: CommandToWrite) { + command.timeout!.signal.removeEventListener('abort', command.timeout!.listener); + } + static #flushToWrite(toBeSent: CommandToWrite, err: Error) { if (toBeSent.abort) { RedisCommandsQueue.#removeAbortListener(toBeSent); } - + if (toBeSent.timeout) { + RedisCommandsQueue.#removeTimeoutListener(toBeSent); + } + toBeSent.reject(err); } diff --git a/packages/client/lib/client/index.spec.ts b/packages/client/lib/client/index.spec.ts index 4f752210dbe..f04d6467062 100644 --- a/packages/client/lib/client/index.spec.ts +++ b/packages/client/lib/client/index.spec.ts @@ -1,9 +1,9 @@ import { strict as assert } from 'node:assert'; import testUtils, { GLOBAL, waitTillBeenCalled } from '../test-utils'; import RedisClient, { RedisClientOptions, RedisClientType } from '.'; -import { AbortError, ClientClosedError, ClientOfflineError, ConnectionTimeoutError, DisconnectsClientError, ErrorReply, MultiErrorReply, SocketClosedUnexpectedlyError, WatchError } from '../errors'; +import { AbortError, ClientClosedError, ClientOfflineError, ConnectionTimeoutError, DisconnectsClientError, ErrorReply, MultiErrorReply, SocketClosedUnexpectedlyError, TimeoutError, WatchError } from '../errors'; import { defineScript } from '../lua-script'; -import { spy } from 'sinon'; +import { spy, stub } from 'sinon'; import { once } from 'node:events'; import { MATH_FUNCTION, loadMathFunction } from '../commands/FUNCTION_LOAD.spec'; import { RESP_TYPES } from '../RESP/decoder'; @@ -239,30 +239,84 @@ describe('Client', () => { assert.equal(await client.sendCommand(['PING']), 'PONG'); }, GLOBAL.SERVERS.OPEN); - describe('AbortController', () => { - before(function () { - if (!global.AbortController) { - this.skip(); - } + testUtils.testWithClient('Unactivated AbortController should not abort', async client => { + await client.sendCommand(['PING'], { + abortSignal: new AbortController().signal }); + }, GLOBAL.SERVERS.OPEN); - testUtils.testWithClient('success', async client => { - await client.sendCommand(['PING'], { - abortSignal: new AbortController().signal - }); - }, GLOBAL.SERVERS.OPEN); + testUtils.testWithClient('AbortError', async client => { + await blockSetImmediate(async () => { + await assert.rejects(client.sendCommand(['PING'], { + abortSignal: AbortSignal.timeout(5) + }), AbortError); + }) + }, GLOBAL.SERVERS.OPEN); - testUtils.testWithClient('AbortError', client => { - const controller = new AbortController(); - controller.abort(); + testUtils.testWithClient('Timeout with custom timeout config', async client => { + await blockSetImmediate(async () => { + await assert.rejects(client.sendCommand(['PING'], { + timeout: 5 + }), TimeoutError); + }) + }, GLOBAL.SERVERS.OPEN); - return assert.rejects( - client.sendCommand(['PING'], { - abortSignal: controller.signal - }), - AbortError - ); - }, GLOBAL.SERVERS.OPEN); + testUtils.testWithCluster('Timeout with custom timeout config (cluster)', async cluster => { + await blockSetImmediate(async () => { + await assert.rejects(cluster.sendCommand(undefined, true, ['PING'], { + timeout: 5 + }), TimeoutError); + }) + }, GLOBAL.CLUSTERS.OPEN); + + testUtils.testWithClientSentinel('Timeout with custom timeout config (sentinel)', async sentinel => { + await blockSetImmediate(async () => { + await assert.rejects(sentinel.sendCommand(true, ['PING'], { + timeout: 5 + }), TimeoutError); + }) + }, GLOBAL.CLUSTERS.OPEN); + + testUtils.testWithClient('Timeout with global timeout config', async client => { + await blockSetImmediate(async () => { + await assert.rejects(client.ping(), TimeoutError); + await assert.rejects(client.sendCommand(['PING']), TimeoutError); + }); + }, { + ...GLOBAL.SERVERS.OPEN, + clientOptions: { + commandOptions: { + timeout: 5 + } + } + }); + + testUtils.testWithCluster('Timeout with global timeout config (cluster)', async cluster => { + await blockSetImmediate(async () => { + await assert.rejects(cluster.HSET('key', 'foo', 'value'), TimeoutError); + await assert.rejects(cluster.sendCommand(undefined, true, ['PING']), TimeoutError); + }); + }, { + ...GLOBAL.CLUSTERS.OPEN, + clusterConfiguration: { + commandOptions: { + timeout: 5 + } + } + }); + + testUtils.testWithClientSentinel('Timeout with global timeout config (sentinel)', async sentinel => { + await blockSetImmediate(async () => { + await assert.rejects(sentinel.HSET('key', 'foo', 'value'), TimeoutError); + await assert.rejects(sentinel.sendCommand(true, ['PING']), TimeoutError); + }); + }, { + ...GLOBAL.SENTINEL.OPEN, + clientOptions: { + commandOptions: { + timeout: 5 + } + } }); testUtils.testWithClient('undefined and null should not break the client', async client => { @@ -900,3 +954,23 @@ describe('Client', () => { }, GLOBAL.SERVERS.OPEN); }); }); + +/** + * Executes the provided function in a context where setImmediate is stubbed to not do anything. + * This blocks setImmediate callbacks from executing + */ +async function blockSetImmediate(fn: () => Promise) { + let setImmediateStub: any; + + try { + setImmediateStub = stub(global, 'setImmediate'); + setImmediateStub.callsFake(() => { + //Dont call the callback, effectively blocking execution + }); + await fn(); + } finally { + if (setImmediateStub) { + setImmediateStub.restore(); + } + } +} diff --git a/packages/client/lib/client/index.ts b/packages/client/lib/client/index.ts index a446ad8e755..128dc599677 100644 --- a/packages/client/lib/client/index.ts +++ b/packages/client/lib/client/index.ts @@ -526,7 +526,7 @@ export default class RedisClient< async #handshake(chainId: symbol, asap: boolean) { const promises = []; const commandsWithErrorHandlers = await this.#getHandshakeCommands(); - + if (asap) commandsWithErrorHandlers.reverse() for (const { cmd, errorHandler } of commandsWithErrorHandlers) { @@ -632,7 +632,7 @@ export default class RedisClient< // since they could be connected to an older version that doesn't support them. } }); - + commands.push({ cmd: [ 'CLIENT', @@ -889,7 +889,13 @@ export default class RedisClient< return Promise.reject(new ClientOfflineError()); } - const promise = this._self.#queue.addCommand(args, options); + // Merge global options with provided options + const opts = { + ...this._self._commandOptions, + ...options + } + + const promise = this._self.#queue.addCommand(args, opts); this._self.#scheduleWrite(); return promise; } diff --git a/packages/client/lib/cluster/index.ts b/packages/client/lib/cluster/index.ts index c2c251810e3..6d26ac98c9a 100644 --- a/packages/client/lib/cluster/index.ts +++ b/packages/client/lib/cluster/index.ts @@ -38,12 +38,12 @@ export interface RedisClusterOptions< // POLICIES extends CommandPolicies = CommandPolicies > extends ClusterCommander { /** - * Should contain details for some of the cluster nodes that the client will use to discover + * Should contain details for some of the cluster nodes that the client will use to discover * the "cluster topology". We recommend including details for at least 3 nodes here. */ rootNodes: Array; /** - * Default values used for every client in the cluster. Use this to specify global values, + * Default values used for every client in the cluster. Use this to specify global values, * for example: ACL credentials, timeouts, TLS configuration etc. */ defaults?: Partial; @@ -68,13 +68,13 @@ export interface RedisClusterOptions< nodeAddressMap?: NodeAddressMap; /** * Client Side Caching configuration for the pool. - * - * Enables Redis Servers and Clients to work together to cache results from commands + * + * Enables Redis Servers and Clients to work together to cache results from commands * sent to a server. The server will notify the client when cached results are no longer valid. * In pooled mode, the cache is shared across all clients in the pool. - * + * * Note: Client Side Caching is only supported with RESP3. - * + * * @example Anonymous cache configuration * ``` * const client = createCluster({ @@ -86,7 +86,7 @@ export interface RedisClusterOptions< * minimum: 5 * }); * ``` - * + * * @example Using a controllable cache * ``` * const cache = new BasicPooledClientSideCache({ @@ -406,7 +406,7 @@ export default class RedisCluster< proxy._commandOptions[key] = value; return proxy as RedisClusterType< M, - F, + F, S, RESP, K extends 'typeMapping' ? V extends TypeMapping ? V : {} : TYPE_MAPPING @@ -489,7 +489,7 @@ export default class RedisCluster< myFn = this._handleAsk(fn); continue; } - + if (err.message.startsWith('MOVED')) { await this._slots.rediscover(client); client = await this._slots.getClient(firstKey, isReadonly); @@ -497,7 +497,7 @@ export default class RedisCluster< } throw err; - } + } } } @@ -508,10 +508,16 @@ export default class RedisCluster< options?: ClusterCommandOptions, // defaultPolicies?: CommandPolicies ): Promise { + + // Merge global options with local options + const opts = { + ...this._self._commandOptions, + ...options + } return this._self._execute( firstKey, isReadonly, - options, + opts, (client, opts) => client.sendCommand(args, opts) ); } diff --git a/packages/client/lib/sentinel/index.ts b/packages/client/lib/sentinel/index.ts index b4a794b871a..b3f3bbf0b8d 100644 --- a/packages/client/lib/sentinel/index.ts +++ b/packages/client/lib/sentinel/index.ts @@ -35,7 +35,7 @@ export class RedisSentinelClient< /** * Indicates if the client connection is open - * + * * @returns `true` if the client connection is open, `false` otherwise */ @@ -45,7 +45,7 @@ export class RedisSentinelClient< /** * Indicates if the client connection is ready to accept commands - * + * * @returns `true` if the client connection is ready, `false` otherwise */ get isReady() { @@ -54,7 +54,7 @@ export class RedisSentinelClient< /** * Gets the command options configured for this client - * + * * @returns The command options for this client or `undefined` if none were set */ get commandOptions() { @@ -241,10 +241,10 @@ export class RedisSentinelClient< /** * Releases the client lease back to the pool - * + * * After calling this method, the client instance should no longer be used as it * will be returned to the client pool and may be given to other operations. - * + * * @returns A promise that resolves when the client is ready to be reused, or undefined * if the client was immediately ready * @throws Error if the lease has already been released @@ -274,7 +274,7 @@ export default class RedisSentinel< /** * Indicates if the sentinel connection is open - * + * * @returns `true` if the sentinel connection is open, `false` otherwise */ get isOpen() { @@ -283,7 +283,7 @@ export default class RedisSentinel< /** * Indicates if the sentinel connection is ready to accept commands - * + * * @returns `true` if the sentinel connection is ready, `false` otherwise */ get isReady() { @@ -554,15 +554,15 @@ export default class RedisSentinel< /** * Acquires a master client lease for exclusive operations - * + * * Used when multiple commands need to run on an exclusive client (for example, using `WATCH/MULTI/EXEC`). * The returned client must be released after use with the `release()` method. - * + * * @returns A promise that resolves to a Redis client connected to the master node * @example * ```javascript * const clientLease = await sentinel.acquire(); - * + * * try { * await clientLease.watch('key'); * const resp = await clientLease.multi() @@ -671,7 +671,7 @@ class RedisSentinelInternal< super(); this.#validateOptions(options); - + this.#name = options.name; this.#RESP = options.RESP; @@ -733,7 +733,7 @@ class RedisSentinelInternal< /** * Gets a client lease from the master client pool - * + * * @returns A client info object or a promise that resolves to a client info object * when a client becomes available */ @@ -748,10 +748,10 @@ class RedisSentinelInternal< /** * Releases a client lease back to the pool - * + * * If the client was used for a transaction that might have left it in a dirty state, * it will be reset before being returned to the pool. - * + * * @param clientInfo The client info object representing the client to release * @returns A promise that resolves when the client is ready to be reused, or undefined * if the client was immediately ready or no longer exists @@ -791,10 +791,10 @@ class RedisSentinelInternal< async #connect() { let count = 0; - while (true) { + while (true) { this.#trace("starting connect loop"); - count+=1; + count+=1; if (this.#destroy) { this.#trace("in #connect and want to destroy") return; @@ -847,7 +847,7 @@ class RedisSentinelInternal< try { /* - // force testing of READONLY errors + // force testing of READONLY errors if (clientInfo !== undefined) { if (Math.floor(Math.random() * 10) < 1) { console.log("throwing READONLY error"); @@ -861,7 +861,7 @@ class RedisSentinelInternal< throw err; } - /* + /* rediscover and retry if doing a command against a "master" a) READONLY error (topology has changed) but we haven't been notified yet via pubsub b) client is "not ready" (disconnected), which means topology might have changed, but sentinel might not see it yet @@ -1574,4 +1574,4 @@ export class RedisSentinelFactory extends EventEmitter { } }); } -} \ No newline at end of file +} diff --git a/packages/client/lib/sentinel/utils.ts b/packages/client/lib/sentinel/utils.ts index 7e2404c2f7a..c124981e257 100644 --- a/packages/client/lib/sentinel/utils.ts +++ b/packages/client/lib/sentinel/utils.ts @@ -6,7 +6,7 @@ import { NamespaceProxySentinel, NamespaceProxySentinelClient, ProxySentinel, Pr /* TODO: should use map interface, would need a transform reply probably? as resp2 is list form, which this depends on */ export function parseNode(node: Record): RedisNode | undefined{ - + if (node.flags.includes("s_down") || node.flags.includes("disconnected") || node.flags.includes("failover_in_progress")) { return undefined; } diff --git a/packages/test-utils/lib/index.ts b/packages/test-utils/lib/index.ts index 43dd4debfdf..aab1c700f5e 100644 --- a/packages/test-utils/lib/index.ts +++ b/packages/test-utils/lib/index.ts @@ -179,7 +179,7 @@ export default class TestUtils { this.#VERSION_NUMBERS = numbers; this.#DOCKER_IMAGE = { image: dockerImageName, - version: string, + version: string, mode: "server" }; } @@ -315,7 +315,7 @@ export default class TestUtils { if (passIndex != 0) { password = options.serverArguments[passIndex]; } - + if (this.isVersionGreaterThan(options.minimumDockerVersion)) { const dockerImage = this.#DOCKER_IMAGE; before(function () { @@ -333,18 +333,19 @@ export default class TestUtils { const promises = await dockerPromises; const rootNodes: Array = promises.map(promise => ({ - host: "127.0.0.1", + host: "127.0.0.1", port: promise.port })); const sentinel = createSentinel({ - name: 'mymaster', - sentinelRootNodes: rootNodes, - nodeClientOptions: { + name: 'mymaster', + sentinelRootNodes: rootNodes, + nodeClientOptions: { + commandOptions: options.clientOptions?.commandOptions, password: password || undefined, }, - sentinelClientOptions: { + sentinelClientOptions: { password: password || undefined, }, replicaPoolSize: options?.replicaPoolSize || 0, @@ -507,7 +508,7 @@ export default class TestUtils { it(title, async function () { if (!dockersPromise) return this.skip(); - + const dockers = await dockersPromise, cluster = createCluster({ rootNodes: dockers.map(({ port }) => ({ @@ -580,12 +581,12 @@ export default class TestUtils { const tmpDir = fs.mkdtempSync(path.join(os.tmpdir(), appPrefix)); sentinels.push(await spawnSentinelNode(this.#DOCKER_IMAGE, options.serverArguments, masterPort, sentinelName, tmpDir)) - + if (tmpDir) { fs.rmSync(tmpDir, { recursive: true }); } } - + return sentinels } } From d9a6bb376f9e6a54959c4424aad2e4c636071164 Mon Sep 17 00:00:00 2001 From: Nikolay Karadzhov Date: Tue, 8 Jul 2025 14:28:50 +0300 Subject: [PATCH 1641/1748] chore(release): use deploy keys for relese (#3013) main branch is protected and does not allow direct pushes. the release action needs to push. branch protection rules can be bypassed for people and apps, but not github actions. one of the workarounds is to use a ruleset in which we set a deploy key see: https://github.com/orgs/community/discussions/25305\#discussioncomment-10728028 --- .github/workflows/release.yml | 1 + 1 file changed, 1 insertion(+) diff --git a/.github/workflows/release.yml b/.github/workflows/release.yml index d5732f2eda0..e7c9d58fe71 100644 --- a/.github/workflows/release.yml +++ b/.github/workflows/release.yml @@ -23,6 +23,7 @@ jobs: uses: actions/checkout@v4 with: fetch-depth: 0 + ssh-key: ${{ secrets.RELEASE_KEY }} - name: Setup Node.js uses: actions/setup-node@v4 From 748cad2e7f71df66e8787d78720f02a1b394ad82 Mon Sep 17 00:00:00 2001 From: GitHub Action Date: Tue, 8 Jul 2025 11:37:25 +0000 Subject: [PATCH 1642/1748] Release client@5.6.0 --- package-lock.json | 14 +++++++++++++- packages/client/package.json | 2 +- 2 files changed, 14 insertions(+), 2 deletions(-) diff --git a/package-lock.json b/package-lock.json index d9fc9f93f92..f17556e3a04 100644 --- a/package-lock.json +++ b/package-lock.json @@ -7351,7 +7351,7 @@ }, "packages/client": { "name": "@redis/client", - "version": "5.5.6", + "version": "5.6.0", "license": "MIT", "dependencies": { "cluster-key-slot": "1.1.2" @@ -7449,6 +7449,18 @@ "node": ">= 18" } }, + "packages/redis/node_modules/@redis/client": { + "version": "5.5.6", + "resolved": "https://registry.npmjs.org/@redis/client/-/client-5.5.6.tgz", + "integrity": "sha512-M3Svdwt6oSfyfQdqEr0L2HOJH2vK7GgCFx1NfAQvpWAT4+ljoT1L5S5cKT3dA9NJrxrOPDkdoTPWJnIrGCOcmw==", + "license": "MIT", + "dependencies": { + "cluster-key-slot": "1.1.2" + }, + "engines": { + "node": ">= 18" + } + }, "packages/search": { "name": "@redis/search", "version": "5.5.6", diff --git a/packages/client/package.json b/packages/client/package.json index b95d1087d07..ee98d77ca1f 100644 --- a/packages/client/package.json +++ b/packages/client/package.json @@ -1,6 +1,6 @@ { "name": "@redis/client", - "version": "5.5.6", + "version": "5.6.0", "license": "MIT", "main": "./dist/index.js", "types": "./dist/index.d.ts", From b7a5f40ab154dd293f630af90f1370b2e6a836e2 Mon Sep 17 00:00:00 2001 From: GitHub Action Date: Tue, 8 Jul 2025 11:37:32 +0000 Subject: [PATCH 1643/1748] Release bloom@5.6.0 --- package-lock.json | 16 ++++++++++++++-- packages/bloom/package.json | 4 ++-- 2 files changed, 16 insertions(+), 4 deletions(-) diff --git a/package-lock.json b/package-lock.json index f17556e3a04..fb9d1d164ad 100644 --- a/package-lock.json +++ b/package-lock.json @@ -7337,7 +7337,7 @@ }, "packages/bloom": { "name": "@redis/bloom", - "version": "5.5.6", + "version": "5.6.0", "license": "MIT", "devDependencies": { "@redis/test-utils": "*" @@ -7346,7 +7346,7 @@ "node": ">= 18" }, "peerDependencies": { - "@redis/client": "^5.5.6" + "@redis/client": "^5.6.0" } }, "packages/client": { @@ -7449,6 +7449,18 @@ "node": ">= 18" } }, + "packages/redis/node_modules/@redis/bloom": { + "version": "5.5.6", + "resolved": "https://registry.npmjs.org/@redis/bloom/-/bloom-5.5.6.tgz", + "integrity": "sha512-bNR3mxkwtfuCxNOzfV8B3R5zA1LiN57EH6zK4jVBIgzMzliNuReZXBFGnXvsi80/SYohajn78YdpYI+XNpqL+A==", + "license": "MIT", + "engines": { + "node": ">= 18" + }, + "peerDependencies": { + "@redis/client": "^5.5.6" + } + }, "packages/redis/node_modules/@redis/client": { "version": "5.5.6", "resolved": "https://registry.npmjs.org/@redis/client/-/client-5.5.6.tgz", diff --git a/packages/bloom/package.json b/packages/bloom/package.json index 83dd6f893f9..47d1f6978ef 100644 --- a/packages/bloom/package.json +++ b/packages/bloom/package.json @@ -1,6 +1,6 @@ { "name": "@redis/bloom", - "version": "5.5.6", + "version": "5.6.0", "license": "MIT", "main": "./dist/lib/index.js", "types": "./dist/lib/index.d.ts", @@ -13,7 +13,7 @@ "release": "release-it" }, "peerDependencies": { - "@redis/client": "^5.5.6" + "@redis/client": "^5.6.0" }, "devDependencies": { "@redis/test-utils": "*" From dab595d8d9a70d3efe957e1ac06b6f6639ecb8d3 Mon Sep 17 00:00:00 2001 From: GitHub Action Date: Tue, 8 Jul 2025 11:37:38 +0000 Subject: [PATCH 1644/1748] Release json@5.6.0 --- package-lock.json | 16 ++++++++++++++-- packages/json/package.json | 4 ++-- 2 files changed, 16 insertions(+), 4 deletions(-) diff --git a/package-lock.json b/package-lock.json index fb9d1d164ad..cd844a0de51 100644 --- a/package-lock.json +++ b/package-lock.json @@ -7423,7 +7423,7 @@ }, "packages/json": { "name": "@redis/json", - "version": "5.5.6", + "version": "5.6.0", "license": "MIT", "devDependencies": { "@redis/test-utils": "*" @@ -7432,7 +7432,7 @@ "node": ">= 18" }, "peerDependencies": { - "@redis/client": "^5.5.6" + "@redis/client": "^5.6.0" } }, "packages/redis": { @@ -7473,6 +7473,18 @@ "node": ">= 18" } }, + "packages/redis/node_modules/@redis/json": { + "version": "5.5.6", + "resolved": "https://registry.npmjs.org/@redis/json/-/json-5.5.6.tgz", + "integrity": "sha512-AIsoe3SsGQagqAmSQHaqxEinm5oCWr7zxPWL90kKaEdLJ+zw8KBznf2i9oK0WUFP5pFssSQUXqnscQKe2amfDQ==", + "license": "MIT", + "engines": { + "node": ">= 18" + }, + "peerDependencies": { + "@redis/client": "^5.5.6" + } + }, "packages/search": { "name": "@redis/search", "version": "5.5.6", diff --git a/packages/json/package.json b/packages/json/package.json index 0d77a3bc6b1..edfdaec8efa 100644 --- a/packages/json/package.json +++ b/packages/json/package.json @@ -1,6 +1,6 @@ { "name": "@redis/json", - "version": "5.5.6", + "version": "5.6.0", "license": "MIT", "main": "./dist/lib/index.js", "types": "./dist/lib/index.d.ts", @@ -13,7 +13,7 @@ "release": "release-it" }, "peerDependencies": { - "@redis/client": "^5.5.6" + "@redis/client": "^5.6.0" }, "devDependencies": { "@redis/test-utils": "*" From ff5c6b3066a2e821a62fe617ec1032be456a90ec Mon Sep 17 00:00:00 2001 From: GitHub Action Date: Tue, 8 Jul 2025 11:37:44 +0000 Subject: [PATCH 1645/1748] Release search@5.6.0 --- package-lock.json | 16 ++++++++++++++-- packages/search/package.json | 4 ++-- 2 files changed, 16 insertions(+), 4 deletions(-) diff --git a/package-lock.json b/package-lock.json index cd844a0de51..2b21ffe23a9 100644 --- a/package-lock.json +++ b/package-lock.json @@ -7485,9 +7485,21 @@ "@redis/client": "^5.5.6" } }, + "packages/redis/node_modules/@redis/search": { + "version": "5.5.6", + "resolved": "https://registry.npmjs.org/@redis/search/-/search-5.5.6.tgz", + "integrity": "sha512-JSqasYqO0mVcHL7oxvbySRBBZYRYhFl3W7f0Da7BW8M/r0Z9wCiVrdjnN4/mKBpWZkoJT/iuisLUdPGhpKxBew==", + "license": "MIT", + "engines": { + "node": ">= 18" + }, + "peerDependencies": { + "@redis/client": "^5.5.6" + } + }, "packages/search": { "name": "@redis/search", - "version": "5.5.6", + "version": "5.6.0", "license": "MIT", "devDependencies": { "@redis/test-utils": "*" @@ -7496,7 +7508,7 @@ "node": ">= 18" }, "peerDependencies": { - "@redis/client": "^5.5.6" + "@redis/client": "^5.6.0" } }, "packages/test-utils": { diff --git a/packages/search/package.json b/packages/search/package.json index 30edac8003e..c876d7460c3 100644 --- a/packages/search/package.json +++ b/packages/search/package.json @@ -1,6 +1,6 @@ { "name": "@redis/search", - "version": "5.5.6", + "version": "5.6.0", "license": "MIT", "main": "./dist/lib/index.js", "types": "./dist/lib/index.d.ts", @@ -14,7 +14,7 @@ "release": "release-it" }, "peerDependencies": { - "@redis/client": "^5.5.6" + "@redis/client": "^5.6.0" }, "devDependencies": { "@redis/test-utils": "*" From a6193a770c141974d499bd35934d0cf880858b8f Mon Sep 17 00:00:00 2001 From: GitHub Action Date: Tue, 8 Jul 2025 11:37:51 +0000 Subject: [PATCH 1646/1748] Release time-series@5.6.0 --- package-lock.json | 16 ++++++++++++++-- packages/time-series/package.json | 4 ++-- 2 files changed, 16 insertions(+), 4 deletions(-) diff --git a/package-lock.json b/package-lock.json index 2b21ffe23a9..4686dcf150e 100644 --- a/package-lock.json +++ b/package-lock.json @@ -7497,6 +7497,18 @@ "@redis/client": "^5.5.6" } }, + "packages/redis/node_modules/@redis/time-series": { + "version": "5.5.6", + "resolved": "https://registry.npmjs.org/@redis/time-series/-/time-series-5.5.6.tgz", + "integrity": "sha512-jkpcgq3NOI3TX7xEAJ3JgesJTxAx7k0m6lNxNsYdEM8KOl+xj7GaB/0CbLkoricZDmFSEAz7ClA1iK9XkGHf+Q==", + "license": "MIT", + "engines": { + "node": ">= 18" + }, + "peerDependencies": { + "@redis/client": "^5.5.6" + } + }, "packages/search": { "name": "@redis/search", "version": "5.6.0", @@ -7577,7 +7589,7 @@ }, "packages/time-series": { "name": "@redis/time-series", - "version": "5.5.6", + "version": "5.6.0", "license": "MIT", "devDependencies": { "@redis/test-utils": "*" @@ -7586,7 +7598,7 @@ "node": ">= 18" }, "peerDependencies": { - "@redis/client": "^5.5.6" + "@redis/client": "^5.6.0" } } } diff --git a/packages/time-series/package.json b/packages/time-series/package.json index f90bc8f5f9b..762aea77661 100644 --- a/packages/time-series/package.json +++ b/packages/time-series/package.json @@ -1,6 +1,6 @@ { "name": "@redis/time-series", - "version": "5.5.6", + "version": "5.6.0", "license": "MIT", "main": "./dist/lib/index.js", "types": "./dist/lib/index.d.ts", @@ -13,7 +13,7 @@ "release": "release-it" }, "peerDependencies": { - "@redis/client": "^5.5.6" + "@redis/client": "^5.6.0" }, "devDependencies": { "@redis/test-utils": "*" From e5b2466da3fb3f1ff17ca58f7e96148e7045992b Mon Sep 17 00:00:00 2001 From: GitHub Action Date: Tue, 8 Jul 2025 11:37:56 +0000 Subject: [PATCH 1647/1748] Release entraid@5.6.0 --- package-lock.json | 4 ++-- packages/entraid/package.json | 4 ++-- 2 files changed, 4 insertions(+), 4 deletions(-) diff --git a/package-lock.json b/package-lock.json index 4686dcf150e..106d5dfb591 100644 --- a/package-lock.json +++ b/package-lock.json @@ -7367,7 +7367,7 @@ }, "packages/entraid": { "name": "@redis/entraid", - "version": "5.5.6", + "version": "5.6.0", "license": "MIT", "dependencies": { "@azure/identity": "^4.7.0", @@ -7386,7 +7386,7 @@ "node": ">= 18" }, "peerDependencies": { - "@redis/client": "^5.5.6" + "@redis/client": "^5.6.0" } }, "packages/entraid/node_modules/@types/node": { diff --git a/packages/entraid/package.json b/packages/entraid/package.json index 79641cdf9a9..173bd0a6c67 100644 --- a/packages/entraid/package.json +++ b/packages/entraid/package.json @@ -1,6 +1,6 @@ { "name": "@redis/entraid", - "version": "5.5.6", + "version": "5.6.0", "license": "MIT", "main": "./dist/index.js", "types": "./dist/index.d.ts", @@ -22,7 +22,7 @@ "@azure/msal-node": "^2.16.1" }, "peerDependencies": { - "@redis/client": "^5.5.6" + "@redis/client": "^5.6.0" }, "devDependencies": { "@types/express": "^4.17.21", From 4d886b88661a21be7b1ea4b44bd58c9ca19dffc1 Mon Sep 17 00:00:00 2001 From: GitHub Action Date: Tue, 8 Jul 2025 11:38:02 +0000 Subject: [PATCH 1648/1748] Release redis@5.6.0 --- package-lock.json | 72 ++++--------------------------------- packages/redis/package.json | 12 +++---- 2 files changed, 12 insertions(+), 72 deletions(-) diff --git a/package-lock.json b/package-lock.json index 106d5dfb591..b8ff84d8259 100644 --- a/package-lock.json +++ b/package-lock.json @@ -7436,79 +7436,19 @@ } }, "packages/redis": { - "version": "5.5.6", - "license": "MIT", - "dependencies": { - "@redis/bloom": "5.5.6", - "@redis/client": "5.5.6", - "@redis/json": "5.5.6", - "@redis/search": "5.5.6", - "@redis/time-series": "5.5.6" - }, - "engines": { - "node": ">= 18" - } - }, - "packages/redis/node_modules/@redis/bloom": { - "version": "5.5.6", - "resolved": "https://registry.npmjs.org/@redis/bloom/-/bloom-5.5.6.tgz", - "integrity": "sha512-bNR3mxkwtfuCxNOzfV8B3R5zA1LiN57EH6zK4jVBIgzMzliNuReZXBFGnXvsi80/SYohajn78YdpYI+XNpqL+A==", - "license": "MIT", - "engines": { - "node": ">= 18" - }, - "peerDependencies": { - "@redis/client": "^5.5.6" - } - }, - "packages/redis/node_modules/@redis/client": { - "version": "5.5.6", - "resolved": "https://registry.npmjs.org/@redis/client/-/client-5.5.6.tgz", - "integrity": "sha512-M3Svdwt6oSfyfQdqEr0L2HOJH2vK7GgCFx1NfAQvpWAT4+ljoT1L5S5cKT3dA9NJrxrOPDkdoTPWJnIrGCOcmw==", + "version": "5.6.0", "license": "MIT", "dependencies": { - "cluster-key-slot": "1.1.2" + "@redis/bloom": "5.6.0", + "@redis/client": "5.6.0", + "@redis/json": "5.6.0", + "@redis/search": "5.6.0", + "@redis/time-series": "5.6.0" }, "engines": { "node": ">= 18" } }, - "packages/redis/node_modules/@redis/json": { - "version": "5.5.6", - "resolved": "https://registry.npmjs.org/@redis/json/-/json-5.5.6.tgz", - "integrity": "sha512-AIsoe3SsGQagqAmSQHaqxEinm5oCWr7zxPWL90kKaEdLJ+zw8KBznf2i9oK0WUFP5pFssSQUXqnscQKe2amfDQ==", - "license": "MIT", - "engines": { - "node": ">= 18" - }, - "peerDependencies": { - "@redis/client": "^5.5.6" - } - }, - "packages/redis/node_modules/@redis/search": { - "version": "5.5.6", - "resolved": "https://registry.npmjs.org/@redis/search/-/search-5.5.6.tgz", - "integrity": "sha512-JSqasYqO0mVcHL7oxvbySRBBZYRYhFl3W7f0Da7BW8M/r0Z9wCiVrdjnN4/mKBpWZkoJT/iuisLUdPGhpKxBew==", - "license": "MIT", - "engines": { - "node": ">= 18" - }, - "peerDependencies": { - "@redis/client": "^5.5.6" - } - }, - "packages/redis/node_modules/@redis/time-series": { - "version": "5.5.6", - "resolved": "https://registry.npmjs.org/@redis/time-series/-/time-series-5.5.6.tgz", - "integrity": "sha512-jkpcgq3NOI3TX7xEAJ3JgesJTxAx7k0m6lNxNsYdEM8KOl+xj7GaB/0CbLkoricZDmFSEAz7ClA1iK9XkGHf+Q==", - "license": "MIT", - "engines": { - "node": ">= 18" - }, - "peerDependencies": { - "@redis/client": "^5.5.6" - } - }, "packages/search": { "name": "@redis/search", "version": "5.6.0", diff --git a/packages/redis/package.json b/packages/redis/package.json index bf5ea798e70..32251c7e501 100644 --- a/packages/redis/package.json +++ b/packages/redis/package.json @@ -1,7 +1,7 @@ { "name": "redis", "description": "A modern, high performance Redis client", - "version": "5.5.6", + "version": "5.6.0", "license": "MIT", "main": "./dist/index.js", "types": "./dist/index.d.ts", @@ -13,11 +13,11 @@ "release": "release-it" }, "dependencies": { - "@redis/bloom": "5.5.6", - "@redis/client": "5.5.6", - "@redis/json": "5.5.6", - "@redis/search": "5.5.6", - "@redis/time-series": "5.5.6" + "@redis/bloom": "5.6.0", + "@redis/client": "5.6.0", + "@redis/json": "5.6.0", + "@redis/search": "5.6.0", + "@redis/time-series": "5.6.0" }, "engines": { "node": ">= 18" From 987515c69b51bf4ed4eca718a38c7ed2ef49c9b4 Mon Sep 17 00:00:00 2001 From: Clo Junseo Kim Date: Thu, 10 Jul 2025 17:22:51 +0900 Subject: [PATCH 1649/1748] docs: fix hyperlink for lua scripts, functions (#3015) --- docs/clustering.md | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/docs/clustering.md b/docs/clustering.md index f335c259c24..6803583fa51 100644 --- a/docs/clustering.md +++ b/docs/clustering.md @@ -35,8 +35,8 @@ await cluster.close(); | maxCommandRedirections | `16` | The maximum number of times a command will be redirected due to `MOVED` or `ASK` errors | | nodeAddressMap | | Defines the [node address mapping](#node-address-map) | | modules | | Included [Redis Modules](../README.md#packages) | -| scripts | | Script definitions (see [Lua Scripts](../README.md#lua-scripts)) | -| functions | | Function definitions (see [Functions](../README.md#functions)) | +| scripts | | Script definitions (see [Lua Scripts](./programmability.md#lua-scripts)) | +| functions | | Function definitions (see [Functions](./programmability.md#functions)) | ## Auth with password and username From c21dd924fe8aa03c7abe1a7733a564fd3fc74011 Mon Sep 17 00:00:00 2001 From: Nikolay Karadzhov Date: Thu, 17 Jul 2025 14:02:22 +0300 Subject: [PATCH 1650/1748] fix(search): adjust field types for ft.search (#3018) In ft.search SORTBY, SUMMARIZE and HIGHLIGHT all take string arguments and do not need @ or $ as a prefix. In fact, if you put @ or $ there, redis returns error like this: [SimpleError: Property `@age` not loaded nor in schema] fixes #3017 --- packages/search/lib/commands/SEARCH.ts | 12 ++++++------ 1 file changed, 6 insertions(+), 6 deletions(-) diff --git a/packages/search/lib/commands/SEARCH.ts b/packages/search/lib/commands/SEARCH.ts index b8efda05777..61e1d8d84d2 100644 --- a/packages/search/lib/commands/SEARCH.ts +++ b/packages/search/lib/commands/SEARCH.ts @@ -1,7 +1,7 @@ import { CommandParser } from '@redis/client/dist/lib/client/parser'; import { RedisArgument, Command, ReplyUnion } from '@redis/client/dist/lib/RESP/types'; import { RedisVariadicArgument, parseOptionalVariadicArgument } from '@redis/client/dist/lib/commands/generic-transformers'; -import { RediSearchProperty, RediSearchLanguage } from './CREATE'; +import { RediSearchLanguage } from './CREATE'; import { DEFAULT_DIALECT } from '../dialect/default'; export type FtSearchParams = Record; @@ -32,13 +32,13 @@ export interface FtSearchOptions { INFIELDS?: RedisVariadicArgument; RETURN?: RedisVariadicArgument; SUMMARIZE?: boolean | { - FIELDS?: RediSearchProperty | Array; + FIELDS?: RedisArgument | Array; FRAGS?: number; LEN?: number; SEPARATOR?: RedisArgument; }; HIGHLIGHT?: boolean | { - FIELDS?: RediSearchProperty | Array; + FIELDS?: RedisArgument | Array; TAGS?: { open: RedisArgument; close: RedisArgument; @@ -51,7 +51,7 @@ export interface FtSearchOptions { EXPANDER?: RedisArgument; SCORER?: RedisArgument; SORTBY?: RedisArgument | { - BY: RediSearchProperty; + BY: RedisArgument; DIRECTION?: 'ASC' | 'DESC'; }; LIMIT?: { @@ -133,7 +133,7 @@ export function parseSearchOptions(parser: CommandParser, options?: FtSearchOpti if (options?.SORTBY) { parser.push('SORTBY'); - + if (typeof options.SORTBY === 'string' || options.SORTBY instanceof Buffer) { parser.push(options.SORTBY); } else { @@ -193,7 +193,7 @@ export default { value: withoutDocuments ? Object.create(null) : documentValue(reply[i++]) }); } - + return { total: reply[0], documents From 539fe52236d54b75a9413e8b97b70908fa98b4f4 Mon Sep 17 00:00:00 2001 From: Nikolay Karadzhov Date: Mon, 21 Jul 2025 18:17:07 +0300 Subject: [PATCH 1651/1748] fix(client): make socket.host not required (#3024) Underlying node tls.ConnectionOptions does not require host, so we shouldnt as well. Further, if `url` is provided in the upper level config, it takes precedence, which could be misleading: createClient({ url: 'rediss://user:secret@localhost:6379/0', socket: { tls: true, host: 'somehost' <-- this gets overwritten to `localhost` } }); fixes #3023 --- packages/client/lib/client/socket.ts | 7 +++---- 1 file changed, 3 insertions(+), 4 deletions(-) diff --git a/packages/client/lib/client/socket.ts b/packages/client/lib/client/socket.ts index 58ccbe0b0c5..5f0bcc44929 100644 --- a/packages/client/lib/client/socket.ts +++ b/packages/client/lib/client/socket.ts @@ -38,7 +38,6 @@ type RedisTcpOptions = RedisSocketOptionsCommon & NetOptions & Omit< type RedisTlsOptions = RedisSocketOptionsCommon & tls.ConnectionOptions & { tls: true; - host: string; } type RedisIpcOptions = RedisSocketOptionsCommon & Omit< @@ -238,7 +237,7 @@ export default class RedisSocket extends EventEmitter { } } while (this.#isOpen && !this.#isReady); } - + async #createSocket(): Promise { const socket = this.#socketFactory.create(); @@ -293,7 +292,7 @@ export default class RedisSocket extends EventEmitter { write(iterable: Iterable>) { if (!this.#socket) return; - + this.#socket.cork(); for (const args of iterable) { for (const toWrite of args) { @@ -364,7 +363,7 @@ export default class RedisSocket extends EventEmitter { const jitter = Math.floor(Math.random() * 200); // Delay is an exponential back off, (times^2) * 50 ms, with a maximum value of 2000 ms: const delay = Math.min(Math.pow(2, retries) * 50, 2000); - + return delay + jitter; } } From ddd2cc5185dc3a0cdb36c84046f5a1ab7dc682eb Mon Sep 17 00:00:00 2001 From: Nikolay Karadzhov Date: Wed, 23 Jul 2025 13:41:30 +0300 Subject: [PATCH 1652/1748] fix(client): export RedisJSON type (#3026) fixes #3014 --- packages/json/lib/index.ts | 1 + 1 file changed, 1 insertion(+) diff --git a/packages/json/lib/index.ts b/packages/json/lib/index.ts index bc0e103e8c8..1993f9ef42d 100644 --- a/packages/json/lib/index.ts +++ b/packages/json/lib/index.ts @@ -1 +1,2 @@ export { default } from './commands'; +export type { RedisJSON } from './commands'; From 0541b32f34045faaff8538072d63cafbede5a4fc Mon Sep 17 00:00:00 2001 From: Pavel Pashov <60297174+PavelPashov@users.noreply.github.com> Date: Wed, 23 Jul 2025 18:16:08 +0300 Subject: [PATCH 1653/1748] docs: Update doctests (#3020) * move all doctests from emb-examples branch * fix readme * add package-lock.json * --wip-- [skip ci] * fix: replace client.quit() with client.close() as quit is deprecated - doctests/cmds-hash.js - doctests/cmds-list.js - doctests/cmds-servermgmt.js - doctests/cmds-set.js * fix: replace client.quit() with client.close() as quit is deprecated - doctests/cmds-sorted-set.js - doctests/cmds-string.js - doctests/dt-bitfield.js - doctests/dt-bitmap.js * fix: replace client.quit() with client.close() as quit is deprecated - dt-bloom.js: replace client.quit() with client.close() - dt-cms.js: replace client.quit() with client.close() - dt-cuckoo.js: replace client.quit() with client.close() and update expected output comments to reflect v5 boolean returns - dt-geo.js: replace client.quit() with client.close() * fix(doctests): correct pfAdd return values and replace quit with close - Fix dt-hll.js: pfAdd returns 1 instead of true in comments and assertions - Fix dt-hash.js and dt-hll.js: replace deprecated client.quit() with client.close() * fix(doctests): correct API usage and return values in json and list examples - Fix dt-json.js: use options object for json.type, json.strLen, json.del, json.arrPop, json.objLen, json.objKeys - Fix dt-json.js: correct json.del return value from [1] to 1 - Fix dt-list.js: correct client initialization, return values (null, OK, 1), and error type - Replace deprecated client.quit() with client.close() in both files * fix(doctests): update dt-set.js and dt-ss.js for v5 compliance - Updated boolean return values to numbers for SISMEMBER and SMISMEMBER commands - Fixed client lifecycle to use client.close() instead of client.quit() - Removed unnecessary await from createClient() - Added order-independent assertions for set operations - Removed debug statement * fix(doctests): update deprecated methods and imports for v5 compliance - Fix dt-string.js: remove await from client creation and replace client.quit() with client.close() - Fix dt-tdigest.js: replace deprecated client.quit() with client.close() - Fix dt-topk.js: replace client.quit() with client.close() and fix output comment from [1, 0] to [true, false] - Fix query-agg.js: update @redis/search imports to use new constant names and replace client.disconnect() with client.close() * fix(doctests): update imports and replace deprecated disconnect with close - Replace SchemaFieldTypes/VectorAlgorithms with SCHEMA_FIELD_TYPE/SCHEMA_VECTOR_FIELD_ALGORITHM - Replace client.disconnect() with client.close() for consistent deprecation handling - Update query-combined.js, query-em.js, query-ft.js, and query-geo.js * fix(doctests): update imports and replace deprecated methods in remaining files - Update imports to use SCHEMA_FIELD_TYPE and SCHEMA_VECTOR_FIELD_ALGORITHM constants - Replace deprecated disconnect() and quit() methods with close() - Fix assertion in search-quickstart.js to use correct bicycle ID * fix(doctests): update cmds-generic.js and cmds-cnxmgmt.js for v5 compliance - Replace deprecated client.quit() with client.close() - Update sScanIterator to use collection-yielding behavior (value -> values) - Fix HSCAN API changes: tuples renamed to entries - Fix cursor type issues: use string '0' instead of number 0 for hScan - Fix infinite loop in scan cleanup by using do-while pattern * fix(doctests): update dt-streams.js object shapes and parameters for v5 compliance - Update stream result objects from tuple format to proper object format with id/message properties - Change xRead/xReadGroup results from nested arrays to objects with name/messages structure - Update xAutoClaim results to use nextId, messages, and deletedMessages properties - Add missing properties to xInfo* results (max-deleted-entry-id, entries-added, recorded-first-entry-id, entries-read, lag, inactive) - Modernize parameter names (count -> COUNT, block -> BLOCK, etc.) - Update MAXLEN/APPROXIMATE options to new TRIM object structure - Fix error message format for XADD duplicate ID error - Update boolean return values (True -> OK) --------- Co-authored-by: Nikolay Karadzhov --- doctests/README.md | 32 + doctests/cmds-cnxmgmt.js | 49 + doctests/cmds-generic.js | 195 ++ doctests/cmds-hash.js | 109 + doctests/cmds-list.js | 129 + doctests/cmds-servermgmt.js | 45 + doctests/cmds-set.js | 44 + doctests/cmds-sorted-set.js | 115 + doctests/cmds-string.js | 27 + doctests/data/query_em.json | 92 + doctests/data/query_vector.json | 3952 ++++++++++++++++++++++++++ doctests/dt-bitfield.js | 76 + doctests/dt-bitmap.js | 39 + doctests/dt-bloom.js | 46 + doctests/dt-cms.js | 50 + doctests/dt-cuckoo.js | 38 + doctests/dt-geo.js | 59 + doctests/dt-hash.js | 98 + doctests/dt-hll.js | 38 + doctests/dt-json.js | 425 +++ doctests/dt-list.js | 329 +++ doctests/dt-set.js | 176 ++ doctests/dt-ss.js | 162 ++ doctests/dt-streams.js | 366 +++ doctests/dt-string.js | 68 + doctests/dt-tdigest.js | 85 + doctests/dt-topk.js | 48 + doctests/package-lock.json | 889 ++++++ doctests/package.json | 12 + doctests/query-agg.js | 139 + doctests/query-combined.js | 191 ++ doctests/query-em.js | 121 + doctests/query-ft.js | 84 + doctests/query-geo.js | 82 + doctests/query-range.js | 98 + doctests/query-vector.js | 110 + doctests/run_examples.sh | 15 + doctests/search-quickstart.js | 231 ++ doctests/string-set-get-example.js | 27 + packages/client/lib/commands/SCAN.ts | 14 +- 40 files changed, 8899 insertions(+), 6 deletions(-) create mode 100644 doctests/README.md create mode 100644 doctests/cmds-cnxmgmt.js create mode 100644 doctests/cmds-generic.js create mode 100644 doctests/cmds-hash.js create mode 100644 doctests/cmds-list.js create mode 100644 doctests/cmds-servermgmt.js create mode 100644 doctests/cmds-set.js create mode 100644 doctests/cmds-sorted-set.js create mode 100644 doctests/cmds-string.js create mode 100644 doctests/data/query_em.json create mode 100644 doctests/data/query_vector.json create mode 100644 doctests/dt-bitfield.js create mode 100644 doctests/dt-bitmap.js create mode 100644 doctests/dt-bloom.js create mode 100644 doctests/dt-cms.js create mode 100644 doctests/dt-cuckoo.js create mode 100644 doctests/dt-geo.js create mode 100644 doctests/dt-hash.js create mode 100644 doctests/dt-hll.js create mode 100644 doctests/dt-json.js create mode 100644 doctests/dt-list.js create mode 100644 doctests/dt-set.js create mode 100644 doctests/dt-ss.js create mode 100644 doctests/dt-streams.js create mode 100644 doctests/dt-string.js create mode 100644 doctests/dt-tdigest.js create mode 100644 doctests/dt-topk.js create mode 100644 doctests/package-lock.json create mode 100644 doctests/package.json create mode 100644 doctests/query-agg.js create mode 100644 doctests/query-combined.js create mode 100644 doctests/query-em.js create mode 100644 doctests/query-ft.js create mode 100644 doctests/query-geo.js create mode 100644 doctests/query-range.js create mode 100644 doctests/query-vector.js create mode 100755 doctests/run_examples.sh create mode 100644 doctests/search-quickstart.js create mode 100644 doctests/string-set-get-example.js diff --git a/doctests/README.md b/doctests/README.md new file mode 100644 index 00000000000..59d1cb0364c --- /dev/null +++ b/doctests/README.md @@ -0,0 +1,32 @@ +# Command examples for redis.io + +## Setup + +To set up the examples folder so that you can run an example / develop one of your own: + +``` +$ git clone https://github.com/redis/node-redis.git +$ cd node-redis +$ npm install -ws && npm run build +$ cd doctests +$ npm install +``` + +## How to add examples + +Create regular node file in the current folder with meaningful name. It makes sense prefix example files with +command category (e.g. string, set, list, hash, etc) to make navigation in the folder easier. + +### Special markup + +See https://github.com/redis-stack/redis-stack-website#readme for more details. + +## How to test the examples + +Just include necessary assertions in the example file and run +```bash +sh doctests/run_examples.sh +``` +to test all examples in the current folder. + +See `tests.js` for more details. diff --git a/doctests/cmds-cnxmgmt.js b/doctests/cmds-cnxmgmt.js new file mode 100644 index 00000000000..8b616b6f105 --- /dev/null +++ b/doctests/cmds-cnxmgmt.js @@ -0,0 +1,49 @@ +// EXAMPLE: cmds_cnxmgmt +// REMOVE_START +import assert from "node:assert"; +// REMOVE_END + +// HIDE_START +import { createClient } from 'redis'; + +const client = createClient(); +await client.connect().catch(console.error); +// HIDE_END + +// STEP_START auth1 +// REMOVE_START +await client.sendCommand(['CONFIG', 'SET', 'requirepass', 'temp_pass']); +// REMOVE_END +const res1 = await client.auth({ password: 'temp_pass' }); +console.log(res1); // OK + +const res2 = await client.auth({ username: 'default', password: 'temp_pass' }); +console.log(res2); // OK + +// REMOVE_START +assert.equal(res1, "OK"); +assert.equal(res2, "OK"); +await client.sendCommand(['CONFIG', 'SET', 'requirepass', '']); +// REMOVE_END +// STEP_END + +// STEP_START auth2 +// REMOVE_START +await client.sendCommand([ + 'ACL', 'SETUSER', 'test-user', + 'on', '>strong_password', '+acl' +]); +// REMOVE_END +const res3 = await client.auth({ username: 'test-user', password: 'strong_password' }); +console.log(res3); // OK + +// REMOVE_START +assert.equal(res3, "OK"); +await client.auth({ username: 'default', password: '' }) +await client.sendCommand(['ACL', 'DELUSER', 'test-user']); +// REMOVE_END +// STEP_END + +// HIDE_START +await client.close(); +// HIDE_END diff --git a/doctests/cmds-generic.js b/doctests/cmds-generic.js new file mode 100644 index 00000000000..50329ede460 --- /dev/null +++ b/doctests/cmds-generic.js @@ -0,0 +1,195 @@ +// EXAMPLE: cmds_generic +// REMOVE_START +import assert from "node:assert"; +// REMOVE_END + +// HIDE_START +import { createClient } from 'redis'; + +const client = createClient(); +await client.connect().catch(console.error); +// HIDE_END + +// STEP_START del +const delRes1 = await client.set('key1', 'Hello'); +console.log(delRes1); // OK + +const delRes2 = await client.set('key2', 'World'); +console.log(delRes2); // OK + +const delRes3 = await client.del(['key1', 'key2', 'key3']); +console.log(delRes3); // 2 +// REMOVE_START +assert.equal(delRes3, 2); +// REMOVE_END +// STEP_END + +// STEP_START expire +const expireRes1 = await client.set('mykey', 'Hello'); +console.log(expireRes1); // OK + +const expireRes2 = await client.expire('mykey', 10); +console.log(expireRes2); // 1 + +const expireRes3 = await client.ttl('mykey'); +console.log(expireRes3); // 10 +// REMOVE_START +assert.equal(expireRes3, 10); +// REMOVE_END + +const expireRes4 = await client.set('mykey', 'Hello World'); +console.log(expireRes4); // OK + +const expireRes5 = await client.ttl('mykey'); +console.log(expireRes5); // -1 +// REMOVE_START +assert.equal(expireRes5, -1); +// REMOVE_END + +const expireRes6 = await client.expire('mykey', 10, "XX"); +console.log(expireRes6); // 0 +// REMOVE_START +assert.equal(expireRes6, 0) +// REMOVE_END + +const expireRes7 = await client.ttl('mykey'); +console.log(expireRes7); // -1 +// REMOVE_START +assert.equal(expireRes7, -1); +// REMOVE_END + +const expireRes8 = await client.expire('mykey', 10, "NX"); +console.log(expireRes8); // 1 +// REMOVE_START +assert.equal(expireRes8, 1); +// REMOVE_END + +const expireRes9 = await client.ttl('mykey'); +console.log(expireRes9); // 10 +// REMOVE_START +assert.equal(expireRes9, 10); +await client.del('mykey'); +// REMOVE_END +// STEP_END + +// STEP_START ttl +const ttlRes1 = await client.set('mykey', 'Hello'); +console.log(ttlRes1); // OK + +const ttlRes2 = await client.expire('mykey', 10); +console.log(ttlRes2); // 1 + +const ttlRes3 = await client.ttl('mykey'); +console.log(ttlRes3); // 10 +// REMOVE_START +assert.equal(ttlRes3, 10); +await client.del('mykey'); +// REMOVE_END +// STEP_END + +// STEP_START scan1 +const scan1Res1 = await client.sAdd('myset', ['1', '2', '3', 'foo', 'foobar', 'feelsgood']); +console.log(scan1Res1); // 6 + +let scan1Res2 = []; +for await (const values of client.sScanIterator('myset', { MATCH: 'f*' })) { + scan1Res2 = scan1Res2.concat(values); +} +console.log(scan1Res2); // ['foo', 'foobar', 'feelsgood'] +// REMOVE_START +console.assert(scan1Res2.sort().toString() === ['foo', 'foobar', 'feelsgood'].sort().toString()); +await client.del('myset'); +// REMOVE_END +// STEP_END + +// STEP_START scan2 +// REMOVE_START +for (let i = 1; i <= 1000; i++) { + await client.set(`key:${i}`, i); +} +// REMOVE_END +let cursor = '0'; +let scanResult; + +scanResult = await client.scan(cursor, { MATCH: '*11*' }); +console.log(scanResult.cursor, scanResult.keys); + +scanResult = await client.scan(scanResult.cursor, { MATCH: '*11*' }); +console.log(scanResult.cursor, scanResult.keys); + +scanResult = await client.scan(scanResult.cursor, { MATCH: '*11*' }); +console.log(scanResult.cursor, scanResult.keys); + +scanResult = await client.scan(scanResult.cursor, { MATCH: '*11*' }); +console.log(scanResult.cursor, scanResult.keys); + +scanResult = await client.scan(scanResult.cursor, { MATCH: '*11*', COUNT: 1000 }); +console.log(scanResult.cursor, scanResult.keys); +// REMOVE_START +console.assert(scanResult.keys.length === 18); +cursor = '0'; +const prefix = 'key:*'; +do { + scanResult = await client.scan(cursor, { MATCH: prefix, COUNT: 1000 }); + console.log(scanResult.cursor, scanResult.keys); + cursor = scanResult.cursor; + const keys = scanResult.keys; + if (keys.length) { + await client.del(keys); + } +} while (cursor !== '0'); +// REMOVE_END +// STEP_END + +// STEP_START scan3 +const scan3Res1 = await client.geoAdd('geokey', { longitude: 0, latitude: 0, member: 'value' }); +console.log(scan3Res1); // 1 + +const scan3Res2 = await client.zAdd('zkey', [{ score: 1000, value: 'value' }]); +console.log(scan3Res2); // 1 + +const scan3Res3 = await client.type('geokey'); +console.log(scan3Res3); // zset +// REMOVE_START +console.assert(scan3Res3 === 'zset'); +// REMOVE_END + +const scan3Res4 = await client.type('zkey'); +console.log(scan3Res4); // zset +// REMOVE_START +console.assert(scan3Res4 === 'zset'); +// REMOVE_END + +const scan3Res5 = await client.scan('0', { TYPE: 'zset' }); +console.log(scan3Res5.keys); // ['zkey', 'geokey'] +// REMOVE_START +console.assert(scan3Res5.keys.sort().toString() === ['zkey', 'geokey'].sort().toString()); +await client.del(['geokey', 'zkey']); +// REMOVE_END +// STEP_END + +// STEP_START scan4 +const scan4Res1 = await client.hSet('myhash', { a: 1, b: 2 }); +console.log(scan4Res1); // 2 + +const scan4Res2 = await client.hScan('myhash', '0'); +console.log(scan4Res2.entries); // [{field: 'a', value: '1'}, {field: 'b', value: '2'}] +// REMOVE_START +assert.deepEqual(scan4Res2.entries, [ + { field: 'a', value: '1' }, + { field: 'b', value: '2' } +]); +// REMOVE_END + +const scan4Res3 = await client.hScan('myhash', '0', { COUNT: 10 }); +const items = scan4Res3.entries.map((item) => item.field) +console.log(items); // ['a', 'b'] +// REMOVE_START +assert.deepEqual(items, ['a', 'b']) +await client.del('myhash'); +// REMOVE_END +// STEP_END + +// HIDE_START +await client.close(); +// HIDE_END diff --git a/doctests/cmds-hash.js b/doctests/cmds-hash.js new file mode 100644 index 00000000000..8ce29785763 --- /dev/null +++ b/doctests/cmds-hash.js @@ -0,0 +1,109 @@ +// EXAMPLE: cmds_hash +// HIDE_START +import assert from 'node:assert'; +import { createClient } from 'redis'; + +const client = createClient(); +await client.connect().catch(console.error); +// HIDE_END + +// STEP_START hset +const res1 = await client.hSet('myhash', 'field1', 'Hello') +console.log(res1) // 1 + +const res2 = await client.hGet('myhash', 'field1') +console.log(res2) // Hello + +const res3 = await client.hSet( + 'myhash', + { + 'field2': 'Hi', + 'field3': 'World' + } +) +console.log(res3) // 2 + +const res4 = await client.hGet('myhash', 'field2') +console.log(res4) // Hi + +const res5 = await client.hGet('myhash', 'field3') +console.log(res5) // World + +const res6 = await client.hGetAll('myhash') +console.log(res6) + +// REMOVE_START +assert.equal(res1, 1); +assert.equal(res2, 'Hello'); +assert.equal(res3, 2); +assert.equal(res4, 'Hi'); +assert.equal(res5, 'World'); +assert.deepEqual(res6, { + field1: 'Hello', + field2: 'Hi', + field3: 'World' +}); +await client.del('myhash') +// REMOVE_END +// STEP_END + +// STEP_START hget +const res7 = await client.hSet('myhash', 'field1', 'foo') +console.log(res7) // 1 + +const res8 = await client.hGet('myhash', 'field1') +console.log(res8) // foo + +const res9 = await client.hGet('myhash', 'field2') +console.log(res9) // null + +// REMOVE_START +assert.equal(res7, 1); +assert.equal(res8, 'foo'); +assert.equal(res9, null); +await client.del('myhash') +// REMOVE_END +// STEP_END + +// STEP_START hgetall +const res10 = await client.hSet( + 'myhash', + { + 'field1': 'Hello', + 'field2': 'World' + } +) + +const res11 = await client.hGetAll('myhash') +console.log(res11) // [Object: null prototype] { field1: 'Hello', field2: 'World' } + +// REMOVE_START +assert.deepEqual(res11, { + field1: 'Hello', + field2: 'World' +}); +await client.del('myhash') +// REMOVE_END +// STEP_END + +// STEP_START hvals +const res12 = await client.hSet( + 'myhash', + { + 'field1': 'Hello', + 'field2': 'World' + } +) + +const res13 = await client.hVals('myhash') +console.log(res13) // [ 'Hello', 'World' ] + +// REMOVE_START +assert.deepEqual(res13, [ 'Hello', 'World' ]); +await client.del('myhash') +// REMOVE_END +// STEP_END + +// HIDE_START +await client.close(); +// HIDE_END diff --git a/doctests/cmds-list.js b/doctests/cmds-list.js new file mode 100644 index 00000000000..9d1b4154dfe --- /dev/null +++ b/doctests/cmds-list.js @@ -0,0 +1,129 @@ +// EXAMPLE: cmds_list +// HIDE_START +import assert from 'node:assert'; +import { createClient } from 'redis'; + +const client = createClient(); +await client.connect().catch(console.error); +// HIDE_END + +// STEP_START lpush +const res1 = await client.lPush('mylist', 'world'); +console.log(res1); // 1 + +const res2 = await client.lPush('mylist', 'hello'); +console.log(res2); // 2 + +const res3 = await client.lRange('mylist', 0, -1); +console.log(res3); // [ 'hello', 'world' ] + +// REMOVE_START +assert.deepEqual(res3, [ 'hello', 'world' ]); +await client.del('mylist'); +// REMOVE_END +// STEP_END + +// STEP_START lrange +const res4 = await client.rPush('mylist', 'one'); +console.log(res4); // 1 + +const res5 = await client.rPush('mylist', 'two'); +console.log(res5); // 2 + +const res6 = await client.rPush('mylist', 'three'); +console.log(res6); // 3 + +const res7 = await client.lRange('mylist', 0, 0); +console.log(res7); // [ 'one' ] + +const res8 = await client.lRange('mylist', -3, 2); +console.log(res8); // [ 'one', 'two', 'three' ] + +const res9 = await client.lRange('mylist', -100, 100); +console.log(res9); // [ 'one', 'two', 'three' ] + +const res10 = await client.lRange('mylist', 5, 10); +console.log(res10); // [] + +// REMOVE_START +assert.deepEqual(res7, [ 'one' ]); +assert.deepEqual(res8, [ 'one', 'two', 'three' ]); +assert.deepEqual(res9, [ 'one', 'two', 'three' ]); +assert.deepEqual(res10, []); +await client.del('mylist'); +// REMOVE_END +// STEP_END + +// STEP_START llen +const res11 = await client.lPush('mylist', 'World'); +console.log(res11); // 1 + +const res12 = await client.lPush('mylist', 'Hello'); +console.log(res12); // 2 + +const res13 = await client.lLen('mylist'); +console.log(res13); // 2 + +// REMOVE_START +assert.equal(res13, 2); +await client.del('mylist'); +// REMOVE_END +// STEP_END + +// STEP_START rpush +const res14 = await client.rPush('mylist', 'hello'); +console.log(res14); // 1 + +const res15 = await client.rPush('mylist', 'world'); +console.log(res15); // 2 + +const res16 = await client.lRange('mylist', 0, -1); +console.log(res16); // [ 'hello', 'world' ] + +// REMOVE_START +assert.deepEqual(res16, [ 'hello', 'world' ]); +await client.del('mylist'); +// REMOVE_END +// STEP_END + +// STEP_START lpop +const res17 = await client.rPush('mylist', ["one", "two", "three", "four", "five"]); +console.log(res17); // 5 + +const res18 = await client.lPop('mylist'); +console.log(res18); // 'one' + +const res19 = await client.lPopCount('mylist', 2); +console.log(res19); // [ 'two', 'three' ] + +const res20 = await client.lRange('mylist', 0, -1); +console.log(res20); // [ 'four', 'five' ] + +// REMOVE_START +assert.deepEqual(res20, [ 'four', 'five' ]); +await client.del('mylist'); +// REMOVE_END +// STEP_END + +// STEP_START rpop +const res21 = await client.rPush('mylist', ["one", "two", "three", "four", "five"]); +console.log(res21); // 5 + +const res22 = await client.rPop('mylist'); +console.log(res22); // 'five' + +const res23 = await client.rPopCount('mylist', 2); +console.log(res23); // [ 'four', 'three' ] + +const res24 = await client.lRange('mylist', 0, -1); +console.log(res24); // [ 'one', 'two' ] + +// REMOVE_START +assert.deepEqual(res24, [ 'one', 'two' ]); +await client.del('mylist'); +// REMOVE_END +// STEP_END + +// HIDE_START +await client.close(); +// HIDE_END diff --git a/doctests/cmds-servermgmt.js b/doctests/cmds-servermgmt.js new file mode 100644 index 00000000000..0a53e05919d --- /dev/null +++ b/doctests/cmds-servermgmt.js @@ -0,0 +1,45 @@ +// EXAMPLE: cmds_servermgmt +// REMOVE_START +import assert from 'node:assert'; +// REMOVE_END + +// HIDE_START +import { createClient } from 'redis'; + +const client = createClient(); +await client.connect().catch(console.error); +// HIDE_END + +// STEP_START flushall +// REMOVE_START +await client.set('foo', '1'); +await client.set('bar', '2'); +await client.set('baz', '3'); +// REMOVE_END +const res1 = await client.flushAll('SYNC'); // or ASYNC +console.log(res1); // OK + +const res2 = await client.keys('*'); +console.log(res2); // [] + +// REMOVE_START +assert.equal(res1, 'OK'); +assert.deepEqual(res2, []); +// REMOVE_END +// STEP_END + +// STEP_START info +const res3 = await client.info(); +console.log(res3) +// # Server +// redis_version:7.4.0 +// redis_git_sha1:c9d29f6a +// redis_git_dirty:0 +// redis_build_id:4c367a16e3f9616 +// redis_mode:standalone +// ... +// STEP_END + +// HIDE_START +await client.close(); +// HIDE_END diff --git a/doctests/cmds-set.js b/doctests/cmds-set.js new file mode 100644 index 00000000000..8a9a3837036 --- /dev/null +++ b/doctests/cmds-set.js @@ -0,0 +1,44 @@ +// EXAMPLE: cmds_set +// REMOVE_START +import assert from 'node:assert'; +// REMOVE_END + +// HIDE_START +import { createClient } from 'redis'; + +const client = createClient(); +await client.connect().catch(console.error); +// HIDE_END + +// STEP_START sadd +const res1 = await client.sAdd('myset', ['Hello', 'World']); +console.log(res1); // 2 + +const res2 = await client.sAdd('myset', ['World']); +console.log(res2); // 0 + +const res3 = await client.sMembers('myset') +console.log(res3); // ['Hello', 'World'] + +// REMOVE_START +assert.deepEqual(res3, ['Hello', 'World']); +await client.del('myset'); +// REMOVE_END +// STEP_END + +// STEP_START smembers +const res4 = await client.sAdd('myset', ['Hello', 'World']); +console.log(res4); // 2 + +const res5 = await client.sMembers('myset') +console.log(res5); // ['Hello', 'World'] + +// REMOVE_START +assert.deepEqual(res5, ['Hello', 'World']); +await client.del('myset'); +// REMOVE_END +// STEP_END + +// HIDE_START +await client.close(); +// HIDE_END diff --git a/doctests/cmds-sorted-set.js b/doctests/cmds-sorted-set.js new file mode 100644 index 00000000000..b718938cc2b --- /dev/null +++ b/doctests/cmds-sorted-set.js @@ -0,0 +1,115 @@ +// EXAMPLE: cmds_sorted_set +// REMOVE_START +import assert from "node:assert"; +// REMOVE_END + +// HIDE_START +import { createClient } from 'redis'; + +const client = createClient(); +client.on('error', err => console.log('Redis Client Error', err)); +await client.connect().catch(console.error); +// HIDE_END + +// STEP_START zadd +const val1 = await client.zAdd("myzset", [{ value: 'one', score: 1 }]); +console.log(val1); +// returns 1 + +const val2 = await client.zAdd("myzset", [{ value: 'uno', score: 1 }]); +console.log(val2); +// returns 1 + +const val3 = await client.zAdd("myzset", [{ value: 'two', score: 2 }, { value: 'three', score: 3 }]); +console.log(val3); +// returns 2 + +const val4 = await client.zRangeWithScores("myzset", 0, -1); +console.log(val4); +// returns [{value: 'one', score: 1}, {value: 'uno', score: 1}, {value: 'two', score: 2}, {value: 'three', score: 3} ] + +// REMOVE_START +assert.equal(val1, 1); +assert.equal(val2, 1); +assert.equal(val3, 2); +assert.deepEqual(val4, [ + { value: 'one', score: 1 }, + { value: 'uno', score: 1 }, + { value: 'two', score: 2 }, + { value: 'three', score: 3 } +]); +await client.del('myzset'); +// REMOVE_END +// STEP_END + +// STEP_START zrange1 +const val5 = await client.zAdd("myzset", [ + { value: 'one', score: 1 }, + { value: 'two', score: 2 }, + { value: 'three', score: 3 } +]); +console.log(val5); +// returns 3 + +const val6 = await client.zRange('myzset', 0, -1); +console.log(val6); +// returns ['one', 'two', 'three'] +// REMOVE_START +console.assert(JSON.stringify(val6) === JSON.stringify(['one', 'two', 'three'])); +// REMOVE_END + +const val7 = await client.zRange('myzset', 2, 3); +console.log(val7); +// returns ['three'] +// REMOVE_START +console.assert(JSON.stringify(val7) === JSON.stringify(['three'])); +// REMOVE_END + +const val8 = await client.zRange('myzset', -2, -1); +console.log(val8); +// returns ['two', 'three'] +// REMOVE_START +console.assert(JSON.stringify(val8) === JSON.stringify(['two', 'three'])); +await client.del('myzset'); +// REMOVE_END +// STEP_END + +// STEP_START zrange2 +const val9 = await client.zAdd("myzset", [ + { value: 'one', score: 1 }, + { value: 'two', score: 2 }, + { value: 'three', score: 3 } +]); +console.log(val9); +// returns 3 + +const val10 = await client.zRangeWithScores('myzset', 0, 1); +console.log(val10); +// returns [{value: 'one', score: 1}, {value: 'two', score: 2}] +// REMOVE_START +console.assert(JSON.stringify(val10) === JSON.stringify([{value: 'one', score: 1}, {value: 'two', score: 2}])); +await client.del('myzset'); +// REMOVE_END +// STEP_END + +// STEP_START zrange3 +const val11 = await client.zAdd("myzset", [ + { value: 'one', score: 1 }, + { value: 'two', score: 2 }, + { value: 'three', score: 3 } +]); +console.log(val11); +// returns 3 + +const val12 = await client.zRange('myzset', 2, 3, { BY: 'SCORE', LIMIT: { offset: 1, count: 1 } }); +console.log(val12); +// >>> ['three'] +// REMOVE_START +console.assert(JSON.stringify(val12) === JSON.stringify(['three'])); +await client.del('myzset'); +// REMOVE_END +// STEP_END + +// HIDE_START +await client.close(); +// HIDE_END diff --git a/doctests/cmds-string.js b/doctests/cmds-string.js new file mode 100644 index 00000000000..00b3f738fa3 --- /dev/null +++ b/doctests/cmds-string.js @@ -0,0 +1,27 @@ +// EXAMPLE: cmds_string +// REMOVE_START +import assert from "node:assert"; +// REMOVE_END + +// HIDE_START +import { createClient } from 'redis'; + +const client = createClient(); +client.on('error', err => console.log('Redis Client Error', err)); +await client.connect().catch(console.error); +// HIDE_END + +// STEP_START incr +await client.set("mykey", "10"); +const value1 = await client.incr("mykey"); +console.log(value1); +// returns 11 +// REMOVE_START +assert.equal(value1, 11); +await client.del('mykey'); +// REMOVE_END +// STEP_END + +// HIDE_START +await client.close(); +// HIDE_END diff --git a/doctests/data/query_em.json b/doctests/data/query_em.json new file mode 100644 index 00000000000..bcf908aaf85 --- /dev/null +++ b/doctests/data/query_em.json @@ -0,0 +1,92 @@ +[ + { + "pickup_zone": "POLYGON((-74.0610 40.7578, -73.9510 40.7578, -73.9510 40.6678, -74.0610 40.6678, -74.0610 40.7578))", + "store_location": "-74.0060,40.7128", + "brand": "Velorim", + "model": "Jigger", + "price": 270, + "description": "Small and powerful, the Jigger is the best ride for the smallest of tikes! This is the tiniest kids’ pedal bike on the market available without a coaster brake, the Jigger is the vehicle of choice for the rare tenacious little rider raring to go.", + "condition": "new" + }, + { + "pickup_zone": "POLYGON((-118.2887 34.0972, -118.1987 34.0972, -118.1987 33.9872, -118.2887 33.9872, -118.2887 34.0972))", + "store_location": "-118.2437,34.0522", + "brand": "Bicyk", + "model": "Hillcraft", + "price": 1200, + "description": "Kids want to ride with as little weight as possible. Especially on an incline! They may be at the age when a 27.5\" wheel bike is just too clumsy coming off a 24\" bike. The Hillcraft 26 is just the solution they need!", + "condition": "used" + }, + { + "pickup_zone": "POLYGON((-87.6848 41.9331, -87.5748 41.9331, -87.5748 41.8231, -87.6848 41.8231, -87.6848 41.9331))", + "store_location": "-87.6298,41.8781", + "brand": "Nord", + "model": "Chook air 5", + "price": 815, + "description": "The Chook Air 5 gives kids aged six years and older a durable and uberlight mountain bike for their first experience on tracks and easy cruising through forests and fields. The lower top tube makes it easy to mount and dismount in any situation, giving your kids greater safety on the trails.", + "condition": "used" + }, + { + "pickup_zone": "POLYGON((-80.2433 25.8067, -80.1333 25.8067, -80.1333 25.6967, -80.2433 25.6967, -80.2433 25.8067))", + "store_location": "-80.1918,25.7617", + "brand": "Eva", + "model": "Eva 291", + "price": 3400, + "description": "The sister company to Nord, Eva launched in 2005 as the first and only women-dedicated bicycle brand. Designed by women for women, allEva bikes are optimized for the feminine physique using analytics from a body metrics database. If you like 29ers, try the Eva 291. It’s a brand new bike for 2022.. This full-suspension, cross-country ride has been designed for velocity. The 291 has 100mm of front and rear travel, a superlight aluminum frame and fast-rolling 29-inch wheels. Yippee!", + "condition": "used" + }, + { + "pickup_zone": "POLYGON((-122.4644 37.8199, -122.3544 37.8199, -122.3544 37.7099, -122.4644 37.7099, -122.4644 37.8199))", + "store_location": "-122.4194,37.7749", + "brand": "Noka Bikes", + "model": "Kahuna", + "price": 3200, + "description": "Whether you want to try your hand at XC racing or are looking for a lively trail bike that's just as inspiring on the climbs as it is over rougher ground, the Wilder is one heck of a bike built specifically for short women. Both the frames and components have been tweaked to include a women’s saddle, different bars and unique colourway.", + "condition": "used" + }, + { + "pickup_zone": "POLYGON((-0.1778 51.5524, 0.0822 51.5524, 0.0822 51.4024, -0.1778 51.4024, -0.1778 51.5524))", + "store_location": "-0.1278,51.5074", + "brand": "Breakout", + "model": "XBN 2.1 Alloy", + "price": 810, + "description": "The XBN 2.1 Alloy is our entry-level road bike – but that’s not to say that it’s a basic machine. With an internal weld aluminium frame, a full carbon fork, and the slick-shifting Claris gears from Shimano’s, this is a bike which doesn’t break the bank and delivers craved performance.", + "condition": "new" + }, + { + "pickup_zone": "POLYGON((2.1767 48.9016, 2.5267 48.9016, 2.5267 48.5516, 2.1767 48.5516, 2.1767 48.9016))", + "store_location": "2.3522,48.8566", + "brand": "ScramBikes", + "model": "WattBike", + "price": 2300, + "description": "The WattBike is the best e-bike for people who still feel young at heart. It has a Bafang 1000W mid-drive system and a 48V 17.5AH Samsung Lithium-Ion battery, allowing you to ride for more than 60 miles on one charge. It’s great for tackling hilly terrain or if you just fancy a more leisurely ride. With three working modes, you can choose between E-bike, assisted bicycle, and normal bike modes.", + "condition": "new" + }, + { + "pickup_zone": "POLYGON((13.3260 52.5700, 13.6550 52.5700, 13.6550 52.2700, 13.3260 52.2700, 13.3260 52.5700))", + "store_location": "13.4050,52.5200", + "brand": "Peaknetic", + "model": "Secto", + "price": 430, + "description": "If you struggle with stiff fingers or a kinked neck or back after a few minutes on the road, this lightweight, aluminum bike alleviates those issues and allows you to enjoy the ride. From the ergonomic grips to the lumbar-supporting seat position, the Roll Low-Entry offers incredible comfort. The rear-inclined seat tube facilitates stability by allowing you to put a foot on the ground to balance at a stop, and the low step-over frame makes it accessible for all ability and mobility levels. The saddle is very soft, with a wide back to support your hip joints and a cutout in the center to redistribute that pressure. Rim brakes deliver satisfactory braking control, and the wide tires provide a smooth, stable ride on paved roads and gravel. Rack and fender mounts facilitate setting up the Roll Low-Entry as your preferred commuter, and the BMX-like handlebar offers space for mounting a flashlight, bell, or phone holder.", + "condition": "new" + }, + { + "pickup_zone": "POLYGON((1.9450 41.4301, 2.4018 41.4301, 2.4018 41.1987, 1.9450 41.1987, 1.9450 41.4301))", + "store_location": "2.1734, 41.3851", + "brand": "nHill", + "model": "Summit", + "price": 1200, + "description": "This budget mountain bike from nHill performs well both on bike paths and on the trail. The fork with 100mm of travel absorbs rough terrain. Fat Kenda Booster tires give you grip in corners and on wet trails. The Shimano Tourney drivetrain offered enough gears for finding a comfortable pace to ride uphill, and the Tektro hydraulic disc brakes break smoothly. Whether you want an affordable bike that you can take to work, but also take trail in mountains on the weekends or you’re just after a stable, comfortable ride for the bike path, the Summit gives a good value for money.", + "condition": "new" + }, + { + "pickup_zone": "POLYGON((12.4464 42.1028, 12.5464 42.1028, 12.5464 41.7028, 12.4464 41.7028, 12.4464 42.1028))", + "store_location": "12.4964,41.9028", + "model": "ThrillCycle", + "brand": "BikeShind", + "price": 815, + "description": "An artsy, retro-inspired bicycle that’s as functional as it is pretty: The ThrillCycle steel frame offers a smooth ride. A 9-speed drivetrain has enough gears for coasting in the city, but we wouldn’t suggest taking it to the mountains. Fenders protect you from mud, and a rear basket lets you transport groceries, flowers and books. The ThrillCycle comes with a limited lifetime warranty, so this little guy will last you long past graduation.", + "condition": "refurbished" + } +] diff --git a/doctests/data/query_vector.json b/doctests/data/query_vector.json new file mode 100644 index 00000000000..625479e111b --- /dev/null +++ b/doctests/data/query_vector.json @@ -0,0 +1,3952 @@ +[ + { + "brand": "Velorim", + "condition": "new", + "description": "Small and powerful, the Jigger is the best ride for the smallest of tikes! This is the tiniest kids\u2019 pedal bike on the market available without a coaster brake, the Jigger is the vehicle of choice for the rare tenacious little rider raring to go.", + "description_embeddings": [ + -0.026918452233076096, + 0.07200391590595245, + 0.019199736416339874, + -0.024749649688601494, + -0.09264523535966873, + 0.017702950164675713, + 0.11252444237470627, + 0.09377790987491608, + 0.005099582951515913, + 0.07054618746042252, + 0.0025260779075324535, + -0.04007257893681526, + 0.013598357327282429, + 0.03940897434949875, + -0.0069704861380159855, + 0.057934146374464035, + 0.15386416018009186, + 0.04337097704410553, + 0.07119690626859665, + -0.048173222690820694, + -0.09069827198982239, + -0.016886970028281212, + -0.04425429925322533, + -0.019464140757918358, + -0.027470778673887253, + 0.005336642265319824, + -0.09170512855052948, + 0.03556380048394203, + 0.023559197783470154, + -0.03628315031528473, + -0.04448218643665314, + 0.011364061385393143, + 0.009603296406567097, + -0.04861818626523018, + -0.03343017399311066, + -0.01483147218823433, + 0.06086479872465134, + 0.02109363302588463, + -0.025959225371479988, + 0.014000430703163147, + -0.00846729427576065, + 0.07305800914764404, + 0.02457829751074314, + -0.12663941085338593, + 0.010544337332248688, + 0.013315590098500252, + 0.07280771434307098, + -0.08232685923576355, + 0.0040486594662070274, + -0.026350753381848335, + 0.06408613175153732, + -0.01415738184005022, + 0.04628903791308403, + -0.02050374448299408, + 0.04177685081958771, + -0.09207800775766373, + -0.005421833600848913, + -0.005136478692293167, + -0.024564260616898537, + 0.026354163885116577, + -0.05851329490542412, + 0.03147275745868683, + -0.02183554694056511, + 0.03346295654773712, + -0.02240697667002678, + -0.09603817760944366, + -0.02274233102798462, + -0.039677977561950684, + 0.007695100735872984, + 0.039304088801145554, + -0.017668871209025383, + 0.022897064685821533, + -0.039273541420698166, + 0.08864572644233704, + -0.04432578384876251, + -0.06769558042287827, + 0.06696884334087372, + 0.07118263095617294, + -0.024863263592123985, + 0.01151553075760603, + -0.11591693758964539, + -0.025131937116384506, + 0.052269868552684784, + -0.03035089746117592, + 0.00906881783157587, + 0.04585501551628113, + 0.038361817598342896, + 0.03026638925075531, + 0.015340524725615978, + -0.006911538541316986, + 0.022395918145775795, + 0.13969141244888306, + 0.047686025500297546, + 0.05438247323036194, + 0.02779674343764782, + -0.04191797226667404, + -0.021741557866334915, + 0.003305602353066206, + -0.11100355535745621, + 0.016258426010608673, + 0.06977421790361404, + 0.08189859241247177, + 0.0966871827840805, + 0.03519754856824875, + 0.05674563720822334, + 0.034512776881456375, + 0.07052291929721832, + -0.06342307478189468, + 0.051868196576833725, + -0.013776403851807117, + -0.007541927974671125, + -0.043183840811252594, + 0.021481933072209358, + 0.0380198135972023, + -0.07870583236217499, + -0.10873759537935257, + -0.08491706103086472, + 0.03155837208032608, + -0.03790571913123131, + 0.041968367993831635, + -0.00593406381085515, + 0.036538854241371155, + -0.004705581348389387, + 0.004229994490742683, + -0.00729013979434967, + -0.019296232610940933, + -0.014331319369375706, + -4.401502220942261e-34, + -0.0067550260573625565, + 0.07402423769235611, + -0.012888211756944656, + -0.055266380310058594, + 0.04810081049799919, + 0.005175809375941753, + -0.004325157031416893, + -0.10392399877309799, + -0.03650582954287529, + 0.07477248460054398, + 0.0022102247457951307, + -0.05040738359093666, + -0.003033560933545232, + 0.060498371720314026, + 0.08619660884141922, + -0.04577762633562088, + -0.10468175262212753, + -0.07177772372961044, + -0.05756700038909912, + -0.02839704230427742, + -0.028650879859924316, + -0.010213681496679783, + 0.008074316196143627, + 0.03448071703314781, + -0.025478240102529526, + -0.029753824695944786, + 0.05397271364927292, + -0.0006062929751351476, + -0.03292117267847061, + 0.040799956768751144, + -0.0933983102440834, + -0.026921836659312248, + 0.00327915046364069, + -0.025635670870542526, + -0.057946983724832535, + -0.06664302200078964, + 0.04280361905694008, + -0.027111517265439034, + -0.08359260112047195, + 0.03483080118894577, + 0.023318039253354073, + -0.08598511666059494, + -0.08378149569034576, + 0.054067932069301605, + -0.014853178523480892, + -0.05498708039522171, + 0.0711284726858139, + 0.12539872527122498, + -0.04355800896883011, + -0.027553806081414223, + -0.009111037477850914, + -0.058482203632593155, + 0.07053986191749573, + -0.009027705527842045, + -0.017481567338109016, + 0.011404255405068398, + 0.06084389239549637, + -0.028110956773161888, + -0.08594057708978653, + 0.05215488001704216, + -0.07651112973690033, + -0.027076181024312973, + -0.01357623003423214, + -0.01263132132589817, + -0.0193876251578331, + 0.013576658442616463, + 0.038156066089868546, + -0.04309772700071335, + -0.04051121696829796, + -0.025885144248604774, + -0.003073574509471655, + -0.0003303807170595974, + 0.08043289929628372, + -0.039484549313783646, + 0.10091833025217056, + -0.04735022410750389, + 0.027813943102955818, + -0.0038837436586618423, + -0.05234759673476219, + -0.00716474698856473, + 0.016360750421881676, + -0.025806615129113197, + -0.03212691470980644, + -0.08456144481897354, + -0.019326699897646904, + -0.03228791803121567, + 0.07633069902658463, + -0.07643644511699677, + -0.03988131135702133, + 0.02396279387176037, + -0.055901359766721725, + 0.009231535717844963, + 0.0344320572912693, + 0.07486359030008316, + -0.03505600243806839, + -2.324423447670953e-34, + -0.04453577473759651, + 0.06512241810560226, + 0.03920532390475273, + 0.062222111970186234, + -0.015745285898447037, + -0.017774563282728195, + 0.08228200674057007, + -0.05798694118857384, + -0.042758435010910034, + -0.018822822719812393, + -0.07607664912939072, + -0.02666221559047699, + 0.036936040967702866, + -0.034714240580797195, + 0.06992944329977036, + 0.00530517753213644, + -0.0005260169273242354, + -0.03961028903722763, + 0.08799499273300171, + -0.04191635549068451, + 0.07468635588884354, + -0.010930310003459454, + -0.0611649826169014, + -0.04100184887647629, + 0.07131826132535934, + 0.03241356834769249, + -0.0545443594455719, + -0.005295638460665941, + -0.04712966829538345, + 0.032524388283491135, + -0.05130890756845474, + -0.01299980841577053, + 0.06523969769477844, + -0.011433755978941917, + 0.018730396404862404, + 0.047184932976961136, + -0.043041545897722244, + -0.03231072053313255, + -0.015864262357354164, + 0.03991076350212097, + -0.017617924138903618, + -0.03504975512623787, + 0.027346905320882797, + 0.05564267560839653, + 0.01610865257680416, + 0.0470576174557209, + -0.010647954419255257, + 0.13047614693641663, + -0.011055804789066315, + 0.011903814040124416, + -0.01350466813892126, + -0.0019897734746336937, + 0.053073540329933167, + 0.0717632919549942, + 0.007322370074689388, + -0.0206251572817564, + 0.061210062354803085, + 0.03184640407562256, + -0.035093698650598526, + -0.0026315131690353155, + -0.03291690722107887, + -0.04229205846786499, + -0.04241437837481499, + 0.1091129332780838, + 0.02229561097919941, + 0.02223002351820469, + 0.03949614241719246, + 0.031568314880132675, + -0.07121116667985916, + -0.07664268463850021, + -0.04235681891441345, + -0.011173299513757229, + 0.1190338209271431, + -0.09825095534324646, + -0.0375107042491436, + 0.007167852018028498, + 0.047537703067064285, + 0.044423725455999374, + 0.022106878459453583, + -0.02811007760465145, + 0.033864255994558334, + 0.0643145889043808, + 0.03725901246070862, + -0.0497952364385128, + -0.021733446046710014, + 0.023898839950561523, + -0.11254694312810898, + -0.06519465893507004, + 0.04424642026424408, + 0.09124527126550674, + 0.006083414424210787, + 0.09144245833158493, + 0.02653978019952774, + -0.01318738516420126, + 0.0480327382683754, + -3.0391063887691416e-08, + 0.051376331597566605, + -0.0002709411783143878, + -0.03103259764611721, + -0.018394535407423973, + 0.05002995952963829, + 0.05086217448115349, + -0.07317503541707993, + -0.0172730665653944, + -0.08379635214805603, + 0.1180257499217987, + 0.08445936441421509, + -0.025030585005879402, + -0.01965731382369995, + 0.046042654663324356, + 0.03724817931652069, + 0.028524605557322502, + 0.061249297112226486, + -0.027382537722587585, + -0.0011134583037346601, + -0.001871297718025744, + -0.04395337030291557, + 0.002261978341266513, + 0.06950556486845016, + -0.024213269352912903, + -0.0782783254981041, + -0.10320401936769485, + -0.022083906456828117, + -0.04333319142460823, + -0.0334695503115654, + 0.007842703722417355, + -0.03523677587509155, + 0.08107997477054596, + 0.00924254022538662, + -0.013395791873335838, + -0.019067300483584404, + -0.008446489460766315, + -0.1053837463259697, + 0.06697141379117966, + 0.06984667479991913, + 0.007155571132898331, + -0.038544610142707825, + 0.0132181691005826, + -0.004773592576384544, + -0.022143904119729996, + -0.09064015746116638, + -0.07600560784339905, + -0.042070601135492325, + -0.08189931511878967, + 0.03302472084760666, + 0.043238356709480286, + -0.01407547201961279, + -0.03778013586997986, + 0.030578600242733955, + 0.021573437377810478, + 0.04664295166730881, + 0.056082408875226974, + -0.07687672227621078, + -0.0018553169211372733, + -0.051700614392757416, + 0.043752558529376984, + -0.02636834792792797, + 0.05589277669787407, + 0.05282546952366829, + -0.016411008313298225 + ], + "model": "Jigger", + "pickup_zone": "POLYGON((-74.0610 40.7578, -73.9510 40.7578, -73.9510 40.6678, -74.0610 40.6678, -74.0610 40.7578))", + "price": 270, + "store_location": "-74.0060,40.7128" + }, + { + "brand": "Bicyk", + "condition": "used", + "description": "Kids want to ride with as little weight as possible. Especially on an incline! They may be at the age when a 27.5\" wheel bike is just too clumsy coming off a 24\" bike. The Hillcraft 26 is just the solution they need!", + "description_embeddings": [ + -0.004883771762251854, + 0.08099519461393356, + -0.022444017231464386, + 0.05437565594911575, + -0.07422323524951935, + 0.0066548739559948444, + -0.06268022209405899, + 0.042389899492263794, + -0.03745086118578911, + 0.058961447328329086, + 0.025613723322749138, + -0.04209878668189049, + 0.06861244142055511, + 0.01983577199280262, + 0.026353053748607635, + 0.045618414878845215, + 0.040685027837753296, + 0.09574265778064728, + 0.005801026243716478, + -0.027659950777888298, + -0.0223013274371624, + 0.040641166269779205, + 0.06608107686042786, + 0.0691058486700058, + -0.03629102557897568, + 0.035505786538124084, + -0.09211395680904388, + -0.011358118616044521, + -0.025078972801566124, + -0.017709167674183846, + 0.07587391883134842, + 0.08128049969673157, + 0.060521550476551056, + -0.0845090001821518, + -0.03779749944806099, + 0.030346086248755455, + 0.017926080152392387, + 0.003489845432341099, + -0.05622200667858124, + -0.06886664777994156, + -0.051538050174713135, + 0.029196197167038918, + -0.0028146395925432444, + 0.012419342994689941, + -0.06346380710601807, + -0.011617675423622131, + 0.04980290308594704, + -0.0799335315823555, + 0.016635078936815262, + 0.07064730674028397, + 0.04530491679906845, + -0.04372858256101608, + 0.07056037336587906, + -0.05052798613905907, + -0.01064316462725401, + -0.04754374921321869, + -0.08878123015165329, + 0.005363269243389368, + 0.032587066292762756, + -0.05610528588294983, + -0.0012875061947852373, + -0.03215320408344269, + -0.0045777312479913235, + -0.026692084968090057, + -0.09758491814136505, + -0.046251099556684494, + 0.03897765651345253, + -0.06587375700473785, + -0.013586618937551975, + -0.020807752385735512, + 0.023367363959550858, + 0.011167124845087528, + 0.003386110533028841, + -0.024887114763259888, + -0.029615335166454315, + -0.02571641281247139, + -0.03150812163949013, + -0.0395360104739666, + -0.049686528742313385, + -0.023117102682590485, + -0.07580453157424927, + 0.020851964130997658, + 0.0917917937040329, + -0.038357049226760864, + 0.05106140300631523, + -0.03367459401488304, + 0.05801103636622429, + 0.0628814697265625, + 0.024997225031256676, + 0.015594316646456718, + -0.01490987278521061, + 0.07070998847484589, + -0.039144083857536316, + 0.0657331570982933, + -0.053744420409202576, + -0.01675831526517868, + -0.008745769970119, + -0.07664742320775986, + -0.06751038879156113, + 0.0023392336443066597, + 0.018902592360973358, + 0.06754770874977112, + 0.07430258393287659, + 0.0806465670466423, + -0.031056180596351624, + 0.06557579338550568, + 0.06529161334037781, + -0.03742394223809242, + 0.0007822285988368094, + 0.10523669421672821, + 0.0038901956286281347, + -0.014934191480278969, + 0.0647430568933487, + 0.03438747301697731, + -0.046527378261089325, + 0.014247977174818516, + -0.020184241235256195, + 0.016480082646012306, + -0.05491460859775543, + 0.07232335209846497, + -0.016330908983945847, + 0.011368552222847939, + -0.001964043825864792, + 0.0009170984267257154, + 0.019140562042593956, + -0.002758787479251623, + -0.05629577115178108, + 1.7957766384702998e-33, + -0.10579885542392731, + 0.08271613717079163, + 0.03821941837668419, + 0.06078806892037392, + 0.017647448927164078, + -0.07404755055904388, + 0.06083450838923454, + -0.07097837328910828, + -0.01949647255241871, + -0.005204185377806425, + 0.0160058680921793, + -0.03624944016337395, + 0.065463587641716, + -0.04834574833512306, + 0.09314870834350586, + -0.022509299218654633, + -0.047614336013793945, + -0.042122796177864075, + 0.0014064351562410593, + 0.08215921372175217, + 0.0144058121368289, + -0.08526691794395447, + -0.01885370910167694, + 0.020506983622908592, + -0.0041589876636862755, + -0.0928102508187294, + 0.0965222716331482, + 0.05469893291592598, + 0.002785224001854658, + 0.006347258575260639, + -0.09394793212413788, + -0.08587668836116791, + 0.00999284815043211, + -0.015109539031982422, + 0.035454027354717255, + -0.08842843770980835, + -0.015698572620749474, + 0.05549640208482742, + -0.011119373142719269, + 0.012295924127101898, + 0.007523554377257824, + -0.03497130423784256, + -0.05309790000319481, + -0.021819932386279106, + 0.011010204441845417, + 0.0778549313545227, + 0.122015580534935, + 0.0451776348054409, + -0.0894458070397377, + 0.0031173918396234512, + -0.003828236600384116, + 0.0010151821188628674, + 0.0007775757694616914, + -0.0007406148943118751, + 0.0005911831394769251, + 0.029611686244606972, + -0.010095393285155296, + -0.015750357881188393, + -0.08871200680732727, + 0.06563369184732437, + 0.052563928067684174, + -0.02150006778538227, + 0.032858334481716156, + -0.039781685918569565, + -0.02986454777419567, + -0.017254218459129333, + 0.013349524699151516, + 0.04903600737452507, + -0.102760910987854, + 0.027411801740527153, + -0.007306735496968031, + 0.03547230362892151, + 0.03793823719024658, + -0.014224819839000702, + 0.004229242447763681, + -0.04914051666855812, + -0.05566011741757393, + -0.08426816016435623, + 0.0378078892827034, + -0.02177048847079277, + 0.037800222635269165, + 0.01145790982991457, + -0.03493969514966011, + -0.06417357921600342, + -0.04812582954764366, + -0.030254419893026352, + -0.12552082538604736, + 0.0017056012293323874, + -0.053679559379816055, + 0.019939688965678215, + -0.04766315221786499, + -0.143480584025383, + -0.024615854024887085, + 0.06507551670074463, + 0.01710103265941143, + -2.55080180279124e-33, + -0.01073896698653698, + 0.08023330569267273, + 0.028500312939286232, + -0.033364687114953995, + 0.018465891480445862, + -0.018969086930155754, + 0.116150863468647, + -0.04905116185545921, + 0.0067994301207363605, + -0.051097989082336426, + -0.047208935022354126, + 0.003005147911608219, + -0.006951641291379929, + 0.0299075860530138, + 0.023957515135407448, + 0.005555577110499144, + -0.020836569368839264, + 0.013542957603931427, + 0.09286782890558243, + -0.04009733721613884, + 0.05567550286650658, + 0.01991306059062481, + -0.16575683653354645, + -0.003300475887954235, + 0.11635366082191467, + 0.008300523273646832, + -0.1112738847732544, + 0.05307481065392494, + 0.009467027150094509, + 0.11263766884803772, + -0.04102758690714836, + -0.0505208782851696, + 0.1890914887189865, + -0.01593983918428421, + 0.011381726711988449, + 0.01095605455338955, + -0.08038999140262604, + -0.012621873058378696, + -0.005316049326211214, + 0.017261112108826637, + 0.03283751755952835, + -0.04533768445253372, + 0.03397509828209877, + 0.04211656376719475, + 0.024692395702004433, + -0.02541458234190941, + 0.02313675545156002, + 0.02338019199669361, + -0.011879520490765572, + -0.05438990890979767, + 0.03806900233030319, + 0.01261812262237072, + 0.02512892708182335, + -0.0028746703173965216, + -0.016077643260359764, + -0.032072994858026505, + -0.006427581422030926, + 0.01777057908475399, + 0.02934812381863594, + -0.05759994685649872, + -2.871774631785229e-05, + -0.03137838840484619, + -0.06273766607046127, + 0.04409930482506752, + -0.05993351340293884, + 0.007546861190348864, + 0.0053585791029036045, + 0.042325496673583984, + -0.007369876839220524, + 0.04489513114094734, + -0.12103329598903656, + 0.017391694709658623, + 0.0304956566542387, + -0.034047987312078476, + 0.02484256401658058, + -0.06834809482097626, + 0.06508748978376389, + 0.08324999362230301, + -0.020252887159585953, + -0.014722783118486404, + 0.02126440405845642, + -0.05160334333777428, + 0.045947108417749405, + 0.022960059344768524, + 0.023375188931822777, + -0.060902271419763565, + -0.05150751397013664, + -0.1094929575920105, + -0.04899677261710167, + 0.09132419526576996, + 0.051848214119672775, + -0.0077659315429627895, + 0.0012422297149896622, + 0.058530740439891815, + 0.040777210146188736, + -3.354356081786136e-08, + 0.025891084223985672, + -0.04088461399078369, + -0.06885679066181183, + 0.01951301097869873, + 0.047974348068237305, + 0.04472370818257332, + 0.004657004959881306, + 0.001041706302203238, + -0.02763887122273445, + 0.03814717009663582, + 0.03166148066520691, + 0.0063626947812736034, + 0.09577886760234833, + 0.06234167888760567, + 0.0010398400481790304, + 0.010609040968120098, + 0.020408503711223602, + 0.05596008151769638, + 0.00923844799399376, + 0.011290326714515686, + 0.02393697388470173, + -0.03378620743751526, + 0.010788901709020138, + 0.0072112190537154675, + -0.03552679717540741, + -0.10475718975067139, + 0.003995304461568594, + -0.002284976886585355, + -0.014504319056868553, + -0.06887608021497726, + 0.03398992121219635, + -0.005206231493502855, + 0.049566611647605896, + 0.00902023445814848, + 0.06874160468578339, + 0.014804325066506863, + -0.07230424880981445, + 0.0428827665746212, + 0.013657039031386375, + 0.027973631396889687, + -0.035619381815195084, + 0.06485525518655777, + -0.06238642707467079, + -0.012459578923881054, + 0.020500177517533302, + -0.0715484470129013, + -0.16523504257202148, + 0.013638298027217388, + 0.07008316367864609, + 0.026970835402607918, + 0.004871702753007412, + -0.0012540861498564482, + -0.028708957135677338, + 0.05812879279255867, + 0.12611250579357147, + 0.09877888858318329, + -0.04118988662958145, + -0.02214396744966507, + -0.10328112542629242, + 0.029945021495223045, + 0.004513312131166458, + 0.011272193863987923, + 0.03294430673122406, + -0.042709026485681534 + ], + "model": "Hillcraft", + "pickup_zone": "POLYGON((-118.2887 34.0972, -118.1987 34.0972, -118.1987 33.9872, -118.2887 33.9872, -118.2887 34.0972))", + "price": 1200, + "store_location": "-118.2437,34.0522" + }, + { + "brand": "Nord", + "condition": "used", + "description": "The Chook Air 5 gives kids aged six years and older a durable and uberlight mountain bike for their first experience on tracks and easy cruising through forests and fields. The lower top tube makes it easy to mount and dismount in any situation, giving your kids greater safety on the trails.", + "description_embeddings": [ + -0.0018494834657758474, + 0.057690851390361786, + 0.038153987377882004, + 0.06570218503475189, + 0.028856752440333366, + -0.062333013862371445, + -0.014953209087252617, + 0.046022992581129074, + -0.08184631168842316, + 0.03648914024233818, + 0.03869181126356125, + 0.010564669035375118, + -0.020310621708631516, + -0.04062078520655632, + -0.0125962495803833, + 0.14169928431510925, + 0.03418859466910362, + -0.06641822308301926, + 0.005633663386106491, + -0.09943458437919617, + 0.023237863555550575, + -0.04369724169373512, + 0.016574522480368614, + 0.07258585095405579, + 0.018674470484256744, + -0.05764088034629822, + -0.0795072391629219, + 0.04034125804901123, + -0.036483921110630035, + -0.033106740564107895, + 0.02980787307024002, + -0.0028512384742498398, + 0.00786224752664566, + -0.03016488254070282, + -0.12349128723144531, + 0.031072411686182022, + 0.08362030982971191, + 0.025227056816220284, + -0.030982907861471176, + -0.006486377213150263, + -0.023590318858623505, + -0.03374557942152023, + -0.04145599156618118, + -0.09421771764755249, + -0.0013142612297087908, + -0.003397064981982112, + -0.0031338112894445658, + -0.11464792490005493, + 0.040542472153902054, + 0.02896481193602085, + 0.007327641360461712, + -0.06064218282699585, + 0.049546848982572556, + -0.05917377769947052, + -0.01963184028863907, + -0.002139812568202615, + -0.14361988008022308, + -0.05401389300823212, + 0.12506668269634247, + -0.07141950726509094, + -0.0032961040269583464, + 0.015251584351062775, + -0.05507654324173927, + -0.009836667217314243, + -0.02802908606827259, + -0.01053905300796032, + -0.03239851072430611, + -0.10646941512823105, + 0.03140658512711525, + 0.028125958517193794, + -0.004000179003924131, + -0.0018343725241720676, + 0.01727917790412903, + -0.013935663737356663, + -0.02435036562383175, + 0.04411163926124573, + -0.009158330969512463, + -0.023309389129281044, + 0.01229795441031456, + -0.04689493402838707, + -0.02138197235763073, + 0.013063939288258553, + 0.02832808345556259, + 0.031972818076610565, + 0.020882662385702133, + -0.015083174221217632, + 0.002903456799685955, + -0.047304242849349976, + -0.10658963769674301, + -0.06274145841598511, + -0.030370736494660378, + 0.0539257749915123, + 0.00848578754812479, + 0.02172423154115677, + -0.007691903971135616, + -0.10581931471824646, + 0.06078812852501869, + 0.007988635450601578, + -0.116583913564682, + 0.05237157270312309, + 0.024445366114377975, + -0.02832716703414917, + 0.04004029557108879, + 0.02844531089067459, + -0.0455174520611763, + -0.07379734516143799, + 0.09453199058771133, + -0.011599121615290642, + -0.0027194281574338675, + 0.014355232007801533, + -0.059690698981285095, + -0.012937567196786404, + 0.034315045922994614, + -0.047598548233509064, + -0.03261064738035202, + 0.05839747563004494, + -0.12691465020179749, + 0.03778312727808952, + 0.006131120957434177, + 0.04806787148118019, + -0.03441976010799408, + 0.08042734116315842, + 0.008934197947382927, + 0.027216315269470215, + 0.0016972541343420744, + -0.10113930702209473, + -0.0003218930505681783, + 9.75532108829862e-34, + -0.0537416972219944, + 0.06875170767307281, + -0.01566525548696518, + 0.01952524110674858, + -0.0005404680850915611, + -0.08984242379665375, + 0.04537447541952133, + -0.1295408457517624, + 7.603532867506146e-05, + 0.040753066539764404, + 0.016371281817555428, + 0.029906686395406723, + -0.005372706335037947, + -0.06687828153371811, + 0.11607439070940018, + 0.016209086403250694, + -0.11238335072994232, + -0.057236358523368835, + -0.09619198739528656, + 0.027146028354763985, + -0.09542766213417053, + -0.0360424630343914, + -0.09913153201341629, + 0.04242968559265137, + 0.05494842678308487, + -0.025294257327914238, + 0.06307625770568848, + 0.007745219860225916, + -0.019641151651740074, + 0.07056662440299988, + -0.05425839126110077, + 0.012385660782456398, + -0.006104010157287121, + -0.07186716794967651, + -0.10919132828712463, + -0.0017968777101486921, + 0.010471112094819546, + -0.011221444234251976, + -0.035078633576631546, + 0.009300827980041504, + 0.0802159234881401, + -0.10042990744113922, + -0.01718892529606819, + 0.05525779724121094, + -0.019847391173243523, + 0.08405174314975739, + 0.06403975188732147, + 0.0627448782324791, + -0.07871688157320023, + 0.0002676364383660257, + -0.05110163986682892, + 0.006078454665839672, + -0.019803548231720924, + -0.014253707602620125, + 0.039836447685956955, + 0.021812820807099342, + 0.00014793995069339871, + -0.006739508826285601, + -0.023166682571172714, + 0.05429920181632042, + 0.1337796151638031, + -0.01773720420897007, + 0.024304436519742012, + -0.016411837190389633, + -0.07777299731969833, + 0.042669400572776794, + 0.06226645037531853, + -0.023803485557436943, + 0.00396164134144783, + -0.049458179622888565, + -0.004774407483637333, + 0.036529600620269775, + 0.02002328634262085, + -0.02465248480439186, + -0.024076614528894424, + -0.03887653350830078, + 0.057176534086465836, + -0.03888818621635437, + -0.027698175981640816, + 0.001469603506848216, + 0.04755152389407158, + -0.0938708707690239, + 0.013991769403219223, + -0.026987746357917786, + -0.03277081623673439, + -0.04837489873170853, + 0.02711542509496212, + -0.09185922145843506, + -0.03453921154141426, + 0.03532274439930916, + -0.024472877383232117, + -0.09732313454151154, + 0.008513586595654488, + 0.03207352012395859, + 0.030766190961003304, + -2.5405224720352836e-33, + 0.08067575097084045, + -0.008160247467458248, + 0.03101508319377899, + 0.005022854544222355, + 0.0010000152979046106, + 0.06958161294460297, + 0.10594375431537628, + -0.06177408620715141, + -0.01650322414934635, + 0.03043895959854126, + -0.018333615735173225, + 0.06436911225318909, + 0.02439672127366066, + -0.01213911734521389, + 0.09937868267297745, + 0.02093592658638954, + -0.003140130080282688, + -0.016580305993556976, + 0.10430759936571121, + -0.019038936123251915, + 0.05574416741728783, + 0.07455366849899292, + -0.029936065897345543, + 0.0017704741330817342, + 0.07022519409656525, + 0.02108696848154068, + -0.050372399389743805, + 0.027957700192928314, + 0.005698348395526409, + 0.03494682535529137, + -0.02610155940055847, + -0.0068950653076171875, + 0.1346345692873001, + 0.03947756066918373, + -0.05966781824827194, + -0.010489783249795437, + -0.04099087417125702, + -0.027186688035726547, + -0.04620056599378586, + -0.02478279173374176, + -0.007060651201754808, + -0.023514581844210625, + 0.06082035228610039, + -0.05849218741059303, + -0.0036981222219765186, + 0.04169313609600067, + 0.09352244436740875, + 0.020741308107972145, + -0.0019505824893712997, + -0.10489305853843689, + 0.08789870887994766, + 0.039999835193157196, + -0.014334832318127155, + 0.008347390219569206, + 0.03113699145615101, + 0.10894497483968735, + 0.027165820822119713, + 0.0064145540818572044, + -0.00803150050342083, + -0.055484138429164886, + -0.03251631557941437, + 0.02290980890393257, + 0.04825572296977043, + 0.01608354039490223, + -0.04969468340277672, + -0.004120110999792814, + -0.03278858959674835, + 0.009696378372609615, + -0.04376300796866417, + -0.0009336083312518895, + -0.0313178189098835, + -0.05882050469517708, + 0.057815250009298325, + -0.02050788328051567, + -0.024381538853049278, + 0.06283771246671677, + 0.06954411417245865, + 0.09720556437969208, + -0.056403111666440964, + -0.04526498168706894, + -0.07259991019964218, + 0.001844316371716559, + 0.04090375825762749, + 0.06941819936037064, + -0.041316937655210495, + 0.002292247023433447, + -0.03425106778740883, + -0.0628972053527832, + 0.0063382769003510475, + 0.09668626636266708, + 0.062228571623563766, + 0.03658486530184746, + -0.08789398521184921, + 0.0009696391643956304, + -0.004108588211238384, + -3.183854957455878e-08, + 0.09346635639667511, + 0.042943451553583145, + -0.0005091542261652648, + 0.026524608954787254, + 0.009858721867203712, + 0.03737989068031311, + -0.014056878164410591, + 0.038327474147081375, + -0.010239921510219574, + 0.05757640674710274, + 0.014411918818950653, + 0.038742948323488235, + 0.06475342065095901, + -0.0011537548853084445, + -0.017729472368955612, + 0.05167960748076439, + 0.024419916793704033, + 0.08681508898735046, + 0.015235288999974728, + 0.036676522344350815, + -0.03421042487025261, + -0.019762659445405006, + 0.09467294812202454, + -0.053133491426706314, + -0.04356615990400314, + -0.03919585049152374, + 0.015591761097311974, + 0.021372869610786438, + -0.0058142030611634254, + -0.022864477708935738, + -0.02901598811149597, + 0.0458187572658062, + -0.030826766043901443, + -0.008975986391305923, + 0.03365883231163025, + -0.010484383441507816, + -0.1445801854133606, + 0.05828193947672844, + -0.028194306418299675, + 0.05975533276796341, + 0.014028828591108322, + 0.0036430624313652515, + 0.024983061477541924, + 0.01454286277294159, + -0.006972659844905138, + 0.037446193397045135, + -0.06949692964553833, + -0.09630566090345383, + 0.03263894096016884, + 0.048720087856054306, + -0.06886433064937592, + 0.018142051994800568, + 0.03894415125250816, + 0.05843216925859451, + 0.06860719621181488, + 0.04971907287836075, + -0.025701280683279037, + -0.06293400377035141, + -0.05422094464302063, + 0.01912975125014782, + 0.009564549662172794, + 0.055643752217292786, + -0.0027948219794780016, + 0.0329461470246315 + ], + "model": "Chook air 5", + "pickup_zone": "POLYGON((-87.6848 41.9331, -87.5748 41.9331, -87.5748 41.8231, -87.6848 41.8231, -87.6848 41.9331))", + "price": 815, + "store_location": "-87.6298,41.8781" + }, + { + "brand": "Eva", + "condition": "used", + "description": "The sister company to Nord, Eva launched in 2005 as the first and only women-dedicated bicycle brand. Designed by women for women, allEva bikes are optimized for the feminine physique using analytics from a body metrics database. If you like 29ers, try the Eva 291. It\u2019s a brand new bike for 2022.. This full-suspension, cross-country ride has been designed for velocity. The 291 has 100mm of front and rear travel, a superlight aluminum frame and fast-rolling 29-inch wheels. Yippee!", + "description_embeddings": [ + 0.035103436559438705, + 0.02666405402123928, + -0.06364161521196365, + 0.02551678754389286, + -0.005281867925077677, + -0.04041111096739769, + -0.020820949226617813, + -0.03485208749771118, + -0.07683732360601425, + 0.021527189761400223, + 0.007516156416386366, + -0.0034474905114620924, + 0.030465560033917427, + -0.06572654843330383, + -0.020914506167173386, + 0.032637566328048706, + 0.0815017819404602, + -0.022764643654227257, + 0.07385077327489853, + 0.058004263788461685, + -0.03062591142952442, + -0.03927106410264969, + 0.01410673838108778, + 0.05480317771434784, + -0.05647570267319679, + -0.008725483901798725, + -0.05373937636613846, + 0.03546926751732826, + -0.026455262675881386, + -0.09718386828899384, + -0.040623344480991364, + 0.03622450679540634, + 0.08971034735441208, + -0.038574062287807465, + -0.05105822533369064, + 0.021020587533712387, + 0.003312851767987013, + -0.008969941176474094, + -0.0568903312087059, + 0.033502694219350815, + -0.0578635074198246, + -0.03721226751804352, + -0.036668986082077026, + 0.0022641047835350037, + 0.038053013384342194, + 0.03350543603301048, + 0.05258995667099953, + -0.007619654294103384, + -0.05145309492945671, + 0.026286069303750992, + 0.09238694608211517, + -0.06733417510986328, + 0.05791507661342621, + -0.07623173296451569, + -0.0052175214514136314, + -0.04393303394317627, + -0.15249019861221313, + -0.019764462485909462, + -0.03331150859594345, + -0.11831824481487274, + 0.05683637782931328, + 0.01903577335178852, + -0.023615414276719093, + 0.04120348393917084, + -0.05669170990586281, + 0.002014430705457926, + 0.009953595697879791, + 0.008390115574002266, + -0.025982441380620003, + -0.05977262556552887, + 0.0611012764275074, + 0.010146236047148705, + -0.047625984996557236, + 0.09061623364686966, + 0.02551751770079136, + 0.06041749566793442, + 0.1113404780626297, + 0.017673874273896217, + -0.05184035003185272, + 0.023109234869480133, + -0.04408435523509979, + -0.09029985964298248, + 0.06733208149671555, + 0.049696795642375946, + 0.05554559826850891, + 0.01563390903174877, + -0.019527558237314224, + -0.014796985313296318, + 0.003595865098759532, + 0.012917418964207172, + -0.06858907639980316, + -0.008697138167917728, + 0.04721860587596893, + 0.03153855353593826, + -0.05275731533765793, + 0.019923491403460503, + -0.009707989171147346, + -0.018920045346021652, + 0.03773808106780052, + 0.042773403227329254, + -0.03819388151168823, + 0.04029008001089096, + 0.060834936797618866, + 0.07531940191984177, + -0.04695741459727287, + 0.00488079572096467, + 0.13518987596035004, + -0.014429144561290741, + 0.0314130075275898, + 0.04135369136929512, + -0.021350333467125893, + -0.01289785373955965, + 0.032701168209314346, + -0.037027571350336075, + -0.018168980255723, + -0.07188623398542404, + -0.036501020193099976, + 0.03303954005241394, + 0.10676111280918121, + -0.03277475759387016, + -0.06245853006839752, + -0.011879103258252144, + 0.0533418171107769, + -0.01446340698748827, + 0.03530410677194595, + 0.05820532143115997, + -0.0015766610158607364, + -8.477015062817539e-34, + -0.10837127268314362, + 0.015582970343530178, + 0.0182977095246315, + 0.055213626474142075, + -0.03584470599889755, + 0.02778925560414791, + 0.08698058873414993, + -0.04999695345759392, + -0.0842076763510704, + -0.040448326617479324, + -0.06311061233282089, + 0.02668358013033867, + 0.04676111042499542, + -0.006417605560272932, + 0.12788759171962738, + 0.02197197638452053, + 0.07035308331251144, + -0.04392284154891968, + -0.008788101375102997, + -0.001108768628910184, + 0.07960236072540283, + -0.0019975434988737106, + -0.010499294847249985, + 0.03599945828318596, + 0.02329486794769764, + -0.01962442137300968, + 0.15486162900924683, + 0.027215363457798958, + -0.022036811336874962, + 0.04227740690112114, + -0.07894206047058105, + -0.03180933743715286, + 0.020022651180624962, + -0.05859709531068802, + -0.0009564562351442873, + 0.009732870385050774, + -0.09007531404495239, + 0.041766807436943054, + 0.003291067900136113, + 0.0950121283531189, + 0.02693203091621399, + -0.01629827171564102, + -0.01884087361395359, + -0.029889501631259918, + -0.003714299062266946, + 0.09247232973575592, + 0.06151247024536133, + 0.09120925515890121, + -0.011266379617154598, + 0.03680066391825676, + -0.1628604233264923, + -0.008045083843171597, + -0.023876454681158066, + 0.046465914696455, + 0.01841152086853981, + 0.01759498566389084, + -0.012628139927983284, + 0.02123965509235859, + -0.07098977267742157, + 0.025028834119439125, + -0.032171521335840225, + 0.0538402758538723, + -0.00211805896833539, + -0.019548164680600166, + -0.046793293207883835, + 0.04394293949007988, + 0.022090770304203033, + -0.058215122669935226, + -0.05101722851395607, + 0.014057288877665997, + -0.05860457569360733, + 0.006166193168610334, + 0.04641130194067955, + 0.03165149688720703, + 0.045289356261491776, + 0.038580797612667084, + -0.0335664264857769, + 0.013378577306866646, + 0.058321163058280945, + -0.03730795532464981, + -0.01996016688644886, + 0.025774186477065086, + 0.001352976425550878, + 0.023636724799871445, + 0.026727410033345222, + -0.0730309784412384, + -0.01613243669271469, + -0.009525856003165245, + -0.03985843434929848, + -0.003976964857429266, + -0.006743384525179863, + -0.06324627995491028, + 0.03959967568516731, + 0.021898096427321434, + -0.004184887744486332, + -3.110083923319741e-34, + 0.03726857528090477, + -0.016945818439126015, + 0.05512943118810654, + 0.0471712127327919, + 0.097488172352314, + 0.02456829510629177, + 0.08276744186878204, + -0.02230781689286232, + -0.03785759210586548, + 0.010082835331559181, + 0.07151538133621216, + -0.04426267370581627, + 0.03402319550514221, + 0.05808456242084503, + 0.07566764950752258, + 0.07006501406431198, + 0.002404613886028528, + -0.10078012198209763, + 0.0029093879275023937, + -0.12370351701974869, + -0.03431423008441925, + 0.08334372937679291, + 0.001441179309040308, + -0.08001153916120529, + 0.020076438784599304, + 0.011177998036146164, + 0.034560561180114746, + 0.041609298437833786, + -0.04921843111515045, + 0.03598331660032272, + -0.10671709477901459, + 0.02969883382320404, + 0.048317357897758484, + 0.12375228852033615, + 0.040479566901922226, + 0.0330270379781723, + -0.06994733214378357, + 0.008698385208845139, + 0.03447363153100014, + -0.012882913462817669, + -0.02672695368528366, + -0.06423910707235336, + -0.0506032295525074, + 0.0053747911006212234, + 0.0682206079363823, + -0.005316274706274271, + 0.027013784274458885, + -0.006269444711506367, + 0.08528510481119156, + -0.0731915608048439, + 0.0046186731196939945, + -0.024244142696261406, + 0.0360478051006794, + 0.020591434091329575, + 0.04302601516246796, + -0.14392279088497162, + 0.05873512104153633, + -0.010028650052845478, + -0.04548479616641998, + -0.013535127975046635, + 0.01113649271428585, + 0.028603754937648773, + -0.04036158323287964, + 0.0704532265663147, + -0.09373150765895844, + -0.07198052108287811, + -0.002203285926952958, + -0.0855080857872963, + -0.08842256665229797, + -9.343179408460855e-05, + -0.029457710683345795, + -0.02197202481329441, + -0.048936717212200165, + 0.06165122613310814, + -0.0626755878329277, + 0.009602922946214676, + 0.07251632958650589, + 0.03113914094865322, + -0.0010769155342131853, + -0.040936876088380814, + -0.04328843951225281, + -0.020654935389757156, + 0.08143945783376694, + 0.03210373967885971, + 0.03849901258945465, + 0.08388370275497437, + -0.04882088676095009, + -0.04764509201049805, + -0.01594746671617031, + 0.08005915582180023, + -0.0007682700525037944, + 0.04356953501701355, + -0.08784972876310349, + -0.009003485552966595, + -0.012332507409155369, + -3.9398202034135466e-08, + 0.006421600468456745, + 0.04334808140993118, + 0.0013437344459816813, + -0.009495558217167854, + -0.022657591849565506, + -0.012704327702522278, + 0.018703341484069824, + -0.07545771449804306, + -0.07807240635156631, + 0.0031038280576467514, + -0.023932253941893578, + 0.040701914578676224, + 0.10603249073028564, + 0.03773405775427818, + 0.05925403907895088, + -0.02083730883896351, + 0.052759915590286255, + 0.08195225894451141, + -0.057340435683727264, + -0.03538660705089569, + 0.04492119327187538, + -0.07101765275001526, + 0.02868317998945713, + -0.11165601015090942, + -0.03126508370041847, + -0.07953942567110062, + -0.022893071174621582, + -0.08839578926563263, + 0.00727836275473237, + -0.08685773611068726, + 0.026177138090133667, + 0.028831886127591133, + 0.03626682609319687, + -0.045381225645542145, + -0.013574030250310898, + 0.028583087027072906, + -0.004301569424569607, + 0.04326719045639038, + -0.023370176553726196, + 0.057897310703992844, + -0.029195263981819153, + -0.001789534231647849, + 0.032133687287569046, + 0.003419605316594243, + 0.028055502101778984, + -0.0038284522015601397, + -0.022835813462734222, + -0.07568305730819702, + 0.017481982707977295, + 0.013085611164569855, + 0.009357997216284275, + -0.030347391963005066, + -0.0020967889577150345, + 0.03544173017144203, + 0.004640925209969282, + 0.025202661752700806, + -0.0916307270526886, + -0.06341513246297836, + -0.017053432762622833, + 0.06747056543827057, + 0.0467485710978508, + -0.14364545047283173, + 0.024641912430524826, + -0.04414863884449005 + ], + "model": "Eva 291", + "pickup_zone": "POLYGON((-80.2433 25.8067, -80.1333 25.8067, -80.1333 25.6967, -80.2433 25.6967, -80.2433 25.8067))", + "price": 3400, + "store_location": "-80.1918,25.7617" + }, + { + "brand": "Noka Bikes", + "condition": "used", + "description": "Whether you want to try your hand at XC racing or are looking for a lively trail bike that's just as inspiring on the climbs as it is over rougher ground, the Wilder is one heck of a bike built specifically for short women. Both the frames and components have been tweaked to include a women\u2019s saddle, different bars and unique colourway.", + "description_embeddings": [ + 0.07420087605714798, + -0.01575474441051483, + 0.010670951567590237, + 0.07449527084827423, + 0.0060751838609576225, + 0.03211340680718422, + -0.008086569607257843, + 0.025549469515681267, + -0.07154879719018936, + 0.014914358966052532, + -0.030715830624103546, + -0.0064881909638643265, + -0.0071744490414857864, + -0.0495535172522068, + 0.03856685757637024, + 0.056132566183805466, + 0.10794447362422943, + -0.010181701742112637, + 0.0811777114868164, + 0.10135157406330109, + -0.0769873708486557, + -0.019640743732452393, + -0.028702793642878532, + 0.07654724270105362, + -0.07045057415962219, + -0.10104052722454071, + -0.004679638426750898, + -0.0027665267698466778, + 0.030919065698981285, + -0.05461210012435913, + -0.020669877529144287, + 0.031864751130342484, + 0.0835040882229805, + -0.05930671840906143, + -0.006361816544085741, + -0.044132985174655914, + 0.035945694893598557, + -0.0027891425415873528, + -0.07598478347063065, + -0.03105698712170124, + -0.059601303189992905, + -0.018435664474964142, + -0.03314891457557678, + 0.06877987831830978, + -0.016064301133155823, + 0.01545438077300787, + 0.056307148188352585, + -0.08382677286863327, + -0.05274924635887146, + 0.01139853335916996, + 0.10750263929367065, + -0.004042057786136866, + 0.003356053726747632, + -0.01083006989210844, + -0.06153054162859917, + -0.054990023374557495, + -0.1360272616147995, + 0.025806479156017303, + 0.043233320116996765, + -0.009591769427061081, + 0.04422037675976753, + 0.02436559833586216, + -0.0012137923622503877, + -0.015348327346146107, + 0.02425999939441681, + -0.03409525752067566, + -0.08487176895141602, + 0.017345253378152847, + 0.035498712211847305, + -0.05615586042404175, + 0.02745831571519375, + 0.04017677903175354, + -0.0936250239610672, + 0.048734042793512344, + 0.024287866428494453, + -0.030274393036961555, + 0.08800201117992401, + 0.0879313051700592, + -0.0550895519554615, + -0.0026407642289996147, + -0.09552258998155594, + -0.05870680510997772, + 0.09009534120559692, + 0.05674433708190918, + 0.05360940843820572, + -0.0023017164785414934, + 0.005036584101617336, + -0.017987212166190147, + -0.05129106715321541, + -0.0012550759129226208, + -0.05818512290716171, + 0.06725005060434341, + 0.02540011890232563, + 0.054589059203863144, + 0.01723511517047882, + -0.04752320423722267, + 0.10586173087358475, + 0.05530202388763428, + -0.03287632390856743, + 0.05258375406265259, + 0.01655220240354538, + 0.011849797330796719, + 0.032285332679748535, + 0.03760116547346115, + -0.017522728070616722, + -0.05457543954253197, + 0.006817341782152653, + -0.028400525450706482, + -0.0027318967040628195, + -0.04200681298971176, + -0.026171568781137466, + -0.03272707015275955, + 0.016305547207593918, + 0.01545319240540266, + 0.0056663742288947105, + -0.0977277085185051, + -0.0009383464348502457, + -0.005693590268492699, + 0.039240892976522446, + 0.01645808108150959, + -0.01950509287416935, + 0.007762903813272715, + -0.004406424704939127, + 0.016364052891731262, + -0.006125410553067923, + -0.029666034504771233, + 0.044946398586034775, + 1.8357034061219326e-33, + -0.053835414350032806, + 0.03495992720127106, + -0.05977822467684746, + -0.009059705771505833, + 0.004659880883991718, + 0.022712793201208115, + 0.04409413039684296, + -0.10094501823186874, + -0.08702167868614197, + 0.0066354661248624325, + 0.011878615245223045, + 0.0033176038414239883, + -0.012380938045680523, + 0.0010130798909813166, + 0.0644172728061676, + -0.07130810618400574, + -0.0045928386971354485, + -0.03375661373138428, + -0.022520871832966805, + 0.07504180818796158, + 0.009861051104962826, + 0.07191396504640579, + -0.014361198991537094, + 0.01449025422334671, + -0.010663183405995369, + -0.028888769447803497, + 0.09297508746385574, + 0.04386264830827713, + -0.09579017758369446, + 0.007718369830399752, + -0.11800525337457657, + -0.020133504644036293, + -0.0034684871789067984, + -0.03735410049557686, + 0.006975250784307718, + -0.012902614660561085, + 0.04201272875070572, + 0.04273228347301483, + -0.07133302837610245, + 0.09145014733076096, + 0.0048939259722828865, + -0.058044496923685074, + -0.017591776326298714, + -0.03502552956342697, + -0.05678534135222435, + 0.05471408739686012, + 0.09568087011575699, + 0.09486310184001923, + -0.045761823654174805, + 0.018188882619142532, + -0.12331638485193253, + -0.023129422217607498, + -0.05455191433429718, + 0.06732556223869324, + 0.048286039382219315, + -0.0014048831071704626, + 0.03308422118425369, + -0.009839850477874279, + -0.05661558359861374, + 0.03290552645921707, + 0.03661618381738663, + 0.0047610136680305, + -0.05254451557993889, + 0.006052205804735422, + -0.13153813779354095, + -0.006893055979162455, + 0.037368785589933395, + -0.03058353252708912, + -0.0017269864911213517, + -0.008851475082337856, + -0.0740399956703186, + 0.057353563606739044, + 0.12296456098556519, + 0.0413251556456089, + 0.09911972284317017, + 0.016817500814795494, + -0.048074446618556976, + 0.05658562108874321, + -0.012700358405709267, + -0.08663205802440643, + 0.0014393541496247053, + 0.00613820506259799, + -0.04446813836693764, + 0.04673916846513748, + -0.02757285162806511, + -0.05133393779397011, + -0.028082016855478287, + -0.04668722674250603, + -0.03849129378795624, + -0.047927819192409515, + -0.06503037363290787, + -0.08361010253429413, + 0.0393000952899456, + 0.04658354073762894, + 0.009724845178425312, + -2.0251938301584547e-33, + 0.07265108078718185, + -0.04307040572166443, + 0.04996086284518242, + 0.03520473092794418, + 0.1084771379828453, + 0.005833788774907589, + 0.046216338872909546, + -0.04042917117476463, + -0.04179416596889496, + 0.03598152473568916, + 0.04082870855927467, + -0.10612837225198746, + -0.010728210210800171, + 0.03610331192612648, + 0.027484672144055367, + -0.008130725473165512, + 0.026157474145293236, + -0.07420457899570465, + 0.025218356400728226, + -0.0898737907409668, + -0.02742879092693329, + 0.03535477817058563, + -0.062408916652202606, + -0.1569257229566574, + -0.018789052963256836, + 0.04845775291323662, + -0.015149657614529133, + -0.0018356313230469823, + -0.04590461030602455, + 0.023325689136981964, + -0.13069787621498108, + -0.01994006149470806, + -0.021577563136816025, + -0.0231519415974617, + -0.002478161361068487, + 0.022444186732172966, + -0.04681165888905525, + 0.02769998274743557, + 0.003438781714066863, + 0.03829822316765785, + 0.0243418887257576, + -0.020368436351418495, + -0.005010040942579508, + 0.04648580029606819, + 0.061260052025318146, + 0.03784928843379021, + 0.008547516539692879, + 0.027585946023464203, + 0.043608926236629486, + 0.047329846769571304, + 0.10433284938335419, + 0.0069401939399540424, + 0.043503858149051666, + 0.06499945372343063, + -0.01040438748896122, + -0.11596397310495377, + 0.06451629102230072, + 0.032956890761852264, + 0.0007023254293017089, + 0.0898808017373085, + -0.055529963225126266, + -0.006572310347110033, + -0.06089714914560318, + 0.022685443982481956, + -0.0013110691215842962, + -0.09440209716558456, + -0.03121936321258545, + -0.003408250631764531, + -0.08051110059022903, + 0.01802976056933403, + 0.032015636563301086, + -9.986010991269723e-05, + -0.011806532740592957, + 0.06818002462387085, + 0.028290249407291412, + -0.005312844179570675, + 0.06016719341278076, + 0.014397598803043365, + 0.07122596353292465, + 0.007864296436309814, + 0.03002011403441429, + -0.05413847416639328, + 0.09744291007518768, + -0.01965983398258686, + 0.04150940105319023, + 0.14307892322540283, + -0.11320102959871292, + 0.03503091260790825, + 0.003515399992465973, + -0.023677969351410866, + 0.03955819085240364, + 0.0534457303583622, + 0.01904974691569805, + 0.031267836689949036, + -0.018026702105998993, + -3.930426117904062e-08, + 0.0003057526773773134, + 0.013398992829024792, + -0.10113491117954254, + -0.016498351469635963, + -0.00534934364259243, + 0.017148228362202644, + -0.03217773512005806, + -0.030934104695916176, + -0.06334194540977478, + 0.07242853194475174, + -0.0047585368156433105, + -0.01996637135744095, + -0.0016463182400912046, + 0.01777566224336624, + -0.04115547239780426, + 0.02906038798391819, + 0.10273877531290054, + 0.022776247933506966, + 0.030065912753343582, + -0.014570299535989761, + 0.047994960099458694, + -0.044575825333595276, + 0.025753378868103027, + -0.002644166350364685, + -0.09429919719696045, + -0.11280428618192673, + 0.021306486800312996, + 0.010426397435367107, + 0.021093064919114113, + -0.00363176385872066, + -0.024409662932157516, + 0.11473581194877625, + 0.0020395806059241295, + -0.08371622860431671, + -0.03764123469591141, + 0.039440982043743134, + -0.022172994911670685, + 0.0410802997648716, + -0.05086303874850273, + 0.053952641785144806, + -0.06084167957305908, + -0.036132071167230606, + 0.02080748789012432, + -0.043120622634887695, + 0.04048219695687294, + 0.05917482078075409, + 0.04085611552000046, + -0.05118294060230255, + 0.015727631747722626, + 0.01915580965578556, + 0.03531794995069504, + -0.058007121086120605, + 0.08498086780309677, + 0.0036822939291596413, + 0.018125539645552635, + 0.10145474225282669, + -0.05804500728845596, + -0.008451723493635654, + -0.029611116275191307, + 0.008798911236226559, + -0.03220565244555473, + -0.05617227777838707, + 0.03482669219374657, + -0.03508705273270607 + ], + "model": "Kahuna", + "pickup_zone": "POLYGON((-122.4644 37.8199, -122.3544 37.8199, -122.3544 37.7099, -122.4644 37.7099, -122.4644 37.8199))", + "price": 3200, + "store_location": "-122.4194,37.7749" + }, + { + "brand": "Breakout", + "condition": "new", + "description": "The XBN 2.1 Alloy is our entry-level road bike \u2013 but that\u2019s not to say that it\u2019s a basic machine. With an internal weld aluminium frame, a full carbon fork, and the slick-shifting Claris gears from Shimano\u2019s, this is a bike which doesn\u2019t break the bank and delivers craved performance.", + "description_embeddings": [ + -0.03482655808329582, + -0.024290302768349648, + 0.029588153585791588, + -0.04068901762366295, + 0.006173150148242712, + -0.06071849912405014, + 0.021348219364881516, + 0.032668184489011765, + -0.12000397592782974, + -0.026135660707950592, + -0.0262606181204319, + -0.014927615411579609, + 0.06986956298351288, + -0.006424048915505409, + -0.002834598533809185, + -0.005902229342609644, + 0.07471967488527298, + -0.10197333246469498, + 0.08836513012647629, + 0.0504477359354496, + 0.005102860741317272, + 0.0359189435839653, + -0.0477643720805645, + 0.02106260135769844, + 0.030146656557917595, + 0.04048784077167511, + -0.023416390642523766, + -0.0016326266340911388, + 0.039820387959480286, + -0.04475116729736328, + -0.03422572463750839, + 0.09266746789216995, + -0.04106999188661575, + -0.02442021667957306, + -0.006325263995677233, + -0.057226430624723434, + 0.12074039876461029, + 0.012658749707043171, + -0.09748225659132004, + -0.048439353704452515, + -0.004550903104245663, + -0.014449959620833397, + 0.03684601932764053, + 0.018772389739751816, + 0.09577438980340958, + 0.04358510673046112, + 0.06987427175045013, + -0.0561334528028965, + -0.03684744983911514, + 0.02584502473473549, + 0.039536770433187485, + -0.061133887618780136, + 0.05351594462990761, + -0.025124184787273407, + 0.09431525319814682, + 0.05062168091535568, + -0.11715397238731384, + 0.044368330389261246, + -0.024262988939881325, + -0.024561986327171326, + 0.036067184060811996, + 0.025171849876642227, + 0.033648427575826645, + 0.046933531761169434, + 0.09548858553171158, + -0.06253167241811752, + -0.045195333659648895, + -0.04183578118681908, + -0.040385909378528595, + -0.027590686455368996, + 0.08240272104740143, + -0.04743463918566704, + 0.004482025280594826, + 0.03219064325094223, + 0.0007497097249142826, + -0.04202196002006531, + 0.10904812812805176, + -0.06721051037311554, + -0.023717032745480537, + -0.01718866638839245, + -0.11568725854158401, + 0.038704562932252884, + 0.04929516091942787, + -0.03311185538768768, + -0.0017465045675635338, + -0.06153444945812225, + 0.019559966400265694, + -0.010708335787057877, + -0.06623212993144989, + 0.03114122338593006, + 0.03581896796822548, + 0.05368824675679207, + 0.016093581914901733, + -0.0077278027310967445, + 0.025581376627087593, + 0.02375067211687565, + -0.024797597900032997, + 0.08371419459581375, + -0.023117447271943092, + 0.04535554349422455, + -0.007224411237984896, + 0.06131899356842041, + 0.02283627539873123, + -0.052097078412771225, + -0.07922855019569397, + 0.0016958570340648293, + 0.051785532385110855, + 0.06701217591762543, + -0.02913537621498108, + 0.020526740700006485, + 0.03941992297768593, + -0.02217993699014187, + -0.08051256835460663, + -0.10155311226844788, + -0.05453299731016159, + -0.06470758467912674, + 0.002029003808274865, + -0.013401892967522144, + -0.011006158776581287, + 0.045922696590423584, + -0.040529411286115646, + 0.00991761963814497, + -0.06682797521352768, + 0.05892198532819748, + -0.028617633506655693, + -0.06339140236377716, + 0.0044001322239637375, + -4.362402064591546e-33, + -0.10147389024496078, + 0.039431530982255936, + -0.04599899426102638, + -0.049156975001096725, + -0.02249804697930813, + -0.026725362986326218, + 0.04369090870022774, + -0.0005211823736317456, + -0.030739039182662964, + -0.0065231663174927235, + 0.025333819910883904, + 0.10470504313707352, + 0.021332278847694397, + 0.05556178465485573, + 0.035125020891427994, + -0.1538446545600891, + 0.007054235320538282, + -0.036041803658008575, + 0.05932909622788429, + 0.0337519533932209, + 0.014841740019619465, + 0.030220910906791687, + 0.04296395927667618, + 0.02538421005010605, + -0.006869863253086805, + -0.0031259972602128983, + 0.11390665918588638, + -0.046557214111089706, + 0.00429841224104166, + 0.0428440123796463, + -0.1384042203426361, + 0.04695465788245201, + -0.0574827715754509, + -0.030760489404201508, + -0.07478123158216476, + -0.029447706416249275, + -0.06204935908317566, + -0.03058161959052086, + -0.02502700313925743, + -0.019530421122908592, + -0.02094511315226555, + -0.03218250721693039, + -0.05274674668908119, + -0.02680223248898983, + -0.01955571211874485, + 0.018619371578097343, + 0.007585515268146992, + 0.07155954837799072, + -0.018960801884531975, + -0.05782055854797363, + 0.017819860950112343, + 0.04109930992126465, + -0.03562675416469574, + 0.016705146059393883, + 0.0415029413998127, + 0.05103381723165512, + 0.035572972148656845, + -0.015020242892205715, + -0.03179502114653587, + 0.0891512930393219, + 0.007475084625184536, + 0.042234599590301514, + -0.0542532242834568, + 0.05480321869254112, + -0.10602187365293503, + 0.054763536900281906, + 0.04739809036254883, + -0.03327919542789459, + 0.0082072913646698, + -0.0650608167052269, + -0.08271145075559616, + -0.10922762006521225, + 0.03824940696358681, + 0.05978637561202049, + 0.10005365312099457, + 0.029007570818066597, + -0.034110404551029205, + 0.02545103058218956, + 0.05190473049879074, + -0.003596875350922346, + -0.08473207801580429, + 0.012021052651107311, + 0.018175840377807617, + 0.017920689657330513, + -0.03348258137702942, + 0.04048585146665573, + 0.030843179672956467, + 0.019531769677996635, + -0.00910205114632845, + 0.05721440538764, + 0.007200753781944513, + -0.11394353210926056, + 0.03483451530337334, + 0.029621547088027, + -0.03755185008049011, + 9.393710556981876e-34, + -0.009642334654927254, + -0.001342272269539535, + 0.01180787943303585, + 0.026348579674959183, + -0.011158440262079239, + -0.0019478596514090896, + 0.028490403667092323, + -0.052807874977588654, + -0.029139718040823936, + 0.08380713313817978, + 0.09657946228981018, + 0.00991911068558693, + 0.03502354025840759, + -0.016754556447267532, + 0.017460109665989876, + -0.01642783358693123, + 0.022211389616131783, + -0.008778599090874195, + 3.654864485724829e-05, + -0.09229966253042221, + 0.04083291441202164, + 0.08299239724874496, + -0.04151370748877525, + -0.05360836163163185, + -0.07950830459594727, + 0.03959093242883682, + -0.13340364396572113, + 0.054901909083127975, + 0.05736061558127403, + 0.04770279303193092, + -0.11388063430786133, + 0.0237369854003191, + 0.02305987849831581, + -0.013226242735981941, + -0.020716888830065727, + 0.03368164971470833, + -0.002396275522187352, + -0.012604329735040665, + -0.02715742215514183, + 0.0019254887010902166, + -0.00887741707265377, + -0.028728270903229713, + -0.05864499509334564, + 0.10937061905860901, + -0.004027256276458502, + 0.019197454676032066, + -0.03928225114941597, + -0.018220100551843643, + 0.02466396614909172, + 0.00980517640709877, + 0.05513712763786316, + 0.10625418275594711, + 0.08366440236568451, + -0.02691512741148472, + -0.026599230244755745, + -0.049529410898685455, + -0.05376818776130676, + -0.003328752936795354, + -0.04345694184303284, + 0.11166279017925262, + 0.0407760851085186, + -0.00792837142944336, + 0.03168283402919769, + -0.0018123856279999018, + 0.0357111431658268, + -0.1368872970342636, + 0.00755245191976428, + -0.010281031019985676, + -0.07121222466230392, + 0.03101137839257717, + 0.0662078782916069, + 0.007085337769240141, + -0.007063422352075577, + -0.05238816514611244, + -0.004185437690466642, + 0.00880429521203041, + 0.05528423562645912, + -0.08964741230010986, + 0.0042182342149317265, + -0.029020531103014946, + 0.014597366563975811, + 0.0002752764557953924, + 0.032267484813928604, + 0.11250412464141846, + 0.002768452512100339, + -0.026546282693743706, + 0.017761094495654106, + 0.023686127737164497, + -0.012699384242296219, + 0.018606871366500854, + -0.009665136225521564, + -0.06902427226305008, + -0.04932688549160957, + 0.051991984248161316, + -0.05918996408581734, + -4.18107468647122e-08, + -0.06375139951705933, + -0.0029491656459867954, + 0.032401617616415024, + -0.024946069344878197, + -0.026347795501351357, + 0.020192604511976242, + -0.025759287178516388, + 0.037004951387643814, + 0.043629322201013565, + 0.06278910487890244, + 0.02097572200000286, + -0.07064115256071091, + -0.005097216460853815, + 0.016486743465065956, + -0.0032796994782984257, + 0.07957274466753006, + 0.03776371479034424, + 0.04259084165096283, + -0.005312138702720404, + -0.11992045491933823, + 0.0859612300992012, + -0.0500827394425869, + 0.02553708106279373, + -0.052115052938461304, + -0.07368568331003189, + -0.11394031345844269, + -0.04513951390981674, + 0.022121727466583252, + -0.010451988317072392, + 0.003997989930212498, + -0.13713733851909637, + 0.030619636178016663, + 0.05786789208650589, + 0.05840904265642166, + 0.04732450470328331, + -0.012547117657959461, + -0.027409126982092857, + 0.049672048538923264, + -0.00018399256805423647, + -0.004273589234799147, + -0.011101006530225277, + 0.017240682616829872, + -0.002812016289681196, + 0.034935809671878815, + 0.006601597648113966, + 0.014211715199053288, + -0.06332860141992569, + -0.05397455021739006, + -0.038572490215301514, + -0.06450864672660828, + 0.07963147014379501, + 0.029951799660921097, + 0.03153093531727791, + -0.03852103650569916, + -0.025755248963832855, + 0.13077980279922485, + -0.08185151219367981, + -0.09442253410816193, + 0.01002975832670927, + 0.02509395219385624, + -0.029981056228280067, + -0.025097224861383438, + 0.04001101851463318, + 0.006130080670118332 + ], + "model": "XBN 2.1 Alloy", + "pickup_zone": "POLYGON((-0.1778 51.5524, 0.0822 51.5524, 0.0822 51.4024, -0.1778 51.4024, -0.1778 51.5524))", + "price": 810, + "store_location": "-0.1278,51.5074" + }, + { + "brand": "ScramBikes", + "condition": "new", + "description": "The WattBike is the best e-bike for people who still feel young at heart. It has a Bafang 1000W mid-drive system and a 48V 17.5AH Samsung Lithium-Ion battery, allowing you to ride for more than 60 miles on one charge. It\u2019s great for tackling hilly terrain or if you just fancy a more leisurely ride. With three working modes, you can choose between E-bike, assisted bicycle, and normal bike modes.", + "description_embeddings": [ + -0.006927311420440674, + 0.15900678932666779, + -0.005495064426213503, + 0.06652409583330154, + 0.027506737038493156, + 0.03928178921341896, + 0.0212758406996727, + 0.08142763376235962, + -0.004556896630674601, + 0.04261007905006409, + 0.0852041020989418, + -0.048658017069101334, + 0.04896014556288719, + 0.008289206773042679, + 0.09533551335334778, + 0.042673129588365555, + 0.11990982294082642, + -0.09381455183029175, + -0.001154645229689777, + -0.009989846497774124, + 0.042972203344106674, + 0.055328626185655594, + 0.044754382222890854, + 0.01764347031712532, + 0.04503790661692619, + -0.003830699948593974, + -0.03431522846221924, + 0.03080279380083084, + -0.07722456008195877, + -0.09261710941791534, + 0.04810558632016182, + 0.04917953535914421, + 0.027227703481912613, + -0.04199009761214256, + -0.11837311834096909, + 0.028090154752135277, + 0.08678824454545975, + -0.052036624401807785, + -0.06585966050624847, + -0.04037070646882057, + -0.08040877431631088, + 0.0018185328226536512, + 0.043989114463329315, + -0.011817573569715023, + 0.006730342749506235, + -0.011594205163419247, + 0.008361537009477615, + -0.054372794926166534, + 0.033345889300107956, + -0.03933562710881233, + 0.10115987062454224, + -0.08934503048658371, + 0.04519934952259064, + 0.03582148626446724, + -0.021424520760774612, + -0.07357319444417953, + -0.07426925748586655, + 0.018053170293569565, + 0.04639451205730438, + -0.07499486207962036, + 0.057791586965322495, + -0.05478687211871147, + 0.030340412631630898, + -0.0056327772326767445, + -0.0452428013086319, + -0.044913534075021744, + 0.0526629202067852, + -0.08259481936693192, + -0.05616062134504318, + 0.0005400424124673009, + -0.04933836683630943, + 1.7464293705415912e-05, + -0.007984945550560951, + 0.009467384777963161, + -0.04055757448077202, + -0.0056908270344138145, + 0.036597516387701035, + 0.023873118683695793, + 0.00418807053938508, + 0.04676923528313637, + -0.053795333951711655, + -0.015527212992310524, + -0.080276258289814, + 0.026996882632374763, + 0.1144171804189682, + -0.011178486980497837, + 0.06854862719774246, + 0.022201502695679665, + -0.01913747377693653, + 0.009713435545563698, + 0.01423699501901865, + 0.04772024229168892, + 0.034634001553058624, + 0.0756441131234169, + -0.01204933412373066, + -0.031088251620531082, + -0.02462930977344513, + -0.04386857897043228, + -0.07706759870052338, + -0.002071107504889369, + -0.019828464835882187, + 0.10314348340034485, + 0.07235224545001984, + 0.03167572245001793, + -0.02631053328514099, + -0.036559153348207474, + 0.012040914967656136, + 0.08063311874866486, + 0.008269569836556911, + -0.012434666976332664, + 0.00650354428216815, + -0.05601133033633232, + -0.028117282316088676, + -0.025071244686841965, + -0.01907099038362503, + 0.003760937135666609, + -0.052525606006383896, + 0.07173370569944382, + 0.09372185170650482, + 0.1006019189953804, + 0.023999499157071114, + 0.041847433894872665, + 0.02114962227642536, + 0.05726422369480133, + 0.008218048140406609, + -0.06967302411794662, + 0.01968124881386757, + 1.844028477519647e-33, + -0.031731415539979935, + 0.03648626059293747, + -0.026035945862531662, + -0.026796391233801842, + 0.009511047042906284, + 0.050696976482868195, + -0.03008556365966797, + -0.027142174541950226, + -0.07182826101779938, + -0.02520623430609703, + -0.005983793176710606, + 0.07632830739021301, + 0.09301365166902542, + 0.0744069442152977, + 0.10173527896404266, + -0.08317957073450089, + -0.06219454109668732, + -0.0776224136352539, + 0.013563201762735844, + 0.005839265417307615, + 0.011761599220335484, + -0.0701083242893219, + 0.005647188518196344, + -0.007439272943884134, + -4.4855809392174706e-05, + -0.05095696449279785, + 0.13620494306087494, + 0.017342044040560722, + -0.01352923084050417, + 0.03086051531136036, + -0.0740780308842659, + -0.06721310317516327, + -0.06769067794084549, + 0.01185466069728136, + -0.07234086841344833, + 0.0020215404219925404, + -0.0108563881367445, + 0.007788154762238264, + 0.07912690192461014, + -0.02952556498348713, + -0.010350959375500679, + 0.025074176490306854, + -0.050776757299900055, + 0.00947178341448307, + 0.03259417414665222, + 0.0319671556353569, + 0.0379379540681839, + 0.05242982879281044, + -0.07516643404960632, + 0.020074544474482536, + -0.08712191134691238, + -0.01204316969960928, + -0.006554455496370792, + -0.005875407252460718, + -0.06922540068626404, + 0.041081465780735016, + -0.003257623640820384, + 0.0391659140586853, + -0.03731334209442139, + -0.03579063341021538, + -0.04008869081735611, + 0.05648549646139145, + -0.01142149232327938, + -0.05515728518366814, + -0.06406072527170181, + -0.013155528344213963, + 0.0029902313835918903, + -0.020829875022172928, + -0.13008210062980652, + -0.039412979036569595, + 0.05974568799138069, + -0.06617670506238937, + 0.09968123584985733, + -0.04378099367022514, + 0.008272947743535042, + -0.007245620246976614, + -0.05228377878665924, + -0.05929296463727951, + -0.07816570997238159, + 0.04665905237197876, + -0.01630396395921707, + -0.020880484953522682, + -0.06532212346792221, + -0.003213261254131794, + 0.0416768379509449, + -0.05093088001012802, + -0.0871049091219902, + -0.014571009203791618, + 0.0010796786518767476, + 0.007087667006999254, + 0.05550219491124153, + -0.07753361761569977, + 0.022848013788461685, + 0.02596968039870262, + 0.04259824752807617, + -2.380165022444961e-33, + 0.05820159241557121, + 0.046025633811950684, + 0.10894608497619629, + 0.05244075506925583, + 0.15792003273963928, + 0.024086199700832367, + 0.029237573966383934, + -0.023072607815265656, + -0.07495220005512238, + -0.013094011694192886, + -0.004260784015059471, + -0.053080882877111435, + -0.00895928218960762, + -0.03149857744574547, + 0.13309381902217865, + 0.038643430918455124, + -0.02136029675602913, + -0.030247559770941734, + 0.08822915703058243, + -0.026076463982462883, + 0.050446517765522, + 0.011856893077492714, + -0.06957101821899414, + -0.02773614600300789, + 0.04625667631626129, + 0.008786994963884354, + -0.07701855897903442, + 0.001076446962542832, + -0.004858669359236956, + 0.0067254831083118916, + -0.02791707031428814, + -0.019495468586683273, + 0.0703381896018982, + 0.030558260157704353, + -0.05546342208981514, + 0.0420801043510437, + -0.03730127215385437, + -0.01322201732546091, + 0.038336288183927536, + 0.06427455693483353, + 0.028238974511623383, + -0.06713984161615372, + 0.03150900453329086, + 0.024600861594080925, + 0.016221575438976288, + 0.025838203728199005, + -0.02313261851668358, + 0.0557485967874527, + 0.015211678110063076, + 0.029895616695284843, + 0.06008606031537056, + 0.04473312199115753, + -0.050083715468645096, + -0.017571061849594116, + -0.014091495424509048, + -0.06641074270009995, + -0.008071008138358593, + 0.03312089666724205, + -0.09482058882713318, + -0.08763624727725983, + 0.030528157949447632, + 0.013274747878313065, + 0.06158939003944397, + 0.0748405009508133, + -0.12128522247076035, + -0.12076683342456818, + 0.0464591421186924, + 0.0045278542675077915, + -0.002574144396930933, + -0.028266169130802155, + -0.04953430965542793, + -0.007604515179991722, + -0.01084962673485279, + -0.06370151042938232, + 0.00812828354537487, + -0.027805447578430176, + 0.044624149799346924, + 0.016610005870461464, + -0.027103105559945107, + -0.046117331832647324, + -0.002852817066013813, + 0.05479831248521805, + 0.05342277139425278, + -0.03752776235342026, + -0.0053502353839576244, + -0.018950853496789932, + -0.07712738960981369, + -0.13029317557811737, + -0.013171681202948093, + 0.02069253847002983, + 0.029317859560251236, + 0.018668048083782196, + -0.061371881514787674, + 0.05376929044723511, + 0.01483837515115738, + -3.964297690117746e-08, + 0.03559036925435066, + -0.005289694294333458, + -0.013635152950882912, + -0.020672710612416267, + 0.039649732410907745, + -0.06206038221716881, + 0.04933065176010132, + 0.010378899984061718, + 0.020493512973189354, + 0.027486076578497887, + 0.07419533282518387, + -0.03473059833049774, + 0.05251400172710419, + 0.013588557951152325, + 0.01814397983253002, + -0.024619124829769135, + 0.11751414835453033, + 0.05483951419591904, + 0.022471528500318527, + 0.0301313828676939, + 0.018649602308869362, + -0.06219587102532387, + 0.04238469526171684, + -0.053293377161026, + 0.002427657600492239, + -0.0019911346025764942, + 0.004944113548845053, + -0.061433035880327225, + -0.013191037811338902, + -0.030179856345057487, + -0.051340680569410324, + 0.022094713523983955, + -0.035341646522283554, + -0.007219062652438879, + -0.06182244420051575, + -0.06059279292821884, + -0.041057273745536804, + 0.002089729532599449, + -0.043397895991802216, + 0.07170896232128143, + 0.036471735686063766, + -0.015439609996974468, + -0.01848314143717289, + 0.0056609525345265865, + -0.0012752920156344771, + -0.056571539491415024, + -0.01979043148458004, + -0.12011151015758514, + -0.02223217859864235, + 0.029830070212483406, + 0.027551136910915375, + -0.0010704555315896869, + 0.000702365068718791, + -0.005173679441213608, + -0.019360698759555817, + 0.17009279131889343, + -0.04601005092263222, + -0.04016156122088432, + -0.01600208878517151, + 0.04198170080780983, + 0.02032368630170822, + -0.06263051927089691, + -0.048464443534612656, + 0.009351130574941635 + ], + "model": "WattBike", + "pickup_zone": "POLYGON((2.1767 48.9016, 2.5267 48.9016, 2.5267 48.5516, 2.1767 48.5516, 2.1767 48.9016))", + "price": 2300, + "store_location": "2.3522,48.8566" + }, + { + "brand": "Peaknetic", + "condition": "new", + "description": "If you struggle with stiff fingers or a kinked neck or back after a few minutes on the road, this lightweight, aluminum bike alleviates those issues and allows you to enjoy the ride. From the ergonomic grips to the lumbar-supporting seat position, the Roll Low-Entry offers incredible comfort. The rear-inclined seat tube facilitates stability by allowing you to put a foot on the ground to balance at a stop, and the low step-over frame makes it accessible for all ability and mobility levels. The saddle is very soft, with a wide back to support your hip joints and a cutout in the center to redistribute that pressure. Rim brakes deliver satisfactory braking control, and the wide tires provide a smooth, stable ride on paved roads and gravel. Rack and fender mounts facilitate setting up the Roll Low-Entry as your preferred commuter, and the BMX-like handlebar offers space for mounting a flashlight, bell, or phone holder.", + "description_embeddings": [ + 0.0022135202307254076, + 0.0681997612118721, + 0.0064607178792357445, + 0.007070810534060001, + -0.054231829941272736, + 0.008059866726398468, + 0.040072083473205566, + 0.0956423208117485, + 0.049143481999635696, + 0.0379914790391922, + -0.020757146179676056, + 0.06460786610841751, + 0.07982552796602249, + -0.0321788415312767, + -0.006194670218974352, + 0.0375053696334362, + 0.1270381063222885, + -0.02341461181640625, + -0.007247396744787693, + 0.0682845488190651, + -0.026470346376299858, + 0.005658577661961317, + 0.04653697833418846, + 0.03987714648246765, + -0.12344618886709213, + 0.03188862279057503, + -0.02708076685667038, + 0.06964577734470367, + 0.02201199159026146, + -0.02890665829181671, + -0.040280576795339584, + 0.06318672746419907, + 0.07309666275978088, + -0.08932560682296753, + -0.11648543179035187, + -0.029937371611595154, + 0.10487373173236847, + 0.041655637323856354, + -0.022871389985084534, + -0.04677774757146835, + 0.02746077999472618, + 0.01847464218735695, + 0.08173345029354095, + 0.06561841815710068, + 0.05970228090882301, + -0.024480478838086128, + 0.06907600909471512, + -0.03947169706225395, + 0.024855393916368484, + -0.013199429027736187, + 0.09996063262224197, + 0.0028578736819326878, + 0.08506831526756287, + 0.014810853637754917, + -0.03929787874221802, + 0.0018542942125350237, + -0.14215077459812164, + 0.06970464438199997, + 0.0265016071498394, + -0.06698428839445114, + 0.07732488960027695, + -0.04583248123526573, + 0.028696633875370026, + 0.013155206106603146, + 0.02578958310186863, + -0.029291296377778053, + -0.00031816380214877427, + -0.125708669424057, + -0.042060736566782, + -0.05381627008318901, + -0.0538906455039978, + 0.0014778936747461557, + -0.09889215975999832, + -0.0429794080555439, + -0.013458915054798126, + -0.04558553919196129, + 0.09005370736122131, + -0.04213777929544449, + -0.1349189579486847, + 0.055852413177490234, + 0.006197084207087755, + 0.016338912770152092, + 0.032463233917951584, + 0.02548987604677677, + 0.030681701377034187, + -0.0703219398856163, + 0.05395807698369026, + -0.009628057479858398, + -0.031475286930799484, + -0.027579376474022865, + 0.04098684713244438, + 0.03901920095086098, + -0.01192146260291338, + -0.04101356491446495, + -0.08753280341625214, + 0.0705665871500969, + 0.03578994423151016, + 0.032069701701402664, + -0.03211764618754387, + 0.09590566158294678, + 0.0611797571182251, + 0.004700345452874899, + 0.10438291728496552, + 0.07409676164388657, + -0.0090691689401865, + 0.011407091282308102, + 0.05370165407657623, + -0.020438825711607933, + -0.04398776963353157, + 0.032895661890506744, + -0.0012730252929031849, + 0.018297234550118446, + -0.04333885386586189, + 0.06208101287484169, + -0.08955910801887512, + -0.04672509804368019, + -0.06376977264881134, + 0.022537674754858017, + 0.02881341427564621, + -0.0030273371376097202, + -0.06542699784040451, + 0.007231372408568859, + 0.01855703443288803, + 0.012365416623651981, + 0.018434280529618263, + -0.04855069890618324, + -0.08907819539308548, + -2.615521077916409e-34, + -0.046131156384944916, + -0.028526470065116882, + 0.01342072058469057, + -0.010418130084872246, + -0.019669201225042343, + -0.07430055737495422, + -0.006000629626214504, + -0.03320513665676117, + -0.05303066596388817, + 0.07680810242891312, + -0.0061429338529706, + 0.06154831871390343, + 0.09432042390108109, + 0.0038354273419827223, + 0.14506474137306213, + -0.07032876461744308, + -0.07443196326494217, + -0.04439792409539223, + -0.005104243289679289, + -3.3934433304239064e-05, + -0.08257872611284256, + -0.056131236255168915, + 0.04174238070845604, + 0.0017745267832651734, + -0.002772972686216235, + 0.006998051423579454, + 0.09371285885572433, + -0.013138788752257824, + -0.042773280292749405, + 0.00896062795072794, + -0.09441300481557846, + -0.007952050305902958, + -0.015586148016154766, + -0.017506571486592293, + 0.01347337942570448, + 0.027937229722738266, + -0.11657726019620895, + -0.03324989974498749, + -0.018427638337016106, + -0.04819897934794426, + -0.04077771306037903, + -0.0001697312545729801, + -0.05697356536984444, + 0.07191669940948486, + 0.017228955402970314, + 0.03711475431919098, + 0.042057864367961884, + 0.004786928184330463, + -0.06956200301647186, + 0.06036132574081421, + -0.0016305814497172832, + 0.02468821406364441, + -0.013794313184916973, + -0.025994934141635895, + -0.07489914447069168, + 0.01067660003900528, + -0.010298662818968296, + -0.022154971957206726, + -0.09746527671813965, + 0.06335768103599548, + -0.022728655487298965, + -0.0005628599901683629, + -0.04313022270798683, + -0.04123309254646301, + -0.08378659188747406, + 0.020546691492199898, + 0.01680913008749485, + -0.010155763477087021, + 0.008547178469598293, + -0.0211178008466959, + 0.023050235584378242, + 0.06479462236166, + 0.061528537422418594, + 0.022156352177262306, + 0.019619867205619812, + 0.07456795871257782, + 0.049971628934144974, + -0.07878398895263672, + 0.04972408711910248, + -0.015442566946148872, + 0.044758349657058716, + -0.037393294274806976, + -0.03831558674573898, + -0.016534404829144478, + -0.05692937225103378, + 0.02438316121697426, + -0.008604591712355614, + -0.06003899499773979, + 0.04884570837020874, + -0.05407913774251938, + -0.004864877089858055, + -0.039421387016773224, + -0.0335439033806324, + 0.05149518698453903, + -0.00761334178969264, + -4.919814414418271e-34, + 0.0605306513607502, + 0.013155259191989899, + 0.0022042335476726294, + -0.016238724812865257, + 0.017820321023464203, + -0.029504502192139626, + 0.1179562360048294, + -0.08494631201028824, + -0.05847567319869995, + -0.03412671759724617, + 0.01584434323012829, + 0.0205944012850523, + 0.011703073978424072, + 0.024820921942591667, + 0.07683313637971878, + 0.016703670844435692, + -0.0465223602950573, + -0.08929521590471268, + 0.013778958469629288, + -0.06448621302843094, + 0.047237128019332886, + 0.11140323430299759, + -0.036479681730270386, + -0.055252984166145325, + -0.08155377954244614, + -0.0011454259511083364, + -0.07414430379867554, + 0.04839516803622246, + -0.036127883940935135, + 0.04249665141105652, + -0.05564387887716293, + 0.008439254947006702, + 0.056384216994047165, + 0.0037136792670935392, + -0.06267311424016953, + 0.06599505990743637, + -0.0944608598947525, + -0.01900491677224636, + 0.0017078104428946972, + 0.018181079998612404, + 0.0072936019860208035, + 0.03901578113436699, + 0.022009696811437607, + -0.02042953297495842, + 0.04422936588525772, + 0.099826380610466, + 0.0022175773046910763, + 0.05569764971733093, + 0.005229232832789421, + -0.06281892955303192, + 0.06203164905309677, + 0.05493532121181488, + 0.06598878651857376, + 0.06272768974304199, + 0.05623844638466835, + -0.053184594959020615, + 0.012609804049134254, + -0.009191329590976238, + -0.07956400513648987, + -0.005095178727060556, + -0.06295286118984222, + 0.05799472704529762, + 0.004298996180295944, + -0.0245245061814785, + -0.010027579963207245, + -0.02338206022977829, + -0.03576310724020004, + -0.0735088586807251, + -0.10477573424577713, + 0.010318689979612827, + -0.031293049454689026, + -0.06913845986127853, + 0.07762496173381805, + -0.029345618560910225, + 0.03347797319293022, + 0.014305013231933117, + 0.06321006268262863, + -0.09872464835643768, + -0.040937263518571854, + 0.028350593522191048, + -0.08784928917884827, + 0.005375882610678673, + 0.059733789414167404, + 0.027322115376591682, + -0.04657347500324249, + 0.02783248759806156, + -0.12815773487091064, + 0.004414730705320835, + -0.009058866649866104, + 0.06062375009059906, + 0.04072198644280434, + 0.05082780495285988, + -0.036888547241687775, + 0.07301440089941025, + 0.022250277921557426, + -4.836826761334123e-08, + -0.02333931438624859, + -0.0006597689352929592, + -0.011001646518707275, + 0.01690472476184368, + -0.0037487675435841084, + 0.039681874215602875, + -0.012263616546988487, + 0.010128140449523926, + 0.03165998309850693, + 0.0028211181052029133, + 0.009292887523770332, + -0.05068610981106758, + -0.05350435897707939, + 0.0027450143825262785, + -0.02869512513279915, + 0.18523184955120087, + 0.010015024803578854, + 0.04042758792638779, + 0.010014397092163563, + -0.07638011872768402, + -0.04126984626054764, + -0.06051325798034668, + 0.04609273001551628, + -0.00863336119800806, + -0.010159372352063656, + -0.0686831921339035, + -0.022274712100625038, + 0.054596077650785446, + -0.004217579495161772, + 0.005010589957237244, + -0.0023027928546071053, + 0.054350487887859344, + 0.051168326288461685, + -0.0005977987311780453, + 0.0008231411338783801, + -0.005349422339349985, + -0.04023698344826698, + 0.01875622756779194, + 0.051792532205581665, + 0.0725129023194313, + 0.003990984987467527, + -0.0412585511803627, + -0.030975697562098503, + 0.02229507826268673, + -0.023536084219813347, + 0.004254089202731848, + -0.047596003860235214, + 0.054139409214258194, + 0.024750487878918648, + 0.00830126740038395, + 0.04204926639795303, + -0.05580601468682289, + 0.014396552927792072, + 0.0785103291273117, + -0.01755904220044613, + 0.09679318964481354, + -0.01722336746752262, + -0.03680667281150818, + -0.021823544055223465, + 0.044612541794776917, + -0.0008680447936058044, + 0.027066197246313095, + -0.008826435543596745, + -0.034606464207172394 + ], + "model": "Secto", + "pickup_zone": "POLYGON((13.3260 52.5700, 13.6550 52.5700, 13.6550 52.2700, 13.3260 52.2700, 13.3260 52.5700))", + "price": 430, + "store_location": "13.4050,52.5200" + }, + { + "brand": "nHill", + "condition": "new", + "description": "This budget mountain bike from nHill performs well both on bike paths and on the trail. The fork with 100mm of travel absorbs rough terrain. Fat Kenda Booster tires give you grip in corners and on wet trails. The Shimano Tourney drivetrain offered enough gears for finding a comfortable pace to ride uphill, and the Tektro hydraulic disc brakes break smoothly. Whether you want an affordable bike that you can take to work, but also take trail in mountains on the weekends or you\u2019re just after a stable, comfortable ride for the bike path, the Summit gives a good value for money.", + "description_embeddings": [ + -0.024333441630005836, + 0.029308300465345383, + -0.012847754172980785, + -0.013123984448611736, + -0.039905961602926254, + -0.04596070945262909, + -0.01287133153527975, + 0.03984961286187172, + -0.043438445776700974, + 0.012349214404821396, + -0.021342391148209572, + -0.027458613738417625, + -0.004793129395693541, + 0.016479987651109695, + 0.02052542380988598, + -0.009542089886963367, + 0.10066469013690948, + 0.017397526651620865, + 0.07893014699220657, + -0.0765308141708374, + -0.007650941610336304, + 9.538845188217238e-05, + 0.03314477205276489, + 0.026631362736225128, + -0.01621824875473976, + 0.03593063727021217, + -0.014871303923428059, + 0.05259871482849121, + 0.027785824611783028, + -0.044416770339012146, + -0.026691904291510582, + -0.01390022411942482, + -0.0573929138481617, + -0.08761026710271835, + 0.012642575427889824, + 0.04252056032419205, + 0.04015251249074936, + -0.0052475095726549625, + -0.08608037978410721, + 0.0028531427960842848, + -0.003371630096808076, + -0.02032443881034851, + -0.029511747881770134, + -0.043690912425518036, + 0.042122989892959595, + -0.057133886963129044, + 0.019293654710054398, + -0.019127611070871353, + 0.036713697016239166, + -0.00833116751164198, + 0.009040397591888905, + -0.1087319627404213, + 0.09127230197191238, + -0.048213958740234375, + -0.023604029789566994, + 0.0008899428648874164, + -0.093184694647789, + -0.03436879441142082, + 0.00807907897979021, + -0.0520450733602047, + 0.02262270636856556, + -0.03259866312146187, + -0.0391012541949749, + 0.001295328140258789, + 0.06636986881494522, + -0.04248524457216263, + -0.07093628495931625, + -0.06715728342533112, + 0.007415436673909426, + -0.06185786426067352, + 0.036563266068696976, + 0.030749274417757988, + 0.007705106865614653, + 0.0023627770133316517, + -0.026215652003884315, + -0.020864665508270264, + 0.05270038917660713, + 0.021477004513144493, + 0.0068344962783157825, + 0.024355463683605194, + -0.03746088594198227, + 0.031043346971273422, + 0.09879995882511139, + -0.050035204738378525, + 0.08045479655265808, + -0.04274336248636246, + 0.02994127944111824, + -0.03783947601914406, + 0.039749279618263245, + 0.046901557594537735, + 0.08667739480733871, + 0.02831847593188286, + -0.008148318156599998, + 0.007401767186820507, + -0.10989774018526077, + -0.014092149212956429, + 9.036048140842468e-06, + 0.054770372807979584, + -0.012413500808179379, + 0.01861056312918663, + 0.060349978506565094, + 0.06291640549898148, + 0.007055714726448059, + -0.021361181512475014, + 0.03852728381752968, + -0.018908292055130005, + 0.047424111515283585, + 0.054501011967659, + 0.017648596316576004, + 0.11429999768733978, + -0.041358742862939835, + -0.03363256901502609, + 0.0030338421929627657, + 0.030056871473789215, + 0.008060693740844727, + -0.0688784271478653, + -0.09984277933835983, + 0.030267179012298584, + -0.028509166091680527, + 0.02268315851688385, + -0.07580948621034622, + -0.0405895970761776, + 0.03384138271212578, + 0.07081738114356995, + 0.019772004336118698, + -0.06172173097729683, + 0.008248022757470608, + 1.9840379362262432e-33, + 0.037930894643068314, + 0.04577890411019325, + -0.03732670471072197, + -0.032416991889476776, + 0.014588817022740841, + -0.06602118909358978, + -0.013814577832818031, + -0.10539791733026505, + -0.12850415706634521, + -0.005076196976006031, + -0.07302963733673096, + 0.07235066592693329, + -0.04646436870098114, + 0.059371400624513626, + 0.09111672639846802, + -0.10133209824562073, + -0.05483068525791168, + -0.053797587752342224, + -0.03709062561392784, + 0.08241501450538635, + 0.03958267718553543, + 0.01433452870696783, + 0.05912068858742714, + -0.0029660870786756277, + 0.01806815154850483, + -0.0680701732635498, + 0.013625666499137878, + 0.015582672320306301, + -0.02142208069562912, + 0.06357447057962418, + -0.12094264477491379, + -0.09666852653026581, + -0.03333089128136635, + -0.06621623784303665, + -0.03821765258908272, + -0.010776739567518234, + -0.08198492974042892, + -0.03277386352419853, + 0.014794036746025085, + 0.013517632149159908, + -0.0034003634937107563, + 0.005664682947099209, + -0.022207774221897125, + 0.015086682513356209, + -0.06799489259719849, + 0.08191754668951035, + 0.15600024163722992, + 0.07579171657562256, + -0.05843832343816757, + -0.008412746712565422, + -0.07857701182365417, + -0.0038521387614309788, + -0.02901686355471611, + 0.0006679021753370762, + -0.0697081908583641, + -0.05330892279744148, + 0.053166620433330536, + 0.004783661104738712, + 0.013741954229772091, + 0.07203977555036545, + -0.015861764550209045, + 0.033332549035549164, + -0.022547632455825806, + -0.011850795708596706, + -0.09526507556438446, + -0.016078950837254524, + -0.004922114312648773, + 0.004731250926852226, + -0.006536174099892378, + 0.004852978512644768, + 0.01268527377396822, + 0.05300697684288025, + 0.11899229139089584, + 0.025274425745010376, + 0.11763036996126175, + -0.08781222999095917, + -0.016429787501692772, + -0.02661072462797165, + -0.01769474893808365, + 0.05961911380290985, + 0.005877051502466202, + -0.03780582174658775, + -0.028037618845701218, + 0.023397648707032204, + -0.006716609466820955, + 0.015020519495010376, + 0.040884219110012054, + -0.06197969987988472, + -0.029691174626350403, + -0.004976964555680752, + -0.03422437235713005, + 0.018984060734510422, + -0.00813105795532465, + -0.020747167989611626, + 0.0281930360943079, + -3.2326105667872445e-33, + 0.12847235798835754, + 0.026267321780323982, + 0.10983140766620636, + 0.011159190908074379, + -0.044980239123106, + 0.017553674057126045, + -0.004794386215507984, + -0.015802551060914993, + -0.01226617582142353, + -0.019019361585378647, + 0.011876302771270275, + -0.023136194795370102, + 0.03209578990936279, + 0.07442695647478104, + 0.05641649663448334, + -0.05300089344382286, + -0.03638714924454689, + -0.014843880198895931, + 0.036886364221572876, + -0.1257546991109848, + -0.008151554502546787, + 0.07542898505926132, + -0.11252967268228531, + -0.09049033373594284, + -0.0030374047346413136, + 0.04011000320315361, + -0.1538117229938507, + 0.07717141509056091, + -0.04436258599162102, + 0.07290291786193848, + -0.0072043901309370995, + 0.03950807452201843, + 0.002569766016677022, + -0.006248284596949816, + 0.001176450401544571, + 0.12515375018119812, + 0.02005600742995739, + -0.00013864059292245656, + 0.024221621453762054, + 0.05465780198574066, + 0.09940708428621292, + -0.053944025188684464, + 0.09602796286344528, + 0.00014552564243786037, + 0.026602625846862793, + 0.016811460256576538, + 0.0015170868718996644, + 0.08731727302074432, + 0.005771235562860966, + -0.009904555976390839, + 0.06958170980215073, + 0.09265917539596558, + 0.005830116104334593, + 0.10210693627595901, + 0.0011325017549097538, + -0.006749417632818222, + 0.01503710262477398, + -0.03714510798454285, + -0.0939728170633316, + -0.03357759863138199, + -0.050196800380945206, + -0.006316252052783966, + -0.06414957344532013, + 0.007235861383378506, + -0.01670873910188675, + -0.07423098385334015, + 0.018221961334347725, + -0.05320512503385544, + -0.010248126462101936, + 0.013013344258069992, + -0.12916545569896698, + -0.04244375228881836, + 0.08774643391370773, + -0.03917357325553894, + -0.027767449617385864, + 0.02099072001874447, + 0.0595061257481575, + 0.01945589855313301, + 0.03815269470214844, + -0.028747402131557465, + -0.025533201172947884, + -0.038349006325006485, + -0.014486055821180344, + -0.013576257973909378, + 0.051731329411268234, + 0.06515145301818848, + -0.011720783077180386, + -0.1163521260023117, + -0.03931708261370659, + 0.03394515439867973, + 0.09380073100328445, + 0.00931628793478012, + -0.01171857863664627, + 0.04104544222354889, + -0.008916886523365974, + -4.140364140425845e-08, + 0.061763226985931396, + 0.02101300284266472, + -0.019075985997915268, + -0.002017116406932473, + 0.015830116346478462, + -0.009857675060629845, + -0.018932191655039787, + -0.0007055550813674927, + 0.013249439187347889, + 0.0642324835062027, + 0.12860508263111115, + 0.03213700279593468, + -0.0670522004365921, + 0.041800037026405334, + -0.0426088348031044, + 0.0437115803360939, + 0.021514814347028732, + 0.12901803851127625, + 0.015236952342092991, + -0.02232329733669758, + -0.05031055584549904, + -0.052778035402297974, + 0.000233030179515481, + -0.022497626021504402, + -0.014370465651154518, + 0.017518600448966026, + 0.014312495477497578, + 0.010902844369411469, + 0.0027771666646003723, + -0.04195915535092354, + -0.06504479050636292, + 0.038553908467292786, + -0.01374481339007616, + 0.03843652829527855, + 0.09058628976345062, + -0.0230315700173378, + -0.11761228740215302, + 0.06487669050693512, + -0.005885662976652384, + 0.06288695335388184, + 0.05191958323121071, + 0.011840583756566048, + -0.027309859171509743, + 0.040654800832271576, + -0.005980184301733971, + 0.03252403438091278, + -0.043744225054979324, + 0.006068602204322815, + -0.00032751375692896545, + -0.022902367636561394, + -0.050694938749074936, + 0.026743048802018166, + 0.06005466729402542, + 0.02092430181801319, + 0.02072584442794323, + 0.08342021703720093, + -0.05768749862909317, + -0.08585236966609955, + -0.03160274401307106, + 0.05222279205918312, + 0.029474787414073944, + -0.08763033896684647, + -0.01591801643371582, + -0.07798203080892563 + ], + "model": "Summit", + "pickup_zone": "POLYGON((1.9450 41.4301, 2.4018 41.4301, 2.4018 41.1987, 1.9450 41.1987, 1.9450 41.4301))", + "price": 1200, + "store_location": "2.1734, 41.3851" + }, + { + "brand": "BikeShind", + "condition": "refurbished", + "description": "An artsy, retro-inspired bicycle that\u2019s as functional as it is pretty: The ThrillCycle steel frame offers a smooth ride. A 9-speed drivetrain has enough gears for coasting in the city, but we wouldn\u2019t suggest taking it to the mountains. Fenders protect you from mud, and a rear basket lets you transport groceries, flowers and books. The ThrillCycle comes with a limited lifetime warranty, so this little guy will last you long past graduation.", + "description_embeddings": [ + -0.005154624115675688, + 0.10099548101425171, + 0.04890008643269539, + 0.0625317171216011, + -0.0363173745572567, + 0.025850096717476845, + 0.032137978821992874, + -0.03442877531051636, + -0.10096120089292526, + -0.007441149093210697, + 0.002616145182400942, + 0.002316742204129696, + 0.042584337294101715, + 0.026208963245153427, + 0.018415318801999092, + 0.050033390522003174, + 0.14831797778606415, + -0.006160213612020016, + 0.05086936056613922, + 0.0131875304505229, + -0.05973455682396889, + 0.045886117964982986, + -0.025052331387996674, + 0.04576171189546585, + -0.006168896332383156, + 0.03315199911594391, + -0.07763667404651642, + 0.05837876722216606, + -0.03229084238409996, + -0.021293187513947487, + -0.008595496416091919, + 0.06880632787942886, + -0.04528025537729263, + -0.020664094015955925, + -0.029125794768333435, + 0.0445701889693737, + 0.023799002170562744, + -0.013527574017643929, + -0.01646343804895878, + 0.027023920789361, + 0.0014843698590993881, + 0.014952401630580425, + -0.010172702372074127, + 0.07603776454925537, + 0.017975280061364174, + -0.08911248296499252, + 0.055482957512140274, + -0.027819648385047913, + 0.019395964220166206, + 0.05424710363149643, + 0.06634850054979324, + -0.05045575276017189, + -0.0006240577204152942, + -0.06339468061923981, + -0.023632608354091644, + -0.060364823788404465, + -0.11193189769983292, + 0.046562716364860535, + -0.010353066958487034, + -0.043506212532520294, + 0.051063187420368195, + 0.003231980837881565, + 0.027102531865239143, + 0.002380193443968892, + -0.0056939758360385895, + -0.026826998218894005, + -0.014482468366622925, + -0.0185822993516922, + 0.013927196152508259, + -0.10704471915960312, + 0.0673733651638031, + -0.0010599792003631592, + -0.07455477863550186, + -0.014452059753239155, + -0.02294282428920269, + -0.010201872326433659, + 0.023037923499941826, + -0.05595972761511803, + -0.09120096266269684, + -0.029177049174904823, + -0.02343042939901352, + -0.029726101085543633, + 0.07366959005594254, + -0.046458736062049866, + 0.006261167582124472, + -0.013148028403520584, + -0.0014217026764526963, + 0.017440352588891983, + -0.04511420056223869, + 0.008813070133328438, + -0.022654451429843903, + -0.014097515493631363, + -0.020799456164240837, + -0.05456947907805443, + -0.07338607311248779, + 0.017073828727006912, + -0.04925030097365379, + -0.024428002536296844, + -0.00979585014283657, + 0.025696750730276108, + 0.07033320516347885, + 0.0389082096517086, + 0.12711860239505768, + 0.07472020387649536, + -0.019501805305480957, + 0.01663217693567276, + 0.027799105271697044, + 0.039703886955976486, + -0.029871169477701187, + 0.005997804459184408, + -0.0015701024094596505, + 0.0411832258105278, + 8.85713889147155e-05, + -0.029442811384797096, + 0.009789851494133472, + -0.04723270237445831, + -0.09891993552446365, + 0.05102938041090965, + -0.007144609931856394, + 0.01668076030910015, + 0.004902719985693693, + 0.02218448370695114, + 0.03827798366546631, + 0.001542922342196107, + 0.004610520787537098, + -0.12595036625862122, + 0.07361572980880737, + -1.521196586481568e-33, + -0.01484542153775692, + 0.024973904713988304, + 0.006633534096181393, + 0.01254032738506794, + 0.06355929374694824, + -0.04809075966477394, + 0.02005922608077526, + -0.06251886487007141, + -0.06941430270671844, + 0.02304103970527649, + -0.020975833758711815, + 0.08137834072113037, + 0.049319952726364136, + 0.11945393681526184, + 0.17472721636295319, + -0.03430051729083061, + -0.07276412844657898, + -0.11828659474849701, + 0.04918891564011574, + -0.07279565185308456, + -0.06228293105959892, + -0.03494764119386673, + 0.004517378751188517, + -0.0431801863014698, + -0.07457974553108215, + -0.0003888327337335795, + 0.05853814631700516, + 0.05362739413976669, + 0.01866878569126129, + 0.004751134198158979, + -0.07936650514602661, + 0.033309247344732285, + -0.01660560630261898, + -0.08851712942123413, + 0.042956508696079254, + 0.03460550308227539, + -0.054352447390556335, + -0.016094308346509933, + 0.019760286435484886, + -0.03591147065162659, + -0.04307156056165695, + -0.04029138758778572, + -0.09315019100904465, + 0.055713504552841187, + 0.003642909461632371, + 0.0391569547355175, + 0.1300928145647049, + 0.026774607598781586, + -0.054334208369255066, + -0.060861144214868546, + -0.020662454888224602, + -0.034525640308856964, + 0.004193050786852837, + 0.02453654631972313, + -0.09028972685337067, + 0.004444751888513565, + 0.06320545822381973, + 0.01895289309322834, + -0.04660886526107788, + 0.08611723780632019, + 0.05705415457487106, + 0.030612647533416748, + 0.019753821194171906, + -0.061483170837163925, + -0.025262145325541496, + 0.054222241044044495, + 0.017605014145374298, + -0.013110420666635036, + 0.04744894057512283, + -0.004785404074937105, + 0.04680679365992546, + 0.001831064117141068, + 0.07773134112358093, + 0.008347200229763985, + 0.08674833923578262, + 0.024294976145029068, + -0.03695613518357277, + -0.0440739244222641, + 0.00895663257688284, + -0.0060102143324911594, + -0.0361272394657135, + -0.08447428047657013, + -0.08971717208623886, + 0.03131894767284393, + 0.0721370130777359, + 0.031180810183286667, + 0.039390258491039276, + -0.09044987708330154, + -0.007888346910476685, + 0.008131768554449081, + -0.03624662756919861, + -0.033649034798145294, + 0.031620997935533524, + 0.05870470404624939, + 0.018141048029065132, + -5.7662626416839565e-34, + 0.09117024391889572, + -0.023953478783369064, + 0.080879345536232, + 0.0035459804348647594, + -0.013222851790487766, + -0.025237491354346275, + 0.019038159400224686, + -0.06588941067457199, + -0.11689981818199158, + 0.0007165187271311879, + -0.0933104082942009, + 0.014618181623518467, + 0.12644915282726288, + 0.0027253602165728807, + 0.15038658678531647, + 0.0053163510747253895, + -0.005645217839628458, + 0.009321562945842743, + 0.002719732467085123, + -0.01651378907263279, + 0.0063886744901537895, + 0.05738385394215584, + -0.14213474094867706, + -0.0669025406241417, + -0.05766160413622856, + 0.0031909833196550608, + -0.13478563725948334, + -0.03741385415196419, + 0.041654907166957855, + 0.04030514881014824, + -0.05102578178048134, + 0.0028968513943254948, + 0.0738285705447197, + -0.03643025830388069, + -0.015445208176970482, + 0.06674294173717499, + 0.029759766533970833, + -0.024196317419409752, + -0.009355752728879452, + 0.023269686847925186, + 0.01623220555484295, + -0.08251868188381195, + 0.020170293748378754, + 0.0749305933713913, + 0.08206077665090561, + 0.012237507849931717, + -0.026570556685328484, + 0.016253553330898285, + -0.003425935748964548, + 0.005059909541159868, + 0.12109038233757019, + 0.08107315003871918, + -0.04097432270646095, + 0.0009462210582569242, + 0.058092083781957626, + -0.03609737753868103, + -0.01580999232828617, + -0.0342475026845932, + 0.012185491621494293, + -0.058348096907138824, + -0.05934947729110718, + 0.026715252548456192, + -0.031246516853570938, + 0.012632215395569801, + -0.01100403256714344, + -0.05962579324841499, + -0.000801989808678627, + -0.06919746845960617, + -0.1299775391817093, + -0.03721063956618309, + -0.03989635780453682, + 0.04918158799409866, + -0.0022671804763376713, + -0.04244564101099968, + -0.02631748840212822, + -0.04100838303565979, + 0.08634430915117264, + 0.05031242221593857, + 0.06688959896564484, + 0.024407675489783287, + 0.0018788984743878245, + -0.026026425883173943, + 0.04438408091664314, + 0.02198263816535473, + -0.014974343590438366, + -0.026490231975913048, + -0.09914876520633698, + -0.09834708273410797, + 0.017346272245049477, + 0.00089951854897663, + 0.05497178062796593, + 0.06180766969919205, + -0.050425756722688675, + 0.013637324795126915, + -0.03599657490849495, + -4.404103037813911e-08, + -0.0024933917447924614, + 0.049585312604904175, + -0.020710783079266548, + -0.014283453114330769, + -0.0064896950498223305, + 0.04579087719321251, + 0.02242390066385269, + 0.02515084482729435, + -0.031156959012150764, + 0.006718308199197054, + 0.061417631804943085, + -0.009935885667800903, + -0.021248064935207367, + 0.011152776889503002, + -0.059073783457279205, + 0.05840323865413666, + 0.06523993611335754, + 0.03690555691719055, + 0.03174441307783127, + 0.02336188219487667, + 0.003073832020163536, + -0.017117813229560852, + 0.02877660095691681, + 0.0017827276606112719, + -0.07995487004518509, + -0.032443612813949585, + -0.01474942360073328, + -0.016080526635050774, + 0.0771399736404419, + 0.017354682087898254, + -0.02578366920351982, + 0.004991421941667795, + 0.006590475793927908, + -0.032372940331697464, + 0.038421064615249634, + -0.1204696074128151, + -0.05457846075296402, + 0.036750562489032745, + 0.013932188041508198, + 0.0031626380514353514, + 0.009978784248232841, + -0.0138266421854496, + 0.006769631989300251, + 0.059896957129240036, + 0.00822784099727869, + -0.05025117099285126, + -0.10305225104093552, + -0.07187453657388687, + 0.00836241990327835, + 0.049537766724824905, + 0.007570290472358465, + -0.08729138970375061, + -0.020592620596289635, + 0.04918212443590164, + 0.054685790091753006, + 0.04091939702630043, + -0.05420788377523422, + -0.04473182559013367, + -0.11178043484687805, + 0.09808126091957092, + -0.015394269488751888, + -0.0039725536480546, + 0.03283742815256119, + -0.05677533522248268 + ], + "model": "ThrillCycle", + "pickup_zone": "POLYGON((12.4464 42.1028, 12.5464 42.1028, 12.5464 41.7028, 12.4464 41.7028, 12.4464 42.1028))", + "price": 815, + "store_location": "12.4964,41.9028" + } +] \ No newline at end of file diff --git a/doctests/dt-bitfield.js b/doctests/dt-bitfield.js new file mode 100644 index 00000000000..9685372de41 --- /dev/null +++ b/doctests/dt-bitfield.js @@ -0,0 +1,76 @@ +// EXAMPLE: bitfield_tutorial +// HIDE_START +import assert from 'assert'; +import { createClient } from 'redis'; + +const client = createClient(); +await client.connect(); +// HIDE_END + +// REMOVE_START +await client.flushDb(); +// REMOVE_END + +// STEP_START bf +let res1 = await client.bitField("bike:1:stats", [{ + operation: 'SET', + encoding: 'u32', + offset: '#0', + value: 1000 +}]); +console.log(res1); // >>> [0] + +let res2 = await client.bitField('bike:1:stats', [ + { + operation: 'INCRBY', + encoding: 'u32', + offset: '#0', + increment: -50 + }, + { + operation: 'INCRBY', + encoding: 'u32', + offset: '#1', + increment: 1 + } +]); +console.log(res2); // >>> [950, 1] + +let res3 = await client.bitField('bike:1:stats', [ + { + operation: 'INCRBY', + encoding: 'u32', + offset: '#0', + increment: 500 + }, + { + operation: 'INCRBY', + encoding: 'u32', + offset: '#1', + increment: 1 + } +]); +console.log(res3); // >>> [1450, 2] + +let res4 = await client.bitField('bike:1:stats', [ + { + operation: 'GET', + encoding: 'u32', + offset: '#0' + }, + { + operation: 'GET', + encoding: 'u32', + offset: '#1' + } +]); +console.log(res4); // >>> [1450, 2] +// STEP_END + +// REMOVE_START +assert.deepEqual(res1, [0]) +assert.deepEqual(res2, [950, 1]) +assert.deepEqual(res3, [1450, 2]) +assert.deepEqual(res4, [1450, 2]) +await client.close(); +// REMOVE_END diff --git a/doctests/dt-bitmap.js b/doctests/dt-bitmap.js new file mode 100644 index 00000000000..845fd2a721c --- /dev/null +++ b/doctests/dt-bitmap.js @@ -0,0 +1,39 @@ +// EXAMPLE: bitmap_tutorial +// HIDE_START +import assert from 'assert'; +import { createClient } from 'redis'; + +const client = createClient(); +await client.connect(); +// HIDE_END + +// REMOVE_START +await client.flushDb(); +// REMOVE_END + +// STEP_START ping +const res1 = await client.setBit("pings:2024-01-01-00:00", 123, 1) +console.log(res1) // >>> 0 + +const res2 = await client.getBit("pings:2024-01-01-00:00", 123) +console.log(res2) // >>> 1 + +const res3 = await client.getBit("pings:2024-01-01-00:00", 456) +console.log(res3) // >>> 0 +// STEP_END + +// REMOVE_START +assert.equal(res1, 0) +// REMOVE_END + +// STEP_START bitcount +// HIDE_START +await client.setBit("pings:2024-01-01-00:00", 123, 1) +// HIDE_END +const res4 = await client.bitCount("pings:2024-01-01-00:00") +console.log(res4) // >>> 1 +// STEP_END +// REMOVE_START +assert.equal(res4, 1) +await client.close(); +// REMOVE_END \ No newline at end of file diff --git a/doctests/dt-bloom.js b/doctests/dt-bloom.js new file mode 100644 index 00000000000..d065937e763 --- /dev/null +++ b/doctests/dt-bloom.js @@ -0,0 +1,46 @@ +// EXAMPLE: bf_tutorial +// HIDE_START +import assert from 'assert'; +import { createClient } from 'redis'; + +const client = createClient(); +await client.connect(); +// HIDE_END + +// REMOVE_START +await client.flushDb(); +// REMOVE_END + +// STEP_START bloom +const res1 = await client.bf.reserve('bikes:models', 0.01, 1000); +console.log(res1); // >>> OK + +const res2 = await client.bf.add('bikes:models', 'Smoky Mountain Striker'); +console.log(res2); // >>> true + +const res3 = await client.bf.exists('bikes:models', 'Smoky Mountain Striker'); +console.log(res3); // >>> true + +const res4 = await client.bf.mAdd('bikes:models', [ + 'Rocky Mountain Racer', + 'Cloudy City Cruiser', + 'Windy City Wippet' +]); +console.log(res4); // >>> [true, true, true] + +const res5 = await client.bf.mExists('bikes:models', [ + 'Rocky Mountain Racer', + 'Cloudy City Cruiser', + 'Windy City Wippet' +]); +console.log(res5); // >>> [true, true, true] +// STEP_END + +// REMOVE_START +assert.equal(res1, 'OK') +assert.equal(res2, true) +assert.equal(res3, true) +assert.deepEqual(res4, [true, true, true]) +assert.deepEqual(res5, [true, true, true]) +await client.close(); +// REMOVE_END diff --git a/doctests/dt-cms.js b/doctests/dt-cms.js new file mode 100644 index 00000000000..b0d9fa68469 --- /dev/null +++ b/doctests/dt-cms.js @@ -0,0 +1,50 @@ +// EXAMPLE: cms_tutorial +// HIDE_START +import assert from 'assert'; +import { createClient } from 'redis'; + +const client = createClient(); +await client.connect(); +// HIDE_END + +// REMOVE_START +await client.flushDb(); +// REMOVE_END + +// STEP_START cms +const res1 = await client.cms.initByProb('bikes:profit', 0.001, 0.002); +console.log(res1); // >>> OK + +const res2 = await client.cms.incrBy('bikes:profit', { + item: 'Smoky Mountain Striker', + incrementBy: 100 +}); +console.log(res2); // >>> [100] + +const res3 = await client.cms.incrBy('bikes:profit', [ + { + item: 'Rocky Mountain Racer', + incrementBy: 200 + }, + { + item: 'Cloudy City Cruiser', + incrementBy: 150 + } +]); +console.log(res3); // >>> [200, 150] + +const res4 = await client.cms.query('bikes:profit', 'Smoky Mountain Striker'); +console.log(res4); // >>> [100] + +const res5 = await client.cms.info('bikes:profit'); +console.log(res5.width, res5.depth, res5.count); // >>> 2000 9 450 +// STEP_END + +// REMOVE_START +assert.equal(res1, 'OK') +assert.deepEqual(res2, [100]) +assert.deepEqual(res3, [200, 150]) +assert.deepEqual(res4, [100]) +assert.deepEqual(res5, { width: 2000, depth: 9, count: 450 }) +await client.close(); +// REMOVE_END \ No newline at end of file diff --git a/doctests/dt-cuckoo.js b/doctests/dt-cuckoo.js new file mode 100644 index 00000000000..4b11bca2345 --- /dev/null +++ b/doctests/dt-cuckoo.js @@ -0,0 +1,38 @@ +// EXAMPLE: cuckoo_tutorial +// HIDE_START +import assert from 'assert'; +import { createClient } from 'redis'; + +const client = createClient(); +await client.connect(); +// HIDE_END + +// REMOVE_START +await client.flushDb(); +// REMOVE_END + +// STEP_START cuckoo +const res1 = await client.cf.reserve('bikes:models', 1000000); +console.log(res1); // >>> OK + +const res2 = await client.cf.add('bikes:models', 'Smoky Mountain Striker'); +console.log(res2); // >>> true + +const res3 = await client.cf.exists('bikes:models', 'Smoky Mountain Striker'); +console.log(res3); // >>> true + +const res4 = await client.cf.exists('bikes:models', 'Terrible Bike Name'); +console.log(res4); // >>> false + +const res5 = await client.cf.del('bikes:models', 'Smoky Mountain Striker'); +console.log(res5); // >>> true +// STEP_END + +// REMOVE_START +assert.equal(res1, 'OK') +assert.equal(res2, true) +assert.equal(res3, true) +assert.equal(res4, false) +assert.equal(res5, true) +await client.close(); +// REMOVE_END diff --git a/doctests/dt-geo.js b/doctests/dt-geo.js new file mode 100644 index 00000000000..7ec9376a8a7 --- /dev/null +++ b/doctests/dt-geo.js @@ -0,0 +1,59 @@ +// EXAMPLE: geo_tutorial +// HIDE_START +import assert from 'assert'; +import { createClient } from 'redis'; + +const client = createClient(); +await client.connect(); +// HIDE_END + +// REMOVE_START +await client.del('bikes:rentable') +// REMOVE_END + +// STEP_START geoAdd +const res1 = await client.geoAdd('bikes:rentable', { + longitude: -122.27652, + latitude: 37.805186, + member: 'station:1' +}); +console.log(res1) // 1 + +const res2 = await client.geoAdd('bikes:rentable', { + longitude: -122.2674626, + latitude: 37.8062344, + member: 'station:2' +}); +console.log(res2) // 1 + +const res3 = await client.geoAdd('bikes:rentable', { + longitude: -122.2469854, + latitude: 37.8104049, + member: 'station:3' +}) +console.log(res3) // 1 +// STEP_END + +// REMOVE_START +assert.equal(res1, 1); +assert.equal(res2, 1); +assert.equal(res3, 1); +// REMOVE_END + +// STEP_START geoSearch +const res4 = await client.geoSearch( + 'bikes:rentable', { + longitude: -122.27652, + latitude: 37.805186, + }, + { radius: 5, + unit: 'km' + } +); +console.log(res4) // ['station:1', 'station:2', 'station:3'] +// STEP_END + +// REMOVE_START +assert.deepEqual(res4, ['station:1', 'station:2', 'station:3']); +// REMOVE_END +await client.close() diff --git a/doctests/dt-hash.js b/doctests/dt-hash.js new file mode 100644 index 00000000000..e77d6fd7b95 --- /dev/null +++ b/doctests/dt-hash.js @@ -0,0 +1,98 @@ +// EXAMPLE: hash_tutorial +// HIDE_START +import assert from 'assert'; +import { createClient } from 'redis'; + +const client = createClient(); +await client.connect(); +// HIDE_END +// STEP_START set_get_all +const res1 = await client.hSet( + 'bike:1', + { + 'model': 'Deimos', + 'brand': 'Ergonom', + 'type': 'Enduro bikes', + 'price': 4972, + } +) +console.log(res1) // 4 + +const res2 = await client.hGet('bike:1', 'model') +console.log(res2) // 'Deimos' + +const res3 = await client.hGet('bike:1', 'price') +console.log(res3) // '4972' + +const res4 = await client.hGetAll('bike:1') +console.log(res4) +/* +{ + brand: 'Ergonom', + model: 'Deimos', + price: '4972', + type: 'Enduro bikes' +} +*/ +// STEP_END + +// REMOVE_START +assert.equal(res1, 4); +assert.equal(res2, 'Deimos'); +assert.equal(res3, '4972'); +assert.deepEqual(res4, { + model: 'Deimos', + brand: 'Ergonom', + type: 'Enduro bikes', + price: '4972' +}); +// REMOVE_END + +// STEP_START hmGet +const res5 = await client.hmGet('bike:1', ['model', 'price']) +console.log(res5) // ['Deimos', '4972'] +// STEP_END + +// REMOVE_START +assert.deepEqual(Object.values(res5), ['Deimos', '4972']) +// REMOVE_END + +// STEP_START hIncrBy +const res6 = await client.hIncrBy('bike:1', 'price', 100) +console.log(res6) // 5072 +const res7 = await client.hIncrBy('bike:1', 'price', -100) +console.log(res7) // 4972 +// STEP_END + +// REMOVE_START +assert.equal(res6, 5072) +assert.equal(res7, 4972) +// REMOVE_END + +// STEP_START hIncrBy_hGet_hMget +const res11 = await client.hIncrBy('bike:1:stats', 'rides', 1) +console.log(res11) // 1 +const res12 = await client.hIncrBy('bike:1:stats', 'rides', 1) +console.log(res12) // 2 +const res13 = await client.hIncrBy('bike:1:stats', 'rides', 1) +console.log(res13) // 3 +const res14 = await client.hIncrBy('bike:1:stats', 'crashes', 1) +console.log(res14) // 1 +const res15 = await client.hIncrBy('bike:1:stats', 'owners', 1) +console.log(res15) // 1 +const res16 = await client.hGet('bike:1:stats', 'rides') +console.log(res16) // 3 +const res17 = await client.hmGet('bike:1:stats', ['crashes', 'owners']) +console.log(res17) // ['1', '1'] +// STEP_END + +// REMOVE_START +assert.equal(res11, 1); +assert.equal(res12, 2); +assert.equal(res13, 3); +assert.equal(res14, 1); +assert.equal(res15, 1); +assert.equal(res16, '3'); +assert.deepEqual(res17, ['1', '1']); +await client.close(); +// REMOVE_END \ No newline at end of file diff --git a/doctests/dt-hll.js b/doctests/dt-hll.js new file mode 100644 index 00000000000..762ce5db15e --- /dev/null +++ b/doctests/dt-hll.js @@ -0,0 +1,38 @@ +// EXAMPLE: hll_tutorial +// HIDE_START +import assert from 'assert'; +import { createClient } from 'redis'; + +const client = createClient(); +await client.connect(); +// HIDE_END + +// REMOVE_START +await client.flushDb(); +// REMOVE_END + +// STEP_START pfadd +const res1 = await client.pfAdd('bikes', ['Hyperion', 'Deimos', 'Phoebe', 'Quaoar']); +console.log(res1); // >>> 1 + +const res2 = await client.pfCount('bikes'); +console.log(res2); // >>> 4 + +const res3 = await client.pfAdd('commuter_bikes', ['Salacia', 'Mimas', 'Quaoar']); +console.log(res3); // >>> 1 + +const res4 = await client.pfMerge('all_bikes', ['bikes', 'commuter_bikes']); +console.log(res4); // >>> OK + +const res5 = await client.pfCount('all_bikes'); +console.log(res5); // >>> 6 +// STEP_END + +// REMOVE_START +assert.equal(res1, 1) +assert.equal(res2, 4) +assert.equal(res3, 1) +assert.equal(res4, 'OK') +assert.equal(res5, 6) +await client.close(); +// REMOVE_END diff --git a/doctests/dt-json.js b/doctests/dt-json.js new file mode 100644 index 00000000000..00578f48f33 --- /dev/null +++ b/doctests/dt-json.js @@ -0,0 +1,425 @@ +// EXAMPLE: json_tutorial +// HIDE_START +import assert from 'assert'; +import { + createClient +} from 'redis'; + +const client = await createClient(); +await client.connect(); +// HIDE_END +// REMOVE_START +await client.flushDb(); +// REMOVE_END + +// STEP_START set_get +const res1 = await client.json.set("bike", "$", '"Hyperion"'); +console.log(res1); // OK + +const res2 = await client.json.get("bike", { path: "$" }); +console.log(res2); // ['"Hyperion"'] + +const res3 = await client.json.type("bike", { path: "$" }); +console.log(res3); // [ 'string' ] +// STEP_END + +// REMOVE_START +assert.deepEqual(res2, ['"Hyperion"']); +// REMOVE_END + +// STEP_START str +const res4 = await client.json.strLen("bike", { path: "$" }); +console.log(res4) // [10] + +const res5 = await client.json.strAppend("bike", '" (Enduro bikes)"'); +console.log(res5) // 27 + +const res6 = await client.json.get("bike", { path: "$" }); +console.log(res6) // ['"Hyperion"" (Enduro bikes)"'] +// STEP_END + +// REMOVE_START +assert.deepEqual(res6, ['"Hyperion"" (Enduro bikes)"']); +// REMOVE_END + +// STEP_START num +const res7 = await client.json.set("crashes", "$", 0); +console.log(res7) // OK + +const res8 = await client.json.numIncrBy("crashes", "$", 1); +console.log(res8) // [1] + +const res9 = await client.json.numIncrBy("crashes", "$", 1.5); +console.log(res9) // [2.5] + +const res10 = await client.json.numIncrBy("crashes", "$", -0.75); +console.log(res10) // [1.75] +// STEP_END + +// REMOVE_START +assert.deepEqual(res10, [1.75]) +// REMOVE_END + +// STEP_START arr +const res11 = await client.json.set("newbike", "$", ["Deimos", {"crashes": 0 }, null]); +console.log(res11); // OK + +const res12 = await client.json.get("newbike", { path: "$" }); +console.log(res12); // [[ 'Deimos', { crashes: 0 }, null ]] + +const res13 = await client.json.get("newbike", { path: "$[1].crashes" }); +console.log(res13); // [0] + +const res14 = await client.json.del("newbike", { path: "$.[-1]"} ); +console.log(res14); // 1 + +const res15 = await client.json.get("newbike", { path: "$" }); +console.log(res15); // [[ 'Deimos', { crashes: 0 } ]] +// STEP_END + +// REMOVE_START +assert.deepEqual(res15, [["Deimos", { + "crashes": 0 +}]]); +// REMOVE_END + +// STEP_START arr2 +const res16 = await client.json.set("riders", "$", []); +console.log(res16); // OK + +const res17 = await client.json.arrAppend("riders", "$", "Norem"); +console.log(res17); // [1] + +const res18 = await client.json.get("riders", { path: "$" }); +console.log(res18); // [[ 'Norem' ]] + +const res19 = await client.json.arrInsert("riders", "$", 1, "Prickett", "Royse", "Castilla"); +console.log(res19); // [4] + +const res20 = await client.json.get("riders", { path: "$" }); +console.log(res20); // [[ 'Norem', 'Prickett', 'Royse', 'Castilla' ]] + +const res21 = await client.json.arrTrim("riders", "$", 1, 1); +console.log(res21); // [1] + +const res22 = await client.json.get("riders", { path: "$" }); +console.log(res22); // [[ 'Prickett' ]] + +const res23 = await client.json.arrPop("riders", { path: "$" }); +console.log(res23); // [ 'Prickett' ] + +const res24 = await client.json.arrPop("riders", { path: "$" }); +console.log(res24); // [null] +// STEP_END + +// REMOVE_START +assert.deepEqual(res24, [null]); +// REMOVE_END + +// STEP_START obj +const res25 = await client.json.set( + "bike:1", "$", { + "model": "Deimos", + "brand": "Ergonom", + "price": 4972 + } +); +console.log(res25); // OK + +const res26 = await client.json.objLen("bike:1", { path: "$" }); +console.log(res26); // [3] + +const res27 = await client.json.objKeys("bike:1", { path: "$" }); +console.log(res27); // [['model', 'brand', 'price']] +// STEP_END + +// REMOVE_START +assert.deepEqual(res27, [ + ["model", "brand", "price"] +]); +// REMOVE_END + +// STEP_START set_bikes +// HIDE_START +const inventoryJSON = { + "inventory": { + "mountain_bikes": [{ + "id": "bike:1", + "model": "Phoebe", + "description": "This is a mid-travel trail slayer that is a fantastic daily driver or one bike quiver. The Shimano Claris 8-speed groupset gives plenty of gear range to tackle hills and there\u2019s room for mudguards and a rack too. This is the bike for the rider who wants trail manners with low fuss ownership.", + "price": 1920, + "specs": { + "material": "carbon", + "weight": 13.1 + }, + "colors": ["black", "silver"], + }, + { + "id": "bike:2", + "model": "Quaoar", + "description": "Redesigned for the 2020 model year, this bike impressed our testers and is the best all-around trail bike we've ever tested. The Shimano gear system effectively does away with an external cassette, so is super low maintenance in terms of wear and teaawait client. All in all it's an impressive package for the price, making it very competitive.", + "price": 2072, + "specs": { + "material": "aluminium", + "weight": 7.9 + }, + "colors": ["black", "white"], + }, + { + "id": "bike:3", + "model": "Weywot", + "description": "This bike gives kids aged six years and older a durable and uberlight mountain bike for their first experience on tracks and easy cruising through forests and fields. A set of powerful Shimano hydraulic disc brakes provide ample stopping ability. If you're after a budget option, this is one of the best bikes you could get.", + "price": 3264, + "specs": { + "material": "alloy", + "weight": 13.8 + }, + }, + ], + "commuter_bikes": [{ + "id": "bike:4", + "model": "Salacia", + "description": "This bike is a great option for anyone who just wants a bike to get about on With a slick-shifting Claris gears from Shimano\u2019s, this is a bike which doesn\u2019t break the bank and delivers craved performance. It\u2019s for the rider who wants both efficiency and capability.", + "price": 1475, + "specs": { + "material": "aluminium", + "weight": 16.6 + }, + "colors": ["black", "silver"], + }, + { + "id": "bike:5", + "model": "Mimas", + "description": "A real joy to ride, this bike got very high scores in last years Bike of the year report. The carefully crafted 50-34 tooth chainset and 11-32 tooth cassette give an easy-on-the-legs bottom gear for climbing, and the high-quality Vittoria Zaffiro tires give balance and grip.It includes a low-step frame , our memory foam seat, bump-resistant shocks and conveniently placed thumb throttle. Put it all together and you get a bike that helps redefine what can be done for this price.", + "price": 3941, + "specs": { + "material": "alloy", + "weight": 11.6 + }, + }, + ], + } +}; +// HIDE_END + +const res28 = await client.json.set("bikes:inventory", "$", inventoryJSON); +console.log(res28); // OK +// STEP_END + +// STEP_START get_bikes +const res29 = await client.json.get("bikes:inventory", { + path: "$.inventory.*" +}); +console.log(res29); +/* +[ + [ + { + id: 'bike:1', + model: 'Phoebe', + description: 'This is a mid-travel trail slayer that is a fantastic daily driver or one bike quiver. The Shimano Claris 8-speed groupset gives plenty of gear range to tackle hills and there’s room for mudguards and a rack too. This is the bike for the rider who wants trail manners with low fuss ownership.', + price: 1920, + specs: [Object], + colors: [Array] + }, + { + id: 'bike:2', + model: 'Quaoar', + description: "Redesigned for the 2020 model year, this bike impressed our testers and is the best all-around trail bike we've ever tested. The Shimano gear system effectively does away with an external cassette, so is super low maintenance in terms of wear and teaawait client. All in all it's an impressive package for the price, making it very competitive.", + price: 2072, + specs: [Object], + colors: [Array] + }, + { + id: 'bike:3', + model: 'Weywot', + description: "This bike gives kids aged six years and older a durable and uberlight mountain bike for their first experience on tracks and easy cruising through forests and fields. A set of powerful Shimano hydraulic disc brakes provide ample stopping ability. If you're after a budget option, this is one of the best bikes you could get.", + price: 3264, + specs: [Object] + } + ], + [ + { + id: 'bike:4', + model: 'Salacia', + description: 'This bike is a great option for anyone who just wants a bike to get about on With a slick-shifting Claris gears from Shimano’s, this is a bike which doesn’t break the bank and delivers craved performance. It’s for the rider who wants both efficiency and capability.', + price: 1475, + specs: [Object], + colors: [Array] + }, + { + id: 'bike:5', + model: 'Mimas', + description: 'A real joy to ride, this bike got very high scores in last years Bike of the year report. The carefully crafted 50-34 tooth chainset and 11-32 tooth cassette give an easy-on-the-legs bottom gear for climbing, and the high-quality Vittoria Zaffiro tires give balance and grip.It includes a low-step frame , our memory foam seat, bump-resistant shocks and conveniently placed thumb throttle. Put it all together and you get a bike that helps redefine what can be done for this price.', + price: 3941, + specs: [Object] + } + ] +] +*/ +// STEP_END + +// STEP_START get_mtnbikes +const res30 = await client.json.get("bikes:inventory", { + path: "$.inventory.mountain_bikes[*].model" +}); +console.log(res30); // ['Phoebe', 'Quaoar', 'Weywot'] + +const res31 = await client.json.get("bikes:inventory", { + path: '$.inventory["mountain_bikes"][*].model' +}); +console.log(res31); // ['Phoebe', 'Quaoar', 'Weywot'] + +const res32 = await client.json.get("bikes:inventory", { + path: "$..mountain_bikes[*].model" +}); +console.log(res32); // ['Phoebe', 'Quaoar', 'Weywot'] +// STEP_END + +// REMOVE_START +assert.deepEqual(res30, ["Phoebe", "Quaoar", "Weywot"]); +assert.deepEqual(res31, ["Phoebe", "Quaoar", "Weywot"]); +assert.deepEqual(res32, ["Phoebe", "Quaoar", "Weywot"]); +// REMOVE_END + +// STEP_START get_models +const res33 = await client.json.get("bikes:inventory", { + path: "$..model" +}); +console.log(res33); // ['Phoebe', 'Quaoar', 'Weywot', 'Salacia', 'Mimas'] +// STEP_END + +// REMOVE_START +assert.deepEqual(res33, ["Phoebe", "Quaoar", "Weywot", "Salacia", "Mimas"]); +// REMOVE_END + +// STEP_START get2mtnbikes +const res34 = await client.json.get("bikes:inventory", { + path: "$..mountain_bikes[0:2].model" +}); +console.log(res34); // ['Phoebe', 'Quaoar'] +// STEP_END + +// REMOVE_START +assert.deepEqual(res34, ["Phoebe", "Quaoar"]); +// REMOVE_END + +// STEP_START filter1 +const res35 = await client.json.get("bikes:inventory", { + path: "$..mountain_bikes[?(@.price < 3000 && @.specs.weight < 10)]" +}); +console.log(res35); +/* +[ + { + id: 'bike:2', + model: 'Quaoar', + description: "Redesigned for the 2020 model year, this bike impressed our testers and is the best all-around trail bike we've ever tested. The Shimano gear system effectively does away with an external cassette, so is super low maintenance in terms of wear and teaawait client. All in all it's an impressive package for the price, making it very competitive.", + price: 2072, + specs: { material: 'aluminium', weight: 7.9 }, + colors: [ 'black', 'white' ] + } +] +*/ +// STEP_END + +// STEP_START filter2 +// names of bikes made from an alloy +const res36 = await client.json.get("bikes:inventory", { + path: "$..[?(@.specs.material == 'alloy')].model" +}); +console.log(res36); // ['Weywot', 'Mimas'] +// STEP_END +// REMOVE_START +assert.deepEqual(res36, ["Weywot", "Mimas"]); +// REMOVE_END + +// STEP_START filter3 +const res37 = await client.json.get("bikes:inventory", { + path: "$..[?(@.specs.material =~ '(?i)al')].model" +}); +console.log(res37); // ['Quaoar', 'Weywot', 'Salacia', 'Mimas'] +// STEP_END + +// REMOVE_START +assert.deepEqual(res37, ["Quaoar", "Weywot", "Salacia", "Mimas"]); +// REMOVE_END + +// STEP_START filter4 +const res37a = await client.json.set( + 'bikes:inventory', + '$.inventory.mountain_bikes[0].regex_pat', + '(?i)al' +); + +const res37b = await client.json.set( + 'bikes:inventory', + '$.inventory.mountain_bikes[1].regex_pat', + '(?i)al' +); + +const res37c = await client.json.set( + 'bikes:inventory', + '$.inventory.mountain_bikes[2].regex_pat', + '(?i)al' +); + +const res37d = await client.json.get( + 'bikes:inventory', + { path: '$.inventory.mountain_bikes[?(@.specs.material =~ @.regex_pat)].model' } +); +console.log(res37d); // ['Quaoar', 'Weywot'] +// STEP_END + +// STEP_START update_bikes +const res38 = await client.json.get("bikes:inventory", { + path: "$..price" +}); +console.log(res38); // [1920, 2072, 3264, 1475, 3941] + +const res39 = await client.json.numIncrBy("bikes:inventory", "$..price", -100); +console.log(res39); // [1820, 1972, 3164, 1375, 3841] + +const res40 = await client.json.numIncrBy("bikes:inventory", "$..price", 100); +console.log(res40); // [1920, 2072, 3264, 1475, 3941] +// STEP_END + +// REMOVE_START +assert.deepEqual(res40.sort(), [1475, 1920, 2072, 3264, 3941]); +// REMOVE_END + +// STEP_START update_filters1 +const res40a = await client.json.set( + 'bikes:inventory', + '$.inventory.*[?(@.price<2000)].price', + 1500 +); + +// Get all prices from the inventory +const res40b = await client.json.get( + 'bikes:inventory', + { path: "$..price" } +); +console.log(res40b); // [1500, 2072, 3264, 1500, 3941] +// STEP_END + +// STEP_START update_filters2 +const res41 = await client.json.arrAppend( + "bikes:inventory", "$.inventory.*[?(@.price<2000)].colors", "pink" +); +console.log(res41); // [3, 3] + +const res42 = await client.json.get("bikes:inventory", { + path: "$..[*].colors" +}); +console.log(res42); // [['black', 'silver', 'pink'], ['black', 'white'], ['black', 'silver', 'pink']] +// STEP_END + +// REMOVE_START +assert.deepEqual(res42, [ + ["black", "silver", "pink"], + ["black", "white"], + ["black", "silver", "pink"], +]); +await client.close(); +// REMOVE_END diff --git a/doctests/dt-list.js b/doctests/dt-list.js new file mode 100644 index 00000000000..a2d0fb86c66 --- /dev/null +++ b/doctests/dt-list.js @@ -0,0 +1,329 @@ +// EXAMPLE: list_tutorial +// HIDE_START +import assert from 'assert'; +import { createClient } from 'redis'; + +const client = createClient(); +await client.connect(); +// HIDE_END +// REMOVE_START +await client.del('bikes:repairs'); +await client.del('bikes:finished'); +// REMOVE_END + +// STEP_START queue +const res1 = await client.lPush('bikes:repairs', 'bike:1'); +console.log(res1); // 1 + +const res2 = await client.lPush('bikes:repairs', 'bike:2'); +console.log(res2); // 2 + +const res3 = await client.rPop('bikes:repairs'); +console.log(res3); // bike:1 + +const res4 = await client.rPop('bikes:repairs'); +console.log(res4); // bike:2 +// STEP_END + +// REMOVE_START +assert.equal(res1, 1); +assert.equal(res2, 2); +assert.equal(res3, 'bike:1'); +assert.equal(res4, 'bike:2'); +// REMOVE_END + +// STEP_START stack +const res5 = await client.lPush('bikes:repairs', 'bike:1'); +console.log(res5); // 1 + +const res6 = await client.lPush('bikes:repairs', 'bike:2'); +console.log(res6); // 2 + +const res7 = await client.lPop('bikes:repairs'); +console.log(res7); // bike:2 + +const res8 = await client.lPop('bikes:repairs'); +console.log(res8); // bike:1 +// STEP_END + +// REMOVE_START +assert.equal(res5, 1); +assert.equal(res6, 2); +assert.equal(res7, 'bike:2'); +assert.equal(res8, 'bike:1'); +// REMOVE_END + +// STEP_START lLen +const res9 = await client.lLen('bikes:repairs'); +console.log(res9); // 0 +// STEP_END + +// REMOVE_START +assert.equal(res9, 0); +// REMOVE_END + +// STEP_START lMove_lRange +const res10 = await client.lPush('bikes:repairs', 'bike:1'); +console.log(res10); // 1 + +const res11 = await client.lPush('bikes:repairs', 'bike:2'); +console.log(res11); // 2 + +const res12 = await client.lMove('bikes:repairs', 'bikes:finished', 'LEFT', 'LEFT'); +console.log(res12); // 'bike:2' + +const res13 = await client.lRange('bikes:repairs', 0, -1); +console.log(res13); // ['bike:1'] + +const res14 = await client.lRange('bikes:finished', 0, -1); +console.log(res14); // ['bike:2'] +// STEP_END + +// REMOVE_START +assert.equal(res10, 1); +assert.equal(res11, 2); +assert.equal(res12, 'bike:2'); +assert.deepEqual(res13, ['bike:1']); +assert.deepEqual(res14, ['bike:2']); +await client.del('bikes:repairs'); +// REMOVE_END + +// STEP_START lPush_rPush +const res15 = await client.rPush('bikes:repairs', 'bike:1'); +console.log(res15); // 1 + +const res16 = await client.rPush('bikes:repairs', 'bike:2'); +console.log(res16); // 2 + +const res17 = await client.lPush('bikes:repairs', 'bike:important_bike'); +console.log(res17); // 3 + +const res18 = await client.lRange('bikes:repairs', 0, -1); +console.log(res18); // ['bike:important_bike', 'bike:1', 'bike:2'] +// STEP_END + +// REMOVE_START +assert.equal(res15, 1); +assert.equal(res16, 2); +assert.equal(res17, 3); +assert.deepEqual(res18, ['bike:important_bike', 'bike:1', 'bike:2']); +await client.del('bikes:repairs'); +// REMOVE_END + +// STEP_START variadic +const res19 = await client.rPush('bikes:repairs', ['bike:1', 'bike:2', 'bike:3']); +console.log(res19); // 3 + +const res20 = await client.lPush( + 'bikes:repairs', ['bike:important_bike', 'bike:very_important_bike'] +); +console.log(res20); // 5 + +const res21 = await client.lRange('bikes:repairs', 0, -1); +console.log(res21); // ['bike:very_important_bike', 'bike:important_bike', 'bike:1', 'bike:2', 'bike:3'] +// STEP_END + +// REMOVE_START +assert.equal(res19, 3); +assert.equal(res20, 5); +assert.deepEqual(res21, [ + 'bike:very_important_bike', + 'bike:important_bike', + 'bike:1', + 'bike:2', + 'bike:3', +]); +await client.del('bikes:repairs'); +// REMOVE_END + +// STEP_START lPop_rPop +const res22 = await client.rPush('bikes:repairs', ['bike:1', 'bike:2', 'bike:3']); +console.log(res22); // 3 + +const res23 = await client.rPop('bikes:repairs'); +console.log(res23); // 'bike:3' + +const res24 = await client.lPop('bikes:repairs'); +console.log(res24); // 'bike:1' + +const res25 = await client.rPop('bikes:repairs'); +console.log(res25); // 'bike:2' + +const res26 = await client.rPop('bikes:repairs'); +console.log(res26); // null +// STEP_END + +// REMOVE_START +assert.deepEqual(res22, 3); +assert.equal(res23, 'bike:3'); +assert.equal(res24, 'bike:1'); +assert.equal(res25, 'bike:2'); +assert.equal(res26, null); +// REMOVE_END + +// STEP_START lTrim +const res27 = await client.lPush( + 'bikes:repairs', ['bike:1', 'bike:2', 'bike:3', 'bike:4', 'bike:5'] +); +console.log(res27); // 5 + +const res28 = await client.lTrim('bikes:repairs', 0, 2); +console.log(res28); // OK + +const res29 = await client.lRange('bikes:repairs', 0, -1); +console.log(res29); // ['bike:5', 'bike:4', 'bike:3'] +// STEP_END + +// REMOVE_START +assert.equal(res27, 5); +assert.equal(res28, 'OK'); +assert.deepEqual(res29, ['bike:5', 'bike:4', 'bike:3']); +await client.del('bikes:repairs'); +// REMOVE_END + +// STEP_START lTrim_end_of_list +const res27eol = await client.rPush( + 'bikes:repairs', ['bike:1', 'bike:2', 'bike:3', 'bike:4', 'bike:5'] +); +console.log(res27eol); // 5 + +const res28eol = await client.lTrim('bikes:repairs', -3, -1); +console.log(res28eol); // 'OK' + +const res29eol = await client.lRange('bikes:repairs', 0, -1); +console.log(res29eol); // ['bike:3', 'bike:4', 'bike:5'] +// STEP_END + +// REMOVE_START +assert.equal(res27eol, 5); +assert.equal(res28eol, 'OK'); +assert.deepEqual(res29eol, ['bike:3', 'bike:4', 'bike:5']); +await client.del('bikes:repairs'); +// REMOVE_END + +// STEP_START brPop +const res31 = await client.rPush('bikes:repairs', ['bike:1', 'bike:2']); +console.log(res31); // 2 + +const res32 = await client.brPop('bikes:repairs', 1); +console.log(res32); // { key: 'bikes:repairs', element: 'bike:2' } + +const res33 = await client.brPop('bikes:repairs', 1); +console.log(res33); // { key: 'bikes:repairs', element: 'bike:1' } + +const res34 = await client.brPop('bikes:repairs', 1); +console.log(res34); // null +// STEP_END + +// REMOVE_START +assert.equal(res31, 2); +assert.deepEqual(res32, { key: 'bikes:repairs', element: 'bike:2' }); +assert.deepEqual(res33, { key: 'bikes:repairs', element: 'bike:1' }); +assert.equal(res34, null); +await client.del('bikes:repairs'); +await client.del('new_bikes'); +// REMOVE_END + +// STEP_START rule_1 +const res35 = await client.del('new_bikes'); +console.log(res35); // 0 + +const res36 = await client.lPush('new_bikes', ['bike:1', 'bike:2', 'bike:3']); +console.log(res36); // 3 +// STEP_END + +// REMOVE_START +assert.equal(res35, 0); +assert.equal(res36, 3); +await client.del('new_bikes'); +// REMOVE_END + +// STEP_START rule_1.1 +const res37 = await client.set('new_bikes', 'bike:1'); +console.log(res37); // 'OK' + +const res38 = await client.type('new_bikes'); +console.log(res38); // 'string' + +try { + const res39 = await client.lPush('new_bikes', 'bike:2', 'bike:3'); + // redis.exceptions.ResponseError: + // [SimpleError: WRONGTYPE Operation against a key holding the wrong kind of value] +} +catch(e){ + console.log(e); +} +// STEP_END + +// REMOVE_START +assert.equal(res37, 'OK'); +assert.equal(res38, 'string'); +await client.del('new_bikes'); +// REMOVE_END + +// STEP_START rule_2 +await client.lPush('bikes:repairs', ['bike:1', 'bike:2', 'bike:3']); +console.log(res36); // 3 + +const res40 = await client.exists('bikes:repairs') +console.log(res40); // 1 + +const res41 = await client.lPop('bikes:repairs'); +console.log(res41); // 'bike:3' + +const res42 = await client.lPop('bikes:repairs'); +console.log(res42); // 'bike:2' + +const res43 = await client.lPop('bikes:repairs'); +console.log(res43); // 'bike:1' + +const res44 = await client.exists('bikes:repairs'); +console.log(res44); // 0 +// STEP_END + +// REMOVE_START +assert.equal(res40, 1); +assert.equal(res41, 'bike:3'); +assert.equal(res42, 'bike:2'); +assert.equal(res43, 'bike:1'); +assert.equal(res44, 0); +await client.del('bikes:repairs'); +// REMOVE_END + +// STEP_START rule_3 +const res45 = await client.del('bikes:repairs'); +console.log(res45); // 0 + +const res46 = await client.lLen('bikes:repairs'); +console.log(res46); // 0 + +const res47 = await client.lPop('bikes:repairs'); +console.log(res47); // null +// STEP_END + +// REMOVE_START +assert.equal(res45, 0); +assert.equal(res46, 0); +assert.equal(res47, null); +// REMOVE_END + +// STEP_START lTrim.1 +const res48 = await client.lPush( + 'bikes:repairs', ['bike:1', 'bike:2', 'bike:3', 'bike:4', 'bike:5'] +); +console.log(res48); // 5 + +const res49 = await client.lTrim('bikes:repairs', 0, 2); +console.log(res49); // 'OK' + +const res50 = await client.lRange('bikes:repairs', 0, -1); +console.log(res50); // ['bike:5', 'bike:4', 'bike:3'] +// STEP_END + +// REMOVE_START +assert.equal(res48, 5); +assert.equal(res49, 'OK'); +assert.deepEqual(res50, ['bike:5', 'bike:4', 'bike:3']); +await client.del('bikes:repairs'); +await client.close(); +// REMOVE_END \ No newline at end of file diff --git a/doctests/dt-set.js b/doctests/dt-set.js new file mode 100644 index 00000000000..e3d34096399 --- /dev/null +++ b/doctests/dt-set.js @@ -0,0 +1,176 @@ +// EXAMPLE: sets_tutorial +// HIDE_START +import assert from 'assert'; +import { createClient } from 'redis'; + +const client = createClient(); +await client.connect(); +// HIDE_END +// REMOVE_START +await client.del('bikes:racing:france') +await client.del('bikes:racing:usa') +// REMOVE_END + +// STEP_START sAdd +const res1 = await client.sAdd('bikes:racing:france', 'bike:1') +console.log(res1) // >>> 1 + +const res2 = await client.sAdd('bikes:racing:france', 'bike:1') +console.log(res2) // >>> 0 +const res3 = await client.sAdd('bikes:racing:france', ['bike:2', 'bike:3']) +console.log(res3) // >>> 2 +const res4 = await client.sAdd('bikes:racing:usa', ['bike:1', 'bike:4']) +console.log(res4) // >>> 2 +// STEP_END + +// REMOVE_START +assert.equal(res1, 1) +assert.equal(res2, 0) +assert.equal(res3, 2) +assert.equal(res4, 2) +// REMOVE_END + +// STEP_START sIsMember +// HIDE_START +await client.del('bikes:racing:france') +await client.del('bikes:racing:usa') +await client.sAdd('bikes:racing:france', 'bike:1', 'bike:2', 'bike:3') +await client.sAdd('bikes:racing:usa', 'bike:1', 'bike:4') +// HIDE_END +const res5 = await client.sIsMember('bikes:racing:usa', 'bike:1') +console.log(res5) // >>> 1 + +const res6 = await client.sIsMember('bikes:racing:usa', 'bike:2') +console.log(res6) // >>> 0 +// STEP_END + +// REMOVE_START +assert.equal(res5, 1) +assert.equal(res6, 0) +// REMOVE_END + +// STEP_START sinster +// HIDE_START +await client.del('bikes:racing:france') +await client.del('bikes:racing:usa') +await client.sAdd('bikes:racing:france', 'bike:1', 'bike:2', 'bike:3') +await client.sAdd('bikes:racing:usa', 'bike:1', 'bike:4') +// HIDE_END +const res7 = await client.sInter('bikes:racing:france', 'bikes:racing:usa') +console.log(res7) // >>> {'bike:1'} +// STEP_END + +// REMOVE_START +assert.deepEqual(res7, [ 'bike:1' ]) +// REMOVE_END + +// STEP_START sCard +// HIDE_START +await client.del('bikes:racing:france') +await client.sAdd('bikes:racing:france', ['bike:1', 'bike:2', 'bike:3']) +// HIDE_END +const res8 = await client.sCard('bikes:racing:france') +console.log(res8) // >>> 3 +// STEP_END + +// REMOVE_START +assert.equal(res8, 3) +await client.del('bikes:racing:france') +// REMOVE_END + +// STEP_START sAdd_sMembers +const res9 = await client.sAdd('bikes:racing:france', ['bike:1', 'bike:2', 'bike:3']) +console.log(res9) // >>> 3 + +const res10 = await client.sMembers('bikes:racing:france') +console.log(res10) // >>> ['bike:1', 'bike:2', 'bike:3'] +// STEP_END + +// REMOVE_START +assert.equal(res9, 3) +assert.deepEqual(res10.sort(), ['bike:1', 'bike:2', 'bike:3']) +// REMOVE_END + +// STEP_START smIsMember +const res11 = await client.sIsMember('bikes:racing:france', 'bike:1') +console.log(res11) // >>> 1 + +const res12 = await client.smIsMember('bikes:racing:france', ['bike:2', 'bike:3', 'bike:4']) +console.log(res12) // >>> [1, 1, 0] +// STEP_END + +// REMOVE_START +assert.equal(res11, 1) +assert.deepEqual(res12, [1, 1, 0]) +// REMOVE_END + +// STEP_START sDiff +await client.sAdd('bikes:racing:france', ['bike:1', 'bike:2', 'bike:3']) +await client.sAdd('bikes:racing:usa', ['bike:1', 'bike:4']) +const res13 = await client.sDiff(['bikes:racing:france', 'bikes:racing:usa']) +console.log(res13) // >>> [ 'bike:2', 'bike:3' ] +// STEP_END + +// REMOVE_START +assert.deepEqual(res13.sort(), ['bike:2', 'bike:3'].sort()) +await client.del('bikes:racing:france') +await client.del('bikes:racing:usa') +// REMOVE_END + +// STEP_START multisets +await client.sAdd('bikes:racing:france', ['bike:1', 'bike:2', 'bike:3']) +await client.sAdd('bikes:racing:usa', ['bike:1', 'bike:4']) +await client.sAdd('bikes:racing:italy', ['bike:1', 'bike:2', 'bike:3', 'bike:4']) + +const res14 = await client.sInter( + ['bikes:racing:france', 'bikes:racing:usa', 'bikes:racing:italy'] +) +console.log(res14) // >>> ['bike:1'] + +const res15 = await client.sUnion( + ['bikes:racing:france', 'bikes:racing:usa', 'bikes:racing:italy'] +) +console.log(res15) // >>> ['bike:1', 'bike:2', 'bike:3', 'bike:4'] + +const res16 = await client.sDiff(['bikes:racing:france', 'bikes:racing:usa', 'bikes:racing:italy']) +console.log(res16) // >>> [] + +const res17 = await client.sDiff(['bikes:racing:usa', 'bikes:racing:france']) +console.log(res17) // >>> ['bike:4'] + +const res18 = await client.sDiff(['bikes:racing:france', 'bikes:racing:usa']) +console.log(res18) // >>> ['bike:2', 'bike:3'] +// STEP_END + +// REMOVE_START +assert.deepEqual(res14, ['bike:1']) +assert.deepEqual(res15.sort(), ['bike:1', 'bike:2', 'bike:3', 'bike:4']) +assert.deepEqual(res16, []) +assert.deepEqual(res17, ['bike:4']) +assert.deepEqual(res18.sort(), ['bike:2', 'bike:3'].sort()) +await client.del('bikes:racing:france') +await client.del('bikes:racing:usa') +await client.del('bikes:racing:italy') +// REMOVE_END + +// STEP_START sRem +await client.sAdd('bikes:racing:france', ['bike:1', 'bike:2', 'bike:3', 'bike:4', 'bike:5']) + +const res19 = await client.sRem('bikes:racing:france', 'bike:1') +console.log(res19) // >>> 1 + +const res20 = await client.sPop('bikes:racing:france') +console.log(res20) // >>> bike:3 or other random value + +const res21 = await client.sMembers('bikes:racing:france') +console.log(res21) // >>> ['bike:2', 'bike:4', 'bike:5']; depends on previous result + +const res22 = await client.sRandMember('bikes:racing:france') +console.log(res22) // >>> bike:4 or other random value +// STEP_END + +// REMOVE_START +assert.equal(res19, 1) +await client.close() +// none of the other results are deterministic +// REMOVE_END diff --git a/doctests/dt-ss.js b/doctests/dt-ss.js new file mode 100644 index 00000000000..bba85b11580 --- /dev/null +++ b/doctests/dt-ss.js @@ -0,0 +1,162 @@ +// EXAMPLE: ss_tutorial +// HIDE_START +import assert from 'assert'; +import { createClient } from 'redis'; + +const client = createClient(); +await client.connect(); +// HIDE_END + +// REMOVE_START +await client.flushDb(); +// REMOVE_END + +// STEP_START zadd +const res1 = await client.zAdd('racer_scores', { score: 10, value: 'Norem' }); +console.log(res1); // >>> 1 + +const res2 = await client.zAdd('racer_scores', { score: 12, value: 'Castilla' }); +console.log(res2); // >>> 1 + +const res3 = await client.zAdd('racer_scores', [ + { score: 8, value: 'Sam-Bodden' }, + { score: 10, value: 'Royce' }, + { score: 6, value: 'Ford' }, + { score: 14, value: 'Prickett' }, + { score: 12, value: 'Castilla' } +]); +console.log(res3); // >>> 4 +// STEP_END + +// REMOVE_START +assert.equal(res1, 1) +assert.equal(res2, 1) +assert.equal(res3, 4) +// REMOVE_END + +// REMOVE_START +const count = await client.zCard('racer_scores'); +console.assert(count === 6); +// REMOVE_END + +// STEP_START zrange +const res4 = await client.zRange('racer_scores', 0, -1); +console.log(res4); // >>> ['Ford', 'Sam-Bodden', 'Norem', 'Royce', 'Castilla', 'Prickett'] +// STEP_END + +// REMOVE_START +assert.deepEqual(res4, ['Ford', 'Sam-Bodden', 'Norem', 'Royce', 'Castilla', 'Prickett']) +// REMOVE_END + +// STEP_START zrange_withscores +const res6 = await client.zRangeWithScores('racer_scores', 0, -1); +console.log(res6); +// >>> [ +// { value: 'Ford', score: 6 }, { value: 'Sam-Bodden', score: 8 }, +// { value: 'Norem', score: 10 }, { value: 'Royce', score: 10 }, +// { value: 'Castilla', score: 12 }, { value: 'Prickett', score: 14 } +// ] +// STEP_END + +// REMOVE_START +assert.deepEqual(res6, [ { value: 'Ford', score: 6 }, { value: 'Sam-Bodden', score: 8 }, { value: 'Norem', score: 10 }, { value: 'Royce', score: 10 }, { value: 'Castilla', score: 12 }, { value: 'Prickett', score: 14 } ] +) +// REMOVE_END + +// STEP_START zrangebyscore +const res7 = await client.zRangeByScore('racer_scores', '-inf', 10); +console.log(res7); // >>> ['Ford', 'Sam-Bodden', 'Norem', 'Royce'] +// STEP_END + +// REMOVE_START +assert.deepEqual(res7, ['Ford', 'Sam-Bodden', 'Norem', 'Royce']) +// REMOVE_END + +// STEP_START zremrangebyscore +const res8 = await client.zRem('racer_scores', 'Castilla'); +console.log(res8); // >>> 1 + +const res9 = await client.zRemRangeByScore('racer_scores', '-inf', 9); +console.log(res9); // >>> 2 + +// REMOVE_START +assert.equal(res8, 1) +assert.equal(res9, 2) +// REMOVE_END + +const res10 = await client.zRange('racer_scores', 0, -1); +console.log(res10); // >>> ['Norem', 'Royce', 'Prickett'] +// STEP_END + +// REMOVE_START +assert.deepEqual(res10, ['Norem', 'Royce', 'Prickett']) +// REMOVE_END + +// REMOVE_START +const count2 = await client.zCard('racer_scores'); +console.assert(count2 === 3); +// REMOVE_END + +// STEP_START zrank +const res11 = await client.zRank('racer_scores', 'Norem'); +console.log(res11); // >>> 0 + +const res12 = await client.zRevRank('racer_scores', 'Norem'); +console.log(res12); // >>> 2 +// STEP_END + +// STEP_START zadd_lex +const res13 = await client.zAdd('racer_scores', [ + { score: 0, value: 'Norem' }, + { score: 0, value: 'Sam-Bodden' }, + { score: 0, value: 'Royce' }, + { score: 0, value: 'Ford' }, + { score: 0, value: 'Prickett' }, + { score: 0, value: 'Castilla' } +]); +console.log(res13); // >>> 3 + +// REMOVE_START +assert.equal(count2, 3) +assert.equal(res11, 0) +assert.equal(res12, 2) +assert.equal(res13, 3) +// REMOVE_END + +const res14 = await client.zRange('racer_scores', 0, -1); +console.log(res14); // >>> ['Castilla', 'Ford', 'Norem', 'Prickett', 'Royce', 'Sam-Bodden'] + +const res15 = await client.zRangeByLex('racer_scores', '[A', '[L'); +console.log(res15); // >>> ['Castilla', 'Ford'] +// STEP_END + +// REMOVE_START +assert.deepEqual(res14, ['Castilla', 'Ford', 'Norem', 'Prickett', 'Royce', 'Sam-Bodden']) +assert.deepEqual(res15, ['Castilla', 'Ford']) +// REMOVE_END + +// STEP_START leaderboard +const res16 = await client.zAdd('racer_scores', { score: 100, value: 'Wood' }); +console.log(res16); // >>> 1 + +const res17 = await client.zAdd('racer_scores', { score: 100, value: 'Henshaw' }); +console.log(res17); // >>> 1 + +const res18 = await client.zAdd('racer_scores', { score: 150, value: 'Henshaw' }, { nx: true }); +console.log(res18); // >>> 0 + +const res19 = await client.zIncrBy('racer_scores', 50, 'Wood'); +console.log(res19); // >>> 150.0 + +const res20 = await client.zIncrBy('racer_scores', 50, 'Henshaw'); +console.log(res20); // >>> 200.0 +// STEP_END + +// REMOVE_START +assert.equal(res16, 1) +assert.equal(res17, 1) +assert.equal(res18, 0) +assert.equal(res19, 150.0) +assert.equal(res20, 200.0) +await client.close(); +// REMOVE_END diff --git a/doctests/dt-streams.js b/doctests/dt-streams.js new file mode 100644 index 00000000000..00768654730 --- /dev/null +++ b/doctests/dt-streams.js @@ -0,0 +1,366 @@ +// EXAMPLE: stream_tutorial +// HIDE_START +import assert from 'assert'; +import { + createClient +} from 'redis'; + +const client = await createClient(); +await client.connect(); +// HIDE_END +// REMOVE_START +await client.flushDb(); +// REMOVE_END + +// STEP_START xAdd +const res1 = await client.xAdd( + 'race:france', '*', { + 'rider': 'Castilla', + 'speed': '30.2', + 'position': '1', + 'location_id': '1' + } +); +console.log(res1); // >>> 1700073067968-0 N.B. actual values will differ from these examples + +const res2 = await client.xAdd( + 'race:france', '*', { + 'rider': 'Norem', + 'speed': '28.8', + 'position': '3', + 'location_id': '1' + }, +); +console.log(res2); // >>> 1692629594113-0 + +const res3 = await client.xAdd( + 'race:france', '*', { + 'rider': 'Prickett', + 'speed': '29.7', + 'position': '2', + 'location_id': '1' + }, +); +console.log(res3); // >>> 1692629613374-0 +// STEP_END + +// REMOVE_START +assert.equal(await client.xLen('race:france'), 3); +// REMOVE_END + +// STEP_START xRange +const res4 = await client.xRange('race:france', '1691765278160-0', '+', {COUNT: 2}); +console.log(res4); // >>> [{ id: '1692629576966-0', message: { rider: 'Castilla', speed: '30.2', position: '1', location_id: '1' } }, { id: '1692629594113-0', message: { rider: 'Norem', speed: '28.8', position: '3', location_id: '1' } }] +// STEP_END + +// STEP_START xread_block +const res5 = await client.xRead({ + key: 'race:france', + id: '0-0' +}, { + COUNT: 100, + BLOCK: 300 +}); +console.log(res5); // >>> [{ name: 'race:france', messages: [{ id: '1692629576966-0', message: { rider: 'Castilla', speed: '30.2', position: '1', location_id: '1' } }, { id: '1692629594113-0', message: { rider: 'Norem', speed: '28.8', position: '3', location_id: '1' } }, { id: '1692629613374-0', message: { rider: 'Prickett', speed: '29.7', position: '2', location_id: '1' } }] }] +// STEP_END + +// STEP_START xAdd_2 +const res6 = await client.xAdd( + 'race:france', '*', { + 'rider': 'Castilla', + 'speed': '29.9', + 'position': '1', + 'location_id': '2' + } +); +console.log(res6); // >>> 1692629676124-0 +// STEP_END + +// STEP_START xlen +const res7 = await client.xLen('race:france'); +console.log(res7); // >>> 4 +// STEP_END + + +// STEP_START xAdd_id +const res8 = await client.xAdd('race:usa', '0-1', { + 'racer': 'Castilla' +}); +console.log(res8); // >>> 0-1 + +const res9 = await client.xAdd('race:usa', '0-2', { + 'racer': 'Norem' +}); +console.log(res9); // >>> 0-2 +// STEP_END + +// STEP_START xAdd_bad_id +try { + const res10 = await client.xAdd('race:usa', '0-1', { + 'racer': 'Prickett' + }); + console.log(res10); // >>> 0-1 +} catch (error) { + console.error(error); // >>> [SimpleError: ERR The ID specified in XADD is equal or smaller than the target stream top item] +} +// STEP_END + +// STEP_START xadd_7 +const res11a = await client.xAdd('race:usa', '0-*', { racer: 'Norem' }); +console.log(res11a); // >>> 0-3 +// STEP_END + +// STEP_START xRange_all +const res11 = await client.xRange('race:france', '-', '+'); +console.log(res11); // >>> [{ id: '1692629576966-0', message: { rider: 'Castilla', speed: '30.2', position: '1', location_id: '1' } }, { id: '1692629594113-0', message: { rider: 'Norem', speed: '28.8', position: '3', location_id: '1' } }, { id: '1692629613374-0', message: { rider: 'Prickett', speed: '29.7', position: '2', location_id: '1' } }, { id: '1692629676124-0', message: { rider: 'Castilla', speed: '29.9', position: '1', location_id: '2' } }] +// STEP_END + +// STEP_START xRange_time +const res12 = await client.xRange('race:france', '1692629576965', '1692629576967'); +console.log(res12); // >>> [{ id: '1692629576966-0', message: { rider: 'Castilla', speed: '30.2', position: '1', location_id: '1' } }] +// STEP_END + +// STEP_START xRange_step_1 +const res13 = await client.xRange('race:france', '-', '+', {COUNT: 2}); +console.log(res13); // >>> [{ id: '1692629576966-0', message: { rider: 'Castilla', speed: '30.2', position: '1', location_id: '1' } }, { id: '1692629594113-0', message: { rider: 'Norem', speed: '28.8', position: '3', location_id: '1' } }] +// STEP_END + +// STEP_START xRange_step_2 +const res14 = await client.xRange('race:france', '(1692629594113-0', '+', {COUNT: 2}); +console.log(res14); // >>> [{ id: '1692629613374-0', message: { rider: 'Prickett', speed: '29.7', position: '2', location_id: '1' } }, { id: '1692629676124-0', message: { rider: 'Castilla', speed: '29.9', position: '1', location_id: '2' } }] +// STEP_END + +// STEP_START xRange_empty +const res15 = await client.xRange('race:france', '(1692629676124-0', '+', {COUNT: 2}); +console.log(res15); // >>> [] +// STEP_END + +// STEP_START xrevrange +const res16 = await client.xRevRange('race:france', '+', '-', {COUNT: 1}); +console.log( + res16 +); // >>> [{ id: '1692629676124-0', message: { rider: 'Castilla', speed: '29.9', position: '1', location_id: '2' } }] +// STEP_END + +// STEP_START xread +const res17 = await client.xRead({ + key: 'race:france', + id: '0-0' +}, { + COUNT: 2 +}); +console.log(res17); // >>> [{ name: 'race:france', messages: [{ id: '1692629576966-0', message: { rider: 'Castilla', speed: '30.2', position: '1', location_id: '1' } }, { id: '1692629594113-0', message: { rider: 'Norem', speed: '28.8', position: '3', location_id: '1' } }] }] +// STEP_END + +// STEP_START xgroup_create +const res18 = await client.xGroupCreate('race:france', 'france_riders', '$'); +console.log(res18); // >>> OK +// STEP_END + +// STEP_START xgroup_create_mkstream +const res19 = await client.xGroupCreate('race:italy', 'italy_riders', '$', { + MKSTREAM: true +}); +console.log(res19); // >>> OK +// STEP_END + +// STEP_START xgroup_read +await client.xAdd('race:italy', '*', { + 'rider': 'Castilla' +}); +await client.xAdd('race:italy', '*', { + 'rider': 'Royce' +}); +await client.xAdd('race:italy', '*', { + 'rider': 'Sam-Bodden' +}); +await client.xAdd('race:italy', '*', { + 'rider': 'Prickett' +}); +await client.xAdd('race:italy', '*', { + 'rider': 'Norem' +}); + +const res20 = await client.xReadGroup( + 'italy_riders', + 'Alice', { + key: 'race:italy', + id: '>' + }, { + COUNT: 1 + } +); +console.log(res20); // >>> [{ name: 'race:italy', messages: [{ id: '1692629925771-0', message: { rider: 'Castilla' } }] }] +// STEP_END + +// STEP_START xgroup_read_id +const res21 = await client.xReadGroup( + 'italy_riders', + 'Alice', { + key: 'race:italy', + id: '0' + }, { + COUNT: 1 + } +); +console.log(res21); // >>> [{ name: 'race:italy', messages: [{ id: '1692629925771-0', message: { rider: 'Castilla' } }] }] +// STEP_END + +// STEP_START xack +const res22 = await client.xAck('race:italy', 'italy_riders', '1692629925771-0') +console.log(res22); // >>> 1 + +const res23 = await client.xReadGroup( + 'italy_riders', + 'Alice', { + key: 'race:italy', + id: '0' + }, { + COUNT: 1 + } +); +console.log(res23); // >>> [{ name: 'race:italy', messages: [] }] +// STEP_END + +// STEP_START xgroup_read_bob +const res24 = await client.xReadGroup( + 'italy_riders', + 'Bob', { + key: 'race:italy', + id: '>' + }, { + COUNT: 2 + } +); +console.log(res24); // >>> [{ name: 'race:italy', messages: [{ id: '1692629925789-0', message: { rider: 'Royce' } }, { id: '1692629925790-0', message: { rider: 'Sam-Bodden' } }] }] +// STEP_END + +// STEP_START xpending +const res25 = await client.xPending('race:italy', 'italy_riders'); +console.log(res25); // >>> {'pending': 2, 'firstId': '1692629925789-0', 'lastId': '1692629925790-0', 'consumers': [{'name': 'Bob', 'deliveriesCounter': 2}]} +// STEP_END + +// STEP_START xpending_plus_minus +const res26 = await client.xPendingRange('race:italy', 'italy_riders', '-', '+', 10); +console.log(res26); // >>> [{'id': '1692629925789-0', 'consumer': 'Bob', 'millisecondsSinceLastDelivery': 31084, 'deliveriesCounter:': 1}, {'id': '1692629925790-0', 'consumer': 'Bob', 'millisecondsSinceLastDelivery': 31084, 'deliveriesCounter': 1}] +// STEP_END + +// STEP_START xRange_pending +const res27 = await client.xRange('race:italy', '1692629925789-0', '1692629925789-0'); +console.log(res27); // >>> [{ id: '1692629925789-0', message: { rider: 'Royce' } }] +// STEP_END + +// STEP_START xclaim +const res28 = await client.xClaim( + 'race:italy', 'italy_riders', 'Alice', 60000, ['1692629925789-0'] +); +console.log(res28); // >>> [{ id: '1692629925789-0', message: { rider: 'Royce' } }] +// STEP_END + +// STEP_START xautoclaim +const res29 = await client.xAutoClaim('race:italy', 'italy_riders', 'Alice', 1, '0-0', { + COUNT: 1 +}); +console.log(res29); // >>> { nextId: '1692629925790-0', messages: [{ id: '1692629925789-0', message: { rider: 'Royce' } }], deletedMessages: [] } +// STEP_END + +// STEP_START xautoclaim_cursor +const res30 = await client.xAutoClaim( + 'race:italy', 'italy_riders', 'Alice', 1, '(1692629925789-0', + { + COUNT: 1 + } +); +console.log(res30); // >>> { nextId: '0-0', messages: [{ id: '1692629925790-0', message: { rider: 'Sam-Bodden' } }], deletedMessages: [] } +// STEP_END + +// STEP_START xinfo +const res31 = await client.xInfoStream('race:italy'); +console.log(res31); // >>> { length: 5, 'radix-tree-keys': 1, 'radix-tree-nodes': 2, 'last-generated-id': '1692629926436-0', 'max-deleted-entry-id': '0-0', 'entries-added': 5, 'recorded-first-entry-id': '1692629925771-0', groups: 1, 'first-entry': { id: '1692629925771-0', message: { rider: 'Castilla' } }, 'last-entry': { id: '1692629926436-0', message: { rider: 'Norem' } } } +// STEP_END + +// STEP_START xinfo_groups +const res32 = await client.xInfoGroups('race:italy'); +console.log(res32); // >>> [{ name: 'italy_riders', consumers: 2, pending: 3, 'last-delivered-id': '1692629925790-0', 'entries-read': 3, lag: 2 }] +// STEP_END + +// STEP_START xinfo_consumers +const res33 = await client.xInfoConsumers('race:italy', 'italy_riders'); +console.log(res33); // >>> [{ name: 'Alice', pending: 3, idle: 170582, inactive: 170582 }, { name: 'Bob', pending: 0, idle: 489404, inactive: 489404 }] +// STEP_END + +// STEP_START maxlen +await client.xAdd('race:italy', '*', { + 'rider': 'Jones' +}, { + TRIM: { + strategy: 'MAXLEN', + strategyModifier: '~', + threshold: 2 + } +}); +await client.xAdd('race:italy', '*', { + 'rider': 'Wood' +}, { + TRIM: { + strategy: 'MAXLEN', + strategyModifier: '~', + threshold: 2 + } +}); +await client.xAdd('race:italy', '*', { + 'rider': 'Henshaw' +}, { + TRIM: { + strategy: 'MAXLEN', + strategyModifier: '~', + threshold: 2 + } +}); + +const res34 = await client.xLen('race:italy'); +console.log(res34); // >>> 8 + +const res35 = await client.xRange('race:italy', '-', '+'); +console.log(res35); // >>> [{ id: '1692629925771-0', message: { rider: 'Castilla' } }, { id: '1692629925789-0', message: { rider: 'Royce' } }, { id: '1692629925790-0', message: { rider: 'Sam-Bodden' } }, { id: '1692629925791-0', message: { rider: 'Prickett' } }, { id: '1692629926436-0', message: { rider: 'Norem' } }, { id: '1692630612602-0', message: { rider: 'Jones' } }, { id: '1692630641947-0', message: { rider: 'Wood' } }, { id: '1692630648281-0', message: { rider: 'Henshaw' } }] + +await client.xAdd('race:italy', '*', { + 'rider': 'Smith' +}, { + TRIM: { + strategy: 'MAXLEN', + strategyModifier: '=', + threshold: 2 + } +}); + +const res36 = await client.xRange('race:italy', '-', '+'); +console.log(res36); // >>> [{ id: '1692630648281-0', message: { rider: 'Henshaw' } }, { id: '1692631018238-0', message: { rider: 'Smith' } }] +// STEP_END + +// STEP_START xTrim +const res37 = await client.xTrim('race:italy', 'MAXLEN', 10, { + strategyModifier: '=', +}); +console.log(res37); // >>> 0 +// STEP_END + +// STEP_START xTrim2 +const res38 = await client.xTrim('race:italy', "MAXLEN", 10); +console.log(res38); // >>> 0 +// STEP_END + +// STEP_START xDel +const res39 = await client.xRange('race:italy', '-', '+'); +console.log(res39); // >>> [{ id: '1692630648281-0', message: { rider: 'Henshaw' } }, { id: '1692631018238-0', message: { rider: 'Smith' } }] + +const res40 = await client.xDel('race:italy', '1692631018238-0'); +console.log(res40); // >>> 1 + +const res41 = await client.xRange('race:italy', '-', '+'); +console.log(res41); // >>> [{ id: '1692630648281-0', message: { rider: 'Henshaw' } }] +// STEP_END + +// REMOVE_START +await client.quit(); +// REMOVE_END \ No newline at end of file diff --git a/doctests/dt-string.js b/doctests/dt-string.js new file mode 100644 index 00000000000..6f77709abfa --- /dev/null +++ b/doctests/dt-string.js @@ -0,0 +1,68 @@ +// EXAMPLE: set_tutorial +// HIDE_START +import assert from 'assert'; +import { createClient } from 'redis'; + +const client = createClient(); +await client.connect(); +// HIDE_END +// REMOVE_START +await client.flushDb(); +// REMOVE_END + +// STEP_START set_get +const res1 = await client.set("bike:1", "Deimos"); +console.log(res1); // OK +const res2 = await client.get("bike:1"); +console.log(res2); // Deimos +// STEP_END + +// REMOVE_START +assert.equal(res1, 'OK'); +assert.equal(res2, 'Deimos'); +// REMOVE_END + +// STEP_START setnx_xx +const res3 = await client.set("bike:1", "bike", {'NX': true}); +console.log(res3); // null +console.log(await client.get("bike:1")); // Deimos +const res4 = await client.set("bike:1", "bike", {'XX': true}); +console.log(res4); // OK +// STEP_END + +// REMOVE_START +assert.equal(res3, null); +assert.equal(res4, 'OK'); +// REMOVE_END + +// STEP_START mset +const res5 = await client.mSet([ + ["bike:1", "Deimos"], + ["bike:2", "Ares"], + ["bike:3", "Vanth"] +]); + +console.log(res5); // OK +const res6 = await client.mGet(["bike:1", "bike:2", "bike:3"]); +console.log(res6); // ['Deimos', 'Ares', 'Vanth'] +// STEP_END + +// REMOVE_START +assert.equal(res5, 'OK'); +assert.deepEqual(res6, ["Deimos", "Ares", "Vanth"]); +// REMOVE_END + +// STEP_START incr +await client.set("total_crashes", 0); +const res7 = await client.incr("total_crashes"); +console.log(res7); // 1 +const res8 = await client.incrBy("total_crashes", 10); +console.log(res8); // 11 +// STEP_END + +// REMOVE_START +assert.equal(res7, 1); +assert.equal(res8, 11); + +await client.close(); +// REMOVE_END diff --git a/doctests/dt-tdigest.js b/doctests/dt-tdigest.js new file mode 100644 index 00000000000..c59168076e3 --- /dev/null +++ b/doctests/dt-tdigest.js @@ -0,0 +1,85 @@ +// EXAMPLE: tdigest_tutorial +// HIDE_START +import assert from 'assert'; +import { createClient } from 'redis'; + +const client = createClient(); +await client.connect(); +// HIDE_END + +// REMOVE_START +await client.flushDb(); +// REMOVE_END + +// STEP_START tdig_start +const res1 = await client.tDigest.create('bikes:sales', 100); +console.log(res1); // >>> OK + +const res2 = await client.tDigest.add('bikes:sales', [21]); +console.log(res2); // >>> OK + +const res3 = await client.tDigest.add('bikes:sales', [150, 95, 75, 34]); +console.log(res3); // >>> OK +// STEP_END + +// REMOVE_START +assert.equal(res1, 'OK') +assert.equal(res2, 'OK') +assert.equal(res3, 'OK') +// REMOVE_END + +// STEP_START tdig_cdf +const res4 = await client.tDigest.create('racer_ages'); +console.log(res4); // >>> OK + +const res5 = await client.tDigest.add('racer_ages', [ + 45.88, 44.2, 58.03, 19.76, 39.84, 69.28, 50.97, 25.41, 19.27, 85.71, 42.63 +]); +console.log(res5); // >>> OK + +const res6 = await client.tDigest.rank('racer_ages', [50]); +console.log(res6); // >>> [7] + +const res7 = await client.tDigest.rank('racer_ages', [50, 40]); +console.log(res7); // >>> [7, 4] +// STEP_END + +// REMOVE_START +assert.equal(res4, 'OK') +assert.equal(res5, 'OK') +assert.deepEqual(res6, [7]) +assert.deepEqual(res7, [7, 4]) +// REMOVE_END + +// STEP_START tdig_quant +const res8 = await client.tDigest.quantile('racer_ages', [0.5]); +console.log(res8); // >>> [44.2] + +const res9 = await client.tDigest.byRank('racer_ages', [4]); +console.log(res9); // >>> [42.63] +// STEP_END + +// STEP_START tdig_min +const res10 = await client.tDigest.min('racer_ages'); +console.log(res10); // >>> 19.27 + +const res11 = await client.tDigest.max('racer_ages'); +console.log(res11); // >>> 85.71 +// STEP_END + +// REMOVE_START +assert.deepEqual(res8, [44.2]) +assert.deepEqual(res9, [42.63]) +assert.equal(res10, 19.27) +assert.equal(res11, 85.71) +// REMOVE_END + +// STEP_START tdig_reset +const res12 = await client.tDigest.reset('racer_ages'); +console.log(res12); // >>> OK +// STEP_END + +// REMOVE_START +assert.equal(res12, 'OK') +await client.close(); +// REMOVE_END diff --git a/doctests/dt-topk.js b/doctests/dt-topk.js new file mode 100644 index 00000000000..49b929aed8a --- /dev/null +++ b/doctests/dt-topk.js @@ -0,0 +1,48 @@ +// EXAMPLE: topk_tutorial +// HIDE_START +import assert from 'assert'; +import { createClient } from 'redis'; + +const client = createClient(); +await client.connect(); +// HIDE_END + +// REMOVE_START +await client.flushDb(); +// REMOVE_END + +// STEP_START topk +const res1 = await client.topK.reserve('bikes:keywords', 5, { + width: 2000, + depth: 7, + decay: 0.925 +}); +console.log(res1); // >>> OK + +const res2 = await client.topK.add('bikes:keywords', [ + 'store', + 'seat', + 'handlebars', + 'handles', + 'pedals', + 'tires', + 'store', + 'seat' +]); +console.log(res2); // >>> [null, null, null, null, null, 'handlebars', null, null] + +const res3 = await client.topK.list('bikes:keywords'); +console.log(res3); // >>> ['store', 'seat', 'pedals', 'tires', 'handles'] + +const res4 = await client.topK.query('bikes:keywords', ['store', 'handlebars']); +console.log(res4); // >>> [true, false] +// STEP_END + +// REMOVE_START +assert.equal(res1, 'OK') +assert.deepEqual(res2, [null, null, null, null, null, 'handlebars', null, null]) +assert.deepEqual(res3, ['store', 'seat', 'pedals', 'tires', 'handles']) +assert.deepEqual(res4, [1, 0]) +await client.close(); +// REMOVE_END + diff --git a/doctests/package-lock.json b/doctests/package-lock.json new file mode 100644 index 00000000000..2f30678e529 --- /dev/null +++ b/doctests/package-lock.json @@ -0,0 +1,889 @@ +{ + "name": "node-redis-doctests", + "version": "1.0.0", + "lockfileVersion": 3, + "requires": true, + "packages": { + "": { + "name": "node-redis-doctests", + "version": "1.0.0", + "dependencies": { + "@xenova/transformers": "^2.17.2", + "redis": "file:/packages/redis" + } + }, + "..": { + "name": "redis-monorepo", + "extraneous": true, + "workspaces": [ + "./packages/client", + "./packages/test-utils", + "./packages/bloom", + "./packages/json", + "./packages/search", + "./packages/time-series", + "./packages/entraid", + "./packages/redis" + ], + "devDependencies": { + "@istanbuljs/nyc-config-typescript": "^1.0.2", + "@release-it/bumper": "^7.0.5", + "@types/mocha": "^10.0.6", + "@types/node": "^20.11.16", + "gh-pages": "^6.1.1", + "mocha": "^10.2.0", + "nyc": "^15.1.0", + "release-it": "^19.0.2", + "ts-node": "^10.9.2", + "tsx": "^4.7.0", + "typedoc": "^0.25.7", + "typescript": "^5.3.3" + } + }, + "../../../../../packages/redis": {}, + "node_modules/@huggingface/jinja": { + "version": "0.2.2", + "resolved": "https://registry.npmjs.org/@huggingface/jinja/-/jinja-0.2.2.tgz", + "integrity": "sha512-/KPde26khDUIPkTGU82jdtTW9UAuvUTumCAbFs/7giR0SxsvZC4hru51PBvpijH6BVkHcROcvZM/lpy5h1jRRA==", + "license": "MIT", + "engines": { + "node": ">=18" + } + }, + "node_modules/@protobufjs/aspromise": { + "version": "1.1.2", + "resolved": "https://registry.npmjs.org/@protobufjs/aspromise/-/aspromise-1.1.2.tgz", + "integrity": "sha512-j+gKExEuLmKwvz3OgROXtrJ2UG2x8Ch2YZUxahh+s1F2HZ+wAceUNLkvy6zKCPVRkU++ZWQrdxsUeQXmcg4uoQ==", + "license": "BSD-3-Clause" + }, + "node_modules/@protobufjs/base64": { + "version": "1.1.2", + "resolved": "https://registry.npmjs.org/@protobufjs/base64/-/base64-1.1.2.tgz", + "integrity": "sha512-AZkcAA5vnN/v4PDqKyMR5lx7hZttPDgClv83E//FMNhR2TMcLUhfRUBHCmSl0oi9zMgDDqRUJkSxO3wm85+XLg==", + "license": "BSD-3-Clause" + }, + "node_modules/@protobufjs/codegen": { + "version": "2.0.4", + "resolved": "https://registry.npmjs.org/@protobufjs/codegen/-/codegen-2.0.4.tgz", + "integrity": "sha512-YyFaikqM5sH0ziFZCN3xDC7zeGaB/d0IUb9CATugHWbd1FRFwWwt4ld4OYMPWu5a3Xe01mGAULCdqhMlPl29Jg==", + "license": "BSD-3-Clause" + }, + "node_modules/@protobufjs/eventemitter": { + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/@protobufjs/eventemitter/-/eventemitter-1.1.0.tgz", + "integrity": "sha512-j9ednRT81vYJ9OfVuXG6ERSTdEL1xVsNgqpkxMsbIabzSo3goCjDIveeGv5d03om39ML71RdmrGNjG5SReBP/Q==", + "license": "BSD-3-Clause" + }, + "node_modules/@protobufjs/fetch": { + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/@protobufjs/fetch/-/fetch-1.1.0.tgz", + "integrity": "sha512-lljVXpqXebpsijW71PZaCYeIcE5on1w5DlQy5WH6GLbFryLUrBD4932W/E2BSpfRJWseIL4v/KPgBFxDOIdKpQ==", + "license": "BSD-3-Clause", + "dependencies": { + "@protobufjs/aspromise": "^1.1.1", + "@protobufjs/inquire": "^1.1.0" + } + }, + "node_modules/@protobufjs/float": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/@protobufjs/float/-/float-1.0.2.tgz", + "integrity": "sha512-Ddb+kVXlXst9d+R9PfTIxh1EdNkgoRe5tOX6t01f1lYWOvJnSPDBlG241QLzcyPdoNTsblLUdujGSE4RzrTZGQ==", + "license": "BSD-3-Clause" + }, + "node_modules/@protobufjs/inquire": { + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/@protobufjs/inquire/-/inquire-1.1.0.tgz", + "integrity": "sha512-kdSefcPdruJiFMVSbn801t4vFK7KB/5gd2fYvrxhuJYg8ILrmn9SKSX2tZdV6V+ksulWqS7aXjBcRXl3wHoD9Q==", + "license": "BSD-3-Clause" + }, + "node_modules/@protobufjs/path": { + "version": "1.1.2", + "resolved": "https://registry.npmjs.org/@protobufjs/path/-/path-1.1.2.tgz", + "integrity": "sha512-6JOcJ5Tm08dOHAbdR3GrvP+yUUfkjG5ePsHYczMFLq3ZmMkAD98cDgcT2iA1lJ9NVwFd4tH/iSSoe44YWkltEA==", + "license": "BSD-3-Clause" + }, + "node_modules/@protobufjs/pool": { + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/@protobufjs/pool/-/pool-1.1.0.tgz", + "integrity": "sha512-0kELaGSIDBKvcgS4zkjz1PeddatrjYcmMWOlAuAPwAeccUrPHdUqo/J6LiymHHEiJT5NrF1UVwxY14f+fy4WQw==", + "license": "BSD-3-Clause" + }, + "node_modules/@protobufjs/utf8": { + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/@protobufjs/utf8/-/utf8-1.1.0.tgz", + "integrity": "sha512-Vvn3zZrhQZkkBE8LSuW3em98c0FwgO4nxzv6OdSxPKJIEKY2bGbHn+mhGIPerzI4twdxaP8/0+06HBpwf345Lw==", + "license": "BSD-3-Clause" + }, + "node_modules/@types/long": { + "version": "4.0.2", + "resolved": "https://registry.npmjs.org/@types/long/-/long-4.0.2.tgz", + "integrity": "sha512-MqTGEo5bj5t157U6fA/BiDynNkn0YknVdh48CMPkTSpFTVmvao5UQmm7uEF6xBEo7qIMAlY/JSleYaE6VOdpaA==", + "license": "MIT" + }, + "node_modules/@types/node": { + "version": "24.0.10", + "resolved": "https://registry.npmjs.org/@types/node/-/node-24.0.10.tgz", + "integrity": "sha512-ENHwaH+JIRTDIEEbDK6QSQntAYGtbvdDXnMXnZaZ6k13Du1dPMmprkEHIL7ok2Wl2aZevetwTAb5S+7yIF+enA==", + "license": "MIT", + "dependencies": { + "undici-types": "~7.8.0" + } + }, + "node_modules/@xenova/transformers": { + "version": "2.17.2", + "resolved": "https://registry.npmjs.org/@xenova/transformers/-/transformers-2.17.2.tgz", + "integrity": "sha512-lZmHqzrVIkSvZdKZEx7IYY51TK0WDrC8eR0c5IMnBsO8di8are1zzw8BlLhyO2TklZKLN5UffNGs1IJwT6oOqQ==", + "license": "Apache-2.0", + "dependencies": { + "@huggingface/jinja": "^0.2.2", + "onnxruntime-web": "1.14.0", + "sharp": "^0.32.0" + }, + "optionalDependencies": { + "onnxruntime-node": "1.14.0" + } + }, + "node_modules/b4a": { + "version": "1.6.7", + "resolved": "https://registry.npmjs.org/b4a/-/b4a-1.6.7.tgz", + "integrity": "sha512-OnAYlL5b7LEkALw87fUVafQw5rVR9RjwGd4KUwNQ6DrrNmaVaUCgLipfVlzrPQ4tWOR9P0IXGNOx50jYCCdSJg==", + "license": "Apache-2.0" + }, + "node_modules/bare-events": { + "version": "2.5.4", + "resolved": "https://registry.npmjs.org/bare-events/-/bare-events-2.5.4.tgz", + "integrity": "sha512-+gFfDkR8pj4/TrWCGUGWmJIkBwuxPS5F+a5yWjOHQt2hHvNZd5YLzadjmDUtFmMM4y429bnKLa8bYBMHcYdnQA==", + "license": "Apache-2.0", + "optional": true + }, + "node_modules/bare-fs": { + "version": "4.1.6", + "resolved": "https://registry.npmjs.org/bare-fs/-/bare-fs-4.1.6.tgz", + "integrity": "sha512-25RsLF33BqooOEFNdMcEhMpJy8EoR88zSMrnOQOaM3USnOK2VmaJ1uaQEwPA6AQjrv1lXChScosN6CzbwbO9OQ==", + "license": "Apache-2.0", + "optional": true, + "dependencies": { + "bare-events": "^2.5.4", + "bare-path": "^3.0.0", + "bare-stream": "^2.6.4" + }, + "engines": { + "bare": ">=1.16.0" + }, + "peerDependencies": { + "bare-buffer": "*" + }, + "peerDependenciesMeta": { + "bare-buffer": { + "optional": true + } + } + }, + "node_modules/bare-os": { + "version": "3.6.1", + "resolved": "https://registry.npmjs.org/bare-os/-/bare-os-3.6.1.tgz", + "integrity": "sha512-uaIjxokhFidJP+bmmvKSgiMzj2sV5GPHaZVAIktcxcpCyBFFWO+YlikVAdhmUo2vYFvFhOXIAlldqV29L8126g==", + "license": "Apache-2.0", + "optional": true, + "engines": { + "bare": ">=1.14.0" + } + }, + "node_modules/bare-path": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/bare-path/-/bare-path-3.0.0.tgz", + "integrity": "sha512-tyfW2cQcB5NN8Saijrhqn0Zh7AnFNsnczRcuWODH0eYAXBsJ5gVxAUuNr7tsHSC6IZ77cA0SitzT+s47kot8Mw==", + "license": "Apache-2.0", + "optional": true, + "dependencies": { + "bare-os": "^3.0.1" + } + }, + "node_modules/bare-stream": { + "version": "2.6.5", + "resolved": "https://registry.npmjs.org/bare-stream/-/bare-stream-2.6.5.tgz", + "integrity": "sha512-jSmxKJNJmHySi6hC42zlZnq00rga4jjxcgNZjY9N5WlOe/iOoGRtdwGsHzQv2RlH2KOYMwGUXhf2zXd32BA9RA==", + "license": "Apache-2.0", + "optional": true, + "dependencies": { + "streamx": "^2.21.0" + }, + "peerDependencies": { + "bare-buffer": "*", + "bare-events": "*" + }, + "peerDependenciesMeta": { + "bare-buffer": { + "optional": true + }, + "bare-events": { + "optional": true + } + } + }, + "node_modules/base64-js": { + "version": "1.5.1", + "resolved": "https://registry.npmjs.org/base64-js/-/base64-js-1.5.1.tgz", + "integrity": "sha512-AKpaYlHn8t4SVbOHCy+b5+KKgvR4vrsD8vbvrbiQJps7fKDTkjkDry6ji0rUJjC0kzbNePLwzxq8iypo41qeWA==", + "funding": [ + { + "type": "github", + "url": "https://github.com/sponsors/feross" + }, + { + "type": "patreon", + "url": "https://www.patreon.com/feross" + }, + { + "type": "consulting", + "url": "https://feross.org/support" + } + ], + "license": "MIT" + }, + "node_modules/bl": { + "version": "4.1.0", + "resolved": "https://registry.npmjs.org/bl/-/bl-4.1.0.tgz", + "integrity": "sha512-1W07cM9gS6DcLperZfFSj+bWLtaPGSOHWhPiGzXmvVJbRLdG82sH/Kn8EtW1VqWVA54AKf2h5k5BbnIbwF3h6w==", + "license": "MIT", + "dependencies": { + "buffer": "^5.5.0", + "inherits": "^2.0.4", + "readable-stream": "^3.4.0" + } + }, + "node_modules/buffer": { + "version": "5.7.1", + "resolved": "https://registry.npmjs.org/buffer/-/buffer-5.7.1.tgz", + "integrity": "sha512-EHcyIPBQ4BSGlvjB16k5KgAJ27CIsHY/2JBmCRReo48y9rQ3MaUzWX3KVlBa4U7MyX02HdVj0K7C3WaB3ju7FQ==", + "funding": [ + { + "type": "github", + "url": "https://github.com/sponsors/feross" + }, + { + "type": "patreon", + "url": "https://www.patreon.com/feross" + }, + { + "type": "consulting", + "url": "https://feross.org/support" + } + ], + "license": "MIT", + "dependencies": { + "base64-js": "^1.3.1", + "ieee754": "^1.1.13" + } + }, + "node_modules/chownr": { + "version": "1.1.4", + "resolved": "https://registry.npmjs.org/chownr/-/chownr-1.1.4.tgz", + "integrity": "sha512-jJ0bqzaylmJtVnNgzTeSOs8DPavpbYgEr/b0YL8/2GO3xJEhInFmhKMUnEJQjZumK7KXGFhUy89PrsJWlakBVg==", + "license": "ISC" + }, + "node_modules/color": { + "version": "4.2.3", + "resolved": "https://registry.npmjs.org/color/-/color-4.2.3.tgz", + "integrity": "sha512-1rXeuUUiGGrykh+CeBdu5Ie7OJwinCgQY0bc7GCRxy5xVHy+moaqkpL/jqQq0MtQOeYcrqEz4abc5f0KtU7W4A==", + "license": "MIT", + "dependencies": { + "color-convert": "^2.0.1", + "color-string": "^1.9.0" + }, + "engines": { + "node": ">=12.5.0" + } + }, + "node_modules/color-convert": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/color-convert/-/color-convert-2.0.1.tgz", + "integrity": "sha512-RRECPsj7iu/xb5oKYcsFHSppFNnsj/52OVTRKb4zP5onXwVF3zVmmToNcOfGC+CRDpfK/U584fMg38ZHCaElKQ==", + "license": "MIT", + "dependencies": { + "color-name": "~1.1.4" + }, + "engines": { + "node": ">=7.0.0" + } + }, + "node_modules/color-name": { + "version": "1.1.4", + "resolved": "https://registry.npmjs.org/color-name/-/color-name-1.1.4.tgz", + "integrity": "sha512-dOy+3AuW3a2wNbZHIuMZpTcgjGuLU/uBL/ubcZF9OXbDo8ff4O8yVp5Bf0efS8uEoYo5q4Fx7dY9OgQGXgAsQA==", + "license": "MIT" + }, + "node_modules/color-string": { + "version": "1.9.1", + "resolved": "https://registry.npmjs.org/color-string/-/color-string-1.9.1.tgz", + "integrity": "sha512-shrVawQFojnZv6xM40anx4CkoDP+fZsw/ZerEMsW/pyzsRbElpsL/DBVW7q3ExxwusdNXI3lXpuhEZkzs8p5Eg==", + "license": "MIT", + "dependencies": { + "color-name": "^1.0.0", + "simple-swizzle": "^0.2.2" + } + }, + "node_modules/decompress-response": { + "version": "6.0.0", + "resolved": "https://registry.npmjs.org/decompress-response/-/decompress-response-6.0.0.tgz", + "integrity": "sha512-aW35yZM6Bb/4oJlZncMH2LCoZtJXTRxES17vE3hoRiowU2kWHaJKFkSBDnDR+cm9J+9QhXmREyIfv0pji9ejCQ==", + "license": "MIT", + "dependencies": { + "mimic-response": "^3.1.0" + }, + "engines": { + "node": ">=10" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/deep-extend": { + "version": "0.6.0", + "resolved": "https://registry.npmjs.org/deep-extend/-/deep-extend-0.6.0.tgz", + "integrity": "sha512-LOHxIOaPYdHlJRtCQfDIVZtfw/ufM8+rVj649RIHzcm/vGwQRXFt6OPqIFWsm2XEMrNIEtWR64sY1LEKD2vAOA==", + "license": "MIT", + "engines": { + "node": ">=4.0.0" + } + }, + "node_modules/detect-libc": { + "version": "2.0.4", + "resolved": "https://registry.npmjs.org/detect-libc/-/detect-libc-2.0.4.tgz", + "integrity": "sha512-3UDv+G9CsCKO1WKMGw9fwq/SWJYbI0c5Y7LU1AXYoDdbhE2AHQ6N6Nb34sG8Fj7T5APy8qXDCKuuIHd1BR0tVA==", + "license": "Apache-2.0", + "engines": { + "node": ">=8" + } + }, + "node_modules/end-of-stream": { + "version": "1.4.5", + "resolved": "https://registry.npmjs.org/end-of-stream/-/end-of-stream-1.4.5.tgz", + "integrity": "sha512-ooEGc6HP26xXq/N+GCGOT0JKCLDGrq2bQUZrQ7gyrJiZANJ/8YDTxTpQBXGMn+WbIQXNVpyWymm7KYVICQnyOg==", + "license": "MIT", + "dependencies": { + "once": "^1.4.0" + } + }, + "node_modules/expand-template": { + "version": "2.0.3", + "resolved": "https://registry.npmjs.org/expand-template/-/expand-template-2.0.3.tgz", + "integrity": "sha512-XYfuKMvj4O35f/pOXLObndIRvyQ+/+6AhODh+OKWj9S9498pHHn/IMszH+gt0fBCRWMNfk1ZSp5x3AifmnI2vg==", + "license": "(MIT OR WTFPL)", + "engines": { + "node": ">=6" + } + }, + "node_modules/fast-fifo": { + "version": "1.3.2", + "resolved": "https://registry.npmjs.org/fast-fifo/-/fast-fifo-1.3.2.tgz", + "integrity": "sha512-/d9sfos4yxzpwkDkuN7k2SqFKtYNmCTzgfEpz82x34IM9/zc8KGxQoXg1liNC/izpRM/MBdt44Nmx41ZWqk+FQ==", + "license": "MIT" + }, + "node_modules/flatbuffers": { + "version": "1.12.0", + "resolved": "https://registry.npmjs.org/flatbuffers/-/flatbuffers-1.12.0.tgz", + "integrity": "sha512-c7CZADjRcl6j0PlvFy0ZqXQ67qSEZfrVPynmnL+2zPc+NtMvrF8Y0QceMo7QqnSPc7+uWjUIAbvCQ5WIKlMVdQ==", + "license": "SEE LICENSE IN LICENSE.txt" + }, + "node_modules/fs-constants": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/fs-constants/-/fs-constants-1.0.0.tgz", + "integrity": "sha512-y6OAwoSIf7FyjMIv94u+b5rdheZEjzR63GTyZJm5qh4Bi+2YgwLCcI/fPFZkL5PSixOt6ZNKm+w+Hfp/Bciwow==", + "license": "MIT" + }, + "node_modules/github-from-package": { + "version": "0.0.0", + "resolved": "https://registry.npmjs.org/github-from-package/-/github-from-package-0.0.0.tgz", + "integrity": "sha512-SyHy3T1v2NUXn29OsWdxmK6RwHD+vkj3v8en8AOBZ1wBQ/hCAQ5bAQTD02kW4W9tUp/3Qh6J8r9EvntiyCmOOw==", + "license": "MIT" + }, + "node_modules/guid-typescript": { + "version": "1.0.9", + "resolved": "https://registry.npmjs.org/guid-typescript/-/guid-typescript-1.0.9.tgz", + "integrity": "sha512-Y8T4vYhEfwJOTbouREvG+3XDsjr8E3kIr7uf+JZ0BYloFsttiHU0WfvANVsR7TxNUJa/WpCnw/Ino/p+DeBhBQ==", + "license": "ISC" + }, + "node_modules/ieee754": { + "version": "1.2.1", + "resolved": "https://registry.npmjs.org/ieee754/-/ieee754-1.2.1.tgz", + "integrity": "sha512-dcyqhDvX1C46lXZcVqCpK+FtMRQVdIMN6/Df5js2zouUsqG7I6sFxitIC+7KYK29KdXOLHdu9zL4sFnoVQnqaA==", + "funding": [ + { + "type": "github", + "url": "https://github.com/sponsors/feross" + }, + { + "type": "patreon", + "url": "https://www.patreon.com/feross" + }, + { + "type": "consulting", + "url": "https://feross.org/support" + } + ], + "license": "BSD-3-Clause" + }, + "node_modules/inherits": { + "version": "2.0.4", + "resolved": "https://registry.npmjs.org/inherits/-/inherits-2.0.4.tgz", + "integrity": "sha512-k/vGaX4/Yla3WzyMCvTQOXYeIHvqOKtnqBduzTHpzpQZzAskKMhZ2K+EnBiSM9zGSoIFeMpXKxa4dYeZIQqewQ==", + "license": "ISC" + }, + "node_modules/ini": { + "version": "1.3.8", + "resolved": "https://registry.npmjs.org/ini/-/ini-1.3.8.tgz", + "integrity": "sha512-JV/yugV2uzW5iMRSiZAyDtQd+nxtUnjeLt0acNdw98kKLrvuRVyB80tsREOE7yvGVgalhZ6RNXCmEHkUKBKxew==", + "license": "ISC" + }, + "node_modules/is-arrayish": { + "version": "0.3.2", + "resolved": "https://registry.npmjs.org/is-arrayish/-/is-arrayish-0.3.2.tgz", + "integrity": "sha512-eVRqCvVlZbuw3GrM63ovNSNAeA1K16kaR/LRY/92w0zxQ5/1YzwblUX652i4Xs9RwAGjW9d9y6X88t8OaAJfWQ==", + "license": "MIT" + }, + "node_modules/long": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/long/-/long-4.0.0.tgz", + "integrity": "sha512-XsP+KhQif4bjX1kbuSiySJFNAehNxgLb6hPRGJ9QsUr8ajHkuXGdrHmFUTUUXhDwVX2R5bY4JNZEwbUiMhV+MA==", + "license": "Apache-2.0" + }, + "node_modules/mimic-response": { + "version": "3.1.0", + "resolved": "https://registry.npmjs.org/mimic-response/-/mimic-response-3.1.0.tgz", + "integrity": "sha512-z0yWI+4FDrrweS8Zmt4Ej5HdJmky15+L2e6Wgn3+iK5fWzb6T3fhNFq2+MeTRb064c6Wr4N/wv0DzQTjNzHNGQ==", + "license": "MIT", + "engines": { + "node": ">=10" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/minimist": { + "version": "1.2.8", + "resolved": "https://registry.npmjs.org/minimist/-/minimist-1.2.8.tgz", + "integrity": "sha512-2yyAR8qBkN3YuheJanUpWC5U3bb5osDywNB8RzDVlDwDHbocAJveqqj1u8+SVD7jkWT4yvsHCpWqqWqAxb0zCA==", + "license": "MIT", + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/mkdirp-classic": { + "version": "0.5.3", + "resolved": "https://registry.npmjs.org/mkdirp-classic/-/mkdirp-classic-0.5.3.tgz", + "integrity": "sha512-gKLcREMhtuZRwRAfqP3RFW+TK4JqApVBtOIftVgjuABpAtpxhPGaDcfvbhNvD0B8iD1oUr/txX35NjcaY6Ns/A==", + "license": "MIT" + }, + "node_modules/napi-build-utils": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/napi-build-utils/-/napi-build-utils-2.0.0.tgz", + "integrity": "sha512-GEbrYkbfF7MoNaoh2iGG84Mnf/WZfB0GdGEsM8wz7Expx/LlWf5U8t9nvJKXSp3qr5IsEbK04cBGhol/KwOsWA==", + "license": "MIT" + }, + "node_modules/node-abi": { + "version": "3.75.0", + "resolved": "https://registry.npmjs.org/node-abi/-/node-abi-3.75.0.tgz", + "integrity": "sha512-OhYaY5sDsIka7H7AtijtI9jwGYLyl29eQn/W623DiN/MIv5sUqc4g7BIDThX+gb7di9f6xK02nkp8sdfFWZLTg==", + "license": "MIT", + "dependencies": { + "semver": "^7.3.5" + }, + "engines": { + "node": ">=10" + } + }, + "node_modules/node-addon-api": { + "version": "6.1.0", + "resolved": "https://registry.npmjs.org/node-addon-api/-/node-addon-api-6.1.0.tgz", + "integrity": "sha512-+eawOlIgy680F0kBzPUNFhMZGtJ1YmqM6l4+Crf4IkImjYrO/mqPwRMh352g23uIaQKFItcQ64I7KMaJxHgAVA==", + "license": "MIT" + }, + "node_modules/once": { + "version": "1.4.0", + "resolved": "https://registry.npmjs.org/once/-/once-1.4.0.tgz", + "integrity": "sha512-lNaJgI+2Q5URQBkccEKHTQOPaXdUxnZZElQTZY0MFUAuaEqe1E+Nyvgdz/aIyNi6Z9MzO5dv1H8n58/GELp3+w==", + "license": "ISC", + "dependencies": { + "wrappy": "1" + } + }, + "node_modules/onnx-proto": { + "version": "4.0.4", + "resolved": "https://registry.npmjs.org/onnx-proto/-/onnx-proto-4.0.4.tgz", + "integrity": "sha512-aldMOB3HRoo6q/phyB6QRQxSt895HNNw82BNyZ2CMh4bjeKv7g/c+VpAFtJuEMVfYLMbRx61hbuqnKceLeDcDA==", + "license": "MIT", + "dependencies": { + "protobufjs": "^6.8.8" + } + }, + "node_modules/onnxruntime-common": { + "version": "1.14.0", + "resolved": "https://registry.npmjs.org/onnxruntime-common/-/onnxruntime-common-1.14.0.tgz", + "integrity": "sha512-3LJpegM2iMNRX2wUmtYfeX/ytfOzNwAWKSq1HbRrKc9+uqG/FsEA0bbKZl1btQeZaXhC26l44NWpNUeXPII7Ew==", + "license": "MIT" + }, + "node_modules/onnxruntime-node": { + "version": "1.14.0", + "resolved": "https://registry.npmjs.org/onnxruntime-node/-/onnxruntime-node-1.14.0.tgz", + "integrity": "sha512-5ba7TWomIV/9b6NH/1x/8QEeowsb+jBEvFzU6z0T4mNsFwdPqXeFUM7uxC6QeSRkEbWu3qEB0VMjrvzN/0S9+w==", + "license": "MIT", + "optional": true, + "os": [ + "win32", + "darwin", + "linux" + ], + "dependencies": { + "onnxruntime-common": "~1.14.0" + } + }, + "node_modules/onnxruntime-web": { + "version": "1.14.0", + "resolved": "https://registry.npmjs.org/onnxruntime-web/-/onnxruntime-web-1.14.0.tgz", + "integrity": "sha512-Kcqf43UMfW8mCydVGcX9OMXI2VN17c0p6XvR7IPSZzBf/6lteBzXHvcEVWDPmCKuGombl997HgLqj91F11DzXw==", + "license": "MIT", + "dependencies": { + "flatbuffers": "^1.12.0", + "guid-typescript": "^1.0.9", + "long": "^4.0.0", + "onnx-proto": "^4.0.4", + "onnxruntime-common": "~1.14.0", + "platform": "^1.3.6" + } + }, + "node_modules/platform": { + "version": "1.3.6", + "resolved": "https://registry.npmjs.org/platform/-/platform-1.3.6.tgz", + "integrity": "sha512-fnWVljUchTro6RiCFvCXBbNhJc2NijN7oIQxbwsyL0buWJPG85v81ehlHI9fXrJsMNgTofEoWIQeClKpgxFLrg==", + "license": "MIT" + }, + "node_modules/prebuild-install": { + "version": "7.1.3", + "resolved": "https://registry.npmjs.org/prebuild-install/-/prebuild-install-7.1.3.tgz", + "integrity": "sha512-8Mf2cbV7x1cXPUILADGI3wuhfqWvtiLA1iclTDbFRZkgRQS0NqsPZphna9V+HyTEadheuPmjaJMsbzKQFOzLug==", + "license": "MIT", + "dependencies": { + "detect-libc": "^2.0.0", + "expand-template": "^2.0.3", + "github-from-package": "0.0.0", + "minimist": "^1.2.3", + "mkdirp-classic": "^0.5.3", + "napi-build-utils": "^2.0.0", + "node-abi": "^3.3.0", + "pump": "^3.0.0", + "rc": "^1.2.7", + "simple-get": "^4.0.0", + "tar-fs": "^2.0.0", + "tunnel-agent": "^0.6.0" + }, + "bin": { + "prebuild-install": "bin.js" + }, + "engines": { + "node": ">=10" + } + }, + "node_modules/prebuild-install/node_modules/tar-fs": { + "version": "2.1.3", + "resolved": "https://registry.npmjs.org/tar-fs/-/tar-fs-2.1.3.tgz", + "integrity": "sha512-090nwYJDmlhwFwEW3QQl+vaNnxsO2yVsd45eTKRBzSzu+hlb1w2K9inVq5b0ngXuLVqQ4ApvsUHHnu/zQNkWAg==", + "license": "MIT", + "dependencies": { + "chownr": "^1.1.1", + "mkdirp-classic": "^0.5.2", + "pump": "^3.0.0", + "tar-stream": "^2.1.4" + } + }, + "node_modules/prebuild-install/node_modules/tar-stream": { + "version": "2.2.0", + "resolved": "https://registry.npmjs.org/tar-stream/-/tar-stream-2.2.0.tgz", + "integrity": "sha512-ujeqbceABgwMZxEJnk2HDY2DlnUZ+9oEcb1KzTVfYHio0UE6dG71n60d8D2I4qNvleWrrXpmjpt7vZeF1LnMZQ==", + "license": "MIT", + "dependencies": { + "bl": "^4.0.3", + "end-of-stream": "^1.4.1", + "fs-constants": "^1.0.0", + "inherits": "^2.0.3", + "readable-stream": "^3.1.1" + }, + "engines": { + "node": ">=6" + } + }, + "node_modules/protobufjs": { + "version": "6.11.4", + "resolved": "https://registry.npmjs.org/protobufjs/-/protobufjs-6.11.4.tgz", + "integrity": "sha512-5kQWPaJHi1WoCpjTGszzQ32PG2F4+wRY6BmAT4Vfw56Q2FZ4YZzK20xUYQH4YkfehY1e6QSICrJquM6xXZNcrw==", + "hasInstallScript": true, + "license": "BSD-3-Clause", + "dependencies": { + "@protobufjs/aspromise": "^1.1.2", + "@protobufjs/base64": "^1.1.2", + "@protobufjs/codegen": "^2.0.4", + "@protobufjs/eventemitter": "^1.1.0", + "@protobufjs/fetch": "^1.1.0", + "@protobufjs/float": "^1.0.2", + "@protobufjs/inquire": "^1.1.0", + "@protobufjs/path": "^1.1.2", + "@protobufjs/pool": "^1.1.0", + "@protobufjs/utf8": "^1.1.0", + "@types/long": "^4.0.1", + "@types/node": ">=13.7.0", + "long": "^4.0.0" + }, + "bin": { + "pbjs": "bin/pbjs", + "pbts": "bin/pbts" + } + }, + "node_modules/pump": { + "version": "3.0.3", + "resolved": "https://registry.npmjs.org/pump/-/pump-3.0.3.tgz", + "integrity": "sha512-todwxLMY7/heScKmntwQG8CXVkWUOdYxIvY2s0VWAAMh/nd8SoYiRaKjlr7+iCs984f2P8zvrfWcDDYVb73NfA==", + "license": "MIT", + "dependencies": { + "end-of-stream": "^1.1.0", + "once": "^1.3.1" + } + }, + "node_modules/rc": { + "version": "1.2.8", + "resolved": "https://registry.npmjs.org/rc/-/rc-1.2.8.tgz", + "integrity": "sha512-y3bGgqKj3QBdxLbLkomlohkvsA8gdAiUQlSBJnBhfn+BPxg4bc62d8TcBW15wavDfgexCgccckhcZvywyQYPOw==", + "license": "(BSD-2-Clause OR MIT OR Apache-2.0)", + "dependencies": { + "deep-extend": "^0.6.0", + "ini": "~1.3.0", + "minimist": "^1.2.0", + "strip-json-comments": "~2.0.1" + }, + "bin": { + "rc": "cli.js" + } + }, + "node_modules/readable-stream": { + "version": "3.6.2", + "resolved": "https://registry.npmjs.org/readable-stream/-/readable-stream-3.6.2.tgz", + "integrity": "sha512-9u/sniCrY3D5WdsERHzHE4G2YCXqoG5FTHUiCC4SIbr6XcLZBY05ya9EKjYek9O5xOAwjGq+1JdGBAS7Q9ScoA==", + "license": "MIT", + "dependencies": { + "inherits": "^2.0.3", + "string_decoder": "^1.1.1", + "util-deprecate": "^1.0.1" + }, + "engines": { + "node": ">= 6" + } + }, + "node_modules/redis": { + "resolved": "../../../../../packages/redis", + "link": true + }, + "node_modules/safe-buffer": { + "version": "5.2.1", + "resolved": "https://registry.npmjs.org/safe-buffer/-/safe-buffer-5.2.1.tgz", + "integrity": "sha512-rp3So07KcdmmKbGvgaNxQSJr7bGVSVk5S9Eq1F+ppbRo70+YeaDxkw5Dd8NPN+GD6bjnYm2VuPuCXmpuYvmCXQ==", + "funding": [ + { + "type": "github", + "url": "https://github.com/sponsors/feross" + }, + { + "type": "patreon", + "url": "https://www.patreon.com/feross" + }, + { + "type": "consulting", + "url": "https://feross.org/support" + } + ], + "license": "MIT" + }, + "node_modules/semver": { + "version": "7.7.2", + "resolved": "https://registry.npmjs.org/semver/-/semver-7.7.2.tgz", + "integrity": "sha512-RF0Fw+rO5AMf9MAyaRXI4AV0Ulj5lMHqVxxdSgiVbixSCXoEmmX/jk0CuJw4+3SqroYO9VoUh+HcuJivvtJemA==", + "license": "ISC", + "bin": { + "semver": "bin/semver.js" + }, + "engines": { + "node": ">=10" + } + }, + "node_modules/sharp": { + "version": "0.32.6", + "resolved": "https://registry.npmjs.org/sharp/-/sharp-0.32.6.tgz", + "integrity": "sha512-KyLTWwgcR9Oe4d9HwCwNM2l7+J0dUQwn/yf7S0EnTtb0eVS4RxO0eUSvxPtzT4F3SY+C4K6fqdv/DO27sJ/v/w==", + "hasInstallScript": true, + "license": "Apache-2.0", + "dependencies": { + "color": "^4.2.3", + "detect-libc": "^2.0.2", + "node-addon-api": "^6.1.0", + "prebuild-install": "^7.1.1", + "semver": "^7.5.4", + "simple-get": "^4.0.1", + "tar-fs": "^3.0.4", + "tunnel-agent": "^0.6.0" + }, + "engines": { + "node": ">=14.15.0" + }, + "funding": { + "url": "https://opencollective.com/libvips" + } + }, + "node_modules/simple-concat": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/simple-concat/-/simple-concat-1.0.1.tgz", + "integrity": "sha512-cSFtAPtRhljv69IK0hTVZQ+OfE9nePi/rtJmw5UjHeVyVroEqJXP1sFztKUy1qU+xvz3u/sfYJLa947b7nAN2Q==", + "funding": [ + { + "type": "github", + "url": "https://github.com/sponsors/feross" + }, + { + "type": "patreon", + "url": "https://www.patreon.com/feross" + }, + { + "type": "consulting", + "url": "https://feross.org/support" + } + ], + "license": "MIT" + }, + "node_modules/simple-get": { + "version": "4.0.1", + "resolved": "https://registry.npmjs.org/simple-get/-/simple-get-4.0.1.tgz", + "integrity": "sha512-brv7p5WgH0jmQJr1ZDDfKDOSeWWg+OVypG99A/5vYGPqJ6pxiaHLy8nxtFjBA7oMa01ebA9gfh1uMCFqOuXxvA==", + "funding": [ + { + "type": "github", + "url": "https://github.com/sponsors/feross" + }, + { + "type": "patreon", + "url": "https://www.patreon.com/feross" + }, + { + "type": "consulting", + "url": "https://feross.org/support" + } + ], + "license": "MIT", + "dependencies": { + "decompress-response": "^6.0.0", + "once": "^1.3.1", + "simple-concat": "^1.0.0" + } + }, + "node_modules/simple-swizzle": { + "version": "0.2.2", + "resolved": "https://registry.npmjs.org/simple-swizzle/-/simple-swizzle-0.2.2.tgz", + "integrity": "sha512-JA//kQgZtbuY83m+xT+tXJkmJncGMTFT+C+g2h2R9uxkYIrE2yy9sgmcLhCnw57/WSD+Eh3J97FPEDFnbXnDUg==", + "license": "MIT", + "dependencies": { + "is-arrayish": "^0.3.1" + } + }, + "node_modules/streamx": { + "version": "2.22.1", + "resolved": "https://registry.npmjs.org/streamx/-/streamx-2.22.1.tgz", + "integrity": "sha512-znKXEBxfatz2GBNK02kRnCXjV+AA4kjZIUxeWSr3UGirZMJfTE9uiwKHobnbgxWyL/JWro8tTq+vOqAK1/qbSA==", + "license": "MIT", + "dependencies": { + "fast-fifo": "^1.3.2", + "text-decoder": "^1.1.0" + }, + "optionalDependencies": { + "bare-events": "^2.2.0" + } + }, + "node_modules/string_decoder": { + "version": "1.3.0", + "resolved": "https://registry.npmjs.org/string_decoder/-/string_decoder-1.3.0.tgz", + "integrity": "sha512-hkRX8U1WjJFd8LsDJ2yQ/wWWxaopEsABU1XfkM8A+j0+85JAGppt16cr1Whg6KIbb4okU6Mql6BOj+uup/wKeA==", + "license": "MIT", + "dependencies": { + "safe-buffer": "~5.2.0" + } + }, + "node_modules/strip-json-comments": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/strip-json-comments/-/strip-json-comments-2.0.1.tgz", + "integrity": "sha512-4gB8na07fecVVkOI6Rs4e7T6NOTki5EmL7TUduTs6bu3EdnSycntVJ4re8kgZA+wx9IueI2Y11bfbgwtzuE0KQ==", + "license": "MIT", + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/tar-fs": { + "version": "3.1.0", + "resolved": "https://registry.npmjs.org/tar-fs/-/tar-fs-3.1.0.tgz", + "integrity": "sha512-5Mty5y/sOF1YWj1J6GiBodjlDc05CUR8PKXrsnFAiSG0xA+GHeWLovaZPYUDXkH/1iKRf2+M5+OrRgzC7O9b7w==", + "license": "MIT", + "dependencies": { + "pump": "^3.0.0", + "tar-stream": "^3.1.5" + }, + "optionalDependencies": { + "bare-fs": "^4.0.1", + "bare-path": "^3.0.0" + } + }, + "node_modules/tar-stream": { + "version": "3.1.7", + "resolved": "https://registry.npmjs.org/tar-stream/-/tar-stream-3.1.7.tgz", + "integrity": "sha512-qJj60CXt7IU1Ffyc3NJMjh6EkuCFej46zUqJ4J7pqYlThyd9bO0XBTmcOIhSzZJVWfsLks0+nle/j538YAW9RQ==", + "license": "MIT", + "dependencies": { + "b4a": "^1.6.4", + "fast-fifo": "^1.2.0", + "streamx": "^2.15.0" + } + }, + "node_modules/text-decoder": { + "version": "1.2.3", + "resolved": "https://registry.npmjs.org/text-decoder/-/text-decoder-1.2.3.tgz", + "integrity": "sha512-3/o9z3X0X0fTupwsYvR03pJ/DjWuqqrfwBgTQzdWDiQSm9KitAyz/9WqsT2JQW7KV2m+bC2ol/zqpW37NHxLaA==", + "license": "Apache-2.0", + "dependencies": { + "b4a": "^1.6.4" + } + }, + "node_modules/tunnel-agent": { + "version": "0.6.0", + "resolved": "https://registry.npmjs.org/tunnel-agent/-/tunnel-agent-0.6.0.tgz", + "integrity": "sha512-McnNiV1l8RYeY8tBgEpuodCC1mLUdbSN+CYBL7kJsJNInOP8UjDDEwdk6Mw60vdLLrr5NHKZhMAOSrR2NZuQ+w==", + "license": "Apache-2.0", + "dependencies": { + "safe-buffer": "^5.0.1" + }, + "engines": { + "node": "*" + } + }, + "node_modules/undici-types": { + "version": "7.8.0", + "resolved": "https://registry.npmjs.org/undici-types/-/undici-types-7.8.0.tgz", + "integrity": "sha512-9UJ2xGDvQ43tYyVMpuHlsgApydB8ZKfVYTsLDhXkFL/6gfkp+U8xTGdh8pMJv1SpZna0zxG1DwsKZsreLbXBxw==", + "license": "MIT" + }, + "node_modules/util-deprecate": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/util-deprecate/-/util-deprecate-1.0.2.tgz", + "integrity": "sha512-EPD5q1uXyFxJpCrLnCc1nHnq3gOa6DZBocAIiI2TaSCA7VCJ1UJDMagCzIkXNsUYfD1daK//LTEQ8xiIbrHtcw==", + "license": "MIT" + }, + "node_modules/wrappy": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/wrappy/-/wrappy-1.0.2.tgz", + "integrity": "sha512-l4Sp/DRseor9wL6EvV2+TuQn63dMkPjZ/sp9XkghTEbV9KlPS1xUsZ3u7/IQO4wxtcFB4bgpQPRcR3QCvezPcQ==", + "license": "ISC" + } + } +} diff --git a/doctests/package.json b/doctests/package.json new file mode 100644 index 00000000000..ee07eb44fb8 --- /dev/null +++ b/doctests/package.json @@ -0,0 +1,12 @@ +{ + "name": "node-redis-doctests", + "version": "1.0.0", + "description": "Code examples for redis.io", + "main": "index.js", + "private": true, + "type": "module", + "dependencies": { + "redis": "file:/packages/redis", + "@xenova/transformers": "^2.17.2" + } +} diff --git a/doctests/query-agg.js b/doctests/query-agg.js new file mode 100644 index 00000000000..82d378b86b2 --- /dev/null +++ b/doctests/query-agg.js @@ -0,0 +1,139 @@ +// EXAMPLE: query_agg +// HIDE_START +import assert from 'node:assert'; +import fs from 'node:fs'; +import { createClient } from 'redis'; +import { SCHEMA_FIELD_TYPE, FT_AGGREGATE_STEPS, FT_AGGREGATE_GROUP_BY_REDUCERS } from '@redis/search'; + +const client = createClient(); + +await client.connect().catch(console.error); + +// create index +await client.ft.create('idx:bicycle', { + '$.condition': { + type: SCHEMA_FIELD_TYPE.TAG, + AS: 'condition' + }, + '$.price': { + type: SCHEMA_FIELD_TYPE.NUMERIC, + AS: 'price' + } +}, { + ON: 'JSON', + PREFIX: 'bicycle:' +}) + +// load data +const bicycles = JSON.parse(fs.readFileSync('data/query_em.json', 'utf8')); + +await Promise.all( + bicycles.map((bicycle, bid) => { + return client.json.set(`bicycle:${bid}`, '$', bicycle); + }) +); +// HIDE_END + +// STEP_START agg1 +const res1 = await client.ft.aggregate('idx:bicycle', '@condition:{new}', { + LOAD: ['__key', 'price'], + APPLY: { + expression: '@price - (@price * 0.1)', + AS: 'discounted' + } +}); + +console.log(res1.results.length); // >>> 5 +console.log(res1.results); // >>> +//[ +// [Object: null prototype] { __key: 'bicycle:0', price: '270' }, +// [Object: null prototype] { __key: 'bicycle:5', price: '810' }, +// [Object: null prototype] { __key: 'bicycle:6', price: '2300' }, +// [Object: null prototype] { __key: 'bicycle:7', price: '430' }, +// [Object: null prototype] { __key: 'bicycle:8', price: '1200' } +//] +// REMOVE_START +assert.strictEqual(res1.results.length, 5); +// REMOVE_END +// STEP_END + +// STEP_START agg2 +const res2 = await client.ft.aggregate('idx:bicycle', '*', { + LOAD: ['@price'], + STEPS: [{ + type: FT_AGGREGATE_STEPS.APPLY, + expression: '@price<1000', + AS: 'price_category' + },{ + type: FT_AGGREGATE_STEPS.GROUPBY, + properties: '@condition', + REDUCE:[{ + type: FT_AGGREGATE_GROUP_BY_REDUCERS.SUM, + property: '@price_category', + AS: 'num_affordable' + }] + }] +}); +console.log(res2.results.length); // >>> 3 +console.log(res2.results); // >>> +//[[Object: null prototype] { condition: 'refurbished', num_affordable: '1' }, +// [Object: null prototype] { condition: 'used', num_affordable: '1' }, +// [Object: null prototype] { condition: 'new', num_affordable: '3' } +//] +// REMOVE_START +assert.strictEqual(res2.results.length, 3); +// REMOVE_END +// STEP_END + +// STEP_START agg3 +const res3 = await client.ft.aggregate('idx:bicycle', '*', { + STEPS: [{ + type: FT_AGGREGATE_STEPS.APPLY, + expression: "'bicycle'", + AS: 'type' + }, { + type: FT_AGGREGATE_STEPS.GROUPBY, + properties: '@type', + REDUCE: [{ + type: FT_AGGREGATE_GROUP_BY_REDUCERS.COUNT, + property: null, + AS: 'num_total' + }] + }] +}); +console.log(res3.results.length); // >>> 1 +console.log(res3.results); // >>> +//[ [Object: null prototype] { type: 'bicycle', num_total: '10' } ] +// REMOVE_START +assert.strictEqual(res3.results.length, 1); +// REMOVE_END +// STEP_END + +// STEP_START agg4 +const res4 = await client.ft.aggregate('idx:bicycle', '*', { + LOAD: ['__key'], + STEPS: [{ + type: FT_AGGREGATE_STEPS.GROUPBY, + properties: '@condition', + REDUCE: [{ + type: FT_AGGREGATE_GROUP_BY_REDUCERS.TOLIST, + property: '__key', + AS: 'bicycles' + }] + }] +}); +console.log(res4.results.length); // >>> 3 +console.log(res4.results); // >>> +//[[Object: null prototype] {condition: 'refurbished', bicycles: [ 'bicycle:9' ]}, +// [Object: null prototype] {condition: 'used', bicycles: [ 'bicycle:1', 'bicycle:2', 'bicycle:3', 'bicycle:4' ]}, +// [Object: null prototype] {condition: 'new', bicycles: [ 'bicycle:5', 'bicycle:6', 'bicycle:7', 'bicycle:0', 'bicycle:8' ]}] +// REMOVE_START +assert.strictEqual(res4.results.length, 3); +// REMOVE_END +// STEP_END + +// REMOVE_START +// destroy index and data +await client.ft.dropIndex('idx:bicycle', { DD: true }); +await client.close(); +// REMOVE_END \ No newline at end of file diff --git a/doctests/query-combined.js b/doctests/query-combined.js new file mode 100644 index 00000000000..a2ad8d43c93 --- /dev/null +++ b/doctests/query-combined.js @@ -0,0 +1,191 @@ +// EXAMPLE: query_combined +// HIDE_START +import assert from 'node:assert'; +import fs from 'node:fs'; +import { createClient } from 'redis'; +import { SCHEMA_FIELD_TYPE, SCHEMA_VECTOR_FIELD_ALGORITHM } from '@redis/search'; +import { pipeline } from '@xenova/transformers'; + +function float32Buffer(arr) { + const floatArray = new Float32Array(arr); + const float32Buffer = Buffer.from(floatArray.buffer); + return float32Buffer; +} + +async function embedText(sentence) { + let modelName = 'Xenova/all-MiniLM-L6-v2'; + let pipe = await pipeline('feature-extraction', modelName); + + let vectorOutput = await pipe(sentence, { + pooling: 'mean', + normalize: true, + }); + + if (vectorOutput == null) { + throw new Error('vectorOutput is undefined'); + } + + const embedding = Object.values(vectorOutput.data); + + return embedding; +} + +let vector_query = float32Buffer(await embedText('That is a very happy person')); + +const client = createClient(); +await client.connect().catch(console.error); + +// create index +await client.ft.create('idx:bicycle', { + '$.description': { + type: SCHEMA_FIELD_TYPE.TEXT, + AS: 'description' + }, + '$.condition': { + type: SCHEMA_FIELD_TYPE.TAG, + AS: 'condition' + }, + '$.price': { + type: SCHEMA_FIELD_TYPE.NUMERIC, + AS: 'price' + }, + '$.description_embeddings': { + type: SCHEMA_FIELD_TYPE.VECTOR, + TYPE: 'FLOAT32', + ALGORITHM: SCHEMA_VECTOR_FIELD_ALGORITHM.FLAT, + DIM: 384, + DISTANCE_METRIC: 'COSINE', + AS: 'vector', + } +}, { + ON: 'JSON', + PREFIX: 'bicycle:' +}); + +// load data +const bicycles = JSON.parse(fs.readFileSync('data/query_vector.json', 'utf8')); + +await Promise.all( + bicycles.map((bicycle, bid) => { + return client.json.set(`bicycle:${bid}`, '$', bicycle); + }) +); +// HIDE_END + +// STEP_START combined1 +const res1 = await client.ft.search('idx:bicycle', '@price:[500 1000] @condition:{new}'); +console.log(res1.total); // >>> 1 +console.log(res1); // >>> +//{ +// total: 1, +// documents: [ { id: 'bicycle:5', value: [Object: null prototype] } ] +//} +// REMOVE_START +assert.strictEqual(res1.total, 1); +// REMOVE_END +// STEP_END + +// STEP_START combined2 +const res2 = await client.ft.search('idx:bicycle', 'kids @price:[500 1000] @condition:{used}'); +console.log(res2.total); // >>> 1 +console.log(res2); // >>> +// { +// total: 1, +// documents: [ { id: 'bicycle:2', value: [Object: null prototype] } ] +// } +// REMOVE_START +assert.strictEqual(res2.total, 1); +// REMOVE_END +// STEP_END + +// STEP_START combined3 +const res3 = await client.ft.search('idx:bicycle', '(kids | small) @condition:{used}'); +console.log(res3.total); // >>> 2 +console.log(res3); // >>> +//{ +// total: 2, +// documents: [ +// { id: 'bicycle:2', value: [Object: null prototype] }, +// { id: 'bicycle:1', value: [Object: null prototype] } +// ] +//} +// REMOVE_START +assert.strictEqual(res3.total, 2); +// REMOVE_END +// STEP_END + +// STEP_START combined4 +const res4 = await client.ft.search('idx:bicycle', '@description:(kids | small) @condition:{used}'); +console.log(res4.total); // >>> 2 +console.log(res4); // >>> +//{ +// total: 2, +// documents: [ +// { id: 'bicycle:2', value: [Object: null prototype] }, +// { id: 'bicycle:1', value: [Object: null prototype] } +// ] +//} +// REMOVE_START +assert.strictEqual(res4.total, 2); +// REMOVE_END +// STEP_END + +// STEP_START combined5 +const res5 = await client.ft.search('idx:bicycle', '@description:(kids | small) @condition:{new | used}'); +console.log(res5.total); // >>> 3 +console.log(res5); // >>> +//{ +// total: 3, +// documents: [ +// { id: 'bicycle:1', value: [Object: null prototype] }, +// { id: 'bicycle:0', value: [Object: null prototype] }, +// { id: 'bicycle:2', value: [Object: null prototype] } +// ] +//} +// REMOVE_START +assert.strictEqual(res5.total, 3); +// REMOVE_END +// STEP_END + +// STEP_START combined6 +const res6 = await client.ft.search('idx:bicycle', '@price:[500 1000] -@condition:{new}'); +console.log(res6.total); // >>> 2 +console.log(res6); // >>> +//{ +// total: 2, +// documents: [ +// { id: 'bicycle:2', value: [Object: null prototype] }, +// { id: 'bicycle:9', value: [Object: null prototype] } +// ] +//} +// REMOVE_START +assert.strictEqual(res6.total, 2); +// REMOVE_END +// STEP_END + +// STEP_START combined7 +const res7 = await client.ft.search('idx:bicycle', + '(@price:[500 1000] -@condition:{new})=>[KNN 3 @vector $query_vector]', { + PARAMS: { query_vector: vector_query }, + DIALECT: 2 + } +); +console.log(res7.total); // >>> 2 +console.log(res7); // >>> +//{ +// total: 2, +// documents: [ +// { id: 'bicycle:2', value: [Object: null prototype] }, +// { id: 'bicycle:9', value: [Object: null prototype] } +// ] +//} +// REMOVE_START +assert.strictEqual(res7.total, 2); +// REMOVE_END +// STEP_END + +// REMOVE_START +// destroy index and data +await client.ft.dropIndex('idx:bicycle', { DD: true }); +await client.close(); +// REMOVE_END \ No newline at end of file diff --git a/doctests/query-em.js b/doctests/query-em.js new file mode 100644 index 00000000000..9b5782e09ce --- /dev/null +++ b/doctests/query-em.js @@ -0,0 +1,121 @@ +// EXAMPLE: query_em +// HIDE_START +import assert from 'node:assert'; +import fs from 'node:fs'; +import { createClient, SCHEMA_FIELD_TYPE } from 'redis'; + +const client = createClient(); + +await client.connect().catch(console.error); + +// create index +await client.ft.create('idx:bicycle', { + '$.description': { + type: SCHEMA_FIELD_TYPE.TEXT, + AS: 'description' + }, + '$.price': { + type: SCHEMA_FIELD_TYPE.NUMERIC, + AS: 'price' + }, + '$.condition': { + type: SCHEMA_FIELD_TYPE.TAG, + AS: 'condition' + } +}, { + ON: 'JSON', + PREFIX: 'bicycle:' +}) + +// load data +const bicycles = JSON.parse(fs.readFileSync('data/query_em.json', 'utf8')); + +await Promise.all( + bicycles.map((bicycle, bid) => { + return client.json.set(`bicycle:${bid}`, '$', bicycle); + }) +); +// HIDE_END + +// STEP_START em1 +const res1 = await client.ft.search('idx:bicycle', '@price:[270 270]'); +console.log(res1.total); // >>> 1 +// REMOVE_START +assert.strictEqual(res1.total, 1); +// REMOVE_END + +try { + const res2 = await client.ft.search('idx:bicycle', '@price:[270]'); + console.log(res2.total); // >>> 1 + assert.strictEqual(res2.total, 1); +} catch (err) { + console.log("'@price:[270]' syntax not yet supported."); +} + +try { + const res3 = await client.ft.search('idx:bicycle', '@price==270'); + console.log(res3.total); // >>> 1 + assert.strictEqual(res3.total, 1); +} catch (err) { + console.log("'@price==270' syntax not yet supported."); +} + +// FILTER is not supported +// const res4 = await client.ft.search('idx:bicycle', '*', { +// FILTER: { +// field: 'price', +// min: 270, +// max: 270, +// } +// }); +// console.log(res4.total); // >>> 1 +// REMOVE_START +// assert.strictEqual(res4.total, 10); +// REMOVE_END +// STEP_END + +// STEP_START em2 +const res5 = await client.ft.search('idx:bicycle', '@condition:{new}'); +console.log(res5.total); // >>> 5 +// REMOVE_START +assert.strictEqual(res5.total, 5); +// REMOVE_END +// STEP_END + +// STEP_START em3 +await client.ft.create('idx:email', { + '$.email': { + type: SCHEMA_FIELD_TYPE.TAG, + AS: 'email' + } +}, { + ON: 'JSON', + PREFIX: 'key:' +}) + +await client.json.set('key:1', '$', { email: 'test@redis.com' }); + +try { + const res6 = await client.ft.search('idx:email', 'test@redis.com', { DIALECT: 2 }); + console.log(res6); +} catch (err) { + console.log("'test@redis.com' syntax not yet supported."); +} +// REMOVE_START +await client.ft.dropIndex('idx:email', { DD: true }); +// REMOVE_END +// STEP_END + +// STEP_START em4 +const res7 = await client.ft.search('idx:bicycle', '@description:"rough terrain"'); +console.log(res7.total); // >>> 1 (Result{1 total, docs: [Document {'id': 'bicycle:8'...) +// REMOVE_START +assert.strictEqual(res7.total, 1); +// REMOVE_END +// STEP_END + +// REMOVE_START +// destroy index and data +await client.ft.dropIndex('idx:bicycle', { DD: true }); +await client.close(); +// REMOVE_END diff --git a/doctests/query-ft.js b/doctests/query-ft.js new file mode 100644 index 00000000000..a70b2ea3f5d --- /dev/null +++ b/doctests/query-ft.js @@ -0,0 +1,84 @@ +// EXAMPLE: query_ft +// HIDE_START +import assert from 'node:assert'; +import fs from 'node:fs'; +import { createClient, SCHEMA_FIELD_TYPE } from 'redis'; + +const client = createClient(); + +await client.connect().catch(console.error); + +// create index +await client.ft.create('idx:bicycle', { + '$.model': { + type: SCHEMA_FIELD_TYPE.TEXT, + AS: 'model' + }, + '$.brand': { + type: SCHEMA_FIELD_TYPE.TEXT, + AS: 'brand' + }, + '$.description': { + type: SCHEMA_FIELD_TYPE.TEXT, + AS: 'description' + } +}, { + ON: 'JSON', + PREFIX: 'bicycle:' +}) + +// load data +const bicycles = JSON.parse(fs.readFileSync('data/query_em.json', 'utf8')); + +await Promise.all( + bicycles.map((bicycle, bid) => { + return client.json.set(`bicycle:${bid}`, '$', bicycle); + }) +); +// HIDE_END + +// STEP_START ft1 +const res1 = await client.ft.search('idx:bicycle', '@description: kids'); +console.log(res1.total); // >>> 2 +// REMOVE_START +assert.strictEqual(res1.total, 2); +// REMOVE_END +// STEP_END + +// STEP_START ft2 +const res2 = await client.ft.search('idx:bicycle', '@model: ka*'); +console.log(res2.total); // >>> 1 +// REMOVE_START +assert.strictEqual(res2.total, 1); +// REMOVE_END +// STEP_END + +// STEP_START ft3 +const res3 = await client.ft.search('idx:bicycle', '@brand: *bikes'); +console.log(res3.total); // >>> 2 +// REMOVE_START +assert.strictEqual(res3.total, 2); +// REMOVE_END +// STEP_END + +// STEP_START ft4 +const res4 = await client.ft.search('idx:bicycle', '%optamized%'); +console.log(res4); // >>> { total: 1, documents: [ { id: 'bicycle:3', value: [Object: null prototype] } ]} +// REMOVE_START +assert.strictEqual(res4.total, 1); +// REMOVE_END +// STEP_END + +// STEP_START ft5 +const res5 = await client.ft.search('idx:bicycle', '%%optamised%%'); +console.log(res5); // >>> { total: 1, documents: [ { id: 'bicycle:3', value: [Object: null prototype] } ]} +// REMOVE_START +assert.strictEqual(res5.total, 1); +// REMOVE_END +// STEP_END + +// REMOVE_START +// destroy index and data +await client.ft.dropIndex('idx:bicycle', { DD: true }); +await client.close(); +// REMOVE_END \ No newline at end of file diff --git a/doctests/query-geo.js b/doctests/query-geo.js new file mode 100644 index 00000000000..41ef2141eb8 --- /dev/null +++ b/doctests/query-geo.js @@ -0,0 +1,82 @@ +// EXAMPLE: query_geo +// HIDE_START +import assert from 'node:assert'; +import fs from 'node:fs'; +import { createClient } from 'redis'; +import { SCHEMA_FIELD_TYPE } from '@redis/search'; + +const client = createClient(); + +await client.connect().catch(console.error); + +// create index +await client.ft.create('idx:bicycle', { + '$.store_location': { + type: SCHEMA_FIELD_TYPE.GEO, + AS: 'store_location' + }, + '$.pickup_zone': { + type: SCHEMA_FIELD_TYPE.GEOSHAPE, + AS: 'pickup_zone' + } +}, { + ON: 'JSON', + PREFIX: 'bicycle:' +}) + +// load data +const bicycles = JSON.parse(fs.readFileSync('data/query_em.json', 'utf8')); + +await Promise.all( + bicycles.map((bicycle, bid) => { + return client.json.set(`bicycle:${bid}`, '$', bicycle); + }) +); +// HIDE_END + +// STEP_START geo1 +const res1= await client.ft.search('idx:bicycle', '@store_location:[-0.1778 51.5524 20 mi]'); +console.log(res1.total); // >>> 1 +console.log(res1); // >>> {total: 1, documents: [ { id: 'bicycle:5', value: [Object: null prototype] } ]} +// REMOVE_START +assert.strictEqual(res1.total, 1); +// REMOVE_END +// STEP_END + +// STEP_START geo2 +const params_dict_geo2 = { bike: 'POINT(-0.1278 51.5074)' }; +const q_geo2 = '@pickup_zone:[CONTAINS $bike]'; +const res2 = await client.ft.search('idx:bicycle', q_geo2, { PARAMS: params_dict_geo2, DIALECT: 3 }); +console.log(res2.total); // >>> 1 +console.log(res2); // >>> {total: 1, documents: [ { id: 'bicycle:5', value: [Object: null prototype] } ]} +// REMOVE_START +assert.strictEqual(res2.total, 1); +// REMOVE_END +// STEP_END + +// STEP_START geo3 +const params_dict_geo3 = { europe: 'POLYGON((-25 35, 40 35, 40 70, -25 70, -25 35))' }; +const q_geo3 = '@pickup_zone:[WITHIN $europe]'; +const res3 = await client.ft.search('idx:bicycle', q_geo3, { PARAMS: params_dict_geo3, DIALECT: 3 }); +console.log(res3.total); // >>> 5 +console.log(res3); // >>> +// { +// total: 5, +// documents: [ +// { id: 'bicycle:5', value: [Object: null prototype] }, +// { id: 'bicycle:6', value: [Object: null prototype] }, +// { id: 'bicycle:7', value: [Object: null prototype] }, +// { id: 'bicycle:8', value: [Object: null prototype] }, +// { id: 'bicycle:9', value: [Object: null prototype] } +// ] +// } +// REMOVE_START +assert.strictEqual(res3.total, 5); +// REMOVE_END +// STEP_END + +// REMOVE_START +// destroy index and data +await client.ft.dropIndex('idx:bicycle', { DD: true }); +await client.close(); +// REMOVE_END \ No newline at end of file diff --git a/doctests/query-range.js b/doctests/query-range.js new file mode 100644 index 00000000000..29b269e37b5 --- /dev/null +++ b/doctests/query-range.js @@ -0,0 +1,98 @@ +// EXAMPLE: query_range +// HIDE_START +import assert from 'node:assert'; +import fs from 'node:fs'; +import { createClient, SCHEMA_FIELD_TYPE,} from 'redis'; + +const client = createClient(); + +await client.connect().catch(console.error); + +// create index +await client.ft.create('idx:bicycle', { + '$.description': { + type: SCHEMA_FIELD_TYPE.TEXT, + AS: 'description' + }, + '$.price': { + type: SCHEMA_FIELD_TYPE.NUMERIC, + AS: 'price' + }, + '$.condition': { + type: SCHEMA_FIELD_TYPE.TAG, + AS: 'condition' + } +}, { + ON: 'JSON', + PREFIX: 'bicycle:' +}) + +// load data +const bicycles = JSON.parse(fs.readFileSync('data/query_em.json', 'utf8')); + +await Promise.all( + bicycles.map((bicycle, bid) => { + return client.json.set(`bicycle:${bid}`, '$', bicycle); + }) +); +// HIDE_END + +// STEP_START range1 +const res1 = await client.ft.search('idx:bicycle', '@price:[500 1000]'); +console.log(res1.total); // >>> 3 +// REMOVE_START +assert.strictEqual(res1.total, 3); +// REMOVE_END +// STEP_END + +// STEP_START range2 +// FILTER is not supported +// const res2 = await client.ft.search('idx:bicycle', '*', { +// FILTER: { +// field: 'price', +// min: 500, +// max: 1000, +// } +// }); +// console.log(res2.total); // >>> 3 +// REMOVE_START +// assert.strictEqual(res2.total, 3); +// REMOVE_END +// STEP_END + +// STEP_START range3 +// FILTER is not supported +// const res3 = await client.ft.search('idx:bicycle', '*', { +// FILTER: { +// field: 'price', +// min: '(1000', +// max: '+inf, +// } +// }); +// console.log(res3.total); // >>> 5 +// REMOVE_START +// assert.strictEqual(res3.total, 5); +// REMOVE_END +// STEP_END + +// STEP_START range4 +const res4 = await client.ft.search( + 'idx:bicycle', + '@price:[-inf 2000]', + { + SORTBY: 'price', + LIMIT: { from: 0, size: 5 } + } +); +console.log(res4.total); // >>> 7 +console.log(res4); // >>> { total: 7, documents: [ { id: 'bicycle:0', value: [Object: null prototype] }, { id: 'bicycle:7', value: [Object: null prototype] }, { id: 'bicycle:5', value: [Object: null prototype] }, { id: 'bicycle:2', value: [Object: null prototype] }, { id: 'bicycle:9', value: [Object: null prototype] } ] } +// REMOVE_START +assert.strictEqual(res4.total, 7); +// REMOVE_END +// STEP_END + +// REMOVE_START +// destroy index and data +await client.ft.dropIndex('idx:bicycle', { DD: true }); +await client.close(); +// REMOVE_END \ No newline at end of file diff --git a/doctests/query-vector.js b/doctests/query-vector.js new file mode 100644 index 00000000000..91ee63120d3 --- /dev/null +++ b/doctests/query-vector.js @@ -0,0 +1,110 @@ +// EXAMPLE: query_vector +// HIDE_START +import assert from 'node:assert'; +import fs from 'node:fs'; +import { createClient } from 'redis'; +import { SCHEMA_FIELD_TYPE, SCHEMA_VECTOR_FIELD_ALGORITHM } from '@redis/search'; +import { pipeline } from '@xenova/transformers'; + +function float32Buffer(arr) { + const floatArray = new Float32Array(arr); + const float32Buffer = Buffer.from(floatArray.buffer); + return float32Buffer; +} + +async function embedText(sentence) { + let modelName = 'Xenova/all-MiniLM-L6-v2'; + let pipe = await pipeline('feature-extraction', modelName); + + let vectorOutput = await pipe(sentence, { + pooling: 'mean', + normalize: true, + }); + + const embedding = Object.values(vectorOutput?.data); + + return embedding; +} + +const vector_query = float32Buffer(await embedText('That is a very happy person')); + +const client = createClient(); +await client.connect().catch(console.error); + +// create index +await client.ft.create('idx:bicycle', { + '$.description': { + type: SCHEMA_FIELD_TYPE.TEXT, + AS: 'description' + }, + '$.description_embeddings': { + type: SCHEMA_FIELD_TYPE.VECTOR, + TYPE: 'FLOAT32', + ALGORITHM: SCHEMA_VECTOR_FIELD_ALGORITHM.FLAT, + DIM: 384, + DISTANCE_METRIC: 'COSINE', + AS: 'vector' + } +}, { + ON: 'JSON', + PREFIX: 'bicycle:' +}); + +// load data +const bicycles = JSON.parse(fs.readFileSync('data/query_vector.json', 'utf8')); + +await Promise.all( + bicycles.map((bicycle, bid) => { + return client.json.set(`bicycle:${bid}`, '$', bicycle); + }) +); +// HIDE_END + +// STEP_START vector1 +const res1 = await client.ft.search('idx:bicycle', + '*=>[KNN 3 @vector $query_vector AS score]', { + PARAMS: { query_vector: vector_query }, + RETURN: ['description'], + DIALECT: 2 + } +); +console.log(res1.total); // >>> 3 +console.log(res1); // >>> +//{ +// total: 3, +// documents: [ +// { id: 'bicycle:0', value: [Object: null prototype] {} }, +// { id: 'bicycle:2', value: [Object: null prototype] {} }, +// { id: 'bicycle:9', value: [Object: null prototype] {} } +// ] +//} +// REMOVE_START +assert.strictEqual(res1.total, 3); +// REMOVE_END +// STEP_END + +// STEP_START vector2 +const res2 = await client.ft.search('idx:bicycle', + '@vector:[VECTOR_RANGE 0.9 $query_vector]=>{$YIELD_DISTANCE_AS: vector_dist}', { + PARAMS: { query_vector: vector_query }, + SORTBY: 'vector_dist', + RETURN: ['vector_dist', 'description'], + DIALECT: 2 + } +); +console.log(res2.total); // >>> 1 +console.log(res2); // >>> +//{ +// total: 1, +// documents: [ { id: 'bicycle:0', value: [Object: null prototype] } ] +//} +// REMOVE_START +assert.strictEqual(res2.total, 1); +// REMOVE_END +// STEP_END + +// REMOVE_START +// destroy index and data +await client.ft.dropIndex('idx:bicycle', { DD: true }); +await client.close(); +// REMOVE_END \ No newline at end of file diff --git a/doctests/run_examples.sh b/doctests/run_examples.sh new file mode 100755 index 00000000000..ee7b50dc69a --- /dev/null +++ b/doctests/run_examples.sh @@ -0,0 +1,15 @@ +#!/bin/sh + + +basepath=`readlink -f $1` +if [ $? -ne 0 ]; then +basepath=`readlink -f $(dirname $0)` +fi +echo "No path specified, using ${basepath}" + +set -e +cd ${basepath} +for i in `ls ${basepath}/*.js`; do + redis-cli flushdb + node $i +done \ No newline at end of file diff --git a/doctests/search-quickstart.js b/doctests/search-quickstart.js new file mode 100644 index 00000000000..ec3f65a5e3a --- /dev/null +++ b/doctests/search-quickstart.js @@ -0,0 +1,231 @@ +// EXAMPLE: search_quickstart +// REMOVE_START +import assert from 'assert'; +// REMOVE_END +// HIDE_START +import { createClient, SCHEMA_FIELD_TYPE } from 'redis'; +// HIDE_END +// STEP_START connect +const client = createClient(); +client.on('error', err => console.log('Redis Client Error', err)); + +await client.connect(); +// STEP_END + +// STEP_START data_sample +const bicycle1 = { + brand: 'Velorim', + model: 'Jigger', + price: 270, + description: + 'Small and powerful, the Jigger is the best ' + + 'ride for the smallest of tikes! This is the tiniest kids\u2019 ' + + 'pedal bike on the market available without a coaster brake, the ' + + 'Jigger is the vehicle of choice for the rare tenacious little' + + 'rider raring to go.', + condition: 'new' +}; +// STEP_END +const bicycles = [ + bicycle1, + { + brand: 'Bicyk', + model: 'Hillcraft', + price: 1200, + description: 'Kids want to ride with as little weight as possible. Especially on an incline! They may be at the age when a 27.5\" wheel bike is just too clumsy coming off a 24\" bike. The Hillcraft 26 is just the solution they need!', + condition: 'used' + }, + { + brand: 'Nord', + model: 'Chook air 5', + price: 815, + description: 'The Chook Air 5 gives kids aged six years and older a durable and uberlight mountain bike for their first experience on tracks and easy cruising through forests and fields. The lower top tube makes it easy to mount and dismount in any situation, giving your kids greater safety on the trails.', + condition: 'used' + }, + { + brand: 'Eva', + model: 'Eva 291', + price: 3400, + description: 'The sister company to Nord, Eva launched in 2005 as the first and only women-dedicated bicycle brand. Designed by women for women, allEva bikes are optimized for the feminine physique using analytics from a body metrics database. If you like 29ers, try the Eva 291. It\u2019s a brand new bike for 2022.. This full-suspension, cross-country ride has been designed for velocity. The 291 has 100mm of front and rear travel, a superlight aluminum frame and fast-rolling 29-inch wheels. Yippee!', + condition: 'used' + }, + { + brand: 'Noka Bikes', + model: 'Kahuna', + price: 3200, + description: 'Whether you want to try your hand at XC racing or are looking for a lively trail bike that\'s just as inspiring on the climbs as it is over rougher ground, the Wilder is one heck of a bike built specifically for short women. Both the frames and components have been tweaked to include a women\u2019s saddle, different bars and unique colourway.', + condition: 'used' + }, + { + brand: 'Breakout', + model: 'XBN 2.1 Alloy', + price: 810, + description: 'The XBN 2.1 Alloy is our entry-level road bike \u2013 but that\u2019s not to say that it\u2019s a basic machine. With an internal weld aluminium frame, a full carbon fork, and the slick-shifting Claris gears from Shimano\u2019s, this is a bike which doesn\u2019t break the bank and delivers craved performance.', + condition: 'new' + }, + { + brand: 'ScramBikes', + model: 'WattBike', + price: 2300, + description: 'The WattBike is the best e-bike for people who still feel young at heart. It has a Bafang 1000W mid-drive system and a 48V 17.5AH Samsung Lithium-Ion battery, allowing you to ride for more than 60 miles on one charge. It\u2019s great for tackling hilly terrain or if you just fancy a more leisurely ride. With three working modes, you can choose between E-bike, assisted bicycle, and normal bike modes.', + condition: 'new' + }, + { + brand: 'Peaknetic', + model: 'Secto', + price: 430, + description: 'If you struggle with stiff fingers or a kinked neck or back after a few minutes on the road, this lightweight, aluminum bike alleviates those issues and allows you to enjoy the ride. From the ergonomic grips to the lumbar-supporting seat position, the Roll Low-Entry offers incredible comfort. The rear-inclined seat tube facilitates stability by allowing you to put a foot on the ground to balance at a stop, and the low step-over frame makes it accessible for all ability and mobility levels. The saddle is very soft, with a wide back to support your hip joints and a cutout in the center to redistribute that pressure. Rim brakes deliver satisfactory braking control, and the wide tires provide a smooth, stable ride on paved roads and gravel. Rack and fender mounts facilitate setting up the Roll Low-Entry as your preferred commuter, and the BMX-like handlebar offers space for mounting a flashlight, bell, or phone holder.', + condition: 'new' + }, + { + brand: 'nHill', + model: 'Summit', + price: 1200, + description: 'This budget mountain bike from nHill performs well both on bike paths and on the trail. The fork with 100mm of travel absorbs rough terrain. Fat Kenda Booster tires give you grip in corners and on wet trails. The Shimano Tourney drivetrain offered enough gears for finding a comfortable pace to ride uphill, and the Tektro hydraulic disc brakes break smoothly. Whether you want an affordable bike that you can take to work, but also take trail in mountains on the weekends or you\u2019re just after a stable, comfortable ride for the bike path, the Summit gives a good value for money.', + condition: 'new' + }, + { + model: 'ThrillCycle', + brand: 'BikeShind', + price: 815, + description: 'An artsy, retro-inspired bicycle that\u2019s as functional as it is pretty: The ThrillCycle steel frame offers a smooth ride. A 9-speed drivetrain has enough gears for coasting in the city, but we wouldn\u2019t suggest taking it to the mountains. Fenders protect you from mud, and a rear basket lets you transport groceries, flowers and books. The ThrillCycle comes with a limited lifetime warranty, so this little guy will last you long past graduation.', + condition: 'refurbished' + } +]; +// STEP_START create_index +const schema = { + '$.brand': { + type: SCHEMA_FIELD_TYPE.TEXT, + SORTABLE: true, + AS: 'brand' + }, + '$.model': { + type: SCHEMA_FIELD_TYPE.TEXT, + AS: 'model' + }, + '$.description': { + type: SCHEMA_FIELD_TYPE.TEXT, + AS: 'description' + }, + '$.price': { + type: SCHEMA_FIELD_TYPE.NUMERIC, + AS: 'price' + }, + '$.condition': { + type: SCHEMA_FIELD_TYPE.TAG, + AS: 'condition' + } +}; + +try { + await client.ft.create('idx:bicycle', schema, { + ON: 'JSON', + PREFIX: 'bicycle:' + }); +} 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); + } +} +// STEP_END + +// STEP_START add_documents +await Promise.all( + bicycles.map((bicycle, i) => client.json.set(`bicycle:${i}`, '$', bicycle)) +); +// STEP_END + +// STEP_START wildcard_query +let result = await client.ft.search('idx:bicycle', '*', { + LIMIT: { + from: 0, + size: 10 + } +}); + +console.log(JSON.stringify(result, null, 2)); + +/* +{ + "total": 10, + "documents": ... +} +*/ +// STEP_END + +// REMOVE_START +assert.equal(result.documents[0].id, 'bicycle:0'); +// REMOVE_END + +// STEP_START query_single_term +result = await client.ft.search( + 'idx:bicycle', + '@model:Jigger', + { + LIMIT: { + from: 0, + size: 10 + } +}); + +console.log(JSON.stringify(result, null, 2)); +/* +{ + "total": 1, + "documents": [{ + "id": "bicycle:0", + "value": { + "brand": "Velorim", + "model": "Jigger", + "price": 270, + "description": "Small and powerful, the Jigger is the best ride for the smallest of tikes! This is the tiniest kids’ pedal bike on the market available without a coaster brake, the Jigger is the vehicle of choice for the rare tenacious little rider raring to go.", + "condition": "new" + } + }] +} + */ +// STEP_END +// REMOVE_START +assert.equal(result.documents[0].id, 'bicycle:0'); +// REMOVE_END + +// STEP_START query_exact_matching +result = await client.ft.search( + 'idx:bicycle', + '@brand:"Noka Bikes"', + { + LIMIT: { + from: 0, + size: 10 + } + } +); + +console.log(JSON.stringify(result, null, 2)); + +/* +{ + "total": 1, + "documents": [{ + "id": "bicycle:4", + "value": { + "brand": "Noka Bikes", + "model": "Kahuna", + "price": 3200, + "description": "Whether you want to try your hand at XC racing or are looking for a lively trail bike that's just as inspiring on the climbs as it is over rougher ground, the Wilder is one heck of a bike built specifically for short women. Both the frames and components have been tweaked to include a women’s saddle, different bars and unique colourway.", + "condition": "used" + } + }] +} +*/ +// STEP_END + +// REMOVE_START +assert.equal(result.documents[0].id, 'bicycle:4'); +// REMOVE END + +await client.close(); diff --git a/doctests/string-set-get-example.js b/doctests/string-set-get-example.js new file mode 100644 index 00000000000..b9be382285e --- /dev/null +++ b/doctests/string-set-get-example.js @@ -0,0 +1,27 @@ +// EXAMPLE: set_and_get +// REMOVE_START +import assert from "node:assert"; +// REMOVE_END + +// HIDE_START +import { createClient } from 'redis'; + +const client = createClient(); + +client.on('error', err => console.log('Redis Client Error', err)); + +await client.connect().catch(console.error); + +// HIDE_END +await client.set('bike:1', 'Process 134'); +const value = await client.get('bike:1'); +console.log(value); +// returns 'Process 134' +//REMOVE_START +assert.equal(value, 'Process 134'); +await client.del('bike:1'); +//REMOVE_END + +// HIDE_START +await client.close(); +// HIDE_END diff --git a/packages/client/lib/commands/SCAN.ts b/packages/client/lib/commands/SCAN.ts index 41991a24172..173e8959afa 100644 --- a/packages/client/lib/commands/SCAN.ts +++ b/packages/client/lib/commands/SCAN.ts @@ -3,7 +3,7 @@ import { RedisArgument, CommandArguments, BlobStringReply, ArrayReply, Command } /** * Common options for SCAN-type commands - * + * * @property MATCH - Pattern to filter returned keys * @property COUNT - Hint for how many elements to return per iteration */ @@ -14,7 +14,7 @@ export interface ScanCommonOptions { /** * Parses scan arguments for SCAN-type commands - * + * * @param parser - The command parser * @param cursor - The cursor position for iteration * @param options - Scan options @@ -36,7 +36,7 @@ export function parseScanArguments( /** * Pushes scan arguments to the command arguments array - * + * * @param args - The command arguments array * @param cursor - The cursor position for iteration * @param options - Scan options @@ -62,7 +62,7 @@ export function pushScanArguments( /** * Options for the SCAN command - * + * * @property TYPE - Filter by value type */ export interface ScanOptions extends ScanCommonOptions { @@ -74,7 +74,7 @@ export default { IS_READ_ONLY: true, /** * Constructs the SCAN command - * + * * @param parser - The command parser * @param cursor - The cursor position to start scanning from * @param options - Scan options @@ -87,14 +87,16 @@ export default { if (options?.TYPE) { parser.push('TYPE', options.TYPE); } + console.log('eeeeeeeeee', parser.redisArgs) }, /** * Transforms the SCAN reply into a structured object - * + * * @param reply - The raw reply containing cursor and keys * @returns Object with cursor and keys properties */ transformReply([cursor, keys]: [BlobStringReply, ArrayReply]) { + console.log(cursor, keys) return { cursor, keys From 4ae14bb558946258b075baf31e91b8d28329acd2 Mon Sep 17 00:00:00 2001 From: "Bobby I." Date: Wed, 23 Jul 2025 18:21:13 +0300 Subject: [PATCH 1654/1748] Add Redis transparent proxy test utilities (#3019) --- packages/client/lib/test-utils.ts | 6 + packages/test-utils/lib/index.ts | 38 +++ packages/test-utils/lib/redis-proxy-spec.ts | 111 +++++++ packages/test-utils/lib/redis-proxy.ts | 329 ++++++++++++++++++++ packages/test-utils/lib/test-utils.ts | 25 ++ 5 files changed, 509 insertions(+) create mode 100644 packages/test-utils/lib/redis-proxy-spec.ts create mode 100644 packages/test-utils/lib/redis-proxy.ts create mode 100644 packages/test-utils/lib/test-utils.ts diff --git a/packages/client/lib/test-utils.ts b/packages/client/lib/test-utils.ts index 809ee788e92..b9b906e943c 100644 --- a/packages/client/lib/test-utils.ts +++ b/packages/client/lib/test-utils.ts @@ -93,6 +93,12 @@ export const GLOBAL = { password: 'password' } }, + OPEN_RESP_3: { + serverArguments: [...DEBUG_MODE_ARGS], + clientOptions: { + RESP: 3, + } + }, ASYNC_BASIC_AUTH: { serverArguments: ['--requirepass', 'password', ...DEBUG_MODE_ARGS], clientOptions: { diff --git a/packages/test-utils/lib/index.ts b/packages/test-utils/lib/index.ts index aab1c700f5e..64b9abc7f48 100644 --- a/packages/test-utils/lib/index.ts +++ b/packages/test-utils/lib/index.ts @@ -26,6 +26,7 @@ import { hideBin } from 'yargs/helpers'; import * as fs from 'node:fs'; import * as os from 'node:os'; import * as path from 'node:path'; +import { RedisProxy, getFreePortNumber } from './redis-proxy'; interface TestUtilsConfig { /** @@ -296,7 +297,44 @@ export default class TestUtils { } }); } + testWithProxiedClient( + title: string, + fn: (proxiedClient: RedisClientType, proxy: RedisProxy) => unknown, + options: ClientTestOptions + ) { + + this.testWithClient(title, async (client) => { + const freePort = await getFreePortNumber() + const socketOptions = client?.options?.socket; + const proxy = new RedisProxy({ + listenHost: '127.0.0.1', + listenPort: freePort, + //@ts-ignore + targetPort: socketOptions.port, + //@ts-ignore + targetHost: socketOptions.host, + enableLogging: true + }); + + + await proxy.start(); + const proxyClient = client.duplicate({ + socket: { + port: proxy.config.listenPort, + host: proxy.config.listenHost + }, + }); + await proxyClient.connect(); + + try { + await fn(proxyClient, proxy); + } finally { + await proxyClient.destroy(); + await proxy.stop() + } + }, options); + } testWithClientSentinel< M extends RedisModules = {}, F extends RedisFunctions = {}, diff --git a/packages/test-utils/lib/redis-proxy-spec.ts b/packages/test-utils/lib/redis-proxy-spec.ts new file mode 100644 index 00000000000..89b3b28c35a --- /dev/null +++ b/packages/test-utils/lib/redis-proxy-spec.ts @@ -0,0 +1,111 @@ +import { strict as assert } from 'node:assert'; +import { Buffer } from 'node:buffer'; +import { testUtils, GLOBAL } from './test-utils'; +import { RedisProxy } from './redis-proxy'; +import type { RedisClientType } from '@redis/client/lib/client/index.js'; + +describe('RedisSocketProxy', function () { + testUtils.testWithClient('basic proxy functionality', async (client: RedisClientType) => { + const socketOptions = client?.options?.socket; + //@ts-ignore + assert(socketOptions?.port, 'Test requires a TCP connection to Redis'); + + const proxyPort = 50000 + Math.floor(Math.random() * 10000); + const proxy = new RedisProxy({ + listenHost: '127.0.0.1', + listenPort: proxyPort, + //@ts-ignore + targetPort: socketOptions.port, + //@ts-ignore + targetHost: socketOptions.host || '127.0.0.1', + enableLogging: true + }); + + const proxyEvents = { + connections: [] as any[], + dataTransfers: [] as any[] + }; + + proxy.on('connection', (connectionInfo) => { + proxyEvents.connections.push(connectionInfo); + }); + + proxy.on('data', (connectionId, direction, data) => { + proxyEvents.dataTransfers.push({ connectionId, direction, dataLength: data.length }); + }); + + try { + await proxy.start(); + + const proxyClient = client.duplicate({ + socket: { + port: proxyPort, + host: '127.0.0.1' + }, + }); + + await proxyClient.connect(); + + const stats = proxy.getStats(); + assert.equal(stats.activeConnections, 1, 'Should have one active connection'); + assert.equal(proxyEvents.connections.length, 1, 'Should have recorded one connection event'); + + const pingResult = await proxyClient.ping(); + assert.equal(pingResult, 'PONG', 'Client should be able to communicate with Redis through the proxy'); + + const clientToServerTransfers = proxyEvents.dataTransfers.filter(t => t.direction === 'client->server'); + const serverToClientTransfers = proxyEvents.dataTransfers.filter(t => t.direction === 'server->client'); + + assert(clientToServerTransfers.length > 0, 'Should have client->server data transfers'); + assert(serverToClientTransfers.length > 0, 'Should have server->client data transfers'); + + const testKey = `test:proxy:${Date.now()}`; + const testValue = 'proxy-test-value'; + + await proxyClient.set(testKey, testValue); + const retrievedValue = await proxyClient.get(testKey); + assert.equal(retrievedValue, testValue, 'Should be able to set and get values through proxy'); + + proxyClient.destroy(); + + + } finally { + await proxy.stop(); + } + }, GLOBAL.SERVERS.OPEN_RESP_3); + + testUtils.testWithProxiedClient('custom message injection via proxy client', + async (proxiedClient: RedisClientType, proxy: RedisProxy) => { + const customMessageTransfers: any[] = []; + + proxy.on('data', (connectionId, direction, data) => { + if (direction === 'server->client') { + customMessageTransfers.push({ connectionId, dataLength: data.length, data }); + } + }); + + + const stats = proxy.getStats(); + assert.equal(stats.activeConnections, 1, 'Should have one active connection'); + + // Send a resp3 push + const customMessage = Buffer.from('>4\r\n$6\r\nMOVING\r\n:1\r\n:2\r\n$6\r\nhost:3\r\n'); + + const sendResults = proxy.sendToAllClients(customMessage); + assert.equal(sendResults.length, 1, 'Should send to one client'); + assert.equal(sendResults[0].success, true, 'Custom message send should succeed'); + + + const customMessageFound = customMessageTransfers.find(transfer => + transfer.dataLength === customMessage.length + ); + assert(customMessageFound, 'Should have recorded the custom message transfer'); + + assert.equal(customMessageFound.dataLength, customMessage.length, + 'Custom message length should match'); + + const pingResult = await proxiedClient.ping(); + assert.equal(pingResult, 'PONG', 'Client should be able to communicate with Redis through the proxy'); + + }, GLOBAL.SERVERS.OPEN_RESP_3) +}); diff --git a/packages/test-utils/lib/redis-proxy.ts b/packages/test-utils/lib/redis-proxy.ts new file mode 100644 index 00000000000..217ec528a33 --- /dev/null +++ b/packages/test-utils/lib/redis-proxy.ts @@ -0,0 +1,329 @@ +import * as net from 'net'; +import { EventEmitter } from 'events'; + +interface ProxyConfig { + readonly listenPort: number; + readonly listenHost?: string; + readonly targetHost: string; + readonly targetPort: number; + readonly timeout?: number; + readonly enableLogging?: boolean; +} + +interface ConnectionInfo { + readonly id: string; + readonly clientAddress: string; + readonly clientPort: number; + readonly connectedAt: Date; +} + +interface ActiveConnection extends ConnectionInfo { + readonly clientSocket: net.Socket; + readonly serverSocket: net.Socket; +} + +type SendResult = + | { readonly success: true; readonly connectionId: string } + | { readonly success: false; readonly error: string; readonly connectionId: string }; + +type DataDirection = 'client->server' | 'server->client'; + +interface ProxyStats { + readonly activeConnections: number; + readonly totalConnections: number; + readonly connections: readonly ConnectionInfo[]; +} + +interface ProxyEvents { + /** Emitted when a new client connects */ + 'connection': (connectionInfo: ConnectionInfo) => void; + /** Emitted when a connection is closed */ + 'disconnect': (connectionInfo: ConnectionInfo) => void; + /** Emitted when data is transferred */ + 'data': (connectionId: string, direction: DataDirection, data: Buffer) => void; + /** Emitted when an error occurs */ + 'error': (error: Error, connectionId?: string) => void; + /** Emitted when the proxy server starts */ + 'listening': (host: string, port: number) => void; + /** Emitted when the proxy server stops */ + 'close': () => void; +} + +export class RedisProxy extends EventEmitter { + private readonly server: net.Server; + public readonly config: Required; + private readonly connections: Map; + private isRunning: boolean; + + constructor(config: ProxyConfig) { + super(); + + + this.config = { + listenHost: '127.0.0.1', + timeout: 30000, + enableLogging: false, + ...config + }; + + this.connections = new Map(); + this.isRunning = false; + this.server = this.createServer(); + } + + public async start(): Promise { + return new Promise((resolve, reject) => { + if (this.isRunning) { + reject(new Error('Proxy is already running')); + return; + } + + this.server.listen(this.config.listenPort, this.config.listenHost, () => { + this.isRunning = true; + this.log(`Proxy listening on ${this.config.listenHost}:${this.config.listenPort}`); + this.log(`Forwarding to Redis server at ${this.config.targetHost}:${this.config.targetPort}`); + this.emit('listening', this.config.listenHost, this.config.listenPort); + resolve(); + }); + + this.server.on('error', (error) => { + this.emit('error', error); + reject(error); + }); + }); + } + + public async stop(): Promise { + return new Promise((resolve) => { + if (!this.isRunning) { + resolve(); + return; + } + + Array.from(this.connections.keys()).forEach((connectionId) => { + this.closeConnection(connectionId); + }); + + this.server.close(() => { + this.isRunning = false; + this.log('Proxy server stopped'); + this.emit('close'); + resolve(); + }); + }); + } + + public getStats(): ProxyStats { + const connections = Array.from(this.connections.values()); + + return { + activeConnections: connections.length, + totalConnections: connections.length, + connections: connections.map((conn) => ({ + id: conn.id, + clientAddress: conn.clientAddress, + clientPort: conn.clientPort, + connectedAt: conn.connectedAt, + })) + }; + } + + public closeConnection(connectionId: string): boolean { + const connection = this.connections.get(connectionId); + if (!connection) { + return false; + } + + connection.clientSocket.destroy(); + connection.serverSocket.destroy(); + this.connections.delete(connectionId); + this.emit('disconnect', connection); + return true; + } + + public sendToClient(connectionId: string, data: Buffer): SendResult { + const connection = this.connections.get(connectionId); + if (!connection) { + return { + success: false, + error: 'Connection not found', + connectionId + }; + } + + if (connection.clientSocket.destroyed || !connection.clientSocket.writable) { + return { + success: false, + error: 'Client socket is not writable', + connectionId + }; + } + + try { + connection.clientSocket.write(data); + + this.log(`Sent ${data.length} bytes to client ${connectionId}`); + this.emit('data', connectionId, 'server->client', data); + + return { + success: true, + connectionId + }; + } catch (error) { + const errorMessage = error instanceof Error ? error.message : 'Unknown error'; + this.log(`Failed to send data to client ${connectionId}: ${errorMessage}`); + return { + success: false, + error: errorMessage, + connectionId + }; + } + } + + public sendToAllClients(data: Buffer): readonly SendResult[] { + const connectionIds = Array.from(this.connections.keys()); + const results = connectionIds.map((connectionId) => + this.sendToClient(connectionId, data) + ); + + const successCount = results.filter((result) => result.success).length; + const totalCount = results.length; + + this.log(`Sent ${data.length} bytes to ${successCount}/${totalCount} clients`); + + return results; + } + + public sendToClients(connectionIds: readonly string[], data: Buffer): readonly SendResult[] { + const results = connectionIds.map((connectionId) => + this.sendToClient(connectionId, data) + ); + + const successCount = results.filter((result) => result.success).length; + const totalCount = results.length; + + this.log(`Sent ${data.length} bytes to ${successCount}/${totalCount} specified clients`); + + return results; + } + + public getActiveConnectionIds(): readonly string[] { + return Array.from(this.connections.keys()); + } + + private createServer(): net.Server { + return net.createServer((clientSocket) => { + this.handleClientConnection(clientSocket); + }); + } + + private handleClientConnection(clientSocket: net.Socket): void { + const connectionId = this.generateConnectionId(); + const serverSocket = net.createConnection({ + host: this.config.targetHost, + port: this.config.targetPort + }); + + const connectionInfo: ActiveConnection = { + id: connectionId, + clientAddress: clientSocket.remoteAddress || 'unknown', + clientPort: clientSocket.remotePort || 0, + connectedAt: new Date(), + clientSocket, + serverSocket + }; + + this.connections.set(connectionId, connectionInfo); + this.log(`New connection ${connectionId} from ${connectionInfo.clientAddress}:${connectionInfo.clientPort}`); + + clientSocket.setTimeout(this.config.timeout); + + serverSocket.on('connect', () => { + this.log(`Connected to Redis server for connection ${connectionId}`); + this.emit('connection', connectionInfo); + }); + + clientSocket.on('data', (data) => { + this.emit('data', connectionId, 'client->server', data); + serverSocket.write(data); + }); + + serverSocket.on('data', (data) => { + this.emit('data', connectionId, 'server->client', data); + clientSocket.write(data); + }); + + clientSocket.on('close', () => { + this.log(`Client disconnected for connection ${connectionId}`); + serverSocket.destroy(); + this.cleanupConnection(connectionId); + }); + + serverSocket.on('close', () => { + this.log(`Server disconnected for connection ${connectionId}`); + clientSocket.destroy(); + this.cleanupConnection(connectionId); + }); + + clientSocket.on('error', (error) => { + this.log(`Client error for connection ${connectionId}: ${error.message}`); + this.emit('error', error, connectionId); + serverSocket.destroy(); + this.cleanupConnection(connectionId); + }); + + serverSocket.on('error', (error) => { + this.log(`Server error for connection ${connectionId}: ${error.message}`); + this.emit('error', error, connectionId); + clientSocket.destroy(); + this.cleanupConnection(connectionId); + }); + + clientSocket.on('timeout', () => { + this.log(`Connection ${connectionId} timed out`); + clientSocket.destroy(); + serverSocket.destroy(); + this.cleanupConnection(connectionId); + }); + } + + private cleanupConnection(connectionId: string): void { + const connection = this.connections.get(connectionId); + if (connection) { + this.connections.delete(connectionId); + this.emit('disconnect', connection); + } + } + + private generateConnectionId(): string { + return `conn_${Date.now()}_${Math.random().toString(36).substr(2, 9)}`; + } + + private log(message: string): void { + if (this.config.enableLogging) { + console.log(`[RedisProxy] ${new Date().toISOString()} - ${message}`); + } + } +} +import { createServer } from 'net'; + +export function getFreePortNumber(): Promise { + return new Promise((resolve, reject) => { + const server = createServer(); + + server.listen(0, () => { + const address = server.address(); + server.close(() => { + if (address && typeof address === 'object') { + resolve(address.port); + } + }); + }); + + server.on('error', reject); + }); +} + +export { RedisProxy as RedisTransparentProxy }; +export type { ProxyConfig, ConnectionInfo, ProxyEvents, SendResult, DataDirection, ProxyStats }; + diff --git a/packages/test-utils/lib/test-utils.ts b/packages/test-utils/lib/test-utils.ts new file mode 100644 index 00000000000..fe27bd93d37 --- /dev/null +++ b/packages/test-utils/lib/test-utils.ts @@ -0,0 +1,25 @@ +import TestUtils from './index' + +export const testUtils = TestUtils.createFromConfig({ + dockerImageName: 'redislabs/client-libs-test', + dockerImageVersionArgument: 'redis-version', + defaultDockerVersion: '8.2-M01-pre' +}); + + + +export const DEBUG_MODE_ARGS = testUtils.isVersionGreaterThan([7]) ? + ['--enable-debug-command', 'yes'] : + []; + +export const GLOBAL = { + SERVERS: { + + OPEN_RESP_3: { + serverArguments: [...DEBUG_MODE_ARGS], + clientOptions: { + RESP: 3, + } + }, + } +} From 1c266371a3dd1d4fa66dfbe52892eec5fc77af6f Mon Sep 17 00:00:00 2001 From: GitHub Action Date: Wed, 23 Jul 2025 15:52:42 +0000 Subject: [PATCH 1655/1748] Release client@5.6.1 --- package-lock.json | 14 +++++++++++++- packages/client/package.json | 2 +- 2 files changed, 14 insertions(+), 2 deletions(-) diff --git a/package-lock.json b/package-lock.json index b8ff84d8259..f5ff9b6b95c 100644 --- a/package-lock.json +++ b/package-lock.json @@ -7351,7 +7351,7 @@ }, "packages/client": { "name": "@redis/client", - "version": "5.6.0", + "version": "5.6.1", "license": "MIT", "dependencies": { "cluster-key-slot": "1.1.2" @@ -7449,6 +7449,18 @@ "node": ">= 18" } }, + "packages/redis/node_modules/@redis/client": { + "version": "5.6.0", + "resolved": "https://registry.npmjs.org/@redis/client/-/client-5.6.0.tgz", + "integrity": "sha512-wmP9kCFElCSr4MM4+1E4VckDuN4wLtiXSM/J0rKVQppajxQhowci89RGZr2OdLualowb8SRJ/R6OjsXrn9ZNFA==", + "license": "MIT", + "dependencies": { + "cluster-key-slot": "1.1.2" + }, + "engines": { + "node": ">= 18" + } + }, "packages/search": { "name": "@redis/search", "version": "5.6.0", diff --git a/packages/client/package.json b/packages/client/package.json index ee98d77ca1f..9fe67df521e 100644 --- a/packages/client/package.json +++ b/packages/client/package.json @@ -1,6 +1,6 @@ { "name": "@redis/client", - "version": "5.6.0", + "version": "5.6.1", "license": "MIT", "main": "./dist/index.js", "types": "./dist/index.d.ts", From 945b77af16ae4195856706ab464475a977d5c59a Mon Sep 17 00:00:00 2001 From: GitHub Action Date: Wed, 23 Jul 2025 15:52:49 +0000 Subject: [PATCH 1656/1748] Release bloom@5.6.1 --- package-lock.json | 16 ++++++++++++++-- packages/bloom/package.json | 4 ++-- 2 files changed, 16 insertions(+), 4 deletions(-) diff --git a/package-lock.json b/package-lock.json index f5ff9b6b95c..53202b80163 100644 --- a/package-lock.json +++ b/package-lock.json @@ -7337,7 +7337,7 @@ }, "packages/bloom": { "name": "@redis/bloom", - "version": "5.6.0", + "version": "5.6.1", "license": "MIT", "devDependencies": { "@redis/test-utils": "*" @@ -7346,7 +7346,7 @@ "node": ">= 18" }, "peerDependencies": { - "@redis/client": "^5.6.0" + "@redis/client": "^5.6.1" } }, "packages/client": { @@ -7449,6 +7449,18 @@ "node": ">= 18" } }, + "packages/redis/node_modules/@redis/bloom": { + "version": "5.6.0", + "resolved": "https://registry.npmjs.org/@redis/bloom/-/bloom-5.6.0.tgz", + "integrity": "sha512-l13/d6BaZDJzogzZJEphIeZ8J0hpQpjkMiozomTm6nJiMNYkoPsNOBOOQua4QsG0fFjyPmLMDJFPAp5FBQtTXg==", + "license": "MIT", + "engines": { + "node": ">= 18" + }, + "peerDependencies": { + "@redis/client": "^5.6.0" + } + }, "packages/redis/node_modules/@redis/client": { "version": "5.6.0", "resolved": "https://registry.npmjs.org/@redis/client/-/client-5.6.0.tgz", diff --git a/packages/bloom/package.json b/packages/bloom/package.json index 47d1f6978ef..ef900b00a6d 100644 --- a/packages/bloom/package.json +++ b/packages/bloom/package.json @@ -1,6 +1,6 @@ { "name": "@redis/bloom", - "version": "5.6.0", + "version": "5.6.1", "license": "MIT", "main": "./dist/lib/index.js", "types": "./dist/lib/index.d.ts", @@ -13,7 +13,7 @@ "release": "release-it" }, "peerDependencies": { - "@redis/client": "^5.6.0" + "@redis/client": "^5.6.1" }, "devDependencies": { "@redis/test-utils": "*" From fc5ff18875bca9c6f8b379637f74d3916edef97f Mon Sep 17 00:00:00 2001 From: GitHub Action Date: Wed, 23 Jul 2025 15:52:56 +0000 Subject: [PATCH 1657/1748] Release json@5.6.1 --- package-lock.json | 16 ++++++++++++++-- packages/json/package.json | 4 ++-- 2 files changed, 16 insertions(+), 4 deletions(-) diff --git a/package-lock.json b/package-lock.json index 53202b80163..35177cf51a2 100644 --- a/package-lock.json +++ b/package-lock.json @@ -7423,7 +7423,7 @@ }, "packages/json": { "name": "@redis/json", - "version": "5.6.0", + "version": "5.6.1", "license": "MIT", "devDependencies": { "@redis/test-utils": "*" @@ -7432,7 +7432,7 @@ "node": ">= 18" }, "peerDependencies": { - "@redis/client": "^5.6.0" + "@redis/client": "^5.6.1" } }, "packages/redis": { @@ -7473,6 +7473,18 @@ "node": ">= 18" } }, + "packages/redis/node_modules/@redis/json": { + "version": "5.6.0", + "resolved": "https://registry.npmjs.org/@redis/json/-/json-5.6.0.tgz", + "integrity": "sha512-YQN9ZqaSDpdLfJqwzcF4WeuJMGru/h4WsV7GeeNtXsSeyQjHTyDxrd48xXfRRJGv7HitA7zGnzdHplNeKOgrZA==", + "license": "MIT", + "engines": { + "node": ">= 18" + }, + "peerDependencies": { + "@redis/client": "^5.6.0" + } + }, "packages/search": { "name": "@redis/search", "version": "5.6.0", diff --git a/packages/json/package.json b/packages/json/package.json index edfdaec8efa..e62377feb14 100644 --- a/packages/json/package.json +++ b/packages/json/package.json @@ -1,6 +1,6 @@ { "name": "@redis/json", - "version": "5.6.0", + "version": "5.6.1", "license": "MIT", "main": "./dist/lib/index.js", "types": "./dist/lib/index.d.ts", @@ -13,7 +13,7 @@ "release": "release-it" }, "peerDependencies": { - "@redis/client": "^5.6.0" + "@redis/client": "^5.6.1" }, "devDependencies": { "@redis/test-utils": "*" From 726e2c7c649110a307dd3553c0f6c16dc0324aea Mon Sep 17 00:00:00 2001 From: GitHub Action Date: Wed, 23 Jul 2025 15:53:04 +0000 Subject: [PATCH 1658/1748] Release search@5.6.1 --- package-lock.json | 16 ++++++++++++++-- packages/search/package.json | 4 ++-- 2 files changed, 16 insertions(+), 4 deletions(-) diff --git a/package-lock.json b/package-lock.json index 35177cf51a2..b02d5b9dd7c 100644 --- a/package-lock.json +++ b/package-lock.json @@ -7485,9 +7485,21 @@ "@redis/client": "^5.6.0" } }, + "packages/redis/node_modules/@redis/search": { + "version": "5.6.0", + "resolved": "https://registry.npmjs.org/@redis/search/-/search-5.6.0.tgz", + "integrity": "sha512-sLgQl92EyMVNHtri5K8Q0j2xt9c0cO9HYurXz667Un4xeUYR+B/Dw5lLG35yqO7VvVxb9amHJo9sAWumkKZYwA==", + "license": "MIT", + "engines": { + "node": ">= 18" + }, + "peerDependencies": { + "@redis/client": "^5.6.0" + } + }, "packages/search": { "name": "@redis/search", - "version": "5.6.0", + "version": "5.6.1", "license": "MIT", "devDependencies": { "@redis/test-utils": "*" @@ -7496,7 +7508,7 @@ "node": ">= 18" }, "peerDependencies": { - "@redis/client": "^5.6.0" + "@redis/client": "^5.6.1" } }, "packages/test-utils": { diff --git a/packages/search/package.json b/packages/search/package.json index c876d7460c3..c8fb49d5332 100644 --- a/packages/search/package.json +++ b/packages/search/package.json @@ -1,6 +1,6 @@ { "name": "@redis/search", - "version": "5.6.0", + "version": "5.6.1", "license": "MIT", "main": "./dist/lib/index.js", "types": "./dist/lib/index.d.ts", @@ -14,7 +14,7 @@ "release": "release-it" }, "peerDependencies": { - "@redis/client": "^5.6.0" + "@redis/client": "^5.6.1" }, "devDependencies": { "@redis/test-utils": "*" From 3c4defda0ba9eb66b300321dcafeff85c7c31a8c Mon Sep 17 00:00:00 2001 From: GitHub Action Date: Wed, 23 Jul 2025 15:53:11 +0000 Subject: [PATCH 1659/1748] Release time-series@5.6.1 --- package-lock.json | 16 ++++++++++++++-- packages/time-series/package.json | 4 ++-- 2 files changed, 16 insertions(+), 4 deletions(-) diff --git a/package-lock.json b/package-lock.json index b02d5b9dd7c..2acb520b064 100644 --- a/package-lock.json +++ b/package-lock.json @@ -7497,6 +7497,18 @@ "@redis/client": "^5.6.0" } }, + "packages/redis/node_modules/@redis/time-series": { + "version": "5.6.0", + "resolved": "https://registry.npmjs.org/@redis/time-series/-/time-series-5.6.0.tgz", + "integrity": "sha512-tXABmN1vu4aTNL3WI4Iolpvx/5jgil2Bs31ozvKblT+jkUoRkk8ykmYo9Pv/Mp7Gk6/Qkr/2rMgVminrt/4BBQ==", + "license": "MIT", + "engines": { + "node": ">= 18" + }, + "peerDependencies": { + "@redis/client": "^5.6.0" + } + }, "packages/search": { "name": "@redis/search", "version": "5.6.1", @@ -7577,7 +7589,7 @@ }, "packages/time-series": { "name": "@redis/time-series", - "version": "5.6.0", + "version": "5.6.1", "license": "MIT", "devDependencies": { "@redis/test-utils": "*" @@ -7586,7 +7598,7 @@ "node": ">= 18" }, "peerDependencies": { - "@redis/client": "^5.6.0" + "@redis/client": "^5.6.1" } } } diff --git a/packages/time-series/package.json b/packages/time-series/package.json index 762aea77661..1c42757523f 100644 --- a/packages/time-series/package.json +++ b/packages/time-series/package.json @@ -1,6 +1,6 @@ { "name": "@redis/time-series", - "version": "5.6.0", + "version": "5.6.1", "license": "MIT", "main": "./dist/lib/index.js", "types": "./dist/lib/index.d.ts", @@ -13,7 +13,7 @@ "release": "release-it" }, "peerDependencies": { - "@redis/client": "^5.6.0" + "@redis/client": "^5.6.1" }, "devDependencies": { "@redis/test-utils": "*" From 3365920470b2297c01580901c9e82ff452b2f5ec Mon Sep 17 00:00:00 2001 From: GitHub Action Date: Wed, 23 Jul 2025 15:53:19 +0000 Subject: [PATCH 1660/1748] Release entraid@5.6.1 --- package-lock.json | 4 ++-- packages/entraid/package.json | 4 ++-- 2 files changed, 4 insertions(+), 4 deletions(-) diff --git a/package-lock.json b/package-lock.json index 2acb520b064..073126960af 100644 --- a/package-lock.json +++ b/package-lock.json @@ -7367,7 +7367,7 @@ }, "packages/entraid": { "name": "@redis/entraid", - "version": "5.6.0", + "version": "5.6.1", "license": "MIT", "dependencies": { "@azure/identity": "^4.7.0", @@ -7386,7 +7386,7 @@ "node": ">= 18" }, "peerDependencies": { - "@redis/client": "^5.6.0" + "@redis/client": "^5.6.1" } }, "packages/entraid/node_modules/@types/node": { diff --git a/packages/entraid/package.json b/packages/entraid/package.json index 173bd0a6c67..4e702fabb65 100644 --- a/packages/entraid/package.json +++ b/packages/entraid/package.json @@ -1,6 +1,6 @@ { "name": "@redis/entraid", - "version": "5.6.0", + "version": "5.6.1", "license": "MIT", "main": "./dist/index.js", "types": "./dist/index.d.ts", @@ -22,7 +22,7 @@ "@azure/msal-node": "^2.16.1" }, "peerDependencies": { - "@redis/client": "^5.6.0" + "@redis/client": "^5.6.1" }, "devDependencies": { "@types/express": "^4.17.21", From e96db0d13c8161fa81d27b63072c09849bcbb6a2 Mon Sep 17 00:00:00 2001 From: GitHub Action Date: Wed, 23 Jul 2025 15:53:25 +0000 Subject: [PATCH 1661/1748] Release redis@5.6.1 --- package-lock.json | 72 ++++--------------------------------- packages/redis/package.json | 12 +++---- 2 files changed, 12 insertions(+), 72 deletions(-) diff --git a/package-lock.json b/package-lock.json index 073126960af..71e0ce79df8 100644 --- a/package-lock.json +++ b/package-lock.json @@ -7436,79 +7436,19 @@ } }, "packages/redis": { - "version": "5.6.0", - "license": "MIT", - "dependencies": { - "@redis/bloom": "5.6.0", - "@redis/client": "5.6.0", - "@redis/json": "5.6.0", - "@redis/search": "5.6.0", - "@redis/time-series": "5.6.0" - }, - "engines": { - "node": ">= 18" - } - }, - "packages/redis/node_modules/@redis/bloom": { - "version": "5.6.0", - "resolved": "https://registry.npmjs.org/@redis/bloom/-/bloom-5.6.0.tgz", - "integrity": "sha512-l13/d6BaZDJzogzZJEphIeZ8J0hpQpjkMiozomTm6nJiMNYkoPsNOBOOQua4QsG0fFjyPmLMDJFPAp5FBQtTXg==", - "license": "MIT", - "engines": { - "node": ">= 18" - }, - "peerDependencies": { - "@redis/client": "^5.6.0" - } - }, - "packages/redis/node_modules/@redis/client": { - "version": "5.6.0", - "resolved": "https://registry.npmjs.org/@redis/client/-/client-5.6.0.tgz", - "integrity": "sha512-wmP9kCFElCSr4MM4+1E4VckDuN4wLtiXSM/J0rKVQppajxQhowci89RGZr2OdLualowb8SRJ/R6OjsXrn9ZNFA==", + "version": "5.6.1", "license": "MIT", "dependencies": { - "cluster-key-slot": "1.1.2" + "@redis/bloom": "5.6.1", + "@redis/client": "5.6.1", + "@redis/json": "5.6.1", + "@redis/search": "5.6.1", + "@redis/time-series": "5.6.1" }, "engines": { "node": ">= 18" } }, - "packages/redis/node_modules/@redis/json": { - "version": "5.6.0", - "resolved": "https://registry.npmjs.org/@redis/json/-/json-5.6.0.tgz", - "integrity": "sha512-YQN9ZqaSDpdLfJqwzcF4WeuJMGru/h4WsV7GeeNtXsSeyQjHTyDxrd48xXfRRJGv7HitA7zGnzdHplNeKOgrZA==", - "license": "MIT", - "engines": { - "node": ">= 18" - }, - "peerDependencies": { - "@redis/client": "^5.6.0" - } - }, - "packages/redis/node_modules/@redis/search": { - "version": "5.6.0", - "resolved": "https://registry.npmjs.org/@redis/search/-/search-5.6.0.tgz", - "integrity": "sha512-sLgQl92EyMVNHtri5K8Q0j2xt9c0cO9HYurXz667Un4xeUYR+B/Dw5lLG35yqO7VvVxb9amHJo9sAWumkKZYwA==", - "license": "MIT", - "engines": { - "node": ">= 18" - }, - "peerDependencies": { - "@redis/client": "^5.6.0" - } - }, - "packages/redis/node_modules/@redis/time-series": { - "version": "5.6.0", - "resolved": "https://registry.npmjs.org/@redis/time-series/-/time-series-5.6.0.tgz", - "integrity": "sha512-tXABmN1vu4aTNL3WI4Iolpvx/5jgil2Bs31ozvKblT+jkUoRkk8ykmYo9Pv/Mp7Gk6/Qkr/2rMgVminrt/4BBQ==", - "license": "MIT", - "engines": { - "node": ">= 18" - }, - "peerDependencies": { - "@redis/client": "^5.6.0" - } - }, "packages/search": { "name": "@redis/search", "version": "5.6.1", diff --git a/packages/redis/package.json b/packages/redis/package.json index 32251c7e501..bdb86663480 100644 --- a/packages/redis/package.json +++ b/packages/redis/package.json @@ -1,7 +1,7 @@ { "name": "redis", "description": "A modern, high performance Redis client", - "version": "5.6.0", + "version": "5.6.1", "license": "MIT", "main": "./dist/index.js", "types": "./dist/index.d.ts", @@ -13,11 +13,11 @@ "release": "release-it" }, "dependencies": { - "@redis/bloom": "5.6.0", - "@redis/client": "5.6.0", - "@redis/json": "5.6.0", - "@redis/search": "5.6.0", - "@redis/time-series": "5.6.0" + "@redis/bloom": "5.6.1", + "@redis/client": "5.6.1", + "@redis/json": "5.6.1", + "@redis/search": "5.6.1", + "@redis/time-series": "5.6.1" }, "engines": { "node": ">= 18" From ff8319d2d7fe7afb6ae1eb129ff5526370aef4af Mon Sep 17 00:00:00 2001 From: Pavel Pashov <60297174+PavelPashov@users.noreply.github.com> Date: Fri, 25 Jul 2025 16:39:32 +0300 Subject: [PATCH 1662/1748] fix(pool): chain promise handlers to prevent unhandled rejections (#3035) --- packages/client/lib/client/pool.spec.ts | 31 +++++++++++++++++++++++++ packages/client/lib/client/pool.ts | 5 ++-- 2 files changed, 34 insertions(+), 2 deletions(-) diff --git a/packages/client/lib/client/pool.spec.ts b/packages/client/lib/client/pool.spec.ts index 8fc7a258df9..f292dc171c7 100644 --- a/packages/client/lib/client/pool.spec.ts +++ b/packages/client/lib/client/pool.spec.ts @@ -8,4 +8,35 @@ describe('RedisClientPool', () => { 'PONG' ); }, GLOBAL.SERVERS.OPEN); + + testUtils.testWithClientPool( + 'proper error propagation in sequential operations', + async (pool) => { + let hasUnhandledRejection = false; + + process.once('unhandledRejection', () => { + hasUnhandledRejection = true; + }); + + const groupName = 'test-group'; + const streamName = 'test-stream'; + + // First attempt - should succeed + await pool.xGroupCreate(streamName, groupName, '0', { + MKSTREAM: true, + }); + + // Subsequent attempts - should all throw BUSYGROUP errors and be handled properly + for (let i = 0; i < 3; i++) { + await assert.rejects( + pool.xGroupCreate(streamName, groupName, '0', { + MKSTREAM: true, + }) + ); + } + + assert.equal(hasUnhandledRejection, false); + }, + GLOBAL.SERVERS.OPEN + ); }); diff --git a/packages/client/lib/client/pool.ts b/packages/client/lib/client/pool.ts index 6f633c9caa7..b53bb2c7e61 100644 --- a/packages/client/lib/client/pool.ts +++ b/packages/client/lib/client/pool.ts @@ -438,8 +438,9 @@ export class RedisClientPool< ) { const result = fn(node.value); if (result instanceof Promise) { - result.then(resolve, reject); - result.finally(() => this.#returnClient(node)) + result + .then(resolve, reject) + .finally(() => this.#returnClient(node)) } else { resolve(result); this.#returnClient(node); From d941ec5a4cc73ff8d0965becfdca41a4dc521004 Mon Sep 17 00:00:00 2001 From: Pavel Pashov <60297174+PavelPashov@users.noreply.github.com> Date: Fri, 25 Jul 2025 17:58:28 +0300 Subject: [PATCH 1663/1748] Add Redis 8.2 New Stream Commands (#3029) * chore: update Redis version from 8.2-RC1-pre to 8.2-rc1 * feat: implement XDELEX command for Redis 8.2 * feat: implement XACKDEL command for Redis 8.2 * refactor: create shared stream deletion types for Redis 8.2 commands * feat: add Redis 8.2 deletion policies to XTRIM command * feat: add Redis 8.2 deletion policies to XADD commands * fix: correct XDELEX command method name and test parameter --- .github/workflows/tests.yml | 2 +- packages/bloom/lib/test-utils.ts | 2 +- packages/client/lib/commands/XACKDEL.spec.ts | 196 ++++++++++++++++++ packages/client/lib/commands/XACKDEL.ts | 45 ++++ packages/client/lib/commands/XADD.spec.ts | 80 +++++++ packages/client/lib/commands/XADD.ts | 8 + .../lib/commands/XADD_NOMKSTREAM.spec.ts | 88 +++++++- packages/client/lib/commands/XDELEX.spec.ts | 156 ++++++++++++++ packages/client/lib/commands/XDELEX.ts | 42 ++++ packages/client/lib/commands/XTRIM.spec.ts | 95 ++++++++- packages/client/lib/commands/XTRIM.ts | 8 + .../lib/commands/common-stream.types.ts | 28 +++ packages/client/lib/commands/index.ts | 6 + packages/client/lib/sentinel/test-util.ts | 2 +- packages/client/lib/test-utils.ts | 2 +- packages/entraid/lib/test-utils.ts | 2 +- packages/json/lib/test-utils.ts | 2 +- packages/search/lib/test-utils.ts | 2 +- packages/time-series/lib/test-utils.ts | 2 +- 19 files changed, 746 insertions(+), 22 deletions(-) create mode 100644 packages/client/lib/commands/XACKDEL.spec.ts create mode 100644 packages/client/lib/commands/XACKDEL.ts create mode 100644 packages/client/lib/commands/XDELEX.spec.ts create mode 100644 packages/client/lib/commands/XDELEX.ts create mode 100644 packages/client/lib/commands/common-stream.types.ts diff --git a/.github/workflows/tests.yml b/.github/workflows/tests.yml index 89efdb61114..df8cb1d1b6c 100644 --- a/.github/workflows/tests.yml +++ b/.github/workflows/tests.yml @@ -22,7 +22,7 @@ jobs: fail-fast: false matrix: node-version: ["18", "20", "22"] - redis-version: ["rs-7.2.0-v13", "rs-7.4.0-v1", "8.0.2", "8.2-M01-pre"] + redis-version: ["rs-7.4.0-v1", "8.0.2", "8.2-rc1"] steps: - uses: actions/checkout@v4 with: diff --git a/packages/bloom/lib/test-utils.ts b/packages/bloom/lib/test-utils.ts index 4396c94f726..268ebca8cb9 100644 --- a/packages/bloom/lib/test-utils.ts +++ b/packages/bloom/lib/test-utils.ts @@ -4,7 +4,7 @@ import RedisBloomModules from '.'; export default TestUtils.createFromConfig({ dockerImageName: 'redislabs/client-libs-test', dockerImageVersionArgument: 'redis-version', - defaultDockerVersion: '8.2-M01-pre' + defaultDockerVersion: '8.2-rc1' }); export const GLOBAL = { diff --git a/packages/client/lib/commands/XACKDEL.spec.ts b/packages/client/lib/commands/XACKDEL.spec.ts new file mode 100644 index 00000000000..9d7bad15a24 --- /dev/null +++ b/packages/client/lib/commands/XACKDEL.spec.ts @@ -0,0 +1,196 @@ +import { strict as assert } from "node:assert"; +import XACKDEL from "./XACKDEL"; +import { parseArgs } from "./generic-transformers"; +import testUtils, { GLOBAL } from "../test-utils"; +import { + STREAM_DELETION_POLICY, + STREAM_DELETION_REPLY_CODES, +} from "./common-stream.types"; + +describe("XACKDEL", () => { + describe("transformArguments", () => { + it("string - without policy", () => { + assert.deepEqual(parseArgs(XACKDEL, "key", "group", "0-0"), [ + "XACKDEL", + "key", + "group", + "IDS", + "1", + "0-0", + ]); + }); + + it("string - with policy", () => { + assert.deepEqual( + parseArgs( + XACKDEL, + "key", + "group", + "0-0", + STREAM_DELETION_POLICY.KEEPREF + ), + ["XACKDEL", "key", "group", "KEEPREF", "IDS", "1", "0-0"] + ); + }); + + it("array - without policy", () => { + assert.deepEqual(parseArgs(XACKDEL, "key", "group", ["0-0", "1-0"]), [ + "XACKDEL", + "key", + "group", + "IDS", + "2", + "0-0", + "1-0", + ]); + }); + + it("array - with policy", () => { + assert.deepEqual( + parseArgs( + XACKDEL, + "key", + "group", + ["0-0", "1-0"], + STREAM_DELETION_POLICY.DELREF + ), + ["XACKDEL", "key", "group", "DELREF", "IDS", "2", "0-0", "1-0"] + ); + }); + }); + + testUtils.testAll( + `XACKDEL non-existing key - without policy`, + async (client) => { + const reply = await client.xAckDel("{tag}stream-key", "testgroup", "0-0"); + assert.deepEqual(reply, [STREAM_DELETION_REPLY_CODES.NOT_FOUND]); + }, + { + client: { ...GLOBAL.SERVERS.OPEN, minimumDockerVersion: [8, 2] }, + cluster: { ...GLOBAL.CLUSTERS.OPEN, minimumDockerVersion: [8, 2] }, + } + ); + + testUtils.testAll( + `XACKDEL existing key - without policy`, + async (client) => { + const streamKey = "{tag}stream-key"; + const groupName = "testgroup"; + + // create consumer group, stream and message + await client.xGroupCreate(streamKey, groupName, "0", { MKSTREAM: true }); + const messageId = await client.xAdd(streamKey, "*", { field: "value" }); + + // read message + await client.xReadGroup(groupName, "testconsumer", { + key: streamKey, + id: ">", + }); + + const reply = await client.xAckDel(streamKey, groupName, messageId); + assert.deepEqual(reply, [STREAM_DELETION_REPLY_CODES.DELETED]); + }, + { + client: { ...GLOBAL.SERVERS.OPEN, minimumDockerVersion: [8, 2] }, + cluster: { ...GLOBAL.CLUSTERS.OPEN, minimumDockerVersion: [8, 2] }, + } + ); + + testUtils.testAll( + `XACKDEL existing key - with policy`, + async (client) => { + const streamKey = "{tag}stream-key"; + const groupName = "testgroup"; + + // create consumer group, stream and message + await client.xGroupCreate(streamKey, groupName, "0", { MKSTREAM: true }); + const messageId = await client.xAdd(streamKey, "*", { field: "value" }); + + // read message + await client.xReadGroup(groupName, "testconsumer", { + key: streamKey, + id: ">", + }); + + const reply = await client.xAckDel( + streamKey, + groupName, + messageId, + STREAM_DELETION_POLICY.DELREF + ); + assert.deepEqual(reply, [STREAM_DELETION_REPLY_CODES.DELETED]); + }, + { + client: { ...GLOBAL.SERVERS.OPEN, minimumDockerVersion: [8, 2] }, + cluster: { ...GLOBAL.CLUSTERS.OPEN, minimumDockerVersion: [8, 2] }, + } + ); + + testUtils.testAll( + `XACKDEL acknowledge policy - with consumer group`, + async (client) => { + const streamKey = "{tag}stream-key"; + const groupName = "testgroup"; + + // create consumer groups, stream and message + await client.xGroupCreate(streamKey, groupName, "0", { MKSTREAM: true }); + await client.xGroupCreate(streamKey, "some-other-group", "0"); + const messageId = await client.xAdd(streamKey, "*", { field: "value" }); + + // read message + await client.xReadGroup(groupName, "testconsumer", { + key: streamKey, + id: ">", + }); + + const reply = await client.xAckDel( + streamKey, + groupName, + messageId, + STREAM_DELETION_POLICY.ACKED + ); + assert.deepEqual(reply, [STREAM_DELETION_REPLY_CODES.DANGLING_REFS]); + }, + { + client: { ...GLOBAL.SERVERS.OPEN, minimumDockerVersion: [8, 2] }, + cluster: { ...GLOBAL.CLUSTERS.OPEN, minimumDockerVersion: [8, 2] }, + } + ); + + testUtils.testAll( + `XACKDEL multiple keys`, + async (client) => { + const streamKey = "{tag}stream-key"; + const groupName = "testgroup"; + + // create consumer groups, stream and add messages + await client.xGroupCreate(streamKey, groupName, "0", { MKSTREAM: true }); + const messageIds = await Promise.all([ + client.xAdd(streamKey, "*", { field: "value1" }), + client.xAdd(streamKey, "*", { field: "value2" }), + ]); + + // read messages + await client.xReadGroup(groupName, "testconsumer", { + key: streamKey, + id: ">", + }); + + const reply = await client.xAckDel( + streamKey, + groupName, + [...messageIds, "0-0"], + STREAM_DELETION_POLICY.DELREF + ); + assert.deepEqual(reply, [ + STREAM_DELETION_REPLY_CODES.DELETED, + STREAM_DELETION_REPLY_CODES.DELETED, + STREAM_DELETION_REPLY_CODES.NOT_FOUND, + ]); + }, + { + client: { ...GLOBAL.SERVERS.OPEN, minimumDockerVersion: [8, 2] }, + cluster: { ...GLOBAL.CLUSTERS.OPEN, minimumDockerVersion: [8, 2] }, + } + ); +}); diff --git a/packages/client/lib/commands/XACKDEL.ts b/packages/client/lib/commands/XACKDEL.ts new file mode 100644 index 00000000000..6e209879e49 --- /dev/null +++ b/packages/client/lib/commands/XACKDEL.ts @@ -0,0 +1,45 @@ +import { CommandParser } from "../client/parser"; +import { RedisArgument, ArrayReply, Command } from "../RESP/types"; +import { + StreamDeletionReplyCode, + StreamDeletionPolicy, +} from "./common-stream.types"; +import { RedisVariadicArgument } from "./generic-transformers"; + +/** + * Acknowledges and deletes one or multiple messages for a stream consumer group + */ +export default { + IS_READ_ONLY: false, + /** + * Constructs the XACKDEL command to acknowledge and delete one or multiple messages for a stream consumer group + * + * @param parser - The command parser + * @param key - The stream key + * @param group - The consumer group name + * @param id - One or more message IDs to acknowledge and delete + * @param policy - Policy to apply when deleting entries (optional, defaults to KEEPREF) + * @returns Array of integers: -1 (not found), 1 (acknowledged and deleted), 2 (acknowledged with dangling refs) + * @see https://redis.io/commands/xackdel/ + */ + parseCommand( + parser: CommandParser, + key: RedisArgument, + group: RedisArgument, + id: RedisVariadicArgument, + policy?: StreamDeletionPolicy + ) { + parser.push("XACKDEL"); + parser.pushKey(key); + parser.push(group); + + if (policy) { + parser.push(policy); + } + + parser.push("IDS"); + parser.pushVariadicWithLength(id); + }, + transformReply: + undefined as unknown as () => ArrayReply, +} as const satisfies Command; diff --git a/packages/client/lib/commands/XADD.spec.ts b/packages/client/lib/commands/XADD.spec.ts index 321581d0865..a41e8682751 100644 --- a/packages/client/lib/commands/XADD.spec.ts +++ b/packages/client/lib/commands/XADD.spec.ts @@ -2,6 +2,7 @@ import { strict as assert } from 'node:assert'; import testUtils, { GLOBAL } from '../test-utils'; import XADD from './XADD'; import { parseArgs } from './generic-transformers'; +import { STREAM_DELETION_POLICY } from './common-stream.types'; describe('XADD', () => { describe('transformArguments', () => { @@ -78,6 +79,37 @@ describe('XADD', () => { ['XADD', 'key', '1000', 'LIMIT', '1', '*', 'field', 'value'] ); }); + + it('with TRIM.policy', () => { + assert.deepEqual( + parseArgs(XADD, 'key', '*', { + field: 'value' + }, { + TRIM: { + threshold: 1000, + policy: STREAM_DELETION_POLICY.DELREF + } + }), + ['XADD', 'key', '1000', 'DELREF', '*', 'field', 'value'] + ); + }); + + it('with all TRIM options', () => { + assert.deepEqual( + parseArgs(XADD, 'key', '*', { + field: 'value' + }, { + TRIM: { + strategy: 'MAXLEN', + strategyModifier: '~', + threshold: 1000, + limit: 100, + policy: STREAM_DELETION_POLICY.ACKED + } + }), + ['XADD', 'key', 'MAXLEN', '~', '1000', 'LIMIT', '100', 'ACKED', '*', 'field', 'value'] + ); + }); }); testUtils.testAll('xAdd', async client => { @@ -91,4 +123,52 @@ describe('XADD', () => { client: GLOBAL.SERVERS.OPEN, cluster: GLOBAL.CLUSTERS.OPEN }); + + testUtils.testAll( + 'xAdd with TRIM policy', + async (client) => { + assert.equal( + typeof await client.xAdd('{tag}key', '*', + { field: 'value' }, + { + TRIM: { + strategy: 'MAXLEN', + threshold: 1000, + policy: STREAM_DELETION_POLICY.KEEPREF + } + } + ), + 'string' + ); + }, + { + client: { ...GLOBAL.SERVERS.OPEN, minimumDockerVersion: [8, 2] }, + cluster: { ...GLOBAL.CLUSTERS.OPEN, minimumDockerVersion: [8, 2] }, + } + ); + + testUtils.testAll( + 'xAdd with all TRIM options', + async (client) => { + assert.equal( + typeof await client.xAdd('{tag}key2', '*', + { field: 'value' }, + { + TRIM: { + strategy: 'MAXLEN', + strategyModifier: '~', + threshold: 1000, + limit: 10, + policy: STREAM_DELETION_POLICY.DELREF + } + } + ), + 'string' + ); + }, + { + client: { ...GLOBAL.SERVERS.OPEN, minimumDockerVersion: [8, 2] }, + cluster: { ...GLOBAL.CLUSTERS.OPEN, minimumDockerVersion: [8, 2] }, + } + ); }); diff --git a/packages/client/lib/commands/XADD.ts b/packages/client/lib/commands/XADD.ts index b0c50b1bfdb..f2509a9fa7b 100644 --- a/packages/client/lib/commands/XADD.ts +++ b/packages/client/lib/commands/XADD.ts @@ -1,5 +1,6 @@ import { CommandParser } from '../client/parser'; import { RedisArgument, BlobStringReply, Command } from '../RESP/types'; +import { StreamDeletionPolicy } from './common-stream.types'; import { Tail } from './generic-transformers'; /** @@ -10,6 +11,7 @@ import { Tail } from './generic-transformers'; * @property TRIM.strategyModifier - Exact ('=') or approximate ('~') trimming * @property TRIM.threshold - Maximum stream length or minimum ID to retain * @property TRIM.limit - Maximum number of entries to trim in one call + * @property TRIM.policy - Policy to apply when trimming entries (optional, defaults to KEEPREF) */ export interface XAddOptions { TRIM?: { @@ -17,6 +19,8 @@ export interface XAddOptions { strategyModifier?: '=' | '~'; threshold: number; limit?: number; + /** added in 8.2 */ + policy?: StreamDeletionPolicy; }; } @@ -58,6 +62,10 @@ export function parseXAddArguments( if (options.TRIM.limit) { parser.push('LIMIT', options.TRIM.limit.toString()); } + + if (options.TRIM.policy) { + parser.push(options.TRIM.policy); + } } parser.push(id); diff --git a/packages/client/lib/commands/XADD_NOMKSTREAM.spec.ts b/packages/client/lib/commands/XADD_NOMKSTREAM.spec.ts index 97927f212ff..a957d0f06c1 100644 --- a/packages/client/lib/commands/XADD_NOMKSTREAM.spec.ts +++ b/packages/client/lib/commands/XADD_NOMKSTREAM.spec.ts @@ -2,6 +2,7 @@ import { strict as assert } from 'node:assert'; import testUtils, { GLOBAL } from '../test-utils'; import XADD_NOMKSTREAM from './XADD_NOMKSTREAM'; import { parseArgs } from './generic-transformers'; +import { STREAM_DELETION_POLICY } from './common-stream.types'; describe('XADD NOMKSTREAM', () => { testUtils.isVersionGreaterThanHook([6, 2]); @@ -80,17 +81,82 @@ describe('XADD NOMKSTREAM', () => { ['XADD', 'key', 'NOMKSTREAM', '1000', 'LIMIT', '1', '*', 'field', 'value'] ); }); - }); - testUtils.testAll('xAddNoMkStream', async client => { - assert.equal( - await client.xAddNoMkStream('key', '*', { - field: 'value' - }), - null - ); - }, { - client: GLOBAL.SERVERS.OPEN, - cluster: GLOBAL.CLUSTERS.OPEN + it('with TRIM.policy', () => { + assert.deepEqual( + parseArgs(XADD_NOMKSTREAM, 'key', '*', { + field: 'value' + }, { + TRIM: { + threshold: 1000, + policy: STREAM_DELETION_POLICY.DELREF + } + }), + ['XADD', 'key', 'NOMKSTREAM', '1000', 'DELREF', '*', 'field', 'value'] + ); + }); + + it('with all TRIM options', () => { + assert.deepEqual( + parseArgs(XADD_NOMKSTREAM, 'key', '*', { + field: 'value' + }, { + TRIM: { + strategy: 'MAXLEN', + strategyModifier: '~', + threshold: 1000, + limit: 100, + policy: STREAM_DELETION_POLICY.ACKED + } + }), + ['XADD', 'key', 'NOMKSTREAM', 'MAXLEN', '~', '1000', 'LIMIT', '100', 'ACKED', '*', 'field', 'value'] + ); + }); }); + + testUtils.testAll( + 'xAddNoMkStream - null when stream does not exist', + async (client) => { + assert.equal( + await client.xAddNoMkStream('{tag}nonexistent-stream', '*', { + field: 'value' + }), + null + ); + }, + { + client: GLOBAL.SERVERS.OPEN, + cluster: GLOBAL.CLUSTERS.OPEN, + } + ); + + testUtils.testAll( + 'xAddNoMkStream - with all TRIM options', + async (client) => { + const streamKey = '{tag}stream'; + + // Create stream and add some messages + await client.xAdd(streamKey, '*', { field: 'value1' }); + + // Use NOMKSTREAM with all TRIM options + const messageId = await client.xAddNoMkStream(streamKey, '*', + { field: 'value2' }, + { + TRIM: { + strategyModifier: '~', + limit: 1, + strategy: 'MAXLEN', + threshold: 2, + policy: STREAM_DELETION_POLICY.DELREF + } + } + ); + + assert.equal(typeof messageId, 'string'); + }, + { + client: { ...GLOBAL.SERVERS.OPEN, minimumDockerVersion: [8, 2] }, + cluster: { ...GLOBAL.CLUSTERS.OPEN, minimumDockerVersion: [8, 2] }, + } + ); }); diff --git a/packages/client/lib/commands/XDELEX.spec.ts b/packages/client/lib/commands/XDELEX.spec.ts new file mode 100644 index 00000000000..8c421503256 --- /dev/null +++ b/packages/client/lib/commands/XDELEX.spec.ts @@ -0,0 +1,156 @@ +import { strict as assert } from "node:assert"; +import XDELEX from "./XDELEX"; +import { parseArgs } from "./generic-transformers"; +import testUtils, { GLOBAL } from "../test-utils"; +import { + STREAM_DELETION_POLICY, + STREAM_DELETION_REPLY_CODES, +} from "./common-stream.types"; + +describe("XDELEX", () => { + describe("transformArguments", () => { + it("string - without policy", () => { + assert.deepEqual(parseArgs(XDELEX, "key", "0-0"), [ + "XDELEX", + "key", + "IDS", + "1", + "0-0", + ]); + }); + + it("string - with policy", () => { + assert.deepEqual( + parseArgs(XDELEX, "key", "0-0", STREAM_DELETION_POLICY.KEEPREF), + ["XDELEX", "key", "KEEPREF", "IDS", "1", "0-0"] + ); + }); + + it("array - without policy", () => { + assert.deepEqual(parseArgs(XDELEX, "key", ["0-0", "1-0"]), [ + "XDELEX", + "key", + "IDS", + "2", + "0-0", + "1-0", + ]); + }); + + it("array - with policy", () => { + assert.deepEqual( + parseArgs(XDELEX, "key", ["0-0", "1-0"], STREAM_DELETION_POLICY.DELREF), + ["XDELEX", "key", "DELREF", "IDS", "2", "0-0", "1-0"] + ); + }); + }); + + testUtils.testAll( + `XDELEX non-existing key - without policy`, + async (client) => { + const reply = await client.xDelEx("{tag}stream-key", "0-0"); + assert.deepEqual(reply, [STREAM_DELETION_REPLY_CODES.NOT_FOUND]); + }, + { + client: { ...GLOBAL.SERVERS.OPEN, minimumDockerVersion: [8, 2] }, + cluster: { ...GLOBAL.CLUSTERS.OPEN, minimumDockerVersion: [8, 2] }, + } + ); + + testUtils.testAll( + `XDELEX existing key - without policy`, + async (client) => { + const streamKey = "{tag}stream-key"; + const messageId = await client.xAdd(streamKey, "*", { + field: "value", + }); + + const reply = await client.xDelEx( + streamKey, + messageId, + ); + assert.deepEqual(reply, [STREAM_DELETION_REPLY_CODES.DELETED]); + }, + { + client: { ...GLOBAL.SERVERS.OPEN, minimumDockerVersion: [8, 2] }, + cluster: { ...GLOBAL.CLUSTERS.OPEN, minimumDockerVersion: [8, 2] }, + } + ); + + testUtils.testAll( + `XDELEX existing key - with policy`, + async (client) => { + const streamKey = "{tag}stream-key"; + const messageId = await client.xAdd(streamKey, "*", { + field: "value", + }); + + const reply = await client.xDelEx( + streamKey, + messageId, + STREAM_DELETION_POLICY.DELREF + ); + assert.deepEqual(reply, [STREAM_DELETION_REPLY_CODES.DELETED]); + }, + { + client: { ...GLOBAL.SERVERS.OPEN, minimumDockerVersion: [8, 2] }, + cluster: { ...GLOBAL.CLUSTERS.OPEN, minimumDockerVersion: [8, 2] }, + } + ); + + testUtils.testAll( + `XDELEX acknowledge policy - with consumer group`, + async (client) => { + const streamKey = "{tag}stream-key"; + + // Add a message to the stream + const messageId = await client.xAdd(streamKey, "*", { + field: "value", + }); + + // Create consumer group + await client.xGroupCreate(streamKey, "testgroup", "0"); + + const reply = await client.xDelEx( + streamKey, + messageId, + STREAM_DELETION_POLICY.ACKED + ); + assert.deepEqual(reply, [STREAM_DELETION_REPLY_CODES.DANGLING_REFS]); + }, + { + client: { ...GLOBAL.SERVERS.OPEN, minimumDockerVersion: [8, 2] }, + cluster: { ...GLOBAL.CLUSTERS.OPEN, minimumDockerVersion: [8, 2] }, + } + ); + + testUtils.testAll( + `XDELEX multiple keys`, + async (client) => { + const streamKey = "{tag}stream-key"; + const messageIds = await Promise.all([ + client.xAdd(streamKey, "*", { + field: "value1", + }), + client.xAdd(streamKey, "*", { + field: "value2", + }), + ]); + + const reply = await client.xDelEx( + streamKey, + [...messageIds, "0-0"], + STREAM_DELETION_POLICY.DELREF + ); + assert.deepEqual(reply, [ + STREAM_DELETION_REPLY_CODES.DELETED, + STREAM_DELETION_REPLY_CODES.DELETED, + STREAM_DELETION_REPLY_CODES.NOT_FOUND, + ]); + }, + { + client: { ...GLOBAL.SERVERS.OPEN, minimumDockerVersion: [8, 2] }, + cluster: { ...GLOBAL.CLUSTERS.OPEN, minimumDockerVersion: [8, 2] }, + } + ); +}); diff --git a/packages/client/lib/commands/XDELEX.ts b/packages/client/lib/commands/XDELEX.ts new file mode 100644 index 00000000000..021dd0a9e13 --- /dev/null +++ b/packages/client/lib/commands/XDELEX.ts @@ -0,0 +1,42 @@ +import { CommandParser } from "../client/parser"; +import { RedisArgument, ArrayReply, Command } from "../RESP/types"; +import { + StreamDeletionPolicy, + StreamDeletionReplyCode, +} from "./common-stream.types"; +import { RedisVariadicArgument } from "./generic-transformers"; + +/** + * Deletes one or multiple entries from the stream + */ +export default { + IS_READ_ONLY: false, + /** + * Constructs the XDELEX command to delete one or multiple entries from the stream + * + * @param parser - The command parser + * @param key - The stream key + * @param id - One or more message IDs to delete + * @param policy - Policy to apply when deleting entries (optional, defaults to KEEPREF) + * @returns Array of integers: -1 (not found), 1 (deleted), 2 (dangling refs) + * @see https://redis.io/commands/xdelex/ + */ + parseCommand( + parser: CommandParser, + key: RedisArgument, + id: RedisVariadicArgument, + policy?: StreamDeletionPolicy + ) { + parser.push("XDELEX"); + parser.pushKey(key); + + if (policy) { + parser.push(policy); + } + + parser.push("IDS"); + parser.pushVariadicWithLength(id); + }, + transformReply: + undefined as unknown as () => ArrayReply, +} as const satisfies Command; diff --git a/packages/client/lib/commands/XTRIM.spec.ts b/packages/client/lib/commands/XTRIM.spec.ts index 2c31f0fef92..b88cf84676e 100644 --- a/packages/client/lib/commands/XTRIM.spec.ts +++ b/packages/client/lib/commands/XTRIM.spec.ts @@ -2,6 +2,7 @@ import { strict as assert } from 'node:assert'; import testUtils, { GLOBAL } from '../test-utils'; import XTRIM from './XTRIM'; import { parseArgs } from './generic-transformers'; +import { STREAM_DELETION_POLICY } from './common-stream.types'; describe('XTRIM', () => { describe('transformArguments', () => { @@ -12,6 +13,13 @@ describe('XTRIM', () => { ); }); + it('simple - MINID', () => { + assert.deepEqual( + parseArgs(XTRIM, 'key', 'MINID', 123), + ['XTRIM', 'key', 'MINID', '123'] + ); + }); + it('with strategyModifier', () => { assert.deepEqual( parseArgs(XTRIM, 'key', 'MAXLEN', 1, { @@ -39,15 +47,96 @@ describe('XTRIM', () => { ['XTRIM', 'key', 'MAXLEN', '=', '1', 'LIMIT', '1'] ); }); + + it('with policy', () => { + assert.deepEqual( + parseArgs(XTRIM, 'key', 'MAXLEN', 1, { + policy: STREAM_DELETION_POLICY.DELREF + }), + ['XTRIM', 'key', 'MAXLEN', '1', 'DELREF'] + ); + }); + + it('with all options', () => { + assert.deepEqual( + parseArgs(XTRIM, 'key', 'MAXLEN', 1, { + strategyModifier: '~', + LIMIT: 100, + policy: STREAM_DELETION_POLICY.ACKED + }), + ['XTRIM', 'key', 'MAXLEN', '~', '1', 'LIMIT', '100', 'ACKED'] + ); + }); + }); + + testUtils.testAll('xTrim with MAXLEN', async client => { + assert.equal( + typeof await client.xTrim('key', 'MAXLEN', 1), + 'number' + ); + }, { + client: GLOBAL.SERVERS.OPEN, + cluster: GLOBAL.CLUSTERS.OPEN, }); - testUtils.testAll('xTrim', async client => { + testUtils.testAll('xTrim with MINID', async client => { assert.equal( - await client.xTrim('key', 'MAXLEN', 1), - 0 + typeof await client.xTrim('key', 'MINID', 1), + 'number' ); }, { client: GLOBAL.SERVERS.OPEN, cluster: GLOBAL.CLUSTERS.OPEN, }); + + testUtils.testAll( + 'xTrim with LIMIT', + async (client) => { + assert.equal( + typeof await client.xTrim('{tag}key', 'MAXLEN', 1000, { + strategyModifier: '~', + LIMIT: 10 + }), + 'number' + ); + }, + { + client: GLOBAL.SERVERS.OPEN, + cluster: GLOBAL.CLUSTERS.OPEN, + } + ); + + testUtils.testAll( + 'xTrim with policy', + async (client) => { + assert.equal( + typeof await client.xTrim('{tag}key', 'MAXLEN', 0, { + policy: STREAM_DELETION_POLICY.DELREF + }), + 'number' + ); + }, + { + client: { ...GLOBAL.SERVERS.OPEN, minimumDockerVersion: [8, 2] }, + cluster: { ...GLOBAL.CLUSTERS.OPEN, minimumDockerVersion: [8, 2] }, + } + ); + + testUtils.testAll( + 'xTrim with all options', + async (client) => { + assert.equal( + typeof await client.xTrim('{tag}key', 'MINID', 0, { + strategyModifier: '~', + LIMIT: 10, + policy: STREAM_DELETION_POLICY.KEEPREF + }), + 'number' + ); + }, + { + client: { ...GLOBAL.SERVERS.OPEN, minimumDockerVersion: [8, 2] }, + cluster: { ...GLOBAL.CLUSTERS.OPEN, minimumDockerVersion: [8, 2] }, + } + ); }); diff --git a/packages/client/lib/commands/XTRIM.ts b/packages/client/lib/commands/XTRIM.ts index 6125720111a..34171d4611e 100644 --- a/packages/client/lib/commands/XTRIM.ts +++ b/packages/client/lib/commands/XTRIM.ts @@ -1,16 +1,20 @@ import { CommandParser } from '../client/parser'; import { NumberReply, Command, RedisArgument } from '../RESP/types'; +import { StreamDeletionPolicy } from './common-stream.types'; /** * Options for the XTRIM command * * @property strategyModifier - Exact ('=') or approximate ('~') trimming * @property LIMIT - Maximum number of entries to trim in one call (Redis 6.2+) + * @property policy - Policy to apply when deleting entries (optional, defaults to KEEPREF) */ export interface XTrimOptions { strategyModifier?: '=' | '~'; /** added in 6.2 */ LIMIT?: number; + /** added in 8.2 */ + policy?: StreamDeletionPolicy; } /** @@ -49,6 +53,10 @@ export default { if (options?.LIMIT) { parser.push('LIMIT', options.LIMIT.toString()); } + + if (options?.policy) { + parser.push(options.policy); + } }, transformReply: undefined as unknown as () => NumberReply } as const satisfies Command; diff --git a/packages/client/lib/commands/common-stream.types.ts b/packages/client/lib/commands/common-stream.types.ts new file mode 100644 index 00000000000..60955b6e3c3 --- /dev/null +++ b/packages/client/lib/commands/common-stream.types.ts @@ -0,0 +1,28 @@ +/** Common stream deletion policies + * + * Added in Redis 8.2 + */ +export const STREAM_DELETION_POLICY = { + /** Preserve references (default) */ + KEEPREF: "KEEPREF", + /** Delete all references */ + DELREF: "DELREF", + /** Only acknowledged entries */ + ACKED: "ACKED", +} as const; + +export type StreamDeletionPolicy = + (typeof STREAM_DELETION_POLICY)[keyof typeof STREAM_DELETION_POLICY]; + +/** Common reply codes for stream deletion operations */ +export const STREAM_DELETION_REPLY_CODES = { + /** ID not found */ + NOT_FOUND: -1, + /** Entry deleted */ + DELETED: 1, + /** Dangling references */ + DANGLING_REFS: 2, +} as const; + +export type StreamDeletionReplyCode = + (typeof STREAM_DELETION_REPLY_CODES)[keyof typeof STREAM_DELETION_REPLY_CODES]; diff --git a/packages/client/lib/commands/index.ts b/packages/client/lib/commands/index.ts index 87ab8d10b8f..4614c8b282b 100644 --- a/packages/client/lib/commands/index.ts +++ b/packages/client/lib/commands/index.ts @@ -280,6 +280,7 @@ import TYPE from './TYPE'; import UNLINK from './UNLINK'; import WAIT from './WAIT'; import XACK from './XACK'; +import XACKDEL from './XACKDEL'; import XADD_NOMKSTREAM from './XADD_NOMKSTREAM'; import XADD from './XADD'; import XAUTOCLAIM_JUSTID from './XAUTOCLAIM_JUSTID'; @@ -287,6 +288,7 @@ import XAUTOCLAIM from './XAUTOCLAIM'; import XCLAIM_JUSTID from './XCLAIM_JUSTID'; import XCLAIM from './XCLAIM'; import XDEL from './XDEL'; +import XDELEX from './XDELEX'; import XGROUP_CREATE from './XGROUP_CREATE'; import XGROUP_CREATECONSUMER from './XGROUP_CREATECONSUMER'; import XGROUP_DELCONSUMER from './XGROUP_DELCONSUMER'; @@ -924,6 +926,8 @@ export default { wait: WAIT, XACK, xAck: XACK, + XACKDEL, + xAckDel: XACKDEL, XADD_NOMKSTREAM, xAddNoMkStream: XADD_NOMKSTREAM, XADD, @@ -938,6 +942,8 @@ export default { xClaim: XCLAIM, XDEL, xDel: XDEL, + XDELEX, + xDelEx: XDELEX, XGROUP_CREATE, xGroupCreate: XGROUP_CREATE, XGROUP_CREATECONSUMER, diff --git a/packages/client/lib/sentinel/test-util.ts b/packages/client/lib/sentinel/test-util.ts index c8efa47f41d..7c4752a8852 100644 --- a/packages/client/lib/sentinel/test-util.ts +++ b/packages/client/lib/sentinel/test-util.ts @@ -174,7 +174,7 @@ export class SentinelFramework extends DockerBase { this.#testUtils = TestUtils.createFromConfig({ dockerImageName: 'redislabs/client-libs-test', dockerImageVersionArgument: 'redis-version', - defaultDockerVersion: '8.2-M01-pre' + defaultDockerVersion: '8.2-rc1' }); this.#nodeMap = new Map>>>(); this.#sentinelMap = new Map>>>(); diff --git a/packages/client/lib/test-utils.ts b/packages/client/lib/test-utils.ts index b9b906e943c..86b6ed294ac 100644 --- a/packages/client/lib/test-utils.ts +++ b/packages/client/lib/test-utils.ts @@ -9,7 +9,7 @@ import RedisBloomModules from '@redis/bloom'; const utils = TestUtils.createFromConfig({ dockerImageName: 'redislabs/client-libs-test', dockerImageVersionArgument: 'redis-version', - defaultDockerVersion: '8.2-M01-pre' + defaultDockerVersion: '8.2-rc1' }); export default utils; diff --git a/packages/entraid/lib/test-utils.ts b/packages/entraid/lib/test-utils.ts index 3c561d4ba44..e5d977a6b40 100644 --- a/packages/entraid/lib/test-utils.ts +++ b/packages/entraid/lib/test-utils.ts @@ -6,7 +6,7 @@ import { EntraidCredentialsProvider } from './entraid-credentials-provider'; export const testUtils = TestUtils.createFromConfig({ dockerImageName: 'redislabs/client-libs-test', dockerImageVersionArgument: 'redis-version', - defaultDockerVersion: '8.2-M01-pre' + defaultDockerVersion: '8.2-rc1' }); const DEBUG_MODE_ARGS = testUtils.isVersionGreaterThan([7]) ? diff --git a/packages/json/lib/test-utils.ts b/packages/json/lib/test-utils.ts index 6b6859d61bf..cba2d95e737 100644 --- a/packages/json/lib/test-utils.ts +++ b/packages/json/lib/test-utils.ts @@ -4,7 +4,7 @@ import RedisJSON from '.'; export default TestUtils.createFromConfig({ dockerImageName: 'redislabs/client-libs-test', dockerImageVersionArgument: 'redis-version', - defaultDockerVersion: '8.2-M01-pre' + defaultDockerVersion: '8.2-rc1' }); export const GLOBAL = { diff --git a/packages/search/lib/test-utils.ts b/packages/search/lib/test-utils.ts index a2b9c816da1..d4d91307b99 100644 --- a/packages/search/lib/test-utils.ts +++ b/packages/search/lib/test-utils.ts @@ -5,7 +5,7 @@ import { RespVersions } from '@redis/client'; export default TestUtils.createFromConfig({ dockerImageName: 'redislabs/client-libs-test', dockerImageVersionArgument: 'redis-version', - defaultDockerVersion: '8.2-M01-pre' + defaultDockerVersion: '8.2-rc1' }); export const GLOBAL = { diff --git a/packages/time-series/lib/test-utils.ts b/packages/time-series/lib/test-utils.ts index 8a664ee8df2..9c59918e705 100644 --- a/packages/time-series/lib/test-utils.ts +++ b/packages/time-series/lib/test-utils.ts @@ -4,7 +4,7 @@ import TimeSeries from '.'; export default TestUtils.createFromConfig({ dockerImageName: 'redislabs/client-libs-test', dockerImageVersionArgument: 'redis-version', - defaultDockerVersion: '8.2-M01-pre' + defaultDockerVersion: '8.2-rc1' }); export const GLOBAL = { From c2dc73c5d8eb1009d23645eb9f5fed4c59465711 Mon Sep 17 00:00:00 2001 From: andy-stark-redis <164213578+andy-stark-redis@users.noreply.github.com> Date: Fri, 25 Jul 2025 16:00:46 +0100 Subject: [PATCH 1664/1748] docs: DOC-5473 added time series doc examples (#3030) --- doctests/dt-time-series.js | 635 +++++++++++++++++++++++++++++++++++++ 1 file changed, 635 insertions(+) create mode 100644 doctests/dt-time-series.js diff --git a/doctests/dt-time-series.js b/doctests/dt-time-series.js new file mode 100644 index 00000000000..d2d94e7dc40 --- /dev/null +++ b/doctests/dt-time-series.js @@ -0,0 +1,635 @@ +// EXAMPLE: time_series_tutorial +// HIDE_START +import assert from 'assert'; +import { createClient } from 'redis'; +import { TIME_SERIES_AGGREGATION_TYPE, TIME_SERIES_REDUCERS } from '@redis/time-series'; + +const client = createClient(); +await client.connect(); +// HIDE_END + +// REMOVE_START +await client.del([ + 'thermometer:1', 'thermometer:2', 'thermometer:3', + 'rg:1', 'rg:2', 'rg:3', 'rg:4', + 'sensor3', + 'wind:1', 'wind:2', 'wind:3', 'wind:4', + 'hyg:1', 'hyg:compacted' +]); +// REMOVE_END + +// STEP_START create +const res1 = await client.ts.create('thermometer:1'); +console.log(res1); // >>> OK + +const res2 = await client.type('thermometer:1'); +console.log(res2); // >>> TSDB-TYPE + +const res3 = await client.ts.info('thermometer:1'); +console.log(res3); +// >>> { rules: [], ... totalSamples: 0, ... +// STEP_END +// REMOVE_START +assert.equal(res1, 'OK'); +assert.equal(res2, 'TSDB-TYPE'); +assert.equal(res3.totalSamples, 0); +// REMOVE_END + +// STEP_START create_retention +const res4 = await client.ts.add('thermometer:2', 1, 10.8, { RETENTION: 100 }); +console.log(res4); // >>> 1 + +const res5 = await client.ts.info('thermometer:2'); +console.log(res5); +// >>> { rules: [], ... retentionTime: 100, ... +// STEP_END +// REMOVE_START +assert.equal(res4, 1); +assert.equal(res5.retentionTime, 100); +// REMOVE_END + +// STEP_START create_labels +const res6 = await client.ts.add('thermometer:3', 1, 10.4, { + LABELS: { location: 'UK', type: 'Mercury' } +}); +console.log(res6); // >>> 1 + +const res7 = await client.ts.info('thermometer:3'); +console.log(res7); +// >>> { labels: [{ name: 'location', value: 'UK' }, { name: 'type', value: 'Mercury' }], ... } +// STEP_END +// REMOVE_START +assert.equal(res6, 1); +assert.deepEqual(res7.labels, [ + { name: 'location', value: 'UK' }, + { name: 'type', value: 'Mercury' }, +]); +// REMOVE_END + +// STEP_START madd +const res8 = await client.ts.mAdd([ + { key: 'thermometer:1', timestamp: 1, value: 9.2 }, + { key: 'thermometer:1', timestamp: 2, value: 9.9 }, + { key: 'thermometer:2', timestamp: 2, value: 10.3 } +]); +console.log(res8); // >>> [1, 2, 2] +// STEP_END +// REMOVE_START +assert.deepEqual(res8, [1, 2, 2]); +// REMOVE_END + +// STEP_START get +// The last recorded temperature for thermometer:2 +// was 10.3 at time 2. +const res9 = await client.ts.get('thermometer:2'); +console.log(res9); // >>> { timestamp: 2, value: 10.3 } +// STEP_END +// REMOVE_START +assert.equal(res9.timestamp, 2); +assert.equal(res9.value, 10.3); +// REMOVE_END + +// STEP_START range +// Add 5 data points to a time series named "rg:1". +const res10 = await client.ts.create('rg:1'); +console.log(res10); // >>> OK + +const res11 = await client.ts.mAdd([ + { key: 'rg:1', timestamp: 0, value: 18 }, + { key: 'rg:1', timestamp: 1, value: 14 }, + { key: 'rg:1', timestamp: 2, value: 22 }, + { key: 'rg:1', timestamp: 3, value: 18 }, + { key: 'rg:1', timestamp: 4, value: 24 } +]); +console.log(res11); // >>> [0, 1, 2, 3, 4] + +// Retrieve all the data points in ascending order. +const res12 = await client.ts.range('rg:1', '-', '+'); +console.log(res12); +// >>> [{ timestamp: 0, value: 18 }, { timestamp: 1, value: 14 }, ...] + +// Retrieve data points up to time 1 (inclusive). +const res13 = await client.ts.range('rg:1', '-', 1); +console.log(res13); +// >>> [{ timestamp: 0, value: 18 }, { timestamp: 1, value: 14 }] + +// Retrieve data points from time 3 onwards. +const res14 = await client.ts.range('rg:1', 3, '+'); +console.log(res14); +// >>> [{ timestamp: 3, value: 18 }, { timestamp: 4, value: 24 }] + +// Retrieve all the data points in descending order. +const res15 = await client.ts.revRange('rg:1', '-', '+'); +console.log(res15); +// >>> [{ timestamp: 4, value: 24 }, { timestamp: 3, value: 18 }, ...] + +// Retrieve data points up to time 1 (inclusive), but return them +// in descending order. +const res16 = await client.ts.revRange('rg:1', '-', 1); +console.log(res16); +// >>> [{ timestamp: 1, value: 14 }, { timestamp: 0, value: 18 }] +// STEP_END +// REMOVE_START +assert.equal(res10, 'OK'); +assert.deepEqual(res11, [0, 1, 2, 3, 4]); + +assert.deepEqual(res12, [ + { timestamp: 0, value: 18 }, + { timestamp: 1, value: 14 }, + { timestamp: 2, value: 22 }, + { timestamp: 3, value: 18 }, + { timestamp: 4, value: 24 } +]); +assert.deepEqual(res13, [ + { timestamp: 0, value: 18 }, + { timestamp: 1, value: 14 } +]); +assert.deepEqual(res14, [ + { timestamp: 3, value: 18 }, + { timestamp: 4, value: 24 } +]); +assert.deepEqual(res15, [ + { timestamp: 4, value: 24 }, + { timestamp: 3, value: 18 }, + { timestamp: 2, value: 22 }, + { timestamp: 1, value: 14 }, + { timestamp: 0, value: 18 } +]); +assert.deepEqual(res16, [ + { timestamp: 1, value: 14 }, + { timestamp: 0, value: 18 } +]); +// REMOVE_END + +// STEP_START range_filter +const res17 = await client.ts.range('rg:1', '-', '+', { + FILTER_BY_TS: [0, 2, 4] +}); +console.log(res17); +// >>> [{ timestamp: 0, value: 18 }, { timestamp: 2, value: 22 }, { timestamp: 4, value: 24 }] + +const res18 = await client.ts.revRange('rg:1', '-', '+', { + FILTER_BY_TS: [0, 2, 4], + FILTER_BY_VALUE: { min: 20, max: 25 } +}); +console.log(res18); +// >>> [{ timestamp: 4, value: 24 }, { timestamp: 2, value: 22 }] + +const res19 = await client.ts.revRange('rg:1', '-', '+', { + FILTER_BY_TS: [0, 2, 4], + FILTER_BY_VALUE: { min: 22, max: 22 }, + COUNT: 1 +}); +console.log(res19); +// >>> [{ timestamp: 2, value: 22 }] +// STEP_END +// REMOVE_START +assert.deepEqual(res17, [ + { timestamp: 0, value: 18 }, + { timestamp: 2, value: 22 }, + { timestamp: 4, value: 24 } +]); +assert.deepEqual(res18, [ + { timestamp: 4, value: 24 }, + { timestamp: 2, value: 22 } +]); +assert.deepEqual(res19, [ + { timestamp: 2, value: 22 } +]); +// REMOVE_END + +// STEP_START query_multi +// Create three new "rg:" time series (two in the US +// and one in the UK, with different units) and add some +// data points. +const res20 = await client.ts.create('rg:2', { + LABELS: { location: 'us', unit: 'cm' } +}); +console.log(res20); // >>> OK + +const res21 = await client.ts.create('rg:3', { + LABELS: { location: 'us', unit: 'in' } +}); +console.log(res21); // >>> OK + +const res22 = await client.ts.create('rg:4', { + LABELS: { location: 'uk', unit: 'mm' } +}); +console.log(res22); // >>> OK + +const res23 = await client.ts.mAdd([ + { key: 'rg:2', timestamp: 0, value: 1.8 }, + { key: 'rg:3', timestamp: 0, value: 0.9 }, + { key: 'rg:4', timestamp: 0, value: 25 } +]); +console.log(res23); // >>> [0, 0, 0] + +const res24 = await client.ts.mAdd([ + { key: 'rg:2', timestamp: 1, value: 2.1 }, + { key: 'rg:3', timestamp: 1, value: 0.77 }, + { key: 'rg:4', timestamp: 1, value: 18 } +]); +console.log(res24); // >>> [1, 1, 1] + +const res25 = await client.ts.mAdd([ + { key: 'rg:2', timestamp: 2, value: 2.3 }, + { key: 'rg:3', timestamp: 2, value: 1.1 }, + { key: 'rg:4', timestamp: 2, value: 21 } +]); +console.log(res25); // >>> [2, 2, 2] + +const res26 = await client.ts.mAdd([ + { key: 'rg:2', timestamp: 3, value: 1.9 }, + { key: 'rg:3', timestamp: 3, value: 0.81 }, + { key: 'rg:4', timestamp: 3, value: 19 } +]); +console.log(res26); // >>> [3, 3, 3] + +const res27 = await client.ts.mAdd([ + { key: 'rg:2', timestamp: 4, value: 1.78 }, + { key: 'rg:3', timestamp: 4, value: 0.74 }, + { key: 'rg:4', timestamp: 4, value: 23 } +]); +console.log(res27); // >>> [4, 4, 4] + +// Retrieve the last data point from each US time series. +const res28 = await client.ts.mGet(['location=us']); +console.log(res28); +// >>> { "rg:2": { sample: { timestamp: 4, value: 1.78 } }, "rg:3": { sample: { timestamp: 4, value: 0.74 } } } + +// Retrieve the same data points, but include the `unit` +// label in the results. +const res29 = await client.ts.mGetSelectedLabels(['location=us'], ['unit']); +console.log(res29); +// >>> { "rg:2": { labels: { unit: 'cm' }, sample: { timestamp: 4, value: 1.78 } }, "rg:3": { labels: { unit: 'in' }, sample: { timestamp: 4, value: 0.74 } } } + +// Retrieve data points up to time 2 (inclusive) from all +// time series that use millimeters as the unit. Include all +// labels in the results. +const res30 = await client.ts.mRangeWithLabels('-', 2, 'unit=mm'); +console.log(res30); +// >>> { "rg:4": { labels: { location: 'uk', unit: 'mm' }, samples: [ +// { timestamp: 0, value: 25 }, +// { timestamp: 1, value: 18 }, +// { timestamp: 2, value: 21 } +// ] } } + +// Retrieve data points from time 1 to time 3 (inclusive) from +// all time series that use centimeters or millimeters as the unit, +// but only return the `location` label. Return the results +// in descending order of timestamp. +const res31 = await client.ts.mRevRangeSelectedLabels( + 1, 3, + ['location'], + ['unit=(cm,mm)'] +); +console.log(res31); +// >>> { "rg:2": { labels: { location: 'us' }, samples: [ +// { timestamp: 3, value: 1.9 }, +// { timestamp: 2, value: 2.3 }, +// { timestamp: 1, value: 2.1 } +// ] }, "rg:4": { labels: { location: 'uk' }, samples: [ +// { timestamp: 3, value: 19 }, +// { timestamp: 2, value: 21 }, +// { timestamp: 1, value: 18 } +// ] } } +// STEP_END +// REMOVE_START +assert.equal(res20, 'OK'); +assert.equal(res21, 'OK'); +assert.equal(res22, 'OK'); +assert.deepEqual(res23, [0, 0, 0]); +assert.deepEqual(res24, [1, 1, 1]); +assert.deepEqual(res25, [2, 2, 2]); +assert.deepEqual(res26, [3, 3, 3]); +assert.deepEqual(res27, [4, 4, 4]); + +assert.deepEqual(res28, { + "rg:2": { sample: { timestamp: 4, value: 1.78 } }, + "rg:3": { sample: { timestamp: 4, value: 0.74 } } +}); +assert.deepEqual(res29, { + "rg:2": { labels: { unit: 'cm' }, sample: { timestamp: 4, value: 1.78 } }, + "rg:3": { labels: { unit: 'in' }, sample: { timestamp: 4, value: 0.74 } } +}); + +assert.deepEqual(res30, { + "rg:4": { + labels: { location: 'uk', unit: 'mm' }, + samples: [ + { timestamp: 0, value: 25 }, + { timestamp: 1, value: 18 }, + { timestamp: 2, value: 21 } + ] + } +}); +assert.deepEqual(res31, { + "rg:2": { + labels: { location: 'us' }, + samples: [ + { timestamp: 3, value: 1.9 }, + { timestamp: 2, value: 2.3 }, + { timestamp: 1, value: 2.1 } + ] + }, + "rg:4": { + labels: { location: 'uk' }, + samples: [ + { timestamp: 3, value: 19 }, + { timestamp: 2, value: 21 }, + { timestamp: 1, value: 18 } + ] + } +}); +// REMOVE_END + +// STEP_START agg +const res32 = await client.ts.range('rg:2', '-', '+', { + AGGREGATION: { + type: TIME_SERIES_AGGREGATION_TYPE.AVG, + timeBucket: 2 + } +}); +console.log(res32); +// >>> [{ timestamp: 0, value: 1.9500000000000002 },{ timestamp: 2, value: 2.0999999999999996 }, { timestamp: 4, value: 1.78 }] +// STEP_END +// REMOVE_START +assert.deepEqual(res32, [ + { timestamp: 0, value: 1.9500000000000002 }, + { timestamp: 2, value: 2.0999999999999996 }, + { timestamp: 4, value: 1.78 } +]); +// REMOVE_END + +// STEP_START agg_bucket +const res33 = await client.ts.create('sensor3'); +console.log(res33); // >>> OK + +const res34 = await client.ts.mAdd([ + { key: 'sensor3', timestamp: 10, value: 1000 }, + { key: 'sensor3', timestamp: 20, value: 2000 }, + { key: 'sensor3', timestamp: 30, value: 3000 }, + { key: 'sensor3', timestamp: 40, value: 4000 }, + { key: 'sensor3', timestamp: 50, value: 5000 }, + { key: 'sensor3', timestamp: 60, value: 6000 }, + { key: 'sensor3', timestamp: 70, value: 7000 } +]); +console.log(res34); // >>> [10, 20, 30, 40, 50, 60, 70] + +const res35 = await client.ts.range('sensor3', 10, 70, { + AGGREGATION: { + type: TIME_SERIES_AGGREGATION_TYPE.MIN, + timeBucket: 25 + } +}); +console.log(res35); +// >>> [{ timestamp: 0, value: 1000 }, { timestamp: 25, value: 3000 }, { timestamp: 50, value: 5000 }] +// STEP_END +// REMOVE_START +assert.equal(res33, 'OK'); +assert.deepEqual(res34, [10, 20, 30, 40, 50, 60, 70]); +assert.deepEqual(res35, [ + { timestamp: 0, value: 1000 }, + { timestamp: 25, value: 3000 }, + { timestamp: 50, value: 5000 } +]); +// REMOVE_END + +// STEP_START agg_align +const res36 = await client.ts.range('sensor3', 10, 70, { + AGGREGATION: { + type: TIME_SERIES_AGGREGATION_TYPE.MIN, + timeBucket: 25 + }, + ALIGN: 'START' +}); +console.log(res36); +// >>> [{ timestamp: 10, value: 1000 }, { timestamp: 35, value: 4000 }, { timestamp: 60, value: 6000 }] +// STEP_END +// REMOVE_START +assert.deepEqual(res36, [ + { timestamp: 10, value: 1000 }, + { timestamp: 35, value: 4000 }, + { timestamp: 60, value: 6000 } +]); +// REMOVE_END + +// STEP_START agg_multi +const res37 = await client.ts.create('wind:1', { + LABELS: { country: 'uk' } +}); +console.log(res37); // >>> OK + +const res38 = await client.ts.create('wind:2', { + LABELS: { country: 'uk' } +}); +console.log(res38); // >>> OK + +const res39 = await client.ts.create('wind:3', { + LABELS: { country: 'us' } +}); +console.log(res39); // >>> OK + +const res40 = await client.ts.create('wind:4', { + LABELS: { country: 'us' } +}); +console.log(res40); // >>> OK + +const res41 = await client.ts.mAdd([ + { key: 'wind:1', timestamp: 1, value: 12 }, + { key: 'wind:2', timestamp: 1, value: 18 }, + { key: 'wind:3', timestamp: 1, value: 5 }, + { key: 'wind:4', timestamp: 1, value: 20 } +]); +console.log(res41); // >>> [1, 1, 1, 1] + +const res42 = await client.ts.mAdd([ + { key: 'wind:1', timestamp: 2, value: 14 }, + { key: 'wind:2', timestamp: 2, value: 21 }, + { key: 'wind:3', timestamp: 2, value: 4 }, + { key: 'wind:4', timestamp: 2, value: 25 } +]); +console.log(res42); // >>> [2, 2, 2, 2] + +const res43 = await client.ts.mAdd([ + { key: 'wind:1', timestamp: 3, value: 10 }, + { key: 'wind:2', timestamp: 3, value: 24 }, + { key: 'wind:3', timestamp: 3, value: 8 }, + { key: 'wind:4', timestamp: 3, value: 18 } +]); +console.log(res43); // >>> [3, 3, 3, 3] + +// The result pairs contain the timestamp and the maximum sample value +// for the country at that timestamp. +const res44 = await client.ts.mRangeGroupBy( + '-', '+', ['country=(us,uk)'], + {label: 'country', REDUCE: TIME_SERIES_REDUCERS.MAX} +); +console.log(res44); +// >>> { "country=uk": { samples: [ +// { timestamp: 1, value: 18 }, +// { timestamp: 2, value: 21 }, +// { timestamp: 3, value: 24 } +// ] }, "country=us": { samples: [ +// { timestamp: 1, value: 20 }, +// { timestamp: 2, value: 25 }, +// { timestamp: 3, value: 18 } +// ] } } + +// The result pairs contain the timestamp and the average sample value +// for the country at that timestamp. +const res45 = await client.ts.mRangeGroupBy( + '-', '+', ['country=(us,uk)'], + { label: 'country', REDUCE: TIME_SERIES_REDUCERS.AVG} +); +console.log(res45); +// >>> { +// "country=uk": { +// samples: [{ timestamp: 1, value: 15 }, { timestamp: 2, value: 17.5 }, { timestamp: 3, value: 17 }] +// }, +// "country=us": { +// samples: [{ timestamp: 1, value: 12.5 }, { timestamp: 2, value: 14.5 }, { timestamp: 3, value: 13 }] +// } +// } +// STEP_END +// REMOVE_START +assert.equal(res37, 'OK'); +assert.equal(res38, 'OK'); +assert.equal(res39, 'OK'); +assert.equal(res40, 'OK'); +assert.deepEqual(res41, [1, 1, 1, 1]); +assert.deepEqual(res42, [2, 2, 2, 2]); +assert.deepEqual(res43, [3, 3, 3, 3]); + +assert.deepEqual(res44, { + "country=uk": { + samples: [ + { timestamp: 1, value: 18 }, + { timestamp: 2, value: 21 }, + { timestamp: 3, value: 24 } + ] + }, + "country=us": { + samples: [ + { timestamp: 1, value: 20 }, + { timestamp: 2, value: 25 }, + { timestamp: 3, value: 18 } + ] + } +}); +assert.deepEqual(res45, { + "country=uk": { + samples: [ + { timestamp: 1, value: 15 }, + { timestamp: 2, value: 17.5 }, + { timestamp: 3, value: 17 } + ] + }, + "country=us": { + samples: [ + { timestamp: 1, value: 12.5 }, + { timestamp: 2, value: 14.5 }, + { timestamp: 3, value: 13 } + ] + } +}); +// REMOVE_END + +// STEP_START create_compaction +const res46 = await client.ts.create('hyg:1'); +console.log(res46); // >>> OK + +const res47 = await client.ts.create('hyg:compacted'); +console.log(res47); // >>> OK + +const res48 = await client.ts.createRule('hyg:1', 'hyg:compacted', TIME_SERIES_AGGREGATION_TYPE.MIN, 3); +console.log(res48); // >>> OK + +const res49 = await client.ts.info('hyg:1'); +console.log(res49.rules); +// >>> [{ aggregationType: 'MIN', key: 'hyg:compacted', timeBucket: 3}] + +const res50 = await client.ts.info('hyg:compacted'); +console.log(res50.sourceKey); // >>> 'hyg:1' +// STEP_END +// REMOVE_START +assert.equal(res46, 'OK'); +assert.equal(res47, 'OK'); +assert.equal(res48, 'OK'); +assert.deepEqual(res49.rules, [ + { aggregationType: 'MIN', key: 'hyg:compacted', timeBucket: 3} +]); +assert.equal(res50.sourceKey, 'hyg:1'); +// REMOVE_END + +// STEP_START comp_add +const res51 = await client.ts.mAdd([ + { key: 'hyg:1', timestamp: 0, value: 75 }, + { key: 'hyg:1', timestamp: 1, value: 77 }, + { key: 'hyg:1', timestamp: 2, value: 78 } +]); +console.log(res51); // >>> [0, 1, 2] + +const res52 = await client.ts.range('hyg:compacted', '-', '+'); +console.log(res52); // >>> [] + +const res53 = await client.ts.add('hyg:1', 3, 79); +console.log(res53); // >>> 3 + +const res54 = await client.ts.range('hyg:compacted', '-', '+'); +console.log(res54); // >>> [{ timestamp: 0, value: 75 }] +// STEP_END +// REMOVE_START +assert.deepEqual(res51, [0, 1, 2]); +assert.deepEqual(res52, []); +assert.equal(res53, 3); +assert.deepEqual(res54, [{ timestamp: 0, value: 75 }]); +// REMOVE_END + +// STEP_START del +const res55 = await client.ts.info('thermometer:1'); +console.log(res55.totalSamples); // >>> 2 +console.log(res55.firstTimestamp); // >>> 1 +console.log(res55.lastTimestamp); // >>> 2 + +const res56 = await client.ts.add('thermometer:1', 3, 9.7); +console.log(res56); // >>> 3 + +const res57 = await client.ts.info('thermometer:1'); +console.log(res57.totalSamples); // >>> 3 +console.log(res57.firstTimestamp); // >>> 1 +console.log(res57.lastTimestamp); // >>> 3 + +const res58 = await client.ts.del('thermometer:1', 1, 2); +console.log(res58); // >>> 2 + +const res59 = await client.ts.info('thermometer:1'); +console.log(res59.totalSamples); // >>> 1 +console.log(res59.firstTimestamp); // >>> 3 +console.log(res59.lastTimestamp); // >>> 3 + +const res60 = await client.ts.del('thermometer:1', 3, 3); +console.log(res60); // >>> 1 + +const res61 = await client.ts.info('thermometer:1'); +console.log(res61.totalSamples); // >>> 0 +// STEP_END +// REMOVE_START +assert.equal(res55.totalSamples, 2); +assert.equal(res55.firstTimestamp, 1); +assert.equal(res55.lastTimestamp, 2); +assert.equal(res56, 3); +assert.equal(res57.totalSamples, 3); +assert.equal(res57.firstTimestamp, 1); +assert.equal(res57.lastTimestamp, 3); +assert.equal(res58, 2); +assert.equal(res59.totalSamples, 1); +assert.equal(res59.firstTimestamp, 3); +assert.equal(res59.lastTimestamp, 3); +assert.equal(res60, 1); +assert.equal(res61.totalSamples, 0); +// REMOVE_END + +// HIDE_START +await client.quit(); +// HIDE_END \ No newline at end of file From 28d719d699b41023fa63ba616cc6931d927d5132 Mon Sep 17 00:00:00 2001 From: andy-stark-redis <164213578+andy-stark-redis@users.noreply.github.com> Date: Fri, 25 Jul 2025 16:01:05 +0100 Subject: [PATCH 1665/1748] docs: DOC-5074 added vector set doc examples (#3031) --- doctests/dt-vec-set.js | 281 +++++++++++++++++++++++++++++++++++++++++ 1 file changed, 281 insertions(+) create mode 100644 doctests/dt-vec-set.js diff --git a/doctests/dt-vec-set.js b/doctests/dt-vec-set.js new file mode 100644 index 00000000000..0e8cb918d7e --- /dev/null +++ b/doctests/dt-vec-set.js @@ -0,0 +1,281 @@ +// EXAMPLE: vecset_tutorial +// REMOVE_START +/** + * Code samples for Vector set doc pages: + * https://redis.io/docs/latest/develop/data-types/vector-sets/ + */ + +import assert from 'assert'; +// REMOVE_END +// HIDE_START +import { createClient } from 'redis'; + +const client = createClient({ + RESP: 3 // Required for vector set commands +}); + +await client.connect(); +// HIDE_END + +// REMOVE_START +await client.del([ + "points", "quantSetQ8", "quantSetNoQ", + "quantSetBin", "setNotReduced", "setReduced" +]); +// REMOVE_END + +// STEP_START vadd +const res1 = await client.vAdd("points", [1.0, 1.0], "pt:A"); +console.log(res1); // >>> true + +const res2 = await client.vAdd("points", [-1.0, -1.0], "pt:B"); +console.log(res2); // >>> true + +const res3 = await client.vAdd("points", [-1.0, 1.0], "pt:C"); +console.log(res3); // >>> true + +const res4 = await client.vAdd("points", [1.0, -1.0], "pt:D"); +console.log(res4); // >>> true + +const res5 = await client.vAdd("points", [1.0, 0], "pt:E"); +console.log(res5); // >>> true + +const res6 = await client.type("points"); +console.log(res6); // >>> vectorset +// STEP_END +// REMOVE_START +assert.equal(res1, true); +assert.equal(res2, true); +assert.equal(res3, true); +assert.equal(res4, true); +assert.equal(res5, true); +assert.equal(res6, "vectorset"); +// REMOVE_END + +// STEP_START vcardvdim +const res7 = await client.vCard("points"); +console.log(res7); // >>> 5 + +const res8 = await client.vDim("points"); +console.log(res8); // >>> 2 +// STEP_END +// REMOVE_START +assert.equal(res7, 5); +assert.equal(res8, 2); +// REMOVE_END + +// STEP_START vemb +const res9 = await client.vEmb("points", "pt:A"); +console.log(res9); // >>> [0.9999999403953552, 0.9999999403953552] + +const res10 = await client.vEmb("points", "pt:B"); +console.log(res10); // >>> [-0.9999999403953552, -0.9999999403953552] + +const res11 = await client.vEmb("points", "pt:C"); +console.log(res11); // >>> [-0.9999999403953552, 0.9999999403953552] + +const res12 = await client.vEmb("points", "pt:D"); +console.log(res12); // >>> [0.9999999403953552, -0.9999999403953552] + +const res13 = await client.vEmb("points", "pt:E"); +console.log(res13); // >>> [1, 0] +// STEP_END +// REMOVE_START +assert(Math.abs(1 - res9[0]) < 0.001); +assert(Math.abs(1 - res9[1]) < 0.001); +assert(Math.abs(1 + res10[0]) < 0.001); +assert(Math.abs(1 + res10[1]) < 0.001); +assert(Math.abs(1 + res11[0]) < 0.001); +assert(Math.abs(1 - res11[1]) < 0.001); +assert(Math.abs(1 - res12[0]) < 0.001); +assert(Math.abs(1 + res12[1]) < 0.001); +assert.deepEqual(res13, [1, 0]); +// REMOVE_END + +// STEP_START attr +const res14 = await client.vSetAttr("points", "pt:A", { + name: "Point A", + description: "First point added" +}); +console.log(res14); // >>> true + +const res15 = await client.vGetAttr("points", "pt:A"); +console.log(res15); +// >>> {name: 'Point A', description: 'First point added'} + +const res16 = await client.vSetAttr("points", "pt:A", ""); +console.log(res16); // >>> true + +const res17 = await client.vGetAttr("points", "pt:A"); +console.log(res17); // >>> null +// STEP_END +// REMOVE_START +assert.equal(res14, true); +assert.deepEqual(res15, {name: "Point A", description: "First point added"}); +assert.equal(res16, true); +assert.equal(res17, null); +// REMOVE_END + +// STEP_START vrem +const res18 = await client.vAdd("points", [0, 0], "pt:F"); +console.log(res18); // >>> true + +const res19 = await client.vCard("points"); +console.log(res19); // >>> 6 + +const res20 = await client.vRem("points", "pt:F"); +console.log(res20); // >>> true + +const res21 = await client.vCard("points"); +console.log(res21); // >>> 5 +// STEP_END +// REMOVE_START +assert.equal(res18, true); +assert.equal(res19, 6); +assert.equal(res20, true); +assert.equal(res21, 5); +// REMOVE_END + +// STEP_START vsim_basic +const res22 = await client.vSim("points", [0.9, 0.1]); +console.log(res22); +// >>> ['pt:E', 'pt:A', 'pt:D', 'pt:C', 'pt:B'] +// STEP_END +// REMOVE_START +assert.deepEqual(res22, ["pt:E", "pt:A", "pt:D", "pt:C", "pt:B"]); +// REMOVE_END + +// STEP_START vsim_options +const res23 = await client.vSimWithScores("points", "pt:A", { COUNT: 4 }); +console.log(res23); +// >>> {pt:A: 1.0, pt:E: 0.8535534143447876, pt:D: 0.5, pt:C: 0.5} +// STEP_END +// REMOVE_START +assert.equal(res23["pt:A"], 1.0); +assert.equal(res23["pt:C"], 0.5); +assert.equal(res23["pt:D"], 0.5); +assert(Math.abs(res23["pt:E"] - 0.85) < 0.005); +// REMOVE_END + +// STEP_START vsim_filter +const res24 = await client.vSetAttr("points", "pt:A", { + size: "large", + price: 18.99 +}); +console.log(res24); // >>> true + +const res25 = await client.vSetAttr("points", "pt:B", { + size: "large", + price: 35.99 +}); +console.log(res25); // >>> true + +const res26 = await client.vSetAttr("points", "pt:C", { + size: "large", + price: 25.99 +}); +console.log(res26); // >>> true + +const res27 = await client.vSetAttr("points", "pt:D", { + size: "small", + price: 21.00 +}); +console.log(res27); // >>> true + +const res28 = await client.vSetAttr("points", "pt:E", { + size: "small", + price: 17.75 +}); +console.log(res28); // >>> true + +// Return elements in order of distance from point A whose +// `size` attribute is `large`. +const res29 = await client.vSim("points", "pt:A", { + FILTER: '.size == "large"' +}); +console.log(res29); // >>> ['pt:A', 'pt:C', 'pt:B'] + +// Return elements in order of distance from point A whose size is +// `large` and whose price is greater than 20.00. +const res30 = await client.vSim("points", "pt:A", { + FILTER: '.size == "large" && .price > 20.00' +}); +console.log(res30); // >>> ['pt:C', 'pt:B'] +// STEP_END +// REMOVE_START +assert.equal(res24, true); +assert.equal(res25, true); +assert.equal(res26, true); +assert.equal(res27, true); +assert.equal(res28, true); +assert.deepEqual(res29, ['pt:A', 'pt:C', 'pt:B']); +assert.deepEqual(res30, ['pt:C', 'pt:B']); +// REMOVE_END + +// STEP_START add_quant +const res31 = await client.vAdd("quantSetQ8", [1.262185, 1.958231], "quantElement", { + QUANT: 'Q8' +}); +console.log(res31); // >>> true + +const res32 = await client.vEmb("quantSetQ8", "quantElement"); +console.log(`Q8: ${res32}`); +// >>> Q8: [1.2643694877624512, 1.958230972290039] + +const res33 = await client.vAdd("quantSetNoQ", [1.262185, 1.958231], "quantElement", { + QUANT: 'NOQUANT' +}); +console.log(res33); // >>> true + +const res34 = await client.vEmb("quantSetNoQ", "quantElement"); +console.log(`NOQUANT: ${res34}`); +// >>> NOQUANT: [1.262184977531433, 1.958230972290039] + +const res35 = await client.vAdd("quantSetBin", [1.262185, 1.958231], "quantElement", { + QUANT: 'BIN' +}); +console.log(res35); // >>> true + +const res36 = await client.vEmb("quantSetBin", "quantElement"); +console.log(`BIN: ${res36}`); +// >>> BIN: [1, 1] +// STEP_END +// REMOVE_START +assert.equal(res31, true); +assert(Math.abs(res32[0] - 1.2643694877624512) < 0.001); +assert(Math.abs(res32[1] - 1.958230972290039) < 0.001); +assert.equal(res33, true); +assert(Math.abs(res34[0] - 1.262184977531433) < 0.001); +assert(Math.abs(res34[1] - 1.958230972290039) < 0.001); +assert.equal(res35, true); +assert.deepEqual(res36, [1, 1]); +// REMOVE_END + +// STEP_START add_reduce +// Create a list of 300 arbitrary values. +const values = Array.from({length: 300}, (_, x) => x / 299); + +const res37 = await client.vAdd("setNotReduced", values, "element"); +console.log(res37); // >>> true + +const res38 = await client.vDim("setNotReduced"); +console.log(res38); // >>> 300 + +const res39 = await client.vAdd("setReduced", values, "element", { + REDUCE: 100 +}); +console.log(res39); // >>> true + +const res40 = await client.vDim("setReduced"); +console.log(res40); // >>> 100 +// STEP_END +// REMOVE_START +assert.equal(res37, true); +assert.equal(res38, 300); +assert.equal(res39, true); +assert.equal(res40, 100); +// REMOVE_END + +// HIDE_START +await client.quit(); +// HIDE_END From d8e14fa4febe69cfee942a477f99dd2492e8ff18 Mon Sep 17 00:00:00 2001 From: Nikolay Karadzhov Date: Mon, 28 Jul 2025 14:54:50 +0300 Subject: [PATCH 1666/1748] fix(scan): remove console.logs (#3038) fixes #3037 --- packages/client/lib/commands/SCAN.ts | 2 -- 1 file changed, 2 deletions(-) diff --git a/packages/client/lib/commands/SCAN.ts b/packages/client/lib/commands/SCAN.ts index 173e8959afa..d3153b786f1 100644 --- a/packages/client/lib/commands/SCAN.ts +++ b/packages/client/lib/commands/SCAN.ts @@ -87,7 +87,6 @@ export default { if (options?.TYPE) { parser.push('TYPE', options.TYPE); } - console.log('eeeeeeeeee', parser.redisArgs) }, /** * Transforms the SCAN reply into a structured object @@ -96,7 +95,6 @@ export default { * @returns Object with cursor and keys properties */ transformReply([cursor, keys]: [BlobStringReply, ArrayReply]) { - console.log(cursor, keys) return { cursor, keys From 5f09e4a8a58cfaa093a9860724f812c1c19e0d33 Mon Sep 17 00:00:00 2001 From: Pavel Pashov <60297174+PavelPashov@users.noreply.github.com> Date: Thu, 31 Jul 2025 15:11:51 +0300 Subject: [PATCH 1667/1748] feat: add EPSILON parameter support to VSIM command (#3041) --- packages/client/lib/commands/VSIM.spec.ts | 30 ++++++++++++++++++++--- packages/client/lib/commands/VSIM.ts | 5 ++++ 2 files changed, 31 insertions(+), 4 deletions(-) diff --git a/packages/client/lib/commands/VSIM.spec.ts b/packages/client/lib/commands/VSIM.spec.ts index b7e10eb6c49..dbfad76fd59 100644 --- a/packages/client/lib/commands/VSIM.spec.ts +++ b/packages/client/lib/commands/VSIM.spec.ts @@ -31,14 +31,15 @@ describe('VSIM', () => { FILTER: '.price > 20', 'FILTER-EF': 50, TRUTH: true, - NOTHREAD: true + NOTHREAD: true, + EPSILON: 0.1 }); assert.deepEqual( parser.redisArgs, [ - 'VSIM', 'key', 'ELE', 'element', - 'COUNT', '5', 'EF', '100', 'FILTER', '.price > 20', - 'FILTER-EF', '50', 'TRUTH', 'NOTHREAD' + 'VSIM', 'key', 'ELE', 'element', 'COUNT', '5', + 'EPSILON', '0.1', 'EF', '100', 'FILTER', '.price > 20', + 'FILTER-EF', '50', 'TRUTH', 'NOTHREAD', ] ); }); @@ -56,6 +57,27 @@ describe('VSIM', () => { cluster: { ...GLOBAL.CLUSTERS.OPEN, minimumDockerVersion: [8, 0] } }); + + testUtils.testAll('vSim with options', async client => { + await client.vAdd('key', [1.0, 2.0, 3.0], 'element1'); + await client.vAdd('key', [1.1, 2.1, 3.1], 'element2'); + + const result = await client.vSim('key', 'element1', { + EPSILON: 0.1, + COUNT: 1, + EF: 100, + FILTER: '.year == 8', + 'FILTER-EF': 50, + TRUTH: true, + NOTHREAD: true + }); + + assert.ok(Array.isArray(result)); + }, { + client: { ...GLOBAL.SERVERS.OPEN, minimumDockerVersion: [8, 0] }, + cluster: { ...GLOBAL.CLUSTERS.OPEN, minimumDockerVersion: [8, 0] } + }); + testUtils.testWithClient('vSim with RESP3', async client => { await client.vAdd('resp3-key', [1.0, 2.0, 3.0], 'element1'); await client.vAdd('resp3-key', [1.1, 2.1, 3.1], 'element2'); diff --git a/packages/client/lib/commands/VSIM.ts b/packages/client/lib/commands/VSIM.ts index dc41a54caf1..7c94cd7c79d 100644 --- a/packages/client/lib/commands/VSIM.ts +++ b/packages/client/lib/commands/VSIM.ts @@ -4,6 +4,7 @@ import { transformDoubleArgument } from './generic-transformers'; export interface VSimOptions { COUNT?: number; + EPSILON?: number; EF?: number; FILTER?: string; 'FILTER-EF'?: number; @@ -44,6 +45,10 @@ export default { parser.push('COUNT', options.COUNT.toString()); } + if (options?.EPSILON !== undefined) { + parser.push('EPSILON', options.EPSILON.toString()); + } + if (options?.EF !== undefined) { parser.push('EF', options.EF.toString()); } From facc91847e5605fbe8ecaa8dd44a8b913c495e57 Mon Sep 17 00:00:00 2001 From: GitHub Action Date: Thu, 31 Jul 2025 13:39:03 +0000 Subject: [PATCH 1668/1748] Release client@5.7.0 --- package-lock.json | 14 +++++++++++++- packages/client/package.json | 2 +- 2 files changed, 14 insertions(+), 2 deletions(-) diff --git a/package-lock.json b/package-lock.json index 71e0ce79df8..640861530b2 100644 --- a/package-lock.json +++ b/package-lock.json @@ -7351,7 +7351,7 @@ }, "packages/client": { "name": "@redis/client", - "version": "5.6.1", + "version": "5.7.0", "license": "MIT", "dependencies": { "cluster-key-slot": "1.1.2" @@ -7449,6 +7449,18 @@ "node": ">= 18" } }, + "packages/redis/node_modules/@redis/client": { + "version": "5.6.1", + "resolved": "https://registry.npmjs.org/@redis/client/-/client-5.6.1.tgz", + "integrity": "sha512-bWHmSFIJ5w1Y4aHsYs46XMDHKQsBHFRhNcllYaBxz2Zl+lu+gbm5yI9BqxvKh48bLTs/Wx1Kns0gN2WIasE8MA==", + "license": "MIT", + "dependencies": { + "cluster-key-slot": "1.1.2" + }, + "engines": { + "node": ">= 18" + } + }, "packages/search": { "name": "@redis/search", "version": "5.6.1", diff --git a/packages/client/package.json b/packages/client/package.json index 9fe67df521e..091ddaf5c91 100644 --- a/packages/client/package.json +++ b/packages/client/package.json @@ -1,6 +1,6 @@ { "name": "@redis/client", - "version": "5.6.1", + "version": "5.7.0", "license": "MIT", "main": "./dist/index.js", "types": "./dist/index.d.ts", From 69fa90784344b3e5e7b26b2925effbe288f974a4 Mon Sep 17 00:00:00 2001 From: GitHub Action Date: Thu, 31 Jul 2025 13:39:11 +0000 Subject: [PATCH 1669/1748] Release bloom@5.7.0 --- package-lock.json | 16 ++++++++++++++-- packages/bloom/package.json | 4 ++-- 2 files changed, 16 insertions(+), 4 deletions(-) diff --git a/package-lock.json b/package-lock.json index 640861530b2..79a9493d3c8 100644 --- a/package-lock.json +++ b/package-lock.json @@ -7337,7 +7337,7 @@ }, "packages/bloom": { "name": "@redis/bloom", - "version": "5.6.1", + "version": "5.7.0", "license": "MIT", "devDependencies": { "@redis/test-utils": "*" @@ -7346,7 +7346,7 @@ "node": ">= 18" }, "peerDependencies": { - "@redis/client": "^5.6.1" + "@redis/client": "^5.7.0" } }, "packages/client": { @@ -7449,6 +7449,18 @@ "node": ">= 18" } }, + "packages/redis/node_modules/@redis/bloom": { + "version": "5.6.1", + "resolved": "https://registry.npmjs.org/@redis/bloom/-/bloom-5.6.1.tgz", + "integrity": "sha512-5/22U76IMEfn6TeZ+uvjXspHw+ykBF0kpBa8xouzeHaQMXs/auqBUOEYzU2VKYDvnd2RSpPTyIg82oB7PpUgLg==", + "license": "MIT", + "engines": { + "node": ">= 18" + }, + "peerDependencies": { + "@redis/client": "^5.6.1" + } + }, "packages/redis/node_modules/@redis/client": { "version": "5.6.1", "resolved": "https://registry.npmjs.org/@redis/client/-/client-5.6.1.tgz", diff --git a/packages/bloom/package.json b/packages/bloom/package.json index ef900b00a6d..edc106214e0 100644 --- a/packages/bloom/package.json +++ b/packages/bloom/package.json @@ -1,6 +1,6 @@ { "name": "@redis/bloom", - "version": "5.6.1", + "version": "5.7.0", "license": "MIT", "main": "./dist/lib/index.js", "types": "./dist/lib/index.d.ts", @@ -13,7 +13,7 @@ "release": "release-it" }, "peerDependencies": { - "@redis/client": "^5.6.1" + "@redis/client": "^5.7.0" }, "devDependencies": { "@redis/test-utils": "*" From b5cf002751853bb8fe17207efbd15a66b4958e93 Mon Sep 17 00:00:00 2001 From: GitHub Action Date: Thu, 31 Jul 2025 13:39:18 +0000 Subject: [PATCH 1670/1748] Release json@5.7.0 --- package-lock.json | 16 ++++++++++++++-- packages/json/package.json | 4 ++-- 2 files changed, 16 insertions(+), 4 deletions(-) diff --git a/package-lock.json b/package-lock.json index 79a9493d3c8..2fda06e6a8c 100644 --- a/package-lock.json +++ b/package-lock.json @@ -7423,7 +7423,7 @@ }, "packages/json": { "name": "@redis/json", - "version": "5.6.1", + "version": "5.7.0", "license": "MIT", "devDependencies": { "@redis/test-utils": "*" @@ -7432,7 +7432,7 @@ "node": ">= 18" }, "peerDependencies": { - "@redis/client": "^5.6.1" + "@redis/client": "^5.7.0" } }, "packages/redis": { @@ -7473,6 +7473,18 @@ "node": ">= 18" } }, + "packages/redis/node_modules/@redis/json": { + "version": "5.6.1", + "resolved": "https://registry.npmjs.org/@redis/json/-/json-5.6.1.tgz", + "integrity": "sha512-cTggVzPIVuiFeXcEcnTRiUzV7rmUvM9KUYxWiHyjsAVACTEUe4ifKkvzrij0H/z3ammU5tfGACffDB3olBwtVA==", + "license": "MIT", + "engines": { + "node": ">= 18" + }, + "peerDependencies": { + "@redis/client": "^5.6.1" + } + }, "packages/search": { "name": "@redis/search", "version": "5.6.1", diff --git a/packages/json/package.json b/packages/json/package.json index e62377feb14..fac6209c360 100644 --- a/packages/json/package.json +++ b/packages/json/package.json @@ -1,6 +1,6 @@ { "name": "@redis/json", - "version": "5.6.1", + "version": "5.7.0", "license": "MIT", "main": "./dist/lib/index.js", "types": "./dist/lib/index.d.ts", @@ -13,7 +13,7 @@ "release": "release-it" }, "peerDependencies": { - "@redis/client": "^5.6.1" + "@redis/client": "^5.7.0" }, "devDependencies": { "@redis/test-utils": "*" From f37f98aced7dd76b55c2b39b053b0e05b1f98806 Mon Sep 17 00:00:00 2001 From: GitHub Action Date: Thu, 31 Jul 2025 13:39:26 +0000 Subject: [PATCH 1671/1748] Release search@5.7.0 --- package-lock.json | 16 ++++++++++++++-- packages/search/package.json | 4 ++-- 2 files changed, 16 insertions(+), 4 deletions(-) diff --git a/package-lock.json b/package-lock.json index 2fda06e6a8c..52d8b4f2a6e 100644 --- a/package-lock.json +++ b/package-lock.json @@ -7485,9 +7485,21 @@ "@redis/client": "^5.6.1" } }, + "packages/redis/node_modules/@redis/search": { + "version": "5.6.1", + "resolved": "https://registry.npmjs.org/@redis/search/-/search-5.6.1.tgz", + "integrity": "sha512-+eOjx8O2YoKygjqkLpTHqcAq0zKLjior+ee2tRBx/3RSf1+OHxiC9Y6NstshQpvB1XHqTw9n7+f0+MsRJZrp0g==", + "license": "MIT", + "engines": { + "node": ">= 18" + }, + "peerDependencies": { + "@redis/client": "^5.6.1" + } + }, "packages/search": { "name": "@redis/search", - "version": "5.6.1", + "version": "5.7.0", "license": "MIT", "devDependencies": { "@redis/test-utils": "*" @@ -7496,7 +7508,7 @@ "node": ">= 18" }, "peerDependencies": { - "@redis/client": "^5.6.1" + "@redis/client": "^5.7.0" } }, "packages/test-utils": { diff --git a/packages/search/package.json b/packages/search/package.json index c8fb49d5332..5d7fffcfd26 100644 --- a/packages/search/package.json +++ b/packages/search/package.json @@ -1,6 +1,6 @@ { "name": "@redis/search", - "version": "5.6.1", + "version": "5.7.0", "license": "MIT", "main": "./dist/lib/index.js", "types": "./dist/lib/index.d.ts", @@ -14,7 +14,7 @@ "release": "release-it" }, "peerDependencies": { - "@redis/client": "^5.6.1" + "@redis/client": "^5.7.0" }, "devDependencies": { "@redis/test-utils": "*" From fa3ca983a3e1fda3a81f254ba9dd82cd6af12df4 Mon Sep 17 00:00:00 2001 From: GitHub Action Date: Thu, 31 Jul 2025 13:39:34 +0000 Subject: [PATCH 1672/1748] Release time-series@5.7.0 --- package-lock.json | 16 ++++++++++++++-- packages/time-series/package.json | 4 ++-- 2 files changed, 16 insertions(+), 4 deletions(-) diff --git a/package-lock.json b/package-lock.json index 52d8b4f2a6e..c9e584024b2 100644 --- a/package-lock.json +++ b/package-lock.json @@ -7497,6 +7497,18 @@ "@redis/client": "^5.6.1" } }, + "packages/redis/node_modules/@redis/time-series": { + "version": "5.6.1", + "resolved": "https://registry.npmjs.org/@redis/time-series/-/time-series-5.6.1.tgz", + "integrity": "sha512-sd3q4jMJdoSO2akw1L9NrdFI1JJ6zeMgMUoTh4a34p9sY3AnOI4aDLCecy8L2IcPAP1oNR3TbLFJiCJDQ35QTA==", + "license": "MIT", + "engines": { + "node": ">= 18" + }, + "peerDependencies": { + "@redis/client": "^5.6.1" + } + }, "packages/search": { "name": "@redis/search", "version": "5.7.0", @@ -7577,7 +7589,7 @@ }, "packages/time-series": { "name": "@redis/time-series", - "version": "5.6.1", + "version": "5.7.0", "license": "MIT", "devDependencies": { "@redis/test-utils": "*" @@ -7586,7 +7598,7 @@ "node": ">= 18" }, "peerDependencies": { - "@redis/client": "^5.6.1" + "@redis/client": "^5.7.0" } } } diff --git a/packages/time-series/package.json b/packages/time-series/package.json index 1c42757523f..44fca5643f3 100644 --- a/packages/time-series/package.json +++ b/packages/time-series/package.json @@ -1,6 +1,6 @@ { "name": "@redis/time-series", - "version": "5.6.1", + "version": "5.7.0", "license": "MIT", "main": "./dist/lib/index.js", "types": "./dist/lib/index.d.ts", @@ -13,7 +13,7 @@ "release": "release-it" }, "peerDependencies": { - "@redis/client": "^5.6.1" + "@redis/client": "^5.7.0" }, "devDependencies": { "@redis/test-utils": "*" From c75809ec650883a33ddcdca365c91aa5fbbf4961 Mon Sep 17 00:00:00 2001 From: GitHub Action Date: Thu, 31 Jul 2025 13:39:41 +0000 Subject: [PATCH 1673/1748] Release entraid@5.7.0 --- package-lock.json | 4 ++-- packages/entraid/package.json | 4 ++-- 2 files changed, 4 insertions(+), 4 deletions(-) diff --git a/package-lock.json b/package-lock.json index c9e584024b2..eacb323d1a5 100644 --- a/package-lock.json +++ b/package-lock.json @@ -7367,7 +7367,7 @@ }, "packages/entraid": { "name": "@redis/entraid", - "version": "5.6.1", + "version": "5.7.0", "license": "MIT", "dependencies": { "@azure/identity": "^4.7.0", @@ -7386,7 +7386,7 @@ "node": ">= 18" }, "peerDependencies": { - "@redis/client": "^5.6.1" + "@redis/client": "^5.7.0" } }, "packages/entraid/node_modules/@types/node": { diff --git a/packages/entraid/package.json b/packages/entraid/package.json index 4e702fabb65..cb57d9074c3 100644 --- a/packages/entraid/package.json +++ b/packages/entraid/package.json @@ -1,6 +1,6 @@ { "name": "@redis/entraid", - "version": "5.6.1", + "version": "5.7.0", "license": "MIT", "main": "./dist/index.js", "types": "./dist/index.d.ts", @@ -22,7 +22,7 @@ "@azure/msal-node": "^2.16.1" }, "peerDependencies": { - "@redis/client": "^5.6.1" + "@redis/client": "^5.7.0" }, "devDependencies": { "@types/express": "^4.17.21", From e2d4b43e39d145e605ca582dbb520b94ab505117 Mon Sep 17 00:00:00 2001 From: GitHub Action Date: Thu, 31 Jul 2025 13:39:49 +0000 Subject: [PATCH 1674/1748] Release redis@5.7.0 --- package-lock.json | 72 ++++--------------------------------- packages/redis/package.json | 12 +++---- 2 files changed, 12 insertions(+), 72 deletions(-) diff --git a/package-lock.json b/package-lock.json index eacb323d1a5..5ecc96aff06 100644 --- a/package-lock.json +++ b/package-lock.json @@ -7436,79 +7436,19 @@ } }, "packages/redis": { - "version": "5.6.1", - "license": "MIT", - "dependencies": { - "@redis/bloom": "5.6.1", - "@redis/client": "5.6.1", - "@redis/json": "5.6.1", - "@redis/search": "5.6.1", - "@redis/time-series": "5.6.1" - }, - "engines": { - "node": ">= 18" - } - }, - "packages/redis/node_modules/@redis/bloom": { - "version": "5.6.1", - "resolved": "https://registry.npmjs.org/@redis/bloom/-/bloom-5.6.1.tgz", - "integrity": "sha512-5/22U76IMEfn6TeZ+uvjXspHw+ykBF0kpBa8xouzeHaQMXs/auqBUOEYzU2VKYDvnd2RSpPTyIg82oB7PpUgLg==", - "license": "MIT", - "engines": { - "node": ">= 18" - }, - "peerDependencies": { - "@redis/client": "^5.6.1" - } - }, - "packages/redis/node_modules/@redis/client": { - "version": "5.6.1", - "resolved": "https://registry.npmjs.org/@redis/client/-/client-5.6.1.tgz", - "integrity": "sha512-bWHmSFIJ5w1Y4aHsYs46XMDHKQsBHFRhNcllYaBxz2Zl+lu+gbm5yI9BqxvKh48bLTs/Wx1Kns0gN2WIasE8MA==", + "version": "5.7.0", "license": "MIT", "dependencies": { - "cluster-key-slot": "1.1.2" + "@redis/bloom": "5.7.0", + "@redis/client": "5.7.0", + "@redis/json": "5.7.0", + "@redis/search": "5.7.0", + "@redis/time-series": "5.7.0" }, "engines": { "node": ">= 18" } }, - "packages/redis/node_modules/@redis/json": { - "version": "5.6.1", - "resolved": "https://registry.npmjs.org/@redis/json/-/json-5.6.1.tgz", - "integrity": "sha512-cTggVzPIVuiFeXcEcnTRiUzV7rmUvM9KUYxWiHyjsAVACTEUe4ifKkvzrij0H/z3ammU5tfGACffDB3olBwtVA==", - "license": "MIT", - "engines": { - "node": ">= 18" - }, - "peerDependencies": { - "@redis/client": "^5.6.1" - } - }, - "packages/redis/node_modules/@redis/search": { - "version": "5.6.1", - "resolved": "https://registry.npmjs.org/@redis/search/-/search-5.6.1.tgz", - "integrity": "sha512-+eOjx8O2YoKygjqkLpTHqcAq0zKLjior+ee2tRBx/3RSf1+OHxiC9Y6NstshQpvB1XHqTw9n7+f0+MsRJZrp0g==", - "license": "MIT", - "engines": { - "node": ">= 18" - }, - "peerDependencies": { - "@redis/client": "^5.6.1" - } - }, - "packages/redis/node_modules/@redis/time-series": { - "version": "5.6.1", - "resolved": "https://registry.npmjs.org/@redis/time-series/-/time-series-5.6.1.tgz", - "integrity": "sha512-sd3q4jMJdoSO2akw1L9NrdFI1JJ6zeMgMUoTh4a34p9sY3AnOI4aDLCecy8L2IcPAP1oNR3TbLFJiCJDQ35QTA==", - "license": "MIT", - "engines": { - "node": ">= 18" - }, - "peerDependencies": { - "@redis/client": "^5.6.1" - } - }, "packages/search": { "name": "@redis/search", "version": "5.7.0", diff --git a/packages/redis/package.json b/packages/redis/package.json index bdb86663480..a14f6ba10bc 100644 --- a/packages/redis/package.json +++ b/packages/redis/package.json @@ -1,7 +1,7 @@ { "name": "redis", "description": "A modern, high performance Redis client", - "version": "5.6.1", + "version": "5.7.0", "license": "MIT", "main": "./dist/index.js", "types": "./dist/index.d.ts", @@ -13,11 +13,11 @@ "release": "release-it" }, "dependencies": { - "@redis/bloom": "5.6.1", - "@redis/client": "5.6.1", - "@redis/json": "5.6.1", - "@redis/search": "5.6.1", - "@redis/time-series": "5.6.1" + "@redis/bloom": "5.7.0", + "@redis/client": "5.7.0", + "@redis/json": "5.7.0", + "@redis/search": "5.7.0", + "@redis/time-series": "5.7.0" }, "engines": { "node": ">= 18" From 66638fc90343aae3f8280829be1673693ec7fc85 Mon Sep 17 00:00:00 2001 From: Pavel Pashov <60297174+PavelPashov@users.noreply.github.com> Date: Mon, 4 Aug 2025 12:07:18 +0300 Subject: [PATCH 1675/1748] Add support svs vamana index creation (#3025) * feat(search): add SVS-VAMANA vector index algorithm support - Add VAMANA algorithm with compression and tuning parameters - Include comprehensive test coverage for various configurations - Fix parameter validation to handle falsy values correctly * feat(search): add additional VAMANA compression algorithms - Add LVQ4, LVQ4x4, LVQ4x8, LeanVec4x8, and LeanVec8x8 compression options - Update test to use LeanVec4x8 compression algorithm * chore: update Redis version from 8.2-rc1 to 8.2-rc2-pre --- .github/workflows/tests.yml | 2 +- packages/bloom/lib/test-utils.ts | 2 +- packages/client/lib/sentinel/test-util.ts | 2 +- packages/client/lib/test-utils.ts | 2 +- packages/entraid/lib/test-utils.ts | 2 +- packages/json/lib/test-utils.ts | 2 +- packages/search/lib/commands/CREATE.spec.ts | 112 +++++++++++++++++++- packages/search/lib/commands/CREATE.ts | 81 ++++++++++++-- packages/search/lib/test-utils.ts | 2 +- packages/time-series/lib/test-utils.ts | 2 +- 10 files changed, 193 insertions(+), 16 deletions(-) diff --git a/.github/workflows/tests.yml b/.github/workflows/tests.yml index df8cb1d1b6c..89925b55d44 100644 --- a/.github/workflows/tests.yml +++ b/.github/workflows/tests.yml @@ -22,7 +22,7 @@ jobs: fail-fast: false matrix: node-version: ["18", "20", "22"] - redis-version: ["rs-7.4.0-v1", "8.0.2", "8.2-rc1"] + redis-version: ["rs-7.4.0-v1", "8.0.2", "8.2-rc2-pre"] steps: - uses: actions/checkout@v4 with: diff --git a/packages/bloom/lib/test-utils.ts b/packages/bloom/lib/test-utils.ts index 268ebca8cb9..0f77acae6f6 100644 --- a/packages/bloom/lib/test-utils.ts +++ b/packages/bloom/lib/test-utils.ts @@ -4,7 +4,7 @@ import RedisBloomModules from '.'; export default TestUtils.createFromConfig({ dockerImageName: 'redislabs/client-libs-test', dockerImageVersionArgument: 'redis-version', - defaultDockerVersion: '8.2-rc1' + defaultDockerVersion: '8.2-rc2-pre' }); export const GLOBAL = { diff --git a/packages/client/lib/sentinel/test-util.ts b/packages/client/lib/sentinel/test-util.ts index 7c4752a8852..a88c9818580 100644 --- a/packages/client/lib/sentinel/test-util.ts +++ b/packages/client/lib/sentinel/test-util.ts @@ -174,7 +174,7 @@ export class SentinelFramework extends DockerBase { this.#testUtils = TestUtils.createFromConfig({ dockerImageName: 'redislabs/client-libs-test', dockerImageVersionArgument: 'redis-version', - defaultDockerVersion: '8.2-rc1' + defaultDockerVersion: '8.2-rc2-pre' }); this.#nodeMap = new Map>>>(); this.#sentinelMap = new Map>>>(); diff --git a/packages/client/lib/test-utils.ts b/packages/client/lib/test-utils.ts index 86b6ed294ac..62509dee143 100644 --- a/packages/client/lib/test-utils.ts +++ b/packages/client/lib/test-utils.ts @@ -9,7 +9,7 @@ import RedisBloomModules from '@redis/bloom'; const utils = TestUtils.createFromConfig({ dockerImageName: 'redislabs/client-libs-test', dockerImageVersionArgument: 'redis-version', - defaultDockerVersion: '8.2-rc1' + defaultDockerVersion: '8.2-rc2-pre' }); export default utils; diff --git a/packages/entraid/lib/test-utils.ts b/packages/entraid/lib/test-utils.ts index e5d977a6b40..1b63a955bfa 100644 --- a/packages/entraid/lib/test-utils.ts +++ b/packages/entraid/lib/test-utils.ts @@ -6,7 +6,7 @@ import { EntraidCredentialsProvider } from './entraid-credentials-provider'; export const testUtils = TestUtils.createFromConfig({ dockerImageName: 'redislabs/client-libs-test', dockerImageVersionArgument: 'redis-version', - defaultDockerVersion: '8.2-rc1' + defaultDockerVersion: '8.2-rc2-pre' }); const DEBUG_MODE_ARGS = testUtils.isVersionGreaterThan([7]) ? diff --git a/packages/json/lib/test-utils.ts b/packages/json/lib/test-utils.ts index cba2d95e737..81a546fcd64 100644 --- a/packages/json/lib/test-utils.ts +++ b/packages/json/lib/test-utils.ts @@ -4,7 +4,7 @@ import RedisJSON from '.'; export default TestUtils.createFromConfig({ dockerImageName: 'redislabs/client-libs-test', dockerImageVersionArgument: 'redis-version', - defaultDockerVersion: '8.2-rc1' + defaultDockerVersion: '8.2-rc2-pre' }); export const GLOBAL = { diff --git a/packages/search/lib/commands/CREATE.spec.ts b/packages/search/lib/commands/CREATE.spec.ts index 2c54d3d0235..268421ef35f 100644 --- a/packages/search/lib/commands/CREATE.spec.ts +++ b/packages/search/lib/commands/CREATE.spec.ts @@ -1,6 +1,6 @@ import { strict as assert } from 'node:assert'; import testUtils, { GLOBAL } from '../test-utils'; -import CREATE, { SCHEMA_FIELD_TYPE, SCHEMA_TEXT_FIELD_PHONETIC, SCHEMA_VECTOR_FIELD_ALGORITHM, REDISEARCH_LANGUAGE } from './CREATE'; +import CREATE, { SCHEMA_FIELD_TYPE, SCHEMA_TEXT_FIELD_PHONETIC, SCHEMA_VECTOR_FIELD_ALGORITHM, REDISEARCH_LANGUAGE, VAMANA_COMPRESSION_ALGORITHM } from './CREATE'; import { parseArgs } from '@redis/client/lib/commands/generic-transformers'; describe('FT.CREATE', () => { @@ -206,6 +206,33 @@ describe('FT.CREATE', () => { ] ); }); + + it('VAMANA algorithm', () => { + assert.deepEqual( + parseArgs(CREATE, 'index', { + field: { + type: SCHEMA_FIELD_TYPE.VECTOR, + ALGORITHM: SCHEMA_VECTOR_FIELD_ALGORITHM.VAMANA, + TYPE: "FLOAT32", + COMPRESSION: VAMANA_COMPRESSION_ALGORITHM.LVQ8, + DIM: 1024, + DISTANCE_METRIC: 'COSINE', + CONSTRUCTION_WINDOW_SIZE: 300, + GRAPH_MAX_DEGREE: 128, + SEARCH_WINDOW_SIZE: 20, + EPSILON: 0.02, + TRAINING_THRESHOLD: 20480, + REDUCE: 512, + } + }), + [ + 'FT.CREATE', 'index', 'SCHEMA', 'field', 'VECTOR', 'SVS-VAMANA', '20', 'TYPE', + 'FLOAT32', 'DIM', '1024', 'DISTANCE_METRIC', 'COSINE', 'COMPRESSION', 'LVQ8', + 'CONSTRUCTION_WINDOW_SIZE', '300', 'GRAPH_MAX_DEGREE', '128', 'SEARCH_WINDOW_SIZE', '20', + 'EPSILON', '0.02', 'TRAINING_THRESHOLD', '20480', 'REDUCE', '512' + ] + ); + }); }); describe('GEOSHAPE', () => { @@ -556,4 +583,87 @@ describe('FT.CREATE', () => { "OK" ); }, GLOBAL.SERVERS.OPEN); + + testUtils.testWithClientIfVersionWithinRange([[8, 2], 'LATEST'], 'client.ft.create vector svs-vamana', async client => { + assert.equal( + await client.ft.create("index_svs_vamana_min_config", { + field: { + type: SCHEMA_FIELD_TYPE.VECTOR, + ALGORITHM: SCHEMA_VECTOR_FIELD_ALGORITHM.VAMANA, + TYPE: "FLOAT32", + DIM: 768, + DISTANCE_METRIC: 'L2', + }, + }), + "OK" + ); + + assert.equal( + await client.ft.create("index_svs_vamana_no_compression", { + field: { + type: SCHEMA_FIELD_TYPE.VECTOR, + ALGORITHM: SCHEMA_VECTOR_FIELD_ALGORITHM.VAMANA, + TYPE: "FLOAT32", + DIM: 512, + DISTANCE_METRIC: 'L2', + CONSTRUCTION_WINDOW_SIZE: 200, + GRAPH_MAX_DEGREE: 64, + SEARCH_WINDOW_SIZE: 50, + EPSILON: 0.01 + }, + }), + "OK" + ); + + assert.equal( + await client.ft.create("index_svs_vamana_compression", { + field: { + type: SCHEMA_FIELD_TYPE.VECTOR, + ALGORITHM: SCHEMA_VECTOR_FIELD_ALGORITHM.VAMANA, + TYPE: "FLOAT32", + COMPRESSION: VAMANA_COMPRESSION_ALGORITHM.LeanVec4x8, + DIM: 1024, + DISTANCE_METRIC: 'COSINE', + CONSTRUCTION_WINDOW_SIZE: 300, + GRAPH_MAX_DEGREE: 128, + SEARCH_WINDOW_SIZE: 20, + EPSILON: 0.02, + TRAINING_THRESHOLD: 20480, + REDUCE: 512, + }, + }), + "OK" + ); + + assert.equal( + await client.ft.create("index_svs_vamana_float16", { + field: { + type: SCHEMA_FIELD_TYPE.VECTOR, + ALGORITHM: SCHEMA_VECTOR_FIELD_ALGORITHM.VAMANA, + TYPE: "FLOAT16", + DIM: 128, + DISTANCE_METRIC: 'IP', + }, + }), + "OK" + ); + + await assert.rejects( + client.ft.create("index_svs_vamana_invalid_config", { + field: { + type: SCHEMA_FIELD_TYPE.VECTOR, + ALGORITHM: SCHEMA_VECTOR_FIELD_ALGORITHM.VAMANA, + TYPE: "FLOAT32", + DIM: 2, + DISTANCE_METRIC: 'L2', + CONSTRUCTION_WINDOW_SIZE: 200, + GRAPH_MAX_DEGREE: 64, + SEARCH_WINDOW_SIZE: 50, + EPSILON: 0.01, + // TRAINING_THRESHOLD should error without COMPRESSION + TRAINING_THRESHOLD: 2048 + }, + }), + ) + }, GLOBAL.SERVERS.OPEN); }); diff --git a/packages/search/lib/commands/CREATE.ts b/packages/search/lib/commands/CREATE.ts index 9f24a256fae..3f892df40d2 100644 --- a/packages/search/lib/commands/CREATE.ts +++ b/packages/search/lib/commands/CREATE.ts @@ -54,7 +54,11 @@ interface SchemaTagField extends SchemaCommonField Date: Tue, 5 Aug 2025 03:12:10 -0400 Subject: [PATCH 1676/1748] fix: createClient url+tls invariant violation check (#2835) --- packages/client/lib/client/index.spec.ts | 49 +++++++++++++++++++++++- packages/client/lib/client/index.ts | 49 ++++++++++++++++-------- 2 files changed, 82 insertions(+), 16 deletions(-) diff --git a/packages/client/lib/client/index.spec.ts b/packages/client/lib/client/index.spec.ts index f04d6467062..0aed98450d2 100644 --- a/packages/client/lib/client/index.spec.ts +++ b/packages/client/lib/client/index.spec.ts @@ -64,7 +64,8 @@ describe('Client', () => { const expected: RedisClientOptions = { socket: { host: 'localhost', - port: 6379 + port: 6379, + tls: false }, username: 'user', password: 'secret', @@ -161,12 +162,58 @@ describe('Client', () => { { socket: { host: 'localhost', + tls: false } } ); }); }); + describe('parseOptions', () => { + it('should throw error if tls socket option is set to true and the url protocol is "redis:"', () => { + assert.throws( + () => RedisClient.parseOptions({ + url: 'redis://localhost', + socket: { + tls: true + } + }), + TypeError + ); + }); + it('should throw error if tls socket option is set to false and the url protocol is "rediss:"', () => { + assert.throws( + () => RedisClient.parseOptions({ + url: 'rediss://localhost', + socket: { + tls: false + } + }), + TypeError + ); + }); + it('should not throw when tls socket option and url protocol matches"', () => { + assert.equal( + RedisClient.parseOptions({ + url: 'rediss://localhost', + socket: { + tls: true + } + }).socket.tls, + true + ); + assert.equal( + RedisClient.parseOptions({ + url: 'redis://localhost', + socket: { + tls: false + } + }).socket.tls, + false + ); + }); + }); + describe('authentication', () => { testUtils.testWithClient('Client should be authenticated', async client => { assert.equal( diff --git a/packages/client/lib/client/index.ts b/packages/client/lib/client/index.ts index 128dc599677..ccad872e223 100644 --- a/packages/client/lib/client/index.ts +++ b/packages/client/lib/client/index.ts @@ -315,21 +315,45 @@ export default class RedisClient< return RedisClient.factory(options)(options); } - static parseURL(url: string): RedisClientOptions { + static parseOptions(options: O): O { + if (options?.url) { + const parsed = RedisClient.parseURL(options.url); + if (options.socket) { + if (options.socket.tls !== undefined && options.socket.tls !== parsed.socket.tls) { + throw new TypeError(`tls socket option is set to ${options.socket.tls} which is mismatch with protocol or the URL ${options.url} passed`) + } + parsed.socket = Object.assign(options.socket, parsed.socket); + } + + Object.assign(options, parsed); + } + return options; + } + + static parseURL(url: string): RedisClientOptions & { + socket: Exclude & { + tls: boolean + } + } { // https://www.iana.org/assignments/uri-schemes/prov/redis const { hostname, port, protocol, username, password, pathname } = new URL(url), - parsed: RedisClientOptions = { + parsed: RedisClientOptions & { + socket: Exclude & { + tls: boolean + } + } = { socket: { - host: hostname + host: hostname, + tls: false } }; - if (protocol === 'rediss:') { - parsed!.socket!.tls = true; - } else if (protocol !== 'redis:') { + if (protocol !== 'redis:' && protocol !== 'rediss:') { throw new TypeError('Invalid protocol'); } + parsed.socket.tls = protocol === 'rediss:'; + if (port) { (parsed.socket as TcpSocketConnectOpts).port = Number(port); } @@ -464,15 +488,6 @@ export default class RedisClient< }; } - if (options?.url) { - const parsed = RedisClient.parseURL(options.url); - if (options.socket) { - parsed.socket = Object.assign(options.socket, parsed.socket); - } - - Object.assign(options, parsed); - } - if (options?.database) { this._self.#selectedDB = options.database; } @@ -481,6 +496,10 @@ export default class RedisClient< this._commandOptions = options.commandOptions; } + if (options) { + return RedisClient.parseOptions(options); + } + return options; } From de5e916e05cde15938ef15039ff93c6cf8e558e5 Mon Sep 17 00:00:00 2001 From: GitHub Action Date: Tue, 5 Aug 2025 09:57:55 +0000 Subject: [PATCH 1677/1748] Release client@5.8.0 --- package-lock.json | 14 +++++++++++++- packages/client/package.json | 2 +- 2 files changed, 14 insertions(+), 2 deletions(-) diff --git a/package-lock.json b/package-lock.json index 5ecc96aff06..e94583bb6ad 100644 --- a/package-lock.json +++ b/package-lock.json @@ -7351,7 +7351,7 @@ }, "packages/client": { "name": "@redis/client", - "version": "5.7.0", + "version": "5.8.0", "license": "MIT", "dependencies": { "cluster-key-slot": "1.1.2" @@ -7449,6 +7449,18 @@ "node": ">= 18" } }, + "packages/redis/node_modules/@redis/client": { + "version": "5.7.0", + "resolved": "https://registry.npmjs.org/@redis/client/-/client-5.7.0.tgz", + "integrity": "sha512-YV3Knspdj9k6H6s4v8QRcj1WBxHt40vtPmszLKGwRUOUpUOLWSlI9oCUjprMDcQNzgSCXGXYdL/Aj6nT2+Ub0w==", + "license": "MIT", + "dependencies": { + "cluster-key-slot": "1.1.2" + }, + "engines": { + "node": ">= 18" + } + }, "packages/search": { "name": "@redis/search", "version": "5.7.0", diff --git a/packages/client/package.json b/packages/client/package.json index 091ddaf5c91..b7c11d57a84 100644 --- a/packages/client/package.json +++ b/packages/client/package.json @@ -1,6 +1,6 @@ { "name": "@redis/client", - "version": "5.7.0", + "version": "5.8.0", "license": "MIT", "main": "./dist/index.js", "types": "./dist/index.d.ts", From 42ec77115169334ba4ecdf4450081e38ca6a2012 Mon Sep 17 00:00:00 2001 From: GitHub Action Date: Tue, 5 Aug 2025 09:58:03 +0000 Subject: [PATCH 1678/1748] Release bloom@5.8.0 --- package-lock.json | 16 ++++++++++++++-- packages/bloom/package.json | 4 ++-- 2 files changed, 16 insertions(+), 4 deletions(-) diff --git a/package-lock.json b/package-lock.json index e94583bb6ad..11dd6d69ce3 100644 --- a/package-lock.json +++ b/package-lock.json @@ -7337,7 +7337,7 @@ }, "packages/bloom": { "name": "@redis/bloom", - "version": "5.7.0", + "version": "5.8.0", "license": "MIT", "devDependencies": { "@redis/test-utils": "*" @@ -7346,7 +7346,7 @@ "node": ">= 18" }, "peerDependencies": { - "@redis/client": "^5.7.0" + "@redis/client": "^5.8.0" } }, "packages/client": { @@ -7449,6 +7449,18 @@ "node": ">= 18" } }, + "packages/redis/node_modules/@redis/bloom": { + "version": "5.7.0", + "resolved": "https://registry.npmjs.org/@redis/bloom/-/bloom-5.7.0.tgz", + "integrity": "sha512-KtBHDH2Aw1BxYDQd87PJsdEmZcpMbD4oPzdBwB4IvSRmMovukO2NNGi5vpCHhCoicS83zu7cjX1fw79uFBZFJA==", + "license": "MIT", + "engines": { + "node": ">= 18" + }, + "peerDependencies": { + "@redis/client": "^5.7.0" + } + }, "packages/redis/node_modules/@redis/client": { "version": "5.7.0", "resolved": "https://registry.npmjs.org/@redis/client/-/client-5.7.0.tgz", diff --git a/packages/bloom/package.json b/packages/bloom/package.json index edc106214e0..00af3c18690 100644 --- a/packages/bloom/package.json +++ b/packages/bloom/package.json @@ -1,6 +1,6 @@ { "name": "@redis/bloom", - "version": "5.7.0", + "version": "5.8.0", "license": "MIT", "main": "./dist/lib/index.js", "types": "./dist/lib/index.d.ts", @@ -13,7 +13,7 @@ "release": "release-it" }, "peerDependencies": { - "@redis/client": "^5.7.0" + "@redis/client": "^5.8.0" }, "devDependencies": { "@redis/test-utils": "*" From cf16f8b790bb84a5c0b72d9f2c0e324df1ffd184 Mon Sep 17 00:00:00 2001 From: GitHub Action Date: Tue, 5 Aug 2025 09:58:10 +0000 Subject: [PATCH 1679/1748] Release json@5.8.0 --- package-lock.json | 16 ++++++++++++++-- packages/json/package.json | 4 ++-- 2 files changed, 16 insertions(+), 4 deletions(-) diff --git a/package-lock.json b/package-lock.json index 11dd6d69ce3..f30ee3a7d1e 100644 --- a/package-lock.json +++ b/package-lock.json @@ -7423,7 +7423,7 @@ }, "packages/json": { "name": "@redis/json", - "version": "5.7.0", + "version": "5.8.0", "license": "MIT", "devDependencies": { "@redis/test-utils": "*" @@ -7432,7 +7432,7 @@ "node": ">= 18" }, "peerDependencies": { - "@redis/client": "^5.7.0" + "@redis/client": "^5.8.0" } }, "packages/redis": { @@ -7473,6 +7473,18 @@ "node": ">= 18" } }, + "packages/redis/node_modules/@redis/json": { + "version": "5.7.0", + "resolved": "https://registry.npmjs.org/@redis/json/-/json-5.7.0.tgz", + "integrity": "sha512-VP3wtse1PSB/UjZAV1lWyDrWrrZcwi/cjb3L0lIarcIJ+EbHliB2QPml0Bvjz8F8F0eDJRtChJVXFc+jhGxCtA==", + "license": "MIT", + "engines": { + "node": ">= 18" + }, + "peerDependencies": { + "@redis/client": "^5.7.0" + } + }, "packages/search": { "name": "@redis/search", "version": "5.7.0", diff --git a/packages/json/package.json b/packages/json/package.json index fac6209c360..bab27df34cd 100644 --- a/packages/json/package.json +++ b/packages/json/package.json @@ -1,6 +1,6 @@ { "name": "@redis/json", - "version": "5.7.0", + "version": "5.8.0", "license": "MIT", "main": "./dist/lib/index.js", "types": "./dist/lib/index.d.ts", @@ -13,7 +13,7 @@ "release": "release-it" }, "peerDependencies": { - "@redis/client": "^5.7.0" + "@redis/client": "^5.8.0" }, "devDependencies": { "@redis/test-utils": "*" From 1436a6e304c2c369561e0e0b13626baae212253f Mon Sep 17 00:00:00 2001 From: GitHub Action Date: Tue, 5 Aug 2025 09:58:17 +0000 Subject: [PATCH 1680/1748] Release search@5.8.0 --- package-lock.json | 16 ++++++++++++++-- packages/search/package.json | 4 ++-- 2 files changed, 16 insertions(+), 4 deletions(-) diff --git a/package-lock.json b/package-lock.json index f30ee3a7d1e..051afd3243b 100644 --- a/package-lock.json +++ b/package-lock.json @@ -7485,9 +7485,21 @@ "@redis/client": "^5.7.0" } }, + "packages/redis/node_modules/@redis/search": { + "version": "5.7.0", + "resolved": "https://registry.npmjs.org/@redis/search/-/search-5.7.0.tgz", + "integrity": "sha512-dDZIq8pZJnT+kZ9xRlLLi2Rvkd792z9eh31QRIwPr5wXjAXeaQ+Nf65em6dLpsxZ60MmhwDwLrBPJpYVjKPBPQ==", + "license": "MIT", + "engines": { + "node": ">= 18" + }, + "peerDependencies": { + "@redis/client": "^5.7.0" + } + }, "packages/search": { "name": "@redis/search", - "version": "5.7.0", + "version": "5.8.0", "license": "MIT", "devDependencies": { "@redis/test-utils": "*" @@ -7496,7 +7508,7 @@ "node": ">= 18" }, "peerDependencies": { - "@redis/client": "^5.7.0" + "@redis/client": "^5.8.0" } }, "packages/test-utils": { diff --git a/packages/search/package.json b/packages/search/package.json index 5d7fffcfd26..8095a418d66 100644 --- a/packages/search/package.json +++ b/packages/search/package.json @@ -1,6 +1,6 @@ { "name": "@redis/search", - "version": "5.7.0", + "version": "5.8.0", "license": "MIT", "main": "./dist/lib/index.js", "types": "./dist/lib/index.d.ts", @@ -14,7 +14,7 @@ "release": "release-it" }, "peerDependencies": { - "@redis/client": "^5.7.0" + "@redis/client": "^5.8.0" }, "devDependencies": { "@redis/test-utils": "*" From 4b6a3d1c39ac88521dc440791ce6c2d7c3b3dc3f Mon Sep 17 00:00:00 2001 From: GitHub Action Date: Tue, 5 Aug 2025 09:58:25 +0000 Subject: [PATCH 1681/1748] Release time-series@5.8.0 --- package-lock.json | 16 ++++++++++++++-- packages/time-series/package.json | 4 ++-- 2 files changed, 16 insertions(+), 4 deletions(-) diff --git a/package-lock.json b/package-lock.json index 051afd3243b..0ae7329f750 100644 --- a/package-lock.json +++ b/package-lock.json @@ -7497,6 +7497,18 @@ "@redis/client": "^5.7.0" } }, + "packages/redis/node_modules/@redis/time-series": { + "version": "5.7.0", + "resolved": "https://registry.npmjs.org/@redis/time-series/-/time-series-5.7.0.tgz", + "integrity": "sha512-AJTF9sz3y1MJAukgQW4Jw8zt8qGOE3+1d87pufOP35zsFBlHipGscpctoXiNMebfy0114y/FjSprr65LjbJQSQ==", + "license": "MIT", + "engines": { + "node": ">= 18" + }, + "peerDependencies": { + "@redis/client": "^5.7.0" + } + }, "packages/search": { "name": "@redis/search", "version": "5.8.0", @@ -7577,7 +7589,7 @@ }, "packages/time-series": { "name": "@redis/time-series", - "version": "5.7.0", + "version": "5.8.0", "license": "MIT", "devDependencies": { "@redis/test-utils": "*" @@ -7586,7 +7598,7 @@ "node": ">= 18" }, "peerDependencies": { - "@redis/client": "^5.7.0" + "@redis/client": "^5.8.0" } } } diff --git a/packages/time-series/package.json b/packages/time-series/package.json index 44fca5643f3..0ca01943a5a 100644 --- a/packages/time-series/package.json +++ b/packages/time-series/package.json @@ -1,6 +1,6 @@ { "name": "@redis/time-series", - "version": "5.7.0", + "version": "5.8.0", "license": "MIT", "main": "./dist/lib/index.js", "types": "./dist/lib/index.d.ts", @@ -13,7 +13,7 @@ "release": "release-it" }, "peerDependencies": { - "@redis/client": "^5.7.0" + "@redis/client": "^5.8.0" }, "devDependencies": { "@redis/test-utils": "*" From 0916d33b120c41cac892436768b30674c82c0383 Mon Sep 17 00:00:00 2001 From: GitHub Action Date: Tue, 5 Aug 2025 09:58:32 +0000 Subject: [PATCH 1682/1748] Release entraid@5.8.0 --- package-lock.json | 4 ++-- packages/entraid/package.json | 4 ++-- 2 files changed, 4 insertions(+), 4 deletions(-) diff --git a/package-lock.json b/package-lock.json index 0ae7329f750..4c53f7eea63 100644 --- a/package-lock.json +++ b/package-lock.json @@ -7367,7 +7367,7 @@ }, "packages/entraid": { "name": "@redis/entraid", - "version": "5.7.0", + "version": "5.8.0", "license": "MIT", "dependencies": { "@azure/identity": "^4.7.0", @@ -7386,7 +7386,7 @@ "node": ">= 18" }, "peerDependencies": { - "@redis/client": "^5.7.0" + "@redis/client": "^5.8.0" } }, "packages/entraid/node_modules/@types/node": { diff --git a/packages/entraid/package.json b/packages/entraid/package.json index cb57d9074c3..2ef4cfca794 100644 --- a/packages/entraid/package.json +++ b/packages/entraid/package.json @@ -1,6 +1,6 @@ { "name": "@redis/entraid", - "version": "5.7.0", + "version": "5.8.0", "license": "MIT", "main": "./dist/index.js", "types": "./dist/index.d.ts", @@ -22,7 +22,7 @@ "@azure/msal-node": "^2.16.1" }, "peerDependencies": { - "@redis/client": "^5.7.0" + "@redis/client": "^5.8.0" }, "devDependencies": { "@types/express": "^4.17.21", From 12f7d8a7fef4cb0961afa56f153236356e003e86 Mon Sep 17 00:00:00 2001 From: GitHub Action Date: Tue, 5 Aug 2025 09:58:40 +0000 Subject: [PATCH 1683/1748] Release redis@5.8.0 --- package-lock.json | 72 ++++--------------------------------- packages/redis/package.json | 12 +++---- 2 files changed, 12 insertions(+), 72 deletions(-) diff --git a/package-lock.json b/package-lock.json index 4c53f7eea63..ede6844f0be 100644 --- a/package-lock.json +++ b/package-lock.json @@ -7436,79 +7436,19 @@ } }, "packages/redis": { - "version": "5.7.0", - "license": "MIT", - "dependencies": { - "@redis/bloom": "5.7.0", - "@redis/client": "5.7.0", - "@redis/json": "5.7.0", - "@redis/search": "5.7.0", - "@redis/time-series": "5.7.0" - }, - "engines": { - "node": ">= 18" - } - }, - "packages/redis/node_modules/@redis/bloom": { - "version": "5.7.0", - "resolved": "https://registry.npmjs.org/@redis/bloom/-/bloom-5.7.0.tgz", - "integrity": "sha512-KtBHDH2Aw1BxYDQd87PJsdEmZcpMbD4oPzdBwB4IvSRmMovukO2NNGi5vpCHhCoicS83zu7cjX1fw79uFBZFJA==", - "license": "MIT", - "engines": { - "node": ">= 18" - }, - "peerDependencies": { - "@redis/client": "^5.7.0" - } - }, - "packages/redis/node_modules/@redis/client": { - "version": "5.7.0", - "resolved": "https://registry.npmjs.org/@redis/client/-/client-5.7.0.tgz", - "integrity": "sha512-YV3Knspdj9k6H6s4v8QRcj1WBxHt40vtPmszLKGwRUOUpUOLWSlI9oCUjprMDcQNzgSCXGXYdL/Aj6nT2+Ub0w==", + "version": "5.8.0", "license": "MIT", "dependencies": { - "cluster-key-slot": "1.1.2" + "@redis/bloom": "5.8.0", + "@redis/client": "5.8.0", + "@redis/json": "5.8.0", + "@redis/search": "5.8.0", + "@redis/time-series": "5.8.0" }, "engines": { "node": ">= 18" } }, - "packages/redis/node_modules/@redis/json": { - "version": "5.7.0", - "resolved": "https://registry.npmjs.org/@redis/json/-/json-5.7.0.tgz", - "integrity": "sha512-VP3wtse1PSB/UjZAV1lWyDrWrrZcwi/cjb3L0lIarcIJ+EbHliB2QPml0Bvjz8F8F0eDJRtChJVXFc+jhGxCtA==", - "license": "MIT", - "engines": { - "node": ">= 18" - }, - "peerDependencies": { - "@redis/client": "^5.7.0" - } - }, - "packages/redis/node_modules/@redis/search": { - "version": "5.7.0", - "resolved": "https://registry.npmjs.org/@redis/search/-/search-5.7.0.tgz", - "integrity": "sha512-dDZIq8pZJnT+kZ9xRlLLi2Rvkd792z9eh31QRIwPr5wXjAXeaQ+Nf65em6dLpsxZ60MmhwDwLrBPJpYVjKPBPQ==", - "license": "MIT", - "engines": { - "node": ">= 18" - }, - "peerDependencies": { - "@redis/client": "^5.7.0" - } - }, - "packages/redis/node_modules/@redis/time-series": { - "version": "5.7.0", - "resolved": "https://registry.npmjs.org/@redis/time-series/-/time-series-5.7.0.tgz", - "integrity": "sha512-AJTF9sz3y1MJAukgQW4Jw8zt8qGOE3+1d87pufOP35zsFBlHipGscpctoXiNMebfy0114y/FjSprr65LjbJQSQ==", - "license": "MIT", - "engines": { - "node": ">= 18" - }, - "peerDependencies": { - "@redis/client": "^5.7.0" - } - }, "packages/search": { "name": "@redis/search", "version": "5.8.0", diff --git a/packages/redis/package.json b/packages/redis/package.json index a14f6ba10bc..c3cbc01442e 100644 --- a/packages/redis/package.json +++ b/packages/redis/package.json @@ -1,7 +1,7 @@ { "name": "redis", "description": "A modern, high performance Redis client", - "version": "5.7.0", + "version": "5.8.0", "license": "MIT", "main": "./dist/index.js", "types": "./dist/index.d.ts", @@ -13,11 +13,11 @@ "release": "release-it" }, "dependencies": { - "@redis/bloom": "5.7.0", - "@redis/client": "5.7.0", - "@redis/json": "5.7.0", - "@redis/search": "5.7.0", - "@redis/time-series": "5.7.0" + "@redis/bloom": "5.8.0", + "@redis/client": "5.8.0", + "@redis/json": "5.8.0", + "@redis/search": "5.8.0", + "@redis/time-series": "5.8.0" }, "engines": { "node": ">= 18" From 6406172ea8f833cb483206c452b0c4f16958d393 Mon Sep 17 00:00:00 2001 From: Nikolay Karadzhov Date: Wed, 6 Aug 2025 09:49:52 +0300 Subject: [PATCH 1684/1748] chore(tests): bump test container version 8.2 (#3046) --- .github/workflows/tests.yml | 2 +- README.md | 3 ++- packages/bloom/lib/test-utils.ts | 2 +- packages/client/lib/sentinel/test-util.ts | 32 +++++++++++------------ packages/client/lib/test-utils.ts | 2 +- packages/entraid/lib/test-utils.ts | 2 +- packages/json/lib/test-utils.ts | 2 +- packages/search/lib/test-utils.ts | 2 +- packages/time-series/lib/test-utils.ts | 2 +- 9 files changed, 25 insertions(+), 24 deletions(-) diff --git a/.github/workflows/tests.yml b/.github/workflows/tests.yml index 89925b55d44..9753535b53d 100644 --- a/.github/workflows/tests.yml +++ b/.github/workflows/tests.yml @@ -22,7 +22,7 @@ jobs: fail-fast: false matrix: node-version: ["18", "20", "22"] - redis-version: ["rs-7.4.0-v1", "8.0.2", "8.2-rc2-pre"] + redis-version: ["rs-7.4.0-v1", "8.0.2", "8.2"] steps: - uses: actions/checkout@v4 with: diff --git a/README.md b/README.md index ab6b4707e6f..9948858db9f 100644 --- a/README.md +++ b/README.md @@ -241,7 +241,7 @@ Node Redis v5 adds support for [Client Side Caching](https://redis.io/docs/manua ```typescript // Enable client side caching with RESP3 const client = createClient({ - RESP: 3, + RESP: 3, clientSideCache: { ttl: 0, // Time-to-live (0 = no expiration) maxEntries: 0, // Maximum entries (0 = unlimited) @@ -304,6 +304,7 @@ Node Redis is supported with the following versions of Redis: | Version | Supported | | ------- | ------------------ | +| 8.2.z | :heavy_check_mark: | | 8.0.z | :heavy_check_mark: | | 7.4.z | :heavy_check_mark: | | 7.2.z | :heavy_check_mark: | diff --git a/packages/bloom/lib/test-utils.ts b/packages/bloom/lib/test-utils.ts index 0f77acae6f6..64bc3484090 100644 --- a/packages/bloom/lib/test-utils.ts +++ b/packages/bloom/lib/test-utils.ts @@ -4,7 +4,7 @@ import RedisBloomModules from '.'; export default TestUtils.createFromConfig({ dockerImageName: 'redislabs/client-libs-test', dockerImageVersionArgument: 'redis-version', - defaultDockerVersion: '8.2-rc2-pre' + defaultDockerVersion: '8.2' }); export const GLOBAL = { diff --git a/packages/client/lib/sentinel/test-util.ts b/packages/client/lib/sentinel/test-util.ts index a88c9818580..1f8d75a76da 100644 --- a/packages/client/lib/sentinel/test-util.ts +++ b/packages/client/lib/sentinel/test-util.ts @@ -174,16 +174,16 @@ export class SentinelFramework extends DockerBase { this.#testUtils = TestUtils.createFromConfig({ dockerImageName: 'redislabs/client-libs-test', dockerImageVersionArgument: 'redis-version', - defaultDockerVersion: '8.2-rc2-pre' + defaultDockerVersion: '8.2' }); this.#nodeMap = new Map>>>(); this.#sentinelMap = new Map>>>(); } - getSentinelClient(opts?: Partial>, errors = true) { if (opts?.sentinelRootNodes !== undefined) { throw new Error("cannot specify sentinelRootNodes here"); @@ -252,7 +252,7 @@ export class SentinelFramework extends DockerBase { protected async spawnRedisSentinelNodes(replicasCount: number) { const master = await this.#testUtils.spawnRedisServer({serverArguments: DEBUG_MODE_ARGS}) - + const replicas: Array = [] for (let i = 0; i < replicasCount; i++) { const replica = await this.#testUtils.spawnRedisServer({serverArguments: DEBUG_MODE_ARGS}) @@ -282,7 +282,7 @@ export class SentinelFramework extends DockerBase { async getAllRunning() { for (const port of this.getAllNodesPort()) { let first = true; - while (await isPortAvailable(port)) { + while (await isPortAvailable(port)) { if (!first) { console.log(`problematic restart ${port}`); await setTimeout(500); @@ -295,7 +295,7 @@ export class SentinelFramework extends DockerBase { for (const port of this.getAllSentinelsPort()) { let first = true; - while (await isPortAvailable(port)) { + while (await isPortAvailable(port)) { if (!first) { await setTimeout(500); } else { @@ -325,7 +325,7 @@ export class SentinelFramework extends DockerBase { await client.connect(); await client.replicaOf("127.0.0.1", masterPort); await client.close(); - + this.#nodeList.push(replica); this.#nodeMap.set(replica.port.toString(), replica); @@ -333,9 +333,9 @@ export class SentinelFramework extends DockerBase { async getMaster(tracer?: Array): Promise { const client = RedisClient.create({ - name: this.config.sentinelName, + name: this.config.sentinelName, socket: { - host: "127.0.0.1", + host: "127.0.0.1", port: this.#sentinelList[0].port, }, modules: RedisSentinelModule, @@ -464,9 +464,9 @@ export class SentinelFramework extends DockerBase { async sentinelSentinels() { const client = RedisClient.create({ - name: this.config.sentinelName, + name: this.config.sentinelName, socket: { - host: "127.0.0.1", + host: "127.0.0.1", port: this.#sentinelList[0].port, }, modules: RedisSentinelModule, @@ -480,9 +480,9 @@ export class SentinelFramework extends DockerBase { async sentinelMaster() { const client = RedisClient.create({ - name: this.config.sentinelName, + name: this.config.sentinelName, socket: { - host: "127.0.0.1", + host: "127.0.0.1", port: this.#sentinelList[0].port, }, modules: RedisSentinelModule, @@ -496,9 +496,9 @@ export class SentinelFramework extends DockerBase { async sentinelReplicas() { const client = RedisClient.create({ - name: this.config.sentinelName, + name: this.config.sentinelName, socket: { - host: "127.0.0.1", + host: "127.0.0.1", port: this.#sentinelList[0].port, }, modules: RedisSentinelModule, @@ -509,4 +509,4 @@ export class SentinelFramework extends DockerBase { return replicas } -} \ No newline at end of file +} diff --git a/packages/client/lib/test-utils.ts b/packages/client/lib/test-utils.ts index 62509dee143..d6cb67aa01d 100644 --- a/packages/client/lib/test-utils.ts +++ b/packages/client/lib/test-utils.ts @@ -9,7 +9,7 @@ import RedisBloomModules from '@redis/bloom'; const utils = TestUtils.createFromConfig({ dockerImageName: 'redislabs/client-libs-test', dockerImageVersionArgument: 'redis-version', - defaultDockerVersion: '8.2-rc2-pre' + defaultDockerVersion: '8.2' }); export default utils; diff --git a/packages/entraid/lib/test-utils.ts b/packages/entraid/lib/test-utils.ts index 1b63a955bfa..2a240db3418 100644 --- a/packages/entraid/lib/test-utils.ts +++ b/packages/entraid/lib/test-utils.ts @@ -6,7 +6,7 @@ import { EntraidCredentialsProvider } from './entraid-credentials-provider'; export const testUtils = TestUtils.createFromConfig({ dockerImageName: 'redislabs/client-libs-test', dockerImageVersionArgument: 'redis-version', - defaultDockerVersion: '8.2-rc2-pre' + defaultDockerVersion: '8.2' }); const DEBUG_MODE_ARGS = testUtils.isVersionGreaterThan([7]) ? diff --git a/packages/json/lib/test-utils.ts b/packages/json/lib/test-utils.ts index 81a546fcd64..629c2a5fd60 100644 --- a/packages/json/lib/test-utils.ts +++ b/packages/json/lib/test-utils.ts @@ -4,7 +4,7 @@ import RedisJSON from '.'; export default TestUtils.createFromConfig({ dockerImageName: 'redislabs/client-libs-test', dockerImageVersionArgument: 'redis-version', - defaultDockerVersion: '8.2-rc2-pre' + defaultDockerVersion: '8.2' }); export const GLOBAL = { diff --git a/packages/search/lib/test-utils.ts b/packages/search/lib/test-utils.ts index ef4c759b43a..ed1f864ef2e 100644 --- a/packages/search/lib/test-utils.ts +++ b/packages/search/lib/test-utils.ts @@ -5,7 +5,7 @@ import { RespVersions } from '@redis/client'; export default TestUtils.createFromConfig({ dockerImageName: 'redislabs/client-libs-test', dockerImageVersionArgument: 'redis-version', - defaultDockerVersion: '8.2-rc2-pre' + defaultDockerVersion: '8.2' }); export const GLOBAL = { diff --git a/packages/time-series/lib/test-utils.ts b/packages/time-series/lib/test-utils.ts index 388069ca8a7..d454a3c6b63 100644 --- a/packages/time-series/lib/test-utils.ts +++ b/packages/time-series/lib/test-utils.ts @@ -4,7 +4,7 @@ import TimeSeries from '.'; export default TestUtils.createFromConfig({ dockerImageName: 'redislabs/client-libs-test', dockerImageVersionArgument: 'redis-version', - defaultDockerVersion: '8.2-rc2-pre' + defaultDockerVersion: '8.2' }); export const GLOBAL = { From 2f106324507eec905b8fe7691ba11179acdeeca7 Mon Sep 17 00:00:00 2001 From: Nikolay Karadzhov Date: Wed, 6 Aug 2025 09:50:19 +0300 Subject: [PATCH 1685/1748] fix(commands): expire, expireAt are not readonly (#3045) fixes #3044 --- packages/client/lib/commands/EXPIRE.ts | 1 - packages/client/lib/commands/EXPIREAT.ts | 1 - 2 files changed, 2 deletions(-) diff --git a/packages/client/lib/commands/EXPIRE.ts b/packages/client/lib/commands/EXPIRE.ts index 15855832c32..985b81071a3 100644 --- a/packages/client/lib/commands/EXPIRE.ts +++ b/packages/client/lib/commands/EXPIRE.ts @@ -2,7 +2,6 @@ import { CommandParser } from '../client/parser'; import { RedisArgument, NumberReply, Command } from '../RESP/types'; export default { - IS_READ_ONLY: true, /** * Sets a timeout on key. After the timeout has expired, the key will be automatically deleted * @param parser - The Redis command parser diff --git a/packages/client/lib/commands/EXPIREAT.ts b/packages/client/lib/commands/EXPIREAT.ts index 4956b8aa23b..a20407aa78e 100644 --- a/packages/client/lib/commands/EXPIREAT.ts +++ b/packages/client/lib/commands/EXPIREAT.ts @@ -3,7 +3,6 @@ import { RedisArgument, NumberReply, Command } from '../RESP/types'; import { transformEXAT } from './generic-transformers'; export default { - IS_READ_ONLY: true, /** * Sets the expiration for a key at a specific Unix timestamp * @param parser - The Redis command parser From 68cebf7f6e05f81cae18629e49a3a17caa8e3802 Mon Sep 17 00:00:00 2001 From: Pavel Pashov <60297174+PavelPashov@users.noreply.github.com> Date: Wed, 13 Aug 2025 08:51:30 +0300 Subject: [PATCH 1686/1748] fix: parse database from Redis URL (#3052) --- packages/client/lib/client/index.spec.ts | 8 ++++++++ packages/client/lib/client/index.ts | 10 ++++++++-- 2 files changed, 16 insertions(+), 2 deletions(-) diff --git a/packages/client/lib/client/index.spec.ts b/packages/client/lib/client/index.spec.ts index 0aed98450d2..9c33f2ce847 100644 --- a/packages/client/lib/client/index.spec.ts +++ b/packages/client/lib/client/index.spec.ts @@ -167,6 +167,14 @@ describe('Client', () => { } ); }); + + it('DB in URL should be parsed', async () => { + const client = RedisClient.create({ + url: 'redis://user:secret@localhost:6379/5' + }); + + assert.equal(client?.options?.database, 5); + }) }); describe('parseOptions', () => { diff --git a/packages/client/lib/client/index.ts b/packages/client/lib/client/index.ts index ccad872e223..1a27ea88986 100644 --- a/packages/client/lib/client/index.ts +++ b/packages/client/lib/client/index.ts @@ -496,8 +496,14 @@ export default class RedisClient< this._commandOptions = options.commandOptions; } - if (options) { - return RedisClient.parseOptions(options); + if (options?.url) { + const parsedOptions = RedisClient.parseOptions(options); + + if (parsedOptions?.database) { + this._self.#selectedDB = parsedOptions.database; + } + + return parsedOptions; } return options; From 883375cf4d16f948423e1f7fd778949ec9a08e11 Mon Sep 17 00:00:00 2001 From: GitHub Action Date: Wed, 13 Aug 2025 06:22:15 +0000 Subject: [PATCH 1687/1748] Release client@5.8.1 --- package-lock.json | 14 +++++++++++++- packages/client/package.json | 2 +- 2 files changed, 14 insertions(+), 2 deletions(-) diff --git a/package-lock.json b/package-lock.json index ede6844f0be..b2209c7533b 100644 --- a/package-lock.json +++ b/package-lock.json @@ -7351,7 +7351,7 @@ }, "packages/client": { "name": "@redis/client", - "version": "5.8.0", + "version": "5.8.1", "license": "MIT", "dependencies": { "cluster-key-slot": "1.1.2" @@ -7449,6 +7449,18 @@ "node": ">= 18" } }, + "packages/redis/node_modules/@redis/client": { + "version": "5.8.0", + "resolved": "https://registry.npmjs.org/@redis/client/-/client-5.8.0.tgz", + "integrity": "sha512-ywZjKGoSSAECGYOd9bJpws6d4867SN686obUWT/sRmo1c/Q8V+jWyInvlqwKa0BOvTHHwYeB2WFUEvd6PADeOQ==", + "license": "MIT", + "dependencies": { + "cluster-key-slot": "1.1.2" + }, + "engines": { + "node": ">= 18" + } + }, "packages/search": { "name": "@redis/search", "version": "5.8.0", diff --git a/packages/client/package.json b/packages/client/package.json index b7c11d57a84..c685f60e77b 100644 --- a/packages/client/package.json +++ b/packages/client/package.json @@ -1,6 +1,6 @@ { "name": "@redis/client", - "version": "5.8.0", + "version": "5.8.1", "license": "MIT", "main": "./dist/index.js", "types": "./dist/index.d.ts", From 4900d2b2ad0328f422318021bd61df4e99874475 Mon Sep 17 00:00:00 2001 From: GitHub Action Date: Wed, 13 Aug 2025 06:22:21 +0000 Subject: [PATCH 1688/1748] Release bloom@5.8.1 --- package-lock.json | 16 ++++++++++++++-- packages/bloom/package.json | 4 ++-- 2 files changed, 16 insertions(+), 4 deletions(-) diff --git a/package-lock.json b/package-lock.json index b2209c7533b..bb7986cc951 100644 --- a/package-lock.json +++ b/package-lock.json @@ -7337,7 +7337,7 @@ }, "packages/bloom": { "name": "@redis/bloom", - "version": "5.8.0", + "version": "5.8.1", "license": "MIT", "devDependencies": { "@redis/test-utils": "*" @@ -7346,7 +7346,7 @@ "node": ">= 18" }, "peerDependencies": { - "@redis/client": "^5.8.0" + "@redis/client": "^5.8.1" } }, "packages/client": { @@ -7449,6 +7449,18 @@ "node": ">= 18" } }, + "packages/redis/node_modules/@redis/bloom": { + "version": "5.8.0", + "resolved": "https://registry.npmjs.org/@redis/bloom/-/bloom-5.8.0.tgz", + "integrity": "sha512-kpKZzAAjGiGYn88Bqq6+ozxPg6kGYWRZH9vnOwGcoSCbrW14SZpZVMYMFSio8FH9ZJUdUcmT/RLGlA1W1t0UWQ==", + "license": "MIT", + "engines": { + "node": ">= 18" + }, + "peerDependencies": { + "@redis/client": "^5.8.0" + } + }, "packages/redis/node_modules/@redis/client": { "version": "5.8.0", "resolved": "https://registry.npmjs.org/@redis/client/-/client-5.8.0.tgz", diff --git a/packages/bloom/package.json b/packages/bloom/package.json index 00af3c18690..35e65009cef 100644 --- a/packages/bloom/package.json +++ b/packages/bloom/package.json @@ -1,6 +1,6 @@ { "name": "@redis/bloom", - "version": "5.8.0", + "version": "5.8.1", "license": "MIT", "main": "./dist/lib/index.js", "types": "./dist/lib/index.d.ts", @@ -13,7 +13,7 @@ "release": "release-it" }, "peerDependencies": { - "@redis/client": "^5.8.0" + "@redis/client": "^5.8.1" }, "devDependencies": { "@redis/test-utils": "*" From 80c3dd3dc23a9e8446e2a54898c62cf04f6aadeb Mon Sep 17 00:00:00 2001 From: GitHub Action Date: Wed, 13 Aug 2025 06:22:27 +0000 Subject: [PATCH 1689/1748] Release json@5.8.1 --- package-lock.json | 16 ++++++++++++++-- packages/json/package.json | 4 ++-- 2 files changed, 16 insertions(+), 4 deletions(-) diff --git a/package-lock.json b/package-lock.json index bb7986cc951..85852311032 100644 --- a/package-lock.json +++ b/package-lock.json @@ -7423,7 +7423,7 @@ }, "packages/json": { "name": "@redis/json", - "version": "5.8.0", + "version": "5.8.1", "license": "MIT", "devDependencies": { "@redis/test-utils": "*" @@ -7432,7 +7432,7 @@ "node": ">= 18" }, "peerDependencies": { - "@redis/client": "^5.8.0" + "@redis/client": "^5.8.1" } }, "packages/redis": { @@ -7473,6 +7473,18 @@ "node": ">= 18" } }, + "packages/redis/node_modules/@redis/json": { + "version": "5.8.0", + "resolved": "https://registry.npmjs.org/@redis/json/-/json-5.8.0.tgz", + "integrity": "sha512-xPBpwY6aKoRzMSu67MpwrBiSliON9bfHo9Y/pSPBjW8/KoOm1MzGqwJUO20qdjXpFoKJsDWwxIE1LHdBNzcImw==", + "license": "MIT", + "engines": { + "node": ">= 18" + }, + "peerDependencies": { + "@redis/client": "^5.8.0" + } + }, "packages/search": { "name": "@redis/search", "version": "5.8.0", diff --git a/packages/json/package.json b/packages/json/package.json index bab27df34cd..2879186a9ca 100644 --- a/packages/json/package.json +++ b/packages/json/package.json @@ -1,6 +1,6 @@ { "name": "@redis/json", - "version": "5.8.0", + "version": "5.8.1", "license": "MIT", "main": "./dist/lib/index.js", "types": "./dist/lib/index.d.ts", @@ -13,7 +13,7 @@ "release": "release-it" }, "peerDependencies": { - "@redis/client": "^5.8.0" + "@redis/client": "^5.8.1" }, "devDependencies": { "@redis/test-utils": "*" From 603fa71fe13a6664f3e3ada2e73413fa3ad1cf89 Mon Sep 17 00:00:00 2001 From: GitHub Action Date: Wed, 13 Aug 2025 06:22:33 +0000 Subject: [PATCH 1690/1748] Release search@5.8.1 --- package-lock.json | 16 ++++++++++++++-- packages/search/package.json | 4 ++-- 2 files changed, 16 insertions(+), 4 deletions(-) diff --git a/package-lock.json b/package-lock.json index 85852311032..70cfac04da1 100644 --- a/package-lock.json +++ b/package-lock.json @@ -7485,9 +7485,21 @@ "@redis/client": "^5.8.0" } }, + "packages/redis/node_modules/@redis/search": { + "version": "5.8.0", + "resolved": "https://registry.npmjs.org/@redis/search/-/search-5.8.0.tgz", + "integrity": "sha512-lF9pNv9vySmirm1EzCH6YeRjhvH6lLT7tvebYHEM7WTkEQ/7kZWb4athliKESHpxzTQ36U9UbzuedSywHV6OhQ==", + "license": "MIT", + "engines": { + "node": ">= 18" + }, + "peerDependencies": { + "@redis/client": "^5.8.0" + } + }, "packages/search": { "name": "@redis/search", - "version": "5.8.0", + "version": "5.8.1", "license": "MIT", "devDependencies": { "@redis/test-utils": "*" @@ -7496,7 +7508,7 @@ "node": ">= 18" }, "peerDependencies": { - "@redis/client": "^5.8.0" + "@redis/client": "^5.8.1" } }, "packages/test-utils": { diff --git a/packages/search/package.json b/packages/search/package.json index 8095a418d66..866371ba118 100644 --- a/packages/search/package.json +++ b/packages/search/package.json @@ -1,6 +1,6 @@ { "name": "@redis/search", - "version": "5.8.0", + "version": "5.8.1", "license": "MIT", "main": "./dist/lib/index.js", "types": "./dist/lib/index.d.ts", @@ -14,7 +14,7 @@ "release": "release-it" }, "peerDependencies": { - "@redis/client": "^5.8.0" + "@redis/client": "^5.8.1" }, "devDependencies": { "@redis/test-utils": "*" From c0e6c788732aa6c88435f7a9a6133f23bea40d08 Mon Sep 17 00:00:00 2001 From: GitHub Action Date: Wed, 13 Aug 2025 06:22:39 +0000 Subject: [PATCH 1691/1748] Release time-series@5.8.1 --- package-lock.json | 16 ++++++++++++++-- packages/time-series/package.json | 4 ++-- 2 files changed, 16 insertions(+), 4 deletions(-) diff --git a/package-lock.json b/package-lock.json index 70cfac04da1..0abb53badd7 100644 --- a/package-lock.json +++ b/package-lock.json @@ -7497,6 +7497,18 @@ "@redis/client": "^5.8.0" } }, + "packages/redis/node_modules/@redis/time-series": { + "version": "5.8.0", + "resolved": "https://registry.npmjs.org/@redis/time-series/-/time-series-5.8.0.tgz", + "integrity": "sha512-kPTlW2ACXokjQNXjCD8Pw9mHDoB94AHUlHFahyjxz9lUJUVwiva2Dgfrd2k3JxHhSBqyY2PREIj9YwIUSTSSqQ==", + "license": "MIT", + "engines": { + "node": ">= 18" + }, + "peerDependencies": { + "@redis/client": "^5.8.0" + } + }, "packages/search": { "name": "@redis/search", "version": "5.8.1", @@ -7577,7 +7589,7 @@ }, "packages/time-series": { "name": "@redis/time-series", - "version": "5.8.0", + "version": "5.8.1", "license": "MIT", "devDependencies": { "@redis/test-utils": "*" @@ -7586,7 +7598,7 @@ "node": ">= 18" }, "peerDependencies": { - "@redis/client": "^5.8.0" + "@redis/client": "^5.8.1" } } } diff --git a/packages/time-series/package.json b/packages/time-series/package.json index 0ca01943a5a..b22bbd9cb08 100644 --- a/packages/time-series/package.json +++ b/packages/time-series/package.json @@ -1,6 +1,6 @@ { "name": "@redis/time-series", - "version": "5.8.0", + "version": "5.8.1", "license": "MIT", "main": "./dist/lib/index.js", "types": "./dist/lib/index.d.ts", @@ -13,7 +13,7 @@ "release": "release-it" }, "peerDependencies": { - "@redis/client": "^5.8.0" + "@redis/client": "^5.8.1" }, "devDependencies": { "@redis/test-utils": "*" From 4e5e31dc554b2c9bf1bbfa23be2dd502da4f4385 Mon Sep 17 00:00:00 2001 From: GitHub Action Date: Wed, 13 Aug 2025 06:22:45 +0000 Subject: [PATCH 1692/1748] Release entraid@5.8.1 --- package-lock.json | 4 ++-- packages/entraid/package.json | 4 ++-- 2 files changed, 4 insertions(+), 4 deletions(-) diff --git a/package-lock.json b/package-lock.json index 0abb53badd7..b7a57379ee5 100644 --- a/package-lock.json +++ b/package-lock.json @@ -7367,7 +7367,7 @@ }, "packages/entraid": { "name": "@redis/entraid", - "version": "5.8.0", + "version": "5.8.1", "license": "MIT", "dependencies": { "@azure/identity": "^4.7.0", @@ -7386,7 +7386,7 @@ "node": ">= 18" }, "peerDependencies": { - "@redis/client": "^5.8.0" + "@redis/client": "^5.8.1" } }, "packages/entraid/node_modules/@types/node": { diff --git a/packages/entraid/package.json b/packages/entraid/package.json index 2ef4cfca794..2eee504a1fd 100644 --- a/packages/entraid/package.json +++ b/packages/entraid/package.json @@ -1,6 +1,6 @@ { "name": "@redis/entraid", - "version": "5.8.0", + "version": "5.8.1", "license": "MIT", "main": "./dist/index.js", "types": "./dist/index.d.ts", @@ -22,7 +22,7 @@ "@azure/msal-node": "^2.16.1" }, "peerDependencies": { - "@redis/client": "^5.8.0" + "@redis/client": "^5.8.1" }, "devDependencies": { "@types/express": "^4.17.21", From cafdc6345995851d6dab05d6292acb9331083483 Mon Sep 17 00:00:00 2001 From: GitHub Action Date: Wed, 13 Aug 2025 06:22:51 +0000 Subject: [PATCH 1693/1748] Release redis@5.8.1 --- package-lock.json | 72 ++++--------------------------------- packages/redis/package.json | 12 +++---- 2 files changed, 12 insertions(+), 72 deletions(-) diff --git a/package-lock.json b/package-lock.json index b7a57379ee5..b4fa4834c61 100644 --- a/package-lock.json +++ b/package-lock.json @@ -7436,79 +7436,19 @@ } }, "packages/redis": { - "version": "5.8.0", - "license": "MIT", - "dependencies": { - "@redis/bloom": "5.8.0", - "@redis/client": "5.8.0", - "@redis/json": "5.8.0", - "@redis/search": "5.8.0", - "@redis/time-series": "5.8.0" - }, - "engines": { - "node": ">= 18" - } - }, - "packages/redis/node_modules/@redis/bloom": { - "version": "5.8.0", - "resolved": "https://registry.npmjs.org/@redis/bloom/-/bloom-5.8.0.tgz", - "integrity": "sha512-kpKZzAAjGiGYn88Bqq6+ozxPg6kGYWRZH9vnOwGcoSCbrW14SZpZVMYMFSio8FH9ZJUdUcmT/RLGlA1W1t0UWQ==", - "license": "MIT", - "engines": { - "node": ">= 18" - }, - "peerDependencies": { - "@redis/client": "^5.8.0" - } - }, - "packages/redis/node_modules/@redis/client": { - "version": "5.8.0", - "resolved": "https://registry.npmjs.org/@redis/client/-/client-5.8.0.tgz", - "integrity": "sha512-ywZjKGoSSAECGYOd9bJpws6d4867SN686obUWT/sRmo1c/Q8V+jWyInvlqwKa0BOvTHHwYeB2WFUEvd6PADeOQ==", + "version": "5.8.1", "license": "MIT", "dependencies": { - "cluster-key-slot": "1.1.2" + "@redis/bloom": "5.8.1", + "@redis/client": "5.8.1", + "@redis/json": "5.8.1", + "@redis/search": "5.8.1", + "@redis/time-series": "5.8.1" }, "engines": { "node": ">= 18" } }, - "packages/redis/node_modules/@redis/json": { - "version": "5.8.0", - "resolved": "https://registry.npmjs.org/@redis/json/-/json-5.8.0.tgz", - "integrity": "sha512-xPBpwY6aKoRzMSu67MpwrBiSliON9bfHo9Y/pSPBjW8/KoOm1MzGqwJUO20qdjXpFoKJsDWwxIE1LHdBNzcImw==", - "license": "MIT", - "engines": { - "node": ">= 18" - }, - "peerDependencies": { - "@redis/client": "^5.8.0" - } - }, - "packages/redis/node_modules/@redis/search": { - "version": "5.8.0", - "resolved": "https://registry.npmjs.org/@redis/search/-/search-5.8.0.tgz", - "integrity": "sha512-lF9pNv9vySmirm1EzCH6YeRjhvH6lLT7tvebYHEM7WTkEQ/7kZWb4athliKESHpxzTQ36U9UbzuedSywHV6OhQ==", - "license": "MIT", - "engines": { - "node": ">= 18" - }, - "peerDependencies": { - "@redis/client": "^5.8.0" - } - }, - "packages/redis/node_modules/@redis/time-series": { - "version": "5.8.0", - "resolved": "https://registry.npmjs.org/@redis/time-series/-/time-series-5.8.0.tgz", - "integrity": "sha512-kPTlW2ACXokjQNXjCD8Pw9mHDoB94AHUlHFahyjxz9lUJUVwiva2Dgfrd2k3JxHhSBqyY2PREIj9YwIUSTSSqQ==", - "license": "MIT", - "engines": { - "node": ">= 18" - }, - "peerDependencies": { - "@redis/client": "^5.8.0" - } - }, "packages/search": { "name": "@redis/search", "version": "5.8.1", diff --git a/packages/redis/package.json b/packages/redis/package.json index c3cbc01442e..de4ac275ed0 100644 --- a/packages/redis/package.json +++ b/packages/redis/package.json @@ -1,7 +1,7 @@ { "name": "redis", "description": "A modern, high performance Redis client", - "version": "5.8.0", + "version": "5.8.1", "license": "MIT", "main": "./dist/index.js", "types": "./dist/index.d.ts", @@ -13,11 +13,11 @@ "release": "release-it" }, "dependencies": { - "@redis/bloom": "5.8.0", - "@redis/client": "5.8.0", - "@redis/json": "5.8.0", - "@redis/search": "5.8.0", - "@redis/time-series": "5.8.0" + "@redis/bloom": "5.8.1", + "@redis/client": "5.8.1", + "@redis/json": "5.8.1", + "@redis/search": "5.8.1", + "@redis/time-series": "5.8.1" }, "engines": { "node": ">= 18" From 746e9b184b84e98383d7fac47f2a219cbb75d970 Mon Sep 17 00:00:00 2001 From: Nathan Friedly Date: Wed, 13 Aug 2025 15:26:14 -0400 Subject: [PATCH 1694/1748] docs: Clustering sendCommand docs (#3053) We noticed that `sendCommand()` takes different arguments for clusters vs clients, and I wanted to document the differences. I think I got it correct, but please review closely just to be sure. --- docs/clustering.md | 20 ++++++++++++++++++++ 1 file changed, 20 insertions(+) diff --git a/docs/clustering.md b/docs/clustering.md index 6803583fa51..3e4f8446b6a 100644 --- a/docs/clustering.md +++ b/docs/clustering.md @@ -38,6 +38,25 @@ await cluster.close(); | scripts | | Script definitions (see [Lua Scripts](./programmability.md#lua-scripts)) | | functions | | Function definitions (see [Functions](./programmability.md#functions)) | +## Usage + +Most redis commands are the same as with individual clients. + +### Unsupported Redis Commands + +If you want to run commands and/or use arguments that Node Redis doesn't know about (yet!) use `.sendCommand()`. + +When clustering, `sendCommand` takes 3 arguments to help with routing to the correct redis node: +* `firstKey`: the key that is being operated on, or `undefined` to route to a random node. +* `isReadOnly`: determines if the command needs to go to the master or may go to a replica. +* `args`: the command and all arguments (including the key), as an array of strings. + +```javascript +await cluster.sendCommand("key", false, ["SET", "key", "value", "NX"]); // 'OK' + +await cluster.sendCommand("key", true, ["HGETALL", "key"]); // ['key1', 'field1', 'key2', 'field2'] +``` + ## Auth with password and username Specifying the password in the URL or a root node will only affect the connection to that specific node. In case you want to set the password for all the connections being created from a cluster instance, use the `defaults` option. @@ -114,3 +133,4 @@ Admin commands such as `MEMORY STATS`, `FLUSHALL`, etc. are not attached to the ### "Forwarded Commands" Certain commands (e.g. `PUBLISH`) are forwarded to other cluster nodes by the Redis server. The client sends these commands to a random node in order to spread the load across the cluster. + From 82847fb92c07b86ba72eba364ce879e0249009d5 Mon Sep 17 00:00:00 2001 From: Arek W Date: Thu, 14 Aug 2025 09:42:36 +0200 Subject: [PATCH 1695/1748] fix: Stop erasing `ErrorReply` stack (#3050) It was very difficult to debug `ErrorReply` errors due to `error.stack` being erased. Given this restores standard JS Error behaviour, I have not added any tests. --- packages/client/lib/errors.ts | 7 +------ 1 file changed, 1 insertion(+), 6 deletions(-) diff --git a/packages/client/lib/errors.ts b/packages/client/lib/errors.ts index db37ec1a9ba..5cb9166df02 100644 --- a/packages/client/lib/errors.ts +++ b/packages/client/lib/errors.ts @@ -63,12 +63,7 @@ export class ReconnectStrategyError extends Error { } } -export class ErrorReply extends Error { - constructor(message: string) { - super(message); - this.stack = undefined; - } -} +export class ErrorReply extends Error {} export class SimpleError extends ErrorReply {} From fceb60968e3ebc36dbb457a24b8cbb1ca0db01ae Mon Sep 17 00:00:00 2001 From: Nathan Friedly Date: Thu, 14 Aug 2025 03:49:05 -0400 Subject: [PATCH 1696/1748] docs: Call out sendCommand cluster difference in readme (#3054) A followup to https://github.com/redis/node-redis/pull/3053 --- README.md | 2 ++ 1 file changed, 2 insertions(+) diff --git a/README.md b/README.md index 9948858db9f..05c55985b33 100644 --- a/README.md +++ b/README.md @@ -143,6 +143,8 @@ await client.sendCommand(["SET", "key", "value", "NX"]); // 'OK' await client.sendCommand(["HGETALL", "key"]); // ['key1', 'field1', 'key2', 'field2'] ``` +_Note: the [API is different when using a cluster](https://github.com/redis/node-redis/blob/master/docs/clustering.md#unsupported-redis-commands)._ + ### Transactions (Multi/Exec) Start a [transaction](https://redis.io/topics/transactions) by calling `.multi()`, then chaining your commands. When From ed6aca7d03339a84515b2cf30b17161bcdb375f5 Mon Sep 17 00:00:00 2001 From: Manuel Spigolon Date: Mon, 18 Aug 2025 10:22:44 +0200 Subject: [PATCH 1697/1748] fix(ts): xtrim threshold accepts string (#3058) * fix(ts): xtrim threshold accepts string * test: check MINID with text id --- packages/client/lib/commands/XTRIM.spec.ts | 15 +++++++++++++++ packages/client/lib/commands/XTRIM.ts | 2 +- 2 files changed, 16 insertions(+), 1 deletion(-) diff --git a/packages/client/lib/commands/XTRIM.spec.ts b/packages/client/lib/commands/XTRIM.spec.ts index b88cf84676e..38254d565e9 100644 --- a/packages/client/lib/commands/XTRIM.spec.ts +++ b/packages/client/lib/commands/XTRIM.spec.ts @@ -18,6 +18,11 @@ describe('XTRIM', () => { parseArgs(XTRIM, 'key', 'MINID', 123), ['XTRIM', 'key', 'MINID', '123'] ); + + assert.deepEqual( + parseArgs(XTRIM, 'key', 'MINID', '0-0'), + ['XTRIM', 'key', 'MINID', '0-0'] + ); }); it('with strategyModifier', () => { @@ -89,6 +94,16 @@ describe('XTRIM', () => { cluster: GLOBAL.CLUSTERS.OPEN, }); + testUtils.testAll('xTrim with string MINID', async client => { + assert.equal( + typeof await client.xTrim('key', 'MINID', '0-0'), + 'number' + ); + }, { + client: GLOBAL.SERVERS.OPEN, + cluster: GLOBAL.CLUSTERS.OPEN, + }); + testUtils.testAll( 'xTrim with LIMIT', async (client) => { diff --git a/packages/client/lib/commands/XTRIM.ts b/packages/client/lib/commands/XTRIM.ts index 34171d4611e..8d40824d791 100644 --- a/packages/client/lib/commands/XTRIM.ts +++ b/packages/client/lib/commands/XTRIM.ts @@ -37,7 +37,7 @@ export default { parser: CommandParser, key: RedisArgument, strategy: 'MAXLEN' | 'MINID', - threshold: number, + threshold: number | string, options?: XTrimOptions ) { parser.push('XTRIM') From d5423b93d639a8f8e8e612e49034c01e69639cb9 Mon Sep 17 00:00:00 2001 From: Nikolay Karadzhov Date: Mon, 18 Aug 2025 11:32:41 +0300 Subject: [PATCH 1698/1748] chore(tests): bump test container version 8.2.1-pre (#3057) --- .github/workflows/tests.yml | 2 +- packages/bloom/lib/test-utils.ts | 2 +- packages/client/lib/sentinel/test-util.ts | 2 +- packages/client/lib/test-utils.ts | 2 +- packages/entraid/lib/test-utils.ts | 2 +- packages/json/lib/test-utils.ts | 2 +- packages/search/lib/test-utils.ts | 2 +- packages/test-utils/lib/test-utils.ts | 2 +- packages/time-series/lib/test-utils.ts | 2 +- 9 files changed, 9 insertions(+), 9 deletions(-) diff --git a/.github/workflows/tests.yml b/.github/workflows/tests.yml index 9753535b53d..f522282d28d 100644 --- a/.github/workflows/tests.yml +++ b/.github/workflows/tests.yml @@ -22,7 +22,7 @@ jobs: fail-fast: false matrix: node-version: ["18", "20", "22"] - redis-version: ["rs-7.4.0-v1", "8.0.2", "8.2"] + redis-version: ["rs-7.4.0-v1", "8.0.2", "8.2", "8.2.1-pre"] steps: - uses: actions/checkout@v4 with: diff --git a/packages/bloom/lib/test-utils.ts b/packages/bloom/lib/test-utils.ts index 64bc3484090..c2991de60d0 100644 --- a/packages/bloom/lib/test-utils.ts +++ b/packages/bloom/lib/test-utils.ts @@ -4,7 +4,7 @@ import RedisBloomModules from '.'; export default TestUtils.createFromConfig({ dockerImageName: 'redislabs/client-libs-test', dockerImageVersionArgument: 'redis-version', - defaultDockerVersion: '8.2' + defaultDockerVersion: '8.2.1-pre' }); export const GLOBAL = { diff --git a/packages/client/lib/sentinel/test-util.ts b/packages/client/lib/sentinel/test-util.ts index 1f8d75a76da..33e8e330c25 100644 --- a/packages/client/lib/sentinel/test-util.ts +++ b/packages/client/lib/sentinel/test-util.ts @@ -174,7 +174,7 @@ export class SentinelFramework extends DockerBase { this.#testUtils = TestUtils.createFromConfig({ dockerImageName: 'redislabs/client-libs-test', dockerImageVersionArgument: 'redis-version', - defaultDockerVersion: '8.2' + defaultDockerVersion: '8.2.1-pre' }); this.#nodeMap = new Map>>>(); this.#sentinelMap = new Map>>>(); diff --git a/packages/client/lib/test-utils.ts b/packages/client/lib/test-utils.ts index d6cb67aa01d..e54a7d7647c 100644 --- a/packages/client/lib/test-utils.ts +++ b/packages/client/lib/test-utils.ts @@ -9,7 +9,7 @@ import RedisBloomModules from '@redis/bloom'; const utils = TestUtils.createFromConfig({ dockerImageName: 'redislabs/client-libs-test', dockerImageVersionArgument: 'redis-version', - defaultDockerVersion: '8.2' + defaultDockerVersion: '8.2.1-pre' }); export default utils; diff --git a/packages/entraid/lib/test-utils.ts b/packages/entraid/lib/test-utils.ts index 2a240db3418..7a624902f5a 100644 --- a/packages/entraid/lib/test-utils.ts +++ b/packages/entraid/lib/test-utils.ts @@ -6,7 +6,7 @@ import { EntraidCredentialsProvider } from './entraid-credentials-provider'; export const testUtils = TestUtils.createFromConfig({ dockerImageName: 'redislabs/client-libs-test', dockerImageVersionArgument: 'redis-version', - defaultDockerVersion: '8.2' + defaultDockerVersion: '8.2.1-pre' }); const DEBUG_MODE_ARGS = testUtils.isVersionGreaterThan([7]) ? diff --git a/packages/json/lib/test-utils.ts b/packages/json/lib/test-utils.ts index 629c2a5fd60..2cc3804fe90 100644 --- a/packages/json/lib/test-utils.ts +++ b/packages/json/lib/test-utils.ts @@ -4,7 +4,7 @@ import RedisJSON from '.'; export default TestUtils.createFromConfig({ dockerImageName: 'redislabs/client-libs-test', dockerImageVersionArgument: 'redis-version', - defaultDockerVersion: '8.2' + defaultDockerVersion: '8.2.1-pre' }); export const GLOBAL = { diff --git a/packages/search/lib/test-utils.ts b/packages/search/lib/test-utils.ts index ed1f864ef2e..9b82816cd4f 100644 --- a/packages/search/lib/test-utils.ts +++ b/packages/search/lib/test-utils.ts @@ -5,7 +5,7 @@ import { RespVersions } from '@redis/client'; export default TestUtils.createFromConfig({ dockerImageName: 'redislabs/client-libs-test', dockerImageVersionArgument: 'redis-version', - defaultDockerVersion: '8.2' + defaultDockerVersion: '8.2.1-pre' }); export const GLOBAL = { diff --git a/packages/test-utils/lib/test-utils.ts b/packages/test-utils/lib/test-utils.ts index fe27bd93d37..11364493091 100644 --- a/packages/test-utils/lib/test-utils.ts +++ b/packages/test-utils/lib/test-utils.ts @@ -3,7 +3,7 @@ import TestUtils from './index' export const testUtils = TestUtils.createFromConfig({ dockerImageName: 'redislabs/client-libs-test', dockerImageVersionArgument: 'redis-version', - defaultDockerVersion: '8.2-M01-pre' + defaultDockerVersion: '8.2.1-pre' }); diff --git a/packages/time-series/lib/test-utils.ts b/packages/time-series/lib/test-utils.ts index d454a3c6b63..2c345961630 100644 --- a/packages/time-series/lib/test-utils.ts +++ b/packages/time-series/lib/test-utils.ts @@ -4,7 +4,7 @@ import TimeSeries from '.'; export default TestUtils.createFromConfig({ dockerImageName: 'redislabs/client-libs-test', dockerImageVersionArgument: 'redis-version', - defaultDockerVersion: '8.2' + defaultDockerVersion: '8.2.1-pre' }); export const GLOBAL = { From e347d566cb824c9945d8fe86bd80cc69eeb0195c Mon Sep 17 00:00:00 2001 From: Nikolay Karadzhov Date: Wed, 20 Aug 2025 11:11:34 +0300 Subject: [PATCH 1699/1748] fix(sentinel): properly pass reconnectStrategy (#3063) Properly pass reconnectStrategy to master/replica nodes. Before that strategies passed in the nodeClientOptions.socket object were ignored. fixes #3061 --- packages/client/lib/sentinel/index.ts | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/packages/client/lib/sentinel/index.ts b/packages/client/lib/sentinel/index.ts index b3f3bbf0b8d..63c45862935 100644 --- a/packages/client/lib/sentinel/index.ts +++ b/packages/client/lib/sentinel/index.ts @@ -716,7 +716,7 @@ class RedisSentinelInternal< ); } - #createClient(node: RedisNode, clientOptions: RedisClientOptions, reconnectStrategy?: undefined | false) { + #createClient(node: RedisNode, clientOptions: RedisClientOptions, reconnectStrategy?: false) { return RedisClient.create({ //first take the globally set RESP RESP: this.#RESP, @@ -726,7 +726,7 @@ class RedisSentinelInternal< ...clientOptions.socket, host: node.host, port: node.port, - reconnectStrategy + ...(reconnectStrategy !== undefined && { reconnectStrategy }) } }); } From b9a5d3664087231ae0bb006af4ae040874735c95 Mon Sep 17 00:00:00 2001 From: Nikolay Karadzhov Date: Wed, 20 Aug 2025 11:12:25 +0300 Subject: [PATCH 1700/1748] fix(search): properly decide if response has docs (#3060) fixes: #3056 --- packages/search/lib/commands/SEARCH.spec.ts | 79 +++++++++++++++++++++ packages/search/lib/commands/SEARCH.ts | 3 +- 2 files changed, 81 insertions(+), 1 deletion(-) diff --git a/packages/search/lib/commands/SEARCH.spec.ts b/packages/search/lib/commands/SEARCH.spec.ts index ab480808ffa..97e1a9a9885 100644 --- a/packages/search/lib/commands/SEARCH.spec.ts +++ b/packages/search/lib/commands/SEARCH.spec.ts @@ -326,5 +326,84 @@ describe('FT.SEARCH', () => { } ); }, GLOBAL.SERVERS.OPEN); + + testUtils.testWithClient('properly parse content/nocontent scenarios', async client => { + + const indexName = 'foo'; + await client.ft.create( + indexName, + { + itemOrder: { + type: 'NUMERIC', + SORTABLE: true, + }, + name: { + type: 'TEXT', + }, + }, + { + ON: 'HASH', + PREFIX: 'item:', + } + ); + + await client.hSet("item:1", { + itemOrder: 1, + name: "First item", + }); + + await client.hSet("item:2", { + itemOrder: 2, + name: "Second item", + }); + + await client.hSet("item:3", { + itemOrder: 3, + name: "Third item", + }); + + // Search with SORTBY and LIMIT + let result = await client.ft.search(indexName, "@itemOrder:[0 10]", { + SORTBY: { + BY: "itemOrder", + DIRECTION: "ASC", + }, + LIMIT: { + from: 0, + size: 1, // only get first result + }, + }); + + assert.equal(result.total, 3, "Result's `total` value reflects the total scanned documents"); + assert.equal(result.documents.length, 1); + let doc = result.documents[0]; + assert.equal(doc.id, 'item:1'); + assert.equal(doc.value.itemOrder, '1'); + assert.equal(doc.value.name, 'First item'); + + await client.del("item:3"); + + // Search again after removing item:3 + result = await client.ft.search(indexName, "@itemOrder:[0 10]", { + SORTBY: { + BY: "itemOrder", + DIRECTION: "ASC", + }, + LIMIT: { + from: 0, + size: 1, // only get first result + }, + }); + + assert.equal(result.total, 2, "Result's `total` value reflects the total scanned documents"); + assert.equal(result.documents.length, 1); + doc = result.documents[0]; + assert.equal(doc.id, 'item:1'); + assert.equal(doc.value.itemOrder, '1'); + assert.equal(doc.value.name, 'First item'); + + + }, GLOBAL.SERVERS.OPEN); + }); }); diff --git a/packages/search/lib/commands/SEARCH.ts b/packages/search/lib/commands/SEARCH.ts index 61e1d8d84d2..03779a446cc 100644 --- a/packages/search/lib/commands/SEARCH.ts +++ b/packages/search/lib/commands/SEARCH.ts @@ -183,7 +183,8 @@ export default { }, transformReply: { 2: (reply: SearchRawReply): SearchReply => { - const withoutDocuments = (reply[0] + 1 == reply.length) + // if reply[2] is array, then we have content/documents. Otherwise, only ids + const withoutDocuments = reply.length > 2 && !Array.isArray(reply[2]); const documents = []; let i = 1; From 2e660120996d29ca7e4093e1fafdb03bb6374618 Mon Sep 17 00:00:00 2001 From: GitHub Action Date: Wed, 20 Aug 2025 08:33:04 +0000 Subject: [PATCH 1701/1748] Release client@5.8.2 --- package-lock.json | 14 +++++++++++++- packages/client/package.json | 2 +- 2 files changed, 14 insertions(+), 2 deletions(-) diff --git a/package-lock.json b/package-lock.json index b4fa4834c61..f99a8dbf5e7 100644 --- a/package-lock.json +++ b/package-lock.json @@ -7351,7 +7351,7 @@ }, "packages/client": { "name": "@redis/client", - "version": "5.8.1", + "version": "5.8.2", "license": "MIT", "dependencies": { "cluster-key-slot": "1.1.2" @@ -7449,6 +7449,18 @@ "node": ">= 18" } }, + "packages/redis/node_modules/@redis/client": { + "version": "5.8.1", + "resolved": "https://registry.npmjs.org/@redis/client/-/client-5.8.1.tgz", + "integrity": "sha512-hD5Tvv7G0t8b3w8ao3kQ4jEPUmUUC6pqA18c8ciYF5xZGfUGBg0olQHW46v6qSt4O5bxOuB3uV7pM6H5wEjBwA==", + "license": "MIT", + "dependencies": { + "cluster-key-slot": "1.1.2" + }, + "engines": { + "node": ">= 18" + } + }, "packages/search": { "name": "@redis/search", "version": "5.8.1", diff --git a/packages/client/package.json b/packages/client/package.json index c685f60e77b..1332083bf18 100644 --- a/packages/client/package.json +++ b/packages/client/package.json @@ -1,6 +1,6 @@ { "name": "@redis/client", - "version": "5.8.1", + "version": "5.8.2", "license": "MIT", "main": "./dist/index.js", "types": "./dist/index.d.ts", From 0a5d076cf511204da8ac8b87601e4298ea731b97 Mon Sep 17 00:00:00 2001 From: GitHub Action Date: Wed, 20 Aug 2025 08:33:10 +0000 Subject: [PATCH 1702/1748] Release bloom@5.8.2 --- package-lock.json | 16 ++++++++++++++-- packages/bloom/package.json | 4 ++-- 2 files changed, 16 insertions(+), 4 deletions(-) diff --git a/package-lock.json b/package-lock.json index f99a8dbf5e7..3c45f421cf9 100644 --- a/package-lock.json +++ b/package-lock.json @@ -7337,7 +7337,7 @@ }, "packages/bloom": { "name": "@redis/bloom", - "version": "5.8.1", + "version": "5.8.2", "license": "MIT", "devDependencies": { "@redis/test-utils": "*" @@ -7346,7 +7346,7 @@ "node": ">= 18" }, "peerDependencies": { - "@redis/client": "^5.8.1" + "@redis/client": "^5.8.2" } }, "packages/client": { @@ -7449,6 +7449,18 @@ "node": ">= 18" } }, + "packages/redis/node_modules/@redis/bloom": { + "version": "5.8.1", + "resolved": "https://registry.npmjs.org/@redis/bloom/-/bloom-5.8.1.tgz", + "integrity": "sha512-hJOJr/yX6BttnyZ+nxD3Ddiu2lPig4XJjyAK1v7OSHOJNUTfn3RHBryB9wgnBMBdkg9glVh2AjItxIXmr600MA==", + "license": "MIT", + "engines": { + "node": ">= 18" + }, + "peerDependencies": { + "@redis/client": "^5.8.1" + } + }, "packages/redis/node_modules/@redis/client": { "version": "5.8.1", "resolved": "https://registry.npmjs.org/@redis/client/-/client-5.8.1.tgz", diff --git a/packages/bloom/package.json b/packages/bloom/package.json index 35e65009cef..e2ff5a8b42d 100644 --- a/packages/bloom/package.json +++ b/packages/bloom/package.json @@ -1,6 +1,6 @@ { "name": "@redis/bloom", - "version": "5.8.1", + "version": "5.8.2", "license": "MIT", "main": "./dist/lib/index.js", "types": "./dist/lib/index.d.ts", @@ -13,7 +13,7 @@ "release": "release-it" }, "peerDependencies": { - "@redis/client": "^5.8.1" + "@redis/client": "^5.8.2" }, "devDependencies": { "@redis/test-utils": "*" From 5e575b9550e8f970c364189026b2e44e966ccd74 Mon Sep 17 00:00:00 2001 From: GitHub Action Date: Wed, 20 Aug 2025 08:33:16 +0000 Subject: [PATCH 1703/1748] Release json@5.8.2 --- package-lock.json | 16 ++++++++++++++-- packages/json/package.json | 4 ++-- 2 files changed, 16 insertions(+), 4 deletions(-) diff --git a/package-lock.json b/package-lock.json index 3c45f421cf9..c2a56173c62 100644 --- a/package-lock.json +++ b/package-lock.json @@ -7423,7 +7423,7 @@ }, "packages/json": { "name": "@redis/json", - "version": "5.8.1", + "version": "5.8.2", "license": "MIT", "devDependencies": { "@redis/test-utils": "*" @@ -7432,7 +7432,7 @@ "node": ">= 18" }, "peerDependencies": { - "@redis/client": "^5.8.1" + "@redis/client": "^5.8.2" } }, "packages/redis": { @@ -7473,6 +7473,18 @@ "node": ">= 18" } }, + "packages/redis/node_modules/@redis/json": { + "version": "5.8.1", + "resolved": "https://registry.npmjs.org/@redis/json/-/json-5.8.1.tgz", + "integrity": "sha512-kyvM8Vn+WjJI++nRsIoI9TbdfCs1/TgD0Hp7Z7GiG6W4IEBzkXGQakli+R5BoJzUfgh7gED2fkncYy1NLprMNg==", + "license": "MIT", + "engines": { + "node": ">= 18" + }, + "peerDependencies": { + "@redis/client": "^5.8.1" + } + }, "packages/search": { "name": "@redis/search", "version": "5.8.1", diff --git a/packages/json/package.json b/packages/json/package.json index 2879186a9ca..ff689dd17ee 100644 --- a/packages/json/package.json +++ b/packages/json/package.json @@ -1,6 +1,6 @@ { "name": "@redis/json", - "version": "5.8.1", + "version": "5.8.2", "license": "MIT", "main": "./dist/lib/index.js", "types": "./dist/lib/index.d.ts", @@ -13,7 +13,7 @@ "release": "release-it" }, "peerDependencies": { - "@redis/client": "^5.8.1" + "@redis/client": "^5.8.2" }, "devDependencies": { "@redis/test-utils": "*" From c73a8c6569e5031ec545f1124e6d502e5a772528 Mon Sep 17 00:00:00 2001 From: GitHub Action Date: Wed, 20 Aug 2025 08:33:23 +0000 Subject: [PATCH 1704/1748] Release search@5.8.2 --- package-lock.json | 16 ++++++++++++++-- packages/search/package.json | 4 ++-- 2 files changed, 16 insertions(+), 4 deletions(-) diff --git a/package-lock.json b/package-lock.json index c2a56173c62..71e161933f4 100644 --- a/package-lock.json +++ b/package-lock.json @@ -7485,9 +7485,21 @@ "@redis/client": "^5.8.1" } }, + "packages/redis/node_modules/@redis/search": { + "version": "5.8.1", + "resolved": "https://registry.npmjs.org/@redis/search/-/search-5.8.1.tgz", + "integrity": "sha512-CzuKNTInTNQkxqehSn7QiYcM+th+fhjQn5ilTvksP1wPjpxqK0qWt92oYg3XZc3tO2WuXkqDvTujc4D7kb6r/A==", + "license": "MIT", + "engines": { + "node": ">= 18" + }, + "peerDependencies": { + "@redis/client": "^5.8.1" + } + }, "packages/search": { "name": "@redis/search", - "version": "5.8.1", + "version": "5.8.2", "license": "MIT", "devDependencies": { "@redis/test-utils": "*" @@ -7496,7 +7508,7 @@ "node": ">= 18" }, "peerDependencies": { - "@redis/client": "^5.8.1" + "@redis/client": "^5.8.2" } }, "packages/test-utils": { diff --git a/packages/search/package.json b/packages/search/package.json index 866371ba118..40238080e8b 100644 --- a/packages/search/package.json +++ b/packages/search/package.json @@ -1,6 +1,6 @@ { "name": "@redis/search", - "version": "5.8.1", + "version": "5.8.2", "license": "MIT", "main": "./dist/lib/index.js", "types": "./dist/lib/index.d.ts", @@ -14,7 +14,7 @@ "release": "release-it" }, "peerDependencies": { - "@redis/client": "^5.8.1" + "@redis/client": "^5.8.2" }, "devDependencies": { "@redis/test-utils": "*" From 01157a014767d79d0808c069e21ebcc0c423f33b Mon Sep 17 00:00:00 2001 From: GitHub Action Date: Wed, 20 Aug 2025 08:33:29 +0000 Subject: [PATCH 1705/1748] Release time-series@5.8.2 --- package-lock.json | 16 ++++++++++++++-- packages/time-series/package.json | 4 ++-- 2 files changed, 16 insertions(+), 4 deletions(-) diff --git a/package-lock.json b/package-lock.json index 71e161933f4..abcc27d1bda 100644 --- a/package-lock.json +++ b/package-lock.json @@ -7497,6 +7497,18 @@ "@redis/client": "^5.8.1" } }, + "packages/redis/node_modules/@redis/time-series": { + "version": "5.8.1", + "resolved": "https://registry.npmjs.org/@redis/time-series/-/time-series-5.8.1.tgz", + "integrity": "sha512-klvdR96U9oSOyqvcectoAGhYlMOnMS3I5UWUOgdBn1buMODiwM/E4Eds7gxldKmtowe4rLJSF1CyIqyZTjy8Ow==", + "license": "MIT", + "engines": { + "node": ">= 18" + }, + "peerDependencies": { + "@redis/client": "^5.8.1" + } + }, "packages/search": { "name": "@redis/search", "version": "5.8.2", @@ -7577,7 +7589,7 @@ }, "packages/time-series": { "name": "@redis/time-series", - "version": "5.8.1", + "version": "5.8.2", "license": "MIT", "devDependencies": { "@redis/test-utils": "*" @@ -7586,7 +7598,7 @@ "node": ">= 18" }, "peerDependencies": { - "@redis/client": "^5.8.1" + "@redis/client": "^5.8.2" } } } diff --git a/packages/time-series/package.json b/packages/time-series/package.json index b22bbd9cb08..46ea5b16fef 100644 --- a/packages/time-series/package.json +++ b/packages/time-series/package.json @@ -1,6 +1,6 @@ { "name": "@redis/time-series", - "version": "5.8.1", + "version": "5.8.2", "license": "MIT", "main": "./dist/lib/index.js", "types": "./dist/lib/index.d.ts", @@ -13,7 +13,7 @@ "release": "release-it" }, "peerDependencies": { - "@redis/client": "^5.8.1" + "@redis/client": "^5.8.2" }, "devDependencies": { "@redis/test-utils": "*" From 3d6eefc2469091fad057cc28670bf5562d869626 Mon Sep 17 00:00:00 2001 From: GitHub Action Date: Wed, 20 Aug 2025 08:33:35 +0000 Subject: [PATCH 1706/1748] Release entraid@5.8.2 --- package-lock.json | 4 ++-- packages/entraid/package.json | 4 ++-- 2 files changed, 4 insertions(+), 4 deletions(-) diff --git a/package-lock.json b/package-lock.json index abcc27d1bda..71ce9570246 100644 --- a/package-lock.json +++ b/package-lock.json @@ -7367,7 +7367,7 @@ }, "packages/entraid": { "name": "@redis/entraid", - "version": "5.8.1", + "version": "5.8.2", "license": "MIT", "dependencies": { "@azure/identity": "^4.7.0", @@ -7386,7 +7386,7 @@ "node": ">= 18" }, "peerDependencies": { - "@redis/client": "^5.8.1" + "@redis/client": "^5.8.2" } }, "packages/entraid/node_modules/@types/node": { diff --git a/packages/entraid/package.json b/packages/entraid/package.json index 2eee504a1fd..9991fa3fb89 100644 --- a/packages/entraid/package.json +++ b/packages/entraid/package.json @@ -1,6 +1,6 @@ { "name": "@redis/entraid", - "version": "5.8.1", + "version": "5.8.2", "license": "MIT", "main": "./dist/index.js", "types": "./dist/index.d.ts", @@ -22,7 +22,7 @@ "@azure/msal-node": "^2.16.1" }, "peerDependencies": { - "@redis/client": "^5.8.1" + "@redis/client": "^5.8.2" }, "devDependencies": { "@types/express": "^4.17.21", From 7b56e9a3dca9222d04b4b3741ab209f9f0adaf09 Mon Sep 17 00:00:00 2001 From: GitHub Action Date: Wed, 20 Aug 2025 08:33:41 +0000 Subject: [PATCH 1707/1748] Release redis@5.8.2 --- package-lock.json | 72 ++++--------------------------------- packages/redis/package.json | 12 +++---- 2 files changed, 12 insertions(+), 72 deletions(-) diff --git a/package-lock.json b/package-lock.json index 71ce9570246..736abe70a46 100644 --- a/package-lock.json +++ b/package-lock.json @@ -7436,79 +7436,19 @@ } }, "packages/redis": { - "version": "5.8.1", - "license": "MIT", - "dependencies": { - "@redis/bloom": "5.8.1", - "@redis/client": "5.8.1", - "@redis/json": "5.8.1", - "@redis/search": "5.8.1", - "@redis/time-series": "5.8.1" - }, - "engines": { - "node": ">= 18" - } - }, - "packages/redis/node_modules/@redis/bloom": { - "version": "5.8.1", - "resolved": "https://registry.npmjs.org/@redis/bloom/-/bloom-5.8.1.tgz", - "integrity": "sha512-hJOJr/yX6BttnyZ+nxD3Ddiu2lPig4XJjyAK1v7OSHOJNUTfn3RHBryB9wgnBMBdkg9glVh2AjItxIXmr600MA==", - "license": "MIT", - "engines": { - "node": ">= 18" - }, - "peerDependencies": { - "@redis/client": "^5.8.1" - } - }, - "packages/redis/node_modules/@redis/client": { - "version": "5.8.1", - "resolved": "https://registry.npmjs.org/@redis/client/-/client-5.8.1.tgz", - "integrity": "sha512-hD5Tvv7G0t8b3w8ao3kQ4jEPUmUUC6pqA18c8ciYF5xZGfUGBg0olQHW46v6qSt4O5bxOuB3uV7pM6H5wEjBwA==", + "version": "5.8.2", "license": "MIT", "dependencies": { - "cluster-key-slot": "1.1.2" + "@redis/bloom": "5.8.2", + "@redis/client": "5.8.2", + "@redis/json": "5.8.2", + "@redis/search": "5.8.2", + "@redis/time-series": "5.8.2" }, "engines": { "node": ">= 18" } }, - "packages/redis/node_modules/@redis/json": { - "version": "5.8.1", - "resolved": "https://registry.npmjs.org/@redis/json/-/json-5.8.1.tgz", - "integrity": "sha512-kyvM8Vn+WjJI++nRsIoI9TbdfCs1/TgD0Hp7Z7GiG6W4IEBzkXGQakli+R5BoJzUfgh7gED2fkncYy1NLprMNg==", - "license": "MIT", - "engines": { - "node": ">= 18" - }, - "peerDependencies": { - "@redis/client": "^5.8.1" - } - }, - "packages/redis/node_modules/@redis/search": { - "version": "5.8.1", - "resolved": "https://registry.npmjs.org/@redis/search/-/search-5.8.1.tgz", - "integrity": "sha512-CzuKNTInTNQkxqehSn7QiYcM+th+fhjQn5ilTvksP1wPjpxqK0qWt92oYg3XZc3tO2WuXkqDvTujc4D7kb6r/A==", - "license": "MIT", - "engines": { - "node": ">= 18" - }, - "peerDependencies": { - "@redis/client": "^5.8.1" - } - }, - "packages/redis/node_modules/@redis/time-series": { - "version": "5.8.1", - "resolved": "https://registry.npmjs.org/@redis/time-series/-/time-series-5.8.1.tgz", - "integrity": "sha512-klvdR96U9oSOyqvcectoAGhYlMOnMS3I5UWUOgdBn1buMODiwM/E4Eds7gxldKmtowe4rLJSF1CyIqyZTjy8Ow==", - "license": "MIT", - "engines": { - "node": ">= 18" - }, - "peerDependencies": { - "@redis/client": "^5.8.1" - } - }, "packages/search": { "name": "@redis/search", "version": "5.8.2", diff --git a/packages/redis/package.json b/packages/redis/package.json index de4ac275ed0..583a6606817 100644 --- a/packages/redis/package.json +++ b/packages/redis/package.json @@ -1,7 +1,7 @@ { "name": "redis", "description": "A modern, high performance Redis client", - "version": "5.8.1", + "version": "5.8.2", "license": "MIT", "main": "./dist/index.js", "types": "./dist/index.d.ts", @@ -13,11 +13,11 @@ "release": "release-it" }, "dependencies": { - "@redis/bloom": "5.8.1", - "@redis/client": "5.8.1", - "@redis/json": "5.8.1", - "@redis/search": "5.8.1", - "@redis/time-series": "5.8.1" + "@redis/bloom": "5.8.2", + "@redis/client": "5.8.2", + "@redis/json": "5.8.2", + "@redis/search": "5.8.2", + "@redis/time-series": "5.8.2" }, "engines": { "node": ">= 18" From 672246dbd20bc7c36daf4067146d52b8e3cca2e2 Mon Sep 17 00:00:00 2001 From: Nikolay Karadzhov Date: Tue, 26 Aug 2025 16:35:45 +0300 Subject: [PATCH 1708/1748] fix(ts): use all commands in cluster type (#3065) RedisClusterType was using some type restrictions to hide some commands based on its properties. In reality, all commands should be exposed to user ( as in v4 ). fixes #3064 --- packages/client/lib/client/index.ts | 10 +++--- packages/client/lib/cluster/index.ts | 48 ++-------------------------- 2 files changed, 8 insertions(+), 50 deletions(-) diff --git a/packages/client/lib/client/index.ts b/packages/client/lib/client/index.ts index 1a27ea88986..57b12316708 100644 --- a/packages/client/lib/client/index.ts +++ b/packages/client/lib/client/index.ts @@ -146,14 +146,14 @@ export interface RedisClientOptions< clientInfoTag?: string; } -type WithCommands< +export type WithCommands< RESP extends RespVersions, TYPE_MAPPING extends TypeMapping > = { [P in keyof typeof COMMANDS]: CommandSignature<(typeof COMMANDS)[P], RESP, TYPE_MAPPING>; }; -type WithModules< +export type WithModules< M extends RedisModules, RESP extends RespVersions, TYPE_MAPPING extends TypeMapping @@ -163,7 +163,7 @@ type WithModules< }; }; -type WithFunctions< +export type WithFunctions< F extends RedisFunctions, RESP extends RespVersions, TYPE_MAPPING extends TypeMapping @@ -173,7 +173,7 @@ type WithFunctions< }; }; -type WithScripts< +export type WithScripts< S extends RedisScripts, RESP extends RespVersions, TYPE_MAPPING extends TypeMapping @@ -498,7 +498,7 @@ export default class RedisClient< if (options?.url) { const parsedOptions = RedisClient.parseOptions(options); - + if (parsedOptions?.database) { this._self.#selectedDB = parsedOptions.database; } diff --git a/packages/client/lib/cluster/index.ts b/packages/client/lib/cluster/index.ts index 6d26ac98c9a..16454e66fb2 100644 --- a/packages/client/lib/cluster/index.ts +++ b/packages/client/lib/cluster/index.ts @@ -1,6 +1,6 @@ import { RedisClientOptions, RedisClientType } from '../client'; import { CommandOptions } from '../client/commands-queue'; -import { Command, CommandArguments, CommanderConfig, CommandSignature, /*CommandPolicies, CommandWithPoliciesSignature,*/ TypeMapping, RedisArgument, RedisFunction, RedisFunctions, RedisModules, RedisScript, RedisScripts, ReplyUnion, RespVersions } from '../RESP/types'; +import { Command, CommandArguments, CommanderConfig, TypeMapping, RedisArgument, RedisFunction, RedisFunctions, RedisModules, RedisScript, RedisScripts, ReplyUnion, RespVersions } from '../RESP/types'; import COMMANDS from '../commands'; import { EventEmitter } from 'node:events'; import { attachConfig, functionArgumentsPrefix, getTransformReply, scriptArgumentsPrefix } from '../commander'; @@ -13,6 +13,8 @@ import { ClientSideCacheConfig, PooledClientSideCacheProvider } from '../client/ import { BasicCommandParser } from '../client/parser'; import { ASKING_CMD } from '../commands/ASKING'; import SingleEntryCache from '../single-entry-cache' +import { WithCommands, WithFunctions, WithModules, WithScripts } from '../client'; + interface ClusterCommander< M extends RedisModules, F extends RedisFunctions, @@ -103,50 +105,6 @@ export interface RedisClusterOptions< clientSideCache?: PooledClientSideCacheProvider | ClientSideCacheConfig; } -// remove once request & response policies are ready -type ClusterCommand< - NAME extends PropertyKey, - COMMAND extends Command -> = COMMAND['NOT_KEYED_COMMAND'] extends true ? ( - COMMAND['IS_FORWARD_COMMAND'] extends true ? NAME : never -) : NAME; - -// CommandWithPoliciesSignature<(typeof COMMANDS)[P], RESP, TYPE_MAPPING, POLICIES> -type WithCommands< - RESP extends RespVersions, - TYPE_MAPPING extends TypeMapping -> = { - [P in keyof typeof COMMANDS as ClusterCommand]: CommandSignature<(typeof COMMANDS)[P], RESP, TYPE_MAPPING>; -}; - -type WithModules< - M extends RedisModules, - RESP extends RespVersions, - TYPE_MAPPING extends TypeMapping -> = { - [P in keyof M]: { - [C in keyof M[P] as ClusterCommand]: CommandSignature; - }; -}; - -type WithFunctions< - F extends RedisFunctions, - RESP extends RespVersions, - TYPE_MAPPING extends TypeMapping -> = { - [L in keyof F]: { - [C in keyof F[L] as ClusterCommand]: CommandSignature; - }; -}; - -type WithScripts< - S extends RedisScripts, - RESP extends RespVersions, - TYPE_MAPPING extends TypeMapping -> = { - [P in keyof S as ClusterCommand]: CommandSignature; -}; - export type RedisClusterType< M extends RedisModules = {}, F extends RedisFunctions = {}, From 1d824df9e1483905e24b1f47962e6b182eb3f184 Mon Sep 17 00:00:00 2001 From: blackman <125454400+watersRand@users.noreply.github.com> Date: Wed, 27 Aug 2025 18:10:15 +0300 Subject: [PATCH 1709/1748] feat: add LATENCY_RESET command (#3047) --- .../client/lib/commands/LATENCY_RESET.spec.ts | 104 ++++++++++++++++++ packages/client/lib/commands/LATENCY_RESET.ts | 24 ++++ packages/client/lib/commands/index.ts | 3 + 3 files changed, 131 insertions(+) create mode 100644 packages/client/lib/commands/LATENCY_RESET.spec.ts create mode 100644 packages/client/lib/commands/LATENCY_RESET.ts diff --git a/packages/client/lib/commands/LATENCY_RESET.spec.ts b/packages/client/lib/commands/LATENCY_RESET.spec.ts new file mode 100644 index 00000000000..030d0d78e0a --- /dev/null +++ b/packages/client/lib/commands/LATENCY_RESET.spec.ts @@ -0,0 +1,104 @@ +import { strict as assert } from 'node:assert'; +import testUtils, { GLOBAL } from '../test-utils'; +import LATENCY_RESET, { LATENCY_EVENTS } from './LATENCY_RESET'; +import { parseArgs } from './generic-transformers'; + +describe('LATENCY RESET', function () { + + + it('transformArguments with no events', () => { + assert.deepEqual( + parseArgs(LATENCY_RESET), + [ + 'LATENCY', + 'RESET' + ] + ); + }); + + it('transformArguments with one event', () => { + assert.deepEqual( + parseArgs(LATENCY_RESET, LATENCY_EVENTS.COMMAND), + [ + 'LATENCY', + 'RESET', + 'command' + ] + ); + }); + + it('transformArguments with multiple events', () => { + assert.deepEqual( + parseArgs(LATENCY_RESET, LATENCY_EVENTS.COMMAND, LATENCY_EVENTS.FORK), + [ + 'LATENCY', + 'RESET', + 'command', + 'fork' + ] + ); + }); + + + testUtils.testWithClient('client.latencyReset', async client => { + + await client.configSet('latency-monitor-threshold', '1'); + + + await client.sendCommand(['DEBUG', 'SLEEP', '0.1']); + + + const latestLatencyBeforeReset = await client.latencyLatest(); + assert.ok(latestLatencyBeforeReset.length > 0, 'Expected latency events to be recorded before first reset.'); + assert.equal(latestLatencyBeforeReset[0][0], 'command', 'Expected "command" event to be recorded.'); + assert.ok(Number(latestLatencyBeforeReset[0][2]) >= 100, 'Expected latest latency for "command" to be at least 100ms.'); + + + const replyAll = await client.latencyReset(); + + assert.equal(typeof replyAll, 'number'); + assert.ok(replyAll >= 0); + + + const latestLatencyAfterAllReset = await client.latencyLatest(); + assert.deepEqual(latestLatencyAfterAllReset, [], 'Expected no latency events after resetting all.'); + + + await client.sendCommand(['DEBUG', 'SLEEP', '0.05']); + const latestLatencyBeforeSpecificReset = await client.latencyLatest(); + assert.ok(latestLatencyBeforeSpecificReset.length > 0, 'Expected latency events before specific reset.'); + + + const replySpecific = await client.latencyReset(LATENCY_EVENTS.COMMAND); + assert.equal(typeof replySpecific, 'number'); + assert.ok(replySpecific >= 0); + + + const latestLatencyAfterSpecificReset = await client.latencyLatest(); + assert.deepEqual(latestLatencyAfterSpecificReset, [], 'Expected no latency events after specific reset of "command".'); + + + await client.sendCommand(['DEBUG', 'SLEEP', '0.02']); + + + const latestLatencyBeforeMultipleReset = await client.latencyLatest(); + assert.ok(latestLatencyBeforeMultipleReset.length > 0, 'Expected latency events before multiple reset.'); + + + const replyMultiple = await client.latencyReset(LATENCY_EVENTS.COMMAND, LATENCY_EVENTS.FORK); + assert.equal(typeof replyMultiple, 'number'); + assert.ok(replyMultiple >= 0); + + const latestLatencyAfterMultipleReset = await client.latencyLatest(); + assert.deepEqual(latestLatencyAfterMultipleReset, [], 'Expected no latency events after multiple specified resets.'); + + }, { + + ...GLOBAL.SERVERS.OPEN, + clientOptions: { + socket: { + connectTimeout: 300000 + } + } + }); +}); diff --git a/packages/client/lib/commands/LATENCY_RESET.ts b/packages/client/lib/commands/LATENCY_RESET.ts new file mode 100644 index 00000000000..0efa5767630 --- /dev/null +++ b/packages/client/lib/commands/LATENCY_RESET.ts @@ -0,0 +1,24 @@ +import { CommandParser } from '../client/parser'; +import { Command } from '../RESP/types'; +import { LATENCY_EVENTS, LatencyEvent } from './LATENCY_GRAPH'; + +export { LATENCY_EVENTS, LatencyEvent }; + +export default { + NOT_KEYED_COMMAND: true, + IS_READ_ONLY: false, + /** + * Constructs the LATENCY RESET command + * * @param parser - The command parser + * @param events - The latency events to reset. If not specified, all events are reset. + * @see https://redis.io/commands/latency-reset/ + */ + parseCommand(parser: CommandParser, ...events: Array) { + const args = ['LATENCY', 'RESET']; + if (events.length > 0) { + args.push(...events); + } + parser.push(...args); + }, + transformReply: undefined as unknown as () => number +} as const satisfies Command; diff --git a/packages/client/lib/commands/index.ts b/packages/client/lib/commands/index.ts index 4614c8b282b..05c07bbf34a 100644 --- a/packages/client/lib/commands/index.ts +++ b/packages/client/lib/commands/index.ts @@ -171,6 +171,7 @@ import LATENCY_DOCTOR from './LATENCY_DOCTOR'; import LATENCY_GRAPH from './LATENCY_GRAPH'; import LATENCY_HISTORY from './LATENCY_HISTORY'; import LATENCY_LATEST from './LATENCY_LATEST'; +import LATENCY_RESET from './LATENCY_RESET'; import LCS_IDX_WITHMATCHLEN from './LCS_IDX_WITHMATCHLEN'; import LCS_IDX from './LCS_IDX'; import LCS_LEN from './LCS_LEN'; @@ -706,6 +707,8 @@ export default { latencyHistory: LATENCY_HISTORY, LATENCY_LATEST, latencyLatest: LATENCY_LATEST, + LATENCY_RESET, + latencyReset: LATENCY_RESET, LCS_IDX_WITHMATCHLEN, lcsIdxWithMatchLen: LCS_IDX_WITHMATCHLEN, LCS_IDX, From 6ad4c686557d67b50aca287e5a86500aff5d73c6 Mon Sep 17 00:00:00 2001 From: Nikolay Karadzhov Date: Mon, 1 Sep 2025 12:07:40 +0300 Subject: [PATCH 1710/1748] docs: fix scanIterator example in readme (#3072) fixes: #3071 --- README.md | 5 ++--- 1 file changed, 2 insertions(+), 3 deletions(-) diff --git a/README.md b/README.md index 05c55985b33..1789a51d37e 100644 --- a/README.md +++ b/README.md @@ -198,9 +198,8 @@ See the [Pub/Sub overview](https://github.com/redis/node-redis/blob/master/docs/ 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); +for await (const keys of client.scanIterator()) { + console.log(keys, await client.mGet(keys)); } ``` From 208a0d250f98f7690a4981ee39d0013bef999aec Mon Sep 17 00:00:00 2001 From: Nikolay Karadzhov Date: Wed, 10 Sep 2025 11:05:37 +0300 Subject: [PATCH 1711/1748] Hitless upgrades (#3021) * feat(errors): Add specialized timeout error types for maintenance scenarios - Added `SocketTimeoutDuringMaintananceError`, a subclass of `TimeoutError`, to handle socket timeouts during maintenance. - Added `CommandTimeoutDuringMaintenanceError`, another subclass of `TimeoutError`, to address command write timeouts during maintenance. * feat(linked-list): Add EmptyAwareSinglyLinkedList and enhance DoublyLinkedList functionality - Introduced `EmptyAwareSinglyLinkedList`, a subclass of `SinglyLinkedList` that emits an `empty` event when the list becomes empty due to `reset`, `shift`, or `remove` operations. - Added `nodes()` iterator method to `DoublyLinkedList` for iterating over nodes directly. - Enhanced unit tests for `DoublyLinkedList` and `SinglyLinkedList` to cover edge cases and new functionality. - Added comprehensive tests for `EmptyAwareSinglyLinkedList` to validate `empty` event emission under various scenarios. - Improved code formatting and consistency. * refactor(commands-queue): Improve push notification handling - Replaced `setInvalidateCallback` with a more flexible `addPushHandler` method, allowing multiple handlers for push notifications. - Introduced the `PushHandler` type to standardize push notification processing. - Refactored `RedisCommandsQueue` to use a `#pushHandlers` array, enabling dynamic and modular handling of push notifications. - Updated `RedisClient` to leverage the new handler mechanism for `invalidate` push notifications, simplifying and decoupling logic. * feat(commands-queue): Add method to wait for in-flight commands to complete - Introduced `waitForInflightCommandsToComplete` method to asynchronously wait for all in-flight commands to finish processing. - Utilized the `empty` event from `#waitingForReply` to signal when all commands have been completed. * feat(commands-queue): Introduce maintenance mode support for commands-queue - Added `#maintenanceCommandTimeout` and `setMaintenanceCommandTimeout` method to dynamically adjust command timeouts during maintenance * refator(client): Extract socket event listener setup into helper method * refactor(socket): Add maintenance mode support and dynamic timeout handling - Added `#maintenanceTimeout` and `setMaintenanceTimeout` method to dynamically adjust socket timeouts during maintenance. * feat(client): Add Redis Enterprise maintenance configuration options - Added `maintPushNotifications` option to control how the client handles Redis Enterprise maintenance push notifications (`disabled`, `enabled`, `au to`). - Added `maintMovingEndpointType` option to specify the endpoint type for reconnecting during a MOVING notification (`auto`, `internal-ip`, `external-ip`, etc.). - Added `maintRelaxedCommandTimeout` option to define a relaxed timeout for commands during maintenance. - Added `maintRelaxedSocketTimeout` option to define a relaxed timeout for the socket during maintenance. - Enforced RESP3 requirement for maintenance-related features (`maintPushNotifications`). * feat(client): Add socket helpers and pause mechanism - Introduced `#paused` flag with corresponding `_pause` and `_unpause` methods to temporarily halt writing commands to the socket during maintenance windows. - Updated `#write` method to respect the `#paused` flag, preventing new commands from being written during maintenance. - Added `_ejectSocket` method to safely detach from and return the current socket - Added `_insertSocket` method to receive and start using a new socket * feat(client): Add Redis Enterprise maintenance handling capabilities - Introduced `EnterpriseMaintenanceManager` to manage Redis Enterprise maintenance events and push notifications. - Integrated `EnterpriseMaintenanceManager` into `RedisClient` to handle maintenance push notifications and manage socket transitions. - Implemented graceful handling of MOVING, MIGRATING, and FAILOVER push notifications, including socket replacement and timeout adjustments. * test: add E2E test infrastructure for Redis maintenance scenarios * test: add E2E tests for Redis Enterprise maintenance timeout handling (#3) * test: add connection handoff test --------- Co-authored-by: Pavel Pashov Co-authored-by: Pavel Pashov <60297174+PavelPashov@users.noreply.github.com> --- package-lock.json | 16 - packages/bloom/package.json | 2 +- packages/client/lib/client/commands-queue.ts | 111 ++++-- .../client/enterprise-maintenance-manager.ts | 348 ++++++++++++++++++ packages/client/lib/client/index.ts | 196 ++++++++-- .../client/lib/client/linked-list.spec.ts | 111 ++++-- packages/client/lib/client/linked-list.ts | 41 ++- packages/client/lib/client/socket.ts | 26 +- packages/client/lib/errors.ts | 12 + .../test-scenario/connection-handoff.e2e.ts | 126 +++++++ .../test-scenario/fault-injector-client.ts | 187 ++++++++++ .../test-scenario/push-notification.e2e.ts | 94 +++++ .../test-scenario/test-command-runner.ts | 108 ++++++ .../tests/test-scenario/test-scenario.util.ts | 197 ++++++++++ .../timeout-during-notifications.e2e.ts | 159 ++++++++ packages/entraid/package.json | 2 +- packages/json/package.json | 2 +- packages/redis/package.json | 10 +- packages/search/package.json | 2 +- packages/time-series/package.json | 2 +- 20 files changed, 1635 insertions(+), 117 deletions(-) create mode 100644 packages/client/lib/client/enterprise-maintenance-manager.ts create mode 100644 packages/client/lib/tests/test-scenario/connection-handoff.e2e.ts create mode 100644 packages/client/lib/tests/test-scenario/fault-injector-client.ts create mode 100644 packages/client/lib/tests/test-scenario/push-notification.e2e.ts create mode 100644 packages/client/lib/tests/test-scenario/test-command-runner.ts create mode 100644 packages/client/lib/tests/test-scenario/test-scenario.util.ts create mode 100644 packages/client/lib/tests/test-scenario/timeout-during-notifications.e2e.ts diff --git a/package-lock.json b/package-lock.json index 736abe70a46..288e109c979 100644 --- a/package-lock.json +++ b/package-lock.json @@ -7319,22 +7319,6 @@ "url": "https://github.com/sponsors/sindresorhus" } }, - "packages/authx": { - "name": "@redis/authx", - "version": "5.0.0-next.5", - "extraneous": true, - "license": "MIT", - "dependencies": { - "@azure/msal-node": "^2.16.1" - }, - "devDependencies": {}, - "engines": { - "node": ">= 18" - }, - "peerDependencies": { - "@redis/client": "^5.0.0-next.5" - } - }, "packages/bloom": { "name": "@redis/bloom", "version": "5.8.2", diff --git a/packages/bloom/package.json b/packages/bloom/package.json index e2ff5a8b42d..4f46bce4ad7 100644 --- a/packages/bloom/package.json +++ b/packages/bloom/package.json @@ -13,7 +13,7 @@ "release": "release-it" }, "peerDependencies": { - "@redis/client": "^5.8.2" + "@redis/client": "^5.8.2 || ^5.9.0-0" }, "devDependencies": { "@redis/test-utils": "*" diff --git a/packages/client/lib/client/commands-queue.ts b/packages/client/lib/client/commands-queue.ts index 52a07a7e3b5..ae67ca28cd6 100644 --- a/packages/client/lib/client/commands-queue.ts +++ b/packages/client/lib/client/commands-queue.ts @@ -1,10 +1,11 @@ -import { SinglyLinkedList, DoublyLinkedNode, DoublyLinkedList } from './linked-list'; +import { DoublyLinkedNode, DoublyLinkedList, EmptyAwareSinglyLinkedList } from './linked-list'; import encodeCommand from '../RESP/encoder'; import { Decoder, PUSH_TYPE_MAPPING, RESP_TYPES } from '../RESP/decoder'; import { TypeMapping, ReplyUnion, RespVersions, RedisArgument } from '../RESP/types'; import { ChannelListeners, PubSub, PubSubCommand, PubSubListener, PubSubType, PubSubTypeListeners } from './pub-sub'; -import { AbortError, ErrorReply, TimeoutError } from '../errors'; +import { AbortError, ErrorReply, CommandTimeoutDuringMaintenanceError, TimeoutError } from '../errors'; import { MonitorCallback } from '.'; +import { dbgMaintenance } from './enterprise-maintenance-manager'; export interface CommandOptions { chainId?: symbol; @@ -30,6 +31,7 @@ export interface CommandToWrite extends CommandWaitingForReply { timeout: { signal: AbortSignal; listener: () => unknown; + originalTimeout: number | undefined; } | undefined; } @@ -50,22 +52,74 @@ const RESP2_PUSH_TYPE_MAPPING = { [RESP_TYPES.SIMPLE_STRING]: Buffer }; +// Try to handle a push notification. Return whether you +// successfully consumed the notification or not. This is +// important in order for the queue to be able to pass the +// notification to another handler if the current one did not +// succeed. +type PushHandler = (pushItems: Array) => boolean; + export default class RedisCommandsQueue { readonly #respVersion; readonly #maxLength; readonly #toWrite = new DoublyLinkedList(); - readonly #waitingForReply = new SinglyLinkedList(); + readonly #waitingForReply = new EmptyAwareSinglyLinkedList(); readonly #onShardedChannelMoved; #chainInExecution: symbol | undefined; readonly decoder; readonly #pubSub = new PubSub(); + #pushHandlers: PushHandler[] = [this.#onPush.bind(this)]; + + #maintenanceCommandTimeout: number | undefined + + setMaintenanceCommandTimeout(ms: number | undefined) { + // Prevent possible api misuse + if (this.#maintenanceCommandTimeout === ms) { + dbgMaintenance(`Queue already set maintenanceCommandTimeout to ${ms}, skipping`); + return; + }; + + dbgMaintenance(`Setting maintenance command timeout to ${ms}`); + this.#maintenanceCommandTimeout = ms; + + if(this.#maintenanceCommandTimeout === undefined) { + dbgMaintenance(`Queue will keep maintenanceCommandTimeout for exisitng commands, just to be on the safe side. New commands will receive normal timeouts`); + return; + } + + let counter = 0; + const total = this.#toWrite.length; + + // Overwrite timeouts of all eligible toWrite commands + for(const node of this.#toWrite.nodes()) { + const command = node.value; + + // Remove timeout listener if it exists + RedisCommandsQueue.#removeTimeoutListener(command) + + counter++; + const newTimeout = this.#maintenanceCommandTimeout; + + // Overwrite the command's timeout + const signal = AbortSignal.timeout(newTimeout); + command.timeout = { + signal, + listener: () => { + this.#toWrite.remove(node); + command.reject(new CommandTimeoutDuringMaintenanceError(newTimeout)); + }, + originalTimeout: command.timeout?.originalTimeout + }; + signal.addEventListener('abort', command.timeout.listener, { once: true }); + }; + dbgMaintenance(`Total of ${counter} of ${total} timeouts reset to ${ms}`); + } + get isPubSubActive() { return this.#pubSub.isActive; } - #invalidateCallback?: (key: RedisArgument | null) => unknown; - constructor( respVersion: RespVersions, maxLength: number | null | undefined, @@ -107,6 +161,7 @@ export default class RedisCommandsQueue { } return true; } + return false } #getTypeMapping() { @@ -119,30 +174,27 @@ export default class RedisCommandsQueue { onErrorReply: err => this.#onErrorReply(err), //TODO: we can shave off a few cycles by not adding onPush handler at all if CSC is not used onPush: push => { - if (!this.#onPush(push)) { - // currently only supporting "invalidate" over RESP3 push messages - switch (push[0].toString()) { - case "invalidate": { - if (this.#invalidateCallback) { - if (push[1] !== null) { - for (const key of push[1]) { - this.#invalidateCallback(key); - } - } else { - this.#invalidateCallback(null); - } - } - break; - } - } + for(const pushHandler of this.#pushHandlers) { + if(pushHandler(push)) return } }, getTypeMapping: () => this.#getTypeMapping() }); } - setInvalidateCallback(callback?: (key: RedisArgument | null) => unknown) { - this.#invalidateCallback = callback; + addPushHandler(handler: PushHandler): void { + this.#pushHandlers.push(handler); + } + + async waitForInflightCommandsToComplete(): Promise { + // In-flight commands already completed + if(this.#waitingForReply.length === 0) { + return + }; + // Otherwise wait for in-flight commands to fire `empty` event + return new Promise(resolve => { + this.#waitingForReply.events.on('empty', resolve) + }); } addCommand( @@ -168,15 +220,20 @@ export default class RedisCommandsQueue { typeMapping: options?.typeMapping }; - const timeout = options?.timeout; + // If #maintenanceCommandTimeout was explicitly set, we should + // use it instead of the timeout provided by the command + const timeout = this.#maintenanceCommandTimeout ?? options?.timeout; + const wasInMaintenance = this.#maintenanceCommandTimeout !== undefined; if (timeout) { + const signal = AbortSignal.timeout(timeout); value.timeout = { signal, listener: () => { this.#toWrite.remove(node); - value.reject(new TimeoutError()); - } + value.reject(wasInMaintenance ? new CommandTimeoutDuringMaintenanceError(timeout) : new TimeoutError()); + }, + originalTimeout: options?.timeout }; signal.addEventListener('abort', value.timeout.listener, { once: true }); } @@ -432,7 +489,7 @@ export default class RedisCommandsQueue { } static #removeTimeoutListener(command: CommandToWrite) { - command.timeout!.signal.removeEventListener('abort', command.timeout!.listener); + command.timeout?.signal.removeEventListener('abort', command.timeout!.listener); } static #flushToWrite(toBeSent: CommandToWrite, err: Error) { diff --git a/packages/client/lib/client/enterprise-maintenance-manager.ts b/packages/client/lib/client/enterprise-maintenance-manager.ts new file mode 100644 index 00000000000..d4766d9e533 --- /dev/null +++ b/packages/client/lib/client/enterprise-maintenance-manager.ts @@ -0,0 +1,348 @@ +import { RedisClientOptions } from "."; +import RedisCommandsQueue from "./commands-queue"; +import { RedisArgument } from "../.."; +import { isIP } from "net"; +import { lookup } from "dns/promises"; +import assert from "node:assert"; +import { setTimeout } from "node:timers/promises"; +import RedisSocket from "./socket"; +import diagnostics_channel from "node:diagnostics_channel"; + +export const MAINTENANCE_EVENTS = { + PAUSE_WRITING: "pause-writing", + RESUME_WRITING: "resume-writing", + TIMEOUTS_UPDATE: "timeouts-update", +} as const; + +const PN = { + MOVING: "MOVING", + MIGRATING: "MIGRATING", + MIGRATED: "MIGRATED", + FAILING_OVER: "FAILING_OVER", + FAILED_OVER: "FAILED_OVER", +}; + +export type DiagnosticsEvent = { + type: string; + timestamp: number; + data?: Object; +}; + +export const dbgMaintenance = (...args: any[]) => { + if (!process.env.REDIS_DEBUG_MAINTENANCE) return; + return console.log("[MNT]", ...args); +}; + +export const emitDiagnostics = (event: DiagnosticsEvent) => { + if (!process.env.REDIS_EMIT_DIAGNOSTICS) return; + + const channel = diagnostics_channel.channel("redis.maintenance"); + channel.publish(event); +}; + +export interface MaintenanceUpdate { + relaxedCommandTimeout?: number; + relaxedSocketTimeout?: number; +} + +interface Client { + _ejectSocket: () => RedisSocket; + _insertSocket: (socket: RedisSocket) => void; + _pause: () => void; + _unpause: () => void; + _maintenanceUpdate: (update: MaintenanceUpdate) => void; + duplicate: (options: RedisClientOptions) => Client; + connect: () => Promise; + destroy: () => void; +} + +export default class EnterpriseMaintenanceManager { + #commandsQueue: RedisCommandsQueue; + #options: RedisClientOptions; + #isMaintenance = 0; + #client: Client; + + static setupDefaultMaintOptions(options: RedisClientOptions) { + if (options.maintPushNotifications === undefined) { + options.maintPushNotifications = + options?.RESP === 3 ? "auto" : "disabled"; + } + if (options.maintMovingEndpointType === undefined) { + options.maintMovingEndpointType = "auto"; + } + if (options.maintRelaxedSocketTimeout === undefined) { + options.maintRelaxedSocketTimeout = 10000; + } + if (options.maintRelaxedCommandTimeout === undefined) { + options.maintRelaxedCommandTimeout = 10000; + } + } + + static async getHandshakeCommand( + tls: boolean, + host: string, + options: RedisClientOptions, + ): Promise< + | { cmd: Array; errorHandler: (error: Error) => void } + | undefined + > { + if (options.maintPushNotifications === "disabled") return; + + const movingEndpointType = await determineEndpoint(tls, host, options); + return { + cmd: [ + "CLIENT", + "MAINT_NOTIFICATIONS", + "ON", + "moving-endpoint-type", + movingEndpointType, + ], + errorHandler: (error: Error) => { + dbgMaintenance("handshake failed:", error); + if (options.maintPushNotifications === "enabled") { + throw error; + } + }, + }; + } + + constructor( + commandsQueue: RedisCommandsQueue, + client: Client, + options: RedisClientOptions, + ) { + this.#commandsQueue = commandsQueue; + this.#options = options; + this.#client = client; + + this.#commandsQueue.addPushHandler(this.#onPush); + } + + #onPush = (push: Array): boolean => { + dbgMaintenance("ONPUSH:", push.map(String)); + + if (!Array.isArray(push) || !["MOVING", "MIGRATING", "MIGRATED", "FAILING_OVER", "FAILED_OVER"].includes(String(push[0]))) { + return false; + } + + const type = String(push[0]); + + emitDiagnostics({ + type, + timestamp: Date.now(), + data: { + push: push.map(String), + }, + }); + switch (type) { + case PN.MOVING: { + // [ 'MOVING', '17', '15', '54.78.247.156:12075' ] + // ^seq ^after ^new ip + const afterSeconds = push[2]; + const url: string | null = push[3] ? String(push[3]) : null; + dbgMaintenance("Received MOVING:", afterSeconds, url); + this.#onMoving(afterSeconds, url); + return true; + } + case PN.MIGRATING: + case PN.FAILING_OVER: { + dbgMaintenance("Received MIGRATING|FAILING_OVER"); + this.#onMigrating(); + return true; + } + case PN.MIGRATED: + case PN.FAILED_OVER: { + dbgMaintenance("Received MIGRATED|FAILED_OVER"); + this.#onMigrated(); + return true; + } + } + return false; + }; + + // Queue: + // toWrite [ C D E ] + // waitingForReply [ A B ] - aka In-flight commands + // + // time: ---1-2---3-4-5-6--------------------------- + // + // 1. [EVENT] MOVING PN received + // 2. [ACTION] Pause writing ( we need to wait for new socket to connect and for all in-flight commands to complete ) + // 3. [EVENT] New socket connected + // 4. [EVENT] In-flight commands completed + // 5. [ACTION] Destroy old socket + // 6. [ACTION] Resume writing -> we are going to write to the new socket from now on + #onMoving = async ( + afterSeconds: number, + url: string | null, + ): Promise => { + // 1 [EVENT] MOVING PN received + this.#onMigrating(); + + let host: string; + let port: number; + + // The special value `none` indicates that the `MOVING` message doesn’t need + // to contain an endpoint. Instead it contains the value `null` then. In + // such a corner case, the client is expected to schedule a graceful + // reconnect to its currently configured endpoint after half of the grace + // period that was communicated by the server is over. + if (url === null) { + assert(this.#options.maintMovingEndpointType === "none"); + assert(this.#options.socket !== undefined); + assert("host" in this.#options.socket); + assert(typeof this.#options.socket.host === "string"); + host = this.#options.socket.host; + assert(typeof this.#options.socket.port === "number"); + port = this.#options.socket.port; + const waitTime = (afterSeconds * 1000) / 2; + dbgMaintenance(`Wait for ${waitTime}ms`); + await setTimeout(waitTime); + } else { + const split = url.split(":"); + host = split[0]; + port = Number(split[1]); + } + + // 2 [ACTION] Pause writing + dbgMaintenance("Pausing writing of new commands to old socket"); + this.#client._pause(); + + dbgMaintenance("Creating new tmp client"); + let start = performance.now(); + + const tmpOptions = this.#options; + // If the URL is provided, it takes precedense + if(tmpOptions.url) { + const u = new URL(tmpOptions.url); + u.hostname = host; + u.port = String(port); + tmpOptions.url = u.toString(); + } else { + tmpOptions.socket = { + ...tmpOptions.socket, + host, + port + } + } + const tmpClient = this.#client.duplicate(tmpOptions); + dbgMaintenance(`Tmp client created in ${( performance.now() - start ).toFixed(2)}ms`); + dbgMaintenance( + `Set timeout for tmp client to ${this.#options.maintRelaxedSocketTimeout}`, + ); + tmpClient._maintenanceUpdate({ + relaxedCommandTimeout: this.#options.maintRelaxedCommandTimeout, + relaxedSocketTimeout: this.#options.maintRelaxedSocketTimeout, + }); + dbgMaintenance(`Connecting tmp client: ${host}:${port}`); + start = performance.now(); + await tmpClient.connect(); + dbgMaintenance(`Connected to tmp client in ${(performance.now() - start).toFixed(2)}ms`); + // 3 [EVENT] New socket connected + + dbgMaintenance(`Wait for all in-flight commands to complete`); + await this.#commandsQueue.waitForInflightCommandsToComplete(); + dbgMaintenance(`In-flight commands completed`); + // 4 [EVENT] In-flight commands completed + + dbgMaintenance("Swap client sockets..."); + const oldSocket = this.#client._ejectSocket(); + const newSocket = tmpClient._ejectSocket(); + this.#client._insertSocket(newSocket); + tmpClient._insertSocket(oldSocket); + tmpClient.destroy(); + dbgMaintenance("Swap client sockets done."); + // 5 + 6 + dbgMaintenance("Resume writing"); + this.#client._unpause(); + this.#onMigrated(); + }; + + #onMigrating = () => { + this.#isMaintenance++; + if (this.#isMaintenance > 1) { + dbgMaintenance(`Timeout relaxation already done`); + return; + } + + const update: MaintenanceUpdate = { + relaxedCommandTimeout: this.#options.maintRelaxedCommandTimeout, + relaxedSocketTimeout: this.#options.maintRelaxedSocketTimeout, + }; + + this.#client._maintenanceUpdate(update); + }; + + #onMigrated = () => { + //ensure that #isMaintenance doesnt go under 0 + this.#isMaintenance = Math.max(this.#isMaintenance - 1, 0); + if (this.#isMaintenance > 0) { + dbgMaintenance(`Not ready to unrelax timeouts yet`); + return; + } + + const update: MaintenanceUpdate = { + relaxedCommandTimeout: undefined, + relaxedSocketTimeout: undefined + }; + + this.#client._maintenanceUpdate(update); + }; +} + +export type MovingEndpointType = + | "auto" + | "internal-ip" + | "internal-fqdn" + | "external-ip" + | "external-fqdn" + | "none"; + +function isPrivateIP(ip: string): boolean { + const version = isIP(ip); + if (version === 4) { + const octets = ip.split(".").map(Number); + return ( + octets[0] === 10 || + (octets[0] === 172 && octets[1] >= 16 && octets[1] <= 31) || + (octets[0] === 192 && octets[1] === 168) + ); + } + if (version === 6) { + return ( + ip.startsWith("fc") || // Unique local + ip.startsWith("fd") || // Unique local + ip === "::1" || // Loopback + ip.startsWith("fe80") // Link-local unicast + ); + } + return false; +} + +async function determineEndpoint( + tlsEnabled: boolean, + host: string, + options: RedisClientOptions, +): Promise { + assert(options.maintMovingEndpointType !== undefined); + if (options.maintMovingEndpointType !== "auto") { + dbgMaintenance( + `Determine endpoint type: ${options.maintMovingEndpointType}`, + ); + return options.maintMovingEndpointType; + } + + const ip = isIP(host) ? host : (await lookup(host, { family: 0 })).address; + + const isPrivate = isPrivateIP(ip); + + let result: MovingEndpointType; + if (tlsEnabled) { + result = isPrivate ? "internal-fqdn" : "external-fqdn"; + } else { + result = isPrivate ? "internal-ip" : "external-ip"; + } + + dbgMaintenance(`Determine endpoint type: ${result}`); + return result; +} diff --git a/packages/client/lib/client/index.ts b/packages/client/lib/client/index.ts index 57b12316708..cf5763357a6 100644 --- a/packages/client/lib/client/index.ts +++ b/packages/client/lib/client/index.ts @@ -1,5 +1,5 @@ import COMMANDS from '../commands'; -import RedisSocket, { RedisSocketOptions } from './socket'; +import RedisSocket, { RedisSocketOptions, RedisTcpSocketOptions } from './socket'; import { BasicAuth, CredentialsError, CredentialsProvider, StreamingCredentialsProvider, UnableToObtainNewCredentialsError, Disposable } from '../authx'; import RedisCommandsQueue, { CommandOptions } from './commands-queue'; import { EventEmitter } from 'node:events'; @@ -20,6 +20,7 @@ import { BasicClientSideCache, ClientSideCacheConfig, ClientSideCacheProvider } import { BasicCommandParser, CommandParser } from './parser'; import SingleEntryCache from '../single-entry-cache'; import { version } from '../../package.json' +import EnterpriseMaintenanceManager, { MaintenanceUpdate, MovingEndpointType } from './enterprise-maintenance-manager'; export interface RedisClientOptions< M extends RedisModules = RedisModules, @@ -144,7 +145,46 @@ export interface RedisClientOptions< * Tag to append to library name that is sent to the Redis server */ clientInfoTag?: string; -} + /** + * Controls how the client handles Redis Enterprise maintenance push notifications. + * + * - `disabled`: The feature is not used by the client. + * - `enabled`: The client attempts to enable the feature on the server. If the server responds with an error, the connection is interrupted. + * - `auto`: The client attempts to enable the feature on the server. If the server returns an error, the client disables the feature and continues. + * + * The default is `auto`. + */ + maintPushNotifications?: 'disabled' | 'enabled' | 'auto'; + /** + * Controls how the client requests the endpoint to reconnect to during a MOVING notification in Redis Enterprise maintenance. + * + * - `auto`: If the connection is opened to a name or IP address that is from/resolves to a reserved private IP range, request an internal endpoint (e.g., internal-ip), otherwise an external one. If TLS is enabled, then request a FQDN. + * - `internal-ip`: Enforce requesting the internal IP. + * - `internal-fqdn`: Enforce requesting the internal FQDN. + * - `external-ip`: Enforce requesting the external IP address. + * - `external-fqdn`: Enforce requesting the external FQDN. + * - `none`: Used to request a null endpoint, which tells the client to reconnect based on its current config + + * The default is `auto`. + */ + maintMovingEndpointType?: MovingEndpointType; + /** + * Specifies a more relaxed timeout (in milliseconds) for commands during a maintenance window. + * This helps minimize command timeouts during maintenance. If not provided, the `commandOptions.timeout` + * will be used instead. Timeouts during maintenance period result in a `CommandTimeoutDuringMaintenance` error. + * + * The default is 10000 + */ + maintRelaxedCommandTimeout?: number; + /** + * Specifies a more relaxed timeout (in milliseconds) for the socket during a maintenance window. + * This helps minimize socket timeouts during maintenance. If not provided, the `socket.timeout` + * will be used instead. Timeouts during maintenance period result in a `SocketTimeoutDuringMaintenance` error. + * + * The default is 10000 + */ + maintRelaxedSocketTimeout?: number; +}; export type WithCommands< RESP extends RespVersions, @@ -390,7 +430,7 @@ export default class RedisClient< } readonly #options?: RedisClientOptions; - readonly #socket: RedisSocket; + #socket: RedisSocket; readonly #queue: RedisCommandsQueue; #selectedDB = 0; #monitorCallback?: MonitorCallback; @@ -403,11 +443,16 @@ export default class RedisClient< #watchEpoch?: number; #clientSideCache?: ClientSideCacheProvider; #credentialsSubscription: Disposable | null = null; + // Flag used to pause writing to the socket during maintenance windows. + // When true, prevents new commands from being written while waiting for: + // 1. New socket to be ready after maintenance redirect + // 2. In-flight commands on the old socket to complete + #paused = false; + get clientSideCache() { return this._self.#clientSideCache; } - get options(): RedisClientOptions | undefined { return this._self.#options; } @@ -457,6 +502,11 @@ export default class RedisClient< this.#queue = this.#initiateQueue(); this.#socket = this.#initiateSocket(); + + if(options?.maintPushNotifications !== 'disabled') { + new EnterpriseMaintenanceManager(this.#queue, this, this.#options!); + }; + if (options?.clientSideCache) { if (options.clientSideCache instanceof ClientSideCacheProvider) { this.#clientSideCache = options.clientSideCache; @@ -464,7 +514,19 @@ export default class RedisClient< const cscConfig = options.clientSideCache; this.#clientSideCache = new BasicClientSideCache(cscConfig); } - this.#queue.setInvalidateCallback(this.#clientSideCache.invalidate.bind(this.#clientSideCache)); + this.#queue.addPushHandler((push: Array): boolean => { + if (push[0].toString() !== 'invalidate') return false; + + if (push[1] !== null) { + for (const key of push[1]) { + this.#clientSideCache?.invalidate(key) + } + } else { + this.#clientSideCache?.invalidate(null) + } + + return true + }); } } @@ -473,7 +535,12 @@ export default class RedisClient< throw new Error('Client Side Caching is only supported with RESP3'); } + if (options?.maintPushNotifications && options?.maintPushNotifications !== 'disabled' && options?.RESP !== 3) { + throw new Error('Graceful Maintenance is only supported with RESP3'); + } + } + #initiateOptions(options?: RedisClientOptions): RedisClientOptions | undefined { // Convert username/password to credentialsProvider if no credentialsProvider is already in place @@ -496,13 +563,15 @@ export default class RedisClient< this._commandOptions = options.commandOptions; } + if(options?.maintPushNotifications !== 'disabled') { + EnterpriseMaintenanceManager.setupDefaultMaintOptions(options!); + } + if (options?.url) { const parsedOptions = RedisClient.parseOptions(options); - if (parsedOptions?.database) { this._self.#selectedDB = parsedOptions.database; } - return parsedOptions; } @@ -679,9 +748,44 @@ export default class RedisClient< commands.push({cmd: this.#clientSideCache.trackingOn()}); } + const { tls, host } = this.#options!.socket as RedisTcpSocketOptions; + const maintenanceHandshakeCmd = await EnterpriseMaintenanceManager.getHandshakeCommand(!!tls, host!, this.#options!); + if(maintenanceHandshakeCmd) { + commands.push(maintenanceHandshakeCmd); + }; + return commands; } + #attachListeners(socket: RedisSocket) { + socket.on('data', chunk => { + try { + this.#queue.decoder.write(chunk); + } catch (err) { + this.#queue.resetDecoder(); + this.emit('error', err); + } + }) + .on('error', err => { + this.emit('error', err); + this.#clientSideCache?.onError(); + if (this.#socket.isOpen && !this.#options?.disableOfflineQueue) { + this.#queue.flushWaitingForReply(err); + } else { + this.#queue.flushAll(err); + } + }) + .on('connect', () => this.emit('connect')) + .on('ready', () => { + this.emit('ready'); + this.#setPingTimer(); + this.#maybeScheduleWrite(); + }) + .on('reconnecting', () => this.emit('reconnecting')) + .on('drain', () => this.#maybeScheduleWrite()) + .on('end', () => this.emit('end')); + } + #initiateSocket(): RedisSocket { const socketInitiator = async () => { const promises = [], @@ -713,33 +817,9 @@ export default class RedisClient< } }; - return new RedisSocket(socketInitiator, this.#options?.socket) - .on('data', chunk => { - try { - this.#queue.decoder.write(chunk); - } catch (err) { - this.#queue.resetDecoder(); - this.emit('error', err); - } - }) - .on('error', err => { - this.emit('error', err); - this.#clientSideCache?.onError(); - if (this.#socket.isOpen && !this.#options?.disableOfflineQueue) { - this.#queue.flushWaitingForReply(err); - } else { - this.#queue.flushAll(err); - } - }) - .on('connect', () => this.emit('connect')) - .on('ready', () => { - this.emit('ready'); - this.#setPingTimer(); - this.#maybeScheduleWrite(); - }) - .on('reconnecting', () => this.emit('reconnecting')) - .on('drain', () => this.#maybeScheduleWrite()) - .on('end', () => this.emit('end')); + const socket = new RedisSocket(socketInitiator, this.#options?.socket); + this.#attachListeners(socket); + return socket; } #pingTimer?: NodeJS.Timeout; @@ -851,6 +931,51 @@ export default class RedisClient< return this as unknown as RedisClientType; } + /** + * @internal + */ + _ejectSocket(): RedisSocket { + const socket = this._self.#socket; + // @ts-ignore + this._self.#socket = null; + socket.removeAllListeners(); + return socket; + } + + /** + * @internal + */ + _insertSocket(socket: RedisSocket) { + if(this._self.#socket) { + this._self._ejectSocket().destroy(); + } + this._self.#socket = socket; + this._self.#attachListeners(this._self.#socket); + } + + /** + * @internal + */ + _maintenanceUpdate(update: MaintenanceUpdate) { + this._self.#socket.setMaintenanceTimeout(update.relaxedSocketTimeout); + this._self.#queue.setMaintenanceCommandTimeout(update.relaxedCommandTimeout); + } + + /** + * @internal + */ + _pause() { + this._self.#paused = true; + } + + /** + * @internal + */ + _unpause() { + this._self.#paused = false; + this._self.#maybeScheduleWrite(); + } + /** * @internal */ @@ -1080,6 +1205,9 @@ export default class RedisClient< } #write() { + if(this.#paused) { + return + } this.#socket.write(this.#queue.commandsToWrite()); } diff --git a/packages/client/lib/client/linked-list.spec.ts b/packages/client/lib/client/linked-list.spec.ts index 9547fb81c7c..c791d21900d 100644 --- a/packages/client/lib/client/linked-list.spec.ts +++ b/packages/client/lib/client/linked-list.spec.ts @@ -1,138 +1,197 @@ -import { SinglyLinkedList, DoublyLinkedList } from './linked-list'; -import { equal, deepEqual } from 'assert/strict'; - -describe('DoublyLinkedList', () => { +import { + SinglyLinkedList, + DoublyLinkedList, + EmptyAwareSinglyLinkedList, +} from "./linked-list"; +import { equal, deepEqual } from "assert/strict"; + +describe("DoublyLinkedList", () => { const list = new DoublyLinkedList(); - it('should start empty', () => { + it("should start empty", () => { equal(list.length, 0); equal(list.head, undefined); equal(list.tail, undefined); deepEqual(Array.from(list), []); }); - it('shift empty', () => { + it("shift empty", () => { equal(list.shift(), undefined); equal(list.length, 0); deepEqual(Array.from(list), []); }); - it('push 1', () => { + it("push 1", () => { list.push(1); equal(list.length, 1); deepEqual(Array.from(list), [1]); }); - it('push 2', () => { + it("push 2", () => { list.push(2); equal(list.length, 2); deepEqual(Array.from(list), [1, 2]); }); - it('unshift 0', () => { + it("unshift 0", () => { list.unshift(0); equal(list.length, 3); deepEqual(Array.from(list), [0, 1, 2]); }); - it('remove middle node', () => { + it("remove middle node", () => { list.remove(list.head!.next!); equal(list.length, 2); deepEqual(Array.from(list), [0, 2]); }); - it('remove head', () => { + it("remove head", () => { list.remove(list.head!); equal(list.length, 1); deepEqual(Array.from(list), [2]); }); - it('remove tail', () => { + it("remove tail", () => { list.remove(list.tail!); equal(list.length, 0); deepEqual(Array.from(list), []); }); - it('unshift empty queue', () => { + it("unshift empty queue", () => { list.unshift(0); equal(list.length, 1); deepEqual(Array.from(list), [0]); }); - it('push 1', () => { + it("push 1", () => { list.push(1); equal(list.length, 2); deepEqual(Array.from(list), [0, 1]); }); - it('shift', () => { + it("shift", () => { equal(list.shift(), 0); equal(list.length, 1); deepEqual(Array.from(list), [1]); }); - it('shift last element', () => { + it("shift last element", () => { equal(list.shift(), 1); equal(list.length, 0); deepEqual(Array.from(list), []); }); + + it("provide forEach for nodes", () => { + list.reset(); + list.push(1); + list.push(2); + list.push(3); + let count = 0; + for(const _ of list.nodes()) { + count++; + } + equal(count, 3); + for(const _ of list.nodes()) { + count++; + } + equal(count, 6); + }); }); -describe('SinglyLinkedList', () => { +describe("SinglyLinkedList", () => { const list = new SinglyLinkedList(); - it('should start empty', () => { + it("should start empty", () => { equal(list.length, 0); equal(list.head, undefined); equal(list.tail, undefined); deepEqual(Array.from(list), []); }); - it('shift empty', () => { + it("shift empty", () => { equal(list.shift(), undefined); equal(list.length, 0); deepEqual(Array.from(list), []); }); - it('push 1', () => { + it("push 1", () => { list.push(1); equal(list.length, 1); deepEqual(Array.from(list), [1]); }); - it('push 2', () => { + it("push 2", () => { list.push(2); equal(list.length, 2); deepEqual(Array.from(list), [1, 2]); }); - it('push 3', () => { + it("push 3", () => { list.push(3); equal(list.length, 3); deepEqual(Array.from(list), [1, 2, 3]); }); - it('shift 1', () => { + it("shift 1", () => { equal(list.shift(), 1); equal(list.length, 2); deepEqual(Array.from(list), [2, 3]); }); - it('shift 2', () => { + it("shift 2", () => { equal(list.shift(), 2); equal(list.length, 1); deepEqual(Array.from(list), [3]); }); - it('shift 3', () => { + it("shift 3", () => { equal(list.shift(), 3); equal(list.length, 0); deepEqual(Array.from(list), []); }); - it('should be empty', () => { + it("should be empty", () => { equal(list.length, 0); equal(list.head, undefined); equal(list.tail, undefined); }); }); + +describe("EmptyAwareSinglyLinkedList", () => { + it("should emit 'empty' event when reset", () => { + const list = new EmptyAwareSinglyLinkedList(); + let count = 0; + list.events.on("empty", () => count++); + list.push(1); + list.reset(); + equal(count, 1); + list.reset(); + equal(count, 1); + }); + + it("should emit 'empty' event when shift makes the list empty", () => { + const list = new EmptyAwareSinglyLinkedList(); + let count = 0; + list.events.on("empty", () => count++); + list.push(1); + list.push(2); + list.shift(); + equal(count, 0); + list.shift(); + equal(count, 1); + list.shift(); + equal(count, 1); + }); + + it("should emit 'empty' event when remove makes the list empty", () => { + const list = new EmptyAwareSinglyLinkedList(); + let count = 0; + list.events.on("empty", () => count++); + const node1 = list.push(1); + const node2 = list.push(2); + list.remove(node1, undefined); + equal(count, 0); + list.remove(node2, undefined); + equal(count, 1); + }); +}); diff --git a/packages/client/lib/client/linked-list.ts b/packages/client/lib/client/linked-list.ts index 29678f027b5..461f1d40827 100644 --- a/packages/client/lib/client/linked-list.ts +++ b/packages/client/lib/client/linked-list.ts @@ -1,3 +1,5 @@ +import EventEmitter from "events"; + export interface DoublyLinkedNode { value: T; previous: DoublyLinkedNode | undefined; @@ -32,7 +34,7 @@ export class DoublyLinkedList { next: undefined, value }; - } + } return this.#tail = this.#tail.next = { previous: this.#tail, @@ -93,7 +95,7 @@ export class DoublyLinkedList { node.previous!.next = node.next; node.previous = undefined; } - + node.next = undefined; } @@ -109,6 +111,14 @@ export class DoublyLinkedList { node = node.next; } } + + *nodes() { + let node = this.#head; + while(node) { + yield node; + node = node.next; + } + } } export interface SinglyLinkedNode { @@ -201,3 +211,30 @@ export class SinglyLinkedList { } } } + +export class EmptyAwareSinglyLinkedList extends SinglyLinkedList { + readonly events = new EventEmitter(); + reset() { + const old = this.length; + super.reset(); + if(old !== this.length && this.length === 0) { + this.events.emit('empty'); + } + } + shift(): T | undefined { + const old = this.length; + const ret = super.shift(); + if(old !== this.length && this.length === 0) { + this.events.emit('empty'); + } + return ret; + } + remove(node: SinglyLinkedNode, parent: SinglyLinkedNode | undefined) { + const old = this.length; + super.remove(node, parent); + if(old !== this.length && this.length === 0) { + this.events.emit('empty'); + } + } + +} diff --git a/packages/client/lib/client/socket.ts b/packages/client/lib/client/socket.ts index 5f0bcc44929..c5569e86547 100644 --- a/packages/client/lib/client/socket.ts +++ b/packages/client/lib/client/socket.ts @@ -1,9 +1,10 @@ import { EventEmitter, once } from 'node:events'; import net from 'node:net'; import tls from 'node:tls'; -import { ConnectionTimeoutError, ClientClosedError, SocketClosedUnexpectedlyError, ReconnectStrategyError, SocketTimeoutError } from '../errors'; +import { ConnectionTimeoutError, ClientClosedError, SocketClosedUnexpectedlyError, ReconnectStrategyError, SocketTimeoutError, SocketTimeoutDuringMaintenanceError } from '../errors'; import { setTimeout } from 'node:timers/promises'; import { RedisArgument } from '../RESP/types'; +import { dbgMaintenance } from './enterprise-maintenance-manager'; type NetOptions = { tls?: false; @@ -60,6 +61,8 @@ export default class RedisSocket extends EventEmitter { readonly #socketFactory; readonly #socketTimeout; + #maintenanceTimeout: number | undefined; + #socket?: net.Socket | tls.TLSSocket; #isOpen = false; @@ -238,6 +241,22 @@ export default class RedisSocket extends EventEmitter { } while (this.#isOpen && !this.#isReady); } + setMaintenanceTimeout(ms?: number) { + dbgMaintenance(`Set socket timeout to ${ms}`); + if (this.#maintenanceTimeout === ms) { + dbgMaintenance(`Socket already set maintenanceCommandTimeout to ${ms}, skipping`); + return; + }; + + this.#maintenanceTimeout = ms; + + if(ms !== undefined) { + this.#socket?.setTimeout(ms); + } else { + this.#socket?.setTimeout(this.#socketTimeout ?? 0); + } + } + async #createSocket(): Promise { const socket = this.#socketFactory.create(); @@ -260,7 +279,10 @@ export default class RedisSocket extends EventEmitter { if (this.#socketTimeout) { socket.once('timeout', () => { - socket.destroy(new SocketTimeoutError(this.#socketTimeout!)); + const error = this.#maintenanceTimeout + ? new SocketTimeoutDuringMaintenanceError(this.#maintenanceTimeout) + : new SocketTimeoutError(this.#socketTimeout!) + socket.destroy(error); }); socket.setTimeout(this.#socketTimeout); } diff --git a/packages/client/lib/errors.ts b/packages/client/lib/errors.ts index 5cb9166df02..4d9ddf7f2b1 100644 --- a/packages/client/lib/errors.ts +++ b/packages/client/lib/errors.ts @@ -71,6 +71,18 @@ export class BlobError extends ErrorReply {} export class TimeoutError extends Error {} +export class SocketTimeoutDuringMaintenanceError extends TimeoutError { + constructor(timeout: number) { + super(`Socket timeout during maintenance. Expecting data, but didn't receive any in ${timeout}ms.`); + } +} + +export class CommandTimeoutDuringMaintenanceError extends TimeoutError { + constructor(timeout: number) { + super(`Command timeout during maintenance. Waited to write command for more than ${timeout}ms.`); + } +} + export class MultiErrorReply extends ErrorReply { replies: Array; errorIndexes: Array; diff --git a/packages/client/lib/tests/test-scenario/connection-handoff.e2e.ts b/packages/client/lib/tests/test-scenario/connection-handoff.e2e.ts new file mode 100644 index 00000000000..c9207d1d5eb --- /dev/null +++ b/packages/client/lib/tests/test-scenario/connection-handoff.e2e.ts @@ -0,0 +1,126 @@ +import diagnostics_channel from "node:diagnostics_channel"; +import { FaultInjectorClient } from "./fault-injector-client"; +import { + getDatabaseConfig, + getDatabaseConfigFromEnv, + getEnvConfig, + RedisConnectionConfig, +} from "./test-scenario.util"; +import { createClient } from "../../.."; +import { DiagnosticsEvent } from "../../client/enterprise-maintenance-manager"; +import { before } from "mocha"; +import { spy } from "sinon"; +import assert from "node:assert"; +import { TestCommandRunner } from "./test-command-runner"; +import net from "node:net"; + +describe("Connection Handoff", () => { + const diagnosticsLog: DiagnosticsEvent[] = []; + + const onMessageHandler = (message: unknown) => { + diagnosticsLog.push(message as DiagnosticsEvent); + }; + + let clientConfig: RedisConnectionConfig; + let client: ReturnType>; + let faultInjectorClient: FaultInjectorClient; + let connectSpy = spy(net, "createConnection"); + + before(() => { + const envConfig = getEnvConfig(); + const redisConfig = getDatabaseConfigFromEnv( + envConfig.redisEndpointsConfigPath, + ); + + faultInjectorClient = new FaultInjectorClient(envConfig.faultInjectorUrl); + clientConfig = getDatabaseConfig(redisConfig); + }); + + beforeEach(async () => { + diagnosticsLog.length = 0; + diagnostics_channel.subscribe("redis.maintenance", onMessageHandler); + + connectSpy.resetHistory(); + + client = createClient({ + socket: { + host: clientConfig.host, + port: clientConfig.port, + ...(clientConfig.tls === true ? { tls: true } : {}), + }, + password: clientConfig.password, + username: clientConfig.username, + RESP: 3, + maintPushNotifications: "auto", + maintMovingEndpointType: "external-ip", + maintRelaxedCommandTimeout: 10000, + maintRelaxedSocketTimeout: 10000, + }); + + client.on("error", (err: Error) => { + throw new Error(`Client error: ${err.message}`); + }); + + await client.connect(); + await client.flushAll(); + }); + + afterEach(() => { + diagnostics_channel.unsubscribe("redis.maintenance", onMessageHandler); + client.destroy(); + }); + + describe("New Connection Establishment", () => { + it("should establish new connection", async () => { + assert.equal(connectSpy.callCount, 1); + + const { action_id: lowTimeoutBindAndMigrateActionId } = + await faultInjectorClient.migrateAndBindAction({ + bdbId: clientConfig.bdbId, + clusterIndex: 0, + }); + + const lowTimeoutWaitPromise = faultInjectorClient.waitForAction( + lowTimeoutBindAndMigrateActionId, + ); + + await lowTimeoutWaitPromise; + assert.equal(connectSpy.callCount, 2); + }); + }); + + describe("TLS Connection Handoff", () => { + it("TODO receiveMessagesWithTLSEnabledTest", async () => { + // + }); + it("TODO connectionHandoffWithStaticInternalNameTest", async () => { + // + }); + it("TODO connectionHandoffWithStaticExternalNameTest", async () => { + // + }); + }); + + describe("Traffic Resumption", () => { + it("Traffic resumed after handoff", async () => { + const { action_id } = await faultInjectorClient.migrateAndBindAction({ + bdbId: clientConfig.bdbId, + clusterIndex: 0, + }); + + const workloadPromise = faultInjectorClient.waitForAction(action_id); + + const commandPromises = + await TestCommandRunner.fireCommandsUntilStopSignal( + client, + workloadPromise, + ); + + const rejected = ( + await Promise.all(commandPromises.commandPromises) + ).filter((result) => result.status === "rejected"); + + assert.ok(rejected.length === 0); + }); + }); +}); diff --git a/packages/client/lib/tests/test-scenario/fault-injector-client.ts b/packages/client/lib/tests/test-scenario/fault-injector-client.ts new file mode 100644 index 00000000000..d6635ac42e6 --- /dev/null +++ b/packages/client/lib/tests/test-scenario/fault-injector-client.ts @@ -0,0 +1,187 @@ +import { setTimeout } from "node:timers/promises"; + +export type ActionType = + | "dmc_restart" + | "failover" + | "reshard" + | "sequence_of_actions" + | "network_failure" + | "execute_rlutil_command" + | "execute_rladmin_command" + | "migrate" + | "bind"; + +export interface ActionRequest { + type: ActionType; + parameters?: { + bdb_id?: string; + [key: string]: unknown; + }; +} + +export interface ActionStatus { + status: string; + error: unknown; + output: string; +} + +export class FaultInjectorClient { + private baseUrl: string; + #fetch: typeof fetch; + + constructor(baseUrl: string, fetchImpl: typeof fetch = fetch) { + this.baseUrl = baseUrl.replace(/\/+$/, ""); // trim trailing slash + this.#fetch = fetchImpl; + } + + /** + * Lists all available actions. + * @throws {Error} When the HTTP request fails or response cannot be parsed as JSON + */ + public listActions(): Promise { + return this.#request("GET", "/action"); + } + + /** + * Triggers a specific action. + * @param action The action request to trigger + * @throws {Error} When the HTTP request fails or response cannot be parsed as JSON + */ + public triggerAction(action: ActionRequest): Promise { + return this.#request("POST", "/action", action); + } + + /** + * Gets the status of a specific action. + * @param actionId The ID of the action to check + * @throws {Error} When the HTTP request fails or response cannot be parsed as JSON + */ + public getActionStatus(actionId: string): Promise { + return this.#request("GET", `/action/${actionId}`); + } + + /** + * Executes an rladmin command. + * @param command The rladmin command to execute + * @param bdbId Optional database ID to target + * @throws {Error} When the HTTP request fails or response cannot be parsed as JSON + */ + public executeRladminCommand( + command: string, + bdbId?: string + ): Promise { + const cmd = bdbId ? `rladmin -b ${bdbId} ${command}` : `rladmin ${command}`; + return this.#request("POST", "/rladmin", cmd); + } + + /** + * Waits for an action to complete. + * @param actionId The ID of the action to wait for + * @param options Optional timeout and max wait time + * @throws {Error} When the action does not complete within the max wait time + */ + public async waitForAction( + actionId: string, + { + timeoutMs, + maxWaitTimeMs, + }: { + timeoutMs?: number; + maxWaitTimeMs?: number; + } = {} + ): Promise { + const timeout = timeoutMs || 1000; + const maxWaitTime = maxWaitTimeMs || 60000; + + const startTime = Date.now(); + + while (Date.now() - startTime < maxWaitTime) { + const action = await this.getActionStatus(actionId); + + if (["finished", "failed", "success"].includes(action.status)) { + return action; + } + + await setTimeout(timeout); + } + + throw new Error(`Timeout waiting for action ${actionId}`); + } + + async migrateAndBindAction({ + bdbId, + clusterIndex, + }: { + bdbId: string | number; + clusterIndex: string | number; + }) { + const bdbIdStr = bdbId.toString(); + const clusterIndexStr = clusterIndex.toString(); + + return this.triggerAction<{ + action_id: string; + }>({ + type: "sequence_of_actions", + parameters: { + bdbId: bdbIdStr, + actions: [ + { + type: "migrate", + params: { + cluster_index: clusterIndexStr, + }, + }, + { + type: "bind", + params: { + cluster_index: clusterIndexStr, + bdb_id: bdbIdStr, + }, + }, + ], + }, + }); + } + + async #request( + method: string, + path: string, + body?: Object | string + ): Promise { + const url = `${this.baseUrl}${path}`; + const headers: Record = { + "Content-Type": "application/json", + }; + + let payload: string | undefined; + + if (body) { + if (typeof body === "string") { + headers["Content-Type"] = "text/plain"; + payload = body; + } else { + headers["Content-Type"] = "application/json"; + payload = JSON.stringify(body); + } + } + + const response = await this.#fetch(url, { method, headers, body: payload }); + + if (!response.ok) { + try { + const text = await response.text(); + throw new Error(`HTTP ${response.status} - ${text}`); + } catch { + throw new Error(`HTTP ${response.status}`); + } + } + + try { + return (await response.json()) as T; + } catch { + throw new Error( + `HTTP ${response.status} - Unable to parse response as JSON` + ); + } + } +} diff --git a/packages/client/lib/tests/test-scenario/push-notification.e2e.ts b/packages/client/lib/tests/test-scenario/push-notification.e2e.ts new file mode 100644 index 00000000000..3408931728e --- /dev/null +++ b/packages/client/lib/tests/test-scenario/push-notification.e2e.ts @@ -0,0 +1,94 @@ +import assert from "node:assert"; +import diagnostics_channel from "node:diagnostics_channel"; +import { FaultInjectorClient } from "./fault-injector-client"; +import { + getDatabaseConfig, + getDatabaseConfigFromEnv, + getEnvConfig, + RedisConnectionConfig, +} from "./test-scenario.util"; +import { createClient } from "../../.."; +import { DiagnosticsEvent } from "../../client/enterprise-maintenance-manager"; +import { before } from "mocha"; + +describe("Push Notifications", () => { + const diagnosticsLog: DiagnosticsEvent[] = []; + + const onMessageHandler = (message: unknown) => { + diagnosticsLog.push(message as DiagnosticsEvent); + }; + + let clientConfig: RedisConnectionConfig; + let client: ReturnType>; + let faultInjectorClient: FaultInjectorClient; + + before(() => { + const envConfig = getEnvConfig(); + const redisConfig = getDatabaseConfigFromEnv( + envConfig.redisEndpointsConfigPath + ); + + faultInjectorClient = new FaultInjectorClient(envConfig.faultInjectorUrl); + clientConfig = getDatabaseConfig(redisConfig); + }); + + beforeEach(async () => { + diagnosticsLog.length = 0; + diagnostics_channel.subscribe("redis.maintenance", onMessageHandler); + + client = createClient({ + socket: { + host: clientConfig.host, + port: clientConfig.port, + ...(clientConfig.tls === true ? { tls: true } : {}), + }, + password: clientConfig.password, + username: clientConfig.username, + RESP: 3, + maintPushNotifications: "auto", + maintMovingEndpointType: "external-ip", + maintRelaxedCommandTimeout: 10000, + maintRelaxedSocketTimeout: 10000, + }); + + client.on("error", (err: Error) => { + throw new Error(`Client error: ${err.message}`); + }); + + await client.connect(); + }); + + afterEach(() => { + diagnostics_channel.unsubscribe("redis.maintenance", onMessageHandler); + client.destroy(); + }); + + it("should receive MOVING, MIGRATING, and MIGRATED push notifications", async () => { + const { action_id: migrateActionId } = + await faultInjectorClient.triggerAction<{ action_id: string }>({ + type: "migrate", + parameters: { + cluster_index: "0", + }, + }); + + await faultInjectorClient.waitForAction(migrateActionId); + + const { action_id: bindActionId } = + await faultInjectorClient.triggerAction<{ action_id: string }>({ + type: "bind", + parameters: { + cluster_index: "0", + bdb_id: `${clientConfig.bdbId}`, + }, + }); + + await faultInjectorClient.waitForAction(bindActionId); + + const pushNotificationLogs = diagnosticsLog.filter((log) => { + return ["MOVING", "MIGRATING", "MIGRATED"].includes(log?.type); + }); + + assert.strictEqual(pushNotificationLogs.length, 3); + }); +}); diff --git a/packages/client/lib/tests/test-scenario/test-command-runner.ts b/packages/client/lib/tests/test-scenario/test-command-runner.ts new file mode 100644 index 00000000000..9e1acc3a8ab --- /dev/null +++ b/packages/client/lib/tests/test-scenario/test-command-runner.ts @@ -0,0 +1,108 @@ +import { randomUUID } from "node:crypto"; +import { setTimeout } from "node:timers/promises"; +import { createClient } from "../../.."; + +/** + * Options for the `fireCommandsUntilStopSignal` method + */ +type FireCommandsUntilStopSignalOptions = { + /** + * Number of commands to fire in each batch + */ + batchSize: number; + /** + * Timeout between batches in milliseconds + */ + timeoutMs: number; + /** + * Function that creates the commands to be executed + */ + createCommands: ( + client: ReturnType> + ) => Array<() => Promise>; +}; + +/** + * Utility class for running test commands until a stop signal is received + */ +export class TestCommandRunner { + private static readonly defaultOptions: FireCommandsUntilStopSignalOptions = { + batchSize: 60, + timeoutMs: 10, + createCommands: ( + client: ReturnType> + ) => [ + () => client.set(randomUUID(), Date.now()), + () => client.get(randomUUID()), + ], + }; + + static #toSettled(p: Promise) { + return p + .then((value) => ({ status: "fulfilled" as const, value, error: null })) + .catch((reason) => ({ + status: "rejected" as const, + value: null, + error: reason, + })); + } + + static async #racePromises({ + timeout, + stopper, + }: { + timeout: Promise; + stopper: Promise; + }) { + return Promise.race([ + TestCommandRunner.#toSettled(timeout).then((result) => ({ + ...result, + stop: false, + })), + TestCommandRunner.#toSettled(stopper).then((result) => ({ + ...result, + stop: true, + })), + ]); + } + + /** + * Fires a batch of test commands until a stop signal is received + * @param client - The Redis client to use + * @param stopSignalPromise - Promise that resolves when the execution should stop + * @param options - Options for the command execution + * @returns An object containing the promises of all executed commands and the result of the stop signal + */ + static async fireCommandsUntilStopSignal( + client: ReturnType>, + stopSignalPromise: Promise, + options?: Partial + ) { + const executeOptions = { + ...TestCommandRunner.defaultOptions, + ...options, + }; + + const commandPromises = []; + + while (true) { + for (let i = 0; i < executeOptions.batchSize; i++) { + for (const command of executeOptions.createCommands(client)) { + commandPromises.push(TestCommandRunner.#toSettled(command())); + } + } + + const result = await TestCommandRunner.#racePromises({ + timeout: setTimeout(executeOptions.timeoutMs), + stopper: stopSignalPromise, + }); + + if (result.stop) { + return { + commandPromises, + stopResult: result, + }; + } + } + } +} diff --git a/packages/client/lib/tests/test-scenario/test-scenario.util.ts b/packages/client/lib/tests/test-scenario/test-scenario.util.ts new file mode 100644 index 00000000000..b130cdc5386 --- /dev/null +++ b/packages/client/lib/tests/test-scenario/test-scenario.util.ts @@ -0,0 +1,197 @@ +import { readFileSync } from "fs"; +import { createClient, RedisClientOptions } from "../../.."; +import { stub } from "sinon"; + +type DatabaseEndpoint = { + addr: string[]; + addr_type: string; + dns_name: string; + oss_cluster_api_preferred_endpoint_type: string; + oss_cluster_api_preferred_ip_type: string; + port: number; + proxy_policy: string; + uid: string; +}; + +type DatabaseConfig = { + bdb_id: number; + username: string; + password: string; + tls: boolean; + raw_endpoints: DatabaseEndpoint[]; + endpoints: string[]; +}; + +type DatabasesConfig = { + [databaseName: string]: DatabaseConfig; +}; + +type EnvConfig = { + redisEndpointsConfigPath: string; + faultInjectorUrl: string; +}; + +/** + * Reads environment variables required for the test scenario + * @returns Environment configuration object + * @throws Error if required environment variables are not set + */ +export function getEnvConfig(): EnvConfig { + if (!process.env.REDIS_ENDPOINTS_CONFIG_PATH) { + throw new Error( + "REDIS_ENDPOINTS_CONFIG_PATH environment variable must be set" + ); + } + + if (!process.env.FAULT_INJECTION_API_URL) { + throw new Error("FAULT_INJECTION_API_URL environment variable must be set"); + } + + return { + redisEndpointsConfigPath: process.env.REDIS_ENDPOINTS_CONFIG_PATH, + faultInjectorUrl: process.env.FAULT_INJECTION_API_URL, + }; +} + +/** + * Reads database configuration from a file + * @param filePath - The path to the database configuration file + * @returns Parsed database configuration object + * @throws Error if file doesn't exist or JSON is invalid + */ +export function getDatabaseConfigFromEnv(filePath: string): DatabasesConfig { + try { + const fileContent = readFileSync(filePath, "utf8"); + return JSON.parse(fileContent) as DatabasesConfig; + } catch (error) { + throw new Error(`Failed to read or parse database config from ${filePath}`); + } +} + +export interface RedisConnectionConfig { + host: string; + port: number; + username: string; + password: string; + tls: boolean; + bdbId: number; +} + +/** + * Gets Redis connection parameters for a specific database + * @param databasesConfig - The parsed database configuration object + * @param databaseName - Optional name of the database to retrieve (defaults to the first one) + * @returns Redis connection configuration with host, port, username, password, and tls + * @throws Error if the specified database is not found in the configuration + */ +export function getDatabaseConfig( + databasesConfig: DatabasesConfig, + databaseName?: string +): RedisConnectionConfig { + const dbConfig = databaseName + ? databasesConfig[databaseName] + : Object.values(databasesConfig)[0]; + + if (!dbConfig) { + throw new Error( + `Database ${databaseName ? databaseName : ""} not found in configuration` + ); + } + + const endpoint = dbConfig.raw_endpoints[0]; // Use the first endpoint + + return { + host: endpoint.dns_name, + port: endpoint.port, + username: dbConfig.username, + password: dbConfig.password, + tls: dbConfig.tls, + bdbId: dbConfig.bdb_id, + }; +} + +// TODO this should be moved in the tests utils package +export async function blockSetImmediate(fn: () => Promise) { + let setImmediateStub: any; + + try { + setImmediateStub = stub(global, "setImmediate"); + setImmediateStub.callsFake(() => { + //Dont call the callback, effectively blocking execution + }); + await fn(); + } finally { + if (setImmediateStub) { + setImmediateStub.restore(); + } + } +} + +/** + * Factory class for creating and managing Redis clients + */ +export class ClientFactory { + private readonly clients = new Map< + string, + ReturnType> + >(); + + constructor(private readonly config: RedisConnectionConfig) {} + + /** + * Creates a new client with the specified options and connects it to the database + * @param key - The key to store the client under + * @param options - Optional client options + * @returns The created and connected client + */ + async create(key: string, options: Partial = {}) { + const client = createClient({ + socket: { + host: this.config.host, + port: this.config.port, + ...(this.config.tls === true ? { tls: true } : {}), + }, + password: this.config.password, + username: this.config.username, + RESP: 3, + maintPushNotifications: "auto", + maintMovingEndpointType: "auto", + ...options, + }); + + client.on("error", (err: Error) => { + throw new Error(`Client error: ${err.message}`); + }); + + await client.connect(); + + this.clients.set(key, client); + + return client; + } + + /** + * Gets an existing client by key or the first one if no key is provided + * @param key - The key of the client to retrieve + * @returns The client if found, undefined otherwise + */ + get(key?: string) { + if (key) { + return this.clients.get(key); + } + + // Get the first one if no key is provided + return this.clients.values().next().value; + } + + /** + * Destroys all created clients + */ + destroyAll() { + this.clients.forEach((client) => { + if (client && client.isOpen) { + client.destroy(); + } + }); + } +} diff --git a/packages/client/lib/tests/test-scenario/timeout-during-notifications.e2e.ts b/packages/client/lib/tests/test-scenario/timeout-during-notifications.e2e.ts new file mode 100644 index 00000000000..7bdf23fcb15 --- /dev/null +++ b/packages/client/lib/tests/test-scenario/timeout-during-notifications.e2e.ts @@ -0,0 +1,159 @@ +import assert from "node:assert"; + +import { FaultInjectorClient } from "./fault-injector-client"; +import { + ClientFactory, + getDatabaseConfig, + getDatabaseConfigFromEnv, + getEnvConfig, + RedisConnectionConfig, + blockSetImmediate +} from "./test-scenario.util"; +import { createClient } from "../../.."; +import { before } from "mocha"; +import { TestCommandRunner } from "./test-command-runner"; + +describe("Timeout Handling During Notifications", () => { + let clientConfig: RedisConnectionConfig; + let clientFactory: ClientFactory; + let faultInjectorClient: FaultInjectorClient; + let defaultClient: ReturnType>; + + before(() => { + const envConfig = getEnvConfig(); + const redisConfig = getDatabaseConfigFromEnv( + envConfig.redisEndpointsConfigPath + ); + + clientConfig = getDatabaseConfig(redisConfig); + faultInjectorClient = new FaultInjectorClient(envConfig.faultInjectorUrl); + clientFactory = new ClientFactory(clientConfig); + }); + + beforeEach(async () => { + defaultClient = await clientFactory.create("default"); + + await defaultClient.flushAll(); + }); + + afterEach(async () => { + clientFactory.destroyAll(); + }); + + it("should relax command timeout on MOVING, MIGRATING, and MIGRATED", async () => { + // PART 1 + // Set very low timeout to trigger errors + const lowTimeoutClient = await clientFactory.create("lowTimeout", { + maintRelaxedCommandTimeout: 50, + }); + + const { action_id: lowTimeoutBindAndMigrateActionId } = + await faultInjectorClient.migrateAndBindAction({ + bdbId: clientConfig.bdbId, + clusterIndex: 0, + }); + + const lowTimeoutWaitPromise = faultInjectorClient.waitForAction( + lowTimeoutBindAndMigrateActionId + ); + + const lowTimeoutCommandPromises = + await TestCommandRunner.fireCommandsUntilStopSignal( + lowTimeoutClient, + lowTimeoutWaitPromise + ); + + const lowTimeoutRejectedCommands = ( + await Promise.all(lowTimeoutCommandPromises.commandPromises) + ).filter((result) => result.status === "rejected"); + + assert.ok(lowTimeoutRejectedCommands.length > 0); + assert.strictEqual( + lowTimeoutRejectedCommands.filter((rejected) => { + return ( + // TODO instanceof doesn't work for some reason + rejected.error.constructor.name === + "CommandTimeoutDuringMaintananceError" + ); + }).length, + lowTimeoutRejectedCommands.length + ); + + // PART 2 + // Set high timeout to avoid errors + const highTimeoutClient = await clientFactory.create("highTimeout", { + maintRelaxedCommandTimeout: 10000, + }); + + const { action_id: highTimeoutBindAndMigrateActionId } = + await faultInjectorClient.migrateAndBindAction({ + bdbId: clientConfig.bdbId, + clusterIndex: 0, + }); + + const highTimeoutWaitPromise = faultInjectorClient.waitForAction( + highTimeoutBindAndMigrateActionId + ); + + const highTimeoutCommandPromises = + await TestCommandRunner.fireCommandsUntilStopSignal( + highTimeoutClient, + highTimeoutWaitPromise + ); + + const highTimeoutRejectedCommands = ( + await Promise.all(highTimeoutCommandPromises.commandPromises) + ).filter((result) => result.status === "rejected"); + + assert.strictEqual(highTimeoutRejectedCommands.length, 0); + }); + + it("should unrelax command timeout after MAINTENANCE", async () => { + const clientWithCommandTimeout = await clientFactory.create( + "clientWithCommandTimeout", + { + commandOptions: { + timeout: 100, + }, + } + ); + + const { action_id: bindAndMigrateActionId } = + await faultInjectorClient.migrateAndBindAction({ + bdbId: clientConfig.bdbId, + clusterIndex: 0, + }); + + const lowTimeoutWaitPromise = faultInjectorClient.waitForAction( + bindAndMigrateActionId + ); + + const relaxedTimeoutCommandPromises = + await TestCommandRunner.fireCommandsUntilStopSignal( + clientWithCommandTimeout, + lowTimeoutWaitPromise + ); + + const relaxedTimeoutRejectedCommands = ( + await Promise.all(relaxedTimeoutCommandPromises.commandPromises) + ).filter((result) => result.status === "rejected"); + + assert.ok(relaxedTimeoutRejectedCommands.length === 0); + + const start = performance.now(); + + let error: any; + await blockSetImmediate(async () => { + try { + await clientWithCommandTimeout.set("key", "value"); + } catch (err: any) { + error = err; + } + }); + + // Make sure it took less than 1sec to fail + assert.ok(performance.now() - start < 1000); + assert.ok(error instanceof Error); + assert.ok(error.constructor.name === "TimeoutError"); + }); +}); diff --git a/packages/entraid/package.json b/packages/entraid/package.json index 9991fa3fb89..272747d1a89 100644 --- a/packages/entraid/package.json +++ b/packages/entraid/package.json @@ -22,7 +22,7 @@ "@azure/msal-node": "^2.16.1" }, "peerDependencies": { - "@redis/client": "^5.8.2" + "@redis/client": "^5.8.2 || ^5.9.0-0" }, "devDependencies": { "@types/express": "^4.17.21", diff --git a/packages/json/package.json b/packages/json/package.json index ff689dd17ee..a1db4e44b8d 100644 --- a/packages/json/package.json +++ b/packages/json/package.json @@ -13,7 +13,7 @@ "release": "release-it" }, "peerDependencies": { - "@redis/client": "^5.8.2" + "@redis/client": "^5.8.2 || ^5.9.0-0" }, "devDependencies": { "@redis/test-utils": "*" diff --git a/packages/redis/package.json b/packages/redis/package.json index 583a6606817..ed2715d06f7 100644 --- a/packages/redis/package.json +++ b/packages/redis/package.json @@ -13,11 +13,11 @@ "release": "release-it" }, "dependencies": { - "@redis/bloom": "5.8.2", - "@redis/client": "5.8.2", - "@redis/json": "5.8.2", - "@redis/search": "5.8.2", - "@redis/time-series": "5.8.2" + "@redis/bloom": "^5.8.2 || ^5.9.0-0", + "@redis/client": "^5.8.2 || ^5.9.0-0", + "@redis/json": "^5.8.2 || ^5.9.0-0", + "@redis/search": "^5.8.2 || ^5.9.0-0", + "@redis/time-series": "^5.8.2 || ^5.9.0-0" }, "engines": { "node": ">= 18" diff --git a/packages/search/package.json b/packages/search/package.json index 40238080e8b..20fe27aad60 100644 --- a/packages/search/package.json +++ b/packages/search/package.json @@ -14,7 +14,7 @@ "release": "release-it" }, "peerDependencies": { - "@redis/client": "^5.8.2" + "@redis/client": "^5.8.2 || ^5.9.0-0" }, "devDependencies": { "@redis/test-utils": "*" diff --git a/packages/time-series/package.json b/packages/time-series/package.json index 46ea5b16fef..0e13ac1a11c 100644 --- a/packages/time-series/package.json +++ b/packages/time-series/package.json @@ -13,7 +13,7 @@ "release": "release-it" }, "peerDependencies": { - "@redis/client": "^5.8.2" + "@redis/client": "^5.8.2 || ^5.9.0-0" }, "devDependencies": { "@redis/test-utils": "*" From 550767e3aec49ad83653bde531d5e97c470a6ce6 Mon Sep 17 00:00:00 2001 From: GitHub Action Date: Wed, 10 Sep 2025 09:04:19 +0000 Subject: [PATCH 1712/1748] Release client@5.9.0-beta.0 --- package-lock.json | 22 +++++++++++----------- packages/client/package.json | 2 +- 2 files changed, 12 insertions(+), 12 deletions(-) diff --git a/package-lock.json b/package-lock.json index 288e109c979..77f386ddf23 100644 --- a/package-lock.json +++ b/package-lock.json @@ -7330,12 +7330,12 @@ "node": ">= 18" }, "peerDependencies": { - "@redis/client": "^5.8.2" + "@redis/client": "^5.8.2 || ^5.9.0-0" } }, "packages/client": { "name": "@redis/client", - "version": "5.8.2", + "version": "5.9.0-beta.0", "license": "MIT", "dependencies": { "cluster-key-slot": "1.1.2" @@ -7370,7 +7370,7 @@ "node": ">= 18" }, "peerDependencies": { - "@redis/client": "^5.8.2" + "@redis/client": "^5.8.2 || ^5.9.0-0" } }, "packages/entraid/node_modules/@types/node": { @@ -7416,18 +7416,18 @@ "node": ">= 18" }, "peerDependencies": { - "@redis/client": "^5.8.2" + "@redis/client": "^5.8.2 || ^5.9.0-0" } }, "packages/redis": { "version": "5.8.2", "license": "MIT", "dependencies": { - "@redis/bloom": "5.8.2", - "@redis/client": "5.8.2", - "@redis/json": "5.8.2", - "@redis/search": "5.8.2", - "@redis/time-series": "5.8.2" + "@redis/bloom": "^5.8.2 || ^5.9.0-0", + "@redis/client": "^5.8.2 || ^5.9.0-0", + "@redis/json": "^5.8.2 || ^5.9.0-0", + "@redis/search": "^5.8.2 || ^5.9.0-0", + "@redis/time-series": "^5.8.2 || ^5.9.0-0" }, "engines": { "node": ">= 18" @@ -7444,7 +7444,7 @@ "node": ">= 18" }, "peerDependencies": { - "@redis/client": "^5.8.2" + "@redis/client": "^5.8.2 || ^5.9.0-0" } }, "packages/test-utils": { @@ -7522,7 +7522,7 @@ "node": ">= 18" }, "peerDependencies": { - "@redis/client": "^5.8.2" + "@redis/client": "^5.8.2 || ^5.9.0-0" } } } diff --git a/packages/client/package.json b/packages/client/package.json index 1332083bf18..2b8ad41610a 100644 --- a/packages/client/package.json +++ b/packages/client/package.json @@ -1,6 +1,6 @@ { "name": "@redis/client", - "version": "5.8.2", + "version": "5.9.0-beta.0", "license": "MIT", "main": "./dist/index.js", "types": "./dist/index.d.ts", From ceeca035d017cd379763a6f54cb9465d4aa1e24d Mon Sep 17 00:00:00 2001 From: GitHub Action Date: Wed, 10 Sep 2025 09:04:24 +0000 Subject: [PATCH 1713/1748] Release bloom@5.9.0-beta.0 --- package-lock.json | 4 ++-- packages/bloom/package.json | 4 ++-- 2 files changed, 4 insertions(+), 4 deletions(-) diff --git a/package-lock.json b/package-lock.json index 77f386ddf23..dc0ed7fa66d 100644 --- a/package-lock.json +++ b/package-lock.json @@ -7321,7 +7321,7 @@ }, "packages/bloom": { "name": "@redis/bloom", - "version": "5.8.2", + "version": "5.9.0-beta.0", "license": "MIT", "devDependencies": { "@redis/test-utils": "*" @@ -7330,7 +7330,7 @@ "node": ">= 18" }, "peerDependencies": { - "@redis/client": "^5.8.2 || ^5.9.0-0" + "@redis/client": "^5.9.0-beta.0" } }, "packages/client": { diff --git a/packages/bloom/package.json b/packages/bloom/package.json index 4f46bce4ad7..6947934fc4b 100644 --- a/packages/bloom/package.json +++ b/packages/bloom/package.json @@ -1,6 +1,6 @@ { "name": "@redis/bloom", - "version": "5.8.2", + "version": "5.9.0-beta.0", "license": "MIT", "main": "./dist/lib/index.js", "types": "./dist/lib/index.d.ts", @@ -13,7 +13,7 @@ "release": "release-it" }, "peerDependencies": { - "@redis/client": "^5.8.2 || ^5.9.0-0" + "@redis/client": "^5.9.0-beta.0" }, "devDependencies": { "@redis/test-utils": "*" From 24ec2606b054d627325efc030ccf78ea00eb1ec8 Mon Sep 17 00:00:00 2001 From: GitHub Action Date: Wed, 10 Sep 2025 09:04:30 +0000 Subject: [PATCH 1714/1748] Release json@5.9.0-beta.0 --- package-lock.json | 4 ++-- packages/json/package.json | 4 ++-- 2 files changed, 4 insertions(+), 4 deletions(-) diff --git a/package-lock.json b/package-lock.json index dc0ed7fa66d..13e15fe14bd 100644 --- a/package-lock.json +++ b/package-lock.json @@ -7407,7 +7407,7 @@ }, "packages/json": { "name": "@redis/json", - "version": "5.8.2", + "version": "5.9.0-beta.0", "license": "MIT", "devDependencies": { "@redis/test-utils": "*" @@ -7416,7 +7416,7 @@ "node": ">= 18" }, "peerDependencies": { - "@redis/client": "^5.8.2 || ^5.9.0-0" + "@redis/client": "^5.9.0-beta.0" } }, "packages/redis": { diff --git a/packages/json/package.json b/packages/json/package.json index a1db4e44b8d..552d67ac39b 100644 --- a/packages/json/package.json +++ b/packages/json/package.json @@ -1,6 +1,6 @@ { "name": "@redis/json", - "version": "5.8.2", + "version": "5.9.0-beta.0", "license": "MIT", "main": "./dist/lib/index.js", "types": "./dist/lib/index.d.ts", @@ -13,7 +13,7 @@ "release": "release-it" }, "peerDependencies": { - "@redis/client": "^5.8.2 || ^5.9.0-0" + "@redis/client": "^5.9.0-beta.0" }, "devDependencies": { "@redis/test-utils": "*" From 35cf4844621ef4e88f38f44e03096a4a759fca47 Mon Sep 17 00:00:00 2001 From: GitHub Action Date: Wed, 10 Sep 2025 09:04:35 +0000 Subject: [PATCH 1715/1748] Release search@5.9.0-beta.0 --- package-lock.json | 4 ++-- packages/search/package.json | 4 ++-- 2 files changed, 4 insertions(+), 4 deletions(-) diff --git a/package-lock.json b/package-lock.json index 13e15fe14bd..0f0721fc098 100644 --- a/package-lock.json +++ b/package-lock.json @@ -7435,7 +7435,7 @@ }, "packages/search": { "name": "@redis/search", - "version": "5.8.2", + "version": "5.9.0-beta.0", "license": "MIT", "devDependencies": { "@redis/test-utils": "*" @@ -7444,7 +7444,7 @@ "node": ">= 18" }, "peerDependencies": { - "@redis/client": "^5.8.2 || ^5.9.0-0" + "@redis/client": "^5.9.0-beta.0" } }, "packages/test-utils": { diff --git a/packages/search/package.json b/packages/search/package.json index 20fe27aad60..0ca44b22737 100644 --- a/packages/search/package.json +++ b/packages/search/package.json @@ -1,6 +1,6 @@ { "name": "@redis/search", - "version": "5.8.2", + "version": "5.9.0-beta.0", "license": "MIT", "main": "./dist/lib/index.js", "types": "./dist/lib/index.d.ts", @@ -14,7 +14,7 @@ "release": "release-it" }, "peerDependencies": { - "@redis/client": "^5.8.2 || ^5.9.0-0" + "@redis/client": "^5.9.0-beta.0" }, "devDependencies": { "@redis/test-utils": "*" From aee6f058535e4eafddd04194901817ffb4afe1a8 Mon Sep 17 00:00:00 2001 From: GitHub Action Date: Wed, 10 Sep 2025 09:04:41 +0000 Subject: [PATCH 1716/1748] Release time-series@5.9.0-beta.0 --- package-lock.json | 4 ++-- packages/time-series/package.json | 4 ++-- 2 files changed, 4 insertions(+), 4 deletions(-) diff --git a/package-lock.json b/package-lock.json index 0f0721fc098..605458b1c1e 100644 --- a/package-lock.json +++ b/package-lock.json @@ -7513,7 +7513,7 @@ }, "packages/time-series": { "name": "@redis/time-series", - "version": "5.8.2", + "version": "5.9.0-beta.0", "license": "MIT", "devDependencies": { "@redis/test-utils": "*" @@ -7522,7 +7522,7 @@ "node": ">= 18" }, "peerDependencies": { - "@redis/client": "^5.8.2 || ^5.9.0-0" + "@redis/client": "^5.9.0-beta.0" } } } diff --git a/packages/time-series/package.json b/packages/time-series/package.json index 0e13ac1a11c..81e170b467a 100644 --- a/packages/time-series/package.json +++ b/packages/time-series/package.json @@ -1,6 +1,6 @@ { "name": "@redis/time-series", - "version": "5.8.2", + "version": "5.9.0-beta.0", "license": "MIT", "main": "./dist/lib/index.js", "types": "./dist/lib/index.d.ts", @@ -13,7 +13,7 @@ "release": "release-it" }, "peerDependencies": { - "@redis/client": "^5.8.2 || ^5.9.0-0" + "@redis/client": "^5.9.0-beta.0" }, "devDependencies": { "@redis/test-utils": "*" From 1df34a4b115e354ea38b9ba696822b1196f2bc40 Mon Sep 17 00:00:00 2001 From: GitHub Action Date: Wed, 10 Sep 2025 09:04:46 +0000 Subject: [PATCH 1717/1748] Release entraid@5.9.0-beta.0 --- package-lock.json | 4 ++-- packages/entraid/package.json | 4 ++-- 2 files changed, 4 insertions(+), 4 deletions(-) diff --git a/package-lock.json b/package-lock.json index 605458b1c1e..662a6e2cbcd 100644 --- a/package-lock.json +++ b/package-lock.json @@ -7351,7 +7351,7 @@ }, "packages/entraid": { "name": "@redis/entraid", - "version": "5.8.2", + "version": "5.9.0-beta.0", "license": "MIT", "dependencies": { "@azure/identity": "^4.7.0", @@ -7370,7 +7370,7 @@ "node": ">= 18" }, "peerDependencies": { - "@redis/client": "^5.8.2 || ^5.9.0-0" + "@redis/client": "^5.9.0-beta.0" } }, "packages/entraid/node_modules/@types/node": { diff --git a/packages/entraid/package.json b/packages/entraid/package.json index 272747d1a89..b02fdc7ea5e 100644 --- a/packages/entraid/package.json +++ b/packages/entraid/package.json @@ -1,6 +1,6 @@ { "name": "@redis/entraid", - "version": "5.8.2", + "version": "5.9.0-beta.0", "license": "MIT", "main": "./dist/index.js", "types": "./dist/index.d.ts", @@ -22,7 +22,7 @@ "@azure/msal-node": "^2.16.1" }, "peerDependencies": { - "@redis/client": "^5.8.2 || ^5.9.0-0" + "@redis/client": "^5.9.0-beta.0" }, "devDependencies": { "@types/express": "^4.17.21", From e2702b63f2f7317f1745216ceebe3d1686c17dd0 Mon Sep 17 00:00:00 2001 From: GitHub Action Date: Wed, 10 Sep 2025 09:04:52 +0000 Subject: [PATCH 1718/1748] Release redis@5.9.0-beta.0 --- package-lock.json | 12 ++++++------ packages/redis/package.json | 12 ++++++------ 2 files changed, 12 insertions(+), 12 deletions(-) diff --git a/package-lock.json b/package-lock.json index 662a6e2cbcd..664dbb68ceb 100644 --- a/package-lock.json +++ b/package-lock.json @@ -7420,14 +7420,14 @@ } }, "packages/redis": { - "version": "5.8.2", + "version": "5.9.0-beta.0", "license": "MIT", "dependencies": { - "@redis/bloom": "^5.8.2 || ^5.9.0-0", - "@redis/client": "^5.8.2 || ^5.9.0-0", - "@redis/json": "^5.8.2 || ^5.9.0-0", - "@redis/search": "^5.8.2 || ^5.9.0-0", - "@redis/time-series": "^5.8.2 || ^5.9.0-0" + "@redis/bloom": "5.9.0-beta.0", + "@redis/client": "5.9.0-beta.0", + "@redis/json": "5.9.0-beta.0", + "@redis/search": "5.9.0-beta.0", + "@redis/time-series": "5.9.0-beta.0" }, "engines": { "node": ">= 18" diff --git a/packages/redis/package.json b/packages/redis/package.json index ed2715d06f7..06b76cc5cbd 100644 --- a/packages/redis/package.json +++ b/packages/redis/package.json @@ -1,7 +1,7 @@ { "name": "redis", "description": "A modern, high performance Redis client", - "version": "5.8.2", + "version": "5.9.0-beta.0", "license": "MIT", "main": "./dist/index.js", "types": "./dist/index.d.ts", @@ -13,11 +13,11 @@ "release": "release-it" }, "dependencies": { - "@redis/bloom": "^5.8.2 || ^5.9.0-0", - "@redis/client": "^5.8.2 || ^5.9.0-0", - "@redis/json": "^5.8.2 || ^5.9.0-0", - "@redis/search": "^5.8.2 || ^5.9.0-0", - "@redis/time-series": "^5.8.2 || ^5.9.0-0" + "@redis/bloom": "5.9.0-beta.0", + "@redis/client": "5.9.0-beta.0", + "@redis/json": "5.9.0-beta.0", + "@redis/search": "5.9.0-beta.0", + "@redis/time-series": "5.9.0-beta.0" }, "engines": { "node": ">= 18" From 6eed1ee7adbaed0ba6a2781699a77283c6f6f6c4 Mon Sep 17 00:00:00 2001 From: Nikolay Karadzhov Date: Fri, 12 Sep 2025 14:47:57 +0300 Subject: [PATCH 1719/1748] fix(cluster): prevent infinite loop (#3078) getRandomNode could end up in an infinite loop if this.masters is empty and this.replicas is empty. fixes: #3075 --- packages/client/lib/cluster/cluster-slots.spec.ts | 11 ++++++++++- packages/client/lib/cluster/cluster-slots.ts | 3 ++- 2 files changed, 12 insertions(+), 2 deletions(-) diff --git a/packages/client/lib/cluster/cluster-slots.spec.ts b/packages/client/lib/cluster/cluster-slots.spec.ts index bea1453037a..76c4cb53fdf 100644 --- a/packages/client/lib/cluster/cluster-slots.spec.ts +++ b/packages/client/lib/cluster/cluster-slots.spec.ts @@ -5,7 +5,6 @@ import RedisClusterSlots from './cluster-slots'; describe('RedisClusterSlots', () => { describe('initialization', () => { - describe('clientSideCache validation', () => { const mockEmit = ((_event: string | symbol, ..._args: any[]): boolean => true) as EventEmitter['emit']; const clientSideCacheConfig = { ttl: 0, maxEntries: 0 }; @@ -45,4 +44,14 @@ describe('RedisClusterSlots', () => { }); }); }); + + describe('getRandomNode', ()=> { + it('should not enter infinite loop when no nodes', () => { + const slots = new RedisClusterSlots({ + rootNodes: [] + }, () => true) + slots.getRandomNode() + slots.getRandomNode() + }); + }); }); diff --git a/packages/client/lib/cluster/cluster-slots.ts b/packages/client/lib/cluster/cluster-slots.ts index 84486112320..9c75b3ab4bf 100644 --- a/packages/client/lib/cluster/cluster-slots.ts +++ b/packages/client/lib/cluster/cluster-slots.ts @@ -462,6 +462,7 @@ export default class RedisClusterSlots< } *#iterateAllNodes() { + if(this.masters.length + this.replicas.length === 0) return let i = Math.floor(Math.random() * (this.masters.length + this.replicas.length)); if (i < this.masters.length) { do { @@ -542,7 +543,7 @@ export default class RedisClusterSlots< this.masters[index] : this.replicas[index - this.masters.length], client = this.#createClient(node, false); - + this.pubSubNode = { address: node.address, client, From 073db12dbbccc6e9dd2c574623413616e6d93ea0 Mon Sep 17 00:00:00 2001 From: Caiuri Uller <54175227+Caiuriuller@users.noreply.github.com> Date: Tue, 16 Sep 2025 10:41:40 -0300 Subject: [PATCH 1720/1748] docs: update RedisJSON documentation link (#3079) * docs: update RedisJSON documentation link * docs: update RedisJSON path syntax documentation link --- packages/json/README.md | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/packages/json/README.md b/packages/json/README.md index 86996a68370..ed60a64351a 100644 --- a/packages/json/README.md +++ b/packages/json/README.md @@ -1,6 +1,6 @@ # @redis/json -This package provides support for the [RedisJSON](https://redis.io/docs/data-types/json/) module, which adds JSON as a native data type to Redis. +This package provides support for the [RedisJSON](https://redis.io/docs/latest/develop/data-types/json/) module, which adds JSON as a native data type to Redis. Should be used with [`redis`/`@redis/client`](https://github.com/redis/node-redis). @@ -33,7 +33,7 @@ await client.json.set('noderedis:jsondata', '$', { }); ``` -For more information about RedisJSON's path syntax, [check out the documentation](https://redis.io/docs/data-types/json/path/). +For more information about RedisJSON's path syntax, [check out the documentation](https://redis.io/docs/latest/develop/data-types/json/path). ### Retrieving JSON Documents from Redis From b97bbbe8c69d130cae162f436908cb66b2f02b91 Mon Sep 17 00:00:00 2001 From: Pavel Pashov <60297174+PavelPashov@users.noreply.github.com> Date: Thu, 18 Sep 2025 12:45:55 +0300 Subject: [PATCH 1721/1748] refactor(test): improve test scenario reliability and maintainability (#3077) * refactor(test): improve test scenario reliability and maintainability * tests: add resp3 check test (#1) * test: refactor connection handoff tests with enhanced spy utility (#2) * test: add comprehensive push notification disabled scenarios (#3) * tests: add params config tests (#4) * tests: add feature enablement tests (#5) --------- Co-authored-by: Nikolay Karadzhov --- .../client/enterprise-maintenance-manager.ts | 21 +- .../tests/test-scenario/configuration.e2e.ts | 201 ++++++++++ .../test-scenario/connection-handoff.e2e.ts | 198 ++++++---- .../test-scenario/fault-injector-client.ts | 21 +- .../tests/test-scenario/negative-tests.e2e.ts | 15 + .../test-scenario/push-notification.e2e.ts | 351 +++++++++++++++--- .../test-scenario/test-command-runner.ts | 108 ------ .../tests/test-scenario/test-scenario.util.ts | 113 +++--- .../timeout-during-notifications.e2e.ts | 313 +++++++++++----- packages/client/tsconfig.json | 3 +- 10 files changed, 930 insertions(+), 414 deletions(-) create mode 100644 packages/client/lib/tests/test-scenario/configuration.e2e.ts create mode 100644 packages/client/lib/tests/test-scenario/negative-tests.e2e.ts delete mode 100644 packages/client/lib/tests/test-scenario/test-command-runner.ts diff --git a/packages/client/lib/client/enterprise-maintenance-manager.ts b/packages/client/lib/client/enterprise-maintenance-manager.ts index d4766d9e533..631fb1f7115 100644 --- a/packages/client/lib/client/enterprise-maintenance-manager.ts +++ b/packages/client/lib/client/enterprise-maintenance-manager.ts @@ -51,9 +51,10 @@ interface Client { _pause: () => void; _unpause: () => void; _maintenanceUpdate: (update: MaintenanceUpdate) => void; - duplicate: (options: RedisClientOptions) => Client; + duplicate: () => Client; connect: () => Promise; destroy: () => void; + on: (event: string, callback: (value: unknown) => void) => void; } export default class EnterpriseMaintenanceManager { @@ -211,21 +212,25 @@ export default class EnterpriseMaintenanceManager { dbgMaintenance("Creating new tmp client"); let start = performance.now(); - const tmpOptions = this.#options; // If the URL is provided, it takes precedense - if(tmpOptions.url) { - const u = new URL(tmpOptions.url); + // the options object could just be mutated + if(this.#options.url) { + const u = new URL(this.#options.url); u.hostname = host; u.port = String(port); - tmpOptions.url = u.toString(); + this.#options.url = u.toString(); } else { - tmpOptions.socket = { - ...tmpOptions.socket, + this.#options.socket = { + ...this.#options.socket, host, port } } - const tmpClient = this.#client.duplicate(tmpOptions); + const tmpClient = this.#client.duplicate(); + tmpClient.on('error', (error: unknown) => { + //We dont know how to handle tmp client errors + dbgMaintenance(`[ERR]`, error) + }); dbgMaintenance(`Tmp client created in ${( performance.now() - start ).toFixed(2)}ms`); dbgMaintenance( `Set timeout for tmp client to ${this.#options.maintRelaxedSocketTimeout}`, diff --git a/packages/client/lib/tests/test-scenario/configuration.e2e.ts b/packages/client/lib/tests/test-scenario/configuration.e2e.ts new file mode 100644 index 00000000000..a648375f6e4 --- /dev/null +++ b/packages/client/lib/tests/test-scenario/configuration.e2e.ts @@ -0,0 +1,201 @@ +import assert from "node:assert"; +import diagnostics_channel from "node:diagnostics_channel"; +import { DiagnosticsEvent } from "../../client/enterprise-maintenance-manager"; + +import { + RedisConnectionConfig, + createTestClient, + getDatabaseConfig, + getDatabaseConfigFromEnv, + getEnvConfig, +} from "./test-scenario.util"; +import { createClient } from "../../.."; +import { FaultInjectorClient } from "./fault-injector-client"; +import { MovingEndpointType } from "../../../dist/lib/client/enterprise-maintenance-manager"; +import { RedisTcpSocketOptions } from "../../client/socket"; + +describe("Client Configuration and Handshake", () => { + let clientConfig: RedisConnectionConfig; + let client: ReturnType>; + let faultInjectorClient: FaultInjectorClient; + let log: DiagnosticsEvent[] = []; + + before(() => { + const envConfig = getEnvConfig(); + const redisConfig = getDatabaseConfigFromEnv( + envConfig.redisEndpointsConfigPath, + ); + + faultInjectorClient = new FaultInjectorClient(envConfig.faultInjectorUrl); + clientConfig = getDatabaseConfig(redisConfig); + + diagnostics_channel.subscribe("redis.maintenance", (event) => { + log.push(event as DiagnosticsEvent); + }); + }); + + beforeEach(() => { + log.length = 0; + }); + + afterEach(async () => { + if (client && client.isOpen) { + await client.flushAll(); + client.destroy(); + } + }); + + describe("Parameter Configuration", () => { + const endpoints: MovingEndpointType[] = [ + "auto", + // "internal-ip", + // "internal-fqdn", + "external-ip", + "external-fqdn", + "none", + ]; + + for (const endpointType of endpoints) { + it(`clientHandshakeWithEndpointType '${endpointType}'`, async () => { + try { + client = await createTestClient(clientConfig, { + maintMovingEndpointType: endpointType, + }); + client.on("error", () => {}); + + //need to copy those because they will be mutated later + const oldOptions = JSON.parse(JSON.stringify(client.options)); + assert.ok(oldOptions); + + const { action_id } = await faultInjectorClient.migrateAndBindAction({ + bdbId: clientConfig.bdbId, + clusterIndex: 0, + }); + + await faultInjectorClient.waitForAction(action_id); + + const movingEvent = log.find((event) => event.type === "MOVING"); + assert(!!movingEvent, "Didnt receive moving PN"); + + let endpoint: string | undefined; + try { + //@ts-ignore + endpoint = movingEvent.data.push[3]; + } catch (err) { + assert( + false, + `couldnt get endpoint from event ${JSON.stringify(movingEvent)}`, + ); + } + + assert(endpoint !== undefined, "no endpoint"); + + const newOptions = client.options; + assert.ok(newOptions); + + if (oldOptions?.url) { + if (endpointType === "none") { + assert.equal( + newOptions!.url, + oldOptions.url, + "For movingEndpointTpe 'none', we expect old and new url to be the same", + ); + } else { + assert.equal( + newOptions.url, + endpoint, + "Expected what came through the wire to be set in the new client", + ); + assert.notEqual( + newOptions!.url, + oldOptions.url, + `For movingEndpointTpe ${endpointType}, we expect old and new url to be different`, + ); + } + } else { + const oldSocket = oldOptions.socket as RedisTcpSocketOptions; + const newSocket = newOptions.socket as RedisTcpSocketOptions; + assert.ok(oldSocket); + assert.ok(newSocket); + + if (endpointType === "none") { + assert.equal( + newSocket.host, + oldSocket.host, + "For movingEndpointTpe 'none', we expect old and new host to be the same", + ); + } else { + assert.equal( + newSocket.host + ":" + newSocket.port, + endpoint, + "Expected what came through the wire to be set in the new client", + ); + assert.notEqual( + newSocket.host, + oldSocket.host, + `For movingEndpointTpe ${endpointType}, we expect old and new host to be different`, + ); + } + } + } catch (error: any) { + if ( + endpointType === "internal-fqdn" || + endpointType === "internal-ip" + ) { + // errors are expected here, because we cannot connect to internal endpoints unless we are deployed in the same place as the server + } else { + assert(false, error); + } + } + }); + } + }); + + describe("Feature Enablement", () => { + it("connectionHandshakeIncludesEnablingNotifications", async () => { + client = await createTestClient(clientConfig, { + maintPushNotifications: "enabled", + }); + + const { action_id } = await faultInjectorClient.migrateAndBindAction({ + bdbId: clientConfig.bdbId, + clusterIndex: 0, + }); + + await faultInjectorClient.waitForAction(action_id); + + let movingEvent = false; + let migratingEvent = false; + let migratedEvent = false; + for (const event of log) { + if (event.type === "MOVING") movingEvent = true; + if (event.type === "MIGRATING") migratingEvent = true; + if (event.type === "MIGRATED") migratedEvent = true; + } + assert.ok(movingEvent, "didnt receive MOVING PN"); + assert.ok(migratingEvent, "didnt receive MIGRATING PN"); + assert.ok(migratedEvent, "didnt receive MIGRATED PN"); + }); + + it("disabledDontReceiveNotifications", async () => { + try { + client = await createTestClient(clientConfig, { + maintPushNotifications: "disabled", + socket: { + reconnectStrategy: false + } + }); + client.on('error', console.log.bind(console)) + + const { action_id } = await faultInjectorClient.migrateAndBindAction({ + bdbId: clientConfig.bdbId, + clusterIndex: 0, + }); + + await faultInjectorClient.waitForAction(action_id); + + assert.equal(log.length, 0, "received a PN while feature is disabled"); + } catch (error: any) { } + }); + }); +}); diff --git a/packages/client/lib/tests/test-scenario/connection-handoff.e2e.ts b/packages/client/lib/tests/test-scenario/connection-handoff.e2e.ts index c9207d1d5eb..3fbf5e38d40 100644 --- a/packages/client/lib/tests/test-scenario/connection-handoff.e2e.ts +++ b/packages/client/lib/tests/test-scenario/connection-handoff.e2e.ts @@ -1,126 +1,176 @@ -import diagnostics_channel from "node:diagnostics_channel"; import { FaultInjectorClient } from "./fault-injector-client"; import { + createTestClient, getDatabaseConfig, getDatabaseConfigFromEnv, getEnvConfig, RedisConnectionConfig, } from "./test-scenario.util"; -import { createClient } from "../../.."; -import { DiagnosticsEvent } from "../../client/enterprise-maintenance-manager"; +import { createClient, RedisClientOptions } from "../../.."; import { before } from "mocha"; -import { spy } from "sinon"; +import Sinon, { SinonSpy, spy, stub } from "sinon"; import assert from "node:assert"; -import { TestCommandRunner } from "./test-command-runner"; -import net from "node:net"; -describe("Connection Handoff", () => { - const diagnosticsLog: DiagnosticsEvent[] = []; +/** + * Creates a spy on a duplicated client method + * @param client - The Redis client instance + * @param funcName - The name of the method to spy on + * @returns Object containing the promise that resolves with the spy and restore function + */ +const spyOnTemporaryClientInstanceMethod = ( + client: ReturnType>, + methodName: string +) => { + const { promise, resolve } = ( + Promise as typeof Promise & { + withResolvers: () => { + promise: Promise<{ spy: SinonSpy; restore: () => void }>; + resolve: (value: any) => void; + }; + } + ).withResolvers(); + + const originalDuplicate = client.duplicate.bind(client); + + const duplicateStub: Sinon.SinonStub = stub( + // Temporary clients (in the context of hitless upgrade) + // are created by calling the duplicate method on the client. + Object.getPrototypeOf(client), + "duplicate" + ).callsFake((opts) => { + const tmpClient = originalDuplicate(opts); + resolve({ + spy: spy(tmpClient, methodName), + restore: duplicateStub.restore, + }); - const onMessageHandler = (message: unknown) => { - diagnosticsLog.push(message as DiagnosticsEvent); + return tmpClient; + }); + + return { + getSpy: () => promise, }; +}; +describe("Connection Handoff", () => { let clientConfig: RedisConnectionConfig; - let client: ReturnType>; + let client: ReturnType>; let faultInjectorClient: FaultInjectorClient; - let connectSpy = spy(net, "createConnection"); before(() => { const envConfig = getEnvConfig(); const redisConfig = getDatabaseConfigFromEnv( - envConfig.redisEndpointsConfigPath, + envConfig.redisEndpointsConfigPath ); faultInjectorClient = new FaultInjectorClient(envConfig.faultInjectorUrl); clientConfig = getDatabaseConfig(redisConfig); }); - beforeEach(async () => { - diagnosticsLog.length = 0; - diagnostics_channel.subscribe("redis.maintenance", onMessageHandler); - - connectSpy.resetHistory(); + afterEach(async () => { + if (client && client.isOpen) { + await client.flushAll(); + client.destroy(); + } + }); - client = createClient({ - socket: { - host: clientConfig.host, - port: clientConfig.port, - ...(clientConfig.tls === true ? { tls: true } : {}), + describe("New Connection Establishment & Traffic Resumption", () => { + const cases: Array<{ + name: string; + clientOptions: Partial; + }> = [ + { + name: "default options", + clientOptions: {}, }, - password: clientConfig.password, - username: clientConfig.username, - RESP: 3, - maintPushNotifications: "auto", - maintMovingEndpointType: "external-ip", - maintRelaxedCommandTimeout: 10000, - maintRelaxedSocketTimeout: 10000, - }); + { + name: "external-ip", + clientOptions: { + maintMovingEndpointType: "external-ip", + }, + }, + { + name: "external-fqdn", + clientOptions: { + maintMovingEndpointType: "external-fqdn", + }, + }, + { + name: "auto", + clientOptions: { + maintMovingEndpointType: "auto", + }, + }, + { + name: "none", + clientOptions: { + maintMovingEndpointType: "none", + }, + }, + ]; - client.on("error", (err: Error) => { - throw new Error(`Client error: ${err.message}`); - }); + for (const { name, clientOptions } of cases) { + it(`should establish new connection and resume traffic afterwards - ${name}`, async () => { + client = await createTestClient(clientConfig, clientOptions); - await client.connect(); - await client.flushAll(); - }); + const spyObject = spyOnTemporaryClientInstanceMethod(client, "connect"); - afterEach(() => { - diagnostics_channel.unsubscribe("redis.maintenance", onMessageHandler); - client.destroy(); - }); + // PART 1 Establish initial connection + const { action_id: lowTimeoutBindAndMigrateActionId } = + await faultInjectorClient.migrateAndBindAction({ + bdbId: clientConfig.bdbId, + clusterIndex: 0, + }); - describe("New Connection Establishment", () => { - it("should establish new connection", async () => { - assert.equal(connectSpy.callCount, 1); + await faultInjectorClient.waitForAction( + lowTimeoutBindAndMigrateActionId + ); - const { action_id: lowTimeoutBindAndMigrateActionId } = - await faultInjectorClient.migrateAndBindAction({ - bdbId: clientConfig.bdbId, - clusterIndex: 0, - }); + const spyResult = await spyObject.getSpy(); - const lowTimeoutWaitPromise = faultInjectorClient.waitForAction( - lowTimeoutBindAndMigrateActionId, - ); + assert.strictEqual(spyResult.spy.callCount, 1); - await lowTimeoutWaitPromise; - assert.equal(connectSpy.callCount, 2); - }); + // PART 2 Verify traffic resumption + const currentTime = Date.now().toString(); + await client.set("key", currentTime); + const result = await client.get("key"); + + assert.strictEqual(result, currentTime); + + spyResult.restore(); + }); + } }); describe("TLS Connection Handoff", () => { - it("TODO receiveMessagesWithTLSEnabledTest", async () => { + it.skip("TODO receiveMessagesWithTLSEnabledTest", async () => { // }); - it("TODO connectionHandoffWithStaticInternalNameTest", async () => { + it.skip("TODO connectionHandoffWithStaticInternalNameTest", async () => { // }); - it("TODO connectionHandoffWithStaticExternalNameTest", async () => { + it.skip("TODO connectionHandoffWithStaticExternalNameTest", async () => { // }); }); - describe("Traffic Resumption", () => { - it("Traffic resumed after handoff", async () => { - const { action_id } = await faultInjectorClient.migrateAndBindAction({ - bdbId: clientConfig.bdbId, - clusterIndex: 0, - }); + describe("Connection Cleanup", () => { + it("should shut down old connection", async () => { + const spyObject = spyOnTemporaryClientInstanceMethod(client, "destroy"); + + const { action_id: lowTimeoutBindAndMigrateActionId } = + await faultInjectorClient.migrateAndBindAction({ + bdbId: clientConfig.bdbId, + clusterIndex: 0, + }); - const workloadPromise = faultInjectorClient.waitForAction(action_id); + await faultInjectorClient.waitForAction(lowTimeoutBindAndMigrateActionId); - const commandPromises = - await TestCommandRunner.fireCommandsUntilStopSignal( - client, - workloadPromise, - ); + const spyResult = await spyObject.getSpy(); - const rejected = ( - await Promise.all(commandPromises.commandPromises) - ).filter((result) => result.status === "rejected"); + assert.equal(spyResult.spy.callCount, 1); - assert.ok(rejected.length === 0); + spyResult.restore(); }); }); }); diff --git a/packages/client/lib/tests/test-scenario/fault-injector-client.ts b/packages/client/lib/tests/test-scenario/fault-injector-client.ts index d6635ac42e6..13c81412b18 100644 --- a/packages/client/lib/tests/test-scenario/fault-injector-client.ts +++ b/packages/client/lib/tests/test-scenario/fault-injector-client.ts @@ -9,7 +9,8 @@ export type ActionType = | "execute_rlutil_command" | "execute_rladmin_command" | "migrate" - | "bind"; + | "bind" + | "update_cluster_config"; export interface ActionRequest { type: ActionType; @@ -47,7 +48,9 @@ export class FaultInjectorClient { * @param action The action request to trigger * @throws {Error} When the HTTP request fails or response cannot be parsed as JSON */ - public triggerAction(action: ActionRequest): Promise { + public triggerAction( + action: ActionRequest + ): Promise { return this.#request("POST", "/action", action); } @@ -60,20 +63,6 @@ export class FaultInjectorClient { return this.#request("GET", `/action/${actionId}`); } - /** - * Executes an rladmin command. - * @param command The rladmin command to execute - * @param bdbId Optional database ID to target - * @throws {Error} When the HTTP request fails or response cannot be parsed as JSON - */ - public executeRladminCommand( - command: string, - bdbId?: string - ): Promise { - const cmd = bdbId ? `rladmin -b ${bdbId} ${command}` : `rladmin ${command}`; - return this.#request("POST", "/rladmin", cmd); - } - /** * Waits for an action to complete. * @param actionId The ID of the action to wait for diff --git a/packages/client/lib/tests/test-scenario/negative-tests.e2e.ts b/packages/client/lib/tests/test-scenario/negative-tests.e2e.ts new file mode 100644 index 00000000000..9e90b80c502 --- /dev/null +++ b/packages/client/lib/tests/test-scenario/negative-tests.e2e.ts @@ -0,0 +1,15 @@ +import assert from "assert"; +import { createClient } from "../../.."; + +describe("Negative tests", () => { + it("should only be enabled with RESP3", () => { + assert.throws( + () => + createClient({ + RESP: 2, + maintPushNotifications: "enabled", + }), + "Error: Graceful Maintenance is only supported with RESP3", + ); + }); +}); diff --git a/packages/client/lib/tests/test-scenario/push-notification.e2e.ts b/packages/client/lib/tests/test-scenario/push-notification.e2e.ts index 3408931728e..9962d0a02dc 100644 --- a/packages/client/lib/tests/test-scenario/push-notification.e2e.ts +++ b/packages/client/lib/tests/test-scenario/push-notification.e2e.ts @@ -2,6 +2,7 @@ import assert from "node:assert"; import diagnostics_channel from "node:diagnostics_channel"; import { FaultInjectorClient } from "./fault-injector-client"; import { + createTestClient, getDatabaseConfig, getDatabaseConfigFromEnv, getEnvConfig, @@ -12,14 +13,21 @@ import { DiagnosticsEvent } from "../../client/enterprise-maintenance-manager"; import { before } from "mocha"; describe("Push Notifications", () => { - const diagnosticsLog: DiagnosticsEvent[] = []; - - const onMessageHandler = (message: unknown) => { - diagnosticsLog.push(message as DiagnosticsEvent); + const createNotificationMessageHandler = ( + result: Record, + notifications: Array + ) => { + return (message: unknown) => { + if (notifications.includes((message as DiagnosticsEvent).type)) { + const event = message as DiagnosticsEvent; + result[event.type] = (result[event.type] ?? 0) + 1; + } + }; }; + let onMessageHandler: ReturnType; let clientConfig: RedisConnectionConfig; - let client: ReturnType>; + let client: ReturnType>; let faultInjectorClient: FaultInjectorClient; before(() => { @@ -32,63 +40,310 @@ describe("Push Notifications", () => { clientConfig = getDatabaseConfig(redisConfig); }); - beforeEach(async () => { - diagnosticsLog.length = 0; - diagnostics_channel.subscribe("redis.maintenance", onMessageHandler); - - client = createClient({ - socket: { - host: clientConfig.host, - port: clientConfig.port, - ...(clientConfig.tls === true ? { tls: true } : {}), - }, - password: clientConfig.password, - username: clientConfig.username, - RESP: 3, - maintPushNotifications: "auto", - maintMovingEndpointType: "external-ip", - maintRelaxedCommandTimeout: 10000, - maintRelaxedSocketTimeout: 10000, + afterEach(() => { + if (onMessageHandler!) { + diagnostics_channel.unsubscribe("redis.maintenance", onMessageHandler); + } + + if (client && client.isOpen) { + client.destroy(); + } + }); + + describe("Push Notifications Enabled", () => { + beforeEach(async () => { + client = await createTestClient(clientConfig); + + await client.flushAll(); }); - client.on("error", (err: Error) => { - throw new Error(`Client error: ${err.message}`); + it("should receive MOVING, MIGRATING, and MIGRATED push notifications", async () => { + const notifications: Array = [ + "MOVING", + "MIGRATING", + "MIGRATED", + ]; + + const diagnosticsMap: Record = {}; + + onMessageHandler = createNotificationMessageHandler( + diagnosticsMap, + notifications + ); + + diagnostics_channel.subscribe("redis.maintenance", onMessageHandler); + + const { action_id: bindAndMigrateActionId } = + await faultInjectorClient.migrateAndBindAction({ + bdbId: clientConfig.bdbId, + clusterIndex: 0, + }); + + await faultInjectorClient.waitForAction(bindAndMigrateActionId); + + assert.strictEqual( + diagnosticsMap.MOVING, + 1, + "Should have received exactly one MOVING notification" + ); + assert.strictEqual( + diagnosticsMap.MIGRATING, + 1, + "Should have received exactly one MIGRATING notification" + ); + assert.strictEqual( + diagnosticsMap.MIGRATED, + 1, + "Should have received exactly one MIGRATED notification" + ); }); - await client.connect(); - }); + it("should receive FAILING_OVER and FAILED_OVER push notifications", async () => { + const notifications: Array = [ + "FAILING_OVER", + "FAILED_OVER", + ]; - afterEach(() => { - diagnostics_channel.unsubscribe("redis.maintenance", onMessageHandler); - client.destroy(); + const diagnosticsMap: Record = {}; + + onMessageHandler = createNotificationMessageHandler( + diagnosticsMap, + notifications + ); + + diagnostics_channel.subscribe("redis.maintenance", onMessageHandler); + + const { action_id: failoverActionId } = + await faultInjectorClient.triggerAction({ + type: "failover", + parameters: { + bdb_id: clientConfig.bdbId.toString(), + cluster_index: 0, + }, + }); + + await faultInjectorClient.waitForAction(failoverActionId); + + assert.strictEqual( + diagnosticsMap.FAILING_OVER, + 1, + "Should have received exactly one FAILING_OVER notification" + ); + assert.strictEqual( + diagnosticsMap.FAILED_OVER, + 1, + "Should have received exactly one FAILED_OVER notification" + ); + }); }); - it("should receive MOVING, MIGRATING, and MIGRATED push notifications", async () => { - const { action_id: migrateActionId } = - await faultInjectorClient.triggerAction<{ action_id: string }>({ - type: "migrate", - parameters: { - cluster_index: "0", - }, + describe("Push Notifications Disabled - Client", () => { + beforeEach(async () => { + client = await createTestClient(clientConfig, { + maintPushNotifications: "disabled", }); - await faultInjectorClient.waitForAction(migrateActionId); + client.on("error", (_err) => { + // Expect the socket to be closed + // Ignore errors + }); + + await client.flushAll(); + }); + + it("should NOT receive MOVING, MIGRATING, and MIGRATED push notifications", async () => { + const notifications: Array = [ + "MOVING", + "MIGRATING", + "MIGRATED", + ]; + + const diagnosticsMap: Record = {}; + + onMessageHandler = createNotificationMessageHandler( + diagnosticsMap, + notifications + ); + + diagnostics_channel.subscribe("redis.maintenance", onMessageHandler); + + const { action_id: bindAndMigrateActionId } = + await faultInjectorClient.migrateAndBindAction({ + bdbId: clientConfig.bdbId, + clusterIndex: 0, + }); + + await faultInjectorClient.waitForAction(bindAndMigrateActionId); + + assert.strictEqual( + diagnosticsMap.MOVING, + undefined, + "Should NOT have received exactly one MOVING notification" + ); + assert.strictEqual( + diagnosticsMap.MIGRATING, + undefined, + "Should NOT have received exactly one MIGRATING notification" + ); + assert.strictEqual( + diagnosticsMap.MIGRATED, + undefined, + "Should NOT have received exactly one MIGRATED notification" + ); + }); - const { action_id: bindActionId } = - await faultInjectorClient.triggerAction<{ action_id: string }>({ - type: "bind", - parameters: { - cluster_index: "0", - bdb_id: `${clientConfig.bdbId}`, - }, + it("should NOT receive FAILING_OVER and FAILED_OVER push notifications", async () => { + const notifications: Array = [ + "FAILING_OVER", + "FAILED_OVER", + ]; + + const diagnosticsMap: Record = {}; + + onMessageHandler = createNotificationMessageHandler( + diagnosticsMap, + notifications + ); + + diagnostics_channel.subscribe("redis.maintenance", onMessageHandler); + + const { action_id: failoverActionId } = + await faultInjectorClient.triggerAction({ + type: "failover", + parameters: { + bdb_id: clientConfig.bdbId.toString(), + cluster_index: 0, + }, + }); + + await faultInjectorClient.waitForAction(failoverActionId); + + assert.strictEqual( + diagnosticsMap.FAILING_OVER, + undefined, + "Should have received exactly one FAILING_OVER notification" + ); + assert.strictEqual( + diagnosticsMap.FAILED_OVER, + undefined, + "Should have received exactly one FAILED_OVER notification" + ); + }); + }); + + describe("Push Notifications Disabled - Server", () => { + beforeEach(async () => { + client = await createTestClient(clientConfig); + + client.on("error", (_err) => { + // Expect the socket to be closed + // Ignore errors }); - await faultInjectorClient.waitForAction(bindActionId); + await client.flushAll(); + }); + + before(async () => { + const { action_id: disablePushNotificationsActionId } = + await faultInjectorClient.triggerAction({ + type: "update_cluster_config", + parameters: { + config: { client_maint_notifications: false }, + }, + }); + + await faultInjectorClient.waitForAction(disablePushNotificationsActionId); + }); + + after(async () => { + const { action_id: enablePushNotificationsActionId } = + await faultInjectorClient.triggerAction({ + type: "update_cluster_config", + parameters: { + config: { client_maint_notifications: true }, + }, + }); - const pushNotificationLogs = diagnosticsLog.filter((log) => { - return ["MOVING", "MIGRATING", "MIGRATED"].includes(log?.type); + await faultInjectorClient.waitForAction(enablePushNotificationsActionId); }); - assert.strictEqual(pushNotificationLogs.length, 3); + it("should NOT receive MOVING, MIGRATING, and MIGRATED push notifications", async () => { + const notifications: Array = [ + "MOVING", + "MIGRATING", + "MIGRATED", + ]; + + const diagnosticsMap: Record = {}; + + onMessageHandler = createNotificationMessageHandler( + diagnosticsMap, + notifications + ); + + diagnostics_channel.subscribe("redis.maintenance", onMessageHandler); + + const { action_id: bindAndMigrateActionId } = + await faultInjectorClient.migrateAndBindAction({ + bdbId: clientConfig.bdbId, + clusterIndex: 0, + }); + + await faultInjectorClient.waitForAction(bindAndMigrateActionId); + + assert.strictEqual( + diagnosticsMap.MOVING, + undefined, + "Should NOT have received exactly one MOVING notification" + ); + assert.strictEqual( + diagnosticsMap.MIGRATING, + undefined, + "Should NOT have received exactly one MIGRATING notification" + ); + assert.strictEqual( + diagnosticsMap.MIGRATED, + undefined, + "Should NOT have received exactly one MIGRATED notification" + ); + }); + + it("should NOT receive FAILING_OVER and FAILED_OVER push notifications", async () => { + const notifications: Array = [ + "FAILING_OVER", + "FAILED_OVER", + ]; + + const diagnosticsMap: Record = {}; + + onMessageHandler = createNotificationMessageHandler( + diagnosticsMap, + notifications + ); + + diagnostics_channel.subscribe("redis.maintenance", onMessageHandler); + + const { action_id: failoverActionId } = + await faultInjectorClient.triggerAction({ + type: "failover", + parameters: { + bdb_id: clientConfig.bdbId.toString(), + cluster_index: 0, + }, + }); + + await faultInjectorClient.waitForAction(failoverActionId); + + assert.strictEqual( + diagnosticsMap.FAILING_OVER, + undefined, + "Should have received exactly one FAILING_OVER notification" + ); + assert.strictEqual( + diagnosticsMap.FAILED_OVER, + undefined, + "Should have received exactly one FAILED_OVER notification" + ); + }); }); }); diff --git a/packages/client/lib/tests/test-scenario/test-command-runner.ts b/packages/client/lib/tests/test-scenario/test-command-runner.ts deleted file mode 100644 index 9e1acc3a8ab..00000000000 --- a/packages/client/lib/tests/test-scenario/test-command-runner.ts +++ /dev/null @@ -1,108 +0,0 @@ -import { randomUUID } from "node:crypto"; -import { setTimeout } from "node:timers/promises"; -import { createClient } from "../../.."; - -/** - * Options for the `fireCommandsUntilStopSignal` method - */ -type FireCommandsUntilStopSignalOptions = { - /** - * Number of commands to fire in each batch - */ - batchSize: number; - /** - * Timeout between batches in milliseconds - */ - timeoutMs: number; - /** - * Function that creates the commands to be executed - */ - createCommands: ( - client: ReturnType> - ) => Array<() => Promise>; -}; - -/** - * Utility class for running test commands until a stop signal is received - */ -export class TestCommandRunner { - private static readonly defaultOptions: FireCommandsUntilStopSignalOptions = { - batchSize: 60, - timeoutMs: 10, - createCommands: ( - client: ReturnType> - ) => [ - () => client.set(randomUUID(), Date.now()), - () => client.get(randomUUID()), - ], - }; - - static #toSettled(p: Promise) { - return p - .then((value) => ({ status: "fulfilled" as const, value, error: null })) - .catch((reason) => ({ - status: "rejected" as const, - value: null, - error: reason, - })); - } - - static async #racePromises({ - timeout, - stopper, - }: { - timeout: Promise; - stopper: Promise; - }) { - return Promise.race([ - TestCommandRunner.#toSettled(timeout).then((result) => ({ - ...result, - stop: false, - })), - TestCommandRunner.#toSettled(stopper).then((result) => ({ - ...result, - stop: true, - })), - ]); - } - - /** - * Fires a batch of test commands until a stop signal is received - * @param client - The Redis client to use - * @param stopSignalPromise - Promise that resolves when the execution should stop - * @param options - Options for the command execution - * @returns An object containing the promises of all executed commands and the result of the stop signal - */ - static async fireCommandsUntilStopSignal( - client: ReturnType>, - stopSignalPromise: Promise, - options?: Partial - ) { - const executeOptions = { - ...TestCommandRunner.defaultOptions, - ...options, - }; - - const commandPromises = []; - - while (true) { - for (let i = 0; i < executeOptions.batchSize; i++) { - for (const command of executeOptions.createCommands(client)) { - commandPromises.push(TestCommandRunner.#toSettled(command())); - } - } - - const result = await TestCommandRunner.#racePromises({ - timeout: setTimeout(executeOptions.timeoutMs), - stopper: stopSignalPromise, - }); - - if (result.stop) { - return { - commandPromises, - stopResult: result, - }; - } - } - } -} diff --git a/packages/client/lib/tests/test-scenario/test-scenario.util.ts b/packages/client/lib/tests/test-scenario/test-scenario.util.ts index b130cdc5386..c98ba90fe19 100644 --- a/packages/client/lib/tests/test-scenario/test-scenario.util.ts +++ b/packages/client/lib/tests/test-scenario/test-scenario.util.ts @@ -110,8 +110,18 @@ export function getDatabaseConfig( }; } -// TODO this should be moved in the tests utils package -export async function blockSetImmediate(fn: () => Promise) { +/** + * Executes the provided function in a context where setImmediate is stubbed to not do anything. + * This blocks setImmediate callbacks from executing + * + * @param command - The command to execute + * @returns The error and duration of the command execution + */ +export async function blockCommand(command: () => Promise) { + let error: any; + + const start = performance.now(); + let setImmediateStub: any; try { @@ -119,79 +129,46 @@ export async function blockSetImmediate(fn: () => Promise) { setImmediateStub.callsFake(() => { //Dont call the callback, effectively blocking execution }); - await fn(); + await command(); + } catch (err: any) { + error = err; } finally { if (setImmediateStub) { setImmediateStub.restore(); } } + + return { + error, + duration: performance.now() - start, + }; } /** - * Factory class for creating and managing Redis clients + * Creates a test client with the provided configuration, connects it and attaches an error handler listener + * @param clientConfig - The Redis connection configuration + * @param options - Optional client options + * @returns The created Redis client */ -export class ClientFactory { - private readonly clients = new Map< - string, - ReturnType> - >(); - - constructor(private readonly config: RedisConnectionConfig) {} - - /** - * Creates a new client with the specified options and connects it to the database - * @param key - The key to store the client under - * @param options - Optional client options - * @returns The created and connected client - */ - async create(key: string, options: Partial = {}) { - const client = createClient({ - socket: { - host: this.config.host, - port: this.config.port, - ...(this.config.tls === true ? { tls: true } : {}), - }, - password: this.config.password, - username: this.config.username, - RESP: 3, - maintPushNotifications: "auto", - maintMovingEndpointType: "auto", - ...options, - }); - - client.on("error", (err: Error) => { - throw new Error(`Client error: ${err.message}`); - }); - - await client.connect(); - - this.clients.set(key, client); - - return client; - } - - /** - * Gets an existing client by key or the first one if no key is provided - * @param key - The key of the client to retrieve - * @returns The client if found, undefined otherwise - */ - get(key?: string) { - if (key) { - return this.clients.get(key); - } - - // Get the first one if no key is provided - return this.clients.values().next().value; - } - - /** - * Destroys all created clients - */ - destroyAll() { - this.clients.forEach((client) => { - if (client && client.isOpen) { - client.destroy(); - } - }); - } +export async function createTestClient( + clientConfig: RedisConnectionConfig, + options: Partial = {} +) { + const client = createClient({ + socket: { + host: clientConfig.host, + port: clientConfig.port, + ...(clientConfig.tls === true ? { tls: true } : {}), + }, + password: clientConfig.password, + username: clientConfig.username, + RESP: 3, + maintPushNotifications: "auto", + maintMovingEndpointType: "auto", + ...options, + }); + + await client.connect(); + + return client; } diff --git a/packages/client/lib/tests/test-scenario/timeout-during-notifications.e2e.ts b/packages/client/lib/tests/test-scenario/timeout-during-notifications.e2e.ts index 7bdf23fcb15..a60aacb703c 100644 --- a/packages/client/lib/tests/test-scenario/timeout-during-notifications.e2e.ts +++ b/packages/client/lib/tests/test-scenario/timeout-during-notifications.e2e.ts @@ -2,22 +2,48 @@ import assert from "node:assert"; import { FaultInjectorClient } from "./fault-injector-client"; import { - ClientFactory, getDatabaseConfig, getDatabaseConfigFromEnv, getEnvConfig, RedisConnectionConfig, - blockSetImmediate + blockCommand, + createTestClient, } from "./test-scenario.util"; import { createClient } from "../../.."; import { before } from "mocha"; -import { TestCommandRunner } from "./test-command-runner"; +import diagnostics_channel from "node:diagnostics_channel"; +import { DiagnosticsEvent } from "../../client/enterprise-maintenance-manager"; describe("Timeout Handling During Notifications", () => { let clientConfig: RedisConnectionConfig; - let clientFactory: ClientFactory; let faultInjectorClient: FaultInjectorClient; - let defaultClient: ReturnType>; + let client: ReturnType>; + + const NORMAL_COMMAND_TIMEOUT = 50; + const RELAXED_COMMAND_TIMEOUT = 2000; + + /** + * Creates a handler for the `redis.maintenance` channel that will execute and block a command on the client + * when a notification is received and save the result in the `result` object. + * This is used to test that the command timeout is relaxed during notifications. + */ + const createNotificationMessageHandler = ( + client: ReturnType>, + result: Record, + notifications: Array + ) => { + return (message: unknown) => { + if (notifications.includes((message as DiagnosticsEvent).type)) { + setImmediate(async () => { + result[(message as DiagnosticsEvent).type] = await blockCommand( + async () => { + await client.set("key", "value"); + } + ); + }); + } + }; + }; before(() => { const envConfig = getEnvConfig(); @@ -27,133 +53,238 @@ describe("Timeout Handling During Notifications", () => { clientConfig = getDatabaseConfig(redisConfig); faultInjectorClient = new FaultInjectorClient(envConfig.faultInjectorUrl); - clientFactory = new ClientFactory(clientConfig); }); beforeEach(async () => { - defaultClient = await clientFactory.create("default"); + client = await createTestClient(clientConfig, { + commandOptions: { timeout: NORMAL_COMMAND_TIMEOUT }, + maintRelaxedCommandTimeout: RELAXED_COMMAND_TIMEOUT, + }); - await defaultClient.flushAll(); + await client.flushAll(); }); - afterEach(async () => { - clientFactory.destroyAll(); + afterEach(() => { + if (client && client.isOpen) { + client.destroy(); + } }); - it("should relax command timeout on MOVING, MIGRATING, and MIGRATED", async () => { + it("should relax command timeout on MOVING, MIGRATING", async () => { // PART 1 - // Set very low timeout to trigger errors - const lowTimeoutClient = await clientFactory.create("lowTimeout", { - maintRelaxedCommandTimeout: 50, + // Normal command timeout + const { error, duration } = await blockCommand(async () => { + await client.set("key", "value"); }); - const { action_id: lowTimeoutBindAndMigrateActionId } = - await faultInjectorClient.migrateAndBindAction({ - bdbId: clientConfig.bdbId, - clusterIndex: 0, - }); - - const lowTimeoutWaitPromise = faultInjectorClient.waitForAction( - lowTimeoutBindAndMigrateActionId + assert.ok( + error instanceof Error, + "Command Timeout error should be instanceof Error" + ); + assert.ok( + duration > NORMAL_COMMAND_TIMEOUT && + duration < NORMAL_COMMAND_TIMEOUT * 1.1, + `Normal command should timeout within normal timeout ms` + ); + assert.strictEqual( + error?.constructor?.name, + "TimeoutError", + "Command Timeout error should be TimeoutError" ); - const lowTimeoutCommandPromises = - await TestCommandRunner.fireCommandsUntilStopSignal( - lowTimeoutClient, - lowTimeoutWaitPromise - ); + // PART 2 + // Command timeout during maintenance + const notifications: Array = [ + "MOVING", + "MIGRATING", + ]; - const lowTimeoutRejectedCommands = ( - await Promise.all(lowTimeoutCommandPromises.commandPromises) - ).filter((result) => result.status === "rejected"); + const result: Record< + DiagnosticsEvent["type"], + { error: any; duration: number } + > = {}; - assert.ok(lowTimeoutRejectedCommands.length > 0); - assert.strictEqual( - lowTimeoutRejectedCommands.filter((rejected) => { - return ( - // TODO instanceof doesn't work for some reason - rejected.error.constructor.name === - "CommandTimeoutDuringMaintananceError" - ); - }).length, - lowTimeoutRejectedCommands.length + const onMessageHandler = createNotificationMessageHandler( + client, + result, + notifications ); - // PART 2 - // Set high timeout to avoid errors - const highTimeoutClient = await clientFactory.create("highTimeout", { - maintRelaxedCommandTimeout: 10000, - }); + diagnostics_channel.subscribe("redis.maintenance", onMessageHandler); - const { action_id: highTimeoutBindAndMigrateActionId } = + const { action_id: bindAndMigrateActionId } = await faultInjectorClient.migrateAndBindAction({ bdbId: clientConfig.bdbId, clusterIndex: 0, }); - const highTimeoutWaitPromise = faultInjectorClient.waitForAction( - highTimeoutBindAndMigrateActionId - ); + await faultInjectorClient.waitForAction(bindAndMigrateActionId); + + diagnostics_channel.unsubscribe("redis.maintenance", onMessageHandler); - const highTimeoutCommandPromises = - await TestCommandRunner.fireCommandsUntilStopSignal( - highTimeoutClient, - highTimeoutWaitPromise + notifications.forEach((notification) => { + assert.ok( + result[notification]?.error instanceof Error, + `${notification} notification error should be instanceof Error` + ); + assert.ok( + result[notification]?.duration > RELAXED_COMMAND_TIMEOUT && + result[notification]?.duration < RELAXED_COMMAND_TIMEOUT * 1.1, + `${notification} notification should timeout within relaxed timeout` ); + assert.strictEqual( + result[notification]?.error?.constructor?.name, + "CommandTimeoutDuringMaintenanceError", + `${notification} notification error should be CommandTimeoutDuringMaintenanceError` + ); + }); + }); - const highTimeoutRejectedCommands = ( - await Promise.all(highTimeoutCommandPromises.commandPromises) - ).filter((result) => result.status === "rejected"); + it("should unrelax command timeout after MIGRATED and MOVING", async () => { + const { action_id: migrateActionId } = + await faultInjectorClient.triggerAction({ + type: "migrate", + parameters: { + cluster_index: 0, + }, + }); - assert.strictEqual(highTimeoutRejectedCommands.length, 0); - }); + await faultInjectorClient.waitForAction(migrateActionId); + + // PART 1 + // After migration + const { error: errorMigrate, duration: durationMigrate } = + await blockCommand(async () => { + await client.set("key", "value"); + }); + + assert.ok( + errorMigrate instanceof Error, + "Command Timeout error should be instanceof Error" + ); + assert.ok( + durationMigrate > NORMAL_COMMAND_TIMEOUT && + durationMigrate < NORMAL_COMMAND_TIMEOUT * 1.1, + `Normal command should timeout within normal timeout ms` + ); + assert.strictEqual( + errorMigrate?.constructor?.name, + "TimeoutError", + "Command Timeout error should be TimeoutError" + ); - it("should unrelax command timeout after MAINTENANCE", async () => { - const clientWithCommandTimeout = await clientFactory.create( - "clientWithCommandTimeout", + const { action_id: bindActionId } = await faultInjectorClient.triggerAction( { - commandOptions: { - timeout: 100, + type: "bind", + parameters: { + bdb_id: clientConfig.bdbId.toString(), + cluster_index: 0, }, } ); - const { action_id: bindAndMigrateActionId } = - await faultInjectorClient.migrateAndBindAction({ - bdbId: clientConfig.bdbId, - clusterIndex: 0, - }); + await faultInjectorClient.waitForAction(bindActionId); - const lowTimeoutWaitPromise = faultInjectorClient.waitForAction( - bindAndMigrateActionId + // PART 2 + // After bind + const { error: errorBind, duration: durationBind } = await blockCommand( + async () => { + await client.set("key", "value"); + } ); - const relaxedTimeoutCommandPromises = - await TestCommandRunner.fireCommandsUntilStopSignal( - clientWithCommandTimeout, - lowTimeoutWaitPromise - ); + assert.ok( + errorBind instanceof Error, + "Command Timeout error should be instanceof Error" + ); + assert.ok( + durationBind > NORMAL_COMMAND_TIMEOUT && + durationBind < NORMAL_COMMAND_TIMEOUT * 1.1, + `Normal command should timeout within normal timeout ms` + ); + assert.strictEqual( + errorBind?.constructor?.name, + "TimeoutError", + "Command Timeout error should be TimeoutError" + ); + }); - const relaxedTimeoutRejectedCommands = ( - await Promise.all(relaxedTimeoutCommandPromises.commandPromises) - ).filter((result) => result.status === "rejected"); + it("should relax command timeout on FAILING_OVER", async () => { + const notifications: Array = ["FAILING_OVER"]; - assert.ok(relaxedTimeoutRejectedCommands.length === 0); + const result: Record< + DiagnosticsEvent["type"], + { error: any; duration: number } + > = {}; - const start = performance.now(); + const onMessageHandler = createNotificationMessageHandler( + client, + result, + notifications + ); - let error: any; - await blockSetImmediate(async () => { - try { - await clientWithCommandTimeout.set("key", "value"); - } catch (err: any) { - error = err; - } + diagnostics_channel.subscribe("redis.maintenance", onMessageHandler); + + const { action_id: failoverActionId } = + await faultInjectorClient.triggerAction({ + type: "failover", + parameters: { + bdb_id: clientConfig.bdbId.toString(), + cluster_index: 0, + }, + }); + + await faultInjectorClient.waitForAction(failoverActionId); + + diagnostics_channel.unsubscribe("redis.maintenance", onMessageHandler); + + notifications.forEach((notification) => { + assert.ok( + result[notification]?.error instanceof Error, + `${notification} notification error should be instanceof Error` + ); + assert.ok( + result[notification]?.duration > RELAXED_COMMAND_TIMEOUT && + result[notification]?.duration < RELAXED_COMMAND_TIMEOUT * 1.1, + `${notification} notification should timeout within relaxed timeout` + ); + assert.strictEqual( + result[notification]?.error?.constructor?.name, + "CommandTimeoutDuringMaintenanceError", + `${notification} notification error should be CommandTimeoutDuringMaintenanceError` + ); }); + }); - // Make sure it took less than 1sec to fail - assert.ok(performance.now() - start < 1000); - assert.ok(error instanceof Error); - assert.ok(error.constructor.name === "TimeoutError"); + it("should unrelax command timeout after FAILED_OVER", async () => { + const { action_id: failoverActionId } = + await faultInjectorClient.triggerAction({ + type: "failover", + parameters: { + bdb_id: clientConfig.bdbId.toString(), + cluster_index: 0, + }, + }); + + await faultInjectorClient.waitForAction(failoverActionId); + + const { error, duration } = await blockCommand(async () => { + await client.set("key", "value"); + }); + + assert.ok( + error instanceof Error, + "Command Timeout error should be instanceof Error" + ); + assert.ok( + duration > NORMAL_COMMAND_TIMEOUT && + duration < NORMAL_COMMAND_TIMEOUT * 1.1, + `Normal command should timeout within normal timeout ms` + ); + assert.strictEqual( + error?.constructor?.name, + "TimeoutError", + "Command Timeout error should be TimeoutError" + ); }); }); diff --git a/packages/client/tsconfig.json b/packages/client/tsconfig.json index b1f7b44d915..f87c7d4f533 100644 --- a/packages/client/tsconfig.json +++ b/packages/client/tsconfig.json @@ -11,7 +11,8 @@ "exclude": [ "./lib/test-utils.ts", "./lib/**/*.spec.ts", - "./lib/sentinel/test-util.ts" + "./lib/sentinel/test-util.ts", + "./lib/tests/**/*.ts" ], "typedocOptions": { "entryPoints": [ From ed7b905479d911bcacbcab5d05a36a247f0f4ab4 Mon Sep 17 00:00:00 2001 From: Elena Kolevska Date: Thu, 18 Sep 2025 14:37:59 +0100 Subject: [PATCH 1722/1748] Improve stale issue management workflow (#3082) MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit **Added Dual-Tier Stale Policy:** - Standard timeline: Issues/PRs still follow 365→30 day cycle - Accelerated timeline: Issues with needs-information label now get 30→7 day cycle - Separate jobs handle each policy independently - Detailed messages explaining why items are marked stale and how to keep them open - Exempt labels: `no-stale`, `needs-information` (for standard policy) - Only `no-stale` exempt for PRs (`needs-info` doesn't apply) --- .github/workflows/stale-issues.yml | 106 ++++++++++++++++++++++++----- 1 file changed, 88 insertions(+), 18 deletions(-) diff --git a/.github/workflows/stale-issues.yml b/.github/workflows/stale-issues.yml index 445af1c818c..ba0fb5587ed 100644 --- a/.github/workflows/stale-issues.yml +++ b/.github/workflows/stale-issues.yml @@ -1,25 +1,95 @@ -name: "Close stale issues" +name: "Stale Issue Management" on: schedule: - - cron: "0 0 * * *" + # Run daily at midnight UTC + - cron: "0 0 * * *" + workflow_dispatch: # Allow manual triggering + +env: + # Default stale policy timeframes + DAYS_BEFORE_STALE: 365 + DAYS_BEFORE_CLOSE: 30 + + # Accelerated timeline for needs-information issues + NEEDS_INFO_DAYS_BEFORE_STALE: 30 + NEEDS_INFO_DAYS_BEFORE_CLOSE: 7 -permissions: {} jobs: stale: - permissions: - issues: write # to close stale issues (actions/stale) - pull-requests: write # to close stale PRs (actions/stale) - runs-on: ubuntu-latest steps: - - uses: actions/stale@v9 - with: - repo-token: ${{ secrets.GITHUB_TOKEN }} - stale-issue-message: 'This issue is marked stale. It will be closed in 30 days if it is not updated.' - stale-pr-message: 'This pull request is marked stale. It will be closed in 30 days if it is not updated.' - days-before-stale: 365 - days-before-close: 30 - stale-issue-label: "Stale" - stale-pr-label: "Stale" - operations-per-run: 10 - remove-stale-when-updated: true + # First step: Handle regular issues (excluding needs-information) + - name: Mark regular issues as stale + uses: actions/stale@v9 + with: + repo-token: ${{ secrets.GITHUB_TOKEN }} + + # Default stale policy + days-before-stale: ${{ env.DAYS_BEFORE_STALE }} + days-before-close: ${{ env.DAYS_BEFORE_CLOSE }} + + # Explicit stale label configuration + stale-issue-label: "stale" + stale-pr-label: "stale" + + stale-issue-message: | + This issue has been automatically marked as stale due to inactivity. + It will be closed in 30 days if no further activity occurs. + If you believe this issue is still relevant, please add a comment to keep it open. + + close-issue-message: | + This issue has been automatically closed due to inactivity. + If you believe this issue is still relevant, please reopen it or create a new issue with updated information. + + # Exclude needs-information issues from this step + exempt-issue-labels: 'no-stale,needs-information' + + # Remove stale label when issue/PR becomes active again + remove-stale-when-updated: true + + # Apply to pull requests with same timeline + days-before-pr-stale: ${{ env.DAYS_BEFORE_STALE }} + days-before-pr-close: ${{ env.DAYS_BEFORE_CLOSE }} + + stale-pr-message: | + This pull request has been automatically marked as stale due to inactivity. + It will be closed in 30 days if no further activity occurs. + + close-pr-message: | + This pull request has been automatically closed due to inactivity. + If you would like to continue this work, please reopen the PR or create a new one. + + # Only exclude no-stale PRs (needs-information PRs follow standard timeline) + exempt-pr-labels: 'no-stale' + + # Second step: Handle needs-information issues with accelerated timeline + - name: Mark needs-information issues as stale + uses: actions/stale@v9 + with: + repo-token: ${{ secrets.GITHUB_TOKEN }} + + # Accelerated timeline for needs-information + days-before-stale: ${{ env.NEEDS_INFO_DAYS_BEFORE_STALE }} + days-before-close: ${{ env.NEEDS_INFO_DAYS_BEFORE_CLOSE }} + + # Explicit stale label configuration + stale-issue-label: "stale" + + # Only target ISSUES with needs-information label (not PRs) + only-issue-labels: 'needs-information' + + stale-issue-message: | + This issue has been marked as stale because it requires additional information + that has not been provided for 30 days. It will be closed in 7 days if the + requested information is not provided. + + close-issue-message: | + This issue has been closed because the requested information was not provided within the specified timeframe. + If you can provide the missing information, please reopen this issue or create a new one. + + # Disable PR processing for this step + days-before-pr-stale: -1 + days-before-pr-close: -1 + + # Remove stale label when issue becomes active again + remove-stale-when-updated: true From e138cbd05bc04f6c5c01864d3582fe2a6f5585dc Mon Sep 17 00:00:00 2001 From: Nikolay Karadzhov Date: Tue, 23 Sep 2025 14:24:37 +0300 Subject: [PATCH 1723/1748] fix(init): option parsing (#3086) * refactor(maint): rename options * fix(init): option parsing * refactor(client): #options cannot be undefined --- .../enterprise-maintenance-manager.spec.ts | 49 ++++++++++++ .../client/enterprise-maintenance-manager.ts | 34 ++++---- packages/client/lib/client/index.spec.ts | 2 +- packages/client/lib/client/index.ts | 77 +++++++++---------- 4 files changed, 108 insertions(+), 54 deletions(-) create mode 100644 packages/client/lib/client/enterprise-maintenance-manager.spec.ts diff --git a/packages/client/lib/client/enterprise-maintenance-manager.spec.ts b/packages/client/lib/client/enterprise-maintenance-manager.spec.ts new file mode 100644 index 00000000000..59e2bfe8c0a --- /dev/null +++ b/packages/client/lib/client/enterprise-maintenance-manager.spec.ts @@ -0,0 +1,49 @@ +import assert from "node:assert"; +import { createClient } from "../../"; + +describe("EnterpriseMaintenanceManager does not prevent proper options parsing", () => { + it("should not throw when initializing without options", async () => { + const client = createClient(); + assert.doesNotThrow(async () => { + //Expected to reject because there is no url or socket provided and there is no running server on localhost + await assert.rejects(client.connect); + }); + }); + + it("should not throw when initializing without url/socket and with maint", async () => { + const client = createClient({ + maintNotifications: "enabled", + RESP: 3, + }); + assert.doesNotThrow(async () => { + //Expected to reject because there is no url or socket provided and there is no running server on localhost + await assert.rejects(client.connect); + }); + }); + it("should not throw when initializing with url and with maint", async () => { + const client = createClient({ + maintNotifications: "enabled", + RESP: 3, + url: "redis://localhost:6379", + }); + assert.doesNotThrow(async () => { + //Expected to reject because there is no url or socket provided and there is no running server on localhost + await assert.rejects(client.connect); + }); + }); + + it("should not throw when initializing with socket and with maint", async () => { + const client = createClient({ + maintNotifications: "enabled", + RESP: 3, + socket: { + host: "localhost", + port: 6379, + }, + }); + assert.doesNotThrow(async () => { + //Expected to reject because there is no url or socket provided and there is no running server on localhost + await assert.rejects(client.connect); + }); + }); +}); diff --git a/packages/client/lib/client/enterprise-maintenance-manager.ts b/packages/client/lib/client/enterprise-maintenance-manager.ts index 631fb1f7115..9892a5be8a4 100644 --- a/packages/client/lib/client/enterprise-maintenance-manager.ts +++ b/packages/client/lib/client/enterprise-maintenance-manager.ts @@ -5,7 +5,7 @@ import { isIP } from "net"; import { lookup } from "dns/promises"; import assert from "node:assert"; import { setTimeout } from "node:timers/promises"; -import RedisSocket from "./socket"; +import RedisSocket, { RedisTcpSocketOptions } from "./socket"; import diagnostics_channel from "node:diagnostics_channel"; export const MAINTENANCE_EVENTS = { @@ -64,12 +64,12 @@ export default class EnterpriseMaintenanceManager { #client: Client; static setupDefaultMaintOptions(options: RedisClientOptions) { - if (options.maintPushNotifications === undefined) { - options.maintPushNotifications = + if (options.maintNotifications === undefined) { + options.maintNotifications = options?.RESP === 3 ? "auto" : "disabled"; } - if (options.maintMovingEndpointType === undefined) { - options.maintMovingEndpointType = "auto"; + if (options.maintEndpointType === undefined) { + options.maintEndpointType = "auto"; } if (options.maintRelaxedSocketTimeout === undefined) { options.maintRelaxedSocketTimeout = 10000; @@ -80,14 +80,20 @@ export default class EnterpriseMaintenanceManager { } static async getHandshakeCommand( - tls: boolean, - host: string, options: RedisClientOptions, ): Promise< | { cmd: Array; errorHandler: (error: Error) => void } | undefined > { - if (options.maintPushNotifications === "disabled") return; + if (options.maintNotifications === "disabled") return; + + const host = options.url + ? new URL(options.url).hostname + : (options.socket as RedisTcpSocketOptions | undefined)?.host; + + if (!host) return; + + const tls = options.socket?.tls ?? false const movingEndpointType = await determineEndpoint(tls, host, options); return { @@ -100,7 +106,7 @@ export default class EnterpriseMaintenanceManager { ], errorHandler: (error: Error) => { dbgMaintenance("handshake failed:", error); - if (options.maintPushNotifications === "enabled") { + if (options.maintNotifications === "enabled") { throw error; } }, @@ -189,7 +195,7 @@ export default class EnterpriseMaintenanceManager { // reconnect to its currently configured endpoint after half of the grace // period that was communicated by the server is over. if (url === null) { - assert(this.#options.maintMovingEndpointType === "none"); + assert(this.#options.maintEndpointType === "none"); assert(this.#options.socket !== undefined); assert("host" in this.#options.socket); assert(typeof this.#options.socket.host === "string"); @@ -329,12 +335,12 @@ async function determineEndpoint( host: string, options: RedisClientOptions, ): Promise { - assert(options.maintMovingEndpointType !== undefined); - if (options.maintMovingEndpointType !== "auto") { + assert(options.maintEndpointType !== undefined); + if (options.maintEndpointType !== "auto") { dbgMaintenance( - `Determine endpoint type: ${options.maintMovingEndpointType}`, + `Determine endpoint type: ${options.maintEndpointType}`, ); - return options.maintMovingEndpointType; + return options.maintEndpointType; } const ip = isIP(host) ? host : (await lookup(host, { family: 0 })).address; diff --git a/packages/client/lib/client/index.spec.ts b/packages/client/lib/client/index.spec.ts index 9c33f2ce847..d7ce00f38ae 100644 --- a/packages/client/lib/client/index.spec.ts +++ b/packages/client/lib/client/index.spec.ts @@ -1,7 +1,7 @@ import { strict as assert } from 'node:assert'; import testUtils, { GLOBAL, waitTillBeenCalled } from '../test-utils'; import RedisClient, { RedisClientOptions, RedisClientType } from '.'; -import { AbortError, ClientClosedError, ClientOfflineError, ConnectionTimeoutError, DisconnectsClientError, ErrorReply, MultiErrorReply, SocketClosedUnexpectedlyError, TimeoutError, WatchError } from '../errors'; +import { AbortError, ClientClosedError, ClientOfflineError, ConnectionTimeoutError, DisconnectsClientError, ErrorReply, MultiErrorReply, TimeoutError, WatchError } from '../errors'; import { defineScript } from '../lua-script'; import { spy, stub } from 'sinon'; import { once } from 'node:events'; diff --git a/packages/client/lib/client/index.ts b/packages/client/lib/client/index.ts index cf5763357a6..d6f62ec458a 100644 --- a/packages/client/lib/client/index.ts +++ b/packages/client/lib/client/index.ts @@ -1,5 +1,5 @@ import COMMANDS from '../commands'; -import RedisSocket, { RedisSocketOptions, RedisTcpSocketOptions } from './socket'; +import RedisSocket, { RedisSocketOptions } from './socket'; import { BasicAuth, CredentialsError, CredentialsProvider, StreamingCredentialsProvider, UnableToObtainNewCredentialsError, Disposable } from '../authx'; import RedisCommandsQueue, { CommandOptions } from './commands-queue'; import { EventEmitter } from 'node:events'; @@ -154,7 +154,7 @@ export interface RedisClientOptions< * * The default is `auto`. */ - maintPushNotifications?: 'disabled' | 'enabled' | 'auto'; + maintNotifications?: 'disabled' | 'enabled' | 'auto'; /** * Controls how the client requests the endpoint to reconnect to during a MOVING notification in Redis Enterprise maintenance. * @@ -167,19 +167,19 @@ export interface RedisClientOptions< * The default is `auto`. */ - maintMovingEndpointType?: MovingEndpointType; + maintEndpointType?: MovingEndpointType; /** * Specifies a more relaxed timeout (in milliseconds) for commands during a maintenance window. - * This helps minimize command timeouts during maintenance. If not provided, the `commandOptions.timeout` - * will be used instead. Timeouts during maintenance period result in a `CommandTimeoutDuringMaintenance` error. + * This helps minimize command timeouts during maintenance. Timeouts during maintenance period result + * in a `CommandTimeoutDuringMaintenance` error. * * The default is 10000 */ maintRelaxedCommandTimeout?: number; /** * Specifies a more relaxed timeout (in milliseconds) for the socket during a maintenance window. - * This helps minimize socket timeouts during maintenance. If not provided, the `socket.timeout` - * will be used instead. Timeouts during maintenance period result in a `SocketTimeoutDuringMaintenance` error. + * This helps minimize socket timeouts during maintenance. Timeouts during maintenance period result + * in a `SocketTimeoutDuringMaintenance` error. * * The default is 10000 */ @@ -429,7 +429,7 @@ export default class RedisClient< return parsed; } - readonly #options?: RedisClientOptions; + readonly #options: RedisClientOptions; #socket: RedisSocket; readonly #queue: RedisCommandsQueue; #selectedDB = 0; @@ -453,7 +453,7 @@ export default class RedisClient< return this._self.#clientSideCache; } - get options(): RedisClientOptions | undefined { + get options(): RedisClientOptions { return this._self.#options; } @@ -503,15 +503,15 @@ export default class RedisClient< this.#socket = this.#initiateSocket(); - if(options?.maintPushNotifications !== 'disabled') { - new EnterpriseMaintenanceManager(this.#queue, this, this.#options!); + if(this.#options.maintNotifications !== 'disabled') { + new EnterpriseMaintenanceManager(this.#queue, this, this.#options); }; - if (options?.clientSideCache) { - if (options.clientSideCache instanceof ClientSideCacheProvider) { - this.#clientSideCache = options.clientSideCache; + if (this.#options.clientSideCache) { + if (this.#options.clientSideCache instanceof ClientSideCacheProvider) { + this.#clientSideCache = this.#options.clientSideCache; } else { - const cscConfig = options.clientSideCache; + const cscConfig = this.#options.clientSideCache; this.#clientSideCache = new BasicClientSideCache(cscConfig); } this.#queue.addPushHandler((push: Array): boolean => { @@ -535,16 +535,16 @@ export default class RedisClient< throw new Error('Client Side Caching is only supported with RESP3'); } - if (options?.maintPushNotifications && options?.maintPushNotifications !== 'disabled' && options?.RESP !== 3) { + if (options?.maintNotifications && options?.maintNotifications !== 'disabled' && options?.RESP !== 3) { throw new Error('Graceful Maintenance is only supported with RESP3'); } } - #initiateOptions(options?: RedisClientOptions): RedisClientOptions | undefined { + #initiateOptions(options: RedisClientOptions = {}): RedisClientOptions { // Convert username/password to credentialsProvider if no credentialsProvider is already in place - if (!options?.credentialsProvider && (options?.username || options?.password)) { + if (!options.credentialsProvider && (options.username || options.password)) { options.credentialsProvider = { type: 'async-credentials-provider', @@ -555,19 +555,19 @@ export default class RedisClient< }; } - if (options?.database) { + if (options.database) { this._self.#selectedDB = options.database; } - if (options?.commandOptions) { + if (options.commandOptions) { this._commandOptions = options.commandOptions; } - if(options?.maintPushNotifications !== 'disabled') { - EnterpriseMaintenanceManager.setupDefaultMaintOptions(options!); + if(options.maintNotifications !== 'disabled') { + EnterpriseMaintenanceManager.setupDefaultMaintOptions(options); } - if (options?.url) { + if (options.url) { const parsedOptions = RedisClient.parseOptions(options); if (parsedOptions?.database) { this._self.#selectedDB = parsedOptions.database; @@ -580,8 +580,8 @@ export default class RedisClient< #initiateQueue(): RedisCommandsQueue { return new RedisCommandsQueue( - this.#options?.RESP ?? 2, - this.#options?.commandsQueueMaxLength, + this.#options.RESP ?? 2, + this.#options.commandsQueueMaxLength, (channel, listeners) => this.emit('sharded-channel-moved', channel, listeners) ); } @@ -591,7 +591,7 @@ export default class RedisClient< */ private reAuthenticate = async (credentials: BasicAuth) => { // Re-authentication is not supported on RESP2 with PubSub active - if (!(this.isPubSubActive && !this.#options?.RESP)) { + if (!(this.isPubSubActive && !this.#options.RESP)) { await this.sendCommand( parseArgs(COMMANDS.AUTH, { username: credentials.username, @@ -640,9 +640,9 @@ export default class RedisClient< Array<{ cmd: CommandArguments } & { errorHandler?: (err: Error) => void }> > { const commands = []; - const cp = this.#options?.credentialsProvider; + const cp = this.#options.credentialsProvider; - if (this.#options?.RESP) { + if (this.#options.RESP) { const hello: HelloOptions = {}; if (cp && cp.type === 'async-credentials-provider') { @@ -702,7 +702,7 @@ export default class RedisClient< } } - if (this.#options?.name) { + if (this.#options.name) { commands.push({ cmd: parseArgs(COMMANDS.CLIENT_SETNAME, this.#options.name) }); @@ -713,11 +713,11 @@ export default class RedisClient< commands.push({ cmd: ['SELECT', this.#selectedDB.toString()] }); } - if (this.#options?.readonly) { + if (this.#options.readonly) { commands.push({ cmd: parseArgs(COMMANDS.READONLY) }); } - if (!this.#options?.disableClientInfo) { + if (!this.#options.disableClientInfo) { commands.push({ cmd: ['CLIENT', 'SETINFO', 'LIB-VER', version], errorHandler: () => { @@ -732,7 +732,7 @@ export default class RedisClient< 'CLIENT', 'SETINFO', 'LIB-NAME', - this.#options?.clientInfoTag + this.#options.clientInfoTag ? `node-redis(${this.#options.clientInfoTag})` : 'node-redis' ], @@ -748,8 +748,7 @@ export default class RedisClient< commands.push({cmd: this.#clientSideCache.trackingOn()}); } - const { tls, host } = this.#options!.socket as RedisTcpSocketOptions; - const maintenanceHandshakeCmd = await EnterpriseMaintenanceManager.getHandshakeCommand(!!tls, host!, this.#options!); + const maintenanceHandshakeCmd = await EnterpriseMaintenanceManager.getHandshakeCommand(this.#options); if(maintenanceHandshakeCmd) { commands.push(maintenanceHandshakeCmd); }; @@ -769,7 +768,7 @@ export default class RedisClient< .on('error', err => { this.emit('error', err); this.#clientSideCache?.onError(); - if (this.#socket.isOpen && !this.#options?.disableOfflineQueue) { + if (this.#socket.isOpen && !this.#options.disableOfflineQueue) { this.#queue.flushWaitingForReply(err); } else { this.#queue.flushAll(err); @@ -817,7 +816,7 @@ export default class RedisClient< } }; - const socket = new RedisSocket(socketInitiator, this.#options?.socket); + const socket = new RedisSocket(socketInitiator, this.#options.socket); this.#attachListeners(socket); return socket; } @@ -825,7 +824,7 @@ export default class RedisClient< #pingTimer?: NodeJS.Timeout; #setPingTimer(): void { - if (!this.#options?.pingInterval || !this.#socket.isReady) return; + if (!this.#options.pingInterval || !this.#socket.isReady) return; clearTimeout(this.#pingTimer); this.#pingTimer = setTimeout(() => { @@ -986,7 +985,7 @@ export default class RedisClient< transformReply: TransformReply | undefined, ) { const csc = this._self.#clientSideCache; - const defaultTypeMapping = this._self.#options?.commandOptions === commandOptions; + const defaultTypeMapping = this._self.#options.commandOptions === commandOptions; const fn = () => { return this.sendCommand(parser.redisArgs, commandOptions) }; @@ -1035,7 +1034,7 @@ export default class RedisClient< ): Promise { if (!this._self.#socket.isOpen) { return Promise.reject(new ClientClosedError()); - } else if (!this._self.#socket.isReady && this._self.#options?.disableOfflineQueue) { + } else if (!this._self.#socket.isReady && this._self.#options.disableOfflineQueue) { return Promise.reject(new ClientOfflineError()); } From e7ff3b5142c799951d09d97e43af2c77ce77cf15 Mon Sep 17 00:00:00 2001 From: GitHub Action Date: Fri, 26 Sep 2025 08:52:43 +0000 Subject: [PATCH 1724/1748] Release client@5.9.0-beta.1 --- package-lock.json | 14 +++++++++++++- packages/client/package.json | 2 +- 2 files changed, 14 insertions(+), 2 deletions(-) diff --git a/package-lock.json b/package-lock.json index 664dbb68ceb..6a9f719168c 100644 --- a/package-lock.json +++ b/package-lock.json @@ -7335,7 +7335,7 @@ }, "packages/client": { "name": "@redis/client", - "version": "5.9.0-beta.0", + "version": "5.9.0-beta.1", "license": "MIT", "dependencies": { "cluster-key-slot": "1.1.2" @@ -7433,6 +7433,18 @@ "node": ">= 18" } }, + "packages/redis/node_modules/@redis/client": { + "version": "5.9.0-beta.0", + "resolved": "https://registry.npmjs.org/@redis/client/-/client-5.9.0-beta.0.tgz", + "integrity": "sha512-2nOFp/C5r2eoyW2S8Rr5RWB+7Fw/6b58ReqRSy5mDErxKT+aI04HvLmnHPKUHXZYQcAPNmxcq1YHslw1zJ+hag==", + "license": "MIT", + "dependencies": { + "cluster-key-slot": "1.1.2" + }, + "engines": { + "node": ">= 18" + } + }, "packages/search": { "name": "@redis/search", "version": "5.9.0-beta.0", diff --git a/packages/client/package.json b/packages/client/package.json index 2b8ad41610a..a48c515484e 100644 --- a/packages/client/package.json +++ b/packages/client/package.json @@ -1,6 +1,6 @@ { "name": "@redis/client", - "version": "5.9.0-beta.0", + "version": "5.9.0-beta.1", "license": "MIT", "main": "./dist/index.js", "types": "./dist/index.d.ts", From 55a4980fa1d45579fcb62e624466d6c3ce97231a Mon Sep 17 00:00:00 2001 From: GitHub Action Date: Fri, 26 Sep 2025 08:52:50 +0000 Subject: [PATCH 1725/1748] Release bloom@5.9.0-beta.1 --- package-lock.json | 16 ++++++++++++++-- packages/bloom/package.json | 4 ++-- 2 files changed, 16 insertions(+), 4 deletions(-) diff --git a/package-lock.json b/package-lock.json index 6a9f719168c..282622cbb93 100644 --- a/package-lock.json +++ b/package-lock.json @@ -7321,7 +7321,7 @@ }, "packages/bloom": { "name": "@redis/bloom", - "version": "5.9.0-beta.0", + "version": "5.9.0-beta.1", "license": "MIT", "devDependencies": { "@redis/test-utils": "*" @@ -7330,7 +7330,7 @@ "node": ">= 18" }, "peerDependencies": { - "@redis/client": "^5.9.0-beta.0" + "@redis/client": "^5.9.0-beta.1" } }, "packages/client": { @@ -7433,6 +7433,18 @@ "node": ">= 18" } }, + "packages/redis/node_modules/@redis/bloom": { + "version": "5.9.0-beta.0", + "resolved": "https://registry.npmjs.org/@redis/bloom/-/bloom-5.9.0-beta.0.tgz", + "integrity": "sha512-qQWIvvvNCLfDL4Y/HExoM5MQGvaZwe4l6Nl6beR3wq1heflyH89OHehzeiXtyVC7iTAWzMZg7cGDsgrBtepK1g==", + "license": "MIT", + "engines": { + "node": ">= 18" + }, + "peerDependencies": { + "@redis/client": "^5.9.0-beta.0" + } + }, "packages/redis/node_modules/@redis/client": { "version": "5.9.0-beta.0", "resolved": "https://registry.npmjs.org/@redis/client/-/client-5.9.0-beta.0.tgz", diff --git a/packages/bloom/package.json b/packages/bloom/package.json index 6947934fc4b..2068945f545 100644 --- a/packages/bloom/package.json +++ b/packages/bloom/package.json @@ -1,6 +1,6 @@ { "name": "@redis/bloom", - "version": "5.9.0-beta.0", + "version": "5.9.0-beta.1", "license": "MIT", "main": "./dist/lib/index.js", "types": "./dist/lib/index.d.ts", @@ -13,7 +13,7 @@ "release": "release-it" }, "peerDependencies": { - "@redis/client": "^5.9.0-beta.0" + "@redis/client": "^5.9.0-beta.1" }, "devDependencies": { "@redis/test-utils": "*" From 2a91b6614bb166c86c87fd2e57d622e323ea3f5c Mon Sep 17 00:00:00 2001 From: GitHub Action Date: Fri, 26 Sep 2025 08:52:56 +0000 Subject: [PATCH 1726/1748] Release json@5.9.0-beta.1 --- package-lock.json | 16 ++++++++++++++-- packages/json/package.json | 4 ++-- 2 files changed, 16 insertions(+), 4 deletions(-) diff --git a/package-lock.json b/package-lock.json index 282622cbb93..88d47dd2e7c 100644 --- a/package-lock.json +++ b/package-lock.json @@ -7407,7 +7407,7 @@ }, "packages/json": { "name": "@redis/json", - "version": "5.9.0-beta.0", + "version": "5.9.0-beta.1", "license": "MIT", "devDependencies": { "@redis/test-utils": "*" @@ -7416,7 +7416,7 @@ "node": ">= 18" }, "peerDependencies": { - "@redis/client": "^5.9.0-beta.0" + "@redis/client": "^5.9.0-beta.1" } }, "packages/redis": { @@ -7457,6 +7457,18 @@ "node": ">= 18" } }, + "packages/redis/node_modules/@redis/json": { + "version": "5.9.0-beta.0", + "resolved": "https://registry.npmjs.org/@redis/json/-/json-5.9.0-beta.0.tgz", + "integrity": "sha512-h3iC0Pr8U0Z3zpJYt1AOO9R2pvbu+/5QKeWmqEPJkpxxYVw/bBs3GztM4zDKHmM/EUnIODXT/JAoD3D1Qx0nXg==", + "license": "MIT", + "engines": { + "node": ">= 18" + }, + "peerDependencies": { + "@redis/client": "^5.9.0-beta.0" + } + }, "packages/search": { "name": "@redis/search", "version": "5.9.0-beta.0", diff --git a/packages/json/package.json b/packages/json/package.json index 552d67ac39b..dc505ed6ff4 100644 --- a/packages/json/package.json +++ b/packages/json/package.json @@ -1,6 +1,6 @@ { "name": "@redis/json", - "version": "5.9.0-beta.0", + "version": "5.9.0-beta.1", "license": "MIT", "main": "./dist/lib/index.js", "types": "./dist/lib/index.d.ts", @@ -13,7 +13,7 @@ "release": "release-it" }, "peerDependencies": { - "@redis/client": "^5.9.0-beta.0" + "@redis/client": "^5.9.0-beta.1" }, "devDependencies": { "@redis/test-utils": "*" From e8109cc74c000e48376fd193896f70159b4c17ac Mon Sep 17 00:00:00 2001 From: GitHub Action Date: Fri, 26 Sep 2025 08:53:04 +0000 Subject: [PATCH 1727/1748] Release search@5.9.0-beta.1 --- package-lock.json | 16 ++++++++++++++-- packages/search/package.json | 4 ++-- 2 files changed, 16 insertions(+), 4 deletions(-) diff --git a/package-lock.json b/package-lock.json index 88d47dd2e7c..9d9705c3a8f 100644 --- a/package-lock.json +++ b/package-lock.json @@ -7469,9 +7469,21 @@ "@redis/client": "^5.9.0-beta.0" } }, + "packages/redis/node_modules/@redis/search": { + "version": "5.9.0-beta.0", + "resolved": "https://registry.npmjs.org/@redis/search/-/search-5.9.0-beta.0.tgz", + "integrity": "sha512-fpfMV59CMUeWkdBCncHe/PzBY4E8+mrg7f3KnlTf7z3CW+E+WTj2xst29JGXrcGDWiwRqocwCps4RS2KreyfzA==", + "license": "MIT", + "engines": { + "node": ">= 18" + }, + "peerDependencies": { + "@redis/client": "^5.9.0-beta.0" + } + }, "packages/search": { "name": "@redis/search", - "version": "5.9.0-beta.0", + "version": "5.9.0-beta.1", "license": "MIT", "devDependencies": { "@redis/test-utils": "*" @@ -7480,7 +7492,7 @@ "node": ">= 18" }, "peerDependencies": { - "@redis/client": "^5.9.0-beta.0" + "@redis/client": "^5.9.0-beta.1" } }, "packages/test-utils": { diff --git a/packages/search/package.json b/packages/search/package.json index 0ca44b22737..f3b83b02d4f 100644 --- a/packages/search/package.json +++ b/packages/search/package.json @@ -1,6 +1,6 @@ { "name": "@redis/search", - "version": "5.9.0-beta.0", + "version": "5.9.0-beta.1", "license": "MIT", "main": "./dist/lib/index.js", "types": "./dist/lib/index.d.ts", @@ -14,7 +14,7 @@ "release": "release-it" }, "peerDependencies": { - "@redis/client": "^5.9.0-beta.0" + "@redis/client": "^5.9.0-beta.1" }, "devDependencies": { "@redis/test-utils": "*" From 87d0e4f3f95f8033a8edba22ec832dae7ee31dc1 Mon Sep 17 00:00:00 2001 From: GitHub Action Date: Fri, 26 Sep 2025 08:53:11 +0000 Subject: [PATCH 1728/1748] Release time-series@5.9.0-beta.1 --- package-lock.json | 16 ++++++++++++++-- packages/time-series/package.json | 4 ++-- 2 files changed, 16 insertions(+), 4 deletions(-) diff --git a/package-lock.json b/package-lock.json index 9d9705c3a8f..a9b236791e1 100644 --- a/package-lock.json +++ b/package-lock.json @@ -7481,6 +7481,18 @@ "@redis/client": "^5.9.0-beta.0" } }, + "packages/redis/node_modules/@redis/time-series": { + "version": "5.9.0-beta.0", + "resolved": "https://registry.npmjs.org/@redis/time-series/-/time-series-5.9.0-beta.0.tgz", + "integrity": "sha512-AjJRNMnTL6t1IVheqO6fX4CvTZ1ZLvCffnsgoQBWSd0WdNEeY/ObwJeziwGbZ8OjmxrnldCgJft8HF+o2/DRrw==", + "license": "MIT", + "engines": { + "node": ">= 18" + }, + "peerDependencies": { + "@redis/client": "^5.9.0-beta.0" + } + }, "packages/search": { "name": "@redis/search", "version": "5.9.0-beta.1", @@ -7561,7 +7573,7 @@ }, "packages/time-series": { "name": "@redis/time-series", - "version": "5.9.0-beta.0", + "version": "5.9.0-beta.1", "license": "MIT", "devDependencies": { "@redis/test-utils": "*" @@ -7570,7 +7582,7 @@ "node": ">= 18" }, "peerDependencies": { - "@redis/client": "^5.9.0-beta.0" + "@redis/client": "^5.9.0-beta.1" } } } diff --git a/packages/time-series/package.json b/packages/time-series/package.json index 81e170b467a..1517416b354 100644 --- a/packages/time-series/package.json +++ b/packages/time-series/package.json @@ -1,6 +1,6 @@ { "name": "@redis/time-series", - "version": "5.9.0-beta.0", + "version": "5.9.0-beta.1", "license": "MIT", "main": "./dist/lib/index.js", "types": "./dist/lib/index.d.ts", @@ -13,7 +13,7 @@ "release": "release-it" }, "peerDependencies": { - "@redis/client": "^5.9.0-beta.0" + "@redis/client": "^5.9.0-beta.1" }, "devDependencies": { "@redis/test-utils": "*" From f37cba43e83cd96ec278c4c427c01b95b9554d3e Mon Sep 17 00:00:00 2001 From: GitHub Action Date: Fri, 26 Sep 2025 08:53:17 +0000 Subject: [PATCH 1729/1748] Release entraid@5.9.0-beta.1 --- package-lock.json | 4 ++-- packages/entraid/package.json | 4 ++-- 2 files changed, 4 insertions(+), 4 deletions(-) diff --git a/package-lock.json b/package-lock.json index a9b236791e1..1336871467c 100644 --- a/package-lock.json +++ b/package-lock.json @@ -7351,7 +7351,7 @@ }, "packages/entraid": { "name": "@redis/entraid", - "version": "5.9.0-beta.0", + "version": "5.9.0-beta.1", "license": "MIT", "dependencies": { "@azure/identity": "^4.7.0", @@ -7370,7 +7370,7 @@ "node": ">= 18" }, "peerDependencies": { - "@redis/client": "^5.9.0-beta.0" + "@redis/client": "^5.9.0-beta.1" } }, "packages/entraid/node_modules/@types/node": { diff --git a/packages/entraid/package.json b/packages/entraid/package.json index b02fdc7ea5e..204f72105ad 100644 --- a/packages/entraid/package.json +++ b/packages/entraid/package.json @@ -1,6 +1,6 @@ { "name": "@redis/entraid", - "version": "5.9.0-beta.0", + "version": "5.9.0-beta.1", "license": "MIT", "main": "./dist/index.js", "types": "./dist/index.d.ts", @@ -22,7 +22,7 @@ "@azure/msal-node": "^2.16.1" }, "peerDependencies": { - "@redis/client": "^5.9.0-beta.0" + "@redis/client": "^5.9.0-beta.1" }, "devDependencies": { "@types/express": "^4.17.21", From f33568e4bfd8fa7b802f025d924af66226816846 Mon Sep 17 00:00:00 2001 From: GitHub Action Date: Fri, 26 Sep 2025 08:53:23 +0000 Subject: [PATCH 1730/1748] Release redis@5.9.0-beta.1 --- package-lock.json | 72 ++++--------------------------------- packages/redis/package.json | 12 +++---- 2 files changed, 12 insertions(+), 72 deletions(-) diff --git a/package-lock.json b/package-lock.json index 1336871467c..85a6579f881 100644 --- a/package-lock.json +++ b/package-lock.json @@ -7420,79 +7420,19 @@ } }, "packages/redis": { - "version": "5.9.0-beta.0", - "license": "MIT", - "dependencies": { - "@redis/bloom": "5.9.0-beta.0", - "@redis/client": "5.9.0-beta.0", - "@redis/json": "5.9.0-beta.0", - "@redis/search": "5.9.0-beta.0", - "@redis/time-series": "5.9.0-beta.0" - }, - "engines": { - "node": ">= 18" - } - }, - "packages/redis/node_modules/@redis/bloom": { - "version": "5.9.0-beta.0", - "resolved": "https://registry.npmjs.org/@redis/bloom/-/bloom-5.9.0-beta.0.tgz", - "integrity": "sha512-qQWIvvvNCLfDL4Y/HExoM5MQGvaZwe4l6Nl6beR3wq1heflyH89OHehzeiXtyVC7iTAWzMZg7cGDsgrBtepK1g==", - "license": "MIT", - "engines": { - "node": ">= 18" - }, - "peerDependencies": { - "@redis/client": "^5.9.0-beta.0" - } - }, - "packages/redis/node_modules/@redis/client": { - "version": "5.9.0-beta.0", - "resolved": "https://registry.npmjs.org/@redis/client/-/client-5.9.0-beta.0.tgz", - "integrity": "sha512-2nOFp/C5r2eoyW2S8Rr5RWB+7Fw/6b58ReqRSy5mDErxKT+aI04HvLmnHPKUHXZYQcAPNmxcq1YHslw1zJ+hag==", + "version": "5.9.0-beta.1", "license": "MIT", "dependencies": { - "cluster-key-slot": "1.1.2" + "@redis/bloom": "5.9.0-beta.1", + "@redis/client": "5.9.0-beta.1", + "@redis/json": "5.9.0-beta.1", + "@redis/search": "5.9.0-beta.1", + "@redis/time-series": "5.9.0-beta.1" }, "engines": { "node": ">= 18" } }, - "packages/redis/node_modules/@redis/json": { - "version": "5.9.0-beta.0", - "resolved": "https://registry.npmjs.org/@redis/json/-/json-5.9.0-beta.0.tgz", - "integrity": "sha512-h3iC0Pr8U0Z3zpJYt1AOO9R2pvbu+/5QKeWmqEPJkpxxYVw/bBs3GztM4zDKHmM/EUnIODXT/JAoD3D1Qx0nXg==", - "license": "MIT", - "engines": { - "node": ">= 18" - }, - "peerDependencies": { - "@redis/client": "^5.9.0-beta.0" - } - }, - "packages/redis/node_modules/@redis/search": { - "version": "5.9.0-beta.0", - "resolved": "https://registry.npmjs.org/@redis/search/-/search-5.9.0-beta.0.tgz", - "integrity": "sha512-fpfMV59CMUeWkdBCncHe/PzBY4E8+mrg7f3KnlTf7z3CW+E+WTj2xst29JGXrcGDWiwRqocwCps4RS2KreyfzA==", - "license": "MIT", - "engines": { - "node": ">= 18" - }, - "peerDependencies": { - "@redis/client": "^5.9.0-beta.0" - } - }, - "packages/redis/node_modules/@redis/time-series": { - "version": "5.9.0-beta.0", - "resolved": "https://registry.npmjs.org/@redis/time-series/-/time-series-5.9.0-beta.0.tgz", - "integrity": "sha512-AjJRNMnTL6t1IVheqO6fX4CvTZ1ZLvCffnsgoQBWSd0WdNEeY/ObwJeziwGbZ8OjmxrnldCgJft8HF+o2/DRrw==", - "license": "MIT", - "engines": { - "node": ">= 18" - }, - "peerDependencies": { - "@redis/client": "^5.9.0-beta.0" - } - }, "packages/search": { "name": "@redis/search", "version": "5.9.0-beta.1", diff --git a/packages/redis/package.json b/packages/redis/package.json index 06b76cc5cbd..8bb1c145df5 100644 --- a/packages/redis/package.json +++ b/packages/redis/package.json @@ -1,7 +1,7 @@ { "name": "redis", "description": "A modern, high performance Redis client", - "version": "5.9.0-beta.0", + "version": "5.9.0-beta.1", "license": "MIT", "main": "./dist/index.js", "types": "./dist/index.d.ts", @@ -13,11 +13,11 @@ "release": "release-it" }, "dependencies": { - "@redis/bloom": "5.9.0-beta.0", - "@redis/client": "5.9.0-beta.0", - "@redis/json": "5.9.0-beta.0", - "@redis/search": "5.9.0-beta.0", - "@redis/time-series": "5.9.0-beta.0" + "@redis/bloom": "5.9.0-beta.1", + "@redis/client": "5.9.0-beta.1", + "@redis/json": "5.9.0-beta.1", + "@redis/search": "5.9.0-beta.1", + "@redis/time-series": "5.9.0-beta.1" }, "engines": { "node": ">= 18" From dee7955579295757f4baa662d4ae5f1b3d40b517 Mon Sep 17 00:00:00 2001 From: killa Date: Wed, 1 Oct 2025 02:50:24 +0800 Subject: [PATCH 1731/1748] fix: add default modules in createClientPool (#3088) --- packages/redis/index.ts | 22 +++++++++++++++++++++- 1 file changed, 21 insertions(+), 1 deletion(-) diff --git a/packages/redis/index.ts b/packages/redis/index.ts index 61da052ea20..61ddb35b7bf 100644 --- a/packages/redis/index.ts +++ b/packages/redis/index.ts @@ -12,7 +12,10 @@ import { RedisClusterType as genericRedisClusterType, RedisSentinelOptions, RedisSentinelType as genericRedisSentinelType, - createSentinel as genericCreateSentinel + createSentinel as genericCreateSentinel, + createClientPool as genericCreateClientPool, + RedisClientPoolType as GenericRedisClientPoolType, + RedisPoolOptions, } from '@redis/client'; import RedisBloomModules from '@redis/bloom'; import RedisJSON from '@redis/json'; @@ -60,6 +63,23 @@ export function createClient< }); } +export function createClientPool< + M extends RedisModules, + F extends RedisFunctions, + S extends RedisScripts, + RESP extends RespVersions, + TYPE_MAPPING extends TypeMapping = {} +>(clientOptions?: Omit, "clientSideCache">, + options?: Partial): GenericRedisClientPoolType { + return genericCreateClientPool({ + ...options, + modules: { + ...modules, + ...(clientOptions?.modules as M) + } + }, options); +} + export type RedisClusterType< M extends RedisModules = RedisDefaultModules, F extends RedisFunctions = {}, From 3d55ec4bc2256f6552545ddbbfec09aac6fcb75e Mon Sep 17 00:00:00 2001 From: andy-stark-redis <164213578+andy-stark-redis@users.noreply.github.com> Date: Tue, 30 Sep 2025 19:55:07 +0100 Subject: [PATCH 1732/1748] docs: DOC-5743 BITOP examples (#3087) --- doctests/dt-bitmap.js | 128 ++++++++++++++++++++++++++++++++++++++++-- 1 file changed, 124 insertions(+), 4 deletions(-) diff --git a/doctests/dt-bitmap.js b/doctests/dt-bitmap.js index 845fd2a721c..0d6a292d0f1 100644 --- a/doctests/dt-bitmap.js +++ b/doctests/dt-bitmap.js @@ -1,11 +1,17 @@ // EXAMPLE: bitmap_tutorial -// HIDE_START +// REMOVE_START import assert from 'assert'; -import { createClient } from 'redis'; +// REMOVE_END +import { createClient, RESP_TYPES } from 'redis'; -const client = createClient(); +const client = createClient({ + commandOptions: { + typeMapping: { + [RESP_TYPES.BLOB_STRING]: Buffer + } + } +}); await client.connect(); -// HIDE_END // REMOVE_START await client.flushDb(); @@ -35,5 +41,119 @@ console.log(res4) // >>> 1 // STEP_END // REMOVE_START assert.equal(res4, 1) +// REMOVE_END + +// STEP_START bitop_setup +await client.setBit("A", 0, 1) +await client.setBit("A", 1, 1) +await client.setBit("A", 3, 1) +await client.setBit("A", 4, 1) + +const res5 = await client.get("A") +console.log(res5.readUInt8(0).toString(2).padStart(8, '0')) +// >>> 11011000 + +await client.setBit("B", 3, 1) +await client.setBit("B", 4, 1) +await client.setBit("B", 7, 1) + +const res6 = await client.get("B") +console.log(res6.readUInt8(0).toString(2).padStart(8, '0')) +// >>> 00011001 + +await client.setBit("C", 1, 1) +await client.setBit("C", 2, 1) +await client.setBit("C", 4, 1) +await client.setBit("C", 5, 1) + +const res7 = await client.get("C") +console.log(res7.readUInt8(0).toString(2).padStart(8, '0')) +// >>> 01101100 +// STEP_END +// REMOVE_START +assert.equal(res5.readUInt8(0), 0b11011000) +assert.equal(res6.readUInt8(0), 0b00011001) +assert.equal(res7.readUInt8(0), 0b01101100) +// REMOVE_END + +// STEP_START bitop_and +await client.bitOp("AND", "R", ["A", "B", "C"]) +const res8 = await client.get("R") +console.log(res8.readUInt8(0).toString(2).padStart(8, '0')) +// >>> 00001000 +// STEP_END +// REMOVE_START +assert.equal(res8.readUInt8(0), 0b00001000) +// REMOVE_END + +// STEP_START bitop_or +await client.bitOp("OR", "R", ["A", "B", "C"]) +const res9 = await client.get("R") +console.log(res9.readUInt8(0).toString(2).padStart(8, '0')) +// >>> 11111101 +// STEP_END +// REMOVE_START +assert.equal(res9.readUInt8(0), 0b11111101) +// REMOVE_END + +// STEP_START bitop_xor +await client.bitOp("XOR", "R", ["A", "B"]) // XOR uses two keys here +const res10 = await client.get("R") +console.log(res10.readUInt8(0).toString(2).padStart(8, '0')) +// >>> 11000001 +// STEP_END +// REMOVE_START +assert.equal(res10.readUInt8(0), 0b11000001) +// REMOVE_END + +// STEP_START bitop_not +await client.bitOp("NOT", "R", "A") +const res11 = await client.get("R") +console.log(res11.readUInt8(0).toString(2).padStart(8, '0')) +// >>> 00100111 +// STEP_END +// REMOVE_START +assert.equal(res11.readUInt8(0), 0b00100111) +// REMOVE_END + +// STEP_START bitop_diff +await client.bitOp("DIFF", "R", ["A", "B", "C"]) +const res12 = await client.get("R") +console.log(res12.readUInt8(0).toString(2).padStart(8, '0')) +// >>> 10000000 +// STEP_END +// REMOVE_START +assert.equal(res12.readUInt8(0), 0b10000000) +// REMOVE_END + +// STEP_START bitop_diff1 +await client.bitOp("DIFF1", "R", ["A", "B", "C"]) +const res13 = await client.get("R") +console.log(res13.readUInt8(0).toString(2).padStart(8, '0')) +// >>> 00100101 +// STEP_END +// REMOVE_START +assert.equal(res13.readUInt8(0), 0b00100101) +// REMOVE_END + +// STEP_START bitop_andor +await client.bitOp("ANDOR", "R", ["A", "B", "C"]) +const res14 = await client.get("R") +console.log(res14.readUInt8(0).toString(2).padStart(8, '0')) +// >>> 01011000 +// STEP_END +// REMOVE_START +assert.equal(res14.readUInt8(0), 0b01011000) +// REMOVE_END + +// STEP_START bitop_one +await client.bitOp("ONE", "R", ["A", "B", "C"]) +const res15 = await client.get("R") +console.log(res15.readUInt8(0).toString(2).padStart(8, '0')) +// >>> 10100101 +// STEP_END +// REMOVE_START +assert.equal(res15.readUInt8(0), 0b10100101) + await client.close(); // REMOVE_END \ No newline at end of file From e588eab249d3b5af5ec55a39cae0da689ecd52c8 Mon Sep 17 00:00:00 2001 From: Pavel Pashov <60297174+PavelPashov@users.noreply.github.com> Date: Wed, 1 Oct 2025 16:33:22 +0300 Subject: [PATCH 1733/1748] fix: add typed/untyped mode support for multi-commands (#3084) --- packages/client/lib/client/index.ts | 6 +++--- packages/client/lib/client/multi-command.ts | 19 ++++++++++++++++--- packages/client/lib/client/pool.ts | 6 ++++-- packages/client/lib/multi-command.ts | 7 +++++++ 4 files changed, 30 insertions(+), 8 deletions(-) diff --git a/packages/client/lib/client/index.ts b/packages/client/lib/client/index.ts index d6f62ec458a..e21eef568f9 100644 --- a/packages/client/lib/client/index.ts +++ b/packages/client/lib/client/index.ts @@ -10,7 +10,7 @@ import { TcpSocketConnectOpts } from 'node:net'; import { PUBSUB_TYPE, PubSubType, PubSubListener, PubSubTypeListeners, ChannelListeners } from './pub-sub'; import { Command, CommandSignature, TypeMapping, CommanderConfig, RedisFunction, RedisFunctions, RedisModules, RedisScript, RedisScripts, ReplyUnion, RespVersions, RedisArgument, ReplyWithTypeMapping, SimpleStringReply, TransformReply, CommandArguments } from '../RESP/types'; import RedisClientMultiCommand, { RedisClientMultiCommandType } from './multi-command'; -import { RedisMultiQueuedCommand } from '../multi-command'; +import { MULTI_MODE, MultiMode, RedisMultiQueuedCommand } from '../multi-command'; import HELLO, { HelloOptions } from '../commands/HELLO'; import { ScanOptions, ScanCommonOptions } from '../commands/SCAN'; import { RedisLegacyClient, RedisLegacyClientType } from './legacy-mode'; @@ -1314,8 +1314,8 @@ export default class RedisClient< return execResult as Array; } - MULTI() { - type Multi = new (...args: ConstructorParameters) => RedisClientMultiCommandType<[], M, F, S, RESP, TYPE_MAPPING>; + MULTI() { + type Multi = new (...args: ConstructorParameters) => RedisClientMultiCommandType; return new ((this as any).Multi as Multi)( this._executeMulti.bind(this), this._executePipeline.bind(this), diff --git a/packages/client/lib/client/multi-command.ts b/packages/client/lib/client/multi-command.ts index a687655b60a..fdb958b8033 100644 --- a/packages/client/lib/client/multi-command.ts +++ b/packages/client/lib/client/multi-command.ts @@ -1,5 +1,5 @@ import COMMANDS from '../commands'; -import RedisMultiCommand, { MULTI_REPLY, MultiReply, MultiReplyType, RedisMultiQueuedCommand } from '../multi-command'; +import RedisMultiCommand, { MULTI_MODE, MULTI_REPLY, MultiMode, MultiReply, MultiReplyType, RedisMultiQueuedCommand } from '../multi-command'; import { ReplyWithTypeMapping, CommandReply, Command, CommandArguments, CommanderConfig, RedisFunctions, RedisModules, RedisScripts, RespVersions, TransformReply, RedisScript, RedisFunction, TypeMapping } from '../RESP/types'; import { attachConfig, functionArgumentsPrefix, getTransformReply } from '../commander'; import { BasicCommandParser } from './parser'; @@ -13,7 +13,7 @@ type CommandSignature< S extends RedisScripts, RESP extends RespVersions, TYPE_MAPPING extends TypeMapping -> = (...args: Tail>) => RedisClientMultiCommandType< +> = (...args: Tail>) => InternalRedisClientMultiCommandType< [...REPLIES, ReplyWithTypeMapping, TYPE_MAPPING>], M, F, @@ -70,7 +70,7 @@ type WithScripts< [P in keyof S]: CommandSignature; }; -export type RedisClientMultiCommandType< +type InternalRedisClientMultiCommandType< REPLIES extends Array, M extends RedisModules, F extends RedisFunctions, @@ -85,6 +85,19 @@ export type RedisClientMultiCommandType< WithScripts ); +type TypedOrAny = + [Flag] extends [MULTI_MODE['TYPED']] ? T : any; + +export type RedisClientMultiCommandType< + isTyped extends MultiMode, + REPLIES extends Array, + M extends RedisModules, + F extends RedisFunctions, + S extends RedisScripts, + RESP extends RespVersions, + TYPE_MAPPING extends TypeMapping +> = TypedOrAny>; + type ExecuteMulti = (commands: Array, selectedDB?: number) => Promise>; export default class RedisClientMultiCommand { diff --git a/packages/client/lib/client/pool.ts b/packages/client/lib/client/pool.ts index b53bb2c7e61..75aca57a9f5 100644 --- a/packages/client/lib/client/pool.ts +++ b/packages/client/lib/client/pool.ts @@ -10,6 +10,7 @@ import RedisClientMultiCommand, { RedisClientMultiCommandType } from './multi-co import { BasicPooledClientSideCache, ClientSideCacheConfig, PooledClientSideCacheProvider } from './cache'; import { BasicCommandParser } from './parser'; import SingleEntryCache from '../single-entry-cache'; +import { MULTI_MODE, MultiMode } from '../multi-command'; export interface RedisPoolOptions { /** @@ -486,8 +487,9 @@ export class RedisClientPool< return this.execute(client => client.sendCommand(args, options)); } - MULTI() { - type Multi = new (...args: ConstructorParameters) => RedisClientMultiCommandType<[], M, F, S, RESP, TYPE_MAPPING>; + + MULTI() { + type Multi = new (...args: ConstructorParameters) => RedisClientMultiCommandType; return new ((this as any).Multi as Multi)( (commands, selectedDB) => this.execute(client => client._executeMulti(commands, selectedDB)), commands => this.execute(client => client._executePipeline(commands)), diff --git a/packages/client/lib/multi-command.ts b/packages/client/lib/multi-command.ts index 3d45a02fb4d..bb30fddc29e 100644 --- a/packages/client/lib/multi-command.ts +++ b/packages/client/lib/multi-command.ts @@ -6,6 +6,13 @@ export type MULTI_REPLY = { TYPED: 'typed'; }; +export type MULTI_MODE = { + TYPED: 'typed'; + UNTYPED: 'untyped'; +}; + +export type MultiMode = MULTI_MODE[keyof MULTI_MODE]; + export type MultiReply = MULTI_REPLY[keyof MULTI_REPLY]; export type MultiReplyType = T extends MULTI_REPLY['TYPED'] ? REPLIES : Array; From 3b0fb9a1611250df34e191a0fcc6ce64e6c5c707 Mon Sep 17 00:00:00 2001 From: GitHub Action Date: Wed, 1 Oct 2025 13:46:33 +0000 Subject: [PATCH 1734/1748] Release client@5.9.0-beta.2 --- package-lock.json | 14 +++++++++++++- packages/client/package.json | 2 +- 2 files changed, 14 insertions(+), 2 deletions(-) diff --git a/package-lock.json b/package-lock.json index 85a6579f881..4f31b310c53 100644 --- a/package-lock.json +++ b/package-lock.json @@ -7335,7 +7335,7 @@ }, "packages/client": { "name": "@redis/client", - "version": "5.9.0-beta.1", + "version": "5.9.0-beta.2", "license": "MIT", "dependencies": { "cluster-key-slot": "1.1.2" @@ -7433,6 +7433,18 @@ "node": ">= 18" } }, + "packages/redis/node_modules/@redis/client": { + "version": "5.9.0-beta.1", + "resolved": "https://registry.npmjs.org/@redis/client/-/client-5.9.0-beta.1.tgz", + "integrity": "sha512-69X9EYy0P9iz7FwAW+lFHOgchAulUv6VbAp1d2PSMT45I1nsB8tbvldbv5+D8OYNKIg8S27hkDFwVNgWEIwlTQ==", + "license": "MIT", + "dependencies": { + "cluster-key-slot": "1.1.2" + }, + "engines": { + "node": ">= 18" + } + }, "packages/search": { "name": "@redis/search", "version": "5.9.0-beta.1", diff --git a/packages/client/package.json b/packages/client/package.json index a48c515484e..cbe6274d25d 100644 --- a/packages/client/package.json +++ b/packages/client/package.json @@ -1,6 +1,6 @@ { "name": "@redis/client", - "version": "5.9.0-beta.1", + "version": "5.9.0-beta.2", "license": "MIT", "main": "./dist/index.js", "types": "./dist/index.d.ts", From fcdac2e47f0a8acf5a9fda7a722ffcde36c4f92a Mon Sep 17 00:00:00 2001 From: GitHub Action Date: Wed, 1 Oct 2025 13:46:40 +0000 Subject: [PATCH 1735/1748] Release bloom@5.9.0-beta.2 --- package-lock.json | 16 ++++++++++++++-- packages/bloom/package.json | 4 ++-- 2 files changed, 16 insertions(+), 4 deletions(-) diff --git a/package-lock.json b/package-lock.json index 4f31b310c53..94b039aec60 100644 --- a/package-lock.json +++ b/package-lock.json @@ -7321,7 +7321,7 @@ }, "packages/bloom": { "name": "@redis/bloom", - "version": "5.9.0-beta.1", + "version": "5.9.0-beta.2", "license": "MIT", "devDependencies": { "@redis/test-utils": "*" @@ -7330,7 +7330,7 @@ "node": ">= 18" }, "peerDependencies": { - "@redis/client": "^5.9.0-beta.1" + "@redis/client": "^5.9.0-beta.2" } }, "packages/client": { @@ -7433,6 +7433,18 @@ "node": ">= 18" } }, + "packages/redis/node_modules/@redis/bloom": { + "version": "5.9.0-beta.1", + "resolved": "https://registry.npmjs.org/@redis/bloom/-/bloom-5.9.0-beta.1.tgz", + "integrity": "sha512-oy1wzhJ31WpvEhfeFMCM2YupAcH2iqU1sxJI2b6QETk4W5RGE5FhrPcSRp7gItRfpc1xNFFRd1YjjcZFplGvXg==", + "license": "MIT", + "engines": { + "node": ">= 18" + }, + "peerDependencies": { + "@redis/client": "^5.9.0-beta.1" + } + }, "packages/redis/node_modules/@redis/client": { "version": "5.9.0-beta.1", "resolved": "https://registry.npmjs.org/@redis/client/-/client-5.9.0-beta.1.tgz", diff --git a/packages/bloom/package.json b/packages/bloom/package.json index 2068945f545..d6e6eb39364 100644 --- a/packages/bloom/package.json +++ b/packages/bloom/package.json @@ -1,6 +1,6 @@ { "name": "@redis/bloom", - "version": "5.9.0-beta.1", + "version": "5.9.0-beta.2", "license": "MIT", "main": "./dist/lib/index.js", "types": "./dist/lib/index.d.ts", @@ -13,7 +13,7 @@ "release": "release-it" }, "peerDependencies": { - "@redis/client": "^5.9.0-beta.1" + "@redis/client": "^5.9.0-beta.2" }, "devDependencies": { "@redis/test-utils": "*" From 46937829733fe9e29820636bae66e42ee767111c Mon Sep 17 00:00:00 2001 From: GitHub Action Date: Wed, 1 Oct 2025 13:46:48 +0000 Subject: [PATCH 1736/1748] Release json@5.9.0-beta.2 --- package-lock.json | 16 ++++++++++++++-- packages/json/package.json | 4 ++-- 2 files changed, 16 insertions(+), 4 deletions(-) diff --git a/package-lock.json b/package-lock.json index 94b039aec60..d21805430e1 100644 --- a/package-lock.json +++ b/package-lock.json @@ -7407,7 +7407,7 @@ }, "packages/json": { "name": "@redis/json", - "version": "5.9.0-beta.1", + "version": "5.9.0-beta.2", "license": "MIT", "devDependencies": { "@redis/test-utils": "*" @@ -7416,7 +7416,7 @@ "node": ">= 18" }, "peerDependencies": { - "@redis/client": "^5.9.0-beta.1" + "@redis/client": "^5.9.0-beta.2" } }, "packages/redis": { @@ -7457,6 +7457,18 @@ "node": ">= 18" } }, + "packages/redis/node_modules/@redis/json": { + "version": "5.9.0-beta.1", + "resolved": "https://registry.npmjs.org/@redis/json/-/json-5.9.0-beta.1.tgz", + "integrity": "sha512-p7FVtHjUQLvMN/GHjcR/3luULxcHkTVOlJsn5tXQqTgIADHFfe/bV7dIlKieuxbaIdsD+uL1+9d1K7CGhjBRUw==", + "license": "MIT", + "engines": { + "node": ">= 18" + }, + "peerDependencies": { + "@redis/client": "^5.9.0-beta.1" + } + }, "packages/search": { "name": "@redis/search", "version": "5.9.0-beta.1", diff --git a/packages/json/package.json b/packages/json/package.json index dc505ed6ff4..826e65bc846 100644 --- a/packages/json/package.json +++ b/packages/json/package.json @@ -1,6 +1,6 @@ { "name": "@redis/json", - "version": "5.9.0-beta.1", + "version": "5.9.0-beta.2", "license": "MIT", "main": "./dist/lib/index.js", "types": "./dist/lib/index.d.ts", @@ -13,7 +13,7 @@ "release": "release-it" }, "peerDependencies": { - "@redis/client": "^5.9.0-beta.1" + "@redis/client": "^5.9.0-beta.2" }, "devDependencies": { "@redis/test-utils": "*" From 4d986d42cc6dde237a77a14f0c6f966e51331879 Mon Sep 17 00:00:00 2001 From: GitHub Action Date: Wed, 1 Oct 2025 13:46:54 +0000 Subject: [PATCH 1737/1748] Release search@5.9.0-beta.2 --- package-lock.json | 16 ++++++++++++++-- packages/search/package.json | 4 ++-- 2 files changed, 16 insertions(+), 4 deletions(-) diff --git a/package-lock.json b/package-lock.json index d21805430e1..e4a36235237 100644 --- a/package-lock.json +++ b/package-lock.json @@ -7469,9 +7469,21 @@ "@redis/client": "^5.9.0-beta.1" } }, + "packages/redis/node_modules/@redis/search": { + "version": "5.9.0-beta.1", + "resolved": "https://registry.npmjs.org/@redis/search/-/search-5.9.0-beta.1.tgz", + "integrity": "sha512-oNqPQCcwJL2b4JI0AxPkYWF7a0kWIVqD2Td2LklFQHIa2Fd3k/jQzFsferSc6tJkasr1Hm7XeIXEAiZMzq/xOQ==", + "license": "MIT", + "engines": { + "node": ">= 18" + }, + "peerDependencies": { + "@redis/client": "^5.9.0-beta.1" + } + }, "packages/search": { "name": "@redis/search", - "version": "5.9.0-beta.1", + "version": "5.9.0-beta.2", "license": "MIT", "devDependencies": { "@redis/test-utils": "*" @@ -7480,7 +7492,7 @@ "node": ">= 18" }, "peerDependencies": { - "@redis/client": "^5.9.0-beta.1" + "@redis/client": "^5.9.0-beta.2" } }, "packages/test-utils": { diff --git a/packages/search/package.json b/packages/search/package.json index f3b83b02d4f..d39d0a70077 100644 --- a/packages/search/package.json +++ b/packages/search/package.json @@ -1,6 +1,6 @@ { "name": "@redis/search", - "version": "5.9.0-beta.1", + "version": "5.9.0-beta.2", "license": "MIT", "main": "./dist/lib/index.js", "types": "./dist/lib/index.d.ts", @@ -14,7 +14,7 @@ "release": "release-it" }, "peerDependencies": { - "@redis/client": "^5.9.0-beta.1" + "@redis/client": "^5.9.0-beta.2" }, "devDependencies": { "@redis/test-utils": "*" From 43e5a582dd8f627b1bf994278d898d9e2b889406 Mon Sep 17 00:00:00 2001 From: GitHub Action Date: Wed, 1 Oct 2025 13:47:01 +0000 Subject: [PATCH 1738/1748] Release time-series@5.9.0-beta.2 --- package-lock.json | 16 ++++++++++++++-- packages/time-series/package.json | 4 ++-- 2 files changed, 16 insertions(+), 4 deletions(-) diff --git a/package-lock.json b/package-lock.json index e4a36235237..0ba70e6a938 100644 --- a/package-lock.json +++ b/package-lock.json @@ -7481,6 +7481,18 @@ "@redis/client": "^5.9.0-beta.1" } }, + "packages/redis/node_modules/@redis/time-series": { + "version": "5.9.0-beta.1", + "resolved": "https://registry.npmjs.org/@redis/time-series/-/time-series-5.9.0-beta.1.tgz", + "integrity": "sha512-yBdlZ1FDKziPGF5vu3P3KxY4icNp22t889p2mwz7vobZnbz4ry3L5YuJr8Y3a3cKbzjGbuUF5X/+V5TMJkhsbg==", + "license": "MIT", + "engines": { + "node": ">= 18" + }, + "peerDependencies": { + "@redis/client": "^5.9.0-beta.1" + } + }, "packages/search": { "name": "@redis/search", "version": "5.9.0-beta.2", @@ -7561,7 +7573,7 @@ }, "packages/time-series": { "name": "@redis/time-series", - "version": "5.9.0-beta.1", + "version": "5.9.0-beta.2", "license": "MIT", "devDependencies": { "@redis/test-utils": "*" @@ -7570,7 +7582,7 @@ "node": ">= 18" }, "peerDependencies": { - "@redis/client": "^5.9.0-beta.1" + "@redis/client": "^5.9.0-beta.2" } } } diff --git a/packages/time-series/package.json b/packages/time-series/package.json index 1517416b354..bd31e6845cb 100644 --- a/packages/time-series/package.json +++ b/packages/time-series/package.json @@ -1,6 +1,6 @@ { "name": "@redis/time-series", - "version": "5.9.0-beta.1", + "version": "5.9.0-beta.2", "license": "MIT", "main": "./dist/lib/index.js", "types": "./dist/lib/index.d.ts", @@ -13,7 +13,7 @@ "release": "release-it" }, "peerDependencies": { - "@redis/client": "^5.9.0-beta.1" + "@redis/client": "^5.9.0-beta.2" }, "devDependencies": { "@redis/test-utils": "*" From a95d5598898a00049291704fb010fc96082468a0 Mon Sep 17 00:00:00 2001 From: GitHub Action Date: Wed, 1 Oct 2025 13:47:07 +0000 Subject: [PATCH 1739/1748] Release entraid@5.9.0-beta.2 --- package-lock.json | 4 ++-- packages/entraid/package.json | 4 ++-- 2 files changed, 4 insertions(+), 4 deletions(-) diff --git a/package-lock.json b/package-lock.json index 0ba70e6a938..111da1bf217 100644 --- a/package-lock.json +++ b/package-lock.json @@ -7351,7 +7351,7 @@ }, "packages/entraid": { "name": "@redis/entraid", - "version": "5.9.0-beta.1", + "version": "5.9.0-beta.2", "license": "MIT", "dependencies": { "@azure/identity": "^4.7.0", @@ -7370,7 +7370,7 @@ "node": ">= 18" }, "peerDependencies": { - "@redis/client": "^5.9.0-beta.1" + "@redis/client": "^5.9.0-beta.2" } }, "packages/entraid/node_modules/@types/node": { diff --git a/packages/entraid/package.json b/packages/entraid/package.json index 204f72105ad..555e2c79411 100644 --- a/packages/entraid/package.json +++ b/packages/entraid/package.json @@ -1,6 +1,6 @@ { "name": "@redis/entraid", - "version": "5.9.0-beta.1", + "version": "5.9.0-beta.2", "license": "MIT", "main": "./dist/index.js", "types": "./dist/index.d.ts", @@ -22,7 +22,7 @@ "@azure/msal-node": "^2.16.1" }, "peerDependencies": { - "@redis/client": "^5.9.0-beta.1" + "@redis/client": "^5.9.0-beta.2" }, "devDependencies": { "@types/express": "^4.17.21", From 17fc7255368c0a8020cddf2150c233738c3893ea Mon Sep 17 00:00:00 2001 From: GitHub Action Date: Wed, 1 Oct 2025 13:47:14 +0000 Subject: [PATCH 1740/1748] Release redis@5.9.0-beta.2 --- package-lock.json | 72 ++++--------------------------------- packages/redis/package.json | 12 +++---- 2 files changed, 12 insertions(+), 72 deletions(-) diff --git a/package-lock.json b/package-lock.json index 111da1bf217..a95a6d1bff9 100644 --- a/package-lock.json +++ b/package-lock.json @@ -7420,79 +7420,19 @@ } }, "packages/redis": { - "version": "5.9.0-beta.1", - "license": "MIT", - "dependencies": { - "@redis/bloom": "5.9.0-beta.1", - "@redis/client": "5.9.0-beta.1", - "@redis/json": "5.9.0-beta.1", - "@redis/search": "5.9.0-beta.1", - "@redis/time-series": "5.9.0-beta.1" - }, - "engines": { - "node": ">= 18" - } - }, - "packages/redis/node_modules/@redis/bloom": { - "version": "5.9.0-beta.1", - "resolved": "https://registry.npmjs.org/@redis/bloom/-/bloom-5.9.0-beta.1.tgz", - "integrity": "sha512-oy1wzhJ31WpvEhfeFMCM2YupAcH2iqU1sxJI2b6QETk4W5RGE5FhrPcSRp7gItRfpc1xNFFRd1YjjcZFplGvXg==", - "license": "MIT", - "engines": { - "node": ">= 18" - }, - "peerDependencies": { - "@redis/client": "^5.9.0-beta.1" - } - }, - "packages/redis/node_modules/@redis/client": { - "version": "5.9.0-beta.1", - "resolved": "https://registry.npmjs.org/@redis/client/-/client-5.9.0-beta.1.tgz", - "integrity": "sha512-69X9EYy0P9iz7FwAW+lFHOgchAulUv6VbAp1d2PSMT45I1nsB8tbvldbv5+D8OYNKIg8S27hkDFwVNgWEIwlTQ==", + "version": "5.9.0-beta.2", "license": "MIT", "dependencies": { - "cluster-key-slot": "1.1.2" + "@redis/bloom": "5.9.0-beta.2", + "@redis/client": "5.9.0-beta.2", + "@redis/json": "5.9.0-beta.2", + "@redis/search": "5.9.0-beta.2", + "@redis/time-series": "5.9.0-beta.2" }, "engines": { "node": ">= 18" } }, - "packages/redis/node_modules/@redis/json": { - "version": "5.9.0-beta.1", - "resolved": "https://registry.npmjs.org/@redis/json/-/json-5.9.0-beta.1.tgz", - "integrity": "sha512-p7FVtHjUQLvMN/GHjcR/3luULxcHkTVOlJsn5tXQqTgIADHFfe/bV7dIlKieuxbaIdsD+uL1+9d1K7CGhjBRUw==", - "license": "MIT", - "engines": { - "node": ">= 18" - }, - "peerDependencies": { - "@redis/client": "^5.9.0-beta.1" - } - }, - "packages/redis/node_modules/@redis/search": { - "version": "5.9.0-beta.1", - "resolved": "https://registry.npmjs.org/@redis/search/-/search-5.9.0-beta.1.tgz", - "integrity": "sha512-oNqPQCcwJL2b4JI0AxPkYWF7a0kWIVqD2Td2LklFQHIa2Fd3k/jQzFsferSc6tJkasr1Hm7XeIXEAiZMzq/xOQ==", - "license": "MIT", - "engines": { - "node": ">= 18" - }, - "peerDependencies": { - "@redis/client": "^5.9.0-beta.1" - } - }, - "packages/redis/node_modules/@redis/time-series": { - "version": "5.9.0-beta.1", - "resolved": "https://registry.npmjs.org/@redis/time-series/-/time-series-5.9.0-beta.1.tgz", - "integrity": "sha512-yBdlZ1FDKziPGF5vu3P3KxY4icNp22t889p2mwz7vobZnbz4ry3L5YuJr8Y3a3cKbzjGbuUF5X/+V5TMJkhsbg==", - "license": "MIT", - "engines": { - "node": ">= 18" - }, - "peerDependencies": { - "@redis/client": "^5.9.0-beta.1" - } - }, "packages/search": { "name": "@redis/search", "version": "5.9.0-beta.2", diff --git a/packages/redis/package.json b/packages/redis/package.json index 8bb1c145df5..c2939f2c5ac 100644 --- a/packages/redis/package.json +++ b/packages/redis/package.json @@ -1,7 +1,7 @@ { "name": "redis", "description": "A modern, high performance Redis client", - "version": "5.9.0-beta.1", + "version": "5.9.0-beta.2", "license": "MIT", "main": "./dist/index.js", "types": "./dist/index.d.ts", @@ -13,11 +13,11 @@ "release": "release-it" }, "dependencies": { - "@redis/bloom": "5.9.0-beta.1", - "@redis/client": "5.9.0-beta.1", - "@redis/json": "5.9.0-beta.1", - "@redis/search": "5.9.0-beta.1", - "@redis/time-series": "5.9.0-beta.1" + "@redis/bloom": "5.9.0-beta.2", + "@redis/client": "5.9.0-beta.2", + "@redis/json": "5.9.0-beta.2", + "@redis/search": "5.9.0-beta.2", + "@redis/time-series": "5.9.0-beta.2" }, "engines": { "node": ">= 18" From 73413e086c26c33cfa0575a00c27ef367e31aa77 Mon Sep 17 00:00:00 2001 From: "Vuong Ngo (Viktor)" <61380192+ntvviktor@users.noreply.github.com> Date: Thu, 2 Oct 2025 17:39:39 +0700 Subject: [PATCH 1741/1748] fix: resolve doubly linked list push issue (#3085) * fix redis sentinel failover * fix: resolve doubly linked list push issue * fix semicolon syntax * correctly set the removed node ref * fix linked list node iterator * revert push logic and refactor remove logic * add linked list tests --- .../client/lib/client/linked-list.spec.ts | 34 ++++++++++++++++++- packages/client/lib/client/linked-list.ts | 21 +++++++----- packages/client/lib/sentinel/index.ts | 15 ++++++-- 3 files changed, 59 insertions(+), 11 deletions(-) diff --git a/packages/client/lib/client/linked-list.spec.ts b/packages/client/lib/client/linked-list.spec.ts index c791d21900d..88379364054 100644 --- a/packages/client/lib/client/linked-list.spec.ts +++ b/packages/client/lib/client/linked-list.spec.ts @@ -6,7 +6,7 @@ import { import { equal, deepEqual } from "assert/strict"; describe("DoublyLinkedList", () => { - const list = new DoublyLinkedList(); + const list = new DoublyLinkedList(); it("should start empty", () => { equal(list.length, 0); @@ -96,6 +96,38 @@ describe("DoublyLinkedList", () => { } equal(count, 6); }); + + it("should handle remove on empty list", () => { + list.reset(); + const node = list.push(1); + list.remove(node); + equal(list.length, 0); + deepEqual(Array.from(list), []); + list.remove(node); + equal(list.length, 0); + deepEqual(Array.from(list), []); + }); + + + it("should safely remove nodes while iterating", () => { + list.reset(); + list.push(1); + list.push(2); + list.push(3); + list.push(4); + list.push(5); + + const visited: number[] = []; + for (const node of list.nodes()) { + visited.push(node.value); + if (node.value % 2 === 0) { + list.remove(node); + } + } + deepEqual(visited, [1, 2, 3, 4, 5]); + equal(list.length, 3); + deepEqual(Array.from(list), [1, 3, 5]); + }); }); describe("SinglyLinkedList", () => { diff --git a/packages/client/lib/client/linked-list.ts b/packages/client/lib/client/linked-list.ts index 461f1d40827..910319268a4 100644 --- a/packages/client/lib/client/linked-list.ts +++ b/packages/client/lib/client/linked-list.ts @@ -29,7 +29,7 @@ export class DoublyLinkedList { ++this.#length; if (this.#tail === undefined) { - return this.#tail = this.#head = { + return this.#head = this.#tail = { previous: this.#head, next: undefined, value @@ -73,7 +73,7 @@ export class DoublyLinkedList { --this.#length; const node = this.#head; if (node.next) { - node.next.previous = node.previous; + node.next.previous = undefined; this.#head = node.next; node.next = undefined; } else { @@ -83,19 +83,23 @@ export class DoublyLinkedList { } remove(node: DoublyLinkedNode) { + if (this.#length === 0) return; --this.#length; if (this.#tail === node) { this.#tail = node.previous; - } - + } if (this.#head === node) { this.#head = node.next; } else { - node.previous!.next = node.next; - node.previous = undefined; + if (node.previous) { + node.previous.next = node.next; + } + if (node.next) { + node.next.previous = node.previous; + } } - + node.previous = undefined; node.next = undefined; } @@ -115,8 +119,9 @@ export class DoublyLinkedList { *nodes() { let node = this.#head; while(node) { + const next = node.next yield node; - node = node.next; + node = next; } } } diff --git a/packages/client/lib/sentinel/index.ts b/packages/client/lib/sentinel/index.ts index 63c45862935..a9a2b9a5e5d 100644 --- a/packages/client/lib/sentinel/index.ts +++ b/packages/client/lib/sentinel/index.ts @@ -928,6 +928,16 @@ class RedisSentinelInternal< } } + #handleSentinelFailure(node: RedisNode) { + const found = this.#sentinelRootNodes.findIndex( + (rootNode) => rootNode.host === node.host && rootNode.port === node.port + ); + if (found !== -1) { + this.#sentinelRootNodes.splice(found, 1); + } + this.#reset(); + } + async close() { this.#destroy = true; @@ -1197,8 +1207,9 @@ class RedisSentinelInternal< error: err }; this.emit('client-error', event); - this.#reset(); - }); + this.#handleSentinelFailure(node); + }) + .on('end', () => this.#handleSentinelFailure(node)); this.#sentinelClient = client; this.#trace(`transform: adding sentinel client connect() to promise list`); From 0438865b8af06592cfe3a400997be74fc757ff54 Mon Sep 17 00:00:00 2001 From: "Bobby I." Date: Mon, 6 Oct 2025 13:53:37 +0300 Subject: [PATCH 1742/1748] chore: Update default Redis version to 8.4-M01-pre in tests (#3092) --- .github/workflows/tests.yml | 2 +- packages/bloom/lib/test-utils.ts | 2 +- packages/client/lib/sentinel/test-util.ts | 2 +- packages/client/lib/test-utils.ts | 2 +- packages/entraid/lib/test-utils.ts | 2 +- packages/json/lib/test-utils.ts | 2 +- packages/search/lib/test-utils.ts | 2 +- packages/test-utils/lib/test-utils.ts | 2 +- packages/time-series/lib/test-utils.ts | 2 +- 9 files changed, 9 insertions(+), 9 deletions(-) diff --git a/.github/workflows/tests.yml b/.github/workflows/tests.yml index f522282d28d..45ada77197f 100644 --- a/.github/workflows/tests.yml +++ b/.github/workflows/tests.yml @@ -22,7 +22,7 @@ jobs: fail-fast: false matrix: node-version: ["18", "20", "22"] - redis-version: ["rs-7.4.0-v1", "8.0.2", "8.2", "8.2.1-pre"] + redis-version: ["rs-7.4.0-v1", "8.0.2", "8.2", "8.4-M01-pre"] steps: - uses: actions/checkout@v4 with: diff --git a/packages/bloom/lib/test-utils.ts b/packages/bloom/lib/test-utils.ts index c2991de60d0..2bad3e07617 100644 --- a/packages/bloom/lib/test-utils.ts +++ b/packages/bloom/lib/test-utils.ts @@ -4,7 +4,7 @@ import RedisBloomModules from '.'; export default TestUtils.createFromConfig({ dockerImageName: 'redislabs/client-libs-test', dockerImageVersionArgument: 'redis-version', - defaultDockerVersion: '8.2.1-pre' + defaultDockerVersion: '8.4-M01-pre' }); export const GLOBAL = { diff --git a/packages/client/lib/sentinel/test-util.ts b/packages/client/lib/sentinel/test-util.ts index 33e8e330c25..6998b31c7ff 100644 --- a/packages/client/lib/sentinel/test-util.ts +++ b/packages/client/lib/sentinel/test-util.ts @@ -174,7 +174,7 @@ export class SentinelFramework extends DockerBase { this.#testUtils = TestUtils.createFromConfig({ dockerImageName: 'redislabs/client-libs-test', dockerImageVersionArgument: 'redis-version', - defaultDockerVersion: '8.2.1-pre' + defaultDockerVersion: '8.4-M01-pre' }); this.#nodeMap = new Map>>>(); this.#sentinelMap = new Map>>>(); diff --git a/packages/client/lib/test-utils.ts b/packages/client/lib/test-utils.ts index e54a7d7647c..e9998f1350e 100644 --- a/packages/client/lib/test-utils.ts +++ b/packages/client/lib/test-utils.ts @@ -9,7 +9,7 @@ import RedisBloomModules from '@redis/bloom'; const utils = TestUtils.createFromConfig({ dockerImageName: 'redislabs/client-libs-test', dockerImageVersionArgument: 'redis-version', - defaultDockerVersion: '8.2.1-pre' + defaultDockerVersion: '8.4-M01-pre' }); export default utils; diff --git a/packages/entraid/lib/test-utils.ts b/packages/entraid/lib/test-utils.ts index 7a624902f5a..add48e79d74 100644 --- a/packages/entraid/lib/test-utils.ts +++ b/packages/entraid/lib/test-utils.ts @@ -6,7 +6,7 @@ import { EntraidCredentialsProvider } from './entraid-credentials-provider'; export const testUtils = TestUtils.createFromConfig({ dockerImageName: 'redislabs/client-libs-test', dockerImageVersionArgument: 'redis-version', - defaultDockerVersion: '8.2.1-pre' + defaultDockerVersion: '8.4-M01-pre' }); const DEBUG_MODE_ARGS = testUtils.isVersionGreaterThan([7]) ? diff --git a/packages/json/lib/test-utils.ts b/packages/json/lib/test-utils.ts index 2cc3804fe90..41e743b7132 100644 --- a/packages/json/lib/test-utils.ts +++ b/packages/json/lib/test-utils.ts @@ -4,7 +4,7 @@ import RedisJSON from '.'; export default TestUtils.createFromConfig({ dockerImageName: 'redislabs/client-libs-test', dockerImageVersionArgument: 'redis-version', - defaultDockerVersion: '8.2.1-pre' + defaultDockerVersion: '8.4-M01-pre' }); export const GLOBAL = { diff --git a/packages/search/lib/test-utils.ts b/packages/search/lib/test-utils.ts index 9b82816cd4f..035ae29dd01 100644 --- a/packages/search/lib/test-utils.ts +++ b/packages/search/lib/test-utils.ts @@ -5,7 +5,7 @@ import { RespVersions } from '@redis/client'; export default TestUtils.createFromConfig({ dockerImageName: 'redislabs/client-libs-test', dockerImageVersionArgument: 'redis-version', - defaultDockerVersion: '8.2.1-pre' + defaultDockerVersion: '8.4-M01-pre' }); export const GLOBAL = { diff --git a/packages/test-utils/lib/test-utils.ts b/packages/test-utils/lib/test-utils.ts index 11364493091..7a172f6c4de 100644 --- a/packages/test-utils/lib/test-utils.ts +++ b/packages/test-utils/lib/test-utils.ts @@ -3,7 +3,7 @@ import TestUtils from './index' export const testUtils = TestUtils.createFromConfig({ dockerImageName: 'redislabs/client-libs-test', dockerImageVersionArgument: 'redis-version', - defaultDockerVersion: '8.2.1-pre' + defaultDockerVersion: '8.4-M01-pre' }); diff --git a/packages/time-series/lib/test-utils.ts b/packages/time-series/lib/test-utils.ts index 2c345961630..0275da9cf2c 100644 --- a/packages/time-series/lib/test-utils.ts +++ b/packages/time-series/lib/test-utils.ts @@ -4,7 +4,7 @@ import TimeSeries from '.'; export default TestUtils.createFromConfig({ dockerImageName: 'redislabs/client-libs-test', dockerImageVersionArgument: 'redis-version', - defaultDockerVersion: '8.2.1-pre' + defaultDockerVersion: '8.4-M01-pre' }); export const GLOBAL = { From adb19c5c5f7f4037ea639ea6b8039e5b53499449 Mon Sep 17 00:00:00 2001 From: Nikolay Karadzhov Date: Mon, 6 Oct 2025 18:30:18 +0300 Subject: [PATCH 1743/1748] tests: Adjust scenario tests according to latest maint naming changes (#3090) * rename maint options according to the latest client options * adjust env variables cae repo uses RE_FAULT_INJECTOR_URL for fault injector DATABASE_NAME is needed to choose from the many databases in cae * fix connection cleanup test --- .../tests/test-scenario/configuration.e2e.ts | 8 +- .../test-scenario/connection-handoff.e2e.ts | 9 +- .../test-scenario/fault-injector-client.ts | 23 +- .../tests/test-scenario/negative-tests.e2e.ts | 2 +- .../tests/test-scenario/pn-failover.e2e.ts | 226 ++++++++++++++++++ .../test-scenario/push-notification.e2e.ts | 113 +-------- .../tests/test-scenario/test-scenario.util.ts | 12 +- .../timeout-during-notifications.e2e.ts | 84 +------ .../tests/test-scenario/to-failover.e2e.ts | 151 ++++++++++++ 9 files changed, 419 insertions(+), 209 deletions(-) create mode 100644 packages/client/lib/tests/test-scenario/pn-failover.e2e.ts create mode 100644 packages/client/lib/tests/test-scenario/to-failover.e2e.ts diff --git a/packages/client/lib/tests/test-scenario/configuration.e2e.ts b/packages/client/lib/tests/test-scenario/configuration.e2e.ts index a648375f6e4..a352a8f10e0 100644 --- a/packages/client/lib/tests/test-scenario/configuration.e2e.ts +++ b/packages/client/lib/tests/test-scenario/configuration.e2e.ts @@ -11,7 +11,7 @@ import { } from "./test-scenario.util"; import { createClient } from "../../.."; import { FaultInjectorClient } from "./fault-injector-client"; -import { MovingEndpointType } from "../../../dist/lib/client/enterprise-maintenance-manager"; +import { MovingEndpointType } from "../../../lib/client/enterprise-maintenance-manager"; import { RedisTcpSocketOptions } from "../../client/socket"; describe("Client Configuration and Handshake", () => { @@ -59,7 +59,7 @@ describe("Client Configuration and Handshake", () => { it(`clientHandshakeWithEndpointType '${endpointType}'`, async () => { try { client = await createTestClient(clientConfig, { - maintMovingEndpointType: endpointType, + maintEndpointType: endpointType }); client.on("error", () => {}); @@ -154,7 +154,7 @@ describe("Client Configuration and Handshake", () => { describe("Feature Enablement", () => { it("connectionHandshakeIncludesEnablingNotifications", async () => { client = await createTestClient(clientConfig, { - maintPushNotifications: "enabled", + maintNotifications: "enabled" }); const { action_id } = await faultInjectorClient.migrateAndBindAction({ @@ -180,7 +180,7 @@ describe("Client Configuration and Handshake", () => { it("disabledDontReceiveNotifications", async () => { try { client = await createTestClient(clientConfig, { - maintPushNotifications: "disabled", + maintNotifications: "disabled", socket: { reconnectStrategy: false } diff --git a/packages/client/lib/tests/test-scenario/connection-handoff.e2e.ts b/packages/client/lib/tests/test-scenario/connection-handoff.e2e.ts index 3fbf5e38d40..7a9a4c24df1 100644 --- a/packages/client/lib/tests/test-scenario/connection-handoff.e2e.ts +++ b/packages/client/lib/tests/test-scenario/connection-handoff.e2e.ts @@ -86,25 +86,25 @@ describe("Connection Handoff", () => { { name: "external-ip", clientOptions: { - maintMovingEndpointType: "external-ip", + maintEndpointType: "external-ip", }, }, { name: "external-fqdn", clientOptions: { - maintMovingEndpointType: "external-fqdn", + maintEndpointType: "external-fqdn", }, }, { name: "auto", clientOptions: { - maintMovingEndpointType: "auto", + maintEndpointType: "auto", }, }, { name: "none", clientOptions: { - maintMovingEndpointType: "none", + maintEndpointType: "none", }, }, ]; @@ -156,6 +156,7 @@ describe("Connection Handoff", () => { describe("Connection Cleanup", () => { it("should shut down old connection", async () => { + client = await createTestClient(clientConfig); const spyObject = spyOnTemporaryClientInstanceMethod(client, "destroy"); const { action_id: lowTimeoutBindAndMigrateActionId } = diff --git a/packages/client/lib/tests/test-scenario/fault-injector-client.ts b/packages/client/lib/tests/test-scenario/fault-injector-client.ts index 13c81412b18..c03fa1afa1e 100644 --- a/packages/client/lib/tests/test-scenario/fault-injector-client.ts +++ b/packages/client/lib/tests/test-scenario/fault-injector-client.ts @@ -54,6 +54,20 @@ export class FaultInjectorClient { return this.#request("POST", "/action", action); } + // public async printStatus() { + // const action = { + // type: 'execute_rladmin_command', + // parameters: { + // rladmin_command: "status", + // bdb_id: "1" + // } + // } + // const { action_id } = await this.#request<{action_id: string}>("POST", "/action", action); + // const status = await this.waitForAction(action_id); + // //@ts-ignore + // console.log(status.output.output); + // } + /** * Gets the status of a specific action. * @param actionId The ID of the action to check @@ -87,7 +101,13 @@ export class FaultInjectorClient { while (Date.now() - startTime < maxWaitTime) { const action = await this.getActionStatus(actionId); - if (["finished", "failed", "success"].includes(action.status)) { + if (action.status === "failed") { + throw new Error( + `Action id: ${actionId} failed! Error: ${action.error}` + ); + } + + if (["finished", "success"].includes(action.status)) { return action; } @@ -118,6 +138,7 @@ export class FaultInjectorClient { type: "migrate", params: { cluster_index: clusterIndexStr, + bdb_id: bdbIdStr, }, }, { diff --git a/packages/client/lib/tests/test-scenario/negative-tests.e2e.ts b/packages/client/lib/tests/test-scenario/negative-tests.e2e.ts index 9e90b80c502..51558777016 100644 --- a/packages/client/lib/tests/test-scenario/negative-tests.e2e.ts +++ b/packages/client/lib/tests/test-scenario/negative-tests.e2e.ts @@ -7,7 +7,7 @@ describe("Negative tests", () => { () => createClient({ RESP: 2, - maintPushNotifications: "enabled", + maintNotifications: "enabled", }), "Error: Graceful Maintenance is only supported with RESP3", ); diff --git a/packages/client/lib/tests/test-scenario/pn-failover.e2e.ts b/packages/client/lib/tests/test-scenario/pn-failover.e2e.ts new file mode 100644 index 00000000000..7b977f33a25 --- /dev/null +++ b/packages/client/lib/tests/test-scenario/pn-failover.e2e.ts @@ -0,0 +1,226 @@ +import assert from "node:assert"; +import diagnostics_channel from "node:diagnostics_channel"; +import { FaultInjectorClient } from "./fault-injector-client"; +import { + createTestClient, + getDatabaseConfig, + getDatabaseConfigFromEnv, + getEnvConfig, + RedisConnectionConfig, +} from "./test-scenario.util"; +import { createClient } from "../../.."; +import { DiagnosticsEvent } from "../../client/enterprise-maintenance-manager"; +import { before } from "mocha"; + +describe("Push Notifications", () => { + const createNotificationMessageHandler = ( + result: Record, + notifications: Array + ) => { + return (message: unknown) => { + if (notifications.includes((message as DiagnosticsEvent).type)) { + const event = message as DiagnosticsEvent; + result[event.type] = (result[event.type] ?? 0) + 1; + } + }; + }; + + let onMessageHandler: ReturnType; + let clientConfig: RedisConnectionConfig; + let client: ReturnType>; + let faultInjectorClient: FaultInjectorClient; + + before(() => { + const envConfig = getEnvConfig(); + const redisConfig = getDatabaseConfigFromEnv( + envConfig.redisEndpointsConfigPath + ); + + faultInjectorClient = new FaultInjectorClient(envConfig.faultInjectorUrl); + clientConfig = getDatabaseConfig(redisConfig); + }); + + afterEach(() => { + if (onMessageHandler!) { + diagnostics_channel.unsubscribe("redis.maintenance", onMessageHandler); + } + + if (client && client.isOpen) { + client.destroy(); + } + }); + + describe("Push Notifications Enabled", () => { + beforeEach(async () => { + client = await createTestClient(clientConfig); + + await client.flushAll(); + }); + + it("should receive FAILING_OVER and FAILED_OVER push notifications", async () => { + const notifications: Array = [ + "FAILING_OVER", + "FAILED_OVER", + ]; + + const diagnosticsMap: Record = {}; + + onMessageHandler = createNotificationMessageHandler( + diagnosticsMap, + notifications + ); + + diagnostics_channel.subscribe("redis.maintenance", onMessageHandler); + + const { action_id: failoverActionId } = + await faultInjectorClient.triggerAction({ + type: "failover", + parameters: { + bdb_id: clientConfig.bdbId.toString(), + cluster_index: 0, + }, + }); + + await faultInjectorClient.waitForAction(failoverActionId); + + assert.strictEqual( + diagnosticsMap.FAILING_OVER, + 1, + "Should have received exactly one FAILING_OVER notification" + ); + assert.strictEqual( + diagnosticsMap.FAILED_OVER, + 1, + "Should have received exactly one FAILED_OVER notification" + ); + }); + }); + + describe("Push Notifications Disabled - Client", () => { + beforeEach(async () => { + client = await createTestClient(clientConfig, { + maintNotifications: "disabled", + }); + + client.on("error", (_err) => { + // Expect the socket to be closed + // Ignore errors + }); + + await client.flushAll(); + }); + + it("should NOT receive FAILING_OVER and FAILED_OVER push notifications", async () => { + const notifications: Array = [ + "FAILING_OVER", + "FAILED_OVER", + ]; + + const diagnosticsMap: Record = {}; + + onMessageHandler = createNotificationMessageHandler( + diagnosticsMap, + notifications + ); + + diagnostics_channel.subscribe("redis.maintenance", onMessageHandler); + + const { action_id: failoverActionId } = + await faultInjectorClient.triggerAction({ + type: "failover", + parameters: { + bdb_id: clientConfig.bdbId.toString(), + cluster_index: 0, + }, + }); + + await faultInjectorClient.waitForAction(failoverActionId); + + assert.strictEqual( + diagnosticsMap.FAILING_OVER, + undefined, + "Should have received exactly one FAILING_OVER notification" + ); + assert.strictEqual( + diagnosticsMap.FAILED_OVER, + undefined, + "Should have received exactly one FAILED_OVER notification" + ); + }); + }); + + describe("Push Notifications Disabled - Server", () => { + beforeEach(async () => { + client = await createTestClient(clientConfig); + + client.on("error", (_err) => { + // Expect the socket to be closed + // Ignore errors + }); + + await client.flushAll(); + }); + + before(async () => { + const { action_id: disablePushNotificationsActionId } = + await faultInjectorClient.triggerAction({ + type: "update_cluster_config", + parameters: { + config: { client_maint_notifications: false }, + }, + }); + + await faultInjectorClient.waitForAction(disablePushNotificationsActionId); + }); + + after(async () => { + const { action_id: enablePushNotificationsActionId } = + await faultInjectorClient.triggerAction({ + type: "update_cluster_config", + parameters: { + config: { client_maint_notifications: true }, + }, + }); + + await faultInjectorClient.waitForAction(enablePushNotificationsActionId); + }); + + it("should NOT receive FAILING_OVER and FAILED_OVER push notifications", async () => { + const notifications: Array = [ + "FAILING_OVER", + "FAILED_OVER", + ]; + + const diagnosticsMap: Record = {}; + + onMessageHandler = createNotificationMessageHandler( + diagnosticsMap, + notifications + ); + + diagnostics_channel.subscribe("redis.maintenance", onMessageHandler); + + const { action_id: failoverActionId } = + await faultInjectorClient.triggerAction({ + type: "failover", + parameters: { + bdb_id: clientConfig.bdbId.toString(), + cluster_index: 0, + }, + }); + + await faultInjectorClient.waitForAction(failoverActionId); + + assert.strictEqual( + diagnosticsMap.FAILING_OVER, + undefined, + "Should have received exactly one FAILING_OVER notification" + ); + assert.strictEqual( + diagnosticsMap.FAILED_OVER, + undefined, + "Should have received exactly one FAILED_OVER notification" + ); + }); + }); +}); diff --git a/packages/client/lib/tests/test-scenario/push-notification.e2e.ts b/packages/client/lib/tests/test-scenario/push-notification.e2e.ts index 9962d0a02dc..bfaef8351b4 100644 --- a/packages/client/lib/tests/test-scenario/push-notification.e2e.ts +++ b/packages/client/lib/tests/test-scenario/push-notification.e2e.ts @@ -98,49 +98,12 @@ describe("Push Notifications", () => { ); }); - it("should receive FAILING_OVER and FAILED_OVER push notifications", async () => { - const notifications: Array = [ - "FAILING_OVER", - "FAILED_OVER", - ]; - - const diagnosticsMap: Record = {}; - - onMessageHandler = createNotificationMessageHandler( - diagnosticsMap, - notifications - ); - - diagnostics_channel.subscribe("redis.maintenance", onMessageHandler); - - const { action_id: failoverActionId } = - await faultInjectorClient.triggerAction({ - type: "failover", - parameters: { - bdb_id: clientConfig.bdbId.toString(), - cluster_index: 0, - }, - }); - - await faultInjectorClient.waitForAction(failoverActionId); - - assert.strictEqual( - diagnosticsMap.FAILING_OVER, - 1, - "Should have received exactly one FAILING_OVER notification" - ); - assert.strictEqual( - diagnosticsMap.FAILED_OVER, - 1, - "Should have received exactly one FAILED_OVER notification" - ); - }); }); describe("Push Notifications Disabled - Client", () => { beforeEach(async () => { client = await createTestClient(clientConfig, { - maintPushNotifications: "disabled", + maintNotifications: "disabled", }); client.on("error", (_err) => { @@ -192,43 +155,6 @@ describe("Push Notifications", () => { ); }); - it("should NOT receive FAILING_OVER and FAILED_OVER push notifications", async () => { - const notifications: Array = [ - "FAILING_OVER", - "FAILED_OVER", - ]; - - const diagnosticsMap: Record = {}; - - onMessageHandler = createNotificationMessageHandler( - diagnosticsMap, - notifications - ); - - diagnostics_channel.subscribe("redis.maintenance", onMessageHandler); - - const { action_id: failoverActionId } = - await faultInjectorClient.triggerAction({ - type: "failover", - parameters: { - bdb_id: clientConfig.bdbId.toString(), - cluster_index: 0, - }, - }); - - await faultInjectorClient.waitForAction(failoverActionId); - - assert.strictEqual( - diagnosticsMap.FAILING_OVER, - undefined, - "Should have received exactly one FAILING_OVER notification" - ); - assert.strictEqual( - diagnosticsMap.FAILED_OVER, - undefined, - "Should have received exactly one FAILED_OVER notification" - ); - }); }); describe("Push Notifications Disabled - Server", () => { @@ -308,42 +234,5 @@ describe("Push Notifications", () => { ); }); - it("should NOT receive FAILING_OVER and FAILED_OVER push notifications", async () => { - const notifications: Array = [ - "FAILING_OVER", - "FAILED_OVER", - ]; - - const diagnosticsMap: Record = {}; - - onMessageHandler = createNotificationMessageHandler( - diagnosticsMap, - notifications - ); - - diagnostics_channel.subscribe("redis.maintenance", onMessageHandler); - - const { action_id: failoverActionId } = - await faultInjectorClient.triggerAction({ - type: "failover", - parameters: { - bdb_id: clientConfig.bdbId.toString(), - cluster_index: 0, - }, - }); - - await faultInjectorClient.waitForAction(failoverActionId); - - assert.strictEqual( - diagnosticsMap.FAILING_OVER, - undefined, - "Should have received exactly one FAILING_OVER notification" - ); - assert.strictEqual( - diagnosticsMap.FAILED_OVER, - undefined, - "Should have received exactly one FAILED_OVER notification" - ); - }); }); }); diff --git a/packages/client/lib/tests/test-scenario/test-scenario.util.ts b/packages/client/lib/tests/test-scenario/test-scenario.util.ts index c98ba90fe19..96df0acbd6d 100644 --- a/packages/client/lib/tests/test-scenario/test-scenario.util.ts +++ b/packages/client/lib/tests/test-scenario/test-scenario.util.ts @@ -43,13 +43,13 @@ export function getEnvConfig(): EnvConfig { ); } - if (!process.env.FAULT_INJECTION_API_URL) { - throw new Error("FAULT_INJECTION_API_URL environment variable must be set"); + if (!process.env.RE_FAULT_INJECTOR_URL) { + throw new Error("RE_FAULT_INJECTOR_URL environment variable must be set"); } return { redisEndpointsConfigPath: process.env.REDIS_ENDPOINTS_CONFIG_PATH, - faultInjectorUrl: process.env.FAULT_INJECTION_API_URL, + faultInjectorUrl: process.env.RE_FAULT_INJECTOR_URL, }; } @@ -86,7 +86,7 @@ export interface RedisConnectionConfig { */ export function getDatabaseConfig( databasesConfig: DatabasesConfig, - databaseName?: string + databaseName = process.env.DATABASE_NAME ): RedisConnectionConfig { const dbConfig = databaseName ? databasesConfig[databaseName] @@ -163,8 +163,8 @@ export async function createTestClient( password: clientConfig.password, username: clientConfig.username, RESP: 3, - maintPushNotifications: "auto", - maintMovingEndpointType: "auto", + maintNotifications: "auto", + maintEndpointType: "auto", ...options, }); diff --git a/packages/client/lib/tests/test-scenario/timeout-during-notifications.e2e.ts b/packages/client/lib/tests/test-scenario/timeout-during-notifications.e2e.ts index a60aacb703c..30cdd4669cb 100644 --- a/packages/client/lib/tests/test-scenario/timeout-during-notifications.e2e.ts +++ b/packages/client/lib/tests/test-scenario/timeout-during-notifications.e2e.ts @@ -146,6 +146,7 @@ describe("Timeout Handling During Notifications", () => { type: "migrate", parameters: { cluster_index: 0, + bdb_id: clientConfig.bdbId.toString(), }, }); @@ -163,7 +164,7 @@ describe("Timeout Handling During Notifications", () => { "Command Timeout error should be instanceof Error" ); assert.ok( - durationMigrate > NORMAL_COMMAND_TIMEOUT && + durationMigrate >= NORMAL_COMMAND_TIMEOUT && durationMigrate < NORMAL_COMMAND_TIMEOUT * 1.1, `Normal command should timeout within normal timeout ms` ); @@ -198,7 +199,7 @@ describe("Timeout Handling During Notifications", () => { "Command Timeout error should be instanceof Error" ); assert.ok( - durationBind > NORMAL_COMMAND_TIMEOUT && + durationBind >= NORMAL_COMMAND_TIMEOUT && durationBind < NORMAL_COMMAND_TIMEOUT * 1.1, `Normal command should timeout within normal timeout ms` ); @@ -208,83 +209,4 @@ describe("Timeout Handling During Notifications", () => { "Command Timeout error should be TimeoutError" ); }); - - it("should relax command timeout on FAILING_OVER", async () => { - const notifications: Array = ["FAILING_OVER"]; - - const result: Record< - DiagnosticsEvent["type"], - { error: any; duration: number } - > = {}; - - const onMessageHandler = createNotificationMessageHandler( - client, - result, - notifications - ); - - diagnostics_channel.subscribe("redis.maintenance", onMessageHandler); - - const { action_id: failoverActionId } = - await faultInjectorClient.triggerAction({ - type: "failover", - parameters: { - bdb_id: clientConfig.bdbId.toString(), - cluster_index: 0, - }, - }); - - await faultInjectorClient.waitForAction(failoverActionId); - - diagnostics_channel.unsubscribe("redis.maintenance", onMessageHandler); - - notifications.forEach((notification) => { - assert.ok( - result[notification]?.error instanceof Error, - `${notification} notification error should be instanceof Error` - ); - assert.ok( - result[notification]?.duration > RELAXED_COMMAND_TIMEOUT && - result[notification]?.duration < RELAXED_COMMAND_TIMEOUT * 1.1, - `${notification} notification should timeout within relaxed timeout` - ); - assert.strictEqual( - result[notification]?.error?.constructor?.name, - "CommandTimeoutDuringMaintenanceError", - `${notification} notification error should be CommandTimeoutDuringMaintenanceError` - ); - }); - }); - - it("should unrelax command timeout after FAILED_OVER", async () => { - const { action_id: failoverActionId } = - await faultInjectorClient.triggerAction({ - type: "failover", - parameters: { - bdb_id: clientConfig.bdbId.toString(), - cluster_index: 0, - }, - }); - - await faultInjectorClient.waitForAction(failoverActionId); - - const { error, duration } = await blockCommand(async () => { - await client.set("key", "value"); - }); - - assert.ok( - error instanceof Error, - "Command Timeout error should be instanceof Error" - ); - assert.ok( - duration > NORMAL_COMMAND_TIMEOUT && - duration < NORMAL_COMMAND_TIMEOUT * 1.1, - `Normal command should timeout within normal timeout ms` - ); - assert.strictEqual( - error?.constructor?.name, - "TimeoutError", - "Command Timeout error should be TimeoutError" - ); - }); }); diff --git a/packages/client/lib/tests/test-scenario/to-failover.e2e.ts b/packages/client/lib/tests/test-scenario/to-failover.e2e.ts new file mode 100644 index 00000000000..506aa6f74bc --- /dev/null +++ b/packages/client/lib/tests/test-scenario/to-failover.e2e.ts @@ -0,0 +1,151 @@ +import assert from "node:assert"; + +import { FaultInjectorClient } from "./fault-injector-client"; +import { + getDatabaseConfig, + getDatabaseConfigFromEnv, + getEnvConfig, + RedisConnectionConfig, + blockCommand, + createTestClient, +} from "./test-scenario.util"; +import { createClient } from "../../.."; +import { before } from "mocha"; +import diagnostics_channel from "node:diagnostics_channel"; +import { DiagnosticsEvent } from "../../client/enterprise-maintenance-manager"; + +describe("Timeout Handling During Notifications", () => { + let clientConfig: RedisConnectionConfig; + let faultInjectorClient: FaultInjectorClient; + let client: ReturnType>; + + const NORMAL_COMMAND_TIMEOUT = 50; + const RELAXED_COMMAND_TIMEOUT = 2000; + + /** + * Creates a handler for the `redis.maintenance` channel that will execute and block a command on the client + * when a notification is received and save the result in the `result` object. + * This is used to test that the command timeout is relaxed during notifications. + */ + const createNotificationMessageHandler = ( + client: ReturnType>, + result: Record, + notifications: Array + ) => { + return (message: unknown) => { + if (notifications.includes((message as DiagnosticsEvent).type)) { + setImmediate(async () => { + result[(message as DiagnosticsEvent).type] = await blockCommand( + async () => { + await client.set("key", "value"); + } + ); + }); + } + }; + }; + + before(() => { + const envConfig = getEnvConfig(); + const redisConfig = getDatabaseConfigFromEnv( + envConfig.redisEndpointsConfigPath + ); + + clientConfig = getDatabaseConfig(redisConfig); + faultInjectorClient = new FaultInjectorClient(envConfig.faultInjectorUrl); + }); + + beforeEach(async () => { + client = await createTestClient(clientConfig, { + commandOptions: { timeout: NORMAL_COMMAND_TIMEOUT }, + maintRelaxedCommandTimeout: RELAXED_COMMAND_TIMEOUT, + }); + + await client.flushAll(); + }); + + afterEach(() => { + if (client && client.isOpen) { + client.destroy(); + } + }); + + it("should relax command timeout on FAILING_OVER", async () => { + const notifications: Array = ["FAILING_OVER"]; + + const result: Record< + DiagnosticsEvent["type"], + { error: any; duration: number } + > = {}; + + const onMessageHandler = createNotificationMessageHandler( + client, + result, + notifications + ); + + diagnostics_channel.subscribe("redis.maintenance", onMessageHandler); + + const { action_id: failoverActionId } = + await faultInjectorClient.triggerAction({ + type: "failover", + parameters: { + bdb_id: clientConfig.bdbId.toString(), + cluster_index: 0, + }, + }); + + await faultInjectorClient.waitForAction(failoverActionId); + + diagnostics_channel.unsubscribe("redis.maintenance", onMessageHandler); + + notifications.forEach((notification) => { + assert.ok( + result[notification]?.error instanceof Error, + `${notification} notification error should be instanceof Error` + ); + assert.ok( + result[notification]?.duration > RELAXED_COMMAND_TIMEOUT && + result[notification]?.duration < RELAXED_COMMAND_TIMEOUT * 1.1, + `${notification} notification should timeout within relaxed timeout` + ); + assert.strictEqual( + result[notification]?.error?.constructor?.name, + "CommandTimeoutDuringMaintenanceError", + `${notification} notification error should be CommandTimeoutDuringMaintenanceError` + ); + }); + }); + + it("should unrelax command timeout after FAILED_OVER", async () => { + const { action_id: failoverActionId } = + await faultInjectorClient.triggerAction({ + type: "failover", + parameters: { + bdb_id: clientConfig.bdbId.toString(), + cluster_index: 0, + }, + }); + + await faultInjectorClient.waitForAction(failoverActionId); + + const { error, duration } = await blockCommand(async () => { + await client.set("key", "value"); + }); + + assert.ok( + error instanceof Error, + "Command Timeout error should be instanceof Error" + ); + assert.ok( + duration > NORMAL_COMMAND_TIMEOUT && + duration < NORMAL_COMMAND_TIMEOUT * 1.1, + `Normal command should timeout within normal timeout ms` + ); + assert.strictEqual( + error?.constructor?.name, + "TimeoutError", + "Command Timeout error should be TimeoutError" + ); + }); +}); From bab26b08a7e265330abde8a67f82382045f9f503 Mon Sep 17 00:00:00 2001 From: Nikolay Karadzhov Date: Tue, 7 Oct 2025 12:26:13 +0300 Subject: [PATCH 1744/1748] test(maint): touch up wrong assertions (#3096) --- .../timeout-during-notifications.e2e.ts | 12 ++++++------ .../lib/tests/test-scenario/to-failover.e2e.ts | 8 ++++---- 2 files changed, 10 insertions(+), 10 deletions(-) diff --git a/packages/client/lib/tests/test-scenario/timeout-during-notifications.e2e.ts b/packages/client/lib/tests/test-scenario/timeout-during-notifications.e2e.ts index 30cdd4669cb..848e17f4506 100644 --- a/packages/client/lib/tests/test-scenario/timeout-during-notifications.e2e.ts +++ b/packages/client/lib/tests/test-scenario/timeout-during-notifications.e2e.ts @@ -82,8 +82,8 @@ describe("Timeout Handling During Notifications", () => { "Command Timeout error should be instanceof Error" ); assert.ok( - duration > NORMAL_COMMAND_TIMEOUT && - duration < NORMAL_COMMAND_TIMEOUT * 1.1, + duration >= NORMAL_COMMAND_TIMEOUT && + duration < NORMAL_COMMAND_TIMEOUT * 1.2, `Normal command should timeout within normal timeout ms` ); assert.strictEqual( @@ -128,8 +128,8 @@ describe("Timeout Handling During Notifications", () => { `${notification} notification error should be instanceof Error` ); assert.ok( - result[notification]?.duration > RELAXED_COMMAND_TIMEOUT && - result[notification]?.duration < RELAXED_COMMAND_TIMEOUT * 1.1, + result[notification]?.duration >= RELAXED_COMMAND_TIMEOUT && + result[notification]?.duration < RELAXED_COMMAND_TIMEOUT * 1.2, `${notification} notification should timeout within relaxed timeout` ); assert.strictEqual( @@ -165,7 +165,7 @@ describe("Timeout Handling During Notifications", () => { ); assert.ok( durationMigrate >= NORMAL_COMMAND_TIMEOUT && - durationMigrate < NORMAL_COMMAND_TIMEOUT * 1.1, + durationMigrate < NORMAL_COMMAND_TIMEOUT * 1.2, `Normal command should timeout within normal timeout ms` ); assert.strictEqual( @@ -200,7 +200,7 @@ describe("Timeout Handling During Notifications", () => { ); assert.ok( durationBind >= NORMAL_COMMAND_TIMEOUT && - durationBind < NORMAL_COMMAND_TIMEOUT * 1.1, + durationBind < NORMAL_COMMAND_TIMEOUT * 1.2, `Normal command should timeout within normal timeout ms` ); assert.strictEqual( diff --git a/packages/client/lib/tests/test-scenario/to-failover.e2e.ts b/packages/client/lib/tests/test-scenario/to-failover.e2e.ts index 506aa6f74bc..765859bfc8a 100644 --- a/packages/client/lib/tests/test-scenario/to-failover.e2e.ts +++ b/packages/client/lib/tests/test-scenario/to-failover.e2e.ts @@ -105,8 +105,8 @@ describe("Timeout Handling During Notifications", () => { `${notification} notification error should be instanceof Error` ); assert.ok( - result[notification]?.duration > RELAXED_COMMAND_TIMEOUT && - result[notification]?.duration < RELAXED_COMMAND_TIMEOUT * 1.1, + result[notification]?.duration >= RELAXED_COMMAND_TIMEOUT && + result[notification]?.duration < RELAXED_COMMAND_TIMEOUT * 1.2, `${notification} notification should timeout within relaxed timeout` ); assert.strictEqual( @@ -138,8 +138,8 @@ describe("Timeout Handling During Notifications", () => { "Command Timeout error should be instanceof Error" ); assert.ok( - duration > NORMAL_COMMAND_TIMEOUT && - duration < NORMAL_COMMAND_TIMEOUT * 1.1, + duration >= NORMAL_COMMAND_TIMEOUT && + duration < NORMAL_COMMAND_TIMEOUT * 1.2, `Normal command should timeout within normal timeout ms` ); assert.strictEqual( From 8a1b4b40912dc3143606d5a0039683a3fe066e33 Mon Sep 17 00:00:00 2001 From: Nikolay Karadzhov Date: Tue, 7 Oct 2025 12:31:24 +0300 Subject: [PATCH 1745/1748] fix(pool): make createClientPool use provided options (#3095) fixes: #3094 --- packages/redis/index.ts | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/packages/redis/index.ts b/packages/redis/index.ts index 61ddb35b7bf..f4341bbf48f 100644 --- a/packages/redis/index.ts +++ b/packages/redis/index.ts @@ -72,7 +72,7 @@ export function createClientPool< >(clientOptions?: Omit, "clientSideCache">, options?: Partial): GenericRedisClientPoolType { return genericCreateClientPool({ - ...options, + ...clientOptions, modules: { ...modules, ...(clientOptions?.modules as M) From 5b63382e6abec9e5796bd16caff12fe1dd92d52b Mon Sep 17 00:00:00 2001 From: Stef Schoonderwoerd Date: Tue, 7 Oct 2025 11:43:38 +0200 Subject: [PATCH 1746/1748] feat(client): Emit invalidate events from client (#3076) * add emitInvalidate option * Add documentation for event * Re-write emitInvalidate logic * Fix issues after merge --------- Co-authored-by: Nikolay Karadzhov --- README.md | 1 + packages/client/lib/client/index.ts | 33 ++++++++++++++++++++++++++--- 2 files changed, 31 insertions(+), 3 deletions(-) diff --git a/README.md b/README.md index 1789a51d37e..e6332764e4c 100644 --- a/README.md +++ b/README.md @@ -293,6 +293,7 @@ The Node Redis client class is an Nodejs EventEmitter and it emits an event each | `error` | An error has occurred—usually a network issue such as "Socket closed unexpectedly" | `(error: Error)` | | `reconnecting` | Client is trying to reconnect to the server | _No arguments_ | | `sharded-channel-moved` | See [here](https://github.com/redis/node-redis/blob/master/docs/pub-sub.md#sharded-channel-moved-event) | See [here](https://github.com/redis/node-redis/blob/master/docs/pub-sub.md#sharded-channel-moved-event) | +| `invalidate` | Client Tracking is on with `emitInvalidate` and a key is invalidated | `(key: RedisItem \| null)` | > :warning: You **MUST** listen to `error` events. If a client doesn't have at least one `error` listener registered and > an `error` occurs, that error will be thrown and the Node.js process will exit. See the [ > `EventEmitter` docs](https://nodejs.org/api/events.html#events_error_events) for more details. diff --git a/packages/client/lib/client/index.ts b/packages/client/lib/client/index.ts index e21eef568f9..919baab0df9 100644 --- a/packages/client/lib/client/index.ts +++ b/packages/client/lib/client/index.ts @@ -145,6 +145,11 @@ export interface RedisClientOptions< * Tag to append to library name that is sent to the Redis server */ clientInfoTag?: string; + /** + * When set to true, client tracking is turned on and the client emits `invalidate` events when it receives invalidation messages from the redis server. + * Mutually exclusive with `clientSideCache` option. + */ + emitInvalidate?: boolean; /** * Controls how the client handles Redis Enterprise maintenance push notifications. * @@ -525,6 +530,19 @@ export default class RedisClient< this.#clientSideCache?.invalidate(null) } + return true + }); + } else if (options?.emitInvalidate) { + this.#queue.addPushHandler((push: Array): boolean => { + if (push[0].toString() !== 'invalidate') return false; + + if (push[1] !== null) { + for (const key of push[1]) { + this.emit('invalidate', key); + } + } else { + this.emit('invalidate', null); + } return true }); } @@ -534,11 +552,15 @@ export default class RedisClient< if (options?.clientSideCache && options?.RESP !== 3) { throw new Error('Client Side Caching is only supported with RESP3'); } - + if (options?.emitInvalidate && options?.RESP !== 3) { + throw new Error('emitInvalidate is only supported with RESP3'); + } + if (options?.clientSideCache && options?.emitInvalidate) { + throw new Error('emitInvalidate is not supported (or necessary) when clientSideCache is enabled'); + } if (options?.maintNotifications && options?.maintNotifications !== 'disabled' && options?.RESP !== 3) { throw new Error('Graceful Maintenance is only supported with RESP3'); } - } #initiateOptions(options: RedisClientOptions = {}): RedisClientOptions { @@ -743,12 +765,17 @@ export default class RedisClient< } }); } - + if (this.#clientSideCache) { commands.push({cmd: this.#clientSideCache.trackingOn()}); } + if (this.#options?.emitInvalidate) { + commands.push({cmd: ['CLIENT', 'TRACKING', 'ON']}); + } + const maintenanceHandshakeCmd = await EnterpriseMaintenanceManager.getHandshakeCommand(this.#options); + if(maintenanceHandshakeCmd) { commands.push(maintenanceHandshakeCmd); }; From d6d8d8e8ed98f401d1fb69b97b08a767ee9de166 Mon Sep 17 00:00:00 2001 From: blackman <125454400+watersRand@users.noreply.github.com> Date: Wed, 8 Oct 2025 16:47:57 +0300 Subject: [PATCH 1747/1748] fix(client): export various enum values (#3074) Certain command parameter enums, specifically ClientKillFilters for the CLIENT KILL command, were not publicly exported by the @redis/client package. The purpose of this change is to make the ClientKillFilters enum accessible to all package consumers, eliminating the need for users to rely on an unstable internal import path. This matters because it provides a reliable and supported way for developers to use commands that require these enums, improving the package's usability and API stability. fixes: #2805 Co-authored-by: Nikolay Karadzhov --- packages/client/index.ts | 4 +--- packages/client/lib/commands/index.ts | 20 +++++++++++++++----- 2 files changed, 16 insertions(+), 8 deletions(-) diff --git a/packages/client/index.ts b/packages/client/index.ts index 2deb0c39b47..09426cb50a3 100644 --- a/packages/client/index.ts +++ b/packages/client/index.ts @@ -31,9 +31,7 @@ export const createSentinel = RedisSentinel.create; export { GEO_REPLY_WITH, GeoReplyWith } from './lib/commands/GEOSEARCH_WITH'; -export { SetOptions } from './lib/commands/SET'; -export { REDIS_FLUSH_MODES } from './lib/commands/FLUSHALL'; +export { SetOptions, CLIENT_KILL_FILTERS, FAILOVER_MODES, CLUSTER_SLOT_STATES, COMMAND_LIST_FILTER_BY, REDIS_FLUSH_MODES } from './lib/commands' export { BasicClientSideCache, BasicPooledClientSideCache } from './lib/client/cache'; - diff --git a/packages/client/lib/commands/index.ts b/packages/client/lib/commands/index.ts index 05c07bbf34a..54ede43d011 100644 --- a/packages/client/lib/commands/index.ts +++ b/packages/client/lib/commands/index.ts @@ -35,7 +35,7 @@ import CLIENT_GETNAME from './CLIENT_GETNAME'; import CLIENT_GETREDIR from './CLIENT_GETREDIR'; import CLIENT_ID from './CLIENT_ID'; import CLIENT_INFO from './CLIENT_INFO'; -import CLIENT_KILL from './CLIENT_KILL'; +import CLIENT_KILL, { CLIENT_KILL_FILTERS } from './CLIENT_KILL'; import CLIENT_LIST from './CLIENT_LIST'; import CLIENT_NO_EVICT from './CLIENT_NO-EVICT'; import CLIENT_NO_TOUCH from './CLIENT_NO-TOUCH'; @@ -51,7 +51,7 @@ import CLUSTER_COUNT_FAILURE_REPORTS from './CLUSTER_COUNT-FAILURE-REPORTS'; import CLUSTER_COUNTKEYSINSLOT from './CLUSTER_COUNTKEYSINSLOT'; import CLUSTER_DELSLOTS from './CLUSTER_DELSLOTS'; import CLUSTER_DELSLOTSRANGE from './CLUSTER_DELSLOTSRANGE'; -import CLUSTER_FAILOVER from './CLUSTER_FAILOVER'; +import CLUSTER_FAILOVER, { FAILOVER_MODES } from './CLUSTER_FAILOVER'; import CLUSTER_FLUSHSLOTS from './CLUSTER_FLUSHSLOTS'; import CLUSTER_FORGET from './CLUSTER_FORGET'; import CLUSTER_GETKEYSINSLOT from './CLUSTER_GETKEYSINSLOT'; @@ -67,13 +67,13 @@ import CLUSTER_REPLICATE from './CLUSTER_REPLICATE'; import CLUSTER_RESET from './CLUSTER_RESET'; import CLUSTER_SAVECONFIG from './CLUSTER_SAVECONFIG'; import CLUSTER_SET_CONFIG_EPOCH from './CLUSTER_SET-CONFIG-EPOCH'; -import CLUSTER_SETSLOT from './CLUSTER_SETSLOT'; +import CLUSTER_SETSLOT, { CLUSTER_SLOT_STATES } from './CLUSTER_SETSLOT'; import CLUSTER_SLOTS from './CLUSTER_SLOTS'; import COMMAND_COUNT from './COMMAND_COUNT'; import COMMAND_GETKEYS from './COMMAND_GETKEYS'; import COMMAND_GETKEYSANDFLAGS from './COMMAND_GETKEYSANDFLAGS'; import COMMAND_INFO from './COMMAND_INFO'; -import COMMAND_LIST from './COMMAND_LIST'; +import COMMAND_LIST, { COMMAND_LIST_FILTER_BY } from './COMMAND_LIST'; import COMMAND from './COMMAND'; import CONFIG_GET from './CONFIG_GET'; import CONFIG_RESETASTAT from './CONFIG_RESETSTAT'; @@ -117,7 +117,7 @@ import EXISTS from './EXISTS'; import EXPIRE from './EXPIRE'; import EXPIREAT from './EXPIREAT'; import EXPIRETIME from './EXPIRETIME'; -import FLUSHALL from './FLUSHALL'; +import FLUSHALL, { REDIS_FLUSH_MODES } from './FLUSHALL'; import FLUSHDB from './FLUSHDB'; import FCALL from './FCALL'; import FCALL_RO from './FCALL_RO'; @@ -362,6 +362,16 @@ import VSETATTR from './VSETATTR'; import VSIM from './VSIM'; import VSIM_WITHSCORES from './VSIM_WITHSCORES'; +export { + CLIENT_KILL_FILTERS, + FAILOVER_MODES, + CLUSTER_SLOT_STATES, + COMMAND_LIST_FILTER_BY, + REDIS_FLUSH_MODES +}; + +export { SetOptions } from './SET'; + export default { ACL_CAT, aclCat: ACL_CAT, From bd11e382d0dc03de2abf1dfc951258f8d1ce7e48 Mon Sep 17 00:00:00 2001 From: Trofymenko Vladyslav Date: Thu, 9 Oct 2025 16:06:57 +0300 Subject: [PATCH 1748/1748] feat: add cluster/node events (#1855) (#3083) * add cluster/node events * add test for cluster events positive branch * add cluster events docs section fixes: #1855 --------- Co-authored-by: Nikolay Karadzhov --- docs/clustering.md | 18 +++++++++ packages/client/lib/cluster/cluster-slots.ts | 34 +++++++++++------ packages/client/lib/cluster/index.spec.ts | 39 ++++++++++++++++++++ packages/test-utils/lib/index.ts | 7 +++- 4 files changed, 86 insertions(+), 12 deletions(-) diff --git a/docs/clustering.md b/docs/clustering.md index 3e4f8446b6a..4afd95afd23 100644 --- a/docs/clustering.md +++ b/docs/clustering.md @@ -120,6 +120,24 @@ createCluster({ > This is a common problem when using ElastiCache. See [Accessing ElastiCache from outside AWS](https://docs.aws.amazon.com/AmazonElastiCache/latest/red-ug/accessing-elasticache.html) for more information on that. +### Events + +The Node Redis Cluster class extends Node.js’s EventEmitter and emits the following events: + +| Name | When | Listener arguments | +| ----------------------- | ---------------------------------------------------------------------------------- | --------------------------------------------------------- | +| `connect` | The cluster has successfully connected and is ready to us | _No arguments_ | +| `disconnect` | The cluster has disconnected | _No arguments_ | +| `error` | The cluster has errored | `(error: Error)` | +| `node-ready` | A cluster node is ready to establish a connection | `(node: { host: string, port: number })` | +| `node-connect` | A cluster node has connected | `(node: { host: string, port: number })` | +| `node-reconnecting` | A cluster node is attempting to reconnect after an error | `(node: { host: string, port: number })` | +| `node-disconnect` | A cluster node has disconnected | `(node: { host: string, port: number })` | +| `node-error` | A cluster node has has errored (usually during TCP connection) | `(error: Error, node: { host: string, port: number })` | + +> :warning: You **MUST** listen to `error` events. If a cluster doesn't have at least one `error` listener registered and +> an `error` occurs, that error will be thrown and the Node.js process will exit. See the [ > `EventEmitter` docs](https://nodejs.org/api/events.html#events_error_events) for more details. + ## Command Routing ### Commands that operate on Redis Keys diff --git a/packages/client/lib/cluster/cluster-slots.ts b/packages/client/lib/cluster/cluster-slots.ts index 9c75b3ab4bf..737413677e7 100644 --- a/packages/client/lib/cluster/cluster-slots.ts +++ b/packages/client/lib/cluster/cluster-slots.ts @@ -80,9 +80,9 @@ type PubSubNode< RESP extends RespVersions, TYPE_MAPPING extends TypeMapping > = ( - Omit, 'client'> & - Required, 'client'>> -); + Omit, 'client'> & + Required, 'client'>> + ); type PubSubToResubscribe = Record< PUBSUB_TYPE['CHANNELS'] | PUBSUB_TYPE['PATTERNS'], @@ -153,6 +153,7 @@ export default class RedisClusterSlots< this.#isOpen = true; try { await this.#discoverWithRootNodes(); + this.#emit('connect'); } catch (err) { this.#isOpen = false; throw err; @@ -333,17 +334,26 @@ export default class RedisClusterSlots< } #createClient(node: ShardNode, readonly = node.readonly) { + const socket = + this.#getNodeAddress(node.address) ?? + { host: node.host, port: node.port, }; + const client = Object.freeze({ + host: socket.host, + port: socket.port, + }); + const emit = this.#emit; return this.#clientFactory( this.#clientOptionsDefaults({ clientSideCache: this.clientSideCache, RESP: this.#options.RESP, - socket: this.#getNodeAddress(node.address) ?? { - host: node.host, - port: node.port - }, - readonly - }) - ).on('error', err => console.error(err)); + socket, + readonly, + })) + .on('error', error => emit('node-error', error, client)) + .on('reconnecting', () => emit('node-reconnecting', client)) + .once('ready', () => emit('node-ready', client)) + .once('connect', () => emit('node-connect', client)) + .once('end', () => emit('node-disconnect', client)); } #createNodeClient(node: ShardNode, readonly?: boolean) { @@ -406,6 +416,7 @@ export default class RedisClusterSlots< this.#resetSlots(); this.nodeByAddress.clear(); + this.#emit('disconnect'); } *#clients() { @@ -443,6 +454,7 @@ export default class RedisClusterSlots< this.nodeByAddress.clear(); await Promise.allSettled(promises); + this.#emit('disconnect'); } getClient( @@ -542,7 +554,7 @@ export default class RedisClusterSlots< node = index < this.masters.length ? this.masters[index] : this.replicas[index - this.masters.length], - client = this.#createClient(node, false); + client = this.#createClient(node, false); this.pubSubNode = { address: node.address, diff --git a/packages/client/lib/cluster/index.spec.ts b/packages/client/lib/cluster/index.spec.ts index 4db5f32e853..af8e880ec3b 100644 --- a/packages/client/lib/cluster/index.spec.ts +++ b/packages/client/lib/cluster/index.spec.ts @@ -339,4 +339,43 @@ describe('Cluster', () => { minimumDockerVersion: [7] }); }); + + describe('clusterEvents', () => { + testUtils.testWithCluster('should fire events', async (cluster) => { + const log: string[] = []; + + cluster + .on('connect', () => log.push('connect')) + .on('disconnect', () => log.push('disconnect')) + .on('error', () => log.push('error')) + .on('node-error', () => log.push('node-error')) + .on('node-reconnecting', () => log.push('node-reconnecting')) + .on('node-ready', () => log.push('node-ready')) + .on('node-connect', () => log.push('node-connect')) + .on('node-disconnect', () => log.push('node-disconnect')) + + await cluster.connect(); + cluster.destroy(); + + assert.deepEqual(log, [ + 'node-connect', + 'node-connect', + 'node-ready', + 'node-ready', + 'connect', + 'node-disconnect', + 'node-disconnect', + 'disconnect', + ]); + }, { + ...GLOBAL.CLUSTERS.OPEN, + disableClusterSetup: true, + numberOfMasters: 2, + numberOfReplicas: 1, + clusterConfiguration: { + minimizeConnections: false + } + }); + }); + }); diff --git a/packages/test-utils/lib/index.ts b/packages/test-utils/lib/index.ts index 64b9abc7f48..65a5af5ab55 100644 --- a/packages/test-utils/lib/index.ts +++ b/packages/test-utils/lib/index.ts @@ -116,6 +116,7 @@ interface ClusterTestOptions< clusterConfiguration?: Partial>; numberOfMasters?: number; numberOfReplicas?: number; + disableClusterSetup?: boolean; } interface AllTestOptions< @@ -554,10 +555,14 @@ export default class TestUtils { port } })), - minimizeConnections: true, + minimizeConnections: options.clusterConfiguration?.minimizeConnections ?? true, ...options.clusterConfiguration }); + if(options.disableClusterSetup) { + return fn(cluster); + } + await cluster.connect(); try {